From 50a557137938cd204d5c49edb0e3c15992c52849 Mon Sep 17 00:00:00 2001 From: liuqihong <540762622@qq.com> Date: Tue, 30 Nov 2021 22:52:14 +0800 Subject: [PATCH 001/675] =?UTF-8?q?=E6=83=8A=E6=83=8A=E4=BF=AE=E6=94=B9?= =?UTF-8?q?=E7=9A=84=E4=B8=9C=E8=A5=BF?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .gitattributes | 2 +- .gitignore | 118 +++++++++--------- src/IFoxCAD.Basal/IFoxCAD.Basal.csproj | 3 +- src/IFoxCAD.Basal/LinqEx.cs | 2 +- src/IFoxCAD.Basal/LoopList.cs | 2 - .../ExtensionMethod/CollectionEx.cs | 37 +++++- src/IFoxCAD.Cad/ExtensionMethod/Curve2dEx.cs | 4 +- src/IFoxCAD.Cad/ExtensionMethod/Curve3dEx.cs | 4 +- src/IFoxCAD.Cad/ExtensionMethod/CurveEx.cs | 86 ++++++------- .../ExtensionMethod/DBDictionaryEx.cs | 14 +-- src/IFoxCAD.Cad/ExtensionMethod/DBObjectEx.cs | 4 +- src/IFoxCAD.Cad/ExtensionMethod/EditorEx.cs | 24 ++-- src/IFoxCAD.Cad/ExtensionMethod/EntityEx.cs | 23 ++-- src/IFoxCAD.Cad/ExtensionMethod/GeometryEx.cs | 80 ++++++------ src/IFoxCAD.Cad/ExtensionMethod/ObjectIdEx.cs | 24 ++-- .../ExtensionMethod/SelectionSetEx.cs | 12 +- .../ExtensionMethod/SymbolTableEx.cs | 12 +- .../ExtensionMethod/SymbolTableRecordEx.cs | 37 +++--- src/IFoxCAD.Cad/IFoxCAD.Cad.csproj | 13 +- src/IFoxCAD.Cad/IFoxCAD.Cad.csproj.data | 1 + src/IFoxCAD.Cad/ResultData/LispDottedPair.cs | 2 +- src/IFoxCAD.Cad/ResultData/LispList.cs | 2 +- src/IFoxCAD.Cad/ResultData/TypedValueList.cs | 2 +- src/IFoxCAD.Cad/ResultData/XRecordDataList.cs | 2 +- src/IFoxCAD.Cad/ResultData/XdataList.cs | 4 +- src/IFoxCAD.Cad/Runtime/AcadVersion.cs | 2 +- src/IFoxCAD.Cad/Runtime/AssemInfo.cs | 2 +- src/IFoxCAD.Cad/Runtime/AutoRegAssem.cs | 8 +- src/IFoxCAD.Cad/Runtime/DBTrans.cs | 14 +-- src/IFoxCAD.Cad/Runtime/Env.cs | 4 +- src/IFoxCAD.Cad/Runtime/SymbolTable.cs | 16 +-- src/IFoxCAD.Cad/SelectionFilter/OpComp.cs | 2 +- src/IFoxCAD.Cad/SelectionFilter/OpEqual.cs | 2 +- src/IFoxCAD.Cad/SelectionFilter/OpFilter.cs | 2 +- src/IFoxCAD.WPF/IFoxCAD.WPF.csproj | 15 +-- tests/DBTrans.test/DBTrans.test.csproj | 13 +- tests/DBTrans.test/Test.cs | 48 +++---- tests/DBTrans.test/testenv.cs | 12 +- tests/DBTrans.test/testselectfilter.cs | 11 +- 39 files changed, 326 insertions(+), 339 deletions(-) diff --git a/.gitattributes b/.gitattributes index 63a09c5..8bb03aa 100644 --- a/.gitattributes +++ b/.gitattributes @@ -1,4 +1,4 @@ -############################################################################### +############################################################################### # Set default behavior to automatically normalize line endings. ############################################################################### * text=auto diff --git a/.gitignore b/.gitignore index faf8762..25cc348 100644 --- a/.gitignore +++ b/.gitignore @@ -1,11 +1,11 @@ -## Ignore Visual Studio temporary files, build results, and +## Ignore Visual Studio temporary files, build results, and ## files generated by popular Visual Studio add-ons. -## Visual StudioʱļɽVisualStudioòɵļ +## 忽略Visual Studio临时文件、生成结果和由VisualStudio常用插件生成的文件。 ## ## Get latest from https://github.com/github/gitignore/blob/master/VisualStudio.gitignore # User-specific files -# ûضļ +# 用户特定文件 *.rsuser *.suo *.user @@ -13,12 +13,12 @@ *.sln.docstates # User-specific files (MonoDevelop/Xamarin Studio) -# ûضļ (MonoDevelop/Xamarin Studio) +# 用户特定文件 (MonoDevelop/Xamarin Studio) *.userprefs # Build results -# Build -# ﷨[abc]ƥκһڷеַҪôƥһ aҪôƥһ bҪôƥһ c *.[oa]Git .o .a βļ +# Build 结果 +# 语法:[abc]匹配任何一个列在方括号中的字符(要么匹配一个 a,要么匹配一个 b,要么匹配一个 c)——如 *.[oa]表明Git忽略所有以 .o 或 .a 结尾的文件 [Dd]ebug/ [Dd]ebugPublic/ [Rr]elease/ @@ -33,34 +33,34 @@ bld/ [Ll]og/ # Visual Studio 2015/2017 cache/options directory -# Visual Studio 2015/2017 /ѡ Ŀ¼ +# Visual Studio 2015/2017 缓存/选项 目录 .vs/ # Uncomment if you have tasks that create the project's static files in wwwroot -# wwwrootдĿ̬ļȡע +# 如果您有在wwwroot中创建项目静态文件的任务,请取消注释 #wwwroot/ # Visual Studio 2017 auto generated files -# Visual Studio 2017Զɵļ +# Visual Studio 2017自动生成的文件 Generated\ Files/ # MSTest test Results -# MSTestԽ +# MSTest测试结果 [Tt]est[Rr]esult*/ [Bb]uild[Ll]og.* # NUNIT -# NUnit JUnit .NET 棬֧ .NET ԣȫʹ C# дȫúܶ߼ .NET ԣ綨Լصķ书ܡ +# NUnit 是 JUnit 的 .NET 版,支持所有 .NET 语言,完全使用 C# 编写,并进行完全重新设计以利用很多高级的 .NET 语言特性,例如定制属性以及其他相关的反射功能。 *.VisualState.xml TestResult.xml # Build Results of an ATL Project -# ATLĿɽ +# ATL项目的生成结果 [Dd]ebugPS/ [Rr]eleasePS/ dlldata.c # Benchmark Results -# Benchmark +# Benchmark结果 BenchmarkDotNet.Artifacts/ # .NET Core @@ -69,11 +69,11 @@ project.fragment.lock.json artifacts/ # StyleCop -# ⹤ +# 代码检测工具 StyleCopReport.xml # Files built by Visual Studio -# Visual Studio builtļ +# Visual Studio built的文件 *_i.c *_p.c *_h.h @@ -103,11 +103,11 @@ StyleCopReport.xml *.scc # Chutzpah Test files -# Chutzpahļ +# Chutzpah测试文件 _Chutzpah* # Visual C++ cache files -# Visual C++ ļ +# Visual C++ 缓存文件 ipch/ *.aps *.ncb @@ -119,23 +119,23 @@ ipch/ *.VC.VC.opendb # Visual Studio profiler -# Ӧóܷ +# 应用程序性能分析工具 *.psess *.vsp *.vspx *.sap # Visual Studio Trace Files -# Visual Studio ļ +# Visual Studio 跟踪文件 *.e2e # TFS 2012 Local Workspace -# TFS 2012 ع +# TFS 2012 本地工作区 $tf/ # Guidance Automation Toolkit -# ׹ּڼ򻯽õĴ뼯ɵӦóḶ́ʹܹʦִֶܽͨеһϵпԶ -# ʹô˹ߣȷظԵġ׳ĿԺһµķʽɣʱ䡣 +# 这套工具旨在简化将可重用的代码集成到应用程序的过程,使架构师能将通常需手动执行的一系列开发工作自动化起来。 +# 使用此工具,还能确保重复性的、易出错的开发工作以合理、一致的方式完成,并能缩短软件开发时间。 *.gpState # ReSharper is a .NET coding add-in @@ -173,11 +173,11 @@ AutoTest.Net/ .sass-cache/ # Installshield output folder -# Installshield ļ +# Installshield 输出文件夹 [Ee]xpress/ # DocProject is a documentation generator add-in -# DocProject һĵӳ +# DocProject 是一个文档生成器外接程序 DocProject/buildhelp/ DocProject/Help/*.HxT DocProject/Help/*.HxC @@ -195,32 +195,32 @@ publish/ *.azurePubxml # Note: Comment the next line if you want to checkin your web deploy settings, # but database connection strings (with potential passwords) will be unencrypted -# ע⣺Ҫǩwebãһעͣ -# ݿַпܵ룩δܵ +# 注意:如果要签入web部署设置,请在下一行添加注释, +# 但是数据库连接字符串(带有可能的密码)将是未加密的 *.pubxml *.publishproj # Microsoft Azure Web App publish settings. Comment the next line if you want to # checkin your Azure Web App publish settings, but sensitive information contained # in these scripts will be unencrypted -# Microsoft Azure Web Appá -# ǩAzure Web AppãһעͣЩűаϢ +# Microsoft Azure Web App发布设置。 +# 如果您想签入Azure Web App发布设置,请在下一行添加注释,但这些脚本中包含的敏感信息将不加密 PublishScripts/ # NuGet Packages -# NuGet +# NuGet包 *.nupkg # The packages folder can be ignored because of Package Restore -# Package RestoreļпԺ +# 由于Package Restore,包文件夹可以忽略 **/[Pp]ackages/* # except build/, which is used as an MSBuild target. -# build/MSBuildĿꡣ +# 除了build/,它用作MSBuild目标。 !**/[Pp]ackages/build/ # Uncomment if necessary however generally it will be regenerated when needed -# ҪʱȡעͣͨҪʱע +# 必要时取消注释,但通常需要时会重新生成注释 #!**/[Pp]ackages/repositories.config # NuGet v3's project.json files produces more ignorable files -# NuGet v3project.jsonļɸɺԵļ +# NuGet v3的project.json文件生成更多可忽略的文件 *.nuget.props *.nuget.targets @@ -233,7 +233,7 @@ ecf/ rcf/ # Windows Store app package directories and files -# WindowsӦ̵ӦóĿ¼ļ +# Windows应用商店应用程序包目录和文件 AppPackages/ BundleArtifacts/ Package.StoreAssociation.xml @@ -241,16 +241,16 @@ _pkginfo.txt *.appx # Visual Studio cache files -# Visual Studioļ +# Visual Studio缓存文件 # files ending in .cache can be ignored -# Ժ.cacheβļ +# 可以忽略以.cache结尾的文件 *.[Cc]ache # but keep track of directories ending in .cache -# Ҫ.cacheβĿ¼ +# 但要跟踪以.cache结尾的目录 !?*.[Cc]ache/ # Others -# +# 其他 ClientBin/ ~$* *~ @@ -262,16 +262,16 @@ ClientBin/ orleans.codegen.cs # Including strong name files can present a security risk -# ǿļܻȫ +# 包含强名称文件可能会带来安全风险 # (https://github.com/github/gitignore/pull/2483#issue-259490424) #*.snk # Since there are multiple workflows, uncomment next line to ignore bower_components -#жȡעһԺbower_components +#由于有多个工作流,取消注释下一行以忽略bower_components # (https://github.com/github/gitignore/pull/1529#issuecomment-104372622) #bower_components/ # ASP.NET Core default setup: bower directory is configured as wwwroot/lib/ and bower restore is true -#ASP.NETĬãbowerĿ¼Ϊwwwroot/lib/bower restoreΪtrue +#ASP.NET核心默认设置:bower目录配置为wwwroot/lib/并且bower restore为true **/wwwroot/lib/ # RIA/Silverlight projects @@ -280,7 +280,7 @@ Generated_Code/ # Backup & report files from converting an old project file # to a newer Visual Studio version. Backup files are not needed, # because we have git ;-) -#ĿļתΪµVisual Studio汾ıݺͱļҪļΪgit +#将旧项目文件转换为新的Visual Studio版本的备份和报告文件。不需要备份文件,因为我们有git; _UpgradeReport_Files/ Backup*/ UpgradeLog*.XML @@ -294,7 +294,7 @@ ServiceFabricBackup/ *.ndf # Business Intelligence projects -# ҵĿ +# 商业智能项目 *.rdl.data *.bim.layout *.bim_*.settings @@ -304,28 +304,28 @@ ServiceFabricBackup/ FakesAssemblies/ # GhostDoc plugin setting file -# GhostDocļ +# GhostDoc插件设置文件 *.GhostDoc.xml # Node.js Tools for Visual Studio -# Visual StudioNode.js +# 用于Visual Studio的Node.js工具 .ntvs_analysis.dat node_modules/ # Visual Studio 6 build log -# Visual Studio 6־ +# Visual Studio 6生成日志 *.plg # Visual Studio 6 workspace options file -# Visual Studio 6ѡļ +# Visual Studio 6工作区选项文件 *.opt # Visual Studio 6 auto-generated workspace file (contains which files were open etc.) -# Visual Studio 6ԶɵĹļ򿪵ļȣ +# Visual Studio 6自动生成的工作区文件(包含打开的文件等) *.vbw # Visual Studio LightSwitch build output -# Visual Studio LightSwitch +# Visual Studio LightSwitch生成输出 **/*.HTMLClient/GeneratedArtifacts **/*.DesktopClient/GeneratedArtifacts **/*.DesktopClient/ModelManifest.xml @@ -334,7 +334,7 @@ node_modules/ _Pvt_Extensions # Paket dependency manager -# Paketϵ +# Paket依赖关系管理器 .paket/paket.exe paket-files/ @@ -353,7 +353,7 @@ __pycache__/ *.pyc # Cake - Uncomment if you are using it -# Cake-ʹȡע +# Cake-如果你正在使用它,请取消注释 # tools/** # !tools/packages.config @@ -361,7 +361,7 @@ __pycache__/ *.tss # Telerik's JustMock configuration file -# TelerikJustMockļ +# Telerik的JustMock配置文件 *.jmconfig # BizTalk build output @@ -372,29 +372,29 @@ __pycache__/ *.xsd.cs # OpenCover UI analysis results -# OpenCover UI +# OpenCover UI分析结果 OpenCover/ # Azure Stream Analytics local run output -# Azure +# Azure流分析本地运行输出 ASALocalRun/ # MSBuild Binary and Structured Log -# MSBuildƺͽṹ־ +# MSBuild二进制和结构化日志 *.binlog # NVidia Nsight GPU debugger configuration file -# NVidia Nsight GPUļ +# NVidia Nsight GPU调试器配置文件 *.nvuser # MFractors (Xamarin productivity tool) working folder -# MFractorsXamarinߣļ +# MFractors(Xamarin生产力工具)工作文件夹 .mfractor/ # Local History for Visual Studio -# Visual Studio ıʷ¼ +# Visual Studio 的本地历史记录 .localhistory/ # BeatPulse healthcheck temp database -# BeatPulse healthcheck ʱݿ +# BeatPulse healthcheck 临时数据库 healthchecksdb diff --git a/src/IFoxCAD.Basal/IFoxCAD.Basal.csproj b/src/IFoxCAD.Basal/IFoxCAD.Basal.csproj index 9330969..8ae4570 100644 --- a/src/IFoxCAD.Basal/IFoxCAD.Basal.csproj +++ b/src/IFoxCAD.Basal/IFoxCAD.Basal.csproj @@ -1,5 +1,4 @@ - - + net35;net40 true diff --git a/src/IFoxCAD.Basal/LinqEx.cs b/src/IFoxCAD.Basal/LinqEx.cs index b0454a4..7b20401 100644 --- a/src/IFoxCAD.Basal/LinqEx.cs +++ b/src/IFoxCAD.Basal/LinqEx.cs @@ -339,4 +339,4 @@ public static IOrderedEnumerable ThenBy(this IOrderedEnumerable s #endregion Order } -} \ No newline at end of file +} diff --git a/src/IFoxCAD.Basal/LoopList.cs b/src/IFoxCAD.Basal/LoopList.cs index 3d132c8..79319f0 100644 --- a/src/IFoxCAD.Basal/LoopList.cs +++ b/src/IFoxCAD.Basal/LoopList.cs @@ -496,9 +496,7 @@ public override string ToString() { string s = "( "; foreach (T value in this) - { s += value.ToString() + " "; - } return s + ")"; } diff --git a/src/IFoxCAD.Cad/ExtensionMethod/CollectionEx.cs b/src/IFoxCAD.Cad/ExtensionMethod/CollectionEx.cs index 8fb683a..9b0c984 100644 --- a/src/IFoxCAD.Cad/ExtensionMethod/CollectionEx.cs +++ b/src/IFoxCAD.Cad/ExtensionMethod/CollectionEx.cs @@ -2,6 +2,7 @@ using Autodesk.AutoCAD.Geometry; using System; using System.Collections.Generic; +using System.Collections.Specialized; using System.Linq; namespace IFoxCAD.Cad @@ -75,6 +76,27 @@ public static List ToList(this ObjectIdCollection ids) return ids.Cast().ToList(); } + public static List ToList(this StringCollection strs) + { + return strs.Cast().ToList(); + } + + /* Cast不进行过滤,而是直接强转 + var s = new System.Collections.ArrayList(); + s.Add("1"); + s.Add("a"); + s.Add(5666); + + var aaa = s.Cast().ToList(); + System.InvalidCastException: 指定的转换无效。 + + List..ctor(IEnumerable) + + System.Linq.Enumerable.ToList(IEnumerable) + + var aaa = s.Cast().ToList(); + System.InvalidCastException: 无法将类型为“System.Int32”的对象强制转换为类型“System.String”。 + + List..ctor(IEnumerable) + + System.Linq.Enumerable.ToList(IEnumerable) + */ /// /// 遍历集合的迭代器,执行action委托 @@ -84,11 +106,13 @@ public static List ToList(this ObjectIdCollection ids) /// 要运行的委托 public static void ForEach(this IEnumerable source, Action action) { + if (action == null) + throw new ArgumentNullException(nameof(action)); foreach (var element in source) - { - action?.Invoke(element); - } + action.Invoke(element); } + + /// /// 同时遍历集合索引和值的迭代器,执行action委托 /// @@ -97,14 +121,15 @@ public static void ForEach(this IEnumerable source, Action action) /// 要运行的委托 public static void ForEach(this IEnumerable source, Action action) { + if (action == null) + throw new ArgumentNullException(nameof(action)); int i = 0; foreach (var item in source) { - action?.Invoke(i, item); + action.Invoke(i, item); i++; } - } } -} \ No newline at end of file +} diff --git a/src/IFoxCAD.Cad/ExtensionMethod/Curve2dEx.cs b/src/IFoxCAD.Cad/ExtensionMethod/Curve2dEx.cs index e5d7943..fb3b773 100644 --- a/src/IFoxCAD.Cad/ExtensionMethod/Curve2dEx.cs +++ b/src/IFoxCAD.Cad/ExtensionMethod/Curve2dEx.cs @@ -183,7 +183,7 @@ public static Ellipse ToCurve(this EllipticalArc2d ea2d) /// 实体类构造线 public static Xline ToCurve(this Line2d line2d) { - Plane plane = new Plane(); + var plane = new Plane(); return new Xline { @@ -315,4 +315,4 @@ public static Spline ToCurve(this NurbCurve2d nc2d) #endregion NurbCurve2d } -} \ No newline at end of file +} diff --git a/src/IFoxCAD.Cad/ExtensionMethod/Curve3dEx.cs b/src/IFoxCAD.Cad/ExtensionMethod/Curve3dEx.cs index d5ad9ae..9d477ba 100644 --- a/src/IFoxCAD.Cad/ExtensionMethod/Curve3dEx.cs +++ b/src/IFoxCAD.Cad/ExtensionMethod/Curve3dEx.cs @@ -403,7 +403,7 @@ public static Curve ToCurve(this CircularArc3d ca3d) /// /// 三维解析类圆/弧 /// 实体圆 - public static Circle ToCircle(this CircularArc3d ca3d) => + public static Circle ToCircle(this CircularArc3d ca3d) => new Circle(ca3d.Center, ca3d.Normal, ca3d.Radius); /// @@ -564,4 +564,4 @@ public static Polyline3d ToCurve(this PolylineCurve3d pl3d) #endregion PolylineCurve3d } -} \ No newline at end of file +} diff --git a/src/IFoxCAD.Cad/ExtensionMethod/CurveEx.cs b/src/IFoxCAD.Cad/ExtensionMethod/CurveEx.cs index fd3bf83..af13df1 100644 --- a/src/IFoxCAD.Cad/ExtensionMethod/CurveEx.cs +++ b/src/IFoxCAD.Cad/ExtensionMethod/CurveEx.cs @@ -644,7 +644,7 @@ public static EllipticalArc3d ToEllipticalArc3d(this Arc arc) public static NurbCurve3d ToNurbCurve3d(this Arc arc) { return new NurbCurve3d(ToEllipticalArc3d(arc)); - } + } #endregion Arc @@ -780,14 +780,11 @@ public static NurbCurve3d ToNurbCurve3d(this Polyline2d pl2d) /// 三维ge多段线 public static PolylineCurve3d ToPolylineCurve3d(this Polyline2d pl) { - Point3dCollection pnts = new Point3dCollection(); + var pnts = new Point3dCollection(); foreach (Vertex2d ver in pl) - { pnts.Add(ver.Position); - } return new PolylineCurve3d(pnts); } - #endregion Polyline2d #region Polyline3d @@ -912,16 +909,16 @@ public static void ChamferAt(this Polyline polyline, int index, double radius, b if (index < 1 || index > polyline.NumberOfVertices - 2) throw new System.Exception("错误的索引号"); - if (polyline.GetSegmentType(index - 1) != SegmentType.Line || polyline.GetSegmentType(index) != SegmentType.Line) + if (polyline.GetSegmentType(index - 1) != SegmentType.Line || + polyline.GetSegmentType(index) != SegmentType.Line) throw new System.Exception("非直线段不能倒角"); //获取当前索引号的前后两段直线,并组合为Ge复合曲线 - Curve3d[] c3ds = - new Curve3d[] - { - polyline.GetLineSegmentAt(index - 1), - polyline.GetLineSegmentAt(index) - }; + var c3ds = new Curve3d[] + { + polyline.GetLineSegmentAt(index - 1), + polyline.GetLineSegmentAt(index) + }; var cc3d = new CompositeCurve3d(c3ds); //试倒直角 @@ -929,31 +926,26 @@ public static void ChamferAt(this Polyline polyline, int index, double radius, b //1、=3时倒角方向正确 //2、=2时倒角方向相反 //3、=0或为直线时失败 - c3ds = - cc3d.GetTrimmedOffset - ( - radius, - Vector3d.ZAxis, - OffsetCurveExtensionType.Chamfer - ); + c3ds = cc3d.GetTrimmedOffset + ( + radius, + Vector3d.ZAxis, + OffsetCurveExtensionType.Chamfer + ); - if (c3ds.Length > 0 && c3ds[0] is CompositeCurve3d) + if (c3ds.Length > 0 && c3ds[0] is CompositeCurve3d newcc3d) { - var newcc3d = c3ds[0] as CompositeCurve3d; c3ds = newcc3d.GetCurves(); if (c3ds.Length == 3) { - c3ds = - cc3d.GetTrimmedOffset - ( - -radius, - Vector3d.ZAxis, - OffsetCurveExtensionType.Chamfer - ); + c3ds = cc3d.GetTrimmedOffset + ( + -radius, + Vector3d.ZAxis, + OffsetCurveExtensionType.Chamfer + ); if (c3ds.Length == 0 || c3ds[0] is LineSegment3d) - { throw new System.Exception("倒角半径过大"); - } } else if (c3ds.Length == 2) { @@ -966,26 +958,22 @@ public static void ChamferAt(this Polyline polyline, int index, double radius, b } //GetTrimmedOffset会生成倒角+偏移,故先反方向倒角,再倒回 - c3ds = - cc3d.GetTrimmedOffset - ( - -radius, - Vector3d.ZAxis, - OffsetCurveExtensionType.Extend - ); - OffsetCurveExtensionType type = - isFillet ? - OffsetCurveExtensionType.Fillet : OffsetCurveExtensionType.Chamfer; - c3ds = - c3ds[0].GetTrimmedOffset - ( - radius, - Vector3d.ZAxis, - type - ); + c3ds = cc3d.GetTrimmedOffset + ( + -radius, + Vector3d.ZAxis, + OffsetCurveExtensionType.Extend + ); + var type = isFillet ? OffsetCurveExtensionType.Fillet : OffsetCurveExtensionType.Chamfer; + c3ds = c3ds[0].GetTrimmedOffset + ( + radius, + Vector3d.ZAxis, + type + ); //将结果Ge曲线转为Db曲线,并将相关的数值反映到原曲线 - Polyline plTemp = c3ds[0].ToCurve() as Polyline; + var plTemp = c3ds[0].ToCurve() as Polyline; polyline.RemoveVertexAt(index); polyline.AddVertexAt(index, plTemp.GetPoint2dAt(1), plTemp.GetBulgeAt(1), 0, 0); polyline.AddVertexAt(index + 1, plTemp.GetPoint2dAt(2), 0, 0, 0); @@ -995,4 +983,4 @@ public static void ChamferAt(this Polyline polyline, int index, double radius, b #endregion Curve } -} \ No newline at end of file +} diff --git a/src/IFoxCAD.Cad/ExtensionMethod/DBDictionaryEx.cs b/src/IFoxCAD.Cad/ExtensionMethod/DBDictionaryEx.cs index dff4ff3..6d3921e 100644 --- a/src/IFoxCAD.Cad/ExtensionMethod/DBDictionaryEx.cs +++ b/src/IFoxCAD.Cad/ExtensionMethod/DBDictionaryEx.cs @@ -55,16 +55,16 @@ public static T GetAt(this DBDictionary dict, string key, Transaction trans = /// /// 对象类型 /// 字典 - /// 事务 + /// 事务 /// 键 /// 值 - public static void SetAt(this DBDictionary dict, string key, T obj, Transaction trans = null) where T : DBObject + public static void SetAt(this DBDictionary dict, string key, T obj, Transaction tr = null) where T : DBObject { - trans ??= DBTrans.Top.Transaction; + tr ??= DBTrans.Top.Transaction; using (dict.ForWrite()) { dict.SetAt(key, obj); - trans.AddNewlyCreatedDBObject(obj, true); + tr.AddNewlyCreatedDBObject(obj, true); } } @@ -116,7 +116,7 @@ public static DBDictionary GetXDictionary(this DBObject obj, Transaction trans = id = obj.ExtensionDictionary; } - return id.GetObject(tr:trans); + return id.GetObject(tr: trans); } #region 数据表 @@ -158,8 +158,8 @@ public static DataTable CreateDataTable(Dictionary colTypes, o /// 数据 public static void SetValue(this DataCell cell, CellType type, object value) { - - + + switch (type) { case CellType.Bool: diff --git a/src/IFoxCAD.Cad/ExtensionMethod/DBObjectEx.cs b/src/IFoxCAD.Cad/ExtensionMethod/DBObjectEx.cs index eac264c..3052258 100644 --- a/src/IFoxCAD.Cad/ExtensionMethod/DBObjectEx.cs +++ b/src/IFoxCAD.Cad/ExtensionMethod/DBObjectEx.cs @@ -75,7 +75,7 @@ public static void ChangeXData(this DBObject obj, string appName, DxfCode dxfCod break; if (data[i].TypeCode == (int)dxfCode) { - data[i] = new TypedValue((int)dxfCode,newvalue); + data[i] = new TypedValue((int)dxfCode, newvalue); } } } @@ -83,7 +83,7 @@ public static void ChangeXData(this DBObject obj, string appName, DxfCode dxfCod using (obj.ForWrite()) { obj.XData = data; - } + } } #endregion diff --git a/src/IFoxCAD.Cad/ExtensionMethod/EditorEx.cs b/src/IFoxCAD.Cad/ExtensionMethod/EditorEx.cs index a396bbd..f7234c8 100644 --- a/src/IFoxCAD.Cad/ExtensionMethod/EditorEx.cs +++ b/src/IFoxCAD.Cad/ExtensionMethod/EditorEx.cs @@ -516,7 +516,7 @@ public static Matrix3d GetMatrix(this Editor editor, CoordinateSystemCode from, (CoordinateSystemCode.MDcs, CoordinateSystemCode.Ucs) => editor.GetMatrixFromMDcsToWcs() * editor.GetMatrixFromWcsToUcs(), (CoordinateSystemCode.MDcs, CoordinateSystemCode.PDcs) => editor.GetMatrixFromMDcsToPDcs(), (CoordinateSystemCode.PDcs, CoordinateSystemCode.MDcs) => editor.GetMatrixFromPDcsToMDcs(), - (CoordinateSystemCode.PDcs, CoordinateSystemCode.Wcs or CoordinateSystemCode.Ucs) + (CoordinateSystemCode.PDcs, CoordinateSystemCode.Wcs or CoordinateSystemCode.Ucs) or (CoordinateSystemCode.Wcs or CoordinateSystemCode.Ucs, CoordinateSystemCode.PDcs) => throw new Autodesk.AutoCAD.Runtime.Exception(ErrorStatus.InvalidInput,"To be used only with DCS"), (_, _) => Matrix3d.Identity }; @@ -624,7 +624,7 @@ public static void ZoomWindow(this Editor ed, Point3d lpt, Point3d rpt, double o /// 偏移距离 public static void ZoomExtents(this Editor ed, double offsetDist = 0.00) { - Database db = ed.Document.Database; + var db = ed.Document.Database; db.UpdateExt(true); ed.ZoomWindow(db.Extmax, db.Extmin, offsetDist); } @@ -737,22 +737,16 @@ public static PromptResult GetString(this Editor ed, string Message, string Defa /// 缓冲结果,返回值 public static ResultBuffer RunLisp(this Editor ed, string arg) { - _ = AcedEvaluateLisp(arg, out IntPtr rb); - if (rb != IntPtr.Zero) + AcedEvaluateLisp(arg, out IntPtr rb); + try { - try - { - var rbb = DisposableWrapper.Create(typeof(ResultBuffer), rb, true) as ResultBuffer; - return rbb; - } - catch - { - return null; - } + if (rb != IntPtr.Zero) + return DisposableWrapper.Create(typeof(ResultBuffer), rb, true) as ResultBuffer; } + catch + { } return null; } - #endregion 执行lisp } -} \ No newline at end of file +} diff --git a/src/IFoxCAD.Cad/ExtensionMethod/EntityEx.cs b/src/IFoxCAD.Cad/ExtensionMethod/EntityEx.cs index 795e9df..0635006 100644 --- a/src/IFoxCAD.Cad/ExtensionMethod/EntityEx.cs +++ b/src/IFoxCAD.Cad/ExtensionMethod/EntityEx.cs @@ -225,8 +225,7 @@ public static string GetUnFormatString(this MText mt) List strs = new(); mt.ExplodeFragments( strs, - (f, o) => - { + (f, o) => { o.Add(f.Text); return MTextFragmentCallbackStatus.Continue; }); @@ -317,13 +316,13 @@ public static Circle CreateCircle(Point3d startPoint, Point3d endPoint) /// 三点法创建圆(失败则返回Null) /// /// 第一点 - /// 第二点 + /// 第二点 /// 第三点 /// - public static Circle CreateCircle(Point3d pt1, Point3d pt2, Point3d pt3) + public static Circle CreateCircle(Point3d pt1, Point3d PointV, Point3d pt3) { - //先判断三点是否共线,得到pt1点指向pt2、pt2点的矢量 - Vector3d va = pt1.GetVectorTo(pt2); + //先判断三点是否共线,得到pt1点指向PointV、PointV点的矢量 + Vector3d va = pt1.GetVectorTo(PointV); Vector3d vb = pt1.GetVectorTo(pt3); //如两矢量夹角为0或180度(π弧度),则三点共线. if (va.GetAngleTo(vb) == 0 | va.GetAngleTo(vb) == Math.PI) @@ -333,7 +332,7 @@ public static Circle CreateCircle(Point3d pt1, Point3d pt2, Point3d pt3) else { //创建一个几何类的圆弧对象 - CircularArc3d geArc = new(pt1, pt2, pt3); + CircularArc3d geArc = new(pt1, PointV, pt3); geArc.ToCircle(); return geArc.ToCircle(); } @@ -382,8 +381,8 @@ public static void ClipBlockRef(this BlockReference bref, IEnumerable p /// /// 块参照 /// 第一角点 - /// 第二角点 - public static void ClipBlockRef(this BlockReference bref, Point3d pt1, Point3d pt2) + /// 第二角点 + public static void ClipBlockRef(this BlockReference bref, Point3d pt1, Point3d PointV) { if (bref == null) { @@ -391,11 +390,11 @@ public static void ClipBlockRef(this BlockReference bref, Point3d pt1, Point3d p } Matrix3d mat = bref.BlockTransform.Inverse(); pt1 = pt1.TransformBy(mat); - pt2 = pt2.TransformBy(mat); + PointV = PointV.TransformBy(mat); Point2dCollection pts = new() { - new Point2d(Math.Min(pt1.X, pt2.X), Math.Min(pt1.Y, pt2.Y)), - new Point2d(Math.Max(pt1.X, pt2.X), Math.Max(pt1.Y, pt2.Y)) + new Point2d(Math.Min(pt1.X, PointV.X), Math.Min(pt1.Y, PointV.Y)), + new Point2d(Math.Max(pt1.X, PointV.X), Math.Max(pt1.Y, PointV.Y)) }; SpatialFilterDefinition sfd = new(pts, Vector3d.ZAxis, 0.0, 0.0, 0.0, true); diff --git a/src/IFoxCAD.Cad/ExtensionMethod/GeometryEx.cs b/src/IFoxCAD.Cad/ExtensionMethod/GeometryEx.cs index befb4a6..21c5956 100644 --- a/src/IFoxCAD.Cad/ExtensionMethod/GeometryEx.cs +++ b/src/IFoxCAD.Cad/ExtensionMethod/GeometryEx.cs @@ -13,6 +13,10 @@ namespace IFoxCAD.Cad /// public static class GeometryEx { + public static double DistanceTo(this Point2d pt1, Point2d PointV) + { + return pt1.GetDistanceTo(PointV); + } #region Point&Circle @@ -50,12 +54,12 @@ public static PointOnRegionType PointOnRegion(this IEnumerable pts, Poi foreach (var node in ptlst.GetNodes()) { var pt1 = node.Value; - var pt2 = node.Next.Value; - if (pt.Y < pt1.Y && pt.Y < pt2.Y) + var PointV = node.Next.Value; + if (pt.Y < pt1.Y && pt.Y < PointV.Y) continue; - if (pt1.X < pt.X && pt2.X < pt.X) + if (pt1.X < pt.X && PointV.X < pt.X) continue; - Vector2d vec = pt2 - pt1; + Vector2d vec = PointV - pt1; double t = (pt.X - pt1.X) / vec.X; double y = t * vec.Y + pt1.Y; if (y < pt.Y && t >= 0 && t <= 1) @@ -102,12 +106,12 @@ public static PointOnRegionType PointOnRegion(this IEnumerable pts, Poi foreach (var node in ptlst.GetNodes()) { var pt1 = node.Value; - var pt2 = node.Next.Value; - if (pt.Y < pt1.Y && pt.Y < pt2.Y) + var PointV = node.Next.Value; + if (pt.Y < pt1.Y && pt.Y < PointV.Y) continue; - if (pt1.X < pt.X && pt2.X < pt.X) + if (pt1.X < pt.X && PointV.X < pt.X) continue; - Vector3d vec = pt2 - pt1; + Vector3d vec = PointV - pt1; double t = (pt.X - pt1.X) / vec.X; double y = t * vec.Y + pt1.Y; if (y < pt.Y && t >= 0 && t <= 1) @@ -122,17 +126,17 @@ public static PointOnRegionType PointOnRegion(this IEnumerable pts, Poi /// 按两点返回最小包围圆 /// /// 基准点 - /// 基准点 + /// 基准点 /// 输出圆上的点 /// 解析类圆对象 - public static CircularArc2d GetMinCircle(Point2d pt1, Point2d pt2, out LoopList ptlst) + public static CircularArc2d GetMinCircle(Point2d pt1, Point2d PointV, out LoopList ptlst) { - ptlst = new LoopList { pt1, pt2 }; + ptlst = new LoopList { pt1, PointV }; return new CircularArc2d ( - (pt1 + pt2.GetAsVector()) / 2, - pt1.GetDistanceTo(pt2) / 2 + (pt1 + PointV.GetAsVector()) / 2, + pt1.DistanceTo(PointV) / 2 ); } @@ -140,14 +144,14 @@ public static CircularArc2d GetMinCircle(Point2d pt1, Point2d pt2, out LoopList< /// 按三点返回最小包围圆 /// /// 基准点 - /// 基准点 + /// 基准点 /// 基准点 /// 输出圆上的点 /// 解析类圆对象 - public static CircularArc2d GetMinCircle(Point2d pt1, Point2d pt2, Point2d pt3, out LoopList ptlst) + public static CircularArc2d GetMinCircle(Point2d pt1, Point2d PointV, Point2d pt3, out LoopList ptlst) { ptlst = - new LoopList { pt1, pt2, pt3 }; + new LoopList { pt1, PointV, pt3 }; //遍历各点与下一点的向量长度,找到距离最大的两个点 double maxLength; @@ -155,7 +159,7 @@ public static CircularArc2d GetMinCircle(Point2d pt1, Point2d pt2, Point2d pt3, ptlst.GetNodes().FindByMax ( out maxLength, - node => node.Value.GetDistanceTo(node.Next.Value) + node => node.Value.DistanceTo(node.Next.Value) ); //以两点做最小包围圆 @@ -173,7 +177,7 @@ public static CircularArc2d GetMinCircle(Point2d pt1, Point2d pt2, Point2d pt3, //否则按三点做圆 ptlst.SetFirst(maxNode); - ca2d = new CircularArc2d(pt1, pt2, pt3); + ca2d = new CircularArc2d(pt1, PointV, pt3); ca2d.SetAngles(0, Math.PI * 2); return ca2d; } @@ -182,15 +186,15 @@ public static CircularArc2d GetMinCircle(Point2d pt1, Point2d pt2, Point2d pt3, /// 按四点返回最小包围圆 /// /// 基准点 - /// 基准点 + /// 基准点 /// 基准点 /// 基准点 /// 输出圆上的点 /// 解析类圆对象 - public static CircularArc2d GetMinCircle(Point2d pt1, Point2d pt2, Point2d pt3, Point2d pt4, out LoopList ptlst) + public static CircularArc2d GetMinCircle(Point2d pt1, Point2d PointV, Point2d pt3, Point2d pt4, out LoopList ptlst) { LoopList iniptlst = - new LoopList { pt1, pt2, pt3, pt4 }; + new LoopList { pt1, PointV, pt3, pt4 }; ptlst = null; CircularArc2d ca2d = null; @@ -224,22 +228,22 @@ public static CircularArc2d GetMinCircle(Point2d pt1, Point2d pt2, Point2d pt3, /// /// 基准点 /// 第一点 - /// 第二点 + /// 第二点 /// 三点围成的三角形的有向面积 - private static double CalArea(Point2d ptBase, Point2d pt1, Point2d pt2) + private static double CalArea(Point2d ptBase, Point2d pt1, Point2d PointV) { - return (pt2 - ptBase).DotProduct((pt1 - ptBase).GetPerpendicularVector()) / 2; + return (PointV - ptBase).DotProduct((pt1 - ptBase).GetPerpendicularVector()) / 2; } /// /// 计算三点围成的三角形的真实面积 /// /// 基准点 /// 第一点 - /// 第二点 + /// 第二点 /// 三点围成的三角形的真实面积 - public static double GetArea(this Point2d ptBase, Point2d pt1, Point2d pt2) + public static double GetArea(this Point2d ptBase, Point2d pt1, Point2d PointV) { - return Math.Abs(CalArea(ptBase, pt1, pt2)); + return Math.Abs(CalArea(ptBase, pt1, PointV)); } /// @@ -247,12 +251,12 @@ public static double GetArea(this Point2d ptBase, Point2d pt1, Point2d pt2) /// /// 基点 /// 第一点 - /// 第二点 + /// 第二点 /// OrientationType 类型值 - public static OrientationType IsClockWise(this Point2d ptBase, Point2d pt1, Point2d pt2) + public static OrientationType IsClockWise(this Point2d ptBase, Point2d pt1, Point2d PointV) { - return CalArea(ptBase, pt1, pt2) switch + return CalArea(ptBase, pt1, PointV) switch { > 0 => OrientationType.CounterClockWise, @@ -385,7 +389,7 @@ public static CircularArc2d GetMinCircle(this List pnts, out LoopList

ca2d.Center.GetDistanceTo(pnt)); + tpnts[3] = pnts.FindByMax(pnt => ca2d.Center.DistanceTo(pnt)); //如果最远点属于圆结束 while (!ca2d.IsIn(tpnts[3])) @@ -404,7 +408,7 @@ public static CircularArc2d GetMinCircle(this List pnts, out LoopList

ca2d.Center.GetDistanceTo(pnt)); + .FindByMax(pnt => ca2d.Center.DistanceTo(pnt)); } tpnts[0] = ptlst.First.Value; tpnts[1] = ptlst.First.Next.Value; @@ -413,7 +417,7 @@ public static CircularArc2d GetMinCircle(this List pnts, out LoopList

ca2d.Center.GetDistanceTo(pnt)); + tpnts[3] = pnts.FindByMax(pnt => ca2d.Center.DistanceTo(pnt)); } return ca2d; @@ -691,16 +695,16 @@ public static Point3d Point3d(this Point2d pt) { return new Point3d(pt.X, pt.Y, 0); } - + ///

/// 获取两个点之间的中点 /// /// 第一点 - /// 第二点 + /// 第二点 /// 返回两个点之间的中点 - public static Point3d GetMidPointTo(this Point3d pt1, Point3d pt2) + public static Point3d GetMidPointTo(this Point3d pt1, Point3d PointV) { - return new Point3d((pt1.X + pt2.X) * 0.5, (pt1.Y + pt2.Y) * 0.5, (pt1.Z + pt2.Z) * 0.5); + return new Point3d((pt1.X + PointV.X) * 0.5, (pt1.Y + PointV.Y) * 0.5, (pt1.Z + PointV.Z) * 0.5); } /// @@ -730,4 +734,4 @@ public static Point3d Polar(this Point3d pt, double ang, double len) return pt + Vector3d.XAxis.RotateBy(ang, Vector3d.ZAxis) * len; } } -} \ No newline at end of file +} diff --git a/src/IFoxCAD.Cad/ExtensionMethod/ObjectIdEx.cs b/src/IFoxCAD.Cad/ExtensionMethod/ObjectIdEx.cs index fef501c..2a89bd5 100644 --- a/src/IFoxCAD.Cad/ExtensionMethod/ObjectIdEx.cs +++ b/src/IFoxCAD.Cad/ExtensionMethod/ObjectIdEx.cs @@ -21,7 +21,8 @@ public static class ObjectIdEx /// 打开模式 /// 打开删除对象 /// 指定类型对象 - public static T GetObject(this ObjectId id, OpenMode mode = OpenMode.ForRead, bool openErased = false, Transaction tr = default) where T : DBObject + public static T GetObject(this ObjectId id, + OpenMode mode = OpenMode.ForRead, bool openErased = false, Transaction tr = default) where T : DBObject { tr ??= DBTrans.Top.Transaction; return tr.GetObject(id, mode, openErased) as T; @@ -36,11 +37,12 @@ public static T GetObject(this ObjectId id, OpenMode mode = OpenMode.ForRead, /// 打开模式 /// 打开删除对象 /// 指定类型对象集合 - public static IEnumerable GetObject(this IEnumerable ids, OpenMode mode = OpenMode.ForRead, bool openErased = false, Transaction tr = default) where T : DBObject + public static IEnumerable GetObject(this IEnumerable ids, + OpenMode mode = OpenMode.ForRead, bool openErased = false, Transaction tr = default) where T : DBObject { return ids.Select(id => id.GetObject(mode, openErased, tr)); } - + /// /// 返回符合类型的对象id /// @@ -50,11 +52,19 @@ public static IEnumerable GetObject(this IEnumerable ids, OpenMo public static IEnumerable OfType(this IEnumerable ids) where T : DBObject { string dxfName = RXClass.GetClass(typeof(T)).DxfName; - return - ids - .Where(id => id.ObjectClass.DxfName == dxfName); + return ids.Where(id => id.ObjectClass().DxfName == dxfName); + } + + //Acad08缺少 id.ObjectClass 如何补偿? + public static RXClass ObjectClass(this ObjectId id) + { +#if NET35 + return RXClass.GetClass(id.GetType()); +#else + return id.ObjectClass; +#endif } #endregion GetObject } -} \ No newline at end of file +} diff --git a/src/IFoxCAD.Cad/ExtensionMethod/SelectionSetEx.cs b/src/IFoxCAD.Cad/ExtensionMethod/SelectionSetEx.cs index e8e91f5..9ede888 100644 --- a/src/IFoxCAD.Cad/ExtensionMethod/SelectionSetEx.cs +++ b/src/IFoxCAD.Cad/ExtensionMethod/SelectionSetEx.cs @@ -7,7 +7,7 @@ namespace IFoxCAD.Cad { - + /// /// 选择集扩展类 /// @@ -47,7 +47,7 @@ public static IEnumerable GetObjectIds(this SelectionSet ss) where return ss .GetObjectIds() - .Where(id => id.ObjectClass.DxfName == dxfName); + .Where(id => id.ObjectClass().DxfName == dxfName); } /// @@ -60,7 +60,7 @@ public static IEnumerable> GetObjectIdGroup(this Sel return ss .GetObjectIds() - .GroupBy(id => id.ObjectClass.DxfName); + .GroupBy(id => id.ObjectClass().DxfName); } #endregion @@ -74,14 +74,14 @@ public static IEnumerable> GetObjectIdGroup(this Sel /// 事务 /// 打开模式 /// 图元集合 - public static IEnumerable GetEntities(this SelectionSet ss, OpenMode openMode=OpenMode.ForRead, Transaction tr = default) where T : Entity + public static IEnumerable GetEntities(this SelectionSet ss, OpenMode openMode = OpenMode.ForRead, Transaction tr = default) where T : Entity { return ss .GetObjectIds() .Select(id => tr.GetObject(id, openMode) as T); } - + #endregion #region ForEach @@ -103,4 +103,4 @@ public static void ForEach(this SelectionSet ss, Action action, OpenMode o } #endregion } -} \ No newline at end of file +} diff --git a/src/IFoxCAD.Cad/ExtensionMethod/SymbolTableEx.cs b/src/IFoxCAD.Cad/ExtensionMethod/SymbolTableEx.cs index bbeba00..a3f0687 100644 --- a/src/IFoxCAD.Cad/ExtensionMethod/SymbolTableEx.cs +++ b/src/IFoxCAD.Cad/ExtensionMethod/SymbolTableEx.cs @@ -51,8 +51,7 @@ public static ObjectId Rename(this SymbolTable tab { if (table.Has(Oldname)) { - table.Change(Oldname, ly => - { + table.Change(Oldname, ly => { ly.Name = NewName; } ); @@ -102,8 +101,7 @@ public static bool Delete(this SymbolTable table, /// TODO: 需要测试匿名块等特殊的块是否能定义 public static ObjectId Add(this SymbolTable table, string name, Action action = null, Func> ents = null, Func> attdef = null) { - return table.Add(name, btr => - { + return table.Add(name, btr => { action?.Invoke(btr); if (ents is not null) { @@ -206,8 +204,7 @@ public static ObjectId Add(this SymbolTable { return table.Add( name, - ltt => - { + ltt => { ltt.AsciiDescription = description; ltt.PatternLength = length; //线型的总长度 ltt.NumDashes = dash.Length; //组成线型的笔画数目 @@ -238,8 +235,7 @@ public static ObjectId Add(this SymbolTable - { + tstr => { tstr.Name = textStyleName; tstr.FileName = font; tstr.XScale = xscale; diff --git a/src/IFoxCAD.Cad/ExtensionMethod/SymbolTableRecordEx.cs b/src/IFoxCAD.Cad/ExtensionMethod/SymbolTableRecordEx.cs index 4f73f65..c66b2e2 100644 --- a/src/IFoxCAD.Cad/ExtensionMethod/SymbolTableRecordEx.cs +++ b/src/IFoxCAD.Cad/ExtensionMethod/SymbolTableRecordEx.cs @@ -27,19 +27,19 @@ public static class SymbolTableRecordEx /// /// 块表记录 /// 实体 - /// 事务管理器 + /// 事务管理器 /// 对象 id - public static ObjectId AddEntity(this BlockTableRecord btr, Entity entity, Transaction trans = null) + public static ObjectId AddEntity(this BlockTableRecord btr, Entity entity, Transaction tr = null) { if (entity is null) - throw new ArgumentNullException(nameof(entity),"对象为 null"); + throw new ArgumentNullException(nameof(entity), "对象为 null"); ObjectId id; - trans ??= DBTrans.Top.Transaction; + tr ??= DBTrans.Top.Transaction; using (btr.ForWrite()) { id = btr.AppendEntity(entity); - trans.AddNewlyCreatedDBObject(entity, true); + tr.AddNewlyCreatedDBObject(entity, true); } return id; } @@ -52,20 +52,19 @@ public static ObjectId AddEntity(this BlockTableRecord btr, Entity entity, Trans /// 事务 /// 实体集合 /// 对象 id 列表 - public static IEnumerable AddEntity(this BlockTableRecord btr, IEnumerable ents, Transaction trans = null) where T : Entity + public static IEnumerable AddEntity(this BlockTableRecord btr, IEnumerable ents, Transaction tr = null) where T : Entity { - if (ents.Any(ent => ent is null)) + if (ents.Any(ent => ent is null)) throw new ArgumentNullException(nameof(ents), "实体集合内存在 null 对象"); - trans ??= DBTrans.Top.Transaction; + tr ??= DBTrans.Top.Transaction; using (btr.ForWrite()) { return ents .Select( - ent => - { + ent => { ObjectId id = btr.AppendEntity(ent); - trans.AddNewlyCreatedDBObject(ent, true); + tr.AddNewlyCreatedDBObject(ent, true); return id; }) .ToList(); @@ -180,13 +179,13 @@ public static ObjectId AddPline(this BlockTableRecord btr, List pts, Li /// 轻多段线id public static ObjectId AddPline(this BlockTableRecord btr, List<(Point3d pt, double bulge, double startWidth, double endWidth)> pts, Action action = default, Transaction trans = default) { - + Polyline pl = new(); pts.ForEach((i, vertex) => { pl.AddVertexAt(i, vertex.pt.Point2d(), vertex.bulge, vertex.startWidth, vertex.endWidth); }); - + return btr.AddEnt(pl, action, trans); } #endif @@ -238,7 +237,7 @@ public static IEnumerable GetObjectIds(this BlockTableRecord btr) w { string dxfName = RXClass.GetClass(typeof(T)).DxfName; return btr.Cast() - .Where(id => id.ObjectClass.DxfName == dxfName); + .Where(id => id.ObjectClass().DxfName == dxfName); } /// @@ -251,7 +250,7 @@ public static IEnumerable> GetObjectIds(this BlockTa return btr .Cast() - .GroupBy(id => id.ObjectClass.DxfName); + .GroupBy(id => id.ObjectClass().DxfName); } /// @@ -306,9 +305,9 @@ public static ObjectId InsertBlock(this BlockTableRecord blockTableRecord, Point ObjectId blockId, Scale3d scale = default, double rotation = default, - Dictionary atts = default, Transaction trans = null) + Dictionary atts = default, Transaction tr = null) { - trans ??= DBTrans.Top.Transaction; + tr ??= DBTrans.Top.Transaction; if (!DBTrans.Top.BlockTable.Has(blockId)) { DBTrans.Top.Editor.WriteMessage($"\n不存在名字为{DBTrans.Top.GetObject(blockId).Name}的块定义。"); @@ -335,12 +334,10 @@ public static ObjectId InsertBlock(this BlockTableRecord blockTableRecord, Point attref.Position = attdef.Position.TransformBy(blockref.BlockTransform); attref.AdjustAlignment(DBTrans.Top.Database); if (atts.ContainsKey(attdef.Tag)) - { attref.TextString = atts[attdef.Tag]; - } blockref.AttributeCollection.AppendAttribute(attref); - trans.AddNewlyCreatedDBObject(attref, true); + tr.AddNewlyCreatedDBObject(attref, true); } } } diff --git a/src/IFoxCAD.Cad/IFoxCAD.Cad.csproj b/src/IFoxCAD.Cad/IFoxCAD.Cad.csproj index b28ee58..9227301 100644 --- a/src/IFoxCAD.Cad/IFoxCAD.Cad.csproj +++ b/src/IFoxCAD.Cad/IFoxCAD.Cad.csproj @@ -1,5 +1,4 @@ - - + net35;net40 true @@ -22,12 +21,10 @@ - - runtime - + - + runtime @@ -42,8 +39,8 @@ $(Configuration);ac2013 - - + + True diff --git a/src/IFoxCAD.Cad/IFoxCAD.Cad.csproj.data b/src/IFoxCAD.Cad/IFoxCAD.Cad.csproj.data index e69de29..5f28270 100644 --- a/src/IFoxCAD.Cad/IFoxCAD.Cad.csproj.data +++ b/src/IFoxCAD.Cad/IFoxCAD.Cad.csproj.data @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/IFoxCAD.Cad/ResultData/LispDottedPair.cs b/src/IFoxCAD.Cad/ResultData/LispDottedPair.cs index 9acad02..0aa2f69 100644 --- a/src/IFoxCAD.Cad/ResultData/LispDottedPair.cs +++ b/src/IFoxCAD.Cad/ResultData/LispDottedPair.cs @@ -69,4 +69,4 @@ public override List Value #endregion } -} \ No newline at end of file +} diff --git a/src/IFoxCAD.Cad/ResultData/LispList.cs b/src/IFoxCAD.Cad/ResultData/LispList.cs index 8f87c2f..4742d38 100644 --- a/src/IFoxCAD.Cad/ResultData/LispList.cs +++ b/src/IFoxCAD.Cad/ResultData/LispList.cs @@ -199,4 +199,4 @@ public void Add(LispList value) public static implicit operator LispList(TypedValue[] values) => new(values); #endregion } -} \ No newline at end of file +} diff --git a/src/IFoxCAD.Cad/ResultData/TypedValueList.cs b/src/IFoxCAD.Cad/ResultData/TypedValueList.cs index c82d464..2fccce5 100644 --- a/src/IFoxCAD.Cad/ResultData/TypedValueList.cs +++ b/src/IFoxCAD.Cad/ResultData/TypedValueList.cs @@ -66,4 +66,4 @@ public override string ToString() } #endregion } -} \ No newline at end of file +} diff --git a/src/IFoxCAD.Cad/ResultData/XRecordDataList.cs b/src/IFoxCAD.Cad/ResultData/XRecordDataList.cs index 1aa2304..0364f9a 100644 --- a/src/IFoxCAD.Cad/ResultData/XRecordDataList.cs +++ b/src/IFoxCAD.Cad/ResultData/XRecordDataList.cs @@ -62,4 +62,4 @@ public void Add(DxfCode code, object obj) public static implicit operator XRecordDataList(TypedValue[] values) => new(values); #endregion } -} \ No newline at end of file +} diff --git a/src/IFoxCAD.Cad/ResultData/XdataList.cs b/src/IFoxCAD.Cad/ResultData/XdataList.cs index c31f53c..e22ffe2 100644 --- a/src/IFoxCAD.Cad/ResultData/XdataList.cs +++ b/src/IFoxCAD.Cad/ResultData/XdataList.cs @@ -16,7 +16,7 @@ public XDataList() } public XDataList(IEnumerable values) : base(values) { } - + #region 添加数据 /// /// 添加数据 @@ -68,4 +68,4 @@ public void Add(DxfCode code, object obj) public static implicit operator XDataList(TypedValue[] values) => new(values); #endregion } -} \ No newline at end of file +} diff --git a/src/IFoxCAD.Cad/Runtime/AcadVersion.cs b/src/IFoxCAD.Cad/Runtime/AcadVersion.cs index 4e33b58..a0f977c 100644 --- a/src/IFoxCAD.Cad/Runtime/AcadVersion.cs +++ b/src/IFoxCAD.Cad/Runtime/AcadVersion.cs @@ -127,4 +127,4 @@ public override string ToString() $"名称:{ProductName}\n版本号:{ProgId}\n注册表位置:{ProductRootKey}"; } } -} \ No newline at end of file +} diff --git a/src/IFoxCAD.Cad/Runtime/AssemInfo.cs b/src/IFoxCAD.Cad/Runtime/AssemInfo.cs index 5974800..62b70f8 100644 --- a/src/IFoxCAD.Cad/Runtime/AssemInfo.cs +++ b/src/IFoxCAD.Cad/Runtime/AssemInfo.cs @@ -33,4 +33,4 @@ public struct AssemInfo /// public string Description { get; set; } } -} \ No newline at end of file +} diff --git a/src/IFoxCAD.Cad/Runtime/AutoRegAssem.cs b/src/IFoxCAD.Cad/Runtime/AutoRegAssem.cs index 0042dd2..1852003 100644 --- a/src/IFoxCAD.Cad/Runtime/AutoRegAssem.cs +++ b/src/IFoxCAD.Cad/Runtime/AutoRegAssem.cs @@ -112,9 +112,9 @@ public void RegApp() appkey.Close(); } -#endregion RegApp + #endregion RegApp -#region IExtensionApplication 成员 + #region IExtensionApplication 成员 /// /// 初始化函数 @@ -126,6 +126,6 @@ public void RegApp() /// public abstract void Terminate(); -#endregion IExtensionApplication 成员 + #endregion IExtensionApplication 成员 } -} \ No newline at end of file +} diff --git a/src/IFoxCAD.Cad/Runtime/DBTrans.cs b/src/IFoxCAD.Cad/Runtime/DBTrans.cs index 9175048..555153b 100644 --- a/src/IFoxCAD.Cad/Runtime/DBTrans.cs +++ b/src/IFoxCAD.Cad/Runtime/DBTrans.cs @@ -1,14 +1,8 @@ using System; using System.Collections.Generic; -using System.Linq; -using System.Text; - - -using Autodesk.AutoCAD.Runtime; using Autodesk.AutoCAD.DatabaseServices; using Autodesk.AutoCAD.EditorInput; using Autodesk.AutoCAD.ApplicationServices; -using Autodesk.AutoCAD.Geometry; using System.IO; namespace IFoxCAD.Cad @@ -81,7 +75,7 @@ public DBTrans(Document doc = null, bool commit = true, bool doclock = false) public DBTrans(Database database, bool commit = true) { Database = database; - Init(commit,false); + Init(commit, false); } /// /// 构造函数,打开文件,默认提交事务 @@ -93,7 +87,7 @@ public DBTrans(string fileName, bool commit = true) Database = new Database(false, true); Database.ReadDwgFile(fileName, FileShare.Read, true, null); Database.CloseInput(true); - Init(commit,false); + Init(commit, false); } /// /// 初始化事务及事务队列、提交模式 @@ -251,7 +245,7 @@ public T GetObject(ObjectId id, } -#endregion + #endregion @@ -272,7 +266,7 @@ public void Commit() { Abort(); } - + } protected virtual void Dispose(bool disposing) diff --git a/src/IFoxCAD.Cad/Runtime/Env.cs b/src/IFoxCAD.Cad/Runtime/Env.cs index e6cf84e..d2919ca 100644 --- a/src/IFoxCAD.Cad/Runtime/Env.cs +++ b/src/IFoxCAD.Cad/Runtime/Env.cs @@ -1,4 +1,4 @@ -using Autodesk.AutoCAD.ApplicationServices; +using Autodesk.AutoCAD.ApplicationServices; using Autodesk.AutoCAD.DatabaseServices; using Autodesk.AutoCAD.EditorInput; using Autodesk.AutoCAD.GraphicsSystem; @@ -443,4 +443,4 @@ public static void SetEnv(string var, string? value) #nullable disable #endregion } -} \ No newline at end of file +} diff --git a/src/IFoxCAD.Cad/Runtime/SymbolTable.cs b/src/IFoxCAD.Cad/Runtime/SymbolTable.cs index 374ab58..b141835 100644 --- a/src/IFoxCAD.Cad/Runtime/SymbolTable.cs +++ b/src/IFoxCAD.Cad/Runtime/SymbolTable.cs @@ -1,14 +1,14 @@ -using System; +using System; using System.Collections; using System.Collections.Generic; -using System.Linq; +using System.Linq; using Autodesk.AutoCAD.DatabaseServices; namespace IFoxCAD.Cad { - public class SymbolTable : IEnumerable - where TTable : SymbolTable + public class SymbolTable : IEnumerable + where TTable : SymbolTable where TRecord : SymbolTableRecord, new() - { + { #region 程序集内部属性 /// /// 事务管理器 @@ -50,7 +50,7 @@ internal SymbolTable(DBTrans tr, ObjectId tableId) /// 对象的id public ObjectId this[string key] { - get + get { if (Has(key)) { @@ -146,7 +146,7 @@ public void Remove(string name) { Remove(record); } - + } /// /// 删除符号表记录 @@ -273,7 +273,7 @@ public ObjectId GetRecordFrom(SymbolTable table, string name, b { ObjectId id = table[name]; using IdMapping idm = new(); - using (ObjectIdCollection ids = new(){ id }) + using (ObjectIdCollection ids = new() { id }) { table.Database.WblockCloneObjects(ids, CurrentSymbolTable.Id, idm, DuplicateRecordCloning.Replace, false); } diff --git a/src/IFoxCAD.Cad/SelectionFilter/OpComp.cs b/src/IFoxCAD.Cad/SelectionFilter/OpComp.cs index 4e1fb16..567be46 100644 --- a/src/IFoxCAD.Cad/SelectionFilter/OpComp.cs +++ b/src/IFoxCAD.Cad/SelectionFilter/OpComp.cs @@ -80,4 +80,4 @@ public override IEnumerable GetValues() yield return Value; } } -} \ No newline at end of file +} diff --git a/src/IFoxCAD.Cad/SelectionFilter/OpEqual.cs b/src/IFoxCAD.Cad/SelectionFilter/OpEqual.cs index dac84d5..76dc738 100644 --- a/src/IFoxCAD.Cad/SelectionFilter/OpEqual.cs +++ b/src/IFoxCAD.Cad/SelectionFilter/OpEqual.cs @@ -87,4 +87,4 @@ public void SetValue(int code, object value) Value = new TypedValue(code, value); } } -} \ No newline at end of file +} diff --git a/src/IFoxCAD.Cad/SelectionFilter/OpFilter.cs b/src/IFoxCAD.Cad/SelectionFilter/OpFilter.cs index 23ef25b..cd73129 100644 --- a/src/IFoxCAD.Cad/SelectionFilter/OpFilter.cs +++ b/src/IFoxCAD.Cad/SelectionFilter/OpFilter.cs @@ -339,4 +339,4 @@ private static Op GetCompOp(string content, Op left, object right) #endregion Operator } -} \ No newline at end of file +} diff --git a/src/IFoxCAD.WPF/IFoxCAD.WPF.csproj b/src/IFoxCAD.WPF/IFoxCAD.WPF.csproj index c23b004..8690b9e 100644 --- a/src/IFoxCAD.WPF/IFoxCAD.WPF.csproj +++ b/src/IFoxCAD.WPF/IFoxCAD.WPF.csproj @@ -1,7 +1,8 @@  - net45 + + NET45;NET46;NET47;NET48 true true true @@ -21,18 +22,18 @@ - DEBUG;TRACE + DEBUG;TRACE - + - - True - - + + True + + diff --git a/tests/DBTrans.test/DBTrans.test.csproj b/tests/DBTrans.test/DBTrans.test.csproj index a6f4773..d856f39 100644 --- a/tests/DBTrans.test/DBTrans.test.csproj +++ b/tests/DBTrans.test/DBTrans.test.csproj @@ -1,10 +1,18 @@  - net45 + + preview + + 1.0.0.* + 1.0.0.0 + False + git + + + NET45;NET46;NET47;NET48 true true - preview @@ -12,4 +20,5 @@ + diff --git a/tests/DBTrans.test/Test.cs b/tests/DBTrans.test/Test.cs index e942ff4..4f7265c 100644 --- a/tests/DBTrans.test/Test.cs +++ b/tests/DBTrans.test/Test.cs @@ -1,24 +1,17 @@ using System; - using System.Collections.Generic; using System.Linq; -using System.Text; -using System.Threading.Tasks; -using System.Diagnostics; -using System.Collections; +using System.IO; using Autodesk.AutoCAD.ApplicationServices; using Autodesk.AutoCAD.EditorInput; using Autodesk.AutoCAD.Runtime; using Autodesk.AutoCAD.Geometry; using Autodesk.AutoCAD.DatabaseServices; -using Autodesk.AutoCAD.Internal; +using Autodesk.AutoCAD.Colors; using IFoxCAD.Cad; -using Autodesk.AutoCAD.Colors; -using IFoxCAD.WPF; using test.wpf; -using System.IO; namespace test { @@ -45,7 +38,7 @@ public void Dbtest() Circle circle = new(new Point3d(0, 0, 0), Vector3d.ZAxis, 2); //var lienid = tr.AddEntity(line); //var cirid = tr.AddEntity(circle); - //var linent = tr.GetObject(lienid); + //var linent = tr.GetObject(lienid); //var lineent = tr.GetObject(cirid); //var linee = tr.GetObject(cirid); //经测试,类型不匹配,返回null //var dd = tr.GetObject(lienid); @@ -70,8 +63,8 @@ public void drawarc() { using var tr = new DBTrans(); Arc arc1 = EntityEx.CreateArcSCE(new Point3d(2, 0, 0), new Point3d(0, 0, 0), new Point3d(0, 2, 0));//起点,圆心,终点 - Arc arc2 = EntityEx.CreateArc(new Point3d(4, 0, 0), new Point3d(0, 0, 0), Math.PI / 2);//起点,圆心,弧度 - Arc arc3 = EntityEx.CreateArc(new Point3d(1, 0, 0), new Point3d(0, 0, 0), new Point3d(0, 1, 0));//起点,圆上一点,终点 + Arc arc2 = EntityEx.CreateArc(new Point3d(4, 0, 0), new Point3d(0, 0, 0), Math.PI / 2); //起点,圆心,弧度 + Arc arc3 = EntityEx.CreateArc(new Point3d(1, 0, 0), new Point3d(0, 0, 0), new Point3d(0, 1, 0)); //起点,圆上一点,终点 tr.CurrentSpace.AddEntity(arc1, arc2, arc3); tr.CurrentSpace.AddArc(new Point3d(0, 0, 0), new Point3d(1, 1, 0), new Point3d(2, 0, 0));//起点,圆上一点,终点 } @@ -80,7 +73,7 @@ public void drawarc() public void draCircle() { using var tr = new DBTrans(); - Circle circle1 = EntityEx.CreateCircle(new Point3d(0, 0, 0), new Point3d(1, 0, 0));//起点,终点 + Circle circle1 = EntityEx.CreateCircle(new Point3d(0, 0, 0), new Point3d(1, 0, 0)); //起点,终点 Circle circle2 = EntityEx.CreateCircle(new Point3d(-2, 0, 0), new Point3d(2, 0, 0), new Point3d(0, 2, 0));//三点画圆,成功 Circle circle3 = EntityEx.CreateCircle(new Point3d(-2, 0, 0), new Point3d(0, 0, 0), new Point3d(2, 0, 0));//起点,圆心,终点,失败 tr.CurrentSpace.AddEntity(circle1, circle2); @@ -102,16 +95,14 @@ public void Layertest() { using var tr = new DBTrans(); tr.LayerTable.Add("1"); - tr.LayerTable.Add("2", lt => - { - lt.Color = Color.FromColorIndex(ColorMethod.ByColor, 1); + tr.LayerTable.Add("2", lt => { + lt.Color = Color.FromColorIndex(ColorMethod.ByColor, 1); lt.LineWeight = LineWeight.LineWeight030; }); tr.LayerTable.Remove("3"); tr.LayerTable.Delete("0"); - tr.LayerTable.Change("4", lt => - { + tr.LayerTable.Change("4", lt => { lt.Color = Color.FromColorIndex(ColorMethod.ByColor, 2); }); } @@ -138,11 +129,11 @@ public void Layertest2() public void LayerDel() { using var tr = new DBTrans(); - Env.Editor.WriteMessage(tr.LayerTable.Delete("0").ToString()); //删除图层 0 + Env.Editor.WriteMessage(tr.LayerTable.Delete("0").ToString()); //删除图层 0 Env.Editor.WriteMessage(tr.LayerTable.Delete("Defpoints").ToString());//删除图层 Defpoints - Env.Editor.WriteMessage(tr.LayerTable.Delete("1").ToString());//删除不存在的图层 1 - Env.Editor.WriteMessage(tr.LayerTable.Delete("2").ToString());//删除有图元的图层 2 - Env.Editor.WriteMessage(tr.LayerTable.Delete("3").ToString());//删除图层 3 + Env.Editor.WriteMessage(tr.LayerTable.Delete("1").ToString()); //删除不存在的图层 1 + Env.Editor.WriteMessage(tr.LayerTable.Delete("2").ToString()); //删除有图元的图层 2 + Env.Editor.WriteMessage(tr.LayerTable.Delete("3").ToString()); //删除图层 3 tr.LayerTable.Remove("2"); //测试是否能强制删除 } @@ -213,8 +204,7 @@ public void BlockDef() using var tr = new DBTrans(); //var line = new Line(new Point3d(0, 0, 0), new Point3d(1, 1, 0)); tr.BlockTable.Add("test", - btr => - { + btr => { btr.Origin = new Point3d(0, 0, 0); }, () => //图元 @@ -237,8 +227,7 @@ public void BlockDefChange() { using var tr = new DBTrans(); //var line = new Line(new Point3d(0, 0, 0), new Point3d(1, 1, 0)); - tr.BlockTable.Change("test", btr => - { + tr.BlockTable.Change("test", btr => { btr.Origin = new Point3d(5, 5, 0); btr.AddEntity(new Circle(new Point3d(0, 0, 0), Vector3d.ZAxis, 2)); btr.GetEntities() @@ -280,7 +269,7 @@ public void InsertBlockDef() { "tagTest3", "1" }, { "tagTest4", "" } }; - tr.CurrentSpace.InsertBlock(new Point3d(10,10, 0), "test2", atts: def2); + tr.CurrentSpace.InsertBlock(new Point3d(10, 10, 0), "test2", atts: def2); } @@ -289,8 +278,7 @@ public void TestClipBlock() { using var tr = new DBTrans(); tr.BlockTable.Add("test1", - btr => - { + btr => { btr.Origin = new Point3d(0, 0, 0); btr.AddEntity(new Line(new Point3d(0, 0, 0), new Point3d(10, 10, 0)), new Line(new Point3d(10, 10, 0), new Point3d(10, 0, 0)) @@ -305,7 +293,7 @@ public void TestClipBlock() var id1 = tr.CurrentSpace.InsertBlock(new Point3d(20, 20, 0), "test1"); var bref1 = tr.GetObject(id); - + bref1.ClipBlockRef(new Point3d(13, 13, 0), new Point3d(17, 17, 0)); } diff --git a/tests/DBTrans.test/testenv.cs b/tests/DBTrans.test/testenv.cs index e2513b5..c665641 100644 --- a/tests/DBTrans.test/testenv.cs +++ b/tests/DBTrans.test/testenv.cs @@ -1,11 +1,5 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; -using IFoxCAD.Cad; +using IFoxCAD.Cad; using Autodesk.AutoCAD.Runtime; -using Autodesk.AutoCAD.ApplicationServices; namespace test { @@ -14,16 +8,12 @@ public class testenv [CommandMethod("testenum")] public void testenum() { - Env.CmdEcho = true; - } [CommandMethod("testenum1")] public void testenum1() { - Env.CmdEcho = false; - } [CommandMethod("testdimblk")] diff --git a/tests/DBTrans.test/testselectfilter.cs b/tests/DBTrans.test/testselectfilter.cs index 1224a2f..9b658ee 100644 --- a/tests/DBTrans.test/testselectfilter.cs +++ b/tests/DBTrans.test/testselectfilter.cs @@ -17,13 +17,13 @@ public class testselectfilter [CommandMethod("testfilter")] public void testfilter() { - + var p = new Point3d(10, 10, 0); var f = OpFilter.Bulid( - e =>!(e.Dxf(0) == "line" & e.Dxf(8) == "0") + e => !(e.Dxf(0) == "line" & e.Dxf(8) == "0") | e.Dxf(0) != "circle" & e.Dxf(8) == "2" & e.Dxf(10) >= p); - - + + var f2 = OpFilter.Bulid( e => e.Or( !e.And(e.Dxf(0) == "line", e.Dxf(8) == "0"), @@ -39,10 +39,7 @@ public void testfilter() [CommandMethod("testselectanpoint")] public void testselectanpoint() { - - var sel2 = Env.Editor.SelectAtPoint(new Point3d(0, 0, 0)); - Env.Editor.WriteMessage(""); } } -- Gitee From f41578b79805257e3e33a20d55fcb5ec3683a48e Mon Sep 17 00:00:00 2001 From: liuqihong <540762622@qq.com> Date: Thu, 2 Dec 2021 02:45:17 +0800 Subject: [PATCH 002/675] =?UTF-8?q?=E4=BF=AE=E6=94=B9csproj=E6=A0=BC?= =?UTF-8?q?=E5=BC=8F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.en.md | 36 - src/IFoxCAD.Basal/IFoxCAD.Basal.csproj | 66 +- src/IFoxCAD.Cad/ExtensionMethod/EditorEx.cs | 2 +- tests/DBTrans.test/Test.cs | 974 ++++++++++---------- 4 files changed, 526 insertions(+), 552 deletions(-) delete mode 100644 README.en.md diff --git a/README.en.md b/README.en.md deleted file mode 100644 index 7c3d2c4..0000000 --- a/README.en.md +++ /dev/null @@ -1,36 +0,0 @@ -# IFoxCAD - -#### Description -基于.NET的Cad二次开发类库 - -#### Software Architecture -Software architecture description - -#### Installation - -1. xxxx -2. xxxx -3. xxxx - -#### Instructions - -1. xxxx -2. xxxx -3. xxxx - -#### Contribution - -1. Fork the repository -2. Create Feat_xxx branch -3. Commit your code -4. Create Pull Request - - -#### Gitee Feature - -1. You can use Readme\_XXX.md to support different languages, such as Readme\_en.md, Readme\_zh.md -2. Gitee blog [blog.gitee.com](https://blog.gitee.com) -3. Explore open source project [https://gitee.com/explore](https://gitee.com/explore) -4. The most valuable open source project [GVP](https://gitee.com/gvp) -5. The manual of Gitee [https://gitee.com/help](https://gitee.com/help) -6. The most popular members [https://gitee.com/gitee-stars/](https://gitee.com/gitee-stars/) diff --git a/src/IFoxCAD.Basal/IFoxCAD.Basal.csproj b/src/IFoxCAD.Basal/IFoxCAD.Basal.csproj index 8ae4570..b3cec15 100644 --- a/src/IFoxCAD.Basal/IFoxCAD.Basal.csproj +++ b/src/IFoxCAD.Basal/IFoxCAD.Basal.csproj @@ -1,35 +1,43 @@  - - net35;net40 - true - 0.1.1 - InspireFunction - xsfhlzh;vicwjb - 基于.NET的二次开发基本类库 - InspireFunction - https://gitee.com/inspirefunction/ifoxcad - https://gitee.com/inspirefunction/ifoxcad.git - git - IFoxCAD;C#;NET;Common;Basal - 增加在net35支持 - true - true - preview - true - LICENSE - true - + + + preview + + 1.0.0.* + 1.0.0.0 + False + net35;net40 + 0.1.1 - - - + true + true + InspireFunction + xsfhlzh;vicwjb + 基于.NET的二次开发基本类库 + InspireFunction + git + https://gitee.com/inspirefunction/ifoxcad.git - - - True - - - + https://gitee.com/inspirefunction/ifoxcad + IFoxCAD;C#;NET;Common;Basal + 增加在net35支持 + true + true + + LICENSE + true + + + + + + + + + True + + + diff --git a/src/IFoxCAD.Cad/ExtensionMethod/EditorEx.cs b/src/IFoxCAD.Cad/ExtensionMethod/EditorEx.cs index f7234c8..2175194 100644 --- a/src/IFoxCAD.Cad/ExtensionMethod/EditorEx.cs +++ b/src/IFoxCAD.Cad/ExtensionMethod/EditorEx.cs @@ -638,7 +638,7 @@ public static void ZoomExtents(this Editor ed, double offsetDist = 0.00) public static void ZoomObject(this Editor ed, Entity ent, double offsetDist = 0.00) { Extents3d ext = ent.GeometricExtents; - ed.ZoomWindow(ext.MinPoint, ext.MinPoint, offsetDist); + ed.ZoomWindow(ext.MinPoint, ext.MaxPoint, offsetDist); } #endregion diff --git a/tests/DBTrans.test/Test.cs b/tests/DBTrans.test/Test.cs index c05b5c5..c261a33 100644 --- a/tests/DBTrans.test/Test.cs +++ b/tests/DBTrans.test/Test.cs @@ -14,181 +14,181 @@ using test.wpf; -namespace test; - -public class Test +namespace test { - [CommandMethod("dbtest")] - public void Dbtest() + public class Test { - using var tr = new DBTrans(); - tr.Editor.WriteMessage("\n测试 Editor 属性是否工作!"); - tr.Editor.WriteMessage("\n----------开始测试--------------"); - tr.Editor.WriteMessage("\n测试document属性是否工作"); - if (tr.Document == Getdoc()) + [CommandMethod("dbtest")] + public void Dbtest() { - tr.Editor.WriteMessage("\ndocument 正常"); + using var tr = new DBTrans(); + tr.Editor.WriteMessage("\n测试 Editor 属性是否工作!"); + tr.Editor.WriteMessage("\n----------开始测试--------------"); + tr.Editor.WriteMessage("\n测试document属性是否工作"); + if (tr.Document == Getdoc()) + { + tr.Editor.WriteMessage("\ndocument 正常"); + } + tr.Editor.WriteMessage("\n测试database属性是否工作"); + if (tr.Database == Getdb()) + { + tr.Editor.WriteMessage("\ndatabase 正常"); + } + + Line line = new(new Point3d(0, 0, 0), new Point3d(1, 1, 0)); + Circle circle = new(new Point3d(0, 0, 0), Vector3d.ZAxis, 2); + //var lienid = tr.AddEntity(line); + //var cirid = tr.AddEntity(circle); + //var linent = tr.GetObject(lienid); + //var lineent = tr.GetObject(cirid); + //var linee = tr.GetObject(cirid); //经测试,类型不匹配,返回null + //var dd = tr.GetObject(lienid); + //List ds = new() { linee, dd }; } - tr.Editor.WriteMessage("\n测试database属性是否工作"); - if (tr.Database == Getdb()) + + //add entity test + [CommandMethod("addent")] + public void Addent() { - tr.Editor.WriteMessage("\ndatabase 正常"); + using var tr = new DBTrans(); + Line line = new(new Point3d(0, 0, 0), new Point3d(1, 1, 0)); + tr.CurrentSpace.AddEntity(line); + Line line1 = new(new Point3d(10, 10, 0), new Point3d(41, 1, 0)); + tr.ModelSpace.AddEntity(line1); + Line line2 = new(new Point3d(-10, 10, 0), new Point3d(41, 1, 0)); + tr.PaperSpace.AddEntity(line2); } - Line line = new(new Point3d(0, 0, 0), new Point3d(1, 1, 0)); - Circle circle = new(new Point3d(0, 0, 0), Vector3d.ZAxis, 2); - //var lienid = tr.AddEntity(line); - //var cirid = tr.AddEntity(circle); - //var linent = tr.GetObject(lienid); - //var lineent = tr.GetObject(cirid); - //var linee = tr.GetObject(cirid); //经测试,类型不匹配,返回null - //var dd = tr.GetObject(lienid); - //List ds = new() { linee, dd }; - } - - //add entity test - [CommandMethod("addent")] - public void Addent() - { - using var tr = new DBTrans(); - Line line = new(new Point3d(0, 0, 0), new Point3d(1, 1, 0)); - tr.CurrentSpace.AddEntity(line); - Line line1 = new(new Point3d(10, 10, 0), new Point3d(41, 1, 0)); - tr.ModelSpace.AddEntity(line1); - Line line2 = new(new Point3d(-10, 10, 0), new Point3d(41, 1, 0)); - tr.PaperSpace.AddEntity(line2); - } - - [CommandMethod("drawarc")] - public void drawarc() - { - using var tr = new DBTrans(); - Arc arc1 = EntityEx.CreateArcSCE(new Point3d(2, 0, 0), new Point3d(0, 0, 0), new Point3d(0, 2, 0));//起点,圆心,终点 - Arc arc2 = EntityEx.CreateArc(new Point3d(4, 0, 0), new Point3d(0, 0, 0), Math.PI / 2);//起点,圆心,弧度 - Arc arc3 = EntityEx.CreateArc(new Point3d(1, 0, 0), new Point3d(0, 0, 0), new Point3d(0, 1, 0));//起点,圆上一点,终点 - tr.CurrentSpace.AddEntity(arc1, arc2, arc3); - tr.CurrentSpace.AddArc(new Point3d(0, 0, 0), new Point3d(1, 1, 0), new Point3d(2, 0, 0));//起点,圆上一点,终点 - } + [CommandMethod("drawarc")] + public void drawarc() + { + using var tr = new DBTrans(); + Arc arc1 = EntityEx.CreateArcSCE(new Point3d(2, 0, 0), new Point3d(0, 0, 0), new Point3d(0, 2, 0));//起点,圆心,终点 + Arc arc2 = EntityEx.CreateArc(new Point3d(4, 0, 0), new Point3d(0, 0, 0), Math.PI / 2); //起点,圆心,弧度 + Arc arc3 = EntityEx.CreateArc(new Point3d(1, 0, 0), new Point3d(0, 0, 0), new Point3d(0, 1, 0)); //起点,圆上一点,终点 + tr.CurrentSpace.AddEntity(arc1, arc2, arc3); + tr.CurrentSpace.AddArc(new Point3d(0, 0, 0), new Point3d(1, 1, 0), new Point3d(2, 0, 0));//起点,圆上一点,终点 + } - [CommandMethod("drawcircle")] - public void draCircle() - { - using var tr = new DBTrans(); - Circle circle1 = EntityEx.CreateCircle(new Point3d(0, 0, 0), new Point3d(1, 0, 0));//起点,终点 - Circle circle2 = EntityEx.CreateCircle(new Point3d(-2, 0, 0), new Point3d(2, 0, 0), new Point3d(0, 2, 0));//三点画圆,成功 - Circle circle3 = EntityEx.CreateCircle(new Point3d(-2, 0, 0), new Point3d(0, 0, 0), new Point3d(2, 0, 0));//起点,圆心,终点,失败 - tr.CurrentSpace.AddEntity(circle1, circle2); - if (circle3 is not null) + [CommandMethod("drawcircle")] + public void draCircle() { + using var tr = new DBTrans(); + Circle circle1 = EntityEx.CreateCircle(new Point3d(0, 0, 0), new Point3d(1, 0, 0)); //起点,终点 + Circle circle2 = EntityEx.CreateCircle(new Point3d(-2, 0, 0), new Point3d(2, 0, 0), new Point3d(0, 2, 0));//三点画圆,成功 + Circle circle3 = EntityEx.CreateCircle(new Point3d(-2, 0, 0), new Point3d(0, 0, 0), new Point3d(2, 0, 0));//起点,圆心,终点,失败 + tr.CurrentSpace.AddEntity(circle1, circle2); + if (circle3 is not null) + { + tr.CurrentSpace.AddEntity(circle3); + } + else + { + tr.Editor.WriteMessage("三点画圆失败"); + } tr.CurrentSpace.AddEntity(circle3); + tr.CurrentSpace.AddCircle(new Point3d(0, 0, 0), new Point3d(1, 1, 0), new Point3d(2, 0, 0));//三点画圆,成功 + tr.CurrentSpace.AddCircle(new Point3d(0, 0, 0), new Point3d(1, 1, 0), new Point3d(2, 2, 0));//起点,圆上一点,终点(共线) } - else + + [CommandMethod("layertest")] + public void Layertest() { - tr.Editor.WriteMessage("三点画圆失败"); + using var tr = new DBTrans(); + tr.LayerTable.Add("1"); + tr.LayerTable.Add("2", lt => + { + lt.Color = Color.FromColorIndex(ColorMethod.ByColor, 1); + lt.LineWeight = LineWeight.LineWeight030; + + }); + tr.LayerTable.Remove("3"); + tr.LayerTable.Delete("0"); + tr.LayerTable.Change("4", lt => + { + lt.Color = Color.FromColorIndex(ColorMethod.ByColor, 2); + }); } - tr.CurrentSpace.AddEntity(circle3); - tr.CurrentSpace.AddCircle(new Point3d(0, 0, 0), new Point3d(1, 1, 0), new Point3d(2, 0, 0));//三点画圆,成功 - tr.CurrentSpace.AddCircle(new Point3d(0, 0, 0), new Point3d(1, 1, 0), new Point3d(2, 2, 0));//起点,圆上一点,终点(共线) - } - [CommandMethod("layertest")] - public void Layertest() - { - using var tr = new DBTrans(); - tr.LayerTable.Add("1"); - tr.LayerTable.Add("2", lt => - { - lt.Color = Color.FromColorIndex(ColorMethod.ByColor, 1); - lt.LineWeight = LineWeight.LineWeight030; - }); - tr.LayerTable.Remove("3"); - tr.LayerTable.Delete("0"); - tr.LayerTable.Change("4", lt => + //添加图层 + [CommandMethod("layerAdd1")] + public void Layertest1() { - lt.Color = Color.FromColorIndex(ColorMethod.ByColor, 2); - }); - } - + using var tr = new DBTrans(); + tr.LayerTable.Add("test1", Color.FromColorIndex(ColorMethod.ByColor, 1)); + } - //添加图层 - [CommandMethod("layerAdd1")] - public void Layertest1() - { - using var tr = new DBTrans(); - tr.LayerTable.Add("test1", Color.FromColorIndex(ColorMethod.ByColor, 1)); - } + //添加图层 + [CommandMethod("layerAdd2")] + public void Layertest2() + { + using var tr = new DBTrans(); + tr.LayerTable.Add("test2", 2); + //tr.LayerTable["3"] = new LayerTableRecord(); + } + //删除图层 + [CommandMethod("layerdel")] + public void LayerDel() + { + using var tr = new DBTrans(); + Env.Editor.WriteMessage(tr.LayerTable.Delete("0").ToString()); //删除图层 0 + Env.Editor.WriteMessage(tr.LayerTable.Delete("Defpoints").ToString());//删除图层 Defpoints + Env.Editor.WriteMessage(tr.LayerTable.Delete("1").ToString()); //删除不存在的图层 1 + Env.Editor.WriteMessage(tr.LayerTable.Delete("2").ToString()); //删除有图元的图层 2 + Env.Editor.WriteMessage(tr.LayerTable.Delete("3").ToString()); //删除图层 3 - //添加图层 - [CommandMethod("layerAdd2")] - public void Layertest2() - { - using var tr = new DBTrans(); - tr.LayerTable.Add("test2", 2); - //tr.LayerTable["3"] = new LayerTableRecord(); - } - //删除图层 - [CommandMethod("layerdel")] - public void LayerDel() - { - using var tr = new DBTrans(); - Env.Editor.WriteMessage(tr.LayerTable.Delete("0").ToString()); //删除图层 0 - Env.Editor.WriteMessage(tr.LayerTable.Delete("Defpoints").ToString());//删除图层 Defpoints - Env.Editor.WriteMessage(tr.LayerTable.Delete("1").ToString());//删除不存在的图层 1 - Env.Editor.WriteMessage(tr.LayerTable.Delete("2").ToString());//删除有图元的图层 2 - Env.Editor.WriteMessage(tr.LayerTable.Delete("3").ToString());//删除图层 3 - - tr.LayerTable.Remove("2"); //测试是否能强制删除 - } + tr.LayerTable.Remove("2"); //测试是否能强制删除 + } - //添加直线 - [CommandMethod("linedemo1")] - public void AddLine1() - { - using var tr = new DBTrans(); - // tr.ModelSpace.AddEnt(line); - // tr.ModelSpace.AddEnts(line,circle); - - // tr.PaperSpace.AddEnt(line); - // tr.PaperSpace.AddEnts(line,circle); - - // tr.addent(btr,line); - // tr.addents(btr,line,circle); - - - // tr.BlockTable.Add(new BlockTableRecord(), line => - // { - // line. - // }); - Line line1 = new(new Point3d(0, 0, 0), new Point3d(1, 1, 0)); - Line line2 = new(new Point3d(0, 0, 0), new Point3d(1, 1, 0)); - Line line3 = new(new Point3d(1, 1, 0), new Point3d(3, 3, 0)); - Circle circle = new Circle(new Point3d(0, 0, 0), Vector3d.ZAxis, 10); - tr.CurrentSpace.AddEntity(line1); - tr.CurrentSpace.AddEntity(line2, line3, circle); - } + //添加直线 + [CommandMethod("linedemo1")] + public void AddLine1() + { + using var tr = new DBTrans(); + // tr.ModelSpace.AddEnt(line); + // tr.ModelSpace.AddEnts(line,circle); + + // tr.PaperSpace.AddEnt(line); + // tr.PaperSpace.AddEnts(line,circle); + + // tr.addent(btr,line); + // tr.addents(btr,line,circle); + + + // tr.BlockTable.Add(new BlockTableRecord(), line => + // { + // line. + // }); + Line line1 = new(new Point3d(0, 0, 0), new Point3d(1, 1, 0)); + Line line2 = new(new Point3d(0, 0, 0), new Point3d(1, 1, 0)); + Line line3 = new(new Point3d(1, 1, 0), new Point3d(3, 3, 0)); + Circle circle = new Circle(new Point3d(0, 0, 0), Vector3d.ZAxis, 10); + tr.CurrentSpace.AddEntity(line1); + tr.CurrentSpace.AddEntity(line2, line3, circle); + } - //增加多段线1 - [CommandMethod("Pldemo1")] - public void AddPolyline1() - { - using var tr = new DBTrans(); - Polyline pl = new Polyline(); - pl.AddVertexAt(0, new Point2d(0, 0), 0, 0, 0); - pl.AddVertexAt(1, new Point2d(10, 10), 0, 0, 0); - pl.AddVertexAt(2, new Point2d(20, 20), 0, 0, 0); - pl.AddVertexAt(3, new Point2d(30, 30), 0, 0, 0); - pl.AddVertexAt(4, new Point2d(40, 40), 0, 0, 0); - pl.Closed = true; - pl.Color = Color.FromColorIndex(ColorMethod.ByColor, 6); - tr.CurrentSpace.AddEntity(pl); - } + //增加多段线1 + [CommandMethod("Pldemo1")] + public void AddPolyline1() + { + using var tr = new DBTrans(); + Polyline pl = new Polyline(); + pl.AddVertexAt(0, new Point2d(0, 0), 0, 0, 0); + pl.AddVertexAt(1, new Point2d(10, 10), 0, 0, 0); + pl.AddVertexAt(2, new Point2d(20, 20), 0, 0, 0); + pl.AddVertexAt(3, new Point2d(30, 30), 0, 0, 0); + pl.AddVertexAt(4, new Point2d(40, 40), 0, 0, 0); + pl.Closed = true; + pl.Color = Color.FromColorIndex(ColorMethod.ByColor, 6); + tr.CurrentSpace.AddEntity(pl); + } - //增加多段线2 - [CommandMethod("pldemo2")] - public void Addpl2() - { - var pts = new List<(Point3d, double, double, double)> + //增加多段线2 + [CommandMethod("pldemo2")] + public void Addpl2() + { + var pts = new List<(Point3d, double, double, double)> { (new Point3d(0,0,0),0,0,0), (new Point3d(10,0,0),0,0,0), @@ -196,154 +196,154 @@ public void Addpl2() (new Point3d(0,10,0),0,0,0), (new Point3d(5,5,0),0,0,0) }; - using var tr = new DBTrans(); - tr.CurrentSpace.AddPline(pts); - } + using var tr = new DBTrans(); + tr.CurrentSpace.AddPline(pts); + } - //块定义 - [CommandMethod("blockdef")] - public void BlockDef() - { - using var tr = new DBTrans(); - //var line = new Line(new Point3d(0, 0, 0), new Point3d(1, 1, 0)); - tr.BlockTable.Add("test", - btr => - { - btr.Origin = new Point3d(0, 0, 0); - }, - () => //图元 - { - return new List { new Line(new Point3d(0, 0, 0), new Point3d(1, 1, 0)) }; - }, - () => //属性定义 - { - var id1 = new AttributeDefinition() { Position = new Point3d(0, 0, 0), Tag = "start", Height = 0.2 }; - var id2 = new AttributeDefinition() { Position = new Point3d(1, 1, 0), Tag = "end", Height = 0.2 }; - return new List { id1, id2 }; - } - ); - //ObjectId objectId = tr.BlockTable.Add("a");//新建块 - //objectId.GetObject().AddEntity();//测试添加空实体 - } - //修改块定义 - [CommandMethod("blockdefchange")] - public void BlockDefChange() - { - using var tr = new DBTrans(); - //var line = new Line(new Point3d(0, 0, 0), new Point3d(1, 1, 0)); - tr.BlockTable.Change("test", btr => + //块定义 + [CommandMethod("blockdef")] + public void BlockDef() { - btr.Origin = new Point3d(5, 5, 0); - btr.AddEntity(new Circle(new Point3d(0, 0, 0), Vector3d.ZAxis, 2)); - btr.GetEntities() - .ToList() - .ForEach(e => e.Flush()); //刷新块显示 - - }); - tr.Editor.Regen(); - } + using var tr = new DBTrans(); + //var line = new Line(new Point3d(0, 0, 0), new Point3d(1, 1, 0)); + tr.BlockTable.Add("test", + btr => + { + btr.Origin = new Point3d(0, 0, 0); + }, + () => //图元 + { + return new List { new Line(new Point3d(0, 0, 0), new Point3d(1, 1, 0)) }; + }, + () => //属性定义 + { + var id1 = new AttributeDefinition() { Position = new Point3d(0, 0, 0), Tag = "start", Height = 0.2 }; + var id2 = new AttributeDefinition() { Position = new Point3d(1, 1, 0), Tag = "end", Height = 0.2 }; + return new List { id1, id2 }; + } + ); + //ObjectId objectId = tr.BlockTable.Add("a");//新建块 + //objectId.GetObject().AddEntity();//测试添加空实体 + } + //修改块定义 + [CommandMethod("blockdefchange")] + public void BlockDefChange() + { + using var tr = new DBTrans(); + //var line = new Line(new Point3d(0, 0, 0), new Point3d(1, 1, 0)); + tr.BlockTable.Change("test", btr => + { + btr.Origin = new Point3d(5, 5, 0); + btr.AddEntity(new Circle(new Point3d(0, 0, 0), Vector3d.ZAxis, 2)); + btr.GetEntities() + .ToList() + .ForEach(e => e.Flush()); //刷新块显示 + + }); + tr.Editor.Regen(); + } - [CommandMethod("insertblockdef")] - public void InsertBlockDef() - { - using var tr = new DBTrans(); - var line1 = new Line(new Point3d(0, 0, 0), new Point3d(1, 1, 0)); - var line2 = new Line(new Point3d(0, 0, 0), new Point3d(-1, 1, 0)); - var att1 = new AttributeDefinition() { Position = new Point3d(10, 10, 0), Tag = "tagTest1", Height = 1, TextString = "valueTest1" }; - var att2 = new AttributeDefinition() { Position = new Point3d(10, 12, 0), Tag = "tagTest2", Height = 1, TextString = "valueTest2" }; - tr.BlockTable.Add("test1", line1, line2, att1, att2); - - - var ents = new List(); - var line5 = new Line(new Point3d(0, 0, 0), new Point3d(1, 1, 0)); - var line6 = new Line(new Point3d(0, 0, 0), new Point3d(-1, 1, 0)); - ents.Add(line5); - ents.Add(line6); - tr.BlockTable.Add("test44", ents); - - - var line3 = new Line(new Point3d(5, 5, 0), new Point3d(6, 6, 0)); - var line4 = new Line(new Point3d(5, 5, 0), new Point3d(-6, 6, 0)); - var att3 = new AttributeDefinition() { Position = new Point3d(10, 14, 0), Tag = "tagTest3", Height = 1, TextString = "valueTest3" }; - var att4 = new AttributeDefinition() { Position = new Point3d(10, 16, 0), Tag = "tagTest4", Height = 1, TextString = "valueTest4" }; - tr.BlockTable.Add("test2", new List { line3, line4 }, new List { att3, att4 }); - //tr.CurrentSpace.InsertBlock(new Point3d(4, 4, 0), "test1"); // 测试默认 - //tr.CurrentSpace.InsertBlock(new Point3d(4, 4, 0), "test2"); - //tr.CurrentSpace.InsertBlock(new Point3d(4, 4, 0), "test3"); //测试插入不存在的块定义 - //tr.CurrentSpace.InsertBlock(new Point3d(0, 0, 0), "test1", new Scale3d(2)); // 测试放大2倍 - //tr.CurrentSpace.InsertBlock(new Point3d(4, 4, 0), "test1", new Scale3d(2), Math.PI / 4); // 测试放大2倍,旋转45度 - - var def1 = new Dictionary + [CommandMethod("insertblockdef")] + public void InsertBlockDef() + { + using var tr = new DBTrans(); + var line1 = new Line(new Point3d(0, 0, 0), new Point3d(1, 1, 0)); + var line2 = new Line(new Point3d(0, 0, 0), new Point3d(-1, 1, 0)); + var att1 = new AttributeDefinition() { Position = new Point3d(10, 10, 0), Tag = "tagTest1", Height = 1, TextString = "valueTest1" }; + var att2 = new AttributeDefinition() { Position = new Point3d(10, 12, 0), Tag = "tagTest2", Height = 1, TextString = "valueTest2" }; + tr.BlockTable.Add("test1", line1, line2, att1, att2); + + + var ents = new List(); + var line5 = new Line(new Point3d(0, 0, 0), new Point3d(1, 1, 0)); + var line6 = new Line(new Point3d(0, 0, 0), new Point3d(-1, 1, 0)); + ents.Add(line5); + ents.Add(line6); + tr.BlockTable.Add("test44", ents); + + + var line3 = new Line(new Point3d(5, 5, 0), new Point3d(6, 6, 0)); + var line4 = new Line(new Point3d(5, 5, 0), new Point3d(-6, 6, 0)); + var att3 = new AttributeDefinition() { Position = new Point3d(10, 14, 0), Tag = "tagTest3", Height = 1, TextString = "valueTest3" }; + var att4 = new AttributeDefinition() { Position = new Point3d(10, 16, 0), Tag = "tagTest4", Height = 1, TextString = "valueTest4" }; + tr.BlockTable.Add("test2", new List { line3, line4 }, new List { att3, att4 }); + //tr.CurrentSpace.InsertBlock(new Point3d(4, 4, 0), "test1"); // 测试默认 + //tr.CurrentSpace.InsertBlock(new Point3d(4, 4, 0), "test2"); + //tr.CurrentSpace.InsertBlock(new Point3d(4, 4, 0), "test3"); //测试插入不存在的块定义 + //tr.CurrentSpace.InsertBlock(new Point3d(0, 0, 0), "test1", new Scale3d(2)); // 测试放大2倍 + //tr.CurrentSpace.InsertBlock(new Point3d(4, 4, 0), "test1", new Scale3d(2), Math.PI / 4); // 测试放大2倍,旋转45度 + + var def1 = new Dictionary { { "tagTest1", "1" }, { "tagTest2", "2" } }; - tr.CurrentSpace.InsertBlock(new Point3d(0, 0, 0), "test1", atts: def1); - var def2 = new Dictionary + tr.CurrentSpace.InsertBlock(new Point3d(0, 0, 0), "test1", atts: def1); + var def2 = new Dictionary { { "tagTest3", "1" }, { "tagTest4", "" } }; - tr.CurrentSpace.InsertBlock(new Point3d(10, 10, 0), "test2", atts: def2); - tr.CurrentSpace.InsertBlock(new Point3d(-10, 0, 0), "test44"); - } - - [CommandMethod("testblocknullbug")] - public void TestBlockNullBug() - { - using var tr = new DBTrans(); - - var ents = new List(); - var line5 = new Line(new Point3d(0, 0, 0), new Point3d(1, 1, 0)); - var line6 = new Line(new Point3d(0, 0, 0), new Point3d(-1, 1, 0)); - ents.Add(line5); - ents.Add(line6); - tr.BlockTable.Add("test44", ents); - tr.CurrentSpace.InsertBlock(new Point3d(0, 0, 0), "test44"); - } + tr.CurrentSpace.InsertBlock(new Point3d(10, 10, 0), "test2", atts: def2); + tr.CurrentSpace.InsertBlock(new Point3d(-10, 0, 0), "test44"); + } + [CommandMethod("testblocknullbug")] + public void TestBlockNullBug() + { + using var tr = new DBTrans(); - [CommandMethod("testclip")] - public void TestClipBlock() - { - using var tr = new DBTrans(); - tr.BlockTable.Add("test1", - btr => - { - btr.Origin = new Point3d(0, 0, 0); - btr.AddEntity(new Line(new Point3d(0, 0, 0), new Point3d(10, 10, 0)), - new Line(new Point3d(10, 10, 0), new Point3d(10, 0, 0)) - ); - } - ); - //tr.BlockTable.Add("hah"); - var id = tr.CurrentSpace.InsertBlock(new Point3d(0, 0, 0), "test1"); - var bref = tr.GetObject(id); - var pts = new List { new Point3d(3, 3, 0), new Point3d(7, 3, 0), new Point3d(7, 7, 0), new Point3d(3, 7, 0) }; - bref.ClipBlockRef(pts); + var ents = new List(); + var line5 = new Line(new Point3d(0, 0, 0), new Point3d(1, 1, 0)); + var line6 = new Line(new Point3d(0, 0, 0), new Point3d(-1, 1, 0)); + ents.Add(line5); + ents.Add(line6); + tr.BlockTable.Add("test44", ents); + tr.CurrentSpace.InsertBlock(new Point3d(0, 0, 0), "test44"); + } - var id1 = tr.CurrentSpace.InsertBlock(new Point3d(20, 20, 0), "test1"); - var bref1 = tr.GetObject(id); - bref1.ClipBlockRef(new Point3d(13, 13, 0), new Point3d(17, 17, 0)); - } + [CommandMethod("testclip")] + public void TestClipBlock() + { + using var tr = new DBTrans(); + tr.BlockTable.Add("test1", + btr => + { + btr.Origin = new Point3d(0, 0, 0); + btr.AddEntity(new Line(new Point3d(0, 0, 0), new Point3d(10, 10, 0)), + new Line(new Point3d(10, 10, 0), new Point3d(10, 0, 0)) + ); + } + ); + //tr.BlockTable.Add("hah"); + var id = tr.CurrentSpace.InsertBlock(new Point3d(0, 0, 0), "test1"); + var bref = tr.GetObject(id); + var pts = new List { new Point3d(3, 3, 0), new Point3d(7, 3, 0), new Point3d(7, 7, 0), new Point3d(3, 7, 0) }; + bref.ClipBlockRef(pts); + var id1 = tr.CurrentSpace.InsertBlock(new Point3d(20, 20, 0), "test1"); + var bref1 = tr.GetObject(id); + bref1.ClipBlockRef(new Point3d(13, 13, 0), new Point3d(17, 17, 0)); + } - // 测试扩展数据 - [CommandMethod("addxdata")] - public void AddXdata() - { - using var tr = new DBTrans(); - var appname = "myapp"; - tr.RegAppTable.Add(appname); // add函数会默认的在存在这个名字的时候返回这个名字的regapp的id,不存在就新建 - tr.RegAppTable.Add("myapp2"); - var line = new Line(new Point3d(0, 0, 0), new Point3d(1, 1, 0)) + // 测试扩展数据 + [CommandMethod("addxdata")] + public void AddXdata() { - XData = new XDataList() + using var tr = new DBTrans(); + var appname = "myapp"; + + tr.RegAppTable.Add(appname); // add函数会默认的在存在这个名字的时候返回这个名字的regapp的id,不存在就新建 + tr.RegAppTable.Add("myapp2"); + + var line = new Line(new Point3d(0, 0, 0), new Point3d(1, 1, 0)) + { + XData = new XDataList() { { DxfCode.ExtendedDataRegAppName, appname }, //可以用dxfcode和int表示组码 { DxfCode.ExtendedDataAsciiString, "hahhahah" }, @@ -352,276 +352,278 @@ public void AddXdata() { DxfCode.ExtendedDataAsciiString, "hahhahah" }, {1070, 12 } } - }; - - tr.CurrentSpace.AddEntity(line); - } + }; - [CommandMethod("getxdata")] - public void GetXdata() - { - var doc = Application.DocumentManager.MdiActiveDocument; - var ed = doc.Editor; + tr.CurrentSpace.AddEntity(line); + } - var res = ed.GetEntity("\n select the entity:"); - if (res.Status == PromptStatus.OK) + [CommandMethod("getxdata")] + public void GetXdata() { - using var tr = new DBTrans(); - var data = tr.GetObject(res.ObjectId).XData; - ed.WriteMessage(data.ToString()); + var doc = Application.DocumentManager.MdiActiveDocument; + var ed = doc.Editor; + + var res = ed.GetEntity("\n select the entity:"); + if (res.Status == PromptStatus.OK) + { + using var tr = new DBTrans(); + var data = tr.GetObject(res.ObjectId).XData; + ed.WriteMessage(data.ToString()); + } } - } - [CommandMethod("changexdata")] - public void Changexdata() - { - var doc = Application.DocumentManager.MdiActiveDocument; - var ed = doc.Editor; - var appname = "myapp"; - var res = ed.GetEntity("\n select the entity:"); - if (res.Status == PromptStatus.OK) + [CommandMethod("changexdata")] + public void Changexdata() { - using var tr = new DBTrans(); - var data = tr.GetObject(res.ObjectId); - data.ChangeXData(appname, DxfCode.ExtendedDataAsciiString, "change"); + var doc = Application.DocumentManager.MdiActiveDocument; + var ed = doc.Editor; + var appname = "myapp"; + var res = ed.GetEntity("\n select the entity:"); + if (res.Status == PromptStatus.OK) + { + using var tr = new DBTrans(); + var data = tr.GetObject(res.ObjectId); + data.ChangeXData(appname, DxfCode.ExtendedDataAsciiString, "change"); - ed.WriteMessage(data.XData.ToString()); + ed.WriteMessage(data.XData.ToString()); + } } - } - [CommandMethod("removexdata")] - public void Removexdata() - { - var doc = Application.DocumentManager.MdiActiveDocument; - var ed = doc.Editor; - var appname = "myapp"; - var res = ed.GetEntity("\n select the entity:"); - if (res.Status == PromptStatus.OK) + [CommandMethod("removexdata")] + public void Removexdata() { - using var tr = new DBTrans(); - var data = tr.GetObject(res.ObjectId); - data.RemoveXData(appname, DxfCode.ExtendedDataAsciiString); + var doc = Application.DocumentManager.MdiActiveDocument; + var ed = doc.Editor; + var appname = "myapp"; + var res = ed.GetEntity("\n select the entity:"); + if (res.Status == PromptStatus.OK) + { + using var tr = new DBTrans(); + var data = tr.GetObject(res.ObjectId); + data.RemoveXData(appname, DxfCode.ExtendedDataAsciiString); - ed.WriteMessage(data.XData.ToString()); + ed.WriteMessage(data.XData.ToString()); + } } - } - [CommandMethod("PrintLayerName")] - public void PrintLayerName() - { - using var tr = new DBTrans(); - foreach (var layerRecord in tr.LayerTable.GetRecords()) + [CommandMethod("PrintLayerName")] + public void PrintLayerName() { - tr.Editor.WriteMessage(layerRecord.Name); - } - - } + using var tr = new DBTrans(); + foreach (var layerRecord in tr.LayerTable.GetRecords()) + { + tr.Editor.WriteMessage(layerRecord.Name); + } + } - [CommandMethod("testwpf")] - public void TestWPf() - { - var test = new TestView(); - Application.ShowModalWindow(test); - } + [CommandMethod("testwpf")] + public void TestWPf() + { - [CommandMethod("testpt")] - public void TestPt() - { - //var pt = Env.Editor.GetPoint("pick pt:").Value; - //var pl = Env.Editor.GetEntity("pick pl").ObjectId; - var tr1 = HostApplicationServices.WorkingDatabase.TransactionManager.TopTransaction; - using var tr2 = new DBTrans(); - var tr3 = HostApplicationServices.WorkingDatabase.TransactionManager.TopTransaction; - var tr6 = Application.DocumentManager.MdiActiveDocument.TransactionManager.TopTransaction; - Env.Print(tr2.Transaction == tr3); - Env.Print(tr3 == tr6); - using var tr4 = new DBTrans(); - var tr5 = HostApplicationServices.WorkingDatabase.TransactionManager.TopTransaction; - var tr7 = Application.DocumentManager.MdiActiveDocument.TransactionManager.TopTransaction; - Env.Print(tr4.Transaction == tr5); - Env.Print(tr5 == tr7); - var trm = HostApplicationServices.WorkingDatabase.TransactionManager; - //var ptt = tr.GetObject(pl).GetClosestPointTo(pt,false); - //var pt1 = new Point3d(0, 0.00000000000001, 0); - //var pt2 = new Point3d(0, 0.00001, 0); - //Env.Print(Tolerance.Global.EqualPoint); - //Env.Print(pt1.IsEqualTo(pt2).ToString()); - //Env.Print(pt1.IsEqualTo(pt2,new Tolerance(0.0,1e-6)).ToString()); - //Env.Print((pt1 == pt2).ToString()); - //Env.Print((pt1 != pt2).ToString()); + var test = new TestView(); + Application.ShowModalWindow(test); + } + [CommandMethod("testpt")] + public void TestPt() + { + //var pt = Env.Editor.GetPoint("pick pt:").Value; + //var pl = Env.Editor.GetEntity("pick pl").ObjectId; + var tr1 = HostApplicationServices.WorkingDatabase.TransactionManager.TopTransaction; + using var tr2 = new DBTrans(); + var tr3 = HostApplicationServices.WorkingDatabase.TransactionManager.TopTransaction; + var tr6 = Application.DocumentManager.MdiActiveDocument.TransactionManager.TopTransaction; + Env.Print(tr2.Transaction == tr3); + Env.Print(tr3 == tr6); + using var tr4 = new DBTrans(); + var tr5 = HostApplicationServices.WorkingDatabase.TransactionManager.TopTransaction; + var tr7 = Application.DocumentManager.MdiActiveDocument.TransactionManager.TopTransaction; + Env.Print(tr4.Transaction == tr5); + Env.Print(tr5 == tr7); + var trm = HostApplicationServices.WorkingDatabase.TransactionManager; + //var ptt = tr.GetObject(pl).GetClosestPointTo(pt,false); + //var pt1 = new Point3d(0, 0.00000000000001, 0); + //var pt2 = new Point3d(0, 0.00001, 0); + //Env.Print(Tolerance.Global.EqualPoint); + //Env.Print(pt1.IsEqualTo(pt2).ToString()); + //Env.Print(pt1.IsEqualTo(pt2,new Tolerance(0.0,1e-6)).ToString()); + //Env.Print((pt1 == pt2).ToString()); + //Env.Print((pt1 != pt2).ToString()); - } + } - public Database Getdb() - { - var db = Application.DocumentManager.MdiActiveDocument.Database; - return db; - } + public Database Getdb() + { + var db = Application.DocumentManager.MdiActiveDocument.Database; + return db; + } - public Document Getdoc() - { - var doc = Application.DocumentManager.MdiActiveDocument; - return doc; - } - //public override void Initialize() - //{ - // Application.DocumentManager.MdiActiveDocument.Editor.WriteMessage("\nload...."); - //} + public Document Getdoc() + { + var doc = Application.DocumentManager.MdiActiveDocument; + return doc; + } - //public override void Terminate() - //{ - // Application.DocumentManager.MdiActiveDocument.Editor.WriteMessage("\nunload...."); - //} -} + //public override void Initialize() + //{ + // Application.DocumentManager.MdiActiveDocument.Editor.WriteMessage("\nload...."); + //} + //public override void Terminate() + //{ + // Application.DocumentManager.MdiActiveDocument.Editor.WriteMessage("\nunload...."); + //} + } -public class BlockImportClass -{ - [CommandMethod("CBLL")] - public void cbll() + public class BlockImportClass { - string filename = @"C:\Users\vic\Desktop\Drawing1.dwg"; - using var tr = new DBTrans(); - using var tr1 = new DBTrans(filename); - //tr.BlockTable.GetBlockFrom(filename, true); - string blkdefname = SymbolUtilityServices.RepairSymbolName(SymbolUtilityServices.GetSymbolNameFromPathName(filename, "dwg"), false); - tr.Database.Insert(blkdefname, tr1.Database, false); //插入了块定义,未插入块参照 - } + [CommandMethod("CBLL")] + public void cbll() + { + string filename = @"C:\Users\vic\Desktop\Drawing1.dwg"; + using var tr = new DBTrans(); + using var tr1 = new DBTrans(filename); + //tr.BlockTable.GetBlockFrom(filename, true); + string blkdefname = SymbolUtilityServices.RepairSymbolName(SymbolUtilityServices.GetSymbolNameFromPathName(filename, "dwg"), false); + tr.Database.Insert(blkdefname, tr1.Database, false); //插入了块定义,未插入块参照 + } - [CommandMethod("CBL")] - public void CombineBlocksIntoLibrary() - { - Document doc = - Application.DocumentManager.MdiActiveDocument; - Editor ed = doc.Editor; - Database destDb = doc.Database; - // Get name of folder from which to load and import blocks + [CommandMethod("CBL")] + public void CombineBlocksIntoLibrary() + { + Document doc = + Application.DocumentManager.MdiActiveDocument; + Editor ed = doc.Editor; + Database destDb = doc.Database; - PromptResult pr = - ed.GetString("\nEnter the folder of source drawings: "); + // Get name of folder from which to load and import blocks - if (pr.Status != PromptStatus.OK) - return; - string pathName = pr.StringResult; + PromptResult pr = + ed.GetString("\nEnter the folder of source drawings: "); - // Check the folder exists + if (pr.Status != PromptStatus.OK) + return; + string pathName = pr.StringResult; - if (!Directory.Exists(pathName)) - { - ed.WriteMessage( - "\nDirectory does not exist: {0}", pathName - ); - return; - } + // Check the folder exists - // Get the names of our DWG files in that folder + if (!Directory.Exists(pathName)) + { + ed.WriteMessage( + "\nDirectory does not exist: {0}", pathName + ); + return; + } - string[] fileNames = Directory.GetFiles(pathName, "*.dwg"); + // Get the names of our DWG files in that folder - // A counter for the files we've imported + string[] fileNames = Directory.GetFiles(pathName, "*.dwg"); - int imported = 0, failed = 0; + // A counter for the files we've imported - // For each file in our list + int imported = 0, failed = 0; - foreach (string fileName in fileNames) - { - // Double-check we have a DWG file (probably unnecessary) + // For each file in our list - if (fileName.EndsWith( - ".dwg", - StringComparison.InvariantCultureIgnoreCase - ) - ) + foreach (string fileName in fileNames) { - // Catch exceptions at the file level to allow skipping + // Double-check we have a DWG file (probably unnecessary) - try + if (fileName.EndsWith( + ".dwg", + StringComparison.InvariantCultureIgnoreCase + ) + ) { - // Suggestion from Thorsten Meinecke... + // Catch exceptions at the file level to allow skipping - string destName = - SymbolUtilityServices.GetSymbolNameFromPathName( - fileName, "dwg" - ); + try + { + // Suggestion from Thorsten Meinecke... - // And from Dan Glassman... + string destName = + SymbolUtilityServices.GetSymbolNameFromPathName( + fileName, "dwg" + ); - destName = - SymbolUtilityServices.RepairSymbolName( - destName, false - ); + // And from Dan Glassman... - // Create a source database to load the DWG into + destName = + SymbolUtilityServices.RepairSymbolName( + destName, false + ); - using (Database db = new Database(false, true)) - { - // Read the DWG into our side database + // Create a source database to load the DWG into - db.ReadDwgFile(fileName, FileShare.Read, true, ""); - bool isAnno = db.AnnotativeDwg; + using (Database db = new Database(false, true)) + { + // Read the DWG into our side database - // Insert it into the destination database as - // a named block definition + db.ReadDwgFile(fileName, FileShare.Read, true, ""); + bool isAnno = db.AnnotativeDwg; - ObjectId btrId = destDb.Insert( - destName, - db, - false - ); + // Insert it into the destination database as + // a named block definition - if (isAnno) - { - // If an annotative block, open the resultant BTR - // and set its annotative definition status + ObjectId btrId = destDb.Insert( + destName, + db, + false + ); - Transaction tr = - destDb.TransactionManager.StartTransaction(); - using (tr) + if (isAnno) { - BlockTableRecord btr = - (BlockTableRecord)tr.GetObject( - btrId, - OpenMode.ForWrite - ); - btr.Annotative = AnnotativeStates.True; - tr.Commit(); + // If an annotative block, open the resultant BTR + // and set its annotative definition status + + Transaction tr = + destDb.TransactionManager.StartTransaction(); + using (tr) + { + BlockTableRecord btr = + (BlockTableRecord)tr.GetObject( + btrId, + OpenMode.ForWrite + ); + btr.Annotative = AnnotativeStates.True; + tr.Commit(); + } } - } - // Print message and increment imported block counter + // Print message and increment imported block counter - ed.WriteMessage("\nImported from \"{0}\".", fileName); - imported++; + ed.WriteMessage("\nImported from \"{0}\".", fileName); + imported++; + } + } + catch (System.Exception ex) + { + ed.WriteMessage( + "\nProblem importing \"{0}\": {1} - file skipped.", + fileName, ex.Message + ); + failed++; } - } - catch (System.Exception ex) - { - ed.WriteMessage( - "\nProblem importing \"{0}\": {1} - file skipped.", - fileName, ex.Message - ); - failed++; } } - } - ed.WriteMessage( - "\nImported block definitions from {0} files{1} in " + - "\"{2}\" into the current drawing.", - imported, - failed > 0 ? " (" + failed + " failed)" : "", - pathName - ); + ed.WriteMessage( + "\nImported block definitions from {0} files{1} in " + + "\"{2}\" into the current drawing.", + imported, + failed > 0 ? " (" + failed + " failed)" : "", + pathName + ); + } } -} \ No newline at end of file + +} -- Gitee From 869e3b86c235886a3b5f17a07b39759e0aa2dec1 Mon Sep 17 00:00:00 2001 From: vicwjb Date: Tue, 7 Dec 2021 23:03:35 +0800 Subject: [PATCH 003/675] =?UTF-8?q?=E4=BC=98=E5=8C=96=E5=AD=97=E4=BD=93?= =?UTF-8?q?=E6=A0=B7=E5=BC=8F=E7=9B=B8=E5=85=B3=E5=87=BD=E6=95=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/IFoxCAD.Cad/ExtensionMethod/Enums.cs | 35 +++++++++++ .../ExtensionMethod/SymbolTableEx.cs | 61 +++++++++++++++++- src/IFoxCAD.Cad/GlobalUsings.cs | 1 + src/IFoxCAD.Cad/Runtime/SymbolTable.cs | 1 + tests/DBTrans.test/testid.cs | 62 ++++++++++++++++++- 5 files changed, 158 insertions(+), 2 deletions(-) diff --git a/src/IFoxCAD.Cad/ExtensionMethod/Enums.cs b/src/IFoxCAD.Cad/ExtensionMethod/Enums.cs index bae131f..9cae1fb 100644 --- a/src/IFoxCAD.Cad/ExtensionMethod/Enums.cs +++ b/src/IFoxCAD.Cad/ExtensionMethod/Enums.cs @@ -70,3 +70,38 @@ public enum PointOnRegionType /// Error } + + + +public enum FontTTF +{ + [Description("宋体.ttf")] + 宋体, + [Description("simfang.ttf")] + 仿宋, + [Description("FSGB2312.ttf")] + 仿宋GB2312, + [Description("Arial.ttf")] + Arial, + [Description("Romans")] + Romans +} + + + +public static class EnumHelper +{ + public static string GetDesc(this Enum val) + { + var type = val.GetType(); + var memberInfo = type.GetMember(val.ToString()); + var attributes = memberInfo[0].GetCustomAttributes(typeof(DescriptionAttribute), false); + //如果没有定义描述,就把当前枚举值的对应名称返回 + if (attributes == null || attributes.Length != 1) + { + return val.ToString(); + } + return (attributes.Single() as DescriptionAttribute).Description; + } +} + diff --git a/src/IFoxCAD.Cad/ExtensionMethod/SymbolTableEx.cs b/src/IFoxCAD.Cad/ExtensionMethod/SymbolTableEx.cs index 4da3daf..f8b7880 100644 --- a/src/IFoxCAD.Cad/ExtensionMethod/SymbolTableEx.cs +++ b/src/IFoxCAD.Cad/ExtensionMethod/SymbolTableEx.cs @@ -232,7 +232,10 @@ public static ObjectId Add(this SymbolTable /// 字体名 /// 宽度比例 /// 文字样式Id - public static ObjectId Add(this SymbolTable table, string textStyleName, string font, double xscale) + public static ObjectId Add(this SymbolTable table, + string textStyleName, + string font, + double xscale = 1.0) { return table.Add( @@ -244,6 +247,62 @@ public static ObjectId Add(this SymbolTable + /// 添加文字样式记录 + /// + /// 文字样式表 + /// 文字样式名 + /// 字体名枚举 + /// 宽度比例 + /// 文字样式Id + public static ObjectId Add(this SymbolTable table, string textStyleName, FontTTF fontTTF, double xscale = 1.0) + { + return table.Add(textStyleName, fontTTF.GetDesc(), xscale); + } + + /// + ///

添加文字样式记录,如果存在就默认强制替换

+ /// 此函数为了 而设 + ///
+ /// 文字样式表 + /// 文字样式名 + /// 字体名 + /// 大字体名 + /// 宽度比例 + /// 高度 + /// 是否强制替换 + /// 文字样式Id + public static ObjectId AddWithChange(this SymbolTable table, + string textStyleName, + string smallFont, + string bigFont = null, + double xScale = 1, + double height = 0, + bool forceChange = true) + { + if (forceChange && table.Has(textStyleName)) + { + table.Change(textStyleName, ttr => + { + ttr.FileName = smallFont; + ttr.XScale = xScale; + ttr.TextSize = height; + if (bigFont != null) + { + ttr.BigFontFileName = bigFont; + } + }); + return table[textStyleName]; + } + return table.Add(textStyleName, ttr => + { + ttr.FileName = smallFont; + ttr.XScale = xScale; + ttr.TextSize = height; + }); + } + + #endregion #region 注册应用程序表 diff --git a/src/IFoxCAD.Cad/GlobalUsings.cs b/src/IFoxCAD.Cad/GlobalUsings.cs index 08bd0b7..e51dd91 100644 --- a/src/IFoxCAD.Cad/GlobalUsings.cs +++ b/src/IFoxCAD.Cad/GlobalUsings.cs @@ -8,6 +8,7 @@ global using System.Reflection; global using System.Text.RegularExpressions; global using Microsoft.Win32; +global using System.ComponentModel; /// autocad 引用 diff --git a/src/IFoxCAD.Cad/Runtime/SymbolTable.cs b/src/IFoxCAD.Cad/Runtime/SymbolTable.cs index a6d005b..727dc63 100644 --- a/src/IFoxCAD.Cad/Runtime/SymbolTable.cs +++ b/src/IFoxCAD.Cad/Runtime/SymbolTable.cs @@ -171,6 +171,7 @@ private static void Change(TRecord record, Action action) { action?.Invoke(record); } + Env.Editor.Regen(); } /// /// 修改符号表 diff --git a/tests/DBTrans.test/testid.cs b/tests/DBTrans.test/testid.cs index 91809b4..911fd53 100644 --- a/tests/DBTrans.test/testid.cs +++ b/tests/DBTrans.test/testid.cs @@ -14,8 +14,19 @@ using IFoxCAD.Cad; namespace test { - public class Testid + + public class Testid : AutoRegAssem { + public override void Initialize() + { + Env.Print("\nloading..."); + } + + public override void Terminate() + { + throw new NotImplementedException(); + } + [CommandMethod("testid")] public void TestId() { @@ -41,5 +52,54 @@ public void TestId() } + [CommandMethod("testmycommand")] + public void TestMyCommand() + { + using (var dbtrans = new DBTrans(Env.Document,true,false)) { + using (var trans = Env.Database.TransactionManager.StartTransaction()) + { + var l1 = new Line(new Point3d(0, 0, 0), new Point3d(100, 100, 0)); + var blkred = trans.GetObject(Env.Database.CurrentSpaceId, OpenMode.ForWrite) as BlockTableRecord; + blkred.AppendEntity(l1); + trans.AddNewlyCreatedDBObject(l1, true); + trans.Commit(); + } + //dbtrans.Dispose(); + } + } + [CommandMethod("testtextstyle")] + public void TestTextStyle() + { + using var tr = new DBTrans(); + tr.TextStyleTable.Add("宋体", "宋体.ttf", 0.8); + + tr.TextStyleTable.Add("宋体1", FontTTF.宋体, 0.8); + tr.TextStyleTable.Add("仿宋体", FontTTF.仿宋, 0.8); + tr.TextStyleTable.Add("fsgb2312", FontTTF.仿宋GB2312, 0.8); + tr.TextStyleTable.Add("arial", FontTTF.Arial, 0.8); + tr.TextStyleTable.Add("romas", FontTTF.Romans, 0.8); + + + + tr.TextStyleTable.Add("daziti", ttr => + { + ttr.FileName = "ascii.shx"; + ttr.BigFontFileName = "gbcbig.shx"; + }); + } + + [CommandMethod("testtextstylechange")] + public void TestTextStyleChange() + { + using var tr = new DBTrans(); + + + tr.TextStyleTable.AddWithChange("宋体1", "simfang.ttf", height: 5); + tr.TextStyleTable.AddWithChange("仿宋体", "宋体.ttf"); + tr.TextStyleTable.AddWithChange("fsgb2312", "Romans", "gbcbig"); + + + + } } } -- Gitee From 1cc76dc4dbd14b3495ff193a55f8dcb4d6f767ab Mon Sep 17 00:00:00 2001 From: vicwjb Date: Tue, 7 Dec 2021 23:12:04 +0800 Subject: [PATCH 004/675] =?UTF-8?q?AddEntity=20=E5=A2=9E=E5=8A=A0=E4=B8=80?= =?UTF-8?q?=E4=B8=AA=E5=A7=94=E6=89=98=E4=BC=A0=E5=85=A5=20fixed=20#I4LB7Z?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../ExtensionMethod/SymbolTableRecordEx.cs | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/src/IFoxCAD.Cad/ExtensionMethod/SymbolTableRecordEx.cs b/src/IFoxCAD.Cad/ExtensionMethod/SymbolTableRecordEx.cs index e5d5523..0f97ead 100644 --- a/src/IFoxCAD.Cad/ExtensionMethod/SymbolTableRecordEx.cs +++ b/src/IFoxCAD.Cad/ExtensionMethod/SymbolTableRecordEx.cs @@ -87,6 +87,23 @@ private static ObjectId AddEnt(this BlockTableRecord btr, T ent, Action ac action?.Invoke(ent); return btr.AddEntity(ent, trans); } + /// + /// 委托式的添加图元 + /// + /// 块表 + /// 返回图元的委托 + /// 事务 + /// 图元id,如果委托返回 null,则为 ObjectId.Null + public static ObjectId AddEnt(this BlockTableRecord btr, Func action, Transaction transaction) + { + transaction ??= DBTrans.Top.Transaction; + var ent = action?.Invoke(); + if (ent == null) + { + return ObjectId.Null; + } + return btr.AddEntity(ent, transaction); + } /// /// 在指定绘图空间添加直线 -- Gitee From 742426010fef5f95ec4c6aca876051ab6cc21393 Mon Sep 17 00:00:00 2001 From: vicwjb Date: Tue, 7 Dec 2021 23:29:46 +0800 Subject: [PATCH 005/675] =?UTF-8?q?=E6=9B=B4=E6=96=B0readme?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index f312887..191a2bd 100644 --- a/README.md +++ b/README.md @@ -2,7 +2,11 @@ #### 介绍 -基于.NET的Cad二次开发类库 +基于.NET的Cad二次开发类库。 + +可以加群交流: + +![ifoxcad用户交流群群二维码](/Users/vic/Downloads/ifoxcad用户交流群群二维码.png) #### 软件架构及相关说明 @@ -20,9 +24,9 @@ 1. 快速入门 - - 打开vs,新建一个standard类型的类库项目,修改项目文件里的`netcore2.0`为`NET45`。其中的net45,可以改为NET35以上的标准TFM(如:net35、net40、net45、net46、net47等等)。同时可以指定多版本。具体的详细的教程见 [VS通过添加不同引用库,建立多条件编译]( https://www.yuque.com/vicwjb/zqpcd0/ufbwyl)。 + - 打开vs,新建一个standard类型的类库项目,修改项目文件里的`netcore2.0`为`NET45`。其中的net45,可以改为NET45以上的标准TFM(如:net45、net46、net47等等)。同时可以指定多版本。具体的详细的教程见 [VS通过添加不同引用库,建立多条件编译]( https://www.yuque.com/vicwjb/zqpcd0/ufbwyl)。 - 右键项目文件,选择管理nuget程序包。 - - 在nuget程序里搜索**ifoxcad**,直接选择最新的版本,然后点击安装**IFoxCAD.Cad**,nuget会自动安装ifoxcad依赖的库。 + - 在nuget程序里搜索**ifoxcad**,直接选择最新的版本(如果你是 **net40** 或者 **net35** 的用户,可以安装 **0.1.6** 版本),然后点击安装**IFoxCAD.Cad**,nuget会自动安装ifoxcad依赖的库。 - 添加引用 ```c# -- Gitee From 482e148a53f668b1baacc2aad0430993e3d27570 Mon Sep 17 00:00:00 2001 From: vicwjb Date: Tue, 7 Dec 2021 23:36:04 +0800 Subject: [PATCH 006/675] =?UTF-8?q?=E6=9B=B4=E6=96=B0readme?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 2 +- ...244\344\272\214\347\273\264\347\240\201.png" | Bin 0 -> 20425 bytes 2 files changed, 1 insertion(+), 1 deletion(-) create mode 100644 "docs/ifoxcad\347\224\250\346\210\267\344\272\244\346\265\201\347\276\244\347\276\244\344\272\214\347\273\264\347\240\201.png" diff --git a/README.md b/README.md index 191a2bd..48292fc 100644 --- a/README.md +++ b/README.md @@ -6,7 +6,7 @@ 可以加群交流: -![ifoxcad用户交流群群二维码](/Users/vic/Downloads/ifoxcad用户交流群群二维码.png) +![ifoxcad用户交流群群二维码](./docs/ifoxcad用户交流群群二维码.png) #### 软件架构及相关说明 diff --git "a/docs/ifoxcad\347\224\250\346\210\267\344\272\244\346\265\201\347\276\244\347\276\244\344\272\214\347\273\264\347\240\201.png" "b/docs/ifoxcad\347\224\250\346\210\267\344\272\244\346\265\201\347\276\244\347\276\244\344\272\214\347\273\264\347\240\201.png" new file mode 100644 index 0000000000000000000000000000000000000000..a9d08c483b80f21d4b0ac6a0afe80f9b6b4233e6 GIT binary patch literal 20425 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 Date: Mon, 13 Dec 2021 19:34:56 +0800 Subject: [PATCH 007/675] =?UTF-8?q?=E6=9E=9A=E4=B8=BE=E7=BB=91=E5=AE=9A?= =?UTF-8?q?=E8=BE=85=E5=8A=A9=E7=B1=BB=20fixed=20#I4LZ3W?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/IFoxCAD.WPF/EnumSelection.cs | 72 ++++++++++++++++++++++++++++++++ 1 file changed, 72 insertions(+) create mode 100644 src/IFoxCAD.WPF/EnumSelection.cs diff --git a/src/IFoxCAD.WPF/EnumSelection.cs b/src/IFoxCAD.WPF/EnumSelection.cs new file mode 100644 index 0000000..12960f9 --- /dev/null +++ b/src/IFoxCAD.WPF/EnumSelection.cs @@ -0,0 +1,72 @@ +namespace IFoxCAD.WPF; +public class EnumSelection : INotifyPropertyChanged where T : struct, IComparable, IFormattable, IConvertible +{ + private T value; // stored value of the Enum + private readonly bool isFlagged; // Enum uses flags? + private readonly bool canDeselect; // Can be deselected? (Radio buttons cannot deselect, checkboxes can) + private readonly T blankValue; // what is considered the "blank" value if it can be deselected? + + public EnumSelection(T value) : this(value, false, default) { } + public EnumSelection(T value, bool canDeselect) : this(value, canDeselect, default) { } + public EnumSelection(T value, T blankValue) : this(value, true, blankValue) { } + public EnumSelection(T value, bool canDeselect, T blankValue) + { + if (!typeof(T).IsEnum) throw new ArgumentException($"{nameof(T)} must be an enum type"); // I really wish there was a way to constrain generic types to enums... + isFlagged = typeof(T).IsDefined(typeof(FlagsAttribute), false); + + this.value = value; + this.canDeselect = canDeselect; + this.blankValue = blankValue; + } + + public T Value + { + get { return value; } + set + { + if (this.value.Equals(value)) return; + this.value = value; + OnPropertyChanged(); + OnPropertyChanged("Item[]"); // Notify that the indexer property has changed + } + } + + [IndexerName("Item")] + public bool this[T key] + { + get + { + int iKey = (int)(object)key; + return isFlagged ? ((int)(object)value & iKey) == iKey : value.Equals(key); + } + set + { + if (isFlagged) + { + int iValue = (int)(object)this.value; + int iKey = (int)(object)key; + + if (((iValue & iKey) == iKey) == value) return; + + if (value) + Value = (T)(object)(iValue | iKey); + else + Value = (T)(object)(iValue & ~iKey); + } + else + { + if (this.value.Equals(key) == value) return; + if (!value && !canDeselect) return; + + Value = value ? key : blankValue; + } + } + } + + public event PropertyChangedEventHandler PropertyChanged; + + private void OnPropertyChanged([CallerMemberName] string propertyName = "") + { + PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName)); + } +} \ No newline at end of file -- Gitee From 03d5d501d93c4d3465232b5b98c16a1dbd995e2e Mon Sep 17 00:00:00 2001 From: vicwjb Date: Thu, 16 Dec 2021 19:14:44 +0800 Subject: [PATCH 008/675] =?UTF-8?q?=E4=BF=AE=E5=A4=8D=E7=BC=A9=E6=94=BE?= =?UTF-8?q?=E9=94=99=E8=AF=AF?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/IFoxCAD.Cad/ExtensionMethod/EditorEx.cs | 6 +++--- tests/DBTrans.test/testeditor.cs | 24 +++++++++++++++++++++ tests/DBTrans.test/testenv.cs | 13 +---------- 3 files changed, 28 insertions(+), 15 deletions(-) diff --git a/src/IFoxCAD.Cad/ExtensionMethod/EditorEx.cs b/src/IFoxCAD.Cad/ExtensionMethod/EditorEx.cs index bca5891..005e82e 100644 --- a/src/IFoxCAD.Cad/ExtensionMethod/EditorEx.cs +++ b/src/IFoxCAD.Cad/ExtensionMethod/EditorEx.cs @@ -577,12 +577,12 @@ public static void ZoomExtents(this Editor ed, double offsetDist = 0.00) Database db = ed.Document.Database; db.UpdateExt(true); - if (db.GetValidExtents3d(out Extents3d extents3D) == true) + if (db.GetValidExtents3d(out Extents3d extents3D)) { - ed.ZoomWindow(extents3D.MinPoint, extents3D.MinPoint, offsetDist); + ed.ZoomWindow(extents3D.MinPoint, extents3D.MaxPoint, offsetDist); return; } - ed.ZoomWindow(db.Extmax, db.Extmin, offsetDist); + ed.ZoomWindow(db.Extmin, db.Extmax, offsetDist); } /// diff --git a/tests/DBTrans.test/testeditor.cs b/tests/DBTrans.test/testeditor.cs index 397d5c4..7740285 100644 --- a/tests/DBTrans.test/testeditor.cs +++ b/tests/DBTrans.test/testeditor.cs @@ -34,5 +34,29 @@ public void tested() var s = ed.GetString("qustr"); Env.Editor.WriteMessage(""); } + [CommandMethod("testzoom")] + public void testzoom() + { + using var tr = new DBTrans(); + var res = Env.Editor.GetEntity("\npick ent:"); + if (res.Status == Autodesk.AutoCAD.EditorInput.PromptStatus.OK) + { + Env.Editor.ZoomObject(res.ObjectId.GetObject()); + } + + + } + [CommandMethod("testzoomextent")] + public void testzoomextent() + { + //using var tr = new DBTrans(); + //var res = Env.Editor.GetEntity("\npick ent:"); + //if (res.Status == Autodesk.AutoCAD.EditorInput.PromptStatus.OK) + //{ + // Env.Editor.ZoomObject(res.ObjectId.GetObject()); + //} + + Env.Editor.ZoomExtents(); + } } } diff --git a/tests/DBTrans.test/testenv.cs b/tests/DBTrans.test/testenv.cs index 274d5d8..e353708 100644 --- a/tests/DBTrans.test/testenv.cs +++ b/tests/DBTrans.test/testenv.cs @@ -66,17 +66,6 @@ public void testosmode1() } - [CommandMethod("testzoom")] - public void testzoom() - { - using var tr = new DBTrans(); - var res = Env.Editor.GetEntity("\npick ent:"); - if (res.Status == Autodesk.AutoCAD.EditorInput.PromptStatus.OK) - { - Env.Editor.ZoomObject(res.ObjectId.GetObject()); - } - - - } + } } -- Gitee From 6037f88d9371cc17dee75367ad55530e38f35f29 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=B0=8F=E8=B4=B1=E8=B4=B1?= <369034346@qq.com> Date: Tue, 21 Dec 2021 15:34:46 +0000 Subject: [PATCH 009/675] update README.md. --- README.md | 32 +++++++++++++++++++++----------- 1 file changed, 21 insertions(+), 11 deletions(-) diff --git a/README.md b/README.md index 48292fc..9ca7377 100644 --- a/README.md +++ b/README.md @@ -68,17 +68,27 @@ 为了将程序集的初始化和通过写注册表的方式实现自动加载统一设置,减少每次重复的工作量,内裤提供了`AutoRegAssem`抽象类来完成此功能,只要在需要初始化的类继承`AutoRegAssem`类,然后实现`Initialize()` 和`Terminate()`两个函数就可以了。特别强调的是,一个程序集里只能有一个类继承,不管是不是同一个命名空间。 ```c# - public class Test : AutoRegAssem //继承 - { - public override void Initialize() //实现接口函数 - { - throw new NotImplementedException(); - } - public override void Terminate() //实现接口函数 - { - throw new NotImplementedException(); - } - } +using Autodesk.AutoCAD.Runtime; +using IFoxCAD.Cad; +using System; +using System.Reflection; + +[assembly: ExtensionApplication(typeof(CadLoad.CmdINI))] +namespace CadLoad +{ + internal class CmdINI : AutoRegAssem + { + public override void Initialize() + { + throw new NotImplementedException(); + } + + public override void Terminate() + { + throw new NotImplementedException(); + } + } +} ``` 7. 天秀的打开模式提权 -- Gitee From ce158056b84707b8062186cb90f06f4196e41989 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=B0=8F=E8=B4=B1=E8=B4=B1?= <369034346@qq.com> Date: Tue, 21 Dec 2021 15:35:19 +0000 Subject: [PATCH 010/675] update README.md. --- README.md | 33 ++++++++++++++++----------------- 1 file changed, 16 insertions(+), 17 deletions(-) diff --git a/README.md b/README.md index 9ca7377..88d2661 100644 --- a/README.md +++ b/README.md @@ -68,27 +68,26 @@ 为了将程序集的初始化和通过写注册表的方式实现自动加载统一设置,减少每次重复的工作量,内裤提供了`AutoRegAssem`抽象类来完成此功能,只要在需要初始化的类继承`AutoRegAssem`类,然后实现`Initialize()` 和`Terminate()`两个函数就可以了。特别强调的是,一个程序集里只能有一个类继承,不管是不是同一个命名空间。 ```c# -using Autodesk.AutoCAD.Runtime; -using IFoxCAD.Cad; -using System; -using System.Reflection; - -[assembly: ExtensionApplication(typeof(CadLoad.CmdINI))] -namespace CadLoad -{ - internal class CmdINI : AutoRegAssem + using Autodesk.AutoCAD.Runtime; + using IFoxCAD.Cad; + using System; + using System.Reflection; + [assembly: ExtensionApplication(typeof(CadLoad.CmdINI))] + namespace CadLoad { - public override void Initialize() + internal class CmdINI : AutoRegAssem { - throw new NotImplementedException(); - } - - public override void Terminate() - { - throw new NotImplementedException(); + public override void Initialize() + { + throw new NotImplementedException(); + } + + public override void Terminate() + { + throw new NotImplementedException(); + } } } -} ``` 7. 天秀的打开模式提权 -- Gitee From f6211c4f5c3468b95512bdf3f39fa6c1db2f155d Mon Sep 17 00:00:00 2001 From: vicwjb Date: Thu, 23 Dec 2021 00:13:22 +0800 Subject: [PATCH 011/675] 0.2.0.2 --- src/IFoxCAD.Cad/IFoxCAD.Cad.csproj | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/src/IFoxCAD.Cad/IFoxCAD.Cad.csproj b/src/IFoxCAD.Cad/IFoxCAD.Cad.csproj index ef105fd..b685601 100644 --- a/src/IFoxCAD.Cad/IFoxCAD.Cad.csproj +++ b/src/IFoxCAD.Cad/IFoxCAD.Cad.csproj @@ -3,7 +3,7 @@ net45 true - 0.2.0 + 0.2.0.2 InspireFunction xsfhlzh;vicwjb 基于.NET的Cad二次开发类库 @@ -38,9 +38,6 @@ - - - -- Gitee From 2e729086d216cf695e9cb310e5132028ceb0a387 Mon Sep 17 00:00:00 2001 From: vicwjb Date: Tue, 4 Jan 2022 22:36:00 +0800 Subject: [PATCH 012/675] wpf 0.2.1 --- src/IFoxCAD.WPF/IFoxCAD.WPF.csproj | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/IFoxCAD.WPF/IFoxCAD.WPF.csproj b/src/IFoxCAD.WPF/IFoxCAD.WPF.csproj index 7cc8354..51532d1 100644 --- a/src/IFoxCAD.WPF/IFoxCAD.WPF.csproj +++ b/src/IFoxCAD.WPF/IFoxCAD.WPF.csproj @@ -6,7 +6,7 @@ true true true - 0.2.0 + 0.2.1 xsfhlzh;vicwjb InspireFunction WPF的简单MVVM模式开发类库 @@ -16,7 +16,7 @@ https://gitee.com/inspirefunction/ifoxcad.git IFoxCAD;C#;NET;WPF;MVVM git - 增加c#10语法相关内容. + 增加 EnumSelection. preview -- Gitee From 22fac0fb0b3eb0fc6f30e5ff314733197a5eddc1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=B0=8F=E8=B4=B1=E8=B4=B1?= <369034346@qq.com> Date: Fri, 7 Jan 2022 16:43:34 +0000 Subject: [PATCH 013/675] update docs/WPF.md. --- docs/WPF.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/WPF.md b/docs/WPF.md index 08d7c28..73f4f11 100644 --- a/docs/WPF.md +++ b/docs/WPF.md @@ -225,7 +225,7 @@ public class TestViewModel : ViewModelBase 首先是在xaml里引入命名空间。 -`xmlns:eb="clr-namespace:IFoxCad.WPF;assembly=IFoxCad"` +`xmlns:eb="clr-namespace:IFoxCAD.WPF;assembly=IFoxCAD.WPF"` 然后 -- Gitee From 9180ced8efb38a58dc1174498db37b992260591f Mon Sep 17 00:00:00 2001 From: liuqihong <540762622@qq.com> Date: Tue, 11 Jan 2022 16:43:44 +0800 Subject: [PATCH 014/675] =?UTF-8?q?=E4=BF=AE=E6=94=B9=E6=A1=88=E4=BE=8B?= =?UTF-8?q?=E9=94=99=E8=AF=AF?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.en.md | 36 ------------------------------------ README.md | 2 +- 2 files changed, 1 insertion(+), 37 deletions(-) delete mode 100644 README.en.md diff --git a/README.en.md b/README.en.md deleted file mode 100644 index 7c3d2c4..0000000 --- a/README.en.md +++ /dev/null @@ -1,36 +0,0 @@ -# IFoxCAD - -#### Description -基于.NET的Cad二次开发类库 - -#### Software Architecture -Software architecture description - -#### Installation - -1. xxxx -2. xxxx -3. xxxx - -#### Instructions - -1. xxxx -2. xxxx -3. xxxx - -#### Contribution - -1. Fork the repository -2. Create Feat_xxx branch -3. Commit your code -4. Create Pull Request - - -#### Gitee Feature - -1. You can use Readme\_XXX.md to support different languages, such as Readme\_en.md, Readme\_zh.md -2. Gitee blog [blog.gitee.com](https://blog.gitee.com) -3. Explore open source project [https://gitee.com/explore](https://gitee.com/explore) -4. The most valuable open source project [GVP](https://gitee.com/gvp) -5. The manual of Gitee [https://gitee.com/help](https://gitee.com/help) -6. The most popular members [https://gitee.com/gitee-stars/](https://gitee.com/gitee-stars/) diff --git a/README.md b/README.md index f312887..c511226 100644 --- a/README.md +++ b/README.md @@ -40,7 +40,7 @@ [CommandMethod("hello")] public void Hello() { - using var tr = new DBTrans() + using var tr = new DBTrans(); var line1 = new Line(new Point3d(0, 0, 0), new Point3d(1, 1, 0)); tr.CurrentSpace.AddEntity(line1); } -- Gitee From f9490e05d5659dcee08f672ec3220918faf01994 Mon Sep 17 00:00:00 2001 From: vicwjb Date: Wed, 12 Jan 2022 14:10:59 +0000 Subject: [PATCH 015/675] update docs/DBTrans.md. --- docs/DBTrans.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/DBTrans.md b/docs/DBTrans.md index 29f88dc..5e75860 100644 --- a/docs/DBTrans.md +++ b/docs/DBTrans.md @@ -59,7 +59,7 @@ using (DBTrans tr = new DBTrans()) // 第一步,开启事务 - **向符号表里添加元素** ```c# - using (DBTransaction tr = new DBTransaction()) + using (DBTrans tr = new DBTrans()) { // 第一步,开启事务 var layerTable = tr.LayerTable; // 第二步,获取图层表 @@ -75,7 +75,7 @@ using (DBTrans tr = new DBTrans()) // 第一步,开启事务 想要添加和获取符号表内的某个元素非常的简单: ```c# - using (DBTransaction tr = new DBTransaction()) // 第一步,开启事务 + using (DBTrans tr = new DBTrans()) // 第一步,开启事务 { var layerTable = tr.LayerTable; // 第二步,获取图层表 layerTable.Add("1"); // 第三步,添加名为“1”的图层,即新建图层 -- Gitee From 1988260f58bd399c12210aea80fdbd9775015b20 Mon Sep 17 00:00:00 2001 From: vicwjb Date: Wed, 12 Jan 2022 15:03:47 +0000 Subject: [PATCH 016/675] =?UTF-8?q?update=20docs/=E5=85=B3=E4=BA=8EIFoxCAD?= =?UTF-8?q?=E7=9A=84=E6=9E=B6=E6=9E=84=E8=AF=B4=E6=98=8E.md.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- ...32\204\346\236\266\346\236\204\350\257\264\346\230\216.md" | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git "a/docs/\345\205\263\344\272\216IFoxCAD\347\232\204\346\236\266\346\236\204\350\257\264\346\230\216.md" "b/docs/\345\205\263\344\272\216IFoxCAD\347\232\204\346\236\266\346\236\204\350\257\264\346\230\216.md" index d828220..68ac9f5 100644 --- "a/docs/\345\205\263\344\272\216IFoxCAD\347\232\204\346\236\266\346\236\204\350\257\264\346\230\216.md" +++ "b/docs/\345\205\263\344\272\216IFoxCAD\347\232\204\346\236\266\346\236\204\350\257\264\346\230\216.md" @@ -7,10 +7,10 @@ IFoxCAD是基于NFOX类库的重制版,主要是提供一个最小化的内核 ## 一、组织结构图 - IFoxCAD - - IFoxCAD.Cad - cad相关的类库 + - IFoxCAD.Basal - cad以外常用的类库 - LinqEx - linq扩展类 - LoopList - 环链表 - - IFoxCAD.Basal - cad以外常用的类库 + - IFoxCAD.Cad - cad相关的类库 - Runtime - 包含系统级别的功能 - AcadVersion - cad版本号类 - AssemInfo - 程序集信息 -- Gitee From 12a03efc8cc18c7a7bb231ad80cb897b09ef01a5 Mon Sep 17 00:00:00 2001 From: vicwjb Date: Thu, 13 Jan 2022 23:15:14 +0800 Subject: [PATCH 017/675] =?UTF-8?q?=E4=BF=AE=E5=A4=8D=E8=BF=87=E6=BB=A4?= =?UTF-8?q?=E5=99=A8Op=E7=B1=BB=E5=AE=9E=E4=BE=8B=E4=B8=8D=E8=83=BD?= =?UTF-8?q?=E8=B0=83=E7=94=A8Dxf=E5=87=BD=E6=95=B0=E7=9A=84=E9=94=99?= =?UTF-8?q?=E8=AF=AF?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/IFoxCAD.Cad/SelectionFilter/OpFilter.cs | 8 ++++---- tests/DBTrans.test/testselectfilter.cs | 12 ++++++------ 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/src/IFoxCAD.Cad/SelectionFilter/OpFilter.cs b/src/IFoxCAD.Cad/SelectionFilter/OpFilter.cs index ec45ac9..f579249 100644 --- a/src/IFoxCAD.Cad/SelectionFilter/OpFilter.cs +++ b/src/IFoxCAD.Cad/SelectionFilter/OpFilter.cs @@ -121,7 +121,7 @@ private Op(OpFilter filter) /// /// 操作符类型的可变参数 /// Op对象 - public static Op And(params Op[] args) + public Op And(params Op[] args) { var filter = new OpAnd(); foreach (var op in args) @@ -134,7 +134,7 @@ public static Op And(params Op[] args) /// /// 操作符类型的可变参数 /// Op对象 - public static Op Or(params Op[] args) + public Op Or(params Op[] args) { var filter = new OpOr(); foreach (var op in args) @@ -147,7 +147,7 @@ public static Op Or(params Op[] args) /// /// 组码 /// Op对象 - public static Op Dxf(int code) + public Op Dxf(int code) { return new Op(new OpEqual(code)); } @@ -158,7 +158,7 @@ public static Op Dxf(int code) /// 组码 /// 关系运算符的值,比如">,>,=" /// Op对象 - public static Op Dxf(int code, string content) + public Op Dxf(int code, string content) { return new Op(new OpComp(content, code)); } diff --git a/tests/DBTrans.test/testselectfilter.cs b/tests/DBTrans.test/testselectfilter.cs index 9a4289b..1224a2f 100644 --- a/tests/DBTrans.test/testselectfilter.cs +++ b/tests/DBTrans.test/testselectfilter.cs @@ -20,15 +20,15 @@ public void testfilter() var p = new Point3d(10, 10, 0); var f = OpFilter.Bulid( - e =>!(OpFilter.Op.Dxf(0) == "line" & OpFilter.Op.Dxf(8) == "0") - | OpFilter.Op.Dxf(0) != "circle" & OpFilter.Op.Dxf(8) == "2" & OpFilter.Op.Dxf(10) >= p); + e =>!(e.Dxf(0) == "line" & e.Dxf(8) == "0") + | e.Dxf(0) != "circle" & e.Dxf(8) == "2" & e.Dxf(10) >= p); var f2 = OpFilter.Bulid( - e => OpFilter.Op.Or( - !OpFilter.Op.And(OpFilter.Op.Dxf(0) == "line", OpFilter.Op.Dxf(8) == "0"), - OpFilter.Op.And(OpFilter.Op.Dxf(0) != "circle", OpFilter.Op.Dxf(8) == "2", - OpFilter.Op.Dxf(10) <= new Point3d(10, 10, 0)))); + e => e.Or( + !e.And(e.Dxf(0) == "line", e.Dxf(8) == "0"), + e.And(e.Dxf(0) != "circle", e.Dxf(8) == "2", + e.Dxf(10) <= new Point3d(10, 10, 0)))); SelectionFilter f3 = f; SelectionFilter f4 = f2; -- Gitee From 02cfc2bcffc57babd522991c3d19dbc51546d5f1 Mon Sep 17 00:00:00 2001 From: vicwjb Date: Thu, 13 Jan 2022 23:31:16 +0800 Subject: [PATCH 018/675] =?UTF-8?q?=E4=BC=98=E5=8C=96readme?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index c93078b..bdf9d7d 100644 --- a/README.md +++ b/README.md @@ -24,7 +24,18 @@ 1. 快速入门 - - 打开vs,新建一个standard类型的类库项目,修改项目文件里的`netcore2.0`为`NET45`。其中的net45,可以改为NET45以上的标准TFM(如:net45、net46、net47等等)。同时可以指定多版本。具体的详细的教程见 [VS通过添加不同引用库,建立多条件编译]( https://www.yuque.com/vicwjb/zqpcd0/ufbwyl)。 + - 打开vs,新建一个standard类型的类库项目 + - 双击项目,打开项目文件: + -- 修改项目文件里的`netcore2.0`为`NET45`。其中的net45,可以改为NET45以上的标准TFM(如:net45、net46、net47等等)。同时可以指定多版本。具体的详细的教程见 [VS通过添加不同引用库,建立多条件编译]( https://www.yuque.com/vicwjb/zqpcd0/ufbwyl)。 + -- 在 ` xxx ` 中增加 `preview`,主要是为了支持最新的语法,本项目采用了最新的语法编写。项目文件想在的内容类似如下: + ``` + + + net45 + preview + + + ``` - 右键项目文件,选择管理nuget程序包。 - 在nuget程序里搜索**ifoxcad**,直接选择最新的版本(如果你是 **net40** 或者 **net35** 的用户,可以安装 **0.1.6** 版本),然后点击安装**IFoxCAD.Cad**,nuget会自动安装ifoxcad依赖的库。 - 添加引用 -- Gitee From b105c178fdde12955b142554c095558b6ef1216d Mon Sep 17 00:00:00 2001 From: vicwjb Date: Thu, 13 Jan 2022 15:39:03 +0000 Subject: [PATCH 019/675] update README.md. --- README.md | 29 +++++++++++++++++++---------- 1 file changed, 19 insertions(+), 10 deletions(-) diff --git a/README.md b/README.md index bdf9d7d..c6027fe 100644 --- a/README.md +++ b/README.md @@ -26,16 +26,19 @@ - 打开vs,新建一个standard类型的类库项目 - 双击项目,打开项目文件: - -- 修改项目文件里的`netcore2.0`为`NET45`。其中的net45,可以改为NET45以上的标准TFM(如:net45、net46、net47等等)。同时可以指定多版本。具体的详细的教程见 [VS通过添加不同引用库,建立多条件编译]( https://www.yuque.com/vicwjb/zqpcd0/ufbwyl)。 - -- 在 ` xxx ` 中增加 `preview`,主要是为了支持最新的语法,本项目采用了最新的语法编写。项目文件想在的内容类似如下: - ``` - - - net45 - preview - - - ``` + - 修改项目文件里的`netcore2.0`为`NET45`。其中的net45,可以改为NET45以上的标准TFM(如:net45、net46、net47等等)。同时可以指定多版本。具体的详细的教程见 [VS通过添加不同引用库,建立多条件编译]( https://www.yuque.com/vicwjb/zqpcd0/ufbwyl)。 + - 在 ` xxx ` 中增加 `preview`,主要是为了支持最新的语法,本项目采用了最新的语法编写。项目文件现在的内容类似如下: + + + ``` + + + net45 + preview + + + ``` + - 右键项目文件,选择管理nuget程序包。 - 在nuget程序里搜索**ifoxcad**,直接选择最新的版本(如果你是 **net40** 或者 **net35** 的用户,可以安装 **0.1.6** 版本),然后点击安装**IFoxCAD.Cad**,nuget会自动安装ifoxcad依赖的库。 - 添加引用 @@ -58,6 +61,12 @@ using var tr = new DBTrans(); var line1 = new Line(new Point3d(0, 0, 0), new Point3d(1, 1, 0)); tr.CurrentSpace.AddEntity(line1); + // 如果你没有添加preview到项目文件里的话:按如下旧语法: + // using(var tr = new DBTrans()) + // { + // var line1 = new Line(new Point3d(0, 0, 0), new Point3d(1, 1, 0)); + // tr.CurrentSpace.AddEntity(line1); + // } } ``` -- Gitee From cec5f2f9ba104191bdc3baeb85dd5f6ee01817b6 Mon Sep 17 00:00:00 2001 From: vicwjb Date: Thu, 13 Jan 2022 23:57:33 +0800 Subject: [PATCH 020/675] v0.2.0.3 --- src/IFoxCAD.Cad/IFoxCAD.Cad.csproj | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/IFoxCAD.Cad/IFoxCAD.Cad.csproj b/src/IFoxCAD.Cad/IFoxCAD.Cad.csproj index b685601..e831042 100644 --- a/src/IFoxCAD.Cad/IFoxCAD.Cad.csproj +++ b/src/IFoxCAD.Cad/IFoxCAD.Cad.csproj @@ -3,7 +3,7 @@ net45 true - 0.2.0.2 + 0.2.0.3 InspireFunction xsfhlzh;vicwjb 基于.NET的Cad二次开发类库 @@ -12,7 +12,7 @@ https://gitee.com/inspirefunction/ifoxcad.git git IFoxCAD;CAD;AutoCad;C#;NET - 删除net35支持,升级至net45,增加c#10语法相关内容. + 修复选择集过滤器错误. true true preview -- Gitee From 864f35e3225baf81bcfc22381f92fbee4439833b Mon Sep 17 00:00:00 2001 From: vicwjb Date: Mon, 17 Jan 2022 22:11:47 +0800 Subject: [PATCH 021/675] =?UTF-8?q?=E4=BF=AE=E5=A4=8D=E7=89=88=E6=9C=AC?= =?UTF-8?q?=E4=BF=A1=E6=81=AF=E7=B1=BB=E5=8F=AF=E4=BB=A5=E8=A2=AB=E5=AE=9E?= =?UTF-8?q?=E4=BE=8B=E5=8C=96=E5=90=8E=E5=87=BA=E9=94=99=E7=9A=84=E9=97=AE?= =?UTF-8?q?=E9=A2=98=20fixed=20#I4R74V?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/IFoxCAD.Cad/Runtime/AcadVersion.cs | 97 +++++++------------------- src/IFoxCAD.Cad/Runtime/CadVersion.cs | 38 ++++++++++ tests/DBTrans.test/testenv.cs | 16 +++++ 3 files changed, 81 insertions(+), 70 deletions(-) create mode 100644 src/IFoxCAD.Cad/Runtime/CadVersion.cs diff --git a/src/IFoxCAD.Cad/Runtime/AcadVersion.cs b/src/IFoxCAD.Cad/Runtime/AcadVersion.cs index 5441a09..cb6870a 100644 --- a/src/IFoxCAD.Cad/Runtime/AcadVersion.cs +++ b/src/IFoxCAD.Cad/Runtime/AcadVersion.cs @@ -1,79 +1,47 @@ namespace IFoxCAD.Cad; using Registry = Microsoft.Win32.Registry; + /// /// cad版本号类 /// -public class AcadVersion +public static class AcadVersion { - /// - /// 主版本 - /// - public int Major - { private set; get; } - - /// - /// 次版本 - /// - public int Minor - { private set; get; } - - /// - /// 版本号 - /// - public double ProgId => double.Parse($"{Major}.{Minor}"); - - /// - /// 注册表名称 - /// - public string ProductName - { private set; get; } - - /// - /// 注册表位置 - /// - public string ProductRootKey - { private set; get; } - private static readonly string _pattern = @"Autodesk\\AutoCAD\\R(\d+)\.(\d+)\\.*?"; - private static List _versions; - /// /// 所有安装的cad的版本号 /// - public static List Versions + public static List Versions { get { - if (_versions == null) + + string[] copys = + Registry.LocalMachine + .OpenSubKey(@"SOFTWARE\Autodesk\Hardcopy") + .GetValueNames(); + var _versions = new List(); + foreach (var rootkey in copys) { - string[] copys = - Registry.LocalMachine - .OpenSubKey(@"SOFTWARE\Autodesk\Hardcopy") - .GetValueNames(); - _versions = new List(); - foreach (var rootkey in copys) + if (Regex.IsMatch(rootkey, _pattern)) { - if (Regex.IsMatch(rootkey, _pattern)) - { - var gs = Regex.Match(rootkey, _pattern).Groups; - var ver = - new AcadVersion - { - ProductRootKey = rootkey, - ProductName = - Registry.LocalMachine - .OpenSubKey("SOFTWARE") - .OpenSubKey(rootkey) - .GetValue("ProductName") - .ToString(), + var gs = Regex.Match(rootkey, _pattern).Groups; + var ver = + new CadVersion + { + ProductRootKey = rootkey, + ProductName = + Registry.LocalMachine + .OpenSubKey("SOFTWARE") + .OpenSubKey(rootkey) + .GetValue("ProductName") + .ToString(), - Major = int.Parse(gs[1].Value), - Minor = int.Parse(gs[2].Value), - }; + Major = int.Parse(gs[1].Value), + Minor = int.Parse(gs[2].Value), + }; - _versions.Add(ver); - } + _versions.Add(ver); } } return _versions; @@ -83,7 +51,7 @@ public static List Versions /// 已打开的cad的版本号 /// 已打开cad的application对象 /// cad版本号对象 - public static AcadVersion FromApp(object app) + public static CadVersion FromApp(object app) { if (app == null) { @@ -107,17 +75,6 @@ public static AcadVersion FromApp(object app) if (ver.Major == major && ver.Minor == minor) return ver; } - return null; } - - /// - /// 转换为字符串 - /// - /// 表示版本号的字符串 - public override string ToString() - { - return - $"名称:{ProductName}\n版本号:{ProgId}\n注册表位置:{ProductRootKey}"; - } } diff --git a/src/IFoxCAD.Cad/Runtime/CadVersion.cs b/src/IFoxCAD.Cad/Runtime/CadVersion.cs new file mode 100644 index 0000000..95cb119 --- /dev/null +++ b/src/IFoxCAD.Cad/Runtime/CadVersion.cs @@ -0,0 +1,38 @@ +namespace IFoxCAD.Cad; + +public record CadVersion +{ + /// + /// 主版本 + /// + public int Major; + + /// + /// 次版本 + /// + public int Minor; + + /// + /// 版本号 + /// + public double ProgId => double.Parse($"{Major}.{Minor}"); + + /// + /// 注册表名称 + /// + public string ProductName; + + /// + /// 注册表位置 + /// + public string ProductRootKey; + + /// + /// 转换为字符串 + /// + /// 表示版本号的字符串 + public override string ToString() + { + return $"名称:{ProductName}\n版本号:{ProgId}\n注册表位置:{ProductRootKey}"; + } +} diff --git a/tests/DBTrans.test/testenv.cs b/tests/DBTrans.test/testenv.cs index e353708..1f6006e 100644 --- a/tests/DBTrans.test/testenv.cs +++ b/tests/DBTrans.test/testenv.cs @@ -10,6 +10,13 @@ namespace test { + public static class ObjEx + { + public static void Print(this Object obj) + { + Env.Print(obj); + } + } public class testenv { [CommandMethod("testenum")] @@ -66,6 +73,15 @@ public void testosmode1() } + [CommandMethod("testcadver")] + public void testcadver() + { + //Env.Print(AcadVersion.Versions); + AcadVersion.Versions.ForEach(v => Env.Print(v)); + AcadVersion.FromApp(Application.AcadApplication).Print(); + + } + } } -- Gitee From b7e5d7e0bd9b5900da2ca9b1fa78b1965e1add61 Mon Sep 17 00:00:00 2001 From: vicwjb Date: Thu, 20 Jan 2022 21:59:02 +0800 Subject: [PATCH 022/675] =?UTF-8?q?=E4=BF=AE=E5=A4=8DSymbolTable=20=E7=9A=84=20Database=20=E5=B1=9E=E6=80=A7?= =?UTF-8?q?=E6=9C=AA=E5=88=9D=E5=A7=8B=E5=8C=96=E3=80=82=20fixed=20#I4ROGQ?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/IFoxCAD.Cad/ExtensionMethod/EditorEx.cs | 106 ++++++ .../ExtensionMethod/SymbolTableEx.cs | 3 +- src/IFoxCAD.Cad/IFoxCAD.Cad.csproj | 4 +- src/IFoxCAD.Cad/Runtime/SymbolTable.cs | 1 + tests/DBTrans.test/Test.cs | 126 ------- tests/DBTrans.test/TestJig.cs | 327 ++++++++++++++++++ tests/DBTrans.test/testblock.cs | 257 ++++++++++++++ tests/DBTrans.test/testenv.cs | 16 +- 8 files changed, 710 insertions(+), 130 deletions(-) create mode 100644 tests/DBTrans.test/TestJig.cs create mode 100644 tests/DBTrans.test/testblock.cs diff --git a/src/IFoxCAD.Cad/ExtensionMethod/EditorEx.cs b/src/IFoxCAD.Cad/ExtensionMethod/EditorEx.cs index 005e82e..9326762 100644 --- a/src/IFoxCAD.Cad/ExtensionMethod/EditorEx.cs +++ b/src/IFoxCAD.Cad/ExtensionMethod/EditorEx.cs @@ -54,6 +54,112 @@ public static SelectionSet SelectByLineWeight(this Editor editor, LineWeight lin PromptSelectionResult res = editor.SelectAll(filter); return res.Value; } + + //#region 即时选择样板 + + ///// + ///// 即时选择,框选更新关键字 + ///// + //public static void SelectTest() + //{ + // Env.Editor.WriteMessage("\n[白嫖工具]--测试"); + // //激活选中事件 + // Env.Editor.SelectionAdded += SelectTest_SelectionAdded; + // //初始化坐标系 + // Env.Editor.CurrentUserCoordinateSystem = Matrix3d.Identity; + + // //创建过滤器 + // var sf = new OpEqual(0, "arc"); + // var pso = new PromptSelectionOptions + // { + // MessageForAdding = "\n请选择对象:" + // }; + + // pso.Keywords.Add("Z"); + // pso.Keywords.Add("X"); + // pso.Keywords.Add("Q"); + // //注册关键字 + // pso.KeywordInput += SelectTest_KeywordInput; + // try + // { + // //用户选择 + // var psr = Env.Editor.GetSelection(pso, sf); + // //处理代码 + + + // } + // catch (Exception ex)//捕获关键字 + // { + // if (ex.Message == "XuError") + // { + // //关闭关键字事件 + // pso.KeywordInput -= SelectTest_KeywordInput; + // //关闭选中事件 + // Env.Editor.SelectionAdded -= SelectTest_SelectionAdded; + // //重新调用自身 + // ZengLiangYuanJiao(); + // } + // } + // //关闭关键字事件 + // pso.KeywordInput -= SelectTest_KeywordInput; + // //关闭选中事件 + // Env.Editor.SelectionAdded -= SelectTest_SelectionAdded; + //} + + ///// + ///// 即时选择 + ///// + ///// + ///// + //private static void SelectTest_SelectionAdded(object sender, SelectionAddedEventArgs e) + //{ + // //关闭选中事件 + // Env.Editor.SelectionAdded -= SelectTest_SelectionAdded; + // using (var tr = new DBTrans()) + // { + // //处理代码 + // for (int i = 0; i < e.AddedObjects.Count; i++) + // { + + + // //处理完移除已处理的对象 + // e.Remove(i); + // } + // } + // //激活选中事件 + // Env.Editor.SelectionAdded += SelectTest_SelectionAdded; + //} + + ///// + ///// 关键字响应 + ///// + ///// + ///// + //private static void SelectTest_KeywordInput(object sender, SelectionTextInputEventArgs e) + //{ + // //获取关键字 + // switch (e.Input) + // { + // case "Z": + // { + // break; + // } + // case "X": + // { + // break; + // } + + // case "Q": + // { + // break; + // } + // } + // //抛出异常,用于更新提示信息 + // throw new ArgumentException("XuError"); + //} + + + //#endregion #endregion #region Info diff --git a/src/IFoxCAD.Cad/ExtensionMethod/SymbolTableEx.cs b/src/IFoxCAD.Cad/ExtensionMethod/SymbolTableEx.cs index f8b7880..7b4bb9d 100644 --- a/src/IFoxCAD.Cad/ExtensionMethod/SymbolTableEx.cs +++ b/src/IFoxCAD.Cad/ExtensionMethod/SymbolTableEx.cs @@ -161,8 +161,9 @@ public static ObjectId GetBlockFrom(this SymbolTable net45 true - 0.2.0.3 + 0.2.0.4 InspireFunction xsfhlzh;vicwjb 基于.NET的Cad二次开发类库 @@ -12,7 +12,7 @@ https://gitee.com/inspirefunction/ifoxcad.git git IFoxCAD;CAD;AutoCad;C#;NET - 修复选择集过滤器错误. + 修复版本信息类错误. true true preview diff --git a/src/IFoxCAD.Cad/Runtime/SymbolTable.cs b/src/IFoxCAD.Cad/Runtime/SymbolTable.cs index 727dc63..5174057 100644 --- a/src/IFoxCAD.Cad/Runtime/SymbolTable.cs +++ b/src/IFoxCAD.Cad/Runtime/SymbolTable.cs @@ -32,6 +32,7 @@ public class SymbolTable : IEnumerable internal SymbolTable(DBTrans tr, ObjectId tableId) { DTrans = tr; + Database = tr.Database; CurrentSymbolTable = DTrans.GetObject(tableId); } diff --git a/tests/DBTrans.test/Test.cs b/tests/DBTrans.test/Test.cs index 99b283d..1e40df7 100644 --- a/tests/DBTrans.test/Test.cs +++ b/tests/DBTrans.test/Test.cs @@ -200,133 +200,7 @@ public void Addpl2() tr.CurrentSpace.AddPline(pts); } - //块定义 - [CommandMethod("blockdef")] - public void BlockDef() - { - using var tr = new DBTrans(); - //var line = new Line(new Point3d(0, 0, 0), new Point3d(1, 1, 0)); - tr.BlockTable.Add("test", - btr => - { - btr.Origin = new Point3d(0, 0, 0); - }, - () => //图元 - { - return new List { new Line(new Point3d(0, 0, 0), new Point3d(1, 1, 0)) }; - }, - () => //属性定义 - { - var id1 = new AttributeDefinition() { Position = new Point3d(0, 0, 0), Tag = "start", Height = 0.2 }; - var id2 = new AttributeDefinition() { Position = new Point3d(1, 1, 0), Tag = "end", Height = 0.2 }; - return new List { id1, id2 }; - } - ); - //ObjectId objectId = tr.BlockTable.Add("a");//新建块 - //objectId.GetObject().AddEntity();//测试添加空实体 - } - //修改块定义 - [CommandMethod("blockdefchange")] - public void BlockDefChange() - { - using var tr = new DBTrans(); - //var line = new Line(new Point3d(0, 0, 0), new Point3d(1, 1, 0)); - tr.BlockTable.Change("test", btr => - { - btr.Origin = new Point3d(5, 5, 0); - btr.AddEntity(new Circle(new Point3d(0, 0, 0), Vector3d.ZAxis, 2)); - btr.GetEntities() - .ToList() - .ForEach(e => e.Flush()); //刷新块显示 - - }); - tr.Editor.Regen(); - } - - [CommandMethod("insertblockdef")] - public void InsertBlockDef() - { - using var tr = new DBTrans(); - var line1 = new Line(new Point3d(0, 0, 0), new Point3d(1, 1, 0)); - var line2 = new Line(new Point3d(0, 0, 0), new Point3d(-1, 1, 0)); - var att1 = new AttributeDefinition() { Position = new Point3d(10, 10, 0), Tag = "tagTest1", Height = 1, TextString = "valueTest1" }; - var att2 = new AttributeDefinition() { Position = new Point3d(10, 12, 0), Tag = "tagTest2", Height = 1, TextString = "valueTest2" }; - tr.BlockTable.Add("test1", line1, line2, att1, att2); - - - var ents = new List(); - var line5 = new Line(new Point3d(0, 0, 0), new Point3d(1, 1, 0)); - var line6 = new Line(new Point3d(0, 0, 0), new Point3d(-1, 1, 0)); - ents.Add(line5); - ents.Add(line6); - tr.BlockTable.Add("test44", ents); - - - var line3 = new Line(new Point3d(5, 5, 0), new Point3d(6, 6, 0)); - var line4 = new Line(new Point3d(5, 5, 0), new Point3d(-6, 6, 0)); - var att3 = new AttributeDefinition() { Position = new Point3d(10, 14, 0), Tag = "tagTest3", Height = 1, TextString = "valueTest3" }; - var att4 = new AttributeDefinition() { Position = new Point3d(10, 16, 0), Tag = "tagTest4", Height = 1, TextString = "valueTest4" }; - tr.BlockTable.Add("test2", new List { line3, line4 }, new List { att3, att4 }); - //tr.CurrentSpace.InsertBlock(new Point3d(4, 4, 0), "test1"); // 测试默认 - //tr.CurrentSpace.InsertBlock(new Point3d(4, 4, 0), "test2"); - //tr.CurrentSpace.InsertBlock(new Point3d(4, 4, 0), "test3"); //测试插入不存在的块定义 - //tr.CurrentSpace.InsertBlock(new Point3d(0, 0, 0), "test1", new Scale3d(2)); // 测试放大2倍 - //tr.CurrentSpace.InsertBlock(new Point3d(4, 4, 0), "test1", new Scale3d(2), Math.PI / 4); // 测试放大2倍,旋转45度 - - var def1 = new Dictionary - { - { "tagTest1", "1" }, - { "tagTest2", "2" } - }; - tr.CurrentSpace.InsertBlock(new Point3d(0, 0, 0), "test1", atts: def1); - var def2 = new Dictionary - { - { "tagTest3", "1" }, - { "tagTest4", "" } - }; - tr.CurrentSpace.InsertBlock(new Point3d(10, 10, 0), "test2", atts: def2); - tr.CurrentSpace.InsertBlock(new Point3d(-10, 0, 0), "test44"); - } - - [CommandMethod("testblocknullbug")] - public void TestBlockNullBug() - { - using var tr = new DBTrans(); - - var ents = new List(); - var line5 = new Line(new Point3d(0, 0, 0), new Point3d(1, 1, 0)); - var line6 = new Line(new Point3d(0, 0, 0), new Point3d(-1, 1, 0)); - ents.Add(line5); - ents.Add(line6); - tr.BlockTable.Add("test44", ents); - tr.CurrentSpace.InsertBlock(new Point3d(0, 0, 0), "test44"); - } - - [CommandMethod("testclip")] - public void TestClipBlock() - { - using var tr = new DBTrans(); - tr.BlockTable.Add("test1", - btr => - { - btr.Origin = new Point3d(0, 0, 0); - btr.AddEntity(new Line(new Point3d(0, 0, 0), new Point3d(10, 10, 0)), - new Line(new Point3d(10, 10, 0), new Point3d(10, 0, 0)) - ); - } - ); - //tr.BlockTable.Add("hah"); - var id = tr.CurrentSpace.InsertBlock(new Point3d(0, 0, 0), "test1"); - var bref = tr.GetObject(id); - var pts = new List { new Point3d(3, 3, 0), new Point3d(7, 3, 0), new Point3d(7, 7, 0), new Point3d(3, 7, 0) }; - bref.ClipBlockRef(pts); - - var id1 = tr.CurrentSpace.InsertBlock(new Point3d(20, 20, 0), "test1"); - var bref1 = tr.GetObject(id); - - bref1.ClipBlockRef(new Point3d(13, 13, 0), new Point3d(17, 17, 0)); - } diff --git a/tests/DBTrans.test/TestJig.cs b/tests/DBTrans.test/TestJig.cs new file mode 100644 index 0000000..a3328b4 --- /dev/null +++ b/tests/DBTrans.test/TestJig.cs @@ -0,0 +1,327 @@ + + +namespace test; + + +using Autodesk.AutoCAD.ApplicationServices; +using Autodesk.AutoCAD.DatabaseServices; +using Autodesk.AutoCAD.EditorInput; +using Autodesk.AutoCAD.Geometry; +using Autodesk.AutoCAD.Runtime; + +using System; +using System.Windows.Forms; + +public class Commands +{ + + [CommandMethod("loop")] + + public void Loop() + + { + + DocumentCollection dm = + + Autodesk.AutoCAD.ApplicationServices.Application.DocumentManager; + + Editor ed = dm.MdiActiveDocument.Editor; + + // Create and add our message filter + + MyMessageFilter filter = new MyMessageFilter(); + + System.Windows.Forms.Application.AddMessageFilter(filter); + + // Start the loop + + while (true) + + { + + // Check for user input events + + System.Windows.Forms.Application.DoEvents(); + + // Check whether the filter has set the flag + + if (filter.bCanceled == true) + + { + + ed.WriteMessage("\nLoop cancelled."); + + break; + + } + + ed.WriteMessage($"\nInside while loop...and {filter.Key}"); + + } + + // We're done - remove the message filter + + System.Windows.Forms.Application.RemoveMessageFilter(filter); + + } + + + + // Our message filter class + + public class MyMessageFilter : IMessageFilter + + { + + public const int WM_KEYDOWN = 0x0100; + + public bool bCanceled = false; + public Keys Key { get; private set; } + public bool PreFilterMessage(ref Message m) + + { + + if (m.Msg == WM_KEYDOWN) + + { + + // Check for the Escape keypress + + Keys kc = (Keys)(int)m.WParam & Keys.KeyCode; + + if (m.Msg == WM_KEYDOWN && kc == Keys.Escape) + + { + + bCanceled = true; + + } + Key = kc; + + // Return true to filter all keypresses + + return true; + + } + + // Return false to let other messages through + + return false; + + } + + } + + + + + + + + + + [CommandMethod("QT")] + static public void QuickText() + { + Document doc = + Autodesk.AutoCAD.ApplicationServices.Application.DocumentManager.MdiActiveDocument; + Database db = doc.Database; + Editor ed = doc.Editor; + + PromptStringOptions pso = + new PromptStringOptions("\nEnter text string"); + pso.AllowSpaces = true; + PromptResult pr = ed.GetString(pso); + + if (pr.Status != PromptStatus.OK) + return; + + Transaction tr = + doc.TransactionManager.StartTransaction(); + using (tr) + { + BlockTableRecord btr = + (BlockTableRecord)tr.GetObject( + db.CurrentSpaceId, OpenMode.ForWrite + ); + + // Create the text object, set its normal and contents + + DBText txt = new DBText(); + txt.Normal = + ed.CurrentUserCoordinateSystem. + CoordinateSystem3d.Zaxis; + txt.TextString = pr.StringResult; + + // We'll add the text to the database before jigging + // it - this allows alignment adjustments to be + // reflected + + btr.AppendEntity(txt); + tr.AddNewlyCreatedDBObject(txt, true); + + // Create our jig + + TextPlacementJig pj = new TextPlacementJig(tr, db, txt); + + // Loop as we run our jig, as we may have keywords + + PromptStatus stat = PromptStatus.Keyword; + while (stat == PromptStatus.Keyword) + { + PromptResult res = ed.Drag(pj); + stat = res.Status; + if ( + stat != PromptStatus.OK && + stat != PromptStatus.Keyword + ) + return; + } + + tr.Commit(); + } + } + + class TextPlacementJig : EntityJig + { + // Declare some internal state + + Database _db; + Transaction _tr; + Point3d _position; + double _angle, _txtSize; + + // Constructor + + public TextPlacementJig( + Transaction tr, Database db, Entity ent + ) : base(ent) + { + _db = db; + _tr = tr; + _angle = 0; + _txtSize = 1; + } + + protected override SamplerStatus Sampler( + JigPrompts jp + ) + { + // We acquire a point but with keywords + + JigPromptPointOptions po = + new JigPromptPointOptions( + "\nPosition of text" + ); + + po.UserInputControls = + (UserInputControls.Accept3dCoordinates | + UserInputControls.NullResponseAccepted | + UserInputControls.NoNegativeResponseAccepted | + UserInputControls.GovernedByOrthoMode); + + po.SetMessageAndKeywords( + "\nSpecify position of text or " + + "[Bold/Italic/LArger/Smaller/" + + "ROtate90/LEft/Middle/RIght]: ", + "Bold Italic LArger Smaller " + + "ROtate90 LEft Middle RIght" + ); + + PromptPointResult ppr = jp.AcquirePoint(po); + + if (ppr.Status == PromptStatus.Keyword) + { + switch (ppr.StringResult) + { + case "Bold": + { + // TODO + + break; + } + case "Italic": + { + // TODO + + break; + } + case "LArger": + { + // Multiple the text size by two + + _txtSize *= 2; + break; + } + case "Smaller": + { + // Divide the text size by two + + _txtSize /= 2; + break; + } + case "ROtate90": + { + // To rotate clockwise we subtract 90 degrees and + // then normalise the angle between 0 and 360 + + _angle -= Math.PI / 2; + while (_angle < Math.PI * 2) + { + _angle += Math.PI * 2; + } + break; + } + case "LEft": + { + // TODO + + break; + } + case "RIght": + { + // TODO + + break; + } + case "Middle": + { + // TODO + + break; + } + } + + return SamplerStatus.OK; + } + else if (ppr.Status == PromptStatus.OK) + { + // Check if it has changed or not (reduces flicker) + + if ( + _position.DistanceTo(ppr.Value) < + Tolerance.Global.EqualPoint + ) + return SamplerStatus.NoChange; + + _position = ppr.Value; + return SamplerStatus.OK; + } + + return SamplerStatus.Cancel; + } + + protected override bool Update() + { + // Set properties on our text object + + DBText txt = (DBText)Entity; + + txt.Position = _position; + txt.Height = _txtSize; + txt.Rotation = _angle; + + return true; + } + } +} diff --git a/tests/DBTrans.test/testblock.cs b/tests/DBTrans.test/testblock.cs new file mode 100644 index 0000000..27afb09 --- /dev/null +++ b/tests/DBTrans.test/testblock.cs @@ -0,0 +1,257 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +using Autodesk.AutoCAD.ApplicationServices; +using Autodesk.AutoCAD.Colors; +using Autodesk.AutoCAD.DatabaseServices; +using Autodesk.AutoCAD.EditorInput; +using Autodesk.AutoCAD.Geometry; +using Autodesk.AutoCAD.Runtime; + +using IFoxCAD.Cad; +namespace test; +public class TestBlock +{ + //块定义 + [CommandMethod("blockdef")] + public void BlockDef() + { + using var tr = new DBTrans(); + //var line = new Line(new Point3d(0, 0, 0), new Point3d(1, 1, 0)); + tr.BlockTable.Add("test", + btr => + { + btr.Origin = new Point3d(0, 0, 0); + }, + () => //图元 + { + return new List { new Line(new Point3d(0, 0, 0), new Point3d(1, 1, 0)) }; + }, + () => //属性定义 + { + var id1 = new AttributeDefinition() { Position = new Point3d(0, 0, 0), Tag = "start", Height = 0.2 }; + var id2 = new AttributeDefinition() { Position = new Point3d(1, 1, 0), Tag = "end", Height = 0.2 }; + return new List { id1, id2 }; + } + ); + //ObjectId objectId = tr.BlockTable.Add("a");//新建块 + //objectId.GetObject().AddEntity();//测试添加空实体 + } + //修改块定义 + [CommandMethod("blockdefchange")] + public void BlockDefChange() + { + using var tr = new DBTrans(); + //var line = new Line(new Point3d(0, 0, 0), new Point3d(1, 1, 0)); + tr.BlockTable.Change("test", btr => + { + btr.Origin = new Point3d(5, 5, 0); + btr.AddEntity(new Circle(new Point3d(0, 0, 0), Vector3d.ZAxis, 2)); + btr.GetEntities() + .ToList() + .ForEach(e => e.Flush()); //刷新块显示 + + }); + tr.Editor.Regen(); + } + + [CommandMethod("insertblockdef")] + public void InsertBlockDef() + { + using var tr = new DBTrans(); + var line1 = new Line(new Point3d(0, 0, 0), new Point3d(1, 1, 0)); + var line2 = new Line(new Point3d(0, 0, 0), new Point3d(-1, 1, 0)); + var att1 = new AttributeDefinition() { Position = new Point3d(10, 10, 0), Tag = "tagTest1", Height = 1, TextString = "valueTest1" }; + var att2 = new AttributeDefinition() { Position = new Point3d(10, 12, 0), Tag = "tagTest2", Height = 1, TextString = "valueTest2" }; + tr.BlockTable.Add("test1", line1, line2, att1, att2); + + + var ents = new List(); + var line5 = new Line(new Point3d(0, 0, 0), new Point3d(1, 1, 0)); + var line6 = new Line(new Point3d(0, 0, 0), new Point3d(-1, 1, 0)); + ents.Add(line5); + ents.Add(line6); + tr.BlockTable.Add("test44", ents); + + + var line3 = new Line(new Point3d(5, 5, 0), new Point3d(6, 6, 0)); + var line4 = new Line(new Point3d(5, 5, 0), new Point3d(-6, 6, 0)); + var att3 = new AttributeDefinition() { Position = new Point3d(10, 14, 0), Tag = "tagTest3", Height = 1, TextString = "valueTest3" }; + var att4 = new AttributeDefinition() { Position = new Point3d(10, 16, 0), Tag = "tagTest4", Height = 1, TextString = "valueTest4" }; + tr.BlockTable.Add("test2", new List { line3, line4 }, new List { att3, att4 }); + //tr.CurrentSpace.InsertBlock(new Point3d(4, 4, 0), "test1"); // 测试默认 + //tr.CurrentSpace.InsertBlock(new Point3d(4, 4, 0), "test2"); + //tr.CurrentSpace.InsertBlock(new Point3d(4, 4, 0), "test3"); //测试插入不存在的块定义 + //tr.CurrentSpace.InsertBlock(new Point3d(0, 0, 0), "test1", new Scale3d(2)); // 测试放大2倍 + //tr.CurrentSpace.InsertBlock(new Point3d(4, 4, 0), "test1", new Scale3d(2), Math.PI / 4); // 测试放大2倍,旋转45度 + + var def1 = new Dictionary + { + { "tagTest1", "1" }, + { "tagTest2", "2" } + }; + tr.CurrentSpace.InsertBlock(new Point3d(0, 0, 0), "test1", atts: def1); + var def2 = new Dictionary + { + { "tagTest3", "1" }, + { "tagTest4", "" } + }; + tr.CurrentSpace.InsertBlock(new Point3d(10, 10, 0), "test2", atts: def2); + tr.CurrentSpace.InsertBlock(new Point3d(-10, 0, 0), "test44"); + } + + [CommandMethod("testblocknullbug")] + public void TestBlockNullBug() + { + using var tr = new DBTrans(); + + var ents = new List(); + var line5 = new Line(new Point3d(0, 0, 0), new Point3d(1, 1, 0)); + var line6 = new Line(new Point3d(0, 0, 0), new Point3d(-1, 1, 0)); + ents.Add(line5); + ents.Add(line6); + tr.BlockTable.Add("test44", ents); + tr.CurrentSpace.InsertBlock(new Point3d(0, 0, 0), "test44"); + } + + [CommandMethod("test_block_file")] + public void TestBlockFile() + { + var tr = new DBTrans(); + var id = tr.BlockTable.GetBlockFrom(@"C:\Users\vic\Desktop\test.dwg",false); + tr.CurrentSpace.InsertBlock(Point3d.Origin, id); + } + + + [CommandMethod("testclip")] + public void TestClipBlock() + { + using var tr = new DBTrans(); + tr.BlockTable.Add("test1", + btr => + { + btr.Origin = new Point3d(0, 0, 0); + btr.AddEntity(new Line(new Point3d(0, 0, 0), new Point3d(10, 10, 0)), + new Line(new Point3d(10, 10, 0), new Point3d(10, 0, 0)) + ); + } + ); + //tr.BlockTable.Add("hah"); + var id = tr.CurrentSpace.InsertBlock(new Point3d(0, 0, 0), "test1"); + var bref = tr.GetObject(id); + var pts = new List { new Point3d(3, 3, 0), new Point3d(7, 3, 0), new Point3d(7, 7, 0), new Point3d(3, 7, 0) }; + bref.ClipBlockRef(pts); + + var id1 = tr.CurrentSpace.InsertBlock(new Point3d(20, 20, 0), "test1"); + var bref1 = tr.GetObject(id); + + bref1.ClipBlockRef(new Point3d(13, 13, 0), new Point3d(17, 17, 0)); + } + + /// + /// 给用户的测试程序,不知道对错 + /// + [CommandMethod("test_block_ej")] + public void EJ() + { + using var tr = new DBTrans(); + + //Point3d.Origin.AddBellowToModelSpace(100, 100, 5, 3, 30);//画波纹管 + + //Database db2 = new Database(false, true); + //string fullFileName = @".\MyBlockDwgFile\001.dwg"; + //db2.ReadDwgFile(fullFileName, System.IO.FileShare.Read, true, null); + //db2.CloseInput(true); + //string blockName = "test"; + //if (!tr.BlockTable.Has(blockName)) + //{ + // //tr.Database.Insert(blockName, db2, false);//插入块 + // db.Insert(blockName, db2, false); + + //} + + string fullFileName = @".\MyBlockDwgFile\001.dwg"; + var blockdef = tr.BlockTable.GetBlockFrom(fullFileName, false); + + tr.Database.Clayer = tr.LayerTable["0"];//当前图层切换为0图层 + tr.LayerTable.Change(tr.Database.Clayer, ltr => + { + ltr.Color = Color.FromColorIndex(ColorMethod.ByAci, 2); //ColorMethod.ByAci可以让我们使用AutoCAD ACI颜色索引……这里为2(表示黄色) + }); + + ObjectId id = tr.ModelSpace.InsertBlock(Point3d.Origin, blockdef);//插入块参照 + + var entTest = tr.GetObject(id); + entTest.Draw(); + + PromptEntityOptions PEO = new PromptEntityOptions("\n请选择一个块"); + PEO.SetRejectMessage("\n对象必须是块"); + PEO.AddAllowedClass(typeof(BlockReference), true); + + PromptEntityResult PER = Env.Editor.GetEntity(PEO); + if (PER.Status != PromptStatus.OK) + { + return; + } + + var Bref = tr.GetObject(PER.ObjectId); + //var BTR = tr.GetObject(Bref.BlockTableRecord, OpenMode.ForWrite); + ////如果知道块名字BTRName + //BlockTableRecord BTR = tr.GetObject(tr.BlockTable[blockName], OpenMode.ForWrite); + + var btr = tr.BlockTable[Bref.Name]; + + tr.BlockTable.Change(btr, ltr => + { + + foreach (ObjectId OID in ltr) + { + var Ent = tr.GetObject(OID); + using (Ent.ForWrite()) + { + if (Ent is MText mText) + { + switch(mText.Text) + { + case "$$A": + mText.Contents = "hahaha"; + break; + case "$$B": + ; + break; + default: + ; + break; + } + + }; + if (Ent is DBText dBText) { dBText.TextString = "haha"; }; + if (Ent is Dimension dimension) + { + switch (dimension.DimensionText) + { + case "$$pipeLen": + dimension.DimensionText = "350"; + break; + default: + break; + } + }; + } + + + } + ltr.GetEntities() + .ToList() + .ForEach(e => e.Flush()); //刷新块显示 + }); + + + tr.Editor.Regen(); + + + } +} \ No newline at end of file diff --git a/tests/DBTrans.test/testenv.cs b/tests/DBTrans.test/testenv.cs index 1f6006e..b548e2e 100644 --- a/tests/DBTrans.test/testenv.cs +++ b/tests/DBTrans.test/testenv.cs @@ -12,10 +12,22 @@ namespace test { public static class ObjEx { - public static void Print(this Object obj) + /// + /// cad的打印 + /// + /// + public static void Print(this object obj) { Env.Print(obj); } + /// + /// 系统的打印 + /// + /// + public static void PrintLine(this object obj) + { + Console.WriteLine(obj.ToString()); + } } public class testenv { @@ -79,6 +91,8 @@ public void testcadver() //Env.Print(AcadVersion.Versions); AcadVersion.Versions.ForEach(v => Env.Print(v)); AcadVersion.FromApp(Application.AcadApplication).Print(); + 1.Print(); + "1".Print(); } -- Gitee From 55b440c67c26186968f5589df63146abb41f32f7 Mon Sep 17 00:00:00 2001 From: vicwjb Date: Fri, 21 Jan 2022 22:57:18 +0800 Subject: [PATCH 023/675] =?UTF-8?q?test=E6=94=B9=E5=90=8D?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- IFoxCAD.sln | 8 +- src/IFoxCAD.Cad/IFoxCAD.Cad.csproj | 4 +- .../Properties/launchSettings.json | 2 +- tests/{DBTrans.test => Test}/Test.cs | 0 .../DBTrans.test.csproj => Test/Test.csproj} | 0 tests/{DBTrans.test => Test}/TestJig.cs | 0 .../{DBTrans.test => Test}/testConvexHull.cs | 0 tests/{DBTrans.test => Test}/testblock.cs | 113 ++++++++++++------ tests/{DBTrans.test => Test}/testeditor.cs | 0 tests/{DBTrans.test => Test}/testenv.cs | 0 tests/{DBTrans.test => Test}/testid.cs | 0 .../testselectfilter.cs | 0 .../{DBTrans.test => Test}/wpf/TestView.xaml | 0 .../wpf/TestView.xaml.cs | 0 .../wpf/TestViewModel.cs | 0 15 files changed, 82 insertions(+), 45 deletions(-) rename tests/{DBTrans.test => Test}/Properties/launchSettings.json (90%) rename tests/{DBTrans.test => Test}/Test.cs (100%) rename tests/{DBTrans.test/DBTrans.test.csproj => Test/Test.csproj} (100%) rename tests/{DBTrans.test => Test}/TestJig.cs (100%) rename tests/{DBTrans.test => Test}/testConvexHull.cs (100%) rename tests/{DBTrans.test => Test}/testblock.cs (74%) rename tests/{DBTrans.test => Test}/testeditor.cs (100%) rename tests/{DBTrans.test => Test}/testenv.cs (100%) rename tests/{DBTrans.test => Test}/testid.cs (100%) rename tests/{DBTrans.test => Test}/testselectfilter.cs (100%) rename tests/{DBTrans.test => Test}/wpf/TestView.xaml (100%) rename tests/{DBTrans.test => Test}/wpf/TestView.xaml.cs (100%) rename tests/{DBTrans.test => Test}/wpf/TestViewModel.cs (100%) diff --git a/IFoxCAD.sln b/IFoxCAD.sln index 309172c..3f8ddc8 100644 --- a/IFoxCAD.sln +++ b/IFoxCAD.sln @@ -1,15 +1,15 @@  Microsoft Visual Studio Solution File, Format Version 12.00 -# Visual Studio Version 16 -VisualStudioVersion = 16.0.31025.194 +# Visual Studio Version 17 +VisualStudioVersion = 17.1.32113.165 MinimumVisualStudioVersion = 10.0.40219.1 Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "IFoxCAD.Cad", "src\IFoxCAD.Cad\IFoxCAD.Cad.csproj", "{D5FAED7C-A99B-4BEE-A745-45442DC44971}" EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "DBTrans.test", "tests\DBTrans.test\DBTrans.test.csproj", "{B1602568-F023-46C7-B635-3CB281F1EC00}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Test", "tests\DBTrans.test\Test.csproj", "{B1602568-F023-46C7-B635-3CB281F1EC00}" EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "IFoxCAD.WPF", "src\IFoxCAD.WPF\IFoxCAD.WPF.csproj", "{D820D629-1AB3-4BE3-A772-CB753D98CCB8}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "IFoxCAD.Basal", "src\IFoxCAD.Basal\IFoxCAD.Basal.csproj", "{40BF07C4-100A-4810-A27B-4587CFB38E6E}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "IFoxCAD.Basal", "src\IFoxCAD.Basal\IFoxCAD.Basal.csproj", "{40BF07C4-100A-4810-A27B-4587CFB38E6E}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution diff --git a/src/IFoxCAD.Cad/IFoxCAD.Cad.csproj b/src/IFoxCAD.Cad/IFoxCAD.Cad.csproj index f0cd726..dd05c56 100644 --- a/src/IFoxCAD.Cad/IFoxCAD.Cad.csproj +++ b/src/IFoxCAD.Cad/IFoxCAD.Cad.csproj @@ -3,7 +3,7 @@ net45 true - 0.2.0.4 + 0.2.0.5 InspireFunction xsfhlzh;vicwjb 基于.NET的Cad二次开发类库 @@ -12,7 +12,7 @@ https://gitee.com/inspirefunction/ifoxcad.git git IFoxCAD;CAD;AutoCad;C#;NET - 修复版本信息类错误. + 修复符号表类错误. true true preview diff --git a/tests/DBTrans.test/Properties/launchSettings.json b/tests/Test/Properties/launchSettings.json similarity index 90% rename from tests/DBTrans.test/Properties/launchSettings.json rename to tests/Test/Properties/launchSettings.json index 1ecc024..4b464c3 100644 --- a/tests/DBTrans.test/Properties/launchSettings.json +++ b/tests/Test/Properties/launchSettings.json @@ -1,6 +1,6 @@ { "profiles": { - "DBTrans.test": { + "Test": { "commandName": "Executable", "executablePath": "C:\\Program Files\\Autodesk\\AutoCAD 2021\\acad.exe", "commandLineArgs": "/nologo", diff --git a/tests/DBTrans.test/Test.cs b/tests/Test/Test.cs similarity index 100% rename from tests/DBTrans.test/Test.cs rename to tests/Test/Test.cs diff --git a/tests/DBTrans.test/DBTrans.test.csproj b/tests/Test/Test.csproj similarity index 100% rename from tests/DBTrans.test/DBTrans.test.csproj rename to tests/Test/Test.csproj diff --git a/tests/DBTrans.test/TestJig.cs b/tests/Test/TestJig.cs similarity index 100% rename from tests/DBTrans.test/TestJig.cs rename to tests/Test/TestJig.cs diff --git a/tests/DBTrans.test/testConvexHull.cs b/tests/Test/testConvexHull.cs similarity index 100% rename from tests/DBTrans.test/testConvexHull.cs rename to tests/Test/testConvexHull.cs diff --git a/tests/DBTrans.test/testblock.cs b/tests/Test/testblock.cs similarity index 74% rename from tests/DBTrans.test/testblock.cs rename to tests/Test/testblock.cs index 27afb09..b03ad0d 100644 --- a/tests/DBTrans.test/testblock.cs +++ b/tests/Test/testblock.cs @@ -39,6 +39,19 @@ public void BlockDef() ); //ObjectId objectId = tr.BlockTable.Add("a");//新建块 //objectId.GetObject().AddEntity();//测试添加空实体 + tr.BlockTable.Add("test1", + btr => + { + btr.Origin = new Point3d(0, 0, 0); + + }, + () => + { + return new List { new Line(new Point3d(0, 0, 0), new Point3d(1, 1, 0)) , + new DBText{ Position = new Point3d(0,0,0), + TextString = "123" + } }; + }); } //修改块定义 [CommandMethod("blockdefchange")] @@ -46,14 +59,31 @@ public void BlockDefChange() { using var tr = new DBTrans(); //var line = new Line(new Point3d(0, 0, 0), new Point3d(1, 1, 0)); + //tr.BlockTable.Change("test", btr => + //{ + // btr.Origin = new Point3d(5, 5, 0); + // btr.AddEntity(new Circle(new Point3d(0, 0, 0), Vector3d.ZAxis, 2)); + // btr.GetEntities() + // .ToList() + // .ForEach(e => e.Flush()); //刷新块显示 + + //}); + tr.BlockTable.Change("test", btr => { - btr.Origin = new Point3d(5, 5, 0); - btr.AddEntity(new Circle(new Point3d(0, 0, 0), Vector3d.ZAxis, 2)); - btr.GetEntities() - .ToList() - .ForEach(e => e.Flush()); //刷新块显示 - + foreach (var id in btr) + { + var ent = tr.GetObject(id); + using (ent.ForWrite()) + { + if (ent is Dimension dBText) + { + dBText.DimensionText = "234"; + dBText.RecomputeDimensionBlock(true); + } + } + + } }); tr.Editor.Regen(); } @@ -157,36 +187,44 @@ public void TestClipBlock() [CommandMethod("test_block_ej")] public void EJ() { - using var tr = new DBTrans(); - - //Point3d.Origin.AddBellowToModelSpace(100, 100, 5, 3, 30);//画波纹管 - - //Database db2 = new Database(false, true); - //string fullFileName = @".\MyBlockDwgFile\001.dwg"; - //db2.ReadDwgFile(fullFileName, System.IO.FileShare.Read, true, null); - //db2.CloseInput(true); - //string blockName = "test"; - //if (!tr.BlockTable.Has(blockName)) - //{ - // //tr.Database.Insert(blockName, db2, false);//插入块 - // db.Insert(blockName, db2, false); + using (var tr = new DBTrans()) + { - //} + //Point3d.Origin.AddBellowToModelSpace(100, 100, 5, 3, 30);//画波纹管 + + //Database db2 = new Database(false, true); + //string fullFileName = @".\MyBlockDwgFile\001.dwg"; + //db2.ReadDwgFile(fullFileName, System.IO.FileShare.Read, true, null); + //db2.CloseInput(true); + //string blockName = "test"; + //if (!tr.BlockTable.Has(blockName)) + //{ + // //tr.Database.Insert(blockName, db2, false);//插入块 + // db.Insert(blockName, db2, false); + + //} + + string fullFileName = @"C:\Users\vic\Desktop\001.dwg"; + var blockdef = tr.BlockTable.GetBlockFrom(fullFileName, false); + + tr.Database.Clayer = tr.LayerTable["0"];//当前图层切换为0图层 + tr.LayerTable.Change(tr.Database.Clayer, ltr => + { + ltr.Color = Color.FromColorIndex(ColorMethod.ByAci, 2); //ColorMethod.ByAci可以让我们使用AutoCAD ACI颜色索引……这里为2(表示黄色) + }); + + ObjectId id = tr.ModelSpace.InsertBlock(Point3d.Origin, blockdef);//插入块参照 - string fullFileName = @".\MyBlockDwgFile\001.dwg"; - var blockdef = tr.BlockTable.GetBlockFrom(fullFileName, false); - tr.Database.Clayer = tr.LayerTable["0"];//当前图层切换为0图层 - tr.LayerTable.Change(tr.Database.Clayer, ltr => - { - ltr.Color = Color.FromColorIndex(ColorMethod.ByAci, 2); //ColorMethod.ByAci可以让我们使用AutoCAD ACI颜色索引……这里为2(表示黄色) - }); - ObjectId id = tr.ModelSpace.InsertBlock(Point3d.Origin, blockdef);//插入块参照 - var entTest = tr.GetObject(id); - entTest.Draw(); + var entTest = tr.GetObject(id); + entTest.Draw(); + + } + + using var tr2 = new DBTrans(); PromptEntityOptions PEO = new PromptEntityOptions("\n请选择一个块"); PEO.SetRejectMessage("\n对象必须是块"); PEO.AddAllowedClass(typeof(BlockReference), true); @@ -197,19 +235,19 @@ public void EJ() return; } - var Bref = tr.GetObject(PER.ObjectId); + var Bref = tr2.GetObject(PER.ObjectId); //var BTR = tr.GetObject(Bref.BlockTableRecord, OpenMode.ForWrite); ////如果知道块名字BTRName //BlockTableRecord BTR = tr.GetObject(tr.BlockTable[blockName], OpenMode.ForWrite); - var btr = tr.BlockTable[Bref.Name]; + var btr = tr2.BlockTable[Bref.Name]; - tr.BlockTable.Change(btr, ltr => + tr2.BlockTable.Change(btr, ltr => { foreach (ObjectId OID in ltr) { - var Ent = tr.GetObject(OID); + var Ent = tr2.GetObject(OID); using (Ent.ForWrite()) { if (Ent is MText mText) @@ -235,6 +273,7 @@ public void EJ() { case "$$pipeLen": dimension.DimensionText = "350"; + dimension.RecomputeDimensionBlock(true); break; default: break; @@ -244,13 +283,11 @@ public void EJ() } - ltr.GetEntities() - .ToList() - .ForEach(e => e.Flush()); //刷新块显示 + }); - tr.Editor.Regen(); + tr2.Editor.Regen(); } diff --git a/tests/DBTrans.test/testeditor.cs b/tests/Test/testeditor.cs similarity index 100% rename from tests/DBTrans.test/testeditor.cs rename to tests/Test/testeditor.cs diff --git a/tests/DBTrans.test/testenv.cs b/tests/Test/testenv.cs similarity index 100% rename from tests/DBTrans.test/testenv.cs rename to tests/Test/testenv.cs diff --git a/tests/DBTrans.test/testid.cs b/tests/Test/testid.cs similarity index 100% rename from tests/DBTrans.test/testid.cs rename to tests/Test/testid.cs diff --git a/tests/DBTrans.test/testselectfilter.cs b/tests/Test/testselectfilter.cs similarity index 100% rename from tests/DBTrans.test/testselectfilter.cs rename to tests/Test/testselectfilter.cs diff --git a/tests/DBTrans.test/wpf/TestView.xaml b/tests/Test/wpf/TestView.xaml similarity index 100% rename from tests/DBTrans.test/wpf/TestView.xaml rename to tests/Test/wpf/TestView.xaml diff --git a/tests/DBTrans.test/wpf/TestView.xaml.cs b/tests/Test/wpf/TestView.xaml.cs similarity index 100% rename from tests/DBTrans.test/wpf/TestView.xaml.cs rename to tests/Test/wpf/TestView.xaml.cs diff --git a/tests/DBTrans.test/wpf/TestViewModel.cs b/tests/Test/wpf/TestViewModel.cs similarity index 100% rename from tests/DBTrans.test/wpf/TestViewModel.cs rename to tests/Test/wpf/TestViewModel.cs -- Gitee From e618acd4476012d87c403ab664391d9d4759b525 Mon Sep 17 00:00:00 2001 From: vicwjb Date: Fri, 21 Jan 2022 23:00:47 +0800 Subject: [PATCH 024/675] =?UTF-8?q?=E6=94=B9=E5=90=8Dtest?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- IFoxCAD.sln | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/IFoxCAD.sln b/IFoxCAD.sln index 3f8ddc8..6744427 100644 --- a/IFoxCAD.sln +++ b/IFoxCAD.sln @@ -5,7 +5,7 @@ VisualStudioVersion = 17.1.32113.165 MinimumVisualStudioVersion = 10.0.40219.1 Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "IFoxCAD.Cad", "src\IFoxCAD.Cad\IFoxCAD.Cad.csproj", "{D5FAED7C-A99B-4BEE-A745-45442DC44971}" EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Test", "tests\DBTrans.test\Test.csproj", "{B1602568-F023-46C7-B635-3CB281F1EC00}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Test", "tests\Test\Test.csproj", "{B1602568-F023-46C7-B635-3CB281F1EC00}" EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "IFoxCAD.WPF", "src\IFoxCAD.WPF\IFoxCAD.WPF.csproj", "{D820D629-1AB3-4BE3-A772-CB753D98CCB8}" EndProject -- Gitee From a8964512369d68afc8da3d009d27439933f6e5c0 Mon Sep 17 00:00:00 2001 From: vicwjb Date: Sat, 22 Jan 2022 23:07:07 +0800 Subject: [PATCH 025/675] =?UTF-8?q?=E5=A2=9E=E5=8A=A0=E6=89=93=E5=8D=B0?= =?UTF-8?q?=E5=87=BD=E6=95=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/IFoxCAD.Cad/ExtensionMethod/ObjEx.cs | 21 +++++++++++++++++++++ tests/Test/testenv.cs | 20 -------------------- 2 files changed, 21 insertions(+), 20 deletions(-) create mode 100644 src/IFoxCAD.Cad/ExtensionMethod/ObjEx.cs diff --git a/src/IFoxCAD.Cad/ExtensionMethod/ObjEx.cs b/src/IFoxCAD.Cad/ExtensionMethod/ObjEx.cs new file mode 100644 index 0000000..c831959 --- /dev/null +++ b/src/IFoxCAD.Cad/ExtensionMethod/ObjEx.cs @@ -0,0 +1,21 @@ +namespace IFoxCAD.Cad; + +public static class ObjEx +{ + /// + /// cad的打印 + /// + /// + public static void Print(this object obj) + { + Application.DocumentManager.MdiActiveDocument.Editor.WriteMessage($"{obj}\n"); + } + /// + /// 系统的打印 + /// + /// + public static void PrintLine(this object obj) + { + Console.WriteLine(obj.ToString()); + } +} diff --git a/tests/Test/testenv.cs b/tests/Test/testenv.cs index b548e2e..c4531e5 100644 --- a/tests/Test/testenv.cs +++ b/tests/Test/testenv.cs @@ -1,4 +1,3 @@ -using System; using System.Collections.Generic; using System.Linq; using System.Text; @@ -10,25 +9,6 @@ namespace test { - public static class ObjEx - { - /// - /// cad的打印 - /// - /// - public static void Print(this object obj) - { - Env.Print(obj); - } - /// - /// 系统的打印 - /// - /// - public static void PrintLine(this object obj) - { - Console.WriteLine(obj.ToString()); - } - } public class testenv { [CommandMethod("testenum")] -- Gitee From a1683b7af08728f38c572e1e77cb4405464c731d Mon Sep 17 00:00:00 2001 From: vicwjb Date: Mon, 24 Jan 2022 20:37:06 +0800 Subject: [PATCH 026/675] =?UTF-8?q?=E6=9B=B4=E6=96=B0=E5=B8=AE=E5=8A=A9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 115 ++++++++++++----------- tests/Test/GlobalUsings.cs | 24 +++++ tests/Test/Test.cs | 21 +---- tests/Test/testeditor.cs | 92 ++++++++----------- tests/Test/wpf/Class1.cs | 13 +++ tests/Test/wpf/TestView.xaml | 4 +- tests/Test/wpf/TestViewModel.cs | 156 +++++++++++++++----------------- 7 files changed, 215 insertions(+), 210 deletions(-) create mode 100644 tests/Test/GlobalUsings.cs create mode 100644 tests/Test/wpf/Class1.cs diff --git a/README.md b/README.md index c6027fe..5a61fc6 100644 --- a/README.md +++ b/README.md @@ -16,63 +16,68 @@ #### 安装教程 -1. vs新建net standord 类库 -2. 修改项目TargetFramework为net45,保存重加载项目 -3. 右键项目,管理nuget程序包,搜索ifoxcad,安装最新版就可以了 +1. vs新建net standard 类库 +2. 修改项目TargetFramework为net45,保存重加载项目 +3. 右键项目,管理nuget程序包,搜索ifoxcad,安装最新版就可以了 #### 使用说明 -1. 快速入门 - - - 打开vs,新建一个standard类型的类库项目 +1. 快速入门 + + - 打开vs,新建一个standard类型的类库项目![](/Users/vic/Library/Application%20Support/marktext/images/2022-01-24-20-15-21-image.png) + - 双击项目,打开项目文件: - - 修改项目文件里的`netcore2.0`为`NET45`。其中的net45,可以改为NET45以上的标准TFM(如:net45、net46、net47等等)。同时可以指定多版本。具体的详细的教程见 [VS通过添加不同引用库,建立多条件编译]( https://www.yuque.com/vicwjb/zqpcd0/ufbwyl)。 - - 在 ` xxx ` 中增加 `preview`,主要是为了支持最新的语法,本项目采用了最新的语法编写。项目文件现在的内容类似如下: - - - ``` - - - net45 - preview - - - ``` - - - 右键项目文件,选择管理nuget程序包。 - - 在nuget程序里搜索**ifoxcad**,直接选择最新的版本(如果你是 **net40** 或者 **net35** 的用户,可以安装 **0.1.6** 版本),然后点击安装**IFoxCAD.Cad**,nuget会自动安装ifoxcad依赖的库。 - - 添加引用 - - ```c# - using Autodesk.AutoCAD.ApplicationServices; - using Autodesk.AutoCAD.EditorInput; - using Autodesk.AutoCAD.Runtime; - using Autodesk.AutoCAD.Geometry; - using Autodesk.AutoCAD.DatabaseServices; - using IFoxCAD.Cad; + + - 修改项目文件里的`netcore2.0`为`NET45`。其中的net45,可以改为NET45以上的标准TFM(如:net45、net46、net47等等)。同时可以指定多版本。具体的详细的教程见 [VS通过添加不同引用库,建立多条件编译]( https://www.yuque.com/vicwjb/zqpcd0/ufbwyl)。 + + - 在 ` xxx ` 中增加 `preview`,主要是为了支持最新的语法,本项目采用了最新的语法编写。项目文件现在的内容类似如下: + + ```xml + + + net45 + preview + + ``` - + + - 右键项目文件,选择管理nuget程序包。![](/Users/vic/Library/Application%20Support/marktext/images/2022-01-24-20-17-21-image.png) + + - 在nuget程序里搜索**ifoxcad**,直接选择最新的版本(如果你是 **net40** 或者 **net35** 的用户,可以安装 **0.1.6** 版本),然后点击安装**IFoxCAD.Cad**,nuget会自动安装ifoxcad依赖的库。(按下图绿色框框里选择浏览,程序包来源选择nuget.org,安装IFoxCAD.Cad包。IFoxCAD.Basal是IFoxCAD.Cad的依赖项会自动安装,如果要开发wpf界面的话,可以安装IFoxCAD.WPF,提供了简单的mvvm支持)![](/Users/vic/Library/Application%20Support/marktext/images/2022-01-24-20-18-42-image.png) + + - 添加引用 + + ```c# + using Autodesk.AutoCAD.ApplicationServices; + using Autodesk.AutoCAD.EditorInput; + using Autodesk.AutoCAD.Runtime; + using Autodesk.AutoCAD.Geometry; + using Autodesk.AutoCAD.DatabaseServices; + using IFoxCAD.Cad; + ``` + - 添加代码 - - ```c# - [CommandMethod("hello")] - public void Hello() - { - using var tr = new DBTrans(); - var line1 = new Line(new Point3d(0, 0, 0), new Point3d(1, 1, 0)); - tr.CurrentSpace.AddEntity(line1); - // 如果你没有添加preview到项目文件里的话:按如下旧语法: - // using(var tr = new DBTrans()) - // { - // var line1 = new Line(new Point3d(0, 0, 0), new Point3d(1, 1, 0)); - // tr.CurrentSpace.AddEntity(line1); - // } - } - ``` - - 这段代码就是在cad的当前空间内添加了一条直线。 - + + ```c# + [CommandMethod("hello")] + public void Hello() + { + using var tr = new DBTrans(); + var line1 = new Line(new Point3d(0, 0, 0), new Point3d(1, 1, 0)); + tr.CurrentSpace.AddEntity(line1); + // 如果你没有添加preview到项目文件里的话:按如下旧语法: + // using(var tr = new DBTrans()) + // { + // var line1 = new Line(new Point3d(0, 0, 0), new Point3d(1, 1, 0)); + // tr.CurrentSpace.AddEntity(line1); + // } + } + ``` + + 这段代码就是在cad的当前空间内添加了一条直线。 + - F6生成,然后打开cad,netload命令将刚刚生成的dll加载。 + - 运行hello命令,然后缩放一下视图,现在一条直线和一个圆已经显示在屏幕上了。 2. [事务管理器用法](/docs/DBTrans.md) @@ -84,9 +89,9 @@ 5. [WPF支持](/docs/WPF.md) 6. 天秀的自动加载与初始化 - + 为了将程序集的初始化和通过写注册表的方式实现自动加载统一设置,减少每次重复的工作量,内裤提供了`AutoRegAssem`抽象类来完成此功能,只要在需要初始化的类继承`AutoRegAssem`类,然后实现`Initialize()` 和`Terminate()`两个函数就可以了。特别强调的是,一个程序集里只能有一个类继承,不管是不是同一个命名空间。 - + ```c# using Autodesk.AutoCAD.Runtime; using IFoxCAD.Cad; @@ -101,7 +106,7 @@ { throw new NotImplementedException(); } - + public override void Terminate() { throw new NotImplementedException(); @@ -111,9 +116,9 @@ ``` 7. 天秀的打开模式提权 - + 由于cad的对象是有打开模式,是否可写等等,为了安全起见,在处理对象时,一般是用读模式打开,然后需要写数据的时候在提权为写模式,然后在降级到读模式,但是这个过程中,很容易漏掉某些步骤,然后cad崩溃。为了处理这些情况,内裤提供了提权类来保证读写模式的有序转换。 - + ```c# using(line.ForWrite()) //开启对象写模式提权事务 { diff --git a/tests/Test/GlobalUsings.cs b/tests/Test/GlobalUsings.cs new file mode 100644 index 0000000..5323ef0 --- /dev/null +++ b/tests/Test/GlobalUsings.cs @@ -0,0 +1,24 @@ +/// 系统引用 +global using System; +global using System.Collections; +global using System.Collections.Generic; +global using System.IO; +global using System.Linq; +global using System.Text; +global using System.Reflection; +global using System.Text.RegularExpressions; +global using Microsoft.Win32; +global using System.ComponentModel; + + +/// autocad 引用 +global using Autodesk.AutoCAD.ApplicationServices; +global using Autodesk.AutoCAD.EditorInput; +global using Autodesk.AutoCAD.Colors; +global using Autodesk.AutoCAD.DatabaseServices; +global using Autodesk.AutoCAD.Geometry; +global using Autodesk.AutoCAD.Runtime; + +/// ifoxcad +global using IFoxCAD.Cad; +global using IFoxCAD.WPF; diff --git a/tests/Test/Test.cs b/tests/Test/Test.cs index 1e40df7..b7da9eb 100644 --- a/tests/Test/Test.cs +++ b/tests/Test/Test.cs @@ -1,17 +1,4 @@ -using Autodesk.AutoCAD.ApplicationServices; -using Autodesk.AutoCAD.Colors; -using Autodesk.AutoCAD.DatabaseServices; -using Autodesk.AutoCAD.EditorInput; -using Autodesk.AutoCAD.Geometry; -using Autodesk.AutoCAD.Runtime; - -using IFoxCAD.Cad; - -using System; -using System.Collections.Generic; -using System.IO; -using System.Linq; - + using test.wpf; namespace test; @@ -291,13 +278,7 @@ public void PrintLayerName() } - [CommandMethod("testwpf")] - public void TestWPf() - { - var test = new TestView(); - Application.ShowModalWindow(test); - } [CommandMethod("testpt")] public void TestPt() diff --git a/tests/Test/testeditor.cs b/tests/Test/testeditor.cs index 7740285..9913e50 100644 --- a/tests/Test/testeditor.cs +++ b/tests/Test/testeditor.cs @@ -1,62 +1,50 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; -using Autodesk.AutoCAD.ApplicationServices; -using Autodesk.AutoCAD.DatabaseServices; -using Autodesk.AutoCAD.EditorInput; -using Autodesk.AutoCAD.Geometry; -using Autodesk.AutoCAD.Runtime; -using IFoxCAD.Cad; -namespace test +namespace test; + +public class testeditor { - public class testeditor + [CommandMethod("tested")] + public void tested() { - [CommandMethod("tested")] - public void tested() + var pts = new List { - var pts = new List - { - new Point2d(0,0), - new Point2d(0,1), - new Point2d(1,1), - new Point2d(1,0) - }; - var res = EditorEx.GetLines(pts, false); - var res1 = EditorEx.GetLines(pts, true); - var res2 = pts.Select(pt => new TypedValue((int)LispDataType.Point2d, pt)).ToList(); + new Point2d(0,0), + new Point2d(0,1), + new Point2d(1,1), + new Point2d(1,0) + }; + var res = EditorEx.GetLines(pts, false); + var res1 = EditorEx.GetLines(pts, true); + var res2 = pts.Select(pt => new TypedValue((int)LispDataType.Point2d, pt)).ToList(); - Editor ed = Application.DocumentManager.MdiActiveDocument.Editor; - var pt = ed.GetPoint("qudiam", new Point3d(0, 0, 0)); - var d = ed.GetDouble("qudoule"); - var i = ed.GetInteger("quint"); - var s = ed.GetString("qustr"); - Env.Editor.WriteMessage(""); - } - [CommandMethod("testzoom")] - public void testzoom() + Editor ed = Application.DocumentManager.MdiActiveDocument.Editor; + var pt = ed.GetPoint("qudiam", new Point3d(0, 0, 0)); + var d = ed.GetDouble("qudoule"); + var i = ed.GetInteger("quint"); + var s = ed.GetString("qustr"); + Env.Editor.WriteMessage(""); + } + [CommandMethod("testzoom")] + public void testzoom() + { + using var tr = new DBTrans(); + var res = Env.Editor.GetEntity("\npick ent:"); + if (res.Status == Autodesk.AutoCAD.EditorInput.PromptStatus.OK) { - using var tr = new DBTrans(); - var res = Env.Editor.GetEntity("\npick ent:"); - if (res.Status == Autodesk.AutoCAD.EditorInput.PromptStatus.OK) - { - Env.Editor.ZoomObject(res.ObjectId.GetObject()); - } + Env.Editor.ZoomObject(res.ObjectId.GetObject()); + } - } - [CommandMethod("testzoomextent")] - public void testzoomextent() - { - //using var tr = new DBTrans(); - //var res = Env.Editor.GetEntity("\npick ent:"); - //if (res.Status == Autodesk.AutoCAD.EditorInput.PromptStatus.OK) - //{ - // Env.Editor.ZoomObject(res.ObjectId.GetObject()); - //} + } + [CommandMethod("testzoomextent")] + public void testzoomextent() + { + //using var tr = new DBTrans(); + //var res = Env.Editor.GetEntity("\npick ent:"); + //if (res.Status == Autodesk.AutoCAD.EditorInput.PromptStatus.OK) + //{ + // Env.Editor.ZoomObject(res.ObjectId.GetObject()); + //} - Env.Editor.ZoomExtents(); - } + Env.Editor.ZoomExtents(); } } diff --git a/tests/Test/wpf/Class1.cs b/tests/Test/wpf/Class1.cs new file mode 100644 index 0000000..4358668 --- /dev/null +++ b/tests/Test/wpf/Class1.cs @@ -0,0 +1,13 @@ +namespace Test.wpf +{ + public class Class1 + { + [CommandMethod("testwpf")] + public void TestWPf() + { + + var test = new TestView(); + Application.ShowModalWindow(test); + } + } +} diff --git a/tests/Test/wpf/TestView.xaml b/tests/Test/wpf/TestView.xaml index 42cb304..bd75779 100644 --- a/tests/Test/wpf/TestView.xaml +++ b/tests/Test/wpf/TestView.xaml @@ -1,9 +1,9 @@ - Name = "hello " + Name, - can => !string.IsNullOrEmpty(Name)); - } - return clickCommand; + clickCommand = new( + execute => Name = "hello " + Name, + can => !string.IsNullOrEmpty(Name)); } + return clickCommand; } + } - private bool receiveMouseMove; + private bool receiveMouseMove; - public bool ReceiveMouseMove - { - get { return receiveMouseMove; } - set { Set(ref receiveMouseMove, value); } - } + public bool ReceiveMouseMove + { + get { return receiveMouseMove; } + set { Set(ref receiveMouseMove, value); } + } - private string tipText; + private string tipText; - public string TipText - { - get { return tipText; } - set { Set(ref tipText, value); } - } + public string TipText + { + get { return tipText; } + set { Set(ref tipText, value); } + } - private RelayCommand loadedCommand; - public RelayCommand LoadCommand + private RelayCommand loadedCommand; + public RelayCommand LoadCommand + { + get { - get + if (loadedCommand is null) { - if (loadedCommand is null) - { - loadedCommand = new( - execute => MessageBox.Show("程序加载完毕")); - } - return loadedCommand; + loadedCommand = new( + execute => MessageBox.Show("程序加载完毕")); } + return loadedCommand; } + } - private RelayCommand mouseMoveCommand; + private RelayCommand mouseMoveCommand; - public RelayCommand MouseMoveCommand + public RelayCommand MouseMoveCommand + { + get { - get + if (mouseMoveCommand is null) { - if (mouseMoveCommand is null) - { - mouseMoveCommand = new( - execute => + mouseMoveCommand = new( + execute => + { + var pt = execute.GetPosition(execute.Device.Target); + var left = "左键放开"; + var mid = "中键放开"; + var right = "右键放开"; + + if (execute.LeftButton == MouseButtonState.Pressed) + { + left = "左键放下"; + } + if (execute.MiddleButton == MouseButtonState.Pressed) + { + mid = "中键放下"; + } + if (execute.RightButton == MouseButtonState.Pressed) { - var pt = execute.GetPosition(execute.Device.Target); - var left = "左键放开"; - var mid = "中键放开"; - var right = "右键放开"; - - if (execute.LeftButton == MouseButtonState.Pressed) - { - left = "左键放下"; - } - if (execute.MiddleButton == MouseButtonState.Pressed) - { - mid = "中键放下"; - } - if (execute.RightButton == MouseButtonState.Pressed) - { - right = "右键放下"; - } - TipText = $"当前鼠标位置:X={pt.X},Y={pt.Y}。当前鼠标状态:{left}、{mid}、{right}"; - }, - can => ReceiveMouseMove); - } - return mouseMoveCommand; + right = "右键放下"; + } + TipText = $"当前鼠标位置:X={pt.X},Y={pt.Y}。当前鼠标状态:{left}、{mid}、{right}"; + }, + can => ReceiveMouseMove); } + return mouseMoveCommand; } + } - public TestViewModel() - { - Name = "world"; - } + public TestViewModel() + { + Name = "world"; + } - } } -- Gitee From 91d1ad1f8aae753a27efff5aa7afc865f71ba33a Mon Sep 17 00:00:00 2001 From: vicwjb Date: Mon, 24 Jan 2022 20:44:51 +0800 Subject: [PATCH 027/675] =?UTF-8?q?=E6=9B=B4=E6=96=B0readme?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 18 +++++++++--------- ...4\344\272\214\347\273\264\347\240\201.png" | Bin docs/png/nuget.png | Bin 0 -> 159167 bytes docs/png/nuget1.png | Bin 0 -> 27373 bytes docs/png/standard.png | Bin 0 -> 148673 bytes 5 files changed, 9 insertions(+), 9 deletions(-) rename "docs/ifoxcad\347\224\250\346\210\267\344\272\244\346\265\201\347\276\244\347\276\244\344\272\214\347\273\264\347\240\201.png" => "docs/png/ifoxcad\347\224\250\346\210\267\344\272\244\346\265\201\347\276\244\347\276\244\344\272\214\347\273\264\347\240\201.png" (100%) create mode 100644 docs/png/nuget.png create mode 100644 docs/png/nuget1.png create mode 100644 docs/png/standard.png diff --git a/README.md b/README.md index 5a61fc6..a92646b 100644 --- a/README.md +++ b/README.md @@ -6,7 +6,7 @@ 可以加群交流: -![ifoxcad用户交流群群二维码](./docs/ifoxcad用户交流群群二维码.png) +![ifoxcad用户交流群群二维码](./docs/png/ifoxcad用户交流群群二维码.png) #### 软件架构及相关说明 @@ -24,14 +24,14 @@ 1. 快速入门 - - 打开vs,新建一个standard类型的类库项目![](/Users/vic/Library/Application%20Support/marktext/images/2022-01-24-20-15-21-image.png) + - 打开vs,新建一个standard类型的类库项目![](./docs/png/standard.png) - 双击项目,打开项目文件: - 修改项目文件里的`netcore2.0`为`NET45`。其中的net45,可以改为NET45以上的标准TFM(如:net45、net46、net47等等)。同时可以指定多版本。具体的详细的教程见 [VS通过添加不同引用库,建立多条件编译]( https://www.yuque.com/vicwjb/zqpcd0/ufbwyl)。 - 在 ` xxx ` 中增加 `preview`,主要是为了支持最新的语法,本项目采用了最新的语法编写。项目文件现在的内容类似如下: - + ```xml @@ -40,11 +40,11 @@ ``` - - - 右键项目文件,选择管理nuget程序包。![](/Users/vic/Library/Application%20Support/marktext/images/2022-01-24-20-17-21-image.png) - - - 在nuget程序里搜索**ifoxcad**,直接选择最新的版本(如果你是 **net40** 或者 **net35** 的用户,可以安装 **0.1.6** 版本),然后点击安装**IFoxCAD.Cad**,nuget会自动安装ifoxcad依赖的库。(按下图绿色框框里选择浏览,程序包来源选择nuget.org,安装IFoxCAD.Cad包。IFoxCAD.Basal是IFoxCAD.Cad的依赖项会自动安装,如果要开发wpf界面的话,可以安装IFoxCAD.WPF,提供了简单的mvvm支持)![](/Users/vic/Library/Application%20Support/marktext/images/2022-01-24-20-18-42-image.png) - + + - 右键项目文件,选择管理nuget程序包。![](./docs/png/nuget1.png) + + - 在nuget程序里搜索**ifoxcad**,直接选择最新的版本(如果你是 **net40** 或者 **net35** 的用户,可以安装 **0.1.6** 版本),然后点击安装**IFoxCAD.Cad**,nuget会自动安装ifoxcad依赖的库。(按下图绿色框框里选择浏览,程序包来源选择nuget.org,安装IFoxCAD.Cad包。IFoxCAD.Basal是IFoxCAD.Cad的依赖项会自动安装,如果要开发wpf界面的话,可以安装IFoxCAD.WPF,提供了简单的mvvm支持)![](./docs/png/nuget.png) + - 添加引用 ```c# @@ -77,7 +77,7 @@ 这段代码就是在cad的当前空间内添加了一条直线。 - F6生成,然后打开cad,netload命令将刚刚生成的dll加载。 - + - 运行hello命令,然后缩放一下视图,现在一条直线和一个圆已经显示在屏幕上了。 2. [事务管理器用法](/docs/DBTrans.md) diff --git "a/docs/ifoxcad\347\224\250\346\210\267\344\272\244\346\265\201\347\276\244\347\276\244\344\272\214\347\273\264\347\240\201.png" "b/docs/png/ifoxcad\347\224\250\346\210\267\344\272\244\346\265\201\347\276\244\347\276\244\344\272\214\347\273\264\347\240\201.png" similarity index 100% rename from "docs/ifoxcad\347\224\250\346\210\267\344\272\244\346\265\201\347\276\244\347\276\244\344\272\214\347\273\264\347\240\201.png" rename to "docs/png/ifoxcad\347\224\250\346\210\267\344\272\244\346\265\201\347\276\244\347\276\244\344\272\214\347\273\264\347\240\201.png" diff --git a/docs/png/nuget.png b/docs/png/nuget.png new file mode 100644 index 0000000000000000000000000000000000000000..d0af980fedd9eca39d0e0bf787111685242994ac GIT binary patch literal 159167 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Ð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 literal 0 HcmV?d00001 diff --git a/docs/png/nuget1.png b/docs/png/nuget1.png new file mode 100644 index 0000000000000000000000000000000000000000..91983ffef3b246ea796296b14fe09963663de184 GIT binary patch literal 27373 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 literal 0 HcmV?d00001 diff --git a/docs/png/standard.png b/docs/png/standard.png new file mode 100644 index 0000000000000000000000000000000000000000..dfc34fecb32120f54436115208a4abaea2dac516 GIT binary patch literal 148673 zcmeFZbyQp1*Dj1pDNss*7H=t1C{nzHpvApd(NHLsAjREVT8bBUw*+?&l;SSIwRmus zAeWwVe(!s~aqsE<@B8azjGev5W)b#U>zQ*t^O-XVQdO2AyhnKt4GoP@PWH`PG&Ecd zG&HPTJe<2b?#d(*XlVB>EhQyYEPRqbufEUiq@&}4&Rv~e}myD3xkzDGn1VUR>T zbSHh|9{Errn4VsJE?gbe0HOexzGigV&$^|n(7H(07MRqZ?W-O!l28U01$G>DzR0X|3_G zfLos>JWm2_-M;LYgSV7Zy8O7!z%RXUH9Te9odlc$EP z5ubk+I94mgxN5nn}#AQ1Q&$;44n!KlPRJt z7AH4AksiELycGzKX2|d*$fn1{^UeD9Br%d#@LAxXpeovpuPoY!wj+NW8EksG!?Z6t ztD_{@#9?d116>#@PY2LR=&5dEi3c7MdnjP3&@)_qapABMer(Hc_3glCkhl!=g#5!Z z%@EHn26YG7sK*lbnF2{AgPd!h?uMg&MeaYrOCs3)>_NPGpqaKGJ*4dVrdD}_em0d! z_xUwP&p|roG_fY)+0Kma;`mT5fc6tgd~1k%poP$nVHfARbRTVFil*F3bM_mQ3tn+u zbHCY*iWOMS^Mo;+9{0O7Jgwk;$&!ebl-icUz58{AKpYvs?CoAgKFlCW(1AR|S)Sv$Or@$c+%%6|SB&Z)y{3JM#k%*9ePA(_?HuCqcPV+5udBo@L#*!^M-!x)th z4E$m<$Tqwy$$_5@B}%erHWVr;Fyn?PH5mK&C)%hPSTIGslkJ7amz{jO|IVOPqshtE}QJr8b zsne%2Q5P#ZD={kuD>#Ry_RMu@nM!3D=W4%RBt(CX=9cGMy}wGkT2x%*~mP{*yme=L_@~F`opd{a^saFA1sv-FNa`PU)iHj?cFQBck!Km2kS=qX1RY2(k9N#4-H#HBQjBt9VpgnI+|0eneKzWp z%d7%3Uml!v)jr_~0+9$R7te;t|(sPd4Lgmy)G%#v9p;yVz zQpjH;UN=!%zZNn*Mh>q_``9_V3^>oPdb)%R#M_yWhgv>9>S&+;y#99AzCUJ-5-GG$ zHGfi-6p++n4Vz(isYkhW<96qU2HgyO*7uvQ zj32q*Rm+^;;5+9+#c}U(82viEGucaFIL3!-7KUn zG%Zx(JtntHJ3dOdzBxzkCsCUag{yaOK3RWlvs=4{2Y#dfGxw`Z0L_SAadpt316{NlTkl<9GI*t_#$; z_wXEl94|Eol~W?;le!dF#-F9|nEoZ1bMR!QQszX$Qo;=Pg1MCWPB&$ja!+x$TCZIH zYu0fR^Pr@>B*nDXO$NW{Uwn65xm+n8+=-Nyb?JRD^+?Anl|y)}rptO&w2N*HwH~*A z)-RiE=bC$}c+eNCb6OGG@2>SgcYoB%li${Pl)BDhz^ycPf5jbxG@t}{L_tAM=16y zcI|2$o1%HM#-a~1ojaL3HO`-Q@{3A~S?1(s6W~Xi#Xk+!y$>%7>}!^Gzt5$eeLZvB z4xO9P-_+5m8?;#HVXc%mL{#}4-c)0@;v!)*Fqbhduso4}z+nKVxAOHxN5)r4r7ulH z+PB6hy=x4|4DD62JB8D!LhlV^yzISD!*()Bl%7;!SIFb7R1>q#p71Rikek1K=Kjx? zZvUvJ(C;0)>QC}$1(YE59`3(3A^oI@dV_D0U;FSMZ*MvjIxr8SQbe6w*MAHL8&Yg> zxamW!?QCBkCZTL)bb-1MP7lLk+anu@1>KDIFZq@jb?tzX)TWL)=S}yqlT#{u0h_sG z&x&R8+wr5BsT$BiH+ z=5ppVA%e7#)L0Q7FRhE&)r+#?q2g+aC_~5Q=*I0mj!Q{Ycz=l%==#unF^wHa>LCPD z01-Fp+>9^oq{3%!z{t=|uYHbngIm-uk$~nVx<-*?RQ&OQ&TWWFwu))mj);I~(7C-W zy}6Otj~IL0aE!-R7Vbi5wx?)f3W4UhuC`f)l^du7n6E`0>i3U(rYU@+kfc_JkXeK zuAa_(c`rY|XauTBZW{Csmif|uyNd`z`e7hyo+k=WS7B=Q9bacnU=Md~pl&_YW8kc# z`Ke0kx~p!DX#Yb_tOsIf=zn@?A&=*QBo4ms8ro7=jV!d2=L>qSuMfmG;ljeeqxVJo z!;8i%bY^#;V}t(m#=nQ+6GUVray|KfzNr`# z?}4gP40FE_$^T=f^zYFzp!iXC=zo~fUsK5bhK7#Na2qKk{o~5rt?NT_`t<*K$bW~) z|E%QyVDkU!eE!F9{_HIO$8i3~aQ=TY96uyJs1^Ig3nLvGgze8MFm>gnL66rGjo0P@ zR+UyZ@#cw##Xh0-MhGU)Ns>7nDywe7@$WwWH?U*G(&e!=JZ3LF<{U4B-fxQ*CKg4i zaNLK88WL`vR3LJ_@tj%>i$HK0Y%;;ePqrNb-9h$cLfJXkiB0oPLSPK2U+g!Bi23U9 z8}oA&1kAv|R6`}|Y=i6is>trLH})IVRgs{3AJ!k^a|;Px(BbR;n7ui$h=im&uNDo! zb6Z-2I@mkc0B|rF+Uu=|(T0 z$ekbsB^n2rzP`)SKsoF_TSB~^uvgy(70Rrm076D@OcObZ4l z^{Sk&c-(JQFR(Rx6IgO=_6`b%#ssvGm1$1n-Nhl(Az^XI;^2khwmou$e?^&KW#IX?!WWA{5}lLXX(1jzh)ez zI(603B>S1>oJQMBoPL;)o)F77OFKd3nGYA-)aZ`g#4xALv0`IloxdyZxiksEkIcQ) z26AG(Ow;wM5D9rzs#?7-`K*UD2$g$O&7_BYzR%kd=O1lXb$S(Rq1c=TOO@|&rqZOp zTnGpNZMHKT4kx;%EveXy1YFO=?jS_;u#|J9?grr?a?6eQND7!D9o;n0Qkr z2R*i=MNr+yw)qGcGB2F@nmy16eWQ^xEu3nQcyri^Jl%4LUhUC8<=uro#mw6bcI)nc zTU9vSgPg2n!J9gNwE7__;)4>R1~b=bbkxbX4M(U<+9@SMO%h>{P@%R&RgJ(-$>G$J z-cjRcRe0Tz0o+KoeU)XrlqF>f`B4uZO0}WJPuF8$^&+UQbw15FJ7U zWM2yGBB9gg7-tW1mFWaLt$wNXBmUnpLGLS8j>T#%hQPwHg%<}?*7ej;J}NmY)oKz^ zEAiG(7w}0d!G~Xz34OxWtQbSBeTgo%OH7tkJ?V3BY&UK7c$IifsGZ;oE>Fc_>ur&KS>qw$U!nJ?!l^~_8Rqw@Tu|sVvo%Vc z{z5ex;ms7xjP$g;h1acxM;7<6HO_1evTTVzvqrD4Q$i=oKU11U-^9`MIFQX!{i|d0 zgEFbUz=X!e6wjL!YlgcoO@+E`%_HdMyW+MNwOi}sj^U(@?ymj@seHWox>;rRHg0cR zau#5n6x7Aw!I%>bRlh8^FUMYfIayzvA?vLeUT{cLwU>+P!b`mz8f=d$?`vAJPdGM` zqHzb)!gcCSz5Cx6RyxmwP2zO;QJv&Xjq?GQ+qMiR`wdIC{p2U|i!As|0Bzy?1Jn4Y zDJ@`6gOu$9=_f&kaz6jp`17}$VNvjtf1+Asx64jRUN*L;+wZd$lYZEc%I>h84t<8J z2@_C_OIs*uc%xeN65jRpz15zdweD6nz#Fayvb`NJJSFjJIOMS|t)M43wHmtw+C|4J zOQe?yH?hM~rUkyKh70-xYc-n;zLYx{JJe@5lm7P{)^F@#z9YIO?Ban^?NVRqwbXTv zHyxt4;Rgf%0hZ?N*(W*o^PM8H<;}f?V%uiJygB@SCMBmtGnh`wqJ4(h!Vujd5tj6! ztCq!xwE&h#r-zY_i6X)mz<57Bht_`uDrL3Rf^0y z%P;vLkGydqy+Tc7^NIw2iVCVr*sPjji1KpBYj%pjm_5E8T+uYxmwO7=(r?4bG=R&~ zfMYoHvAE{owD!s5Ct29n`SBk_@8%act~}>2FA|S^AYP0SZG&2wu`1@92dG=iJr6zF z5=kh1hO@^(Qss7lOC__!!+gEmiz$k&J|?MFTZi3-6NSo;nra{C<`#S!6>jlu6$@7z z$-xhA0EE+JJEBWDA*56Z^MACvIy{X;U#QQ*7+|bKGn6Yot05LLoqcAEV`Yr1xi|hP zG2HS;<2Opw$T_MxZaJ}C&!JrZ+uPOPU^7 z!l^C4xyR>yrkPdHgKh#O&2<1`C(eDPJiy)Qm*Xn!H!fxDSI4f-n{C==n{68k2Psd+ z=dm;b0IPd_h)N1=EP+68)>R=uH6&Xo1N3DcI9>UJ2&uj&ppi8AhsmhoV?j0jUti`< z9!@W8bjYG|Y3GVeIYqMCu}S=mNXX6op`_looJFs<2?0r3*B?^2yNIAfUH1UmYE)N@ z5e@ljCyQ2ZN_NVxE`r>F%5mT7n}4mvxXt&wA9hF9Zy(cbervGREOCU@?y3#yXfe;Z zD+yEqRy!$CouU>a8{S~OA7z>I&#{%`LGE3aBpLI2OYE1%Zuihi-~7SS^1oxmJWEe> zo6qlh5w%1@ofM5epL;>`h-p#}|Gr;o-@5Jiy6<*VP)^rp~A|g1( zP{mqrf^GFCtBOHCEq9t`hOM~N2D`t;eHOVT*JKzhYQxE@)>jSj>WN*dWGM^nUCu5ku!Q zZh_o##BdrP-{M*n<5qG-+BTUr_%mTbjdwF`LgVwv)xL~+h$96b9zJEDL9&sgu5L9) zvuz`X^1e`>#}+~7Rx3e@O6wkE{_%TU>42HpH0M5VjX^$-Hw*@*>7yfH*iJiq=&KZb zl)!E~53EDz)X#aDZ!P^%Y>p!O#x*SUh710t)%{GE?dae_AaoSF|KU4vGZWL#o}q3{ zZao$*QMB<9ErOBtHhS3ygB&4_f&71fomjdeHmj8wU(*g>A{VqoSEkGAnAR{4E=qoT zH+g(=PhQQl?C@{#*hyMVegn0Klm>_6q?hK{tU9{Ovq`*_kh#)Ac~HcK-_*3e-PqcR-&w9@%+7o>2i)bFBnGGF49)RiL{Wx2YXhsb(fCZOV$B z!?vGg3jTxZ@fG__lK=4uG+{k-Y;VW+eH7(Vg57-9m*W$?DBjI1|7=W~)RXkxs@uJr z-U=^qeo{q)gE)`g6+_09#+cNsG8Ygfxy7kF@kT}5cd8E@o7CZFPN}n^8{u>!nRZQ- z*xb(7r~)FyPE*`QS^E;tVa(V7P8)N9k~ndeykj-85mG@oGaaBTqo5zB#D2)Uyi5ka_5ff4LXkYEo{|En}G)I z7u8_agmVh&zo&(pnp&QplQ_H0EXu6S?D4qd()uu8dZ(q%7Sx-%ZpIq_4yqyE$i<$G zWA};8{`Y5=($tKBMHy#co7-yfsQtYtBtDq!@v+qG+dm~ch z>F_O8|HIi=F2(G(aMQ?(?Y{7{Z!d;2ZV3`c;l=}@I#2&ys1|D{!3!IR8Et$CtLCV6 zN|rvhdZ^`(Q0(67pD14CwD{4-?(F+g;?qmHqtl(<*B28)lSNt0ee5$HjNGQZ^4fc^ zTsFS(Z=XHs?q9x;A8f3Y$O}V4qnv$;!uZHp@^;u$=P-Vv6J+| z`fIprb__40Z4$~e72~+JZ`S$JqdjV3H?f{j|0_8}$9gNDK6}Rw5o!X$!t$avF(fQg zNNHVj?- z1pJA=_=?@bi}n!H)GaAmaBK7b9M&{NT%k6=o_I0BuB@KWDjZB7_H~5QyQc! za-AG`d189wWiU69+vlCeETFmNY>jBq1W)cfaMrIee5YO*SSh45)_Oy_wOUloq(w2n z@b6oAXU7_^{OpPI6GMi4loRucRfs2an8|ihkZeauD8K5;22}&9M^)OdZjIwiazN<~ z(vE6b5kS+ay^44I%FiCewcHdzj(0L@>uK|p`7}yQ3HW+s`)`{7%3ZTK|GFD~pRjv? z>0q<%7w0v!w=yn34=3COTBw;yuhQ*JKnGA;y$0y;{zowD1@?tdIebN%`AEJOoDcB^ z2(olpb#U%@r|7&4oXtS|5?Q}7ZzDe#X`oUi*O9-5|US3b-R3+@o%86nCKtUVz4*QZ9u z>d{P>z|sg6yP+Di^qxTRmMZyaH66w?_06y<7=-Z*oK4$8>$Scl{@J<#>I}Wn_$!j5+_`{PTZ%>VK|c#M6IZ zX#i6V+&2e#s$lcn*Qg4A26!>gSed%#j92psq* zD^8=rkf6%uaig5sd@Gm0$M*hbG|sn2&SF&?&q`dIogdUS*qGt5v|mTCPA_e4p`Q{HZ%ySzT%Zf_$OmT`4ea;5BMjx zMrz7SHUq+ei-KZ!W1j9#a>1}l`=oh=B+xXF$!m88R<&C9_4(XPk5#66qA8Q0t|a0f zD#ni&u{&uHgP`1Nh|@NKw|x}t_5!~%{4sE3q}=JiYuU*z#Cv!2KT^p@0sG`q zG4Ix;PbEtjf25L)*l+pMr?Zt^#ZKaD(i_ucmadz?)8#2NicUKR(}H?llyZ=7Q^ZvY zTY)`zQxwjfr-T0Ej#?$bPEsBdwc_TasEF9{cjku91p|uP<>~YDRWi#2`8Woe19X(8 z`DU+GGPvh&_FVw1%asW{f7q++S}}PXjXQ}UF^$^r@T@Bmlk8V->+*#zo$Yi;ljsyvL(FIs%0$<)O>h zn88{!f7EO!)YK#juw1T)P8YEgNX(q;lVhMx3ps<|KdHEn4IUSXR9xmhHnsRacPKp@ zgAY75bixn_S@ca~WiQel;tHh_(8^O6x*TXa+V!|xPP-{=`!WS)#S0?-%hcs4Wb1sP zrly;hkioZ65hJxD9M-qIo6U@LA1~e(%}uo5^uscr3Ksz&u31x;knD2!9H2nTVtOTv zT{qDdv?s4RS8bZ2MZ-qPbCadWXF2hXxnZ_(9&`HUgox&wPsD!e%r7>~tpq1{VBbA> zV=eiAsgF{OVVI~4>HG2&Jc$iH4VQjmF0Zt+w_hyNet)@Bkv4+P zx%{1(aq!GjZLHy(C`0&{oAodfA-JY%9cbrLnqYh~g#m=5Ts^0D7iK$Nnn3LsrNg#M z^6G>P%xtrT*o8Bv%M^l3=@*)nZigvd=!a4-QG+7cX9}fQy_A_>%#TOA#V2;hJK-Lx zJQBMkV>$P{Es{xYPAXj1J5S}=x+qlYlG3tum);0@rvLij8wVgE{ktSa-;SkAn3OMv znLV-FC^;%XehZA+1YGp2>2hDN0;}`Sn-92jwK(T^T(aF+wAFD`#(q5X+yhrpLi5r# zIQtu9vdpaRH556BK&CVZ-Hq)Q8xbg?eyUN)Bv2FEFDK>B7V4Vj41o0DSjWS@;U~c^ z|3OYVH$i;gm}@}6fRNIQ#f4D1JkYFXFcJ0nfaMy^U$FPfzIP{uL!(P@XfE?TloN|< z%ey*~Zkvd17umu2S_aIMex=Cb`wHvXFU}e^N3~C+uu-k;BQDxoGd4hssmk1K+g_d$ zjdl3-3yb1dgsPYBjGxv-?{-?f8>6@gh1>`cAUXL7Z|p=;p8wmNO&mh)3(+x)Y@w%9 zBZhk-I}7j1Vx3p^#cAxWRo+|-4s2&6W`rQ6o*-65na%!Ymn{b*8xHF)=jJOCIlo>W z9@Lq})CH4PI*~hC9W^dfMmzbp0$~?vAa4J~0bN&_zq2I2F;Xs5Z%H2Y8mpA*VPlxY*YlRY{y)pnS)q~Sd@C`xh24;i5EOTM z_Msd5?9iwD)H_qaY`0Q0`bJ=pAr$nhnsa^>N^=(%6Cj@g^9pX;`HtHgu%$vnFE^tL zGsBy+uGn=i!n`zEV;^z%zV+I^>P0!KH}Sb|-kNP?0Xf34WbW!sRaEwM(@i&tH#!ff z3K88-61yMYp=v(y<{Wk&B^JD&yd<_|F;c6yT)~TZ-dM<-yHMXUgPP=gc2SoRMYm$-9Irv8H7aJd^Y8y^49ze+1 zNtI>l^5%4Q4N^conh>3c3QUQbKxoql_fz#VihKKR^6lGI_>TKBXvyQK|1p_wXf}n z{>B;f`B1eIWT82Km(Q!zD1%AGP1ns$6f055*Qxt^4yDMpt^wc}DdutKbMql_6(*SPZaLLAjp7ulGB)8 zv~=9>S{Qs0%vWC6k{{|bB3>lHZF?k8IHu=EtGU-A4;Q;0w93fMyM33%Bw8eTwQF9V zZtebqpo-fAa=aXMvgmtLZ+4{#Ua}A6jW=D*Pk`O4fDfYYKz}aeneqeGf@!|3kCHu+T}L(+F;W2`Rbdf}bVl6}X~z z4%!Ox8|#>oCiCgEF!??%u(Deu@~X%6pF}`URF%_O5+2gTw_LC}Fo9iRnLhAni}c47 z31a(m4rZ3A26{qoeVcTH(Gz)H3NIgdmo)4LWw_gSztXG-SadlH1J+o0n|dug?p@>k z+p}uQr)!ru#KdYHFM~-12?=wB2u|I@EvE@aPo~yEf=bP2#Rh{!mwmW@JB{y*s+&9- zgg?I-;BLdIo?F>}*pqL1wJFzcsC~HI@yRxE5gTB}F^ZgLcT9wRg65gJTwnc)J1I@c z0qdkV_HXV!(&~;*zh0}jV9hsT3?bEbFPozRd88_H7B(hn0VN4lUV{YYCd6qvuBCmZ z9|N|A(qh~jc5k|0K(*UI#(Y`*pDEquWG+vRi-z|M81zn5?!9iZ4R0p0gm%#>p`FYc z0v%5iiV*ddl8Anm%QWOP)Lp0(nY~|SU=-*L(MY)x4=IxP&UjLu*~cFQ_9_{ z>z9~%c8JzshYls*2d#9KY)s~5?i-+ni4W)Ifz`n9s)RPIM>?BL{fR-yg zQ@sNk1-J3ESNhTY)SHkVtT!MUGS4jRa23t4DbU0~$ZiGKFH%B#q>^HB{rx5DWP9KLpma?mj@78#(7m3!N46`x!y!jBauC1IszNx*vIUK(yYAno+jz6 zsbFD-3$?gpl)TwtYY!*co7!H&^wP;o0;Rw{w|g#tG13D(CI>q?P5@`!JCs*%cuY?o zTbRdje2YJ!+`H~=1f{h$ANID#Se?F1%ob<5o~rT=H!m|xHJ{O+lFZse7JXKELqsie z!nTZxt)mk7JGQ6$M)BeLYD(&|yjLtGacMA{*WrYBw;*lqJXHidug3f(S{rzdc-BOz@k7vn z4>IG?6L`$C9KCjD$&yvebNT#XRBlHQmekCq(2^#7^KnAjp{==$aF83rZhA^*h34qZ zd8&}|MjlaBu>L4iK(i#t7U9z=LRxm;F;2bW2ld&m4ux=n{nZEVH&m6IxgE@bD$f{> zR`xqe$x&O+XKY@+7ppp`NW4(F1YVIm?(-=F28|HGHxs->^TPq|x%_^jqq z@6gNyKS~C&1?zqb%iJ~59b;T!Ii-cJVVIZnM+7r?-zWIiF)3LkeEOVkDn@%oC$pJ; z^Egx-y?sc^^bCn)i}SGRwr8I#m1D0tt-*}xM>J`(0n+PVRC;{ANS@<{edN%qvJCKw zZ>dS4%7y1LVs5`g(9Z4Hw*>#d7-+wUS@mMO&p|5wSM*;Lym!}@a+@zCy6`;Y(Jgn5 z&m5;qw~-5=^Pgy`MSp&)5aV1a@7ZL^H^%283Ts&{H_F_Uj*__oD7YUwaS@LpGkHYLxm6_$8vKLdGoWTKQo3XI44DEaLzQBV?AML zJ{&!5u0|NdHovBxOkeHY$-+tv-4nX`M<@1^XD_wuct@DL1oFohp4QVtRsr_#J_uD) zj#o*P3<7=_bkv)?tDE@&R8xwm zoZQxc<+!`>th(ix_L5HpCDXF^%&igRn>hiri?)plc}m_nd>mIFE2&G~{xC|;-K)Z-}T{I+V3<7CzNET z4RT~Sk#@7kIjlSy_FJV=ZO6vT$D#eGfOstq)yXRmTD^KhQ-3nbd1gK8b8`}%>N%QL zyneCxI`o$Qqxi76Y%XLtXobe>W)^;YkVuz7EN`Esenxa$GgrOC?n z81VAAe`;FCf4$Ul&B489RFKdD1Sqv3Ei7KxVGVul@|pIwi%i4TF-Yj#r{mEE8zhgP zgXQwwxJ-i5%;xrP(^!>5wJtxT5QiC|(LaP{hR?Lre{0xK?UQBOeDgMVo}(4*lrgk~ zCbKwWHE-v%(%s;=S2RQ&wdpf*EU#+T@By~Y`j1m&DG;;xScE9`^UEL?d8M(Yjor`o zl4@*Wdx3&ieyAV+Q)1tQxLQvgIgyDi+pxk}{55Y%;18G4RMP1Uq`H2BPM)5c?~o(ANXYB20j=oNyV%nMBGTmDVR(^(1PAHwre zoWVvx0k{SQj0&-=yaaqhts>`^w`{X?CwJB+o@V0p5hJ_IABv@JB4AKPZvEUffXYq`MK9r9ej&W1{eYOsK949KYUGREx9mBUzXvdL^oHBSX zn{x`zZe>WIE%lt?cC!is^D{C<2CaY+mw+3u+hBQJ;WDEVe-|KL>f5pHF}}U6&4*@U zi6ijQG%rwIVC;<&tb{|-R;^_Ah2IFAj{}@Vo}m*v!@ohJ?)AJ9=!%p>ISzb%b=jVf z2Fc@fH7}!EK5u{&I%t2lNwKlM{@I)eKv1GnlEv}lqW1Fs)ti!= zg$j3v6gJfz$A6NJyNSwv_njg*-ekUsGnkJT zZRfs)e`l75)hPeO_r6K?ou5ji$4)hXiQ0vfmWH|=e?{> z*=Q_qLBXkQ)e(G*H`loyS3@Ruq$yFb1jYoK(nmlq6ae923}0y<=W%f*lD$0@hz04) z32<=|{oQFI`^wL!@ud3lcIB`I3P>4IYx`mVv*c7HuB;>GXiP8RXx@-;iP9>*;@)r& zrZdLY#Lw->lBK%?bvlyZfvQug-52+s`xh=vcRAy=-o!LeLRoNvdj0H5Aa(Sr_{{R- zmrN`9vqyzAX%@Rw&W@bV)kl|K7g|G_on4g9C(8%ky5jZ0+F%o4r>^UVw6k8~uS}{M z?b7S9wP$0_Hk{cNjP^9nijqx0n+wFsqmpse_gNW}$J2w0;mZ>29xK3ASY%+@JZoX{ zPR)Ynqo=+*A<0)~KHhchsWM;E-8vj7e9)Y}@E6{`qiZIk6swY9=>U$O$HwF=E#uBi zjas%l^TD$trZO7>=Uj4&-=X|&xb5ogi8VuRtG}fP{--aEz5t{75UvCw*p5NYOCr3o zEOvH9GCHMna+!QxLg9_2TPSLm*sZd{RwNHlaFS9e^@19Yce<0kn3^6?oxj#58p8P) zg7fvHoJSHDoRso@N>NA8p-OQMLnoGCc7hz|Ik*6n5SuHK$X%tEd%j9%bmss2#9v|MMO`f1(=ONN=<%ZD}`1XsQpUyX0Mc@PQf9 zW@cSha-fFI&RPwcnia5k==qPXVTv^%bktW`We7M1JMDTV-tg~8-L;6$#}$Elt`hsk zhK!KrX+9am=Cd9F_iBK_?gIZ+ORUN~#Ro>-F|QNli?R;o zom{?PLqw$|L1l4z7gs7OFs%DO74H0Vj9tY669suu5b1W3h*^GaQpWb4L$XyDIRZLG zI9wqea4PQP<@4cm#M7$F`1a?$nG~_*HOAg;UtBlIAnjB! zcs8`SR8+~8S~%a*-g(y29pWSpOlko5AuJM_Zl2hT@K+E*w7O^u?JgOwn{VU}1pdxk zUN9QT?5hN??DiXEe2qs4eW=JD^lajHHm|Ae>kD40 zu%0bXMb`FAT_zImjZO2Ns;L#`UBMp;RjOrm;X|RL2h8=h_Edi*p2fzDnhxGhk;*U{ z>NlsfbA`mWm&9bX-Vd}!a6ic3-ni}FOW}jUC^gR(I+Ng%X#+P|sg*u&oiB^b^PS#% zEYhOJH`^Y@QWAwkZiwIDUYhBNI^p48xi?*0F8R_d8dS*NUVLbt*NJyU;~OqG_b;fK z&7<0>TtYs*-EnP_saijh($$71)${e@0c>|;w6cnY3|zBlx9x)L*RQ2iZoRzTrkZAM zrHlT(9C*?C}%wQR@kvKjOM_RvpSqqgD+*a zv)%Bg+;5WN7yW=fUH3fR!;BBlXR)DX^Ll^W9#ZzZ^Jy)$=N{p9otZ|(yk))pj}NP! zS1wA|ZF=GfKE*aM$f4g2;Pv(Vwd~2)t(-SSmPtFrLDzjOm(y{#Iwd}K-)sb%st>E# zCkW1N<@RP6EsH4QAp1H?Ai4&vYrS3x@5M)6`zy(>bb1KjQVtTpfFoYSiikFAm)VrkCqcZR&-wzy!rQTmVK2PSp6bT0xq%2SGeoNxDZJuAfQcDh8e$6mk-ilF?g`sDFrv zE^`Sr1Lwo@a~}DZs~`sj^`P6bT`n;-{P^2~PLzt2p1q!e%^h%)jAaWPV^p|v}U2pT)JszRiRIzhyA`qCGAD`y6>Y9hD2*9n%U+dLsYBFAy)R&~`$6Q_6uHS64@8YZ6xHWy}lXeK&=e0pDl z$sWErT`L;J9fZe?tuG%u+DMC9E9gc5__o>yF-_9EP3WBN7g%fo>&4Uh_>*S8-0riV z!&olTv{cdxJ=V5|FM-&_t>Fv8g?;ms*9{rSIbnNf<=MW_^>tZ6vr$E7v1rpSow^eW zt_!;@@h)a-;MX+ks^AUA+b$7W(n#@1_I{GC7Po-(3*-1@xmWJFyq@2gu(`Rev+8=wr71;6XrUQ8u-&cX@_MF^W`?`Xt*gFs+Ed~`SrUSk zpUg6r^o`*dP&r28#{^?q@V%$@8lPUTaz<@8XqNdTES7cmrQH1dWsG99=guFqU-~>y zK^-4M^_q&$&*}2iud0_tprYcN61i14ZS_g!O26o|?ww^0uSx#X4nn3zuXVSNvj&yt z3?~i8o!q8AR01}IS67~>xCZ`Bm?o^sel?D9o~P=WJJ(+Vi{Cf)2LK^$^mPy_`}DN1 zS$V!sXg%M$X>sYU^?3SnHP6{(6H{~3GO|Y-d!eLOrF}=;&GFX8%SFNf0bAl!-=gh| z*07#{l zR1eo#?I_}~SD9{acux1`YLCxEfh#0$k*evnY4TdlPTq`7^E3A+i$6d(uB8qo+?<%6 z_FA}~*MR`GrN2dfbS!x`Bmb}fMI7k0S4{WH%U*2@{=z!#Y~Ml;qTLXkDEv$03VHKl z32C$yqB22I?0og}spix!TX0xE<+V<=l(iWLlk4_C2fGl2j?6pFEwzamVsw>GBt_Rp zJ2?mO3X{j&pOm5Kji8!BrdYXA*PXT!ynI{Es>)MxSZ~jd}sb$ZP!D|FYQn-J?~# zR&0P*1W7=P_+RgEpU9_Ww(ma7V~plCb(C577@PPhmc6POQSY)XF6qM(x#;y07F=Ib z9JQ#~I;UrdO6o#s(9RXN56)(p`#jDEE!fo!rd*9_0asDxS5{$2pFbnbic*Gv?XC>KM4 zRW+&pHias|< zcD?CUYX(oao_+`H=p~L)B8^7zgr&e9ldD^jhi9+XxQ|siuO7cZ&i6RW0(>O-O~cQ= z6FHCKnweF%^{G`iFTW*bhQZNj#(A^q_x(L@d#zAF-puK|22RLmNSScQ4NR&77(Lpl z99yOEP&+!#ty$Z)o&o#<pHDZnzOm2{DgN6hPLkVA=dp- zuaSVmcJ#==ZP^J)m!nZqp}MzJqSqsdz28#*?XrR zG|<`5qZ8LXY#_h&XCkR{zg!-Uy?;tGsux(ZrfM>SG;D;a`Jan#efB;`C8qy+)Zq9r zdofq1%6$1{C?z*oity=wz6$`RfyU&USm9N{Z#L65=_kXpb{uMYMhNOVNNG~4u^|o) z;BfVX+TT-{v`G*dsJ(!lyZSn z2M`5pcv(=b)DruY@%$ZINqn^v9(sDq^b6OpHBWGxyZIEW0Yr`$J6W%-7i^w=A$FRx zeZItnF8EGnnA|yIUBZ?Hm74?JKT)4gB7YU{x;2Z#Sk?XbUL%#j>Aw=wj!dODd33Ir z3zIL53zR6A&`MwgLV6p3+`T0I5cW zz8G$}zKNLLOmfP%6-Ynfwc6&A&m37&*p6CP}x2=bl`!<4OhWn*r-iEN1H1}_=qQ{knQPq3CUU)Z-?awqE zQkpsWhH!6D=emPo^uLkvC6l{4M5RAJG5GmBQ}wDvLtjE9fM@ z`i*+|j|HX=ioXxGoB0Oay}KGW$DI)cFH_WGl(`IkD!c{ggpT4*Grx zU9yM6!X0!+hnu03R(LJn<}`z z)$F#Brqo&l^02I4L%>OF6$IQxrA`y$vQbj|+EV#<_pXwxvv0F< zU0SU&K7M``c+h_c*Vu-m)&w|O56moh&?&d4J1ypqwCr_@gkR^KsH^r8t;TWQ-(v0dBvu_xp8l{w`@rF44WLwz*NS-(*D9TarEjG4n0 z)BdLQIdWcBiw>0>q60OFYp=NfwDCeR%ALnr&SvR=)*m$eE~*&N!(t;2x4F^6YFM5yGm%4@Wb+gLg0LGYwWZKz`}N*7I?kiL+lBX$&J4HmZ}3NOYI|H&ssi7Y`vmX%{Eg z<|Ac*XYl5M!_(UHVwW4-iOPJWT*B$`b256uQ|d4lp(=&#R8l=epTh$E*)@&lH6ZJd z;90clU@0STFdk7sn6oHZgK%>%1-KLr^L7U8cUm>vGH7%pXacY@dcGkfi&;{i zZ+yi2ma2ssM97cyv0()qNB5e&G2c2}!r{=rcHNzk^0LFNuP)CFR~WCt<UKIdtj{V`_ab7^AeIj4MU5(|s%Qo4fknRZi_^?Ak1#kSo)TkW~x9WLMI zO=a$@TDVKN+xWGADtmr|X83knSmwe}?QO{80rQxZRW5`e4$GDER~#*WAIdBWNGr#& zcId&*Qkr!-bJRG?ayMZDsoa*_v*kQ;WvH_AabL|T*bWr(fX(TqU6Kg_r-yiPGtu1W z-JQLsleG-85}hGcc3Up!7jAUZ2X(ti9@FZ*jOD?w;si(~q+{@N)qVTk3 zr5(4Cl-YTKTL;j;x1xA|wI$ zPK-BL7@RKw*fiyoiI{@o5wHy)3ftqN9etod`*Y@zZiTBR#|jz165s0Z@K=YG!YiW* zPkM98Bk$u}A?Z@n&bMV19_@2v^E$;1GVyt#EMeQY%7n>+e*ps|YpJYrV(1)ql6(%Q zKc_HTH&o7@XrexZyye2&Xwxp$g-eN|K7Vrb=+G{ytdUz3d(MN>?60@~cvf&Pg;g>Q z_OuuCQv4<9;=YX+dsMT}RnLP&{V zt$UU=N19A?C5`T>i9zC2+mvd{F5UOl)C&*d+uJo8En10My^){uf2B<2t;+T~NtTXD zAxnan!qjgsFwPu`>3W%#g>g0w+}LL-6U|Lq3!kAH8Ni)MOnI%G6R2sSqyGvdG5YRy zUb_fse{6S&zESv8g>NMjl0ag3E0^jJUdx+G;Ty!)`OXgCM0*b&IbE~8%sQa-kYr8W zz_k7awEHm|VaiWkezts}K1Jfh&~2kZBe-0n1*Fq&%i#U8%PGa zene}#&ojH<8yGNs?2#p5Tv;N~-(bqVKEYL`*8>vvj|5Frx}3?nr++ihoFE&qtfKc-tSw(je z)~c7&CD##6*;c){A^pG7@(&VW3>h{d#6p%gXDDej%FuO^hAn6Fn@F6~(>Jst^zGcf zb~X1H3K)xGZIgXCe~|YausF!$ z8Lzv$IXfVxJC~5z*!K#hZj^*!P<8<)#{+a&XRC5k1KD^ynmr#C&1tpq9A{ri3{5QD1));gF4$SqGtG@(zPE0b@@+#ZFWSlZ5lD{h{BGCMAC zp&LEvHcU^F^0c3HZK%{uNDA;oOgR3idP&5VK{fuXWii0KmxFdhn3D$21o#^U4Oge0FY8%TgNyS? zpKA?V*M-~kz5gyOFz#g#5Zet8@4;no4q~bp1QbW2#xI;H!zJ{5lSa5G#UKdkAcyA< z=L%Y&du4JGGE%jxmJA3(FA|I)W9v_~gD>3|IBvaTNJc)k%Bc-0Me!VjShTjJV0%q- zM(fTg$!mSzV!QBXcH2aoF13BCVbfW>b~|X1D>}AE|5NV~1N}>Gl(AlPh73!`{q66o zD`vXvN_NqYaHhwV(oug+#ViYbm>qK#`Q7EPv5=tLA4oexG4)JG%ag$3bWL^Lm#EfE zWvg2N-w*f34xhN3gl=4G``CVA+78+5W}gcflfK;y6OWZMT2DL%O|DNht**4CkWBav zHZXybD$~GK#rR1UiMI2utQ_Zj44&=bqPMb5N2fn>U)B+72Bw)i7_Y;ZaCD%KY*xTY z^RUL%OM>I)i@z?cubLI)1w0lB()*BjO=?3%nT)0_kq$W}w4~Qt8$RzAS?4j>y_|kP zYIC%~*E;oatl}|++N{H<2E!CbZT`A^4Owm?h|=B^z?~EYI3Fh9vTVk*t^{7Qv81!` zb;p>57zDU`HG{=Y2c_(-Qa(IkN_#-ji4plGLi;-Ui|U^U>Ej>D$^ic8i{XDQ73FAu zOmnW(AfB#Q?76|GY2WeRuCj)Dg=f?r9S)h6Oa-P>Kn2shIP$!#!LWjY&PPelk+JA| zrfGyIF%2?Gg@ag9F1|rEA6#Y2_^x#Z>k}UTR2dmmJr4|1Fw1IAC!5crWQbmqtMuh8 z$=?-NJ_;_;XgZbIy9^OlX8n0b_gz5581EZS$4^jS##NL7T(^&SK2TE&RkxcKu;VGC zHbbLI?XsEF0=Dn|RV}zeQKGy3Y?1vh$VcgIl;2fWhL$TDPwFFSXQ-an8}Nl1&A_vi zv-s)FW&lq|Y6D(}<}ufJ+kKWLzp7KFuYWnqmiaVybOtkv%g{`0&-|@KPL!jl0y3SJ zvwh&I#TTMuC}JN6q?W0gUu{#+*6J}LinvzCg1>}1E3`y!*IX8(!Wa?nxN;7W7yYZd zVipxw=Xu6vKT{^23JX82IK-`2Cz{Db{l}T3C$v<2eh$<=E6a?|-6rilyKqKDGCa73 zxK)X%O&1e3Ei>tnq^Xn2!YTyjjLfm^(4N)i@<|c7?ocN&h|u1c6I)RLhha2^6Kqmi z6dLv_KAq&#?hcROz#)dOGM$T72lPAPiw#aESkLg>A@a@k97?8TprU94Q@P+;-uWa= zb3D8#gozSC?+=hUmLG8nue}beUU?MLxOzBrS~D~!heX_~B=c8+Ei6O<-dk<-R}KV1 zW~s{-56h80p;?rk9krnfe&h80yWni`zYO!AMiBKJshItogJy?=sO0J|B_NudR+ujc zsc|^P^VT#kcD0u+Ox^P@x50kF8kB#7`j)5L&r~CS{O-`>E z+x*h^s}xyKRhkj&vAv6lcHu+)T0bTjXVA+ZDp2+FTa5*QpCi>OFk|ygb_K29N*3K} zAyUJtTO!Zl*yb-v%6{5ndYxMKe-hgMYx4KsUzI+fo-6X*u*6K4LveSvOdI>=Dt5g9 z@Jeg{NZygA)3WiT`EP6hxE|V#PAzGdNzO@J?$PG&fSNNok0A@1&*r7YCC@(12(=n) zJjs1oJV8B&woMYF}&4QwjJ~Ek91b|z<;OF%FGW_2tH1^DK z>|flx+wLx;G%a5X$l|wxO&(|BM zZYA_y4CnQ#EZtrCC$-Au+m_*h{^G+r=2!SE!!uemuapDFVAgrl3%$}_@-OYiCBGh~ zZ7}KPTGzCc^oGu^GCHEerkEoJ_mkE@#3;}v)5S=;&*|75%F)J+R>~dFz>mdWH-^^Y zn58Y+5lUZ;(HAE^g^n^Bkj^v`vO?V(g12GJIx+L_v;W??^p!s);y`vLvKl*EFJw2U zkYm(x^Z?JAQ51?XA8t{6!%a=>u~vlLT7NXu^v!)_uoF12C}N)|uKRZv=wK;t|AfQR=cT(hv&;9n z3jG74sGDq+rjA^c&h?u^eaJ$ULSr|A0^w-3sx%*&`Ii<>bNX{dt7Qfl0X-M|6F}4E z@!G7{3i@_jr*@AVUqV)(Hv|lRNYW-7sMpVb$5a81vu#EnC_@GH*#2Hnlc$I#Q+fFv zgB9y_=neDoiU^y>qjU@Ghs=^4=iidL%X?n)Yp0c9L*Ez{$wz=pI> zbL)Pj`@L#?%Ii}3vHfueCSNV>P|A_yIspboDdDYK8yjDh`AXL(JGN(GC(8lutTH2tgi``=vT-+oqk>!(^9L;Kabul*ADzsMGF=?DqE zWxvW`#&%`oVguiLt_tz8I;q20Ry#sEpi(%{yw4;LpbOvY*@c|D$`fYT!EpUOf~!=v>Y;w#x%GQ?8-5< zCb_RY3U|JS<<^*=V}koMJ>l&~roq@?4Id$oW;Uk?B=yA*9PqvrO0#*k&p)Ej>j9v%lrf%U;e- z*39)!U>t0kVcdy8u}e~ox2D;xtrhtwOW0L!IJz6@AgB4D1{kV+t~!WnQ%hE}?CHg= zIYYf<8HrEWwy)^rlO?6<(McL`QNiW#p%bomrR_>q@C(z*|TplkR+&b=X&l*)y@jr^wD<6NSDivz<3i0GQP54ZZEqBZ?lnSrok@ zY%aJL<~n{;EQ6vg3cclYI7}L+*m`>Zh;5v&I)9grx}{q4MTw?I-A~(R@pb-#3_#Mg zCsJt%K02apDZoabfxcAWP1<`3$nPzZ-tGx$&0fJzDg=y5#qQW}c*5Rt54M#uc-GlW z23VG5v0<}y$p$A^zElfn6>kv5yDQ*8S(#48^6Vy}gd7KwI~PDIJgJ#!G4 z+==n(76DEZg8C&Pt(5Ky)WYFVZTCTfnnzX(!kVkvoVqOYFC0A6|!{L)qr}-?q}Z z{YCVNfbAO@3g+VFU53fh+1oSVU&yPOL;By7x%~4IZqJ8(9XpF%M&TpBxEX)OLFK-4 z)-dN4Q*5%^V0Yx7E#fPe-No$alNn=;kEAA>hKwdRJk;n%;CMAx* z|DPk-dY7rh^eVo-vS$1HrepKln6vFWTFTe662SEyr%O5&b zI+>F=m-le(cH$-FOCKCdj7A>TH*{nfVv5ZY#`r_7=?a-{O!y(_c-OOpVx}3wH_J1&k z-cKx)wDidH`afLAb19H=Wvx_^%HQrcipQ1SqS#HK_uZ1{zrw(O3KBf~lq;>=)MNhX z-a2YO5#~qc-WPuk5&lmJ!(W?clQJvNJ(tM-Pu`vTks@4Pb&Ky~=ilvspZ_&%io#@$ zLGT0qWQhIEy>DNpFyz#p-irMD)Q0j6wX8h3?18$D|B!L~7N$-?+t81hPZ-bqr$zkN z27M_)!R7nK6MwH)Nx65@BT6*5Efas^?}bz;+1VF!%9Wd1Wq&7k^2^3|-%%vn)jj`^ zx%f}c@fJb3aw_Bb{~1C4H_QHC$NayJ`MELH*d4B%ZEqxKB&$7);9DaFObA*xR(j7PIYg;Enu1Af)IG&x25 ziQGSh)ZCf#mvn@DOJ1M-8%?fXz)RWxXS6aOqS^Y#0O-j_2|_bjSJ+?wEo7s_mgUEk zh~Y28-0+X#sxONY2t0k&?JxaBwf-;b{%@}mSt$q4Uu*OCKlP3;7k<7W9$@_m8$5$0WTvm_huHMgk{SCG@`unWDxNAxs3_qH8 zZn%u^+k@Hd5gmd?MadThIjGC<{bfVXigVI&8u1^n4X-qI+h&cv2P@c_m(W!!%$uS3 z%0?$4dyBYy6o{nZHrmubbhlB{LvBZ6v!Y*E@gbrGq(5FT@3b|PPrBn8IQge@>6p{* z%=#}w9U6Xiwhy#uzkg>A9w(=OR%hn>?|$8E&fxs2 z-q*xjgQi~dYuo(7jI49XtX}hYFU8Krtkp)-SkF7dKbf${Y){`W7@xQLmvIL&~op#ZcS+mPR-o!Mp~{!SwzV4BZSES z9Qot-nN7b?I3{WGfO(ZXeDq*;c(3O?7m;fiV=ox-FfDURAp;?Gt!n z?1cxL@2X#jE{@e9N??2;!6uhph$1Dtjfj#v%mPwQ&F71`NH_OvY-4p&Ols#0{X~Y} zo)55kl1^lgo;~C@NeEm;1q2P~nwX8r2*4eROkdI-aWtywKJ*GJD$FVsj;SaMASXLM z6Pfn4c=ANYQ~e`RaY-?;ObXF)Y-GWl6q|JBzbPU9E0Ost#N|HvNs1McZfku==l!F! z#PYuJ>J{+){wL+Vrh5%st_t^=(tp!^zm6`skMEbjO}G)_M6z_5w>~{ttwu~VR-gB~ zg*2>S0VOS!SHrciWfAYZFk$f@_qn-qDwDJ5)bPVXWu<5L?W?Zp_FedF3qEytw;X3f zC5IipaDf}bKemaUm@le$qX2i`k4smWZx9V{O6>8<4rtK2Km$jKobuE$^k3BZkY>D6 z9)9=jgn)g5ryRC_&GphbA3FB*mlh!&<#Ts{Y{jbB&$^D#kNunTjVc(%NAB@gt7lH< zytX-y+PzF=s$(h)9snS-4q8LG(1m!#uK*@^cpXkt}#AYQWS@KAaB?)f>m?ad;o zZD2)5RKLXh;stWE5h`HA1L;S&?==P*D$;R(Muc5yKw@j2$m&`@g(zP8k1 ze1y*!UT;3cL`DC;x#K*6)gClBdX-XcI_s#u1u(N30#-*VPIEU_RsQF*Y5{h&RDudQ ze`OR6>rX1t=0y}&$C2-YH#D~SwTh5}Bls@M&yjdEe#)u6>AIF{Mi=AAc56rwaI4MEoYP2f z+bC1vJ(Tzz8mBUUC@0i*`l_Zw02cUaW$v_&Wi7PP))f`@gxY9Sav+g=swKuT!w?qO zZXhHE9F^SEFOQa%V-_0YZK}AfVzMX2zq%fb;A$^x*`U+bp)n}ocg|jV*VNr( zH7$;_2Afljf}4cvcl4%hlM%%sGp8Or3!KueIou*p3#KLWv9!z-E){Ovjr zluuYcZ()1dsJtyUw29o|hi%l}?vbpqw|O})+8W88T3uDegG(OH8JFmBJ__&jXo<5Y zrHEjsWia$C0juAUJc!66f}$D_BdIs|$YKQ?^ilyNi#Gx8l4%e%@DbjXt)XdM0QO5D zs0fhksSY3F03|C1S~CJqS&Cks|4;ecCpSSI2g)PBdI9=!A0D&G>BHpU@XBqy5<+@m zEf}0;@lH#2V+PJ`zdz?rXi7?GR!K9k9AX@LBVf|E%r*R5RwfRQ^To>w9Zl$Y_r6$8 zy~lJj>q>&tooO0%YPN=WSHD{y%vsl2dSGTR0s2zFs!2&>(3_HjTlF)z&#)17U)PV} z54-q|PaZ+*dVm0%ilT{+fV(!gblri~%fr}4j)$-v8|PwwR#{&cV9<|2+0(6zZvQiN z2CN+!ZP5IPE;nX$`1{kLB$%pk1@WrPrD3&wI(du8kb60u$j879F8=1|t?9$e%Nw&z zI-DT(v(<0VRge*YVWgy0j}&BGDK9Gauw~+z;aWrYiPOtfRQf%T1`RPMXn8r+c?#(i z)#wH)ZaZE8>0>?1aLxWTvXg{U`?5JR#ITaSh)wV_2@NFFl)IG-ed?z4#Kq}W>HhiE zjdXJ_0>sTqn#E@{x$ZwdktM8@_O6yPh`6&JunCM5IV#}YcG3Be!~&+ZP>^}7z}=#s z6`Wnhmwljv&0pJ*IzlA1KK}lgJTaQ%lSU@1&Q;q3@yEb|d47M>iq}2kx-cc1m_tVd zrc&EY)T)!KU;zS+d@t#L{w4f5^hsNa(ehb^-{pr_-k@Z>PCF=a< zzgAV)usHjqpF7E0f+&SIK`uJvR*(+5_q|tLM#iK<#OS=8U#X8qCB+HN@ap2X?`gkb z5cUt4ue9sl*K^F{vEN7{YgrD5T%4X?%UR4uGTlDjxl}5k66s3Y>k7wT1o+BA0qkY* z2yX52r#XIcPhs2mHHkEHAMkjX=3t1FRQTq^Z_4k)TUJA=2X@6K zTEg1xp=ZTP)J+V%byEaXca;wBKh&1j7aAzjbO?b9Zvip{k@-z{Q6xn3`-xH5 z@GULX-@-o9P<$3pG^>*kFWFSM7~=X${~Ee7Dn><29`_0dY`b(iQtMPviyTpnRP&Z0gdjDTYueD+Ll_p zhmf3Ylut*uhg!qPIu@V$Zd)}zy!*3d5qvXDomofgsg`cf#FT)FT!iDMsm>9*LN*Oh ztTpnz6`OnKEc=w;F$Jd~=s-o~5gRsQH{_Bjbi`BS0YAOQ-DO}->c{HnkvU^dm`0Vs zrKES>90|KDVv}W7T{cEjJ8-Xw!J++r^J2DmTN1Le_*P&$9Z{e^8}b&yIEzhQ7j;CJ z#e8$}Xf{||m+O9gRhlHC?ci;AD&(?g7LzS{(;7ikquwRWZf90jziWa1pw+#Jar?+l z04!D}A5aNYH#!TY?Ux06UU*WR4H?=$!-Cr~A@sWC)Z#o{CT46h0Hq)~qy&&gYSZui z(^%7TkvJ=^A>IbKLbx>os|8eYeG0n0e@Mr2DF;{oWIQE#`c6xeY9=)R@45k6o#rq zB=A9BkHGCtnyU|!lW>D4E=~Y}c~&sxg!s``uNbJT zdo$`V(wQB3(D#n7%-=&>;`M~!_=$-lg%1N2Xpm2bQNZ!CF_nH~%yW^o+~oZoPA5Vc z;T{9rW~!oZ&jgadKF!2tbd9suAenI6=N@VLs4EWb=_&`3aKKc(}kD!{X zVKJ0d4ZCp5(k|I#_O-F@Noyd|WoGjaRnY-?LU4#ld{-E47TTEY8EuEIz!S{e-FA;W z#RbxSDDzK}62+*gGTL5z+20tF_qqMTBr4X(Gw620Ck;zSa6~X_R94M4w|R|zSz=dzDlvPu#S0|8`u2gGE)=Hr6OK!L zvKJ{EfK*j0JX}Wxsjf{QB^3{eQIW5oRS~EM4#ZVyD?uTwz$#U*$yAS&g40F0^z9_( zNMFe`u~8`2D4A;t?vQ(gFbxqU4z0y0>4bGg_bfhZ$s4M&dLI7-{u+aNs%7ho(k1~2hsT1YVv;t+?a2#G`h4((CV`akI5Eerap@E(d%D2*{bGl@Jk7SK0I5N z!~h3{91Mo+0(j*>mXVIntT(qP`F7soeRPkd&FSpP#Gq_*9@M&C5z#^Op7Z3x`Dkqi zx9i`q!}H)mx5i|%CfDvVS((9Qx&jbY`VNwNC~Bw_QdQVgX!W6%;jITM(Jl8^Md?t(DzC#Fxc zKPwEJ9h+Ffbc??XpS%8%r3)%RkSnU%U`5*uidTo zZZ8ilKC%FpJzqiOGBsXBsD$^}Kzq!Q*=~e`)RKm#)ZOLn%o37$lOFJ+ma-=vf?jj~ zjZ51Cs!oV2*;Fjta$-q){ur}RftBDO`^%~B0I$_XD&#W`9igZ1j zy}tX+-2AYbIhG=Aezz@@`0dHKKp4^Yu>Cb#a=~|QUe%v1JsO(3n##~sBy4SRj*x$$ zjwW{J`hd}Nu5Y`+hu@v>`CV#u6@kpm6K~x;TYT3@aVZWOQ#+uE?FL0h3rruQP}^qg za+|t*wk^BAjhmZKIR;@!Vw%vKm5qn-JL^_nYi^d+aSmHE#92K1PC@EX>+42JrF-(Wx*xn*~A&~bUZrmF^g(z?w9Q(UDeXS zbdgX8LzB}b66hg0z%y&?5%OfeX3pbWIogKxv`#goLG~&nrTpSJ>EL^Qq$O)oKw(*Y zd*sec4r;@z#7U$!hMgu`=1_n79{ij89 z(f>TRTS|XHm8mZ_v?ZHO6=06U0``M#JLg&H8MA(IHp1QuSb_+0=#%^)^lW;f@n@#! z2a)LQNf^LN&m^ztCcvue>xj+$Md?A#kdNI%?_HJhlD2=H$&}LIfL34MuoQwIZI_Ln zJ+Kuzc!IBTDxynT$sF7W5$!(8SM;mYYpFPxT&Z1gxGBtde0+{84l`7E_qaa4aPCs2 zl|X9@bl~XFBMPAV*%~&8>z)&Kh4loxA<(4rDMdR7obG|fLPG+1Z#VJWK__oAd zkH>I|X^WwB4KL76v1~a8LUWrVioCfSRdok-J`;Zs#+(3m2Bmy9!jQX!B&WA$>8SZf z8wT^7hk_I4zv>zB)cS@HoJ{*MvmR!c4mi1(TLL$o^d@Ti#OT}}zRU-zS?oL~yftlT z$biiVoy+D#8z^aUI!fr}{8^_n0Nv~8w;HWv4i3pTN!!zdt*gsP@%LsxjS(t!QdWRA zS3Y0>-tV?{5RY?9Z1sE)GfuO&Z?F!Q1f9^EY8@)+YfiiB>9cfX>oB{em!by9 z$=)u;Dyz#s>jR%^FV(c`z}jZAk5Uh_WIyMy`Is6g1iynT;sWhLnwf8i5@MSJRvu`r zab+H=iz4?IDNLjH7mk->F(71;;Z@+bnx!N|i}8YxoTZwG$0me$7H5gsW>~z{N*OZ7 zjpqrxPin<6B+_~E)9|~>vFJBh)tLFf#|0N+Ipl)Rsv;%#*1d`WVITRw-E`CaFE?Pg0F$7_i6mdx1-KP_@rav!TBG(@9TuSRivNXAFAbGhzHM!hQRdRoG z3|%KtFw)31EOV5zi{|gL_HW*m#73>oP3Cb#T)o?6sv6ehs1912lhJ96UwSKA@3@G@s^Zt_>+prPz#eiyrEHh=_g4d_sV&i?k;*RA#F+IIwNiTbWc+n`B6=u_k3Hpzbh1D$0b z*jL+l0q>n} z)yX9}?SA@{$4U-ZDpXcSAzX=Pk8EJkREt}kF6l@EY0*cp~mSsQ> zlUDbhb^E#12rB-*Gw{{I*G-V1SaBRtY&sjf-sZd+lhzMvQcd|y4VN;!hE$B(upQU; zd+Ep>)3$o0it&hyvmau&sg6BKX@aY&GlrI!D=PCD8P^wQ-~a;q z?t*&LwBHct_s!i=LcHhL?R`cDN=)jPuYeq#0?01Nhn({B_E6nWv2A&f{Woq75XJ-y zN9%p_HYye{Cp8w#+(a`sl%x&>FgmTGAJcYB*b68m7X0QyK&wNayP{N0&oBQ1Rd?Sj z^DH<3nyy+T(sA46R>yR(X|6jW(TOIP$SFTMK;m%}aWn1QLJ@~^#VS5pm1eMxtC|Ao zKpE%Iv)x_%A#<&tnx@lMzOhy~|Bx=H4Ks8&nIR4alqh))d>2j7MYS35ER6Y_;J(w_ zyrL&<Mar|0yz zsBrjNxsy4QEuIqPVTJZ=Q^;GcDi1U{2Gkik{xT5(rL&2%|y>CrqrRrWjrI z(ujIFuVPzvfEmDCLH&-=WNrZv+;a9!cWZ;8-hyF@Q^xsZL|H=!u!@WK+Q^0E>9qJ9 zZU)5M7N&|( z1V}m!wdWvNv+#GtxZ;o}+I5W?zk$8VJm?rCYri9v&gw7aXx(rPuR7V*4KWv=msvCM ztItHlC()yawiNTXEZQpVR-K7LF>blBC7I>pN8Sb|TaUW<3QN^sFW{taEb03Ty6N?D zd+AK&?~VT?JG~TKxFk;qd65J4(26EL22s$=HJZ<0wTGgB_w`ESs3g2{0?>cukW(vk zma-T!bsp!P^wPLL>a=h_oiJy0xK!+;3mvSHjZ6LtTFOgn{|BwyKmSjxDyQ zw(7dY!M4T5=4-=jZaRch7RQM*^nyV_HZRClKYh7c}mZ?vSH4Y`eglxE3@7S&IPNn_#lK*vv`&)by%!1 zzr|aD!W9Npi5Ut{212|i^?hecf8TQA?=ag{iwW4+$q*T-Bh90#bDi1*18q%zVH|Z~ zoi&XmN^KGMQm}9{^xe@(%e&vBD(@|}aW9)*1^Zh6uD?xrJX(3G^0!}c$d`)7&Z5yZ z%T;pdqIVCMJgx+05Q2hL>0es9Xl+Spz)g&Vx~K^;?Ri^Xa5ek!jHlFi%xCh*zL9}O z{#6L>@XTwJ&eV2>0>mq|s6wWqb21NX<1ygiJoeNRT-;cTMr^TY{ms-&4@jqnkfWXND;f z4*+({7H8bIS7?~X{a$`GbqT7_n=&MNczOdptvP*P*UL%`r`ArzL5=_?SophXCh4V(4|66b zo<#U-RdSW)5_syhPCsE@+J3*ftrOimedLICGP%KfZi&)3K1vUDR2v^%sJ4wUMkJ_g zv***hV^@9IhNhPZOXF+!)mYN_}d15Q;_yhzUU;_d>7x8SDR|wyrZ}j*&Y|nMkZ-i6Caqnd?Sw->nwV@hDo~nP#En)D2Ts$xqqj zykN9Zc z;&b-;Iz>wN&zu&72_+z?aEZ~og9=v7Cr3vtt1(7D*O*6VnYL0h2g-5rA;&Jn9Ts(Y zjV5m1I)TqnrnUmegTqF4P%sT^z7KlldlHhia~Rv^d5#b3*d|Vj&tK==Z9H z_st}GoR3~u%ni6!Bjbc;YY~Z^+&d5O_?h+mL_q_x)Yp?QF;ml0cM$g$4#TeuTx0oa zVt2MJc1@$?v+2%H`DbY#%7;G|4y~X)6uAXZ3u>J#z_)=E4Wo*)YvKwZ1c5}UXaWPU zdH2%4$!HT1?q%>>8j)g@2SlH2TLgl>I<01$R-fmS+M{(zHeU2e^tixhSTuQza<0^wp44}5WDwc0W=bjyg+ z*c%K$w!NW^E6{hn3-!eZe#|x1#u_UUj{y1_W!L<3CCbzL&8INP&2hJlr5-mu3ud{v zIPGUGVTRazChp0WK%mY1{W3@UvVeWHV^ExAVtK4*!?RVVJvs3w4y}h~Eks+#la}UZ zQm%x`c;2s^0gCeHZ|;4KS}jGC`C`NAByh%Z9QC860eAVf)UpP6T1H}0j)>f@#0SuZs83@S!oj9KKz^OwuUNaNE+ouN!E!I`ny8De z=u^iMABPJ=-x<7hgH|j-K?BD%;@B$wCX6+ndN)UZzK}l9>0=s@*)YzcIy+>{z)d?l32B&G>hVw+Mj9Rm39v;Mr>D!Mky*cx?r5N>8W@)z8KUSDt0BHxB=+3`|7_b%xHbxGYf`mfM zVYAoODBI@tEY6H`NG4wtQ4(QbE2gLWF=*Pa%kCPT+s`-U6(%~$3VvI)=`grA_&Zc_ zt~$gC4=gf{Q{a+Jh5M!`X>A(;y31jzDLTU-l)Zkq%UKK1ADs>7ahdUJ!`|{Xgt| zcUY6@67Pzlf(ot(NVlURMXAyeJH3S76ltN?NGJha!3L;E5hNf8fk22zFQJHn(pwT* z2#B-<5-A}-NOC{av&Y@D=br!XbN}J__!4-_%sVshl;1on_FasFD-#3}4p5Y#?*v0# zbj_n4zJ4UAR-13(<^1-xbITULS)7a#AsZqBE$i}>lW)7v7sN$mR=!wOrN%6meVbrk z!qI$lP9oj3to?7J-=V^eeTTo(o)Xyo1TBbQ;p5C&Um9J)c4pt_n+5w%o(wyrzPv9$ z&G$=TzhGyQ$cvnuBspi77xw+=jQKmHZqI4YV4>}0C+5#*W3Mionj=E)DrcEhh1meX@0#*&yTV3C zrs5D+%su_@-+gx?5I;u{5yy4x7QHLKT{$u&@Y(!DgvQSEFET8|CtWP?4c!)MmoGS; z?WwtF-M}gI5Jv-@x@&QLHLa6yjKvHRO1OR7Ih3)IQQL;gRh0Of|&=R zAzYoOtTIyglsv1mjK2oL6OZmjyEPP}aj)_$I^}7Zf(BouEn3ZL)0CHo7RJ!8=0A;i zC9Sw7Mq&)N+mz#F0WkS1TVuJZ2S$L(@b7irC zhXm{LL>!dTr5h(tW4`Hp9xABinS1N;hjNw;qB!)1-J_d_Mnz%8^^xmZHaFkJhK0$O z@_}lXhiWKA-L!;mo z&Uhh&-E+dIC_wZ4&6q;6)#3GgkI=dbo_EUZ_~Rm4F^C9(#Um;wYc?(>sn!!RXsI@VQYh04ut zJLRyUV3E~4u$@tM;gNxqsYxN3$iFO@c2bbDRQ^DY2E2yA%CdCS=l)z3EK!`ZmnmFv zCIJyGbIq4z?;EFF6VZ9JRp~w|OSJf{7tvugQS~gWB_+c~Uj!A)@guS03{?D)mqqEb z@C$idV9jsh8h$p<4#e+SWv|7sG@ViL^|f||jN4>Pf$dS=rMwVhXBeadP8boB7*$c7 zInjB6sbfD{pJvzDJ;0<-pBW00_R8qq7YJl9C6?HHDf6lgnbC`@!|cIZmYQ`6I3`zt z%sK6L36QE23@uuwgpl5MF^doPVV!3cRfEUOHjXx|DJgka8!2vvwU1PqrK5s|zbHRM zdLR5=qIW8j180>_-@D?{Wu=1^lDeWAzbtS_5QP3?r~ShCoSmmT50N*1&n|eA*~-*% zP(RSaBmXW($f`^Fwv&h4UH?dbiabaWMuq}P?vi_1J3@cHdy>Pqgv zqtr?OkMAVv>gZ_Uix%dy+|$X$wBqiic(kxhLefK3KOgkd4UCqS!APB%pD$1SiA}(X zvHQj0afutvVeBgH#1w1#XF2-&@wA=#19YOnUWWtcyQ{j6h}HoLo%^^kpf_DgD$ch# zWYk}Zps0d6`3$Dk%jh&bj4d zSst*5?*w=E?>Gr?`4^7nop-k9ujdsm-HjA0tm2B@a+KL*0j>f9B^b(XdkDo&2%3^R zcGiR|Hklwkw{^X$e^c7Bib8xC&=l5)eA1OaqPtKxLiPNUbDUgO4$5^711RK zolLScu)K3}Pj#PBSXpqh4Qd$lzT6fQJzSPAvYTXm_G*Z?DbBDkXY>n#IkGC?c(}uc zJmzOTWAV-~VFaU>UT^O_{k}||;Th4eem1ufJ;Bx&?<+Qtq6Mbb?2!$r#*u5K868$d z^)huLid64M;Z{{vFNw8wwzMwW$5;kVen54o24-%SI40;Bgr9;^b|nNdKidzKUzVn- z7Cxpq;@n71sbMvNoM$fsDrJBEEXS&fuh>0~!lu@*%%g){0$l~%>Rp;K1ZMYdLAu*cSnKwfk2ii8dNkG$&V*|a_WCL>a(e+ln^2$;NRMDW%q-%P$ z9L#g4pzGERaQA_?(8!XLg@pkr>tf+vykv(1k?-U;7_zCe4b(rR+L@EJ1cyT z`C1gXtPcL-Bx$e?rMg=1T2U5;%kM9stI0-i4MMjkT43Z?RuZPDyJEMpe(T%I@I1(qsU6qY z6JLuRv+*!F-?l-o1h=S~vK1FQn@MvYrbpIi*Jmsm+VW zSFj5}R`^`r6E$B%1=CKqv*R8oSFp$_N+E2vJ_Vnme#PS)E!I9s^&+8vlhJU=!Q#8W5_pS&=AgC3W@?*)0D{)+?Fl!8c@R$f9eTWcdY? zW_5{63X%SUtFhM4YQHFUt`(QIylgV(D_T9Nh%d0T1G0KBMf$O_O_Qp@AU*1Mao+n; zuRxS571k~R;n@promXvzww(GvrL6Jk?a2Bpj~~S`uHo;^ z*`L9KQ~?aOo8FQ>1m-mFwDk>{ew(Z!;SvJ%31dzgBf4 zkt9eg_o-TVbQX;wUB4^<6}&Awnw8n@mH$;NJ*hh2e*L-=T);=RI?UmFAo(MS=d*HX z$F;2OEu%Rmn0p-Sj#`LT9BsfrH!T11Bd#we2F%+dkF7YR^p}0b%+Sz7{TSXuYv5C| zk{>m*o#~|O7Yr&#A-T%;M!%MM;+5rBQrNWFghZB8GRU&F35Fw*_3>*60g`sAmW|#> zO{C?ju@r3e8!MQo62B|GFYrYR(J!s9D9}~o#j{ewz=nWAs?dVDTJIC~=mNclE#a-O zz;aVWzv-TSl{6gWA{cyKN!k$nny*UJzo80U>6EH<8EGR86Vk(|L2lA~gtW>>x zOg0nUQ17GRVk#G^E|U=iwpCnl45-#Z_X?IADy()A?D(fd+)GEC+z>vml%`VEpGaIb zJGJE4~X#+PR{SU*n>0=s>`uj`#rc>3ZQA$OGf|GO7Nv4&FGVpJ_!RuZ% zCyihhleh?!W^t}ffI^xR$PbdM3AY*u`Y zcVgb+h*+NP?3tH^Tt4VmG2&wF7;s}3A8Fa3S|n0^r&#=O*khKWX7%gg+o`kN=imnn zj4yOv>CssjF@CFyuPG2#27{aKns$VmLT6iQuNJ~QmqWj~$&1^tpNYS$`*izUWbTL- zk486_qHTWn6y3e;r5>%T0Wr%wRl6ez*pRl5g^@i!AOw3v|+6S`-YkcRb2#QRF$)+&|uSSFkPd34P3L$(IH>Y8`14N zhL%#C^yMXINXtZ2Ez73)`Nxn>q@4rV!m~v(-BIFHO*f9oiBb>LIR`lNh{yYPf z8;{rv`hqCU-VlvdgCA|D%l5Pf8k)4^ikXiLwzMMh2~mYXy^_3S0zrq~_%%`SR8F*V zBDT$@dt-wy(L=1P{@Ta0|5Qz*lFUJk!`xVqAAlG7frlrp1)+WHZ(qVpc~Bp$rv~0SbomkzQ|2Oyx+j@8f&R(ACek=eaW_gcv_H)-eK+@UiY=i3YVt5ZDI#vMqb`QxQ9CmL*J{GN`XEfF8VD~`*6 z)tGDOr-gM2w7A`Z?6Y3JKNcDoqv>EBFzu%zMIEeA&Oj!!<_B~O1;LLR0QS>H2m7j) z`A5U6s3#(3V27RC!xBh1f-?CTSzoYkI=mI3kZ?*)lYTEV$RCm@?JJz}Voc9cF)df{ zU4|{GEe&alK4Tw66t;C{bX-+{x=z&S%C5$^WfaHL!rRru@N*5BR*=@7^Y=Z+zxzYC zeC}JRVLyKWIQ`#3ax`V+kxt;_gcONd0W*2vL%(@WAae{bf~D2a1J{Eu%1i!q3Nla1 z{aFg9-xz?>%AZ=?i`PZN(L62ZA>9B18x(b57+xUaBa+uYjNfy+6e_Pbqmi>{)?isI zv zOFqo-In>7TQ^T=wi*VC@iXMbc2KC9ZGwc?DqT3cuPDuOxGWN94y`ZU&ZUvAl&e(n2 zjn)DqckhAevi(n<9I2robx0#Ef>z}riBIh;JJnVfS!!WGo|#&Z_AcH9%aGN`g-!>w zH$(q5fk-YNO7;khQy`?LRlV?4eOjtQ)WkGTNredc1W;LqXnlCD;so~_UaWTt#3Z7&{^-;OX?O%}kuY4( z6#G1cK1Lb8$aTZ;I`{238=ZHVuvdj2UBcVi1@|{N95ZYOJO!0+aSo{NE31%+R(yvo z8!le8&d2u$2BiFo&~;liEz2%TWV}9&PKCL*DWBN2k5IiUDNyCB_B~$JjZpa(&l|x~ zpF}pS6Y8_?O4l_}`WfWsiAEL(LjzrL(QlEfg8JBd-4V85U+=`@jA)-S@w0dR*+$1| z(sv!-oa}bcx*1hGR%uU$+2p!R-+WAs9L}fd@Y)`nubzFyFQ?&c*ECqMb!Yx^pSQ#9 zxXA*;G547u-3-diB7bOu^7hxix^G0rNgoTMI(C`X&__q27mHPW7s8p0*K^Uod1al@ zGQie#h}U#LtD0`-W(E)rW0pR-wQf$tG_vlwsu%OEFjxy1p(31yS9_(zoILsYF}U!X z4`=^m+kZ3g_oqf*Y2%w5NEu7G0BXY|WBE&xH;1dOGHPInia#YkzxLda_z5wattLr+ zE>m7Nl&r*hMtHgLVMjt9N`>ZIV@9rzTV)UYM1K&mozvdiX2G70(+Zk|LES&l1ZT^NrC$h79)3oMqh%gHMh?pt>gWz+5??h+4K5=2KTxApd?X~#hcA+a9{<$F>RsV=23Omqp~+nIu5Iq zP){V}Ao1>flaAaKbw@lru%?Ee`++UU3cunetB>1bp{>xdXfT z@N$Aye#+-dD4~^}C{QN9-(a;|E5d6zLincdrrYX`T2PB|P>4LjrqwAw2W#JB;8GbdzU0CtbfE;x>pF2ft;Q`W>(mK zq2_hF8+7Ap7`l~OzJ5Xw3E?BoD8cAAA~tbGOMhJGn#7-3{qN`y+WA{)kv5$UjJD$h z{$bWB;?!(Lx5Zph7Y6$&JT?>Jj=*)mKrmQ=yK5S#M9QsvOwm54KJgYuVa}D=BB0N* zt1VoqBNJ)ac_;?=g84->B5C*%HHIs%Dj<0-?(=l!>GZxb%=sGlR?+$W7M16uLeOKt z0dT};if}WP9q%grL`A!r-XFXXymlQt!K-Z(o%>2!%cINIOaTL6+tv{r(S)>FR#Nv#<$#e@l2ajIdcPOy3m@=Y&+F#uJCH?m2rh&U>+NNH$ z(=uF(M{~^5vAfG;sZZyS&;>knndlo6G$D3szw61F5xl}gI(=a7eW7B&Q=wh>%*&)9 zCMxg#`lgx%ZIQe&U$!VbT#;5(;w@=Jb184obcr{tBi`;$&#MWfyVKnI-;bqpF7 zc1A3Zf_i#pL)E_NaLpzEtn`_qQ+%0iG82qae{i`$@KRgajjJ(@=Gc?!w;Or7U+@oQ+ zP%;>(K*!$tfD%qx$%VgUC52r6y=N&z1ZpaE;;enB-F99V4WVXg-*2j!yi2lR&{e{n{O;KWcv_45!{riUujs#2C zq1kn`q{xSd*C>mx+~1k_$`S6S$9SdX^uofrM~(tD^#s-jPH;^`M!lVaS*A>oI9g?R z8lj+ZSyzT!IIIWn3;bx;4X5DAi|IA(U|frl502?u`}O*vEH%jxoFH8zxBDJNXJ0Ha zZ)Di3ueQ5GPUxEK*Q_Z5<9QKkZ7~S zbIbVsq(^o;1#?v}eYQkzeYkv{pFryl9Tsc~jc=k9Y&8(kK755Ev^v(=oDcim@2PkA zNCEV|=3xqze`BTh4v-KK^8@B)^N_<{^DUd*Caa9Td_%$*)#FxvKuPn+Ig`+aJv=}U_b9dBb$}#gY z@{|%GHN_(iS0h!I`;96$>1Q^mTtIVjFFNHeV-9Ey9DGcyo)a`z;8tL%5W>Z>ruq=?<9NA+h>ORBq;+1h?y!n)-ZY=S^y5<_{}o0| z)EU6gOE@Qo{n44_yXk2x#Chin<~$q}&$VsGts}<6l4H&#RI7m|riQ?y0RU$#$s{YA zK&;NRY$<%|3>2eUZB5u`AH4OyOXEM!;P_!}ZS1!Dm;Ytq{`tW#R`2(BQ75#ulgL|s z#ecen{rlbSV*m!(-n~C6*Z!~HB2NqyNf;KgZF=9stmx%Da@W{tFWSZ$tk3R})}Y<2W8?|NG59KN**Af%u`8JA41< z;0FO5r<}&@Q~zr<=F$LzfXLy2W&b?nzxjCoeE@Fi|8M1g-=_cfw(|HTljOfLrS=C7 zc$$A3*;sd7`uW!aXV%KKfXj8#oZoZ8|0S-X_ea|$*fmU_j`S@4BG22Flqm5oO&Qp@ z#*%}6<=3^E0?$IU3BPV6z}QQkAIa`))qu>@qaT02gtBqV%=sRM`5}qMRd#<3JJ_Ip z_2Hh?Gn6QX%KTINt+wd^2&4@b{aZZHTjI~r4{?7DE`B}wnwER%ir~W|QlMWQpizn1 zKK0LCDW>m{x`v+CTLo|*^rL()+`4l8S0Qfh9i77jpQb+tS|As$x-|CwqEP%ccRc^{ zLapv~{Axk)=IUoSpyY0F15lXH5gGTshrfT!`EUhCsL*Fss0Crr;)eIH%Z<{$&LNW$ z4>z-n49OU`{zP{CB5CDj_ov?&=t8h{dg}BKKw;2k5}zUZMYo2 z*YmKEBJ1P@%If_7=rc#38efi2Z2lD!GurvM-^$AHb1tN+VW{|tvszhI<*`$3{^_%Y z&Zh?m1RmI=`b)iw();qpb&Ybay3+z!C zC7yB_lK5_o>@ct1`73p@1~}k0i}52Z-a)K@<`TI*e?1_lIKL>XusbIT7B-b{yug}? z0weDRzyyWgH;Mc@n+G=_T2L0D;q9;#jYtvM2l}i2h z3I6zQz7@coQn>Z(t?K@N8uZV1exd-(t@u^1kRScN|9tCkQ0<6~ZTN$!8|I>tNHHnHM`G0V={KvWdLJQ-P zEgPn2`LgFJFg6u_LvMO!0E{zY^kcVc}#bm^-YD3WCoIvP5yh<^X5>dTj@M|#F~ z;kU*e?`s?q{sjg6SCZZYh|PknQg)0!N7mC>s?L!9z2_fXgh=ccc~;mf<*{2g@%oqj z52FSJ-A@=^+PgcYVx;htuTOyY_PUGTE>GN$$$sE*I4bIp?h5#c!UJvn71F)!8@G7N zOs3(r-F{?C1iKb*jX;KA;a$b{M4Ebfngz;+Y*tS0p%FbnXZDHwsNSUzWrZ+b zyq1c3XEgsmj&Ru65MZP!b>9Ku??j0X*nLUEOgdFnMZJBYl)5dKCwpU-dnfN+dhNu- z5oNb7ai7UjquPa$$||uDIHYujGLyX+#hmZ>MP>eSsI;$h-s!XKe>k>XYahKgN?f>ko?cwlJ$loX?K47Ro;N4jg1Xc zE&rH@YYmC)-f&DpO2*IXqW!67vR+3nWE7BI3NwW$>t%FJMon~d(;5ajnWF}+$0&R+ zdi=ZOi9WO2{MIKTixs_QXZVEid;j4@)&P-_3oPWpqTtFNaYn#_c#%ab-@Ux|#? zb}!rsc)w3VZahR?Q79{JdolkV713)YKd_{=WuhPUX83Dj>#NekT)lp^I)XPYW_`6!K0#}N75Nb}COp%HiSVck zC)iqk?Dq@3;VSSSawe;A$C+MTtYBvCj9ul7LzvboKt6KYsige$g_7wY`3b6S3ca3w0 zj{jXCwVg43D2@yX*qPHf=V%xHY@{Z*Fnthf9qi>bKv?Fb*GRB7-6)^TW@;_#H~1;B zMD{ZV%?QRvJM;31GP`$3vPOemfp$43~G9UH;0x($OM%qgu}|0_%KHhDkTTAwfV z0juTA9Mw0eud(s#r$dvq&{>Oug);u=^IW4V)#Lz6dN#j=zmW#bT$(L`Qt9ec3vlqZ zmM2TtOUO3(eZB8g>uIK;0*s0%G+46rxr zMrvHhw^{DYcri7CvBC>#JG;M~z-A}566nQ@Y7xS_+PX0IY9xIlFdpK#{sBzSo06M}pw=;@e6Z4$Q^>K0j}rm~;p}=9>fQC_z=gyY zb8Us1{PZY|)zr&Ab1(G*B(w_7{sgi8_vx17M!Xn zS<9*8>wUCX@9x5`5=m2>jVzr|_6Z^b4Nb=~kWM5A+2Ik)+;+(-4Dav=bCFZBrdA8L zE{?1u)U-v=U&j*oEgh0@9eFs3^XI=5uYGXw`7D%3g@BQAA-`IEJ2SlpQbwIk{AePD^^;0hc=ae4%C^cyEu|VXp$9IGLn1KpFp%Sl+0pdc)Q|%kr;)G2 zX1dh|Pw!7lE;DE{=TstdI`p?W{YU5(bG&D}hPk9sD?DsrPIFaIBB)!79X?7PFnkV0 zccm$|GZXZ*<|=%N8hcZ`6LwAqGWjBW&I*5_BeWO=VYF<^>dQ0Y6Opt@NkWhKcAw5l zctAJHFP+in7~X6!KOYETO({=$l-aiTf8jeR0X~%#iU)e{^ZY)85lF1!u%w zYSHWstGNf0u4_12o^_m#oJq|gjC83Uqc1)dy;t|kYF8dLwy>{l&_52~th>!^(<{6* zETtW5^Ps$74CM9nXFi-P%Cdmwgu?eLo=tH1yL73!p5?}Yyaou(luJtGvidOuvhkG70iV(j+A)l{mDtO~Tm7}EM>eU(# zviHaJnG6w3Gj%WIJ(YE{j`Z#R9>R?8+K9VK$2`*uwk6eo%g0LBK;3l>)8((S!iUNs zZzGpTp34^Z!++Vm@6sSBL)(bcyftT{3LZvTA7+^3qQ(j$p%Z%?XJr^`SD7IH1e^rx zTP$UmO}k7-XKq$AEa}UqNBI2+kj#kDkL-n`kclO|(f9?m(Q!ZXVoYiXI;{NG7qU| zU?^@08m^BQ%|j0w8|XbYQW-tcOH(H)q-Rm75Y4#q6L-(nfEuW-a>r8BLcN!RL+W5>)h&>hB&AI+vBB@Gc z!3#(c$QO$0=op59$dY9<;|$8Wwl)?#=&ab)2m>zeo}4}#b{kK}tdleu_A{>DhJ-u` zE7eLy(orpDlQShh(SdI!6Y^CZvbkhg4a2ZneHejiUxG_lg+gL8g0jL^;)BiF%HRbR z=j|$J3vh?vkGVdqx_gt&R*l%0DO&7ix%Xw#WJ4GhT6iE|`sg)>zmh51hyQTaFv=}2 zj`nUW|9WiI7rvAeD0qd>$6Z>Y`#zSt4nr+|XtExeZDEdJ3%T;KlEfH#+;*fxRJntY z7XW7n=31fCYfDN9+prRZX&7aJT)f|ZmuMby++b=Y~Esdi*4$U>R9aQi@`X6WOOX}T)tO_{#%a$V1V#_`SG>> zf^k^eN6T-&KFKYhg^yUfRhb!Se6(lId1gSLMFM&$W31%Hk6TpM4GvNU_%w=M;LVFj zajfa{NOjaYt>U@O1|=b6Jx*b-Do>u3Vs8Yreru=~Sl%6kXsqoHg%v*{_%RWIQ|)aE zSy`kzE7ZrQysaAug!RQRf`>$eu5><7gWs3{V>3r3(VMAZYZI{q=}Pv!bPczPun}13@(YOg z)CG@a9?e1f)kVF$c?3N_o(g^N9Pz|;*)DiWa&&V&vB9S@s&HRBnRnfDw?#MKBf%38{7OSGPzzlrn&B*!wKfMGJpztdP}+j zJh;TcjMz?bk%bk6t+Z-AmWDIuPD036DgNyBj$@<0qoG4D8cFe;YNL}0%z!j&;)k!u z6p7F~>dr0(?St9;+^@tjv-wr69+=8G=?l9qP$=P4gI+7)YK4bU!w ziaFu9b`ZuKg+OK+VQxyRkkR;XQ|gc$Z~K2pN}Da&@jmQZ(=NgS1lGAQSkml0_U>cE zO>73m0L>;`EarxT-O+O8@>$?$19mDY2|13)T;Ty>B(Qf;k$%`Eey(>mPnG6r$N;W) zX{XG#*gpEDK+oH{8?0I*k%B~M_J+>Tme`P)21E+(n*Th2*rsv#Y2_|54rNPdYcCG+ z4B_O%n$e=AVB_L`TAy;|R8$VmFh0dv01a7*b7^F!+Vkt%ddrv!$KUqF>}$BuegJK` zUlAo)c)rNKQYH|l^mjjkv1>G(D?dIVq57(L=m$bTt0lo9tSgur*4=mNTU8f<#!C!> z!DcW%K|uri_U`>K1HN@@X=Z}^RaelvBN0sKXfU2Yq59Ng9r?QvoHv+bMQ!dnI4Jc3 zHu47d=<^?D(5ZWkvXP5-nz8EP6YuXhYMl~k&{E7nfSPwX1FddnFGODmoUYQ-tQ*D9 z>mJ|eRU(_DRlY-H1ib5)G+NqTxM#XM^PL{1(bL9v@bvw+|2}>{p0rcAR`j)l$vAIH zMJ***_uf6qf*YK~qqXTrf1Xlze7b(27#6@cX|iAO5dxVqN5U2o0Btwtz$95pu3lRB zdckx&s5kqve?mWWTThuoPJ1%!t;Lk?l`EtCnN?@Mvx_H~+$=Oz>QGF|1#5tq;iP?@ zP1Z2XbRx7nCe-SOX_=M2LqI0n!%hrX-!5}}@j@QhR1jF+t!GB`31_`W}ZMma*G$B zBNjX-LpeeG`j{Db*l1+sgxjP25&m(`nF0Hes8Pmzrxm$0&f|?~)#1un7JvbfpXXmen+sC@gw|`q$M#XWmMW zWdR6bU*V`)a{Wil7k>2Hd9h5Jguc36nzRA4SDK;EC9D8D(YH;8#)0<0+(aCF&+)_U zC3e7y*|^GZjyvwB5p|B2{UX84}H)%p}fGAU^cI_OG4DrwNth~b{hpg zY|8gzm^|+A1yAAw%Y5Ymj@NO=7sw4-SZM75eeX+&5rF)h=iqj?kqZLuXcr;|bU{sq zd&+R0lfY%|ajBJdxkF^d-<>YT7SVll<6sj(Y>E3RA|f+#SS;Yv5g$2dEV8e{dt@}x zNOR6mW#W(gt1|@@<%V^G&9MPYP84!URFv~jIkV8wc0n_25=F6hlYRLt&@ zZ98(2VJ4J)zVe6=P^Y52V_-W-$Ls{>#S~Ok6;5s+-4n1tQOY~Mox5t3yxh~A@WJKm z1+zxZ~S&XS(wyD>_m+Y;|5p<9F*_4Wu@s59!nzGUX2P z2n@KRSe(jN)e>HU5Q^!Dji#ZZNmkdntF-`kz&30k#E8PuM@}L?_*0s%r`BM>9s%7& z%B+tlD4hsn-m{CGyMb^jXz-qRpVDGogeVmF5=F8ZTb~&@2h@CFPG^ZDR*TI%U&pM2 z&;xR(^O11G_Z#;&uJF++Mgzdac31tXHobo~|(z|ZV zkkXZ*HaS-4?3~!V!;{(A==8!3*4|fAz^TB$msy+c8<$Wx5yn4no)tW_#EV0{Kf)$vfg473N zDXOIG+|wTY)6%^aBZPF~+KVt!0pf?CbUWy`K&C?20YqI&5<1*Px}gGHLo=*A&E01M zZ5j)!*+XPI*m^O{HH`TcK&En7-dEr%$C2VFPt1`hl<1V1O+ZWEoitje`jqQJ_s=sw ze8Ne?sQ+%kmf0rAvn#x_9DgC=tjF$~oNm{5AMUZ`P-_io*(WS~h=ht!>=CAEmPdi% zhrxYE%wuS`%3nt*>c54Sobkx;HX6r{sSpDT36+Q%#g+oYg>Db(^&ajf&ug&d*R~G`op~D)J zcm$!f%{CGaNt>2#zndS)dK1iAeY0+XJ6#a|A0D9|o-I2Bi6F5URO;%8FL7`Vj*dJg z&TWOUq=n)$;X)XrU<*+%EW?!d!q=y38HJEhW-x>qS6Kt8Usy?_ni**=_%uwm`ve}0 zDlh9E3nWy-R%L9xhE}}YGA33G2juQY_xjZ)^B=~x%IY?mX;pz4Lkvsrm#2RTm|za? z-)V+D$!Wpwv=C%mE~xIY{g+RFP)1*H_GJ_@t-`5^gTB~MQ-x~_WNmKunTgM!G(_Ca zO$R)xkS501?{xk0n|Cu|taTM?k#)17hMhEKwsAWFh8d5juu3Np3Q>ln2=F$ny3W9x z$-=$lk6_(xo~cf>TcAGG5qOyv`48Mm96PMrpn-8PD6?#I{zhP2ICXMvoOC~cGIIg{q>6G7Km(zX>fQfQW=Pg|*;>}s{5 za6LWHv938ofD@$bH8@Uz&9bX{Gja@d`qF*bu~qydmA(mx6n1BM0m?MN^1x3@3<%DW zC7x_Y<~Zm0+;Pfh@n}4EKFfVnigtBD67V*M?dZE(kP^POz1Oe7b2K;;QxM{`2Z^(g z`;LD6SKIO6!Bfn{`rh6Q6KY?+kjb~85gdW4ZV+JHxRR_^UlV3F;D7rlDM^@u827{| zoYNHN^qmVe?S8oMofEM9vk7L?+-OREK5PW!hT-fRO0Vz=rJ~4yt}*MviAZJN@RL4Ip|gN_okio2lC3>Yx?fXa@fOe!-R67VfSbR3 zd`zgOUZ_n*j6Jzq#xicAEtQJ>TX6n-LY^?*hysfauEeZ%25)5l<^fyjGXK{$mluJ(VJNRQG?#mzi7-LfSPdwS2WCk zV{@U>dwJO;I>N`jYYcTd)fDjmfDRY9fm1I?<%2P%sMbP^XHph8|I_*ZIH7U^Vt?k8 zl^JLQ4o^XdSHze%v0_LA6IeUq+#NwMUNOW*yO%vU-w#m&dl>gIE?R%nWUFli%BGYrhTV##`{|a_Lz=_tqMX1l zyCs8V?`t1x8o(A>8j&+>&?N%qE^9=nDc~ddD>q~bbF3JnT0--Zeh9gs`%xSPiA@8= z`8-Jgu?}#RNu`3PimgjsZ;WH_BKNXwvmgI~{JSXXIk^p*U%{1p>(^CeDRKy`hzbdP zws(TV3nHtbcoM*xVK>$V%ySW>g985%oXUUon})d*q|HO_7s_H}vwkbky!?Esfl{iB z4Jpk!qXF_f-tnuX`L3ca05h$P&+%l4Xb>ZibFz%oYs$+-&=g4G0Hf z-R+RB{9ju(r)(B;y0j<<)}Qn@mZ(vHjiT$VnNl%z`dL-EIs z|8!~H=djkn9;NDQe0gxp)HcPu>)MK6>JgaPXVsb}@k`{_)zix-HL7r$oyXOHh+m3= znSSdk=xH{g>3+Oo-tArQ$eqO|obfA1j_l_iq?Zjb%&dIm+dFY{e@rHH2CuCz^TaO` zDmT=sm!uL2v4Pb6w_gOxZyT+IM}qBc$@G8E zyo=nDuRgbLAJ;TC-Cy=h_;^zP9^jM(%r^xelVNE}X$C!HOr@2VgSbV-!aA~t1LWn& zIIW3fRFz8UgU2%J=nCeecFUR*ZkU{kQ3H9X6KrCg>ZzCXKJaPCQ_kg?)^sJWHIPkm zMUiFWdYPGFe0$`#*H=fAEF+K<|9N4|G6h`)c*K=Xz~G)8@%;}NSbTU3Ki8~&fnC>q zBZml7e|h0yeN&M@0eNX#w5`;gJZfm_(x}w?2rJ5~s0g-i!({QmJsV>)BH`%gyU#8+ z=&>+Ol(@_Bo}s~7rD4zOVH3=@B^QH{xx^5=+SWX8BU_0v31*n#uqF*xxg>5ywfcF2 zvakT#*h=3B{RA3CJd7Bras~UW*cwr;@9TI>aYwayv>)W`#sHSG#-Gz5P zkj8)(;nd~EjoKk{YWb@Ec0-LH9=Oew<^w2W-z~-75V9uDrRmI8^P5 z3P|jy`F^-Z7zp*F{NEh>`+^ zE_K|N>vB3|#0o9ijwgBG?%fVMB(i#Gk?h3#h8djJ-OuP*?L4D(R@u%%j9<>w_vD^S zDC<%g3MN^=imUxpjp)v6(uV{z4;zuzbAG!8Kb*ag&c7*b<2z+(&mYtm+RY7CFb(OA z7%)>#xQq!v34$%^k3-aahtb$B>)`9dd4az>SYBTjw#Q51HXN*O)cC&PtPd(qjGT9J zIp>`jMVMLGUE>(5O32#%WnoV$=By?nMo2>i9*BtdAtyL9 z7JOmHl;Y-{TlbJ#eySapt=y!%(#!V0h`ju&sF-p4$$b|_cE?JXOZPRGKXWcITIPTr zEmUrsaJwDT+Pf_o@KD8Io z_YR+HS4~3PeMUNx7g#X9vp|Cbb8Df2_^ww%xa?}G>GJO5oK4N}hf1QMrVe7w6b5T? zo<&0Ir5lmN(qB{pA&y9^*N~&iY0rm8xig`(>*vcO`n*Pl(#PUEe@1HFZMEqg5z49m z<%pVFX*YGs<%63B)T50uecl!EUfNdnY%Rx|#jwKiSu~9RwoHT9bh_Z%*bW-Ui%8>g z_ik})Ukrg3o1@RYIn+~fGs27gCbrvy7a5TO#T=6M9d2fq(JCC_9&qUNX~~XbS2^SP z$BpHVepVbsj3k3*vS}5b%BS!26|d6nmV2#4-qzdbn&Lv9J8(Pd~ll?^^Ll!W#?*#(V0pt`FZha zwHTQ9P|X{UVcLrqcwExQR#N-V#Ur=!$A*Ovm|qtE!p^C4xO|aefD&TW8hYESVCDkx z_Y3o+WgY5N90YfQuwAjG`bN5=exA(nE*>AwVGfH#*M1 z^UOJOzPumaFaEa4CTp#G-EH0FTK!&1O|MxGu(KA>4$#UAgkXFf{9VGZeeX@KUL0jIelUR z{#kFzZQ#6T7TKC4*2ZKyCZ$bNy>U$UZ9F@`ABJt)@t~;^g&jtVm;cy_W3KfY#ztmr z)!Ai|>LQ?X>@#;7h2~tZd~Cu*9^q)eT^jpkPO&tWTJDbt@9NIx^SZDuq8L&~Ro~!> zuEU9oh0MT34AK%oO6@Akm04)mlAWe7*`ahizf~x?p#ny?Gq;TM5N|{_`EHVfi_TPj zQRX4c1Q#R-N9b{QV7IG2Mp}LBB8>$VX2Wu`Umpw(woUV^kFaPb)wP0ilC@UA(Wh(dTj#}VZU zq5kn?>sXyh9!tDy&Z)L@KOBsSqawNK-M?Gy*n5$lv#i=p^A1v!tio?z4f0Tm+z0C7 z*823q!_)Dp&nt%?Al5HKJjz!LgR3eQC5?F0=IIH7E4QmdH&kd-v|OT)ztber?qyUN z`_l`Bs_$0zUa)jH2d$;AKA;k7KgV*7Q*NV1+Lwjrhz)6(Q>BJDT2Fj~_?6!Ip62nu zMe;0L-3Dz$zLFaEJj(i#i}oDI4G1E=KrtRdI$MP)}c% z;eA^pHPCAwcQelb_0pdqDZb%mU*yOPf}XYC$Eu1lNUh%@(ZbhedLU|W#ky)z#lh09 zzdK{1jC`GbrIsFDj`DmS2jwXC7mUHUF70Eq*3=|t(B6wFLo*0H??};KsCM8r=DHTM z`4uTA@Vh>2(6oEIx9sTAkroZXohP?kJZK}Js0istn;SG4%`Xdy!{*CTj=`vskyYB@ zQQRb_%(P@BaY^*poyfRLT$>p@O`Orc%Dcl^pHs5gx`3Taj#TK-{C1LtIz=l#y|Zn4|`yun}v;@pG($f{6vF z>A7*O-%YXFXMQWzJy;itsfMMfD0d29k1ofH7}Bjq{*2F+ZH=+e2ue71GCUeKQG6>b%kc+j=5tM&Cgq@B2E4SjQJ$8}_3zP?t`K}1G*Jj@a6JyW+%Jy9iT7}o&7Lr}>p{u=GL@1N zu=SFDoP2V4o*NxEU`DSl7${RZn*{g6btpg^;TB^iuWpX$n&Dx=kxZQPHI_k)%^p)a zx*+&Lcr&_pzi$qLS~l2`BbF0bxVh(T)CMz5K4)2>Qc%rb%fKIe}@;m#+7e9etrgImKaqk7NEb} zL&Hp4zX*KjNKUl8&!JG3qL0b`-37n}yd9U!Ul?#pj5l;o#h#SsL8_J!9YIGOX}2E* zd{Wmxc!pEEoNXO<6ju6Lxyol$$Z;;IM1B_BIP-exQ~IoGR+gRv+MuRJBoQTgC5QJn z4!VV*A&2*QnlCIM;ZTR$w0GLvobs7^M_Ls!dtjYmukKhajJ{pJV$TPuP|z2+F5;rD zUUfnUZ8K(u*ERMWD)>Cq;Jr%bdf55r_!wXSt6a^+kKD9zff81U;<`gr7#*wnSZ+A~8?dM#wU3j*aJLmJ^=0;vhW9WTdgv?i^M) z3^K1Wto(EnDl12Nk^p+?*F~!sl^Na=9w7G$EVTXU4w~`JL`8^Mo0f;L^!e}D6USdP zGzSxy^n~s|@|1)01db?_^T*MIZk~w3i4i5Q8iHk7<_b%LHZDI0Vwe?I9+qX_60^`D z&!6-Kr&+4{JUYbou}kPxV9=RjWaUw^PqMc9 z;dkp@mRQGPhg#fb$*8g%JjJC~!^m9PSr9$=ey(_Z4*Q%(W*|UB6j@%;IUrh^4ZqDF zrttdG4ANL>`VUM_1Ou7ZumpMesM(Q z^O!4xto!r??lUzMIDy6Dsg>^#?mUo zkO+zHu#wdBvkfnv6(Xp%*(cq~ouTUTHyThpO(@^3yCND4 zjjXClF(AD`6Gis~;XMc?0>au4T4Z33Hqd{(Z+*U@XVX$=VZAZ8`Cwq;rl1iM=JPqF zIM~8`)Sv16I;1A-%iZvf`OO;hfDwy%!UrGWn#~{U_1j*r`R2Z!oq81TLabT&O4`{~ z5agjODLer@Q5R-p$kk7Ks(9!%k7(tM(E>I~&p+*BvucOJ}UTsnFJ;gy6)+p?I@AJ9qb0FS==GP4eVVl$f z&zTHo8&b}f#xe(*-T?BH567|L0R7f%0$u!2lxPB!BVq7Ejje}q@VvI*_(g&xUW?3w ztG4ctU-lSXq*%>iok+Km^2i#II2HOY99PPoco^m-jA+*cO_mims3mW>^)A3s;m<@6 zNo_wIOQ01I-kw&JwNoaIL|L|2?j5^7BX^+L>DDvU`x^{!P3#997MH$hMli+-qOI2k}h@wJYSDB1ybBRreddGL;a00&+ zgDxvu8~g%fP7ZB*eaZ6A#Qsgdr!4nHxrp!ZDG!)xwM6aZQeVUr5i@x*-$9&Z8H9^9 z?NzxYLaD`PRWBS)+_JM|;Z^4@`uu?9sn-@_^2;lXYQfj5 z@{w?x_Ofu*#;WC4E1Fy?wwx?V zNlQ7hh*Oqc@lTHCR7Q=RQjqAq#p&eojBn+Im{%FlvsJvtM- zCkv4v^u?*LAa*3V`CY3mQM=7VUE5jLmY5lO6O!=q_-!%)hBdnNnc65_wYvv{n=d_u1 zO9-?vPu1^ZCbu$)%x`@%we~QSWTJ z`#M)>KLO?~A=*+XB=4e%=))12*Negob5AkW0(oI>W;JxmgUR96WiQo^wSC~;#}`S@ z-aXJ*11&4Ghgor>&NVp^HoQXYwMAE!dFi;c>g@h-gu`D|blcnb0to88?A&K6FpLf{9;i!X&96)+`)Af{A+8<{ z2~W(RO%@l!oj?Hl_dEewmmHUeb}0AAM;w4ISOsY;?Vw)jNg!e*yRZAsPbDQ9;n#`! zwL8B2+f~{56xjfX8TrUFmYs9o;nw{1ZkZU+ZB*uz?f)Mc@Ba)2M`&0Blkoff-R`W< zf4v4Y!~rFT;6Yi##($e+ZUwOXhHH2Kn#(`U5BMyyY`f8OF0FW1`|3Z(Aq4(y3+J}UqkC=tzpkYWpm$kzAOV1__}@qO1IU9r zd+NXQwL9jwy%tk5fZk@^DwG}l0RNE;`TpPkFW52iRfFPxa%0b4)}s}qIAw*4k%zg)DN80meg zVsQxVH2=12#sM4J_!VY!eivDDAFytEe4yz+K~?>cCV4Z@6!aUfx!+NHk5lYr@r{wr zCvEbPl7<+^A@5u%@uzMD9R@eD=De_TduU%AyF$5gJ|1^Hfj3oKP4jBEkoDfK9Ua-` zu;6Y^x$iNTALL8tc^G7BF#m+%U6wl>JU_cX*0DZs{Eouq+oR!!1Nb=w;&RcQM1FHTwM(v#R_uHeq-bk$MGV+V;^ZmjMEA_Kz%hUX?H@ zK=$`<(qZW_FDyHTYA=U$jGvgr$tGG!cUjcv^5VP~%Ds}6iCPt0|{knIt&eXi^89w7wiYrKbjm50L`UM9VAlEQz1jjxVvh9*)x%g*u2 zxwpgiDf`5|7&{YaHW3ndj5lNQqs9Dh0lD>7CTmY2b1$e*C$Hv#w) zZ`5~5OqW|tmnR`7KcbBjYtFRDB~g`};U-x7=iA+v7ajvDtR_=*?)QIFVS+$HkZ{lY z@b_N8e|lRZG_sXn%2y%$>)Z9??*U92K`hwK%xedF{WpD|7<=RawN0e zxZ6X%zynaB8W&nHay+x6lRl$M1`3yIB63~n*{56qfCJv`9eZ|EUv~G7nICNW%2}DP z_uh&OATv1u9#ba~)`60uPm3pm7?w<%^s?gL2{wg7=l!#m`vTn^AoJ<(j*h*pNm?9~ z=%`R=Om4vIjtke`C>qkS05;aVeb=YkCs%Jz|vb#FO@lnLVkeN@qALkgt z52MT0!)3meA+&rraH#9U9YwL-x?f{>-!eD#c|ynqBtf@ZrBzamc|QP0Bt1)Yy8DV1 z?bt~mpdN78p6K5C#Gio2ncdN1;h@&W>Fgp;F!lr*FdB7nk<-t!Jbp^W}Jj zG|fV`ylqt`gR{<=#nITYJE=6W}6BE%3qebrNw7i7SoZ9d&^=4{hc#0uCI`I%mVUW;~?>x&eb(q&|-JB zI*Cm2&}M|FhhqiXUVa!o@+G>oVQZD{*~y@LfwU5zgG^|jQ@z4Bt!E}D!bux@N=S~4 z&2CvB{Y^?bJ-WiYNFOVE5BCjDalzVF@d2oqqUlz~F`U7ViFwh!jC-2`GIOJS+uOe@*A zr!h#b23J0@8LD8*4|)gu!>;CB_5w#NJ}!cv z07e{;t3L3yIT+4+tHGQ{;Acf(pAst7t4Bc1P$()63n;5l$Jk?`J`jm^< zX;nKzx5l;Gm6ss${Xrf~nH<9T@|4G2=&on1taFi5IjvRGPntXDeq{{Ts3U`7CvRt; zq9H=dl=N#6wCfc~UovBhaLPByl#jz|V5hdernAZ^miY8ZN%8>9;gdkA>Yc_`>P=jN z$%n3hgB?p~7q!{V$IQ@IQ9%)wXB&>MP# z-FkzYHZ|Tgo5S=`QocR@jZ)BX<%+f2$H4~c*dhNvedI#=c+S1~V=gxp7%8z3Al# zgrO^GZ>!~pYoKV_xG9%et5En#u42ejC?@bOEVeeRZ?A>=g-@Qffl0+GcUp+j7F^z~ z1@^;%$fi3&;lqoU!G_HeQvn=2_2&LmvyiTe(;PFzHB+ODP5zHkY~oc1%+(*}Y1D2= z4hIY*Z&Is91fUsM^}LK^xQal7#hL@0TKXLOcsxug9$p)AZz^i*Al(eINyVZpB&%HM z?i{%%N+o=yWdq2Q@Zbmp4bStR;6v>St#fL$!GuEbu=aT&s&~Cot6%o~okUfJx zZ|p%>Gf+8;M6S9I7UUNbF6v{cR{e~vqy$#txME5v*3*&5ID**5Etru4{DcudwGz%< z9Mntg_9uTTUrYb1p7FmKgTs`Y-}UBY1G79rX{uhp#O4n-ilMUeT&E zTd(;o9%Ou)tU9cd_Iz<`wm$XT=~hTL2J3iHu8+> zm_?R{mT4L5vKi>6>JCwb_Q=6`1Kn@W>1X zIT}`$gX~>&&9YwNlffxv;5RjZ3m-{Q9{?gj>gO!ovca}*?p`e=H+s@p^x^pGB4$uV zWdnq_l9vEr-(_vL9$I?FZ^J$PZ3|x*w@@vg7qf)Ph|IF&3SDDxu}e4mI%NNuj^VB_DDxWFRt&n+i5><5MHD zEVjTdu!50b%cpJFX|v;V_fC}{t{N`Di`M$VI^wd{~6rLlDLTN6;^0ub~fWKBN+?`{XQK53__wUK=`P^^e8 zerMcQc|j;2z4 z>4>x(GWUgTxcZgJN}!r}Ld}DIil|U(6`nQc5~&)@U(}4jPj$XwE#>2`6)8jF^?2^0 z#@u=}8j}DJs}Z&VbL$k>acah1-} zN!(I*IpNXoeSZ^KWANe|K+o9EEM^s~yF7$Fk)|wU)t9m9acVsSSmxflMA{D1 zAvxo8)eObFfw6u`yD`Y6zj!FkirVTdZQkPVa>bojcR=haN3HADqL_}b(GK`v)*W*FFADKAm5D19zwj} z0#)F)MsExPCG;gM+a?|FcXtIdAVmPsn+`0e11Aa_<$MI&kHg8gsE%`9L{#?5)7xB^ zAMZCy6v{NGyvkF(E#HRdwc#{@6 z%})=d&s!Ct4VFiAAAM6mhrXPe)c4sCW2Vx;i(?= zLP=2vggnZvI9rdM%L<3~NBeXLOnpruni*~#`{2ZJVJvT{uz+s6{$4l+XY0AdcfXci zL@>o$NVBQKHYuqwEb|4_w0JjCC4%}A00vV0LjA1Nt)uA)ul+om{)JwRawUCHtAsL< zzmnXD#TLaYr;jx{F~X-|oj6&OaQgh{4BK%@+wx-n!@eI*h3}oSwy>n%Z-%e`6ajLA zL&4^uA37F^{z(+uS}=9%Vfj%H^ArOEc;|Zf9;5~76(V88M^3*Zad~9nZt%CfXio=g zH0I+K!fs8qU8u(iI7P<&LSy6`e5uv>gr0~M5mTLu8fe1t@-I?m{XF#5bCx;$H_?N{rX zh$v|t{3BLz9n8YU=)-a3-46Ej9uu))Wf%Cu@8gO+0TNBc>x{XkS%xE>Gd!dHX2bMA zV?uco%-%KlwxMzbYIqIphXF1(Ka=RIjzAG-_JUElFQIPJ2*87srQatXFZX{C=@&k% za4{M|Ho%)nZ>dMqh6ZVONiImmS$mSIejIehKegHh^>H$tI}kM3%#)UuKUP*X*n}<*yA{c5Q{wYCf?6kf3hhH{ z)60yKI5VQeUv*iXK6^#ShF8&6Nb8NGBAr)xT+rDAZEbSmlLeqFo^~(U@9U_HC=<^0 zkh3n&1Y1`YENa=x;VD-fwp=pyW1HW1t}piY<7&5Ii=DNAt?8Vp{!y*;x4r%A2?V-) zx%oM%7H#*_-EZfsnOs2g9sbql!p^}V&C5hLqmrr-nPA6qL?gNeg1sZqa%6x zlewOr-bxR&Oo#Wfdwco+7NuKwL9P1VqjUp}-oLQCn#p?Et-5pWz%d}Oi&t)E4w7Y`H^&oYnlZ&kIlv>J zTZJZ|zy}ftzF4a8rnXyU(%+qUM8Wf=XNf~6?9rW$mIj1K#WN|@i=u;{LJC_z-5Jz!*4x#|<8yvOUw~+!lroQy&4e{P{<3z4RxQIR*XO zEkysfOak~^ZZ?}#kQX0UJY=*ttT@=WN>bJBiZv3A^NM>x4)Oj6);_E>SO8$eu_ZfPqD2VE>lpV*y|$y*d8}I_%g- zQD9gTyW9VohXgp_{{oP13=CV;QuQ4=@gIryCsP89C-+YrwEMIF|1sXvt9GZ(8rrr8 z{l|vGxoXAUf@E2LN9>Z?<_46M3u?$Q9cysR!;Q!~Nx zYorVCE9V@5q{()6*_{5d-+RZ4>3lP`&8MrMd`;=PXD@xSB>iFQuHSLKsqt#y>(#_9 z%(E5G7e$BPbpfV35a7zB=6!p1=%n0xkw8zZk4MMtUs;HT>Me1CO5*nf#CM5Rq}IN- zhchUs=E;Z&YlVb*CX$$luHNp}aQ?-v>i5v$?Tgg^`b{yZ9)*REJpl?0S>a!zW{>!+ zkF3<%POJ+P=k~ueBu!md+DDa5Km2AJr};18dB-zD_Braq7#SM($<%7q(Y;Cv9Ho6V zjK3^|I~}2M6yX1i7JG}Gf8yowfJtcF&Ao|Jt zu2Y)}E2QmXPqe^@-Eyz^jqU1QjYeR3C`jt!4jw2eX}JbmdbZIs?BPBtp-^pzkkRu( zzD!~5+@axe10w}LS8Uba!V6=e}PkZV|dA{n3(fD5ZAsPx#lJ)V{PG-hbb! zwexQ@L@akAmX3JdjByeQCv0v$3ND5z(^m?=F8X;|^BT5Jil$laCOELWkrhXBV_hw> z4vX!g?ub-wcFFnhdHP44>CDTLsbvkZEI1EO!=$+YVy8mGWQUY9B#{e%)$%>Gn=1iS zQlK-20$JxFj%&?HP9|{ zudk@Cc&55d;^s$3@!A!%3Ue9g?~LO&!v|5m(oFy)#U&DdC0HgL+g#eSvE4R_lN!Do z$S0QiVTTUfmLpl9l*&(QO>rj>_<&$#Dxg>MLH*VaKpAY9$m`h>;x( zf)iElUHS!Lg-)4hFV4pgt zzg1!UrK&VqF!O`4cpjmR#Q?XZ5&8T*Wwbh#shR7gEN0N!g0W)O;%2K_ocUl8ZplRa zc~q^=s~bsvZzjb*U*fsl1TB;UHlY+<7a*i#lY{NcXzhFCB&;pAQK;=cz!g|fw3~J|Iws;zU!IRD#3aK|w z^+#3mwdk`;#X3~|$89V|+adf(CEI~rU3%Q_&VK1>1yaU3s)k4d7BkQu1)zD>$`R~o z9*i%Y>@bV8PLsIp_KJ9lG1cj&NmN-IOjcQYyk85h{pEZmR^CwV-7c8|jMOP$oU=-n zrn`(vgSK5If2VgNDLLIr`Y-rJ4-ZPZ9N86ghNgnN6CxZo5!^NUIGtzNbZqc>3`H_s z;y{L}e{W60*=CxSepv#iPbQ+%7X9gJ%9)k@D@!upa7F$iw@8oRb_LUC6(f#alJrYt z%99TnmnNu1ww}JhLUk5a{eM9x4yL)ii1}zHtqZ!87?e$r!LE3i(5g&Fs}#yXHfH91 z3^OvaCq93Zm~5t+G!4Cwom4BAr!riO%U5ADHhBm=O%KP#IV2_GrH-*D5*fvj>Mn&9YX9d^f1-6dD z__zm^EgzrZxJ*zh_o(l5EY_|-ir(>|IBsn?Y`6z+gX#OW9^ zcVoZ<5x5A~;cgMy4s*XJ62;y`8&LPhhprT@9)sXK0|LS}Yyt?{BqNS8*JzM25&UdF zyM;_2YuJefd{DlabJ<-@(@wV;Th2H)7E|~VDQB)Z(TuoJAXKFjsncO{M#F_Mkwtil z4Pz=(nU8eY$BG79shwrsFvBH+lyg)SjX3nTYy-w!r0V|HU+ENvFbp`!59fGz zqr)bI=sDdguLZvXoihhv3qwt~s2h}Qs)_s-QdLJvl4IA)JI_uz971nG{k%m@v4 zHRigdoUNZhc~jalyIs9YG?m_yjsr^kA3cUE-6K}kg*beCJg86#KoFij?PJ#k;Ga zsrJnl9>&9$bja_^Hsqgp_hDhXtNXZT^Z~Vi3AAz9b;SU}$gV3>aRF!)&N*TVz4H|+ z*)CjW@yfIr?{eRcLmtQsL{*Lae9bgtApf#wU)9_737s%IzgHV;CjO=T_w~t~%7xtj z2?25QWRLA5u(8fgC7@v!1L85=T(!0Sv}|D3iPvpkVmwZ`XA%18Gpz>Y*T@<^r&(5D zR|e7@^pVq(-&e(2%5DnoYXokh`_Rc{WHIvF%5MM0>?gpZFzdQZ*%?NOOg@n5-7dP| zR~eiI!36m&+YIK^=|fQZqI6zq^^b8+p48!+rsPa;-4jtU^MedEULJ%^u}-8W2Ue_- zc6U&wWh8Ts#)E7qZ!0O(8YQ|?bqBxxXxRFp=e}L|1Ygp3+p(oHqLBePOUw$lZhQ4$ z6{LZJ)z<6)l=3k*yZFQuE~-?AYKZ*omJt;zFHym{*PYmL*Om4ztN;WtH7Mq z;2IoN%0cYkHQ)`NDv_7+6#xe1#RT*9t1M2myZVy3y7DD#Wu(&qfK<52nRJ`}3A2@; z7<8+t5|ojWNs9+tx@-fw5mRV?kMZqkWLy6=cCjHf|8@(2C&HGv zbOP@#+gmPZ0bn-%KhBrIP z`|pcJs!%U?a{Q`ksvh2_6dv3cB2Io`sBG0Y~{owK)`}Jb`W|aCae&lWwwu|?^5kK-UaN4Jc$jAO$@GjDO zyQ?3+_7b&Yn7@zrJDz$p0kR4HF=g~`Fa8hS)qj#_o0PyUGsx(npa1nToPV^@yV>JA zwKUj(1D`*4#?{@a`2U`=Mm-SBNG6Beg6z_byF}nSS^p2IlJ75GNC6n#J4@Pix82@B z!XG;-xLq)y>3;ZMZ^D@b!lmGfrR&fg3a$jv0$3#cT27X|vbDH+(%ZtX!16Rw4*Y=6 zu{O?FD34hKQ0{9kB}+Go)IKb)sJpDj{{u=#2~FDtrc+|k?Z3)XdOq;aL2`Yz4W(1( zvz=^}+X3RqJnL5(p*n_Zz27;lA0#sB?)5evv;5&~AQ!PZ#5F@`4t-km;d678gzvn^ zWtUixgTKKTyg28Cumpubw$iUjxJ!!k4`WMowNvA_zHievWkoi)!W^<%o9b4+(zT8L zIniFJ=imGRbM@)1?9P2&o!XO^(+RWv&RIi0O4J?A?Ytbhx_fsJ1US?#95FKTbG9pK z6dSw-<#}4g_DP@TD769bj_YhIEFt1rk?r;gmZlI7N5_%%^B-$?`JMR2*|;j|LJA?? zIOR)A0j*jfD+A@piJ~&^=@Hv|fUgGkI(|L+;5IHP|9<^Z@41Z6WNnADP-&~ua{*se zg+pe33Yav@DTBNWWqbbfl2xn!&!GEKDqEp`3DZs`PE+XOQJVX-Y^fmgV&dXj+t(#R z2#zkDa0|!ue{#ws%R;&0V=KAF#?rrwJlRn@vJlLEWDc8{Jg??PGw9&2s06OCPjJS{L0MZWb zm~ycTu^$&pPYWW^5#AfL>s=Ym!b{(UV7Uh1QX92K#4ba2;>Zj#*Y)ZFYgA$s5Bu$z zlT?Bq`BO`>5H9xBf#9d^nokQHSGg#ylojsbDC2Q|)w^gm3KLZ(@{7JmngJOetq zZEYkU1_Hh5*}X-gsU&9^7cKDxK`VSsAq?f5QJBv!m~=v1|v7B@q$(gTXUQS3D!Mm$BX7gOocTJh>r(4hF z#Xbg^^0o|U)1f2Mew{eRt0$nBggg}XpR&4adNI`i3_Jg02!F`EFsTal1 za(?EQmcc(VfaM{PR zVbyg19vlk*yP#@X^e~G6$-ohXJo3|w>)6TR)&X2E^N&?~%X4ot0UK=JC;telxdLz4sU-VOV; zr$2~O+;((=?E^AlRDD>q`%8K|WLj*ZgZ)6j%gu&v#Hq6*o~_)I<+ zRmyhf>PeR1nV$jRsi%%nW#%zOKuM2kEl){%km0X{FVFa%b}mJHRQE@B#i}A}=F$d( zJ4uz#gff}T@usenc(`3bMUu=7p(!fPfwLF@sw~JZ1{X(^v0f99-w^ZA0^$ov`FGbo zJ;>auwu6H_eHOG(@*JzG4<8GOQAX32tq+Bl)qUu@k<|otEjt#=hA+w@vM|G+z`f$=Aw#2=L{!g^HF=mvVk)d^t^eOcI7Q>1Y~S z=O>?-b%AOQ71Y2#8t6+_@r04)SNXoso6#sZq{xM(%A?fQTy>(7tifVAV&5n??DyQX z87BO8buBTfmrjkfcgV?!Y#|VMzz|JIW|L-n2D$t# z$IKko$V>8n01*=9w0VXH>5d?Tq>VJeoT2TFlCdg*?iQUI!GV53H zTed3;`&UrQ6&#ytPBu5|*^S%8WMqv}U3K2*y6NT~3d)bTe|2BMzElL__AmPNmoHQF zCF6gW2DwWW6vV_Fd362Vp1#*cm`B{acfjJ6kLvTDy?)~&Zk1_VK9mxuxW(97kZtat zhSlOm-I_P)9qX_ObvG(9n+BU+Q*omv4g-p1L~V4iS(&1!a!w;+4-2cw5$+NL7X&Q{ zc;0t_TSOzG<=Me`B|XV_c0+MJxF59A8^?e8Dc9GfJ6|(M-dnGURFA&&kh&bBbHa0U zkUaE8U-op5T5bi=8wG_8BPgiqgtm~UISsDz6Aj}dLn}{2Em-v5%F4F+3&tn7^p`nY zVq9jiV>akMh~<4gmEMV3aL4Jh_C9gSIuEUXSGfLBu#r8j@zp)+U>Ex|tGmH%*>1P$ zQk8IpfI60T%gjBo}m^`N|gvY7~HBKOHD(UI) z26C=HQO%xF_Vf3pRfV%e&N16GIKD0O`+Bc0Ncm15RG4=*dcja#1u9YvylGM0D11;kg^D&Y^R`ixp71d`lH=(4R5I-G!QSHk~F zlcj1}&aI^ewHHYe141W)$}SoV>oZJFLh*GPaCf4sqx*w^(tsSkgSW(vGMk%-Y zWt{!DBgf2h(=T=zpL7+P#xRM0;*s2b+<7SXjt1`;ZPiq_f)8GJ^M{q zzAf2Kmz6i$JQYkPU}s$nRQ2>D`slAV&Uviah0e6nJTSa6%biAZ$=3A1(A?(suOnY7 zU;n=EG_9@@n+IVY2C;?ux!Xfn5x?o*gNu~?^2Rf4R@eW8$JEFAikdTHzgvL(LN#@g z;7`2-HmT*O`3|1_{7l|#b-)7E2(^ebrK>#7wzy)0UeF(|(R~QkCEu>Kbi+B?kSRiPO)#c-0jKxzepH+NpA2yYw6Cm9(F3gi7mwH(|3xsVx23VAqtVi3-#!GR=@O1xHt)j3{W)sMss16 z^QEh%{Ylq!*qfBj1Ras*g|NQ(x|IiIOHs;9L&CG+CkGAtl3nrfhK3|w^!&YG28)weq!v%0fl!HGn zfjh{RRHUh_G@V8m`r`s#*9K2htDW(anVX?zip$=|DsgSaT$5xfUhCHKqOMRo07mSs4&|XlH z^4%?-CDtKx>pkXcyZTxmIaSeb%Ub#*9oF z3f4(sviI_VBzZR`>afRshJ|AHU0mx?tzC#|FLalbla)6g*&|QUA^vK4XG*Q&6?=GJ zd^)EEMSa3gMO;HK75w=lzYwZ9pKzjb9Xu4Pt-!4-(6_M9|Da}nP6MhvKfZMA{w;M# z0?+~G=rj$k)Ibt)4P3z#iwT93V)Jo{rUk8=?(XJM8on?ORc2=y(p>`Sv_nE;_3X0b$5mhmx8JL-ajo=4WMltF0pN!;XgHOm0=`y zY-!Dw{t1~eU_l}z2@XZd9d+-W&rDAm*feUU+iF`w9=HOgXnrboHT3l zOXfhvS>jwPB~OXJb9FZ3tT|1BVByd}K9RAof~b|g>(&v+X>#OHp<(q6690p+%+GCJy@pDTnc>Yjx;ge083kf1%Xj*6OfCv(I%jJvf9>Z*%#lWkgE!Yf*v zJO}-TVs7xpOOvN7M(N(0#i7Il7i4X&>aGpIM)JHLqo$FWD9871Ap#vWL6;mUdVP65 zs66ktH4K}JzE;pmdTxW3D+7z~2q_DtFoGCV9a?$&V1r{>rH`un5-H0yLGBxEQnyLd zo?l7*rh~szCljGDB2>$HHom^~D*725LY#_T3n@{Zil&4d%i+pciu6}|XE0_V+(UoB z%8RI2Vxgfr`@3A-Y)8stL?^49lU-lKZae6#Vx3*uN?*gTPc|~zm52-v%&;Vhu7cNa z6}v!!t^JAVp|iGe9o0=;;#cPOzZqnvA53GSDyM_80-}d1Isg)g z%~&lnpW_`!j5e|6JTTr3Urg+F==KSfVCauH_&phJzS+YcD-qcJ{<$04v9?>`$*Lj7 zw%A#qOG@c-(c^Zh^qz z{vA5z!2I=GwN%3!K6AP>zoal!9z?C6I~M;qs@-gE)M+?9t3hHFCja)NFrlrgTSR!# zsmLjL-^nzVhK@qVyOTcy_k^d0;K?@O6ooS>$N>6)r@wp{HYM%e^bN;y^(~L9?V>SPm3K z^q5jCGXw{s_h*y-&$-X5`{KTM-r?_u-|^X-j$y<}UG>ih*F6AOYe(+o6gxs|mT+_1%evFeX|#SaDrB{1WWp%S{nFBt zoZ9koxhir;D_&y|(>~KU%ca!1tC!5L4_^;ZZGEnYIyBtX!Ny|`ZY>o-9CfcPekyWm z^BjIe?4=sg)grecF6F=%1qf_M5QGt@DpxvDQ^6Dl&KP9Le&OyC>9w{Fyo*2|%MT=; z*=wySgPm%QRM10F<}BjNf>n#3o_=%7+SL5E4QElXiq&CszT0Lz2%l!rw_9@}uyZ#v zsdIgIJxG>^@u9t#+!e>j4sPq@woRmP;g;|& zJG{z82H_Qq+HaJdi@8>w+n)n2w_o8_e%lPqDCy{)^E>oK*xAbhYI7p*gcvv^H@fdp zn&_`$u1}ixz{UHM+ri#^#9!{j@=v)zijQ~)uI!Kvu=fBO-0GR<3~Q>rm@V(MPwbR2 zWX_tghPGYHHb=a7hrDN*`(7BYA9aNfS#Q2bRI3MHJjl?WUh!=H&qn>|G`AGi5i(Ve zP>w5v2_xt(z4>;6J~r*s2Yn2}4I`wgpRT6H?j{S}aRdaYl+*4`m0n=dZ{x>otQ2JI z8)>jc{~Fk}mOj0MMmYD7QFLJ^gizuNQm86lEGzcii@wTQCj~40BE|MpC;(Iqb?rO% zO};Oi)s52cT0ZDQ2?z``mVNc3^~aMFGrC6?*dYeQ{#Y&z(Vmqsr`REAd6#QckmbBZ z!+WBuTjf1m-(M}j?4ArU5YX)RGPOY7((Bt{gIkMfD7LV z91{xm-QRN<%>tYxxhD`C52ZIgrg|E0D;oI*|jW^(~ z&bt~q>>w0$>cr1l`8sMI?_tRNLS=dX2Gdus`kVBm^%8ZIoFe!ifk^~hVBtodoQEIV24vk&ndyU~s7Y!BDJUPEQ=!p+13g}?w8u+5qX z`tVDk7neCA`=ii`Oq8Z2GWnt*sUEF8}_2zWebcS_|0F_xVX+*4O_# z692y?tN`AJ&=!fy_kSz-)t6GDg&{U2s)* zF!_ZE(amPnvF+8VOS|CsmWmh=ZR*?|{%#-d*95nS=hkm(Fjmb0swuKYue56D&Ev*h zNV4Uab1qOT|Cv5K^dzW@OzyRJ(~rebzeeR4!HhEp36#w@`hYQxdU(laR-Jw=Tp*1# zoQv$|5IfH7(+tAaoPQbM;$Cnu!Pked?=>=GNiDOr#snyw|9p$4YDvlBo}K>QV3G~;oq5z^hyO$sVdl^2rUl- z{-XU7)M%-3V6N)6e6fv|iaD!90#zh(mtG*w`S)BMl282gE6~ncndIm-9w&bdIedns zrTYkTeP_`S7aPO-G!9v`XP?;6{&v3jI&c~XiKW0rDr{${0$s1dl{}6;5K_f+>G%YxlBq|xy!M}SMOCOMhTxuPOMZRBSSshyc9WVa^ zLtqaN)#zK-=FA;0?=|so9ASZ`pATk+4)3*9JXjU`^*{l(5;{tDzFJ8?9Z;b5F_|!{ z-pLguIShBl3!~nh@urLBb%rV$wJ&3a3_&70G)WkYyo5iJJCu%Jf_t~emy9I@x*?)I zHiwe%6XEJR*r!josl=(c^El;MG*LJy)$k(*OLCtphBL-lwHlfJ(@GKv>)!E`2xjJP z>89l@Gol)GhUpLQWM?n+5*+Q>`)3$h&gFvxmVKl9*Eke>;m_zl0PT6`BwO!C;0J(U zpHz~RnlzLf)fK$@W{du0t!L85{3INAetNEjK3_&hSscQm@Lu^8V8prL?wjt1$F$l> zi|N_f<(j)|*7}`$UN7gkwHrHb;O>@#S2;fMDspAsr1@c3X`r&S?>D9z1$N#P>R0`f zs9v+Ld&4I8Su*~TKw&b14C9!mN*0@%ccMx~*@COtddRRgP3&8&%*B|~B=z|x1jqe6 z`9gBmiA|L5ng&X}uS@V>v6A*%UTIac_1gSzOth~=lrJS(<{$e`Fwy$}BHBH8{VGtB z`h!Q*ocEmaplM%wbG~?p8~Q$pAh*_NmKzH4jS6jL)+inl?7_abNO4^mgoSGz)k25L z+P^GTuqXQshs5|Z154c=jdtmb%Pv zIGlUL@%_H98;?9*R*nYs^Wu6hT6Jyj`gXHR%&H#SJ&xEWi@ER|ieza!Q@39v_p7%6 z3L43%&^mRs?LbA_-9D%XpCFneWrr?BV+kTh`D*wX3D&I;J~fMW&~EbpZP*ADBBUr& zx1Z?8+klXwICtI2e`Xq|EO<3b-??TN0kA@5iPoFGiR@*tRA@$boGZywUtfwqu<+ga z3a$H1N}WPd=P>M~rwGQ$X^63?XDP_2MF+uA8&5x`MYxw1Py;T-c=@_0ob^Ty!dglw z?pSac!;YcBs;}`)R8pYu?}b1Ar#yUkU2*YTeH;JwGz|U$AH0#n!eA#e$XZR0ZcESt zU-NrtNiseL6jnJ#)u8zfhwYB+YwWn_hEaj5s_73$eM4Jx+%KC8-S48&-){nZ7&$D3 z>bz?}fN+Z2kAh&6HKtp1FBf^Jp8E57pUeVE&Y*csDkSZ?dbH0xEv6NwFmkI1pfmq2 zc(!$on}%=2^8vvCrlWi8GqIPQ!3ll)1F`s>$z=dzVf_E-~Z5+s4J#nUAs`H1=2>1vHrkhH?ZV?nIwP;pIYluyz!4Lk4Tm9(yRyP5I`$oqU!wwd&I=ptlXhZ(Ok(|;0W@uoTO zl0iG?%=>%I71|CKT3pdi%mdrnUw0OvK2|*MnZMVoy}s1xc^OEN;78lezHA?mB%@gp z-|@cV?FJjP>Pj#;MWD(Fq#e^5m(}ibbQturqE-#@!#pyG(opncQw0+;%$AtvDz!MC z4srVDzOY@RRra>qqa)_~Z1I*`hPX*Fd4< zg>Zf@E_g8TNVd^%FvFqqBIZVy%wMuQ$ai~W5^N57Q9sFPkOLu&T4)UO17_;6mYU=r z-(~@1gL+kwlXJ}Sw54qykGMeJVhKX*Qvbro+^JVu&g84`EP?$1CnVv!wZGB4X?ik#Bqp4o>v?)8}m zMSIXF+SGcqBO20^N4a<@l-V9$)$3o^m3?=Xlrly!vQL!T?BJ2X33t`Bmd&uI)u^-d z)DmPH>_NX8FjTe7L>%(aDVv^UTN-)0<#TMq{e~i+pQ9PQ~l@v z01kWP9tJP2SUt?5@u{~xi_do3Y5sC_q?j&$J}2elQEZlCn(E1kT}1nQ6(FQ9r6Kpk z9=YYlfwLW|zfDFggw%3MJCsA&th)#*dHRqDC0|u{&;Q1_tNQV_vB#6{nlQIM|C;>x zThlfvbIAeMGRG6k>q9eJcA-sjP%Y305rp*T`~eP*@l}9FFXuzE-d-xM;7t3Jm4b9>q-Y@$6$k1aLpGKnnV%qqeO2@4{w`8W3R5>h;>NmOg`;|o6ME zTeS#J8^R@uA){*SBEGdjVZ8$=xJ%ha^Rn!dgJD)H+^F?LETqWfDEet|=fnWik(OlK zPn7Gv2SGj`_c8~k^>5#klvuM=Y=YnXz?Xjdo6&e|17O*oXmwKmP5{e-Hp%2h&8^&2 z#gyyH?aF)YZU;a%YxnJWa=J~jNG=JZrey+y59#))^vi55=}_yxYED_^`-0HBC7rZj zhr@c;T-(T#r6EEWL>X1-P>yoJE}c!jBt_u>2 z>C+-QRbkq0qyai3ied7D!pqj$mg`J?)58I65>cl7$7dYqKj{OhXn`O{+K#miPb=o> z7t#=+nIwabw~O3y18c)gllis1=V>xKT#`Q(R`~5MG}=vZ{q3;hC9fM#xc-#>Qj(7+ z4wZWEB+6g=GVO1jQJciFF)IFGwUsVaEj%lty`;?{wbv~d0cl^~<*fF+tgB`c)_VAo z52{kzpmS_zI;u|@B2Xw+>Qg30p4vlpK?!pS^S_?!H}%p8+4ha~qg%EeGv!wh&#=T# z9uVZMdJ*~CoMw#Rc+ zZB8_U7wbo_Q1hX19=ukXS9L9+qzdxNfm#J+mC2XX5VA<+sl1#D{~}(<%z+gd&)*J9 zguU-?8 zEKU9nr}Ty7U(%uZ_0>SW@V;(!HMX_PICnsnu zjG;qg7E`O2%S-tt-$%kE)eOgrtc>-uCVY;FpK+~QONlrA*6A@n%H?0F?~Au||oaWtV&9!X~MHn2u-sqdT=@!b4w`Qqj@v(N~?dO zB5v5;5q@P+?tapsMuIYdWdd`tY@hcu{<@t5b@ie0ycSwy&IXeX&`&_d_~P)<;jKc*Ylv)KQ)0RpmVs??5E;<8xSC& zlW7<^oR1!Sv^a1GaGdAzZ~D?vJFkti{RG{UcyD*n1a=`ae-za^;mCpk9^kuWZ!N#4 zR#VJdYa0ImbNZnX0AdNCNO+^oc^d)8dtU+VjpgKS#PpjI7d6Ntq25M|ecy7i2=I=Q zS1fx+*LH^f{@pw3ksVlA(kSrG7x(=(>t2bLS)~*;+p>n%1<~O?NVsf&bz;JO!(*Ky z0y8CT^dPt(tQ1)A>9X^u4LE-nzB_-@%Wkhiu;Diiunf0eDK)po&dKFi02=VqPx6Pm zgrVbYgY2713r!1CgM$;%Ourj3k7}*$-#_#9kt$W(I&QoX#kV}q zzsA&;u@%6SNOcQOGWocmV`ltuG1jsX$6FN5Y+6`kvnz>R-UaS>%FG71p;^m@*OuG> za&J`Z<0aiCi2gnO5}+gfvk4ge0SL|)=uAYM|0Zz(`#@3>)uCa|f6IPw#z+IcBwS>% zD|r=DTa!Dw=KUv;boS^bWWaDE+8pawmTq%=Xy?Ek+jnV{Ns~`X41#${-Tzs_ z98--pw+d(Wkn;Py#6tnsn@&iob)q`dvJEvR*?{-oX*A(!3=Lt)LNjTRJXLgiGB&_v z$nd!P$?S09`=<$ApK#H2o&Ml_a9z;ud~gg?JK08SDF4lo-f>{#!@$PK2S9A&*>`y9 z0r2{8wE7{~D2V@V^aTJhiYeQA>nl=?Z5)NeCYfdZ2AiG+cmDTwHC0ipa&W z1@-Q$t~Vcl3k_sonnIAS6V+wY@!zD;4-Ecz5`722?;-7xdh>sK`<+|E04*DK^nf_C zikVkmmP<(i!X$sLLgBtkihuiX7YIPdV=q4ZafEN1{CM)eLoLJPe~#LXp+a#`y6Gmv6L5w;{Up3a0r|0> z6aMJ~XyM&pw&;d;m(i$A_B9%QW=SwfTAVr>#jJ;@NAV1K4F51Lf(M(KgWWb1_h*+y z<;HV>?|5_P#(QEfMxIC-ja{|MY!ocb_YayAJ}jls5HmELTSZsT=y-Hu4n;1Mq1ET$ z$S8pnXh{lqK1ko-i0(Y?akq&<7uBJagriC+G+rO5u6ov&Fh=#x4q_;pw<}(ZuGSI# z926P=EE%HqpA<30(bOsdf|G*1>7UvhrdQ|F+USu>#yDutcKkoV-q{QC*^xBN%;G%U zdk#K}G>W7NI({n~EF7$4&%xHfgRV(Puao_o8}a%ibm^T{8iMHS-q>HXr8U2-U1CrY z=h=1-@*0@$u;-@aCD39XNW)m@5Y@SE8|KOL zvh3N{NIl%-nJB*sKTw^(0I>$oXk7AIuDdPvC$q=Zh53VwNC7%xrmxkE=#UzcnRD#a zn0Asw!>8dvWYkC6(~@(@s)lKWrFnMq8an!IU3Jeea zrur0v)JiHBawk*s7Tr|na};C%WGEPKMy9HDm0Nyu$d)xj=}{mN>d+M^reAkunt<~s z1bENng(x4~&>Y*UPB6VGY7*J8$fpZm`jS}9{)@eneJz9C{`vi-eH0|;x~gH=4174a zEWZQ1OXd}2@bPjEd(A3T$U#q+nVLuI4bBE6?+I~o)DM`w0I_u3L6R({FT71I-5P}I zd2{Q5T$*q(f*(2~->OO8$3yqnF z>WgXm*aJZFxOA#}r`Wwh_TQ(U7d=;Bc%JC9gj9;0gpDt~Tbl3nCDEt)k)fQaT*I^1 z(>&DMy^V!VH=}(7eGfkdA5JORtmY+rK8R0!R#XE0BYlJo@kgHVf1m83e(4=136HU* zd4eygrJd9dS?wqF8aB+0NHkJkryPt5f|4}f-5)V%S6}#tAMFEP$4IGc_Jgba>^d+3 zL(xX*l1Pl+Jk&mJP>&e3Kd?w#nt4NA*l6dfdBJz7o9EVHYjGv{zDhK;`=YRz4Wu%L z6$<;H<${;QMjqcMz%*T1Pfu5vW#yghHlu_lC9_@G;F{ zowi*9i{xCVJh}CZT_rU{!0?G5u%d3>TnIJ2peRlO)lKN^xl}YDY&bd8T28Ci z>OZB55t>%i08VHWBa~5~1QdS~V9w+ASAB2S$PJ@{*ttn=0$jBz2kOn)m?%#%+G;CE zEZ>a7spp0yffn*Wbx;T3D7rI>RGn4Ya;5)RYvvPJuyO)-DC@I#@byF^JCpT%6P`(T~=A5 zd41#f6{xtTc^FsP#aMu|A-?njm&W2^>f0h@+j#GNB`=a zd}2?Kt;SsPaP3vgo>9_S-{-N5egV9&?k5<9c2cd8SkV<#g zJbQKioW)xi7ukcMtGYvW)qB65tN||<*-mnuh2aVk4mhC%wZ>}1L=3cRT`Pdx$BpJp zF~1{v?3b`s&8*|Xr^xAquHhN{Os9fqY2gt)P4t0;m}mDdjrY&-NDD*8hV67XQq;&} zNIRhy=3ThyrwowGtAp=~^Prv7%ueTK->65h#Jn5Tdih|RWk4?G%I7Uh z&pK^=bep=1_d!32R(u}VRjqhtSxT4DC9vhHKxLk`D$}J^`xNW5daEmgQi*Aq&bi^4 zxXesv^6wmKe?3?t)SCeAxLr!UXunB}>2TXN7jm5)MdD0X(-cf#BeNSv?-}b(wydEG z9}HSWvaCAs3;t14HP%}C%4ZF$UwfdZ>;I3CH5zZR0L_!i~1B@JB02|2~#Rx9Lo&Uisk(Z~uAA73qPb6tC96c&i=h z)(wjETSIkj0Xl3~+a+b;+gO#WxZC0~f~W$B*oikiA!5i0Q45%%g_5GUkjYwsOYsuN zSk%luLm1EQFk+P~^|Gx*sm9@M74sjLBEtmK$$65#e~@0*6=PD>j98(7CJZ*u1DGVEQ?by)C?Oep?Z8+<@epz5-$Cl7O znWDpSOe~q}+Z-}DnXOhY$9aNLWZ_}nL#;;GF-UU#hCrNZ0yVhTuMe7PMml=G^q971=3p`B zZA>jkz#W68`yxR*+_9-Bjjt7_UqGxYwOmnh_lc319IUV&$()R2sAyV{-Lht19O~kV z6x0c7wr*HdoDr(g&R?Q2t#Ux;5gNME^_0);3gXs>UiR<0ONmuVX~&&}vHj^?*DVSM zSLboUgM8__1(5KD&b-rokp96kRy?(QlkaxE7IA(NBjdS8=p`X&fi)OwG@V;k4IwA)A2=75cI?IB_hfbK0=`C^+1=pb z3HKer|EUER$dxzD#0+-OKEd2-Rz_j(il1<^t~~&(%J_^BF)SZ*9@&lrN=~$amY+_DBB}pE#&C~SeRi<9@Lu} zszC8UO5K9#4^f6uvCx|?e6q^0U`K-SJg%WGMn#NGH|uHvCn&mCxG09M?xP%m@KfpjeQF^ zW%CIi1jOL`>*lTC`tQ83uBd|thCsF<7e2bd9K~*FGV-gW8tBCz88^i_Q@CxGAG+Ae z_KuqH#`NFqP1H-c^dY7owNkJ8n!i-WF~N?cv~XOBz+OZQ5RS#|F0Z$n!uZEVz7Yi& zi81rq_0lQ1diy=a?W#kEJ|=MCyalYaChpp!@#r` z7d>G4t*iqm4ryAH8vfX-l!16}s+5Kp-5xdSeD8Hfuje|Oo;Q<`9OE5*bjgRF5|y?g z^n`_h1%KO@nWsIeJts?NKA01x%1K!Dv~ViigVZ`yk8v{^@6rdugkw5{@x>5wmlD|@ zucmi_V$WeE3GZYt6!<1sVNtmZY4p)T9eC*3*W$7*-VHb#dtafrd@fbFZ!*11XC3%a zGxZEw2o>@)9=R+@<`nxq>pCY3Ei+AW(=yXg`$ycoURTPsh9s1vqDZ2@)zc^2{Msn} zs<_uYytYc(sfO2eA%T9KmKI^YVjkD^rXrx`!U}eo|Fuu*bGgiI3F+TK0_0-n;AfM} z+h`0820XzY=voKaCwN}wWQM4%d{M|-Auv`eC3T6`UbcUyh@~639J=M_?>~fDgwaKY zApjko(^l>gj%sf~r;j(=euo7bunUN#WYry_tW1CRd&dWtj#bp`egj^i!Lm{6> z;J$p?cI3DJ6Q|sjbzQzVR_CCz*o&rksV#pU+~EduI&|KRzpG80A(#LC-<%J%vmSlc zb}133R*jwb8Dt|syt{af_MWsamAs!p(DYLJ?gqQtMcZ`_c8P7g%qtr$05ZI??}?$< zDz1f1Rm+R#wv1jDdkhGpk&)%ab@uNRTcyct-TK?b&wJx`OEbj0j?X7Q1nBgJ<8OWy z69a-h-OERT?~p7IuYv&1vgU9AZzblBN8(tk*bW4_YpN4`)4vvCf5*@*_XWg!QZvRFIUxYzQ&UKeX%YbfJ>X*O&lN3I4K_H;U11-;q*IfP|y$ zaZ47r*uNj?+lL*8Ms#e6C%@NS0?Rk8TxYPva&HJ^aT|E?gN?|sar7ahsXGefJP8TO zc$X2IF8U_?m{xffVw~JCQmbF}v_F{ycnFhhW8+z_Vy0OQZp-Wf&Nx)?)_;M8{^974 zUUe5L(v5^Da^qrQYaNPY;ZCq4>PL+xgO{E1V^_9jQvqEH{_LuY7FI2VRAhX0#G0no zrQ}8|cowb^!?NG0k!GIt$g;iStu)^qi?L2u#vHi6X^|HVqX&^3kmGD()a)9tlgnB^ zn~^%uloyW~gKC6i`97*=YTYL{n_+4q@pF0VgU{x8p0ciaqPh05%Jz1Uz=G%5nlyRT zfnkK1x$ijM7)UnndQfo;q~_@TTI(d(m8IwIwXnUW07c4x)5dvOypvBi-m}_viFsJ< z_7^g~uzcR$mVznq^V;N`?z2fNO50S;T1B@gt4F`tawW10(0e^T3?kuM_=6c!zsO7$ zC58)m&9i$uRPBEGtfrsb?pCF5tr74nRo-G`H z8cWG!)u3L7YzuDOt^)VpSsdCS5b)D^s_D0>4&IF|M3EQJT3nuW6AonzQF5A6%Nu$984&cVy=VTIi-!k``Un zi;jBsYzRNP$N60Y@UuIMxq((;_A8yKSg1Y)A&khVsF+VX7Lj;AI8&P*8f89G^_qp7 z)MJ-m!C@gStr2v~9SvO<5lV1&_W*`S=3k%&#j9KuK3SZgKUE=ojB8)jS0BSzGre#K zQt{Ptid6+95PRy4o|pvElTeR(!(+HC+W`$2#+s#31(e}#UXG}<4?l0IYU{SOzBjgk zCa+dD)_oTnSwZkC^4#7>D2# z3>U%e80Idxy^%tHCxN%qblwfS)*SU+czUo05A}Po-S^F62_?)OE?i1VH#*=DrJIM6Zp!R&$N43##9ly4k z`|7JpWs46sO~%a1#T+}+{gc*6Xa+n_23afVrLyw4>aBo7yp5Gj#e^I`gWVw7ey7V z02G?)qz!y8L;^HYwDh%>-sQID{?*wmZ7j3!SWSP8A^)Il&F1&AK;-iJo>3E{7smZ2 zH6qm(d};wqfqrWr3_gzU*4%5U%Ia8Kx*1c>EuMMOFXy-Zwz=Fa7Q56?h`DD-vR!xTH(+ydtc9C?s;;JzLHogla>~zUY`JAFtJ3IXT~j;Xd>&)OzK3 zdE5LG`FF?5)a|da6ez>0I)ZTi)A9vtU8<`7*1tm9Ye>Pv4OZjkE9>l4zbF78?GDr@ z?C@^%vuNv zXe%XF4$tdEn?Ms%S{i8cd3VPLBHA?=%r28MggiZ0 zJ{O`89GMxKXsyM#gE;*bzq!VccrXELh)=KfQXsY#Hz;4RY^*FNo49Z4o4jjr&kDYn zmPvt@L{xFzx7VnXUsTO}dTe-WygX_Ul&a&|L9k&}V--Hm^j#<~z$0oP9jwwe=dZS7 zJAm%?dymB|9S+Ph|Km}V_jsi0q5NSF4ku;!0QsxiY zzb?Ccjj)xJ!PC2;aEz#7KEugd?(q1oI!sl$!Y*=6vWC|~Vk~C*wi`r&42=1Zs)dLk z0}9yW#((TMMH30KMh7G#fan`2Oh{AM=z7VN^1xSCTv zbR%_M5VHq113p6T^?izkd!99Ffw7*n)MK#o*r6v3Pw%Gq)YM13YV_XinKQo%#!W>Q z{-X5FR}Qm3#B9U8w}Bs}Ynz-u`{2M{GgX-HFfiXl#+{d5TB0I%tM@qKl%qgZ!}*%X zzR{GWL|7_AyJpiCZF49a;#m}5i+Sd-fTgD9WdlYqcIf+})Ti03GdNX7C*LvJ8K<6r zTj?5#q}WGKTYm&*8qW+IC$U0RapZH2<=3bVjA9Wi{`jTfY=k0c@x5|FOYWOG%<|ry= z9JSY~51X&Jay~|x=}tBsr*L2#4E5{O7)cah7r04_fooT(N$2J=`KL7~4%KJYtn(Jl z9^R*!PXAkLqulU#iwGw11mZMdx*+qL?73^kz4ZR@5Id{WqcZz}fgkT5+&^x9yD&V% zWeJRNQb@5qnPPl%R5(0o5 zs5*Avs_vJqdY7$*AFH4DI+Fz!RxsuzAE*;$`(3|(k~YQ18X0i=T~?9?HhsVmXAG^v z9I#g{njU!^BFP#}O94NhcltSA4_m2UB73xLbua z)$#Ii2IWrab^R0(tfPvb!t(q)-h=fkr*mw&X8w`DzT0{AV1J{as?7iOaXTb^(E?Kg zc@{(Ap~yesp0zI z*90Bu;9ya2^3K*>1?#_R#|%I_j_uMZ`w`YZb|m`iI3dt>&+{n}j4?uA9rxreEFxNf zd05nVqdoGz(bi%pK~ASXCFHWUr%{8IOR<|yl0k>6R*4FSbn%oK=&bYikfuyN%1!EM zdSSo+D|_Tc2dNf0N3aWc)a#F>nVNU={w7jSwj(nW*db(D(cEVJ z(LZkF-I5T>aJGOGVW)2ykk*Vs4u_V#StH2F9Noc43+$gc71g=BbWcs|JL}W&X4P_n zYEf|r1M^I7eO~-dTw0(j)?{}uj_%gLWzJizaK!Gmg!e28w437Q32pdZTEw-4vim6Y z`+la4cBk{1XYTzmlVM5<#;b64^z)rYXS*v|e)N~K_WBn?;fQ`gC2JbH;T&~L!@jap zh}i8P>~sy_N&~rv4v4tvd$?z+pYSv-tBX-}>hb}-5f5-B*3BCweVA}4~{-NACp zcK8ye8nFdfsX(u4{zpV@L{f^p8FOSNZ zs2N}N33~S7CZ250{-Kg%!Ah(7qY;z`d$^8CVfNQkj@3Lb#DA<1Dfab;C64%k)DWl6 zP%=&SpR%8~A=`)d9+%st&h%zga*X=fY*&_C;l!@1R*OMgnQ37c4O4uedSF5Fv3GRcQ#`p3u`T>FyL`ltU79SaZ1a5Z%f`BmgDwngh?MAvx&xL%@_# z7rOni%=!5nanRYcAmUq+%;ox1ft2hOYUSxNW|YlOPZxTLhWc1)WF!TvlGEYnSM``w ztoa)?_@LxD7a*HM=K3o+T%keDD==LTomeOR-9!0lB#ca14Ab?*BlTg3QivA54^vEN z6!>KG&mhTl?oVU?eCxh1j$*qsk$)ae>mxvDo-E6itcKkMW4x?(Ow$t z19-a2$11qZcax?)0vl0AZ)HteP@(18@AVkX;lF z8u0`2^fT8-@(jT|hXrr!caY9nzI$>pGIK>4Q+FJ8&v#z;1g?&{Eizc~qh*dy(tO zP7;3u$VRwrrHt1ySyim^HVAc}rfTx-I!|JFp{tZ0Wb;Yj_X-398>RB%;#u6I?OcHw zNi&}B6>}HXiqh|oiw9xKDb<{*GM>Gt9fejl&V#`byCVp!b({OT=AZu#t__{cvG&rK z;M6kg+iTv{pFQkK+|+kryzHwR!2trRSpVYR#Rz>Z+bu^l5}tnPxmjpwqsYVPtfgaB z+eLKamd*Fvwmj}F&9ONBsFv(kSIIEC6%>fFE&x05Yf3VkYp<>#J1nVT`X(dPWd0U* zx$K3v2EVClC%d5v+z3f5epNB)*GI4!yN`{`DfyebT+DA@tot3e*WbW5#3K<@Jv=@; z(GX5(e@M<5|2?|ih!rR0*^1h**T1w9Y3`;ef|IHB@7F2Nc2bcu%=*rnWc6)!74>bU zOsMGFC=V7;O-c?9{#IrCIo!9=IR|_Vx^KtTmOFO(jjt2EX8;PKL+#mE(I%^rvq z#uKwY;(BdFa0(%wRGabGeh&YBPJ_hXxt|bJ z*=mP@VQ#rg43`HZ#~g!DDMVAOp~aO)@_qaWd7;@2u1bTzwSBjGg#W97%S%r@Dk+q# z0!#YWxj+LfO;Fz{VwZP$ZR#18aXWQ!Ar(hsUBsj|qPTlp{nKv~DZ~N8&(S zsOniNzBZw|nDy;}TJRdEqU%`Yd#;oncrcvvbz(;=aP6PNlPjX1{_-leu4ex$=jF7* z&T9$g*D+VFuIh%?*%{e(RYE>ivRmfOAeL^@aT`UeW~+OuT%yxxNx%e-PoF1n+!jXA zRDF7sg+2o4+_WMUuSONcfipL7M`7V*-$thmjzoFeA=a#S{G5GEWqOZLl9-)BoLK`gpDaH%`}K z^rM9CW6~ZDqUX<3=-Z*DPjY9fdTq=pneySL*l0zHJ5k+*Vd$4Umd?^#luJ?dB^3*C zlaJ)vcVZ!~`Xw0ezE3prWHE8_N#sHd{(5hhnp+5WmJy0X-xbX$lY?&zsA$FW4s{}v z5b{MwA~=^owQxwmm-wv5KN87VpLz?jErv{H{xtv{FfAeXH*`xODwzGI<-?a~x8tvX zizIPb=jWKXm}j_5^*wU61~e4w^zHyn6E|N1O%sb5 ztu0fR-U*#*nT8P>+wNSHU32O z<^rrk<)L^EZ3rrZXy{iG#l$~ic`{0uqn?VwlWa))f^D!q(#eX{QlK?+_at7 z>vB35h^IiMJ+<23^H3>&H{}%=@IMgY*#$*+A4Z8MK0P)>w|j3Qwe+^@&^3g!R<)c$ z)81SzD4gBKUNwolB6zh=DRp}|`>kxu->4ddOpI@H#>Z2@oQI8SKpPS+WG7kXM;p>F z0|JN8f_()A;{2`a5k05sL-LPHKgRz9E1G(=-lhdPm*0*C_Ik}gLyf^kJ5wLv%Dj|g zsGsx_BbFOQ{s0<9LO+~9NqtYt&GC2l@bMN_o>6+y7B&!m19LS*!fW2+~muyWYrGq%;HN<&LBBp?8wZy z5nxnGy5(3`;WtCaBLYyCAMVwO*D(crl5p_8VmTf{(8@?w_vc~%EV$~J)bAh#;GuEL za^D)Nr#ARU-`c-SF8L4Hy!I~O7{%vb{EvY>0kaZTjV#n4HHr)B#7`YiH(F@Ty0T7l zwefrJ@}T$j()nO zcCOI5q*9y)_f&*N6Bb?~uZB!w8$QK^49)B_))R8cgwYsI&0h34ce!;NF`O-2$FqGD*XKa$VplHgtJXlx@w(B?qXR|gRj zKSVxV+8pM(ryRSW>dd6aAgR9KrGLm^<-5t@;u#e9Pf(>YT7#WB11-0$BR&@3W@!~91TM2vTT5PiF z67hETHMT}Xz1c2$`D-f4q*3rJygLRp-q-I&O*1vvU-8fcmFq>m5G8v8wDCP$RDMn4 z_7Br#S1M2p_G=1$I(ihic%ZXE0pbN74={qMl;eT!9^6JRlsima<9ECFYJQb`21_335qn-iiIOK zW+G*%!u7nJU7QXoHQ1{8w9>bY3zn-7$KUknh{N9RR1VltO~xiUGhd>)UJBYqTGe;Z z!_A&p$t~Y}5*;d(VB9K`K4+sAZ3iw5Syi&cxg@)8{kK^FB;;a)a8rYbLu|{T>2Gm7 zR9!+Bu(gYBG;sxjxhg8zeh{BiDFA%=?#iz1Ch}p%p_z zgyXM(J#SAy0&GVE>7}%J#bK0bmrZj}JW1o)aE4~{a4|?C#UbMI(j6O}W5!i#SAI0X zMBfB@{B|#FzPd+DrAP`6S+BIG0n_!8_K&pPZE;pU%O0j+1lg2&D8ogBTC@uTLRK5x zWw$0-oPMM7dDunfNVR?s`Z=~7F>FY_jVmg-(a&8^%FU_iqIbRU^*#zlb`G1<%H9y>+Xq#$@rGs^+|HxU97mw!pL@0`qg`|GhcB*N((|lZP&xNq zwz6$TJ{ehYhs0TRPrxn-noB3@E%VlTCyKT9DadJbk&4#SDv4fZC@`-*tKa__=8{L} z7O03h>&3!*975kf+om|dF8}VG1Wf_zdXH(gRQq2|V8s+2$*w4Bt#1RvDM{lf;;JzN z%YgK}U6d8|sHb{r3;27NJly>8!0YJhLX{_$F#`T@Gq>%g1yyt3g69m`|Za} zVKB`z-_O1Lp-DzUE~O@y4a^HzqIDOkaPD?6gQZmDc7x&acGf8nzAoWqJ@XR+sB_#9 zYa2$TUAQ!9JfAD9$4!t*HqkQMuORv{TjQNqq(xTHws+lN+ z6FD$EW29#OBANVz9?^Nc0Q2Ig=1m+j@HHqDo|QbMylqDGDq+~#0b>EXT23w6`Tm;| z=W%YiN&ctl|A0rg+TteU!uMo%?YI+Mc_*N97nH^N=uiwm}OpX^mK)A zvv8;1yA-v0GN<(2nytX$Qz47kcE_gQY{|gj}-6 zyfA-qX?QrNT!I0^P3qW|C%5~W%J};1v~7AaX6JpeP6^Rb}inrFR^JNC`dk5K)1dQNRXB7nNQD1VRZA z6eUV%3M7ya0wOIT2`vdEBzXrNF>`0$eec6t>%Fz!{T2e8v(G+z|NH;9z1#g^NJW}e zgG6?bw>*Wdkpp`(5d2}j*WSspsqGa*lI~^i?uQTfmk+cIhETsCVICM?J>sE2=npEj z@<39|_yty{$2W@A4F!;;_3L8dk@x zMvReW$H&9WWd2Y!1$OCBykl$j4&VK(l*t1|Y9?$sgL$-ZBC#U?fNg@oojVe>_j zCX|#;X)%75k{jWW1TkzPCPVFgJ8Vk%tjMU2j14ictC=-u)wRRhli53)4Qm*s1m6xP z`Q7&qheBQ^7PmheYF+9*`+Y3`bfm;yzwKf>-fUX$ju4;-gMP}N+An#74;(yWRIA>X z+X(f&^EM~9QNt(Z4@0+_Iz8kg((N*8S%v&=-flv>xA890F+)Y?q{4@%yU^-p^9}Bc z)d!*8WgnU}jYoFVI(M5C=xy)DDqBH~QjK31mL);Tyjp{tx=t{iYUp)TmIWdwI2*ev z)3N*frVg9JxC65%t*?H!QNI4xCAHZYyyA(?DLG}XuPa$TOcyp%>cW*ru*dXny!0_1 z@Y`1_t-IJ=Rw#M?e)ysCGLkPrMzJUouR&@+9T+rymL>beP(7-6x}Sx5Lo}R6+^%X* z4zpNt+)zXcu)~9R-*0EW1CZcF`w*1XQGNT+{88d&Yl!-zE4>BV8w^=>sB8dgYJF4YzVFvxy{ybJv*^h@B?jKS#%Ji& z5ChVrRY=>KXuOzVZ?9V4!Bj-qr>_bVO=&+IrDIv{FSNS5D|C{PdCkm@tOcyTNAP@y zmb?QmxbL|#7>taI;^@s&ff8-z$;}}enpiso-~sN#0^ZtxvM!F9B5by7P}T_ zT0r`f!9HmJ#K`@~E9zPt&*u%7`Igw;O)Z zoTwM9ufv+_Yx3=<9p3G8DL5bwoyXRBwZeKNc?{bASJvafy?$=VwnZuKiMn%-26z;X zGdM9bE#77Zjn!ycVw-ETx5Ze zi_G-NvTLjB;~$a4wc*Nx^HQHDw6JKXbB1T7Zl0n$jc z$>{D?(#UYtdK7O|EgzB_vbsV4>4T9G;H*}xB{+&YOn))Q4Ae^^w>ikeqn zcXjhyAoL==$B(=!DgGlk{q^=P4M4=WA?fiyjb-(={~HuA75|$lz|r{Mn)=`FTMTp5q z#E_PANVc5$;C}MIFLBflT{`sO@~*Vc*-r{(ANv=n zoOvpFecQV0pZ6Z$tGE7^shb+=rN!CjcDegwo*PO+*Ss^(XnEy%P~G+JC294XBQe*i z7N9k|F8(Sf0Z(Ia#-qAu6gn%I3G37sGV}m`LQ%bMb|Z)Ag)zCnn%!5wGX7 zYx+jU<(xxC@=CK@Gh0*qL+AIvz4NhZU$*ib*8L@@ln&7Tpgk{$B5g;*?LRlrGJWm) z3D!UlFF9YIRX7?B=OpI`Cx$P-Od!mE&Pt@H+Y6~1oym^EcLWf#IJKbqhIgZTnUx-{ zLsEBXT&ZJsG73 zI6_OG>V>w%K5iOcw0DZ|;#`zr?$$FGWB9_^HAhRdh-ZAUAyL>scl&KcKBK!AOu#=* z?OPMQ4j@-5bidQI5SgxI>DtiQ&z3hl73N8?g%}o0TZ*~jf;G#Wsk;b&vB0qBYArReHTMjMm>))n!K$w ze!b0r_1L(Qp8v_)o*-8e$`PAzRJ!EAZ;spX3G$d}wj>OjU|)qvpv86jhmT1`!s$-6 zMz_(E4z901cN_I;4!5hc=KzKHgB8It!S&XPH*UHo=6&JTjNC#{EsIUzM00ldF*Nm znlX4+iQkG@>K{##5>kETF+o0#!~GKU87G5_hLvPD9PT z?|&+8@2ajW-LQH6J>)v6>Fj5H)189pwi2ZU6C0iFj|u*`pOS(0jVgOWMWdV{L6_Hq zeVRBJ27R6uHSw(Q^>oRbG*n9>PQ3=-+!jYb}rNf9z$W0wuhDO$^>uTxy9^sFzopuiv@OFe58Tl!mu_pz(yDe)dR}Uq zPLB9^LX6YY7YOwit} zxy=2dB)plYu9>bue`V1)(DoL7q(pwBgpy|9@jk}#q;Hp4Uyf~o#NohrF4Dm3r&;aj zF&nef;?9a;aOZRLPaotQ)sWWaeC)0;RCfuC6n?}(()k39eY~SJm}(48Thu1SuhjAx z7l?kf<=XI7%=iuRT}raHikZ5pmpiqeJM)nSfq?khC+Dlq)!uVvH5M4=fIFg$eCPUy z+^)uy8$wh`c#l5QhXb9p4*8(4AYY+>6C~n9=cZ2rmEI!th0re**y-_S>`0CK$jzo+ z>|52@Zs)o*+uKzL?bj8wL*!F1$&EeRy zz_ggsPBgK|d8s2Q-AIG<>N|w?sOheNOtm8IE^VYzeg@bc`uT*$D|?g_CrVPgM3_#(1f#usSQB^buxf*VRc1@a%MSXSfr6tZ+kDX-x)zqcfyalD*Jhn5I_>) zG5%#34{1w~_(i(AFN~WQ4dGROAe^RjySizGB|vH{DM{L65n$z+&XOMJ6&)H9^lV(` zva)wP#9$2s5r~mWg{037aT+lguQ8UUcv+OxdX#c@bF`>aG6@t{MeMZ_RqbYC6J6O% z21fY$42!Z%1O-tRdX<6*@L3k}2z<1V(mOWsRVkUiPn(2Tu3rGpDaGgS)n zX9^3pK=F)l-v9gBL%*&j$9+0sZoe48n||r+C7_JS9+&zK~Y^+x%bMgG~l>UCqhWb zSG34{Q5EHb9m&%+@op(g5a79Yz`!So1wB`FF9u+7SWjW=vHPDI+i3eHd*11O`k=0i z-aC)C3Cwbs*W8;)xFANz&^qhR_`UsB=d6UQbbSrS^xT5(v06}Yrx!dGCTnPs;l}51~tgJxRiR_MA@b>gJniO+Icz0nI^!`g9v(zbj)m#3u{Oi z>dT4CUVNFkJFsJS$wf|H;EvrW29mR7jPzDf|^+2H~t*3tFZQM@ksj`wJ<@ubuu zPZ*9+`fz6$PJu!77B=^maDzcignj6t9IGav*t4 z=~!HSBf|EKHU_c+S~&%qMW&SPG#tCfXEJI5L?Hqggyk!oA&yY>Noz!t4k6P3-tnrQ zRP=QC@N0X8#I)npVz1jB=qc<73+X1Ssjh?)dR%SE!=+b7+pNYTE(P+py~0;NfUPf? zFEdO)G^;0WQ8;2q|H1{2Pxj7)6p+~;n+=tZPtoqO zXJy?Mcs}{+-h5rr(TzJo*~o0Y-33|bK`0>fuRaLh{wj+idsi2LkvNHwG1V>K|1X45l9r%V0jOoSf}q>6M!kudimp zfska&&i+h;RQ+k1%Yvw|?N2*npxg1Fui_JYyWbm|T$-9r^|_ArI~}yb*F)M-si|of znR)xoN@QCGl3{UXp6-6@i{g&EyZ-1GJmXR$-fl)NrR8Y-<-&iQxljsgFMuFF=H`l-(7f>(imP6jB@# zjmjW~IKe(}oO=VC_A0afLTX*=^BO&&+6Z-Hmarj)pP`j3nG)BEDul?o@)etX2WQjM zJ*{*^pWo&RFtqI{ZKB`p^}8e{Aunx{w*bzuI^^(ryz-dD&)9^_ZJra0%e@c!GBBI1 z)eY5=+FrpBmhaG}u_B~04uF-+Sp9Gg0Snp#P>`lqhr&b_79fpB#b~>B0gpoZessV<-h!3$kYf6JESL_XI`5G^}L*HrT zzdSN9UX!0QD8D;Rii?_7qA%=Qc}6gRnyJ4lb3WzozY_VTe}(_=19d}QDPB@fd%-<3=*I}BL zIsPay@@@B&X64aQlgq=H_{Y3k<_h<@DHNu}Mr--2TK}g7d)eX0S9D8sspPYqdP}GH)p{hLq%2wdhy^)6wP>liPXD1J$8G`q%h+9h zzV6bM+vPr_5XwB3Tx^!GM>)t3#)$v!Y#1y_MSF@2uHg z;MLTA8cW6Vj@{^P7%m)|=wnu}xg;4!P87cco8MadhbhT@Pg@qt_BD&=H}s$-c)Ga3 zhaOm}6;>vBDo_o12rb$7pQdAq#B*2TO&edZ?lxJO$U%66g*&D8ga z8nU6@_0nw8TQ^Ohj}TajF`bYW-xyxCTpHr0<>_% z7nK@s_~4;M2>%#LTedZsl|`3HaG5Ih8GOz!X$uVEJd zU?Op_?vz<6=ThcNj1g%2Hwo*qf*cBuO8C(qduI6FdM9q9YDVTP#9+Q`R2>7v9(AJh z#?JG|g@*J47W}4^#v(K$jaKH#Sn-gG@jT@fhl!W|6G5h0J+yoGo{5-aEXC;tDXiB) z6U;_K5Q;d&spT)18%wQDl}u+AiMRN7_CE%>mcUQiJ2%T@o7;KVhFn^*;kbQ(kAkWH5U5Y znKxZv-BDRAiDbqK9o?q7vzimb!yNZ21(h80m$cM6)GcKQEWpiKzXq5uH8*u;OMS?{ zhkLTeVC0XbGy0%GLTIdsGp~pdvr%iyPdhbqR9Jb*EA$ap_0c?3jZvdOue!a0=M1{1h#GS~Nad=n9m&Y5h zQ-*}a$gQf04!*|HZ}T>-Pd1z=9!M|D*3_~*veg6)5qbjDJcNB~+1d3ir?F;d>KI|P z#n5Bs+wM&$<#JDH#-hj$FQ2YJO8z8V`BekqFi$Bj(d}4p#$b6cNK3guUeY7K^H8Rp zZx4)uWpmZT#^e2mrZd^KmhOHiERaw?c&#(@xEjc%1~ko~G3LVELoJ`DJ%3?2j&r&X;w`)?-=gj zorJGB?z?Hnay51Wy1vrU`T$L3*i8;?-!If<3>-a#0Rki;v`zMW2{iBH*={6rVY&E`p!ac{ zrHA~IpNi-&b>iH!tbThu^FjfkyFL71XmMrRgJ#Jaam2DNA154F$u*pH7_Q#e#asg62*3Twl zrYl=d1P$7pA|Z5k{>G+N18uiaQsaqRxbWdm`1wD*`ct-mc=-^nvq!{MxGL$bY*gxF z5o&kC^Qcz@wJ+r5v9xGARuR2o>_u>Ef;}`s|C!_VGNa6~I!{otp3l$*YM)iS$Hxu$ z9SObu=?;0>eThi!Bqh=%TNYer8ta~gca5Z>jRO&{eD1`vTLh9$JyvK~5kNS}9Pl;o z>cg)PPJ*|2x@AZ_j0IdrAL8+Ph&n=x-&B0O5}s8rtZbAw#0(YD>XOY#M{ya&2J5QX zW88#}pCPd(T`FEUv zI_TgXNa2ZNd@E-9LRbMsL#LW+iL1ZJ8jhI2cA5jH+Q$iZ4$~k+LD+^>JGVjQ0~g5c_1(dXN-oB^pEkh z`R`#*BdX5TqvEL%=%Wa0iP(TFK;_e!)Nq(J^(pgcnZdJ5yJ}24z%UfYjMxr)!m)OT zx1vC|Q5YpI?7fE32Yz+or`YB`W=?ak`NUNnEpq7Sv|i9B%m%H~KS92kIxv9H#GoZ< z924;XG!Gt!UYP0)-$Uzv8`~?9m1>VY^m@oEfdPp7-a#D} z?;!Twz5}Eh*2OLUSEewSZOaK{9CBUqvJ&P#4dygj#5$0k=Ah^LEX25Vx80baI3lFP znDkZFG#>*hRzjAjlRq|L^g5XW*%unFRms&bb6q73>0onypNUq)b}zXj4Y)XFvG_0P zmF;oU8umwX&I%1V#+hJyx<&b8w)8HmLjWNq_grs78PmyoUA*LnN_ZxA*ktq5fGp!A zs;^E~Rc3@TiF?*7CK$?u6oGJ6=857RCnOGSd-G+tl}ojeeWA{mtlPQH)Wr4IG3Us= z12>=d8ccYUm@YY1c0M!RS1uoDbSSwzXh@}R(E4z(c(MjQLy4KMxmTWMj+)s0r9nb% zUZDe|`DJl1g*8;NLQ8?wl~T|v(a@;Jx#y-c0#z&^aINf{l#FjgP`MX?_SuT4Kk>tS zK4mEOl^TR2HZjcDdPW`luEyPY1Rb1v&FGIq^MjR#Q@M)a!o?icy%5$jytuAJaAnq+ zz(c!=>UV70QeF2u5N7CLU&%fanf8gTJ<|S^o!@&Iw9TgXWkjISKsEA{9X74c3aJ^0 zsOIHoXhHJ^LFsBmGc`jpQ@&2%5{;~d!aI1i_b5SQ3dHaL)`DlqBMji#iW7wbOE|#; z4W9<)rOuZsCCP={gtf8gpqeXYy;A3t7mSA;5WI;G;dhEP8;Z1x+nbAB36559Ehr_C zrG&vrUl`xLg+J8BPlg>f7sGKgv$>@nQE^6m=7j`fk1lJB>ky!{z$4V8-BJP)hAqXg zWm@v0a7>^onuyY73PAN7_4(i{AK+cv##<`{w|;H&r+)XMfIOiO*0NYci+h_r|4OB@ zC0XnBHN_Pc*H3f(>8_w2g5S`2w;VIKxxDwJFWaX6sgxIO9m#z1^A~dzGIxmA!~ua)q*A5N?6+x)W9RQ69%w&8 zi+rEWiZmm~24FLXDrD%Ju%`$Q%5$WM%3lii0-H-4bjMiWm9!YD{GZ zdh1PG^+4PpXYWWzlt;I*!;ssoJrHms7A(s!iZTW++2oj#Q}2o~m`9&Hz`PsAIZJZy zOx2&jD1|kCP^*@rWgzcNd+y_y?2#m}?Z!ELv%U7bxMV}heMe?)4p$&XGxXw6ff(0oBsUh`O7!$J;w{AFg=Ol{@~oVNU^u~3U^Fyiix9h3Q(;$ad7^a z+V)>wQ6yBh-Er52`X*hJH-A0b;Ez&BS8(m<>}+(3_UwEl_@XpXb_PBah*2Qbvkc3 z`bt8VX zW9hWi)c5@B7z7@M;gG7oB|X{K08b&&y6@Q!Phq;E*=(k+bJNEhHaiC4{<$Mv&m44; zW0-_@p^k1RxuRyZVWp{AuAt}Oh&Q(dg2-o)*K_rkJEt@61%VHD7hVLwD)Pu2mmE-2 z-jJ;O{C^HY4&-;y}SF*_me%@jYr@b{s?Qg{Y@`|W1qb|vO*;Gy>S=jlqX3eS|E zLshsEhs&t@7wvLy;=?=BIgX7J$kKDU6`B(rRrfjFy8=K9@U92d6iOE58Vg;5Q3Xiz zV^FRW#}!Xf!`GlX)ezF5R+J+6ygT?<58JlTepEr-pwxd8`K}?XP?!gHj<_aZH7x6DdE6fhmjoE1fn{kYxyhZrmLlje52ib8V(m9>WYaweA-br4m^ z=SK$9tT7nGX6L;2TT~FRhZZws?=5Xb-^tAhN25xC5Ps8vr@J@qJo0lfEx?L_l-$WZ z>(9ct-*H>6OW1Mqi`2&J+_Tf(uJc`}p%9=E2cBlGfN8JZE!liz{~lsUjzM=%-;Y^XKW)8XrL<^?l_48CST zS6Bdq#tDh?J*uM|InY#O`Zbu^Sq~_)66dr0@69PI!nygkGwb(BeCQ1L5Z9dQpZs{( zl?3AAA7o~zBCulbrn9ZT3jM^&4C{RMQSO9W@PNkMUVI{Up5aA>zryj!2!`eTOrxT;j%Eons;+hc(Y2Q1e$_MR*F=+<_P z0yCt$u~JE~nYZkHH>%ZSmTLfiURtP|poP%#*Grk|^5}&yg*~T)Y7->}7K+(4g~5= zBte>(pV5%faEuB~z3XNljBfPWjd*L_2RZAUk7kEJ#V@`73my3iS_mgbQlSv$EkxS^ zVnVBqbHs)Wl%CAD`hdQ=K&K7^tNQX~9nn)Q#7)ee>p!LZa{M|n*J?_jjkS9Ca2{Wh z>c)L2^_y3$J?xDB*kYI>P7P!?;c%ad(Ajv)$vOW4w4k$B|7alN{1!ug0mPhV&cY_s z`73;udlg(m(tK!p_%H5gjKWk{8W0!R@4>@2nJlVXY%98X0ov%CCr~^KPaZTQxst~D zKJTkP2pX8!E39fVW%vGFMz@_bTLXRD8U=YY& zsewU+;=^$-fjc}JX5|kf6Bp?hzY_PU;9H2Zarla9ocV}tN#?i zI*;QHM;18gJ7ilE*Hk(Crb@vAy?N2X7g9YFtkaOOyFy3dZC$E zPOxUGhC=4KRqah_jrRJ6SQHZ%ENn<^hcq@Mz=obcP7x61G*;YJ{@wz1e&!KKrerkn zcvJ}!y4>KVP}4wvEWqy16^4>`V_#69uCR6{rouJ9{Y^=Ese8$|E}eDtc#4-L+{}z` zGu~9-YT`8@i0TgpQ3c*zJwupk*mPQ-pjMU344`hQ*6WSyEsZE0QcX41*^bID`vx%m z1}(Q94E+%w*xDZv9k~eWiP!3cInZ{!v6)GgvXDuVyv$_fk4OFK2T{eu*d{#O-_dd@m9~P26;3Kk-$wM+37+C~F;GiaLsJ2c2*Ca-G!6TBDha zdwQ6nIv9J(^|b@B@NpElb#S=%43!5fcNjqF>BbwK@i1ih3(2n*0FWH&)Zx!I_=&85e#L0X z^8^VC>(jw;KL>p+ncQLlw0J+487dcIauQK<&TS<0B##(4SVe$TxC% zD^+9&^tB(3%Rh|$_*tNBVeRD||2%Jh{=*h?V6L7K$OZp(uAc1$T5@i`lKk6~ize;j zfLaH=cA8JG$VmS_b|WB@XL9EHUus+b^B-=0g2sV&?%g+;5ao^wBskb+y}!lA z&iqo_*?oD?JU^mh(RAKGFW-Z2{{5Vc@~#y<+f?8B4RiNxRY&iOo+tCkbvO<^{``wx z@i)I)qH&y%l*Ds=SII`=clUm00cc!QMjjogpRM$b-LpY^baQ>a2}f)$dmO&oq$fOY z=btm?A_OLr2ak^z;blbM-Ek`ITG-M?WL<{h;tR~&0dh4#uLRBD($KUrJ+x3Klmg>G zm+Ck}UC?Y^dw$}Z&%DIXy;m<*%zk{<3E`$g*gZ2Ylx_Rgg_y=SXLP#@dY+r&J?yf3 zzn{G;_eXQxMFX}3m~+}RE+FZFNM#AoRQz)WCX%UQqz9)%$tm!6)|pdVq27xr3`{To zUAEAc6Qdd#Wi=)Ul-g5V|A}pQ+(E)UQ7XVRlkslMpaV_Ic#)r+lWv{|_1)fwVEYbj-r2IZYc7C(}$OFt6#YwckK%mb?j zt+#$GJSl`|8`Glg{=8Il%Ss(Jo~3CgvOl(*8=mBy2Q7B?wgXS#6G1szubkn`w#HQI zzy)b9W$RMh4QL$N%qD^s39)nK&Fd|h&QltCs^*KnBQ8&!1Y}f>qyMN#f07GxeJ3d9 zkHbryy~t(9LYzZ^S6SWotp?}dv4=S8R!sXWzdr6(zt*V*D5rO7XzgCA05GH!3{!JeW+kCX%^8(S*PKE^Olkd24?pB z%FMnr=`kr>o!PJFa)f-?EZW8glX>szYhH#HTxqUlj+V|kMl8T8kd@IuU#_Q3a1{K8xPpbLmM4ZbHWZK@=9@Gg5`r4W%lvSxKDF` z+>W3IF~tQv%UZOP^bF7MhiCkgg8sWSezwAjG|K(=e+mp2&UyvjGi(rO86!+&Y;a4sQ@2FSp!|khVu!+>H!=K z;KNh^e3+f#f<_F-dHV2G`><0srukY;WDpLA(yZI+^&)Bhsi( zRdSLL?hPZcw`p!EsQ?J;w02OIBZb{1ToUT1kEBHrKbhf>*M2$_ig>C=F7 zFn^{9MR;lJ)kt)>>6jQ?$~8Ac=Xhn6FQS`x5k-XqmDJ5K3#`zooWKM+;AT%|SVNoJ z9HIr#5BwfV@=w(8DvmQGJ>rfYX#gYi3{B0gFHi|rJ*GVr^<5Z8KUry!<@Q#uLmyBxdqAC9G^dcV(&lfHLfN+!m+ z)A5`cwtPUAWO86+TYp$!r+5TT@UP~QkLqmG3~uz2?4CP}?+gCtt^HxZ3-5IPx4cNH z;O*uq35(m7iKUL!N)okdqvcoD|IWWi!V6&bKslh-phlCVlyn~Iwbz|Dm6s2ted6}b z-cOb=;i>B>NAfmM_;-rmkgF`udck8SY`X0xe8HcqkvotWcV;eK(hu1l&bS|3QkV^K zT^kfr(r2P)S8dQ*m3mPLbZ#mU*7nRRcIIh4Rb^*^!S=%2@FcIwMHSCg^C=^*d8tRY zE>J|B08)Ek>!g8aaH^Bi%}XAJ1LD;Z9b0mHAGArDc@6GRunM)KJR-(MYh0~m6_3gs zjk}ZCE@@JD97%ltpz~?epH!Hjf+{SlrMK6#--1w2b&udC&pq29HrfDCVXkk1ta0RA zlTQiXv6$kE%6&V()ji)ywiF(6)}T>lL3qc8mhMtD{A`c z9PaMip7H5*`!DS!CCe7SJ#>TMrr%2{R31&-OXgfho>ab6I#YhG_W?%ib$As7#R6Bj zlj?K1*KdRnabee~Xa}&ev>-LWL8sNTQtY$j1Ee0p>nG1UJ)UT`n=N(>dWW$4D%p9y ziKc;_w*TGQ{U;9m^W$s*yCh{+Z6{L@;~aU(ejvCYx6xOsuetWS3X9wC5=&Kdgt&@W zDz}>-iy5kK%s=_)d9nMyt(q+<>d(%c7_nB+)6ujKXgl3$mswdNo4Vt@#cy__mw!&) zdm?cwtbJYLS1e2;vZ=k=7n?SFb5O%jGjZ0qJcD=OLRco3ACbo>=9b~B9z7Inbr&3v zbIfgEvuer$p1F?y+zabRT*olCj6_W*0!8rOB!EjcAj)|`8u5Q|a`xd zp+t&IQv1nLgWEP11c9>H(%s(%d8*gK7JCpPTDM21z_0x zO!(-Y+kDK^+nNhEYHOllkU5+`E{r-i{ z6eJPk8(*ye&$wNb5iqDyq8CsQXeyB`z3HHt@drKur9}#D6@K9PPU}6J6-?_0rd+zM zqvwlSIPyi-xUt56n>7;O?p~eE^^tM^q>qT$y9X^wFD|mvJ2}tU9KG>d<2SDG8dH#G zSce*P-d<#+3kc-vQI1>=gT)|pj~IWP!rQ|A$-5vvJpx?9<`H@$lwc;&B3s_7Ll3>pSo_{)iUI*JdOo+U0NZ&Gs-SYYoy zR|n<(D_b9p@?H;^+4E{kn`BP@ba?C&=qniJ$toD8N9rO;pT$dOu7F`a8I(~`ip~&R zC9vU3@B+JRviildBW8v3Y|eo-y7xZmi-k^qY-9;a2Y@`_=dBHzCtBUyjeI{x5?5{0 zQ!qg6PdTsCTm1(3(44xpV5_^zaWows*M| zDmY~o@`R+mr*89P5xpMeN@yzcaz`C}0KwV3w}u|M*%=Q1$rJAt>+E$MJDw#+?NJ_0KzoWg5!2w@vB(XtlW56{&j3~^{ZkRhX!t2uWF1;aNIb!D*5js;wHsA5b zPL_>}VwF1jeLPmo5#n{ z>F-f=xuO-ePxx58Nbk(og?TDaEOjIwyyAD^*KV6j;kX5Y2d;-%3*9q zu6CG8@DOavWLccKoTAp{ixl_U+aB1kJz9XY)t{8(tvx#28WpEO;8)))D z8}Oc>!5s+UdrHg6L45V;V)}|>-ZuLWF42UP?|V^?XiYSKClF6a-#pXAYbvL#qsKFv zfP=9r#|F8=;)sG+y-#BczJeZl6d2JPGIU-Ragr~-V-QTVb9r)1i5iPln z*~kghA$J*vFg9j)m3(UNXxw%P(Gykee=C;uj6PbyDC}u0T5%>nc@Sm)ubjz=GTm`b zVD?f_$duqJPoFrjc+9bW5H_w3KM?_2n&U=$hTM+b;QvzP#3fGeHXHkbHu!F8D=<{6 z)uFxyYW1ZUkN1c&M|sB{4kV>W|EvJZpLAYc%BJ)Dz@t2&;4>v8g^3Z~3Z^b8yE+H< z#@(ttI4~s7$kHZXZ38AD54f`B>Xk=UuFOo^BMP+Hu-C5;r_vWlmT8(guxt8 zOgzs_Hk{|-QE%(O}}ioG9?dJrliNiPU=TYBl%lQBSp&V z{RL4ZaCFPoQv^rAvJ<@VE}!Uf?ty)DUaDkp;~XiD5kQDzX?{DAWAZVDsX6r@DiN{iCy8;W74LZm5G^nOyl1`Zs~+j874NwYf8ok%_!2m3 z>nk9p7BSZ58YP|QgP2B-u)lP=<5tG*1B^ZUUQbn4!S{eeKRnut1OM&9ci&@06aft| z@7p_UpNB8qYNgh_?4P|AHjb-%$-L4L^3Ex0nD=Xq?(@czgOvlRUPC~5B;^4R9+3`C z!ym5xZffyVfihpAm-ju`qyhYe0L}Q@y2;T?M}XZ;eV&iM6WEo={tM=;!^%?>^*>+)l=FlYW#~D zfL{mr?WEP<*PB!R_NyQ7eogcgupp+aJXeL?swNcq0JJ-c^{qn>$BHi1zdd7f5TKlb zlr_HFd;d92-(>J{;E$pm{aQqgz`qU91;~s->yy&{@e<#4pWHJbNY#9-cI3Z~{Wx%t zu{QaN@qc|aTL4^8xQoo7-+vwZGe8ey`s5z`+mnlq|9{Kst3deQvI17g{|}axLxjty z#$V)~_b1WAJ?>uNRD^*GnKj$+mCOQk z>{L|yM3H0C>oW}hBPj&7Y~Iv|3?Ew%u?VA+)~~eJ8SAqxYk>G?p(%`7E1)!v6_Q^n z2n$A84Yd=aoOV2h`^GwCr57H{^E~JY*=3JNEw=Q1f)h{kjckFC0UtQR%2V^kB;A13 zLtbfBRF)U6bQul!dw~1&OM-sg@UQ-B(cE=+p+nX>+9%21BkTZlv<48Wjg3`5iKJ|r z>&1jrXTE`TOkr5N@18sX10!f;)C|p5Fw{Abf<=o$iRx<;9^~;pUyku+eBQz+BSKIc!8(fYP3Leo zZS2)R@MxQ)fCTQUhRE;$gnDXIr1rPEmapondF34254Z`1C}A$Ij0hhYN9Z^S7qSIz z9Mz~VoujPqSsrMyCw!o>2)S-UNYp4La(cEc4$i7caL$gAk#IkwE{Oq`rhNV85%f;I?R7I`0NiI)rUNN^>!^Nh`D?l`Xe>gyamW& zhRnQ}6z#1;tFEF>P(M&7sA@S$9(N+`dOp0pyW~@E;hK<-Mueycdbk25{c)g*eZY`z ztJVRekM-u8DS8O5xaQG^JOd<}j7?AOQ&`nT(H_8p@-SJ=a;&_DVIzr~7f8L2Wed%z zs(T*b+dr-Jtrj|lU1|*fWu`KbTN*pZe>D|%QJDPOnCLNsF_~&^K*IPni8@hES=NR_wpjYboQ<5lXIq= zO(n!FzwedgeC|%J7(Nc&7TdLPt2wxUESJKg0l5}1cO5t1;L(?H1-r3;m=@ zCv!PrqCJ%KTx}0E>T>9*?U8w%W_z7E*E>_a`m-}_d2vMJerH86#$ZR)ct)Mlwgl8# z4+dKEmeFlHju|(6y+uWac_Dp ziaBSOHdVX+sDxiDM*4eI^K^9c()naSsV zk}9m!TWfp{A(?$-rpUF@;o!~Ehnnh!q{qQ261K$#kp4bYO*>5-LeuAJnHXwLVQqd! z8G-WgnS{g~T6(bS>EO)NDRRtI^ihe1#RIC@y>mK4f+tU?9mPS$K>k0)-BLfW zYXa>o_$U=b5wuXk<%--dt}mP?p8f!XoH3uEcU2YBT@~#+OiIFc?BBB9(NYN~m-5)< zR_1l&2I*Kxk=)!%cpx9!)<4~IcGQ8v=`oA)c=(|_q8icU|~k2Q1q{B=XhOVpceuQUirEz`t*TzhtR!JByKAI zq9ExVx=N9Z1v0RUkw##*%!uC(UsR-Qv3$3eFt{B}xFJ2feL(3Rwh8_CN`DDU* z`Y|E}<-B&XA>vi3GeY@!M?v*Xc=1+tFkM_PZs_7)^TM@fr#X@y; z1r-sLUW2g8(m@ElDGNvmAP|t27!grvx=L><(n~-f^r8r;5RejDAQ6#H5?Tl(Bzgbn zDna()JRjco!+Xy9DQ6h}b7$tx@0PjuP6ZaWtfd-3t#s{nN9AbTisqM&ys4Q!~msg3M|*2vNVu!Jax`Vp;YRPHaAhebo2$X&ssuks_*rg z+r066v8Y=Ik->a)Geo~y0zA6q3Gk4{huBNv@nw8u|Gxt)ZMV5>O(QF0LS11%>TwcO zp{aj}obkD@W-MYSN`@FGoaphcEWi~qSJ}6ETdQR4gP3dL|FVPC668rzQpVUeEfLMV z2}^XkT$k@3ll37X{DKuZ77C`fr=Darp}LV)_!-j(27v^6MrIE^>+{t)Ho2=usNZ}z z&dAMl&@C*x;h8OWTsK2DuOUJ9Y;g^W5)CB*Wp)^ulaL3dc#u5Vn9M4FWDs;Z;M^1Q z6kwr+!{GCXAR~;}k-TJ1k>jtKrDt82_Ga|!h5A+l;UM!2n8ctScW0Rx!*Elj9I`FeT+?}}5f zD1<|}7B}6=l%h{)^=0Dd?-ThG(LIUX!Tk}O{b`Ky(_(>hy!A5n${3%BN$tdEu$pc{ z;LxhBw?78()FhH@L|As1{pwcBrAN`N3ssA!jiORGz1>vN_nVsEJ(K5&$=%W-r+B%uvOHEME?+E_EuU)Hj2c)dXi$mBf=q^2wNMQ01a36wZSFN52pF(z zC}R*Pw*@p4smI2&dcT%~C^D^a77_RQcUEE*hvhNI7ocM|`DyXx^!&oN?Fpw+tF54@ z*;-Ht^|7dAfQFj28D(NiFtX@RY10TMx=MOMvAIm2dU-}&5W1g*dJVwk68+^G3ubO+ zv$uNAhQ8;(X*_%&Z71hZ^yBO{qf&i@BLx5T^>?xf=^(<{#%!OeiO{MQovL@LcBo;b z()jQ;B-d1n%y5@Cy`2UpOP|Q0z0@}S%_=wf-K9dYZ2p~|(L13|_nm@<==oxkJ5`*b z?3uS}B&z3sBL#MHPpS}Uwv5H+eSBtVt`at~bpBo7ImJISRZm3}+p<1d8V{&tE;xe_ z7AV-?qlUOA&MA3%zazhJoLk0%;HQu0OMMV}L2?qi-EKBm^Iuo5AYA0U15U;U?5sQI z%)Pszk7|QF^v*8IrC7qy)vBaX8jpxqZ9%2J9gGX>C1MvQ`!xufpBDVf49TjS2n&^Y zwA08&yIXdC13GiciE`v#0z=D!$OkG&w`n>p6E#5Li7LlWm=-X%U`}Xpp$l9f!FTwaL`NRP z+VYIV;gjS#465bXiQ<+j=jU-l=e{O(k2>Q2uC3nHnG#MT)d%n*Z~X}J0Srlr6+@En zXlzY{b{Furb}GL4nW#YF@@xm{N$f51v92P$)4z?PH0(Z%^v4z(i7lc6v*ceO<#^$7~3Kau=LqAg#2MFvRa^3*5P z6+%v5I(tb3P%zXEFBBVFD9HKs0#^i`9VM(AQK|R=WZkn6Q?zQeeLfH1ck35$hdm20 z*|E_@CAbge2v{qO>tas#=GC6tO!V@51!cn?b;G;jdycPSo92P(_xQ8wKhn%r)-2zH zP35{H^tCdkQ*X87Kepo}aG%-T=5e0oIHsQzJ)E|{KO*3VmQ z?}MMBzZSMTG$<30F54@jVWiziF8`n`BS;a8RQlf4Sh?0a;{qb}9P3y@q9U3=ZAs2B&I9%xIp=_j>n!(hmf)Jt@3yWK0)s-7;u@Fj>>Rgdj(-wMnh6bV zHc0GlyH~WdeyS>f&hi^4mSqWm^LhnX8DGZ?Q!&d!ox8vqt*eI1ccO0s5R{ekG^Zbz&@{|7tF`*S37|-%4wAQ zd@NwIa!K)Yf9b;S+Fy8Bh}|!(hG2iSWRhk;UpVlFGYhwJpmuuw8i4(I8mPjhQ0e5t zawpdEtkxbx>m{Q2nSulLAxBGau43R4?J0KF&jS{CGaz1C`%iaVUtQN=LDAC&->L70whA+2s)L{I|~Ks@VJMohz#eyr=ne# z3u3!VNgQDY)tfZBNy$nme&T)f=@F#V$%lqcEVzu@t$;9%UH@#cY1(o9IT8s~M6h)JOWE z4GGLow~p&j#X8gbaQt&U3ojOeQ3`Yio#TDWk0C3ChV8Mip>Vwad&SkqmH;FUy+hI+ z2u(3$;eP4Oh+W_xn--IJ>kPX^s*nm{N+l+)*NL+<=Sw@EN0XB)fid>QqA~uUf#UE7 z9a&4O(fXe?gi9V8Rn6Kq((pg(AM+qdbs+^#P#RDPSW4u)#wM-^D@LRh>06O9z2E!& zR%-B;jP?9hvTOF>4itm~11Z7C6cW<7U7fv!$y6;@#>3t{J^f}o^PMy`B-{qTVFLS{#uQY}e@R5^} zU7+tN+bi78p#fYk7J-oSV+@0WLhjD^EMN>Pc;;LtJ0+Y- zhUItubx1sq>phA9`2yuWoDQE;o}JQBV`4!!#waOybE-sho{TQP6#UiPMm-jIv|f1v zcj|@^3!=)Mf9y6N%xsIeFi`hG7kWlht1h{6RHvN(c0F89u5rZ%^=Ku3iLUe~GWyCg z60=IihbtdvD@QT*6r%QVMim2$`0;#&td}K??nRp%w*m2J=*F1Ds=RPyE`qygo5w$$ z|5Q|QW>2Cw(XntM6m1?lSZUHA!CkahM<3K3V0p}Fl-F#8YPd;OWK(msdGZTuj~oKT zRcA-yrD47SFRQ+Vj^zHralGzxQ&P07Kpx$#CP2g%uHKh@vAdv{rvm8+ui%M0ZCP{5 zu&g?pM}O3bHoTgM06e(@xWg7W-F`+60&^GG{q;cQQ9y6fR?N@rxu4b4>;{ROo@L*9 zu*){;2I#OlFoBGfYAvs+*A0UV`IkQh>o|Lk`$fX;m|+Xe5ARZ(eG(A^cCG7R&$?(< zvd4ssO<3^-8hjg?@mbWTM7?J;#jt;StPP~O55GD@y#kU?r!h=@Vwo?dIQ z>3*SbAwVnB1{<&%9kyTkFo2aW%pHbSK^ySheKW)$AVym7y)3dtI-c^{NQ=uB?N&8- zpSzr&-U#yV#HsM}F27e*c-gdc&0bF?H#R+l3hkm8jdkqPcQT*6mxZ|+6>A&!cP!k; z--X+=#Y>zSs&E&@)_Zm{I8GzR@bsH$*;Km}L+;q6xY-Xn3^hnxQO-rpOnGq+_pwsj zz_vKsr6??3wzK~gq=IjgK#V58lh^pC=lA(c1_0lO>c#Xu81#ZK^DDXY5-FyDW`pD3 zqN#xNUBx;t;99SI(&$enE213vv$S^~SV%MRpg*6K=lmCSGtlkB&n;r;e_7E2a$IFpYG(EY<)>ssURMnoilq3!=v8_KGVA`ZP-YlQP6Dqh9!L$mq${+kWJzJqX{=A zD**3oB1IW&1M zP~b@)R|D=|RayR}I>lzT6t->)kAbmju=k#zEQf5nth~Owp{PTD{`o9_q>ALn>~ADp z=$^n7AKEYQNnO-<@Gmo)^XAw;lnn4bU`kmF8()xK#HUX+hzQUgkW6Utb$U$JdNyH&Hn2=X$GcSCIx>x0 zYQtNOh(cE}c)zg_Tw3Y_iv3`;Q}SGa&FZwFG!HDF>-Pu%fn-Qw!qp6CC{Zk%qBBlT2M z86Vh9A)>~kr0w@sN{A5h4x+$gB@dN~-*zk%AbXD#g4MUi6AI{eF4De4F}jUS>ts=` z#@OH}&h`a+^R$72Nl>RYIh}kgtJ@{Janug`$=C|0U@gU$b)lmMh4cyN9`NR89#F!C zoK=Az7R464fV=aK(D{zbX))=S43PuXA4$;*w~?zebzYhKUND43M*=i;f_GXtlo&2Y zES>N((09dipbQ>qH$xqESumNzl(auon<(uf{=BXNA zh0f#xRYrJFgbJh+6{lq~sUM{hFxmQAOAZ1`Z=P-@H|Gq@JLqkam2@hS$URXe&VQ36#9wMEP&UIDZtxxVt{nU}f^eN0h5({um%lI6!|{w20- zwJ=fe=IegVS%S#ehn!!9JG80bfNYag$R^XmKN{_xw8$B8KM2Rf%WjdpKSzB$tub1;E#8pzcG}g2Or;un&SK^dJzMFM}WwXXjfU)Guip}kHG~eUW=-{n9z-h z^Ah3Ps=NaCXodQ6dMtKvo3}t|b|W%eLQYc7Er)Tz8UC%lV8iP{^S5o!n&0QdqQ04q z*b#JE0=TCZ#9ua-Qb;wB+29A?_>*L%Fe0l4~WllA6yY^^|M!Qdv>M_<3Ns!1#$m#cmeBlB=7 z2^#RrUFZ(zuF4`+-YO=o~V35#I?Jx*l%&x z7Dr^{z5#zKS64EC6g4%UX(p>Zh@KP7;MrY#s5Gr;2sted7>U=q{kRK39^n(Jd#>F$ zN-Nb7woRl$q~%D9$I#i>a`1DmY1h1QWrgs_YxpaE z2Mb>c?MS2qAAkZyZX7i)3dQ$ZWw)f|Eu3G4uLLk~4o#}usjOMPh^=fpUtUvOe6dd? z!)L*}+c^5T+T9}m2uJ1WNxwKjB#xt92gWJz)k%@cb=ofQL{K4=;P4}KIIRs*06$IyxjA{t7q>zhtVP~f! zoUMCTUjx!6Tyo}^PV8J0zuypRp2 z!wf2*B3)F0C@50PA-js-Ebw($N1C0J&Ul3EDzVyTfVI+RRJ+7VZT?mSJO5d4*nCO1 zOVwW9Xoz#7&7}EUUxuzHE=6CcGHck+i zUV)@!v4WU3ZNz5KL>XfqD0x!@MCHiT?|uO)1S(eLdASo4gu12kz-jls*!bD50bH6_{dZ62r=$%>FTRJ`ZhA3D;B$vy};CU$QRi)*eWACMw@tmG|L zy#qk#@nfa8&(1-f8(>F-g?PBL3ePMb-}NOc(N;N8R$#Pgs`Tt;d$jM=c4drVUuJt( zrf7z8N~ej0Cc1zWfTtBod!$M@$l>>0#Ribe@1js)>~X^}l3AS;WEWL3sw_&z`)#Ey zi3yoc$JG5GMo;5Q|6A`LL{hu&H<;(hZ-N zF|!JVC)}plc9yP`QY8WfOfCCvoL|(mew|^k*o%y|IT}$w3^0#UYmhws2k)gnxjC)7&NuWQ;;gkbBk;?@o?<8DbUa#Q znF3g=;@o+`({wYkLR%8BQE_27m)1$!Jexd?Y8~lRX6a;a0|Gv)sJ1`;Z`hv5f>^W) z)CsFwYDQx{g+Jn0btb>IthzD(i7)KAuM||Cea{y=)q3gjs3i5)L5Sg*@<(*p#-w*W z2|u5J`Clh#j|z6`v4kf46n!i|wyz|#zi)E+w93vb!2Kg~P!qT(*4AUK;Lh@ifBq*T za3!JrtF6>c7J0h{Rs>x~VC{+tdH6JR z{mfZf{x$b=-5jlfh#csnzgdumV$ny#~z$Zj1F8}J!{JBgkKSTj-LH3H;_c~td zr}n*t;~IctkD~{kv*Osg&M`oJ;l!7(IM&UamHz*$Y*o5&LKU)xL8YY;O#@5BYuy&r z{fSMlEBx)YUb;&hvq#B$qa%kOhT6gR=-iv!llV?Sf^Z`-siTPTGujz&iJJlY2>IZ+ zCd?PObFHqZk@H@v<{d{<2mX8(rzBnA=)J@s&gy0OW>$>cv_+jZld@buyA3G;!3Wff zn>I-asQq!-pz<=MvuOA2HJSf_ra%B#xMVUL%6b@pyXUshk4;uF=wVI4>M^9hnl}Nu z4|v{#K_!+uwGx>eIB$G_1&j!!4GfGb&W)$!FEUc3$?ba1{^DsK@eI3oy10COyt|+d zAt}zwM0-u%KOi9welLxmFfb~gc<-?X3q&e?4&C)Hng3G<&C8kBC`aK8`)^VDCXp-2 zB{>u7*nMx@PRA%Hz6VzDiwoAlRODwYJ}}NDws=Ozz|-;KHR+#sYR%VY{@1R_{1=O8 zem`I!bGS~+hoB)x%yckvj|WN$JpYmSzT$KSV#AXXip5s1rGQg>k3GEFY92w-L^pTN zjiM+6F9pS)2c<8)uAxQ(G5*~===W9PG?Ux4Z+8XTmp=Yld#*G+&0-oYrzM9pT|Eif zD;)lJU(1is_ivH6hxSXu51&i>DyQE%>z#T*1&WsF<_V)TeWO1!T}ecn(?TTL9T0^$U}0k!8eOt!^A^iLchg(9 z{U#_;8hPzT=Ie*UJy%c#%AM-PpCe@d9{TtsYg_$QPR6MR>R~tj${6m+bjv?k=JM*D zl8w-%7Pti1JoJO?{g`3Q=e;z^jybu)R$O7Bn8#>KqQ^)=Y-^^qU5ii4+^kQFmPUaso)XV#Fm(1P*CDl5#Cd?DloUWZ+ zWz;Z$b-9PTjN2%DvHJ5cSpKnII*@Xu85~Tw3kganT1=$WU!T}uY^7Nn-K}0%vjXe=VGC|ljYAS zKvub^y<&x|mH)Dz4G&uuX@Z~3@hCF;TtZyDUA{cqbT5mLB(VBO%Gd*7Ql}23#K)S+SgR|F0Q395x3JtH3OsKKx)>fiaRM^M&NqjweRf%wjy5aJp-ULF#H` zF(UGCzB2T5`dTTGuYlO$nuTa?gwNsAtYDNH>EYxZwgS8dcJM9E*gf~ffQ_EjN6YNU z3D4!aW0(jd8!?AJB+G~^_IIkF6fhBwm@BM>5W#t`R2m$O^xTU4u-PfadXCCxn;`%a~Fd@lkt0Hv(UG?@6+V6 z(DG5X%RGE=KAW6t0~0+HCOs1{mdNFtyLULVvPGMIR#Nx|p~tss=Y^hUD<<$As2!%{ zT&?9~e#Ia7@=h<{=;Z6_?c!|cV}3j`O2Y8N%#YYjsxj0~pn{>!z8X~tlq>rBlvmgS z2~Kmk?kIln&4QcN7ypEiX^E1fqhB5C77NclpZ# zC|8M#CbbecC{i%h#fcvZ(aL7l%CqO_CI-KhWkrkpS8P7Ef8oNtx~>BR3USOm34Ax` z9W*l8v~V1D{c&SrV{~2J4t z@Zx=ZM()#;a9jCV^ZL>%Ip}fwp4DQPM})0|CSKYreJ>IEj}|+2r*_(~5IgREPyUvH zO7AQ^^U6)sIaN!&*e#v%L>|78Y4Ms<#a!D)z;D4d{!GK{@r zj`48l5Bajk_<(O+NCst7_6BF_3B0Y&5DPb#tRB5{o5{Fr%`-ZCu4_tn@AEm2XQYr> znd#Q*TNlEo`-w*|fpS0;=&kFHF;^cXK`P{w6;@5eeBdr};>%Pq>m@ZJa{qgTiNrJE zEtngakt`mcE4KGymfmkR;@CIjFZuEE^@Jw;kVOtwfibq*N}dqKgX6Gzf1RXaMV*~a zo%QCco376#hRphemN_RhZN9}3I%Lk^yMFUtK4wM zfeTdBebDKSe)^IL517(=s)7OAfHG`z5wTt29*>LlU?x>$yg~e7{zI&6k^Ky6y^# zo-BfBdRRe2%WypVM{7%`RA$0EE7>IOc9?rtA4y)SUQ7z?UQF6jr$+J(b8m7t7>Kgf zY*@wg=ecaS7kyo^Z6d4yQ>1jla1+jJDfnaLU2Lzi#K)0Yna>|dKA}n%-y(Z_z4{$L z9N7~{|EmWhoa@5(*;;~sid*l{Z?HOyq6}<*qplSFwHYcK(NiV-PPo_9;~T{p#C$K#FOkBEx1jrA`~)AdvpPwIwx zm%^5Wtb}!jBioj!uLG{0NG1So6P9Yj&mq8;qRpdU8ez%XL{pjCp5+kk>n;I3}vG48NlY>P@bUpi52s%~rWb(aD z*e7w&(cZHk|FAwb>VfTGZ}PGA>`zXvS*&(el~?6p*{V3e5|$jwSrhNzX}BW7n?F>m zX2g|DEBMU5i@v>YV!GJa=a{Nx;&FV_=bYQ1k#l@92`!s#FsJ>#M#c^rmA?6 zG@)r?!I5nv({hlZ#L^Xkek#6svt9pDGXe*{j}UH$szgx)~86w;8_R&RhyD)>>#M>Nem=V~}-gb&yz57bI1Eo+_Q5;F7HpFj$;E~M`H zI=3Vi+&tsAxbzTTCIWuy?<)T^1qkVEHMT6^Zq@JES7DpH*gO|In(vGDX4&DiL&g=e z6O+^qPVhgKn*Nb2gLU8X>#b7PFS5_{-2vkx)!ghS>H+XL zfDhoRbP1~l{Y^f^9eDuGERcn^OV(&n)cjTtY?MW9R85m&UBmY`SiVWX+K)S@Ciu@b z^y|QEJo-&P{p-}1bZ$ZleGPp$7rHZ)wLUZ|=GPoRup9jru1{InJk^Hp!_vI!g@x#&BzM&g0ao))7w_dfV1QXei6g;ynPMTb&9z!2RS}O z5xsyF)R%QlA2(%TAEiP7xa_KmSu)o)A+ECfo;A$OOHEPTmmX~;9}|A``p_l50$E~Y zw3VhRPTGJ2S%dO(sWa!SD?!SM_bE3@-CxC@L!42@diu&p)%h0i|7KzS8 zCOfh!=WA&FcCwU)fSqmlAi-$L2FE|UtOp-%>m^`w!CksIAd)cPVZ(P=(0q`}4J#|{mY5bP~5qW?EyyB8RmF13n zeYgMEYGr5tW2+VA0P+8nt!mqM2pP9Ek`Tw1&Olykt?(kRiW30c>j7V6jbjr|Va|Jdds|AQc9dV! zY5UZ$D(+10O7GeJCvf>|FnpE$sa!(eGTy9eEPC5!%L{TuiVkxj=qYsR5*#6hUmo#X zUMwgo(8I&otlVcroE z(0yrpN}p%AQvB90uD^xbC)0xG`<##^W!XQU6u^||rM{SCh0p8@Zc{xo57|{8 ziDq!65f$uDL8dOe?X(FLC5Nd5ceGkbKl4?U+LCqVX@vO&rptub-PDpKKbPMAeaVcr zcXohn&LdJYtL{6V8r>Mh3(o6n$>5IQQHX;r57qEP&suo41&!y3wet7~CntEqFNmQ> zkJ~3axsi9uAY<}Dxd?W#zQ~?Jbaw{P$LBnpCZ-Bo)GNFQ>aH@Q2`3Nx2=gk+E51tT zxj7SaUxhvUY4{FH4kE0;|Ne!FsiTc8gBh|X{|u|XDu)r30135%;G$Iv)Dt}qVb9=! zPAQIkjIx-(5Z}PjbA9pi@;X@xpm;O7)*rk+<}(UxB!>|N%^{iNV7QCi1h@BBuP(6K786SZ(|8heo7teYhnLbgUq-w$9%+Pc0Fls**{EpjP0MJWqoC< zBvS)^W%C}^`U@D&_ix$}7hhO$nSLmtg(_si{im5?0hK$zR({0M^g&)m?}%epAjm(n z62{XpfiN(d4%f|j)CiZFFR-mfnwm;09hFflT#5bVI?OTaY zhhGx?-*we5G5qtWWljMK^^WyMR&(lHK!NWr_H5Sr-Yhi#FuH-{NdkjsZGGHz z-6VjN5vRLg;a^458sb;t{k-5DVQU(Hod+TgP}L>gWJUcV4*wzN`^X3)pfgs0g0HUU z0hSgeEnu7DtikKTJXpV_L;-Zhfn=3atc~l&el7rPvsQlAhBcu6W2m?y@(WnIFwCg>#q0ou}B%AD^`1tjtu(TL&1#YX(HNU>N zp(qsvc{B7zWL;O&~cx97P} zt^WW^ix?nq?QeJ-Vrg2xo7upNIu=1Dl2`v{H2|T#YaU5R4Tj65f^K^wpVF=FYwtFQ zzJ4SOv3{DXEx;=wl8%|+)b(Sowm2Sq0FJlvb5)q!mb`cs6hQn?9OtW8-Y1`w>|YX1 z^sP*~HL%REEcamgW}$9fDVQMRxVI6=NfW926fcz#U^US$HK$T31i9iQbBZ`L^vF$$ z3Bs{rRF(A0lC4>TR~jLo=LWeOR15;!2d^FIxf;WT7%i>!sw!p3b+;T50C8QOdRXh# zC3~ONpN40{NTTmouV?#r2hNER9qd0G_{o_+35 zv93)RV=#5x>>6P0jWa;P4U*SylS)&QT^O*w`tWab5=nw=bn}kXSM;2ZjyC#SOLhk)(+9$=z#XTsD%b%GRMcyqGsp} z({kkPMn|6Et{O4+mj&u%WC*G&Br;Fx2t=gmdK2C@R&8hlwdc7qY8 z4~}sT;^>qkz+(!4r(LkSiEtC|?WfKx@4+nuMub}W-JYZQ+4jmF^A+r)*FJ@I@I;6@>07v{ipc6S&A+&8~%mzP8{YRf=lt(2i{k#Ni`aV>p*gFeK zl&^_2T}7yG^eKgG?c&HwuTZpdhL}0maI2OboC{}+XUlVQsocw?;6xt~7$fmcXSY|F zNidVlwqJL2c3Pg=Z5D~ck%5&zqzG_#ose~jkwv=3ZUZ@m0M?pq(rloaNDF& zB~{GbPUEqMgbeI+U886>;_*CbiZfy3hIgZ|%Z!nSoVi!<%&8hAr_i6H{ItEUbZ#M| zeU;)(XDR|(mu)KuFDSoEf^{6B-}nDEI0{4}b>!_zJgOyeI7~L3L*J(2i;hd4W|%1X zMc^qEH9#@dKIMyte)&m7dud?hI9!^|m@T?mqpk-sykNWbZh#ukoq(_o-x|hePa%C9 ziOFskD-k@M3%iVilVU{4>5!< zK0S3L%`WiGLSz=(-g$Gz9yx2`=JN`Y*2 zrw~X$2qq)&6nw-x$AyuUrGXbAR-{JbZh_UMC^R|Ki{)N!3IPnidi7rJh7ul9P*uTA zp}Tkz9pAg9e@6KH$Rj1_MWX3OChBADy}Dce5)&UVf5J8Rbz#d#V-sq(4&@QVx4Q$0 zH*kJKTg-&M^LopFd-;#}bjwFi(|O~EhuZBx9&dzP`}&EL7lHZ)F|N)_=}N`JhhDK< zdas=iz>ZC)Kh->sCem)o76cH--+VoUA4_kIG@DO?K`IW;Tx!>pU>OQV}(l4_T~!K>)JJ&1MHhMj;%F)zRC!gHU9Y*^d<=@@fxK| zhMBO`o-7KLyQ@RcMc6P?@ls8>hwIRsLJ5aqI^%0aRgq@4zKt^*Mt6RsN3II2WXSrP`E>I!gf?R=j&BL{ z@=?nQ{uOT88hr^w&IY^Vc;NQ9 z4h$LP3s!%Q2Ex5iK3?-1fx{+5(5@T{A2O64w4gM^GS1{7r6G8=WP`Z?(a@EE)i&eteEWuk6#%(*`U<$1&N#|r`fGC@GGtzQXl0&VyG*T4QaVM-c+^Y6dq`fs^b r)#U%U>mLO8?=b!Y9sj>$7n6Tc6526%x9N{fz(2Js+JEFJTi^XZFauyo literal 0 HcmV?d00001 -- Gitee From 15c231f7aaad9d7343158912cf224d806b2d739e Mon Sep 17 00:00:00 2001 From: vicwjb Date: Mon, 24 Jan 2022 21:21:00 +0800 Subject: [PATCH 028/675] =?UTF-8?q?=E8=BF=87=E6=BB=A4inoide?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .gitignore | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.gitignore b/.gitignore index f87938b..e1e90fc 100644 --- a/.gitignore +++ b/.gitignore @@ -399,3 +399,5 @@ ASALocalRun/ # BeatPulse healthcheck ʱݿ healthchecksdb *.DS_Store + +.ionide \ No newline at end of file -- Gitee From 265fe6f5f6e05968071989f282d7519a6bfa573a Mon Sep 17 00:00:00 2001 From: vicwjb Date: Mon, 24 Jan 2022 23:27:45 +0800 Subject: [PATCH 029/675] =?UTF-8?q?=E5=A2=9E=E5=8A=A0ssget=E5=87=BD?= =?UTF-8?q?=E6=95=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/IFoxCAD.Cad/ExtensionMethod/EditorEx.cs | 59 +++++++++++++++++++++ tests/Test/Test.cs | 1 - tests/Test/testeditor.cs | 11 ++++ tests/Test/wpf/TestView.xaml.cs | 2 +- 4 files changed, 71 insertions(+), 2 deletions(-) diff --git a/src/IFoxCAD.Cad/ExtensionMethod/EditorEx.cs b/src/IFoxCAD.Cad/ExtensionMethod/EditorEx.cs index 9326762..4ca861d 100644 --- a/src/IFoxCAD.Cad/ExtensionMethod/EditorEx.cs +++ b/src/IFoxCAD.Cad/ExtensionMethod/EditorEx.cs @@ -55,6 +55,65 @@ public static SelectionSet SelectByLineWeight(this Editor editor, LineWeight lin return res.Value; } + + public static PromptSelectionResult SSGet(this Editor editor, string mode = null, SelectionFilter filter = null, string[] messages = null, string[] keywords = null) + { + var pso = new PromptSelectionOptions(); + PromptSelectionResult ss = null; + if (mode != null) + { + mode = mode.ToUpper(); + pso.SinglePickInSpace = mode.Contains(":A"); + pso.RejectObjectsFromNonCurrentSpace = mode.Contains(":C"); + pso.AllowDuplicates = mode.Contains(":D"); + pso.SelectEverythingInAperture = mode.Contains(":E"); + pso.RejectObjectsOnLockedLayers = mode.Contains(":L"); + pso.PrepareOptionalDetails = mode.Contains(":N"); + pso.SingleOnly = mode.Contains(":S"); + pso.RejectPaperspaceViewport = mode.Contains(":V"); + pso.AllowSubSelections = mode.Contains("-A"); + pso.ForceSubSelections = mode.Contains("-F"); + + } + if (messages != null) + { + pso.MessageForAdding = messages[0]; + pso.MessageForRemoval = messages[1]; + } + + if (keywords != null) + { + foreach (var keyword in keywords) + { + pso.Keywords.Add(keyword); + } + if (pso.MessageForRemoval == null) + { + pso.MessageForAdding = "选择对象"; + } + pso.MessageForAdding += $"[{string.Join(" / ", keywords)}]"; + pso.KeywordInput += (s,e) => + throw new Autodesk.AutoCAD.Runtime.Exception(ErrorStatus.OK, e.Input); + } + try + { + if (filter != null) + { + ss = editor.GetSelection(pso, filter); + } + else + { + ss = editor.GetSelection(pso); + } + } + catch (Autodesk.AutoCAD.Runtime.Exception e) + { + + editor.WriteMessage($"\nKey is {e.Message}"); + } + return ss; + } + //#region 即时选择样板 /////

diff --git a/tests/Test/Test.cs b/tests/Test/Test.cs index b7da9eb..12aa699 100644 --- a/tests/Test/Test.cs +++ b/tests/Test/Test.cs @@ -1,5 +1,4 @@  -using test.wpf; namespace test; diff --git a/tests/Test/testeditor.cs b/tests/Test/testeditor.cs index 9913e50..9ce4c32 100644 --- a/tests/Test/testeditor.cs +++ b/tests/Test/testeditor.cs @@ -47,4 +47,15 @@ public void testzoomextent() Env.Editor.ZoomExtents(); } + + [CommandMethod("testssget")] + public void testssget() + { + var ss = + + Env.Editor.SSGet(":S", messages: new string[2] { "get", "del" }, keywords: new string[2] { "A", "B" }); + Env.Editor.SSGet(":S", messages: [ "get", "del" ], keywords: [ "A", "B" ]); + + Env.Print(ss); + } } diff --git a/tests/Test/wpf/TestView.xaml.cs b/tests/Test/wpf/TestView.xaml.cs index 77284ab..a86b16d 100644 --- a/tests/Test/wpf/TestView.xaml.cs +++ b/tests/Test/wpf/TestView.xaml.cs @@ -12,7 +12,7 @@ using System.Windows.Media.Imaging; using System.Windows.Shapes; -namespace test.wpf +namespace Test.wpf { /// /// TestView.xaml 的交互逻辑 -- Gitee From f133cdc38087d3c55f1c73466e7cd60e4925ecc4 Mon Sep 17 00:00:00 2001 From: vicwjb Date: Tue, 8 Feb 2022 23:35:11 +0800 Subject: [PATCH 030/675] test --- tests/Test/testeditor.cs | 1 - 1 file changed, 1 deletion(-) diff --git a/tests/Test/testeditor.cs b/tests/Test/testeditor.cs index 9ce4c32..ea0bb52 100644 --- a/tests/Test/testeditor.cs +++ b/tests/Test/testeditor.cs @@ -54,7 +54,6 @@ public void testssget() var ss = Env.Editor.SSGet(":S", messages: new string[2] { "get", "del" }, keywords: new string[2] { "A", "B" }); - Env.Editor.SSGet(":S", messages: [ "get", "del" ], keywords: [ "A", "B" ]); Env.Print(ss); } -- Gitee From 2cf9c13a189fd66e610f9df0914c82e969eded60 Mon Sep 17 00:00:00 2001 From: vicwjb Date: Thu, 10 Feb 2022 20:57:25 +0800 Subject: [PATCH 031/675] =?UTF-8?q?=E4=BF=AE=E5=A4=8D=E5=90=8E=E5=8F=B0?= =?UTF-8?q?=E6=89=93=E5=BC=80=E6=96=87=E4=BB=B6=E4=BF=9D=E5=AD=98=E6=8A=A5?= =?UTF-8?q?=E9=94=99?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/IFoxCAD.Cad/IFoxCAD.Cad.csproj | 2 +- src/IFoxCAD.Cad/Runtime/DBTrans.cs | 28 ++++++++++++++++++---------- tests/Test/IFoxCAD1.cs | 16 ++++++++++++++++ tests/Test/testid.cs | 2 +- 4 files changed, 36 insertions(+), 12 deletions(-) create mode 100644 tests/Test/IFoxCAD1.cs diff --git a/src/IFoxCAD.Cad/IFoxCAD.Cad.csproj b/src/IFoxCAD.Cad/IFoxCAD.Cad.csproj index dd05c56..e04c03c 100644 --- a/src/IFoxCAD.Cad/IFoxCAD.Cad.csproj +++ b/src/IFoxCAD.Cad/IFoxCAD.Cad.csproj @@ -3,7 +3,7 @@ net45 true - 0.2.0.5 + 0.2.0.6 InspireFunction xsfhlzh;vicwjb 基于.NET的Cad二次开发类库 diff --git a/src/IFoxCAD.Cad/Runtime/DBTrans.cs b/src/IFoxCAD.Cad/Runtime/DBTrans.cs index 90aa274..5c162a0 100644 --- a/src/IFoxCAD.Cad/Runtime/DBTrans.cs +++ b/src/IFoxCAD.Cad/Runtime/DBTrans.cs @@ -58,6 +58,10 @@ public static DBTrans Top /// 事务管理器 /// public Transaction Transaction { get; private set; } + /// + /// 文档对象是否存在 + /// + public bool HasDocument { get; private set; } #endregion @@ -67,13 +71,10 @@ public static DBTrans Top /// /// 要打开的文档 /// 事务是否提交 - public DBTrans(Document doc = null, bool commit = true, bool doclock = false) + public DBTrans(bool commit = true, bool doclock = false) { - doc ??= Application.DocumentManager.MdiActiveDocument; - Document = doc; - Database = Document.Database; - Editor = Document.Editor; - Init(commit, doclock); + Database = HostApplicationServices.WorkingDatabase; + Init(true, commit, doclock); } /// @@ -84,7 +85,7 @@ public DBTrans(Document doc = null, bool commit = true, bool doclock = false) public DBTrans(Database database, bool commit = true) { Database = database; - Init(commit, false); + Init(false, commit, false); } /// /// 构造函数,打开文件,默认提交事务 @@ -103,14 +104,19 @@ public DBTrans(string fileName, bool commit = true) Database.ReadDwgFile(fileName, FileShare.Read, true, null); } Database.CloseInput(true); - Init(commit, false); + Init(false, commit, false); } /// /// 初始化事务及事务队列、提交模式 /// /// 提交模式 - private void Init(bool commit, bool doclock) + private void Init(bool hasDoc, bool commit, bool doclock) { + if (HasDocument = hasDoc) + { + Document = Application.DocumentManager.GetDocument(Database); + Editor = Document.Editor; + } if (doclock) { documentLock = Document.LockDocument(); @@ -309,7 +315,7 @@ public void Commit() protected virtual void Dispose(bool disposing) { - Transaction.TransactionManager.QueueForGraphicsFlush(); + //Transaction.TransactionManager.QueueForGraphicsFlush(); if (!disposedValue) { if (disposing) @@ -319,6 +325,7 @@ protected virtual void Dispose(bool disposing) dBTrans.Pop(); if (!Transaction.IsDisposed) { + Transaction.TransactionManager.QueueForGraphicsFlush(); Transaction.Dispose(); } documentLock?.Dispose(); @@ -328,6 +335,7 @@ protected virtual void Dispose(bool disposing) // 将大型字段设置为 null disposedValue = true; } + //Transaction.TransactionManager.QueueForGraphicsFlush(); } // 仅当“Dispose(bool disposing)”拥有用于释放未托管资源的代码时才替代终结器 diff --git a/tests/Test/IFoxCAD1.cs b/tests/Test/IFoxCAD1.cs new file mode 100644 index 0000000..fe1300a --- /dev/null +++ b/tests/Test/IFoxCAD1.cs @@ -0,0 +1,16 @@ +namespace Test +{ + public class IFoxCAD1 + { + [CommandMethod("testtr")] + public void Testtr() + { + string filename = @"C:\Users\vic\Desktop\test.dwg"; + using var tr = new DBTrans(filename); + tr.ModelSpace.AddCircle(new Point3d(0, 0, 0), 20); + tr.Database.SaveAs(filename,DwgVersion.Current); + + + } + } +} diff --git a/tests/Test/testid.cs b/tests/Test/testid.cs index 911fd53..7d35984 100644 --- a/tests/Test/testid.cs +++ b/tests/Test/testid.cs @@ -55,7 +55,7 @@ public void TestId() [CommandMethod("testmycommand")] public void TestMyCommand() { - using (var dbtrans = new DBTrans(Env.Document,true,false)) { + using (var dbtrans = new DBTrans(true,false)) { using (var trans = Env.Database.TransactionManager.StartTransaction()) { var l1 = new Line(new Point3d(0, 0, 0), new Point3d(100, 100, 0)); -- Gitee From 3f999fe020e41bed60df1195dacc88055a0340e5 Mon Sep 17 00:00:00 2001 From: Leon_Office Date: Fri, 11 Feb 2022 10:30:23 +0800 Subject: [PATCH 032/675] =?UTF-8?q?1=E3=80=81=E4=BF=AE=E6=94=B9=E6=9E=84?= =?UTF-8?q?=E9=80=A0=E5=87=BD=E6=95=B0=EF=BC=9ADBTrans(string=20fileName,?= =?UTF-8?q?=20bool=20commit=20=3D=20true)=EF=BC=8C=E5=8E=9F=E6=96=B9?= =?UTF-8?q?=E6=B3=95=E4=BB=85=E6=94=AF=E6=8C=81=E6=96=87=E4=BB=B6=E5=AD=98?= =?UTF-8?q?=E5=9C=A8=EF=BC=8C=E5=B9=B6=E4=B8=94=E6=96=87=E4=BB=B6=E4=B8=8D?= =?UTF-8?q?=E8=83=BD=E8=AF=95=E5=89=8D=E5=8F=B0=E6=89=93=E5=BC=80=202?= =?UTF-8?q?=E3=80=81=E4=BF=AE=E6=94=B9void=20Dispose(bool=20disposing)?= =?UTF-8?q?=EF=BC=8C=E5=A6=82=E6=9E=9C=E6=96=87=E6=A1=A3=E4=B8=8D=E6=98=AF?= =?UTF-8?q?=E6=BF=80=E6=B4=BB=E7=8A=B6=E6=80=81=E5=87=BA=E7=8E=B0=E5=BC=82?= =?UTF-8?q?=E5=B8=B8=203=E3=80=81=E6=B7=BB=E5=8A=A0void=20SaveAs(string=20?= =?UTF-8?q?fileName,=20DwgVersion=20version)=E6=96=B9=E6=B3=95=EF=BC=8C?= =?UTF-8?q?=E4=BF=9D=E5=AD=98=E6=95=B0=E6=8D=AE=E6=96=87=E4=BB=B6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/IFoxCAD.Cad/Runtime/DBTrans.cs | 47 ++++++++++++++++++++++++------ tests/Test/testFileDatabase.cs | 28 ++++++++++++++++++ tests/Test/testid.cs | 2 +- 3 files changed, 67 insertions(+), 10 deletions(-) create mode 100644 tests/Test/testFileDatabase.cs diff --git a/src/IFoxCAD.Cad/Runtime/DBTrans.cs b/src/IFoxCAD.Cad/Runtime/DBTrans.cs index 90aa274..07131cd 100644 --- a/src/IFoxCAD.Cad/Runtime/DBTrans.cs +++ b/src/IFoxCAD.Cad/Runtime/DBTrans.cs @@ -67,7 +67,7 @@ public static DBTrans Top /// /// 要打开的文档 /// 事务是否提交 - public DBTrans(Document doc = null, bool commit = true, bool doclock = false) + public DBTrans(bool commit = true, bool doclock = false, Document doc = null) { doc ??= Application.DocumentManager.MdiActiveDocument; Document = doc; @@ -93,17 +93,34 @@ public DBTrans(Database database, bool commit = true) /// 事务是否提交 public DBTrans(string fileName, bool commit = true) { - Database = new Database(false, true); - if (Path.GetExtension(fileName).ToLower().Contains("dxf")) + if (string.IsNullOrWhiteSpace(fileName)) + throw new ArgumentNullException(nameof(fileName)); + + var doc = Autodesk.AutoCAD.ApplicationServices.Application.DocumentManager.Cast() + .FirstOrDefault(doc => doc.Database.OriginalFileName == fileName); + if (null != doc) { - Database.DxfIn(fileName, null); + Database = doc.Database; + Document = doc; + Editor = doc.Editor; + Init(commit, true); } else { - Database.ReadDwgFile(fileName, FileShare.Read, true, null); + if (System.IO.File.Exists(fileName)) + { + Database = new Database(false, true); + if (Path.GetExtension(fileName).ToLower().Contains("dxf")) + Database.DxfIn(fileName, null); + else + Database.ReadDwgFile(fileName, FileOpenMode.OpenForReadAndWriteNoShare, true, null); + } + else + Database = new Database(true, false); + + Database.CloseInput(true); + Init(commit, false); } - Database.CloseInput(true); - Init(commit, false); } /// /// 初始化事务及事务队列、提交模式 @@ -288,7 +305,18 @@ public ObjectId GetObjectId(string handleString) #endregion #region idispose接口相关函数 - + /// + /// 保存 + /// + /// 文件名 + /// 版本 + public void SaveAs(string fileName, DwgVersion version) + { + if (Document != null) + Document.SendStringToExecute("_qsave\n", false, true, true); + else + Database.SaveAs(fileName, version); + } public void Abort() { Transaction.Abort(); @@ -309,7 +337,6 @@ public void Commit() protected virtual void Dispose(bool disposing) { - Transaction.TransactionManager.QueueForGraphicsFlush(); if (!disposedValue) { if (disposing) @@ -319,6 +346,8 @@ protected virtual void Dispose(bool disposing) dBTrans.Pop(); if (!Transaction.IsDisposed) { + if (Document?.IsActive==true) + Transaction.TransactionManager.QueueForGraphicsFlush(); Transaction.Dispose(); } documentLock?.Dispose(); diff --git a/tests/Test/testFileDatabase.cs b/tests/Test/testFileDatabase.cs new file mode 100644 index 0000000..8650982 --- /dev/null +++ b/tests/Test/testFileDatabase.cs @@ -0,0 +1,28 @@ +/************************************************************** +*作者:Leon +*创建时间:2022/2/11 9:55:32 +**************************************************************/ +namespace Test +{ + public class TestFileDatabase + { + [CommandMethod("Test_FileDatabaseInit")] + public void TestDatabase() + { + try + { + var fileName = @"C:\Users\Administrator\Desktop\合并详图测试BUG.dwg"; + + DBTrans trans = new(fileName); + trans.ModelSpace.AddEntity(new Line(new(0, 0, 0), new(1000, 1000, 0))); + trans.SaveAs(fileName, DwgVersion.AC1021); + trans.Dispose(); + } + catch (System.Exception e) + { + System.Windows.MessageBox.Show(e.Message); + } + + } + } +} \ No newline at end of file diff --git a/tests/Test/testid.cs b/tests/Test/testid.cs index 911fd53..7d35984 100644 --- a/tests/Test/testid.cs +++ b/tests/Test/testid.cs @@ -55,7 +55,7 @@ public void TestId() [CommandMethod("testmycommand")] public void TestMyCommand() { - using (var dbtrans = new DBTrans(Env.Document,true,false)) { + using (var dbtrans = new DBTrans(true,false)) { using (var trans = Env.Database.TransactionManager.StartTransaction()) { var l1 = new Line(new Point3d(0, 0, 0), new Point3d(100, 100, 0)); -- Gitee From dac024ca944190da1bc6641cd032a86666cf546e Mon Sep 17 00:00:00 2001 From: vicwjb Date: Sat, 12 Feb 2022 23:53:40 +0800 Subject: [PATCH 033/675] =?UTF-8?q?=E4=BF=AE=E5=A4=8DGetEntities=E5=92=8CG?= =?UTF-8?q?etExtents=E5=87=BD=E6=95=B0=E9=94=99=E8=AF=AF?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/IFoxCAD.Cad/ExtensionMethod/EntityEx.cs | 16 +- .../ExtensionMethod/SelectionSetEx.cs | 1 + tests/Test/Test.cs | 18 +- tests/Test/testblock.cs | 159 ++++++++++++++++-- 4 files changed, 166 insertions(+), 28 deletions(-) diff --git a/src/IFoxCAD.Cad/ExtensionMethod/EntityEx.cs b/src/IFoxCAD.Cad/ExtensionMethod/EntityEx.cs index 716643c..0c01b0a 100644 --- a/src/IFoxCAD.Cad/ExtensionMethod/EntityEx.cs +++ b/src/IFoxCAD.Cad/ExtensionMethod/EntityEx.cs @@ -166,11 +166,19 @@ public static void Mirror(this Entity ent, Point3d basePoint) /// 实体集合的范围 public static Extents3d GetExtents(this IEnumerable ents) { - var it = ents.GetEnumerator(); - var ext = it.Current.GeometricExtents; - while (it.MoveNext()) - ext.AddExtents(it.Current.GeometricExtents); + var ext = new Extents3d(); + foreach (var item in ents) + { + ext.AddExtents(item.GeometricExtents); + } + + //var it = ents.GetEnumerator(); + //var ext = it.Current.GeometricExtents; + //while (it.MoveNext()) + // ext.AddExtents(it.Current.GeometricExtents); return ext; + + } #endregion diff --git a/src/IFoxCAD.Cad/ExtensionMethod/SelectionSetEx.cs b/src/IFoxCAD.Cad/ExtensionMethod/SelectionSetEx.cs index 51f82ab..b4a94eb 100644 --- a/src/IFoxCAD.Cad/ExtensionMethod/SelectionSetEx.cs +++ b/src/IFoxCAD.Cad/ExtensionMethod/SelectionSetEx.cs @@ -68,6 +68,7 @@ public static IEnumerable> GetObjectIdGroup(this Sel /// 图元集合 public static IEnumerable GetEntities(this SelectionSet ss, OpenMode openMode = OpenMode.ForRead, Transaction tr = default) where T : Entity { + tr ??= DBTrans.Top.Transaction; return ss .GetObjectIds() diff --git a/tests/Test/Test.cs b/tests/Test/Test.cs index 12aa699..efaa113 100644 --- a/tests/Test/Test.cs +++ b/tests/Test/Test.cs @@ -2,7 +2,7 @@ namespace test; -public class Test +public class Test : AutoRegAssem { [CommandMethod("dbtest")] public void Dbtest() @@ -323,15 +323,15 @@ public Document Getdoc() return doc; } - //public override void Initialize() - //{ - // Application.DocumentManager.MdiActiveDocument.Editor.WriteMessage("\nload...."); - //} + public override void Initialize() + { + Application.DocumentManager.MdiActiveDocument.Editor.WriteMessage("\nload...."); + } - //public override void Terminate() - //{ - // Application.DocumentManager.MdiActiveDocument.Editor.WriteMessage("\nunload...."); - //} + public override void Terminate() + { + Application.DocumentManager.MdiActiveDocument.Editor.WriteMessage("\nunload...."); + } } diff --git a/tests/Test/testblock.cs b/tests/Test/testblock.cs index b03ad0d..1e9853a 100644 --- a/tests/Test/testblock.cs +++ b/tests/Test/testblock.cs @@ -1,18 +1,4 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; - -using Autodesk.AutoCAD.ApplicationServices; -using Autodesk.AutoCAD.Colors; -using Autodesk.AutoCAD.DatabaseServices; -using Autodesk.AutoCAD.EditorInput; -using Autodesk.AutoCAD.Geometry; -using Autodesk.AutoCAD.Runtime; - -using IFoxCAD.Cad; -namespace test; +namespace test; public class TestBlock { //块定义 @@ -291,4 +277,147 @@ public void EJ() } + + [CommandMethod("W_KSZK")] + public void QuickBlockDef() + { + //Database db = HostApplicationServices.WorkingDatabase; + Editor ed = Application.DocumentManager.MdiActiveDocument.Editor; + PromptSelectionOptions promptOpt = new PromptSelectionOptions + { + MessageForAdding = "请选择需要快速制作块的对象" + }; + string blockName = "W_BLOCK_" + DateTime.Now.ToString("yyyyMMdd_HHmmss"); + //var rss = ed.GetSelection(promptOpt); + var rss = Env.Editor.GetSelection(promptOpt); + using (var tr = new DBTrans()) + { + if (rss.Status == PromptStatus.OK) + { + //SelectionSet ss = rss.Value; + //ObjectId[] ids = ss.GetObjectIds(); + //var ents = new List>(); + //var extents = new Extents3d(); + //foreach (var id in ids) + //{ + // Entity ent = tr.GetObject(id); + // if (ent == null) + // continue; + // try + // { + // extents.AddExtents(ent.GeometricExtents); + // var order = id.Handle.Value; + // var newEnt = ent.Clone() as Entity; + // ents.Add(new KeyValuePair(newEnt, order)); + // ent.UpgradeOpen(); + // ent.Erase(); + // ent.DowngradeOpen(); + // } + // catch (System.Exception exc) + // { + // ed.WriteMessage(exc.Message); + // } + //} + //ents = ents.OrderBy(x => x.Value).ToList(); + var ents = rss.Value.GetEntities(); + //ents.ForEach(ent => extents.AddExtents(ent.GeometricExtents)); + var extents = ents.GetExtents(); + + Point3d pt = extents.MinPoint; + Matrix3d matrix = Matrix3d.Displacement(Point3d.Origin - pt); + //var newEnts = new List(); + //foreach (var ent in ents) + //{ + // var newEnt = ent.Key; + // newEnt.TransformBy(matrix); + // newEnts.Add(newEnt); + //} + //if (tr.BlockTable.Has(blockName)) + //{ + // Application.ShowAlertDialog(Environment.NewLine + "块名重复,程序退出!"); + // return; + //} + ents.ForEach(ent => + ent.ForWrite(e => e.TransformBy(matrix))); + //var newents = ents.Select(ent => + //{ + // var maping = new IdMapping(); + // return ent.DeepClone(ent, maping, true) as Entity; + //}); + var newents = ents.Select(ent => ent.Clone() as Entity); + + //ents.ForEach(ent => ent.ForWrite(e => e.Erase(true))); // 删除实体就会卡死,比较奇怪,估计是Clone()函数的问题 + // 经过测试不是删除的问题 + var btrId = tr.BlockTable.Add(blockName, newents); + ents.ForEach(ent => ent.ForWrite(e => e.Erase(true))); + var bId = tr.CurrentSpace.InsertBlock(pt, blockName); + //tr.GetObject(bId, OpenMode.ForWrite).Move(Point3d.Origin, Point3d.Origin); + //var ed = Application.DocumentManager.MdiActiveDocument.Editor; + //ed.Regen(); + //tr.Editor.Regen(); + // 调用regen() 卡死 + } + //tr.Editor.Regen(); + //ed.Regen(); + } + //using (var tr = new DBTrans()) + //{ + // tr.CurrentSpace.InsertBlock(Point3d.Origin, blockName); + // tr.Editor.Regen(); + //} + } + + [CommandMethod("testquickblockdef")] + public void TestQuickBlockDef() + { + Database db = HostApplicationServices.WorkingDatabase; + Editor ed = Application.DocumentManager.MdiActiveDocument.Editor; + PromptSelectionOptions promptOpt = new PromptSelectionOptions + { + MessageForAdding = "请选择需要快速制作块的对象" + }; + string blockName = "W_BLOCK_" + DateTime.Now.ToString("yyyyMMdd_HHmmss"); + var rss = ed.GetSelection(promptOpt); + //var rss = Env.Editor.GetSelection(promptOpt); + if (rss.Status != PromptStatus.OK) + { + return; + } + + using (var tr = db.TransactionManager.StartTransaction()) + { + var ids = rss.Value.GetObjectIds(); + var bt = tr.GetObject(db.BlockTableId,OpenMode.ForRead) as BlockTable; + var btr = new BlockTableRecord(); + btr.Name = blockName; + foreach (var item in ids) + { + var ent = tr.GetObject(item,OpenMode.ForRead) as Entity; + + btr.AppendEntity(ent.Clone() as Entity); + ent.ForWrite(e => e.Erase(true)); + } + bt.UpgradeOpen(); + bt.Add(btr); + tr.AddNewlyCreatedDBObject(btr,true); + bt.DowngradeOpen(); + // tr.Commit(); + //} + + //using (var tr1 = db.TransactionManager.StartTransaction()) + //{ + //var bt = tr1.GetObject(db.BlockTableId, OpenMode.ForRead) as BlockTable; + var btr1 = tr.GetObject(db.CurrentSpaceId, OpenMode.ForWrite) as BlockTableRecord; + var br = new BlockReference(Point3d.Origin, bt[blockName]); + br.ScaleFactors = default; + btr1.AppendEntity(br); + tr.AddNewlyCreatedDBObject(br,true); + btr1.DowngradeOpen(); + ed.Regen(); + tr.Commit(); + } + //ed.Regen(); + + } + } \ No newline at end of file -- Gitee From 4421504a5a889cf2ecedad60409eca0e5eef42a5 Mon Sep 17 00:00:00 2001 From: vicwjb Date: Sun, 13 Feb 2022 00:25:15 +0800 Subject: [PATCH 034/675] =?UTF-8?q?=E6=81=A2=E5=A4=8Ddbtrans=E6=9E=84?= =?UTF-8?q?=E9=80=A0=E5=87=BD=E6=95=B0=E7=9A=84=E9=BB=98=E8=AE=A4doc?= =?UTF-8?q?=E5=8F=82=E6=95=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/IFoxCAD.Cad/Runtime/DBTrans.cs | 19 ++++++++----------- tests/Test/testid.cs | 2 +- tests/Test/{IFoxCAD1.cs => testtr.cs} | 2 +- 3 files changed, 10 insertions(+), 13 deletions(-) rename tests/Test/{IFoxCAD1.cs => testtr.cs} (93%) diff --git a/src/IFoxCAD.Cad/Runtime/DBTrans.cs b/src/IFoxCAD.Cad/Runtime/DBTrans.cs index 5c162a0..31fdccb 100644 --- a/src/IFoxCAD.Cad/Runtime/DBTrans.cs +++ b/src/IFoxCAD.Cad/Runtime/DBTrans.cs @@ -71,10 +71,12 @@ public static DBTrans Top /// /// 要打开的文档 /// 事务是否提交 - public DBTrans(bool commit = true, bool doclock = false) + public DBTrans(Document doc = null, bool commit = true, bool doclock = false) { - Database = HostApplicationServices.WorkingDatabase; - Init(true, commit, doclock); + Document = doc ?? Application.DocumentManager.MdiActiveDocument; + Database = Document.Database; + Editor = Document.Editor; + Init(commit, doclock); } /// @@ -85,7 +87,7 @@ public DBTrans(bool commit = true, bool doclock = false) public DBTrans(Database database, bool commit = true) { Database = database; - Init(false, commit, false); + Init(commit, false); } /// /// 构造函数,打开文件,默认提交事务 @@ -104,19 +106,14 @@ public DBTrans(string fileName, bool commit = true) Database.ReadDwgFile(fileName, FileShare.Read, true, null); } Database.CloseInput(true); - Init(false, commit, false); + Init(commit, false); } /// /// 初始化事务及事务队列、提交模式 /// /// 提交模式 - private void Init(bool hasDoc, bool commit, bool doclock) + private void Init(bool commit, bool doclock) { - if (HasDocument = hasDoc) - { - Document = Application.DocumentManager.GetDocument(Database); - Editor = Document.Editor; - } if (doclock) { documentLock = Document.LockDocument(); diff --git a/tests/Test/testid.cs b/tests/Test/testid.cs index 7d35984..911fd53 100644 --- a/tests/Test/testid.cs +++ b/tests/Test/testid.cs @@ -55,7 +55,7 @@ public void TestId() [CommandMethod("testmycommand")] public void TestMyCommand() { - using (var dbtrans = new DBTrans(true,false)) { + using (var dbtrans = new DBTrans(Env.Document,true,false)) { using (var trans = Env.Database.TransactionManager.StartTransaction()) { var l1 = new Line(new Point3d(0, 0, 0), new Point3d(100, 100, 0)); diff --git a/tests/Test/IFoxCAD1.cs b/tests/Test/testtr.cs similarity index 93% rename from tests/Test/IFoxCAD1.cs rename to tests/Test/testtr.cs index fe1300a..674e87a 100644 --- a/tests/Test/IFoxCAD1.cs +++ b/tests/Test/testtr.cs @@ -1,6 +1,6 @@ namespace Test { - public class IFoxCAD1 + public class TestTrans { [CommandMethod("testtr")] public void Testtr() -- Gitee From c863182455bc1bf02911857f1505e617b8a9a99d Mon Sep 17 00:00:00 2001 From: Leon_Office Date: Wed, 16 Feb 2022 13:12:01 +0800 Subject: [PATCH 035/675] =?UTF-8?q?=E6=B5=8B=E8=AF=95=E7=94=A8=E4=BE=8B?= =?UTF-8?q?=EF=BC=9Ausing=20DBTrans=20trans=20=3D=20new(fileName)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- tests/Test/testFileDatabase.cs | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/tests/Test/testFileDatabase.cs b/tests/Test/testFileDatabase.cs index 8650982..fb61dba 100644 --- a/tests/Test/testFileDatabase.cs +++ b/tests/Test/testFileDatabase.cs @@ -12,11 +12,12 @@ public void TestDatabase() try { var fileName = @"C:\Users\Administrator\Desktop\合并详图测试BUG.dwg"; - - DBTrans trans = new(fileName); + using DBTrans trans = new(fileName); trans.ModelSpace.AddEntity(new Line(new(0, 0, 0), new(1000, 1000, 0))); - trans.SaveAs(fileName, DwgVersion.AC1021); - trans.Dispose(); + if (trans.Document != null && trans.Document.IsActive) + trans.Document.SendStringToExecute("_qsave\n", false, true, true); + else + trans.Database.SaveAs(fileName, DwgVersion.AC1021); } catch (System.Exception e) { -- Gitee From 59a4caf38147a635b0274dfd3020cdf0132cfc67 Mon Sep 17 00:00:00 2001 From: vicwjb Date: Thu, 17 Feb 2022 21:37:37 +0800 Subject: [PATCH 036/675] =?UTF-8?q?=E5=BC=80=E5=90=AFenable?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/IFoxCAD.Basal/GlobalUsings.cs | 5 +- src/IFoxCAD.Basal/IFoxCAD.Basal.csproj | 5 + src/IFoxCAD.Basal/LinkedListEx.cs | 59 + src/IFoxCAD.Basal/LinqEx.cs | 20 +- src/IFoxCAD.Basal/LoopList.cs | 1016 ++++++++--------- src/IFoxCAD.Cad/ExtensionMethod/Curve2dEx.cs | 2 +- src/IFoxCAD.Cad/ExtensionMethod/Curve3dEx.cs | 12 +- src/IFoxCAD.Cad/ExtensionMethod/CurveEx.cs | 78 +- .../ExtensionMethod/DBDictionaryEx.cs | 45 +- src/IFoxCAD.Cad/ExtensionMethod/EditorEx.cs | 14 +- src/IFoxCAD.Cad/ExtensionMethod/EntityEx.cs | 69 +- src/IFoxCAD.Cad/ExtensionMethod/Enums.cs | 2 +- src/IFoxCAD.Cad/ExtensionMethod/GeometryEx.cs | 49 +- src/IFoxCAD.Cad/ExtensionMethod/ObjectIdEx.cs | 6 +- .../ExtensionMethod/SelectionSetEx.cs | 10 +- .../ExtensionMethod/SymbolTableEx.cs | 49 +- .../ExtensionMethod/SymbolTableRecordEx.cs | 57 +- src/IFoxCAD.Cad/IFoxCAD.Cad.csproj | 1 + src/IFoxCAD.Cad/ResultData/LispList.cs | 4 +- src/IFoxCAD.Cad/Runtime/AcadVersion.cs | 2 +- src/IFoxCAD.Cad/Runtime/CadVersion.cs | 4 +- src/IFoxCAD.Cad/Runtime/DBTrans.cs | 111 +- src/IFoxCAD.Cad/Runtime/SymbolTable.cs | 38 +- src/IFoxCAD.Cad/SelectionFilter/OpFilter.cs | 34 +- src/IFoxCAD.WPF/Converter.cs | 6 +- src/IFoxCAD.WPF/DependencyObjectExtensions.cs | 4 +- src/IFoxCAD.WPF/EnumSelection.cs | 2 +- src/IFoxCAD.WPF/EventBindingExtension.cs | 74 +- src/IFoxCAD.WPF/IFoxCAD.WPF.csproj | 1 + src/IFoxCAD.WPF/RelayCommand.cs | 6 +- src/IFoxCAD.WPF/ViewModelBase.cs | 4 +- tests/Test/Test.cs | 1 + tests/Test/testblock.cs | 12 + 33 files changed, 955 insertions(+), 847 deletions(-) create mode 100644 src/IFoxCAD.Basal/LinkedListEx.cs diff --git a/src/IFoxCAD.Basal/GlobalUsings.cs b/src/IFoxCAD.Basal/GlobalUsings.cs index bee3171..15c8a97 100644 --- a/src/IFoxCAD.Basal/GlobalUsings.cs +++ b/src/IFoxCAD.Basal/GlobalUsings.cs @@ -4,7 +4,6 @@ global using System.Collections.Generic; global using System.IO; global using System.Linq; - - - +global using System.Text; +global using System.Reflection; diff --git a/src/IFoxCAD.Basal/IFoxCAD.Basal.csproj b/src/IFoxCAD.Basal/IFoxCAD.Basal.csproj index 0e17a78..fa009d6 100644 --- a/src/IFoxCAD.Basal/IFoxCAD.Basal.csproj +++ b/src/IFoxCAD.Basal/IFoxCAD.Basal.csproj @@ -3,6 +3,7 @@ net45 true + enable 0.2.0 InspireFunction xsfhlzh;vicwjb @@ -28,5 +29,9 @@ + + + + diff --git a/src/IFoxCAD.Basal/LinkedListEx.cs b/src/IFoxCAD.Basal/LinkedListEx.cs new file mode 100644 index 0000000..6481d85 --- /dev/null +++ b/src/IFoxCAD.Basal/LinkedListEx.cs @@ -0,0 +1,59 @@ +namespace IFoxCAD.Basal; + +public static class LinkedListEx +{ + /// + /// 获取节点 + /// + /// 节点数据比较委托 + /// 节点 + public static LinkedListNode? GetNode(this LinkedList linkedList, Func func) + { + var node = linkedList.First; + if (node != null) + { + for (int i = 0; i < linkedList.Count; i++) + { + if (func(node.Value)) + { + return node; + } + node = node.Next; + } + return null; + } + + return null; + } + + public static LinkedListNode Add(this LinkedList linkedList, T value) + { + return linkedList.AddLast(value); + } + + /// + /// 获取节点的查询器 + /// + /// + public static IEnumerable> GetNodes(this LinkedList linkedList) + { + var node = linkedList.First; + for (int i = 0; i < linkedList.Count; i++) + { + yield return node; + node = node.Next; + } + } + + + /// + /// 获取当前节点的临近节点 + /// + /// 搜索方向标志,为向前搜索,为向后搜索 + /// 节点 + public static LinkedListNode GetNext(this LinkedListNode node, bool forward) + { + return forward ? node.Next : node.Previous; + } +} + diff --git a/src/IFoxCAD.Basal/LinqEx.cs b/src/IFoxCAD.Basal/LinqEx.cs index 836063f..b79b4f7 100644 --- a/src/IFoxCAD.Basal/LinqEx.cs +++ b/src/IFoxCAD.Basal/LinqEx.cs @@ -20,7 +20,7 @@ public static TValue FindByMax(this IEnumerable source, Fu { var itor = source.GetEnumerator(); if (!itor.MoveNext()) - throw new ArgumentNullException(); + throw new ArgumentNullException(nameof(source), "对象为 null"); TValue value = itor.Current; TKey key = func(value); @@ -51,7 +51,7 @@ public static TValue FindByMax(this IEnumerable source, ou { var itor = source.GetEnumerator(); if (!itor.MoveNext()) - throw new ArgumentNullException(); + throw new ArgumentNullException(nameof(source), "对象为 null"); TValue value = itor.Current; TKey key = func(value); @@ -80,7 +80,7 @@ public static TValue FindByMax(this IEnumerable source, Comparis { var itor = source.GetEnumerator(); if (!itor.MoveNext()) - throw new ArgumentNullException(); + throw new ArgumentNullException(nameof(source), "对象为 null"); TValue value = itor.Current; @@ -110,7 +110,7 @@ public static TValue FindByMin(this IEnumerable source, ou { var itor = source.GetEnumerator(); if (!itor.MoveNext()) - throw new ArgumentNullException(); + throw new ArgumentNullException(nameof(source), "对象为 null"); TValue value = itor.Current; TKey key = func(value); @@ -141,7 +141,7 @@ public static TValue FindByMin(this IEnumerable source, Fu { var itor = source.GetEnumerator(); if (!itor.MoveNext()) - throw new ArgumentNullException(); + throw new ArgumentNullException(nameof(source), "对象为 null"); TValue value = itor.Current; TKey key = func(value); @@ -169,7 +169,7 @@ public static TValue FindByMin(this IEnumerable source, Comparis { var itor = source.GetEnumerator(); if (!itor.MoveNext()) - throw new ArgumentNullException(); + throw new ArgumentNullException(nameof(source), "对象为 null"); TValue value = itor.Current; @@ -198,7 +198,7 @@ public static TValue[] FindByExt(this IEnumerable source, { var itor = source.GetEnumerator(); if (!itor.MoveNext()) - throw new ArgumentNullException(); + throw new ArgumentNullException(nameof(source), "对象为 null"); TValue[] values = new TValue[2]; values[0] = values[1] = itor.Current; @@ -234,7 +234,7 @@ public static TValue[] FindByExt(this IEnumerable source, Compar { var itor = source.GetEnumerator(); if (!itor.MoveNext()) - throw new ArgumentNullException(); + throw new ArgumentNullException(nameof(source), "对象为 null"); TValue[] values = new TValue[2]; values[0] = values[1] = itor.Current; @@ -262,7 +262,7 @@ public static TKey[] FindExt(this IEnumerable source, Func { var itor = source.GetEnumerator(); if (!itor.MoveNext()) - throw new ArgumentNullException(); + throw new ArgumentNullException(nameof(source), "对象为 null"); TKey[] keys = new TKey[2]; keys[0] = keys[1] = func(itor.Current); @@ -288,7 +288,7 @@ public static TKey[] FindExt(this IEnumerable source, Func /// 泛型 private class SpecComparer : IComparer { - private Comparison _comp; + private readonly Comparison _comp; internal SpecComparer(Comparison comp) { diff --git a/src/IFoxCAD.Basal/LoopList.cs b/src/IFoxCAD.Basal/LoopList.cs index 3046b38..8e2cfb4 100644 --- a/src/IFoxCAD.Basal/LoopList.cs +++ b/src/IFoxCAD.Basal/LoopList.cs @@ -1,509 +1,507 @@ -namespace IFoxCAD.Basal -{ - /// - /// 环链表节点 - /// - /// - public class LoopListNode - { - /// - /// 取值 - /// - public T Value { get; set; } - - /// - /// 上一个节点 - /// - public LoopListNode Previous - { internal set; get; } - - /// - /// 下一个节点 - /// - public LoopListNode Next - { internal set; get; } - - /// - ///环链表序列 - /// - public LoopList List - { internal set; get; } - /// - /// 环链表节点构造函数 - /// - /// 节点值 - public LoopListNode(T value) - { - Value = value; - } - /// - /// 获取当前节点的临近节点 - /// - /// 搜索方向标志,为向前搜索,为向后搜索 - /// - public LoopListNode GetNext(bool forward) - { - return forward ? Next : Previous; - } - } - - /// - /// 环链表 - /// - /// - public class LoopList : - IEnumerable, IFormattable - { - /// - /// 默认构造函数 - /// - public LoopList() - { } - /// - /// 环链表构造函数 - /// - /// 节点迭代器 - public LoopList(IEnumerable values) - { - foreach (T value in values) - Add(value); - } - - /// - /// 节点数 - /// - public int Count - { get; private set; } - - /// - /// 首节点 - /// - public LoopListNode First - { get; private set; } - - /// - /// 尾节点 - /// - public LoopListNode Last - { - get { return First?.Previous; } - } - /// - /// 设置首节点 - /// - /// 节点 - /// - public bool SetFirst(LoopListNode node) - { - if (Contains(node)) - { - First = node; - return true; - } - return false; - } - - /// - /// 交换两个节点的值 - /// - /// 第一个节点 - /// 第二个节点 - public void Swap(LoopListNode node1, LoopListNode node2) - { - T value = node1.Value; - node1.Value = node2.Value; - node2.Value = value; - } - - #region Contains - /// - /// 是否包含节点 - /// - /// - /// - public bool Contains(LoopListNode node) - { - return node != null && node.List == this; - } - /// - /// 是否包含值 - /// - /// - /// - public bool Contains(T value) - { - LoopListNode node = First; - if (node == null) - return false; - - for (int i = 0; i < Count; i++) - { - if (node.Value.Equals(value)) - return true; - node = node.Next; - } - - return false; - } - /// - /// 获取节点 - /// - /// - /// - public LoopListNode GetNode(Func func) - { - LoopListNode node = First; - if (node == null) - return null; - - for (int i = 0; i < Count; i++) - { - if (func(node.Value)) - { - return node; - } - node = node.Next; - } - return null; - } - - #endregion Contains - - #region Add - - /// - /// 在首节点之前插入节点,并设置新节点为首节点 - /// - /// - /// - public LoopListNode AddFirst(T value) - { - LoopListNode node = new(value) - { - List = this - }; - if (Count == 0) - { - First = node; - First.Previous = First.Next = node; - } - else - { - LoopListNode last = Last; - First.Previous = last.Next = node; - node.Next = First; - node.Previous = last; - First = node; - } - Count++; - return First; - } - - /// - /// 在尾节点之后插入节点,并设置新节点为尾节点 - /// - /// - /// - public LoopListNode Add(T value) - { - LoopListNode node = new(value); - node.List = this; - if (Count == 0) - { - First = node; - First.Previous = First.Next = node; - } - else - { - LoopListNode last = First.Previous; - First.Previous = last.Next = node; - node.Next = First; - node.Previous = last; - } - Count++; - return Last; - } - /// - /// 前面增加节点 - /// - /// - /// - /// - public LoopListNode AddBefore(LoopListNode node, T value) - { - if (node == First) - { - return AddFirst(value); - } - else - { - LoopListNode tnode = new(value); - node.Previous.Next = tnode; - tnode.Previous = node.Previous; - node.Previous = tnode; - tnode.Next = node; - Count++; - return tnode; - } - } - /// - /// 后面增加节点 - /// - /// - /// - /// - public LoopListNode AddAfter(LoopListNode node, T value) - { - LoopListNode tnode = new(value); - node.Next.Previous = tnode; - tnode.Next = node.Next; - node.Next = tnode; - tnode.Previous = node; - Count++; - return tnode; - } - - #endregion Add - - #region Remove - - /// - /// 删除首节点 - /// - /// - public bool RemoveFirst() - { - switch (Count) - { - case 0: - return false; - - case 1: - First = null; - break; - - default: - LoopListNode last = Last; - First = First.Next; - First.Previous = last; - last.Next = First; - break; - } - Count--; - return true; - } - - /// - /// 删除尾节点 - /// - /// - public bool RemoveLast() - { - switch (Count) - { - case 0: - return false; - - case 1: - First = null; - break; - - default: - LoopListNode last = Last.Previous; - last.Next = First; - First.Previous = last; - break; - } - Count--; - return true; - } - - /// - /// 删除节点 - /// - /// - /// - public bool Remove(LoopListNode node) - { - if (Contains(node)) - { - if (Count == 1) - { - First = null; - } - else - { - if (node == First) - { - RemoveFirst(); - } - else - { - node.Next.Previous = node.Previous; - node.Previous.Next = node.Next; - } - } - Count--; - return true; - } - return false; - } - - #endregion Remove - - #region LinkTo - - /// - /// 链接两节点,并去除这两个节点间的所有节点 - /// - /// - /// - public void LinkTo(LoopListNode from, LoopListNode to) - { - if (from != to && Contains(from) && Contains(to)) - { - LoopListNode node = from.Next; - bool isFirstChanged = false; - int number = 0; - - while (node != to) - { - if (node == First) - isFirstChanged = true; - - node = node.Next; - number++; - } - - from.Next = to; - to.Previous = from; - - if (number > 0 && isFirstChanged) - First = to; - - Count -= number; - } - } - - /// - /// 链接两节点,并去除这两个节点间的所有节点 - /// - /// - /// - /// - public void LinkTo(LoopListNode from, LoopListNode to, int number) - { - if (from != to && Contains(from) && Contains(to)) - { - from.Next = to; - to.Previous = from; - First = to; - Count -= number; - } - } - - /// - /// 链接两节点,并去除这两个节点间的所有节点 - /// - /// - /// - /// - /// - public void LinkTo(LoopListNode from, LoopListNode to, int number, bool isFirstChanged) - { - if (from != to && Contains(from) && Contains(to)) - { - from.Next = to; - to.Previous = from; - if (isFirstChanged) - First = to; - Count -= number; - } - } - - #endregion LinkTo - - #region IEnumerable 成员 - - /// - /// 获取节点的查询器 - /// - /// - /// - public IEnumerable> GetNodes(LoopListNode from) - { - LoopListNode node = from; - for (int i = 0; i < Count; i++) - { - yield return node; - node = node.Next; - } - } - - /// - /// 获取节点的查询器 - /// - /// - public IEnumerable> GetNodes() - { - LoopListNode node = First; - for (int i = 0; i < Count; i++) - { - yield return node; - node = node.Next; - } - } - - /// - /// 获取节点值的查询器 - /// - /// - public IEnumerator GetEnumerator() - { - LoopListNode node = First; - for (int i = 0; i < Count; i++) - { - yield return node.Value; - node = node.Next; - } - } - - IEnumerator IEnumerable.GetEnumerator() - { - return GetEnumerator(); - } - - #region IEnumerable 成员 - - System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() - { - return GetEnumerator(); - } - - #endregion IEnumerable 成员 - - #endregion IEnumerable 成员 - - #region IFormattable 成员 - /// - /// 转换为字符串 - /// - /// - public override string ToString() - { - string s = "( "; - foreach (T value in this) - { - s += value.ToString() + " "; - } - return s + ")"; - } - - string IFormattable.ToString(string format, IFormatProvider formatProvider) - { - return ToString(); - } - - #endregion IFormattable 成员 - } -} \ No newline at end of file +//namespace IFoxCAD.Basal +//{ +// /// +// /// 环链表节点 +// /// +// /// +// public class LoopListNode +// { +// /// +// /// 取值 +// /// +// public T Value { get; set; } + +// /// +// /// 上一个节点 +// /// +// public LoopListNode Previous +// { internal set; get; } + +// /// +// /// 下一个节点 +// /// +// public LoopListNode Next +// { internal set; get; } + +// /// +// ///环链表序列 +// /// +// public LoopList List +// { internal set; get; } +// /// +// /// 环链表节点构造函数 +// /// +// /// 节点值 +// public LoopListNode(T value) +// { +// Value = value; +// } +// /// +// /// 获取当前节点的临近节点 +// /// +// /// 搜索方向标志,为向前搜索,为向后搜索 +// /// +// public LoopListNode GetNext(bool forward) +// { +// return forward ? Next : Previous; +// } +// } + +// /// +// /// 环链表 +// /// +// /// +// public class LoopList : +// IEnumerable, IFormattable +// { +// /// +// /// 默认构造函数 +// /// +// public LoopList() +// { } +// /// +// /// 环链表构造函数 +// /// +// /// 节点迭代器 +// public LoopList(IEnumerable values) +// { +// foreach (T value in values) +// Add(value); +// } + +// /// +// /// 节点数 +// /// +// public int Count +// { get; private set; } + +// /// +// /// 首节点 +// /// +// public LoopListNode? First +// { get; private set; } + +// /// +// /// 尾节点 +// /// +// public LoopListNode? Last +// { +// get { return First?.Previous; } +// } +// /// +// /// 设置首节点 +// /// +// /// 节点 +// /// +// public bool SetFirst(LoopListNode node) +// { +// if (Contains(node)) +// { +// First = node; +// return true; +// } +// return false; +// } + +// /// +// /// 交换两个节点的值 +// /// +// /// 第一个节点 +// /// 第二个节点 +// public void Swap(LoopListNode node1, LoopListNode node2) +// { +// (node2.Value, node1.Value) = (node1.Value, node2.Value); +// } + +// #region Contains +// /// +// /// 是否包含节点 +// /// +// /// +// /// +// public bool Contains(LoopListNode node) +// { +// return node != null && node.List == this; +// } +// /// +// /// 是否包含值 +// /// +// /// +// /// +// public bool Contains(T value) +// { +// var node = First; +// if (node == null) +// return false; + +// for (int i = 0; i < Count; i++) +// { +// if (node.Value!.Equals(value)) +// return true; +// node = node.Next; +// } + +// return false; +// } +// /// +// /// 获取节点 +// /// +// /// +// /// +// public LoopListNode GetNode(Func func) +// { +// LoopListNode node = First; +// if (node == null) +// return null; + +// for (int i = 0; i < Count; i++) +// { +// if (func(node.Value)) +// { +// return node; +// } +// node = node.Next; +// } +// return null; +// } + +// #endregion Contains + +// #region Add + +// /// +// /// 在首节点之前插入节点,并设置新节点为首节点 +// /// +// /// +// /// +// public LoopListNode AddFirst(T value) +// { +// LoopListNode node = new(value) +// { +// List = this +// }; +// if (Count == 0) +// { +// First = node; +// First.Previous = First.Next = node; +// } +// else +// { +// LoopListNode last = Last; +// First.Previous = last.Next = node; +// node.Next = First; +// node.Previous = last; +// First = node; +// } +// Count++; +// return First; +// } + +// /// +// /// 在尾节点之后插入节点,并设置新节点为尾节点 +// /// +// /// +// /// +// public LoopListNode Add(T value) +// { +// LoopListNode node = new(value); +// node.List = this; +// if (Count == 0) +// { +// First = node; +// First.Previous = First.Next = node; +// } +// else +// { +// LoopListNode last = First.Previous; +// First.Previous = last.Next = node; +// node.Next = First; +// node.Previous = last; +// } +// Count++; +// return Last; +// } +// /// +// /// 前面增加节点 +// /// +// /// +// /// +// /// +// public LoopListNode AddBefore(LoopListNode node, T value) +// { +// if (node == First) +// { +// return AddFirst(value); +// } +// else +// { +// LoopListNode tnode = new(value); +// node.Previous.Next = tnode; +// tnode.Previous = node.Previous; +// node.Previous = tnode; +// tnode.Next = node; +// Count++; +// return tnode; +// } +// } +// /// +// /// 后面增加节点 +// /// +// /// +// /// +// /// +// public LoopListNode AddAfter(LoopListNode node, T value) +// { +// LoopListNode tnode = new(value); +// node.Next.Previous = tnode; +// tnode.Next = node.Next; +// node.Next = tnode; +// tnode.Previous = node; +// Count++; +// return tnode; +// } + +// #endregion Add + +// #region Remove + +// /// +// /// 删除首节点 +// /// +// /// +// public bool RemoveFirst() +// { +// switch (Count) +// { +// case 0: +// return false; + +// case 1: +// First = null; +// break; + +// default: +// LoopListNode last = Last; +// First = First.Next; +// First.Previous = last; +// last.Next = First; +// break; +// } +// Count--; +// return true; +// } + +// /// +// /// 删除尾节点 +// /// +// /// +// public bool RemoveLast() +// { +// switch (Count) +// { +// case 0: +// return false; + +// case 1: +// First = null; +// break; + +// default: +// LoopListNode last = Last.Previous; +// last.Next = First; +// First.Previous = last; +// break; +// } +// Count--; +// return true; +// } + +// /// +// /// 删除节点 +// /// +// /// +// /// +// public bool Remove(LoopListNode node) +// { +// if (Contains(node)) +// { +// if (Count == 1) +// { +// First = null; +// } +// else +// { +// if (node == First) +// { +// RemoveFirst(); +// } +// else +// { +// node.Next.Previous = node.Previous; +// node.Previous.Next = node.Next; +// } +// } +// Count--; +// return true; +// } +// return false; +// } + +// #endregion Remove + +// #region LinkTo + +// /// +// /// 链接两节点,并去除这两个节点间的所有节点 +// /// +// /// +// /// +// public void LinkTo(LoopListNode from, LoopListNode to) +// { +// if (from != to && Contains(from) && Contains(to)) +// { +// LoopListNode node = from.Next; +// bool isFirstChanged = false; +// int number = 0; + +// while (node != to) +// { +// if (node == First) +// isFirstChanged = true; + +// node = node.Next; +// number++; +// } + +// from.Next = to; +// to.Previous = from; + +// if (number > 0 && isFirstChanged) +// First = to; + +// Count -= number; +// } +// } + +// /// +// /// 链接两节点,并去除这两个节点间的所有节点 +// /// +// /// +// /// +// /// +// public void LinkTo(LoopListNode from, LoopListNode to, int number) +// { +// if (from != to && Contains(from) && Contains(to)) +// { +// from.Next = to; +// to.Previous = from; +// First = to; +// Count -= number; +// } +// } + +// /// +// /// 链接两节点,并去除这两个节点间的所有节点 +// /// +// /// +// /// +// /// +// /// +// public void LinkTo(LoopListNode from, LoopListNode to, int number, bool isFirstChanged) +// { +// if (from != to && Contains(from) && Contains(to)) +// { +// from.Next = to; +// to.Previous = from; +// if (isFirstChanged) +// First = to; +// Count -= number; +// } +// } + +// #endregion LinkTo + +// #region IEnumerable 成员 + +// /// +// /// 获取节点的查询器 +// /// +// /// +// /// +// public IEnumerable> GetNodes(LoopListNode from) +// { +// LoopListNode node = from; +// for (int i = 0; i < Count; i++) +// { +// yield return node; +// node = node.Next; +// } +// } + +// /// +// /// 获取节点的查询器 +// /// +// /// +// public IEnumerable> GetNodes() +// { +// LoopListNode node = First; +// for (int i = 0; i < Count; i++) +// { +// yield return node; +// node = node.Next; +// } +// } + +// /// +// /// 获取节点值的查询器 +// /// +// /// +// public IEnumerator GetEnumerator() +// { +// LoopListNode node = First; +// for (int i = 0; i < Count; i++) +// { +// yield return node.Value; +// node = node.Next; +// } +// } + +// IEnumerator IEnumerable.GetEnumerator() +// { +// return GetEnumerator(); +// } + +// #region IEnumerable 成员 + +// System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() +// { +// return GetEnumerator(); +// } + +// #endregion IEnumerable 成员 + +// #endregion IEnumerable 成员 + +// #region IFormattable 成员 +// /// +// /// 转换为字符串 +// /// +// /// +// public override string ToString() +// { +// string s = "( "; +// foreach (T value in this) +// { +// s += $"{value} "; +// } +// return s + ")"; +// } + +// string IFormattable.ToString(string format, IFormatProvider formatProvider) +// { +// return ToString(); +// } + +// #endregion IFormattable 成员 +// } +//} \ No newline at end of file diff --git a/src/IFoxCAD.Cad/ExtensionMethod/Curve2dEx.cs b/src/IFoxCAD.Cad/ExtensionMethod/Curve2dEx.cs index 5f7a8f4..d0d35e1 100644 --- a/src/IFoxCAD.Cad/ExtensionMethod/Curve2dEx.cs +++ b/src/IFoxCAD.Cad/ExtensionMethod/Curve2dEx.cs @@ -14,7 +14,7 @@ public static class Curve2dEx /// Ge2d曲线 /// 曲线转换矩阵 /// Db曲线 - public static Curve ToCurve(this Curve2d curve, Matrix3d mat) + public static Curve? ToCurve(this Curve2d curve, Matrix3d mat) { return curve switch { diff --git a/src/IFoxCAD.Cad/ExtensionMethod/Curve3dEx.cs b/src/IFoxCAD.Cad/ExtensionMethod/Curve3dEx.cs index 7e6f4a9..e436d20 100644 --- a/src/IFoxCAD.Cad/ExtensionMethod/Curve3dEx.cs +++ b/src/IFoxCAD.Cad/ExtensionMethod/Curve3dEx.cs @@ -90,7 +90,7 @@ public static Curve3d GetSubCurve(this Curve3d curve, double from, double to) /// /// 三维解析类曲线 /// 三维实体类曲线 - public static Curve ToCurve(this Curve3d curve) + public static Curve? ToCurve(this Curve3d curve) { return curve switch { @@ -110,7 +110,7 @@ public static Curve ToCurve(this Curve3d curve) /// /// 三维解析类曲线 /// 三维解析类Nurb曲线 - public static NurbCurve3d ToNurbCurve3d(this Curve3d curve) + public static NurbCurve3d? ToNurbCurve3d(this Curve3d curve) { return curve switch { @@ -252,7 +252,7 @@ public static List GetSplitCurves(this CompositeCurve3d c3d, L /// /// 三维复合曲线 /// 实体曲线 - public static Curve ToCurve(this CompositeCurve3d curve) + public static Curve? ToCurve(this CompositeCurve3d curve) { Curve3d[] cs = curve.GetCurves(); if (cs.Length == 0) @@ -277,10 +277,10 @@ public static Curve ToCurve(this CompositeCurve3d curve) } if (hasNurb) { - NurbCurve3d nc3d = cs[0].ToNurbCurve3d(); + NurbCurve3d? nc3d = cs[0].ToNurbCurve3d(); for (int i = 1; i < cs.Length; i++) - nc3d.JoinWith(cs[i].ToNurbCurve3d()); - return nc3d.ToCurve(); + nc3d?.JoinWith(cs[i].ToNurbCurve3d()); + return nc3d?.ToCurve(); } else { diff --git a/src/IFoxCAD.Cad/ExtensionMethod/CurveEx.cs b/src/IFoxCAD.Cad/ExtensionMethod/CurveEx.cs index a8fa435..99c8b02 100644 --- a/src/IFoxCAD.Cad/ExtensionMethod/CurveEx.cs +++ b/src/IFoxCAD.Cad/ExtensionMethod/CurveEx.cs @@ -44,7 +44,9 @@ public static IEnumerable GetSplitCurves(this Curve curve, IEnumerable(); } - + /// + /// 边 + /// private struct EdgeItem : IEquatable { public Edge Edge; @@ -56,17 +58,17 @@ public EdgeItem(Edge edge, bool forward) Forward = forward; } - public CompositeCurve3d GetCurve() + public CompositeCurve3d? GetCurve() { - CompositeCurve3d cc3d = Edge.Curve; + var cc3d = Edge.Curve; if (Forward) { return cc3d; } else { - cc3d = cc3d.Clone() as CompositeCurve3d; - return cc3d.GetReverseParameterCurve() as CompositeCurve3d; + cc3d = cc3d!.Clone() as CompositeCurve3d; + return cc3d!.GetReverseParameterCurve() as CompositeCurve3d; } } @@ -77,11 +79,11 @@ public bool Equals(EdgeItem other) Forward == other.Forward; } - public void FindRegion(List edges, List> regions) + public void FindRegion(List edges, List> regions) { - var region = new LoopList(); + var region = new LinkedList(); var edgeItem = this; - region.Add(edgeItem); + region.AddLast(edgeItem); var edgeItem2 = this.GetNext(edges); if (edgeItem2.Edge != null) { @@ -104,7 +106,7 @@ public void FindRegion(List edges, List> regions) { if (edgeItem2.Edge == edgeItem.Edge) break; - region.Add(edgeItem2); + region.AddLast(edgeItem2); edgeItem2 = edgeItem2.GetNext(edges); } if (edgeItem2.Edge == edgeItem.Edge) @@ -168,24 +170,34 @@ public override string ToString() string.Format("{0}-{1}", Edge.StartIndex, Edge.EndIndex) : string.Format("{0}-{1}", Edge.EndIndex, Edge.StartIndex); } + + public override bool Equals(object obj) + { + return obj is EdgeItem item && Equals(item); + } + + public override int GetHashCode() + { + return this.ToString().GetHashCode(); + } } private class Edge { - public CompositeCurve3d Curve; + public CompositeCurve3d? Curve; public int StartIndex; public int EndIndex; public Vector3d GetStartVector() { - var inter = Curve.GetInterval(); - PointOnCurve3d poc = new(Curve, inter.LowerBound); + var inter = Curve?.GetInterval(); + PointOnCurve3d poc = new(Curve, inter!.LowerBound); return poc.GetDerivative(1); } public Vector3d GetEndVector() { - var inter = Curve.GetInterval(); + var inter = Curve!.GetInterval(); PointOnCurve3d poc = new(Curve, inter.UpperBound); return -poc.GetDerivative(1); } @@ -260,7 +272,7 @@ public static List Topo(List curves) } else if (gc1.IsClosed()) { - newCurves.Add(gc1.ToCurve()); + newCurves.Add(gc1.ToCurve()!); } else { @@ -269,7 +281,7 @@ public static List Topo(List curves) } else if (gc1.IsClosed()) { - newCurves.Add(gc1.ToCurve()); + newCurves.Add(gc1.ToCurve()!); } } @@ -280,7 +292,7 @@ public static List Topo(List curves) foreach (var edge in edges) { - if (edge.Curve.IsClosed()) + if (edge.Curve!.IsClosed()) { closedEdges.Add(edge); } @@ -314,7 +326,7 @@ public static List Topo(List curves) } } - newCurves.AddRange(closedEdges.Select(e => e.Curve.ToCurve())); + newCurves.AddRange(closedEdges.Select(e => e.Curve!.ToCurve())!); edges = edges @@ -348,7 +360,7 @@ public static List Topo(List curves) } } - List> regions = new(); + var regions = new List>(); foreach (var edge in edges) { var edgeItem = new EdgeItem(edge, true); @@ -370,7 +382,7 @@ public static List Topo(List curves) if (eq = node2 != null) { var b = node.Value.Forward; - var b2 = node2.Value.Forward; + var b2 = node2!.Value.Forward; for (int k = 1; k < regions[i].Count; k++) { node = node.GetNext(b); @@ -396,7 +408,7 @@ public static List Topo(List curves) region .Select(e => e.GetCurve()) .ToArray(); - newCurves.Add(new CompositeCurve3d(cs3ds.ToArray()).ToCurve()); + newCurves.Add(new CompositeCurve3d(cs3ds.ToArray()).ToCurve()!); } return newCurves; @@ -452,7 +464,7 @@ public static List BreakCurve(List curves) { foreach (CompositeCurve3d c3d in c3ds) { - Curve c = c3d.ToCurve(); + var c = c3d.ToCurve(); if (c != null) { c.SetPropertiesFrom(curves[i]); @@ -477,7 +489,7 @@ public static List BreakCurve(List curves) /// 曲线 /// ge曲线 [Obsolete("请使用Cad自带的 GetGeCurve 函数!")] - public static Curve3d ToCurve3d(this Curve curve) + public static Curve3d? ToCurve3d(this Curve curve) { return curve switch { @@ -498,7 +510,7 @@ public static Curve3d ToCurve3d(this Curve curve) ///
/// 曲线 /// 复合曲线 - public static CompositeCurve3d ToCompositeCurve3d(this Curve curve) + public static CompositeCurve3d? ToCompositeCurve3d(this Curve curve) { return curve switch { @@ -508,7 +520,7 @@ public static CompositeCurve3d ToCompositeCurve3d(this Curve curve) Ellipse el => new CompositeCurve3d(new Curve3d[] { ToCurve3d(el) }), Polyline pl => new CompositeCurve3d(new Curve3d[] { ToCurve3d(pl) }), - Polyline2d pl2 => new CompositeCurve3d(new Curve3d[] { ToCurve3d(pl2) }), + Polyline2d pl2 => new CompositeCurve3d(new Curve3d[] { ToCurve3d(pl2)! }), Polyline3d pl3 => new CompositeCurve3d(new Curve3d[] { ToCurve3d(pl3) }), Spline sp => new CompositeCurve3d(new Curve3d[] { ToCurve3d(sp) }), _ => null @@ -520,7 +532,7 @@ public static CompositeCurve3d ToCompositeCurve3d(this Curve curve) ///
/// 曲线 /// Nurb曲线 - public static NurbCurve3d ToNurbCurve3d(this Curve curve) + public static NurbCurve3d? ToNurbCurve3d(this Curve curve) { return curve switch { @@ -728,7 +740,7 @@ public static NurbCurve3d ToCurve3d(this Spline spl) ///
/// 二维多段线 /// 三维ge曲线 - public static Curve3d ToCurve3d(this Polyline2d pl2d) + public static Curve3d? ToCurve3d(this Polyline2d pl2d) { switch (pl2d.PolyType) { @@ -752,7 +764,7 @@ public static Curve3d ToCurve3d(this Polyline2d pl2d) ///
/// 二维多段线 /// 三维Nurb曲线 - public static NurbCurve3d ToNurbCurve3d(this Polyline2d pl2d) + public static NurbCurve3d? ToNurbCurve3d(this Polyline2d pl2d) { switch (pl2d.PolyType) { @@ -863,12 +875,12 @@ public static CompositeCurve3d ToCurve3d(this Polyline pl) ///
/// 多段线 /// Nurb曲线 - public static NurbCurve3d ToNurbCurve3d(this Polyline pl) + public static NurbCurve3d? ToNurbCurve3d(this Polyline pl) { - NurbCurve3d nc3d = null; + NurbCurve3d? nc3d = null; for (int i = 0; i < pl.NumberOfVertices; i++) { - NurbCurve3d nc3dtemp = null; + NurbCurve3d? nc3dtemp = null; switch (pl.GetSegmentType(i)) { case SegmentType.Line: @@ -934,7 +946,7 @@ public static void ChamferAt(this Polyline polyline, int index, double radius, b if (c3ds.Length > 0 && c3ds[0] is CompositeCurve3d) { var newcc3d = c3ds[0] as CompositeCurve3d; - c3ds = newcc3d.GetCurves(); + c3ds = newcc3d!.GetCurves(); if (c3ds.Length == 3) { c3ds = @@ -979,9 +991,9 @@ public static void ChamferAt(this Polyline polyline, int index, double radius, b ); //将结果Ge曲线转为Db曲线,并将相关的数值反映到原曲线 - Polyline plTemp = c3ds[0].ToCurve() as Polyline; + Polyline? plTemp = c3ds[0].ToCurve() as Polyline; polyline.RemoveVertexAt(index); - polyline.AddVertexAt(index, plTemp.GetPoint2dAt(1), plTemp.GetBulgeAt(1), 0, 0); + polyline.AddVertexAt(index, plTemp!.GetPoint2dAt(1), plTemp.GetBulgeAt(1), 0, 0); polyline.AddVertexAt(index + 1, plTemp.GetPoint2dAt(2), 0, 0, 0); } diff --git a/src/IFoxCAD.Cad/ExtensionMethod/DBDictionaryEx.cs b/src/IFoxCAD.Cad/ExtensionMethod/DBDictionaryEx.cs index 5dd0993..c1344d7 100644 --- a/src/IFoxCAD.Cad/ExtensionMethod/DBDictionaryEx.cs +++ b/src/IFoxCAD.Cad/ExtensionMethod/DBDictionaryEx.cs @@ -14,12 +14,16 @@ public static class DBDictionaryEx /// 字典 /// 事务 /// 对象迭代器 - public static IEnumerable GetAllObjects(this DBDictionary dict, Transaction trans = null) where T : DBObject + public static IEnumerable GetAllObjects(this DBDictionary dict, DBTrans? trans = null) where T : DBObject { - trans ??= DBTrans.Top.Transaction; + trans ??= DBTrans.Top; foreach (DBDictionaryEntry e in dict) { - yield return trans.GetObject(e.Value, OpenMode.ForRead) as T; + var ent = trans.GetObject(e.Value); + if (ent is not null) + { + yield return ent; + } } } @@ -31,15 +35,15 @@ public static IEnumerable GetAllObjects(this DBDictionary dict, Transactio /// 事务 /// 指定的键值 /// T 类型的对象 - public static T GetAt(this DBDictionary dict, string key, Transaction trans = null) where T : DBObject + public static T? GetAt(this DBDictionary dict, string key, DBTrans? trans = null) where T : DBObject { - trans ??= DBTrans.Top.Transaction; + trans ??= DBTrans.Top; if (dict.Contains(key)) { ObjectId id = dict.GetAt(key); if (!id.IsNull) { - return trans.GetObject(id, OpenMode.ForRead) as T; + return trans.GetObject(id); } } return null; @@ -53,7 +57,7 @@ public static T GetAt(this DBDictionary dict, string key, Transaction trans = /// 事务 /// 键 /// 值 - public static void SetAt(this DBDictionary dict, string key, T obj, Transaction trans = null) where T : DBObject + public static void SetAt(this DBDictionary dict, string key, T obj, Transaction? trans = null) where T : DBObject { trans ??= DBTrans.Top.Transaction; using (dict.ForWrite()) @@ -71,9 +75,9 @@ public static void SetAt(this DBDictionary dict, string key, T obj, Transacti /// 字典 /// 键值 /// 扩展数据 - public static XRecordDataList GetXRecord(this DBDictionary dict, string key) + public static XRecordDataList? GetXRecord(this DBDictionary dict, string key) { - Xrecord rec = dict.GetAt(key); + Xrecord? rec = dict.GetAt(key); if (rec != null) return rec.Data; return null; @@ -98,9 +102,9 @@ public static void SetXRecord(this DBDictionary dict, string key, XRecordDataLis /// 对象 /// 事务 /// 扩展字典对象 - public static DBDictionary GetXDictionary(this DBObject obj, Transaction trans = null) + public static DBDictionary? GetXDictionary(this DBObject obj, DBTrans? trans = null) { - trans ??= DBTrans.Top.Transaction; + trans ??= DBTrans.Top; ObjectId id = obj.ExtensionDictionary; if (id.IsNull) { @@ -213,9 +217,10 @@ public static void SetValue(this DataCell cell, CellType type, object value) /// 是否创建子字典 /// 键值列表 /// 字典 - public static DBDictionary GetSubDictionary(this DBDictionary dict, bool createSubDictionary, IEnumerable dictNames, Transaction trans = null) + public static DBDictionary? GetSubDictionary(this DBDictionary dict, bool createSubDictionary, IEnumerable dictNames, DBTrans? trans = null) { - trans ??= DBTrans.Top.Transaction; + DBDictionary? newdict = null; + trans ??= DBTrans.Top; if (createSubDictionary) { using (dict.ForWrite()) @@ -223,16 +228,16 @@ public static DBDictionary GetSubDictionary(this DBDictionary dict, bool createS foreach (string name in dictNames) { - if (dict.Contains(name)) + if (dict!.Contains(name)) { - dict = dict.GetAt(name, trans); + newdict = dict.GetAt(name, trans); } else { DBDictionary subDict = new(); - dict.SetAt(name, subDict, trans); - dict = subDict; - dict.TreatElementsAsHard = true; + dict.SetAt(name, subDict, trans); + newdict = subDict; + newdict.TreatElementsAsHard = true; } } } @@ -241,13 +246,13 @@ public static DBDictionary GetSubDictionary(this DBDictionary dict, bool createS foreach (string name in dictNames) { if (dict.Contains(name)) - dict = dict.GetAt(name, trans); + newdict = dict.GetAt(name, trans); else return null; } } - return dict; + return newdict; } diff --git a/src/IFoxCAD.Cad/ExtensionMethod/EditorEx.cs b/src/IFoxCAD.Cad/ExtensionMethod/EditorEx.cs index 4ca861d..7ba92f8 100644 --- a/src/IFoxCAD.Cad/ExtensionMethod/EditorEx.cs +++ b/src/IFoxCAD.Cad/ExtensionMethod/EditorEx.cs @@ -15,7 +15,7 @@ public static class EditorEx /// 点 /// 过滤器 /// 选择集结果类 - public static PromptSelectionResult SelectAtPoint(this Editor editor, Point3d point, SelectionFilter filter = default) + public static PromptSelectionResult SelectAtPoint(this Editor editor, Point3d point, SelectionFilter? filter = default) { return editor.SelectCrossingWindow(point, point, filter); } @@ -56,10 +56,10 @@ public static SelectionSet SelectByLineWeight(this Editor editor, LineWeight lin } - public static PromptSelectionResult SSGet(this Editor editor, string mode = null, SelectionFilter filter = null, string[] messages = null, string[] keywords = null) + public static PromptSelectionResult? SSGet(this Editor editor, string? mode = null, SelectionFilter? filter = null, string[]? messages = null, string[]? keywords = null) { var pso = new PromptSelectionOptions(); - PromptSelectionResult ss = null; + PromptSelectionResult? ss = null; if (mode != null) { mode = mode.ToUpper(); @@ -557,8 +557,8 @@ public static Matrix3d GetMatrixFromMDcsToPDcs(this Editor editor) Matrix3d mat; using (Transaction tr = db.TransactionManager.StartTransaction()) { - Viewport vp = tr.GetObject(editor.CurrentViewportObjectId, OpenMode.ForRead) as Viewport; - if (vp.Number == 1) + Viewport? vp = tr.GetObject(editor.CurrentViewportObjectId, OpenMode.ForRead) as Viewport; + if (vp?.Number == 1) { try { @@ -571,7 +571,7 @@ public static Matrix3d GetMatrixFromMDcsToPDcs(this Editor editor) throw new Autodesk.AutoCAD.Runtime.Exception(ErrorStatus.InvalidInput, "Aucun fenêtre active"); } } - Point3d vCtr = new(vp.ViewCenter.X, vp.ViewCenter.Y, 0.0); + Point3d vCtr = new(vp!.ViewCenter.X, vp.ViewCenter.Y, 0.0); mat = Matrix3d.Displacement(vCtr.GetAsVector().Negate()); mat = Matrix3d.Displacement(vp.CenterPoint.GetAsVector()) * mat; mat = Matrix3d.Scaling(vp.CustomScale, vp.CenterPoint) * mat; @@ -857,7 +857,7 @@ public static PromptResult GetString(this Editor ed, string Message, string Defa /// lisp语句 /// 缓冲结果,返回值 #pragma warning disable IDE0060 // 删除未使用的参数 - public static ResultBuffer RunLisp(this Editor ed, string arg) + public static ResultBuffer? RunLisp(this Editor ed, string arg) #pragma warning restore IDE0060 // 删除未使用的参数 { _ = AcedEvaluateLisp(arg, out IntPtr rb); diff --git a/src/IFoxCAD.Cad/ExtensionMethod/EntityEx.cs b/src/IFoxCAD.Cad/ExtensionMethod/EntityEx.cs index 0c01b0a..d94875e 100644 --- a/src/IFoxCAD.Cad/ExtensionMethod/EntityEx.cs +++ b/src/IFoxCAD.Cad/ExtensionMethod/EntityEx.cs @@ -12,25 +12,23 @@ public static class EntityEx /// 刷新实体显示 /// /// 实体对象 - public static void Flush(this Entity entity, Transaction trans = null) + public static void Flush(this Entity entity, DBTrans? trans = null) { - if (entity is null) - { - throw new ArgumentNullException(nameof(entity)); - } - if (trans is null) - { - trans = DBTrans.Top.Transaction; - } + //if (entity is null) + //{ + // throw new ArgumentNullException(nameof(entity)); + //} + trans ??= DBTrans.Top; entity.RecordGraphicsModified(true); - trans.TransactionManager.QueueForGraphicsFlush(); + trans.Transaction.TransactionManager.QueueForGraphicsFlush(); + trans.Document?.TransactionManager.FlushGraphics(); } /// /// 刷新实体显示 /// /// 实体id - public static void Flush(this ObjectId id) => Flush(DBTrans.Top.GetObject(id)); + public static void Flush(this ObjectId id) => Flush(DBTrans.Top.GetObject(id)!); #endregion #region 多段线端点坐标 @@ -40,12 +38,12 @@ public static void Flush(this Entity entity, Transaction trans = null) /// 二维多段线 /// 事务 /// 端点坐标集合 - public static IEnumerable GetPoints(this Polyline2d pl2d, Transaction tr = null) + public static IEnumerable GetPoints(this Polyline2d pl2d, DBTrans? tr = null) { - tr ??= DBTrans.Top.Transaction; + tr ??= DBTrans.Top; foreach (ObjectId id in pl2d) { - yield return ((Vertex2d)tr.GetObject(id, OpenMode.ForRead)).Position; + yield return tr.GetObject(id)!.Position; } } @@ -55,12 +53,12 @@ public static IEnumerable GetPoints(this Polyline2d pl2d, Transaction t /// 三维多段线 /// 事务 /// 端点坐标集合 - public static IEnumerable GetPoints(this Polyline3d pl3d, Transaction tr = null) + public static IEnumerable GetPoints(this Polyline3d pl3d, DBTrans? tr = null) { - tr ??= DBTrans.Top.Transaction; + tr ??= DBTrans.Top; foreach (ObjectId id in pl3d) { - yield return ((PolylineVertex3d)tr.GetObject(id, OpenMode.ForRead)).Position; + yield return tr.GetObject(id, OpenMode.ForRead)!.Position; } } @@ -171,14 +169,7 @@ public static Extents3d GetExtents(this IEnumerable ents) { ext.AddExtents(item.GeometricExtents); } - - //var it = ents.GetEnumerator(); - //var ext = it.Current.GeometricExtents; - //while (it.MoveNext()) - // ext.AddExtents(it.Current.GeometricExtents); return ext; - - } #endregion @@ -316,7 +307,7 @@ public static Circle CreateCircle(Point3d startPoint, Point3d endPoint) /// 第二点 /// 第三点 /// - public static Circle CreateCircle(Point3d pt1, Point3d pt2, Point3d pt3) + public static Circle? CreateCircle(Point3d pt1, Point3d pt2, Point3d pt3) { //先判断三点是否共线,得到pt1点指向pt2、pt2点的矢量 Vector3d va = pt1.GetVectorTo(pt2); @@ -351,14 +342,14 @@ public static Circle CreateCircle(Point3d pt1, Point3d pt2, Point3d pt3) /// 裁剪多边形点表 public static void ClipBlockRef(this BlockReference bref, IEnumerable pt3ds) { - if (bref == null) - { - throw new ArgumentNullException(nameof(bref)); - } - if (pt3ds == null) - { - throw new ArgumentNullException(nameof(pt3ds)); - } + //if (bref == null) + //{ + // throw new ArgumentNullException(nameof(bref)); + //} + //if (pt3ds == null) + //{ + // throw new ArgumentNullException(nameof(pt3ds)); + //} Matrix3d mat = bref.BlockTransform.Inverse(); var pts = pt3ds @@ -368,7 +359,7 @@ public static void ClipBlockRef(this BlockReference bref, IEnumerable p SpatialFilterDefinition sfd = new(pts, Vector3d.ZAxis, 0.0, 0.0, 0.0, true); using SpatialFilter sf = new() { Definition = sfd }; - var dict = bref.GetXDictionary().GetSubDictionary(true, new string[] { filterDictName }); + var dict = bref.GetXDictionary()!.GetSubDictionary(true, new string[] { filterDictName })!; dict.SetAt(spatialName, sf); //SetToDictionary(dict, spatialName, sf); } @@ -381,10 +372,10 @@ public static void ClipBlockRef(this BlockReference bref, IEnumerable p /// 第二角点 public static void ClipBlockRef(this BlockReference bref, Point3d pt1, Point3d pt2) { - if (bref == null) - { - throw new ArgumentNullException(nameof(bref)); - } + //if (bref == null) + //{ + // throw new ArgumentNullException(nameof(bref)); + //} Matrix3d mat = bref.BlockTransform.Inverse(); pt1 = pt1.TransformBy(mat); pt2 = pt2.TransformBy(mat); @@ -396,7 +387,7 @@ public static void ClipBlockRef(this BlockReference bref, Point3d pt1, Point3d p SpatialFilterDefinition sfd = new(pts, Vector3d.ZAxis, 0.0, 0.0, 0.0, true); using SpatialFilter sf = new() { Definition = sfd }; - var dict = bref.GetXDictionary().GetSubDictionary(true, new string[] { filterDictName }); + var dict = bref.GetXDictionary()!.GetSubDictionary(true, new string[] { filterDictName })!; dict.SetAt(spatialName, sf); //SetToDictionary(dict, spatialName, sf); } diff --git a/src/IFoxCAD.Cad/ExtensionMethod/Enums.cs b/src/IFoxCAD.Cad/ExtensionMethod/Enums.cs index 9cae1fb..15115df 100644 --- a/src/IFoxCAD.Cad/ExtensionMethod/Enums.cs +++ b/src/IFoxCAD.Cad/ExtensionMethod/Enums.cs @@ -101,7 +101,7 @@ public static string GetDesc(this Enum val) { return val.ToString(); } - return (attributes.Single() as DescriptionAttribute).Description; + return ((DescriptionAttribute)attributes.Single()).Description; } } diff --git a/src/IFoxCAD.Cad/ExtensionMethod/GeometryEx.cs b/src/IFoxCAD.Cad/ExtensionMethod/GeometryEx.cs index 7b13404..27b0eeb 100644 --- a/src/IFoxCAD.Cad/ExtensionMethod/GeometryEx.cs +++ b/src/IFoxCAD.Cad/ExtensionMethod/GeometryEx.cs @@ -19,7 +19,7 @@ public static class GeometryEx public static PointOnRegionType PointOnRegion(this IEnumerable pts, Point2d pt) { //遍历点集并生成首尾连接的多边形 - var ptlst = new LoopList(pts); + var ptlst = new LinkedList(pts); if (ptlst.Count < 3) return PointOnRegionType.Error; @@ -69,7 +69,7 @@ public static PointOnRegionType PointOnRegion(this IEnumerable pts, Poi public static PointOnRegionType PointOnRegion(this IEnumerable pts, Point3d pt) { //遍历点集并生成首尾连接的多边形 - var ptlst = new LoopList(pts); + var ptlst = new LinkedList(pts); if (ptlst.First.Value == ptlst.Last.Value) ptlst.RemoveLast(); if (ptlst.Count < 3) @@ -119,9 +119,9 @@ public static PointOnRegionType PointOnRegion(this IEnumerable pts, Poi /// 基准点 /// 输出圆上的点 /// 解析类圆对象 - public static CircularArc2d GetMinCircle(Point2d pt1, Point2d pt2, out LoopList ptlst) + public static CircularArc2d GetMinCircle(Point2d pt1, Point2d pt2, out LinkedList ptlst) { - ptlst = new LoopList { pt1, pt2 }; + ptlst = new LinkedList { pt1, pt2 }; return new CircularArc2d ( @@ -138,14 +138,13 @@ public static CircularArc2d GetMinCircle(Point2d pt1, Point2d pt2, out LoopList< /// 基准点 /// 输出圆上的点 /// 解析类圆对象 - public static CircularArc2d GetMinCircle(Point2d pt1, Point2d pt2, Point2d pt3, out LoopList ptlst) + public static CircularArc2d GetMinCircle(Point2d pt1, Point2d pt2, Point2d pt3, out LinkedList ptlst) { - ptlst = - new LoopList { pt1, pt2, pt3 }; + ptlst = new LinkedList { pt1, pt2, pt3 }; //遍历各点与下一点的向量长度,找到距离最大的两个点 - LoopListNode maxNode = - ptlst.GetNodes().FindByMax + LinkedListNode maxNode = + ptlst.GetNodes().FindByMax ( out double maxLength, node => node.Value.GetDistanceTo(node.Next.Value) @@ -153,7 +152,7 @@ public static CircularArc2d GetMinCircle(Point2d pt1, Point2d pt2, Point2d pt3, //以两点做最小包围圆 CircularArc2d ca2d = - GetMinCircle(maxNode.Value, maxNode.Next.Value, out LoopList tptlst); + GetMinCircle(maxNode.Value, maxNode.Next.Value, out LinkedList tptlst); //如果另一点属于该圆 if (ca2d.IsIn(maxNode.Previous.Value)) @@ -162,9 +161,9 @@ public static CircularArc2d GetMinCircle(Point2d pt1, Point2d pt2, Point2d pt3, ptlst = tptlst; return ca2d; } - //否则按三点做圆 - ptlst.SetFirst(maxNode); + //ptlst.SetFirst(maxNode); + ptlst = new LinkedList { maxNode.Value, maxNode.Next.Value, maxNode.Previous.Value }; ca2d = new CircularArc2d(pt1, pt2, pt3); ca2d.SetAngles(0, Math.PI * 2); return ca2d; @@ -179,19 +178,19 @@ public static CircularArc2d GetMinCircle(Point2d pt1, Point2d pt2, Point2d pt3, /// 基准点 /// 输出圆上的点 /// 解析类圆对象 - public static CircularArc2d GetMinCircle(Point2d pt1, Point2d pt2, Point2d pt3, Point2d pt4, out LoopList ptlst) + public static CircularArc2d? GetMinCircle(Point2d pt1, Point2d pt2, Point2d pt3, Point2d pt4, out LinkedList? ptlst) { - LoopList iniptlst = new() { pt1, pt2, pt3, pt4 }; + var iniptlst = new LinkedList() { pt1, pt2, pt3, pt4 }; ptlst = null; - CircularArc2d ca2d = null; + CircularArc2d? ca2d = null; //遍历C43的组合,环链表的优势在这里 - foreach (LoopListNode firstNode in iniptlst.GetNodes()) + foreach (LinkedListNode firstNode in iniptlst.GetNodes()) { //获取各组合下三点的最小包围圆 - LoopListNode secondNode = firstNode.Next; - LoopListNode thirdNode = secondNode.Next; - CircularArc2d tca2d = GetMinCircle(firstNode.Value, secondNode.Value, thirdNode.Value, out LoopList tptlst); + var secondNode = firstNode.Next; + var thirdNode = secondNode.Next; + CircularArc2d tca2d = GetMinCircle(firstNode.Value, secondNode.Value, thirdNode.Value, out LinkedList tptlst); //如果另一点属于该圆,并且半径小于当前值就把它做为候选解 if (tca2d.IsIn(firstNode.Previous.Value)) @@ -345,7 +344,7 @@ public static OrientationType IsClockWise(this IEnumerable pnts) /// 点集 /// 输出圆上的点 /// 解析类圆对象 - public static CircularArc2d GetMinCircle(this List pnts, out LoopList ptlst) + public static CircularArc2d? GetMinCircle(this List pnts, out LinkedList? ptlst) { //点数较小时直接返回 switch (pnts.Count) @@ -355,7 +354,7 @@ public static CircularArc2d GetMinCircle(this List pnts, out LoopList

{ pnts[0] }; + ptlst = new LinkedList { pnts[0] }; return new CircularArc2d(pnts[0], 0); case 2: @@ -371,7 +370,7 @@ public static CircularArc2d GetMinCircle(this List pnts, out LoopList

ca2d.Center.GetDistanceTo(pnt)); @@ -383,7 +382,7 @@ public static CircularArc2d GetMinCircle(this List pnts, out LoopList

pnts, out LoopList

ca2d.Center.GetDistanceTo(pnt)); + .FindByMax(pnt => ca2d!.Center.GetDistanceTo(pnt)); } tpnts[0] = ptlst.First.Value; tpnts[1] = ptlst.First.Next.Value; @@ -413,7 +412,7 @@ public static CircularArc2d GetMinCircle(this List pnts, out LoopList

/// 点集 /// 凸包 - public static List ConvexHull(this List points) + public static List? ConvexHull(this List points) { if (points == null) return null; diff --git a/src/IFoxCAD.Cad/ExtensionMethod/ObjectIdEx.cs b/src/IFoxCAD.Cad/ExtensionMethod/ObjectIdEx.cs index 84749e2..8408a48 100644 --- a/src/IFoxCAD.Cad/ExtensionMethod/ObjectIdEx.cs +++ b/src/IFoxCAD.Cad/ExtensionMethod/ObjectIdEx.cs @@ -16,7 +16,7 @@ public static class ObjectIdEx /// 打开模式 /// 打开删除对象 /// 指定类型对象 - public static T GetObject(this ObjectId id, OpenMode mode = OpenMode.ForRead, bool openErased = false, Transaction tr = default) where T : DBObject + public static T? GetObject(this ObjectId id, OpenMode mode = OpenMode.ForRead, bool openErased = false, Transaction? tr = default) where T : DBObject { tr ??= DBTrans.Top.Transaction; //tr = Env.GetTrans(tr); @@ -32,7 +32,7 @@ public static T GetObject(this ObjectId id, OpenMode mode = OpenMode.ForRead, /// 打开模式 /// 打开删除对象 /// 指定类型对象集合 - public static IEnumerable GetObject(this IEnumerable ids, OpenMode mode = OpenMode.ForRead, bool openErased = false, Transaction tr = default) where T : DBObject + public static IEnumerable GetObject(this IEnumerable ids, OpenMode mode = OpenMode.ForRead, bool openErased = false, Transaction? tr = default) where T : DBObject { return ids.Select(id => id.GetObject(mode, openErased, tr)); } @@ -68,7 +68,7 @@ public static void Erase(this ObjectId id) { if (id.IsOk()) { - var ent = id.GetObject(); + var ent = id.GetObject()!; using (ent.ForWrite()) { ent.Erase(); diff --git a/src/IFoxCAD.Cad/ExtensionMethod/SelectionSetEx.cs b/src/IFoxCAD.Cad/ExtensionMethod/SelectionSetEx.cs index b4a94eb..9a02642 100644 --- a/src/IFoxCAD.Cad/ExtensionMethod/SelectionSetEx.cs +++ b/src/IFoxCAD.Cad/ExtensionMethod/SelectionSetEx.cs @@ -66,13 +66,13 @@ public static IEnumerable> GetObjectIdGroup(this Sel /// 事务 /// 打开模式 /// 图元集合 - public static IEnumerable GetEntities(this SelectionSet ss, OpenMode openMode = OpenMode.ForRead, Transaction tr = default) where T : Entity + public static IEnumerable GetEntities(this SelectionSet ss, OpenMode openMode = OpenMode.ForRead, DBTrans? tr = default) where T : Entity { - tr ??= DBTrans.Top.Transaction; + tr ??= DBTrans.Top; return ss .GetObjectIds() - .Select(id => tr.GetObject(id, openMode) as T); + .Select(id => tr.GetObject(id, openMode)); } #endregion @@ -87,9 +87,9 @@ public static IEnumerable GetEntities(this SelectionSet ss, OpenMode openM /// 事务 /// 打开模式 /// 处理函数 - public static void ForEach(this SelectionSet ss, Action action, OpenMode openMode = OpenMode.ForRead, Transaction tr = default) where T : Entity + public static void ForEach(this SelectionSet ss, Action action, OpenMode openMode = OpenMode.ForRead, DBTrans? tr = default) where T : Entity { - foreach (T ent in ss.GetEntities(openMode, tr)) + foreach (T? ent in ss.GetEntities(openMode, tr)) { action?.Invoke(ent); } diff --git a/src/IFoxCAD.Cad/ExtensionMethod/SymbolTableEx.cs b/src/IFoxCAD.Cad/ExtensionMethod/SymbolTableEx.cs index 7b4bb9d..75c2d40 100644 --- a/src/IFoxCAD.Cad/ExtensionMethod/SymbolTableEx.cs +++ b/src/IFoxCAD.Cad/ExtensionMethod/SymbolTableEx.cs @@ -66,15 +66,19 @@ public static bool Delete(this SymbolTable table, } table.CurrentSymbolTable.GenerateUsageData(); var ltr = table.GetRecord(name); - if (ltr.IsUsed) + if (ltr is not null) { - return false; - } - using (ltr.ForWrite()) - { - ltr.Erase(); + if (ltr.IsUsed) + { + return false; + } + using (ltr.ForWrite()) + { + ltr.Erase(); + } + return true; } - return true; + return false; } #endregion @@ -89,7 +93,7 @@ public static bool Delete(this SymbolTable table, /// 添加属性定义的委托 /// 块定义id /// TODO: 需要测试匿名块等特殊的块是否能定义 - public static ObjectId Add(this SymbolTable table, string name, Action action = null, Func> ents = null, Func> attdef = null) + public static ObjectId Add(this SymbolTable table, string name, Action? action = null, Func>? ents = null, Func>? attdef = null) { return table.Add(name, btr => { @@ -104,15 +108,8 @@ public static ObjectId Add(this SymbolTable table, { btr.AddEntity(adddefres); } - //if (ents is not null) - //{ - // btr.AddEntity(ents?.Invoke()); - //} - //if (attdef is not null) - //{ - // btr.AddEntity(attdef?.Invoke()); - //} - }); + + }); } ///

/// 添加块定义 @@ -122,9 +119,19 @@ public static ObjectId Add(this SymbolTable table, /// 图元 /// 属性定义 /// - public static ObjectId Add(this SymbolTable table, string name, IEnumerable ents = null, IEnumerable attdef = null) + public static ObjectId Add(this SymbolTable table, string name, IEnumerable? ents = null, IEnumerable? attdef = null) { - return table.Add(name, null, () => { return ents; }, () => { return attdef; }); + return table.Add(name, btr => + { + if (ents != null) + { + btr.AddEntity(ents); + } + if (attdef != null) + { + btr.AddEntity(attdef); + } + }); } /// @@ -276,7 +283,7 @@ public static ObjectId Add(this SymbolTable public static ObjectId AddWithChange(this SymbolTable table, string textStyleName, string smallFont, - string bigFont = null, + string bigFont = "", double xScale = 1, double height = 0, bool forceChange = true) @@ -288,7 +295,7 @@ public static ObjectId AddWithChange(this SymbolTable实体 /// 事务管理器 /// 对象 id - public static ObjectId AddEntity(this BlockTableRecord btr, Entity entity, Transaction trans = null) + public static ObjectId AddEntity(this BlockTableRecord btr, Entity entity, Transaction? trans = null) { - if (entity is null) - throw new ArgumentNullException(nameof(entity), "对象为 null"); + //if (entity is null) + // throw new ArgumentNullException(nameof(entity), "对象为 null"); ObjectId id; trans ??= DBTrans.Top.Transaction; @@ -40,10 +40,10 @@ public static ObjectId AddEntity(this BlockTableRecord btr, Entity entity, Trans /// 事务 /// 实体集合 /// 对象 id 列表 - public static IEnumerable AddEntity(this BlockTableRecord btr, IEnumerable ents, Transaction trans = null) where T : Entity + public static IEnumerable AddEntity(this BlockTableRecord btr, IEnumerable ents, Transaction? trans = null) where T : Entity { - if (ents.Any(ent => ent is null)) - throw new ArgumentNullException(nameof(ents), "实体集合内存在 null 对象"); + //if (ents.Any(ent => ent is null)) + // throw new ArgumentNullException(nameof(ents), "实体集合内存在 null 对象"); trans ??= DBTrans.Top.Transaction; using (btr.ForWrite()) @@ -81,9 +81,9 @@ public static IEnumerable AddEntity(this BlockTableRecord btr, params /// 图元属性设置委托 /// 事务管理器 /// 图元id - private static ObjectId AddEnt(this BlockTableRecord btr, T ent, Action action, Transaction trans) where T : Entity + private static ObjectId AddEnt(this BlockTableRecord btr, T ent, Action? action, Transaction? trans) where T : Entity { - trans ??= DBTrans.Top.Transaction; + //trans ??= DBTrans.Top.Transaction; action?.Invoke(ent); return btr.AddEntity(ent, trans); } @@ -94,10 +94,10 @@ private static ObjectId AddEnt(this BlockTableRecord btr, T ent, Action ac /// 返回图元的委托 /// 事务 /// 图元id,如果委托返回 null,则为 ObjectId.Null - public static ObjectId AddEnt(this BlockTableRecord btr, Func action, Transaction transaction) + public static ObjectId AddEnt(this BlockTableRecord btr, Func action, Transaction? transaction) { - transaction ??= DBTrans.Top.Transaction; - var ent = action?.Invoke(); + //transaction ??= DBTrans.Top.Transaction; + var ent = action.Invoke(); if (ent == null) { return ObjectId.Null; @@ -114,7 +114,7 @@ public static ObjectId AddEnt(this BlockTableRecord btr, Func action, Tr /// 绘图空间 /// 直线属性设置委托 /// 直线的id - public static ObjectId AddLine(this BlockTableRecord btr, Point3d start, Point3d end, Action action = default, Transaction trans = default) + public static ObjectId AddLine(this BlockTableRecord btr, Point3d start, Point3d end, Action? action = default, Transaction? trans = default) { var line = new Line(start, end); return btr.AddEnt(line, action, trans); @@ -128,7 +128,7 @@ public static ObjectId AddLine(this BlockTableRecord btr, Point3d start, Point3d /// 圆属性设置委托 /// 事务管理器 /// 圆的id - public static ObjectId AddCircle(this BlockTableRecord btr, Point3d center, double radius, Action action = default, Transaction trans = default) + public static ObjectId AddCircle(this BlockTableRecord btr, Point3d center, double radius, Action? action = default, Transaction? trans = default) { var circle = new Circle(center, Vector3d.ZAxis, radius); return btr.AddEnt(circle, action, trans); @@ -145,10 +145,15 @@ public static ObjectId AddCircle(this BlockTableRecord btr, Point3d center, doub /// 事务管理器 /// 三点有外接圆则返回圆的id,否则返回ObjectId.Null public static ObjectId AddCircle(this BlockTableRecord btr, Point3d p0, Point3d p1, Point3d p2, - Action action = default, Transaction trans = default) + Action? action = default, Transaction? trans = default) { - Circle circle = EntityEx.CreateCircle(p0, p1, p2); - return circle is not null ? btr.AddEnt(circle, action, trans) : throw new ArgumentNullException(nameof(circle), "对象为 null"); + var circle = EntityEx.CreateCircle(p0, p1, p2); + //return circle is not null ? btr.AddEnt(circle, action, trans) : throw new ArgumentNullException(nameof(circle), "对象为 null"); + if (circle == null) + { + throw new ArgumentNullException(nameof(circle), "对象为 null"); + } + return btr.AddEnt(circle, action, trans); } /// /// 在指定的绘图空间添加轻多段线 @@ -161,7 +166,7 @@ public static ObjectId AddCircle(this BlockTableRecord btr, Point3d p0, Point3d /// 轻多段线属性设置委托 /// 事务管理器 /// 轻多段线id - public static ObjectId AddPline(this BlockTableRecord btr, List pts, List bulges = default, List startWidths = default, List endWidths = default, Action action = default, Transaction trans = default) + public static ObjectId AddPline(this BlockTableRecord btr, List pts, List? bulges = default, List? startWidths = default, List? endWidths = default, Action? action = default, Transaction? trans = default) { bulges ??= new List(new double[pts.Count]); startWidths ??= new List(new double[pts.Count]); @@ -183,7 +188,7 @@ public static ObjectId AddPline(this BlockTableRecord btr, List pts, Li /// 轻多段线属性设置委托 /// 事务管理器 /// 轻多段线id - public static ObjectId AddPline(this BlockTableRecord btr, List<(Point3d pt, double bulge, double startWidth, double endWidth)> pts, Action action = default, Transaction trans = default) + public static ObjectId AddPline(this BlockTableRecord btr, List<(Point3d pt, double bulge, double startWidth, double endWidth)> pts, Action? action = default, Transaction? trans = default) { Polyline pl = new(); @@ -207,7 +212,7 @@ public static ObjectId AddPline(this BlockTableRecord btr, List<(Point3d pt, dou /// 圆弧属性设置委托 /// 事务管理器 /// 圆弧id - public static ObjectId AddArc(this BlockTableRecord btr, Point3d startPoint, Point3d pointOnArc, Point3d endPoint, Action action = default, Transaction trans = default) + public static ObjectId AddArc(this BlockTableRecord btr, Point3d startPoint, Point3d pointOnArc, Point3d endPoint, Action? action = default, Transaction? trans = default) { var arc = EntityEx.CreateArc(startPoint, pointOnArc, endPoint); return btr.AddEnt(arc, action, trans); @@ -223,7 +228,7 @@ public static ObjectId AddArc(this BlockTableRecord btr, Point3d startPoint, Poi /// 事务 /// 打开模式 /// 实体集合 - public static IEnumerable GetEntities(this BlockTableRecord btr, OpenMode mode = OpenMode.ForRead, Transaction trans = null) where T : Entity + public static IEnumerable GetEntities(this BlockTableRecord btr, OpenMode mode = OpenMode.ForRead, Transaction? trans = default) where T : Entity { trans ??= DBTrans.Top.Transaction; return @@ -265,7 +270,7 @@ public static IEnumerable> GetObjectIds(this BlockTa /// 块表 /// 事务 /// 绘制顺序表 - public static DrawOrderTable GetDrawOrderTable(this BlockTableRecord btr, Transaction tr = null) + public static DrawOrderTable? GetDrawOrderTable(this BlockTableRecord btr, Transaction? tr = default) { tr ??= DBTrans.Top.Transaction; return tr.GetObject(btr.DrawOrderTableId, OpenMode.ForRead) as DrawOrderTable; @@ -288,12 +293,12 @@ public static ObjectId InsertBlock(this BlockTableRecord blockTableRecord, Point string blockName, Scale3d scale = default, double rotation = default, - Dictionary atts = default, Transaction trans = null) + Dictionary? atts = default, Transaction? trans = null) { trans ??= DBTrans.Top.Transaction; if (!DBTrans.Top.BlockTable.Has(blockName)) { - DBTrans.Top.Editor.WriteMessage($"\n不存在名字为{blockName}的块定义。"); + DBTrans.Top.Editor?.WriteMessage($"\n不存在名字为{blockName}的块定义。"); return ObjectId.Null; } return blockTableRecord.InsertBlock(position, DBTrans.Top.BlockTable[blockName], scale, rotation, atts, trans); @@ -311,12 +316,12 @@ public static ObjectId InsertBlock(this BlockTableRecord blockTableRecord, Point ObjectId blockId, Scale3d scale = default, double rotation = default, - Dictionary atts = default, Transaction trans = null) + Dictionary? atts = default, Transaction? trans = null) { trans ??= DBTrans.Top.Transaction; if (!DBTrans.Top.BlockTable.Has(blockId)) { - DBTrans.Top.Editor.WriteMessage($"\n不存在名字为{DBTrans.Top.GetObject(blockId).Name}的块定义。"); + DBTrans.Top.Editor?.WriteMessage($"\n不存在块定义。"); return ObjectId.Null; } using var blockref = new BlockReference(position, blockId) @@ -327,7 +332,7 @@ public static ObjectId InsertBlock(this BlockTableRecord blockTableRecord, Point var objid = blockTableRecord.AddEntity(blockref); if (atts != default) { - var btr = DBTrans.Top.GetObject(blockref.BlockTableRecord); + var btr = DBTrans.Top.GetObject(blockref.BlockTableRecord)!; if (btr.HasAttributeDefinitions) { var attdefs = btr diff --git a/src/IFoxCAD.Cad/IFoxCAD.Cad.csproj b/src/IFoxCAD.Cad/IFoxCAD.Cad.csproj index e04c03c..9e83c0c 100644 --- a/src/IFoxCAD.Cad/IFoxCAD.Cad.csproj +++ b/src/IFoxCAD.Cad/IFoxCAD.Cad.csproj @@ -3,6 +3,7 @@ net45 true + enable 0.2.0.6 InspireFunction xsfhlzh;vicwjb diff --git a/src/IFoxCAD.Cad/ResultData/LispList.cs b/src/IFoxCAD.Cad/ResultData/LispList.cs index 1193441..1af143a 100644 --- a/src/IFoxCAD.Cad/ResultData/LispList.cs +++ b/src/IFoxCAD.Cad/ResultData/LispList.cs @@ -40,7 +40,7 @@ public virtual List Value /// /// 组码 /// 组码值 - public override void Add(int code, object obj) + public override void Add(int code, object? obj) { if (code < 5000) { @@ -54,7 +54,7 @@ public override void Add(int code, object obj) /// /// dxfcode枚举值 /// 组码值 - public void Add(LispDataType code, object obj) + public void Add(LispDataType code, object? obj) { Add((int)code, obj); } diff --git a/src/IFoxCAD.Cad/Runtime/AcadVersion.cs b/src/IFoxCAD.Cad/Runtime/AcadVersion.cs index cb6870a..5761498 100644 --- a/src/IFoxCAD.Cad/Runtime/AcadVersion.cs +++ b/src/IFoxCAD.Cad/Runtime/AcadVersion.cs @@ -51,7 +51,7 @@ public static List Versions /// 已打开的cad的版本号 /// 已打开cad的application对象 /// cad版本号对象 - public static CadVersion FromApp(object app) + public static CadVersion? FromApp(object app) { if (app == null) { diff --git a/src/IFoxCAD.Cad/Runtime/CadVersion.cs b/src/IFoxCAD.Cad/Runtime/CadVersion.cs index 95cb119..6be6af8 100644 --- a/src/IFoxCAD.Cad/Runtime/CadVersion.cs +++ b/src/IFoxCAD.Cad/Runtime/CadVersion.cs @@ -20,12 +20,12 @@ public record CadVersion /// /// 注册表名称 /// - public string ProductName; + public string? ProductName; /// /// 注册表位置 /// - public string ProductRootKey; + public string? ProductRootKey; /// /// 转换为字符串 diff --git a/src/IFoxCAD.Cad/Runtime/DBTrans.cs b/src/IFoxCAD.Cad/Runtime/DBTrans.cs index 31fdccb..5136c5a 100644 --- a/src/IFoxCAD.Cad/Runtime/DBTrans.cs +++ b/src/IFoxCAD.Cad/Runtime/DBTrans.cs @@ -6,7 +6,7 @@ public class DBTrans : IDisposable /// /// 文档锁 /// - private DocumentLock documentLock = default; + private readonly DocumentLock? documentLock; /// /// 是否释放资源 /// @@ -14,7 +14,7 @@ public class DBTrans : IDisposable /// /// 是否提交事务 /// - private bool _commit; + private readonly bool _commit; /// /// 事务栈 /// @@ -49,19 +49,15 @@ public static DBTrans Top /// /// 文档 /// - public Document Document { get; private set; } + public Document? Document { get; private set; } /// /// 命令行 /// - public Editor Editor { get; private set; } + public Editor? Editor { get; private set; } /// /// 事务管理器 /// public Transaction Transaction { get; private set; } - /// - /// 文档对象是否存在 - /// - public bool HasDocument { get; private set; } #endregion @@ -71,12 +67,19 @@ public static DBTrans Top /// /// 要打开的文档 /// 事务是否提交 - public DBTrans(Document doc = null, bool commit = true, bool doclock = false) + /// 是否锁文档 + public DBTrans(Document? doc = null, bool commit = true, bool doclock = false) { Document = doc ?? Application.DocumentManager.MdiActiveDocument; Database = Document.Database; Editor = Document.Editor; - Init(commit, doclock); + Transaction = Database.TransactionManager.StartTransaction(); + _commit = commit; + if (doclock) + { + documentLock = Document.LockDocument(); + } + dBTrans.Push(this); } /// @@ -87,7 +90,9 @@ public DBTrans(Document doc = null, bool commit = true, bool doclock = false) public DBTrans(Database database, bool commit = true) { Database = database; - Init(commit, false); + Transaction = Database.TransactionManager.StartTransaction(); + _commit = commit; + dBTrans.Push(this); } /// /// 构造函数,打开文件,默认提交事务 @@ -96,31 +101,25 @@ public DBTrans(Database database, bool commit = true) /// 事务是否提交 public DBTrans(string fileName, bool commit = true) { - Database = new Database(false, true); + Database = CreateDatabase(fileName); + Transaction = Database.TransactionManager.StartTransaction(); + _commit = commit; + dBTrans.Push(this); + } + + private static Database CreateDatabase(string fileName) + { + var db = new Database(false, true); if (Path.GetExtension(fileName).ToLower().Contains("dxf")) { - Database.DxfIn(fileName, null); + db.DxfIn(fileName, null); } else { - Database.ReadDwgFile(fileName, FileShare.Read, true, null); + db.ReadDwgFile(fileName, FileShare.Read, true, null); } - Database.CloseInput(true); - Init(commit, false); - } - /// - /// 初始化事务及事务队列、提交模式 - /// - /// 提交模式 - private void Init(bool commit, bool doclock) - { - if (doclock) - { - documentLock = Document.LockDocument(); - } - Transaction = Database.TransactionManager.StartTransaction(); - _commit = commit; - dBTrans.Push(this); + db.CloseInput(true); + return db; } #endregion @@ -146,15 +145,15 @@ public static implicit operator Transaction(DBTrans tr) /// /// 当前绘图空间 /// - public BlockTableRecord CurrentSpace => BlockTable.GetRecord(Database.CurrentSpaceId); + public BlockTableRecord CurrentSpace => BlockTable.GetRecord(Database.CurrentSpaceId)!; /// /// 模型空间 /// - public BlockTableRecord ModelSpace => BlockTable.GetRecord(BlockTable.CurrentSymbolTable[BlockTableRecord.ModelSpace]); + public BlockTableRecord ModelSpace => BlockTable.GetRecord(BlockTable.CurrentSymbolTable[BlockTableRecord.ModelSpace])!; /// /// 图纸空间 /// - public BlockTableRecord PaperSpace => BlockTable.GetRecord(BlockTable.CurrentSymbolTable[BlockTableRecord.PaperSpace]); + public BlockTableRecord PaperSpace => BlockTable.GetRecord(BlockTable.CurrentSymbolTable[BlockTableRecord.PaperSpace])!; /// /// 层表 /// @@ -200,61 +199,60 @@ public static implicit operator Transaction(DBTrans tr) /// /// 命名对象字典 /// - public DBDictionary NamedObjectsDict => GetObject(Database.NamedObjectsDictionaryId); + public DBDictionary NamedObjectsDict => GetObject(Database.NamedObjectsDictionaryId)!; /// /// 组字典 /// - public DBDictionary GroupDict => GetObject(Database.GroupDictionaryId); + public DBDictionary GroupDict => GetObject(Database.GroupDictionaryId)!; /// /// 多重引线样式字典 /// - public DBDictionary MLeaderStyleDict => GetObject(Database.MLeaderStyleDictionaryId); + public DBDictionary MLeaderStyleDict => GetObject(Database.MLeaderStyleDictionaryId)!; /// /// 多线样式字典 /// - public DBDictionary MLStyleDict => GetObject(Database.MLStyleDictionaryId); + public DBDictionary MLStyleDict => GetObject(Database.MLStyleDictionaryId)!; /// /// 材质字典 /// - public DBDictionary MaterialDict => GetObject(Database.MaterialDictionaryId); + public DBDictionary MaterialDict => GetObject(Database.MaterialDictionaryId)!; /// /// 表格样式字典 /// - public DBDictionary TableStyleDict => GetObject(Database.TableStyleDictionaryId); + public DBDictionary TableStyleDict => GetObject(Database.TableStyleDictionaryId)!; /// /// 视觉样式字典 /// - public DBDictionary VisualStyleDict => GetObject(Database.VisualStyleDictionaryId); + public DBDictionary VisualStyleDict => GetObject(Database.VisualStyleDictionaryId)!; /// /// 颜色字典 /// - public DBDictionary ColorDict => GetObject(Database.ColorDictionaryId); + public DBDictionary ColorDict => GetObject(Database.ColorDictionaryId)!; /// /// 打印设置字典 /// - public DBDictionary PlotSettingsDict => GetObject(Database.PlotSettingsDictionaryId); + public DBDictionary PlotSettingsDict => GetObject(Database.PlotSettingsDictionaryId)!; /// /// 打印样式表名字典 /// - public DBDictionary PlotStyleNameDict => GetObject(Database.PlotStyleNameDictionaryId); + public DBDictionary PlotStyleNameDict => GetObject(Database.PlotStyleNameDictionaryId)!; /// /// 布局字典 /// - public DBDictionary LayoutDict => GetObject(Database.LayoutDictionaryId); + public DBDictionary LayoutDict => GetObject(Database.LayoutDictionaryId)!; /// /// 数据链接字典 /// - public DBDictionary DataLinkDict => GetObject(Database.DataLinkDictionaryId); -#if ac2013 - /// - /// 详细视图样式字典 - /// - public DBDictionary DetailViewStyleDict => GetObject(Database.DetailViewStyleDictionaryId); - /// - /// 剖面视图样式字典 - /// - public DBDictionary SectionViewStyleDict => GetObject(Database.SectionViewStyleDictionaryId); -#endif + public DBDictionary DataLinkDict => GetObject(Database.DataLinkDictionaryId)!; + /// + /// 详细视图样式字典 + /// + public DBDictionary DetailViewStyleDict => GetObject(Database.DetailViewStyleDictionaryId)!; + /// + /// 剖面视图样式字典 + /// + public DBDictionary SectionViewStyleDict => GetObject(Database.SectionViewStyleDictionaryId)!; + #endregion #region 获取对象 @@ -267,7 +265,7 @@ public static implicit operator Transaction(DBTrans tr) /// 是否打开已删除对象,默认为不打开 /// 是否打开锁定图层对象,默认为不打开 /// 图元对象,类型不匹配时返回 - public T GetObject(ObjectId id, + public T? GetObject(ObjectId id, OpenMode mode = OpenMode.ForRead, bool openErased = false, bool forceOpenOnLockedLayer = false) where T : DBObject @@ -295,6 +293,7 @@ public ObjectId GetObjectId(string handleString) public void Abort() { Transaction.Abort(); + Dispose(false); } public void Commit() diff --git a/src/IFoxCAD.Cad/Runtime/SymbolTable.cs b/src/IFoxCAD.Cad/Runtime/SymbolTable.cs index 5174057..ffd4eca 100644 --- a/src/IFoxCAD.Cad/Runtime/SymbolTable.cs +++ b/src/IFoxCAD.Cad/Runtime/SymbolTable.cs @@ -33,7 +33,7 @@ internal SymbolTable(DBTrans tr, ObjectId tableId) { DTrans = tr; Database = tr.Database; - CurrentSymbolTable = DTrans.GetObject(tableId); + CurrentSymbolTable = DTrans.GetObject(tableId)!; } #endregion @@ -100,12 +100,12 @@ private ObjectId Add(TRecord record) /// 符号表记录名 /// 符号表记录处理函数的无返回值委托 /// 对象id - public ObjectId Add(string name, Action action = null) + public ObjectId Add(string name, Action? action = null) { ObjectId id = this[name]; if (id.IsNull) { - TRecord record = new() + var record = new TRecord() { Name = name }; @@ -137,7 +137,7 @@ private static void Remove(TRecord record) /// 符号表记录名 public void Remove(string name) { - TRecord record = GetRecord(name); + var record = GetRecord(name); if (record != null) { Remove(record); @@ -150,7 +150,7 @@ public void Remove(string name) /// 符号表记录对象id public void Remove(ObjectId id) { - TRecord record = GetRecord(id); + var record = GetRecord(id); if (record != null) { Remove(record); @@ -170,9 +170,10 @@ private static void Change(TRecord record, Action action) { using (record.ForWrite()) { - action?.Invoke(record); + action.Invoke(record); } - Env.Editor.Regen(); + // 调用regen()函数可能会导致卡顿 + //Env.Editor.Regen(); } /// /// 修改符号表 @@ -209,7 +210,7 @@ public void Change(ObjectId id, Action action) /// 符号表记录的id /// 打开模式,默认为只读 /// 符号表记录 - public TRecord GetRecord(ObjectId id, OpenMode openMode = OpenMode.ForRead) => id.IsNull ? null : DTrans.GetObject(id, openMode); + public TRecord? GetRecord(ObjectId id, OpenMode openMode = OpenMode.ForRead) => id.IsNull ? null : DTrans.GetObject(id, openMode); /// /// 获取符号表记录 @@ -217,7 +218,7 @@ public void Change(ObjectId id, Action action) /// 符号表记录名 /// 打开模式,默认为只读 /// 符号表记录 - public TRecord GetRecord(string name, OpenMode openMode = OpenMode.ForRead) => GetRecord(this[name], openMode); + public TRecord? GetRecord(string name, OpenMode openMode = OpenMode.ForRead) => GetRecord(this[name], openMode); /// /// 获取符号表记录 @@ -225,14 +226,21 @@ public void Change(ObjectId id, Action action) /// 符号表记录集合 public IEnumerable GetRecords() { - return this.Select(id => GetRecord(id)); + foreach (var item in this) + { + var record = GetRecord(item); + if (record is not null) + { + yield return record; + } + } } /// /// 获取符号表记录的名字集合 /// /// 记录的名字集合 - public IEnumerable GetRecordNames() => this.Select(id => GetRecord(id).Name); + public IEnumerable GetRecordNames() => GetRecords().Select(record => record.Name); /// /// 获取符合过滤条件的符号表记录名字集合 /// @@ -240,10 +248,10 @@ public IEnumerable GetRecords() /// 记录的名字集合 public IEnumerable GetRecordNames(Func filter) { - foreach (var id in this) + foreach (var item in this) { - var record = GetRecord(id); - if (filter.Invoke(record)) + var record = GetRecord(item); + if (record is not null && filter.Invoke(record)) { yield return record.Name; } @@ -261,7 +269,7 @@ public ObjectId GetRecordFrom(SymbolTable table, string name, b { if (table is null) { - throw new ArgumentNullException(nameof(table)); + throw new ArgumentNullException(nameof(table),"对象为null"); } ObjectId rid = this[name]; diff --git a/src/IFoxCAD.Cad/SelectionFilter/OpFilter.cs b/src/IFoxCAD.Cad/SelectionFilter/OpFilter.cs index f579249..a698aeb 100644 --- a/src/IFoxCAD.Cad/SelectionFilter/OpFilter.cs +++ b/src/IFoxCAD.Cad/SelectionFilter/OpFilter.cs @@ -92,7 +92,7 @@ public override string ToString() /// 过滤器 public static OpFilter Bulid(Func func) { - return func(new OpFilter.Op()).Filter; + return func(new Op()).Filter!; } #region Operator @@ -105,7 +105,7 @@ public class Op /// /// 过滤器属性 /// - internal OpFilter Filter { get; private set; } + internal OpFilter? Filter { get; private set; } internal Op() { @@ -121,11 +121,13 @@ private Op(OpFilter filter) /// /// 操作符类型的可变参数 /// Op对象 +#pragma warning disable CA1822 // 将成员标记为 static public Op And(params Op[] args) +#pragma warning restore CA1822 // 将成员标记为 static { var filter = new OpAnd(); foreach (var op in args) - filter.Add(op.Filter); + filter.Add(op.Filter!); return new Op(filter); } @@ -134,11 +136,13 @@ public Op And(params Op[] args) /// /// 操作符类型的可变参数 /// Op对象 +#pragma warning disable CA1822 // 将成员标记为 static public Op Or(params Op[] args) +#pragma warning restore CA1822 // 将成员标记为 static { var filter = new OpOr(); foreach (var op in args) - filter.Add(op.Filter); + filter.Add(op.Filter!); return new Op(filter); } @@ -147,7 +151,9 @@ public Op Or(params Op[] args) /// /// 组码 /// Op对象 +#pragma warning disable CA1822 // 将成员标记为 static public Op Dxf(int code) +#pragma warning restore CA1822 // 将成员标记为 static { return new Op(new OpEqual(code)); } @@ -158,7 +164,9 @@ public Op Dxf(int code) /// 组码 /// 关系运算符的值,比如">,>,=" /// Op对象 +#pragma warning disable CA1822 // 将成员标记为 static public Op Dxf(int code, string content) +#pragma warning restore CA1822 // 将成员标记为 static { return new Op(new OpComp(content, code)); } @@ -170,7 +178,7 @@ public Op Dxf(int code, string content) /// Op对象 public static Op operator !(Op right) { - right.Filter = !right.Filter; + right.Filter = !right.Filter!; return right; } @@ -182,7 +190,7 @@ public Op Dxf(int code, string content) /// Op对象 public static Op operator ==(Op left, object right) { - var eq = (OpEqual)left.Filter; + var eq = (OpEqual)left.Filter!; eq.SetValue(right); return left; } @@ -195,7 +203,7 @@ public Op Dxf(int code, string content) /// Op对象 public static Op operator !=(Op left, object right) { - var eq = (OpEqual)left.Filter; + var eq = (OpEqual)left.Filter!; eq.SetValue(right); left.Filter = eq.Not; return left; @@ -203,7 +211,7 @@ public Op Dxf(int code, string content) private static Op GetCompOp(string content, Op left, object right) { - var eq = (OpEqual)left.Filter; + var eq = (OpEqual)left.Filter!; var comp = new OpComp(content, eq.Value.TypeCode, right); return new Op(comp); } @@ -284,8 +292,8 @@ private static Op GetCompOp(string content, Op left, object right) { var filter = new OpAnd { - left.Filter, - right.Filter + left.Filter!, + right.Filter! }; return new Op(filter); } @@ -300,8 +308,8 @@ private static Op GetCompOp(string content, Op left, object right) { var filter = new OpOr { - left.Filter, - right.Filter + left.Filter!, + right.Filter! }; return new Op(filter); } @@ -314,7 +322,7 @@ private static Op GetCompOp(string content, Op left, object right) /// Op对象 public static Op operator ^(Op left, Op right) { - var filter = new OpXor(left.Filter, right.Filter); + var filter = new OpXor(left.Filter!, right.Filter!); return new Op(filter); } diff --git a/src/IFoxCAD.WPF/Converter.cs b/src/IFoxCAD.WPF/Converter.cs index f165ad6..828ece9 100644 --- a/src/IFoxCAD.WPF/Converter.cs +++ b/src/IFoxCAD.WPF/Converter.cs @@ -15,7 +15,7 @@ public class StringToIntConverter : IValueConverter /// 转换后的值。 如果该方法返回 null,则使用有效的 null 值。 public object Convert(object value, Type targetType, object parameter, CultureInfo culture) { - string a = value as string; + string? a = value as string; _ = int.TryParse(a, out int b); return b; } @@ -47,7 +47,7 @@ public class StringToDoubleConverter : IValueConverter /// 转换后的值。 如果该方法返回 null,则使用有效的 null 值。 public object Convert(object value, Type targetType, object parameter, CultureInfo culture) { - string a = value as string; + string? a = value as string; _ = double.TryParse(a, out double b); return b; } @@ -91,7 +91,7 @@ public object Convert(object value, Type targetType, object parameter, CultureIn /// 转换后的值。 如果该方法返回 null,则使用有效的 null 值。 public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture) { - string a = value as string; + string? a = value as string; _ = int.TryParse(a, out int b); return b; } diff --git a/src/IFoxCAD.WPF/DependencyObjectExtensions.cs b/src/IFoxCAD.WPF/DependencyObjectExtensions.cs index f919fbb..04e969e 100644 --- a/src/IFoxCAD.WPF/DependencyObjectExtensions.cs +++ b/src/IFoxCAD.WPF/DependencyObjectExtensions.cs @@ -10,7 +10,7 @@ public static class DependencyObjectExtensions /// /// 子对象 /// 依赖属性 - public static DependencyObject GetParentObject(this DependencyObject child) + public static DependencyObject? GetParentObject(this DependencyObject child) { if (child == null) return null; @@ -19,7 +19,7 @@ public static DependencyObject GetParentObject(this DependencyObject child) DependencyObject parent = ContentOperations.GetParent(contentElement); if (parent != null) return parent; - FrameworkContentElement fce = contentElement as FrameworkContentElement; + FrameworkContentElement? fce = contentElement as FrameworkContentElement; return fce?.Parent; } diff --git a/src/IFoxCAD.WPF/EnumSelection.cs b/src/IFoxCAD.WPF/EnumSelection.cs index 12960f9..d964351 100644 --- a/src/IFoxCAD.WPF/EnumSelection.cs +++ b/src/IFoxCAD.WPF/EnumSelection.cs @@ -63,7 +63,7 @@ public bool this[T key] } } - public event PropertyChangedEventHandler PropertyChanged; + public event PropertyChangedEventHandler? PropertyChanged; private void OnPropertyChanged([CallerMemberName] string propertyName = "") { diff --git a/src/IFoxCAD.WPF/EventBindingExtension.cs b/src/IFoxCAD.WPF/EventBindingExtension.cs index 4e5e623..33f0407 100644 --- a/src/IFoxCAD.WPF/EventBindingExtension.cs +++ b/src/IFoxCAD.WPF/EventBindingExtension.cs @@ -9,11 +9,11 @@ public class EventBindingExtension : MarkupExtension /// /// 命令属性 /// - public string Command { get; set; } + public string? Command { get; set; } /// /// 命令参数属性 /// - public string CommandParameter { get; set; } + public string? CommandParameter { get; set; } /// /// 当在派生类中实现时,返回用作此标记扩展的目标属性值的对象。 /// @@ -23,18 +23,18 @@ public class EventBindingExtension : MarkupExtension /// /// /// - public override object ProvideValue(IServiceProvider serviceProvider) + public override object? ProvideValue(IServiceProvider serviceProvider) { if (serviceProvider == null) { throw new ArgumentNullException(nameof(serviceProvider)); } - if (!(serviceProvider.GetService(typeof(IProvideValueTarget)) is IProvideValueTarget targetProvider)) + if (serviceProvider.GetService(typeof(IProvideValueTarget)) is not IProvideValueTarget targetProvider) { throw new InvalidOperationException(); } - if (!(targetProvider.TargetObject is FrameworkElement targetObject)) + if (targetProvider.TargetObject is not FrameworkElement targetObject) { throw new InvalidOperationException(); } @@ -58,22 +58,22 @@ public override object ProvideValue(IServiceProvider serviceProvider) } } - return CreateHandler(memberInfo, Command, targetObject.GetType()); + return CreateHandler(memberInfo, Command!, targetObject.GetType()); } - private Type GetEventHandlerType(MemberInfo memberInfo) + private Type? GetEventHandlerType(MemberInfo memberInfo) { - Type eventHandlerType = null; - if (memberInfo is EventInfo) + Type? eventHandlerType = null; + if (memberInfo is EventInfo eventInfo) { - var info = memberInfo as EventInfo; - var eventInfo = info; + //var info = memberInfo as EventInfo; + //var eventInfo = info; eventHandlerType = eventInfo.EventHandlerType; } - else if (memberInfo is MethodInfo) + else if (memberInfo is MethodInfo methodInfo) { - var info = memberInfo as MethodInfo; - var methodInfo = info; + //var info = memberInfo as MethodInfo; + //var methodInfo = info; ParameterInfo[] pars = methodInfo.GetParameters(); eventHandlerType = pars[1].ParameterType; } @@ -81,9 +81,11 @@ private Type GetEventHandlerType(MemberInfo memberInfo) return eventHandlerType; } - private object CreateHandler(MemberInfo memberInfo, string cmdName, Type targetType) +#pragma warning disable IDE0060 // 删除未使用的参数 + private object? CreateHandler(MemberInfo memberInfo, string cmdName, Type targetType) +#pragma warning restore IDE0060 // 删除未使用的参数 { - Type eventHandlerType = GetEventHandlerType(memberInfo); + Type? eventHandlerType = GetEventHandlerType(memberInfo); if (eventHandlerType == null) return null; @@ -115,7 +117,9 @@ private object CreateHandler(MemberInfo memberInfo, string cmdName, Type targetT static readonly MethodInfo getMethod = typeof(EventBindingExtension).GetMethod("HandlerIntern", new Type[] { typeof(object), typeof(object), typeof(string), typeof(string) }); +#pragma warning disable IDE0051 // 删除未使用的私有成员 static void Handler(object sender, object args) +#pragma warning restore IDE0051 // 删除未使用的私有成员 { HandlerIntern(sender, args, "cmd", null); } @@ -126,16 +130,15 @@ static void Handler(object sender, object args) /// The arguments. /// Name of the command. /// The command parameter. - public static void HandlerIntern(object sender, object args, string cmdName, string commandParameter) + public static void HandlerIntern(object sender, object args, string cmdName, string? commandParameter) { - var fe = sender as FrameworkElement; - if (fe != null) + if (sender is FrameworkElement fe) { - ICommand cmd = GetCommand(fe, cmdName); - object commandParam = null; + var cmd = GetCommand(fe, cmdName); + object? commandParam = null; if (!string.IsNullOrWhiteSpace(commandParameter)) { - commandParam = GetCommandParameter(fe, args, commandParameter); + commandParam = GetCommandParameter(fe, args, commandParameter!); } if ((cmd != null) && cmd.CanExecute(commandParam)) { @@ -144,7 +147,7 @@ public static void HandlerIntern(object sender, object args, string cmdName, str } } - internal static ICommand GetCommand(FrameworkElement target, string cmdName) + internal static ICommand? GetCommand(FrameworkElement target, string cmdName) { var vm = FindViewModel(target); if (vm == null) return null; @@ -159,30 +162,25 @@ internal static ICommand GetCommand(FrameworkElement target, string cmdName) throw new Exception("EventBinding path error: '" + cmdName + "' property not found on '" + vmType + "' 'DelegateCommand'"); #endif + +#pragma warning disable CS0162 // 检测到无法访问的代码 return null; +#pragma warning restore CS0162 // 检测到无法访问的代码 } internal static object GetCommandParameter(FrameworkElement target, object args, string commandParameter) { var classify = commandParameter.Split('.'); - object ret; - switch (classify[0]) + object ret = classify[0] switch { - case "$e": - ret = args; - break; - case "$this": - ret = classify.Length > 1 ? FollowPropertyPath(target, commandParameter.Replace("$this.", ""), target.GetType()) : target; - break; - default: - ret = commandParameter; - break; - } - + "$e" => args, + "$this" => classify.Length > 1 ? FollowPropertyPath(target, commandParameter.Replace("$this.", ""), target.GetType()) : target, + _ => commandParameter, + }; return ret; } - internal static ViewModelBase FindViewModel(FrameworkElement target) + internal static ViewModelBase? FindViewModel(FrameworkElement? target) { if (target == null) return null; @@ -193,7 +191,7 @@ internal static ViewModelBase FindViewModel(FrameworkElement target) return FindViewModel(parent); } - internal static object FollowPropertyPath(object target, string path, Type valueType = null) + internal static object FollowPropertyPath(object target, string path, Type? valueType = null) { if (target == null) throw new ArgumentNullException(nameof(target)); if (path == null) throw new ArgumentNullException(nameof(path)); diff --git a/src/IFoxCAD.WPF/IFoxCAD.WPF.csproj b/src/IFoxCAD.WPF/IFoxCAD.WPF.csproj index 51532d1..6b61141 100644 --- a/src/IFoxCAD.WPF/IFoxCAD.WPF.csproj +++ b/src/IFoxCAD.WPF/IFoxCAD.WPF.csproj @@ -2,6 +2,7 @@ net45 + enable true true true diff --git a/src/IFoxCAD.WPF/RelayCommand.cs b/src/IFoxCAD.WPF/RelayCommand.cs index 70b654f..628122a 100644 --- a/src/IFoxCAD.WPF/RelayCommand.cs +++ b/src/IFoxCAD.WPF/RelayCommand.cs @@ -8,7 +8,7 @@ namespace IFoxCAD.WPF; /// public class RelayCommand : ICommand { - readonly Func _canExecute; + readonly Func? _canExecute; readonly Action _execute; /// /// 初始化 类. @@ -24,7 +24,7 @@ public RelayCommand(Action execute) : this(execute, null) /// 执行函数委托 /// 是否可执行函数委托 /// execute - public RelayCommand(Action execute, Func canExecute) + public RelayCommand(Action execute, Func? canExecute) { _execute = execute ?? throw new ArgumentNullException(nameof(execute)); _canExecute = canExecute; @@ -60,7 +60,7 @@ public event EventHandler CanExecuteChanged [DebuggerStepThrough] public bool CanExecute(object parameter) { - return _canExecute == null ? true : _canExecute(parameter); + return _canExecute == null || _canExecute(parameter); } /// /// 定义在调用此命令时要调用的方法。 diff --git a/src/IFoxCAD.WPF/ViewModelBase.cs b/src/IFoxCAD.WPF/ViewModelBase.cs index c85b8b9..c992e81 100644 --- a/src/IFoxCAD.WPF/ViewModelBase.cs +++ b/src/IFoxCAD.WPF/ViewModelBase.cs @@ -9,7 +9,7 @@ public class ViewModelBase : INotifyPropertyChanged /// /// 属性值更改事件。 /// - public event PropertyChangedEventHandler PropertyChanged; + public event PropertyChangedEventHandler? PropertyChanged; /// /// 属性改变时调用 /// @@ -27,7 +27,7 @@ public void OnPropertyChanged([CallerMemberName] string propertyName = "") /// 属性值 /// 属性名 /// 成功返回 ,反之 - protected virtual bool Set(ref T storage, T value, [CallerMemberName] string propertyName = null) + protected virtual bool Set(ref T storage, T value, [CallerMemberName] string propertyName = "") { if (object.Equals(storage, value)) return false; diff --git a/tests/Test/Test.cs b/tests/Test/Test.cs index efaa113..b51ea14 100644 --- a/tests/Test/Test.cs +++ b/tests/Test/Test.cs @@ -30,6 +30,7 @@ public void Dbtest() //var linee = tr.GetObject(cirid); //经测试,类型不匹配,返回null //var dd = tr.GetObject(lienid); //List ds = new() { linee, dd }; + //tr.CurrentSpace.AddEntity(line,tr); } //add entity test diff --git a/tests/Test/testblock.cs b/tests/Test/testblock.cs index 1e9853a..09c0584 100644 --- a/tests/Test/testblock.cs +++ b/tests/Test/testblock.cs @@ -420,4 +420,16 @@ public void TestQuickBlockDef() } + public void TestWblock() + { + Database db = new Database(false,true); + var curdb = HostApplicationServices.WorkingDatabase; + var opts = new PromptSelectionOptions(); + opts.MessageForAdding = "选择对象"; + var ss = Env.Editor.GetSelection(opts).Value; + var ids = new ObjectIdCollection(ss.GetObjectIds()); + db = curdb.Wblock(ids, Point3d.Origin); + db.SaveAs(@"c:\test.dwg", DwgVersion.Current); + } + } \ No newline at end of file -- Gitee From f1f0c4b3c07f37ae558785e9eacd69bf87b3c3e5 Mon Sep 17 00:00:00 2001 From: vicwjb Date: Thu, 17 Feb 2022 22:26:44 +0800 Subject: [PATCH 037/675] =?UTF-8?q?=E4=BC=98=E5=8C=96=E6=89=93=E5=BC=80?= =?UTF-8?q?=E6=96=87=E4=BB=B6=E7=9A=84=E4=BA=8B=E5=8A=A1=E6=9E=84=E9=80=A0?= =?UTF-8?q?=E5=87=BD=E6=95=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/IFoxCAD.Cad/Runtime/DBTrans.cs | 44 ++++++++++++++++++++---------- 1 file changed, 29 insertions(+), 15 deletions(-) diff --git a/src/IFoxCAD.Cad/Runtime/DBTrans.cs b/src/IFoxCAD.Cad/Runtime/DBTrans.cs index d442b5e..f5f65c7 100644 --- a/src/IFoxCAD.Cad/Runtime/DBTrans.cs +++ b/src/IFoxCAD.Cad/Runtime/DBTrans.cs @@ -101,27 +101,41 @@ public DBTrans(Database database, bool commit = true) /// 事务是否提交 public DBTrans(string fileName, bool commit = true) { - Database = CreateDatabase(fileName); - Transaction = Database.TransactionManager.StartTransaction(); - _commit = commit; - dBTrans.Push(this); - } - - private static Database CreateDatabase(string fileName) - { - var db = new Database(false, true); - if (Path.GetExtension(fileName).ToLower().Contains("dxf")) + if (string.IsNullOrWhiteSpace(fileName)) + throw new ArgumentNullException(nameof(fileName)); + if (File.Exists(fileName)) { - db.DxfIn(fileName, null); + var doc = + Application.DocumentManager.Cast().FirstOrDefault(doc => doc.Name == fileName); + if (doc is not null) + { + Database = doc.Database; + Document = doc; + Editor = doc.Editor; + } + else + { + Database = new Database(false, true); + if (Path.GetExtension(fileName).ToLower().Contains("dxf")) + { + Database.DxfIn(fileName, null); + } + else + { + Database.ReadDwgFile(fileName, FileOpenMode.OpenForReadAndWriteNoShare, true, null); + } + Database.CloseInput(true); + } } else { - db.ReadDwgFile(fileName, FileShare.Read, true, null); + Database = new Database(true, false); } - db.CloseInput(true); - return db; + + Transaction = Database.TransactionManager.StartTransaction(); + _commit = commit; + dBTrans.Push(this); } - #endregion #region 类型转换 -- Gitee From 36a45546d562ca06a9cf11124d7b3f9c9d14d966 Mon Sep 17 00:00:00 2001 From: vicwjb Date: Thu, 17 Feb 2022 22:53:35 +0800 Subject: [PATCH 038/675] =?UTF-8?q?=E5=87=86=E5=A4=87=E5=8F=91=E5=B8=830.3?= =?UTF-8?q?.0=E7=89=88=E6=9C=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/IFoxCAD.Basal/IFoxCAD.Basal.csproj | 4 ++-- src/IFoxCAD.Cad/IFoxCAD.Cad.csproj | 4 ++-- src/IFoxCAD.WPF/IFoxCAD.WPF.csproj | 4 ++-- tests/Test/testid.cs | 2 +- 4 files changed, 7 insertions(+), 7 deletions(-) diff --git a/src/IFoxCAD.Basal/IFoxCAD.Basal.csproj b/src/IFoxCAD.Basal/IFoxCAD.Basal.csproj index fa009d6..3205fd9 100644 --- a/src/IFoxCAD.Basal/IFoxCAD.Basal.csproj +++ b/src/IFoxCAD.Basal/IFoxCAD.Basal.csproj @@ -4,7 +4,7 @@ net45 true enable - 0.2.0 + 0.3.0 InspireFunction xsfhlzh;vicwjb 基于.NET的二次开发基本类库 @@ -13,7 +13,7 @@ https://gitee.com/inspirefunction/ifoxcad.git git IFoxCAD;C#;NET;Common;Basal - 删除net35支持,升级至net45,增加c#10语法相关内容. + 删除LoopList类,改为利用LinkedList;开启可空类型. true true preview diff --git a/src/IFoxCAD.Cad/IFoxCAD.Cad.csproj b/src/IFoxCAD.Cad/IFoxCAD.Cad.csproj index 9e83c0c..d489c32 100644 --- a/src/IFoxCAD.Cad/IFoxCAD.Cad.csproj +++ b/src/IFoxCAD.Cad/IFoxCAD.Cad.csproj @@ -4,7 +4,7 @@ net45 true enable - 0.2.0.6 + 0.3.0 InspireFunction xsfhlzh;vicwjb 基于.NET的Cad二次开发类库 @@ -13,7 +13,7 @@ https://gitee.com/inspirefunction/ifoxcad.git git IFoxCAD;CAD;AutoCad;C#;NET - 修复符号表类错误. + 开启可空类型;优化事务构造函数. true true preview diff --git a/src/IFoxCAD.WPF/IFoxCAD.WPF.csproj b/src/IFoxCAD.WPF/IFoxCAD.WPF.csproj index 6b61141..8c47b25 100644 --- a/src/IFoxCAD.WPF/IFoxCAD.WPF.csproj +++ b/src/IFoxCAD.WPF/IFoxCAD.WPF.csproj @@ -7,7 +7,7 @@ true true true - 0.2.1 + 0.3.0 xsfhlzh;vicwjb InspireFunction WPF的简单MVVM模式开发类库 @@ -17,7 +17,7 @@ https://gitee.com/inspirefunction/ifoxcad.git IFoxCAD;C#;NET;WPF;MVVM git - 增加 EnumSelection. + 开启可空类型. preview diff --git a/tests/Test/testid.cs b/tests/Test/testid.cs index 7d35984..911fd53 100644 --- a/tests/Test/testid.cs +++ b/tests/Test/testid.cs @@ -55,7 +55,7 @@ public void TestId() [CommandMethod("testmycommand")] public void TestMyCommand() { - using (var dbtrans = new DBTrans(true,false)) { + using (var dbtrans = new DBTrans(Env.Document,true,false)) { using (var trans = Env.Database.TransactionManager.StartTransaction()) { var l1 = new Line(new Point3d(0, 0, 0), new Point3d(100, 100, 0)); -- Gitee From 0d5e5d6c88ba4989c3bdd82b8d7241dbaccd9d8a Mon Sep 17 00:00:00 2001 From: vicwjb Date: Fri, 18 Feb 2022 23:23:18 +0800 Subject: [PATCH 039/675] =?UTF-8?q?=E5=8F=96=E6=B6=88=E4=BB=A3=E7=A0=81?= =?UTF-8?q?=E9=87=8C=E7=9A=84regen=E5=87=BD=E6=95=B0=EF=BC=8C=E4=BC=9A?= =?UTF-8?q?=E5=8D=A1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/IFoxCAD.Cad/ExtensionMethod/ObjectIdEx.cs | 2 +- src/IFoxCAD.Cad/IFoxCAD.Cad.csproj | 2 +- src/IFoxCAD.Cad/Runtime/Env.cs | 2 -- tests/Test/Test.cs | 18 +++++++++++++++++- 4 files changed, 19 insertions(+), 5 deletions(-) diff --git a/src/IFoxCAD.Cad/ExtensionMethod/ObjectIdEx.cs b/src/IFoxCAD.Cad/ExtensionMethod/ObjectIdEx.cs index 8408a48..802acf0 100644 --- a/src/IFoxCAD.Cad/ExtensionMethod/ObjectIdEx.cs +++ b/src/IFoxCAD.Cad/ExtensionMethod/ObjectIdEx.cs @@ -73,7 +73,7 @@ public static void Erase(this ObjectId id) { ent.Erase(); }// 第一种读写权限自动转换写法 - Env.Editor.Regen(); + //Env.Editor.Regen(); } } } diff --git a/src/IFoxCAD.Cad/IFoxCAD.Cad.csproj b/src/IFoxCAD.Cad/IFoxCAD.Cad.csproj index d489c32..932577a 100644 --- a/src/IFoxCAD.Cad/IFoxCAD.Cad.csproj +++ b/src/IFoxCAD.Cad/IFoxCAD.Cad.csproj @@ -4,7 +4,7 @@ net45 true enable - 0.3.0 + 0.3.0.1 InspireFunction xsfhlzh;vicwjb 基于.NET的Cad二次开发类库 diff --git a/src/IFoxCAD.Cad/Runtime/Env.cs b/src/IFoxCAD.Cad/Runtime/Env.cs index cf26763..4b5beda 100644 --- a/src/IFoxCAD.Cad/Runtime/Env.cs +++ b/src/IFoxCAD.Cad/Runtime/Env.cs @@ -391,7 +391,6 @@ public static void SetVar(string varName, object value) { Application.SetSystemVariable(varName, value); } -#nullable enable /// /// 获取系统环境变量 /// @@ -412,7 +411,6 @@ public static void SetEnv(string var, string? value) //创建、修改或删除当前进程中或者为当前用户或本地计算机保留的 Windows 操作系统注册表项中存储的环境变量 Environment.SetEnvironmentVariable(var, value); } -#nullable disable #endregion diff --git a/tests/Test/Test.cs b/tests/Test/Test.cs index b51ea14..ea9f8e6 100644 --- a/tests/Test/Test.cs +++ b/tests/Test/Test.cs @@ -20,7 +20,7 @@ public void Dbtest() { tr.Editor.WriteMessage("\ndatabase 正常"); } - + Line line = new(new Point3d(0, 0, 0), new Point3d(1, 1, 0)); Circle circle = new(new Point3d(0, 0, 0), Vector3d.ZAxis, 2); //var lienid = tr.AddEntity(line); @@ -309,7 +309,23 @@ public void TestPt() } + [CommandMethod("TestBack")] + public void TestBack() + { + using var tr = new DBTrans(@"C:\Users\vic\Desktop\test.dwg"); + tr.ModelSpace.GetEntities().ForEach(ent => + { + ent.ForWrite(e => e.ColorIndex = 3); + }); + tr.Database.SaveAs(@"C:\Users\vic\Desktop\test.dwg", DwgVersion.Current); + tr.ModelSpace.GetEntities().ForEach(ent => + { + ent.ForWrite(e => e.ColorIndex = 4); + }); + tr.Database.SaveAs(@"C:\Users\vic\Desktop\test.dwg", DwgVersion.Current); + + } public Database Getdb() { -- Gitee From 2f69454dbc61de581b12bc0fa818a34ef57f3912 Mon Sep 17 00:00:00 2001 From: vicwjb Date: Thu, 24 Feb 2022 20:36:22 +0800 Subject: [PATCH 040/675] =?UTF-8?q?=E6=81=A2=E5=A4=8DLoopList=E7=B1=BB?= =?UTF-8?q?=EF=BC=8C=E5=A2=9E=E5=8A=A0net35=EF=BC=8Cnet40=E7=89=88?= =?UTF-8?q?=E6=9C=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/IFoxCAD.Basal/IFoxCAD.Basal.csproj | 34 +- src/IFoxCAD.Basal/LinkedListEx.cs | 59 - src/IFoxCAD.Basal/LoopList.cs | 1017 +++++++++-------- src/IFoxCAD.Cad/ExtensionMethod/CurveEx.cs | 92 +- src/IFoxCAD.Cad/ExtensionMethod/EditorEx.cs | 79 +- src/IFoxCAD.Cad/ExtensionMethod/EntityEx.cs | 42 +- src/IFoxCAD.Cad/ExtensionMethod/GeometryEx.cs | 56 +- .../ExtensionMethod/SymbolTableRecordEx.cs | 16 +- src/IFoxCAD.Cad/IFoxCAD.Cad.csproj | 47 +- src/IFoxCAD.Cad/Runtime/AcadVersion.cs | 7 +- src/IFoxCAD.Cad/Runtime/AutoRegAssem.cs | 5 +- src/IFoxCAD.Cad/Runtime/CadVersion.cs | 55 + src/IFoxCAD.Cad/Runtime/DBTrans.cs | 37 +- tests/Test/TestCurve.cs | 25 + tests/Test/testblock.cs | 18 + 15 files changed, 891 insertions(+), 698 deletions(-) delete mode 100644 src/IFoxCAD.Basal/LinkedListEx.cs create mode 100644 tests/Test/TestCurve.cs diff --git a/src/IFoxCAD.Basal/IFoxCAD.Basal.csproj b/src/IFoxCAD.Basal/IFoxCAD.Basal.csproj index 3205fd9..567d1d3 100644 --- a/src/IFoxCAD.Basal/IFoxCAD.Basal.csproj +++ b/src/IFoxCAD.Basal/IFoxCAD.Basal.csproj @@ -1,10 +1,10 @@ - net45 + net35;net40;net45 true enable - 0.3.0 + 0.3.1 InspireFunction xsfhlzh;vicwjb 基于.NET的二次开发基本类库 @@ -13,8 +13,8 @@ https://gitee.com/inspirefunction/ifoxcad.git git IFoxCAD;C#;NET;Common;Basal - 删除LoopList类,改为利用LinkedList;开启可空类型. - true + 恢复LoopList类. + true preview true @@ -28,10 +28,28 @@ + + + + + - - - - + + + + + + + DEBUG + + + $(Configuration);ac2009 + + + $(Configuration);ac2013 + + + $(Configuration);ac2015 + diff --git a/src/IFoxCAD.Basal/LinkedListEx.cs b/src/IFoxCAD.Basal/LinkedListEx.cs deleted file mode 100644 index 6481d85..0000000 --- a/src/IFoxCAD.Basal/LinkedListEx.cs +++ /dev/null @@ -1,59 +0,0 @@ -namespace IFoxCAD.Basal; - -public static class LinkedListEx -{ - /// - /// 获取节点 - /// - /// 节点数据比较委托 - /// 节点 - public static LinkedListNode? GetNode(this LinkedList linkedList, Func func) - { - var node = linkedList.First; - if (node != null) - { - for (int i = 0; i < linkedList.Count; i++) - { - if (func(node.Value)) - { - return node; - } - node = node.Next; - } - return null; - } - - return null; - } - - public static LinkedListNode Add(this LinkedList linkedList, T value) - { - return linkedList.AddLast(value); - } - - /// - /// 获取节点的查询器 - /// - /// - public static IEnumerable> GetNodes(this LinkedList linkedList) - { - var node = linkedList.First; - for (int i = 0; i < linkedList.Count; i++) - { - yield return node; - node = node.Next; - } - } - - - /// - /// 获取当前节点的临近节点 - /// - /// 搜索方向标志,为向前搜索,为向后搜索 - /// 节点 - public static LinkedListNode GetNext(this LinkedListNode node, bool forward) - { - return forward ? node.Next : node.Previous; - } -} - diff --git a/src/IFoxCAD.Basal/LoopList.cs b/src/IFoxCAD.Basal/LoopList.cs index 8e2cfb4..ee6edc5 100644 --- a/src/IFoxCAD.Basal/LoopList.cs +++ b/src/IFoxCAD.Basal/LoopList.cs @@ -1,507 +1,510 @@ -//namespace IFoxCAD.Basal -//{ -// /// -// /// 环链表节点 -// /// -// /// -// public class LoopListNode -// { -// /// -// /// 取值 -// /// -// public T Value { get; set; } - -// /// -// /// 上一个节点 -// /// -// public LoopListNode Previous -// { internal set; get; } - -// /// -// /// 下一个节点 -// /// -// public LoopListNode Next -// { internal set; get; } - -// /// -// ///环链表序列 -// /// -// public LoopList List -// { internal set; get; } -// /// -// /// 环链表节点构造函数 -// /// -// /// 节点值 -// public LoopListNode(T value) -// { -// Value = value; -// } -// /// -// /// 获取当前节点的临近节点 -// /// -// /// 搜索方向标志,为向前搜索,为向后搜索 -// /// -// public LoopListNode GetNext(bool forward) -// { -// return forward ? Next : Previous; -// } -// } - -// /// -// /// 环链表 -// /// -// /// -// public class LoopList : -// IEnumerable, IFormattable -// { -// /// -// /// 默认构造函数 -// /// -// public LoopList() -// { } -// /// -// /// 环链表构造函数 -// /// -// /// 节点迭代器 -// public LoopList(IEnumerable values) -// { -// foreach (T value in values) -// Add(value); -// } - -// /// -// /// 节点数 -// /// -// public int Count -// { get; private set; } - -// /// -// /// 首节点 -// /// -// public LoopListNode? First -// { get; private set; } - -// /// -// /// 尾节点 -// /// -// public LoopListNode? Last -// { -// get { return First?.Previous; } -// } -// /// -// /// 设置首节点 -// /// -// /// 节点 -// /// -// public bool SetFirst(LoopListNode node) -// { -// if (Contains(node)) -// { -// First = node; -// return true; -// } -// return false; -// } - -// /// -// /// 交换两个节点的值 -// /// -// /// 第一个节点 -// /// 第二个节点 -// public void Swap(LoopListNode node1, LoopListNode node2) -// { -// (node2.Value, node1.Value) = (node1.Value, node2.Value); -// } - -// #region Contains -// /// -// /// 是否包含节点 -// /// -// /// -// /// -// public bool Contains(LoopListNode node) -// { -// return node != null && node.List == this; -// } -// /// -// /// 是否包含值 -// /// -// /// -// /// -// public bool Contains(T value) -// { -// var node = First; -// if (node == null) -// return false; - -// for (int i = 0; i < Count; i++) -// { -// if (node.Value!.Equals(value)) -// return true; -// node = node.Next; -// } - -// return false; -// } -// /// -// /// 获取节点 -// /// -// /// -// /// -// public LoopListNode GetNode(Func func) -// { -// LoopListNode node = First; -// if (node == null) -// return null; - -// for (int i = 0; i < Count; i++) -// { -// if (func(node.Value)) -// { -// return node; -// } -// node = node.Next; -// } -// return null; -// } - -// #endregion Contains - -// #region Add - -// /// -// /// 在首节点之前插入节点,并设置新节点为首节点 -// /// -// /// -// /// -// public LoopListNode AddFirst(T value) -// { -// LoopListNode node = new(value) -// { -// List = this -// }; -// if (Count == 0) -// { -// First = node; -// First.Previous = First.Next = node; -// } -// else -// { -// LoopListNode last = Last; -// First.Previous = last.Next = node; -// node.Next = First; -// node.Previous = last; -// First = node; -// } -// Count++; -// return First; -// } - -// /// -// /// 在尾节点之后插入节点,并设置新节点为尾节点 -// /// -// /// -// /// -// public LoopListNode Add(T value) -// { -// LoopListNode node = new(value); -// node.List = this; -// if (Count == 0) -// { -// First = node; -// First.Previous = First.Next = node; -// } -// else -// { -// LoopListNode last = First.Previous; -// First.Previous = last.Next = node; -// node.Next = First; -// node.Previous = last; -// } -// Count++; -// return Last; -// } -// /// -// /// 前面增加节点 -// /// -// /// -// /// -// /// -// public LoopListNode AddBefore(LoopListNode node, T value) -// { -// if (node == First) -// { -// return AddFirst(value); -// } -// else -// { -// LoopListNode tnode = new(value); -// node.Previous.Next = tnode; -// tnode.Previous = node.Previous; -// node.Previous = tnode; -// tnode.Next = node; -// Count++; -// return tnode; -// } -// } -// /// -// /// 后面增加节点 -// /// -// /// -// /// -// /// -// public LoopListNode AddAfter(LoopListNode node, T value) -// { -// LoopListNode tnode = new(value); -// node.Next.Previous = tnode; -// tnode.Next = node.Next; -// node.Next = tnode; -// tnode.Previous = node; -// Count++; -// return tnode; -// } - -// #endregion Add - -// #region Remove - -// /// -// /// 删除首节点 -// /// -// /// -// public bool RemoveFirst() -// { -// switch (Count) -// { -// case 0: -// return false; - -// case 1: -// First = null; -// break; - -// default: -// LoopListNode last = Last; -// First = First.Next; -// First.Previous = last; -// last.Next = First; -// break; -// } -// Count--; -// return true; -// } - -// /// -// /// 删除尾节点 -// /// -// /// -// public bool RemoveLast() -// { -// switch (Count) -// { -// case 0: -// return false; - -// case 1: -// First = null; -// break; - -// default: -// LoopListNode last = Last.Previous; -// last.Next = First; -// First.Previous = last; -// break; -// } -// Count--; -// return true; -// } - -// /// -// /// 删除节点 -// /// -// /// -// /// -// public bool Remove(LoopListNode node) -// { -// if (Contains(node)) -// { -// if (Count == 1) -// { -// First = null; -// } -// else -// { -// if (node == First) -// { -// RemoveFirst(); -// } -// else -// { -// node.Next.Previous = node.Previous; -// node.Previous.Next = node.Next; -// } -// } -// Count--; -// return true; -// } -// return false; -// } - -// #endregion Remove - -// #region LinkTo - -// /// -// /// 链接两节点,并去除这两个节点间的所有节点 -// /// -// /// -// /// -// public void LinkTo(LoopListNode from, LoopListNode to) -// { -// if (from != to && Contains(from) && Contains(to)) -// { -// LoopListNode node = from.Next; -// bool isFirstChanged = false; -// int number = 0; - -// while (node != to) -// { -// if (node == First) -// isFirstChanged = true; - -// node = node.Next; -// number++; -// } - -// from.Next = to; -// to.Previous = from; - -// if (number > 0 && isFirstChanged) -// First = to; - -// Count -= number; -// } -// } - -// /// -// /// 链接两节点,并去除这两个节点间的所有节点 -// /// -// /// -// /// -// /// -// public void LinkTo(LoopListNode from, LoopListNode to, int number) -// { -// if (from != to && Contains(from) && Contains(to)) -// { -// from.Next = to; -// to.Previous = from; -// First = to; -// Count -= number; -// } -// } - -// /// -// /// 链接两节点,并去除这两个节点间的所有节点 -// /// -// /// -// /// -// /// -// /// -// public void LinkTo(LoopListNode from, LoopListNode to, int number, bool isFirstChanged) -// { -// if (from != to && Contains(from) && Contains(to)) -// { -// from.Next = to; -// to.Previous = from; -// if (isFirstChanged) -// First = to; -// Count -= number; -// } -// } - -// #endregion LinkTo - -// #region IEnumerable 成员 - -// /// -// /// 获取节点的查询器 -// /// -// /// -// /// -// public IEnumerable> GetNodes(LoopListNode from) -// { -// LoopListNode node = from; -// for (int i = 0; i < Count; i++) -// { -// yield return node; -// node = node.Next; -// } -// } - -// /// -// /// 获取节点的查询器 -// /// -// /// -// public IEnumerable> GetNodes() -// { -// LoopListNode node = First; -// for (int i = 0; i < Count; i++) -// { -// yield return node; -// node = node.Next; -// } -// } - -// /// -// /// 获取节点值的查询器 -// /// -// /// -// public IEnumerator GetEnumerator() -// { -// LoopListNode node = First; -// for (int i = 0; i < Count; i++) -// { -// yield return node.Value; -// node = node.Next; -// } -// } - -// IEnumerator IEnumerable.GetEnumerator() -// { -// return GetEnumerator(); -// } - -// #region IEnumerable 成员 - -// System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() -// { -// return GetEnumerator(); -// } - -// #endregion IEnumerable 成员 - -// #endregion IEnumerable 成员 - -// #region IFormattable 成员 -// /// -// /// 转换为字符串 -// /// -// /// -// public override string ToString() -// { -// string s = "( "; -// foreach (T value in this) -// { -// s += $"{value} "; -// } -// return s + ")"; -// } - -// string IFormattable.ToString(string format, IFormatProvider formatProvider) -// { -// return ToString(); -// } - -// #endregion IFormattable 成员 -// } -//} \ No newline at end of file +namespace IFoxCAD.Basal +{ + /// + /// 环链表节点 + /// + /// + public class LoopListNode + { + /// + /// 取值 + /// + public T Value { get; set; } + + /// + /// 上一个节点 + /// + public LoopListNode? Previous + { internal set; get; } + + /// + /// 下一个节点 + /// + public LoopListNode? Next + { internal set; get; } + + /// + ///环链表序列 + /// + public LoopList? List + { internal set; get; } + /// + /// 环链表节点构造函数 + /// + /// 节点值 + public LoopListNode(T value) + { + Value = value; + } + /// + /// 获取当前节点的临近节点 + /// + /// 搜索方向标志,为向前搜索,为向后搜索 + /// + public LoopListNode? GetNext(bool forward) + { + return forward ? Next : Previous; + } + } + + /// + /// 环链表 + /// + /// + public class LoopList : IEnumerable, IFormattable + { + /// + /// 默认构造函数 + /// + public LoopList() + { } + /// + /// 环链表构造函数 + /// + /// 节点迭代器 + public LoopList(IEnumerable values) + { + foreach (T value in values) + Add(value); + } + + /// + /// 节点数 + /// + public int Count + { get; private set; } + + /// + /// 首节点 + /// + public LoopListNode? First + { get; private set; } + + /// + /// 尾节点 + /// + public LoopListNode? Last + { + get { return First?.Previous; } + } + /// + /// 设置首节点 + /// + /// 节点 + /// + public bool SetFirst(LoopListNode node) + { + if (Contains(node)) + { + First = node; + return true; + } + return false; + } + + /// + /// 交换两个节点的值 + /// + /// 第一个节点 + /// 第二个节点 + public void Swap(LoopListNode node1, LoopListNode node2) + { +#if ac2009 +#pragma warning disable IDE0180 // 使用元组交换值 + T value = node1.Value; +#pragma warning restore IDE0180 // 使用元组交换值 + node1.Value = node2.Value; + node2.Value = value; +#else + (node2.Value, node1.Value) = (node1.Value, node2.Value); +#endif + } + +#region Contains + /// + /// 是否包含节点 + /// + /// + /// + public bool Contains(LoopListNode node) + { + return node != null && node.List == this; + } + /// + /// 是否包含值 + /// + /// + /// + public bool Contains(T value) + { + + var node = First; + if (node != null) + { + for (int i = 0; i < Count; i++) + { + if (node!.Value!.Equals(value)) + return true; + node = node.Next; + } + } + return false; + } + /// + /// 获取节点 + /// + /// + /// + public LoopListNode? GetNode(Func func) + { + var node = First; + if (node != null) + { + for (int i = 0; i < Count; i++) + { + if (func(node!.Value)) + { + return node; + } + node = node.Next; + } + } + return null; + } + +#endregion Contains + +#region Add + + /// + /// 在首节点之前插入节点,并设置新节点为首节点 + /// + /// + /// + public LoopListNode AddFirst(T value) + { + var node = new LoopListNode(value) + { + List = this + }; + if (Count == 0) + { + First = node; + First.Previous = First.Next = node; + } + else + { + LoopListNode last = Last!; + First!.Previous = last.Next = node; + node.Next = First; + node.Previous = last; + First = node; + } + Count++; + return First; + } + + /// + /// 在尾节点之后插入节点,并设置新节点为尾节点 + /// + /// + /// + public LoopListNode Add(T value) + { + var node = new LoopListNode(value) + { + List = this + }; + if (Count == 0) + { + First = node; + First.Previous = First.Next = node; + } + else + { + var last = First!.Previous!; + First.Previous = last.Next = node; + node.Next = First; + node.Previous = last; + } + Count++; + return Last!; + } + /// + /// 前面增加节点 + /// + /// + /// + /// + public LoopListNode AddBefore(LoopListNode node, T value) + { + if (node == First) + { + return AddFirst(value); + } + else + { + var tnode = new LoopListNode(value); + node.Previous!.Next = tnode; + tnode.Previous = node.Previous; + node.Previous = tnode; + tnode.Next = node; + Count++; + return tnode; + } + } + /// + /// 后面增加节点 + /// + /// + /// + /// + public LoopListNode AddAfter(LoopListNode node, T value) + { + LoopListNode tnode = new(value); + node.Next!.Previous = tnode; + tnode.Next = node.Next; + node.Next = tnode; + tnode.Previous = node; + Count++; + return tnode; + } + +#endregion Add + +#region Remove + + /// + /// 删除首节点 + /// + /// + public bool RemoveFirst() + { + switch (Count) + { + case 0: + return false; + + case 1: + First = null; + break; + + default: + LoopListNode last = Last!; + First = First!.Next; + First!.Previous = last; + last.Next = First; + break; + } + Count--; + return true; + } + + /// + /// 删除尾节点 + /// + /// + public bool RemoveLast() + { + switch (Count) + { + case 0: + return false; + + case 1: + First = null; + break; + + default: + LoopListNode last = Last!.Previous!; + last.Next = First; + First!.Previous = last; + break; + } + Count--; + return true; + } + + /// + /// 删除节点 + /// + /// + /// + public bool Remove(LoopListNode node) + { + if (Contains(node)) + { + if (Count == 1) + { + First = null; + } + else + { + if (node == First) + { + RemoveFirst(); + } + else + { + node.Next!.Previous = node.Previous; + node.Previous!.Next = node.Next; + } + } + Count--; + return true; + } + return false; + } + +#endregion Remove + +#region LinkTo + + /// + /// 链接两节点,并去除这两个节点间的所有节点 + /// + /// + /// + public void LinkTo(LoopListNode from, LoopListNode to) + { + if (from != to && Contains(from) && Contains(to)) + { + LoopListNode node = from.Next!; + bool isFirstChanged = false; + int number = 0; + + while (node != to) + { + if (node == First) + isFirstChanged = true; + + node = node.Next!; + number++; + } + + from.Next = to; + to.Previous = from; + + if (number > 0 && isFirstChanged) + First = to; + + Count -= number; + } + } + + /// + /// 链接两节点,并去除这两个节点间的所有节点 + /// + /// + /// + /// + public void LinkTo(LoopListNode from, LoopListNode to, int number) + { + if (from != to && Contains(from) && Contains(to)) + { + from.Next = to; + to.Previous = from; + First = to; + Count -= number; + } + } + + /// + /// 链接两节点,并去除这两个节点间的所有节点 + /// + /// + /// + /// + /// + public void LinkTo(LoopListNode from, LoopListNode to, int number, bool isFirstChanged) + { + if (from != to && Contains(from) && Contains(to)) + { + from.Next = to; + to.Previous = from; + if (isFirstChanged) + First = to; + Count -= number; + } + } + +#endregion LinkTo + +#region IEnumerable 成员 + + /// + /// 获取节点的查询器 + /// + /// + /// + public IEnumerable> GetNodes(LoopListNode from) + { + var node = from; + for (int i = 0; i < Count; i++) + { + yield return node!; + node = node!.Next; + } + } + + /// + /// 获取节点的查询器 + /// + /// + public IEnumerable> GetNodes() + { + LoopListNode node = First!; + for (int i = 0; i < Count; i++) + { + yield return node!; + node = node.Next!; + } + } + + /// + /// 获取节点值的查询器 + /// + /// + public IEnumerator GetEnumerator() + { + LoopListNode node = First!; + for (int i = 0; i < Count; i++) + { + yield return node!.Value; + node = node.Next!; + } + } + + IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); + +#region IEnumerable 成员 + + IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); + +#endregion IEnumerable 成员 + +#endregion IEnumerable 成员 + +#region IFormattable 成员 + /// + /// 转换为字符串 + /// + /// + public override string ToString() + { + string s = "( "; + foreach (T value in this) + { + s += $"{value} "; + } + return s + ")"; + } + + string IFormattable.ToString(string format, IFormatProvider formatProvider) + { + return ToString(); + } + +#endregion IFormattable 成员 + } +} \ No newline at end of file diff --git a/src/IFoxCAD.Cad/ExtensionMethod/CurveEx.cs b/src/IFoxCAD.Cad/ExtensionMethod/CurveEx.cs index 99c8b02..0d04a2f 100644 --- a/src/IFoxCAD.Cad/ExtensionMethod/CurveEx.cs +++ b/src/IFoxCAD.Cad/ExtensionMethod/CurveEx.cs @@ -79,11 +79,11 @@ public bool Equals(EdgeItem other) Forward == other.Forward; } - public void FindRegion(List edges, List> regions) + public void FindRegion(List edges, List> regions) { - var region = new LinkedList(); + var region = new LoopList(); var edgeItem = this; - region.AddLast(edgeItem); + region.Add(edgeItem); var edgeItem2 = this.GetNext(edges); if (edgeItem2.Edge != null) { @@ -91,9 +91,10 @@ public void FindRegion(List edges, List> regions) foreach (var edgeList2 in regions) { var node = edgeList2.GetNode(e => e.Equals(edgeItem)); - if (node != null) + //var node = edgeList2.Find(edgeItem); + if (node != null && node != edgeList2.Last) { - if (node.Next.Value.Equals(edgeItem2)) + if (node.Next!.Value.Equals(edgeItem2)) { hasList = true; break; @@ -106,7 +107,7 @@ public void FindRegion(List edges, List> regions) { if (edgeItem2.Edge == edgeItem.Edge) break; - region.AddLast(edgeItem2); + region.Add(edgeItem2); //上一条语句判断失误,导致不停的将相同的值加入region,不能退出循环 edgeItem2 = edgeItem2.GetNext(edges); } if (edgeItem2.Edge == edgeItem.Edge) @@ -222,12 +223,16 @@ public bool IsNext(Edge edge, int index, ref Vector3d vec, ref bool forward) return false; } } - + /// + /// 获取曲线集所围成的封闭区域的曲线集 + /// + /// 曲线集 + /// 曲线集 public static List Topo(List curves) { //首先按交点分解为Ge曲线集 - List geCurves = new(); - List> paramss = new(); + var geCurves = new List(); + var paramss = new List>(); foreach (var curve in curves) { @@ -239,24 +244,24 @@ public static List Topo(List curves) } } - List edges = new(); - CurveCurveIntersector3d cci3d = new(); - List newCurves = new(); + var edges = new List(); + var cci3d = new CurveCurveIntersector3d(); + var newCurves = new List(); for (int i = 0; i < curves.Count; i++) { - CompositeCurve3d gc1 = geCurves[i]; - List pars1 = paramss[i]; + var gc1 = geCurves[i]; + var pars1 = paramss[i]; for (int j = i; j < curves.Count; j++) { - CompositeCurve3d gc2 = geCurves[j]; - List pars2 = paramss[j]; + var gc2 = geCurves[j]; + var pars2 = paramss[j]; cci3d.Set(gc1, gc2, Vector3d.ZAxis); for (int k = 0; k < cci3d.NumberOfIntersectionPoints; k++) { - double[] pars = cci3d.GetIntersectionParameters(k); + var pars = cci3d.GetIntersectionParameters(k); pars1.Add(pars[0]); pars2.Add(pars[1]); } @@ -264,11 +269,10 @@ public static List Topo(List curves) if (pars1.Count > 0) { - List c3ds = gc1.GetSplitCurves(pars1); + var c3ds = gc1.GetSplitCurves(pars1); if (c3ds.Count > 0) { - edges.AddRange( - c3ds.Select(c => new Edge { Curve = c })); + edges.AddRange(c3ds.Select(c => new Edge { Curve = c })); } else if (gc1.IsClosed()) { @@ -360,11 +364,11 @@ public static List Topo(List curves) } } - var regions = new List>(); + var regions = new List>(); foreach (var edge in edges) - { + { // 这里有bug,两个内接的矩形会卡死 var edgeItem = new EdgeItem(edge, true); - edgeItem.FindRegion(edges, regions); + edgeItem.FindRegion(edges, regions); // 经测试是这里卡住了 edgeItem = new EdgeItem(edge, false); edgeItem.FindRegion(edges, regions); } @@ -377,17 +381,19 @@ public static List Topo(List curves) if (regions[i].Count == regions[j].Count) { var node = regions[i].First; - var curve = node.Value.Edge.Curve; + var curve = node!.Value.Edge.Curve; var node2 = regions[j].GetNode(e => e.Edge.Curve == curve); - if (eq = node2 != null) + //var node2 = regions[j].Find(node.Value); + if (node2 != null) { + eq = true; var b = node.Value.Forward; - var b2 = node2!.Value.Forward; + var b2 = node2.Value.Forward; for (int k = 1; k < regions[i].Count; k++) { node = node.GetNext(b); node2 = node2.GetNext(b2); - if (node.Value.Edge.Curve != node2.Value.Edge.Curve) + if (node!.Value.Edge.Curve != node2!.Value.Edge.Curve) { eq = false; break; @@ -408,7 +414,7 @@ public static List Topo(List curves) region .Select(e => e.GetCurve()) .ToArray(); - newCurves.Add(new CompositeCurve3d(cs3ds.ToArray()).ToCurve()!); + newCurves.Add(new CompositeCurve3d(cs3ds).ToCurve()!); } return newCurves; @@ -419,10 +425,10 @@ public static List Topo(List curves) /// /// 曲线列表 /// 打断后的曲线列表 - public static List BreakCurve(List curves) + public static List BreakCurve(this List curves) { - List geCurves = new(); - List> paramss = new(); + var geCurves = new List(); // 存储曲线转换后的复合曲线 + var paramss = new List>(); // 存储每个曲线的交点参数值 foreach (var curve in curves) { @@ -434,32 +440,32 @@ public static List BreakCurve(List curves) } } - List oldCurves = new(); - List newCurves = new(); - CurveCurveIntersector3d cci3d = new(); + //var oldCurves = new List(); + var newCurves = new List(); + var cci3d = new CurveCurveIntersector3d(); for (int i = 0; i < curves.Count; i++) { - CompositeCurve3d gc1 = geCurves[i]; - List pars1 = paramss[i]; + var gc1 = geCurves[i]; + var pars1 = paramss[i]; //引用 for (int j = i; j < curves.Count; j++) { - CompositeCurve3d gc2 = geCurves[j]; - List pars2 = paramss[j]; + var gc2 = geCurves[j]; + var pars2 = paramss[j]; // 引用 cci3d.Set(gc1, gc2, Vector3d.ZAxis); for (int k = 0; k < cci3d.NumberOfIntersectionPoints; k++) { - double[] pars = cci3d.GetIntersectionParameters(k); - pars1.Add(pars[0]); - pars2.Add(pars[1]); + var pars = cci3d.GetIntersectionParameters(k); + pars1.Add(pars[0]); // 引用修改会同步到源对象 + pars2.Add(pars[1]); // 引用修改会同步到源对象 } } if (pars1.Count > 0) { - List c3ds = gc1.GetSplitCurves(pars1); + var c3ds = gc1.GetSplitCurves(pars1); if (c3ds.Count > 1) { foreach (CompositeCurve3d c3d in c3ds) @@ -471,7 +477,7 @@ public static List BreakCurve(List curves) newCurves.Add(c); } } - oldCurves.Add(curves[i]); + //oldCurves.Add(curves[i]); } } } diff --git a/src/IFoxCAD.Cad/ExtensionMethod/EditorEx.cs b/src/IFoxCAD.Cad/ExtensionMethod/EditorEx.cs index 7ba92f8..bfcfa0f 100644 --- a/src/IFoxCAD.Cad/ExtensionMethod/EditorEx.cs +++ b/src/IFoxCAD.Cad/ExtensionMethod/EditorEx.cs @@ -599,6 +599,70 @@ public static Matrix3d GetMatrixFromPDcsToMDcs(this Editor editor) /// 变换矩阵 public static Matrix3d GetMatrix(this Editor editor, CoordinateSystemCode from, CoordinateSystemCode to) { +#if ac2009 + switch (from) + { + case CoordinateSystemCode.Wcs: + switch (to) + { + case CoordinateSystemCode.Ucs: + return editor.GetMatrixFromWcsToUcs(); + + case CoordinateSystemCode.MDcs: + return editor.GetMatrixFromMDcsToWcs(); + + case CoordinateSystemCode.PDcs: + throw new Autodesk.AutoCAD.Runtime.Exception( + ErrorStatus.InvalidInput, + "To be used only with DCS"); + } + break; + case CoordinateSystemCode.Ucs: + switch (to) + { + case CoordinateSystemCode.Wcs: + return editor.GetMatrixFromUcsToWcs(); + + case CoordinateSystemCode.MDcs: + return editor.GetMatrixFromUcsToWcs() * editor.GetMatrixFromWcsToMDcs(); + + case CoordinateSystemCode.PDcs: + throw new Autodesk.AutoCAD.Runtime.Exception( + ErrorStatus.InvalidInput, + "To be used only with DCS"); + } + break; + case CoordinateSystemCode.MDcs: + switch (to) + { + case CoordinateSystemCode.Wcs: + return editor.GetMatrixFromMDcsToWcs(); + + case CoordinateSystemCode.Ucs: + return editor.GetMatrixFromMDcsToWcs() * editor.GetMatrixFromWcsToUcs(); + + case CoordinateSystemCode.PDcs: + return editor.GetMatrixFromMDcsToPDcs(); + } + break; + case CoordinateSystemCode.PDcs: + switch (to) + { + case CoordinateSystemCode.Wcs: + throw new Autodesk.AutoCAD.Runtime.Exception( + ErrorStatus.InvalidInput, + "To be used only with DCS"); + case CoordinateSystemCode.Ucs: + throw new Autodesk.AutoCAD.Runtime.Exception( + ErrorStatus.InvalidInput, + "To be used only with DCS"); + case CoordinateSystemCode.MDcs: + return editor.GetMatrixFromPDcsToMDcs(); + } + break; + } + return Matrix3d.Identity; +#else return (from, to) switch { (CoordinateSystemCode.Wcs, CoordinateSystemCode.Ucs) => editor.GetMatrixFromWcsToUcs(), @@ -613,11 +677,12 @@ public static Matrix3d GetMatrix(this Editor editor, CoordinateSystemCode from, or (CoordinateSystemCode.Wcs or CoordinateSystemCode.Ucs, CoordinateSystemCode.PDcs) => throw new Autodesk.AutoCAD.Runtime.Exception(ErrorStatus.InvalidInput, "To be used only with DCS"), (_, _) => Matrix3d.Identity }; +#endif } - #endregion +#endregion - #region 缩放 +#region 缩放 /// /// 缩放窗口范围 @@ -762,9 +827,9 @@ public static void ZoomObject(this Editor ed, Entity ent, double offsetDist = 0. ed.ZoomWindow(ext.MinPoint, ext.MaxPoint, offsetDist); } - #endregion +#endregion - #region Get交互类 +#region Get交互类 /// /// 获取Point @@ -831,9 +896,9 @@ public static PromptResult GetString(this Editor ed, string Message, string Defa return ed.GetString(strOp); } - #endregion Get交互类 +#endregion Get交互类 - #region 执行lisp +#region 执行lisp #if ac2009 [System.Security.SuppressUnmanagedCodeSecurity] @@ -876,5 +941,5 @@ public static PromptResult GetString(this Editor ed, string Message, string Defa return null; } - #endregion 执行lisp +#endregion 执行lisp } diff --git a/src/IFoxCAD.Cad/ExtensionMethod/EntityEx.cs b/src/IFoxCAD.Cad/ExtensionMethod/EntityEx.cs index d94875e..9f46056 100644 --- a/src/IFoxCAD.Cad/ExtensionMethod/EntityEx.cs +++ b/src/IFoxCAD.Cad/ExtensionMethod/EntityEx.cs @@ -260,7 +260,11 @@ public static Arc CreateArc(Point3d startPoint, Point3d pointOnArc, Point3d endP //创建一个几何类的圆弧对象 CircularArc3d geArc = new(startPoint, pointOnArc, endPoint); //将几何类圆弧对象的圆心和半径赋值给圆弧 +#if ac2009 + return (Arc)geArc.ToCurve(); +#else return (Arc)Curve.CreateFromGeCurve(geArc); +#endif } /// @@ -282,9 +286,9 @@ public static Arc CreateArc(Point3d startPoint, Point3d centerPoint, double angl return arc; } - #endregion +#endregion - #region 圆 +#region 圆 /// /// 两点创建圆(两点中点为圆心) @@ -326,11 +330,11 @@ public static Circle CreateCircle(Point3d startPoint, Point3d endPoint) } } - #endregion +#endregion - #region 块参照 +#region 块参照 - #region 裁剪块参照 +#region 裁剪块参照 private const string filterDictName = "ACAD_FILTER"; private const string spatialName = "SPATIAL"; @@ -391,6 +395,30 @@ public static void ClipBlockRef(this BlockReference bref, Point3d pt1, Point3d p dict.SetAt(spatialName, sf); //SetToDictionary(dict, spatialName, sf); } - #endregion - #endregion +#endregion + + /// + /// 更新动态块属性值 + /// + /// 动态块 + /// 属性值字典 + public static void ChangeBlockProperty(this BlockReference blockReference, Dictionary propertyNameValues) + { + if (blockReference.IsDynamicBlock) + { + using (blockReference.ForWrite()) + { + foreach (DynamicBlockReferenceProperty item in blockReference.DynamicBlockReferencePropertyCollection) + { + if (propertyNameValues.ContainsKey(item.PropertyName)) + { + item.Value = propertyNameValues[item.PropertyName]; + } + } + } + + } + } + +#endregion } diff --git a/src/IFoxCAD.Cad/ExtensionMethod/GeometryEx.cs b/src/IFoxCAD.Cad/ExtensionMethod/GeometryEx.cs index 27b0eeb..db3bff6 100644 --- a/src/IFoxCAD.Cad/ExtensionMethod/GeometryEx.cs +++ b/src/IFoxCAD.Cad/ExtensionMethod/GeometryEx.cs @@ -19,14 +19,14 @@ public static class GeometryEx public static PointOnRegionType PointOnRegion(this IEnumerable pts, Point2d pt) { //遍历点集并生成首尾连接的多边形 - var ptlst = new LinkedList(pts); + var ptlst = new LoopList(pts); if (ptlst.Count < 3) return PointOnRegionType.Error; var ls2ds = new List(); foreach (var node in ptlst.GetNodes()) { - ls2ds.Add(new LineSegment2d(node.Value, node.Next.Value)); + ls2ds.Add(new LineSegment2d(node.Value, node.Next!.Value)); } var cc2d = new CompositeCurve2d(ls2ds.ToArray()); @@ -44,7 +44,7 @@ public static PointOnRegionType PointOnRegion(this IEnumerable pts, Poi foreach (var node in ptlst.GetNodes()) { var pt1 = node.Value; - var pt2 = node.Next.Value; + var pt2 = node.Next!.Value; if (pt.Y < pt1.Y && pt.Y < pt2.Y) continue; if (pt1.X < pt.X && pt2.X < pt.X) @@ -69,8 +69,8 @@ public static PointOnRegionType PointOnRegion(this IEnumerable pts, Poi public static PointOnRegionType PointOnRegion(this IEnumerable pts, Point3d pt) { //遍历点集并生成首尾连接的多边形 - var ptlst = new LinkedList(pts); - if (ptlst.First.Value == ptlst.Last.Value) + var ptlst = new LoopList(pts); + if (ptlst.First!.Value == ptlst.Last!.Value) ptlst.RemoveLast(); if (ptlst.Count < 3) return PointOnRegionType.Error; @@ -78,7 +78,7 @@ public static PointOnRegionType PointOnRegion(this IEnumerable pts, Poi var ls3ds = new List(); foreach (var node in ptlst.GetNodes()) { - ls3ds.Add(new LineSegment3d(node.Value, node.Next.Value)); + ls3ds.Add(new LineSegment3d(node.Value, node.Next!.Value)); } var cc3d = new CompositeCurve3d(ls3ds.ToArray()); @@ -96,7 +96,7 @@ public static PointOnRegionType PointOnRegion(this IEnumerable pts, Poi foreach (var node in ptlst.GetNodes()) { var pt1 = node.Value; - var pt2 = node.Next.Value; + var pt2 = node.Next!.Value; if (pt.Y < pt1.Y && pt.Y < pt2.Y) continue; if (pt1.X < pt.X && pt2.X < pt.X) @@ -119,9 +119,9 @@ public static PointOnRegionType PointOnRegion(this IEnumerable pts, Poi /// 基准点 /// 输出圆上的点 /// 解析类圆对象 - public static CircularArc2d GetMinCircle(Point2d pt1, Point2d pt2, out LinkedList ptlst) + public static CircularArc2d GetMinCircle(Point2d pt1, Point2d pt2, out LoopList ptlst) { - ptlst = new LinkedList { pt1, pt2 }; + ptlst = new LoopList { pt1, pt2 }; return new CircularArc2d ( @@ -138,24 +138,24 @@ public static CircularArc2d GetMinCircle(Point2d pt1, Point2d pt2, out LinkedLis /// 基准点 /// 输出圆上的点 /// 解析类圆对象 - public static CircularArc2d GetMinCircle(Point2d pt1, Point2d pt2, Point2d pt3, out LinkedList ptlst) + public static CircularArc2d GetMinCircle(Point2d pt1, Point2d pt2, Point2d pt3, out LoopList ptlst) { - ptlst = new LinkedList { pt1, pt2, pt3 }; + ptlst = new LoopList { pt1, pt2, pt3 }; //遍历各点与下一点的向量长度,找到距离最大的两个点 - LinkedListNode maxNode = - ptlst.GetNodes().FindByMax + LoopListNode maxNode = + ptlst.GetNodes().FindByMax ( out double maxLength, - node => node.Value.GetDistanceTo(node.Next.Value) + node => node.Value.GetDistanceTo(node.Next!.Value) ); //以两点做最小包围圆 CircularArc2d ca2d = - GetMinCircle(maxNode.Value, maxNode.Next.Value, out LinkedList tptlst); + GetMinCircle(maxNode.Value, maxNode.Next!.Value, out LoopList tptlst); //如果另一点属于该圆 - if (ca2d.IsIn(maxNode.Previous.Value)) + if (ca2d.IsIn(maxNode.Previous!.Value)) { //返回 ptlst = tptlst; @@ -163,7 +163,7 @@ public static CircularArc2d GetMinCircle(Point2d pt1, Point2d pt2, Point2d pt3, } //否则按三点做圆 //ptlst.SetFirst(maxNode); - ptlst = new LinkedList { maxNode.Value, maxNode.Next.Value, maxNode.Previous.Value }; + ptlst = new LoopList { maxNode.Value, maxNode.Next.Value, maxNode.Previous.Value }; ca2d = new CircularArc2d(pt1, pt2, pt3); ca2d.SetAngles(0, Math.PI * 2); return ca2d; @@ -178,22 +178,22 @@ public static CircularArc2d GetMinCircle(Point2d pt1, Point2d pt2, Point2d pt3, /// 基准点 /// 输出圆上的点 /// 解析类圆对象 - public static CircularArc2d? GetMinCircle(Point2d pt1, Point2d pt2, Point2d pt3, Point2d pt4, out LinkedList? ptlst) + public static CircularArc2d? GetMinCircle(Point2d pt1, Point2d pt2, Point2d pt3, Point2d pt4, out LoopList? ptlst) { - var iniptlst = new LinkedList() { pt1, pt2, pt3, pt4 }; + var iniptlst = new LoopList() { pt1, pt2, pt3, pt4 }; ptlst = null; CircularArc2d? ca2d = null; //遍历C43的组合,环链表的优势在这里 - foreach (LinkedListNode firstNode in iniptlst.GetNodes()) + foreach (LoopListNode firstNode in iniptlst.GetNodes()) { //获取各组合下三点的最小包围圆 var secondNode = firstNode.Next; - var thirdNode = secondNode.Next; - CircularArc2d tca2d = GetMinCircle(firstNode.Value, secondNode.Value, thirdNode.Value, out LinkedList tptlst); + var thirdNode = secondNode!.Next; + CircularArc2d tca2d = GetMinCircle(firstNode.Value, secondNode.Value, thirdNode!.Value, out LoopList tptlst); //如果另一点属于该圆,并且半径小于当前值就把它做为候选解 - if (tca2d.IsIn(firstNode.Previous.Value)) + if (tca2d.IsIn(firstNode.Previous!.Value)) { if (ca2d == null || tca2d.Radius < ca2d.Radius) { @@ -344,7 +344,7 @@ public static OrientationType IsClockWise(this IEnumerable pnts) /// 点集 /// 输出圆上的点 /// 解析类圆对象 - public static CircularArc2d? GetMinCircle(this List pnts, out LinkedList? ptlst) + public static CircularArc2d? GetMinCircle(this List pnts, out LoopList? ptlst) { //点数较小时直接返回 switch (pnts.Count) @@ -354,7 +354,7 @@ public static OrientationType IsClockWise(this IEnumerable pnts) return null; case 1: - ptlst = new LinkedList { pnts[0] }; + ptlst = new LoopList { pnts[0] }; return new CircularArc2d(pnts[0], 0); case 2: @@ -384,7 +384,7 @@ public static OrientationType IsClockWise(this IEnumerable pnts) //将结果作为新的前三点 if (ptlst!.Count == 3) { - tpnts[2] = ptlst.Last.Value; + tpnts[2] = ptlst.Last!.Value; } else { @@ -394,8 +394,8 @@ public static OrientationType IsClockWise(this IEnumerable pnts) tpnts.Except(ptlst) .FindByMax(pnt => ca2d!.Center.GetDistanceTo(pnt)); } - tpnts[0] = ptlst.First.Value; - tpnts[1] = ptlst.First.Next.Value; + tpnts[0] = ptlst.First!.Value; + tpnts[1] = ptlst.First.Next!.Value; //按此三点计算最小包围圆 ca2d = GetMinCircle(tpnts[0], tpnts[1], tpnts[2], out ptlst); diff --git a/src/IFoxCAD.Cad/ExtensionMethod/SymbolTableRecordEx.cs b/src/IFoxCAD.Cad/ExtensionMethod/SymbolTableRecordEx.cs index 0dae4a0..2a6e1bd 100644 --- a/src/IFoxCAD.Cad/ExtensionMethod/SymbolTableRecordEx.cs +++ b/src/IFoxCAD.Cad/ExtensionMethod/SymbolTableRecordEx.cs @@ -178,7 +178,7 @@ public static ObjectId AddPline(this BlockTableRecord btr, List pts, Li } return btr.AddEnt(pl, action, trans); } - +#if !ac2009 /// /// 在指定的绘图空间添加轻多段线 @@ -200,7 +200,7 @@ public static ObjectId AddPline(this BlockTableRecord btr, List<(Point3d pt, dou return btr.AddEnt(pl, action, trans); } - +#endif /// /// 在指定绘图空间X-Y平面3点画圆弧 @@ -217,9 +217,9 @@ public static ObjectId AddArc(this BlockTableRecord btr, Point3d startPoint, Poi var arc = EntityEx.CreateArc(startPoint, pointOnArc, endPoint); return btr.AddEnt(arc, action, trans); } - #endregion +#endregion - #region 获取实体/实体id +#region 获取实体/实体id /// /// 获取块表记录内的指定类型的实体 /// @@ -276,9 +276,9 @@ public static IEnumerable> GetObjectIds(this BlockTa return tr.GetObject(btr.DrawOrderTableId, OpenMode.ForRead) as DrawOrderTable; } - #endregion +#endregion - #region 插入块参照 +#region 插入块参照 /// /// 插入块参照 @@ -357,8 +357,8 @@ public static ObjectId InsertBlock(this BlockTableRecord blockTableRecord, Point return objid; } - #endregion - #endregion +#endregion +#endregion diff --git a/src/IFoxCAD.Cad/IFoxCAD.Cad.csproj b/src/IFoxCAD.Cad/IFoxCAD.Cad.csproj index 932577a..a7ab56a 100644 --- a/src/IFoxCAD.Cad/IFoxCAD.Cad.csproj +++ b/src/IFoxCAD.Cad/IFoxCAD.Cad.csproj @@ -1,10 +1,10 @@ - net45 + net35;net40;net45 true enable - 0.3.0.1 + 0.3.1 InspireFunction xsfhlzh;vicwjb 基于.NET的Cad二次开发类库 @@ -22,15 +22,43 @@ true + + + runtime + + + + + runtime + + + - - + + + runtime + + + + + + - + + DEBUG + + + $(Configuration);ac2009 + + + $(Configuration);ac2013 + + + $(Configuration);ac2015 + @@ -40,12 +68,12 @@ - + @@ -57,4 +85,5 @@ + diff --git a/src/IFoxCAD.Cad/Runtime/AcadVersion.cs b/src/IFoxCAD.Cad/Runtime/AcadVersion.cs index 5761498..99bceb2 100644 --- a/src/IFoxCAD.Cad/Runtime/AcadVersion.cs +++ b/src/IFoxCAD.Cad/Runtime/AcadVersion.cs @@ -51,13 +51,8 @@ public static List Versions /// 已打开的cad的版本号 /// 已打开cad的application对象 /// cad版本号对象 - public static CadVersion? FromApp(object app) + public static CadVersion? FromApp(object app!!) { - if (app == null) - { - throw new ArgumentNullException(nameof(app)); - } - string acver = app.GetType() .InvokeMember( diff --git a/src/IFoxCAD.Cad/Runtime/AutoRegAssem.cs b/src/IFoxCAD.Cad/Runtime/AutoRegAssem.cs index d329084..e336b3d 100644 --- a/src/IFoxCAD.Cad/Runtime/AutoRegAssem.cs +++ b/src/IFoxCAD.Cad/Runtime/AutoRegAssem.cs @@ -74,8 +74,11 @@ public AutoRegAssem() private static RegistryKey GetAcAppKey() { - +#if ac2009 + string key = HostApplicationServices.Current.RegistryProductRootKey; +#else string key = HostApplicationServices.Current.MachineRegistryProductRootKey; +#endif RegistryKey ackey = Registry.CurrentUser.OpenSubKey(key, true); return ackey.CreateSubKey("Applications"); diff --git a/src/IFoxCAD.Cad/Runtime/CadVersion.cs b/src/IFoxCAD.Cad/Runtime/CadVersion.cs index 6be6af8..aeec9e2 100644 --- a/src/IFoxCAD.Cad/Runtime/CadVersion.cs +++ b/src/IFoxCAD.Cad/Runtime/CadVersion.cs @@ -1,5 +1,59 @@ namespace IFoxCAD.Cad; +#if ac2009 + +public class CadVersion +{ + + /// + /// 主版本 + /// + public int Major { get; set; } + + /// + /// 次版本 + /// + public int Minor { get; set; } + + /// + /// 版本号 + /// + public double ProgId => double.Parse($"{Major}.{Minor}"); + + /// + /// 注册表名称 + /// + public string? ProductName { get; set; } + + /// + /// 注册表位置 + /// + public string? ProductRootKey { get; set; } + + /// + /// 转换为字符串 + /// + /// 表示版本号的字符串 + public override string ToString() + { + return $"名称:{ProductName}\n版本号:{ProgId}\n注册表位置:{ProductRootKey}"; + } + // public override bool Equals(object obj) + // { + // return base.Equals(obj); + // } + + // public override int GetHashCode() + // { + // return base.GetHashCode(); + // } + + // //public override string ToString() + // //{ + // // return base.ToString(); + // //} +} +#else public record CadVersion { /// @@ -36,3 +90,4 @@ public override string ToString() return $"名称:{ProductName}\n版本号:{ProgId}\n注册表位置:{ProductRootKey}"; } } +#endif \ No newline at end of file diff --git a/src/IFoxCAD.Cad/Runtime/DBTrans.cs b/src/IFoxCAD.Cad/Runtime/DBTrans.cs index f5f65c7..67fb7f0 100644 --- a/src/IFoxCAD.Cad/Runtime/DBTrans.cs +++ b/src/IFoxCAD.Cad/Runtime/DBTrans.cs @@ -2,7 +2,7 @@ public class DBTrans : IDisposable { - #region 私有字段 +#region 私有字段 /// /// 文档锁 /// @@ -21,7 +21,7 @@ public class DBTrans : IDisposable private static readonly Stack dBTrans = new(); #endregion - #region 公开属性 +#region 公开属性 /// /// 返回当前事务 /// @@ -61,7 +61,7 @@ public static DBTrans Top #endregion - #region 构造函数 +#region 构造函数 /// /// 默认构造函数,默认为打开当前文档,默认提交事务 /// @@ -101,8 +101,14 @@ public DBTrans(Database database, bool commit = true) /// 事务是否提交 public DBTrans(string fileName, bool commit = true) { +#if ac2009 + if (string.IsNullOrEmpty(fileName.Trim())) + throw new ArgumentNullException(nameof(fileName)); +#else + if (string.IsNullOrWhiteSpace(fileName)) throw new ArgumentNullException(nameof(fileName)); +#endif if (File.Exists(fileName)) { var doc = @@ -136,9 +142,9 @@ public DBTrans(string fileName, bool commit = true) _commit = commit; dBTrans.Push(this); } - #endregion +#endregion - #region 类型转换 +#region 类型转换 /// /// 隐式转换为Transaction /// @@ -148,9 +154,9 @@ public static implicit operator Transaction(DBTrans tr) { return tr.Transaction; } - #endregion +#endregion - #region 符号表 +#region 符号表 /// /// 块表 @@ -206,9 +212,9 @@ public static implicit operator Transaction(DBTrans tr) /// 视口表 /// public SymbolTable ViewportTable => new(this, Database.ViewportTableId); - #endregion +#endregion - #region 字典 +#region 字典 //TODO: 补充关于扩展字典,命名对象字典,组字典,多线样式字典等对象字典的属性 /// /// 命名对象字典 @@ -258,6 +264,7 @@ public static implicit operator Transaction(DBTrans tr) /// 数据链接字典 /// public DBDictionary DataLinkDict => GetObject(Database.DataLinkDictionaryId)!; +#if !ac2009 /// /// 详细视图样式字典 /// @@ -266,10 +273,10 @@ public static implicit operator Transaction(DBTrans tr) /// 剖面视图样式字典 /// public DBDictionary SectionViewStyleDict => GetObject(Database.SectionViewStyleDictionaryId)!; +#endif +#endregion - #endregion - - #region 获取对象 +#region 获取对象 /// /// 根据对象id获取图元对象 /// @@ -300,9 +307,9 @@ public ObjectId GetObjectId(string handleString) - #endregion +#endregion - #region idispose接口相关函数 +#region idispose接口相关函数 public void Abort() { @@ -361,5 +368,5 @@ public void Dispose() Dispose(disposing: true); GC.SuppressFinalize(this); } - #endregion +#endregion } diff --git a/tests/Test/TestCurve.cs b/tests/Test/TestCurve.cs new file mode 100644 index 0000000..3c68e86 --- /dev/null +++ b/tests/Test/TestCurve.cs @@ -0,0 +1,25 @@ +namespace Test +{ + public class TestCurve + { + [CommandMethod("testbreakcurve")] + public void TestBreakCurve() + { + var ents = Env.Editor.SSGet().Value.GetEntities(); + var tt = CurveEx.BreakCurve(ents.ToList()); + using var tr = new DBTrans(); + tt.ForEach(t => t.ForWrite(e => e.ColorIndex = 1)); + tr.CurrentSpace.AddEntity(tt); + } + [CommandMethod("testtopo")] + public void TestToPo() + { + var ents = Env.Editor.SSGet().Value.GetEntities(); + var tt = CurveEx.Topo(ents.ToList()); + using var tr = new DBTrans(); + tt.ForEach(t => t.ForWrite(e => e.ColorIndex = 1)); + tr.CurrentSpace.AddEntity(tt); + } + + } +} diff --git a/tests/Test/testblock.cs b/tests/Test/testblock.cs index 09c0584..0a9a79f 100644 --- a/tests/Test/testblock.cs +++ b/tests/Test/testblock.cs @@ -432,4 +432,22 @@ public void TestWblock() db.SaveAs(@"c:\test.dwg", DwgVersion.Current); } + public void TestChangeDynameicBlock() + { + var pro = new Dictionary(); + pro.Add("haha", 1); + var blockid = Env.Editor.GetEntity("选择个块").ObjectId; + using (var tr = new DBTrans()) + { + var blockref = tr.GetObject(blockid); + blockref.ChangeBlockProperty(pro); + // 这是第一个函数的用法 + } + + + + } + + + } \ No newline at end of file -- Gitee From 8da4ef2f98ee50c33f54d28b30ee92bb751fb013 Mon Sep 17 00:00:00 2001 From: vicwjb Date: Thu, 3 Mar 2022 03:15:15 +0000 Subject: [PATCH 041/675] =?UTF-8?q?update=20README.md.=20=E5=B8=AE?= =?UTF-8?q?=E5=8A=A9=E9=87=8C=E6=98=8E=E7=A1=AEstandord=E4=B8=BA2.0?= =?UTF-8?q?=E7=89=88=E6=9C=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index a92646b..50f9e4b 100644 --- a/README.md +++ b/README.md @@ -24,7 +24,7 @@ 1. 快速入门 - - 打开vs,新建一个standard类型的类库项目![](./docs/png/standard.png) + - 打开vs,新建一个standard类型的类库项目,**注意,需要选择类型的时候一定要选standord2.0** ![](./docs/png/standard.png) - 双击项目,打开项目文件: -- Gitee From 7ee42275c92600be93a970ac7d63d7502e27650a Mon Sep 17 00:00:00 2001 From: vicwjb Date: Thu, 3 Mar 2022 20:56:22 +0800 Subject: [PATCH 042/675] =?UTF-8?q?=E4=BC=98=E5=8C=96Edge=E5=92=8CEdgeItem?= =?UTF-8?q?=E7=B1=BB?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/IFoxCAD.Cad/ExtensionMethod/CurveEx.cs | 31 +++++++++------- src/IFoxCAD.Cad/Runtime/DBTrans.cs | 1 - tests/Test/TestCurve.cs | 41 ++++++++++++++++++++++ tests/Test/TestJig.cs | 11 +++--- 4 files changed, 64 insertions(+), 20 deletions(-) diff --git a/src/IFoxCAD.Cad/ExtensionMethod/CurveEx.cs b/src/IFoxCAD.Cad/ExtensionMethod/CurveEx.cs index 0d04a2f..9684fc3 100644 --- a/src/IFoxCAD.Cad/ExtensionMethod/CurveEx.cs +++ b/src/IFoxCAD.Cad/ExtensionMethod/CurveEx.cs @@ -45,7 +45,7 @@ public static IEnumerable GetSplitCurves(this Curve curve, IEnumerable(); } /// - /// 边 + /// 边节点 /// private struct EdgeItem : IEquatable { @@ -67,8 +67,8 @@ public EdgeItem(Edge edge, bool forward) } else { - cc3d = cc3d!.Clone() as CompositeCurve3d; - return cc3d!.GetReverseParameterCurve() as CompositeCurve3d; + cc3d = cc3d.Clone() as CompositeCurve3d; + return cc3d?.GetReverseParameterCurve() as CompositeCurve3d; } } @@ -182,23 +182,28 @@ public override int GetHashCode() return this.ToString().GetHashCode(); } } - + /// + /// 边 + /// private class Edge { - public CompositeCurve3d? Curve; + public CompositeCurve3d Curve; public int StartIndex; public int EndIndex; - + public Edge(CompositeCurve3d curve) + { + Curve = curve; + } public Vector3d GetStartVector() { - var inter = Curve?.GetInterval(); - PointOnCurve3d poc = new(Curve, inter!.LowerBound); + var inter = Curve.GetInterval(); + PointOnCurve3d poc = new(Curve, inter.LowerBound); return poc.GetDerivative(1); } public Vector3d GetEndVector() { - var inter = Curve!.GetInterval(); + var inter = Curve.GetInterval(); PointOnCurve3d poc = new(Curve, inter.UpperBound); return -poc.GetDerivative(1); } @@ -272,7 +277,7 @@ public static List Topo(List curves) var c3ds = gc1.GetSplitCurves(pars1); if (c3ds.Count > 0) { - edges.AddRange(c3ds.Select(c => new Edge { Curve = c })); + edges.AddRange(c3ds.Select(c => new Edge(c))); } else if (gc1.IsClosed()) { @@ -280,7 +285,7 @@ public static List Topo(List curves) } else { - edges.Add(new Edge { Curve = gc1 }); + edges.Add(new Edge(gc1)); } } else if (gc1.IsClosed()) @@ -296,7 +301,7 @@ public static List Topo(List curves) foreach (var edge in edges) { - if (edge.Curve!.IsClosed()) + if (edge.Curve.IsClosed()) { closedEdges.Add(edge); } @@ -330,7 +335,7 @@ public static List Topo(List curves) } } - newCurves.AddRange(closedEdges.Select(e => e.Curve!.ToCurve())!); + newCurves.AddRange(closedEdges.Select(e => e.Curve.ToCurve())!); edges = edges diff --git a/src/IFoxCAD.Cad/Runtime/DBTrans.cs b/src/IFoxCAD.Cad/Runtime/DBTrans.cs index 67fb7f0..79f032e 100644 --- a/src/IFoxCAD.Cad/Runtime/DBTrans.cs +++ b/src/IFoxCAD.Cad/Runtime/DBTrans.cs @@ -215,7 +215,6 @@ public static implicit operator Transaction(DBTrans tr) #endregion #region 字典 - //TODO: 补充关于扩展字典,命名对象字典,组字典,多线样式字典等对象字典的属性 /// /// 命名对象字典 /// diff --git a/tests/Test/TestCurve.cs b/tests/Test/TestCurve.cs index 3c68e86..4155779 100644 --- a/tests/Test/TestCurve.cs +++ b/tests/Test/TestCurve.cs @@ -20,6 +20,47 @@ public void TestToPo() tt.ForEach(t => t.ForWrite(e => e.ColorIndex = 1)); tr.CurrentSpace.AddEntity(tt); } + [CommandMethod("testCurveCurveIntersector3d")] + public void TestCurveCurveIntersector3d() + { + var ents = Env.Editor.SSGet().Value.GetEntities() + .Select(e => e.ToCompositeCurve3d()).ToList(); + + var cci3d = new CurveCurveIntersector3d(); + + + for (int i = 0; i < ents.Count; i++) + { + var gc1 = ents[i]; + var int1 = gc1.GetInterval(); + //var pars1 = paramss[i]; + for (int j = i; j < ents.Count; j++) + { + var gc2 = ents[j]; + //var pars2 = paramss[j]; + var int2 = gc2.GetInterval(); + cci3d.Set(gc1, gc2, int1,int2, Vector3d.ZAxis); + var d = cci3d.OverlapCount(); + for (int k = 0; k < cci3d.NumberOfIntersectionPoints; k++) + { + //var a = cci3d.GetOverlapRanges(k); + var b = cci3d.IsTangential(k); + var c = cci3d.IsTransversal(k); + //var d = cci3d.OverlapCount(); + var e = cci3d.OverlapDirection(); + Env.Print("i"); + } + + + + } + + } + // var tt = CurveEx.Topo(ents.ToList()); + //using var tr = new DBTrans(); + //tt.ForEach(t => t.ForWrite(e => e.ColorIndex = 1)); + //tr.CurrentSpace.AddEntity(tt); + } } } diff --git a/tests/Test/TestJig.cs b/tests/Test/TestJig.cs index a3328b4..81e1566 100644 --- a/tests/Test/TestJig.cs +++ b/tests/Test/TestJig.cs @@ -236,13 +236,12 @@ JigPrompts jp { case "Bold": { - // TODO - + break; } case "Italic": { - // TODO + break; } @@ -274,19 +273,19 @@ JigPrompts jp } case "LEft": { - // TODO + break; } case "RIght": { - // TODO + break; } case "Middle": { - // TODO + break; } -- Gitee From cbccce1166ef092d4b0ceda7168a5203ecd12729 Mon Sep 17 00:00:00 2001 From: liuqihong <540762622@qq.com> Date: Sun, 20 Mar 2022 18:12:24 +0800 Subject: [PATCH 043/675] =?UTF-8?q?=E4=BF=AE=E6=94=B9=E9=93=BE=E8=A1=A8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/IFoxCAD.Basal/LoopList.cs | 183 +++++++++++------- src/IFoxCAD.Cad/ExtensionMethod/GeometryEx.cs | 7 +- 2 files changed, 120 insertions(+), 70 deletions(-) diff --git a/src/IFoxCAD.Basal/LoopList.cs b/src/IFoxCAD.Basal/LoopList.cs index 79319f0..58c6412 100644 --- a/src/IFoxCAD.Basal/LoopList.cs +++ b/src/IFoxCAD.Basal/LoopList.cs @@ -1,6 +1,5 @@ using System; using System.Collections.Generic; -using System.Linq; namespace IFoxCAD.Collections { /// @@ -12,25 +11,23 @@ public class LoopListNode /// /// 取值 /// - public T Value { get; set; } + public T Value { internal set; get; } /// /// 上一个节点 /// - public LoopListNode Previous - { internal set; get; } + public LoopListNode Previous { internal set; get; } /// /// 下一个节点 /// - public LoopListNode Next - { internal set; get; } + public LoopListNode Next { internal set; get; } /// ///环链表序列 /// - public LoopList List - { internal set; get; } + public LoopList List { internal set; get; } + /// /// 环链表节点构造函数 /// @@ -54,43 +51,49 @@ public LoopListNode GetNext(bool forward) /// 环链表 /// /// - public class LoopList : - IEnumerable, IFormattable + public class LoopList : IEnumerable, IFormattable { + #region 成员 + /// - /// 默认构造函数 + /// 节点数 /// - public LoopList() - { } + public int Count { get; private set; } + /// - /// 环链表构造函数 + /// 首节点 /// - /// 节点迭代器 - public LoopList(IEnumerable values) - { - foreach (T value in values) - Add(value); - } + public LoopListNode First { get; private set; } /// - /// 节点数 + /// 尾节点 /// - public int Count - { get; private set; } + public LoopListNode Last => First?.Previous; + + #endregion + + #region 构造 /// - /// 首节点 + /// 默认构造函数 /// - public LoopListNode First - { get; private set; } + public LoopList() { } /// - /// 尾节点 + /// 环链表构造函数 /// - public LoopListNode Last + /// 节点迭代器 + public LoopList(IEnumerable values) { - get { return First?.Previous; } + var ge = values.GetEnumerator(); + while (ge.MoveNext()) + Add(ge.Current); } + + #endregion + + #region 方法 + /// /// 设置首节点 /// @@ -113,12 +116,60 @@ public bool SetFirst(LoopListNode node) /// 第二个节点 public void Swap(LoopListNode node1, LoopListNode node2) { - T value = node1.Value; + T value = node1.Value; node1.Value = node2.Value; node2.Value = value; } - #region Contains + /// + /// 链内翻转 + /// + public void Reverse() + { + LoopListNode first = First; + if (first == null) + return; + var last = Last; + for (int i = 0; i < Count / 2; i++) + { + Swap(first, last); + first = first.Next; + last = last.Previous; + } + } + + public void Clear() + { + //清理的时候不释放数组长度 + ForEach(a => + { + a.Value = default; + return false; + }); + Count = 0; + } + + /// + /// 从头遍历 + /// + /// + public void ForEach(Func, bool> action) + { + LoopListNode node = First; + if (node == null) + return; + + for (int i = 0; i < Count; i++) + { + if (action(node)) + break; + node = node.Next; + } + } + #endregion + + #region + /// /// 是否包含节点 /// @@ -128,6 +179,7 @@ public bool Contains(LoopListNode node) { return node != null && node.List == this; } + /// /// 是否包含值 /// @@ -135,19 +187,19 @@ public bool Contains(LoopListNode node) /// public bool Contains(T value) { - LoopListNode node = First; - if (node == null) - return false; - - for (int i = 0; i < Count; i++) + bool result = false; + ForEach(node => { - if (node.Value.Equals(value)) + if (node.Equals(value)) + { + result = true; return true; - node = node.Next; - } - - return false; + } + return false; + }); + return result; } + /// /// 获取节点 /// @@ -155,22 +207,20 @@ public bool Contains(T value) /// public LoopListNode GetNode(Func func) { - LoopListNode node = First; - if (node == null) - return null; - - for (int i = 0; i < Count; i++) + LoopListNode result = null; + ForEach(a => { - if (func(node.Value)) + if (func(a.Value)) { - return node; + result = a; + return true; } - node = node.Next; - } - return null; + return false; + }); + return result; } - #endregion Contains + #endregion #region Add @@ -187,16 +237,16 @@ public LoopListNode AddFirst(T value) }; if (Count == 0) { - First = node; + First = node; First.Previous = First.Next = node; } else { LoopListNode last = Last; - First.Previous = last.Next = node; - node.Next = First; - node.Previous = last; - First = node; + First.Previous = last.Next = node; + node.Next = First; + node.Previous = last; + First = node; } Count++; return First; @@ -213,15 +263,15 @@ public LoopListNode Add(T value) node.List = this; if (Count == 0) { - First = node; + First = node; First.Previous = First.Next = node; } else { LoopListNode last = First.Previous; - First.Previous = last.Next = node; - node.Next = First; - node.Previous = last; + First.Previous = last.Next = node; + node.Next = First; + node.Previous = last; } Count++; return Last; @@ -266,7 +316,7 @@ public LoopListNode AddAfter(LoopListNode node, T value) return tnode; } - #endregion Add + #endregion #region Remove @@ -352,7 +402,7 @@ public bool Remove(LoopListNode node) return false; } - #endregion Remove + #endregion #region LinkTo @@ -424,7 +474,7 @@ public void LinkTo(LoopListNode from, LoopListNode to, int number, bool is } } - #endregion LinkTo + #endregion #region IEnumerable 成员 @@ -485,9 +535,10 @@ System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() #endregion IEnumerable 成员 - #endregion IEnumerable 成员 + #endregion #region IFormattable 成员 + /// /// 转换为字符串 /// @@ -505,6 +556,6 @@ string IFormattable.ToString(string format, IFormatProvider formatProvider) return ToString(); } - #endregion IFormattable 成员 + #endregion } } diff --git a/src/IFoxCAD.Cad/ExtensionMethod/GeometryEx.cs b/src/IFoxCAD.Cad/ExtensionMethod/GeometryEx.cs index 1eaa06a..dacdc65 100644 --- a/src/IFoxCAD.Cad/ExtensionMethod/GeometryEx.cs +++ b/src/IFoxCAD.Cad/ExtensionMethod/GeometryEx.cs @@ -150,8 +150,7 @@ public static CircularArc2d GetMinCircle(Point2d pt1, Point2d PointV, out LoopLi /// 解析类圆对象 public static CircularArc2d GetMinCircle(Point2d pt1, Point2d PointV, Point2d pt3, out LoopList ptlst) { - ptlst = - new LoopList { pt1, PointV, pt3 }; + ptlst = new LoopList { pt1, PointV, pt3 }; //遍历各点与下一点的向量长度,找到距离最大的两个点 double maxLength; @@ -213,7 +212,7 @@ public static CircularArc2d GetMinCircle(Point2d pt1, Point2d PointV, Point2d pt { if (ca2d == null || tca2d.Radius < ca2d.Radius) { - ca2d = tca2d; + ca2d = tca2d; ptlst = tptlst; } } @@ -563,7 +562,7 @@ public static Vector3d Wcs2Dcs(this Vector3d vec, bool atPaperSpace) #endregion Ucs - + /// /// 返回不等比例变换矩阵 /// -- Gitee From 646b3f476f2106f9bb164d2f8ed13def2e54bf5e Mon Sep 17 00:00:00 2001 From: liuqihong <540762622@qq.com> Date: Sun, 20 Mar 2022 19:16:35 +0800 Subject: [PATCH 044/675] =?UTF-8?q?=E4=BF=AE=E6=94=B9=E5=8F=8C=E5=90=91?= =?UTF-8?q?=E9=93=BE=E8=A1=A8=E7=9A=84=E4=B8=80=E4=BA=9Bbug?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/IFoxCAD.Basal/LoopList.cs | 79 ++++++++++++++-------- src/IFoxCAD.Cad/ExtensionMethod/CurveEx.cs | 26 +++---- 2 files changed, 63 insertions(+), 42 deletions(-) diff --git a/src/IFoxCAD.Basal/LoopList.cs b/src/IFoxCAD.Basal/LoopList.cs index 58c6412..c8ca91b 100644 --- a/src/IFoxCAD.Basal/LoopList.cs +++ b/src/IFoxCAD.Basal/LoopList.cs @@ -32,10 +32,12 @@ public class LoopListNode /// 环链表节点构造函数 /// /// 节点值 - public LoopListNode(T value) + public LoopListNode(T value, LoopList ts) { Value = value; + List = ts; } + /// /// 获取当前节点的临近节点 /// @@ -149,6 +151,7 @@ public void Clear() Count = 0; } + /// /// 从头遍历 /// @@ -190,7 +193,7 @@ public bool Contains(T value) bool result = false; ForEach(node => { - if (node.Equals(value)) + if (node.Value.Equals(value)) { result = true; return true; @@ -200,6 +203,26 @@ public bool Contains(T value) return result; } + /// + /// 查找节点 + /// + /// + /// + public LoopListNode Find(T t2) + { + LoopListNode result = null; + ForEach(node => + { + if (node.Value.Equals(t2)) + { + result = node; + return true; + } + return false; + }); + return result; + } + /// /// 获取节点 /// @@ -208,11 +231,11 @@ public bool Contains(T value) public LoopListNode GetNode(Func func) { LoopListNode result = null; - ForEach(a => + ForEach(node => { - if (func(a.Value)) + if (func(node.Value)) { - result = a; + result = node; return true; } return false; @@ -231,10 +254,8 @@ public LoopListNode GetNode(Func func) /// public LoopListNode AddFirst(T value) { - LoopListNode node = new(value) - { - List = this - }; + LoopListNode node = new(value, this); + if (Count == 0) { First = node; @@ -259,8 +280,8 @@ public LoopListNode AddFirst(T value) /// public LoopListNode Add(T value) { - LoopListNode node = new(value); - node.List = this; + LoopListNode node = new(value, this); + if (Count == 0) { First = node; @@ -290,7 +311,8 @@ public LoopListNode AddBefore(LoopListNode node, T value) } else { - LoopListNode tnode = new(value); + LoopListNode tnode = new(value, this); + node.Previous.Next = tnode; tnode.Previous = node.Previous; node.Previous = tnode; @@ -307,7 +329,8 @@ public LoopListNode AddBefore(LoopListNode node, T value) /// public LoopListNode AddAfter(LoopListNode node, T value) { - LoopListNode tnode = new(value); + LoopListNode tnode = new(value, this); + node.Next.Previous = tnode; tnode.Next = node.Next; node.Next = tnode; @@ -342,7 +365,6 @@ public bool RemoveFirst() last.Next = First; break; } - Count--; return true; } @@ -378,28 +400,27 @@ public bool RemoveLast() /// public bool Remove(LoopListNode node) { - if (Contains(node)) + if (!Contains(node)) + return false; + + if (Count == 1) { - if (Count == 1) + First = null; + } + else + { + if (node == First) { - First = null; + RemoveFirst(); } else { - if (node == First) - { - RemoveFirst(); - } - else - { - node.Next.Previous = node.Previous; - node.Previous.Next = node.Next; - } + node.Next.Previous = node.Previous; + node.Previous.Next = node.Next; } - Count--; - return true; } - return false; + Count--; + return true; } #endregion diff --git a/src/IFoxCAD.Cad/ExtensionMethod/CurveEx.cs b/src/IFoxCAD.Cad/ExtensionMethod/CurveEx.cs index af13df1..524a6ea 100644 --- a/src/IFoxCAD.Cad/ExtensionMethod/CurveEx.cs +++ b/src/IFoxCAD.Cad/ExtensionMethod/CurveEx.cs @@ -58,7 +58,7 @@ private struct EdgeItem : IEquatable public EdgeItem(Edge edge, bool forward) { - Edge = edge; + Edge = edge; Forward = forward; } @@ -125,12 +125,12 @@ public EdgeItem GetNext(List edges) int next; if (Forward) { - vec = Edge.GetEndVector(); + vec = Edge.GetEndVector(); next = Edge.EndIndex; } else { - vec = Edge.GetStartVector(); + vec = Edge.GetStartVector(); next = Edge.StartIndex; } @@ -148,19 +148,19 @@ public EdgeItem GetNext(List edges) var angle2 = vec.GetAngleTo(vec3, Vector3d.ZAxis); if (angle2 < angle) { - vec2 = vec3; - angle = angle2; - item.Edge = edge; + vec2 = vec3; + angle = angle2; + item.Edge = edge; item.Forward = forward; } } else { - vec2 = vec3; - angle = vec.GetAngleTo(vec2, Vector3d.ZAxis); - item.Edge = edge; + vec2 = vec3; + angle = vec.GetAngleTo(vec2, Vector3d.ZAxis); + item.Edge = edge; item.Forward = forward; - hasNext = true; + hasNext = true; } } } @@ -335,7 +335,7 @@ public static List Topo(List curves) if (nums[edge.StartIndex] == 1 && nums[edge.EndIndex] == 1) { nums[edge.StartIndex] = 0; - nums[edge.EndIndex] = 0; + nums[edge.EndIndex] = 0; } else { @@ -343,12 +343,12 @@ public static List Topo(List curves) if (nums[edge.StartIndex] == 1) { nums[edge.StartIndex] = 0; - nums[next = edge.EndIndex]--; + nums[next = edge.EndIndex]--; } else { nums[edge.EndIndex] = 0; - nums[next = edge.StartIndex]--; + nums[next = edge.StartIndex]--; } } } -- Gitee From 306b62d867617dc0bce57ea7201054ed0ed1c0d4 Mon Sep 17 00:00:00 2001 From: liuqihong <540762622@qq.com> Date: Sun, 20 Mar 2022 20:44:07 +0800 Subject: [PATCH 045/675] =?UTF-8?q?=E4=BF=AE=E6=94=B9=E7=8E=AF=E9=93=BE?= =?UTF-8?q?=E8=A1=A8=E9=94=99=E8=AF=AF,=E5=A2=9E=E5=8A=A0=E9=93=BE?= =?UTF-8?q?=E8=A1=A8=E9=80=86=E8=BD=AC=E9=87=87=E7=94=A8=E4=BA=8C=E5=88=86?= =?UTF-8?q?=E6=B3=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/IFoxCAD.Basal/LoopList.cs | 291 +++++++++++++++++++++------------- 1 file changed, 180 insertions(+), 111 deletions(-) diff --git a/src/IFoxCAD.Basal/LoopList.cs b/src/IFoxCAD.Basal/LoopList.cs index ee6edc5..3007dae 100644 --- a/src/IFoxCAD.Basal/LoopList.cs +++ b/src/IFoxCAD.Basal/LoopList.cs @@ -14,28 +14,27 @@ public class LoopListNode /// /// 上一个节点 /// - public LoopListNode? Previous - { internal set; get; } + public LoopListNode? Previous { internal set; get; } /// /// 下一个节点 /// - public LoopListNode? Next - { internal set; get; } + public LoopListNode? Next { internal set; get; } /// ///环链表序列 /// - public LoopList? List - { internal set; get; } + public LoopList? List { internal set; get; } /// /// 环链表节点构造函数 /// /// 节点值 - public LoopListNode(T value) + public LoopListNode(T value, LoopList ts) { Value = value; + List = ts; } + /// /// 获取当前节点的临近节点 /// @@ -53,40 +52,47 @@ public LoopListNode(T value) /// public class LoopList : IEnumerable, IFormattable { + #region 成员 + /// - /// 默认构造函数 + /// 节点数 /// - public LoopList() - { } + public int Count { get; private set; } + /// - /// 环链表构造函数 + /// 首节点 /// - /// 节点迭代器 - public LoopList(IEnumerable values) - { - foreach (T value in values) - Add(value); - } + public LoopListNode First { get; private set; } /// - /// 节点数 + /// 尾节点 /// - public int Count - { get; private set; } + public LoopListNode Last => First?.Previous; + + #endregion + + #region 构造 /// - /// 首节点 + /// 默认构造函数 /// - public LoopListNode? First - { get; private set; } + public LoopList() { } /// - /// 尾节点 + /// 环链表构造函数 /// - public LoopListNode? Last + /// 节点迭代器 + public LoopList(IEnumerable values) { - get { return First?.Previous; } + var ge = values.GetEnumerator(); + while (ge.MoveNext()) + Add(ge.Current); } + + #endregion + + #region 方法 + /// /// 设置首节点 /// @@ -94,12 +100,11 @@ public LoopListNode? Last /// public bool SetFirst(LoopListNode node) { - if (Contains(node)) - { - First = node; - return true; - } - return false; + if (!Contains(node)) + return false; + + First = node; + return true; } /// @@ -109,18 +114,65 @@ public bool SetFirst(LoopListNode node) /// 第二个节点 public void Swap(LoopListNode node1, LoopListNode node2) { -#if ac2009 -#pragma warning disable IDE0180 // 使用元组交换值 - T value = node1.Value; -#pragma warning restore IDE0180 // 使用元组交换值 - node1.Value = node2.Value; - node2.Value = value; +#if NET35 + T value = node1.Value; + node1.Value = node2.Value; + node2.Value = value; #else (node2.Value, node1.Value) = (node1.Value, node2.Value); #endif } -#region Contains + /// + /// 链内翻转 + /// + public void Reverse() + { + LoopListNode first = First; + if (first == null) + return; + var last = Last; + for (int i = 0; i < Count / 2; i++) + { + Swap(first, last); + first = first.Next; + last = last.Previous; + } + } + + public void Clear() + { + //清理的时候不释放数组长度 + ForEach(a => + { + a.Value = default; + return false; + }); + Count = 0; + } + + + /// + /// 从头遍历 + /// + /// + public void ForEach(Func, bool> action) + { + LoopListNode node = First; + if (node == null) + return; + + for (int i = 0; i < Count; i++) + { + if (action(node)) + break; + node = node.Next; + } + } + #endregion + + #region Contains + /// /// 是否包含节点 /// @@ -130,6 +182,7 @@ public bool Contains(LoopListNode node) { return node != null && node.List == this; } + /// /// 是否包含值 /// @@ -137,19 +190,39 @@ public bool Contains(LoopListNode node) /// public bool Contains(T value) { - - var node = First; - if (node != null) + bool result = false; + ForEach(node => { - for (int i = 0; i < Count; i++) + if (node.Value.Equals(value)) { - if (node!.Value!.Equals(value)) - return true; - node = node.Next; + result = true; + return true; } - } - return false; + return false; + }); + return result; } + + /// + /// 查找节点 + /// + /// + /// + public LoopListNode Find(T t2) + { + LoopListNode result = null; + ForEach(node => + { + if (node.Value.Equals(t2)) + { + result = node; + return true; + } + return false; + }); + return result; + } + /// /// 获取节点 /// @@ -157,24 +230,22 @@ public bool Contains(T value) /// public LoopListNode? GetNode(Func func) { - var node = First; - if (node != null) + LoopListNode result = null; + ForEach(node => { - for (int i = 0; i < Count; i++) + if (func(node.Value)) { - if (func(node!.Value)) - { - return node; - } - node = node.Next; + result = node; + return true; } - } - return null; + return false; + }); + return result; } -#endregion Contains + #endregion -#region Add + #region Add /// /// 在首节点之前插入节点,并设置新节点为首节点 @@ -183,22 +254,20 @@ public bool Contains(T value) /// public LoopListNode AddFirst(T value) { - var node = new LoopListNode(value) - { - List = this - }; + var node = new LoopListNode(value, this); + if (Count == 0) { - First = node; + First = node; First.Previous = First.Next = node; } else { LoopListNode last = Last!; - First!.Previous = last.Next = node; - node.Next = First; - node.Previous = last; - First = node; + First!.Previous = last.Next = node; + node.Next = First; + node.Previous = last; + First = node; } Count++; return First; @@ -211,21 +280,19 @@ public LoopListNode AddFirst(T value) /// public LoopListNode Add(T value) { - var node = new LoopListNode(value) - { - List = this - }; + var node = new LoopListNode(value, this); + if (Count == 0) { - First = node; + First = node; First.Previous = First.Next = node; } else { - var last = First!.Previous!; + var last = First!.Previous!; First.Previous = last.Next = node; - node.Next = First; - node.Previous = last; + node.Next = First; + node.Previous = last; } Count++; return Last!; @@ -244,7 +311,8 @@ public LoopListNode AddBefore(LoopListNode node, T value) } else { - var tnode = new LoopListNode(value); + var tnode = new LoopListNode(value, this); + node.Previous!.Next = tnode; tnode.Previous = node.Previous; node.Previous = tnode; @@ -261,7 +329,8 @@ public LoopListNode AddBefore(LoopListNode node, T value) /// public LoopListNode AddAfter(LoopListNode node, T value) { - LoopListNode tnode = new(value); + var tnode = new LoopListNode(value, this); + node.Next!.Previous = tnode; tnode.Next = node.Next; node.Next = tnode; @@ -270,9 +339,9 @@ public LoopListNode AddAfter(LoopListNode node, T value) return tnode; } -#endregion Add + #endregion -#region Remove + #region Remove /// /// 删除首节点 @@ -296,7 +365,6 @@ public bool RemoveFirst() last.Next = First; break; } - Count--; return true; } @@ -332,33 +400,33 @@ public bool RemoveLast() /// public bool Remove(LoopListNode node) { - if (Contains(node)) + if (!Contains(node)) + return false; + + if (Count == 1) + { + First = null; + } + else { - if (Count == 1) + if (node == First) { - First = null; + RemoveFirst(); } else { - if (node == First) - { - RemoveFirst(); - } - else - { - node.Next!.Previous = node.Previous; - node.Previous!.Next = node.Next; - } + node.Next.Previous = node.Previous; + node.Previous.Next = node.Next; } - Count--; - return true; } - return false; + + Count--; + return true; } -#endregion Remove + #endregion -#region LinkTo + #region LinkTo /// /// 链接两节点,并去除这两个节点间的所有节点 @@ -428,9 +496,9 @@ public void LinkTo(LoopListNode from, LoopListNode to, int number, bool is } } -#endregion LinkTo + #endregion -#region IEnumerable 成员 + #region IEnumerable 成员 /// /// 获取节点的查询器 @@ -477,27 +545,28 @@ public IEnumerator GetEnumerator() IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); -#region IEnumerable 成员 + #region IEnumerable 成员 IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); -#endregion IEnumerable 成员 + #endregion IEnumerable 成员 -#endregion IEnumerable 成员 + #endregion -#region IFormattable 成员 + #region IFormattable 成员 /// /// 转换为字符串 /// /// public override string ToString() { - string s = "( "; + var s = new StringBuilder(); + s.Append("( "); foreach (T value in this) - { - s += $"{value} "; - } - return s + ")"; + s.Append($"{value} "); + + s.Append(")"); + return s.ToString(); } string IFormattable.ToString(string format, IFormatProvider formatProvider) @@ -505,6 +574,6 @@ string IFormattable.ToString(string format, IFormatProvider formatProvider) return ToString(); } -#endregion IFormattable 成员 + #endregion } } \ No newline at end of file -- Gitee From 1eed3874942e691f852fa93f34caa5cfe45141af Mon Sep 17 00:00:00 2001 From: vicwjb Date: Sun, 20 Mar 2022 23:53:38 +0800 Subject: [PATCH 046/675] =?UTF-8?q?=E4=BC=98=E5=8C=96looplist?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/IFoxCAD.Basal/LoopList.cs | 95 +++++++++++++++++++++++++---------- 1 file changed, 69 insertions(+), 26 deletions(-) diff --git a/src/IFoxCAD.Basal/LoopList.cs b/src/IFoxCAD.Basal/LoopList.cs index 3007dae..b257952 100644 --- a/src/IFoxCAD.Basal/LoopList.cs +++ b/src/IFoxCAD.Basal/LoopList.cs @@ -1,4 +1,7 @@ -namespace IFoxCAD.Basal +using static System.Net.Mime.MediaTypeNames; +using System.Collections.Generic; + +namespace IFoxCAD.Basal { /// /// 环链表节点 @@ -43,6 +46,13 @@ public LoopListNode(T value, LoopList ts) public LoopListNode? GetNext(bool forward) { return forward ? Next : Previous; +} + + internal void Invalidate() + { + List = null; + Next = null; + Previous = null; } } @@ -62,12 +72,12 @@ public class LoopList : IEnumerable, IFormattable /// /// 首节点 /// - public LoopListNode First { get; private set; } + public LoopListNode? First { get; private set; } /// /// 尾节点 /// - public LoopListNode Last => First?.Previous; + public LoopListNode? Last => First?.Previous; #endregion @@ -115,7 +125,7 @@ public bool SetFirst(LoopListNode node) public void Swap(LoopListNode node1, LoopListNode node2) { #if NET35 - T value = node1.Value; + var value = node1.Value; node1.Value = node2.Value; node2.Value = value; #else @@ -128,15 +138,15 @@ public void Swap(LoopListNode node1, LoopListNode node2) /// public void Reverse() { - LoopListNode first = First; + var first = First; if (first == null) return; var last = Last; for (int i = 0; i < Count / 2; i++) { - Swap(first, last); - first = first.Next; - last = last.Previous; + Swap(first!, last!); + first = first!.Next; + last = last!.Previous; } } @@ -145,7 +155,8 @@ public void Clear() //清理的时候不释放数组长度 ForEach(a => { - a.Value = default; + //a.Value = default; + a.Invalidate(); return false; }); Count = 0; @@ -158,16 +169,17 @@ public void Clear() /// public void ForEach(Func, bool> action) { - LoopListNode node = First; + var node = First; if (node == null) return; - for (int i = 0; i < Count; i++) { - if (action(node)) + if (action(node!)) break; - node = node.Next; + node = node!.Next; } + + } #endregion @@ -193,7 +205,8 @@ public bool Contains(T value) bool result = false; ForEach(node => { - if (node.Value.Equals(value)) + + if (node.Value!.Equals(value)) { result = true; return true; @@ -208,19 +221,48 @@ public bool Contains(T value) /// /// /// - public LoopListNode Find(T t2) + public LoopListNode? Find(T value) { - LoopListNode result = null; - ForEach(node => + //LoopListNode result = null; + //ForEach(node => + //{ + // if (node.Value.Equals(t2)) + // { + // result = node; + // return true; + // } + // return false; + //}); + //return result; + + LoopListNode? node = First; + EqualityComparer c = EqualityComparer.Default; + if (node != null) { - if (node.Value.Equals(t2)) + if (value != null) { - result = node; - return true; + do + { + if (c.Equals(node!.Value, value)) + { + return node; + } + node = node.Next; + } while (node != First); } - return false; - }); - return result; + else + { + do + { + if (node!.Value == null) + { + return node; + } + node = node.Next; + } while (node != First); + } + } + return null; } /// @@ -230,7 +272,7 @@ public LoopListNode Find(T t2) /// public LoopListNode? GetNode(Func func) { - LoopListNode result = null; + LoopListNode? result = null; ForEach(node => { if (func(node.Value)) @@ -415,10 +457,11 @@ public bool Remove(LoopListNode node) } else { - node.Next.Previous = node.Previous; - node.Previous.Next = node.Next; + node.Next!.Previous = node.Previous; + node.Previous!.Next = node.Next; } } + node.Invalidate(); Count--; return true; -- Gitee From e100a24886b857377912b144027f551d42f05f26 Mon Sep 17 00:00:00 2001 From: liuqihong <540762622@qq.com> Date: Mon, 21 Mar 2022 01:14:36 +0800 Subject: [PATCH 047/675] =?UTF-8?q?=E4=BC=98=E5=8C=96LoopList?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/IFoxCAD.Basal/LoopList.cs | 228 ++++++++++++++++++++-------------- 1 file changed, 134 insertions(+), 94 deletions(-) diff --git a/src/IFoxCAD.Basal/LoopList.cs b/src/IFoxCAD.Basal/LoopList.cs index c8ca91b..bac9911 100644 --- a/src/IFoxCAD.Basal/LoopList.cs +++ b/src/IFoxCAD.Basal/LoopList.cs @@ -1,5 +1,8 @@ using System; +using System.Collections; using System.Collections.Generic; +using System.Text; + namespace IFoxCAD.Collections { /// @@ -11,22 +14,22 @@ public class LoopListNode /// /// 取值 /// - public T Value { internal set; get; } + public T Value; /// /// 上一个节点 /// - public LoopListNode Previous { internal set; get; } + public LoopListNode? Previous { internal set; get; } /// /// 下一个节点 /// - public LoopListNode Next { internal set; get; } + public LoopListNode? Next { internal set; get; } /// ///环链表序列 /// - public LoopList List { internal set; get; } + public LoopList? List { internal set; get; } /// /// 环链表节点构造函数 @@ -43,10 +46,21 @@ public LoopListNode(T value, LoopList ts) /// /// 搜索方向标志,为向前搜索,为向后搜索 /// - public LoopListNode GetNext(bool forward) + public LoopListNode? GetNext(bool forward) { return forward ? Next : Previous; } + + /// + /// 无效化成员 + /// + internal void Invalidate() + { + Value = default!; + List = null; + Next = null; + Previous = null; + } } /// @@ -65,12 +79,12 @@ public class LoopList : IEnumerable, IFormattable /// /// 首节点 /// - public LoopListNode First { get; private set; } + public LoopListNode? First { get; private set; } /// /// 尾节点 /// - public LoopListNode Last => First?.Previous; + public LoopListNode? Last => First?.Previous; #endregion @@ -103,12 +117,11 @@ public LoopList(IEnumerable values) /// public bool SetFirst(LoopListNode node) { - if (Contains(node)) - { - First = node; - return true; - } - return false; + if (!Contains(node)) + return false; + + First = node; + return true; } /// @@ -118,9 +131,13 @@ public bool SetFirst(LoopListNode node) /// 第二个节点 public void Swap(LoopListNode node1, LoopListNode node2) { - T value = node1.Value; - node1.Value = node2.Value; - node2.Value = value; +#if NET35 + var value = node1.Value; + node1.Value = node2.Value; + node2.Value = value; +#else + (node2.Value, node1.Value) = (node1.Value, node2.Value); +#endif } /// @@ -128,24 +145,29 @@ public void Swap(LoopListNode node1, LoopListNode node2) /// public void Reverse() { - LoopListNode first = First; + var first = First; if (first == null) return; + var last = Last; for (int i = 0; i < Count / 2; i++) { - Swap(first, last); - first = first.Next; - last = last.Previous; + Swap(first!, last!); + first = first!.Next; + last = last!.Previous; } } + /// + /// 清理 + /// public void Clear() { //清理的时候不释放数组长度 ForEach(a => { - a.Value = default; + //a.Value = default; + a.Invalidate(); return false; }); Count = 0; @@ -158,20 +180,19 @@ public void Clear() /// public void ForEach(Func, bool> action) { - LoopListNode node = First; + var node = First; if (node == null) return; - for (int i = 0; i < Count; i++) { - if (action(node)) + if (action(node!)) break; - node = node.Next; + node = node!.Next; } } #endregion - #region + #region Contains /// /// 是否包含节点 @@ -193,7 +214,7 @@ public bool Contains(T value) bool result = false; ForEach(node => { - if (node.Value.Equals(value)) + if (node.Value!.Equals(value)) { result = true; return true; @@ -208,19 +229,44 @@ public bool Contains(T value) /// /// /// - public LoopListNode Find(T t2) + public LoopListNode? Find(T value) { - LoopListNode result = null; - ForEach(node => + //LoopListNode result = null; + //ForEach(node => + //{ + // if (node.Value.Equals(t2)) + // { + // result = node; + // return true; + // } + // return false; + //}); + //return result; + + LoopListNode? node = First; + EqualityComparer c = EqualityComparer.Default; + if (node != null) { - if (node.Value.Equals(t2)) + if (value != null) { - result = node; - return true; + do + { + if (c.Equals(node!.Value, value)) + return node; + node = node.Next; + } while (node != First); } - return false; - }); - return result; + else + { + do + { + if (node!.Value == null) + return node; + node = node.Next; + } while (node != First); + } + } + return null; } /// @@ -228,9 +274,9 @@ public LoopListNode Find(T t2) /// /// /// - public LoopListNode GetNode(Func func) + public LoopListNode? GetNode(Func func) { - LoopListNode result = null; + LoopListNode? result = null; ForEach(node => { if (func(node.Value)) @@ -254,7 +300,7 @@ public LoopListNode GetNode(Func func) /// public LoopListNode AddFirst(T value) { - LoopListNode node = new(value, this); + var node = new LoopListNode(value, this); if (Count == 0) { @@ -263,8 +309,8 @@ public LoopListNode AddFirst(T value) } else { - LoopListNode last = Last; - First.Previous = last.Next = node; + LoopListNode last = Last!; + First!.Previous = last.Next = node; node.Next = First; node.Previous = last; First = node; @@ -280,7 +326,7 @@ public LoopListNode AddFirst(T value) /// public LoopListNode Add(T value) { - LoopListNode node = new(value, this); + var node = new LoopListNode(value, this); if (Count == 0) { @@ -289,14 +335,15 @@ public LoopListNode Add(T value) } else { - LoopListNode last = First.Previous; - First.Previous = last.Next = node; - node.Next = First; - node.Previous = last; + var last = First!.Previous!; + First.Previous = last.Next = node; + node.Next = First; + node.Previous = last; } Count++; - return Last; + return Last!; } + /// /// 前面增加节点 /// @@ -306,21 +353,17 @@ public LoopListNode Add(T value) public LoopListNode AddBefore(LoopListNode node, T value) { if (node == First) - { return AddFirst(value); - } - else - { - LoopListNode tnode = new(value, this); - - node.Previous.Next = tnode; - tnode.Previous = node.Previous; - node.Previous = tnode; - tnode.Next = node; - Count++; - return tnode; - } + + var tnode = new LoopListNode(value, this); + node.Previous!.Next = tnode; + tnode.Previous = node.Previous; + node.Previous = tnode; + tnode.Next = node; + Count++; + return tnode; } + /// /// 后面增加节点 /// @@ -329,9 +372,8 @@ public LoopListNode AddBefore(LoopListNode node, T value) /// public LoopListNode AddAfter(LoopListNode node, T value) { - LoopListNode tnode = new(value, this); - - node.Next.Previous = tnode; + var tnode = new LoopListNode(value, this); + node.Next!.Previous = tnode; tnode.Next = node.Next; node.Next = tnode; tnode.Previous = node; @@ -359,9 +401,9 @@ public bool RemoveFirst() break; default: - LoopListNode last = Last; - First = First.Next; - First.Previous = last; + LoopListNode last = Last!; + First = First!.Next; + First!.Previous = last; last.Next = First; break; } @@ -384,9 +426,9 @@ public bool RemoveLast() break; default: - LoopListNode last = Last.Previous; + LoopListNode last = Last!.Previous!; last.Next = First; - First.Previous = last; + First!.Previous = last; break; } Count--; @@ -415,10 +457,12 @@ public bool Remove(LoopListNode node) } else { - node.Next.Previous = node.Previous; - node.Previous.Next = node.Next; + node.Next!.Previous = node.Previous; + node.Previous!.Next = node.Next; } } + node.Invalidate(); + Count--; return true; } @@ -436,7 +480,7 @@ public void LinkTo(LoopListNode from, LoopListNode to) { if (from != to && Contains(from) && Contains(to)) { - LoopListNode node = from.Next; + LoopListNode node = from.Next!; bool isFirstChanged = false; int number = 0; @@ -445,7 +489,7 @@ public void LinkTo(LoopListNode from, LoopListNode to) if (node == First) isFirstChanged = true; - node = node.Next; + node = node.Next!; number++; } @@ -506,11 +550,11 @@ public void LinkTo(LoopListNode from, LoopListNode to, int number, bool is /// public IEnumerable> GetNodes(LoopListNode from) { - LoopListNode node = from; + var node = from; for (int i = 0; i < Count; i++) { - yield return node; - node = node.Next; + yield return node!; + node = node!.Next; } } @@ -520,11 +564,11 @@ public IEnumerable> GetNodes(LoopListNode from) /// public IEnumerable> GetNodes() { - LoopListNode node = First; + LoopListNode node = First!; for (int i = 0; i < Count; i++) { - yield return node; - node = node.Next; + yield return node!; + node = node.Next!; } } @@ -534,42 +578,38 @@ public IEnumerable> GetNodes() /// public IEnumerator GetEnumerator() { - LoopListNode node = First; + LoopListNode node = First!; for (int i = 0; i < Count; i++) { - yield return node.Value; - node = node.Next; + yield return node!.Value; + node = node.Next!; } } - IEnumerator IEnumerable.GetEnumerator() - { - return GetEnumerator(); - } + IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); #region IEnumerable 成员 - System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() - { - return GetEnumerator(); - } + IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); #endregion IEnumerable 成员 #endregion #region IFormattable 成员 - /// /// 转换为字符串 /// /// public override string ToString() { - string s = "( "; + var s = new StringBuilder(); + s.Append("( "); foreach (T value in this) - s += value.ToString() + " "; - return s + ")"; + s.Append($"{value} "); + + s.Append(")"); + return s.ToString(); } string IFormattable.ToString(string format, IFormatProvider formatProvider) @@ -579,4 +619,4 @@ string IFormattable.ToString(string format, IFormatProvider formatProvider) #endregion } -} +} \ No newline at end of file -- Gitee From e8d08002c86cee653c79244a3f09d2f9676874a8 Mon Sep 17 00:00:00 2001 From: liuqihong <540762622@qq.com> Date: Mon, 21 Mar 2022 01:16:05 +0800 Subject: [PATCH 048/675] =?UTF-8?q?=E4=BC=98=E5=8C=96Looplist?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/IFoxCAD.Basal/LoopList.cs | 55 ++++++++++++++++------------------- 1 file changed, 25 insertions(+), 30 deletions(-) diff --git a/src/IFoxCAD.Basal/LoopList.cs b/src/IFoxCAD.Basal/LoopList.cs index b257952..40bd168 100644 --- a/src/IFoxCAD.Basal/LoopList.cs +++ b/src/IFoxCAD.Basal/LoopList.cs @@ -1,7 +1,4 @@ -using static System.Net.Mime.MediaTypeNames; -using System.Collections.Generic; - -namespace IFoxCAD.Basal +namespace IFoxCAD.Basal { /// /// 环链表节点 @@ -12,7 +9,7 @@ public class LoopListNode /// /// 取值 /// - public T Value { get; set; } + public T Value; /// /// 上一个节点 @@ -28,6 +25,7 @@ public class LoopListNode ///环链表序列 /// public LoopList? List { internal set; get; } + /// /// 环链表节点构造函数 /// @@ -46,12 +44,16 @@ public LoopListNode(T value, LoopList ts) public LoopListNode? GetNext(bool forward) { return forward ? Next : Previous; -} + } + /// + /// 无效化成员 + /// internal void Invalidate() { - List = null; - Next = null; + Value = default!; + List = null; + Next = null; Previous = null; } } @@ -125,7 +127,7 @@ public bool SetFirst(LoopListNode node) public void Swap(LoopListNode node1, LoopListNode node2) { #if NET35 - var value = node1.Value; + var value = node1.Value; node1.Value = node2.Value; node2.Value = value; #else @@ -141,6 +143,7 @@ public void Reverse() var first = First; if (first == null) return; + var last = Last; for (int i = 0; i < Count / 2; i++) { @@ -150,6 +153,9 @@ public void Reverse() } } + /// + /// 清理 + /// public void Clear() { //清理的时候不释放数组长度 @@ -178,8 +184,6 @@ public void ForEach(Func, bool> action) break; node = node!.Next; } - - } #endregion @@ -205,7 +209,6 @@ public bool Contains(T value) bool result = false; ForEach(node => { - if (node.Value!.Equals(value)) { result = true; @@ -244,9 +247,7 @@ public bool Contains(T value) do { if (c.Equals(node!.Value, value)) - { return node; - } node = node.Next; } while (node != First); } @@ -255,9 +256,7 @@ public bool Contains(T value) do { if (node!.Value == null) - { return node; - } node = node.Next; } while (node != First); } @@ -339,6 +338,7 @@ public LoopListNode Add(T value) Count++; return Last!; } + /// /// 前面增加节点 /// @@ -348,21 +348,17 @@ public LoopListNode Add(T value) public LoopListNode AddBefore(LoopListNode node, T value) { if (node == First) - { return AddFirst(value); - } - else - { - var tnode = new LoopListNode(value, this); - - node.Previous!.Next = tnode; - tnode.Previous = node.Previous; - node.Previous = tnode; - tnode.Next = node; - Count++; - return tnode; - } + + var tnode = new LoopListNode(value, this); + node.Previous!.Next = tnode; + tnode.Previous = node.Previous; + node.Previous = tnode; + tnode.Next = node; + Count++; + return tnode; } + /// /// 后面增加节点 /// @@ -372,7 +368,6 @@ public LoopListNode AddBefore(LoopListNode node, T value) public LoopListNode AddAfter(LoopListNode node, T value) { var tnode = new LoopListNode(value, this); - node.Next!.Previous = tnode; tnode.Next = node.Next; node.Next = tnode; -- Gitee From 18584e7507f6f515ac027e8d7cbde11087b92d4d Mon Sep 17 00:00:00 2001 From: liuqihong <540762622@qq.com> Date: Mon, 21 Mar 2022 02:37:34 +0800 Subject: [PATCH 049/675] =?UTF-8?q?=E4=BF=AE=E6=94=B9LoopList=E7=9A=84?= =?UTF-8?q?=E6=B8=85=E7=90=86=E9=94=99=E8=AF=AF?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/IFoxCAD.Basal/LoopList.cs | 62 +++++++++++++++++++++-------------- 1 file changed, 37 insertions(+), 25 deletions(-) diff --git a/src/IFoxCAD.Basal/LoopList.cs b/src/IFoxCAD.Basal/LoopList.cs index 40bd168..87a5962 100644 --- a/src/IFoxCAD.Basal/LoopList.cs +++ b/src/IFoxCAD.Basal/LoopList.cs @@ -22,7 +22,7 @@ public class LoopListNode public LoopListNode? Next { internal set; get; } /// - ///环链表序列 + /// 环链表序列 /// public LoopList? List { internal set; get; } @@ -51,7 +51,6 @@ public LoopListNode(T value, LoopList ts) /// internal void Invalidate() { - Value = default!; List = null; Next = null; Previous = null; @@ -158,17 +157,12 @@ public void Reverse() /// public void Clear() { - //清理的时候不释放数组长度 - ForEach(a => - { - //a.Value = default; - a.Invalidate(); - return false; - }); + //移除头部,表示链表再也无法遍历得到 + First = null; Count = 0; + GC.Collect(); } - /// /// 从头遍历 /// @@ -220,7 +214,7 @@ public bool Contains(T value) } /// - /// 查找节点 + /// 查找第一个出现的节点 /// /// /// @@ -433,33 +427,51 @@ public bool RemoveLast() /// /// 删除节点 /// - /// + /// 指定节点 /// public bool Remove(LoopListNode node) { if (!Contains(node)) return false; + InternalRemove(node); + return true; + } - if (Count == 1) + /// + /// 删除节点 + /// + /// 将移除所有含有此值 + /// + public bool Remove(T value) + { + ForEach(node => { - First = null; + if (node!.Value.Equals(value)) + InternalRemove(node); + return false; + }); + return true; + } + + /// + /// 删除节点_内部调用 + /// + /// 此值肯定存在当前链表 + /// + void InternalRemove(LoopListNode node) + { + if (Count == 1 || node == First) + { + RemoveFirst(); } else { - if (node == First) - { - RemoveFirst(); - } - else - { - node.Next!.Previous = node.Previous; - node.Previous!.Next = node.Next; - } + node.Next!.Previous = node.Previous; + node.Previous!.Next = node.Next; } - node.Invalidate(); + node.Invalidate(); Count--; - return true; } #endregion -- Gitee From 88e45b8de8579abb0bc073ff0e052ad7dc4507a1 Mon Sep 17 00:00:00 2001 From: liuqihong <540762622@qq.com> Date: Mon, 21 Mar 2022 02:38:25 +0800 Subject: [PATCH 050/675] =?UTF-8?q?=E4=BF=AE=E6=94=B9LoopList=E7=9A=84?= =?UTF-8?q?=E6=B8=85=E7=90=86=E9=94=99=E8=AF=AF?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/IFoxCAD.Basal/LoopList.cs | 62 +++++++++++++++++++++-------------- 1 file changed, 37 insertions(+), 25 deletions(-) diff --git a/src/IFoxCAD.Basal/LoopList.cs b/src/IFoxCAD.Basal/LoopList.cs index bac9911..0a6cbd1 100644 --- a/src/IFoxCAD.Basal/LoopList.cs +++ b/src/IFoxCAD.Basal/LoopList.cs @@ -27,7 +27,7 @@ public class LoopListNode public LoopListNode? Next { internal set; get; } /// - ///环链表序列 + /// 环链表序列 /// public LoopList? List { internal set; get; } @@ -56,7 +56,6 @@ public LoopListNode(T value, LoopList ts) /// internal void Invalidate() { - Value = default!; List = null; Next = null; Previous = null; @@ -163,17 +162,12 @@ public void Reverse() /// public void Clear() { - //清理的时候不释放数组长度 - ForEach(a => - { - //a.Value = default; - a.Invalidate(); - return false; - }); + //移除头部,表示链表再也无法遍历得到 + First = null; Count = 0; + GC.Collect(); } - /// /// 从头遍历 /// @@ -225,7 +219,7 @@ public bool Contains(T value) } /// - /// 查找节点 + /// 查找第一个出现的节点 /// /// /// @@ -438,33 +432,51 @@ public bool RemoveLast() /// /// 删除节点 /// - /// + /// 指定节点 /// public bool Remove(LoopListNode node) { if (!Contains(node)) return false; + InternalRemove(node); + return true; + } - if (Count == 1) + /// + /// 删除节点 + /// + /// 将移除所有含有此值 + /// + public bool Remove(T value) + { + ForEach(node => { - First = null; + if (node!.Value.Equals(value)) + InternalRemove(node); + return false; + }); + return true; + } + + /// + /// 删除节点_内部调用 + /// + /// 此值肯定存在当前链表 + /// + void InternalRemove(LoopListNode node) + { + if (Count == 1 || node == First) + { + RemoveFirst(); } else { - if (node == First) - { - RemoveFirst(); - } - else - { - node.Next!.Previous = node.Previous; - node.Previous!.Next = node.Next; - } + node.Next!.Previous = node.Previous; + node.Previous!.Next = node.Next; } - node.Invalidate(); + node.Invalidate(); Count--; - return true; } #endregion -- Gitee From 4c993b033faced7ef96159d448934b0239d57b2d Mon Sep 17 00:00:00 2001 From: liuqihong <540762622@qq.com> Date: Mon, 21 Mar 2022 02:40:37 +0800 Subject: [PATCH 051/675] =?UTF-8?q?=E5=B0=91=E4=BA=86=E4=B8=80=E4=B8=AA?= =?UTF-8?q?=E6=84=9F=E5=8F=B9=E5=8F=B7?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/IFoxCAD.Basal/LoopList.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/IFoxCAD.Basal/LoopList.cs b/src/IFoxCAD.Basal/LoopList.cs index 87a5962..d9a9bd4 100644 --- a/src/IFoxCAD.Basal/LoopList.cs +++ b/src/IFoxCAD.Basal/LoopList.cs @@ -446,7 +446,7 @@ public bool Remove(T value) { ForEach(node => { - if (node!.Value.Equals(value)) + if (node!.Value!.Equals(value)) InternalRemove(node); return false; }); -- Gitee From 5521d7cb5ba2a872c115e0dc7232fea1b1a9e44d Mon Sep 17 00:00:00 2001 From: liuqihong <540762622@qq.com> Date: Mon, 21 Mar 2022 02:40:56 +0800 Subject: [PATCH 052/675] =?UTF-8?q?=E5=B0=91=E4=BA=86=E4=B8=80=E4=B8=AA?= =?UTF-8?q?=E6=84=9F=E5=8F=B9=E5=8F=B7?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/IFoxCAD.Basal/LoopList.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/IFoxCAD.Basal/LoopList.cs b/src/IFoxCAD.Basal/LoopList.cs index 0a6cbd1..6504033 100644 --- a/src/IFoxCAD.Basal/LoopList.cs +++ b/src/IFoxCAD.Basal/LoopList.cs @@ -451,7 +451,7 @@ public bool Remove(T value) { ForEach(node => { - if (node!.Value.Equals(value)) + if (node!.Value!.Equals(value)) InternalRemove(node); return false; }); -- Gitee From 74c2eee904e2c87afdb3b028aa73490620bdd5f1 Mon Sep 17 00:00:00 2001 From: liuqihong <540762622@qq.com> Date: Mon, 21 Mar 2022 11:43:05 +0800 Subject: [PATCH 053/675] =?UTF-8?q?=E5=A2=9E=E5=8A=A0LoopList=E7=9A=84Find?= =?UTF-8?q?s?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/IFoxCAD.Basal/LoopList.cs | 95 ++++++++++++++++++++++++++++------- 1 file changed, 77 insertions(+), 18 deletions(-) diff --git a/src/IFoxCAD.Basal/LoopList.cs b/src/IFoxCAD.Basal/LoopList.cs index 6504033..e75eccd 100644 --- a/src/IFoxCAD.Basal/LoopList.cs +++ b/src/IFoxCAD.Basal/LoopList.cs @@ -11,6 +11,7 @@ namespace IFoxCAD.Collections /// public class LoopListNode { + #region 成员 /// /// 取值 /// @@ -30,7 +31,9 @@ public class LoopListNode /// 环链表序列 /// public LoopList? List { internal set; get; } + #endregion + #region 构造 /// /// 环链表节点构造函数 /// @@ -50,7 +53,9 @@ public LoopListNode(T value, LoopList ts) { return forward ? Next : Previous; } + #endregion + #region 方法 /// /// 无效化成员 /// @@ -60,6 +65,7 @@ internal void Invalidate() Next = null; Previous = null; } + #endregion } /// @@ -184,7 +190,6 @@ public void ForEach(Func, bool> action) node = node!.Next; } } - #endregion #region Contains @@ -238,7 +243,7 @@ public bool Contains(T value) //return result; LoopListNode? node = First; - EqualityComparer c = EqualityComparer.Default; + var c = EqualityComparer.Default; if (node != null) { if (value != null) @@ -263,6 +268,40 @@ public bool Contains(T value) return null; } + /// + /// 查找所有出现的节点 + /// + /// + /// + public IEnumerable>? Finds(T value) + { + LoopListNode? node = First; + if (node == null) + return null; + + List> result = new(); + var c = EqualityComparer.Default; + if (value != null) + { + do + { + if (c.Equals(node!.Value, value)) + result.Add(node); + node = node.Next; + } while (node != First); + } + else + { + do + { + if (node!.Value == null) + result.Add(node); + node = node.Next; + } while (node != First); + } + return result; + } + /// /// 获取节点 /// @@ -449,12 +488,13 @@ public bool Remove(LoopListNode node) /// public bool Remove(T value) { - ForEach(node => - { - if (node!.Value!.Equals(value)) - InternalRemove(node); + var lst = Finds(value); + if (lst == null) return false; - }); + + var ge = lst!.GetEnumerator(); + while (ge.MoveNext()) + InternalRemove(ge.Current); return true; } @@ -553,6 +593,8 @@ public void LinkTo(LoopListNode from, LoopListNode to, int number, bool is #endregion + #endregion + #region IEnumerable 成员 /// @@ -610,25 +652,42 @@ public IEnumerator GetEnumerator() #region IFormattable 成员 /// - /// 转换为字符串 + /// 转换为字符串_格式化实现 /// + /// + /// /// - public override string ToString() + string IFormattable.ToString(string? format, IFormatProvider? formatProvider) { - var s = new StringBuilder(); - s.Append("( "); - foreach (T value in this) - s.Append($"{value} "); - - s.Append(")"); - return s.ToString(); + return ToString(format, formatProvider); } - string IFormattable.ToString(string format, IFormatProvider formatProvider) + /// + /// 转换为字符串_无参调用 + /// + /// + public override string ToString() { - return ToString(); + return ToString(null, null); } + /// + /// 转换为字符串_有参调用 + /// + /// + public string ToString(string? format, IFormatProvider? formatProvider = null) + { + var s = new StringBuilder(); + s.Append($"Count = {Count};"); + if (format == null) + { + s.Append("{ "); + foreach (T value in this) + s.Append($"{value} "); + s.Append(" }"); + } + return s.ToString(); + } #endregion } } \ No newline at end of file -- Gitee From d77a81eff7915c1b864873a3e89cccacc5beae76 Mon Sep 17 00:00:00 2001 From: liuqihong <540762622@qq.com> Date: Mon, 21 Mar 2022 11:44:03 +0800 Subject: [PATCH 054/675] =?UTF-8?q?=E5=A2=9E=E5=8A=A0LoopList=E7=9A=84Find?= =?UTF-8?q?s?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/IFoxCAD.Basal/LoopList.cs | 102 +++++++++++++++++++++++++++------- 1 file changed, 83 insertions(+), 19 deletions(-) diff --git a/src/IFoxCAD.Basal/LoopList.cs b/src/IFoxCAD.Basal/LoopList.cs index d9a9bd4..e75eccd 100644 --- a/src/IFoxCAD.Basal/LoopList.cs +++ b/src/IFoxCAD.Basal/LoopList.cs @@ -1,4 +1,9 @@ -namespace IFoxCAD.Basal +using System; +using System.Collections; +using System.Collections.Generic; +using System.Text; + +namespace IFoxCAD.Collections { /// /// 环链表节点 @@ -6,6 +11,7 @@ /// public class LoopListNode { + #region 成员 /// /// 取值 /// @@ -25,7 +31,9 @@ public class LoopListNode /// 环链表序列 /// public LoopList? List { internal set; get; } + #endregion + #region 构造 /// /// 环链表节点构造函数 /// @@ -45,7 +53,9 @@ public LoopListNode(T value, LoopList ts) { return forward ? Next : Previous; } + #endregion + #region 方法 /// /// 无效化成员 /// @@ -55,6 +65,7 @@ internal void Invalidate() Next = null; Previous = null; } + #endregion } /// @@ -179,7 +190,6 @@ public void ForEach(Func, bool> action) node = node!.Next; } } - #endregion #region Contains @@ -233,7 +243,7 @@ public bool Contains(T value) //return result; LoopListNode? node = First; - EqualityComparer c = EqualityComparer.Default; + var c = EqualityComparer.Default; if (node != null) { if (value != null) @@ -258,6 +268,40 @@ public bool Contains(T value) return null; } + /// + /// 查找所有出现的节点 + /// + /// + /// + public IEnumerable>? Finds(T value) + { + LoopListNode? node = First; + if (node == null) + return null; + + List> result = new(); + var c = EqualityComparer.Default; + if (value != null) + { + do + { + if (c.Equals(node!.Value, value)) + result.Add(node); + node = node.Next; + } while (node != First); + } + else + { + do + { + if (node!.Value == null) + result.Add(node); + node = node.Next; + } while (node != First); + } + return result; + } + /// /// 获取节点 /// @@ -444,12 +488,13 @@ public bool Remove(LoopListNode node) /// public bool Remove(T value) { - ForEach(node => - { - if (node!.Value!.Equals(value)) - InternalRemove(node); + var lst = Finds(value); + if (lst == null) return false; - }); + + var ge = lst!.GetEnumerator(); + while (ge.MoveNext()) + InternalRemove(ge.Current); return true; } @@ -548,6 +593,8 @@ public void LinkTo(LoopListNode from, LoopListNode to, int number, bool is #endregion + #endregion + #region IEnumerable 成员 /// @@ -605,25 +652,42 @@ public IEnumerator GetEnumerator() #region IFormattable 成员 /// - /// 转换为字符串 + /// 转换为字符串_格式化实现 /// + /// + /// /// - public override string ToString() + string IFormattable.ToString(string? format, IFormatProvider? formatProvider) { - var s = new StringBuilder(); - s.Append("( "); - foreach (T value in this) - s.Append($"{value} "); - - s.Append(")"); - return s.ToString(); + return ToString(format, formatProvider); } - string IFormattable.ToString(string format, IFormatProvider formatProvider) + /// + /// 转换为字符串_无参调用 + /// + /// + public override string ToString() { - return ToString(); + return ToString(null, null); } + /// + /// 转换为字符串_有参调用 + /// + /// + public string ToString(string? format, IFormatProvider? formatProvider = null) + { + var s = new StringBuilder(); + s.Append($"Count = {Count};"); + if (format == null) + { + s.Append("{ "); + foreach (T value in this) + s.Append($"{value} "); + s.Append(" }"); + } + return s.ToString(); + } #endregion } } \ No newline at end of file -- Gitee From bf3e0eee3c402397dc7cb0c7dd885a746cb44978 Mon Sep 17 00:00:00 2001 From: liuqihong <540762622@qq.com> Date: Mon, 21 Mar 2022 11:47:12 +0800 Subject: [PATCH 055/675] =?UTF-8?q?=E4=BF=AE=E6=94=B9=E5=91=BD=E5=90=8D?= =?UTF-8?q?=E7=A9=BA=E9=97=B4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/IFoxCAD.Basal/LoopList.cs | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/src/IFoxCAD.Basal/LoopList.cs b/src/IFoxCAD.Basal/LoopList.cs index e75eccd..3ef67cf 100644 --- a/src/IFoxCAD.Basal/LoopList.cs +++ b/src/IFoxCAD.Basal/LoopList.cs @@ -1,9 +1,4 @@ -using System; -using System.Collections; -using System.Collections.Generic; -using System.Text; - -namespace IFoxCAD.Collections +namespace IFoxCAD.Basal { /// /// 环链表节点 -- Gitee From 9a9e986a22bfc0b2049df3b0598cdb4fcfd263bd Mon Sep 17 00:00:00 2001 From: vicwjb Date: Fri, 25 Mar 2022 10:21:56 +0800 Subject: [PATCH 056/675] =?UTF-8?q?=E4=BC=98=E5=8C=96=E4=BB=A3=E7=A0=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/IFoxCAD.Basal/LoopList.cs | 1117 ++++++++++++------------ src/IFoxCAD.Cad/IFoxCAD.Cad.csproj | 1 + src/IFoxCAD.Cad/Runtime/SymbolTable.cs | 24 +- tests/Test/Test.cs | 23 +- tests/Test/Test.csproj | 1 + tests/Test/TestLoop.cs | 37 + tests/Test/testblock.cs | 11 + 7 files changed, 646 insertions(+), 568 deletions(-) create mode 100644 tests/Test/TestLoop.cs diff --git a/src/IFoxCAD.Basal/LoopList.cs b/src/IFoxCAD.Basal/LoopList.cs index 3ef67cf..b83f6ad 100644 --- a/src/IFoxCAD.Basal/LoopList.cs +++ b/src/IFoxCAD.Basal/LoopList.cs @@ -1,287 +1,252 @@ -namespace IFoxCAD.Basal +namespace IFoxCAD.Basal; + +/// +/// 环链表节点 +/// +/// +public class LoopListNode { + #region 成员 /// - /// 环链表节点 - /// - /// - public class LoopListNode - { - #region 成员 - /// - /// 取值 - /// - public T Value; - - /// - /// 上一个节点 - /// - public LoopListNode? Previous { internal set; get; } - - /// - /// 下一个节点 - /// - public LoopListNode? Next { internal set; get; } - - /// - /// 环链表序列 - /// - public LoopList? List { internal set; get; } - #endregion - - #region 构造 - /// - /// 环链表节点构造函数 - /// - /// 节点值 - public LoopListNode(T value, LoopList ts) - { - Value = value; - List = ts; - } + /// 取值 + /// + public T Value; - /// - /// 获取当前节点的临近节点 - /// - /// 搜索方向标志,为向前搜索,为向后搜索 - /// - public LoopListNode? GetNext(bool forward) - { - return forward ? Next : Previous; - } - #endregion + /// + /// 上一个节点 + /// + public LoopListNode? Previous { internal set; get; } - #region 方法 - /// - /// 无效化成员 - /// - internal void Invalidate() - { - List = null; - Next = null; - Previous = null; - } - #endregion + /// + /// 下一个节点 + /// + public LoopListNode? Next { internal set; get; } + + /// + /// 环链表序列 + /// + public LoopList? List { internal set; get; } + #endregion + + #region 构造 + /// + /// 环链表节点构造函数 + /// + /// 节点值 + public LoopListNode(T value, LoopList ts) + { + Value = value; + List = ts; } /// - /// 环链表 + /// 获取当前节点的临近节点 /// - /// - public class LoopList : IEnumerable, IFormattable + /// 搜索方向标志,为向前搜索,为向后搜索 + /// + public LoopListNode? GetNext(bool forward) { - #region 成员 + return forward ? Next : Previous; + } + #endregion - /// - /// 节点数 - /// - public int Count { get; private set; } + #region 方法 + /// + /// 无效化成员 + /// + internal void Invalidate() + { + List = null; + Next = null; + Previous = null; + } + #endregion +} + +/// +/// 环链表 +/// +/// +public class LoopList : IEnumerable, IFormattable +{ + #region 成员 - /// - /// 首节点 - /// - public LoopListNode? First { get; private set; } + /// + /// 节点数 + /// + public int Count { get; private set; } - /// - /// 尾节点 - /// - public LoopListNode? Last => First?.Previous; + /// + /// 首节点 + /// + public LoopListNode? First { get; private set; } - #endregion + /// + /// 尾节点 + /// + public LoopListNode? Last => First?.Previous; - #region 构造 + #endregion - /// - /// 默认构造函数 - /// - public LoopList() { } + #region 构造 - /// - /// 环链表构造函数 - /// - /// 节点迭代器 - public LoopList(IEnumerable values) - { - var ge = values.GetEnumerator(); - while (ge.MoveNext()) - Add(ge.Current); - } + /// + /// 默认构造函数 + /// + public LoopList() { } - #endregion + /// + /// 环链表构造函数 + /// + /// 节点迭代器 + public LoopList(IEnumerable values) + { + var ge = values.GetEnumerator(); + while (ge.MoveNext()) + Add(ge.Current); + } - #region 方法 + #endregion - /// - /// 设置首节点 - /// - /// 节点 - /// - public bool SetFirst(LoopListNode node) - { - if (!Contains(node)) - return false; + #region 方法 - First = node; - return true; - } + /// + /// 设置首节点 + /// + /// 节点 + /// + public bool SetFirst(LoopListNode node) + { + if (!Contains(node)) + return false; - /// - /// 交换两个节点的值 - /// - /// 第一个节点 - /// 第二个节点 - public void Swap(LoopListNode node1, LoopListNode node2) - { + First = node; + return true; + } + + /// + /// 交换两个节点的值 + /// + /// 第一个节点 + /// 第二个节点 + public void Swap(LoopListNode node1, LoopListNode node2) + { #if NET35 - var value = node1.Value; - node1.Value = node2.Value; - node2.Value = value; + var value = node1.Value; + node1.Value = node2.Value; + node2.Value = value; #else - (node2.Value, node1.Value) = (node1.Value, node2.Value); + (node2.Value, node1.Value) = (node1.Value, node2.Value); #endif - } - - /// - /// 链内翻转 - /// - public void Reverse() - { - var first = First; - if (first == null) - return; - - var last = Last; - for (int i = 0; i < Count / 2; i++) - { - Swap(first!, last!); - first = first!.Next; - last = last!.Previous; - } - } + } - /// - /// 清理 - /// - public void Clear() - { - //移除头部,表示链表再也无法遍历得到 - First = null; - Count = 0; - GC.Collect(); - } + /// + /// 链内翻转 + /// + public void Reverse() + { + var first = First; + if (first == null) + return; - /// - /// 从头遍历 - /// - /// - public void ForEach(Func, bool> action) + var last = Last; + for (int i = 0; i < Count / 2; i++) { - var node = First; - if (node == null) - return; - for (int i = 0; i < Count; i++) - { - if (action(node!)) - break; - node = node!.Next; - } + Swap(first!, last!); + first = first!.Next; + last = last!.Previous; } + } - #region Contains + /// + /// 清理 + /// + public void Clear() + { + //移除头部,表示链表再也无法遍历得到 + First = null; + Count = 0; + + } - /// - /// 是否包含节点 - /// - /// - /// - public bool Contains(LoopListNode node) + /// + /// 从头遍历 + /// + /// + public void ForEach(Func, bool> action) + { + var node = First; + if (node == null) + return; + for (int i = 0; i < Count; i++) { - return node != null && node.List == this; + if (action(node!)) + break; + node = node!.Next; } + } - /// - /// 是否包含值 - /// - /// - /// - public bool Contains(T value) - { - bool result = false; - ForEach(node => - { - if (node.Value!.Equals(value)) - { - result = true; - return true; - } - return false; - }); - return result; - } + #region Contains + + /// + /// 是否包含节点 + /// + /// + /// + public bool Contains(LoopListNode node) + { + return node != null && node.List == this; + } - /// - /// 查找第一个出现的节点 - /// - /// - /// - public LoopListNode? Find(T value) + /// + /// 是否包含值 + /// + /// + /// + public bool Contains(T value) + { + bool result = false; + ForEach(node => { - //LoopListNode result = null; - //ForEach(node => - //{ - // if (node.Value.Equals(t2)) - // { - // result = node; - // return true; - // } - // return false; - //}); - //return result; - - LoopListNode? node = First; - var c = EqualityComparer.Default; - if (node != null) + if (node.Value!.Equals(value)) { - if (value != null) - { - do - { - if (c.Equals(node!.Value, value)) - return node; - node = node.Next; - } while (node != First); - } - else - { - do - { - if (node!.Value == null) - return node; - node = node.Next; - } while (node != First); - } + result = true; + return true; } - return null; - } + return false; + }); + return result; + } - /// - /// 查找所有出现的节点 - /// - /// - /// - public IEnumerable>? Finds(T value) + /// + /// 查找第一个出现的节点 + /// + /// + /// + public LoopListNode? Find(T value) + { + //LoopListNode result = null; + //ForEach(node => + //{ + // if (node.Value.Equals(t2)) + // { + // result = node; + // return true; + // } + // return false; + //}); + //return result; + + LoopListNode? node = First; + var c = EqualityComparer.Default; + if (node != null) { - LoopListNode? node = First; - if (node == null) - return null; - - List> result = new(); - var c = EqualityComparer.Default; if (value != null) { do { if (c.Equals(node!.Value, value)) - result.Add(node); + return node; node = node.Next; } while (node != First); } @@ -290,399 +255,433 @@ public bool Contains(T value) do { if (node!.Value == null) - result.Add(node); + return node; node = node.Next; } while (node != First); } - return result; } + return null; + } + + /// + /// 查找所有出现的节点 + /// + /// + /// + public IEnumerable>? Finds(T value) + { + LoopListNode? node = First; + if (node == null) + return null; - /// - /// 获取节点 - /// - /// - /// - public LoopListNode? GetNode(Func func) + List> result = new(); + var c = EqualityComparer.Default; + if (value != null) { - LoopListNode? result = null; - ForEach(node => + do { - if (func(node.Value)) - { - result = node; - return true; - } - return false; - }); - return result; + if (c.Equals(node!.Value, value)) + result.Add(node); + node = node.Next; + } while (node != First); } - - #endregion - - #region Add - - /// - /// 在首节点之前插入节点,并设置新节点为首节点 - /// - /// - /// - public LoopListNode AddFirst(T value) + else { - var node = new LoopListNode(value, this); - - if (Count == 0) + do { - First = node; - First.Previous = First.Next = node; - } - else - { - LoopListNode last = Last!; - First!.Previous = last.Next = node; - node.Next = First; - node.Previous = last; - First = node; - } - Count++; - return First; + if (node!.Value == null) + result.Add(node); + node = node.Next; + } while (node != First); } + return result; + } - /// - /// 在尾节点之后插入节点,并设置新节点为尾节点 - /// - /// - /// - public LoopListNode Add(T value) + /// + /// 获取节点 + /// + /// + /// + public LoopListNode? GetNode(Func func) + { + LoopListNode? result = null; + ForEach(node => { - var node = new LoopListNode(value, this); - - if (Count == 0) + if (func(node.Value)) { - First = node; - First.Previous = First.Next = node; + result = node; + return true; } - else - { - var last = First!.Previous!; - First.Previous = last.Next = node; - node.Next = First; - node.Previous = last; - } - Count++; - return Last!; - } + return false; + }); + return result; + } + + #endregion + + #region Add - /// - /// 前面增加节点 - /// - /// - /// - /// - public LoopListNode AddBefore(LoopListNode node, T value) + /// + /// 在首节点之前插入节点,并设置新节点为首节点 + /// + /// + /// + public LoopListNode AddFirst(T value) + { + var node = new LoopListNode(value, this); + + if (Count == 0) { - if (node == First) - return AddFirst(value); - - var tnode = new LoopListNode(value, this); - node.Previous!.Next = tnode; - tnode.Previous = node.Previous; - node.Previous = tnode; - tnode.Next = node; - Count++; - return tnode; + First = node; + First.Previous = First.Next = node; } - - /// - /// 后面增加节点 - /// - /// - /// - /// - public LoopListNode AddAfter(LoopListNode node, T value) + else { - var tnode = new LoopListNode(value, this); - node.Next!.Previous = tnode; - tnode.Next = node.Next; - node.Next = tnode; - tnode.Previous = node; - Count++; - return tnode; + LoopListNode last = Last!; + First!.Previous = last.Next = node; + node.Next = First; + node.Previous = last; + First = node; } + Count++; + return First; + } - #endregion - - #region Remove + /// + /// 在尾节点之后插入节点,并设置新节点为尾节点 + /// + /// + /// + public LoopListNode Add(T value) + { + var node = new LoopListNode(value, this); - /// - /// 删除首节点 - /// - /// - public bool RemoveFirst() + if (Count == 0) { - switch (Count) - { - case 0: - return false; - - case 1: - First = null; - break; - - default: - LoopListNode last = Last!; - First = First!.Next; - First!.Previous = last; - last.Next = First; - break; - } - return true; + First = node; + First.Previous = First.Next = node; } - - /// - /// 删除尾节点 - /// - /// - public bool RemoveLast() + else { - switch (Count) - { - case 0: - return false; - - case 1: - First = null; - break; - - default: - LoopListNode last = Last!.Previous!; - last.Next = First; - First!.Previous = last; - break; - } - Count--; - return true; + var last = First!.Previous!; + First.Previous = last.Next = node; + node.Next = First; + node.Previous = last; } + Count++; + return Last!; + } - /// - /// 删除节点 - /// - /// 指定节点 - /// - public bool Remove(LoopListNode node) + /// + /// 前面增加节点 + /// + /// + /// + /// + public LoopListNode AddBefore(LoopListNode node, T value) + { + if (node == First) + return AddFirst(value); + + var tnode = new LoopListNode(value, this); + node.Previous!.Next = tnode; + tnode.Previous = node.Previous; + node.Previous = tnode; + tnode.Next = node; + Count++; + return tnode; + } + + /// + /// 后面增加节点 + /// + /// + /// + /// + public LoopListNode AddAfter(LoopListNode node, T value) + { + var tnode = new LoopListNode(value, this); + node.Next!.Previous = tnode; + tnode.Next = node.Next; + node.Next = tnode; + tnode.Previous = node; + Count++; + return tnode; + } + + #endregion + + #region Remove + + /// + /// 删除首节点 + /// + /// + public bool RemoveFirst() + { + switch (Count) { - if (!Contains(node)) + case 0: return false; - InternalRemove(node); - return true; + + case 1: + First = null; + break; + + default: + LoopListNode last = Last!; + First = First!.Next; + First!.Previous = last; + last.Next = First; + break; } + return true; + } - /// - /// 删除节点 - /// - /// 将移除所有含有此值 - /// - public bool Remove(T value) + /// + /// 删除尾节点 + /// + /// + public bool RemoveLast() + { + switch (Count) { - var lst = Finds(value); - if (lst == null) + case 0: return false; - var ge = lst!.GetEnumerator(); - while (ge.MoveNext()) - InternalRemove(ge.Current); - return true; + case 1: + First = null; + break; + + default: + LoopListNode last = Last!.Previous!; + last.Next = First; + First!.Previous = last; + break; } + Count--; + return true; + } - /// - /// 删除节点_内部调用 - /// - /// 此值肯定存在当前链表 - /// - void InternalRemove(LoopListNode node) - { - if (Count == 1 || node == First) - { - RemoveFirst(); - } - else - { - node.Next!.Previous = node.Previous; - node.Previous!.Next = node.Next; - } + /// + /// 删除节点 + /// + /// 指定节点 + /// + public bool Remove(LoopListNode node) + { + if (!Contains(node)) + return false; + InternalRemove(node); + return true; + } + + /// + /// 删除节点 + /// + /// 将移除所有含有此值 + /// + public bool Remove(T value) + { + var lst = Finds(value); + if (lst == null) + return false; + + var ge = lst!.GetEnumerator(); + while (ge.MoveNext()) + InternalRemove(ge.Current); + return true; + } - node.Invalidate(); - Count--; + /// + /// 删除节点_内部调用 + /// + /// 此值肯定存在当前链表 + /// + void InternalRemove(LoopListNode node) + { + if (Count == 1 || node == First) + { + RemoveFirst(); } + else + { + node.Next!.Previous = node.Previous; + node.Previous!.Next = node.Next; + } + + node.Invalidate(); + Count--; + } - #endregion + #endregion - #region LinkTo + #region LinkTo - /// - /// 链接两节点,并去除这两个节点间的所有节点 - /// - /// - /// - public void LinkTo(LoopListNode from, LoopListNode to) + /// + /// 链接两节点,并去除这两个节点间的所有节点 + /// + /// + /// + public void LinkTo(LoopListNode from, LoopListNode to) + { + if (from != to && Contains(from) && Contains(to)) { - if (from != to && Contains(from) && Contains(to)) - { - LoopListNode node = from.Next!; - bool isFirstChanged = false; - int number = 0; + LoopListNode node = from.Next!; + bool isFirstChanged = false; + int number = 0; - while (node != to) - { - if (node == First) - isFirstChanged = true; + while (node != to) + { + if (node == First) + isFirstChanged = true; - node = node.Next!; - number++; - } + node = node.Next!; + number++; + } - from.Next = to; - to.Previous = from; + from.Next = to; + to.Previous = from; - if (number > 0 && isFirstChanged) - First = to; + if (number > 0 && isFirstChanged) + First = to; - Count -= number; - } + Count -= number; } + } - /// - /// 链接两节点,并去除这两个节点间的所有节点 - /// - /// - /// - /// - public void LinkTo(LoopListNode from, LoopListNode to, int number) + /// + /// 链接两节点,并去除这两个节点间的所有节点 + /// + /// + /// + /// + public void LinkTo(LoopListNode from, LoopListNode to, int number) + { + if (from != to && Contains(from) && Contains(to)) { - if (from != to && Contains(from) && Contains(to)) - { - from.Next = to; - to.Previous = from; - First = to; - Count -= number; - } + from.Next = to; + to.Previous = from; + First = to; + Count -= number; } + } - /// - /// 链接两节点,并去除这两个节点间的所有节点 - /// - /// - /// - /// - /// - public void LinkTo(LoopListNode from, LoopListNode to, int number, bool isFirstChanged) + /// + /// 链接两节点,并去除这两个节点间的所有节点 + /// + /// + /// + /// + /// + public void LinkTo(LoopListNode from, LoopListNode to, int number, bool isFirstChanged) + { + if (from != to && Contains(from) && Contains(to)) { - if (from != to && Contains(from) && Contains(to)) - { - from.Next = to; - to.Previous = from; - if (isFirstChanged) - First = to; - Count -= number; - } + from.Next = to; + to.Previous = from; + if (isFirstChanged) + First = to; + Count -= number; } + } - #endregion + #endregion - #endregion + #endregion - #region IEnumerable 成员 + #region IEnumerable 成员 - /// - /// 获取节点的查询器 - /// - /// - /// - public IEnumerable> GetNodes(LoopListNode from) + /// + /// 获取节点的查询器 + /// + /// + /// + public IEnumerable> GetNodes(LoopListNode from) + { + var node = from; + for (int i = 0; i < Count; i++) { - var node = from; - for (int i = 0; i < Count; i++) - { - yield return node!; - node = node!.Next; - } + yield return node!; + node = node!.Next; } + } - /// - /// 获取节点的查询器 - /// - /// - public IEnumerable> GetNodes() + /// + /// 获取节点的查询器 + /// + /// + public IEnumerable> GetNodes() + { + LoopListNode node = First!; + for (int i = 0; i < Count; i++) { - LoopListNode node = First!; - for (int i = 0; i < Count; i++) - { - yield return node!; - node = node.Next!; - } + yield return node!; + node = node.Next!; } + } - /// - /// 获取节点值的查询器 - /// - /// - public IEnumerator GetEnumerator() + /// + /// 获取节点值的查询器 + /// + /// + public IEnumerator GetEnumerator() + { + LoopListNode node = First!; + for (int i = 0; i < Count; i++) { - LoopListNode node = First!; - for (int i = 0; i < Count; i++) - { - yield return node!.Value; - node = node.Next!; - } + yield return node!.Value; + node = node.Next!; } + } - IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); + IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); - #region IEnumerable 成员 + #region IEnumerable 成员 - IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); + IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); - #endregion IEnumerable 成员 + #endregion IEnumerable 成员 - #endregion + #endregion - #region IFormattable 成员 - /// - /// 转换为字符串_格式化实现 - /// - /// - /// - /// - string IFormattable.ToString(string? format, IFormatProvider? formatProvider) - { - return ToString(format, formatProvider); - } + #region IFormattable 成员 + /// + /// 转换为字符串_格式化实现 + /// + /// + /// + /// + string IFormattable.ToString(string? format, IFormatProvider? formatProvider) + { + return ToString(format, formatProvider); + } - /// - /// 转换为字符串_无参调用 - /// - /// - public override string ToString() - { - return ToString(null, null); - } + /// + /// 转换为字符串_无参调用 + /// + /// + public override string ToString() + { + return ToString(null, null); + } - /// - /// 转换为字符串_有参调用 - /// - /// - public string ToString(string? format, IFormatProvider? formatProvider = null) + /// + /// 转换为字符串_有参调用 + /// + /// + public string ToString(string? format, IFormatProvider? formatProvider = null) + { + var s = new StringBuilder(); + s.Append($"Count = {Count};"); + if (format == null) { - var s = new StringBuilder(); - s.Append($"Count = {Count};"); - if (format == null) - { - s.Append("{ "); - foreach (T value in this) - s.Append($"{value} "); - s.Append(" }"); - } - return s.ToString(); + s.Append("{ "); + foreach (T value in this) + s.Append($"{value} "); + s.Append(" }"); } - #endregion + return s.ToString(); } -} \ No newline at end of file + #endregion +} diff --git a/src/IFoxCAD.Cad/IFoxCAD.Cad.csproj b/src/IFoxCAD.Cad/IFoxCAD.Cad.csproj index a7ab56a..0697e44 100644 --- a/src/IFoxCAD.Cad/IFoxCAD.Cad.csproj +++ b/src/IFoxCAD.Cad/IFoxCAD.Cad.csproj @@ -20,6 +20,7 @@ true LICENSE true + x64 diff --git a/src/IFoxCAD.Cad/Runtime/SymbolTable.cs b/src/IFoxCAD.Cad/Runtime/SymbolTable.cs index ffd4eca..15d74eb 100644 --- a/src/IFoxCAD.Cad/Runtime/SymbolTable.cs +++ b/src/IFoxCAD.Cad/Runtime/SymbolTable.cs @@ -210,7 +210,7 @@ public void Change(ObjectId id, Action action) /// 符号表记录的id /// 打开模式,默认为只读 /// 符号表记录 - public TRecord? GetRecord(ObjectId id, OpenMode openMode = OpenMode.ForRead) => id.IsNull ? null : DTrans.GetObject(id, openMode); + public TRecord? GetRecord(ObjectId id, OpenMode openMode = OpenMode.ForRead) => /*id.IsNull ? null : */DTrans.GetObject(id, openMode); /// /// 获取符号表记录 @@ -302,6 +302,28 @@ internal ObjectId GetRecordFrom(Func> tabl } #endregion + #region 遍历 + /// + /// 遍历集合的迭代器,执行action委托 + /// + /// 集合值的类型 + /// 集合 + /// 要运行的委托 + public void ForEach(Action action) + { + //GetRecords().ForEach(re => action(re)); + + foreach (var item in this) + { + var record = GetRecord(item); + if (record is not null) + { + action(record); + } + } + } + #endregion + #region IEnumerable 成员 public IEnumerator GetEnumerator() diff --git a/tests/Test/Test.cs b/tests/Test/Test.cs index ea9f8e6..eac5da4 100644 --- a/tests/Test/Test.cs +++ b/tests/Test/Test.cs @@ -223,14 +223,21 @@ public void GetXdata() { var doc = Application.DocumentManager.MdiActiveDocument; var ed = doc.Editor; - - var res = ed.GetEntity("\n select the entity:"); - if (res.Status == PromptStatus.OK) - { - using var tr = new DBTrans(); - var data = tr.GetObject(res.ObjectId).XData; - ed.WriteMessage(data.ToString()); - } + using var tr = new DBTrans(); + tr.RegAppTable.ForEach(id => + id.GetObject().Name.Print()); + tr.RegAppTable.GetRecords().ForEach(rec => rec.Name.Print()); + tr.RegAppTable.GetRecordNames().ForEach(name => name.Print()); + tr.RegAppTable.ForEach(re => re.Name.Print()); + + //var res = ed.GetEntity("\n select the entity:"); + //if (res.Status == PromptStatus.OK) + //{ + // using var tr = new DBTrans(); + // tr.RegAppTable.ForEach(id => id.GetObject().Print()); + // var data = tr.GetObject(res.ObjectId).XData; + // ed.WriteMessage(data.ToString()); + //} } [CommandMethod("changexdata")] diff --git a/tests/Test/Test.csproj b/tests/Test/Test.csproj index a6f4773..6057504 100644 --- a/tests/Test/Test.csproj +++ b/tests/Test/Test.csproj @@ -5,6 +5,7 @@ true true preview + x64 diff --git a/tests/Test/TestLoop.cs b/tests/Test/TestLoop.cs new file mode 100644 index 0000000..bfd1007 --- /dev/null +++ b/tests/Test/TestLoop.cs @@ -0,0 +1,37 @@ +using IFoxCAD.Basal; + +using System.Diagnostics.CodeAnalysis; +namespace Test +{ + public class TestLoop + { + + + + + [CommandMethod("testloop")] + public void Testloop() + { + for (int i = 0; i < 10000000; i++) + { + var loop = new LoopList(); + for (int j = 0; j < 100000; j++) + { + loop.Add(j); + } + //loop.Add(1); + //loop.Add(2); + //loop.Add(3); + //loop.Add(4); + + //loop.Add(5); + + + loop.Clear(); + + } + + + } + } +} diff --git a/tests/Test/testblock.cs b/tests/Test/testblock.cs index 0a9a79f..6349384 100644 --- a/tests/Test/testblock.cs +++ b/tests/Test/testblock.cs @@ -55,6 +55,9 @@ public void BlockDefChange() //}); + + + tr.BlockTable.Change("test", btr => { foreach (var id in btr) @@ -67,6 +70,14 @@ public void BlockDefChange() dBText.DimensionText = "234"; dBText.RecomputeDimensionBlock(true); } + + if (ent is Hatch hatch) + { + hatch.ColorIndex = 0; + + } + + } } -- Gitee From 801bd6a057a661551637c64d4edbd06ad2ef07f8 Mon Sep 17 00:00:00 2001 From: liuqihong <540762622@qq.com> Date: Sat, 26 Mar 2022 16:50:46 +0800 Subject: [PATCH 057/675] =?UTF-8?q?=E4=BF=AE=E6=94=B9=3D=3Dnull=E4=B8=BAis?= =?UTF-8?q?Null?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/IFoxCAD.Basal/LoopList.cs | 22 +++++++++---------- src/IFoxCAD.Cad/ExtensionMethod/CurveEx.cs | 18 +++++++-------- .../ExtensionMethod/DBDictionaryEx.cs | 2 +- src/IFoxCAD.Cad/ExtensionMethod/EditorEx.cs | 14 ++++++------ src/IFoxCAD.Cad/ExtensionMethod/EntityEx.cs | 6 ++--- src/IFoxCAD.Cad/ExtensionMethod/Enums.cs | 2 +- src/IFoxCAD.Cad/ExtensionMethod/GeometryEx.cs | 4 ++-- .../ExtensionMethod/SymbolTableEx.cs | 8 +++---- .../ExtensionMethod/SymbolTableRecordEx.cs | 4 ++-- src/IFoxCAD.Cad/Runtime/AutoRegAssem.cs | 2 +- src/IFoxCAD.Cad/Runtime/SymbolTable.cs | 8 +++---- src/IFoxCAD.WPF/DependencyObjectExtensions.cs | 6 ++--- src/IFoxCAD.WPF/EventBindingExtension.cs | 22 +++++++++---------- src/IFoxCAD.WPF/RelayCommand.cs | 18 +++++++-------- tests/Test/testFileDatabase.cs | 2 +- tests/Test/testblock.cs | 2 +- 16 files changed, 70 insertions(+), 70 deletions(-) diff --git a/src/IFoxCAD.Basal/LoopList.cs b/src/IFoxCAD.Basal/LoopList.cs index b83f6ad..c3bde54 100644 --- a/src/IFoxCAD.Basal/LoopList.cs +++ b/src/IFoxCAD.Basal/LoopList.cs @@ -146,7 +146,7 @@ public void Swap(LoopListNode node1, LoopListNode node2) public void Reverse() { var first = First; - if (first == null) + if (first is null) return; var last = Last; @@ -176,7 +176,7 @@ public void Clear() public void ForEach(Func, bool> action) { var node = First; - if (node == null) + if (node is null) return; for (int i = 0; i < Count; i++) { @@ -195,7 +195,7 @@ public void ForEach(Func, bool> action) /// public bool Contains(LoopListNode node) { - return node != null && node.List == this; + return node is not null && node.List == this; } /// @@ -239,9 +239,9 @@ public bool Contains(T value) LoopListNode? node = First; var c = EqualityComparer.Default; - if (node != null) + if (node is not null) { - if (value != null) + if (value is not null) { do { @@ -254,7 +254,7 @@ public bool Contains(T value) { do { - if (node!.Value == null) + if (node!.Value is null) return node; node = node.Next; } while (node != First); @@ -271,12 +271,12 @@ public bool Contains(T value) public IEnumerable>? Finds(T value) { LoopListNode? node = First; - if (node == null) + if (node is null) return null; List> result = new(); var c = EqualityComparer.Default; - if (value != null) + if (value is not null) { do { @@ -289,7 +289,7 @@ public bool Contains(T value) { do { - if (node!.Value == null) + if (node!.Value is null) result.Add(node); node = node.Next; } while (node != First); @@ -484,7 +484,7 @@ public bool Remove(LoopListNode node) public bool Remove(T value) { var lst = Finds(value); - if (lst == null) + if (lst is null) return false; var ge = lst!.GetEnumerator(); @@ -674,7 +674,7 @@ public string ToString(string? format, IFormatProvider? formatProvider = null) { var s = new StringBuilder(); s.Append($"Count = {Count};"); - if (format == null) + if (format is null) { s.Append("{ "); foreach (T value in this) diff --git a/src/IFoxCAD.Cad/ExtensionMethod/CurveEx.cs b/src/IFoxCAD.Cad/ExtensionMethod/CurveEx.cs index 9684fc3..31b3027 100644 --- a/src/IFoxCAD.Cad/ExtensionMethod/CurveEx.cs +++ b/src/IFoxCAD.Cad/ExtensionMethod/CurveEx.cs @@ -85,14 +85,14 @@ public void FindRegion(List edges, List> regions) var edgeItem = this; region.Add(edgeItem); var edgeItem2 = this.GetNext(edges); - if (edgeItem2.Edge != null) + if (edgeItem2.Edge is not null) { bool hasList = false; foreach (var edgeList2 in regions) { var node = edgeList2.GetNode(e => e.Equals(edgeItem)); //var node = edgeList2.Find(edgeItem); - if (node != null && node != edgeList2.Last) + if (node is not null && node != edgeList2.Last) { if (node.Next!.Value.Equals(edgeItem2)) { @@ -103,7 +103,7 @@ public void FindRegion(List edges, List> regions) } if (!hasList) { - while (edgeItem2.Edge != null) + while (edgeItem2.Edge is not null) { if (edgeItem2.Edge == edgeItem.Edge) break; @@ -242,7 +242,7 @@ public static List Topo(List curves) foreach (var curve in curves) { var cc3d = curve.ToCompositeCurve3d(); - if (cc3d != null) + if (cc3d is not null) { geCurves.Add(cc3d); paramss.Add(new List()); @@ -389,7 +389,7 @@ public static List Topo(List curves) var curve = node!.Value.Edge.Curve; var node2 = regions[j].GetNode(e => e.Edge.Curve == curve); //var node2 = regions[j].Find(node.Value); - if (node2 != null) + if (node2 is not null) { eq = true; var b = node.Value.Forward; @@ -438,7 +438,7 @@ public static List BreakCurve(this List curves) foreach (var curve in curves) { var cc3d = curve.ToCompositeCurve3d(); - if (cc3d != null) + if (cc3d is not null) { geCurves.Add(cc3d); paramss.Add(new List()); @@ -476,7 +476,7 @@ public static List BreakCurve(this List curves) foreach (CompositeCurve3d c3d in c3ds) { var c = c3d.ToCurve(); - if (c != null) + if (c is not null) { c.SetPropertiesFrom(curves[i]); newCurves.Add(c); @@ -905,11 +905,11 @@ public static CompositeCurve3d ToCurve3d(this Polyline pl) default: break; } - if (nc3d == null) + if (nc3d is null) { nc3d = nc3dtemp; } - else if (nc3dtemp != null) + else if (nc3dtemp is not null) { nc3d.JoinWith(nc3dtemp); } diff --git a/src/IFoxCAD.Cad/ExtensionMethod/DBDictionaryEx.cs b/src/IFoxCAD.Cad/ExtensionMethod/DBDictionaryEx.cs index c1344d7..7edebf0 100644 --- a/src/IFoxCAD.Cad/ExtensionMethod/DBDictionaryEx.cs +++ b/src/IFoxCAD.Cad/ExtensionMethod/DBDictionaryEx.cs @@ -78,7 +78,7 @@ public static void SetAt(this DBDictionary dict, string key, T obj, Transacti public static XRecordDataList? GetXRecord(this DBDictionary dict, string key) { Xrecord? rec = dict.GetAt(key); - if (rec != null) + if (rec is not null) return rec.Data; return null; } diff --git a/src/IFoxCAD.Cad/ExtensionMethod/EditorEx.cs b/src/IFoxCAD.Cad/ExtensionMethod/EditorEx.cs index bfcfa0f..66e3ef7 100644 --- a/src/IFoxCAD.Cad/ExtensionMethod/EditorEx.cs +++ b/src/IFoxCAD.Cad/ExtensionMethod/EditorEx.cs @@ -60,7 +60,7 @@ public static SelectionSet SelectByLineWeight(this Editor editor, LineWeight lin { var pso = new PromptSelectionOptions(); PromptSelectionResult? ss = null; - if (mode != null) + if (mode is not null) { mode = mode.ToUpper(); pso.SinglePickInSpace = mode.Contains(":A"); @@ -75,19 +75,19 @@ public static SelectionSet SelectByLineWeight(this Editor editor, LineWeight lin pso.ForceSubSelections = mode.Contains("-F"); } - if (messages != null) + if (messages is not null) { pso.MessageForAdding = messages[0]; pso.MessageForRemoval = messages[1]; } - if (keywords != null) + if (keywords is not null) { foreach (var keyword in keywords) { pso.Keywords.Add(keyword); } - if (pso.MessageForRemoval == null) + if (pso.MessageForRemoval is null) { pso.MessageForAdding = "选择对象"; } @@ -97,7 +97,7 @@ public static SelectionSet SelectByLineWeight(this Editor editor, LineWeight lin } try { - if (filter != null) + if (filter is not null) { ss = editor.GetSelection(pso, filter); } @@ -357,9 +357,9 @@ public static void WriteMessage(string format, params object[] args) /// 有,没有 public static bool HasEditor() { - return Application.DocumentManager.MdiActiveDocument != null + return Application.DocumentManager.MdiActiveDocument is not null && Application.DocumentManager.Count != 0 - && Application.DocumentManager.MdiActiveDocument.Editor != null; + && Application.DocumentManager.MdiActiveDocument.Editor is not null; }// /// diff --git a/src/IFoxCAD.Cad/ExtensionMethod/EntityEx.cs b/src/IFoxCAD.Cad/ExtensionMethod/EntityEx.cs index 9f46056..3c3a226 100644 --- a/src/IFoxCAD.Cad/ExtensionMethod/EntityEx.cs +++ b/src/IFoxCAD.Cad/ExtensionMethod/EntityEx.cs @@ -346,11 +346,11 @@ public static Circle CreateCircle(Point3d startPoint, Point3d endPoint) /// 裁剪多边形点表 public static void ClipBlockRef(this BlockReference bref, IEnumerable pt3ds) { - //if (bref == null) + //if (bref is null) //{ // throw new ArgumentNullException(nameof(bref)); //} - //if (pt3ds == null) + //if (pt3ds is null) //{ // throw new ArgumentNullException(nameof(pt3ds)); //} @@ -376,7 +376,7 @@ public static void ClipBlockRef(this BlockReference bref, IEnumerable p /// 第二角点 public static void ClipBlockRef(this BlockReference bref, Point3d pt1, Point3d pt2) { - //if (bref == null) + //if (bref is null) //{ // throw new ArgumentNullException(nameof(bref)); //} diff --git a/src/IFoxCAD.Cad/ExtensionMethod/Enums.cs b/src/IFoxCAD.Cad/ExtensionMethod/Enums.cs index 15115df..fc0fbaf 100644 --- a/src/IFoxCAD.Cad/ExtensionMethod/Enums.cs +++ b/src/IFoxCAD.Cad/ExtensionMethod/Enums.cs @@ -97,7 +97,7 @@ public static string GetDesc(this Enum val) var memberInfo = type.GetMember(val.ToString()); var attributes = memberInfo[0].GetCustomAttributes(typeof(DescriptionAttribute), false); //如果没有定义描述,就把当前枚举值的对应名称返回 - if (attributes == null || attributes.Length != 1) + if (attributes is null || attributes.Length != 1) { return val.ToString(); } diff --git a/src/IFoxCAD.Cad/ExtensionMethod/GeometryEx.cs b/src/IFoxCAD.Cad/ExtensionMethod/GeometryEx.cs index db3bff6..84b57f7 100644 --- a/src/IFoxCAD.Cad/ExtensionMethod/GeometryEx.cs +++ b/src/IFoxCAD.Cad/ExtensionMethod/GeometryEx.cs @@ -195,7 +195,7 @@ public static CircularArc2d GetMinCircle(Point2d pt1, Point2d pt2, Point2d pt3, //如果另一点属于该圆,并且半径小于当前值就把它做为候选解 if (tca2d.IsIn(firstNode.Previous!.Value)) { - if (ca2d == null || tca2d.Radius < ca2d.Radius) + if (ca2d is null || tca2d.Radius < ca2d.Radius) { ca2d = tca2d; ptlst = tptlst; @@ -414,7 +414,7 @@ public static OrientationType IsClockWise(this IEnumerable pnts) /// 凸包 public static List? ConvexHull(this List points) { - if (points == null) + if (points is null) return null; if (points.Count <= 1) diff --git a/src/IFoxCAD.Cad/ExtensionMethod/SymbolTableEx.cs b/src/IFoxCAD.Cad/ExtensionMethod/SymbolTableEx.cs index 75c2d40..018fc00 100644 --- a/src/IFoxCAD.Cad/ExtensionMethod/SymbolTableEx.cs +++ b/src/IFoxCAD.Cad/ExtensionMethod/SymbolTableEx.cs @@ -99,12 +99,12 @@ public static ObjectId Add(this SymbolTable table, { action?.Invoke(btr); var entsres = ents?.Invoke(); - if (entsres != null) + if (entsres is not null) { btr.AddEntity(entsres); } var adddefres = attdef?.Invoke(); - if (adddefres != null) + if (adddefres is not null) { btr.AddEntity(adddefres); } @@ -123,11 +123,11 @@ public static ObjectId Add(this SymbolTable table, { return table.Add(name, btr => { - if (ents != null) + if (ents is not null) { btr.AddEntity(ents); } - if (attdef != null) + if (attdef is not null) { btr.AddEntity(attdef); } diff --git a/src/IFoxCAD.Cad/ExtensionMethod/SymbolTableRecordEx.cs b/src/IFoxCAD.Cad/ExtensionMethod/SymbolTableRecordEx.cs index 2a6e1bd..1d7c543 100644 --- a/src/IFoxCAD.Cad/ExtensionMethod/SymbolTableRecordEx.cs +++ b/src/IFoxCAD.Cad/ExtensionMethod/SymbolTableRecordEx.cs @@ -98,7 +98,7 @@ public static ObjectId AddEnt(this BlockTableRecord btr, Func action, Tr { //transaction ??= DBTrans.Top.Transaction; var ent = action.Invoke(); - if (ent == null) + if (ent is null) { return ObjectId.Null; } @@ -149,7 +149,7 @@ public static ObjectId AddCircle(this BlockTableRecord btr, Point3d p0, Point3d { var circle = EntityEx.CreateCircle(p0, p1, p2); //return circle is not null ? btr.AddEnt(circle, action, trans) : throw new ArgumentNullException(nameof(circle), "对象为 null"); - if (circle == null) + if (circle is null) { throw new ArgumentNullException(nameof(circle), "对象为 null"); } diff --git a/src/IFoxCAD.Cad/Runtime/AutoRegAssem.cs b/src/IFoxCAD.Cad/Runtime/AutoRegAssem.cs index e336b3d..f306fc3 100644 --- a/src/IFoxCAD.Cad/Runtime/AutoRegAssem.cs +++ b/src/IFoxCAD.Cad/Runtime/AutoRegAssem.cs @@ -46,7 +46,7 @@ public abstract class AutoRegAssem : IExtensionApplication /// 路径对象 public static DirectoryInfo GetDirectory(Assembly assem) { - if (assem == null) + if (assem is null) { throw new(nameof(assem)); } diff --git a/src/IFoxCAD.Cad/Runtime/SymbolTable.cs b/src/IFoxCAD.Cad/Runtime/SymbolTable.cs index 15d74eb..27dd4f9 100644 --- a/src/IFoxCAD.Cad/Runtime/SymbolTable.cs +++ b/src/IFoxCAD.Cad/Runtime/SymbolTable.cs @@ -138,7 +138,7 @@ private static void Remove(TRecord record) public void Remove(string name) { var record = GetRecord(name); - if (record != null) + if (record is not null) { Remove(record); } @@ -151,7 +151,7 @@ public void Remove(string name) public void Remove(ObjectId id) { var record = GetRecord(id); - if (record != null) + if (record is not null) { Remove(record); } @@ -183,7 +183,7 @@ private static void Change(TRecord record, Action action) public void Change(string name, Action action) { var record = GetRecord(name); - if (record != null) + if (record is not null) { Change(record, action); } @@ -196,7 +196,7 @@ public void Change(string name, Action action) public void Change(ObjectId id, Action action) { var record = GetRecord(id); - if (record != null) + if (record is not null) { Change(record, action); } diff --git a/src/IFoxCAD.WPF/DependencyObjectExtensions.cs b/src/IFoxCAD.WPF/DependencyObjectExtensions.cs index 04e969e..596961d 100644 --- a/src/IFoxCAD.WPF/DependencyObjectExtensions.cs +++ b/src/IFoxCAD.WPF/DependencyObjectExtensions.cs @@ -12,12 +12,12 @@ public static class DependencyObjectExtensions /// 依赖属性 public static DependencyObject? GetParentObject(this DependencyObject child) { - if (child == null) return null; + if (child is null) return null; if (child is ContentElement contentElement) { DependencyObject parent = ContentOperations.GetParent(contentElement); - if (parent != null) return parent; + if (parent is not null) return parent; FrameworkContentElement? fce = contentElement as FrameworkContentElement; return fce?.Parent; @@ -26,7 +26,7 @@ public static class DependencyObjectExtensions if (child is FrameworkElement frameworkElement) { DependencyObject parent = frameworkElement.Parent; - if (parent != null) return parent; + if (parent is not null) return parent; } return VisualTreeHelper.GetParent(child); diff --git a/src/IFoxCAD.WPF/EventBindingExtension.cs b/src/IFoxCAD.WPF/EventBindingExtension.cs index 33f0407..df4ee6f 100644 --- a/src/IFoxCAD.WPF/EventBindingExtension.cs +++ b/src/IFoxCAD.WPF/EventBindingExtension.cs @@ -25,7 +25,7 @@ public class EventBindingExtension : MarkupExtension /// public override object? ProvideValue(IServiceProvider serviceProvider) { - if (serviceProvider == null) + if (serviceProvider is null) { throw new ArgumentNullException(nameof(serviceProvider)); } @@ -40,7 +40,7 @@ public class EventBindingExtension : MarkupExtension } var memberInfo = targetProvider.TargetProperty as MemberInfo; - if (memberInfo == null) + if (memberInfo is null) { throw new InvalidOperationException(); } @@ -87,7 +87,7 @@ public class EventBindingExtension : MarkupExtension { Type? eventHandlerType = GetEventHandlerType(memberInfo); - if (eventHandlerType == null) return null; + if (eventHandlerType is null) return null; var handlerInfo = eventHandlerType.GetMethod("Invoke"); var method = new DynamicMethod("", handlerInfo.ReturnType, @@ -101,7 +101,7 @@ public class EventBindingExtension : MarkupExtension gen.Emit(OpCodes.Ldarg, 0); gen.Emit(OpCodes.Ldarg, 1); gen.Emit(OpCodes.Ldstr, cmdName); - if (CommandParameter == null) + if (CommandParameter is null) { gen.Emit(OpCodes.Ldnull); } @@ -140,7 +140,7 @@ public static void HandlerIntern(object sender, object args, string cmdName, str { commandParam = GetCommandParameter(fe, args, commandParameter!); } - if ((cmd != null) && cmd.CanExecute(commandParam)) + if ((cmd is not null) && cmd.CanExecute(commandParam)) { cmd.Execute(commandParam); } @@ -150,11 +150,11 @@ public static void HandlerIntern(object sender, object args, string cmdName, str internal static ICommand? GetCommand(FrameworkElement target, string cmdName) { var vm = FindViewModel(target); - if (vm == null) return null; + if (vm is null) return null; var vmType = vm.GetType(); var cmdProp = vmType.GetProperty(cmdName); - if (cmdProp != null) + if (cmdProp is not null) { return cmdProp.GetValue(vm) as ICommand; } @@ -182,7 +182,7 @@ internal static object GetCommandParameter(FrameworkElement target, object args, internal static ViewModelBase? FindViewModel(FrameworkElement? target) { - if (target == null) return null; + if (target is null) return null; if (target.DataContext is ViewModelBase vm) return vm; @@ -193,15 +193,15 @@ internal static object GetCommandParameter(FrameworkElement target, object args, internal static object FollowPropertyPath(object target, string path, Type? valueType = null) { - if (target == null) throw new ArgumentNullException(nameof(target)); - if (path == null) throw new ArgumentNullException(nameof(path)); + if (target is null) throw new ArgumentNullException(nameof(target)); + if (path is null) throw new ArgumentNullException(nameof(path)); Type currentType = valueType ?? target.GetType(); foreach (string propertyName in path.Split('.')) { PropertyInfo property = currentType.GetProperty(propertyName); - if (property == null) throw new NullReferenceException("property"); + if (property is null) throw new NullReferenceException("property"); target = property.GetValue(target); currentType = property.PropertyType; diff --git a/src/IFoxCAD.WPF/RelayCommand.cs b/src/IFoxCAD.WPF/RelayCommand.cs index 628122a..72fabca 100644 --- a/src/IFoxCAD.WPF/RelayCommand.cs +++ b/src/IFoxCAD.WPF/RelayCommand.cs @@ -37,14 +37,14 @@ public event EventHandler CanExecuteChanged { add { - if (_canExecute != null) + if (_canExecute is not null) { CommandManager.RequerySuggested += value; } } remove { - if (_canExecute != null) + if (_canExecute is not null) { CommandManager.RequerySuggested -= value; } @@ -60,7 +60,7 @@ public event EventHandler CanExecuteChanged [DebuggerStepThrough] public bool CanExecute(object parameter) { - return _canExecute == null || _canExecute(parameter); + return _canExecute is null || _canExecute(parameter); } /// /// 定义在调用此命令时要调用的方法。 @@ -108,14 +108,14 @@ public event EventHandler CanExecuteChanged { add { - if (_canExecute != null) + if (_canExecute is not null) { CommandManager.RequerySuggested += value; } } remove { - if (_canExecute != null) + if (_canExecute is not null) { CommandManager.RequerySuggested -= value; } @@ -130,7 +130,7 @@ public event EventHandler CanExecuteChanged /// public bool CanExecute(object parameter) { - if (_canExecute == null) + if (_canExecute is null) { return true; } @@ -142,7 +142,7 @@ public bool CanExecute(object parameter) /// 此命令使用的数据。 如果此命令不需要传递数据,则该对象可以设置为 。 public void Execute(object parameter) { - if (_execute != null && CanExecute(parameter)) + if (_execute is not null && CanExecute(parameter)) { _execute((T)parameter); } @@ -160,11 +160,11 @@ public class EventCommand : TriggerAction /// 要执行的动作参数, 如果动作为提供参数,就设置为null protected override void Invoke(object parameter) { - if (CommandParameter != null) + if (CommandParameter is not null) { parameter = CommandParameter; } - if (Command != null) + if (Command is not null) { Command.Execute(parameter); } diff --git a/tests/Test/testFileDatabase.cs b/tests/Test/testFileDatabase.cs index fb61dba..7493f30 100644 --- a/tests/Test/testFileDatabase.cs +++ b/tests/Test/testFileDatabase.cs @@ -14,7 +14,7 @@ public void TestDatabase() var fileName = @"C:\Users\Administrator\Desktop\合并详图测试BUG.dwg"; using DBTrans trans = new(fileName); trans.ModelSpace.AddEntity(new Line(new(0, 0, 0), new(1000, 1000, 0))); - if (trans.Document != null && trans.Document.IsActive) + if (trans.Document is not null && trans.Document.IsActive) trans.Document.SendStringToExecute("_qsave\n", false, true, true); else trans.Database.SaveAs(fileName, DwgVersion.AC1021); diff --git a/tests/Test/testblock.cs b/tests/Test/testblock.cs index 6349384..95b3145 100644 --- a/tests/Test/testblock.cs +++ b/tests/Test/testblock.cs @@ -312,7 +312,7 @@ public void QuickBlockDef() //foreach (var id in ids) //{ // Entity ent = tr.GetObject(id); - // if (ent == null) + // if (ent is null) // continue; // try // { -- Gitee From 7f215368e13f81b2c12ef85c8ad228dbb8788966 Mon Sep 17 00:00:00 2001 From: liuqihong <540762622@qq.com> Date: Sat, 26 Mar 2022 16:51:10 +0800 Subject: [PATCH 058/675] =?UTF-8?q?=E5=A2=9E=E5=8A=A0=E5=85=8B=E9=9A=86?= =?UTF-8?q?=E5=87=BD=E6=95=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/IFoxCAD.Basal/LoopList.cs | 65 +++++++++++++++++++++++------------ 1 file changed, 43 insertions(+), 22 deletions(-) diff --git a/src/IFoxCAD.Basal/LoopList.cs b/src/IFoxCAD.Basal/LoopList.cs index c3bde54..eba21de 100644 --- a/src/IFoxCAD.Basal/LoopList.cs +++ b/src/IFoxCAD.Basal/LoopList.cs @@ -36,7 +36,7 @@ public class LoopListNode public LoopListNode(T value, LoopList ts) { Value = value; - List = ts; + List = ts; } /// @@ -56,8 +56,8 @@ public LoopListNode(T value, LoopList ts) /// internal void Invalidate() { - List = null; - Next = null; + List = null; + Next = null; Previous = null; } #endregion @@ -67,7 +67,7 @@ internal void Invalidate() /// 环链表 /// /// -public class LoopList : IEnumerable, IFormattable +public class LoopList : IEnumerable, IFormattable, ICloneable { #region 成员 @@ -132,9 +132,9 @@ public bool SetFirst(LoopListNode node) public void Swap(LoopListNode node1, LoopListNode node2) { #if NET35 - var value = node1.Value; - node1.Value = node2.Value; - node2.Value = value; + var value = node1.Value; + node1.Value = node2.Value; + node2.Value = value; #else (node2.Value, node1.Value) = (node1.Value, node2.Value); #endif @@ -148,7 +148,6 @@ public void Reverse() var first = First; if (first is null) return; - var last = Last; for (int i = 0; i < Count / 2; i++) { @@ -166,11 +165,10 @@ public void Clear() //移除头部,表示链表再也无法遍历得到 First = null; Count = 0; - } /// - /// 从头遍历 + /// 从头遍历_非迭代器 /// /// public void ForEach(Func, bool> action) @@ -332,16 +330,16 @@ public LoopListNode AddFirst(T value) if (Count == 0) { - First = node; + First = node; First.Previous = First.Next = node; } else { LoopListNode last = Last!; - First!.Previous = last.Next = node; - node.Next = First; - node.Previous = last; - First = node; + First!.Previous = last.Next = node; + node.Next = First; + node.Previous = last; + First = node; } Count++; return First; @@ -358,20 +356,30 @@ public LoopListNode Add(T value) if (Count == 0) { - First = node; + First = node; First.Previous = First.Next = node; } else { - var last = First!.Previous!; + var last = First!.Previous!; First.Previous = last.Next = node; - node.Next = First; - node.Previous = last; + node.Next = First; + node.Previous = last; } Count++; return Last!; } + /// + /// 在尾节点之后插入节点,并设置新节点为尾节点_此函数仅为与LinkedList同名方法 + /// + /// + /// + public LoopListNode AddLast(T value) + { + return Add(value); + } + /// /// 前面增加节点 /// @@ -590,7 +598,7 @@ public void LinkTo(LoopListNode from, LoopListNode to, int number, bool is #endregion - #region IEnumerable 成员 + #region IEnumerable /// /// 获取节点的查询器 @@ -645,7 +653,7 @@ public IEnumerator GetEnumerator() #endregion - #region IFormattable 成员 + #region IFormattable /// /// 转换为字符串_格式化实现 /// @@ -684,4 +692,17 @@ public string ToString(string? format, IFormatProvider? formatProvider = null) return s.ToString(); } #endregion -} + + #region ICloneable + public object Clone() + { + //浅克隆 + var lst = new LoopList>(); + ForEach(node => { + lst.Add(node); + return false; + }); + return lst; + } + #endregion +} \ No newline at end of file -- Gitee From eeedf1cb4359205372c13c2d5f13d5a65756e80e Mon Sep 17 00:00:00 2001 From: liuqihong <540762622@qq.com> Date: Sat, 26 Mar 2022 17:55:28 +0800 Subject: [PATCH 059/675] =?UTF-8?q?=E5=8E=BB=E6=8E=89=E5=85=8B=E9=9A=86?= =?UTF-8?q?=E5=87=BD=E6=95=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/IFoxCAD.Basal/LoopList.cs | 24 +++++++++++++----------- 1 file changed, 13 insertions(+), 11 deletions(-) diff --git a/src/IFoxCAD.Basal/LoopList.cs b/src/IFoxCAD.Basal/LoopList.cs index eba21de..c0a64f8 100644 --- a/src/IFoxCAD.Basal/LoopList.cs +++ b/src/IFoxCAD.Basal/LoopList.cs @@ -67,7 +67,7 @@ internal void Invalidate() /// 环链表 /// /// -public class LoopList : IEnumerable, IFormattable, ICloneable +public class LoopList : IEnumerable, IFormattable { #region 成员 @@ -694,15 +694,17 @@ public string ToString(string? format, IFormatProvider? formatProvider = null) #endregion #region ICloneable - public object Clone() - { - //浅克隆 - var lst = new LoopList>(); - ForEach(node => { - lst.Add(node); - return false; - }); - return lst; - } + /* 山人说无法分辨ICloneable接口是深浅克隆,因此不要在泛型模板实现克隆函数,让用户自己来 + * 因此约定了:CopyTo(T,index)是深克隆;MemberwiseClone()是浅克隆; + * public object Clone() + * { + * var lst = new LoopList>(); + * ForEach(node => { + * lst.Add(node); + * return false; + * }); + * return lst; + * } + */ #endregion } \ No newline at end of file -- Gitee From 22bf3b3acc24ec811fc1a37763ef4a3f91fc8dd8 Mon Sep 17 00:00:00 2001 From: liuqihong <540762622@qq.com> Date: Tue, 29 Mar 2022 21:00:51 +0800 Subject: [PATCH 060/675] =?UTF-8?q?=E5=A2=9E=E5=8A=A0AddRange?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/IFoxCAD.Basal/IFoxCAD.Basal.csproj | 2 + src/IFoxCAD.Basal/LoopList.cs | 93 +++++++------ .../ExtensionMethod/CollectionEx.cs | 4 +- src/IFoxCAD.Cad/ExtensionMethod/Curve2dEx.cs | 2 +- src/IFoxCAD.Cad/ExtensionMethod/CurveEx.cs | 30 ++--- .../ExtensionMethod/DBDictionaryEx.cs | 2 +- src/IFoxCAD.Cad/ExtensionMethod/EditorEx.cs | 10 +- src/IFoxCAD.Cad/ExtensionMethod/EntityEx.cs | 6 +- src/IFoxCAD.Cad/ExtensionMethod/GeometryEx.cs | 4 +- .../ExtensionMethod/SymbolTableEx.cs | 12 +- src/IFoxCAD.Cad/IFoxCAD.Cad.csproj | 123 +++++++++--------- src/IFoxCAD.Cad/Runtime/AcadVersion.cs | 4 +- src/IFoxCAD.Cad/Runtime/AutoRegAssem.cs | 4 +- src/IFoxCAD.Cad/Runtime/SymbolTable.cs | 10 +- src/IFoxCAD.WPF/DependencyObjectExtensions.cs | 16 ++- src/IFoxCAD.WPF/EventBindingExtension.cs | 46 ++++--- src/IFoxCAD.WPF/IFoxCAD.WPF.csproj | 68 +++++----- src/IFoxCAD.WPF/RelayCommand.cs | 24 ++-- .../Properties/launchSettings.json | 0 tests/{DBTrans.test => Test}/Test.cs | 5 +- .../DBTrans.test.csproj => Test/Test.csproj} | 17 ++- .../{DBTrans.test => Test}/testConvexHull.cs | 0 tests/{DBTrans.test => Test}/testeditor.cs | 0 tests/{DBTrans.test => Test}/testenv.cs | 0 .../testselectfilter.cs | 0 .../{DBTrans.test => Test}/wpf/TestView.xaml | 0 .../wpf/TestView.xaml.cs | 0 .../wpf/TestViewModel.cs | 0 28 files changed, 254 insertions(+), 228 deletions(-) rename tests/{DBTrans.test => Test}/Properties/launchSettings.json (100%) rename tests/{DBTrans.test => Test}/Test.cs (99%) rename tests/{DBTrans.test/DBTrans.test.csproj => Test/Test.csproj} (58%) rename tests/{DBTrans.test => Test}/testConvexHull.cs (100%) rename tests/{DBTrans.test => Test}/testeditor.cs (100%) rename tests/{DBTrans.test => Test}/testenv.cs (100%) rename tests/{DBTrans.test => Test}/testselectfilter.cs (100%) rename tests/{DBTrans.test => Test}/wpf/TestView.xaml (100%) rename tests/{DBTrans.test => Test}/wpf/TestView.xaml.cs (100%) rename tests/{DBTrans.test => Test}/wpf/TestViewModel.cs (100%) diff --git a/src/IFoxCAD.Basal/IFoxCAD.Basal.csproj b/src/IFoxCAD.Basal/IFoxCAD.Basal.csproj index b3cec15..a15060d 100644 --- a/src/IFoxCAD.Basal/IFoxCAD.Basal.csproj +++ b/src/IFoxCAD.Basal/IFoxCAD.Basal.csproj @@ -2,6 +2,8 @@ preview + enable + 1.0.0.* 1.0.0.0 diff --git a/src/IFoxCAD.Basal/LoopList.cs b/src/IFoxCAD.Basal/LoopList.cs index e75eccd..fca9931 100644 --- a/src/IFoxCAD.Basal/LoopList.cs +++ b/src/IFoxCAD.Basal/LoopList.cs @@ -32,7 +32,6 @@ public class LoopListNode /// public LoopList? List { internal set; get; } #endregion - #region 构造 /// /// 环链表节点构造函数 @@ -54,7 +53,6 @@ public LoopListNode(T value, LoopList ts) return forward ? Next : Previous; } #endregion - #region 方法 /// /// 无效化成员 @@ -75,7 +73,6 @@ internal void Invalidate() public class LoopList : IEnumerable, IFormattable { #region 成员 - /// /// 节点数 /// @@ -92,9 +89,7 @@ public class LoopList : IEnumerable, IFormattable public LoopListNode? Last => First?.Previous; #endregion - #region 构造 - /// /// 默认构造函数 /// @@ -112,9 +107,7 @@ public LoopList(IEnumerable values) } #endregion - #region 方法 - /// /// 设置首节点 /// @@ -137,9 +130,9 @@ public bool SetFirst(LoopListNode node) public void Swap(LoopListNode node1, LoopListNode node2) { #if NET35 - var value = node1.Value; - node1.Value = node2.Value; - node2.Value = value; + var value = node1.Value; + node1.Value = node2.Value; + node2.Value = value; #else (node2.Value, node1.Value) = (node1.Value, node2.Value); #endif @@ -151,9 +144,8 @@ public void Swap(LoopListNode node1, LoopListNode node2) public void Reverse() { var first = First; - if (first == null) + if (first is null) return; - var last = Last; for (int i = 0; i < Count / 2; i++) { @@ -171,17 +163,16 @@ public void Clear() //移除头部,表示链表再也无法遍历得到 First = null; Count = 0; - GC.Collect(); } /// - /// 从头遍历 + /// 从头遍历_非迭代器 /// /// public void ForEach(Func, bool> action) { var node = First; - if (node == null) + if (node is null) return; for (int i = 0; i < Count; i++) { @@ -192,7 +183,6 @@ public void ForEach(Func, bool> action) } #region Contains - /// /// 是否包含节点 /// @@ -200,7 +190,7 @@ public void ForEach(Func, bool> action) /// public bool Contains(LoopListNode node) { - return node != null && node.List == this; + return node is not null && node.List == this; } /// @@ -211,8 +201,7 @@ public bool Contains(LoopListNode node) public bool Contains(T value) { bool result = false; - ForEach(node => - { + ForEach(node => { if (node.Value!.Equals(value)) { result = true; @@ -244,9 +233,9 @@ public bool Contains(T value) LoopListNode? node = First; var c = EqualityComparer.Default; - if (node != null) + if (node is not null) { - if (value != null) + if (value is not null) { do { @@ -259,7 +248,7 @@ public bool Contains(T value) { do { - if (node!.Value == null) + if (node!.Value is null) return node; node = node.Next; } while (node != First); @@ -276,12 +265,12 @@ public bool Contains(T value) public IEnumerable>? Finds(T value) { LoopListNode? node = First; - if (node == null) + if (node is null) return null; List> result = new(); var c = EqualityComparer.Default; - if (value != null) + if (value is not null) { do { @@ -294,7 +283,7 @@ public bool Contains(T value) { do { - if (node!.Value == null) + if (node!.Value is null) result.Add(node); node = node.Next; } while (node != First); @@ -310,8 +299,7 @@ public bool Contains(T value) public LoopListNode? GetNode(Func func) { LoopListNode? result = null; - ForEach(node => - { + ForEach(node => { if (func(node.Value)) { result = node; @@ -323,9 +311,7 @@ public bool Contains(T value) } #endregion - #region Add - /// /// 在首节点之前插入节点,并设置新节点为首节点 /// @@ -377,6 +363,23 @@ public LoopListNode Add(T value) return Last!; } + /// + /// 在尾节点之后插入节点,并设置新节点为尾节点_此函数仅为与LinkedList同名方法 + /// + /// + /// + public LoopListNode AddLast(T value) + { + return Add(value); + } + + public void AddRange(IEnumerable list) + { + var ge = list.GetEnumerator(); + while (ge.MoveNext()) + Add(ge.Current); + } + /// /// 前面增加节点 /// @@ -415,9 +418,7 @@ public LoopListNode AddAfter(LoopListNode node, T value) } #endregion - #region Remove - /// /// 删除首节点 /// @@ -489,7 +490,7 @@ public bool Remove(LoopListNode node) public bool Remove(T value) { var lst = Finds(value); - if (lst == null) + if (lst is null) return false; var ge = lst!.GetEnumerator(); @@ -520,9 +521,7 @@ void InternalRemove(LoopListNode node) } #endregion - #region LinkTo - /// /// 链接两节点,并去除这两个节点间的所有节点 /// @@ -592,11 +591,8 @@ public void LinkTo(LoopListNode from, LoopListNode to, int number, bool is } #endregion - #endregion - - #region IEnumerable 成员 - + #region IEnumerable /// /// 获取节点的查询器 /// @@ -643,14 +639,11 @@ public IEnumerator GetEnumerator() IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); #region IEnumerable 成员 - IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); #endregion IEnumerable 成员 - #endregion - - #region IFormattable 成员 + #region IFormattable /// /// 转换为字符串_格式化实现 /// @@ -679,7 +672,7 @@ public string ToString(string? format, IFormatProvider? formatProvider = null) { var s = new StringBuilder(); s.Append($"Count = {Count};"); - if (format == null) + if (format is null) { s.Append("{ "); foreach (T value in this) @@ -689,5 +682,19 @@ public string ToString(string? format, IFormatProvider? formatProvider = null) return s.ToString(); } #endregion + #region ICloneable + /* 山人说无法分辨ICloneable接口是深浅克隆,因此不要在泛型模板实现克隆函数,让用户自己来 + * 因此约定了:CopyTo(T,index)是深克隆;MemberwiseClone()是浅克隆; + * public object Clone() + * { + * var lst = new LoopList>(); + * ForEach(node => { + * lst.Add(node); + * return false; + * }); + * return lst; + * } + */ + #endregion } } \ No newline at end of file diff --git a/src/IFoxCAD.Cad/ExtensionMethod/CollectionEx.cs b/src/IFoxCAD.Cad/ExtensionMethod/CollectionEx.cs index 9b0c984..406a33d 100644 --- a/src/IFoxCAD.Cad/ExtensionMethod/CollectionEx.cs +++ b/src/IFoxCAD.Cad/ExtensionMethod/CollectionEx.cs @@ -106,7 +106,7 @@ public static List ToList(this StringCollection strs) /// 要运行的委托 public static void ForEach(this IEnumerable source, Action action) { - if (action == null) + if (action is null) throw new ArgumentNullException(nameof(action)); foreach (var element in source) action.Invoke(element); @@ -121,7 +121,7 @@ public static void ForEach(this IEnumerable source, Action action) /// 要运行的委托 public static void ForEach(this IEnumerable source, Action action) { - if (action == null) + if (action is null) throw new ArgumentNullException(nameof(action)); int i = 0; foreach (var item in source) diff --git a/src/IFoxCAD.Cad/ExtensionMethod/Curve2dEx.cs b/src/IFoxCAD.Cad/ExtensionMethod/Curve2dEx.cs index fb3b773..7acb61e 100644 --- a/src/IFoxCAD.Cad/ExtensionMethod/Curve2dEx.cs +++ b/src/IFoxCAD.Cad/ExtensionMethod/Curve2dEx.cs @@ -18,7 +18,7 @@ public static class Curve2dEx /// Ge2d曲线 /// 曲线转换矩阵 /// Db曲线 - public static Curve ToCurve(this Curve2d curve, Matrix3d mat) + public static Curve? ToCurve(this Curve2d curve, Matrix3d mat) { return curve switch { diff --git a/src/IFoxCAD.Cad/ExtensionMethod/CurveEx.cs b/src/IFoxCAD.Cad/ExtensionMethod/CurveEx.cs index 524a6ea..26fbd18 100644 --- a/src/IFoxCAD.Cad/ExtensionMethod/CurveEx.cs +++ b/src/IFoxCAD.Cad/ExtensionMethod/CurveEx.cs @@ -64,14 +64,12 @@ public EdgeItem(Edge edge, bool forward) public CompositeCurve3d GetCurve() { - CompositeCurve3d cc3d = Edge.Curve; + var cc3d = Edge.Curve; if (Forward) - { return cc3d; - } else { - cc3d = cc3d.Clone() as CompositeCurve3d; + cc3d = (CompositeCurve3d)cc3d.Clone(); return cc3d.GetReverseParameterCurve() as CompositeCurve3d; } } @@ -89,13 +87,13 @@ public void FindRegion(List edges, List> regions) var edgeItem = this; region.Add(edgeItem); var edgeItem2 = this.GetNext(edges); - if (edgeItem2.Edge != null) + if (edgeItem2.Edge is not null) { bool hasList = false; foreach (var edgeList2 in regions) { var node = edgeList2.GetNode(e => e.Equals(edgeItem)); - if (node != null) + if (node is not null) { if (node.Next.Value.Equals(edgeItem2)) { @@ -106,7 +104,7 @@ public void FindRegion(List edges, List> regions) } if (!hasList) { - while (edgeItem2.Edge != null) + while (edgeItem2.Edge is not null) { if (edgeItem2.Edge == edgeItem.Edge) break; @@ -226,7 +224,7 @@ public static List Topo(List curves) foreach (var curve in curves) { var cc3d = curve.ToCompositeCurve3d(); - if (cc3d != null) + if (cc3d is not null) { geCurves.Add(cc3d); paramss.Add(new List()); @@ -373,7 +371,7 @@ public static List Topo(List curves) var node = regions[i].First; var curve = node.Value.Edge.Curve; var node2 = regions[j].GetNode(e => e.Edge.Curve == curve); - if (eq = node2 != null) + if (eq = node2 is not null) { var b = node.Value.Forward; var b2 = node2.Value.Forward; @@ -421,7 +419,7 @@ public static List BreakCurve(List curves) foreach (var curve in curves) { var cc3d = curve.ToCompositeCurve3d(); - if (cc3d != null) + if (cc3d is not null) { geCurves.Add(cc3d); paramss.Add(new List()); @@ -459,7 +457,7 @@ public static List BreakCurve(List curves) foreach (CompositeCurve3d c3d in c3ds) { Curve c = c3d.ToCurve(); - if (c != null) + if (c is not null) { c.SetPropertiesFrom(curves[i]); newCurves.Add(c); @@ -483,7 +481,7 @@ public static List BreakCurve(List curves) /// 曲线 /// ge曲线 [Obsolete("请使用Cad自带的 GetGeCurve 函数!")] - public static Curve3d ToCurve3d(this Curve curve) + public static Curve3d? ToCurve3d(this Curve curve) { return curve switch { @@ -504,7 +502,7 @@ public static Curve3d ToCurve3d(this Curve curve) /// /// 曲线 /// 复合曲线 - public static CompositeCurve3d ToCompositeCurve3d(this Curve curve) + public static CompositeCurve3d? ToCompositeCurve3d(this Curve curve) { return curve switch { @@ -526,7 +524,7 @@ public static CompositeCurve3d ToCompositeCurve3d(this Curve curve) /// /// 曲线 /// Nurb曲线 - public static NurbCurve3d ToNurbCurve3d(this Curve curve) + public static NurbCurve3d? ToNurbCurve3d(this Curve curve) { return curve switch { @@ -885,11 +883,11 @@ public static NurbCurve3d ToNurbCurve3d(this Polyline pl) default: break; } - if (nc3d == null) + if (nc3d is null) { nc3d = nc3dtemp; } - else if (nc3dtemp != null) + else if (nc3dtemp is not null) { nc3d.JoinWith(nc3dtemp); } diff --git a/src/IFoxCAD.Cad/ExtensionMethod/DBDictionaryEx.cs b/src/IFoxCAD.Cad/ExtensionMethod/DBDictionaryEx.cs index 6d3921e..d4fba82 100644 --- a/src/IFoxCAD.Cad/ExtensionMethod/DBDictionaryEx.cs +++ b/src/IFoxCAD.Cad/ExtensionMethod/DBDictionaryEx.cs @@ -79,7 +79,7 @@ public static void SetAt(this DBDictionary dict, string key, T obj, Transacti public static XRecordDataList GetXRecord(this DBDictionary dict, string key) { Xrecord rec = dict.GetAt(key); - if (rec != null) + if (rec is not null) return rec.Data; return null; } diff --git a/src/IFoxCAD.Cad/ExtensionMethod/EditorEx.cs b/src/IFoxCAD.Cad/ExtensionMethod/EditorEx.cs index 2175194..9745eb9 100644 --- a/src/IFoxCAD.Cad/ExtensionMethod/EditorEx.cs +++ b/src/IFoxCAD.Cad/ExtensionMethod/EditorEx.cs @@ -200,9 +200,9 @@ public static void WriteMessage(string format, params object[] args) /// 有,没有 public static bool HasEditor() { - return Application.DocumentManager.MdiActiveDocument != null + return Application.DocumentManager.MdiActiveDocument is not null && Application.DocumentManager.Count != 0 - && Application.DocumentManager.MdiActiveDocument.Editor != null; + && Application.DocumentManager.MdiActiveDocument.Editor is not null; }// /// @@ -548,8 +548,8 @@ public static void ZoomWindow(this Editor ed, Point3d minPoint, Point3d maxPoint { for (int k = 0; k < 2; k++) { - int n = i * 4 + j * 2 + k; - pnts[n] = new Point3d(oldpnts[i][0], oldpnts[j][1], oldpnts[k][2]); + int n = i * 4 + j * 2 + k; + pnts[n] = new Point3d(oldpnts[i][0], oldpnts[j][1], oldpnts[k][2]); dpnts[n] = pnts[n].TransformBy(ed.GetMatrixFromWcsToMDcs()); } } @@ -656,7 +656,7 @@ public static PromptPointResult GetPoint(this Editor ed, string Message, Point3d { PromptPointOptions ptOp = new(Message) { - BasePoint = BasePoint, + BasePoint = BasePoint, UseBasePoint = true }; return ed.GetPoint(ptOp); diff --git a/src/IFoxCAD.Cad/ExtensionMethod/EntityEx.cs b/src/IFoxCAD.Cad/ExtensionMethod/EntityEx.cs index 0635006..c14b566 100644 --- a/src/IFoxCAD.Cad/ExtensionMethod/EntityEx.cs +++ b/src/IFoxCAD.Cad/ExtensionMethod/EntityEx.cs @@ -354,11 +354,11 @@ public static Circle CreateCircle(Point3d pt1, Point3d PointV, Point3d pt3) /// 裁剪多边形点表 public static void ClipBlockRef(this BlockReference bref, IEnumerable pt3ds) { - if (bref == null) + if (bref is null) { throw new ArgumentNullException(nameof(bref)); } - if (pt3ds == null) + if (pt3ds is null) { throw new ArgumentNullException(nameof(pt3ds)); } @@ -384,7 +384,7 @@ public static void ClipBlockRef(this BlockReference bref, IEnumerable p /// 第二角点 public static void ClipBlockRef(this BlockReference bref, Point3d pt1, Point3d PointV) { - if (bref == null) + if (bref is null) { throw new ArgumentNullException(nameof(bref)); } diff --git a/src/IFoxCAD.Cad/ExtensionMethod/GeometryEx.cs b/src/IFoxCAD.Cad/ExtensionMethod/GeometryEx.cs index dacdc65..8f2a7a1 100644 --- a/src/IFoxCAD.Cad/ExtensionMethod/GeometryEx.cs +++ b/src/IFoxCAD.Cad/ExtensionMethod/GeometryEx.cs @@ -210,7 +210,7 @@ public static CircularArc2d GetMinCircle(Point2d pt1, Point2d PointV, Point2d pt //如果另一点属于该圆,并且半径小于当前值就把它做为候选解 if (tca2d.IsIn(firstNode.Previous.Value)) { - if (ca2d == null || tca2d.Radius < ca2d.Radius) + if (ca2d is null || tca2d.Radius < ca2d.Radius) { ca2d = tca2d; ptlst = tptlst; @@ -429,7 +429,7 @@ public static CircularArc2d GetMinCircle(this List pnts, out LoopList

凸包 public static List ConvexHull(this List points) { - if (points == null) + if (points is null) return null; if (points.Count <= 1) diff --git a/src/IFoxCAD.Cad/ExtensionMethod/SymbolTableEx.cs b/src/IFoxCAD.Cad/ExtensionMethod/SymbolTableEx.cs index c3c9e6b..327f8ba 100644 --- a/src/IFoxCAD.Cad/ExtensionMethod/SymbolTableEx.cs +++ b/src/IFoxCAD.Cad/ExtensionMethod/SymbolTableEx.cs @@ -37,7 +37,7 @@ public static ObjectId Add(this SymbolTable table, /// 图层id public static ObjectId Add(this SymbolTable table, string name, int colorIndex) { - colorIndex %= 256;//防止输入的颜色超出256 + colorIndex %= 256; //防止输入的颜色超出256 colorIndex = Math.Abs(colorIndex);//防止负数 return table.Add(name, lt => lt.Color = Color.FromColorIndex(ColorMethod.ByColor, (short)colorIndex)); } @@ -104,12 +104,12 @@ public static ObjectId Add(this SymbolTable table, return table.Add(name, btr => { action?.Invoke(btr); var entsres = ents?.Invoke(); - if (entsres != null) + if (entsres is not null) { btr.AddEntity(entsres); } var adddefres = attdef?.Invoke(); - if (adddefres != null) + if (adddefres is not null) { btr.AddEntity(adddefres); } @@ -216,7 +216,7 @@ public static ObjectId Add(this SymbolTable name, ltt => { ltt.AsciiDescription = description; - ltt.PatternLength = length; //线型的总长度 + ltt.PatternLength = length; //线型的总长度 ltt.NumDashes = dash.Length; //组成线型的笔画数目 for (int i = 0; i < dash.Length; i++) { @@ -246,9 +246,9 @@ public static ObjectId Add(this SymbolTable { - tstr.Name = textStyleName; + tstr.Name = textStyleName; tstr.FileName = font; - tstr.XScale = xscale; + tstr.XScale = xscale; }); } #endregion diff --git a/src/IFoxCAD.Cad/IFoxCAD.Cad.csproj b/src/IFoxCAD.Cad/IFoxCAD.Cad.csproj index 9227301..d742bef 100644 --- a/src/IFoxCAD.Cad/IFoxCAD.Cad.csproj +++ b/src/IFoxCAD.Cad/IFoxCAD.Cad.csproj @@ -1,63 +1,66 @@  - - net35;net40 - true - 0.1.3 - InspireFunction - xsfhlzh;vicwjb - 基于.NET的Cad二次开发类库 - InspireFunction - https://gitee.com/inspirefunction/ifoxcad - https://gitee.com/inspirefunction/ifoxcad.git - git - IFoxCAD;CAD;AutoCad;C#;NET - Optimize and add multiple functions. - true - true - preview - true - LICENSE - true - - - - - - - - runtime - - - - - - DEBUG - - - $(Configuration);ac2009 - - - $(Configuration);ac2013 - - - - - - True - - - - - - - - - - - - - - - + + + preview + enable + + net35;net40 + true + 0.1.3 + InspireFunction + xsfhlzh;vicwjb + 基于.NET的Cad二次开发类库 + InspireFunction + https://gitee.com/inspirefunction/ifoxcad + https://gitee.com/inspirefunction/ifoxcad.git + git + IFoxCAD;CAD;AutoCad;C#;NET + Optimize and add multiple functions. + true + true + true + LICENSE + true + + + + + + + + runtime + + + + + + DEBUG + + + $(Configuration);ac2009 + + + $(Configuration);ac2013 + + + + + + True + + + + + + + + + + + + + + + diff --git a/src/IFoxCAD.Cad/Runtime/AcadVersion.cs b/src/IFoxCAD.Cad/Runtime/AcadVersion.cs index a0f977c..7be301f 100644 --- a/src/IFoxCAD.Cad/Runtime/AcadVersion.cs +++ b/src/IFoxCAD.Cad/Runtime/AcadVersion.cs @@ -51,7 +51,7 @@ public static List Versions { get { - if (_versions == null) + if (_versions is null) { string[] copys = Registry.LocalMachine @@ -91,7 +91,7 @@ public static List Versions /// cad版本号对象 public static AcadVersion FromApp(object app) { - if (app == null) + if (app is null) { throw new ArgumentNullException(nameof(app)); } diff --git a/src/IFoxCAD.Cad/Runtime/AutoRegAssem.cs b/src/IFoxCAD.Cad/Runtime/AutoRegAssem.cs index 1852003..111be63 100644 --- a/src/IFoxCAD.Cad/Runtime/AutoRegAssem.cs +++ b/src/IFoxCAD.Cad/Runtime/AutoRegAssem.cs @@ -26,7 +26,7 @@ public enum AssemLoadType ///

/// 无效 /// - Disabled = 20 + Disabled = 20 } /// @@ -53,7 +53,7 @@ public abstract class AutoRegAssem : CadRuntime.IExtensionApplication /// 路径对象 public static DirectoryInfo GetDirectory(Assembly assem) { - if (assem == null) + if (assem is null) { throw new(nameof(assem)); } diff --git a/src/IFoxCAD.Cad/Runtime/SymbolTable.cs b/src/IFoxCAD.Cad/Runtime/SymbolTable.cs index b141835..c319b5e 100644 --- a/src/IFoxCAD.Cad/Runtime/SymbolTable.cs +++ b/src/IFoxCAD.Cad/Runtime/SymbolTable.cs @@ -36,7 +36,7 @@ public class SymbolTable : IEnumerable /// 符号表id internal SymbolTable(DBTrans tr, ObjectId tableId) { - DTrans = tr; + DTrans = tr; CurrentSymbolTable = DTrans.GetObject(tableId); } @@ -142,7 +142,7 @@ private static void Remove(TRecord record) public void Remove(string name) { TRecord record = GetRecord(name); - if (record != null) + if (record is not null) { Remove(record); } @@ -155,7 +155,7 @@ public void Remove(string name) public void Remove(ObjectId id) { TRecord record = GetRecord(id); - if (record != null) + if (record is not null) { Remove(record); } @@ -185,7 +185,7 @@ private static void Change(TRecord record, Action action) public void Change(string name, Action action) { var record = GetRecord(name); - if (record != null) + if (record is not null) { Change(record, action); } @@ -198,7 +198,7 @@ public void Change(string name, Action action) public void Change(ObjectId id, Action action) { var record = GetRecord(id); - if (record != null) + if (record is not null) { Change(record, action); } diff --git a/src/IFoxCAD.WPF/DependencyObjectExtensions.cs b/src/IFoxCAD.WPF/DependencyObjectExtensions.cs index f45e259..39cd184 100644 --- a/src/IFoxCAD.WPF/DependencyObjectExtensions.cs +++ b/src/IFoxCAD.WPF/DependencyObjectExtensions.cs @@ -13,23 +13,25 @@ public static class DependencyObjectExtensions /// /// 子对象 /// 依赖属性 - public static DependencyObject GetParentObject(this DependencyObject child) + public static DependencyObject? GetParentObject(this DependencyObject child) { - if (child == null) return null; + if (child is null) + return null; if (child is ContentElement contentElement) { - DependencyObject parent = ContentOperations.GetParent(contentElement); - if (parent != null) return parent; + var parent = ContentOperations.GetParent(contentElement); + if (parent is not null) return parent; - FrameworkContentElement fce = contentElement as FrameworkContentElement; + var fce = contentElement as FrameworkContentElement; return fce?.Parent; } if (child is FrameworkElement frameworkElement) { - DependencyObject parent = frameworkElement.Parent; - if (parent != null) return parent; + var parent = frameworkElement.Parent; + if (parent is not null) + return parent; } return VisualTreeHelper.GetParent(child); diff --git a/src/IFoxCAD.WPF/EventBindingExtension.cs b/src/IFoxCAD.WPF/EventBindingExtension.cs index 6701677..9298c07 100644 --- a/src/IFoxCAD.WPF/EventBindingExtension.cs +++ b/src/IFoxCAD.WPF/EventBindingExtension.cs @@ -34,7 +34,7 @@ public class EventBindingExtension : MarkupExtension /// public override object ProvideValue(IServiceProvider serviceProvider) { - if (serviceProvider == null) + if (serviceProvider is null) { throw new ArgumentNullException(nameof(serviceProvider)); } @@ -49,7 +49,7 @@ public override object ProvideValue(IServiceProvider serviceProvider) } var memberInfo = targetProvider.TargetProperty as MemberInfo; - if (memberInfo == null) + if (memberInfo is null) { throw new InvalidOperationException(); } @@ -69,22 +69,22 @@ public override object ProvideValue(IServiceProvider serviceProvider) return CreateHandler(memberInfo, Command, targetObject.GetType()); } - + private Type GetEventHandlerType(MemberInfo memberInfo) { Type eventHandlerType = null; if (memberInfo is EventInfo) { - var info = memberInfo as EventInfo; - var eventInfo = info; + var info = memberInfo as EventInfo; + var eventInfo = info; eventHandlerType = eventInfo.EventHandlerType; } else if (memberInfo is MethodInfo) { - var info = memberInfo as MethodInfo; - var methodInfo = info; + var info = memberInfo as MethodInfo; + var methodInfo = info; ParameterInfo[] pars = methodInfo.GetParameters(); - eventHandlerType = pars[1].ParameterType; + eventHandlerType = pars[1].ParameterType; } return eventHandlerType; @@ -94,7 +94,7 @@ private object CreateHandler(MemberInfo memberInfo, string cmdName, Type targetT { Type eventHandlerType = GetEventHandlerType(memberInfo); - if (eventHandlerType == null) return null; + if (eventHandlerType is null) return null; var handlerInfo = eventHandlerType.GetMethod("Invoke"); var method = new DynamicMethod("", handlerInfo.ReturnType, @@ -108,7 +108,7 @@ private object CreateHandler(MemberInfo memberInfo, string cmdName, Type targetT gen.Emit(OpCodes.Ldarg, 0); gen.Emit(OpCodes.Ldarg, 1); gen.Emit(OpCodes.Ldstr, cmdName); - if (CommandParameter == null) + if (CommandParameter is null) { gen.Emit(OpCodes.Ldnull); } @@ -138,7 +138,7 @@ static void Handler(object sender, object args) public static void HandlerIntern(object sender, object args, string cmdName, string commandParameter) { var fe = sender as FrameworkElement; - if (fe != null) + if (fe is not null) { ICommand cmd = GetCommand(fe, cmdName); object commandParam = null; @@ -146,7 +146,7 @@ public static void HandlerIntern(object sender, object args, string cmdName, str { commandParam = GetCommandParameter(fe, args, commandParameter); } - if ((cmd != null) && cmd.CanExecute(commandParam)) + if ((cmd is not null) && cmd.CanExecute(commandParam)) { cmd.Execute(commandParam); } @@ -156,18 +156,15 @@ public static void HandlerIntern(object sender, object args, string cmdName, str internal static ICommand GetCommand(FrameworkElement target, string cmdName) { var vm = FindViewModel(target); - if (vm == null) return null; + if (vm is null) return null; var vmType = vm.GetType(); var cmdProp = vmType.GetProperty(cmdName); - if (cmdProp != null) - { + if (cmdProp is not null) return cmdProp.GetValue(vm) as ICommand; - } #if DEBUG throw new Exception("EventBinding path error: '" + cmdName + "' property not found on '" + vmType + "' 'DelegateCommand'"); #endif - return null; } @@ -191,28 +188,29 @@ internal static object GetCommandParameter(FrameworkElement target, object args, return ret; } - internal static ViewModelBase FindViewModel(FrameworkElement target) + internal static ViewModelBase? FindViewModel(FrameworkElement? target) { - if (target == null) return null; + if (target is null) + return null; - if (target.DataContext is ViewModelBase vm) return vm; + if (target.DataContext is ViewModelBase vm) + return vm; var parent = target.GetParentObject() as FrameworkElement; - return FindViewModel(parent); } internal static object FollowPropertyPath(object target, string path, Type valueType = null) { - if (target == null) throw new ArgumentNullException(nameof(target)); - if (path == null) throw new ArgumentNullException(nameof(path)); + if (target is null) throw new ArgumentNullException(nameof(target)); + if (path is null) throw new ArgumentNullException(nameof(path)); Type currentType = valueType ?? target.GetType(); foreach (string propertyName in path.Split('.')) { PropertyInfo property = currentType.GetProperty(propertyName); - if (property == null) throw new NullReferenceException("property"); + if (property is null) throw new NullReferenceException("property"); target = property.GetValue(target); currentType = property.PropertyType; diff --git a/src/IFoxCAD.WPF/IFoxCAD.WPF.csproj b/src/IFoxCAD.WPF/IFoxCAD.WPF.csproj index 8690b9e..297d6ce 100644 --- a/src/IFoxCAD.WPF/IFoxCAD.WPF.csproj +++ b/src/IFoxCAD.WPF/IFoxCAD.WPF.csproj @@ -1,39 +1,43 @@  - - - NET45;NET46;NET47;NET48 - true - true - true - true - 0.1.0 - xsfhlzh;vicwjb - InspireFunction - WPF的简单MVVM模式开发类库 - InspireFunction - LICENSE - https://gitee.com/inspirefunction/ifoxcad - https://gitee.com/inspirefunction/ifoxcad.git - IFoxCAD;C#;NET;WPF;MVVM - git - 基于NFOX类库的重构版本 - preview - + + + preview + enable - - DEBUG;TRACE - + + NET45;NET46;NET47;NET48; - - - + true + true + true + true + 0.1.0 + xsfhlzh;vicwjb + InspireFunction + WPF的简单MVVM模式开发类库 + InspireFunction + LICENSE + https://gitee.com/inspirefunction/ifoxcad + https://gitee.com/inspirefunction/ifoxcad.git + IFoxCAD;C#;NET;WPF;MVVM + git + 基于NFOX类库的重构版本 + - - - True - - - + + DEBUG;TRACE + + + + + + + + + True + + + diff --git a/src/IFoxCAD.WPF/RelayCommand.cs b/src/IFoxCAD.WPF/RelayCommand.cs index 338c515..cb4f118 100644 --- a/src/IFoxCAD.WPF/RelayCommand.cs +++ b/src/IFoxCAD.WPF/RelayCommand.cs @@ -30,7 +30,7 @@ public RelayCommand(Action execute):this(execute,null) /// execute public RelayCommand(Action execute,Func canExecute) { - _execute = execute ?? throw new ArgumentNullException(nameof(execute)); + _execute = execute ?? throw new ArgumentNullException(nameof(execute)); _canExecute = canExecute; } @@ -41,14 +41,14 @@ public event EventHandler CanExecuteChanged { add { - if (_canExecute != null) + if (_canExecute is not null) { CommandManager.RequerySuggested += value; } } remove { - if (_canExecute != null) + if (_canExecute is not null) { CommandManager.RequerySuggested -= value; } @@ -64,7 +64,7 @@ public event EventHandler CanExecuteChanged [DebuggerStepThrough] public bool CanExecute(object parameter) { - return _canExecute == null ? true : _canExecute(parameter); + return _canExecute is null ? true : _canExecute(parameter); } /// /// 定义在调用此命令时要调用的方法。 @@ -93,7 +93,7 @@ public RelayCommand(Action execute) : this(execute, (o)=>true) { } - + /// /// 初始化 类。 /// @@ -102,7 +102,7 @@ public RelayCommand(Action execute) : this(execute, (o)=>true) /// execute public RelayCommand(Action execute, Func canExecute) { - _execute = execute ?? throw new ArgumentNullException(nameof(execute)); + _execute = execute ?? throw new ArgumentNullException(nameof(execute)); _canExecute = canExecute; } /// @@ -112,14 +112,14 @@ public event EventHandler CanExecuteChanged { add { - if (_canExecute != null) + if (_canExecute is not null) { CommandManager.RequerySuggested += value; } } remove { - if (_canExecute != null) + if (_canExecute is not null) { CommandManager.RequerySuggested -= value; } @@ -134,7 +134,7 @@ public event EventHandler CanExecuteChanged /// public bool CanExecute(object parameter) { - if (_canExecute == null) + if (_canExecute is null) { return true; } @@ -146,7 +146,7 @@ public bool CanExecute(object parameter) /// 此命令使用的数据。 如果此命令不需要传递数据,则该对象可以设置为 。 public void Execute(object parameter) { - if (_execute != null && CanExecute(parameter)) + if (_execute is not null && CanExecute(parameter)) { _execute((T)parameter); } @@ -164,11 +164,11 @@ public class EventCommand : TriggerAction /// 要执行的动作参数, 如果动作为提供参数,就设置为null protected override void Invoke(object parameter) { - if (CommandParameter != null) + if (CommandParameter is not null) { parameter = CommandParameter; } - if (Command != null) + if (Command is not null) { Command.Execute(parameter); } diff --git a/tests/DBTrans.test/Properties/launchSettings.json b/tests/Test/Properties/launchSettings.json similarity index 100% rename from tests/DBTrans.test/Properties/launchSettings.json rename to tests/Test/Properties/launchSettings.json diff --git a/tests/DBTrans.test/Test.cs b/tests/Test/Test.cs similarity index 99% rename from tests/DBTrans.test/Test.cs rename to tests/Test/Test.cs index c261a33..78746f4 100644 --- a/tests/DBTrans.test/Test.cs +++ b/tests/Test/Test.cs @@ -79,12 +79,11 @@ public void draCircle() Circle circle3 = EntityEx.CreateCircle(new Point3d(-2, 0, 0), new Point3d(0, 0, 0), new Point3d(2, 0, 0));//起点,圆心,终点,失败 tr.CurrentSpace.AddEntity(circle1, circle2); if (circle3 is not null) - { tr.CurrentSpace.AddEntity(circle3); - } else { tr.Editor.WriteMessage("三点画圆失败"); + return; } tr.CurrentSpace.AddEntity(circle3); tr.CurrentSpace.AddCircle(new Point3d(0, 0, 0), new Point3d(1, 1, 0), new Point3d(2, 0, 0));//三点画圆,成功 @@ -98,7 +97,7 @@ public void Layertest() tr.LayerTable.Add("1"); tr.LayerTable.Add("2", lt => { - lt.Color = Color.FromColorIndex(ColorMethod.ByColor, 1); + lt.Color = Color.FromColorIndex(ColorMethod.ByColor, 1); lt.LineWeight = LineWeight.LineWeight030; }); diff --git a/tests/DBTrans.test/DBTrans.test.csproj b/tests/Test/Test.csproj similarity index 58% rename from tests/DBTrans.test/DBTrans.test.csproj rename to tests/Test/Test.csproj index d856f39..fa8ed34 100644 --- a/tests/DBTrans.test/DBTrans.test.csproj +++ b/tests/Test/Test.csproj @@ -3,18 +3,31 @@ preview + enable + 1.0.0.* 1.0.0.0 False git - - NET45;NET46;NET47;NET48 + + NET45; true true + + + + + + + + + + + diff --git a/tests/DBTrans.test/testConvexHull.cs b/tests/Test/testConvexHull.cs similarity index 100% rename from tests/DBTrans.test/testConvexHull.cs rename to tests/Test/testConvexHull.cs diff --git a/tests/DBTrans.test/testeditor.cs b/tests/Test/testeditor.cs similarity index 100% rename from tests/DBTrans.test/testeditor.cs rename to tests/Test/testeditor.cs diff --git a/tests/DBTrans.test/testenv.cs b/tests/Test/testenv.cs similarity index 100% rename from tests/DBTrans.test/testenv.cs rename to tests/Test/testenv.cs diff --git a/tests/DBTrans.test/testselectfilter.cs b/tests/Test/testselectfilter.cs similarity index 100% rename from tests/DBTrans.test/testselectfilter.cs rename to tests/Test/testselectfilter.cs diff --git a/tests/DBTrans.test/wpf/TestView.xaml b/tests/Test/wpf/TestView.xaml similarity index 100% rename from tests/DBTrans.test/wpf/TestView.xaml rename to tests/Test/wpf/TestView.xaml diff --git a/tests/DBTrans.test/wpf/TestView.xaml.cs b/tests/Test/wpf/TestView.xaml.cs similarity index 100% rename from tests/DBTrans.test/wpf/TestView.xaml.cs rename to tests/Test/wpf/TestView.xaml.cs diff --git a/tests/DBTrans.test/wpf/TestViewModel.cs b/tests/Test/wpf/TestViewModel.cs similarity index 100% rename from tests/DBTrans.test/wpf/TestViewModel.cs rename to tests/Test/wpf/TestViewModel.cs -- Gitee From 6ef2c162617262ebfbe22eb626aa3dd4f0268d90 Mon Sep 17 00:00:00 2001 From: liuqihong <540762622@qq.com> Date: Tue, 29 Mar 2022 13:02:45 +0000 Subject: [PATCH 061/675] =?UTF-8?q?update=20src/IFoxCAD.Basal/LoopList.cs.?= =?UTF-8?q?=20=E5=A2=9E=E5=8A=A0AddRange?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/IFoxCAD.Basal/LoopList.cs | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/src/IFoxCAD.Basal/LoopList.cs b/src/IFoxCAD.Basal/LoopList.cs index c0a64f8..2db7af3 100644 --- a/src/IFoxCAD.Basal/LoopList.cs +++ b/src/IFoxCAD.Basal/LoopList.cs @@ -1,4 +1,4 @@ -namespace IFoxCAD.Basal; +namespace IFoxCAD.Basal; /// /// 环链表节点 @@ -380,6 +380,17 @@ public LoopListNode AddLast(T value) return Add(value); } + /// + /// 容器内容全部加入到末尾 + /// + /// + public void AddRange(IEnumerable list) + { + var ge = list.GetEnumerator(); + while (ge.MoveNext()) + Add(ge.Current); + } + /// /// 前面增加节点 /// -- Gitee From 46f10bb2b99e4d1f7b06af2b38caa361faf81c2f Mon Sep 17 00:00:00 2001 From: liuqihong <540762622@qq.com> Date: Tue, 29 Mar 2022 21:03:51 +0800 Subject: [PATCH 062/675] =?UTF-8?q?=E5=A2=9E=E5=8A=A0=E6=B3=A8=E9=87=8A?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/IFoxCAD.Basal/LoopList.cs | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/IFoxCAD.Basal/LoopList.cs b/src/IFoxCAD.Basal/LoopList.cs index fca9931..2c0b088 100644 --- a/src/IFoxCAD.Basal/LoopList.cs +++ b/src/IFoxCAD.Basal/LoopList.cs @@ -373,6 +373,11 @@ public LoopListNode AddLast(T value) return Add(value); } + + /// + /// 容器内容全部加入到末尾 + /// + /// public void AddRange(IEnumerable list) { var ge = list.GetEnumerator(); -- Gitee From 85ea824c8bdcb227c9a74fa4bf9587dcc8180e43 Mon Sep 17 00:00:00 2001 From: liuqihong <540762622@qq.com> Date: Mon, 4 Apr 2022 04:37:29 +0800 Subject: [PATCH 063/675] =?UTF-8?q?=E4=BF=AE=E6=94=B9=E8=B0=83=E8=AF=95?= =?UTF-8?q?=E5=91=BD=E4=BB=A4=E9=94=99=E8=AF=AF?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/IFoxCAD.Cad/ExtensionMethod/CurveEx.cs | 1014 ----------------- .../ExtensionMethod/SelectionSetEx.cs | 3 + tests/Test/Test.cs | 40 +- 3 files changed, 22 insertions(+), 1035 deletions(-) delete mode 100644 src/IFoxCAD.Cad/ExtensionMethod/CurveEx.cs diff --git a/src/IFoxCAD.Cad/ExtensionMethod/CurveEx.cs b/src/IFoxCAD.Cad/ExtensionMethod/CurveEx.cs deleted file mode 100644 index 31b3027..0000000 --- a/src/IFoxCAD.Cad/ExtensionMethod/CurveEx.cs +++ /dev/null @@ -1,1014 +0,0 @@ -namespace IFoxCAD.Cad; - -using IFoxCAD.Basal; -/// -/// 实体类曲线扩展类 -/// -public static class CurveEx -{ - /// - /// 曲线长度 - /// - /// 曲线 - /// 长度 - public static double GetLength(this Curve curve) - { - return - curve.GetDistanceAtParameter(curve.EndParam); - } - - /// - /// 获取分割曲线集合 - /// - /// 曲线 - /// 打断参数表 - /// 打断后曲线的集合 - public static IEnumerable GetSplitCurves(this Curve curve, IEnumerable pars) - { - return - curve - .GetSplitCurves(new DoubleCollection(pars.ToArray())) - .Cast(); - } - - /// - /// 获取分割曲线集合 - /// - /// 曲线 - /// 打断点表 - /// 打断后曲线的集合 - public static IEnumerable GetSplitCurves(this Curve curve, IEnumerable points) - { - return - curve - .GetSplitCurves(new Point3dCollection(points.ToArray())) - .Cast(); - } - /// - /// 边节点 - /// - private struct EdgeItem : IEquatable - { - public Edge Edge; - public bool Forward; - - public EdgeItem(Edge edge, bool forward) - { - Edge = edge; - Forward = forward; - } - - public CompositeCurve3d? GetCurve() - { - var cc3d = Edge.Curve; - if (Forward) - { - return cc3d; - } - else - { - cc3d = cc3d.Clone() as CompositeCurve3d; - return cc3d?.GetReverseParameterCurve() as CompositeCurve3d; - } - } - - public bool Equals(EdgeItem other) - { - return - Edge == other.Edge && - Forward == other.Forward; - } - - public void FindRegion(List edges, List> regions) - { - var region = new LoopList(); - var edgeItem = this; - region.Add(edgeItem); - var edgeItem2 = this.GetNext(edges); - if (edgeItem2.Edge is not null) - { - bool hasList = false; - foreach (var edgeList2 in regions) - { - var node = edgeList2.GetNode(e => e.Equals(edgeItem)); - //var node = edgeList2.Find(edgeItem); - if (node is not null && node != edgeList2.Last) - { - if (node.Next!.Value.Equals(edgeItem2)) - { - hasList = true; - break; - } - } - } - if (!hasList) - { - while (edgeItem2.Edge is not null) - { - if (edgeItem2.Edge == edgeItem.Edge) - break; - region.Add(edgeItem2); //上一条语句判断失误,导致不停的将相同的值加入region,不能退出循环 - edgeItem2 = edgeItem2.GetNext(edges); - } - if (edgeItem2.Edge == edgeItem.Edge) - regions.Add(region); - } - } - } - - public EdgeItem GetNext(List edges) - { - Vector3d vec; - int next; - if (Forward) - { - vec = Edge.GetEndVector(); - next = Edge.EndIndex; - } - else - { - vec = Edge.GetStartVector(); - next = Edge.StartIndex; - } - - EdgeItem item = new(); - Vector3d vec2, vec3 = new(); - double angle = 0; - bool hasNext = false; - bool forward = false; - foreach (var edge in edges) - { - if (edge.IsNext(Edge, next, ref vec3, ref forward)) - { - if (hasNext) - { - var angle2 = vec.GetAngleTo(vec3, Vector3d.ZAxis); - if (angle2 < angle) - { - vec2 = vec3; - angle = angle2; - item.Edge = edge; - item.Forward = forward; - } - } - else - { - vec2 = vec3; - angle = vec.GetAngleTo(vec2, Vector3d.ZAxis); - item.Edge = edge; - item.Forward = forward; - hasNext = true; - } - } - } - return item; - } - - public override string ToString() - { - return - Forward ? - string.Format("{0}-{1}", Edge.StartIndex, Edge.EndIndex) : - string.Format("{0}-{1}", Edge.EndIndex, Edge.StartIndex); - } - - public override bool Equals(object obj) - { - return obj is EdgeItem item && Equals(item); - } - - public override int GetHashCode() - { - return this.ToString().GetHashCode(); - } - } - /// - /// 边 - /// - private class Edge - { - public CompositeCurve3d Curve; - public int StartIndex; - public int EndIndex; - public Edge(CompositeCurve3d curve) - { - Curve = curve; - } - public Vector3d GetStartVector() - { - var inter = Curve.GetInterval(); - PointOnCurve3d poc = new(Curve, inter.LowerBound); - return poc.GetDerivative(1); - } - - public Vector3d GetEndVector() - { - var inter = Curve.GetInterval(); - PointOnCurve3d poc = new(Curve, inter.UpperBound); - return -poc.GetDerivative(1); - } - - public bool IsNext(Edge edge, int index, ref Vector3d vec, ref bool forward) - { - if (edge != this) - { - if (StartIndex == index) - { - vec = GetStartVector(); - forward = true; - return true; - } - else if (EndIndex == index) - { - vec = GetEndVector(); - forward = false; - return true; - } - } - return false; - } - } - /// - /// 获取曲线集所围成的封闭区域的曲线集 - /// - /// 曲线集 - /// 曲线集 - public static List Topo(List curves) - { - //首先按交点分解为Ge曲线集 - var geCurves = new List(); - var paramss = new List>(); - - foreach (var curve in curves) - { - var cc3d = curve.ToCompositeCurve3d(); - if (cc3d is not null) - { - geCurves.Add(cc3d); - paramss.Add(new List()); - } - } - - var edges = new List(); - var cci3d = new CurveCurveIntersector3d(); - var newCurves = new List(); - - for (int i = 0; i < curves.Count; i++) - { - var gc1 = geCurves[i]; - var pars1 = paramss[i]; - for (int j = i; j < curves.Count; j++) - { - var gc2 = geCurves[j]; - var pars2 = paramss[j]; - - cci3d.Set(gc1, gc2, Vector3d.ZAxis); - - for (int k = 0; k < cci3d.NumberOfIntersectionPoints; k++) - { - var pars = cci3d.GetIntersectionParameters(k); - pars1.Add(pars[0]); - pars2.Add(pars[1]); - } - } - - if (pars1.Count > 0) - { - var c3ds = gc1.GetSplitCurves(pars1); - if (c3ds.Count > 0) - { - edges.AddRange(c3ds.Select(c => new Edge(c))); - } - else if (gc1.IsClosed()) - { - newCurves.Add(gc1.ToCurve()!); - } - else - { - edges.Add(new Edge(gc1)); - } - } - else if (gc1.IsClosed()) - { - newCurves.Add(gc1.ToCurve()!); - } - } - - //构建边的邻接表 - var knots = new List(); - var nums = new List(); - var closedEdges = new List(); - - foreach (var edge in edges) - { - if (edge.Curve.IsClosed()) - { - closedEdges.Add(edge); - } - else - { - if (knots.Contains(edge.Curve.StartPoint)) - { - edge.StartIndex = - knots.IndexOf(edge.Curve.StartPoint); - nums[edge.StartIndex]++; - } - else - { - knots.Add(edge.Curve.StartPoint); - nums.Add(1); - edge.StartIndex = knots.Count - 1; - } - - if (knots.Contains(edge.Curve.EndPoint)) - { - edge.EndIndex = - knots.IndexOf(edge.Curve.EndPoint); - nums[edge.EndIndex]++; - } - else - { - knots.Add(edge.Curve.EndPoint); - nums.Add(1); - edge.EndIndex = knots.Count - 1; - } - } - } - - newCurves.AddRange(closedEdges.Select(e => e.Curve.ToCurve())!); - - edges = - edges - .Except(closedEdges) - .Where(e => nums[e.StartIndex] > 1 && nums[e.EndIndex] > 1) - .ToList(); - - foreach (var edge in edges.Except(closedEdges)) - { - if (nums[edge.StartIndex] == 1 || nums[edge.EndIndex] == 1) - { - if (nums[edge.StartIndex] == 1 && nums[edge.EndIndex] == 1) - { - nums[edge.StartIndex] = 0; - nums[edge.EndIndex] = 0; - } - else - { - int next = -1; - if (nums[edge.StartIndex] == 1) - { - nums[edge.StartIndex] = 0; - nums[next = edge.EndIndex]--; - } - else - { - nums[edge.EndIndex] = 0; - nums[next = edge.StartIndex]--; - } - } - } - } - - var regions = new List>(); - foreach (var edge in edges) - { // 这里有bug,两个内接的矩形会卡死 - var edgeItem = new EdgeItem(edge, true); - edgeItem.FindRegion(edges, regions); // 经测试是这里卡住了 - edgeItem = new EdgeItem(edge, false); - edgeItem.FindRegion(edges, regions); - } - - for (int i = 0; i < regions.Count; i++) - { - for (int j = i + 1; j < regions.Count;) - { - bool eq = false; - if (regions[i].Count == regions[j].Count) - { - var node = regions[i].First; - var curve = node!.Value.Edge.Curve; - var node2 = regions[j].GetNode(e => e.Edge.Curve == curve); - //var node2 = regions[j].Find(node.Value); - if (node2 is not null) - { - eq = true; - var b = node.Value.Forward; - var b2 = node2.Value.Forward; - for (int k = 1; k < regions[i].Count; k++) - { - node = node.GetNext(b); - node2 = node2.GetNext(b2); - if (node!.Value.Edge.Curve != node2!.Value.Edge.Curve) - { - eq = false; - break; - } - } - } - } - if (eq) - regions.RemoveAt(j); - else - j++; - } - } - - foreach (var region in regions) - { - var cs3ds = - region - .Select(e => e.GetCurve()) - .ToArray(); - newCurves.Add(new CompositeCurve3d(cs3ds).ToCurve()!); - } - - return newCurves; - } - - /// - /// 曲线打断 - /// - /// 曲线列表 - /// 打断后的曲线列表 - public static List BreakCurve(this List curves) - { - var geCurves = new List(); // 存储曲线转换后的复合曲线 - var paramss = new List>(); // 存储每个曲线的交点参数值 - - foreach (var curve in curves) - { - var cc3d = curve.ToCompositeCurve3d(); - if (cc3d is not null) - { - geCurves.Add(cc3d); - paramss.Add(new List()); - } - } - - //var oldCurves = new List(); - var newCurves = new List(); - var cci3d = new CurveCurveIntersector3d(); - - for (int i = 0; i < curves.Count; i++) - { - var gc1 = geCurves[i]; - var pars1 = paramss[i]; //引用 - for (int j = i; j < curves.Count; j++) - { - var gc2 = geCurves[j]; - var pars2 = paramss[j]; // 引用 - - cci3d.Set(gc1, gc2, Vector3d.ZAxis); - - for (int k = 0; k < cci3d.NumberOfIntersectionPoints; k++) - { - var pars = cci3d.GetIntersectionParameters(k); - pars1.Add(pars[0]); // 引用修改会同步到源对象 - pars2.Add(pars[1]); // 引用修改会同步到源对象 - } - } - - if (pars1.Count > 0) - { - var c3ds = gc1.GetSplitCurves(pars1); - if (c3ds.Count > 1) - { - foreach (CompositeCurve3d c3d in c3ds) - { - var c = c3d.ToCurve(); - if (c is not null) - { - c.SetPropertiesFrom(curves[i]); - newCurves.Add(c); - } - } - //oldCurves.Add(curves[i]); - } - } - } - - return newCurves; - } - - //转换DBCurve为GeCurved - - #region Curve - - /// - /// 将曲线转换为ge曲线,此函数将在未来淘汰,二惊加油 - /// - /// 曲线 - /// ge曲线 - [Obsolete("请使用Cad自带的 GetGeCurve 函数!")] - public static Curve3d? ToCurve3d(this Curve curve) - { - return curve switch - { - Line li => ToCurve3d(li), - Circle ci => ToCurve3d(ci), - Arc arc => ToCurve3d(arc), - Ellipse el => ToCurve3d(el), - Polyline pl => ToCurve3d(pl), - Polyline2d pl2 => ToCurve3d(pl2), - Polyline3d pl3 => ToCurve3d(pl3), - Spline sp => ToCurve3d(sp), - _ => null - }; - } - - /// - /// 将曲线转换为复合曲线 - /// - /// 曲线 - /// 复合曲线 - public static CompositeCurve3d? ToCompositeCurve3d(this Curve curve) - { - return curve switch - { - Line li => new CompositeCurve3d(new Curve3d[] { ToCurve3d(li) }), - Circle ci => new CompositeCurve3d(new Curve3d[] { ToCurve3d(ci) }), - Arc arc => new CompositeCurve3d(new Curve3d[] { ToCurve3d(arc) }), - Ellipse el => new CompositeCurve3d(new Curve3d[] { ToCurve3d(el) }), - Polyline pl => new CompositeCurve3d(new Curve3d[] { ToCurve3d(pl) }), - - Polyline2d pl2 => new CompositeCurve3d(new Curve3d[] { ToCurve3d(pl2)! }), - Polyline3d pl3 => new CompositeCurve3d(new Curve3d[] { ToCurve3d(pl3) }), - Spline sp => new CompositeCurve3d(new Curve3d[] { ToCurve3d(sp) }), - _ => null - }; - } - - /// - /// 将曲线转换为Nurb曲线 - /// - /// 曲线 - /// Nurb曲线 - public static NurbCurve3d? ToNurbCurve3d(this Curve curve) - { - return curve switch - { - Line li => ToNurbCurve3d(li), - Circle ci => ToNurbCurve3d(ci), - Arc arc => ToNurbCurve3d(arc), - Ellipse el => ToNurbCurve3d(el), - Polyline pl => ToNurbCurve3d(pl), - Polyline2d pl2 => ToNurbCurve3d(pl2), - Polyline3d pl3 => ToNurbCurve3d(pl3), - Spline sp => ToNurbCurve3d(sp), - _ => null - }; - } - - #region Line - - /// - /// 将直线转换为ge直线 - /// - /// 直线 - /// ge直线 - public static LineSegment3d ToCurve3d(this Line line) - { - return new LineSegment3d(line.StartPoint, line.EndPoint); - } - - /// - /// 将直线转换为Nurb曲线 - /// - /// 直线 - /// Nurb曲线 - public static NurbCurve3d ToNurbCurve3d(this Line line) - { - return new NurbCurve3d(ToCurve3d(line)); - } - - #endregion Line - - #region Circle - - /// - /// 将圆转换为ge圆弧曲线 - /// - /// 圆 - /// ge圆弧曲线 - public static CircularArc3d ToCurve3d(this Circle cir) - { - return - new CircularArc3d( - cir.Center, - cir.Normal, - cir.Radius); - } - - /// - /// 将圆转换为ge椭圆曲线 - /// - /// 圆 - /// ge椭圆曲线 - public static EllipticalArc3d ToEllipticalArc3d(this Circle cir) - { - return ToCurve3d(cir).ToEllipticalArc3d(); - } - - /// - /// 将圆转换为Nurb曲线 - /// - /// 圆 - /// Nurb曲线 - public static NurbCurve3d ToNurbCurve3d(this Circle cir) - { - return new NurbCurve3d(ToEllipticalArc3d(cir)); - } - - #endregion Circle - - #region Arc - - /// - /// 将圆弧转换为ge圆弧曲线 - /// - /// 圆弧 - /// ge圆弧曲线 - public static CircularArc3d ToCurve3d(this Arc arc) - { - Plane plane = new(arc.Center, arc.Normal); - - return - new CircularArc3d( - arc.Center, - arc.Normal, - plane.GetCoordinateSystem().Xaxis, - arc.Radius, - arc.StartAngle, - arc.EndAngle - ); - } - - /// - /// 将圆弧转换为ge椭圆曲线 - /// - /// 圆弧 - /// ge椭圆曲线 - public static EllipticalArc3d ToEllipticalArc3d(this Arc arc) - { - return ToCurve3d(arc).ToEllipticalArc3d(); - } - - /// - /// 将圆弧转换为三维Nurb曲线 - /// - /// 圆弧 - /// 三维Nurb曲线 - public static NurbCurve3d ToNurbCurve3d(this Arc arc) - { - return new NurbCurve3d(ToEllipticalArc3d(arc)); - } - - #endregion Arc - - #region Ellipse - - /// - /// 将椭圆转换为三维ge椭圆曲线 - /// - /// 椭圆 - /// 三维ge椭圆曲线 - public static EllipticalArc3d ToCurve3d(this Ellipse ell) - { - return - new EllipticalArc3d( - ell.Center, - ell.MajorAxis.GetNormal(), - ell.MinorAxis.GetNormal(), - ell.MajorRadius, - ell.MinorRadius, - ell.StartParam, - ell.EndParam); - } - - /// - /// 将椭圆转换为三维Nurb曲线 - /// - /// 椭圆 - /// 三维Nurb曲线 - public static NurbCurve3d ToNurbCurve3d(this Ellipse ell) - { - return new NurbCurve3d(ToCurve3d(ell)); - } - - #endregion Ellipse - - #region Spline - - /// - /// 将样条曲线转换为三维Nurb曲线 - /// - /// 样条曲线 - /// 三维Nurb曲线 - public static NurbCurve3d ToCurve3d(this Spline spl) - { - NurbCurve3d nc3d; - NurbsData ndata = spl.NurbsData; - KnotCollection knots = new(); - foreach (Double knot in ndata.GetKnots()) - knots.Add(knot); - - if (ndata.Rational) - { - nc3d = - new NurbCurve3d( - ndata.Degree, - knots, - ndata.GetControlPoints(), - ndata.GetWeights(), - ndata.Periodic); - } - else - { - nc3d = - new NurbCurve3d( - ndata.Degree, - knots, - ndata.GetControlPoints(), - ndata.Periodic); - } - - if (spl.HasFitData) - { - var fdata = spl.FitData; - var vec = new Vector3d(); - if (fdata.TangentsExist && (fdata.StartTangent != vec || fdata.EndTangent != vec)) - nc3d.SetFitData(fdata.GetFitPoints(), fdata.StartTangent, fdata.EndTangent); - } - return nc3d; - } - - #endregion Spline - - #region Polyline2d - - /// - /// 将二维多段线转换为三维ge曲线 - /// - /// 二维多段线 - /// 三维ge曲线 - public static Curve3d? ToCurve3d(this Polyline2d pl2d) - { - switch (pl2d.PolyType) - { - case Poly2dType.SimplePoly: - case Poly2dType.FitCurvePoly: - Polyline pl = new(); - pl.ConvertFrom(pl2d, false); - return ToCurve3d(pl); - - default: - return ToNurbCurve3d(pl2d); - } - - //Polyline pl = new Polyline(); - //pl.ConvertFrom(pl2d, false); - //return ToCurve3d(pl); - } - - /// - /// 将二维多段线转换为三维Nurb曲线 - /// - /// 二维多段线 - /// 三维Nurb曲线 - public static NurbCurve3d? ToNurbCurve3d(this Polyline2d pl2d) - { - switch (pl2d.PolyType) - { - case Poly2dType.SimplePoly: - case Poly2dType.FitCurvePoly: - Polyline pl = new(); - pl.ConvertFrom(pl2d, false); - return ToNurbCurve3d(pl); - - default: - return ToCurve3d(pl2d.Spline); - } - } - - /// - /// 将二维多段线转换为三维ge多段线 - /// - /// 二维多段线 - /// 三维ge多段线 - public static PolylineCurve3d ToPolylineCurve3d(this Polyline2d pl) - { - Point3dCollection pnts = new(); - foreach (Vertex2d ver in pl) - { - pnts.Add(ver.Position); - } - return new PolylineCurve3d(pnts); - } - - #endregion Polyline2d - - #region Polyline3d - - /// - /// 将三维多段线转换为三维曲线 - /// - /// 三维多段线 - /// 三维曲线 - public static Curve3d ToCurve3d(this Polyline3d pl3d) - { - return pl3d.PolyType switch - { - Poly3dType.SimplePoly => ToPolylineCurve3d(pl3d), - _ => ToNurbCurve3d(pl3d), - }; - } - - /// - /// 将三维多段线转换为三维Nurb曲线 - /// - /// 三维多段线 - /// 三维Nurb曲线 - public static NurbCurve3d ToNurbCurve3d(this Polyline3d pl3d) - { - return ToCurve3d(pl3d.Spline); - } - - /// - /// 将三维多段线转换为三维ge多段线 - /// - /// 三维多段线 - /// 三维ge多段线 - public static PolylineCurve3d ToPolylineCurve3d(this Polyline3d pl) - { - Point3dCollection pnts = new(); - foreach (ObjectId id in pl) - { - PolylineVertex3d ver = (PolylineVertex3d)id.GetObject(OpenMode.ForRead); - pnts.Add(ver.Position); - } - return new PolylineCurve3d(pnts); - } - - #endregion Polyline3d - - #region Polyline - - /// - /// 多段线转换为复合曲线 - /// - /// 多段线对象 - /// 复合曲线对象 - public static CompositeCurve3d ToCurve3d(this Polyline pl) - { - List c3ds = new(); - - for (int i = 0; i < pl.NumberOfVertices; i++) - { - switch (pl.GetSegmentType(i)) - { - case SegmentType.Line: - c3ds.Add(pl.GetLineSegmentAt(i)); - break; - - case SegmentType.Arc: - c3ds.Add(pl.GetArcSegmentAt(i)); - break; - - default: - break; - } - } - return new CompositeCurve3d(c3ds.ToArray()); - } - - /// - /// 多段线转换为Nurb曲线 - /// - /// 多段线 - /// Nurb曲线 - public static NurbCurve3d? ToNurbCurve3d(this Polyline pl) - { - NurbCurve3d? nc3d = null; - for (int i = 0; i < pl.NumberOfVertices; i++) - { - NurbCurve3d? nc3dtemp = null; - switch (pl.GetSegmentType(i)) - { - case SegmentType.Line: - nc3dtemp = new NurbCurve3d(pl.GetLineSegmentAt(i)); - break; - - case SegmentType.Arc: - nc3dtemp = pl.GetArcSegmentAt(i).ToNurbCurve3d(); - break; - - default: - break; - } - if (nc3d is null) - { - nc3d = nc3dtemp; - } - else if (nc3dtemp is not null) - { - nc3d.JoinWith(nc3dtemp); - } - } - return nc3d; - } - - /// - /// 为优化多段线倒角 - /// - /// 优化多段线 - /// 顶点索引号 - /// 倒角半径 - /// 倒角类型 - public static void ChamferAt(this Polyline polyline, int index, double radius, bool isFillet) - { - if (index < 1 || index > polyline.NumberOfVertices - 2) - throw new System.Exception("错误的索引号"); - - if (polyline.GetSegmentType(index - 1) != SegmentType.Line || polyline.GetSegmentType(index) != SegmentType.Line) - throw new System.Exception("非直线段不能倒角"); - - //获取当前索引号的前后两段直线,并组合为Ge复合曲线 - Curve3d[] c3ds = - new Curve3d[] - { - polyline.GetLineSegmentAt(index - 1), - polyline.GetLineSegmentAt(index) - }; - var cc3d = new CompositeCurve3d(c3ds); - - //试倒直角 - //子曲线的个数有三种情况: - //1、=3时倒角方向正确 - //2、=2时倒角方向相反 - //3、=0或为直线时失败 - c3ds = - cc3d.GetTrimmedOffset - ( - radius, - Vector3d.ZAxis, - OffsetCurveExtensionType.Chamfer - ); - - if (c3ds.Length > 0 && c3ds[0] is CompositeCurve3d) - { - var newcc3d = c3ds[0] as CompositeCurve3d; - c3ds = newcc3d!.GetCurves(); - if (c3ds.Length == 3) - { - c3ds = - cc3d.GetTrimmedOffset - ( - -radius, - Vector3d.ZAxis, - OffsetCurveExtensionType.Chamfer - ); - if (c3ds.Length == 0 || c3ds[0] is LineSegment3d) - { - throw new System.Exception("倒角半径过大"); - } - } - else if (c3ds.Length == 2) - { - radius = -radius; - } - } - else - { - throw new System.Exception("倒角半径过大"); - } - - //GetTrimmedOffset会生成倒角+偏移,故先反方向倒角,再倒回 - c3ds = - cc3d.GetTrimmedOffset - ( - -radius, - Vector3d.ZAxis, - OffsetCurveExtensionType.Extend - ); - OffsetCurveExtensionType type = - isFillet ? - OffsetCurveExtensionType.Fillet : OffsetCurveExtensionType.Chamfer; - c3ds = - c3ds[0].GetTrimmedOffset - ( - radius, - Vector3d.ZAxis, - type - ); - - //将结果Ge曲线转为Db曲线,并将相关的数值反映到原曲线 - Polyline? plTemp = c3ds[0].ToCurve() as Polyline; - polyline.RemoveVertexAt(index); - polyline.AddVertexAt(index, plTemp!.GetPoint2dAt(1), plTemp.GetBulgeAt(1), 0, 0); - polyline.AddVertexAt(index + 1, plTemp.GetPoint2dAt(2), 0, 0, 0); - } - - #endregion Polyline - - #endregion Curve -} diff --git a/src/IFoxCAD.Cad/ExtensionMethod/SelectionSetEx.cs b/src/IFoxCAD.Cad/ExtensionMethod/SelectionSetEx.cs index 9a02642..9de6ade 100644 --- a/src/IFoxCAD.Cad/ExtensionMethod/SelectionSetEx.cs +++ b/src/IFoxCAD.Cad/ExtensionMethod/SelectionSetEx.cs @@ -68,6 +68,9 @@ public static IEnumerable> GetObjectIdGroup(this Sel /// 图元集合 public static IEnumerable GetEntities(this SelectionSet ss, OpenMode openMode = OpenMode.ForRead, DBTrans? tr = default) where T : Entity { + if (ss is null) + throw new ArgumentNullException(nameof(ss)); + tr ??= DBTrans.Top; return ss diff --git a/tests/Test/Test.cs b/tests/Test/Test.cs index eac5da4..62447c1 100644 --- a/tests/Test/Test.cs +++ b/tests/Test/Test.cs @@ -20,12 +20,12 @@ public void Dbtest() { tr.Editor.WriteMessage("\ndatabase 正常"); } - + Line line = new(new Point3d(0, 0, 0), new Point3d(1, 1, 0)); Circle circle = new(new Point3d(0, 0, 0), Vector3d.ZAxis, 2); //var lienid = tr.AddEntity(line); //var cirid = tr.AddEntity(circle); - //var linent = tr.GetObject(lienid); + //var linent = tr.GetObject(lienid); //var lineent = tr.GetObject(cirid); //var linee = tr.GetObject(cirid); //经测试,类型不匹配,返回null //var dd = tr.GetObject(lienid); @@ -51,8 +51,8 @@ public void drawarc() { using var tr = new DBTrans(); Arc arc1 = EntityEx.CreateArcSCE(new Point3d(2, 0, 0), new Point3d(0, 0, 0), new Point3d(0, 2, 0));//起点,圆心,终点 - Arc arc2 = EntityEx.CreateArc(new Point3d(4, 0, 0), new Point3d(0, 0, 0), Math.PI / 2);//起点,圆心,弧度 - Arc arc3 = EntityEx.CreateArc(new Point3d(1, 0, 0), new Point3d(0, 0, 0), new Point3d(0, 1, 0));//起点,圆上一点,终点 + Arc arc2 = EntityEx.CreateArc(new Point3d(4, 0, 0), new Point3d(0, 0, 0), Math.PI / 2); //起点,圆心,弧度 + Arc arc3 = EntityEx.CreateArc(new Point3d(1, 0, 0), new Point3d(0, 0, 0), new Point3d(0, 1, 0)); //起点,圆上一点,终点 tr.CurrentSpace.AddEntity(arc1, arc2, arc3); tr.CurrentSpace.AddArc(new Point3d(0, 0, 0), new Point3d(1, 1, 0), new Point3d(2, 0, 0));//起点,圆上一点,终点 } @@ -61,7 +61,7 @@ public void drawarc() public void draCircle() { using var tr = new DBTrans(); - Circle circle1 = EntityEx.CreateCircle(new Point3d(0, 0, 0), new Point3d(1, 0, 0));//起点,终点 + Circle circle1 = EntityEx.CreateCircle(new Point3d(0, 0, 0), new Point3d(1, 0, 0)); //起点,终点 Circle circle2 = EntityEx.CreateCircle(new Point3d(-2, 0, 0), new Point3d(2, 0, 0), new Point3d(0, 2, 0));//三点画圆,成功 Circle circle3 = EntityEx.CreateCircle(new Point3d(-2, 0, 0), new Point3d(0, 0, 0), new Point3d(2, 0, 0));//起点,圆心,终点,失败 tr.CurrentSpace.AddEntity(circle1, circle2); @@ -83,16 +83,14 @@ public void Layertest() { using var tr = new DBTrans(); tr.LayerTable.Add("1"); - tr.LayerTable.Add("2", lt => - { - lt.Color = Color.FromColorIndex(ColorMethod.ByColor, 1); + tr.LayerTable.Add("2", lt => { + lt.Color = Color.FromColorIndex(ColorMethod.ByColor, 1); lt.LineWeight = LineWeight.LineWeight030; }); tr.LayerTable.Remove("3"); tr.LayerTable.Delete("0"); - tr.LayerTable.Change("4", lt => - { + tr.LayerTable.Change("4", lt => { lt.Color = Color.FromColorIndex(ColorMethod.ByColor, 2); }); } @@ -119,11 +117,11 @@ public void Layertest2() public void LayerDel() { using var tr = new DBTrans(); - Env.Editor.WriteMessage(tr.LayerTable.Delete("0").ToString()); //删除图层 0 + Env.Editor.WriteMessage(tr.LayerTable.Delete("0").ToString()); //删除图层 0 Env.Editor.WriteMessage(tr.LayerTable.Delete("Defpoints").ToString());//删除图层 Defpoints - Env.Editor.WriteMessage(tr.LayerTable.Delete("1").ToString());//删除不存在的图层 1 - Env.Editor.WriteMessage(tr.LayerTable.Delete("2").ToString());//删除有图元的图层 2 - Env.Editor.WriteMessage(tr.LayerTable.Delete("3").ToString());//删除图层 3 + Env.Editor.WriteMessage(tr.LayerTable.Delete("1").ToString()); //删除不存在的图层 1 + Env.Editor.WriteMessage(tr.LayerTable.Delete("2").ToString()); //删除有图元的图层 2 + Env.Editor.WriteMessage(tr.LayerTable.Delete("3").ToString()); //删除图层 3 tr.LayerTable.Remove("2"); //测试是否能强制删除 } @@ -224,12 +222,12 @@ public void GetXdata() var doc = Application.DocumentManager.MdiActiveDocument; var ed = doc.Editor; using var tr = new DBTrans(); - tr.RegAppTable.ForEach(id => + tr.RegAppTable.ForEach(id => id.GetObject().Name.Print()); tr.RegAppTable.GetRecords().ForEach(rec => rec.Name.Print()); tr.RegAppTable.GetRecordNames().ForEach(name => name.Print()); tr.RegAppTable.ForEach(re => re.Name.Print()); - + //var res = ed.GetEntity("\n select the entity:"); //if (res.Status == PromptStatus.OK) //{ @@ -320,14 +318,12 @@ public void TestPt() public void TestBack() { using var tr = new DBTrans(@"C:\Users\vic\Desktop\test.dwg"); - tr.ModelSpace.GetEntities().ForEach(ent => - { + tr.ModelSpace.GetEntities().ForEach(ent => { ent.ForWrite(e => e.ColorIndex = 3); }); tr.Database.SaveAs(@"C:\Users\vic\Desktop\test.dwg", DwgVersion.Current); - tr.ModelSpace.GetEntities().ForEach(ent => - { + tr.ModelSpace.GetEntities().ForEach(ent => { ent.ForWrite(e => e.ColorIndex = 4); }); tr.Database.SaveAs(@"C:\Users\vic\Desktop\test.dwg", DwgVersion.Current); @@ -349,12 +345,14 @@ public Document Getdoc() public override void Initialize() { + //文档管理器将比此接口前创建,因此此句会执行 Application.DocumentManager.MdiActiveDocument.Editor.WriteMessage("\nload...."); } public override void Terminate() { - Application.DocumentManager.MdiActiveDocument.Editor.WriteMessage("\nunload...."); + //文档管理器将比此接口前死亡,因此此句不会执行 + Application.DocumentManager.MdiActiveDocument?.Editor.WriteMessage("\nunload...."); } } -- Gitee From b62b7745c4fa8186fe0cacf3e27e958d73d84a2b Mon Sep 17 00:00:00 2001 From: liuqihong <540762622@qq.com> Date: Mon, 4 Apr 2022 04:38:23 +0800 Subject: [PATCH 064/675] =?UTF-8?q?=E4=BF=AE=E6=94=B9=E8=87=AA=E5=8A=A8?= =?UTF-8?q?=E6=89=A7=E8=A1=8C=E5=85=B3=E9=97=AD=E9=94=99=E8=AF=AF?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- tests/Test/Test.cs | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/tests/Test/Test.cs b/tests/Test/Test.cs index 62447c1..24e2fc2 100644 --- a/tests/Test/Test.cs +++ b/tests/Test/Test.cs @@ -1,6 +1,4 @@ - - -namespace test; +namespace test; public class Test : AutoRegAssem { @@ -83,14 +81,16 @@ public void Layertest() { using var tr = new DBTrans(); tr.LayerTable.Add("1"); - tr.LayerTable.Add("2", lt => { - lt.Color = Color.FromColorIndex(ColorMethod.ByColor, 1); + tr.LayerTable.Add("2", lt => + { + lt.Color = Color.FromColorIndex(ColorMethod.ByColor, 1); lt.LineWeight = LineWeight.LineWeight030; }); tr.LayerTable.Remove("3"); tr.LayerTable.Delete("0"); - tr.LayerTable.Change("4", lt => { + tr.LayerTable.Change("4", lt => + { lt.Color = Color.FromColorIndex(ColorMethod.ByColor, 2); }); } @@ -318,12 +318,14 @@ public void TestPt() public void TestBack() { using var tr = new DBTrans(@"C:\Users\vic\Desktop\test.dwg"); - tr.ModelSpace.GetEntities().ForEach(ent => { + tr.ModelSpace.GetEntities().ForEach(ent => + { ent.ForWrite(e => e.ColorIndex = 3); }); tr.Database.SaveAs(@"C:\Users\vic\Desktop\test.dwg", DwgVersion.Current); - tr.ModelSpace.GetEntities().ForEach(ent => { + tr.ModelSpace.GetEntities().ForEach(ent => + { ent.ForWrite(e => e.ColorIndex = 4); }); tr.Database.SaveAs(@"C:\Users\vic\Desktop\test.dwg", DwgVersion.Current); -- Gitee From 19b7a93f0212f8c58965f0310fab3b1396be647f Mon Sep 17 00:00:00 2001 From: liuqihong <540762622@qq.com> Date: Mon, 4 Apr 2022 04:38:44 +0800 Subject: [PATCH 065/675] =?UTF-8?q?=E5=A2=9E=E5=8A=A0=E6=9B=B2=E7=BA=BF?= =?UTF-8?q?=E7=B1=BB=E6=B3=A8=E9=87=8A?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/IFoxCAD.Cad/ExtensionMethod/CurveEx.cs | 1054 ++++++++++++++++++++ 1 file changed, 1054 insertions(+) create mode 100644 src/IFoxCAD.Cad/ExtensionMethod/CurveEx.cs diff --git a/src/IFoxCAD.Cad/ExtensionMethod/CurveEx.cs b/src/IFoxCAD.Cad/ExtensionMethod/CurveEx.cs new file mode 100644 index 0000000..39364ac --- /dev/null +++ b/src/IFoxCAD.Cad/ExtensionMethod/CurveEx.cs @@ -0,0 +1,1054 @@ +namespace IFoxCAD.Cad; + +using IFoxCAD.Basal; +using System.Diagnostics; + +/// +/// 实体类曲线扩展类 +/// +public static class CurveEx +{ + /// + /// 曲线长度 + /// + /// 曲线 + /// 长度 + public static double GetLength(this Curve curve) + { + return + curve.GetDistanceAtParameter(curve.EndParam); + } + + /// + /// 获取分割曲线集合 + /// + /// 曲线 + /// 打断参数表 + /// 打断后曲线的集合 + public static IEnumerable GetSplitCurves(this Curve curve, IEnumerable pars) + { + return + curve + .GetSplitCurves(new DoubleCollection(pars.ToArray())) + .Cast(); + } + + /// + /// 获取分割曲线集合 + /// + /// 曲线 + /// 打断点表 + /// 打断后曲线的集合 + public static IEnumerable GetSplitCurves(this Curve curve, IEnumerable points) + { + return + curve + .GetSplitCurves(new Point3dCollection(points.ToArray())) + .Cast(); + } + /// + /// 边节点 + /// + private struct EdgeItem : IEquatable + { + public Edge Edge; + public bool Forward; + + public EdgeItem(Edge edge, bool forward) + { + Edge = edge; + Forward = forward; + } + + public CompositeCurve3d? GetCurve() + { + var cc3d = Edge.Curve; + if (Forward) + { + return cc3d; + } + else + { + cc3d = cc3d.Clone() as CompositeCurve3d; + return cc3d?.GetReverseParameterCurve() as CompositeCurve3d; + } + } + + public bool Equals(EdgeItem other) + { + return Edge == other.Edge && Forward == other.Forward; + } + + /// + /// 查找区域 + /// + /// + /// + public void FindRegion(List edges, List> regions) + { + var region = new LoopList(); + var edgeItem = this; + region.Add(edgeItem); + var edgeItem2 = this.GetNext(edges); + if (edgeItem2.Edge is not null) + { + bool hasList = false; + foreach (var edgeList2 in regions) + { + var node = edgeList2.GetNode(e => e.Equals(edgeItem)); + //var node = edgeList2.Find(edgeItem); + if (node is not null && node != edgeList2.Last) + { + if (node.Next!.Value.Equals(edgeItem2)) + { + hasList = true; + break; + } + } + } + if (!hasList) + { + while (edgeItem2.Edge is not null) + { + if (edgeItem2.Edge == edgeItem.Edge) + break; + region.Add(edgeItem2); //上一条语句判断失误,导致不停的将相同的值加入region,不能退出循环 + edgeItem2 = edgeItem2.GetNext(edges); + } + if (edgeItem2.Edge == edgeItem.Edge) + regions.Add(region); + } + } + } + + public EdgeItem GetNext(List edges) + { + Vector3d vec; + int next; + if (Forward) + { + vec = Edge.GetEndVector(); + next = Edge.EndIndex; + } + else + { + vec = Edge.GetStartVector(); + next = Edge.StartIndex; + } + + EdgeItem item = new(); + Vector3d vec2, vec3 = new(); + double angle = 0; + bool hasNext = false; + bool forward = false; + foreach (var edge in edges) + { + if (edge.IsNext(Edge, next, ref vec3, ref forward)) + { + if (hasNext) + { + var angle2 = vec.GetAngleTo(vec3, Vector3d.ZAxis); + if (angle2 < angle) + { + vec2 = vec3; + angle = angle2; + item.Edge = edge; + item.Forward = forward; + } + } + else + { + vec2 = vec3; + angle = vec.GetAngleTo(vec2, Vector3d.ZAxis); + item.Edge = edge; + item.Forward = forward; + hasNext = true; + } + } + } + return item; + } + + public override string ToString() + { + return + Forward ? + string.Format("{0}-{1}", Edge.StartIndex, Edge.EndIndex) : + string.Format("{0}-{1}", Edge.EndIndex, Edge.StartIndex); + } + + public override bool Equals(object obj) + { + return obj is EdgeItem item && Equals(item); + } + + public override int GetHashCode() + { + return this.ToString().GetHashCode(); + } + } + /// + /// 边 + /// + private class Edge + { + public CompositeCurve3d Curve; + public int StartIndex; + public int EndIndex; + public Edge(CompositeCurve3d curve) + { + Curve = curve; + } + public Vector3d GetStartVector() + { + var inter = Curve.GetInterval(); + PointOnCurve3d poc = new(Curve, inter.LowerBound); + return poc.GetDerivative(1); + } + + public Vector3d GetEndVector() + { + var inter = Curve.GetInterval(); + PointOnCurve3d poc = new(Curve, inter.UpperBound); + return -poc.GetDerivative(1); + } + + public bool IsNext(Edge edge, int index, ref Vector3d vec, ref bool forward) + { + if (edge != this) + { + if (StartIndex == index) + { + vec = GetStartVector(); + forward = true; + return true; + } + else if (EndIndex == index) + { + vec = GetEndVector(); + forward = false; + return true; + } + } + return false; + } + } + /// + /// 获取曲线集所围成的封闭区域的曲线集 + /// + /// 曲线集 + /// 闭合的曲线集 + public static List Topo(List curves) + { + var edges = new List(); + var newCurves = new List(); + GetEdgesAndnewCurves(curves, edges, newCurves); + + //构建边的邻接表 + + //knots 和 nums 实际上是一个键值对(基于ArrayOfStruct思想,拆开更合适内存布局) + //knots 是不重复地将所有交点设置为节点,如果是重复,就对应 nums++ + //nums 是记录每个节点坐标被重复了几次 + var knots = new List(); + var nums = new List(); + var closedEdges = new List(); + + for (int i = 0; i < edges.Count; i++) + { + var edge = edges[i]; + if (edge.Curve.IsClosed()) + { + closedEdges.Add(edge); + continue; + } + + if (knots.Contains(edge.Curve.StartPoint)) + { + //含有就是其他曲线"共用"此交点, + //节点所在索引==共用计数索引=>将它++ + edge.StartIndex = knots.IndexOf(edge.Curve.StartPoint); + nums[edge.StartIndex]++; + } + else + { + //不含有就加入节点,共用计数也加入,边界加入节点索引 + knots.Add(edge.Curve.StartPoint); + nums.Add(1); + edge.StartIndex = knots.Count - 1; + } + + if (knots.Contains(edge.Curve.EndPoint)) + { + edge.EndIndex = knots.IndexOf(edge.Curve.EndPoint); + nums[edge.EndIndex]++; + } + else + { + knots.Add(edge.Curve.EndPoint); + nums.Add(1); + edge.EndIndex = knots.Count - 1; + } + } + + newCurves.AddRange(closedEdges.Select(e => e.Curve.ToCurve())!); + + //这里把交点只有一条曲线通过的点过滤掉了,也就是尾巴的图元, + //剩下的都是闭合的曲线连接了,每个点都至少有两条曲线通过 + edges = + edges + .Except(closedEdges) + .Where(e => nums[e.StartIndex] > 1 && nums[e.EndIndex] > 1) + .ToList(); + + //这一大坨 不用看了 注释掉也没影响貌似,而且后续也没有用 nums + // foreach (var edge in edges.Except(closedEdges)) + // { + // if (nums[edge.StartIndex] == 1 || nums[edge.EndIndex] == 1) + // { + // if (nums[edge.StartIndex] == 1 && nums[edge.EndIndex] == 1) + // { + // nums[edge.StartIndex] = 0; + // nums[edge.EndIndex] = 0; + // } + // else + // { + // int next = -1; + // if (nums[edge.StartIndex] == 1) + // { + // nums[edge.StartIndex] = 0; + // nums[next = edge.EndIndex]--; + // } + // else + // { + // nums[edge.EndIndex] = 0; + // nums[next = edge.StartIndex]--; + // } + // } + // } + // } + + var regions = new List>(); + for (int i = 0; i < edges.Count; i++) + { + var edge = edges[i]; + //TODO 这里有bug,两个内接的矩形会卡死 + var edgeItem = new EdgeItem(edge, true); + edgeItem.FindRegion(edges, regions); // 经测试是这里卡住了 + edgeItem = new EdgeItem(edge, false); + edgeItem.FindRegion(edges, regions); + } + + for (int i = 0; i < regions.Count; i++) + { + for (int j = i + 1; j < regions.Count;) + { + bool eq = false; + if (regions[i].Count == regions[j].Count) + { + var node = regions[i].First; + var curve = node!.Value.Edge.Curve; + var node2 = regions[j].GetNode(e => e.Edge.Curve == curve); + //var node2 = regions[j].Find(node.Value); + if (node2 is not null) + { + eq = true; + var b = node.Value.Forward; + var b2 = node2.Value.Forward; + for (int k = 1; k < regions[i].Count; k++) + { + node = node.GetNext(b); + node2 = node2.GetNext(b2); + if (node!.Value.Edge.Curve != node2!.Value.Edge.Curve) + { + eq = false; + break; + } + } + } + } + if (eq) + regions.RemoveAt(j); + else + j++; + } + } + + for (int i = 0; i < regions.Count; i++) + { + var cs3ds = + regions[i] + .Select(e => e.GetCurve()) + .ToArray(); + newCurves.Add(new CompositeCurve3d(cs3ds).ToCurve()!); + } + + return newCurves; + } + + /// + /// 从曲线集合分离边界(交点断分曲线的)和独立的曲线 + /// + /// + /// + /// + static void GetEdgesAndnewCurves(List curves, + List edgesOut, + List newCurvesOut) + { + //首先按交点分解为Ge曲线集 + var geCurves = new List(); + var paramss = new List>(); + + for (int i = 0; i < curves.Count; i++) + { + var cc3d = curves[i].ToCompositeCurve3d(); + if (cc3d is not null) + { + geCurves.Add(cc3d); + paramss.Add(new List()); + } + } + + var cci3d = new CurveCurveIntersector3d(); + + //遍历所有曲线,然后获取交点...此处是O(n²) + for (int i = 0; i < curves.Count; i++) + { + var gc1 = geCurves[i]; + var pars1 = paramss[i]; + //曲线a,和曲线b/c/d/e...分别交点,组成交点数组 + //第一次是aa对比,所以会怎么样呢?(交点无限个) + for (int j = i; j < curves.Count; j++) + { + var gc2 = geCurves[j]; + var pars2 = paramss[j]; + + //求交类此方法内部会重置,不需要清空,每次set都会有个新的结果 + cci3d.Set(gc1, gc2, Vector3d.ZAxis); + + //计算两条曲线的交点(多个),分别放入对应的交点参数集 + for (int k = 0; k < cci3d.NumberOfIntersectionPoints; k++) + { + var pars = cci3d.GetIntersectionParameters(k); + pars1.Add(pars[0]);//0是第一条曲线的交点参数 + pars2.Add(pars[1]);//1是第二条曲线的交点参数 + } + } + + //有交点参数 + if (pars1.Count > 0) + { + //根据交点参数断分曲线,然后获取边界 + var c3ds = gc1.GetSplitCurves(pars1); + if (c3ds.Count > 0) + { + edgesOut.AddRange(c3ds.Select(c => new Edge(c))); + } + //惊惊留:(不敢删啊...) + //狐哥写的这里出现的条件是:有曲线参数,但是切分不出来曲线...没懂为什么... + //猜测:曲线参数在头或者尾,那么交点就是直接碰头碰尾, + //也就是aa判断的时候,同一条曲线自己和自己产生的? + //参数大于0{是这些参数? 头参/尾参/参数不在曲线上?} + else if (gc1.IsClosed()) + { + newCurvesOut.Add(gc1.ToCurve()!); + } + else + { + edgesOut.Add(new Edge(gc1)); + } + } + else if (gc1.IsClosed()) + { + newCurvesOut.Add(gc1.ToCurve()!); + } + } + } + + /// + /// 曲线打断 + /// + /// 曲线列表 + /// 打断后的曲线列表 + public static List BreakCurve(this List curves) + { + var geCurves = new List(); // 存储曲线转换后的复合曲线 + var paramss = new List>(); // 存储每个曲线的交点参数值 + + foreach (var curve in curves) + { + var cc3d = curve.ToCompositeCurve3d(); + if (cc3d is not null) + { + geCurves.Add(cc3d); + paramss.Add(new List()); + } + } + + //var oldCurves = new List(); + var newCurves = new List(); + var cci3d = new CurveCurveIntersector3d(); + + for (int i = 0; i < curves.Count; i++) + { + var gc1 = geCurves[i]; + var pars1 = paramss[i]; //引用 + for (int j = i; j < curves.Count; j++) + { + var gc2 = geCurves[j]; + var pars2 = paramss[j]; // 引用 + + cci3d.Set(gc1, gc2, Vector3d.ZAxis); + + for (int k = 0; k < cci3d.NumberOfIntersectionPoints; k++) + { + var pars = cci3d.GetIntersectionParameters(k); + pars1.Add(pars[0]); // 引用修改会同步到源对象 + pars2.Add(pars[1]); // 引用修改会同步到源对象 + } + } + + if (pars1.Count > 0) + { + var c3ds = gc1.GetSplitCurves(pars1); + if (c3ds.Count > 1) + { + foreach (CompositeCurve3d c3d in c3ds) + { + var c = c3d.ToCurve(); + if (c is not null) + { + c.SetPropertiesFrom(curves[i]); + newCurves.Add(c); + } + } + //oldCurves.Add(curves[i]); + } + } + } + + return newCurves; + } + + //转换DBCurve为GeCurved + + #region Curve + + /// + /// 将曲线转换为ge曲线,此函数将在未来淘汰,二惊加油 + /// + /// 曲线 + /// ge曲线 + [Obsolete("请使用Cad自带的 GetGeCurve 函数!")] + public static Curve3d? ToCurve3d(this Curve curve) + { + return curve switch + { + Line li => ToCurve3d(li), + Circle ci => ToCurve3d(ci), + Arc arc => ToCurve3d(arc), + Ellipse el => ToCurve3d(el), + Polyline pl => ToCurve3d(pl), + Polyline2d pl2 => ToCurve3d(pl2), + Polyline3d pl3 => ToCurve3d(pl3), + Spline sp => ToCurve3d(sp), + _ => null + }; + } + + /// + /// 将曲线转换为复合曲线 + /// + /// 曲线 + /// 复合曲线 + public static CompositeCurve3d? ToCompositeCurve3d(this Curve curve) + { + return curve switch + { + Line li => new CompositeCurve3d(new Curve3d[] { ToCurve3d(li) }), + Circle ci => new CompositeCurve3d(new Curve3d[] { ToCurve3d(ci) }), + Arc arc => new CompositeCurve3d(new Curve3d[] { ToCurve3d(arc) }), + Ellipse el => new CompositeCurve3d(new Curve3d[] { ToCurve3d(el) }), + Polyline pl => new CompositeCurve3d(new Curve3d[] { ToCurve3d(pl) }), + + Polyline2d pl2 => new CompositeCurve3d(new Curve3d[] { ToCurve3d(pl2)! }), + Polyline3d pl3 => new CompositeCurve3d(new Curve3d[] { ToCurve3d(pl3) }), + Spline sp => new CompositeCurve3d(new Curve3d[] { ToCurve3d(sp) }), + _ => null + }; + } + + /// + /// 将曲线转换为Nurb曲线 + /// + /// 曲线 + /// Nurb曲线 + public static NurbCurve3d? ToNurbCurve3d(this Curve curve) + { + return curve switch + { + Line li => ToNurbCurve3d(li), + Circle ci => ToNurbCurve3d(ci), + Arc arc => ToNurbCurve3d(arc), + Ellipse el => ToNurbCurve3d(el), + Polyline pl => ToNurbCurve3d(pl), + Polyline2d pl2 => ToNurbCurve3d(pl2), + Polyline3d pl3 => ToNurbCurve3d(pl3), + Spline sp => ToNurbCurve3d(sp), + _ => null + }; + } + + #region Line + + /// + /// 将直线转换为ge直线 + /// + /// 直线 + /// ge直线 + public static LineSegment3d ToCurve3d(this Line line) + { + return new LineSegment3d(line.StartPoint, line.EndPoint); + } + + /// + /// 将直线转换为Nurb曲线 + /// + /// 直线 + /// Nurb曲线 + public static NurbCurve3d ToNurbCurve3d(this Line line) + { + return new NurbCurve3d(ToCurve3d(line)); + } + + #endregion Line + + #region Circle + + /// + /// 将圆转换为ge圆弧曲线 + /// + /// 圆 + /// ge圆弧曲线 + public static CircularArc3d ToCurve3d(this Circle cir) + { + return + new CircularArc3d( + cir.Center, + cir.Normal, + cir.Radius); + } + + /// + /// 将圆转换为ge椭圆曲线 + /// + /// 圆 + /// ge椭圆曲线 + public static EllipticalArc3d ToEllipticalArc3d(this Circle cir) + { + return ToCurve3d(cir).ToEllipticalArc3d(); + } + + /// + /// 将圆转换为Nurb曲线 + /// + /// 圆 + /// Nurb曲线 + public static NurbCurve3d ToNurbCurve3d(this Circle cir) + { + return new NurbCurve3d(ToEllipticalArc3d(cir)); + } + + #endregion Circle + + #region Arc + + /// + /// 将圆弧转换为ge圆弧曲线 + /// + /// 圆弧 + /// ge圆弧曲线 + public static CircularArc3d ToCurve3d(this Arc arc) + { + Plane plane = new(arc.Center, arc.Normal); + + return + new CircularArc3d( + arc.Center, + arc.Normal, + plane.GetCoordinateSystem().Xaxis, + arc.Radius, + arc.StartAngle, + arc.EndAngle + ); + } + + /// + /// 将圆弧转换为ge椭圆曲线 + /// + /// 圆弧 + /// ge椭圆曲线 + public static EllipticalArc3d ToEllipticalArc3d(this Arc arc) + { + return ToCurve3d(arc).ToEllipticalArc3d(); + } + + /// + /// 将圆弧转换为三维Nurb曲线 + /// + /// 圆弧 + /// 三维Nurb曲线 + public static NurbCurve3d ToNurbCurve3d(this Arc arc) + { + return new NurbCurve3d(ToEllipticalArc3d(arc)); + } + + #endregion Arc + + #region Ellipse + + /// + /// 将椭圆转换为三维ge椭圆曲线 + /// + /// 椭圆 + /// 三维ge椭圆曲线 + public static EllipticalArc3d ToCurve3d(this Ellipse ell) + { + return + new EllipticalArc3d( + ell.Center, + ell.MajorAxis.GetNormal(), + ell.MinorAxis.GetNormal(), + ell.MajorRadius, + ell.MinorRadius, + ell.StartParam, + ell.EndParam); + } + + /// + /// 将椭圆转换为三维Nurb曲线 + /// + /// 椭圆 + /// 三维Nurb曲线 + public static NurbCurve3d ToNurbCurve3d(this Ellipse ell) + { + return new NurbCurve3d(ToCurve3d(ell)); + } + + #endregion Ellipse + + #region Spline + + /// + /// 将样条曲线转换为三维Nurb曲线 + /// + /// 样条曲线 + /// 三维Nurb曲线 + public static NurbCurve3d ToCurve3d(this Spline spl) + { + NurbCurve3d nc3d; + NurbsData ndata = spl.NurbsData; + KnotCollection knots = new(); + foreach (Double knot in ndata.GetKnots()) + knots.Add(knot); + + if (ndata.Rational) + { + nc3d = + new NurbCurve3d( + ndata.Degree, + knots, + ndata.GetControlPoints(), + ndata.GetWeights(), + ndata.Periodic); + } + else + { + nc3d = + new NurbCurve3d( + ndata.Degree, + knots, + ndata.GetControlPoints(), + ndata.Periodic); + } + + if (spl.HasFitData) + { + var fdata = spl.FitData; + var vec = new Vector3d(); + if (fdata.TangentsExist && (fdata.StartTangent != vec || fdata.EndTangent != vec)) + nc3d.SetFitData(fdata.GetFitPoints(), fdata.StartTangent, fdata.EndTangent); + } + return nc3d; + } + + #endregion Spline + + #region Polyline2d + + /// + /// 将二维多段线转换为三维ge曲线 + /// + /// 二维多段线 + /// 三维ge曲线 + public static Curve3d? ToCurve3d(this Polyline2d pl2d) + { + switch (pl2d.PolyType) + { + case Poly2dType.SimplePoly: + case Poly2dType.FitCurvePoly: + Polyline pl = new(); + pl.ConvertFrom(pl2d, false); + return ToCurve3d(pl); + + default: + return ToNurbCurve3d(pl2d); + } + + //Polyline pl = new Polyline(); + //pl.ConvertFrom(pl2d, false); + //return ToCurve3d(pl); + } + + /// + /// 将二维多段线转换为三维Nurb曲线 + /// + /// 二维多段线 + /// 三维Nurb曲线 + public static NurbCurve3d? ToNurbCurve3d(this Polyline2d pl2d) + { + switch (pl2d.PolyType) + { + case Poly2dType.SimplePoly: + case Poly2dType.FitCurvePoly: + Polyline pl = new(); + pl.ConvertFrom(pl2d, false); + return ToNurbCurve3d(pl); + + default: + return ToCurve3d(pl2d.Spline); + } + } + + /// + /// 将二维多段线转换为三维ge多段线 + /// + /// 二维多段线 + /// 三维ge多段线 + public static PolylineCurve3d ToPolylineCurve3d(this Polyline2d pl) + { + Point3dCollection pnts = new(); + foreach (Vertex2d ver in pl) + { + pnts.Add(ver.Position); + } + return new PolylineCurve3d(pnts); + } + + #endregion Polyline2d + + #region Polyline3d + + /// + /// 将三维多段线转换为三维曲线 + /// + /// 三维多段线 + /// 三维曲线 + public static Curve3d ToCurve3d(this Polyline3d pl3d) + { + return pl3d.PolyType switch + { + Poly3dType.SimplePoly => ToPolylineCurve3d(pl3d), + _ => ToNurbCurve3d(pl3d), + }; + } + + /// + /// 将三维多段线转换为三维Nurb曲线 + /// + /// 三维多段线 + /// 三维Nurb曲线 + public static NurbCurve3d ToNurbCurve3d(this Polyline3d pl3d) + { + return ToCurve3d(pl3d.Spline); + } + + /// + /// 将三维多段线转换为三维ge多段线 + /// + /// 三维多段线 + /// 三维ge多段线 + public static PolylineCurve3d ToPolylineCurve3d(this Polyline3d pl) + { + Point3dCollection pnts = new(); + foreach (ObjectId id in pl) + { + PolylineVertex3d ver = (PolylineVertex3d)id.GetObject(OpenMode.ForRead); + pnts.Add(ver.Position); + } + return new PolylineCurve3d(pnts); + } + + #endregion Polyline3d + + #region Polyline + + /// + /// 多段线转换为复合曲线 + /// + /// 多段线对象 + /// 复合曲线对象 + public static CompositeCurve3d ToCurve3d(this Polyline pl) + { + List c3ds = new(); + + for (int i = 0; i < pl.NumberOfVertices; i++) + { + switch (pl.GetSegmentType(i)) + { + case SegmentType.Line: + c3ds.Add(pl.GetLineSegmentAt(i)); + break; + + case SegmentType.Arc: + c3ds.Add(pl.GetArcSegmentAt(i)); + break; + + default: + break; + } + } + return new CompositeCurve3d(c3ds.ToArray()); + } + + /// + /// 多段线转换为Nurb曲线 + /// + /// 多段线 + /// Nurb曲线 + public static NurbCurve3d? ToNurbCurve3d(this Polyline pl) + { + NurbCurve3d? nc3d = null; + for (int i = 0; i < pl.NumberOfVertices; i++) + { + NurbCurve3d? nc3dtemp = null; + switch (pl.GetSegmentType(i)) + { + case SegmentType.Line: + nc3dtemp = new NurbCurve3d(pl.GetLineSegmentAt(i)); + break; + + case SegmentType.Arc: + nc3dtemp = pl.GetArcSegmentAt(i).ToNurbCurve3d(); + break; + + default: + break; + } + if (nc3d is null) + { + nc3d = nc3dtemp; + } + else if (nc3dtemp is not null) + { + nc3d.JoinWith(nc3dtemp); + } + } + return nc3d; + } + + /// + /// 为优化多段线倒角 + /// + /// 优化多段线 + /// 顶点索引号 + /// 倒角半径 + /// 倒角类型 + public static void ChamferAt(this Polyline polyline, int index, double radius, bool isFillet) + { + if (index < 1 || index > polyline.NumberOfVertices - 2) + throw new System.Exception("错误的索引号"); + + if (polyline.GetSegmentType(index - 1) != SegmentType.Line || polyline.GetSegmentType(index) != SegmentType.Line) + throw new System.Exception("非直线段不能倒角"); + + //获取当前索引号的前后两段直线,并组合为Ge复合曲线 + Curve3d[] c3ds = + new Curve3d[] + { + polyline.GetLineSegmentAt(index - 1), + polyline.GetLineSegmentAt(index) + }; + var cc3d = new CompositeCurve3d(c3ds); + + //试倒直角 + //子曲线的个数有三种情况: + //1、=3时倒角方向正确 + //2、=2时倒角方向相反 + //3、=0或为直线时失败 + c3ds = + cc3d.GetTrimmedOffset + ( + radius, + Vector3d.ZAxis, + OffsetCurveExtensionType.Chamfer + ); + + if (c3ds.Length > 0 && c3ds[0] is CompositeCurve3d) + { + var newcc3d = c3ds[0] as CompositeCurve3d; + c3ds = newcc3d!.GetCurves(); + if (c3ds.Length == 3) + { + c3ds = + cc3d.GetTrimmedOffset + ( + -radius, + Vector3d.ZAxis, + OffsetCurveExtensionType.Chamfer + ); + if (c3ds.Length == 0 || c3ds[0] is LineSegment3d) + { + throw new System.Exception("倒角半径过大"); + } + } + else if (c3ds.Length == 2) + { + radius = -radius; + } + } + else + { + throw new System.Exception("倒角半径过大"); + } + + //GetTrimmedOffset会生成倒角+偏移,故先反方向倒角,再倒回 + c3ds = + cc3d.GetTrimmedOffset + ( + -radius, + Vector3d.ZAxis, + OffsetCurveExtensionType.Extend + ); + OffsetCurveExtensionType type = + isFillet ? + OffsetCurveExtensionType.Fillet : OffsetCurveExtensionType.Chamfer; + c3ds = + c3ds[0].GetTrimmedOffset + ( + radius, + Vector3d.ZAxis, + type + ); + + //将结果Ge曲线转为Db曲线,并将相关的数值反映到原曲线 + Polyline? plTemp = c3ds[0].ToCurve() as Polyline; + polyline.RemoveVertexAt(index); + polyline.AddVertexAt(index, plTemp!.GetPoint2dAt(1), plTemp.GetBulgeAt(1), 0, 0); + polyline.AddVertexAt(index + 1, plTemp.GetPoint2dAt(2), 0, 0, 0); + } + + #endregion Polyline + + #endregion Curve +} -- Gitee From a66e5f4b8e4ff5ad9a0d648031630c39d5776765 Mon Sep 17 00:00:00 2001 From: liuqihong <540762622@qq.com> Date: Mon, 4 Apr 2022 04:48:57 +0800 Subject: [PATCH 066/675] =?UTF-8?q?=E4=BF=AE=E6=94=B9=E6=9B=B2=E7=BA=BF?= =?UTF-8?q?=E7=B1=BB=E6=B3=A8=E9=87=8A?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/IFoxCAD.Cad/ExtensionMethod/CurveEx.cs | 17 ++++++++--------- 1 file changed, 8 insertions(+), 9 deletions(-) diff --git a/src/IFoxCAD.Cad/ExtensionMethod/CurveEx.cs b/src/IFoxCAD.Cad/ExtensionMethod/CurveEx.cs index 39364ac..d7c9506 100644 --- a/src/IFoxCAD.Cad/ExtensionMethod/CurveEx.cs +++ b/src/IFoxCAD.Cad/ExtensionMethod/CurveEx.cs @@ -245,7 +245,6 @@ public static List Topo(List curves) GetEdgesAndnewCurves(curves, edges, newCurves); //构建边的邻接表 - //knots 和 nums 实际上是一个键值对(基于ArrayOfStruct思想,拆开更合适内存布局) //knots 是不重复地将所有交点设置为节点,如果是重复,就对应 nums++ //nums 是记录每个节点坐标被重复了几次 @@ -388,12 +387,12 @@ public static List Topo(List curves) /// /// 从曲线集合分离边界(交点断分曲线的)和独立的曲线 /// - /// - /// - /// + /// 传入判断的曲线集 + /// 边界(可能仍然存在自闭,因为样条曲线允许打个鱼形圈,尾巴又交叉在其他曲线) + /// 自闭的曲线 static void GetEdgesAndnewCurves(List curves, List edgesOut, - List newCurvesOut) + List closedCurvesOut) { //首先按交点分解为Ge曲线集 var geCurves = new List(); @@ -417,7 +416,7 @@ static void GetEdgesAndnewCurves(List curves, var gc1 = geCurves[i]; var pars1 = paramss[i]; //曲线a,和曲线b/c/d/e...分别交点,组成交点数组 - //第一次是aa对比,所以会怎么样呢?(交点无限个) + //第一次是 aa对比,所以会怎么样呢?(交点无限个) for (int j = i; j < curves.Count; j++) { var gc2 = geCurves[j]; @@ -447,11 +446,11 @@ static void GetEdgesAndnewCurves(List curves, //惊惊留:(不敢删啊...) //狐哥写的这里出现的条件是:有曲线参数,但是切分不出来曲线...没懂为什么... //猜测:曲线参数在头或者尾,那么交点就是直接碰头碰尾, - //也就是aa判断的时候,同一条曲线自己和自己产生的? + //也就是 aa对比 同一条曲线自己和自己产生的? //参数大于0{是这些参数? 头参/尾参/参数不在曲线上?} else if (gc1.IsClosed()) { - newCurvesOut.Add(gc1.ToCurve()!); + closedCurvesOut.Add(gc1.ToCurve()!); } else { @@ -460,7 +459,7 @@ static void GetEdgesAndnewCurves(List curves, } else if (gc1.IsClosed()) { - newCurvesOut.Add(gc1.ToCurve()!); + closedCurvesOut.Add(gc1.ToCurve()!); } } } -- Gitee From ae799633a0f97a9a84218c2d32ca39d836c67c7a Mon Sep 17 00:00:00 2001 From: liuqihong <540762622@qq.com> Date: Mon, 4 Apr 2022 12:36:32 +0800 Subject: [PATCH 067/675] =?UTF-8?q?=E9=87=8D=E8=BD=BD=E8=BF=90=E7=AE=97?= =?UTF-8?q?=E7=AC=A6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/IFoxCAD.Cad/ExtensionMethod/CurveEx.cs | 96 ++++++++++++++++++---- 1 file changed, 82 insertions(+), 14 deletions(-) diff --git a/src/IFoxCAD.Cad/ExtensionMethod/CurveEx.cs b/src/IFoxCAD.Cad/ExtensionMethod/CurveEx.cs index d7c9506..278cefa 100644 --- a/src/IFoxCAD.Cad/ExtensionMethod/CurveEx.cs +++ b/src/IFoxCAD.Cad/ExtensionMethod/CurveEx.cs @@ -51,15 +51,20 @@ public static IEnumerable GetSplitCurves(this Curve curve, IEnumerable private struct EdgeItem : IEquatable { + #region 字段 public Edge Edge; public bool Forward; + #endregion + #region 构造 public EdgeItem(Edge edge, bool forward) { Edge = edge; Forward = forward; } + #endregion + #region 方法 public CompositeCurve3d? GetCurve() { var cc3d = Edge.Curve; @@ -74,11 +79,6 @@ public EdgeItem(Edge edge, bool forward) } } - public bool Equals(EdgeItem other) - { - return Edge == other.Edge && Forward == other.Forward; - } - /// /// 查找区域 /// @@ -168,7 +168,9 @@ public EdgeItem GetNext(List edges) } return item; } + #endregion + #region 类型转换 public override string ToString() { return @@ -176,29 +178,51 @@ public override string ToString() string.Format("{0}-{1}", Edge.StartIndex, Edge.EndIndex) : string.Format("{0}-{1}", Edge.EndIndex, Edge.StartIndex); } - + #endregion + #region 重载运算符_比较 public override bool Equals(object obj) { - return obj is EdgeItem item && Equals(item); + return this == (EdgeItem)obj; + } + public bool Equals(EdgeItem b) + { + return this == b; + } + public static bool operator !=(EdgeItem a, EdgeItem b) + { + return !(a == b); + } + public static bool operator ==(EdgeItem a, EdgeItem b) + { + return a.Edge == b.Edge && a.Forward == b.Forward; } - public override int GetHashCode() { - return this.ToString().GetHashCode(); + return base.GetHashCode(); + //return this.ToString().GetHashCode(); } + #endregion } + /// /// 边 /// - private class Edge + private class Edge : IEquatable { + #region 字段 public CompositeCurve3d Curve; public int StartIndex; public int EndIndex; + #endregion + + #region 构造 public Edge(CompositeCurve3d curve) { Curve = curve; } + #endregion + + #region 方法 public Vector3d GetStartVector() { var inter = Curve.GetInterval(); @@ -213,17 +237,25 @@ public Vector3d GetEndVector() return -poc.GetDerivative(1); } - public bool IsNext(Edge edge, int index, ref Vector3d vec, ref bool forward) + /// + /// 判断节点位置 + /// + /// 边界 + /// 边界是否位于此处 + /// + /// + /// + public bool IsNext(Edge edge, int startOrEndIndex, ref Vector3d vec, ref bool forward) { if (edge != this) { - if (StartIndex == index) + if (StartIndex == startOrEndIndex) { vec = GetStartVector(); forward = true; return true; } - else if (EndIndex == index) + else if (EndIndex == startOrEndIndex) { vec = GetEndVector(); forward = false; @@ -232,6 +264,41 @@ public bool IsNext(Edge edge, int index, ref Vector3d vec, ref bool forward) } return false; } + #endregion + + #region 重载运算符_比较 + public override bool Equals(object? obj) + { + return this == obj as Edge; + } + public bool Equals(Edge? b) + { + return this == b; + } + public static bool operator !=(Edge? a, Edge? b) + { + return !(a == b); + } + public static bool operator ==(Edge? a, Edge? b) + { + //此处地方不允许使用==null,因为此处是定义 + if (b is null) + return a is null; + else if (a is null) + return false; + if (ReferenceEquals(a, b))//同一对象 + return true; + + return a.Curve == b.Curve && + a.StartIndex == b.StartIndex && + a.EndIndex == b.EndIndex; + } + + public override int GetHashCode() + { + return base.GetHashCode(); + } + #endregion } /// /// 获取曲线集所围成的封闭区域的曲线集 @@ -270,7 +337,7 @@ public static List Topo(List curves) } else { - //不含有就加入节点,共用计数也加入,边界加入节点索引 + //不含有就加入节点,共用计数也加入,边界设置节点索引 knots.Add(edge.Curve.StartPoint); nums.Add(1); edge.StartIndex = knots.Count - 1; @@ -326,6 +393,7 @@ public static List Topo(List curves) // } // } + //在边界集中找到闭合链条 var regions = new List>(); for (int i = 0; i < edges.Count; i++) { -- Gitee From c78710885cadafcf97095de90b0a4041dde2ced6 Mon Sep 17 00:00:00 2001 From: vicwjb Date: Mon, 4 Apr 2022 12:56:37 +0800 Subject: [PATCH 068/675] =?UTF-8?q?=E5=A2=9E=E5=8A=A0=E8=AE=A1=E6=97=B6?= =?UTF-8?q?=E5=87=BD=E6=95=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/IFoxCAD.Cad/ExtensionMethod/Tools.cs | 31 ++ src/IFoxCAD.Cad/IFoxCAD.Cad.csproj | 22 +- src/IFoxCAD.Cad/QuadTree/Class1.cs | 528 +++++++++++++++++++++++ tests/Test/GlobalUsings.cs | 10 +- tests/Test/Test.cs | 59 +++ tests/Test/Test.csproj | 4 +- tests/Test/TestCurve.cs | 1 + 7 files changed, 635 insertions(+), 20 deletions(-) create mode 100644 src/IFoxCAD.Cad/ExtensionMethod/Tools.cs create mode 100644 src/IFoxCAD.Cad/QuadTree/Class1.cs diff --git a/src/IFoxCAD.Cad/ExtensionMethod/Tools.cs b/src/IFoxCAD.Cad/ExtensionMethod/Tools.cs new file mode 100644 index 0000000..4c70511 --- /dev/null +++ b/src/IFoxCAD.Cad/ExtensionMethod/Tools.cs @@ -0,0 +1,31 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; + +namespace IFoxCAD.Cad +{ + public static class Tools + { + public static void TestTimes(int count, string message, Action action) + { + System.Diagnostics.Stopwatch watch = new System.Diagnostics.Stopwatch(); + watch.Start(); //开始监视代码运行时间 + for (int i = 0; i < count; i++) + { + action.Invoke(); //需要测试的代码 + } + watch.Stop(); //停止监视 + TimeSpan timespan = watch.Elapsed; //获取当前实例测量得出的总时间 + double time = timespan.TotalMilliseconds; + string name = "毫秒"; + if (timespan.TotalMilliseconds > 1000) + { + time = timespan.TotalSeconds; + name = "秒"; + } + Env.Print($"{message} 代码执行 {count} 次的时间:{time} ({name})"); //总毫秒数 + } + } + +} diff --git a/src/IFoxCAD.Cad/IFoxCAD.Cad.csproj b/src/IFoxCAD.Cad/IFoxCAD.Cad.csproj index 0697e44..55ee81f 100644 --- a/src/IFoxCAD.Cad/IFoxCAD.Cad.csproj +++ b/src/IFoxCAD.Cad/IFoxCAD.Cad.csproj @@ -1,9 +1,11 @@ - + net35;net40;net45 true enable + true + true 0.3.1 InspireFunction xsfhlzh;vicwjb @@ -67,24 +69,8 @@ - - - - - - + - - - - - - diff --git a/src/IFoxCAD.Cad/QuadTree/Class1.cs b/src/IFoxCAD.Cad/QuadTree/Class1.cs new file mode 100644 index 0000000..947bdf3 --- /dev/null +++ b/src/IFoxCAD.Cad/QuadTree/Class1.cs @@ -0,0 +1,528 @@ +namespace IFoxCAD.Cad; + +using System.Windows; + +//---------------------------------------------------------------- +// Copyright (c) Microsoft Corporation. All rights reserved. +//---------------------------------------------------------------- + +/// +/// This class efficiently stores and retrieves arbitrarily sized and positioned +/// objects in a quad-tree data structure. This can be used to do efficient hit +/// detection or visiblility checks on objects in a virtualized canvas. +/// The object does not need to implement any special interface because the Rect Bounds +/// of those objects is handled as a separate argument to Insert. +/// 此类有效地存储和检索四叉树数据结构中任意大小和位置的对象。 这可用于对虚拟画布中的对象进行有效的命中检测或可见性检查。 +/// 该对象不需要实现任何特殊接口,因为这些对象的 Rect Bounds 被作为单独的参数进行插入。 +/// +public class QuadTree where T : class +{ + + + Rect bounds; // overall bounds we are indexing. 索引的总体范围 + Quadrant? root; + IDictionary? table; + + + + /// + /// This determines the overall quad-tree indexing strategy, changing this bounds + /// is expensive since it has to re-divide the entire thing - like a re-hash operation. + /// 这决定了整个四叉树索引策略,改变这个界限是昂贵的,因为它必须重新划分整个事物 - 就像重新散列操作一样。 + /// + public Rect Bounds + { + get { return this.bounds; } + set { this.bounds = value; ReIndex(); } + } + + /// + /// Insert a node with given bounds into this QuadTree. + /// 将具有给定边界的节点插入此 QuadTree。 + /// + /// The node to insert 要插入的节点 + /// The bounds of this node 这个节点的边界 + public void Insert(T node, Rect bounds) + { + if (this.bounds.Width == 0 || this.bounds.Height == 0) + { + throw new ArgumentException("Bounds must be not zero"); + } + if (bounds.Width == 0 || bounds.Height == 0) + { + throw new ArgumentException("Bounds must be not zero"); + } + if (this.root == null) + { + this.root = new Quadrant(null, this.bounds); + } + + Quadrant parent = this.root.Insert(node, bounds); + + if (this.table == null) + { + this.table = new Dictionary(); + } + this.table[node] = parent; + + + } + + /// + /// Get a list of the nodes that intersect the given bounds. + /// 获取与给定边界相交的节点列表。 + /// + /// The bounds to test 给定的边界 + /// + /// List of zero or mode nodes found inside the given bounds + /// 在给定范围内找到零个或多个节点的列表 + /// + public IEnumerable GetNodesInside(Rect bounds) + { + foreach (QuadNode n in GetNodes(bounds)) + { + yield return n.Node; + } + } + + /// + /// Get a list of the nodes that intersect the given bounds. + /// 获取与给定边界相交的节点列表。 + /// + /// The bounds to test 给定的边界 + /// 如果此象限中有任何节点与给定边界相交,则返回 true。 + public bool HasNodesInside(Rect bounds) + { + if (this.root == null) + { + return false; + } + return this.root.HasIntersectingNodes(bounds); + } + + /// + /// Get list of nodes that intersect the given bounds. + /// + /// The bounds to test + /// The list of nodes intersecting the given bounds + IEnumerable GetNodes(Rect bounds) + { + List result = new(); + if (this.root != null) + { + this.root.GetIntersectingNodes(result, bounds); + } + return result; + } + + /// + /// Remove the given node from this QuadTree. + /// + /// The node to remove + /// True if the node was found and removed. + public bool Remove(T node) + { + if (this.table != null) + { + //Quadrant parent = null; + if (table.TryGetValue(node, out Quadrant parent)) + { + parent.RemoveNode(node); + this.table.Remove(node); + return true; + } + } + return false; + } + + /// + /// Rebuild all the Quadrants according to the current QuadTree Bounds. + /// + void ReIndex() + { + this.root = null; + foreach (QuadNode n in GetNodes(this.bounds)) + { + Insert(n.Node, n.Bounds); + } + } + + /// + /// Each node stored in the tree has a position, width & height. + /// + internal class QuadNode + { + Rect bounds; + QuadNode? next; // linked in a circular list. + T node; // the actual visual object being stored here. + + /// + /// Construct new QuadNode to wrap the given node with given bounds + /// + /// The node + /// The bounds of that node + public QuadNode(T node, Rect bounds) + { + this.node = node; + this.bounds = bounds; + } + + /// + /// The node + /// + public T Node + { + get { return this.node; } + set { this.node = value; } + } + + /// + /// The Rect bounds of the node + /// + public Rect Bounds + { + get { return this.bounds; } + } + + /// + /// QuadNodes form a linked list in the Quadrant. + /// + public QuadNode? Next + { + get { return this.next; } + set { this.next = value; } + } + } + + + /// + /// The canvas is split up into four Quadrants and objects are stored in the quadrant that contains them + /// and each quadrant is split up into four child Quadrants recurrsively. Objects that overlap more than + /// one quadrant are stored in the this.nodes list for this Quadrant. + /// 画布分为四个象限,对象存储在包含它们的象限中,每个象限递归地分成四个子象限。 重叠多个象限的对象存储在此象限的 this.nodes 列表中。 + /// + internal class Quadrant + { + readonly Quadrant? parent; + Rect bounds; // quadrant bounds. + + QuadNode nodes; // nodes that overlap the sub quadrant boundaries. + + // The quadrant is subdivided when nodes are inserted that are + // completely contained within those subdivisions. + Quadrant topLeft; + Quadrant topRight; + Quadrant bottomLeft; + Quadrant bottomRight; + + + + /// + /// Construct new Quadrant with a given bounds all nodes stored inside this quadrant + /// will fit inside this bounds. + /// + /// The parent quadrant (if any) + /// The bounds of this quadrant + public Quadrant(Quadrant? parent, Rect bounds) + { + this.parent = parent; + //Fx.Assert(bounds.Width != 0 && bounds.Height != 0, "Cannot have empty bound"); + if (bounds.Width == 0 || bounds.Height == 0) + { + throw new ArgumentException("Bounds must be not zero"); + } + this.bounds = bounds; + } + + /// + /// The parent Quadrant or null if this is the root + /// + internal Quadrant? Parent + { + get { return this.parent; } + } + + /// + /// The bounds of this quadrant + /// + internal Rect Bounds + { + get { return this.bounds; } + } + + /// + /// Insert the given node + /// + /// The node + /// The bounds of that node + /// + internal Quadrant Insert(T node, Rect bounds) + { + + + if (bounds.Width == 0 || bounds.Height == 0) + { + throw new ArgumentException("Bounds must be not zero"); + } + + Quadrant toInsert = this; + while (true) + { + double w = toInsert.bounds.Width / 2; + if (w < 1) + { + w = 1; + } + double h = toInsert.bounds.Height / 2; + if (h < 1) + { + h = 1; + } + + // assumption that the Rect struct is almost as fast as doing the operations + // manually since Rect is a value type. + + Rect topLeft = new Rect(toInsert.bounds.Left, toInsert.bounds.Top, w, h); + Rect topRight = new Rect(toInsert.bounds.Left + w, toInsert.bounds.Top, w, h); + Rect bottomLeft = new Rect(toInsert.bounds.Left, toInsert.bounds.Top + h, w, h); + Rect bottomRight = new Rect(toInsert.bounds.Left + w, toInsert.bounds.Top + h, w, h); + + Quadrant? child = null; + + // See if any child quadrants completely contain this node. + if (topLeft.Contains(bounds)) + { + if (toInsert.topLeft == null) + { + toInsert.topLeft = new Quadrant(toInsert, topLeft); + } + child = toInsert.topLeft; + } + else if (topRight.Contains(bounds)) + { + if (toInsert.topRight == null) + { + toInsert.topRight = new Quadrant(toInsert, topRight); + } + child = toInsert.topRight; + } + else if (bottomLeft.Contains(bounds)) + { + if (toInsert.bottomLeft == null) + { + toInsert.bottomLeft = new Quadrant(toInsert, bottomLeft); + } + child = toInsert.bottomLeft; + } + else if (bottomRight.Contains(bounds)) + { + if (toInsert.bottomRight == null) + { + toInsert.bottomRight = new Quadrant(toInsert, bottomRight); + } + child = toInsert.bottomRight; + } + + if (child != null) + { + toInsert = child; + } + else + { + QuadNode n = new QuadNode(node, bounds); + if (toInsert.nodes == null) + { + n.Next = n; + } + else + { + // link up in circular link list. + QuadNode x = toInsert.nodes; + n.Next = x.Next; + x.Next = n; + } + toInsert.nodes = n; + return toInsert; + } + } + } + + /// + /// Returns all nodes in this quadrant that intersect the given bounds. + /// The nodes are returned in pretty much random order as far as the caller is concerned. + /// + /// List of nodes found in the given bounds + /// The bounds that contains the nodes you want returned + internal void GetIntersectingNodes(List nodes, Rect bounds) + { + if (bounds.IsEmpty) return; + double w = this.bounds.Width / 2; + double h = this.bounds.Height / 2; + + // assumption that the Rect struct is almost as fast as doing the operations + // manually since Rect is a value type. + + Rect topLeft = new Rect(this.bounds.Left, this.bounds.Top, w, h); + Rect topRight = new Rect(this.bounds.Left + w, this.bounds.Top, w, h); + Rect bottomLeft = new Rect(this.bounds.Left, this.bounds.Top + h, w, h); + Rect bottomRight = new Rect(this.bounds.Left + w, this.bounds.Top + h, w, h); + + // See if any child quadrants completely contain this node. + if (topLeft.IntersectsWith(bounds) && this.topLeft != null) + { + this.topLeft.GetIntersectingNodes(nodes, bounds); + } + + if (topRight.IntersectsWith(bounds) && this.topRight != null) + { + this.topRight.GetIntersectingNodes(nodes, bounds); + } + + if (bottomLeft.IntersectsWith(bounds) && this.bottomLeft != null) + { + this.bottomLeft.GetIntersectingNodes(nodes, bounds); + } + + if (bottomRight.IntersectsWith(bounds) && this.bottomRight != null) + { + this.bottomRight.GetIntersectingNodes(nodes, bounds); + } + + GetIntersectingNodes(this.nodes, nodes, bounds); + } + + /// + /// Walk the given linked list of QuadNodes and check them against the given bounds. + /// Add all nodes that intersect the bounds in to the list. + /// + /// The last QuadNode in a circularly linked list + /// The resulting nodes are added to this list + /// The bounds to test against each node + static void GetIntersectingNodes(QuadNode last, List nodes, Rect bounds) + { + if (last != null) + { + QuadNode? n = last; + do + { + n = n.Next; // first node. + if (n.Bounds.IntersectsWith(bounds)) + { + nodes.Add(n); + } + } while (n != last); + } + } + + /// + /// Return true if there are any nodes in this Quadrant that intersect the given bounds. + /// 如果此象限中有任何节点与给定边界相交,则返回 true。 + /// + /// The bounds to test + /// boolean + internal bool HasIntersectingNodes(Rect bounds) + { + if (bounds.IsEmpty) return false; + double w = this.bounds.Width / 2; + double h = this.bounds.Height / 2; + + // assumption that the Rect struct is almost as fast as doing the operations + // manually since Rect is a value type. + + Rect topLeft = new Rect(this.bounds.Left, this.bounds.Top, w, h); + Rect topRight = new Rect(this.bounds.Left + w, this.bounds.Top, w, h); + Rect bottomLeft = new Rect(this.bounds.Left, this.bounds.Top + h, w, h); + Rect bottomRight = new Rect(this.bounds.Left + w, this.bounds.Top + h, w, h); + + bool found = false; + + // See if any child quadrants completely contain this node. + if (topLeft.IntersectsWith(bounds) && this.topLeft != null) + { + found = this.topLeft.HasIntersectingNodes(bounds); + } + + if (!found && topRight.IntersectsWith(bounds) && this.topRight != null) + { + found = this.topRight.HasIntersectingNodes(bounds); + } + + if (!found && bottomLeft.IntersectsWith(bounds) && this.bottomLeft != null) + { + found = this.bottomLeft.HasIntersectingNodes(bounds); + } + + if (!found && bottomRight.IntersectsWith(bounds) && this.bottomRight != null) + { + found = this.bottomRight.HasIntersectingNodes(bounds); + } + if (!found) + { + found = HasIntersectingNodes(this.nodes, bounds); + } + return found; + } + + /// + /// Walk the given linked list and test each node against the given bounds/ + /// + /// The last node in the circularly linked list. + /// Bounds to test + /// Return true if a node in the list intersects the bounds + static bool HasIntersectingNodes(QuadNode last, Rect bounds) + { + if (last != null) + { + QuadNode n = last; + do + { + n = n.Next; // first node. + if (n.Bounds.IntersectsWith(bounds)) + { + return true; + } + } while (n != last); + } + return false; + } + + /// + /// Remove the given node from this Quadrant. + /// + /// The node to remove + /// Returns true if the node was found and removed. + internal bool RemoveNode(T node) + { + bool rc = false; + if (this.nodes != null) + { + QuadNode p = this.nodes; + while (p.Next.Node != node && p.Next != this.nodes) + { + p = p.Next; + } + if (p.Next.Node == node) + { + rc = true; + QuadNode n = p.Next; + if (p == n) + { + // list goes to empty + this.nodes = null; + } + else + { + if (this.nodes == n) this.nodes = p; + p.Next = n.Next; + } + } + } + return rc; + } + + } + +} + + + diff --git a/tests/Test/GlobalUsings.cs b/tests/Test/GlobalUsings.cs index 5323ef0..cc64ff0 100644 --- a/tests/Test/GlobalUsings.cs +++ b/tests/Test/GlobalUsings.cs @@ -9,7 +9,7 @@ global using System.Text.RegularExpressions; global using Microsoft.Win32; global using System.ComponentModel; - +global using System.Runtime.CompilerServices; /// autocad 引用 global using Autodesk.AutoCAD.ApplicationServices; @@ -18,7 +18,15 @@ global using Autodesk.AutoCAD.DatabaseServices; global using Autodesk.AutoCAD.Geometry; global using Autodesk.AutoCAD.Runtime; +global using acgi = Autodesk.AutoCAD.GraphicsInterface; /// ifoxcad global using IFoxCAD.Cad; global using IFoxCAD.WPF; +global using IFoxCAD.Basal; + + +namespace System.Runtime.CompilerServices +{ + internal static class IsExternalInit { } +} \ No newline at end of file diff --git a/tests/Test/Test.cs b/tests/Test/Test.cs index 24e2fc2..12fc863 100644 --- a/tests/Test/Test.cs +++ b/tests/Test/Test.cs @@ -1,5 +1,8 @@ namespace test; + + + public class Test : AutoRegAssem { [CommandMethod("dbtest")] @@ -283,6 +286,62 @@ public void PrintLayerName() } + [CommandMethod("testrec")] + public void TestRec() + { + Point2d p1 = new(10000.2, 100000.5); + Point2d p2 = new(15000.9, 100000.5); + Point2d p3 = new(15000.9, 105000.7); + Point2d p4 = new(10000.2, 105000.7); + + var p12 = p2 - p1; + var p23 = p3 - p2; + var p34 = p4 - p3; + var p41 = p1 - p4; + var p13 = p3 - p1; + var p24 = p4 - p2; + + + const double pi90 = Math.PI / 2; + + Tools.TestTimes(1000000,"对角线",() => + { + var result = false; + if (Math.Abs(p13.Length - p24.Length) <= 1e8) + { + result = p41.IsParallelTo(p12); + } + + }); + + Tools.TestTimes(1000000,"三次点乘", () => + { + var result = false; + if (Math.Abs(p12.DotProduct(p23)) < 1e8 && + Math.Abs(p23.DotProduct(p34)) < 1e8 && + Math.Abs(p34.DotProduct(p41)) < 1e8) + { + result = true; + } + + }); + + Tools.TestTimes(1000000, "三次垂直", () => + { + var result = false; + if (p12.IsParallelTo(p23) && + p23.IsParallelTo(p34) && + p34.IsParallelTo(p41)) + { + result = true; + } + + }); + + + } + + [CommandMethod("testpt")] diff --git a/tests/Test/Test.csproj b/tests/Test/Test.csproj index 6057504..9ccbef9 100644 --- a/tests/Test/Test.csproj +++ b/tests/Test/Test.csproj @@ -1,7 +1,7 @@  - net45 + net45 true true preview @@ -13,4 +13,6 @@ + + diff --git a/tests/Test/TestCurve.cs b/tests/Test/TestCurve.cs index 4155779..8512c90 100644 --- a/tests/Test/TestCurve.cs +++ b/tests/Test/TestCurve.cs @@ -19,6 +19,7 @@ public void TestToPo() using var tr = new DBTrans(); tt.ForEach(t => t.ForWrite(e => e.ColorIndex = 1)); tr.CurrentSpace.AddEntity(tt); + } [CommandMethod("testCurveCurveIntersector3d")] public void TestCurveCurveIntersector3d() -- Gitee From e003833006097c2f8cd0e8be33788fb610cf0d3e Mon Sep 17 00:00:00 2001 From: vicwjb Date: Mon, 4 Apr 2022 23:33:46 +0800 Subject: [PATCH 069/675] =?UTF-8?q?GetEdgesAndnewCurves=E5=A2=9E=E5=8A=A0?= =?UTF-8?q?=E5=8E=BB=E9=87=8D=EF=BC=8C=E4=BD=86=E6=98=AF=E4=B8=8D=E4=BD=9C?= =?UTF-8?q?=E7=94=A8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/IFoxCAD.Cad/ExtensionMethod/CurveEx.cs | 60 +++++++++++++++++----- tests/Test/TestCurve.cs | 45 +++++++++++++--- 2 files changed, 86 insertions(+), 19 deletions(-) diff --git a/src/IFoxCAD.Cad/ExtensionMethod/CurveEx.cs b/src/IFoxCAD.Cad/ExtensionMethod/CurveEx.cs index 278cefa..6d14580 100644 --- a/src/IFoxCAD.Cad/ExtensionMethod/CurveEx.cs +++ b/src/IFoxCAD.Cad/ExtensionMethod/CurveEx.cs @@ -49,7 +49,7 @@ public static IEnumerable GetSplitCurves(this Curve curve, IEnumerable /// 边节点 /// - private struct EdgeItem : IEquatable + public struct EdgeItem : IEquatable { #region 字段 public Edge Edge; @@ -207,7 +207,7 @@ public override int GetHashCode() /// /// 边 /// - private class Edge : IEquatable + public class Edge : IEquatable { #region 字段 public CompositeCurve3d Curve; @@ -458,9 +458,7 @@ public static List Topo(List curves) /// 传入判断的曲线集 /// 边界(可能仍然存在自闭,因为样条曲线允许打个鱼形圈,尾巴又交叉在其他曲线) /// 自闭的曲线 - static void GetEdgesAndnewCurves(List curves, - List edgesOut, - List closedCurvesOut) + public static void GetEdgesAndnewCurves(List curves, List edgesOut, List closedCurvesOut) { //首先按交点分解为Ge曲线集 var geCurves = new List(); @@ -502,6 +500,11 @@ static void GetEdgesAndnewCurves(List curves, } } + + if (gc1.IsClosed()) + { + closedCurvesOut.Add(gc1.ToCurve()!); + } //有交点参数 if (pars1.Count > 0) { @@ -516,22 +519,53 @@ static void GetEdgesAndnewCurves(List curves, //猜测:曲线参数在头或者尾,那么交点就是直接碰头碰尾, //也就是 aa对比 同一条曲线自己和自己产生的? //参数大于0{是这些参数? 头参/尾参/参数不在曲线上?} - else if (gc1.IsClosed()) - { - closedCurvesOut.Add(gc1.ToCurve()!); - } + //else if (gc1.IsClosed()) + //{ + // closedCurvesOut.Add(gc1.ToCurve()!); + //} else { edgesOut.Add(new Edge(gc1)); } } - else if (gc1.IsClosed()) - { - closedCurvesOut.Add(gc1.ToCurve()!); - } + //else if (gc1.IsClosed()) + //{ + // closedCurvesOut.Add(gc1.ToCurve()!); + //} + } + edgesOut = edgesOut.Distinct(new EdgeComparer()).ToList(); + } + + + private class EdgeComparer : IEqualityComparer + { + + public bool Equals(Edge x, Edge y) + { +#if ac2009 + var pts = x.Curve.GetSamplePoints(100); + return pts.All(pt => y.Curve.IsOn(pt)); +#elif ac2013 || ac2015 + var pts = x.Curve.GetSamplePoints(100); + return pts.All(pt => y.Curve.IsOn(pt.Point)); +#endif + + + //return x.Curve.IsEqualTo(y.Curve); + } + + // If Equals() returns true for a pair of objects + // then GetHashCode() must return the same value for these objects. + + public int GetHashCode(Edge product) + { + return product.Curve.GetHashCode(); } } + + + /// /// 曲线打断 /// diff --git a/tests/Test/TestCurve.cs b/tests/Test/TestCurve.cs index 8512c90..34d0254 100644 --- a/tests/Test/TestCurve.cs +++ b/tests/Test/TestCurve.cs @@ -1,4 +1,6 @@ -namespace Test +using Autodesk.AutoCAD.BoundaryRepresentation; + +namespace Test { public class TestCurve { @@ -42,16 +44,26 @@ public void TestCurveCurveIntersector3d() var int2 = gc2.GetInterval(); cci3d.Set(gc1, gc2, int1,int2, Vector3d.ZAxis); var d = cci3d.OverlapCount(); + var a = cci3d.GetIntersectionRanges(); + Env.Print($"{a[0].LowerBound}-{a[0].UpperBound} and {a[1].LowerBound}-{a[1].UpperBound}"); + for (int m = 0; m < d; m++) + { + var b = cci3d.GetOverlapRanges(m); + Env.Print($"{b[0].LowerBound}-{b[0].UpperBound} and {b[1].LowerBound}-{b[1].UpperBound}"); + } + for (int k = 0; k < cci3d.NumberOfIntersectionPoints; k++) { //var a = cci3d.GetOverlapRanges(k); - var b = cci3d.IsTangential(k); - var c = cci3d.IsTransversal(k); + //var b = cci3d.IsTangential(k); + //var c = cci3d.IsTransversal(k); //var d = cci3d.OverlapCount(); - var e = cci3d.OverlapDirection(); - Env.Print("i"); + //var e = cci3d.OverlapDirection(); + var pt = cci3d.GetIntersectionParameters(k); + var pts = cci3d.GetIntersectionPoint(k); + Env.Print(pts); } - + } @@ -63,5 +75,26 @@ public void TestCurveCurveIntersector3d() //tr.CurrentSpace.AddEntity(tt); } + [CommandMethod("testGetEdgesAndnewCurves")] + public void TestGetEdgesAndnewCurves() + { + var curves = Env.Editor.SSGet().Value.GetEntities().ToList(); + var edges = new List(); + var newCurves = new List(); + CurveEx.GetEdgesAndnewCurves(curves, edges, newCurves); + + using var tr = new DBTrans(); + + for (int i = 0; i < edges.Count; i++) + { + var ent = edges[i].Curve.ToCurve(); + ent.ColorIndex = i; + tr.CurrentSpace.AddEntity(ent); + } + + + //Env.Print(""); + + } } } -- Gitee From 881b982e67121922edc07421d47f01073e660d5a Mon Sep 17 00:00:00 2001 From: liuqihong <540762622@qq.com> Date: Tue, 5 Apr 2022 00:15:53 +0800 Subject: [PATCH 070/675] =?UTF-8?q?=E6=8B=86=E7=A6=BBTopo=E6=96=87?= =?UTF-8?q?=E4=BB=B6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/IFoxCAD.Cad/ExtensionMethod/CurveEx.cs | 506 +-------------- src/IFoxCAD.Cad/ExtensionMethod/Topo.cs | 685 +++++++++++++++++++++ tests/Test/TestCurve.cs | 23 +- 3 files changed, 707 insertions(+), 507 deletions(-) create mode 100644 src/IFoxCAD.Cad/ExtensionMethod/Topo.cs diff --git a/src/IFoxCAD.Cad/ExtensionMethod/CurveEx.cs b/src/IFoxCAD.Cad/ExtensionMethod/CurveEx.cs index 6d14580..648d96a 100644 --- a/src/IFoxCAD.Cad/ExtensionMethod/CurveEx.cs +++ b/src/IFoxCAD.Cad/ExtensionMethod/CurveEx.cs @@ -46,399 +46,24 @@ public static IEnumerable GetSplitCurves(this Curve curve, IEnumerable(); } - /// - /// 边节点 - /// - public struct EdgeItem : IEquatable - { - #region 字段 - public Edge Edge; - public bool Forward; - #endregion - - #region 构造 - public EdgeItem(Edge edge, bool forward) - { - Edge = edge; - Forward = forward; - } - #endregion - - #region 方法 - public CompositeCurve3d? GetCurve() - { - var cc3d = Edge.Curve; - if (Forward) - { - return cc3d; - } - else - { - cc3d = cc3d.Clone() as CompositeCurve3d; - return cc3d?.GetReverseParameterCurve() as CompositeCurve3d; - } - } - - /// - /// 查找区域 - /// - /// - /// - public void FindRegion(List edges, List> regions) - { - var region = new LoopList(); - var edgeItem = this; - region.Add(edgeItem); - var edgeItem2 = this.GetNext(edges); - if (edgeItem2.Edge is not null) - { - bool hasList = false; - foreach (var edgeList2 in regions) - { - var node = edgeList2.GetNode(e => e.Equals(edgeItem)); - //var node = edgeList2.Find(edgeItem); - if (node is not null && node != edgeList2.Last) - { - if (node.Next!.Value.Equals(edgeItem2)) - { - hasList = true; - break; - } - } - } - if (!hasList) - { - while (edgeItem2.Edge is not null) - { - if (edgeItem2.Edge == edgeItem.Edge) - break; - region.Add(edgeItem2); //上一条语句判断失误,导致不停的将相同的值加入region,不能退出循环 - edgeItem2 = edgeItem2.GetNext(edges); - } - if (edgeItem2.Edge == edgeItem.Edge) - regions.Add(region); - } - } - } - - public EdgeItem GetNext(List edges) - { - Vector3d vec; - int next; - if (Forward) - { - vec = Edge.GetEndVector(); - next = Edge.EndIndex; - } - else - { - vec = Edge.GetStartVector(); - next = Edge.StartIndex; - } - - EdgeItem item = new(); - Vector3d vec2, vec3 = new(); - double angle = 0; - bool hasNext = false; - bool forward = false; - foreach (var edge in edges) - { - if (edge.IsNext(Edge, next, ref vec3, ref forward)) - { - if (hasNext) - { - var angle2 = vec.GetAngleTo(vec3, Vector3d.ZAxis); - if (angle2 < angle) - { - vec2 = vec3; - angle = angle2; - item.Edge = edge; - item.Forward = forward; - } - } - else - { - vec2 = vec3; - angle = vec.GetAngleTo(vec2, Vector3d.ZAxis); - item.Edge = edge; - item.Forward = forward; - hasNext = true; - } - } - } - return item; - } - #endregion - - #region 类型转换 - public override string ToString() - { - return - Forward ? - string.Format("{0}-{1}", Edge.StartIndex, Edge.EndIndex) : - string.Format("{0}-{1}", Edge.EndIndex, Edge.StartIndex); - } - #endregion - #region 重载运算符_比较 - public override bool Equals(object obj) - { - return this == (EdgeItem)obj; - } - public bool Equals(EdgeItem b) - { - return this == b; - } - public static bool operator !=(EdgeItem a, EdgeItem b) - { - return !(a == b); - } - public static bool operator ==(EdgeItem a, EdgeItem b) - { - return a.Edge == b.Edge && a.Forward == b.Forward; - } - public override int GetHashCode() - { - return base.GetHashCode(); - //return this.ToString().GetHashCode(); - } - #endregion - } /// - /// 边 - /// - public class Edge : IEquatable - { - #region 字段 - public CompositeCurve3d Curve; - public int StartIndex; - public int EndIndex; - #endregion - - #region 构造 - public Edge(CompositeCurve3d curve) - { - Curve = curve; - } - #endregion - - #region 方法 - public Vector3d GetStartVector() - { - var inter = Curve.GetInterval(); - PointOnCurve3d poc = new(Curve, inter.LowerBound); - return poc.GetDerivative(1); - } - - public Vector3d GetEndVector() - { - var inter = Curve.GetInterval(); - PointOnCurve3d poc = new(Curve, inter.UpperBound); - return -poc.GetDerivative(1); - } - - /// - /// 判断节点位置 - /// - /// 边界 - /// 边界是否位于此处 - /// - /// - /// - public bool IsNext(Edge edge, int startOrEndIndex, ref Vector3d vec, ref bool forward) - { - if (edge != this) - { - if (StartIndex == startOrEndIndex) - { - vec = GetStartVector(); - forward = true; - return true; - } - else if (EndIndex == startOrEndIndex) - { - vec = GetEndVector(); - forward = false; - return true; - } - } - return false; - } - #endregion - - #region 重载运算符_比较 - public override bool Equals(object? obj) - { - return this == obj as Edge; - } - public bool Equals(Edge? b) - { - return this == b; - } - public static bool operator !=(Edge? a, Edge? b) - { - return !(a == b); - } - public static bool operator ==(Edge? a, Edge? b) - { - //此处地方不允许使用==null,因为此处是定义 - if (b is null) - return a is null; - else if (a is null) - return false; - if (ReferenceEquals(a, b))//同一对象 - return true; - - return a.Curve == b.Curve && - a.StartIndex == b.StartIndex && - a.EndIndex == b.EndIndex; - } - - public override int GetHashCode() - { - return base.GetHashCode(); - } - #endregion - } - /// - /// 获取曲线集所围成的封闭区域的曲线集 + /// 获取曲线集所围成的封闭区域的曲线集 TESTTopo /// /// 曲线集 /// 闭合的曲线集 public static List Topo(List curves) { - var edges = new List(); + //闭合的曲线集合 var newCurves = new List(); - GetEdgesAndnewCurves(curves, edges, newCurves); - - //构建边的邻接表 - //knots 和 nums 实际上是一个键值对(基于ArrayOfStruct思想,拆开更合适内存布局) - //knots 是不重复地将所有交点设置为节点,如果是重复,就对应 nums++ - //nums 是记录每个节点坐标被重复了几次 - var knots = new List(); - var nums = new List(); - var closedEdges = new List(); - - for (int i = 0; i < edges.Count; i++) - { - var edge = edges[i]; - if (edge.Curve.IsClosed()) - { - closedEdges.Add(edge); - continue; - } - - if (knots.Contains(edge.Curve.StartPoint)) - { - //含有就是其他曲线"共用"此交点, - //节点所在索引==共用计数索引=>将它++ - edge.StartIndex = knots.IndexOf(edge.Curve.StartPoint); - nums[edge.StartIndex]++; - } - else - { - //不含有就加入节点,共用计数也加入,边界设置节点索引 - knots.Add(edge.Curve.StartPoint); - nums.Add(1); - edge.StartIndex = knots.Count - 1; - } - - if (knots.Contains(edge.Curve.EndPoint)) - { - edge.EndIndex = knots.IndexOf(edge.Curve.EndPoint); - nums[edge.EndIndex]++; - } - else - { - knots.Add(edge.Curve.EndPoint); - nums.Add(1); - edge.EndIndex = knots.Count - 1; - } - } + var edges = new List(); - newCurves.AddRange(closedEdges.Select(e => e.Curve.ToCurve())!); - - //这里把交点只有一条曲线通过的点过滤掉了,也就是尾巴的图元, - //剩下的都是闭合的曲线连接了,每个点都至少有两条曲线通过 - edges = - edges - .Except(closedEdges) - .Where(e => nums[e.StartIndex] > 1 && nums[e.EndIndex] > 1) - .ToList(); - - //这一大坨 不用看了 注释掉也没影响貌似,而且后续也没有用 nums - // foreach (var edge in edges.Except(closedEdges)) - // { - // if (nums[edge.StartIndex] == 1 || nums[edge.EndIndex] == 1) - // { - // if (nums[edge.StartIndex] == 1 && nums[edge.EndIndex] == 1) - // { - // nums[edge.StartIndex] = 0; - // nums[edge.EndIndex] = 0; - // } - // else - // { - // int next = -1; - // if (nums[edge.StartIndex] == 1) - // { - // nums[edge.StartIndex] = 0; - // nums[next = edge.EndIndex]--; - // } - // else - // { - // nums[edge.EndIndex] = 0; - // nums[next = edge.StartIndex]--; - // } - // } - // } - // } - - //在边界集中找到闭合链条 - var regions = new List>(); - for (int i = 0; i < edges.Count; i++) - { - var edge = edges[i]; - //TODO 这里有bug,两个内接的矩形会卡死 - var edgeItem = new EdgeItem(edge, true); - edgeItem.FindRegion(edges, regions); // 经测试是这里卡住了 - edgeItem = new EdgeItem(edge, false); - edgeItem.FindRegion(edges, regions); - } + var topo = new Topo(curves); + topo.GetEdgesAndnewCurves(edges,newCurves); + topo.AdjacencyList(edges, newCurves); - for (int i = 0; i < regions.Count; i++) - { - for (int j = i + 1; j < regions.Count;) - { - bool eq = false; - if (regions[i].Count == regions[j].Count) - { - var node = regions[i].First; - var curve = node!.Value.Edge.Curve; - var node2 = regions[j].GetNode(e => e.Edge.Curve == curve); - //var node2 = regions[j].Find(node.Value); - if (node2 is not null) - { - eq = true; - var b = node.Value.Forward; - var b2 = node2.Value.Forward; - for (int k = 1; k < regions[i].Count; k++) - { - node = node.GetNext(b); - node2 = node2.GetNext(b2); - if (node!.Value.Edge.Curve != node2!.Value.Edge.Curve) - { - eq = false; - break; - } - } - } - } - if (eq) - regions.RemoveAt(j); - else - j++; - } - } + var regions = topo.GetRegions(edges); + topo.RegionsInfo(regions); for (int i = 0; i < regions.Count; i++) { @@ -448,123 +73,10 @@ public static List Topo(List curves) .ToArray(); newCurves.Add(new CompositeCurve3d(cs3ds).ToCurve()!); } - return newCurves; } - /// - /// 从曲线集合分离边界(交点断分曲线的)和独立的曲线 - /// - /// 传入判断的曲线集 - /// 边界(可能仍然存在自闭,因为样条曲线允许打个鱼形圈,尾巴又交叉在其他曲线) - /// 自闭的曲线 - public static void GetEdgesAndnewCurves(List curves, List edgesOut, List closedCurvesOut) - { - //首先按交点分解为Ge曲线集 - var geCurves = new List(); - var paramss = new List>(); - - for (int i = 0; i < curves.Count; i++) - { - var cc3d = curves[i].ToCompositeCurve3d(); - if (cc3d is not null) - { - geCurves.Add(cc3d); - paramss.Add(new List()); - } - } - - var cci3d = new CurveCurveIntersector3d(); - - //遍历所有曲线,然后获取交点...此处是O(n²) - for (int i = 0; i < curves.Count; i++) - { - var gc1 = geCurves[i]; - var pars1 = paramss[i]; - //曲线a,和曲线b/c/d/e...分别交点,组成交点数组 - //第一次是 aa对比,所以会怎么样呢?(交点无限个) - for (int j = i; j < curves.Count; j++) - { - var gc2 = geCurves[j]; - var pars2 = paramss[j]; - - //求交类此方法内部会重置,不需要清空,每次set都会有个新的结果 - cci3d.Set(gc1, gc2, Vector3d.ZAxis); - - //计算两条曲线的交点(多个),分别放入对应的交点参数集 - for (int k = 0; k < cci3d.NumberOfIntersectionPoints; k++) - { - var pars = cci3d.GetIntersectionParameters(k); - pars1.Add(pars[0]);//0是第一条曲线的交点参数 - pars2.Add(pars[1]);//1是第二条曲线的交点参数 - } - } - - - if (gc1.IsClosed()) - { - closedCurvesOut.Add(gc1.ToCurve()!); - } - //有交点参数 - if (pars1.Count > 0) - { - //根据交点参数断分曲线,然后获取边界 - var c3ds = gc1.GetSplitCurves(pars1); - if (c3ds.Count > 0) - { - edgesOut.AddRange(c3ds.Select(c => new Edge(c))); - } - //惊惊留:(不敢删啊...) - //狐哥写的这里出现的条件是:有曲线参数,但是切分不出来曲线...没懂为什么... - //猜测:曲线参数在头或者尾,那么交点就是直接碰头碰尾, - //也就是 aa对比 同一条曲线自己和自己产生的? - //参数大于0{是这些参数? 头参/尾参/参数不在曲线上?} - //else if (gc1.IsClosed()) - //{ - // closedCurvesOut.Add(gc1.ToCurve()!); - //} - else - { - edgesOut.Add(new Edge(gc1)); - } - } - //else if (gc1.IsClosed()) - //{ - // closedCurvesOut.Add(gc1.ToCurve()!); - //} - } - edgesOut = edgesOut.Distinct(new EdgeComparer()).ToList(); - } - - - private class EdgeComparer : IEqualityComparer - { - - public bool Equals(Edge x, Edge y) - { -#if ac2009 - var pts = x.Curve.GetSamplePoints(100); - return pts.All(pt => y.Curve.IsOn(pt)); -#elif ac2013 || ac2015 - var pts = x.Curve.GetSamplePoints(100); - return pts.All(pt => y.Curve.IsOn(pt.Point)); -#endif - - - //return x.Curve.IsEqualTo(y.Curve); - } - - // If Equals() returns true for a pair of objects - // then GetHashCode() must return the same value for these objects. - - public int GetHashCode(Edge product) - { - return product.Curve.GetHashCode(); - } - } - - - + /// /// 曲线打断 diff --git a/src/IFoxCAD.Cad/ExtensionMethod/Topo.cs b/src/IFoxCAD.Cad/ExtensionMethod/Topo.cs new file mode 100644 index 0000000..dc88add --- /dev/null +++ b/src/IFoxCAD.Cad/ExtensionMethod/Topo.cs @@ -0,0 +1,685 @@ +using IFoxCAD.Basal; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; + +namespace IFoxCAD.Cad +{ + /// + /// 边节点 + /// + public struct EdgeItem : IEquatable + { + #region 字段 + /// + /// 边界 + /// + public Edge Edge; + /// + /// 用来判断搜索方向(向前还是向后) + /// + public bool Forward; + #endregion + + #region 构造 + public EdgeItem(Edge edge, bool forward) + { + Edge = edge; + Forward = forward; + } + #endregion + + #region 方法 + public CompositeCurve3d? GetCurve() + { + var cc3d = Edge.Curve; + if (Forward) + { + return cc3d; + } + else + { + //反向曲线参数 + cc3d = cc3d.Clone() as CompositeCurve3d; + return cc3d?.GetReverseParameterCurve() as CompositeCurve3d; + } + } + + /// + /// 查找面域 + /// + /// + /// + public void FindRegion(List edges, List> regions) + { + var region = new LoopList(); + var edgeItem = this; + region.Add(edgeItem); + var edgeItem2 = this.GetNext(edges); + if (edgeItem2.Edge is null) + return; + + bool hasList = false; + + for (int i = 0; i < regions.Count; i++) + { + var edgeList2 = regions[i]; + var node = edgeList2.GetNode(e => e.Equals(edgeItem)); + if (node is not null && node != edgeList2.Last) + { + if (node.Next!.Value.Equals(edgeItem2)) + { + hasList = true; + break; + } + } + } + if (!hasList) + { + while (edgeItem2.Edge is not null) + { + if (edgeItem2.Edge == edgeItem.Edge) + break; + region.Add(edgeItem2); //TODO 此处死循环,上一条语句判断失误,导致不停的将相同的值加入region + edgeItem2 = edgeItem2.GetNext(edges); + } + if (edgeItem2.Edge == edgeItem.Edge) + regions.Add(region); + } + } + + /// + /// 获取下一个 + /// + /// + /// + public EdgeItem GetNext(List edges) + { + Vector3d vec; + int next; + if (Forward) + { + vec = Edge.GetEndVector(); + next = Edge.EndIndex; + } + else + { + vec = Edge.GetStartVector(); + next = Edge.StartIndex; + } + + EdgeItem item = new(); + Vector3d vec2, vec3 = new(); + double angle = 0; + bool hasNext = false; + bool forward = false; + for (int i = 0; i < edges.Count; i++) + { + var edge = edges[i]; + if (edge.IsNext(Edge, next, ref vec3, ref forward)) + { + if (hasNext) + { + var angle2 = vec.GetAngleTo(vec3, Vector3d.ZAxis); + if (angle2 < angle) + { + vec2 = vec3; + angle = angle2; + item.Edge = edge; + item.Forward = forward; + } + } + else + { + vec2 = vec3; + angle = vec.GetAngleTo(vec2, Vector3d.ZAxis); + item.Edge = edge; + item.Forward = forward; + hasNext = true; + } + } + } + return item; + } + #endregion + + #region 类型转换 + public override string ToString() + { + return + Forward ? + string.Format("{0}-{1}", Edge.StartIndex, Edge.EndIndex) : + string.Format("{0}-{1}", Edge.EndIndex, Edge.StartIndex); + } + #endregion + + #region 重载运算符_比较 + public override bool Equals(object obj) + { + return this == (EdgeItem)obj; + } + public bool Equals(EdgeItem b) + { + return this == b; + } + public static bool operator !=(EdgeItem a, EdgeItem b) + { + return !(a == b); + } + public static bool operator ==(EdgeItem a, EdgeItem b) + { + return a.Edge == b.Edge && a.Forward == b.Forward; + } + public override int GetHashCode() + { + return base.GetHashCode(); + //return this.ToString().GetHashCode(); + } + #endregion + } + + /// + /// 边 + /// + public class Edge : IEquatable + { + #region 字段 + public CompositeCurve3d Curve; + public int StartIndex; + public int EndIndex; + #endregion + + #region 构造 + public Edge(CompositeCurve3d curve) + { + Curve = curve; + } + #endregion + + #region 方法 + public Vector3d GetStartVector() + { + var inter = Curve.GetInterval(); + PointOnCurve3d poc = new(Curve, inter.LowerBound); + return poc.GetDerivative(1); + } + + public Vector3d GetEndVector() + { + var inter = Curve.GetInterval(); + PointOnCurve3d poc = new(Curve, inter.UpperBound); + return -poc.GetDerivative(1); + } + + /// + /// 判断节点位置 + /// + /// 边界 + /// 边界是否位于此处 + /// + /// + /// + public bool IsNext(Edge edge, int startOrEndIndex, ref Vector3d vec, ref bool forward) + { + if (edge != this) + { + if (StartIndex == startOrEndIndex) + { + vec = GetStartVector(); + forward = true; + return true; + } + else if (EndIndex == startOrEndIndex) + { + vec = GetEndVector(); + forward = false; + return true; + } + } + return false; + } + #endregion + + #region 重载运算符_比较 + public override bool Equals(object? obj) + { + return this == obj as Edge; + } + public bool Equals(Edge? b) + { + return this == b; + } + public static bool operator !=(Edge? a, Edge? b) + { + return !(a == b); + } + public static bool operator ==(Edge? a, Edge? b) + { + //此处地方不允许使用==null,因为此处是定义 + if (b is null) + return a is null; + else if (a is null) + return false; + if (ReferenceEquals(a, b))//同一对象 + return true; + + return a.Curve == b.Curve && + a.StartIndex == b.StartIndex && + a.EndIndex == b.EndIndex; + } + + public override int GetHashCode() + { + return base.GetHashCode(); + } + #endregion + } + + public class Topo + { + /// + /// 用于切割的曲线集 + /// + List _curves; + + public Topo(List curves) + { + _curves = curves; + } + + /// + /// 从曲线集合分离边界(交点断分曲线的)和独立的曲线 + /// + /// 边界(可能仍然存在自闭,因为样条曲线允许打个鱼形圈,尾巴又交叉在其他曲线) + /// 自闭的曲线 + public void GetEdgesAndnewCurves(List edges, List closedCurvesOut) + { + //首先按交点分解为Ge曲线集 + var geCurves = new List(); + var paramss = new List>(); + + for (int i = 0; i < _curves.Count; i++) + { + var cc3d = _curves[i].ToCompositeCurve3d(); + if (cc3d is not null) + { + geCurves.Add(cc3d); + paramss.Add(new List()); + } + } + + var cci3d = new CurveCurveIntersector3d(); + + //遍历所有曲线,然后获取交点...此处是O(n²) + for (int i = 0; i < _curves.Count; i++) + { + var gc1 = geCurves[i]; + var pars1 = paramss[i]; + //曲线a,和曲线b/c/d/e...分别交点,组成交点数组 + //第一次是 aa对比,所以会怎么样呢?(交点无限个) + for (int j = i; j < _curves.Count; j++) + { + var gc2 = geCurves[j]; + var pars2 = paramss[j]; + + //求交类此方法内部会重置,不需要清空,每次set都会有个新的结果 + cci3d.Set(gc1, gc2, Vector3d.ZAxis); + + //计算两条曲线的交点(多个),分别放入对应的交点参数集 + for (int k = 0; k < cci3d.NumberOfIntersectionPoints; k++) + { + var pars = cci3d.GetIntersectionParameters(k); + pars1.Add(pars[0]);//0是第一条曲线的交点参数 + pars2.Add(pars[1]);//1是第二条曲线的交点参数 + } + } + + if (gc1.IsClosed()) + { + closedCurvesOut.Add(gc1.ToCurve()!); + } + //有交点参数 + if (pars1.Count > 0) + { + //根据交点参数断分曲线,然后获取边界 + var c3ds = gc1.GetSplitCurves(pars1); + if (c3ds.Count > 0) + { + edges.AddRange(c3ds.Select(c => new Edge(c))); + } + //惊惊留:(不敢删啊...) + //狐哥写的这里出现的条件是:有曲线参数,但是切分不出来曲线...没懂为什么... + //猜测:曲线参数在头或者尾,那么交点就是直接碰头碰尾, + //也就是 aa对比 同一条曲线自己和自己产生的? + //参数大于0{是这些参数? 头参/尾参/参数不在曲线上?} + //else if (gc1.IsClosed()) + //{ + // closedCurvesOut.Add(gc1.ToCurve()!); + //} + else + { + edges.Add(new Edge(gc1)); + } + } + //else if (gc1.IsClosed()) + //{ + // closedCurvesOut.Add(gc1.ToCurve()!); + //} + } + edges = edges.Distinct(new EdgeComparer()).ToList(); + } + + + private class EdgeComparer : IEqualityComparer + { + + public bool Equals(Edge x, Edge y) + { +#if ac2009 + var pts = x.Curve.GetSamplePoints(100); + return pts.All(pt => y.Curve.IsOn(pt)); +#elif ac2013 || ac2015 + var pts = x.Curve.GetSamplePoints(100); + return pts.All(pt => y.Curve.IsOn(pt.Point)); +#endif + //return x.Curve.IsEqualTo(y.Curve); + } + + // If Equals() returns true for a pair of objects + // then GetHashCode() must return the same value for these objects. + public int GetHashCode(Edge product) + { + return product.Curve.GetHashCode(); + } + } + +#if true2 + /// + /// 从曲线集合分离边界(交点断分曲线的)和独立的曲线 + /// + /// 边界(可能仍然存在自闭,因为样条曲线允许打个鱼形圈,尾巴又交叉在其他曲线) + /// 自闭的曲线 + public void GetEdgesAndnewCurves2(List closedCurvesOut) + { + //首先按交点分解为Ge曲线集 + var geCurves = new List(); + var paramss = new List>(); + + for (int i = 0; i < _curves.Count; i++) + { + var cc3d = _curves[i].ToCompositeCurve3d(); + if (cc3d is not null) + { + geCurves.Add(cc3d); + paramss.Add(new List()); + } + } + + var cci3d = new CurveCurveIntersector3d(); + + //遍历所有曲线,然后获取交点...此处是O(n²) + for (int i = 0; i < geCurves.Count; i++) + { + var gc1 = geCurves[i]; + var pars1 = paramss[i]; + //曲线a,和曲线b/c/d/e...分别交点,组成交点数组 + //第一次是 aa对比,所以会怎么样呢?(交点无限个 cci3d.NumberOfIntersectionPoints == 0) + for (int j = i; j < geCurves.Count; j++) + { + var gc2 = geCurves[j]; + var pars2 = paramss[j]; + + //求交类此方法内部会重置,不需要清空,每次set都会有个新的结果 + cci3d.Set(gc1, gc2, Vector3d.ZAxis); + + //没有交点,可能为同方向重叠的 + //完全重合多段线的时候,此处也不是 ==0 + if (cci3d.NumberOfIntersectionPoints == 0) + { + //TODO 两个内接矩形共边(重叠部分边界)会导致处理曲线错误 + var gc1s = gc1.GetCurves(); + var gc2s = gc2.GetCurves(); + if (gc1s.Length == gc2s.Length && gc1s.Length == 0) + continue; + + //点在它们之间 + //if (OnLine(gc1.StartPoint, gc1.EndPoint, gc2.StartPoint)) + //{ + // var line1 = new Line(gc1.StartPoint, gc2.StartPoint).ToCompositeCurve3d(); + // var line2 = new Line(gc2.StartPoint, gc1.EndPoint).ToCompositeCurve3d(); + // _edges.Add(new Edge(line1!)); + // _edges.Add(new Edge(line2!)); + //} + //if (OnLine(gc1.StartPoint, gc1.EndPoint, gc2.EndPoint)) + //{ + // var line1 = new Line(gc1.StartPoint, gc2.EndPoint).ToCompositeCurve3d(); + // var line2 = new Line(gc2.EndPoint, gc1.EndPoint).ToCompositeCurve3d(); + // _edges.Add(new Edge(line1!)); + // _edges.Add(new Edge(line2!)); + //} + } + else + { + //计算两条曲线的交点(多个),分别放入对应的交点参数集 + for (int k = 0; k < cci3d.NumberOfIntersectionPoints; k++) + { + var pars = cci3d.GetIntersectionParameters(k); + pars1.Add(pars[0]);//0是第一条曲线的交点参数 + pars2.Add(pars[1]);//1是第二条曲线的交点参数 + } + } + } + + //有交点参数 + if (pars1.Count > 0) + { + //根据交点参数断分曲线,然后获取边界 + var c3ds = gc1.GetSplitCurves(pars1); + if (c3ds.Count > 0) + { + _edges.AddRange(c3ds.Select(c => new Edge(c))); + } + //惊惊留:(不敢删啊...) + //狐哥写的这里出现的条件是:有曲线参数,但是切分不出来曲线...没懂为什么... + //猜测:曲线参数在头或者尾,那么交点就是直接碰头碰尾, + //也就是 aa对比 同一条曲线自己和自己产生的? + //参数大于0{是这些参数? 头参/尾参/参数不在曲线上?} + else if (gc1.IsClosed()) + { + closedCurvesOut.Add(gc1.ToCurve()!); + } + else + { + _edges.Add(new Edge(gc1)); + } + } + else if (gc1.IsClosed()) + { + closedCurvesOut.Add(gc1.ToCurve()!); + } + } + } +#endif + + /// + /// 判断点是否在线段上 + /// 原文链接 + /// + /// 线段点头 + /// 线段点尾 + /// this是否线段ab内 + public bool OnLine(Point3d sp, Point3d ep, Point3d op) + { + //叉乘是保证面积一致 + //叉乘:依次用手指盖住每列,交叉相乘再相减 + var cross = (sp.X - op.X) * (ep.Y - op.Y) - (ep.X - op.X) * (sp.Y - op.Y) < 1e-10; + + return cross + && Math.Min(sp.X, ep.X) <= op.X + && op.X <= Math.Max(sp.X, ep.X) + && Math.Min(sp.Y, ep.Y) <= op.Y + && op.Y <= Math.Max(sp.Y, ep.Y); + } + + /// + /// 创建邻接表 + /// + /// + /// + public void AdjacencyList(List edges, List closedCurvesOut) + { + //构建边的邻接表 + //knots 和 nums 实际上是一个键值对(基于ArrayOfStruct思想,拆开更合适内存布局) + //knots 是不重复地将所有交点设置为节点,如果是重复,就对应 nums++ + //nums 是记录每个节点坐标被重复了几次 + var knots = new List(); + var nums = new List(); + var closedEdges = new List(); + + for (int i = 0; i < edges.Count; i++) + { + var edge = edges[i]; + if (edge.Curve.IsClosed()) + { + closedEdges.Add(edge); + continue; + } + + if (knots.Contains(edge.Curve.StartPoint)) + { + //含有就是其他曲线"共用"此交点, + //节点所在索引==共用计数索引=>将它++ + edge.StartIndex = knots.IndexOf(edge.Curve.StartPoint); + nums[edge.StartIndex]++; + } + else + { + //不含有就加入节点,共用计数也加入,边界设置节点索引 + knots.Add(edge.Curve.StartPoint); + nums.Add(1); + edge.StartIndex = knots.Count - 1; + } + + if (knots.Contains(edge.Curve.EndPoint)) + { + edge.EndIndex = knots.IndexOf(edge.Curve.EndPoint); + nums[edge.EndIndex]++; + } + else + { + knots.Add(edge.Curve.EndPoint); + nums.Add(1); + edge.EndIndex = knots.Count - 1; + } + } + + closedCurvesOut.AddRange(closedEdges.Select(e => e.Curve.ToCurve())!); + + //这里把交点只有一条曲线通过的点过滤掉了,也就是尾巴的图元, + //剩下的都是闭合的曲线连接了,每个点都至少有两条曲线通过 + var tmp = edges + .Except(closedEdges) + .Where(e => nums[e.StartIndex] > 1 && nums[e.EndIndex] > 1); + edges.Clear(); + var ge = tmp.GetEnumerator(); + while (ge.MoveNext()) + { + edges.Add(ge.Current); + } + + //这一大坨不用看了 注释掉也没影响貌似,而且后续也没有用 nums + // foreach (var edge in edges.Except(closedEdges)) + // { + // if (nums[edge.StartIndex] == 1 || nums[edge.EndIndex] == 1) + // { + // if (nums[edge.StartIndex] == 1 && nums[edge.EndIndex] == 1) + // { + // nums[edge.StartIndex] = 0; + // nums[edge.EndIndex] = 0; + // } + // else + // { + // int next = -1; + // if (nums[edge.StartIndex] == 1) + // { + // nums[edge.StartIndex] = 0; + // nums[next = edge.EndIndex]--; + // } + // else + // { + // nums[edge.EndIndex] = 0; + // nums[next = edge.StartIndex]--; + // } + // } + // } + // } + } + + /// + /// 获取多个面域 + /// + /// + /// + public List> GetRegions(List edges) + { + //利用边界的顺序和逆序获取闭合链条 + var regions = new List>(); + for (int i = 0; i < edges.Count; i++) + { + var edge = edges[i]; + //TODO 这里有bug,两个内接的矩形会卡死 + var edgeItem = new EdgeItem(edge, true); + edgeItem.FindRegion(edges, regions); // 经测试是这里卡住了 testTopo + edgeItem = new EdgeItem(edge, false); + edgeItem.FindRegion(edges, regions); + } + return regions; + } + + /// + /// 这是做什么的 + /// + /// + public void RegionsInfo(List> regions) + { + for (int i = 0; i < regions.Count; i++) + { + for (int j = i + 1; j < regions.Count;) + { + bool eq = false; + if (regions[i].Count == regions[j].Count) + { + var node = regions[i].First; + var curve = node!.Value.Edge.Curve; + var node2 = regions[j].GetNode(e => e.Edge.Curve == curve); + //var node2 = regions[j].Find(node.Value); + if (node2 is not null) + { + eq = true; + var b = node.Value.Forward; + var b2 = node2.Value.Forward; + for (int k = 1; k < regions[i].Count; k++) + { + node = node.GetNext(b); + node2 = node2.GetNext(b2); + if (node!.Value.Edge.Curve != node2!.Value.Edge.Curve) + { + eq = false; + break; + } + } + } + } + if (eq) + regions.RemoveAt(j); + else + j++; + } + } + } + + + + } +} diff --git a/tests/Test/TestCurve.cs b/tests/Test/TestCurve.cs index 34d0254..d6095fa 100644 --- a/tests/Test/TestCurve.cs +++ b/tests/Test/TestCurve.cs @@ -8,7 +8,7 @@ public class TestCurve public void TestBreakCurve() { var ents = Env.Editor.SSGet().Value.GetEntities(); - var tt = CurveEx.BreakCurve(ents.ToList()); + var tt = CurveEx.BreakCurve(ents.ToList()); using var tr = new DBTrans(); tt.ForEach(t => t.ForWrite(e => e.ColorIndex = 1)); tr.CurrentSpace.AddEntity(tt); @@ -16,19 +16,21 @@ public void TestBreakCurve() [CommandMethod("testtopo")] public void TestToPo() { - var ents = Env.Editor.SSGet().Value.GetEntities(); + var ents = Env.Editor.SSGet().Value?.GetEntities(); + if (ents == null) + return; var tt = CurveEx.Topo(ents.ToList()); using var tr = new DBTrans(); tt.ForEach(t => t.ForWrite(e => e.ColorIndex = 1)); tr.CurrentSpace.AddEntity(tt); - + } [CommandMethod("testCurveCurveIntersector3d")] public void TestCurveCurveIntersector3d() { var ents = Env.Editor.SSGet().Value.GetEntities() .Select(e => e.ToCompositeCurve3d()).ToList(); - + var cci3d = new CurveCurveIntersector3d(); @@ -42,7 +44,7 @@ public void TestCurveCurveIntersector3d() var gc2 = ents[j]; //var pars2 = paramss[j]; var int2 = gc2.GetInterval(); - cci3d.Set(gc1, gc2, int1,int2, Vector3d.ZAxis); + cci3d.Set(gc1, gc2, int1, int2, Vector3d.ZAxis); var d = cci3d.OverlapCount(); var a = cci3d.GetIntersectionRanges(); Env.Print($"{a[0].LowerBound}-{a[0].UpperBound} and {a[1].LowerBound}-{a[1].UpperBound}"); @@ -78,10 +80,13 @@ public void TestCurveCurveIntersector3d() [CommandMethod("testGetEdgesAndnewCurves")] public void TestGetEdgesAndnewCurves() { - var curves = Env.Editor.SSGet().Value.GetEntities().ToList(); - var edges = new List(); + var curves = Env.Editor.SSGet().Value?.GetEntities().ToList(); + if (curves == null) + return; + var edges = new List(); var newCurves = new List(); - CurveEx.GetEdgesAndnewCurves(curves, edges, newCurves); + var topo = new Topo(curves); + topo.GetEdgesAndnewCurves(edges, newCurves); using var tr = new DBTrans(); @@ -92,9 +97,7 @@ public void TestGetEdgesAndnewCurves() tr.CurrentSpace.AddEntity(ent); } - //Env.Print(""); - } } } -- Gitee From aea0719af86e60c309df5d91f0c582a09609dde9 Mon Sep 17 00:00:00 2001 From: liuqihong <540762622@qq.com> Date: Tue, 5 Apr 2022 00:53:10 +0800 Subject: [PATCH 071/675] =?UTF-8?q?GetEdgesAndnewCurves=E7=AC=AC=E4=B8=80?= =?UTF-8?q?=E4=B8=AA=E5=BE=AA=E7=8E=AF=E4=B9=8B=E5=90=8E=E4=B8=8D=E5=86=8D?= =?UTF-8?q?=E7=94=A8=5Fcurves?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/IFoxCAD.Cad/ExtensionMethod/CurveEx.cs | 4 +- src/IFoxCAD.Cad/ExtensionMethod/Topo.cs | 1011 +++++++++----------- src/IFoxCAD.Cad/IFoxCAD.Cad.csproj | 15 + tests/Test/TestCurve.cs | 25 +- 4 files changed, 464 insertions(+), 591 deletions(-) diff --git a/src/IFoxCAD.Cad/ExtensionMethod/CurveEx.cs b/src/IFoxCAD.Cad/ExtensionMethod/CurveEx.cs index 648d96a..6b6660c 100644 --- a/src/IFoxCAD.Cad/ExtensionMethod/CurveEx.cs +++ b/src/IFoxCAD.Cad/ExtensionMethod/CurveEx.cs @@ -48,7 +48,7 @@ public static IEnumerable GetSplitCurves(this Curve curve, IEnumerable - /// 获取曲线集所围成的封闭区域的曲线集 TESTTopo + /// 获取曲线集所围成的封闭区域的曲线集 /// /// 曲线集 /// 闭合的曲线集 @@ -76,8 +76,6 @@ public static List Topo(List curves) return newCurves; } - - /// /// 曲线打断 /// diff --git a/src/IFoxCAD.Cad/ExtensionMethod/Topo.cs b/src/IFoxCAD.Cad/ExtensionMethod/Topo.cs index dc88add..e27e1a1 100644 --- a/src/IFoxCAD.Cad/ExtensionMethod/Topo.cs +++ b/src/IFoxCAD.Cad/ExtensionMethod/Topo.cs @@ -1,685 +1,544 @@ -using IFoxCAD.Basal; -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; +namespace IFoxCAD.Cad; -namespace IFoxCAD.Cad +using IFoxCAD.Basal; + +/// +/// 边节点 +/// +public struct EdgeItem : IEquatable { + #region 字段 + /// + /// 边界 + /// + public Edge Edge; /// - /// 边节点 + /// 用来判断搜索方向(向前还是向后) /// - public struct EdgeItem : IEquatable + public bool Forward; + #endregion + + #region 构造 + public EdgeItem(Edge edge, bool forward) { - #region 字段 - /// - /// 边界 - /// - public Edge Edge; - /// - /// 用来判断搜索方向(向前还是向后) - /// - public bool Forward; - #endregion - - #region 构造 - public EdgeItem(Edge edge, bool forward) + Edge = edge; + Forward = forward; + } + #endregion + + #region 方法 + public CompositeCurve3d? GetCurve() + { + var cc3d = Edge.Curve; + if (Forward) { - Edge = edge; - Forward = forward; + return cc3d; } - #endregion - - #region 方法 - public CompositeCurve3d? GetCurve() + else { - var cc3d = Edge.Curve; - if (Forward) - { - return cc3d; - } - else - { - //反向曲线参数 - cc3d = cc3d.Clone() as CompositeCurve3d; - return cc3d?.GetReverseParameterCurve() as CompositeCurve3d; - } + //反向曲线参数 + cc3d = cc3d.Clone() as CompositeCurve3d; + return cc3d?.GetReverseParameterCurve() as CompositeCurve3d; } + } - /// - /// 查找面域 - /// - /// - /// - public void FindRegion(List edges, List> regions) - { - var region = new LoopList(); - var edgeItem = this; - region.Add(edgeItem); - var edgeItem2 = this.GetNext(edges); - if (edgeItem2.Edge is null) - return; + /// + /// 查找面域 + /// + /// + /// + public void FindRegion(List edges, List> regions) + { + var region = new LoopList(); + var edgeItem = this; + region.Add(edgeItem); + var edgeItem2 = this.GetNext(edges); + if (edgeItem2.Edge is null) + return; - bool hasList = false; + bool hasList = false; - for (int i = 0; i < regions.Count; i++) + for (int i = 0; i < regions.Count; i++) + { + var edgeList2 = regions[i]; + var node = edgeList2.GetNode(e => e.Equals(edgeItem)); + if (node is not null && node != edgeList2.Last) { - var edgeList2 = regions[i]; - var node = edgeList2.GetNode(e => e.Equals(edgeItem)); - if (node is not null && node != edgeList2.Last) + if (node.Next!.Value.Equals(edgeItem2)) { - if (node.Next!.Value.Equals(edgeItem2)) - { - hasList = true; - break; - } + hasList = true; + break; } } - if (!hasList) + } + if (!hasList) + { + while (edgeItem2.Edge is not null) { - while (edgeItem2.Edge is not null) - { - if (edgeItem2.Edge == edgeItem.Edge) - break; - region.Add(edgeItem2); //TODO 此处死循环,上一条语句判断失误,导致不停的将相同的值加入region - edgeItem2 = edgeItem2.GetNext(edges); - } if (edgeItem2.Edge == edgeItem.Edge) - regions.Add(region); + break; + region.Add(edgeItem2); //TODO 此处死循环,上一条语句判断失误,导致不停的将相同的值加入region + edgeItem2 = edgeItem2.GetNext(edges); } + if (edgeItem2.Edge == edgeItem.Edge) + regions.Add(region); } + } - /// - /// 获取下一个 - /// - /// - /// - public EdgeItem GetNext(List edges) + /// + /// 获取下一个 + /// + /// + /// + public EdgeItem GetNext(List edges) + { + Vector3d vec; + int next; + if (Forward) { - Vector3d vec; - int next; - if (Forward) - { - vec = Edge.GetEndVector(); - next = Edge.EndIndex; - } - else - { - vec = Edge.GetStartVector(); - next = Edge.StartIndex; - } + vec = Edge.GetEndVector(); + next = Edge.EndIndex; + } + else + { + vec = Edge.GetStartVector(); + next = Edge.StartIndex; + } - EdgeItem item = new(); - Vector3d vec2, vec3 = new(); - double angle = 0; - bool hasNext = false; - bool forward = false; - for (int i = 0; i < edges.Count; i++) + EdgeItem item = new(); + Vector3d vec2, vec3 = new(); + double angle = 0; + bool hasNext = false; + bool forward = false; + for (int i = 0; i < edges.Count; i++) + { + var edge = edges[i]; + if (edge.IsNext(Edge, next, ref vec3, ref forward)) { - var edge = edges[i]; - if (edge.IsNext(Edge, next, ref vec3, ref forward)) + if (hasNext) { - if (hasNext) - { - var angle2 = vec.GetAngleTo(vec3, Vector3d.ZAxis); - if (angle2 < angle) - { - vec2 = vec3; - angle = angle2; - item.Edge = edge; - item.Forward = forward; - } - } - else + var angle2 = vec.GetAngleTo(vec3, Vector3d.ZAxis); + if (angle2 < angle) { vec2 = vec3; - angle = vec.GetAngleTo(vec2, Vector3d.ZAxis); + angle = angle2; item.Edge = edge; item.Forward = forward; - hasNext = true; } } + else + { + vec2 = vec3; + angle = vec.GetAngleTo(vec2, Vector3d.ZAxis); + item.Edge = edge; + item.Forward = forward; + hasNext = true; + } } - return item; } - #endregion + return item; + } + #endregion - #region 类型转换 - public override string ToString() - { - return - Forward ? - string.Format("{0}-{1}", Edge.StartIndex, Edge.EndIndex) : - string.Format("{0}-{1}", Edge.EndIndex, Edge.StartIndex); - } - #endregion + #region 类型转换 + public override string ToString() + { + return + Forward ? + string.Format("{0}-{1}", Edge.StartIndex, Edge.EndIndex) : + string.Format("{0}-{1}", Edge.EndIndex, Edge.StartIndex); + } + #endregion - #region 重载运算符_比较 - public override bool Equals(object obj) - { - return this == (EdgeItem)obj; - } - public bool Equals(EdgeItem b) - { - return this == b; - } - public static bool operator !=(EdgeItem a, EdgeItem b) - { - return !(a == b); - } - public static bool operator ==(EdgeItem a, EdgeItem b) - { - return a.Edge == b.Edge && a.Forward == b.Forward; - } - public override int GetHashCode() - { - return base.GetHashCode(); - //return this.ToString().GetHashCode(); - } - #endregion + #region 重载运算符_比较 + public override bool Equals(object obj) + { + return this == (EdgeItem)obj; + } + public bool Equals(EdgeItem b) + { + return this == b; + } + public static bool operator !=(EdgeItem a, EdgeItem b) + { + return !(a == b); + } + public static bool operator ==(EdgeItem a, EdgeItem b) + { + return a.Edge == b.Edge && a.Forward == b.Forward; } + public override int GetHashCode() + { + return base.GetHashCode(); + //return this.ToString().GetHashCode(); + } + #endregion +} - /// - /// 边 - /// - public class Edge : IEquatable +/// +/// 边 +/// +public class Edge : IEquatable +{ + #region 字段 + public CompositeCurve3d Curve; + public int StartIndex; + public int EndIndex; + #endregion + + #region 构造 + public Edge(CompositeCurve3d curve) { - #region 字段 - public CompositeCurve3d Curve; - public int StartIndex; - public int EndIndex; - #endregion - - #region 构造 - public Edge(CompositeCurve3d curve) - { - Curve = curve; - } - #endregion + Curve = curve; + } + #endregion - #region 方法 - public Vector3d GetStartVector() - { - var inter = Curve.GetInterval(); - PointOnCurve3d poc = new(Curve, inter.LowerBound); - return poc.GetDerivative(1); - } + #region 方法 + public Vector3d GetStartVector() + { + var inter = Curve.GetInterval(); + PointOnCurve3d poc = new(Curve, inter.LowerBound); + return poc.GetDerivative(1); + } - public Vector3d GetEndVector() - { - var inter = Curve.GetInterval(); - PointOnCurve3d poc = new(Curve, inter.UpperBound); - return -poc.GetDerivative(1); - } + public Vector3d GetEndVector() + { + var inter = Curve.GetInterval(); + PointOnCurve3d poc = new(Curve, inter.UpperBound); + return -poc.GetDerivative(1); + } - /// - /// 判断节点位置 - /// - /// 边界 - /// 边界是否位于此处 - /// - /// - /// - public bool IsNext(Edge edge, int startOrEndIndex, ref Vector3d vec, ref bool forward) + /// + /// 判断节点位置 + /// + /// 边界 + /// 边界是否位于此处 + /// + /// + /// + public bool IsNext(Edge edge, int startOrEndIndex, ref Vector3d vec, ref bool forward) + { + if (edge != this) { - if (edge != this) + if (StartIndex == startOrEndIndex) { - if (StartIndex == startOrEndIndex) - { - vec = GetStartVector(); - forward = true; - return true; - } - else if (EndIndex == startOrEndIndex) - { - vec = GetEndVector(); - forward = false; - return true; - } + vec = GetStartVector(); + forward = true; + return true; + } + else if (EndIndex == startOrEndIndex) + { + vec = GetEndVector(); + forward = false; + return true; } - return false; } - #endregion + return false; + } + #endregion - #region 重载运算符_比较 - public override bool Equals(object? obj) - { - return this == obj as Edge; - } - public bool Equals(Edge? b) - { - return this == b; - } - public static bool operator !=(Edge? a, Edge? b) - { - return !(a == b); - } - public static bool operator ==(Edge? a, Edge? b) - { - //此处地方不允许使用==null,因为此处是定义 - if (b is null) - return a is null; - else if (a is null) - return false; - if (ReferenceEquals(a, b))//同一对象 - return true; + #region 重载运算符_比较 + public override bool Equals(object? obj) + { + return this == obj as Edge; + } + public bool Equals(Edge? b) + { + return this == b; + } + public static bool operator !=(Edge? a, Edge? b) + { + return !(a == b); + } + public static bool operator ==(Edge? a, Edge? b) + { + //此处地方不允许使用==null,因为此处是定义 + if (b is null) + return a is null; + else if (a is null) + return false; + if (ReferenceEquals(a, b))//同一对象 + return true; - return a.Curve == b.Curve && - a.StartIndex == b.StartIndex && - a.EndIndex == b.EndIndex; - } + return a.Curve == b.Curve && + a.StartIndex == b.StartIndex && + a.EndIndex == b.EndIndex; + } - public override int GetHashCode() - { - return base.GetHashCode(); - } - #endregion + public override int GetHashCode() + { + return base.GetHashCode(); } + #endregion +} + +public class Topo +{ + /// + /// 用于切割的曲线集 + /// + List _curves; - public class Topo + public Topo(List curves) { - /// - /// 用于切割的曲线集 - /// - List _curves; - - public Topo(List curves) - { - _curves = curves; - } + _curves = curves; + } - /// - /// 从曲线集合分离边界(交点断分曲线的)和独立的曲线 - /// - /// 边界(可能仍然存在自闭,因为样条曲线允许打个鱼形圈,尾巴又交叉在其他曲线) - /// 自闭的曲线 - public void GetEdgesAndnewCurves(List edges, List closedCurvesOut) - { - //首先按交点分解为Ge曲线集 - var geCurves = new List(); - var paramss = new List>(); + /// + /// 从曲线集合分离边界(交点断分曲线的)和独立的曲线 + /// + /// 边界(可能仍然存在自闭,因为样条曲线允许打个鱼形圈,尾巴又交叉在其他曲线) + /// 自闭的曲线 + public void GetEdgesAndnewCurves(List edges, List closedCurvesOut) + { + var geCurves = new List(); + var paramss = new List>(); - for (int i = 0; i < _curves.Count; i++) + //将曲线按节点转为Ge曲线集(提取多段线每段/样条曲线子段..) + for (int i = 0; i < _curves.Count; i++) + { + var cc3d = _curves[i].ToCompositeCurve3d(); + if (cc3d is not null) { - var cc3d = _curves[i].ToCompositeCurve3d(); - if (cc3d is not null) - { - geCurves.Add(cc3d); - paramss.Add(new List()); - } + geCurves.Add(cc3d); + paramss.Add(new List()); } + } + + var cci3d = new CurveCurveIntersector3d(); - var cci3d = new CurveCurveIntersector3d(); + //遍历所有曲线,然后获取交点...此处是O(n²) + for (int i = 0; i < geCurves.Count; i++) + { + var gc1 = geCurves[i]; + var pars1 = paramss[i]; - //遍历所有曲线,然后获取交点...此处是O(n²) - for (int i = 0; i < _curves.Count; i++) + //曲线a和曲线a/b/c/d/e...分别交点,组成交点数组(第一次是自交对比) + for (int j = i; j < geCurves.Count; j++) { - var gc1 = geCurves[i]; - var pars1 = paramss[i]; - //曲线a,和曲线b/c/d/e...分别交点,组成交点数组 - //第一次是 aa对比,所以会怎么样呢?(交点无限个) - for (int j = i; j < _curves.Count; j++) - { - var gc2 = geCurves[j]; - var pars2 = paramss[j]; + var gc2 = geCurves[j]; + var pars2 = paramss[j]; - //求交类此方法内部会重置,不需要清空,每次set都会有个新的结果 - cci3d.Set(gc1, gc2, Vector3d.ZAxis); + //求交类此方法内部会重置,不需要清空,每次set都会有个新的结果 + cci3d.Set(gc1, gc2, Vector3d.ZAxis); - //计算两条曲线的交点(多个),分别放入对应的交点参数集 - for (int k = 0; k < cci3d.NumberOfIntersectionPoints; k++) - { - var pars = cci3d.GetIntersectionParameters(k); - pars1.Add(pars[0]);//0是第一条曲线的交点参数 - pars2.Add(pars[1]);//1是第二条曲线的交点参数 - } + //计算两条曲线的交点(多个),分别放入对应的交点参数集 + for (int k = 0; k < cci3d.NumberOfIntersectionPoints; k++) + { + var pars = cci3d.GetIntersectionParameters(k); + pars1.Add(pars[0]);//0是第一条曲线的交点参数 + pars2.Add(pars[1]);//1是第二条曲线的交点参数 } + } - if (gc1.IsClosed()) + if (gc1.IsClosed()) + { + closedCurvesOut.Add(gc1.ToCurve()!); + } + //有交点参数 + if (pars1.Count > 0) + { + //根据交点参数断分曲线,然后获取边界 + var c3ds = gc1.GetSplitCurves(pars1); + if (c3ds.Count > 0) { - closedCurvesOut.Add(gc1.ToCurve()!); - } - //有交点参数 - if (pars1.Count > 0) - { - //根据交点参数断分曲线,然后获取边界 - var c3ds = gc1.GetSplitCurves(pars1); - if (c3ds.Count > 0) - { - edges.AddRange(c3ds.Select(c => new Edge(c))); - } - //惊惊留:(不敢删啊...) - //狐哥写的这里出现的条件是:有曲线参数,但是切分不出来曲线...没懂为什么... - //猜测:曲线参数在头或者尾,那么交点就是直接碰头碰尾, - //也就是 aa对比 同一条曲线自己和自己产生的? - //参数大于0{是这些参数? 头参/尾参/参数不在曲线上?} - //else if (gc1.IsClosed()) - //{ - // closedCurvesOut.Add(gc1.ToCurve()!); - //} - else - { - edges.Add(new Edge(gc1)); - } + edges.AddRange(c3ds.Select(c => new Edge(c))); } + //惊惊留:(不敢删啊...) + //狐哥写的这里出现的条件是:有曲线参数,但是切分不出来曲线...没懂为什么... + //猜测:曲线参数在头或者尾,那么交点就是直接碰头碰尾, + //也就是 aa对比 同一条曲线自己和自己产生的? + //参数大于0{是这些参数? 头参/尾参/参数不在曲线上?} //else if (gc1.IsClosed()) //{ // closedCurvesOut.Add(gc1.ToCurve()!); //} + else + { + edges.Add(new Edge(gc1)); + } } - edges = edges.Distinct(new EdgeComparer()).ToList(); } + edges = edges.Distinct(new EdgeComparer()).ToList(); + } - - private class EdgeComparer : IEqualityComparer + private class EdgeComparer : IEqualityComparer + { + public bool Equals(Edge x, Edge y) { - - public bool Equals(Edge x, Edge y) - { #if ac2009 - var pts = x.Curve.GetSamplePoints(100); - return pts.All(pt => y.Curve.IsOn(pt)); + var pts = x.Curve.GetSamplePoints(100); + return pts.All(pt => y.Curve.IsOn(pt)); #elif ac2013 || ac2015 - var pts = x.Curve.GetSamplePoints(100); - return pts.All(pt => y.Curve.IsOn(pt.Point)); + var pts = x.Curve.GetSamplePoints(100); + return pts.All(pt => y.Curve.IsOn(pt.Point)); #endif - //return x.Curve.IsEqualTo(y.Curve); - } - - // If Equals() returns true for a pair of objects - // then GetHashCode() must return the same value for these objects. - public int GetHashCode(Edge product) - { - return product.Curve.GetHashCode(); - } + //return x.Curve.IsEqualTo(y.Curve); } -#if true2 - /// - /// 从曲线集合分离边界(交点断分曲线的)和独立的曲线 - /// - /// 边界(可能仍然存在自闭,因为样条曲线允许打个鱼形圈,尾巴又交叉在其他曲线) - /// 自闭的曲线 - public void GetEdgesAndnewCurves2(List closedCurvesOut) + // If Equals() returns true for a pair of objects + // then GetHashCode() must return the same value for these objects. + public int GetHashCode(Edge product) { - //首先按交点分解为Ge曲线集 - var geCurves = new List(); - var paramss = new List>(); + return product.Curve.GetHashCode(); + } + } - for (int i = 0; i < _curves.Count; i++) + /// + /// 创建邻接表 + /// + /// + /// + public void AdjacencyList(List edges, List closedCurvesOut) + { + //构建边的邻接表 + //knots 和 nums 实际上是一个键值对(基于ArrayOfStruct思想,拆开更合适内存布局) + //knots 是不重复地将所有交点设置为节点,如果是重复,就对应 nums++ + //nums 是记录每个节点坐标被重复了几次 + var knots = new List(); + var nums = new List(); + var closedEdges = new List(); + + for (int i = 0; i < edges.Count; i++) + { + var edge = edges[i]; + if (edge.Curve.IsClosed()) { - var cc3d = _curves[i].ToCompositeCurve3d(); - if (cc3d is not null) - { - geCurves.Add(cc3d); - paramss.Add(new List()); - } + closedEdges.Add(edge); + continue; } - var cci3d = new CurveCurveIntersector3d(); - - //遍历所有曲线,然后获取交点...此处是O(n²) - for (int i = 0; i < geCurves.Count; i++) + if (knots.Contains(edge.Curve.StartPoint)) { - var gc1 = geCurves[i]; - var pars1 = paramss[i]; - //曲线a,和曲线b/c/d/e...分别交点,组成交点数组 - //第一次是 aa对比,所以会怎么样呢?(交点无限个 cci3d.NumberOfIntersectionPoints == 0) - for (int j = i; j < geCurves.Count; j++) - { - var gc2 = geCurves[j]; - var pars2 = paramss[j]; - - //求交类此方法内部会重置,不需要清空,每次set都会有个新的结果 - cci3d.Set(gc1, gc2, Vector3d.ZAxis); - - //没有交点,可能为同方向重叠的 - //完全重合多段线的时候,此处也不是 ==0 - if (cci3d.NumberOfIntersectionPoints == 0) - { - //TODO 两个内接矩形共边(重叠部分边界)会导致处理曲线错误 - var gc1s = gc1.GetCurves(); - var gc2s = gc2.GetCurves(); - if (gc1s.Length == gc2s.Length && gc1s.Length == 0) - continue; - - //点在它们之间 - //if (OnLine(gc1.StartPoint, gc1.EndPoint, gc2.StartPoint)) - //{ - // var line1 = new Line(gc1.StartPoint, gc2.StartPoint).ToCompositeCurve3d(); - // var line2 = new Line(gc2.StartPoint, gc1.EndPoint).ToCompositeCurve3d(); - // _edges.Add(new Edge(line1!)); - // _edges.Add(new Edge(line2!)); - //} - //if (OnLine(gc1.StartPoint, gc1.EndPoint, gc2.EndPoint)) - //{ - // var line1 = new Line(gc1.StartPoint, gc2.EndPoint).ToCompositeCurve3d(); - // var line2 = new Line(gc2.EndPoint, gc1.EndPoint).ToCompositeCurve3d(); - // _edges.Add(new Edge(line1!)); - // _edges.Add(new Edge(line2!)); - //} - } - else - { - //计算两条曲线的交点(多个),分别放入对应的交点参数集 - for (int k = 0; k < cci3d.NumberOfIntersectionPoints; k++) - { - var pars = cci3d.GetIntersectionParameters(k); - pars1.Add(pars[0]);//0是第一条曲线的交点参数 - pars2.Add(pars[1]);//1是第二条曲线的交点参数 - } - } - } - - //有交点参数 - if (pars1.Count > 0) - { - //根据交点参数断分曲线,然后获取边界 - var c3ds = gc1.GetSplitCurves(pars1); - if (c3ds.Count > 0) - { - _edges.AddRange(c3ds.Select(c => new Edge(c))); - } - //惊惊留:(不敢删啊...) - //狐哥写的这里出现的条件是:有曲线参数,但是切分不出来曲线...没懂为什么... - //猜测:曲线参数在头或者尾,那么交点就是直接碰头碰尾, - //也就是 aa对比 同一条曲线自己和自己产生的? - //参数大于0{是这些参数? 头参/尾参/参数不在曲线上?} - else if (gc1.IsClosed()) - { - closedCurvesOut.Add(gc1.ToCurve()!); - } - else - { - _edges.Add(new Edge(gc1)); - } - } - else if (gc1.IsClosed()) - { - closedCurvesOut.Add(gc1.ToCurve()!); - } + //含有就是其他曲线"共用"此交点, + //节点所在索引==共用计数索引=>将它++ + edge.StartIndex = knots.IndexOf(edge.Curve.StartPoint); + nums[edge.StartIndex]++; } - } -#endif - - /// - /// 判断点是否在线段上 - /// 原文链接 - /// - /// 线段点头 - /// 线段点尾 - /// this是否线段ab内 - public bool OnLine(Point3d sp, Point3d ep, Point3d op) - { - //叉乘是保证面积一致 - //叉乘:依次用手指盖住每列,交叉相乘再相减 - var cross = (sp.X - op.X) * (ep.Y - op.Y) - (ep.X - op.X) * (sp.Y - op.Y) < 1e-10; - - return cross - && Math.Min(sp.X, ep.X) <= op.X - && op.X <= Math.Max(sp.X, ep.X) - && Math.Min(sp.Y, ep.Y) <= op.Y - && op.Y <= Math.Max(sp.Y, ep.Y); - } - - /// - /// 创建邻接表 - /// - /// - /// - public void AdjacencyList(List edges, List closedCurvesOut) - { - //构建边的邻接表 - //knots 和 nums 实际上是一个键值对(基于ArrayOfStruct思想,拆开更合适内存布局) - //knots 是不重复地将所有交点设置为节点,如果是重复,就对应 nums++ - //nums 是记录每个节点坐标被重复了几次 - var knots = new List(); - var nums = new List(); - var closedEdges = new List(); - - for (int i = 0; i < edges.Count; i++) + else { - var edge = edges[i]; - if (edge.Curve.IsClosed()) - { - closedEdges.Add(edge); - continue; - } - - if (knots.Contains(edge.Curve.StartPoint)) - { - //含有就是其他曲线"共用"此交点, - //节点所在索引==共用计数索引=>将它++ - edge.StartIndex = knots.IndexOf(edge.Curve.StartPoint); - nums[edge.StartIndex]++; - } - else - { - //不含有就加入节点,共用计数也加入,边界设置节点索引 - knots.Add(edge.Curve.StartPoint); - nums.Add(1); - edge.StartIndex = knots.Count - 1; - } - - if (knots.Contains(edge.Curve.EndPoint)) - { - edge.EndIndex = knots.IndexOf(edge.Curve.EndPoint); - nums[edge.EndIndex]++; - } - else - { - knots.Add(edge.Curve.EndPoint); - nums.Add(1); - edge.EndIndex = knots.Count - 1; - } + //不含有就加入节点,共用计数也加入,边界设置节点索引 + knots.Add(edge.Curve.StartPoint); + nums.Add(1); + edge.StartIndex = knots.Count - 1; } - closedCurvesOut.AddRange(closedEdges.Select(e => e.Curve.ToCurve())!); - - //这里把交点只有一条曲线通过的点过滤掉了,也就是尾巴的图元, - //剩下的都是闭合的曲线连接了,每个点都至少有两条曲线通过 - var tmp = edges - .Except(closedEdges) - .Where(e => nums[e.StartIndex] > 1 && nums[e.EndIndex] > 1); - edges.Clear(); - var ge = tmp.GetEnumerator(); - while (ge.MoveNext()) + if (knots.Contains(edge.Curve.EndPoint)) { - edges.Add(ge.Current); + edge.EndIndex = knots.IndexOf(edge.Curve.EndPoint); + nums[edge.EndIndex]++; } + else + { + knots.Add(edge.Curve.EndPoint); + nums.Add(1); + edge.EndIndex = knots.Count - 1; + } + } + + closedCurvesOut.AddRange(closedEdges.Select(e => e.Curve.ToCurve())!); - //这一大坨不用看了 注释掉也没影响貌似,而且后续也没有用 nums - // foreach (var edge in edges.Except(closedEdges)) - // { - // if (nums[edge.StartIndex] == 1 || nums[edge.EndIndex] == 1) - // { - // if (nums[edge.StartIndex] == 1 && nums[edge.EndIndex] == 1) - // { - // nums[edge.StartIndex] = 0; - // nums[edge.EndIndex] = 0; - // } - // else - // { - // int next = -1; - // if (nums[edge.StartIndex] == 1) - // { - // nums[edge.StartIndex] = 0; - // nums[next = edge.EndIndex]--; - // } - // else - // { - // nums[edge.EndIndex] = 0; - // nums[next = edge.StartIndex]--; - // } - // } - // } - // } + //这里把交点只有一条曲线通过的点过滤掉了,也就是尾巴的图元, + //剩下的都是闭合的曲线连接了,每个点都至少有两条曲线通过 + var tmp = edges + .Except(closedEdges) + .Where(e => nums[e.StartIndex] > 1 && nums[e.EndIndex] > 1); + edges.Clear(); + var ge = tmp.GetEnumerator(); + while (ge.MoveNext()) + { + edges.Add(ge.Current); } - /// - /// 获取多个面域 - /// - /// - /// - public List> GetRegions(List edges) + //这一大坨不用看了 注释掉也没影响貌似,而且后续也没有用 nums + // foreach (var edge in edges.Except(closedEdges)) + // { + // if (nums[edge.StartIndex] == 1 || nums[edge.EndIndex] == 1) + // { + // if (nums[edge.StartIndex] == 1 && nums[edge.EndIndex] == 1) + // { + // nums[edge.StartIndex] = 0; + // nums[edge.EndIndex] = 0; + // } + // else + // { + // int next = -1; + // if (nums[edge.StartIndex] == 1) + // { + // nums[edge.StartIndex] = 0; + // nums[next = edge.EndIndex]--; + // } + // else + // { + // nums[edge.EndIndex] = 0; + // nums[next = edge.StartIndex]--; + // } + // } + // } + // } + } + + /// + /// 获取多个面域 + /// + /// + /// + public List> GetRegions(List edges) + { + //利用边界的顺序和逆序获取闭合链条 + var regions = new List>(); + for (int i = 0; i < edges.Count; i++) { - //利用边界的顺序和逆序获取闭合链条 - var regions = new List>(); - for (int i = 0; i < edges.Count; i++) - { - var edge = edges[i]; - //TODO 这里有bug,两个内接的矩形会卡死 - var edgeItem = new EdgeItem(edge, true); - edgeItem.FindRegion(edges, regions); // 经测试是这里卡住了 testTopo - edgeItem = new EdgeItem(edge, false); - edgeItem.FindRegion(edges, regions); - } - return regions; + var edge = edges[i]; + //TODO 这里有bug,两个内接的矩形会卡死 + var edgeItem = new EdgeItem(edge, true); + edgeItem.FindRegion(edges, regions); // 经测试是这里卡住了 testTopo + edgeItem = new EdgeItem(edge, false); + edgeItem.FindRegion(edges, regions); } + return regions; + } - /// - /// 这是做什么的 - /// - /// - public void RegionsInfo(List> regions) + /// + /// 广度优先算法? + /// + /// + public void RegionsInfo(List> regions) + { + for (int i = 0; i < regions.Count; i++) { - for (int i = 0; i < regions.Count; i++) + for (int j = i + 1; j < regions.Count;) { - for (int j = i + 1; j < regions.Count;) + bool eq = false; + if (regions[i].Count == regions[j].Count) { - bool eq = false; - if (regions[i].Count == regions[j].Count) + var node = regions[i].First; + var curve = node!.Value.Edge.Curve; + var node2 = regions[j].GetNode(e => e.Edge.Curve == curve); + //var node2 = regions[j].Find(node.Value); + if (node2 is not null) { - var node = regions[i].First; - var curve = node!.Value.Edge.Curve; - var node2 = regions[j].GetNode(e => e.Edge.Curve == curve); - //var node2 = regions[j].Find(node.Value); - if (node2 is not null) + eq = true; + var b = node.Value.Forward; + var b2 = node2.Value.Forward; + for (int k = 1; k < regions[i].Count; k++) { - eq = true; - var b = node.Value.Forward; - var b2 = node2.Value.Forward; - for (int k = 1; k < regions[i].Count; k++) + node = node.GetNext(b); + node2 = node2.GetNext(b2); + if (node!.Value.Edge.Curve != node2!.Value.Edge.Curve) { - node = node.GetNext(b); - node2 = node2.GetNext(b2); - if (node!.Value.Edge.Curve != node2!.Value.Edge.Curve) - { - eq = false; - break; - } + eq = false; + break; } } } - if (eq) - regions.RemoveAt(j); - else - j++; } + if (eq) + regions.RemoveAt(j); + else + j++; } } - - - } + } diff --git a/src/IFoxCAD.Cad/IFoxCAD.Cad.csproj b/src/IFoxCAD.Cad/IFoxCAD.Cad.csproj index 55ee81f..c42b6b6 100644 --- a/src/IFoxCAD.Cad/IFoxCAD.Cad.csproj +++ b/src/IFoxCAD.Cad/IFoxCAD.Cad.csproj @@ -73,4 +73,19 @@ + + + + True + True + Settings.settings + + + + + + SettingsSingleFileGenerator + Settings.Designer.cs + + diff --git a/tests/Test/TestCurve.cs b/tests/Test/TestCurve.cs index d6095fa..aa365ce 100644 --- a/tests/Test/TestCurve.cs +++ b/tests/Test/TestCurve.cs @@ -13,18 +13,6 @@ public void TestBreakCurve() tt.ForEach(t => t.ForWrite(e => e.ColorIndex = 1)); tr.CurrentSpace.AddEntity(tt); } - [CommandMethod("testtopo")] - public void TestToPo() - { - var ents = Env.Editor.SSGet().Value?.GetEntities(); - if (ents == null) - return; - var tt = CurveEx.Topo(ents.ToList()); - using var tr = new DBTrans(); - tt.ForEach(t => t.ForWrite(e => e.ColorIndex = 1)); - tr.CurrentSpace.AddEntity(tt); - - } [CommandMethod("testCurveCurveIntersector3d")] public void TestCurveCurveIntersector3d() { @@ -77,6 +65,19 @@ public void TestCurveCurveIntersector3d() //tr.CurrentSpace.AddEntity(tt); } + + [CommandMethod("testtopo")] + public void TestToPo() + { + var ents = Env.Editor.SSGet().Value?.GetEntities(); + if (ents == null) + return; + var tt = CurveEx.Topo(ents.ToList()); + using var tr = new DBTrans(); + tt.ForEach(t => t.ForWrite(e => e.ColorIndex = 1)); + tr.CurrentSpace.AddEntity(tt); + } + [CommandMethod("testGetEdgesAndnewCurves")] public void TestGetEdgesAndnewCurves() { -- Gitee From 5f757e47abf0f80d28f2d23491d746a9a7cb4f4f Mon Sep 17 00:00:00 2001 From: liuqihong <540762622@qq.com> Date: Tue, 5 Apr 2022 22:21:12 +0800 Subject: [PATCH 072/675] =?UTF-8?q?=E4=BC=98=E5=8C=96=E5=87=A0=E5=A4=84?= =?UTF-8?q?=E9=80=9F=E5=BA=A6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/IFoxCAD.Cad/ExtensionMethod/Curve3dEx.cs | 40 ++++++++++++++------ src/IFoxCAD.Cad/ExtensionMethod/Topo.cs | 14 +++---- 2 files changed, 34 insertions(+), 20 deletions(-) diff --git a/src/IFoxCAD.Cad/ExtensionMethod/Curve3dEx.cs b/src/IFoxCAD.Cad/ExtensionMethod/Curve3dEx.cs index e436d20..f0e14ba 100644 --- a/src/IFoxCAD.Cad/ExtensionMethod/Curve3dEx.cs +++ b/src/IFoxCAD.Cad/ExtensionMethod/Curve3dEx.cs @@ -149,8 +149,7 @@ public static bool IsCircular(this Curve3d curve) /// 三维复合曲线列表 public static List GetSplitCurves(this CompositeCurve3d c3d, List pars) { - Interval inter = c3d.GetInterval(); - Curve3d[] c3ds = c3d.GetCurves(); + //曲线参数剔除重复的 pars.Sort(); for (int i = pars.Count - 1; i > 0; i--) { @@ -160,6 +159,11 @@ public static List GetSplitCurves(this CompositeCurve3d c3d, L if (pars.Count == 0) return new List(); + + //这个是曲线参数类 + var inter = c3d.GetInterval(); + //曲线们 + var c3ds = c3d.GetCurves(); if (c3ds.Length == 1 && c3ds[0].IsClosed()) { //闭合曲线不允许打断于一点 @@ -207,8 +211,8 @@ public static List GetSplitCurves(this CompositeCurve3d c3d, L { List cc3ds = new(); //复合曲线参数转换到包含曲线参数 - CompositeParameter cp1 = c3d.GlobalToLocalParameter(pars[i]); - CompositeParameter cp2 = c3d.GlobalToLocalParameter(pars[i + 1]); + var cp1 = c3d.GlobalToLocalParameter(pars[i]); + var cp2 = c3d.GlobalToLocalParameter(pars[i + 1]); if (cp1.SegmentIndex == cp2.SegmentIndex) { cc3ds.Add( @@ -238,15 +242,28 @@ public static List GetSplitCurves(this CompositeCurve3d c3d, L if (c3d.IsClosed() && c3ds.Length > 1) { - var cs = curves[curves.Count - 1].GetCurves().ToList(); - cs.AddRange(curves[0].GetCurves()); - curves[curves.Count - 1] = new CompositeCurve3d(cs.ToArray()); + var cus1 = curves[curves.Count - 1].GetCurves(); + var cus2 = curves[0].GetCurves(); + var cs = Combine2(cus1, cus2); + curves[curves.Count - 1] = new CompositeCurve3d(cs); curves.RemoveAt(0); } - return curves; } + /// + /// 合并数组 + /// + /// + /// + static T[] Combine2(T[] a, T[] b) + { + var c = new T[a.Length + b.Length]; + Array.Copy(a, 0, c, 0, a.Length); + Array.Copy(b, 0, c, a.Length, b.Length); + return c; + } + /// /// 将复合曲线转换为实体类曲线 /// @@ -267,9 +284,10 @@ public static List GetSplitCurves(this CompositeCurve3d c3d, L { bool hasNurb = false; - foreach (var c in cs) + for (int i = 0; i < cs.Length; i++) { - if (c is NurbCurve3d || c is EllipticalArc3d) + var c = cs[i].GetType().Name; + if (c == nameof(NurbCurve3d) || c == nameof(EllipticalArc3d)) { hasNurb = true; break; @@ -277,7 +295,7 @@ public static List GetSplitCurves(this CompositeCurve3d c3d, L } if (hasNurb) { - NurbCurve3d? nc3d = cs[0].ToNurbCurve3d(); + var nc3d = cs[0].ToNurbCurve3d(); for (int i = 1; i < cs.Length; i++) nc3d?.JoinWith(cs[i].ToNurbCurve3d()); return nc3d?.ToCurve(); diff --git a/src/IFoxCAD.Cad/ExtensionMethod/Topo.cs b/src/IFoxCAD.Cad/ExtensionMethod/Topo.cs index e27e1a1..ae8c0a8 100644 --- a/src/IFoxCAD.Cad/ExtensionMethod/Topo.cs +++ b/src/IFoxCAD.Cad/ExtensionMethod/Topo.cs @@ -344,17 +344,13 @@ public void GetEdgesAndnewCurves(List edges, List closedCurvesOut) { edges.AddRange(c3ds.Select(c => new Edge(c))); } - //惊惊留:(不敢删啊...) - //狐哥写的这里出现的条件是:有曲线参数,但是切分不出来曲线...没懂为什么... - //猜测:曲线参数在头或者尾,那么交点就是直接碰头碰尾, - //也就是 aa对比 同一条曲线自己和自己产生的? - //参数大于0{是这些参数? 头参/尾参/参数不在曲线上?} - //else if (gc1.IsClosed()) - //{ - // closedCurvesOut.Add(gc1.ToCurve()!); - //} else { + //惊惊留:(不敢删啊...) + //狐哥写的这里出现的条件是:有曲线参数,但是切分不出来曲线...没懂为什么... + //猜测:曲线参数在头或者尾,那么交点就是直接碰头碰尾, + //也就是 aa对比 同一条曲线自己和自己产生的? + //参数大于0{是这些参数? 头参/尾参/参数不在曲线上?} edges.Add(new Edge(gc1)); } } -- Gitee From 511c8ee5694563c4a65138927518f1e057ae482d Mon Sep 17 00:00:00 2001 From: liuqihong <540762622@qq.com> Date: Wed, 6 Apr 2022 00:24:25 +0800 Subject: [PATCH 073/675] =?UTF-8?q?is=E6=AF=94=E8=BE=83=E5=BF=AB?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/IFoxCAD.Cad/ExtensionMethod/Curve3dEx.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/IFoxCAD.Cad/ExtensionMethod/Curve3dEx.cs b/src/IFoxCAD.Cad/ExtensionMethod/Curve3dEx.cs index f0e14ba..20b0037 100644 --- a/src/IFoxCAD.Cad/ExtensionMethod/Curve3dEx.cs +++ b/src/IFoxCAD.Cad/ExtensionMethod/Curve3dEx.cs @@ -286,8 +286,8 @@ static T[] Combine2(T[] a, T[] b) for (int i = 0; i < cs.Length; i++) { - var c = cs[i].GetType().Name; - if (c == nameof(NurbCurve3d) || c == nameof(EllipticalArc3d)) + var c = cs[i]; + if (c is NurbCurve3d || c is EllipticalArc3d) { hasNurb = true; break; -- Gitee From 669b50a70ca31cf2d2f8554bf7ab621531682d58 Mon Sep 17 00:00:00 2001 From: vicwjb Date: Tue, 5 Apr 2022 16:10:41 +0800 Subject: [PATCH 074/675] =?UTF-8?q?=E6=B8=85=E7=90=86=E9=A1=B9=E7=9B=AE?= =?UTF-8?q?=E6=96=87=E4=BB=B6=E6=97=A0=E7=94=A8=E7=9A=84=E5=86=85=E5=AE=B9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/IFoxCAD.Cad/IFoxCAD.Cad.csproj | 15 --------------- 1 file changed, 15 deletions(-) diff --git a/src/IFoxCAD.Cad/IFoxCAD.Cad.csproj b/src/IFoxCAD.Cad/IFoxCAD.Cad.csproj index c42b6b6..55ee81f 100644 --- a/src/IFoxCAD.Cad/IFoxCAD.Cad.csproj +++ b/src/IFoxCAD.Cad/IFoxCAD.Cad.csproj @@ -73,19 +73,4 @@ - - - - True - True - Settings.settings - - - - - - SettingsSingleFileGenerator - Settings.Designer.cs - - -- Gitee From f6e15561e4a51ceb37e893d18cc3bb5db23ba3cf Mon Sep 17 00:00:00 2001 From: vicwjb Date: Tue, 5 Apr 2022 22:36:43 +0800 Subject: [PATCH 075/675] =?UTF-8?q?=E5=A2=9E=E5=8A=A0ent=E7=B1=BB=E6=B5=8B?= =?UTF-8?q?=E8=AF=95=E5=87=BD=E6=95=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- tests/Test/TestCurve.cs | 2 +- tests/Test/TestEnt.cs | 18 ++++++++++++++++++ 2 files changed, 19 insertions(+), 1 deletion(-) create mode 100644 tests/Test/TestEnt.cs diff --git a/tests/Test/TestCurve.cs b/tests/Test/TestCurve.cs index aa365ce..5a48606 100644 --- a/tests/Test/TestCurve.cs +++ b/tests/Test/TestCurve.cs @@ -74,7 +74,7 @@ public void TestToPo() return; var tt = CurveEx.Topo(ents.ToList()); using var tr = new DBTrans(); - tt.ForEach(t => t.ForWrite(e => e.ColorIndex = 1)); + tt.ForEach((i,t) => t.ForWrite(e => e.ColorIndex = i)); tr.CurrentSpace.AddEntity(tt); } diff --git a/tests/Test/TestEnt.cs b/tests/Test/TestEnt.cs new file mode 100644 index 0000000..3c8b4c3 --- /dev/null +++ b/tests/Test/TestEnt.cs @@ -0,0 +1,18 @@ +namespace Test; + +public class TestEnt +{ + [CommandMethod("TestEntRoration")] + public void TestEntRoration() + { + var line = new Line(new(0,0,0),new(100,0,0)); + + using var tr = new DBTrans(); + tr.CurrentSpace.AddEntity(line); + var line2 = line.Clone() as Line; + tr.CurrentSpace.AddEntity(line2); + line2.Rotation(new(100, 0, 0), Math.PI / 2); + + + } +} -- Gitee From d87ba6f8d6e2824f96e0ce1db42d6432ba9441d3 Mon Sep 17 00:00:00 2001 From: vicwjb Date: Tue, 5 Apr 2022 22:42:23 +0800 Subject: [PATCH 076/675] =?UTF-8?q?=E6=9B=B4=E6=96=B0=E7=BA=BF=E6=80=A7?= =?UTF-8?q?=E5=8F=98=E6=8D=A2=E7=9A=84=E6=B3=A8=E9=87=8A?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/IFoxCAD.Cad/ExtensionMethod/EntityEx.cs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/IFoxCAD.Cad/ExtensionMethod/EntityEx.cs b/src/IFoxCAD.Cad/ExtensionMethod/EntityEx.cs index 3c3a226..60e421a 100644 --- a/src/IFoxCAD.Cad/ExtensionMethod/EntityEx.cs +++ b/src/IFoxCAD.Cad/ExtensionMethod/EntityEx.cs @@ -76,7 +76,7 @@ public static IEnumerable GetPoints(this Polyline pl) } #endregion - #region TransformBy + #region 实体线性变换 /// /// 移动实体 @@ -105,7 +105,7 @@ public static void Scale(this Entity ent, Point3d center, double scaleValue) /// /// 实体 /// 旋转中心 - /// 转角 + /// 转角,弧度制,正数为顺时针 /// 旋转平面的法向矢量 public static void Rotation(this Entity ent, Point3d center, double angle, Vector3d normal) { @@ -117,7 +117,7 @@ public static void Rotation(this Entity ent, Point3d center, double angle, Vecto /// /// 实体 /// 旋转中心 - /// 转角 + /// 转角,弧度制,正数为顺时针 public static void Rotation(this Entity ent, Point3d center, double angle) { ent.TransformBy(Matrix3d.Rotation(angle, Vector3d.ZAxis.TransformBy(ent.Ecs), center)); -- Gitee From 005c9557dd0578dd5000c7fff23003ec5e0a8ca9 Mon Sep 17 00:00:00 2001 From: vicwjb Date: Tue, 5 Apr 2022 23:56:05 +0800 Subject: [PATCH 077/675] =?UTF-8?q?=E6=B5=8B=E8=AF=95=E7=B1=BB=E5=9E=8B?= =?UTF-8?q?=E6=B5=8B=E8=AF=95=E9=80=9F=E5=BA=A6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- tests/Test/TestEnt.cs | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/tests/Test/TestEnt.cs b/tests/Test/TestEnt.cs index 3c8b4c3..e3cccf7 100644 --- a/tests/Test/TestEnt.cs +++ b/tests/Test/TestEnt.cs @@ -15,4 +15,26 @@ public void TestEntRoration() } + + + [CommandMethod("Testtypespeed")] + public void TestTypeSpeed() + { + var line = new Line(); + var line1 = line as Entity; + Tools.TestTimes(100000, "is 匹配:", () => + { + var t = line1 is Line; + }); + Tools.TestTimes(100000, "name 匹配:", () => + { + //var t = line.GetType().Name; + var tt = line1.GetType().Name == nameof(Line); + }); + Tools.TestTimes(100000, "dxfname 匹配:", () => + { + //var t = line.GetType().Name; + var tt = line1.GetRXClass().DxfName == nameof(Line); + }); + } } -- Gitee From d526d980d3c14b6ebc38ce93dadde91675e5deb6 Mon Sep 17 00:00:00 2001 From: liuqihong <540762622@qq.com> Date: Wed, 6 Apr 2022 00:36:25 +0800 Subject: [PATCH 078/675] =?UTF-8?q?=E5=A4=87=E4=BB=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/IFoxCAD.Cad/ExtensionMethod/Topo.cs | 72 ++++++++++++++++--------- 1 file changed, 47 insertions(+), 25 deletions(-) diff --git a/src/IFoxCAD.Cad/ExtensionMethod/Topo.cs b/src/IFoxCAD.Cad/ExtensionMethod/Topo.cs index ae8c0a8..4c7f43e 100644 --- a/src/IFoxCAD.Cad/ExtensionMethod/Topo.cs +++ b/src/IFoxCAD.Cad/ExtensionMethod/Topo.cs @@ -271,6 +271,25 @@ public override int GetHashCode() } #endregion } + + +public class CurveInfo: Rect +{ + public Curve Curve; + public CompositeCurve3d? CompositeCurve3d + { + get { + return + } + } + + public List? Paramss; + public CurveInfo(Curve curve) + { + Curve = curve; + Paramss = new List(); + } +} public class Topo { @@ -291,12 +310,14 @@ public Topo(List curves) /// 自闭的曲线 public void GetEdgesAndnewCurves(List edges, List closedCurvesOut) { + //请不要把它做成结构,因为这是一种ArrayOfStruct写法 var geCurves = new List(); var paramss = new List>(); - + //将曲线按节点转为Ge曲线集(提取多段线每段/样条曲线子段..) for (int i = 0; i < _curves.Count; i++) { + _curves[i].GeometricExtents. var cc3d = _curves[i].ToCompositeCurve3d(); if (cc3d is not null) { @@ -307,13 +328,13 @@ public void GetEdgesAndnewCurves(List edges, List closedCurvesOut) var cci3d = new CurveCurveIntersector3d(); - //遍历所有曲线,然后获取交点...此处是O(n²) + //此处是O(n²) + //曲线a和曲线a/b/c/d/e...求交点,组成交点数组(第一次是自交对比) for (int i = 0; i < geCurves.Count; i++) { var gc1 = geCurves[i]; var pars1 = paramss[i]; - - //曲线a和曲线a/b/c/d/e...分别交点,组成交点数组(第一次是自交对比) + for (int j = i; j < geCurves.Count; j++) { var gc2 = geCurves[j]; @@ -335,6 +356,7 @@ public void GetEdgesAndnewCurves(List edges, List closedCurvesOut) { closedCurvesOut.Add(gc1.ToCurve()!); } + //有交点参数 if (pars1.Count > 0) { @@ -358,27 +380,27 @@ public void GetEdgesAndnewCurves(List edges, List closedCurvesOut) edges = edges.Distinct(new EdgeComparer()).ToList(); } - private class EdgeComparer : IEqualityComparer - { - public bool Equals(Edge x, Edge y) - { -#if ac2009 - var pts = x.Curve.GetSamplePoints(100); - return pts.All(pt => y.Curve.IsOn(pt)); -#elif ac2013 || ac2015 - var pts = x.Curve.GetSamplePoints(100); - return pts.All(pt => y.Curve.IsOn(pt.Point)); -#endif - //return x.Curve.IsEqualTo(y.Curve); - } - - // If Equals() returns true for a pair of objects - // then GetHashCode() must return the same value for these objects. - public int GetHashCode(Edge product) - { - return product.Curve.GetHashCode(); - } - } +// private class EdgeComparer : IEqualityComparer +// { +// public bool Equals(Edge x, Edge y) +// { +//#if ac2009 +// var pts = x.Curve.GetSamplePoints(100); +// return pts.All(pt => y.Curve.IsOn(pt)); +//#elif ac2013 || ac2015 +// var pts = x.Curve.GetSamplePoints(100); +// return pts.All(pt => y.Curve.IsOn(pt.Point)); +//#endif +// //return x.Curve.IsEqualTo(y.Curve); +// } + +// // If Equals() returns true for a pair of objects +// // then GetHashCode() must return the same value for these objects. +// public int GetHashCode(Edge product) +// { +// return product.Curve.GetHashCode(); +// } +// } /// /// 创建邻接表 -- Gitee From b06931563345e0a9550d0681f0c225422ca8c949 Mon Sep 17 00:00:00 2001 From: vicwjb <403009819@qq.com> Date: Wed, 6 Apr 2022 01:09:22 +0800 Subject: [PATCH 079/675] =?UTF-8?q?=E5=B0=86Combine2=E5=87=BD=E6=95=B0?= =?UTF-8?q?=E7=A7=BB=E8=87=B3Basal=E9=A1=B9=E7=9B=AE?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/IFoxCAD.Basal/ArrayEx.cs | 19 +++++++++++++++++++ src/IFoxCAD.Cad/ExtensionMethod/Curve3dEx.cs | 15 +-------------- src/IFoxCAD.Cad/ExtensionMethod/CurveEx.cs | 3 --- src/IFoxCAD.Cad/GlobalUsings.cs | 3 +++ 4 files changed, 23 insertions(+), 17 deletions(-) create mode 100644 src/IFoxCAD.Basal/ArrayEx.cs diff --git a/src/IFoxCAD.Basal/ArrayEx.cs b/src/IFoxCAD.Basal/ArrayEx.cs new file mode 100644 index 0000000..64e98ae --- /dev/null +++ b/src/IFoxCAD.Basal/ArrayEx.cs @@ -0,0 +1,19 @@ +namespace IFoxCAD.Basal +{ + public static class ArrayEx + { + /// + /// 合并数组 + /// + /// + /// + public static T[] Combine2(this T[] a, T[] b) + { + var c = new T[a.Length + b.Length]; + Array.Copy(a, 0, c, 0, a.Length); + Array.Copy(b, 0, c, a.Length, b.Length); + return c; + } + + } +} diff --git a/src/IFoxCAD.Cad/ExtensionMethod/Curve3dEx.cs b/src/IFoxCAD.Cad/ExtensionMethod/Curve3dEx.cs index 20b0037..226e5de 100644 --- a/src/IFoxCAD.Cad/ExtensionMethod/Curve3dEx.cs +++ b/src/IFoxCAD.Cad/ExtensionMethod/Curve3dEx.cs @@ -244,26 +244,13 @@ public static List GetSplitCurves(this CompositeCurve3d c3d, L { var cus1 = curves[curves.Count - 1].GetCurves(); var cus2 = curves[0].GetCurves(); - var cs = Combine2(cus1, cus2); + var cs = cus1.Combine2(cus2); curves[curves.Count - 1] = new CompositeCurve3d(cs); curves.RemoveAt(0); } return curves; } - /// - /// 合并数组 - /// - /// - /// - static T[] Combine2(T[] a, T[] b) - { - var c = new T[a.Length + b.Length]; - Array.Copy(a, 0, c, 0, a.Length); - Array.Copy(b, 0, c, a.Length, b.Length); - return c; - } - /// /// 将复合曲线转换为实体类曲线 /// diff --git a/src/IFoxCAD.Cad/ExtensionMethod/CurveEx.cs b/src/IFoxCAD.Cad/ExtensionMethod/CurveEx.cs index 6b6660c..9f09464 100644 --- a/src/IFoxCAD.Cad/ExtensionMethod/CurveEx.cs +++ b/src/IFoxCAD.Cad/ExtensionMethod/CurveEx.cs @@ -1,8 +1,5 @@ namespace IFoxCAD.Cad; -using IFoxCAD.Basal; -using System.Diagnostics; - /// /// 实体类曲线扩展类 /// diff --git a/src/IFoxCAD.Cad/GlobalUsings.cs b/src/IFoxCAD.Cad/GlobalUsings.cs index e51dd91..a29b199 100644 --- a/src/IFoxCAD.Cad/GlobalUsings.cs +++ b/src/IFoxCAD.Cad/GlobalUsings.cs @@ -18,3 +18,6 @@ global using Autodesk.AutoCAD.DatabaseServices; global using Autodesk.AutoCAD.Geometry; global using Autodesk.AutoCAD.Runtime; + +/// ifoxcad.basal 引用 +global using IFoxCAD.Basal; \ No newline at end of file -- Gitee From 9b2b8542281e6c3f31ebc25da40a091214fb9daa Mon Sep 17 00:00:00 2001 From: liuqihong <540762622@qq.com> Date: Wed, 6 Apr 2022 01:18:28 +0800 Subject: [PATCH 080/675] =?UTF-8?q?=E6=B7=BB=E5=8A=A0rect=E7=B1=BB?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/IFoxCAD.Cad/QuadTree/Rect.cs | 501 +++++++++++++++++++++++++++++++ 1 file changed, 501 insertions(+) create mode 100644 src/IFoxCAD.Cad/QuadTree/Rect.cs diff --git a/src/IFoxCAD.Cad/QuadTree/Rect.cs b/src/IFoxCAD.Cad/QuadTree/Rect.cs new file mode 100644 index 0000000..60fd4b8 --- /dev/null +++ b/src/IFoxCAD.Cad/QuadTree/Rect.cs @@ -0,0 +1,501 @@ +namespace IFoxCAD.Cad; + +using IFoxCAD.Basal; +using System.Runtime.InteropServices; + + +/// +/// Linq Distinct 消重比较两点在容差范围内就去除 +/// +public class TolerancePoint2d : IEqualityComparer +{ + double _tolerance; + public TolerancePoint2d(double tolerance = 1e-6) + { + _tolerance = tolerance; + } + + public bool Equals(Point2d a, Point2d b)//Point3d是struct不会为null + { + if (b == null) + return a == null; + else if (a == null) + return false; +#if true + // 方形限定 + // 在 0~1e-6 范围实现 圆形限定 则计算部分在浮点数6位后,没有啥意义 + // 在 0~1e-6 范围实现 从时间和CPU消耗来说,圆形限定 都没有 方形限定 的好 + return a.IsEqualTo(b, new Tolerance(_tolerance, _tolerance)); +#else + // 圆形限定 + // DistanceTo 分别对XYZ进行了一次乘法,也是总数3次乘法,然后求了一次平方根 + // (X86.CPU.FSQRT指令用的牛顿迭代法/软件层面可以使用快速平方根....我还以为CPU会采取快速平方根这样的取表操作) + return a.DistanceTo(b) <= _tolerance; +#endif + } + + public int GetHashCode(Point2d obj) + { + //结构体直接返回 obj.GetHashCode(); Point3d ToleranceDistinct3d + //因为结构体是用可值叠加来判断?或者因为结构体兼备了一些享元模式的状态? + //而类是构造的指针,所以取哈希值要改成x+y+z..s给Equals判断用,+是会溢出,所以用^ + return (int)obj.X ^ (int)obj.Y ^ (int)obj.Z; + } +} + + +[Serializable] +[StructLayout(LayoutKind.Sequential)] +public class Rect : IEquatable +{ + public static TolerancePoint2d _RectTolerance = new(1e-6); + public static Tolerance _cadTolerance = new(1e-6, 1e-6); + + #region 字段 + //这里的成员不要用{get}封装成属性,否则会导致跳转了一次函数, + //10w图元将会从187毫秒变成400毫秒 + //不用 protected 否则子类传入Rect对象进来无法用 + internal double _X; + internal double _Y; + internal double _Right; + internal double _Top; + #endregion + + #region 成员 + public double X => _X; + public double Y => _Y; + public double Left => _X; + public double Bottom => _Y; + public double Right => _Right; + public double Top => _Top; + + public double Width => _Right - _X; + public double Height => _Top - _Y; + public double Area + { + get + { + var ar = (_Right - _X) * (_Top - _Y); + return ar < 1e-10 ? 0 : ar; + } + } + + public Point2d MinPoint => LeftLower; + public Point2d MaxPoint => RightUpper; + public Point2d CenterPoint => Midst; + + /// + /// 左下Min + /// + public Point2d LeftLower => new(_X, _Y); + + /// + /// 左中 + /// + public Point2d LeftMidst => new(_X, Midst.Y); + + /// + /// 左上 + /// + public Point2d LeftUpper => new(_X, _Top); + + /// + /// 右上Max + /// + public Point2d RightUpper => new(_Right, _Top); + + /// + /// 右中 + /// + public Point2d RightMidst => new(_Right, Midst.Y); + + /// + /// 右下 + /// + public Point2d RightBottom => new(_Right, _Y); + + /// + /// 中间 + /// + public Point2d Midst => new(((_Right - _X) * 0.5) + _X, ((_Top - _Y) * 0.5) + _Y); + + /// + /// 中上 + /// + public Point2d MidstUpper => new(Midst.X, _Top); + + /// + /// 中下 + /// + public Point2d MidstBottom => new(Midst.X, _Y); + #endregion + + #region 构造 + public Rect() { } + + public Rect(double left, double bottom, double right, double top) + { + _X = left; + _Y = bottom; + _Right = right; + _Top = top; + } + + /// + /// 构造矩形类 + /// + /// + /// + /// 是否检查大小 + public Rect(Point2d p1, Point2d p3, bool check = false) + { + if (check) + { + _X = Math.Min(p1.X, p3.X); + _Y = Math.Min(p1.Y, p3.Y); + _Right = Math.Max(p1.X, p3.X); + _Top = Math.Max(p1.Y, p3.Y); + } + else + { + _X = p1.X; + _Y = p1.Y; + _Right = p3.X; + _Top = p3.Y; + } + } + #endregion + + #region 重载运算符_比较 + public override bool Equals(object? obj) + { + return this == obj as Rect; + } + public bool Equals(Rect? b) + { + return this == b; + } + public static bool operator !=(Rect? a, Rect? b) + { + return !(a == b); + } + public static bool operator ==(Rect? a, Rect? b) + { + //此处地方不允许使用==null,因为此处是定义 + if (b is null) + return a is null; + else if (a is null) + return false; + if (ReferenceEquals(a, b))//同一对象 + return true; + + return a.Equals(b, 0); + } + + /// + /// 比较核心 + /// + public bool Equals(Rect? b, double tolerance = 1e-6) + { + if (b is null) + return false; + if (ReferenceEquals(this, b)) //同一对象 + return true; + + return Math.Abs(_X - b._X) < tolerance && + Math.Abs(_Right - b._Right) < tolerance && + Math.Abs(_Top - b._Top) < tolerance && + Math.Abs(_Y - b._Y) < tolerance; + } + + public override int GetHashCode() + { + return base.GetHashCode(); + } + #endregion + + #region 转换类型 +#if !WinForm + // 隐式转换(相当于是重载赋值运算符) + public static implicit operator Rect(System.Windows.Rect rect) + { + return new Rect(rect.Left, rect.Bottom, rect.Right, rect.Top); + } + public static implicit operator Rect(System.Drawing.RectangleF rect) + { + return new Rect(rect.Left, rect.Bottom, rect.Right, rect.Top); + } + public static implicit operator Rect(System.Drawing.Rectangle rect) + { + return new Rect(rect.Left, rect.Bottom, rect.Right, rect.Top); + } +#endif + #endregion + + #region 包含 + public bool Contains(Point2d Point2d) + { + return ContainsInternal(Point2d.X, Point2d.Y); + } + public bool Contains(double x, double y) + { + return ContainsInternal(x, y); + } + private bool ContainsInternal(double x, double y) + { + return _X <= x && x <= _Right && + _Y <= y && y <= _Top; + } + + /// + /// 包含是四个点都包含 + /// + /// + /// + public bool Contains(Rect rect) + { + return _X <= rect._X && rect._Right <= _Right && + _Y <= rect._Y && rect._Top <= _Top; + } + + /// + /// 碰撞是其中一个点在就是在 + /// + /// + /// + public bool IntersectsWith(Rect rect) + { + return rect._X <= _Right && _X <= rect._Right && + rect._Top >= _Y && rect._Y <= _Top; + } + #endregion + + #region 方法 + /// + /// 获取共点 + /// + /// + public Point2d[] GetCommonPoint(Rect other) + { + return ToPoints().Intersect(other.ToPoints(), _RectTolerance).ToArray(); + } + + public Point2d[] ToPoints() + { + Point2d a = MinPoint;//min + Point2d b = new(_Right, _Y); + Point2d c = MaxPoint;//max + Point2d d = new(_X, _Top); + return new Point2d[] { a, b, c, d }; + } + + public (Point2d boxMin, Point2d boxRigthDown, Point2d boxMax, Point2d boxLeftUp) ToPoints4() + { + Point2d a = MinPoint;//min + Point2d b = new(_Right, _Y); + Point2d c = MaxPoint;//max + Point2d d = new(_X, _Top); + return (a, b, c, d); + } + + /// + /// 四周膨胀 + /// + /// + public Rect Expand(double d) + { + return new Rect(_X - d, _Y - d, _Right + d, _Top + d); + } + + /// + /// 是否矩形(带角度) + /// + /// + /// + public static bool IsRectAngle(List? ptList, double tolerance = 1e-8) + { + if (ptList == null) + throw new ArgumentNullException(nameof(ptList)); + + var pts = ptList.ToList(); + /* 消重,不这里设置,否则这不是一个正确的单元测试 + * //var ptList = pts.Distinct().ToList(); + * var ptList = pts.DistinctExBy((a, b) => a.DistanceTo(b) < 1e-6).ToList(); + */ + if (ptList.Count == 5) + { + //首尾点相同移除最后 + if (pts[0].IsEqualTo(pts[pts.Count - 1], _cadTolerance)) + pts.RemoveAt(pts.Count - 1); + } + if (pts.Count != 4) + return false; + + //最快的方案 + //点乘求值法:(为了处理 正梯形/平行四边形 需要三次) + //这里的容差要在1e-8内,因为点乘的三次浮点数乘法会令精度变低 + var dot = DotProductValue( pts[0],pts[1], pts[3]); + if (Math.Abs(dot) < tolerance) + { + dot = DotProductValue( pts[1],pts[2], pts[0]); + if (Math.Abs(dot) < tolerance) + { + dot = DotProductValue(pts[2],pts[3], pts[1]); + return Math.Abs(dot) < tolerance; + } + } + return false; + } + + /// + /// 点积,求值 + /// 1.是两个向量的长度与它们夹角余弦的积 + /// 2.求四个点是否矩形使用 + /// + /// 原点 + /// 点 + /// 点 + /// 0方向相同,夹角0~90度;=0相互垂直;<0方向相反,夹角90~180度]]> + static double DotProductValue(Point2d o, Point2d a, Point2d b) + { + var oa = o.GetVectorTo(a); + var ob = o.GetVectorTo(b); + return (oa.X * ob.X) + (oa.Y * ob.Y); + } + + /// + /// 是否轴向矩形(无角度) + /// + public static bool IsRect(List? ptList, double tolerance = 1e-10) + { + if (ptList == null) + throw new ArgumentNullException(nameof(ptList)); + + var pts = ptList.ToList(); + if (ptList.Count == 5) + { + //首尾点相同移除最后 + if (pts[0].IsEqualTo(pts[pts.Count - 1], _cadTolerance)) + pts.RemoveAt(pts.Count - 1); + } + if (pts.Count != 4) + return false; + + return Math.Abs(pts[0].X - pts[3].X) < tolerance && + Math.Abs(pts[0].Y - pts[1].Y) < tolerance && + Math.Abs(pts[1].X - pts[2].X) < tolerance && + Math.Abs(pts[2].Y - pts[3].Y) < tolerance; + } + + /// + /// 获取点集的包围盒的最小点和最大点(无角度) + /// + /// + public static (Point2d boxMin, Point2d boxMax) GetMinMax(IEnumerable pts) + { + var xMin = double.MaxValue; + var xMax = double.MinValue; + var yMin = double.MaxValue; + var yMax = double.MinValue; + //var zMin = double.MaxValue; + //var zMax = double.MinValue; + + pts.ForEach(p => { + xMin = Math.Min(p.X, xMin); + xMax = Math.Max(p.X, xMax); + yMin = Math.Min(p.Y, yMin); + yMax = Math.Max(p.Y, yMax); + //zMin = Math.Min(p.Z, zMin); + //zMax = Math.Max(p.Z, zMax); + }); + return (new Point2d(xMin, yMin), new Point2d(xMax, yMax)); + } + + /// + /// 矩形点序逆时针排列,将min点[0],max点是[3](带角度) + /// + /// + /// + public static bool RectAnglePointOrder(List? pts) + { + if (pts == null) + throw new ArgumentNullException(nameof(pts)); + + if (!Rect.IsRectAngle(pts)) + return false; + + //获取min和max点(非包围盒) + pts = pts.OrderBy(a => a.X).ThenBy(a => a.Y).ToList(); + var minPt = pts.First(); + var maxPt = pts.Last(); + var link = new LoopList(); + link.AddRange(pts); + + pts.Clear(); + //排序这四个点,左下/右下/右上/左上 + var node = link.Find(minPt); + for (int i = 0; i < 4; i++) + { + pts.Add(node!.Value); + node = node.Next; + } + //保证是逆时针 + var isAcw = CrossAclockwise(pts[0],pts[1], pts[2]); + if (!isAcw) + { + var tmp = pts[1]; + pts[1] = pts[3]; + pts[3] = tmp; + } + return true; + } + + /// + /// 叉积,二维叉乘计算 + /// + /// 传参是向量,表示原点是0,0 + /// 传参是向量,表示原点是0,0 + /// 其模为a与b构成的平行四边形面积 + static double Cross(Vector2d a, Vector2d b) + { + return a.X * b.Y - a.Y * b.X; + } + + /// + /// 叉积,二维叉乘计算 + /// + /// 原点 + /// oa向量 + /// ob向量,此为判断点 + /// 返回值有正负,表示绕原点四象限的位置变换,也就是有向面积 + static double Cross(Point2d o, Point2d a, Point2d b) + { + return Cross(o.GetVectorTo(a), o.GetVectorTo(b)); + } + + /// + /// 叉积,逆时针方向为真 + /// + /// 直线点1 + /// 直线点2 + /// 判断点 + /// b点在oa的逆时针 + static bool CrossAclockwise(Point2d o, Point2d a, Point2d b) + { + return Cross(o, a, b) > -1e-6;//浮点数容差考虑 + } + +#if !WinForm + //public Autodesk.AutoCAD.DatabaseServices.Entity ToPolyLine() + //{ + // var bv = new List(); + // var pts = ToPoints(); + // pts.ForEach(item => { + // bv.Add(new BulgeVertexWidth(item.ToPoint2d(), 0)); + // }); + // return EntityAdd.AddPolyLineToEntity(bv); + //} +#endif + + #endregion +} -- Gitee From 9f9be36fcb044a0544540c63d6c8eca42a3b85e7 Mon Sep 17 00:00:00 2001 From: liuqihong <540762622@qq.com> Date: Wed, 6 Apr 2022 03:34:20 +0800 Subject: [PATCH 081/675] =?UTF-8?q?net35=E6=94=AF=E6=8C=81=E5=85=83?= =?UTF-8?q?=E7=BB=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/IFoxCAD.Cad/IFoxCAD.Cad.csproj | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/IFoxCAD.Cad/IFoxCAD.Cad.csproj b/src/IFoxCAD.Cad/IFoxCAD.Cad.csproj index 55ee81f..b3d0273 100644 --- a/src/IFoxCAD.Cad/IFoxCAD.Cad.csproj +++ b/src/IFoxCAD.Cad/IFoxCAD.Cad.csproj @@ -24,25 +24,25 @@ true x64 - + runtime - + + + runtime - runtime - -- Gitee From e392156ad5929694ce6dfb62213746d3052c8709 Mon Sep 17 00:00:00 2001 From: liuqihong <540762622@qq.com> Date: Wed, 6 Apr 2022 03:34:55 +0800 Subject: [PATCH 082/675] =?UTF-8?q?=E5=9B=9B=E5=8F=89=E6=A0=91=E7=9F=A9?= =?UTF-8?q?=E5=BD=A2=E5=86=B2=E7=AA=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/IFoxCAD.Cad/QuadTree/Class1.cs | 77 +++++++++++++++--------------- 1 file changed, 39 insertions(+), 38 deletions(-) diff --git a/src/IFoxCAD.Cad/QuadTree/Class1.cs b/src/IFoxCAD.Cad/QuadTree/Class1.cs index 947bdf3..81cdbcb 100644 --- a/src/IFoxCAD.Cad/QuadTree/Class1.cs +++ b/src/IFoxCAD.Cad/QuadTree/Class1.cs @@ -1,4 +1,5 @@ namespace IFoxCAD.Cad; +using Box = System.Windows.Rect; using System.Windows; @@ -10,16 +11,16 @@ /// This class efficiently stores and retrieves arbitrarily sized and positioned /// objects in a quad-tree data structure. This can be used to do efficient hit /// detection or visiblility checks on objects in a virtualized canvas. -/// The object does not need to implement any special interface because the Rect Bounds +/// The object does not need to implement any special interface because the Box Bounds /// of those objects is handled as a separate argument to Insert. /// 此类有效地存储和检索四叉树数据结构中任意大小和位置的对象。 这可用于对虚拟画布中的对象进行有效的命中检测或可见性检查。 -/// 该对象不需要实现任何特殊接口,因为这些对象的 Rect Bounds 被作为单独的参数进行插入。 +/// 该对象不需要实现任何特殊接口,因为这些对象的 Box Bounds 被作为单独的参数进行插入。 /// public class QuadTree where T : class { - Rect bounds; // overall bounds we are indexing. 索引的总体范围 + Box bounds; // overall bounds we are indexing. 索引的总体范围 Quadrant? root; IDictionary? table; @@ -30,7 +31,7 @@ public class QuadTree where T : class /// is expensive since it has to re-divide the entire thing - like a re-hash operation. /// 这决定了整个四叉树索引策略,改变这个界限是昂贵的,因为它必须重新划分整个事物 - 就像重新散列操作一样。 /// - public Rect Bounds + public Box Bounds { get { return this.bounds; } set { this.bounds = value; ReIndex(); } @@ -42,7 +43,7 @@ public Rect Bounds /// /// The node to insert 要插入的节点 /// The bounds of this node 这个节点的边界 - public void Insert(T node, Rect bounds) + public void Insert(T node, Box bounds) { if (this.bounds.Width == 0 || this.bounds.Height == 0) { @@ -77,7 +78,7 @@ public void Insert(T node, Rect bounds) /// List of zero or mode nodes found inside the given bounds /// 在给定范围内找到零个或多个节点的列表 /// - public IEnumerable GetNodesInside(Rect bounds) + public IEnumerable GetNodesInside(Box bounds) { foreach (QuadNode n in GetNodes(bounds)) { @@ -91,7 +92,7 @@ public IEnumerable GetNodesInside(Rect bounds) /// /// The bounds to test 给定的边界 /// 如果此象限中有任何节点与给定边界相交,则返回 true。 - public bool HasNodesInside(Rect bounds) + public bool HasNodesInside(Box bounds) { if (this.root == null) { @@ -105,7 +106,7 @@ public bool HasNodesInside(Rect bounds) /// /// The bounds to test /// The list of nodes intersecting the given bounds - IEnumerable GetNodes(Rect bounds) + IEnumerable GetNodes(Box bounds) { List result = new(); if (this.root != null) @@ -152,7 +153,7 @@ void ReIndex() /// internal class QuadNode { - Rect bounds; + Box bounds; QuadNode? next; // linked in a circular list. T node; // the actual visual object being stored here. @@ -161,7 +162,7 @@ internal class QuadNode /// /// The node /// The bounds of that node - public QuadNode(T node, Rect bounds) + public QuadNode(T node, Box bounds) { this.node = node; this.bounds = bounds; @@ -177,9 +178,9 @@ public T Node } /// - /// The Rect bounds of the node + /// The Box bounds of the node /// - public Rect Bounds + public Box Bounds { get { return this.bounds; } } @@ -204,7 +205,7 @@ public QuadNode? Next internal class Quadrant { readonly Quadrant? parent; - Rect bounds; // quadrant bounds. + Box bounds; // quadrant bounds. QuadNode nodes; // nodes that overlap the sub quadrant boundaries. @@ -223,7 +224,7 @@ internal class Quadrant /// /// The parent quadrant (if any) /// The bounds of this quadrant - public Quadrant(Quadrant? parent, Rect bounds) + public Quadrant(Quadrant? parent, Box bounds) { this.parent = parent; //Fx.Assert(bounds.Width != 0 && bounds.Height != 0, "Cannot have empty bound"); @@ -245,7 +246,7 @@ internal Quadrant? Parent /// /// The bounds of this quadrant /// - internal Rect Bounds + internal Box Bounds { get { return this.bounds; } } @@ -256,7 +257,7 @@ internal Rect Bounds /// The node /// The bounds of that node /// - internal Quadrant Insert(T node, Rect bounds) + internal Quadrant Insert(T node, Box bounds) { @@ -279,13 +280,13 @@ internal Quadrant Insert(T node, Rect bounds) h = 1; } - // assumption that the Rect struct is almost as fast as doing the operations - // manually since Rect is a value type. + // assumption that the Box struct is almost as fast as doing the operations + // manually since Box is a value type. - Rect topLeft = new Rect(toInsert.bounds.Left, toInsert.bounds.Top, w, h); - Rect topRight = new Rect(toInsert.bounds.Left + w, toInsert.bounds.Top, w, h); - Rect bottomLeft = new Rect(toInsert.bounds.Left, toInsert.bounds.Top + h, w, h); - Rect bottomRight = new Rect(toInsert.bounds.Left + w, toInsert.bounds.Top + h, w, h); + Box topLeft = new Box(toInsert.bounds.Left, toInsert.bounds.Top, w, h); + Box topRight = new Box(toInsert.bounds.Left + w, toInsert.bounds.Top, w, h); + Box bottomLeft = new Box(toInsert.bounds.Left, toInsert.bounds.Top + h, w, h); + Box bottomRight = new Box(toInsert.bounds.Left + w, toInsert.bounds.Top + h, w, h); Quadrant? child = null; @@ -353,19 +354,19 @@ internal Quadrant Insert(T node, Rect bounds) /// /// List of nodes found in the given bounds /// The bounds that contains the nodes you want returned - internal void GetIntersectingNodes(List nodes, Rect bounds) + internal void GetIntersectingNodes(List nodes, Box bounds) { if (bounds.IsEmpty) return; double w = this.bounds.Width / 2; double h = this.bounds.Height / 2; - // assumption that the Rect struct is almost as fast as doing the operations - // manually since Rect is a value type. + // assumption that the Box struct is almost as fast as doing the operations + // manually since Box is a value type. - Rect topLeft = new Rect(this.bounds.Left, this.bounds.Top, w, h); - Rect topRight = new Rect(this.bounds.Left + w, this.bounds.Top, w, h); - Rect bottomLeft = new Rect(this.bounds.Left, this.bounds.Top + h, w, h); - Rect bottomRight = new Rect(this.bounds.Left + w, this.bounds.Top + h, w, h); + Box topLeft = new Box(this.bounds.Left, this.bounds.Top, w, h); + Box topRight = new Box(this.bounds.Left + w, this.bounds.Top, w, h); + Box bottomLeft = new Box(this.bounds.Left, this.bounds.Top + h, w, h); + Box bottomRight = new Box(this.bounds.Left + w, this.bounds.Top + h, w, h); // See if any child quadrants completely contain this node. if (topLeft.IntersectsWith(bounds) && this.topLeft != null) @@ -398,7 +399,7 @@ internal void GetIntersectingNodes(List nodes, Rect bounds) /// The last QuadNode in a circularly linked list /// The resulting nodes are added to this list /// The bounds to test against each node - static void GetIntersectingNodes(QuadNode last, List nodes, Rect bounds) + static void GetIntersectingNodes(QuadNode last, List nodes, Box bounds) { if (last != null) { @@ -420,19 +421,19 @@ static void GetIntersectingNodes(QuadNode last, List nodes, Rect bound /// /// The bounds to test /// boolean - internal bool HasIntersectingNodes(Rect bounds) + internal bool HasIntersectingNodes(Box bounds) { if (bounds.IsEmpty) return false; double w = this.bounds.Width / 2; double h = this.bounds.Height / 2; - // assumption that the Rect struct is almost as fast as doing the operations - // manually since Rect is a value type. + // assumption that the Box struct is almost as fast as doing the operations + // manually since Box is a value type. - Rect topLeft = new Rect(this.bounds.Left, this.bounds.Top, w, h); - Rect topRight = new Rect(this.bounds.Left + w, this.bounds.Top, w, h); - Rect bottomLeft = new Rect(this.bounds.Left, this.bounds.Top + h, w, h); - Rect bottomRight = new Rect(this.bounds.Left + w, this.bounds.Top + h, w, h); + Box topLeft = new Box(this.bounds.Left, this.bounds.Top, w, h); + Box topRight = new Box(this.bounds.Left + w, this.bounds.Top, w, h); + Box bottomLeft = new Box(this.bounds.Left, this.bounds.Top + h, w, h); + Box bottomRight = new Box(this.bounds.Left + w, this.bounds.Top + h, w, h); bool found = false; @@ -469,7 +470,7 @@ internal bool HasIntersectingNodes(Rect bounds) /// The last node in the circularly linked list. /// Bounds to test /// Return true if a node in the list intersects the bounds - static bool HasIntersectingNodes(QuadNode last, Rect bounds) + static bool HasIntersectingNodes(QuadNode last, Box bounds) { if (last != null) { -- Gitee From 2ca92db146f95b3d4453159726ffc0ead7ba9bfe Mon Sep 17 00:00:00 2001 From: liuqihong <540762622@qq.com> Date: Wed, 6 Apr 2022 03:35:26 +0800 Subject: [PATCH 083/675] =?UTF-8?q?=E9=98=BF=E6=83=8A=E7=9A=84=E7=9F=A9?= =?UTF-8?q?=E5=BD=A2=E7=B1=BB?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/IFoxCAD.Cad/QuadTree/Rect.cs | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/src/IFoxCAD.Cad/QuadTree/Rect.cs b/src/IFoxCAD.Cad/QuadTree/Rect.cs index 60fd4b8..c02fdc6 100644 --- a/src/IFoxCAD.Cad/QuadTree/Rect.cs +++ b/src/IFoxCAD.Cad/QuadTree/Rect.cs @@ -22,10 +22,10 @@ public bool Equals(Point2d a, Point2d b)//Point3d是struct不会为null else if (a == null) return false; #if true - // 方形限定 - // 在 0~1e-6 范围实现 圆形限定 则计算部分在浮点数6位后,没有啥意义 - // 在 0~1e-6 范围实现 从时间和CPU消耗来说,圆形限定 都没有 方形限定 的好 - return a.IsEqualTo(b, new Tolerance(_tolerance, _tolerance)); + // 方形限定 + // 在 0~1e-6 范围实现 圆形限定 则计算部分在浮点数6位后,没有啥意义 + // 在 0~1e-6 范围实现 从时间和CPU消耗来说,圆形限定 都没有 方形限定 的好 + return a.IsEqualTo(b, new Tolerance(_tolerance, _tolerance)); #else // 圆形限定 // DistanceTo 分别对XYZ进行了一次乘法,也是总数3次乘法,然后求了一次平方根 @@ -39,7 +39,7 @@ public int GetHashCode(Point2d obj) //结构体直接返回 obj.GetHashCode(); Point3d ToleranceDistinct3d //因为结构体是用可值叠加来判断?或者因为结构体兼备了一些享元模式的状态? //而类是构造的指针,所以取哈希值要改成x+y+z..s给Equals判断用,+是会溢出,所以用^ - return (int)obj.X ^ (int)obj.Y ^ (int)obj.Z; + return (int)obj.X ^ (int)obj.Y;// ^ (int)obj.Z; } } @@ -248,7 +248,7 @@ private bool ContainsInternal(double x, double y) } /// - /// 包含是四个点都包含 + /// 四个点都在内部就是包含 /// /// /// @@ -259,7 +259,7 @@ public bool Contains(Rect rect) } /// - /// 碰撞是其中一个点在就是在 + /// 一个点在内部就是碰撞 /// /// /// @@ -334,13 +334,13 @@ public static bool IsRectAngle(List? ptList, double tolerance = 1e-8) //最快的方案 //点乘求值法:(为了处理 正梯形/平行四边形 需要三次) //这里的容差要在1e-8内,因为点乘的三次浮点数乘法会令精度变低 - var dot = DotProductValue( pts[0],pts[1], pts[3]); + var dot = DotProductValue(pts[0], pts[1], pts[3]); if (Math.Abs(dot) < tolerance) { - dot = DotProductValue( pts[1],pts[2], pts[0]); + dot = DotProductValue(pts[1], pts[2], pts[0]); if (Math.Abs(dot) < tolerance) { - dot = DotProductValue(pts[2],pts[3], pts[1]); + dot = DotProductValue(pts[2], pts[3], pts[1]); return Math.Abs(dot) < tolerance; } } @@ -370,7 +370,7 @@ public static bool IsRect(List? ptList, double tolerance = 1e-10) { if (ptList == null) throw new ArgumentNullException(nameof(ptList)); - + var pts = ptList.ToList(); if (ptList.Count == 5) { @@ -440,7 +440,7 @@ public static bool RectAnglePointOrder(List? pts) node = node.Next; } //保证是逆时针 - var isAcw = CrossAclockwise(pts[0],pts[1], pts[2]); + var isAcw = CrossAclockwise(pts[0], pts[1], pts[2]); if (!isAcw) { var tmp = pts[1]; -- Gitee From 8d08a3bd5bbb1ead149f0104c7b1330c6b69e831 Mon Sep 17 00:00:00 2001 From: liuqihong <540762622@qq.com> Date: Wed, 6 Apr 2022 03:46:03 +0800 Subject: [PATCH 084/675] =?UTF-8?q?=E6=9B=B2=E7=BA=BF=E6=B6=88=E9=87=8D?= =?UTF-8?q?=E6=AD=A3=E7=A1=AE=E4=BA=86?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/IFoxCAD.Cad/ExtensionMethod/Topo.cs | 297 ++++++++++++++++-------- tests/Test/TestCurve.cs | 7 +- 2 files changed, 208 insertions(+), 96 deletions(-) diff --git a/src/IFoxCAD.Cad/ExtensionMethod/Topo.cs b/src/IFoxCAD.Cad/ExtensionMethod/Topo.cs index 4c7f43e..f20ce2a 100644 --- a/src/IFoxCAD.Cad/ExtensionMethod/Topo.cs +++ b/src/IFoxCAD.Cad/ExtensionMethod/Topo.cs @@ -29,7 +29,7 @@ public EdgeItem(Edge edge, bool forward) #region 方法 public CompositeCurve3d? GetCurve() { - var cc3d = Edge.Curve; + var cc3d = Edge.GeCurve3d; if (Forward) { return cc3d; @@ -175,36 +175,38 @@ public override int GetHashCode() #endregion } -/// -/// 边 -/// + public class Edge : IEquatable { #region 字段 - public CompositeCurve3d Curve; + public CompositeCurve3d GeCurve3d; public int StartIndex; public int EndIndex; + public static Tolerance _cadTolerance = new(1e-6, 1e-6); #endregion #region 构造 + /// + /// 边线(没有包围盒,除非ToCurve) + /// public Edge(CompositeCurve3d curve) { - Curve = curve; + GeCurve3d = curve; } #endregion #region 方法 public Vector3d GetStartVector() { - var inter = Curve.GetInterval(); - PointOnCurve3d poc = new(Curve, inter.LowerBound); + var inter = GeCurve3d.GetInterval(); + PointOnCurve3d poc = new(GeCurve3d, inter.LowerBound); return poc.GetDerivative(1); } public Vector3d GetEndVector() { - var inter = Curve.GetInterval(); - PointOnCurve3d poc = new(Curve, inter.UpperBound); + var inter = GeCurve3d.GetInterval(); + PointOnCurve3d poc = new(GeCurve3d, inter.UpperBound); return -poc.GetDerivative(1); } @@ -260,111 +262,214 @@ public bool Equals(Edge? b) if (ReferenceEquals(a, b))//同一对象 return true; - return a.Curve == b.Curve && - a.StartIndex == b.StartIndex && - a.EndIndex == b.EndIndex; + return a.GeCurve3d == b.GeCurve3d + && a.StartIndex == b.StartIndex + && a.EndIndex == b.EndIndex; } + /// + /// 采样点比较(会排序,无视顺序逆序) + /// + /// + /// 切割曲线份数 + /// + public bool SplitPointEquals(Edge? b, int splitNum = 5) + { + //此处地方不允许使用==null,因为此处是定义 + if (b is null) + return this is null; + else if (this is null) + return false; + if (ReferenceEquals(this, b))//同一对象 + return true; + + //曲线采样分割点也一样,才是一样 + Point3d[] sp1; + Point3d[] sp2; +#if NET35 + sp1 = GeCurve3d.GetSamplePoints(splitNum); + sp2 = b.GeCurve3d.GetSamplePoints(splitNum); + if (sp1.Length != sp2.Length) + return false; +#else + var tmp1 = GeCurve3d.GetSamplePoints(splitNum); + var tmp2 = b.GeCurve3d.GetSamplePoints(splitNum); + if (tmp1.Length != tmp2.Length) + return false; + + sp1 = tmp1.Select(a => a.Point).ToArray(); + sp2 = tmp2.Select(a => a.Point).ToArray(); +#endif + + //因为两条曲线可能是逆向的,所以采样之后需要点排序 + sp1 = sp1.OrderBy(a => a.X).ThenBy(a => a.Y).ThenBy(a => a.Z).ToArray(); + sp2 = sp2.OrderBy(a => a.X).ThenBy(a => a.Y).ThenBy(a => a.Z).ToArray(); + + for (int i = 0; i < sp1.Length; i++) + { + if (!sp1[i].IsEqualTo(sp2[i], _cadTolerance)) + return false; + } + return true; + } public override int GetHashCode() { return base.GetHashCode(); } #endregion } - -public class CurveInfo: Rect + +public class CurveInfo : Rect { + /// + /// 曲线图元(母线) + /// public Curve Curve; - public CompositeCurve3d? CompositeCurve3d - { - get { - return + /// + /// 曲线参数 + /// + public List Paramss; + + Edge? _Edge; + /// + /// 边界 + /// + public Edge Edge + { + get + { + if (_Edge == null) + _Edge = new Edge(Curve.ToCompositeCurve3d()!); + return _Edge; } } - - public List? Paramss; + public CurveInfo(Curve curve) { - Curve = curve; Paramss = new List(); + Curve = curve; + + //TODO 此处存在bug:射线之类的没有过滤 + var box = Curve.GeometricExtents; + _X = box.MinPoint.X; + _Y = box.MinPoint.Y; + _Right = box.MaxPoint.X; + _Top = box.MaxPoint.Y; + } + + /// + /// 分割曲线,返回子线 + /// + /// 曲线参数 + /// + public List Split(List pars1) + { + var edges = new List(); + var c3ds = Edge.GeCurve3d.GetSplitCurves(pars1); + if (c3ds.Count > 0) + edges.AddRange(c3ds.Select(c => new Edge(c))); + return edges; } } + +// private class EdgeComparer : IEqualityComparer +// { +// public bool Equals(Edge x, Edge y) +// { +//#if ac2009 +// var pts = x.Curve.GetSamplePoints(100); +// return pts.All(pt => y.Curve.IsOn(pt)); +//#elif ac2013 || ac2015 +// var pts = x.Curve.GetSamplePoints(100); +// return pts.All(pt => y.Curve.IsOn(pt.Point)); +//#endif +// //return x.Curve.IsEqualTo(y.Curve); +// } + +// // If Equals() returns true for a pair of objects +// // then GetHashCode() must return the same value for these objects. +// public int GetHashCode(Edge product) +// { +// return product.Curve.GetHashCode(); +// } +// } + + public class Topo { /// /// 用于切割的曲线集 /// - List _curves; + List _curves; + + /// + /// 求交类 + /// + static CurveCurveIntersector3d _cci3d = new(); + public static Tolerance _cadTolerance = new(1e-6, 1e-6); public Topo(List curves) { - _curves = curves; + _curves = new(); + for (int i = 0; i < curves.Count; i++) + _curves.Add(new CurveInfo(curves[i])); + + //TODO 这里需要补充 列扫碰撞检测(碰撞算法) + //将包围盒碰撞的放入一个集合a,这个集合a又被_curves储存起来, + //集合a再运行 GetEdgesAndnewCurves } /// - /// 从曲线集合分离边界(交点断分曲线的)和独立的曲线 + /// 利用交点断分曲线和独立曲线 /// - /// 边界(可能仍然存在自闭,因为样条曲线允许打个鱼形圈,尾巴又交叉在其他曲线) + /// 边界(可能仍然存在自闭,因为样条曲线允许打个鱼形圈,尾巴又交叉在其他曲线) /// 自闭的曲线 - public void GetEdgesAndnewCurves(List edges, List closedCurvesOut) + public void GetEdgesAndnewCurves(List edgesOut, List closedCurvesOut) { - //请不要把它做成结构,因为这是一种ArrayOfStruct写法 - var geCurves = new List(); - var paramss = new List>(); - - //将曲线按节点转为Ge曲线集(提取多段线每段/样条曲线子段..) - for (int i = 0; i < _curves.Count; i++) + //此处是O(n²) + //曲线a和其他曲线n根据 交点 切割子线(第一次是自交对比) + for (int a = 0; a < _curves.Count; a++) { - _curves[i].GeometricExtents. - var cc3d = _curves[i].ToCompositeCurve3d(); - if (cc3d is not null) + var curve1 = _curves[a]; + var gc1 = curve1.Edge.GeCurve3d; + var pars1 = curve1.Paramss; + + for (int n = a; n < _curves.Count; n++) { - geCurves.Add(cc3d); - paramss.Add(new List()); - } - } + var curve2 = _curves[n]; - var cci3d = new CurveCurveIntersector3d(); + //包围盒没有碰撞就直接结束 + if (!curve1.IntersectsWith(curve2)) + continue; - //此处是O(n²) - //曲线a和曲线a/b/c/d/e...求交点,组成交点数组(第一次是自交对比) - for (int i = 0; i < geCurves.Count; i++) - { - var gc1 = geCurves[i]; - var pars1 = paramss[i]; - - for (int j = i; j < geCurves.Count; j++) - { - var gc2 = geCurves[j]; - var pars2 = paramss[j]; + var gc2 = curve2.Edge.GeCurve3d; + var pars2 = curve2.Paramss; //求交类此方法内部会重置,不需要清空,每次set都会有个新的结果 - cci3d.Set(gc1, gc2, Vector3d.ZAxis); + _cci3d.Set(gc1, gc2, Vector3d.ZAxis); //计算两条曲线的交点(多个),分别放入对应的交点参数集 - for (int k = 0; k < cci3d.NumberOfIntersectionPoints; k++) + for (int k = 0; k < _cci3d.NumberOfIntersectionPoints; k++) { - var pars = cci3d.GetIntersectionParameters(k); + var pars = _cci3d.GetIntersectionParameters(k); pars1.Add(pars[0]);//0是第一条曲线的交点参数 pars2.Add(pars[1]);//1是第二条曲线的交点参数 } } if (gc1.IsClosed()) - { closedCurvesOut.Add(gc1.ToCurve()!); - } //有交点参数 if (pars1.Count > 0) { //根据交点参数断分曲线,然后获取边界 - var c3ds = gc1.GetSplitCurves(pars1); + var c3ds = curve1.Split(pars1); if (c3ds.Count > 0) { - edges.AddRange(c3ds.Select(c => new Edge(c))); + edgesOut.AddRange(c3ds); } else { @@ -373,34 +478,38 @@ public void GetEdgesAndnewCurves(List edges, List closedCurvesOut) //猜测:曲线参数在头或者尾,那么交点就是直接碰头碰尾, //也就是 aa对比 同一条曲线自己和自己产生的? //参数大于0{是这些参数? 头参/尾参/参数不在曲线上?} - edges.Add(new Edge(gc1)); + edgesOut.Add(curve1.Edge); } } - } - edges = edges.Distinct(new EdgeComparer()).ToList(); - } -// private class EdgeComparer : IEqualityComparer -// { -// public bool Equals(Edge x, Edge y) -// { -//#if ac2009 -// var pts = x.Curve.GetSamplePoints(100); -// return pts.All(pt => y.Curve.IsOn(pt)); -//#elif ac2013 || ac2015 -// var pts = x.Curve.GetSamplePoints(100); -// return pts.All(pt => y.Curve.IsOn(pt.Point)); -//#endif -// //return x.Curve.IsEqualTo(y.Curve); -// } + if (edgesOut.Count > 0) + { + //Edge没有包围盒,无法快速判断 + //曲线a和其他曲线n根据交点切割子线,会造成重复部分,例如多段线逆向相同 + for (int i = edgesOut.Count - 1; i >= 0; i--) + { + var aa = edgesOut[i]; + for (int j = i - 1; j >= 0; j--) + { + var bb = edgesOut[j]; + //顺序 || 逆序 + if ((aa.GeCurve3d.StartPoint.IsEqualTo(bb.GeCurve3d.StartPoint, _cadTolerance) && + aa.GeCurve3d.EndPoint.IsEqualTo(bb.GeCurve3d.EndPoint, _cadTolerance)) + || + (aa.GeCurve3d.StartPoint.IsEqualTo(bb.GeCurve3d.EndPoint, _cadTolerance) && + aa.GeCurve3d.EndPoint.IsEqualTo(bb.GeCurve3d.StartPoint, _cadTolerance))) + { + if (aa.SplitPointEquals(bb, 5)) + edgesOut.RemoveAt(i); + } + } + } + } + } -// // If Equals() returns true for a pair of objects -// // then GetHashCode() must return the same value for these objects. -// public int GetHashCode(Edge product) -// { -// return product.Curve.GetHashCode(); -// } -// } + //TODO: 此处是否消重?此处数据量超大,但是可以用四叉树 + //edgesOut = edgesOut.Distinct(new EdgeComparer()).ToList(); + } /// /// 创建邻接表 @@ -420,41 +529,41 @@ public void AdjacencyList(List edges, List closedCurvesOut) for (int i = 0; i < edges.Count; i++) { var edge = edges[i]; - if (edge.Curve.IsClosed()) + if (edge.GeCurve3d.IsClosed()) { closedEdges.Add(edge); continue; } - if (knots.Contains(edge.Curve.StartPoint)) + if (knots.Contains(edge.GeCurve3d.StartPoint)) { //含有就是其他曲线"共用"此交点, //节点所在索引==共用计数索引=>将它++ - edge.StartIndex = knots.IndexOf(edge.Curve.StartPoint); + edge.StartIndex = knots.IndexOf(edge.GeCurve3d.StartPoint); nums[edge.StartIndex]++; } else { //不含有就加入节点,共用计数也加入,边界设置节点索引 - knots.Add(edge.Curve.StartPoint); + knots.Add(edge.GeCurve3d.StartPoint); nums.Add(1); edge.StartIndex = knots.Count - 1; } - if (knots.Contains(edge.Curve.EndPoint)) + if (knots.Contains(edge.GeCurve3d.EndPoint)) { - edge.EndIndex = knots.IndexOf(edge.Curve.EndPoint); + edge.EndIndex = knots.IndexOf(edge.GeCurve3d.EndPoint); nums[edge.EndIndex]++; } else { - knots.Add(edge.Curve.EndPoint); + knots.Add(edge.GeCurve3d.EndPoint); nums.Add(1); edge.EndIndex = knots.Count - 1; } } - closedCurvesOut.AddRange(closedEdges.Select(e => e.Curve.ToCurve())!); + closedCurvesOut.AddRange(closedEdges.Select(e => e.GeCurve3d.ToCurve())!); //这里把交点只有一条曲线通过的点过滤掉了,也就是尾巴的图元, //剩下的都是闭合的曲线连接了,每个点都至少有两条曲线通过 @@ -531,8 +640,8 @@ public void RegionsInfo(List> regions) if (regions[i].Count == regions[j].Count) { var node = regions[i].First; - var curve = node!.Value.Edge.Curve; - var node2 = regions[j].GetNode(e => e.Edge.Curve == curve); + var curve = node!.Value.Edge.GeCurve3d; + var node2 = regions[j].GetNode(e => e.Edge.GeCurve3d == curve); //var node2 = regions[j].Find(node.Value); if (node2 is not null) { @@ -543,7 +652,7 @@ public void RegionsInfo(List> regions) { node = node.GetNext(b); node2 = node2.GetNext(b2); - if (node!.Value.Edge.Curve != node2!.Value.Edge.Curve) + if (node!.Value.Edge.GeCurve3d != node2!.Value.Edge.GeCurve3d) { eq = false; break; diff --git a/tests/Test/TestCurve.cs b/tests/Test/TestCurve.cs index 5a48606..16c8842 100644 --- a/tests/Test/TestCurve.cs +++ b/tests/Test/TestCurve.cs @@ -13,6 +13,7 @@ public void TestBreakCurve() tt.ForEach(t => t.ForWrite(e => e.ColorIndex = 1)); tr.CurrentSpace.AddEntity(tt); } + [CommandMethod("testCurveCurveIntersector3d")] public void TestCurveCurveIntersector3d() { @@ -66,6 +67,8 @@ public void TestCurveCurveIntersector3d() } + + [CommandMethod("testtopo")] public void TestToPo() { @@ -93,7 +96,7 @@ public void TestGetEdgesAndnewCurves() for (int i = 0; i < edges.Count; i++) { - var ent = edges[i].Curve.ToCurve(); + var ent = edges[i].GeCurve3d.ToCurve(); ent.ColorIndex = i; tr.CurrentSpace.AddEntity(ent); } @@ -101,4 +104,4 @@ public void TestGetEdgesAndnewCurves() //Env.Print(""); } } -} +} \ No newline at end of file -- Gitee From a40957ea7fc39e0376e7a88f7dfb58832273670d Mon Sep 17 00:00:00 2001 From: liuqihong <540762622@qq.com> Date: Wed, 6 Apr 2022 06:00:34 +0800 Subject: [PATCH 085/675] =?UTF-8?q?=E5=85=A8=E9=83=A8=E5=81=9A=E6=88=90?= =?UTF-8?q?=E7=B1=BB=E7=BB=A7=E6=89=BF=E6=96=B9=E5=BC=8F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/IFoxCAD.Cad/ExtensionMethod/Topo.cs | 498 ++++++++++++------------ 1 file changed, 255 insertions(+), 243 deletions(-) diff --git a/src/IFoxCAD.Cad/ExtensionMethod/Topo.cs b/src/IFoxCAD.Cad/ExtensionMethod/Topo.cs index f20ce2a..125bee8 100644 --- a/src/IFoxCAD.Cad/ExtensionMethod/Topo.cs +++ b/src/IFoxCAD.Cad/ExtensionMethod/Topo.cs @@ -2,16 +2,216 @@ using IFoxCAD.Basal; -/// -/// 边节点 -/// -public struct EdgeItem : IEquatable + +public class Edge : IEquatable { #region 字段 + public CompositeCurve3d GeCurve3d; + public int StartIndex; + public int EndIndex; + public static Tolerance CadTolerance = new(1e-6, 1e-6); + #endregion + + #region 构造 /// - /// 边界 + /// 边线(没有包围盒,除非ToCurve) + /// + public Edge(CompositeCurve3d geCurve3d) + { + GeCurve3d = geCurve3d; + } + public Edge(Edge edge) : this(edge.GeCurve3d) + { + StartIndex = edge.StartIndex; + EndIndex = edge.EndIndex; + } + #endregion + + #region 方法 + public Vector3d GetStartVector() + { + var inter = GeCurve3d.GetInterval(); + PointOnCurve3d poc = new(GeCurve3d, inter.LowerBound); + return poc.GetDerivative(1); + } + + public Vector3d GetEndVector() + { + var inter = GeCurve3d.GetInterval(); + PointOnCurve3d poc = new(GeCurve3d, inter.UpperBound); + return -poc.GetDerivative(1); + } + + /// + /// 判断节点位置 /// - public Edge Edge; + /// 边界 + /// 边界是否位于此处 + /// + /// + /// + public bool IsNext(Edge edge, int startOrEndIndex, ref Vector3d vec, ref bool forward) + { + if (edge == this) + return false; + + if (StartIndex == startOrEndIndex) + { + vec = GetStartVector(); + forward = true; + return true; + } + else if (EndIndex == startOrEndIndex) + { + vec = GetEndVector(); + forward = false; + return true; + } + return false; + } + #endregion + + #region 重载运算符_比较 + public override bool Equals(object? obj) + { + return this == obj as Edge; + } + public bool Equals(Edge? b) + { + return this == b; + } + public static bool operator !=(Edge? a, Edge? b) + { + return !(a == b); + } + public static bool operator ==(Edge? a, Edge? b) + { + //此处地方不允许使用==null,因为此处是定义 + if (b is null) + return a is null; + else if (a is null) + return false; + if (ReferenceEquals(a, b))//同一对象 + return true; + + return a.GeCurve3d == b.GeCurve3d + && a.StartIndex == b.StartIndex + && a.EndIndex == b.EndIndex; + } + + /// + /// 采样点比较(会排序,无视顺序逆序) + /// + /// + /// 切割曲线份数 + /// + public bool SplitPointEquals(Edge? b, int splitNum = 4) + { + if (b is null) + return this is null; + else if (this is null) + return false; + if (ReferenceEquals(this, b))//同一对象 + return true; + + //这里获取曲线长度会经过很多次平方根, + //所以不要这样做,直接采样点之后判断就完事 + + //曲线采样分割点也一样,才是一样 + Point3d[] sp1; + Point3d[] sp2; + +#if NET35 + sp1 = GeCurve3d.GetSamplePoints(splitNum); + sp2 = b.GeCurve3d.GetSamplePoints(splitNum); + if (sp1.Length != sp2.Length) + return false; +#else + var tmp1 = GeCurve3d.GetSamplePoints(splitNum); + var tmp2 = b.GeCurve3d.GetSamplePoints(splitNum); + if (tmp1.Length != tmp2.Length) + return false; + + sp1 = tmp1.Select(a => a.Point).ToArray(); + sp2 = tmp2.Select(a => a.Point).ToArray(); +#endif + + //因为两条曲线可能是逆向的,所以采样之后需要点排序 + sp1 = sp1.OrderBy(a => a.X).ThenBy(a => a.Y).ThenBy(a => a.Z).ToArray(); + sp2 = sp2.OrderBy(a => a.X).ThenBy(a => a.Y).ThenBy(a => a.Z).ToArray(); + + for (int i = 0; i < sp1.Length; i++) + { + if (!sp1[i].IsEqualTo(sp2[i], CadTolerance)) + return false; + } + return true; + } + + /// + /// 消重顺序逆序 + /// + /// + public static void Distinct(List edgesOut) + { + if (edgesOut.Count > 0) + return; + + //Edge没有包围盒,无法快速判断 + //曲线a和其他曲线n根据交点切割子线,会造成重复部分,例如多段线逆向相同 + for (int i = edgesOut.Count - 1; i >= 0; i--) + { + var aa = edgesOut[i]; + for (int j = i - 1; j >= 0; j--) + { + var bb = edgesOut[j]; + //顺序 || 逆序 + if ((aa.GeCurve3d.StartPoint.IsEqualTo(bb.GeCurve3d.StartPoint, CadTolerance) && + aa.GeCurve3d.EndPoint.IsEqualTo(bb.GeCurve3d.EndPoint, CadTolerance)) + || + (aa.GeCurve3d.StartPoint.IsEqualTo(bb.GeCurve3d.EndPoint, CadTolerance) && + aa.GeCurve3d.EndPoint.IsEqualTo(bb.GeCurve3d.StartPoint, CadTolerance))) + { + if (aa.SplitPointEquals(bb, 5)) + edgesOut.RemoveAt(i); + } + } + } + } + + // private class EdgeComparer : IEqualityComparer + // { + // public bool Equals(Edge x, Edge y) + // { + //#if ac2009 + // var pts = x.Curve.GetSamplePoints(100); + // return pts.All(pt => y.Curve.IsOn(pt)); + //#elif ac2013 || ac2015 + // var pts = x.Curve.GetSamplePoints(100); + // return pts.All(pt => y.Curve.IsOn(pt.Point)); + //#endif + // //return x.Curve.IsEqualTo(y.Curve); + // } + + // // If Equals() returns true for a pair of objects + // // then GetHashCode() must return the same value for these objects. + // public int GetHashCode(Edge product) + // { + // return product.Curve.GetHashCode(); + // } + // } + + public override int GetHashCode() + { + return base.GetHashCode(); + } + #endregion +} + + +public class EdgeItem : Edge, IEquatable +{ + #region 字段 /// /// 用来判断搜索方向(向前还是向后) /// @@ -19,9 +219,11 @@ public struct EdgeItem : IEquatable #endregion #region 构造 - public EdgeItem(Edge edge, bool forward) + /// + /// 边节点 + /// + public EdgeItem(Edge edge, bool forward) : base(edge) { - Edge = edge; Forward = forward; } #endregion @@ -29,7 +231,7 @@ public EdgeItem(Edge edge, bool forward) #region 方法 public CompositeCurve3d? GetCurve() { - var cc3d = Edge.GeCurve3d; + var cc3d = GeCurve3d; if (Forward) { return cc3d; @@ -53,7 +255,7 @@ public void FindRegion(List edges, List> regions) var edgeItem = this; region.Add(edgeItem); var edgeItem2 = this.GetNext(edges); - if (edgeItem2.Edge is null) + if (edgeItem2 is null) return; bool hasList = false; @@ -73,14 +275,14 @@ public void FindRegion(List edges, List> regions) } if (!hasList) { - while (edgeItem2.Edge is not null) + while (edgeItem2 is not null) { - if (edgeItem2.Edge == edgeItem.Edge) + if (edgeItem2 == edgeItem) break; region.Add(edgeItem2); //TODO 此处死循环,上一条语句判断失误,导致不停的将相同的值加入region edgeItem2 = edgeItem2.GetNext(edges); } - if (edgeItem2.Edge == edgeItem.Edge) + if (edgeItem2 == edgeItem) regions.Add(region); } } @@ -90,22 +292,22 @@ public void FindRegion(List edges, List> regions) /// /// /// - public EdgeItem GetNext(List edges) + public EdgeItem? GetNext(List edges) { Vector3d vec; int next; if (Forward) { - vec = Edge.GetEndVector(); - next = Edge.EndIndex; + vec = GetEndVector(); + next = EndIndex; } else { - vec = Edge.GetStartVector(); - next = Edge.StartIndex; + vec = GetStartVector(); + next = StartIndex; } - EdgeItem item = new(); + EdgeItem? edgeItem = null; Vector3d vec2, vec3 = new(); double angle = 0; bool hasNext = false; @@ -113,7 +315,7 @@ public EdgeItem GetNext(List edges) for (int i = 0; i < edges.Count; i++) { var edge = edges[i]; - if (edge.IsNext(Edge, next, ref vec3, ref forward)) + if (this.IsNext(edge, next, ref vec3, ref forward)) { if (hasNext) { @@ -122,21 +324,19 @@ public EdgeItem GetNext(List edges) { vec2 = vec3; angle = angle2; - item.Edge = edge; - item.Forward = forward; + edgeItem = new EdgeItem(edge, forward); } } else { + hasNext = true; vec2 = vec3; angle = vec.GetAngleTo(vec2, Vector3d.ZAxis); - item.Edge = edge; - item.Forward = forward; - hasNext = true; + edgeItem = new EdgeItem(edge, forward); } } } - return item; + return edgeItem; } #endregion @@ -145,114 +345,25 @@ public override string ToString() { return Forward ? - string.Format("{0}-{1}", Edge.StartIndex, Edge.EndIndex) : - string.Format("{0}-{1}", Edge.EndIndex, Edge.StartIndex); - } - #endregion - - #region 重载运算符_比较 - public override bool Equals(object obj) - { - return this == (EdgeItem)obj; - } - public bool Equals(EdgeItem b) - { - return this == b; - } - public static bool operator !=(EdgeItem a, EdgeItem b) - { - return !(a == b); - } - public static bool operator ==(EdgeItem a, EdgeItem b) - { - return a.Edge == b.Edge && a.Forward == b.Forward; - } - public override int GetHashCode() - { - return base.GetHashCode(); - //return this.ToString().GetHashCode(); - } - #endregion -} - - -public class Edge : IEquatable -{ - #region 字段 - public CompositeCurve3d GeCurve3d; - public int StartIndex; - public int EndIndex; - public static Tolerance _cadTolerance = new(1e-6, 1e-6); - #endregion - - #region 构造 - /// - /// 边线(没有包围盒,除非ToCurve) - /// - public Edge(CompositeCurve3d curve) - { - GeCurve3d = curve; - } - #endregion - - #region 方法 - public Vector3d GetStartVector() - { - var inter = GeCurve3d.GetInterval(); - PointOnCurve3d poc = new(GeCurve3d, inter.LowerBound); - return poc.GetDerivative(1); - } - - public Vector3d GetEndVector() - { - var inter = GeCurve3d.GetInterval(); - PointOnCurve3d poc = new(GeCurve3d, inter.UpperBound); - return -poc.GetDerivative(1); - } - - /// - /// 判断节点位置 - /// - /// 边界 - /// 边界是否位于此处 - /// - /// - /// - public bool IsNext(Edge edge, int startOrEndIndex, ref Vector3d vec, ref bool forward) - { - if (edge != this) - { - if (StartIndex == startOrEndIndex) - { - vec = GetStartVector(); - forward = true; - return true; - } - else if (EndIndex == startOrEndIndex) - { - vec = GetEndVector(); - forward = false; - return true; - } - } - return false; + string.Format("{0}-{1}", StartIndex, EndIndex) : + string.Format("{0}-{1}", EndIndex, StartIndex); } #endregion #region 重载运算符_比较 public override bool Equals(object? obj) { - return this == obj as Edge; + return this == obj as EdgeItem; } - public bool Equals(Edge? b) + public bool Equals(EdgeItem? b) { return this == b; } - public static bool operator !=(Edge? a, Edge? b) + public static bool operator !=(EdgeItem? a, EdgeItem? b) { return !(a == b); } - public static bool operator ==(Edge? a, Edge? b) + public static bool operator ==(EdgeItem? a, EdgeItem? b) { //此处地方不允许使用==null,因为此处是定义 if (b is null) @@ -262,55 +373,7 @@ public bool Equals(Edge? b) if (ReferenceEquals(a, b))//同一对象 return true; - return a.GeCurve3d == b.GeCurve3d - && a.StartIndex == b.StartIndex - && a.EndIndex == b.EndIndex; - } - - /// - /// 采样点比较(会排序,无视顺序逆序) - /// - /// - /// 切割曲线份数 - /// - public bool SplitPointEquals(Edge? b, int splitNum = 5) - { - //此处地方不允许使用==null,因为此处是定义 - if (b is null) - return this is null; - else if (this is null) - return false; - if (ReferenceEquals(this, b))//同一对象 - return true; - - //曲线采样分割点也一样,才是一样 - Point3d[] sp1; - Point3d[] sp2; -#if NET35 - sp1 = GeCurve3d.GetSamplePoints(splitNum); - sp2 = b.GeCurve3d.GetSamplePoints(splitNum); - if (sp1.Length != sp2.Length) - return false; -#else - var tmp1 = GeCurve3d.GetSamplePoints(splitNum); - var tmp2 = b.GeCurve3d.GetSamplePoints(splitNum); - if (tmp1.Length != tmp2.Length) - return false; - - sp1 = tmp1.Select(a => a.Point).ToArray(); - sp2 = tmp2.Select(a => a.Point).ToArray(); -#endif - - //因为两条曲线可能是逆向的,所以采样之后需要点排序 - sp1 = sp1.OrderBy(a => a.X).ThenBy(a => a.Y).ThenBy(a => a.Z).ToArray(); - sp2 = sp2.OrderBy(a => a.X).ThenBy(a => a.Y).ThenBy(a => a.Z).ToArray(); - - for (int i = 0; i < sp1.Length; i++) - { - if (!sp1[i].IsEqualTo(sp2[i], _cadTolerance)) - return false; - } - return true; + return a.Forward == b.Forward && (a == b as Edge); } public override int GetHashCode() { @@ -347,8 +410,8 @@ public Edge Edge public CurveInfo(Curve curve) { - Paramss = new List(); Curve = curve; + Paramss = new List(); //TODO 此处存在bug:射线之类的没有过滤 var box = Curve.GeometricExtents; @@ -374,29 +437,6 @@ public List Split(List pars1) } -// private class EdgeComparer : IEqualityComparer -// { -// public bool Equals(Edge x, Edge y) -// { -//#if ac2009 -// var pts = x.Curve.GetSamplePoints(100); -// return pts.All(pt => y.Curve.IsOn(pt)); -//#elif ac2013 || ac2015 -// var pts = x.Curve.GetSamplePoints(100); -// return pts.All(pt => y.Curve.IsOn(pt.Point)); -//#endif -// //return x.Curve.IsEqualTo(y.Curve); -// } - -// // If Equals() returns true for a pair of objects -// // then GetHashCode() must return the same value for these objects. -// public int GetHashCode(Edge product) -// { -// return product.Curve.GetHashCode(); -// } -// } - - public class Topo { /// @@ -405,10 +445,10 @@ public class Topo List _curves; /// - /// 求交类 + /// 求交类(每次set自动重置,都会有个新的结果) /// static CurveCurveIntersector3d _cci3d = new(); - public static Tolerance _cadTolerance = new(1e-6, 1e-6); + public static Tolerance CadTolerance = new(1e-6, 1e-6); public Topo(List curves) { @@ -447,7 +487,6 @@ public void GetEdgesAndnewCurves(List edgesOut, List closedCurvesOu var gc2 = curve2.Edge.GeCurve3d; var pars2 = curve2.Paramss; - //求交类此方法内部会重置,不需要清空,每次set都会有个新的结果 _cci3d.Set(gc1, gc2, Vector3d.ZAxis); //计算两条曲线的交点(多个),分别放入对应的交点参数集 @@ -462,53 +501,27 @@ public void GetEdgesAndnewCurves(List edgesOut, List closedCurvesOu if (gc1.IsClosed()) closedCurvesOut.Add(gc1.ToCurve()!); + if (pars1.Count == 0) + continue; + //有交点参数 - if (pars1.Count > 0) + //根据交点参数断分曲线,然后获取边界 + var c3ds = curve1.Split(pars1); + if (c3ds.Count > 0) { - //根据交点参数断分曲线,然后获取边界 - var c3ds = curve1.Split(pars1); - if (c3ds.Count > 0) - { - edgesOut.AddRange(c3ds); - } - else - { - //惊惊留:(不敢删啊...) - //狐哥写的这里出现的条件是:有曲线参数,但是切分不出来曲线...没懂为什么... - //猜测:曲线参数在头或者尾,那么交点就是直接碰头碰尾, - //也就是 aa对比 同一条曲线自己和自己产生的? - //参数大于0{是这些参数? 头参/尾参/参数不在曲线上?} - edgesOut.Add(curve1.Edge); - } + edgesOut.AddRange(c3ds); } - - if (edgesOut.Count > 0) + else { - //Edge没有包围盒,无法快速判断 - //曲线a和其他曲线n根据交点切割子线,会造成重复部分,例如多段线逆向相同 - for (int i = edgesOut.Count - 1; i >= 0; i--) - { - var aa = edgesOut[i]; - for (int j = i - 1; j >= 0; j--) - { - var bb = edgesOut[j]; - //顺序 || 逆序 - if ((aa.GeCurve3d.StartPoint.IsEqualTo(bb.GeCurve3d.StartPoint, _cadTolerance) && - aa.GeCurve3d.EndPoint.IsEqualTo(bb.GeCurve3d.EndPoint, _cadTolerance)) - || - (aa.GeCurve3d.StartPoint.IsEqualTo(bb.GeCurve3d.EndPoint, _cadTolerance) && - aa.GeCurve3d.EndPoint.IsEqualTo(bb.GeCurve3d.StartPoint, _cadTolerance))) - { - if (aa.SplitPointEquals(bb, 5)) - edgesOut.RemoveAt(i); - } - } - } + //惊惊留:(不敢删啊...) + //狐哥写的这里出现的条件是:有曲线参数,但是切分不出来曲线...没懂为什么... + //是这些参数?{参数0位置?头参/尾参/参数不在曲线上?} + edgesOut.Add(curve1.Edge); } - } - //TODO: 此处是否消重?此处数据量超大,但是可以用四叉树 - //edgesOut = edgesOut.Distinct(new EdgeComparer()).ToList(); + //有交点的才消重,无交点必然不重复. + Edge.Distinct(edgesOut); + } } /// @@ -640,8 +653,8 @@ public void RegionsInfo(List> regions) if (regions[i].Count == regions[j].Count) { var node = regions[i].First; - var curve = node!.Value.Edge.GeCurve3d; - var node2 = regions[j].GetNode(e => e.Edge.GeCurve3d == curve); + var curve = node!.Value.GeCurve3d; + var node2 = regions[j].GetNode(e => e.GeCurve3d == curve); //var node2 = regions[j].Find(node.Value); if (node2 is not null) { @@ -652,7 +665,7 @@ public void RegionsInfo(List> regions) { node = node.GetNext(b); node2 = node2.GetNext(b2); - if (node!.Value.Edge.GeCurve3d != node2!.Value.Edge.GeCurve3d) + if (node!.Value.GeCurve3d != node2!.Value.GeCurve3d) { eq = false; break; @@ -667,5 +680,4 @@ public void RegionsInfo(List> regions) } } } - -} +} \ No newline at end of file -- Gitee From d90054c64de22f681392e13c5250e2c1d75544fe Mon Sep 17 00:00:00 2001 From: liuqihong <540762622@qq.com> Date: Tue, 5 Apr 2022 22:27:32 +0000 Subject: [PATCH 086/675] =?UTF-8?q?update=20src/IFoxCAD.Cad/ExtensionMetho?= =?UTF-8?q?d/Topo.cs.=20=E8=BF=90=E7=AE=97=E7=AC=A6=E5=86=99=E9=94=99?= =?UTF-8?q?=E4=BA=86?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/IFoxCAD.Cad/ExtensionMethod/Topo.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/IFoxCAD.Cad/ExtensionMethod/Topo.cs b/src/IFoxCAD.Cad/ExtensionMethod/Topo.cs index 125bee8..31202a5 100644 --- a/src/IFoxCAD.Cad/ExtensionMethod/Topo.cs +++ b/src/IFoxCAD.Cad/ExtensionMethod/Topo.cs @@ -1,4 +1,4 @@ -namespace IFoxCAD.Cad; +namespace IFoxCAD.Cad; using IFoxCAD.Basal; @@ -154,7 +154,7 @@ public bool SplitPointEquals(Edge? b, int splitNum = 4) /// public static void Distinct(List edgesOut) { - if (edgesOut.Count > 0) + if (edgesOut.Count == 0) return; //Edge没有包围盒,无法快速判断 -- Gitee From 6d91c79a1c83bdee6aacaacccaba4411f8cb434a Mon Sep 17 00:00:00 2001 From: liuqihong <540762622@qq.com> Date: Tue, 5 Apr 2022 22:29:20 +0000 Subject: [PATCH 087/675] =?UTF-8?q?update=20src/IFoxCAD.Cad/ExtensionMetho?= =?UTF-8?q?d/Topo.cs.=20=E5=88=A9=E7=94=A8=E9=BB=98=E8=AE=A4=E5=80=BC?= =?UTF-8?q?=E5=88=87=E5=89=B2?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/IFoxCAD.Cad/ExtensionMethod/Topo.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/IFoxCAD.Cad/ExtensionMethod/Topo.cs b/src/IFoxCAD.Cad/ExtensionMethod/Topo.cs index 31202a5..a70837c 100644 --- a/src/IFoxCAD.Cad/ExtensionMethod/Topo.cs +++ b/src/IFoxCAD.Cad/ExtensionMethod/Topo.cs @@ -172,7 +172,7 @@ public static void Distinct(List edgesOut) (aa.GeCurve3d.StartPoint.IsEqualTo(bb.GeCurve3d.EndPoint, CadTolerance) && aa.GeCurve3d.EndPoint.IsEqualTo(bb.GeCurve3d.StartPoint, CadTolerance))) { - if (aa.SplitPointEquals(bb, 5)) + if (aa.SplitPointEquals(bb)) edgesOut.RemoveAt(i); } } -- Gitee From 5a359b2f01ee2cec78e41c243a67ac9966a6a853 Mon Sep 17 00:00:00 2001 From: liuqihong <540762622@qq.com> Date: Wed, 6 Apr 2022 19:22:24 +0800 Subject: [PATCH 088/675] =?UTF-8?q?=E5=A2=9E=E5=8A=A0Topo=E9=87=87?= =?UTF-8?q?=E6=A0=B7=E8=AF=B4=E6=98=8E?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- ...271\346\225\260\350\257\264\346\230\216.png" | Bin 0 -> 28362 bytes 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 "docs/Topo\345\221\275\344\273\244\350\257\264\346\230\216/\346\233\262\347\272\277\351\207\207\346\240\267\347\202\271\346\225\260\350\257\264\346\230\216.png" diff --git "a/docs/Topo\345\221\275\344\273\244\350\257\264\346\230\216/\346\233\262\347\272\277\351\207\207\346\240\267\347\202\271\346\225\260\350\257\264\346\230\216.png" "b/docs/Topo\345\221\275\344\273\244\350\257\264\346\230\216/\346\233\262\347\272\277\351\207\207\346\240\267\347\202\271\346\225\260\350\257\264\346\230\216.png" new file mode 100644 index 0000000000000000000000000000000000000000..1c27b4ee80a3695d690b1d90a6e0fd3f477d8d1b GIT binary patch literal 28362 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 H)8PLBRGT($ literal 0 HcmV?d00001 -- Gitee From 435e4d33fb0a7462e1057bcab6e57b83128b25d5 Mon Sep 17 00:00:00 2001 From: liuqihong <540762622@qq.com> Date: Wed, 6 Apr 2022 19:40:10 +0800 Subject: [PATCH 089/675] =?UTF-8?q?=E5=A2=9E=E5=8A=A0=E6=9B=B2=E7=BA=BF?= =?UTF-8?q?=E9=87=87=E6=A0=B7=E7=82=B9=E6=95=B0=E8=AF=B4=E6=98=8E?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/IFoxCAD.Cad/ExtensionMethod/Topo.cs | 40 +++++-------------------- 1 file changed, 8 insertions(+), 32 deletions(-) diff --git a/src/IFoxCAD.Cad/ExtensionMethod/Topo.cs b/src/IFoxCAD.Cad/ExtensionMethod/Topo.cs index a70837c..f3f58e1 100644 --- a/src/IFoxCAD.Cad/ExtensionMethod/Topo.cs +++ b/src/IFoxCAD.Cad/ExtensionMethod/Topo.cs @@ -103,8 +103,11 @@ public bool Equals(Edge? b) /// 采样点比较(会排序,无视顺序逆序) /// /// - /// 切割曲线份数 - /// + /// + /// 切割曲线份数(采样3点令样条和圆弧重叠发生失效,因此从4开始) + /// 图例说明在将本工程docs内 + /// + /// 采样点重叠true public bool SplitPointEquals(Edge? b, int splitNum = 4) { if (b is null) @@ -149,7 +152,7 @@ public bool SplitPointEquals(Edge? b, int splitNum = 4) } /// - /// 消重顺序逆序 + /// 消重顺序逆序 /// /// public static void Distinct(List edgesOut) @@ -589,33 +592,6 @@ public void AdjacencyList(List edges, List closedCurvesOut) { edges.Add(ge.Current); } - - //这一大坨不用看了 注释掉也没影响貌似,而且后续也没有用 nums - // foreach (var edge in edges.Except(closedEdges)) - // { - // if (nums[edge.StartIndex] == 1 || nums[edge.EndIndex] == 1) - // { - // if (nums[edge.StartIndex] == 1 && nums[edge.EndIndex] == 1) - // { - // nums[edge.StartIndex] = 0; - // nums[edge.EndIndex] = 0; - // } - // else - // { - // int next = -1; - // if (nums[edge.StartIndex] == 1) - // { - // nums[edge.StartIndex] = 0; - // nums[next = edge.EndIndex]--; - // } - // else - // { - // nums[edge.EndIndex] = 0; - // nums[next = edge.StartIndex]--; - // } - // } - // } - // } } /// @@ -630,9 +606,9 @@ public List> GetRegions(List edges) for (int i = 0; i < edges.Count; i++) { var edge = edges[i]; - //TODO 这里有bug,两个内接的矩形会卡死 + //TODO 这里有bug,两个内接的矩形会卡死 FindRegion var edgeItem = new EdgeItem(edge, true); - edgeItem.FindRegion(edges, regions); // 经测试是这里卡住了 testTopo + edgeItem.FindRegion(edges, regions); edgeItem = new EdgeItem(edge, false); edgeItem.FindRegion(edges, regions); } -- Gitee From f48fb85c718e76475494c322b65021e95e871552 Mon Sep 17 00:00:00 2001 From: liuqihong <540762622@qq.com> Date: Wed, 6 Apr 2022 19:55:47 +0800 Subject: [PATCH 090/675] =?UTF-8?q?=E5=B0=86ToCurves=E7=94=9F=E6=88=90?= =?UTF-8?q?=E6=94=BE=E5=88=B0=E6=9C=80=E5=90=8E?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/IFoxCAD.Cad/ExtensionMethod/CurveEx.cs | 21 +++++++++++---------- src/IFoxCAD.Cad/ExtensionMethod/Topo.cs | 19 +++++++++++-------- tests/Test/TestCurve.cs | 4 ++-- 3 files changed, 24 insertions(+), 20 deletions(-) diff --git a/src/IFoxCAD.Cad/ExtensionMethod/CurveEx.cs b/src/IFoxCAD.Cad/ExtensionMethod/CurveEx.cs index 9f09464..4839b46 100644 --- a/src/IFoxCAD.Cad/ExtensionMethod/CurveEx.cs +++ b/src/IFoxCAD.Cad/ExtensionMethod/CurveEx.cs @@ -49,28 +49,29 @@ public static IEnumerable GetSplitCurves(this Curve curve, IEnumerable /// 曲线集 /// 闭合的曲线集 - public static List Topo(List curves) + public static IEnumerable? Topo(List curves) { //闭合的曲线集合 - var newCurves = new List(); + var closedCurve3d = new List(); var edges = new List(); var topo = new Topo(curves); - topo.GetEdgesAndnewCurves(edges,newCurves); - topo.AdjacencyList(edges, newCurves); + topo.GetEdgesAndnewCurves(edges,closedCurve3d); + topo.AdjacencyList(edges, closedCurve3d); var regions = topo.GetRegions(edges); topo.RegionsInfo(regions); for (int i = 0; i < regions.Count; i++) { - var cs3ds = - regions[i] - .Select(e => e.GetCurve()) - .ToArray(); - newCurves.Add(new CompositeCurve3d(cs3ds).ToCurve()!); + var cs3ds = regions[i] + .Select(e => e.GetCurve()) + .ToArray(); + closedCurve3d.Add(new CompositeCurve3d(cs3ds)); } - return newCurves; + + //因为生成可能导致遗忘释放,所以这里统一生成 + return closedCurve3d.Select(e => e.ToCurve()!); } /// diff --git a/src/IFoxCAD.Cad/ExtensionMethod/Topo.cs b/src/IFoxCAD.Cad/ExtensionMethod/Topo.cs index f3f58e1..e6fa4d4 100644 --- a/src/IFoxCAD.Cad/ExtensionMethod/Topo.cs +++ b/src/IFoxCAD.Cad/ExtensionMethod/Topo.cs @@ -469,7 +469,7 @@ public Topo(List curves) /// /// 边界(可能仍然存在自闭,因为样条曲线允许打个鱼形圈,尾巴又交叉在其他曲线) /// 自闭的曲线 - public void GetEdgesAndnewCurves(List edgesOut, List closedCurvesOut) + public void GetEdgesAndnewCurves(List edgesOut, List closedCurvesOut) { //此处是O(n²) //曲线a和其他曲线n根据 交点 切割子线(第一次是自交对比) @@ -502,7 +502,10 @@ public void GetEdgesAndnewCurves(List edgesOut, List closedCurvesOu } if (gc1.IsClosed()) - closedCurvesOut.Add(gc1.ToCurve()!); + { + //closedCurvesOut.Add(gc1.ToCurve()!); + closedCurvesOut.Add(gc1); + } if (pars1.Count == 0) continue; @@ -530,9 +533,9 @@ public void GetEdgesAndnewCurves(List edgesOut, List closedCurvesOu /// /// 创建邻接表 /// - /// - /// - public void AdjacencyList(List edges, List closedCurvesOut) + /// 传入的集合将被传出,扔掉单交点图元剩下闭合图元集 + /// + public void AdjacencyList(List edges, List closedCurve3dOut) { //构建边的邻接表 //knots 和 nums 实际上是一个键值对(基于ArrayOfStruct思想,拆开更合适内存布局) @@ -579,19 +582,19 @@ public void AdjacencyList(List edges, List closedCurvesOut) } } - closedCurvesOut.AddRange(closedEdges.Select(e => e.GeCurve3d.ToCurve())!); + //closedCurvesOut.AddRange(closedEdges.Select(e => e.GeCurve3d.ToCurve())!); + closedCurve3dOut.AddRange(closedEdges.Select(e => e.GeCurve3d)); //这里把交点只有一条曲线通过的点过滤掉了,也就是尾巴的图元, //剩下的都是闭合的曲线连接了,每个点都至少有两条曲线通过 var tmp = edges .Except(closedEdges) .Where(e => nums[e.StartIndex] > 1 && nums[e.EndIndex] > 1); + edges.Clear(); var ge = tmp.GetEnumerator(); while (ge.MoveNext()) - { edges.Add(ge.Current); - } } /// diff --git a/tests/Test/TestCurve.cs b/tests/Test/TestCurve.cs index 16c8842..20b9043 100644 --- a/tests/Test/TestCurve.cs +++ b/tests/Test/TestCurve.cs @@ -88,9 +88,9 @@ public void TestGetEdgesAndnewCurves() if (curves == null) return; var edges = new List(); - var newCurves = new List(); + var closedCurve3d = new List(); var topo = new Topo(curves); - topo.GetEdgesAndnewCurves(edges, newCurves); + topo.GetEdgesAndnewCurves(edges, closedCurve3d); using var tr = new DBTrans(); -- Gitee From 4d49963f73c8eb88af861ffe8381477b732b3820 Mon Sep 17 00:00:00 2001 From: liuqihong <540762622@qq.com> Date: Wed, 6 Apr 2022 20:04:05 +0800 Subject: [PATCH 091/675] =?UTF-8?q?=E4=BF=AE=E6=94=B9=E6=B5=8B=E8=AF=95?= =?UTF-8?q?=E5=91=BD=E4=BB=A4=E5=B9=B6=E4=BF=AE=E5=A4=8D=E4=BA=86Adjacency?= =?UTF-8?q?List=E7=9A=84bug?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/IFoxCAD.Cad/ExtensionMethod/Topo.cs | 15 +++++++-------- tests/Test/TestCurve.cs | 1 + 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/src/IFoxCAD.Cad/ExtensionMethod/Topo.cs b/src/IFoxCAD.Cad/ExtensionMethod/Topo.cs index e6fa4d4..e34f1e3 100644 --- a/src/IFoxCAD.Cad/ExtensionMethod/Topo.cs +++ b/src/IFoxCAD.Cad/ExtensionMethod/Topo.cs @@ -502,10 +502,7 @@ public void GetEdgesAndnewCurves(List edgesOut, List clo } if (gc1.IsClosed()) - { - //closedCurvesOut.Add(gc1.ToCurve()!); closedCurvesOut.Add(gc1); - } if (pars1.Count == 0) continue; @@ -589,12 +586,14 @@ public void AdjacencyList(List edges, List closedCurve3d //剩下的都是闭合的曲线连接了,每个点都至少有两条曲线通过 var tmp = edges .Except(closedEdges) - .Where(e => nums[e.StartIndex] > 1 && nums[e.EndIndex] > 1); - + .Where(e => nums[e.StartIndex] > 1 && nums[e.EndIndex] > 1) + .ToArray();//要ToArray克隆,否则下面会清空掉这个容器 + edges.Clear(); - var ge = tmp.GetEnumerator(); - while (ge.MoveNext()) - edges.Add(ge.Current); + for (int i = 0; i < tmp.Length; i++) + { + edges.Add(tmp[i]); + } } /// diff --git a/tests/Test/TestCurve.cs b/tests/Test/TestCurve.cs index 20b9043..65ed754 100644 --- a/tests/Test/TestCurve.cs +++ b/tests/Test/TestCurve.cs @@ -91,6 +91,7 @@ public void TestGetEdgesAndnewCurves() var closedCurve3d = new List(); var topo = new Topo(curves); topo.GetEdgesAndnewCurves(edges, closedCurve3d); + topo.AdjacencyList(edges, closedCurve3d);//增加测试 using var tr = new DBTrans(); -- Gitee From d84f3f61daf589153a0802f2dcbcdde786defd8c Mon Sep 17 00:00:00 2001 From: liuqihong <540762622@qq.com> Date: Wed, 6 Apr 2022 23:10:12 +0800 Subject: [PATCH 092/675] =?UTF-8?q?=E5=8A=A0=E5=85=A5=E7=A2=B0=E6=92=9E?= =?UTF-8?q?=E7=AE=97=E6=B3=95=E5=88=86=E9=93=BE=E6=89=A7=E8=A1=8C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/IFoxCAD.Basal/ArrayEx.cs | 18 +++ src/IFoxCAD.Cad/ExtensionMethod/CurveEx.cs | 36 +++--- src/IFoxCAD.Cad/ExtensionMethod/Topo.cs | 144 ++++++++++++++++----- tests/Test/TestCurve.cs | 34 +++-- 4 files changed, 171 insertions(+), 61 deletions(-) diff --git a/src/IFoxCAD.Basal/ArrayEx.cs b/src/IFoxCAD.Basal/ArrayEx.cs index 64e98ae..818d54e 100644 --- a/src/IFoxCAD.Basal/ArrayEx.cs +++ b/src/IFoxCAD.Basal/ArrayEx.cs @@ -15,5 +15,23 @@ public static T[] Combine2(this T[] a, T[] b) return c; } + /// + /// 单数组消重 + /// + /// + /// + public static void Deduplication(List edgesOut, Func func) + { + for (int i = 0; i < edgesOut.Count; i++) + { + var first = edgesOut[i]; + for (int j = edgesOut.Count - 1; j > i; j--) + { + var last = edgesOut[j]; + if (func(first, last)) + edgesOut.RemoveAt(j); + } + } + } } } diff --git a/src/IFoxCAD.Cad/ExtensionMethod/CurveEx.cs b/src/IFoxCAD.Cad/ExtensionMethod/CurveEx.cs index 4839b46..25c4c8b 100644 --- a/src/IFoxCAD.Cad/ExtensionMethod/CurveEx.cs +++ b/src/IFoxCAD.Cad/ExtensionMethod/CurveEx.cs @@ -51,27 +51,29 @@ public static IEnumerable GetSplitCurves(this Curve curve, IEnumerable闭合的曲线集 public static IEnumerable? Topo(List curves) { - //闭合的曲线集合 - var closedCurve3d = new List(); - var edges = new List(); + ////闭合的曲线集合 + //var closedCurve3d = new List(); + //var edges = new List(); - var topo = new Topo(curves); - topo.GetEdgesAndnewCurves(edges,closedCurve3d); - topo.AdjacencyList(edges, closedCurve3d); + //var topo = new Topo(curves); + //topo.GetEdgesAndnewCurves(edges,closedCurve3d); + //topo.AdjacencyList(edges, closedCurve3d); - var regions = topo.GetRegions(edges); - topo.RegionsInfo(regions); + //var regions = topo.GetRegions(edges); + //topo.RegionsInfo(regions); - for (int i = 0; i < regions.Count; i++) - { - var cs3ds = regions[i] - .Select(e => e.GetCurve()) - .ToArray(); - closedCurve3d.Add(new CompositeCurve3d(cs3ds)); - } + //for (int i = 0; i < regions.Count; i++) + //{ + // var cs3ds = regions[i] + // .Select(e => e.GetCurve()) + // .ToArray(); + // closedCurve3d.Add(new CompositeCurve3d(cs3ds)); + //} + + ////因为生成可能导致遗忘释放,所以这里统一生成 + //return closedCurve3d.Select(e => e.ToCurve()!); - //因为生成可能导致遗忘释放,所以这里统一生成 - return closedCurve3d.Select(e => e.ToCurve()!); + return null; } /// diff --git a/src/IFoxCAD.Cad/ExtensionMethod/Topo.cs b/src/IFoxCAD.Cad/ExtensionMethod/Topo.cs index e34f1e3..b345570 100644 --- a/src/IFoxCAD.Cad/ExtensionMethod/Topo.cs +++ b/src/IFoxCAD.Cad/ExtensionMethod/Topo.cs @@ -162,26 +162,21 @@ public static void Distinct(List edgesOut) //Edge没有包围盒,无法快速判断 //曲线a和其他曲线n根据交点切割子线,会造成重复部分,例如多段线逆向相同 - for (int i = edgesOut.Count - 1; i >= 0; i--) - { - var aa = edgesOut[i]; - for (int j = i - 1; j >= 0; j--) + ArrayEx.Deduplication(edgesOut, (first, last) => { + //顺序 || 逆序 + if ((first.GeCurve3d.StartPoint.IsEqualTo(last.GeCurve3d.StartPoint, CadTolerance) && + first.GeCurve3d.EndPoint.IsEqualTo(last.GeCurve3d.EndPoint, CadTolerance)) + || + (first.GeCurve3d.StartPoint.IsEqualTo(last.GeCurve3d.EndPoint, CadTolerance) && + first.GeCurve3d.EndPoint.IsEqualTo(last.GeCurve3d.StartPoint, CadTolerance))) { - var bb = edgesOut[j]; - //顺序 || 逆序 - if ((aa.GeCurve3d.StartPoint.IsEqualTo(bb.GeCurve3d.StartPoint, CadTolerance) && - aa.GeCurve3d.EndPoint.IsEqualTo(bb.GeCurve3d.EndPoint, CadTolerance)) - || - (aa.GeCurve3d.StartPoint.IsEqualTo(bb.GeCurve3d.EndPoint, CadTolerance) && - aa.GeCurve3d.EndPoint.IsEqualTo(bb.GeCurve3d.StartPoint, CadTolerance))) - { - if (aa.SplitPointEquals(bb)) - edgesOut.RemoveAt(i); - } + return first.SplitPointEquals(last); } - } + return false; + }); } + // private class EdgeComparer : IEqualityComparer // { // public bool Equals(Edge x, Edge y) @@ -396,6 +391,10 @@ public class CurveInfo : Rect /// 曲线参数 /// public List Paramss; + /// + /// 碰撞链 + /// + public CollisionChain? CollisionChain; Edge? _Edge; /// @@ -439,13 +438,26 @@ public List Split(List pars1) } } +public class CollisionChain +{ + /// + /// 碰撞链 + /// + public List Collision; + + public CollisionChain() + { + Collision = new List(); + } +} + public class Topo { /// /// 用于切割的曲线集 /// - List _curves; + List _cuc; /// /// 求交类(每次set自动重置,都会有个新的结果) @@ -455,33 +467,102 @@ public class Topo public Topo(List curves) { - _curves = new(); + List curveList = new(); + + //提取包围盒信息 + for (int i = 0; i < curves.Count; i++) + curveList.Add(new CurveInfo(curves[i])); + + _cuc = XCollision(curveList); + } + + /// + /// 列扫碰撞检测(碰撞算法) + /// 比四叉树还快哦~ + /// + /// + List XCollision(List curves) + { + //先排序X:不需要Y排序,因为Y的上下浮动不共X .ThenBy(a => a.Box.Y) + //因为先排序就可以有序遍历x区间,超过就break,达到更快 + curves = curves.OrderBy(a => a._X).ToList(); + + List collision = new(); + CollisionChain? cuc = null; + + //遍历所有图元 for (int i = 0; i < curves.Count; i++) - _curves.Add(new CurveInfo(curves[i])); + { + var one = curves[i]; + if (one.CollisionChain != null)//有碰撞链直接跳过 + continue; + + //搜索范围要在 one 的头尾中间的部分 + for (int j = i + 1; j < curves.Count; j++) + { + var two = curves[j]; + //x碰撞:矩形2的Left 在 矩形1[Left-Right]闭区间 + if (one._X <= two._X && two._X <= one._Right) + { + //y碰撞,那就是真的碰撞了 + if ((one._Top >= two._Top && two._Top >= one._Y) || + (one._Top >= two._Y && two._Y >= one._Y)) + { + if (cuc == null) + { + cuc = new(); + one.CollisionChain = cuc;//碰撞链设置 + cuc.Collision.Add(one);//本体也在链条内 + } + two.CollisionChain = cuc;//碰撞链设置 + cuc.Collision.Add(two); + } + //这里想中断y高过它的无意义比较, + //但是必须排序Y,而排序Y必须同X,而这里不是同X(而是同X区间),所以不能中断 + //而做到X区间排序,就必须创造一个集合,再排序这个集合, + //会导致每个图元都拥有一次X区间集合,开销更巨大(因此放弃). + } + else + break;//因为已经排序了,后续的必然超过 x碰撞区间 + } + if (cuc != null) + { + collision.Add(cuc); + cuc = null; + } + } + return collision; + } - //TODO 这里需要补充 列扫碰撞检测(碰撞算法) - //将包围盒碰撞的放入一个集合a,这个集合a又被_curves储存起来, - //集合a再运行 GetEdgesAndnewCurves + /// + /// 遍历多条碰撞链 + /// + /// + public void CollisionFor(Action> action) + { + _cuc.ForEach(a => { + action(a.Collision);//输出每条碰撞链 + }); } /// - /// 利用交点断分曲线和独立曲线 + /// 利用交点断分曲线和独立自闭曲线 /// /// 边界(可能仍然存在自闭,因为样条曲线允许打个鱼形圈,尾巴又交叉在其他曲线) /// 自闭的曲线 - public void GetEdgesAndnewCurves(List edgesOut, List closedCurvesOut) + public void GetEdgesAndnewCurves(List infos, List edgesOut, List closedCurvesOut) { //此处是O(n²) //曲线a和其他曲线n根据 交点 切割子线(第一次是自交对比) - for (int a = 0; a < _curves.Count; a++) + for (int a = 0; a < infos.Count; a++) { - var curve1 = _curves[a]; + var curve1 = infos[a]; var gc1 = curve1.Edge.GeCurve3d; var pars1 = curve1.Paramss; - for (int n = a; n < _curves.Count; n++) + for (int n = a; n < infos.Count; n++) { - var curve2 = _curves[n]; + var curve2 = infos[n]; //包围盒没有碰撞就直接结束 if (!curve1.IntersectsWith(curve2)) @@ -530,7 +611,7 @@ public void GetEdgesAndnewCurves(List edgesOut, List clo /// /// 创建邻接表 /// - /// 传入的集合将被传出,扔掉单交点图元剩下闭合图元集 + /// 传入传出,扔掉单交点图元剩下闭合图元集 /// public void AdjacencyList(List edges, List closedCurve3dOut) { @@ -542,6 +623,8 @@ public void AdjacencyList(List edges, List closedCurve3d var nums = new List(); var closedEdges = new List(); + //一次遍历就完成了,所以不需要四叉树, + //但是不需要时,交点集合 knots 会不断增大,会变得更加慢 for (int i = 0; i < edges.Count; i++) { var edge = edges[i]; @@ -579,7 +662,6 @@ public void AdjacencyList(List edges, List closedCurve3d } } - //closedCurvesOut.AddRange(closedEdges.Select(e => e.GeCurve3d.ToCurve())!); closedCurve3dOut.AddRange(closedEdges.Select(e => e.GeCurve3d)); //这里把交点只有一条曲线通过的点过滤掉了,也就是尾巴的图元, @@ -591,9 +673,7 @@ public void AdjacencyList(List edges, List closedCurve3d edges.Clear(); for (int i = 0; i < tmp.Length; i++) - { edges.Add(tmp[i]); - } } /// diff --git a/tests/Test/TestCurve.cs b/tests/Test/TestCurve.cs index 65ed754..dd63f0c 100644 --- a/tests/Test/TestCurve.cs +++ b/tests/Test/TestCurve.cs @@ -77,7 +77,7 @@ public void TestToPo() return; var tt = CurveEx.Topo(ents.ToList()); using var tr = new DBTrans(); - tt.ForEach((i,t) => t.ForWrite(e => e.ColorIndex = i)); + tt.ForEach((i, t) => t.ForWrite(e => e.ColorIndex = i)); tr.CurrentSpace.AddEntity(tt); } @@ -87,20 +87,30 @@ public void TestGetEdgesAndnewCurves() var curves = Env.Editor.SSGet().Value?.GetEntities().ToList(); if (curves == null) return; - var edges = new List(); - var closedCurve3d = new List(); + using var tr = new DBTrans(); + + var edgess = new List>(); + var closedCurve3ds = new List>(); + var topo = new Topo(curves); - topo.GetEdgesAndnewCurves(edges, closedCurve3d); - topo.AdjacencyList(edges, closedCurve3d);//增加测试 - using var tr = new DBTrans(); + topo.CollisionFor(infos => { + var gs = new List(); + var c3 = new List(); - for (int i = 0; i < edges.Count; i++) - { - var ent = edges[i].GeCurve3d.ToCurve(); - ent.ColorIndex = i; - tr.CurrentSpace.AddEntity(ent); - } + topo.GetEdgesAndnewCurves(infos, gs, c3); + topo.AdjacencyList(gs, c3);//增加测试..需要加入四叉树 + + for (int i = 0; i < gs.Count; i++) + { + var ent = gs[i].GeCurve3d.ToCurve(); + ent.ColorIndex = i; + tr.CurrentSpace.AddEntity(ent); + } + + edgess.Add(gs); + closedCurve3ds.Add(c3); + }); //Env.Print(""); } -- Gitee From 55c7d3d87ad89d9908fe75675bc913dcf8ba39a3 Mon Sep 17 00:00:00 2001 From: vicwjb <403009819@qq.com> Date: Thu, 7 Apr 2022 00:18:14 +0800 Subject: [PATCH 093/675] =?UTF-8?q?=E6=95=B4=E7=90=86=E5=8F=8A=E6=B3=A8?= =?UTF-8?q?=E9=87=8A=E4=BB=A3=E7=A0=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/IFoxCAD.Cad/ExtensionMethod/Topo.cs | 41 ++++++------------------- src/IFoxCAD.Cad/GlobalUsings.cs | 2 +- src/IFoxCAD.Cad/QuadTree/Rect.cs | 21 +++++-------- 3 files changed, 19 insertions(+), 45 deletions(-) diff --git a/src/IFoxCAD.Cad/ExtensionMethod/Topo.cs b/src/IFoxCAD.Cad/ExtensionMethod/Topo.cs index b345570..bf608cc 100644 --- a/src/IFoxCAD.Cad/ExtensionMethod/Topo.cs +++ b/src/IFoxCAD.Cad/ExtensionMethod/Topo.cs @@ -1,8 +1,8 @@ namespace IFoxCAD.Cad; -using IFoxCAD.Basal; - - +/// +/// 边 +/// public class Edge : IEquatable { #region 字段 @@ -162,7 +162,7 @@ public static void Distinct(List edgesOut) //Edge没有包围盒,无法快速判断 //曲线a和其他曲线n根据交点切割子线,会造成重复部分,例如多段线逆向相同 - ArrayEx.Deduplication(edgesOut, (first, last) => { + Basal.ArrayEx.Deduplication(edgesOut, (first, last) => { //顺序 || 逆序 if ((first.GeCurve3d.StartPoint.IsEqualTo(last.GeCurve3d.StartPoint, CadTolerance) && first.GeCurve3d.EndPoint.IsEqualTo(last.GeCurve3d.EndPoint, CadTolerance)) @@ -175,30 +175,6 @@ public static void Distinct(List edgesOut) return false; }); } - - - // private class EdgeComparer : IEqualityComparer - // { - // public bool Equals(Edge x, Edge y) - // { - //#if ac2009 - // var pts = x.Curve.GetSamplePoints(100); - // return pts.All(pt => y.Curve.IsOn(pt)); - //#elif ac2013 || ac2015 - // var pts = x.Curve.GetSamplePoints(100); - // return pts.All(pt => y.Curve.IsOn(pt.Point)); - //#endif - // //return x.Curve.IsEqualTo(y.Curve); - // } - - // // If Equals() returns true for a pair of objects - // // then GetHashCode() must return the same value for these objects. - // public int GetHashCode(Edge product) - // { - // return product.Curve.GetHashCode(); - // } - // } - public override int GetHashCode() { return base.GetHashCode(); @@ -206,7 +182,9 @@ public override int GetHashCode() #endregion } - +/// +/// 边节点 +/// public class EdgeItem : Edge, IEquatable { #region 字段 @@ -380,7 +358,9 @@ public override int GetHashCode() #endregion } - +/// +/// 曲线信息 +/// public class CurveInfo : Rect { /// @@ -451,7 +431,6 @@ public CollisionChain() } } - public class Topo { /// diff --git a/src/IFoxCAD.Cad/GlobalUsings.cs b/src/IFoxCAD.Cad/GlobalUsings.cs index a29b199..4ca7903 100644 --- a/src/IFoxCAD.Cad/GlobalUsings.cs +++ b/src/IFoxCAD.Cad/GlobalUsings.cs @@ -9,7 +9,7 @@ global using System.Text.RegularExpressions; global using Microsoft.Win32; global using System.ComponentModel; - +global using System.Runtime.InteropServices; /// autocad 引用 global using Autodesk.AutoCAD.ApplicationServices; diff --git a/src/IFoxCAD.Cad/QuadTree/Rect.cs b/src/IFoxCAD.Cad/QuadTree/Rect.cs index c02fdc6..820a8bc 100644 --- a/src/IFoxCAD.Cad/QuadTree/Rect.cs +++ b/src/IFoxCAD.Cad/QuadTree/Rect.cs @@ -1,9 +1,5 @@ namespace IFoxCAD.Cad; -using IFoxCAD.Basal; -using System.Runtime.InteropServices; - - /// /// Linq Distinct 消重比较两点在容差范围内就去除 /// @@ -17,21 +13,20 @@ public TolerancePoint2d(double tolerance = 1e-6) public bool Equals(Point2d a, Point2d b)//Point3d是struct不会为null { - if (b == null) - return a == null; - else if (a == null) - return false; -#if true // 方形限定 // 在 0~1e-6 范围实现 圆形限定 则计算部分在浮点数6位后,没有啥意义 // 在 0~1e-6 范围实现 从时间和CPU消耗来说,圆形限定 都没有 方形限定 的好 - return a.IsEqualTo(b, new Tolerance(_tolerance, _tolerance)); -#else + if (_tolerance <= 1e-6) + { + return Math.Abs(a.X - b.X) <= _tolerance && Math.Abs(a.Y - b.Y) <= _tolerance; + } + else + { // 圆形限定 // DistanceTo 分别对XYZ进行了一次乘法,也是总数3次乘法,然后求了一次平方根 // (X86.CPU.FSQRT指令用的牛顿迭代法/软件层面可以使用快速平方根....我还以为CPU会采取快速平方根这样的取表操作) - return a.DistanceTo(b) <= _tolerance; -#endif + return a.IsEqualTo(b, new Tolerance(_tolerance, _tolerance)); + } } public int GetHashCode(Point2d obj) -- Gitee From 6a810ea04faa593b8045d28684013ed0e96d1e1a Mon Sep 17 00:00:00 2001 From: liuqihong <540762622@qq.com> Date: Thu, 7 Apr 2022 01:23:06 +0800 Subject: [PATCH 094/675] =?UTF-8?q?=E7=A2=B0=E6=92=9E=E7=AE=97=E6=B3=95?= =?UTF-8?q?=E4=BF=AE=E6=94=B9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/IFoxCAD.Cad/ExtensionMethod/Topo.cs | 29 ++++++++++++------------- 1 file changed, 14 insertions(+), 15 deletions(-) diff --git a/src/IFoxCAD.Cad/ExtensionMethod/Topo.cs b/src/IFoxCAD.Cad/ExtensionMethod/Topo.cs index bf608cc..aa6a723 100644 --- a/src/IFoxCAD.Cad/ExtensionMethod/Topo.cs +++ b/src/IFoxCAD.Cad/ExtensionMethod/Topo.cs @@ -436,7 +436,7 @@ public class Topo /// /// 用于切割的曲线集 /// - List _cuc; + List _coc; /// /// 求交类(每次set自动重置,都会有个新的结果) @@ -452,7 +452,7 @@ public Topo(List curves) for (int i = 0; i < curves.Count; i++) curveList.Add(new CurveInfo(curves[i])); - _cuc = XCollision(curveList); + _coc = XCollision(curveList); } /// @@ -467,14 +467,13 @@ List XCollision(List curves) curves = curves.OrderBy(a => a._X).ToList(); List collision = new(); - CollisionChain? cuc = null; + CollisionChain? coc = null; //遍历所有图元 for (int i = 0; i < curves.Count; i++) { var one = curves[i]; - if (one.CollisionChain != null)//有碰撞链直接跳过 - continue; + coc = one.CollisionChain;//有碰撞链就直接利用之前的链 //搜索范围要在 one 的头尾中间的部分 for (int j = i + 1; j < curves.Count; j++) @@ -487,14 +486,14 @@ List XCollision(List curves) if ((one._Top >= two._Top && two._Top >= one._Y) || (one._Top >= two._Y && two._Y >= one._Y)) { - if (cuc == null) + if (coc == null) { - cuc = new(); - one.CollisionChain = cuc;//碰撞链设置 - cuc.Collision.Add(one);//本体也在链条内 + coc = new(); + one.CollisionChain = coc;//碰撞链设置 + coc.Collision.Add(one);//本体也加入链 } - two.CollisionChain = cuc;//碰撞链设置 - cuc.Collision.Add(two); + two.CollisionChain = coc;//碰撞链设置 + coc.Collision.Add(two); } //这里想中断y高过它的无意义比较, //但是必须排序Y,而排序Y必须同X,而这里不是同X(而是同X区间),所以不能中断 @@ -504,10 +503,10 @@ List XCollision(List curves) else break;//因为已经排序了,后续的必然超过 x碰撞区间 } - if (cuc != null) + if (coc != null && !collision.Contains(coc)) { - collision.Add(cuc); - cuc = null; + collision.Add(coc); + coc = null; } } return collision; @@ -519,7 +518,7 @@ List XCollision(List curves) /// public void CollisionFor(Action> action) { - _cuc.ForEach(a => { + _coc.ForEach(a => { action(a.Collision);//输出每条碰撞链 }); } -- Gitee From 13874e8d9a384c78fdff8e7ab356dfd7f5674d39 Mon Sep 17 00:00:00 2001 From: liuqihong <540762622@qq.com> Date: Thu, 7 Apr 2022 03:27:52 +0800 Subject: [PATCH 095/675] =?UTF-8?q?=E9=93=BE=E8=A1=A8=E7=A7=BB=E9=99=A4?= =?UTF-8?q?=E5=A4=B4=E5=85=83=E7=B4=A0=E5=B0=91=E4=BA=86=E5=87=8F=E6=B3=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/IFoxCAD.Basal/LoopList.cs | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/src/IFoxCAD.Basal/LoopList.cs b/src/IFoxCAD.Basal/LoopList.cs index 2db7af3..35f21d0 100644 --- a/src/IFoxCAD.Basal/LoopList.cs +++ b/src/IFoxCAD.Basal/LoopList.cs @@ -204,8 +204,7 @@ public bool Contains(LoopListNode node) public bool Contains(T value) { bool result = false; - ForEach(node => - { + ForEach(node => { if (node.Value!.Equals(value)) { result = true; @@ -303,8 +302,7 @@ public bool Contains(T value) public LoopListNode? GetNode(Func func) { LoopListNode? result = null; - ForEach(node => - { + ForEach(node => { if (func(node.Value)) { result = node; @@ -454,6 +452,7 @@ public bool RemoveFirst() last.Next = First; break; } + Count--; return true; } @@ -521,16 +520,15 @@ void InternalRemove(LoopListNode node) { if (Count == 1 || node == First) { - RemoveFirst(); + RemoveFirst();//此处会减数字 } else { node.Next!.Previous = node.Previous; node.Previous!.Next = node.Next; + Count--; } - node.Invalidate(); - Count--; } #endregion -- Gitee From 4c6c7daeeab3147118635bac7f41f355687d9bb5 Mon Sep 17 00:00:00 2001 From: liuqihong <540762622@qq.com> Date: Thu, 7 Apr 2022 03:28:16 +0800 Subject: [PATCH 096/675] =?UTF-8?q?=E4=BF=AE=E6=94=B9=E5=A5=BD=E4=BA=86?= =?UTF-8?q?=E8=85=B0=E8=BA=AB=E9=97=AD=E7=8E=AF=E6=AD=BB=E5=BE=AA=E7=8E=AF?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/IFoxCAD.Cad/ExtensionMethod/CurveEx.cs | 38 ++--- src/IFoxCAD.Cad/ExtensionMethod/Topo.cs | 177 +++++++++++---------- tests/Test/TestCurve.cs | 21 ++- 3 files changed, 122 insertions(+), 114 deletions(-) diff --git a/src/IFoxCAD.Cad/ExtensionMethod/CurveEx.cs b/src/IFoxCAD.Cad/ExtensionMethod/CurveEx.cs index 25c4c8b..ed4d98a 100644 --- a/src/IFoxCAD.Cad/ExtensionMethod/CurveEx.cs +++ b/src/IFoxCAD.Cad/ExtensionMethod/CurveEx.cs @@ -51,29 +51,29 @@ public static IEnumerable GetSplitCurves(this Curve curve, IEnumerable闭合的曲线集 public static IEnumerable? Topo(List curves) { - ////闭合的曲线集合 - //var closedCurve3d = new List(); - //var edges = new List(); + //闭合的曲线集合 + var closedCurve3d = new List(); - //var topo = new Topo(curves); - //topo.GetEdgesAndnewCurves(edges,closedCurve3d); - //topo.AdjacencyList(edges, closedCurve3d); + var topo = new Topo(curves); + topo.CollisionFor(infos => { + var gs = new List(); + var c3 = new List(); - //var regions = topo.GetRegions(edges); - //topo.RegionsInfo(regions); + topo.GetEdgesAndnewCurves(infos, gs, c3); + topo.AdjacencyList(gs, c3);//增加测试..需要加入四叉树 + var regions = topo.GetRegions(gs); - //for (int i = 0; i < regions.Count; i++) - //{ - // var cs3ds = regions[i] - // .Select(e => e.GetCurve()) - // .ToArray(); - // closedCurve3d.Add(new CompositeCurve3d(cs3ds)); - //} - - ////因为生成可能导致遗忘释放,所以这里统一生成 - //return closedCurve3d.Select(e => e.ToCurve()!); + for (int i = 0; i < regions.Count; i++) + { + var cs3ds = regions[i] + .Select(e => e.GetCurve()) + .ToArray(); + closedCurve3d.Add(new CompositeCurve3d(cs3ds)); + } + }); - return null; + //因为生成可能导致遗忘释放,所以这里统一生成 + return closedCurve3d.Select(e => e.ToCurve()!); } /// diff --git a/src/IFoxCAD.Cad/ExtensionMethod/Topo.cs b/src/IFoxCAD.Cad/ExtensionMethod/Topo.cs index aa6a723..320186e 100644 --- a/src/IFoxCAD.Cad/ExtensionMethod/Topo.cs +++ b/src/IFoxCAD.Cad/ExtensionMethod/Topo.cs @@ -189,7 +189,7 @@ public class EdgeItem : Edge, IEquatable { #region 字段 /// - /// 用来判断搜索方向(向前还是向后) + /// 搜索方向标志,为向前搜索,为向后搜索 /// public bool Forward; #endregion @@ -223,26 +223,26 @@ public EdgeItem(Edge edge, bool forward) : base(edge) /// /// 查找面域 /// - /// - /// - public void FindRegion(List edges, List> regions) + /// 用来查询位置的 + /// 返回的面域 + public void FindRegion(List edges, List> regions_out) { - var region = new LoopList(); + var result = new LoopList();//新的面域 var edgeItem = this; - region.Add(edgeItem); - var edgeItem2 = this.GetNext(edges); - if (edgeItem2 is null) + result.Add(edgeItem); + var getEdgeItem = this.GetNext(edges); + if (getEdgeItem is null) return; bool hasList = false; - for (int i = 0; i < regions.Count; i++) + for (int i = 0; i < regions_out.Count; i++) { - var edgeList2 = regions[i]; + var edgeList2 = regions_out[i]; var node = edgeList2.GetNode(e => e.Equals(edgeItem)); if (node is not null && node != edgeList2.Last) { - if (node.Next!.Value.Equals(edgeItem2)) + if (node.Next!.Value.Equals(getEdgeItem)) { hasList = true; break; @@ -251,15 +251,21 @@ public void FindRegion(List edges, List> regions) } if (!hasList) { - while (edgeItem2 is not null) + //之前这里有个死循环,造成的原因是8字走b了(腰身闭环),然后下面闭环永远找不到开头 + while (getEdgeItem is not null) { - if (edgeItem2 == edgeItem) + if (result.Contains(getEdgeItem)) + { + //腰身闭环,从前头开始剔除到这个重复节点 + while (result.First?.Value != getEdgeItem) + result.RemoveFirst(); break; - region.Add(edgeItem2); //TODO 此处死循环,上一条语句判断失误,导致不停的将相同的值加入region - edgeItem2 = edgeItem2.GetNext(edges); + } + result.Add(getEdgeItem); + getEdgeItem = getEdgeItem.GetNext(edges); } - if (edgeItem2 == edgeItem) - regions.Add(region); + if (getEdgeItem == edgeItem)//TODO 这里必须存在可以环形排序方式,令它不会重复加入一样的链条. + regions_out.Add(result); } } @@ -364,11 +370,11 @@ public override int GetHashCode() public class CurveInfo : Rect { /// - /// 曲线图元(母线) + /// 曲线图元 /// public Curve Curve; /// - /// 曲线参数 + /// 曲线分割点的参数 /// public List Paramss; /// @@ -433,17 +439,14 @@ public CollisionChain() public class Topo { - /// - /// 用于切割的曲线集 - /// + // 碰撞链集合 List _coc; - - /// - /// 求交类(每次set自动重置,都会有个新的结果) - /// + // 求交类(每次set自动重置,都会有个新的结果) static CurveCurveIntersector3d _cci3d = new(); + // cad容差类 public static Tolerance CadTolerance = new(1e-6, 1e-6); + public Topo(List curves) { List curveList = new(); @@ -483,8 +486,10 @@ List XCollision(List curves) if (one._X <= two._X && two._X <= one._Right) { //y碰撞,那就是真的碰撞了 - if ((one._Top >= two._Top && two._Top >= one._Y) || - (one._Top >= two._Y && two._Y >= one._Y)) + if ((one._Top >= two._Top && two._Top >= one._Y) /*包容上边*/ + || (one._Top >= two._Y && two._Y >= one._Y) /*包容下边*/ + || (two._Top >= one._Top && one._Y >= two._Y)) /*穿过*/ + { if (coc == null) { @@ -526,21 +531,21 @@ public void CollisionFor(Action> action) /// /// 利用交点断分曲线和独立自闭曲线 /// - /// 边界(可能仍然存在自闭,因为样条曲线允许打个鱼形圈,尾巴又交叉在其他曲线) - /// 自闭的曲线 - public void GetEdgesAndnewCurves(List infos, List edgesOut, List closedCurvesOut) + /// 边界(可能仍然存在自闭,因为样条曲线允许打个鱼形圈,尾巴又交叉在其他曲线) + /// 自闭的曲线 + public void GetEdgesAndnewCurves(List infos_In, List edges_Out, List closedCurves_Out) { //此处是O(n²) //曲线a和其他曲线n根据 交点 切割子线(第一次是自交对比) - for (int a = 0; a < infos.Count; a++) + for (int a = 0; a < infos_In.Count; a++) { - var curve1 = infos[a]; + var curve1 = infos_In[a]; var gc1 = curve1.Edge.GeCurve3d; var pars1 = curve1.Paramss; - for (int n = a; n < infos.Count; n++) + for (int n = a; n < infos_In.Count; n++) { - var curve2 = infos[n]; + var curve2 = infos_In[n]; //包围盒没有碰撞就直接结束 if (!curve1.IntersectsWith(curve2)) @@ -561,7 +566,7 @@ public void GetEdgesAndnewCurves(List infos, List edgesOut, Lis } if (gc1.IsClosed()) - closedCurvesOut.Add(gc1); + closedCurves_Out.Add(gc1); if (pars1.Count == 0) continue; @@ -571,41 +576,40 @@ public void GetEdgesAndnewCurves(List infos, List edgesOut, Lis var c3ds = curve1.Split(pars1); if (c3ds.Count > 0) { - edgesOut.AddRange(c3ds); + edges_Out.AddRange(c3ds); } else { //惊惊留:(不敢删啊...) //狐哥写的这里出现的条件是:有曲线参数,但是切分不出来曲线...没懂为什么... //是这些参数?{参数0位置?头参/尾参/参数不在曲线上?} - edgesOut.Add(curve1.Edge); + edges_Out.Add(curve1.Edge); } //有交点的才消重,无交点必然不重复. - Edge.Distinct(edgesOut); + Edge.Distinct(edges_Out); } } /// /// 创建邻接表 /// - /// 传入传出,扔掉单交点图元剩下闭合图元集 - /// - public void AdjacencyList(List edges, List closedCurve3dOut) + /// 传入传出,扔掉单交点图元剩下闭合图元集 + /// + public void AdjacencyList(List edges_InOut, List closedCurve3d_Out) { //构建边的邻接表 - //knots 和 nums 实际上是一个键值对(基于ArrayOfStruct思想,拆开更合适内存布局) - //knots 是不重复地将所有交点设置为节点,如果是重复,就对应 nums++ - //nums 是记录每个节点坐标被重复了几次 + //下面是键值对(基于ArrayOfStruct思想,拆开更合适内存布局) + //knots 是不重复地将所有交点设置为节点(如果是重复就对应 nums++) + //nums 是记录每个交点被重复了几次 var knots = new List(); var nums = new List(); var closedEdges = new List(); - //一次遍历就完成了,所以不需要四叉树, - //但是不需要时,交点集合 knots 会不断增大,会变得更加慢 - for (int i = 0; i < edges.Count; i++) + //交点集合 knots 会不断增大,会变得更加慢,因此我在一开始就进行碰撞链分析 + for (int i = 0; i < edges_InOut.Count; i++) { - var edge = edges[i]; + var edge = edges_InOut[i]; if (edge.GeCurve3d.IsClosed()) { closedEdges.Add(edge); @@ -640,80 +644,79 @@ public void AdjacencyList(List edges, List closedCurve3d } } - closedCurve3dOut.AddRange(closedEdges.Select(e => e.GeCurve3d)); + closedCurve3d_Out.AddRange(closedEdges.Select(e => e.GeCurve3d)); //这里把交点只有一条曲线通过的点过滤掉了,也就是尾巴的图元, //剩下的都是闭合的曲线连接了,每个点都至少有两条曲线通过 - var tmp = edges + var tmp = edges_InOut .Except(closedEdges) .Where(e => nums[e.StartIndex] > 1 && nums[e.EndIndex] > 1) .ToArray();//要ToArray克隆,否则下面会清空掉这个容器 - edges.Clear(); + edges_InOut.Clear(); for (int i = 0; i < tmp.Length; i++) - edges.Add(tmp[i]); + edges_InOut.Add(tmp[i]); } /// /// 获取多个面域 /// - /// + /// 剩下都有两个交点的线 /// public List> GetRegions(List edges) { + var regions_out = new List>(); + //利用边界的顺序和逆序获取闭合链条 - var regions = new List>(); for (int i = 0; i < edges.Count; i++) { var edge = edges[i]; //TODO 这里有bug,两个内接的矩形会卡死 FindRegion var edgeItem = new EdgeItem(edge, true); - edgeItem.FindRegion(edges, regions); + edgeItem.FindRegion(edges, regions_out); edgeItem = new EdgeItem(edge, false); - edgeItem.FindRegion(edges, regions); + edgeItem.FindRegion(edges, regions_out); } - return regions; - } + DeduplicationRegions(regions_out); + return regions_out; + } + /// - /// 广度优先算法? + /// 移除相同面域 /// /// - public void RegionsInfo(List> regions) + void DeduplicationRegions(List> regions) { - for (int i = 0; i < regions.Count; i++) - { - for (int j = i + 1; j < regions.Count;) + Basal.ArrayEx.Deduplication(regions,(first, last) => { + bool eq = false;//是否存在相同成员 + if (first.Count == last.Count) { - bool eq = false; - if (regions[i].Count == regions[j].Count) + var node1 = first.First; + var curve1 = node1!.Value.GeCurve3d; + + //两个面域对比,找到相同成员 + var node2 = last.GetNode(e => e.GeCurve3d == curve1); + if (node2 is not null) { - var node = regions[i].First; - var curve = node!.Value.GeCurve3d; - var node2 = regions[j].GetNode(e => e.GeCurve3d == curve); - //var node2 = regions[j].Find(node.Value); - if (node2 is not null) + eq = true; + var f1 = node1.Value.Forward; + var f2 = node2.Value.Forward; + //链条搜索方向来进行 + //判断每个节点的成员如果一致就会执行移除 + for (int k = 1; k < first.Count; k++) { - eq = true; - var b = node.Value.Forward; - var b2 = node2.Value.Forward; - for (int k = 1; k < regions[i].Count; k++) + node1 = node1.GetNext(f1); + node2 = node2.GetNext(f2); + if (node1!.Value.GeCurve3d != node2!.Value.GeCurve3d) { - node = node.GetNext(b); - node2 = node2.GetNext(b2); - if (node!.Value.GeCurve3d != node2!.Value.GeCurve3d) - { - eq = false; - break; - } + eq = false; + break; } } } - if (eq) - regions.RemoveAt(j); - else - j++; } - } + return eq; + }); } } \ No newline at end of file diff --git a/tests/Test/TestCurve.cs b/tests/Test/TestCurve.cs index dd63f0c..35d261d 100644 --- a/tests/Test/TestCurve.cs +++ b/tests/Test/TestCurve.cs @@ -75,7 +75,9 @@ public void TestToPo() var ents = Env.Editor.SSGet().Value?.GetEntities(); if (ents == null) return; + var tt = CurveEx.Topo(ents.ToList()); + using var tr = new DBTrans(); tt.ForEach((i, t) => t.ForWrite(e => e.ColorIndex = i)); tr.CurrentSpace.AddEntity(tt); @@ -89,8 +91,8 @@ public void TestGetEdgesAndnewCurves() return; using var tr = new DBTrans(); - var edgess = new List>(); - var closedCurve3ds = new List>(); + var edgesGroup = new List>(); + var closedCurve3dGroup = new List>(); var topo = new Topo(curves); @@ -101,16 +103,19 @@ public void TestGetEdgesAndnewCurves() topo.GetEdgesAndnewCurves(infos, gs, c3); topo.AdjacencyList(gs, c3);//增加测试..需要加入四叉树 - for (int i = 0; i < gs.Count; i++) + edgesGroup.Add(gs); + closedCurve3dGroup.Add(c3); + }); + + foreach (var edge in edgesGroup) + { + for (int i = 0; i < edge.Count; i++) { - var ent = gs[i].GeCurve3d.ToCurve(); + var ent = edge[i].GeCurve3d.ToCurve(); ent.ColorIndex = i; tr.CurrentSpace.AddEntity(ent); } - - edgess.Add(gs); - closedCurve3ds.Add(c3); - }); + } //Env.Print(""); } -- Gitee From 16e00953f2ba29a1edb54e30205e04cd9f956bda Mon Sep 17 00:00:00 2001 From: liuqihong <540762622@qq.com> Date: Thu, 7 Apr 2022 11:10:27 +0800 Subject: [PATCH 097/675] =?UTF-8?q?=E4=BF=AE=E6=94=B9=E6=B3=A8=E9=87=8A?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/IFoxCAD.Cad/ExtensionMethod/Topo.cs | 22 +++++++++++++--------- 1 file changed, 13 insertions(+), 9 deletions(-) diff --git a/src/IFoxCAD.Cad/ExtensionMethod/Topo.cs b/src/IFoxCAD.Cad/ExtensionMethod/Topo.cs index 320186e..20ff04a 100644 --- a/src/IFoxCAD.Cad/ExtensionMethod/Topo.cs +++ b/src/IFoxCAD.Cad/ExtensionMethod/Topo.cs @@ -489,7 +489,6 @@ List XCollision(List curves) if ((one._Top >= two._Top && two._Top >= one._Y) /*包容上边*/ || (one._Top >= two._Y && two._Y >= one._Y) /*包容下边*/ || (two._Top >= one._Top && one._Y >= two._Y)) /*穿过*/ - { if (coc == null) { @@ -531,8 +530,9 @@ public void CollisionFor(Action> action) /// /// 利用交点断分曲线和独立自闭曲线 /// - /// 边界(可能仍然存在自闭,因为样条曲线允许打个鱼形圈,尾巴又交叉在其他曲线) - /// 自闭的曲线 + /// 传入每组有碰撞的 + /// 传出不自闭的曲线集 + /// 传出自闭曲线集 public void GetEdgesAndnewCurves(List infos_In, List edges_Out, List closedCurves_Out) { //此处是O(n²) @@ -594,8 +594,8 @@ public void GetEdgesAndnewCurves(List infos_In, List edges_Out, /// /// 创建邻接表 /// - /// 传入传出,扔掉单交点图元剩下闭合图元集 - /// + /// 传入每组有碰撞的;传出闭合边界集(扔掉单交点的) + /// 传出自闭曲线集 public void AdjacencyList(List edges_InOut, List closedCurve3d_Out) { //构建边的邻接表 @@ -667,11 +667,15 @@ public List> GetRegions(List edges) { var regions_out = new List>(); - //利用边界的顺序和逆序获取闭合链条 + /* + * TODO 这里暴力算法需要优化 + * 狐哥为了处理左拐还是右拐图形bd,用了所有的线做种子线,然后往前后推进 + * 前后推进:利用边界的顺序和逆序获取闭合链条, + * 会造成一环就有: 123,231,312,321,213,132,再其后再判断他们重复 + */ for (int i = 0; i < edges.Count; i++) { var edge = edges[i]; - //TODO 这里有bug,两个内接的矩形会卡死 FindRegion var edgeItem = new EdgeItem(edge, true); edgeItem.FindRegion(edges, regions_out); edgeItem = new EdgeItem(edge, false); @@ -681,14 +685,14 @@ public List> GetRegions(List edges) DeduplicationRegions(regions_out); return regions_out; } - + /// /// 移除相同面域 /// /// void DeduplicationRegions(List> regions) { - Basal.ArrayEx.Deduplication(regions,(first, last) => { + Basal.ArrayEx.Deduplication(regions, (first, last) => { bool eq = false;//是否存在相同成员 if (first.Count == last.Count) { -- Gitee From 0e5fcafa00fa11337499eb5f06d590ec0cc1ea22 Mon Sep 17 00:00:00 2001 From: vicwjb <403009819@qq.com> Date: Thu, 7 Apr 2022 23:47:37 +0800 Subject: [PATCH 098/675] =?UTF-8?q?=E6=B7=BB=E5=8A=A0GitHub=E4=B8=8A?= =?UTF-8?q?=E6=8A=84=E6=9D=A5=E7=9A=84=E5=9B=BE=E5=92=8Cdfs=E7=9A=84?= =?UTF-8?q?=E5=AE=9E=E7=8E=B0=EF=BC=8C=E7=94=A8=E4=BA=8E=E5=90=8E=E7=BB=AD?= =?UTF-8?q?=E6=94=B9=E9=80=A0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/IFoxCAD.Cad/QuadTree/Graph.cs | 634 +++++++++++++++++++++++++++++ src/IFoxCAD.Cad/QuadTree/IGraph.cs | 121 ++++++ 2 files changed, 755 insertions(+) create mode 100644 src/IFoxCAD.Cad/QuadTree/Graph.cs create mode 100644 src/IFoxCAD.Cad/QuadTree/IGraph.cs diff --git a/src/IFoxCAD.Cad/QuadTree/Graph.cs b/src/IFoxCAD.Cad/QuadTree/Graph.cs new file mode 100644 index 0000000..43f29bb --- /dev/null +++ b/src/IFoxCAD.Cad/QuadTree/Graph.cs @@ -0,0 +1,634 @@ +using Exception = System.Exception; + +namespace IFoxCAD.Cad +{ + /// + /// 无权无向图实现 + /// IEnumerable 枚举所有顶点。 + /// + public class Graph : IGraph, IEnumerable + { + /// + /// 存储所有节点的字典,key为节点的类型,value为节点类型 + /// + /// + private Dictionary> vertices { get; set; } + + public int VerticesCount => vertices.Count; + public bool IsWeightedGraph => false; + + public Graph() + { + vertices = new Dictionary>(); + } + + /// + /// 返回一个参考顶点。 + /// 时间复杂度: O(1). + /// + private GraphVertex referenceVertex + { + get + { + using (var enumerator = vertices.GetEnumerator()) + { + if (enumerator.MoveNext()) + { + return enumerator.Current.Value; + } + } + + return null; + } + } + + IGraphVertex IGraph.ReferenceVertex => referenceVertex; + + + /// + /// 向该图添加一个新顶点。 + /// 时间复杂度: O(1). + /// + public void AddVertex(T value) + { + if (value == null) + { + throw new ArgumentNullException(); + } + + var newVertex = new GraphVertex(value); + + vertices.Add(value, newVertex); + } + + /// + /// 从此图中删除现有顶点。 + /// 时间复杂度: O(V) 其中 V 是顶点数。 + /// + public void RemoveVertex(T vertex) + { + if (vertex == null) + { + throw new ArgumentNullException(); + } + + if (!vertices.ContainsKey(vertex)) + { + throw new Exception("顶点不在此图中。"); + } + + foreach (var v in vertices[vertex].Edges) + { + v.Edges.Remove(vertices[vertex]); + } + + vertices.Remove(vertex); + } + + /// + /// 向该图添加一条边。 + /// 时间复杂度: O(1). + /// + public void AddEdge(T source, T dest) + { + if (source == null || dest == null) + { + throw new ArgumentException(); + } + + if (!vertices.ContainsKey(source) || !vertices.ContainsKey(dest)) + { + throw new Exception("源或目标顶点不在此图中。"); + } + + if (vertices[source].Edges.Contains(vertices[dest]) + || vertices[dest].Edges.Contains(vertices[source])) + { + throw new Exception("边已经存在。"); + } + + vertices[source].Edges.Add(vertices[dest]); + vertices[dest].Edges.Add(vertices[source]); + } + + /// + /// 从此图中删除一条边。 + /// 时间复杂度: O(1). + /// + public void RemoveEdge(T source, T dest) + { + + if (source == null || dest == null) + { + throw new ArgumentException(); + } + + if (!vertices.ContainsKey(source) || !vertices.ContainsKey(dest)) + { + throw new Exception("源或目标顶点不在此图中。"); + } + + if (!vertices[source].Edges.Contains(vertices[dest]) + || !vertices[dest].Edges.Contains(vertices[source])) + { + throw new Exception("边不存在。"); + } + + vertices[source].Edges.Remove(vertices[dest]); + vertices[dest].Edges.Remove(vertices[source]); + } + + /// + /// 我们在给定的来源和目的地之间是否有边? + /// 时间复杂度: O(1). + /// + public bool HasEdge(T source, T dest) + { + if (!vertices.ContainsKey(source) || !vertices.ContainsKey(dest)) + { + throw new ArgumentException("源或目标不在此图中。"); + } + + return vertices[source].Edges.Contains(vertices[dest]) + && vertices[dest].Edges.Contains(vertices[source]); + } + /// + /// 节点的邻接表 + /// + /// + /// + public IEnumerable Edges(T vertex) + { + if (!vertices.ContainsKey(vertex)) + { + throw new ArgumentException("顶点不在此图中。"); + } + + return vertices[vertex].Edges.Select(x => x.Key); + } + + public bool ContainsVertex(T value) + { + return vertices.ContainsKey(value); + } + + public IGraphVertex GetVertex(T value) + { + return vertices[value]; + } + + /// + /// 克隆此图。目测是深克隆 + /// + public Graph Clone() + { + var newGraph = new Graph(); + + foreach (var vertex in vertices) + { + newGraph.AddVertex(vertex.Key); + } + + foreach (var vertex in vertices) + { + foreach (var edge in vertex.Value.Edges) + { + newGraph.AddEdge(vertex.Value.Key, edge.Key); + } + } + + return newGraph; + } + + public IEnumerator GetEnumerator() + { + return vertices.Select(x => x.Key).GetEnumerator(); + } + + IEnumerator IEnumerable.GetEnumerator() + { + return GetEnumerator() as IEnumerator; + } + + IGraph IGraph.Clone() + { + return Clone(); + } + + public IEnumerable> VerticesAsEnumberable => (IEnumerable>)vertices.Select(x => x.Value); + + /// + /// 邻接表图实现的顶点。 + /// IEnumerable 枚举所有出边目标顶点。 + /// + private class GraphVertex : IEnumerable, IGraphVertex + { + public T Key { get; set; } + /// + /// 邻接表 + /// + /// + public HashSet> Edges { get; } + + IEnumerable> IGraphVertex.Edges => (IEnumerable>)Edges.Select(x => new Edge(x, 1)); + + public GraphVertex(T value) + { + Key = value; + Edges = new HashSet>(); + } + + public IEdge GetEdge(IGraphVertex targetVertex) + { + return new Edge(targetVertex, 1); + } + + IEnumerator IEnumerable.GetEnumerator() + { + return GetEnumerator(); + } + + public IEnumerator GetEnumerator() + { + return Edges.Select(x => x.Key).GetEnumerator(); + } + } + } + + /// + /// 深度优先搜索。 + /// + public class DepthFirst + { + /// + /// 如果项目存在,则返回 true。 + /// 这个函数需要改写,在ifoxcad里返回的应该是所有的环 + /// + public bool Find(IGraph graph, T vertex) + { + return dfs(graph.ReferenceVertex, new HashSet(), vertex); + } + + /// + /// 递归 DFS。 + /// 这个函数需要改写,在ifoxcad里返回的应该是所有的环 + /// + private bool dfs(IGraphVertex current, + HashSet visited, T searchVetex) + { + visited.Add(current.Key); + + if (current.Key.Equals(searchVetex)) + { + return true; + } + + foreach (var edge in current.Edges) + { + if (visited.Contains(edge.TargetVertexKey)) + { + continue; + } + + if (dfs(edge.TargetVertex, visited, searchVetex)) + { + return true; + } + } + + return false; + } + + + } + +// ===========另一个dfs实现,感觉比较好一点在于可以用委托干点其他的事情,和前一个可以结合一下 + + public interface IDirectedWeightedGraph + { + int Count { get; } + + Vertex?[] Vertices { get; } + + void AddEdge(Vertex startVertex, Vertex endVertex, double weight); + + Vertex AddVertex(T data); + + bool AreAdjacent(Vertex startVertex, Vertex endVertex); + + double AdjacentDistance(Vertex startVertex, Vertex endVertex); + + IEnumerable?> GetNeighbors(Vertex vertex); + + void RemoveEdge(Vertex startVertex, Vertex endVertex); + + void RemoveVertex(Vertex vertex); + } + public interface IGraphSearch + { + /// + /// 从起始顶点遍历图。 + /// + ///图实例。 + ///搜索开始的顶点。 + ///每个图顶点需要执行的动作 + void VisitAll(IDirectedWeightedGraph graph, Vertex startVertex, Action>? action = null); + } + + /// + /// Implementation of graph vertex. + /// + /// Generic Type. + public class Vertex + { + /// + /// Gets vertex data. + /// + public T Data { get; } + + /// + /// Gets an index of the vertex in graph adjacency matrix. + /// + public int Index { get; } + + /// + /// Gets reference to the graph this vertex belongs to. + /// + public DirectedWeightedGraph? Graph { get; private set; } + + /// + /// Initializes a new instance of the class. + /// + /// Vertex data. Generic type. + /// Index of the vertex in graph adjacency matrix. + /// Graph this vertex belongs to. + public Vertex(T data, int index, DirectedWeightedGraph? graph) + { + Data = data; + Index = index; + Graph = graph; + } + + /// + /// Initializes a new instance of the class. + /// + /// Vertex data. Generic type. + /// Index of the vertex in graph adjacency matrix. + public Vertex(T data, int index) + { + Data = data; + Index = index; + } + + /// + /// Sets graph reference to the null. This method called when vertex removed from the graph. + /// + public void SetGraphNull() => Graph = null; + + /// + /// Override of base ToString method. Prints vertex data and index in graph adjacency matrix. + /// + /// String which contains vertex data and index in graph adjacency matrix.. + public override string ToString() => $"Vertex Data: {Data}, Index: {Index}"; + } + + /// + /// Implementation of the directed weighted graph via adjacency matrix. + /// + /// Generic Type. + public class DirectedWeightedGraph : IDirectedWeightedGraph + { + /// + /// Capacity of the graph, indicates the maximum amount of vertices. + /// + private readonly int capacity; + + /// + /// Adjacency matrix which reflects the edges between vertices and their weight. + /// Zero value indicates no edge between two vertices. + /// + private readonly double[,] adjacencyMatrix; + + /// + /// Initializes a new instance of the class. + /// + /// Capacity of the graph, indicates the maximum amount of vertices. + public DirectedWeightedGraph(int capacity) + { + ThrowIfNegativeCapacity(capacity); + + this.capacity = capacity; + Vertices = new Vertex[capacity]; + adjacencyMatrix = new double[capacity, capacity]; + Count = 0; + } + + /// + /// Gets list of vertices of the graph. + /// + public Vertex?[] Vertices { get; private set; } + + /// + /// Gets current amount of vertices in the graph. + /// + public int Count { get; private set; } + + /// + /// Adds new vertex to the graph. + /// + /// Data of the vertex. + /// Reference to created vertex. + public Vertex AddVertex(T data) + { + ThrowIfOverflow(); + var vertex = new Vertex(data, Count, this); + Vertices[Count] = vertex; + Count++; + return vertex; + } + + /// + /// Creates an edge between two vertices of the graph. + /// + /// Vertex, edge starts at. + /// Vertex, edge ends at. + /// Double weight of an edge. + public void AddEdge(Vertex startVertex, Vertex endVertex, double weight) + { + ThrowIfVertexNotInGraph(startVertex); + ThrowIfVertexNotInGraph(endVertex); + + ThrowIfWeightZero(weight); + + var currentEdgeWeight = adjacencyMatrix[startVertex.Index, endVertex.Index]; + + ThrowIfEdgeExists(currentEdgeWeight); + + adjacencyMatrix[startVertex.Index, endVertex.Index] = weight; + } + + /// + /// Removes vertex from the graph. + /// + /// Vertex to be removed. + public void RemoveVertex(Vertex vertex) + { + ThrowIfVertexNotInGraph(vertex); + + Vertices[vertex.Index] = null; + vertex.SetGraphNull(); + + for (var i = 0; i < Count; i++) + { + adjacencyMatrix[i, vertex.Index] = 0; + adjacencyMatrix[vertex.Index, i] = 0; + } + + Count--; + } + + /// + /// Removes edge between two vertices. + /// + /// Vertex, edge starts at. + /// Vertex, edge ends at. + public void RemoveEdge(Vertex startVertex, Vertex endVertex) + { + ThrowIfVertexNotInGraph(startVertex); + ThrowIfVertexNotInGraph(endVertex); + adjacencyMatrix[startVertex.Index, endVertex.Index] = 0; + } + + /// + /// Gets a neighbors of particular vertex. + /// + /// Vertex, method gets list of neighbors for. + /// Collection of the neighbors of particular vertex. + public IEnumerable?> GetNeighbors(Vertex vertex) + { + ThrowIfVertexNotInGraph(vertex); + + for (var i = 0; i < Count; i++) + { + if (adjacencyMatrix[vertex.Index, i] != 0) + { + yield return Vertices[i]; + } + } + } + + /// + /// Returns true, if there is an edge between two vertices. + /// + /// Vertex, edge starts at. + /// Vertex, edge ends at. + /// True if edge exists, otherwise false. + public bool AreAdjacent(Vertex startVertex, Vertex endVertex) + { + ThrowIfVertexNotInGraph(startVertex); + ThrowIfVertexNotInGraph(endVertex); + + return adjacencyMatrix[startVertex.Index, endVertex.Index] != 0; + } + + /// + /// Return the distance between two vertices in the graph. + /// + /// first vertex in edge. + /// secnod vertex in edge. + /// distance between the two. + public double AdjacentDistance(Vertex startVertex, Vertex endVertex) + { + if (AreAdjacent(startVertex, endVertex)) + { + return adjacencyMatrix[startVertex.Index, endVertex.Index]; + } + + return 0; + } + + private static void ThrowIfNegativeCapacity(int capacity) + { + if (capacity < 0) + { + throw new InvalidOperationException("Graph capacity should always be a non-negative integer."); + } + } + + private static void ThrowIfWeightZero(double weight) + { + if (weight.Equals(0.0d)) + { + throw new InvalidOperationException("Edge weight cannot be zero."); + } + } + + private static void ThrowIfEdgeExists(double currentEdgeWeight) + { + if (!currentEdgeWeight.Equals(0.0d)) + { + throw new InvalidOperationException($"Vertex already exists: {currentEdgeWeight}"); + } + } + + private void ThrowIfOverflow() + { + if (Count == capacity) + { + throw new InvalidOperationException("Graph overflow."); + } + } + + private void ThrowIfVertexNotInGraph(Vertex vertex) + { + if (vertex.Graph != this) + { + throw new InvalidOperationException($"Vertex does not belong to graph: {vertex}."); + } + } + } + /// + /// 深度优先搜索 -遍历图的算法。 + /// 算法从用户选择的根节点开始。 + /// 算法在回溯之前尽可能沿着每个分支探索。 + /// + /// 顶点数据类型。 + public class DepthFirstSearch : IGraphSearch where T : IComparable + { + /// + /// 从起始顶点遍历图。 + /// + ///图实例。 + ///搜索开始的顶点。 + ///每个图顶点需要执行的动作 + public void VisitAll(IDirectedWeightedGraph graph, Vertex startVertex, Action>? action = default) + { + Dfs(graph, startVertex, action, new HashSet>()); + } + + /// + /// 从起始顶点遍历图。 + /// + ///图实例。 + ///搜索开始的顶点。 + ///每个图顶点需要执行的动作 + ///具有访问顶点的哈希集。 + private void Dfs(IDirectedWeightedGraph graph, Vertex startVertex, Action>? action, HashSet> visited) + { + action?.Invoke(startVertex); + + visited.Add(startVertex); + + foreach (var vertex in graph.GetNeighbors(startVertex)) + { + if (vertex == null || visited.Contains(vertex)) + { + continue; + } + + Dfs(graph, vertex!, action, visited); + } + } + } + +} diff --git a/src/IFoxCAD.Cad/QuadTree/IGraph.cs b/src/IFoxCAD.Cad/QuadTree/IGraph.cs new file mode 100644 index 0000000..905f614 --- /dev/null +++ b/src/IFoxCAD.Cad/QuadTree/IGraph.cs @@ -0,0 +1,121 @@ +namespace IFoxCAD.Cad +{ + /// + /// 无向图 + /// + /// 数据类型 + public interface IGraph + { + /// + /// 是否为有权图 + /// + /// + bool IsWeightedGraph { get; } + /// + /// 节点的数量 + /// + /// + int VerticesCount { get; } + /// + /// 搜索的起始节点 + /// + /// + IGraphVertex ReferenceVertex { get; } + /// + /// 是否存在节点 + /// + /// + /// + bool ContainsVertex(T key); + /// + /// 获取节点 + /// + /// + /// + IGraphVertex GetVertex(T key); + /// + /// 图节点的迭代器 + /// + /// + IEnumerable> VerticesAsEnumberable { get; } + /// + /// 是否有边 + /// + /// 源节点 + /// 目的节点 + /// + bool HasEdge(T source, T destination); + /// + /// 图克隆函数 + /// + /// + IGraph Clone(); + } + + /// + /// 无向图节点 + /// + /// 节点数据类型 + public interface IGraphVertex + { + /// + /// 节点的键 + /// + /// + T Key { get; } + /// + /// 节点的邻接表 + /// + /// + IEnumerable> Edges { get; } + /// + /// 获取节点的邻接边 + /// + /// 目标节点 + /// + IEdge GetEdge(IGraphVertex targetVertex); + } + /// + /// 无向图边 + /// + /// 边类型 + public interface IEdge + { + W Weight() where W : IComparable; // 权重,ifoxcad里应该用不到 + /// + /// 目标节点的键 + /// + /// + T TargetVertexKey { get; } + /// + /// 目标节点 + /// + /// + IGraphVertex TargetVertex { get; } + } + /// + /// 无向图中边的定义 + /// + /// 边的类型 + /// 权重的类型 + internal class Edge : IEdge where C : IComparable + { + // 这里的权重是个泛型,所以是否可以用curve类型作为权重? + private object weight; + + internal Edge(IGraphVertex target, C weight) + { + this.TargetVertex = target; + this.weight = weight; + } + + public T TargetVertexKey => TargetVertex.Key; + + public IGraphVertex TargetVertex { get; private set; } + + public W Weight() where W : IComparable + { + return (W)weight; + } + } +} \ No newline at end of file -- Gitee From 048ae577da3a9c3ff42ec2e9304854edab9c9717 Mon Sep 17 00:00:00 2001 From: vicwjb <403009819@qq.com> Date: Fri, 8 Apr 2022 00:09:18 +0800 Subject: [PATCH 099/675] =?UTF-8?q?=E6=9B=B4=E6=96=B0gitignore=E6=96=87?= =?UTF-8?q?=E4=BB=B6=EF=BC=8C=E8=BF=87=E6=BB=A4vscode=E9=85=8D=E7=BD=AE?= =?UTF-8?q?=E6=96=87=E4=BB=B6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .gitignore | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/.gitignore b/.gitignore index e1e90fc..e71f827 100644 --- a/.gitignore +++ b/.gitignore @@ -398,6 +398,10 @@ ASALocalRun/ # BeatPulse healthcheck temp database # BeatPulse healthcheck ʱݿ healthchecksdb + +#macos *.DS_Store -.ionide \ No newline at end of file +#vscode +.ionide +.vscode \ No newline at end of file -- Gitee From 54a7821bb9e6a9779ff178be98c00a263ae41f83 Mon Sep 17 00:00:00 2001 From: liuqihong <540762622@qq.com> Date: Fri, 8 Apr 2022 20:11:04 +0800 Subject: [PATCH 100/675] =?UTF-8?q?=E5=A2=9E=E5=8A=A0=E8=AF=B4=E6=98=8E?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/IFoxCAD.Basal/ArrayEx.cs | 23 +++++++++++++++-------- 1 file changed, 15 insertions(+), 8 deletions(-) diff --git a/src/IFoxCAD.Basal/ArrayEx.cs b/src/IFoxCAD.Basal/ArrayEx.cs index 818d54e..2491e16 100644 --- a/src/IFoxCAD.Basal/ArrayEx.cs +++ b/src/IFoxCAD.Basal/ArrayEx.cs @@ -1,5 +1,10 @@ namespace IFoxCAD.Basal { + /* + * 由于linq的函数大部分带有状态机,而cad是一个单机程序, + * 使用状态机会变得缓慢,因此我们设计的时候着重于时间优化, + * 本工具类在着重于数组遍历时候替代linq + */ public static class ArrayEx { /// @@ -16,22 +21,24 @@ public static T[] Combine2(this T[] a, T[] b) } /// - /// 单数组消重 + /// 一维数组消重 /// /// - /// - public static void Deduplication(List edgesOut, Func func) + /// 传入有重复成员的数组,传出没有重复的 + /// 传出参数1:数组开头;传出参数2:数组结尾;返回值比较结尾为就移除 + public static void Deduplication(List listInOut, Func func) { - for (int i = 0; i < edgesOut.Count; i++) + for (int i = 0; i < listInOut.Count; i++) { - var first = edgesOut[i]; - for (int j = edgesOut.Count - 1; j > i; j--) + var first = listInOut[i]; + for (int j = listInOut.Count - 1; j > i; j--) { - var last = edgesOut[j]; + var last = listInOut[j]; if (func(first, last)) - edgesOut.RemoveAt(j); + listInOut.RemoveAt(j); } } } + } } -- Gitee From 235baf5f1174096e2c12e17f1ddbbb7e518d6d94 Mon Sep 17 00:00:00 2001 From: liuqihong <540762622@qq.com> Date: Fri, 8 Apr 2022 21:06:17 +0800 Subject: [PATCH 101/675] =?UTF-8?q?=E4=BF=AE=E6=94=B9=E5=93=88=E5=B8=8C?= =?UTF-8?q?=E5=80=BC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/IFoxCAD.Cad/QuadTree/Rect.cs | 72 +++++++++++++++++++++++++++----- 1 file changed, 62 insertions(+), 10 deletions(-) diff --git a/src/IFoxCAD.Cad/QuadTree/Rect.cs b/src/IFoxCAD.Cad/QuadTree/Rect.cs index 820a8bc..11ec80a 100644 --- a/src/IFoxCAD.Cad/QuadTree/Rect.cs +++ b/src/IFoxCAD.Cad/QuadTree/Rect.cs @@ -205,7 +205,7 @@ public bool Equals(Rect? b, double tolerance = 1e-6) public override int GetHashCode() { - return base.GetHashCode(); + return (int)_X ^ (int)_Y ^ (int)_Right ^ (int)_Top; } #endregion @@ -481,16 +481,68 @@ static bool CrossAclockwise(Point2d o, Point2d a, Point2d b) } #if !WinForm - //public Autodesk.AutoCAD.DatabaseServices.Entity ToPolyLine() - //{ - // var bv = new List(); - // var pts = ToPoints(); - // pts.ForEach(item => { - // bv.Add(new BulgeVertexWidth(item.ToPoint2d(), 0)); - // }); - // return EntityAdd.AddPolyLineToEntity(bv); - //} + public Autodesk.AutoCAD.DatabaseServices.Entity ToPolyLine() + { + var bv = new List(); + var pts = ToPoints(); + Polyline pl = new(); + pl.SetDatabaseDefaults(); + pts.ForEach((i, vertex) => { + pl.AddVertexAt(i, vertex, 0, 0, 0); + }); + return pl; + } #endif + /// + /// 列扫碰撞检测(碰撞算法) + /// 比四叉树还快哦~ + /// + /// + /// 集合 + /// 先处理集合每一个成员 + /// 碰撞,返回两个碰撞的成员 + /// 后处理集合每一个成员 + public static void XCollision(List curves, + Action firstProcessing, + Action collisionProcessing, + Action lastProcessing) where T : Rect + { + //先排序X:不需要Y排序,因为Y的上下浮动不共X .ThenBy(a => a.Box.Y) + //因为先排序就可以有序遍历x区间,超过就break,达到更快 + curves = curves.OrderBy(a => a._X).ToList(); + + //遍历所有图元 + for (int i = 0; i < curves.Count; i++) + { + var oneRect = curves[i]; + firstProcessing(oneRect); + + //搜索范围要在 one 的头尾中间的部分 + for (int j = i + 1; j < curves.Count; j++) + { + var twoRect = curves[j]; + //x碰撞:矩形2的Left 在 矩形1[Left-Right]闭区间 + if (oneRect._X <= twoRect._X && twoRect._X <= oneRect._Right) + { + //y碰撞,那就是真的碰撞了 + if ((oneRect._Top >= twoRect._Top && twoRect._Top >= oneRect._Y) /*包容上边*/ + || (oneRect._Top >= twoRect._Y && twoRect._Y >= oneRect._Y) /*包容下边*/ + || (twoRect._Top >= oneRect._Top && oneRect._Y >= twoRect._Y)) /*穿过*/ + { + collisionProcessing(oneRect, twoRect); + } + //这里想中断y高过它的无意义比较, + //但是必须排序Y,而排序Y必须同X,而这里不是同X(而是同X区间),所以不能中断 + //而做到X区间排序,就必须创造一个集合,再排序这个集合, + //会导致每个图元都拥有一次X区间集合,开销更巨大(因此放弃). + } + else + break;//因为已经排序了,后续的必然超过 x碰撞区间 + } + lastProcessing(oneRect); + } + } + #endregion } -- Gitee From ac1a117f82a6f2b55994aa0f28babc2a4d69c7e8 Mon Sep 17 00:00:00 2001 From: liuqihong <540762622@qq.com> Date: Fri, 8 Apr 2022 21:12:40 +0800 Subject: [PATCH 102/675] =?UTF-8?q?=E5=A2=9E=E5=8A=A0=E7=A2=B0=E6=92=9E?= =?UTF-8?q?=E6=97=B6=E5=80=99=E6=B6=88=E9=87=8D=E5=9B=BE=E5=85=83?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/IFoxCAD.Cad/ExtensionMethod/Topo.cs | 120 ++++++++++-------------- 1 file changed, 49 insertions(+), 71 deletions(-) diff --git a/src/IFoxCAD.Cad/ExtensionMethod/Topo.cs b/src/IFoxCAD.Cad/ExtensionMethod/Topo.cs index 20ff04a..ba801ee 100644 --- a/src/IFoxCAD.Cad/ExtensionMethod/Topo.cs +++ b/src/IFoxCAD.Cad/ExtensionMethod/Topo.cs @@ -127,18 +127,12 @@ public bool SplitPointEquals(Edge? b, int splitNum = 4) #if NET35 sp1 = GeCurve3d.GetSamplePoints(splitNum); sp2 = b.GeCurve3d.GetSamplePoints(splitNum); - if (sp1.Length != sp2.Length) - return false; #else var tmp1 = GeCurve3d.GetSamplePoints(splitNum); var tmp2 = b.GeCurve3d.GetSamplePoints(splitNum); - if (tmp1.Length != tmp2.Length) - return false; - sp1 = tmp1.Select(a => a.Point).ToArray(); sp2 = tmp2.Select(a => a.Point).ToArray(); #endif - //因为两条曲线可能是逆向的,所以采样之后需要点排序 sp1 = sp1.OrderBy(a => a.X).ThenBy(a => a.Y).ThenBy(a => a.Z).ToArray(); sp2 = sp2.OrderBy(a => a.X).ThenBy(a => a.Y).ThenBy(a => a.Z).ToArray(); @@ -163,12 +157,14 @@ public static void Distinct(List edgesOut) //Edge没有包围盒,无法快速判断 //曲线a和其他曲线n根据交点切割子线,会造成重复部分,例如多段线逆向相同 Basal.ArrayEx.Deduplication(edgesOut, (first, last) => { + var pta1 = first.GeCurve3d.StartPoint; + var pta2 = first.GeCurve3d.EndPoint; + var ptb1 = last.GeCurve3d.StartPoint; + var ptb2 = last.GeCurve3d.EndPoint; //顺序 || 逆序 - if ((first.GeCurve3d.StartPoint.IsEqualTo(last.GeCurve3d.StartPoint, CadTolerance) && - first.GeCurve3d.EndPoint.IsEqualTo(last.GeCurve3d.EndPoint, CadTolerance)) + if ((pta1.IsEqualTo(ptb1, CadTolerance) && pta2.IsEqualTo(ptb2, CadTolerance)) || - (first.GeCurve3d.StartPoint.IsEqualTo(last.GeCurve3d.EndPoint, CadTolerance) && - first.GeCurve3d.EndPoint.IsEqualTo(last.GeCurve3d.StartPoint, CadTolerance))) + (pta1.IsEqualTo(ptb2, CadTolerance) && pta2.IsEqualTo(ptb1, CadTolerance))) { return first.SplitPointEquals(last); } @@ -177,7 +173,7 @@ public static void Distinct(List edgesOut) } public override int GetHashCode() { - return base.GetHashCode(); + return GeCurve3d.GetHashCode() ^ StartIndex ^ EndIndex; } #endregion } @@ -359,7 +355,7 @@ public bool Equals(EdgeItem? b) } public override int GetHashCode() { - return base.GetHashCode(); + return base.GetHashCode() ^ (Forward ? 0 : 1); } #endregion } @@ -412,7 +408,7 @@ public CurveInfo(Curve curve) /// /// 分割曲线,返回子线 /// - /// 曲线参数 + /// 曲线分割点的参数 /// public List Split(List pars1) { @@ -440,7 +436,7 @@ public CollisionChain() public class Topo { // 碰撞链集合 - List _coc; + List _CollisionChains; // 求交类(每次set自动重置,都会有个新的结果) static CurveCurveIntersector3d _cci3d = new(); // cad容差类 @@ -455,74 +451,56 @@ public Topo(List curves) for (int i = 0; i < curves.Count; i++) curveList.Add(new CurveInfo(curves[i])); - _coc = XCollision(curveList); - } - - /// - /// 列扫碰撞检测(碰撞算法) - /// 比四叉树还快哦~ - /// - /// - List XCollision(List curves) - { - //先排序X:不需要Y排序,因为Y的上下浮动不共X .ThenBy(a => a.Box.Y) - //因为先排序就可以有序遍历x区间,超过就break,达到更快 - curves = curves.OrderBy(a => a._X).ToList(); - - List collision = new(); - CollisionChain? coc = null; - - //遍历所有图元 - for (int i = 0; i < curves.Count; i++) - { - var one = curves[i]; - coc = one.CollisionChain;//有碰撞链就直接利用之前的链 + //碰撞检测+消重 + _CollisionChains = new(); + CollisionChain? tmp = null; + + Rect.XCollision(curveList, + oneRect => { + tmp = oneRect.CollisionChain;//有碰撞链就直接利用之前的链 + }, + (oneRect, twoRect) => { + //消重:包围盒大小一样+首尾相同+采样点相同 + if (oneRect.Equals(twoRect, 1e-6)) + { + var pta1 = oneRect.Edge.GeCurve3d.StartPoint; + var pta2 = oneRect.Edge.GeCurve3d.EndPoint; + var ptb1 = twoRect.Edge.GeCurve3d.StartPoint; + var ptb2 = twoRect.Edge.GeCurve3d.EndPoint; + + if ((pta1.IsEqualTo(ptb1, CadTolerance) && pta2.IsEqualTo(ptb2, CadTolerance)) + || + (pta1.IsEqualTo(ptb2, CadTolerance) && pta2.IsEqualTo(ptb1, CadTolerance))) + if (oneRect.Edge.SplitPointEquals(twoRect.Edge)) + return; + } - //搜索范围要在 one 的头尾中间的部分 - for (int j = i + 1; j < curves.Count; j++) - { - var two = curves[j]; - //x碰撞:矩形2的Left 在 矩形1[Left-Right]闭区间 - if (one._X <= two._X && two._X <= one._Right) + if (tmp == null) { - //y碰撞,那就是真的碰撞了 - if ((one._Top >= two._Top && two._Top >= one._Y) /*包容上边*/ - || (one._Top >= two._Y && two._Y >= one._Y) /*包容下边*/ - || (two._Top >= one._Top && one._Y >= two._Y)) /*穿过*/ - { - if (coc == null) - { - coc = new(); - one.CollisionChain = coc;//碰撞链设置 - coc.Collision.Add(one);//本体也加入链 - } - two.CollisionChain = coc;//碰撞链设置 - coc.Collision.Add(two); - } - //这里想中断y高过它的无意义比较, - //但是必须排序Y,而排序Y必须同X,而这里不是同X(而是同X区间),所以不能中断 - //而做到X区间排序,就必须创造一个集合,再排序这个集合, - //会导致每个图元都拥有一次X区间集合,开销更巨大(因此放弃). + tmp = new(); + oneRect.CollisionChain = tmp;//碰撞链设置 + tmp.Collision.Add(oneRect);//本体也加入链 } - else - break;//因为已经排序了,后续的必然超过 x碰撞区间 - } - if (coc != null && !collision.Contains(coc)) - { - collision.Add(coc); - coc = null; - } - } - return collision; + twoRect.CollisionChain = tmp;//碰撞链设置 + tmp.Collision.Add(twoRect); + }, + oneRect => { + if (tmp != null && !_CollisionChains.Contains(tmp)) + { + _CollisionChains.Add(tmp); + tmp = null; + } + }); } + /// /// 遍历多条碰撞链 /// /// public void CollisionFor(Action> action) { - _coc.ForEach(a => { + _CollisionChains.ForEach(a => { action(a.Collision);//输出每条碰撞链 }); } -- Gitee From 28aebd89d2784ea76538a51f24dc881a9bb1d326 Mon Sep 17 00:00:00 2001 From: vicwjb <403009819@qq.com> Date: Fri, 8 Apr 2022 21:01:22 +0800 Subject: [PATCH 103/675] =?UTF-8?q?=E5=A2=9E=E5=8A=A0=E5=A4=9A=E6=AE=B5?= =?UTF-8?q?=E7=BA=BF=E8=B0=83=E7=94=A8=E6=97=A0=E5=8F=82=E6=9E=84=E9=80=A0?= =?UTF-8?q?=E5=87=BD=E6=95=B0=E6=97=B6=EF=BC=8C=E8=AE=BE=E7=BD=AE=E5=88=9D?= =?UTF-8?q?=E5=A7=8B=E5=8C=96?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../ExtensionMethod/SymbolTableRecordEx.cs | 49 ++++++++++++------- tests/Test/TestCurve.cs | 29 +++++++++++ 2 files changed, 60 insertions(+), 18 deletions(-) diff --git a/src/IFoxCAD.Cad/ExtensionMethod/SymbolTableRecordEx.cs b/src/IFoxCAD.Cad/ExtensionMethod/SymbolTableRecordEx.cs index 1d7c543..2073633 100644 --- a/src/IFoxCAD.Cad/ExtensionMethod/SymbolTableRecordEx.cs +++ b/src/IFoxCAD.Cad/ExtensionMethod/SymbolTableRecordEx.cs @@ -166,19 +166,25 @@ public static ObjectId AddCircle(this BlockTableRecord btr, Point3d p0, Point3d /// 轻多段线属性设置委托 /// 事务管理器 /// 轻多段线id - public static ObjectId AddPline(this BlockTableRecord btr, List pts, List? bulges = default, List? startWidths = default, List? endWidths = default, Action? action = default, Transaction? trans = default) + public static ObjectId AddPline(this BlockTableRecord btr, + List pts, + List? bulges = default, + List? startWidths = default, + List? endWidths = default, + Action? action = default, + Transaction? trans = default) { bulges ??= new List(new double[pts.Count]); startWidths ??= new List(new double[pts.Count]); endWidths ??= new List(new double[pts.Count]); Polyline pl = new(); + pl.SetDatabaseDefaults(); for (int i = 0; i < pts.Count; i++) { pl.AddVertexAt(i, pts[i].Point2d(), bulges[i], startWidths[i], endWidths[i]); } return btr.AddEnt(pl, action, trans); } -#if !ac2009 /// /// 在指定的绘图空间添加轻多段线 @@ -188,19 +194,21 @@ public static ObjectId AddPline(this BlockTableRecord btr, List pts, Li /// 轻多段线属性设置委托 /// 事务管理器 /// 轻多段线id - public static ObjectId AddPline(this BlockTableRecord btr, List<(Point3d pt, double bulge, double startWidth, double endWidth)> pts, Action? action = default, Transaction? trans = default) - { + public static ObjectId AddPline(this BlockTableRecord btr, + List<(Point3d pt, double bulge, double startWidth, double endWidth)> pts, + Action? action = default, + Transaction? trans = default) + { - Polyline pl = new(); - pts.ForEach((i, vertex) => - { - pl.AddVertexAt(i, vertex.pt.Point2d(), vertex.bulge, vertex.startWidth, vertex.endWidth); - }); + Polyline pl = new(); + pl.SetDatabaseDefaults(); + pts.ForEach((i, vertex) => + { + pl.AddVertexAt(i, vertex.pt.Point2d(), vertex.bulge, vertex.startWidth, vertex.endWidth); + }); - return btr.AddEnt(pl, action, trans); - } - -#endif + return btr.AddEnt(pl, action, trans); + } /// /// 在指定绘图空间X-Y平面3点画圆弧 @@ -217,9 +225,11 @@ public static ObjectId AddArc(this BlockTableRecord btr, Point3d startPoint, Poi var arc = EntityEx.CreateArc(startPoint, pointOnArc, endPoint); return btr.AddEnt(arc, action, trans); } -#endregion -#region 获取实体/实体id + // todo: 所有涉及默认无参构造的实体类型,都需要调用SetDatabaseDefaults(); + #endregion + + #region 获取实体/实体id /// /// 获取块表记录内的指定类型的实体 /// @@ -276,9 +286,12 @@ public static IEnumerable> GetObjectIds(this BlockTa return tr.GetObject(btr.DrawOrderTableId, OpenMode.ForRead) as DrawOrderTable; } -#endregion + -#region 插入块参照 + + #endregion + + #region 插入块参照 /// /// 插入块参照 @@ -358,7 +371,7 @@ public static ObjectId InsertBlock(this BlockTableRecord blockTableRecord, Point } #endregion -#endregion + #endregion diff --git a/tests/Test/TestCurve.cs b/tests/Test/TestCurve.cs index 35d261d..787150d 100644 --- a/tests/Test/TestCurve.cs +++ b/tests/Test/TestCurve.cs @@ -2,6 +2,35 @@ namespace Test { + + public class TestGraph + { + [CommandMethod("testgraph")] + public void TestGraph1() + { + var graph = new Graph(); + graph.AddVertex(1); + graph.AddVertex(2); + graph.AddVertex(3); + graph.AddVertex(4); + graph.AddVertex(5); + graph.AddVertex(6); + graph.AddVertex(7); + + + graph.AddEdge(1, 2); + graph.AddEdge(2, 3); + graph.AddEdge(3, 4); + graph.AddEdge(4, 5); + graph.AddEdge(5, 6); + graph.AddEdge(6, 7); + graph.AddEdge(1, 7); + graph.AddEdge(6,1); + graph.AddEdge(7, 2); + graph.AddEdge(5, 3); + Env.Print(graph); + } + } public class TestCurve { [CommandMethod("testbreakcurve")] -- Gitee From 94b34a7d8ebfaca58a65ea27d5fb0c890847b4eb Mon Sep 17 00:00:00 2001 From: liuqihong <540762622@qq.com> Date: Fri, 8 Apr 2022 22:58:21 +0800 Subject: [PATCH 104/675] =?UTF-8?q?=E7=A2=B0=E6=92=9E=E7=AE=97=E6=B3=95?= =?UTF-8?q?=E5=8A=A0=E5=85=A5=E9=80=BB=E8=BE=91=E4=B8=AD=E6=96=AD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/IFoxCAD.Cad/QuadTree/Rect.cs | 34 +++++++++++++++++++------------- 1 file changed, 20 insertions(+), 14 deletions(-) diff --git a/src/IFoxCAD.Cad/QuadTree/Rect.cs b/src/IFoxCAD.Cad/QuadTree/Rect.cs index 11ec80a..e0b5047 100644 --- a/src/IFoxCAD.Cad/QuadTree/Rect.cs +++ b/src/IFoxCAD.Cad/QuadTree/Rect.cs @@ -499,29 +499,32 @@ public Autodesk.AutoCAD.DatabaseServices.Entity ToPolyLine() /// 比四叉树还快哦~ /// /// - /// 集合 - /// 先处理集合每一个成员 - /// 碰撞,返回两个碰撞的成员 + /// 继承Rect的集合 + /// 先处理集合每一个成员;返回true就跳过后续委托 + /// 碰撞,返回两个碰撞的成员;返回true就跳过后续委托 /// 后处理集合每一个成员 - public static void XCollision(List curves, - Action firstProcessing, - Action collisionProcessing, + public static void XCollision(List box, + Func firstProcessing, + Func collisionProcessing, Action lastProcessing) where T : Rect { //先排序X:不需要Y排序,因为Y的上下浮动不共X .ThenBy(a => a.Box.Y) //因为先排序就可以有序遍历x区间,超过就break,达到更快 - curves = curves.OrderBy(a => a._X).ToList(); + box = box.OrderBy(a => a._X).ToList(); //遍历所有图元 - for (int i = 0; i < curves.Count; i++) + for (int i = 0; i < box.Count; i++) { - var oneRect = curves[i]; - firstProcessing(oneRect); + var oneRect = box[i]; + if (firstProcessing(oneRect)) + continue; + + bool actionlast = true; //搜索范围要在 one 的头尾中间的部分 - for (int j = i + 1; j < curves.Count; j++) + for (int j = i + 1; j < box.Count; j++) { - var twoRect = curves[j]; + var twoRect = box[j]; //x碰撞:矩形2的Left 在 矩形1[Left-Right]闭区间 if (oneRect._X <= twoRect._X && twoRect._X <= oneRect._Right) { @@ -530,7 +533,8 @@ public static void XCollision(List curves, || (oneRect._Top >= twoRect._Y && twoRect._Y >= oneRect._Y) /*包容下边*/ || (twoRect._Top >= oneRect._Top && oneRect._Y >= twoRect._Y)) /*穿过*/ { - collisionProcessing(oneRect, twoRect); + if (collisionProcessing(oneRect, twoRect)) + actionlast = false; } //这里想中断y高过它的无意义比较, //但是必须排序Y,而排序Y必须同X,而这里不是同X(而是同X区间),所以不能中断 @@ -540,7 +544,9 @@ public static void XCollision(List curves, else break;//因为已经排序了,后续的必然超过 x碰撞区间 } - lastProcessing(oneRect); + + if (actionlast) + lastProcessing(oneRect); } } -- Gitee From db83b25a8212bfbfbca8f7ad5a34b882bb730f85 Mon Sep 17 00:00:00 2001 From: liuqihong <540762622@qq.com> Date: Fri, 8 Apr 2022 22:59:09 +0800 Subject: [PATCH 105/675] =?UTF-8?q?topo=E5=8A=A0=E5=85=A5=E7=A2=B0?= =?UTF-8?q?=E6=92=9E=E4=B8=AD=E6=96=AD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/IFoxCAD.Cad/ExtensionMethod/Topo.cs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/IFoxCAD.Cad/ExtensionMethod/Topo.cs b/src/IFoxCAD.Cad/ExtensionMethod/Topo.cs index ba801ee..fee0763 100644 --- a/src/IFoxCAD.Cad/ExtensionMethod/Topo.cs +++ b/src/IFoxCAD.Cad/ExtensionMethod/Topo.cs @@ -458,6 +458,7 @@ public Topo(List curves) Rect.XCollision(curveList, oneRect => { tmp = oneRect.CollisionChain;//有碰撞链就直接利用之前的链 + return false; }, (oneRect, twoRect) => { //消重:包围盒大小一样+首尾相同+采样点相同 @@ -472,7 +473,7 @@ public Topo(List curves) || (pta1.IsEqualTo(ptb2, CadTolerance) && pta2.IsEqualTo(ptb1, CadTolerance))) if (oneRect.Edge.SplitPointEquals(twoRect.Edge)) - return; + return true;//跳过后续步骤 } if (tmp == null) @@ -483,6 +484,7 @@ public Topo(List curves) } twoRect.CollisionChain = tmp;//碰撞链设置 tmp.Collision.Add(twoRect); + return false; }, oneRect => { if (tmp != null && !_CollisionChains.Contains(tmp)) -- Gitee From 0c441aa6c4a4001e0ba86cb7627156e3372b01d0 Mon Sep 17 00:00:00 2001 From: liuqihong <540762622@qq.com> Date: Fri, 8 Apr 2022 23:05:27 +0800 Subject: [PATCH 106/675] =?UTF-8?q?rect=E7=B1=BB=E5=A2=9E=E5=8A=A0?= =?UTF-8?q?=E6=B3=A8=E9=87=8A?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/IFoxCAD.Cad/QuadTree/Rect.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/IFoxCAD.Cad/QuadTree/Rect.cs b/src/IFoxCAD.Cad/QuadTree/Rect.cs index e0b5047..84bae17 100644 --- a/src/IFoxCAD.Cad/QuadTree/Rect.cs +++ b/src/IFoxCAD.Cad/QuadTree/Rect.cs @@ -525,7 +525,7 @@ public static void XCollision(List box, for (int j = i + 1; j < box.Count; j++) { var twoRect = box[j]; - //x碰撞:矩形2的Left 在 矩形1[Left-Right]闭区间 + //x碰撞:矩形2的Left 在 矩形1[Left-Right]闭区间;穿过的话,也必然有自己的Left因此不需要处理 if (oneRect._X <= twoRect._X && twoRect._X <= oneRect._Right) { //y碰撞,那就是真的碰撞了 -- Gitee From 2a628a3944e84cae5dbc2b5ba799867e43aca472 Mon Sep 17 00:00:00 2001 From: liuqihong <540762622@qq.com> Date: Fri, 8 Apr 2022 23:20:25 +0800 Subject: [PATCH 107/675] =?UTF-8?q?=E4=BF=AE=E6=94=B9GetObjectId=E7=9A=84?= =?UTF-8?q?=E4=BD=8E=E7=89=88=E6=9C=AC=E5=A4=84=E7=90=86?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/IFoxCAD.Cad/Runtime/DBTrans.cs | 116 ++++++++++++++++++++++++----- 1 file changed, 99 insertions(+), 17 deletions(-) diff --git a/src/IFoxCAD.Cad/Runtime/DBTrans.cs b/src/IFoxCAD.Cad/Runtime/DBTrans.cs index 79f032e..3cc42ad 100644 --- a/src/IFoxCAD.Cad/Runtime/DBTrans.cs +++ b/src/IFoxCAD.Cad/Runtime/DBTrans.cs @@ -2,7 +2,7 @@ public class DBTrans : IDisposable { -#region 私有字段 + #region 私有字段 /// /// 文档锁 /// @@ -21,7 +21,7 @@ public class DBTrans : IDisposable private static readonly Stack dBTrans = new(); #endregion -#region 公开属性 + #region 公开属性 /// /// 返回当前事务 /// @@ -61,7 +61,7 @@ public static DBTrans Top #endregion -#region 构造函数 + #region 构造函数 /// /// 默认构造函数,默认为打开当前文档,默认提交事务 /// @@ -137,14 +137,14 @@ public DBTrans(string fileName, bool commit = true) { Database = new Database(true, false); } - + Transaction = Database.TransactionManager.StartTransaction(); _commit = commit; dBTrans.Push(this); } -#endregion + #endregion -#region 类型转换 + #region 类型转换 /// /// 隐式转换为Transaction /// @@ -154,9 +154,9 @@ public static implicit operator Transaction(DBTrans tr) { return tr.Transaction; } -#endregion + #endregion -#region 符号表 + #region 符号表 /// /// 块表 @@ -212,9 +212,9 @@ public static implicit operator Transaction(DBTrans tr) /// 视口表 /// public SymbolTable ViewportTable => new(this, Database.ViewportTableId); -#endregion + #endregion -#region 字典 + #region 字典 /// /// 命名对象字典 /// @@ -273,9 +273,9 @@ public static implicit operator Transaction(DBTrans tr) /// public DBDictionary SectionViewStyleDict => GetObject(Database.SectionViewStyleDictionaryId)!; #endif -#endregion + #endregion -#region 获取对象 + #region 获取对象 /// /// 根据对象id获取图元对象 /// @@ -301,14 +301,96 @@ public static implicit operator Transaction(DBTrans tr) public ObjectId GetObjectId(string handleString) { var hanle = new Handle(Convert.ToInt64(handleString, 16)); - return Database.GetObjectId(false, hanle, 0); + //return Database.GetObjectId(false, hanle, 0); + return Helper.TryGetObjectId(Database, hanle); } + class Helper + { + /* + * id = db.GetObjectId(false, handle, 0); + * 参数意义: db.GetObjectId(如果没有找到就创建,句柄号,标记..将来备用) + * 在vs的输出会一直抛出: + * 引发的异常:“Autodesk.AutoCAD.Runtime.Exception”(位于 AcdbMgd.dll 中) + * "eUnknownHandle" + * 这就是为什么慢的原因,所以直接运行就好了!而Debug还是需要用arx的API替代. + */ + + [System.Security.SuppressUnmanagedCodeSecurity] + [DllImport("acdb17.dll", CallingConvention = CallingConvention.ThisCall/*08的调用约定 高版本是__cdecl*/, + EntryPoint = "?getAcDbObjectId@AcDbDatabase@@QAE?AW4ErrorStatus@Acad@@AAVAcDbObjectId@@_NABVAcDbHandle@@K@Z")] + extern static int getAcDbObjectId17x32(IntPtr db, out ObjectId id, [MarshalAs(UnmanagedType.U1)] bool createnew, ref Handle h, uint reserved); + + [System.Security.SuppressUnmanagedCodeSecurity] + [DllImport("acdb17.dll", CallingConvention = CallingConvention.ThisCall/*08的调用约定 高版本是__cdecl*/, + EntryPoint = "?getAcDbObjectId@AcDbDatabase@@QEAA?AW4ErrorStatus@Acad@@AEAVAcDbObjectId@@_NAEBVAcDbHandle@@K@Z")] + extern static int getAcDbObjectId17x64(IntPtr db, out ObjectId id, [MarshalAs(UnmanagedType.U1)] bool createnew, ref Handle h, uint reserved); + + [System.Security.SuppressUnmanagedCodeSecurity] + [DllImport("acdb18.dll", CallingConvention = CallingConvention.ThisCall/*08的调用约定 高版本是__cdecl*/, + EntryPoint = "?getAcDbObjectId@AcDbDatabase@@QAE?AW4ErrorStatus@Acad@@AAVAcDbObjectId@@_NABVAcDbHandle@@K@Z")] + extern static int getAcDbObjectId18x32(IntPtr db, out ObjectId id, [MarshalAs(UnmanagedType.U1)] bool createnew, ref Handle h, uint reserved); + + [System.Security.SuppressUnmanagedCodeSecurity] + [DllImport("acdb18.dll", CallingConvention = CallingConvention.ThisCall/*08的调用约定 高版本是__cdecl*/, + EntryPoint = "?getAcDbObjectId@AcDbDatabase@@QEAA?AW4ErrorStatus@Acad@@AEAVAcDbObjectId@@_NAEBVAcDbHandle@@K@Z")] + extern static int getAcDbObjectId18x64(IntPtr db, out ObjectId id, [MarshalAs(UnmanagedType.U1)] bool createnew, ref Handle h, uint reserved); + + /// + /// 句柄转id,NET35(08~12)专用的 + /// + /// 数据库 + /// 句柄 + /// 返回的id + /// 不存在则创建 + /// 保留,用于未来 + /// 成功0,其他值都是错误.可以强转ErrorStatus + static int GetAcDbObjectId(IntPtr db, Handle handle, out ObjectId id, bool createIfNotFound = false, uint reserved = 0) + { + id = ObjectId.Null; + switch (Application.Version.Major) + { + case 17: + { + if (IntPtr.Size == 4) + return getAcDbObjectId17x32(db, out id, createIfNotFound, ref handle, reserved); + else + return getAcDbObjectId17x64(db, out id, createIfNotFound, ref handle, reserved); + } + case 18: + { + if (IntPtr.Size == 4) + return getAcDbObjectId18x32(db, out id, createIfNotFound, ref handle, reserved); + else + return getAcDbObjectId18x64(db, out id, createIfNotFound, ref handle, reserved); + } + } + return -1; + } + /// + /// 句柄转id + /// + /// 数据库 + /// 句柄 + /// id + public static ObjectId TryGetObjectId(Database db, Handle handle) + { +#if !NET35 + //高版本直接利用 + var es = db.TryGetObjectId(handle, out ObjectId id); + //if (!es) +#else + var es = GetAcDbObjectId(db.UnmanagedObject, handle, out ObjectId id); + //if (ErrorStatus.OK != (ErrorStatus)es) +#endif + return id; + } + } -#endregion + #endregion -#region idispose接口相关函数 + #region idispose接口相关函数 public void Abort() { @@ -340,7 +422,7 @@ protected virtual void Dispose(bool disposing) dBTrans.Pop(); if (!Transaction.IsDisposed) { - if (Document?.IsActive==true) + if (Document?.IsActive == true) Transaction.TransactionManager.QueueForGraphicsFlush(); Transaction.Dispose(); } @@ -367,5 +449,5 @@ public void Dispose() Dispose(disposing: true); GC.SuppressFinalize(this); } -#endregion + #endregion } -- Gitee From 270e1cc62b82021728197f609dd3d17eacf8bb23 Mon Sep 17 00:00:00 2001 From: liuqihong <540762622@qq.com> Date: Fri, 8 Apr 2022 23:39:41 +0800 Subject: [PATCH 108/675] =?UTF-8?q?=E5=A2=9E=E5=8A=A0=E4=BC=98=E5=8C=96?= =?UTF-8?q?=E6=96=B9=E6=A1=88?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/IFoxCAD.Cad/ExtensionMethod/Topo.cs | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/IFoxCAD.Cad/ExtensionMethod/Topo.cs b/src/IFoxCAD.Cad/ExtensionMethod/Topo.cs index fee0763..129eceb 100644 --- a/src/IFoxCAD.Cad/ExtensionMethod/Topo.cs +++ b/src/IFoxCAD.Cad/ExtensionMethod/Topo.cs @@ -652,6 +652,10 @@ public List> GetRegions(List edges) * 狐哥为了处理左拐还是右拐图形bd,用了所有的线做种子线,然后往前后推进 * 前后推进:利用边界的顺序和逆序获取闭合链条, * 会造成一环就有: 123,231,312,321,213,132,再其后再判断他们重复 + * + * 优化方案一:邻接表按引用次数排序之后,上面的交点如果只有两个图元,组成链,然后移除出邻接表. + * 这样我只有遇到多个共点时候,那就只有:多个链头尾(减少了腰身)和邻接表的 + * 实现递减. */ for (int i = 0; i < edges.Count; i++) { -- Gitee From 14b4b2f3aa477f5ab8630b5d86c2ece2f213f379 Mon Sep 17 00:00:00 2001 From: vicwjb <403009819@qq.com> Date: Fri, 8 Apr 2022 23:49:48 +0800 Subject: [PATCH 109/675] =?UTF-8?q?=E5=A2=9E=E5=8A=A0=E5=9B=BE=E7=9A=84?= =?UTF-8?q?=E6=B3=A8=E9=87=8A=EF=BC=8C=E4=B8=BA=E5=90=8E=E7=BB=AD=E6=94=B9?= =?UTF-8?q?=E9=80=A0=E7=90=86=E6=B8=85=E6=80=9D=E8=B7=AF?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/IFoxCAD.Cad/QuadTree/Graph.cs | 73 +++++++++++++++++------------- src/IFoxCAD.Cad/QuadTree/IGraph.cs | 8 +++- 2 files changed, 49 insertions(+), 32 deletions(-) diff --git a/src/IFoxCAD.Cad/QuadTree/Graph.cs b/src/IFoxCAD.Cad/QuadTree/Graph.cs index 43f29bb..757352b 100644 --- a/src/IFoxCAD.Cad/QuadTree/Graph.cs +++ b/src/IFoxCAD.Cad/QuadTree/Graph.cs @@ -12,14 +12,14 @@ public class Graph : IGraph, IEnumerable /// 存储所有节点的字典,key为节点的类型,value为节点类型 /// /// - private Dictionary> vertices { get; set; } + private Dictionary> vertices = new Dictionary>(); public int VerticesCount => vertices.Count; public bool IsWeightedGraph => false; public Graph() { - vertices = new Dictionary>(); + //vertices = new Dictionary>(); } /// @@ -217,44 +217,55 @@ IGraph IGraph.Clone() public IEnumerable> VerticesAsEnumberable => (IEnumerable>)vertices.Select(x => x.Value); + + } + + /// + /// 邻接表图实现的顶点。 + /// IEnumerable 枚举所有出边目标顶点。 + /// + public class GraphVertex : IEnumerable, IGraphVertex + { + public T Key { get; set; } /// - /// 邻接表图实现的顶点。 - /// IEnumerable 枚举所有出边目标顶点。 + /// 邻接表 + /// todo:这里应该改为adjacencyList 来表示邻接点表 /// - private class GraphVertex : IEnumerable, IGraphVertex - { - public T Key { get; set; } - /// - /// 邻接表 - /// - /// - public HashSet> Edges { get; } - - IEnumerable> IGraphVertex.Edges => (IEnumerable>)Edges.Select(x => new Edge(x, 1)); + /// + public HashSet> Edges { get; } + /// + /// 邻接边表 + /// 这个类的定义有问题: + /// todo:邻接边表和邻接表的名字是一样的,造成误解 + /// + /// + /// + /// + IEnumerable> IGraphVertex.Edges => (IEnumerable>)Edges.Select(x => new Edge(x, 1)); - public GraphVertex(T value) - { - Key = value; - Edges = new HashSet>(); - } + public GraphVertex(T value) + { + Key = value; + Edges = new HashSet>(); + } - public IEdge GetEdge(IGraphVertex targetVertex) - { - return new Edge(targetVertex, 1); - } + public IEdge GetEdge(IGraphVertex targetVertex) + { + return new Edge(targetVertex, 1); + } - IEnumerator IEnumerable.GetEnumerator() - { - return GetEnumerator(); - } + IEnumerator IEnumerable.GetEnumerator() + { + return GetEnumerator(); + } - public IEnumerator GetEnumerator() - { - return Edges.Select(x => x.Key).GetEnumerator(); - } + public IEnumerator GetEnumerator() + { + return Edges.Select(x => x.Key).GetEnumerator(); } } + /// /// 深度优先搜索。 /// diff --git a/src/IFoxCAD.Cad/QuadTree/IGraph.cs b/src/IFoxCAD.Cad/QuadTree/IGraph.cs index 905f614..2b65f30 100644 --- a/src/IFoxCAD.Cad/QuadTree/IGraph.cs +++ b/src/IFoxCAD.Cad/QuadTree/IGraph.cs @@ -54,6 +54,9 @@ public interface IGraph /// /// 无向图节点 + /// 顶点的数据结构: + /// key --- point3d 用于表示点的坐标和作为数据存储的key + /// edges -- 邻接边表, 每个边的定义为,(边,下一顶点),这样遍历每个顶点的邻接边表时,就可以知道多少个邻接边及对应的邻接点 /// /// 节点数据类型 public interface IGraphVertex @@ -64,7 +67,7 @@ public interface IGraphVertex /// T Key { get; } /// - /// 节点的邻接表 + /// 节点的邻接边表 /// /// IEnumerable> Edges { get; } @@ -101,6 +104,9 @@ public interface IEdge internal class Edge : IEdge where C : IComparable { // 这里的权重是个泛型,所以是否可以用curve类型作为权重? + // 看来 这里可以用一个曲线作为边的一个值,取消权重的概念, + // 或者说当C 为int的时候 可以视为权重,当为其他类型的时候 就是一种边的数据类型 + // 所以这里就不能约束C的类型为IComparable private object weight; internal Edge(IGraphVertex target, C weight) -- Gitee From c66138cced1522235b161ae096b5165365f4efda Mon Sep 17 00:00:00 2001 From: liuqihong <540762622@qq.com> Date: Sat, 9 Apr 2022 00:51:10 +0800 Subject: [PATCH 110/675] =?UTF-8?q?=E4=BF=AE=E6=94=B9=E6=B3=A8=E5=86=8C?= =?UTF-8?q?=E8=A1=A8bug?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/IFoxCAD.Cad/Runtime/AutoRegAssem.cs | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/src/IFoxCAD.Cad/Runtime/AutoRegAssem.cs b/src/IFoxCAD.Cad/Runtime/AutoRegAssem.cs index f306fc3..99af880 100644 --- a/src/IFoxCAD.Cad/Runtime/AutoRegAssem.cs +++ b/src/IFoxCAD.Cad/Runtime/AutoRegAssem.cs @@ -75,7 +75,7 @@ public AutoRegAssem() private static RegistryKey GetAcAppKey() { #if ac2009 - string key = HostApplicationServices.Current.RegistryProductRootKey; + string key = HostApplicationServices.Current.RegistryProductRootKey; #else string key = HostApplicationServices.Current.MachineRegistryProductRootKey; #endif @@ -87,8 +87,17 @@ private static RegistryKey GetAcAppKey() private bool SearchForReg() { RegistryKey appkey = GetAcAppKey(); + if (appkey.SubKeyCount == 0) + return false; + var regApps = appkey.GetSubKeyNames(); - return regApps.Contains(_info.Name); + if (regApps.Contains(_info.Name)) + { + //20220409 bug:文件名相同,路径不同,需要判断路径 + var info = appkey.OpenSubKey(_info.Name); + return info.GetValue("LOADER")?.ToString().ToLower() == _info.Loader.ToLower(); + } + return false; } /// -- Gitee From bcc3ecbc34fa5eee0a1d9755ea73a2dbf122c5f9 Mon Sep 17 00:00:00 2001 From: liuqihong <540762622@qq.com> Date: Sat, 9 Apr 2022 02:32:37 +0800 Subject: [PATCH 111/675] =?UTF-8?q?=E6=B5=8B=E8=AF=95=E6=96=87=E4=BB=B6?= =?UTF-8?q?=E5=8E=BB=E9=99=A4=E8=AD=A6=E5=91=8A?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- tests/Test/Test.cs | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/tests/Test/Test.cs b/tests/Test/Test.cs index 12fc863..8b9b7a0 100644 --- a/tests/Test/Test.cs +++ b/tests/Test/Test.cs @@ -1,7 +1,5 @@ -namespace test; - - - +#pragma warning disable CS0219 // 变量已被赋值,但从未使用过它的值 +namespace test; public class Test : AutoRegAssem { @@ -317,6 +315,7 @@ public void TestRec() Tools.TestTimes(1000000,"三次点乘", () => { var result = false; + if (Math.Abs(p12.DotProduct(p23)) < 1e8 && Math.Abs(p23.DotProduct(p34)) < 1e8 && Math.Abs(p34.DotProduct(p41)) < 1e8) @@ -563,3 +562,6 @@ public void CombineBlocksIntoLibrary() } } + + +#pragma warning restore CS0219 // 变量已被赋值,但从未使用过它的值 -- Gitee From 40a0c92e9fca431ccdbad180ae2afc08eeaf915f Mon Sep 17 00:00:00 2001 From: liuqihong <540762622@qq.com> Date: Sat, 9 Apr 2022 02:33:14 +0800 Subject: [PATCH 112/675] =?UTF-8?q?=E4=BF=AE=E6=94=B9=E6=B3=A8=E9=87=8A?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../ExtensionMethod/SymbolTableRecordEx.cs | 37 +++++++------------ 1 file changed, 14 insertions(+), 23 deletions(-) diff --git a/src/IFoxCAD.Cad/ExtensionMethod/SymbolTableRecordEx.cs b/src/IFoxCAD.Cad/ExtensionMethod/SymbolTableRecordEx.cs index 2073633..e2bf8b0 100644 --- a/src/IFoxCAD.Cad/ExtensionMethod/SymbolTableRecordEx.cs +++ b/src/IFoxCAD.Cad/ExtensionMethod/SymbolTableRecordEx.cs @@ -5,8 +5,6 @@ /// public static class SymbolTableRecordEx { - - #region 块表记录 #region 添加实体 @@ -37,8 +35,8 @@ public static ObjectId AddEntity(this BlockTableRecord btr, Entity entity, Trans /// /// 实体类型 /// 块表记录 - /// 事务 /// 实体集合 + /// 事务 /// 对象 id 列表 public static IEnumerable AddEntity(this BlockTableRecord btr, IEnumerable ents, Transaction? trans = null) where T : Entity { @@ -50,8 +48,7 @@ public static IEnumerable AddEntity(this BlockTableRecord btr, IEnu { return ents .Select( - ent => - { + ent => { ObjectId id = btr.AppendEntity(ent); trans.AddNewlyCreatedDBObject(ent, true); return id; @@ -59,6 +56,7 @@ public static IEnumerable AddEntity(this BlockTableRecord btr, IEnu .ToList(); } } + /// /// 添加多个实体 /// @@ -199,14 +197,13 @@ public static ObjectId AddPline(this BlockTableRecord btr, Action? action = default, Transaction? trans = default) { - + Polyline pl = new(); pl.SetDatabaseDefaults(); - pts.ForEach((i, vertex) => - { + pts.ForEach((i, vertex) => { pl.AddVertexAt(i, vertex.pt.Point2d(), vertex.bulge, vertex.startWidth, vertex.endWidth); }); - + return btr.AddEnt(pl, action, trans); } @@ -235,8 +232,8 @@ public static ObjectId AddArc(this BlockTableRecord btr, Point3d startPoint, Poi /// /// 实体类型 /// 块表记录 - /// 事务 /// 打开模式 + /// 事务 /// 实体集合 public static IEnumerable GetEntities(this BlockTableRecord btr, OpenMode mode = OpenMode.ForRead, Transaction? trans = default) where T : Entity { @@ -278,29 +275,26 @@ public static IEnumerable> GetObjectIds(this BlockTa /// 获取绘制顺序表 /// /// 块表 - /// 事务 + /// 事务 /// 绘制顺序表 - public static DrawOrderTable? GetDrawOrderTable(this BlockTableRecord btr, Transaction? tr = default) + public static DrawOrderTable? GetDrawOrderTable(this BlockTableRecord btr, Transaction? trans = default) { - tr ??= DBTrans.Top.Transaction; - return tr.GetObject(btr.DrawOrderTableId, OpenMode.ForRead) as DrawOrderTable; + trans ??= DBTrans.Top.Transaction; + return trans.GetObject(btr.DrawOrderTableId, OpenMode.ForRead) as DrawOrderTable; } - - - - #endregion #region 插入块参照 - /// /// 插入块参照 /// + /// 块表记录 /// 插入点 /// 块名 /// 块插入比例,默认为1 /// 块插入旋转角(弧度),默认为0 /// 属性字典{Tag,Value},默认为null + /// 事务 /// 块参照对象id public static ObjectId InsertBlock(this BlockTableRecord blockTableRecord, Point3d position, string blockName, @@ -369,10 +363,7 @@ public static ObjectId InsertBlock(this BlockTableRecord blockTableRecord, Point } return objid; } - -#endregion #endregion - - + #endregion } -- Gitee From 8c7be4471a865ad8f2ecf660c8a4cf352a4a2b66 Mon Sep 17 00:00:00 2001 From: liuqihong <540762622@qq.com> Date: Sat, 9 Apr 2022 02:44:24 +0800 Subject: [PATCH 113/675] =?UTF-8?q?=E4=BF=AE=E6=94=B9=E6=B3=A8=E9=87=8A?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/IFoxCAD.Cad/ExtensionMethod/CurveEx.cs | 2 +- src/IFoxCAD.Cad/ExtensionMethod/Topo.cs | 35 +++++++++++++++++----- 2 files changed, 28 insertions(+), 9 deletions(-) diff --git a/src/IFoxCAD.Cad/ExtensionMethod/CurveEx.cs b/src/IFoxCAD.Cad/ExtensionMethod/CurveEx.cs index ed4d98a..8112b9d 100644 --- a/src/IFoxCAD.Cad/ExtensionMethod/CurveEx.cs +++ b/src/IFoxCAD.Cad/ExtensionMethod/CurveEx.cs @@ -60,7 +60,7 @@ public static IEnumerable GetSplitCurves(this Curve curve, IEnumerable(); topo.GetEdgesAndnewCurves(infos, gs, c3); - topo.AdjacencyList(gs, c3);//增加测试..需要加入四叉树 + topo.AdjacencyList(gs, c3); var regions = topo.GetRegions(gs); for (int i = 0; i < regions.Count; i++) diff --git a/src/IFoxCAD.Cad/ExtensionMethod/Topo.cs b/src/IFoxCAD.Cad/ExtensionMethod/Topo.cs index 129eceb..af96c2e 100644 --- a/src/IFoxCAD.Cad/ExtensionMethod/Topo.cs +++ b/src/IFoxCAD.Cad/ExtensionMethod/Topo.cs @@ -7,8 +7,8 @@ public class Edge : IEquatable { #region 字段 public CompositeCurve3d GeCurve3d; - public int StartIndex; - public int EndIndex; + public int StartIndex;//DESIGN0409 这个东西只是用在邻接表过滤就没用了. + public int EndIndex; //DESIGN0409 这个东西只是用在邻接表过滤就没用了. public static Tolerance CadTolerance = new(1e-6, 1e-6); #endregion @@ -586,6 +586,8 @@ public void AdjacencyList(List edges_InOut, List closedC var nums = new List(); var closedEdges = new List(); + //var dic = new Dictionary(); + //交点集合 knots 会不断增大,会变得更加慢,因此我在一开始就进行碰撞链分析 for (int i = 0; i < edges_InOut.Count; i++) { @@ -596,12 +598,26 @@ public void AdjacencyList(List edges_InOut, List closedC continue; } + var sp = edge.GeCurve3d.StartPoint; + var ep = edge.GeCurve3d.EndPoint; + + //狐哥在图元信息上面储存的是计数索引(也没有办法储存计数 DESIGN0409),因此这样的写法不对 + //if (dic.Keys.Contains(sp)) + // dic[sp]++;//含有就是其他曲线"共用"此交点,计数 + //else + // dic.Add(sp, 1);//不含有就加入节点 + + //if (dic.Keys.Contains(ep)) + // dic[ep]++; + //else + // dic.Add(ep, 1); + if (knots.Contains(edge.GeCurve3d.StartPoint)) { //含有就是其他曲线"共用"此交点, //节点所在索引==共用计数索引=>将它++ - edge.StartIndex = knots.IndexOf(edge.GeCurve3d.StartPoint); - nums[edge.StartIndex]++; + edge.StartIndex = knots.IndexOf(edge.GeCurve3d.StartPoint);//给它交点计数的索引,不是次数 + nums[edge.StartIndex]++;//交点计数 } else { @@ -630,12 +646,15 @@ public void AdjacencyList(List edges_InOut, List closedC //剩下的都是闭合的曲线连接了,每个点都至少有两条曲线通过 var tmp = edges_InOut .Except(closedEdges) - .Where(e => nums[e.StartIndex] > 1 && nums[e.EndIndex] > 1) - .ToArray();//要ToArray克隆,否则下面会清空掉这个容器 + .Where(e => nums[e.StartIndex] > 1 && nums[e.EndIndex] > 1); + + //TODO DESIGN0409 按照引用次数的排序,不成功 + //tmp = tmp.OrderBy(e => nums[e.StartIndex]); + var tmpArr = tmp.ToArray();//Clear导致tmp失效 edges_InOut.Clear(); - for (int i = 0; i < tmp.Length; i++) - edges_InOut.Add(tmp[i]); + for (int i = 0; i < tmpArr.Length; i++) + edges_InOut.Add(tmpArr[i]); } /// -- Gitee From e7299492ca18d5de73b7c3716b3d927c7658aa92 Mon Sep 17 00:00:00 2001 From: vicwjb <403009819@qq.com> Date: Sat, 9 Apr 2022 18:30:47 +0800 Subject: [PATCH 114/675] =?UTF-8?q?=E4=BF=AE=E5=A4=8D=E6=8F=92=E5=85=A5?= =?UTF-8?q?=E5=9D=97=E5=8F=82=E7=85=A7=E6=97=B6=E4=B8=8D=E8=83=BD=E4=BF=AE?= =?UTF-8?q?=E6=94=B9=E4=B8=8D=E5=8F=AF=E8=A7=81=E5=B1=9E=E6=80=A7=E5=92=8C?= =?UTF-8?q?=E5=9B=BA=E5=AE=9A=E5=B1=9E=E6=80=A7=E7=9A=84=E9=94=99=E8=AF=AF?= =?UTF-8?q?=E3=80=82fixed=20#I510AT?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/IFoxCAD.Cad/ExtensionMethod/SymbolTableRecordEx.cs | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/IFoxCAD.Cad/ExtensionMethod/SymbolTableRecordEx.cs b/src/IFoxCAD.Cad/ExtensionMethod/SymbolTableRecordEx.cs index e2bf8b0..2f0a240 100644 --- a/src/IFoxCAD.Cad/ExtensionMethod/SymbolTableRecordEx.cs +++ b/src/IFoxCAD.Cad/ExtensionMethod/SymbolTableRecordEx.cs @@ -342,12 +342,11 @@ public static ObjectId InsertBlock(this BlockTableRecord blockTableRecord, Point var btr = DBTrans.Top.GetObject(blockref.BlockTableRecord)!; if (btr.HasAttributeDefinitions) { - var attdefs = btr - .GetEntities() - .Where(attdef => !(attdef.Constant || attdef.Invisible)); + var attdefs = btr.GetEntities(); foreach (var attdef in attdefs) { using AttributeReference attref = new(); + attref.SetDatabaseDefaults(); attref.SetAttributeFromBlock(attdef, blockref.BlockTransform); attref.Position = attdef.Position.TransformBy(blockref.BlockTransform); attref.AdjustAlignment(DBTrans.Top.Database); -- Gitee From 1631442261a7d54f16a9e2856300d8471cfd52c8 Mon Sep 17 00:00:00 2001 From: vicwjb <403009819@qq.com> Date: Sat, 9 Apr 2022 20:02:04 +0800 Subject: [PATCH 115/675] =?UTF-8?q?=E5=A2=9E=E5=8A=A0=E6=B7=BB=E5=8A=A0?= =?UTF-8?q?=E5=B1=9E=E6=80=A7=E5=88=B0=E5=9D=97=E5=AE=9A=E4=B9=89=E5=87=BD?= =?UTF-8?q?=E6=95=B0=E3=80=82fixed=20#I4YNQ1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../ExtensionMethod/SymbolTableEx.cs | 45 +++++++++++++++++++ src/IFoxCAD.Cad/QuadTree/Graph.cs | 2 +- src/IFoxCAD.Cad/QuadTree/IGraph.cs | 2 +- tests/Test/testblock.cs | 13 ++++++ 4 files changed, 60 insertions(+), 2 deletions(-) diff --git a/src/IFoxCAD.Cad/ExtensionMethod/SymbolTableEx.cs b/src/IFoxCAD.Cad/ExtensionMethod/SymbolTableEx.cs index 018fc00..3c0ace3 100644 --- a/src/IFoxCAD.Cad/ExtensionMethod/SymbolTableEx.cs +++ b/src/IFoxCAD.Cad/ExtensionMethod/SymbolTableEx.cs @@ -146,6 +146,51 @@ public static ObjectId Add(this SymbolTable table, return table.Add(name, null, () => { return ents; }); } + /// + /// 添加属性到块定义 + /// + /// 块表 + /// 块定义id + /// 属性列表 + public static void AddAttsToBlocks(this SymbolTable table, ObjectId id, List atts) + { + table.Change(id, btr => + { + var attTags = new List(); + btr.GetEntities() + .ForEach(def => attTags.Add(def.Tag.ToUpper())); + foreach (AttributeDefinition att in atts) + { + if (!attTags.Contains(att.Tag.ToUpper())) + { + btr.AddEntity(att); + } + } + }); + } + /// + /// 添加属性到块定义 + /// + /// 块表 + /// 块定义名字 + /// 属性列表 + public static void AddAttsToBlocks(this SymbolTable table, string name, List atts) + { + table.Change(name, btr => + { + var attTags = new List(); + btr.GetEntities() + .ForEach(def => attTags.Add(def.Tag.ToUpper())); + foreach (AttributeDefinition att in atts) + { + if (!attTags.Contains(att.Tag.ToUpper())) + { + btr.AddEntity(att); + } + } + }); + } + /// /// 从文件中获取块定义 /// diff --git a/src/IFoxCAD.Cad/QuadTree/Graph.cs b/src/IFoxCAD.Cad/QuadTree/Graph.cs index 757352b..92f3516 100644 --- a/src/IFoxCAD.Cad/QuadTree/Graph.cs +++ b/src/IFoxCAD.Cad/QuadTree/Graph.cs @@ -298,7 +298,7 @@ private bool dfs(IGraphVertex current, { if (visited.Contains(edge.TargetVertexKey)) { - continue; + continue; // 改造这个搜索函数,当搜索闭合的时候,将闭合链存入结果列表 } if (dfs(edge.TargetVertex, visited, searchVetex)) diff --git a/src/IFoxCAD.Cad/QuadTree/IGraph.cs b/src/IFoxCAD.Cad/QuadTree/IGraph.cs index 2b65f30..2b6cbaf 100644 --- a/src/IFoxCAD.Cad/QuadTree/IGraph.cs +++ b/src/IFoxCAD.Cad/QuadTree/IGraph.cs @@ -84,7 +84,7 @@ public interface IGraphVertex /// 边类型 public interface IEdge { - W Weight() where W : IComparable; // 权重,ifoxcad里应该用不到 + W Weight() where W : IComparable; // 权重,ifoxcad里应该用不到,计划改为 curve /// /// 目标节点的键 /// diff --git a/tests/Test/testblock.cs b/tests/Test/testblock.cs index 95b3145..d402f1f 100644 --- a/tests/Test/testblock.cs +++ b/tests/Test/testblock.cs @@ -130,6 +130,19 @@ public void InsertBlockDef() tr.CurrentSpace.InsertBlock(new Point3d(-10, 0, 0), "test44"); } + [CommandMethod("addattsdef")] + public void AddAttsDef() + { + using var tr = new DBTrans(); + var blockid = Env.Editor.GetEntity("pick block:").ObjectId; + var blockref = tr.GetObject(blockid).BlockTableRecord; + + var att1 = new AttributeDefinition() { Position = new Point3d(20, 20, 0), Tag = "addtagTest1", Height = 1, TextString = "valueTest1" }; + var att2 = new AttributeDefinition() { Position = new Point3d(10, 12, 0), Tag = "tagTest2", Height = 1, TextString = "valueTest2" }; + + tr.BlockTable.AddAttsToBlocks(blockref, new List { att1, att2 }); + } + [CommandMethod("testblocknullbug")] public void TestBlockNullBug() { -- Gitee From b6b0f93c471bedf31d3bf19f34c44e706e562bc7 Mon Sep 17 00:00:00 2001 From: vicwjb <403009819@qq.com> Date: Sat, 9 Apr 2022 23:52:02 +0800 Subject: [PATCH 116/675] =?UTF-8?q?=E5=88=9D=E6=AD=A5=E5=9B=BE=E5=AE=9E?= =?UTF-8?q?=E7=8E=B0=EF=BC=8C=E6=84=9F=E8=A7=89=E6=80=A7=E8=83=BD=E4=B8=8D?= =?UTF-8?q?=E6=98=AF=E5=BE=88=E5=A5=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/IFoxCAD.Cad/QuadTree/Graph.cs | 373 ++++++++++++++--------------- src/IFoxCAD.Cad/QuadTree/IGraph.cs | 155 +++++++----- tests/Test/TestCurve.cs | 42 ++-- 3 files changed, 294 insertions(+), 276 deletions(-) diff --git a/src/IFoxCAD.Cad/QuadTree/Graph.cs b/src/IFoxCAD.Cad/QuadTree/Graph.cs index 92f3516..7851f66 100644 --- a/src/IFoxCAD.Cad/QuadTree/Graph.cs +++ b/src/IFoxCAD.Cad/QuadTree/Graph.cs @@ -6,16 +6,19 @@ namespace IFoxCAD.Cad /// 无权无向图实现 /// IEnumerable 枚举所有顶点。 /// - public class Graph : IGraph, IEnumerable + public class Graph : IGraph, IEnumerable { /// - /// 存储所有节点的字典,key为节点的类型,value为节点类型 + /// 存储所有节点的字典,key为顶点的类型,value为邻接表 /// /// - private Dictionary> vertices = new Dictionary>(); + private Dictionary> vertices = new (); + /// + /// 邻接边表 + /// + private Dictionary> edges = new (); public int VerticesCount => vertices.Count; - public bool IsWeightedGraph => false; public Graph() { @@ -23,180 +26,182 @@ public Graph() } /// - /// 返回一个参考顶点。 + /// 向该图添加一个边。 /// 时间复杂度: O(1). /// - private GraphVertex referenceVertex + public void AddEdge(Curve3d value!!) { - get - { - using (var enumerator = vertices.GetEnumerator()) - { - if (enumerator.MoveNext()) - { - return enumerator.Current.Value; - } - } + var start = value.StartPoint; + var end = value.EndPoint; - return null; + if (vertices.ContainsKey(start)) // 如果曲线的起点在邻接表字典里 + { + var nextVertex = new GraphVertex(end); + var edge = new GraphEdge(nextVertex, value); + vertices[start].Add(end); // 邻接表加曲线的终点 + edges[start].Add(edge); // 邻接边表加曲线 } - } - - IGraphVertex IGraph.ReferenceVertex => referenceVertex; - - - /// - /// 向该图添加一个新顶点。 - /// 时间复杂度: O(1). - /// - public void AddVertex(T value) - { - if (value == null) + else if (vertices.ContainsKey(end)) // 如果曲线的终点在邻接表字典里 { - throw new ArgumentNullException(); + var nextVertex = new GraphVertex(start); + var edge = new GraphEdge(nextVertex, value); + vertices[end].Add(start); // 邻接表加曲线的起点 + edges[end].Add(edge); // 邻接边表加曲线 + } + else + { + // 添加起点 + vertices.Add(start,new HashSet()); + vertices[start].Add(end); + edges.Add(start, new HashSet()); + edges[start].Add(new GraphEdge(new GraphVertex(end), value)); + // 添加终点 + vertices.Add(end,new HashSet()); + vertices[end].Add(start); + edges.Add(end, new HashSet()); + edges[end].Add(new GraphEdge(new GraphVertex(start),value)); } - var newVertex = new GraphVertex(value); - - vertices.Add(value, newVertex); } /// - /// 从此图中删除现有顶点。 - /// 时间复杂度: O(V) 其中 V 是顶点数。 + /// 从此图中删除一条边。 + /// 时间复杂度: O(2V*E) 其中 V 是顶点数,E是边数。 /// - public void RemoveVertex(T vertex) + public void RemoveEdge(Curve3d curve!!) { - if (vertex == null) - { - throw new ArgumentNullException(); - } + var start = curve.StartPoint; + var end = curve.EndPoint; - if (!vertices.ContainsKey(vertex)) + if (!vertices.ContainsKey(start) || !vertices.ContainsKey(end)) { - throw new Exception("顶点不在此图中。"); + throw new Exception("源或目标顶点不在此图中。"); } - foreach (var v in vertices[vertex].Edges) + if (!edges[start].Contains(new GraphEdge(new GraphVertex(end),curve)) + || !edges[end].Contains(new GraphEdge(new GraphVertex(start),curve))) { - v.Edges.Remove(vertices[vertex]); + throw new Exception("边不存在。"); } - vertices.Remove(vertex); + RemoveVertex(start); + RemoveVertex(end); } + /// - /// 向该图添加一条边。 - /// 时间复杂度: O(1). + /// 从此图中删除现有顶点。 + /// 时间复杂度: O(V*E) 其中 V 是顶点数,E是边数。 /// - public void AddEdge(T source, T dest) + public void RemoveVertex(Point3d vertex) { - if (source == null || dest == null) + if (!vertices.ContainsKey(vertex)) { - throw new ArgumentException(); + throw new Exception("顶点不在此图中。"); } - - if (!vertices.ContainsKey(source) || !vertices.ContainsKey(dest)) + // 删除邻接表里的vertex点,先删除后面的遍历可以少一轮 + vertices.Remove(vertex); + // 删除其他顶点的邻接表里的vertex点 + foreach (var item in vertices.Values) { - throw new Exception("源或目标顶点不在此图中。"); + item.Remove(vertex); } - if (vertices[source].Edges.Contains(vertices[dest]) - || vertices[dest].Edges.Contains(vertices[source])) + // 删除邻接边表里的vertex点,先删除后面的遍历可以少一轮 + edges.Remove(vertex); + // 删除其他顶点的邻接边表的指向vertex的边 + foreach (var item in edges.Values) { - throw new Exception("边已经存在。"); + foreach (var edge in item) + { + if (edge.TargetVertexKey == vertex) + { + item.Remove(edge); + } + } } + - vertices[source].Edges.Add(vertices[dest]); - vertices[dest].Edges.Add(vertices[source]); } /// - /// 从此图中删除一条边。 - /// 时间复杂度: O(1). + /// 向该图添加一个新顶点,但是无边。 + /// 时间复杂度: O(E). E是边数 /// - public void RemoveEdge(T source, T dest) + public void AddVertex(GraphVertex vertex!!) { - - if (source == null || dest == null) + if (vertices.ContainsKey(vertex.Key)) { - throw new ArgumentException(); + throw new Exception("顶点已经存在。"); } - if (!vertices.ContainsKey(source) || !vertices.ContainsKey(dest)) - { - throw new Exception("源或目标顶点不在此图中。"); - } + vertices.Add(vertex.Key, new HashSet()); + edges.Add(vertex.Key, new HashSet()); - if (!vertices[source].Edges.Contains(vertices[dest]) - || !vertices[dest].Edges.Contains(vertices[source])) + foreach (var item in vertex.Edges) { - throw new Exception("边不存在。"); + // 根据顶点的邻接边表构建图的邻接表 + vertices[vertex.Key].Add(item.Key); + // 根据顶点的邻接边表构建图的邻接边表 + edges[vertex.Key].Add(item.Value); } - - vertices[source].Edges.Remove(vertices[dest]); - vertices[dest].Edges.Remove(vertices[source]); } + + /// /// 我们在给定的来源和目的地之间是否有边? - /// 时间复杂度: O(1). + /// 时间复杂度: O(E).E 是边 /// - public bool HasEdge(T source, T dest) + public bool HasEdge(Point3d source, Point3d dest) { if (!vertices.ContainsKey(source) || !vertices.ContainsKey(dest)) { throw new ArgumentException("源或目标不在此图中。"); } - - return vertices[source].Edges.Contains(vertices[dest]) - && vertices[dest].Edges.Contains(vertices[source]); - } - /// - /// 节点的邻接表 - /// - /// - /// - public IEnumerable Edges(T vertex) - { - if (!vertices.ContainsKey(vertex)) + foreach (var item in edges[source]) { - throw new ArgumentException("顶点不在此图中。"); + if (item.TargetVertexKey == dest) + { + return true; + } } - - return vertices[vertex].Edges.Select(x => x.Key); + return false; + } + - public bool ContainsVertex(T value) + public bool ContainsVertex(Point3d value) { return vertices.ContainsKey(value); } - - public IGraphVertex GetVertex(T value) + public IGraphVertex GetVertex(Point3d key) { - return vertices[value]; + + var vertex = new GraphVertex(key); + foreach (var item in edges[key]) + { + vertex.AddEdge(item); + } + return vertex; + } + /// /// 克隆此图。目测是深克隆 /// - public Graph Clone() + public Graph Clone() { - var newGraph = new Graph(); - - foreach (var vertex in vertices) - { - newGraph.AddVertex(vertex.Key); - } + var newGraph = new Graph(); - foreach (var vertex in vertices) + foreach (var vertex in edges.Values) { - foreach (var edge in vertex.Value.Edges) + foreach (var item in vertex) { - newGraph.AddEdge(vertex.Value.Key, edge.Key); + newGraph.AddEdge(item.TargetEdge); } } - return newGraph; } @@ -205,34 +210,31 @@ public IEnumerator GetEnumerator() return vertices.Select(x => x.Key).GetEnumerator(); } - IEnumerator IEnumerable.GetEnumerator() + IEnumerator? IEnumerable.GetEnumerator() { - return GetEnumerator() as IEnumerator; + return GetEnumerator() as IEnumerator; } - IGraph IGraph.Clone() + IGraph IGraph.Clone() { return Clone(); } - public IEnumerable> VerticesAsEnumberable => (IEnumerable>)vertices.Select(x => x.Value); + + + public IEnumerable VerticesAsEnumberable => (IEnumerable)vertices.Select(x => x.Value); } /// /// 邻接表图实现的顶点。 - /// IEnumerable 枚举所有出边目标顶点。 + /// IEnumerable 枚举所有邻接点。 /// - public class GraphVertex : IEnumerable, IGraphVertex + public class GraphVertex : IEnumerable, IGraphVertex { - public T Key { get; set; } - /// - /// 邻接表 - /// todo:这里应该改为adjacencyList 来表示邻接点表 - /// - /// - public HashSet> Edges { get; } + public Point3d Key { get; private set; } + /// /// 邻接边表 /// 这个类的定义有问题: @@ -241,17 +243,20 @@ public class GraphVertex : IEnumerable, IGraphVertex /// /// /// - IEnumerable> IGraphVertex.Edges => (IEnumerable>)Edges.Select(x => new Edge(x, 1)); + public Dictionary Edges => new (); - public GraphVertex(T value) + public GraphVertex(Point3d value) { Key = value; - Edges = new HashSet>(); } - public IEdge GetEdge(IGraphVertex targetVertex) + public void AddEdge(IEdge edge) { - return new Edge(targetVertex, 1); + Edges.Add(edge.TargetVertexKey,edge); + } + public IEdge GetEdge(IGraphVertex targetVertex) + { + return Edges[targetVertex.Key]; } IEnumerator IEnumerable.GetEnumerator() @@ -259,93 +264,67 @@ IEnumerator IEnumerable.GetEnumerator() return GetEnumerator(); } - public IEnumerator GetEnumerator() + public IEnumerator GetEnumerator() { return Edges.Select(x => x.Key).GetEnumerator(); } + + } /// /// 深度优先搜索。 /// - public class DepthFirst - { - /// - /// 如果项目存在,则返回 true。 - /// 这个函数需要改写,在ifoxcad里返回的应该是所有的环 - /// - public bool Find(IGraph graph, T vertex) - { - return dfs(graph.ReferenceVertex, new HashSet(), vertex); - } - - /// - /// 递归 DFS。 - /// 这个函数需要改写,在ifoxcad里返回的应该是所有的环 - /// - private bool dfs(IGraphVertex current, - HashSet visited, T searchVetex) - { - visited.Add(current.Key); - - if (current.Key.Equals(searchVetex)) - { - return true; - } - - foreach (var edge in current.Edges) - { - if (visited.Contains(edge.TargetVertexKey)) - { - continue; // 改造这个搜索函数,当搜索闭合的时候,将闭合链存入结果列表 - } - - if (dfs(edge.TargetVertex, visited, searchVetex)) - { - return true; - } - } + //public class DepthFirst + //{ + // /// + // /// 如果项目存在,则返回 true。 + // /// 这个函数需要改写,在ifoxcad里返回的应该是所有的环 + // /// + // public bool Find(IGraph graph, T vertex) + // { + // return dfs(graph.ReferenceVertex, new HashSet(), vertex); + // } + + // /// + // /// 递归 DFS。 + // /// 这个函数需要改写,在ifoxcad里返回的应该是所有的环 + // /// + // private bool dfs(IGraphVertex current, + // HashSet visited, T searchVetex) + // { + // visited.Add(current.Key); + + // if (current.Key.Equals(searchVetex)) + // { + // return true; + // } + + // foreach (var edge in current.Edges) + // { + // if (visited.Contains(edge.TargetVertexKey)) + // { + // continue; // 改造这个搜索函数,当搜索闭合的时候,将闭合链存入结果列表 + // } + + // if (dfs(edge.TargetVertex, visited, searchVetex)) + // { + // return true; + // } + // } + + // return false; + // } + + + //} - return false; - } - } - // ===========另一个dfs实现,感觉比较好一点在于可以用委托干点其他的事情,和前一个可以结合一下 - public interface IDirectedWeightedGraph - { - int Count { get; } - - Vertex?[] Vertices { get; } - - void AddEdge(Vertex startVertex, Vertex endVertex, double weight); - - Vertex AddVertex(T data); - - bool AreAdjacent(Vertex startVertex, Vertex endVertex); - - double AdjacentDistance(Vertex startVertex, Vertex endVertex); - - IEnumerable?> GetNeighbors(Vertex vertex); - - void RemoveEdge(Vertex startVertex, Vertex endVertex); - - void RemoveVertex(Vertex vertex); - } - public interface IGraphSearch - { - /// - /// 从起始顶点遍历图。 - /// - ///图实例。 - ///搜索开始的顶点。 - ///每个图顶点需要执行的动作 - void VisitAll(IDirectedWeightedGraph graph, Vertex startVertex, Action>? action = null); - } - + /* /// /// Implementation of graph vertex. /// @@ -641,5 +620,5 @@ private void Dfs(IDirectedWeightedGraph graph, Vertex startVertex, Action< } } } - + */ } diff --git a/src/IFoxCAD.Cad/QuadTree/IGraph.cs b/src/IFoxCAD.Cad/QuadTree/IGraph.cs index 2b6cbaf..e3cf5f9 100644 --- a/src/IFoxCAD.Cad/QuadTree/IGraph.cs +++ b/src/IFoxCAD.Cad/QuadTree/IGraph.cs @@ -1,127 +1,166 @@ +using IFoxCAD.Cad; + namespace IFoxCAD.Cad { /// /// 无向图 /// /// 数据类型 - public interface IGraph + public interface IGraph { + ///// + ///// 是否为有权图 + ///// + ///// + //bool IsWeightedGraph { get; } /// - /// 是否为有权图 - /// - /// - bool IsWeightedGraph { get; } - /// - /// 节点的数量 + /// 顶点的数量 /// /// int VerticesCount { get; } + ///// + ///// 搜索的起始顶点 + ///// + ///// + // ReferenceVertex { get; } /// - /// 搜索的起始节点 + /// 是否存在顶点 /// - /// - IGraphVertex ReferenceVertex { get; } - /// - /// 是否存在节点 - /// - /// + /// 顶点键 /// - bool ContainsVertex(T key); + bool ContainsVertex(Point3d key); /// - /// 获取节点 + /// 获取顶点 /// /// /// - IGraphVertex GetVertex(T key); + IGraphVertex GetVertex(Point3d key); /// - /// 图节点的迭代器 + /// 顶点的迭代器 /// /// - IEnumerable> VerticesAsEnumberable { get; } + IEnumerable VerticesAsEnumberable { get; } /// /// 是否有边 /// - /// 源节点 - /// 目的节点 + /// 源顶点 + /// 目的顶点 /// - bool HasEdge(T source, T destination); + bool HasEdge(Point3d source, Point3d destination); /// /// 图克隆函数 /// /// - IGraph Clone(); + IGraph Clone(); } /// - /// 无向图节点 + /// 无向图顶点 /// 顶点的数据结构: /// key --- point3d 用于表示点的坐标和作为数据存储的key /// edges -- 邻接边表, 每个边的定义为,(边,下一顶点),这样遍历每个顶点的邻接边表时,就可以知道多少个邻接边及对应的邻接点 /// - /// 节点数据类型 - public interface IGraphVertex + /// 顶点数据类型 + public interface IGraphVertex { /// - /// 节点的键 + /// 顶点的键 /// /// - T Key { get; } + Point3d Key { get; } + ///// + ///// 顶点的邻接边表 + ///// + ///// + //List> Edges { get; } /// - /// 节点的邻接边表 + /// 获取顶点的邻接边 /// - /// - IEnumerable> Edges { get; } + /// 目标顶点 + /// + IEdge GetEdge(IGraphVertex targetVertex); /// - /// 获取节点的邻接边 + /// 添加邻接边 /// - /// 目标节点 - /// - IEdge GetEdge(IGraphVertex targetVertex); + /// 边类型 + void AddEdge(IEdge edge); } /// /// 无向图边 /// - /// 边类型 - public interface IEdge + /// 顶点类型 + /// 边的类型 + public interface IEdge { - W Weight() where W : IComparable; // 权重,ifoxcad里应该用不到,计划改为 curve /// - /// 目标节点的键 + /// 边 + /// + Curve3d TargetEdge { get; } + /// + /// 目标顶点的键 /// /// - T TargetVertexKey { get; } + Point3d TargetVertexKey { get; } /// - /// 目标节点 + /// 目标顶点 /// /// - IGraphVertex TargetVertex { get; } + IGraphVertex TargetVertex { get; } } /// /// 无向图中边的定义 /// /// 边的类型 /// 权重的类型 - internal class Edge : IEdge where C : IComparable + internal class GraphEdge : IEdge { - // 这里的权重是个泛型,所以是否可以用curve类型作为权重? - // 看来 这里可以用一个曲线作为边的一个值,取消权重的概念, - // 或者说当C 为int的时候 可以视为权重,当为其他类型的时候 就是一种边的数据类型 - // 所以这里就不能约束C的类型为IComparable - private object weight; - - internal Edge(IGraphVertex target, C weight) + // 这里的传入的两个参数分别为下一点和下一点之间的曲线 + internal GraphEdge(IGraphVertex target, Curve3d edge) { this.TargetVertex = target; - this.weight = weight; + this.TargetEdge = edge; } - public T TargetVertexKey => TargetVertex.Key; + public Point3d TargetVertexKey => TargetVertex.Key; - public IGraphVertex TargetVertex { get; private set; } + public IGraphVertex TargetVertex { get; private set; } - public W Weight() where W : IComparable - { - return (W)weight; - } + public Curve3d TargetEdge { get; private set; } } -} \ No newline at end of file +} + + +// ===================== +// 另一个实现的 +/* +public interface IDirectedWeightedGraph +{ + int Count { get; } + + Vertex?[] Vertices { get; } + + void AddEdge(Vertex startVertex, Vertex endVertex, double weight); + + Vertex AddVertex(T data); + + bool AreAdjacent(Vertex startVertex, Vertex endVertex); + + double AdjacentDistance(Vertex startVertex, Vertex endVertex); + + IEnumerable?> GetNeighbors(Vertex vertex); + + void RemoveEdge(Vertex startVertex, Vertex endVertex); + + void RemoveVertex(Vertex vertex); +} +public interface IGraphSearch +{ + /// + /// 从起始顶点遍历图。 + /// + ///图实例。 + ///搜索开始的顶点。 + ///每个图顶点需要执行的动作 + void VisitAll(IDirectedWeightedGraph graph, Vertex startVertex, Action>? action = null); +} +*/ \ No newline at end of file diff --git a/tests/Test/TestCurve.cs b/tests/Test/TestCurve.cs index 787150d..35bb2b3 100644 --- a/tests/Test/TestCurve.cs +++ b/tests/Test/TestCurve.cs @@ -8,27 +8,27 @@ public class TestGraph [CommandMethod("testgraph")] public void TestGraph1() { - var graph = new Graph(); - graph.AddVertex(1); - graph.AddVertex(2); - graph.AddVertex(3); - graph.AddVertex(4); - graph.AddVertex(5); - graph.AddVertex(6); - graph.AddVertex(7); - - - graph.AddEdge(1, 2); - graph.AddEdge(2, 3); - graph.AddEdge(3, 4); - graph.AddEdge(4, 5); - graph.AddEdge(5, 6); - graph.AddEdge(6, 7); - graph.AddEdge(1, 7); - graph.AddEdge(6,1); - graph.AddEdge(7, 2); - graph.AddEdge(5, 3); - Env.Print(graph); + //var graph = new Graph(); + //graph.AddVertex(1); + //graph.AddVertex(2); + //graph.AddVertex(3); + //graph.AddVertex(4); + //graph.AddVertex(5); + //graph.AddVertex(6); + //graph.AddVertex(7); + + + //graph.AddEdge(1, 2); + //graph.AddEdge(2, 3); + //graph.AddEdge(3, 4); + //graph.AddEdge(4, 5); + //graph.AddEdge(5, 6); + //graph.AddEdge(6, 7); + //graph.AddEdge(1, 7); + //graph.AddEdge(6,1); + //graph.AddEdge(7, 2); + //graph.AddEdge(5, 3); + //Env.Print(graph); } } public class TestCurve -- Gitee From 25eb971b19fc633792b1594a91f29b1912a94f2f Mon Sep 17 00:00:00 2001 From: vicwjb <403009819@qq.com> Date: Sun, 10 Apr 2022 13:45:36 +0800 Subject: [PATCH 117/675] =?UTF-8?q?=E5=AE=8C=E6=88=90=E5=9B=BE=E5=92=8Cdfs?= =?UTF-8?q?=E7=9A=84=E4=BB=A3=E7=A0=81=EF=BC=8C=E5=BC=80=E5=A7=8B=E6=B5=8B?= =?UTF-8?q?=E8=AF=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/IFoxCAD.Cad/QuadTree/Graph.cs | 688 +++++++++++++++-------------- src/IFoxCAD.Cad/QuadTree/IGraph.cs | 15 +- tests/Test/TestCurve.cs | 37 ++ 3 files changed, 409 insertions(+), 331 deletions(-) diff --git a/src/IFoxCAD.Cad/QuadTree/Graph.cs b/src/IFoxCAD.Cad/QuadTree/Graph.cs index 7851f66..e9e77b7 100644 --- a/src/IFoxCAD.Cad/QuadTree/Graph.cs +++ b/src/IFoxCAD.Cad/QuadTree/Graph.cs @@ -1,4 +1,6 @@ -using Exception = System.Exception; +using System; + +using Exception = System.Exception; namespace IFoxCAD.Cad { @@ -20,6 +22,8 @@ public class Graph : IGraph, IEnumerable public int VerticesCount => vertices.Count; + public Point3d ReferenceVertex => vertices.FirstOrDefault().Key; + public Graph() { //vertices = new Dictionary>(); @@ -187,6 +191,16 @@ public IGraphVertex GetVertex(Point3d key) } + public HashSet GetAdjacencyList(Point3d vertex) + { + return vertices[vertex]; + } + + public HashSet GetAdjacencyEdge(Point3d vertex) + { + return edges[vertex]; + } + /// /// 克隆此图。目测是深克隆 @@ -220,11 +234,11 @@ IGraph IGraph.Clone() return Clone(); } - + public IEnumerable VerticesAsEnumberable => (IEnumerable)vertices.Select(x => x.Value); + public IEnumerable Point3dAsEnumberable => vertices.Select(x => x.Key); - } /// @@ -276,349 +290,371 @@ public IEnumerator GetEnumerator() /// /// 深度优先搜索。 /// - //public class DepthFirst - //{ - // /// - // /// 如果项目存在,则返回 true。 - // /// 这个函数需要改写,在ifoxcad里返回的应该是所有的环 - // /// - // public bool Find(IGraph graph, T vertex) - // { - // return dfs(graph.ReferenceVertex, new HashSet(), vertex); - // } - - // /// - // /// 递归 DFS。 - // /// 这个函数需要改写,在ifoxcad里返回的应该是所有的环 - // /// - // private bool dfs(IGraphVertex current, - // HashSet visited, T searchVetex) - // { - // visited.Add(current.Key); - - // if (current.Key.Equals(searchVetex)) - // { - // return true; - // } - - // foreach (var edge in current.Edges) - // { - // if (visited.Contains(edge.TargetVertexKey)) - // { - // continue; // 改造这个搜索函数,当搜索闭合的时候,将闭合链存入结果列表 - // } - - // if (dfs(edge.TargetVertex, visited, searchVetex)) - // { - // return true; - // } - // } - - // return false; - // } - - - //} - - - -// ===========另一个dfs实现,感觉比较好一点在于可以用委托干点其他的事情,和前一个可以结合一下 - - /* - /// - /// Implementation of graph vertex. - /// - /// Generic Type. - public class Vertex + public class DepthFirst { + // 存储所有的边 + public List> Curve3ds { get; } = new List> (); /// - /// Gets vertex data. - /// - public T Data { get; } - - /// - /// Gets an index of the vertex in graph adjacency matrix. - /// - public int Index { get; } - - /// - /// Gets reference to the graph this vertex belongs to. - /// - public DirectedWeightedGraph? Graph { get; private set; } - - /// - /// Initializes a new instance of the class. - /// - /// Vertex data. Generic type. - /// Index of the vertex in graph adjacency matrix. - /// Graph this vertex belongs to. - public Vertex(T data, int index, DirectedWeightedGraph? graph) - { - Data = data; - Index = index; - Graph = graph; - } - - /// - /// Initializes a new instance of the class. - /// - /// Vertex data. Generic type. - /// Index of the vertex in graph adjacency matrix. - public Vertex(T data, int index) - { - Data = data; - Index = index; - } - - /// - /// Sets graph reference to the null. This method called when vertex removed from the graph. - /// - public void SetGraphNull() => Graph = null; - - /// - /// Override of base ToString method. Prints vertex data and index in graph adjacency matrix. - /// - /// String which contains vertex data and index in graph adjacency matrix.. - public override string ToString() => $"Vertex Data: {Data}, Index: {Index}"; - } - - /// - /// Implementation of the directed weighted graph via adjacency matrix. - /// - /// Generic Type. - public class DirectedWeightedGraph : IDirectedWeightedGraph - { - /// - /// Capacity of the graph, indicates the maximum amount of vertices. - /// - private readonly int capacity; - - /// - /// Adjacency matrix which reflects the edges between vertices and their weight. - /// Zero value indicates no edge between two vertices. - /// - private readonly double[,] adjacencyMatrix; - - /// - /// Initializes a new instance of the class. - /// - /// Capacity of the graph, indicates the maximum amount of vertices. - public DirectedWeightedGraph(int capacity) - { - ThrowIfNegativeCapacity(capacity); - - this.capacity = capacity; - Vertices = new Vertex[capacity]; - adjacencyMatrix = new double[capacity, capacity]; - Count = 0; - } - - /// - /// Gets list of vertices of the graph. - /// - public Vertex?[] Vertices { get; private set; } - - /// - /// Gets current amount of vertices in the graph. - /// - public int Count { get; private set; } - - /// - /// Adds new vertex to the graph. - /// - /// Data of the vertex. - /// Reference to created vertex. - public Vertex AddVertex(T data) - { - ThrowIfOverflow(); - var vertex = new Vertex(data, Count, this); - Vertices[Count] = vertex; - Count++; - return vertex; - } - - /// - /// Creates an edge between two vertices of the graph. - /// - /// Vertex, edge starts at. - /// Vertex, edge ends at. - /// Double weight of an edge. - public void AddEdge(Vertex startVertex, Vertex endVertex, double weight) - { - ThrowIfVertexNotInGraph(startVertex); - ThrowIfVertexNotInGraph(endVertex); - - ThrowIfWeightZero(weight); - - var currentEdgeWeight = adjacencyMatrix[startVertex.Index, endVertex.Index]; - - ThrowIfEdgeExists(currentEdgeWeight); - - adjacencyMatrix[startVertex.Index, endVertex.Index] = weight; - } - - /// - /// Removes vertex from the graph. + /// 如果项目存在,则返回 true。 + /// 这个函数需要改写,在ifoxcad里返回的应该是所有的环 /// - /// Vertex to be removed. - public void RemoveVertex(Vertex vertex) + public void FindAll(IGraph graph) { - ThrowIfVertexNotInGraph(vertex); - - Vertices[vertex.Index] = null; - vertex.SetGraphNull(); - - for (var i = 0; i < Count; i++) + foreach (var item in graph.Point3dAsEnumberable) { - adjacencyMatrix[i, vertex.Index] = 0; - adjacencyMatrix[vertex.Index, i] = 0; - } - - Count--; - } - - /// - /// Removes edge between two vertices. - /// - /// Vertex, edge starts at. - /// Vertex, edge ends at. - public void RemoveEdge(Vertex startVertex, Vertex endVertex) - { - ThrowIfVertexNotInGraph(startVertex); - ThrowIfVertexNotInGraph(endVertex); - adjacencyMatrix[startVertex.Index, endVertex.Index] = 0; - } - - /// - /// Gets a neighbors of particular vertex. - /// - /// Vertex, method gets list of neighbors for. - /// Collection of the neighbors of particular vertex. - public IEnumerable?> GetNeighbors(Vertex vertex) - { - ThrowIfVertexNotInGraph(vertex); - - for (var i = 0; i < Count; i++) - { - if (adjacencyMatrix[vertex.Index, i] != 0) + var curves = new LoopList(); + var visited = new List(); + if (dfs(graph,item,visited,item)) { - yield return Vertices[i]; + for (int i = 0; i < visited.Count - 1; i++) + { + var cur = visited[i]; + var next = visited[i + 1]; + var curedge = graph.GetAdjacencyEdge(cur); + foreach (var edge in curedge) + { + if (edge.TargetVertexKey == next) + { + curves.Add(edge.TargetEdge); + } + } + + } } + Curve3ds.Add(curves); } + + } /// - /// Returns true, if there is an edge between two vertices. - /// - /// Vertex, edge starts at. - /// Vertex, edge ends at. - /// True if edge exists, otherwise false. - public bool AreAdjacent(Vertex startVertex, Vertex endVertex) - { - ThrowIfVertexNotInGraph(startVertex); - ThrowIfVertexNotInGraph(endVertex); - - return adjacencyMatrix[startVertex.Index, endVertex.Index] != 0; - } - - /// - /// Return the distance between two vertices in the graph. + /// 递归 DFS。 + /// 这个函数需要改写,在ifoxcad里返回的应该是所有的环 /// - /// first vertex in edge. - /// secnod vertex in edge. - /// distance between the two. - public double AdjacentDistance(Vertex startVertex, Vertex endVertex) - { - if (AreAdjacent(startVertex, endVertex)) - { - return adjacencyMatrix[startVertex.Index, endVertex.Index]; - } - - return 0; - } - - private static void ThrowIfNegativeCapacity(int capacity) + private bool dfs(IGraph graph, Point3d current, List visited, Point3d search) { - if (capacity < 0) + + visited.Add(current); + if (current == search && visited.Count >= 2) { - throw new InvalidOperationException("Graph capacity should always be a non-negative integer."); + return true; } - } - private static void ThrowIfWeightZero(double weight) - { - if (weight.Equals(0.0d)) + // 改造这个搜索函数,当搜索闭合的时候,将闭合链存入结果列表 + foreach (var edge in graph.GetAdjacencyList(current)) { - throw new InvalidOperationException("Edge weight cannot be zero."); - } - } - - private static void ThrowIfEdgeExists(double currentEdgeWeight) - { - if (!currentEdgeWeight.Equals(0.0d)) - { - throw new InvalidOperationException($"Vertex already exists: {currentEdgeWeight}"); - } - } - - private void ThrowIfOverflow() - { - if (Count == capacity) - { - throw new InvalidOperationException("Graph overflow."); - } - } - - private void ThrowIfVertexNotInGraph(Vertex vertex) - { - if (vertex.Graph != this) - { - throw new InvalidOperationException($"Vertex does not belong to graph: {vertex}."); - } - } - } - /// - /// 深度优先搜索 -遍历图的算法。 - /// 算法从用户选择的根节点开始。 - /// 算法在回溯之前尽可能沿着每个分支探索。 - /// - /// 顶点数据类型。 - public class DepthFirstSearch : IGraphSearch where T : IComparable - { - /// - /// 从起始顶点遍历图。 - /// - ///图实例。 - ///搜索开始的顶点。 - ///每个图顶点需要执行的动作 - public void VisitAll(IDirectedWeightedGraph graph, Vertex startVertex, Action>? action = default) - { - Dfs(graph, startVertex, action, new HashSet>()); - } - - /// - /// 从起始顶点遍历图。 - /// - ///图实例。 - ///搜索开始的顶点。 - ///每个图顶点需要执行的动作 - ///具有访问顶点的哈希集。 - private void Dfs(IDirectedWeightedGraph graph, Vertex startVertex, Action>? action, HashSet> visited) - { - action?.Invoke(startVertex); - - visited.Add(startVertex); - - foreach (var vertex in graph.GetNeighbors(startVertex)) - { - if (vertex == null || visited.Contains(vertex)) + if (visited.Contains(edge)) { - continue; + continue; + } + if (dfs(graph,edge,visited,search)) + { + return true; } - - Dfs(graph, vertex!, action, visited); } + return false; } } - */ + + + + // ===========另一个dfs实现,感觉比较好一点在于可以用委托干点其他的事情,和前一个可以结合一下 + + /* + /// + /// Implementation of graph vertex. + /// + /// Generic Type. + public class Vertex + { + /// + /// Gets vertex data. + /// + public T Data { get; } + + /// + /// Gets an index of the vertex in graph adjacency matrix. + /// + public int Index { get; } + + /// + /// Gets reference to the graph this vertex belongs to. + /// + public DirectedWeightedGraph? Graph { get; private set; } + + /// + /// Initializes a new instance of the class. + /// + /// Vertex data. Generic type. + /// Index of the vertex in graph adjacency matrix. + /// Graph this vertex belongs to. + public Vertex(T data, int index, DirectedWeightedGraph? graph) + { + Data = data; + Index = index; + Graph = graph; + } + + /// + /// Initializes a new instance of the class. + /// + /// Vertex data. Generic type. + /// Index of the vertex in graph adjacency matrix. + public Vertex(T data, int index) + { + Data = data; + Index = index; + } + + /// + /// Sets graph reference to the null. This method called when vertex removed from the graph. + /// + public void SetGraphNull() => Graph = null; + + /// + /// Override of base ToString method. Prints vertex data and index in graph adjacency matrix. + /// + /// String which contains vertex data and index in graph adjacency matrix.. + public override string ToString() => $"Vertex Data: {Data}, Index: {Index}"; + } + + /// + /// Implementation of the directed weighted graph via adjacency matrix. + /// + /// Generic Type. + public class DirectedWeightedGraph : IDirectedWeightedGraph + { + /// + /// Capacity of the graph, indicates the maximum amount of vertices. + /// + private readonly int capacity; + + /// + /// Adjacency matrix which reflects the edges between vertices and their weight. + /// Zero value indicates no edge between two vertices. + /// + private readonly double[,] adjacencyMatrix; + + /// + /// Initializes a new instance of the class. + /// + /// Capacity of the graph, indicates the maximum amount of vertices. + public DirectedWeightedGraph(int capacity) + { + ThrowIfNegativeCapacity(capacity); + + this.capacity = capacity; + Vertices = new Vertex[capacity]; + adjacencyMatrix = new double[capacity, capacity]; + Count = 0; + } + + /// + /// Gets list of vertices of the graph. + /// + public Vertex?[] Vertices { get; private set; } + + /// + /// Gets current amount of vertices in the graph. + /// + public int Count { get; private set; } + + /// + /// Adds new vertex to the graph. + /// + /// Data of the vertex. + /// Reference to created vertex. + public Vertex AddVertex(T data) + { + ThrowIfOverflow(); + var vertex = new Vertex(data, Count, this); + Vertices[Count] = vertex; + Count++; + return vertex; + } + + /// + /// Creates an edge between two vertices of the graph. + /// + /// Vertex, edge starts at. + /// Vertex, edge ends at. + /// Double weight of an edge. + public void AddEdge(Vertex startVertex, Vertex endVertex, double weight) + { + ThrowIfVertexNotInGraph(startVertex); + ThrowIfVertexNotInGraph(endVertex); + + ThrowIfWeightZero(weight); + + var currentEdgeWeight = adjacencyMatrix[startVertex.Index, endVertex.Index]; + + ThrowIfEdgeExists(currentEdgeWeight); + + adjacencyMatrix[startVertex.Index, endVertex.Index] = weight; + } + + /// + /// Removes vertex from the graph. + /// + /// Vertex to be removed. + public void RemoveVertex(Vertex vertex) + { + ThrowIfVertexNotInGraph(vertex); + + Vertices[vertex.Index] = null; + vertex.SetGraphNull(); + + for (var i = 0; i < Count; i++) + { + adjacencyMatrix[i, vertex.Index] = 0; + adjacencyMatrix[vertex.Index, i] = 0; + } + + Count--; + } + + /// + /// Removes edge between two vertices. + /// + /// Vertex, edge starts at. + /// Vertex, edge ends at. + public void RemoveEdge(Vertex startVertex, Vertex endVertex) + { + ThrowIfVertexNotInGraph(startVertex); + ThrowIfVertexNotInGraph(endVertex); + adjacencyMatrix[startVertex.Index, endVertex.Index] = 0; + } + + /// + /// Gets a neighbors of particular vertex. + /// + /// Vertex, method gets list of neighbors for. + /// Collection of the neighbors of particular vertex. + public IEnumerable?> GetNeighbors(Vertex vertex) + { + ThrowIfVertexNotInGraph(vertex); + + for (var i = 0; i < Count; i++) + { + if (adjacencyMatrix[vertex.Index, i] != 0) + { + yield return Vertices[i]; + } + } + } + + /// + /// Returns true, if there is an edge between two vertices. + /// + /// Vertex, edge starts at. + /// Vertex, edge ends at. + /// True if edge exists, otherwise false. + public bool AreAdjacent(Vertex startVertex, Vertex endVertex) + { + ThrowIfVertexNotInGraph(startVertex); + ThrowIfVertexNotInGraph(endVertex); + + return adjacencyMatrix[startVertex.Index, endVertex.Index] != 0; + } + + /// + /// Return the distance between two vertices in the graph. + /// + /// first vertex in edge. + /// secnod vertex in edge. + /// distance between the two. + public double AdjacentDistance(Vertex startVertex, Vertex endVertex) + { + if (AreAdjacent(startVertex, endVertex)) + { + return adjacencyMatrix[startVertex.Index, endVertex.Index]; + } + + return 0; + } + + private static void ThrowIfNegativeCapacity(int capacity) + { + if (capacity < 0) + { + throw new InvalidOperationException("Graph capacity should always be a non-negative integer."); + } + } + + private static void ThrowIfWeightZero(double weight) + { + if (weight.Equals(0.0d)) + { + throw new InvalidOperationException("Edge weight cannot be zero."); + } + } + + private static void ThrowIfEdgeExists(double currentEdgeWeight) + { + if (!currentEdgeWeight.Equals(0.0d)) + { + throw new InvalidOperationException($"Vertex already exists: {currentEdgeWeight}"); + } + } + + private void ThrowIfOverflow() + { + if (Count == capacity) + { + throw new InvalidOperationException("Graph overflow."); + } + } + + private void ThrowIfVertexNotInGraph(Vertex vertex) + { + if (vertex.Graph != this) + { + throw new InvalidOperationException($"Vertex does not belong to graph: {vertex}."); + } + } + } + /// + /// 深度优先搜索 -遍历图的算法。 + /// 算法从用户选择的根节点开始。 + /// 算法在回溯之前尽可能沿着每个分支探索。 + /// + /// 顶点数据类型。 + public class DepthFirstSearch : IGraphSearch where T : IComparable + { + /// + /// 从起始顶点遍历图。 + /// + ///图实例。 + ///搜索开始的顶点。 + ///每个图顶点需要执行的动作 + public void VisitAll(IDirectedWeightedGraph graph, Vertex startVertex, Action>? action = default) + { + Dfs(graph, startVertex, action, new HashSet>()); + } + + /// + /// 从起始顶点遍历图。 + /// + ///图实例。 + ///搜索开始的顶点。 + ///每个图顶点需要执行的动作 + ///具有访问顶点的哈希集。 + private void Dfs(IDirectedWeightedGraph graph, Vertex startVertex, Action>? action, HashSet> visited) + { + action?.Invoke(startVertex); + + visited.Add(startVertex); + + foreach (var vertex in graph.GetNeighbors(startVertex)) + { + if (vertex == null || visited.Contains(vertex)) + { + continue; + } + + Dfs(graph, vertex!, action, visited); + } + } + } + */ } diff --git a/src/IFoxCAD.Cad/QuadTree/IGraph.cs b/src/IFoxCAD.Cad/QuadTree/IGraph.cs index e3cf5f9..ddb1345 100644 --- a/src/IFoxCAD.Cad/QuadTree/IGraph.cs +++ b/src/IFoxCAD.Cad/QuadTree/IGraph.cs @@ -18,11 +18,11 @@ public interface IGraph /// /// int VerticesCount { get; } - ///// - ///// 搜索的起始顶点 - ///// - ///// - // ReferenceVertex { get; } + /// + /// 搜索的起始顶点 + /// + /// + Point3d ReferenceVertex { get; } /// /// 是否存在顶点 /// @@ -40,6 +40,8 @@ public interface IGraph /// /// IEnumerable VerticesAsEnumberable { get; } + IEnumerable Point3dAsEnumberable { get; } + /// /// 是否有边 /// @@ -52,6 +54,9 @@ public interface IGraph /// /// IGraph Clone(); + + HashSet GetAdjacencyList(Point3d vertex); + HashSet GetAdjacencyEdge(Point3d vertex); } /// diff --git a/tests/Test/TestCurve.cs b/tests/Test/TestCurve.cs index 35bb2b3..d571d41 100644 --- a/tests/Test/TestCurve.cs +++ b/tests/Test/TestCurve.cs @@ -5,9 +5,46 @@ namespace Test public class TestGraph { + [CommandMethod("testpointindict")] + public void TestPointInDict() + { + var pt1 = new Point3d(0.0255, 0.452, 0); + var pt2 = new Point3d(0.0255001, 0.452003, 0); + var pt3 = new Point3d(0.0255002, 0.4520001, 0); + var pt4 = new Point3d(0.0255450, 0.45287893, 0); + var pt5 = new Point3d(0.02554935, 0.452092375, 0); + var dict = new Dictionary(); + dict.Add(pt1, 1); + dict.Add(pt2, 2); + dict.Add(pt3, 3); + dict.Add(pt4, 4); + dict.Add(pt5, 5); + Env.Print(dict[pt1]); + } + + + + [CommandMethod("testgraph")] public void TestGraph1() { + var ents = Env.Editor.SSGet().Value.GetEntities(); + var graph = new IFoxCAD.Cad.Graph(); + ents.ForEach(ent => graph.AddEdge(ent.GetGeCurve())); + var dfs = new DepthFirst(); + dfs.FindAll(graph); + var res = dfs.Curve3ds.Select(loop => + { + var curves = loop.ToArray(); + var comcur = new CompositeCurve3d(curves); + return comcur.ToCurve(); + }); + using var tr = new DBTrans(); + res.ForEach((i, t) => t.ForWrite(e => e.ColorIndex = i + 1)); + tr.CurrentSpace.AddEntity(res); + + + //var graph = new Graph(); //graph.AddVertex(1); //graph.AddVertex(2); -- Gitee From ed6cfbc3af5c69b259c7fafa40e0d57f34ee538a Mon Sep 17 00:00:00 2001 From: vicwjb <403009819@qq.com> Date: Sun, 10 Apr 2022 21:09:25 +0800 Subject: [PATCH 118/675] =?UTF-8?q?=E4=BF=AEgraph=E7=9A=84bug?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/IFoxCAD.Cad/QuadTree/Graph.cs | 679 ++++++++++--------------- src/IFoxCAD.Cad/QuadTree/IGraph.cs | 92 +--- src/IFoxCAD.Cad/QuadTree/othergraph.cs | 323 ++++++++++++ tests/Test/TestCurve.cs | 32 +- 4 files changed, 619 insertions(+), 507 deletions(-) create mode 100644 src/IFoxCAD.Cad/QuadTree/othergraph.cs diff --git a/src/IFoxCAD.Cad/QuadTree/Graph.cs b/src/IFoxCAD.Cad/QuadTree/Graph.cs index e9e77b7..50daf82 100644 --- a/src/IFoxCAD.Cad/QuadTree/Graph.cs +++ b/src/IFoxCAD.Cad/QuadTree/Graph.cs @@ -1,104 +1,57 @@ -using System; + using Exception = System.Exception; -namespace IFoxCAD.Cad +namespace IFoxCAD.Cad.FirstGraph { /// /// 无权无向图实现 /// IEnumerable 枚举所有顶点。 /// - public class Graph : IGraph, IEnumerable + public class Graph : IGraph, IEnumerable { /// /// 存储所有节点的字典,key为顶点的类型,value为邻接表 /// /// - private Dictionary> vertices = new (); + private Dictionary> vertices = new (); /// /// 邻接边表 /// - private Dictionary> edges = new (); - + private Dictionary> edges = new (); public int VerticesCount => vertices.Count; - public Point3d ReferenceVertex => vertices.FirstOrDefault().Key; public Graph() { //vertices = new Dictionary>(); + } /// - /// 向该图添加一个边。 - /// 时间复杂度: O(1). - /// - public void AddEdge(Curve3d value!!) - { - var start = value.StartPoint; - var end = value.EndPoint; - - if (vertices.ContainsKey(start)) // 如果曲线的起点在邻接表字典里 - { - var nextVertex = new GraphVertex(end); - var edge = new GraphEdge(nextVertex, value); - vertices[start].Add(end); // 邻接表加曲线的终点 - edges[start].Add(edge); // 邻接边表加曲线 - } - else if (vertices.ContainsKey(end)) // 如果曲线的终点在邻接表字典里 - { - var nextVertex = new GraphVertex(start); - var edge = new GraphEdge(nextVertex, value); - vertices[end].Add(start); // 邻接表加曲线的起点 - edges[end].Add(edge); // 邻接边表加曲线 - } - else - { - // 添加起点 - vertices.Add(start,new HashSet()); - vertices[start].Add(end); - edges.Add(start, new HashSet()); - edges[start].Add(new GraphEdge(new GraphVertex(end), value)); - // 添加终点 - vertices.Add(end,new HashSet()); - vertices[end].Add(start); - edges.Add(end, new HashSet()); - edges[end].Add(new GraphEdge(new GraphVertex(start),value)); - } - - } - - /// - /// 从此图中删除一条边。 - /// 时间复杂度: O(2V*E) 其中 V 是顶点数,E是边数。 + /// 向该图添加一个新顶点,但是无边。 + /// 时间复杂度: O(E). E是边数 /// - public void RemoveEdge(Curve3d curve!!) + public void AddVertex(Point3d pt) { - var start = curve.StartPoint; - var end = curve.EndPoint; - - if (!vertices.ContainsKey(start) || !vertices.ContainsKey(end)) + var vertex = new GraphVertex(pt); + if (vertices.ContainsKey(vertex)) { - throw new Exception("源或目标顶点不在此图中。"); + throw new Exception("顶点已经存在。"); } - if (!edges[start].Contains(new GraphEdge(new GraphVertex(end),curve)) - || !edges[end].Contains(new GraphEdge(new GraphVertex(start),curve))) - { - throw new Exception("边不存在。"); - } + vertices.Add(vertex, new HashSet()); + edges.Add(vertex, new HashSet()); - RemoveVertex(start); - RemoveVertex(end); } - /// /// 从此图中删除现有顶点。 /// 时间复杂度: O(V*E) 其中 V 是顶点数,E是边数。 /// - public void RemoveVertex(Point3d vertex) + public void RemoveVertex(Point3d pt) { + var vertex = new GraphVertex(pt); if (!vertices.ContainsKey(vertex)) { throw new Exception("顶点不在此图中。"); @@ -118,46 +71,147 @@ public void RemoveVertex(Point3d vertex) { foreach (var edge in item) { - if (edge.TargetVertexKey == vertex) + if (vertex.Equals(edge.TargetVertex)) { item.Remove(edge); } } } - - } + /// - /// 向该图添加一个新顶点,但是无边。 - /// 时间复杂度: O(E). E是边数 + /// 向该图添加一个边。 + /// 时间复杂度: O(1). /// - public void AddVertex(GraphVertex vertex!!) + public void AddEdge(Curve3d value!!) { - if (vertices.ContainsKey(vertex.Key)) + // 函数有问题,有一个端点在图里时,另一个点应该新增个顶点, + var start = new GraphVertex(value.StartPoint); + var end = new GraphVertex(value.EndPoint); + + + if (vertices.ContainsKey(start) && !vertices.ContainsKey(end)) // 如果曲线的起点在邻接表字典里,终点不在 { - throw new Exception("顶点已经存在。"); + var edge = new GraphEdge(end, value); + vertices[start].Add(end); // 邻接表加曲线的终点 + edges[start].Add(edge); // 邻接边表加曲线 + + // 邻接表字典添加终点 + vertices.Add(end, new HashSet()); + // 邻接表加入起点 + vertices[end].Add(start); + // 邻接边表字典添加终点 + edges.Add(end, new HashSet()); + // 邻接边表加入起点 + edges[end].Add(new GraphEdge(start, value)); + } + else if (vertices.ContainsKey(end) && !vertices.ContainsKey(start)) // 如果曲线的终点在邻接表字典里,起点不在 + { + + var edge = new GraphEdge(start, value); + vertices[end].Add(start); // 邻接表加曲线的起点 + edges[end].Add(edge); // 邻接边表加曲线 - vertices.Add(vertex.Key, new HashSet()); - edges.Add(vertex.Key, new HashSet()); + // 邻接表字典添加起点 + vertices.Add(start, new HashSet()); + // 邻接表加入终点 + vertices[start].Add(end); + // 邻接边表字典添加起点 + edges.Add(start, new HashSet()); + // 邻接边表加入终点 + edges[start].Add(new GraphEdge(end, value)); + } + else if (vertices.ContainsKey(start) && vertices.ContainsKey(end)) // 起点和终点同时在 + { + var edge = new GraphEdge(end, value); + vertices[start].Add(end); // 邻接表加曲线的终点 + edges[start].Add(edge); // 邻接边表加曲线 - foreach (var item in vertex.Edges) + var edge1 = new GraphEdge(start, value); + vertices[end].Add(start); // 邻接表加曲线的起点 + edges[end].Add(edge1); // 邻接边表加曲线 + } + else { - // 根据顶点的邻接边表构建图的邻接表 - vertices[vertex.Key].Add(item.Key); - // 根据顶点的邻接边表构建图的邻接边表 - edges[vertex.Key].Add(item.Value); + // 邻接表字典添加起点 + vertices.Add(start,new HashSet()); + // 邻接表加入终点 + vertices[start].Add(end); + // 邻接边表字典添加起点 + edges.Add(start, new HashSet()); + // 邻接边表加入终点 + edges[start].Add(new GraphEdge(end, value)); + + // 邻接表字典添加终点 + vertices.Add(end,new HashSet()); + // 邻接表加入起点 + vertices[end].Add(start); + // 邻接边表字典添加终点 + edges.Add(end, new HashSet()); + // 邻接边表加入起点 + edges[end].Add(new GraphEdge(start,value)); } + } + /// + /// 从此图中删除一条边。 + /// 时间复杂度: O(2V*E) 其中 V 是顶点数,E是边数。 + /// + public void RemoveEdge(Curve3d curve!!) + { + var start = new GraphVertex(curve.StartPoint); + var end = new GraphVertex(curve.EndPoint); + + if (!vertices.ContainsKey(start) || !vertices.ContainsKey(end)) + { + throw new Exception("源或目标顶点不在此图中。"); + } + + if (!edges[start].Contains(new GraphEdge(end,curve)) + || !edges[end].Contains(new GraphEdge(start,curve))) + { + throw new Exception("边不存在。"); + } + // 曲线的起点邻接表里删除终点 + vertices[start].Remove(end); + // 曲线的终点邻接表里删除起点 + vertices[end].Remove(start); + // 曲线的起点邻接边表里删除终点邻接边 + edges[start].Remove(new GraphEdge(end,curve)); + // 曲线的终点邻接边表里删除起点邻接边 + edges[end].Remove(new GraphEdge(start,curve)); + + // 如果 邻接表的长度为0,说明为孤立的顶点就删除 + if (vertices[start].Count == 0) + { + vertices.Remove(start); + edges.Remove(start); + } + if (vertices[end].Count == 0) + { + vertices.Remove(end); + edges.Remove(end); + + } + + + } + + + + + + /// /// 我们在给定的来源和目的地之间是否有边? /// 时间复杂度: O(E).E 是边 /// - public bool HasEdge(Point3d source, Point3d dest) + public bool HasEdge(IGraphVertex source, IGraphVertex dest) { if (!vertices.ContainsKey(source) || !vertices.ContainsKey(dest)) { @@ -165,7 +219,7 @@ public bool HasEdge(Point3d source, Point3d dest) } foreach (var item in edges[source]) { - if (item.TargetVertexKey == dest) + if (item.TargetVertex == dest) { return true; } @@ -175,28 +229,18 @@ public bool HasEdge(Point3d source, Point3d dest) } - public bool ContainsVertex(Point3d value) + public bool ContainsVertex(IGraphVertex value) { return vertices.ContainsKey(value); } - public IGraphVertex GetVertex(Point3d key) - { - - var vertex = new GraphVertex(key); - foreach (var item in edges[key]) - { - vertex.AddEdge(item); - } - return vertex; - - } + - public HashSet GetAdjacencyList(Point3d vertex) + public HashSet GetAdjacencyList(IGraphVertex vertex) { return vertices[vertex]; } - public HashSet GetAdjacencyEdge(Point3d vertex) + public HashSet GetAdjacencyEdge(IGraphVertex vertex) { return edges[vertex]; } @@ -224,9 +268,9 @@ public IEnumerator GetEnumerator() return vertices.Select(x => x.Key).GetEnumerator(); } - IEnumerator? IEnumerable.GetEnumerator() + IEnumerator? IEnumerable.GetEnumerator() { - return GetEnumerator() as IEnumerator; + return GetEnumerator() as IEnumerator; } IGraph IGraph.Clone() @@ -236,56 +280,143 @@ IGraph IGraph.Clone() - public IEnumerable VerticesAsEnumberable => (IEnumerable)vertices.Select(x => x.Value); - public IEnumerable Point3dAsEnumberable => vertices.Select(x => x.Key); + public IEnumerable VerticesAsEnumberable => + vertices.Select(x => x.Key); + + public virtual string ToReadable() + { + int i = 1; + string output = string.Empty; + + foreach (var node in vertices) + { + var adjacents = string.Empty; + + output = String.Format("{1}\r\n{0}-{2}: [",i, output, node.Key.Data.ToString()); + + foreach (var adjacentNode in node.Value) + adjacents = String.Format("{0}{1},", adjacents, adjacentNode.Data.ToString()); + + if (adjacents.Length > 0) + adjacents = adjacents.TrimEnd(new char[] { ',', ' ' }); + + output = String.Format("{0}{1}]", output, adjacents); + i++; + } + return output; + } } /// /// 邻接表图实现的顶点。 /// IEnumerable 枚举所有邻接点。 /// - public class GraphVertex : IEnumerable, IGraphVertex + public struct GraphVertex : IGraphVertex, IEquatable { - public Point3d Key { get; private set; } - - /// - /// 邻接边表 - /// 这个类的定义有问题: - /// todo:邻接边表和邻接表的名字是一样的,造成误解 - /// - /// - /// - /// - public Dictionary Edges => new (); + public Point3d Data { get; private set; } + public GraphVertex(Point3d value) { - Key = value; + Data = value; } - public void AddEdge(IEdge edge) + public bool Equals(GraphVertex other) { - Edges.Add(edge.TargetVertexKey,edge); + return Data.IsEqualTo(other.Data, new Tolerance(1e-6,1e-6)); } - public IEdge GetEdge(IGraphVertex targetVertex) + public override bool Equals(Object obj) { - return Edges[targetVertex.Key]; + if (obj is null) + return false; + if (obj is not GraphVertex personObj) + return false; + else + return Equals(personObj); } - IEnumerator IEnumerable.GetEnumerator() + public override int GetHashCode() { - return GetEnumerator(); + // 原来的代码 不起作用,那么就转字符串算 + //return (Data.X., Data.Y, Data.Z).GetHashCode(); + + return (Data.X.ToString("n6"), Data.Y.ToString("n6"), Data.Z.ToString("n6")).GetHashCode(); } + public static bool operator ==(GraphVertex person1, GraphVertex person2) + { + if (((object)person1) == null || ((object)person2) == null) + return Object.Equals(person1, person2); - public IEnumerator GetEnumerator() + return person1.Equals(person2); + } + public static bool operator !=(GraphVertex person1, GraphVertex person2) { - return Edges.Select(x => x.Key).GetEnumerator(); + if (((object)person1) == null || ((object)person2) == null) + return !Object.Equals(person1, person2); + + return !(person1.Equals(person2)); } - } + /// + /// 无向图中边的定义 + /// + /// 边的类型 + /// 权重的类型 + public class GraphEdge : IEdge, IEquatable + { + // 这里的传入的两个参数分别为下一点和下一点之间的曲线 + internal GraphEdge(IGraphVertex target, Curve3d edge) + { + this.TargetVertex = target; + this.TargetEdge = edge; + } + + public IGraphVertex TargetVertex { get; private set; } + + public Curve3d TargetEdge { get; private set; } + + public bool Equals(GraphEdge other) + { + if (other is null) + { + return false; + } + return TargetVertex == other.TargetVertex && TargetEdge == other.TargetEdge; + } + public override bool Equals(Object obj) + { + if (obj is null) + return false; + if (obj is not GraphEdge personObj) + return false; + else + return Equals(personObj); + } + + public override int GetHashCode() + { + return (TargetVertex.GetHashCode(), TargetEdge.GetHashCode()).GetHashCode(); + } + public static bool operator ==(GraphEdge person1, GraphEdge person2) + { + if (((object)person1) == null || ((object)person2) == null) + return Object.Equals(person1, person2); + + return person1.Equals(person2); + } + public static bool operator !=(GraphEdge person1, GraphEdge person2) + { + if (((object)person1) == null || ((object)person2) == null) + return !Object.Equals(person1, person2); + + return !(person1.Equals(person2)); + } + + + } /// /// 深度优先搜索。 @@ -300,10 +431,10 @@ public class DepthFirst /// public void FindAll(IGraph graph) { - foreach (var item in graph.Point3dAsEnumberable) + foreach (var item in graph.VerticesAsEnumberable) { var curves = new LoopList(); - var visited = new List(); + var visited = new List(); if (dfs(graph,item,visited,item)) { for (int i = 0; i < visited.Count - 1; i++) @@ -313,7 +444,7 @@ public void FindAll(IGraph graph) var curedge = graph.GetAdjacencyEdge(cur); foreach (var edge in curedge) { - if (edge.TargetVertexKey == next) + if (edge.TargetVertex == next) { curves.Add(edge.TargetEdge); } @@ -331,7 +462,7 @@ public void FindAll(IGraph graph) /// 递归 DFS。 /// 这个函数需要改写,在ifoxcad里返回的应该是所有的环 /// - private bool dfs(IGraph graph, Point3d current, List visited, Point3d search) + private bool dfs(IGraph graph, IGraphVertex current, List visited, IGraphVertex search) { visited.Add(current); @@ -358,303 +489,5 @@ private bool dfs(IGraph graph, Point3d current, List visited, Point3d s - // ===========另一个dfs实现,感觉比较好一点在于可以用委托干点其他的事情,和前一个可以结合一下 - - /* - /// - /// Implementation of graph vertex. - /// - /// Generic Type. - public class Vertex - { - /// - /// Gets vertex data. - /// - public T Data { get; } - - /// - /// Gets an index of the vertex in graph adjacency matrix. - /// - public int Index { get; } - - /// - /// Gets reference to the graph this vertex belongs to. - /// - public DirectedWeightedGraph? Graph { get; private set; } - - /// - /// Initializes a new instance of the class. - /// - /// Vertex data. Generic type. - /// Index of the vertex in graph adjacency matrix. - /// Graph this vertex belongs to. - public Vertex(T data, int index, DirectedWeightedGraph? graph) - { - Data = data; - Index = index; - Graph = graph; - } - - /// - /// Initializes a new instance of the class. - /// - /// Vertex data. Generic type. - /// Index of the vertex in graph adjacency matrix. - public Vertex(T data, int index) - { - Data = data; - Index = index; - } - - /// - /// Sets graph reference to the null. This method called when vertex removed from the graph. - /// - public void SetGraphNull() => Graph = null; - - /// - /// Override of base ToString method. Prints vertex data and index in graph adjacency matrix. - /// - /// String which contains vertex data and index in graph adjacency matrix.. - public override string ToString() => $"Vertex Data: {Data}, Index: {Index}"; - } - - /// - /// Implementation of the directed weighted graph via adjacency matrix. - /// - /// Generic Type. - public class DirectedWeightedGraph : IDirectedWeightedGraph - { - /// - /// Capacity of the graph, indicates the maximum amount of vertices. - /// - private readonly int capacity; - - /// - /// Adjacency matrix which reflects the edges between vertices and their weight. - /// Zero value indicates no edge between two vertices. - /// - private readonly double[,] adjacencyMatrix; - - /// - /// Initializes a new instance of the class. - /// - /// Capacity of the graph, indicates the maximum amount of vertices. - public DirectedWeightedGraph(int capacity) - { - ThrowIfNegativeCapacity(capacity); - - this.capacity = capacity; - Vertices = new Vertex[capacity]; - adjacencyMatrix = new double[capacity, capacity]; - Count = 0; - } - - /// - /// Gets list of vertices of the graph. - /// - public Vertex?[] Vertices { get; private set; } - - /// - /// Gets current amount of vertices in the graph. - /// - public int Count { get; private set; } - - /// - /// Adds new vertex to the graph. - /// - /// Data of the vertex. - /// Reference to created vertex. - public Vertex AddVertex(T data) - { - ThrowIfOverflow(); - var vertex = new Vertex(data, Count, this); - Vertices[Count] = vertex; - Count++; - return vertex; - } - - /// - /// Creates an edge between two vertices of the graph. - /// - /// Vertex, edge starts at. - /// Vertex, edge ends at. - /// Double weight of an edge. - public void AddEdge(Vertex startVertex, Vertex endVertex, double weight) - { - ThrowIfVertexNotInGraph(startVertex); - ThrowIfVertexNotInGraph(endVertex); - - ThrowIfWeightZero(weight); - - var currentEdgeWeight = adjacencyMatrix[startVertex.Index, endVertex.Index]; - - ThrowIfEdgeExists(currentEdgeWeight); - - adjacencyMatrix[startVertex.Index, endVertex.Index] = weight; - } - - /// - /// Removes vertex from the graph. - /// - /// Vertex to be removed. - public void RemoveVertex(Vertex vertex) - { - ThrowIfVertexNotInGraph(vertex); - - Vertices[vertex.Index] = null; - vertex.SetGraphNull(); - - for (var i = 0; i < Count; i++) - { - adjacencyMatrix[i, vertex.Index] = 0; - adjacencyMatrix[vertex.Index, i] = 0; - } - - Count--; - } - - /// - /// Removes edge between two vertices. - /// - /// Vertex, edge starts at. - /// Vertex, edge ends at. - public void RemoveEdge(Vertex startVertex, Vertex endVertex) - { - ThrowIfVertexNotInGraph(startVertex); - ThrowIfVertexNotInGraph(endVertex); - adjacencyMatrix[startVertex.Index, endVertex.Index] = 0; - } - - /// - /// Gets a neighbors of particular vertex. - /// - /// Vertex, method gets list of neighbors for. - /// Collection of the neighbors of particular vertex. - public IEnumerable?> GetNeighbors(Vertex vertex) - { - ThrowIfVertexNotInGraph(vertex); - - for (var i = 0; i < Count; i++) - { - if (adjacencyMatrix[vertex.Index, i] != 0) - { - yield return Vertices[i]; - } - } - } - - /// - /// Returns true, if there is an edge between two vertices. - /// - /// Vertex, edge starts at. - /// Vertex, edge ends at. - /// True if edge exists, otherwise false. - public bool AreAdjacent(Vertex startVertex, Vertex endVertex) - { - ThrowIfVertexNotInGraph(startVertex); - ThrowIfVertexNotInGraph(endVertex); - - return adjacencyMatrix[startVertex.Index, endVertex.Index] != 0; - } - - /// - /// Return the distance between two vertices in the graph. - /// - /// first vertex in edge. - /// secnod vertex in edge. - /// distance between the two. - public double AdjacentDistance(Vertex startVertex, Vertex endVertex) - { - if (AreAdjacent(startVertex, endVertex)) - { - return adjacencyMatrix[startVertex.Index, endVertex.Index]; - } - - return 0; - } - - private static void ThrowIfNegativeCapacity(int capacity) - { - if (capacity < 0) - { - throw new InvalidOperationException("Graph capacity should always be a non-negative integer."); - } - } - - private static void ThrowIfWeightZero(double weight) - { - if (weight.Equals(0.0d)) - { - throw new InvalidOperationException("Edge weight cannot be zero."); - } - } - - private static void ThrowIfEdgeExists(double currentEdgeWeight) - { - if (!currentEdgeWeight.Equals(0.0d)) - { - throw new InvalidOperationException($"Vertex already exists: {currentEdgeWeight}"); - } - } - - private void ThrowIfOverflow() - { - if (Count == capacity) - { - throw new InvalidOperationException("Graph overflow."); - } - } - - private void ThrowIfVertexNotInGraph(Vertex vertex) - { - if (vertex.Graph != this) - { - throw new InvalidOperationException($"Vertex does not belong to graph: {vertex}."); - } - } - } - /// - /// 深度优先搜索 -遍历图的算法。 - /// 算法从用户选择的根节点开始。 - /// 算法在回溯之前尽可能沿着每个分支探索。 - /// - /// 顶点数据类型。 - public class DepthFirstSearch : IGraphSearch where T : IComparable - { - /// - /// 从起始顶点遍历图。 - /// - ///图实例。 - ///搜索开始的顶点。 - ///每个图顶点需要执行的动作 - public void VisitAll(IDirectedWeightedGraph graph, Vertex startVertex, Action>? action = default) - { - Dfs(graph, startVertex, action, new HashSet>()); - } - - /// - /// 从起始顶点遍历图。 - /// - ///图实例。 - ///搜索开始的顶点。 - ///每个图顶点需要执行的动作 - ///具有访问顶点的哈希集。 - private void Dfs(IDirectedWeightedGraph graph, Vertex startVertex, Action>? action, HashSet> visited) - { - action?.Invoke(startVertex); - - visited.Add(startVertex); - - foreach (var vertex in graph.GetNeighbors(startVertex)) - { - if (vertex == null || visited.Contains(vertex)) - { - continue; - } - - Dfs(graph, vertex!, action, visited); - } - } - } - */ + } diff --git a/src/IFoxCAD.Cad/QuadTree/IGraph.cs b/src/IFoxCAD.Cad/QuadTree/IGraph.cs index ddb1345..a25a3a6 100644 --- a/src/IFoxCAD.Cad/QuadTree/IGraph.cs +++ b/src/IFoxCAD.Cad/QuadTree/IGraph.cs @@ -22,25 +22,24 @@ public interface IGraph /// 搜索的起始顶点 /// /// - Point3d ReferenceVertex { get; } + //Point3d ReferenceVertex { get; } /// /// 是否存在顶点 /// /// 顶点键 /// - bool ContainsVertex(Point3d key); + bool ContainsVertex(IGraphVertex key); /// /// 获取顶点 /// /// /// - IGraphVertex GetVertex(Point3d key); + //IGraphVertex GetVertex(IGraphVertex key); /// /// 顶点的迭代器 /// /// IEnumerable VerticesAsEnumberable { get; } - IEnumerable Point3dAsEnumberable { get; } /// /// 是否有边 @@ -48,22 +47,19 @@ public interface IGraph /// 源顶点 /// 目的顶点 /// - bool HasEdge(Point3d source, Point3d destination); + bool HasEdge(IGraphVertex source, IGraphVertex destination); /// /// 图克隆函数 /// /// IGraph Clone(); - HashSet GetAdjacencyList(Point3d vertex); - HashSet GetAdjacencyEdge(Point3d vertex); + HashSet GetAdjacencyList(IGraphVertex vertex); + HashSet GetAdjacencyEdge(IGraphVertex vertex); } /// /// 无向图顶点 - /// 顶点的数据结构: - /// key --- point3d 用于表示点的坐标和作为数据存储的key - /// edges -- 邻接边表, 每个边的定义为,(边,下一顶点),这样遍历每个顶点的邻接边表时,就可以知道多少个邻接边及对应的邻接点 /// /// 顶点数据类型 public interface IGraphVertex @@ -72,23 +68,28 @@ public interface IGraphVertex /// 顶点的键 /// /// - Point3d Key { get; } - ///// - ///// 顶点的邻接边表 - ///// - ///// + //int Key { get; } + + /// + /// 顶点的数据 + /// + Point3d Data { get; } + /// + /// 顶点的邻接边表 + /// + /// //List> Edges { get; } /// /// 获取顶点的邻接边 /// /// 目标顶点 /// - IEdge GetEdge(IGraphVertex targetVertex); + //IEdge GetEdge(IGraphVertex targetVertex); /// /// 添加邻接边 /// /// 边类型 - void AddEdge(IEdge edge); + //void AddEdge(IEdge edge); } /// /// 无向图边 @@ -105,67 +106,14 @@ public interface IEdge /// 目标顶点的键 /// /// - Point3d TargetVertexKey { get; } + //Point3d TargetVertexKey { get; } /// /// 目标顶点 /// /// IGraphVertex TargetVertex { get; } } - /// - /// 无向图中边的定义 - /// - /// 边的类型 - /// 权重的类型 - internal class GraphEdge : IEdge - { - // 这里的传入的两个参数分别为下一点和下一点之间的曲线 - internal GraphEdge(IGraphVertex target, Curve3d edge) - { - this.TargetVertex = target; - this.TargetEdge = edge; - } - - public Point3d TargetVertexKey => TargetVertex.Key; - - public IGraphVertex TargetVertex { get; private set; } - - public Curve3d TargetEdge { get; private set; } - } + } -// ===================== -// 另一个实现的 -/* -public interface IDirectedWeightedGraph -{ - int Count { get; } - - Vertex?[] Vertices { get; } - - void AddEdge(Vertex startVertex, Vertex endVertex, double weight); - - Vertex AddVertex(T data); - - bool AreAdjacent(Vertex startVertex, Vertex endVertex); - - double AdjacentDistance(Vertex startVertex, Vertex endVertex); - - IEnumerable?> GetNeighbors(Vertex vertex); - - void RemoveEdge(Vertex startVertex, Vertex endVertex); - - void RemoveVertex(Vertex vertex); -} -public interface IGraphSearch -{ - /// - /// 从起始顶点遍历图。 - /// - ///图实例。 - ///搜索开始的顶点。 - ///每个图顶点需要执行的动作 - void VisitAll(IDirectedWeightedGraph graph, Vertex startVertex, Action>? action = null); -} -*/ \ No newline at end of file diff --git a/src/IFoxCAD.Cad/QuadTree/othergraph.cs b/src/IFoxCAD.Cad/QuadTree/othergraph.cs new file mode 100644 index 0000000..ae98aaa --- /dev/null +++ b/src/IFoxCAD.Cad/QuadTree/othergraph.cs @@ -0,0 +1,323 @@ +//using System; +//using System.Collections.Generic; +//using System.Linq; +//using System.Text; + +//namespace IFoxCAD.Cad.QuadTree +//{ +// // ===================== +// // 另一个实现的 + +// public interface IGraph +// { +// int Count { get; } + +// Vertex?[] Vertices { get; } + +// void AddEdge(Vertex startVertex, Vertex endVertex, double weight); + +// Vertex AddVertex(T data); + +// bool AreAdjacent(Vertex startVertex, Vertex endVertex); + +// double AdjacentDistance(Vertex startVertex, Vertex endVertex); + +// IEnumerable?> GetNeighbors(Vertex vertex); + +// void RemoveEdge(Vertex startVertex, Vertex endVertex); + +// void RemoveVertex(Vertex vertex); +// } + + + + + +// public interface IGraphSearch +// { +// /// +// /// 从起始顶点遍历图。 +// /// +// ///图实例。 +// ///搜索开始的顶点。 +// ///每个图顶点需要执行的动作 +// void VisitAll(IGraph graph, Vertex startVertex, Action>? action = null); +// } + +// // ===========另一个dfs实现,感觉比较好一点在于可以用委托干点其他的事情,和前一个可以结合一下 + + +// /// +// /// Implementation of graph vertex. +// /// +// /// Generic Type. +// public class Vertex +// { +// /// +// /// Gets vertex data. +// /// +// public T Data { get; } + +// /// +// /// Gets an index of the vertex in graph adjacency matrix. +// /// +// public int Index { get; } + + + + +// /// +// /// Initializes a new instance of the class. +// /// +// /// Vertex data. Generic type. +// /// Index of the vertex in graph adjacency matrix. +// public Vertex(T data, int index) +// { +// Data = data; +// Index = index; +// } + + +// } + +// /// +// /// Implementation of the directed weighted graph via adjacency matrix. +// /// +// /// Generic Type. +// public class Graph : IGraph +// { +// /// +// /// Capacity of the graph, indicates the maximum amount of vertices. +// /// +// private readonly int capacity; + +// /// +// /// Adjacency matrix which reflects the edges between vertices and their weight. +// /// Zero value indicates no edge between two vertices. +// /// +// private readonly double[,] adjacencyMatrix; + +// /// +// /// Initializes a new instance of the class. +// /// +// /// Capacity of the graph, indicates the maximum amount of vertices. +// public Graph(int capacity) +// { +// ThrowIfNegativeCapacity(capacity); + +// this.capacity = capacity; +// Vertices = new Vertex[capacity]; +// adjacencyMatrix = new double[capacity, capacity]; +// Count = 0; +// } + +// /// +// /// Gets list of vertices of the graph. +// /// +// public Vertex?[] Vertices { get; private set; } + +// /// +// /// Gets current amount of vertices in the graph. +// /// +// public int Count { get; private set; } + +// /// +// /// Adds new vertex to the graph. +// /// +// /// Data of the vertex. +// /// Reference to created vertex. +// public Vertex AddVertex(T data) +// { +// ThrowIfOverflow(); +// var vertex = new Vertex(data, Count, this); +// Vertices[Count] = vertex; +// Count++; +// return vertex; +// } + +// /// +// /// Creates an edge between two vertices of the graph. +// /// +// /// Vertex, edge starts at. +// /// Vertex, edge ends at. +// /// Double weight of an edge. +// public void AddEdge(Vertex startVertex, Vertex endVertex, double weight) +// { +// ThrowIfVertexNotInGraph(startVertex); +// ThrowIfVertexNotInGraph(endVertex); + +// ThrowIfWeightZero(weight); + +// var currentEdgeWeight = adjacencyMatrix[startVertex.Index, endVertex.Index]; + +// ThrowIfEdgeExists(currentEdgeWeight); + +// adjacencyMatrix[startVertex.Index, endVertex.Index] = weight; +// } + +// /// +// /// Removes vertex from the graph. +// /// +// /// Vertex to be removed. +// public void RemoveVertex(Vertex vertex) +// { +// ThrowIfVertexNotInGraph(vertex); + +// Vertices[vertex.Index] = null; +// vertex.SetGraphNull(); + +// for (var i = 0; i < Count; i++) +// { +// adjacencyMatrix[i, vertex.Index] = 0; +// adjacencyMatrix[vertex.Index, i] = 0; +// } + +// Count--; +// } + +// /// +// /// Removes edge between two vertices. +// /// +// /// Vertex, edge starts at. +// /// Vertex, edge ends at. +// public void RemoveEdge(Vertex startVertex, Vertex endVertex) +// { +// ThrowIfVertexNotInGraph(startVertex); +// ThrowIfVertexNotInGraph(endVertex); +// adjacencyMatrix[startVertex.Index, endVertex.Index] = 0; +// } + +// /// +// /// Gets a neighbors of particular vertex. +// /// +// /// Vertex, method gets list of neighbors for. +// /// Collection of the neighbors of particular vertex. +// public IEnumerable?> GetNeighbors(Vertex vertex) +// { +// ThrowIfVertexNotInGraph(vertex); + +// for (var i = 0; i < Count; i++) +// { +// if (adjacencyMatrix[vertex.Index, i] != 0) +// { +// yield return Vertices[i]; +// } +// } +// } + +// /// +// /// Returns true, if there is an edge between two vertices. +// /// +// /// Vertex, edge starts at. +// /// Vertex, edge ends at. +// /// True if edge exists, otherwise false. +// public bool AreAdjacent(Vertex startVertex, Vertex endVertex) +// { +// ThrowIfVertexNotInGraph(startVertex); +// ThrowIfVertexNotInGraph(endVertex); + +// return adjacencyMatrix[startVertex.Index, endVertex.Index] != 0; +// } + +// /// +// /// Return the distance between two vertices in the graph. +// /// +// /// first vertex in edge. +// /// secnod vertex in edge. +// /// distance between the two. +// public double AdjacentDistance(Vertex startVertex, Vertex endVertex) +// { +// if (AreAdjacent(startVertex, endVertex)) +// { +// return adjacencyMatrix[startVertex.Index, endVertex.Index]; +// } + +// return 0; +// } + +// private static void ThrowIfNegativeCapacity(int capacity) +// { +// if (capacity < 0) +// { +// throw new InvalidOperationException("Graph capacity should always be a non-negative integer."); +// } +// } + +// private static void ThrowIfWeightZero(double weight) +// { +// if (weight.Equals(0.0d)) +// { +// throw new InvalidOperationException("Edge weight cannot be zero."); +// } +// } + +// private static void ThrowIfEdgeExists(double currentEdgeWeight) +// { +// if (!currentEdgeWeight.Equals(0.0d)) +// { +// throw new InvalidOperationException($"Vertex already exists: {currentEdgeWeight}"); +// } +// } + +// private void ThrowIfOverflow() +// { +// if (Count == capacity) +// { +// throw new InvalidOperationException("Graph overflow."); +// } +// } + +// private void ThrowIfVertexNotInGraph(Vertex vertex) +// { +// if (vertex.Graph != this) +// { +// throw new InvalidOperationException($"Vertex does not belong to graph: {vertex}."); +// } +// } +// } +// /// +// /// 深度优先搜索 -遍历图的算法。 +// /// 算法从用户选择的根节点开始。 +// /// 算法在回溯之前尽可能沿着每个分支探索。 +// /// +// /// 顶点数据类型。 +// public class DepthFirstSearch : IGraphSearch where T : IComparable +// { +// /// +// /// 从起始顶点遍历图。 +// /// +// ///图实例。 +// ///搜索开始的顶点。 +// ///每个图顶点需要执行的动作 +// public void VisitAll(IGraph graph, Vertex startVertex, Action>? action = default) +// { +// Dfs(graph, startVertex, action, new HashSet>()); +// } + +// /// +// /// 从起始顶点遍历图。 +// /// +// ///图实例。 +// ///搜索开始的顶点。 +// ///每个图顶点需要执行的动作 +// ///具有访问顶点的哈希集。 +// private void Dfs(IGraph graph, Vertex startVertex, Action>? action, HashSet> visited) +// { +// action?.Invoke(startVertex); + +// visited.Add(startVertex); + +// foreach (var vertex in graph.GetNeighbors(startVertex)) +// { +// if (vertex == null || visited.Contains(vertex)) +// { +// continue; +// } + +// Dfs(graph, vertex!, action, visited); +// } +// } +// } + + +//} diff --git a/tests/Test/TestCurve.cs b/tests/Test/TestCurve.cs index d571d41..2c4f76b 100644 --- a/tests/Test/TestCurve.cs +++ b/tests/Test/TestCurve.cs @@ -29,19 +29,27 @@ public void TestPointInDict() public void TestGraph1() { var ents = Env.Editor.SSGet().Value.GetEntities(); - var graph = new IFoxCAD.Cad.Graph(); + // 新建图 + var graph = new IFoxCAD.Cad.FirstGraph.Graph(); + // 将曲线加入到图中 ents.ForEach(ent => graph.AddEdge(ent.GetGeCurve())); - var dfs = new DepthFirst(); - dfs.FindAll(graph); - var res = dfs.Curve3ds.Select(loop => - { - var curves = loop.ToArray(); - var comcur = new CompositeCurve3d(curves); - return comcur.ToCurve(); - }); - using var tr = new DBTrans(); - res.ForEach((i, t) => t.ForWrite(e => e.ColorIndex = i + 1)); - tr.CurrentSpace.AddEntity(res); + + Env.Print(graph.ToReadable()); + // 新建 dfs + //var dfs = new IFoxCAD.Cad.FirstGraph.DepthFirst(); + //// 查询全部的 闭合环 + //dfs.FindAll(graph); + //// 遍历闭合环的列表,将每个闭合环转换为实体曲线 + //var res = dfs.Curve3ds.Select(loop => + //{ + // var curves = loop.ToArray(); + // var comcur = new CompositeCurve3d(curves); + // return comcur.ToCurve(); + //}); + + //using var tr = new DBTrans(); + //res.ForEach((i, t) => t.ForWrite(e => e.ColorIndex = i + 1)); + //tr.CurrentSpace.AddEntity(res); -- Gitee From 786d1b02b61b41d37825d78bd1baa0c27c0496a8 Mon Sep 17 00:00:00 2001 From: vicwjb <403009819@qq.com> Date: Mon, 11 Apr 2022 01:17:23 +0800 Subject: [PATCH 119/675] =?UTF-8?q?=E4=BC=98=E5=8C=96dfs=E7=AE=97=E6=B3=95?= =?UTF-8?q?=EF=BC=8C=E8=BF=98=E6=98=AF=E6=9C=89=E9=97=AE=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/IFoxCAD.Cad/QuadTree/Graph.cs | 178 +++++++++++++++++++++-------- src/IFoxCAD.Cad/QuadTree/IGraph.cs | 4 +- tests/Test/TestCurve.cs | 34 +++--- 3 files changed, 151 insertions(+), 65 deletions(-) diff --git a/src/IFoxCAD.Cad/QuadTree/Graph.cs b/src/IFoxCAD.Cad/QuadTree/Graph.cs index 50daf82..6108b88 100644 --- a/src/IFoxCAD.Cad/QuadTree/Graph.cs +++ b/src/IFoxCAD.Cad/QuadTree/Graph.cs @@ -1,5 +1,7 @@  +using Autodesk.AutoCAD.BoundaryRepresentation; + using Exception = System.Exception; namespace IFoxCAD.Cad.FirstGraph @@ -39,7 +41,7 @@ public void AddVertex(Point3d pt) { throw new Exception("顶点已经存在。"); } - + //vertex.Index = vertices.Count; vertices.Add(vertex, new HashSet()); edges.Add(vertex, new HashSet()); @@ -200,13 +202,6 @@ public void RemoveEdge(Curve3d curve!!) } - - - - - - - /// /// 我们在给定的来源和目的地之间是否有边? /// 时间复杂度: O(E).E 是边 @@ -229,12 +224,29 @@ public bool HasEdge(IGraphVertex source, IGraphVertex dest) } + public IEdge? GetEdge(IGraphVertex source, IGraphVertex dest) + { + if (!vertices.ContainsKey(source) || !vertices.ContainsKey(dest)) + { + throw new ArgumentException("源或目标不在此图中。"); + } + foreach (var item in edges[source]) + { + if (item.TargetVertex == dest) + { + return item; + } + } + return null; + } + public bool ContainsVertex(IGraphVertex value) { return vertices.ContainsKey(value); } + public HashSet GetAdjacencyList(IGraphVertex vertex) { return vertices[vertex]; @@ -245,6 +257,28 @@ public HashSet GetAdjacencyEdge(IGraphVertex vertex) return edges[vertex]; } + public LoopList GetCurves(List graphVertices) + { + var curves = new LoopList(); + for (int i = 0; i < graphVertices.Count - 1; i++) + { + var cur = graphVertices[i]; + var next = graphVertices[i + 1]; + var edge = GetEdge(cur, next); + if (edge is not null) + { + curves.Add(edge.TargetEdge); + } + } + var lastedge = GetEdge(graphVertices[graphVertices.Count - 1], graphVertices[0]); + if (lastedge is not null) + { + curves.Add(lastedge.TargetEdge); + } + return curves; + } + + /// /// 克隆此图。目测是深克隆 @@ -278,8 +312,6 @@ IGraph IGraph.Clone() return Clone(); } - - public IEnumerable VerticesAsEnumberable => vertices.Select(x => x.Key); @@ -312,14 +344,15 @@ public virtual string ToReadable() /// 邻接表图实现的顶点。 /// IEnumerable 枚举所有邻接点。 /// - public struct GraphVertex : IGraphVertex, IEquatable + public struct GraphVertex : IGraphVertex, IEquatable, IComparable ,IComparable { public Point3d Data { get; private set; } - + //public int Index { get; set; } public GraphVertex(Point3d value) { Data = value; + //Index = -1; } public bool Equals(GraphVertex other) @@ -343,6 +376,46 @@ public override int GetHashCode() return (Data.X.ToString("n6"), Data.Y.ToString("n6"), Data.Z.ToString("n6")).GetHashCode(); } + + public int CompareTo(IGraphVertex other) + { + if (Equals(other)) + { + return 0; + } + else if (Data.X <= other.Data.X) + { + return -1; + } + else + { + return 1; + } + } + + int IComparable.CompareTo(IGraphVertex other) + { + return CompareTo(other); + } + + public int CompareTo(object obj) + { + if (obj is null) + { + return 1; + } + try + { + var other = (GraphVertex)obj; + return CompareTo(other); + } + catch (Exception) + { + + throw new ArgumentException("Object is not a IGraphVertex"); + } + } + public static bool operator ==(GraphVertex person1, GraphVertex person2) { if (((object)person1) == null || ((object)person2) == null) @@ -424,66 +497,73 @@ public override int GetHashCode() public class DepthFirst { // 存储所有的边 - public List> Curve3ds { get; } = new List> (); + public List> Curve3ds { get; } = new(); + private readonly List GraphVertices; + private readonly IGraph _graph; + public DepthFirst(IGraph graph) + { + _graph = graph; + GraphVertices = graph.VerticesAsEnumberable.ToList(); + //for (int i = 0; i < GraphVertices.Count; i++) + //{ + // GraphVertices[0].Index = i; + //} + } /// /// 如果项目存在,则返回 true。 /// 这个函数需要改写,在ifoxcad里返回的应该是所有的环 /// - public void FindAll(IGraph graph) + public void FindAll() { - foreach (var item in graph.VerticesAsEnumberable) + foreach (var item in GraphVertices) { - var curves = new LoopList(); var visited = new List(); - if (dfs(graph,item,visited,item)) - { - for (int i = 0; i < visited.Count - 1; i++) - { - var cur = visited[i]; - var next = visited[i + 1]; - var curedge = graph.GetAdjacencyEdge(cur); - foreach (var edge in curedge) - { - if (edge.TargetVertex == next) - { - curves.Add(edge.TargetEdge); - } - } - - } - } - Curve3ds.Add(curves); + dfs(_graph, item, visited, e => { + var copy = new IGraphVertex[e.Count]; + e.CopyTo(copy); + Curve3ds.Add(copy.ToList()); + }); } - - } /// /// 递归 DFS。 - /// 这个函数需要改写,在ifoxcad里返回的应该是所有的环 /// - private bool dfs(IGraph graph, IGraphVertex current, List visited, IGraphVertex search) + private void dfs(IGraph graph, IGraphVertex current, List visited, Action>? action) { - + visited.Add(current); - if (current == search && visited.Count >= 2) - { - return true; - } - + // 改造这个搜索函数,当搜索闭合的时候,将闭合链存入结果列表 foreach (var edge in graph.GetAdjacencyList(current)) { if (visited.Contains(edge)) { + if (visited[0].Equals(edge)) + { + if (visited.Count == 2) + { + var cur1 = graph.GetEdge(visited[0], visited[1]); + var cur2 = graph.GetEdge(visited[1], visited[0]); + if (cur1 is not null && cur2 is not null && cur1.TargetEdge.IsEqualTo(cur2.TargetEdge)) + { + continue; + } // todo: 这里不太对,应该是两点的时候是两条一样就舍弃 + action?.Invoke(visited); + } + else if (visited.Count > 2) + { + action?.Invoke(visited); + } // todo: 这里不对,应该有一种回退机制,搜索到闭合了 就回退回去上一个点继续搜才对 + + } + continue; } - if (dfs(graph,edge,visited,search)) - { - return true; - } + dfs(graph, edge, visited, action); + } - return false; + } } diff --git a/src/IFoxCAD.Cad/QuadTree/IGraph.cs b/src/IFoxCAD.Cad/QuadTree/IGraph.cs index a25a3a6..fa9b822 100644 --- a/src/IFoxCAD.Cad/QuadTree/IGraph.cs +++ b/src/IFoxCAD.Cad/QuadTree/IGraph.cs @@ -53,7 +53,7 @@ public interface IGraph /// /// IGraph Clone(); - + IEdge? GetEdge(IGraphVertex source, IGraphVertex dest); HashSet GetAdjacencyList(IGraphVertex vertex); HashSet GetAdjacencyEdge(IGraphVertex vertex); } @@ -68,7 +68,7 @@ public interface IGraphVertex /// 顶点的键 /// /// - //int Key { get; } + //int Index { get; set; } /// /// 顶点的数据 diff --git a/tests/Test/TestCurve.cs b/tests/Test/TestCurve.cs index 2c4f76b..9bd50d5 100644 --- a/tests/Test/TestCurve.cs +++ b/tests/Test/TestCurve.cs @@ -35,21 +35,27 @@ public void TestGraph1() ents.ForEach(ent => graph.AddEdge(ent.GetGeCurve())); Env.Print(graph.ToReadable()); - // 新建 dfs - //var dfs = new IFoxCAD.Cad.FirstGraph.DepthFirst(); - //// 查询全部的 闭合环 - //dfs.FindAll(graph); - //// 遍历闭合环的列表,将每个闭合环转换为实体曲线 - //var res = dfs.Curve3ds.Select(loop => - //{ - // var curves = loop.ToArray(); - // var comcur = new CompositeCurve3d(curves); - // return comcur.ToCurve(); - //}); + //新建 dfs + var dfs = new IFoxCAD.Cad.FirstGraph.DepthFirst(graph); + // 查询全部的 闭合环 + dfs.FindAll(); + // 遍历闭合环的列表,将每个闭合环转换为实体曲线 + var res = new List(); + foreach (var item in dfs.Curve3ds) + { + var curves = graph.GetCurves(item).ToArray(); + var comcur = new CompositeCurve3d(curves); + var tmp = comcur.ToCurve(); + if (tmp is not null) + { + res.Add(tmp); + } + } + - //using var tr = new DBTrans(); - //res.ForEach((i, t) => t.ForWrite(e => e.ColorIndex = i + 1)); - //tr.CurrentSpace.AddEntity(res); + using var tr = new DBTrans(); + res.ForEach((i, t) => t.ForWrite(e => e.ColorIndex = i + 1)); + tr.CurrentSpace.AddEntity(res); -- Gitee From e268f7894d2d54b63da620f767a6b96a43214fcc Mon Sep 17 00:00:00 2001 From: liuqihong <540762622@qq.com> Date: Mon, 11 Apr 2022 01:57:55 +0800 Subject: [PATCH 120/675] =?UTF-8?q?=E5=A2=9E=E5=8A=A0=E6=B5=8B=E8=AF=95?= =?UTF-8?q?=E6=A0=B7=E5=9B=BE?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- ...205\346\216\245\347\237\251\345\275\242.png" | Bin 0 -> 40404 bytes 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 "docs/Topo\345\221\275\344\273\244\350\257\264\346\230\216/\346\265\213\350\257\225\346\241\210\344\276\213\344\270\244\344\270\252\345\206\205\346\216\245\347\237\251\345\275\242.png" diff --git "a/docs/Topo\345\221\275\344\273\244\350\257\264\346\230\216/\346\265\213\350\257\225\346\241\210\344\276\213\344\270\244\344\270\252\345\206\205\346\216\245\347\237\251\345\275\242.png" "b/docs/Topo\345\221\275\344\273\244\350\257\264\346\230\216/\346\265\213\350\257\225\346\241\210\344\276\213\344\270\244\344\270\252\345\206\205\346\216\245\347\237\251\345\275\242.png" new file mode 100644 index 0000000000000000000000000000000000000000..980676788a18008956c5944bca72949a43bb8b39 GIT binary patch literal 40404 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 literal 0 HcmV?d00001 -- Gitee From 45c4b3598e954f7aa7b08c6eaa6516e1b2c9ad8e Mon Sep 17 00:00:00 2001 From: liuqihong <540762622@qq.com> Date: Mon, 11 Apr 2022 02:04:42 +0800 Subject: [PATCH 121/675] =?UTF-8?q?=E5=A2=9E=E5=8A=A0=E7=BA=AF=E5=8C=96?= =?UTF-8?q?=E8=8A=82=E7=82=B9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/IFoxCAD.Basal/LoopList.cs | 6 +- src/IFoxCAD.Cad/ExtensionMethod/CurveEx.cs | 2 +- src/IFoxCAD.Cad/ExtensionMethod/Edge.cs | 518 +++++++++++++++++++++ src/IFoxCAD.Cad/ExtensionMethod/Topo.cs | 458 +----------------- src/IFoxCAD.Cad/ExtensionMethod/Topo2.cs | 148 ++++++ 5 files changed, 674 insertions(+), 458 deletions(-) create mode 100644 src/IFoxCAD.Cad/ExtensionMethod/Edge.cs create mode 100644 src/IFoxCAD.Cad/ExtensionMethod/Topo2.cs diff --git a/src/IFoxCAD.Basal/LoopList.cs b/src/IFoxCAD.Basal/LoopList.cs index 35f21d0..6c17063 100644 --- a/src/IFoxCAD.Basal/LoopList.cs +++ b/src/IFoxCAD.Basal/LoopList.cs @@ -42,7 +42,7 @@ public LoopListNode(T value, LoopList ts) /// /// 获取当前节点的临近节点 /// - /// 搜索方向标志,为向前搜索,为向后搜索 + /// 搜索方向标志,为向前搜索,为向后搜索 /// public LoopListNode? GetNext(bool forward) { @@ -482,7 +482,7 @@ public bool RemoveLast() } /// - /// 删除节点 + /// 删除此参数节点(唯一) /// /// 指定节点 /// @@ -495,7 +495,7 @@ public bool Remove(LoopListNode node) } /// - /// 删除节点 + /// 删除含有此参数节点(所有) /// /// 将移除所有含有此值 /// diff --git a/src/IFoxCAD.Cad/ExtensionMethod/CurveEx.cs b/src/IFoxCAD.Cad/ExtensionMethod/CurveEx.cs index 8112b9d..841ca11 100644 --- a/src/IFoxCAD.Cad/ExtensionMethod/CurveEx.cs +++ b/src/IFoxCAD.Cad/ExtensionMethod/CurveEx.cs @@ -84,7 +84,7 @@ public static IEnumerable GetSplitCurves(this Curve curve, IEnumerable BreakCurve(this List curves) { var geCurves = new List(); // 存储曲线转换后的复合曲线 - var paramss = new List>(); // 存储每个曲线的交点参数值 + var paramss = new List>(); // 存储每个曲线的交点参数值 foreach (var curve in curves) { diff --git a/src/IFoxCAD.Cad/ExtensionMethod/Edge.cs b/src/IFoxCAD.Cad/ExtensionMethod/Edge.cs new file mode 100644 index 0000000..64e9658 --- /dev/null +++ b/src/IFoxCAD.Cad/ExtensionMethod/Edge.cs @@ -0,0 +1,518 @@ +namespace IFoxCAD.Cad; + +/// +/// +/// +public class Edge : IEquatable +{ + #region ֶ + public CompositeCurve3d GeCurve3d; + public int StartIndex;//DESIGN0409 ֻڽӱ˾û. + public int EndIndex; //DESIGN0409 ֻڽӱ˾û. + public static Tolerance CadTolerance = new(1e-6, 1e-6); + #endregion + + #region + /// + /// (ûаΧ,ToCurve) + /// + public Edge(CompositeCurve3d geCurve3d) + { + GeCurve3d = geCurve3d; + } + public Edge(Edge edge) : this(edge.GeCurve3d) + { + StartIndex = edge.StartIndex; + EndIndex = edge.EndIndex; + } + #endregion + + #region + /// + /// ʼ + /// + /// + public Vector3d GetStartVector() + { + //ȡ߲ + var inter = GeCurve3d.GetInterval(); + var poc = new PointOnCurve3d(GeCurve3d, inter.LowerBound); + // + return poc.GetDerivative(1); + } + + /// + /// + /// + /// + public Vector3d GetEndVector() + { + var inter = GeCurve3d.GetInterval(); + var poc = new PointOnCurve3d(GeCurve3d, inter.UpperBound); + return -poc.GetDerivative(1); + } + + /// + /// жϽڵλ + /// + /// ߽ + /// ߽Ƿλڴ˴ + /// + /// + /// + public bool IsNext(Edge edge, int startOrEndIndex, ref Vector3d vec, ref bool forward) + { + if (edge == this) + return false; + + if (StartIndex == startOrEndIndex) + { + vec = GetStartVector(); + forward = true; + return true; + } + else if (EndIndex == startOrEndIndex) + { + vec = GetEndVector(); + forward = false; + return true; + } + return false; + } + + #endregion + + #region _Ƚ + public override bool Equals(object? obj) + { + return this == obj as Edge; + } + public bool Equals(Edge? b) + { + return this == b; + } + public static bool operator !=(Edge? a, Edge? b) + { + return !(a == b); + } + public static bool operator ==(Edge? a, Edge? b) + { + //˴طʹ==null,Ϊ˴Ƕ + if (b is null) + return a is null; + else if (a is null) + return false; + if (ReferenceEquals(a, b))//ͬһ + return true; + + return a.GeCurve3d == b.GeCurve3d + && a.StartIndex == b.StartIndex + && a.EndIndex == b.EndIndex; + } + + /// + /// Ƚ(,˳) + /// + /// + /// + /// и߷(3ԲصʧЧ,˴4ʼ) + /// ͼ˵ڽdocs + /// + /// صtrue + public bool SplitPointEquals(Edge? b, int splitNum = 4) + { + if (b is null) + return this is null; + else if (this is null) + return false; + if (ReferenceEquals(this, b))//ͬһ + return true; + + //ȡ߳Ȼᾭܶƽ, + //ԲҪ,ֱӲ֮жϾ + + //ָ߲Ҳһ,һ + Point3d[] sp1; + Point3d[] sp2; + +#if NET35 + sp1 = GeCurve3d.GetSamplePoints(splitNum); + sp2 = b.GeCurve3d.GetSamplePoints(splitNum); +#else + var tmp1 = GeCurve3d.GetSamplePoints(splitNum); + var tmp2 = b.GeCurve3d.GetSamplePoints(splitNum); + sp1 = tmp1.Select(a => a.Point).ToArray(); + sp2 = tmp2.Select(a => a.Point).ToArray(); +#endif + //Ϊ߿,Բ֮Ҫ + sp1 = sp1.OrderBy(a => a.X).ThenBy(a => a.Y).ThenBy(a => a.Z).ToArray(); + sp2 = sp2.OrderBy(a => a.X).ThenBy(a => a.Y).ThenBy(a => a.Z).ToArray(); + + for (int i = 0; i < sp1.Length; i++) + { + if (!sp1[i].IsEqualTo(sp2[i], CadTolerance)) + return false; + } + return true; + } + + /// + /// ˳ + /// + /// + public static void Distinct(List edgesOut) + { + if (edgesOut.Count == 0) + return; + + //EdgeûаΧ,޷ж + //anݽи,ظ,ͬ + Basal.ArrayEx.Deduplication(edgesOut, (first, last) => { + var pta1 = first.GeCurve3d.StartPoint; + var pta2 = first.GeCurve3d.EndPoint; + var ptb1 = last.GeCurve3d.StartPoint; + var ptb2 = last.GeCurve3d.EndPoint; + //˳ || + if ((pta1.IsEqualTo(ptb1, CadTolerance) && pta2.IsEqualTo(ptb2, CadTolerance)) + || + (pta1.IsEqualTo(ptb2, CadTolerance) && pta2.IsEqualTo(ptb1, CadTolerance))) + { + return first.SplitPointEquals(last); + } + return false; + }); + } + public override int GetHashCode() + { + return GeCurve3d.GetHashCode() ^ StartIndex ^ EndIndex; + } + #endregion +} + +/// +/// ߽ڵ +/// +public class EdgeItem : Edge, IEquatable +{ + #region ֶ + /// + /// ־,Ϊǰ,Ϊ + /// + public bool Forward; + #endregion + + #region + /// + /// ߽ڵ + /// + public EdgeItem(Edge edge, bool forward) : base(edge) + { + Forward = forward; + } + #endregion + + #region + public CompositeCurve3d? GetCurve() + { + var cc3d = GeCurve3d; + if (Forward) + return cc3d; + + //߲ + cc3d = cc3d.Clone() as CompositeCurve3d; + return cc3d?.GetReverseParameterCurve() as CompositeCurve3d; + } + + /// + /// + /// + /// ѯλõ + /// ص + public void FindRegion(List edges, List> regions_out) + { + var result = new LoopList();//µ + var edgeItem = this; + result.Add(edgeItem); + var getEdgeItem = this.GetNext(edges); + if (getEdgeItem is null) + return; + + bool hasList = false; + + for (int i = 0; i < regions_out.Count; i++) + { + var edgeList2 = regions_out[i]; + var node = edgeList2.GetNode(e => e.Equals(edgeItem)); + if (node is not null && node != edgeList2.Last) + { + if (node.Next!.Value.Equals(getEdgeItem)) + { + hasList = true; + break; + } + } + } + if (!hasList) + { + //֮ǰиѭ,ɵԭ8b(ջ),ȻջԶҲͷ + while (getEdgeItem is not null) + { + if (result.Contains(getEdgeItem)) + { + //ջ,ǰͷʼ޳ظڵ + while (result.First?.Value != getEdgeItem) + result.RemoveFirst(); + break; + } + result.Add(getEdgeItem); + getEdgeItem = getEdgeItem.GetNext(edges); + } + if (getEdgeItem == edgeItem) + regions_out.Add(result); + } + } + + /// + /// ȡһ + /// + /// + /// + public EdgeItem? GetNext(List edges) + { + Vector3d vec; + int next; + if (Forward) + { + vec = GetEndVector(); + next = EndIndex; + } + else + { + vec = GetStartVector(); + next = StartIndex; + } + + EdgeItem? edgeItem = null; + Vector3d vec2, vec3 = new(); + double angle = 0; + bool hasNext = false; + bool forward = false; + for (int i = 0; i < edges.Count; i++) + { + var edge = edges[i]; + if (this.IsNext(edge, next, ref vec3, ref forward)) + { + if (hasNext) + { + var angle2 = vec.GetAngleTo(vec3, Vector3d.ZAxis); + if (angle2 < angle) + { + vec2 = vec3; + angle = angle2; + edgeItem = new EdgeItem(edge, forward); + } + } + else + { + hasNext = true; + vec2 = vec3; + angle = vec.GetAngleTo(vec2, Vector3d.ZAxis); + edgeItem = new EdgeItem(edge, forward); + } + } + } + return edgeItem; + } + #endregion + + #region ת + public override string ToString() + { + return Forward ? + $"{StartIndex}-{EndIndex}" : + $"{EndIndex}-{StartIndex}"; + } + #endregion + + #region _Ƚ + public override bool Equals(object? obj) + { + return this == obj as EdgeItem; + } + public bool Equals(EdgeItem? b) + { + return this == b; + } + public static bool operator !=(EdgeItem? a, EdgeItem? b) + { + return !(a == b); + } + public static bool operator ==(EdgeItem? a, EdgeItem? b) + { + //˴طʹ==null,Ϊ˴Ƕ + if (b is null) + return a is null; + else if (a is null) + return false; + if (ReferenceEquals(a, b))//ͬһ + return true; + + return a.Forward == b.Forward && (a == b as Edge); + } + public override int GetHashCode() + { + return base.GetHashCode() ^ (Forward ? 0 : 1); + } + #endregion +} + + +/// +/// Ϣ +/// +public class CurveInfo : Rect +{ + /// + /// ͼԪ + /// + public Curve Curve; + /// + /// ߷ָIJ + /// + public List Paramss; + /// + /// ײ + /// + public CollisionChain? CollisionChain; + + Edge? _Edge; + /// + /// ߽ + /// + public Edge Edge + { + get + { + if (_Edge == null) + _Edge = new Edge(Curve.ToCompositeCurve3d()!); + return _Edge; + } + } + + public CurveInfo(Curve curve) + { + Curve = curve; + Paramss = new List(); + + //TODO ˴bug:֮ûй + var box = Curve.GeometricExtents; + _X = box.MinPoint.X; + _Y = box.MinPoint.Y; + _Right = box.MaxPoint.X; + _Top = box.MaxPoint.Y; + } + + /// + /// ָ, + /// + /// ߷ָIJ + /// + public List Split(List pars1) + { + var edges = new List(); + var c3ds = Edge.GeCurve3d.GetSplitCurves(pars1); + if (c3ds.Count > 0) + edges.AddRange(c3ds.Select(c => new Edge(c))); + return edges; + } +} + +/// +/// ײ +/// +public class CollisionChain : List +{ +} + + +/// +/// (պ/޷ֲ/Խ) +/// +public class PolyEdge : LoopList +{ + public PolyEdge(params Edge[] ps) + { + for (int i = 0; i < ps.Length; i++) + Add(ps[i]); + } + + public PolyEdge(Knot kn) + { + AddRange(kn); + } + + + /// + /// Ӷηض + /// + /// ߼(,ṩȱ) + /// ҵӶ + /// ض + public static PolyEdge? Contains(IEnumerable lst, Edge find) + { + //̫Ч + //Parallel.ForEach(this, item => { + // Parallel.ForEach(item, item2 => { + // if (item2 == find) + // return; + // }); + //}); + + //߼=>=>Ӷ + var ge1 = lst.GetEnumerator(); + while (ge1.MoveNext()) + { + var ge2 = ge1.Current.GetEnumerator(); + while (ge2.MoveNext()) + { + //Ӷͬ:ض,ڼ + if (ge2.Current == find) + return ge1.Current; + } + } + return null; + } +} + + +/// +/// һ㴢֧ +/// +public class Knot : PolyEdge//ı߼(DZ) +{ + public Point3d Point; // + + public Knot() + { + } + public Knot(Point3d point, Edge edge) + { + Point = point; + Add(edge); + } + + /// + /// оͷسԱ + /// + /// ڵ㼯 + /// ҵ + /// + public static Knot? Contains(IEnumerable knots, Point3d findPoint) + { + var ge = knots.GetEnumerator(); + while (ge.MoveNext()) + if (ge.Current.Point == findPoint) + return ge.Current; + return null; + } +} \ No newline at end of file diff --git a/src/IFoxCAD.Cad/ExtensionMethod/Topo.cs b/src/IFoxCAD.Cad/ExtensionMethod/Topo.cs index af96c2e..e2880aa 100644 --- a/src/IFoxCAD.Cad/ExtensionMethod/Topo.cs +++ b/src/IFoxCAD.Cad/ExtensionMethod/Topo.cs @@ -1,438 +1,5 @@ namespace IFoxCAD.Cad; -/// -/// 边 -/// -public class Edge : IEquatable -{ - #region 字段 - public CompositeCurve3d GeCurve3d; - public int StartIndex;//DESIGN0409 这个东西只是用在邻接表过滤就没用了. - public int EndIndex; //DESIGN0409 这个东西只是用在邻接表过滤就没用了. - public static Tolerance CadTolerance = new(1e-6, 1e-6); - #endregion - - #region 构造 - /// - /// 边线(没有包围盒,除非ToCurve) - /// - public Edge(CompositeCurve3d geCurve3d) - { - GeCurve3d = geCurve3d; - } - public Edge(Edge edge) : this(edge.GeCurve3d) - { - StartIndex = edge.StartIndex; - EndIndex = edge.EndIndex; - } - #endregion - - #region 方法 - public Vector3d GetStartVector() - { - var inter = GeCurve3d.GetInterval(); - PointOnCurve3d poc = new(GeCurve3d, inter.LowerBound); - return poc.GetDerivative(1); - } - - public Vector3d GetEndVector() - { - var inter = GeCurve3d.GetInterval(); - PointOnCurve3d poc = new(GeCurve3d, inter.UpperBound); - return -poc.GetDerivative(1); - } - - /// - /// 判断节点位置 - /// - /// 边界 - /// 边界是否位于此处 - /// - /// - /// - public bool IsNext(Edge edge, int startOrEndIndex, ref Vector3d vec, ref bool forward) - { - if (edge == this) - return false; - - if (StartIndex == startOrEndIndex) - { - vec = GetStartVector(); - forward = true; - return true; - } - else if (EndIndex == startOrEndIndex) - { - vec = GetEndVector(); - forward = false; - return true; - } - return false; - } - #endregion - - #region 重载运算符_比较 - public override bool Equals(object? obj) - { - return this == obj as Edge; - } - public bool Equals(Edge? b) - { - return this == b; - } - public static bool operator !=(Edge? a, Edge? b) - { - return !(a == b); - } - public static bool operator ==(Edge? a, Edge? b) - { - //此处地方不允许使用==null,因为此处是定义 - if (b is null) - return a is null; - else if (a is null) - return false; - if (ReferenceEquals(a, b))//同一对象 - return true; - - return a.GeCurve3d == b.GeCurve3d - && a.StartIndex == b.StartIndex - && a.EndIndex == b.EndIndex; - } - - /// - /// 采样点比较(会排序,无视顺序逆序) - /// - /// - /// - /// 切割曲线份数(采样3点令样条和圆弧重叠发生失效,因此从4开始) - /// 图例说明在将本工程docs内 - /// - /// 采样点重叠true - public bool SplitPointEquals(Edge? b, int splitNum = 4) - { - if (b is null) - return this is null; - else if (this is null) - return false; - if (ReferenceEquals(this, b))//同一对象 - return true; - - //这里获取曲线长度会经过很多次平方根, - //所以不要这样做,直接采样点之后判断就完事 - - //曲线采样分割点也一样,才是一样 - Point3d[] sp1; - Point3d[] sp2; - -#if NET35 - sp1 = GeCurve3d.GetSamplePoints(splitNum); - sp2 = b.GeCurve3d.GetSamplePoints(splitNum); -#else - var tmp1 = GeCurve3d.GetSamplePoints(splitNum); - var tmp2 = b.GeCurve3d.GetSamplePoints(splitNum); - sp1 = tmp1.Select(a => a.Point).ToArray(); - sp2 = tmp2.Select(a => a.Point).ToArray(); -#endif - //因为两条曲线可能是逆向的,所以采样之后需要点排序 - sp1 = sp1.OrderBy(a => a.X).ThenBy(a => a.Y).ThenBy(a => a.Z).ToArray(); - sp2 = sp2.OrderBy(a => a.X).ThenBy(a => a.Y).ThenBy(a => a.Z).ToArray(); - - for (int i = 0; i < sp1.Length; i++) - { - if (!sp1[i].IsEqualTo(sp2[i], CadTolerance)) - return false; - } - return true; - } - - /// - /// 消重顺序逆序 - /// - /// - public static void Distinct(List edgesOut) - { - if (edgesOut.Count == 0) - return; - - //Edge没有包围盒,无法快速判断 - //曲线a和其他曲线n根据交点切割子线,会造成重复部分,例如多段线逆向相同 - Basal.ArrayEx.Deduplication(edgesOut, (first, last) => { - var pta1 = first.GeCurve3d.StartPoint; - var pta2 = first.GeCurve3d.EndPoint; - var ptb1 = last.GeCurve3d.StartPoint; - var ptb2 = last.GeCurve3d.EndPoint; - //顺序 || 逆序 - if ((pta1.IsEqualTo(ptb1, CadTolerance) && pta2.IsEqualTo(ptb2, CadTolerance)) - || - (pta1.IsEqualTo(ptb2, CadTolerance) && pta2.IsEqualTo(ptb1, CadTolerance))) - { - return first.SplitPointEquals(last); - } - return false; - }); - } - public override int GetHashCode() - { - return GeCurve3d.GetHashCode() ^ StartIndex ^ EndIndex; - } - #endregion -} - -/// -/// 边节点 -/// -public class EdgeItem : Edge, IEquatable -{ - #region 字段 - /// - /// 搜索方向标志,为向前搜索,为向后搜索 - /// - public bool Forward; - #endregion - - #region 构造 - /// - /// 边节点 - /// - public EdgeItem(Edge edge, bool forward) : base(edge) - { - Forward = forward; - } - #endregion - - #region 方法 - public CompositeCurve3d? GetCurve() - { - var cc3d = GeCurve3d; - if (Forward) - { - return cc3d; - } - else - { - //反向曲线参数 - cc3d = cc3d.Clone() as CompositeCurve3d; - return cc3d?.GetReverseParameterCurve() as CompositeCurve3d; - } - } - - /// - /// 查找面域 - /// - /// 用来查询位置的 - /// 返回的面域 - public void FindRegion(List edges, List> regions_out) - { - var result = new LoopList();//新的面域 - var edgeItem = this; - result.Add(edgeItem); - var getEdgeItem = this.GetNext(edges); - if (getEdgeItem is null) - return; - - bool hasList = false; - - for (int i = 0; i < regions_out.Count; i++) - { - var edgeList2 = regions_out[i]; - var node = edgeList2.GetNode(e => e.Equals(edgeItem)); - if (node is not null && node != edgeList2.Last) - { - if (node.Next!.Value.Equals(getEdgeItem)) - { - hasList = true; - break; - } - } - } - if (!hasList) - { - //之前这里有个死循环,造成的原因是8字走b了(腰身闭环),然后下面闭环永远找不到开头 - while (getEdgeItem is not null) - { - if (result.Contains(getEdgeItem)) - { - //腰身闭环,从前头开始剔除到这个重复节点 - while (result.First?.Value != getEdgeItem) - result.RemoveFirst(); - break; - } - result.Add(getEdgeItem); - getEdgeItem = getEdgeItem.GetNext(edges); - } - if (getEdgeItem == edgeItem)//TODO 这里必须存在可以环形排序方式,令它不会重复加入一样的链条. - regions_out.Add(result); - } - } - - /// - /// 获取下一个 - /// - /// - /// - public EdgeItem? GetNext(List edges) - { - Vector3d vec; - int next; - if (Forward) - { - vec = GetEndVector(); - next = EndIndex; - } - else - { - vec = GetStartVector(); - next = StartIndex; - } - - EdgeItem? edgeItem = null; - Vector3d vec2, vec3 = new(); - double angle = 0; - bool hasNext = false; - bool forward = false; - for (int i = 0; i < edges.Count; i++) - { - var edge = edges[i]; - if (this.IsNext(edge, next, ref vec3, ref forward)) - { - if (hasNext) - { - var angle2 = vec.GetAngleTo(vec3, Vector3d.ZAxis); - if (angle2 < angle) - { - vec2 = vec3; - angle = angle2; - edgeItem = new EdgeItem(edge, forward); - } - } - else - { - hasNext = true; - vec2 = vec3; - angle = vec.GetAngleTo(vec2, Vector3d.ZAxis); - edgeItem = new EdgeItem(edge, forward); - } - } - } - return edgeItem; - } - #endregion - - #region 类型转换 - public override string ToString() - { - return - Forward ? - string.Format("{0}-{1}", StartIndex, EndIndex) : - string.Format("{0}-{1}", EndIndex, StartIndex); - } - #endregion - - #region 重载运算符_比较 - public override bool Equals(object? obj) - { - return this == obj as EdgeItem; - } - public bool Equals(EdgeItem? b) - { - return this == b; - } - public static bool operator !=(EdgeItem? a, EdgeItem? b) - { - return !(a == b); - } - public static bool operator ==(EdgeItem? a, EdgeItem? b) - { - //此处地方不允许使用==null,因为此处是定义 - if (b is null) - return a is null; - else if (a is null) - return false; - if (ReferenceEquals(a, b))//同一对象 - return true; - - return a.Forward == b.Forward && (a == b as Edge); - } - public override int GetHashCode() - { - return base.GetHashCode() ^ (Forward ? 0 : 1); - } - #endregion -} - -/// -/// 曲线信息 -/// -public class CurveInfo : Rect -{ - /// - /// 曲线图元 - /// - public Curve Curve; - /// - /// 曲线分割点的参数 - /// - public List Paramss; - /// - /// 碰撞链 - /// - public CollisionChain? CollisionChain; - - Edge? _Edge; - /// - /// 边界 - /// - public Edge Edge - { - get - { - if (_Edge == null) - _Edge = new Edge(Curve.ToCompositeCurve3d()!); - return _Edge; - } - } - - public CurveInfo(Curve curve) - { - Curve = curve; - Paramss = new List(); - - //TODO 此处存在bug:射线之类的没有过滤 - var box = Curve.GeometricExtents; - _X = box.MinPoint.X; - _Y = box.MinPoint.Y; - _Right = box.MaxPoint.X; - _Top = box.MaxPoint.Y; - } - - /// - /// 分割曲线,返回子线 - /// - /// 曲线分割点的参数 - /// - public List Split(List pars1) - { - var edges = new List(); - var c3ds = Edge.GeCurve3d.GetSplitCurves(pars1); - if (c3ds.Count > 0) - edges.AddRange(c3ds.Select(c => new Edge(c))); - return edges; - } -} - -public class CollisionChain -{ - /// - /// 碰撞链 - /// - public List Collision; - - public CollisionChain() - { - Collision = new List(); - } -} - public class Topo { // 碰撞链集合 @@ -442,7 +9,6 @@ public class Topo // cad容差类 public static Tolerance CadTolerance = new(1e-6, 1e-6); - public Topo(List curves) { List curveList = new(); @@ -480,10 +46,10 @@ public Topo(List curves) { tmp = new(); oneRect.CollisionChain = tmp;//碰撞链设置 - tmp.Collision.Add(oneRect);//本体也加入链 + tmp.Add(oneRect);//本体也加入链 } twoRect.CollisionChain = tmp;//碰撞链设置 - tmp.Collision.Add(twoRect); + tmp.Add(twoRect); return false; }, oneRect => { @@ -503,7 +69,7 @@ public Topo(List curves) public void CollisionFor(Action> action) { _CollisionChains.ForEach(a => { - action(a.Collision);//输出每条碰撞链 + action(a);//输出每条碰撞链 }); } @@ -586,8 +152,6 @@ public void AdjacencyList(List edges_InOut, List closedC var nums = new List(); var closedEdges = new List(); - //var dic = new Dictionary(); - //交点集合 knots 会不断增大,会变得更加慢,因此我在一开始就进行碰撞链分析 for (int i = 0; i < edges_InOut.Count; i++) { @@ -600,18 +164,7 @@ public void AdjacencyList(List edges_InOut, List closedC var sp = edge.GeCurve3d.StartPoint; var ep = edge.GeCurve3d.EndPoint; - - //狐哥在图元信息上面储存的是计数索引(也没有办法储存计数 DESIGN0409),因此这样的写法不对 - //if (dic.Keys.Contains(sp)) - // dic[sp]++;//含有就是其他曲线"共用"此交点,计数 - //else - // dic.Add(sp, 1);//不含有就加入节点 - - //if (dic.Keys.Contains(ep)) - // dic[ep]++; - //else - // dic.Add(ep, 1); - + if (knots.Contains(edge.GeCurve3d.StartPoint)) { //含有就是其他曲线"共用"此交点, @@ -648,9 +201,6 @@ public void AdjacencyList(List edges_InOut, List closedC .Except(closedEdges) .Where(e => nums[e.StartIndex] > 1 && nums[e.EndIndex] > 1); - //TODO DESIGN0409 按照引用次数的排序,不成功 - //tmp = tmp.OrderBy(e => nums[e.StartIndex]); - var tmpArr = tmp.ToArray();//Clear导致tmp失效 edges_InOut.Clear(); for (int i = 0; i < tmpArr.Length; i++) diff --git a/src/IFoxCAD.Cad/ExtensionMethod/Topo2.cs b/src/IFoxCAD.Cad/ExtensionMethod/Topo2.cs new file mode 100644 index 0000000..5d18133 --- /dev/null +++ b/src/IFoxCAD.Cad/ExtensionMethod/Topo2.cs @@ -0,0 +1,148 @@ +namespace IFoxCAD.Cad; + +public class Topo2 +{ + /// + /// 创建邻接表 + /// + /// 传入每组有碰撞的;传出闭合边界集(扔掉单交点的) + /// 传出自闭曲线集 + public void AdjacencyList(List edges_InOut, List closedCurve3d_Out) + { + /* + * 邻接表:不重复地将共点(交点)作为标记,然后容器加入边 + */ + var knots = new List(); + for (int i = 0; i < edges_InOut.Count; i++) + { + var edge = edges_InOut[i]; + if (edge.GeCurve3d.IsClosed()) + { + closedCurve3d_Out.Add(edge.GeCurve3d); + continue; + } + + var sp = edge.GeCurve3d.StartPoint; + var ep = edge.GeCurve3d.EndPoint; + + //含有就是其他曲线"共用"此交点,在节点上面加入边; + //不含有就加入节点和边; + var kn1 = Knot.Contains(knots, sp); + if (kn1 != null) + kn1.Add(edge); + else + knots.Add(new Knot(sp, edge)); + + var kn2 = Knot.Contains(knots, ep); + if (kn2 != null) + kn2.Add(edge); + else + knots.Add(new Knot(ep, edge)); + } + + /* + * 剪枝: + * 共点若只有一次使用,代表此图元是尾巴,可以扔掉. + * 剩下每个点都至少有两条曲线通过(闭合曲线) + */ + for (int i = knots.Count - 1; i >= 0; i--) + { + var kn = knots[i]; + if (kn.Count == 0)//此条件不会触发;剔除过程不变0,因为移除了kn;剔除前也不会是0,因为必然初始化并加入; + knots.RemoveAt(i); + else if (kn.Count == 1)//孤独点容器必然储存着尾巴图元 + { + //尾巴图元的头点可能有多个连接,因此尾巴图元会在其他集合中,需要移除其他集合的, + //而尾点只有一个 + var fir = kn.First(); + for (int j = 0; j < knots.Count; j++) + knots[j].Remove(fir); + + //剔除点,减少再次进入剔除尾巴 + knots.RemoveAt(i); + } + } + + var pk = PureKnots(knots); + + //TODO 直接进入穷举...图....返回参数取消掉 + //还需要优化深度优先算法=>逆时针最短路径 + //如果次方案测试通过,删除所有 DESIGN0409 的字段所涉及的操作 + } + + /// + /// 纯化节点 + /// + /// + private static List PureKnots(List knots) + { + /* + * 纯化节点: + * 把 "日" 变成 "(|)" 6个变成2个节点,但是穷举起来就是12;23;13;21;32;31好多啊,因此还需要优化深度优先算法=>逆时针最短路径 + * 连续的碎线(不闭合的多段线,无分叉,不自交), + * 在图上的节点就没有必然连接的多段线腰身了,搜索起来更快. + * + * 因为图在深度优先的时候必然会进入重复循环获取下一个节点,(逆序又进行了一次) + * 而上节点和下节点是必然握手的{pt1,pt2,pt2,pt3}, + * 那么我们可以直接数据纯化数据为{pt1,pt3},组成碎线链 EdgePoly ,减少了腰身每次获取的时间. + * + * (日日)两字一个角点相连,造成有(上中/上下/中下)次数没有共点2,直接每个都成为纯化边界; + * 但是此操作能够使得有2就加速 + */ + + var peList = new List(); + for (int i = 0; i < knots.Count; i++) + { + var knot = knots[i]; + if (knot.Count == 2)//共点只有两个图元,它们就是手拉手,没有第三者 + { + //{{a,b}{b,c}{c,d}}=>{a,b,c,d} + //其中一个已经加入,就找到指定多段线,插入子段 + var a = knot.First!.Value; + var b = knot.Last!.Value; + + //多段线集合中查询子段,返回多段线 + var pe = PolyEdge.Contains(peList, a); + if (pe == null) + { + pe = PolyEdge.Contains(peList, b); + if (pe != null) + pe.AddAfter(pe.Find(b)!, a); + else + { + //新建多段线加入 + //遍历顺序可能导致:先有{a,b}来找{d,e}找不到,会生成一条{d,e}, + //所以需要生成完,再找一次首尾. + peList.Add(new PolyEdge(a, b)); + } + } + else + { + pe.AddAfter(pe.Find(a)!, b); + } + } + else + { + peList.Add(new PolyEdge(knot)); + } + } + + //遍历顺序可能导致:先有{a,b}来找{c,d}找不到,会生成一条{c,d};但是完成纯化之后会有{{a,b,c}{c,d}} + //所以需要生成完,再找一次首尾. + Basal.ArrayEx.Deduplication(peList, (first, last) => { + if (first.First!.Value == last.First!.Value)//{{c,b,a}{c,d}}=>{d,c,b,a} + { + last.ForEach(a => first.AddFirst(a)); + return true; + } + else if (first.Last!.Value == last.First!.Value)//{{a,b,c}{c,d}}=>{a,b,c,d} + { + last.ForEach(a => first.AddLast(a)); + return true; + } + return false; + }); + + return peList; + } +} \ No newline at end of file -- Gitee From 2593d9ccdf9a0b1cfc02c2cc5085fe0055f4267f Mon Sep 17 00:00:00 2001 From: liuqihong <540762622@qq.com> Date: Mon, 11 Apr 2022 02:39:27 +0800 Subject: [PATCH 122/675] =?UTF-8?q?=E4=BF=AE=E6=94=B9=E7=BA=AF=E5=8C=96?= =?UTF-8?q?=E6=96=B9=E6=A1=88?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/IFoxCAD.Basal/LoopList.cs | 17 +++++++++++ src/IFoxCAD.Cad/ExtensionMethod/Topo2.cs | 37 +++++++++++++++++++++--- 2 files changed, 50 insertions(+), 4 deletions(-) diff --git a/src/IFoxCAD.Basal/LoopList.cs b/src/IFoxCAD.Basal/LoopList.cs index 6c17063..e15940e 100644 --- a/src/IFoxCAD.Basal/LoopList.cs +++ b/src/IFoxCAD.Basal/LoopList.cs @@ -184,6 +184,23 @@ public void ForEach(Func, bool> action) } } + /// + /// 从头遍历_非迭代器(扔出计数) + /// + /// + public void For(Func, bool> action) + { + var node = First; + if (node is null) + return; + for (int i = 0; i < Count; i++) + { + if (action(i, node!)) + break; + node = node!.Next; + } + } + #region Contains /// diff --git a/src/IFoxCAD.Cad/ExtensionMethod/Topo2.cs b/src/IFoxCAD.Cad/ExtensionMethod/Topo2.cs index 5d18133..fbe310f 100644 --- a/src/IFoxCAD.Cad/ExtensionMethod/Topo2.cs +++ b/src/IFoxCAD.Cad/ExtensionMethod/Topo2.cs @@ -67,7 +67,7 @@ public void AdjacencyList(List edges_InOut, List closedC //TODO 直接进入穷举...图....返回参数取消掉 //还需要优化深度优先算法=>逆时针最短路径 - //如果次方案测试通过,删除所有 DESIGN0409 的字段所涉及的操作 + //如果此方案测试通过,删除所有 DESIGN0409 的字段所涉及的操作 } /// @@ -130,16 +130,45 @@ private static List PureKnots(List knots) //遍历顺序可能导致:先有{a,b}来找{c,d}找不到,会生成一条{c,d};但是完成纯化之后会有{{a,b,c}{c,d}} //所以需要生成完,再找一次首尾. Basal.ArrayEx.Deduplication(peList, (first, last) => { + byte actionNum = 0;//不执行 if (first.First!.Value == last.First!.Value)//{{c,b,a}{c,d}}=>{d,c,b,a} { - last.ForEach(a => first.AddFirst(a)); - return true; + actionNum = 1; } else if (first.Last!.Value == last.First!.Value)//{{a,b,c}{c,d}}=>{a,b,c,d} { - last.ForEach(a => first.AddLast(a)); + actionNum = 2; + } + else if (first.First!.Value == last.Last!.Value)//{{c,b,a}{d,c}}=>{d,c,b,a} + { + actionNum = 1; + last.Reverse(); + } + else if (first.Last!.Value == last.Last!.Value)//{{a,b,c}{d,c}}=>{a,b,c,d} + { + actionNum = 2; + last.Reverse(); + } + + if (actionNum == 1) + { + last.For((num, node) => { + if (num != 1)//跳过第一个 + first.AddFirst(node.Value); + return false; + }); + return true; + } + else if (actionNum == 2) + { + last.For((num, node) => { + if (num != 1)//跳过第一个 + first.AddLast(node.Value); + return false; + }); return true; } + return false; }); -- Gitee From 3785766d9c46b2e68750edba5ec7aae796da42e6 Mon Sep 17 00:00:00 2001 From: liuqihong <540762622@qq.com> Date: Mon, 11 Apr 2022 03:06:22 +0800 Subject: [PATCH 123/675] =?UTF-8?q?=E8=B0=83=E6=95=B4=E9=A6=96=E5=B0=BE?= =?UTF-8?q?=E5=8A=A0=E5=85=A5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/IFoxCAD.Cad/ExtensionMethod/Topo2.cs | 26 +++++++++++++++++------- 1 file changed, 19 insertions(+), 7 deletions(-) diff --git a/src/IFoxCAD.Cad/ExtensionMethod/Topo2.cs b/src/IFoxCAD.Cad/ExtensionMethod/Topo2.cs index fbe310f..f292de5 100644 --- a/src/IFoxCAD.Cad/ExtensionMethod/Topo2.cs +++ b/src/IFoxCAD.Cad/ExtensionMethod/Topo2.cs @@ -96,18 +96,34 @@ private static List PureKnots(List knots) var knot = knots[i]; if (knot.Count == 2)//共点只有两个图元,它们就是手拉手,没有第三者 { - //{{a,b}{b,c}{c,d}}=>{a,b,c,d} + //{{a,b}{b,c}{c,d}}=>{a,b,c}{c,d}=>{a,b,c,d} + //{{a,b}{b,c}{d,c}}=>{a,b,c}{d,c}=>{a,b,c,d} //其中一个已经加入,就找到指定多段线,插入子段 var a = knot.First!.Value; var b = knot.Last!.Value; //多段线集合中查询子段,返回多段线 var pe = PolyEdge.Contains(peList, a); - if (pe == null) + if (pe != null) + { + //含有的话,只会有两种情况:头部尾部 + var fi = pe.Find(a)!; + if (pe.First == fi) + pe.AddBefore(fi, b); + else if (pe.Last == fi) + pe.AddAfter(fi, b); + } + else { pe = PolyEdge.Contains(peList, b); if (pe != null) - pe.AddAfter(pe.Find(b)!, a); + { + var fi = pe.Find(b)!; + if (pe.First == fi) + pe.AddBefore(fi, a); + else if (pe.Last == fi) + pe.AddAfter(fi, a); + } else { //新建多段线加入 @@ -116,10 +132,6 @@ private static List PureKnots(List knots) peList.Add(new PolyEdge(a, b)); } } - else - { - pe.AddAfter(pe.Find(a)!, b); - } } else { -- Gitee From cc48c68ba71038bb75fa204601a1ee7ce45515f8 Mon Sep 17 00:00:00 2001 From: liuqihong <540762622@qq.com> Date: Mon, 11 Apr 2022 03:09:18 +0800 Subject: [PATCH 124/675] =?UTF-8?q?=E4=BF=AE=E6=94=B9=E7=BA=AF=E5=8C=96?= =?UTF-8?q?=E8=8A=82=E7=82=B9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/IFoxCAD.Cad/ExtensionMethod/Topo2.cs | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) diff --git a/src/IFoxCAD.Cad/ExtensionMethod/Topo2.cs b/src/IFoxCAD.Cad/ExtensionMethod/Topo2.cs index f292de5..5a232bd 100644 --- a/src/IFoxCAD.Cad/ExtensionMethod/Topo2.cs +++ b/src/IFoxCAD.Cad/ExtensionMethod/Topo2.cs @@ -144,13 +144,9 @@ private static List PureKnots(List knots) Basal.ArrayEx.Deduplication(peList, (first, last) => { byte actionNum = 0;//不执行 if (first.First!.Value == last.First!.Value)//{{c,b,a}{c,d}}=>{d,c,b,a} - { actionNum = 1; - } else if (first.Last!.Value == last.First!.Value)//{{a,b,c}{c,d}}=>{a,b,c,d} - { actionNum = 2; - } else if (first.First!.Value == last.Last!.Value)//{{c,b,a}{d,c}}=>{d,c,b,a} { actionNum = 1; @@ -169,7 +165,6 @@ private static List PureKnots(List knots) first.AddFirst(node.Value); return false; }); - return true; } else if (actionNum == 2) { @@ -178,10 +173,9 @@ private static List PureKnots(List knots) first.AddLast(node.Value); return false; }); - return true; } - return false; + return actionNum != 0; }); return peList; -- Gitee From 3eca93fbc615caaa829d946d6b7ed2d1ebe01dc3 Mon Sep 17 00:00:00 2001 From: liuqihong <540762622@qq.com> Date: Sun, 10 Apr 2022 19:26:10 +0000 Subject: [PATCH 125/675] =?UTF-8?q?update=20src/IFoxCAD.Cad/ExtensionMetho?= =?UTF-8?q?d/Topo2.cs.=20=E7=AC=AC=E4=B8=80=E4=B8=AA=E6=95=B0=E5=AD=97?= =?UTF-8?q?=E5=87=BA=E9=94=99?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/IFoxCAD.Cad/ExtensionMethod/Topo2.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/IFoxCAD.Cad/ExtensionMethod/Topo2.cs b/src/IFoxCAD.Cad/ExtensionMethod/Topo2.cs index 5a232bd..de0aabc 100644 --- a/src/IFoxCAD.Cad/ExtensionMethod/Topo2.cs +++ b/src/IFoxCAD.Cad/ExtensionMethod/Topo2.cs @@ -161,7 +161,7 @@ private static List PureKnots(List knots) if (actionNum == 1) { last.For((num, node) => { - if (num != 1)//跳过第一个 + if (num != 0)//跳过第一个 first.AddFirst(node.Value); return false; }); @@ -169,7 +169,7 @@ private static List PureKnots(List knots) else if (actionNum == 2) { last.For((num, node) => { - if (num != 1)//跳过第一个 + if (num != 0)//跳过第一个 first.AddLast(node.Value); return false; }); -- Gitee From 2c53b12ae13fa9cb59bb7ed6a8b4349585dd3817 Mon Sep 17 00:00:00 2001 From: liuqihong <540762622@qq.com> Date: Mon, 11 Apr 2022 05:33:07 +0800 Subject: [PATCH 126/675] =?UTF-8?q?=E5=A2=9E=E5=8A=A0=E7=BA=B3=E7=A7=92?= =?UTF-8?q?=E8=AE=A1=E6=97=B6=E5=99=A8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/IFoxCAD.Cad/ExtensionMethod/Tools.cs | 184 +++++++++++++++++++++-- 1 file changed, 171 insertions(+), 13 deletions(-) diff --git a/src/IFoxCAD.Cad/ExtensionMethod/Tools.cs b/src/IFoxCAD.Cad/ExtensionMethod/Tools.cs index 4c70511..bd7be8f 100644 --- a/src/IFoxCAD.Cad/ExtensionMethod/Tools.cs +++ b/src/IFoxCAD.Cad/ExtensionMethod/Tools.cs @@ -1,22 +1,15 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; - -namespace IFoxCAD.Cad +namespace IFoxCAD.Cad { public static class Tools { - public static void TestTimes(int count, string message, Action action) + public static void TestTimes2(int count, string message, Action action) { - System.Diagnostics.Stopwatch watch = new System.Diagnostics.Stopwatch(); + System.Diagnostics.Stopwatch watch = new(); watch.Start(); //开始监视代码运行时间 for (int i = 0; i < count; i++) - { - action.Invoke(); //需要测试的代码 - } + action.Invoke();//需要测试的代码 watch.Stop(); //停止监视 - TimeSpan timespan = watch.Elapsed; //获取当前实例测量得出的总时间 + TimeSpan timespan = watch.Elapsed; //获取当前实例测量得出的总时间 double time = timespan.TotalMilliseconds; string name = "毫秒"; if (timespan.TotalMilliseconds > 1000) @@ -26,6 +19,171 @@ public static void TestTimes(int count, string message, Action action) } Env.Print($"{message} 代码执行 {count} 次的时间:{time} ({name})"); //总毫秒数 } + + /// + /// 纳秒计时器 + /// + public static void TestTimes(int count, string message, Action action, + Timer.TimeEnum timeEnum = Timer.TimeEnum.Nanosecond) + { + double time = Timer.RunTime(() => { + for (int i = 0; i < count; i++) + action(); + }, timeEnum); + + string timeNameZn = ""; + switch (timeEnum) + { + case Timer.TimeEnum.Second: + timeNameZn = " 秒"; + break; + case Timer.TimeEnum.Millisecond: + timeNameZn = " 毫秒"; + break; + case Timer.TimeEnum.Microsecond: + timeNameZn = " 微秒"; + break; + case Timer.TimeEnum.Nanosecond: + timeNameZn = " 纳秒"; + break; + } + + Env.Print($"{message} 代码执行 {count} 次的时间:{time} ({timeNameZn})"); + } } -} + /* + //测试例子,同时验证两个计时器 + var stopwatch = new Stopwatch(); + Timer.RunTime(() => { + + stopwatch.Start(); + for (int i = 0; i < 10000000; i++) + i++; + stopwatch.Stop(); + + }, Timer.TimeEnum.Millisecond, "运行:"); + Console.WriteLine("运行毫秒:" + stopwatch.ElapsedMilliseconds); + */ + + public class Timer + { + [Flags] + public enum TimeEnum + { + /// + /// 秒 + /// + Second, + /// + /// 毫秒 + /// + Millisecond, + /// + /// 微秒 + /// + Microsecond, + /// + /// 纳秒 + /// + Nanosecond, + } + + [DllImport("Kernel32.dll")] + static extern bool QueryPerformanceCounter(out long lpPerformanceCount); + + /// + /// 这个函数会检索性能计数器的频率. + /// 性能计数器的频率在系统启动时是固定的,并且在所有处理器上都是一致的 + /// 因此,只需在应用初始化时查询频率,即可缓存结果 + /// 在运行 Windows XP 或更高版本的系统上,该函数将始终成功,因此永远不会返回零 + /// + /// + /// + [DllImport("Kernel32.dll")] + static extern bool QueryPerformanceFrequency(out long lpFrequency); + + long _startTime, _stopTime; + long _freq; + + public Timer() + { + _startTime = 0; + _stopTime = 0; + + if (!QueryPerformanceFrequency(out _freq)) + throw new Win32Exception("不支持高性能计数器"); + } + + /// + /// 开始计时器 + /// + public void Start() + { + System.Threading.Thread.Sleep(0); + QueryPerformanceCounter(out _startTime); + } + + /// + /// 停止计时器 + /// + public void Stop() + { + QueryPerformanceCounter(out _stopTime); + _Second = (double)(_stopTime - _startTime) / _freq; + } + double _Second = 0; + + // 返回计时器经过时间 + public double Second => _Second; + public double Millisecond => _Second * 1000.0; + public double Microsecond => _Second * 1000000.0; + public double Nanosecond => _Second * 1000000000.0; + + public static double RunTime(Action action, TimeEnum timeEnum = TimeEnum.Nanosecond, string? msg = null) + { + var nanoSecond = new Timer(); + nanoSecond.Start(); + action(); + nanoSecond.Stop(); + + double time = 0; + switch (timeEnum) + { + case TimeEnum.Second: + time = nanoSecond.Second; + break; + case TimeEnum.Millisecond: + time = nanoSecond.Millisecond; + break; + case TimeEnum.Microsecond: + time = nanoSecond.Microsecond; + break; + case TimeEnum.Nanosecond: + time = nanoSecond.Nanosecond; + break; + } + if (msg != null) + { + string timeNameZn = ""; + switch (timeEnum) + { + case TimeEnum.Second: + timeNameZn = " 秒"; + break; + case TimeEnum.Millisecond: + timeNameZn = " 毫秒"; + break; + case TimeEnum.Microsecond: + timeNameZn = " 微秒"; + break; + case TimeEnum.Nanosecond: + timeNameZn = " 纳秒"; + break; + } + Console.WriteLine(msg + " " + time + timeNameZn); + } + return time; + } + } +} \ No newline at end of file -- Gitee From 6a6b6c4c1ad4bd50b3a8173640f1d8d3b88c0a50 Mon Sep 17 00:00:00 2001 From: liuqihong <540762622@qq.com> Date: Mon, 11 Apr 2022 12:22:49 +0800 Subject: [PATCH 127/675] =?UTF-8?q?=E9=87=8D=E5=A4=8D=E5=8A=9F=E8=83=BD?= =?UTF-8?q?=E5=8F=96=E6=B6=88?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/IFoxCAD.Cad/ExtensionMethod/Topo2.cs | 122 +++++++++++++---------- 1 file changed, 72 insertions(+), 50 deletions(-) diff --git a/src/IFoxCAD.Cad/ExtensionMethod/Topo2.cs b/src/IFoxCAD.Cad/ExtensionMethod/Topo2.cs index de0aabc..4edd38c 100644 --- a/src/IFoxCAD.Cad/ExtensionMethod/Topo2.cs +++ b/src/IFoxCAD.Cad/ExtensionMethod/Topo2.cs @@ -48,17 +48,22 @@ public void AdjacencyList(List edges_InOut, List closedC for (int i = knots.Count - 1; i >= 0; i--) { var kn = knots[i]; - if (kn.Count == 0)//此条件不会触发;剔除过程不变0,因为移除了kn;剔除前也不会是0,因为必然初始化并加入; + + if (kn.Count == 0) + { + //此条件不会触发;剔除过程不变0,因为移除了kn; + //剔除前也不会是0,因为必然初始化并加入; knots.RemoveAt(i); - else if (kn.Count == 1)//孤独点容器必然储存着尾巴图元 + } + else if (kn.Count == 1) { + //孤独点容器必然储存着尾巴图元 //尾巴图元的头点可能有多个连接,因此尾巴图元会在其他集合中,需要移除其他集合的, //而尾点只有一个 var fir = kn.First(); for (int j = 0; j < knots.Count; j++) knots[j].Remove(fir); - //剔除点,减少再次进入剔除尾巴 knots.RemoveAt(i); } } @@ -74,7 +79,7 @@ public void AdjacencyList(List edges_InOut, List closedC /// 纯化节点 /// /// - private static List PureKnots(List knots) + List PureKnots(List knots) { /* * 纯化节点: @@ -90,58 +95,20 @@ private static List PureKnots(List knots) * 但是此操作能够使得有2就加速 */ - var peList = new List(); + var peListAll = new List(); + var peList2 = new List(); for (int i = 0; i < knots.Count; i++) { var knot = knots[i]; - if (knot.Count == 2)//共点只有两个图元,它们就是手拉手,没有第三者 - { - //{{a,b}{b,c}{c,d}}=>{a,b,c}{c,d}=>{a,b,c,d} - //{{a,b}{b,c}{d,c}}=>{a,b,c}{d,c}=>{a,b,c,d} - //其中一个已经加入,就找到指定多段线,插入子段 - var a = knot.First!.Value; - var b = knot.Last!.Value; - - //多段线集合中查询子段,返回多段线 - var pe = PolyEdge.Contains(peList, a); - if (pe != null) - { - //含有的话,只会有两种情况:头部尾部 - var fi = pe.Find(a)!; - if (pe.First == fi) - pe.AddBefore(fi, b); - else if (pe.Last == fi) - pe.AddAfter(fi, b); - } - else - { - pe = PolyEdge.Contains(peList, b); - if (pe != null) - { - var fi = pe.Find(b)!; - if (pe.First == fi) - pe.AddBefore(fi, a); - else if (pe.Last == fi) - pe.AddAfter(fi, a); - } - else - { - //新建多段线加入 - //遍历顺序可能导致:先有{a,b}来找{d,e}找不到,会生成一条{d,e}, - //所以需要生成完,再找一次首尾. - peList.Add(new PolyEdge(a, b)); - } - } - } + if (knot.Count == 2) + HandInHandKont(peList2, knot); else - { - peList.Add(new PolyEdge(knot)); - } + peListAll.Add(new PolyEdge(knot)); } - //遍历顺序可能导致:先有{a,b}来找{c,d}找不到,会生成一条{c,d};但是完成纯化之后会有{{a,b,c}{c,d}} + //遍历顺序可能导致:先有{a,b}来找{c,d}找不到,会生成一条{c,d} //所以需要生成完,再找一次首尾. - Basal.ArrayEx.Deduplication(peList, (first, last) => { + Basal.ArrayEx.Deduplication(peList2, (first, last) => { byte actionNum = 0;//不执行 if (first.First!.Value == last.First!.Value)//{{c,b,a}{c,d}}=>{d,c,b,a} actionNum = 1; @@ -178,6 +145,61 @@ private static List PureKnots(List knots) return actionNum != 0; }); - return peList; + peListAll.AddRange(peList2); + return peListAll; + } + + /// + /// 共点只有两个图元,它们就是手拉手,没有第三者 + /// + void HandInHandKont(List peList, Knot knot) + { + var a = knot.First!.Value; + var b = knot.Last!.Value; + peList.Add(new PolyEdge(a, b)); + } + + /// + /// 共点只有两个图元,它们就是手拉手,没有第三者 + /// + [Obsolete("和双重循环功能重复了",true)] + void HandInHandKont2(List peList, Knot knot) + { + //{{a,b}{b,c}{c,d}}=>{a,b,c}{c,d}=>{a,b,c,d} + //{{a,b}{b,c}{d,c}}=>{a,b,c}{d,c}=>{a,b,c,d} + //其中一个已经加入,就找到指定多段线,插入子段 + var a = knot.First!.Value; + var b = knot.Last!.Value; + + //多段线集合中查询子段,返回多段线 + var pe = PolyEdge.Contains(peList, a); + if (pe != null) + { + //含有的话,只会有两种情况:头部尾部 + var fi = pe.Find(a)!; + if (pe.First == fi) + pe.AddBefore(fi, b); + else if (pe.Last == fi) + pe.AddAfter(fi, b); + } + else + { + pe = PolyEdge.Contains(peList, b); + if (pe != null) + { + var fi = pe.Find(b)!; + if (pe.First == fi) + pe.AddBefore(fi, a); + else if (pe.Last == fi) + pe.AddAfter(fi, a); + } + else + { + //新建多段线加入 + //遍历顺序可能导致:先有{a,b}来找{d,e}找不到,会生成一条{d,e}, + //所以需要生成完,再找一次首尾. + peList.Add(new PolyEdge(a, b)); + } + } } } \ No newline at end of file -- Gitee From e46146ea8c5eb57cadf4ff59c3c97b8073251cbd Mon Sep 17 00:00:00 2001 From: liuqihong <540762622@qq.com> Date: Mon, 11 Apr 2022 13:41:55 +0800 Subject: [PATCH 128/675] =?UTF-8?q?=E6=8C=87=E9=92=88=E9=87=8D=E7=BD=AE?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/IFoxCAD.Cad/ExtensionMethod/Edge.cs | 3 - src/IFoxCAD.Cad/ExtensionMethod/Topo2.cs | 150 ++++++++++------------- 2 files changed, 68 insertions(+), 85 deletions(-) diff --git a/src/IFoxCAD.Cad/ExtensionMethod/Edge.cs b/src/IFoxCAD.Cad/ExtensionMethod/Edge.cs index 64e9658..1b4cb33 100644 --- a/src/IFoxCAD.Cad/ExtensionMethod/Edge.cs +++ b/src/IFoxCAD.Cad/ExtensionMethod/Edge.cs @@ -492,9 +492,6 @@ public PolyEdge(Knot kn) { public Point3d Point; // - public Knot() - { - } public Knot(Point3d point, Edge edge) { Point = point; diff --git a/src/IFoxCAD.Cad/ExtensionMethod/Topo2.cs b/src/IFoxCAD.Cad/ExtensionMethod/Topo2.cs index 4edd38c..33045be 100644 --- a/src/IFoxCAD.Cad/ExtensionMethod/Topo2.cs +++ b/src/IFoxCAD.Cad/ExtensionMethod/Topo2.cs @@ -101,105 +101,91 @@ List PureKnots(List knots) { var knot = knots[i]; if (knot.Count == 2) - HandInHandKont(peList2, knot); + { + //共点只有两个图元,它们就是手拉手,没有第三者 + var a = knot.First!.Value; + var b = knot.Last!.Value; + peList2.Add(new PolyEdge(a, b)); + } else peListAll.Add(new PolyEdge(knot)); } //遍历顺序可能导致:先有{a,b}来找{c,d}找不到,会生成一条{c,d} //所以需要生成完,再找一次首尾. - Basal.ArrayEx.Deduplication(peList2, (first, last) => { - byte actionNum = 0;//不执行 - if (first.First!.Value == last.First!.Value)//{{c,b,a}{c,d}}=>{d,c,b,a} - actionNum = 1; - else if (first.Last!.Value == last.First!.Value)//{{a,b,c}{c,d}}=>{a,b,c,d} - actionNum = 2; - else if (first.First!.Value == last.Last!.Value)//{{c,b,a}{d,c}}=>{d,c,b,a} - { - actionNum = 1; - last.Reverse(); - } - else if (first.Last!.Value == last.Last!.Value)//{{a,b,c}{d,c}}=>{a,b,c,d} - { - actionNum = 2; - last.Reverse(); - } - - if (actionNum == 1) - { - last.For((num, node) => { - if (num != 0)//跳过第一个 - first.AddFirst(node.Value); - return false; - }); - } - else if (actionNum == 2) - { - last.For((num, node) => { - if (num != 0)//跳过第一个 - first.AddLast(node.Value); - return false; - }); - } - - return actionNum != 0; - }); + T1(peList2); peListAll.AddRange(peList2); return peListAll; } /// - /// 共点只有两个图元,它们就是手拉手,没有第三者 - /// - void HandInHandKont(List peList, Knot knot) - { - var a = knot.First!.Value; - var b = knot.Last!.Value; - peList.Add(new PolyEdge(a, b)); - } - - /// - /// 共点只有两个图元,它们就是手拉手,没有第三者 + /// PolyEdge{a,b}{b,c}=>{a,b,c} /// - [Obsolete("和双重循环功能重复了",true)] - void HandInHandKont2(List peList, Knot knot) + /// + void T1(List list) { - //{{a,b}{b,c}{c,d}}=>{a,b,c}{c,d}=>{a,b,c,d} - //{{a,b}{b,c}{d,c}}=>{a,b,c}{d,c}=>{a,b,c,d} - //其中一个已经加入,就找到指定多段线,插入子段 - var a = knot.First!.Value; - var b = knot.Last!.Value; - - //多段线集合中查询子段,返回多段线 - var pe = PolyEdge.Contains(peList, a); - if (pe != null) + for (int i = 0; i < list.Count; i++) { - //含有的话,只会有两种情况:头部尾部 - var fi = pe.Find(a)!; - if (pe.First == fi) - pe.AddBefore(fi, b); - else if (pe.Last == fi) - pe.AddAfter(fi, b); - } - else - { - pe = PolyEdge.Contains(peList, b); - if (pe != null) + var plA = list[i]; + for (int j = list.Count - 1; j > i; j--) { - var fi = pe.Find(b)!; - if (pe.First == fi) - pe.AddBefore(fi, a); - else if (pe.Last == fi) - pe.AddAfter(fi, a); - } - else - { - //新建多段线加入 - //遍历顺序可能导致:先有{a,b}来找{d,e}找不到,会生成一条{d,e}, - //所以需要生成完,再找一次首尾. - peList.Add(new PolyEdge(a, b)); + var plB = list[j]; + + byte actionNum = 0;//不执行 + if (plA.First!.Value == plB.First!.Value)//{{c,b,a}{c,d}}=>{d,c,b,a} + actionNum = 1; + else if (plA.Last!.Value == plB.First!.Value)//{{a,b,c}{c,d}}=>{a,b,c,d} + actionNum = 2; + else if (plA.First!.Value == plB.Last!.Value)//{{c,b,a}{d,c}}=>{d,c,b,a} + { + actionNum = 1; + plB.Reverse(); + } + else if (plA.Last!.Value == plB.Last!.Value)//{{a,b,c}{d,c}}=>{a,b,c,d} + { + actionNum = 2; + plB.Reverse(); + } + + if (actionNum == 1) + { + plB.For((num, node) => { + if (num != 0)//跳过第一个 + plA.AddFirst(node.Value); + return false; + }); + } + else if (actionNum == 2) + { + plB.For((num, node) => { + if (num != 0)//跳过第一个 + plA.AddLast(node.Value); + return false; + }); + } + + if (actionNum != 0) + { + list.RemoveAt(j); + j = list.Count - 1;//指针重拨,一旦加入就从尾开始 + } } } } + + //void T2(List peList2) + //{ + // var group = GroupExBy(peList2, (a, b) => { + // if (a.First!.Value == b.First!.Value || + // a.First!.Value == b.Last!.Value || + // a.Last!.Value == b.First!.Value || + // a.Last!.Value == b.Last!.Value) + // { + // //这样就会有{a,b,b,c}四个四个为一组,而且要消重 + // return true; + // } + // return false; + // }); + //} } \ No newline at end of file -- Gitee From 0d25853fec66586ddcc158b7a8da963c8d22eb01 Mon Sep 17 00:00:00 2001 From: liuqihong <540762622@qq.com> Date: Mon, 11 Apr 2022 05:46:32 +0000 Subject: [PATCH 129/675] =?UTF-8?q?update=20src/IFoxCAD.Cad/ExtensionMetho?= =?UTF-8?q?d/Topo2.cs.=20=E4=BF=AE=E6=94=B9=E6=B3=A8=E9=87=8A?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/IFoxCAD.Cad/ExtensionMethod/Topo2.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/IFoxCAD.Cad/ExtensionMethod/Topo2.cs b/src/IFoxCAD.Cad/ExtensionMethod/Topo2.cs index 33045be..67f3b18 100644 --- a/src/IFoxCAD.Cad/ExtensionMethod/Topo2.cs +++ b/src/IFoxCAD.Cad/ExtensionMethod/Topo2.cs @@ -151,7 +151,7 @@ void T1(List list) if (actionNum == 1) { plB.For((num, node) => { - if (num != 0)//跳过第一个 + if (num != 0)//跳过第一个,它是重复的 plA.AddFirst(node.Value); return false; }); @@ -159,7 +159,7 @@ void T1(List list) else if (actionNum == 2) { plB.For((num, node) => { - if (num != 0)//跳过第一个 + if (num != 0)//跳过第一个,它是重复的 plA.AddLast(node.Value); return false; }); -- Gitee From 3752f5beeb586d6cf6bf4351df80f1e1142e7b97 Mon Sep 17 00:00:00 2001 From: liuqihong <540762622@qq.com> Date: Mon, 11 Apr 2022 08:02:08 +0000 Subject: [PATCH 130/675] =?UTF-8?q?update=20src/IFoxCAD.Cad/ExtensionMetho?= =?UTF-8?q?d/Topo2.cs.=20=E4=BF=AE=E6=94=B9=E6=B3=A8=E9=87=8A?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/IFoxCAD.Cad/ExtensionMethod/Topo2.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/IFoxCAD.Cad/ExtensionMethod/Topo2.cs b/src/IFoxCAD.Cad/ExtensionMethod/Topo2.cs index 67f3b18..9888e28 100644 --- a/src/IFoxCAD.Cad/ExtensionMethod/Topo2.cs +++ b/src/IFoxCAD.Cad/ExtensionMethod/Topo2.cs @@ -151,7 +151,7 @@ void T1(List list) if (actionNum == 1) { plB.For((num, node) => { - if (num != 0)//跳过第一个,它是重复的 + if (num != 0)//跳过共元 plA.AddFirst(node.Value); return false; }); @@ -159,7 +159,7 @@ void T1(List list) else if (actionNum == 2) { plB.For((num, node) => { - if (num != 0)//跳过第一个,它是重复的 + if (num != 0)//跳过共元 plA.AddLast(node.Value); return false; }); -- Gitee From e501bb047f28ad1518b4257d52294bbc7cbd5c10 Mon Sep 17 00:00:00 2001 From: liuqihong <540762622@qq.com> Date: Mon, 11 Apr 2022 21:16:50 +0800 Subject: [PATCH 131/675] =?UTF-8?q?=E7=8B=90=E5=93=A5=E7=89=88=E4=B9=9F?= =?UTF-8?q?=E5=81=9A=E5=90=88=E5=B9=B6=E5=A4=9A=E6=AE=B5=E7=BA=BF?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/IFoxCAD.Cad/ExtensionMethod/Edge.cs | 25 ++++- src/IFoxCAD.Cad/ExtensionMethod/Topo.cs | 111 +++++++++++++++++++++-- src/IFoxCAD.Cad/ExtensionMethod/Topo2.cs | 37 ++------ 3 files changed, 135 insertions(+), 38 deletions(-) diff --git a/src/IFoxCAD.Cad/ExtensionMethod/Edge.cs b/src/IFoxCAD.Cad/ExtensionMethod/Edge.cs index 1b4cb33..a749157 100644 --- a/src/IFoxCAD.Cad/ExtensionMethod/Edge.cs +++ b/src/IFoxCAD.Cad/ExtensionMethod/Edge.cs @@ -442,8 +442,7 @@ public class PolyEdge : LoopList { public PolyEdge(params Edge[] ps) { - for (int i = 0; i < ps.Length; i++) - Add(ps[i]); + AddRange(ps); } public PolyEdge(Knot kn) @@ -451,6 +450,28 @@ public PolyEdge(Knot kn) AddRange(kn); } + public List Points() + { + List pts = new(); + + //ΪʱӶ,ԵҲ + var ge = GetEnumerator(); + while (ge.MoveNext()) + { + var sp = ge.Current.GeCurve3d.StartPoint; + if (pts.Count == 0) + pts.Add(sp); + else if (pts[pts.Count - 1] != sp)//Ӷغϵ + pts.Add(sp); + + var ep = ge.Current.GeCurve3d.EndPoint; + if (pts.Count == 0) + pts.Add(ep); + else if (pts[pts.Count - 1] != ep) + pts.Add(ep); + } + return pts; + } /// /// Ӷηض diff --git a/src/IFoxCAD.Cad/ExtensionMethod/Topo.cs b/src/IFoxCAD.Cad/ExtensionMethod/Topo.cs index e2880aa..5984836 100644 --- a/src/IFoxCAD.Cad/ExtensionMethod/Topo.cs +++ b/src/IFoxCAD.Cad/ExtensionMethod/Topo.cs @@ -9,8 +9,12 @@ public class Topo // cad容差类 public static Tolerance CadTolerance = new(1e-6, 1e-6); + public Topo(List curves) { + if (curves == null || curves.Count == 0) + throw new ArgumentNullException(nameof(curves)); + List curveList = new(); //提取包围盒信息 @@ -164,7 +168,7 @@ public void AdjacencyList(List edges_InOut, List closedC var sp = edge.GeCurve3d.StartPoint; var ep = edge.GeCurve3d.EndPoint; - + if (knots.Contains(edge.GeCurve3d.StartPoint)) { //含有就是其他曲线"共用"此交点, @@ -198,13 +202,106 @@ public void AdjacencyList(List edges_InOut, List closedC //这里把交点只有一条曲线通过的点过滤掉了,也就是尾巴的图元, //剩下的都是闭合的曲线连接了,每个点都至少有两条曲线通过 var tmp = edges_InOut - .Except(closedEdges) - .Where(e => nums[e.StartIndex] > 1 && nums[e.EndIndex] > 1); + .Except(closedEdges)/*闭合多段线*/ + .Where(e => nums[e.StartIndex] > 1 && nums[e.EndIndex] > 1)/*剪枝:尾巴多段线*/; + +#if true2 //待测试... + //无第三者图元提取,并且合并多段线 + var handInhand = tmp.Where(e => nums[e.StartIndex] == 1 && nums[e.EndIndex] == 1); + var gPe = GetPolyEdge(handInhand); + var links2 = MergePolyEdge(gPe); + + //将手拉手的剔除,然后转为多段线 + var tmpArr = tmp.Except(handInhand); + var links = GetPolyEdge(tmpArr); + + links.AddRange(links2); + return links; //提供给图 +#else + var tmpArr = tmp.ToArray(); + edges_InOut.Clear(); + for (int i = 0; i < tmpArr.Length; i++) + edges_InOut.Add(tmpArr[i]); +#endif + } + + /// + /// 转为多段线 + /// + /// + /// + List GetPolyEdge(IEnumerable edges) + { + var links = new List(); + var ge = edges.GetEnumerator(); + while (ge.MoveNext()) + links.Add(new PolyEdge(ge.Current)); + return links; + } - var tmpArr = tmp.ToArray();//Clear导致tmp失效 - edges_InOut.Clear(); - for (int i = 0; i < tmpArr.Length; i++) - edges_InOut.Add(tmpArr[i]); + /// + /// 合并多段线 + /// + /// + List MergePolyEdge(List pes) + { + //然后要利用双循环,提取a和b比较共点 + for (int i = 0; i < pes.Count; i++) + { + var plA = pes[i]; + for (int j = pes.Count - 1; j > i; j--) + { + var plB = pes[j]; + + var apts = plA.Points(); + var bpts = plB.Points(); + var aSp = apts[0]; + var aEp = apts[apts.Count - 1]; + var bSp = bpts[0]; + var bEp = bpts[apts.Count - 1]; + + byte actionNum = 0;//不执行 + if (aSp == bSp)//{{c,b,a}{c,d}}=>{d,c,b,a} + actionNum = 1; + else if (aEp == bSp)//{{a,b,c}{c,d}}=>{a,b,c,d} + actionNum = 2; + else if (aSp == bEp)//{{c,b,a}{d,c}}=>{d,c,b,a} + { + actionNum = 1; + plB.Reverse(); + } + else if (aEp == bEp)//{{a,b,c}{d,c}}=>{a,b,c,d} + { + actionNum = 2; + plB.Reverse(); + } + + if (actionNum == 1) + { + plB.For((num, node) => { + if (num != 0)//跳过共元 + plA.AddFirst(node.Value); + return false; + }); + } + else if (actionNum == 2) + { + plB.For((num, node) => { + if (num != 0)//跳过共元 + plA.AddLast(node.Value); + return false; + }); + } + + if (actionNum != 0) + { + pes.RemoveAt(j); + j = pes.Count - 1;//指针重拨,一旦加入就从尾开始 + } + + } + } + return pes; } /// diff --git a/src/IFoxCAD.Cad/ExtensionMethod/Topo2.cs b/src/IFoxCAD.Cad/ExtensionMethod/Topo2.cs index 9888e28..3be6197 100644 --- a/src/IFoxCAD.Cad/ExtensionMethod/Topo2.cs +++ b/src/IFoxCAD.Cad/ExtensionMethod/Topo2.cs @@ -57,9 +57,8 @@ public void AdjacencyList(List edges_InOut, List closedC } else if (kn.Count == 1) { - //孤独点容器必然储存着尾巴图元 - //尾巴图元的头点可能有多个连接,因此尾巴图元会在其他集合中,需要移除其他集合的, - //而尾点只有一个 + //尾巴图元的尾点只有一个,因此它被孤独容器储存着; + //尾巴图元的头点可能有多个连接,会储存在其他容器,需要移除; var fir = kn.First(); for (int j = 0; j < knots.Count; j++) knots[j].Remove(fir); @@ -101,29 +100,24 @@ List PureKnots(List knots) { var knot = knots[i]; if (knot.Count == 2) - { - //共点只有两个图元,它们就是手拉手,没有第三者 - var a = knot.First!.Value; - var b = knot.Last!.Value; - peList2.Add(new PolyEdge(a, b)); - } + peList2.Add(new PolyEdge(knot)); else peListAll.Add(new PolyEdge(knot)); } - //遍历顺序可能导致:先有{a,b}来找{c,d}找不到,会生成一条{c,d} - //所以需要生成完,再找一次首尾. - T1(peList2); + //共点只有两个图元,它们就是手拉手,没有第三者,然后合并多段线 + MergePolyEdge(peList2); peListAll.AddRange(peList2); return peListAll; } /// - /// PolyEdge{a,b}{b,c}=>{a,b,c} + /// 合并多段线 + /// PolyEdge{{a,b}{b,c}..}=>{a,b,c..} /// /// - void T1(List list) + void MergePolyEdge(List list) { for (int i = 0; i < list.Count; i++) { @@ -173,19 +167,4 @@ void T1(List list) } } } - - //void T2(List peList2) - //{ - // var group = GroupExBy(peList2, (a, b) => { - // if (a.First!.Value == b.First!.Value || - // a.First!.Value == b.Last!.Value || - // a.Last!.Value == b.First!.Value || - // a.Last!.Value == b.Last!.Value) - // { - // //这样就会有{a,b,b,c}四个四个为一组,而且要消重 - // return true; - // } - // return false; - // }); - //} } \ No newline at end of file -- Gitee From 8812984a373445e159a5ba4cf77eb703ae6451a9 Mon Sep 17 00:00:00 2001 From: liuqihong <540762622@qq.com> Date: Mon, 11 Apr 2022 21:23:55 +0800 Subject: [PATCH 132/675] =?UTF-8?q?=E7=BC=A9=E5=87=8F=E4=BB=A3=E7=A0=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/IFoxCAD.Cad/ExtensionMethod/Topo.cs | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) diff --git a/src/IFoxCAD.Cad/ExtensionMethod/Topo.cs b/src/IFoxCAD.Cad/ExtensionMethod/Topo.cs index 5984836..39f7466 100644 --- a/src/IFoxCAD.Cad/ExtensionMethod/Topo.cs +++ b/src/IFoxCAD.Cad/ExtensionMethod/Topo.cs @@ -205,17 +205,17 @@ public void AdjacencyList(List edges_InOut, List closedC .Except(closedEdges)/*闭合多段线*/ .Where(e => nums[e.StartIndex] > 1 && nums[e.EndIndex] > 1)/*剪枝:尾巴多段线*/; -#if true2 //待测试... +#if true2 + //待测试... //无第三者图元提取,并且合并多段线 var handInhand = tmp.Where(e => nums[e.StartIndex] == 1 && nums[e.EndIndex] == 1); var gPe = GetPolyEdge(handInhand); - var links2 = MergePolyEdge(gPe); + MergePolyEdge(gPe); //将手拉手的剔除,然后转为多段线 - var tmpArr = tmp.Except(handInhand); - var links = GetPolyEdge(tmpArr); - - links.AddRange(links2); + var links = GetPolyEdge(tmp.Except(handInhand)); + + links.AddRange(gPe); return links; //提供给图 #else var tmpArr = tmp.ToArray(); @@ -243,7 +243,7 @@ List GetPolyEdge(IEnumerable edges) /// 合并多段线 /// /// - List MergePolyEdge(List pes) + void MergePolyEdge(List pes) { //然后要利用双循环,提取a和b比较共点 for (int i = 0; i < pes.Count; i++) @@ -301,7 +301,6 @@ List MergePolyEdge(List pes) } } - return pes; } /// -- Gitee From aa198c6d30e1f6f51727791e31891703955d82cd Mon Sep 17 00:00:00 2001 From: vicwjb <403009819@qq.com> Date: Mon, 11 Apr 2022 22:42:36 +0800 Subject: [PATCH 133/675] =?UTF-8?q?=E7=9B=AE=E5=89=8D=E5=B7=B2=E7=BB=8F?= =?UTF-8?q?=E5=8F=AF=E4=BB=A5=E8=BF=90=E8=A1=8C=EF=BC=8C=E8=BF=98=E5=B7=AE?= =?UTF-8?q?=E8=BF=87=E6=BB=A4=E6=8E=89=E9=87=8D=E5=A4=8D=E7=9A=84=EF=BC=8C?= =?UTF-8?q?=E7=8E=B0=E6=9C=89=E7=9A=84=E8=BF=87=E6=BB=A4=E4=B8=8D=E5=AE=8C?= =?UTF-8?q?=E5=96=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/IFoxCAD.Cad/QuadTree/Graph.cs | 134 +++++++++++++++++++++--------- 1 file changed, 97 insertions(+), 37 deletions(-) diff --git a/src/IFoxCAD.Cad/QuadTree/Graph.cs b/src/IFoxCAD.Cad/QuadTree/Graph.cs index 6108b88..bdf3299 100644 --- a/src/IFoxCAD.Cad/QuadTree/Graph.cs +++ b/src/IFoxCAD.Cad/QuadTree/Graph.cs @@ -1,9 +1,7 @@  -using Autodesk.AutoCAD.BoundaryRepresentation; using Exception = System.Exception; - namespace IFoxCAD.Cad.FirstGraph { /// @@ -279,7 +277,6 @@ public LoopList GetCurves(List graphVertices) } - /// /// 克隆此图。目测是深克隆 /// @@ -344,7 +341,7 @@ public virtual string ToReadable() /// 邻接表图实现的顶点。 /// IEnumerable 枚举所有邻接点。 /// - public struct GraphVertex : IGraphVertex, IEquatable, IComparable ,IComparable + public class GraphVertex : IGraphVertex, IEquatable, IComparable ,IComparable { public Point3d Data { get; private set; } @@ -515,56 +512,119 @@ public DepthFirst(IGraph graph) /// public void FindAll() { + //var visited = new List(); foreach (var item in GraphVertices) { - var visited = new List(); - dfs(_graph, item, visited, e => { - var copy = new IGraphVertex[e.Count]; - e.CopyTo(copy); - Curve3ds.Add(copy.ToList()); - }); + + dfs(_graph, new List { item }); + } } /// /// 递归 DFS。 /// - private void dfs(IGraph graph, IGraphVertex current, List visited, Action>? action) + private void dfs(IGraph graph, List visited) { - - visited.Add(current); - - // 改造这个搜索函数,当搜索闭合的时候,将闭合链存入结果列表 - foreach (var edge in graph.GetAdjacencyList(current)) + var startNode = visited[0]; + IGraphVertex nextNode; + var sub = new List(); + var adjlist = graph.GetAdjacencyList(startNode).ToList(); + for (int i = 0; i < adjlist.Count; i++) { - if (visited.Contains(edge)) + if (adjlist[i].Equals(startNode)) + { + nextNode = adjlist[i + 1]; + } + else + { + nextNode = adjlist[i]; + } + + if (!visited.Contains(nextNode)) { - if (visited[0].Equals(edge)) + sub = new List { nextNode }; + sub.AddRange(visited); + dfs(graph, sub); + } + else if (visited.Count > 2 && nextNode.Equals(visited[visited.Count - 1])) + { + var cur = RotateToSmallest(visited); + var inv = Invert(cur); + if (IsNew(cur) && IsNew(inv)) { - if (visited.Count == 2) - { - var cur1 = graph.GetEdge(visited[0], visited[1]); - var cur2 = graph.GetEdge(visited[1], visited[0]); - if (cur1 is not null && cur2 is not null && cur1.TargetEdge.IsEqualTo(cur2.TargetEdge)) - { - continue; - } // todo: 这里不太对,应该是两点的时候是两条一样就舍弃 - action?.Invoke(visited); - } - else if (visited.Count > 2) - { - action?.Invoke(visited); - } // todo: 这里不对,应该有一种回退机制,搜索到闭合了 就回退回去上一个点继续搜才对 - + Curve3ds.Add(cur); } - - continue; } - dfs(graph, edge, visited, action); - } + + // visited.Add(current); + + //// 改造这个搜索函数,当搜索闭合的时候,将闭合链存入结果列表 + // foreach (var vertex in graph.GetAdjacencyList(current)) + // { + // if (!visited.Contains(vertex)) + // { + + // dfs(graph, vertex, visited); + // } + // else if (visited.Count > 2 && vertex.Equals(visited[0])) + // { + // var curcycle = RotateToSmallest(visited); + // var invertcycle = Invert(curcycle); + // if (!Curve3ds.Contains(curcycle) && !Curve3ds.Contains(invertcycle)) + // { + // Curve3ds.Add(curcycle); + // } + // } + + // } } + + + private List RotateToSmallest(List lst) + { + var index = lst.IndexOf(lst.Min()); + return lst.Skip(index).Concat(lst.Take(index)).ToList(); + } + + private List Invert(List lst) + { + var tmp = lst.ToList(); + tmp.Reverse(); + return RotateToSmallest(tmp); + } + private bool Equals(List a, List b) + { + bool ret = (a[0] == b[0]) && (a.Count == b.Count); + + for (int i = 1; ret && (i < a.Count); i++) + if (!a[i].Equals( b[i])) + { + ret = false; + } + + return ret; + } + + + private bool IsNew(List path) + { + bool ret = true; + + foreach (var p in Curve3ds) + if (Equals(p, path)) + { + ret = false; + break; + } + + return ret; + } + + + } -- Gitee From be14fd25fae0d75cda3b0fe4acc12f522f3abe10 Mon Sep 17 00:00:00 2001 From: liuqihong <540762622@qq.com> Date: Tue, 12 Apr 2022 00:57:19 +0800 Subject: [PATCH 134/675] =?UTF-8?q?=E4=BF=AE=E6=94=B9=E4=BA=8B=E5=8A=A1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../ExtensionMethod/SelectionSetEx.cs | 1 - src/IFoxCAD.Cad/ExtensionMethod/Topo.cs | 8 +-- src/IFoxCAD.Cad/Runtime/DBTrans.cs | 54 ++++++++++++------- tests/Test/TestCurve.cs | 28 +++++----- 4 files changed, 52 insertions(+), 39 deletions(-) diff --git a/src/IFoxCAD.Cad/ExtensionMethod/SelectionSetEx.cs b/src/IFoxCAD.Cad/ExtensionMethod/SelectionSetEx.cs index 9de6ade..fa4b7b0 100644 --- a/src/IFoxCAD.Cad/ExtensionMethod/SelectionSetEx.cs +++ b/src/IFoxCAD.Cad/ExtensionMethod/SelectionSetEx.cs @@ -81,7 +81,6 @@ public static IEnumerable> GetObjectIdGroup(this Sel #endregion #region ForEach - /// /// 遍历选择集 /// diff --git a/src/IFoxCAD.Cad/ExtensionMethod/Topo.cs b/src/IFoxCAD.Cad/ExtensionMethod/Topo.cs index 39f7466..6ae7444 100644 --- a/src/IFoxCAD.Cad/ExtensionMethod/Topo.cs +++ b/src/IFoxCAD.Cad/ExtensionMethod/Topo.cs @@ -5,7 +5,7 @@ public class Topo // 碰撞链集合 List _CollisionChains; // 求交类(每次set自动重置,都会有个新的结果) - static CurveCurveIntersector3d _cci3d = new(); + static CurveCurveIntersector3d _Cci3d = new(); // cad容差类 public static Tolerance CadTolerance = new(1e-6, 1e-6); @@ -104,12 +104,12 @@ public void GetEdgesAndnewCurves(List infos_In, List edges_Out, var gc2 = curve2.Edge.GeCurve3d; var pars2 = curve2.Paramss; - _cci3d.Set(gc1, gc2, Vector3d.ZAxis); + _Cci3d.Set(gc1, gc2, Vector3d.ZAxis); //计算两条曲线的交点(多个),分别放入对应的交点参数集 - for (int k = 0; k < _cci3d.NumberOfIntersectionPoints; k++) + for (int k = 0; k < _Cci3d.NumberOfIntersectionPoints; k++) { - var pars = _cci3d.GetIntersectionParameters(k); + var pars = _Cci3d.GetIntersectionParameters(k); pars1.Add(pars[0]);//0是第一条曲线的交点参数 pars2.Add(pars[1]);//1是第二条曲线的交点参数 } diff --git a/src/IFoxCAD.Cad/Runtime/DBTrans.cs b/src/IFoxCAD.Cad/Runtime/DBTrans.cs index 3cc42ad..4c0c00f 100644 --- a/src/IFoxCAD.Cad/Runtime/DBTrans.cs +++ b/src/IFoxCAD.Cad/Runtime/DBTrans.cs @@ -29,23 +29,40 @@ public static DBTrans Top { get { - DBTrans trans; - try + /* + * 1.1 + * 事务栈上面有事务,这个事务属于当前文档, + * 那么直接提交原本事务然后再开一个(一直把栈前面的同数据库提交清空) + * 那不就发生跨事务读取图元了吗?....否决 + * + * 1.2 + * 跨文档事务出错 Autodesk.AutoCAD.Runtime.Exception:“eNotFromThisDocument” + * Curves.GetEntities()会从Top获取事务(Top会new一个),此时会是当前文档; + * 然后命令文中发生了 using var tr = new DBTrans(); + * 当退出命令此事务释放,但是从来不释放Top, + * 然后我新建了一个文档,再进行命令=>又进入Top,Top返回了前一个文档的事务 + * 因此所以无法清理栈,所以Dispose不触发,导致无法刷新图元和Ctrl+Z出错 + */ + + if (dBTrans.Count == 0)//静态获取的时候就是0 + throw new ArgumentNullException("事务栈没有任何事务,请在调用前创建:" + nameof(DBTrans)); + + var db = Application.DocumentManager.MdiActiveDocument.Database; + var trans = dBTrans.Peek(); + while (dBTrans.Count != 0 && trans.Database != db) //跨数据库 { + if (trans._commit) + trans.Commit(); + dBTrans.Pop(); trans = dBTrans.Peek(); } - catch (System.Exception) - { + if (dBTrans.Count == 0) trans = new DBTrans(); - } + return trans; } } - /// - /// 数据库 - /// - public Database Database { get; private set; } /// /// 文档 /// @@ -58,9 +75,14 @@ public static DBTrans Top /// 事务管理器 /// public Transaction Transaction { get; private set; } - + /// + /// 数据库 + /// + public Database Database { get; private set; } #endregion + + #region 构造函数 /// /// 默认构造函数,默认为打开当前文档,默认提交事务 @@ -76,9 +98,8 @@ public DBTrans(Document? doc = null, bool commit = true, bool doclock = false) Transaction = Database.TransactionManager.StartTransaction(); _commit = commit; if (doclock) - { documentLock = Document.LockDocument(); - } + dBTrans.Push(this); } @@ -111,8 +132,9 @@ public DBTrans(string fileName, bool commit = true) #endif if (File.Exists(fileName)) { - var doc = - Application.DocumentManager.Cast().FirstOrDefault(doc => doc.Name == fileName); + var doc = Application.DocumentManager + .Cast() + .FirstOrDefault(doc => doc.Name == fileName); if (doc is not null) { Database = doc.Database; @@ -123,13 +145,9 @@ public DBTrans(string fileName, bool commit = true) { Database = new Database(false, true); if (Path.GetExtension(fileName).ToLower().Contains("dxf")) - { Database.DxfIn(fileName, null); - } else - { Database.ReadDwgFile(fileName, FileOpenMode.OpenForReadAndWriteNoShare, true, null); - } Database.CloseInput(true); } } diff --git a/tests/Test/TestCurve.cs b/tests/Test/TestCurve.cs index 9bd50d5..169b639 100644 --- a/tests/Test/TestCurve.cs +++ b/tests/Test/TestCurve.cs @@ -22,13 +22,14 @@ public void TestPointInDict() Env.Print(dict[pt1]); } - - - [CommandMethod("testgraph")] public void TestGraph1() { - var ents = Env.Editor.SSGet().Value.GetEntities(); + using var tr = new DBTrans(); + var ents = Env.Editor.SSGet().Value?.GetEntities(); + if (ents == null) + return; + // 新建图 var graph = new IFoxCAD.Cad.FirstGraph.Graph(); // 将曲线加入到图中 @@ -51,14 +52,10 @@ public void TestGraph1() res.Add(tmp); } } - - using var tr = new DBTrans(); res.ForEach((i, t) => t.ForWrite(e => e.ColorIndex = i + 1)); tr.CurrentSpace.AddEntity(res); - - //var graph = new Graph(); //graph.AddVertex(1); //graph.AddVertex(2); @@ -81,15 +78,17 @@ public void TestGraph1() //graph.AddEdge(5, 3); //Env.Print(graph); } + } + public class TestCurve { [CommandMethod("testbreakcurve")] public void TestBreakCurve() { + using var tr = new DBTrans(); var ents = Env.Editor.SSGet().Value.GetEntities(); var tt = CurveEx.BreakCurve(ents.ToList()); - using var tr = new DBTrans(); tt.ForEach(t => t.ForWrite(e => e.ColorIndex = 1)); tr.CurrentSpace.AddEntity(tt); } @@ -97,6 +96,7 @@ public void TestBreakCurve() [CommandMethod("testCurveCurveIntersector3d")] public void TestCurveCurveIntersector3d() { + using var tr = new DBTrans(); var ents = Env.Editor.SSGet().Value.GetEntities() .Select(e => e.ToCompositeCurve3d()).ToList(); @@ -140,25 +140,21 @@ public void TestCurveCurveIntersector3d() } } - // var tt = CurveEx.Topo(ents.ToList()); - //using var tr = new DBTrans(); + // var tt = CurveEx.Topo(ents.ToList()); //tt.ForEach(t => t.ForWrite(e => e.ColorIndex = 1)); //tr.CurrentSpace.AddEntity(tt); } - - [CommandMethod("testtopo")] public void TestToPo() { + using var tr = new DBTrans(); var ents = Env.Editor.SSGet().Value?.GetEntities(); if (ents == null) return; var tt = CurveEx.Topo(ents.ToList()); - - using var tr = new DBTrans(); tt.ForEach((i, t) => t.ForWrite(e => e.ColorIndex = i)); tr.CurrentSpace.AddEntity(tt); } @@ -166,10 +162,10 @@ public void TestToPo() [CommandMethod("testGetEdgesAndnewCurves")] public void TestGetEdgesAndnewCurves() { + using var tr = new DBTrans(); var curves = Env.Editor.SSGet().Value?.GetEntities().ToList(); if (curves == null) return; - using var tr = new DBTrans(); var edgesGroup = new List>(); var closedCurve3dGroup = new List>(); -- Gitee From 057a8f814b4a97df0e17b4a55c7ed3b2f8ef574d Mon Sep 17 00:00:00 2001 From: liuqihong <540762622@qq.com> Date: Tue, 12 Apr 2022 00:58:30 +0800 Subject: [PATCH 135/675] =?UTF-8?q?=E4=BF=AE=E6=94=B9=E6=B3=A8=E9=87=8A?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/IFoxCAD.Cad/Runtime/DBTrans.cs | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/src/IFoxCAD.Cad/Runtime/DBTrans.cs b/src/IFoxCAD.Cad/Runtime/DBTrans.cs index 4c0c00f..b3c6654 100644 --- a/src/IFoxCAD.Cad/Runtime/DBTrans.cs +++ b/src/IFoxCAD.Cad/Runtime/DBTrans.cs @@ -30,12 +30,12 @@ public static DBTrans Top get { /* - * 1.1 + * 0x01 * 事务栈上面有事务,这个事务属于当前文档, * 那么直接提交原本事务然后再开一个(一直把栈前面的同数据库提交清空) * 那不就发生跨事务读取图元了吗?....否决 * - * 1.2 + * 0x02 * 跨文档事务出错 Autodesk.AutoCAD.Runtime.Exception:“eNotFromThisDocument” * Curves.GetEntities()会从Top获取事务(Top会new一个),此时会是当前文档; * 然后命令文中发生了 using var tr = new DBTrans(); @@ -62,7 +62,6 @@ public static DBTrans Top return trans; } } - /// /// 文档 /// @@ -81,8 +80,6 @@ public static DBTrans Top public Database Database { get; private set; } #endregion - - #region 构造函数 /// /// 默认构造函数,默认为打开当前文档,默认提交事务 -- Gitee From 3971d8335ddb5c03aecc39779950253e6f152921 Mon Sep 17 00:00:00 2001 From: vicwjb <403009819@qq.com> Date: Mon, 11 Apr 2022 23:46:13 +0800 Subject: [PATCH 136/675] =?UTF-8?q?=E6=94=B9=E8=BF=9B=20dfs=20=E7=BB=93?= =?UTF-8?q?=E6=9E=9C=E7=9A=84=E8=BF=87=E6=BB=A4=EF=BC=8C=E8=BF=98=E6=98=AF?= =?UTF-8?q?=E4=B8=8D=E5=AF=B9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/IFoxCAD.Cad/QuadTree/Graph.cs | 33 ++++++++++++++++-------------- src/IFoxCAD.Cad/QuadTree/IGraph.cs | 2 ++ 2 files changed, 20 insertions(+), 15 deletions(-) diff --git a/src/IFoxCAD.Cad/QuadTree/Graph.cs b/src/IFoxCAD.Cad/QuadTree/Graph.cs index bdf3299..7e06fad 100644 --- a/src/IFoxCAD.Cad/QuadTree/Graph.cs +++ b/src/IFoxCAD.Cad/QuadTree/Graph.cs @@ -34,7 +34,7 @@ public Graph() /// public void AddVertex(Point3d pt) { - var vertex = new GraphVertex(pt); + var vertex = new GraphVertex(pt, this); if (vertices.ContainsKey(vertex)) { throw new Exception("顶点已经存在。"); @@ -51,7 +51,7 @@ public void AddVertex(Point3d pt) /// public void RemoveVertex(Point3d pt) { - var vertex = new GraphVertex(pt); + var vertex = new GraphVertex(pt, this); if (!vertices.ContainsKey(vertex)) { throw new Exception("顶点不在此图中。"); @@ -87,8 +87,8 @@ public void RemoveVertex(Point3d pt) public void AddEdge(Curve3d value!!) { // 函数有问题,有一个端点在图里时,另一个点应该新增个顶点, - var start = new GraphVertex(value.StartPoint); - var end = new GraphVertex(value.EndPoint); + var start = new GraphVertex(value.StartPoint, this); + var end = new GraphVertex(value.EndPoint,this); if (vertices.ContainsKey(start) && !vertices.ContainsKey(end)) // 如果曲线的起点在邻接表字典里,终点不在 @@ -162,8 +162,8 @@ public void AddEdge(Curve3d value!!) /// public void RemoveEdge(Curve3d curve!!) { - var start = new GraphVertex(curve.StartPoint); - var end = new GraphVertex(curve.EndPoint); + var start = new GraphVertex(curve.StartPoint, this); + var end = new GraphVertex(curve.EndPoint, this); if (!vertices.ContainsKey(start) || !vertices.ContainsKey(end)) { @@ -344,14 +344,15 @@ public virtual string ToReadable() public class GraphVertex : IGraphVertex, IEquatable, IComparable ,IComparable { public Point3d Data { get; private set; } + public IGraph Graph { get; private set; } //public int Index { get; set; } - public GraphVertex(Point3d value) + public GraphVertex(Point3d value, IGraph graph) { Data = value; + Graph = graph; //Index = -1; } - public bool Equals(GraphVertex other) { return Data.IsEqualTo(other.Data, new Tolerance(1e-6,1e-6)); @@ -365,7 +366,6 @@ public override bool Equals(Object obj) else return Equals(personObj); } - public override int GetHashCode() { // 原来的代码 不起作用,那么就转字符串算 @@ -373,20 +373,23 @@ public override int GetHashCode() return (Data.X.ToString("n6"), Data.Y.ToString("n6"), Data.Z.ToString("n6")).GetHashCode(); } - public int CompareTo(IGraphVertex other) { if (Equals(other)) { return 0; } - else if (Data.X <= other.Data.X) - { - return -1; - } else { - return 1; + var edges = this.Graph.GetAdjacencyList(this); + if (edges.Contains(other)) + { + return 1; + } + else + { + return -1; + } } } diff --git a/src/IFoxCAD.Cad/QuadTree/IGraph.cs b/src/IFoxCAD.Cad/QuadTree/IGraph.cs index fa9b822..45ff6b3 100644 --- a/src/IFoxCAD.Cad/QuadTree/IGraph.cs +++ b/src/IFoxCAD.Cad/QuadTree/IGraph.cs @@ -74,6 +74,8 @@ public interface IGraphVertex /// 顶点的数据 /// Point3d Data { get; } + + IGraph Graph { get; } /// /// 顶点的邻接边表 /// -- Gitee From 961d1113aeb9c84be464b02410c4ca330d0bb6d3 Mon Sep 17 00:00:00 2001 From: vicwjb <403009819@qq.com> Date: Tue, 12 Apr 2022 19:02:15 +0800 Subject: [PATCH 137/675] =?UTF-8?q?=E4=BC=98=E5=8C=96dfs=E4=BB=A3=E7=A0=81?= =?UTF-8?q?=EF=BC=8C=E4=BE=9D=E7=84=B6=E6=9C=89=E9=97=AE=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/IFoxCAD.Cad/QuadTree/Graph.cs | 13 +++---------- 1 file changed, 3 insertions(+), 10 deletions(-) diff --git a/src/IFoxCAD.Cad/QuadTree/Graph.cs b/src/IFoxCAD.Cad/QuadTree/Graph.cs index 7e06fad..5372785 100644 --- a/src/IFoxCAD.Cad/QuadTree/Graph.cs +++ b/src/IFoxCAD.Cad/QuadTree/Graph.cs @@ -531,19 +531,12 @@ private void dfs(IGraph graph, List visited) { var startNode = visited[0]; IGraphVertex nextNode; - var sub = new List(); + List sub; var adjlist = graph.GetAdjacencyList(startNode).ToList(); for (int i = 0; i < adjlist.Count; i++) { - if (adjlist[i].Equals(startNode)) - { - nextNode = adjlist[i + 1]; - } - else - { - nextNode = adjlist[i]; - } - + + nextNode = adjlist[i]; if (!visited.Contains(nextNode)) { sub = new List { nextNode }; -- Gitee From bfe1ac331d779618beba5eb59e3b5e05eaf47696 Mon Sep 17 00:00:00 2001 From: vicwjb <403009819@qq.com> Date: Wed, 13 Apr 2022 00:43:33 +0800 Subject: [PATCH 138/675] =?UTF-8?q?=E4=BF=AE=E5=A4=8D=E5=9B=BE=E5=8F=8Adfs?= =?UTF-8?q?=EF=BC=8C=E7=8E=B0=E5=9C=A8=E7=BB=88=E4=BA=8E=E5=87=BA=E6=9D=A5?= =?UTF-8?q?=E6=AD=A3=E7=A1=AE=E7=9A=84=E7=BB=93=E6=9E=9C=EF=BC=8C=E5=90=8C?= =?UTF-8?q?=E6=97=B6=E5=A2=9E=E5=8A=A0=E4=BA=86=E5=AD=97=E5=85=B8=E5=8F=96?= =?UTF-8?q?key=E7=9A=84=E6=89=A9=E5=B1=95=E5=87=BD=E6=95=B0=EF=BC=88?= =?UTF-8?q?=E6=AD=A4=E5=87=BD=E6=95=B0=E6=97=B6=E9=97=B4=E5=A4=8D=E6=9D=82?= =?UTF-8?q?=E5=BA=A6O(n))?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/IFoxCAD.Basal/DictEx.cs | 27 +++++++++++ src/IFoxCAD.Basal/IFoxCAD.Basal.csproj | 2 +- src/IFoxCAD.Cad/IFoxCAD.Cad.csproj | 2 +- src/IFoxCAD.Cad/QuadTree/Graph.cs | 66 +++++++++++++++++--------- src/IFoxCAD.Cad/QuadTree/IGraph.cs | 2 +- 5 files changed, 73 insertions(+), 26 deletions(-) create mode 100644 src/IFoxCAD.Basal/DictEx.cs diff --git a/src/IFoxCAD.Basal/DictEx.cs b/src/IFoxCAD.Basal/DictEx.cs new file mode 100644 index 0000000..ac5746c --- /dev/null +++ b/src/IFoxCAD.Basal/DictEx.cs @@ -0,0 +1,27 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; + +namespace IFoxCAD.Basal +{ + public static class DictEx + { + public static TKey? GetKey(this IDictionary dict!!, TKey key!!) + { + if (dict.ContainsKey(key)) + { + foreach (var item in dict.Keys) + { + if (key.Equals(item)) + { + return item; + } + } + + } + return default; + + } + } +} diff --git a/src/IFoxCAD.Basal/IFoxCAD.Basal.csproj b/src/IFoxCAD.Basal/IFoxCAD.Basal.csproj index 567d1d3..bee482a 100644 --- a/src/IFoxCAD.Basal/IFoxCAD.Basal.csproj +++ b/src/IFoxCAD.Basal/IFoxCAD.Basal.csproj @@ -4,7 +4,7 @@ net35;net40;net45 true enable - 0.3.1 + 0.3.2 InspireFunction xsfhlzh;vicwjb 基于.NET的二次开发基本类库 diff --git a/src/IFoxCAD.Cad/IFoxCAD.Cad.csproj b/src/IFoxCAD.Cad/IFoxCAD.Cad.csproj index b3d0273..e4ef9c4 100644 --- a/src/IFoxCAD.Cad/IFoxCAD.Cad.csproj +++ b/src/IFoxCAD.Cad/IFoxCAD.Cad.csproj @@ -6,7 +6,7 @@ enable true true - 0.3.1 + 0.3.2 InspireFunction xsfhlzh;vicwjb 基于.NET的Cad二次开发类库 diff --git a/src/IFoxCAD.Cad/QuadTree/Graph.cs b/src/IFoxCAD.Cad/QuadTree/Graph.cs index 5372785..3e3b4ed 100644 --- a/src/IFoxCAD.Cad/QuadTree/Graph.cs +++ b/src/IFoxCAD.Cad/QuadTree/Graph.cs @@ -1,6 +1,8 @@  +using IFoxCAD.Basal; + using Exception = System.Exception; namespace IFoxCAD.Cad.FirstGraph { @@ -21,11 +23,12 @@ public class Graph : IGraph, IEnumerable private Dictionary> edges = new (); public int VerticesCount => vertices.Count; + private static int insertCount; public Graph() { //vertices = new Dictionary>(); - + insertCount = 0; } /// @@ -34,7 +37,7 @@ public Graph() /// public void AddVertex(Point3d pt) { - var vertex = new GraphVertex(pt, this); + var vertex = new GraphVertex(this, pt, insertCount++); if (vertices.ContainsKey(vertex)) { throw new Exception("顶点已经存在。"); @@ -42,7 +45,7 @@ public void AddVertex(Point3d pt) //vertex.Index = vertices.Count; vertices.Add(vertex, new HashSet()); edges.Add(vertex, new HashSet()); - + } /// @@ -51,7 +54,8 @@ public void AddVertex(Point3d pt) /// public void RemoveVertex(Point3d pt) { - var vertex = new GraphVertex(pt, this); + // 新建个临时的顶点,所以插入的顺序号为-1 + var vertex = new GraphVertex(this, pt, -1); if (!vertices.ContainsKey(vertex)) { throw new Exception("顶点不在此图中。"); @@ -86,13 +90,18 @@ public void RemoveVertex(Point3d pt) /// public void AddEdge(Curve3d value!!) { - // 函数有问题,有一个端点在图里时,另一个点应该新增个顶点, - var start = new GraphVertex(value.StartPoint, this); - var end = new GraphVertex(value.EndPoint,this); + // 新建两个临时的顶点, + IGraphVertex start = new GraphVertex(this, value.StartPoint, -1); + + IGraphVertex end = new GraphVertex(this, value.EndPoint,-1); - if (vertices.ContainsKey(start) && !vertices.ContainsKey(end)) // 如果曲线的起点在邻接表字典里,终点不在 + if (vertices.ContainsKey(start) && !vertices.ContainsKey(end)) { + // 如果曲线的起点在邻接表字典里,终点不在 + // 那么起点直接替换为字典内的顶点,而终点的需要更新 + start = vertices.GetKey(start)!; + end.Index = insertCount++; var edge = new GraphEdge(end, value); vertices[start].Add(end); // 邻接表加曲线的终点 edges[start].Add(edge); // 邻接边表加曲线 @@ -107,9 +116,12 @@ public void AddEdge(Curve3d value!!) edges[end].Add(new GraphEdge(start, value)); } - else if (vertices.ContainsKey(end) && !vertices.ContainsKey(start)) // 如果曲线的终点在邻接表字典里,起点不在 + else if (vertices.ContainsKey(end) && !vertices.ContainsKey(start)) { - + // 如果曲线的终点在邻接表字典里,起点不在 + // 那么终点直接替换为字典内的顶点,而起点的需要更新 + end = vertices.GetKey(end)!; + start.Index = insertCount++; var edge = new GraphEdge(start, value); vertices[end].Add(start); // 邻接表加曲线的起点 edges[end].Add(edge); // 邻接边表加曲线 @@ -123,8 +135,11 @@ public void AddEdge(Curve3d value!!) // 邻接边表加入终点 edges[start].Add(new GraphEdge(end, value)); } - else if (vertices.ContainsKey(start) && vertices.ContainsKey(end)) // 起点和终点同时在 + else if (vertices.ContainsKey(start) && vertices.ContainsKey(end)) { + // 起点和终点同时在, 则起点和终点都直接替换为字典内的顶点 + start = vertices.GetKey(start)!; + end = vertices.GetKey(end)!; var edge = new GraphEdge(end, value); vertices[start].Add(end); // 邻接表加曲线的终点 edges[start].Add(edge); // 邻接边表加曲线 @@ -135,6 +150,9 @@ public void AddEdge(Curve3d value!!) } else { + // 两个点都不在,则index都需要更新 + start.Index = insertCount++; + end.Index = insertCount++; // 邻接表字典添加起点 vertices.Add(start,new HashSet()); // 邻接表加入终点 @@ -162,8 +180,9 @@ public void AddEdge(Curve3d value!!) /// public void RemoveEdge(Curve3d curve!!) { - var start = new GraphVertex(curve.StartPoint, this); - var end = new GraphVertex(curve.EndPoint, this); + // 只是为了删除边,所以建立两个临时顶点 + var start = new GraphVertex(this, curve.StartPoint, -1); + var end = new GraphVertex(this, curve.EndPoint, -1); if (!vertices.ContainsKey(start) || !vertices.ContainsKey(end)) { @@ -341,19 +360,20 @@ public virtual string ToReadable() /// 邻接表图实现的顶点。 /// IEnumerable 枚举所有邻接点。 /// - public class GraphVertex : IGraphVertex, IEquatable, IComparable ,IComparable + public class GraphVertex : IGraphVertex, IEquatable, IComparable ,IComparable { public Point3d Data { get; private set; } public IGraph Graph { get; private set; } + public int Index { get; set; } //public int Index { get; set; } - public GraphVertex(Point3d value, IGraph graph) + public GraphVertex(IGraph graph, Point3d value, int index) { Data = value; Graph = graph; - //Index = -1; + Index = index; } - public bool Equals(GraphVertex other) + public bool Equals(IGraphVertex other) { return Data.IsEqualTo(other.Data, new Tolerance(1e-6,1e-6)); } @@ -361,10 +381,10 @@ public override bool Equals(Object obj) { if (obj is null) return false; - if (obj is not GraphVertex personObj) + if (obj is not IGraphVertex vertex) return false; else - return Equals(personObj); + return Equals(vertex); } public override int GetHashCode() { @@ -381,14 +401,13 @@ public int CompareTo(IGraphVertex other) } else { - var edges = this.Graph.GetAdjacencyList(this); - if (edges.Contains(other)) + if (this.Index < other.Index) { - return 1; + return -1; } else { - return -1; + return 1; } } } @@ -532,6 +551,7 @@ private void dfs(IGraph graph, List visited) var startNode = visited[0]; IGraphVertex nextNode; List sub; + var adjlist = graph.GetAdjacencyList(startNode).ToList(); for (int i = 0; i < adjlist.Count; i++) { diff --git a/src/IFoxCAD.Cad/QuadTree/IGraph.cs b/src/IFoxCAD.Cad/QuadTree/IGraph.cs index 45ff6b3..09ba822 100644 --- a/src/IFoxCAD.Cad/QuadTree/IGraph.cs +++ b/src/IFoxCAD.Cad/QuadTree/IGraph.cs @@ -68,7 +68,7 @@ public interface IGraphVertex /// 顶点的键 /// /// - //int Index { get; set; } + int Index { get; set; } /// /// 顶点的数据 -- Gitee From 5a8e2a48e30924ccb3f1cf643829ab4410289465 Mon Sep 17 00:00:00 2001 From: vicwjb <403009819@qq.com> Date: Wed, 13 Apr 2022 00:52:56 +0800 Subject: [PATCH 139/675] =?UTF-8?q?=E5=8F=91=E5=B8=830.3.3=E7=89=88?= =?UTF-8?q?=E6=9C=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/IFoxCAD.Basal/IFoxCAD.Basal.csproj | 4 ++-- src/IFoxCAD.Cad/IFoxCAD.Cad.csproj | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/IFoxCAD.Basal/IFoxCAD.Basal.csproj b/src/IFoxCAD.Basal/IFoxCAD.Basal.csproj index bee482a..607b7ea 100644 --- a/src/IFoxCAD.Basal/IFoxCAD.Basal.csproj +++ b/src/IFoxCAD.Basal/IFoxCAD.Basal.csproj @@ -4,7 +4,7 @@ net35;net40;net45 true enable - 0.3.2 + 0.3.3 InspireFunction xsfhlzh;vicwjb 基于.NET的二次开发基本类库 @@ -13,7 +13,7 @@ https://gitee.com/inspirefunction/ifoxcad.git git IFoxCAD;C#;NET;Common;Basal - 恢复LoopList类. + 增加字典和数组的扩展函数. true preview diff --git a/src/IFoxCAD.Cad/IFoxCAD.Cad.csproj b/src/IFoxCAD.Cad/IFoxCAD.Cad.csproj index e4ef9c4..77ebd26 100644 --- a/src/IFoxCAD.Cad/IFoxCAD.Cad.csproj +++ b/src/IFoxCAD.Cad/IFoxCAD.Cad.csproj @@ -6,7 +6,7 @@ enable true true - 0.3.2 + 0.3.3 InspireFunction xsfhlzh;vicwjb 基于.NET的Cad二次开发类库 @@ -15,7 +15,7 @@ https://gitee.com/inspirefunction/ifoxcad.git git IFoxCAD;CAD;AutoCad;C#;NET - 开启可空类型;优化事务构造函数. + 增加数据结构-图及DFS. true true preview -- Gitee From d2a766f5789261f218c9706b199f079a24282a9f Mon Sep 17 00:00:00 2001 From: liuqihong <540762622@qq.com> Date: Wed, 13 Apr 2022 22:11:45 +0800 Subject: [PATCH 140/675] =?UTF-8?q?=E4=BC=98=E5=8C=96?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/IFoxCAD.Cad/Runtime/AutoRegAssem.cs | 13 +++++-------- 1 file changed, 5 insertions(+), 8 deletions(-) diff --git a/src/IFoxCAD.Cad/Runtime/AutoRegAssem.cs b/src/IFoxCAD.Cad/Runtime/AutoRegAssem.cs index 99af880..a1d2a75 100644 --- a/src/IFoxCAD.Cad/Runtime/AutoRegAssem.cs +++ b/src/IFoxCAD.Cad/Runtime/AutoRegAssem.cs @@ -58,16 +58,14 @@ public static DirectoryInfo GetDirectory(Assembly assem) /// public AutoRegAssem() { - Assembly assem = Assembly.GetCallingAssembly(); + var assem = Assembly.GetCallingAssembly(); _info.Loader = assem.Location; _info.Fullname = assem.FullName; _info.Name = assem.GetName().Name; _info.LoadType = AssemLoadType.Startting; if (!SearchForReg()) - { RegApp(); - } } #region RegApp @@ -79,14 +77,13 @@ private static RegistryKey GetAcAppKey() #else string key = HostApplicationServices.Current.MachineRegistryProductRootKey; #endif - RegistryKey ackey = - Registry.CurrentUser.OpenSubKey(key, true); + var ackey = Registry.CurrentUser.OpenSubKey(key, true); return ackey.CreateSubKey("Applications"); } private bool SearchForReg() { - RegistryKey appkey = GetAcAppKey(); + var appkey = GetAcAppKey(); if (appkey.SubKeyCount == 0) return false; @@ -105,8 +102,8 @@ private bool SearchForReg() /// public void RegApp() { - RegistryKey appkey = GetAcAppKey(); - RegistryKey rk = appkey.CreateSubKey(_info.Name); + var appkey = GetAcAppKey(); + var rk = appkey.CreateSubKey(_info.Name); rk.SetValue("DESCRIPTION", _info.Fullname, RegistryValueKind.String); rk.SetValue("LOADCTRLS", _info.LoadType, RegistryValueKind.DWord); rk.SetValue("LOADER", _info.Loader, RegistryValueKind.String); -- Gitee From 7d1b194dececb9388470fe3d600408b6a69af3aa Mon Sep 17 00:00:00 2001 From: liuqihong <540762622@qq.com> Date: Thu, 14 Apr 2022 05:17:38 +0800 Subject: [PATCH 141/675] =?UTF-8?q?=E6=B7=BB=E5=8A=A0AOP?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/IFoxCAD.Cad/Runtime/AOP.cs | 103 ++++++++++ src/IFoxCAD.Cad/Runtime/IAutoGo.cs | 263 ++++++++++++++++++++++++ src/IFoxCAD.Cad/Runtime/MethodHelper.cs | 44 ++++ tests/Test/TestAOP.cs | 26 +++ 4 files changed, 436 insertions(+) create mode 100644 src/IFoxCAD.Cad/Runtime/AOP.cs create mode 100644 src/IFoxCAD.Cad/Runtime/IAutoGo.cs create mode 100644 src/IFoxCAD.Cad/Runtime/MethodHelper.cs create mode 100644 tests/Test/TestAOP.cs diff --git a/src/IFoxCAD.Cad/Runtime/AOP.cs b/src/IFoxCAD.Cad/Runtime/AOP.cs new file mode 100644 index 0000000..ab04e00 --- /dev/null +++ b/src/IFoxCAD.Cad/Runtime/AOP.cs @@ -0,0 +1,103 @@ +using IFoxCAD.Cad; +using HarmonyLib; + +/* + * 在所有的命令末尾注入清空事务栈函数 + */ +public class AOP +{ + /// + /// 遍历程序域下所有类型 + /// + /// + public static void AppDomainGetTypes(Action action) + { + var assemblies = AppDomain.CurrentDomain.GetAssemblies(); +#if !NET35 + //cad2021出现如下报错 + //System.NotSupportedException:动态程序集中不支持已调用的成员 + assemblies = assemblies.Where(p => !p.IsDynamic).ToArray(); +#endif + //主程序域 + for (int ii = 0; ii < assemblies.Length; ii++) + { + try + { + var assembly = assemblies[ii]; + //引用到test工程之后再调用 + //if (!assembly.GetName(true).Name.Contains(nameof(IFoxCAD))) + // continue; + + Type[]? types = null; + try + { + //获取类型集合,反射时候还依赖其他的dll就会这个错误 + //此通讯库要跳过,否则会报错. + if (Path.GetFileName(assembly.Location) == "AcInfoCenterConn.dll") + continue; + types = assembly.GetTypes(); + } + catch (ReflectionTypeLoadException) { continue; } + if (types is null) + continue; + for (int jj = 0; jj < types.Length; jj++) + { + var type = types[jj]; + if (type is not null) + action(type); + } + } + catch + { } + } + } + + public static void Run() + { + Dictionary cmdDic = new(); + AppDomainGetTypes(type => { + var mets = type.GetMethods();//获得它的成员函数 + for (int i = 0; i < mets.Length; i++) + { + var method = mets[i]; + //找到特性,特性下面的方法要是Public,否则就被编译器优化掉了. + var attr = method.GetCustomAttributes(true); + for (int j = 0; j < attr.Length; j++) + if (attr[j] is CommandMethodAttribute cmdAtt) + cmdDic.Add(cmdAtt.GlobalName, (cmdAtt, type, method)); + } + }); + + //运行的命令写在了Test.dll,当然不是ifox内了.... + if (cmdDic.Count == 0) + return; + + var harmony = new Harmony(nameof(IFoxCAD)); + var mPrefix = SymbolExtensions.GetMethodInfo(() => IFoxCmdAddFirst());//进入函数前 + var mPostfix = SymbolExtensions.GetMethodInfo(() => IFoxCmdAddLast());//进入函数后 + var mp1 = new HarmonyMethod(mPrefix); + var mp2 = new HarmonyMethod(mPostfix); + + foreach (var item in cmdDic) + { + //原函数执行(空间type,函数名) + var mOriginal = AccessTools.Method(item.Value.MetType, item.Value.MetInfo.Name); + //mOriginal.Invoke(); + //新函数执行:创造两个函数加入里面 + var newMet = harmony.Patch(mOriginal, mp1, mp2); + //newMet.Invoke(); + } + } + + public static void IFoxCmdAddFirst() + { + //此生命周期会在事务栈上面,被无限延长 + var _ = DBTrans.Top; + } + + public static void IFoxCmdAddLast() + { + var db = Application.DocumentManager.MdiActiveDocument.Database; + DBTrans.FinishDatabase(db); + } +} \ No newline at end of file diff --git a/src/IFoxCAD.Cad/Runtime/IAutoGo.cs b/src/IFoxCAD.Cad/Runtime/IAutoGo.cs new file mode 100644 index 0000000..91854cc --- /dev/null +++ b/src/IFoxCAD.Cad/Runtime/IAutoGo.cs @@ -0,0 +1,263 @@ +using System.Diagnostics; + +namespace IFoxCAD.Cad; + +[Flags] +public enum Sequence : byte +{ + First,// 最先 + Last, // 最后 +} + +public interface IAutoGo +{ + // 控制加载顺序 + Sequence SequenceId(); + // 关闭cad的时候会自动执行 + void Terminate(); + // 打开cad的时候会自动执行 + void Initialize(); +} + +public class IFoxInitialize : Attribute +{ + public bool IsInitialize { get; private set; } + public Sequence Sequence { get; private set; } + /// + /// 自己制作的一个特性,放在函数上面用来初始化或者结束回收 + /// + /// 优先级 + /// 用于初始化,用于结束回收 + public IFoxInitialize(Sequence sequence = Sequence.Last, bool initialize = true) + { + Sequence = sequence; + IsInitialize = initialize; + } +} + +//为了解决IExtensionApplication在一个dll内无法多次实现接口的关系 +//所以在这里反射加载所有的IAutoGo,以达到能分开写"启动运行"函数的目的 +public class RunClass +{ + public Sequence SequenceId { get; } + MethodInfo _methodInfo; + object? _instance; + + public RunClass(MethodInfo method, Sequence sequence) + { + _methodInfo = method; + SequenceId = sequence; + + var reftype = _methodInfo.ReflectedType; + if (reftype == null) return; + var fullName = reftype.FullName; //命名空间+类 + if (fullName == null) return; + var type = reftype.Assembly.GetType(fullName);//通过程序集反射创建类+ + if (type == null) return; + _instance = Activator.CreateInstance(type); + } + + /// + /// 运行方法 + /// + public void Run() + { + try + { + _methodInfo.Invoke(_instance); + } + catch (System.Exception e) + { + Debugger.Break(); + Debug.WriteLine("AutoClass.RunClass.Run出错" + e.Message); + } + } +} + +public class AutoClass : IExtensionApplication +{ + static List _InitializeList = new(); //储存方法用于初始化 + static List _TerminateList = new(); //储存方法用于结束释放 + const string _iAutoGo = "IAutoGo"; + + //打开cad的时候会自动执行 + public void Initialize() + { + try + { + GetAttributeFunctions(); + GetInterfaceFunctions(_InitializeList, "Initialize"); + //按照 SequenceId 排序_升序 + _InitializeList = _InitializeList.OrderBy(runClass => runClass.SequenceId).ToList(); + RunFunctions(_InitializeList); + } + catch (System.Exception e) + { + Debugger.Break(); + Debug.WriteLine("惊惊连盒,IExtensionApplication,AutoClass.Initialize出错::" + e.Message); + } + } + + //关闭cad的时候会自动执行 + public void Terminate() + { + try + { + GetInterfaceFunctions(_TerminateList, "Terminate"); + //按照 SequenceId 排序_降序 + _TerminateList = _TerminateList.OrderByDescending(runClass => runClass.SequenceId).ToList(); + RunFunctions(_TerminateList); + } + catch (System.Exception e) + { + Debugger.Break(); + Debug.WriteLine("惊惊连盒,IExtensionApplication,AutoClass.Terminate出错::" + e.Message); + } + } + + /// + /// 遍历程序域下所有类型 + /// + /// + public static void AppDomainGetTypes(Action action) + { + int error = 0; + try + { + var assemblies = AppDomain.CurrentDomain.GetAssemblies(); +#if !NET35 + //cad2021出现如下报错 + //System.NotSupportedException:动态程序集中不支持已调用的成员 + assemblies = assemblies.Where(p => !p.IsDynamic).ToArray(); +#endif + //主程序域 + for (int ii = 0; ii < assemblies.Length; ii++) + { + var assembly = assemblies[ii]; + Type[]? types = null; + try + { + //获取类型集合,反射时候还依赖其他的dll就会这个错误 + //此通讯库要跳过,否则会报错. + if (Path.GetFileName(assembly.Location) == "AcInfoCenterConn.dll") + continue; + types = assembly.GetTypes(); + } + catch (ReflectionTypeLoadException) { continue; } + if (types is null) + continue; + for (int jj = 0; jj < types.Length; jj++) + { + var type = types[jj]; + if (type is not null) + { + ++error; + action(type); + } + } + } + + } + catch (System.Exception e) + { + Debugger.Break(); + Debug.WriteLine($"出错:AppDomainGetTypes;计数{error};错误信息:{e.Message}"); + } + } + + /// + /// 收集接口下的函数 + /// + /// 储存要运行的方法 + /// + /// + void GetInterfaceFunctions(List runClassList, string methodName = "Initialize") + { + string JoinBoxSequenceId = nameof(Sequence) + "Id"; + AppDomainGetTypes(type => { + //获取接口集合 + var inters = type.GetInterfaces(); + for (int ii = 0; ii < inters.Length; ii++) + { + if (inters[ii].Name == _iAutoGo)//找到接口的函数 + { + Sequence? sequence = null; + MethodInfo? initialize = null; + + //获得它的成员函数 + var mets = type.GetMethods(); + for (int jj = 0; jj < mets.Length; jj++) + { + var method = mets[jj]; + if (method.IsAbstract) + continue; + if (method.Name == JoinBoxSequenceId) + { + var obj = method.Invoke(); + if (obj != null) + sequence = (Sequence)obj; + continue; + } + else if (method.Name == methodName) + { + initialize = method; + } + if (initialize is not null && sequence is not null) + break; + } + if (initialize is not null) + { + RunClass runc; + if (sequence is not null) + runc = new RunClass(initialize, sequence.Value); + else + runc = new RunClass(initialize, Sequence.Last); + runClassList.Add(runc); + } + break; + } + } + }); + } + + /// + /// 收集特性下的函数 + /// + void GetAttributeFunctions() + { + AppDomainGetTypes(type => { + var mets = type.GetMethods();//获得它的成员函数 + for (int ii = 0; ii < mets.Length; ii++) + { + var method = mets[ii]; + + //找到特性,特性下面的方法要是Public,否则就被编译器优化掉了. + var attr = method.GetCustomAttributes(true); + for (int jj = 0; jj < attr.Length; jj++) + { + if (attr[jj] is IFoxInitialize jjAtt) + { + var runc = new RunClass(method, jjAtt.Sequence); + if (jjAtt.IsInitialize) + _InitializeList.Add(runc); + else + _TerminateList.Add(runc); + break;//特性只会出现一次 + } + } + } + }); + } + + /// + /// 执行收集到的函数 + /// + void RunFunctions(List runClassList) + { + for (int i = runClassList.Count - 1; i >= 0; i--) + { + runClassList[i].Run(); + runClassList.RemoveAt(i); + } + } +} \ No newline at end of file diff --git a/src/IFoxCAD.Cad/Runtime/MethodHelper.cs b/src/IFoxCAD.Cad/Runtime/MethodHelper.cs new file mode 100644 index 0000000..848c5d5 --- /dev/null +++ b/src/IFoxCAD.Cad/Runtime/MethodHelper.cs @@ -0,0 +1,44 @@ +namespace IFoxCAD.Cad; + +public static class MethodInfoHelper +{ + /// + /// 执行函数 + /// + /// 函数 + /// 已经外部创建的对象,为空则此处创建 + public static object? Invoke(this MethodInfo methodInfo, object? instance = null) + { + if (methodInfo == null) + throw new ArgumentNullException(nameof(methodInfo)); + + object? result; + if (methodInfo.IsStatic) + { + //新函数指针进入此处 + //参数数量一定要匹配,为null则参数个数不同导致报错, + //参数为stirng[],则可以传入object[]代替,其他参数是否还可以实现默认构造? + var paramInfos = methodInfo.GetParameters(); + var args = new List { }; + for (int i = 0; i < paramInfos.Length; i++) + args.Add(null!); + result = methodInfo.Invoke(null, args.ToArray());//静态调用 + } + else + { + //原命令的函数指针进入此处 + if (instance == null) + { + var reftype = methodInfo.ReflectedType; + if (reftype == null) return null; + var fullName = reftype.FullName; //命名空间+类 + if (fullName == null) return null; + var type = reftype.Assembly.GetType(fullName);//通过程序集反射创建类+ + if (type == null) return null; + instance = Activator.CreateInstance(type); + } + result = methodInfo.Invoke(instance, null); //非静态,调用实例化方法 + } + return result; + } +} diff --git a/tests/Test/TestAOP.cs b/tests/Test/TestAOP.cs new file mode 100644 index 0000000..a70505b --- /dev/null +++ b/tests/Test/TestAOP.cs @@ -0,0 +1,26 @@ +/* + * 这里必须要实现一次这个接口,才能使用特性 + */ +public class AutoGoExtensionApplication : IExtensionApplication +{ + public void Initialize() + { + new AutoClass().Initialize(); + } + + public void Terminate() + { + } +} + +/* + * 在所有的命令末尾注入清空事务栈函数 + */ +public class AutoAOP +{ + [IFoxInitialize] + public void Initialize() + { + AOP.Run(); + } +} \ No newline at end of file -- Gitee From 3016b93053901b857154380fcfa36badcd107e7a Mon Sep 17 00:00:00 2001 From: liuqihong <540762622@qq.com> Date: Thu, 14 Apr 2022 05:17:53 +0800 Subject: [PATCH 142/675] =?UTF-8?q?=E5=A4=84=E7=90=86=E8=87=AA=E5=8A=A8?= =?UTF-8?q?=E6=89=A7=E8=A1=8C=E7=9B=B8=E5=85=B3=E5=87=BD=E6=95=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/IFoxCAD.Cad/IFoxCAD.Cad.csproj | 5 +++ src/IFoxCAD.Cad/Runtime/AutoRegAssem.cs | 4 ++- src/IFoxCAD.Cad/Runtime/DBTrans.cs | 41 ++++++++++++++++++++----- tests/Test/Test.cs | 36 +++++++++------------- tests/Test/Test.csproj | 4 ++- tests/Test/TestCurve.cs | 6 ++-- tests/Test/TestLoop.cs | 4 --- tests/Test/testid.cs | 33 ++------------------ tests/Test/testtr.cs | 2 -- 9 files changed, 62 insertions(+), 73 deletions(-) diff --git a/src/IFoxCAD.Cad/IFoxCAD.Cad.csproj b/src/IFoxCAD.Cad/IFoxCAD.Cad.csproj index 77ebd26..6e054a4 100644 --- a/src/IFoxCAD.Cad/IFoxCAD.Cad.csproj +++ b/src/IFoxCAD.Cad/IFoxCAD.Cad.csproj @@ -69,8 +69,13 @@ + + + + + diff --git a/src/IFoxCAD.Cad/Runtime/AutoRegAssem.cs b/src/IFoxCAD.Cad/Runtime/AutoRegAssem.cs index a1d2a75..b90cb23 100644 --- a/src/IFoxCAD.Cad/Runtime/AutoRegAssem.cs +++ b/src/IFoxCAD.Cad/Runtime/AutoRegAssem.cs @@ -25,7 +25,7 @@ public enum AssemLoadType /// /// 自动加载程序集的抽象类,继承自 IExtensionApplication 接口 /// -public abstract class AutoRegAssem : IExtensionApplication +public abstract class AutoRegAssem : IAutoGo { private AssemInfo _info = new(); @@ -125,5 +125,7 @@ public void RegApp() /// public abstract void Terminate(); + public abstract Sequence SequenceId(); + #endregion IExtensionApplication 成员 } diff --git a/src/IFoxCAD.Cad/Runtime/DBTrans.cs b/src/IFoxCAD.Cad/Runtime/DBTrans.cs index b3c6654..9e610e0 100644 --- a/src/IFoxCAD.Cad/Runtime/DBTrans.cs +++ b/src/IFoxCAD.Cad/Runtime/DBTrans.cs @@ -42,8 +42,10 @@ public static DBTrans Top * 当退出命令此事务释放,但是从来不释放Top, * 然后我新建了一个文档,再进行命令=>又进入Top,Top返回了前一个文档的事务 * 因此所以无法清理栈,所以Dispose不触发,导致无法刷新图元和Ctrl+Z出错 + * 所以用AOP方式修复 */ - +#if false + //不使用AOP方式修复,强迫用户先开启事务 if (dBTrans.Count == 0)//静态获取的时候就是0 throw new ArgumentNullException("事务栈没有任何事务,请在调用前创建:" + nameof(DBTrans)); @@ -54,14 +56,42 @@ public static DBTrans Top if (trans._commit) trans.Commit(); dBTrans.Pop(); - trans = dBTrans.Peek(); + if (dBTrans.Count != 0) + trans = dBTrans.Peek(); } if (dBTrans.Count == 0) trans = new DBTrans(); - +#else + //使用AOP方式修复 + DBTrans trans; + if (dBTrans.Count == 0) + trans = new DBTrans(); + else + trans = dBTrans.Peek(); +#endif return trans; } } + /// + /// 结束此数据库的所有事务_AOP修复方案 + /// + /// + public static void FinishDatabase(Database db) + { + if (dBTrans.Count == 0) + return; + + var trans = dBTrans.Peek(); + while (dBTrans.Count != 0 && trans.Database == db) //跨数据库 + { + if (trans._commit) + trans.Commit(); + dBTrans.Pop(); + if (dBTrans.Count != 0) + trans = dBTrans.Peek(); + } + } + /// /// 文档 /// @@ -416,14 +446,9 @@ public void Abort() public void Commit() { if (_commit) - { Transaction.Commit(); - } else - { Abort(); - } - } protected virtual void Dispose(bool disposing) diff --git a/tests/Test/Test.cs b/tests/Test/Test.cs index 8b9b7a0..e311942 100644 --- a/tests/Test/Test.cs +++ b/tests/Test/Test.cs @@ -1,7 +1,7 @@ #pragma warning disable CS0219 // 变量已被赋值,但从未使用过它的值 namespace test; -public class Test : AutoRegAssem +public class Test { [CommandMethod("dbtest")] public void Dbtest() @@ -82,16 +82,14 @@ public void Layertest() { using var tr = new DBTrans(); tr.LayerTable.Add("1"); - tr.LayerTable.Add("2", lt => - { + tr.LayerTable.Add("2", lt => { lt.Color = Color.FromColorIndex(ColorMethod.ByColor, 1); lt.LineWeight = LineWeight.LineWeight030; }); tr.LayerTable.Remove("3"); tr.LayerTable.Delete("0"); - tr.LayerTable.Change("4", lt => - { + tr.LayerTable.Change("4", lt => { lt.Color = Color.FromColorIndex(ColorMethod.ByColor, 2); }); } @@ -300,10 +298,9 @@ public void TestRec() var p24 = p4 - p2; - const double pi90 = Math.PI / 2; + const double pi90 = Math.PI / 2; - Tools.TestTimes(1000000,"对角线",() => - { + Tools.TestTimes(1000000, "对角线", () => { var result = false; if (Math.Abs(p13.Length - p24.Length) <= 1e8) { @@ -312,11 +309,10 @@ public void TestRec() }); - Tools.TestTimes(1000000,"三次点乘", () => - { + Tools.TestTimes(1000000, "三次点乘", () => { var result = false; - if (Math.Abs(p12.DotProduct(p23)) < 1e8 && + if (Math.Abs(p12.DotProduct(p23)) < 1e8 && Math.Abs(p23.DotProduct(p34)) < 1e8 && Math.Abs(p34.DotProduct(p41)) < 1e8) { @@ -325,8 +321,7 @@ public void TestRec() }); - Tools.TestTimes(1000000, "三次垂直", () => - { + Tools.TestTimes(1000000, "三次垂直", () => { var result = false; if (p12.IsParallelTo(p23) && p23.IsParallelTo(p34) && @@ -376,14 +371,12 @@ public void TestPt() public void TestBack() { using var tr = new DBTrans(@"C:\Users\vic\Desktop\test.dwg"); - tr.ModelSpace.GetEntities().ForEach(ent => - { + tr.ModelSpace.GetEntities().ForEach(ent => { ent.ForWrite(e => e.ColorIndex = 3); }); tr.Database.SaveAs(@"C:\Users\vic\Desktop\test.dwg", DwgVersion.Current); - tr.ModelSpace.GetEntities().ForEach(ent => - { + tr.ModelSpace.GetEntities().ForEach(ent => { ent.ForWrite(e => e.ColorIndex = 4); }); tr.Database.SaveAs(@"C:\Users\vic\Desktop\test.dwg", DwgVersion.Current); @@ -403,13 +396,14 @@ public Document Getdoc() return doc; } - public override void Initialize() + [IFoxInitialize] + public void Initialize() { //文档管理器将比此接口前创建,因此此句会执行 Application.DocumentManager.MdiActiveDocument.Editor.WriteMessage("\nload...."); } - - public override void Terminate() + [IFoxInitialize(Sequence.First, false)] + public void Terminate() { //文档管理器将比此接口前死亡,因此此句不会执行 Application.DocumentManager.MdiActiveDocument?.Editor.WriteMessage("\nunload...."); @@ -562,6 +556,4 @@ public void CombineBlocksIntoLibrary() } } - - #pragma warning restore CS0219 // 变量已被赋值,但从未使用过它的值 diff --git a/tests/Test/Test.csproj b/tests/Test/Test.csproj index 9ccbef9..d74bc94 100644 --- a/tests/Test/Test.csproj +++ b/tests/Test/Test.csproj @@ -1,10 +1,12 @@  + preview + + net45 true true - preview x64 diff --git a/tests/Test/TestCurve.cs b/tests/Test/TestCurve.cs index 169b639..6e12e5a 100644 --- a/tests/Test/TestCurve.cs +++ b/tests/Test/TestCurve.cs @@ -2,7 +2,6 @@ namespace Test { - public class TestGraph { [CommandMethod("testpointindict")] @@ -25,8 +24,7 @@ public void TestPointInDict() [CommandMethod("testgraph")] public void TestGraph1() { - using var tr = new DBTrans(); - var ents = Env.Editor.SSGet().Value?.GetEntities(); + var ents = Env.Editor.SSGet()?.Value?.GetEntities(); if (ents == null) return; @@ -52,8 +50,8 @@ public void TestGraph1() res.Add(tmp); } } - res.ForEach((i, t) => t.ForWrite(e => e.ColorIndex = i + 1)); + using var tr = new DBTrans(); tr.CurrentSpace.AddEntity(res); //var graph = new Graph(); diff --git a/tests/Test/TestLoop.cs b/tests/Test/TestLoop.cs index bfd1007..b6530ef 100644 --- a/tests/Test/TestLoop.cs +++ b/tests/Test/TestLoop.cs @@ -5,10 +5,6 @@ namespace Test { public class TestLoop { - - - - [CommandMethod("testloop")] public void Testloop() { diff --git a/tests/Test/testid.cs b/tests/Test/testid.cs index 911fd53..b31e845 100644 --- a/tests/Test/testid.cs +++ b/tests/Test/testid.cs @@ -1,32 +1,7 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; - -using Autodesk.AutoCAD.ApplicationServices; -using Autodesk.AutoCAD.Colors; -using Autodesk.AutoCAD.DatabaseServices; -using Autodesk.AutoCAD.EditorInput; -using Autodesk.AutoCAD.Geometry; -using Autodesk.AutoCAD.Runtime; - -using IFoxCAD.Cad; -namespace test +namespace test { - - public class Testid : AutoRegAssem + public class Testid { - public override void Initialize() - { - Env.Print("\nloading..."); - } - - public override void Terminate() - { - throw new NotImplementedException(); - } - [CommandMethod("testid")] public void TestId() { @@ -49,7 +24,6 @@ public void TestId() // } //} - } [CommandMethod("testmycommand")] @@ -97,9 +71,6 @@ public void TestTextStyleChange() tr.TextStyleTable.AddWithChange("宋体1", "simfang.ttf", height: 5); tr.TextStyleTable.AddWithChange("仿宋体", "宋体.ttf"); tr.TextStyleTable.AddWithChange("fsgb2312", "Romans", "gbcbig"); - - - } } } diff --git a/tests/Test/testtr.cs b/tests/Test/testtr.cs index 674e87a..f82fd9f 100644 --- a/tests/Test/testtr.cs +++ b/tests/Test/testtr.cs @@ -9,8 +9,6 @@ public void Testtr() using var tr = new DBTrans(filename); tr.ModelSpace.AddCircle(new Point3d(0, 0, 0), 20); tr.Database.SaveAs(filename,DwgVersion.Current); - - } } } -- Gitee From e7c7331c7a12a77dab5142ce3d578b3792029237 Mon Sep 17 00:00:00 2001 From: liuqihong <540762622@qq.com> Date: Wed, 13 Apr 2022 21:43:56 +0000 Subject: [PATCH 143/675] =?UTF-8?q?update=20src/IFoxCAD.Cad/Runtime/AOP.cs?= =?UTF-8?q?.=20=E5=88=A0=E9=99=A4=E5=92=8CIAutoGo=E9=87=8D=E5=A4=8D?= =?UTF-8?q?=E5=87=BD=E6=95=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/IFoxCAD.Cad/Runtime/AOP.cs | 48 +--------------------------------- 1 file changed, 1 insertion(+), 47 deletions(-) diff --git a/src/IFoxCAD.Cad/Runtime/AOP.cs b/src/IFoxCAD.Cad/Runtime/AOP.cs index ab04e00..d24a85a 100644 --- a/src/IFoxCAD.Cad/Runtime/AOP.cs +++ b/src/IFoxCAD.Cad/Runtime/AOP.cs @@ -1,4 +1,4 @@ -using IFoxCAD.Cad; +using IFoxCAD.Cad; using HarmonyLib; /* @@ -6,52 +6,6 @@ */ public class AOP { - /// - /// 遍历程序域下所有类型 - /// - /// - public static void AppDomainGetTypes(Action action) - { - var assemblies = AppDomain.CurrentDomain.GetAssemblies(); -#if !NET35 - //cad2021出现如下报错 - //System.NotSupportedException:动态程序集中不支持已调用的成员 - assemblies = assemblies.Where(p => !p.IsDynamic).ToArray(); -#endif - //主程序域 - for (int ii = 0; ii < assemblies.Length; ii++) - { - try - { - var assembly = assemblies[ii]; - //引用到test工程之后再调用 - //if (!assembly.GetName(true).Name.Contains(nameof(IFoxCAD))) - // continue; - - Type[]? types = null; - try - { - //获取类型集合,反射时候还依赖其他的dll就会这个错误 - //此通讯库要跳过,否则会报错. - if (Path.GetFileName(assembly.Location) == "AcInfoCenterConn.dll") - continue; - types = assembly.GetTypes(); - } - catch (ReflectionTypeLoadException) { continue; } - if (types is null) - continue; - for (int jj = 0; jj < types.Length; jj++) - { - var type = types[jj]; - if (type is not null) - action(type); - } - } - catch - { } - } - } - public static void Run() { Dictionary cmdDic = new(); -- Gitee From 9c7779a29315f8b324c0ef85aa5fd91f598570bd Mon Sep 17 00:00:00 2001 From: liuqihong <540762622@qq.com> Date: Wed, 13 Apr 2022 21:48:00 +0000 Subject: [PATCH 144/675] =?UTF-8?q?=E4=BF=AE=E6=94=B9=E4=BF=A1=E6=81=AF?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/IFoxCAD.Cad/Runtime/IAutoGo.cs | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/src/IFoxCAD.Cad/Runtime/IAutoGo.cs b/src/IFoxCAD.Cad/Runtime/IAutoGo.cs index 91854cc..b1bfd88 100644 --- a/src/IFoxCAD.Cad/Runtime/IAutoGo.cs +++ b/src/IFoxCAD.Cad/Runtime/IAutoGo.cs @@ -1,4 +1,4 @@ -using System.Diagnostics; +using System.Diagnostics; namespace IFoxCAD.Cad; @@ -94,7 +94,6 @@ public void Initialize() catch (System.Exception e) { Debugger.Break(); - Debug.WriteLine("惊惊连盒,IExtensionApplication,AutoClass.Initialize出错::" + e.Message); } } @@ -111,7 +110,6 @@ public void Terminate() catch (System.Exception e) { Debugger.Break(); - Debug.WriteLine("惊惊连盒,IExtensionApplication,AutoClass.Terminate出错::" + e.Message); } } @@ -126,9 +124,9 @@ public static void AppDomainGetTypes(Action action) { var assemblies = AppDomain.CurrentDomain.GetAssemblies(); #if !NET35 - //cad2021出现如下报错 - //System.NotSupportedException:动态程序集中不支持已调用的成员 - assemblies = assemblies.Where(p => !p.IsDynamic).ToArray(); + //cad2021出现如下报错 + //System.NotSupportedException:动态程序集中不支持已调用的成员 + assemblies = assemblies.Where(p => !p.IsDynamic).ToArray(); #endif //主程序域 for (int ii = 0; ii < assemblies.Length; ii++) -- Gitee From c0f88de4185132d104f9c265a9abe67d6411c5fc Mon Sep 17 00:00:00 2001 From: liuqihong <540762622@qq.com> Date: Wed, 13 Apr 2022 21:52:57 +0000 Subject: [PATCH 145/675] =?UTF-8?q?=E4=BF=AE=E6=94=B9=E4=BF=A1=E6=81=AF?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/IFoxCAD.Cad/Runtime/IAutoGo.cs | 1 - 1 file changed, 1 deletion(-) diff --git a/src/IFoxCAD.Cad/Runtime/IAutoGo.cs b/src/IFoxCAD.Cad/Runtime/IAutoGo.cs index b1bfd88..0c60790 100644 --- a/src/IFoxCAD.Cad/Runtime/IAutoGo.cs +++ b/src/IFoxCAD.Cad/Runtime/IAutoGo.cs @@ -69,7 +69,6 @@ public void Run() catch (System.Exception e) { Debugger.Break(); - Debug.WriteLine("AutoClass.RunClass.Run出错" + e.Message); } } } -- Gitee From 07b4fc3e2695e46dc451bc6f290d11baaceec5ef Mon Sep 17 00:00:00 2001 From: vicwjb <403009819@qq.com> Date: Thu, 14 Apr 2022 19:55:12 +0800 Subject: [PATCH 146/675] =?UTF-8?q?=E4=BF=AE=E5=A4=8D=E6=9B=B2=E7=BA=BF?= =?UTF-8?q?=E4=B8=8D=E8=83=BD=E6=AD=A3=E7=A1=AE=E7=94=9F=E6=88=90=E7=9A=84?= =?UTF-8?q?=E9=97=AE=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/IFoxCAD.Cad/QuadTree/Graph.cs | 335 ++++++++++++++++++------------ 1 file changed, 198 insertions(+), 137 deletions(-) diff --git a/src/IFoxCAD.Cad/QuadTree/Graph.cs b/src/IFoxCAD.Cad/QuadTree/Graph.cs index 3e3b4ed..c594207 100644 --- a/src/IFoxCAD.Cad/QuadTree/Graph.cs +++ b/src/IFoxCAD.Cad/QuadTree/Graph.cs @@ -1,6 +1,8 @@  +using Autodesk.AutoCAD.BoundaryRepresentation; + using IFoxCAD.Basal; using Exception = System.Exception; @@ -31,21 +33,38 @@ public Graph() insertCount = 0; } + + /// /// 向该图添加一个新顶点,但是无边。 /// 时间复杂度: O(E). E是边数 /// - public void AddVertex(Point3d pt) + public IGraphVertex AddVertex(Point3d pt) { - var vertex = new GraphVertex(this, pt, insertCount++); - if (vertices.ContainsKey(vertex)) + if (ContainKeyData(pt,out IGraphVertex? key)) { - throw new Exception("顶点已经存在。"); + return key!; } + var vertex = new GraphVertex(this, pt, insertCount++); //vertex.Index = vertices.Count; vertices.Add(vertex, new HashSet()); edges.Add(vertex, new HashSet()); - + return vertex; + + } + + private bool ContainKeyData(Point3d pt, out IGraphVertex? currentKey) + { + foreach (var key in vertices.Keys) + { + if (key.Data.Equals(pt)) + { + currentKey = key; + return true; + } + } + currentKey = null; + return false; } /// @@ -54,33 +73,32 @@ public void AddVertex(Point3d pt) /// public void RemoveVertex(Point3d pt) { - // 新建个临时的顶点,所以插入的顺序号为-1 - var vertex = new GraphVertex(this, pt, -1); - if (!vertices.ContainsKey(vertex)) - { - throw new Exception("顶点不在此图中。"); - } - // 删除邻接表里的vertex点,先删除后面的遍历可以少一轮 - vertices.Remove(vertex); - // 删除其他顶点的邻接表里的vertex点 - foreach (var item in vertices.Values) - { - item.Remove(vertex); - } - // 删除邻接边表里的vertex点,先删除后面的遍历可以少一轮 - edges.Remove(vertex); - // 删除其他顶点的邻接边表的指向vertex的边 - foreach (var item in edges.Values) + if (ContainKeyData(pt, out IGraphVertex? vertex)) { - foreach (var edge in item) + // 删除邻接表里的vertex点,先删除后面的遍历可以少一轮 + vertices.Remove(vertex!); + // 删除其他顶点的邻接表里的vertex点 + foreach (var item in vertices.Values) + { + item.Remove(vertex!); + } + + // 删除邻接边表里的vertex点,先删除后面的遍历可以少一轮 + edges.Remove(vertex!); + // 删除其他顶点的邻接边表的指向vertex的边 + foreach (var item in edges.Values) { - if (vertex.Equals(edge.TargetVertex)) + foreach (var edge in item) { - item.Remove(edge); + if (vertex!.Equals(edge.TargetVertex)) + { + item.Remove(edge); + } } } } + } @@ -88,89 +106,113 @@ public void RemoveVertex(Point3d pt) /// 向该图添加一个边。 /// 时间复杂度: O(1). /// - public void AddEdge(Curve3d value!!) + public void AddEdge(Curve3d curve!!) { - // 新建两个临时的顶点, - IGraphVertex start = new GraphVertex(this, value.StartPoint, -1); - IGraphVertex end = new GraphVertex(this, value.EndPoint,-1); + var start = AddVertex(curve.StartPoint); + var end = AddVertex(curve.EndPoint); + // 添加起点的邻接表和邻接边 + vertices[start].Add(end); + edges[start].Add(new GraphEdge(end, curve)); - if (vertices.ContainsKey(start) && !vertices.ContainsKey(end)) - { - // 如果曲线的起点在邻接表字典里,终点不在 - // 那么起点直接替换为字典内的顶点,而终点的需要更新 - start = vertices.GetKey(start)!; - end.Index = insertCount++; - var edge = new GraphEdge(end, value); - vertices[start].Add(end); // 邻接表加曲线的终点 - edges[start].Add(edge); // 邻接边表加曲线 - - // 邻接表字典添加终点 - vertices.Add(end, new HashSet()); - // 邻接表加入起点 - vertices[end].Add(start); - // 邻接边表字典添加终点 - edges.Add(end, new HashSet()); - // 邻接边表加入起点 - edges[end].Add(new GraphEdge(start, value)); + var curtmp = (Curve3d)curve.Clone(); + curtmp = curtmp!.GetReverseParameterCurve(); + // 添加终点的邻接表和邻接边 + vertices[end].Add(start); + edges[end].Add(new GraphEdge(start, curtmp)); - } - else if (vertices.ContainsKey(end) && !vertices.ContainsKey(start)) - { - // 如果曲线的终点在邻接表字典里,起点不在 - // 那么终点直接替换为字典内的顶点,而起点的需要更新 - end = vertices.GetKey(end)!; - start.Index = insertCount++; - var edge = new GraphEdge(start, value); - vertices[end].Add(start); // 邻接表加曲线的起点 - edges[end].Add(edge); // 邻接边表加曲线 - - // 邻接表字典添加起点 - vertices.Add(start, new HashSet()); - // 邻接表加入终点 - vertices[start].Add(end); - // 邻接边表字典添加起点 - edges.Add(start, new HashSet()); - // 邻接边表加入终点 - edges[start].Add(new GraphEdge(end, value)); - } - else if (vertices.ContainsKey(start) && vertices.ContainsKey(end)) - { - // 起点和终点同时在, 则起点和终点都直接替换为字典内的顶点 - start = vertices.GetKey(start)!; - end = vertices.GetKey(end)!; - var edge = new GraphEdge(end, value); - vertices[start].Add(end); // 邻接表加曲线的终点 - edges[start].Add(edge); // 邻接边表加曲线 - - var edge1 = new GraphEdge(start, value); - vertices[end].Add(start); // 邻接表加曲线的起点 - edges[end].Add(edge1); // 邻接边表加曲线 - } - else - { - // 两个点都不在,则index都需要更新 - start.Index = insertCount++; - end.Index = insertCount++; - // 邻接表字典添加起点 - vertices.Add(start,new HashSet()); - // 邻接表加入终点 - vertices[start].Add(end); - // 邻接边表字典添加起点 - edges.Add(start, new HashSet()); - // 邻接边表加入终点 - edges[start].Add(new GraphEdge(end, value)); - - // 邻接表字典添加终点 - vertices.Add(end,new HashSet()); - // 邻接表加入起点 - vertices[end].Add(start); - // 邻接边表字典添加终点 - edges.Add(end, new HashSet()); - // 邻接边表加入起点 - edges[end].Add(new GraphEdge(start,value)); - } + + + //// 新建两个临时的顶点, + //IGraphVertex start = new GraphVertex(this, value.StartPoint, -1); + + //IGraphVertex end = new GraphVertex(this, value.EndPoint,-1); + + + //if (vertices.ContainsKey(start) && !vertices.ContainsKey(end)) + //{ + // // 如果曲线的起点在邻接表字典里,终点不在 + // // 那么起点直接替换为字典内的顶点,而终点的需要更新 + // start = vertices.GetKey(start)!; + // end.Index = insertCount++; + + // // 需要确认每个点的邻接边都是起点到终点的模式 + // // 这里起点存在,那么曲线的起点和start是一点,则曲线不用处理 + // var edge = new GraphEdge(end, value); + // vertices[start].Add(end); // 邻接表加曲线的终点 + // edges[start].Add(edge); // 邻接边表加曲线 + + // // 邻接表字典添加终点 + // // 这里的顶点是终点,也就是说这个顶点的邻接点的终点应该是曲线的起点 + // vertices.Add(end, new HashSet()); + // // 邻接表加入起点 + // vertices[end].Add(start); + // // 邻接边表字典添加终点 + // edges.Add(end, new HashSet()); + // // 邻接边表加入起点,进行曲线反向 + // edges[end].Add(new GraphEdge(start, value.GetReverseParameterCurve())); + + //} + //else if (vertices.ContainsKey(end) && !vertices.ContainsKey(start)) + //{ + // // 如果曲线的终点在邻接表字典里,起点不在 + // // 那么终点直接替换为字典内的顶点,而起点的需要更新 + // end = vertices.GetKey(end)!; + // start.Index = insertCount++; + // // 加入的是终点,所以曲线反向 + // var edge = new GraphEdge(start, value.GetReverseParameterCurve()); + // vertices[end].Add(start); // 邻接表加曲线的起点 + // edges[end].Add(edge); // 邻接边表加曲线 + + // // 邻接表字典添加起点 + // vertices.Add(start, new HashSet()); + // // 邻接表加入终点 + // vertices[start].Add(end); + // // 邻接边表字典添加起点 + // edges.Add(start, new HashSet()); + // // 邻接边表加入终点 + // edges[start].Add(new GraphEdge(end, value)); + //} + //else if (vertices.ContainsKey(start) && vertices.ContainsKey(end)) + //{ + // // 起点和终点同时在, 则起点和终点都直接替换为字典内的顶点 + // start = vertices.GetKey(start)!; + // end = vertices.GetKey(end)!; + + // var edge = new GraphEdge(end, value); + // vertices[start].Add(end); // 邻接表加曲线的终点 + // edges[start].Add(edge); // 邻接边表加曲线 + + // // 终点作为顶点时,曲线反向 + // var edge1 = new GraphEdge(start, value.GetReverseParameterCurve()); + // vertices[end].Add(start); // 邻接表加曲线的起点 + // edges[end].Add(edge1); // 邻接边表加曲线 + //} + //else + //{ + // // 两个点都不在,则index都需要更新 + // start.Index = insertCount++; + // end.Index = insertCount++; + // // 邻接表字典添加起点 + // vertices.Add(start,new HashSet()); + // // 邻接表加入终点 + // vertices[start].Add(end); + // // 邻接边表字典添加起点 + // edges.Add(start, new HashSet()); + // // 邻接边表加入终点 + // edges[start].Add(new GraphEdge(end, value)); + + + // // 邻接表字典添加终点 + // vertices.Add(end,new HashSet()); + // // 邻接表加入起点 + // vertices[end].Add(start); + // // 邻接边表字典添加终点 + // edges.Add(end, new HashSet()); + // // 邻接边表加入起点,曲线反向 + // edges[end].Add(new GraphEdge(start,value.GetReverseParameterCurve())); + //} } @@ -180,41 +222,43 @@ public void AddEdge(Curve3d value!!) /// public void RemoveEdge(Curve3d curve!!) { - // 只是为了删除边,所以建立两个临时顶点 - var start = new GraphVertex(this, curve.StartPoint, -1); - var end = new GraphVertex(this, curve.EndPoint, -1); + RemoveVertex(curve.StartPoint); + RemoveVertex(curve.EndPoint); + //// 只是为了删除边,所以建立两个临时顶点 + //var start = new GraphVertex(this, curve.StartPoint, -1); + //var end = new GraphVertex(this, curve.EndPoint, -1); - if (!vertices.ContainsKey(start) || !vertices.ContainsKey(end)) - { - throw new Exception("源或目标顶点不在此图中。"); - } + //if (!vertices.ContainsKey(start) || !vertices.ContainsKey(end)) + //{ + // throw new Exception("源或目标顶点不在此图中。"); + //} - if (!edges[start].Contains(new GraphEdge(end,curve)) - || !edges[end].Contains(new GraphEdge(start,curve))) - { - throw new Exception("边不存在。"); - } - // 曲线的起点邻接表里删除终点 - vertices[start].Remove(end); - // 曲线的终点邻接表里删除起点 - vertices[end].Remove(start); - // 曲线的起点邻接边表里删除终点邻接边 - edges[start].Remove(new GraphEdge(end,curve)); - // 曲线的终点邻接边表里删除起点邻接边 - edges[end].Remove(new GraphEdge(start,curve)); - - // 如果 邻接表的长度为0,说明为孤立的顶点就删除 - if (vertices[start].Count == 0) - { - vertices.Remove(start); - edges.Remove(start); - } - if (vertices[end].Count == 0) - { - vertices.Remove(end); - edges.Remove(end); + //if (!edges[start].Contains(new GraphEdge(end,curve)) + // || !edges[end].Contains(new GraphEdge(start,curve))) + //{ + // throw new Exception("边不存在。"); + //} + //// 曲线的起点邻接表里删除终点 + //vertices[start].Remove(end); + //// 曲线的终点邻接表里删除起点 + //vertices[end].Remove(start); + //// 曲线的起点邻接边表里删除终点邻接边 + //edges[start].Remove(new GraphEdge(end,curve)); + //// 曲线的终点邻接边表里删除起点邻接边 + //edges[end].Remove(new GraphEdge(start,curve)); + + //// 如果 邻接表的长度为0,说明为孤立的顶点就删除 + //if (vertices[start].Count == 0) + //{ + // vertices.Remove(start); + // edges.Remove(start); + //} + //if (vertices[end].Count == 0) + //{ + // vertices.Remove(end); + // edges.Remove(end); - } + //} } @@ -274,9 +318,9 @@ public HashSet GetAdjacencyEdge(IGraphVertex vertex) return edges[vertex]; } - public LoopList GetCurves(List graphVertices) + public List GetCurves(List graphVertices) { - var curves = new LoopList(); + var curves = new List(); for (int i = 0; i < graphVertices.Count - 1; i++) { var cur = graphVertices[i]; @@ -284,14 +328,31 @@ public LoopList GetCurves(List graphVertices) var edge = GetEdge(cur, next); if (edge is not null) { + curves.Add(edge.TargetEdge); } } var lastedge = GetEdge(graphVertices[graphVertices.Count - 1], graphVertices[0]); if (lastedge is not null) { + curves.Add(lastedge.TargetEdge); } + + // 保证曲线顺序连接,代码有问题,主要原因在于结果顶点并不是顺序的 + //var res = new List(); + //res.Add(curves[0]); + //for (int i = 1; i < curves.Count; i++) + //{ + // if (res[i - 1].EndPoint.IsEqualTo(curves[i].EndPoint, new Tolerance(1e-6, 1e-6))) + // { + // res.Add(curves[i].GetReverseParameterCurve()); + // } + // res.Add(curves[i]); + //} + + + //return res; return curves; } -- Gitee From 0b465146357bde558da20cb584f883ddfad77152 Mon Sep 17 00:00:00 2001 From: liuqihong <540762622@qq.com> Date: Thu, 14 Apr 2022 22:34:31 +0800 Subject: [PATCH 147/675] =?UTF-8?q?=E4=BF=AE=E6=94=B9=E5=87=BD=E6=95=B0?= =?UTF-8?q?=E5=BC=95=E7=94=A8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/IFoxCAD.Cad/Runtime/AOP.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/IFoxCAD.Cad/Runtime/AOP.cs b/src/IFoxCAD.Cad/Runtime/AOP.cs index d24a85a..0cfc696 100644 --- a/src/IFoxCAD.Cad/Runtime/AOP.cs +++ b/src/IFoxCAD.Cad/Runtime/AOP.cs @@ -9,7 +9,7 @@ public class AOP public static void Run() { Dictionary cmdDic = new(); - AppDomainGetTypes(type => { + AutoClass.AppDomainGetTypes(type => { var mets = type.GetMethods();//获得它的成员函数 for (int i = 0; i < mets.Length; i++) { -- Gitee From 45fad15b7b592ad761b799b526652f4ead04f57c Mon Sep 17 00:00:00 2001 From: liuqihong <540762622@qq.com> Date: Thu, 14 Apr 2022 22:36:13 +0800 Subject: [PATCH 148/675] =?UTF-8?q?=E5=8A=A0=E5=85=A5=E9=A3=8E=E6=A0=BC?= =?UTF-8?q?=E7=BA=A6=E6=9D=9F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .editorconfig | 217 ++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 217 insertions(+) create mode 100644 .editorconfig diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 0000000..f0012cd --- /dev/null +++ b/.editorconfig @@ -0,0 +1,217 @@ +# 如果要从更高级别的目录继承 .editorconfig 设置,请删除以下行 +root = true + +# c# 文件 +[*.cs] + +#### Core EditorConfig 选项 #### + +# 缩进和间距 +indent_size = 4 +indent_style = space +tab_width = 4 + +# 新行首选项 +end_of_line = crlf +insert_final_newline = false + +#### .NET 编码约定 #### + +# 组织 Using +dotnet_separate_import_directive_groups = false +dotnet_sort_system_directives_first = false +file_header_template = unset + +# this. 和 Me. 首选项 +dotnet_style_qualification_for_event = false +dotnet_style_qualification_for_field = false +dotnet_style_qualification_for_method = false +dotnet_style_qualification_for_property = false + +# 语言关键字与 bcl 类型首选项 +dotnet_style_predefined_type_for_locals_parameters_members = true +dotnet_style_predefined_type_for_member_access = true + +# 括号首选项 +dotnet_style_parentheses_in_arithmetic_binary_operators = always_for_clarity +dotnet_style_parentheses_in_other_binary_operators = always_for_clarity +dotnet_style_parentheses_in_other_operators = never_if_unnecessary +dotnet_style_parentheses_in_relational_binary_operators = always_for_clarity + +# 修饰符首选项 +dotnet_style_require_accessibility_modifiers = for_non_interface_members + +# 表达式级首选项 +dotnet_style_coalesce_expression = true +dotnet_style_collection_initializer = true +dotnet_style_explicit_tuple_names = true +dotnet_style_namespace_match_folder = true +dotnet_style_null_propagation = true +dotnet_style_object_initializer = true +dotnet_style_operator_placement_when_wrapping = beginning_of_line +dotnet_style_prefer_auto_properties = true +dotnet_style_prefer_compound_assignment = true +dotnet_style_prefer_conditional_expression_over_assignment = true +dotnet_style_prefer_conditional_expression_over_return = true +dotnet_style_prefer_inferred_anonymous_type_member_names = true +dotnet_style_prefer_inferred_tuple_names = true +dotnet_style_prefer_is_null_check_over_reference_equality_method = true:error +dotnet_style_prefer_simplified_boolean_expressions = true +dotnet_style_prefer_simplified_interpolation = true + +# 字段首选项 +dotnet_style_readonly_field = true + +# 参数首选项 +dotnet_code_quality_unused_parameters = all + +# 禁止显示首选项 +dotnet_remove_unnecessary_suppression_exclusions = none + +# 新行首选项 +dotnet_style_allow_multiple_blank_lines_experimental = true +dotnet_style_allow_statement_immediately_after_block_experimental = true + +#### c# 编码约定 #### + +# var 首选项 +csharp_style_var_elsewhere = false +csharp_style_var_for_built_in_types = false +csharp_style_var_when_type_is_apparent = false + +# Expression-bodied 成员 +csharp_style_expression_bodied_accessors = true +csharp_style_expression_bodied_constructors = false +csharp_style_expression_bodied_indexers = true +csharp_style_expression_bodied_lambdas = true +csharp_style_expression_bodied_local_functions = false +csharp_style_expression_bodied_methods = false +csharp_style_expression_bodied_operators = false +csharp_style_expression_bodied_properties = true + +# 模式匹配首选项 +csharp_style_pattern_matching_over_as_with_null_check = true +csharp_style_pattern_matching_over_is_with_cast_check = true +csharp_style_prefer_not_pattern = true +csharp_style_prefer_pattern_matching = true +csharp_style_prefer_switch_expression = true + +# Null 检查首选项 +csharp_style_conditional_delegate_call = true + +# 修饰符首选项 +csharp_prefer_static_local_function = true +csharp_preferred_modifier_order = public,private,protected,internal,static,extern,new,virtual,abstract,sealed,override,readonly,unsafe,volatile,async + +# 代码块首选项 +csharp_prefer_braces = true +csharp_prefer_simple_using_statement = true + +# 表达式级首选项 +csharp_prefer_simple_default_expression = true +csharp_style_deconstructed_variable_declaration = true +csharp_style_implicit_object_creation_when_type_is_apparent = true +csharp_style_inlined_variable_declaration = true +csharp_style_pattern_local_over_anonymous_function = true +csharp_style_prefer_index_operator = true +csharp_style_prefer_range_operator = true +csharp_style_throw_expression = true +csharp_style_unused_value_assignment_preference = discard_variable +csharp_style_unused_value_expression_statement_preference = discard_variable + +# "using" 指令首选项 +csharp_using_directive_placement = outside_namespace + +# 新行首选项 +csharp_style_allow_blank_line_after_colon_in_constructor_initializer_experimental = true +csharp_style_allow_blank_lines_between_consecutive_braces_experimental = true +csharp_style_allow_embedded_statements_on_same_line_experimental = true + +#### C# 格式规则 #### + +# 新行首选项 +csharp_new_line_before_catch = true +csharp_new_line_before_else = true +csharp_new_line_before_finally = true +csharp_new_line_before_members_in_anonymous_types = true +csharp_new_line_before_members_in_object_initializers = true +csharp_new_line_before_open_brace = accessors,anonymous_methods,anonymous_types,control_blocks,methods,object_collection_array_initializers,properties,types +csharp_new_line_between_query_expression_clauses = true + +# 缩进首选项 +csharp_indent_block_contents = true +csharp_indent_braces = false +csharp_indent_case_contents = true +csharp_indent_case_contents_when_block = true +csharp_indent_labels = one_less_than_current +csharp_indent_switch_labels = true + +# 空格键首选项 +csharp_space_after_cast = false +csharp_space_after_colon_in_inheritance_clause = true +csharp_space_after_comma = true +csharp_space_after_dot = false +csharp_space_after_keywords_in_control_flow_statements = true +csharp_space_after_semicolon_in_for_statement = true +csharp_space_around_binary_operators = before_and_after +csharp_space_around_declaration_statements = false +csharp_space_before_colon_in_inheritance_clause = true +csharp_space_before_comma = false +csharp_space_before_dot = false +csharp_space_before_open_square_brackets = false +csharp_space_before_semicolon_in_for_statement = false +csharp_space_between_empty_square_brackets = false +csharp_space_between_method_call_empty_parameter_list_parentheses = false +csharp_space_between_method_call_name_and_opening_parenthesis = false +csharp_space_between_method_call_parameter_list_parentheses = false +csharp_space_between_method_declaration_empty_parameter_list_parentheses = false +csharp_space_between_method_declaration_name_and_open_parenthesis = false +csharp_space_between_method_declaration_parameter_list_parentheses = false +csharp_space_between_parentheses = false +csharp_space_between_square_brackets = false + +# 包装首选项 +csharp_preserve_single_line_blocks = true +csharp_preserve_single_line_statements = true + +#### 命名样式 #### + +# 命名规则 + +dotnet_naming_rule.interface_should_be_begins_with_i.severity = suggestion +dotnet_naming_rule.interface_should_be_begins_with_i.symbols = interface +dotnet_naming_rule.interface_should_be_begins_with_i.style = begins_with_i + +dotnet_naming_rule.types_should_be_pascal_case.severity = suggestion +dotnet_naming_rule.types_should_be_pascal_case.symbols = types +dotnet_naming_rule.types_should_be_pascal_case.style = pascal_case + +dotnet_naming_rule.non_field_members_should_be_pascal_case.severity = suggestion +dotnet_naming_rule.non_field_members_should_be_pascal_case.symbols = non_field_members +dotnet_naming_rule.non_field_members_should_be_pascal_case.style = pascal_case + +# 符号规范 + +dotnet_naming_symbols.interface.applicable_kinds = interface +dotnet_naming_symbols.interface.applicable_accessibilities = public, internal, private, protected, protected_internal, private_protected +dotnet_naming_symbols.interface.required_modifiers = + +dotnet_naming_symbols.types.applicable_kinds = class, struct, interface, enum +dotnet_naming_symbols.types.applicable_accessibilities = public, internal, private, protected, protected_internal, private_protected +dotnet_naming_symbols.types.required_modifiers = + +dotnet_naming_symbols.non_field_members.applicable_kinds = property, event, method +dotnet_naming_symbols.non_field_members.applicable_accessibilities = public, internal, private, protected, protected_internal, private_protected +dotnet_naming_symbols.non_field_members.required_modifiers = + +# 命名样式 + +dotnet_naming_style.pascal_case.required_prefix = +dotnet_naming_style.pascal_case.required_suffix = +dotnet_naming_style.pascal_case.word_separator = +dotnet_naming_style.pascal_case.capitalization = pascal_case + +dotnet_naming_style.begins_with_i.required_prefix = I +dotnet_naming_style.begins_with_i.required_suffix = +dotnet_naming_style.begins_with_i.word_separator = +dotnet_naming_style.begins_with_i.capitalization = pascal_case -- Gitee From 8117d814749062fd90a71e8b28b7f930f80ad76d Mon Sep 17 00:00:00 2001 From: vicwjb <403009819@qq.com> Date: Thu, 14 Apr 2022 22:37:09 +0800 Subject: [PATCH 149/675] =?UTF-8?q?=E6=95=B4=E7=90=86=E4=BB=A3=E7=A0=81?= =?UTF-8?q?=EF=BC=8C=E6=B8=85=E9=99=A4=E6=97=A0=E7=94=A8=E7=9A=84=E6=B3=A8?= =?UTF-8?q?=E9=87=8A?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/IFoxCAD.Cad/QuadTree/Graph.cs | 1050 ++++++++++++---------------- src/IFoxCAD.Cad/QuadTree/IGraph.cs | 61 +- src/IFoxCAD.Cad/Runtime/AOP.cs | 3 +- tests/Test/TestCurve.cs | 6 +- 4 files changed, 472 insertions(+), 648 deletions(-) diff --git a/src/IFoxCAD.Cad/QuadTree/Graph.cs b/src/IFoxCAD.Cad/QuadTree/Graph.cs index c594207..5fd7e18 100644 --- a/src/IFoxCAD.Cad/QuadTree/Graph.cs +++ b/src/IFoxCAD.Cad/QuadTree/Graph.cs @@ -1,710 +1,560 @@ - - - -using Autodesk.AutoCAD.BoundaryRepresentation; - -using IFoxCAD.Basal; - -using Exception = System.Exception; -namespace IFoxCAD.Cad.FirstGraph +using Exception = System.Exception; +namespace IFoxCAD.Cad; + +/// +/// 无权无向图实现 +/// IEnumerable 枚举所有顶点。 +/// +public sealed class Graph : IGraph, IEnumerable { + #region 字段及属性 /// - /// 无权无向图实现 - /// IEnumerable 枚举所有顶点。 + /// 存储所有节点的字典,key为顶点的类型,value为邻接表,类型是hashset,不可重复添加点 /// - public class Graph : IGraph, IEnumerable - { - /// - /// 存储所有节点的字典,key为顶点的类型,value为邻接表 - /// - /// - private Dictionary> vertices = new (); - /// - /// 邻接边表 - /// - private Dictionary> edges = new (); - public int VerticesCount => vertices.Count; - - private static int insertCount; - - public Graph() - { - //vertices = new Dictionary>(); - insertCount = 0; - } - - + /// + private readonly Dictionary> vertices = new(); + /// + /// 邻接边表,key为顶点的类型,value为邻接边表,类型是hashset,不可重复添加边 + /// + private readonly Dictionary> edges = new(); + public int VerticesCount => vertices.Count; + /// + /// 目前点增加点的顺序号,这个点号不随删点而减少的 + /// + private static int insertCount; + #endregion - /// - /// 向该图添加一个新顶点,但是无边。 - /// 时间复杂度: O(E). E是边数 - /// - public IGraphVertex AddVertex(Point3d pt) + #region 构造函数及私有函数 + public Graph() + { + insertCount = 0; // 每次新建对象就将顶点顺序号归零 + } + /// + /// 邻接边里是否存在点 + /// + /// 点 + /// 存在就返回找到的顶点,反之返回 + /// 存在点就返回 , 反之返回 + private bool ContainKeyData(Point3d pt, out IGraphVertex? currentKey) + { + foreach (var key in vertices.Keys) { - if (ContainKeyData(pt,out IGraphVertex? key)) + if (key.Data.Equals(pt)) { - return key!; + currentKey = key; + return true; } - var vertex = new GraphVertex(this, pt, insertCount++); - //vertex.Index = vertices.Count; - vertices.Add(vertex, new HashSet()); - edges.Add(vertex, new HashSet()); - return vertex; - } + currentKey = null; + return false; + } + #endregion - private bool ContainKeyData(Point3d pt, out IGraphVertex? currentKey) + #region 添加顶点及边 + /// + /// 向该图添加一个新顶点,但是无边。 + /// + /// 点 + /// 创建的顶点 + public IGraphVertex AddVertex(Point3d pt) + { + if (ContainKeyData(pt, out IGraphVertex? key)) { - foreach (var key in vertices.Keys) - { - if (key.Data.Equals(pt)) - { - currentKey = key; - return true; - } - } - currentKey = null; - return false; + return key!; } + var vertex = new GraphVertex(pt, insertCount++); + vertices.Add(vertex, new HashSet()); + edges.Add(vertex, new HashSet()); + return vertex; + } - /// - /// 从此图中删除现有顶点。 - /// 时间复杂度: O(V*E) 其中 V 是顶点数,E是边数。 - /// - public void RemoveVertex(Point3d pt) - { - - if (ContainKeyData(pt, out IGraphVertex? vertex)) - { - // 删除邻接表里的vertex点,先删除后面的遍历可以少一轮 - vertices.Remove(vertex!); - // 删除其他顶点的邻接表里的vertex点 - foreach (var item in vertices.Values) - { - item.Remove(vertex!); - } - - // 删除邻接边表里的vertex点,先删除后面的遍历可以少一轮 - edges.Remove(vertex!); - // 删除其他顶点的邻接边表的指向vertex的边 - foreach (var item in edges.Values) - { - foreach (var edge in item) - { - if (vertex!.Equals(edge.TargetVertex)) - { - item.Remove(edge); - } - } - } - } - - } - - - /// - /// 向该图添加一个边。 - /// 时间复杂度: O(1). - /// - public void AddEdge(Curve3d curve!!) - { + /// + /// 向该图添加一个边。 + /// + /// + public void AddEdge(Curve3d curve!!) + { + var start = AddVertex(curve.StartPoint); + var end = AddVertex(curve.EndPoint); - var start = AddVertex(curve.StartPoint); - var end = AddVertex(curve.EndPoint); - - // 添加起点的邻接表和邻接边 - vertices[start].Add(end); - edges[start].Add(new GraphEdge(end, curve)); - - var curtmp = (Curve3d)curve.Clone(); - curtmp = curtmp!.GetReverseParameterCurve(); - // 添加终点的邻接表和邻接边 - vertices[end].Add(start); - edges[end].Add(new GraphEdge(start, curtmp)); - - - - //// 新建两个临时的顶点, - //IGraphVertex start = new GraphVertex(this, value.StartPoint, -1); - - //IGraphVertex end = new GraphVertex(this, value.EndPoint,-1); - - - //if (vertices.ContainsKey(start) && !vertices.ContainsKey(end)) - //{ - // // 如果曲线的起点在邻接表字典里,终点不在 - // // 那么起点直接替换为字典内的顶点,而终点的需要更新 - // start = vertices.GetKey(start)!; - // end.Index = insertCount++; - - // // 需要确认每个点的邻接边都是起点到终点的模式 - // // 这里起点存在,那么曲线的起点和start是一点,则曲线不用处理 - // var edge = new GraphEdge(end, value); - // vertices[start].Add(end); // 邻接表加曲线的终点 - // edges[start].Add(edge); // 邻接边表加曲线 - - // // 邻接表字典添加终点 - // // 这里的顶点是终点,也就是说这个顶点的邻接点的终点应该是曲线的起点 - // vertices.Add(end, new HashSet()); - // // 邻接表加入起点 - // vertices[end].Add(start); - // // 邻接边表字典添加终点 - // edges.Add(end, new HashSet()); - // // 邻接边表加入起点,进行曲线反向 - // edges[end].Add(new GraphEdge(start, value.GetReverseParameterCurve())); - - //} - //else if (vertices.ContainsKey(end) && !vertices.ContainsKey(start)) - //{ - // // 如果曲线的终点在邻接表字典里,起点不在 - // // 那么终点直接替换为字典内的顶点,而起点的需要更新 - // end = vertices.GetKey(end)!; - // start.Index = insertCount++; - // // 加入的是终点,所以曲线反向 - // var edge = new GraphEdge(start, value.GetReverseParameterCurve()); - // vertices[end].Add(start); // 邻接表加曲线的起点 - // edges[end].Add(edge); // 邻接边表加曲线 - - // // 邻接表字典添加起点 - // vertices.Add(start, new HashSet()); - // // 邻接表加入终点 - // vertices[start].Add(end); - // // 邻接边表字典添加起点 - // edges.Add(start, new HashSet()); - // // 邻接边表加入终点 - // edges[start].Add(new GraphEdge(end, value)); - //} - //else if (vertices.ContainsKey(start) && vertices.ContainsKey(end)) - //{ - // // 起点和终点同时在, 则起点和终点都直接替换为字典内的顶点 - // start = vertices.GetKey(start)!; - // end = vertices.GetKey(end)!; - - // var edge = new GraphEdge(end, value); - // vertices[start].Add(end); // 邻接表加曲线的终点 - // edges[start].Add(edge); // 邻接边表加曲线 - - // // 终点作为顶点时,曲线反向 - // var edge1 = new GraphEdge(start, value.GetReverseParameterCurve()); - // vertices[end].Add(start); // 邻接表加曲线的起点 - // edges[end].Add(edge1); // 邻接边表加曲线 - //} - //else - //{ - // // 两个点都不在,则index都需要更新 - // start.Index = insertCount++; - // end.Index = insertCount++; - // // 邻接表字典添加起点 - // vertices.Add(start,new HashSet()); - // // 邻接表加入终点 - // vertices[start].Add(end); - // // 邻接边表字典添加起点 - // edges.Add(start, new HashSet()); - // // 邻接边表加入终点 - // edges[start].Add(new GraphEdge(end, value)); - - - // // 邻接表字典添加终点 - // vertices.Add(end,new HashSet()); - // // 邻接表加入起点 - // vertices[end].Add(start); - // // 邻接边表字典添加终点 - // edges.Add(end, new HashSet()); - // // 邻接边表加入起点,曲线反向 - // edges[end].Add(new GraphEdge(start,value.GetReverseParameterCurve())); - //} + // 添加起点的邻接表和邻接边 + vertices[start].Add(end); + edges[start].Add(new GraphEdge(end, curve)); - } + // 为了保证点顺序,每个点的邻接边必须按起点-终点,所以添加曲线终点时,将添加一个方向的曲线 + var curtmp = (Curve3d)curve.Clone(); + curtmp = curtmp!.GetReverseParameterCurve(); - /// - /// 从此图中删除一条边。 - /// 时间复杂度: O(2V*E) 其中 V 是顶点数,E是边数。 - /// - public void RemoveEdge(Curve3d curve!!) - { - RemoveVertex(curve.StartPoint); - RemoveVertex(curve.EndPoint); - //// 只是为了删除边,所以建立两个临时顶点 - //var start = new GraphVertex(this, curve.StartPoint, -1); - //var end = new GraphVertex(this, curve.EndPoint, -1); - - //if (!vertices.ContainsKey(start) || !vertices.ContainsKey(end)) - //{ - // throw new Exception("源或目标顶点不在此图中。"); - //} - - //if (!edges[start].Contains(new GraphEdge(end,curve)) - // || !edges[end].Contains(new GraphEdge(start,curve))) - //{ - // throw new Exception("边不存在。"); - //} - //// 曲线的起点邻接表里删除终点 - //vertices[start].Remove(end); - //// 曲线的终点邻接表里删除起点 - //vertices[end].Remove(start); - //// 曲线的起点邻接边表里删除终点邻接边 - //edges[start].Remove(new GraphEdge(end,curve)); - //// 曲线的终点邻接边表里删除起点邻接边 - //edges[end].Remove(new GraphEdge(start,curve)); - - //// 如果 邻接表的长度为0,说明为孤立的顶点就删除 - //if (vertices[start].Count == 0) - //{ - // vertices.Remove(start); - // edges.Remove(start); - //} - //if (vertices[end].Count == 0) - //{ - // vertices.Remove(end); - // edges.Remove(end); - - //} - - - } + // 添加终点的邻接表和邻接边 + vertices[end].Add(start); + edges[end].Add(new GraphEdge(start, curtmp)); + } + #endregion - /// - /// 我们在给定的来源和目的地之间是否有边? - /// 时间复杂度: O(E).E 是边 - /// - public bool HasEdge(IGraphVertex source, IGraphVertex dest) + #region 删除顶点及边 + /// + /// 从此图中删除现有顶点。 + /// + /// 点 + public void RemoveVertex(Point3d pt) + { + if (ContainKeyData(pt, out IGraphVertex? vertex)) { - if (!vertices.ContainsKey(source) || !vertices.ContainsKey(dest)) - { - throw new ArgumentException("源或目标不在此图中。"); - } - foreach (var item in edges[source]) + // 删除邻接表里的vertex点,先删除后面的遍历可以少一轮 + vertices.Remove(vertex!); + // 删除其他顶点的邻接表里的vertex点 + foreach (var item in vertices.Values) { - if (item.TargetVertex == dest) - { - return true; - } + item.Remove(vertex!); } - return false; - - } - - public IEdge? GetEdge(IGraphVertex source, IGraphVertex dest) - { - if (!vertices.ContainsKey(source) || !vertices.ContainsKey(dest)) + // 删除邻接边表里的vertex点,先删除后面的遍历可以少一轮 + edges.Remove(vertex!); + // 删除其他顶点的邻接边表的指向vertex的边 + foreach (var item in edges.Values) { - throw new ArgumentException("源或目标不在此图中。"); - } - foreach (var item in edges[source]) - { - if (item.TargetVertex == dest) + foreach (var edge in item) { - return item; + if (vertex!.Equals(edge.TargetVertex)) + { + item.Remove(edge); + } } } - return null; } + } + + /// + /// 从此图中删除一条边。 + /// + /// 曲线 + public void RemoveEdge(Curve3d curve!!) + { + RemoveVertex(curve.StartPoint); + RemoveVertex(curve.EndPoint); + } + #endregion - public bool ContainsVertex(IGraphVertex value) + #region 是否存在及获取顶点和边 + /// + /// 我们在给定的来源和目的地之间是否有边? + /// + /// 起点 + /// 终点 + /// 有边返回 ,反之返回 + public bool HasEdge(IGraphVertex source, IGraphVertex dest) + { + if (!vertices.ContainsKey(source) || !vertices.ContainsKey(dest)) { - return vertices.ContainsKey(value); + throw new ArgumentException("源或目标不在此图中。"); } - - - - public HashSet GetAdjacencyList(IGraphVertex vertex) + foreach (var item in edges[source]) { - return vertices[vertex]; + if (item.TargetVertex == dest) + { + return true; + } } + return false; + } - public HashSet GetAdjacencyEdge(IGraphVertex vertex) + /// + /// 获取边 + /// + /// 起点 + /// 终点 + /// + /// 传入的点不在图中时抛出参数异常 + public IEdge? GetEdge(IGraphVertex source, IGraphVertex dest) + { + if (!vertices.ContainsKey(source) || !vertices.ContainsKey(dest)) { - return edges[vertex]; + throw new ArgumentException("源或目标不在此图中。"); } - - public List GetCurves(List graphVertices) + foreach (var item in edges[source]) { - var curves = new List(); - for (int i = 0; i < graphVertices.Count - 1; i++) + if (item.TargetVertex == dest) { - var cur = graphVertices[i]; - var next = graphVertices[i + 1]; - var edge = GetEdge(cur, next); - if (edge is not null) - { - - curves.Add(edge.TargetEdge); - } - } - var lastedge = GetEdge(graphVertices[graphVertices.Count - 1], graphVertices[0]); - if (lastedge is not null) - { - - curves.Add(lastedge.TargetEdge); + return item; } - - // 保证曲线顺序连接,代码有问题,主要原因在于结果顶点并不是顺序的 - //var res = new List(); - //res.Add(curves[0]); - //for (int i = 1; i < curves.Count; i++) - //{ - // if (res[i - 1].EndPoint.IsEqualTo(curves[i].EndPoint, new Tolerance(1e-6, 1e-6))) - // { - // res.Add(curves[i].GetReverseParameterCurve()); - // } - // res.Add(curves[i]); - //} - - - //return res; - return curves; } + return null; + } + /// + /// 是否存在顶点,此函数目前未发现有啥用 + /// + /// 顶点 + /// 存在顶点返回 ,反之返回 + public bool ContainsVertex(IGraphVertex value) + { + return vertices.ContainsKey(value); + } + #endregion - /// - /// 克隆此图。目测是深克隆 - /// - public Graph Clone() - { - var newGraph = new Graph(); + #region 获取邻接表和曲线 + /// + /// 获取顶点的邻接表 + /// + /// 顶点 + /// 邻接表 + public HashSet GetAdjacencyList(IGraphVertex vertex) + { + return vertices[vertex]; + } + + /// + /// 获取顶点的邻接边表 + /// + /// 顶点 + /// 邻接边表 + public HashSet GetAdjacencyEdge(IGraphVertex vertex) + { + return edges[vertex]; + } - foreach (var vertex in edges.Values) + /// + /// 根据顶点表获取曲线集合 + /// + /// 顶点表 + /// 曲线表 + public List GetCurves(List graphVertices) + { + var curves = new List(); + for (int i = 0; i < graphVertices.Count - 1; i++) + { + var cur = graphVertices[i]; + var next = graphVertices[i + 1]; + var edge = GetEdge(cur, next); + if (edge is not null) { - foreach (var item in vertex) - { - newGraph.AddEdge(item.TargetEdge); - } + curves.Add(edge.TargetEdge); } - return newGraph; } - - public IEnumerator GetEnumerator() + var lastedge = GetEdge(graphVertices[graphVertices.Count - 1], graphVertices[0]); + if (lastedge is not null) { - return vertices.Select(x => x.Key).GetEnumerator(); + curves.Add(lastedge.TargetEdge); } + return curves; + } + #endregion - IEnumerator? IEnumerable.GetEnumerator() - { - return GetEnumerator() as IEnumerator; - } + #region 克隆及接口实现 + /// + /// 克隆此图。目测是深克隆 + /// + public Graph Clone() + { + var newGraph = new Graph(); - IGraph IGraph.Clone() + foreach (var vertex in edges.Values) { - return Clone(); + foreach (var item in vertex) + { + newGraph.AddEdge(item.TargetEdge); + } } + return newGraph; + } + IGraph IGraph.Clone() + { + return Clone(); + } + public IEnumerator GetEnumerator() + { + return VerticesAsEnumberable.GetEnumerator(); + } + IEnumerator? IEnumerable.GetEnumerator() + { + return GetEnumerator() as IEnumerator; + } - public IEnumerable VerticesAsEnumberable => - vertices.Select(x => x.Key); + public IEnumerable VerticesAsEnumberable => + vertices.Select(x => x.Key); + #endregion - public virtual string ToReadable() + /// + /// 输出点的邻接表的可读字符串 + /// + /// + public string ToReadable() + { + int i = 1; + string output = string.Empty; + foreach (var node in vertices) { - int i = 1; - string output = string.Empty; - - foreach (var node in vertices) - { - var adjacents = string.Empty; + var adjacents = string.Empty; - output = String.Format("{1}\r\n{0}-{2}: [",i, output, node.Key.Data.ToString()); + output = String.Format("{1}\r\n{0}-{2}: [",i, output, node.Key.Data.ToString()); - foreach (var adjacentNode in node.Value) - adjacents = String.Format("{0}{1},", adjacents, adjacentNode.Data.ToString()); + foreach (var adjacentNode in node.Value) + adjacents = String.Format("{0}{1},", adjacents, adjacentNode.Data.ToString()); - if (adjacents.Length > 0) - adjacents = adjacents.TrimEnd(new char[] { ',', ' ' }); - - output = String.Format("{0}{1}]", output, adjacents); - i++; - } + if (adjacents.Length > 0) + adjacents = adjacents.TrimEnd(new char[] { ',', ' ' }); - return output; + output = String.Format("{0}{1}]", output, adjacents); + i++; } + return output; } +} - /// - /// 邻接表图实现的顶点。 - /// IEnumerable 枚举所有邻接点。 - /// - public class GraphVertex : IGraphVertex, IEquatable, IComparable ,IComparable +/// +/// 邻接表图实现的顶点。 +/// IEnumerable 枚举所有邻接点。 +/// +public class GraphVertex : IGraphVertex, IEquatable, IComparable ,IComparable +{ + public Point3d Data { get; private set; } + public int Index { get; set; } + public GraphVertex(Point3d value, int index) { - public Point3d Data { get; private set; } - public IGraph Graph { get; private set; } - public int Index { get; set; } - - //public int Index { get; set; } - public GraphVertex(IGraph graph, Point3d value, int index) - { - Data = value; - Graph = graph; - Index = index; - } - public bool Equals(IGraphVertex other) - { - return Data.IsEqualTo(other.Data, new Tolerance(1e-6,1e-6)); - } - public override bool Equals(Object obj) - { - if (obj is null) - return false; - if (obj is not IGraphVertex vertex) - return false; - else - return Equals(vertex); - } - public override int GetHashCode() + Data = value; + Index = index; + } + public bool Equals(IGraphVertex other) + { + return Index == other.Index; + } + public override bool Equals(Object obj) + { + if (obj is null) + return false; + if (obj is not IGraphVertex vertex) + return false; + else + return Equals(vertex); + } + public override int GetHashCode() + { + return Index; + } + public int CompareTo(IGraphVertex other) + { + if (Equals(other)) { - // 原来的代码 不起作用,那么就转字符串算 - //return (Data.X., Data.Y, Data.Z).GetHashCode(); - - return (Data.X.ToString("n6"), Data.Y.ToString("n6"), Data.Z.ToString("n6")).GetHashCode(); + return 0; } - public int CompareTo(IGraphVertex other) + else { - if (Equals(other)) + if (this.Index < other.Index) { - return 0; + return -1; } else { - if (this.Index < other.Index) - { - return -1; - } - else - { - return 1; - } + return 1; } } + } - int IComparable.CompareTo(IGraphVertex other) - { - return CompareTo(other); - } + int IComparable.CompareTo(IGraphVertex other) + { + return CompareTo(other); + } - public int CompareTo(object obj) + public int CompareTo(object obj) + { + if (obj is null) { - if (obj is null) - { - return 1; - } - try - { - var other = (GraphVertex)obj; - return CompareTo(other); - } - catch (Exception) - { - - throw new ArgumentException("Object is not a IGraphVertex"); - } + return 1; } - - public static bool operator ==(GraphVertex person1, GraphVertex person2) + try { - if (((object)person1) == null || ((object)person2) == null) - return Object.Equals(person1, person2); - - return person1.Equals(person2); + var other = (GraphVertex)obj; + return CompareTo(other); } - public static bool operator !=(GraphVertex person1, GraphVertex person2) + catch (Exception) { - if (((object)person1) == null || ((object)person2) == null) - return !Object.Equals(person1, person2); - return !(person1.Equals(person2)); + throw new ArgumentException("Object is not a IGraphVertex"); } + } + public static bool operator ==(GraphVertex person1, GraphVertex person2) + { + if (((object)person1) == null || ((object)person2) == null) + return Object.Equals(person1, person2); + + return person1.Equals(person2); } + public static bool operator !=(GraphVertex person1, GraphVertex person2) + { + if (((object)person1) == null || ((object)person2) == null) + return !Object.Equals(person1, person2); - /// - /// 无向图中边的定义 - /// - /// 边的类型 - /// 权重的类型 - public class GraphEdge : IEdge, IEquatable + return !(person1.Equals(person2)); + } + +} + +/// +/// 无向图中边的定义 +/// +public class GraphEdge : IEdge, IEquatable +{ + // 这里的传入的两个参数分别为下一点和下一点之间的曲线 + public GraphEdge(IGraphVertex target, Curve3d edge) { - // 这里的传入的两个参数分别为下一点和下一点之间的曲线 - internal GraphEdge(IGraphVertex target, Curve3d edge) - { - this.TargetVertex = target; - this.TargetEdge = edge; - } + TargetVertex = target; + TargetEdge = edge; + } - public IGraphVertex TargetVertex { get; private set; } + public IGraphVertex TargetVertex { get; private set; } - public Curve3d TargetEdge { get; private set; } + public Curve3d TargetEdge { get; private set; } - public bool Equals(GraphEdge other) - { - if (other is null) - { - return false; - } - return TargetVertex == other.TargetVertex && TargetEdge == other.TargetEdge; - } - public override bool Equals(Object obj) + public bool Equals(GraphEdge other) + { + if (other is null) { - if (obj is null) - return false; - if (obj is not GraphEdge personObj) - return false; - else - return Equals(personObj); + return false; } + return TargetVertex == other.TargetVertex && TargetEdge == other.TargetEdge; + } + public override bool Equals(Object obj) + { + if (obj is null) + return false; + if (obj is not GraphEdge personObj) + return false; + else + return Equals(personObj); + } - public override int GetHashCode() - { - return (TargetVertex.GetHashCode(), TargetEdge.GetHashCode()).GetHashCode(); - } - public static bool operator ==(GraphEdge person1, GraphEdge person2) - { - if (((object)person1) == null || ((object)person2) == null) - return Object.Equals(person1, person2); + public override int GetHashCode() + { + return (TargetVertex.GetHashCode(), TargetEdge.GetHashCode()).GetHashCode(); + } + public static bool operator ==(GraphEdge person1, GraphEdge person2) + { + if (((object)person1) == null || ((object)person2) == null) + return Object.Equals(person1, person2); - return person1.Equals(person2); - } - public static bool operator !=(GraphEdge person1, GraphEdge person2) - { - if (((object)person1) == null || ((object)person2) == null) - return !Object.Equals(person1, person2); + return person1.Equals(person2); + } + public static bool operator !=(GraphEdge person1, GraphEdge person2) + { + if (((object)person1) == null || ((object)person2) == null) + return !Object.Equals(person1, person2); - return !(person1.Equals(person2)); - } + return !(person1.Equals(person2)); + } - } +} + +/// +/// 深度优先搜索。 +/// +public class DepthFirst +{ + // 存储所有的边 + public List> Curve3ds { get; } = new(); /// - /// 深度优先搜索。 + /// 找出所有的路径 /// - public class DepthFirst + /// 图 + public void FindAll(IGraph graph) { - // 存储所有的边 - public List> Curve3ds { get; } = new(); - private readonly List GraphVertices; - private readonly IGraph _graph; - public DepthFirst(IGraph graph) + foreach (var item in graph.VerticesAsEnumberable) { - _graph = graph; - GraphVertices = graph.VerticesAsEnumberable.ToList(); - //for (int i = 0; i < GraphVertices.Count; i++) - //{ - // GraphVertices[0].Index = i; - //} + Dfs(graph, new List { item }); } - /// - /// 如果项目存在,则返回 true。 - /// 这个函数需要改写,在ifoxcad里返回的应该是所有的环 - /// - public void FindAll() - { - //var visited = new List(); - foreach (var item in GraphVertices) - { + } - dfs(_graph, new List { item }); - - } - } + /// + /// 递归 DFS。 + /// + /// 图 + /// 已经遍历的路径 + private void Dfs(IGraph graph, List visited) + { + var startNode = visited[0]; + IGraphVertex nextNode; + List sub; - /// - /// 递归 DFS。 - /// - private void dfs(IGraph graph, List visited) + var adjlist = graph.GetAdjacencyList(startNode).ToList(); + for (int i = 0; i < adjlist.Count; i++) { - var startNode = visited[0]; - IGraphVertex nextNode; - List sub; - - var adjlist = graph.GetAdjacencyList(startNode).ToList(); - for (int i = 0; i < adjlist.Count; i++) + nextNode = adjlist[i]; + // 如果下一个点未遍历过 + if (!visited.Contains(nextNode)) { - - nextNode = adjlist[i]; - if (!visited.Contains(nextNode)) - { - sub = new List { nextNode }; - sub.AddRange(visited); - dfs(graph, sub); - } - else if (visited.Count > 2 && nextNode.Equals(visited[visited.Count - 1])) - { - var cur = RotateToSmallest(visited); - var inv = Invert(cur); - if (IsNew(cur) && IsNew(inv)) - { - Curve3ds.Add(cur); - } - } + // 将下一点加入路径集合,并进行下一次递归 + sub = new List { nextNode }; + sub.AddRange(visited); + Dfs(graph, sub); } - - // visited.Add(current); - - //// 改造这个搜索函数,当搜索闭合的时候,将闭合链存入结果列表 - // foreach (var vertex in graph.GetAdjacencyList(current)) - // { - // if (!visited.Contains(vertex)) - // { - - // dfs(graph, vertex, visited); - // } - // else if (visited.Count > 2 && vertex.Equals(visited[0])) - // { - // var curcycle = RotateToSmallest(visited); - // var invertcycle = Invert(curcycle); - // if (!Curve3ds.Contains(curcycle) && !Curve3ds.Contains(invertcycle)) - // { - // Curve3ds.Add(curcycle); - // } - // } - - // } - - } - - - private List RotateToSmallest(List lst) - { - var index = lst.IndexOf(lst.Min()); - return lst.Skip(index).Concat(lst.Take(index)).ToList(); - } - - private List Invert(List lst) - { - var tmp = lst.ToList(); - tmp.Reverse(); - return RotateToSmallest(tmp); - } - private bool Equals(List a, List b) - { - bool ret = (a[0] == b[0]) && (a.Count == b.Count); - - for (int i = 1; ret && (i < a.Count); i++) - if (!a[i].Equals( b[i])) - { - ret = false; - } - - return ret; - } - - - private bool IsNew(List path) - { - bool ret = true; - - foreach (var p in Curve3ds) - if (Equals(p, path)) + // 如果下一点遍历过,并且路径大于2,说明已经找到起点 + else if (visited.Count > 2 && nextNode.Equals(visited[visited.Count - 1])) + { + // 将重复的路径进行过滤,并把新的路径存入结果 + var cur = DepthFirst.RotateToSmallest(visited); + var inv = DepthFirst.Invert(cur); + if (IsNew(cur) && IsNew(inv)) { - ret = false; - break; + Curve3ds.Add(cur); } - - return ret; + } } - - - } - + /// + /// 将列表旋转到最小的值为列表起点 + /// + /// + /// + private static List RotateToSmallest(List lst) + { + var index = lst.IndexOf(lst.Min()); + return lst.Skip(index).Concat(lst.Take(index)).ToList(); + } + /// + /// 将列表反向,并旋转到起点为最小值 + /// + /// + /// + private static List Invert(List lst) + { + var tmp = lst.ToList(); + tmp.Reverse(); + return DepthFirst.RotateToSmallest(tmp); + } + /// + /// 比较两个列表是否相等 + /// + /// + /// + /// + private static bool Equals(List a, List b) + { + bool ret = (a[0] == b[0]) && (a.Count == b.Count); + for (int i = 1; ret && (i < a.Count); i++) + if (!a[i].Equals( b[i])) + { + ret = false; + } + return ret; + } - + /// + /// 是否已经是存在闭合环 + /// + /// + /// + private bool IsNew(List path) + { + bool ret = true; + foreach (var p in Curve3ds) + if (Equals(p, path)) + { + ret = false; + break; + } + return ret; + } } diff --git a/src/IFoxCAD.Cad/QuadTree/IGraph.cs b/src/IFoxCAD.Cad/QuadTree/IGraph.cs index 09ba822..c206736 100644 --- a/src/IFoxCAD.Cad/QuadTree/IGraph.cs +++ b/src/IFoxCAD.Cad/QuadTree/IGraph.cs @@ -5,36 +5,21 @@ namespace IFoxCAD.Cad /// /// 无向图 /// - /// 数据类型 public interface IGraph { - ///// - ///// 是否为有权图 - ///// - ///// - //bool IsWeightedGraph { get; } /// /// 顶点的数量 /// /// int VerticesCount { get; } - /// - /// 搜索的起始顶点 - /// - /// - //Point3d ReferenceVertex { get; } + /// /// 是否存在顶点 /// /// 顶点键 /// bool ContainsVertex(IGraphVertex key); - /// - /// 获取顶点 - /// - /// - /// - //IGraphVertex GetVertex(IGraphVertex key); + /// /// 顶点的迭代器 /// @@ -53,8 +38,24 @@ public interface IGraph /// /// IGraph Clone(); + /// + /// 获取边 + /// + /// + /// + /// IEdge? GetEdge(IGraphVertex source, IGraphVertex dest); + /// + /// 邻接表 + /// + /// + /// HashSet GetAdjacencyList(IGraphVertex vertex); + /// + /// 邻接边表 + /// + /// + /// HashSet GetAdjacencyEdge(IGraphVertex vertex); } @@ -74,30 +75,10 @@ public interface IGraphVertex /// 顶点的数据 /// Point3d Data { get; } - - IGraph Graph { get; } - /// - /// 顶点的邻接边表 - /// - /// - //List> Edges { get; } - /// - /// 获取顶点的邻接边 - /// - /// 目标顶点 - /// - //IEdge GetEdge(IGraphVertex targetVertex); - /// - /// 添加邻接边 - /// - /// 边类型 - //void AddEdge(IEdge edge); } /// /// 无向图边 /// - /// 顶点类型 - /// 边的类型 public interface IEdge { /// @@ -105,14 +86,8 @@ public interface IEdge /// Curve3d TargetEdge { get; } /// - /// 目标顶点的键 - /// - /// - //Point3d TargetVertexKey { get; } - /// /// 目标顶点 /// - /// IGraphVertex TargetVertex { get; } } diff --git a/src/IFoxCAD.Cad/Runtime/AOP.cs b/src/IFoxCAD.Cad/Runtime/AOP.cs index 0cfc696..3db76c6 100644 --- a/src/IFoxCAD.Cad/Runtime/AOP.cs +++ b/src/IFoxCAD.Cad/Runtime/AOP.cs @@ -1,6 +1,5 @@ -using IFoxCAD.Cad; using HarmonyLib; - +namespace IFoxCAD.Cad; /* * 在所有的命令末尾注入清空事务栈函数 */ diff --git a/tests/Test/TestCurve.cs b/tests/Test/TestCurve.cs index 6e12e5a..3a091b1 100644 --- a/tests/Test/TestCurve.cs +++ b/tests/Test/TestCurve.cs @@ -29,15 +29,15 @@ public void TestGraph1() return; // 新建图 - var graph = new IFoxCAD.Cad.FirstGraph.Graph(); + var graph = new IFoxCAD.Cad.Graph(); // 将曲线加入到图中 ents.ForEach(ent => graph.AddEdge(ent.GetGeCurve())); Env.Print(graph.ToReadable()); //新建 dfs - var dfs = new IFoxCAD.Cad.FirstGraph.DepthFirst(graph); + var dfs = new IFoxCAD.Cad.DepthFirst(); // 查询全部的 闭合环 - dfs.FindAll(); + dfs.FindAll(graph); // 遍历闭合环的列表,将每个闭合环转换为实体曲线 var res = new List(); foreach (var item in dfs.Curve3ds) -- Gitee From ae521b5eec2ed52d0065908bdfec8dd60aad2ab1 Mon Sep 17 00:00:00 2001 From: liuqihong <540762622@qq.com> Date: Thu, 14 Apr 2022 22:39:10 +0800 Subject: [PATCH 150/675] =?UTF-8?q?=E5=88=A0=E9=99=A4=E7=A9=BA=E5=8F=98?= =?UTF-8?q?=E9=87=8F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/IFoxCAD.Cad/Runtime/IAutoGo.cs | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/IFoxCAD.Cad/Runtime/IAutoGo.cs b/src/IFoxCAD.Cad/Runtime/IAutoGo.cs index 0c60790..1a0b271 100644 --- a/src/IFoxCAD.Cad/Runtime/IAutoGo.cs +++ b/src/IFoxCAD.Cad/Runtime/IAutoGo.cs @@ -66,7 +66,7 @@ public void Run() { _methodInfo.Invoke(_instance); } - catch (System.Exception e) + catch (System.Exception) { Debugger.Break(); } @@ -90,7 +90,7 @@ public void Initialize() _InitializeList = _InitializeList.OrderBy(runClass => runClass.SequenceId).ToList(); RunFunctions(_InitializeList); } - catch (System.Exception e) + catch (System.Exception) { Debugger.Break(); } @@ -106,7 +106,7 @@ public void Terminate() _TerminateList = _TerminateList.OrderByDescending(runClass => runClass.SequenceId).ToList(); RunFunctions(_TerminateList); } - catch (System.Exception e) + catch (System.Exception) { Debugger.Break(); } @@ -155,10 +155,10 @@ public static void AppDomainGetTypes(Action action) } } - catch (System.Exception e) + catch (System.Exception) { Debugger.Break(); - Debug.WriteLine($"出错:AppDomainGetTypes;计数{error};错误信息:{e.Message}"); + //Debug.WriteLine($"出错:AppDomainGetTypes;计数{error};错误信息:{e.Message}"); } } -- Gitee From 98d92129b5733bc25cf34c7829de98087956407b Mon Sep 17 00:00:00 2001 From: liuqihong <540762622@qq.com> Date: Thu, 14 Apr 2022 22:54:42 +0800 Subject: [PATCH 151/675] =?UTF-8?q?=E4=BC=98=E5=8C=96=E4=BB=A3=E7=A0=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/IFoxCAD.Cad/QuadTree/Graph.cs | 69 ++++++++++++------------------- 1 file changed, 27 insertions(+), 42 deletions(-) diff --git a/src/IFoxCAD.Cad/QuadTree/Graph.cs b/src/IFoxCAD.Cad/QuadTree/Graph.cs index 5fd7e18..716eabb 100644 --- a/src/IFoxCAD.Cad/QuadTree/Graph.cs +++ b/src/IFoxCAD.Cad/QuadTree/Graph.cs @@ -83,7 +83,7 @@ public void AddEdge(Curve3d curve!!) // 为了保证点顺序,每个点的邻接边必须按起点-终点,所以添加曲线终点时,将添加一个方向的曲线 var curtmp = (Curve3d)curve.Clone(); - curtmp = curtmp!.GetReverseParameterCurve(); + curtmp = curtmp!.GetReverseParameterCurve(); // 添加终点的邻接表和邻接边 vertices[end].Add(start); @@ -286,7 +286,7 @@ public string ToReadable() { var adjacents = string.Empty; - output = String.Format("{1}\r\n{0}-{2}: [",i, output, node.Key.Data.ToString()); + output = String.Format("{1}\r\n{0}-{2}: [", i, output, node.Key.Data.ToString()); foreach (var adjacentNode in node.Value) adjacents = String.Format("{0}{1},", adjacents, adjacentNode.Data.ToString()); @@ -305,7 +305,7 @@ public string ToReadable() /// 邻接表图实现的顶点。 /// IEnumerable 枚举所有邻接点。 /// -public class GraphVertex : IGraphVertex, IEquatable, IComparable ,IComparable +public class GraphVertex : IGraphVertex, IEquatable, IComparable, IComparable { public Point3d Data { get; private set; } public int Index { get; set; } @@ -318,7 +318,7 @@ public bool Equals(IGraphVertex other) { return Index == other.Index; } - public override bool Equals(Object obj) + public override bool Equals(object obj) { if (obj is null) return false; @@ -334,20 +334,12 @@ public override int GetHashCode() public int CompareTo(IGraphVertex other) { if (Equals(other)) - { return 0; - } + + if (Index < other.Index) + return -1; else - { - if (this.Index < other.Index) - { - return -1; - } - else - { - return 1; - } - } + return 1; } int IComparable.CompareTo(IGraphVertex other) @@ -358,9 +350,8 @@ int IComparable.CompareTo(IGraphVertex other) public int CompareTo(object obj) { if (obj is null) - { return 1; - } + try { var other = (GraphVertex)obj; @@ -368,26 +359,24 @@ public int CompareTo(object obj) } catch (Exception) { - throw new ArgumentException("Object is not a IGraphVertex"); } } public static bool operator ==(GraphVertex person1, GraphVertex person2) { - if (((object)person1) == null || ((object)person2) == null) - return Object.Equals(person1, person2); + if (person1 is null || person2 is null) + return Equals(person1, person2); return person1.Equals(person2); } public static bool operator !=(GraphVertex person1, GraphVertex person2) { - if (((object)person1) == null || ((object)person2) == null) - return !Object.Equals(person1, person2); + if (person1 is null || person2 is null) + return !Equals(person1, person2); - return !(person1.Equals(person2)); + return !person1.Equals(person2); } - } /// @@ -409,12 +398,11 @@ public GraphEdge(IGraphVertex target, Curve3d edge) public bool Equals(GraphEdge other) { if (other is null) - { return false; - } - return TargetVertex == other.TargetVertex && TargetEdge == other.TargetEdge; + return TargetVertex == other.TargetVertex && + TargetEdge == other.TargetEdge; } - public override bool Equals(Object obj) + public override bool Equals(object obj) { if (obj is null) return false; @@ -430,20 +418,18 @@ public override int GetHashCode() } public static bool operator ==(GraphEdge person1, GraphEdge person2) { - if (((object)person1) == null || ((object)person2) == null) - return Object.Equals(person1, person2); + if (person1 is null || person2 is null) + return Equals(person1, person2); return person1.Equals(person2); } public static bool operator !=(GraphEdge person1, GraphEdge person2) { - if (((object)person1) == null || ((object)person2) == null) - return !Object.Equals(person1, person2); + if (person1 is null || person2 is null) + return !Equals(person1, person2); - return !(person1.Equals(person2)); + return !person1.Equals(person2); } - - } /// @@ -482,10 +468,10 @@ private void Dfs(IGraph graph, List visited) { nextNode = adjlist[i]; // 如果下一个点未遍历过 - if (!visited.Contains(nextNode)) + if (!visited.Contains(nextNode)) { // 将下一点加入路径集合,并进行下一次递归 - sub = new List { nextNode }; + sub = new List { nextNode }; sub.AddRange(visited); Dfs(graph, sub); } @@ -522,7 +508,7 @@ private static List Invert(List lst) { var tmp = lst.ToList(); tmp.Reverse(); - return DepthFirst.RotateToSmallest(tmp); + return RotateToSmallest(tmp); } /// /// 比较两个列表是否相等 @@ -534,10 +520,9 @@ private static bool Equals(List a, List b) { bool ret = (a[0] == b[0]) && (a.Count == b.Count); for (int i = 1; ret && (i < a.Count); i++) - if (!a[i].Equals( b[i])) - { + if (!a[i].Equals(b[i])) ret = false; - } + return ret; } -- Gitee From f1ef8d90eb5120a70564d1e0837f57a4a9ddaf92 Mon Sep 17 00:00:00 2001 From: liuqihong <540762622@qq.com> Date: Thu, 14 Apr 2022 23:02:46 +0800 Subject: [PATCH 152/675] =?UTF-8?q?=E7=BA=A6=E6=9D=9F=E5=91=BD=E5=90=8D?= =?UTF-8?q?=E7=A9=BA=E9=97=B4=E6=B3=A8=E5=85=A5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/IFoxCAD.Cad/Runtime/AOP.cs | 13 ++++++++----- tests/Test/TestAOP.cs | 2 +- 2 files changed, 9 insertions(+), 6 deletions(-) diff --git a/src/IFoxCAD.Cad/Runtime/AOP.cs b/src/IFoxCAD.Cad/Runtime/AOP.cs index 3db76c6..8dcb869 100644 --- a/src/IFoxCAD.Cad/Runtime/AOP.cs +++ b/src/IFoxCAD.Cad/Runtime/AOP.cs @@ -1,14 +1,17 @@ -using HarmonyLib; namespace IFoxCAD.Cad; -/* - * 在所有的命令末尾注入清空事务栈函数 - */ +using HarmonyLib; + public class AOP { - public static void Run() + /// + /// 在此命名空间下的命令末尾注入清空事务栈函数 + /// + public static void Run(string nameSpace) { Dictionary cmdDic = new(); AutoClass.AppDomainGetTypes(type => { + if (type.Namespace != nameSpace) + return; var mets = type.GetMethods();//获得它的成员函数 for (int i = 0; i < mets.Length; i++) { diff --git a/tests/Test/TestAOP.cs b/tests/Test/TestAOP.cs index a70505b..46b3678 100644 --- a/tests/Test/TestAOP.cs +++ b/tests/Test/TestAOP.cs @@ -21,6 +21,6 @@ public class AutoAOP [IFoxInitialize] public void Initialize() { - AOP.Run(); + AOP.Run(nameof(Test)); } } \ No newline at end of file -- Gitee From 27dcf9bb67a293e4e2b825847f907949800018c5 Mon Sep 17 00:00:00 2001 From: liuqihong <540762622@qq.com> Date: Thu, 14 Apr 2022 23:33:00 +0800 Subject: [PATCH 153/675] =?UTF-8?q?=E4=BF=AE=E6=94=B9=E5=91=BD=E5=90=8D?= =?UTF-8?q?=E7=A9=BA=E9=97=B4/=E5=9B=A0=E4=B8=BAAOP=E8=A6=81=E8=BF=87?= =?UTF-8?q?=E6=BB=A4=E5=91=BD=E5=90=8D=E7=A9=BA=E9=97=B4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- tests/Test/Test.cs | 2 +- tests/Test/TestJig.cs | 2 +- tests/Test/testConvexHull.cs | 2 +- tests/Test/testblock.cs | 2 +- tests/Test/testeditor.cs | 2 +- tests/Test/testenv.cs | 2 +- tests/Test/testid.cs | 2 +- tests/Test/testselectfilter.cs | 2 +- 8 files changed, 8 insertions(+), 8 deletions(-) diff --git a/tests/Test/Test.cs b/tests/Test/Test.cs index e311942..ba649eb 100644 --- a/tests/Test/Test.cs +++ b/tests/Test/Test.cs @@ -1,5 +1,5 @@ #pragma warning disable CS0219 // 变量已被赋值,但从未使用过它的值 -namespace test; +namespace Test; public class Test { diff --git a/tests/Test/TestJig.cs b/tests/Test/TestJig.cs index 81e1566..dc07bf5 100644 --- a/tests/Test/TestJig.cs +++ b/tests/Test/TestJig.cs @@ -1,6 +1,6 @@  -namespace test; +namespace Test; using Autodesk.AutoCAD.ApplicationServices; diff --git a/tests/Test/testConvexHull.cs b/tests/Test/testConvexHull.cs index ee792c6..259b8be 100644 --- a/tests/Test/testConvexHull.cs +++ b/tests/Test/testConvexHull.cs @@ -10,7 +10,7 @@ using Autodesk.AutoCAD.DatabaseServices; using Autodesk.AutoCAD.Colors; -namespace test +namespace Test { public class testConvexHull { diff --git a/tests/Test/testblock.cs b/tests/Test/testblock.cs index d402f1f..ff9ef13 100644 --- a/tests/Test/testblock.cs +++ b/tests/Test/testblock.cs @@ -1,4 +1,4 @@ -namespace test; +namespace Test; public class TestBlock { //块定义 diff --git a/tests/Test/testeditor.cs b/tests/Test/testeditor.cs index ea0bb52..9c71a9a 100644 --- a/tests/Test/testeditor.cs +++ b/tests/Test/testeditor.cs @@ -1,4 +1,4 @@ -namespace test; +namespace Test; public class testeditor { diff --git a/tests/Test/testenv.cs b/tests/Test/testenv.cs index c4531e5..5de650f 100644 --- a/tests/Test/testenv.cs +++ b/tests/Test/testenv.cs @@ -7,7 +7,7 @@ using Autodesk.AutoCAD.ApplicationServices; using Autodesk.AutoCAD.DatabaseServices; -namespace test +namespace Test { public class testenv { diff --git a/tests/Test/testid.cs b/tests/Test/testid.cs index b31e845..73ff962 100644 --- a/tests/Test/testid.cs +++ b/tests/Test/testid.cs @@ -1,4 +1,4 @@ -namespace test +namespace Test { public class Testid { diff --git a/tests/Test/testselectfilter.cs b/tests/Test/testselectfilter.cs index 1224a2f..b6c179b 100644 --- a/tests/Test/testselectfilter.cs +++ b/tests/Test/testselectfilter.cs @@ -10,7 +10,7 @@ using IFoxCAD.Cad; using Autodesk.AutoCAD.EditorInput; -namespace test +namespace Test { public class testselectfilter { -- Gitee From 96abd0f109522d312104d3683066f7c22d488740 Mon Sep 17 00:00:00 2001 From: vicwjb <403009819@qq.com> Date: Fri, 15 Apr 2022 23:29:46 +0800 Subject: [PATCH 154/675] =?UTF-8?q?=E5=B0=86=E5=9B=BE=E5=8F=8ADFS=E6=95=B4?= =?UTF-8?q?=E7=90=86=E4=B8=BAGetAllCycle=E5=87=BD=E6=95=B0=EF=BC=8C?= =?UTF-8?q?=E5=90=8C=E6=97=B6=E5=B0=86=E5=85=B6=E8=AE=BE=E4=B8=BAinternal?= =?UTF-8?q?=E8=AE=BF=E9=97=AE=E7=BA=A7=E5=88=AB=EF=BC=8C=E5=9B=A0=E4=B8=BA?= =?UTF-8?q?=E5=9B=BE=E5=8F=8ADFS=E4=BB=A3=E7=A0=81=E4=B8=8D=E8=83=BD?= =?UTF-8?q?=E9=80=9A=E7=94=A8=EF=BC=8C=E9=98=B2=E6=AD=A2=E7=94=A8=E6=88=B7?= =?UTF-8?q?=E8=AF=AF=E7=94=A8=E3=80=82?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/IFoxCAD.Cad/ExtensionMethod/CurveEx.cs | 34 +++++ src/IFoxCAD.Cad/IFoxCAD.Cad.csproj | 3 + src/IFoxCAD.Cad/QuadTree/Graph.cs | 10 +- src/IFoxCAD.Cad/QuadTree/IGraph.cs | 159 ++++++++++----------- tests/Test/TestCurve.cs | 46 +++--- 5 files changed, 143 insertions(+), 109 deletions(-) diff --git a/src/IFoxCAD.Cad/ExtensionMethod/CurveEx.cs b/src/IFoxCAD.Cad/ExtensionMethod/CurveEx.cs index 841ca11..d574c1b 100644 --- a/src/IFoxCAD.Cad/ExtensionMethod/CurveEx.cs +++ b/src/IFoxCAD.Cad/ExtensionMethod/CurveEx.cs @@ -75,7 +75,41 @@ public static IEnumerable GetSplitCurves(this Curve curve, IEnumerable e.ToCurve()!); } + /// + /// 获取曲线集所围成的封闭区域的曲线集 + /// + /// 曲线集合 + /// 所有的闭合环的曲线集合 + public static IEnumerable GetAllCycle(this IEnumerable curves) + { + // 新建图 + var graph = new Graph(); + foreach (var curve in curves) + { +#if NET35 + graph.AddEdge(curve.ToCurve3d()!); +#else + graph.AddEdge(curve.GetGeCurve()); +#endif + } + //新建 dfs + var dfs = new DepthFirst(); + // 查询全部的 闭合环 + dfs.FindAll(graph); + // 遍历闭合环的列表,将每个闭合环转换为实体曲线 + var res = new List(); + foreach (var item in dfs.Curve3ds) + { + var curve = graph.GetCurves(item).ToArray(); + var comcur = new CompositeCurve3d(curve).ToCurve(); + if (comcur is not null) + { + res.Add(comcur); + } + } + return res; + } /// /// 曲线打断 /// diff --git a/src/IFoxCAD.Cad/IFoxCAD.Cad.csproj b/src/IFoxCAD.Cad/IFoxCAD.Cad.csproj index 6e054a4..7f0e4b3 100644 --- a/src/IFoxCAD.Cad/IFoxCAD.Cad.csproj +++ b/src/IFoxCAD.Cad/IFoxCAD.Cad.csproj @@ -37,12 +37,14 @@ runtime + runtime + @@ -71,6 +73,7 @@ + diff --git a/src/IFoxCAD.Cad/QuadTree/Graph.cs b/src/IFoxCAD.Cad/QuadTree/Graph.cs index 716eabb..e419f19 100644 --- a/src/IFoxCAD.Cad/QuadTree/Graph.cs +++ b/src/IFoxCAD.Cad/QuadTree/Graph.cs @@ -5,7 +5,7 @@ namespace IFoxCAD.Cad; /// 无权无向图实现 /// IEnumerable 枚举所有顶点。 /// -public sealed class Graph : IGraph, IEnumerable +internal sealed class Graph : IGraph, IEnumerable { #region 字段及属性 /// @@ -305,7 +305,7 @@ public string ToReadable() /// 邻接表图实现的顶点。 /// IEnumerable 枚举所有邻接点。 /// -public class GraphVertex : IGraphVertex, IEquatable, IComparable, IComparable +internal sealed class GraphVertex : IGraphVertex, IEquatable, IComparable ,IComparable { public Point3d Data { get; private set; } public int Index { get; set; } @@ -382,7 +382,7 @@ public int CompareTo(object obj) /// /// 无向图中边的定义 /// -public class GraphEdge : IEdge, IEquatable +internal sealed class GraphEdge : IEdge, IEquatable { // 这里的传入的两个参数分别为下一点和下一点之间的曲线 public GraphEdge(IGraphVertex target, Curve3d edge) @@ -435,7 +435,7 @@ public override int GetHashCode() /// /// 深度优先搜索。 /// -public class DepthFirst +internal sealed class DepthFirst { // 存储所有的边 public List> Curve3ds { get; } = new(); @@ -476,7 +476,7 @@ private void Dfs(IGraph graph, List visited) Dfs(graph, sub); } // 如果下一点遍历过,并且路径大于2,说明已经找到起点 - else if (visited.Count > 2 && nextNode.Equals(visited[visited.Count - 1])) + else if (visited.Count > 2 && nextNode.Equals(visited[^1])) { // 将重复的路径进行过滤,并把新的路径存入结果 var cur = DepthFirst.RotateToSmallest(visited); diff --git a/src/IFoxCAD.Cad/QuadTree/IGraph.cs b/src/IFoxCAD.Cad/QuadTree/IGraph.cs index c206736..ca832cc 100644 --- a/src/IFoxCAD.Cad/QuadTree/IGraph.cs +++ b/src/IFoxCAD.Cad/QuadTree/IGraph.cs @@ -1,96 +1,93 @@ -using IFoxCAD.Cad; +namespace IFoxCAD.Cad; -namespace IFoxCAD.Cad +/// +/// 无向图 +/// +internal interface IGraph { /// - /// 无向图 + /// 顶点的数量 /// - public interface IGraph - { - /// - /// 顶点的数量 - /// - /// - int VerticesCount { get; } - - /// - /// 是否存在顶点 - /// - /// 顶点键 - /// - bool ContainsVertex(IGraphVertex key); + /// + int VerticesCount { get; } + + /// + /// 是否存在顶点 + /// + /// 顶点键 + /// + bool ContainsVertex(IGraphVertex key); - /// - /// 顶点的迭代器 - /// - /// - IEnumerable VerticesAsEnumberable { get; } + /// + /// 顶点的迭代器 + /// + /// + IEnumerable VerticesAsEnumberable { get; } - /// - /// 是否有边 - /// - /// 源顶点 - /// 目的顶点 - /// - bool HasEdge(IGraphVertex source, IGraphVertex destination); - /// - /// 图克隆函数 - /// - /// - IGraph Clone(); - /// - /// 获取边 - /// - /// - /// - /// - IEdge? GetEdge(IGraphVertex source, IGraphVertex dest); - /// - /// 邻接表 - /// - /// - /// - HashSet GetAdjacencyList(IGraphVertex vertex); - /// - /// 邻接边表 - /// - /// - /// - HashSet GetAdjacencyEdge(IGraphVertex vertex); - } + /// + /// 是否有边 + /// + /// 源顶点 + /// 目的顶点 + /// + bool HasEdge(IGraphVertex source, IGraphVertex destination); + /// + /// 图克隆函数 + /// + /// + IGraph Clone(); + /// + /// 获取边 + /// + /// + /// + /// + IEdge? GetEdge(IGraphVertex source, IGraphVertex dest); + /// + /// 邻接表 + /// + /// + /// + HashSet GetAdjacencyList(IGraphVertex vertex); + /// + /// 邻接边表 + /// + /// + /// + HashSet GetAdjacencyEdge(IGraphVertex vertex); +} +/// +/// 无向图顶点 +/// +/// 顶点数据类型 +internal interface IGraphVertex +{ /// - /// 无向图顶点 + /// 顶点的键 /// - /// 顶点数据类型 - public interface IGraphVertex - { - /// - /// 顶点的键 - /// - /// - int Index { get; set; } + /// + int Index { get; set; } - /// - /// 顶点的数据 - /// - Point3d Data { get; } - } /// - /// 无向图边 + /// 顶点的数据 /// - public interface IEdge - { - /// - /// 边 - /// - Curve3d TargetEdge { get; } - /// - /// 目标顶点 - /// - IGraphVertex TargetVertex { get; } - } - + Point3d Data { get; } +} +/// +/// 无向图边 +/// +internal interface IEdge +{ + /// + /// 边 + /// + Curve3d TargetEdge { get; } + /// + /// 目标顶点 + /// + IGraphVertex TargetVertex { get; } } + diff --git a/tests/Test/TestCurve.cs b/tests/Test/TestCurve.cs index 3a091b1..522209b 100644 --- a/tests/Test/TestCurve.cs +++ b/tests/Test/TestCurve.cs @@ -27,29 +27,29 @@ public void TestGraph1() var ents = Env.Editor.SSGet()?.Value?.GetEntities(); if (ents == null) return; - - // 新建图 - var graph = new IFoxCAD.Cad.Graph(); - // 将曲线加入到图中 - ents.ForEach(ent => graph.AddEdge(ent.GetGeCurve())); - - Env.Print(graph.ToReadable()); - //新建 dfs - var dfs = new IFoxCAD.Cad.DepthFirst(); - // 查询全部的 闭合环 - dfs.FindAll(graph); - // 遍历闭合环的列表,将每个闭合环转换为实体曲线 - var res = new List(); - foreach (var item in dfs.Curve3ds) - { - var curves = graph.GetCurves(item).ToArray(); - var comcur = new CompositeCurve3d(curves); - var tmp = comcur.ToCurve(); - if (tmp is not null) - { - res.Add(tmp); - } - } + var res = ents.GetAllCycle(); + //// 新建图 + //var graph = new IFoxCAD.Cad.Graph(); + //// 将曲线加入到图中 + //ents.ForEach(ent => graph.AddEdge(ent.GetGeCurve())); + + //Env.Print(graph.ToReadable()); + ////新建 dfs + //var dfs = new IFoxCAD.Cad.DepthFirst(); + //// 查询全部的 闭合环 + //dfs.FindAll(graph); + //// 遍历闭合环的列表,将每个闭合环转换为实体曲线 + //var res = new List(); + //foreach (var item in dfs.Curve3ds) + //{ + // var curves = graph.GetCurves(item).ToArray(); + // var comcur = new CompositeCurve3d(curves); + // var tmp = comcur.ToCurve(); + // if (tmp is not null) + // { + // res.Add(tmp); + // } + //} res.ForEach((i, t) => t.ForWrite(e => e.ColorIndex = i + 1)); using var tr = new DBTrans(); tr.CurrentSpace.AddEntity(res); -- Gitee From 3920d9d053936c7047e640ee80308ce9657fdf5e Mon Sep 17 00:00:00 2001 From: vicwjb <403009819@qq.com> Date: Sat, 16 Apr 2022 20:20:41 +0800 Subject: [PATCH 155/675] =?UTF-8?q?=E5=8F=910.3.3.1=E7=9A=84nuget=E5=8C=85?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/IFoxCAD.Cad/IFoxCAD.Cad.csproj | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/IFoxCAD.Cad/IFoxCAD.Cad.csproj b/src/IFoxCAD.Cad/IFoxCAD.Cad.csproj index 7f0e4b3..408f463 100644 --- a/src/IFoxCAD.Cad/IFoxCAD.Cad.csproj +++ b/src/IFoxCAD.Cad/IFoxCAD.Cad.csproj @@ -6,7 +6,7 @@ enable true true - 0.3.3 + 0.3.3.1 InspireFunction xsfhlzh;vicwjb 基于.NET的Cad二次开发类库 -- Gitee From 46c45568c1591ad8a55e9d606dedaeff183b6f7c Mon Sep 17 00:00:00 2001 From: vicwjb <403009819@qq.com> Date: Sun, 17 Apr 2022 20:57:15 +0800 Subject: [PATCH 156/675] =?UTF-8?q?=E5=88=A0=E9=99=A4=E6=97=A0=E7=94=A8?= =?UTF-8?q?=E4=BB=A3=E7=A0=81=EF=BC=8C=E9=87=8D=E5=91=BD=E5=90=8D=E4=BB=A3?= =?UTF-8?q?=E7=A0=81=E6=96=87=E4=BB=B6=E5=A4=B9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../{QuadTree => Algorithms}/Graph.cs | 0 .../{QuadTree => Algorithms}/IGraph.cs | 0 .../Class1.cs => Algorithms/QuadTree.cs} | 0 .../{QuadTree => Algorithms}/Rect.cs | 0 src/IFoxCAD.Cad/ExtensionMethod/CurveEx.cs | 2 +- src/IFoxCAD.Cad/QuadTree/othergraph.cs | 323 ------------------ 6 files changed, 1 insertion(+), 324 deletions(-) rename src/IFoxCAD.Cad/{QuadTree => Algorithms}/Graph.cs (100%) rename src/IFoxCAD.Cad/{QuadTree => Algorithms}/IGraph.cs (100%) rename src/IFoxCAD.Cad/{QuadTree/Class1.cs => Algorithms/QuadTree.cs} (100%) rename src/IFoxCAD.Cad/{QuadTree => Algorithms}/Rect.cs (100%) delete mode 100644 src/IFoxCAD.Cad/QuadTree/othergraph.cs diff --git a/src/IFoxCAD.Cad/QuadTree/Graph.cs b/src/IFoxCAD.Cad/Algorithms/Graph.cs similarity index 100% rename from src/IFoxCAD.Cad/QuadTree/Graph.cs rename to src/IFoxCAD.Cad/Algorithms/Graph.cs diff --git a/src/IFoxCAD.Cad/QuadTree/IGraph.cs b/src/IFoxCAD.Cad/Algorithms/IGraph.cs similarity index 100% rename from src/IFoxCAD.Cad/QuadTree/IGraph.cs rename to src/IFoxCAD.Cad/Algorithms/IGraph.cs diff --git a/src/IFoxCAD.Cad/QuadTree/Class1.cs b/src/IFoxCAD.Cad/Algorithms/QuadTree.cs similarity index 100% rename from src/IFoxCAD.Cad/QuadTree/Class1.cs rename to src/IFoxCAD.Cad/Algorithms/QuadTree.cs diff --git a/src/IFoxCAD.Cad/QuadTree/Rect.cs b/src/IFoxCAD.Cad/Algorithms/Rect.cs similarity index 100% rename from src/IFoxCAD.Cad/QuadTree/Rect.cs rename to src/IFoxCAD.Cad/Algorithms/Rect.cs diff --git a/src/IFoxCAD.Cad/ExtensionMethod/CurveEx.cs b/src/IFoxCAD.Cad/ExtensionMethod/CurveEx.cs index d574c1b..5dd7d7a 100644 --- a/src/IFoxCAD.Cad/ExtensionMethod/CurveEx.cs +++ b/src/IFoxCAD.Cad/ExtensionMethod/CurveEx.cs @@ -49,6 +49,7 @@ public static IEnumerable GetSplitCurves(this Curve curve, IEnumerable /// 曲线集 /// 闭合的曲线集 + [Obsolete("此函数将被废弃,请使用 GetAllCycle 代替!")] public static IEnumerable? Topo(List curves) { //闭合的曲线集合 @@ -184,7 +185,6 @@ public static List BreakCurve(this List curves) /// /// 曲线 /// ge曲线 - [Obsolete("请使用Cad自带的 GetGeCurve 函数!")] public static Curve3d? ToCurve3d(this Curve curve) { return curve switch diff --git a/src/IFoxCAD.Cad/QuadTree/othergraph.cs b/src/IFoxCAD.Cad/QuadTree/othergraph.cs deleted file mode 100644 index ae98aaa..0000000 --- a/src/IFoxCAD.Cad/QuadTree/othergraph.cs +++ /dev/null @@ -1,323 +0,0 @@ -//using System; -//using System.Collections.Generic; -//using System.Linq; -//using System.Text; - -//namespace IFoxCAD.Cad.QuadTree -//{ -// // ===================== -// // 另一个实现的 - -// public interface IGraph -// { -// int Count { get; } - -// Vertex?[] Vertices { get; } - -// void AddEdge(Vertex startVertex, Vertex endVertex, double weight); - -// Vertex AddVertex(T data); - -// bool AreAdjacent(Vertex startVertex, Vertex endVertex); - -// double AdjacentDistance(Vertex startVertex, Vertex endVertex); - -// IEnumerable?> GetNeighbors(Vertex vertex); - -// void RemoveEdge(Vertex startVertex, Vertex endVertex); - -// void RemoveVertex(Vertex vertex); -// } - - - - - -// public interface IGraphSearch -// { -// /// -// /// 从起始顶点遍历图。 -// /// -// ///图实例。 -// ///搜索开始的顶点。 -// ///每个图顶点需要执行的动作 -// void VisitAll(IGraph graph, Vertex startVertex, Action>? action = null); -// } - -// // ===========另一个dfs实现,感觉比较好一点在于可以用委托干点其他的事情,和前一个可以结合一下 - - -// /// -// /// Implementation of graph vertex. -// /// -// /// Generic Type. -// public class Vertex -// { -// /// -// /// Gets vertex data. -// /// -// public T Data { get; } - -// /// -// /// Gets an index of the vertex in graph adjacency matrix. -// /// -// public int Index { get; } - - - - -// /// -// /// Initializes a new instance of the class. -// /// -// /// Vertex data. Generic type. -// /// Index of the vertex in graph adjacency matrix. -// public Vertex(T data, int index) -// { -// Data = data; -// Index = index; -// } - - -// } - -// /// -// /// Implementation of the directed weighted graph via adjacency matrix. -// /// -// /// Generic Type. -// public class Graph : IGraph -// { -// /// -// /// Capacity of the graph, indicates the maximum amount of vertices. -// /// -// private readonly int capacity; - -// /// -// /// Adjacency matrix which reflects the edges between vertices and their weight. -// /// Zero value indicates no edge between two vertices. -// /// -// private readonly double[,] adjacencyMatrix; - -// /// -// /// Initializes a new instance of the class. -// /// -// /// Capacity of the graph, indicates the maximum amount of vertices. -// public Graph(int capacity) -// { -// ThrowIfNegativeCapacity(capacity); - -// this.capacity = capacity; -// Vertices = new Vertex[capacity]; -// adjacencyMatrix = new double[capacity, capacity]; -// Count = 0; -// } - -// /// -// /// Gets list of vertices of the graph. -// /// -// public Vertex?[] Vertices { get; private set; } - -// /// -// /// Gets current amount of vertices in the graph. -// /// -// public int Count { get; private set; } - -// /// -// /// Adds new vertex to the graph. -// /// -// /// Data of the vertex. -// /// Reference to created vertex. -// public Vertex AddVertex(T data) -// { -// ThrowIfOverflow(); -// var vertex = new Vertex(data, Count, this); -// Vertices[Count] = vertex; -// Count++; -// return vertex; -// } - -// /// -// /// Creates an edge between two vertices of the graph. -// /// -// /// Vertex, edge starts at. -// /// Vertex, edge ends at. -// /// Double weight of an edge. -// public void AddEdge(Vertex startVertex, Vertex endVertex, double weight) -// { -// ThrowIfVertexNotInGraph(startVertex); -// ThrowIfVertexNotInGraph(endVertex); - -// ThrowIfWeightZero(weight); - -// var currentEdgeWeight = adjacencyMatrix[startVertex.Index, endVertex.Index]; - -// ThrowIfEdgeExists(currentEdgeWeight); - -// adjacencyMatrix[startVertex.Index, endVertex.Index] = weight; -// } - -// /// -// /// Removes vertex from the graph. -// /// -// /// Vertex to be removed. -// public void RemoveVertex(Vertex vertex) -// { -// ThrowIfVertexNotInGraph(vertex); - -// Vertices[vertex.Index] = null; -// vertex.SetGraphNull(); - -// for (var i = 0; i < Count; i++) -// { -// adjacencyMatrix[i, vertex.Index] = 0; -// adjacencyMatrix[vertex.Index, i] = 0; -// } - -// Count--; -// } - -// /// -// /// Removes edge between two vertices. -// /// -// /// Vertex, edge starts at. -// /// Vertex, edge ends at. -// public void RemoveEdge(Vertex startVertex, Vertex endVertex) -// { -// ThrowIfVertexNotInGraph(startVertex); -// ThrowIfVertexNotInGraph(endVertex); -// adjacencyMatrix[startVertex.Index, endVertex.Index] = 0; -// } - -// /// -// /// Gets a neighbors of particular vertex. -// /// -// /// Vertex, method gets list of neighbors for. -// /// Collection of the neighbors of particular vertex. -// public IEnumerable?> GetNeighbors(Vertex vertex) -// { -// ThrowIfVertexNotInGraph(vertex); - -// for (var i = 0; i < Count; i++) -// { -// if (adjacencyMatrix[vertex.Index, i] != 0) -// { -// yield return Vertices[i]; -// } -// } -// } - -// /// -// /// Returns true, if there is an edge between two vertices. -// /// -// /// Vertex, edge starts at. -// /// Vertex, edge ends at. -// /// True if edge exists, otherwise false. -// public bool AreAdjacent(Vertex startVertex, Vertex endVertex) -// { -// ThrowIfVertexNotInGraph(startVertex); -// ThrowIfVertexNotInGraph(endVertex); - -// return adjacencyMatrix[startVertex.Index, endVertex.Index] != 0; -// } - -// /// -// /// Return the distance between two vertices in the graph. -// /// -// /// first vertex in edge. -// /// secnod vertex in edge. -// /// distance between the two. -// public double AdjacentDistance(Vertex startVertex, Vertex endVertex) -// { -// if (AreAdjacent(startVertex, endVertex)) -// { -// return adjacencyMatrix[startVertex.Index, endVertex.Index]; -// } - -// return 0; -// } - -// private static void ThrowIfNegativeCapacity(int capacity) -// { -// if (capacity < 0) -// { -// throw new InvalidOperationException("Graph capacity should always be a non-negative integer."); -// } -// } - -// private static void ThrowIfWeightZero(double weight) -// { -// if (weight.Equals(0.0d)) -// { -// throw new InvalidOperationException("Edge weight cannot be zero."); -// } -// } - -// private static void ThrowIfEdgeExists(double currentEdgeWeight) -// { -// if (!currentEdgeWeight.Equals(0.0d)) -// { -// throw new InvalidOperationException($"Vertex already exists: {currentEdgeWeight}"); -// } -// } - -// private void ThrowIfOverflow() -// { -// if (Count == capacity) -// { -// throw new InvalidOperationException("Graph overflow."); -// } -// } - -// private void ThrowIfVertexNotInGraph(Vertex vertex) -// { -// if (vertex.Graph != this) -// { -// throw new InvalidOperationException($"Vertex does not belong to graph: {vertex}."); -// } -// } -// } -// /// -// /// 深度优先搜索 -遍历图的算法。 -// /// 算法从用户选择的根节点开始。 -// /// 算法在回溯之前尽可能沿着每个分支探索。 -// /// -// /// 顶点数据类型。 -// public class DepthFirstSearch : IGraphSearch where T : IComparable -// { -// /// -// /// 从起始顶点遍历图。 -// /// -// ///图实例。 -// ///搜索开始的顶点。 -// ///每个图顶点需要执行的动作 -// public void VisitAll(IGraph graph, Vertex startVertex, Action>? action = default) -// { -// Dfs(graph, startVertex, action, new HashSet>()); -// } - -// /// -// /// 从起始顶点遍历图。 -// /// -// ///图实例。 -// ///搜索开始的顶点。 -// ///每个图顶点需要执行的动作 -// ///具有访问顶点的哈希集。 -// private void Dfs(IGraph graph, Vertex startVertex, Action>? action, HashSet> visited) -// { -// action?.Invoke(startVertex); - -// visited.Add(startVertex); - -// foreach (var vertex in graph.GetNeighbors(startVertex)) -// { -// if (vertex == null || visited.Contains(vertex)) -// { -// continue; -// } - -// Dfs(graph, vertex!, action, visited); -// } -// } -// } - - -//} -- Gitee From a7c09fb02c308178f954bdefc6de33a62d14dd27 Mon Sep 17 00:00:00 2001 From: liuqihong <540762622@qq.com> Date: Sun, 17 Apr 2022 21:01:54 +0800 Subject: [PATCH 157/675] =?UTF-8?q?=E7=A7=BB=E9=99=A4TOPO?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/IFoxCAD.Cad/ExtensionMethod/CurveEx.cs | 32 +- src/IFoxCAD.Cad/ExtensionMethod/Topo.cs | 375 --------------------- src/IFoxCAD.Cad/ExtensionMethod/Topo2.cs | 170 ---------- tests/Test/TestCurve.cs | 56 +-- 4 files changed, 3 insertions(+), 630 deletions(-) delete mode 100644 src/IFoxCAD.Cad/ExtensionMethod/Topo.cs delete mode 100644 src/IFoxCAD.Cad/ExtensionMethod/Topo2.cs diff --git a/src/IFoxCAD.Cad/ExtensionMethod/CurveEx.cs b/src/IFoxCAD.Cad/ExtensionMethod/CurveEx.cs index 5dd7d7a..f6587ca 100644 --- a/src/IFoxCAD.Cad/ExtensionMethod/CurveEx.cs +++ b/src/IFoxCAD.Cad/ExtensionMethod/CurveEx.cs @@ -43,39 +43,11 @@ public static IEnumerable GetSplitCurves(this Curve curve, IEnumerable(); } - + /// /// 获取曲线集所围成的封闭区域的曲线集 /// - /// 曲线集 - /// 闭合的曲线集 - [Obsolete("此函数将被废弃,请使用 GetAllCycle 代替!")] - public static IEnumerable? Topo(List curves) - { - //闭合的曲线集合 - var closedCurve3d = new List(); - - var topo = new Topo(curves); - topo.CollisionFor(infos => { - var gs = new List(); - var c3 = new List(); - - topo.GetEdgesAndnewCurves(infos, gs, c3); - topo.AdjacencyList(gs, c3); - var regions = topo.GetRegions(gs); - - for (int i = 0; i < regions.Count; i++) - { - var cs3ds = regions[i] - .Select(e => e.GetCurve()) - .ToArray(); - closedCurve3d.Add(new CompositeCurve3d(cs3ds)); - } - }); - - //因为生成可能导致遗忘释放,所以这里统一生成 - return closedCurve3d.Select(e => e.ToCurve()!); - } + /// /// 获取曲线集所围成的封闭区域的曲线集 /// diff --git a/src/IFoxCAD.Cad/ExtensionMethod/Topo.cs b/src/IFoxCAD.Cad/ExtensionMethod/Topo.cs deleted file mode 100644 index 6ae7444..0000000 --- a/src/IFoxCAD.Cad/ExtensionMethod/Topo.cs +++ /dev/null @@ -1,375 +0,0 @@ -namespace IFoxCAD.Cad; - -public class Topo -{ - // 碰撞链集合 - List _CollisionChains; - // 求交类(每次set自动重置,都会有个新的结果) - static CurveCurveIntersector3d _Cci3d = new(); - // cad容差类 - public static Tolerance CadTolerance = new(1e-6, 1e-6); - - - public Topo(List curves) - { - if (curves == null || curves.Count == 0) - throw new ArgumentNullException(nameof(curves)); - - List curveList = new(); - - //提取包围盒信息 - for (int i = 0; i < curves.Count; i++) - curveList.Add(new CurveInfo(curves[i])); - - //碰撞检测+消重 - _CollisionChains = new(); - CollisionChain? tmp = null; - - Rect.XCollision(curveList, - oneRect => { - tmp = oneRect.CollisionChain;//有碰撞链就直接利用之前的链 - return false; - }, - (oneRect, twoRect) => { - //消重:包围盒大小一样+首尾相同+采样点相同 - if (oneRect.Equals(twoRect, 1e-6)) - { - var pta1 = oneRect.Edge.GeCurve3d.StartPoint; - var pta2 = oneRect.Edge.GeCurve3d.EndPoint; - var ptb1 = twoRect.Edge.GeCurve3d.StartPoint; - var ptb2 = twoRect.Edge.GeCurve3d.EndPoint; - - if ((pta1.IsEqualTo(ptb1, CadTolerance) && pta2.IsEqualTo(ptb2, CadTolerance)) - || - (pta1.IsEqualTo(ptb2, CadTolerance) && pta2.IsEqualTo(ptb1, CadTolerance))) - if (oneRect.Edge.SplitPointEquals(twoRect.Edge)) - return true;//跳过后续步骤 - } - - if (tmp == null) - { - tmp = new(); - oneRect.CollisionChain = tmp;//碰撞链设置 - tmp.Add(oneRect);//本体也加入链 - } - twoRect.CollisionChain = tmp;//碰撞链设置 - tmp.Add(twoRect); - return false; - }, - oneRect => { - if (tmp != null && !_CollisionChains.Contains(tmp)) - { - _CollisionChains.Add(tmp); - tmp = null; - } - }); - } - - - /// - /// 遍历多条碰撞链 - /// - /// - public void CollisionFor(Action> action) - { - _CollisionChains.ForEach(a => { - action(a);//输出每条碰撞链 - }); - } - - /// - /// 利用交点断分曲线和独立自闭曲线 - /// - /// 传入每组有碰撞的 - /// 传出不自闭的曲线集 - /// 传出自闭曲线集 - public void GetEdgesAndnewCurves(List infos_In, List edges_Out, List closedCurves_Out) - { - //此处是O(n²) - //曲线a和其他曲线n根据 交点 切割子线(第一次是自交对比) - for (int a = 0; a < infos_In.Count; a++) - { - var curve1 = infos_In[a]; - var gc1 = curve1.Edge.GeCurve3d; - var pars1 = curve1.Paramss; - - for (int n = a; n < infos_In.Count; n++) - { - var curve2 = infos_In[n]; - - //包围盒没有碰撞就直接结束 - if (!curve1.IntersectsWith(curve2)) - continue; - - var gc2 = curve2.Edge.GeCurve3d; - var pars2 = curve2.Paramss; - - _Cci3d.Set(gc1, gc2, Vector3d.ZAxis); - - //计算两条曲线的交点(多个),分别放入对应的交点参数集 - for (int k = 0; k < _Cci3d.NumberOfIntersectionPoints; k++) - { - var pars = _Cci3d.GetIntersectionParameters(k); - pars1.Add(pars[0]);//0是第一条曲线的交点参数 - pars2.Add(pars[1]);//1是第二条曲线的交点参数 - } - } - - if (gc1.IsClosed()) - closedCurves_Out.Add(gc1); - - if (pars1.Count == 0) - continue; - - //有交点参数 - //根据交点参数断分曲线,然后获取边界 - var c3ds = curve1.Split(pars1); - if (c3ds.Count > 0) - { - edges_Out.AddRange(c3ds); - } - else - { - //惊惊留:(不敢删啊...) - //狐哥写的这里出现的条件是:有曲线参数,但是切分不出来曲线...没懂为什么... - //是这些参数?{参数0位置?头参/尾参/参数不在曲线上?} - edges_Out.Add(curve1.Edge); - } - - //有交点的才消重,无交点必然不重复. - Edge.Distinct(edges_Out); - } - } - - /// - /// 创建邻接表 - /// - /// 传入每组有碰撞的;传出闭合边界集(扔掉单交点的) - /// 传出自闭曲线集 - public void AdjacencyList(List edges_InOut, List closedCurve3d_Out) - { - //构建边的邻接表 - //下面是键值对(基于ArrayOfStruct思想,拆开更合适内存布局) - //knots 是不重复地将所有交点设置为节点(如果是重复就对应 nums++) - //nums 是记录每个交点被重复了几次 - var knots = new List(); - var nums = new List(); - var closedEdges = new List(); - - //交点集合 knots 会不断增大,会变得更加慢,因此我在一开始就进行碰撞链分析 - for (int i = 0; i < edges_InOut.Count; i++) - { - var edge = edges_InOut[i]; - if (edge.GeCurve3d.IsClosed()) - { - closedEdges.Add(edge); - continue; - } - - var sp = edge.GeCurve3d.StartPoint; - var ep = edge.GeCurve3d.EndPoint; - - if (knots.Contains(edge.GeCurve3d.StartPoint)) - { - //含有就是其他曲线"共用"此交点, - //节点所在索引==共用计数索引=>将它++ - edge.StartIndex = knots.IndexOf(edge.GeCurve3d.StartPoint);//给它交点计数的索引,不是次数 - nums[edge.StartIndex]++;//交点计数 - } - else - { - //不含有就加入节点,共用计数也加入,边界设置节点索引 - knots.Add(edge.GeCurve3d.StartPoint); - nums.Add(1); - edge.StartIndex = knots.Count - 1; - } - - if (knots.Contains(edge.GeCurve3d.EndPoint)) - { - edge.EndIndex = knots.IndexOf(edge.GeCurve3d.EndPoint); - nums[edge.EndIndex]++; - } - else - { - knots.Add(edge.GeCurve3d.EndPoint); - nums.Add(1); - edge.EndIndex = knots.Count - 1; - } - } - - closedCurve3d_Out.AddRange(closedEdges.Select(e => e.GeCurve3d)); - - //这里把交点只有一条曲线通过的点过滤掉了,也就是尾巴的图元, - //剩下的都是闭合的曲线连接了,每个点都至少有两条曲线通过 - var tmp = edges_InOut - .Except(closedEdges)/*闭合多段线*/ - .Where(e => nums[e.StartIndex] > 1 && nums[e.EndIndex] > 1)/*剪枝:尾巴多段线*/; - -#if true2 - //待测试... - //无第三者图元提取,并且合并多段线 - var handInhand = tmp.Where(e => nums[e.StartIndex] == 1 && nums[e.EndIndex] == 1); - var gPe = GetPolyEdge(handInhand); - MergePolyEdge(gPe); - - //将手拉手的剔除,然后转为多段线 - var links = GetPolyEdge(tmp.Except(handInhand)); - - links.AddRange(gPe); - return links; //提供给图 -#else - var tmpArr = tmp.ToArray(); - edges_InOut.Clear(); - for (int i = 0; i < tmpArr.Length; i++) - edges_InOut.Add(tmpArr[i]); -#endif - } - - /// - /// 转为多段线 - /// - /// - /// - List GetPolyEdge(IEnumerable edges) - { - var links = new List(); - var ge = edges.GetEnumerator(); - while (ge.MoveNext()) - links.Add(new PolyEdge(ge.Current)); - return links; - } - - /// - /// 合并多段线 - /// - /// - void MergePolyEdge(List pes) - { - //然后要利用双循环,提取a和b比较共点 - for (int i = 0; i < pes.Count; i++) - { - var plA = pes[i]; - for (int j = pes.Count - 1; j > i; j--) - { - var plB = pes[j]; - - var apts = plA.Points(); - var bpts = plB.Points(); - var aSp = apts[0]; - var aEp = apts[apts.Count - 1]; - var bSp = bpts[0]; - var bEp = bpts[apts.Count - 1]; - - byte actionNum = 0;//不执行 - if (aSp == bSp)//{{c,b,a}{c,d}}=>{d,c,b,a} - actionNum = 1; - else if (aEp == bSp)//{{a,b,c}{c,d}}=>{a,b,c,d} - actionNum = 2; - else if (aSp == bEp)//{{c,b,a}{d,c}}=>{d,c,b,a} - { - actionNum = 1; - plB.Reverse(); - } - else if (aEp == bEp)//{{a,b,c}{d,c}}=>{a,b,c,d} - { - actionNum = 2; - plB.Reverse(); - } - - if (actionNum == 1) - { - plB.For((num, node) => { - if (num != 0)//跳过共元 - plA.AddFirst(node.Value); - return false; - }); - } - else if (actionNum == 2) - { - plB.For((num, node) => { - if (num != 0)//跳过共元 - plA.AddLast(node.Value); - return false; - }); - } - - if (actionNum != 0) - { - pes.RemoveAt(j); - j = pes.Count - 1;//指针重拨,一旦加入就从尾开始 - } - - } - } - } - - /// - /// 获取多个面域 - /// - /// 剩下都有两个交点的线 - /// - public List> GetRegions(List edges) - { - var regions_out = new List>(); - - /* - * TODO 这里暴力算法需要优化 - * 狐哥为了处理左拐还是右拐图形bd,用了所有的线做种子线,然后往前后推进 - * 前后推进:利用边界的顺序和逆序获取闭合链条, - * 会造成一环就有: 123,231,312,321,213,132,再其后再判断他们重复 - * - * 优化方案一:邻接表按引用次数排序之后,上面的交点如果只有两个图元,组成链,然后移除出邻接表. - * 这样我只有遇到多个共点时候,那就只有:多个链头尾(减少了腰身)和邻接表的 - * 实现递减. - */ - for (int i = 0; i < edges.Count; i++) - { - var edge = edges[i]; - var edgeItem = new EdgeItem(edge, true); - edgeItem.FindRegion(edges, regions_out); - edgeItem = new EdgeItem(edge, false); - edgeItem.FindRegion(edges, regions_out); - } - - DeduplicationRegions(regions_out); - return regions_out; - } - - /// - /// 移除相同面域 - /// - /// - void DeduplicationRegions(List> regions) - { - Basal.ArrayEx.Deduplication(regions, (first, last) => { - bool eq = false;//是否存在相同成员 - if (first.Count == last.Count) - { - var node1 = first.First; - var curve1 = node1!.Value.GeCurve3d; - - //两个面域对比,找到相同成员 - var node2 = last.GetNode(e => e.GeCurve3d == curve1); - if (node2 is not null) - { - eq = true; - var f1 = node1.Value.Forward; - var f2 = node2.Value.Forward; - //链条搜索方向来进行 - //判断每个节点的成员如果一致就会执行移除 - for (int k = 1; k < first.Count; k++) - { - node1 = node1.GetNext(f1); - node2 = node2.GetNext(f2); - if (node1!.Value.GeCurve3d != node2!.Value.GeCurve3d) - { - eq = false; - break; - } - } - } - } - return eq; - }); - } -} \ No newline at end of file diff --git a/src/IFoxCAD.Cad/ExtensionMethod/Topo2.cs b/src/IFoxCAD.Cad/ExtensionMethod/Topo2.cs deleted file mode 100644 index 3be6197..0000000 --- a/src/IFoxCAD.Cad/ExtensionMethod/Topo2.cs +++ /dev/null @@ -1,170 +0,0 @@ -namespace IFoxCAD.Cad; - -public class Topo2 -{ - /// - /// 创建邻接表 - /// - /// 传入每组有碰撞的;传出闭合边界集(扔掉单交点的) - /// 传出自闭曲线集 - public void AdjacencyList(List edges_InOut, List closedCurve3d_Out) - { - /* - * 邻接表:不重复地将共点(交点)作为标记,然后容器加入边 - */ - var knots = new List(); - for (int i = 0; i < edges_InOut.Count; i++) - { - var edge = edges_InOut[i]; - if (edge.GeCurve3d.IsClosed()) - { - closedCurve3d_Out.Add(edge.GeCurve3d); - continue; - } - - var sp = edge.GeCurve3d.StartPoint; - var ep = edge.GeCurve3d.EndPoint; - - //含有就是其他曲线"共用"此交点,在节点上面加入边; - //不含有就加入节点和边; - var kn1 = Knot.Contains(knots, sp); - if (kn1 != null) - kn1.Add(edge); - else - knots.Add(new Knot(sp, edge)); - - var kn2 = Knot.Contains(knots, ep); - if (kn2 != null) - kn2.Add(edge); - else - knots.Add(new Knot(ep, edge)); - } - - /* - * 剪枝: - * 共点若只有一次使用,代表此图元是尾巴,可以扔掉. - * 剩下每个点都至少有两条曲线通过(闭合曲线) - */ - for (int i = knots.Count - 1; i >= 0; i--) - { - var kn = knots[i]; - - if (kn.Count == 0) - { - //此条件不会触发;剔除过程不变0,因为移除了kn; - //剔除前也不会是0,因为必然初始化并加入; - knots.RemoveAt(i); - } - else if (kn.Count == 1) - { - //尾巴图元的尾点只有一个,因此它被孤独容器储存着; - //尾巴图元的头点可能有多个连接,会储存在其他容器,需要移除; - var fir = kn.First(); - for (int j = 0; j < knots.Count; j++) - knots[j].Remove(fir); - - knots.RemoveAt(i); - } - } - - var pk = PureKnots(knots); - - //TODO 直接进入穷举...图....返回参数取消掉 - //还需要优化深度优先算法=>逆时针最短路径 - //如果此方案测试通过,删除所有 DESIGN0409 的字段所涉及的操作 - } - - /// - /// 纯化节点 - /// - /// - List PureKnots(List knots) - { - /* - * 纯化节点: - * 把 "日" 变成 "(|)" 6个变成2个节点,但是穷举起来就是12;23;13;21;32;31好多啊,因此还需要优化深度优先算法=>逆时针最短路径 - * 连续的碎线(不闭合的多段线,无分叉,不自交), - * 在图上的节点就没有必然连接的多段线腰身了,搜索起来更快. - * - * 因为图在深度优先的时候必然会进入重复循环获取下一个节点,(逆序又进行了一次) - * 而上节点和下节点是必然握手的{pt1,pt2,pt2,pt3}, - * 那么我们可以直接数据纯化数据为{pt1,pt3},组成碎线链 EdgePoly ,减少了腰身每次获取的时间. - * - * (日日)两字一个角点相连,造成有(上中/上下/中下)次数没有共点2,直接每个都成为纯化边界; - * 但是此操作能够使得有2就加速 - */ - - var peListAll = new List(); - var peList2 = new List(); - for (int i = 0; i < knots.Count; i++) - { - var knot = knots[i]; - if (knot.Count == 2) - peList2.Add(new PolyEdge(knot)); - else - peListAll.Add(new PolyEdge(knot)); - } - - //共点只有两个图元,它们就是手拉手,没有第三者,然后合并多段线 - MergePolyEdge(peList2); - - peListAll.AddRange(peList2); - return peListAll; - } - - /// - /// 合并多段线 - /// PolyEdge{{a,b}{b,c}..}=>{a,b,c..} - /// - /// - void MergePolyEdge(List list) - { - for (int i = 0; i < list.Count; i++) - { - var plA = list[i]; - for (int j = list.Count - 1; j > i; j--) - { - var plB = list[j]; - - byte actionNum = 0;//不执行 - if (plA.First!.Value == plB.First!.Value)//{{c,b,a}{c,d}}=>{d,c,b,a} - actionNum = 1; - else if (plA.Last!.Value == plB.First!.Value)//{{a,b,c}{c,d}}=>{a,b,c,d} - actionNum = 2; - else if (plA.First!.Value == plB.Last!.Value)//{{c,b,a}{d,c}}=>{d,c,b,a} - { - actionNum = 1; - plB.Reverse(); - } - else if (plA.Last!.Value == plB.Last!.Value)//{{a,b,c}{d,c}}=>{a,b,c,d} - { - actionNum = 2; - plB.Reverse(); - } - - if (actionNum == 1) - { - plB.For((num, node) => { - if (num != 0)//跳过共元 - plA.AddFirst(node.Value); - return false; - }); - } - else if (actionNum == 2) - { - plB.For((num, node) => { - if (num != 0)//跳过共元 - plA.AddLast(node.Value); - return false; - }); - } - - if (actionNum != 0) - { - list.RemoveAt(j); - j = list.Count - 1;//指针重拨,一旦加入就从尾开始 - } - } - } - } -} \ No newline at end of file diff --git a/tests/Test/TestCurve.cs b/tests/Test/TestCurve.cs index 522209b..7633187 100644 --- a/tests/Test/TestCurve.cs +++ b/tests/Test/TestCurve.cs @@ -1,6 +1,4 @@ -using Autodesk.AutoCAD.BoundaryRepresentation; - -namespace Test +namespace Test { public class TestGraph { @@ -76,7 +74,6 @@ public void TestGraph1() //graph.AddEdge(5, 3); //Env.Print(graph); } - } public class TestCurve @@ -142,56 +139,5 @@ public void TestCurveCurveIntersector3d() //tt.ForEach(t => t.ForWrite(e => e.ColorIndex = 1)); //tr.CurrentSpace.AddEntity(tt); } - - - [CommandMethod("testtopo")] - public void TestToPo() - { - using var tr = new DBTrans(); - var ents = Env.Editor.SSGet().Value?.GetEntities(); - if (ents == null) - return; - - var tt = CurveEx.Topo(ents.ToList()); - tt.ForEach((i, t) => t.ForWrite(e => e.ColorIndex = i)); - tr.CurrentSpace.AddEntity(tt); - } - - [CommandMethod("testGetEdgesAndnewCurves")] - public void TestGetEdgesAndnewCurves() - { - using var tr = new DBTrans(); - var curves = Env.Editor.SSGet().Value?.GetEntities().ToList(); - if (curves == null) - return; - - var edgesGroup = new List>(); - var closedCurve3dGroup = new List>(); - - var topo = new Topo(curves); - - topo.CollisionFor(infos => { - var gs = new List(); - var c3 = new List(); - - topo.GetEdgesAndnewCurves(infos, gs, c3); - topo.AdjacencyList(gs, c3);//增加测试..需要加入四叉树 - - edgesGroup.Add(gs); - closedCurve3dGroup.Add(c3); - }); - - foreach (var edge in edgesGroup) - { - for (int i = 0; i < edge.Count; i++) - { - var ent = edge[i].GeCurve3d.ToCurve(); - ent.ColorIndex = i; - tr.CurrentSpace.AddEntity(ent); - } - } - - //Env.Print(""); - } } } \ No newline at end of file -- Gitee From ef99f377ee56fdf70881cdeafaf8a51c71438ac3 Mon Sep 17 00:00:00 2001 From: liuqihong <540762622@qq.com> Date: Sun, 17 Apr 2022 21:03:37 +0800 Subject: [PATCH 158/675] =?UTF-8?q?=E4=BF=AE=E6=94=B9=E5=91=BD=E5=90=8D?= =?UTF-8?q?=E7=A9=BA=E9=97=B4=E5=8F=82=E6=95=B0=E4=BE=9D=E7=85=A7=E4=BC=A0?= =?UTF-8?q?=E5=85=A5=E5=8F=82=E6=95=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/IFoxCAD.Cad/Runtime/AOP.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/IFoxCAD.Cad/Runtime/AOP.cs b/src/IFoxCAD.Cad/Runtime/AOP.cs index 8dcb869..f9b2499 100644 --- a/src/IFoxCAD.Cad/Runtime/AOP.cs +++ b/src/IFoxCAD.Cad/Runtime/AOP.cs @@ -28,7 +28,7 @@ public static void Run(string nameSpace) if (cmdDic.Count == 0) return; - var harmony = new Harmony(nameof(IFoxCAD)); + var harmony = new Harmony(nameSpace); var mPrefix = SymbolExtensions.GetMethodInfo(() => IFoxCmdAddFirst());//进入函数前 var mPostfix = SymbolExtensions.GetMethodInfo(() => IFoxCmdAddLast());//进入函数后 var mp1 = new HarmonyMethod(mPrefix); -- Gitee From 82452c318ee46e8f9fec9eb76baa6e283f16dd42 Mon Sep 17 00:00:00 2001 From: liuqihong <540762622@qq.com> Date: Sun, 17 Apr 2022 21:36:40 +0800 Subject: [PATCH 159/675] =?UTF-8?q?=E4=BF=AE=E6=94=B9AOP=E5=A2=9E=E5=8A=A0?= =?UTF-8?q?=E6=8B=92=E7=BB=9D=E6=B3=A8=E5=85=A5=E4=BA=8B=E5=8A=A1=E7=89=B9?= =?UTF-8?q?=E6=80=A7?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/IFoxCAD.Cad/Runtime/AOP.cs | 57 +++++++++++++++++++++++++++++----- tests/Test/TestAOP.cs | 37 ++++++++++++++++++++++ 2 files changed, 86 insertions(+), 8 deletions(-) diff --git a/src/IFoxCAD.Cad/Runtime/AOP.cs b/src/IFoxCAD.Cad/Runtime/AOP.cs index f9b2499..eec890e 100644 --- a/src/IFoxCAD.Cad/Runtime/AOP.cs +++ b/src/IFoxCAD.Cad/Runtime/AOP.cs @@ -1,6 +1,16 @@ namespace IFoxCAD.Cad; using HarmonyLib; +public class IFoxRefuseInjectionTransaction : Attribute +{ + /// + /// 拒绝注入事务 + /// + public IFoxRefuseInjectionTransaction() + { + } +} + public class AOP { /// @@ -12,19 +22,31 @@ public static void Run(string nameSpace) AutoClass.AppDomainGetTypes(type => { if (type.Namespace != nameSpace) return; + //类上面特性 + if (type.IsClass) + { + var attr = type.GetCustomAttributes(true); + if (RefuseInjectionTransaction(attr)) + return; + } + + //函数上面特性 var mets = type.GetMethods();//获得它的成员函数 - for (int i = 0; i < mets.Length; i++) + for (int ii = 0; ii < mets.Length; ii++) { - var method = mets[i]; + var method = mets[ii]; //找到特性,特性下面的方法要是Public,否则就被编译器优化掉了. var attr = method.GetCustomAttributes(true); - for (int j = 0; j < attr.Length; j++) - if (attr[j] is CommandMethodAttribute cmdAtt) - cmdDic.Add(cmdAtt.GlobalName, (cmdAtt, type, method)); + for (int jj = 0; jj < attr.Length; jj++) + if (attr[jj] is CommandMethodAttribute cmdAtt) + { + if (!RefuseInjectionTransaction(attr)) + cmdDic.Add(cmdAtt.GlobalName, (cmdAtt, type, method)); + } } }); - //运行的命令写在了Test.dll,当然不是ifox内了.... + //运行的命令写在了Test.dll,当然不是ifox.cad类库内了.... if (cmdDic.Count == 0) return; @@ -45,9 +67,28 @@ public static void Run(string nameSpace) } } + /// + /// 拒绝注入事务 + /// + /// 属性 + /// + private static bool RefuseInjectionTransaction(object[] attr) + { + bool refuseInjectionTransaction = false; + for (int kk = 0; kk < attr.Length; kk++) + { + if (attr[kk] is IFoxRefuseInjectionTransaction) + { + refuseInjectionTransaction = true; + break; + } + } + return refuseInjectionTransaction; + } + public static void IFoxCmdAddFirst() { - //此生命周期会在事务栈上面,被无限延长 + //此生命周期会在静态事务栈上面,被无限延长 var _ = DBTrans.Top; } @@ -55,5 +96,5 @@ public static void IFoxCmdAddLast() { var db = Application.DocumentManager.MdiActiveDocument.Database; DBTrans.FinishDatabase(db); - } + } } \ No newline at end of file diff --git a/tests/Test/TestAOP.cs b/tests/Test/TestAOP.cs index 46b3678..889d57c 100644 --- a/tests/Test/TestAOP.cs +++ b/tests/Test/TestAOP.cs @@ -23,4 +23,41 @@ public void Initialize() { AOP.Run(nameof(Test)); } +} + + +namespace Test +{ + //拒绝注入事务,写类上,则方法全都拒绝 + [IFoxRefuseInjectionTransaction] + public class MyClass2 + { + //此时这个也是拒绝的..这里加特性只是无所谓 + [IFoxRefuseInjectionTransaction] + [CommandMethod("IFoxRefuseInjectionTransaction2")] + public void TestIFoxRefuseInjectionTransaction() + { + } + + [CommandMethod("InjectionTransaction2")] + public void InjectionTransaction() + { + } + } + + public class MyClass + { + //类不拒绝,这里拒绝 + [IFoxRefuseInjectionTransaction] + [CommandMethod("IFoxRefuseInjectionTransaction")] + public void TestIFoxRefuseInjectionTransaction() + { + } + + //不拒绝 + [CommandMethod("InjectionTransaction")] + public void InjectionTransaction() + { + } + } } \ No newline at end of file -- Gitee From 3dffebd6742893c1a01e5bc973d20c582bbdda50 Mon Sep 17 00:00:00 2001 From: liuqihong <540762622@qq.com> Date: Sun, 17 Apr 2022 22:00:39 +0800 Subject: [PATCH 160/675] =?UTF-8?q?=E4=BF=AE=E6=94=B9AOP=E6=B3=A8=E5=85=A5?= =?UTF-8?q?=E4=BA=8B=E5=8A=A1=E4=BE=8B=E5=AD=90?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- tests/Test/TestAOP.cs | 31 ++++++++++++++++++++----------- 1 file changed, 20 insertions(+), 11 deletions(-) diff --git a/tests/Test/TestAOP.cs b/tests/Test/TestAOP.cs index 889d57c..6f52bda 100644 --- a/tests/Test/TestAOP.cs +++ b/tests/Test/TestAOP.cs @@ -28,34 +28,43 @@ public void Initialize() namespace Test { - //拒绝注入事务,写类上,则方法全都拒绝 - [IFoxRefuseInjectionTransaction] - public class MyClass2 + /* + * 天秀的事务注入,让你告别事务处理 + * https://www.cnblogs.com/JJBox/p/16157578.html + */ + public class AopTestClass { - //此时这个也是拒绝的..这里加特性只是无所谓 + //类不拒绝,这里拒绝 [IFoxRefuseInjectionTransaction] - [CommandMethod("IFoxRefuseInjectionTransaction2")] + [CommandMethod("IFoxRefuseInjectionTransaction")] public void TestIFoxRefuseInjectionTransaction() { } - [CommandMethod("InjectionTransaction2")] + //不拒绝 + [CommandMethod("InjectionTransaction")] public void InjectionTransaction() { + //怎么用事务呢? + //直接用 DBTrans.Top } } - public class MyClass + //拒绝注入事务,写类上,则方法全都拒绝 + [IFoxRefuseInjectionTransaction] + public class AopTestClassRefuseInjection { - //类不拒绝,这里拒绝 + //此时这个也是拒绝的..这里加特性只是无所谓 [IFoxRefuseInjectionTransaction] - [CommandMethod("IFoxRefuseInjectionTransaction")] + [CommandMethod("IFoxRefuseInjectionTransaction2")] public void TestIFoxRefuseInjectionTransaction() { + //拒绝注入就要自己开事务,通常用在循环提交事务上面. + //另见 报错0x02 https://www.cnblogs.com/JJBox/p/10798940.html + using var tr = new DBTrans(); } - //不拒绝 - [CommandMethod("InjectionTransaction")] + [CommandMethod("InjectionTransaction2")] public void InjectionTransaction() { } -- Gitee From da094d99230536476b89e2321803645703233ed5 Mon Sep 17 00:00:00 2001 From: vicwjb <403009819@qq.com> Date: Sun, 17 Apr 2022 23:52:25 +0800 Subject: [PATCH 161/675] =?UTF-8?q?=E4=BF=AEdispose?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/IFoxCAD.Cad/Runtime/DBTrans.cs | 110 ++++++++++++++++------------- tests/Test/TestAOP.cs | 22 +----- tests/Test/testtr.cs | 36 +++++++++- 3 files changed, 100 insertions(+), 68 deletions(-) diff --git a/src/IFoxCAD.Cad/Runtime/DBTrans.cs b/src/IFoxCAD.Cad/Runtime/DBTrans.cs index 9e610e0..92c043d 100644 --- a/src/IFoxCAD.Cad/Runtime/DBTrans.cs +++ b/src/IFoxCAD.Cad/Runtime/DBTrans.cs @@ -44,7 +44,7 @@ public static DBTrans Top * 因此所以无法清理栈,所以Dispose不触发,导致无法刷新图元和Ctrl+Z出错 * 所以用AOP方式修复 */ -#if false +//#if false //不使用AOP方式修复,强迫用户先开启事务 if (dBTrans.Count == 0)//静态获取的时候就是0 throw new ArgumentNullException("事务栈没有任何事务,请在调用前创建:" + nameof(DBTrans)); @@ -61,14 +61,14 @@ public static DBTrans Top } if (dBTrans.Count == 0) trans = new DBTrans(); -#else - //使用AOP方式修复 - DBTrans trans; - if (dBTrans.Count == 0) - trans = new DBTrans(); - else - trans = dBTrans.Peek(); -#endif + //#else + // //使用AOP方式修复 + // DBTrans trans; + // if (dBTrans.Count == 0) + // trans = new DBTrans(); + // else + // trans = dBTrans.Peek(); + //#endif return trans; } } @@ -76,21 +76,21 @@ public static DBTrans Top /// 结束此数据库的所有事务_AOP修复方案 /// /// - public static void FinishDatabase(Database db) - { - if (dBTrans.Count == 0) - return; - - var trans = dBTrans.Peek(); - while (dBTrans.Count != 0 && trans.Database == db) //跨数据库 - { - if (trans._commit) - trans.Commit(); - dBTrans.Pop(); - if (dBTrans.Count != 0) - trans = dBTrans.Peek(); - } - } + //public static void FinishDatabase(Database db) + //{ + // if (dBTrans.Count == 0) + // return; + + // var trans = dBTrans.Peek(); + // while (dBTrans.Count != 0 && trans.Database == db) //跨数据库 + // { + // if (trans._commit) + // trans.Commit(); + // dBTrans.Pop(); + // if (dBTrans.Count != 0) + // trans = dBTrans.Peek(); + // } + //} /// /// 文档 @@ -439,41 +439,55 @@ public static ObjectId TryGetObjectId(Database db, Handle handle) public void Abort() { - Transaction.Abort(); Dispose(false); } public void Commit() { - if (_commit) - Transaction.Commit(); - else - Abort(); + Dispose(true); } protected virtual void Dispose(bool disposing) { - if (!disposedValue) - { - if (disposing) - { - // 释放托管状态(托管对象) - Commit(); - dBTrans.Pop(); - if (!Transaction.IsDisposed) - { - if (Document?.IsActive == true) - Transaction.TransactionManager.QueueForGraphicsFlush(); - Transaction.Dispose(); - } - documentLock?.Dispose(); - } + /* 事务dispose流程: + * 1. 根据传入的参数确定是否提交,true为提交,false为不提交 + * 2. 根据disposedValue的值确定是否重复dispose,false为首次dispose + * 3. 如果锁文档就将文档锁dispose + * 4. 不管是否提交,既然进入dispose,就要将事务栈的当前事务弹出 + * 注意这里的事务栈不是cad的事务管理器,而是dbtrans的事务 + * 5. 清理非托管的字段 + */ - // 释放未托管的资源(未托管的对象)并替代终结器 - // 将大型字段设置为 null - disposedValue = true; + if (disposedValue) + { + return; } - //Transaction.TransactionManager.QueueForGraphicsFlush(); + if (disposing) + { + // 释放托管状态(托管对象) + // 调用cad的事务进行提交 + Transaction.Commit(); + } + else + { + // 否则取消所有的修改 + Transaction.Abort(); + } + // 调用 cad事务的dispose进行销毁 + if (!Transaction.IsDisposed) + { + Transaction.Dispose(); + } + // 调用文档锁dispose + documentLock?.Dispose(); + + // 将事务栈的当前dbtrans弹栈 + dBTrans.Pop(); + + // 释放未托管的资源(未托管的对象)并替代终结器 + // 将大型字段设置为 null + disposedValue = true; + } // 仅当“Dispose(bool disposing)”拥有用于释放未托管资源的代码时才替代终结器 diff --git a/tests/Test/TestAOP.cs b/tests/Test/TestAOP.cs index 6f52bda..290c57d 100644 --- a/tests/Test/TestAOP.cs +++ b/tests/Test/TestAOP.cs @@ -1,21 +1,4 @@ -/* - * 这里必须要实现一次这个接口,才能使用特性 - */ -public class AutoGoExtensionApplication : IExtensionApplication -{ - public void Initialize() - { - new AutoClass().Initialize(); - } - - public void Terminate() - { - } -} - -/* - * 在所有的命令末尾注入清空事务栈函数 - */ + public class AutoAOP { [IFoxInitialize] @@ -69,4 +52,5 @@ public void InjectionTransaction() { } } -} \ No newline at end of file +} + diff --git a/tests/Test/testtr.cs b/tests/Test/testtr.cs index f82fd9f..615791b 100644 --- a/tests/Test/testtr.cs +++ b/tests/Test/testtr.cs @@ -1,4 +1,6 @@ -namespace Test +using System.Runtime.Remoting.Metadata; + +namespace Test { public class TestTrans { @@ -10,5 +12,37 @@ public void Testtr() tr.ModelSpace.AddCircle(new Point3d(0, 0, 0), 20); tr.Database.SaveAs(filename,DwgVersion.Current); } + [CommandMethod("testifoxcommit")] + public void Testifoxcommit() + { + + using var tr = new DBTrans(); + tr.ModelSpace.AddCircle(new Point3d(0, 0, 0), 20); + tr.Commit(); + } + + // AOP 应用 预计示例: + // 1. 无参数 + //[AOP] + //[CommandMethod("TESTAOP")] + //public void testaop() + //{ + // // 不用 using var tr = new DBTrans(); + // var tr = DBTrans.Top; + // tr.ModelSpace.AddCircle(new Point3d(0, 0, 0), 20); + //} + + // 2. 有参数 + //[AOP("file")] + //[CommandMethod("TESTAOP")] + //public void testaop() + //{ + // // 不用 using var tr = new DBTrans(file); + // var tr = DBTrans.Top; + // tr.ModelSpace.AddCircle(new Point3d(0, 0, 0), 20); + //} + + + } } -- Gitee From 94f35581b746fcef81cec5d230cf26ff30e2b924 Mon Sep 17 00:00:00 2001 From: liuqihong <540762622@qq.com> Date: Mon, 18 Apr 2022 00:26:59 +0800 Subject: [PATCH 162/675] =?UTF-8?q?=E7=AD=89=E5=B1=B1=E4=BA=BA=E6=B5=8B?= =?UTF-8?q?=E8=AF=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/IFoxCAD.Cad/Runtime/AOP.cs | 23 ++++++++-- src/IFoxCAD.Cad/Runtime/DBTrans.cs | 69 +++++++++++------------------- 2 files changed, 45 insertions(+), 47 deletions(-) diff --git a/src/IFoxCAD.Cad/Runtime/AOP.cs b/src/IFoxCAD.Cad/Runtime/AOP.cs index eec890e..52e7e62 100644 --- a/src/IFoxCAD.Cad/Runtime/AOP.cs +++ b/src/IFoxCAD.Cad/Runtime/AOP.cs @@ -11,6 +11,24 @@ public IFoxRefuseInjectionTransaction() } } +/* + * 例子 + * public class AutoAOP + * { + * [IFoxInitialize]//自执行接口 + * public void Initialize() + * { + * AOP.Run(nameof(Test)); + * } + * } + * + * 类库用户想侵入的命名空间是用户的, + * 所以需要用户手动进行AOP.Run(), + * 默认情况不侵入用户的命令,必须用户手动启用此功能; + * 启动执行策略之后,侵入命名空间下的命令, + * 此时有拒绝特性的策略保证括免,因为用户肯定是想少写一个事务注入的特性; + */ + public class AOP { /// @@ -94,7 +112,6 @@ public static void IFoxCmdAddFirst() public static void IFoxCmdAddLast() { - var db = Application.DocumentManager.MdiActiveDocument.Database; - DBTrans.FinishDatabase(db); - } + DBTrans.FinishDatabase(); + } } \ No newline at end of file diff --git a/src/IFoxCAD.Cad/Runtime/DBTrans.cs b/src/IFoxCAD.Cad/Runtime/DBTrans.cs index 92c043d..929a6b1 100644 --- a/src/IFoxCAD.Cad/Runtime/DBTrans.cs +++ b/src/IFoxCAD.Cad/Runtime/DBTrans.cs @@ -44,53 +44,37 @@ public static DBTrans Top * 因此所以无法清理栈,所以Dispose不触发,导致无法刷新图元和Ctrl+Z出错 * 所以用AOP方式修复 */ -//#if false +#if true //不使用AOP方式修复,强迫用户先开启事务 - if (dBTrans.Count == 0)//静态获取的时候就是0 + if (dBTrans.Count == 0) throw new ArgumentNullException("事务栈没有任何事务,请在调用前创建:" + nameof(DBTrans)); var db = Application.DocumentManager.MdiActiveDocument.Database; var trans = dBTrans.Peek(); - while (dBTrans.Count != 0 && trans.Database != db) //跨数据库 - { - if (trans._commit) - trans.Commit(); - dBTrans.Pop(); - if (dBTrans.Count != 0) - trans = dBTrans.Peek(); - } + if (trans.Database != db) + trans = new DBTrans(); +#else + //使用AOP方式修复 + DBTrans trans; if (dBTrans.Count == 0) trans = new DBTrans(); - //#else - // //使用AOP方式修复 - // DBTrans trans; - // if (dBTrans.Count == 0) - // trans = new DBTrans(); - // else - // trans = dBTrans.Peek(); - //#endif + else + trans = dBTrans.Peek(); +#endif return trans; } } + /// - /// 结束此数据库的所有事务_AOP修复方案 + /// 结束栈中所有事务_AOP修复方案 + /// 此方式代表了不允许跨事务循环命令 + /// 若有则需在此命令进行 拒绝注入AOP特性 /// - /// - //public static void FinishDatabase(Database db) - //{ - // if (dBTrans.Count == 0) - // return; - - // var trans = dBTrans.Peek(); - // while (dBTrans.Count != 0 && trans.Database == db) //跨数据库 - // { - // if (trans._commit) - // trans.Commit(); - // dBTrans.Pop(); - // if (dBTrans.Count != 0) - // trans = dBTrans.Peek(); - // } - //} + public static void FinishDatabase() + { + while (dBTrans.Count != 0) + dBTrans.Peek().Dispose(); + } /// /// 文档 @@ -459,9 +443,12 @@ protected virtual void Dispose(bool disposing) */ if (disposedValue) - { return; - } + + // 释放未托管的资源(未托管的对象)并替代终结器 + // 将大型字段设置为 null + disposedValue = true; + if (disposing) { // 释放托管状态(托管对象) @@ -475,19 +462,13 @@ protected virtual void Dispose(bool disposing) } // 调用 cad事务的dispose进行销毁 if (!Transaction.IsDisposed) - { Transaction.Dispose(); - } + // 调用文档锁dispose documentLock?.Dispose(); // 将事务栈的当前dbtrans弹栈 dBTrans.Pop(); - - // 释放未托管的资源(未托管的对象)并替代终结器 - // 将大型字段设置为 null - disposedValue = true; - } // 仅当“Dispose(bool disposing)”拥有用于释放未托管资源的代码时才替代终结器 -- Gitee From 31e6ff2681e574267b0a5c8b871a1d12ca9c0f0d Mon Sep 17 00:00:00 2001 From: liuqihong <540762622@qq.com> Date: Mon, 18 Apr 2022 23:53:13 +0800 Subject: [PATCH 163/675] =?UTF-8?q?=E8=A1=A5=E5=85=85?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/IFoxCAD.Cad/Runtime/AOP.cs | 20 +---------------- src/IFoxCAD.Cad/Runtime/DBTrans.cs | 2 +- src/IFoxCAD.Cad/Runtime/IAutoGo.cs | 2 +- tests/Test/TestAOP.cs | 35 ++++++++++++++++++++++++------ 4 files changed, 31 insertions(+), 28 deletions(-) diff --git a/src/IFoxCAD.Cad/Runtime/AOP.cs b/src/IFoxCAD.Cad/Runtime/AOP.cs index 52e7e62..b9cbd3b 100644 --- a/src/IFoxCAD.Cad/Runtime/AOP.cs +++ b/src/IFoxCAD.Cad/Runtime/AOP.cs @@ -10,25 +10,7 @@ public IFoxRefuseInjectionTransaction() { } } - -/* - * 例子 - * public class AutoAOP - * { - * [IFoxInitialize]//自执行接口 - * public void Initialize() - * { - * AOP.Run(nameof(Test)); - * } - * } - * - * 类库用户想侵入的命名空间是用户的, - * 所以需要用户手动进行AOP.Run(), - * 默认情况不侵入用户的命令,必须用户手动启用此功能; - * 启动执行策略之后,侵入命名空间下的命令, - * 此时有拒绝特性的策略保证括免,因为用户肯定是想少写一个事务注入的特性; - */ - + public class AOP { /// diff --git a/src/IFoxCAD.Cad/Runtime/DBTrans.cs b/src/IFoxCAD.Cad/Runtime/DBTrans.cs index 929a6b1..8593f58 100644 --- a/src/IFoxCAD.Cad/Runtime/DBTrans.cs +++ b/src/IFoxCAD.Cad/Runtime/DBTrans.cs @@ -44,7 +44,7 @@ public static DBTrans Top * 因此所以无法清理栈,所以Dispose不触发,导致无法刷新图元和Ctrl+Z出错 * 所以用AOP方式修复 */ -#if true +#if true2 //不使用AOP方式修复,强迫用户先开启事务 if (dBTrans.Count == 0) throw new ArgumentNullException("事务栈没有任何事务,请在调用前创建:" + nameof(DBTrans)); diff --git a/src/IFoxCAD.Cad/Runtime/IAutoGo.cs b/src/IFoxCAD.Cad/Runtime/IAutoGo.cs index 1a0b271..7a28fee 100644 --- a/src/IFoxCAD.Cad/Runtime/IAutoGo.cs +++ b/src/IFoxCAD.Cad/Runtime/IAutoGo.cs @@ -73,7 +73,7 @@ public void Run() } } -public class AutoClass : IExtensionApplication +public class AutoClass //: IExtensionApplication { static List _InitializeList = new(); //储存方法用于初始化 static List _TerminateList = new(); //储存方法用于结束释放 diff --git a/tests/Test/TestAOP.cs b/tests/Test/TestAOP.cs index 290c57d..5dcfd12 100644 --- a/tests/Test/TestAOP.cs +++ b/tests/Test/TestAOP.cs @@ -1,4 +1,25 @@ - +/* +* 这里必须要实现一次这个接口,才能使用特性 +*/ +public class AutoGoExtensionApplication : IExtensionApplication +{ + public void Initialize() + { + new AutoClass().Initialize(); + } + + public void Terminate() + { + } +} + +/* + * 类库用户想侵入的命名空间是用户的, + * 所以需要用户手动进行AOP.Run(), + * 默认情况不侵入用户的命令,必须用户手动启用此功能; + * 启动执行策略之后,侵入命名空间下的命令, + * 此时有拒绝特性的策略保证括免,因为用户肯定是想少写一个事务注入的特性; + */ public class AutoAOP { [IFoxInitialize] @@ -8,7 +29,6 @@ public void Initialize() } } - namespace Test { /* @@ -20,7 +40,7 @@ public class AopTestClass //类不拒绝,这里拒绝 [IFoxRefuseInjectionTransaction] [CommandMethod("IFoxRefuseInjectionTransaction")] - public void TestIFoxRefuseInjectionTransaction() + public void IFoxRefuseInjectionTransaction() { } @@ -30,6 +50,8 @@ public void InjectionTransaction() { //怎么用事务呢? //直接用 DBTrans.Top + var dBTrans = new DBTrans(); + dBTrans.Commit(); } } @@ -40,7 +62,7 @@ public class AopTestClassRefuseInjection //此时这个也是拒绝的..这里加特性只是无所谓 [IFoxRefuseInjectionTransaction] [CommandMethod("IFoxRefuseInjectionTransaction2")] - public void TestIFoxRefuseInjectionTransaction() + public void IFoxRefuseInjectionTransaction2() { //拒绝注入就要自己开事务,通常用在循环提交事务上面. //另见 报错0x02 https://www.cnblogs.com/JJBox/p/10798940.html @@ -48,9 +70,8 @@ public void TestIFoxRefuseInjectionTransaction() } [CommandMethod("InjectionTransaction2")] - public void InjectionTransaction() + public void InjectionTransaction2() { } } -} - +} \ No newline at end of file -- Gitee From 167c8943049b4a2881616b3734df9c2d185fa2d0 Mon Sep 17 00:00:00 2001 From: vicwjb <403009819@qq.com> Date: Tue, 19 Apr 2022 06:03:07 +0000 Subject: [PATCH 164/675] =?UTF-8?q?=E6=9B=B4=E6=96=B0=E5=85=B3=E4=BA=8E?= =?UTF-8?q?=E8=87=AA=E5=8A=A8=E5=8A=A0=E8=BD=BD=E7=9A=84=E5=B8=AE=E5=8A=A9?= =?UTF-8?q?=E9=94=99=E8=AF=AF?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index 50f9e4b..1d1966e 100644 --- a/README.md +++ b/README.md @@ -97,19 +97,20 @@ using IFoxCAD.Cad; using System; using System.Reflection; - [assembly: ExtensionApplication(typeof(CadLoad.CmdINI))] namespace CadLoad { - internal class CmdINI : AutoRegAssem + public class CmdINI : AutoRegAssem { public override void Initialize() { - throw new NotImplementedException(); + //throw new NotImplementedException(); + //这里写用于程序初始化的代码 } public override void Terminate() { - throw new NotImplementedException(); + //throw new NotImplementedException(); + //这里写程序卸载的代码,但是一般你也看不到运行,所以空着也行 } } } -- Gitee From 0c6faade377ae6908aef0851bd9b1e7acda54631 Mon Sep 17 00:00:00 2001 From: liuqihong <540762622@qq.com> Date: Tue, 19 Apr 2022 21:02:18 +0800 Subject: [PATCH 165/675] =?UTF-8?q?=E5=88=A0=E9=99=A4topo?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- ...271\346\225\260\350\257\264\346\230\216.png" | Bin 28362 -> 0 bytes ...205\346\216\245\347\237\251\345\275\242.png" | Bin 40404 -> 0 bytes src/IFoxCAD.Cad/ExtensionMethod/CurveEx.cs | 6 +----- tests/Test/TestAOP.cs | 7 ++++++- 4 files changed, 7 insertions(+), 6 deletions(-) delete mode 100644 "docs/Topo\345\221\275\344\273\244\350\257\264\346\230\216/\346\233\262\347\272\277\351\207\207\346\240\267\347\202\271\346\225\260\350\257\264\346\230\216.png" delete mode 100644 "docs/Topo\345\221\275\344\273\244\350\257\264\346\230\216/\346\265\213\350\257\225\346\241\210\344\276\213\344\270\244\344\270\252\345\206\205\346\216\245\347\237\251\345\275\242.png" diff --git "a/docs/Topo\345\221\275\344\273\244\350\257\264\346\230\216/\346\233\262\347\272\277\351\207\207\346\240\267\347\202\271\346\225\260\350\257\264\346\230\216.png" "b/docs/Topo\345\221\275\344\273\244\350\257\264\346\230\216/\346\233\262\347\272\277\351\207\207\346\240\267\347\202\271\346\225\260\350\257\264\346\230\216.png" deleted file mode 100644 index 1c27b4ee80a3695d690b1d90a6e0fd3f477d8d1b..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 28362 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 H)8PLBRGT($ diff --git "a/docs/Topo\345\221\275\344\273\244\350\257\264\346\230\216/\346\265\213\350\257\225\346\241\210\344\276\213\344\270\244\344\270\252\345\206\205\346\216\245\347\237\251\345\275\242.png" "b/docs/Topo\345\221\275\344\273\244\350\257\264\346\230\216/\346\265\213\350\257\225\346\241\210\344\276\213\344\270\244\344\270\252\345\206\205\346\216\245\347\237\251\345\275\242.png" deleted file mode 100644 index 980676788a18008956c5944bca72949a43bb8b39..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 40404 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 diff --git a/src/IFoxCAD.Cad/ExtensionMethod/CurveEx.cs b/src/IFoxCAD.Cad/ExtensionMethod/CurveEx.cs index f6587ca..d053809 100644 --- a/src/IFoxCAD.Cad/ExtensionMethod/CurveEx.cs +++ b/src/IFoxCAD.Cad/ExtensionMethod/CurveEx.cs @@ -43,11 +43,7 @@ public static IEnumerable GetSplitCurves(this Curve curve, IEnumerable(); } - - /// - /// 获取曲线集所围成的封闭区域的曲线集 - /// - + /// /// 获取曲线集所围成的封闭区域的曲线集 /// diff --git a/tests/Test/TestAOP.cs b/tests/Test/TestAOP.cs index 5dcfd12..a7480be 100644 --- a/tests/Test/TestAOP.cs +++ b/tests/Test/TestAOP.cs @@ -13,6 +13,10 @@ public void Terminate() } } + +//被注入的函数将不能使用断点, +//因此用户要充分了解才能使用 +#if false /* * 类库用户想侵入的命名空间是用户的, * 所以需要用户手动进行AOP.Run(), @@ -74,4 +78,5 @@ public void InjectionTransaction2() { } } -} \ No newline at end of file +} +#endif \ No newline at end of file -- Gitee From 325d54a761064a429cf0b5588a4a139b04cdabff Mon Sep 17 00:00:00 2001 From: liuqihong <540762622@qq.com> Date: Tue, 19 Apr 2022 21:11:35 +0800 Subject: [PATCH 166/675] =?UTF-8?q?=E4=BF=AE=E6=94=B9=E8=87=AA=E5=8A=A8?= =?UTF-8?q?=E6=89=A7=E8=A1=8C=E7=9B=B8=E5=85=B3=E7=9A=84=E8=AF=B4=E6=98=8E?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 37 +++++++++++++++++++++++++++++++++++-- tests/Test/TestAOP.cs | 11 +++++++---- 2 files changed, 42 insertions(+), 6 deletions(-) diff --git a/README.md b/README.md index 1d1966e..6a00531 100644 --- a/README.md +++ b/README.md @@ -116,10 +116,43 @@ } ``` -7. 天秀的打开模式提权 + 使用特性进行分段初始化 - 由于cad的对象是有打开模式,是否可写等等,为了安全起见,在处理对象时,一般是用读模式打开,然后需要写数据的时候在提权为写模式,然后在降级到读模式,但是这个过程中,很容易漏掉某些步骤,然后cad崩溃。为了处理这些情况,内裤提供了提权类来保证读写模式的有序转换。 + ```c# + // 必须实现一次这个接口 + public class CmdINI : AutoRegAssem + { + public override void Initialize() + { + new IFoxCAD.Cad.AutoClass().Initialize(); + } + + public override Sequence SequenceId() + { + return Sequence.Last; + } + public override void Terminate() { } + } + + //其他的类中的函数: + //实现自动接口之后,在任意一个函数上面使用此特性,减少每次改动 CmdINI 类 + public class AutoAOP + { + [IFoxInitialize] + public void Initialize() + { + //TODO 你想在加载dll之后自动执行的函数 + } + } + ``` + + + +7. 天秀的打开模式提权 + + 由于cad的对象是有打开模式,是否可写等等,为了安全起见,在处理对象时,一般是用读模式打开,然后需要写数据的时候在提权为写模式,然后在降级到读模式,但是这个过程中,很容易漏掉某些步骤,然后cad崩溃。为了处理这些情况,内裤提供了提权类来保证读写模式的有序转换。 + ```c# using(line.ForWrite()) //开启对象写模式提权事务 { diff --git a/tests/Test/TestAOP.cs b/tests/Test/TestAOP.cs index a7480be..a38bcb5 100644 --- a/tests/Test/TestAOP.cs +++ b/tests/Test/TestAOP.cs @@ -1,16 +1,19 @@ /* * 这里必须要实现一次这个接口,才能使用特性 */ -public class AutoGoExtensionApplication : IExtensionApplication +public class CmdINI : AutoRegAssem { - public void Initialize() + public override void Initialize() { - new AutoClass().Initialize(); + new IFoxCAD.Cad.AutoClass().Initialize(); } - public void Terminate() + public override Sequence SequenceId() { + return Sequence.Last; } + + public override void Terminate() { } } -- Gitee From a5602a71a966b9157faf89951feae58bacf2cff3 Mon Sep 17 00:00:00 2001 From: liuqihong <540762622@qq.com> Date: Tue, 19 Apr 2022 22:40:31 +0800 Subject: [PATCH 167/675] =?UTF-8?q?=E4=BF=AE=E6=94=B9=E4=BA=8B=E5=8A=A1?= =?UTF-8?q?=E6=A0=88=E5=89=94=E9=99=A4AOP?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/IFoxCAD.Cad/Runtime/DBTrans.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/IFoxCAD.Cad/Runtime/DBTrans.cs b/src/IFoxCAD.Cad/Runtime/DBTrans.cs index 8593f58..ff292e4 100644 --- a/src/IFoxCAD.Cad/Runtime/DBTrans.cs +++ b/src/IFoxCAD.Cad/Runtime/DBTrans.cs @@ -44,7 +44,7 @@ public static DBTrans Top * 因此所以无法清理栈,所以Dispose不触发,导致无法刷新图元和Ctrl+Z出错 * 所以用AOP方式修复 */ -#if true2 +#if true //不使用AOP方式修复,强迫用户先开启事务 if (dBTrans.Count == 0) throw new ArgumentNullException("事务栈没有任何事务,请在调用前创建:" + nameof(DBTrans)); @@ -70,7 +70,7 @@ public static DBTrans Top /// 此方式代表了不允许跨事务循环命令 /// 若有则需在此命令进行 拒绝注入AOP特性 /// - public static void FinishDatabase() + internal static void FinishDatabase() { while (dBTrans.Count != 0) dBTrans.Peek().Dispose(); -- Gitee From 472eac29e813cb32d262fe8d596f2bb149e2050a Mon Sep 17 00:00:00 2001 From: vicwjb <403009819@qq.com> Date: Tue, 19 Apr 2022 22:58:37 +0800 Subject: [PATCH 168/675] =?UTF-8?q?=E6=B3=A8=E9=87=8A=E6=8E=89AOP=E7=9A=84?= =?UTF-8?q?=E7=9B=B8=E5=85=B3=E4=BB=A3=E7=A0=81=EF=BC=8C=E5=BE=85=E4=BC=98?= =?UTF-8?q?=E5=8C=96?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/IFoxCAD.Cad/Runtime/AOP.cs | 180 ++++++++++++++--------------- src/IFoxCAD.Cad/Runtime/DBTrans.cs | 18 +-- tests/Test/testtr.cs | 3 +- 3 files changed, 103 insertions(+), 98 deletions(-) diff --git a/src/IFoxCAD.Cad/Runtime/AOP.cs b/src/IFoxCAD.Cad/Runtime/AOP.cs index b9cbd3b..ac8270b 100644 --- a/src/IFoxCAD.Cad/Runtime/AOP.cs +++ b/src/IFoxCAD.Cad/Runtime/AOP.cs @@ -1,99 +1,99 @@ -namespace IFoxCAD.Cad; -using HarmonyLib; +//namespace IFoxCAD.Cad; +//using HarmonyLib; -public class IFoxRefuseInjectionTransaction : Attribute -{ - /// - /// 拒绝注入事务 - /// - public IFoxRefuseInjectionTransaction() - { - } -} +//public class IFoxRefuseInjectionTransaction : Attribute +//{ +// /// +// /// 拒绝注入事务 +// /// +// public IFoxRefuseInjectionTransaction() +// { +// } +//} -public class AOP -{ - /// - /// 在此命名空间下的命令末尾注入清空事务栈函数 - /// - public static void Run(string nameSpace) - { - Dictionary cmdDic = new(); - AutoClass.AppDomainGetTypes(type => { - if (type.Namespace != nameSpace) - return; - //类上面特性 - if (type.IsClass) - { - var attr = type.GetCustomAttributes(true); - if (RefuseInjectionTransaction(attr)) - return; - } +//public class AOP +//{ +// /// +// /// 在此命名空间下的命令末尾注入清空事务栈函数 +// /// +// public static void Run(string nameSpace) +// { +// Dictionary cmdDic = new(); +// AutoClass.AppDomainGetTypes(type => { +// if (type.Namespace != nameSpace) +// return; +// //类上面特性 +// if (type.IsClass) +// { +// var attr = type.GetCustomAttributes(true); +// if (RefuseInjectionTransaction(attr)) +// return; +// } - //函数上面特性 - var mets = type.GetMethods();//获得它的成员函数 - for (int ii = 0; ii < mets.Length; ii++) - { - var method = mets[ii]; - //找到特性,特性下面的方法要是Public,否则就被编译器优化掉了. - var attr = method.GetCustomAttributes(true); - for (int jj = 0; jj < attr.Length; jj++) - if (attr[jj] is CommandMethodAttribute cmdAtt) - { - if (!RefuseInjectionTransaction(attr)) - cmdDic.Add(cmdAtt.GlobalName, (cmdAtt, type, method)); - } - } - }); +// //函数上面特性 +// var mets = type.GetMethods();//获得它的成员函数 +// for (int ii = 0; ii < mets.Length; ii++) +// { +// var method = mets[ii]; +// //找到特性,特性下面的方法要是Public,否则就被编译器优化掉了. +// var attr = method.GetCustomAttributes(true); +// for (int jj = 0; jj < attr.Length; jj++) +// if (attr[jj] is CommandMethodAttribute cmdAtt) +// { +// if (!RefuseInjectionTransaction(attr)) +// cmdDic.Add(cmdAtt.GlobalName, (cmdAtt, type, method)); +// } +// } +// }); - //运行的命令写在了Test.dll,当然不是ifox.cad类库内了.... - if (cmdDic.Count == 0) - return; +// //运行的命令写在了Test.dll,当然不是ifox.cad类库内了.... +// if (cmdDic.Count == 0) +// return; - var harmony = new Harmony(nameSpace); - var mPrefix = SymbolExtensions.GetMethodInfo(() => IFoxCmdAddFirst());//进入函数前 - var mPostfix = SymbolExtensions.GetMethodInfo(() => IFoxCmdAddLast());//进入函数后 - var mp1 = new HarmonyMethod(mPrefix); - var mp2 = new HarmonyMethod(mPostfix); +// var harmony = new Harmony(nameSpace); +// var mPrefix = SymbolExtensions.GetMethodInfo(() => IFoxCmdAddFirst());//进入函数前 +// var mPostfix = SymbolExtensions.GetMethodInfo(() => IFoxCmdAddLast());//进入函数后 +// var mp1 = new HarmonyMethod(mPrefix); +// var mp2 = new HarmonyMethod(mPostfix); - foreach (var item in cmdDic) - { - //原函数执行(空间type,函数名) - var mOriginal = AccessTools.Method(item.Value.MetType, item.Value.MetInfo.Name); - //mOriginal.Invoke(); - //新函数执行:创造两个函数加入里面 - var newMet = harmony.Patch(mOriginal, mp1, mp2); - //newMet.Invoke(); - } - } +// foreach (var item in cmdDic) +// { +// //原函数执行(空间type,函数名) +// var mOriginal = AccessTools.Method(item.Value.MetType, item.Value.MetInfo.Name); +// //mOriginal.Invoke(); +// //新函数执行:创造两个函数加入里面 +// var newMet = harmony.Patch(mOriginal, mp1, mp2); +// //newMet.Invoke(); +// } +// } - /// - /// 拒绝注入事务 - /// - /// 属性 - /// - private static bool RefuseInjectionTransaction(object[] attr) - { - bool refuseInjectionTransaction = false; - for (int kk = 0; kk < attr.Length; kk++) - { - if (attr[kk] is IFoxRefuseInjectionTransaction) - { - refuseInjectionTransaction = true; - break; - } - } - return refuseInjectionTransaction; - } +// /// +// /// 拒绝注入事务 +// /// +// /// 属性 +// /// +// private static bool RefuseInjectionTransaction(object[] attr) +// { +// bool refuseInjectionTransaction = false; +// for (int kk = 0; kk < attr.Length; kk++) +// { +// if (attr[kk] is IFoxRefuseInjectionTransaction) +// { +// refuseInjectionTransaction = true; +// break; +// } +// } +// return refuseInjectionTransaction; +// } - public static void IFoxCmdAddFirst() - { - //此生命周期会在静态事务栈上面,被无限延长 - var _ = DBTrans.Top; - } +// public static void IFoxCmdAddFirst() +// { +// //此生命周期会在静态事务栈上面,被无限延长 +// var _ = DBTrans.Top; +// } - public static void IFoxCmdAddLast() - { - DBTrans.FinishDatabase(); - } -} \ No newline at end of file +// public static void IFoxCmdAddLast() +// { +// DBTrans.FinishDatabase(); +// } +//} \ No newline at end of file diff --git a/src/IFoxCAD.Cad/Runtime/DBTrans.cs b/src/IFoxCAD.Cad/Runtime/DBTrans.cs index ff292e4..8e258c4 100644 --- a/src/IFoxCAD.Cad/Runtime/DBTrans.cs +++ b/src/IFoxCAD.Cad/Runtime/DBTrans.cs @@ -70,11 +70,11 @@ public static DBTrans Top /// 此方式代表了不允许跨事务循环命令 /// 若有则需在此命令进行 拒绝注入AOP特性 /// - internal static void FinishDatabase() - { - while (dBTrans.Count != 0) - dBTrans.Peek().Dispose(); - } + //internal static void FinishDatabase() + //{ + // while (dBTrans.Count != 0) + // dBTrans.Peek().Dispose(); + //} /// /// 文档 @@ -420,12 +420,16 @@ public static ObjectId TryGetObjectId(Database db, Handle handle) #endregion #region idispose接口相关函数 - + /// + /// 取消事务 + /// public void Abort() { Dispose(false); } - + /// + /// 提交事务 + /// public void Commit() { Dispose(true); diff --git a/tests/Test/testtr.cs b/tests/Test/testtr.cs index 615791b..cb05b55 100644 --- a/tests/Test/testtr.cs +++ b/tests/Test/testtr.cs @@ -18,7 +18,8 @@ public void Testifoxcommit() using var tr = new DBTrans(); tr.ModelSpace.AddCircle(new Point3d(0, 0, 0), 20); - tr.Commit(); + tr.Abort(); + //tr.Commit(); } // AOP 应用 预计示例: -- Gitee From babd84e77fd33f36c7c9e941db96e8aef2122358 Mon Sep 17 00:00:00 2001 From: vicwjb <403009819@qq.com> Date: Tue, 19 Apr 2022 23:02:30 +0800 Subject: [PATCH 169/675] =?UTF-8?q?=E5=8F=91=E5=B8=83v0.3.3.2=20nuget?= =?UTF-8?q?=E5=8C=85?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/IFoxCAD.Cad/IFoxCAD.Cad.csproj | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/IFoxCAD.Cad/IFoxCAD.Cad.csproj b/src/IFoxCAD.Cad/IFoxCAD.Cad.csproj index 408f463..f62a078 100644 --- a/src/IFoxCAD.Cad/IFoxCAD.Cad.csproj +++ b/src/IFoxCAD.Cad/IFoxCAD.Cad.csproj @@ -6,7 +6,7 @@ enable true true - 0.3.3.1 + 0.3.3.2 InspireFunction xsfhlzh;vicwjb 基于.NET的Cad二次开发类库 @@ -15,7 +15,7 @@ https://gitee.com/inspirefunction/ifoxcad.git git IFoxCAD;CAD;AutoCad;C#;NET - 增加数据结构-图及DFS. + 注释掉AOP. true true preview -- Gitee From 3bc39aabb18c8eeb9504da185a3914ae844d3fb5 Mon Sep 17 00:00:00 2001 From: vicwjb <403009819@qq.com> Date: Tue, 19 Apr 2022 23:14:11 +0800 Subject: [PATCH 170/675] =?UTF-8?q?=E5=A2=9E=E5=8A=A0getallcycle=E5=87=BD?= =?UTF-8?q?=E6=95=B0=E7=9A=84=E7=89=B9=E6=AE=8A=E6=83=85=E5=86=B5=E8=AF=B4?= =?UTF-8?q?=E6=98=8E?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/IFoxCAD.Cad/ExtensionMethod/CurveEx.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/IFoxCAD.Cad/ExtensionMethod/CurveEx.cs b/src/IFoxCAD.Cad/ExtensionMethod/CurveEx.cs index d053809..8058315 100644 --- a/src/IFoxCAD.Cad/ExtensionMethod/CurveEx.cs +++ b/src/IFoxCAD.Cad/ExtensionMethod/CurveEx.cs @@ -45,7 +45,7 @@ public static IEnumerable GetSplitCurves(this Curve curve, IEnumerable - /// 获取曲线集所围成的封闭区域的曲线集 + /// 获取曲线集所围成的封闭区域的曲线集,注意此函数不能处理平行边(两个点及两条线组成的闭合环) /// /// 曲线集合 /// 所有的闭合环的曲线集合 -- Gitee From 02dc050de6247e4837a71c399c4719108435b847 Mon Sep 17 00:00:00 2001 From: liuqihong <540762622@qq.com> Date: Wed, 20 Apr 2022 21:39:18 +0800 Subject: [PATCH 171/675] =?UTF-8?q?=E8=A1=A5=E5=85=85=E7=BC=96=E8=AF=91IFo?= =?UTF-8?q?x=E6=BA=90=E7=A0=81=E5=B7=A5=E7=A8=8B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/README.md b/README.md index 6a00531..c3b420a 100644 --- a/README.md +++ b/README.md @@ -11,14 +11,18 @@ #### 软件架构及相关说明 - [软件架构说明](/docs/关于IFoxCAD的架构说明.md) - - [扩展函数说明](/docs/关于扩展函数的说明.md) +#### 编译 IFox 源码工程 + +由于vs2022抛弃了某几个net版本,所以我们同时安装vs2019和vs2022,然后使用vs2022; +其中的原因是vs2019拥有全部net版本,而vs2022拥有最新的分析器和语法. + #### 安装教程 -1. vs新建net standard 类库 -2. 修改项目TargetFramework为net45,保存重加载项目 -3. 右键项目,管理nuget程序包,搜索ifoxcad,安装最新版就可以了 +1. 新建net standard 类库 +2. 修改项目`.csproj`的`TargetFrameworks`为net45,保存重加载项目,这里需要注意和cad版本对照. +3. 右键项目,管理nuget程序包,搜索ifoxcad,安装最新版就可以了. #### 使用说明 @@ -160,4 +164,5 @@ } //关闭事务自动处理读写模式 ``` -8. 未完待续。。。。 \ No newline at end of file +8. 未完待续。。。。 + -- Gitee From 9d8d7d31829a05343ff01953376c250c9c71f4f3 Mon Sep 17 00:00:00 2001 From: liuqihong <540762622@qq.com> Date: Wed, 20 Apr 2022 22:15:17 +0800 Subject: [PATCH 172/675] =?UTF-8?q?=E4=BF=AE=E6=94=B9=E8=87=AA=E5=8A=A8?= =?UTF-8?q?=E6=89=A7=E8=A1=8C=E9=94=99=E8=AF=AF?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 38 +++++++-------------------- src/IFoxCAD.Cad/Runtime/IAutoGo.cs | 42 ++++++++++++++++++------------ tests/Test/TestAOP.cs | 10 +++---- 3 files changed, 40 insertions(+), 50 deletions(-) diff --git a/README.md b/README.md index c3b420a..75095ef 100644 --- a/README.md +++ b/README.md @@ -94,49 +94,31 @@ 6. 天秀的自动加载与初始化 - 为了将程序集的初始化和通过写注册表的方式实现自动加载统一设置,减少每次重复的工作量,内裤提供了`AutoRegAssem`抽象类来完成此功能,只要在需要初始化的类继承`AutoRegAssem`类,然后实现`Initialize()` 和`Terminate()`两个函数就可以了。特别强调的是,一个程序集里只能有一个类继承,不管是不是同一个命名空间。 + 为了将程序集的初始化和通过写注册表的方式实现自动加载统一设置,减少每次重复的工作量,类裤提供了`AutoRegAssem`抽象类来完成此功能,只要在需要初始化的类继承`AutoRegAssem`类,然后实现`Initialize()` 和`Terminate()`两个函数就可以了。 + 特别强调的是,一个程序集里只能有一个类继承,不管是不是同一个命名空间。 + + 但是为了满足开闭原则,使用特性进行分段初始化是目前最佳选择 ```c# using Autodesk.AutoCAD.Runtime; using IFoxCAD.Cad; using System; using System.Reflection; - namespace CadLoad - { - public class CmdINI : AutoRegAssem - { - public override void Initialize() - { - //throw new NotImplementedException(); - //这里写用于程序初始化的代码 - } - - public override void Terminate() - { - //throw new NotImplementedException(); - //这里写程序卸载的代码,但是一般你也看不到运行,所以空着也行 - } - } - } - ``` - - 使用特性进行分段初始化 - ```c# // 必须实现一次这个接口 - public class CmdINI : AutoRegAssem + public class CmdINI : IExtensionApplication { - public override void Initialize() + public void Initialize() { new IFoxCAD.Cad.AutoClass().Initialize(); + //实例化了AutoClass之后会自动执行 IAutoGo 接口下面的类,以及自动执行 特性 [IFoxInitialize] + //而AutoRegAssem继承自IAutoGo,属于一个内部类了,用户可以不需要再处理此注册表部分. } - public override Sequence SequenceId() + public void Terminate() { - return Sequence.Last; + new IFoxCAD.Cad.AutoClass().Terminate(); } - - public override void Terminate() { } } //其他的类中的函数: diff --git a/src/IFoxCAD.Cad/Runtime/IAutoGo.cs b/src/IFoxCAD.Cad/Runtime/IAutoGo.cs index 7a28fee..6a45f47 100644 --- a/src/IFoxCAD.Cad/Runtime/IAutoGo.cs +++ b/src/IFoxCAD.Cad/Runtime/IAutoGo.cs @@ -1,6 +1,5 @@ -using System.Diagnostics; - namespace IFoxCAD.Cad; +using System.Diagnostics; [Flags] public enum Sequence : byte @@ -21,17 +20,17 @@ public interface IAutoGo public class IFoxInitialize : Attribute { - public bool IsInitialize { get; private set; } - public Sequence Sequence { get; private set; } + internal Sequence Sequence; + internal bool IsInitialize; /// /// 自己制作的一个特性,放在函数上面用来初始化或者结束回收 /// /// 优先级 - /// 用于初始化,用于结束回收 - public IFoxInitialize(Sequence sequence = Sequence.Last, bool initialize = true) + /// 用于初始化;用于结束回收 + public IFoxInitialize(Sequence sequence = Sequence.Last, bool isInitialize = true) { Sequence = sequence; - IsInitialize = initialize; + IsInitialize = isInitialize; } } @@ -73,6 +72,10 @@ public void Run() } } +/// +/// 此类初始化要在调用类库上面进行一次,否则反射的项目不包含调用类 +/// 也就是谁引用了 谁负责在 接口上实例化 +/// public class AutoClass //: IExtensionApplication { static List _InitializeList = new(); //储存方法用于初始化 @@ -85,10 +88,11 @@ public void Initialize() try { GetAttributeFunctions(); - GetInterfaceFunctions(_InitializeList, "Initialize"); + GetInterfaceFunctions(_InitializeList, nameof(Initialize)); //按照 SequenceId 排序_升序 _InitializeList = _InitializeList.OrderBy(runClass => runClass.SequenceId).ToList(); RunFunctions(_InitializeList); + _InitializeList.Clear(); } catch (System.Exception) { @@ -101,10 +105,11 @@ public void Terminate() { try { - GetInterfaceFunctions(_TerminateList, "Terminate"); + GetInterfaceFunctions(_TerminateList, nameof(Terminate)); //按照 SequenceId 排序_降序 _TerminateList = _TerminateList.OrderByDescending(runClass => runClass.SequenceId).ToList(); RunFunctions(_TerminateList); + _TerminateList.Clear(); } catch (System.Exception) { @@ -115,10 +120,12 @@ public void Terminate() /// /// 遍历程序域下所有类型 /// - /// + /// 输出每个成员 public static void AppDomainGetTypes(Action action) { - int error = 0; +#if DEBUG + int error = 0; +#endif try { var assemblies = AppDomain.CurrentDomain.GetAssemblies(); @@ -148,17 +155,20 @@ public static void AppDomainGetTypes(Action action) var type = types[jj]; if (type is not null) { - ++error; +#if DEBUG + ++error; +#endif action(type); } } } - } - catch (System.Exception) + catch (System.Exception e) { +#if DEBUG + Debug.WriteLine($"出错:{nameof(AppDomainGetTypes)};计数{error};错误信息:{e.Message}"); Debugger.Break(); - //Debug.WriteLine($"出错:AppDomainGetTypes;计数{error};错误信息:{e.Message}"); +#endif } } @@ -168,7 +178,7 @@ public static void AppDomainGetTypes(Action action) /// 储存要运行的方法 /// /// - void GetInterfaceFunctions(List runClassList, string methodName = "Initialize") + void GetInterfaceFunctions(List runClassList, string methodName = nameof(Initialize)) { string JoinBoxSequenceId = nameof(Sequence) + "Id"; AppDomainGetTypes(type => { diff --git a/tests/Test/TestAOP.cs b/tests/Test/TestAOP.cs index a38bcb5..66d7546 100644 --- a/tests/Test/TestAOP.cs +++ b/tests/Test/TestAOP.cs @@ -1,19 +1,17 @@ /* * 这里必须要实现一次这个接口,才能使用特性 */ -public class CmdINI : AutoRegAssem +public class CmdINI : IExtensionApplication { - public override void Initialize() + public void Initialize() { new IFoxCAD.Cad.AutoClass().Initialize(); } - public override Sequence SequenceId() + public void Terminate() { - return Sequence.Last; + new IFoxCAD.Cad.AutoClass().Terminate(); } - - public override void Terminate() { } } -- Gitee From cdf5f5e2f4ade22c644dd40aeedcb54123db7424 Mon Sep 17 00:00:00 2001 From: liuqihong <540762622@qq.com> Date: Wed, 20 Apr 2022 22:22:15 +0800 Subject: [PATCH 173/675] =?UTF-8?q?=E5=A4=84=E7=90=86=E6=8E=A5=E5=8F=A3?= =?UTF-8?q?=E5=90=8D=E7=A7=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 4 ++-- src/IFoxCAD.Cad/Runtime/AutoRegAssem.cs | 2 +- src/IFoxCAD.Cad/Runtime/IAutoGo.cs | 12 ++++++------ 3 files changed, 9 insertions(+), 9 deletions(-) diff --git a/README.md b/README.md index 75095ef..0ba7a01 100644 --- a/README.md +++ b/README.md @@ -111,8 +111,8 @@ public void Initialize() { new IFoxCAD.Cad.AutoClass().Initialize(); - //实例化了AutoClass之后会自动执行 IAutoGo 接口下面的类,以及自动执行 特性 [IFoxInitialize] - //而AutoRegAssem继承自IAutoGo,属于一个内部类了,用户可以不需要再处理此注册表部分. + //实例化了AutoClass之后会自动执行 IFoxAutoGo 接口下面的类,以及自动执行 特性 [IFoxInitialize] + //而AutoRegAssem继承自IFoxAutoGo,属于一个内部类了,用户可以不需要再处理此注册表部分. } public void Terminate() diff --git a/src/IFoxCAD.Cad/Runtime/AutoRegAssem.cs b/src/IFoxCAD.Cad/Runtime/AutoRegAssem.cs index b90cb23..67707e6 100644 --- a/src/IFoxCAD.Cad/Runtime/AutoRegAssem.cs +++ b/src/IFoxCAD.Cad/Runtime/AutoRegAssem.cs @@ -25,7 +25,7 @@ public enum AssemLoadType /// /// 自动加载程序集的抽象类,继承自 IExtensionApplication 接口 /// -public abstract class AutoRegAssem : IAutoGo +public abstract class AutoRegAssem : IFoxAutoGo { private AssemInfo _info = new(); diff --git a/src/IFoxCAD.Cad/Runtime/IAutoGo.cs b/src/IFoxCAD.Cad/Runtime/IAutoGo.cs index 6a45f47..7fc832b 100644 --- a/src/IFoxCAD.Cad/Runtime/IAutoGo.cs +++ b/src/IFoxCAD.Cad/Runtime/IAutoGo.cs @@ -8,7 +8,7 @@ public enum Sequence : byte Last, // 最后 } -public interface IAutoGo +public interface IFoxAutoGo { // 控制加载顺序 Sequence SequenceId(); @@ -23,7 +23,7 @@ public class IFoxInitialize : Attribute internal Sequence Sequence; internal bool IsInitialize; /// - /// 自己制作的一个特性,放在函数上面用来初始化或者结束回收 + /// 用于初始化和结束回收 /// /// 优先级 /// 用于初始化;用于结束回收 @@ -80,7 +80,6 @@ public class AutoClass //: IExtensionApplication { static List _InitializeList = new(); //储存方法用于初始化 static List _TerminateList = new(); //储存方法用于结束释放 - const string _iAutoGo = "IAutoGo"; //打开cad的时候会自动执行 public void Initialize() @@ -124,7 +123,7 @@ public void Terminate() public static void AppDomainGetTypes(Action action) { #if DEBUG - int error = 0; + int error = 0; #endif try { @@ -156,7 +155,7 @@ public static void AppDomainGetTypes(Action action) if (type is not null) { #if DEBUG - ++error; + ++error; #endif action(type); } @@ -186,7 +185,8 @@ void GetInterfaceFunctions(List runClassList, string methodName = name var inters = type.GetInterfaces(); for (int ii = 0; ii < inters.Length; ii++) { - if (inters[ii].Name == _iAutoGo)//找到接口的函数 + //找到接口名称:是否找nameof(IExtensionApplication),避免重复执行,不找 + if (inters[ii].Name == nameof(IFoxAutoGo)) { Sequence? sequence = null; MethodInfo? initialize = null; -- Gitee From cc8b06de1012f22be167e15daa5d4b5354bfd316 Mon Sep 17 00:00:00 2001 From: liuqihong <540762622@qq.com> Date: Wed, 20 Apr 2022 23:31:34 +0800 Subject: [PATCH 174/675] =?UTF-8?q?=E6=B3=A8=E5=86=8C=E8=A1=A8=E5=8A=A0?= =?UTF-8?q?=E8=BD=BD=E6=94=BE=E5=88=B0=E6=94=B9=E4=B8=BA=E7=B1=BB=E6=94=BE?= =?UTF-8?q?=E8=87=AA=E5=8A=A8=E6=89=A7=E8=A1=8C=E6=8E=A5=E5=8F=A3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/IFoxCAD.Cad/Runtime/AutoRegAssem.cs | 29 +++------- src/IFoxCAD.Cad/Runtime/IAutoGo.cs | 62 ++++++++++++---------- src/IFoxCAD.Cad/Runtime/MethodHelper.cs | 13 ++++- tests/Test/CmdINI.cs | 70 +++++++++++++++++++++++++ tests/Test/Test.cs | 13 ----- tests/Test/TestAOP.cs | 19 +------ 6 files changed, 123 insertions(+), 83 deletions(-) create mode 100644 tests/Test/CmdINI.cs diff --git a/src/IFoxCAD.Cad/Runtime/AutoRegAssem.cs b/src/IFoxCAD.Cad/Runtime/AutoRegAssem.cs index 67707e6..d88b6b2 100644 --- a/src/IFoxCAD.Cad/Runtime/AutoRegAssem.cs +++ b/src/IFoxCAD.Cad/Runtime/AutoRegAssem.cs @@ -1,6 +1,7 @@ namespace IFoxCAD.Cad; using Registry = Microsoft.Win32.Registry; using RegistryKey = Microsoft.Win32.RegistryKey; + /// /// 程序集加载类型 /// @@ -23,9 +24,9 @@ public enum AssemLoadType } /// -/// 自动加载程序集的抽象类,继承自 IExtensionApplication 接口 +/// 初始化程序集信息,并写入注册表 /// -public abstract class AutoRegAssem : IFoxAutoGo +public class AutoRegAssem { private AssemInfo _info = new(); @@ -47,14 +48,13 @@ public abstract class AutoRegAssem : IFoxAutoGo public static DirectoryInfo GetDirectory(Assembly assem) { if (assem is null) - { throw new(nameof(assem)); - } + return new FileInfo(assem.Location).Directory; } /// - /// 初始化程序集信息 + /// 初始化程序集信息,并写入注册表 /// public AutoRegAssem() { @@ -72,7 +72,7 @@ public AutoRegAssem() private static RegistryKey GetAcAppKey() { -#if ac2009 +#if NET35 string key = HostApplicationServices.Current.RegistryProductRootKey; #else string key = HostApplicationServices.Current.MachineRegistryProductRootKey; @@ -110,22 +110,5 @@ public void RegApp() rk.SetValue("MANAGED", 1, RegistryValueKind.DWord); appkey.Close(); } - #endregion RegApp - - #region IExtensionApplication 成员 - - /// - /// 初始化函数 - /// - public abstract void Initialize(); - - /// - /// 结束函数 - /// - public abstract void Terminate(); - - public abstract Sequence SequenceId(); - - #endregion IExtensionApplication 成员 } diff --git a/src/IFoxCAD.Cad/Runtime/IAutoGo.cs b/src/IFoxCAD.Cad/Runtime/IAutoGo.cs index 7fc832b..458e4f5 100644 --- a/src/IFoxCAD.Cad/Runtime/IAutoGo.cs +++ b/src/IFoxCAD.Cad/Runtime/IAutoGo.cs @@ -1,6 +1,9 @@ namespace IFoxCAD.Cad; using System.Diagnostics; +/// +/// 加载时优先级 +/// [Flags] public enum Sequence : byte { @@ -8,6 +11,9 @@ public enum Sequence : byte Last, // 最后 } +/// +/// 加载时自动执行接口 +/// public interface IFoxAutoGo { // 控制加载顺序 @@ -18,9 +24,18 @@ public interface IFoxAutoGo void Initialize(); } +/// +/// 加载时自动执行特性 +/// public class IFoxInitialize : Attribute { - internal Sequence Sequence; + /// + /// 优先级 + /// + internal Sequence SequenceId; + /// + /// 用于初始化;用于结束回收 + /// internal bool IsInitialize; /// /// 用于初始化和结束回收 @@ -29,31 +44,22 @@ public class IFoxInitialize : Attribute /// 用于初始化;用于结束回收 public IFoxInitialize(Sequence sequence = Sequence.Last, bool isInitialize = true) { - Sequence = sequence; + SequenceId = sequence; IsInitialize = isInitialize; } } //为了解决IExtensionApplication在一个dll内无法多次实现接口的关系 -//所以在这里反射加载所有的IAutoGo,以达到能分开写"启动运行"函数的目的 -public class RunClass +//所以在这里反射加载所有的 IAutoGo ,以达到能分开写"启动运行"函数的目的 +class RunClass { - public Sequence SequenceId { get; } + public Sequence Sequence { get; } MethodInfo _methodInfo; - object? _instance; public RunClass(MethodInfo method, Sequence sequence) { _methodInfo = method; - SequenceId = sequence; - - var reftype = _methodInfo.ReflectedType; - if (reftype == null) return; - var fullName = reftype.FullName; //命名空间+类 - if (fullName == null) return; - var type = reftype.Assembly.GetType(fullName);//通过程序集反射创建类+ - if (type == null) return; - _instance = Activator.CreateInstance(type); + Sequence = sequence; } /// @@ -63,7 +69,7 @@ public void Run() { try { - _methodInfo.Invoke(_instance); + _methodInfo.Invoke(); } catch (System.Exception) { @@ -89,7 +95,7 @@ public void Initialize() GetAttributeFunctions(); GetInterfaceFunctions(_InitializeList, nameof(Initialize)); //按照 SequenceId 排序_升序 - _InitializeList = _InitializeList.OrderBy(runClass => runClass.SequenceId).ToList(); + _InitializeList = _InitializeList.OrderBy(runClass => runClass.Sequence).ToList(); RunFunctions(_InitializeList); _InitializeList.Clear(); } @@ -106,7 +112,7 @@ public void Terminate() { GetInterfaceFunctions(_TerminateList, nameof(Terminate)); //按照 SequenceId 排序_降序 - _TerminateList = _TerminateList.OrderByDescending(runClass => runClass.SequenceId).ToList(); + _TerminateList = _TerminateList.OrderByDescending(runClass => runClass.Sequence).ToList(); RunFunctions(_TerminateList); _TerminateList.Clear(); } @@ -175,12 +181,14 @@ public static void AppDomainGetTypes(Action action) /// 收集接口下的函数 /// /// 储存要运行的方法 - /// + /// 查找方法名 /// - void GetInterfaceFunctions(List runClassList, string methodName = nameof(Initialize)) + void GetInterfaceFunctions(List runClassList, string methodName) { - string JoinBoxSequenceId = nameof(Sequence) + "Id"; + const string sqid = nameof(Sequence) + "Id"; AppDomainGetTypes(type => { + if (type.IsAbstract) + return; //获取接口集合 var inters = type.GetInterfaces(); for (int ii = 0; ii < inters.Length; ii++) @@ -198,7 +206,7 @@ void GetInterfaceFunctions(List runClassList, string methodName = name var method = mets[jj]; if (method.IsAbstract) continue; - if (method.Name == JoinBoxSequenceId) + if (method.Name == sqid) { var obj = method.Invoke(); if (obj != null) @@ -233,6 +241,8 @@ void GetInterfaceFunctions(List runClassList, string methodName = name void GetAttributeFunctions() { AppDomainGetTypes(type => { + if (type.IsAbstract) + return; var mets = type.GetMethods();//获得它的成员函数 for (int ii = 0; ii < mets.Length; ii++) { @@ -244,7 +254,7 @@ void GetAttributeFunctions() { if (attr[jj] is IFoxInitialize jjAtt) { - var runc = new RunClass(method, jjAtt.Sequence); + var runc = new RunClass(method, jjAtt.SequenceId); if (jjAtt.IsInitialize) _InitializeList.Add(runc); else @@ -261,10 +271,8 @@ void GetAttributeFunctions() /// void RunFunctions(List runClassList) { - for (int i = runClassList.Count - 1; i >= 0; i--) - { + for (int i = 0; i < runClassList.Count; i++) runClassList[i].Run(); - runClassList.RemoveAt(i); - } + runClassList.Clear(); } } \ No newline at end of file diff --git a/src/IFoxCAD.Cad/Runtime/MethodHelper.cs b/src/IFoxCAD.Cad/Runtime/MethodHelper.cs index 848c5d5..543f428 100644 --- a/src/IFoxCAD.Cad/Runtime/MethodHelper.cs +++ b/src/IFoxCAD.Cad/Runtime/MethodHelper.cs @@ -2,6 +2,8 @@ public static class MethodInfoHelper { + static Dictionary methodDic = new(); + /// /// 执行函数 /// @@ -12,7 +14,7 @@ public static class MethodInfoHelper if (methodInfo == null) throw new ArgumentNullException(nameof(methodInfo)); - object? result; + object? result = null; if (methodInfo.IsStatic) { //新函数指针进入此处 @@ -27,6 +29,10 @@ public static class MethodInfoHelper else { //原命令的函数指针进入此处 + //object instance; + if (methodDic.ContainsKey(methodInfo)) + instance = methodDic[methodInfo]; + if (instance == null) { var reftype = methodInfo.ReflectedType; @@ -36,8 +42,11 @@ public static class MethodInfoHelper var type = reftype.Assembly.GetType(fullName);//通过程序集反射创建类+ if (type == null) return null; instance = Activator.CreateInstance(type); + if (!type.IsAbstract)//无法创建抽象类成员 + methodDic.Add(methodInfo, instance); } - result = methodInfo.Invoke(instance, null); //非静态,调用实例化方法 + if (instance != null) + result = methodInfo.Invoke(instance, null); //非静态,调用实例化方法 } return result; } diff --git a/tests/Test/CmdINI.cs b/tests/Test/CmdINI.cs new file mode 100644 index 0000000..56e9756 --- /dev/null +++ b/tests/Test/CmdINI.cs @@ -0,0 +1,70 @@ +/* + * 自动执行接口 + * 这里必须要实现一次这个接口,才能使用特性 + */ +public class CmdINI : IExtensionApplication +{ + public void Initialize() + { + new IFoxCAD.Cad.AutoRegAssem(); + new IFoxCAD.Cad.AutoClass().Initialize(); + //实例化了 AutoClass 之后会自动执行 IFoxAutoGo 接口下面的类,以及自动执行 特性 [IFoxInitialize] + } + + public void Terminate() + { + new IFoxCAD.Cad.AutoClass().Terminate(); + } +} + +/* + * 自动执行特性例子: + */ +public class Cmd_IFoxInitialize +{ + [IFoxInitialize] + public void NameCasual() + { + var dm = Application.DocumentManager; + var doc = dm.MdiActiveDocument; + var ed = doc.Editor; + ed.WriteMessage("\n 开始自动执行 可以分开多个类和多个函数 \r\n"); + } + + [IFoxInitialize] + public void Initialize() + { + var dm = Application.DocumentManager; + var doc = dm.MdiActiveDocument; + var ed = doc.Editor; + ed.WriteMessage("\n 开始自动执行 Initialize \r\n"); + } + + [IFoxInitialize(isInitialize: false)] + public void Terminate() + { + //try + //{ + // var dm = Application.DocumentManager; + // var doc = dm.MdiActiveDocument; + // var ed = doc.Editor; //注意此时编辑器已经回收,所以此句没用,并引发错误 + // ed.WriteMessage("\n 结束自动执行 Terminate \r\n"); + //} + //catch (System.Exception) + //{ + //} + } + + //[IFoxInitialize] + //public void Initialize() + //{ + // //文档管理器将比此接口前创建,因此此句会执行 + // Application.DocumentManager.MdiActiveDocument.Editor.WriteMessage("\nload...."); + //} + //[IFoxInitialize(Sequence.First, false)] + //public void Terminate() + //{ + // //文档管理器将比此接口前死亡,因此此句不会执行 + // Application.DocumentManager.MdiActiveDocument?.Editor.WriteMessage("\nunload...."); + //} +} \ No newline at end of file diff --git a/tests/Test/Test.cs b/tests/Test/Test.cs index ba649eb..1a2076a 100644 --- a/tests/Test/Test.cs +++ b/tests/Test/Test.cs @@ -395,19 +395,6 @@ public Document Getdoc() var doc = Application.DocumentManager.MdiActiveDocument; return doc; } - - [IFoxInitialize] - public void Initialize() - { - //文档管理器将比此接口前创建,因此此句会执行 - Application.DocumentManager.MdiActiveDocument.Editor.WriteMessage("\nload...."); - } - [IFoxInitialize(Sequence.First, false)] - public void Terminate() - { - //文档管理器将比此接口前死亡,因此此句不会执行 - Application.DocumentManager.MdiActiveDocument?.Editor.WriteMessage("\nunload...."); - } } diff --git a/tests/Test/TestAOP.cs b/tests/Test/TestAOP.cs index 66d7546..b5b5c92 100644 --- a/tests/Test/TestAOP.cs +++ b/tests/Test/TestAOP.cs @@ -1,21 +1,4 @@ -/* -* 这里必须要实现一次这个接口,才能使用特性 -*/ -public class CmdINI : IExtensionApplication -{ - public void Initialize() - { - new IFoxCAD.Cad.AutoClass().Initialize(); - } - - public void Terminate() - { - new IFoxCAD.Cad.AutoClass().Terminate(); - } -} - - -//被注入的函数将不能使用断点, +//被注入的函数将不能使用断点, //因此用户要充分了解才能使用 #if false /* -- Gitee From 4b5651c25b631527a7ff4a16f873d647c8dbf8c4 Mon Sep 17 00:00:00 2001 From: liuqihong <540762622@qq.com> Date: Thu, 21 Apr 2022 00:01:25 +0800 Subject: [PATCH 175/675] =?UTF-8?q?=E8=BF=87=E6=BB=A4=E5=8F=8D=E5=B0=84?= =?UTF-8?q?=E6=88=90=E5=91=98=E4=BB=A5=E5=85=8D=E5=A4=AA=E6=85=A2?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/IFoxCAD.Cad/Runtime/AutoRegAssem.cs | 27 +++++++++++---------- src/IFoxCAD.Cad/Runtime/IAutoGo.cs | 32 ++++++++++++++++++------- tests/Test/CmdINI.cs | 10 ++++---- 3 files changed, 44 insertions(+), 25 deletions(-) diff --git a/src/IFoxCAD.Cad/Runtime/AutoRegAssem.cs b/src/IFoxCAD.Cad/Runtime/AutoRegAssem.cs index d88b6b2..f156555 100644 --- a/src/IFoxCAD.Cad/Runtime/AutoRegAssem.cs +++ b/src/IFoxCAD.Cad/Runtime/AutoRegAssem.cs @@ -28,7 +28,7 @@ public enum AssemLoadType /// public class AutoRegAssem { - private AssemInfo _info = new(); + public AssemInfo Info; /// /// 程序集的路径 @@ -59,10 +59,11 @@ public static DirectoryInfo GetDirectory(Assembly assem) public AutoRegAssem() { var assem = Assembly.GetCallingAssembly(); - _info.Loader = assem.Location; - _info.Fullname = assem.FullName; - _info.Name = assem.GetName().Name; - _info.LoadType = AssemLoadType.Startting; + Info = new(); + Info.Loader = assem.Location; + Info.Fullname = assem.FullName; + Info.Name = assem.GetName().Name; + Info.LoadType = AssemLoadType.Startting; if (!SearchForReg()) RegApp(); @@ -88,11 +89,11 @@ private bool SearchForReg() return false; var regApps = appkey.GetSubKeyNames(); - if (regApps.Contains(_info.Name)) + if (regApps.Contains(Info.Name)) { //20220409 bug:文件名相同,路径不同,需要判断路径 - var info = appkey.OpenSubKey(_info.Name); - return info.GetValue("LOADER")?.ToString().ToLower() == _info.Loader.ToLower(); + var info = appkey.OpenSubKey(Info.Name); + return info.GetValue("LOADER")?.ToString().ToLower() == Info.Loader.ToLower(); } return false; } @@ -100,13 +101,13 @@ private bool SearchForReg() /// /// 在注册表写入自动加载的程序集信息 /// - public void RegApp() + void RegApp() { var appkey = GetAcAppKey(); - var rk = appkey.CreateSubKey(_info.Name); - rk.SetValue("DESCRIPTION", _info.Fullname, RegistryValueKind.String); - rk.SetValue("LOADCTRLS", _info.LoadType, RegistryValueKind.DWord); - rk.SetValue("LOADER", _info.Loader, RegistryValueKind.String); + var rk = appkey.CreateSubKey(Info.Name); + rk.SetValue("DESCRIPTION", Info.Fullname, RegistryValueKind.String); + rk.SetValue("LOADCTRLS", Info.LoadType, RegistryValueKind.DWord); + rk.SetValue("LOADER", Info.Loader, RegistryValueKind.String); rk.SetValue("MANAGED", 1, RegistryValueKind.DWord); appkey.Close(); } diff --git a/src/IFoxCAD.Cad/Runtime/IAutoGo.cs b/src/IFoxCAD.Cad/Runtime/IAutoGo.cs index 458e4f5..1ab34fc 100644 --- a/src/IFoxCAD.Cad/Runtime/IAutoGo.cs +++ b/src/IFoxCAD.Cad/Runtime/IAutoGo.cs @@ -87,6 +87,16 @@ public class AutoClass //: IExtensionApplication static List _InitializeList = new(); //储存方法用于初始化 static List _TerminateList = new(); //储存方法用于结束释放 + string _DllName; + /// + /// 反射此特性:进行加载时自动运行 + /// + /// 约束在此dll进行加速 + public AutoClass(string DllName) + { + _DllName = DllName; + } + //打开cad的时候会自动执行 public void Initialize() { @@ -125,8 +135,9 @@ public void Terminate() /// /// 遍历程序域下所有类型 /// - /// 输出每个成员 - public static void AppDomainGetTypes(Action action) + /// 输出每个成员执行 + /// 过滤此dll,不含后缀 + public static void AppDomainGetTypes(Action action, string? dllNameWithoutExtension = null) { #if DEBUG int error = 0; @@ -144,15 +155,20 @@ public static void AppDomainGetTypes(Action action) { var assembly = assemblies[ii]; Type[]? types = null; + + //获取类型集合,反射时候还依赖其他的dll就会这个错误 + //此通讯库要跳过,否则会报错. + if (dllNameWithoutExtension != null && + Path.GetFileNameWithoutExtension(assembly.Location) != dllNameWithoutExtension) + continue; + if (Path.GetFileName(assembly.Location) == "AcInfoCenterConn.dll") + continue; try { - //获取类型集合,反射时候还依赖其他的dll就会这个错误 - //此通讯库要跳过,否则会报错. - if (Path.GetFileName(assembly.Location) == "AcInfoCenterConn.dll") - continue; types = assembly.GetTypes(); } catch (ReflectionTypeLoadException) { continue; } + if (types is null) continue; for (int jj = 0; jj < types.Length; jj++) @@ -232,7 +248,7 @@ void GetInterfaceFunctions(List runClassList, string methodName) break; } } - }); + },_DllName); } /// @@ -263,7 +279,7 @@ void GetAttributeFunctions() } } } - }); + }, _DllName); } /// diff --git a/tests/Test/CmdINI.cs b/tests/Test/CmdINI.cs index 56e9756..c2be8fc 100644 --- a/tests/Test/CmdINI.cs +++ b/tests/Test/CmdINI.cs @@ -4,19 +4,21 @@ */ public class CmdINI : IExtensionApplication { + AutoClass ac; public void Initialize() { - new IFoxCAD.Cad.AutoRegAssem(); - new IFoxCAD.Cad.AutoClass().Initialize(); + var ara = new IFoxCAD.Cad.AutoRegAssem(); + ac = new AutoClass(ara.Info.Name); + ac.Initialize(); //实例化了 AutoClass 之后会自动执行 IFoxAutoGo 接口下面的类,以及自动执行 特性 [IFoxInitialize] } public void Terminate() { - new IFoxCAD.Cad.AutoClass().Terminate(); + ac.Terminate(); } } - + /* * 自动执行特性例子: */ -- Gitee From 20a8edc078533f2ba350a46e8d7b4c5d7acc059c Mon Sep 17 00:00:00 2001 From: liuqihong <540762622@qq.com> Date: Thu, 21 Apr 2022 00:04:27 +0800 Subject: [PATCH 176/675] =?UTF-8?q?=E4=BF=AE=E6=94=B9=E8=AF=B4=E6=98=8E?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 22 +++++++++++++++++----- tests/Test/CmdINI.cs | 8 +++++--- 2 files changed, 22 insertions(+), 8 deletions(-) diff --git a/README.md b/README.md index 0ba7a01..65cefed 100644 --- a/README.md +++ b/README.md @@ -105,19 +105,26 @@ using System; using System.Reflection; - // 必须实现一次这个接口 + /* + * 自动执行接口 + * 这里必须要实现一次这个接口,才能使用特性 + */ public class CmdINI : IExtensionApplication { + AutoClass ac; public void Initialize() { - new IFoxCAD.Cad.AutoClass().Initialize(); - //实例化了AutoClass之后会自动执行 IFoxAutoGo 接口下面的类,以及自动执行 特性 [IFoxInitialize] - //而AutoRegAssem继承自IFoxAutoGo,属于一个内部类了,用户可以不需要再处理此注册表部分. + var ara = new AutoRegAssem(); + ac = new AutoClass(ara.Info.Name); + ac.Initialize(); + //实例化了 AutoClass 之后会自动执行 IFoxAutoGo 接口下面的类, + //以及自动执行特性 [IFoxInitialize] + //类库用户不在此处进行其他代码,而是实现特性 } public void Terminate() { - new IFoxCAD.Cad.AutoClass().Terminate(); + ac.Terminate(); } } @@ -130,6 +137,11 @@ { //TODO 你想在加载dll之后自动执行的函数 } + [IFoxInitialize(isInitialize: false)] + public void Terminate() + { + //TODO 你想在关闭cad时自动执行的函数 + } } ``` diff --git a/tests/Test/CmdINI.cs b/tests/Test/CmdINI.cs index c2be8fc..39eb4ab 100644 --- a/tests/Test/CmdINI.cs +++ b/tests/Test/CmdINI.cs @@ -7,15 +7,17 @@ public class CmdINI : IExtensionApplication AutoClass ac; public void Initialize() { - var ara = new IFoxCAD.Cad.AutoRegAssem(); + var ara = new AutoRegAssem(); ac = new AutoClass(ara.Info.Name); ac.Initialize(); - //实例化了 AutoClass 之后会自动执行 IFoxAutoGo 接口下面的类,以及自动执行 特性 [IFoxInitialize] + //实例化了 AutoClass 之后会自动执行 IFoxAutoGo 接口下面的类, + //以及自动执行特性 [IFoxInitialize] + //类库用户不在此处进行其他代码,而是实现特性 } public void Terminate() { - ac.Terminate(); + ac.Terminate(); } } -- Gitee From b2500dc6c4060daee8d35d4b4eb014e3aa7af2b8 Mon Sep 17 00:00:00 2001 From: liuqihong <540762622@qq.com> Date: Wed, 20 Apr 2022 16:26:41 +0000 Subject: [PATCH 177/675] =?UTF-8?q?=E6=94=B9=E5=8F=98=E5=8F=98=E9=87=8F?= =?UTF-8?q?=E4=BD=8D=E7=BD=AE?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/IFoxCAD.Cad/Runtime/IAutoGo.cs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/IFoxCAD.Cad/Runtime/IAutoGo.cs b/src/IFoxCAD.Cad/Runtime/IAutoGo.cs index 1ab34fc..9dca603 100644 --- a/src/IFoxCAD.Cad/Runtime/IAutoGo.cs +++ b/src/IFoxCAD.Cad/Runtime/IAutoGo.cs @@ -154,7 +154,6 @@ public static void AppDomainGetTypes(Action action, string? dllNameWithout for (int ii = 0; ii < assemblies.Length; ii++) { var assembly = assemblies[ii]; - Type[]? types = null; //获取类型集合,反射时候还依赖其他的dll就会这个错误 //此通讯库要跳过,否则会报错. @@ -163,6 +162,8 @@ public static void AppDomainGetTypes(Action action, string? dllNameWithout continue; if (Path.GetFileName(assembly.Location) == "AcInfoCenterConn.dll") continue; + + Type[]? types = null; try { types = assembly.GetTypes(); -- Gitee From e6431eef4a210e56e05414599f4489ce493202c6 Mon Sep 17 00:00:00 2001 From: liuqihong <540762622@qq.com> Date: Wed, 20 Apr 2022 16:30:16 +0000 Subject: [PATCH 178/675] =?UTF-8?q?=E4=BF=AE=E6=94=B9=E5=AD=97=E7=AC=A6?= =?UTF-8?q?=E4=B8=B2?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/IFoxCAD.Cad/Runtime/IAutoGo.cs | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/IFoxCAD.Cad/Runtime/IAutoGo.cs b/src/IFoxCAD.Cad/Runtime/IAutoGo.cs index 9dca603..9dbc7c4 100644 --- a/src/IFoxCAD.Cad/Runtime/IAutoGo.cs +++ b/src/IFoxCAD.Cad/Runtime/IAutoGo.cs @@ -157,10 +157,11 @@ public static void AppDomainGetTypes(Action action, string? dllNameWithout //获取类型集合,反射时候还依赖其他的dll就会这个错误 //此通讯库要跳过,否则会报错. + var str = Path.GetFileNameWithoutExtension(assembly.Location); if (dllNameWithoutExtension != null && - Path.GetFileNameWithoutExtension(assembly.Location) != dllNameWithoutExtension) + str != dllNameWithoutExtension) continue; - if (Path.GetFileName(assembly.Location) == "AcInfoCenterConn.dll") + if (str == "AcInfoCenterConn") continue; Type[]? types = null; -- Gitee From e01ecbfc67082d3fcb4b036c853a31d6c7899249 Mon Sep 17 00:00:00 2001 From: vicwjb <403009819@qq.com> Date: Thu, 21 Apr 2022 22:16:35 +0800 Subject: [PATCH 179/675] =?UTF-8?q?=E4=BC=98=E5=8C=96=E5=9B=BE=E9=80=9F?= =?UTF-8?q?=E5=BA=A6=EF=BC=8C=E6=94=B9=E4=B8=BAhash=E7=B4=A2=E5=BC=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/IFoxCAD.Cad/Algorithms/Graph.cs | 58 ++++++++++++++--------------- tests/Test/TestCurve.cs | 51 ++++--------------------- 2 files changed, 36 insertions(+), 73 deletions(-) diff --git a/src/IFoxCAD.Cad/Algorithms/Graph.cs b/src/IFoxCAD.Cad/Algorithms/Graph.cs index e419f19..865e9e8 100644 --- a/src/IFoxCAD.Cad/Algorithms/Graph.cs +++ b/src/IFoxCAD.Cad/Algorithms/Graph.cs @@ -17,6 +17,11 @@ internal sealed class Graph : IGraph, IEnumerable /// 邻接边表,key为顶点的类型,value为邻接边表,类型是hashset,不可重复添加边 /// private readonly Dictionary> edges = new(); + /// + /// 为加快索引,引入hash检索 + /// + private readonly Dictionary vertexs = new(); + public int VerticesCount => vertices.Count; /// /// 目前点增加点的顺序号,这个点号不随删点而减少的 @@ -29,24 +34,10 @@ public Graph() { insertCount = 0; // 每次新建对象就将顶点顺序号归零 } - /// - /// 邻接边里是否存在点 - /// - /// 点 - /// 存在就返回找到的顶点,反之返回 - /// 存在点就返回 , 反之返回 - private bool ContainKeyData(Point3d pt, out IGraphVertex? currentKey) + + private static string GetHashString(Point3d pt) { - foreach (var key in vertices.Keys) - { - if (key.Data.Equals(pt)) - { - currentKey = key; - return true; - } - } - currentKey = null; - return false; + return $"{pt.X:n6}{pt.Y:n6}{pt.Z:n6}"; } #endregion @@ -58,13 +49,19 @@ private bool ContainKeyData(Point3d pt, out IGraphVertex? currentKey) /// 创建的顶点 public IGraphVertex AddVertex(Point3d pt) { - if (ContainKeyData(pt, out IGraphVertex? key)) + + var str = Graph.GetHashString(pt); + if (vertexs.ContainsKey(str)) { - return key!; + return vertexs[str]; } + var vertex = new GraphVertex(pt, insertCount++); vertices.Add(vertex, new HashSet()); edges.Add(vertex, new HashSet()); + + vertexs[str] = vertex; + return vertex; } @@ -89,17 +86,19 @@ public void AddEdge(Curve3d curve!!) vertices[end].Add(start); edges[end].Add(new GraphEdge(start, curtmp)); } - #endregion +#endregion - #region 删除顶点及边 +#region 删除顶点及边 /// /// 从此图中删除现有顶点。 /// /// 点 public void RemoveVertex(Point3d pt) { - if (ContainKeyData(pt, out IGraphVertex? vertex)) + var str = Graph.GetHashString(pt); + if (vertexs.ContainsKey(str)) { + var vertex = vertexs[str]; // 删除邻接表里的vertex点,先删除后面的遍历可以少一轮 vertices.Remove(vertex!); // 删除其他顶点的邻接表里的vertex点 @@ -121,6 +120,7 @@ public void RemoveVertex(Point3d pt) } } } + vertexs.Remove(str); } } @@ -133,9 +133,9 @@ public void RemoveEdge(Curve3d curve!!) RemoveVertex(curve.StartPoint); RemoveVertex(curve.EndPoint); } - #endregion +#endregion - #region 是否存在及获取顶点和边 +#region 是否存在及获取顶点和边 /// /// 我们在给定的来源和目的地之间是否有边? /// @@ -190,9 +190,9 @@ public bool ContainsVertex(IGraphVertex value) { return vertices.ContainsKey(value); } - #endregion +#endregion - #region 获取邻接表和曲线 +#region 获取邻接表和曲线 /// /// 获取顶点的邻接表 /// @@ -238,9 +238,9 @@ public List GetCurves(List graphVertices) } return curves; } - #endregion +#endregion - #region 克隆及接口实现 +#region 克隆及接口实现 /// /// 克隆此图。目测是深克隆 /// @@ -272,7 +272,7 @@ public IEnumerator GetEnumerator() public IEnumerable VerticesAsEnumberable => vertices.Select(x => x.Key); - #endregion +#endregion /// /// 输出点的邻接表的可读字符串 diff --git a/tests/Test/TestCurve.cs b/tests/Test/TestCurve.cs index 7633187..0e59674 100644 --- a/tests/Test/TestCurve.cs +++ b/tests/Test/TestCurve.cs @@ -1,4 +1,6 @@ -namespace Test +using System.Security.Policy; + +namespace Test { public class TestGraph { @@ -26,56 +28,17 @@ public void TestGraph1() if (ents == null) return; var res = ents.GetAllCycle(); - //// 新建图 - //var graph = new IFoxCAD.Cad.Graph(); - //// 将曲线加入到图中 - //ents.ForEach(ent => graph.AddEdge(ent.GetGeCurve())); - - //Env.Print(graph.ToReadable()); - ////新建 dfs - //var dfs = new IFoxCAD.Cad.DepthFirst(); - //// 查询全部的 闭合环 - //dfs.FindAll(graph); - //// 遍历闭合环的列表,将每个闭合环转换为实体曲线 - //var res = new List(); - //foreach (var item in dfs.Curve3ds) - //{ - // var curves = graph.GetCurves(item).ToArray(); - // var comcur = new CompositeCurve3d(curves); - // var tmp = comcur.ToCurve(); - // if (tmp is not null) - // { - // res.Add(tmp); - // } - //} + res.ForEach((i, t) => t.ForWrite(e => e.ColorIndex = i + 1)); using var tr = new DBTrans(); tr.CurrentSpace.AddEntity(res); - //var graph = new Graph(); - //graph.AddVertex(1); - //graph.AddVertex(2); - //graph.AddVertex(3); - //graph.AddVertex(4); - //graph.AddVertex(5); - //graph.AddVertex(6); - //graph.AddVertex(7); - - - //graph.AddEdge(1, 2); - //graph.AddEdge(2, 3); - //graph.AddEdge(3, 4); - //graph.AddEdge(4, 5); - //graph.AddEdge(5, 6); - //graph.AddEdge(6, 7); - //graph.AddEdge(1, 7); - //graph.AddEdge(6,1); - //graph.AddEdge(7, 2); - //graph.AddEdge(5, 3); - //Env.Print(graph); } } + + + public class TestCurve { [CommandMethod("testbreakcurve")] -- Gitee From 14662f98eef6852f30a26fffe6dbfea0bd9085f6 Mon Sep 17 00:00:00 2001 From: liuqihong <540762622@qq.com> Date: Thu, 21 Apr 2022 22:49:50 +0800 Subject: [PATCH 180/675] =?UTF-8?q?=E4=BF=AE=E6=94=B9=E6=8A=BD=E8=B1=A1?= =?UTF-8?q?=E7=B1=BB?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/IFoxCAD.Cad/Runtime/AutoRegAssem.cs | 20 ++++++++++++++++++-- tests/Test/CmdINI.cs | 15 ++++----------- 2 files changed, 22 insertions(+), 13 deletions(-) diff --git a/src/IFoxCAD.Cad/Runtime/AutoRegAssem.cs b/src/IFoxCAD.Cad/Runtime/AutoRegAssem.cs index f156555..8903151 100644 --- a/src/IFoxCAD.Cad/Runtime/AutoRegAssem.cs +++ b/src/IFoxCAD.Cad/Runtime/AutoRegAssem.cs @@ -26,9 +26,10 @@ public enum AssemLoadType /// /// 初始化程序集信息,并写入注册表 /// -public class AutoRegAssem +public abstract class AutoRegAssem : IExtensionApplication { - public AssemInfo Info; + AutoClass ac; + AssemInfo Info; /// /// 程序集的路径 @@ -67,6 +68,12 @@ public AutoRegAssem() if (!SearchForReg()) RegApp(); + + //实例化了 AutoClass 之后会自动执行 IFoxAutoGo 接口下面的类, + //以及自动执行特性 [IFoxInitialize] + //类库用户不在此处进行其他代码,而是实现特性 + ac = new AutoClass(Info.Name); + ac.Initialize(); } #region RegApp @@ -111,5 +118,14 @@ void RegApp() rk.SetValue("MANAGED", 1, RegistryValueKind.DWord); appkey.Close(); } + + //这里的是不会自动执行的 + public virtual void Initialize() { } + public virtual void Terminate() { } + + ~AutoRegAssem() + { + ac.Terminate(); + } #endregion RegApp } diff --git a/tests/Test/CmdINI.cs b/tests/Test/CmdINI.cs index 39eb4ab..75cb57d 100644 --- a/tests/Test/CmdINI.cs +++ b/tests/Test/CmdINI.cs @@ -2,25 +2,18 @@ * 自动执行接口 * 这里必须要实现一次这个接口,才能使用特性 */ -public class CmdINI : IExtensionApplication +public class CmdINI : AutoRegAssem { - AutoClass ac; - public void Initialize() + public override void Initialize() { - var ara = new AutoRegAssem(); - ac = new AutoClass(ara.Info.Name); - ac.Initialize(); - //实例化了 AutoClass 之后会自动执行 IFoxAutoGo 接口下面的类, - //以及自动执行特性 [IFoxInitialize] - //类库用户不在此处进行其他代码,而是实现特性 } - public void Terminate() + public override void Terminate() { - ac.Terminate(); } } + /* * 自动执行特性例子: */ -- Gitee From 31ee5ccd731b27812c2008504af80c6e74c66f6b Mon Sep 17 00:00:00 2001 From: liuqihong <540762622@qq.com> Date: Thu, 21 Apr 2022 22:50:45 +0800 Subject: [PATCH 181/675] =?UTF-8?q?=E4=BF=AE=E6=94=B9=E8=AF=B4=E6=98=8E?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 14 +--- src/IFoxCAD.Cad/ExtensionMethod/Bo.cs | 94 +++++++++++++++++++++++++++ 2 files changed, 97 insertions(+), 11 deletions(-) create mode 100644 src/IFoxCAD.Cad/ExtensionMethod/Bo.cs diff --git a/README.md b/README.md index 65cefed..b38ca2b 100644 --- a/README.md +++ b/README.md @@ -109,22 +109,14 @@ * 自动执行接口 * 这里必须要实现一次这个接口,才能使用特性 */ - public class CmdINI : IExtensionApplication + public class CmdINI : AutoRegAssem { - AutoClass ac; - public void Initialize() + public override void Initialize() { - var ara = new AutoRegAssem(); - ac = new AutoClass(ara.Info.Name); - ac.Initialize(); - //实例化了 AutoClass 之后会自动执行 IFoxAutoGo 接口下面的类, - //以及自动执行特性 [IFoxInitialize] - //类库用户不在此处进行其他代码,而是实现特性 } - public void Terminate() + public override void Terminate() { - ac.Terminate(); } } diff --git a/src/IFoxCAD.Cad/ExtensionMethod/Bo.cs b/src/IFoxCAD.Cad/ExtensionMethod/Bo.cs new file mode 100644 index 0000000..be38f50 --- /dev/null +++ b/src/IFoxCAD.Cad/ExtensionMethod/Bo.cs @@ -0,0 +1,94 @@ +#if true2 +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; + +namespace IFoxCAD.Cad.ExtensionMethod +{ + public class Bo + { + [CommandMethod("TestBo")] + public void TestBo() + { + //TODO 提取邻接表...没做 + var boNodes = new List(); + + if (boNodes.Count == 0) + return; + + //初始化每个节点 + for (int i = 0; i < boNodes.Count; i++) + { + boNodes[i].Color = BoColor.白; + boNodes[i].Distance = double.MaxValue; + boNodes[i].Parent = null; + } + + //源点加入队列 + var s = boNodes[0]; + var q = new Queue(); + q.Enqueue(s); + if (q.Count != 0) + { + var u = q.Dequeue(); + for (int i = 0; i < u.Count; i++) + { + var v = u[i]; + bool cont = false; + switch (v.Color) + { + case BoColor.白: + { + v.Color = BoColor.灰; + v.Distance = u.Distance + 1; + v.Parent = u; + q.Enqueue(v); + } + break; + case BoColor.灰: + { + + } + break; + case BoColor.黑: + cont = true; + break; + default: + break; + } + if (cont) + continue; + + + } + } + + + } + } + + public enum BoColor + { + 白, + 灰, + 黑, + } + public class BoNode : List// 拥有成员 + { + /// + /// 颜色 + /// + public BoColor Color; + /// + /// 从头到此节点的距离 + /// + public double Distance; + /// + /// 父节点 + /// + public BoNode? Parent; + } +} + +#endif \ No newline at end of file -- Gitee From 5aed71186dd660a9fa61857c3e2d7ad7ae56e723 Mon Sep 17 00:00:00 2001 From: liuqihong <540762622@qq.com> Date: Thu, 21 Apr 2022 22:59:41 +0800 Subject: [PATCH 182/675] =?UTF-8?q?=E4=BF=AE=E6=94=B9=E6=B3=A8=E9=87=8A?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 4 ++-- src/IFoxCAD.Cad/ExtensionMethod/Bo.cs | 2 +- src/IFoxCAD.Cad/Runtime/AutoRegAssem.cs | 4 ++-- tests/Test/CmdINI.cs | 2 +- 4 files changed, 6 insertions(+), 6 deletions(-) diff --git a/README.md b/README.md index b38ca2b..76d60e7 100644 --- a/README.md +++ b/README.md @@ -107,7 +107,7 @@ /* * 自动执行接口 - * 这里必须要实现一次这个接口,才能使用特性 + * 这里必须要实现一次这个接口,才能使用 IFoxInitialize 特性进行自动执行 */ public class CmdINI : AutoRegAssem { @@ -122,7 +122,7 @@ //其他的类中的函数: //实现自动接口之后,在任意一个函数上面使用此特性,减少每次改动 CmdINI 类 - public class AutoAOP + public class AutoTest { [IFoxInitialize] public void Initialize() diff --git a/src/IFoxCAD.Cad/ExtensionMethod/Bo.cs b/src/IFoxCAD.Cad/ExtensionMethod/Bo.cs index be38f50..c059a8e 100644 --- a/src/IFoxCAD.Cad/ExtensionMethod/Bo.cs +++ b/src/IFoxCAD.Cad/ExtensionMethod/Bo.cs @@ -1,4 +1,4 @@ -#if true2 +#if true using System; using System.Collections.Generic; using System.Linq; diff --git a/src/IFoxCAD.Cad/Runtime/AutoRegAssem.cs b/src/IFoxCAD.Cad/Runtime/AutoRegAssem.cs index 8903151..879bac0 100644 --- a/src/IFoxCAD.Cad/Runtime/AutoRegAssem.cs +++ b/src/IFoxCAD.Cad/Runtime/AutoRegAssem.cs @@ -78,7 +78,7 @@ public AutoRegAssem() #region RegApp - private static RegistryKey GetAcAppKey() + static RegistryKey GetAcAppKey() { #if NET35 string key = HostApplicationServices.Current.RegistryProductRootKey; @@ -89,7 +89,7 @@ private static RegistryKey GetAcAppKey() return ackey.CreateSubKey("Applications"); } - private bool SearchForReg() + bool SearchForReg() { var appkey = GetAcAppKey(); if (appkey.SubKeyCount == 0) diff --git a/tests/Test/CmdINI.cs b/tests/Test/CmdINI.cs index 75cb57d..3991e6a 100644 --- a/tests/Test/CmdINI.cs +++ b/tests/Test/CmdINI.cs @@ -1,6 +1,6 @@ /* * 自动执行接口 - * 这里必须要实现一次这个接口,才能使用特性 + * 这里必须要实现一次这个接口,才能使用 IFoxInitialize 特性进行自动执行 */ public class CmdINI : AutoRegAssem { -- Gitee From 515638f1cc18ec0a3383cd874741da137b085f0c Mon Sep 17 00:00:00 2001 From: liuqihong <540762622@qq.com> Date: Thu, 21 Apr 2022 22:59:57 +0800 Subject: [PATCH 183/675] =?UTF-8?q?=E4=BF=AE=E6=94=B9=E6=B3=A8=E9=87=8A?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/IFoxCAD.Cad/ExtensionMethod/Bo.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/IFoxCAD.Cad/ExtensionMethod/Bo.cs b/src/IFoxCAD.Cad/ExtensionMethod/Bo.cs index c059a8e..be38f50 100644 --- a/src/IFoxCAD.Cad/ExtensionMethod/Bo.cs +++ b/src/IFoxCAD.Cad/ExtensionMethod/Bo.cs @@ -1,4 +1,4 @@ -#if true +#if true2 using System; using System.Collections.Generic; using System.Linq; -- Gitee From 1e251981e0bc23e5958b2e6e82a9d0273a7479de Mon Sep 17 00:00:00 2001 From: liuqihong <540762622@qq.com> Date: Thu, 21 Apr 2022 23:55:11 +0800 Subject: [PATCH 184/675] =?UTF-8?q?=E8=AE=BA=E6=96=87=E7=AE=97=E6=B3=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/IFoxCAD.Cad/ExtensionMethod/Bo.cs | 130 +++++++++++++++++++------- 1 file changed, 96 insertions(+), 34 deletions(-) diff --git a/src/IFoxCAD.Cad/ExtensionMethod/Bo.cs b/src/IFoxCAD.Cad/ExtensionMethod/Bo.cs index be38f50..df05118 100644 --- a/src/IFoxCAD.Cad/ExtensionMethod/Bo.cs +++ b/src/IFoxCAD.Cad/ExtensionMethod/Bo.cs @@ -1,4 +1,4 @@ -#if true2 +#if true using System; using System.Collections.Generic; using System.Linq; @@ -8,63 +8,125 @@ namespace IFoxCAD.Cad.ExtensionMethod { public class Bo { + // 根据论文撸出来的 + // https://www.docin.com/p-740208811.html?docfrom=rrela [CommandMethod("TestBo")] public void TestBo() { - //TODO 提取邻接表...没做 + //TODO 步骤01:提取邻接表...没做 var boNodes = new List(); if (boNodes.Count == 0) return; - //初始化每个节点 + //步骤02:初始化每个节点 for (int i = 0; i < boNodes.Count; i++) { boNodes[i].Color = BoColor.白; - boNodes[i].Distance = double.MaxValue; + boNodes[i].Distance = int.MaxValue; boNodes[i].Parent = null; } - //源点加入队列 - var s = boNodes[0]; var q = new Queue(); - q.Enqueue(s); - if (q.Count != 0) + + //步骤12:源点无法涉及的点,也就是独立白色的点进行处理 + for (int ss = 0; ss < boNodes.Count; ss++) { - var u = q.Dequeue(); - for (int i = 0; i < u.Count; i++) + //步骤03:源点加入队列 + var s = boNodes[ss]; + //循环的时候,如果它不白色,表示它被处理过 + if (s.Color != BoColor.白) + continue; + + //步骤04:加入队列,进行此源点的邻近点(链条)染色 + q.Enqueue(s); + while (q.Count != 0) { - var v = u[i]; - bool cont = false; - switch (v.Color) + //步骤05: + var u = q.Dequeue(); + //步骤06 + 步骤10:遍历邻近节点 + for (int vv = 0; vv < u.Count; vv++) { - case BoColor.白: - { - v.Color = BoColor.灰; - v.Distance = u.Distance + 1; - v.Parent = u; - q.Enqueue(v); - } - break; - case BoColor.灰: - { - - } - break; - case BoColor.黑: - cont = true; - break; - default: - break; + var v = u[vv]; + switch (v.Color) + { + case BoColor.白://步骤07 + { + v.Color = BoColor.灰; + v.Distance = u.Distance + 1; + v.Parent = u; + q.Enqueue(v); + } + break; + case BoColor.灰://步骤08 + { + v.Add(u); + } + break; + case BoColor.黑://步骤09 + break; + default: + break; + } } - if (cont) - continue; + //步骤11 + u.Color = BoColor.黑; + } + } + var boss = new LoopList>(); + //步骤13 + 步骤18:从所有节点依次取出一个点 + for (int ii = 0; ii < boNodes.Count; ii++) + { + var u = boNodes[ii]; + //步骤14:跳过 + if (u.Count == 0) + continue; + //步骤15:取相遇点,并新建封闭集 + var L = new LoopList(); + for (int jj = 0; jj < u.Count; jj++) + { + var v = boNodes[ii][jj]; + if (u.Distance == v.Distance) + { + //步骤16: + L.Add(u); + L.Add(v); + 步骤20(u, v, L); + } + else if (u.Distance - v.Distance < 1) + { + //步骤17: + L.Add(u.Parent!); + L.Add(u); + L.Add(v); + 步骤20(u.Parent!, v, L); + } + boss.Add(L); } } + //步骤19: 算法结束 + } + /// + /// 这个是排序吗? + /// + /// + /// + /// + void 步骤20(BoNode u, BoNode v, LoopList L) + { + //步骤20: + if (u.Parent == v.Parent) + { + L.AddFirst(u.Parent!); + return; + } + //步骤21: + L.AddFirst(u.Parent!); + L.AddLast(v.Parent!); } } @@ -83,7 +145,7 @@ public class BoNode : List// 拥有成员 /// /// 从头到此节点的距离 /// - public double Distance; + public int Distance; /// /// 父节点 /// -- Gitee From ad4d462f804d53a4e857ee3be6dfb1ab23d8e013 Mon Sep 17 00:00:00 2001 From: liuqihong <540762622@qq.com> Date: Fri, 22 Apr 2022 06:49:01 +0800 Subject: [PATCH 185/675] =?UTF-8?q?=E6=95=B4=E7=90=86=E4=BB=A3=E7=A0=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/IFoxCAD.Cad/Algorithms/Graph.cs | 221 ++++++++++++++++------------ 1 file changed, 124 insertions(+), 97 deletions(-) diff --git a/src/IFoxCAD.Cad/Algorithms/Graph.cs b/src/IFoxCAD.Cad/Algorithms/Graph.cs index 865e9e8..bc50e12 100644 --- a/src/IFoxCAD.Cad/Algorithms/Graph.cs +++ b/src/IFoxCAD.Cad/Algorithms/Graph.cs @@ -1,60 +1,53 @@ using Exception = System.Exception; namespace IFoxCAD.Cad; + /// /// 无权无向图实现 -/// IEnumerable 枚举所有顶点。 +/// IEnumerable 枚举所有顶点; /// internal sealed class Graph : IGraph, IEnumerable { #region 字段及属性 /// - /// 存储所有节点的字典,key为顶点的类型,value为邻接表,类型是hashset,不可重复添加点 + /// 存储所有节点的字典,key为顶点的类型,value为邻接表,类型是hashset,不可重复添加点 /// /// - private readonly Dictionary> vertices = new(); + readonly Dictionary> vertices = new(); /// - /// 邻接边表,key为顶点的类型,value为邻接边表,类型是hashset,不可重复添加边 + /// 邻接边表,key为顶点的类型,value为邻接边表,类型是hashset,不可重复添加边 /// - private readonly Dictionary> edges = new(); + readonly Dictionary> edges = new(); /// - /// 为加快索引,引入hash检索 + /// 为加快索引,引入hash检索 /// - private readonly Dictionary vertexs = new(); + readonly Dictionary vertexs = new(); public int VerticesCount => vertices.Count; /// - /// 目前点增加点的顺序号,这个点号不随删点而减少的 + /// 目前点增加点的顺序号,这个点号不随删点而减少的 /// - private static int insertCount; + static int insertCount; #endregion - #region 构造函数及私有函数 + #region 构造函数 public Graph() { insertCount = 0; // 每次新建对象就将顶点顺序号归零 } - - private static string GetHashString(Point3d pt) - { - return $"{pt.X:n6}{pt.Y:n6}{pt.Z:n6}"; - } #endregion - #region 添加顶点及边 + #region 顶点及边_增 /// - /// 向该图添加一个新顶点,但是无边。 + /// 向该图添加一个新顶点,但是无边; /// /// 点 /// 创建的顶点 public IGraphVertex AddVertex(Point3d pt) { - var str = Graph.GetHashString(pt); if (vertexs.ContainsKey(str)) - { return vertexs[str]; - } var vertex = new GraphVertex(pt, insertCount++); vertices.Add(vertex, new HashSet()); @@ -66,7 +59,7 @@ public IGraphVertex AddVertex(Point3d pt) } /// - /// 向该图添加一个边。 + /// 向该图添加一个边; /// /// public void AddEdge(Curve3d curve!!) @@ -78,19 +71,19 @@ public void AddEdge(Curve3d curve!!) vertices[start].Add(end); edges[start].Add(new GraphEdge(end, curve)); - // 为了保证点顺序,每个点的邻接边必须按起点-终点,所以添加曲线终点时,将添加一个方向的曲线 + // 为了保证点顺序,每个点的邻接边必须按起点-终点,所以添加曲线终点时,将添加一个方向的曲线 var curtmp = (Curve3d)curve.Clone(); - curtmp = curtmp!.GetReverseParameterCurve(); + curtmp = curtmp.GetReverseParameterCurve(); // 添加终点的邻接表和邻接边 vertices[end].Add(start); edges[end].Add(new GraphEdge(start, curtmp)); } -#endregion + #endregion -#region 删除顶点及边 + #region 顶点及边_删 /// - /// 从此图中删除现有顶点。 + /// 从此图中删除现有顶点; /// /// 点 public void RemoveVertex(Point3d pt) @@ -99,25 +92,24 @@ public void RemoveVertex(Point3d pt) if (vertexs.ContainsKey(str)) { var vertex = vertexs[str]; + // 删除邻接表里的vertex点,先删除后面的遍历可以少一轮 vertices.Remove(vertex!); + // 删除其他顶点的邻接表里的vertex点 foreach (var item in vertices.Values) - { item.Remove(vertex!); - } // 删除邻接边表里的vertex点,先删除后面的遍历可以少一轮 edges.Remove(vertex!); + // 删除其他顶点的邻接边表的指向vertex的边 foreach (var item in edges.Values) { foreach (var edge in item) { - if (vertex!.Equals(edge.TargetVertex)) - { + if (vertex.Equals(edge.TargetVertex)) item.Remove(edge); - } } } vertexs.Remove(str); @@ -125,7 +117,7 @@ public void RemoveVertex(Point3d pt) } /// - /// 从此图中删除一条边。 + /// 从此图中删除一条边; /// /// 曲线 public void RemoveEdge(Curve3d curve!!) @@ -133,27 +125,24 @@ public void RemoveEdge(Curve3d curve!!) RemoveVertex(curve.StartPoint); RemoveVertex(curve.EndPoint); } -#endregion + #endregion -#region 是否存在及获取顶点和边 + #region 顶点和边_查 /// /// 我们在给定的来源和目的地之间是否有边? /// /// 起点 /// 终点 - /// 有边返回 ,反之返回 + /// 有边返回 ,反之返回 public bool HasEdge(IGraphVertex source, IGraphVertex dest) { if (!vertices.ContainsKey(source) || !vertices.ContainsKey(dest)) - { - throw new ArgumentException("源或目标不在此图中。"); - } + throw new ArgumentException("源或目标不在此图中;"); + foreach (var item in edges[source]) { if (item.TargetVertex == dest) - { return true; - } } return false; } @@ -168,31 +157,28 @@ public bool HasEdge(IGraphVertex source, IGraphVertex dest) public IEdge? GetEdge(IGraphVertex source, IGraphVertex dest) { if (!vertices.ContainsKey(source) || !vertices.ContainsKey(dest)) - { - throw new ArgumentException("源或目标不在此图中。"); - } + throw new ArgumentException("源或目标不在此图中;"); + foreach (var item in edges[source]) { if (item.TargetVertex == dest) - { return item; - } } return null; } /// - /// 是否存在顶点,此函数目前未发现有啥用 + /// 是否存在顶点,此函数目前未发现有啥用 /// /// 顶点 - /// 存在顶点返回 ,反之返回 + /// 存在顶点返回 ,反之返回 public bool ContainsVertex(IGraphVertex value) { return vertices.ContainsKey(value); } -#endregion + #endregion -#region 获取邻接表和曲线 + #region 获取邻接表和曲线 /// /// 获取顶点的邻接表 /// @@ -227,44 +213,41 @@ public List GetCurves(List graphVertices) var next = graphVertices[i + 1]; var edge = GetEdge(cur, next); if (edge is not null) - { curves.Add(edge.TargetEdge); - } } var lastedge = GetEdge(graphVertices[graphVertices.Count - 1], graphVertices[0]); if (lastedge is not null) - { curves.Add(lastedge.TargetEdge); - } + return curves; } -#endregion + #endregion -#region 克隆及接口实现 + #region 克隆及接口实现 /// - /// 克隆此图。目测是深克隆 + /// 克隆此图;目测是深克隆 /// public Graph Clone() { var newGraph = new Graph(); foreach (var vertex in edges.Values) - { foreach (var item in vertex) - { newGraph.AddEdge(item.TargetEdge); - } - } + return newGraph; } + IGraph IGraph.Clone() { return Clone(); } + public IEnumerator GetEnumerator() { return VerticesAsEnumberable.GetEnumerator(); } + IEnumerator? IEnumerable.GetEnumerator() { return GetEnumerator() as IEnumerator; @@ -272,7 +255,13 @@ public IEnumerator GetEnumerator() public IEnumerable VerticesAsEnumberable => vertices.Select(x => x.Key); -#endregion + #endregion + + #region 方法 + static string GetHashString(Point3d pt) + { + return $"{pt.X:n6}{pt.Y:n6}{pt.Z:n6}"; + } /// /// 输出点的邻接表的可读字符串 @@ -286,38 +275,53 @@ public string ToReadable() { var adjacents = string.Empty; - output = String.Format("{1}\r\n{0}-{2}: [", i, output, node.Key.Data.ToString()); + output = string.Format("{1}\r\n{0}-{2}: [", i, output, node.Key.Data.ToString()); foreach (var adjacentNode in node.Value) - adjacents = String.Format("{0}{1},", adjacents, adjacentNode.Data.ToString()); + adjacents = string.Format("{0}{1},", adjacents, adjacentNode.Data.ToString()); if (adjacents.Length > 0) adjacents = adjacents.TrimEnd(new char[] { ',', ' ' }); - output = String.Format("{0}{1}]", output, adjacents); + output = string.Format("{0}{1}]", output, adjacents); i++; } return output; - } + } + #endregion } + /// -/// 邻接表图实现的顶点。 -/// IEnumerable 枚举所有邻接点。 +/// 邻接表图实现的顶点; +/// IEnumerable 枚举所有邻接点; /// -internal sealed class GraphVertex : IGraphVertex, IEquatable, IComparable ,IComparable +internal sealed class GraphVertex : IGraphVertex, IEquatable, IComparable, IComparable { - public Point3d Data { get; private set; } + #region 属性 + public Point3d Data { get; set; } public int Index { get; set; } + #endregion + + #region 构造 + /// + /// 邻接表图实现的顶点 + /// + /// 点 + /// 所在节点索引 public GraphVertex(Point3d value, int index) { Data = value; Index = index; } + #endregion + + #region 重载运算符_比较 public bool Equals(IGraphVertex other) { return Index == other.Index; } + public override bool Equals(object obj) { if (obj is null) @@ -327,10 +331,12 @@ public override bool Equals(object obj) else return Equals(vertex); } + public override int GetHashCode() { return Index; } + public int CompareTo(IGraphVertex other) { if (Equals(other)) @@ -370,6 +376,7 @@ public int CompareTo(object obj) return person1.Equals(person2); } + public static bool operator !=(GraphVertex person1, GraphVertex person2) { if (person1 is null || person2 is null) @@ -377,24 +384,34 @@ public int CompareTo(object obj) return !person1.Equals(person2); } + #endregion } + /// /// 无向图中边的定义 /// internal sealed class GraphEdge : IEdge, IEquatable { - // 这里的传入的两个参数分别为下一点和下一点之间的曲线 + #region 属性 + public IGraphVertex TargetVertex { get; set; } + public Curve3d TargetEdge { get; set; } + #endregion + + #region 构造 + /// + /// 无向图中边的定义 + /// + /// 下一点 + /// 下一点之间的曲线 public GraphEdge(IGraphVertex target, Curve3d edge) { TargetVertex = target; TargetEdge = edge; } + #endregion - public IGraphVertex TargetVertex { get; private set; } - - public Curve3d TargetEdge { get; private set; } - + #region 重载运算符_比较 public bool Equals(GraphEdge other) { if (other is null) @@ -430,14 +447,19 @@ public override int GetHashCode() return !person1.Equals(person2); } + #endregion } + /// -/// 深度优先搜索。 +/// 深度优先搜索; /// internal sealed class DepthFirst { - // 存储所有的边 + #region 公共方法 + /// + /// 存储所有的边 + /// public List> Curve3ds { get; } = new(); /// @@ -446,18 +468,19 @@ internal sealed class DepthFirst /// 图 public void FindAll(IGraph graph) { - foreach (var item in graph.VerticesAsEnumberable) - { - Dfs(graph, new List { item }); - } - } + var ge = graph.VerticesAsEnumberable.GetEnumerator(); + while (ge.MoveNext()) + Dfs(graph, new List { ge.Current }); + } + #endregion + #region 内部方法 /// - /// 递归 DFS。 + /// 递归 DFS; /// /// 图 /// 已经遍历的路径 - private void Dfs(IGraph graph, List visited) + void Dfs(IGraph graph, List visited) { var startNode = visited[0]; IGraphVertex nextNode; @@ -470,21 +493,19 @@ private void Dfs(IGraph graph, List visited) // 如果下一个点未遍历过 if (!visited.Contains(nextNode)) { - // 将下一点加入路径集合,并进行下一次递归 + // 将下一点加入路径集合,并进行下一次递归 sub = new List { nextNode }; sub.AddRange(visited); Dfs(graph, sub); } - // 如果下一点遍历过,并且路径大于2,说明已经找到起点 + // 如果下一点遍历过,并且路径大于2,说明已经找到起点 else if (visited.Count > 2 && nextNode.Equals(visited[^1])) { - // 将重复的路径进行过滤,并把新的路径存入结果 + // 将重复的路径进行过滤,并把新的路径存入结果 var cur = DepthFirst.RotateToSmallest(visited); var inv = DepthFirst.Invert(cur); if (IsNew(cur) && IsNew(inv)) - { Curve3ds.Add(cur); - } } } } @@ -494,31 +515,34 @@ private void Dfs(IGraph graph, List visited) /// /// /// - private static List RotateToSmallest(List lst) + static List RotateToSmallest(List lst) { var index = lst.IndexOf(lst.Min()); return lst.Skip(index).Concat(lst.Take(index)).ToList(); } + /// - /// 将列表反向,并旋转到起点为最小值 + /// 将列表反向,并旋转到起点为最小值 /// /// /// - private static List Invert(List lst) + static List Invert(List lst) { var tmp = lst.ToList(); tmp.Reverse(); return RotateToSmallest(tmp); } + /// /// 比较两个列表是否相等 /// /// /// /// - private static bool Equals(List a, List b) + static bool Equals(List a, List b) { bool ret = (a[0] == b[0]) && (a.Count == b.Count); + for (int i = 1; ret && (i < a.Count); i++) if (!a[i].Equals(b[i])) ret = false; @@ -531,15 +555,18 @@ private static bool Equals(List a, List b) /// /// /// - private bool IsNew(List path) + bool IsNew(List path) { bool ret = true; - foreach (var p in Curve3ds) - if (Equals(p, path)) + for (int i = 0; i < Curve3ds.Count; i++) + { + if (DepthFirst.Equals(Curve3ds[i], path)) { ret = false; break; } + } return ret; - } -} + } + #endregion +} \ No newline at end of file -- Gitee From 8aeb0cc4c3f5dfa65f98e5762ef91a318c556997 Mon Sep 17 00:00:00 2001 From: liuqihong <540762622@qq.com> Date: Fri, 22 Apr 2022 07:18:34 +0800 Subject: [PATCH 186/675] =?UTF-8?q?=E6=8F=90=E5=8F=96=E7=82=B9=E5=93=88?= =?UTF-8?q?=E5=B8=8C=E5=AD=97=E7=AC=A6=E4=B8=B2=E5=87=BA=E6=9D=A5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/IFoxCAD.Cad/Algorithms/Graph.cs | 9 ++------- src/IFoxCAD.Cad/ExtensionMethod/PointEx.cs | 9 +++++++++ 2 files changed, 11 insertions(+), 7 deletions(-) create mode 100644 src/IFoxCAD.Cad/ExtensionMethod/PointEx.cs diff --git a/src/IFoxCAD.Cad/Algorithms/Graph.cs b/src/IFoxCAD.Cad/Algorithms/Graph.cs index bc50e12..0001b6f 100644 --- a/src/IFoxCAD.Cad/Algorithms/Graph.cs +++ b/src/IFoxCAD.Cad/Algorithms/Graph.cs @@ -45,7 +45,7 @@ public Graph() /// 创建的顶点 public IGraphVertex AddVertex(Point3d pt) { - var str = Graph.GetHashString(pt); + var str = pt.GetHashString(); if (vertexs.ContainsKey(str)) return vertexs[str]; @@ -88,7 +88,7 @@ public void AddEdge(Curve3d curve!!) /// 点 public void RemoveVertex(Point3d pt) { - var str = Graph.GetHashString(pt); + var str = pt.GetHashString(); if (vertexs.ContainsKey(str)) { var vertex = vertexs[str]; @@ -258,11 +258,6 @@ public IEnumerator GetEnumerator() #endregion #region 方法 - static string GetHashString(Point3d pt) - { - return $"{pt.X:n6}{pt.Y:n6}{pt.Z:n6}"; - } - /// /// 输出点的邻接表的可读字符串 /// diff --git a/src/IFoxCAD.Cad/ExtensionMethod/PointEx.cs b/src/IFoxCAD.Cad/ExtensionMethod/PointEx.cs new file mode 100644 index 0000000..30f3c2c --- /dev/null +++ b/src/IFoxCAD.Cad/ExtensionMethod/PointEx.cs @@ -0,0 +1,9 @@ +namespace IFoxCAD.Cad; + +public static class PointEx +{ + public static string GetHashString(this Point3d pt) + { + return $"{pt.X:n6}{pt.Y:n6}{pt.Z:n6}"; + } +} \ No newline at end of file -- Gitee From d741262b8c4284c669bd4658143eb092a7a1b856 Mon Sep 17 00:00:00 2001 From: vicwjb <403009819@qq.com> Date: Fri, 22 Apr 2022 23:21:00 +0800 Subject: [PATCH 187/675] =?UTF-8?q?=E8=B0=83=E6=95=B4=E9=83=A8=E5=88=86?= =?UTF-8?q?=E6=B5=8B=E8=AF=95=E5=87=BD=E6=95=B0=E7=9A=84=E4=BD=8D=E7=BD=AE?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- tests/Test/Test.cs | 187 -------------------------------------- tests/Test/TestDBTrans.cs | 74 +++++++++++++++ tests/Test/TestPoint.cs | 48 ++++++++++ tests/Test/testblock.cs | 159 ++++++++++++++++++++++++++++++++ tests/Test/testtr.cs | 49 ---------- 5 files changed, 281 insertions(+), 236 deletions(-) create mode 100644 tests/Test/TestDBTrans.cs create mode 100644 tests/Test/TestPoint.cs delete mode 100644 tests/Test/testtr.cs diff --git a/tests/Test/Test.cs b/tests/Test/Test.cs index 1a2076a..11f0326 100644 --- a/tests/Test/Test.cs +++ b/tests/Test/Test.cs @@ -338,50 +338,7 @@ public void TestRec() - [CommandMethod("testpt")] - public void TestPt() - { - //var pt = Env.Editor.GetPoint("pick pt:").Value; - //var pl = Env.Editor.GetEntity("pick pl").ObjectId; - var tr1 = HostApplicationServices.WorkingDatabase.TransactionManager.TopTransaction; - using var tr2 = new DBTrans(); - var tr3 = HostApplicationServices.WorkingDatabase.TransactionManager.TopTransaction; - var tr6 = Application.DocumentManager.MdiActiveDocument.TransactionManager.TopTransaction; - Env.Print(tr2.Transaction == tr3); - Env.Print(tr3 == tr6); - using var tr4 = new DBTrans(); - var tr5 = HostApplicationServices.WorkingDatabase.TransactionManager.TopTransaction; - var tr7 = Application.DocumentManager.MdiActiveDocument.TransactionManager.TopTransaction; - Env.Print(tr4.Transaction == tr5); - Env.Print(tr5 == tr7); - var trm = HostApplicationServices.WorkingDatabase.TransactionManager; - //var ptt = tr.GetObject(pl).GetClosestPointTo(pt,false); - //var pt1 = new Point3d(0, 0.00000000000001, 0); - //var pt2 = new Point3d(0, 0.00001, 0); - //Env.Print(Tolerance.Global.EqualPoint); - //Env.Print(pt1.IsEqualTo(pt2).ToString()); - //Env.Print(pt1.IsEqualTo(pt2,new Tolerance(0.0,1e-6)).ToString()); - //Env.Print((pt1 == pt2).ToString()); - //Env.Print((pt1 != pt2).ToString()); - - - - } - [CommandMethod("TestBack")] - public void TestBack() - { - using var tr = new DBTrans(@"C:\Users\vic\Desktop\test.dwg"); - tr.ModelSpace.GetEntities().ForEach(ent => { - ent.ForWrite(e => e.ColorIndex = 3); - }); - tr.Database.SaveAs(@"C:\Users\vic\Desktop\test.dwg", DwgVersion.Current); - - tr.ModelSpace.GetEntities().ForEach(ent => { - ent.ForWrite(e => e.ColorIndex = 4); - }); - tr.Database.SaveAs(@"C:\Users\vic\Desktop\test.dwg", DwgVersion.Current); - } public Database Getdb() { @@ -398,149 +355,5 @@ public Document Getdoc() } -public class BlockImportClass -{ - - [CommandMethod("CBLL")] - public void cbll() - { - string filename = @"C:\Users\vic\Desktop\Drawing1.dwg"; - using var tr = new DBTrans(); - using var tr1 = new DBTrans(filename); - //tr.BlockTable.GetBlockFrom(filename, true); - string blkdefname = SymbolUtilityServices.RepairSymbolName(SymbolUtilityServices.GetSymbolNameFromPathName(filename, "dwg"), false); - tr.Database.Insert(blkdefname, tr1.Database, false); //插入了块定义,未插入块参照 - - } - - - [CommandMethod("CBL")] - public void CombineBlocksIntoLibrary() - { - Document doc = - Application.DocumentManager.MdiActiveDocument; - Editor ed = doc.Editor; - Database destDb = doc.Database; - - // Get name of folder from which to load and import blocks - - PromptResult pr = - ed.GetString("\nEnter the folder of source drawings: "); - - if (pr.Status != PromptStatus.OK) - return; - string pathName = pr.StringResult; - - // Check the folder exists - - if (!Directory.Exists(pathName)) - { - ed.WriteMessage( - "\nDirectory does not exist: {0}", pathName - ); - return; - } - - // Get the names of our DWG files in that folder - - string[] fileNames = Directory.GetFiles(pathName, "*.dwg"); - - // A counter for the files we've imported - - int imported = 0, failed = 0; - - // For each file in our list - - foreach (string fileName in fileNames) - { - // Double-check we have a DWG file (probably unnecessary) - - if (fileName.EndsWith( - ".dwg", - StringComparison.InvariantCultureIgnoreCase - ) - ) - { - // Catch exceptions at the file level to allow skipping - - try - { - // Suggestion from Thorsten Meinecke... - - string destName = - SymbolUtilityServices.GetSymbolNameFromPathName( - fileName, "dwg" - ); - - // And from Dan Glassman... - - destName = - SymbolUtilityServices.RepairSymbolName( - destName, false - ); - - // Create a source database to load the DWG into - - using (Database db = new Database(false, true)) - { - // Read the DWG into our side database - - db.ReadDwgFile(fileName, FileShare.Read, true, ""); - bool isAnno = db.AnnotativeDwg; - - // Insert it into the destination database as - // a named block definition - - ObjectId btrId = destDb.Insert( - destName, - db, - false - ); - - if (isAnno) - { - // If an annotative block, open the resultant BTR - // and set its annotative definition status - - Transaction tr = - destDb.TransactionManager.StartTransaction(); - using (tr) - { - BlockTableRecord btr = - (BlockTableRecord)tr.GetObject( - btrId, - OpenMode.ForWrite - ); - btr.Annotative = AnnotativeStates.True; - tr.Commit(); - } - } - - // Print message and increment imported block counter - - ed.WriteMessage("\nImported from \"{0}\".", fileName); - imported++; - } - } - catch (System.Exception ex) - { - ed.WriteMessage( - "\nProblem importing \"{0}\": {1} - file skipped.", - fileName, ex.Message - ); - failed++; - } - } - } - - ed.WriteMessage( - "\nImported block definitions from {0} files{1} in " + - "\"{2}\" into the current drawing.", - imported, - failed > 0 ? " (" + failed + " failed)" : "", - pathName - ); - } -} #pragma warning restore CS0219 // 变量已被赋值,但从未使用过它的值 diff --git a/tests/Test/TestDBTrans.cs b/tests/Test/TestDBTrans.cs new file mode 100644 index 0000000..d72e932 --- /dev/null +++ b/tests/Test/TestDBTrans.cs @@ -0,0 +1,74 @@ +namespace Test; +public class TestTrans +{ + [CommandMethod("testtr")] + public void Testtr() + { + string filename = @"C:\Users\vic\Desktop\test.dwg"; + using var tr = new DBTrans(filename); + tr.ModelSpace.AddCircle(new Point3d(0, 0, 0), 20); + tr.Database.SaveAs(filename,DwgVersion.Current); + } + [CommandMethod("testifoxcommit")] + public void Testifoxcommit() + { + + using var tr = new DBTrans(); + tr.ModelSpace.AddCircle(new Point3d(0, 0, 0), 20); + tr.Abort(); + //tr.Commit(); + } + + // AOP 应用 预计示例: + // 1. 无参数 + //[AOP] + //[CommandMethod("TESTAOP")] + //public void testaop() + //{ + // // 不用 using var tr = new DBTrans(); + // var tr = DBTrans.Top; + // tr.ModelSpace.AddCircle(new Point3d(0, 0, 0), 20); + //} + + // 2. 有参数 + //[AOP("file")] + //[CommandMethod("TESTAOP")] + //public void testaop() + //{ + // // 不用 using var tr = new DBTrans(file); + // var tr = DBTrans.Top; + // tr.ModelSpace.AddCircle(new Point3d(0, 0, 0), 20); + //} + + + [CommandMethod("testpt")] + public void TestPt() + { + //var pt = Env.Editor.GetPoint("pick pt:").Value; + //var pl = Env.Editor.GetEntity("pick pl").ObjectId; + var tr1 = HostApplicationServices.WorkingDatabase.TransactionManager.TopTransaction; + using var tr2 = new DBTrans(); + var tr3 = HostApplicationServices.WorkingDatabase.TransactionManager.TopTransaction; + var tr6 = Application.DocumentManager.MdiActiveDocument.TransactionManager.TopTransaction; + Env.Print(tr2.Transaction == tr3); + Env.Print(tr3 == tr6); + using var tr4 = new DBTrans(); + var tr5 = HostApplicationServices.WorkingDatabase.TransactionManager.TopTransaction; + var tr7 = Application.DocumentManager.MdiActiveDocument.TransactionManager.TopTransaction; + Env.Print(tr4.Transaction == tr5); + Env.Print(tr5 == tr7); + var trm = HostApplicationServices.WorkingDatabase.TransactionManager; + //var ptt = tr.GetObject(pl).GetClosestPointTo(pt,false); + //var pt1 = new Point3d(0, 0.00000000000001, 0); + //var pt2 = new Point3d(0, 0.00001, 0); + //Env.Print(Tolerance.Global.EqualPoint); + //Env.Print(pt1.IsEqualTo(pt2).ToString()); + //Env.Print(pt1.IsEqualTo(pt2,new Tolerance(0.0,1e-6)).ToString()); + //Env.Print((pt1 == pt2).ToString()); + //Env.Print((pt1 != pt2).ToString()); + + + + } + +} diff --git a/tests/Test/TestPoint.cs b/tests/Test/TestPoint.cs new file mode 100644 index 0000000..dfe7de5 --- /dev/null +++ b/tests/Test/TestPoint.cs @@ -0,0 +1,48 @@ +namespace Test +{ + public class TestPoint + { + [CommandMethod("Testpoint3d")] + public void TestPoint3d() + { + Env.Print($"4位小数的hash:{new Point3d(0.0_001, 0.0_002, 0.0).GetHashCode()}"); + Env.Print($"5位小数的hash:{new Point3d(0.00_001, 0.00_002, 0.0).GetHashCode()}"); + Env.Print($"6位小数的hash:{new Point3d(0.000_001, 0.000_002, 0.0).GetHashCode()}"); + Env.Print($"7位小数的hash:{new Point3d(0.000_0_001, 0.000_0_002, 0.0).GetHashCode()}"); + Env.Print($"8位小数的hash:{new Point3d(0.000_00_001, 0.000_00_002, 0.0).GetHashCode()}"); + Env.Print($"9位小数的hash:{new Point3d(0.000_000_001, 0.000_000_002, 0.0).GetHashCode()}"); + Env.Print($"10位小数的hash:{new Point3d(0.000_000_0001, 0.000_000_0002, 0.0).GetHashCode()}"); + Env.Print($"10位小数的hash:{new Point3d(0.000_000_0001, 0.000_000_0001, 0.0).GetHashCode()}"); + + Env.Print($"11位小数的hash:{new Point3d(0.000_000_000_01, 0.000_000_000_02, 0.0).GetHashCode()}"); + Env.Print($"11位小数的hash:{new Point3d(0.000_000_000_01, 0.000_000_000_01, 0.0).GetHashCode()}"); + + Env.Print($"12位小数的hash:{new Point3d(0.000_000_000_001, 0.000_000_000_002, 0.0).GetHashCode()}"); + Env.Print($"12位小数的hash:{new Point3d(0.000_000_000_001, 0.000_000_000_001, 0.0).GetHashCode()}"); + + Env.Print($"13位小数的hash:{new Point3d(0.000_000_000_0001, 0.000_000_000_0002, 0.0).GetHashCode()}"); + Env.Print($"13位小数的hash:{new Point3d(0.000_000_000_0001, 0.000_000_000_0001, 0.0).GetHashCode()}"); + + Env.Print($"14位小数的hash:{new Point3d(0.000_000_000_000_01, 0.000_000_000_000_02, 0.0).GetHashCode()}"); + Env.Print($"14位小数的hash:{new Point3d(0.000_000_000_000_01, 0.000_000_000_000_01, 0.0).GetHashCode()}"); + + Env.Print($"15位小数的hash:{new Point3d(0.000_000_000_000_001, 0.000_000_000_000_002, 0.0).GetHashCode()}"); + Env.Print($"15位小数的hash:{new Point3d(0.000_000_000_000_001, 0.000_000_000_000_001, 0.0).GetHashCode()}"); + + Env.Print($"16位小数的hash:{new Point3d(0.000_000_000_000_000_1, 0.000_000_000_000_000_2, 0.0).GetHashCode()}"); + Env.Print($"16位小数的hash:{new Point3d(0.000_000_000_000_000_1, 0.000_000_000_000_000_1, 0.0).GetHashCode()}"); + + Env.Print($"17位小数的hash:{new Point3d(0.000_000_000_000_000_01, 0.000_000_000_000_000_02, 0.0).GetHashCode()}"); + Env.Print($"17位小数的hash:{new Point3d(0.000_000_000_000_000_01, 0.000_000_000_000_000_01, 0.0).GetHashCode()}"); + + Env.Print($"18位小数的hash:{new Point3d(0.000_000_000_000_000_001, 0.000_000_000_000_000_002, 0.0).GetHashCode()}"); + Env.Print($"18位小数的hash:{new Point3d(0.000_000_000_000_000_001, 0.000_000_000_000_000_001, 0.0).GetHashCode()}"); + + Env.Print($"19位小数的hash:{new Point3d(0.000_000_000_000_000_000_1, 0.000_000_000_000_000_000_2, 0.0).GetHashCode()}"); + Env.Print($"19位小数的hash:{new Point3d(0.000_000_000_000_000_000_1, 0.000_000_000_000_000_000_1, 0.0).GetHashCode()}"); + + Env.Print($"20位小数的hash:{new Point3d(0.000_000_000_000_000_000_01, 0.000_000_000_000_000_000_02, 0.0).GetHashCode()}"); + Env.Print($"20位小数的hash:{new Point3d(0.000_000_000_000_000_000_01, 0.000_000_000_000_000_000_01, 0.0).GetHashCode()}"); + } + } +} \ No newline at end of file diff --git a/tests/Test/testblock.cs b/tests/Test/testblock.cs index ff9ef13..ebe58dc 100644 --- a/tests/Test/testblock.cs +++ b/tests/Test/testblock.cs @@ -472,6 +472,165 @@ public void TestChangeDynameicBlock() } + [CommandMethod("TestBack")] + public void TestBack() + { + using var tr = new DBTrans(@"C:\Users\vic\Desktop\test.dwg"); + tr.ModelSpace.GetEntities().ForEach(ent => { + ent.ForWrite(e => e.ColorIndex = 3); + }); + tr.Database.SaveAs(@"C:\Users\vic\Desktop\test.dwg", DwgVersion.Current); + + tr.ModelSpace.GetEntities().ForEach(ent => { + ent.ForWrite(e => e.ColorIndex = 4); + }); + tr.Database.SaveAs(@"C:\Users\vic\Desktop\test.dwg", DwgVersion.Current); + + } + +} + +public class BlockImportClass +{ + + [CommandMethod("CBLL")] + public void cbll() + { + string filename = @"C:\Users\vic\Desktop\Drawing1.dwg"; + using var tr = new DBTrans(); + using var tr1 = new DBTrans(filename); + //tr.BlockTable.GetBlockFrom(filename, true); + string blkdefname = SymbolUtilityServices.RepairSymbolName(SymbolUtilityServices.GetSymbolNameFromPathName(filename, "dwg"), false); + tr.Database.Insert(blkdefname, tr1.Database, false); //插入了块定义,未插入块参照 + + } + + + [CommandMethod("CBL")] + public void CombineBlocksIntoLibrary() + { + Document doc = + Application.DocumentManager.MdiActiveDocument; + Editor ed = doc.Editor; + Database destDb = doc.Database; + + // Get name of folder from which to load and import blocks + + PromptResult pr = + ed.GetString("\nEnter the folder of source drawings: "); + + if (pr.Status != PromptStatus.OK) + return; + string pathName = pr.StringResult; + + // Check the folder exists + + if (!Directory.Exists(pathName)) + { + ed.WriteMessage( + "\nDirectory does not exist: {0}", pathName + ); + return; + } + + // Get the names of our DWG files in that folder + + string[] fileNames = Directory.GetFiles(pathName, "*.dwg"); + + // A counter for the files we've imported + int imported = 0, failed = 0; + // For each file in our list + + foreach (string fileName in fileNames) + { + // Double-check we have a DWG file (probably unnecessary) + + if (fileName.EndsWith( + ".dwg", + StringComparison.InvariantCultureIgnoreCase + ) + ) + { + // Catch exceptions at the file level to allow skipping + + try + { + // Suggestion from Thorsten Meinecke... + + string destName = + SymbolUtilityServices.GetSymbolNameFromPathName( + fileName, "dwg" + ); + + // And from Dan Glassman... + + destName = + SymbolUtilityServices.RepairSymbolName( + destName, false + ); + + // Create a source database to load the DWG into + + using (Database db = new Database(false, true)) + { + // Read the DWG into our side database + + db.ReadDwgFile(fileName, FileShare.Read, true, ""); + bool isAnno = db.AnnotativeDwg; + + // Insert it into the destination database as + // a named block definition + + ObjectId btrId = destDb.Insert( + destName, + db, + false + ); + + if (isAnno) + { + // If an annotative block, open the resultant BTR + // and set its annotative definition status + + Transaction tr = + destDb.TransactionManager.StartTransaction(); + using (tr) + { + BlockTableRecord btr = + (BlockTableRecord)tr.GetObject( + btrId, + OpenMode.ForWrite + ); + btr.Annotative = AnnotativeStates.True; + tr.Commit(); + } + } + + // Print message and increment imported block counter + + ed.WriteMessage("\nImported from \"{0}\".", fileName); + imported++; + } + } + catch (System.Exception ex) + { + ed.WriteMessage( + "\nProblem importing \"{0}\": {1} - file skipped.", + fileName, ex.Message + ); + failed++; + } + } + } + + ed.WriteMessage( + "\nImported block definitions from {0} files{1} in " + + "\"{2}\" into the current drawing.", + imported, + failed > 0 ? " (" + failed + " failed)" : "", + pathName + ); + } } \ No newline at end of file diff --git a/tests/Test/testtr.cs b/tests/Test/testtr.cs deleted file mode 100644 index cb05b55..0000000 --- a/tests/Test/testtr.cs +++ /dev/null @@ -1,49 +0,0 @@ -using System.Runtime.Remoting.Metadata; - -namespace Test -{ - public class TestTrans - { - [CommandMethod("testtr")] - public void Testtr() - { - string filename = @"C:\Users\vic\Desktop\test.dwg"; - using var tr = new DBTrans(filename); - tr.ModelSpace.AddCircle(new Point3d(0, 0, 0), 20); - tr.Database.SaveAs(filename,DwgVersion.Current); - } - [CommandMethod("testifoxcommit")] - public void Testifoxcommit() - { - - using var tr = new DBTrans(); - tr.ModelSpace.AddCircle(new Point3d(0, 0, 0), 20); - tr.Abort(); - //tr.Commit(); - } - - // AOP 应用 预计示例: - // 1. 无参数 - //[AOP] - //[CommandMethod("TESTAOP")] - //public void testaop() - //{ - // // 不用 using var tr = new DBTrans(); - // var tr = DBTrans.Top; - // tr.ModelSpace.AddCircle(new Point3d(0, 0, 0), 20); - //} - - // 2. 有参数 - //[AOP("file")] - //[CommandMethod("TESTAOP")] - //public void testaop() - //{ - // // 不用 using var tr = new DBTrans(file); - // var tr = DBTrans.Top; - // tr.ModelSpace.AddCircle(new Point3d(0, 0, 0), 20); - //} - - - - } -} -- Gitee From 18bf789c23a5ca34bc00f5af43c53f5ffcb21681 Mon Sep 17 00:00:00 2001 From: vicwjb <403009819@qq.com> Date: Sat, 23 Apr 2022 23:23:38 +0800 Subject: [PATCH 188/675] =?UTF-8?q?=E4=BC=98=E5=8C=96=E5=9B=BE=E7=9A=84?= =?UTF-8?q?=E9=80=9F=E5=BA=A6=EF=BC=8C=E6=8F=90=E5=8F=96=E5=88=97=E8=A1=A8?= =?UTF-8?q?equalall=E5=87=BD=E6=95=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/IFoxCAD.Basal/ListEx.cs | 27 ++++++++++ src/IFoxCAD.Cad/Algorithms/Graph.cs | 78 +++++++++++++--------------- src/IFoxCAD.Cad/Algorithms/IGraph.cs | 6 +-- tests/Test/TestCurve.cs | 48 ++++++++++++++++- tests/Test/TestPoint.cs | 66 +++++++++++++++++++++++ 5 files changed, 179 insertions(+), 46 deletions(-) create mode 100644 src/IFoxCAD.Basal/ListEx.cs diff --git a/src/IFoxCAD.Basal/ListEx.cs b/src/IFoxCAD.Basal/ListEx.cs new file mode 100644 index 0000000..aebcb71 --- /dev/null +++ b/src/IFoxCAD.Basal/ListEx.cs @@ -0,0 +1,27 @@ + +namespace IFoxCAD.Basal; + +public static class ListEx +{ + public static bool EqualsAll(this IList a, IList b) + { + return EqualsAll(a, b, null); + // there is a slight performance gain in passing null here. + // It is how it is done in other parts of the framework. + } + + public static bool EqualsAll(this IList a!!, IList b!!, IEqualityComparer? comparer) + { + if (a.Count != b.Count) + return false; + + comparer ??= EqualityComparer.Default; + + for (int i = 0; i < a.Count; i++) + { + if (!comparer.Equals(a[i], b[i])) + return false; + } + return true; + } +} diff --git a/src/IFoxCAD.Cad/Algorithms/Graph.cs b/src/IFoxCAD.Cad/Algorithms/Graph.cs index 0001b6f..bd6cc0f 100644 --- a/src/IFoxCAD.Cad/Algorithms/Graph.cs +++ b/src/IFoxCAD.Cad/Algorithms/Graph.cs @@ -6,7 +6,7 @@ namespace IFoxCAD.Cad; /// 无权无向图实现 /// IEnumerable 枚举所有顶点; /// -internal sealed class Graph : IGraph, IEnumerable +public sealed class Graph : IGraph, IEnumerable { #region 字段及属性 /// @@ -27,7 +27,7 @@ internal sealed class Graph : IGraph, IEnumerable /// /// 目前点增加点的顺序号,这个点号不随删点而减少的 /// - static int insertCount; + public int insertCount; #endregion #region 构造函数 @@ -215,7 +215,7 @@ public List GetCurves(List graphVertices) if (edge is not null) curves.Add(edge.TargetEdge); } - var lastedge = GetEdge(graphVertices[graphVertices.Count - 1], graphVertices[0]); + var lastedge = GetEdge(graphVertices[^1], graphVertices[0]); if (lastedge is not null) curves.Add(lastedge.TargetEdge); @@ -291,7 +291,7 @@ public string ToReadable() /// 邻接表图实现的顶点; /// IEnumerable 枚举所有邻接点; /// -internal sealed class GraphVertex : IGraphVertex, IEquatable, IComparable, IComparable +public sealed class GraphVertex : IGraphVertex, IEquatable, IComparable, IComparable { #region 属性 public Point3d Data { get; set; } @@ -386,7 +386,7 @@ public int CompareTo(object obj) /// /// 无向图中边的定义 /// -internal sealed class GraphEdge : IEdge, IEquatable +public sealed class GraphEdge : IEdge, IEquatable { #region 属性 public IGraphVertex TargetVertex { get; set; } @@ -449,13 +449,16 @@ public override int GetHashCode() /// /// 深度优先搜索; /// -internal sealed class DepthFirst +public sealed class DepthFirst { #region 公共方法 /// /// 存储所有的边 /// + public List> Curve3ds { get; } = new(); + private HashSet Curved { get; } = new(); + /// /// 找出所有的路径 @@ -466,7 +469,7 @@ public void FindAll(IGraph graph) var ge = graph.VerticesAsEnumberable.GetEnumerator(); while (ge.MoveNext()) Dfs(graph, new List { ge.Current }); - } + } #endregion #region 内部方法 @@ -485,6 +488,7 @@ void Dfs(IGraph graph, List visited) for (int i = 0; i < adjlist.Count; i++) { nextNode = adjlist[i]; + // 如果下一个点未遍历过 if (!visited.Contains(nextNode)) { @@ -493,14 +497,21 @@ void Dfs(IGraph graph, List visited) sub.AddRange(visited); Dfs(graph, sub); } + // 如果下一点遍历过,并且路径大于2,说明已经找到起点 else if (visited.Count > 2 && nextNode.Equals(visited[^1])) { // 将重复的路径进行过滤,并把新的路径存入结果 - var cur = DepthFirst.RotateToSmallest(visited); - var inv = DepthFirst.Invert(cur); - if (IsNew(cur) && IsNew(inv)) + var cur = RotateToSmallest(visited); + var inv = Invert(cur); + + var curstr = Gethashstring(cur,inv); + //Env.Print(curstr); + if (Isnew(curstr)) + { Curve3ds.Add(cur); + Curved.Add(curstr.Item1); + } } } } @@ -528,40 +539,23 @@ static List Invert(List lst) return RotateToSmallest(tmp); } - /// - /// 比较两个列表是否相等 - /// - /// - /// - /// - static bool Equals(List a, List b) + static (string,string) Gethashstring(List pathone, List pathtwo) { - bool ret = (a[0] == b[0]) && (a.Count == b.Count); - - for (int i = 1; ret && (i < a.Count); i++) - if (!a[i].Equals(b[i])) - ret = false; - - return ret; + var one = new string[pathone.Count]; + var two = new string[pathtwo.Count]; + for (int i = 0; i < pathone.Count; i++) + { + one[i] = pathone[i].Index.ToString(); + two[i] = pathtwo[i].Index.ToString(); + } + return (string.Join("-", one), string.Join("-", two)); } - /// - /// 是否已经是存在闭合环 - /// - /// - /// - bool IsNew(List path) + bool Isnew((string,string) path) { - bool ret = true; - for (int i = 0; i < Curve3ds.Count; i++) - { - if (DepthFirst.Equals(Curve3ds[i], path)) - { - ret = false; - break; - } - } - return ret; - } - #endregion + return !Curved.Contains(path.Item1) && !Curved.Contains(path.Item2); + } + + +#endregion } \ No newline at end of file diff --git a/src/IFoxCAD.Cad/Algorithms/IGraph.cs b/src/IFoxCAD.Cad/Algorithms/IGraph.cs index ca832cc..8bf0ca6 100644 --- a/src/IFoxCAD.Cad/Algorithms/IGraph.cs +++ b/src/IFoxCAD.Cad/Algorithms/IGraph.cs @@ -3,7 +3,7 @@ namespace IFoxCAD.Cad; /// /// 无向图 /// -internal interface IGraph +public interface IGraph { /// /// 顶点的数量 @@ -61,7 +61,7 @@ internal interface IGraph /// 无向图顶点 /// /// 顶点数据类型 -internal interface IGraphVertex +public interface IGraphVertex { /// /// 顶点的键 @@ -77,7 +77,7 @@ internal interface IGraphVertex /// /// 无向图边 /// -internal interface IEdge +public interface IEdge { /// /// 边 diff --git a/tests/Test/TestCurve.cs b/tests/Test/TestCurve.cs index 0e59674..5df50e0 100644 --- a/tests/Test/TestCurve.cs +++ b/tests/Test/TestCurve.cs @@ -24,16 +24,62 @@ public void TestPointInDict() [CommandMethod("testgraph")] public void TestGraph1() { + using var tr = new DBTrans(); var ents = Env.Editor.SSGet()?.Value?.GetEntities(); if (ents == null) return; var res = ents.GetAllCycle(); res.ForEach((i, t) => t.ForWrite(e => e.ColorIndex = i + 1)); - using var tr = new DBTrans(); + tr.CurrentSpace.AddEntity(res); } + + [CommandMethod("testgraphspeed")] + public void TestGraphspeed() + { + using var tr = new DBTrans(); + var ents = Env.Editor.SSGet()?.Value?.GetEntities(); + if (ents == null) + return; + + + var graph = new IFoxCAD.Cad.Graph(); // 为了调试先把图的访问改为internal + + foreach (var curve in ents) + { + + graph.AddEdge(curve.GetGeCurve()); + + + } + //新建 dfs + var dfs = new DepthFirst(); +#if true + Tools.TestTimes2(100, "new", () => { + // 查询全部的 闭合环 + dfs.FindAll(graph); + }); + Tools.TestTimes2(1000, "new", () => { + // 查询全部的 闭合环 + dfs.FindAll(graph); + }); +#else + Tools.TestTimes2(100, "old", () => { + // 查询全部的 闭合环 + dfs.FindAll(graph); + }); + Tools.TestTimes2(1000, "old", () => { + // 查询全部的 闭合环 + dfs.FindAll(graph); + }); +#endif + //res.ForEach((i, t) => t.ForWrite(e => e.ColorIndex = i + 1)); + + //tr.CurrentSpace.AddEntity(res); + + } } diff --git a/tests/Test/TestPoint.cs b/tests/Test/TestPoint.cs index dfe7de5..794ba38 100644 --- a/tests/Test/TestPoint.cs +++ b/tests/Test/TestPoint.cs @@ -44,5 +44,71 @@ public void TestPoint3d() Env.Print($"20位小数的hash:{new Point3d(0.000_000_000_000_000_000_01, 0.000_000_000_000_000_000_02, 0.0).GetHashCode()}"); Env.Print($"20位小数的hash:{new Point3d(0.000_000_000_000_000_000_01, 0.000_000_000_000_000_000_01, 0.0).GetHashCode()}"); } + + [CommandMethod("Testlistequalspeed")] + public void Testlistequalspeed() + { + var lst1 = new List { 1, 2, 3, 4 }; + var lst2 = new List { 1, 2, 3, 4}; + lst1.EqualsAll(null); + Tools.TestTimes2(1000000, "eqaulspeed:", () => { + lst1.EqualsAll(lst2); + }); + Tools.TestTimes2(1000000, "eqaulspeed2:", () => { + lst1.EqualsAll2(lst2); + }); + + } + + [CommandMethod("Testcontains")] + public void Testcontains() + { + // test list and dict contains speed + var lst = new List { 1, 2, 3, 4 , 5,6,7,8,9,10, 11,12,13,14,15,16,17,18,19,20}; + var hashset = new HashSet { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20 }; + var dict = new Dictionary + { + { 1, 0 }, + { 2, 1 }, + { 3, 2 }, + { 4, 3 }, + { 5, 4 }, + { 6, 5 }, + { 7, 6 }, + { 8, 7 }, + { 9, 8 }, + { 10, 9 }, + { 11, 11 }, + { 12, 12 }, + { 13, 13 }, + { 14, 14 }, + { 15, 15 }, + { 16, 16 }, + { 17, 17 }, + { 18, 18 }, + { 19, 19 }, + { 20, 20 }, + }; + + Tools.TestTimes2(100_0000, "list:", () => { + lst.Contains(20); + }); + + Tools.TestTimes2(100_0000, "hashset:", () => { + hashset.Contains(20); + }); + + Tools.TestTimes2(100_0000, "dict:", () => { + dict.ContainsKey(20); + }); + + } + + + + + } + + } \ No newline at end of file -- Gitee From 33273c6edbfde6126cda4a3fc1cd31610418024f Mon Sep 17 00:00:00 2001 From: vicwjb <403009819@qq.com> Date: Tue, 26 Apr 2022 23:38:06 +0800 Subject: [PATCH 189/675] =?UTF-8?q?=E4=BC=98=E5=8C=96=E5=9B=BE=E7=9A=84?= =?UTF-8?q?=E9=80=9F=E5=BA=A6=EF=BC=8C=E5=A2=9E=E5=8A=A0LinkedHashSet?= =?UTF-8?q?=E5=92=8CLinkedhashMap?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- IFoxCAD.sln | 6 + src/IFoxCAD.Basal/LinkedHashMap.cs | 171 ++++++++++++++++ src/IFoxCAD.Basal/LinkedHashSet.cs | 221 +++++++++++++++++++++ src/IFoxCAD.Basal/LoopList.cs | 5 + src/IFoxCAD.Cad/Algorithms/Graph.cs | 149 +++++++++++--- src/IFoxCAD.Cad/Algorithms/IGraph.cs | 7 +- src/IFoxCAD.Cad/ExtensionMethod/CurveEx.cs | 2 +- tests/Test/TestCurve.cs | 12 +- tests/Test/TestLoop.cs | 37 ++-- tests/Test/TestPoint.cs | 4 +- tests/TestConsole/Program.cs | 49 +++++ tests/TestConsole/TestConsole.csproj | 14 ++ 12 files changed, 625 insertions(+), 52 deletions(-) create mode 100644 src/IFoxCAD.Basal/LinkedHashMap.cs create mode 100644 src/IFoxCAD.Basal/LinkedHashSet.cs create mode 100644 tests/TestConsole/Program.cs create mode 100644 tests/TestConsole/TestConsole.csproj diff --git a/IFoxCAD.sln b/IFoxCAD.sln index 6744427..7d79a63 100644 --- a/IFoxCAD.sln +++ b/IFoxCAD.sln @@ -11,6 +11,8 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "IFoxCAD.WPF", "src\IFoxCAD. EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "IFoxCAD.Basal", "src\IFoxCAD.Basal\IFoxCAD.Basal.csproj", "{40BF07C4-100A-4810-A27B-4587CFB38E6E}" EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "TestConsole", "tests\TestConsole\TestConsole.csproj", "{E2873F0B-CAD2-45E8-8FF0-C05C0C6AD955}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -33,6 +35,10 @@ Global {40BF07C4-100A-4810-A27B-4587CFB38E6E}.Debug|Any CPU.Build.0 = Debug|Any CPU {40BF07C4-100A-4810-A27B-4587CFB38E6E}.Release|Any CPU.ActiveCfg = Release|Any CPU {40BF07C4-100A-4810-A27B-4587CFB38E6E}.Release|Any CPU.Build.0 = Release|Any CPU + {E2873F0B-CAD2-45E8-8FF0-C05C0C6AD955}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {E2873F0B-CAD2-45E8-8FF0-C05C0C6AD955}.Debug|Any CPU.Build.0 = Debug|Any CPU + {E2873F0B-CAD2-45E8-8FF0-C05C0C6AD955}.Release|Any CPU.ActiveCfg = Release|Any CPU + {E2873F0B-CAD2-45E8-8FF0-C05C0C6AD955}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE diff --git a/src/IFoxCAD.Basal/LinkedHashMap.cs b/src/IFoxCAD.Basal/LinkedHashMap.cs new file mode 100644 index 0000000..0e6232c --- /dev/null +++ b/src/IFoxCAD.Basal/LinkedHashMap.cs @@ -0,0 +1,171 @@ +namespace IFoxCAD.Basal; + + +/// +/// A least-recently-used cache stored like a dictionary. +/// +/// +/// The type of the key to the cached item +/// +/// +/// The type of the cached item. +/// +/// +/// Derived from https://stackoverflow.com/a/3719378/240845 +/// https://stackoverflow.com/users/240845/mheyman +/// +public class LinkedHashMap +{ + private readonly Dictionary> cacheMap = new(); + + private readonly LinkedList lruList = new(); + + private readonly Action? dispose; + + /// + /// Initializes a new instance of the + /// class. + /// + /// + /// Maximum number of elements to cache. + /// + /// + /// When elements cycle out of the cache, disposes them. May be null. + /// + public LinkedHashMap(int capacity, Action? dispose = null) + { + this.Capacity = capacity; + this.dispose = dispose; + } + + /// + /// Gets the capacity of the cache. + /// + public int Capacity { get; } + + /// Gets the value associated with the specified key. + /// + /// The key of the value to get. + /// + /// + /// When this method returns, contains the value associated with the specified + /// key, if the key is found; otherwise, the default value for the type of the + /// parameter. This parameter is passed + /// uninitialized. + /// + /// + /// true if the + /// contains an element with the specified key; otherwise, false. + /// + public bool TryGetValue(TKey key, out TValue? value) + { + lock (this.cacheMap) + { + if (this.cacheMap.TryGetValue(key, out LinkedListNode.MapItem> node)) + { + value = node.Value.Value; + this.lruList.Remove(node); + this.lruList.AddLast(node); + return true; + } + + value = default; + return false; + } + } + + /// + /// Looks for a value for the matching . If not found, + /// calls to retrieve the value and add it to + /// the cache. + /// + /// + /// The key of the value to look up. + /// + /// + /// Generates a value if one isn't found. + /// + /// + /// The requested value. + /// + public TValue Get(TKey key, Func valueGenerator) + { + lock (this.cacheMap) + { + TValue value; + if (this.cacheMap.TryGetValue(key, out LinkedListNode.MapItem> node)) + { + value = node.Value.Value; + this.lruList.Remove(node); + this.lruList.AddLast(node); + } + else + { + value = valueGenerator(); + if (this.cacheMap.Count >= this.Capacity) + { + this.RemoveFirst(); + } + + MapItem cacheItem = new(key, value); + node = new LinkedListNode(cacheItem); + this.lruList.AddLast(node); + this.cacheMap.Add(key, node); + } + + return value; + } + } + + /// + /// Adds the specified key and value to the dictionary. + /// + /// + /// The key of the element to add. + /// + /// + /// The value of the element to add. The value can be null for reference types. + /// + public void Add(TKey key, TValue value) + { + lock (this.cacheMap) + { + if (this.cacheMap.Count >= this.Capacity) + { + this.RemoveFirst(); + } + + MapItem cacheItem = new(key, value); + LinkedListNode node = new(cacheItem); + this.lruList.AddLast(node); + this.cacheMap.Add(key, node); + } + } + + private void RemoveFirst() + { + // Remove from LRUPriority + LinkedListNode node = this.lruList.First; + this.lruList.RemoveFirst(); + + // Remove from cache + this.cacheMap.Remove(node.Value.Key); + + // dispose + this.dispose?.Invoke(node.Value.Value); + } + + private class MapItem + { + public MapItem(TKey k, TValue v) + { + this.Key = k; + this.Value = v; + } + + public TKey Key { get; } + + public TValue Value { get; } + } +} + diff --git a/src/IFoxCAD.Basal/LinkedHashSet.cs b/src/IFoxCAD.Basal/LinkedHashSet.cs new file mode 100644 index 0000000..8659c24 --- /dev/null +++ b/src/IFoxCAD.Basal/LinkedHashSet.cs @@ -0,0 +1,221 @@ +namespace IFoxCAD.Basal; + +public class LinkedHashSet : ICollection where T : IComparable +{ + private readonly IDictionary> m_Dictionary; + private readonly LoopList m_LinkedList; + + public LinkedHashSet() + { + m_Dictionary = new Dictionary>(); + m_LinkedList = new LoopList(); + } + + public LoopListNode? First => m_LinkedList.First; + + public LoopListNode? Last => m_LinkedList.Last; + + public LoopListNode? MinNode { get; set; } + + public bool Add(T item) + { + if (m_Dictionary.ContainsKey(item)) + return false; + var node = m_LinkedList.AddLast(item); + m_Dictionary.Add(item, node); + + if (MinNode is null) + { + MinNode = node; + } + else + { + if (item.CompareTo(MinNode.Value) < 0) + { + MinNode = node; + } + } + + + + return true; + } + + void ICollection.Add(T item) + { + Add(item); + } + + public LoopListNode AddFirst(T value) + { + if (m_Dictionary.ContainsKey(value)) + { + return m_Dictionary[value]; + } + var node = m_LinkedList.AddFirst(value); + m_Dictionary.Add(value, node); + if (MinNode is null) + { + MinNode = node; + } + else + { + if (value.CompareTo(MinNode.Value) < 0) + { + MinNode = node; + } + } + return node; + } + + public void AddRange(IEnumerable collection) + { + foreach (var item in collection) + { + Add(item); + } + } + + + public void Clear() + { + m_LinkedList.Clear(); + m_Dictionary.Clear(); + } + + public bool Remove(T item) + { + bool found = m_Dictionary.TryGetValue(item, out LoopListNode node); + if (!found) return false; + m_Dictionary.Remove(item); + m_LinkedList.Remove(node); + return true; + } + + public int Count + { + get { return m_Dictionary.Count; } + } + + public void For(LoopListNode from, Action action) + { + var first = from; + var last = from; + if(first is null) return; + + for (int i = 0; i < Count; i++) + { + + action.Invoke(i,first!.Value, last!.Value); + first = first.Next; + last = last.Previous; + } + } + + public List ToList() + { + return m_LinkedList.ToList(); + } + + public IEnumerator GetEnumerator() + { + return m_LinkedList.GetEnumerator(); + } + + IEnumerator IEnumerable.GetEnumerator() + { + return GetEnumerator(); + } + + + public bool Contains(T item) + { + return m_Dictionary.ContainsKey(item); + } + + public void CopyTo(T[] array, int arrayIndex) + { + //m_LinkedList.CopyTo(array, arrayIndex); + return; + } + + public bool SetFirst(LoopListNode node) + { + return m_LinkedList.SetFirst(node); + } + + public LinkedHashSet Clone() + { + var newset = new LinkedHashSet(); + foreach (var item in this) + { + newset.Add(item); + } + return newset; + } + + public virtual bool IsReadOnly + { + get { return m_Dictionary.IsReadOnly; } + } + + public override string ToString() + { + return m_LinkedList.ToString(); + } + + public void UnionWith(IEnumerable other) + { + throw GetNotSupportedDueToSimplification(); + } + + public void IntersectWith(IEnumerable other) + { + throw GetNotSupportedDueToSimplification(); + } + + public void ExceptWith(IEnumerable other) + { + throw GetNotSupportedDueToSimplification(); + } + + public bool IsSubsetOf(IEnumerable other) + { + throw GetNotSupportedDueToSimplification(); + } + + public void SymmetricExceptWith(IEnumerable other) + { + throw GetNotSupportedDueToSimplification(); + } + + public bool IsSupersetOf(IEnumerable other) + { + throw GetNotSupportedDueToSimplification(); + } + + public bool IsProperSupersetOf(IEnumerable other) + { + throw GetNotSupportedDueToSimplification(); + } + + public bool IsProperSubsetOf(IEnumerable other) + { + throw GetNotSupportedDueToSimplification(); + } + + public bool Overlaps(IEnumerable other) + { + throw GetNotSupportedDueToSimplification(); + } + + public bool SetEquals(IEnumerable other) + { + throw GetNotSupportedDueToSimplification(); + } + + private static Exception GetNotSupportedDueToSimplification() + { + return new NotSupportedException("This method is not supported due to simplification of example code."); + } +} diff --git a/src/IFoxCAD.Basal/LoopList.cs b/src/IFoxCAD.Basal/LoopList.cs index e15940e..b22b1f8 100644 --- a/src/IFoxCAD.Basal/LoopList.cs +++ b/src/IFoxCAD.Basal/LoopList.cs @@ -86,6 +86,9 @@ public class LoopList : IEnumerable, IFormattable /// public LoopListNode? Last => First?.Previous; + + + #endregion #region 构造 @@ -641,6 +644,8 @@ public IEnumerable> GetNodes(LoopListNode from) } } + + /// /// 获取节点的查询器 /// diff --git a/src/IFoxCAD.Cad/Algorithms/Graph.cs b/src/IFoxCAD.Cad/Algorithms/Graph.cs index bd6cc0f..dd06876 100644 --- a/src/IFoxCAD.Cad/Algorithms/Graph.cs +++ b/src/IFoxCAD.Cad/Algorithms/Graph.cs @@ -24,10 +24,31 @@ public sealed class Graph : IGraph, IEnumerable readonly Dictionary vertexs = new(); public int VerticesCount => vertices.Count; + + /// + /// Returns a reference vertex. + /// Time complexity: O(1). + /// + private IGraphVertex? referenceVertex + { + get + { + using (var enumerator = vertexs.GetEnumerator()) + { + if (enumerator.MoveNext()) + { + return enumerator.Current.Value; + } + } + + return null; + } + } + IGraphVertex? IGraph.ReferenceVertex => referenceVertex; /// /// 目前点增加点的顺序号,这个点号不随删点而减少的 /// - public int insertCount; + private int insertCount; #endregion #region 构造函数 @@ -106,11 +127,12 @@ public void RemoveVertex(Point3d pt) // 删除其他顶点的邻接边表的指向vertex的边 foreach (var item in edges.Values) { - foreach (var edge in item) - { - if (vertex.Equals(edge.TargetVertex)) - item.Remove(edge); - } + item.RemoveWhere(x => vertex.Equals(x.TargetVertex)); + //foreach (var edge in item) + //{ + // if (vertex.Equals(edge.TargetVertex)) + // item.Remove(edge); + //} } vertexs.Remove(str); } @@ -161,7 +183,7 @@ public bool HasEdge(IGraphVertex source, IGraphVertex dest) foreach (var item in edges[source]) { - if (item.TargetVertex == dest) + if (item.TargetVertex.Equals(dest)) return item; } return null; @@ -452,11 +474,14 @@ public override int GetHashCode() public sealed class DepthFirst { #region 公共方法 - /// - /// 存储所有的边 - /// - +/// +/// 存储所有的边 +/// +#if true + public List> Curve3ds { get; } = new(); +#else public List> Curve3ds { get; } = new(); +#endif private HashSet Curved { get; } = new(); @@ -466,35 +491,86 @@ public sealed class DepthFirst /// 图 public void FindAll(IGraph graph) { - var ge = graph.VerticesAsEnumberable.GetEnumerator(); - while (ge.MoveNext()) - Dfs(graph, new List { ge.Current }); + var total = new HashSet(); + //var graphtmp = graph.Clone(); + foreach (var item in graph.VerticesAsEnumberable) + { + + + Dfs(graph, new LinkedHashSet { item },total); + //graph.RemoveVertex(graphtmp.ReferenceVertex!.Data); + total.Add(item); + } + + + + + + + } - #endregion +#endregion - #region 内部方法 +#region 内部方法 /// /// 递归 DFS; /// /// 图 /// 已经遍历的路径 +#if true + void Dfs(IGraph graph, LinkedHashSet visited, HashSet totalVisited) + { + var adjlist = graph.GetAdjacencyList(/*startNode*/ visited.First!.Value); // O(1) + foreach (var nextNode in adjlist) // O(n) + { + if (totalVisited.Contains(nextNode)) + { + continue; + } + // 如果下一个点未遍历过 + if (!visited.Contains(nextNode)) // O(1) + { + // 将下一点加入路径集合,并进行下一次递归 + var sub = new LinkedHashSet { nextNode }; + sub.AddRange(visited); // O(n) + Dfs(graph, sub,totalVisited); + } + // 如果下一点遍历过,并且路径大于2,说明已经找到起点 + else if (visited.Count > 2 && nextNode.Equals(visited.Last!.Value)) + { + // 将重复的路径进行过滤,并把新的路径存入结果 + var curstr = Gethashstring(visited); // O(n) + if (Isnew(curstr)) // O(1) + { + Curve3ds.Add(visited); + Curved.Add(curstr.Item1); + } + } + } + } + + + + +#else + void Dfs(IGraph graph, List visited) { var startNode = visited[0]; IGraphVertex nextNode; List sub; - var adjlist = graph.GetAdjacencyList(startNode).ToList(); - for (int i = 0; i < adjlist.Count; i++) + var adjlist = graph.GetAdjacencyList(startNode).ToList(); // O(n) + for (int i = 0; i < adjlist.Count; i++) // O(n) { nextNode = adjlist[i]; // 如果下一个点未遍历过 - if (!visited.Contains(nextNode)) + if (!visited.Contains(nextNode)) // O(n) { // 将下一点加入路径集合,并进行下一次递归 sub = new List { nextNode }; - sub.AddRange(visited); + sub.AddRange(visited); // O(n) Dfs(graph, sub); } @@ -502,10 +578,10 @@ void Dfs(IGraph graph, List visited) else if (visited.Count > 2 && nextNode.Equals(visited[^1])) { // 将重复的路径进行过滤,并把新的路径存入结果 - var cur = RotateToSmallest(visited); - var inv = Invert(cur); - - var curstr = Gethashstring(cur,inv); + var cur = RotateToSmallest(visited); // O(n) + var inv = Invert(cur,cur[0]); // O(n) + + var curstr = Gethashstring(cur,inv); //Env.Print(curstr); if (Isnew(curstr)) { @@ -515,6 +591,14 @@ void Dfs(IGraph graph, List visited) } } } +#endif + + + + + + + /// /// 将列表旋转到最小的值为列表起点 @@ -532,11 +616,12 @@ static List RotateToSmallest(List lst) /// /// /// - static List Invert(List lst) + static List Invert(List lst, IGraphVertex vertex) { var tmp = lst.ToList(); tmp.Reverse(); - return RotateToSmallest(tmp); + var index = tmp.IndexOf(vertex); + return tmp.Skip(index).Concat(lst.Take(index)).ToList(); } static (string,string) Gethashstring(List pathone, List pathtwo) @@ -551,6 +636,18 @@ static List Invert(List lst) return (string.Join("-", one), string.Join("-", two)); } + static (string, string) Gethashstring(LinkedHashSet path) + { + var one = new string[path.Count]; + var two = new string[path.Count]; + path.For(path.MinNode!, (i, ver1, ver2) => { + one[i] = ver1.Index.ToString(); + two[i] = ver2.Index.ToString(); + }); + return (string.Join("-", one), string.Join("-", two)); + } + + bool Isnew((string,string) path) { return !Curved.Contains(path.Item1) && !Curved.Contains(path.Item2); diff --git a/src/IFoxCAD.Cad/Algorithms/IGraph.cs b/src/IFoxCAD.Cad/Algorithms/IGraph.cs index 8bf0ca6..73547b7 100644 --- a/src/IFoxCAD.Cad/Algorithms/IGraph.cs +++ b/src/IFoxCAD.Cad/Algorithms/IGraph.cs @@ -55,13 +55,18 @@ public interface IGraph /// /// HashSet GetAdjacencyEdge(IGraphVertex vertex); + IGraphVertex? ReferenceVertex { get; } + + void RemoveVertex(Point3d pt); + void RemoveEdge(Curve3d curve); + } /// /// 无向图顶点 /// /// 顶点数据类型 -public interface IGraphVertex +public interface IGraphVertex : IComparable { /// /// 顶点的键 diff --git a/src/IFoxCAD.Cad/ExtensionMethod/CurveEx.cs b/src/IFoxCAD.Cad/ExtensionMethod/CurveEx.cs index 8058315..4b9b576 100644 --- a/src/IFoxCAD.Cad/ExtensionMethod/CurveEx.cs +++ b/src/IFoxCAD.Cad/ExtensionMethod/CurveEx.cs @@ -70,7 +70,7 @@ public static IEnumerable GetAllCycle(this IEnumerable curves) var res = new List(); foreach (var item in dfs.Curve3ds) { - var curve = graph.GetCurves(item).ToArray(); + var curve = graph.GetCurves(item.ToList()).ToArray(); var comcur = new CompositeCurve3d(curve).ToCurve(); if (comcur is not null) { diff --git a/tests/Test/TestCurve.cs b/tests/Test/TestCurve.cs index 5df50e0..02c07a4 100644 --- a/tests/Test/TestCurve.cs +++ b/tests/Test/TestCurve.cs @@ -28,11 +28,15 @@ public void TestGraph1() var ents = Env.Editor.SSGet()?.Value?.GetEntities(); if (ents == null) return; + Tools.TestTimes2(1, "new", () => { + + var res = ents.GetAllCycle(); - - res.ForEach((i, t) => t.ForWrite(e => e.ColorIndex = i + 1)); - - tr.CurrentSpace.AddEntity(res); + + //res.ForEach((i, t) => t.ForWrite(e => e.ColorIndex = i + 1)); + Env.Print(res.Count()); + tr.CurrentSpace.AddEntity(res); + }); } diff --git a/tests/Test/TestLoop.cs b/tests/Test/TestLoop.cs index b6530ef..8992df9 100644 --- a/tests/Test/TestLoop.cs +++ b/tests/Test/TestLoop.cs @@ -8,26 +8,29 @@ public class TestLoop [CommandMethod("testloop")] public void Testloop() { - for (int i = 0; i < 10000000; i++) + var loop = new LoopList { - var loop = new LoopList(); - for (int j = 0; j < 100000; j++) - { - loop.Add(j); - } - //loop.Add(1); - //loop.Add(2); - //loop.Add(3); - //loop.Add(4); - - //loop.Add(5); - - - loop.Clear(); - - } + 0, + 1, + 2, + 3, + 4, + 5 + }; + + + Env.Print(loop); + + loop.SetFirst(loop.Last); + Env.Print(loop); + Env.Print(loop.Min()); + loop.SetFirst(new LoopListNode (loop.Min() ,loop)); + Env.Print(loop); + + + } } } diff --git a/tests/Test/TestPoint.cs b/tests/Test/TestPoint.cs index 794ba38..9d4ea71 100644 --- a/tests/Test/TestPoint.cs +++ b/tests/Test/TestPoint.cs @@ -54,9 +54,7 @@ public void Testlistequalspeed() Tools.TestTimes2(1000000, "eqaulspeed:", () => { lst1.EqualsAll(lst2); }); - Tools.TestTimes2(1000000, "eqaulspeed2:", () => { - lst1.EqualsAll2(lst2); - }); + } diff --git a/tests/TestConsole/Program.cs b/tests/TestConsole/Program.cs new file mode 100644 index 0000000..bc65e5c --- /dev/null +++ b/tests/TestConsole/Program.cs @@ -0,0 +1,49 @@ +// See https://aka.ms/new-console-template for more information +using System; +using System.Linq; +using IFoxCAD.Basal; + +Console.WriteLine("Hello, World!"); +var loop = new LoopList + { + 0, + 1, + 2, + 3, + 4, + 5 + }; + + +loop.SetFirst(loop.Last!); +Console.WriteLine(loop); +Console.WriteLine(loop.Min()); + +//loop.SetFirst(new LoopListNode(loop.Min(), loop)); + +//Console.WriteLine(loop); + + + +var linkset = new LinkedHashSet(); +linkset.Add(0); +linkset.Add(1); +linkset.Add(2); +linkset.Add(3); +linkset.Add(4); +linkset.Add(5); + +linkset.SetFirst(linkset.Last!); + + +Console.WriteLine(linkset); + +linkset.SetFirst(linkset.MinNode!); + +Console.WriteLine(linkset); + +linkset.For(linkset.First!, (i, next, pre) => { + Console.WriteLine($"{i} - ({next},{pre})"); + +}); +Console.ReadKey(); \ No newline at end of file diff --git a/tests/TestConsole/TestConsole.csproj b/tests/TestConsole/TestConsole.csproj new file mode 100644 index 0000000..0a95101 --- /dev/null +++ b/tests/TestConsole/TestConsole.csproj @@ -0,0 +1,14 @@ + + + + Exe + net45 + enable + preview + + + + + + + -- Gitee From 4f2e973faf03a3897a66a70c0134c9d3a319573a Mon Sep 17 00:00:00 2001 From: liuqihong <540762622@qq.com> Date: Wed, 27 Apr 2022 18:21:05 +0800 Subject: [PATCH 190/675] =?UTF-8?q?=E6=8F=90=E4=BA=A4=E8=AE=BA=E6=96=87?= =?UTF-8?q?=E7=9A=84=E9=97=AD=E5=90=88=E8=BE=B9=E7=95=8C=E7=AE=97=E6=B3=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/IFoxCAD.Basal/LoopList.cs | 15 +- src/IFoxCAD.Cad/Algorithms/Rect.cs | 8 +- src/IFoxCAD.Cad/ExtensionMethod/Bo.cs | 156 ----- src/IFoxCAD.Cad/ExtensionMethod/CurveEx.cs | 3 - src/IFoxCAD.Cad/ExtensionMethod/PointEx.cs | 19 +- .../Edge.cs" | 662 +++++++++--------- .../TestTopo.cs" | 22 + .../Topo.cs" | 446 ++++++++++++ 8 files changed, 848 insertions(+), 483 deletions(-) delete mode 100644 src/IFoxCAD.Cad/ExtensionMethod/Bo.cs rename src/IFoxCAD.Cad/ExtensionMethod/Edge.cs => "src/IFoxCAD.Cad/ExtensionMethod/\351\227\255\345\220\210\350\276\271\347\225\214\347\256\227\346\263\225/Edge.cs" (51%) create mode 100644 "src/IFoxCAD.Cad/ExtensionMethod/\351\227\255\345\220\210\350\276\271\347\225\214\347\256\227\346\263\225/TestTopo.cs" create mode 100644 "src/IFoxCAD.Cad/ExtensionMethod/\351\227\255\345\220\210\350\276\271\347\225\214\347\256\227\346\263\225/Topo.cs" diff --git a/src/IFoxCAD.Basal/LoopList.cs b/src/IFoxCAD.Basal/LoopList.cs index b22b1f8..8693294 100644 --- a/src/IFoxCAD.Basal/LoopList.cs +++ b/src/IFoxCAD.Basal/LoopList.cs @@ -1,5 +1,7 @@ namespace IFoxCAD.Basal; +#line hidden //调试的时候跳过它 + /// /// 环链表节点 /// @@ -171,10 +173,10 @@ public void Clear() } /// - /// 从头遍历_非迭代器 + /// 从头遍历_非迭代器(此处和通用ForEach冲突,所以内部用) /// /// - public void ForEach(Func, bool> action) + void ForEach(Func, bool> action) { var node = First; if (node is null) @@ -725,7 +727,10 @@ public string ToString(string? format, IFormatProvider? formatProvider = null) #endregion #region ICloneable - /* 山人说无法分辨ICloneable接口是深浅克隆,因此不要在泛型模板实现克隆函数,让用户自己来 + /* 山人说无法分辨ICloneable接口是深浅克隆, + * 因此不要在泛型模板实现克隆函数, + * 让用户自己来 new(xx)实现浅克隆,所以也不提供Clone()了 + * * 因此约定了:CopyTo(T,index)是深克隆;MemberwiseClone()是浅克隆; * public object Clone() * { @@ -738,4 +743,6 @@ public string ToString(string? format, IFormatProvider? formatProvider = null) * } */ #endregion -} \ No newline at end of file +} + +#line default \ No newline at end of file diff --git a/src/IFoxCAD.Cad/Algorithms/Rect.cs b/src/IFoxCAD.Cad/Algorithms/Rect.cs index 84bae17..62e43a0 100644 --- a/src/IFoxCAD.Cad/Algorithms/Rect.cs +++ b/src/IFoxCAD.Cad/Algorithms/Rect.cs @@ -230,16 +230,12 @@ public static implicit operator Rect(System.Drawing.Rectangle rect) #region 包含 public bool Contains(Point2d Point2d) { - return ContainsInternal(Point2d.X, Point2d.Y); + return Contains(Point2d.X, Point2d.Y); } public bool Contains(double x, double y) - { - return ContainsInternal(x, y); - } - private bool ContainsInternal(double x, double y) { return _X <= x && x <= _Right && - _Y <= y && y <= _Top; + _Y <= y && y <= _Top; } /// diff --git a/src/IFoxCAD.Cad/ExtensionMethod/Bo.cs b/src/IFoxCAD.Cad/ExtensionMethod/Bo.cs deleted file mode 100644 index df05118..0000000 --- a/src/IFoxCAD.Cad/ExtensionMethod/Bo.cs +++ /dev/null @@ -1,156 +0,0 @@ -#if true -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; - -namespace IFoxCAD.Cad.ExtensionMethod -{ - public class Bo - { - // 根据论文撸出来的 - // https://www.docin.com/p-740208811.html?docfrom=rrela - [CommandMethod("TestBo")] - public void TestBo() - { - //TODO 步骤01:提取邻接表...没做 - var boNodes = new List(); - - if (boNodes.Count == 0) - return; - - //步骤02:初始化每个节点 - for (int i = 0; i < boNodes.Count; i++) - { - boNodes[i].Color = BoColor.白; - boNodes[i].Distance = int.MaxValue; - boNodes[i].Parent = null; - } - - var q = new Queue(); - - //步骤12:源点无法涉及的点,也就是独立白色的点进行处理 - for (int ss = 0; ss < boNodes.Count; ss++) - { - //步骤03:源点加入队列 - var s = boNodes[ss]; - //循环的时候,如果它不白色,表示它被处理过 - if (s.Color != BoColor.白) - continue; - - //步骤04:加入队列,进行此源点的邻近点(链条)染色 - q.Enqueue(s); - while (q.Count != 0) - { - //步骤05: - var u = q.Dequeue(); - //步骤06 + 步骤10:遍历邻近节点 - for (int vv = 0; vv < u.Count; vv++) - { - var v = u[vv]; - switch (v.Color) - { - case BoColor.白://步骤07 - { - v.Color = BoColor.灰; - v.Distance = u.Distance + 1; - v.Parent = u; - q.Enqueue(v); - } - break; - case BoColor.灰://步骤08 - { - v.Add(u); - } - break; - case BoColor.黑://步骤09 - break; - default: - break; - } - } - //步骤11 - u.Color = BoColor.黑; - } - } - - var boss = new LoopList>(); - //步骤13 + 步骤18:从所有节点依次取出一个点 - for (int ii = 0; ii < boNodes.Count; ii++) - { - var u = boNodes[ii]; - //步骤14:跳过 - if (u.Count == 0) - continue; - - //步骤15:取相遇点,并新建封闭集 - var L = new LoopList(); - for (int jj = 0; jj < u.Count; jj++) - { - var v = boNodes[ii][jj]; - if (u.Distance == v.Distance) - { - //步骤16: - L.Add(u); - L.Add(v); - 步骤20(u, v, L); - } - else if (u.Distance - v.Distance < 1) - { - //步骤17: - L.Add(u.Parent!); - L.Add(u); - L.Add(v); - 步骤20(u.Parent!, v, L); - } - boss.Add(L); - } - } - //步骤19: 算法结束 - } - - /// - /// 这个是排序吗? - /// - /// - /// - /// - void 步骤20(BoNode u, BoNode v, LoopList L) - { - //步骤20: - if (u.Parent == v.Parent) - { - L.AddFirst(u.Parent!); - return; - } - - //步骤21: - L.AddFirst(u.Parent!); - L.AddLast(v.Parent!); - } - } - - public enum BoColor - { - 白, - 灰, - 黑, - } - public class BoNode : List// 拥有成员 - { - /// - /// 颜色 - /// - public BoColor Color; - /// - /// 从头到此节点的距离 - /// - public int Distance; - /// - /// 父节点 - /// - public BoNode? Parent; - } -} - -#endif \ No newline at end of file diff --git a/src/IFoxCAD.Cad/ExtensionMethod/CurveEx.cs b/src/IFoxCAD.Cad/ExtensionMethod/CurveEx.cs index 4b9b576..bb8b04e 100644 --- a/src/IFoxCAD.Cad/ExtensionMethod/CurveEx.cs +++ b/src/IFoxCAD.Cad/ExtensionMethod/CurveEx.cs @@ -60,7 +60,6 @@ public static IEnumerable GetAllCycle(this IEnumerable curves) #else graph.AddEdge(curve.GetGeCurve()); #endif - } //新建 dfs var dfs = new DepthFirst(); @@ -73,9 +72,7 @@ public static IEnumerable GetAllCycle(this IEnumerable curves) var curve = graph.GetCurves(item.ToList()).ToArray(); var comcur = new CompositeCurve3d(curve).ToCurve(); if (comcur is not null) - { res.Add(comcur); - } } return res; } diff --git a/src/IFoxCAD.Cad/ExtensionMethod/PointEx.cs b/src/IFoxCAD.Cad/ExtensionMethod/PointEx.cs index 30f3c2c..5e76b99 100644 --- a/src/IFoxCAD.Cad/ExtensionMethod/PointEx.cs +++ b/src/IFoxCAD.Cad/ExtensionMethod/PointEx.cs @@ -2,8 +2,23 @@ public static class PointEx { - public static string GetHashString(this Point3d pt) + public static string GetHashString(this Point3d pt, int xyz = 3, int decimalRetain = 6) { - return $"{pt.X:n6}{pt.Y:n6}{pt.Z:n6}"; + string hash; + string de = "f" + decimalRetain.ToString(); + switch (xyz) + { + case 1: + hash = pt.X.ToString(de); + break; + case 2: + hash = pt.X.ToString(de) + "," + pt.Y.ToString(de); + break; + default: + //hash = $"{pt.X:f6},{pt.Y:f6},{pt.Z:f6}"; + hash = pt.X.ToString(de) + "," + pt.Y.ToString(de) + "," + pt.Z.ToString(de); + break; + } + return "(" + hash + ")"; } } \ No newline at end of file diff --git a/src/IFoxCAD.Cad/ExtensionMethod/Edge.cs "b/src/IFoxCAD.Cad/ExtensionMethod/\351\227\255\345\220\210\350\276\271\347\225\214\347\256\227\346\263\225/Edge.cs" similarity index 51% rename from src/IFoxCAD.Cad/ExtensionMethod/Edge.cs rename to "src/IFoxCAD.Cad/ExtensionMethod/\351\227\255\345\220\210\350\276\271\347\225\214\347\256\227\346\263\225/Edge.cs" index a749157..869e772 100644 --- a/src/IFoxCAD.Cad/ExtensionMethod/Edge.cs +++ "b/src/IFoxCAD.Cad/ExtensionMethod/\351\227\255\345\220\210\350\276\271\347\225\214\347\256\227\346\263\225/Edge.cs" @@ -1,85 +1,103 @@ +using System.Diagnostics; + namespace IFoxCAD.Cad; + /// -/// +/// Ϣ /// -public class Edge : IEquatable +public class CurveInfo : Rect { - #region ֶ - public CompositeCurve3d GeCurve3d; - public int StartIndex;//DESIGN0409 ֻڽӱ˾û. - public int EndIndex; //DESIGN0409 ֻڽӱ˾û. - public static Tolerance CadTolerance = new(1e-6, 1e-6); - #endregion + #region + /// + /// ͼԪ + /// + public Curve Curve; + /// + /// ߷ָIJ + /// + public List Paramss; + /// + /// ײ + /// + public CollisionChain? CollisionChain; - #region + Edge? _Edge; /// - /// (ûаΧ,ToCurve) + /// ߽ /// - public Edge(CompositeCurve3d geCurve3d) + public Edge Edge { - GeCurve3d = geCurve3d; + get + { + if (_Edge == null) + _Edge = new Edge(Curve.ToCompositeCurve3d()!); + return _Edge; + } } - public Edge(Edge edge) : this(edge.GeCurve3d) + #endregion + + #region + public CurveInfo(Curve curve) { - StartIndex = edge.StartIndex; - EndIndex = edge.EndIndex; + Curve = curve; + Paramss = new List(); + + //TODO ˴bug:֮ûй + var box = Curve.GeometricExtents; + _X = box.MinPoint.X; + _Y = box.MinPoint.Y; + _Right = box.MaxPoint.X; + _Top = box.MaxPoint.Y; } #endregion #region /// - /// ʼ + /// ָ, /// + /// ߷ָIJ /// - public Vector3d GetStartVector() + public List Split(List pars1) { - //ȡ߲ - var inter = GeCurve3d.GetInterval(); - var poc = new PointOnCurve3d(GeCurve3d, inter.LowerBound); - // - return poc.GetDerivative(1); + var edges = new List(); + var c3ds = Edge.GeCurve3d.GetSplitCurves(pars1); + if (c3ds.Count > 0) + edges.AddRange(c3ds.Select(c => new Edge(c))); + return edges; } + #endregion +} + +/// +/// ײ +/// +public class CollisionChain : List { } + - /// - /// - /// - /// - public Vector3d GetEndVector() - { - var inter = GeCurve3d.GetInterval(); - var poc = new PointOnCurve3d(GeCurve3d, inter.UpperBound); - return -poc.GetDerivative(1); - } +/// +/// ߱ +/// +[DebuggerDisplay("{DebuggerDisplay,nq}")] +[DebuggerTypeProxy(typeof(Edge))] +public class Edge : IEquatable, IFormattable +{ + [DebuggerBrowsable(DebuggerBrowsableState.Never)] + private string DebuggerDisplay => ToString(); + + #region ֶ + public CompositeCurve3d GeCurve3d; + #endregion + + #region /// - /// жϽڵλ + /// (ûаΧ,ToCurve) /// - /// ߽ - /// ߽Ƿλڴ˴ - /// - /// - /// - public bool IsNext(Edge edge, int startOrEndIndex, ref Vector3d vec, ref bool forward) + public Edge(CompositeCurve3d geCurve3d) { - if (edge == this) - return false; - - if (StartIndex == startOrEndIndex) - { - vec = GetStartVector(); - forward = true; - return true; - } - else if (EndIndex == startOrEndIndex) - { - vec = GetEndVector(); - forward = false; - return true; - } - return false; + GeCurve3d = geCurve3d; } - #endregion #region _Ƚ @@ -105,9 +123,12 @@ public bool Equals(Edge? b) if (ReferenceEquals(a, b))//ͬһ return true; - return a.GeCurve3d == b.GeCurve3d - && a.StartIndex == b.StartIndex - && a.EndIndex == b.EndIndex; + return a.GeCurve3d == b.GeCurve3d; + } + + public override int GetHashCode() + { + return GeCurve3d.GetHashCode(); } /// @@ -155,6 +176,10 @@ public bool SplitPointEquals(Edge? b, int splitNum = 4) } return true; } + #endregion + + #region + public static Tolerance CadTolerance = new(1e-6, 1e-6); /// /// ˳ @@ -182,277 +207,65 @@ public static void Distinct(List edgesOut) return false; }); } - public override int GetHashCode() - { - return GeCurve3d.GetHashCode() ^ StartIndex ^ EndIndex; - } - #endregion -} - -/// -/// ߽ڵ -/// -public class EdgeItem : Edge, IEquatable -{ - #region ֶ - /// - /// ־,Ϊǰ,Ϊ - /// - public bool Forward; - #endregion - - #region - /// - /// ߽ڵ - /// - public EdgeItem(Edge edge, bool forward) : base(edge) - { - Forward = forward; - } #endregion - #region - public CompositeCurve3d? GetCurve() + #region IFormattable + public override string ToString() { - var cc3d = GeCurve3d; - if (Forward) - return cc3d; - - //߲ - cc3d = cc3d.Clone() as CompositeCurve3d; - return cc3d?.GetReverseParameterCurve() as CompositeCurve3d; + return ToString(null, null); } /// - /// + /// תΪַ_ʽʵ /// - /// ѯλõ - /// ص - public void FindRegion(List edges, List> regions_out) + /// + /// + /// + string IFormattable.ToString(string? format, IFormatProvider? formatProvider) { - var result = new LoopList();//µ - var edgeItem = this; - result.Add(edgeItem); - var getEdgeItem = this.GetNext(edges); - if (getEdgeItem is null) - return; - - bool hasList = false; - - for (int i = 0; i < regions_out.Count; i++) - { - var edgeList2 = regions_out[i]; - var node = edgeList2.GetNode(e => e.Equals(edgeItem)); - if (node is not null && node != edgeList2.Last) - { - if (node.Next!.Value.Equals(getEdgeItem)) - { - hasList = true; - break; - } - } - } - if (!hasList) - { - //֮ǰиѭ,ɵԭ8b(ջ),ȻջԶҲͷ - while (getEdgeItem is not null) - { - if (result.Contains(getEdgeItem)) - { - //ջ,ǰͷʼ޳ظڵ - while (result.First?.Value != getEdgeItem) - result.RemoveFirst(); - break; - } - result.Add(getEdgeItem); - getEdgeItem = getEdgeItem.GetNext(edges); - } - if (getEdgeItem == edgeItem) - regions_out.Add(result); - } + return ToString(format, formatProvider); } /// - /// ȡһ + /// תΪַ_вε /// - /// /// - public EdgeItem? GetNext(List edges) + public string ToString(string? format, IFormatProvider? formatProvider = null) { - Vector3d vec; - int next; - if (Forward) + var s = new StringBuilder(); + if (format is null) { - vec = GetEndVector(); - next = EndIndex; + s.Append(nameof(Edge)); + s.Append("{ "); + s.Append("(StartPoint=" + GeCurve3d.StartPoint + "; EndPoint=" + GeCurve3d.EndPoint + ")"); + s.Append(" }\r\n"); } - else - { - vec = GetStartVector(); - next = StartIndex; - } - - EdgeItem? edgeItem = null; - Vector3d vec2, vec3 = new(); - double angle = 0; - bool hasNext = false; - bool forward = false; - for (int i = 0; i < edges.Count; i++) - { - var edge = edges[i]; - if (this.IsNext(edge, next, ref vec3, ref forward)) - { - if (hasNext) - { - var angle2 = vec.GetAngleTo(vec3, Vector3d.ZAxis); - if (angle2 < angle) - { - vec2 = vec3; - angle = angle2; - edgeItem = new EdgeItem(edge, forward); - } - } - else - { - hasNext = true; - vec2 = vec3; - angle = vec.GetAngleTo(vec2, Vector3d.ZAxis); - edgeItem = new EdgeItem(edge, forward); - } - } - } - return edgeItem; - } - #endregion - - #region ת - public override string ToString() - { - return Forward ? - $"{StartIndex}-{EndIndex}" : - $"{EndIndex}-{StartIndex}"; - } - #endregion - - #region _Ƚ - public override bool Equals(object? obj) - { - return this == obj as EdgeItem; - } - public bool Equals(EdgeItem? b) - { - return this == b; - } - public static bool operator !=(EdgeItem? a, EdgeItem? b) - { - return !(a == b); - } - public static bool operator ==(EdgeItem? a, EdgeItem? b) - { - //˴طʹ==null,Ϊ˴Ƕ - if (b is null) - return a is null; - else if (a is null) - return false; - if (ReferenceEquals(a, b))//ͬһ - return true; - - return a.Forward == b.Forward && (a == b as Edge); - } - public override int GetHashCode() - { - return base.GetHashCode() ^ (Forward ? 0 : 1); + return s.ToString(); } #endregion } - +#if true2 /// -/// Ϣ +/// (պ/޷ֲ/Խ) /// -public class CurveInfo : Rect +public class PolyEdge : LoopList { /// - /// ͼԪ + /// (պ/޷ֲ/Խ) /// - public Curve Curve; - /// - /// ߷ָIJ - /// - public List Paramss; - /// - /// ײ - /// - public CollisionChain? CollisionChain; - - Edge? _Edge; - /// - /// ߽ - /// - public Edge Edge - { - get - { - if (_Edge == null) - _Edge = new Edge(Curve.ToCompositeCurve3d()!); - return _Edge; - } - } - - public CurveInfo(Curve curve) - { - Curve = curve; - Paramss = new List(); - - //TODO ˴bug:֮ûй - var box = Curve.GeometricExtents; - _X = box.MinPoint.X; - _Y = box.MinPoint.Y; - _Right = box.MaxPoint.X; - _Top = box.MaxPoint.Y; - } - - /// - /// ָ, - /// - /// ߷ָIJ - /// - public List Split(List pars1) - { - var edges = new List(); - var c3ds = Edge.GeCurve3d.GetSplitCurves(pars1); - if (c3ds.Count > 0) - edges.AddRange(c3ds.Select(c => new Edge(c))); - return edges; - } -} - -/// -/// ײ -/// -public class CollisionChain : List -{ -} - - -/// -/// (պ/޷ֲ/Խ) -/// -public class PolyEdge : LoopList -{ + /// public PolyEdge(params Edge[] ps) { AddRange(ps); } - public PolyEdge(Knot kn) - { - AddRange(kn); - } + // ̬ڸƵ㼯ʱڴ泤 + static List pts = new(); - public List Points() + public Point3d[] Points() { - List pts = new(); + pts.Clear(); //ΪʱӶ,ԵҲ var ge = GetEnumerator(); @@ -470,7 +283,7 @@ public List Points() else if (pts[pts.Count - 1] != ep) pts.Add(ep); } - return pts; + return pts.ToArray(); } /// @@ -505,32 +318,257 @@ public List Points() } } +#endif + + /// -/// һ㴢֧ +/// ڽӱڵ /// -public class Knot : PolyEdge//ı߼(DZ) +//[DebuggerDisplay("ڵ = {Number}; Count = {Count}; Color = {Color}; Distance = {Distance}; ڵ = {Parent?.Number}")] +[DebuggerDisplay("{DebuggerDisplay,nq}")] +[DebuggerTypeProxy(typeof(BoNode))] +public class BoNode : IFormattable { - public Point3d Point; // + [DebuggerBrowsable(DebuggerBrowsableState.Never)] + private string DebuggerDisplay => ToString(); + + #region ֶ + /// + /// ɫ + /// + public BoColor Color; + /// + /// ͷ˲ + /// + public int Steps; + /// + /// ڵ + /// + public BoNode? Parent; + /// + /// 㼯 + /// + public List? Meet; + /// + /// ڽڵ + /// + public List Neighbor; + + + /// + /// + /// + public Point3d Point; + /// + /// (DZ) + /// + public List Edges; + /// + /// ڵ + /// + public int Number; + #endregion - public Knot(Point3d point, Edge edge) + #region + /// + /// ڽӱڵ + /// + /// ڵ + /// + /// + public BoNode(int num, Point3d point, Edge edge) { + Number = num; Point = point; - Add(edge); + Edges = new(); + Edges.Add(edge); + + Neighbor = new(); + } + #endregion + + #region + /// + /// ȡձ߼, + /// + /// + /// + /// + public static LoopList GetEdges(LoopList nodes) + { + if (nodes == null || nodes.Count == 0) + throw new ArgumentNullException(nameof(nodes)); + + LoopList result = new(); + //1-2 2-3 3-4 4-1 + var lp = nodes.First; + do + { + var edge = GetEdge(lp!.Value, lp.Next!.Value); + if (edge != null) + result.Add(edge); + lp = lp.Next; + } while (lp != nodes.First); + + return result; } /// - /// оͷسԱ + /// ȡa-bڵ֮ /// - /// ڵ㼯 - /// ҵ + /// + /// /// - public static Knot? Contains(IEnumerable knots, Point3d findPoint) + static Edge? GetEdge(BoNode node1, BoNode node2) { - var ge = knots.GetEnumerator(); - while (ge.MoveNext()) - if (ge.Current.Point == findPoint) - return ge.Current; + if (node1 == null || node2 == null) + return null; + + for (int i = 0; i < node1.Edges.Count; i++) + { + var node1Edge = node1.Edges[i]; + for (int j = 0; j < node2.Edges.Count; j++) + { + if (node1Edge == node2.Edges[j]) + return node1Edge; + } + } return null; } + #endregion + + #region IFormattable + public override string ToString() + { + return ToString(null, null); + } + + /// + /// תΪַ_ʽʵ + /// + /// + /// + /// + string IFormattable.ToString(string? format, IFormatProvider? formatProvider) + { + return ToString(format, formatProvider); + } + + /// + /// תΪַ_вε + /// + /// + public string ToString(string? format, IFormatProvider? formatProvider = null) + { + var s = new StringBuilder(); + //s.Append($"Count = {Count};"); + if (format is null) + { + s.Append(nameof(BoNode) + $"{Number}"); + s.Append("{ "); + + s.Append("ڵ:("); + for (int i = 0; i < Neighbor.Count; i++) + { + s.Append(this.Neighbor[i].Number); + if (i < Neighbor.Count - 1) + s.Append("--"); + } + s.Append(") "); + + s.Append($"Neighbor.Count={Neighbor.Count}; Color={Color}; Distance={Steps}; ={Parent?.Number}; "); + s.Append($"Point={Point};"); + s.Append(" }\r\n"); + } + return s.ToString(); + } + #endregion + +} + +public enum BoColor +{ + , + , + , +} + +[DebuggerDisplay("{DebuggerDisplay,nq}")] +[DebuggerTypeProxy(typeof(CompositeCurve3ds))] +public class CompositeCurve3ds : List, IFormattable +{ + [DebuggerBrowsable(DebuggerBrowsableState.Never)] + private string DebuggerDisplay => ToString(); + + // cadݲ + public static Tolerance CadTolerance = new(1e-6, 1e-6); + + /// + /// ߽ + /// + /// Ҫкõ + /// + public static CompositeCurve3ds OrderByPoints(LoopList pl) + { + CompositeCurve3ds c3ds = new(); + var lp = pl.First; + do + { + //1͵2Ƚ,߲ + var a1 = lp!.Value.GeCurve3d; + var a2 = lp!.Next!.Value.GeCurve3d; + if (!a1.EndPoint.IsEqualTo(a2.StartPoint, CadTolerance)) + a1 = (CompositeCurve3d)a1.GetReverseParameterCurve(); + c3ds.Add(a1); + lp = lp.Next; + } while (lp != pl.First); + + return c3ds; + } + + + public CompositeCurve3d ToCompositeCurve3d() + { + return new CompositeCurve3d(ToArray()); + } + + #region IFormattable + public override string ToString() + { + return ToString(null, null); + } + + /// + /// תΪַ_ʽʵ + /// + /// + /// + /// + string IFormattable.ToString(string? format, IFormatProvider? formatProvider) + { + return ToString(format, formatProvider); + } + + /// + /// תΪַ_вε + /// + /// + public string ToString(string? format, IFormatProvider? formatProvider = null) + { + var s = new StringBuilder(); + if (format is null) + { + s.Append(nameof(CompositeCurve3ds) + $"{Count}"); + s.Append("{ "); + for (int i = 0; i < Count; i++) + { + s.Append("\r\n(StartPoint=" + this[i].StartPoint + "; EndPoint=" + this[i].EndPoint + ")"); + } + s.Append(" }\r\n"); + } + return s.ToString(); + } + + #endregion } \ No newline at end of file diff --git "a/src/IFoxCAD.Cad/ExtensionMethod/\351\227\255\345\220\210\350\276\271\347\225\214\347\256\227\346\263\225/TestTopo.cs" "b/src/IFoxCAD.Cad/ExtensionMethod/\351\227\255\345\220\210\350\276\271\347\225\214\347\256\227\346\263\225/TestTopo.cs" new file mode 100644 index 0000000..ba64fe7 --- /dev/null +++ "b/src/IFoxCAD.Cad/ExtensionMethod/\351\227\255\345\220\210\350\276\271\347\225\214\347\256\227\346\263\225/TestTopo.cs" @@ -0,0 +1,22 @@ +namespace IFoxCAD.Cad.ExtensionMethod +{ + public class CmdTestTopo + { + [CommandMethod("TestTopo")] + public void TestTopo() + { + using var tr = new DBTrans(); + var ents = Env.Editor.SSGet()?.Value?.GetEntities(); + if (ents == null) + return; + + var curves = Topo.Create(ents.ToList()!); + if (curves == null || !curves.Any()) + return; + + //改颜色,生成图元 + curves.ForEach((num, cu) => cu.ForWrite(e => e.ColorIndex = num)); + tr.CurrentSpace.AddEntity(curves); + } + } +} \ No newline at end of file diff --git "a/src/IFoxCAD.Cad/ExtensionMethod/\351\227\255\345\220\210\350\276\271\347\225\214\347\256\227\346\263\225/Topo.cs" "b/src/IFoxCAD.Cad/ExtensionMethod/\351\227\255\345\220\210\350\276\271\347\225\214\347\256\227\346\263\225/Topo.cs" new file mode 100644 index 0000000..58d3b0c --- /dev/null +++ "b/src/IFoxCAD.Cad/ExtensionMethod/\351\227\255\345\220\210\350\276\271\347\225\214\347\256\227\346\263\225/Topo.cs" @@ -0,0 +1,446 @@ +using System.Diagnostics; + +namespace IFoxCAD.Cad; + +public class Topo +{ + #region 成员 + // 求交类(每次set自动重置,都会有个新的结果) + static CurveCurveIntersector3d _Cci3d = new(); + // cad容差类 + public static Tolerance CadTolerance = new(1e-6, 1e-6); + #endregion + + public List curveList = new(); + + #region 构造 + /// + /// 获取封闭曲线 + /// + /// + /// + public Topo(List curves) + { + if (curves == null || curves.Count == 0) + throw new ArgumentNullException(nameof(curves)); + + curveList = new(); + //提取包围盒信息 + for (int i = 0; i < curves.Count; i++) + curveList.Add(new CurveInfo(curves[i])); + } + + /// + /// 获取曲线集所围成的封闭区域的曲线集 + /// + /// 曲线集 + /// 闭合的曲线集 + public static IEnumerable? Create(List curves) + { + //闭合的曲线集合 + List closedCurve3d = new(); + + //零散的边界 + List gs = new(); + Dictionary boNodes = new(); + + var topo = new Topo(curves); + + var infos = topo.curveList; + + topo.GetEdgesAndnewCurves(infos, gs, closedCurve3d); + topo.AdjacencyList(gs, closedCurve3d, boNodes); + var bos = boNodes.Select(a => a.Value).ToArray(); + + //TODO Topo校验数据 + var regions = topo.BreadthFirstSearch(bos); + if (regions == null || regions.Count == 0) + return null; + + //零碎的曲线在此处生成封闭曲线 + for (int regNum = 0; regNum < regions.Count; regNum++) + { + var pl = BoNode.GetEdges(regions[regNum]); + if (pl.Count == 0) + continue; + var c3ds = CompositeCurve3ds.OrderByPoints(pl); + closedCurve3d.Add(c3ds.ToCompositeCurve3d()); + } + + //因为生成可能导致遗忘释放,所以这里统一生成 + return closedCurve3d.Select(e => e.ToCurve()!).Where(e => e != null).ToList(); + } + #endregion + + #region 方法 + /// + /// 利用交点断分曲线和独立自闭曲线 + /// + /// 传入每组有碰撞的 + /// 传出不自闭的曲线集 + /// 传出自闭曲线集 + public void GetEdgesAndnewCurves(List infos_In, List edges_Out, List closed_Out) + { + //此处是O(n²) + //曲线a和其他曲线n根据 交点 切割子线(第一次是自交对比) + for (int a = 0; a < infos_In.Count; a++) + { + var curve1 = infos_In[a]; + var gc1 = curve1.Edge.GeCurve3d; + var pars1 = curve1.Paramss; + + for (int n = a; n < infos_In.Count; n++) + { + var curve2 = infos_In[n]; + + //包围盒没有碰撞就直接结束 + if (!curve1.IntersectsWith(curve2)) + continue; + + var gc2 = curve2.Edge.GeCurve3d; + var pars2 = curve2.Paramss; + + _Cci3d.Set(gc1, gc2, Vector3d.ZAxis); + + //计算两条曲线的交点(多个),分别放入对应的交点参数集 + for (int k = 0; k < _Cci3d.NumberOfIntersectionPoints; k++) + { + var pars = _Cci3d.GetIntersectionParameters(k); + pars1.Add(pars[0]);//0是第一条曲线的交点参数 + pars2.Add(pars[1]);//1是第二条曲线的交点参数 + } + } + + if (gc1.IsClosed()) + closed_Out.Add(gc1); + + if (pars1.Count == 0) + continue; + + //有交点参数 + //根据交点参数断分曲线,然后获取边界 + var c3ds = curve1.Split(pars1); + if (c3ds.Count > 0) + { + edges_Out.AddRange(c3ds); + } + else + { + //狐哥写的这里出现的条件是:有曲线参数,但是切分不出来曲线...没懂为什么... + //是这些参数?{参数0位置?头参/尾参/参数不在曲线上?} + edges_Out.Add(curve1.Edge); + } + + //有交点的才消重,无交点必然不重复. + Edge.Distinct(edges_Out); + } + } + + /// + /// 创建邻接表 + /// + /// 传入每组有碰撞的 + /// 传出自闭曲线集 + /// 节点集合返回(交点坐标字符串,节点) + public void AdjacencyList(List edges_In, List closed_Out, Dictionary boNodes_Out) + { + int boNumber = 0; + /* + * 邻接表:不重复地将共点(交点)作为标记,然后容器加入边 + */ + for (int i = 0; i < edges_In.Count; i++) + { + var edge = edges_In[i]; + //曲线闭合直接提供出去 + if (edge.GeCurve3d.IsClosed()) + { + closed_Out.Add(edge.GeCurve3d); + continue; + } + + //点转字符串作为key + var sp = edge.GeCurve3d.StartPoint.GetHashString(2); + var ep = edge.GeCurve3d.EndPoint.GetHashString(2); + + //词典key是共用此交点 + BoNode spNode; + BoNode epNode; + if (boNodes_Out.ContainsKey(sp)) + spNode = boNodes_Out[sp]; + else + spNode = new BoNode(boNumber++, edge.GeCurve3d.StartPoint, edge); + + if (boNodes_Out.ContainsKey(ep)) + epNode = boNodes_Out[ep]; + else + epNode = new BoNode(boNumber++, edge.GeCurve3d.EndPoint, edge); + + //加入边图元 + if (!spNode.Edges.Contains(edge)) + spNode.Edges.Add(edge); + if (!epNode.Edges.Contains(edge)) + epNode.Edges.Add(edge); + + //加入邻居节点 + if (!spNode.Neighbor.Contains(epNode)) + spNode.Neighbor.Add(epNode); + if (!epNode.Neighbor.Contains(spNode)) + epNode.Neighbor.Add(spNode); + + //加入词典 + if (!boNodes_Out.ContainsKey(sp)) + boNodes_Out.Add(sp, spNode); + if (!boNodes_Out.ContainsKey(ep)) + boNodes_Out.Add(ep, epNode); + } + + // TODO 如果有效率问题,尝试把剪枝去掉, + // 因为后续的分析边界也同样存在剪枝,可以说可有可无 +#if true2 + /* + * 剪枝: + * 共点若只有一次使用,代表此图元是尾巴,可以扔掉. + * 剩下每个点都至少有两条曲线通过(闭合曲线) + */ + + // 尾巴的编号集合 + var reNums = boNodes_Out.Select(a => a.Value) + .Where(b => b.Neighbor.Count == 1)//只有一个点就是尾巴 + .Select(c => c.Number) + .ToList(); + + for (int ii = boNodes_Out.Count - 1; ii >= 0; ii--) + { + //剔除子元素含有尾巴 + var item = boNodes_Out.ElementAt(ii); + var node = item.Value; + for (int jj = node.Neighbor.Count - 1; jj >= 0; jj--) + { + if (reNums.Contains(node.Neighbor[jj].Number)) + node.Neighbor.RemoveAt(jj); + } + + //剔除词典含有尾巴 + if (reNums.Contains(node.Number)) + boNodes_Out.Remove(item.Key); + } +#endif + } + #endregion + + + + + #region 广度 + /// + /// 广度优先算法 + /// 论文链接 + /// + /// 邻接表 + /// 多个面域 + /// + List> BreadthFirstSearch(BoNode[] boNodes) + { + if (boNodes == null || boNodes.Length == 0) + throw new ArgumentNullException(nameof(boNodes)); + + Topo.Init(boNodes); + + //同一代节点进入队列 + var queue = new Queue(); + + //步骤12:源点无法涉及的点,也就是独立白色的点进行处理 + for (int ssNum = 0; ssNum < boNodes.Length; ssNum++) + { + //步骤03:源点 + var s = boNodes[ssNum]; + //步骤12:循环的时候,如果它不白色,表示它被处理过 + if (s.Color != BoColor.白) + continue; + + //步骤04:源点加入队列,进行此源点的邻近点(邻居/儿子们/同一代的点)涂色 + queue.Enqueue(s); + while (queue.Count != 0) + { + //步骤05: + var 我u = queue.Dequeue(); + + //步骤06 + 步骤10: + //邻近节点(同一代的点)已经在邻接表找到了,这里遍历它们 + 我u.Neighbor.ForEach(邻v => { + switch (邻v.Color) + { + case BoColor.白://步骤07: + { + //把邻近点都涂灰加入队列, + //下次就是=>步骤04 循环,下一代再进入=>步骤08 + 邻v.Color = BoColor.灰; + 邻v.Steps = 我u.Steps + 1; + 邻v.Parent = 我u; + queue.Enqueue(邻v); + } + break; + case BoColor.灰://步骤08 + { + if (邻v.Meet == null) + 邻v.Meet = new(); + 邻v.Meet.Add(我u); + } + break; + case BoColor.黑://步骤09 + { + //论文前面说了曲线它不处理,需要提前获取 + //这里遇到黑色,其实就是腰身闭环(曲线唇形杀回马枪),直接记录环就好了. + //由于有步数,所以不会跨两个的 + //LoopList lst = new(); + //lst.Add(我u); + //lst.Add(子v); + //boss.Add(lst); + } + break; + default: + break; + } + }); + //步骤11 + 我u.Color = BoColor.黑; + } + } + return Topo.MeetGetRegions(boNodes); + } + + + /// + /// 在相遇链中提取封闭的区域 + /// + /// + static List> MeetGetRegions(BoNode[] boNodes) + { + List> regions = new(); + + //步骤13 + 步骤18:从所有节点依次取出一个点 + for (int boNums = 0; boNums < boNodes.Length; boNums++) + { + var 含有相遇v0 = boNodes[boNums]; + //步骤14:跳过 + if (含有相遇v0.Meet == null) + continue; + + //这里就是论文<2 封闭区域分离算法> + //步骤15:新建闭合集 + for (int i = 0; i < 含有相遇v0.Meet.Count; i++) + { + LoopList region = new(); + + //存在多个相遇链,论文的图4a和图4b描述这事,分别是左链和右链分享中间 + var 相遇v1 = 含有相遇v0.Meet[i]; + if (含有相遇v0.Steps == 相遇v1.Steps) //步骤16:步数相同,就是同一代 + { + region.Add(含有相遇v0); + region.Add(相遇v1); + } + else if (相遇v1.Steps + 1 == 含有相遇v0.Steps) //步骤17:步数差1 + { + region.Add(含有相遇v0.Parent!); + region.Add(含有相遇v0); + region.Add(相遇v1); + } + else + { + Debugger.Break();//这里会出现意外吗? + } + GetLink(region); + regions.Add(Topo.OrderByRegionLines(region)); + } + } + return regions; + } + + /// + /// 调整线序 + /// + /// + static LoopList OrderByRegionLines(LoopList L) + { + if (L == null || L.Count == 0) + throw new ArgumentNullException(nameof(L)); + + LoopList list = new(); + var boNode = L.First!.Value; + for (int i = 0; i < L.Count; i++)//约束循环找顺序次数 + { + list.Add(boNode); + L.For((v, item) => { + //循环每个节点,跳过已经是L2的 + //邻居节点作为目标进入循环 + var boNode2 = item.Value; + if (boNode2 != boNode + && !list.Contains(boNode2) + && boNode.Neighbor.Contains(boNode2)) + { + boNode = boNode2;//进入循环 + return true; + } + return false; + }); + } + return list; + } + + + /// + /// 从相遇点开始往上寻找父节点并加入链中 + /// + /// + /// + static void GetLink(LoopList L) + { + if (L == null || L.Count == 0) + throw new ArgumentNullException(nameof(L)); + + var uM = L.First; + var vM = L.Last; + if (uM == vM) + return; + + var u = uM!.Value; + var v = vM!.Value; + + while (u.Parent != null && v.Parent != null) + { + if (u.Parent == v.Parent) + { + //步骤20: + L.AddLast(u.Parent); + break; + } + else + { + //步骤21: + L.AddFirst(u.Parent); + L.AddLast(v.Parent); + u = u.Parent; + v = v.Parent; + } + } + } + + + /// + /// 初始化每个节点 + /// + /// + static void Init(BoNode[] boNodes) + { + //步骤02:初始化每个节点 + for (int i = 0; i < boNodes.Length; i++) + { + boNodes[i].Color = BoColor.白; + boNodes[i].Steps = int.MaxValue; + boNodes[i].Parent = null; + boNodes[i].Meet?.Clear(); + } + } + #endregion + +} \ No newline at end of file -- Gitee From 1526b71f148e9a5907ea569930b0f402a81d1bc7 Mon Sep 17 00:00:00 2001 From: liuqihong <540762622@qq.com> Date: Wed, 27 Apr 2022 18:22:45 +0800 Subject: [PATCH 191/675] =?UTF-8?q?=E5=A2=9E=E5=8A=A0=E7=A4=BA=E6=84=8F?= =?UTF-8?q?=E5=9B=BE?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- ...72\346\226\207\347\274\272\351\231\267.png" | Bin 0 -> 202707 bytes 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 "src/IFoxCAD.Cad/ExtensionMethod/\351\227\255\345\220\210\350\276\271\347\225\214\347\256\227\346\263\225/\350\256\272\346\226\207\347\274\272\351\231\267.png" diff --git "a/src/IFoxCAD.Cad/ExtensionMethod/\351\227\255\345\220\210\350\276\271\347\225\214\347\256\227\346\263\225/\350\256\272\346\226\207\347\274\272\351\231\267.png" "b/src/IFoxCAD.Cad/ExtensionMethod/\351\227\255\345\220\210\350\276\271\347\225\214\347\256\227\346\263\225/\350\256\272\346\226\207\347\274\272\351\231\267.png" new file mode 100644 index 0000000000000000000000000000000000000000..df2895073a33a95aece3bb25f67cdc7c69f171f4 GIT binary patch literal 202707 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%0AX&xbqHs=FRIZ9g{)LtIH6$I{gu&f7f220}*+h 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

rCsAV>NGe3Ec^hV|vMN7m2!WM85s(OPO~hcS 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 Date: Thu, 28 Apr 2022 14:26:17 +0800 Subject: [PATCH 192/675] =?UTF-8?q?=E5=89=94=E9=99=A4null?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/IFoxCAD.Cad/ExtensionMethod/SelectionSetEx.cs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/IFoxCAD.Cad/ExtensionMethod/SelectionSetEx.cs b/src/IFoxCAD.Cad/ExtensionMethod/SelectionSetEx.cs index fa4b7b0..61d1688 100644 --- a/src/IFoxCAD.Cad/ExtensionMethod/SelectionSetEx.cs +++ b/src/IFoxCAD.Cad/ExtensionMethod/SelectionSetEx.cs @@ -75,7 +75,8 @@ public static IEnumerable> GetObjectIdGroup(this Sel return ss .GetObjectIds() - .Select(id => tr.GetObject(id, openMode)); + .Select(id => tr.GetObject(id, openMode)) + .Where(ent => ent != null); } #endregion -- Gitee From b014219bb370aaedb25ec897e027594f82950590 Mon Sep 17 00:00:00 2001 From: vicwjb <403009819@qq.com> Date: Thu, 28 Apr 2022 22:06:28 +0800 Subject: [PATCH 193/675] =?UTF-8?q?=E4=BC=98=E5=8C=96=E4=BB=A3=E7=A0=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/IFoxCAD.Cad/Algorithms/Graph.cs | 10 ------ src/IFoxCAD.Cad/ExtensionMethod/PointEx.cs | 31 ++++++++++--------- .../TestTopo.cs" | 11 ++++--- .../Topo.cs" | 14 ++++----- tests/Test/TestPoint.cs | 13 ++++++++ 5 files changed, 43 insertions(+), 36 deletions(-) diff --git a/src/IFoxCAD.Cad/Algorithms/Graph.cs b/src/IFoxCAD.Cad/Algorithms/Graph.cs index dd06876..1196407 100644 --- a/src/IFoxCAD.Cad/Algorithms/Graph.cs +++ b/src/IFoxCAD.Cad/Algorithms/Graph.cs @@ -495,19 +495,9 @@ public void FindAll(IGraph graph) //var graphtmp = graph.Clone(); foreach (var item in graph.VerticesAsEnumberable) { - - Dfs(graph, new LinkedHashSet { item },total); - //graph.RemoveVertex(graphtmp.ReferenceVertex!.Data); total.Add(item); } - - - - - - - } #endregion diff --git a/src/IFoxCAD.Cad/ExtensionMethod/PointEx.cs b/src/IFoxCAD.Cad/ExtensionMethod/PointEx.cs index 5e76b99..e58029d 100644 --- a/src/IFoxCAD.Cad/ExtensionMethod/PointEx.cs +++ b/src/IFoxCAD.Cad/ExtensionMethod/PointEx.cs @@ -2,23 +2,24 @@ public static class PointEx { + + ///

+ /// 获取点的hash字符串,同时可以作为pt的字符串表示 + /// + /// 点 + /// 指示计算几维坐标的标志,1为计算x,2为计算x,y,其他为计算x,y,z + /// 保留的小数位数 + /// hash字符串 public static string GetHashString(this Point3d pt, int xyz = 3, int decimalRetain = 6) { - string hash; - string de = "f" + decimalRetain.ToString(); - switch (xyz) + var de = $"f{decimalRetain}"; + return xyz switch { - case 1: - hash = pt.X.ToString(de); - break; - case 2: - hash = pt.X.ToString(de) + "," + pt.Y.ToString(de); - break; - default: - //hash = $"{pt.X:f6},{pt.Y:f6},{pt.Z:f6}"; - hash = pt.X.ToString(de) + "," + pt.Y.ToString(de) + "," + pt.Z.ToString(de); - break; - } - return "(" + hash + ")"; + 1 => $"({pt.X.ToString(de)})", + 2 => $"({pt.X.ToString(de)},{pt.Y.ToString(de)})", + _ => $"({pt.X.ToString(de)},{pt.Y.ToString(de)},{pt.Z.ToString(de)})" + }; } + + } \ No newline at end of file diff --git "a/src/IFoxCAD.Cad/ExtensionMethod/\351\227\255\345\220\210\350\276\271\347\225\214\347\256\227\346\263\225/TestTopo.cs" "b/src/IFoxCAD.Cad/ExtensionMethod/\351\227\255\345\220\210\350\276\271\347\225\214\347\256\227\346\263\225/TestTopo.cs" index ba64fe7..19caa69 100644 --- "a/src/IFoxCAD.Cad/ExtensionMethod/\351\227\255\345\220\210\350\276\271\347\225\214\347\256\227\346\263\225/TestTopo.cs" +++ "b/src/IFoxCAD.Cad/ExtensionMethod/\351\227\255\345\220\210\350\276\271\347\225\214\347\256\227\346\263\225/TestTopo.cs" @@ -9,14 +9,17 @@ public void TestTopo() var ents = Env.Editor.SSGet()?.Value?.GetEntities(); if (ents == null) return; - - var curves = Topo.Create(ents.ToList()!); + + Tools.TestTimes2(1, "bfs", () => { + var curves = Topo.Create(ents.ToList()!)!; + if (curves == null || !curves.Any()) return; //改颜色,生成图元 - curves.ForEach((num, cu) => cu.ForWrite(e => e.ColorIndex = num)); - tr.CurrentSpace.AddEntity(curves); + //curves.ForEach((num, cu) => cu.ForWrite(e => e.ColorIndex = num)); + tr.CurrentSpace.AddEntity(curves); + }); } } } \ No newline at end of file diff --git "a/src/IFoxCAD.Cad/ExtensionMethod/\351\227\255\345\220\210\350\276\271\347\225\214\347\256\227\346\263\225/Topo.cs" "b/src/IFoxCAD.Cad/ExtensionMethod/\351\227\255\345\220\210\350\276\271\347\225\214\347\256\227\346\263\225/Topo.cs" index 58d3b0c..aec73d8 100644 --- "a/src/IFoxCAD.Cad/ExtensionMethod/\351\227\255\345\220\210\350\276\271\347\225\214\347\256\227\346\263\225/Topo.cs" +++ "b/src/IFoxCAD.Cad/ExtensionMethod/\351\227\255\345\220\210\350\276\271\347\225\214\347\256\227\346\263\225/Topo.cs" @@ -244,13 +244,13 @@ List> BreadthFirstSearch(BoNode[] boNodes) if (boNodes == null || boNodes.Length == 0) throw new ArgumentNullException(nameof(boNodes)); - Topo.Init(boNodes); + Topo.Init(boNodes); //O(n) //同一代节点进入队列 var queue = new Queue(); //步骤12:源点无法涉及的点,也就是独立白色的点进行处理 - for (int ssNum = 0; ssNum < boNodes.Length; ssNum++) + for (int ssNum = 0; ssNum < boNodes.Length; ssNum++) //O(n) { //步骤03:源点 var s = boNodes[ssNum]; @@ -260,14 +260,14 @@ List> BreadthFirstSearch(BoNode[] boNodes) //步骤04:源点加入队列,进行此源点的邻近点(邻居/儿子们/同一代的点)涂色 queue.Enqueue(s); - while (queue.Count != 0) + while (queue.Count != 0) //O(n) { //步骤05: var 我u = queue.Dequeue(); //步骤06 + 步骤10: //邻近节点(同一代的点)已经在邻接表找到了,这里遍历它们 - 我u.Neighbor.ForEach(邻v => { + 我u.Neighbor.ForEach(邻v => { //O(n) switch (邻v.Color) { case BoColor.白://步骤07: @@ -306,7 +306,7 @@ List> BreadthFirstSearch(BoNode[] boNodes) 我u.Color = BoColor.黑; } } - return Topo.MeetGetRegions(boNodes); + return Topo.MeetGetRegions(boNodes); //O(n2) } @@ -349,8 +349,8 @@ static List> MeetGetRegions(BoNode[] boNodes) { Debugger.Break();//这里会出现意外吗? } - GetLink(region); - regions.Add(Topo.OrderByRegionLines(region)); + GetLink(region); //O(n) + regions.Add(Topo.OrderByRegionLines(region)); //O(n2) } } return regions; diff --git a/tests/Test/TestPoint.cs b/tests/Test/TestPoint.cs index 9d4ea71..7592db9 100644 --- a/tests/Test/TestPoint.cs +++ b/tests/Test/TestPoint.cs @@ -2,6 +2,19 @@ { public class TestPoint { + [CommandMethod("TestptGethash")] + public void TestptGethash() + { + // test + var pt = Env.Editor.GetPoint("pick pt").Value; + //Tools.TestTimes2(1_000_000, "新语法", () => { + // pt.GetHashString2(); + //}); + Tools.TestTimes2(1_000_000, "旧语法", () => { + pt.GetHashString(); + }); + } + [CommandMethod("Testpoint3d")] public void TestPoint3d() { -- Gitee From 3d282eec7cb93265d323de530171bfc487bb35f7 Mon Sep 17 00:00:00 2001 From: vicwjb <403009819@qq.com> Date: Thu, 28 Apr 2022 23:40:47 +0800 Subject: [PATCH 194/675] =?UTF-8?q?SSGet=E5=87=BD=E6=95=B0=E5=A2=9E?= =?UTF-8?q?=E5=8A=A0=E5=85=B3=E9=94=AE=E5=AD=97=E7=9A=84=E5=9B=9E=E8=B0=83?= =?UTF-8?q?=E5=A7=94=E6=89=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/IFoxCAD.Cad/ExtensionMethod/EditorEx.cs | 15 ++++++++++----- tests/Test/testeditor.cs | 13 +++++++++++-- 2 files changed, 21 insertions(+), 7 deletions(-) diff --git a/src/IFoxCAD.Cad/ExtensionMethod/EditorEx.cs b/src/IFoxCAD.Cad/ExtensionMethod/EditorEx.cs index 66e3ef7..3271ce1 100644 --- a/src/IFoxCAD.Cad/ExtensionMethod/EditorEx.cs +++ b/src/IFoxCAD.Cad/ExtensionMethod/EditorEx.cs @@ -56,7 +56,7 @@ public static SelectionSet SelectByLineWeight(this Editor editor, LineWeight lin } - public static PromptSelectionResult? SSGet(this Editor editor, string? mode = null, SelectionFilter? filter = null, string[]? messages = null, string[]? keywords = null) + public static PromptSelectionResult? SSGet(this Editor editor, string? mode = null, SelectionFilter? filter = null, string[]? messages = null, Dictionary? keywords = null) { var pso = new PromptSelectionOptions(); PromptSelectionResult? ss = null; @@ -83,7 +83,7 @@ public static SelectionSet SelectByLineWeight(this Editor editor, LineWeight lin if (keywords is not null) { - foreach (var keyword in keywords) + foreach (var keyword in keywords.Keys) { pso.Keywords.Add(keyword); } @@ -91,9 +91,14 @@ public static SelectionSet SelectByLineWeight(this Editor editor, LineWeight lin { pso.MessageForAdding = "选择对象"; } - pso.MessageForAdding += $"[{string.Join(" / ", keywords)}]"; - pso.KeywordInput += (s,e) => - throw new Autodesk.AutoCAD.Runtime.Exception(ErrorStatus.OK, e.Input); + pso.MessageForAdding += $"[{string.Join(" / ", keywords.Keys.ToArray())}]"; + pso.KeywordInput += (s, e) => { + if (keywords.ContainsKey(e.Input)) + { + keywords[e.Input].Invoke(); + } + }; + } try { diff --git a/tests/Test/testeditor.cs b/tests/Test/testeditor.cs index 9c71a9a..8c5c0a0 100644 --- a/tests/Test/testeditor.cs +++ b/tests/Test/testeditor.cs @@ -51,9 +51,18 @@ public void testzoomextent() [CommandMethod("testssget")] public void testssget() { - var ss = + var action_a = () => { Env.Print("this is a"); }; + var action_b = () => { Env.Print("this is b"); }; - Env.Editor.SSGet(":S", messages: new string[2] { "get", "del" }, keywords: new string[2] { "A", "B" }); + var keyword = new Dictionary + { + { "A", action_a }, + { "B", action_b } + }; + + var ss = Env.Editor.SSGet( ":S", + messages: new string[2] { "get", "del" }, + keywords: keyword); Env.Print(ss); } -- Gitee From 51c96f4c0918463f3c6f00ee69d006d6feead926 Mon Sep 17 00:00:00 2001 From: liuqihong <540762622@qq.com> Date: Fri, 29 Apr 2022 00:08:54 +0800 Subject: [PATCH 195/675] =?UTF-8?q?=E5=A4=B1=E8=B4=A5=E7=9A=84=E8=A7=92?= =?UTF-8?q?=E5=BA=A6=E6=B5=8B=E8=AF=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/IFoxCAD.Cad/ExtensionMethod/PointEx.cs | 2 +- .../Edge.cs" | 14 +- .../Topo.cs" | 301 ++++++++++++++---- 3 files changed, 246 insertions(+), 71 deletions(-) diff --git a/src/IFoxCAD.Cad/ExtensionMethod/PointEx.cs b/src/IFoxCAD.Cad/ExtensionMethod/PointEx.cs index e58029d..b7010b5 100644 --- a/src/IFoxCAD.Cad/ExtensionMethod/PointEx.cs +++ b/src/IFoxCAD.Cad/ExtensionMethod/PointEx.cs @@ -2,7 +2,7 @@ public static class PointEx { - + /// /// 获取点的hash字符串,同时可以作为pt的字符串表示 /// diff --git "a/src/IFoxCAD.Cad/ExtensionMethod/\351\227\255\345\220\210\350\276\271\347\225\214\347\256\227\346\263\225/Edge.cs" "b/src/IFoxCAD.Cad/ExtensionMethod/\351\227\255\345\220\210\350\276\271\347\225\214\347\256\227\346\263\225/Edge.cs" index 869e772..a94a20c 100644 --- "a/src/IFoxCAD.Cad/ExtensionMethod/\351\227\255\345\220\210\350\276\271\347\225\214\347\256\227\346\263\225/Edge.cs" +++ "b/src/IFoxCAD.Cad/ExtensionMethod/\351\227\255\345\220\210\350\276\271\347\225\214\347\256\227\346\263\225/Edge.cs" @@ -38,7 +38,7 @@ public Edge Edge #endregion #region - public CurveInfo(Curve curve) + public CurveInfo(Curve curve!!) { Curve = curve; Paramss = new List(); @@ -385,6 +385,12 @@ public BoNode(int num, Point3d point, Edge edge) Edges.Add(edge); Neighbor = new(); + + + Color = BoColor.; + Steps = int.MaxValue; + Parent = null; + Meet?.Clear(); } #endregion @@ -492,6 +498,7 @@ public enum BoColor , , , + , } [DebuggerDisplay("{DebuggerDisplay,nq}")] @@ -518,8 +525,11 @@ public static CompositeCurve3ds OrderByPoints(LoopList pl) //1͵2Ƚ,߲ var a1 = lp!.Value.GeCurve3d; var a2 = lp!.Next!.Value.GeCurve3d; - if (!a1.EndPoint.IsEqualTo(a2.StartPoint, CadTolerance)) + + if (!a1.EndPoint.IsEqualTo(a2.EndPoint, CadTolerance) && //βͬ + !a1.EndPoint.IsEqualTo(a2.StartPoint, CadTolerance)) a1 = (CompositeCurve3d)a1.GetReverseParameterCurve(); + c3ds.Add(a1); lp = lp.Next; } while (lp != pl.First); diff --git "a/src/IFoxCAD.Cad/ExtensionMethod/\351\227\255\345\220\210\350\276\271\347\225\214\347\256\227\346\263\225/Topo.cs" "b/src/IFoxCAD.Cad/ExtensionMethod/\351\227\255\345\220\210\350\276\271\347\225\214\347\256\227\346\263\225/Topo.cs" index aec73d8..4ff3cbe 100644 --- "a/src/IFoxCAD.Cad/ExtensionMethod/\351\227\255\345\220\210\350\276\271\347\225\214\347\256\227\346\263\225/Topo.cs" +++ "b/src/IFoxCAD.Cad/ExtensionMethod/\351\227\255\345\220\210\350\276\271\347\225\214\347\256\227\346\263\225/Topo.cs" @@ -1,5 +1,3 @@ -using System.Diagnostics; - namespace IFoxCAD.Cad; public class Topo @@ -11,7 +9,7 @@ public class Topo public static Tolerance CadTolerance = new(1e-6, 1e-6); #endregion - public List curveList = new(); + public List CurveList; #region 构造 ///
@@ -24,10 +22,10 @@ public Topo(List curves) if (curves == null || curves.Count == 0) throw new ArgumentNullException(nameof(curves)); - curveList = new(); + CurveList = new(); //提取包围盒信息 for (int i = 0; i < curves.Count; i++) - curveList.Add(new CurveInfo(curves[i])); + CurveList.Add(new CurveInfo(curves[i])); } /// @@ -39,16 +37,13 @@ public Topo(List curves) { //闭合的曲线集合 List closedCurve3d = new(); - //零散的边界 List gs = new(); + //邻接表 Dictionary boNodes = new(); var topo = new Topo(curves); - - var infos = topo.curveList; - - topo.GetEdgesAndnewCurves(infos, gs, closedCurve3d); + topo.GetEdgesAndnewCurves(topo.CurveList, gs, closedCurve3d); topo.AdjacencyList(gs, closedCurve3d, boNodes); var bos = boNodes.Select(a => a.Value).ToArray(); @@ -230,22 +225,18 @@ public void AdjacencyList(List edges_In, List closed_Out - #region 广度 /// /// 广度优先算法 /// 论文链接 /// - /// 邻接表 + /// 邻接节点集合 /// 多个面域 /// List> BreadthFirstSearch(BoNode[] boNodes) { if (boNodes == null || boNodes.Length == 0) throw new ArgumentNullException(nameof(boNodes)); - - Topo.Init(boNodes); //O(n) - //同一代节点进入队列 var queue = new Queue(); @@ -263,7 +254,7 @@ List> BreadthFirstSearch(BoNode[] boNodes) while (queue.Count != 0) //O(n) { //步骤05: - var 我u = queue.Dequeue(); + var meU = queue.Dequeue(); //步骤06 + 步骤10: //邻近节点(同一代的点)已经在邻接表找到了,这里遍历它们 @@ -274,17 +265,17 @@ List> BreadthFirstSearch(BoNode[] boNodes) { //把邻近点都涂灰加入队列, //下次就是=>步骤04 循环,下一代再进入=>步骤08 - 邻v.Color = BoColor.灰; - 邻v.Steps = 我u.Steps + 1; - 邻v.Parent = 我u; - queue.Enqueue(邻v); + meNeighborV.Color = BoColor.灰; + meNeighborV.Steps = meU.Steps + 1; + meNeighborV.Parent = meU; + queue.Enqueue(meNeighborV); } break; case BoColor.灰://步骤08 { - if (邻v.Meet == null) - 邻v.Meet = new(); - 邻v.Meet.Add(我u); + if (meNeighborV.Meet == null) + meNeighborV.Meet = new(); + meNeighborV.Meet.Add(meU); } break; case BoColor.黑://步骤09 @@ -303,13 +294,206 @@ List> BreadthFirstSearch(BoNode[] boNodes) } }); //步骤11 - 我u.Color = BoColor.黑; + meU.Color = BoColor.黑; } } return Topo.MeetGetRegions(boNodes); //O(n2) } +#if true2 + //逆时针旋转方案,出现了找父节点可能到另一个面域内 + + + + + /// + /// 点积,求值 + /// 1.是两个向量的长度与它们夹角余弦的积 + /// 2.求四个点是否矩形使用 + /// + /// 点 + /// 点 + /// 0夹角0~90度;=0相互垂直;<0夹角90~180度]]> + public static double DotProductValue(this Point3d o, Point3d a, Point3d b) + { + var oa = o.GetVectorTo(a); + var ob = o.GetVectorTo(b); + return oa.DotProduct(ob); + //return (oa._X * ob._X) + (oa._Y * ob._Y) + (oa._Z * ob._Z); + } + + public const double Tau = Math.PI * 2.0; + + /// + /// X轴到向量的弧度,cad的获取的弧度是1PI,所以转换为2PI(上小,下大) + /// + /// 向量 + /// X轴到向量的弧度 + public static double GetAngle2XAxis(this Vector2d ve, double tolerance = 1e-6) + { + //世界重合到用户 Vector3d.XAxis->两点向量 + double al = Vector2d.XAxis.GetAngleTo(ve); + al = ve.Y > 0 ? al : Tau - al; //逆时针为正,大于0是上半圆,小于则是下半圆,如果-负值控制正反 + al = Math.Abs(Tau - al) <= tolerance ? 0 : al; + return al; + } + + + /// + /// 叉积,二维叉乘计算 + /// + /// 传参是向量,表示原点是0,0 + /// 传参是向量,表示原点是0,0 + /// 其模为a与b构成的平行四边形面积 + public static double Cross(Vector2d a, Vector2d b) + { + return a.X * b.Y - a.Y * b.X; + } + + /// + /// 叉积,二维叉乘计算 + /// + /// 原点 + /// oa向量 + /// ob向量,此为判断点 + /// 返回值有正负,表示绕原点四象限的位置变换,也就是有向面积 + public static double Cross(Point2d o, Point2d a, Point2d b) + { + return Cross(o.GetVectorTo(a), o.GetVectorTo(b)); + } + + /// + /// 叉积,逆时针方向为真 + /// + /// 直线点1 + /// 直线点2 + /// 判断点 + /// b点在oa的逆时针 + public static bool CrossAclockwise(Point2d o, Point2d a, Point2d b) + { + return Cross(o, a, b) > -1e-6;//浮点数容差考虑 + } + + + + + /// + /// 在相遇链中提取封闭的区域 + /// + /// + static List> MeetGetRegions(BoNode[] boNodes) + { + List> regions = new(); + + //步骤13 + 步骤18:从所有节点依次取出一个点 + for (int boNums = 0; boNums < boNodes.Length; boNums++) + { + var meetContain = boNodes[boNums]; + //步骤14:跳过 + if (meetContain.Meet == null) + continue; + + //这里就是论文<2 封闭区域分离算法> + //步骤15:新建闭合集 + //存在多个相遇链,论文的图4a和图4b描述这事,分别是左链和右链分享中间 + for (int i = 0; i < meetContain.Meet.Count; i++) + { + LoopList region = new(); + + //相遇节点 + var meetNode = meetContain.Meet[i]; + //if (meetNode.Steps + 1 == meetContain.Steps) //步骤17:步数差1 + // region.Add(meetContain.Parent!); + + region.Add(meetContain); + region.Add(meetNode); + + Init(boNodes); + GetLink(region); + regions.Add(region); + } + } + return regions; + } + + private static void Init(BoNode[] boNodes) + { + for (int i = 0; i < boNodes.Length; i++) + { + boNodes[i].Color = BoColor.白; + } + } + + /// + /// 相遇点代表环,所以一直点乘左转,直到闭环 + /// + /// + /// + static void GetLink(LoopList region) + { + if (region == null || region.Count == 0) + throw new ArgumentNullException(nameof(region)); + + //最后,最后的上一个 + var a = region.Last!.Previous!.Value; + var centre = region.Last!.Value;//相遇点 + + BoNode? b; + do + { + //获取左转的节点 + b = LeftTurn(a, centre); + if (b == null || b == region.Last!.Value)//剪枝之后这里是不会出现的 + break; + if (b.Color == BoColor.红) + { + //往前寻找腰身闭环处 + var find = region.Find(b); + while (region.Last != find && region.Count != 0) + region.RemoveLast(); + break; + } + b.Color = BoColor.红;//腰身闭环着色 + region.AddFirst(b); + centre = a; + a = b; + } while (true);//回到相遇点就结束 + } + + + /// + /// 左转算法 boNodeCentre=>boNode1=>boNode1.Neighbor + /// + /// 来源 + /// 中心,x轴到它的夹角 + /// 当节点邻居为1时候是空的 + static BoNode? LeftTurn(BoNode boNode1, BoNode boNodeCentre) + { + BoNode? result = null; + double angle = 0.0; + + var boCPt = new Point2d(boNodeCentre.Point.X, boNodeCentre.Point.Y); + var v1 = boCPt.GetVectorTo(new Point2d(boNode1.Point.X, boNode1.Point.Y)); + + for (int i = 0; i < boNode1.Neighbor.Count; i++) + { + var boNode3 = boNode1.Neighbor[i]; + if (boNode3 == boNodeCentre) + continue; + + var v2 = boCPt.GetVectorTo(new Point2d(boNode3.Point.X, boNode3.Point.Y)); + //求夹角最大的,就是左转的节点 + var tmp = PointEx.Tau - v2.GetAngle2XAxis() + v1.GetAngle2XAxis(); + if (tmp > angle) + { + angle = tmp; + result = boNode3; + } + } + return result; + } +#else /// /// 在相遇链中提取封闭的区域 /// @@ -347,7 +531,7 @@ static List> MeetGetRegions(BoNode[] boNodes) } else { - Debugger.Break();//这里会出现意外吗? + //Debugger.Break();//这里会出现意外吗? } GetLink(region); //O(n) regions.Add(Topo.OrderByRegionLines(region)); //O(n2) @@ -356,38 +540,6 @@ static List> MeetGetRegions(BoNode[] boNodes) return regions; } - /// - /// 调整线序 - /// - /// - static LoopList OrderByRegionLines(LoopList L) - { - if (L == null || L.Count == 0) - throw new ArgumentNullException(nameof(L)); - - LoopList list = new(); - var boNode = L.First!.Value; - for (int i = 0; i < L.Count; i++)//约束循环找顺序次数 - { - list.Add(boNode); - L.For((v, item) => { - //循环每个节点,跳过已经是L2的 - //邻居节点作为目标进入循环 - var boNode2 = item.Value; - if (boNode2 != boNode - && !list.Contains(boNode2) - && boNode.Neighbor.Contains(boNode2)) - { - boNode = boNode2;//进入循环 - return true; - } - return false; - }); - } - return list; - } - - /// /// 从相遇点开始往上寻找父节点并加入链中 /// @@ -425,22 +577,35 @@ static void GetLink(LoopList L) } } - /// - /// 初始化每个节点 + /// 调整线序 /// - /// - static void Init(BoNode[] boNodes) + /// + static LoopList OrderByRegionLines(LoopList region) { - //步骤02:初始化每个节点 - for (int i = 0; i < boNodes.Length; i++) + if (region == null || region.Count == 0) + throw new ArgumentNullException(nameof(region)); + + LoopList list = new(); + var boNode = region.First!.Value; + for (int i = 0; i < region.Count; i++)//约束循环找顺序次数 { - boNodes[i].Color = BoColor.白; - boNodes[i].Steps = int.MaxValue; - boNodes[i].Parent = null; - boNodes[i].Meet?.Clear(); + list.Add(boNode); + region.For((itemNum, item) => { + //邻居节点作为目标进入循环 + var boNode2 = item.Value; + if (boNode2 != boNode + && !list.Contains(boNode2) + && boNode.Neighbor.Contains(boNode2)) + { + boNode = boNode2;//进入循环 + return true; + } + return false; + }); } + return list; } +#endif #endregion - } \ No newline at end of file -- Gitee From fe9886c2933c72d6e72aa1034446cec27e124547 Mon Sep 17 00:00:00 2001 From: liuqihong <540762622@qq.com> Date: Fri, 29 Apr 2022 00:15:31 +0800 Subject: [PATCH 196/675] =?UTF-8?q?=E4=BF=AE=E5=A4=8D=E5=90=88=E5=B9=B6?= =?UTF-8?q?=E6=9C=9F=E9=97=B4=E5=87=BA=E9=94=99?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Topo.cs" | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git "a/src/IFoxCAD.Cad/ExtensionMethod/\351\227\255\345\220\210\350\276\271\347\225\214\347\256\227\346\263\225/Topo.cs" "b/src/IFoxCAD.Cad/ExtensionMethod/\351\227\255\345\220\210\350\276\271\347\225\214\347\256\227\346\263\225/Topo.cs" index 4ff3cbe..de38632 100644 --- "a/src/IFoxCAD.Cad/ExtensionMethod/\351\227\255\345\220\210\350\276\271\347\225\214\347\256\227\346\263\225/Topo.cs" +++ "b/src/IFoxCAD.Cad/ExtensionMethod/\351\227\255\345\220\210\350\276\271\347\225\214\347\256\227\346\263\225/Topo.cs" @@ -7,9 +7,8 @@ public class Topo static CurveCurveIntersector3d _Cci3d = new(); // cad容差类 public static Tolerance CadTolerance = new(1e-6, 1e-6); - #endregion - public List CurveList; + #endregion #region 构造 /// @@ -258,8 +257,8 @@ List> BreadthFirstSearch(BoNode[] boNodes) //步骤06 + 步骤10: //邻近节点(同一代的点)已经在邻接表找到了,这里遍历它们 - 我u.Neighbor.ForEach(邻v => { //O(n) - switch (邻v.Color) + meU.Neighbor.ForEach(meNeighborV => { //O(n) + switch (meNeighborV.Color) { case BoColor.白://步骤07: { -- Gitee From 94c628a64f134bdc407f3681a0feba07c2b74deb Mon Sep 17 00:00:00 2001 From: liuqihong <540762622@qq.com> Date: Fri, 29 Apr 2022 01:10:15 +0800 Subject: [PATCH 197/675] =?UTF-8?q?=E5=A2=9E=E5=8A=A0=E5=85=B3=E9=94=AE?= =?UTF-8?q?=E5=AD=97=E5=9B=9E=E8=B0=83?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/IFoxCAD.Basal/DictEx.cs | 26 ++-- src/IFoxCAD.Cad/ExtensionMethod/EditorEx.cs | 148 ++++++++++++++++++-- 2 files changed, 146 insertions(+), 28 deletions(-) diff --git a/src/IFoxCAD.Basal/DictEx.cs b/src/IFoxCAD.Basal/DictEx.cs index ac5746c..6fbccc4 100644 --- a/src/IFoxCAD.Basal/DictEx.cs +++ b/src/IFoxCAD.Basal/DictEx.cs @@ -7,21 +7,15 @@ namespace IFoxCAD.Basal { public static class DictEx { - public static TKey? GetKey(this IDictionary dict!!, TKey key!!) - { - if (dict.ContainsKey(key)) - { - foreach (var item in dict.Keys) - { - if (key.Equals(item)) - { - return item; - } - } - - } - return default; - - } + //public static TKey? GetKey(this IDictionary dict!!, TKey key!!) + //{ + // if (dict.ContainsKey(key)) + // { + // foreach (var item in dict.Keys) + // if (key.Equals(item)) + // return item; + // } + // return default; + //} } } diff --git a/src/IFoxCAD.Cad/ExtensionMethod/EditorEx.cs b/src/IFoxCAD.Cad/ExtensionMethod/EditorEx.cs index 3271ce1..8277507 100644 --- a/src/IFoxCAD.Cad/ExtensionMethod/EditorEx.cs +++ b/src/IFoxCAD.Cad/ExtensionMethod/EditorEx.cs @@ -56,6 +56,9 @@ public static SelectionSet SelectByLineWeight(this Editor editor, LineWeight lin } + + + public static PromptSelectionResult? SSGet(this Editor editor, string? mode = null, SelectionFilter? filter = null, string[]? messages = null, Dictionary? keywords = null) { var pso = new PromptSelectionOptions(); @@ -119,6 +122,127 @@ public static SelectionSet SelectByLineWeight(this Editor editor, LineWeight lin return ss; } + + /* + * //定义选择集选项 + * var pso = new PromptSelectionOptions + * { + * AllowDuplicates = false, //重复选择 + * }; + * + * //getai遍历全图选择块有用到 + * var dic = new Dictionary() { + * { "Z,全部同名", ()=> { + * getai = BlockHelper.EnumAttIdentical.AllBlockName; + * SendEsc.Esc(); + * }}, + * { "X,动态块显示", ()=> { + * getai = BlockHelper.EnumAttIdentical.Display; + * }}, + * { "V,属性值-默认", ()=> { + * getai = BlockHelper.EnumAttIdentical.DisplayAndTagText; + * }}, + * //允许以下操作,相同的会加入前面的 + * //{ "V,属性值-默认|X,啊啊啊啊", ()=> { + * + * //}}, + * }; + * pso.SsgetAddKeys(dic); + * + * //创建选择集过滤器,只选择块对象 + * var filList = new TypedValue[] { new TypedValue((int)DxfCode.Start, "INSERT") }; + * var filter = new SelectionFilter(filList); + * ssPsr = ed.GetSelection(pso, filter); + */ + + /// + /// 添加选择集关键字和回调 + /// + /// 选择集配置 + /// 关键字,回调委托 + /// + public static void SsgetAddKeys(this PromptSelectionOptions pso, + Dictionary dicActions) + { + Dictionary tmp = new(); + // 后缀名的|号切割,移除掉,组合成新的加入tmp + for (int i = dicActions.Count - 1; i >= 0; i--) + { + var pair = dicActions.ElementAt(i); + var key = pair.Key; + var keySp = key.Split('|'); + if (keySp.Length < 2) + continue; + + for (int j = 0; j < keySp.Length; j++) + { + var item = keySp[j]; + // 防止多个后缀通过|符越过词典约束同名 + // 后缀(key)含有,而且Action(value)不同,就把Action(value)累加到后面. + if (dicActions.ContainsKey(item)) + { + if (dicActions[item] != dicActions[key]) + dicActions[item] += dicActions[key]; + } + else + tmp.Add(item, dicActions[key]); + } + dicActions.Remove(key); + } + + foreach (var item in tmp) + dicActions.Add(item.Key, item.Value); + + //去除关键字重复的,把重复的执行动作移动到前面 + for (int i = 0; i < dicActions.Count; i++) + { + var pair1 = dicActions.ElementAt(i); + var key1 = pair1.Key; + + for (int j = dicActions.Count - 1; j > i; j--) + { + var pair2 = dicActions.ElementAt(j); + var key2 = pair2.Key; + + if (key1.Split(',')[0] == key2.Split(',')[0]) + { + if (dicActions[key1] != dicActions[key2]) + dicActions[key1] += dicActions[key2]; + dicActions.Remove(key2); + } + } + } + + foreach (var item in dicActions) + { + var keySplitS = item.Key.Split(new string[] { ",", "|" }, StringSplitOptions.RemoveEmptyEntries); + for (int i = 0; i < keySplitS.Length; i += 2) + pso.Keywords.Add(keySplitS[i], keySplitS[i], + keySplitS[i + 1] + "(" + keySplitS[i] + ")"); + } + + //回调的时候我想用Dict的O(1)索引, + //但是此函数内进行new Dictionary() 在函数栈释放的时候,它被释放掉了. + //因此 dicActions 参数的生命周期 + tmp = new(dicActions); + dicActions.Clear(); + foreach (var item in tmp) + dicActions.Add(item.Key.Split(',')[0], item.Value); + + var keyWords = pso.Keywords; + //从选择集命令中显示关键字 + pso.MessageForAdding = keyWords.GetDisplayString(true); + //关键字回调事件 ssget关键字 + pso.KeywordInput += (sender, e) => { + dicActions[e.Input].Invoke(); + }; + } + + + + + + //#region 即时选择样板 ///// @@ -685,9 +809,9 @@ public static Matrix3d GetMatrix(this Editor editor, CoordinateSystemCode from, #endif } -#endregion + #endregion -#region 缩放 + #region 缩放 /// /// 缩放窗口范围 @@ -832,9 +956,9 @@ public static void ZoomObject(this Editor ed, Entity ent, double offsetDist = 0. ed.ZoomWindow(ext.MinPoint, ext.MaxPoint, offsetDist); } -#endregion + #endregion -#region Get交互类 + #region Get交互类 /// /// 获取Point @@ -901,17 +1025,17 @@ public static PromptResult GetString(this Editor ed, string Message, string Defa return ed.GetString(strOp); } -#endregion Get交互类 + #endregion Get交互类 -#region 执行lisp + #region 执行lisp #if ac2009 - [System.Security.SuppressUnmanagedCodeSecurity] - [DllImport("acad.exe", CharSet = CharSet.Unicode, CallingConvention = CallingConvention.Cdecl, EntryPoint = "?acedEvaluateLisp@@YAHPB_WAAPAUresbuf@@@Z")] - private static extern int AcedEvaluateLisp(string lispLine, out IntPtr result); + [System.Security.SuppressUnmanagedCodeSecurity] + [DllImport("acad.exe", CharSet = CharSet.Unicode, CallingConvention = CallingConvention.Cdecl, EntryPoint = "?acedEvaluateLisp@@YAHPB_WAAPAUresbuf@@@Z")] + private static extern int AcedEvaluateLisp(string lispLine, out IntPtr result); - [DllImport("acad.exe", CallingConvention = CallingConvention.Cdecl, EntryPoint = "acedInvoke")] - private static extern int AcedInvoke(IntPtr args, out IntPtr result); + [DllImport("acad.exe", CallingConvention = CallingConvention.Cdecl, EntryPoint = "acedInvoke")] + private static extern int AcedInvoke(IntPtr args, out IntPtr result); #else [System.Security.SuppressUnmanagedCodeSecurity] [DllImport("accore.dll", CharSet = CharSet.Unicode, CallingConvention = CallingConvention.Cdecl, EntryPoint = "?acedEvaluateLisp@@YAHPEB_WAEAPEAUresbuf@@@Z")] @@ -946,5 +1070,5 @@ public static PromptResult GetString(this Editor ed, string Message, string Defa return null; } -#endregion 执行lisp + #endregion 执行lisp } -- Gitee From f87c2cdccde4ec74ea2fad68380585a632a7e076 Mon Sep 17 00:00:00 2001 From: liuqihong <540762622@qq.com> Date: Wed, 4 May 2022 21:53:12 +0800 Subject: [PATCH 198/675] =?UTF-8?q?=E5=A2=9E=E5=8A=A0=E6=96=B0=E5=BB=BA?= =?UTF-8?q?=E6=96=87=E5=AD=97=E7=B1=BB?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../AttachmentPointHelper.cs" | 95 ++++++++++ .../TextEntityAdd.cs" | 62 +++++++ .../TextInfo.cs" | 173 ++++++++++++++++++ 3 files changed, 330 insertions(+) create mode 100644 "src/IFoxCAD.Cad/ExtensionMethod/\346\226\260\345\273\272\346\226\207\345\255\227/AttachmentPointHelper.cs" create mode 100644 "src/IFoxCAD.Cad/ExtensionMethod/\346\226\260\345\273\272\346\226\207\345\255\227/TextEntityAdd.cs" create mode 100644 "src/IFoxCAD.Cad/ExtensionMethod/\346\226\260\345\273\272\346\226\207\345\255\227/TextInfo.cs" diff --git "a/src/IFoxCAD.Cad/ExtensionMethod/\346\226\260\345\273\272\346\226\207\345\255\227/AttachmentPointHelper.cs" "b/src/IFoxCAD.Cad/ExtensionMethod/\346\226\260\345\273\272\346\226\207\345\255\227/AttachmentPointHelper.cs" new file mode 100644 index 0000000..989721c --- /dev/null +++ "b/src/IFoxCAD.Cad/ExtensionMethod/\346\226\260\345\273\272\346\226\207\345\255\227/AttachmentPointHelper.cs" @@ -0,0 +1,95 @@ +namespace IFoxCAD.Cad; + +public static class AttachmentPointHelper +{ + static readonly Dictionary _alignment = new() + { + { "左上", AttachmentPoint.TopLeft }, + { "中上", AttachmentPoint.TopCenter },//单行的对齐 + { "右上", AttachmentPoint.TopRight }, + + { "左中", AttachmentPoint.MiddleLeft }, + { "正中", AttachmentPoint.MiddleCenter },//多行的正中 + { "右中", AttachmentPoint.MiddleRight }, + + { "左对齐", AttachmentPoint.BaseLeft },//※优先(放在前面优先获取) + { "左", AttachmentPoint.BaseLeft }, + + { "中间", AttachmentPoint.BaseMid }, + + { "右对齐", AttachmentPoint.BaseRight },//※优先(放在前面优先获取) + { "右", AttachmentPoint.BaseRight }, + + { "左下", AttachmentPoint.BottomLeft }, + { "中下", AttachmentPoint.BottomCenter }, + { "右下", AttachmentPoint.BottomRight }, + + { "对齐", AttachmentPoint.BaseAlign },//※优先(放在前面优先获取) + { "调整", AttachmentPoint.BaseAlign }, + + { "居中", AttachmentPoint.BaseCenter },//单行的中 + { "铺满", AttachmentPoint.BaseFit }, + }; + + /// + /// 输入文字获得对齐方式 + /// + /// + /// + public static AttachmentPoint Get(string key) + { + return _alignment[key]; + } + + /// + /// 输入对齐方式获得文字 + /// + /// + /// + public static string Get(AttachmentPoint value) + { + return _alignment.FirstOrDefault(q => q.Value == value).Key; + } +} + +#if false +//反射描述 +//这些东西cad没有用到啊...所以不纳入了 +public enum AttachmentPoint2 +{ + [Description("下对齐")] + BottomAlign = 14, + [Description("中对齐")] + MiddleAlign = 15,//0xF + [Description("上对齐")] + TopAlign = 16,//0x10 + [Description("下铺满")] + BottomFit = 18, + [Description("中铺满")] + MiddleFit = 19, + [Description("上铺满")] + TopFit = 20, + [Description("下居中")] + BottomMid = 22, + [Description("中居中")] + MiddleMid = 23, + [Description("下居中")] + TopMid = 24, +} + +public static Dictionary GetEnumDic(Type enumType) +{ + Dictionary dic = new(); + var fieldinfos = enumType.GetFields(); + for (int i = 0; i < fieldinfos.Length; i++) + { + var field = fieldinfos[i]; + if (field.FieldType.IsEnum) + { + var objs = field.GetCustomAttributes(typeof(DescriptionAttribute), false); + dic.Add(field.Name, ((DescriptionAttribute)objs[0]).Description); + } + } + return dic; +} +#endif diff --git "a/src/IFoxCAD.Cad/ExtensionMethod/\346\226\260\345\273\272\346\226\207\345\255\227/TextEntityAdd.cs" "b/src/IFoxCAD.Cad/ExtensionMethod/\346\226\260\345\273\272\346\226\207\345\255\227/TextEntityAdd.cs" new file mode 100644 index 0000000..0f06aab --- /dev/null +++ "b/src/IFoxCAD.Cad/ExtensionMethod/\346\226\260\345\273\272\346\226\207\345\255\227/TextEntityAdd.cs" @@ -0,0 +1,62 @@ +namespace IFoxCAD.Cad; + +public static partial class EntityAdd +{ + /// + /// 创建单行文字 + /// + /// 数据库 + /// 内容 + /// 插入点 + /// 字体高度 + /// 文字样式 + /// 对齐方式 + /// 对齐点,因样式 可能无效 + /// + public static Entity AddDBTextToEntity(this Database db, + string textContents, + Point3d position, + double textHigh = 2.5, + ObjectId? textStyleId = null, + AttachmentPoint justify = AttachmentPoint.BaseLeft, + Point3d? justifyPoint = null) + { + var TextInfo = new TextInfo( + textContents, + position, + justify, + justifyPoint, + textStyleId, + textHigh, + db); + return TextInfo.AddDBTextToEntity(); + } + + /// + /// 新建多行文字 + /// + /// 数据库 + /// 内容 + /// 插入点 + /// 字体高度 + /// 文字样式 + /// 对齐方式 + /// + public static Entity AddMTextToEntity(this Database db, + string textContents, + Point3d position, + double textHigh = 2.5, + ObjectId? textStyleId = null, + AttachmentPoint justify = AttachmentPoint.BaseLeft) + { + var TextInfo = new TextInfo( + textContents, + position, + justify, + null, + textStyleId, + textHigh, + db); + return TextInfo.AddMTextToEntity(); + } +} diff --git "a/src/IFoxCAD.Cad/ExtensionMethod/\346\226\260\345\273\272\346\226\207\345\255\227/TextInfo.cs" "b/src/IFoxCAD.Cad/ExtensionMethod/\346\226\260\345\273\272\346\226\207\345\255\227/TextInfo.cs" new file mode 100644 index 0000000..c652ded --- /dev/null +++ "b/src/IFoxCAD.Cad/ExtensionMethod/\346\226\260\345\273\272\346\226\207\345\255\227/TextInfo.cs" @@ -0,0 +1,173 @@ +namespace IFoxCAD.Cad; + +/// +/// 文字信息类 +/// +public class TextInfo +{ + Database? Database; + string? Contents; + Point3d Position; + + public string TextJustifyCn => AttachmentPointHelper.Get(TextJustify); + AttachmentPoint TextJustify; + Point3d? AlignmentPoint; + + double TextHeight; + ObjectId? TextStyleId; + + /// + /// 文字信息类 + /// + /// 内容 + /// 基点 + /// 对齐方式 + /// 对齐点(对齐方式是左,此参数无效) + /// 文字样式id + /// 文字高度 + /// 数据库 + public TextInfo(string? contents, + Point3d position, + AttachmentPoint justify, + Point3d? justifyPoint, + ObjectId? textStyleId = null, + double textHeight = 2.5, + Database? database = null) + { + Contents = contents; + Position = position; + TextJustify = justify; + AlignmentPoint = justifyPoint; + TextHeight = textHeight; + TextStyleId = textStyleId; + Database = database; + } + + /// + /// 创建单行文字 + /// + public DBText AddDBTextToEntity() + { + if (string.IsNullOrEmpty(Contents)) + throw new ArgumentNullException(nameof(Contents) + "创建文字无内容"); + + var acText = new DBText(); + acText.SetDatabaseDefaults(); + + if (Database is not null) + acText.SetDatabaseDefaults(Database);//我的默认值是填满的,所以可以不需要 + + if (TextStyleId is not null) + acText.SetTextStyleId(TextStyleId.Value); + + acText.Height = TextHeight; //高度 + acText.TextString = Contents; //内容 + acText.Position = Position; //插入点(一定要先设置) + acText.Justify = TextJustify; //使他们对齐 + + if (AlignmentPoint is not null) + acText.AlignmentPoint = AlignmentPoint.Value; + else if (acText.Justify != AttachmentPoint.BaseLeft) + acText.AlignmentPoint = Position; + + if (Database is not null) + acText.AdjustAlignment(Database); + return acText; + } + + /// + /// 创建多行文字 + /// + /// + public MText AddMTextToEntity() + { + if (string.IsNullOrEmpty(Contents)) + throw new ArgumentNullException(nameof(Contents) + "创建文字无内容"); + + var mText = new MText(); + mText.SetDatabaseDefaults(); + + if (Database is not null) + mText.SetDatabaseDefaults(Database); + + if (TextStyleId is not null) + mText.SetTextStyleId(TextStyleId.Value); + + mText.TextHeight = TextHeight; //高度 + mText.Contents = Contents; //内容 + mText.Location = Position; //插入点(一定要先设置) + + //mText.SetAttachmentMovingLocation(TextJustify); + mText.Attachment = TextJustify;//使他们对齐 + + return mText; + } +} + +//反射设定对象的文字样式id +public static partial class TextInfoHelper +{ + /// + /// 设置文字样式id + /// + /// 单行文字 + /// 文字样式表记录id + public static void SetTextStyleId(this DBText acText, ObjectId ltrObjectId) + { + SetEntityTxtStyleId(acText, ltrObjectId); + } + + /// + /// 设置文字样式id + /// + /// 多行文字 + /// 文字样式表记录id + public static void SetTextStyleId(this MText acText, ObjectId ltrObjectId) + { + SetEntityTxtStyleId(acText, ltrObjectId); + } + + static void SetEntityTxtStyleId(Entity acText, ObjectId ltrObjectId) + { + GetTextStyleIdType(acText)?.SetValue(acText, ltrObjectId, null); + } + + /// + /// 获取文字样式id + /// + public static ObjectId GetTextStyleId(this DBText acText) + { + return GetEntityTxtStyleId(acText); + } + + /// + /// 获取文字样式id + /// + public static ObjectId GetTextStyleId(this MText acText) + { + return GetEntityTxtStyleId(acText); + } + + static ObjectId GetEntityTxtStyleId(Entity acText) + { + var result = ObjectId.Null; + var id = GetTextStyleIdType(acText)?.GetValue(acText, null); + if (id != null) + result = (ObjectId)id; + return result; + } + + static PropertyInfo? _textStyleId = null; + static PropertyInfo GetTextStyleIdType(Entity acText) + { + if (_textStyleId == null) + { + var entType = acText.GetType(); + var prs = entType.GetProperties(); + _textStyleId = prs.FirstOrDefault(a => a.Name == "TextStyle");//反射获取属性 + if (_textStyleId == null) + _textStyleId = prs.FirstOrDefault(a => a.Name == "TextStyleId");//反射获取属性 + } + return _textStyleId; + } +} -- Gitee From 04524fd807995ed2492b491dcc0842a6876bc4b1 Mon Sep 17 00:00:00 2001 From: liuqihong <540762622@qq.com> Date: Wed, 4 May 2022 22:11:53 +0800 Subject: [PATCH 199/675] =?UTF-8?q?=E5=A2=9E=E5=8A=A0Jig(#I55MNM)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/IFoxCAD.Cad/ExtensionMethod/Jig.cs | 403 +++++++++++++++++++++++++ 1 file changed, 403 insertions(+) create mode 100644 src/IFoxCAD.Cad/ExtensionMethod/Jig.cs diff --git a/src/IFoxCAD.Cad/ExtensionMethod/Jig.cs b/src/IFoxCAD.Cad/ExtensionMethod/Jig.cs new file mode 100644 index 0000000..5ffb377 --- /dev/null +++ b/src/IFoxCAD.Cad/ExtensionMethod/Jig.cs @@ -0,0 +1,403 @@ +/* 封装jig + * 20220503 cad22需要防止刷新过程中更改队列,08不会有. + * 20220326 重绘图元的函数用错了,现在修正过来 + * 20211216 加入块表时候做一个差集,剔除临时图元 + * 20211209 补充正交变量设置和回收设置 + * 作者: 惊惊⎛⎝◕⏝⏝◕。⎠⎞ ⎛⎝≥⏝⏝0⎠⎞ ⎛⎝⓿⏝⏝⓿。⎠⎞ ⎛⎝≥⏝⏝≤⎠⎞ + * 博客: https://www.cnblogs.com/JJBox/p/15650770.html + * + * 例子1: + * var ptjig = new Jig(); + * ptjig.SetOptions(Point3d.Origin); + * var pr = ptjig.Drag(); + * if (pr.Status != PromptStatus.OK) + * return null; + * + * 例子2: + * var ppo1 = new PromptPointOptions(Environment.NewLine + "输入矩形角点1:<空格退出>") + * { + * AllowArbitraryInput = true,//任意输入 + * AllowNone = true //允许回车 + * }; + * var ppr1 = ed.GetPoint(ppo1);//用户点选 + * if (ppr1.Status != PromptStatus.OK) + * return; + * var getPt = ppr1.Value; + * + * var recEntityJig = new Jig((mousePoint, drawEntitys) => { + * #region 画柜子图形 + * double length = Math.Abs(getPt.X - mousePoint.X); + * double high = Math.Abs(getPt.Y - mousePoint.Y); + * var ent = AddRecToEntity(Point3d.Origin, new Point3d(length, high, 0)); + * drawEntitys.Enqueue(ent); + * #endregion + * }); + * recEntityJig.SetOptions("指定矩形角点:", new Dictionary() { { "Z", "中间(Z)" } ); + * + * bool flag = true; + * while (flag) + * { + * var pr = recEntityJig.Drag(); + * if (string.IsNullOrEmpty(pr.StringResult))//在无输入的时候会等于空 + * flag = false; + * else + * { + * switch (pr.StringResult.ToUpper()) //注意cad保留 https://www.cnblogs.com/JJBox/p/10224631.html + * { + * case "Z": + * ed.WriteMessage("\n您触发了z关键字"); + * break; + * case " ": + * flag = false;//空格结束 + * break; + * } + * } + * } + * //开启事务之后,图元加入数据库 + * db.Action(tr=>{ + * recEntityJig.AddEntityToMsPs(tr); + * }); + */ + +namespace IFoxCAD.Cad; + +using Autodesk.AutoCAD.GraphicsInterface; +using Acap = Autodesk.AutoCAD.ApplicationServices.Application; + +public delegate void WorldDrawEvent(WorldDraw draw); +public class Jig : DrawJig +{ + #region 成员 + /// + /// 事件:默认是图元刷新,其余的:亮显/暗显等等工作自由补充 + /// + public event WorldDrawEvent? WorldDrawEvent; + /// + /// 最后的鼠标点,用来确认长度 + /// + public Point3d MousePointWcsLast; + /// + /// 最后的图元,用来生成 + /// + public Entity[] Entitys => _drawEntitys.ToArray(); + + Autodesk.AutoCAD.Geometry.Tolerance _tolerance; + + Queue _drawEntitys;//重复生成的图元,放在这里刷新 + Action>? _action; + JigPromptPointOptions? _options; + const string _orthomode = "orthomode"; + bool _systemVariablesOrthomode = false; //正交修改 + bool _worldDrawFlag = false; // 防止刷新过程中更改队列 + #endregion + + #region 构造 + /// + /// 在界面绘制图元 + /// + /// + /// 用来频繁执行的回调: 鼠标点,加入显示图元的容器 + /// + /// 鼠标移动的容差 + public Jig(Action>? action = null, double tolerance = 1e-6) + { + _action = action; + _tolerance = new(tolerance, tolerance); + _drawEntitys = new(); + } + #endregion + + #region 方法 + + + + /// + /// 鼠标配置:基点 + /// + /// 基点 + /// 提示信息 + /// 光标绑定 + /// 正交开关 + public JigPromptPointOptions SetOptions(Point3d basePoint, + CursorType cursorType = CursorType.RubberBand, + string msg = "点选第二点", + bool orthomode = false) + { + if (orthomode && CadSystem.Getvar(_orthomode) != "1") + { + CadSystem.Setvar(_orthomode, "1");//1正交,0非正交 //setvar: https://www.cnblogs.com/JJBox/p/10209541.html + _systemVariablesOrthomode = true; + } + var tmp = new JigPromptPointOptions(Environment.NewLine + msg) + { + Cursor = cursorType, //光标绑定 + UseBasePoint = true, //基点打开 + BasePoint = basePoint, //基点设定 + + //用户输入控件: 由UCS探测用 | 接受三维坐标 + UserInputControls = + UserInputControls.GovernedByUCSDetect | + UserInputControls.Accept3dCoordinates + }; + _options = tmp; + return _options; + } + + /// + /// 鼠标配置:提示信息,关键字 + /// + /// 信息 + /// 关键字 + /// 正交开关 + /// + public JigPromptPointOptions SetOptions(string msg, Dictionary? keywords = null, bool orthomode = false) + { + if (orthomode && CadSystem.Getvar(_orthomode) != "1") + { + CadSystem.Setvar(_orthomode, "1");//1正交,0非正交 //setvar: https://www.cnblogs.com/JJBox/p/10209541.html + _systemVariablesOrthomode = true; + } + + var tmp = new JigPromptPointOptions(Environment.NewLine + msg) + { + //用户输入控件: 由UCS探测用 | 接受三维坐标 + UserInputControls = + UserInputControls.GovernedByUCSDetect | + UserInputControls.Accept3dCoordinates + }; + + //加入关键字,加入时候将空格内容放到最后 + string spaceValue = string.Empty; + const string spaceKey = " "; + if (keywords != null) + { + var ge = keywords.GetEnumerator(); + while (ge.MoveNext()) + { + if (ge.Current.Key == spaceKey) + spaceValue = ge.Current.Value; + else + tmp.Keywords.Add(ge.Current.Key, ge.Current.Key, ge.Current.Value); + } + } + + //要放最后,才能优先触发其他关键字 + if (spaceValue != string.Empty) + tmp.Keywords.Add(spaceKey, spaceKey, spaceValue); + else + tmp.Keywords.Add(spaceKey, spaceKey, "<空格退出>"); + + _options = tmp; + return _options; + } + + /// + /// 鼠标配置:自定义 + /// + /// + /// 正交开关 + public void SetOptions(Action action, bool orthomode = false) + { + var tmp = new JigPromptPointOptions(); + action.Invoke(tmp); + _options = tmp; + + if (orthomode && CadSystem.Getvar(_orthomode) != "1") + { + CadSystem.Setvar(_orthomode, "1");//1正交,0非正交 //setvar: https://www.cnblogs.com/JJBox/p/10209541.html + _systemVariablesOrthomode = true; + } + } + + /// + /// 执行 + /// + /// + public PromptResult Drag() + { + //jig功能必然是当前前台文档,所以封装内部更好调用 + var dm = Acap.DocumentManager; + var doc = dm.MdiActiveDocument; + var ed = doc.Editor; + var dr = ed.Drag(this); + if (_systemVariablesOrthomode) + CadSystem.Setvar(_orthomode, "0");//1正交,0非正交 //setvar: https://www.cnblogs.com/JJBox/p/10209541.html + return dr; + } + + /// + /// 最后一次的图元加入数据库 + /// + /// 事务 + /// 不生成的图元用于排除,例如刷新时候的提示文字 + /// + public IEnumerable? AddEntityToMsPs(DBTrans tr, + IEnumerable? removeEntity = null) + { + var ents = Entitys; + if (ents.Length == 0) + return null; + + var ids = new List(); + IEnumerable es = ents; + if (removeEntity != null) + es = es.Except(removeEntity); + + var ge = es.GetEnumerator(); + while (ge.MoveNext()) + ids.Add(tr.CurrentSpace.AddEntity(ge.Current)); + + return ids; + } + + #endregion + + #region 重写 + /// + /// 鼠标频繁采点 + /// + /// + /// 返回状态:令频繁刷新结束 + protected override SamplerStatus Sampler(JigPrompts prompts) + { + if (_worldDrawFlag) + return SamplerStatus.NoChange;//OK的时候拖动鼠标与否都不出现图元 + + if (_options is null) + throw new ArgumentNullException(nameof(_options)); + + var pro = prompts.AcquirePoint(_options); + if (pro.Status == PromptStatus.Keyword) + return SamplerStatus.OK; + else if (pro.Status != PromptStatus.OK) + return SamplerStatus.Cancel; + + //上次鼠标点不同(一定要这句,不然图元刷新太快会看到奇怪的边线) + var mousePointWcs = pro.Value; + + //== 是比较类字段,但是最好转为哈希比较. + //IsEqualTo 是方形判断(仅加法),但是cad是距离. + //Distance 是圆形判断(会求平方根,使用了牛顿迭代), + //大量数据(十万以上/频繁刷新)面前会显得非常慢. + if (mousePointWcs.IsEqualTo(MousePointWcsLast, _tolerance)) + return SamplerStatus.NoChange; + + //上次循环的缓冲区图元清理,否则将会在vs输出遗忘 Dispose + while (_drawEntitys.Count > 0) + _drawEntitys.Dequeue().Dispose(); + + //委托把容器扔出去接收新创建的图元,然后给重绘更新 + _action?.Invoke(mousePointWcs, _drawEntitys); + MousePointWcsLast = mousePointWcs; + + return SamplerStatus.OK; + } + + /* WorldDraw 封装外的操作说明: + * 0x01 + * 我有一个业务是一次性生成四个方向的箭头,因为cad08缺少瞬时图元, + * 那么可以先提交一次事务,再开一个事务,把Entity传给jig,最后选择删除部分. + * 虽然这个是可行的方案,但是Entity穿越事务本身来说是非必要不使用的. + * 0x02 + * 四个箭头最近鼠标的亮显,其余淡显, + * 在jig使用淡显ent.Unhighlight和亮显ent.Highlight() + * 需要绕过重绘,否则重绘将导致图元频闪,令这两个操作失效, + * 此时需要自定义一个集合 EntityList (不使用本函数的_drawEntitys) + * 再将 EntityList 传给 WorldDrawEvent 事件,事件内实现亮显和淡显. + * 0x03 + * draw.Geometry.Draw(_drawEntitys[i]); + * 此函数有问题,acad08克隆一份数组也可以用来刷新, + * 而arx上面的jig只能一次改一个,所以可以用此函数. + * 起因是此函数属于异步刷新, + * 同步上下文的刷新是 RawGeometry + * 0x04 + * cad22测试出现,08不会, + * draw.RawGeometry.Draw(ent);会跳到 Sampler(),所以设置 _worldDrawFlag + * 但是禁止重绘重入的话(令图元不频繁重绘),那么鼠标停着的时候就看不见图元, + * 所以只能重绘结束的时候才允许鼠标采集,采集过程的时候不会触发重绘, + * 这样才可以保证容器在重绘中不被更改. + */ + + /// + /// 重绘图形 + /// + protected override bool WorldDraw(WorldDraw draw) + { + _worldDrawFlag = true; + WorldDrawEvent?.Invoke(draw); + _drawEntitys.ForEach(ent => { + draw.RawGeometry.Draw(ent); + }); + _worldDrawFlag = false; + return true; + } + #endregion +} + +class CadSystem +{ + /// + /// 获取系统变量值 + /// + /// 变量名 + /// 成功获取值,失败null + public static string? Getvar(string name) + { + return Acap.GetSystemVariable(name).ToString(); + } + /// + /// 设置系统或环境变量 + /// + /// 变量名 + /// 变量值 + /// 成功设置返回值,失败null + public static void Setvar(string? name, string? parameter) + { + if (name is null) + throw new ArgumentNullException(nameof(name)); + if (parameter is null) + throw new ArgumentNullException(nameof(parameter)); + + try + { + //改系统变量 + var value = Acap.GetSystemVariable(name); + if (value is null) return; + var valueTypeName = value.GetType().Name; + //如果出现了clayer无法设置,是没有锁文档导致的 + switch (valueTypeName) + { + case "String": + Acap.SetSystemVariable(name, parameter.Replace("\"", ""));//去掉引号 + break; + case "Double": + Acap.SetSystemVariable(name, double.Parse(parameter)); + break; + case "Int16": + Acap.SetSystemVariable(name, short.Parse(parameter)); + break; + case "Int32": + Acap.SetSystemVariable(name, int.Parse(parameter)); + break; + } + } + catch + { } + } +} + +#if false +| UserInputControls.NullResponseAccepted //接受空响应 +| UserInputControls.DoNotEchoCancelForCtrlC //不要取消CtrlC的回音 +| UserInputControls.DoNotUpdateLastPoint //不要更新最后一点 +| UserInputControls.NoDwgLimitsChecking //没有Dwg限制检查 +| UserInputControls.NoZeroResponseAccepted //接受非零响应 +| UserInputControls.NoNegativeResponseAccepted //不否定回复已被接受 +| UserInputControls.Accept3dCoordinates //返回点的三维坐标,是转换坐标系了? +| UserInputControls.AcceptMouseUpAsPoint //接受释放按键时的点而不是按下时 +| UserInputControls.AnyBlankTerminatesInput //任何空白终止输入 +| UserInputControls.InitialBlankTerminatesInput //初始空白终止输入 +| UserInputControls.AcceptOtherInputString //接受其他输入字符串 +| UserInputControls.NoZDirectionOrtho //无方向正射,直接输入数字时以基点到当前点作为方向 +| UserInputControls.UseBasePointElevation //使用基点高程,基点的Z高度探测 +#endif \ No newline at end of file -- Gitee From 4a50159c12d22add32972282724a1fbef6248cf6 Mon Sep 17 00:00:00 2001 From: vicwjb <403009819@qq.com> Date: Wed, 4 May 2022 23:38:08 +0800 Subject: [PATCH 200/675] =?UTF-8?q?=E4=BC=98=E5=8C=96env.setvar=E5=87=BD?= =?UTF-8?q?=E6=95=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/IFoxCAD.Cad/ExtensionMethod/Jig.cs | 12 +++++++++--- src/IFoxCAD.Cad/IFoxCAD.Cad.csproj | 2 +- src/IFoxCAD.Cad/Runtime/Env.cs | 11 ++++++++++- tests/Test/testenv.cs | 9 ++++++++- 4 files changed, 28 insertions(+), 6 deletions(-) diff --git a/src/IFoxCAD.Cad/ExtensionMethod/Jig.cs b/src/IFoxCAD.Cad/ExtensionMethod/Jig.cs index 5ffb377..c680b85 100644 --- a/src/IFoxCAD.Cad/ExtensionMethod/Jig.cs +++ b/src/IFoxCAD.Cad/ExtensionMethod/Jig.cs @@ -119,9 +119,9 @@ public Jig(Action>? action = null, double tolerance = 1e- /// 光标绑定 /// 正交开关 public JigPromptPointOptions SetOptions(Point3d basePoint, - CursorType cursorType = CursorType.RubberBand, - string msg = "点选第二点", - bool orthomode = false) + CursorType cursorType = CursorType.RubberBand, + string msg = "点选第二点", + bool orthomode = false) { if (orthomode && CadSystem.Getvar(_orthomode) != "1") { @@ -334,8 +334,14 @@ protected override bool WorldDraw(WorldDraw draw) #endregion } + + + class CadSystem { +/* 此类函数和env类重复 + * todo:计划删除 + */ /// /// 获取系统变量值 /// diff --git a/src/IFoxCAD.Cad/IFoxCAD.Cad.csproj b/src/IFoxCAD.Cad/IFoxCAD.Cad.csproj index f62a078..e82844a 100644 --- a/src/IFoxCAD.Cad/IFoxCAD.Cad.csproj +++ b/src/IFoxCAD.Cad/IFoxCAD.Cad.csproj @@ -6,7 +6,7 @@ enable true true - 0.3.3.2 + 0.3.3.3 InspireFunction xsfhlzh;vicwjb 基于.NET的Cad二次开发类库 diff --git a/src/IFoxCAD.Cad/Runtime/Env.cs b/src/IFoxCAD.Cad/Runtime/Env.cs index 4b5beda..a7d28ed 100644 --- a/src/IFoxCAD.Cad/Runtime/Env.cs +++ b/src/IFoxCAD.Cad/Runtime/Env.cs @@ -389,7 +389,16 @@ public static object GetVar(string varName) /// 变量值 public static void SetVar(string varName, object value) { - Application.SetSystemVariable(varName, value); + try + { + Application.SetSystemVariable(varName, value); + } + catch (System.Exception) + { + + Env.Print($"{varName} 是不存在的变量!"); + } + } /// /// 获取系统环境变量 diff --git a/tests/Test/testenv.cs b/tests/Test/testenv.cs index 5de650f..5e0aa19 100644 --- a/tests/Test/testenv.cs +++ b/tests/Test/testenv.cs @@ -76,6 +76,13 @@ public void testcadver() } - + [CommandMethod("TestGetVar")] + public void TestGetVar() + { + // test getvar + var a = Env.GetVar("dbmod"); + a.Print(); + Env.SetVar("dbmod1", 1); + } } } -- Gitee From 2402ed2ee9e147ce446faea4a260a1e8a4fd768a Mon Sep 17 00:00:00 2001 From: vicwjb <403009819@qq.com> Date: Thu, 5 May 2022 23:15:18 +0800 Subject: [PATCH 201/675] =?UTF-8?q?=E4=BF=AE=E5=A4=8DeNotFromThisDocument?= =?UTF-8?q?=E5=BC=82=E5=B8=B8=EF=BC=8C=EF=BC=88https://gitee.com/inspirefu?= =?UTF-8?q?nction/ifoxcad/issues/I55Y4P=EF=BC=89?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/IFoxCAD.Cad/Runtime/DBTrans.cs | 19 ++-- tests/Test/testenv.cs | 138 +++++++++++++---------------- 2 files changed, 69 insertions(+), 88 deletions(-) diff --git a/src/IFoxCAD.Cad/Runtime/DBTrans.cs b/src/IFoxCAD.Cad/Runtime/DBTrans.cs index 8e258c4..163da44 100644 --- a/src/IFoxCAD.Cad/Runtime/DBTrans.cs +++ b/src/IFoxCAD.Cad/Runtime/DBTrans.cs @@ -43,24 +43,15 @@ public static DBTrans Top * 然后我新建了一个文档,再进行命令=>又进入Top,Top返回了前一个文档的事务 * 因此所以无法清理栈,所以Dispose不触发,导致无法刷新图元和Ctrl+Z出错 * 所以用AOP方式修复 + * + * 0x03 + * 经过艰苦卓绝的测试,aop模式由于不能断点调试,所以暂时放弃。 */ -#if true - //不使用AOP方式修复,强迫用户先开启事务 + + // 由于大量的函数依赖本属性,强迫用户先开启事务 if (dBTrans.Count == 0) throw new ArgumentNullException("事务栈没有任何事务,请在调用前创建:" + nameof(DBTrans)); - - var db = Application.DocumentManager.MdiActiveDocument.Database; var trans = dBTrans.Peek(); - if (trans.Database != db) - trans = new DBTrans(); -#else - //使用AOP方式修复 - DBTrans trans; - if (dBTrans.Count == 0) - trans = new DBTrans(); - else - trans = dBTrans.Peek(); -#endif return trans; } } diff --git a/tests/Test/testenv.cs b/tests/Test/testenv.cs index 5e0aa19..56d92ab 100644 --- a/tests/Test/testenv.cs +++ b/tests/Test/testenv.cs @@ -1,88 +1,78 @@ -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; -using IFoxCAD.Cad; -using Autodesk.AutoCAD.Runtime; -using Autodesk.AutoCAD.ApplicationServices; -using Autodesk.AutoCAD.DatabaseServices; +namespace Test; -namespace Test +public class Testenv { - public class testenv + [CommandMethod("testenum")] + public void Testenum() { - [CommandMethod("testenum")] - public void testenum() - { - - Env.CmdEcho = true; - - } - [CommandMethod("testenum1")] - public void testenum1() - { - - Env.CmdEcho = false; - - } + + Env.CmdEcho = true; + + } + [CommandMethod("testenum1")] + public void Testenum1() + { + + Env.CmdEcho = false; + + } - [CommandMethod("testdimblk")] - public void testdimblk() - { + [CommandMethod("testdimblk")] + public void testdimblk() + { - Env.Dimblk = Env.DimblkType.Dot; - Env.Dimblk = Env.DimblkType.Defult; + Env.Dimblk = Env.DimblkType.Dot; + Env.Dimblk = Env.DimblkType.Defult; - } - [CommandMethod("testdimblk1")] - public void testdimblk1() - { - var dim = Env.Dimblk; - Env.Editor.WriteMessage(dim.ToString()); + } + [CommandMethod("testdimblk1")] + public void Testdimblk1() + { + var dim = Env.Dimblk; + Env.Editor.WriteMessage(dim.ToString()); - } + } - [CommandMethod("testosmode")] - public void testosmode() - { - // 设置osmode变量,多个值用逻辑或 - Env.OSMode = Env.OSModeType.End | Env.OSModeType.Middle; - // 也可以直接写数值,进行强转 - Env.OSMode = (Env.OSModeType)5179; - // 追加模式 - Env.OSMode |= Env.OSModeType.Center; - //检查是否有某个模式 - var os = Env.OSMode.Include(Env.OSModeType.Center); - // 取消某个模式 - Env.OSMode ^= Env.OSModeType.Center; - Env.Editor.WriteMessage(Env.OSMode.ToString()); - } - [CommandMethod("testosmode1")] - public void testosmode1() - { - var dim = Env.OSMode; - Env.Editor.WriteMessage(dim.ToString()); + [CommandMethod("testosmode")] + public void Testosmode() + { + // 设置osmode变量,多个值用逻辑或 + Env.OSMode = Env.OSModeType.End | Env.OSModeType.Middle; + // 也可以直接写数值,进行强转 + Env.OSMode = (Env.OSModeType)5179; + // 追加模式 + Env.OSMode |= Env.OSModeType.Center; + //检查是否有某个模式 + var os = Env.OSMode.Include(Env.OSModeType.Center); + // 取消某个模式 + Env.OSMode ^= Env.OSModeType.Center; + Env.Editor.WriteMessage(Env.OSMode.ToString()); + } + [CommandMethod("testosmode1")] + public void Testosmode1() + { + var dim = Env.OSMode; + Env.Editor.WriteMessage(dim.ToString()); - } + } - [CommandMethod("testcadver")] - public void testcadver() - { - //Env.Print(AcadVersion.Versions); - AcadVersion.Versions.ForEach(v => Env.Print(v)); - AcadVersion.FromApp(Application.AcadApplication).Print(); - 1.Print(); - "1".Print(); + [CommandMethod("testcadver")] + public void Testcadver() + { + //Env.Print(AcadVersion.Versions); + AcadVersion.Versions.ForEach(v => Env.Print(v)); + AcadVersion.FromApp(Application.AcadApplication).Print(); + 1.Print(); + "1".Print(); - } + } - [CommandMethod("TestGetVar")] - public void TestGetVar() - { - // test getvar - var a = Env.GetVar("dbmod"); - a.Print(); - Env.SetVar("dbmod1", 1); - } + [CommandMethod("TestGetVar")] + public void TestGetVar() + { + // test getvar + var a = Env.GetVar("dbmod"); + a.Print(); + Env.SetVar("dbmod1", 1); } } -- Gitee From bc021bedcb1536ba32926143f00dde2dce48dc7f Mon Sep 17 00:00:00 2001 From: liuqihong <540762622@qq.com> Date: Thu, 5 May 2022 23:31:37 +0800 Subject: [PATCH 202/675] =?UTF-8?q?=E4=BC=98=E5=8C=96=E6=B5=8B=E8=AF=95?= =?UTF-8?q?=E4=BB=A3=E7=A0=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- tests/Test/testblock.cs | 76 ++++++++++++++++++++--------------------- 1 file changed, 38 insertions(+), 38 deletions(-) diff --git a/tests/Test/testblock.cs b/tests/Test/testblock.cs index ebe58dc..110280b 100644 --- a/tests/Test/testblock.cs +++ b/tests/Test/testblock.cs @@ -8,8 +8,7 @@ public void BlockDef() using var tr = new DBTrans(); //var line = new Line(new Point3d(0, 0, 0), new Point3d(1, 1, 0)); tr.BlockTable.Add("test", - btr => - { + btr => { btr.Origin = new Point3d(0, 0, 0); }, () => //图元 @@ -26,13 +25,11 @@ public void BlockDef() //ObjectId objectId = tr.BlockTable.Add("a");//新建块 //objectId.GetObject().AddEntity();//测试添加空实体 tr.BlockTable.Add("test1", - btr => - { + btr => { btr.Origin = new Point3d(0, 0, 0); }, - () => - { + () => { return new List { new Line(new Point3d(0, 0, 0), new Point3d(1, 1, 0)) , new DBText{ Position = new Point3d(0,0,0), TextString = "123" @@ -55,11 +52,10 @@ public void BlockDefChange() //}); - - tr.BlockTable.Change("test", btr => - { + + tr.BlockTable.Change("test", btr => { foreach (var id in btr) { var ent = tr.GetObject(id); @@ -74,12 +70,12 @@ public void BlockDefChange() if (ent is Hatch hatch) { hatch.ColorIndex = 0; - + } } - + } }); tr.Editor.Regen(); @@ -161,7 +157,7 @@ public void TestBlockNullBug() public void TestBlockFile() { var tr = new DBTrans(); - var id = tr.BlockTable.GetBlockFrom(@"C:\Users\vic\Desktop\test.dwg",false); + var id = tr.BlockTable.GetBlockFrom(@"C:\Users\vic\Desktop\test.dwg", false); tr.CurrentSpace.InsertBlock(Point3d.Origin, id); } @@ -171,8 +167,7 @@ public void TestClipBlock() { using var tr = new DBTrans(); tr.BlockTable.Add("test1", - btr => - { + btr => { btr.Origin = new Point3d(0, 0, 0); btr.AddEntity(new Line(new Point3d(0, 0, 0), new Point3d(10, 10, 0)), new Line(new Point3d(10, 10, 0), new Point3d(10, 0, 0)) @@ -218,10 +213,9 @@ public void EJ() var blockdef = tr.BlockTable.GetBlockFrom(fullFileName, false); tr.Database.Clayer = tr.LayerTable["0"];//当前图层切换为0图层 - tr.LayerTable.Change(tr.Database.Clayer, ltr => - { + tr.LayerTable.Change(tr.Database.Clayer, ltr => { ltr.Color = Color.FromColorIndex(ColorMethod.ByAci, 2); //ColorMethod.ByAci可以让我们使用AutoCAD ACI颜色索引……这里为2(表示黄色) - }); + }); ObjectId id = tr.ModelSpace.InsertBlock(Point3d.Origin, blockdef);//插入块参照 @@ -231,7 +225,7 @@ public void EJ() var entTest = tr.GetObject(id); entTest.Draw(); - + } using var tr2 = new DBTrans(); @@ -252,8 +246,7 @@ public void EJ() var btr = tr2.BlockTable[Bref.Name]; - tr2.BlockTable.Change(btr, ltr => - { + tr2.BlockTable.Change(btr, ltr => { foreach (ObjectId OID in ltr) { @@ -262,7 +255,7 @@ public void EJ() { if (Ent is MText mText) { - switch(mText.Text) + switch (mText.Text) { case "$$A": mText.Contents = "hahaha"; @@ -290,10 +283,10 @@ public void EJ() } }; } - + } - + }); @@ -411,31 +404,31 @@ public void TestQuickBlockDef() using (var tr = db.TransactionManager.StartTransaction()) { var ids = rss.Value.GetObjectIds(); - var bt = tr.GetObject(db.BlockTableId,OpenMode.ForRead) as BlockTable; + var bt = tr.GetObject(db.BlockTableId, OpenMode.ForRead) as BlockTable; var btr = new BlockTableRecord(); btr.Name = blockName; foreach (var item in ids) { - var ent = tr.GetObject(item,OpenMode.ForRead) as Entity; + var ent = tr.GetObject(item, OpenMode.ForRead) as Entity; btr.AppendEntity(ent.Clone() as Entity); ent.ForWrite(e => e.Erase(true)); } bt.UpgradeOpen(); bt.Add(btr); - tr.AddNewlyCreatedDBObject(btr,true); + tr.AddNewlyCreatedDBObject(btr, true); bt.DowngradeOpen(); - // tr.Commit(); - //} + // tr.Commit(); + //} - //using (var tr1 = db.TransactionManager.StartTransaction()) - //{ + //using (var tr1 = db.TransactionManager.StartTransaction()) + //{ //var bt = tr1.GetObject(db.BlockTableId, OpenMode.ForRead) as BlockTable; var btr1 = tr.GetObject(db.CurrentSpaceId, OpenMode.ForWrite) as BlockTableRecord; var br = new BlockReference(Point3d.Origin, bt[blockName]); br.ScaleFactors = default; btr1.AppendEntity(br); - tr.AddNewlyCreatedDBObject(br,true); + tr.AddNewlyCreatedDBObject(br, true); btr1.DowngradeOpen(); ed.Regen(); tr.Commit(); @@ -446,13 +439,12 @@ public void TestQuickBlockDef() public void TestWblock() { - Database db = new Database(false,true); var curdb = HostApplicationServices.WorkingDatabase; - var opts = new PromptSelectionOptions(); + PromptSelectionOptions opts = new(); opts.MessageForAdding = "选择对象"; var ss = Env.Editor.GetSelection(opts).Value; var ids = new ObjectIdCollection(ss.GetObjectIds()); - db = curdb.Wblock(ids, Point3d.Origin); + var db = curdb.Wblock(ids, Point3d.Origin); db.SaveAs(@"c:\test.dwg", DwgVersion.Current); } @@ -461,7 +453,7 @@ public void TestChangeDynameicBlock() var pro = new Dictionary(); pro.Add("haha", 1); var blockid = Env.Editor.GetEntity("选择个块").ObjectId; - using (var tr = new DBTrans()) + using (var tr = new DBTrans()) { var blockref = tr.GetObject(blockid); blockref.ChangeBlockProperty(pro); @@ -475,16 +467,24 @@ public void TestChangeDynameicBlock() [CommandMethod("TestBack")] public void TestBack() { - using var tr = new DBTrans(@"C:\Users\vic\Desktop\test.dwg"); + string dir = Environment.GetFolderPath(Environment.SpecialFolder.DesktopDirectory); + string dwg = dir + "\\test.dwg"; + if (!File.Exists(dwg)) + { + System.Windows.Forms.MessageBox.Show(dwg, "你还没有创建此文件"); + return; + } + + using var tr = new DBTrans(dwg); tr.ModelSpace.GetEntities().ForEach(ent => { ent.ForWrite(e => e.ColorIndex = 3); }); - tr.Database.SaveAs(@"C:\Users\vic\Desktop\test.dwg", DwgVersion.Current); + tr.Database.SaveAs(dwg, DwgVersion.Current); tr.ModelSpace.GetEntities().ForEach(ent => { ent.ForWrite(e => e.ColorIndex = 4); }); - tr.Database.SaveAs(@"C:\Users\vic\Desktop\test.dwg", DwgVersion.Current); + tr.Database.SaveAs(dwg, DwgVersion.Current); } -- Gitee From 51a7eb9a07c29f46c6505161c7c463421252b4b4 Mon Sep 17 00:00:00 2001 From: liuqihong <540762622@qq.com> Date: Thu, 5 May 2022 23:35:01 +0800 Subject: [PATCH 203/675] =?UTF-8?q?=E4=BF=AE=E6=94=B9=E5=A4=87=E6=B3=A8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../TextEntityAdd.cs" | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git "a/src/IFoxCAD.Cad/ExtensionMethod/\346\226\260\345\273\272\346\226\207\345\255\227/TextEntityAdd.cs" "b/src/IFoxCAD.Cad/ExtensionMethod/\346\226\260\345\273\272\346\226\207\345\255\227/TextEntityAdd.cs" index 0f06aab..5b37a31 100644 --- "a/src/IFoxCAD.Cad/ExtensionMethod/\346\226\260\345\273\272\346\226\207\345\255\227/TextEntityAdd.cs" +++ "b/src/IFoxCAD.Cad/ExtensionMethod/\346\226\260\345\273\272\346\226\207\345\255\227/TextEntityAdd.cs" @@ -11,7 +11,7 @@ public static partial class EntityAdd /// 字体高度 /// 文字样式 /// 对齐方式 - /// 对齐点,因样式 可能无效 + /// 对齐点,因样式 可能无效 /// public static Entity AddDBTextToEntity(this Database db, string textContents, -- Gitee From f4d6290361dcaa10a4d0bed96cf9e6ad1730ad4a Mon Sep 17 00:00:00 2001 From: vicwjb <403009819@qq.com> Date: Thu, 5 May 2022 23:36:05 +0800 Subject: [PATCH 204/675] =?UTF-8?q?=E4=BC=98=E5=8C=96=E4=BB=A3=E7=A0=81?= =?UTF-8?q?=EF=BC=8C=E4=BF=AE=E5=A4=8Dbug=20fixed?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- IFoxCAD.sln | 2 +- src/IFoxCAD.Cad/Runtime/DBTrans.cs | 92 ------------------- .../Runtime/{MethodHelper.cs => Utils.cs} | 89 +++++++++++++++++- 3 files changed, 87 insertions(+), 96 deletions(-) rename src/IFoxCAD.Cad/Runtime/{MethodHelper.cs => Utils.cs} (32%) diff --git a/IFoxCAD.sln b/IFoxCAD.sln index 7d79a63..a9e0664 100644 --- a/IFoxCAD.sln +++ b/IFoxCAD.sln @@ -11,7 +11,7 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "IFoxCAD.WPF", "src\IFoxCAD. EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "IFoxCAD.Basal", "src\IFoxCAD.Basal\IFoxCAD.Basal.csproj", "{40BF07C4-100A-4810-A27B-4587CFB38E6E}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "TestConsole", "tests\TestConsole\TestConsole.csproj", "{E2873F0B-CAD2-45E8-8FF0-C05C0C6AD955}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "TestConsole", "tests\TestConsole\TestConsole.csproj", "{E2873F0B-CAD2-45E8-8FF0-C05C0C6AD955}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution diff --git a/src/IFoxCAD.Cad/Runtime/DBTrans.cs b/src/IFoxCAD.Cad/Runtime/DBTrans.cs index 163da44..25dcbc8 100644 --- a/src/IFoxCAD.Cad/Runtime/DBTrans.cs +++ b/src/IFoxCAD.Cad/Runtime/DBTrans.cs @@ -56,17 +56,6 @@ public static DBTrans Top } } - /// - /// 结束栈中所有事务_AOP修复方案 - /// 此方式代表了不允许跨事务循环命令 - /// 若有则需在此命令进行 拒绝注入AOP特性 - /// - //internal static void FinishDatabase() - //{ - // while (dBTrans.Count != 0) - // dBTrans.Peek().Dispose(); - //} - /// /// 文档 /// @@ -325,88 +314,7 @@ public ObjectId GetObjectId(string handleString) return Helper.TryGetObjectId(Database, hanle); } - class Helper - { - /* - * id = db.GetObjectId(false, handle, 0); - * 参数意义: db.GetObjectId(如果没有找到就创建,句柄号,标记..将来备用) - * 在vs的输出会一直抛出: - * 引发的异常:“Autodesk.AutoCAD.Runtime.Exception”(位于 AcdbMgd.dll 中) - * "eUnknownHandle" - * 这就是为什么慢的原因,所以直接运行就好了!而Debug还是需要用arx的API替代. - */ - [System.Security.SuppressUnmanagedCodeSecurity] - [DllImport("acdb17.dll", CallingConvention = CallingConvention.ThisCall/*08的调用约定 高版本是__cdecl*/, - EntryPoint = "?getAcDbObjectId@AcDbDatabase@@QAE?AW4ErrorStatus@Acad@@AAVAcDbObjectId@@_NABVAcDbHandle@@K@Z")] - extern static int getAcDbObjectId17x32(IntPtr db, out ObjectId id, [MarshalAs(UnmanagedType.U1)] bool createnew, ref Handle h, uint reserved); - - [System.Security.SuppressUnmanagedCodeSecurity] - [DllImport("acdb17.dll", CallingConvention = CallingConvention.ThisCall/*08的调用约定 高版本是__cdecl*/, - EntryPoint = "?getAcDbObjectId@AcDbDatabase@@QEAA?AW4ErrorStatus@Acad@@AEAVAcDbObjectId@@_NAEBVAcDbHandle@@K@Z")] - extern static int getAcDbObjectId17x64(IntPtr db, out ObjectId id, [MarshalAs(UnmanagedType.U1)] bool createnew, ref Handle h, uint reserved); - - [System.Security.SuppressUnmanagedCodeSecurity] - [DllImport("acdb18.dll", CallingConvention = CallingConvention.ThisCall/*08的调用约定 高版本是__cdecl*/, - EntryPoint = "?getAcDbObjectId@AcDbDatabase@@QAE?AW4ErrorStatus@Acad@@AAVAcDbObjectId@@_NABVAcDbHandle@@K@Z")] - extern static int getAcDbObjectId18x32(IntPtr db, out ObjectId id, [MarshalAs(UnmanagedType.U1)] bool createnew, ref Handle h, uint reserved); - - [System.Security.SuppressUnmanagedCodeSecurity] - [DllImport("acdb18.dll", CallingConvention = CallingConvention.ThisCall/*08的调用约定 高版本是__cdecl*/, - EntryPoint = "?getAcDbObjectId@AcDbDatabase@@QEAA?AW4ErrorStatus@Acad@@AEAVAcDbObjectId@@_NAEBVAcDbHandle@@K@Z")] - extern static int getAcDbObjectId18x64(IntPtr db, out ObjectId id, [MarshalAs(UnmanagedType.U1)] bool createnew, ref Handle h, uint reserved); - - /// - /// 句柄转id,NET35(08~12)专用的 - /// - /// 数据库 - /// 句柄 - /// 返回的id - /// 不存在则创建 - /// 保留,用于未来 - /// 成功0,其他值都是错误.可以强转ErrorStatus - static int GetAcDbObjectId(IntPtr db, Handle handle, out ObjectId id, bool createIfNotFound = false, uint reserved = 0) - { - id = ObjectId.Null; - switch (Application.Version.Major) - { - case 17: - { - if (IntPtr.Size == 4) - return getAcDbObjectId17x32(db, out id, createIfNotFound, ref handle, reserved); - else - return getAcDbObjectId17x64(db, out id, createIfNotFound, ref handle, reserved); - } - case 18: - { - if (IntPtr.Size == 4) - return getAcDbObjectId18x32(db, out id, createIfNotFound, ref handle, reserved); - else - return getAcDbObjectId18x64(db, out id, createIfNotFound, ref handle, reserved); - } - } - return -1; - } - - /// - /// 句柄转id - /// - /// 数据库 - /// 句柄 - /// id - public static ObjectId TryGetObjectId(Database db, Handle handle) - { -#if !NET35 - //高版本直接利用 - var es = db.TryGetObjectId(handle, out ObjectId id); - //if (!es) -#else - var es = GetAcDbObjectId(db.UnmanagedObject, handle, out ObjectId id); - //if (ErrorStatus.OK != (ErrorStatus)es) -#endif - return id; - } - } #endregion diff --git a/src/IFoxCAD.Cad/Runtime/MethodHelper.cs b/src/IFoxCAD.Cad/Runtime/Utils.cs similarity index 32% rename from src/IFoxCAD.Cad/Runtime/MethodHelper.cs rename to src/IFoxCAD.Cad/Runtime/Utils.cs index 543f428..eccbaff 100644 --- a/src/IFoxCAD.Cad/Runtime/MethodHelper.cs +++ b/src/IFoxCAD.Cad/Runtime/Utils.cs @@ -1,8 +1,91 @@ namespace IFoxCAD.Cad; -public static class MethodInfoHelper +internal class Helper { - static Dictionary methodDic = new(); + /* + * id = db.GetObjectId(false, handle, 0); + * 参数意义: db.GetObjectId(如果没有找到就创建,句柄号,标记..将来备用) + * 在vs的输出会一直抛出: + * 引发的异常:“Autodesk.AutoCAD.Runtime.Exception”(位于 AcdbMgd.dll 中) + * "eUnknownHandle" + * 这就是为什么慢的原因,所以直接运行就好了!而Debug还是需要用arx的API替代. + */ + + [System.Security.SuppressUnmanagedCodeSecurity] + [DllImport("acdb17.dll", CallingConvention = CallingConvention.ThisCall/*08的调用约定 高版本是__cdecl*/, + EntryPoint = "?getAcDbObjectId@AcDbDatabase@@QAE?AW4ErrorStatus@Acad@@AAVAcDbObjectId@@_NABVAcDbHandle@@K@Z")] + extern static int getAcDbObjectId17x32(IntPtr db, out ObjectId id, [MarshalAs(UnmanagedType.U1)] bool createnew, ref Handle h, uint reserved); + + [System.Security.SuppressUnmanagedCodeSecurity] + [DllImport("acdb17.dll", CallingConvention = CallingConvention.ThisCall/*08的调用约定 高版本是__cdecl*/, + EntryPoint = "?getAcDbObjectId@AcDbDatabase@@QEAA?AW4ErrorStatus@Acad@@AEAVAcDbObjectId@@_NAEBVAcDbHandle@@K@Z")] + extern static int getAcDbObjectId17x64(IntPtr db, out ObjectId id, [MarshalAs(UnmanagedType.U1)] bool createnew, ref Handle h, uint reserved); + + [System.Security.SuppressUnmanagedCodeSecurity] + [DllImport("acdb18.dll", CallingConvention = CallingConvention.ThisCall/*08的调用约定 高版本是__cdecl*/, + EntryPoint = "?getAcDbObjectId@AcDbDatabase@@QAE?AW4ErrorStatus@Acad@@AAVAcDbObjectId@@_NABVAcDbHandle@@K@Z")] + extern static int getAcDbObjectId18x32(IntPtr db, out ObjectId id, [MarshalAs(UnmanagedType.U1)] bool createnew, ref Handle h, uint reserved); + + [System.Security.SuppressUnmanagedCodeSecurity] + [DllImport("acdb18.dll", CallingConvention = CallingConvention.ThisCall/*08的调用约定 高版本是__cdecl*/, + EntryPoint = "?getAcDbObjectId@AcDbDatabase@@QEAA?AW4ErrorStatus@Acad@@AEAVAcDbObjectId@@_NAEBVAcDbHandle@@K@Z")] + extern static int getAcDbObjectId18x64(IntPtr db, out ObjectId id, [MarshalAs(UnmanagedType.U1)] bool createnew, ref Handle h, uint reserved); + + /// + /// 句柄转id,NET35(08~12)专用的 + /// + /// 数据库 + /// 句柄 + /// 返回的id + /// 不存在则创建 + /// 保留,用于未来 + /// 成功0,其他值都是错误.可以强转ErrorStatus + static int GetAcDbObjectId(IntPtr db, Handle handle, out ObjectId id, bool createIfNotFound = false, uint reserved = 0) + { + id = ObjectId.Null; + switch (Application.Version.Major) + { + case 17: + { + if (IntPtr.Size == 4) + return getAcDbObjectId17x32(db, out id, createIfNotFound, ref handle, reserved); + else + return getAcDbObjectId17x64(db, out id, createIfNotFound, ref handle, reserved); + } + case 18: + { + if (IntPtr.Size == 4) + return getAcDbObjectId18x32(db, out id, createIfNotFound, ref handle, reserved); + else + return getAcDbObjectId18x64(db, out id, createIfNotFound, ref handle, reserved); + } + } + return -1; + } + + /// + /// 句柄转id + /// + /// 数据库 + /// 句柄 + /// id + public static ObjectId TryGetObjectId(Database db, Handle handle) + { +#if !NET35 + //高版本直接利用 + var es = db.TryGetObjectId(handle, out ObjectId id); + //if (!es) +#else + var es = GetAcDbObjectId(db.UnmanagedObject, handle, out ObjectId id); + //if (ErrorStatus.OK != (ErrorStatus)es) +#endif + return id; + } +} + +internal static class MethodInfoHelper +{ + private static readonly Dictionary methodDic = new(); /// /// 执行函数 @@ -50,4 +133,4 @@ public static class MethodInfoHelper } return result; } -} +} \ No newline at end of file -- Gitee From 53c869bf6af36243b41e225b5eedfc294eb8b460 Mon Sep 17 00:00:00 2001 From: vicwjb <403009819@qq.com> Date: Thu, 5 May 2022 23:38:53 +0800 Subject: [PATCH 205/675] =?UTF-8?q?v0.3.4=E7=89=88=E6=9C=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/IFoxCAD.Cad/IFoxCAD.Cad.csproj | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/IFoxCAD.Cad/IFoxCAD.Cad.csproj b/src/IFoxCAD.Cad/IFoxCAD.Cad.csproj index e82844a..0142860 100644 --- a/src/IFoxCAD.Cad/IFoxCAD.Cad.csproj +++ b/src/IFoxCAD.Cad/IFoxCAD.Cad.csproj @@ -6,7 +6,7 @@ enable true true - 0.3.3.3 + 0.3.4 InspireFunction xsfhlzh;vicwjb 基于.NET的Cad二次开发类库 -- Gitee From 08dc2d55e8456d4382d9c40128cbc85f75a03b43 Mon Sep 17 00:00:00 2001 From: vicwjb <403009819@qq.com> Date: Fri, 6 May 2022 21:08:59 +0800 Subject: [PATCH 206/675] =?UTF-8?q?=E5=A2=9E=E5=8A=A0=E4=BF=9D=E5=AD=98dwg?= =?UTF-8?q?=E6=96=87=E4=BB=B6=E7=9A=84=E5=87=BD=E6=95=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/IFoxCAD.Cad/Runtime/DBTrans.cs | 20 ++++++++++++++++++++ tests/Test/TestDBTrans.cs | 5 +++-- 2 files changed, 23 insertions(+), 2 deletions(-) diff --git a/src/IFoxCAD.Cad/Runtime/DBTrans.cs b/src/IFoxCAD.Cad/Runtime/DBTrans.cs index 25dcbc8..6bd6419 100644 --- a/src/IFoxCAD.Cad/Runtime/DBTrans.cs +++ b/src/IFoxCAD.Cad/Runtime/DBTrans.cs @@ -318,6 +318,26 @@ public ObjectId GetObjectId(string handleString) #endregion + public void SaveDwgFile() + { + bool flag = true; + foreach (Document doc in Application.DocumentManager) + { + // 前台开图,使用命令保存 + if (doc.Database.Filename == this.Database.Filename) + { + doc.SendStringToExecute("_qsave\n", false, true, true); //不需要切换文档 + flag = false; + break; + } + } + if (flag) + { + // 后台开图,用数据库保存 + Database.SaveAs(Database.Filename, Database.SecurityParameters); + } + } + #region idispose接口相关函数 /// /// 取消事务 diff --git a/tests/Test/TestDBTrans.cs b/tests/Test/TestDBTrans.cs index d72e932..7cda725 100644 --- a/tests/Test/TestDBTrans.cs +++ b/tests/Test/TestDBTrans.cs @@ -6,8 +6,9 @@ public void Testtr() { string filename = @"C:\Users\vic\Desktop\test.dwg"; using var tr = new DBTrans(filename); - tr.ModelSpace.AddCircle(new Point3d(0, 0, 0), 20); - tr.Database.SaveAs(filename,DwgVersion.Current); + tr.ModelSpace.AddCircle(new Point3d(10, 10, 0), 20); + //tr.Database.SaveAs(filename,DwgVersion.Current); + tr.SaveDwgFile(); } [CommandMethod("testifoxcommit")] public void Testifoxcommit() -- Gitee From 0695c97bcdcb7791dc510fcb3376cc4985aff023 Mon Sep 17 00:00:00 2001 From: vicwjb <403009819@qq.com> Date: Fri, 6 May 2022 21:58:34 +0800 Subject: [PATCH 207/675] =?UTF-8?q?=E4=BC=98=E5=8C=96SaveDwgFile=E5=87=BD?= =?UTF-8?q?=E6=95=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/IFoxCAD.Cad/Runtime/DBTrans.cs | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/src/IFoxCAD.Cad/Runtime/DBTrans.cs b/src/IFoxCAD.Cad/Runtime/DBTrans.cs index 6bd6419..6a5d07d 100644 --- a/src/IFoxCAD.Cad/Runtime/DBTrans.cs +++ b/src/IFoxCAD.Cad/Runtime/DBTrans.cs @@ -318,7 +318,12 @@ public ObjectId GetObjectId(string handleString) #endregion - public void SaveDwgFile() + #region 保存文件 + /// + /// 保存当前数据库的dwg文件,如果前台打开则按dwg默认版本保存,否则按version参数的版本保存 + /// + /// dwg版本,默认为2004 + public void SaveDwgFile(DwgVersion version = DwgVersion.AC1800) { bool flag = true; foreach (Document doc in Application.DocumentManager) @@ -334,9 +339,11 @@ public void SaveDwgFile() if (flag) { // 后台开图,用数据库保存 - Database.SaveAs(Database.Filename, Database.SecurityParameters); + Database.SaveAs(Database.Filename, version); } } + #endregion + #region idispose接口相关函数 /// -- Gitee From 167b3a300d6fe1754c4bb42844e08e6dba7cefef Mon Sep 17 00:00:00 2001 From: vicwjb <403009819@qq.com> Date: Fri, 6 May 2022 23:34:44 +0800 Subject: [PATCH 208/675] =?UTF-8?q?=E7=BB=A7=E7=BB=AD=E4=BC=98=E5=8C=96Sav?= =?UTF-8?q?eDwgFile=E5=87=BD=E6=95=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/IFoxCAD.Cad/Runtime/Utils.cs | 18 ++++++++++++++++-- tests/Test/testenv.cs | 10 ++++++++++ 2 files changed, 26 insertions(+), 2 deletions(-) diff --git a/src/IFoxCAD.Cad/Runtime/Utils.cs b/src/IFoxCAD.Cad/Runtime/Utils.cs index eccbaff..8693fc1 100644 --- a/src/IFoxCAD.Cad/Runtime/Utils.cs +++ b/src/IFoxCAD.Cad/Runtime/Utils.cs @@ -1,6 +1,8 @@ -namespace IFoxCAD.Cad; +using System; -internal class Helper +namespace IFoxCAD.Cad; + +public class Helper { /* * id = db.GetObjectId(false, handle, 0); @@ -81,6 +83,18 @@ public static ObjectId TryGetObjectId(Database db, Handle handle) #endif return id; } + + //public static int GetCadFileVersion(string filename) + //{ + // var bytes = File.ReadAllBytes(filename); + // var headstr = Encoding.Default.GetString(bytes)[0..6]; + // if (!headstr.StartsWith("AC")) return 0; + // var vernum = int.Parse(headstr.Replace("AC", "")); + // var a = Enum.Parse(typeof(DwgVersion), "AC1800"); + // Enum.TryParse() + // return vernum + 986; + + //} } internal static class MethodInfoHelper diff --git a/tests/Test/testenv.cs b/tests/Test/testenv.cs index 56d92ab..3c98d91 100644 --- a/tests/Test/testenv.cs +++ b/tests/Test/testenv.cs @@ -75,4 +75,14 @@ public void TestGetVar() a.Print(); Env.SetVar("dbmod1", 1); } + + [CommandMethod("TestDwgVersion")] + public void TestDwgVersion() + { + // + string filename = @"C:\Users\vic\Desktop\test.dwg"; + //var a = Helper.GetCadFileVersion(filename); + //a.Print(); + //((DwgVersion)a).Print(); + } } -- Gitee From 1048b53eea4c10913b41a35ef3f28ef63225e076 Mon Sep 17 00:00:00 2001 From: vicwjb <403009819@qq.com> Date: Sun, 8 May 2022 15:47:38 +0000 Subject: [PATCH 209/675] =?UTF-8?q?=E6=9B=B4=E6=96=B0=E5=B8=AE=E5=8A=A9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/README.md b/README.md index 76d60e7..0c60b92 100644 --- a/README.md +++ b/README.md @@ -18,6 +18,10 @@ 由于vs2022抛弃了某几个net版本,所以我们同时安装vs2019和vs2022,然后使用vs2022; 其中的原因是vs2019拥有全部net版本,而vs2022拥有最新的分析器和语法. +#### IFoxCad 项目模版 + +可以在vs扩展菜单-管理扩展中搜索ifoxcad,即可安装项目模板。使用项目模版可以方便的创建支持多目标多版本的使用ifoxcad类库的项目和类。 + #### 安装教程 1. 新建net standard 类库 -- Gitee From da098d5e63947706b27ed472814c73151bf2a4a8 Mon Sep 17 00:00:00 2001 From: liuqihong <540762622@qq.com> Date: Mon, 9 May 2022 12:11:28 +0000 Subject: [PATCH 210/675] =?UTF-8?q?=E9=94=99=E5=88=AB=E5=AD=97=E6=9B=B4?= =?UTF-8?q?=E6=94=B9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 0c60b92..709132b 100644 --- a/README.md +++ b/README.md @@ -32,7 +32,7 @@ 1. 快速入门 - - 打开vs,新建一个standard类型的类库项目,**注意,需要选择类型的时候一定要选standord2.0** ![](./docs/png/standard.png) + - 打开vs,新建一个standard类型的类库项目,**注意,需要选择类型的时候一定要选standard2.0** ![](./docs/png/standard.png) - 双击项目,打开项目文件: -- Gitee From 14fb910bccd78c1f28685c664e89e616d313af06 Mon Sep 17 00:00:00 2001 From: vicwjb <403009819@qq.com> Date: Tue, 10 May 2022 00:02:39 +0800 Subject: [PATCH 211/675] =?UTF-8?q?=E4=BF=AE=E5=A4=8DDimblk=E5=B1=9E?= =?UTF-8?q?=E6=80=A7=E9=94=99=E8=AF=AF=20fixed?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/IFoxCAD.Cad/IFoxCAD.Cad.csproj | 7 +- src/IFoxCAD.Cad/Runtime/Env.cs | 93 ++++++++++++++++++++------- tests/Test/testenv.cs | 1 + tests/TestConsole/Class1.cs | 96 ++++++++++++++++++++++++++++ tests/TestConsole/Program.cs | 53 +++++---------- tests/TestConsole/TestConsole.csproj | 4 ++ 6 files changed, 190 insertions(+), 64 deletions(-) create mode 100644 tests/TestConsole/Class1.cs diff --git a/src/IFoxCAD.Cad/IFoxCAD.Cad.csproj b/src/IFoxCAD.Cad/IFoxCAD.Cad.csproj index 0142860..439344e 100644 --- a/src/IFoxCAD.Cad/IFoxCAD.Cad.csproj +++ b/src/IFoxCAD.Cad/IFoxCAD.Cad.csproj @@ -6,7 +6,7 @@ enable true true - 0.3.4 + 0.3.4.1 InspireFunction xsfhlzh;vicwjb 基于.NET的Cad二次开发类库 @@ -71,11 +71,6 @@ - - - - - diff --git a/src/IFoxCAD.Cad/Runtime/Env.cs b/src/IFoxCAD.Cad/Runtime/Env.cs index a7d28ed..e48edd3 100644 --- a/src/IFoxCAD.Cad/Runtime/Env.cs +++ b/src/IFoxCAD.Cad/Runtime/Env.cs @@ -193,9 +193,56 @@ public enum DimblkType /// /// 建筑标记 /// - ArchTick, + ArchTick } + private static readonly Dictionary dimdescdict = new() + { + { "实心闭合", DimblkType.Defult }, + { "点", DimblkType.Dot }, + { "小点", DimblkType.DotSmall }, + { "空心点", DimblkType.DotBlank }, + { "原点标记", DimblkType.Origin }, + { "原点标记 2", DimblkType.Origin2 }, + { "打开", DimblkType.Open }, + { "直角", DimblkType.Open90 }, + { "30 度角", DimblkType.Open30 }, + { "闭合", DimblkType.Closed }, + { "空心小点", DimblkType.Small }, + { "无", DimblkType.None }, + { "倾斜", DimblkType.Oblique }, + { "实心框", DimblkType.BoxFilled }, + { "方框", DimblkType.BoxBlank }, + { "空心闭合", DimblkType.ClosedBlank }, + { "实心基准三角形", DimblkType.DatumFilled }, + { "基准三角形", DimblkType.DatumBlank }, + { "完整标记", DimblkType.Integral }, + { "建筑标记", DimblkType.ArchTick }, + + { "", DimblkType.Defult }, + { "_DOT", DimblkType.Dot }, + { "_DOTSMALL", DimblkType.DotSmall }, + { "_DOTBLANK", DimblkType.DotBlank }, + { "_ORIGIN", DimblkType.Origin }, + { "_ORIGIN2", DimblkType.Origin2 }, + { "_OPEN", DimblkType.Open }, + { "_OPEN90", DimblkType.Open90 }, + { "_OPEN30", DimblkType.Open30 }, + { "_CLOSED", DimblkType.Closed }, + { "_SMALL", DimblkType.Small }, + { "_NONE", DimblkType.None }, + { "_OBLIQUE", DimblkType.Oblique }, + { "_BOXFILLED", DimblkType.BoxFilled }, + { "_BOXBLANK", DimblkType.BoxBlank }, + { "_CLOSEDBLANK", DimblkType.ClosedBlank }, + { "_DATUMFILLED", DimblkType.DatumFilled }, + { "_DATUMBLANK", DimblkType.DatumBlank }, + { "_INTEGRAL", DimblkType.Integral }, + { "_ARCHTICK", DimblkType.ArchTick }, + }; + + + /// /// 标注箭头属性 /// @@ -203,15 +250,21 @@ public static DimblkType Dimblk { get { - string s = (string)Application.GetSystemVariable("dimblk"); - if (string.IsNullOrEmpty(s)) - { - return DimblkType.Defult; - } - else - { - return s.ToEnum(); - } + string s = ((string)Application.GetSystemVariable("dimblk")).ToUpper(); + //if (string.IsNullOrEmpty(s)) + //{ + // return DimblkType.Defult; + //} + //else + //{ + // if (dimdescdict.TryGetValue(s, out DimblkType value)) + // { + // return value; + // } + // return s.ToEnum(); + // //return s.FromDescName(); + //} + return dimdescdict[s]; } set { @@ -360,25 +413,21 @@ public static bool Include(this OSModeType osm1, OSModeType osm2) } #endregion OsMode - private static T ToEnum(this string value) - { - return (T)Enum.Parse(typeof(T), value, true); - } private static string GetName(this T value) { return Enum.GetName(typeof(T), value); } - #endregion Enum +#endregion Enum - #region 环境变量 - /// - /// 获取cad变量 - /// - /// 变量名 - /// 变量值 - public static object GetVar(string varName) +#region 环境变量 +/// +/// 获取cad变量 +/// +/// 变量名 +/// 变量值 +public static object GetVar(string varName) { return Application.GetSystemVariable(varName); } diff --git a/tests/Test/testenv.cs b/tests/Test/testenv.cs index 3c98d91..3228b9e 100644 --- a/tests/Test/testenv.cs +++ b/tests/Test/testenv.cs @@ -23,6 +23,7 @@ public void testdimblk() Env.Dimblk = Env.DimblkType.Dot; Env.Dimblk = Env.DimblkType.Defult; + Env.Dimblk = Env.DimblkType.Oblique; } [CommandMethod("testdimblk1")] diff --git a/tests/TestConsole/Class1.cs b/tests/TestConsole/Class1.cs new file mode 100644 index 0000000..c7ba1be --- /dev/null +++ b/tests/TestConsole/Class1.cs @@ -0,0 +1,96 @@ +using System; +using System.Collections.Generic; +using System.ComponentModel; +using System.ComponentModel.DataAnnotations; +using System.Linq; +using System.Reflection; +using System.Text; +using System.Threading.Tasks; +using System.Xml.Linq; + +namespace TestConsole +{ + public static class Program + { + + + public enum Colour + { + [Description("Colour Red")] + Red = 0, + + [Description("Colour Green")] + Green = 1, + + [Description("Colour Blue")] + Blue = 2, + + + Yellow = 3 + } + } + + public static class EnumExtensions + { + + // This extension method is broken out so you can use a similar pattern with + // other MetaData elements in the future. This is your base method for each. + public static T GetAttribute(this Enum value) where T : Attribute + { + //var type = value.GetType(); + //var memberInfo = type.GetMember(value.ToString()); + //var attributes = memberInfo[0].GetCustomAttributes(typeof(T), false); + + //// check if no attributes have been specified. + //if (((Array)attributes).Length > 0) + //{ + // return (T)attributes[0]; + //} + //else + //{ + // return null; + //} + return value.GetType().GetMember(value.ToString()).First().GetCustomAttribute(); + } + + // This method creates a specific call to the above method, requesting the + // Description MetaData attribute. + public static string ToName(this Enum value) + { + var attribute = value.GetAttribute(); + return attribute == null ? value.ToString() : attribute.Description; + } + + /// + /// Find the enum from the description attribute. + /// + /// + /// + /// + public static T FromName(this string desc) where T : struct + { + string attr; + Boolean found = false; + T result = (T)Enum.GetValues(typeof(T)).GetValue(0); + + foreach (object enumVal in Enum.GetValues(typeof(T))) + { + attr = ((Enum)enumVal).ToName(); + + if (attr == desc) + { + result = (T)enumVal; + found = true; + break; + } + } + + if (!found) + { + throw new Exception(); + } + + return result; + } + } +} diff --git a/tests/TestConsole/Program.cs b/tests/TestConsole/Program.cs index bc65e5c..fbef5ec 100644 --- a/tests/TestConsole/Program.cs +++ b/tests/TestConsole/Program.cs @@ -1,49 +1,30 @@ // See https://aka.ms/new-console-template for more information using System; using System.Linq; -using IFoxCAD.Basal; +using System.ComponentModel; +//using IFoxCAD.Basal; +using static TestConsole.Program; +using TestConsole; -Console.WriteLine("Hello, World!"); -var loop = new LoopList - { - 0, - 1, - 2, - 3, - 4, - 5 - }; -loop.SetFirst(loop.Last!); -Console.WriteLine(loop); -Console.WriteLine(loop.Min()); -//loop.SetFirst(new LoopListNode(loop.Min(), loop)); +// display the description attribute from the enum +foreach (Colour type in (Colour[])Enum.GetValues(typeof(Colour))) +{ + Console.WriteLine(EnumExtensions.ToName(type)); +} -//Console.WriteLine(loop); +var de = Colour.Yellow.GetAttribute(); +Console.WriteLine(de); +// Get the array from the description +string xStr = "Yellow"; +Colour thisColour = EnumExtensions.FromName(xStr); +string xStr1 = "Colour Green"; +Colour thisColour1 = EnumExtensions.FromName(xStr1); -var linkset = new LinkedHashSet(); -linkset.Add(0); -linkset.Add(1); -linkset.Add(2); -linkset.Add(3); -linkset.Add(4); -linkset.Add(5); -linkset.SetFirst(linkset.Last!); - -Console.WriteLine(linkset); - -linkset.SetFirst(linkset.MinNode!); - -Console.WriteLine(linkset); - -linkset.For(linkset.First!, (i, next, pre) => { - Console.WriteLine($"{i} - ({next},{pre})"); - -}); -Console.ReadKey(); \ No newline at end of file +Console.ReadLine(); diff --git a/tests/TestConsole/TestConsole.csproj b/tests/TestConsole/TestConsole.csproj index 0a95101..115867a 100644 --- a/tests/TestConsole/TestConsole.csproj +++ b/tests/TestConsole/TestConsole.csproj @@ -11,4 +11,8 @@ + + + + -- Gitee From ef6cbe3c810005a8d86577a0100073e0c82d9f33 Mon Sep 17 00:00:00 2001 From: vicwjb <403009819@qq.com> Date: Tue, 10 May 2022 00:09:04 +0800 Subject: [PATCH 212/675] =?UTF-8?q?Dimblk=20bug=E4=BF=AE=E5=A4=8D=20fixed?= =?UTF-8?q?=20#I56L5I?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- tests/TestConsole/Class1.cs | 96 +--------------------------- tests/TestConsole/Program.cs | 14 ---- tests/TestConsole/TestConsole.csproj | 4 +- 3 files changed, 3 insertions(+), 111 deletions(-) diff --git a/tests/TestConsole/Class1.cs b/tests/TestConsole/Class1.cs index c7ba1be..505dce9 100644 --- a/tests/TestConsole/Class1.cs +++ b/tests/TestConsole/Class1.cs @@ -1,96 +1,4 @@ -using System; -using System.Collections.Generic; -using System.ComponentModel; -using System.ComponentModel.DataAnnotations; -using System.Linq; -using System.Reflection; -using System.Text; -using System.Threading.Tasks; -using System.Xml.Linq; + -namespace TestConsole -{ - public static class Program - { - +namespace TestConsole; - public enum Colour - { - [Description("Colour Red")] - Red = 0, - - [Description("Colour Green")] - Green = 1, - - [Description("Colour Blue")] - Blue = 2, - - - Yellow = 3 - } - } - - public static class EnumExtensions - { - - // This extension method is broken out so you can use a similar pattern with - // other MetaData elements in the future. This is your base method for each. - public static T GetAttribute(this Enum value) where T : Attribute - { - //var type = value.GetType(); - //var memberInfo = type.GetMember(value.ToString()); - //var attributes = memberInfo[0].GetCustomAttributes(typeof(T), false); - - //// check if no attributes have been specified. - //if (((Array)attributes).Length > 0) - //{ - // return (T)attributes[0]; - //} - //else - //{ - // return null; - //} - return value.GetType().GetMember(value.ToString()).First().GetCustomAttribute(); - } - - // This method creates a specific call to the above method, requesting the - // Description MetaData attribute. - public static string ToName(this Enum value) - { - var attribute = value.GetAttribute(); - return attribute == null ? value.ToString() : attribute.Description; - } - - /// - /// Find the enum from the description attribute. - /// - /// - /// - /// - public static T FromName(this string desc) where T : struct - { - string attr; - Boolean found = false; - T result = (T)Enum.GetValues(typeof(T)).GetValue(0); - - foreach (object enumVal in Enum.GetValues(typeof(T))) - { - attr = ((Enum)enumVal).ToName(); - - if (attr == desc) - { - result = (T)enumVal; - found = true; - break; - } - } - - if (!found) - { - throw new Exception(); - } - - return result; - } - } -} diff --git a/tests/TestConsole/Program.cs b/tests/TestConsole/Program.cs index fbef5ec..0fcac6d 100644 --- a/tests/TestConsole/Program.cs +++ b/tests/TestConsole/Program.cs @@ -9,21 +9,7 @@ -// display the description attribute from the enum -foreach (Colour type in (Colour[])Enum.GetValues(typeof(Colour))) -{ - Console.WriteLine(EnumExtensions.ToName(type)); -} -var de = Colour.Yellow.GetAttribute(); -Console.WriteLine(de); - -// Get the array from the description -string xStr = "Yellow"; -Colour thisColour = EnumExtensions.FromName(xStr); - -string xStr1 = "Colour Green"; -Colour thisColour1 = EnumExtensions.FromName(xStr1); diff --git a/tests/TestConsole/TestConsole.csproj b/tests/TestConsole/TestConsole.csproj index 115867a..8590312 100644 --- a/tests/TestConsole/TestConsole.csproj +++ b/tests/TestConsole/TestConsole.csproj @@ -11,8 +11,6 @@ - - - + -- Gitee From 393e4f332a3f3757b936d39db649c87bee61f762 Mon Sep 17 00:00:00 2001 From: vicwjb <403009819@qq.com> Date: Tue, 10 May 2022 19:54:27 +0800 Subject: [PATCH 213/675] =?UTF-8?q?=E4=BF=AE=E6=94=B9=E5=BF=BD=E7=95=A5?= =?UTF-8?q?=E6=96=87=E4=BB=B6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .gitignore | 5 ++++- tests/TestConsole/Program.cs | 2 +- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/.gitignore b/.gitignore index e71f827..5128a84 100644 --- a/.gitignore +++ b/.gitignore @@ -404,4 +404,7 @@ healthchecksdb #vscode .ionide -.vscode \ No newline at end of file +.vscode + +#ifoxcad +tests/TestConsole/ \ No newline at end of file diff --git a/tests/TestConsole/Program.cs b/tests/TestConsole/Program.cs index 0fcac6d..b59da43 100644 --- a/tests/TestConsole/Program.cs +++ b/tests/TestConsole/Program.cs @@ -3,7 +3,7 @@ using System.Linq; using System.ComponentModel; //using IFoxCAD.Basal; -using static TestConsole.Program; +//using static TestConsole.Program; using TestConsole; -- Gitee From a4c4012256ec5ceb43a3cce0b65730fc5929e599 Mon Sep 17 00:00:00 2001 From: vicwjb <403009819@qq.com> Date: Tue, 10 May 2022 21:23:19 +0800 Subject: [PATCH 214/675] =?UTF-8?q?=E4=BC=98=E5=8C=96autoregassem?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/IFoxCAD.Cad/Runtime/AutoRegAssem.cs | 6 +- src/IFoxCAD.Cad/Runtime/Env.cs | 14 ++--- src/IFoxCAD.Cad/Runtime/IAutoGo.cs | 75 +++++++++++++------------ tests/Test/CmdINI.cs | 21 +++++-- tests/Test/Test.cs | 8 +-- tests/Test/testenv.cs | 4 +- 6 files changed, 70 insertions(+), 58 deletions(-) diff --git a/src/IFoxCAD.Cad/Runtime/AutoRegAssem.cs b/src/IFoxCAD.Cad/Runtime/AutoRegAssem.cs index 879bac0..a6976b7 100644 --- a/src/IFoxCAD.Cad/Runtime/AutoRegAssem.cs +++ b/src/IFoxCAD.Cad/Runtime/AutoRegAssem.cs @@ -28,7 +28,7 @@ public enum AssemLoadType /// public abstract class AutoRegAssem : IExtensionApplication { - AutoClass ac; + private readonly AutoClass ac; AssemInfo Info; /// @@ -120,8 +120,8 @@ void RegApp() } //这里的是不会自动执行的 - public virtual void Initialize() { } - public virtual void Terminate() { } + public void Initialize() { } + public void Terminate() { } ~AutoRegAssem() { diff --git a/src/IFoxCAD.Cad/Runtime/Env.cs b/src/IFoxCAD.Cad/Runtime/Env.cs index e48edd3..73438b0 100644 --- a/src/IFoxCAD.Cad/Runtime/Env.cs +++ b/src/IFoxCAD.Cad/Runtime/Env.cs @@ -421,13 +421,13 @@ private static string GetName(this T value) #endregion Enum -#region 环境变量 -/// -/// 获取cad变量 -/// -/// 变量名 -/// 变量值 -public static object GetVar(string varName) + #region 环境变量 + /// + /// 获取cad变量 + /// + /// 变量名 + /// 变量值 + public static object GetVar(string varName) { return Application.GetSystemVariable(varName); } diff --git a/src/IFoxCAD.Cad/Runtime/IAutoGo.cs b/src/IFoxCAD.Cad/Runtime/IAutoGo.cs index 9dbc7c4..97d3503 100644 --- a/src/IFoxCAD.Cad/Runtime/IAutoGo.cs +++ b/src/IFoxCAD.Cad/Runtime/IAutoGo.cs @@ -27,6 +27,7 @@ public interface IFoxAutoGo /// /// 加载时自动执行特性 /// +[AttributeUsage(AttributeTargets.Method)] public class IFoxInitialize : Attribute { /// @@ -54,7 +55,8 @@ public IFoxInitialize(Sequence sequence = Sequence.Last, bool isInitialize = tru class RunClass { public Sequence Sequence { get; } - MethodInfo _methodInfo; + + readonly MethodInfo _methodInfo; public RunClass(MethodInfo method, Sequence sequence) { @@ -87,7 +89,7 @@ public class AutoClass //: IExtensionApplication static List _InitializeList = new(); //储存方法用于初始化 static List _TerminateList = new(); //储存方法用于结束释放 - string _DllName; + readonly string _DllName; /// /// 反射此特性:进行加载时自动运行 /// @@ -106,7 +108,7 @@ public void Initialize() GetInterfaceFunctions(_InitializeList, nameof(Initialize)); //按照 SequenceId 排序_升序 _InitializeList = _InitializeList.OrderBy(runClass => runClass.Sequence).ToList(); - RunFunctions(_InitializeList); + AutoClass.RunFunctions(_InitializeList); _InitializeList.Clear(); } catch (System.Exception) @@ -123,7 +125,7 @@ public void Terminate() GetInterfaceFunctions(_TerminateList, nameof(Terminate)); //按照 SequenceId 排序_降序 _TerminateList = _TerminateList.OrderByDescending(runClass => runClass.Sequence).ToList(); - RunFunctions(_TerminateList); + AutoClass.RunFunctions(_TerminateList); _TerminateList.Clear(); } catch (System.Exception) @@ -204,31 +206,32 @@ public static void AppDomainGetTypes(Action action, string? dllNameWithout void GetInterfaceFunctions(List runClassList, string methodName) { const string sqid = nameof(Sequence) + "Id"; + AppDomainGetTypes(type => { if (type.IsAbstract) + { return; - //获取接口集合 - var inters = type.GetInterfaces(); - for (int ii = 0; ii < inters.Length; ii++) + } + foreach (var inters in type.GetInterfaces()) { - //找到接口名称:是否找nameof(IExtensionApplication),避免重复执行,不找 - if (inters[ii].Name == nameof(IFoxAutoGo)) + if (inters.Name == nameof(IFoxAutoGo)) { Sequence? sequence = null; MethodInfo? initialize = null; - //获得它的成员函数 - var mets = type.GetMethods(); - for (int jj = 0; jj < mets.Length; jj++) + foreach (var method in type.GetMethods()) { - var method = mets[jj]; if (method.IsAbstract) + { continue; + } if (method.Name == sqid) { var obj = method.Invoke(); - if (obj != null) + if (obj is not null) + { sequence = (Sequence)obj; + } continue; } else if (method.Name == methodName) @@ -236,21 +239,22 @@ void GetInterfaceFunctions(List runClassList, string methodName) initialize = method; } if (initialize is not null && sequence is not null) + { break; + } } if (initialize is not null) { - RunClass runc; - if (sequence is not null) - runc = new RunClass(initialize, sequence.Value); - else - runc = new RunClass(initialize, Sequence.Last); + var runc = sequence is not null ? + new RunClass(initialize, sequence.Value) : + new RunClass(initialize, Sequence.Last); runClassList.Add(runc); } break; } } - },_DllName); + }, _DllName); + } /// @@ -258,36 +262,35 @@ void GetInterfaceFunctions(List runClassList, string methodName) /// void GetAttributeFunctions() { + AppDomainGetTypes(type => { if (type.IsAbstract) + { return; - var mets = type.GetMethods();//获得它的成员函数 - for (int ii = 0; ii < mets.Length; ii++) + } + foreach (var method in type.GetMethods()) { - var method = mets[ii]; - - //找到特性,特性下面的方法要是Public,否则就被编译器优化掉了. - var attr = method.GetCustomAttributes(true); - for (int jj = 0; jj < attr.Length; jj++) + var attr = method.GetCustomAttributes(true).FirstOrDefault(); + if (attr is not null) { - if (attr[jj] is IFoxInitialize jjAtt) + var runs = new RunClass(method, attr.SequenceId); + if (attr.IsInitialize) { - var runc = new RunClass(method, jjAtt.SequenceId); - if (jjAtt.IsInitialize) - _InitializeList.Add(runc); - else - _TerminateList.Add(runc); - break;//特性只会出现一次 + _InitializeList.Add(runs); + } + else + { + _TerminateList.Add(runs); } } } - }, _DllName); + },_DllName); } /// /// 执行收集到的函数 /// - void RunFunctions(List runClassList) + static void RunFunctions(List runClassList) { for (int i = 0; i < runClassList.Count; i++) runClassList[i].Run(); diff --git a/tests/Test/CmdINI.cs b/tests/Test/CmdINI.cs index 3991e6a..009690f 100644 --- a/tests/Test/CmdINI.cs +++ b/tests/Test/CmdINI.cs @@ -4,13 +4,13 @@ */ public class CmdINI : AutoRegAssem { - public override void Initialize() - { - } + //public override void Initialize() + //{ + //} - public override void Terminate() - { - } + //public override void Terminate() + //{ + //} } @@ -28,6 +28,15 @@ public void NameCasual() ed.WriteMessage("\n 开始自动执行 可以分开多个类和多个函数 \r\n"); } + [IFoxInitialize] + public void NameCasualtest() + { + var dm = Application.DocumentManager; + var doc = dm.MdiActiveDocument; + var ed = doc.Editor; + ed.WriteMessage("\n 开始自动执行 又一次测试 \r\n"); + } + [IFoxInitialize] public void Initialize() { diff --git a/tests/Test/Test.cs b/tests/Test/Test.cs index 11f0326..4fc00a7 100644 --- a/tests/Test/Test.cs +++ b/tests/Test/Test.cs @@ -46,7 +46,7 @@ public void Addent() } [CommandMethod("drawarc")] - public void drawarc() + public void Drawarc() { using var tr = new DBTrans(); Arc arc1 = EntityEx.CreateArcSCE(new Point3d(2, 0, 0), new Point3d(0, 0, 0), new Point3d(0, 2, 0));//起点,圆心,终点 @@ -57,7 +57,7 @@ public void drawarc() } [CommandMethod("drawcircle")] - public void draCircle() + public void DraCircle() { using var tr = new DBTrans(); Circle circle1 = EntityEx.CreateCircle(new Point3d(0, 0, 0), new Point3d(1, 0, 0)); //起点,终点 @@ -147,7 +147,7 @@ public void AddLine1() Line line1 = new(new Point3d(0, 0, 0), new Point3d(1, 1, 0)); Line line2 = new(new Point3d(0, 0, 0), new Point3d(1, 1, 0)); Line line3 = new(new Point3d(1, 1, 0), new Point3d(3, 3, 0)); - Circle circle = new Circle(new Point3d(0, 0, 0), Vector3d.ZAxis, 10); + Circle circle = new(new Point3d(0, 0, 0), Vector3d.ZAxis, 10); tr.CurrentSpace.AddEntity(line1); tr.CurrentSpace.AddEntity(line2, line3, circle); } @@ -157,7 +157,7 @@ public void AddLine1() public void AddPolyline1() { using var tr = new DBTrans(); - Polyline pl = new Polyline(); + Polyline pl = new(); pl.AddVertexAt(0, new Point2d(0, 0), 0, 0, 0); pl.AddVertexAt(1, new Point2d(10, 10), 0, 0, 0); pl.AddVertexAt(2, new Point2d(20, 20), 0, 0, 0); diff --git a/tests/Test/testenv.cs b/tests/Test/testenv.cs index 3228b9e..05ffc5c 100644 --- a/tests/Test/testenv.cs +++ b/tests/Test/testenv.cs @@ -18,7 +18,7 @@ public void Testenum1() } [CommandMethod("testdimblk")] - public void testdimblk() + public void Testdimblk() { Env.Dimblk = Env.DimblkType.Dot; @@ -81,7 +81,7 @@ public void TestGetVar() public void TestDwgVersion() { // - string filename = @"C:\Users\vic\Desktop\test.dwg"; + //string filename = @"C:\Users\vic\Desktop\test.dwg"; //var a = Helper.GetCadFileVersion(filename); //a.Print(); //((DwgVersion)a).Print(); -- Gitee From c7104a124cd54b7a4e8d434f0fc98f6a3a9bfa82 Mon Sep 17 00:00:00 2001 From: vicwjb <403009819@qq.com> Date: Tue, 10 May 2022 21:34:20 +0800 Subject: [PATCH 215/675] =?UTF-8?q?=E6=9B=B4=E6=96=B0=E5=B8=AE=E5=8A=A9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 26 ++++++++++++++++++-------- 1 file changed, 18 insertions(+), 8 deletions(-) diff --git a/README.md b/README.md index 709132b..6a4b012 100644 --- a/README.md +++ b/README.md @@ -20,7 +20,7 @@ #### IFoxCad 项目模版 -可以在vs扩展菜单-管理扩展中搜索ifoxcad,即可安装项目模板。使用项目模版可以方便的创建支持多目标多版本的使用ifoxcad类库的项目和类。 +可以在vs扩展菜单-管理扩展中搜索ifoxcad,即可安装项目模板。使用项目模版可以方便的创建支持多目标多版本的使用ifoxcad类库的项目和类。如果无法在vs的市场里下载,就去上面的QQ群里下载。 #### 安装教程 @@ -115,13 +115,17 @@ */ public class CmdINI : AutoRegAssem { - public override void Initialize() - { - } - - public override void Terminate() - { + // 这里可以写任何普通的函数,也可以写下面 AutoTest 类里的实现了 IFoxInitialize 特性的初始化函数 + // 继承AutoRegAssem的主要作用是写注册表用来自动加载dll,同时执行实现了 IFoxInitialize 特性的函数 + // 注意这里的自动执行是在cad启动后,加载了dll之后执行,而不是运行命令后执行。 + + [IFoxInitialize] + public void InitOne() + { + //TODO 你想在加载dll之后自动执行的函数 + // 可以随便在哪里类里 可以多次实现 IFoxInitialize 特性 } + } //其他的类中的函数: @@ -133,7 +137,13 @@ { //TODO 你想在加载dll之后自动执行的函数 } - [IFoxInitialize(isInitialize: false)] + [IFoxInitialize] + public void InitTwo() + { + //TODO 你想在加载dll之后自动执行的函数 + // 可以随便在哪里类里 可以多次实现 IFoxInitialize 特性 + } + [IFoxInitialize(isInitialize: false)] // 特性的参数为false的时候就表示卸载时执行的函数 public void Terminate() { //TODO 你想在关闭cad时自动执行的函数 -- Gitee From 7369f614c505484783ebcd4d8899ec7b0389be33 Mon Sep 17 00:00:00 2001 From: vicwjb <403009819@qq.com> Date: Thu, 12 May 2022 13:40:25 +0000 Subject: [PATCH 216/675] =?UTF-8?q?update=20docs/=E5=85=B3=E4=BA=8EIFoxCAD?= =?UTF-8?q?=E7=9A=84=E6=9E=B6=E6=9E=84=E8=AF=B4=E6=98=8E.md.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- ...32\204\346\236\266\346\236\204\350\257\264\346\230\216.md" | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git "a/docs/\345\205\263\344\272\216IFoxCAD\347\232\204\346\236\266\346\236\204\350\257\264\346\230\216.md" "b/docs/\345\205\263\344\272\216IFoxCAD\347\232\204\346\236\266\346\236\204\350\257\264\346\230\216.md" index 68ac9f5..69a9b79 100644 --- "a/docs/\345\205\263\344\272\216IFoxCAD\347\232\204\346\236\266\346\236\204\350\257\264\346\230\216.md" +++ "b/docs/\345\205\263\344\272\216IFoxCAD\347\232\204\346\236\266\346\236\204\350\257\264\346\230\216.md" @@ -45,7 +45,7 @@ IFoxCAD是基于NFOX类库的重制版,主要是提供一个最小化的内核 ### 2.2 关于DBTrans类的具体构成元素的意义 -DBTrans类里基本的封装就是Transaction,然后是Document、Database、Editor、符号表、命名字典等,而抓这些其实都是cad二次开发关于图元操作经常打交道的概念。 +DBTrans类里基本的封装就是Transaction,然后是Document、Database、Editor、符号表、命名字典等,而这些其实都是cad二次开发关于图元操作经常打交道的概念。 DBTrans的每个实例都具有这些属性,而这些属性就对应于cad的相关类库,通过这些属性就可以对数据进行相应的操作。特别是符号表中最常用的就是块表,通过对块表的操作来实现添加图元等。 @@ -109,7 +109,7 @@ DBTrans的每个实例都具有这些属性,而这些属性就对应于cad的 - Has --- 判断符号表是否有符号表记录的函数 - 。。。 -特殊说明:当符号表为块表时,上述函数实际操作的是块定义、属性定义等。所以为了添加图元,需要特殊写法。 +特殊说明:当符号表为块表时,上述函数实际操作的是块定义、属性定义等。所以为了添加图元,需要特殊写法,原因在于cad的实体都是存在符号表记录里的,通常为模型这个块表记录。 # 慢慢完善,想到哪写到哪。。。 -- Gitee From 3d06d43e30f31e75d3b758db2b01b4689ba29fe1 Mon Sep 17 00:00:00 2001 From: vicwjb <403009819@qq.com> Date: Tue, 17 May 2022 22:27:27 +0800 Subject: [PATCH 217/675] =?UTF-8?q?dbtrans=E7=9A=84=E6=89=93=E5=BC=80?= =?UTF-8?q?=E6=96=87=E4=BB=B6=E6=9E=84=E9=80=A0=E5=87=BD=E6=95=B0=E5=A2=9E?= =?UTF-8?q?=E5=8A=A0=20FileOpenMode=20=E9=BB=98=E8=AE=A4=E5=8F=82=E6=95=B0?= =?UTF-8?q?=E3=80=82fixed=20#I57DGZ?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/IFoxCAD.Cad/Runtime/DBTrans.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/IFoxCAD.Cad/Runtime/DBTrans.cs b/src/IFoxCAD.Cad/Runtime/DBTrans.cs index 6a5d07d..30ce0f0 100644 --- a/src/IFoxCAD.Cad/Runtime/DBTrans.cs +++ b/src/IFoxCAD.Cad/Runtime/DBTrans.cs @@ -111,7 +111,7 @@ public DBTrans(Database database, bool commit = true) /// /// 要打开的文件名 /// 事务是否提交 - public DBTrans(string fileName, bool commit = true) + public DBTrans(string fileName, bool commit = true, FileOpenMode openMode = FileOpenMode.OpenForReadAndWriteNoShare) { #if ac2009 if (string.IsNullOrEmpty(fileName.Trim())) @@ -138,7 +138,7 @@ public DBTrans(string fileName, bool commit = true) if (Path.GetExtension(fileName).ToLower().Contains("dxf")) Database.DxfIn(fileName, null); else - Database.ReadDwgFile(fileName, FileOpenMode.OpenForReadAndWriteNoShare, true, null); + Database.ReadDwgFile(fileName, openMode, true, null); Database.CloseInput(true); } } -- Gitee From 720bd217fbbd5259e68b608d0e22586be9af7eea Mon Sep 17 00:00:00 2001 From: vicwjb <403009819@qq.com> Date: Tue, 17 May 2022 22:56:08 +0800 Subject: [PATCH 218/675] =?UTF-8?q?=E5=A2=9E=E5=8A=A0orthomode=E5=8F=98?= =?UTF-8?q?=E9=87=8F=E5=B1=9E=E6=80=A7?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/IFoxCAD.Cad/Runtime/Env.cs | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/src/IFoxCAD.Cad/Runtime/Env.cs b/src/IFoxCAD.Cad/Runtime/Env.cs index 73438b0..9edeedb 100644 --- a/src/IFoxCAD.Cad/Runtime/Env.cs +++ b/src/IFoxCAD.Cad/Runtime/Env.cs @@ -88,6 +88,15 @@ public static bool CmdEcho set => Application.SetSystemVariable("cmdecho", Convert.ToInt16(value)); } + /// + /// 控制在光标是否为正交模式, 为打开正交, 为关闭正交 + /// + public static bool OrthoMode + { + get => Convert.ToInt16(Application.GetSystemVariable("ORTHOMODE")) == 1; + set => Application.SetSystemVariable("ORTHOMODE", Convert.ToInt16(value)); + } + #region Dimblk /// -- Gitee From 4d8fd0c2d015e5c0fc0027e668f17ad414b96417 Mon Sep 17 00:00:00 2001 From: vicwjb <403009819@qq.com> Date: Tue, 17 May 2022 22:56:56 +0800 Subject: [PATCH 219/675] =?UTF-8?q?=E4=BC=98=E5=8C=96jig?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/IFoxCAD.Cad/ExtensionMethod/Jig.cs | 94 ++++++-------------------- 1 file changed, 20 insertions(+), 74 deletions(-) diff --git a/src/IFoxCAD.Cad/ExtensionMethod/Jig.cs b/src/IFoxCAD.Cad/ExtensionMethod/Jig.cs index c680b85..d066a50 100644 --- a/src/IFoxCAD.Cad/ExtensionMethod/Jig.cs +++ b/src/IFoxCAD.Cad/ExtensionMethod/Jig.cs @@ -7,7 +7,7 @@ * 博客: https://www.cnblogs.com/JJBox/p/15650770.html * * 例子1: - * var ptjig = new Jig(); + * var ptjig = new JigEx(); * ptjig.SetOptions(Point3d.Origin); * var pr = ptjig.Drag(); * if (pr.Status != PromptStatus.OK) @@ -24,7 +24,7 @@ * return; * var getPt = ppr1.Value; * - * var recEntityJig = new Jig((mousePoint, drawEntitys) => { + * var recEntityJig = new JigEx((mousePoint, drawEntitys) => { * #region 画柜子图形 * double length = Math.Abs(getPt.X - mousePoint.X); * double high = Math.Abs(getPt.Y - mousePoint.Y); @@ -62,10 +62,10 @@ namespace IFoxCAD.Cad; using Autodesk.AutoCAD.GraphicsInterface; -using Acap = Autodesk.AutoCAD.ApplicationServices.Application; +using Acap = Application; public delegate void WorldDrawEvent(WorldDraw draw); -public class Jig : DrawJig +public class JigEx : DrawJig { #region 成员 /// @@ -82,11 +82,10 @@ public class Jig : DrawJig public Entity[] Entitys => _drawEntitys.ToArray(); Autodesk.AutoCAD.Geometry.Tolerance _tolerance; - - Queue _drawEntitys;//重复生成的图元,放在这里刷新 - Action>? _action; + readonly Queue _drawEntitys;//重复生成的图元,放在这里刷新 + readonly Action>? _action; JigPromptPointOptions? _options; - const string _orthomode = "orthomode"; + //const string _orthomode = "orthomode"; bool _systemVariablesOrthomode = false; //正交修改 bool _worldDrawFlag = false; // 防止刷新过程中更改队列 #endregion @@ -99,7 +98,7 @@ public class Jig : DrawJig /// 用来频繁执行的回调: 鼠标点,加入显示图元的容器 /// /// 鼠标移动的容差 - public Jig(Action>? action = null, double tolerance = 1e-6) + public JigEx(Action>? action = null, double tolerance = 1e-6) { _action = action; _tolerance = new(tolerance, tolerance); @@ -123,9 +122,10 @@ public JigPromptPointOptions SetOptions(Point3d basePoint, string msg = "点选第二点", bool orthomode = false) { - if (orthomode && CadSystem.Getvar(_orthomode) != "1") + if (orthomode && Env.OrthoMode == false) { - CadSystem.Setvar(_orthomode, "1");//1正交,0非正交 //setvar: https://www.cnblogs.com/JJBox/p/10209541.html + //CadSystem.Setvar(_orthomode, "1");//1正交,0非正交 //setvar: https://www.cnblogs.com/JJBox/p/10209541.html + Env.OrthoMode = true; _systemVariablesOrthomode = true; } var tmp = new JigPromptPointOptions(Environment.NewLine + msg) @@ -152,9 +152,10 @@ public JigPromptPointOptions SetOptions(Point3d basePoint, /// public JigPromptPointOptions SetOptions(string msg, Dictionary? keywords = null, bool orthomode = false) { - if (orthomode && CadSystem.Getvar(_orthomode) != "1") + if (orthomode && Env.OrthoMode == false) { - CadSystem.Setvar(_orthomode, "1");//1正交,0非正交 //setvar: https://www.cnblogs.com/JJBox/p/10209541.html + //CadSystem.Setvar(_orthomode, "1");//1正交,0非正交 //setvar: https://www.cnblogs.com/JJBox/p/10209541.html + Env.OrthoMode = true; _systemVariablesOrthomode = true; } @@ -202,9 +203,10 @@ public void SetOptions(Action action, bool orthomode = fa action.Invoke(tmp); _options = tmp; - if (orthomode && CadSystem.Getvar(_orthomode) != "1") + if (orthomode && Env.OrthoMode == false) { - CadSystem.Setvar(_orthomode, "1");//1正交,0非正交 //setvar: https://www.cnblogs.com/JJBox/p/10209541.html + //CadSystem.Setvar(_orthomode, "1");//1正交,0非正交 //setvar: https://www.cnblogs.com/JJBox/p/10209541.html + Env.OrthoMode = true; _systemVariablesOrthomode = true; } } @@ -221,7 +223,8 @@ public PromptResult Drag() var ed = doc.Editor; var dr = ed.Drag(this); if (_systemVariablesOrthomode) - CadSystem.Setvar(_orthomode, "0");//1正交,0非正交 //setvar: https://www.cnblogs.com/JJBox/p/10209541.html + //CadSystem.Setvar(_orthomode, "0");//1正交,0非正交 //setvar: https://www.cnblogs.com/JJBox/p/10209541.html + Env.OrthoMode = false; return dr; } @@ -264,7 +267,7 @@ protected override SamplerStatus Sampler(JigPrompts prompts) return SamplerStatus.NoChange;//OK的时候拖动鼠标与否都不出现图元 if (_options is null) - throw new ArgumentNullException(nameof(_options)); + throw new NullReferenceException(nameof(_options)); var pro = prompts.AcquirePoint(_options); if (pro.Status == PromptStatus.Keyword) @@ -335,63 +338,6 @@ protected override bool WorldDraw(WorldDraw draw) } - - -class CadSystem -{ -/* 此类函数和env类重复 - * todo:计划删除 - */ - /// - /// 获取系统变量值 - /// - /// 变量名 - /// 成功获取值,失败null - public static string? Getvar(string name) - { - return Acap.GetSystemVariable(name).ToString(); - } - /// - /// 设置系统或环境变量 - /// - /// 变量名 - /// 变量值 - /// 成功设置返回值,失败null - public static void Setvar(string? name, string? parameter) - { - if (name is null) - throw new ArgumentNullException(nameof(name)); - if (parameter is null) - throw new ArgumentNullException(nameof(parameter)); - - try - { - //改系统变量 - var value = Acap.GetSystemVariable(name); - if (value is null) return; - var valueTypeName = value.GetType().Name; - //如果出现了clayer无法设置,是没有锁文档导致的 - switch (valueTypeName) - { - case "String": - Acap.SetSystemVariable(name, parameter.Replace("\"", ""));//去掉引号 - break; - case "Double": - Acap.SetSystemVariable(name, double.Parse(parameter)); - break; - case "Int16": - Acap.SetSystemVariable(name, short.Parse(parameter)); - break; - case "Int32": - Acap.SetSystemVariable(name, int.Parse(parameter)); - break; - } - } - catch - { } - } -} - #if false | UserInputControls.NullResponseAccepted //接受空响应 | UserInputControls.DoNotEchoCancelForCtrlC //不要取消CtrlC的回音 -- Gitee From 296d841a6a8167d1992356bba4ef1efac2680b44 Mon Sep 17 00:00:00 2001 From: vicwjb <403009819@qq.com> Date: Tue, 24 May 2022 22:52:11 +0800 Subject: [PATCH 220/675] =?UTF-8?q?=E4=BC=98=E5=8C=96=E4=BE=9D=E8=B5=96?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- IFoxCAD.sln | 10 ++++----- src/IFoxCAD.Basal/IFoxCAD.Basal.csproj | 17 +++++----------- src/IFoxCAD.Cad/IFoxCAD.Cad.csproj | 10 ++++----- src/IFoxCAD.Cad/Runtime/IAutoGo.cs | 28 ++++++++++++++++---------- 4 files changed, 32 insertions(+), 33 deletions(-) diff --git a/IFoxCAD.sln b/IFoxCAD.sln index a9e0664..ee26109 100644 --- a/IFoxCAD.sln +++ b/IFoxCAD.sln @@ -3,15 +3,15 @@ Microsoft Visual Studio Solution File, Format Version 12.00 # Visual Studio Version 17 VisualStudioVersion = 17.1.32113.165 MinimumVisualStudioVersion = 10.0.40219.1 -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "IFoxCAD.Cad", "src\IFoxCAD.Cad\IFoxCAD.Cad.csproj", "{D5FAED7C-A99B-4BEE-A745-45442DC44971}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "IFoxCAD.Cad", "\Mac\Home\Documents\program\projects\ifoxcad\src\IFoxCAD.Cad\IFoxCAD.Cad.csproj", "{D5FAED7C-A99B-4BEE-A745-45442DC44971}" EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Test", "tests\Test\Test.csproj", "{B1602568-F023-46C7-B635-3CB281F1EC00}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Test", "\Mac\Home\Documents\program\projects\ifoxcad\tests\Test\Test.csproj", "{B1602568-F023-46C7-B635-3CB281F1EC00}" EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "IFoxCAD.WPF", "src\IFoxCAD.WPF\IFoxCAD.WPF.csproj", "{D820D629-1AB3-4BE3-A772-CB753D98CCB8}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "IFoxCAD.WPF", "\Mac\Home\Documents\program\projects\ifoxcad\src\IFoxCAD.WPF\IFoxCAD.WPF.csproj", "{D820D629-1AB3-4BE3-A772-CB753D98CCB8}" EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "IFoxCAD.Basal", "src\IFoxCAD.Basal\IFoxCAD.Basal.csproj", "{40BF07C4-100A-4810-A27B-4587CFB38E6E}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "IFoxCAD.Basal", "\Mac\Home\Documents\program\projects\ifoxcad\src\IFoxCAD.Basal\IFoxCAD.Basal.csproj", "{40BF07C4-100A-4810-A27B-4587CFB38E6E}" EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "TestConsole", "tests\TestConsole\TestConsole.csproj", "{E2873F0B-CAD2-45E8-8FF0-C05C0C6AD955}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "TestConsole", "\Mac\Home\Documents\program\projects\ifoxcad\tests\TestConsole\TestConsole.csproj", "{E2873F0B-CAD2-45E8-8FF0-C05C0C6AD955}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution diff --git a/src/IFoxCAD.Basal/IFoxCAD.Basal.csproj b/src/IFoxCAD.Basal/IFoxCAD.Basal.csproj index 607b7ea..b739f81 100644 --- a/src/IFoxCAD.Basal/IFoxCAD.Basal.csproj +++ b/src/IFoxCAD.Basal/IFoxCAD.Basal.csproj @@ -4,7 +4,7 @@ net35;net40;net45 true enable - 0.3.3 + 0.3.4 InspireFunction xsfhlzh;vicwjb 基于.NET的二次开发基本类库 @@ -13,8 +13,7 @@ https://gitee.com/inspirefunction/ifoxcad.git git IFoxCAD;C#;NET;Common;Basal - 增加字典和数组的扩展函数. - + 优化依赖. true preview true @@ -29,16 +28,10 @@ - - - - - - - - + + + - DEBUG diff --git a/src/IFoxCAD.Cad/IFoxCAD.Cad.csproj b/src/IFoxCAD.Cad/IFoxCAD.Cad.csproj index 439344e..522723b 100644 --- a/src/IFoxCAD.Cad/IFoxCAD.Cad.csproj +++ b/src/IFoxCAD.Cad/IFoxCAD.Cad.csproj @@ -15,7 +15,7 @@ https://gitee.com/inspirefunction/ifoxcad.git git IFoxCAD;CAD;AutoCad;C#;NET - 注释掉AOP. + 优化依赖. true true preview @@ -29,22 +29,18 @@ runtime - - runtime - runtime - @@ -71,6 +67,10 @@ + + + + diff --git a/src/IFoxCAD.Cad/Runtime/IAutoGo.cs b/src/IFoxCAD.Cad/Runtime/IAutoGo.cs index 97d3503..58627de 100644 --- a/src/IFoxCAD.Cad/Runtime/IAutoGo.cs +++ b/src/IFoxCAD.Cad/Runtime/IAutoGo.cs @@ -268,23 +268,29 @@ void GetAttributeFunctions() { return; } - foreach (var method in type.GetMethods()) + var mets = type.GetMethods(); + for (int ii = 0; ii < mets.Length; ii++) { - var attr = method.GetCustomAttributes(true).FirstOrDefault(); - if (attr is not null) + var method = mets[ii]; + var attr = method.GetCustomAttributes(true); + for (int jj = 0; jj < attr.Length; jj++) { - var runs = new RunClass(method, attr.SequenceId); - if (attr.IsInitialize) + if (attr[jj] is IFoxInitialize jjAtt) { - _InitializeList.Add(runs); - } - else - { - _TerminateList.Add(runs); + var runc = new RunClass(method, jjAtt.SequenceId); + if (jjAtt.IsInitialize) + { + _InitializeList.Add(runc); + } + else + { + _TerminateList.Add(runc); + } + break; } } } - },_DllName); + }, _DllName); } /// -- Gitee From c9550f1f348c6099fe1d192c355070c418e68fb7 Mon Sep 17 00:00:00 2001 From: liuqihong <540762622@qq.com> Date: Thu, 26 May 2022 00:06:37 +0800 Subject: [PATCH 221/675] =?UTF-8?q?=E8=A1=A5=E5=85=85=E9=BB=98=E8=AE=A4?= =?UTF-8?q?=E5=8F=82=E6=95=B0=E9=98=B2=E6=AD=A2=E4=B8=8D=E5=90=8C=E7=89=88?= =?UTF-8?q?=E6=9C=AC=E5=87=BA=E9=94=99?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/IFoxCAD.Cad/ExtensionMethod/EntityEx.cs | 40 ++++++++++----------- 1 file changed, 19 insertions(+), 21 deletions(-) diff --git a/src/IFoxCAD.Cad/ExtensionMethod/EntityEx.cs b/src/IFoxCAD.Cad/ExtensionMethod/EntityEx.cs index 60e421a..38e07f7 100644 --- a/src/IFoxCAD.Cad/ExtensionMethod/EntityEx.cs +++ b/src/IFoxCAD.Cad/ExtensionMethod/EntityEx.cs @@ -216,8 +216,7 @@ public static string GetUnFormatString(this MText mt) List strs = new(); mt.ExplodeFragments( strs, - (f, o) => - { + (f, o) => { o.Add(f.Text); return MTextFragmentCallbackStatus.Continue; }); @@ -238,6 +237,7 @@ public static string GetUnFormatString(this MText mt) public static Arc CreateArcSCE(Point3d startPoint, Point3d centerPoint, Point3d endPoint) { Arc arc = new(); + arc.SetDatabaseDefaults(); arc.Center = centerPoint; arc.Radius = centerPoint.DistanceTo(startPoint); Vector2d startVector = new(startPoint.X - centerPoint.X, startPoint.Y - centerPoint.Y); @@ -278,6 +278,7 @@ public static Arc CreateArc(Point3d startPoint, Point3d pointOnArc, Point3d endP public static Arc CreateArc(Point3d startPoint, Point3d centerPoint, double angle) { Arc arc = new(); + arc.SetDatabaseDefaults(); arc.Center = centerPoint; arc.Radius = centerPoint.DistanceTo(startPoint); Vector2d startVector = new(startPoint.X - centerPoint.X, startPoint.Y - centerPoint.Y); @@ -286,9 +287,9 @@ public static Arc CreateArc(Point3d startPoint, Point3d centerPoint, double angl return arc; } -#endregion + #endregion -#region 圆 + #region 圆 /// /// 两点创建圆(两点中点为圆心) @@ -299,6 +300,7 @@ public static Arc CreateArc(Point3d startPoint, Point3d centerPoint, double angl public static Circle CreateCircle(Point3d startPoint, Point3d endPoint) { Circle circle = new(); + circle.SetDatabaseDefaults(); circle.Center = startPoint.GetMidPointTo(endPoint); circle.Radius = startPoint.DistanceTo(endPoint) * 0.5; return circle; @@ -318,23 +320,19 @@ public static Circle CreateCircle(Point3d startPoint, Point3d endPoint) Vector3d vb = pt1.GetVectorTo(pt3); //如两矢量夹角为0或180度(π弧度),则三点共线. if (va.GetAngleTo(vb) == 0 | va.GetAngleTo(vb) == Math.PI) - { return null; - } - else - { - //创建一个几何类的圆弧对象 - CircularArc3d geArc = new(pt1, pt2, pt3); - geArc.ToCircle(); - return geArc.ToCircle(); - } + + //创建一个几何类的圆弧对象 + CircularArc3d geArc = new(pt1, pt2, pt3); + geArc.ToCircle(); + return geArc.ToCircle(); } -#endregion + #endregion -#region 块参照 + #region 块参照 -#region 裁剪块参照 + #region 裁剪块参照 private const string filterDictName = "ACAD_FILTER"; private const string spatialName = "SPATIAL"; @@ -395,14 +393,14 @@ public static void ClipBlockRef(this BlockReference bref, Point3d pt1, Point3d p dict.SetAt(spatialName, sf); //SetToDictionary(dict, spatialName, sf); } -#endregion + #endregion /// /// 更新动态块属性值 /// /// 动态块 /// 属性值字典 - public static void ChangeBlockProperty(this BlockReference blockReference, Dictionary propertyNameValues) + public static void ChangeBlockProperty(this BlockReference blockReference, Dictionary propertyNameValues) { if (blockReference.IsDynamicBlock) { @@ -416,9 +414,9 @@ public static void ChangeBlockProperty(this BlockReference blockReference, Dicti } } } - + } } - -#endregion + + #endregion } -- Gitee From 60ccc3d33702c8febe848956bed680af054da68a Mon Sep 17 00:00:00 2001 From: liuqihong <540762622@qq.com> Date: Thu, 26 May 2022 00:08:05 +0800 Subject: [PATCH 222/675] =?UTF-8?q?=E5=88=A0=E9=99=A4=E5=A4=9A=E4=BD=99?= =?UTF-8?q?=E6=B3=A8=E9=87=8A?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/IFoxCAD.Cad/ExtensionMethod/EditorEx.cs | 34 ++++++++------------- 1 file changed, 12 insertions(+), 22 deletions(-) diff --git a/src/IFoxCAD.Cad/ExtensionMethod/EditorEx.cs b/src/IFoxCAD.Cad/ExtensionMethod/EditorEx.cs index 8277507..0d4f19e 100644 --- a/src/IFoxCAD.Cad/ExtensionMethod/EditorEx.cs +++ b/src/IFoxCAD.Cad/ExtensionMethod/EditorEx.cs @@ -1,6 +1,4 @@ -using System.Runtime.InteropServices; - -namespace IFoxCAD.Cad; +namespace IFoxCAD.Cad; /// /// 命令行扩展类 @@ -55,10 +53,6 @@ public static SelectionSet SelectByLineWeight(this Editor editor, LineWeight lin return res.Value; } - - - - public static PromptSelectionResult? SSGet(this Editor editor, string? mode = null, SelectionFilter? filter = null, string[]? messages = null, Dictionary? keywords = null) { var pso = new PromptSelectionOptions(); @@ -106,13 +100,9 @@ public static SelectionSet SelectByLineWeight(this Editor editor, LineWeight lin try { if (filter is not null) - { ss = editor.GetSelection(pso, filter); - } else - { ss = editor.GetSelection(pso); - } } catch (Autodesk.AutoCAD.Runtime.Exception e) { @@ -360,7 +350,7 @@ public static void SsgetAddKeys(this PromptSelectionOptions pso, public static void StreamMessage(string format, params object[] args) { StreamMessage(string.Format(format, args)); - }// + } /// /// 带错误提示对话框的打印信息函数 @@ -379,7 +369,7 @@ public static void StreamMessage(string message) { Message(ex); } - }// + } /// /// 异常信息对话框 @@ -398,7 +388,7 @@ public static void Message(System.Exception ex) catch { } - }// + } /// /// 提示信息对话框 @@ -419,7 +409,7 @@ public static void InfoMessageBox(string caption, string message) { Message(ex); } - }// + } /// /// 提示信息对话框 @@ -439,7 +429,7 @@ public static void InfoMessageBox(string caption, string format, params object[] public static void InfoMessageBox(string message) { InfoMessageBox("NFox.Cad", message); - }// + } /// /// 提示信息对话框 @@ -449,7 +439,7 @@ public static void InfoMessageBox(string message) public static void InfoMessageBox(string format, params object[] args) { InfoMessageBox(string.Format(format, args)); - }// + } /// /// 命令行打印字符串 @@ -468,7 +458,7 @@ public static void WriteMessage(string message) { Message(ex); } - }// + } /// /// 命令行打印字符串 @@ -489,7 +479,7 @@ public static bool HasEditor() return Application.DocumentManager.MdiActiveDocument is not null && Application.DocumentManager.Count != 0 && Application.DocumentManager.MdiActiveDocument.Editor is not null; - }// + } /// /// 判断是否可以打印字符串 @@ -499,7 +489,7 @@ public static bool Acceptable() { return HasEditor() && !Application.DocumentManager.MdiActiveDocument.Editor.IsDragging; - }// + } #endregion Info @@ -1025,7 +1015,7 @@ public static PromptResult GetString(this Editor ed, string Message, string Defa return ed.GetString(strOp); } - #endregion Get交互类 + #endregion #region 执行lisp @@ -1070,5 +1060,5 @@ public static PromptResult GetString(this Editor ed, string Message, string Defa return null; } - #endregion 执行lisp + #endregion } -- Gitee From 2a2d7c18d9321caf769de1edecd210ff2861924d Mon Sep 17 00:00:00 2001 From: liuqihong <540762622@qq.com> Date: Thu, 26 May 2022 00:08:57 +0800 Subject: [PATCH 223/675] =?UTF-8?q?=E4=BF=AE=E6=94=B9=E7=9B=B8=E5=AF=B9?= =?UTF-8?q?=E8=B7=AF=E5=BE=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- IFoxCAD.sln | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/IFoxCAD.sln b/IFoxCAD.sln index ee26109..a9e0664 100644 --- a/IFoxCAD.sln +++ b/IFoxCAD.sln @@ -3,15 +3,15 @@ Microsoft Visual Studio Solution File, Format Version 12.00 # Visual Studio Version 17 VisualStudioVersion = 17.1.32113.165 MinimumVisualStudioVersion = 10.0.40219.1 -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "IFoxCAD.Cad", "\Mac\Home\Documents\program\projects\ifoxcad\src\IFoxCAD.Cad\IFoxCAD.Cad.csproj", "{D5FAED7C-A99B-4BEE-A745-45442DC44971}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "IFoxCAD.Cad", "src\IFoxCAD.Cad\IFoxCAD.Cad.csproj", "{D5FAED7C-A99B-4BEE-A745-45442DC44971}" EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Test", "\Mac\Home\Documents\program\projects\ifoxcad\tests\Test\Test.csproj", "{B1602568-F023-46C7-B635-3CB281F1EC00}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Test", "tests\Test\Test.csproj", "{B1602568-F023-46C7-B635-3CB281F1EC00}" EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "IFoxCAD.WPF", "\Mac\Home\Documents\program\projects\ifoxcad\src\IFoxCAD.WPF\IFoxCAD.WPF.csproj", "{D820D629-1AB3-4BE3-A772-CB753D98CCB8}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "IFoxCAD.WPF", "src\IFoxCAD.WPF\IFoxCAD.WPF.csproj", "{D820D629-1AB3-4BE3-A772-CB753D98CCB8}" EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "IFoxCAD.Basal", "\Mac\Home\Documents\program\projects\ifoxcad\src\IFoxCAD.Basal\IFoxCAD.Basal.csproj", "{40BF07C4-100A-4810-A27B-4587CFB38E6E}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "IFoxCAD.Basal", "src\IFoxCAD.Basal\IFoxCAD.Basal.csproj", "{40BF07C4-100A-4810-A27B-4587CFB38E6E}" EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "TestConsole", "\Mac\Home\Documents\program\projects\ifoxcad\tests\TestConsole\TestConsole.csproj", "{E2873F0B-CAD2-45E8-8FF0-C05C0C6AD955}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "TestConsole", "tests\TestConsole\TestConsole.csproj", "{E2873F0B-CAD2-45E8-8FF0-C05C0C6AD955}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution -- Gitee From 59f98cef5413cdd38bba3f5f6c813a322e17d5c1 Mon Sep 17 00:00:00 2001 From: vicwjb <403009819@qq.com> Date: Thu, 26 May 2022 22:42:10 +0800 Subject: [PATCH 224/675] =?UTF-8?q?v0.3.5=E7=89=88=E6=9C=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/IFoxCAD.Cad/IFoxCAD.Cad.csproj | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/IFoxCAD.Cad/IFoxCAD.Cad.csproj b/src/IFoxCAD.Cad/IFoxCAD.Cad.csproj index 522723b..71129d5 100644 --- a/src/IFoxCAD.Cad/IFoxCAD.Cad.csproj +++ b/src/IFoxCAD.Cad/IFoxCAD.Cad.csproj @@ -6,7 +6,7 @@ enable true true - 0.3.4.1 + 0.3.5 InspireFunction xsfhlzh;vicwjb 基于.NET的Cad二次开发类库 -- Gitee From d478ff88c99673bab2b4ae033835b22a0dc453dd Mon Sep 17 00:00:00 2001 From: vicwjb <403009819@qq.com> Date: Fri, 27 May 2022 00:02:36 +0800 Subject: [PATCH 225/675] =?UTF-8?q?=E5=A2=9E=E5=8A=A0ValueTuple,Index,Rang?= =?UTF-8?q?e=E6=94=AF=E6=8C=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/IFoxCAD.Basal/IFoxCAD.Basal.csproj | 5 +- src/IFoxCAD.Basal/Index.cs | 148 ++ src/IFoxCAD.Basal/Range.cs | 102 + src/IFoxCAD.Basal/RuntimeHelpers.cs | 46 + .../TupleElementNamesAttribute.cs | 56 + src/IFoxCAD.Basal/ValueTuple.cs | 2129 +++++++++++++++++ tests/TestConsole/Program.cs | 12 +- tests/TestConsole/TestConsole.csproj | 2 +- 8 files changed, 2494 insertions(+), 6 deletions(-) create mode 100644 src/IFoxCAD.Basal/Index.cs create mode 100644 src/IFoxCAD.Basal/Range.cs create mode 100644 src/IFoxCAD.Basal/RuntimeHelpers.cs create mode 100644 src/IFoxCAD.Basal/TupleElementNamesAttribute.cs create mode 100644 src/IFoxCAD.Basal/ValueTuple.cs diff --git a/src/IFoxCAD.Basal/IFoxCAD.Basal.csproj b/src/IFoxCAD.Basal/IFoxCAD.Basal.csproj index b739f81..1ff2a4d 100644 --- a/src/IFoxCAD.Basal/IFoxCAD.Basal.csproj +++ b/src/IFoxCAD.Basal/IFoxCAD.Basal.csproj @@ -19,6 +19,7 @@ true LICENSE true + True @@ -29,9 +30,9 @@ - + DEBUG diff --git a/src/IFoxCAD.Basal/Index.cs b/src/IFoxCAD.Basal/Index.cs new file mode 100644 index 0000000..de53ed9 --- /dev/null +++ b/src/IFoxCAD.Basal/Index.cs @@ -0,0 +1,148 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System.Runtime.CompilerServices; + +namespace System; + +/// Represent a type can be used to index a collection either from the start or the end. +/// +/// Index is used by the C# compiler to support the new index syntax +/// +/// int[] someArray = new int[5] { 1, 2, 3, 4, 5 } ; +/// int lastElement = someArray[^1]; // lastElement = 5 +/// +/// +public readonly struct Index : IEquatable +{ + private readonly int _value; + + /// Construct an Index using a value and indicating if the index is from the start or from the end. + /// The index value. it has to be zero or positive number. + /// Indicating if the index is from the start or from the end. + /// + /// If the Index constructed from the end, index value 1 means pointing at the last element and index value 0 means pointing at beyond last element. + /// +#if NET45 + [MethodImpl(MethodImplOptions.AggressiveInlining)] +#endif + public Index(int value, bool fromEnd = false) + { + if (value < 0) + { + throw new ArgumentOutOfRangeException(nameof(value), "value must be non-negative"); + } + + if (fromEnd) + _value = ~value; + else + _value = value; + } + + // The following private constructors mainly created for perf reason to avoid the checks + private Index(int value) + { + _value = value; + } + + /// Create an Index pointing at first element. + public static Index Start => new Index(0); + + /// Create an Index pointing at beyond last element. + public static Index End => new Index(~0); + + /// Create an Index from the start at the position indicated by the value. + /// The index value from the start. +#if NET45 + [MethodImpl(MethodImplOptions.AggressiveInlining)] +#endif + public static Index FromStart(int value) + { + if (value < 0) + { + throw new ArgumentOutOfRangeException(nameof(value), "value must be non-negative"); + } + + return new Index(value); + } + + /// Create an Index from the end at the position indicated by the value. + /// The index value from the end. +#if NET45 + [MethodImpl(MethodImplOptions.AggressiveInlining)] +#endif + public static Index FromEnd(int value) + { + if (value < 0) + { + throw new ArgumentOutOfRangeException(nameof(value), "value must be non-negative"); + } + + return new Index(~value); + } + + /// Returns the index value. + public int Value + { + get + { + if (_value < 0) + return ~_value; + else + return _value; + } + } + + /// Indicates whether the index is from the start or the end. + public bool IsFromEnd => _value < 0; + + /// Calculate the offset from the start using the giving collection length. + /// The length of the collection that the Index will be used with. length has to be a positive value + /// + /// For performance reason, we don't validate the input length parameter and the returned offset value against negative values. + /// we don't validate either the returned offset is greater than the input length. + /// It is expected Index will be used with collections which always have non negative length/count. If the returned offset is negative and + /// then used to index a collection will get out of range exception which will be same affect as the validation. + /// +#if NET45 + [MethodImpl(MethodImplOptions.AggressiveInlining)] +#endif + public int GetOffset(int length) + { + int offset = _value; + if (IsFromEnd) + { + // offset = length - (~value) + // offset = length + (~(~value) + 1) + // offset = length + value + 1 + + offset += length + 1; + } + return offset; + } + + /// Indicates whether the current Index object is equal to another object of the same type. + /// An object to compare with this object + public override bool Equals(object? value) => value is Index && _value == ((Index)value)._value; + + /// Indicates whether the current Index object is equal to another Index object. + /// An object to compare with this object + public bool Equals(Index other) => _value == other._value; + + /// Returns the hash code for this instance. + public override int GetHashCode() => _value; + + /// Converts integer number to an Index. + public static implicit operator Index(int value) => FromStart(value); + + /// Converts the value of the current Index object to its equivalent string representation. + public override string ToString() + { + if (IsFromEnd) + return "^" + ((uint)Value).ToString(); + + return ((uint)Value).ToString(); + } +} + diff --git a/src/IFoxCAD.Basal/Range.cs b/src/IFoxCAD.Basal/Range.cs new file mode 100644 index 0000000..99fc7a2 --- /dev/null +++ b/src/IFoxCAD.Basal/Range.cs @@ -0,0 +1,102 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System.Runtime.CompilerServices; + +namespace System; + +/// Represent a range has start and end indexes. +/// +/// Range is used by the C# compiler to support the range syntax. +/// +/// int[] someArray = new int[5] { 1, 2, 3, 4, 5 }; +/// int[] subArray1 = someArray[0..2]; // { 1, 2 } +/// int[] subArray2 = someArray[1..^0]; // { 2, 3, 4, 5 } +/// +/// +public readonly struct Range : IEquatable +{ + /// Represent the inclusive start index of the Range. + public Index Start { get; } + + /// Represent the exclusive end index of the Range. + public Index End { get; } + + /// Construct a Range object using the start and end indexes. + /// Represent the inclusive start index of the range. + /// Represent the exclusive end index of the range. + public Range(Index start, Index end) + { + Start = start; + End = end; + } + + /// Indicates whether the current Range object is equal to another object of the same type. + /// An object to compare with this object + public override bool Equals(object? value) => + value is Range r && + r.Start.Equals(Start) && + r.End.Equals(End); + + /// Indicates whether the current Range object is equal to another Range object. + /// An object to compare with this object + public bool Equals(Range other) => other.Start.Equals(Start) && other.End.Equals(End); + + /// Returns the hash code for this instance. + public override int GetHashCode() + { + return Start.GetHashCode() * 31 + End.GetHashCode(); + } + + /// Converts the value of the current Range object to its equivalent string representation. + public override string ToString() + { + return Start + ".." + End; + } + + /// Create a Range object starting from start index to the end of the collection. + public static Range StartAt(Index start) => new Range(start, Index.End); + + /// Create a Range object starting from first element in the collection to the end Index. + public static Range EndAt(Index end) => new Range(Index.Start, end); + + /// Create a Range object starting from first element to the end. + public static Range All => new Range(Index.Start, Index.End); + + /// Calculate the start offset and length of range object using a collection length. + /// The length of the collection that the range will be used with. length has to be a positive value. + /// + /// For performance reason, we don't validate the input length parameter against negative values. + /// It is expected Range will be used with collections which always have non negative length/count. + /// We validate the range is inside the length scope though. + /// +#if NET45 + [MethodImpl(MethodImplOptions.AggressiveInlining)] +#endif + [CLSCompliant(false)] + public (int Offset, int Length) GetOffsetAndLength(int length) + { + int start; + Index startIndex = Start; + if (startIndex.IsFromEnd) + start = length - startIndex.Value; + else + start = startIndex.Value; + + int end; + Index endIndex = End; + if (endIndex.IsFromEnd) + end = length - endIndex.Value; + else + end = endIndex.Value; + + if ((uint)end > (uint)length || (uint)start > (uint)end) + { + throw new ArgumentOutOfRangeException(nameof(length)); + } + + return (start, end - start); + } +} + diff --git a/src/IFoxCAD.Basal/RuntimeHelpers.cs b/src/IFoxCAD.Basal/RuntimeHelpers.cs new file mode 100644 index 0000000..a70f317 --- /dev/null +++ b/src/IFoxCAD.Basal/RuntimeHelpers.cs @@ -0,0 +1,46 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +namespace System.Runtime.CompilerServices +{ + internal static class RuntimeHelpers + { + /// + /// Slices the specified array using the specified range. + /// + public static T[] GetSubArray(T[] array, Range range) + { + if (array == null) + { + throw new ArgumentNullException(); + } + + (int offset, int length) = range.GetOffsetAndLength(array.Length); + + if (default(T)! != null || typeof(T[]) == array.GetType()) // TODO-NULLABLE: default(T) == null warning (https://github.com/dotnet/roslyn/issues/34757) + { + // We know the type of the array to be exactly T[]. + + if (length == 0) + { + //return Array.Empty(); + return new T[0]; + + + } + + var dest = new T[length]; + Array.Copy(array, offset, dest, 0, length); + return dest; + } + else + { + // The array is actually a U[] where U:T. + T[] dest = (T[])Array.CreateInstance(array.GetType().GetElementType()!, length); + Array.Copy(array, offset, dest, 0, length); + return dest; + } + } + } +} \ No newline at end of file diff --git a/src/IFoxCAD.Basal/TupleElementNamesAttribute.cs b/src/IFoxCAD.Basal/TupleElementNamesAttribute.cs new file mode 100644 index 0000000..074b2e1 --- /dev/null +++ b/src/IFoxCAD.Basal/TupleElementNamesAttribute.cs @@ -0,0 +1,56 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System.Collections.Generic; + +namespace System.Runtime.CompilerServices +{ + /// + /// Indicates that the use of on a member is meant to be treated as a tuple with element names. + /// + [AttributeUsage(AttributeTargets.Field | AttributeTargets.Parameter | AttributeTargets.Property | AttributeTargets.ReturnValue | AttributeTargets.Class | AttributeTargets.Struct | AttributeTargets.Event)] + public sealed class TupleElementNamesAttribute : Attribute + { + private readonly string[] _transformNames; + + /// + /// Initializes a new instance of the class. + /// + /// + /// Specifies, in a pre-order depth-first traversal of a type's + /// construction, which occurrences are + /// meant to carry element names. + /// + /// + /// This constructor is meant to be used on types that contain an + /// instantiation of that contains + /// element names. For instance, if C is a generic type with + /// two type parameters, then a use of the constructed type C{, might be intended to + /// treat the first type argument as a tuple with element names and the + /// second as a tuple without element names. In which case, the + /// appropriate attribute specification should use a + /// transformNames value of { "name1", "name2", null, null, + /// null }. + /// + public TupleElementNamesAttribute(string[] transformNames) + { + if (transformNames == null) + { + throw new ArgumentNullException(nameof(transformNames)); + } + + _transformNames = transformNames; + } + + /// + /// Specifies, in a pre-order depth-first traversal of a type's + /// construction, which elements are + /// meant to carry element names. + /// + public IList TransformNames => _transformNames; + } +} \ No newline at end of file diff --git a/src/IFoxCAD.Basal/ValueTuple.cs b/src/IFoxCAD.Basal/ValueTuple.cs new file mode 100644 index 0000000..0faa07d --- /dev/null +++ b/src/IFoxCAD.Basal/ValueTuple.cs @@ -0,0 +1,2129 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +#pragma warning disable SA1141 // explicitly not using tuple syntax in tuple implementation + +using System.Collections; +using System.Collections.Generic; +using System.Diagnostics; +using System.Diagnostics.CodeAnalysis; +using System.Runtime.InteropServices; +using System.Runtime.CompilerServices; +using System.Globalization; +using System.Numerics.Hashing; + +namespace System.Collections +{ + public interface IStructuralComparable + { + int CompareTo(object? other, IComparer comparer); + } +} + +namespace System.Collections +{ + public interface IStructuralEquatable + { + bool Equals(object? other, IEqualityComparer comparer); + int GetHashCode(IEqualityComparer comparer); + } +} + + +namespace System.Numerics.Hashing +{ + internal static class HashHelpers + { + public static readonly int RandomSeed = Guid.NewGuid().GetHashCode(); + + public static int Combine(int h1, int h2) + { + unchecked + { + // RyuJIT optimizes this to use the ROL instruction + // Related GitHub pull request: dotnet/coreclr#1830 + uint rol5 = ((uint)h1 << 5) | ((uint)h1 >> 27); + return ((int)rol5 + h1) ^ h2; + } + } + } +} + + + + + + +namespace System +{ + internal static class SR + { + public const string ArgumentException_ValueTupleIncorrectType = "The parameter should be a ValueTuple type of appropriate arity."; + public const string ArgumentException_ValueTupleLastArgumentNotAValueTuple = "The TRest type argument of ValueTuple`8 must be a ValueTuple."; + } + + /// + /// Helper so we can call some tuple methods recursively without knowing the underlying types. + /// + internal interface ITupleInternal + { + int GetHashCode(IEqualityComparer comparer); + int Size { get; } + string ToStringEnd(); + } + + /// + /// The ValueTuple types (from arity 0 to 8) comprise the runtime implementation that underlies tuples in C# and struct tuples in F#. + /// Aside from created via language syntax, they are most easily created via the ValueTuple.Create factory methods. + /// The System.ValueTuple types differ from the System.Tuple types in that: + /// - they are structs rather than classes, + /// - they are mutable rather than readonly, and + /// - their members (such as Item1, Item2, etc) are fields rather than properties. + /// + public struct ValueTuple + : IEquatable, IStructuralEquatable, IStructuralComparable, IComparable, IComparable, ITupleInternal + { + /// + /// Returns a value that indicates whether the current instance is equal to a specified object. + /// + /// The object to compare with this instance. + /// if is a . + public override bool Equals(object obj) + { + return obj is ValueTuple; + } + + /// Returns a value indicating whether this instance is equal to a specified value. + /// An instance to compare to this instance. + /// true if has the same value as this instance; otherwise, false. + public bool Equals(ValueTuple other) + { + return true; + } + + bool IStructuralEquatable.Equals(object other, IEqualityComparer comparer) + { + return other is ValueTuple; + } + + int IComparable.CompareTo(object other) + { + if (other == null) return 1; + + if (!(other is ValueTuple)) + { + throw new ArgumentException(SR.ArgumentException_ValueTupleIncorrectType, nameof(other)); + } + + return 0; + } + + /// Compares this instance to a specified instance and returns an indication of their relative values. + /// An instance to compare. + /// + /// A signed number indicating the relative values of this instance and . + /// Returns less than zero if this instance is less than , zero if this + /// instance is equal to , and greater than zero if this instance is greater + /// than . + /// + public int CompareTo(ValueTuple other) + { + return 0; + } + + int IStructuralComparable.CompareTo(object other, IComparer comparer) + { + if (other == null) return 1; + + if (!(other is ValueTuple)) + { + throw new ArgumentException(SR.ArgumentException_ValueTupleIncorrectType, nameof(other)); + } + + return 0; + } + + /// Returns the hash code for this instance. + /// A 32-bit signed integer hash code. + public override int GetHashCode() + { + return 0; + } + + int IStructuralEquatable.GetHashCode(IEqualityComparer comparer) + { + return 0; + } + + int ITupleInternal.GetHashCode(IEqualityComparer comparer) + { + return 0; + } + + /// + /// Returns a string that represents the value of this instance. + /// + /// The string representation of this instance. + /// + /// The string returned by this method takes the form (). + /// + public override string ToString() + { + return "()"; + } + + string ITupleInternal.ToStringEnd() + { + return ")"; + } + + int ITupleInternal.Size => 0; + + /// Creates a new struct 0-tuple. + /// A 0-tuple. + public static ValueTuple Create() => + new ValueTuple(); + + /// Creates a new struct 1-tuple, or singleton. + /// The type of the first component of the tuple. + /// The value of the first component of the tuple. + /// A 1-tuple (singleton) whose value is (item1). + public static ValueTuple Create(T1 item1) => + new ValueTuple(item1); + + /// Creates a new struct 2-tuple, or pair. + /// The type of the first component of the tuple. + /// The type of the second component of the tuple. + /// The value of the first component of the tuple. + /// The value of the second component of the tuple. + /// A 2-tuple (pair) whose value is (item1, item2). + public static ValueTuple Create(T1 item1, T2 item2) => + new ValueTuple(item1, item2); + + /// Creates a new struct 3-tuple, or triple. + /// The type of the first component of the tuple. + /// The type of the second component of the tuple. + /// The type of the third component of the tuple. + /// The value of the first component of the tuple. + /// The value of the second component of the tuple. + /// The value of the third component of the tuple. + /// A 3-tuple (triple) whose value is (item1, item2, item3). + public static ValueTuple Create(T1 item1, T2 item2, T3 item3) => + new ValueTuple(item1, item2, item3); + + /// Creates a new struct 4-tuple, or quadruple. + /// The type of the first component of the tuple. + /// The type of the second component of the tuple. + /// The type of the third component of the tuple. + /// The type of the fourth component of the tuple. + /// The value of the first component of the tuple. + /// The value of the second component of the tuple. + /// The value of the third component of the tuple. + /// The value of the fourth component of the tuple. + /// A 4-tuple (quadruple) whose value is (item1, item2, item3, item4). + public static ValueTuple Create(T1 item1, T2 item2, T3 item3, T4 item4) => + new ValueTuple(item1, item2, item3, item4); + + /// Creates a new struct 5-tuple, or quintuple. + /// The type of the first component of the tuple. + /// The type of the second component of the tuple. + /// The type of the third component of the tuple. + /// The type of the fourth component of the tuple. + /// The type of the fifth component of the tuple. + /// The value of the first component of the tuple. + /// The value of the second component of the tuple. + /// The value of the third component of the tuple. + /// The value of the fourth component of the tuple. + /// The value of the fifth component of the tuple. + /// A 5-tuple (quintuple) whose value is (item1, item2, item3, item4, item5). + public static ValueTuple Create(T1 item1, T2 item2, T3 item3, T4 item4, T5 item5) => + new ValueTuple(item1, item2, item3, item4, item5); + + /// Creates a new struct 6-tuple, or sextuple. + /// The type of the first component of the tuple. + /// The type of the second component of the tuple. + /// The type of the third component of the tuple. + /// The type of the fourth component of the tuple. + /// The type of the fifth component of the tuple. + /// The type of the sixth component of the tuple. + /// The value of the first component of the tuple. + /// The value of the second component of the tuple. + /// The value of the third component of the tuple. + /// The value of the fourth component of the tuple. + /// The value of the fifth component of the tuple. + /// The value of the sixth component of the tuple. + /// A 6-tuple (sextuple) whose value is (item1, item2, item3, item4, item5, item6). + public static ValueTuple Create(T1 item1, T2 item2, T3 item3, T4 item4, T5 item5, T6 item6) => + new ValueTuple(item1, item2, item3, item4, item5, item6); + + /// Creates a new struct 7-tuple, or septuple. + /// The type of the first component of the tuple. + /// The type of the second component of the tuple. + /// The type of the third component of the tuple. + /// The type of the fourth component of the tuple. + /// The type of the fifth component of the tuple. + /// The type of the sixth component of the tuple. + /// The type of the seventh component of the tuple. + /// The value of the first component of the tuple. + /// The value of the second component of the tuple. + /// The value of the third component of the tuple. + /// The value of the fourth component of the tuple. + /// The value of the fifth component of the tuple. + /// The value of the sixth component of the tuple. + /// The value of the seventh component of the tuple. + /// A 7-tuple (septuple) whose value is (item1, item2, item3, item4, item5, item6, item7). + public static ValueTuple Create(T1 item1, T2 item2, T3 item3, T4 item4, T5 item5, T6 item6, T7 item7) => + new ValueTuple(item1, item2, item3, item4, item5, item6, item7); + + /// Creates a new struct 8-tuple, or octuple. + /// The type of the first component of the tuple. + /// The type of the second component of the tuple. + /// The type of the third component of the tuple. + /// The type of the fourth component of the tuple. + /// The type of the fifth component of the tuple. + /// The type of the sixth component of the tuple. + /// The type of the seventh component of the tuple. + /// The type of the eighth component of the tuple. + /// The value of the first component of the tuple. + /// The value of the second component of the tuple. + /// The value of the third component of the tuple. + /// The value of the fourth component of the tuple. + /// The value of the fifth component of the tuple. + /// The value of the sixth component of the tuple. + /// The value of the seventh component of the tuple. + /// The value of the eighth component of the tuple. + /// An 8-tuple (octuple) whose value is (item1, item2, item3, item4, item5, item6, item7, item8). + public static ValueTuple> Create(T1 item1, T2 item2, T3 item3, T4 item4, T5 item5, T6 item6, T7 item7, T8 item8) => + new ValueTuple>(item1, item2, item3, item4, item5, item6, item7, ValueTuple.Create(item8)); + + internal static int CombineHashCodes(int h1, int h2) + { + return HashHelpers.Combine(HashHelpers.Combine(HashHelpers.RandomSeed, h1), h2); + } + + internal static int CombineHashCodes(int h1, int h2, int h3) + { + return HashHelpers.Combine(CombineHashCodes(h1, h2), h3); + } + + internal static int CombineHashCodes(int h1, int h2, int h3, int h4) + { + return HashHelpers.Combine(CombineHashCodes(h1, h2, h3), h4); + } + + internal static int CombineHashCodes(int h1, int h2, int h3, int h4, int h5) + { + return HashHelpers.Combine(CombineHashCodes(h1, h2, h3, h4), h5); + } + + internal static int CombineHashCodes(int h1, int h2, int h3, int h4, int h5, int h6) + { + return HashHelpers.Combine(CombineHashCodes(h1, h2, h3, h4, h5), h6); + } + + internal static int CombineHashCodes(int h1, int h2, int h3, int h4, int h5, int h6, int h7) + { + return HashHelpers.Combine(CombineHashCodes(h1, h2, h3, h4, h5, h6), h7); + } + + internal static int CombineHashCodes(int h1, int h2, int h3, int h4, int h5, int h6, int h7, int h8) + { + return HashHelpers.Combine(CombineHashCodes(h1, h2, h3, h4, h5, h6, h7), h8); + } + } + + /// Represents a 1-tuple, or singleton, as a value type. + /// The type of the tuple's only component. + public struct ValueTuple + : IEquatable>, IStructuralEquatable, IStructuralComparable, IComparable, IComparable>, ITupleInternal + { + /// + /// The current instance's first component. + /// + public T1 Item1; + + /// + /// Initializes a new instance of the value type. + /// + /// The value of the tuple's first component. + public ValueTuple(T1 item1) + { + Item1 = item1; + } + + /// + /// Returns a value that indicates whether the current instance is equal to a specified object. + /// + /// The object to compare with this instance. + /// if the current instance is equal to the specified object; otherwise, . + /// + /// The parameter is considered to be equal to the current instance under the following conditions: + /// + /// It is a value type. + /// Its components are of the same types as those of the current instance. + /// Its components are equal to those of the current instance. Equality is determined by the default object equality comparer for each component. + /// + /// + public override bool Equals(object obj) + { + return obj is ValueTuple && Equals((ValueTuple)obj); + } + + /// + /// Returns a value that indicates whether the current + /// instance is equal to a specified . + /// + /// The tuple to compare with this instance. + /// if the current instance is equal to the specified tuple; otherwise, . + /// + /// The parameter is considered to be equal to the current instance if each of its field + /// is equal to that of the current instance, using the default comparer for that field's type. + /// + public bool Equals(ValueTuple other) + { + return EqualityComparer.Default.Equals(Item1, other.Item1); + } + + bool IStructuralEquatable.Equals(object other, IEqualityComparer comparer) + { + if (other == null || !(other is ValueTuple)) return false; + + var objTuple = (ValueTuple)other; + + return comparer.Equals(Item1, objTuple.Item1); + } + + int IComparable.CompareTo(object other) + { + if (other == null) return 1; + + if (!(other is ValueTuple)) + { + throw new ArgumentException(SR.ArgumentException_ValueTupleIncorrectType, nameof(other)); + } + + var objTuple = (ValueTuple)other; + + return Comparer.Default.Compare(Item1, objTuple.Item1); + } + + /// Compares this instance to a specified instance and returns an indication of their relative values. + /// An instance to compare. + /// + /// A signed number indicating the relative values of this instance and . + /// Returns less than zero if this instance is less than , zero if this + /// instance is equal to , and greater than zero if this instance is greater + /// than . + /// + public int CompareTo(ValueTuple other) + { + return Comparer.Default.Compare(Item1, other.Item1); + } + + int IStructuralComparable.CompareTo(object other, IComparer comparer) + { + if (other == null) return 1; + + if (!(other is ValueTuple)) + { + throw new ArgumentException(SR.ArgumentException_ValueTupleIncorrectType, nameof(other)); + } + + var objTuple = (ValueTuple)other; + + return comparer.Compare(Item1, objTuple.Item1); + } + + /// + /// Returns the hash code for the current instance. + /// + /// A 32-bit signed integer hash code. + public override int GetHashCode() + { + return EqualityComparer.Default.GetHashCode(Item1); + } + + int IStructuralEquatable.GetHashCode(IEqualityComparer comparer) + { + return comparer.GetHashCode(Item1); + } + + int ITupleInternal.GetHashCode(IEqualityComparer comparer) + { + return comparer.GetHashCode(Item1); + } + + /// + /// Returns a string that represents the value of this instance. + /// + /// The string representation of this instance. + /// + /// The string returned by this method takes the form (Item1), + /// where Item1 represents the value of . If the field is , + /// it is represented as . + /// + public override string ToString() + { + return "(" + Item1?.ToString() + ")"; + } + + string ITupleInternal.ToStringEnd() + { + return Item1?.ToString() + ")"; + } + + int ITupleInternal.Size => 1; + } + + /// + /// Represents a 2-tuple, or pair, as a value type. + /// + /// The type of the tuple's first component. + /// The type of the tuple's second component. + public struct ValueTuple + : IEquatable>, IStructuralEquatable, IStructuralComparable, IComparable, IComparable>, ITupleInternal + { + /// + /// The current instance's first component. + /// + public T1 Item1; + + /// + /// The current instance's first component. + /// + public T2 Item2; + + /// + /// Initializes a new instance of the value type. + /// + /// The value of the tuple's first component. + /// The value of the tuple's second component. + public ValueTuple(T1 item1, T2 item2) + { + Item1 = item1; + Item2 = item2; + } + + /// + /// Returns a value that indicates whether the current instance is equal to a specified object. + /// + /// The object to compare with this instance. + /// if the current instance is equal to the specified object; otherwise, . + /// + /// + /// The parameter is considered to be equal to the current instance under the following conditions: + /// + /// It is a value type. + /// Its components are of the same types as those of the current instance. + /// Its components are equal to those of the current instance. Equality is determined by the default object equality comparer for each component. + /// + /// + public override bool Equals(object obj) + { + return obj is ValueTuple && Equals((ValueTuple)obj); + } + + /// + /// Returns a value that indicates whether the current instance is equal to a specified . + /// + /// The tuple to compare with this instance. + /// if the current instance is equal to the specified tuple; otherwise, . + /// + /// The parameter is considered to be equal to the current instance if each of its fields + /// are equal to that of the current instance, using the default comparer for that field's type. + /// + public bool Equals(ValueTuple other) + { + return EqualityComparer.Default.Equals(Item1, other.Item1) + && EqualityComparer.Default.Equals(Item2, other.Item2); + } + + /// + /// Returns a value that indicates whether the current instance is equal to a specified object based on a specified comparison method. + /// + /// The object to compare with this instance. + /// An object that defines the method to use to evaluate whether the two objects are equal. + /// if the current instance is equal to the specified object; otherwise, . + /// + /// + /// This member is an explicit interface member implementation. It can be used only when the + /// instance is cast to an interface. + /// + /// The implementation is called only if other is not , + /// and if it can be successfully cast (in C#) or converted (in Visual Basic) to a + /// whose components are of the same types as those of the current instance. The IStructuralEquatable.Equals(Object, IEqualityComparer) method + /// first passes the values of the objects to be compared to the + /// implementation. If this method call returns , the method is + /// called again and passed the values of the two instances. + /// + bool IStructuralEquatable.Equals(object other, IEqualityComparer comparer) + { + if (other == null || !(other is ValueTuple)) return false; + + var objTuple = (ValueTuple)other; + + return comparer.Equals(Item1, objTuple.Item1) + && comparer.Equals(Item2, objTuple.Item2); + } + + int IComparable.CompareTo(object other) + { + if (other == null) return 1; + + if (!(other is ValueTuple)) + { + throw new ArgumentException(SR.ArgumentException_ValueTupleIncorrectType, nameof(other)); + } + + return CompareTo((ValueTuple)other); + } + + /// Compares this instance to a specified instance and returns an indication of their relative values. + /// An instance to compare. + /// + /// A signed number indicating the relative values of this instance and . + /// Returns less than zero if this instance is less than , zero if this + /// instance is equal to , and greater than zero if this instance is greater + /// than . + /// + public int CompareTo(ValueTuple other) + { + int c = Comparer.Default.Compare(Item1, other.Item1); + if (c != 0) return c; + + return Comparer.Default.Compare(Item2, other.Item2); + } + + int IStructuralComparable.CompareTo(object other, IComparer comparer) + { + if (other == null) return 1; + + if (!(other is ValueTuple)) + { + throw new ArgumentException(SR.ArgumentException_ValueTupleIncorrectType, nameof(other)); + } + + var objTuple = (ValueTuple)other; + + int c = comparer.Compare(Item1, objTuple.Item1); + if (c != 0) return c; + + return comparer.Compare(Item2, objTuple.Item2); + } + + /// + /// Returns the hash code for the current instance. + /// + /// A 32-bit signed integer hash code. + public override int GetHashCode() + { + return ValueTuple.CombineHashCodes(EqualityComparer.Default.GetHashCode(Item1), + EqualityComparer.Default.GetHashCode(Item2)); + } + + int IStructuralEquatable.GetHashCode(IEqualityComparer comparer) + { + return GetHashCodeCore(comparer); + } + + private int GetHashCodeCore(IEqualityComparer comparer) + { + return ValueTuple.CombineHashCodes(comparer.GetHashCode(Item1), + comparer.GetHashCode(Item2)); + } + + int ITupleInternal.GetHashCode(IEqualityComparer comparer) + { + return GetHashCodeCore(comparer); + } + + /// + /// Returns a string that represents the value of this instance. + /// + /// The string representation of this instance. + /// + /// The string returned by this method takes the form (Item1, Item2), + /// where Item1 and Item2 represent the values of the + /// and fields. If either field value is , + /// it is represented as . + /// + public override string ToString() + { + return "(" + Item1?.ToString() + ", " + Item2?.ToString() + ")"; + } + + string ITupleInternal.ToStringEnd() + { + return Item1?.ToString() + ", " + Item2?.ToString() + ")"; + } + + int ITupleInternal.Size => 2; + } + + /// + /// Represents a 3-tuple, or triple, as a value type. + /// + /// The type of the tuple's first component. + /// The type of the tuple's second component. + /// The type of the tuple's third component. + public struct ValueTuple + : IEquatable>, IStructuralEquatable, IStructuralComparable, IComparable, IComparable>, ITupleInternal + { + /// + /// The current instance's first component. + /// + public T1 Item1; + /// + /// The current instance's second component. + /// + public T2 Item2; + /// + /// The current instance's third component. + /// + public T3 Item3; + + /// + /// Initializes a new instance of the value type. + /// + /// The value of the tuple's first component. + /// The value of the tuple's second component. + /// The value of the tuple's third component. + public ValueTuple(T1 item1, T2 item2, T3 item3) + { + Item1 = item1; + Item2 = item2; + Item3 = item3; + } + + /// + /// Returns a value that indicates whether the current instance is equal to a specified object. + /// + /// The object to compare with this instance. + /// if the current instance is equal to the specified object; otherwise, . + /// + /// The parameter is considered to be equal to the current instance under the following conditions: + /// + /// It is a value type. + /// Its components are of the same types as those of the current instance. + /// Its components are equal to those of the current instance. Equality is determined by the default object equality comparer for each component. + /// + /// + public override bool Equals(object obj) + { + return obj is ValueTuple && Equals((ValueTuple)obj); + } + + /// + /// Returns a value that indicates whether the current + /// instance is equal to a specified . + /// + /// The tuple to compare with this instance. + /// if the current instance is equal to the specified tuple; otherwise, . + /// + /// The parameter is considered to be equal to the current instance if each of its fields + /// are equal to that of the current instance, using the default comparer for that field's type. + /// + public bool Equals(ValueTuple other) + { + return EqualityComparer.Default.Equals(Item1, other.Item1) + && EqualityComparer.Default.Equals(Item2, other.Item2) + && EqualityComparer.Default.Equals(Item3, other.Item3); + } + + bool IStructuralEquatable.Equals(object other, IEqualityComparer comparer) + { + if (other == null || !(other is ValueTuple)) return false; + + var objTuple = (ValueTuple)other; + + return comparer.Equals(Item1, objTuple.Item1) + && comparer.Equals(Item2, objTuple.Item2) + && comparer.Equals(Item3, objTuple.Item3); + } + + int IComparable.CompareTo(object other) + { + if (other == null) return 1; + + if (!(other is ValueTuple)) + { + throw new ArgumentException(SR.ArgumentException_ValueTupleIncorrectType, nameof(other)); + } + + return CompareTo((ValueTuple)other); + } + + /// Compares this instance to a specified instance and returns an indication of their relative values. + /// An instance to compare. + /// + /// A signed number indicating the relative values of this instance and . + /// Returns less than zero if this instance is less than , zero if this + /// instance is equal to , and greater than zero if this instance is greater + /// than . + /// + public int CompareTo(ValueTuple other) + { + int c = Comparer.Default.Compare(Item1, other.Item1); + if (c != 0) return c; + + c = Comparer.Default.Compare(Item2, other.Item2); + if (c != 0) return c; + + return Comparer.Default.Compare(Item3, other.Item3); + } + + int IStructuralComparable.CompareTo(object other, IComparer comparer) + { + if (other == null) return 1; + + if (!(other is ValueTuple)) + { + throw new ArgumentException(SR.ArgumentException_ValueTupleIncorrectType, nameof(other)); + } + + var objTuple = (ValueTuple)other; + + int c = comparer.Compare(Item1, objTuple.Item1); + if (c != 0) return c; + + c = comparer.Compare(Item2, objTuple.Item2); + if (c != 0) return c; + + return comparer.Compare(Item3, objTuple.Item3); + } + + /// + /// Returns the hash code for the current instance. + /// + /// A 32-bit signed integer hash code. + public override int GetHashCode() + { + return ValueTuple.CombineHashCodes(EqualityComparer.Default.GetHashCode(Item1), + EqualityComparer.Default.GetHashCode(Item2), + EqualityComparer.Default.GetHashCode(Item3)); + } + + int IStructuralEquatable.GetHashCode(IEqualityComparer comparer) + { + return GetHashCodeCore(comparer); + } + + private int GetHashCodeCore(IEqualityComparer comparer) + { + return ValueTuple.CombineHashCodes(comparer.GetHashCode(Item1), + comparer.GetHashCode(Item2), + comparer.GetHashCode(Item3)); + } + + int ITupleInternal.GetHashCode(IEqualityComparer comparer) + { + return GetHashCodeCore(comparer); + } + + /// + /// Returns a string that represents the value of this instance. + /// + /// The string representation of this instance. + /// + /// The string returned by this method takes the form (Item1, Item2, Item3). + /// If any field value is , it is represented as . + /// + public override string ToString() + { + return "(" + Item1?.ToString() + ", " + Item2?.ToString() + ", " + Item3?.ToString() + ")"; + } + + string ITupleInternal.ToStringEnd() + { + return Item1?.ToString() + ", " + Item2?.ToString() + ", " + Item3?.ToString() + ")"; + } + + int ITupleInternal.Size => 3; + } + + /// + /// Represents a 4-tuple, or quadruple, as a value type. + /// + /// The type of the tuple's first component. + /// The type of the tuple's second component. + /// The type of the tuple's third component. + /// The type of the tuple's fourth component. + public struct ValueTuple + : IEquatable>, IStructuralEquatable, IStructuralComparable, IComparable, IComparable>, ITupleInternal + { + /// + /// The current instance's first component. + /// + public T1 Item1; + /// + /// The current instance's second component. + /// + public T2 Item2; + /// + /// The current instance's third component. + /// + public T3 Item3; + /// + /// The current instance's fourth component. + /// + public T4 Item4; + + /// + /// Initializes a new instance of the value type. + /// + /// The value of the tuple's first component. + /// The value of the tuple's second component. + /// The value of the tuple's third component. + /// The value of the tuple's fourth component. + public ValueTuple(T1 item1, T2 item2, T3 item3, T4 item4) + { + Item1 = item1; + Item2 = item2; + Item3 = item3; + Item4 = item4; + } + + /// + /// Returns a value that indicates whether the current instance is equal to a specified object. + /// + /// The object to compare with this instance. + /// if the current instance is equal to the specified object; otherwise, . + /// + /// The parameter is considered to be equal to the current instance under the following conditions: + /// + /// It is a value type. + /// Its components are of the same types as those of the current instance. + /// Its components are equal to those of the current instance. Equality is determined by the default object equality comparer for each component. + /// + /// + public override bool Equals(object obj) + { + return obj is ValueTuple && Equals((ValueTuple)obj); + } + + /// + /// Returns a value that indicates whether the current + /// instance is equal to a specified . + /// + /// The tuple to compare with this instance. + /// if the current instance is equal to the specified tuple; otherwise, . + /// + /// The parameter is considered to be equal to the current instance if each of its fields + /// are equal to that of the current instance, using the default comparer for that field's type. + /// + public bool Equals(ValueTuple other) + { + return EqualityComparer.Default.Equals(Item1, other.Item1) + && EqualityComparer.Default.Equals(Item2, other.Item2) + && EqualityComparer.Default.Equals(Item3, other.Item3) + && EqualityComparer.Default.Equals(Item4, other.Item4); + } + + bool IStructuralEquatable.Equals(object other, IEqualityComparer comparer) + { + if (other == null || !(other is ValueTuple)) return false; + + var objTuple = (ValueTuple)other; + + return comparer.Equals(Item1, objTuple.Item1) + && comparer.Equals(Item2, objTuple.Item2) + && comparer.Equals(Item3, objTuple.Item3) + && comparer.Equals(Item4, objTuple.Item4); + } + + int IComparable.CompareTo(object other) + { + if (other == null) return 1; + + if (!(other is ValueTuple)) + { + throw new ArgumentException(SR.ArgumentException_ValueTupleIncorrectType, nameof(other)); + } + + return CompareTo((ValueTuple)other); + } + + /// Compares this instance to a specified instance and returns an indication of their relative values. + /// An instance to compare. + /// + /// A signed number indicating the relative values of this instance and . + /// Returns less than zero if this instance is less than , zero if this + /// instance is equal to , and greater than zero if this instance is greater + /// than . + /// + public int CompareTo(ValueTuple other) + { + int c = Comparer.Default.Compare(Item1, other.Item1); + if (c != 0) return c; + + c = Comparer.Default.Compare(Item2, other.Item2); + if (c != 0) return c; + + c = Comparer.Default.Compare(Item3, other.Item3); + if (c != 0) return c; + + return Comparer.Default.Compare(Item4, other.Item4); + } + + int IStructuralComparable.CompareTo(object other, IComparer comparer) + { + if (other == null) return 1; + + if (!(other is ValueTuple)) + { + throw new ArgumentException(SR.ArgumentException_ValueTupleIncorrectType, nameof(other)); + } + + var objTuple = (ValueTuple)other; + + int c = comparer.Compare(Item1, objTuple.Item1); + if (c != 0) return c; + + c = comparer.Compare(Item2, objTuple.Item2); + if (c != 0) return c; + + c = comparer.Compare(Item3, objTuple.Item3); + if (c != 0) return c; + + return comparer.Compare(Item4, objTuple.Item4); + } + + /// + /// Returns the hash code for the current instance. + /// + /// A 32-bit signed integer hash code. + public override int GetHashCode() + { + return ValueTuple.CombineHashCodes(EqualityComparer.Default.GetHashCode(Item1), + EqualityComparer.Default.GetHashCode(Item2), + EqualityComparer.Default.GetHashCode(Item3), + EqualityComparer.Default.GetHashCode(Item4)); + } + + int IStructuralEquatable.GetHashCode(IEqualityComparer comparer) + { + return GetHashCodeCore(comparer); + } + + private int GetHashCodeCore(IEqualityComparer comparer) + { + return ValueTuple.CombineHashCodes(comparer.GetHashCode(Item1), + comparer.GetHashCode(Item2), + comparer.GetHashCode(Item3), + comparer.GetHashCode(Item4)); + } + + int ITupleInternal.GetHashCode(IEqualityComparer comparer) + { + return GetHashCodeCore(comparer); + } + + /// + /// Returns a string that represents the value of this instance. + /// + /// The string representation of this instance. + /// + /// The string returned by this method takes the form (Item1, Item2, Item3, Item4). + /// If any field value is , it is represented as . + /// + public override string ToString() + { + return "(" + Item1?.ToString() + ", " + Item2?.ToString() + ", " + Item3?.ToString() + ", " + Item4?.ToString() + ")"; + } + + string ITupleInternal.ToStringEnd() + { + return Item1?.ToString() + ", " + Item2?.ToString() + ", " + Item3?.ToString() + ", " + Item4?.ToString() + ")"; + } + + int ITupleInternal.Size => 4; + } + + /// + /// Represents a 5-tuple, or quintuple, as a value type. + /// + /// The type of the tuple's first component. + /// The type of the tuple's second component. + /// The type of the tuple's third component. + /// The type of the tuple's fourth component. + /// The type of the tuple's fifth component. + public struct ValueTuple + : IEquatable>, IStructuralEquatable, IStructuralComparable, IComparable, IComparable>, ITupleInternal + { + /// + /// The current instance's first component. + /// + public T1 Item1; + /// + /// The current instance's second component. + /// + public T2 Item2; + /// + /// The current instance's third component. + /// + public T3 Item3; + /// + /// The current instance's fourth component. + /// + public T4 Item4; + /// + /// The current instance's fifth component. + /// + public T5 Item5; + + /// + /// Initializes a new instance of the value type. + /// + /// The value of the tuple's first component. + /// The value of the tuple's second component. + /// The value of the tuple's third component. + /// The value of the tuple's fourth component. + /// The value of the tuple's fifth component. + public ValueTuple(T1 item1, T2 item2, T3 item3, T4 item4, T5 item5) + { + Item1 = item1; + Item2 = item2; + Item3 = item3; + Item4 = item4; + Item5 = item5; + } + + /// + /// Returns a value that indicates whether the current instance is equal to a specified object. + /// + /// The object to compare with this instance. + /// if the current instance is equal to the specified object; otherwise, . + /// + /// The parameter is considered to be equal to the current instance under the following conditions: + /// + /// It is a value type. + /// Its components are of the same types as those of the current instance. + /// Its components are equal to those of the current instance. Equality is determined by the default object equality comparer for each component. + /// + /// + public override bool Equals(object obj) + { + return obj is ValueTuple && Equals((ValueTuple)obj); + } + + /// + /// Returns a value that indicates whether the current + /// instance is equal to a specified . + /// + /// The tuple to compare with this instance. + /// if the current instance is equal to the specified tuple; otherwise, . + /// + /// The parameter is considered to be equal to the current instance if each of its fields + /// are equal to that of the current instance, using the default comparer for that field's type. + /// + public bool Equals(ValueTuple other) + { + return EqualityComparer.Default.Equals(Item1, other.Item1) + && EqualityComparer.Default.Equals(Item2, other.Item2) + && EqualityComparer.Default.Equals(Item3, other.Item3) + && EqualityComparer.Default.Equals(Item4, other.Item4) + && EqualityComparer.Default.Equals(Item5, other.Item5); + } + + bool IStructuralEquatable.Equals(object other, IEqualityComparer comparer) + { + if (other == null || !(other is ValueTuple)) return false; + + var objTuple = (ValueTuple)other; + + return comparer.Equals(Item1, objTuple.Item1) + && comparer.Equals(Item2, objTuple.Item2) + && comparer.Equals(Item3, objTuple.Item3) + && comparer.Equals(Item4, objTuple.Item4) + && comparer.Equals(Item5, objTuple.Item5); + } + + int IComparable.CompareTo(object other) + { + if (other == null) return 1; + + if (!(other is ValueTuple)) + { + throw new ArgumentException(SR.ArgumentException_ValueTupleIncorrectType, nameof(other)); + } + + return CompareTo((ValueTuple)other); + } + + /// Compares this instance to a specified instance and returns an indication of their relative values. + /// An instance to compare. + /// + /// A signed number indicating the relative values of this instance and . + /// Returns less than zero if this instance is less than , zero if this + /// instance is equal to , and greater than zero if this instance is greater + /// than . + /// + public int CompareTo(ValueTuple other) + { + int c = Comparer.Default.Compare(Item1, other.Item1); + if (c != 0) return c; + + c = Comparer.Default.Compare(Item2, other.Item2); + if (c != 0) return c; + + c = Comparer.Default.Compare(Item3, other.Item3); + if (c != 0) return c; + + c = Comparer.Default.Compare(Item4, other.Item4); + if (c != 0) return c; + + return Comparer.Default.Compare(Item5, other.Item5); + } + + int IStructuralComparable.CompareTo(object other, IComparer comparer) + { + if (other == null) return 1; + + if (!(other is ValueTuple)) + { + throw new ArgumentException(SR.ArgumentException_ValueTupleIncorrectType, nameof(other)); + } + + var objTuple = (ValueTuple)other; + + int c = comparer.Compare(Item1, objTuple.Item1); + if (c != 0) return c; + + c = comparer.Compare(Item2, objTuple.Item2); + if (c != 0) return c; + + c = comparer.Compare(Item3, objTuple.Item3); + if (c != 0) return c; + + c = comparer.Compare(Item4, objTuple.Item4); + if (c != 0) return c; + + return comparer.Compare(Item5, objTuple.Item5); + } + + /// + /// Returns the hash code for the current instance. + /// + /// A 32-bit signed integer hash code. + public override int GetHashCode() + { + return ValueTuple.CombineHashCodes(EqualityComparer.Default.GetHashCode(Item1), + EqualityComparer.Default.GetHashCode(Item2), + EqualityComparer.Default.GetHashCode(Item3), + EqualityComparer.Default.GetHashCode(Item4), + EqualityComparer.Default.GetHashCode(Item5)); + } + + int IStructuralEquatable.GetHashCode(IEqualityComparer comparer) + { + return GetHashCodeCore(comparer); + } + + private int GetHashCodeCore(IEqualityComparer comparer) + { + return ValueTuple.CombineHashCodes(comparer.GetHashCode(Item1), + comparer.GetHashCode(Item2), + comparer.GetHashCode(Item3), + comparer.GetHashCode(Item4), + comparer.GetHashCode(Item5)); + } + + int ITupleInternal.GetHashCode(IEqualityComparer comparer) + { + return GetHashCodeCore(comparer); + } + + /// + /// Returns a string that represents the value of this instance. + /// + /// The string representation of this instance. + /// + /// The string returned by this method takes the form (Item1, Item2, Item3, Item4, Item5). + /// If any field value is , it is represented as . + /// + public override string ToString() + { + return "(" + Item1?.ToString() + ", " + Item2?.ToString() + ", " + Item3?.ToString() + ", " + Item4?.ToString() + ", " + Item5?.ToString() + ")"; + } + + string ITupleInternal.ToStringEnd() + { + return Item1?.ToString() + ", " + Item2?.ToString() + ", " + Item3?.ToString() + ", " + Item4?.ToString() + ", " + Item5?.ToString() + ")"; + } + + int ITupleInternal.Size => 5; + } + + /// + /// Represents a 6-tuple, or sixtuple, as a value type. + /// + /// The type of the tuple's first component. + /// The type of the tuple's second component. + /// The type of the tuple's third component. + /// The type of the tuple's fourth component. + /// The type of the tuple's fifth component. + /// The type of the tuple's sixth component. + public struct ValueTuple + : IEquatable>, IStructuralEquatable, IStructuralComparable, IComparable, IComparable>, ITupleInternal + { + /// + /// The current instance's first component. + /// + public T1 Item1; + /// + /// The current instance's second component. + /// + public T2 Item2; + /// + /// The current instance's third component. + /// + public T3 Item3; + /// + /// The current instance's fourth component. + /// + public T4 Item4; + /// + /// The current instance's fifth component. + /// + public T5 Item5; + /// + /// The current instance's sixth component. + /// + public T6 Item6; + + /// + /// Initializes a new instance of the value type. + /// + /// The value of the tuple's first component. + /// The value of the tuple's second component. + /// The value of the tuple's third component. + /// The value of the tuple's fourth component. + /// The value of the tuple's fifth component. + /// The value of the tuple's sixth component. + public ValueTuple(T1 item1, T2 item2, T3 item3, T4 item4, T5 item5, T6 item6) + { + Item1 = item1; + Item2 = item2; + Item3 = item3; + Item4 = item4; + Item5 = item5; + Item6 = item6; + } + + /// + /// Returns a value that indicates whether the current instance is equal to a specified object. + /// + /// The object to compare with this instance. + /// if the current instance is equal to the specified object; otherwise, . + /// + /// The parameter is considered to be equal to the current instance under the following conditions: + /// + /// It is a value type. + /// Its components are of the same types as those of the current instance. + /// Its components are equal to those of the current instance. Equality is determined by the default object equality comparer for each component. + /// + /// + public override bool Equals(object obj) + { + return obj is ValueTuple && Equals((ValueTuple)obj); + } + + /// + /// Returns a value that indicates whether the current + /// instance is equal to a specified . + /// + /// The tuple to compare with this instance. + /// if the current instance is equal to the specified tuple; otherwise, . + /// + /// The parameter is considered to be equal to the current instance if each of its fields + /// are equal to that of the current instance, using the default comparer for that field's type. + /// + public bool Equals(ValueTuple other) + { + return EqualityComparer.Default.Equals(Item1, other.Item1) + && EqualityComparer.Default.Equals(Item2, other.Item2) + && EqualityComparer.Default.Equals(Item3, other.Item3) + && EqualityComparer.Default.Equals(Item4, other.Item4) + && EqualityComparer.Default.Equals(Item5, other.Item5) + && EqualityComparer.Default.Equals(Item6, other.Item6); + } + + bool IStructuralEquatable.Equals(object other, IEqualityComparer comparer) + { + if (other == null || !(other is ValueTuple)) return false; + + var objTuple = (ValueTuple)other; + + return comparer.Equals(Item1, objTuple.Item1) + && comparer.Equals(Item2, objTuple.Item2) + && comparer.Equals(Item3, objTuple.Item3) + && comparer.Equals(Item4, objTuple.Item4) + && comparer.Equals(Item5, objTuple.Item5) + && comparer.Equals(Item6, objTuple.Item6); + } + + int IComparable.CompareTo(object other) + { + if (other == null) return 1; + + if (!(other is ValueTuple)) + { + throw new ArgumentException(SR.ArgumentException_ValueTupleIncorrectType, nameof(other)); + } + + return CompareTo((ValueTuple)other); + } + + /// Compares this instance to a specified instance and returns an indication of their relative values. + /// An instance to compare. + /// + /// A signed number indicating the relative values of this instance and . + /// Returns less than zero if this instance is less than , zero if this + /// instance is equal to , and greater than zero if this instance is greater + /// than . + /// + public int CompareTo(ValueTuple other) + { + int c = Comparer.Default.Compare(Item1, other.Item1); + if (c != 0) return c; + + c = Comparer.Default.Compare(Item2, other.Item2); + if (c != 0) return c; + + c = Comparer.Default.Compare(Item3, other.Item3); + if (c != 0) return c; + + c = Comparer.Default.Compare(Item4, other.Item4); + if (c != 0) return c; + + c = Comparer.Default.Compare(Item5, other.Item5); + if (c != 0) return c; + + return Comparer.Default.Compare(Item6, other.Item6); + } + + int IStructuralComparable.CompareTo(object other, IComparer comparer) + { + if (other == null) return 1; + + if (!(other is ValueTuple)) + { + throw new ArgumentException(SR.ArgumentException_ValueTupleIncorrectType, nameof(other)); + } + + var objTuple = (ValueTuple)other; + + int c = comparer.Compare(Item1, objTuple.Item1); + if (c != 0) return c; + + c = comparer.Compare(Item2, objTuple.Item2); + if (c != 0) return c; + + c = comparer.Compare(Item3, objTuple.Item3); + if (c != 0) return c; + + c = comparer.Compare(Item4, objTuple.Item4); + if (c != 0) return c; + + c = comparer.Compare(Item5, objTuple.Item5); + if (c != 0) return c; + + return comparer.Compare(Item6, objTuple.Item6); + } + + /// + /// Returns the hash code for the current instance. + /// + /// A 32-bit signed integer hash code. + public override int GetHashCode() + { + return ValueTuple.CombineHashCodes(EqualityComparer.Default.GetHashCode(Item1), + EqualityComparer.Default.GetHashCode(Item2), + EqualityComparer.Default.GetHashCode(Item3), + EqualityComparer.Default.GetHashCode(Item4), + EqualityComparer.Default.GetHashCode(Item5), + EqualityComparer.Default.GetHashCode(Item6)); + } + + int IStructuralEquatable.GetHashCode(IEqualityComparer comparer) + { + return GetHashCodeCore(comparer); + } + + private int GetHashCodeCore(IEqualityComparer comparer) + { + return ValueTuple.CombineHashCodes(comparer.GetHashCode(Item1), + comparer.GetHashCode(Item2), + comparer.GetHashCode(Item3), + comparer.GetHashCode(Item4), + comparer.GetHashCode(Item5), + comparer.GetHashCode(Item6)); + } + + int ITupleInternal.GetHashCode(IEqualityComparer comparer) + { + return GetHashCodeCore(comparer); + } + + /// + /// Returns a string that represents the value of this instance. + /// + /// The string representation of this instance. + /// + /// The string returned by this method takes the form (Item1, Item2, Item3, Item4, Item5, Item6). + /// If any field value is , it is represented as . + /// + public override string ToString() + { + return "(" + Item1?.ToString() + ", " + Item2?.ToString() + ", " + Item3?.ToString() + ", " + Item4?.ToString() + ", " + Item5?.ToString() + ", " + Item6?.ToString() + ")"; + } + + string ITupleInternal.ToStringEnd() + { + return Item1?.ToString() + ", " + Item2?.ToString() + ", " + Item3?.ToString() + ", " + Item4?.ToString() + ", " + Item5?.ToString() + ", " + Item6?.ToString() + ")"; + } + + int ITupleInternal.Size => 6; + } + + /// + /// Represents a 7-tuple, or sentuple, as a value type. + /// + /// The type of the tuple's first component. + /// The type of the tuple's second component. + /// The type of the tuple's third component. + /// The type of the tuple's fourth component. + /// The type of the tuple's fifth component. + /// The type of the tuple's sixth component. + /// The type of the tuple's seventh component. + public struct ValueTuple + : IEquatable>, IStructuralEquatable, IStructuralComparable, IComparable, IComparable>, ITupleInternal + { + /// + /// The current instance's first component. + /// + public T1 Item1; + /// + /// The current instance's second component. + /// + public T2 Item2; + /// + /// The current instance's third component. + /// + public T3 Item3; + /// + /// The current instance's fourth component. + /// + public T4 Item4; + /// + /// The current instance's fifth component. + /// + public T5 Item5; + /// + /// The current instance's sixth component. + /// + public T6 Item6; + /// + /// The current instance's seventh component. + /// + public T7 Item7; + + /// + /// Initializes a new instance of the value type. + /// + /// The value of the tuple's first component. + /// The value of the tuple's second component. + /// The value of the tuple's third component. + /// The value of the tuple's fourth component. + /// The value of the tuple's fifth component. + /// The value of the tuple's sixth component. + /// The value of the tuple's seventh component. + public ValueTuple(T1 item1, T2 item2, T3 item3, T4 item4, T5 item5, T6 item6, T7 item7) + { + Item1 = item1; + Item2 = item2; + Item3 = item3; + Item4 = item4; + Item5 = item5; + Item6 = item6; + Item7 = item7; + } + + /// + /// Returns a value that indicates whether the current instance is equal to a specified object. + /// + /// The object to compare with this instance. + /// if the current instance is equal to the specified object; otherwise, . + /// + /// The parameter is considered to be equal to the current instance under the following conditions: + /// + /// It is a value type. + /// Its components are of the same types as those of the current instance. + /// Its components are equal to those of the current instance. Equality is determined by the default object equality comparer for each component. + /// + /// + public override bool Equals(object obj) + { + return obj is ValueTuple && Equals((ValueTuple)obj); + } + + /// + /// Returns a value that indicates whether the current + /// instance is equal to a specified . + /// + /// The tuple to compare with this instance. + /// if the current instance is equal to the specified tuple; otherwise, . + /// + /// The parameter is considered to be equal to the current instance if each of its fields + /// are equal to that of the current instance, using the default comparer for that field's type. + /// + public bool Equals(ValueTuple other) + { + return EqualityComparer.Default.Equals(Item1, other.Item1) + && EqualityComparer.Default.Equals(Item2, other.Item2) + && EqualityComparer.Default.Equals(Item3, other.Item3) + && EqualityComparer.Default.Equals(Item4, other.Item4) + && EqualityComparer.Default.Equals(Item5, other.Item5) + && EqualityComparer.Default.Equals(Item6, other.Item6) + && EqualityComparer.Default.Equals(Item7, other.Item7); + } + + bool IStructuralEquatable.Equals(object other, IEqualityComparer comparer) + { + if (other == null || !(other is ValueTuple)) return false; + + var objTuple = (ValueTuple)other; + + return comparer.Equals(Item1, objTuple.Item1) + && comparer.Equals(Item2, objTuple.Item2) + && comparer.Equals(Item3, objTuple.Item3) + && comparer.Equals(Item4, objTuple.Item4) + && comparer.Equals(Item5, objTuple.Item5) + && comparer.Equals(Item6, objTuple.Item6) + && comparer.Equals(Item7, objTuple.Item7); + } + + int IComparable.CompareTo(object other) + { + if (other == null) return 1; + + if (!(other is ValueTuple)) + { + throw new ArgumentException(SR.ArgumentException_ValueTupleIncorrectType, nameof(other)); + } + + return CompareTo((ValueTuple)other); + } + + /// Compares this instance to a specified instance and returns an indication of their relative values. + /// An instance to compare. + /// + /// A signed number indicating the relative values of this instance and . + /// Returns less than zero if this instance is less than , zero if this + /// instance is equal to , and greater than zero if this instance is greater + /// than . + /// + public int CompareTo(ValueTuple other) + { + int c = Comparer.Default.Compare(Item1, other.Item1); + if (c != 0) return c; + + c = Comparer.Default.Compare(Item2, other.Item2); + if (c != 0) return c; + + c = Comparer.Default.Compare(Item3, other.Item3); + if (c != 0) return c; + + c = Comparer.Default.Compare(Item4, other.Item4); + if (c != 0) return c; + + c = Comparer.Default.Compare(Item5, other.Item5); + if (c != 0) return c; + + c = Comparer.Default.Compare(Item6, other.Item6); + if (c != 0) return c; + + return Comparer.Default.Compare(Item7, other.Item7); + } + + int IStructuralComparable.CompareTo(object other, IComparer comparer) + { + if (other == null) return 1; + + if (!(other is ValueTuple)) + { + throw new ArgumentException(SR.ArgumentException_ValueTupleIncorrectType, nameof(other)); + } + + var objTuple = (ValueTuple)other; + + int c = comparer.Compare(Item1, objTuple.Item1); + if (c != 0) return c; + + c = comparer.Compare(Item2, objTuple.Item2); + if (c != 0) return c; + + c = comparer.Compare(Item3, objTuple.Item3); + if (c != 0) return c; + + c = comparer.Compare(Item4, objTuple.Item4); + if (c != 0) return c; + + c = comparer.Compare(Item5, objTuple.Item5); + if (c != 0) return c; + + c = comparer.Compare(Item6, objTuple.Item6); + if (c != 0) return c; + + return comparer.Compare(Item7, objTuple.Item7); + } + + /// + /// Returns the hash code for the current instance. + /// + /// A 32-bit signed integer hash code. + public override int GetHashCode() + { + return ValueTuple.CombineHashCodes(EqualityComparer.Default.GetHashCode(Item1), + EqualityComparer.Default.GetHashCode(Item2), + EqualityComparer.Default.GetHashCode(Item3), + EqualityComparer.Default.GetHashCode(Item4), + EqualityComparer.Default.GetHashCode(Item5), + EqualityComparer.Default.GetHashCode(Item6), + EqualityComparer.Default.GetHashCode(Item7)); + } + + int IStructuralEquatable.GetHashCode(IEqualityComparer comparer) + { + return GetHashCodeCore(comparer); + } + + private int GetHashCodeCore(IEqualityComparer comparer) + { + return ValueTuple.CombineHashCodes(comparer.GetHashCode(Item1), + comparer.GetHashCode(Item2), + comparer.GetHashCode(Item3), + comparer.GetHashCode(Item4), + comparer.GetHashCode(Item5), + comparer.GetHashCode(Item6), + comparer.GetHashCode(Item7)); + } + + int ITupleInternal.GetHashCode(IEqualityComparer comparer) + { + return GetHashCodeCore(comparer); + } + + /// + /// Returns a string that represents the value of this instance. + /// + /// The string representation of this instance. + /// + /// The string returned by this method takes the form (Item1, Item2, Item3, Item4, Item5, Item6, Item7). + /// If any field value is , it is represented as . + /// + public override string ToString() + { + return "(" + Item1?.ToString() + ", " + Item2?.ToString() + ", " + Item3?.ToString() + ", " + Item4?.ToString() + ", " + Item5?.ToString() + ", " + Item6?.ToString() + ", " + Item7?.ToString() + ")"; + } + + string ITupleInternal.ToStringEnd() + { + return Item1?.ToString() + ", " + Item2?.ToString() + ", " + Item3?.ToString() + ", " + Item4?.ToString() + ", " + Item5?.ToString() + ", " + Item6?.ToString() + ", " + Item7?.ToString() + ")"; + } + + int ITupleInternal.Size => 7; + } + + /// + /// Represents an 8-tuple, or octuple, as a value type. + /// + /// The type of the tuple's first component. + /// The type of the tuple's second component. + /// The type of the tuple's third component. + /// The type of the tuple's fourth component. + /// The type of the tuple's fifth component. + /// The type of the tuple's sixth component. + /// The type of the tuple's seventh component. + /// The type of the tuple's eighth component. + public struct ValueTuple + : IEquatable>, IStructuralEquatable, IStructuralComparable, IComparable, IComparable>, ITupleInternal + where TRest : struct + { + /// + /// The current instance's first component. + /// + public T1 Item1; + /// + /// The current instance's second component. + /// + public T2 Item2; + /// + /// The current instance's third component. + /// + public T3 Item3; + /// + /// The current instance's fourth component. + /// + public T4 Item4; + /// + /// The current instance's fifth component. + /// + public T5 Item5; + /// + /// The current instance's sixth component. + /// + public T6 Item6; + /// + /// The current instance's seventh component. + /// + public T7 Item7; + /// + /// The current instance's eighth component. + /// + public TRest Rest; + + /// + /// Initializes a new instance of the value type. + /// + /// The value of the tuple's first component. + /// The value of the tuple's second component. + /// The value of the tuple's third component. + /// The value of the tuple's fourth component. + /// The value of the tuple's fifth component. + /// The value of the tuple's sixth component. + /// The value of the tuple's seventh component. + /// The value of the tuple's eight component. + public ValueTuple(T1 item1, T2 item2, T3 item3, T4 item4, T5 item5, T6 item6, T7 item7, TRest rest) + { + if (!(rest is ITupleInternal)) + { + throw new ArgumentException(SR.ArgumentException_ValueTupleLastArgumentNotAValueTuple); + } + + Item1 = item1; + Item2 = item2; + Item3 = item3; + Item4 = item4; + Item5 = item5; + Item6 = item6; + Item7 = item7; + Rest = rest; + } + + /// + /// Returns a value that indicates whether the current instance is equal to a specified object. + /// + /// The object to compare with this instance. + /// if the current instance is equal to the specified object; otherwise, . + /// + /// The parameter is considered to be equal to the current instance under the following conditions: + /// + /// It is a value type. + /// Its components are of the same types as those of the current instance. + /// Its components are equal to those of the current instance. Equality is determined by the default object equality comparer for each component. + /// + /// + public override bool Equals(object obj) + { + return obj is ValueTuple && Equals((ValueTuple)obj); + } + + /// + /// Returns a value that indicates whether the current + /// instance is equal to a specified . + /// + /// The tuple to compare with this instance. + /// if the current instance is equal to the specified tuple; otherwise, . + /// + /// The parameter is considered to be equal to the current instance if each of its fields + /// are equal to that of the current instance, using the default comparer for that field's type. + /// + public bool Equals(ValueTuple other) + { + return EqualityComparer.Default.Equals(Item1, other.Item1) + && EqualityComparer.Default.Equals(Item2, other.Item2) + && EqualityComparer.Default.Equals(Item3, other.Item3) + && EqualityComparer.Default.Equals(Item4, other.Item4) + && EqualityComparer.Default.Equals(Item5, other.Item5) + && EqualityComparer.Default.Equals(Item6, other.Item6) + && EqualityComparer.Default.Equals(Item7, other.Item7) + && EqualityComparer.Default.Equals(Rest, other.Rest); + } + + bool IStructuralEquatable.Equals(object other, IEqualityComparer comparer) + { + if (other == null || !(other is ValueTuple)) return false; + + var objTuple = (ValueTuple)other; + + return comparer.Equals(Item1, objTuple.Item1) + && comparer.Equals(Item2, objTuple.Item2) + && comparer.Equals(Item3, objTuple.Item3) + && comparer.Equals(Item4, objTuple.Item4) + && comparer.Equals(Item5, objTuple.Item5) + && comparer.Equals(Item6, objTuple.Item6) + && comparer.Equals(Item7, objTuple.Item7) + && comparer.Equals(Rest, objTuple.Rest); + } + + int IComparable.CompareTo(object other) + { + if (other == null) return 1; + + if (!(other is ValueTuple)) + { + throw new ArgumentException(SR.ArgumentException_ValueTupleIncorrectType, nameof(other)); + } + + return CompareTo((ValueTuple)other); + } + + /// Compares this instance to a specified instance and returns an indication of their relative values. + /// An instance to compare. + /// + /// A signed number indicating the relative values of this instance and . + /// Returns less than zero if this instance is less than , zero if this + /// instance is equal to , and greater than zero if this instance is greater + /// than . + /// + public int CompareTo(ValueTuple other) + { + int c = Comparer.Default.Compare(Item1, other.Item1); + if (c != 0) return c; + + c = Comparer.Default.Compare(Item2, other.Item2); + if (c != 0) return c; + + c = Comparer.Default.Compare(Item3, other.Item3); + if (c != 0) return c; + + c = Comparer.Default.Compare(Item4, other.Item4); + if (c != 0) return c; + + c = Comparer.Default.Compare(Item5, other.Item5); + if (c != 0) return c; + + c = Comparer.Default.Compare(Item6, other.Item6); + if (c != 0) return c; + + c = Comparer.Default.Compare(Item7, other.Item7); + if (c != 0) return c; + + return Comparer.Default.Compare(Rest, other.Rest); + } + + int IStructuralComparable.CompareTo(object other, IComparer comparer) + { + if (other == null) return 1; + + if (!(other is ValueTuple)) + { + throw new ArgumentException(SR.ArgumentException_ValueTupleIncorrectType, nameof(other)); + } + + var objTuple = (ValueTuple)other; + + int c = comparer.Compare(Item1, objTuple.Item1); + if (c != 0) return c; + + c = comparer.Compare(Item2, objTuple.Item2); + if (c != 0) return c; + + c = comparer.Compare(Item3, objTuple.Item3); + if (c != 0) return c; + + c = comparer.Compare(Item4, objTuple.Item4); + if (c != 0) return c; + + c = comparer.Compare(Item5, objTuple.Item5); + if (c != 0) return c; + + c = comparer.Compare(Item6, objTuple.Item6); + if (c != 0) return c; + + c = comparer.Compare(Item7, objTuple.Item7); + if (c != 0) return c; + + return comparer.Compare(Rest, objTuple.Rest); + } + + /// + /// Returns the hash code for the current instance. + /// + /// A 32-bit signed integer hash code. + public override int GetHashCode() + { + // We want to have a limited hash in this case. We'll use the last 8 elements of the tuple + ITupleInternal rest = Rest as ITupleInternal; + if (rest == null) + { + return ValueTuple.CombineHashCodes(EqualityComparer.Default.GetHashCode(Item1), + EqualityComparer.Default.GetHashCode(Item2), + EqualityComparer.Default.GetHashCode(Item3), + EqualityComparer.Default.GetHashCode(Item4), + EqualityComparer.Default.GetHashCode(Item5), + EqualityComparer.Default.GetHashCode(Item6), + EqualityComparer.Default.GetHashCode(Item7)); + } + + int size = rest.Size; + if (size >= 8) { return rest.GetHashCode(); } + + // In this case, the rest member has less than 8 elements so we need to combine some our elements with the elements in rest + int k = 8 - size; + switch (k) + { + case 1: + return ValueTuple.CombineHashCodes(EqualityComparer.Default.GetHashCode(Item7), + rest.GetHashCode()); + case 2: + return ValueTuple.CombineHashCodes(EqualityComparer.Default.GetHashCode(Item6), + EqualityComparer.Default.GetHashCode(Item7), + rest.GetHashCode()); + case 3: + return ValueTuple.CombineHashCodes(EqualityComparer.Default.GetHashCode(Item5), + EqualityComparer.Default.GetHashCode(Item6), + EqualityComparer.Default.GetHashCode(Item7), + rest.GetHashCode()); + case 4: + return ValueTuple.CombineHashCodes(EqualityComparer.Default.GetHashCode(Item4), + EqualityComparer.Default.GetHashCode(Item5), + EqualityComparer.Default.GetHashCode(Item6), + EqualityComparer.Default.GetHashCode(Item7), + rest.GetHashCode()); + case 5: + return ValueTuple.CombineHashCodes(EqualityComparer.Default.GetHashCode(Item3), + EqualityComparer.Default.GetHashCode(Item4), + EqualityComparer.Default.GetHashCode(Item5), + EqualityComparer.Default.GetHashCode(Item6), + EqualityComparer.Default.GetHashCode(Item7), + rest.GetHashCode()); + case 6: + return ValueTuple.CombineHashCodes(EqualityComparer.Default.GetHashCode(Item2), + EqualityComparer.Default.GetHashCode(Item3), + EqualityComparer.Default.GetHashCode(Item4), + EqualityComparer.Default.GetHashCode(Item5), + EqualityComparer.Default.GetHashCode(Item6), + EqualityComparer.Default.GetHashCode(Item7), + rest.GetHashCode()); + case 7: + case 8: + return ValueTuple.CombineHashCodes(EqualityComparer.Default.GetHashCode(Item1), + EqualityComparer.Default.GetHashCode(Item2), + EqualityComparer.Default.GetHashCode(Item3), + EqualityComparer.Default.GetHashCode(Item4), + EqualityComparer.Default.GetHashCode(Item5), + EqualityComparer.Default.GetHashCode(Item6), + EqualityComparer.Default.GetHashCode(Item7), + rest.GetHashCode()); + } + + Debug.Assert(false, "Missed all cases for computing ValueTuple hash code"); + return -1; + } + + int IStructuralEquatable.GetHashCode(IEqualityComparer comparer) + { + return GetHashCodeCore(comparer); + } + + private int GetHashCodeCore(IEqualityComparer comparer) + { + // We want to have a limited hash in this case. We'll use the last 8 elements of the tuple + ITupleInternal rest = Rest as ITupleInternal; + if (rest == null) + { + return ValueTuple.CombineHashCodes(comparer.GetHashCode(Item1), comparer.GetHashCode(Item2), comparer.GetHashCode(Item3), + comparer.GetHashCode(Item4), comparer.GetHashCode(Item5), comparer.GetHashCode(Item6), + comparer.GetHashCode(Item7)); + } + + int size = rest.Size; + if (size >= 8) { return rest.GetHashCode(comparer); } + + // In this case, the rest member has less than 8 elements so we need to combine some our elements with the elements in rest + int k = 8 - size; + switch (k) + { + case 1: + return ValueTuple.CombineHashCodes(comparer.GetHashCode(Item7), rest.GetHashCode(comparer)); + case 2: + return ValueTuple.CombineHashCodes(comparer.GetHashCode(Item6), comparer.GetHashCode(Item7), rest.GetHashCode(comparer)); + case 3: + return ValueTuple.CombineHashCodes(comparer.GetHashCode(Item5), comparer.GetHashCode(Item6), comparer.GetHashCode(Item7), + rest.GetHashCode(comparer)); + case 4: + return ValueTuple.CombineHashCodes(comparer.GetHashCode(Item4), comparer.GetHashCode(Item5), comparer.GetHashCode(Item6), + comparer.GetHashCode(Item7), rest.GetHashCode(comparer)); + case 5: + return ValueTuple.CombineHashCodes(comparer.GetHashCode(Item3), comparer.GetHashCode(Item4), comparer.GetHashCode(Item5), + comparer.GetHashCode(Item6), comparer.GetHashCode(Item7), rest.GetHashCode(comparer)); + case 6: + return ValueTuple.CombineHashCodes(comparer.GetHashCode(Item2), comparer.GetHashCode(Item3), comparer.GetHashCode(Item4), + comparer.GetHashCode(Item5), comparer.GetHashCode(Item6), comparer.GetHashCode(Item7), + rest.GetHashCode(comparer)); + case 7: + case 8: + return ValueTuple.CombineHashCodes(comparer.GetHashCode(Item1), comparer.GetHashCode(Item2), comparer.GetHashCode(Item3), + comparer.GetHashCode(Item4), comparer.GetHashCode(Item5), comparer.GetHashCode(Item6), + comparer.GetHashCode(Item7), rest.GetHashCode(comparer)); + } + + Debug.Assert(false, "Missed all cases for computing ValueTuple hash code"); + return -1; + } + + int ITupleInternal.GetHashCode(IEqualityComparer comparer) + { + return GetHashCodeCore(comparer); + } + + /// + /// Returns a string that represents the value of this instance. + /// + /// The string representation of this instance. + /// + /// The string returned by this method takes the form (Item1, Item2, Item3, Item4, Item5, Item6, Item7, Rest). + /// If any field value is , it is represented as . + /// + public override string ToString() + { + ITupleInternal rest = Rest as ITupleInternal; + if (rest == null) + { + return "(" + Item1?.ToString() + ", " + Item2?.ToString() + ", " + Item3?.ToString() + ", " + Item4?.ToString() + ", " + Item5?.ToString() + ", " + Item6?.ToString() + ", " + Item7?.ToString() + ", " + Rest.ToString() + ")"; + } + else + { + return "(" + Item1?.ToString() + ", " + Item2?.ToString() + ", " + Item3?.ToString() + ", " + Item4?.ToString() + ", " + Item5?.ToString() + ", " + Item6?.ToString() + ", " + Item7?.ToString() + ", " + rest.ToStringEnd(); + } + } + + string ITupleInternal.ToStringEnd() + { + ITupleInternal rest = Rest as ITupleInternal; + if (rest == null) + { + return Item1?.ToString() + ", " + Item2?.ToString() + ", " + Item3?.ToString() + ", " + Item4?.ToString() + ", " + Item5?.ToString() + ", " + Item6?.ToString() + ", " + Item7?.ToString() + ", " + Rest.ToString() + ")"; + } + else + { + return Item1?.ToString() + ", " + Item2?.ToString() + ", " + Item3?.ToString() + ", " + Item4?.ToString() + ", " + Item5?.ToString() + ", " + Item6?.ToString() + ", " + Item7?.ToString() + ", " + rest.ToStringEnd(); + } + } + + int ITupleInternal.Size + { + get + { + ITupleInternal rest = Rest as ITupleInternal; + return rest == null ? 8 : 7 + rest.Size; + } + } + } +} + + + + diff --git a/tests/TestConsole/Program.cs b/tests/TestConsole/Program.cs index b59da43..b9f4e23 100644 --- a/tests/TestConsole/Program.cs +++ b/tests/TestConsole/Program.cs @@ -5,12 +5,18 @@ //using IFoxCAD.Basal; //using static TestConsole.Program; using TestConsole; +using System.Runtime.CompilerServices; +var valuetuple = (1, 2); +Console.WriteLine(valuetuple.ToString()); - - - +int[] someArray = new int[5] { 1, 2, 3, 4, 5 }; +int lastElement = someArray[^1]; // lastElement = 5 +Console.WriteLine(lastElement); +int midElement = someArray[^3]; +Console.WriteLine(midElement); +var range = someArray[1..3]; Console.ReadLine(); diff --git a/tests/TestConsole/TestConsole.csproj b/tests/TestConsole/TestConsole.csproj index 8590312..c71c3f4 100644 --- a/tests/TestConsole/TestConsole.csproj +++ b/tests/TestConsole/TestConsole.csproj @@ -8,7 +8,7 @@ - + -- Gitee From 05563ae1b017d04e5fed676ff11e1e35644f5473 Mon Sep 17 00:00:00 2001 From: vicwjb <403009819@qq.com> Date: Fri, 27 May 2022 00:06:54 +0800 Subject: [PATCH 226/675] =?UTF-8?q?=E5=A6=82=E6=9E=9C=E8=A6=81=E7=94=A8ran?= =?UTF-8?q?ge=E7=9A=84=E8=AF=AD=E6=B3=95=E6=AF=94=E5=A6=82=20a[1..3]?= =?UTF-8?q?=EF=BC=8C=E9=82=A3=E4=B9=88=E9=9C=80=E8=A6=81=E5=B0=86RuntimeHe?= =?UTF-8?q?lpers.cs=E5=A4=8D=E5=88=B6=E5=88=B0=E4=BD=A0=E7=9A=84=E9=A1=B9?= =?UTF-8?q?=E7=9B=AE=E9=87=8C=E3=80=82?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .gitignore | 2 +- tests/TestConsole/RuntimeHelpers.cs | 48 +++++++++++++++++++++++++++++ 2 files changed, 49 insertions(+), 1 deletion(-) create mode 100644 tests/TestConsole/RuntimeHelpers.cs diff --git a/.gitignore b/.gitignore index 5128a84..e910608 100644 --- a/.gitignore +++ b/.gitignore @@ -407,4 +407,4 @@ healthchecksdb .vscode #ifoxcad -tests/TestConsole/ \ No newline at end of file +#tests/TestConsole/ \ No newline at end of file diff --git a/tests/TestConsole/RuntimeHelpers.cs b/tests/TestConsole/RuntimeHelpers.cs new file mode 100644 index 0000000..36eaa8f --- /dev/null +++ b/tests/TestConsole/RuntimeHelpers.cs @@ -0,0 +1,48 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +// 如果要用range的语法比如 a[1..3],那么需要将本文件复制到你的项目里 + +namespace System.Runtime.CompilerServices +{ + internal static class RuntimeHelpers + { + /// + /// Slices the specified array using the specified range. + /// + public static T[] GetSubArray(T[] array, Range range) + { + if (array == null) + { + throw new ArgumentNullException(); + } + + (int offset, int length) = range.GetOffsetAndLength(array.Length); + + if (default(T)! != null || typeof(T[]) == array.GetType()) // TODO-NULLABLE: default(T) == null warning (https://github.com/dotnet/roslyn/issues/34757) + { + // We know the type of the array to be exactly T[]. + + if (length == 0) + { + //return Array.Empty(); + return new T[0]; + + + } + + var dest = new T[length]; + Array.Copy(array, offset, dest, 0, length); + return dest; + } + else + { + // The array is actually a U[] where U:T. + T[] dest = (T[])Array.CreateInstance(array.GetType().GetElementType()!, length); + Array.Copy(array, offset, dest, 0, length); + return dest; + } + } + } +} \ No newline at end of file -- Gitee From 485540cd6ebbb1a471dc0e550ab7c1843e15161f Mon Sep 17 00:00:00 2001 From: liuqihong <540762622@qq.com> Date: Fri, 27 May 2022 01:22:42 +0800 Subject: [PATCH 227/675] =?UTF-8?q?=E4=BF=AE=E6=94=B9ifox.cad=E9=A1=B9?= =?UTF-8?q?=E7=9B=AE=E6=94=AF=E6=8C=81=E8=87=AA=E5=B8=A6=E5=85=83=E7=BB=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/IFoxCAD.Cad/IFoxCAD.Cad.csproj | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/IFoxCAD.Cad/IFoxCAD.Cad.csproj b/src/IFoxCAD.Cad/IFoxCAD.Cad.csproj index 71129d5..a46fb56 100644 --- a/src/IFoxCAD.Cad/IFoxCAD.Cad.csproj +++ b/src/IFoxCAD.Cad/IFoxCAD.Cad.csproj @@ -68,9 +68,9 @@ - + -- Gitee From 4fd1aee8f9bdf3928e5e3da31bd04e250f5fc47e Mon Sep 17 00:00:00 2001 From: liuqihong <540762622@qq.com> Date: Fri, 27 May 2022 22:33:07 +0800 Subject: [PATCH 228/675] =?UTF-8?q?=E5=A2=9E=E5=8A=A0=E6=B3=A8=E9=87=8A?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/IFoxCAD.Basal/IFoxCAD.Basal.csproj | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/IFoxCAD.Basal/IFoxCAD.Basal.csproj b/src/IFoxCAD.Basal/IFoxCAD.Basal.csproj index 1ff2a4d..9498a74 100644 --- a/src/IFoxCAD.Basal/IFoxCAD.Basal.csproj +++ b/src/IFoxCAD.Basal/IFoxCAD.Basal.csproj @@ -28,11 +28,11 @@ - - - + + + DEBUG -- Gitee From 10ce6feb76eaafe6506d888201f0306023dbdfb7 Mon Sep 17 00:00:00 2001 From: liuqihong <540762622@qq.com> Date: Fri, 27 May 2022 22:42:41 +0800 Subject: [PATCH 229/675] =?UTF-8?q?=E5=A2=9E=E5=8A=A0=E7=9B=AE=E5=BD=95?= =?UTF-8?q?=E5=AD=98=E6=94=BECLS=E4=BB=A3=E7=A0=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/IFoxCAD.Basal/{ => CLS}/Index.cs | 0 src/IFoxCAD.Basal/{ => CLS}/Range.cs | 0 src/IFoxCAD.Basal/{ => CLS}/RuntimeHelpers.cs | 0 .../{ => CLS}/TupleElementNamesAttribute.cs | 0 src/IFoxCAD.Basal/{ => CLS}/ValueTuple.cs | 0 src/IFoxCAD.Basal/IFoxCAD.Basal.csproj | 34 +++++++++---------- 6 files changed, 17 insertions(+), 17 deletions(-) rename src/IFoxCAD.Basal/{ => CLS}/Index.cs (100%) rename src/IFoxCAD.Basal/{ => CLS}/Range.cs (100%) rename src/IFoxCAD.Basal/{ => CLS}/RuntimeHelpers.cs (100%) rename src/IFoxCAD.Basal/{ => CLS}/TupleElementNamesAttribute.cs (100%) rename src/IFoxCAD.Basal/{ => CLS}/ValueTuple.cs (100%) diff --git a/src/IFoxCAD.Basal/Index.cs b/src/IFoxCAD.Basal/CLS/Index.cs similarity index 100% rename from src/IFoxCAD.Basal/Index.cs rename to src/IFoxCAD.Basal/CLS/Index.cs diff --git a/src/IFoxCAD.Basal/Range.cs b/src/IFoxCAD.Basal/CLS/Range.cs similarity index 100% rename from src/IFoxCAD.Basal/Range.cs rename to src/IFoxCAD.Basal/CLS/Range.cs diff --git a/src/IFoxCAD.Basal/RuntimeHelpers.cs b/src/IFoxCAD.Basal/CLS/RuntimeHelpers.cs similarity index 100% rename from src/IFoxCAD.Basal/RuntimeHelpers.cs rename to src/IFoxCAD.Basal/CLS/RuntimeHelpers.cs diff --git a/src/IFoxCAD.Basal/TupleElementNamesAttribute.cs b/src/IFoxCAD.Basal/CLS/TupleElementNamesAttribute.cs similarity index 100% rename from src/IFoxCAD.Basal/TupleElementNamesAttribute.cs rename to src/IFoxCAD.Basal/CLS/TupleElementNamesAttribute.cs diff --git a/src/IFoxCAD.Basal/ValueTuple.cs b/src/IFoxCAD.Basal/CLS/ValueTuple.cs similarity index 100% rename from src/IFoxCAD.Basal/ValueTuple.cs rename to src/IFoxCAD.Basal/CLS/ValueTuple.cs diff --git a/src/IFoxCAD.Basal/IFoxCAD.Basal.csproj b/src/IFoxCAD.Basal/IFoxCAD.Basal.csproj index 9498a74..16cd86f 100644 --- a/src/IFoxCAD.Basal/IFoxCAD.Basal.csproj +++ b/src/IFoxCAD.Basal/IFoxCAD.Basal.csproj @@ -3,7 +3,7 @@ net35;net40;net45 true - enable + enable 0.3.4 InspireFunction xsfhlzh;vicwjb @@ -29,21 +29,21 @@ - - + - - - DEBUG - - - $(Configuration);ac2009 - - - $(Configuration);ac2013 - - - $(Configuration);ac2015 - + --> + + + DEBUG + + + $(Configuration);ac2009 + + + $(Configuration);ac2013 + + + $(Configuration);ac2015 + -- Gitee From ab1cd9a02e71b7c8108e2a996edfd8fd500175be Mon Sep 17 00:00:00 2001 From: liuqihong <540762622@qq.com> Date: Sun, 29 May 2022 04:34:03 +0800 Subject: [PATCH 230/675] =?UTF-8?q?=E9=A2=84=E5=A4=84=E7=90=86net35?= =?UTF-8?q?=E4=BB=A5=E5=85=8D=E9=AB=98=E7=89=88=E6=9C=AC=E8=87=AA=E5=B8=A6?= =?UTF-8?q?=E5=BA=93=E6=97=A0=E6=B3=95=E4=BD=BF=E7=94=A8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/IFoxCAD.Basal/CLS/RuntimeHelpers.cs | 6 ++++-- src/IFoxCAD.Basal/CLS/ValueTuple.cs | 4 +++- tests/TestConsole/Program.cs | 2 +- 3 files changed, 8 insertions(+), 4 deletions(-) diff --git a/src/IFoxCAD.Basal/CLS/RuntimeHelpers.cs b/src/IFoxCAD.Basal/CLS/RuntimeHelpers.cs index a70f317..671ae88 100644 --- a/src/IFoxCAD.Basal/CLS/RuntimeHelpers.cs +++ b/src/IFoxCAD.Basal/CLS/RuntimeHelpers.cs @@ -1,4 +1,5 @@ -// Licensed to the .NET Foundation under one or more agreements. +#if NET35 +// Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. @@ -43,4 +44,5 @@ public static T[] GetSubArray(T[] array, Range range) } } } -} \ No newline at end of file +} +#endif \ No newline at end of file diff --git a/src/IFoxCAD.Basal/CLS/ValueTuple.cs b/src/IFoxCAD.Basal/CLS/ValueTuple.cs index 0faa07d..4467909 100644 --- a/src/IFoxCAD.Basal/CLS/ValueTuple.cs +++ b/src/IFoxCAD.Basal/CLS/ValueTuple.cs @@ -12,6 +12,7 @@ using System.Globalization; using System.Numerics.Hashing; +#if NET35 namespace System.Collections { public interface IStructuralComparable @@ -27,7 +28,8 @@ public interface IStructuralEquatable bool Equals(object? other, IEqualityComparer comparer); int GetHashCode(IEqualityComparer comparer); } -} +} +#endif namespace System.Numerics.Hashing diff --git a/tests/TestConsole/Program.cs b/tests/TestConsole/Program.cs index b9f4e23..91a0510 100644 --- a/tests/TestConsole/Program.cs +++ b/tests/TestConsole/Program.cs @@ -7,7 +7,7 @@ using TestConsole; using System.Runtime.CompilerServices; - +/*下面是元组测试*/ var valuetuple = (1, 2); Console.WriteLine(valuetuple.ToString()); -- Gitee From 00a22790499e1874c705c7b3dad9465885e21927 Mon Sep 17 00:00:00 2001 From: vicwjb <403009819@qq.com> Date: Sun, 29 May 2022 22:30:08 +0800 Subject: [PATCH 231/675] =?UTF-8?q?=E7=9B=B4=E6=8E=A5=E9=9B=86=E6=88=90?= =?UTF-8?q?=E5=85=83=E7=BB=84=E5=92=8C=E7=B4=A2=E5=BC=95=E5=88=87=E7=89=87?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/IFoxCAD.Basal/IFoxCAD.Basal.csproj | 4 ++-- src/IFoxCAD.Cad/IFoxCAD.Cad.csproj | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/IFoxCAD.Basal/IFoxCAD.Basal.csproj b/src/IFoxCAD.Basal/IFoxCAD.Basal.csproj index 16cd86f..4105bb6 100644 --- a/src/IFoxCAD.Basal/IFoxCAD.Basal.csproj +++ b/src/IFoxCAD.Basal/IFoxCAD.Basal.csproj @@ -4,7 +4,7 @@ net35;net40;net45 true enable - 0.3.4 + 0.3.5 InspireFunction xsfhlzh;vicwjb 基于.NET的二次开发基本类库 @@ -13,7 +13,7 @@ https://gitee.com/inspirefunction/ifoxcad.git git IFoxCAD;C#;NET;Common;Basal - 优化依赖. + 直接集成元组和索引切片. true preview true diff --git a/src/IFoxCAD.Cad/IFoxCAD.Cad.csproj b/src/IFoxCAD.Cad/IFoxCAD.Cad.csproj index a46fb56..060e009 100644 --- a/src/IFoxCAD.Cad/IFoxCAD.Cad.csproj +++ b/src/IFoxCAD.Cad/IFoxCAD.Cad.csproj @@ -6,7 +6,7 @@ enable true true - 0.3.5 + 0.3.5.1 InspireFunction xsfhlzh;vicwjb 基于.NET的Cad二次开发类库 @@ -15,7 +15,7 @@ https://gitee.com/inspirefunction/ifoxcad.git git IFoxCAD;CAD;AutoCad;C#;NET - 优化依赖. + 直接集成元组和索引切片. true true preview -- Gitee From c5caa1fc373b8a590b6eee64c23e7edd206a5ffd Mon Sep 17 00:00:00 2001 From: vicwjb <403009819@qq.com> Date: Sun, 29 May 2022 23:35:00 +0800 Subject: [PATCH 232/675] =?UTF-8?q?=E4=BC=98=E5=8C=96=E4=BB=A3=E7=A0=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/IFoxCAD.Basal/CLS/Index.cs | 6 +- src/IFoxCAD.Basal/CLS/Range.cs | 8 +- src/IFoxCAD.Basal/CLS/RuntimeHelpers.cs | 7 +- .../CLS/TupleElementNamesAttribute.cs | 7 +- src/IFoxCAD.Basal/CLS/ValueTuple.cs | 163 +++++++++--------- tests/TestConsole/Program.cs | 5 +- 6 files changed, 92 insertions(+), 104 deletions(-) diff --git a/src/IFoxCAD.Basal/CLS/Index.cs b/src/IFoxCAD.Basal/CLS/Index.cs index de53ed9..391e472 100644 --- a/src/IFoxCAD.Basal/CLS/Index.cs +++ b/src/IFoxCAD.Basal/CLS/Index.cs @@ -47,10 +47,10 @@ private Index(int value) } /// Create an Index pointing at first element. - public static Index Start => new Index(0); + public static Index Start => new(0); /// Create an Index pointing at beyond last element. - public static Index End => new Index(~0); + public static Index End => new(~0); /// Create an Index from the start at the position indicated by the value. /// The index value from the start. @@ -124,7 +124,7 @@ public int GetOffset(int length) /// Indicates whether the current Index object is equal to another object of the same type. /// An object to compare with this object - public override bool Equals(object? value) => value is Index && _value == ((Index)value)._value; + public override bool Equals(object? value) => value is Index index && _value == index._value; /// Indicates whether the current Index object is equal to another Index object. /// An object to compare with this object diff --git a/src/IFoxCAD.Basal/CLS/Range.cs b/src/IFoxCAD.Basal/CLS/Range.cs index 99fc7a2..d046fe3 100644 --- a/src/IFoxCAD.Basal/CLS/Range.cs +++ b/src/IFoxCAD.Basal/CLS/Range.cs @@ -56,13 +56,13 @@ public override string ToString() } /// Create a Range object starting from start index to the end of the collection. - public static Range StartAt(Index start) => new Range(start, Index.End); + public static Range StartAt(Index start) => new(start, Index.End); /// Create a Range object starting from first element in the collection to the end Index. - public static Range EndAt(Index end) => new Range(Index.Start, end); + public static Range EndAt(Index end) => new(Index.Start, end); /// Create a Range object starting from first element to the end. - public static Range All => new Range(Index.Start, Index.End); + public static Range All => new(Index.Start, Index.End); /// Calculate the start offset and length of range object using a collection length. /// The length of the collection that the range will be used with. length has to be a positive value. @@ -74,7 +74,7 @@ public override string ToString() #if NET45 [MethodImpl(MethodImplOptions.AggressiveInlining)] #endif - [CLSCompliant(false)] + //[CLSCompliant(false)] public (int Offset, int Length) GetOffsetAndLength(int length) { int start; diff --git a/src/IFoxCAD.Basal/CLS/RuntimeHelpers.cs b/src/IFoxCAD.Basal/CLS/RuntimeHelpers.cs index 671ae88..5455f1e 100644 --- a/src/IFoxCAD.Basal/CLS/RuntimeHelpers.cs +++ b/src/IFoxCAD.Basal/CLS/RuntimeHelpers.cs @@ -10,13 +10,8 @@ internal static class RuntimeHelpers /// /// Slices the specified array using the specified range. /// - public static T[] GetSubArray(T[] array, Range range) + public static T[] GetSubArray(T[] array!!, Range range) { - if (array == null) - { - throw new ArgumentNullException(); - } - (int offset, int length) = range.GetOffsetAndLength(array.Length); if (default(T)! != null || typeof(T[]) == array.GetType()) // TODO-NULLABLE: default(T) == null warning (https://github.com/dotnet/roslyn/issues/34757) diff --git a/src/IFoxCAD.Basal/CLS/TupleElementNamesAttribute.cs b/src/IFoxCAD.Basal/CLS/TupleElementNamesAttribute.cs index 074b2e1..7fb152f 100644 --- a/src/IFoxCAD.Basal/CLS/TupleElementNamesAttribute.cs +++ b/src/IFoxCAD.Basal/CLS/TupleElementNamesAttribute.cs @@ -36,13 +36,8 @@ public sealed class TupleElementNamesAttribute : Attribute /// transformNames value of { "name1", "name2", null, null, /// null }. /// - public TupleElementNamesAttribute(string[] transformNames) + public TupleElementNamesAttribute(string[] transformNames!!) { - if (transformNames == null) - { - throw new ArgumentNullException(nameof(transformNames)); - } - _transformNames = transformNames; } diff --git a/src/IFoxCAD.Basal/CLS/ValueTuple.cs b/src/IFoxCAD.Basal/CLS/ValueTuple.cs index 4467909..012386a 100644 --- a/src/IFoxCAD.Basal/CLS/ValueTuple.cs +++ b/src/IFoxCAD.Basal/CLS/ValueTuple.cs @@ -1,7 +1,7 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. -#pragma warning disable SA1141 // explicitly not using tuple syntax in tuple implementation +//#pragma warning disable SA1141 // explicitly not using tuple syntax in tuple implementation using System.Collections; using System.Collections.Generic; @@ -19,16 +19,12 @@ public interface IStructuralComparable { int CompareTo(object? other, IComparer comparer); } -} - -namespace System.Collections -{ public interface IStructuralEquatable { bool Equals(object? other, IEqualityComparer comparer); int GetHashCode(IEqualityComparer comparer); } -} +} #endif @@ -103,7 +99,7 @@ public bool Equals(ValueTuple other) return true; } - bool IStructuralEquatable.Equals(object other, IEqualityComparer comparer) + bool IStructuralEquatable.Equals(object? other, IEqualityComparer comparer) { return other is ValueTuple; } @@ -112,7 +108,7 @@ int IComparable.CompareTo(object other) { if (other == null) return 1; - if (!(other is ValueTuple)) + if (other is not ValueTuple) { throw new ArgumentException(SR.ArgumentException_ValueTupleIncorrectType, nameof(other)); } @@ -133,11 +129,11 @@ public int CompareTo(ValueTuple other) return 0; } - int IStructuralComparable.CompareTo(object other, IComparer comparer) + int IStructuralComparable.CompareTo(object? other, IComparer comparer) { if (other == null) return 1; - if (!(other is ValueTuple)) + if (other is not ValueTuple) { throw new ArgumentException(SR.ArgumentException_ValueTupleIncorrectType, nameof(other)); } @@ -183,15 +179,13 @@ string ITupleInternal.ToStringEnd() /// Creates a new struct 0-tuple. /// A 0-tuple. - public static ValueTuple Create() => - new ValueTuple(); + public static ValueTuple Create() => new(); /// Creates a new struct 1-tuple, or singleton. /// The type of the first component of the tuple. /// The value of the first component of the tuple. /// A 1-tuple (singleton) whose value is (item1). - public static ValueTuple Create(T1 item1) => - new ValueTuple(item1); + public static ValueTuple Create(T1 item1) => new(item1); /// Creates a new struct 2-tuple, or pair. /// The type of the first component of the tuple. @@ -199,8 +193,7 @@ public static ValueTuple Create(T1 item1) => /// The value of the first component of the tuple. /// The value of the second component of the tuple. /// A 2-tuple (pair) whose value is (item1, item2). - public static ValueTuple Create(T1 item1, T2 item2) => - new ValueTuple(item1, item2); + public static ValueTuple Create(T1 item1, T2 item2) => new(item1, item2); /// Creates a new struct 3-tuple, or triple. /// The type of the first component of the tuple. @@ -211,7 +204,7 @@ public static ValueTuple Create(T1 item1, T2 item2) => /// The value of the third component of the tuple. /// A 3-tuple (triple) whose value is (item1, item2, item3). public static ValueTuple Create(T1 item1, T2 item2, T3 item3) => - new ValueTuple(item1, item2, item3); + new(item1, item2, item3); /// Creates a new struct 4-tuple, or quadruple. /// The type of the first component of the tuple. @@ -224,7 +217,7 @@ public static ValueTuple Create(T1 item1, T2 item2, T3 i /// The value of the fourth component of the tuple. /// A 4-tuple (quadruple) whose value is (item1, item2, item3, item4). public static ValueTuple Create(T1 item1, T2 item2, T3 item3, T4 item4) => - new ValueTuple(item1, item2, item3, item4); + new(item1, item2, item3, item4); /// Creates a new struct 5-tuple, or quintuple. /// The type of the first component of the tuple. @@ -239,7 +232,7 @@ public static ValueTuple Create(T1 item1, T2 ite /// The value of the fifth component of the tuple. /// A 5-tuple (quintuple) whose value is (item1, item2, item3, item4, item5). public static ValueTuple Create(T1 item1, T2 item2, T3 item3, T4 item4, T5 item5) => - new ValueTuple(item1, item2, item3, item4, item5); + new(item1, item2, item3, item4, item5); /// Creates a new struct 6-tuple, or sextuple. /// The type of the first component of the tuple. @@ -256,7 +249,7 @@ public static ValueTuple Create(T1 item1 /// The value of the sixth component of the tuple. /// A 6-tuple (sextuple) whose value is (item1, item2, item3, item4, item5, item6). public static ValueTuple Create(T1 item1, T2 item2, T3 item3, T4 item4, T5 item5, T6 item6) => - new ValueTuple(item1, item2, item3, item4, item5, item6); + new(item1, item2, item3, item4, item5, item6); /// Creates a new struct 7-tuple, or septuple. /// The type of the first component of the tuple. @@ -275,7 +268,7 @@ public static ValueTuple Create( /// The value of the seventh component of the tuple. /// A 7-tuple (septuple) whose value is (item1, item2, item3, item4, item5, item6, item7). public static ValueTuple Create(T1 item1, T2 item2, T3 item3, T4 item4, T5 item5, T6 item6, T7 item7) => - new ValueTuple(item1, item2, item3, item4, item5, item6, item7); + new(item1, item2, item3, item4, item5, item6, item7); /// Creates a new struct 8-tuple, or octuple. /// The type of the first component of the tuple. @@ -296,7 +289,7 @@ public static ValueTuple CreateThe value of the eighth component of the tuple. /// An 8-tuple (octuple) whose value is (item1, item2, item3, item4, item5, item6, item7, item8). public static ValueTuple> Create(T1 item1, T2 item2, T3 item3, T4 item4, T5 item5, T6 item6, T7 item7, T8 item8) => - new ValueTuple>(item1, item2, item3, item4, item5, item6, item7, ValueTuple.Create(item8)); + new(item1, item2, item3, item4, item5, item6, item7, ValueTuple.Create(item8)); internal static int CombineHashCodes(int h1, int h2) { @@ -368,7 +361,7 @@ public ValueTuple(T1 item1) /// public override bool Equals(object obj) { - return obj is ValueTuple && Equals((ValueTuple)obj); + return obj is ValueTuple tuple && Equals(tuple); } /// @@ -386,9 +379,9 @@ public bool Equals(ValueTuple other) return EqualityComparer.Default.Equals(Item1, other.Item1); } - bool IStructuralEquatable.Equals(object other, IEqualityComparer comparer) + bool IStructuralEquatable.Equals(object? other, IEqualityComparer comparer) { - if (other == null || !(other is ValueTuple)) return false; + if (other == null || other is not ValueTuple) return false; var objTuple = (ValueTuple)other; @@ -399,7 +392,7 @@ int IComparable.CompareTo(object other) { if (other == null) return 1; - if (!(other is ValueTuple)) + if (other is not ValueTuple) { throw new ArgumentException(SR.ArgumentException_ValueTupleIncorrectType, nameof(other)); } @@ -422,11 +415,11 @@ public int CompareTo(ValueTuple other) return Comparer.Default.Compare(Item1, other.Item1); } - int IStructuralComparable.CompareTo(object other, IComparer comparer) + int IStructuralComparable.CompareTo(object? other, IComparer comparer) { if (other == null) return 1; - if (!(other is ValueTuple)) + if (other is not ValueTuple) { throw new ArgumentException(SR.ArgumentException_ValueTupleIncorrectType, nameof(other)); } @@ -522,7 +515,7 @@ public ValueTuple(T1 item1, T2 item2) /// public override bool Equals(object obj) { - return obj is ValueTuple && Equals((ValueTuple)obj); + return obj is ValueTuple tuple && Equals(tuple); } /// @@ -558,9 +551,9 @@ public bool Equals(ValueTuple other) /// implementation. If this method call returns , the method is /// called again and passed the values of the two instances. /// - bool IStructuralEquatable.Equals(object other, IEqualityComparer comparer) + bool IStructuralEquatable.Equals(object? other, IEqualityComparer comparer) { - if (other == null || !(other is ValueTuple)) return false; + if (other is null or not ValueTuple) return false; var objTuple = (ValueTuple)other; @@ -572,7 +565,7 @@ int IComparable.CompareTo(object other) { if (other == null) return 1; - if (!(other is ValueTuple)) + if (other is not ValueTuple) { throw new ArgumentException(SR.ArgumentException_ValueTupleIncorrectType, nameof(other)); } @@ -596,11 +589,11 @@ public int CompareTo(ValueTuple other) return Comparer.Default.Compare(Item2, other.Item2); } - int IStructuralComparable.CompareTo(object other, IComparer comparer) + int IStructuralComparable.CompareTo(object? other, IComparer comparer) { if (other == null) return 1; - if (!(other is ValueTuple)) + if (other is not ValueTuple) { throw new ArgumentException(SR.ArgumentException_ValueTupleIncorrectType, nameof(other)); } @@ -712,7 +705,7 @@ public ValueTuple(T1 item1, T2 item2, T3 item3) /// public override bool Equals(object obj) { - return obj is ValueTuple && Equals((ValueTuple)obj); + return obj is ValueTuple tuple && Equals(tuple); } /// @@ -732,9 +725,9 @@ public bool Equals(ValueTuple other) && EqualityComparer.Default.Equals(Item3, other.Item3); } - bool IStructuralEquatable.Equals(object other, IEqualityComparer comparer) + bool IStructuralEquatable.Equals(object? other, IEqualityComparer comparer) { - if (other == null || !(other is ValueTuple)) return false; + if (other == null || other is not ValueTuple) return false; var objTuple = (ValueTuple)other; @@ -747,7 +740,7 @@ int IComparable.CompareTo(object other) { if (other == null) return 1; - if (!(other is ValueTuple)) + if (other is not ValueTuple) { throw new ArgumentException(SR.ArgumentException_ValueTupleIncorrectType, nameof(other)); } @@ -774,11 +767,11 @@ public int CompareTo(ValueTuple other) return Comparer.Default.Compare(Item3, other.Item3); } - int IStructuralComparable.CompareTo(object other, IComparer comparer) + int IStructuralComparable.CompareTo(object? other, IComparer comparer) { if (other == null) return 1; - if (!(other is ValueTuple)) + if (other is not ValueTuple) { throw new ArgumentException(SR.ArgumentException_ValueTupleIncorrectType, nameof(other)); } @@ -900,7 +893,7 @@ public ValueTuple(T1 item1, T2 item2, T3 item3, T4 item4) /// public override bool Equals(object obj) { - return obj is ValueTuple && Equals((ValueTuple)obj); + return obj is ValueTuple tuple && Equals(tuple); } /// @@ -921,9 +914,9 @@ public bool Equals(ValueTuple other) && EqualityComparer.Default.Equals(Item4, other.Item4); } - bool IStructuralEquatable.Equals(object other, IEqualityComparer comparer) + bool IStructuralEquatable.Equals(object? other, IEqualityComparer comparer) { - if (other == null || !(other is ValueTuple)) return false; + if (other == null || other is not ValueTuple) return false; var objTuple = (ValueTuple)other; @@ -937,7 +930,7 @@ int IComparable.CompareTo(object other) { if (other == null) return 1; - if (!(other is ValueTuple)) + if (other is not ValueTuple) { throw new ArgumentException(SR.ArgumentException_ValueTupleIncorrectType, nameof(other)); } @@ -967,11 +960,11 @@ public int CompareTo(ValueTuple other) return Comparer.Default.Compare(Item4, other.Item4); } - int IStructuralComparable.CompareTo(object other, IComparer comparer) + int IStructuralComparable.CompareTo(object? other, IComparer comparer) { if (other == null) return 1; - if (!(other is ValueTuple)) + if (other is not ValueTuple) { throw new ArgumentException(SR.ArgumentException_ValueTupleIncorrectType, nameof(other)); } @@ -1105,7 +1098,7 @@ public ValueTuple(T1 item1, T2 item2, T3 item3, T4 item4, T5 item5) /// public override bool Equals(object obj) { - return obj is ValueTuple && Equals((ValueTuple)obj); + return obj is ValueTuple tuple && Equals(tuple); } /// @@ -1127,9 +1120,9 @@ public bool Equals(ValueTuple other) && EqualityComparer.Default.Equals(Item5, other.Item5); } - bool IStructuralEquatable.Equals(object other, IEqualityComparer comparer) + bool IStructuralEquatable.Equals(object? other, IEqualityComparer comparer) { - if (other == null || !(other is ValueTuple)) return false; + if (other == null || other is not ValueTuple) return false; var objTuple = (ValueTuple)other; @@ -1144,7 +1137,7 @@ int IComparable.CompareTo(object other) { if (other == null) return 1; - if (!(other is ValueTuple)) + if (other is not ValueTuple) { throw new ArgumentException(SR.ArgumentException_ValueTupleIncorrectType, nameof(other)); } @@ -1177,11 +1170,11 @@ public int CompareTo(ValueTuple other) return Comparer.Default.Compare(Item5, other.Item5); } - int IStructuralComparable.CompareTo(object other, IComparer comparer) + int IStructuralComparable.CompareTo(object? other, IComparer comparer) { if (other == null) return 1; - if (!(other is ValueTuple)) + if (other is not ValueTuple) { throw new ArgumentException(SR.ArgumentException_ValueTupleIncorrectType, nameof(other)); } @@ -1327,7 +1320,7 @@ public ValueTuple(T1 item1, T2 item2, T3 item3, T4 item4, T5 item5, T6 item6) /// public override bool Equals(object obj) { - return obj is ValueTuple && Equals((ValueTuple)obj); + return obj is ValueTuple tuple && Equals(tuple); } /// @@ -1350,9 +1343,9 @@ public bool Equals(ValueTuple other) && EqualityComparer.Default.Equals(Item6, other.Item6); } - bool IStructuralEquatable.Equals(object other, IEqualityComparer comparer) + bool IStructuralEquatable.Equals(object? other, IEqualityComparer comparer) { - if (other == null || !(other is ValueTuple)) return false; + if (other == null || other is not ValueTuple) return false; var objTuple = (ValueTuple)other; @@ -1368,7 +1361,7 @@ int IComparable.CompareTo(object other) { if (other == null) return 1; - if (!(other is ValueTuple)) + if (other is not ValueTuple) { throw new ArgumentException(SR.ArgumentException_ValueTupleIncorrectType, nameof(other)); } @@ -1404,11 +1397,11 @@ public int CompareTo(ValueTuple other) return Comparer.Default.Compare(Item6, other.Item6); } - int IStructuralComparable.CompareTo(object other, IComparer comparer) + int IStructuralComparable.CompareTo(object? other, IComparer comparer) { if (other == null) return 1; - if (!(other is ValueTuple)) + if (other is not ValueTuple) { throw new ArgumentException(SR.ArgumentException_ValueTupleIncorrectType, nameof(other)); } @@ -1566,7 +1559,7 @@ public ValueTuple(T1 item1, T2 item2, T3 item3, T4 item4, T5 item5, T6 item6, T7 /// public override bool Equals(object obj) { - return obj is ValueTuple && Equals((ValueTuple)obj); + return obj is ValueTuple tuple && Equals(tuple); } /// @@ -1590,9 +1583,9 @@ public bool Equals(ValueTuple other) && EqualityComparer.Default.Equals(Item7, other.Item7); } - bool IStructuralEquatable.Equals(object other, IEqualityComparer comparer) + bool IStructuralEquatable.Equals(object? other, IEqualityComparer comparer) { - if (other == null || !(other is ValueTuple)) return false; + if (other == null || other is not ValueTuple) return false; var objTuple = (ValueTuple)other; @@ -1609,7 +1602,7 @@ int IComparable.CompareTo(object other) { if (other == null) return 1; - if (!(other is ValueTuple)) + if (other is not ValueTuple) { throw new ArgumentException(SR.ArgumentException_ValueTupleIncorrectType, nameof(other)); } @@ -1648,11 +1641,11 @@ public int CompareTo(ValueTuple other) return Comparer.Default.Compare(Item7, other.Item7); } - int IStructuralComparable.CompareTo(object other, IComparer comparer) + int IStructuralComparable.CompareTo(object? other, IComparer comparer) { if (other == null) return 1; - if (!(other is ValueTuple)) + if (other is not ValueTuple) { throw new ArgumentException(SR.ArgumentException_ValueTupleIncorrectType, nameof(other)); } @@ -1798,7 +1791,7 @@ public struct ValueTuple /// The value of the tuple's eight component. public ValueTuple(T1 item1, T2 item2, T3 item3, T4 item4, T5 item5, T6 item6, T7 item7, TRest rest) { - if (!(rest is ITupleInternal)) + if (rest is not ITupleInternal) { throw new ArgumentException(SR.ArgumentException_ValueTupleLastArgumentNotAValueTuple); } @@ -1828,7 +1821,7 @@ public ValueTuple(T1 item1, T2 item2, T3 item3, T4 item4, T5 item5, T6 item6, T7 /// public override bool Equals(object obj) { - return obj is ValueTuple && Equals((ValueTuple)obj); + return obj is ValueTuple tuple && Equals(tuple); } /// @@ -1853,9 +1846,9 @@ public bool Equals(ValueTuple other) && EqualityComparer.Default.Equals(Rest, other.Rest); } - bool IStructuralEquatable.Equals(object other, IEqualityComparer comparer) + bool IStructuralEquatable.Equals(object? other, IEqualityComparer comparer) { - if (other == null || !(other is ValueTuple)) return false; + if (other == null || other is not ValueTuple) return false; var objTuple = (ValueTuple)other; @@ -1873,7 +1866,7 @@ int IComparable.CompareTo(object other) { if (other == null) return 1; - if (!(other is ValueTuple)) + if (other is not ValueTuple) { throw new ArgumentException(SR.ArgumentException_ValueTupleIncorrectType, nameof(other)); } @@ -1915,11 +1908,11 @@ public int CompareTo(ValueTuple other) return Comparer.Default.Compare(Rest, other.Rest); } - int IStructuralComparable.CompareTo(object other, IComparer comparer) + int IStructuralComparable.CompareTo(object? other, IComparer comparer) { if (other == null) return 1; - if (!(other is ValueTuple)) + if (other is not ValueTuple) { throw new ArgumentException(SR.ArgumentException_ValueTupleIncorrectType, nameof(other)); } @@ -1957,8 +1950,7 @@ int IStructuralComparable.CompareTo(object other, IComparer comparer) public override int GetHashCode() { // We want to have a limited hash in this case. We'll use the last 8 elements of the tuple - ITupleInternal rest = Rest as ITupleInternal; - if (rest == null) + if (Rest is not ITupleInternal rest) { return ValueTuple.CombineHashCodes(EqualityComparer.Default.GetHashCode(Item1), EqualityComparer.Default.GetHashCode(Item2), @@ -2033,12 +2025,16 @@ int IStructuralEquatable.GetHashCode(IEqualityComparer comparer) private int GetHashCodeCore(IEqualityComparer comparer) { // We want to have a limited hash in this case. We'll use the last 8 elements of the tuple - ITupleInternal rest = Rest as ITupleInternal; - if (rest == null) + if (Rest is not ITupleInternal rest) { - return ValueTuple.CombineHashCodes(comparer.GetHashCode(Item1), comparer.GetHashCode(Item2), comparer.GetHashCode(Item3), - comparer.GetHashCode(Item4), comparer.GetHashCode(Item5), comparer.GetHashCode(Item6), - comparer.GetHashCode(Item7)); + return ValueTuple.CombineHashCodes( + comparer.GetHashCode(Item1), + comparer.GetHashCode(Item2), + comparer.GetHashCode(Item3), + comparer.GetHashCode(Item4), + comparer.GetHashCode(Item5), + comparer.GetHashCode(Item6), + comparer.GetHashCode(Item7)); } int size = rest.Size; @@ -2091,8 +2087,7 @@ int ITupleInternal.GetHashCode(IEqualityComparer comparer) /// public override string ToString() { - ITupleInternal rest = Rest as ITupleInternal; - if (rest == null) + if (Rest is not ITupleInternal rest) { return "(" + Item1?.ToString() + ", " + Item2?.ToString() + ", " + Item3?.ToString() + ", " + Item4?.ToString() + ", " + Item5?.ToString() + ", " + Item6?.ToString() + ", " + Item7?.ToString() + ", " + Rest.ToString() + ")"; } @@ -2104,8 +2099,7 @@ public override string ToString() string ITupleInternal.ToStringEnd() { - ITupleInternal rest = Rest as ITupleInternal; - if (rest == null) + if (Rest is not ITupleInternal rest) { return Item1?.ToString() + ", " + Item2?.ToString() + ", " + Item3?.ToString() + ", " + Item4?.ToString() + ", " + Item5?.ToString() + ", " + Item6?.ToString() + ", " + Item7?.ToString() + ", " + Rest.ToString() + ")"; } @@ -2119,8 +2113,9 @@ int ITupleInternal.Size { get { - ITupleInternal rest = Rest as ITupleInternal; - return rest == null ? 8 : 7 + rest.Size; + //ITupleInternal? rest = Rest as ITupleInternal; + //return rest == null ? 8 : 7 + rest.Size; + return Rest is not ITupleInternal rest ? 8 : 7 + rest.Size; } } } diff --git a/tests/TestConsole/Program.cs b/tests/TestConsole/Program.cs index 91a0510..576993a 100644 --- a/tests/TestConsole/Program.cs +++ b/tests/TestConsole/Program.cs @@ -18,5 +18,8 @@ int midElement = someArray[^3]; Console.WriteLine(midElement); var range = someArray[1..3]; - +foreach (var item in range) +{ + Console.WriteLine(item); +} Console.ReadLine(); -- Gitee From 05cb4807282187dbc95b809682e874ed3a01d844 Mon Sep 17 00:00:00 2001 From: liuqihong <540762622@qq.com> Date: Mon, 30 May 2022 01:18:58 +0800 Subject: [PATCH 233/675] =?UTF-8?q?=E5=A4=84=E7=90=86=E5=A4=A7=E9=87=8F?= =?UTF-8?q?=E7=9A=84=E5=BB=BA=E8=AE=AE?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/IFoxCAD.Basal/CLS/RuntimeHelpers.cs | 3 - .../CLS/TupleElementNamesAttribute.cs | 2 - src/IFoxCAD.Basal/CLS/ValueTuple.cs | 6 - src/IFoxCAD.Basal/IFoxCAD.Basal.csproj | 19 ++ src/IFoxCAD.Cad/Algorithms/Graph.cs | 4 +- src/IFoxCAD.Cad/Algorithms/Rect.cs | 20 +- src/IFoxCAD.Cad/ExtensionMethod/EditorEx.cs | 2 +- .../Edge.cs" | 4 + .../TestTopo.cs" | 2 +- .../Topo.cs" | 63 +++- src/IFoxCAD.WPF/EventBindingExtension.cs | 3 +- tests/Test/TestCurve.cs | 14 +- tests/Test/TestDBTrans.cs | 2 + tests/Test/TestJig.cs | 27 +- tests/Test/testConvexHull.cs | 4 +- tests/Test/testblock.cs | 282 +++++++++--------- tests/Test/testeditor.cs | 10 +- tests/Test/testid.cs | 20 +- tests/Test/testselectfilter.cs | 9 +- 19 files changed, 270 insertions(+), 226 deletions(-) diff --git a/src/IFoxCAD.Basal/CLS/RuntimeHelpers.cs b/src/IFoxCAD.Basal/CLS/RuntimeHelpers.cs index 5455f1e..0e0073b 100644 --- a/src/IFoxCAD.Basal/CLS/RuntimeHelpers.cs +++ b/src/IFoxCAD.Basal/CLS/RuntimeHelpers.cs @@ -17,13 +17,10 @@ public static T[] GetSubArray(T[] array!!, Range range) if (default(T)! != null || typeof(T[]) == array.GetType()) // TODO-NULLABLE: default(T) == null warning (https://github.com/dotnet/roslyn/issues/34757) { // We know the type of the array to be exactly T[]. - if (length == 0) { //return Array.Empty(); return new T[0]; - - } var dest = new T[length]; diff --git a/src/IFoxCAD.Basal/CLS/TupleElementNamesAttribute.cs b/src/IFoxCAD.Basal/CLS/TupleElementNamesAttribute.cs index 7fb152f..60ac337 100644 --- a/src/IFoxCAD.Basal/CLS/TupleElementNamesAttribute.cs +++ b/src/IFoxCAD.Basal/CLS/TupleElementNamesAttribute.cs @@ -2,8 +2,6 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. -using System.Collections.Generic; - namespace System.Runtime.CompilerServices { /// diff --git a/src/IFoxCAD.Basal/CLS/ValueTuple.cs b/src/IFoxCAD.Basal/CLS/ValueTuple.cs index 012386a..4705e2d 100644 --- a/src/IFoxCAD.Basal/CLS/ValueTuple.cs +++ b/src/IFoxCAD.Basal/CLS/ValueTuple.cs @@ -3,13 +3,7 @@ //#pragma warning disable SA1141 // explicitly not using tuple syntax in tuple implementation -using System.Collections; -using System.Collections.Generic; using System.Diagnostics; -using System.Diagnostics.CodeAnalysis; -using System.Runtime.InteropServices; -using System.Runtime.CompilerServices; -using System.Globalization; using System.Numerics.Hashing; #if NET35 diff --git a/src/IFoxCAD.Basal/IFoxCAD.Basal.csproj b/src/IFoxCAD.Basal/IFoxCAD.Basal.csproj index 4105bb6..0b5abcd 100644 --- a/src/IFoxCAD.Basal/IFoxCAD.Basal.csproj +++ b/src/IFoxCAD.Basal/IFoxCAD.Basal.csproj @@ -20,6 +20,7 @@ LICENSE true True + none @@ -46,4 +47,22 @@ $(Configuration);ac2015 + + 1701;1702; + + + 1701;1702; + + + 1701;1702; + + + 1701;1702; + + + 1701;1702; + + + 1701;1702; + diff --git a/src/IFoxCAD.Cad/Algorithms/Graph.cs b/src/IFoxCAD.Cad/Algorithms/Graph.cs index 1196407..e369b6d 100644 --- a/src/IFoxCAD.Cad/Algorithms/Graph.cs +++ b/src/IFoxCAD.Cad/Algorithms/Graph.cs @@ -29,7 +29,7 @@ public sealed class Graph : IGraph, IEnumerable /// Returns a reference vertex. /// Time complexity: O(1). /// - private IGraphVertex? referenceVertex + private IGraphVertex? ReferenceVertex { get { @@ -44,7 +44,7 @@ private IGraphVertex? referenceVertex return null; } } - IGraphVertex? IGraph.ReferenceVertex => referenceVertex; + IGraphVertex? IGraph.ReferenceVertex => ReferenceVertex; /// /// 目前点增加点的顺序号,这个点号不随删点而减少的 /// diff --git a/src/IFoxCAD.Cad/Algorithms/Rect.cs b/src/IFoxCAD.Cad/Algorithms/Rect.cs index 62e43a0..43f45d7 100644 --- a/src/IFoxCAD.Cad/Algorithms/Rect.cs +++ b/src/IFoxCAD.Cad/Algorithms/Rect.cs @@ -5,7 +5,7 @@ /// public class TolerancePoint2d : IEqualityComparer { - double _tolerance; + readonly double _tolerance; public TolerancePoint2d(double tolerance = 1e-6) { _tolerance = tolerance; @@ -43,8 +43,10 @@ public int GetHashCode(Point2d obj) [StructLayout(LayoutKind.Sequential)] public class Rect : IEquatable { - public static TolerancePoint2d _RectTolerance = new(1e-6); - public static Tolerance _cadTolerance = new(1e-6, 1e-6); +#pragma warning disable CA2211 // 非常量字段应当不可见 + public static TolerancePoint2d RectTolerance = new(1e-6); + public static Tolerance CadTolerance = new(1e-6, 1e-6); +#pragma warning restore CA2211 // 非常量字段应当不可见 #region 字段 //这里的成员不要用{get}封装成属性,否则会导致跳转了一次函数, @@ -268,7 +270,7 @@ public bool IntersectsWith(Rect rect) /// public Point2d[] GetCommonPoint(Rect other) { - return ToPoints().Intersect(other.ToPoints(), _RectTolerance).ToArray(); + return ToPoints().Intersect(other.ToPoints(), RectTolerance).ToArray(); } public Point2d[] ToPoints() @@ -316,7 +318,7 @@ public static bool IsRectAngle(List? ptList, double tolerance = 1e-8) if (ptList.Count == 5) { //首尾点相同移除最后 - if (pts[0].IsEqualTo(pts[pts.Count - 1], _cadTolerance)) + if (pts[0].IsEqualTo(pts[^1], CadTolerance)) pts.RemoveAt(pts.Count - 1); } if (pts.Count != 4) @@ -366,7 +368,7 @@ public static bool IsRect(List? ptList, double tolerance = 1e-10) if (ptList.Count == 5) { //首尾点相同移除最后 - if (pts[0].IsEqualTo(pts[pts.Count - 1], _cadTolerance)) + if (pts[0].IsEqualTo(pts[^1], CadTolerance)) pts.RemoveAt(pts.Count - 1); } if (pts.Count != 4) @@ -433,11 +435,7 @@ public static bool RectAnglePointOrder(List? pts) //保证是逆时针 var isAcw = CrossAclockwise(pts[0], pts[1], pts[2]); if (!isAcw) - { - var tmp = pts[1]; - pts[1] = pts[3]; - pts[3] = tmp; - } + (pts[3], pts[1]) = (pts[1], pts[3]); return true; } diff --git a/src/IFoxCAD.Cad/ExtensionMethod/EditorEx.cs b/src/IFoxCAD.Cad/ExtensionMethod/EditorEx.cs index 0d4f19e..ec780df 100644 --- a/src/IFoxCAD.Cad/ExtensionMethod/EditorEx.cs +++ b/src/IFoxCAD.Cad/ExtensionMethod/EditorEx.cs @@ -1042,7 +1042,6 @@ public static PromptResult GetString(this Editor ed, string Message, string Defa /// 缓冲结果,返回值 #pragma warning disable IDE0060 // 删除未使用的参数 public static ResultBuffer? RunLisp(this Editor ed, string arg) -#pragma warning restore IDE0060 // 删除未使用的参数 { _ = AcedEvaluateLisp(arg, out IntPtr rb); if (rb != IntPtr.Zero) @@ -1059,6 +1058,7 @@ public static PromptResult GetString(this Editor ed, string Message, string Defa } return null; } +#pragma warning restore IDE0060 // 删除未使用的参数 #endregion } diff --git "a/src/IFoxCAD.Cad/ExtensionMethod/\351\227\255\345\220\210\350\276\271\347\225\214\347\256\227\346\263\225/Edge.cs" "b/src/IFoxCAD.Cad/ExtensionMethod/\351\227\255\345\220\210\350\276\271\347\225\214\347\256\227\346\263\225/Edge.cs" index a94a20c..0136531 100644 --- "a/src/IFoxCAD.Cad/ExtensionMethod/\351\227\255\345\220\210\350\276\271\347\225\214\347\256\227\346\263\225/Edge.cs" +++ "b/src/IFoxCAD.Cad/ExtensionMethod/\351\227\255\345\220\210\350\276\271\347\225\214\347\256\227\346\263\225/Edge.cs" @@ -179,7 +179,9 @@ public bool SplitPointEquals(Edge? b, int splitNum = 4) #endregion #region +#pragma warning disable CA2211 // dzֶӦɼ public static Tolerance CadTolerance = new(1e-6, 1e-6); +#pragma warning restore CA2211 // dzֶӦɼ /// /// ˳ @@ -508,8 +510,10 @@ public class CompositeCurve3ds : List, IFormattable [DebuggerBrowsable(DebuggerBrowsableState.Never)] private string DebuggerDisplay => ToString(); +#pragma warning disable CA2211 // dzֶӦɼ // cadݲ public static Tolerance CadTolerance = new(1e-6, 1e-6); +#pragma warning restore CA2211 // dzֶӦɼ /// /// ߽ diff --git "a/src/IFoxCAD.Cad/ExtensionMethod/\351\227\255\345\220\210\350\276\271\347\225\214\347\256\227\346\263\225/TestTopo.cs" "b/src/IFoxCAD.Cad/ExtensionMethod/\351\227\255\345\220\210\350\276\271\347\225\214\347\256\227\346\263\225/TestTopo.cs" index 19caa69..815dcb4 100644 --- "a/src/IFoxCAD.Cad/ExtensionMethod/\351\227\255\345\220\210\350\276\271\347\225\214\347\256\227\346\263\225/TestTopo.cs" +++ "b/src/IFoxCAD.Cad/ExtensionMethod/\351\227\255\345\220\210\350\276\271\347\225\214\347\256\227\346\263\225/TestTopo.cs" @@ -3,7 +3,7 @@ public class CmdTestTopo { [CommandMethod("TestTopo")] - public void TestTopo() + public static void TestTopo() { using var tr = new DBTrans(); var ents = Env.Editor.SSGet()?.Value?.GetEntities(); diff --git "a/src/IFoxCAD.Cad/ExtensionMethod/\351\227\255\345\220\210\350\276\271\347\225\214\347\256\227\346\263\225/Topo.cs" "b/src/IFoxCAD.Cad/ExtensionMethod/\351\227\255\345\220\210\350\276\271\347\225\214\347\256\227\346\263\225/Topo.cs" index de38632..f86ab86 100644 --- "a/src/IFoxCAD.Cad/ExtensionMethod/\351\227\255\345\220\210\350\276\271\347\225\214\347\256\227\346\263\225/Topo.cs" +++ "b/src/IFoxCAD.Cad/ExtensionMethod/\351\227\255\345\220\210\350\276\271\347\225\214\347\256\227\346\263\225/Topo.cs" @@ -3,10 +3,13 @@ namespace IFoxCAD.Cad; public class Topo { #region 成员 - // 求交类(每次set自动重置,都会有个新的结果) - static CurveCurveIntersector3d _Cci3d = new(); +#pragma warning disable CA2211 // 非常量字段应当不可见 // cad容差类 public static Tolerance CadTolerance = new(1e-6, 1e-6); +#pragma warning restore CA2211 // 非常量字段应当不可见 + + // 求交类(每次set自动重置,都会有个新的结果) + static readonly CurveCurveIntersector3d _Cci3d = new(); public List CurveList; #endregion @@ -42,12 +45,54 @@ public Topo(List curves) Dictionary boNodes = new(); var topo = new Topo(curves); + +/* 项目“IFoxCAD.Cad (net40)”的未合并的更改 +在此之前: topo.GetEdgesAndnewCurves(topo.CurveList, gs, closedCurve3d); +在此之后: + Topo.GetEdgesAndnewCurves(topo.CurveList, gs, closedCurve3d); +*/ + +/* 项目“IFoxCAD.Cad (net45)”的未合并的更改 +在此之前: + topo.GetEdgesAndnewCurves(topo.CurveList, gs, closedCurve3d); +在此之后: + Topo.GetEdgesAndnewCurves(topo.CurveList, gs, closedCurve3d); +*/ + GetEdgesAndnewCurves(topo.CurveList, gs, closedCurve3d); + +/* 项目“IFoxCAD.Cad (net40)”的未合并的更改 +在此之前: topo.AdjacencyList(gs, closedCurve3d, boNodes); +在此之后: + Topo.AdjacencyList(gs, closedCurve3d, boNodes); +*/ + +/* 项目“IFoxCAD.Cad (net45)”的未合并的更改 +在此之前: + topo.AdjacencyList(gs, closedCurve3d, boNodes); +在此之后: + Topo.AdjacencyList(gs, closedCurve3d, boNodes); +*/ + AdjacencyList(gs, closedCurve3d, boNodes); var bos = boNodes.Select(a => a.Value).ToArray(); //TODO Topo校验数据 + +/* 项目“IFoxCAD.Cad (net40)”的未合并的更改 +在此之前: var regions = topo.BreadthFirstSearch(bos); +在此之后: + var regions = Topo.BreadthFirstSearch(bos); +*/ + +/* 项目“IFoxCAD.Cad (net45)”的未合并的更改 +在此之前: + var regions = topo.BreadthFirstSearch(bos); +在此之后: + var regions = Topo.BreadthFirstSearch(bos); +*/ + var regions = BreadthFirstSearch(bos); if (regions == null || regions.Count == 0) return null; @@ -73,7 +118,7 @@ public Topo(List curves) /// 传入每组有碰撞的 /// 传出不自闭的曲线集 /// 传出自闭曲线集 - public void GetEdgesAndnewCurves(List infos_In, List edges_Out, List closed_Out) + public static void GetEdgesAndnewCurves(List infos_In, List edges_Out, List closed_Out) { //此处是O(n²) //曲线a和其他曲线n根据 交点 切割子线(第一次是自交对比) @@ -133,18 +178,18 @@ public void GetEdgesAndnewCurves(List infos_In, List edges_Out, /// /// 创建邻接表 /// - /// 传入每组有碰撞的 + /// 传入每组有碰撞的 /// 传出自闭曲线集 /// 节点集合返回(交点坐标字符串,节点) - public void AdjacencyList(List edges_In, List closed_Out, Dictionary boNodes_Out) + public static void AdjacencyList(List edgesIn, List closed_Out, Dictionary boNodes_Out) { int boNumber = 0; /* * 邻接表:不重复地将共点(交点)作为标记,然后容器加入边 */ - for (int i = 0; i < edges_In.Count; i++) + for (int i = 0; i < edgesIn.Count; i++) { - var edge = edges_In[i]; + var edge = edgesIn[i]; //曲线闭合直接提供出去 if (edge.GeCurve3d.IsClosed()) { @@ -222,8 +267,6 @@ public void AdjacencyList(List edges_In, List closed_Out } #endregion - - #region 广度 /// /// 广度优先算法 @@ -232,7 +275,7 @@ public void AdjacencyList(List edges_In, List closed_Out /// 邻接节点集合 /// 多个面域 /// - List> BreadthFirstSearch(BoNode[] boNodes) + static List> BreadthFirstSearch(BoNode[] boNodes) { if (boNodes == null || boNodes.Length == 0) throw new ArgumentNullException(nameof(boNodes)); diff --git a/src/IFoxCAD.WPF/EventBindingExtension.cs b/src/IFoxCAD.WPF/EventBindingExtension.cs index df4ee6f..eb58c75 100644 --- a/src/IFoxCAD.WPF/EventBindingExtension.cs +++ b/src/IFoxCAD.WPF/EventBindingExtension.cs @@ -39,8 +39,7 @@ public class EventBindingExtension : MarkupExtension throw new InvalidOperationException(); } - var memberInfo = targetProvider.TargetProperty as MemberInfo; - if (memberInfo is null) + if (targetProvider.TargetProperty is not MemberInfo memberInfo) { throw new InvalidOperationException(); } diff --git a/tests/Test/TestCurve.cs b/tests/Test/TestCurve.cs index 02c07a4..4d5cd96 100644 --- a/tests/Test/TestCurve.cs +++ b/tests/Test/TestCurve.cs @@ -12,12 +12,14 @@ public void TestPointInDict() var pt3 = new Point3d(0.0255002, 0.4520001, 0); var pt4 = new Point3d(0.0255450, 0.45287893, 0); var pt5 = new Point3d(0.02554935, 0.452092375, 0); - var dict = new Dictionary(); - dict.Add(pt1, 1); - dict.Add(pt2, 2); - dict.Add(pt3, 3); - dict.Add(pt4, 4); - dict.Add(pt5, 5); + var dict = new Dictionary + { + { pt1, 1 }, + { pt2, 2 }, + { pt3, 3 }, + { pt4, 4 }, + { pt5, 5 } + }; Env.Print(dict[pt1]); } diff --git a/tests/Test/TestDBTrans.cs b/tests/Test/TestDBTrans.cs index 7cda725..2f14107 100644 --- a/tests/Test/TestDBTrans.cs +++ b/tests/Test/TestDBTrans.cs @@ -47,6 +47,7 @@ public void TestPt() { //var pt = Env.Editor.GetPoint("pick pt:").Value; //var pl = Env.Editor.GetEntity("pick pl").ObjectId; + var tr1 = HostApplicationServices.WorkingDatabase.TransactionManager.TopTransaction; using var tr2 = new DBTrans(); var tr3 = HostApplicationServices.WorkingDatabase.TransactionManager.TopTransaction; @@ -59,6 +60,7 @@ public void TestPt() Env.Print(tr4.Transaction == tr5); Env.Print(tr5 == tr7); var trm = HostApplicationServices.WorkingDatabase.TransactionManager; + //var ptt = tr.GetObject(pl).GetClosestPointTo(pt,false); //var pt1 = new Point3d(0, 0.00000000000001, 0); //var pt2 = new Point3d(0, 0.00001, 0); diff --git a/tests/Test/TestJig.cs b/tests/Test/TestJig.cs index dc07bf5..343763a 100644 --- a/tests/Test/TestJig.cs +++ b/tests/Test/TestJig.cs @@ -17,7 +17,7 @@ public class Commands [CommandMethod("loop")] - public void Loop() + public void Loop() { @@ -29,7 +29,7 @@ public void Loop() // Create and add our message filter - MyMessageFilter filter = new MyMessageFilter(); + MyMessageFilter filter = new(); System.Windows.Forms.Application.AddMessageFilter(filter); @@ -128,8 +128,7 @@ static public void QuickText() Database db = doc.Database; Editor ed = doc.Editor; - PromptStringOptions pso = - new PromptStringOptions("\nEnter text string"); + PromptStringOptions pso = new("\nEnter text string"); pso.AllowSpaces = true; PromptResult pr = ed.GetString(pso); @@ -147,7 +146,7 @@ static public void QuickText() // Create the text object, set its normal and contents - DBText txt = new DBText(); + DBText txt = new(); txt.Normal = ed.CurrentUserCoordinateSystem. CoordinateSystem3d.Zaxis; @@ -162,7 +161,7 @@ static public void QuickText() // Create our jig - TextPlacementJig pj = new TextPlacementJig(tr, db, txt); + TextPlacementJig pj = new(tr, db, txt); // Loop as we run our jig, as we may have keywords @@ -186,8 +185,8 @@ class TextPlacementJig : EntityJig { // Declare some internal state - Database _db; - Transaction _tr; + readonly Database _db; + readonly Transaction _tr; Point3d _position; double _angle, _txtSize; @@ -210,7 +209,7 @@ JigPrompts jp // We acquire a point but with keywords JigPromptPointOptions po = - new JigPromptPointOptions( + new( "\nPosition of text" ); @@ -236,12 +235,12 @@ JigPrompts jp { case "Bold": { - + break; } case "Italic": { - + break; } @@ -273,19 +272,19 @@ JigPrompts jp } case "LEft": { - + break; } case "RIght": { - + break; } case "Middle": { - + break; } diff --git a/tests/Test/testConvexHull.cs b/tests/Test/testConvexHull.cs index 259b8be..19c3da7 100644 --- a/tests/Test/testConvexHull.cs +++ b/tests/Test/testConvexHull.cs @@ -12,10 +12,10 @@ namespace Test { - public class testConvexHull + public class TestConvexHull { [CommandMethod("testch")] - public void testch() + public void Testch() { //using var tr = new DBTrans(); //var pts = new List(); diff --git a/tests/Test/testblock.cs b/tests/Test/testblock.cs index 110280b..569ff39 100644 --- a/tests/Test/testblock.cs +++ b/tests/Test/testblock.cs @@ -229,7 +229,7 @@ public void EJ() } using var tr2 = new DBTrans(); - PromptEntityOptions PEO = new PromptEntityOptions("\n请选择一个块"); + PromptEntityOptions PEO = new("\n请选择一个块"); PEO.SetRejectMessage("\n对象必须是块"); PEO.AddAllowedClass(typeof(BlockReference), true); @@ -300,83 +300,81 @@ public void QuickBlockDef() { //Database db = HostApplicationServices.WorkingDatabase; Editor ed = Application.DocumentManager.MdiActiveDocument.Editor; - PromptSelectionOptions promptOpt = new PromptSelectionOptions + PromptSelectionOptions promptOpt = new() { MessageForAdding = "请选择需要快速制作块的对象" }; string blockName = "W_BLOCK_" + DateTime.Now.ToString("yyyyMMdd_HHmmss"); //var rss = ed.GetSelection(promptOpt); var rss = Env.Editor.GetSelection(promptOpt); - using (var tr = new DBTrans()) + using var tr = new DBTrans(); + if (rss.Status == PromptStatus.OK) { - if (rss.Status == PromptStatus.OK) - { - //SelectionSet ss = rss.Value; - //ObjectId[] ids = ss.GetObjectIds(); - //var ents = new List>(); - //var extents = new Extents3d(); - //foreach (var id in ids) - //{ - // Entity ent = tr.GetObject(id); - // if (ent is null) - // continue; - // try - // { - // extents.AddExtents(ent.GeometricExtents); - // var order = id.Handle.Value; - // var newEnt = ent.Clone() as Entity; - // ents.Add(new KeyValuePair(newEnt, order)); - // ent.UpgradeOpen(); - // ent.Erase(); - // ent.DowngradeOpen(); - // } - // catch (System.Exception exc) - // { - // ed.WriteMessage(exc.Message); - // } - //} - //ents = ents.OrderBy(x => x.Value).ToList(); - var ents = rss.Value.GetEntities(); - //ents.ForEach(ent => extents.AddExtents(ent.GeometricExtents)); - var extents = ents.GetExtents(); - - Point3d pt = extents.MinPoint; - Matrix3d matrix = Matrix3d.Displacement(Point3d.Origin - pt); - //var newEnts = new List(); - //foreach (var ent in ents) - //{ - // var newEnt = ent.Key; - // newEnt.TransformBy(matrix); - // newEnts.Add(newEnt); - //} - //if (tr.BlockTable.Has(blockName)) - //{ - // Application.ShowAlertDialog(Environment.NewLine + "块名重复,程序退出!"); - // return; - //} - ents.ForEach(ent => - ent.ForWrite(e => e.TransformBy(matrix))); - //var newents = ents.Select(ent => - //{ - // var maping = new IdMapping(); - // return ent.DeepClone(ent, maping, true) as Entity; - //}); - var newents = ents.Select(ent => ent.Clone() as Entity); - - //ents.ForEach(ent => ent.ForWrite(e => e.Erase(true))); // 删除实体就会卡死,比较奇怪,估计是Clone()函数的问题 - // 经过测试不是删除的问题 - var btrId = tr.BlockTable.Add(blockName, newents); - ents.ForEach(ent => ent.ForWrite(e => e.Erase(true))); - var bId = tr.CurrentSpace.InsertBlock(pt, blockName); - //tr.GetObject(bId, OpenMode.ForWrite).Move(Point3d.Origin, Point3d.Origin); - //var ed = Application.DocumentManager.MdiActiveDocument.Editor; - //ed.Regen(); - //tr.Editor.Regen(); - // 调用regen() 卡死 - } - //tr.Editor.Regen(); + //SelectionSet ss = rss.Value; + //ObjectId[] ids = ss.GetObjectIds(); + //var ents = new List>(); + //var extents = new Extents3d(); + //foreach (var id in ids) + //{ + // Entity ent = tr.GetObject(id); + // if (ent is null) + // continue; + // try + // { + // extents.AddExtents(ent.GeometricExtents); + // var order = id.Handle.Value; + // var newEnt = ent.Clone() as Entity; + // ents.Add(new KeyValuePair(newEnt, order)); + // ent.UpgradeOpen(); + // ent.Erase(); + // ent.DowngradeOpen(); + // } + // catch (System.Exception exc) + // { + // ed.WriteMessage(exc.Message); + // } + //} + //ents = ents.OrderBy(x => x.Value).ToList(); + var ents = rss.Value.GetEntities(); + //ents.ForEach(ent => extents.AddExtents(ent.GeometricExtents)); + var extents = ents.GetExtents(); + + Point3d pt = extents.MinPoint; + Matrix3d matrix = Matrix3d.Displacement(Point3d.Origin - pt); + //var newEnts = new List(); + //foreach (var ent in ents) + //{ + // var newEnt = ent.Key; + // newEnt.TransformBy(matrix); + // newEnts.Add(newEnt); + //} + //if (tr.BlockTable.Has(blockName)) + //{ + // Application.ShowAlertDialog(Environment.NewLine + "块名重复,程序退出!"); + // return; + //} + ents.ForEach(ent => + ent.ForWrite(e => e.TransformBy(matrix))); + //var newents = ents.Select(ent => + //{ + // var maping = new IdMapping(); + // return ent.DeepClone(ent, maping, true) as Entity; + //}); + var newents = ents.Select(ent => ent.Clone() as Entity); + + //ents.ForEach(ent => ent.ForWrite(e => e.Erase(true))); // 删除实体就会卡死,比较奇怪,估计是Clone()函数的问题 + // 经过测试不是删除的问题 + var btrId = tr.BlockTable.Add(blockName, newents); + ents.ForEach(ent => ent.ForWrite(e => e.Erase(true))); + var bId = tr.CurrentSpace.InsertBlock(pt, blockName); + //tr.GetObject(bId, OpenMode.ForWrite).Move(Point3d.Origin, Point3d.Origin); + //var ed = Application.DocumentManager.MdiActiveDocument.Editor; //ed.Regen(); + //tr.Editor.Regen(); + // 调用regen() 卡死 } + //tr.Editor.Regen(); + //ed.Regen(); //using (var tr = new DBTrans()) //{ // tr.CurrentSpace.InsertBlock(Point3d.Origin, blockName); @@ -389,7 +387,7 @@ public void TestQuickBlockDef() { Database db = HostApplicationServices.WorkingDatabase; Editor ed = Application.DocumentManager.MdiActiveDocument.Editor; - PromptSelectionOptions promptOpt = new PromptSelectionOptions + PromptSelectionOptions promptOpt = new() { MessageForAdding = "请选择需要快速制作块的对象" }; @@ -401,38 +399,40 @@ public void TestQuickBlockDef() return; } - using (var tr = db.TransactionManager.StartTransaction()) + using var tr = db.TransactionManager.StartTransaction(); + var ids = rss.Value.GetObjectIds(); + var bt = tr.GetObject(db.BlockTableId, OpenMode.ForRead) as BlockTable; + var btr = new BlockTableRecord { - var ids = rss.Value.GetObjectIds(); - var bt = tr.GetObject(db.BlockTableId, OpenMode.ForRead) as BlockTable; - var btr = new BlockTableRecord(); - btr.Name = blockName; - foreach (var item in ids) - { - var ent = tr.GetObject(item, OpenMode.ForRead) as Entity; - - btr.AppendEntity(ent.Clone() as Entity); - ent.ForWrite(e => e.Erase(true)); - } - bt.UpgradeOpen(); - bt.Add(btr); - tr.AddNewlyCreatedDBObject(btr, true); - bt.DowngradeOpen(); - // tr.Commit(); - //} + Name = blockName + }; + foreach (var item in ids) + { + var ent = tr.GetObject(item, OpenMode.ForRead) as Entity; - //using (var tr1 = db.TransactionManager.StartTransaction()) - //{ - //var bt = tr1.GetObject(db.BlockTableId, OpenMode.ForRead) as BlockTable; - var btr1 = tr.GetObject(db.CurrentSpaceId, OpenMode.ForWrite) as BlockTableRecord; - var br = new BlockReference(Point3d.Origin, bt[blockName]); - br.ScaleFactors = default; - btr1.AppendEntity(br); - tr.AddNewlyCreatedDBObject(br, true); - btr1.DowngradeOpen(); - ed.Regen(); - tr.Commit(); + btr.AppendEntity(ent.Clone() as Entity); + ent.ForWrite(e => e.Erase(true)); } + bt.UpgradeOpen(); + bt.Add(btr); + tr.AddNewlyCreatedDBObject(btr, true); + bt.DowngradeOpen(); + // tr.Commit(); + //} + + //using (var tr1 = db.TransactionManager.StartTransaction()) + //{ + //var bt = tr1.GetObject(db.BlockTableId, OpenMode.ForRead) as BlockTable; + var btr1 = tr.GetObject(db.CurrentSpaceId, OpenMode.ForWrite) as BlockTableRecord; + var br = new BlockReference(Point3d.Origin, bt[blockName]) + { + ScaleFactors = default + }; + btr1.AppendEntity(br); + tr.AddNewlyCreatedDBObject(br, true); + btr1.DowngradeOpen(); + ed.Regen(); + tr.Commit(); //ed.Regen(); } @@ -450,18 +450,15 @@ public void TestWblock() public void TestChangeDynameicBlock() { - var pro = new Dictionary(); - pro.Add("haha", 1); - var blockid = Env.Editor.GetEntity("选择个块").ObjectId; - using (var tr = new DBTrans()) + var pro = new Dictionary { - var blockref = tr.GetObject(blockid); - blockref.ChangeBlockProperty(pro); - // 这是第一个函数的用法 - } - - - + { "haha", 1 } + }; + var blockid = Env.Editor.GetEntity("选择个块").ObjectId; + using var tr = new DBTrans(); + var blockref = tr.GetObject(blockid); + blockref.ChangeBlockProperty(pro); + // 这是第一个函数的用法 } [CommandMethod("TestBack")] @@ -494,7 +491,7 @@ public class BlockImportClass { [CommandMethod("CBLL")] - public void cbll() + public void Cbll() { string filename = @"C:\Users\vic\Desktop\Drawing1.dwg"; using var tr = new DBTrans(); @@ -502,7 +499,6 @@ public void cbll() //tr.BlockTable.GetBlockFrom(filename, true); string blkdefname = SymbolUtilityServices.RepairSymbolName(SymbolUtilityServices.GetSymbolNameFromPathName(filename, "dwg"), false); tr.Database.Insert(blkdefname, tr1.Database, false); //插入了块定义,未插入块参照 - } @@ -573,46 +569,44 @@ public void CombineBlocksIntoLibrary() // Create a source database to load the DWG into - using (Database db = new Database(false, true)) - { - // Read the DWG into our side database + using Database db = new(false, true); + // Read the DWG into our side database - db.ReadDwgFile(fileName, FileShare.Read, true, ""); - bool isAnno = db.AnnotativeDwg; + db.ReadDwgFile(fileName, FileShare.Read, true, ""); + bool isAnno = db.AnnotativeDwg; - // Insert it into the destination database as - // a named block definition + // Insert it into the destination database as + // a named block definition - ObjectId btrId = destDb.Insert( - destName, - db, - false - ); + ObjectId btrId = destDb.Insert( + destName, + db, + false + ); + + if (isAnno) + { + // If an annotative block, open the resultant BTR + // and set its annotative definition status - if (isAnno) + Transaction tr = + destDb.TransactionManager.StartTransaction(); + using (tr) { - // If an annotative block, open the resultant BTR - // and set its annotative definition status - - Transaction tr = - destDb.TransactionManager.StartTransaction(); - using (tr) - { - BlockTableRecord btr = - (BlockTableRecord)tr.GetObject( - btrId, - OpenMode.ForWrite - ); - btr.Annotative = AnnotativeStates.True; - tr.Commit(); - } + BlockTableRecord btr = + (BlockTableRecord)tr.GetObject( + btrId, + OpenMode.ForWrite + ); + btr.Annotative = AnnotativeStates.True; + tr.Commit(); } + } - // Print message and increment imported block counter + // Print message and increment imported block counter - ed.WriteMessage("\nImported from \"{0}\".", fileName); - imported++; - } + ed.WriteMessage("\nImported from \"{0}\".", fileName); + imported++; } catch (System.Exception ex) { diff --git a/tests/Test/testeditor.cs b/tests/Test/testeditor.cs index 8c5c0a0..5c5ac13 100644 --- a/tests/Test/testeditor.cs +++ b/tests/Test/testeditor.cs @@ -1,9 +1,9 @@ namespace Test; -public class testeditor +public class Testeditor { [CommandMethod("tested")] - public void tested() + public void Tested() { var pts = new List { @@ -24,7 +24,7 @@ public void tested() Env.Editor.WriteMessage(""); } [CommandMethod("testzoom")] - public void testzoom() + public void Testzoom() { using var tr = new DBTrans(); var res = Env.Editor.GetEntity("\npick ent:"); @@ -36,7 +36,7 @@ public void testzoom() } [CommandMethod("testzoomextent")] - public void testzoomextent() + public void Testzoomextent() { //using var tr = new DBTrans(); //var res = Env.Editor.GetEntity("\npick ent:"); @@ -49,7 +49,7 @@ public void testzoomextent() } [CommandMethod("testssget")] - public void testssget() + public void Testssget() { var action_a = () => { Env.Print("this is a"); }; var action_b = () => { Env.Print("this is b"); }; diff --git a/tests/Test/testid.cs b/tests/Test/testid.cs index 73ff962..d93b823 100644 --- a/tests/Test/testid.cs +++ b/tests/Test/testid.cs @@ -29,17 +29,15 @@ public void TestId() [CommandMethod("testmycommand")] public void TestMyCommand() { - using (var dbtrans = new DBTrans(Env.Document,true,false)) { - using (var trans = Env.Database.TransactionManager.StartTransaction()) - { - var l1 = new Line(new Point3d(0, 0, 0), new Point3d(100, 100, 0)); - var blkred = trans.GetObject(Env.Database.CurrentSpaceId, OpenMode.ForWrite) as BlockTableRecord; - blkred.AppendEntity(l1); - trans.AddNewlyCreatedDBObject(l1, true); - trans.Commit(); - } - //dbtrans.Dispose(); - } + using var dbtrans = new DBTrans(Env.Document, true, false); + using var trans = Env.Database.TransactionManager.StartTransaction(); + + var l1 = new Line(new Point3d(0, 0, 0), new Point3d(100, 100, 0)); + var blkred = trans.GetObject(Env.Database.CurrentSpaceId, OpenMode.ForWrite) as BlockTableRecord; + blkred.AppendEntity(l1); + trans.AddNewlyCreatedDBObject(l1, true); + trans.Commit(); + //dbtrans.Dispose(); } [CommandMethod("testtextstyle")] public void TestTextStyle() diff --git a/tests/Test/testselectfilter.cs b/tests/Test/testselectfilter.cs index b6c179b..3c403a9 100644 --- a/tests/Test/testselectfilter.cs +++ b/tests/Test/testselectfilter.cs @@ -12,10 +12,10 @@ namespace Test { - public class testselectfilter + public class Testselectfilter { [CommandMethod("testfilter")] - public void testfilter() + public void Testfilter() { var p = new Point3d(10, 10, 0); @@ -37,12 +37,9 @@ public void testfilter() } [CommandMethod("testselectanpoint")] - public void testselectanpoint() + public void Testselectanpoint() { - - var sel2 = Env.Editor.SelectAtPoint(new Point3d(0, 0, 0)); - Env.Editor.WriteMessage(""); } } -- Gitee From d1c07e7cb4156220b885b300eb23c75f5c0ae236 Mon Sep 17 00:00:00 2001 From: liuqihong <540762622@qq.com> Date: Sun, 5 Jun 2022 21:54:36 +0800 Subject: [PATCH 234/675] =?UTF-8?q?=E5=A2=9E=E5=8A=A0=E6=B3=A8=E9=87=8A?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/IFoxCAD.Basal/CLS/ValueTuple.cs | 35 ++++++++++++++++++++--------- 1 file changed, 24 insertions(+), 11 deletions(-) diff --git a/src/IFoxCAD.Basal/CLS/ValueTuple.cs b/src/IFoxCAD.Basal/CLS/ValueTuple.cs index 4705e2d..433fc6d 100644 --- a/src/IFoxCAD.Basal/CLS/ValueTuple.cs +++ b/src/IFoxCAD.Basal/CLS/ValueTuple.cs @@ -22,6 +22,8 @@ public interface IStructuralEquatable #endif + + namespace System.Numerics.Hashing { internal static class HashHelpers @@ -34,6 +36,9 @@ public static int Combine(int h1, int h2) { // RyuJIT optimizes this to use the ROL instruction // Related GitHub pull request: dotnet/coreclr#1830 + + // RyuJIT 对此进行了优化以使用 ROL 指令 + // 相关 GitHub 拉取请求:dotnet/coreclr#1830 uint rol5 = ((uint)h1 << 5) | ((uint)h1 >> 27); return ((int)rol5 + h1) ^ h2; } @@ -44,18 +49,19 @@ public static int Combine(int h1, int h2) - - namespace System { internal static class SR { - public const string ArgumentException_ValueTupleIncorrectType = "The parameter should be a ValueTuple type of appropriate arity."; - public const string ArgumentException_ValueTupleLastArgumentNotAValueTuple = "The TRest type argument of ValueTuple`8 must be a ValueTuple."; + // public const string ArgumentException_ValueTupleIncorrectType = "The parameter should be a ValueTuple type of appropriate arity."; + // public const string ArgumentException_ValueTupleLastArgumentNotAValueTuple = "The TRest type argument of ValueTuple`8 must be a ValueTuple."; + public const string ArgumentException_ValueTupleIncorrectType = "该参数应该是适当数量的 ValueTuple 类型."; + public const string ArgumentException_ValueTupleLastArgumentNotAValueTuple = "ValueTuple`8 的 TREST 类型参数必须是 ValueTuple."; } + // Helper so we can call some tuple methods recursively without knowing the underlying types. /// - /// Helper so we can call some tuple methods recursively without knowing the underlying types. + /// 帮助器,因此我们可以在不知道底层类型的情况下递归调用一些元组方法. /// internal interface ITupleInternal { @@ -64,13 +70,20 @@ internal interface ITupleInternal string ToStringEnd(); } + + // The ValueTuple types (from arity 0 to 8) comprise the runtime implementation that underlies tuples in C# and struct tuples in F#. + // Aside from created via language syntax, they are most easily created via the ValueTuple.Create factory methods. + // The System.ValueTuple types differ from the System.Tuple types in that: + // - they are structs rather than classes, + // - they are mutable rather than readonly, and + // - their members (such as Item1, Item2, etc) are fields rather than properties. /// - /// The ValueTuple types (from arity 0 to 8) comprise the runtime implementation that underlies tuples in C# and struct tuples in F#. - /// Aside from created via language syntax, they are most easily created via the ValueTuple.Create factory methods. - /// The System.ValueTuple types differ from the System.Tuple types in that: - /// - they are structs rather than classes, - /// - they are mutable rather than readonly, and - /// - their members (such as Item1, Item2, etc) are fields rather than properties. + /// ValueTuple 类型(从 arity 0 到 8)包含运行时实现,它是 C# 中的元组和 F# 中的结构元组的基础. + /// 除了通过语言语法创建之外,它们最容易通过 ValueTuple.Create 工厂方法创建. + /// System.ValueTuple 类型与 System.Tuple 类型的不同之处在于: + /// - 它们是结构而不是类, + /// - 它们是可变的而不是只读的,并且 + /// - 它们的成员(例如 Item1、Item2 等)是字段而不是属性. /// public struct ValueTuple : IEquatable, IStructuralEquatable, IStructuralComparable, IComparable, IComparable, ITupleInternal -- Gitee From 61898f29c0b79632fd6f4b9c98f356ffde6b2f7d Mon Sep 17 00:00:00 2001 From: DYH <1742647821@qq.com> Date: Tue, 7 Jun 2022 12:00:50 +0800 Subject: [PATCH 235/675] =?UTF-8?q?add=E5=BA=8F=E5=88=97=E5=8C=96?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../ExtensionMethod/SerializeTool.cs | 169 ++++++++++++++++++ 1 file changed, 169 insertions(+) create mode 100644 src/IFoxCAD.Cad/ExtensionMethod/SerializeTool.cs diff --git a/src/IFoxCAD.Cad/ExtensionMethod/SerializeTool.cs b/src/IFoxCAD.Cad/ExtensionMethod/SerializeTool.cs new file mode 100644 index 0000000..04fc825 --- /dev/null +++ b/src/IFoxCAD.Cad/ExtensionMethod/SerializeTool.cs @@ -0,0 +1,169 @@ +using Newtonsoft.Json; + +namespace IFoxCAD.Cad +{ + /// + /// 序列化 + /// + public static class SerializeTool + { + /// + /// 序列化为Json + /// + /// + /// 对象 + /// 字符串 + /// + public static string SerializeToJson(this T obj) + { + try + { + var str = JsonConvert.SerializeObject(obj); + return str; + } + catch (System.Exception ex) + { + Env.Editor.WriteMessage(ex.Message); + return String.Empty; + } + } + /// + /// 序列化为Json + /// + /// + /// 对象 + /// 文件路径名 + /// 成功返回True + public static bool SerializeToJson(this T obj, string filename) + { + try + { + var str = JsonConvert.SerializeObject(obj); + File.WriteAllText(filename, str); + return true; + } + catch (System.Exception ex) + { + Env.Editor.WriteMessage(ex.Message); + return false; + } + } + /// + /// Json反序列化 + /// + /// + /// 文件路径名 + /// + public static T? DeserializeFromJsonFile(this string filename) + { + try + { + var str = File.ReadAllText(filename); + return JsonConvert.DeserializeObject(str); + } + catch (System.Exception ex) + { + Env.Editor.WriteMessage(ex.Message); + return default; + } + } + /// + /// Json反序列化 + /// + /// + /// JSON字符串 + /// + public static T? DeserializeFromJson(this string jsonString) + { + try + { + return JsonConvert.DeserializeObject(jsonString); + } + catch (System.Exception ex) + { + Env.Editor.WriteMessage(ex.Message); + return default; + } + } + /// + /// 序列化为二进制数组 + /// + /// + /// 对象 + /// 二进制数组 + public static byte[]? SerializeToByteArray(this T obj) + { + try + { + var str = JsonConvert.SerializeObject(obj); + return System.Text.Encoding.UTF8.GetBytes(str); + } + catch (System.Exception ex) + { + Env.Editor.WriteMessage(ex.Message); + return default; + } + } + /// + /// 序列化为二进制数组 + /// + /// + /// 对象 + /// 文件路径名 + /// 成功返回True + public static bool SerializeToByteArray(this T obj, string filename) + { + try + { + var str = JsonConvert.SerializeObject(obj); + var byteArray = System.Text.Encoding.UTF8.GetBytes(str); + File.WriteAllBytes(filename, byteArray); + return true; + } + catch (System.Exception ex) + { + Env.Editor.WriteMessage(ex.Message); + return false; + } + } + /// + /// 二进制数组反序列化 + /// + /// + /// 二进制数组 + /// + public static T? DeserializeTo(this byte[] byteArray) + { + try + { + var str = System.Text.Encoding.UTF8.GetString(byteArray); + return JsonConvert.DeserializeObject(str); + } + catch (System.Exception ex) + { + Env.Editor.WriteMessage(ex.Message); + return default; + } + } + /// + /// 二进制数组反序列化 + /// + /// + /// 二进制数组 + /// + public static T? DeserializeFromByteArrayFile(this string filename) + { + try + { + var byteArray = File.ReadAllBytes(filename); + var str = System.Text.Encoding.UTF8.GetString(byteArray); + return JsonConvert.DeserializeObject(str); + } + catch (System.Exception ex) + { + Env.Editor.WriteMessage(ex.Message); + return default; + } + } + } +} -- Gitee From 79426cc8795c76943409d2bc0edc850b03790a62 Mon Sep 17 00:00:00 2001 From: DYH <1742647821@qq.com> Date: Wed, 8 Jun 2022 04:31:21 +0000 Subject: [PATCH 236/675] update src/IFoxCAD.Cad/ExtensionMethod/PointEx.cs. --- src/IFoxCAD.Cad/ExtensionMethod/PointEx.cs | 23 +++++++++++++++++++++- 1 file changed, 22 insertions(+), 1 deletion(-) diff --git a/src/IFoxCAD.Cad/ExtensionMethod/PointEx.cs b/src/IFoxCAD.Cad/ExtensionMethod/PointEx.cs index b7010b5..c534be6 100644 --- a/src/IFoxCAD.Cad/ExtensionMethod/PointEx.cs +++ b/src/IFoxCAD.Cad/ExtensionMethod/PointEx.cs @@ -1,4 +1,4 @@ -namespace IFoxCAD.Cad; +namespace IFoxCAD.Cad; public static class PointEx { @@ -20,6 +20,27 @@ public static string GetHashString(this Point3d pt, int xyz = 3, int decimalReta _ => $"({pt.X.ToString(de)},{pt.Y.ToString(de)},{pt.Z.ToString(de)})" }; } + /// + /// 两点计算弧度范围0到2Pi + /// + /// 起点 + /// 终点 + /// 方向 + /// 弧度值 + public static double GetAngle(this Point3d startPoint, Point3d endPoint, Vector3d? direction = null) + { + return startPoint.GetVectorTo(endPoint).AngleOnPlane(new Plane(Point3d.Origin, direction ?? Vector3d.ZAxis)); + } + /// + /// 两点计算弧度范围0到2Pi + /// + /// 起点 + /// 终点 + /// 弧度值 + public static double GetAngle(this Point2d startPoint, Point2d endPoint) + { + return startPoint.GetVectorTo(endPoint).Angle; + } } \ No newline at end of file -- Gitee From eadc0ea7170d3ba5ac766b6fe6d1bdc404f3a699 Mon Sep 17 00:00:00 2001 From: liuqihong <540762622@qq.com> Date: Thu, 9 Jun 2022 13:27:14 +0800 Subject: [PATCH 237/675] =?UTF-8?q?=E5=88=A9=E7=94=A8=E5=85=83=E7=BB=84?= =?UTF-8?q?=E4=BA=A4=E6=8D=A2?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/IFoxCAD.Basal/LoopList.cs | 6 ------ 1 file changed, 6 deletions(-) diff --git a/src/IFoxCAD.Basal/LoopList.cs b/src/IFoxCAD.Basal/LoopList.cs index 8693294..c0e021c 100644 --- a/src/IFoxCAD.Basal/LoopList.cs +++ b/src/IFoxCAD.Basal/LoopList.cs @@ -136,13 +136,7 @@ public bool SetFirst(LoopListNode node) /// 第二个节点 public void Swap(LoopListNode node1, LoopListNode node2) { -#if NET35 - var value = node1.Value; - node1.Value = node2.Value; - node2.Value = value; -#else (node2.Value, node1.Value) = (node1.Value, node2.Value); -#endif } /// -- Gitee From f78bb4f494743aa74c2995c7f6ecd1c93014630c Mon Sep 17 00:00:00 2001 From: liuqihong <540762622@qq.com> Date: Thu, 9 Jun 2022 13:31:17 +0800 Subject: [PATCH 238/675] =?UTF-8?q?=E5=85=83=E7=BB=84=E5=86=B2=E7=AA=81?= =?UTF-8?q?=E8=AF=B4=E6=98=8E?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/IFoxCAD.Basal/CLS/ValueTuple.cs | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/src/IFoxCAD.Basal/CLS/ValueTuple.cs b/src/IFoxCAD.Basal/CLS/ValueTuple.cs index 433fc6d..1bbe861 100644 --- a/src/IFoxCAD.Basal/CLS/ValueTuple.cs +++ b/src/IFoxCAD.Basal/CLS/ValueTuple.cs @@ -3,8 +3,19 @@ //#pragma warning disable SA1141 // explicitly not using tuple syntax in tuple implementation + using System.Diagnostics; using System.Numerics.Hashing; +/* + * 惊惊: + * 首先是因为有人想要编译的时候只形成一个dll,然后把元组塞入IFox,同时也补充了net35没有元组的遗憾. + * 而利用nuget元组包必然会形成依赖地狱. + * + * 如果你的工程使用了nuget元组包,就造成了必须要剔除IFox. + * + * 改IFox的元组命名空间倒是可以分离两者,但是 vs编译器 无法识别带其他命名空间的元组. + * 所以元组本身就是冲突的,需要把其他元组卸载掉,由IFox提供. + */ #if NET35 namespace System.Collections @@ -23,7 +34,6 @@ public interface IStructuralEquatable - namespace System.Numerics.Hashing { internal static class HashHelpers -- Gitee From 2c52e375a78edceea4d8fe4776837b5e867d3971 Mon Sep 17 00:00:00 2001 From: DYH <1742647821@qq.com> Date: Wed, 15 Jun 2022 03:25:42 +0000 Subject: [PATCH 239/675] =?UTF-8?q?update=20src/IFoxCAD.Cad/ExtensionMetho?= =?UTF-8?q?d/GeometryEx.cs.=20Polar=E5=87=BD=E6=95=B0=E5=A2=9E=E5=8A=A0poi?= =?UTF-8?q?nt2d?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/IFoxCAD.Cad/ExtensionMethod/GeometryEx.cs | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/src/IFoxCAD.Cad/ExtensionMethod/GeometryEx.cs b/src/IFoxCAD.Cad/ExtensionMethod/GeometryEx.cs index 84b57f7..bc4231b 100644 --- a/src/IFoxCAD.Cad/ExtensionMethod/GeometryEx.cs +++ b/src/IFoxCAD.Cad/ExtensionMethod/GeometryEx.cs @@ -1,4 +1,4 @@ -namespace IFoxCAD.Cad; +namespace IFoxCAD.Cad; using System.Drawing; using IFoxCAD.Basal; @@ -646,4 +646,16 @@ public static Point3d Polar(this Point3d pt, double ang, double len) { return pt + Vector3d.XAxis.RotateBy(ang, Vector3d.ZAxis) * len; } + /// + /// 计算指定距离和角度的点 + /// + /// 本函数仅适用于x-y平面 + /// 基点 + /// 角度,x轴正向逆时针弧度 + /// 距离 + /// 目标点 + public static Point2d Polar(this Point2d pt, double ang, double len) + { + return pt + Vector2d.XAxis.RotateBy(ang) * len; + } } -- Gitee From c5e660a5268d4e068c027f79435d18cab355b77e Mon Sep 17 00:00:00 2001 From: DYH <1742647821@qq.com> Date: Wed, 15 Jun 2022 03:29:28 +0000 Subject: [PATCH 240/675] =?UTF-8?q?=E5=88=A0=E9=99=A4=E6=96=87=E4=BB=B6=20?= =?UTF-8?q?src/IFoxCAD.Cad/ExtensionMethod/SerializeTool.cs?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../ExtensionMethod/SerializeTool.cs | 169 ------------------ 1 file changed, 169 deletions(-) delete mode 100644 src/IFoxCAD.Cad/ExtensionMethod/SerializeTool.cs diff --git a/src/IFoxCAD.Cad/ExtensionMethod/SerializeTool.cs b/src/IFoxCAD.Cad/ExtensionMethod/SerializeTool.cs deleted file mode 100644 index 04fc825..0000000 --- a/src/IFoxCAD.Cad/ExtensionMethod/SerializeTool.cs +++ /dev/null @@ -1,169 +0,0 @@ -using Newtonsoft.Json; - -namespace IFoxCAD.Cad -{ - /// - /// 序列化 - /// - public static class SerializeTool - { - /// - /// 序列化为Json - /// - /// - /// 对象 - /// 字符串 - /// - public static string SerializeToJson(this T obj) - { - try - { - var str = JsonConvert.SerializeObject(obj); - return str; - } - catch (System.Exception ex) - { - Env.Editor.WriteMessage(ex.Message); - return String.Empty; - } - } - /// - /// 序列化为Json - /// - /// - /// 对象 - /// 文件路径名 - /// 成功返回True - public static bool SerializeToJson(this T obj, string filename) - { - try - { - var str = JsonConvert.SerializeObject(obj); - File.WriteAllText(filename, str); - return true; - } - catch (System.Exception ex) - { - Env.Editor.WriteMessage(ex.Message); - return false; - } - } - /// - /// Json反序列化 - /// - /// - /// 文件路径名 - /// - public static T? DeserializeFromJsonFile(this string filename) - { - try - { - var str = File.ReadAllText(filename); - return JsonConvert.DeserializeObject(str); - } - catch (System.Exception ex) - { - Env.Editor.WriteMessage(ex.Message); - return default; - } - } - /// - /// Json反序列化 - /// - /// - /// JSON字符串 - /// - public static T? DeserializeFromJson(this string jsonString) - { - try - { - return JsonConvert.DeserializeObject(jsonString); - } - catch (System.Exception ex) - { - Env.Editor.WriteMessage(ex.Message); - return default; - } - } - /// - /// 序列化为二进制数组 - /// - /// - /// 对象 - /// 二进制数组 - public static byte[]? SerializeToByteArray(this T obj) - { - try - { - var str = JsonConvert.SerializeObject(obj); - return System.Text.Encoding.UTF8.GetBytes(str); - } - catch (System.Exception ex) - { - Env.Editor.WriteMessage(ex.Message); - return default; - } - } - /// - /// 序列化为二进制数组 - /// - /// - /// 对象 - /// 文件路径名 - /// 成功返回True - public static bool SerializeToByteArray(this T obj, string filename) - { - try - { - var str = JsonConvert.SerializeObject(obj); - var byteArray = System.Text.Encoding.UTF8.GetBytes(str); - File.WriteAllBytes(filename, byteArray); - return true; - } - catch (System.Exception ex) - { - Env.Editor.WriteMessage(ex.Message); - return false; - } - } - /// - /// 二进制数组反序列化 - /// - /// - /// 二进制数组 - /// - public static T? DeserializeTo(this byte[] byteArray) - { - try - { - var str = System.Text.Encoding.UTF8.GetString(byteArray); - return JsonConvert.DeserializeObject(str); - } - catch (System.Exception ex) - { - Env.Editor.WriteMessage(ex.Message); - return default; - } - } - /// - /// 二进制数组反序列化 - /// - /// - /// 二进制数组 - /// - public static T? DeserializeFromByteArrayFile(this string filename) - { - try - { - var byteArray = File.ReadAllBytes(filename); - var str = System.Text.Encoding.UTF8.GetString(byteArray); - return JsonConvert.DeserializeObject(str); - } - catch (System.Exception ex) - { - Env.Editor.WriteMessage(ex.Message); - return default; - } - } - } -} -- Gitee From 024840477506c5d10a4c8b41e5d5844c70986f30 Mon Sep 17 00:00:00 2001 From: DYH <1742647821@qq.com> Date: Wed, 15 Jun 2022 04:29:25 +0000 Subject: [PATCH 241/675] update src/IFoxCAD.Cad/ExtensionMethod/GeometryEx.cs. --- src/IFoxCAD.Cad/ExtensionMethod/GeometryEx.cs | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/src/IFoxCAD.Cad/ExtensionMethod/GeometryEx.cs b/src/IFoxCAD.Cad/ExtensionMethod/GeometryEx.cs index bc4231b..2c1a45a 100644 --- a/src/IFoxCAD.Cad/ExtensionMethod/GeometryEx.cs +++ b/src/IFoxCAD.Cad/ExtensionMethod/GeometryEx.cs @@ -608,6 +608,16 @@ public static Point3d Point3d(this Point2d pt) { return new Point3d(pt.X, pt.Y, 0); } + /// + /// 将二维点转换为三维点 + /// + /// 二维点 + /// Z值 + /// 三维点 + public static Point3d Point3d(this Point2d pt,double z) + { + return new Point3d(pt.X, pt.Y, z); + } /// /// 获取两个点之间的中点 -- Gitee From 746549c0295084cdc3066db041830d7d5ea61be6 Mon Sep 17 00:00:00 2001 From: liuqihong <540762622@qq.com> Date: Wed, 15 Jun 2022 20:36:37 +0800 Subject: [PATCH 242/675] =?UTF-8?q?=E7=A7=BB=E9=99=A4=E5=B1=B1=E4=BA=BA?= =?UTF-8?q?=E7=9A=84=E5=9B=9B=E5=8F=89=E6=A0=91?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/IFoxCAD.Cad/Algorithms/QuadTree.cs | 529 ------------------------- 1 file changed, 529 deletions(-) delete mode 100644 src/IFoxCAD.Cad/Algorithms/QuadTree.cs diff --git a/src/IFoxCAD.Cad/Algorithms/QuadTree.cs b/src/IFoxCAD.Cad/Algorithms/QuadTree.cs deleted file mode 100644 index 81cdbcb..0000000 --- a/src/IFoxCAD.Cad/Algorithms/QuadTree.cs +++ /dev/null @@ -1,529 +0,0 @@ -namespace IFoxCAD.Cad; -using Box = System.Windows.Rect; - -using System.Windows; - -//---------------------------------------------------------------- -// Copyright (c) Microsoft Corporation. All rights reserved. -//---------------------------------------------------------------- - -/// -/// This class efficiently stores and retrieves arbitrarily sized and positioned -/// objects in a quad-tree data structure. This can be used to do efficient hit -/// detection or visiblility checks on objects in a virtualized canvas. -/// The object does not need to implement any special interface because the Box Bounds -/// of those objects is handled as a separate argument to Insert. -/// 此类有效地存储和检索四叉树数据结构中任意大小和位置的对象。 这可用于对虚拟画布中的对象进行有效的命中检测或可见性检查。 -/// 该对象不需要实现任何特殊接口,因为这些对象的 Box Bounds 被作为单独的参数进行插入。 -/// -public class QuadTree where T : class -{ - - - Box bounds; // overall bounds we are indexing. 索引的总体范围 - Quadrant? root; - IDictionary? table; - - - - /// - /// This determines the overall quad-tree indexing strategy, changing this bounds - /// is expensive since it has to re-divide the entire thing - like a re-hash operation. - /// 这决定了整个四叉树索引策略,改变这个界限是昂贵的,因为它必须重新划分整个事物 - 就像重新散列操作一样。 - /// - public Box Bounds - { - get { return this.bounds; } - set { this.bounds = value; ReIndex(); } - } - - /// - /// Insert a node with given bounds into this QuadTree. - /// 将具有给定边界的节点插入此 QuadTree。 - /// - /// The node to insert 要插入的节点 - /// The bounds of this node 这个节点的边界 - public void Insert(T node, Box bounds) - { - if (this.bounds.Width == 0 || this.bounds.Height == 0) - { - throw new ArgumentException("Bounds must be not zero"); - } - if (bounds.Width == 0 || bounds.Height == 0) - { - throw new ArgumentException("Bounds must be not zero"); - } - if (this.root == null) - { - this.root = new Quadrant(null, this.bounds); - } - - Quadrant parent = this.root.Insert(node, bounds); - - if (this.table == null) - { - this.table = new Dictionary(); - } - this.table[node] = parent; - - - } - - /// - /// Get a list of the nodes that intersect the given bounds. - /// 获取与给定边界相交的节点列表。 - /// - /// The bounds to test 给定的边界 - /// - /// List of zero or mode nodes found inside the given bounds - /// 在给定范围内找到零个或多个节点的列表 - /// - public IEnumerable GetNodesInside(Box bounds) - { - foreach (QuadNode n in GetNodes(bounds)) - { - yield return n.Node; - } - } - - /// - /// Get a list of the nodes that intersect the given bounds. - /// 获取与给定边界相交的节点列表。 - /// - /// The bounds to test 给定的边界 - /// 如果此象限中有任何节点与给定边界相交,则返回 true。 - public bool HasNodesInside(Box bounds) - { - if (this.root == null) - { - return false; - } - return this.root.HasIntersectingNodes(bounds); - } - - /// - /// Get list of nodes that intersect the given bounds. - /// - /// The bounds to test - /// The list of nodes intersecting the given bounds - IEnumerable GetNodes(Box bounds) - { - List result = new(); - if (this.root != null) - { - this.root.GetIntersectingNodes(result, bounds); - } - return result; - } - - /// - /// Remove the given node from this QuadTree. - /// - /// The node to remove - /// True if the node was found and removed. - public bool Remove(T node) - { - if (this.table != null) - { - //Quadrant parent = null; - if (table.TryGetValue(node, out Quadrant parent)) - { - parent.RemoveNode(node); - this.table.Remove(node); - return true; - } - } - return false; - } - - /// - /// Rebuild all the Quadrants according to the current QuadTree Bounds. - /// - void ReIndex() - { - this.root = null; - foreach (QuadNode n in GetNodes(this.bounds)) - { - Insert(n.Node, n.Bounds); - } - } - - /// - /// Each node stored in the tree has a position, width & height. - /// - internal class QuadNode - { - Box bounds; - QuadNode? next; // linked in a circular list. - T node; // the actual visual object being stored here. - - /// - /// Construct new QuadNode to wrap the given node with given bounds - /// - /// The node - /// The bounds of that node - public QuadNode(T node, Box bounds) - { - this.node = node; - this.bounds = bounds; - } - - /// - /// The node - /// - public T Node - { - get { return this.node; } - set { this.node = value; } - } - - /// - /// The Box bounds of the node - /// - public Box Bounds - { - get { return this.bounds; } - } - - /// - /// QuadNodes form a linked list in the Quadrant. - /// - public QuadNode? Next - { - get { return this.next; } - set { this.next = value; } - } - } - - - /// - /// The canvas is split up into four Quadrants and objects are stored in the quadrant that contains them - /// and each quadrant is split up into four child Quadrants recurrsively. Objects that overlap more than - /// one quadrant are stored in the this.nodes list for this Quadrant. - /// 画布分为四个象限,对象存储在包含它们的象限中,每个象限递归地分成四个子象限。 重叠多个象限的对象存储在此象限的 this.nodes 列表中。 - /// - internal class Quadrant - { - readonly Quadrant? parent; - Box bounds; // quadrant bounds. - - QuadNode nodes; // nodes that overlap the sub quadrant boundaries. - - // The quadrant is subdivided when nodes are inserted that are - // completely contained within those subdivisions. - Quadrant topLeft; - Quadrant topRight; - Quadrant bottomLeft; - Quadrant bottomRight; - - - - /// - /// Construct new Quadrant with a given bounds all nodes stored inside this quadrant - /// will fit inside this bounds. - /// - /// The parent quadrant (if any) - /// The bounds of this quadrant - public Quadrant(Quadrant? parent, Box bounds) - { - this.parent = parent; - //Fx.Assert(bounds.Width != 0 && bounds.Height != 0, "Cannot have empty bound"); - if (bounds.Width == 0 || bounds.Height == 0) - { - throw new ArgumentException("Bounds must be not zero"); - } - this.bounds = bounds; - } - - /// - /// The parent Quadrant or null if this is the root - /// - internal Quadrant? Parent - { - get { return this.parent; } - } - - /// - /// The bounds of this quadrant - /// - internal Box Bounds - { - get { return this.bounds; } - } - - /// - /// Insert the given node - /// - /// The node - /// The bounds of that node - /// - internal Quadrant Insert(T node, Box bounds) - { - - - if (bounds.Width == 0 || bounds.Height == 0) - { - throw new ArgumentException("Bounds must be not zero"); - } - - Quadrant toInsert = this; - while (true) - { - double w = toInsert.bounds.Width / 2; - if (w < 1) - { - w = 1; - } - double h = toInsert.bounds.Height / 2; - if (h < 1) - { - h = 1; - } - - // assumption that the Box struct is almost as fast as doing the operations - // manually since Box is a value type. - - Box topLeft = new Box(toInsert.bounds.Left, toInsert.bounds.Top, w, h); - Box topRight = new Box(toInsert.bounds.Left + w, toInsert.bounds.Top, w, h); - Box bottomLeft = new Box(toInsert.bounds.Left, toInsert.bounds.Top + h, w, h); - Box bottomRight = new Box(toInsert.bounds.Left + w, toInsert.bounds.Top + h, w, h); - - Quadrant? child = null; - - // See if any child quadrants completely contain this node. - if (topLeft.Contains(bounds)) - { - if (toInsert.topLeft == null) - { - toInsert.topLeft = new Quadrant(toInsert, topLeft); - } - child = toInsert.topLeft; - } - else if (topRight.Contains(bounds)) - { - if (toInsert.topRight == null) - { - toInsert.topRight = new Quadrant(toInsert, topRight); - } - child = toInsert.topRight; - } - else if (bottomLeft.Contains(bounds)) - { - if (toInsert.bottomLeft == null) - { - toInsert.bottomLeft = new Quadrant(toInsert, bottomLeft); - } - child = toInsert.bottomLeft; - } - else if (bottomRight.Contains(bounds)) - { - if (toInsert.bottomRight == null) - { - toInsert.bottomRight = new Quadrant(toInsert, bottomRight); - } - child = toInsert.bottomRight; - } - - if (child != null) - { - toInsert = child; - } - else - { - QuadNode n = new QuadNode(node, bounds); - if (toInsert.nodes == null) - { - n.Next = n; - } - else - { - // link up in circular link list. - QuadNode x = toInsert.nodes; - n.Next = x.Next; - x.Next = n; - } - toInsert.nodes = n; - return toInsert; - } - } - } - - /// - /// Returns all nodes in this quadrant that intersect the given bounds. - /// The nodes are returned in pretty much random order as far as the caller is concerned. - /// - /// List of nodes found in the given bounds - /// The bounds that contains the nodes you want returned - internal void GetIntersectingNodes(List nodes, Box bounds) - { - if (bounds.IsEmpty) return; - double w = this.bounds.Width / 2; - double h = this.bounds.Height / 2; - - // assumption that the Box struct is almost as fast as doing the operations - // manually since Box is a value type. - - Box topLeft = new Box(this.bounds.Left, this.bounds.Top, w, h); - Box topRight = new Box(this.bounds.Left + w, this.bounds.Top, w, h); - Box bottomLeft = new Box(this.bounds.Left, this.bounds.Top + h, w, h); - Box bottomRight = new Box(this.bounds.Left + w, this.bounds.Top + h, w, h); - - // See if any child quadrants completely contain this node. - if (topLeft.IntersectsWith(bounds) && this.topLeft != null) - { - this.topLeft.GetIntersectingNodes(nodes, bounds); - } - - if (topRight.IntersectsWith(bounds) && this.topRight != null) - { - this.topRight.GetIntersectingNodes(nodes, bounds); - } - - if (bottomLeft.IntersectsWith(bounds) && this.bottomLeft != null) - { - this.bottomLeft.GetIntersectingNodes(nodes, bounds); - } - - if (bottomRight.IntersectsWith(bounds) && this.bottomRight != null) - { - this.bottomRight.GetIntersectingNodes(nodes, bounds); - } - - GetIntersectingNodes(this.nodes, nodes, bounds); - } - - /// - /// Walk the given linked list of QuadNodes and check them against the given bounds. - /// Add all nodes that intersect the bounds in to the list. - /// - /// The last QuadNode in a circularly linked list - /// The resulting nodes are added to this list - /// The bounds to test against each node - static void GetIntersectingNodes(QuadNode last, List nodes, Box bounds) - { - if (last != null) - { - QuadNode? n = last; - do - { - n = n.Next; // first node. - if (n.Bounds.IntersectsWith(bounds)) - { - nodes.Add(n); - } - } while (n != last); - } - } - - /// - /// Return true if there are any nodes in this Quadrant that intersect the given bounds. - /// 如果此象限中有任何节点与给定边界相交,则返回 true。 - /// - /// The bounds to test - /// boolean - internal bool HasIntersectingNodes(Box bounds) - { - if (bounds.IsEmpty) return false; - double w = this.bounds.Width / 2; - double h = this.bounds.Height / 2; - - // assumption that the Box struct is almost as fast as doing the operations - // manually since Box is a value type. - - Box topLeft = new Box(this.bounds.Left, this.bounds.Top, w, h); - Box topRight = new Box(this.bounds.Left + w, this.bounds.Top, w, h); - Box bottomLeft = new Box(this.bounds.Left, this.bounds.Top + h, w, h); - Box bottomRight = new Box(this.bounds.Left + w, this.bounds.Top + h, w, h); - - bool found = false; - - // See if any child quadrants completely contain this node. - if (topLeft.IntersectsWith(bounds) && this.topLeft != null) - { - found = this.topLeft.HasIntersectingNodes(bounds); - } - - if (!found && topRight.IntersectsWith(bounds) && this.topRight != null) - { - found = this.topRight.HasIntersectingNodes(bounds); - } - - if (!found && bottomLeft.IntersectsWith(bounds) && this.bottomLeft != null) - { - found = this.bottomLeft.HasIntersectingNodes(bounds); - } - - if (!found && bottomRight.IntersectsWith(bounds) && this.bottomRight != null) - { - found = this.bottomRight.HasIntersectingNodes(bounds); - } - if (!found) - { - found = HasIntersectingNodes(this.nodes, bounds); - } - return found; - } - - /// - /// Walk the given linked list and test each node against the given bounds/ - /// - /// The last node in the circularly linked list. - /// Bounds to test - /// Return true if a node in the list intersects the bounds - static bool HasIntersectingNodes(QuadNode last, Box bounds) - { - if (last != null) - { - QuadNode n = last; - do - { - n = n.Next; // first node. - if (n.Bounds.IntersectsWith(bounds)) - { - return true; - } - } while (n != last); - } - return false; - } - - /// - /// Remove the given node from this Quadrant. - /// - /// The node to remove - /// Returns true if the node was found and removed. - internal bool RemoveNode(T node) - { - bool rc = false; - if (this.nodes != null) - { - QuadNode p = this.nodes; - while (p.Next.Node != node && p.Next != this.nodes) - { - p = p.Next; - } - if (p.Next.Node == node) - { - rc = true; - QuadNode n = p.Next; - if (p == n) - { - // list goes to empty - this.nodes = null; - } - else - { - if (this.nodes == n) this.nodes = p; - p.Next = n.Next; - } - } - } - return rc; - } - - } - -} - - - -- Gitee From ae1bfc909b22767a71d788a45d1d4182585ec3fc Mon Sep 17 00:00:00 2001 From: liuqihong <540762622@qq.com> Date: Wed, 15 Jun 2022 21:40:20 +0800 Subject: [PATCH 243/675] =?UTF-8?q?=E5=8A=A0=E5=85=A5=E6=83=8A=E6=83=8A?= =?UTF-8?q?=E7=9A=84=E5=9B=9B=E5=8F=89=E6=A0=91?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/IFoxCAD.Cad/QuadTree/IHasRect.cs | 19 + src/IFoxCAD.Cad/QuadTree/QuadTree.cs | 247 ++++++ src/IFoxCAD.Cad/QuadTree/QuadTreeEvn.cs | 25 + src/IFoxCAD.Cad/QuadTree/QuadTreeNode.cs | 823 ++++++++++++++++++ .../QuadTree/QuadTreeSelectMode.cs | 22 + .../{Algorithms => QuadTree}/Rect.cs | 0 src/IFoxCAD.Cad/QuadTree/Utility.cs | 32 + 7 files changed, 1168 insertions(+) create mode 100644 src/IFoxCAD.Cad/QuadTree/IHasRect.cs create mode 100644 src/IFoxCAD.Cad/QuadTree/QuadTree.cs create mode 100644 src/IFoxCAD.Cad/QuadTree/QuadTreeEvn.cs create mode 100644 src/IFoxCAD.Cad/QuadTree/QuadTreeNode.cs create mode 100644 src/IFoxCAD.Cad/QuadTree/QuadTreeSelectMode.cs rename src/IFoxCAD.Cad/{Algorithms => QuadTree}/Rect.cs (100%) create mode 100644 src/IFoxCAD.Cad/QuadTree/Utility.cs diff --git a/src/IFoxCAD.Cad/QuadTree/IHasRect.cs b/src/IFoxCAD.Cad/QuadTree/IHasRect.cs new file mode 100644 index 0000000..3e4b208 --- /dev/null +++ b/src/IFoxCAD.Cad/QuadTree/IHasRect.cs @@ -0,0 +1,19 @@ +namespace IFoxCAD.Cad +{ + //因为我想用字段...所以从接口改成了类 + /// + /// 约束传入的对象都要含有包围盒的定义 + /// + public class IHasRect: Rect + { + /// + /// 颜色 + /// + public System.Drawing.Color Color; + + /// + /// 是一个点 + /// + public bool IsPoint; + } +} diff --git a/src/IFoxCAD.Cad/QuadTree/QuadTree.cs b/src/IFoxCAD.Cad/QuadTree/QuadTree.cs new file mode 100644 index 0000000..9f372c9 --- /dev/null +++ b/src/IFoxCAD.Cad/QuadTree/QuadTree.cs @@ -0,0 +1,247 @@ +using System; +using System.Collections.Generic; + +/* + * 四叉树维基百科 http://en.wikipedia.org/wiki/Quadtree + * 四叉树是一种分区空间的算法,更快找出内部或外部给定区域. + * 通过一个正交矩形边界进行中心点分裂四个正交矩形, + * 插入时候会一直分裂四个正交矩形, + * 当分裂四个节点都无法单独拥有 图元包围盒 就停止分裂,并且你属于这四个节点的父亲. + * (不包含就是面积少了,就这么一句话看代码看半天), + * 还可以通过限制树的深度实现加速. + * + * 第一版: https://www.codeproject.com/script/Articles/ViewDownloads.aspx?aid=30535 + * + * 第二版: 找邻居 + * https://blog.csdn.net/dive_shallow/article/details/112438050 + * https://geidav.wordpress.com/2017/12/02/advanced-octrees-4-finding-neighbor-nodes/ + * + * 1.根节点:控制根节点从而控制所有节点 + * 2.子节点:包含自身根节点,插入矩形的时候进行递归分裂自身,和实现查找. + * 3.接口:约束都要有正交矩形,否则无法调用"包含"方法 + * 4.选择模式:模仿cad的窗选和框选 + */ +namespace IFoxCAD.Cad +{ + /// + /// 根节点控制器 + /// + /// 类型接口约束必须有正交矩形 + public class QuadTree where TEntity : IHasRect + { + #region 成员 + /// + /// 根节点 + /// + QuadTreeNode _rootNode; + + /// + /// 四叉树节点的数目 + /// + public int Count { get => _rootNode.CountSubTree; } + #endregion + + #region 构造 + /// + /// 四叉树根节点控制器 + /// + /// 四叉树矩形范围 + /// 最后一个节点有最小面积 + public QuadTree(Rect rect) + { + _rootNode = new QuadTreeNode(rect, null, 0);//初始化根节点 + } + #endregion + + #region 方法 + /// + /// 通过根节点插入数据项 + /// + /// + public void Insert(TEntity ent) + { + while (!_rootNode.Contains(ent)) + { + /* + * 四叉树插入时候,如果超出根边界,就需要扩展 + * 扩展时候有一个要求,当前边界要作为扩展边界的一个象限,也就是反向分裂 + * + * 创建新根,计算原根在新根的位置, + * 替换指针:获取新分裂的节点的父节点,判断它哪个儿子是它, + * 替换之后可能仍然不包含图元边界,再循环计算. + */ + var sq_Left = _rootNode._X; + var sq_Botton = _rootNode._Y; + var sq_Right = _rootNode._Right; + var sq_Top = _rootNode._Top; + if (ent._Y >= _rootNode._Y)//上↑增殖 + { + if (ent._X >= _rootNode._X) + { + //右上↗增殖 + sq_Right += _rootNode.Width; + sq_Top += _rootNode.Height; + } + else + { + //左上↖增殖 + sq_Left -= _rootNode.Width; + sq_Top += _rootNode.Height; + } + } + else//在下↓ + { + if (ent._X >= _rootNode._X) + { + //右下↘增殖 + sq_Right += _rootNode.Width; + sq_Botton -= _rootNode.Height; + } + else + { + //左下↙增殖 + sq_Left -= _rootNode.Width; + sq_Botton -= _rootNode.Height; + } + } + var rectSquare = new Rect(sq_Left, sq_Botton, sq_Right, sq_Top); + + //四叉树的旧根要作为四分之一插入 + //初始化根节点 + var newRoot = new QuadTreeNode(rectSquare, null, 0); + + //新根中计算原根 + //把 旧根节点 连接到 新根节点 上面,然后新根成为根 + var insert = newRoot.Insert(_rootNode); + if (insert is null) + throw new ArgumentException("新根尺寸不对"); + if (!insert.Equals(_rootNode)) + throw new ArgumentNullException("新旧节点大小不一致,无法连接"); + + var insPar = insert.Parent; + _rootNode.Parent = insPar; + if (insPar is null) + return; + + if (_rootNode.Equals(insPar.RightTopTree)) + insPar.RightTopTree = _rootNode; + else if (_rootNode.Equals(insPar.RightBottomTree)) + insPar.RightBottomTree = _rootNode; + else if (_rootNode.Equals(insPar.LeftBottomTree)) + insPar.LeftBottomTree = _rootNode; + else if (_rootNode.Equals(insPar.LeftTopTree)) + insPar.LeftTopTree = _rootNode; + else + throw new ArgumentNullException("新节点不对,无法连接"); + + //其后的子节点层数全部增加层数, + //要加多少层取决于当前根边界属于新根边界的所在层 + var depth = insert.Depth; + if (depth == 0) + throw new ArgumentNullException("插入节点是0,造成错误"); + _rootNode.ForEach(node => + { + node.Depth += depth; + return false; + }); + + //交换根控制 + _rootNode = newRoot; + } + + _rootNode.Insert(ent); + } + + /// + /// 通过根节点插入数据项 + /// + /// + public void Insert(List ents) + { + /* + * 图元点 是不分裂空间的,应该插入到目前对应的最小空间. + * 因此插入时候,先让其他图元插入时候分裂节点, + * 最后再把 图元点 们插入. + */ + var pointEntityNum = new List(); + for (int i = 0; i < ents.Count; i++) + { + var ent = ents[i]; + if (ent.IsPoint) + { + pointEntityNum.Add(i); + continue; + } + Insert(ent); + } + for (int i = 0; i < pointEntityNum.Count; i++) + Insert(ents[pointEntityNum[i]]); + } + + /// + /// 查询四叉树,返回给定区域的数据项 + /// + /// 矩形选区查询 + /// + public List Query(Rect rect, QuadTreeSelectMode selectMode = QuadTreeSelectMode.IntersectsWith) + { + QuadTreeEvn.SelectMode = selectMode; + var results = new List(); + _rootNode.Query(rect, results); + return results; + } + + /// + /// 删除子节点 + /// + /// 根据范围删除 + public void Remove(Rect rect) + { + _rootNode.Remove(rect); + } + + /// + /// 删除子节点 + /// + /// 根据图元删除 + public void Remove(TEntity ent) + { + _rootNode.Remove(ent); + } + + /// + /// 找到附近节点图元 + /// + [Obsolete("找附近节点的并不是最近的图元")] + public TEntity? FindNeibor(Rect rect, QuadTreeFindMode findMode) + { + return _rootNode.FindNeibor(rect, findMode); + } + + /// + /// 找到附近图元 + /// + /// + /// + public TEntity? FindNearEntity(Rect rect) + { + return _rootNode.FindNearEntity(rect); + } + + /// + /// 执行四叉树中特定的行为 + /// + /// + public void ForEach(QTAction action) + { + _rootNode.ForEach(action); + } + + /// + /// 委托:四叉树节点上执行一个操作 + /// + /// + public delegate bool QTAction(QuadTreeNode obj); + #endregion + } +} \ No newline at end of file diff --git a/src/IFoxCAD.Cad/QuadTree/QuadTreeEvn.cs b/src/IFoxCAD.Cad/QuadTree/QuadTreeEvn.cs new file mode 100644 index 0000000..743f652 --- /dev/null +++ b/src/IFoxCAD.Cad/QuadTree/QuadTreeEvn.cs @@ -0,0 +1,25 @@ +namespace IFoxCAD.Cad +{ + public class QuadTreeEvn + { + /// + /// 最小的节点有一个面积(一定要大于0) + /// + public static double MinArea = 1e-6; + + /// + /// 选择模式 + /// + public static QuadTreeSelectMode SelectMode; + + /// + /// 最大深度 + /// + public static int QuadTreeMaximumDepth = 2046; + + /// + /// 节点内容超过就分裂 + /// + public static int QuadTreeContentsCountSplit = 20; + } +} \ No newline at end of file diff --git a/src/IFoxCAD.Cad/QuadTree/QuadTreeNode.cs b/src/IFoxCAD.Cad/QuadTree/QuadTreeNode.cs new file mode 100644 index 0000000..dfc4033 --- /dev/null +++ b/src/IFoxCAD.Cad/QuadTree/QuadTreeNode.cs @@ -0,0 +1,823 @@ +using System; +using System.Collections.Generic; +using System.Linq; + +namespace IFoxCAD.Cad +{ + /// + /// 子节点 + /// + /// + public class QuadTreeNode + : Rect + where TEntity : IHasRect + { + #region 成员 + /// + /// 子节点:第一象限:右上↗ + /// + public QuadTreeNode? RightTopTree; + /// + /// 子节点:第二象限:左上↖ + /// + public QuadTreeNode? LeftTopTree; + /// + /// 子节点:第三象限:左下↙ + /// + public QuadTreeNode? LeftBottomTree; + /// + /// 子节点:第四象限:右下↘ + /// + public QuadTreeNode? RightBottomTree; + /// + /// 所有子节点 + /// + QuadTreeNode[] Nodes + { + get + { + return new QuadTreeNode[] + { + RightTopTree!, + LeftTopTree!, + LeftBottomTree!, + RightBottomTree!, + }; + } + } + /// + /// 所有子节点是空的 + /// + bool NodesIsEmpty => RightTopTree is null && LeftTopTree is null && LeftBottomTree is null && RightBottomTree is null; + + /// + /// 父节点 + /// + public QuadTreeNode? Parent; + /// + /// 节点的在四叉树的深度 + /// + public int Depth; + + // 注意,内容没有限制:这不是 impement 四叉树的标准方法 + /// (节点图元是交叉线压着的,并不是矩形范围内全部,因为这是四叉树的特性决定) + /// + /// 本节点:内容 + /// + public List Contents; + + /// + /// 本节点和旗下所有子节点:内容群 + /// + public void ContentsSubTree(List results) + { + if (Contents is null) + return; + results.AddRange(Contents); + var nodes = Nodes; + for (int i = 0; i < nodes.Length; i++) + nodes[i]?.ContentsSubTree(results); + } + + /// + /// 本节点和旗下所有子节点:内容群数量 + /// + public int CountSubTree + { + get + { + if (Contents is null) + return 0; + int count = Contents.Count; + + var nodes = Nodes; + for (int i = 0; i < nodes.Length; i++) + { + var node = nodes[i]; + if (node is null) + continue; + count += node.CountSubTree; + } + return count; + } + } + #endregion + + #region 构造 + /// + /// 四叉树节点 + /// + /// 当前节点边界 + /// 父节点 + /// 节点深度 + public QuadTreeNode(Rect box, QuadTreeNode? parent, int depth) + { + _X = box._X; + _Y = box._Y; + _Right = box._Right; + _Top = box._Top; + + Parent = parent; + Depth = depth; + Contents = new(); + } + #endregion + + #region 增 + /// + /// 将原有节点插入用 + /// + /// + internal QuadTreeNode? Insert(Rect rect) + { + if (!Contains(rect)) + return null; + + //四叉树分裂,将当前节点分为四个子节点 + if (NodesIsEmpty) + CreateChildren(); + + //当前节点边界 包含 图元包围盒 就插入 + //退出递归:4个节点都不完全包含 + //4个节点的上层 + var nodes = Nodes; + for (int i = 0; i < nodes.Length; i++) + { + var node = nodes[i]; + if (node is null) + continue; + + if (node.Equals(rect)) + { + rect = node; + return node.Insert(rect); + } + } + return this; + } + + /// + /// 将数据项递归插入四叉树 + /// + /// + public QuadTreeNode? Insert(TEntity ent) + { + if (!Contains(ent)) + { + //Debug.WriteLine("不在四叉树边界范围"); + //Trace.WriteLine("不在四叉树边界范围"); + return null; + } + + if (ent.IsPoint) + { + //找到最后一层包含它的节点,然后加入它 + //因此是跳过分裂矩形的,以免造成无限递归 + var minNode = GetMinNode(ent); + minNode.Contents.Add(ent); + return minNode; + } + +#if true2 + //方案二: + //内容数超过才分裂,防止树深度过高,但是多选过滤时候慢一点 + if (Contents.Count > QuadTreeEvn.QuadTreeContentsCountSplit) + { + //分裂出四个子节点 + if (_nodesIsEmpty) + { + CreateChildren(); + //分裂之后将当前层的内容扔到四个子节点, + //如果被压着,那么就不会扔到下面 + for (int i = Contents.Count - 1; i >= 0; i--) + { + var minNode = GetMinNode(Contents[i].Box); + minNode.Contents.Add(Contents[i]); + Contents.RemoveAt(i); + } + } + else + { + //没有分裂的话,就递归 + //退出递归:4个节点都不完全包含,内容就是他们的父亲 + var nodes = _Nodes; + for (int i = 0; i < nodes.Length; i++) + { + var node = nodes[i]; + if (node is null) + continue; + + //这里需要中断.(匿名方法ForEach无法中断,会造成父节点加入内容) + if (node.Contains(ent)) + return node.Insert(ent); + } + } + } +#else + //方案一:分裂到最细节点 + + //分裂出四个子节点 + if (NodesIsEmpty) + CreateChildren(); + + //4个子节点开始递归 + //退出递归:4个节点都不完全包含,内容就是他们的父亲 + var nodes = Nodes; + for (int i = 0; i < nodes.Length; i++) + { + var node = nodes[i]; + if (node is null) + continue; + //这里需要中断.(匿名方法ForEach无法中断,会造成父节点加入内容) + if (node.Contains(ent)) + return node.Insert(ent); + } +#endif + + //为什么要用容器? + //相同包围盒或者四叉树分割线压着多个. + this.Contents.Add(ent); + return this; + } + + /// + /// 创建子节点 + /// + void CreateChildren() + { + // 最小面积控制节点深度,但是这样可能导致树分成高,引起爆栈 + if (Depth > QuadTreeEvn.QuadTreeMaximumDepth) + return; + var recs = RectSplit(this); + var de = Depth + 1; + RightTopTree = new QuadTreeNode(recs[0], this, de); + LeftTopTree = new QuadTreeNode(recs[1], this, de); + LeftBottomTree = new QuadTreeNode(recs[2], this, de); + RightBottomTree = new QuadTreeNode(recs[3], this, de); + } + + /// + /// 矩形分裂为四个 + /// + /// + /// + Rect[] RectSplit(Rect box) + { + var halfWidth = box.Width / 2.0; + var halfHeight = box.Height / 2.0; + + var upperRight = new Rect(box._X + halfWidth, box._Y + halfHeight, box._Right, box._Top); + var upperLeft = new Rect(box._X, box._Y + halfHeight, box._Right - halfWidth, box._Top); + var lowerleft = new Rect(box._X, box._Y, box._Right - halfWidth, box._Top - halfHeight);//基础 + var lowerRight = new Rect(box._X + halfWidth, box._Y, box._Right, box._Top - halfHeight); + + //依照象限顺序输出 + return new Rect[] { upperRight, upperLeft, lowerleft, lowerRight }; + } + #endregion + + #region 删 + /// + /// 删除图元 + /// + /// 根据图元删除 + public bool Remove(TEntity easeEnt) + { + //通过图元id删除无疑是非常低效的, + //1.相当于在所有的容器查找它,但是移除只会移除一次, + // 因此必须要求图元只会加入一次,才能中断检索剩余分支. + //2.这个代价还是太高,因此我们还是要默认条件,图元载入一次之后,不再改动. + //3.不再改动也不太合理,因为cad图元还是可以修改的 + + //1.处理内容 + if (Contents.Remove(easeEnt)) + { + if (CountSubTree == 0) + this.Clear(this); + return true; + } + + //2.递归子节点移除 + var nodes = Nodes; + for (int i = 0; i < nodes.Length; i++) + { + var node = nodes[i]; + if (node is null) + continue; + if (node.Remove(easeEnt)) //递归进入子节点删除内容 + return true; //删除成功就中断其他节点的搜索 + } + return false; + } + + /// + /// 递归进入最下层节点,然后开始清理 + /// + /// + void Clear(QuadTreeNode node) + { + var nodes = Nodes; + for (int i = 0; i < nodes.Length; i++) + nodes[i]?.Clear(nodes[i]); + + node.Contents.Clear(); + //node.Contents = null;//重复加入时候会出错 + node.RightTopTree = null; + node.LeftTopTree = null; + node.LeftBottomTree = null; + node.RightBottomTree = null; + node.Parent = null; + //node.Box = zoreRect; + } + + /// + /// 删除子节点内容 + /// + /// 根据范围删除 + public void Remove(Rect queryArea) + { + //本节点内容移除 + if (Contents is not null && Contents.Count > 0)//从最上层的根节点开始进入 + { + for (int i = Contents.Count - 1; i >= 0; i--) + { + var ent = Contents[i]; + //移除之后,如果容器是0,那么这里不能直接 Contents=null, + //因为此节点下面可能还有节点, + //需要判断了其后数量0才可以清理. + //否则其后还有内容,那么此节点就是仍然可以用的. + if (queryArea.Contains(ent)) + Contents.Remove(ent); + } + } + + //同插入一样 + //跳到指定节点再搜索这个节点下面的图元 + var nodes = Nodes; + for (int i = 0; i < nodes.Length; i++) + { + var node = nodes[i]; + if (node is null) + continue; + if (node.NodesIsEmpty) + continue; + + //此节点边界 完全包含 查询区域,则转到该节点,并跳过其余节点(打断此循环) + if (node.Contains(queryArea)) + { + node.Remove(queryArea); + break; + } + //查询区域 完全包含 此节点边界,提取此节点全部内容 + //跳过分析碰撞,并继续循环搜索其他节点 + if (queryArea.Contains(node)) + { + node.Clear(node); + continue; + } + //查询区域 与 此节点四边形边线碰撞 查询该四边形中,并继续循环搜索其他节点 + //1,角点碰撞 2,边界碰撞 + if (node.IntersectsWith(queryArea)) + node.Remove(queryArea); + } + + //本节点内容移除之后,旗下还有内容的话, + //会跳过此处,再进入子节点进行递归,直到最后一个节点 + if (CountSubTree == 0) + Clear(this); + } + #endregion + + #region 查 + /// + /// 查询范围内的实体 + /// + /// 查询矩形 + /// + public void Query(Rect queryArea, List results) + { + GetCurrentContents(queryArea, results); + + //遍历子节点 + var nodes = Nodes; + for (int i = 0; i < nodes.Length; i++) + { + var node = nodes[i]; + if (node is null) + continue; + //子节点的4个子节点都是空的, + //那么表示元素会在子节点这一层啊... + if (node.NodesIsEmpty) + continue; + + //此节点边界 完全包含 查询区域,则转到该节点,并跳过其余节点(打断此循环) + if (node.Contains(queryArea)) + { + node.Query(queryArea, results); + break; + } + //查询区域 完全包含 此节点边界,提取此节点全部内容 + //跳过分析碰撞,并继续循环搜索其他节点 + if (queryArea.Contains(node)) + { + node.ContentsSubTree(results); + continue; + } + //查询区域 与 此节点四边形边线碰撞 查询该四边形中,并继续循环搜索其他节点 + //1,角点碰撞 2,边界碰撞 + if (node.IntersectsWith(queryArea)) + node.Query(queryArea, results); + } + } + + /// + /// 获取本节点内容 + /// + /// + /// + void GetCurrentContents(Rect queryArea, List results) + { + //遍历当前节点内容,加入方式取决于碰撞模式 + if (QuadTreeEvn.SelectMode == QuadTreeSelectMode.IntersectsWith) + { + for (int i = 0; i < Contents.Count; i++) + { + var ent = Contents[i]; + if (queryArea.IntersectsWith(ent)) + results.Add(ent); + } + } + else + { + for (int i = 0; i < Contents.Count; i++) + { + var ent = Contents[i]; + if (queryArea.Contains(ent)) + results.Add(ent); + } + } + } + + /// + /// 找临近图元 + /// + /// 查找矩形 + /// + public TEntity? FindNearEntity(Rect queryArea) + { + TEntity? resultEntity = default; + //1.找到 查找矩形 所在的节点,利用此节点的矩形. + var queryNode = GetMinNode(queryArea); + var queryAreaCenter = queryArea.CenterPoint; + + //2.从根开始搜索 + // 如果搜索父亲的父亲的...内容群,它不是距离最近的,只是节点(亲属关系)最近 + // 储存找过的<图元,距离> + var entDic = new Dictionary(); + + var old = QuadTreeEvn.SelectMode; + QuadTreeEvn.SelectMode = QuadTreeSelectMode.IntersectsWith; + while (true) + { + //循环找父节点大小 + var hw = queryNode.Width / 2.0; + var hh = queryNode.Height / 2.0; + //3.利用选区中心扩展一个节点边界大小的矩形.从而选择图元 + // 再判断图元的与目标的距离,找到最小距离,即为最近 + var minPt = new Point2d(queryAreaCenter.X - hw, queryAreaCenter.Y - hh); + var maxPt = new Point2d(queryAreaCenter.X + hw, queryAreaCenter.Y + hh); + var ents = new List(); + Query(new Rect(minPt, maxPt), ents); + for (int i = 0; i < ents.Count; i++) + { + var ent = ents[i]; + if (entDic.ContainsKey(ent)) + continue; + var dis = ent.CenterPoint.GetDistanceTo(queryAreaCenter); + if (dis > 1e-6)//剔除本身 + entDic.Add(ent, dis); + } + if (entDic.Count > 0) + { + resultEntity = entDic.OrderBy(a => a.Value).First().Key; + break; + } + if (queryNode.Parent is null)//最顶层就退出 + break; + queryNode = queryNode.Parent;//利用父节点矩形进行变大选区 + } + QuadTreeEvn.SelectMode = old; + return resultEntity; + } + + /// + /// 找临近节点的图元 + /// + /// 查找矩形 + /// 查找什么方向 + /// + [Obsolete("找附近节点的并不是最近的图元")] + public TEntity? FindNeibor(Rect queryArea, QuadTreeFindMode findMode) + { + TEntity? resultEntity = default; + //1.找到 查找矩形 所在的节点,利用此节点的矩形. + //2.利用节点矩形是分裂的特点,边和边必然贴合. + //3.找到方向 findMode 拥有的节点,然后查找节点的内容 + var queryNode = GetMinNode(queryArea); + + bool whileFlag = true; + //同一个节点可能包含邻居,因为四叉树的加入是图元压线, + //那么就在这里搜就得了,用中心点决定空间位置 + //但是本空间的图元可能都比它矮,无法满足条件 + if (queryNode.CountSubTree > 1) + { + resultEntity = GetNearestNeighbor(queryNode, findMode, queryArea); + if (resultEntity is null || resultEntity.CenterPoint == queryArea.CenterPoint) + whileFlag = true; + else + whileFlag = false; + } + + while (whileFlag) + { + //同一个父节点是临近的,优先获取 兄弟节点 的内容. + //循环了第二次是北方兄弟的节点, + //但是这不是一个找到临近图元的方法, + //因为临近的可能是父亲的父亲的父亲...另一个函数 FindNearEntity 写 + //本方案也仅仅作为找北方节点 + var parent = queryNode.Parent; + if (parent is not null) + { + switch (findMode) + { + case QuadTreeFindMode.Top: + { + //下格才获取上格,否则导致做了无用功,上格就直接获取邻居了 + if (parent.LeftBottomTree == queryNode || parent.RightBottomTree == queryNode) + resultEntity = GetNearestNeighbor(parent, findMode, queryArea); + } + break; + case QuadTreeFindMode.Bottom: + { + if (parent.LeftTopTree == queryNode || parent.RightTopTree == queryNode) + resultEntity = GetNearestNeighbor(parent, findMode, queryArea); + } + break; + case QuadTreeFindMode.Left: + { + if (parent.RightTopTree == queryNode || parent.RightBottomTree == queryNode) + resultEntity = GetNearestNeighbor(parent, findMode, queryArea); + } + break; + case QuadTreeFindMode.Right: + { + if (parent.LeftTopTree == queryNode || parent.LeftBottomTree == queryNode) + resultEntity = GetNearestNeighbor(parent, findMode, queryArea); + } + break; + } + } + if (resultEntity is not null) + break; + + //通过 所在节点 找 邻居节点, + //拿到 邻居节点 下面的所有内容(图元) + //内容可能是空的,再从邻居那往北找...如果找到了四叉树最外层,仍然没有内容,退出循环 + var neiborNode = FindNeiborNode(queryNode, findMode); + if (neiborNode is null) + continue; + if (neiborNode.CountSubTree > 0) + { + resultEntity = GetNearestNeighbor(neiborNode, findMode, queryArea); + break; + } + if (neiborNode.Parent is null)//如果找到了四叉树最外层,仍然没有内容,退出循环 + break; + queryNode = neiborNode; + } + + return resultEntity; + } + + /// + /// 查找节点的(本内容和子内容)与(查找面积)矩形中点对比,找到最近一个内容 + /// + /// 查找面积 + /// 查找方向 + /// 查找节点 + /// + TEntity? GetNearestNeighbor(QuadTreeNode queryNode, QuadTreeFindMode findMode, Rect queryArea) + { + TEntity? results = default; + + var lst = new List(); + var qcent = queryArea.CenterPoint; + + switch (findMode) + { + case QuadTreeFindMode.Top: + { + //取出Y比queryArea的还大的一个,是最近的那个 + var qy = qcent.Y; + queryNode.ContentsSubTree(lst); + lst.ForEach(ent => + { + if (ent.CenterPoint.Y > qy) + lst.Add(ent); + }); + lst = lst.OrderBy(ent => ent.CenterPoint.Y).ToList(); + } + break; + case QuadTreeFindMode.Bottom: + { + var qy = qcent.Y; + queryNode.ContentsSubTree(lst); + lst.ForEach(ent => + { + if (ent.CenterPoint.Y < qy) + lst.Add(ent); + }); + lst = lst.OrderByDescending(ent => ent.CenterPoint.Y).ToList(); + } + break; + case QuadTreeFindMode.Left: + { + var qx = qcent.Y; + queryNode.ContentsSubTree(lst); + lst.ForEach(ent => + { + if (ent.CenterPoint.X > qx) + lst.Add(ent); + }); + lst = lst.OrderBy(ent => ent.CenterPoint.X).ToList(); + } + break; + case QuadTreeFindMode.Right: + { + var qx = qcent.Y; + queryNode.ContentsSubTree(lst); + lst.ForEach(ent => + { + if (ent.CenterPoint.X < qx) + lst.Add(ent); + }); + lst = lst.OrderByDescending(ent => ent.CenterPoint.X).ToList(); + } + break; + } + + if (lst.Count > 0) + return lst[0];//可能就是本体重叠 + return results; + } + + /// + /// 找包含它的最小分支 + /// + /// 查询的矩形 + /// 节点 + QuadTreeNode GetMinNode(Rect queryArea) + { + var nodes = Nodes; + for (int i = 0; i < nodes.Length; i++) + { + var node = nodes[i]; + if (node is null) + continue; + + //边界包含查询面积,那么再递归查询, + //直到最后四个都不包含,那么上一个就是图元所在节点 + if (node.Contains(queryArea)) + return node.GetMinNode(queryArea);//中断后面的范围,才可以返回正确的 + } + return this; + } + + /// + /// 四叉树找邻居节点(相同或更大) + /// + /// 源节点 + /// 方向 + /// + QuadTreeNode? FindNeiborNode(QuadTreeNode tar, QuadTreeFindMode findMode) + { + var parent = tar.Parent; + if (parent is null) + return null; + switch (findMode) + { + case QuadTreeFindMode.Top: + { + //判断当前节点在父节点的位置,如果是在 下格 就取对应的 上格 + if (tar == parent.LeftBottomTree) + return parent.LeftTopTree; + if (tar == parent.RightBottomTree) + return parent.RightTopTree; + //否则就是上格 + //找父节点的北方邻居..也就是在爷节点上面找 + //递归,此时必然是 下格,就必然返回 上格,然后退出递归 + var parentNeibor = FindNeiborNode(parent, QuadTreeFindMode.Top); + //父节点的北方邻居 无 子节点 + if (parentNeibor is null || parentNeibor.RightTopTree is null) + return parentNeibor;//返回父节点的北方邻居,比较大 + //父节点的北方邻居 有 子节点,剩下条件就只有这两 + + // 如果直接返回,那么是(相同或更大), + // 而找邻近图元需要的是这个(相同或更大)下面的图元,在外面对这个格子内图元用坐标分析就好了 + if (tar == parent.LeftTopTree) + return parentNeibor.LeftBottomTree; + return parentNeibor.RightBottomTree; + } + case QuadTreeFindMode.Bottom: + { + if (tar == parent.LeftTopTree) + return parent.LeftBottomTree; + if (tar == parent.RightTopTree) + return parent.RightBottomTree; + var parentNeibor = FindNeiborNode(parent, QuadTreeFindMode.Bottom); + if (parentNeibor is null || parentNeibor.RightTopTree is null) + return parentNeibor; + if (tar == parent.LeftBottomTree) + return parentNeibor.LeftTopTree; + return parentNeibor.RightTopTree; + } + case QuadTreeFindMode.Right: + { + if (tar == parent.LeftTopTree) + return parent.RightTopTree; + if (tar == parent.LeftBottomTree) + return parent.RightBottomTree; + var parentNeibor = FindNeiborNode(parent, QuadTreeFindMode.Right); + if (tar == parent.RightTopTree) + return parentNeibor?.LeftTopTree; + return parentNeibor?.LeftBottomTree; + } + case QuadTreeFindMode.Left: + { + if (tar == parent.RightTopTree) + return parent.LeftTopTree; + if (tar == parent.RightBottomTree) + return parent.LeftBottomTree; + var parentNeibor = FindNeiborNode(parent, QuadTreeFindMode.Left); + if (tar == parent.LeftTopTree) + return parentNeibor?.RightTopTree; + return parentNeibor?.RightBottomTree; + } + } + return null; + } + #endregion + + #region 改 + /// + /// 所有的点归类到最小包围它的空间 + /// + public void PointsToMinNode() + { + ForEach(node => + { + for (int i = 0; i < node.Contents.Count; i++) + { + var ent = node.Contents[i]; + if (ent.IsPoint) + { + //如果最小包含!=当前,就是没有放在最适合的位置 + var queryNode = GetMinNode(ent); + if (queryNode != node) + { + node.Remove(ent); + queryNode.Contents.Add(ent); + } + } + } + return false; + }); + } + #endregion + + #region 方法 + /// + /// 递归全部节点(提供给根用的,所以是全部) + /// + /// QTAction + public bool ForEach(QuadTree.QTAction action) + { + //执行本节点 + if (action(this)) + return true; + + //递归执行本节点的子节点 + var nodes = Nodes; + for (int i = 0; i < nodes.Length; i++) + { + var node = nodes[i]; + if (node is null) + continue; + if (node.ForEach(action)) + break; + } + return false; + } + #endregion + } +} \ No newline at end of file diff --git a/src/IFoxCAD.Cad/QuadTree/QuadTreeSelectMode.cs b/src/IFoxCAD.Cad/QuadTree/QuadTreeSelectMode.cs new file mode 100644 index 0000000..79cf64f --- /dev/null +++ b/src/IFoxCAD.Cad/QuadTree/QuadTreeSelectMode.cs @@ -0,0 +1,22 @@ +namespace IFoxCAD.Cad +{ + /// + /// 四叉树选择模式 + /// + public enum QuadTreeSelectMode + { + IntersectsWith, //碰撞到就选中 + Contains, //全包含才选中 + } + + /// + /// 四叉树查找方向 + /// + public enum QuadTreeFindMode + { + Top = 1, //上 + Bottom = 2, //下 + Left = 4, //左 + Right = 8, //右 + } +} \ No newline at end of file diff --git a/src/IFoxCAD.Cad/Algorithms/Rect.cs b/src/IFoxCAD.Cad/QuadTree/Rect.cs similarity index 100% rename from src/IFoxCAD.Cad/Algorithms/Rect.cs rename to src/IFoxCAD.Cad/QuadTree/Rect.cs diff --git a/src/IFoxCAD.Cad/QuadTree/Utility.cs b/src/IFoxCAD.Cad/QuadTree/Utility.cs new file mode 100644 index 0000000..aefaa63 --- /dev/null +++ b/src/IFoxCAD.Cad/QuadTree/Utility.cs @@ -0,0 +1,32 @@ +using System; + +namespace IFoxCAD.Cad +{ + public static class Utility + { + public static Random GetRandom() + { + var tick = DateTime.Now.Ticks; + var tickSeeds = (int)(tick & 0xffffffffL) | (int)(tick >> 32); + return new Random(tickSeeds); + } + + /// + /// 随机颜色 + /// + /// + public static System.Drawing.Color RandomColor + { + get + { + var ran = GetRandom(); + int R = ran.Next(255); + int G = ran.Next(255); + int B = ran.Next(255); + B = (R + G > 400) ? R + G - 400 : B;//0 : 380 - R - G; + B = (B > 255) ? 255 : B; + return System.Drawing.Color.FromArgb(R, G, B); + } + } + } +} \ No newline at end of file -- Gitee From 00c366d995d774aac8e5820e50d56d940e0622c9 Mon Sep 17 00:00:00 2001 From: vicwjb <403009819@qq.com> Date: Wed, 15 Jun 2022 23:00:51 +0800 Subject: [PATCH 244/675] =?UTF-8?q?=E5=B0=9D=E8=AF=95=E6=BA=90=E7=A0=81?= =?UTF-8?q?=E5=8C=85?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/IFoxCAD.Basal/CLS/RuntimeHelpers.cs | 8 +++++--- src/IFoxCAD.Basal/IFoxCAD.Basal.csproj | 2 +- tests/TestConsole/Class1.cs | 4 ---- tests/TestConsole/Program.cs | 8 ++------ tests/TestConsole/RuntimeHelpers.cs | 6 ++++-- tests/TestConsole/TestConsole.csproj | 5 ++++- 6 files changed, 16 insertions(+), 17 deletions(-) delete mode 100644 tests/TestConsole/Class1.cs diff --git a/src/IFoxCAD.Basal/CLS/RuntimeHelpers.cs b/src/IFoxCAD.Basal/CLS/RuntimeHelpers.cs index 0e0073b..0c2692a 100644 --- a/src/IFoxCAD.Basal/CLS/RuntimeHelpers.cs +++ b/src/IFoxCAD.Basal/CLS/RuntimeHelpers.cs @@ -1,11 +1,11 @@ -#if NET35 +//#if NET35 // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. namespace System.Runtime.CompilerServices { - internal static class RuntimeHelpers + public static class RuntimeHelpers { /// /// Slices the specified array using the specified range. @@ -35,6 +35,8 @@ public static T[] GetSubArray(T[] array!!, Range range) return dest; } } + + } } -#endif \ No newline at end of file +//#endif \ No newline at end of file diff --git a/src/IFoxCAD.Basal/IFoxCAD.Basal.csproj b/src/IFoxCAD.Basal/IFoxCAD.Basal.csproj index 0b5abcd..52f00bf 100644 --- a/src/IFoxCAD.Basal/IFoxCAD.Basal.csproj +++ b/src/IFoxCAD.Basal/IFoxCAD.Basal.csproj @@ -4,7 +4,7 @@ net35;net40;net45 true enable - 0.3.5 + 0.3.5.1 InspireFunction xsfhlzh;vicwjb 基于.NET的二次开发基本类库 diff --git a/tests/TestConsole/Class1.cs b/tests/TestConsole/Class1.cs deleted file mode 100644 index 505dce9..0000000 --- a/tests/TestConsole/Class1.cs +++ /dev/null @@ -1,4 +0,0 @@ - - -namespace TestConsole; - diff --git a/tests/TestConsole/Program.cs b/tests/TestConsole/Program.cs index 576993a..40ae94e 100644 --- a/tests/TestConsole/Program.cs +++ b/tests/TestConsole/Program.cs @@ -1,11 +1,7 @@ // See https://aka.ms/new-console-template for more information using System; -using System.Linq; -using System.ComponentModel; -//using IFoxCAD.Basal; +using IFoxCAD.Basal; //using static TestConsole.Program; -using TestConsole; -using System.Runtime.CompilerServices; /*下面是元组测试*/ var valuetuple = (1, 2); @@ -21,5 +17,5 @@ foreach (var item in range) { Console.WriteLine(item); -} +} Console.ReadLine(); diff --git a/tests/TestConsole/RuntimeHelpers.cs b/tests/TestConsole/RuntimeHelpers.cs index 36eaa8f..9e09b29 100644 --- a/tests/TestConsole/RuntimeHelpers.cs +++ b/tests/TestConsole/RuntimeHelpers.cs @@ -3,7 +3,7 @@ // See the LICENSE file in the project root for more information. // 如果要用range的语法比如 a[1..3],那么需要将本文件复制到你的项目里 - +#if true namespace System.Runtime.CompilerServices { internal static class RuntimeHelpers @@ -45,4 +45,6 @@ public static T[] GetSubArray(T[] array, Range range) } } } -} \ No newline at end of file +} + +#endif \ No newline at end of file diff --git a/tests/TestConsole/TestConsole.csproj b/tests/TestConsole/TestConsole.csproj index c71c3f4..c809de4 100644 --- a/tests/TestConsole/TestConsole.csproj +++ b/tests/TestConsole/TestConsole.csproj @@ -8,9 +8,12 @@ - + + + + -- Gitee From 339f50e6e97c54174a9417492c0574b615db96a9 Mon Sep 17 00:00:00 2001 From: vicwjb <403009819@qq.com> Date: Wed, 15 Jun 2022 23:12:35 +0800 Subject: [PATCH 245/675] =?UTF-8?q?=E8=B0=83=E6=95=B4=E6=96=87=E4=BB=B6?= =?UTF-8?q?=E7=BB=93=E6=9E=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/IFoxCAD.Cad/Algorithms/{ => Graph}/Graph.cs | 0 src/IFoxCAD.Cad/Algorithms/{ => Graph}/IGraph.cs | 0 src/IFoxCAD.Cad/{ => Algorithms}/QuadTree/IHasRect.cs | 0 src/IFoxCAD.Cad/{ => Algorithms}/QuadTree/QuadTree.cs | 0 src/IFoxCAD.Cad/{ => Algorithms}/QuadTree/QuadTreeEvn.cs | 0 src/IFoxCAD.Cad/{ => Algorithms}/QuadTree/QuadTreeNode.cs | 0 src/IFoxCAD.Cad/{ => Algorithms}/QuadTree/QuadTreeSelectMode.cs | 0 src/IFoxCAD.Cad/{ => Algorithms}/QuadTree/Rect.cs | 0 src/IFoxCAD.Cad/{ => Algorithms}/QuadTree/Utility.cs | 0 9 files changed, 0 insertions(+), 0 deletions(-) rename src/IFoxCAD.Cad/Algorithms/{ => Graph}/Graph.cs (100%) rename src/IFoxCAD.Cad/Algorithms/{ => Graph}/IGraph.cs (100%) rename src/IFoxCAD.Cad/{ => Algorithms}/QuadTree/IHasRect.cs (100%) rename src/IFoxCAD.Cad/{ => Algorithms}/QuadTree/QuadTree.cs (100%) rename src/IFoxCAD.Cad/{ => Algorithms}/QuadTree/QuadTreeEvn.cs (100%) rename src/IFoxCAD.Cad/{ => Algorithms}/QuadTree/QuadTreeNode.cs (100%) rename src/IFoxCAD.Cad/{ => Algorithms}/QuadTree/QuadTreeSelectMode.cs (100%) rename src/IFoxCAD.Cad/{ => Algorithms}/QuadTree/Rect.cs (100%) rename src/IFoxCAD.Cad/{ => Algorithms}/QuadTree/Utility.cs (100%) diff --git a/src/IFoxCAD.Cad/Algorithms/Graph.cs b/src/IFoxCAD.Cad/Algorithms/Graph/Graph.cs similarity index 100% rename from src/IFoxCAD.Cad/Algorithms/Graph.cs rename to src/IFoxCAD.Cad/Algorithms/Graph/Graph.cs diff --git a/src/IFoxCAD.Cad/Algorithms/IGraph.cs b/src/IFoxCAD.Cad/Algorithms/Graph/IGraph.cs similarity index 100% rename from src/IFoxCAD.Cad/Algorithms/IGraph.cs rename to src/IFoxCAD.Cad/Algorithms/Graph/IGraph.cs diff --git a/src/IFoxCAD.Cad/QuadTree/IHasRect.cs b/src/IFoxCAD.Cad/Algorithms/QuadTree/IHasRect.cs similarity index 100% rename from src/IFoxCAD.Cad/QuadTree/IHasRect.cs rename to src/IFoxCAD.Cad/Algorithms/QuadTree/IHasRect.cs diff --git a/src/IFoxCAD.Cad/QuadTree/QuadTree.cs b/src/IFoxCAD.Cad/Algorithms/QuadTree/QuadTree.cs similarity index 100% rename from src/IFoxCAD.Cad/QuadTree/QuadTree.cs rename to src/IFoxCAD.Cad/Algorithms/QuadTree/QuadTree.cs diff --git a/src/IFoxCAD.Cad/QuadTree/QuadTreeEvn.cs b/src/IFoxCAD.Cad/Algorithms/QuadTree/QuadTreeEvn.cs similarity index 100% rename from src/IFoxCAD.Cad/QuadTree/QuadTreeEvn.cs rename to src/IFoxCAD.Cad/Algorithms/QuadTree/QuadTreeEvn.cs diff --git a/src/IFoxCAD.Cad/QuadTree/QuadTreeNode.cs b/src/IFoxCAD.Cad/Algorithms/QuadTree/QuadTreeNode.cs similarity index 100% rename from src/IFoxCAD.Cad/QuadTree/QuadTreeNode.cs rename to src/IFoxCAD.Cad/Algorithms/QuadTree/QuadTreeNode.cs diff --git a/src/IFoxCAD.Cad/QuadTree/QuadTreeSelectMode.cs b/src/IFoxCAD.Cad/Algorithms/QuadTree/QuadTreeSelectMode.cs similarity index 100% rename from src/IFoxCAD.Cad/QuadTree/QuadTreeSelectMode.cs rename to src/IFoxCAD.Cad/Algorithms/QuadTree/QuadTreeSelectMode.cs diff --git a/src/IFoxCAD.Cad/QuadTree/Rect.cs b/src/IFoxCAD.Cad/Algorithms/QuadTree/Rect.cs similarity index 100% rename from src/IFoxCAD.Cad/QuadTree/Rect.cs rename to src/IFoxCAD.Cad/Algorithms/QuadTree/Rect.cs diff --git a/src/IFoxCAD.Cad/QuadTree/Utility.cs b/src/IFoxCAD.Cad/Algorithms/QuadTree/Utility.cs similarity index 100% rename from src/IFoxCAD.Cad/QuadTree/Utility.cs rename to src/IFoxCAD.Cad/Algorithms/QuadTree/Utility.cs -- Gitee From 644da790190823c1ea3b757ca0d7403fb953a19d Mon Sep 17 00:00:00 2001 From: vicwjb <403009819@qq.com> Date: Wed, 15 Jun 2022 23:16:01 +0800 Subject: [PATCH 246/675] =?UTF-8?q?=E5=8F=91=E5=B8=83v0.3.6=E7=89=88?= =?UTF-8?q?=E6=9C=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/IFoxCAD.Cad/IFoxCAD.Cad.csproj | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/IFoxCAD.Cad/IFoxCAD.Cad.csproj b/src/IFoxCAD.Cad/IFoxCAD.Cad.csproj index 060e009..7a36b3e 100644 --- a/src/IFoxCAD.Cad/IFoxCAD.Cad.csproj +++ b/src/IFoxCAD.Cad/IFoxCAD.Cad.csproj @@ -6,7 +6,7 @@ enable true true - 0.3.5.1 + 0.3.6 InspireFunction xsfhlzh;vicwjb 基于.NET的Cad二次开发类库 @@ -15,7 +15,7 @@ https://gitee.com/inspirefunction/ifoxcad.git git IFoxCAD;CAD;AutoCad;C#;NET - 直接集成元组和索引切片. + 增加四叉树. true true preview -- Gitee From bf6831bb69d22b876c93824e47fa54b9beb48219 Mon Sep 17 00:00:00 2001 From: liuqihong <540762622@qq.com> Date: Thu, 16 Jun 2022 00:49:50 +0800 Subject: [PATCH 247/675] =?UTF-8?q?=E5=A2=9E=E5=8A=A0=E9=9A=8F=E6=9C=BA?= =?UTF-8?q?=E7=A7=8D=E5=AD=90=E6=B3=A8=E9=87=8A?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/IFoxCAD.Cad/Algorithms/QuadTree/Utility.cs | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/src/IFoxCAD.Cad/Algorithms/QuadTree/Utility.cs b/src/IFoxCAD.Cad/Algorithms/QuadTree/Utility.cs index aefaa63..5d55215 100644 --- a/src/IFoxCAD.Cad/Algorithms/QuadTree/Utility.cs +++ b/src/IFoxCAD.Cad/Algorithms/QuadTree/Utility.cs @@ -4,9 +4,23 @@ namespace IFoxCAD.Cad { public static class Utility { + /// + /// 带有随机种子的随机数 + /// 为什么这样写随机种子呢 + /// + /// public static Random GetRandom() { var tick = DateTime.Now.Ticks; + // Convert.ToString(int.MaxValue, 2)输出二进制 + // Convert.ToString(long.MaxValue, 2)输出二进制,刚好长一倍 + // Convert.ToString(0xffffffffL, 2)就是int.MaxValue再按位多1; + + // Convert.ToString(0xffffffffL>>2, 2)就是截断了0xffffffffL的位范围,63-32=31位数量; + // 64位少最高位(符号)和最低位>>到32位范围 + // (&是尽可能为0) (|是尽可能为1) + + //64位的1位尽可能为0;这31位就保持不变;再右移高位31位过来低位,尽可能保持低位为1 var tickSeeds = (int)(tick & 0xffffffffL) | (int)(tick >> 32); return new Random(tickSeeds); } -- Gitee From dede1aff371a339805fd425faffde75cce008d6f Mon Sep 17 00:00:00 2001 From: liuqihong <540762622@qq.com> Date: Thu, 16 Jun 2022 01:22:09 +0800 Subject: [PATCH 248/675] =?UTF-8?q?=E5=A2=9E=E5=8A=A0=E9=9A=8F=E6=9C=BA?= =?UTF-8?q?=E7=A7=8D=E5=AD=90=E6=B3=A8=E9=87=8A2?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Algorithms/QuadTree/Utility.cs | 22 ++++++++++++------- 1 file changed, 14 insertions(+), 8 deletions(-) diff --git a/src/IFoxCAD.Cad/Algorithms/QuadTree/Utility.cs b/src/IFoxCAD.Cad/Algorithms/QuadTree/Utility.cs index 5d55215..6b948b0 100644 --- a/src/IFoxCAD.Cad/Algorithms/QuadTree/Utility.cs +++ b/src/IFoxCAD.Cad/Algorithms/QuadTree/Utility.cs @@ -12,15 +12,21 @@ public static class Utility public static Random GetRandom() { var tick = DateTime.Now.Ticks; - // Convert.ToString(int.MaxValue, 2)输出二进制 - // Convert.ToString(long.MaxValue, 2)输出二进制,刚好长一倍 - // Convert.ToString(0xffffffffL, 2)就是int.MaxValue再按位多1; - // Convert.ToString(0xffffffffL>>2, 2)就是截断了0xffffffffL的位范围,63-32=31位数量; - // 64位少最高位(符号)和最低位>>到32位范围 - // (&是尽可能为0) (|是尽可能为1) - - //64位的1位尽可能为0;这31位就保持不变;再右移高位31位过来低位,尽可能保持低位为1 + /* + * | 高位 | 低位 | + * Convert.ToString(int.MaxValue, 2)输出二进制 "1111111111111111111111111111111" 31个;最高位是符号位,所以少1位 + * Convert.ToString(long.MaxValue,2)输出二进制,刚好长一倍 "11111111111111111111111111111111 1111111111111111111111111111111" 63个;最高位是符号位,所以少1位 + * Convert.ToString(0xffffffffL, 2)int.MaxValue再按位多1 "1 1111111111111111111111111111111" 32个;前面的0不会打印出来 + * + * Convert.ToString(long.MaxValue>>32, 2)相当于平移高位的到低位范围,也就是上面少打印的二进制 + * 验证右移是不是高位保留,答案是 + * var a = Convert.ToInt64("101111111111111111111111111111111111111111111111111111111111111", 2); + * Convert.ToString(a >> 32,2); + * + * (&是尽可能为0) (|是尽可能为1) + * 64位的1位尽可能为0;这31位就保持不变;再右移高位31位过来低位,尽可能保持低位为1 + */ var tickSeeds = (int)(tick & 0xffffffffL) | (int)(tick >> 32); return new Random(tickSeeds); } -- Gitee From d36293ffefb7a321bb777b352a1040370c75f783 Mon Sep 17 00:00:00 2001 From: liuqihong <540762622@qq.com> Date: Thu, 16 Jun 2022 01:27:20 +0800 Subject: [PATCH 249/675] =?UTF-8?q?=E5=A2=9E=E5=8A=A0=E9=9A=8F=E6=9C=BA?= =?UTF-8?q?=E7=A7=8D=E5=AD=90=E6=B3=A8=E9=87=8A3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/IFoxCAD.Cad/Algorithms/QuadTree/Utility.cs | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/IFoxCAD.Cad/Algorithms/QuadTree/Utility.cs b/src/IFoxCAD.Cad/Algorithms/QuadTree/Utility.cs index 6b948b0..b446ede 100644 --- a/src/IFoxCAD.Cad/Algorithms/QuadTree/Utility.cs +++ b/src/IFoxCAD.Cad/Algorithms/QuadTree/Utility.cs @@ -14,7 +14,7 @@ public static Random GetRandom() var tick = DateTime.Now.Ticks; /* - * | 高位 | 低位 | + * | 高位64位 | 低位32位 | * Convert.ToString(int.MaxValue, 2)输出二进制 "1111111111111111111111111111111" 31个;最高位是符号位,所以少1位 * Convert.ToString(long.MaxValue,2)输出二进制,刚好长一倍 "11111111111111111111111111111111 1111111111111111111111111111111" 63个;最高位是符号位,所以少1位 * Convert.ToString(0xffffffffL, 2)int.MaxValue再按位多1 "1 1111111111111111111111111111111" 32个;前面的0不会打印出来 @@ -25,7 +25,8 @@ public static Random GetRandom() * Convert.ToString(a >> 32,2); * * (&是尽可能为0) (|是尽可能为1) - * 64位的1位尽可能为0;这31位就保持不变;再右移高位31位过来低位,尽可能保持低位为1 + * 32位符号位尽可能为0;再右移高位来低位,使得低位尽可能为1...那它含义何在呢?随机数尽可能大值? + * */ var tickSeeds = (int)(tick & 0xffffffffL) | (int)(tick >> 32); return new Random(tickSeeds); -- Gitee From ca48e5677e09471e99f5b1e9dbd04341ff6d0a82 Mon Sep 17 00:00:00 2001 From: liuqihong <540762622@qq.com> Date: Thu, 16 Jun 2022 02:08:24 +0800 Subject: [PATCH 250/675] =?UTF-8?q?=E5=A2=9E=E5=8A=A0=E9=9A=8F=E6=9C=BA?= =?UTF-8?q?=E7=A7=8D=E5=AD=90=E6=B3=A8=E9=87=8A4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/IFoxCAD.Cad/Algorithms/QuadTree/Utility.cs | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/src/IFoxCAD.Cad/Algorithms/QuadTree/Utility.cs b/src/IFoxCAD.Cad/Algorithms/QuadTree/Utility.cs index b446ede..11dbdf3 100644 --- a/src/IFoxCAD.Cad/Algorithms/QuadTree/Utility.cs +++ b/src/IFoxCAD.Cad/Algorithms/QuadTree/Utility.cs @@ -24,10 +24,13 @@ public static Random GetRandom() * var a = Convert.ToInt64("101111111111111111111111111111111111111111111111111111111111111", 2); * Convert.ToString(a >> 32,2); * - * (&是尽可能为0) (|是尽可能为1) - * 32位符号位尽可能为0;再右移高位来低位,使得低位尽可能为1...那它含义何在呢?随机数尽可能大值? - * + * 按位与&是保证符号位肯定是1,其他尽可能为0,高位被去掉只是max&0的原因,强转才是去掉高位...带来第一次随机性 + * (int)(long.MaxValue & 0xffffffffL) | (int)(long.MaxValue >> 32); + * Convert.ToString(long.MaxValue & 0xffffffffL, 2)//去掉高位:"11111111111111111111111111111111" 32个,再强转int + * Convert.ToString((long.MaxValue >> 32), 2) //去掉低位: "1111111111111111111111111111111" 31个,再强转int + * 按位或|是尽可能为1...带来第二次随机性 */ + var tickSeeds = (int)(tick & 0xffffffffL) | (int)(tick >> 32); return new Random(tickSeeds); } -- Gitee From d78828516d234c97e74966833345fa83ee4fb1d9 Mon Sep 17 00:00:00 2001 From: liuqihong <540762622@qq.com> Date: Thu, 16 Jun 2022 22:52:44 +0800 Subject: [PATCH 251/675] =?UTF-8?q?=E5=8A=A0=E5=85=A5=E5=9B=9B=E5=8F=89?= =?UTF-8?q?=E6=A0=91=E5=9B=BE=E5=85=83?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Algorithms/QuadTree/CadEntity.cs | 29 +++++++++++++++++++ .../Algorithms/QuadTree/IHasRect.cs | 4 +-- .../Algorithms/QuadTree/QuadTreeEvn.cs | 6 ++-- src/IFoxCAD.Cad/Algorithms/QuadTree/Rect.cs | 7 +++++ 4 files changed, 42 insertions(+), 4 deletions(-) create mode 100644 src/IFoxCAD.Cad/Algorithms/QuadTree/CadEntity.cs diff --git a/src/IFoxCAD.Cad/Algorithms/QuadTree/CadEntity.cs b/src/IFoxCAD.Cad/Algorithms/QuadTree/CadEntity.cs new file mode 100644 index 0000000..8f2a090 --- /dev/null +++ b/src/IFoxCAD.Cad/Algorithms/QuadTree/CadEntity.cs @@ -0,0 +1,29 @@ +namespace IFoxCAD.Cad; + +/// +/// 四叉树图元 +/// +public class CadEntity : IHasRect, IComparable +{ + public ObjectId ObjectId; + public new bool IsPoint => Area == 0; + public List? Link;//碰撞链 + + /// + /// 四叉树图元 + /// + /// 图元id + /// 包围盒 + public CadEntity(ObjectId objectId, Rect box) + { + ObjectId = objectId; + _X = box._X; + _Y = box._Y; + _Top = box._Top; + _Right = box._Right; + } + public int CompareTo(CadEntity other) + { + return ObjectId.GetHashCode() ^ other.ObjectId.GetHashCode(); + } +} \ No newline at end of file diff --git a/src/IFoxCAD.Cad/Algorithms/QuadTree/IHasRect.cs b/src/IFoxCAD.Cad/Algorithms/QuadTree/IHasRect.cs index 3e4b208..a28d498 100644 --- a/src/IFoxCAD.Cad/Algorithms/QuadTree/IHasRect.cs +++ b/src/IFoxCAD.Cad/Algorithms/QuadTree/IHasRect.cs @@ -9,11 +9,11 @@ public class IHasRect: Rect /// /// 颜色 /// - public System.Drawing.Color Color; + //public System.Drawing.Color Color; /// /// 是一个点 /// public bool IsPoint; } -} +} \ No newline at end of file diff --git a/src/IFoxCAD.Cad/Algorithms/QuadTree/QuadTreeEvn.cs b/src/IFoxCAD.Cad/Algorithms/QuadTree/QuadTreeEvn.cs index 743f652..4189512 100644 --- a/src/IFoxCAD.Cad/Algorithms/QuadTree/QuadTreeEvn.cs +++ b/src/IFoxCAD.Cad/Algorithms/QuadTree/QuadTreeEvn.cs @@ -1,4 +1,5 @@ -namespace IFoxCAD.Cad +#pragma warning disable CA2211 // 非常量字段应当不可见 +namespace IFoxCAD.Cad { public class QuadTreeEvn { @@ -22,4 +23,5 @@ public class QuadTreeEvn /// public static int QuadTreeContentsCountSplit = 20; } -} \ No newline at end of file +} +#pragma warning restore CA2211 // 非常量字段应当不可见 \ No newline at end of file diff --git a/src/IFoxCAD.Cad/Algorithms/QuadTree/Rect.cs b/src/IFoxCAD.Cad/Algorithms/QuadTree/Rect.cs index 43f45d7..33f045e 100644 --- a/src/IFoxCAD.Cad/Algorithms/QuadTree/Rect.cs +++ b/src/IFoxCAD.Cad/Algorithms/QuadTree/Rect.cs @@ -130,6 +130,13 @@ public double Area #region 构造 public Rect() { } + /// + /// 矩形类 + /// + /// 左 + /// 下 + /// 右 + /// 上 public Rect(double left, double bottom, double right, double top) { _X = left; -- Gitee From 569eaf38b0908ec045e55bb6f4e3803f41ec0406 Mon Sep 17 00:00:00 2001 From: liuqihong <540762622@qq.com> Date: Thu, 16 Jun 2022 22:58:36 +0800 Subject: [PATCH 252/675] =?UTF-8?q?=E5=A2=9E=E5=8A=A0=E6=B3=A8=E9=87=8A?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/IFoxCAD.Cad/Algorithms/QuadTree/Utility.cs | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/IFoxCAD.Cad/Algorithms/QuadTree/Utility.cs b/src/IFoxCAD.Cad/Algorithms/QuadTree/Utility.cs index 11dbdf3..a6afcc5 100644 --- a/src/IFoxCAD.Cad/Algorithms/QuadTree/Utility.cs +++ b/src/IFoxCAD.Cad/Algorithms/QuadTree/Utility.cs @@ -24,11 +24,12 @@ public static Random GetRandom() * var a = Convert.ToInt64("101111111111111111111111111111111111111111111111111111111111111", 2); * Convert.ToString(a >> 32,2); * - * 按位与&是保证符号位肯定是1,其他尽可能为0,高位被去掉只是max&0的原因,强转才是去掉高位...带来第一次随机性 + * 按位与&是保证符号位肯定是1,其他尽可能为0,高位被去掉只是MaxValue&0的原因,强转才是去掉高位..."尽可能"一词带来第一次随机性 * (int)(long.MaxValue & 0xffffffffL) | (int)(long.MaxValue >> 32); * Convert.ToString(long.MaxValue & 0xffffffffL, 2)//去掉高位:"11111111111111111111111111111111" 32个,再强转int * Convert.ToString((long.MaxValue >> 32), 2) //去掉低位: "1111111111111111111111111111111" 31个,再强转int - * 按位或|是尽可能为1...带来第二次随机性 + * 按位或|是尽可能为1..."尽可能"一词带来第二次随机性 + * */ var tickSeeds = (int)(tick & 0xffffffffL) | (int)(tick >> 32); -- Gitee From 98ab36037a5de8f02a2a491f9faa28d3571b8baa Mon Sep 17 00:00:00 2001 From: liuqihong <540762622@qq.com> Date: Thu, 16 Jun 2022 23:04:49 +0800 Subject: [PATCH 253/675] =?UTF-8?q?=E4=BC=98=E5=8C=96=E6=B3=A8=E9=87=8A?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/IFoxCAD.Cad/Algorithms/QuadTree/Utility.cs | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/src/IFoxCAD.Cad/Algorithms/QuadTree/Utility.cs b/src/IFoxCAD.Cad/Algorithms/QuadTree/Utility.cs index a6afcc5..1337d82 100644 --- a/src/IFoxCAD.Cad/Algorithms/QuadTree/Utility.cs +++ b/src/IFoxCAD.Cad/Algorithms/QuadTree/Utility.cs @@ -13,7 +13,8 @@ public static Random GetRandom() { var tick = DateTime.Now.Ticks; - /* + /* + * 知识准备: * | 高位64位 | 低位32位 | * Convert.ToString(int.MaxValue, 2)输出二进制 "1111111111111111111111111111111" 31个;最高位是符号位,所以少1位 * Convert.ToString(long.MaxValue,2)输出二进制,刚好长一倍 "11111111111111111111111111111111 1111111111111111111111111111111" 63个;最高位是符号位,所以少1位 @@ -24,9 +25,12 @@ public static Random GetRandom() * var a = Convert.ToInt64("101111111111111111111111111111111111111111111111111111111111111", 2); * Convert.ToString(a >> 32,2); * - * 按位与&是保证符号位肯定是1,其他尽可能为0,高位被去掉只是MaxValue&0的原因,强转才是去掉高位..."尽可能"一词带来第一次随机性 + * 解释代码: + * 0x01: * (int)(long.MaxValue & 0xffffffffL) | (int)(long.MaxValue >> 32); * Convert.ToString(long.MaxValue & 0xffffffffL, 2)//去掉高位:"11111111111111111111111111111111" 32个,再强转int + * 按位与&是保证符号位肯定是1,其他尽可能为0,高位被去掉只是MaxValue&0的原因,强转才是去掉高位..."尽可能"一词带来第一次随机性 + * 0x02: * Convert.ToString((long.MaxValue >> 32), 2) //去掉低位: "1111111111111111111111111111111" 31个,再强转int * 按位或|是尽可能为1..."尽可能"一词带来第二次随机性 * -- Gitee From 2973574894d22c5afe0fcf640a9c3436594e0615 Mon Sep 17 00:00:00 2001 From: liuqihong <540762622@qq.com> Date: Fri, 17 Jun 2022 01:23:56 +0800 Subject: [PATCH 254/675] =?UTF-8?q?Rect=E5=AE=B9=E5=B7=AE=E6=94=B9?= =?UTF-8?q?=E5=86=99=E9=98=B2=E6=AD=A2=E5=9B=9B=E5=8F=89=E6=A0=91=E8=BE=B9?= =?UTF-8?q?=E7=95=8C=E5=88=A4=E6=96=AD=E5=87=BA=E9=94=99?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/IFoxCAD.Cad/Algorithms/QuadTree/Rect.cs | 75 +++++++++++++++------ 1 file changed, 53 insertions(+), 22 deletions(-) diff --git a/src/IFoxCAD.Cad/Algorithms/QuadTree/Rect.cs b/src/IFoxCAD.Cad/Algorithms/QuadTree/Rect.cs index 33f045e..7ecc37d 100644 --- a/src/IFoxCAD.Cad/Algorithms/QuadTree/Rect.cs +++ b/src/IFoxCAD.Cad/Algorithms/QuadTree/Rect.cs @@ -1,4 +1,6 @@ -namespace IFoxCAD.Cad; +using System.Diagnostics; + +namespace IFoxCAD.Cad; /// /// Linq Distinct 消重比较两点在容差范围内就去除 @@ -41,8 +43,13 @@ public int GetHashCode(Point2d obj) [Serializable] [StructLayout(LayoutKind.Sequential)] +[DebuggerDisplay("{DebuggerDisplay,nq}")] +[DebuggerTypeProxy(typeof(Rect))] public class Rect : IEquatable { + [DebuggerBrowsable(DebuggerBrowsableState.Never)] + private string DebuggerDisplay => ToString("f4"); + #pragma warning disable CA2211 // 非常量字段应当不可见 public static TolerancePoint2d RectTolerance = new(1e-6); public static Tolerance CadTolerance = new(1e-6, 1e-6); @@ -173,11 +180,11 @@ public Rect(Point2d p1, Point2d p3, bool check = false) #region 重载运算符_比较 public override bool Equals(object? obj) { - return this == obj as Rect; + return this.Equals(obj as Rect); } public bool Equals(Rect? b) { - return this == b; + return this.Equals(b, 1e-6); } public static bool operator !=(Rect? a, Rect? b) { @@ -214,28 +221,10 @@ public bool Equals(Rect? b, double tolerance = 1e-6) public override int GetHashCode() { - return (int)_X ^ (int)_Y ^ (int)_Right ^ (int)_Top; + return (((int)_X ^ (int)_Y).GetHashCode() ^ (int)_Right).GetHashCode() ^ (int)_Top; } #endregion - #region 转换类型 -#if !WinForm - // 隐式转换(相当于是重载赋值运算符) - public static implicit operator Rect(System.Windows.Rect rect) - { - return new Rect(rect.Left, rect.Bottom, rect.Right, rect.Top); - } - public static implicit operator Rect(System.Drawing.RectangleF rect) - { - return new Rect(rect.Left, rect.Bottom, rect.Right, rect.Top); - } - public static implicit operator Rect(System.Drawing.Rectangle rect) - { - return new Rect(rect.Left, rect.Bottom, rect.Right, rect.Top); - } -#endif - #endregion - #region 包含 public bool Contains(Point2d Point2d) { @@ -552,4 +541,46 @@ public static void XCollision(List box, } #endregion + + #region 转换类型 +#if !WinForm + // 隐式转换(相当于是重载赋值运算符) + public static implicit operator Rect(System.Windows.Rect rect) + { + return new Rect(rect.Left, rect.Bottom, rect.Right, rect.Top); + } + public static implicit operator Rect(System.Drawing.RectangleF rect) + { + return new Rect(rect.Left, rect.Bottom, rect.Right, rect.Top); + } + public static implicit operator Rect(System.Drawing.Rectangle rect) + { + return new Rect(rect.Left, rect.Bottom, rect.Right, rect.Top); + } +#endif + + #region ToString + public sealed override string ToString() + { + return ToString(null, null); + } + public string ToString(IFormatProvider? provider) + { + return ToString(null, provider); + } + public string ToString(string? format = null, IFormatProvider? formatProvider = null) + { + return $"({_X.ToString(format, formatProvider)},{_Y.ToString(format, formatProvider)})," + + $"({_Right.ToString(format, formatProvider)},{_Top.ToString(format, formatProvider)})"; + + // return $"X={_X.ToString(format, formatProvider)}," + + // $"Y={_Y.ToString(format, formatProvider)}," + + // $"Right={_Right.ToString(format, formatProvider)}," + + // $"Top={_Top.ToString(format, formatProvider)}"; + } + #endregion + + #endregion + + } -- Gitee From d1ed623165a70d93910421e98c40db879fbcd87f Mon Sep 17 00:00:00 2001 From: liuqihong <540762622@qq.com> Date: Fri, 17 Jun 2022 01:25:23 +0800 Subject: [PATCH 255/675] =?UTF-8?q?=E5=A2=9E=E5=8A=A0=E5=9B=9B=E5=8F=89?= =?UTF-8?q?=E6=A0=91=E4=BE=8B=E5=AD=90?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../QuadTree/{CadEntity.cs => QuadEntity.cs} | 8 +- .../Algorithms/QuadTree/QuadTree.cs | 5 +- src/IFoxCAD.Cad/ExtensionMethod/EntityEx.cs | 12 +++ tests/Test/Test.cs | 1 + tests/Test/TestQuadTree.cs | 102 ++++++++++++++++++ 5 files changed, 121 insertions(+), 7 deletions(-) rename src/IFoxCAD.Cad/Algorithms/QuadTree/{CadEntity.cs => QuadEntity.cs} (69%) create mode 100644 tests/Test/TestQuadTree.cs diff --git a/src/IFoxCAD.Cad/Algorithms/QuadTree/CadEntity.cs b/src/IFoxCAD.Cad/Algorithms/QuadTree/QuadEntity.cs similarity index 69% rename from src/IFoxCAD.Cad/Algorithms/QuadTree/CadEntity.cs rename to src/IFoxCAD.Cad/Algorithms/QuadTree/QuadEntity.cs index 8f2a090..c5b0627 100644 --- a/src/IFoxCAD.Cad/Algorithms/QuadTree/CadEntity.cs +++ b/src/IFoxCAD.Cad/Algorithms/QuadTree/QuadEntity.cs @@ -3,18 +3,18 @@ /// /// 四叉树图元 /// -public class CadEntity : IHasRect, IComparable +public class QuadEntity : IHasRect, IComparable { public ObjectId ObjectId; public new bool IsPoint => Area == 0; - public List? Link;//碰撞链 + //public List? Link;//碰撞链...这里外面自己封装去 /// /// 四叉树图元 /// /// 图元id /// 包围盒 - public CadEntity(ObjectId objectId, Rect box) + public QuadEntity(ObjectId objectId, Rect box) { ObjectId = objectId; _X = box._X; @@ -22,7 +22,7 @@ public CadEntity(ObjectId objectId, Rect box) _Top = box._Top; _Right = box._Right; } - public int CompareTo(CadEntity other) + public int CompareTo(QuadEntity other) { return ObjectId.GetHashCode() ^ other.ObjectId.GetHashCode(); } diff --git a/src/IFoxCAD.Cad/Algorithms/QuadTree/QuadTree.cs b/src/IFoxCAD.Cad/Algorithms/QuadTree/QuadTree.cs index 9f372c9..634fcc5 100644 --- a/src/IFoxCAD.Cad/Algorithms/QuadTree/QuadTree.cs +++ b/src/IFoxCAD.Cad/Algorithms/QuadTree/QuadTree.cs @@ -104,14 +104,13 @@ public void Insert(TEntity ent) sq_Botton -= _rootNode.Height; } } + //扩大2次方 var rectSquare = new Rect(sq_Left, sq_Botton, sq_Right, sq_Top); //四叉树的旧根要作为四分之一插入 - //初始化根节点 - var newRoot = new QuadTreeNode(rectSquare, null, 0); - //新根中计算原根 //把 旧根节点 连接到 新根节点 上面,然后新根成为根 + var newRoot = new QuadTreeNode(rectSquare, null, 0); var insert = newRoot.Insert(_rootNode); if (insert is null) throw new ArgumentException("新根尺寸不对"); diff --git a/src/IFoxCAD.Cad/ExtensionMethod/EntityEx.cs b/src/IFoxCAD.Cad/ExtensionMethod/EntityEx.cs index 38e07f7..08b325b 100644 --- a/src/IFoxCAD.Cad/ExtensionMethod/EntityEx.cs +++ b/src/IFoxCAD.Cad/ExtensionMethod/EntityEx.cs @@ -328,6 +328,18 @@ public static Circle CreateCircle(Point3d startPoint, Point3d endPoint) return geArc.ToCircle(); } + /// + /// 通过圆心,半径绘制圆形 + /// + /// 图形数据库 + /// 圆心 + /// 半径 + /// 图形的ObjectId + public static Circle? CreateCircle(Point3d center, double radius, double vex = 0, double vey = 0, double vez = 1) + { + return new Circle(center, new Vector3d(vex, vey, vez), radius);//平面法向量XY方向 + } + #endregion #region 块参照 diff --git a/tests/Test/Test.cs b/tests/Test/Test.cs index 4fc00a7..bc45757 100644 --- a/tests/Test/Test.cs +++ b/tests/Test/Test.cs @@ -158,6 +158,7 @@ public void AddPolyline1() { using var tr = new DBTrans(); Polyline pl = new(); + pl.SetDatabaseDefaults(); pl.AddVertexAt(0, new Point2d(0, 0), 0, 0, 0); pl.AddVertexAt(1, new Point2d(10, 10), 0, 0, 0); pl.AddVertexAt(2, new Point2d(20, 20), 0, 0, 0); diff --git a/tests/Test/TestQuadTree.cs b/tests/Test/TestQuadTree.cs new file mode 100644 index 0000000..f6da0e5 --- /dev/null +++ b/tests/Test/TestQuadTree.cs @@ -0,0 +1,102 @@ +namespace Test; + +public class CadEntity : QuadEntity +{ + //这里加入其他字段 + public int MyProperty { get; set; } + public List? Link;//碰撞链...这里外面自己封装去 + public System.Drawing.Color Color { get; set; } + public CadEntity(ObjectId objectId, Rect box) : base(objectId, box) + { + + } +} + +public class TestQuadTree +{ + [CommandMethod("Test_QuadTree")] + public void Test_QuadTree() + { + using var tr = new DBTrans(); + + Rect dbExt; + //使用数据库边界来进行 + if (!tr.Database.GetValidExtents3d(out Extents3d dbExtent)) + { + //throw new ArgumentException("画一个矩形"); + + //测试时候画个矩形,在矩形内画随机坐标的圆形 + dbExt = new Rect(0, 0, 32525, 32525); + } + else + { + var a = new Point2d(dbExtent.MinPoint.X, dbExtent.MinPoint.Y); + var b = new Point2d(dbExtent.MaxPoint.X, dbExtent.MaxPoint.Y); + dbExt = new Rect(a, b); + } + + //创建四叉树 + var _quadTreeRoot = new QuadTree(dbExt); + + //数据库边界 + var pl = dbExt.ToPoints(); + var databaseBoundary = new List<(Point3d, double, double, double)> + { + (new Point3d(pl[0].X,pl[0].Y,0),0,0,0), + (new Point3d(pl[1].X,pl[1].Y,0),0,0,0), + (new Point3d(pl[2].X,pl[2].Y,0),0,0,0), + (new Point3d(pl[3].X,pl[3].Y,0),0,0,0), + }; + tr.CurrentSpace.AddPline(databaseBoundary); + + int maximumItems = 1_0000; //生成多少个图元,30万图元±0.5秒,导致cad会令undo出错(八叉树深度过大 treemax) + + //随机图元生成 + List ces = new(); //用于随机获取图元 + var allTime = Timer.RunTime(() => { + //生成外边界和随机圆形 + var grc = GenerateRandomCircle(maximumItems, dbExt); + foreach (var ent in grc) + { + //初始化图元颜色 + ent.ColorIndex = 1; //Color.FromRgb(0, 0, 0);//黑色 + var edge = ent.GeometricExtents; + //四叉树数据 + var entRect = new Rect(edge.MinPoint.X, edge.MinPoint.Y, edge.MaxPoint.X, edge.MaxPoint.Y); + var entId = tr.CurrentSpace.AddEntity(ent); + var ce = new CadEntity(entId, entRect); + ce.Color = Utility.RandomColor; + ces.Add(ce); + } + }); + + //测试只加入四叉树的时间 + var insertTime = Timer.RunTime(() => { + _quadTreeRoot.Insert(ces); + }); + + tr.Editor.WriteMessage($"\n加入图元数量:{maximumItems}, 插入四叉树时间:{insertTime / 1000.0}秒, 画圆消耗时间:{allTime / 1000.0}秒"); + } + + /// + /// 创建随机圆形 + /// + /// 创建数量 + /// 数据库边界 + IEnumerable GenerateRandomCircle(int createNumber, Rect dbExt) + { + var x1 = (int)dbExt.X; + var x2 = (int)(dbExt.X + dbExt.Width); + var y1 = (int)dbExt.Y; + var y2 = (int)(dbExt.Y + dbExt.Height); + + var rand = Utility.GetRandom(); + for (int i = 0; i < createNumber; i++) + { + var x = rand.Next(x1, x2) + rand.NextDouble(); + var y = rand.Next(y1, y2) + rand.NextDouble(); + yield return EntityEx.CreateCircle(new Point3d(x, y, 0), rand.Next(1, 100)); //起点,终点 + } + } + +} -- Gitee From 2661a4bcf8124d2355680f0600793fd7f7fb9ef3 Mon Sep 17 00:00:00 2001 From: liuqihong <540762622@qq.com> Date: Fri, 17 Jun 2022 01:54:46 +0800 Subject: [PATCH 256/675] =?UTF-8?q?=E5=A2=9E=E5=8A=A0=E4=B8=80=E4=B8=AA?= =?UTF-8?q?=E4=BB=A3=E7=A0=81=E8=A7=84=E8=8C=83=E9=87=8D=E5=86=99Equals?= =?UTF-8?q?=E7=9A=84=E9=83=A8=E5=88=86**=E9=BB=98=E8=AE=A4=E8=A7=84?= =?UTF-8?q?=E5=88=99=E6=98=AF=3D=3D=E6=98=AF0=E5=AE=B9=E5=B7=AE,Eq?= =?UTF-8?q?=E6=98=AF=E6=9C=89=E5=AE=B9=E5=B7=AE**?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/IFoxCAD.Cad/Algorithms/QuadTree/Rect.cs | 3 ++- .../Edge.cs" | 24 ++++++++++++++++--- 2 files changed, 23 insertions(+), 4 deletions(-) diff --git a/src/IFoxCAD.Cad/Algorithms/QuadTree/Rect.cs b/src/IFoxCAD.Cad/Algorithms/QuadTree/Rect.cs index 7ecc37d..3b7c51d 100644 --- a/src/IFoxCAD.Cad/Algorithms/QuadTree/Rect.cs +++ b/src/IFoxCAD.Cad/Algorithms/QuadTree/Rect.cs @@ -15,6 +15,7 @@ public TolerancePoint2d(double tolerance = 1e-6) public bool Equals(Point2d a, Point2d b)//Point3d是struct不会为null { + /*默认规则是==是0容差,Eq是有容差*/ // 方形限定 // 在 0~1e-6 范围实现 圆形限定 则计算部分在浮点数6位后,没有啥意义 // 在 0~1e-6 范围实现 从时间和CPU消耗来说,圆形限定 都没有 方形限定 的好 @@ -184,7 +185,7 @@ public override bool Equals(object? obj) } public bool Equals(Rect? b) { - return this.Equals(b, 1e-6); + return this.Equals(b, 1e-6);/*默认规则是==是0容差,Eq是有容差*/ } public static bool operator !=(Rect? a, Rect? b) { diff --git "a/src/IFoxCAD.Cad/ExtensionMethod/\351\227\255\345\220\210\350\276\271\347\225\214\347\256\227\346\263\225/Edge.cs" "b/src/IFoxCAD.Cad/ExtensionMethod/\351\227\255\345\220\210\350\276\271\347\225\214\347\256\227\346\263\225/Edge.cs" index 0136531..1f35410 100644 --- "a/src/IFoxCAD.Cad/ExtensionMethod/\351\227\255\345\220\210\350\276\271\347\225\214\347\256\227\346\263\225/Edge.cs" +++ "b/src/IFoxCAD.Cad/ExtensionMethod/\351\227\255\345\220\210\350\276\271\347\225\214\347\256\227\346\263\225/Edge.cs" @@ -103,11 +103,11 @@ public Edge(CompositeCurve3d geCurve3d) #region _Ƚ public override bool Equals(object? obj) { - return this == obj as Edge; + return this.Equals(obj as Edge); } public bool Equals(Edge? b) { - return this == b; + return this.Equals(b, 1e-6);/*ĬϹ==0ݲ,Eqݲ*/ } public static bool operator !=(Edge? a, Edge? b) { @@ -123,7 +123,25 @@ public bool Equals(Edge? b) if (ReferenceEquals(a, b))//ͬһ return true; - return a.GeCurve3d == b.GeCurve3d; + return a.Equals(b, 0); + } + + /// + /// ȽϺ + /// + public bool Equals(Edge? b, double tolerance = 1e-6) + { + if (b is null) + return false; + if (ReferenceEquals(this, b)) //ͬһ + return true; + + if (tolerance == 0) + return GeCurve3d == b.GeCurve3d; + + var tol = new Tolerance(tolerance, tolerance); + return GeCurve3d.StartPoint.IsEqualTo(b.GeCurve3d.StartPoint, tol) && + GeCurve3d.EndPoint.IsEqualTo(b.GeCurve3d.EndPoint, tol); } public override int GetHashCode() -- Gitee From aa30c062ac255d44fe76f7d70f5ad6731655aa70 Mon Sep 17 00:00:00 2001 From: liuqihong <540762622@qq.com> Date: Fri, 17 Jun 2022 03:43:33 +0800 Subject: [PATCH 257/675] =?UTF-8?q?=E8=A1=A5=E5=85=85=E5=9B=9B=E5=8F=89?= =?UTF-8?q?=E6=A0=91=E6=B5=8B=E8=AF=95=E6=A1=88=E4=BE=8B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Algorithms/QuadTree/IHasRect.cs | 19 ---- .../Algorithms/QuadTree/QuadEntity.cs | 14 ++- .../Algorithms/QuadTree/QuadTree.cs | 2 +- .../Algorithms/QuadTree/QuadTreeNode.cs | 2 +- tests/Test/TestQuadTree.cs | 96 ++++++++++++++++--- 5 files changed, 99 insertions(+), 34 deletions(-) delete mode 100644 src/IFoxCAD.Cad/Algorithms/QuadTree/IHasRect.cs diff --git a/src/IFoxCAD.Cad/Algorithms/QuadTree/IHasRect.cs b/src/IFoxCAD.Cad/Algorithms/QuadTree/IHasRect.cs deleted file mode 100644 index a28d498..0000000 --- a/src/IFoxCAD.Cad/Algorithms/QuadTree/IHasRect.cs +++ /dev/null @@ -1,19 +0,0 @@ -namespace IFoxCAD.Cad -{ - //因为我想用字段...所以从接口改成了类 - /// - /// 约束传入的对象都要含有包围盒的定义 - /// - public class IHasRect: Rect - { - /// - /// 颜色 - /// - //public System.Drawing.Color Color; - - /// - /// 是一个点 - /// - public bool IsPoint; - } -} \ No newline at end of file diff --git a/src/IFoxCAD.Cad/Algorithms/QuadTree/QuadEntity.cs b/src/IFoxCAD.Cad/Algorithms/QuadTree/QuadEntity.cs index c5b0627..529409c 100644 --- a/src/IFoxCAD.Cad/Algorithms/QuadTree/QuadEntity.cs +++ b/src/IFoxCAD.Cad/Algorithms/QuadTree/QuadEntity.cs @@ -1,12 +1,22 @@ namespace IFoxCAD.Cad; +/* + * 这个类存在的意义是为了不暴露Rect类字段 + * 同时利用了Rect类字段的快速 + * 提供到外面去再继承 + */ + /// /// 四叉树图元 /// -public class QuadEntity : IHasRect, IComparable +public class QuadEntity : Rect, IComparable { public ObjectId ObjectId; - public new bool IsPoint => Area == 0; + /// + /// 是一个点 + /// + public bool IsPoint => Area == 0; + //public List? Link;//碰撞链...这里外面自己封装去 /// diff --git a/src/IFoxCAD.Cad/Algorithms/QuadTree/QuadTree.cs b/src/IFoxCAD.Cad/Algorithms/QuadTree/QuadTree.cs index 634fcc5..6bef72c 100644 --- a/src/IFoxCAD.Cad/Algorithms/QuadTree/QuadTree.cs +++ b/src/IFoxCAD.Cad/Algorithms/QuadTree/QuadTree.cs @@ -27,7 +27,7 @@ namespace IFoxCAD.Cad /// 根节点控制器 /// /// 类型接口约束必须有正交矩形 - public class QuadTree where TEntity : IHasRect + public class QuadTree where TEntity : QuadEntity { #region 成员 /// diff --git a/src/IFoxCAD.Cad/Algorithms/QuadTree/QuadTreeNode.cs b/src/IFoxCAD.Cad/Algorithms/QuadTree/QuadTreeNode.cs index dfc4033..0c0cd5b 100644 --- a/src/IFoxCAD.Cad/Algorithms/QuadTree/QuadTreeNode.cs +++ b/src/IFoxCAD.Cad/Algorithms/QuadTree/QuadTreeNode.cs @@ -10,7 +10,7 @@ namespace IFoxCAD.Cad /// public class QuadTreeNode : Rect - where TEntity : IHasRect + where TEntity : QuadEntity { #region 成员 /// diff --git a/tests/Test/TestQuadTree.cs b/tests/Test/TestQuadTree.cs index f6da0e5..ba7fef4 100644 --- a/tests/Test/TestQuadTree.cs +++ b/tests/Test/TestQuadTree.cs @@ -1,19 +1,25 @@ namespace Test; +/* + * 这里必须要继承它,再提供给四叉树 + * 主要是用户可以扩展属性 + */ public class CadEntity : QuadEntity { //这里加入其他字段 - public int MyProperty { get; set; } public List? Link;//碰撞链...这里外面自己封装去 - public System.Drawing.Color Color { get; set; } + public System.Drawing.Color Color; public CadEntity(ObjectId objectId, Rect box) : base(objectId, box) { - + } } public class TestQuadTree { + QuadTree _quadTreeRoot; + + [CommandMethod("Test_QuadTree")] public void Test_QuadTree() { @@ -36,7 +42,7 @@ public void Test_QuadTree() } //创建四叉树 - var _quadTreeRoot = new QuadTree(dbExt); + _quadTreeRoot = new QuadTree(dbExt); //数据库边界 var pl = dbExt.ToPoints(); @@ -53,7 +59,7 @@ public void Test_QuadTree() //随机图元生成 List ces = new(); //用于随机获取图元 - var allTime = Timer.RunTime(() => { + Timer.RunTime(() => { //生成外边界和随机圆形 var grc = GenerateRandomCircle(maximumItems, dbExt); foreach (var ent in grc) @@ -64,18 +70,20 @@ public void Test_QuadTree() //四叉树数据 var entRect = new Rect(edge.MinPoint.X, edge.MinPoint.Y, edge.MaxPoint.X, edge.MaxPoint.Y); var entId = tr.CurrentSpace.AddEntity(ent); - var ce = new CadEntity(entId, entRect); - ce.Color = Utility.RandomColor; + var ce = new CadEntity(entId, entRect) + { + Color = Utility.RandomColor + }; ces.Add(ce); } - }); + }, msg: "画圆消耗时间:"); //测试只加入四叉树的时间 - var insertTime = Timer.RunTime(() => { + Timer.RunTime(() => { _quadTreeRoot.Insert(ces); - }); + }, msg: "插入四叉树时间:"); - tr.Editor.WriteMessage($"\n加入图元数量:{maximumItems}, 插入四叉树时间:{insertTime / 1000.0}秒, 画圆消耗时间:{allTime / 1000.0}秒"); + tr.Editor.WriteMessage($"\n加入图元数量:{maximumItems}"); } /// @@ -99,4 +107,70 @@ IEnumerable GenerateRandomCircle(int createNumber, Rect dbExt) } } + + #region 四叉树查询节点 + //选择范围改图元颜色 + [CommandMethod("CmdTest_QuadTree3")] + public void CmdTest_QuadTree3() + { + Ssget(QuadTreeSelectMode.IntersectsWith); + } + + [CommandMethod("CmdTest_QuadTree4")] + public void CmdTest_QuadTree4() + { + Ssget(QuadTreeSelectMode.Contains); + } + + /// + /// 改颜色 + /// + /// + void Ssget(QuadTreeSelectMode mode) + { + using var tr = new DBTrans(); + + if (_quadTreeRoot is null) + return; + var rect = GetCorner(tr.Editor); + if (rect is null) + return; + + tr.Editor.WriteMessage("选择模式:" + mode); + + //仿选择集 + var ces = _quadTreeRoot.Query(rect, mode); + ces.ForEach(item => { + var ent = tr.GetObject(item.ObjectId, OpenMode.ForWrite); + ent.Color = Color.FromColor(item.Color); + ent.DowngradeOpen(); + ent.Dispose(); + }); + } + + /// + /// 交互获取 + /// + /// + /// + public Rect? GetCorner(Editor ed) + { + var optionsA = new PromptPointOptions($"{Environment.NewLine}起点位置:"); + var pprA = ed.GetPoint(optionsA); + if (pprA.Status != PromptStatus.OK) + return null; + var optionsB = new PromptCornerOptions(Environment.NewLine + "输入矩形角点2:", pprA.Value) + { + UseDashedLine = true,//使用虚线 + AllowNone = true,//回车 + }; + var pprB = ed.GetCorner(optionsB); + if (pprB.Status != PromptStatus.OK) + return null; + + return new Rect(new Point2d(pprA.Value.X, pprA.Value.Y), + new Point2d(pprB.Value.X, pprB.Value.Y), + true); + } + #endregion } -- Gitee From fe7beb819f8074a7d255b7382fc08634817957c3 Mon Sep 17 00:00:00 2001 From: vicwjb <403009819@qq.com> Date: Fri, 17 Jun 2022 17:46:45 +0800 Subject: [PATCH 258/675] =?UTF-8?q?=E5=8F=91=E5=B8=83v0.3.6.1=E7=89=88?= =?UTF-8?q?=E6=9C=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/IFoxCAD.Cad/IFoxCAD.Cad.csproj | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/IFoxCAD.Cad/IFoxCAD.Cad.csproj b/src/IFoxCAD.Cad/IFoxCAD.Cad.csproj index 7a36b3e..3063300 100644 --- a/src/IFoxCAD.Cad/IFoxCAD.Cad.csproj +++ b/src/IFoxCAD.Cad/IFoxCAD.Cad.csproj @@ -6,7 +6,7 @@ enable true true - 0.3.6 + 0.3.6.1 InspireFunction xsfhlzh;vicwjb 基于.NET的Cad二次开发类库 @@ -15,7 +15,7 @@ https://gitee.com/inspirefunction/ifoxcad.git git IFoxCAD;CAD;AutoCad;C#;NET - 增加四叉树. + 增加四叉树测试. true true preview -- Gitee From 895e4b732e97531c41b2ee38da32ed95a5b04c17 Mon Sep 17 00:00:00 2001 From: liuqihong <540762622@qq.com> Date: Fri, 17 Jun 2022 23:03:34 +0800 Subject: [PATCH 259/675] =?UTF-8?q?=E8=B0=83=E6=95=B4=E9=83=A8=E5=88=86?= =?UTF-8?q?=E5=8F=82=E6=95=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Algorithms/QuadTree/QuadEntity.cs | 12 +++++- src/IFoxCAD.Cad/Algorithms/QuadTree/Rect.cs | 14 +++---- src/IFoxCAD.Cad/ExtensionMethod/EditorEx.cs | 40 +++++++++---------- src/IFoxCAD.Cad/ExtensionMethod/Tools.cs | 2 +- tests/Test/TestQuadTree.cs | 22 +++++----- 5 files changed, 47 insertions(+), 43 deletions(-) diff --git a/src/IFoxCAD.Cad/Algorithms/QuadTree/QuadEntity.cs b/src/IFoxCAD.Cad/Algorithms/QuadTree/QuadEntity.cs index 529409c..3ee17b2 100644 --- a/src/IFoxCAD.Cad/Algorithms/QuadTree/QuadEntity.cs +++ b/src/IFoxCAD.Cad/Algorithms/QuadTree/QuadEntity.cs @@ -11,7 +11,11 @@ /// public class QuadEntity : Rect, IComparable { + /// + /// cad图元id + /// public ObjectId ObjectId; + /// /// 是一个点 /// @@ -32,8 +36,12 @@ public QuadEntity(ObjectId objectId, Rect box) _Top = box._Top; _Right = box._Right; } - public int CompareTo(QuadEntity other) + + public int CompareTo(QuadEntity? other) { - return ObjectId.GetHashCode() ^ other.ObjectId.GetHashCode(); + if (other == null) + return -1; + + return (base.GetHashCode(), IsPoint).GetHashCode() ^ other.GetHashCode(); } } \ No newline at end of file diff --git a/src/IFoxCAD.Cad/Algorithms/QuadTree/Rect.cs b/src/IFoxCAD.Cad/Algorithms/QuadTree/Rect.cs index 3b7c51d..7661107 100644 --- a/src/IFoxCAD.Cad/Algorithms/QuadTree/Rect.cs +++ b/src/IFoxCAD.Cad/Algorithms/QuadTree/Rect.cs @@ -20,16 +20,12 @@ public bool Equals(Point2d a, Point2d b)//Point3d是struct不会为null // 在 0~1e-6 范围实现 圆形限定 则计算部分在浮点数6位后,没有啥意义 // 在 0~1e-6 范围实现 从时间和CPU消耗来说,圆形限定 都没有 方形限定 的好 if (_tolerance <= 1e-6) - { return Math.Abs(a.X - b.X) <= _tolerance && Math.Abs(a.Y - b.Y) <= _tolerance; - } - else - { - // 圆形限定 - // DistanceTo 分别对XYZ进行了一次乘法,也是总数3次乘法,然后求了一次平方根 - // (X86.CPU.FSQRT指令用的牛顿迭代法/软件层面可以使用快速平方根....我还以为CPU会采取快速平方根这样的取表操作) - return a.IsEqualTo(b, new Tolerance(_tolerance, _tolerance)); - } + + // 圆形限定 + // DistanceTo 分别对XYZ进行了一次乘法,也是总数3次乘法,然后求了一次平方根 + // (X86.CPU.FSQRT指令用的牛顿迭代法/软件层面可以使用快速平方根....我还以为CPU会采取快速平方根这样的取表操作) + return a.IsEqualTo(b, new Tolerance(_tolerance, _tolerance)); } public int GetHashCode(Point2d obj) diff --git a/src/IFoxCAD.Cad/ExtensionMethod/EditorEx.cs b/src/IFoxCAD.Cad/ExtensionMethod/EditorEx.cs index ec780df..ef01feb 100644 --- a/src/IFoxCAD.Cad/ExtensionMethod/EditorEx.cs +++ b/src/IFoxCAD.Cad/ExtensionMethod/EditorEx.cs @@ -898,22 +898,22 @@ public static void ZoomWindow(this Editor ed, Point3d lpt, Point3d rpt, double o /// 获取有效的数据库范围 /// /// 数据库 - /// 范围 - /// 数据库没有图元返回 true, 反之返回 false - public static bool GetValidExtents3d(this Database db, out Extents3d dbExtent) + /// 容差值:图元包围盒会超过数据库边界,用此参数扩大边界 + /// + public static Extents3d? GetValidExtents3d(this Database db, double extention = 1e-6) { - dbExtent = new Extents3d(Point3d.Origin, new Point3d(1, 1, 0)); - //数据库没有图元的时候,min是大,max是小,导致新建出错 + db.UpdateExt(true);//更新当前模型空间的范围 + var ve = new Vector3d(extention, extention, extention); + // 数据库没有图元的时候,min是大,max是小,导致新建出错 + // 数据如下: + // min.X == 1E20 && min.Y == 1E20 && min.Z == 1E20 && + // max.X == -1E20 && max.Y == -1E20 && max.Z == -1E20) var a = db.Extmin; var b = db.Extmax; - var ve = new Vector3d(1, 1, 0); - if (!(a.X == 1E20 && a.Y == 1E20 && a.Z == 1E20 && - b.X == -1E20 && b.Y == -1E20 && b.Z == -1E20)) - { - dbExtent = new Extents3d(db.Extmin - ve, db.Extmax + ve); - return true; - } - return false; + if (a.X < b.X && a.Y < b.Y) + return new Extents3d(db.Extmin - ve, db.Extmax + ve); + + return null; } /// @@ -924,14 +924,12 @@ public static bool GetValidExtents3d(this Database db, out Extents3d dbExtent) public static void ZoomExtents(this Editor ed, double offsetDist = 0.00) { Database db = ed.Document.Database; - db.UpdateExt(true); - - if (db.GetValidExtents3d(out Extents3d extents3D)) - { - ed.ZoomWindow(extents3D.MinPoint, extents3D.MaxPoint, offsetDist); - return; - } - ed.ZoomWindow(db.Extmin, db.Extmax, offsetDist); + // db.UpdateExt(true); //GetValidExtents3d内提供了 + var dbExtent = db.GetValidExtents3d(); + if (dbExtent == null) + ed.ZoomWindow(Point3d.Origin, new Point3d(1, 1, 0), offsetDist); + else + ed.ZoomWindow(db.Extmin, db.Extmax, offsetDist); } /// diff --git a/src/IFoxCAD.Cad/ExtensionMethod/Tools.cs b/src/IFoxCAD.Cad/ExtensionMethod/Tools.cs index bd7be8f..26c8b6d 100644 --- a/src/IFoxCAD.Cad/ExtensionMethod/Tools.cs +++ b/src/IFoxCAD.Cad/ExtensionMethod/Tools.cs @@ -181,7 +181,7 @@ public static double RunTime(Action action, TimeEnum timeEnum = TimeEnum.Nanosec timeNameZn = " 纳秒"; break; } - Console.WriteLine(msg + " " + time + timeNameZn); + Env.Print(msg + " " + time + timeNameZn); } return time; } diff --git a/tests/Test/TestQuadTree.cs b/tests/Test/TestQuadTree.cs index ba7fef4..19b3efa 100644 --- a/tests/Test/TestQuadTree.cs +++ b/tests/Test/TestQuadTree.cs @@ -1,17 +1,18 @@ namespace Test; /* - * 这里必须要继承它,再提供给四叉树 + * 这里属于用户调用例子, + * 调用时候必须要继承它,再提供给四叉树 * 主要是用户可以扩展属性 */ public class CadEntity : QuadEntity { //这里加入其他字段 - public List? Link;//碰撞链...这里外面自己封装去 + public List? Link;//碰撞链 public System.Drawing.Color Color; public CadEntity(ObjectId objectId, Rect box) : base(objectId, box) { - + } } @@ -27,7 +28,8 @@ public void Test_QuadTree() Rect dbExt; //使用数据库边界来进行 - if (!tr.Database.GetValidExtents3d(out Extents3d dbExtent)) + var dbExtent = tr.Database.GetValidExtents3d(); + if (dbExtent == null) { //throw new ArgumentException("画一个矩形"); @@ -36,8 +38,8 @@ public void Test_QuadTree() } else { - var a = new Point2d(dbExtent.MinPoint.X, dbExtent.MinPoint.Y); - var b = new Point2d(dbExtent.MaxPoint.X, dbExtent.MaxPoint.Y); + var a = new Point2d(dbExtent.Value.MinPoint.X, dbExtent.Value.MinPoint.Y); + var b = new Point2d(dbExtent.Value.MaxPoint.X, dbExtent.Value.MaxPoint.Y); dbExt = new Rect(a, b); } @@ -55,7 +57,7 @@ public void Test_QuadTree() }; tr.CurrentSpace.AddPline(databaseBoundary); - int maximumItems = 1_0000; //生成多少个图元,30万图元±0.5秒,导致cad会令undo出错(八叉树深度过大 treemax) + int maximumItems = 30_0000; //生成多少个图元,导致cad会令undo出错(八叉树深度过大 treemax) //随机图元生成 List ces = new(); //用于随机获取图元 @@ -76,12 +78,12 @@ public void Test_QuadTree() }; ces.Add(ce); } - }, msg: "画圆消耗时间:"); + }, Timer.TimeEnum.Millisecond, "画圆消耗时间:");//30万图元±3秒.cad2021 //测试只加入四叉树的时间 Timer.RunTime(() => { _quadTreeRoot.Insert(ces); - }, msg: "插入四叉树时间:"); + }, Timer.TimeEnum.Millisecond, "插入四叉树时间:");//30万图元±0.7秒.cad2021 tr.Editor.WriteMessage($"\n加入图元数量:{maximumItems}"); } @@ -168,7 +170,7 @@ void Ssget(QuadTreeSelectMode mode) if (pprB.Status != PromptStatus.OK) return null; - return new Rect(new Point2d(pprA.Value.X, pprA.Value.Y), + return new Rect(new Point2d(pprA.Value.X, pprA.Value.Y), new Point2d(pprB.Value.X, pprB.Value.Y), true); } -- Gitee From 96d8dd689d4394e0b4395302c2f1b40d5441a389 Mon Sep 17 00:00:00 2001 From: wang2006zhi <5432325+wang2006zhi@user.noreply.gitee.com> Date: Sun, 19 Jun 2022 08:33:44 +0000 Subject: [PATCH 260/675] update tests/Test/TestQuadTree.cs. --- tests/Test/TestQuadTree.cs | 58 +++++++++++++++++++++++++++++++++++++- 1 file changed, 57 insertions(+), 1 deletion(-) diff --git a/tests/Test/TestQuadTree.cs b/tests/Test/TestQuadTree.cs index 19b3efa..110dacb 100644 --- a/tests/Test/TestQuadTree.cs +++ b/tests/Test/TestQuadTree.cs @@ -1,4 +1,4 @@ -namespace Test; +namespace Test; /* * 这里属于用户调用例子, @@ -176,3 +176,59 @@ void Ssget(QuadTreeSelectMode mode) } #endregion } + +public void tt6() + { + using var tr = new DBTrans(); + var ed = tr.Editor; + //创建四叉树,默认参数无所谓 + var TreeRoot = new QuadTree(new Rect(0, 0, 32525, 32525)); + + var fil = OpFilter.Bulid(e => e.Dxf(0) == "LINE"); + var psr = ed.SSGet("\n 选择需要连接的直线", fil); + if (psr.Status != PromptStatus.OK) return; + var LineEnts = new List(psr.Value.GetEntities(OpenMode.ForWrite)!); + //将实体插入到四岔树 + foreach (var line in LineEnts) + { + var edge = line.GeometricExtents; + var entRect = new Rect(edge.MinPoint.X, edge.MinPoint.Y, edge.MaxPoint.X, edge.MaxPoint.Y); + var ce = new CadEntity(line.Id, entRect) + { + //四叉树数据 + Angle = line.Angle + }; + TreeRoot.Insert(ce); + } + + var ppo = new PromptPointOptions(Environment.NewLine + "\n指定标注点:<空格退出>") + { + AllowArbitraryInput = true,//任意输入 + AllowNone = true //允许回车 + }; + var ppr = ed.GetPoint(ppo);//用户点选 + if (ppr.Status != PromptStatus.OK) + return; + var rect = new Rect(ppr.Value.Point2d(),100,100); + tr.CurrentSpace.AddEntity(rect.ToPolyLine());//显示选择靶标范围 + + var nent = TreeRoot.FindNearEntity(rect);//查询最近实体,按逆时针 + var ent = tr.GetObject(nent.ObjectId, OpenMode.ForWrite);//打开实体 + ent.ColorIndex = Utility.GetRandom().Next(1, 256);//1~256随机色 + ent.DowngradeOpen();//实体降级 + ent.Dispose(); + + var res = TreeRoot.Query(rect, QuadTreeSelectMode.IntersectsWith);//查询选择靶标范围相碰的ID + res.ForEach(item => + { + if (item.Angle == 0|| item.Angle == Math.PI) //过滤直线角度为0或180的直线 + { + var ent = tr.GetObject(item.ObjectId, OpenMode.ForWrite); + ent.ColorIndex = Utility.GetRandom().Next(1, 7); + ent.DowngradeOpen(); + ent.Dispose(); + } + + }); + + } \ No newline at end of file -- Gitee From 147522b5dce9d4c1039aba30ea4e5a3e6b28a7c6 Mon Sep 17 00:00:00 2001 From: liuqihong <540762622@qq.com> Date: Wed, 22 Jun 2022 03:36:24 +0800 Subject: [PATCH 261/675] =?UTF-8?q?=E5=9B=9B=E5=8F=89=E6=A0=91=E7=A7=BB?= =?UTF-8?q?=E5=85=A5=E6=B5=8B=E8=AF=95=E4=BE=8B=E5=AD=90=E4=BD=86=E6=98=AF?= =?UTF-8?q?=E6=B2=A1=E6=9C=89=E6=9B=B4=E6=94=B9=E5=AE=8C=E6=88=90?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Algorithms/QuadTree/QuadEntity.cs | 4 +- tests/Test/TestQuadTree.cs | 300 ++++++++++++++---- 2 files changed, 248 insertions(+), 56 deletions(-) diff --git a/src/IFoxCAD.Cad/Algorithms/QuadTree/QuadEntity.cs b/src/IFoxCAD.Cad/Algorithms/QuadTree/QuadEntity.cs index 3ee17b2..656d649 100644 --- a/src/IFoxCAD.Cad/Algorithms/QuadTree/QuadEntity.cs +++ b/src/IFoxCAD.Cad/Algorithms/QuadTree/QuadEntity.cs @@ -19,7 +19,9 @@ public class QuadEntity : Rect, IComparable /// /// 是一个点 /// - public bool IsPoint => Area == 0; + /// 面积是0不一定是点,所以需要这样判断, + /// 因为可能是水平或者垂直的直线,没有斜率的时候是包围盒面积是0 + public bool IsPoint => Math.Abs(_X - _Right) < 1e-10 && Math.Abs(_Y - _Top) < 1e-10; //public List? Link;//碰撞链...这里外面自己封装去 diff --git a/tests/Test/TestQuadTree.cs b/tests/Test/TestQuadTree.cs index 110dacb..1025bf9 100644 --- a/tests/Test/TestQuadTree.cs +++ b/tests/Test/TestQuadTree.cs @@ -10,17 +10,18 @@ public class CadEntity : QuadEntity //这里加入其他字段 public List? Link;//碰撞链 public System.Drawing.Color Color; + public double Angle; public CadEntity(ObjectId objectId, Rect box) : base(objectId, box) { } } -public class TestQuadTree +public partial class TestQuadTree { QuadTree _quadTreeRoot; - + #region 四叉树创建并加入 [CommandMethod("Test_QuadTree")] public void Test_QuadTree() { @@ -93,7 +94,7 @@ public void Test_QuadTree() /// /// 创建数量 /// 数据库边界 - IEnumerable GenerateRandomCircle(int createNumber, Rect dbExt) + static IEnumerable GenerateRandomCircle(int createNumber, Rect dbExt) { var x1 = (int)dbExt.X; var x2 = (int)(dbExt.X + dbExt.Width); @@ -109,6 +110,195 @@ IEnumerable GenerateRandomCircle(int createNumber, Rect dbExt) } } + /*TODO 啊惊: 有点懒不想改了*/ +#if true2 + + //选择加入到四叉树 + [CommandMethod("CmdTest_QuadTree21")] + public void CmdTest_QuadTree21() + { + var dm = Acap.DocumentManager; + var doc = dm.MdiActiveDocument; + var db = doc.Database; + var ed = doc.Editor; + ed.WriteMessage("\n选择单个图元加入已有的四叉树"); + + var ss = ed.Ssget(); + if (ss.Count == 0) + return; + + AddQuadTreeRoot(db, ed, ss); + } + + //自动加入全图到四叉树 + [CommandMethod("CmdTest_QuadTree20")] + public void CmdTest_QuadTree20() + { + var dm = Acap.DocumentManager; + var doc = dm.MdiActiveDocument; + var db = doc.Database; + var ed = doc.Editor; + ed.WriteMessage("\n自动加入全图到四叉树"); + + var ss = new List(); + int entnum = 0; + var time1 = Timer.RunTime(() => { + db.Action(tr => { + db.TraverseBlockTable(tr, btRec => { + if (!btRec.IsLayout)//布局跳过 + return false; + + foreach (var item in btRec) + { + //var ent = item.ToEntity(tr); + ss.Add(item); + ++entnum;//图元数量:100000, 遍历全图时间:0.216秒 CmdTest_QuadTree2 + } + return false; + }); + }); + }); + ed.WriteMessage($"\n图元数量:{entnum}, 遍历全图时间:{time1 / 1000.0}秒"); + + //清空原有的 + _quadTreeRoot = null; + AddQuadTreeRoot(db, ed, ss); + } + + void AddQuadTreeRoot(Database db, Editor ed, List ss) + { + if (_quadTreeRoot is null) + { + ed.WriteMessage("\n四叉树是空的,重新初始化"); + + Rect dbExt; + //使用数据库边界来进行 + var dbExtent = db.GetValidExtents3d(); + if (dbExtent == null) + { + //throw new ArgumentException("画一个矩形"); + + //测试时候画个矩形,在矩形内画随机坐标的圆形 + dbExt = new Rect(0, 0, 32525, 32525); + } + else + { + dbExt = new Rect(dbExtent.Value.MinPoint.Point2d(), dbExtent.Value.MaxPoint.Point2d()); + } + _quadTreeRoot = new(dbExt); + } + + /* 测试: + * 为了测试删除内容释放了分支,再重复加入是否报错 + * 先创建 CmdTest_QuadTree1 + * 再减去 CmdTest_QuadTree0 + * 然后原有黑色边界,再生成边界 CmdTest_Create00,对比删除效果. + * 然后加入 CmdTest_QuadTree2 + * 然后原有黑色边界,再生成边界 CmdTest_Create00,对比删除效果. + */ + + List ces = new(); + db.Action(tr => { + ss.ForEach(entId => { + var ent = entId.ToEntity(tr); + if (ent is null) + return; + var edge = new EdgeEntity(ent); + //四叉树数据 + var ce = new CadEntity(entId, edge.Edge) + { + Color = Utility.RandomColor + }; + ces.Add(ce); + + edge.Dispose(); + }); + }); + + var time2 = Timer.RunTime(() => { + _quadTreeRoot.Insert(ces); + }); + ed.WriteMessage($"\n图元数量:{ces.Count}, 加入四叉树时间:{time2 / 1000.0}秒"); + } +#endif + + #endregion + + /*TODO 啊惊: 有点懒不想改了*/ +#if true2 + + #region 节点边界显示 + //四叉树减去节点 + [CommandMethod("CmdTest_QuadTree0")] + public void CmdTest_QuadTree0() + { + var dm = Acap.DocumentManager; + var doc = dm.MdiActiveDocument; + //var db = doc.Database; + var ed = doc.Editor; + ed.WriteMessage("\n四叉树减区"); + + if (_quadTreeRoot is null) + { + ed.WriteMessage("\n四叉树是空的"); + return; + } + var rect = GetCorner(ed); + if (rect is null) + return; + _quadTreeRoot.Remove(rect); + } + + //创建节点边界 + [CommandMethod("CmdTest_QuadTree00")] + public void CmdTest_CreateNodesRect() + { + var dm = Acap.DocumentManager; + var doc = dm.MdiActiveDocument; + var db = doc.Database; + var ed = doc.Editor; + ed.WriteMessage("\n创建边界"); + + if (_quadTreeRoot is null) + { + ed.WriteMessage("\n四叉树是空的"); + return; + } + + //此处发现了一个事务处理的bug,提交数量过多的时候,会导致 ctrl+z 无法回滚, + //需要把事务放在循环体内部 + //报错: 0x6B00500A (msvcr80.dll)处(位于 acad.exe 中)引发的异常: 0xC0000005: 写入位置 0xFFE00000 时发生访问冲突。 + //画出所有的四叉树节点边界,因为事务放在外面引起 + var nodeRects = new List(); + _quadTreeRoot.ForEach(node => { + nodeRects.Add(node); + return false; + }); + var rectIds = new List(); + foreach (var item in nodeRects)//Count = 97341 当数量接近这个量级 + { + db.Action(tr => { + var pts = item.ToPoints(); + var rec = EntityAdd.AddPolyLineToEntity(pts.ToPoint2d()); + rec.ColorIndex = 250; + rectIds.Add(tr.AddEntityToMsPs(db, rec)); + }); + } + db.Action(tr => { + db.CoverGroup(tr, rectIds); + }); + + //获取四叉树深度 + int dep = 0; + _quadTreeRoot.ForEach(node => { + dep = dep > node.Depth ? dep : node.Depth; + return false; + }); + ed.WriteMessage($"\n四叉树深度是: {dep}"); + } + #endregion + +#endif #region 四叉树查询节点 //选择范围改图元颜色 @@ -155,7 +345,7 @@ void Ssget(QuadTreeSelectMode mode) /// /// /// - public Rect? GetCorner(Editor ed) + public static Rect? GetCorner(Editor ed) { var optionsA = new PromptPointOptions($"{Environment.NewLine}起点位置:"); var pprA = ed.GetPoint(optionsA); @@ -177,58 +367,58 @@ void Ssget(QuadTreeSelectMode mode) #endregion } -public void tt6() - { - using var tr = new DBTrans(); - var ed = tr.Editor; - //创建四叉树,默认参数无所谓 - var TreeRoot = new QuadTree(new Rect(0, 0, 32525, 32525)); - - var fil = OpFilter.Bulid(e => e.Dxf(0) == "LINE"); - var psr = ed.SSGet("\n 选择需要连接的直线", fil); - if (psr.Status != PromptStatus.OK) return; - var LineEnts = new List(psr.Value.GetEntities(OpenMode.ForWrite)!); - //将实体插入到四岔树 - foreach (var line in LineEnts) - { - var edge = line.GeometricExtents; - var entRect = new Rect(edge.MinPoint.X, edge.MinPoint.Y, edge.MaxPoint.X, edge.MaxPoint.Y); - var ce = new CadEntity(line.Id, entRect) - { - //四叉树数据 - Angle = line.Angle - }; - TreeRoot.Insert(ce); - } +//public partial class TestQuadTree +//{ +// public void Cmd_tt6() +// { +// using var tr = new DBTrans(); +// var ed = tr.Editor; +// //创建四叉树,默认参数无所谓 +// var TreeRoot = new QuadTree(new Rect(0, 0, 32525, 32525)); - var ppo = new PromptPointOptions(Environment.NewLine + "\n指定标注点:<空格退出>") - { - AllowArbitraryInput = true,//任意输入 - AllowNone = true //允许回车 - }; - var ppr = ed.GetPoint(ppo);//用户点选 - if (ppr.Status != PromptStatus.OK) - return; - var rect = new Rect(ppr.Value.Point2d(),100,100); - tr.CurrentSpace.AddEntity(rect.ToPolyLine());//显示选择靶标范围 - - var nent = TreeRoot.FindNearEntity(rect);//查询最近实体,按逆时针 - var ent = tr.GetObject(nent.ObjectId, OpenMode.ForWrite);//打开实体 - ent.ColorIndex = Utility.GetRandom().Next(1, 256);//1~256随机色 - ent.DowngradeOpen();//实体降级 - ent.Dispose(); +// var fil = OpFilter.Bulid(e => e.Dxf(0) == "LINE"); +// var psr = ed.SSGet("\n 选择需要连接的直线", fil); +// if (psr.Status != PromptStatus.OK) return; +// var LineEnts = new List(psr.Value.GetEntities(OpenMode.ForWrite)!); +// //将实体插入到四岔树 +// foreach (var line in LineEnts) +// { +// var edge = line.GeometricExtents; +// var entRect = new Rect(edge.MinPoint.X, edge.MinPoint.Y, edge.MaxPoint.X, edge.MaxPoint.Y); +// var ce = new CadEntity(line.Id, entRect) +// { +// //四叉树数据 +// Angle = line.Angle +// }; +// TreeRoot.Insert(ce); +// } - var res = TreeRoot.Query(rect, QuadTreeSelectMode.IntersectsWith);//查询选择靶标范围相碰的ID - res.ForEach(item => - { - if (item.Angle == 0|| item.Angle == Math.PI) //过滤直线角度为0或180的直线 - { - var ent = tr.GetObject(item.ObjectId, OpenMode.ForWrite); - ent.ColorIndex = Utility.GetRandom().Next(1, 7); - ent.DowngradeOpen(); - ent.Dispose(); - } +// var ppo = new PromptPointOptions(Environment.NewLine + "\n指定标注点:<空格退出>") +// { +// AllowArbitraryInput = true,//任意输入 +// AllowNone = true //允许回车 +// }; +// var ppr = ed.GetPoint(ppo);//用户点选 +// if (ppr.Status != PromptStatus.OK) +// return; +// var rect = new Rect(ppr.Value.Point2d(), 100, 100); +// tr.CurrentSpace.AddEntity(rect.ToPolyLine());//显示选择靶标范围 - }); +// var nent = TreeRoot.FindNearEntity(rect);//查询最近实体,按逆时针 +// var ent = tr.GetObject(nent.ObjectId, OpenMode.ForWrite);//打开实体 +// ent.ColorIndex = Utility.GetRandom().Next(1, 256);//1~256随机色 +// ent.DowngradeOpen();//实体降级 +// ent.Dispose(); - } \ No newline at end of file +// var res = TreeRoot.Query(rect, QuadTreeSelectMode.IntersectsWith);//查询选择靶标范围相碰的ID +// res.ForEach(item => { +// if (item.Angle == 0 || item.Angle == Math.PI) //过滤直线角度为0或180的直线 +// { +// var ent = tr.GetObject(item.ObjectId, OpenMode.ForWrite); +// ent.ColorIndex = Utility.GetRandom().Next(1, 7); +// ent.DowngradeOpen(); +// ent.Dispose(); +// } +// }); +// } +//} \ No newline at end of file -- Gitee From 8b0e1a2f78c573a2068540da72f56c3c40ce5cac Mon Sep 17 00:00:00 2001 From: liuqihong <540762622@qq.com> Date: Wed, 22 Jun 2022 05:00:56 +0800 Subject: [PATCH 262/675] =?UTF-8?q?=E4=BF=AE=E6=94=B9=E5=9B=9B=E5=8F=89?= =?UTF-8?q?=E6=A0=91SDK=E6=8F=90=E4=BE=9BCadEntity=E7=9A=84=E6=96=B9?= =?UTF-8?q?=E6=B3=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Algorithms/QuadTree/QuadEntity.cs | 28 ++----------------- src/IFoxCAD.Cad/Algorithms/QuadTree/Rect.cs | 7 +++++ tests/Test/TestQuadTree.cs | 15 ++++++++-- 3 files changed, 22 insertions(+), 28 deletions(-) diff --git a/src/IFoxCAD.Cad/Algorithms/QuadTree/QuadEntity.cs b/src/IFoxCAD.Cad/Algorithms/QuadTree/QuadEntity.cs index 656d649..c5f7f11 100644 --- a/src/IFoxCAD.Cad/Algorithms/QuadTree/QuadEntity.cs +++ b/src/IFoxCAD.Cad/Algorithms/QuadTree/QuadEntity.cs @@ -9,41 +9,17 @@ /// /// 四叉树图元 /// -public class QuadEntity : Rect, IComparable +public class QuadEntity : Rect { - /// - /// cad图元id - /// - public ObjectId ObjectId; - - /// - /// 是一个点 - /// - /// 面积是0不一定是点,所以需要这样判断, - /// 因为可能是水平或者垂直的直线,没有斜率的时候是包围盒面积是0 - public bool IsPoint => Math.Abs(_X - _Right) < 1e-10 && Math.Abs(_Y - _Top) < 1e-10; - - //public List? Link;//碰撞链...这里外面自己封装去 - /// /// 四叉树图元 /// - /// 图元id /// 包围盒 - public QuadEntity(ObjectId objectId, Rect box) + public QuadEntity(Rect box) { - ObjectId = objectId; _X = box._X; _Y = box._Y; _Top = box._Top; _Right = box._Right; } - - public int CompareTo(QuadEntity? other) - { - if (other == null) - return -1; - - return (base.GetHashCode(), IsPoint).GetHashCode() ^ other.GetHashCode(); - } } \ No newline at end of file diff --git a/src/IFoxCAD.Cad/Algorithms/QuadTree/Rect.cs b/src/IFoxCAD.Cad/Algorithms/QuadTree/Rect.cs index 7661107..b9ed8c1 100644 --- a/src/IFoxCAD.Cad/Algorithms/QuadTree/Rect.cs +++ b/src/IFoxCAD.Cad/Algorithms/QuadTree/Rect.cs @@ -129,6 +129,13 @@ public double Area /// 中下 /// public Point2d MidstBottom => new(Midst.X, _Y); + + /// + /// 是一个点 + /// + /// 面积是0不一定是点,所以需要这样判断, + /// 因为可能是水平或者垂直的直线,没有斜率的时候是包围盒面积是0 + public bool IsPoint => Math.Abs(_X - _Right) < 1e-10 && Math.Abs(_Y - _Top) < 1e-10; #endregion #region 构造 diff --git a/tests/Test/TestQuadTree.cs b/tests/Test/TestQuadTree.cs index 1025bf9..b15733f 100644 --- a/tests/Test/TestQuadTree.cs +++ b/tests/Test/TestQuadTree.cs @@ -7,13 +7,24 @@ namespace Test; */ public class CadEntity : QuadEntity { + public ObjectId ObjectId; //这里加入其他字段 public List? Link;//碰撞链 public System.Drawing.Color Color; public double Angle; - public CadEntity(ObjectId objectId, Rect box) : base(objectId, box) + public CadEntity(ObjectId objectId, Rect box) : base(box) { - + ObjectId = objectId; + } + public int CompareTo(CadEntity? other) + { + if (other == null) + return -1; + return GetHashCode() ^ other.GetHashCode(); + } + public override int GetHashCode() + { + return (base.GetHashCode(), ObjectId.GetHashCode()).GetHashCode(); } } -- Gitee From a134c68cc8c83081f827e14201a242b788bca51c Mon Sep 17 00:00:00 2001 From: liuqihong <540762622@qq.com> Date: Wed, 22 Jun 2022 05:02:51 +0800 Subject: [PATCH 263/675] =?UTF-8?q?=E6=9A=B4=E9=9C=B2=E4=BF=AE=E6=94=B9?= =?UTF-8?q?=E4=BF=A1=E6=81=AF?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/IFoxCAD.Cad/Algorithms/QuadTree/Rect.cs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/IFoxCAD.Cad/Algorithms/QuadTree/Rect.cs b/src/IFoxCAD.Cad/Algorithms/QuadTree/Rect.cs index b9ed8c1..29c70b9 100644 --- a/src/IFoxCAD.Cad/Algorithms/QuadTree/Rect.cs +++ b/src/IFoxCAD.Cad/Algorithms/QuadTree/Rect.cs @@ -132,9 +132,8 @@ public double Area /// /// 是一个点 + /// 水平或垂直直线包围盒是面积是0,所以面积是0不一定是点 /// - /// 面积是0不一定是点,所以需要这样判断, - /// 因为可能是水平或者垂直的直线,没有斜率的时候是包围盒面积是0 public bool IsPoint => Math.Abs(_X - _Right) < 1e-10 && Math.Abs(_Y - _Top) < 1e-10; #endregion -- Gitee From 408d0f4d8f292abdc67cf2ac1f9d3445dcc4d416 Mon Sep 17 00:00:00 2001 From: liuqihong <540762622@qq.com> Date: Thu, 23 Jun 2022 00:44:57 +0800 Subject: [PATCH 264/675] =?UTF-8?q?net35=E5=8A=A0=E5=85=A5=E7=BA=A2?= =?UTF-8?q?=E9=BB=91=E6=A0=91?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/IFoxCAD.Basal/Sortedset/ISet.cs | 71 + src/IFoxCAD.Basal/Sortedset/SR.cs | 902 ++++++ src/IFoxCAD.Basal/Sortedset/Sortedset.cs | 2913 ++++++++++++++++++++ src/IFoxCAD.Basal/Sortedset/ThrowHelper.cs | 378 +++ src/IFoxCAD.Basal/Sortedset/bithelper.cs | 165 ++ 5 files changed, 4429 insertions(+) create mode 100644 src/IFoxCAD.Basal/Sortedset/ISet.cs create mode 100644 src/IFoxCAD.Basal/Sortedset/SR.cs create mode 100644 src/IFoxCAD.Basal/Sortedset/Sortedset.cs create mode 100644 src/IFoxCAD.Basal/Sortedset/ThrowHelper.cs create mode 100644 src/IFoxCAD.Basal/Sortedset/bithelper.cs diff --git a/src/IFoxCAD.Basal/Sortedset/ISet.cs b/src/IFoxCAD.Basal/Sortedset/ISet.cs new file mode 100644 index 0000000..549e9f0 --- /dev/null +++ b/src/IFoxCAD.Basal/Sortedset/ISet.cs @@ -0,0 +1,71 @@ +#if NET35 +// ==++== +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +// ==--== +/*============================================================ +** +** Interface: ISet +** +** kimhamil +** +** +** Purpose: Base interface for all generic sets. +** +** +===========================================================*/ +namespace System.Collections.Generic +{ + + using System; + using System.Runtime.CompilerServices; + + + /// + /// Generic collection that guarantees the uniqueness of its elements, as defined + /// by some comparer. It also supports basic set operations such as Union, Intersection, + /// Complement and Exclusive Complement. + /// + public interface ISet : ICollection + { + + //Add ITEM to the set, return true if added, false if duplicate + new bool Add(T item); + + //Transform this set into its union with the IEnumerable other + void UnionWith(IEnumerable other); + + //Transform this set into its intersection with the IEnumberable other + void IntersectWith(IEnumerable other); + + //Transform this set so it contains no elements that are also in other + void ExceptWith(IEnumerable other); + + //Transform this set so it contains elements initially in this or in other, but not both + void SymmetricExceptWith(IEnumerable other); + + //Check if this set is a subset of other + bool IsSubsetOf(IEnumerable other); + + //Check if this set is a superset of other + bool IsSupersetOf(IEnumerable other); + + //Check if this set is a subset of other, but not the same as it + bool IsProperSupersetOf(IEnumerable other); + + //Check if this set is a superset of other, but not the same as it + bool IsProperSubsetOf(IEnumerable other); + + //Check if this set has any elements in common with other + bool Overlaps(IEnumerable other); + + //Check if this set contains the same and only the same elements as other + bool SetEquals(IEnumerable other); + + + + } + +} +#endif \ No newline at end of file diff --git a/src/IFoxCAD.Basal/Sortedset/SR.cs b/src/IFoxCAD.Basal/Sortedset/SR.cs new file mode 100644 index 0000000..391d9b7 --- /dev/null +++ b/src/IFoxCAD.Basal/Sortedset/SR.cs @@ -0,0 +1,902 @@ +#if NET35 +namespace System; + + +using System; +using System.Reflection; +using System.Globalization; +using System.Resources; +using System.Text; +using System.ComponentModel; +using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; + +[AttributeUsage(AttributeTargets.All)] +internal sealed class SRDescriptionAttribute : DescriptionAttribute +{ + public SRDescriptionAttribute(string description) + { + DescriptionValue = SR.GetString(description); + } + + public SRDescriptionAttribute(string description, string resourceSet) + { + ResourceManager rm = new ResourceManager(resourceSet, Assembly.GetExecutingAssembly()); + DescriptionValue = rm.GetString(description); + System.Diagnostics.Debug.Assert(DescriptionValue != null, string.Format(CultureInfo.CurrentCulture, "String resource {0} not found.", new object[] { description })); + } +} + +[AttributeUsage(AttributeTargets.All)] +internal sealed class SRCategoryAttribute : CategoryAttribute +{ + private string resourceSet = String.Empty; + + public SRCategoryAttribute(string category) + : base(category) + { + } + + public SRCategoryAttribute(string category, string resourceSet) + : base(category) + { + this.resourceSet = resourceSet; + } + + protected override string GetLocalizedString(string value) + { + if (this.resourceSet.Length > 0) + { + ResourceManager rm = new ResourceManager(resourceSet, Assembly.GetExecutingAssembly()); + String localizedString = rm.GetString(value); + System.Diagnostics.Debug.Assert(localizedString != null, string.Format(CultureInfo.CurrentCulture, "String resource {0} not found.", new object[] { value })); + return localizedString; + } + else + { + return SR.GetString(value); + } + } +} + +[AttributeUsage(AttributeTargets.All)] +internal sealed class SRDisplayNameAttribute : DisplayNameAttribute +{ + public SRDisplayNameAttribute(string name) + { + DisplayNameValue = SR.GetString(name); + } + + public SRDisplayNameAttribute(string name, string resourceSet) + { + ResourceManager rm = new ResourceManager(resourceSet, Assembly.GetExecutingAssembly()); + DisplayNameValue = rm.GetString(name); + System.Diagnostics.Debug.Assert(DisplayNameValue != null, string.Format(CultureInfo.CurrentCulture, "String resource {0} not found.", new object[] { name })); + } +} + +/// +/// AutoGenerated resource class. Usage: +/// +/// string s = SR.GetString(SR.MyIdenfitier); +/// +internal sealed partial class SR +{ + static SR loader = null; + ResourceManager resources; + + internal SR() + { + resources = new System.Resources.ResourceManager("System.Workflow.ComponentModel.StringResources", Assembly.GetExecutingAssembly()); + } + + private static SR GetLoader() + { + if (loader == null) + loader = new SR(); + return loader; + } + + private static CultureInfo Culture + { + get { return null/*use ResourceManager default, CultureInfo.CurrentUICulture*/; } + } + + [SuppressMessage("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")] + internal static string GetString(string name, params object[] args) + { + return GetString(SR.Culture, name, args); + } + + [SuppressMessage("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")] + internal static string GetString(CultureInfo culture, string name, params object[] args) + { + SR sys = GetLoader(); + if (sys == null) + return null; + string res = sys.resources.GetString(name, culture); + System.Diagnostics.Debug.Assert(res != null, string.Format(CultureInfo.CurrentCulture, "String resource {0} not found.", new object[] { name })); + if (args != null && args.Length > 0) + { + return string.Format(CultureInfo.CurrentCulture, res, args); + } + else + { + return res; + } + } + + internal static string GetString(string name) + { + return GetString(SR.Culture, name); + } + + internal static string GetString(CultureInfo culture, string name) + { + SR sys = GetLoader(); + if (sys == null) + return null; + string res = sys.resources.GetString(name, culture); + System.Diagnostics.Debug.Assert(res != null, string.Format(CultureInfo.CurrentCulture, "String resource {0} not found.", new object[] { name })); + return res; + } + + // All these strings should be present in StringResources.resx + internal const string Activity = "Activity"; + internal const string Handlers = "Handlers"; + internal const string Conditions = "Conditions"; + internal const string ConditionedActivityConditions = "ConditionedActivityConditions"; + internal const string Correlations = "Correlations"; + internal const string CorrelationSet = "CorrelationSet"; + internal const string NameDescr = "NameDescr"; + internal const string EnabledDescr = "EnabledDescr"; + internal const string DescriptionDescr = "DescriptionDescr"; + internal const string UnlessConditionDescr = "UnlessConditionDescr"; + internal const string InitializeDescr = "InitializeDescr"; + internal const string CatchTypeDescr = "CatchTypeDescr"; + internal const string ExceptionTypeDescr = "ExceptionTypeDescr"; + internal const string FaultDescription = "FaultDescription"; + internal const string FaultTypeDescription = "FaultTypeDescription"; + internal const string ContainingAssemblyDescr = "ContainingAssemblyDescr"; + internal const string ExecutionModeDescr = "ExecutionModeDescr"; + internal const string Error_ReadOnlyTemplateActivity = "Error_ReadOnlyTemplateActivity"; + internal const string Error_TypeNotString = "Error_TypeNotString"; + internal const string Error_InvalidErrorType = "Error_InvalidErrorType"; + internal const string Error_LiteralConversionFailed = "Error_LiteralConversionFailed"; + internal const string Error_TypeNotPrimitive = "Error_TypeNotPrimitive"; + internal const string CompletedCaleeDescr = "CompletedCaleeDescr"; + internal const string ProxyClassDescr = "ProxyClassDescr"; + internal const string ActivitySetDescr = "ActivitySetDescr"; + internal const string VersionDescr = "VersionDescr"; + internal const string ActivationDescr = "ActivationDescr"; + internal const string CorrelationSetsDescr = "CorrelationSetsDescr"; + internal const string CompanionClassDescr = "CompanionClassDescr"; + internal const string TransactionTypeDescr = "TransactionTypeDescr"; + internal const string SynchronizedDescr = "SynchronizedDescr"; + internal const string IsolationLevelDescr = "IsolationLevelDescr"; + internal const string TimeoutDescr = "TimeoutDescr"; + internal const string BatchableDescr = "BatchableDescr"; + internal const string LRTTimeoutDescr = "LRTTimeoutDescr"; + internal const string OnGetCalleeCountDescr = "OnGetCalleeCountDescr"; + internal const string CompensatableActivityDescr = "CompensatableActivityDescr"; + internal const string OnAfterEventDescr = "OnAfterEventDescr"; + internal const string OnBeforeMethodInvokeDescr = "OnBeforeMethodInvokeDescr"; + internal const string AssignedToDescr = "AssignedToDescr"; + internal const string TypeDescr = "TypeDescr"; + internal const string TemplateActivityDescr = "TemplateActivityDescr"; + internal const string ErrorMessageDescr = "ErrorMessageDescr"; + internal const string WebServiceSynchronizedDescr = "WebServiceSynchronizedDescr"; + internal const string CorrelationSetDescr = "CorrelationSetDescr"; + internal const string ExecutionTypeDescr = "ExecutionTypeDescr"; + internal const string RoleDescr = "RoleDescr"; + internal const string OnInitializeClonesDescr = "OnInitializeClonesDescr"; + internal const string CorrelationSetDisplayName = "CorrelationSetDisplayName"; + internal const string PastingActivities = "PastingActivities"; + internal const string DeletingActivities = "DeletingActivities"; + internal const string DragDropActivities = "DragDropActivities"; + internal const string ChangingEnabled = "ChangingEnabled"; + internal const string ChangingHandler = "ChangingHandler"; + internal const string ChangingParameter = "ChangingParameter"; + internal const string CollectionItem = "CollectionItem"; + internal const string AddingConditionalBranch = "AddingConditionalBranch"; + internal const string AddingEventActivity = "AddingEventActivity"; + internal const string AddingListenBranch = "AddingListenBranch"; + internal const string AddingParallelBranch = "AddingParallelBranch"; + internal const string CurrentProject = "CurrentProject"; + internal const string ReferencedAssemblies = "ReferencedAssemblies"; + internal const string CollectionText = "CollectionText"; + internal const string ParameterDescription = "ParameterDescription"; + internal const string InvokeParameterDescription = "InvokeParameterDescription"; + internal const string ParametersDescription = "ParametersDescription"; + internal const string ChangingParameters = "ChangingParameters"; + internal const string Condition = "ConditionRule"; + internal const string MovingActivities = "MovingActivities"; + internal const string MemberNameDescr = "MemberNameDescr"; + internal const string OnScopeInitializedDescr = "OnScopeInitializedDescr"; + internal const string OnGeneratorInitializedDescr = "OnGeneratorInitializedDescr"; + internal const string OnScopeCompletedDescr = "OnScopeCompletedDescr"; + internal const string OnGeneratorCompletedDescr = "OnGeneratorCompletedDescr"; + internal const string DataElementRuntimeTypeDescr = "DataElementRuntimeTypeDescr"; + internal const string RuleConditionReferencesDescr = "RuleConditionReferencesDescr"; + internal const string CreateActivityFromToolbox = "CreateActivityFromToolbox"; + internal const string MoveMultipleActivities = "MoveMultipleActivities"; + internal const string MoveSingleActivity = "MoveSingleActivity"; + internal const string CutMultipleActivities = "CutMultipleActivities"; + internal const string CutSingleActivity = "CutSingleActivity"; + internal const string CutActivity = "CutActivity"; + internal const string FaultActivityDescription = "FaultActivityDescription"; + internal const string NullConditionExpression = "NullConditionExpression"; + internal const string ParameterTypeDescription = "ParameterTypeDescription"; + internal const string ParameterCategory = "ParameterCategory"; + internal const string ParameterDirectionDescription = "ParameterDirectionDescription"; + internal const string ParameterElementDescription = "ParameterElementDescription"; + internal const string ParameterDlgDescription = "ParameterDlgDescription"; + internal const string ParameterDlgHeader = "ParameterDlgHeader"; + internal const string SuspendActivityDescription = "SuspendActivityDescription"; + internal const string SuspendErrorMessageDescr = "SuspendErrorMessageDescr"; + internal const string TerminateActivityDescription = "TerminateActivityDescription"; + internal const string TerminateErrorMessageDescr = "TerminateErrorMessageDescr"; + internal const string DeclarationCategory = "DeclarationCategory"; + internal const string NoValidActivityPropertiesAvailable = "NoValidActivityPropertiesAvailable"; + internal const string ChooseActivityDatasource = "ChooseActivityDatasource"; + internal const string Promote = "Promote"; + internal const string Type = "Type"; + internal const string NoMatchingActivityProperties = "NoMatchingActivityProperties"; + internal const string ActivityBindIDDescription = "ActivityBindIDDescription"; + internal const string ActivityBindPathDescription = "ActivityBindPathDescription"; + internal const string XPathDescription = "XPathDescription"; + internal const string TransformerDescription = "TransformerDescription"; + internal const string CustomPropertiesCollectionFormHeader = "CustomPropertiesCollectionFormHeader"; + internal const string CustomPropertiesCollectionFormDescription = "CustomPropertiesCollectionFormDescription"; + internal const string BaseTypePropertyName = "BaseTypePropertyName"; + internal const string CustomActivityBaseClassTypeFilterProviderDesc = "CustomActivityBaseClassTypeFilterProviderDesc"; + internal const string CustomActivityDesignerTypeFilterProviderDesc = "CustomActivityDesignerTypeFilterProviderDesc"; + internal const string CustomActivityValidatorTypeFilterProviderDesc = "CustomActivityValidatorTypeFilterProviderDesc"; + internal const string CustomActivityExecutorTypeFilterProviderDesc = "CustomActivityExecutorTypeFilterProviderDesc"; + internal const string GenericParameters = "GenericParameters"; + internal const string ToolboxItem = "ToolboxItem"; + internal const string ToolboxItemCompanionClassDesc = "ToolboxItemCompanionClassDesc"; + internal const string Error_SerializationInsufficientState = "Error_SerializationInsufficientState"; + internal const string Error_ActivityHasParent = "Error_ActivityHasParent"; + internal const string Error_CompensantionParentNotScope = "Error_CompensantionParentNotScope"; + internal const string Error_ConditionedActivityParentNotCAG = "Error_ConditionedActivityParentNotCAG"; + internal const string Error_CorrelationTypeNotComparable = "Error_CorrelationTypeNotComparable"; + internal const string Error_ArgumentTypeNotMatchParameter = "Error_ArgumentTypeNotMatchParameter"; + internal const string Error_TypeTypeMismatch = "Error_TypeTypeMismatch"; + internal const string Error_ParameterTypeMismatch = "Error_ParameterTypeMismatch"; + internal const string Error_InvokeParameterTypeMismatch = "Error_InvokeParameterTypeMismatch"; + internal const string Error_ParameterPropertyNotSet = "Error_ParameterPropertyNotSet"; + internal const string Error_DataSourceNameNotSet = "Error_DataSourceNameNotSet"; + internal const string Error_DataSourceInvalidIdentifier = "Error_DataSourceInvalidIdentifier"; + internal const string Error_ParameterTypeNotExist = "Error_ParameterTypeNotExist"; + internal const string Error_InvalidParameterName = "Error_InvalidParameterName"; + internal const string Error_InvalidParameterType = "Error_InvalidParameterType"; + internal const string Error_InvalidParameterElement = "Error_InvalidParameterElement"; + internal const string Error_InvalidPropertyType = "Error_InvalidPropertyType"; + internal const string Error_TypeNotResolvedInMethodName = "Error_TypeNotResolvedInMethodName"; + internal const string Error_DelegateNoInvoke = "Error_DelegateNoInvoke"; + internal const string Error_TypeNotDelegate = "Error_TypeNotDelegate"; + internal const string Error_MethodSignatureMismatch = "Error_MethodSignatureMismatch"; + internal const string Error_MethodReturnTypeMismatch = "Error_MethodReturnTypeMismatch"; + internal const string Error_PropertyNotSet = "Error_PropertyNotSet"; + internal const string Error_ScopeCouldNotBeResolved = "Error_ScopeCouldNotBeResolved"; + internal const string Error_IfElseNotAllIfElseBranchDecl = "Error_ConditionalNotAllConditionalBranchDecl"; + internal const string Error_TypeTypeMismatchAmbiguity = "Error_TypeTypeMismatchAmbiguity"; + internal const string Error_InvalidCorrelationSetDatasource = "Error_InvalidCorrelationSetDatasource"; + internal const string Error_InvalidCorrelationSetType = "Error_InvalidCorrelationSetType"; + internal const string Error_MissingCorrelationParameterAttribute = "Error_MissingCorrelationParameterAttribute"; + internal const string Error_CorrelationTypeNotConsistent = "Error_CorrelationTypeNotConsistent"; + internal const string Error_CorrelationInvalid = "Error_CorrelationInvalid"; + internal const string Error_MissingDelegateMethod = "Error_MissingDelegateMethod"; + internal const string Error_MissingHostInterface = "Error_MissingHostInterface"; + internal const string Error_MissingMethodName = "Error_MissingMethodName"; + internal const string Error_NoBoundType = "Error_NoBoundType"; + internal const string Error_PortTypeNotAnInterface = "Error_PortTypeNotAnInterface"; + internal const string Error_MethodNotExists = "Error_MethodNotExists"; + internal const string Error_InvalidRequestResponseMethod = "Error_InvalidRequestResponseMethod"; + internal const string General_MissingService = "General_MissingService"; + internal const string Error_ScopeDuplicatedNameActivity = "Error_ScopeDuplicatedNameActivity"; + internal const string Error_DuplicatedActivityID = "Error_DuplicatedActivityID"; + internal const string Error_DuplicatedParameterName = "Error_DuplicatedParameterName"; + internal const string Error_ScopeMissingSerializableAttribute = "Error_ScopeMissingSerializableAttribute"; + internal const string Error_FieldNotExists = "Error_FieldNotExists"; + internal const string Error_PropertyNotExists = "Error_PropertyNotExists"; + internal const string Error_FieldTypeMismatch = "Error_FieldTypeMismatch"; + internal const string Error_PropertyTypeMismatch = "Error_PropertyTypeMismatch"; + internal const string Error_TypeNotResolvedInFieldName = "Error_TypeNotResolvedInFieldName"; + internal const string Error_TypeNotResolvedInPropertyName = "Error_TypeNotResolvedInPropertyName"; + internal const string Error_FieldGenericParamTypeMismatch = "Error_FieldGenericParamTypeMismatch"; + internal const string Error_TypeNotResolved = "Error_TypeNotResolved"; + internal const string Error_TypeIsUnboundedGeneric = "Error_TypeIsUnboundedGeneric"; + internal const string Error_MissingRootActivity = "Error_MissingRootActivity"; + internal const string Error_PropertyNotReadable = "Error_PropertyNotReadable"; + internal const string Error_PropertyNotWritable = "Error_PropertyNotWritable"; + internal const string Error_NotCompositeActivity = "Error_NotCompositeActivity"; + internal const string Error_TypeNotExist = "Error_TypeNotExist"; + internal const string Error_ActivityRefNotResolved = "Error_ActivityRefNotResolved"; + internal const string Error_ActivityRefNotMatchType = "Error_ActivityRefNotMatchType"; + internal const string Error_ActivityValidation = "Error_ActivityValidation"; + internal const string Error_ActiveChildExist = "Error_ActiveChildExist"; + internal const string Error_ActiveChildContextExist = "Error_ActiveChildContextExist"; + internal const string Error_CannotCompleteContext = "Error_CannotCompleteContext"; + internal const string Error_NoPasteSupport = "Error_NoPasteSupport"; + internal const string Error_UnknownSerializationStore = "Error_UnknownSerializationStore"; + internal const string Error_MissingCorrelationSet = "Error_MissingCorrelationSet"; + internal const string Error_CreateVariable = "Error_CreateVariable"; + internal const string Error_DuplicateCorrelationSetName = "Error_DuplicateCorrelationSetName"; + internal const string Error_DragDropInvalid = "Error_DragDropInvalid"; + internal const string AddingImplicitActivity = "AddingImplicitActivity"; + internal const string Failure_DoDefaultAction = "Failure_DoDefaultAction"; + internal const string Failure_DoDefaultActionCaption = "Failure_DoDefaultActionCaption"; + internal const string Error_FaultInsideAtomicScope = "Error_FaultInsideAtomicScope"; + internal const string Error_ListenNotMoreThanOneDelay = "Error_ListenNotMoreThanOneDelay"; + internal const string Error_AtomicScopeWithFaultHandlersActivityDecl = "Error_AtomicScopeWithFaultHandlersActivityDecl"; + internal const string Error_AtomicScopeWithCancellationHandlerActivity = "Error_AtomicScopeWithCancellationHandlerActivity"; + internal const string Error_ScopeDuplicateFaultHandlerActivityForAll = "Error_ScopeDuplicateFaultHandlerActivityForAll"; + internal const string Error_ScopeDuplicateFaultHandlerActivityFor = "Error_ScopeDuplicateFaultHandlerActivityFor"; + internal const string Error_AtomicScopeNestedInNonLRT = "Error_AtomicScopeNestedInNonLRT"; + internal const string Error_LRTScopeNestedInNonLRT = "Error_LRTScopeNestedInNonLRT"; + internal const string Error_CAGNotAllChildrenConditioned = "Error_CAGNotAllChildrenConditioned"; + internal const string Error_ConditionedActivityChildCount = "Error_ConditionedActivityChildCount"; + internal const string Error_NegativeValue = "Error_NegativeValue"; + internal const string Error_MethodWithReturnType = "Error_MethodWithReturnType"; + internal const string Error_SendReceiveOrderIncorrect = "Error_SendReceiveOrderIncorrect"; + internal const string Error_ReceiveSendOrderIncorrect = "Error_ReceiveSendOrderIncorrect"; + internal const string Error_CompensateBadNesting = "Error_CompensateBadNesting"; + internal const string Error_ReferencedAssemblyIsInvalid = "Error_ReferencedAssemblyIsInvalid"; + internal const string Error_TypeToXsdConversion = "Error_TypeToXsdConversion"; + internal const string Error_FieldTypeNotResolved = "Error_FieldTypeNotResolved"; + internal const string Error_PropertyTypeNotResolved = "Error_PropertyTypeNotResolved"; + internal const string Error_CouldNotDeserializeXomlFile = "Error_CouldNotDeserializeXomlFile"; + internal const string Error_InternalCompilerError = "Error_InternalCompilerError"; + internal const string Error_TypeNotAsseblyQualified = "Error_TypeNotAsseblyQualified"; + internal const string CompilerWarning_StandardAssemlbyInReferences = "CompilerWarning_StandardAssemlbyInReferences"; + internal const string Error_SuspendInAtomicScope = "Error_SuspendInAtomicScope"; + internal const string Error_InvalidActivityExecutionContext = "Error_InvalidActivityExecutionContext"; + internal const string Error_NoRuntimeAvailable = "Error_NoRuntimeAvailable"; + internal const string Error_CanNotChangeAtRuntime = "Error_CanNotChangeAtRuntime"; + internal const string Error_DataContextNotInitialized = "Error_DataContextNotInitialized"; + internal const string Error_DataContextAlreadyInitialized = "Error_DataContextAlreadyInitialized"; + internal const string Error_ParseActivityNameDoesNotExist = "Error_ParseActivityNameDoesNotExist"; + internal const string Error_NoParameterPropertyDeclared = "Error_NoParameterPropertyDeclared"; + internal const string Error_PropertyInvalidIdentifier = "Error_PropertyInvalidIdentifier"; + internal const string Error_WorkflowDefinitionModified = "Error_WorkflowDefinitionModified"; + internal const string Error_FieldAlreadyExist = "Error_FieldAlreadyExist"; + internal const string Failure_FieldAlreadyExist = "Failure_FieldAlreadyExist"; + internal const string Error_DifferentTypeFieldExists = "Error_DifferentTypeFieldExists"; + internal const string Error_RootActivityTypeInvalid = "Error_RootActivityTypeInvalid"; + internal const string Error_RootActivityTypeInvalid2 = "Error_RootActivityTypeInvalid2"; + internal const string Error_CannotCompile_No_XClass = "Error_CannotCompile_No_XClass"; + internal const string Error_TemplateActivityIsNotActivity = "Error_TemplateActivityIsNotActivity"; + internal const string Error_TypeIsNotRootActivity = "Error_TypeIsNotRootActivity"; + internal const string Error_NoTypeProvider = "Error_NoTypeProvider"; + internal const string Error_NotCodeGeneratorType = "Error_NotCodeGeneratorType"; + internal const string Error_NotDataContext = "Error_NotDataContext"; + internal const string Error_MissingDefaultConstructor = "Error_MissingDefaultConstructor"; + internal const string Error_ContextStackItemMissing = "Error_ContextStackItemMissing"; + internal const string Error_UnexpectedArgumentType = "Error_UnexpectedArgumentType"; + internal const string Error_EmptyArgument = "Error_EmptyArgument"; + internal const string Error_DPAlreadyExist = "Error_DPAlreadyExist"; + internal const string Error_DuplicateDynamicProperty = "Error_DuplicateDynamicProperty"; + internal const string Error_DynamicPropertyTypeValueMismatch = "Error_DynamicPropertyTypeValueMismatch"; + internal const string Error_DynamicPropertyNoSupport = "Error_DynamicPropertyNoSupport"; + internal const string Error_NoContextForDatasource = "Error_NoContextForDatasource"; + internal const string Error_NoContextForDatasourceCaption = "Error_NoContextForDatasourceCaption"; + internal const string Error_DataSourceHasParent = "Error_DataSourceHasParent"; + internal const string OnTaskCompletedDescr = "OnTaskCompletedDescr"; + internal const string OnTaskInitializedDescr = "OnTaskInitializedDescr"; + internal const string Error_InvalidXmlData = "Error_InvalidXmlData"; + internal const string Error_HandlerNotOnRoot = "Error_HandlerNotOnRoot"; + internal const string Error_InvalidArgumentIndex = "Error_InvalidArgumentIndex"; + internal const string Error_UITypeEditorTypeNotUITypeEditor = "Error_UITypeEditorTypeNotUITypeEditor"; + internal const string FilterDescription_UITypeEditor = "FilterDescription_UITypeEditor"; + internal const string Error_UserCodeFilesNotAllowed = "Error_UserCodeFilesNotAllowed"; + internal const string Error_CodeWithinNotAllowed = "Error_CodeWithinNotAllowed"; + internal const string Error_TypeNotAuthorized = "Error_TypeNotAuthorized"; + internal const string Error_CantDetermineBaseType = "Error_CantDetermineBaseType"; + internal const string Error_MultipleSelectNotSupportedForBindAndPromote = "Error_MultipleSelectNotSupportedForBindAndPromote"; + internal const string Error_CantDetermineBaseTypeCaption = "Error_CantDetermineBaseTypeCaption"; + internal const string Error_CantDeterminePropertyBaseType = "Error_CantDeterminePropertyBaseType"; + internal const string Error_NullCustomActivityTypeName = "Error_NullCustomActivityTypeName"; + internal const string Error_InvalidAttribute = "Error_InvalidAttribute"; + internal const string Error_InvalidAttributes = "Error_InvalidAttributes"; + internal const string Error_ConfigFileMissingOrInvalid = "Error_ConfigFileMissingOrInvalid"; + internal const string Error_CantHaveContextActivity = "Error_CantHaveContextActivity"; + internal const string Error_SynchronizedNeedsDataContext = "Error_SynchronizedNeedsDataContext"; + internal const string Error_MoreThanOneFaultHandlersActivityDecl = "Error_MoreThanOneFaultHandlersActivityDecl"; + internal const string Error_MoreThanOneEventHandlersDecl = "Error_MoreThanOneEventHandlersDecl"; + internal const string Error_MoreThanOneCancelHandler = "Error_MoreThanOneCancelHandler"; + internal const string Error_MetaDataInterfaceMissing = "Error_MetaDataInterfaceMissing"; + internal const string Error_NonActivityExecutor = "Error_NonActivityExecutor"; + internal const string Error_DynamicUpdateEvaluation = "Error_DynamicUpdateEvaluation"; + internal const string Error_CollectionHasNullEntry = "Error_CollectionHasNullEntry"; + internal const string Error_MissingContextProperty = "Error_MissingContextProperty"; + internal const string Error_AssociatedDesignerMissing = "Error_AssociatedDesignerMissing"; + internal const string Error_MissingContextActivityProperty = "Error_MissingContextActivityProperty"; + internal const string Error_MissingActivityProperty = "Error_MissingActivityProperty"; + internal const string Error_MissingOwnerTypeProperty = "Error_MissingOwnerTypeProperty"; + internal const string Error_DOIsNotAnActivity = "Error_DOIsNotAnActivity"; + internal const string Error_PropertyCanBeOnlyCleared = "Error_PropertyCanBeOnlyCleared"; + internal const string Error_PropertyDefaultTypeMismatch = "Error_PropertyDefaultTypeMismatch"; + internal const string Error_PropertyDefaultIsReference = "Error_PropertyDefaultIsReference"; + // workflow load errors + internal const string Error_WorkflowLoadFailed = "Error_WorkflowLoadFailed"; + internal const string Error_WorkflowLoadValidationFailed = "Error_WorkflowLoadValidationFailed"; + internal const string Error_WorkflowLoadDeserializationFailed = "Error_WorkflowLoadDeserializationFailed"; + internal const string Error_WorkflowLoadTypeMismatch = "Error_WorkflowLoadTypeMismatch"; + internal const string Error_WorkflowLoadInvalidXoml = "Error_WorkflowLoadInvalidXoml"; + internal const string Error_WorkflowLoadNotValidRootType = "Error_WorkflowLoadNotValidRootType"; + internal const string Error_CantCreateInstanceOfComponent = "Error_CantCreateInstanceOfComponent"; + internal const string Error_NotComponentFactoryType = "Error_NotComponentFactoryType"; + internal const string Error_WorkflowTerminated = "Error_WorkflowTerminated"; + + // serializer errrors + internal const string Error_SerializerAttributesFoundInComplexProperty = "Error_SerializerAttributesFoundInComplexProperty"; + internal const string Error_InvalidDataFound = "Error_InvalidDataFound"; + internal const string Error_InvalidDataFoundForType = "Error_InvalidDataFoundForType"; + internal const string Error_InvalidDataFoundForType1 = "Error_InvalidDataFoundForType1"; + internal const string Error_SerializerTypeNotResolved = "Error_SerializerTypeNotResolved"; + internal const string Error_MarkupSerializerTypeNotResolved = "Error_MarkupSerializerTypeNotResolved"; + internal const string Error_SerializerTypeNotResolvedWithInnerError = "Error_SerializerTypeNotResolvedWithInnerError"; + internal const string Error_SerializerNotAvailable = "Error_SerializerNotAvailable"; + internal const string Error_SerializerNotAvailableForSerialize = "Error_SerializerNotAvailableForSerialize"; + internal const string Error_SerializerCreateInstanceFailed = "Error_SerializerCreateInstanceFailed"; + internal const string Error_SerializerAddChildFailed = "Error_SerializerAddChildFailed"; + internal const string Error_SerializerNoPropertyAvailable = "Error_SerializerNoPropertyAvailable"; + internal const string Error_SerializerPrimitivePropertyReadOnly = "Error_SerializerPrimitivePropertyReadOnly"; + internal const string Error_SerializerCantChangeIsLocked = "Error_SerializerCantChangeIsLocked"; + internal const string Error_SerializerPrimitivePropertySetFailed = "Error_SerializerPrimitivePropertySetFailed"; + internal const string Error_SerializerPropertyGetFailed = "Error_SerializerPropertyGetFailed"; + internal const string Error_SerializerPrimitivePropertyNoLogic = "Error_SerializerPrimitivePropertyNoLogic"; + internal const string Error_SerializerPrimitivePropertyParentIsNull = "Error_SerializerPrimitivePropertyParentIsNull"; + internal const string Error_SerializerComplexPropertySetFailed = "Error_SerializerComplexPropertySetFailed"; + internal const string Error_SerializerNoChildNotion = "Error_SerializerNoChildNotion"; + internal const string Error_SerializerNoDynamicPropertySupport = "Error_SerializerNoDynamicPropertySupport"; + internal const string Error_SerializerNoSerializeLogic = "Error_SerializerNoSerializeLogic"; + internal const string Error_SerializerReadOnlyPropertyAndValueIsNull = "Error_SerializerReadOnlyPropertyAndValueIsNull"; + internal const string Error_SerializerReadOnlyParametersNoChild = "Error_SerializerReadOnlyParametersNoChild"; + internal const string Error_SerializerNotParameterBindingObject = "Error_SerializerNotParameterBindingObject"; + internal const string Error_SerializerThrewException = "Error_SerializerThrewException"; + internal const string Error_ActivityCollectionSerializer = "Error_ActivityCollectionSerializer"; + internal const string Error_MissingClassAttribute = "Error_MissingClassAttribute"; + internal const string Error_MissingClassAttributeValue = "Error_MissingClassAttributeValue"; + internal const string ExecutorCreationFailedErrorMessage = "ExecutorCreationFailedErrorMessage"; + internal const string VariableGetterCode_VB = "VariableGetterCode_VB"; + internal const string VariableGetterCode_CS = "VariableGetterCode_CS"; + internal const string VariableSetterCode_VB = "VariableSetterCode_VB"; + internal const string VariableSetterCode_CS = "VariableSetterCode_CS"; + internal const string StaticVariableGetterCode_VB = "StaticVariableGetterCode_VB"; + internal const string StaticVariableGetterCode_CS = "StaticVariableGetterCode_CS"; + internal const string StaticVariableSetterCode_VB = "StaticVariableSetterCode_VB"; + internal const string StaticVariableSetterCode_CS = "StaticVariableSetterCode_CS"; + internal const string EnterCodeBesidesCode_VB = "EnterCodeBesidesCode_VB"; + internal const string EnterCodeBesidesCode_CS = "EnterCodeBesidesCode_CS"; + internal const string LeaveCodeBesides1Code_VB = "LeaveCodeBesides1Code_VB"; + internal const string LeaveCodeBesides2Code_VB = "LeaveCodeBesides2Code_VB"; + internal const string LeaveCodeBesides1Code_CS = "LeaveCodeBesides1Code_CS"; + internal const string LeaveCodeBesides2Code_CS = "LeaveCodeBesides2Code_CS"; + internal const string VariableSetterName = "VariableSetterName"; + internal const string VariableGetterName = "VariableGetterName"; + internal const string HandlerGetterName = "HandlerGetterName"; + internal const string WorkflowCreatorName = "WorkflowCreatorName"; + internal const string ActivityMethod = "ActivityMethod"; + internal const string CustomActivityPrivateField = "CustomActivityPrivateField"; + internal const string InitializedVariableDeclaration_VB = "InitializedVariableDeclaration_VB"; + internal const string InitializedVariableDeclaration_CS = "InitializedVariableDeclaration_CS"; + internal const string In = "In"; + internal const string Out = "Out"; + internal const string Ref = "Ref"; + internal const string Required = "Required"; + internal const string Optional = "Optional"; + internal const string Parameters = "Parameters"; + internal const string Properties = "Properties"; + internal const string Error_RecursionDetected = "Error_RecursionDetected"; + internal const string Warning_UnverifiedRecursion = "Warning_UnverifiedRecursion"; + internal const string AddConstructorCode = "AddConstructorCode"; + internal const string Error_UninitializedCorrelation = "Error_UninitializedCorrelation"; + internal const string Error_CorrelationAlreadyInitialized = "Error_CorrelationAlreadyInitialized"; + internal const string Error_CorrelatedSendReceiveAtomicScope = "Error_CorrelatedSendReceiveAtomicScope"; + internal const string Warning_ActivityValidation = "Warning_ActivityValidation"; + internal const string Warning_EmptyBehaviourActivity = "Warning_EmptyBehaviourActivity"; + internal const string Error_ParallelActivationNoCorrelation = "Error_ParallelActivationNoCorrelation"; + internal const string Error_MethodNotAccessible = "Error_MethodNotAccessible"; + internal const string Error_FieldNotAccessible = "Error_FieldNotAccessible"; + internal const string Error_PropertyNotAccessible = "Error_PropertyNotAccessible"; + internal const string Error_GenericArgumentsNotAllowed = "Error_GenericArgumentsNotAllowed"; + internal const string Error_InvalidIdentifier = "Error_InvalidIdentifier"; + internal const string Error_InvalidLanguageIdentifier = "Error_InvalidLanguageIdentifier"; + internal const string DuplicateActivityIdentifier = "DuplicateActivityIdentifier"; + internal const string Error_MissingAttribute = "Error_MissingAttribute"; + internal const string Error_LoadUIPropertiesFile = "Error_LoadUIPropertiesFile"; + internal const string Error_SerializerEventGetFailed = "Error_SerializerEventGetFailed"; + internal const string Error_SerializerEventFailed = "Error_SerializerEventFailed"; + internal const string Error_SerializerNoMemberFound = "Error_SerializerNoMemberFound"; + internal const string Error_DynamicEventConflict = "Error_DynamicEventConflict"; + internal const string Error_SerializerMemberSetFailed = "Error_SerializerMemberSetFailed"; + internal const string Error_ContentPropertyCouldNotBeFound = "Error_ContentPropertyCouldNotBeFound"; + internal const string Error_ContentPropertyValueInvalid = "Error_ContentPropertyValueInvalid"; + internal const string Error_ContentPropertyNoSetter = "Error_ContentPropertyNoSetter"; + internal const string Error_ContentCanNotBeConverted = "Error_ContentCanNotBeConverted"; + internal const string Error_ContentPropertyCanNotBeNull = "Error_ContentPropertyCanNotBeNull"; + internal const string Error_SerializerTypeMismatch = "Error_SerializerTypeMismatch"; + internal const string Error_CouldNotAddValueInContentProperty = "Error_CouldNotAddValueInContentProperty"; + internal const string Error_SerializerTypeRequirement = "Error_SerializerTypeRequirement"; + internal const string Error_CanNotAddActivityInBlackBoxActivity = "Error_CanNotAddActivityInBlackBoxActivity"; + internal const string Error_ContentPropertyCanNotSupportCompactFormat = "Error_ContentPropertyCanNotSupportCompactFormat"; + internal const string Error_ContentPropertyNoMultipleContents = "Error_ContentPropertyNoMultipleContents"; + internal const string Error_InternalSerializerError = "Error_InternalSerializerError"; + internal const string Error_DictionarySerializerNonDictionaryObject = "Error_DictionarySerializerNonDictionaryObject"; + internal const string Error_DictionarySerializerKeyNotFound = "Error_DictionarySerializerKeyNotFound"; + internal const string Error_InvalidCancelActivityState = "Error_InvalidCancelActivityState"; + internal const string Error_InvalidCompensateActivityState = "Error_InvalidCompensateActivityState"; + internal const string Error_InvalidCloseActivityState = "Error_InvalidCloseActivityState"; + internal const string Error_SealedPropertyMetadata = "Error_SealedPropertyMetadata"; + internal const string Error_MemberNotFound = "Error_MemberNotFound"; + internal const string Error_EmptyPathValue = "Error_EmptyPathValue"; + internal const string Error_InvalidCompensatingState = "Error_InvalidCompensatingState"; + internal const string Error_InvalidCancelingState = "Error_InvalidCancelingState"; + internal const string Error_InvalidClosingState = "Error_InvalidClosingState"; + internal const string Error_InvalidStateToExecuteChild = "Error_InvalidStateToExecuteChild"; + internal const string Error_InvalidExecutionState = "Error_InvalidExecutionState"; + internal const string Error_InvalidInitializingState = "Error_InvalidInitializingState"; + internal const string Error_InvalidInvokingState = "Error_InvalidInvokingState"; + internal const string Error_NotRegisteredAs = "Error_NotRegisteredAs"; + internal const string Error_AlreadyRegisteredAs = "Error_AlreadyRegisteredAs"; + internal const string Error_InsertingChildControls = "Error_InsertingChildControls"; + internal const string Error_EmptyToolTipRectangle = "Error_EmptyToolTipRectangle"; + internal const string Error_EmptyRectangleValue = "Error_EmptyRectangleValue"; + internal const string Error_InvalidShadowRectangle = "Error_InvalidShadowRectangle"; + internal const string Error_InvalidShadowDepth = "Error_InvalidShadowDepth"; + internal const string Error_InvalidLightSource = "Error_InvalidLightSource"; + internal const string Error_ChangingDock = "Error_ChangingDock"; + internal const string Error_NullOrEmptyValue = "Error_NullOrEmptyValue"; + internal const string Error_InvalidStateImages = "Error_InvalidStateImages"; + internal const string Error_InvalidConnectorSegment = "Error_InvalidConnectorSegment"; + internal const string Error_InvalidConnectorSource = "Error_InvalidConnectorSource"; + internal const string Error_CreatingToolTip = "Error_CreatingToolTip"; + internal const string Error_InvalidDockStyle = "Error_InvalidDockStyle"; + internal const string Error_InvalidConnectorValue = "Error_InvalidConnectorValue"; + internal const string Error_InvalidDesignerVerbValue = "Error_InvalidDesignerVerbValue"; + internal const string Error_InvalidRuntimeType = "Error_InvalidRuntimeType"; + internal const string Error_InvalidArgumentValue = "Error_InvalidArgumentValue"; + internal const string Error_InvalidRadiusValue = "Error_InvalidRadiusValue"; + internal const string ToolTipString = "ToolTipString"; + + //Collection Editor Resources + internal const string CollectionEditorCaption = "CollectionEditorCaption"; + internal const string CollectionEditorProperties = "CollectionEditorProperties"; + internal const string CollectionEditorPropertiesMultiSelect = "CollectionEditorPropertiesMultiSelect"; + internal const string CollectionEditorPropertiesNone = "CollectionEditorPropertiesNone"; + internal const string CollectionEditorCantRemoveItem = "CollectionEditorCantRemoveItem"; + internal const string CollectionEditorUndoBatchDesc = "CollectionEditorUndoBatchDesc"; + internal const string CollectionEditorInheritedReadOnlySelection = "CollectionEditorInheritedReadOnlySelection"; + internal const string Error_ParameterAlreadyExists = "Error_ParameterAlreadyExists"; + internal const string Error_PropertyAlreadyExists = "Error_PropertyAlreadyExists"; + internal const string Error_HiddenPropertyAlreadyExists = "Error_HiddenPropertyAlreadyExists"; + internal const string Error_CorrelationInUse = "Error_CorrelationInUse"; + internal const string Error_ItemNotExists = "Error_ItemNotExists"; + internal const string Error_NoHelpAvailable = "Error_NoHelpAvailable"; + internal const string Error_DuplicateWorkflow = "Error_DuplicateWorkflow"; + internal const string Error_Recursion = "Error_Recursion"; + internal const string Error_RootActivity = "Error_RootActivity"; + internal const string Error_ConditionDefinitionDeserializationFailed = "Error_ConditionDefinitionDeserializationFailed"; + internal const string Error_InvalidConditionDefinition = "Error_InvalidConditionDefinition"; + internal const string SR_InvokeTransactionalFromAtomic = "SR_InvokeTransactionalFromAtomic"; + internal const string Error_SuspendInAtomicCallChain = "Error_SuspendInAtomicCallChain"; + internal const string Error_LiteralPassedToOutRef = "Error_LiteralPassedToOutRef"; + internal const string Error_GeneratorShouldContainSingleActivity = "Error_GeneratorShouldContainSingleActivity"; + internal const string Error_DeclaringPropertyNotSupported = "Error_DeclaringPropertyNotSupported"; + internal const string Error_DeclaringEventNotSupported = "Error_DeclaringEventNotSupported"; + internal const string Error_DynamicEventNotSupported = "Error_DynamicEventNotSupported"; + internal const string Error_DynamicPropertyNotSupported = "Error_DynamicPropertyNotSupported"; + internal const string Error_ParameterTypeResolution = "Error_ParameterTypeResolution"; + + // Dynamic Validations + internal const string Error_DynamicActivity = "Error_DynamicActivity"; + internal const string Error_DynamicActivity2 = "Error_DynamicActivity2"; + internal const string Error_CompilerValidationFailed = "Error_CompilerValidationFailed"; + internal const string Error_RuntimeValidationFailed = "Error_RuntimeValidationFailed"; + internal const string Error_TransactionAlreadyCanceled = "Error_TransactionAlreadyCanceled"; + internal const string Error_RemoveExecutingActivity = "Error_RemoveExecutingActivity"; + internal const string Error_InsideAtomicScope = "Error_InsideAtomicScope"; + internal const string SuspendReason_WorkflowChange = "SuspendReason_WorkflowChange"; + + //type filtering + internal const string FilterDescription_ParameterDeclaration = "FilterDescription_ParameterDeclaration"; + internal const string FilterDescription_GenericArgument = "FilterDescription_GenericArgument"; + + + internal const string LibraryPathIsInvalid = "LibraryPathIsInvalid"; + + // Activity Set + internal const string Error_CreateValidator = "Error_CreateValidator"; + internal const string Error_InvalidPackageFile = "Error_InvalidPackageFile"; + internal const string Error_AddAssemblyRef = "Error_AddAssemblyRef"; + internal const string Error_AssemblyBadImage = "Error_AssemblyBadImage"; + internal const string BindPropertySetterName = "BindPropertySetterName"; + + // Bind validations + internal const string Error_CannotResolveActivity = "Error_CannotResolveActivity"; + internal const string Error_CannotResolveRelativeActivity = "Error_CannotResolveRelativeActivity"; + internal const string Error_PathNotSetForActivitySource = "Error_PathNotSetForActivitySource"; + internal const string Error_InvalidMemberPath = "Error_InvalidMemberPath"; + internal const string Error_TargetTypeMismatch = "Error_TargetTypeMismatch"; + internal const string Warning_ParameterBinding = "Warning_ParameterBinding"; + internal const string Error_ReferencedActivityPropertyNotBind = "Error_ReferencedActivityPropertyNotBind"; + internal const string Error_TargetTypeDataSourcePathMismatch = "Error_TargetTypeDataSourcePathMismatch"; + internal const string Bind_ActivityDataSourceRecursionDetected = "Bind_ActivityDataSourceRecursionDetected"; + internal const string Bind_DuplicateDataSourceNames = "Bind_DuplicateDataSourceNames"; + internal const string Error_PathNotSetForXmlDataSource = "Error_PathNotSetForXmlDataSource"; + internal const string Error_XmlDocumentLoadFailed = "Error_XmlDocumentLoadFailed"; + internal const string Error_XmlDataSourceInvalidPath = "Error_XmlDataSourceInvalidPath"; + internal const string Error_XmlDataSourceMultipleNodes = "Error_XmlDataSourceMultipleNodes"; + internal const string Error_XmlDataSourceInvalidXPath = "Error_XmlDataSourceInvalidXPath"; + internal const string Error_InvalidObjectRefFormat = "Error_InvalidObjectRefFormat"; + internal const string Error_ReadOnlyDataSource = "Error_ReadOnlyDataSource"; + internal const string Error_HandlerReadOnly = "Error_HandlerReadOnly"; + internal const string Error_XmlDataSourceReadOnly = "Error_XmlDataSourceReadOnly"; + internal const string Error_DataSourceNotExist = "Error_DataSourceNotExist"; + internal const string Error_PropertyNoGetter = "Error_PropertyNoGetter"; + internal const string Error_PropertyNoSetter = "Error_PropertyNoSetter"; + internal const string Error_PropertyHasNoGetterDefined = "Error_PropertyHasNoGetterDefined"; + internal const string Error_PropertyHasNoSetterDefined = "Error_PropertyHasNoSetterDefined"; + internal const string Error_PropertyReferenceNoGetter = "Error_PropertyReferenceNoGetter"; + internal const string Error_PropertyReferenceGetterNoAccess = "Error_PropertyReferenceGetterNoAccess"; + internal const string Error_PropertyHasIndexParameters = "Error_PropertyHasIndexParameters"; + internal const string Error_ReadOnlyField = "Error_ReadOnlyField"; + internal const string Error_NoEnclosingContext = "Error_NoEnclosingContext"; + internal const string Error_NestedPersistOnClose = "Error_NestedPersistOnClose"; + internal const string Error_NestedCompensatableActivity = "Error_NestedCompensatableActivity"; + internal const string Error_InvalidActivityForObjectDatasource = "Error_InvalidActivityForObjectDatasource"; + internal const string Error_DataSourceTypeConversionFailed = "Error_DataSourceTypeConversionFailed"; + internal const string Error_BindDialogWrongPropertyType = "Error_BindDialogWrongPropertyType"; + internal const string Error_BindDialogNoValidPropertySelected = "Error_BindDialogNoValidPropertySelected"; + internal const string Error_BindDialogBindNotValid = "Error_BindDialogBindNotValid"; + internal const string Error_BindDialogCanNotBindToItself = "Error_BindDialogCanNotBindToItself"; + internal const string Error_BindActivityReference = "Error_BindActivityReference"; + internal const string Error_NoTargetTypeForMethod = "Error_NoTargetTypeForMethod"; + internal const string Error_MethodDataSourceIsReadOnly = "Error_MethodDataSourceIsReadOnly"; + internal const string Error_NotMethodDataSource = "Error_NotMethodDataSource"; + internal const string Error_MethodDataSourceWithPath = "Error_MethodDataSourceWithPath"; + internal const string Error_PathSyntax = "Error_PathSyntax"; + internal const string Error_UnmatchedParen = "Error_UnmatchedParen"; + internal const string Error_UnmatchedBracket = "Error_UnmatchedBracket"; + internal const string Error_MemberWithSameNameExists = "Error_MemberWithSameNameExists"; + internal const string Error_ActivityIdentifierCanNotBeEmpty = "Error_ActivityIdentifierCanNotBeEmpty"; + internal const string Error_InvalidActivityIdentifier = "Error_InvalidActivityIdentifier"; + internal const string Error_ActivityBindTypeConversionError = "Error_ActivityBindTypeConversionError"; + internal const string EmptyValue = "EmptyValue"; + internal const string Error_PropertyTypeNotDefined = "Error_PropertyTypeNotDefined"; + + internal const string Error_CompilationFailed = "Error_CompilationFailed"; + internal const string Error_MissingCompilationContext = "Error_MissingCompilationContext"; + + internal const string InvokeWorkflowReference_VB = "InvokeWorkflowReference_VB"; + internal const string InvokeWorkflowReference_CS = "InvokeWorkflowReference_CS"; + internal const string Error_InvalidListItem = "Error_InvalidListItem"; + + internal const string ParserMapPINoWhitespace = "ParserMapPINoWhitespace"; + internal const string ParserMapPIBadCharEqual = "ParserMapPIBadCharEqual"; + internal const string ParserMapPIBadCharQuote = "ParserMapPIBadCharQuote"; + internal const string ParserMapPIBadKey = "ParserMapPIBadKey"; + internal const string ParserMapPIMissingKey = "ParserMapPIMissingKey"; + internal const string ParserMapPIKeyNotSet = "ParserMapPIKeyNotSet"; + internal const string ParserMismatchDelimiter = "ParserMismatchDelimiter"; + internal const string ParserDanglingClause = "ParserDanglingClause"; + internal const string UnknownDefinitionTag = "UnknownDefinitionTag"; + internal const string CDATASection = "CDATASection"; + internal const string TextSection = "TextSection"; + internal const string IncorrectSyntax = "IncorrectSyntax"; + internal const string IncorrectTypeSyntax = "IncorrectTypeSyntax"; + internal const string Error_MultipleRootActivityCreator = "Error_MultipleRootActivityCreator"; + internal const string Error_MustHaveParent = "Error_MustHaveParent"; + + // Workflow References + internal const string Error_ReferenceObjNotInitialized = "Error_ReferenceObjNotInitialized"; + internal const string Error_ReferenceInitResourceManager = "Error_ReferenceInitResourceManager"; + internal const string Error_ResourceReferenceGetObject = "Error_ResourceReferenceGetObject"; + internal const string Error_RefBindCantFindRef = "Error_RefBindCantFindRef"; + internal const string Error_RefBindMissingReferenceName = "Error_RefBindMissingReferenceName"; + internal const string Error_RefBindMissingAttribute = "Error_RefBindMissingAttribute"; + internal const string Error_ReferenceLoad = "Error_ReferenceLoad"; + internal const string Error_ReferenceMissingAttribute = "Error_ReferenceMissingAttribute"; + internal const string Error_ReferenceInvalidResourceFile = "Error_ReferenceInvalidResourceFile"; + internal const string Error_ReferenceEmptyName = "Error_ReferenceEmptyName"; + + internal const string HandlerInvokerName = "HandlerInvokerName"; + internal const string HandlerInvokerSwitchPrefix_CS = "HandlerInvokerSwitchPrefix_CS"; + internal const string HandlerInvokerSwitchPrefix_VB = "HandlerInvokerSwitchPrefix_VB"; + internal const string HandlerInvokerSwitchSuffix_CS = "HandlerInvokerSwitchSuffix_CS"; + internal const string HandlerInvokerSwitchSuffix_VB = "HandlerInvokerSwitchSuffix_VB"; + internal const string HandlerInvokerCaseBegin_CS = "HandlerInvokerCaseBegin_CS"; + internal const string HandlerInvokerCaseBegin_VB = "HandlerInvokerCaseBegin_VB"; + + // Activity Category + internal const string Standard = "Standard"; + internal const string Base = "Base"; + + //CustomActivityDesigner + internal const string ValidatorCompanionClassDesc = "ValidatorCompanionClassDesc"; + internal const string ExecutorCompanionClassDesc = "ExecutorCompanionClassDesc"; + internal const string DesignerCompanionClassDesc = "DesignerCompanionClassDesc"; + internal const string CustomActivityBaseTypeDesc = "CustomActivityBaseTypeDesc"; + internal const string ActivityProperties = "ActivityProperties"; + internal const string ActivityPropertiesDesc = "ActivityPropertiesDesc"; + internal const string CompanionClasses = "CompanionClasses"; + internal const string ActivityDesc = "Activity"; + internal const string Error_TypeConversionFailed = "Error_TypeConversionFailed"; + internal const string SupportDataContext = "SupportDataContext"; + internal const string AdvancedCategory = "AdvancedCategory"; + internal const string SupportDataContextDesc = "SupportDataContextDesc"; + internal const string BaseCompanionClassName = "BaseCompanionClassName"; + internal const string BaseCompanionClassDesc = "BaseCompanionClassDesc"; + internal const string Designer = "Designer"; + internal const string Validator = "Validator"; + internal const string Executor = "Executor"; + internal const string BaseActivityType = "BaseActivityType"; + internal const string Error_NotBuiltInActivity = "Error_NotBuiltInActivity"; + internal const string NoChildActivities_Message = "NoChildActivities_Message"; + internal const string NoChildActivities_Caption = "NoChildActivities_Caption"; + internal const string Error_CustomActivityCantCreate = "Error_CustomActivityCantCreate"; + internal const string Error_CantChangeBuiltInActivity = "Error_CantChangeBuiltInActivity"; + internal const string Error_CantAddBeforeBuiltInActivity = "Error_CantAddBeforeBuiltInActivity"; + internal const string Error_CantAddAfterNonBuiltInActivity = "Error_CantAddAfterNonBuiltInActivity"; + internal const string Error_CannotAddRemoveChildActivities = "Error_CannotAddRemoveChildActivities"; + internal const string Error_CantFindBuiltInActivity = "Error_CantFindBuiltInActivity"; + internal const string Error_MissingBaseCompanionClassAttribute = "Error_MissingBaseCompanionClassAttribute"; + internal const string Error_CantFindBuiltInParent = "Error_CantFindBuiltInParent"; + internal const string Error_CantCreateInstanceOfBaseType = "Error_CantCreateInstanceOfBaseType"; + internal const string Error_CustomActivityTypeCouldNotBeFound = "Error_CustomActivityTypeCouldNotBeFound"; + internal const string None = "None"; + internal const string AtomicTransaction = "AtomicTransaction"; + internal const string LocalDataContext = "LocalDataContext"; + internal const string LocalDataContextDesc = "LocalDataContextDesc"; + internal const string CompanionClass = "CompanionClass"; + internal const string Error_AlreadyRootActivity = "Error_AlreadyRootActivity"; + internal const string RootActivityName = "RootActivityName"; + internal const string RootActivityNameDesc = "RootActivityNameDesc"; + internal const string CustomProperties = "CustomProperties"; + internal const string VisibleDescr = "VisibleDescr"; + internal const string EditableDescr = "EditableDescr"; + internal const string Error_CantCreateMethod = "Error_CantCreateMethod"; + internal const string Error_CantEditNullValue = "Error_CantEditNullValue"; + internal const string Error_CompanionTypeNotSet = "Error_CompanionTypeNotSet"; + internal const string Error_CompanionClassNameCanNotBeEmpty = "Error_CompanionClassNameCanNotBeEmpty"; + internal const string Error_CouldNotEmitFieldInLocalDataContext = "Error_CouldNotEmitFieldInLocalDataContext"; + internal const string Error_CouldNotEmitMethodInLocalDataContext = "Error_CouldNotEmitMethodInLocalDataContext"; + internal const string Error_DerivationFromTypeWithLocalDataContext = "Error_DerivationFromTypeWithLocalDataContext"; + internal const string Error_CompanionTypeDerivationError = "Error_CompanionTypeDerivationError"; + internal const string Error_CantCreateDataContextClass = "Error_CantCreateDataContextClass"; + internal const string ArrayExistingBind = "ArrayExistingBind"; + internal const string Error_NoMatchingFieldsOrProperties = "Error_NoMatchingFieldsOrProperties"; + internal const string ChooseFieldPropertyDatasource = "ChooseFieldPropertyDatasource"; + + internal const string SupportsTransaction = "SupportsTransaction"; + internal const string SupportsExceptions = "SupportsExceptions"; + internal const string SupportsCancellationHandlerActivity = "SupportsCancellationHandlerActivity"; + internal const string SupportsEvents = "SupportsEvents"; + internal const string SupportsDataSources = "SupportsDataSources"; + internal const string SupportsCompensationHandler = "SupportsCompensationHandler"; + internal const string SupportsCompensationHandlerDesc = "SupportsCompensationHandlerDesc"; + internal const string SupportsTransactionDesc = "SupportsTransactionDesc"; + internal const string SupportsExceptionsDesc = "SupportsExceptionsDesc"; + internal const string SupportsCancelHandlerDesc = "SupportsCancelHandlerDesc"; + internal const string SupportsEventsDesc = "SupportsEventsDesc"; + internal const string TransactionDesc = "TransactionDesc"; + + internal const string Error_BaseTypeMustBeActivity = "Error_BaseTypeMustBeActivity"; + internal const string ExistingActivityBindTitle = "ExistingActivityBindTitle"; + internal const string ExistingActivityBindLabel = "ExistingActivityBindLabel"; + internal const string ExistingFieldPropBindTitle = "ExistingFieldPropBindTitle"; + internal const string ExistingFieldPropBindLabel = "ExistingFieldPropBindLabel"; + internal const string ProvidesSynchronization = "ProvidesSynchronization"; + internal const string ProvidesSynchronizationDesc = "ProvidesSynchronizationDesc"; + internal const string SynchronizationHandles = "SynchronizationHandles"; + internal const string SynchronizationHandlesDesc = "SynchronizationHandlesDesc"; + + internal const string Error_TransactionAlreadyApplied = "Error_TransactionAlreadyApplied"; + internal const string Error_BindBaseTypeNotSpecified = "Error_BindBaseTypeNotSpecified"; + internal const string NonDelegateTargetType = "NonDelegateTargetType"; + internal const string Error_ClassnameNotInRootNamespace = "Error_ClassnameNotInRootNamespace"; + internal const string Error_CantUseCurrentProjectTypeAsBase = "Error_CantUseCurrentProjectTypeAsBase"; + internal const string Error_UnboundGenericType = "Error_UnboundGenericType"; + internal const string Error_UnboundGenericTypeDataSource = "Error_UnboundGenericTypeDataSource"; + internal const string Error_BaseTypeUnknown = "Error_BaseTypeUnknown"; + internal const string Error_UnconfiguredBind = "Error_UnconfiguredBind"; + internal const string Error_CanNotEmitMemberInLocalDataContext = "Error_CanNotEmitMemberInLocalDataContext"; + internal const string Error_DesignedTypeNotFound = "Error_DesignedTypeNotFound"; + internal const string Error_PathCouldNotBeResolvedToMember = "Error_PathCouldNotBeResolvedToMember"; + internal const string Error_EdittingNullCollection = "Error_EdittingNullCollection"; + internal const string Error_MoreThanOneCompensationDecl = "Error_MoreThanOneCompensationDecl"; + internal const string Error_ParentDoesNotSupportCompensation = "Error_ParentDoesNotSupportCompensation"; + internal const string Error_CantResolveEventHandler = "Error_CantResolveEventHandler"; + internal const string Error_XSDObjectTypeNotSerializable = "Error_XSDObjectTypeNotSerializable"; + internal const string AEC_InvalidActivity = "AEC_InvalidActivity"; + internal const string GetDynamicActivities_InvalidActivity = "GetDynamicActivities_InvalidActivity"; + internal const string AEC_InvalidNestedActivity = "AEC_InvalidNestedActivity"; + internal const string Error_IDNotSetForActivitySource = "Error_IDNotSetForActivitySource"; + internal const string Error_InvalidCustomPropertyName = "Error_InvalidCustomPropertyName"; + internal const string Error_InvalidCustomPropertyType = "Error_InvalidCustomPropertyType"; + + internal const string Error_DPReadOnly = "Error_DPReadOnly"; + internal const string Error_DPMetaPropertyBinding = "Error_DPMetaPropertyBinding"; + internal const string Error_DPSetValueBind = "Error_DPSetValueBind"; + internal const string Error_DPSetValueHandler = "Error_DPSetValueHandler"; + internal const string Error_DPGetValueHandler = "Error_DPGetValueHandler"; + internal const string Error_DPAddHandlerNonEvent = "Error_DPAddHandlerNonEvent"; + internal const string Error_DPAddHandlerMetaProperty = "Error_DPAddHandlerMetaProperty"; + internal const string Error_DPRemoveHandlerBind = "Error_DPRemoveHandlerBind"; + internal const string Error_LanguageNeedsToBeVBCSharp = "Error_LanguageNeedsToBeVBCSharp"; + internal const string Error_TargetFxNotSupported = "Error_TargetFxNotSupported"; + internal const string Error_CantConvertValueValue = "Error_CantConvertValueValue"; + internal const string Error_TypeIsNotValid = "Error_TypeIsNotValid"; + internal const string Error_TypePropertyInvalid = "Error_TypePropertyInvalid"; + internal const string Error_EventCantBeMetaProperty = "Error_EventCantBeMetaProperty"; + internal const string Error_EventMustBeDelegate = "Error_EventMustBeDelegate"; + internal const string Error_DPPropertyTypeMissing = "Error_DPPropertyTypeMissing"; + + internal const string TransactionalContextActivityDescription = "TransactionalContextActivityDescription"; + internal const string CompensatableTransactionalContextActivityDescription = "CompensatableTransactionalContextActivityDescription"; + internal const string SynchronizationScopeActivityDescription = "SynchronizationScopeActivityDescription"; + internal const string SequenceActivityDescription = "SequenceActivityDescription"; + internal const string CompensateActivityDescription = "CompensateActivityDescription"; + internal const string Error_CompensateBadTargetTX = "Error_CompensateBadTargetTX"; + internal const string Error_CancelHandlerParentNotScope = "Error_CancelHandlerParentNotScope"; + internal const string FaultHandlerActivityDescription = "FaultHandlerActivityDescription"; + internal const string Error_ExceptionTypeNotException = "Error_ExceptionTypeNotException"; + internal const string Error_FaultIsNotOfFaultType = "Error_FaultIsNotOfFaultType"; + internal const string Error_FaultTypeNoDefaultConstructor = "Error_FaultTypeNoDefaultConstructor"; + internal const string FilterDescription_FaultHandlerActivity = "FilterDescription_FaultHandlerActivity"; + internal const string Error_FaultHandlerActivityParentNotFaultHandlersActivity = "Error_FaultHandlerActivityParentNotFaultHandlersActivity"; + internal const string Error_FaultHandlerActivityAllMustBeLast = "Error_FaultHandlerActivityAllMustBeLast"; + internal const string Error_FaultHandlersActivityDeclNotAllFaultHandlerActivityDecl = "Error_FaultHandlersActivityDeclNotAllFaultHandlerActivityDecl"; + internal const string Error_FaultHandlerActivityWrongOrder = "Error_FaultHandlerActivityWrongOrder"; + internal const string Error_SenderMustBeActivityExecutionContext = "Error_SenderMustBeActivityExecutionContext"; + internal const string Error_XomlWorkflowHasCode = "Error_XomlWorkflowHasCode"; + internal const string Error_WrongParamForActivityResolveEventArgs = "Error_WrongParamForActivityResolveEventArgs"; + internal const string Error_ValidatorThrewException = "Error_ValidatorThrewException"; + internal const string Error_Missing_CanModifyProperties_True = "Error_Missing_CanModifyProperties_True"; + internal const string Error_Missing_CanModifyProperties_False = "Error_Missing_CanModifyProperties_False"; + internal const string Error_ModelingConstructsCanNotContainModelingConstructs = "Error_ModelingConstructsCanNotContainModelingConstructs"; + internal const string Error_RootIsNotEnabled = "Error_RootIsNotEnabled"; + internal const string Error_MissingSetAccessor = "Error_MissingSetAccessor"; + internal const string Error_MissingAddHandler = "Error_MissingAddHandler"; + internal const string Error_MissingCLRProperty = "Error_MissingCLRProperty"; + + internal const string Error_NotReadOnlyProperty = "Error_NotReadOnlyProperty"; + internal const string Error_InvalidDependencyProperty = "Error_InvalidDependencyProperty"; + internal const string Error_ActivityNameExist = "Error_ActivityNameExist"; + internal const string CannotCreateAttribute = "CannotCreateAttribute"; + internal const string NamespaceAndDeclaringTypeCannotBeNull = "NamespaceAndDeclaringTypeCannotBeNull"; + internal const string NotElementType = "NotElementType"; + + //Layout persistence errors + internal const string Error_LayoutSerializationActivityNotFound = "Error_LayoutSerializationActivityNotFound"; + internal const string Error_LayoutSerializationAssociatedActivityNotFound = "Error_LayoutSerializationAssociatedActivityNotFound"; + internal const string Error_LayoutSerializationPersistenceSupport = "Error_LayoutSerializationPersistenceSupport"; + internal const string Error_LayoutSerializationRootDesignerNotFound = "Error_LayoutSerializationRootDesignerNotFound"; + internal const string Error_ParameterCannotBeEmpty = "Error_ParameterCannotBeEmpty"; + internal const string InvalidExecutionStatus = "InvalidExecutionStatus"; + internal const string Error_LayoutDeserialization = "Error_LayoutDeserialization"; + internal const string Error_LayoutSerialization = "Error_LayoutSerialization"; + + internal const string Error_SerializerStackOverflow = "Error_SerializerStackOverflow"; + internal const string Error_InvalidActivityForWorkflowChanges = "Error_InvalidActivityForWorkflowChanges"; + internal const string Error_InvalidMemberType = "Error_InvalidMemberType"; + internal const string Error_BindPathNullValue = "Error_BindPathNullValue"; + internal const string Error_MarkupExtensionMissingTerminatingCharacter = "Error_MarkupExtensionMissingTerminatingCharacter"; + internal const string Error_MarkupExtensionDeserializeFailed = "Error_MarkupExtensionDeserializeFailed"; + internal const string Error_ApplyDynamicChangeFailed = "Error_ApplyDynamicChangeFailed"; + internal const string Error_ActivityCircularReference = "Error_ActivityCircularReference"; + internal const string Error_ValidatorTypeIsInvalid = "Error_ValidatorTypeIsInvalid"; + internal const string Error_InvalidServiceProvider = "Error_InvalidServiceProvider"; + internal const string Error_InvalidRootForWorkflowChanges = "Error_InvalidRootForWorkflowChanges"; + internal const string Error_ExtraCharacterFoundAtEnd = "Error_ExtraCharacterFoundAtEnd"; + internal const string Error_WorkflowChangesNotSupported = "Error_WorkflowChangesNotSupported"; + internal const string Error_TypeSystemAttributeArgument = "Error_TypeSystemAttributeArgument"; + + internal const string Error_InvalidElementFoundForType = "Error_InvalidElementFoundForType"; + internal const string Error_ActivitySaveLoadNotCalled = "Error_ActivitySaveLoadNotCalled"; + internal const string Error_CanNotBindProperty = "Error_CanNotBindProperty"; +} +#endif \ No newline at end of file diff --git a/src/IFoxCAD.Basal/Sortedset/Sortedset.cs b/src/IFoxCAD.Basal/Sortedset/Sortedset.cs new file mode 100644 index 0000000..fce371a --- /dev/null +++ b/src/IFoxCAD.Basal/Sortedset/Sortedset.cs @@ -0,0 +1,2913 @@ +#if NET35 +// #define USING_HASH_SET +// ==++== +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +// ==--== +/*============================================================ +** +** Class: SortedSet +** +** Purpose: A generic sorted set. +** +** Date: August 15, 2008 +** +===========================================================*/ + + +namespace System.Collections.Generic +{ + using System; + using System.Diagnostics; + using System.Diagnostics.CodeAnalysis; + using System.Runtime.Serialization; + + + + // + // A binary search tree is a red-black tree if it satisfies the following red-black properties: + // 1. Every node is either red or black + // 2. Every leaf (nil node) is black + // 3. If a node is red, then both its children are black + // 4. Every simple path from a node to a descendant leaf contains the same number of black nodes + // + // The basic idea of red-black tree is to represent 2-3-4 trees as standard BSTs but to add one extra bit of information + // per node to encode 3-nodes and 4-nodes. + // 4-nodes will be represented as: B + // R R + // 3 -node will be represented as: B or B + // R B B R + // + // For a detailed description of the algorithm, take a look at "Algorithms" by Robert Sedgewick. + // + + internal delegate bool TreeWalkPredicate(SortedSet.Node node); + + internal enum TreeRotation + { + LeftRotation = 1, + RightRotation = 2, + RightLeftRotation = 3, + LeftRightRotation = 4, + } + + [SuppressMessage("Microsoft.Naming", "CA1710:IdentifiersShouldHaveCorrectSuffix", Justification = "by design name choice")] + [DebuggerTypeProxy(nameof(SortedSet))]/*这句改了*/ + [DebuggerDisplay("Count = {Count}")] +#if !FEATURE_NETCORE + [Serializable] + public class SortedSet : ISet, ICollection, ICollection, ISerializable, IDeserializationCallback//, IReadOnlyCollection + { +#else + public class SortedSet : ISet, ICollection, ICollection, IReadOnlyCollection { +#endif //!FEATURE_NETCORE + #region local variables/constants + Node root; + IComparer comparer; + int count; + int version; + private Object _syncRoot; + + private const String ComparerName = "Comparer"; + private const String CountName = "Count"; + private const String ItemsName = "Items"; + private const String VersionName = "Version"; + //needed for enumerator + private const String TreeName = "Tree"; + private const String NodeValueName = "Item"; + private const String EnumStartName = "EnumStarted"; + private const String ReverseName = "Reverse"; + private const String EnumVersionName = "EnumVersion"; + +#if !FEATURE_NETCORE + //needed for TreeSubset + private const String minName = "Min"; + private const String maxName = "Max"; + private const String lBoundActiveName = "lBoundActive"; + private const String uBoundActiveName = "uBoundActive"; + + private SerializationInfo siInfo; //A temporary variable which we need during deserialization. +#endif + internal const int StackAllocThreshold = 100; + + #endregion + + #region Constructors + public SortedSet() + { + this.comparer = Comparer.Default; + + } + + public SortedSet(IComparer comparer) + { + if (comparer == null) + { + this.comparer = Comparer.Default; + } + else + { + this.comparer = comparer; + } + } + + + public SortedSet(IEnumerable collection) : this(collection, Comparer.Default) { } + + public SortedSet(IEnumerable collection, IComparer comparer) + : this(comparer) + { + + if (collection == null) + { + throw new ArgumentNullException("collection"); + } + + // these are explicit type checks in the mould of HashSet. It would have worked better + // with something like an ISorted (we could make this work for SortedList.Keys etc) + SortedSet baseSortedSet = collection as SortedSet; + SortedSet baseTreeSubSet = collection as TreeSubSet; + if (baseSortedSet != null && baseTreeSubSet == null && AreComparersEqual(this, baseSortedSet)) + { + //breadth first traversal to recreate nodes + if (baseSortedSet.Count == 0) + { + count = 0; + version = 0; + root = null; + return; + } + + + //pre order way to replicate nodes + Stack theirStack = new Stack.Node>(2 * log2(baseSortedSet.Count) + 2); + Stack myStack = new Stack.Node>(2 * log2(baseSortedSet.Count) + 2); + Node theirCurrent = baseSortedSet.root; + Node myCurrent = (theirCurrent != null ? new SortedSet.Node(theirCurrent.Item, theirCurrent.IsRed) : null); + root = myCurrent; + while (theirCurrent != null) + { + theirStack.Push(theirCurrent); + myStack.Push(myCurrent); + myCurrent.Left = (theirCurrent.Left != null ? new SortedSet.Node(theirCurrent.Left.Item, theirCurrent.Left.IsRed) : null); + theirCurrent = theirCurrent.Left; + myCurrent = myCurrent.Left; + } + while (theirStack.Count != 0) + { + theirCurrent = theirStack.Pop(); + myCurrent = myStack.Pop(); + Node theirRight = theirCurrent.Right; + Node myRight = null; + if (theirRight != null) + { + myRight = new SortedSet.Node(theirRight.Item, theirRight.IsRed); + } + myCurrent.Right = myRight; + + while (theirRight != null) + { + theirStack.Push(theirRight); + myStack.Push(myRight); + myRight.Left = (theirRight.Left != null ? new SortedSet.Node(theirRight.Left.Item, theirRight.Left.IsRed) : null); + theirRight = theirRight.Left; + myRight = myRight.Left; + } + } + count = baseSortedSet.count; + version = 0; + } + else + { //As it stands, you're doing an NlogN sort of the collection + + List els = new List(collection); + els.Sort(this.comparer); + for (int i = 1; i < els.Count; i++) + { + if (comparer.Compare(els[i], els[i - 1]) == 0) + { + els.RemoveAt(i); + i--; + } + } + root = ConstructRootFromSortedArray(els.ToArray(), 0, els.Count - 1, null); + count = els.Count; + version = 0; + } + } + + +#if !FEATURE_NETCORE + protected SortedSet(SerializationInfo info, StreamingContext context) + { + siInfo = info; + } +#endif + #endregion + + #region Bulk Operation Helpers + private void AddAllElements(IEnumerable collection) + { + + foreach (T item in collection) + { + if (!this.Contains(item)) + Add(item); + } + } + + private void RemoveAllElements(IEnumerable collection) + { + T min = this.Min; + T max = this.Max; + foreach (T item in collection) + { + if (!(comparer.Compare(item, min) < 0 || comparer.Compare(item, max) > 0) && this.Contains(item)) + this.Remove(item); + } + } + + private bool ContainsAllElements(IEnumerable collection) + { + foreach (T item in collection) + { + if (!this.Contains(item)) + { + return false; + } + } + return true; + } + + // + // Do a in order walk on tree and calls the delegate for each node. + // If the action delegate returns false, stop the walk. + // + // Return true if the entire tree has been walked. + // Otherwise returns false. + // + internal bool InOrderTreeWalk(TreeWalkPredicate action) + { + return InOrderTreeWalk(action, false); + } + + // Allows for the change in traversal direction. Reverse visits nodes in descending order + internal virtual bool InOrderTreeWalk(TreeWalkPredicate action, bool reverse) + { + if (root == null) + { + return true; + } + + // The maximum height of a red-black tree is 2*lg(n+1). + // See page 264 of "Introduction to algorithms" by Thomas H. Cormen + // note: this should be logbase2, but since the stack grows itself, we + // don't want the extra cost + Stack stack = new Stack(2 * (int)(SortedSet.log2(Count + 1))); + Node current = root; + while (current != null) + { + stack.Push(current); + current = (reverse ? current.Right : current.Left); + } + while (stack.Count != 0) + { + current = stack.Pop(); + if (!action(current)) + { + return false; + } + + Node node = (reverse ? current.Left : current.Right); + while (node != null) + { + stack.Push(node); + node = (reverse ? node.Right : node.Left); + } + } + return true; + } + + // + // Do a left to right breadth first walk on tree and + // calls the delegate for each node. + // If the action delegate returns false, stop the walk. + // + // Return true if the entire tree has been walked. + // Otherwise returns false. + // + internal virtual bool BreadthFirstTreeWalk(TreeWalkPredicate action) + { + if (root == null) + { + return true; + } + + List processQueue = new List(); + processQueue.Add(root); + Node current; + + while (processQueue.Count != 0) + { + current = processQueue[0]; + processQueue.RemoveAt(0); + if (!action(current)) + { + return false; + } + if (current.Left != null) + { + processQueue.Add(current.Left); + } + if (current.Right != null) + { + processQueue.Add(current.Right); + } + } + return true; + } + #endregion + + #region Properties + public int Count + { + get + { + VersionCheck(); + return count; + } + } + + public IComparer Comparer + { + get + { + return comparer; + } + } + + bool ICollection.IsReadOnly + { + get + { + return false; + } + } + + bool ICollection.IsSynchronized + { + get + { + return false; + } + } + + object ICollection.SyncRoot + { + get + { + if (_syncRoot == null) + { + System.Threading.Interlocked.CompareExchange(ref _syncRoot, new Object(), null); + } + return _syncRoot; + } + } + #endregion + + #region Subclass helpers + + //virtual function for subclass that needs to update count + internal virtual void VersionCheck() { } + + + //virtual function for subclass that needs to do range checks + internal virtual bool IsWithinRange(T item) + { + return true; + + } + #endregion + + #region ICollection Members + /// + /// Add the value ITEM to the tree, returns true if added, false if duplicate + /// + /// item to be added + public bool Add(T item) + { + return AddIfNotPresent(item); + } + + void ICollection.Add(T item) + { + AddIfNotPresent(item); + } + + + /// + /// Adds ITEM to the tree if not already present. Returns TRUE if value was successfully added + /// or FALSE if it is a duplicate + /// + internal virtual bool AddIfNotPresent(T item) + { + if (root == null) + { // empty tree + root = new Node(item, false); + count = 1; + version++; + return true; + } + + // + // Search for a node at bottom to insert the new node. + // If we can guanratee the node we found is not a 4-node, it would be easy to do insertion. + // We split 4-nodes along the search path. + // + Node current = root; + Node parent = null; + Node grandParent = null; + Node greatGrandParent = null; + + //even if we don't actually add to the set, we may be altering its structure (by doing rotations + //and such). so update version to disable any enumerators/subsets working on it + version++; + + + int order = 0; + while (current != null) + { + order = comparer.Compare(item, current.Item); + if (order == 0) + { + // We could have changed root node to red during the search process. + // We need to set it to black before we return. + root.IsRed = false; + return false; + } + + // split a 4-node into two 2-nodes + if (Is4Node(current)) + { + Split4Node(current); + // We could have introduced two consecutive red nodes after split. Fix that by rotation. + if (IsRed(parent)) + { + InsertionBalance(current, ref parent, grandParent, greatGrandParent); + } + } + greatGrandParent = grandParent; + grandParent = parent; + parent = current; + current = (order < 0) ? current.Left : current.Right; + } + + Debug.Assert(parent != null, "Parent node cannot be null here!"); + // ready to insert the new node + Node node = new Node(item); + if (order > 0) + { + parent.Right = node; + } + else + { + parent.Left = node; + } + + // the new node will be red, so we will need to adjust the colors if parent node is also red + if (parent.IsRed) + { + InsertionBalance(node, ref parent, grandParent, greatGrandParent); + } + + // Root node is always black + root.IsRed = false; + ++count; + return true; + } + + /// + /// Remove the T ITEM from this SortedSet. Returns true if successfully removed. + /// + /// + /// + public bool Remove(T item) + { + return this.DoRemove(item); // hack so it can be made non-virtual + } + + internal virtual bool DoRemove(T item) + { + + if (root == null) + { + return false; + } + + + // Search for a node and then find its succesor. + // Then copy the item from the succesor to the matching node and delete the successor. + // If a node doesn't have a successor, we can replace it with its left child (if not empty.) + // or delete the matching node. + // + // In top-down implementation, it is important to make sure the node to be deleted is not a 2-node. + // Following code will make sure the node on the path is not a 2 Node. + + //even if we don't actually remove from the set, we may be altering its structure (by doing rotations + //and such). so update version to disable any enumerators/subsets working on it + version++; + + Node current = root; + Node parent = null; + Node grandParent = null; + Node match = null; + Node parentOfMatch = null; + bool foundMatch = false; + while (current != null) + { + if (Is2Node(current)) + { // fix up 2-Node + if (parent == null) + { // current is root. Mark it as red + current.IsRed = true; + } + else + { + Node sibling = GetSibling(current, parent); + if (sibling.IsRed) + { + // If parent is a 3-node, flip the orientation of the red link. + // We can acheive this by a single rotation + // This case is converted to one of other cased below. + Debug.Assert(!parent.IsRed, "parent must be a black node!"); + if (parent.Right == sibling) + { + RotateLeft(parent); + } + else + { + RotateRight(parent); + } + + parent.IsRed = true; + sibling.IsRed = false; // parent's color + // sibling becomes child of grandParent or root after rotation. Update link from grandParent or root + ReplaceChildOfNodeOrRoot(grandParent, parent, sibling); + // sibling will become grandParent of current node + grandParent = sibling; + if (parent == match) + { + parentOfMatch = sibling; + } + + // update sibling, this is necessary for following processing + sibling = (parent.Left == current) ? parent.Right : parent.Left; + } + Debug.Assert(sibling != null || sibling.IsRed == false, "sibling must not be null and it must be black!"); + + if (Is2Node(sibling)) + { + Merge2Nodes(parent, current, sibling); + } + else + { + // current is a 2-node and sibling is either a 3-node or a 4-node. + // We can change the color of current to red by some rotation. + TreeRotation rotation = RotationNeeded(parent, current, sibling); + Node newGrandParent = null; + switch (rotation) + { + case TreeRotation.RightRotation: + Debug.Assert(parent.Left == sibling, "sibling must be left child of parent!"); + Debug.Assert(sibling.Left.IsRed, "Left child of sibling must be red!"); + sibling.Left.IsRed = false; + newGrandParent = RotateRight(parent); + break; + case TreeRotation.LeftRotation: + Debug.Assert(parent.Right == sibling, "sibling must be left child of parent!"); + Debug.Assert(sibling.Right.IsRed, "Right child of sibling must be red!"); + sibling.Right.IsRed = false; + newGrandParent = RotateLeft(parent); + break; + + case TreeRotation.RightLeftRotation: + Debug.Assert(parent.Right == sibling, "sibling must be left child of parent!"); + Debug.Assert(sibling.Left.IsRed, "Left child of sibling must be red!"); + newGrandParent = RotateRightLeft(parent); + break; + + case TreeRotation.LeftRightRotation: + Debug.Assert(parent.Left == sibling, "sibling must be left child of parent!"); + Debug.Assert(sibling.Right.IsRed, "Right child of sibling must be red!"); + newGrandParent = RotateLeftRight(parent); + break; + } + + newGrandParent.IsRed = parent.IsRed; + parent.IsRed = false; + current.IsRed = true; + ReplaceChildOfNodeOrRoot(grandParent, parent, newGrandParent); + if (parent == match) + { + parentOfMatch = newGrandParent; + } + grandParent = newGrandParent; + } + } + } + + // we don't need to compare any more once we found the match + int order = foundMatch ? -1 : comparer.Compare(item, current.Item); + if (order == 0) + { + // save the matching node + foundMatch = true; + match = current; + parentOfMatch = parent; + } + + grandParent = parent; + parent = current; + + if (order < 0) + { + current = current.Left; + } + else + { + current = current.Right; // continue the search in right sub tree after we find a match + } + } + + // move successor to the matching node position and replace links + if (match != null) + { + ReplaceNode(match, parentOfMatch, parent, grandParent); + --count; + } + + if (root != null) + { + root.IsRed = false; + } + return foundMatch; + } + + public virtual void Clear() + { + root = null; + count = 0; + ++version; + } + + + public virtual bool Contains(T item) + { + + return FindNode(item) != null; + } + + + + + public void CopyTo(T[] array) { CopyTo(array, 0, Count); } + + public void CopyTo(T[] array, int index) { CopyTo(array, index, Count); } + + public void CopyTo(T[] array, int index, int count) + { + if (array == null) + { + ThrowHelper.ThrowArgumentNullException(ExceptionArgument.array); + } + + if (index < 0) + { + ThrowHelper.ThrowArgumentOutOfRangeException(ExceptionArgument.index); + } + + if (count < 0) + { + throw new ArgumentOutOfRangeException("count", SR.GetString("SR.ArgumentOutOfRange_NeedNonNegNum")); + } + + // will array, starting at arrayIndex, be able to hold elements? Note: not + // checking arrayIndex >= array.Length (consistency with list of allowing + // count of 0; subsequent check takes care of the rest) + if (index > array.Length || count > array.Length - index) + { + throw new ArgumentException(SR.GetString("SR.Arg_ArrayPlusOffTooSmall")); + } + //upper bound + count += index; + + InOrderTreeWalk(delegate (Node node) + { + if (index >= count) + { + return false; + } + else + { + array[index++] = node.Item; + return true; + } + }); + } + + void ICollection.CopyTo(Array array, int index) + { + if (array == null) + { + ThrowHelper.ThrowArgumentNullException(ExceptionArgument.array); + } + + if (array.Rank != 1) + { + ThrowHelper.ThrowArgumentException(ExceptionResource.Arg_RankMultiDimNotSupported); + } + + if (array.GetLowerBound(0) != 0) + { + ThrowHelper.ThrowArgumentException(ExceptionResource.Arg_NonZeroLowerBound); + } + + if (index < 0) + { + ThrowHelper.ThrowArgumentOutOfRangeException(ExceptionArgument.arrayIndex, ExceptionResource.ArgumentOutOfRange_NeedNonNegNum); + } + + if (array.Length - index < Count) + { + ThrowHelper.ThrowArgumentException(ExceptionResource.Arg_ArrayPlusOffTooSmall); + } + + T[] tarray = array as T[]; + if (tarray != null) + { + CopyTo(tarray, index); + } + else + { + object[] objects = array as object[]; + if (objects == null) + { + ThrowHelper.ThrowArgumentException(ExceptionResource.Argument_InvalidArrayType); + } + + try + { + InOrderTreeWalk(delegate (Node node) { objects[index++] = node.Item; return true; }); + } + catch (ArrayTypeMismatchException) + { + ThrowHelper.ThrowArgumentException(ExceptionResource.Argument_InvalidArrayType); + } + } + } + + #endregion + + #region IEnumerable members + public Enumerator GetEnumerator() + { + return new Enumerator(this); + } + + + + + IEnumerator IEnumerable.GetEnumerator() + { + return new Enumerator(this); + } + + IEnumerator IEnumerable.GetEnumerator() + { + return new Enumerator(this); + } + #endregion + + #region Tree Specific Operations + + private static Node GetSibling(Node node, Node parent) + { + if (parent.Left == node) + { + return parent.Right; + } + return parent.Left; + } + + // After calling InsertionBalance, we need to make sure current and parent up-to-date. + // It doesn't matter if we keep grandParent and greatGrantParent up-to-date + // because we won't need to split again in the next node. + // By the time we need to split again, everything will be correctly set. + // + private void InsertionBalance(Node current, ref Node parent, Node grandParent, Node greatGrandParent) + { + Debug.Assert(grandParent != null, "Grand parent cannot be null here!"); + bool parentIsOnRight = (grandParent.Right == parent); + bool currentIsOnRight = (parent.Right == current); + + Node newChildOfGreatGrandParent; + if (parentIsOnRight == currentIsOnRight) + { // same orientation, single rotation + newChildOfGreatGrandParent = currentIsOnRight ? RotateLeft(grandParent) : RotateRight(grandParent); + } + else + { // different orientaton, double rotation + newChildOfGreatGrandParent = currentIsOnRight ? RotateLeftRight(grandParent) : RotateRightLeft(grandParent); + // current node now becomes the child of greatgrandparent + parent = greatGrandParent; + } + // grand parent will become a child of either parent of current. + grandParent.IsRed = true; + newChildOfGreatGrandParent.IsRed = false; + + ReplaceChildOfNodeOrRoot(greatGrandParent, grandParent, newChildOfGreatGrandParent); + } + + private static bool Is2Node(Node node) + { + Debug.Assert(node != null, "node cannot be null!"); + return IsBlack(node) && IsNullOrBlack(node.Left) && IsNullOrBlack(node.Right); + } + + private static bool Is4Node(Node node) + { + return IsRed(node.Left) && IsRed(node.Right); + } + + private static bool IsBlack(Node node) + { + return (node != null && !node.IsRed); + } + + private static bool IsNullOrBlack(Node node) + { + return (node == null || !node.IsRed); + } + + private static bool IsRed(Node node) + { + return (node != null && node.IsRed); + } + + private static void Merge2Nodes(Node parent, Node child1, Node child2) + { + Debug.Assert(IsRed(parent), "parent must be be red"); + // combing two 2-nodes into a 4-node + parent.IsRed = false; + child1.IsRed = true; + child2.IsRed = true; + } + + // Replace the child of a parent node. + // If the parent node is null, replace the root. + private void ReplaceChildOfNodeOrRoot(Node parent, Node child, Node newChild) + { + if (parent != null) + { + if (parent.Left == child) + { + parent.Left = newChild; + } + else + { + parent.Right = newChild; + } + } + else + { + root = newChild; + } + } + + // Replace the matching node with its succesor. + private void ReplaceNode(Node match, Node parentOfMatch, Node succesor, Node parentOfSuccesor) + { + if (succesor == match) + { // this node has no successor, should only happen if right child of matching node is null. + Debug.Assert(match.Right == null, "Right child must be null!"); + succesor = match.Left; + } + else + { + Debug.Assert(parentOfSuccesor != null, "parent of successor cannot be null!"); + Debug.Assert(succesor.Left == null, "Left child of succesor must be null!"); + Debug.Assert((succesor.Right == null && succesor.IsRed) || (succesor.Right.IsRed && !succesor.IsRed), "Succesor must be in valid state"); + if (succesor.Right != null) + { + succesor.Right.IsRed = false; + } + + if (parentOfSuccesor != match) + { // detach succesor from its parent and set its right child + parentOfSuccesor.Left = succesor.Right; + succesor.Right = match.Right; + } + + succesor.Left = match.Left; + } + + if (succesor != null) + { + succesor.IsRed = match.IsRed; + } + + ReplaceChildOfNodeOrRoot(parentOfMatch, match, succesor); + + } + + internal virtual Node FindNode(T item) + { + Node current = root; + while (current != null) + { + int order = comparer.Compare(item, current.Item); + if (order == 0) + { + return current; + } + else + { + current = (order < 0) ? current.Left : current.Right; + } + } + + return null; + } + + //used for bithelpers. Note that this implementation is completely different + //from the Subset's. The two should not be mixed. This indexes as if the tree were an array. + //http://en.wikipedia.org/wiki/Binary_Tree#Methods_for_storing_binary_trees + internal virtual int InternalIndexOf(T item) + { + Node current = root; + int count = 0; + while (current != null) + { + int order = comparer.Compare(item, current.Item); + if (order == 0) + { + return count; + } + else + { + current = (order < 0) ? current.Left : current.Right; + count = (order < 0) ? (2 * count + 1) : (2 * count + 2); + } + } + return -1; + } + + + + internal Node FindRange(T from, T to) + { + return FindRange(from, to, true, true); + } + internal Node FindRange(T from, T to, bool lowerBoundActive, bool upperBoundActive) + { + Node current = root; + while (current != null) + { + if (lowerBoundActive && comparer.Compare(from, current.Item) > 0) + { + current = current.Right; + } + else + { + if (upperBoundActive && comparer.Compare(to, current.Item) < 0) + { + current = current.Left; + } + else + { + return current; + } + } + } + + return null; + } + + internal void UpdateVersion() + { + ++version; + } + + + private static Node RotateLeft(Node node) + { + Node x = node.Right; + node.Right = x.Left; + x.Left = node; + return x; + } + + private static Node RotateLeftRight(Node node) + { + Node child = node.Left; + Node grandChild = child.Right; + + node.Left = grandChild.Right; + grandChild.Right = node; + child.Right = grandChild.Left; + grandChild.Left = child; + return grandChild; + } + + private static Node RotateRight(Node node) + { + Node x = node.Left; + node.Left = x.Right; + x.Right = node; + return x; + } + + private static Node RotateRightLeft(Node node) + { + Node child = node.Right; + Node grandChild = child.Left; + + node.Right = grandChild.Left; + grandChild.Left = node; + child.Left = grandChild.Right; + grandChild.Right = child; + return grandChild; + } + /// + /// Testing counter that can track rotations + /// + + + private static TreeRotation RotationNeeded(Node parent, Node current, Node sibling) + { + Debug.Assert(IsRed(sibling.Left) || IsRed(sibling.Right), "sibling must have at least one red child"); + if (IsRed(sibling.Left)) + { + if (parent.Left == current) + { + return TreeRotation.RightLeftRotation; + } + return TreeRotation.RightRotation; + } + else + { + if (parent.Left == current) + { + return TreeRotation.LeftRotation; + } + return TreeRotation.LeftRightRotation; + } + } + + /// + /// Used for deep equality of SortedSet testing + /// + /// + public static IEqualityComparer> CreateSetComparer() + { + return new SortedSetEqualityComparer(); + } + + /// + /// Create a new set comparer for this set, where this set's members' equality is defined by the + /// memberEqualityComparer. Note that this equality comparer's definition of equality must be the + /// same as this set's Comparer's definition of equality + /// + public static IEqualityComparer> CreateSetComparer(IEqualityComparer memberEqualityComparer) + { + return new SortedSetEqualityComparer(memberEqualityComparer); + } + + + /// + /// Decides whether these sets are the same, given the comparer. If the EC's are the same, we can + /// just use SetEquals, but if they aren't then we have to manually check with the given comparer + /// + internal static bool SortedSetEquals(SortedSet set1, SortedSet set2, IComparer comparer) + { + // handle null cases first + if (set1 == null) + { + return (set2 == null); + } + else if (set2 == null) + { + // set1 != null + return false; + } + + if (AreComparersEqual(set1, set2)) + { + if (set1.Count != set2.Count) + return false; + + return set1.SetEquals(set2); + } + else + { + bool found = false; + foreach (T item1 in set1) + { + found = false; + foreach (T item2 in set2) + { + if (comparer.Compare(item1, item2) == 0) + { + found = true; + break; + } + } + if (!found) + return false; + } + return true; + } + + } + + + //This is a little frustrating because we can't support more sorted structures + private static bool AreComparersEqual(SortedSet set1, SortedSet set2) + { + return set1.Comparer.Equals(set2.Comparer); + } + + + private static void Split4Node(Node node) + { + node.IsRed = true; + node.Left.IsRed = false; + node.Right.IsRed = false; + } + + /// + /// Copies this to an array. Used for DebugView + /// + /// + internal T[] ToArray() + { + T[] newArray = new T[Count]; + CopyTo(newArray); + return newArray; + } + + + #endregion + + #region ISet Members + + /// + /// Transform this set into its union with the IEnumerable OTHER + ///Attempts to insert each element and rejects it if it exists. + /// NOTE: The caller object is important as UnionWith uses the Comparator + ///associated with THIS to check equality + /// Throws ArgumentNullException if OTHER is null + /// + /// + public void UnionWith(IEnumerable other) + { + if (other == null) + { + throw new ArgumentNullException("other"); + } + + SortedSet s = other as SortedSet; + TreeSubSet t = this as TreeSubSet; + + if (t != null) + VersionCheck(); + + if (s != null && t == null && this.count == 0) + { + SortedSet dummy = new SortedSet(s, this.comparer); + this.root = dummy.root; + this.count = dummy.count; + this.version++; + return; + } + + + if (s != null && t == null && AreComparersEqual(this, s) && (s.Count > this.Count / 2)) + { //this actually hurts if N is much greater than M the /2 is arbitrary + //first do a merge sort to an array. + T[] merged = new T[s.Count + this.Count]; + int c = 0; + Enumerator mine = this.GetEnumerator(); + Enumerator theirs = s.GetEnumerator(); + bool mineEnded = !mine.MoveNext(), theirsEnded = !theirs.MoveNext(); + while (!mineEnded && !theirsEnded) + { + int comp = Comparer.Compare(mine.Current, theirs.Current); + if (comp < 0) + { + merged[c++] = mine.Current; + mineEnded = !mine.MoveNext(); + } + else if (comp == 0) + { + merged[c++] = theirs.Current; + mineEnded = !mine.MoveNext(); + theirsEnded = !theirs.MoveNext(); + } + else + { + merged[c++] = theirs.Current; + theirsEnded = !theirs.MoveNext(); + } + } + + if (!mineEnded || !theirsEnded) + { + Enumerator remaining = (mineEnded ? theirs : mine); + do + { + merged[c++] = remaining.Current; + } while (remaining.MoveNext()); + } + + //now merged has all c elements + + //safe to gc the root, we have all the elements + root = null; + + + root = SortedSet.ConstructRootFromSortedArray(merged, 0, c - 1, null); + count = c; + version++; + } + else + { + AddAllElements(other); + } + } + + + private static Node ConstructRootFromSortedArray(T[] arr, int startIndex, int endIndex, Node redNode) + { + + + + //what does this do? + //you're given a sorted array... say 1 2 3 4 5 6 + //2 cases: + // If there are odd # of elements, pick the middle element (in this case 4), and compute + // its left and right branches + // If there are even # of elements, pick the left middle element, save the right middle element + // and call the function on the rest + // 1 2 3 4 5 6 -> pick 3, save 4 and call the fn on 1,2 and 5,6 + // now add 4 as a red node to the lowest element on the right branch + // 3 3 + // 1 5 -> 1 5 + // 2 6 2 4 6 + // As we're adding to the leftmost of the right branch, nesting will not hurt the red-black properties + // Leaf nodes are red if they have no sibling (if there are 2 nodes or if a node trickles + // down to the bottom + + + //the iterative way to do this ends up wasting more space than it saves in stack frames (at + //least in what i tried) + //so we're doing this recursively + //base cases are described below + int size = endIndex - startIndex + 1; + if (size == 0) + { + return null; + } + Node root = null; + if (size == 1) + { + root = new Node(arr[startIndex], false); + if (redNode != null) + { + root.Left = redNode; + } + } + else if (size == 2) + { + root = new Node(arr[startIndex], false); + root.Right = new Node(arr[endIndex], false); + root.Right.IsRed = true; + if (redNode != null) + { + root.Left = redNode; + } + } + else if (size == 3) + { + root = new Node(arr[startIndex + 1], false); + root.Left = new Node(arr[startIndex], false); + root.Right = new Node(arr[endIndex], false); + if (redNode != null) + { + root.Left.Left = redNode; + + } + } + else + { + int midpt = ((startIndex + endIndex) / 2); + root = new Node(arr[midpt], false); + root.Left = ConstructRootFromSortedArray(arr, startIndex, midpt - 1, redNode); + if (size % 2 == 0) + { + root.Right = ConstructRootFromSortedArray(arr, midpt + 2, endIndex, new Node(arr[midpt + 1], true)); + } + else + { + root.Right = ConstructRootFromSortedArray(arr, midpt + 1, endIndex, null); + } + } + return root; + + } + + + /// + /// Transform this set into its intersection with the IEnumerable OTHER + /// NOTE: The caller object is important as IntersectionWith uses the + /// comparator associated with THIS to check equality + /// Throws ArgumentNullException if OTHER is null + /// + /// + public virtual void IntersectWith(IEnumerable other) + { + if (other == null) + { + throw new ArgumentNullException("other"); + } + + if (Count == 0) + return; + + //HashSet optimizations can't be done until equality comparers and comparers are related + + //Technically, this would work as well with an ISorted + SortedSet s = other as SortedSet; + TreeSubSet t = this as TreeSubSet; + if (t != null) + VersionCheck(); + //only let this happen if i am also a SortedSet, not a SubSet + if (s != null && t == null && AreComparersEqual(this, s)) + { + + + //first do a merge sort to an array. + T[] merged = new T[this.Count]; + int c = 0; + Enumerator mine = this.GetEnumerator(); + Enumerator theirs = s.GetEnumerator(); + bool mineEnded = !mine.MoveNext(), theirsEnded = !theirs.MoveNext(); + T max = Max; + T min = Min; + + while (!mineEnded && !theirsEnded && Comparer.Compare(theirs.Current, max) <= 0) + { + int comp = Comparer.Compare(mine.Current, theirs.Current); + if (comp < 0) + { + mineEnded = !mine.MoveNext(); + } + else if (comp == 0) + { + merged[c++] = theirs.Current; + mineEnded = !mine.MoveNext(); + theirsEnded = !theirs.MoveNext(); + } + else + { + theirsEnded = !theirs.MoveNext(); + } + } + + //now merged has all c elements + + //safe to gc the root, we have all the elements + root = null; + + root = SortedSet.ConstructRootFromSortedArray(merged, 0, c - 1, null); + count = c; + version++; + } + else + { + IntersectWithEnumerable(other); + } + } + + internal virtual void IntersectWithEnumerable(IEnumerable other) + { + // + List toSave = new List(this.Count); + foreach (T item in other) + { + if (this.Contains(item)) + { + toSave.Add(item); + this.Remove(item); + } + } + this.Clear(); + AddAllElements(toSave); + + } + + + + /// + /// Transform this set into its complement with the IEnumerable OTHER + /// NOTE: The caller object is important as ExceptWith uses the + /// comparator associated with THIS to check equality + /// Throws ArgumentNullException if OTHER is null + /// + /// + public void ExceptWith(IEnumerable other) + { + if (other == null) + { + throw new ArgumentNullException("other"); + } + + if (count == 0) + return; + + if (other == this) + { + this.Clear(); + return; + } + + SortedSet asSorted = other as SortedSet; + + if (asSorted != null && AreComparersEqual(this, asSorted)) + { + //outside range, no point doing anything + if (!(comparer.Compare(asSorted.Max, this.Min) < 0 || comparer.Compare(asSorted.Min, this.Max) > 0)) + { + T min = this.Min; + T max = this.Max; + foreach (T item in other) + { + if (comparer.Compare(item, min) < 0) + continue; + if (comparer.Compare(item, max) > 0) + break; + Remove(item); + } + } + + } + else + { + RemoveAllElements(other); + } + } + + /// + /// Transform this set so it contains elements in THIS or OTHER but not both + /// NOTE: The caller object is important as SymmetricExceptWith uses the + /// comparator associated with THIS to check equality + /// Throws ArgumentNullException if OTHER is null + /// + /// + public void SymmetricExceptWith(IEnumerable other) + { + if (other == null) + { + throw new ArgumentNullException("other"); + } + + if (this.Count == 0) + { + this.UnionWith(other); + return; + } + + if (other == this) + { + this.Clear(); + return; + } + + + SortedSet asSorted = other as SortedSet; + +#if USING_HASH_SET + HashSet asHash = other as HashSet; +#endif + if (asSorted != null && AreComparersEqual(this, asSorted)) + { + SymmetricExceptWithSameEC(asSorted); + } +#if USING_HASH_SET + else if (asHash != null && this.comparer.Equals(Comparer.Default) && asHash.Comparer.Equals(EqualityComparer.Default)) { + SymmetricExceptWithSameEC(asHash); + } +#endif + else + { + //need perf improvement on this + T[] elements = (new List(other)).ToArray(); + Array.Sort(elements, this.Comparer); + SymmetricExceptWithSameEC(elements); + } + } + + //OTHER must be a set + internal void SymmetricExceptWithSameEC(ISet other) + { + foreach (T item in other) + { + //yes, it is classier to say + //if (!this.Remove(item))this.Add(item); + //but this ends up saving on rotations + if (this.Contains(item)) + { + this.Remove(item); + } + else + { + this.Add(item); + } + } + } + + //OTHER must be a sorted array + internal void SymmetricExceptWithSameEC(T[] other) + { + if (other.Length == 0) + { + return; + } + T last = other[0]; + for (int i = 0; i < other.Length; i++) + { + while (i < other.Length && i != 0 && comparer.Compare(other[i], last) == 0) + i++; + if (i >= other.Length) + break; + if (this.Contains(other[i])) + { + this.Remove(other[i]); + } + else + { + this.Add(other[i]); + } + last = other[i]; + } + } + + + /// + /// Checks whether this Tree is a subset of the IEnumerable other + /// + /// + /// + [System.Security.SecuritySafeCritical] + public bool IsSubsetOf(IEnumerable other) + { + if (other == null) + { + throw new ArgumentNullException("other"); + } + + if (Count == 0) + return true; + + + SortedSet asSorted = other as SortedSet; + if (asSorted != null && AreComparersEqual(this, asSorted)) + { + if (this.Count > asSorted.Count) + return false; + return IsSubsetOfSortedSetWithSameEC(asSorted); + } + else + { + //worst case: mark every element in my set and see if i've counted all + //O(MlogN) + + ElementCount result = CheckUniqueAndUnfoundElements(other, false); + return (result.uniqueCount == Count && result.unfoundCount >= 0); + } + } + + private bool IsSubsetOfSortedSetWithSameEC(SortedSet asSorted) + { + SortedSet prunedOther = asSorted.GetViewBetween(this.Min, this.Max); + foreach (T item in this) + { + if (!prunedOther.Contains(item)) + return false; + } + return true; + + } + + + /// + /// Checks whether this Tree is a proper subset of the IEnumerable other + /// + /// + /// + [System.Security.SecuritySafeCritical] + public bool IsProperSubsetOf(IEnumerable other) + { + if (other == null) + { + throw new ArgumentNullException("other"); + } + + if ((other as ICollection) != null) + { + if (Count == 0) + return (other as ICollection).Count > 0; + } + + +#if USING_HASH_SET + //do it one way for HashSets + HashSet asHash = other as HashSet; + if (asHash != null && comparer.Equals(Comparer.Default) && asHash.Comparer.Equals(EqualityComparer.Default)) { + return asHash.IsProperSupersetOf(this); + } +#endif + //another for sorted sets with the same comparer + SortedSet asSorted = other as SortedSet; + if (asSorted != null && AreComparersEqual(this, asSorted)) + { + if (this.Count >= asSorted.Count) + return false; + return IsSubsetOfSortedSetWithSameEC(asSorted); + } + + + //worst case: mark every element in my set and see if i've counted all + //O(MlogN). + ElementCount result = CheckUniqueAndUnfoundElements(other, false); + return (result.uniqueCount == Count && result.unfoundCount > 0); + } + + + /// + /// Checks whether this Tree is a super set of the IEnumerable other + /// + /// + /// + public bool IsSupersetOf(IEnumerable other) + { + if (other == null) + { + throw new ArgumentNullException("other"); + } + + if ((other as ICollection) != null && (other as ICollection).Count == 0) + return true; + + //do it one way for HashSets +#if USING_HASH_SET + HashSet asHash = other as HashSet; + if (asHash != null && comparer.Equals(Comparer.Default) && asHash.Comparer.Equals(EqualityComparer.Default)) { + return asHash.IsSubsetOf(this); + } +#endif + //another for sorted sets with the same comparer + SortedSet asSorted = other as SortedSet; + if (asSorted != null && AreComparersEqual(this, asSorted)) + { + if (this.Count < asSorted.Count) + return false; + SortedSet pruned = GetViewBetween(asSorted.Min, asSorted.Max); + foreach (T item in asSorted) + { + if (!pruned.Contains(item)) + return false; + } + return true; + } + //and a third for everything else + return ContainsAllElements(other); + } + + /// + /// Checks whether this Tree is a proper super set of the IEnumerable other + /// + /// + /// + [System.Security.SecuritySafeCritical] + public bool IsProperSupersetOf(IEnumerable other) + { + if (other == null) + { + throw new ArgumentNullException("other"); + } + + if (Count == 0) + return false; + + if ((other as ICollection) != null && (other as ICollection).Count == 0) + return true; + +#if USING_HASH_SET + //do it one way for HashSets + + HashSet asHash = other as HashSet; + if (asHash != null && comparer.Equals(Comparer.Default) && asHash.Comparer.Equals(EqualityComparer.Default)) { + return asHash.IsProperSubsetOf(this); + } +#endif + //another way for sorted sets + SortedSet asSorted = other as SortedSet; + if (asSorted != null && AreComparersEqual(asSorted, this)) + { + if (asSorted.Count >= this.Count) + return false; + SortedSet pruned = GetViewBetween(asSorted.Min, asSorted.Max); + foreach (T item in asSorted) + { + if (!pruned.Contains(item)) + return false; + } + return true; + } + + + //worst case: mark every element in my set and see if i've counted all + //O(MlogN) + //slight optimization, put it into a HashSet and then check can do it in O(N+M) + //but slower in better cases + wastes space + ElementCount result = CheckUniqueAndUnfoundElements(other, true); + return (result.uniqueCount < Count && result.unfoundCount == 0); + } + + + + /// + /// Checks whether this Tree has all elements in common with IEnumerable other + /// + /// + /// + [System.Security.SecuritySafeCritical] + public bool SetEquals(IEnumerable other) + { + if (other == null) + { + throw new ArgumentNullException("other"); + } + +#if USING_HASH_SET + HashSet asHash = other as HashSet; + if (asHash != null && comparer.Equals(Comparer.Default) && asHash.Comparer.Equals(EqualityComparer.Default)) { + return asHash.SetEquals(this); + } +#endif + SortedSet asSorted = other as SortedSet; + if (asSorted != null && AreComparersEqual(this, asSorted)) + { + IEnumerator mine = this.GetEnumerator(); + IEnumerator theirs = asSorted.GetEnumerator(); + bool mineEnded = !mine.MoveNext(); + bool theirsEnded = !theirs.MoveNext(); + while (!mineEnded && !theirsEnded) + { + if (Comparer.Compare(mine.Current, theirs.Current) != 0) + { + return false; + } + mineEnded = !mine.MoveNext(); + theirsEnded = !theirs.MoveNext(); + } + return mineEnded && theirsEnded; + } + + //worst case: mark every element in my set and see if i've counted all + //O(N) by size of other + ElementCount result = CheckUniqueAndUnfoundElements(other, true); + return (result.uniqueCount == Count && result.unfoundCount == 0); + } + + + + /// + /// Checks whether this Tree has any elements in common with IEnumerable other + /// + /// + /// + public bool Overlaps(IEnumerable other) + { + if (other == null) + { + throw new ArgumentNullException("other"); + } + + if (this.Count == 0) + return false; + + if ((other as ICollection != null) && (other as ICollection).Count == 0) + return false; + + SortedSet asSorted = other as SortedSet; + if (asSorted != null && AreComparersEqual(this, asSorted) && (comparer.Compare(Min, asSorted.Max) > 0 || comparer.Compare(Max, asSorted.Min) < 0)) + { + return false; + } +#if USING_HASH_SET + HashSet asHash = other as HashSet; + if (asHash != null && comparer.Equals(Comparer.Default) && asHash.Comparer.Equals(EqualityComparer.Default)) { + return asHash.Overlaps(this); + } +#endif + foreach (T item in other) + { + if (this.Contains(item)) + { + return true; + } + } + return false; + } + + /// + /// This works similar to HashSet's CheckUniqueAndUnfound (description below), except that the bit + /// array maps differently than in the HashSet. We can only use this for the bulk boolean checks. + /// + /// Determines counts that can be used to determine equality, subset, and superset. This + /// is only used when other is an IEnumerable and not a HashSet. If other is a HashSet + /// these properties can be checked faster without use of marking because we can assume + /// other has no duplicates. + /// + /// The following count checks are performed by callers: + /// 1. Equals: checks if unfoundCount = 0 and uniqueFoundCount = Count; i.e. everything + /// in other is in this and everything in this is in other + /// 2. Subset: checks if unfoundCount >= 0 and uniqueFoundCount = Count; i.e. other may + /// have elements not in this and everything in this is in other + /// 3. Proper subset: checks if unfoundCount > 0 and uniqueFoundCount = Count; i.e + /// other must have at least one element not in this and everything in this is in other + /// 4. Proper superset: checks if unfound count = 0 and uniqueFoundCount strictly less + /// than Count; i.e. everything in other was in this and this had at least one element + /// not contained in other. + /// + /// An earlier implementation used delegates to perform these checks rather than returning + /// an ElementCount struct; however this was changed due to the perf overhead of delegates. + /// + /// + /// Allows us to finish faster for equals and proper superset + /// because unfoundCount must be 0. + /// + // + // + // + // + // + // + [System.Security.SecurityCritical] + private unsafe ElementCount CheckUniqueAndUnfoundElements(IEnumerable other, bool returnIfUnfound) + { + ElementCount result; + + // need special case in case this has no elements. + if (Count == 0) + { + int numElementsInOther = 0; + foreach (T item in other) + { + numElementsInOther++; + // break right away, all we want to know is whether other has 0 or 1 elements + break; + } + result.uniqueCount = 0; + result.unfoundCount = numElementsInOther; + return result; + } + + + int originalLastIndex = Count; + int intArrayLength = BitHelper.ToIntArrayLength(originalLastIndex); + + BitHelper bitHelper; + if (intArrayLength <= StackAllocThreshold) + { + int* bitArrayPtr = stackalloc int[intArrayLength]; + bitHelper = new BitHelper(bitArrayPtr, intArrayLength); + } + else + { + int[] bitArray = new int[intArrayLength]; + bitHelper = new BitHelper(bitArray, intArrayLength); + } + + // count of items in other not found in this + int unfoundCount = 0; + // count of unique items in other found in this + int uniqueFoundCount = 0; + + foreach (T item in other) + { + int index = InternalIndexOf(item); + if (index >= 0) + { + if (!bitHelper.IsMarked(index)) + { + // item hasn't been seen yet + bitHelper.MarkBit(index); + uniqueFoundCount++; + } + } + else + { + unfoundCount++; + if (returnIfUnfound) + { + break; + } + } + } + + result.uniqueCount = uniqueFoundCount; + result.unfoundCount = unfoundCount; + return result; + } + public int RemoveWhere(Predicate match) + { + if (match == null) + { + throw new ArgumentNullException("match"); + } + List matches = new List(this.Count); + + BreadthFirstTreeWalk(delegate (Node n) + { + if (match(n.Item)) + { + matches.Add(n.Item); + } + return true; + }); + // reverse breadth first to (try to) incur low cost + int actuallyRemoved = 0; + for (int i = matches.Count - 1; i >= 0; i--) + { + if (this.Remove(matches[i])) + { + actuallyRemoved++; + } + } + + return actuallyRemoved; + + } + + + #endregion + + #region ISorted Members + + + public T Min + { + get + { + T ret = default(T); + InOrderTreeWalk(delegate (SortedSet.Node n) { ret = n.Item; return false; }); + return ret; + } + } + + public T Max + { + get + { + T ret = default(T); + InOrderTreeWalk(delegate (SortedSet.Node n) { ret = n.Item; return false; }, true); + return ret; + } + } + + public IEnumerable Reverse() + { + Enumerator e = new Enumerator(this, true); + while (e.MoveNext()) + { + yield return e.Current; + } + } + + + /// + /// Returns a subset of this tree ranging from values lBound to uBound + /// Any changes made to the subset reflect in the actual tree + /// + /// Lowest Value allowed in the subset + /// Highest Value allowed in the subset + public virtual SortedSet GetViewBetween(T lowerValue, T upperValue) + { + if (Comparer.Compare(lowerValue, upperValue) > 0) + { + throw new ArgumentException("lowerBound is greater than upperBound"); + } + return new TreeSubSet(this, lowerValue, upperValue, true, true); + } + +#if DEBUG + + /// + /// debug status to be checked whenever any operation is called + /// + /// + internal virtual bool versionUpToDate() + { + return true; + } +#endif + + + /// + /// This class represents a subset view into the tree. Any changes to this view + /// are reflected in the actual tree. Uses the Comparator of the underlying tree. + /// + /// +#if !FEATURE_NETCORE + [Serializable] + internal sealed class TreeSubSet : SortedSet, ISerializable, IDeserializationCallback + { +#else + internal sealed class TreeSubSet : SortedSet { +#endif + SortedSet underlying; + T min, max; + //these exist for unbounded collections + //for instance, you could allow this subset to be defined for i>10. The set will throw if + //anything <=10 is added, but there is no upperbound. These features Head(), Tail(), were punted + //in the spec, and are not available, but the framework is there to make them available at some point. + bool lBoundActive, uBoundActive; + //used to see if the count is out of date + + +#if DEBUG + internal override bool versionUpToDate() + { + return (this.version == underlying.version); + } +#endif + + public TreeSubSet(SortedSet Underlying, T Min, T Max, bool lowerBoundActive, bool upperBoundActive) + : base(Underlying.Comparer) + { + underlying = Underlying; + min = Min; + max = Max; + lBoundActive = lowerBoundActive; + uBoundActive = upperBoundActive; + root = underlying.FindRange(min, max, lBoundActive, uBoundActive); // root is first element within range + count = 0; + version = -1; + VersionCheckImpl(); + } + +#if !FEATURE_NETCORE + /// + /// For serialization and deserialization + /// + private TreeSubSet() + { + comparer = null; + } + + + [SuppressMessage("Microsoft.Usage", "CA2236:CallBaseClassMethodsOnISerializableTypes", Justification = "special case TreeSubSet serialization")] + private TreeSubSet(SerializationInfo info, StreamingContext context) + { + siInfo = info; + OnDeserializationImpl(info); + } +#endif // !FEATURE_NETCORE + + /// + /// Additions to this tree need to be added to the underlying tree as well + /// + + internal override bool AddIfNotPresent(T item) + { + + if (!IsWithinRange(item)) + { + ThrowHelper.ThrowArgumentOutOfRangeException(ExceptionArgument.collection); + } + + bool ret = underlying.AddIfNotPresent(item); + VersionCheck(); +#if DEBUG + Debug.Assert(this.versionUpToDate() && this.root == this.underlying.FindRange(min, max)); +#endif + + return ret; + } + + + public override bool Contains(T item) + { + VersionCheck(); +#if DEBUG + Debug.Assert(this.versionUpToDate() && this.root == this.underlying.FindRange(min, max)); +#endif + return base.Contains(item); + } + + internal override bool DoRemove(T item) + { // todo: uppercase this and others + + if (!IsWithinRange(item)) + { + return false; + } + + bool ret = underlying.Remove(item); + VersionCheck(); +#if DEBUG + Debug.Assert(this.versionUpToDate() && this.root == this.underlying.FindRange(min, max)); +#endif + return ret; + } + + public override void Clear() + { + + + if (count == 0) + { + return; + } + + List toRemove = new List(); + BreadthFirstTreeWalk(delegate (Node n) { toRemove.Add(n.Item); return true; }); + while (toRemove.Count != 0) + { + underlying.Remove(toRemove[toRemove.Count - 1]); + toRemove.RemoveAt(toRemove.Count - 1); + } + root = null; + count = 0; + version = underlying.version; + } + + + internal override bool IsWithinRange(T item) + { + + int comp = (lBoundActive ? Comparer.Compare(min, item) : -1); + if (comp > 0) + { + return false; + } + comp = (uBoundActive ? Comparer.Compare(max, item) : 1); + if (comp < 0) + { + return false; + } + return true; + } + + internal override bool InOrderTreeWalk(TreeWalkPredicate action, Boolean reverse) + { + VersionCheck(); + + if (root == null) + { + return true; + } + + // The maximum height of a red-black tree is 2*lg(n+1). + // See page 264 of "Introduction to algorithms" by Thomas H. Cormen + Stack stack = new Stack(2 * (int)SortedSet.log2(count + 1)); //this is not exactly right if count is out of date, but the stack can grow + Node current = root; + while (current != null) + { + if (IsWithinRange(current.Item)) + { + stack.Push(current); + current = (reverse ? current.Right : current.Left); + } + else if (lBoundActive && Comparer.Compare(min, current.Item) > 0) + { + current = current.Right; + } + else + { + current = current.Left; + } + } + + while (stack.Count != 0) + { + current = stack.Pop(); + if (!action(current)) + { + return false; + } + + Node node = (reverse ? current.Left : current.Right); + while (node != null) + { + if (IsWithinRange(node.Item)) + { + stack.Push(node); + node = (reverse ? node.Right : node.Left); + } + else if (lBoundActive && Comparer.Compare(min, node.Item) > 0) + { + node = node.Right; + } + else + { + node = node.Left; + } + } + } + return true; + } + + internal override bool BreadthFirstTreeWalk(TreeWalkPredicate action) + { + VersionCheck(); + + if (root == null) + { + return true; + } + + List processQueue = new List(); + processQueue.Add(root); + Node current; + + while (processQueue.Count != 0) + { + current = processQueue[0]; + processQueue.RemoveAt(0); + if (IsWithinRange(current.Item) && !action(current)) + { + return false; + } + if (current.Left != null && (!lBoundActive || Comparer.Compare(min, current.Item) < 0)) + { + processQueue.Add(current.Left); + } + if (current.Right != null && (!uBoundActive || Comparer.Compare(max, current.Item) > 0)) + { + processQueue.Add(current.Right); + } + + } + return true; + } + + internal override SortedSet.Node FindNode(T item) + { + + if (!IsWithinRange(item)) + { + return null; + } + VersionCheck(); +#if DEBUG + Debug.Assert(this.versionUpToDate() && this.root == this.underlying.FindRange(min, max)); +#endif + return base.FindNode(item); + } + + //this does indexing in an inefficient way compared to the actual sortedset, but it saves a + //lot of space + internal override int InternalIndexOf(T item) + { + int count = -1; + foreach (T i in this) + { + count++; + if (Comparer.Compare(item, i) == 0) + return count; + } +#if DEBUG + Debug.Assert(this.versionUpToDate() && this.root == this.underlying.FindRange(min, max)); +#endif + return -1; + } + /// + /// checks whether this subset is out of date. updates if necessary. + /// + internal override void VersionCheck() + { + VersionCheckImpl(); + } + + private void VersionCheckImpl() + { + Debug.Assert(underlying != null, "Underlying set no longer exists"); + if (this.version != underlying.version) + { + this.root = underlying.FindRange(min, max, lBoundActive, uBoundActive); + this.version = underlying.version; + count = 0; + InOrderTreeWalk(delegate (Node n) { count++; return true; }); + } + } + + + + //This passes functionality down to the underlying tree, clipping edges if necessary + //There's nothing gained by having a nested subset. May as well draw it from the base + //Cannot increase the bounds of the subset, can only decrease it + public override SortedSet GetViewBetween(T lowerValue, T upperValue) + { + + if (lBoundActive && Comparer.Compare(min, lowerValue) > 0) + { + //lBound = min; + throw new ArgumentOutOfRangeException("lowerValue"); + } + if (uBoundActive && Comparer.Compare(max, upperValue) < 0) + { + //uBound = max; + throw new ArgumentOutOfRangeException("upperValue"); + } + TreeSubSet ret = (TreeSubSet)underlying.GetViewBetween(lowerValue, upperValue); + return ret; + } + + internal override void IntersectWithEnumerable(IEnumerable other) + { + + List toSave = new List(this.Count); + foreach (T item in other) + { + if (this.Contains(item)) + { + toSave.Add(item); + this.Remove(item); + } + } + this.Clear(); + this.AddAllElements(toSave); +#if DEBUG + Debug.Assert(this.versionUpToDate() && this.root == this.underlying.FindRange(min, max)); +#endif + } + +#if !FEATURE_NETCORE + void ISerializable.GetObjectData(SerializationInfo info, StreamingContext context) + { + GetObjectData(info, context); + } + + protected override void GetObjectData(SerializationInfo info, StreamingContext context) + { + if (info == null) + { + ThrowHelper.ThrowArgumentNullException(ExceptionArgument.info); + } + info.AddValue(maxName, max, typeof(T)); + info.AddValue(minName, min, typeof(T)); + info.AddValue(lBoundActiveName, lBoundActive); + info.AddValue(uBoundActiveName, uBoundActive); + base.GetObjectData(info, context); + } + + void IDeserializationCallback.OnDeserialization(Object sender) + { + //don't do anything here as its already been done by the constructor + //OnDeserialization(sender); + + } + + protected override void OnDeserialization(Object sender) + { + OnDeserializationImpl(sender); + } + + private void OnDeserializationImpl(Object sender) + { + if (siInfo == null) + { + ThrowHelper.ThrowSerializationException(ExceptionResource.Serialization_InvalidOnDeser); + } + + comparer = (IComparer)siInfo.GetValue(ComparerName, typeof(IComparer)); + int savedCount = siInfo.GetInt32(CountName); + max = (T)siInfo.GetValue(maxName, typeof(T)); + min = (T)siInfo.GetValue(minName, typeof(T)); + lBoundActive = siInfo.GetBoolean(lBoundActiveName); + uBoundActive = siInfo.GetBoolean(uBoundActiveName); + underlying = new SortedSet(); + + if (savedCount != 0) + { + T[] items = (T[])siInfo.GetValue(ItemsName, typeof(T[])); + + if (items == null) + { + ThrowHelper.ThrowSerializationException(ExceptionResource.Serialization_MissingValues); + } + + for (int i = 0; i < items.Length; i++) + { + underlying.Add(items[i]); + } + } + underlying.version = siInfo.GetInt32(VersionName); + count = underlying.count; + version = underlying.version - 1; + VersionCheck(); //this should update the count to be right and update root to be right + + if (count != savedCount) + { + ThrowHelper.ThrowSerializationException(ExceptionResource.Serialization_MismatchedCount); + } + siInfo = null; + + } +#endif // !FEATURE_NETCORE + + + + + } + + + #endregion + + #region Serialization methods + +#if !FEATURE_NETCORE + // LinkDemand here is unnecessary as this is a methodimpl and linkdemand from the interface should suffice + void ISerializable.GetObjectData(SerializationInfo info, StreamingContext context) + { + GetObjectData(info, context); + } + + protected virtual void GetObjectData(SerializationInfo info, StreamingContext context) + { + if (info == null) + { + ThrowHelper.ThrowArgumentNullException(ExceptionArgument.info); + } + + info.AddValue(CountName, count); //This is the length of the bucket array. + info.AddValue(ComparerName, comparer, typeof(IComparer)); + info.AddValue(VersionName, version); + + if (root != null) + { + T[] items = new T[Count]; + CopyTo(items, 0); + info.AddValue(ItemsName, items, typeof(T[])); + } + } + + void IDeserializationCallback.OnDeserialization(Object sender) + { + OnDeserialization(sender); + } + + protected virtual void OnDeserialization(Object sender) + { + if (comparer != null) + { + return; //Somebody had a dependency on this class and fixed us up before the ObjectManager got to it. + } + + if (siInfo == null) + { + ThrowHelper.ThrowSerializationException(ExceptionResource.Serialization_InvalidOnDeser); + } + + comparer = (IComparer)siInfo.GetValue(ComparerName, typeof(IComparer)); + int savedCount = siInfo.GetInt32(CountName); + + if (savedCount != 0) + { + T[] items = (T[])siInfo.GetValue(ItemsName, typeof(T[])); + + if (items == null) + { + ThrowHelper.ThrowSerializationException(ExceptionResource.Serialization_MissingValues); + } + + for (int i = 0; i < items.Length; i++) + { + Add(items[i]); + } + } + + version = siInfo.GetInt32(VersionName); + if (count != savedCount) + { + ThrowHelper.ThrowSerializationException(ExceptionResource.Serialization_MismatchedCount); + } + siInfo = null; + } +#endif //!FEATURE_NETCORE + #endregion + + #region Helper Classes + internal class Node + { + public bool IsRed; + public T Item; + public Node Left; + public Node Right; + + public Node(T item) + { + // The default color will be red, we never need to create a black node directly. + this.Item = item; + IsRed = true; + } + + public Node(T item, bool isRed) + { + // The default color will be red, we never need to create a black node directly. + this.Item = item; + this.IsRed = isRed; + } + } + + [SuppressMessage("Microsoft.Performance", "CA1815:OverrideEqualsAndOperatorEqualsOnValueTypes", Justification = "not an expected scenario")] +#if !FEATURE_NETCORE + [Serializable] + public struct Enumerator : IEnumerator, IEnumerator, ISerializable, IDeserializationCallback + { +#else + public struct Enumerator : IEnumerator, IEnumerator { +#endif + private SortedSet tree; + private int version; + + + private Stack.Node> stack; + private SortedSet.Node current; + static SortedSet.Node dummyNode = new SortedSet.Node(default(T)); + + private bool reverse; + +#if !FEATURE_NETCORE + private SerializationInfo siInfo; +#endif + internal Enumerator(SortedSet set) + { + tree = set; + //this is a hack to make sure that the underlying subset has not been changed since + // + tree.VersionCheck(); + + version = tree.version; + + // 2lg(n + 1) is the maximum height + stack = new Stack.Node>(2 * (int)SortedSet.log2(set.Count + 1)); + current = null; + reverse = false; +#if !FEATURE_NETCORE + siInfo = null; +#endif + Intialize(); + } + + internal Enumerator(SortedSet set, bool reverse) + { + tree = set; + //this is a hack to make sure that the underlying subset has not been changed since + // + tree.VersionCheck(); + version = tree.version; + + // 2lg(n + 1) is the maximum height + stack = new Stack.Node>(2 * (int)SortedSet.log2(set.Count + 1)); + current = null; + this.reverse = reverse; +#if !FEATURE_NETCORE + siInfo = null; +#endif + Intialize(); + + } + +#if !FEATURE_NETCORE + private Enumerator(SerializationInfo info, StreamingContext context) + { + tree = null; + version = -1; + current = null; + reverse = false; + stack = null; + this.siInfo = info; + } + + void ISerializable.GetObjectData(SerializationInfo info, StreamingContext context) + { + GetObjectData(info, context); + } + + private void GetObjectData(SerializationInfo info, StreamingContext context) + { + if (info == null) + { + ThrowHelper.ThrowArgumentNullException(ExceptionArgument.info); + + } + info.AddValue(TreeName, tree, typeof(SortedSet)); + info.AddValue(EnumVersionName, version); + info.AddValue(ReverseName, reverse); + info.AddValue(EnumStartName, !NotStartedOrEnded); + info.AddValue(NodeValueName, (current == null ? dummyNode.Item : current.Item), typeof(T)); + } + + void IDeserializationCallback.OnDeserialization(Object sender) + { + OnDeserialization(sender); + } + + private void OnDeserialization(Object sender) + { + if (siInfo == null) + { + ThrowHelper.ThrowSerializationException(ExceptionResource.Serialization_InvalidOnDeser); + } + + tree = (SortedSet)siInfo.GetValue(TreeName, typeof(SortedSet)); + version = siInfo.GetInt32(EnumVersionName); + reverse = siInfo.GetBoolean(ReverseName); + bool EnumStarted = siInfo.GetBoolean(EnumStartName); + stack = new Stack.Node>(2 * (int)SortedSet.log2(tree.Count + 1)); + current = null; + if (EnumStarted) + { + T item = (T)siInfo.GetValue(NodeValueName, typeof(T)); + Intialize(); + //go until it reaches the value we want + while (this.MoveNext()) + { + if (tree.Comparer.Compare(this.Current, item) == 0) + break; + } + } + + + } +#endif //!FEATURE_NETCORE + + + private void Intialize() + { + + current = null; + SortedSet.Node node = tree.root; + Node next = null, other = null; + while (node != null) + { + next = (reverse ? node.Right : node.Left); + other = (reverse ? node.Left : node.Right); + if (tree.IsWithinRange(node.Item)) + { + stack.Push(node); + node = next; + } + else if (next == null || !tree.IsWithinRange(next.Item)) + { + node = other; + } + else + { + node = next; + } + } + } + + public bool MoveNext() + { + + //this is a hack to make sure that the underlying subset has not been changed since + // + tree.VersionCheck(); + + if (version != tree.version) + { + ThrowHelper.ThrowInvalidOperationException(ExceptionResource.InvalidOperation_EnumFailedVersion); + } + + if (stack.Count == 0) + { + current = null; + return false; + } + + current = stack.Pop(); + SortedSet.Node node = (reverse ? current.Left : current.Right); + Node next = null, other = null; + while (node != null) + { + next = (reverse ? node.Right : node.Left); + other = (reverse ? node.Left : node.Right); + if (tree.IsWithinRange(node.Item)) + { + stack.Push(node); + node = next; + } + else if (other == null || !tree.IsWithinRange(other.Item)) + { + node = next; + } + else + { + node = other; + } + } + return true; + } + + public void Dispose() + { + } + + public T Current + { + get + { + if (current != null) + { + return current.Item; + } + return default(T); + } + } + + object IEnumerator.Current + { + get + { + if (current == null) + { + ThrowHelper.ThrowInvalidOperationException(ExceptionResource.InvalidOperation_EnumOpCantHappen); + } + + return current.Item; + } + } + + internal bool NotStartedOrEnded + { + get + { + return current == null; + } + } + + internal void Reset() + { + if (version != tree.version) + { + ThrowHelper.ThrowInvalidOperationException(ExceptionResource.InvalidOperation_EnumFailedVersion); + } + + stack.Clear(); + Intialize(); + } + + void IEnumerator.Reset() + { + Reset(); + } + + + + + } + + + + internal struct ElementCount + { + internal int uniqueCount; + internal int unfoundCount; + } + #endregion + + #region misc + + /// + /// Searches the set for a given value and returns the equal value it finds, if any. + /// + /// The value to search for. + /// The value from the set that the search found, or the default value of when the search yielded no match. + /// A value indicating whether the search was successful. + /// + /// This can be useful when you want to reuse a previously stored reference instead of + /// a newly constructed one (so that more sharing of references can occur) or to look up + /// a value that has more complete data than the value you currently have, although their + /// comparer functions indicate they are equal. + /// + public bool TryGetValue(T equalValue, out T actualValue) + { + Node node = FindNode(equalValue); + if (node != null) + { + actualValue = node.Item; + return true; + } + actualValue = default(T); + return false; + } + + // used for set checking operations (using enumerables) that rely on counting + private static int log2(int value) + { + //Contract.Requires(value>0) + int c = 0; + while (value > 0) + { + c++; + value >>= 1; + } + return c; + } + #endregion + + + } + + /// + /// A class that generates an IEqualityComparer for this SortedSet. Requires that the definition of + /// equality defined by the IComparer for this SortedSet be consistent with the default IEqualityComparer + /// for the type T. If not, such an IEqualityComparer should be provided through the constructor. + /// + internal class SortedSetEqualityComparer : IEqualityComparer> + { + private IComparer comparer; + private IEqualityComparer e_comparer; + + public SortedSetEqualityComparer() : this(null, null) { } + + public SortedSetEqualityComparer(IComparer comparer) : this(comparer, null) { } + + public SortedSetEqualityComparer(IEqualityComparer memberEqualityComparer) : this(null, memberEqualityComparer) { } + + /// + /// Create a new SetEqualityComparer, given a comparer for member order and another for member equality (these + /// must be consistent in their definition of equality) + /// + public SortedSetEqualityComparer(IComparer comparer, IEqualityComparer memberEqualityComparer) + { + if (comparer == null) + this.comparer = Comparer.Default; + else + this.comparer = comparer; + if (memberEqualityComparer == null) + e_comparer = EqualityComparer.Default; + else + e_comparer = memberEqualityComparer; + } + + + // using comparer to keep equals properties in tact; don't want to choose one of the comparers + public bool Equals(SortedSet x, SortedSet y) + { + return SortedSet.SortedSetEquals(x, y, comparer); + } + //IMPORTANT: this part uses the fact that GetHashCode() is consistent with the notion of equality in + //the set + public int GetHashCode(SortedSet obj) + { + int hashCode = 0; + if (obj != null) + { + foreach (T t in obj) + { + hashCode = hashCode ^ (e_comparer.GetHashCode(t) & 0x7FFFFFFF); + } + } // else returns hashcode of 0 for null HashSets + return hashCode; + } + + // Equals method for the comparer itself. + public override bool Equals(Object obj) + { + SortedSetEqualityComparer comparer = obj as SortedSetEqualityComparer; + if (comparer == null) + { + return false; + } + return (this.comparer == comparer.comparer); + } + + public override int GetHashCode() + { + return comparer.GetHashCode() ^ e_comparer.GetHashCode(); + } + + + } + +} +#endif \ No newline at end of file diff --git a/src/IFoxCAD.Basal/Sortedset/ThrowHelper.cs b/src/IFoxCAD.Basal/Sortedset/ThrowHelper.cs new file mode 100644 index 0000000..01ed264 --- /dev/null +++ b/src/IFoxCAD.Basal/Sortedset/ThrowHelper.cs @@ -0,0 +1,378 @@ +#if NET35 +namespace System +{ + // This file defines an internal class used to throw exceptions in BCL code. + // The main purpose is to reduce code size. + // + // The old way to throw an exception generates quite a lot IL code and assembly code. + // Following is an example: + // C# source + // throw new ArgumentNullException("key", SR.GetString("ArgumentNull_Key")); + // IL code: + // IL_0003: ldstr "key" + // IL_0008: ldstr "ArgumentNull_Key" + // IL_000d: call string System.Environment::GetResourceString(string) + // IL_0012: newobj instance void System.ArgumentNullException::.ctor(string,string) + // IL_0017: throw + // which is 21bytes in IL. + // + // So we want to get rid of the ldstr and call to Environment.GetResource in IL. + // In order to do that, I created two enums: ExceptionResource, ExceptionArgument to represent the + // argument name and resource name in a small integer. The source code will be changed to + // ThrowHelper.ThrowArgumentNullException(ExceptionArgument.key, ExceptionResource.ArgumentNull_Key); + // + // The IL code will be 7 bytes. + // IL_0008: ldc.i4.4 + // IL_0009: ldc.i4.4 + // IL_000a: call void System.ThrowHelper::ThrowArgumentNullException(valuetype System.ExceptionArgument) + // IL_000f: ldarg.0 + // + // This will also reduce the Jitted code size a lot. + // + // It is very important we do this for generic classes because we can easily generate the same code + // multiple times for different instantiation. + // + // < + + + + + + + + + + +#if !SILVERLIGHT + using System.Runtime.Serialization; +#endif + + using System.Diagnostics; + internal static class ThrowHelper + { + internal static void ThrowWrongKeyTypeArgumentException(object key, Type targetType) + { + throw new ArgumentException(SR.GetString("SR.Arg_WrongType", key, targetType), "key"); + } + + internal static void ThrowWrongValueTypeArgumentException(object value, Type targetType) + { + throw new ArgumentException(SR.GetString("SR.Arg_WrongType", value, targetType), "value"); + } + + internal static void ThrowKeyNotFoundException() + { + throw new System.Collections.Generic.KeyNotFoundException(); + } + + internal static void ThrowArgumentException(ExceptionResource resource) + { + throw new ArgumentException(SR.GetString(GetResourceName(resource))); + } + + internal static void ThrowArgumentNullException(ExceptionArgument argument) + { + throw new ArgumentNullException(GetArgumentName(argument)); + } + + internal static void ThrowArgumentOutOfRangeException(ExceptionArgument argument) + { + throw new ArgumentOutOfRangeException(GetArgumentName(argument)); + } + + internal static void ThrowArgumentOutOfRangeException(ExceptionArgument argument, ExceptionResource resource) + { + throw new ArgumentOutOfRangeException(GetArgumentName(argument), SR.GetString(GetResourceName(resource))); + } + + internal static void ThrowInvalidOperationException(ExceptionResource resource) + { + throw new InvalidOperationException(SR.GetString(GetResourceName(resource))); + } + +#if !SILVERLIGHT + internal static void ThrowSerializationException(ExceptionResource resource) + { + throw new SerializationException(SR.GetString(GetResourceName(resource))); + } +#endif + + internal static void ThrowNotSupportedException(ExceptionResource resource) + { + throw new NotSupportedException(SR.GetString(GetResourceName(resource))); + } + + // Allow nulls for reference types and Nullable, but not for value types. + internal static void IfNullAndNullsAreIllegalThenThrow(object value, ExceptionArgument argName) + { + // Note that default(T) is not equal to null for value types except when T is Nullable. + if (value == null && !(default(T) == null)) + ThrowHelper.ThrowArgumentNullException(argName); + } + + // + // This function will convert an ExceptionArgument enum value to the argument name string. + // + internal static string GetArgumentName(ExceptionArgument argument) + { + string argumentName = null; + + switch (argument) + { + case ExceptionArgument.array: + argumentName = "array"; + break; + + case ExceptionArgument.arrayIndex: + argumentName = "arrayIndex"; + break; + + case ExceptionArgument.capacity: + argumentName = "capacity"; + break; + + case ExceptionArgument.collection: + argumentName = "collection"; + break; + + case ExceptionArgument.converter: + argumentName = "converter"; + break; + + case ExceptionArgument.count: + argumentName = "count"; + break; + + case ExceptionArgument.dictionary: + argumentName = "dictionary"; + break; + + case ExceptionArgument.index: + argumentName = "index"; + break; + + case ExceptionArgument.info: + argumentName = "info"; + break; + + case ExceptionArgument.key: + argumentName = "key"; + break; + + case ExceptionArgument.match: + argumentName = "match"; + break; + + case ExceptionArgument.obj: + argumentName = "obj"; + break; + + case ExceptionArgument.queue: + argumentName = "queue"; + break; + + case ExceptionArgument.stack: + argumentName = "stack"; + break; + + case ExceptionArgument.startIndex: + argumentName = "startIndex"; + break; + + case ExceptionArgument.value: + argumentName = "value"; + break; + + case ExceptionArgument.item: + argumentName = "item"; + break; + + default: + Debug.Assert(false, "The enum value is not defined, please checked ExceptionArgumentName Enum."); + return string.Empty; + } + + return argumentName; + } + + // + // This function will convert an ExceptionResource enum value to the resource string. + // + internal static string GetResourceName(ExceptionResource resource) + { + string resourceName = null; + + switch (resource) + { + case ExceptionResource.Argument_ImplementIComparable: + resourceName = "SR.Argument_ImplementIComparable"; + break; + + case ExceptionResource.Argument_AddingDuplicate: + resourceName = "SR.Argument_AddingDuplicate"; + break; + + case ExceptionResource.ArgumentOutOfRange_Index: + resourceName = "SR.ArgumentOutOfRange_Index"; + break; + + case ExceptionResource.ArgumentOutOfRange_NeedNonNegNum: + resourceName = "SR.ArgumentOutOfRange_NeedNonNegNum"; + break; + + case ExceptionResource.ArgumentOutOfRange_NeedNonNegNumRequired: + resourceName = "SR.ArgumentOutOfRange_NeedNonNegNumRequired"; + break; + + case ExceptionResource.ArgumentOutOfRange_SmallCapacity: + resourceName = "SR.ArgumentOutOfRange_SmallCapacity"; + break; + + case ExceptionResource.Arg_ArrayPlusOffTooSmall: + resourceName = "SR.Arg_ArrayPlusOffTooSmall"; + break; + + case ExceptionResource.Arg_RankMultiDimNotSupported: + resourceName = "SR.Arg_MultiRank"; + break; + + case ExceptionResource.Arg_NonZeroLowerBound: + resourceName = "SR.Arg_NonZeroLowerBound"; + break; + + case ExceptionResource.Argument_InvalidArrayType: + resourceName = "SR.Invalid_Array_Type"; + break; + + case ExceptionResource.Argument_InvalidOffLen: + resourceName = "SR.Argument_InvalidOffLen"; + break; + + case ExceptionResource.InvalidOperation_CannotRemoveFromStackOrQueue: + resourceName = "SR.InvalidOperation_CannotRemoveFromStackOrQueue"; + break; + + case ExceptionResource.InvalidOperation_EmptyCollection: + resourceName = "SR.InvalidOperation_EmptyCollection"; + break; + + case ExceptionResource.InvalidOperation_EmptyQueue: + resourceName = "SR.InvalidOperation_EmptyQueue"; + break; + + case ExceptionResource.InvalidOperation_EnumOpCantHappen: + resourceName = "SR.InvalidOperation_EnumOpCantHappen"; + break; + + case ExceptionResource.InvalidOperation_EnumFailedVersion: + resourceName = "SR.InvalidOperation_EnumFailedVersion"; + break; + + case ExceptionResource.InvalidOperation_EmptyStack: + resourceName = "SR.InvalidOperation_EmptyStack"; + break; + + case ExceptionResource.InvalidOperation_EnumNotStarted: + resourceName = "SR.InvalidOperation_EnumNotStarted"; + break; + + case ExceptionResource.InvalidOperation_EnumEnded: + resourceName = "SR.InvalidOperation_EnumEnded"; + break; + + case ExceptionResource.NotSupported_KeyCollectionSet: + resourceName = "SR.NotSupported_KeyCollectionSet"; + break; + + case ExceptionResource.NotSupported_SortedListNestedWrite: + resourceName = "SR.NotSupported_SortedListNestedWrite"; + break; + +#if !SILVERLIGHT + case ExceptionResource.Serialization_InvalidOnDeser: + resourceName = "SR.Serialization_InvalidOnDeser"; + break; + + case ExceptionResource.Serialization_MissingValues: + resourceName = "SR.Serialization_MissingValues"; + break; + + case ExceptionResource.Serialization_MismatchedCount: + resourceName = "SR.Serialization_MismatchedCount"; + break; +#endif + + case ExceptionResource.NotSupported_ValueCollectionSet: + resourceName = "SR.NotSupported_ValueCollectionSet"; + break; + + default: + Debug.Assert(false, "The enum value is not defined, please checked ExceptionArgumentName Enum."); + return string.Empty; + } + + return resourceName; + } + + } + + // + // The convention for this enum is using the argument name as the enum name + // + internal enum ExceptionArgument + { + obj, + dictionary, + array, + info, + key, + collection, + match, + converter, + queue, + stack, + capacity, + index, + startIndex, + value, + count, + arrayIndex, + item, + } + + // + // The convention for this enum is using the resource name as the enum name + // + internal enum ExceptionResource + { + Argument_ImplementIComparable, + ArgumentOutOfRange_NeedNonNegNum, + ArgumentOutOfRange_NeedNonNegNumRequired, + Arg_ArrayPlusOffTooSmall, + Argument_AddingDuplicate, + Serialization_InvalidOnDeser, + Serialization_MismatchedCount, + Serialization_MissingValues, + Arg_RankMultiDimNotSupported, + Arg_NonZeroLowerBound, + Argument_InvalidArrayType, + NotSupported_KeyCollectionSet, + ArgumentOutOfRange_SmallCapacity, + ArgumentOutOfRange_Index, + Argument_InvalidOffLen, + NotSupported_ReadOnlyCollection, + InvalidOperation_CannotRemoveFromStackOrQueue, + InvalidOperation_EmptyCollection, + InvalidOperation_EmptyQueue, + InvalidOperation_EnumOpCantHappen, + InvalidOperation_EnumFailedVersion, + InvalidOperation_EmptyStack, + InvalidOperation_EnumNotStarted, + InvalidOperation_EnumEnded, + NotSupported_SortedListNestedWrite, + NotSupported_ValueCollectionSet, + } +} + + +#endif \ No newline at end of file diff --git a/src/IFoxCAD.Basal/Sortedset/bithelper.cs b/src/IFoxCAD.Basal/Sortedset/bithelper.cs new file mode 100644 index 0000000..f5edff8 --- /dev/null +++ b/src/IFoxCAD.Basal/Sortedset/bithelper.cs @@ -0,0 +1,165 @@ +#if NET35 +using System; +using System.Collections; +using System.Text; + +namespace System.Collections.Generic +{ + + /// + /// ABOUT: + /// Helps with operations that rely on bit marking to indicate whether an item in the + /// collection should be added, removed, visited already, etc. + /// + /// BitHelper doesn't allocate the array; you must pass in an array or ints allocated on the + /// stack or heap. ToIntArrayLength() tells you the int array size you must allocate. + /// + /// USAGE: + /// Suppose you need to represent a bit array of length (i.e. logical bit array length) + /// BIT_ARRAY_LENGTH. Then this is the suggested way to instantiate BitHelper: + /// *************************************************************************** + /// int intArrayLength = BitHelper.ToIntArrayLength(BIT_ARRAY_LENGTH); + /// BitHelper bitHelper; + /// if (intArrayLength less than stack alloc threshold) + /// int* m_arrayPtr = stackalloc int[intArrayLength]; + /// bitHelper = new BitHelper(m_arrayPtr, intArrayLength); + /// else + /// int[] m_arrayPtr = new int[intArrayLength]; + /// bitHelper = new BitHelper(m_arrayPtr, intArrayLength); + /// *************************************************************************** + /// + /// IMPORTANT: + /// The second ctor args, length, should be specified as the length of the int array, not + /// the logical bit array. Because length is used for bounds checking into the int array, + /// it's especially important to get this correct for the stackalloc version. See the code + /// samples above; this is the value gotten from ToIntArrayLength(). + /// + /// The length ctor argument is the only exception; for other methods -- MarkBit and + /// IsMarked -- pass in values as indices into the logical bit array, and it will be mapped + /// to the position within the array of ints. + /// + /// + + + + + unsafe internal class BitHelper + { // should not be serialized + + private const byte MarkedBitFlag = 1; + private const byte IntSize = 32; + + // m_length of underlying int array (not logical bit array) + private int m_length; + + // ptr to stack alloc'd array of ints + [System.Security.SecurityCritical] + private int* m_arrayPtr; + + // array of ints + private int[] m_array; + + // whether to operate on stack alloc'd or heap alloc'd array + private bool useStackAlloc; + + /// + /// Instantiates a BitHelper with a heap alloc'd array of ints + /// + /// int array to hold bits + /// length of int array + // + // + // + // + [System.Security.SecurityCritical] + internal BitHelper(int* bitArrayPtr, int length) + { + this.m_arrayPtr = bitArrayPtr; + this.m_length = length; + useStackAlloc = true; + } + + /// + /// Instantiates a BitHelper with a heap alloc'd array of ints + /// + /// int array to hold bits + /// length of int array + internal BitHelper(int[] bitArray, int length) + { + this.m_array = bitArray; + this.m_length = length; + } + + /// + /// Mark bit at specified position + /// + /// + // + // + // + [System.Security.SecurityCritical] + internal unsafe void MarkBit(int bitPosition) + { + if (useStackAlloc) + { + int bitArrayIndex = bitPosition / IntSize; + if (bitArrayIndex < m_length && bitArrayIndex >= 0) + { + m_arrayPtr[bitArrayIndex] |= (MarkedBitFlag << (bitPosition % IntSize)); + } + } + else + { + int bitArrayIndex = bitPosition / IntSize; + if (bitArrayIndex < m_length && bitArrayIndex >= 0) + { + m_array[bitArrayIndex] |= (MarkedBitFlag << (bitPosition % IntSize)); + } + } + } + + /// + /// Is bit at specified position marked? + /// + /// + /// + // + // + // + [System.Security.SecurityCritical] + internal unsafe bool IsMarked(int bitPosition) + { + if (useStackAlloc) + { + int bitArrayIndex = bitPosition / IntSize; + if (bitArrayIndex < m_length && bitArrayIndex >= 0) + { + return ((m_arrayPtr[bitArrayIndex] & (MarkedBitFlag << (bitPosition % IntSize))) != 0); + } + return false; + } + else + { + int bitArrayIndex = bitPosition / IntSize; + if (bitArrayIndex < m_length && bitArrayIndex >= 0) + { + return ((m_array[bitArrayIndex] & (MarkedBitFlag << (bitPosition % IntSize))) != 0); + } + return false; + } + } + + /// + /// How many ints must be allocated to represent n bits. Returns (n+31)/32, but + /// avoids overflow + /// + /// + /// + internal static int ToIntArrayLength(int n) + { + return n > 0 ? ((n - 1) / IntSize + 1) : 0; + } + + } +} +#endif \ No newline at end of file -- Gitee From b8ab655aaab58add282df911f3d67d67e694af7c Mon Sep 17 00:00:00 2001 From: liuqihong <540762622@qq.com> Date: Thu, 23 Jun 2022 00:46:07 +0800 Subject: [PATCH 265/675] =?UTF-8?q?=E4=BF=AE=E6=94=B9=E4=BA=8ESR=E5=86=B2?= =?UTF-8?q?=E7=AA=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/IFoxCAD.Basal/CLS/ValueTuple.cs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/IFoxCAD.Basal/CLS/ValueTuple.cs b/src/IFoxCAD.Basal/CLS/ValueTuple.cs index 1bbe861..bece1ed 100644 --- a/src/IFoxCAD.Basal/CLS/ValueTuple.cs +++ b/src/IFoxCAD.Basal/CLS/ValueTuple.cs @@ -61,7 +61,8 @@ public static int Combine(int h1, int h2) namespace System { - internal static class SR + //internal static class SR + internal sealed partial class SR { // public const string ArgumentException_ValueTupleIncorrectType = "The parameter should be a ValueTuple type of appropriate arity."; // public const string ArgumentException_ValueTupleLastArgumentNotAValueTuple = "The TRest type argument of ValueTuple`8 must be a ValueTuple."; -- Gitee From bcf847d03f8940c40e090a15475893536de609c2 Mon Sep 17 00:00:00 2001 From: liuqihong <540762622@qq.com> Date: Thu, 23 Jun 2022 00:46:43 +0800 Subject: [PATCH 266/675] =?UTF-8?q?=E5=A2=9E=E5=8A=A0=E7=9B=AE=E5=BD=95?= =?UTF-8?q?=E5=AD=98=E6=94=BE=E7=BA=A2=E9=BB=91=E6=A0=91?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/IFoxCAD.Cad/IFoxCAD.Cad.csproj | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/IFoxCAD.Cad/IFoxCAD.Cad.csproj b/src/IFoxCAD.Cad/IFoxCAD.Cad.csproj index 3063300..d0d9638 100644 --- a/src/IFoxCAD.Cad/IFoxCAD.Cad.csproj +++ b/src/IFoxCAD.Cad/IFoxCAD.Cad.csproj @@ -41,6 +41,9 @@ runtime + + 4.3.0 + -- Gitee From 5081ae2c49714260566ac6b6abfdbc5ed4344481 Mon Sep 17 00:00:00 2001 From: liuqihong <540762622@qq.com> Date: Thu, 23 Jun 2022 00:47:52 +0800 Subject: [PATCH 267/675] =?UTF-8?q?=E5=9B=9B=E5=8F=89=E6=A0=91=E7=9A=84?= =?UTF-8?q?=E7=82=B9=E6=94=B9=E4=B8=BA=E7=BA=A2=E9=BB=91=E6=A0=91=E5=AD=98?= =?UTF-8?q?=E6=94=BE?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Algorithms/QuadTree/QuadTree.cs | 94 +++++++++++++------ .../Algorithms/QuadTree/QuadTreeNode.cs | 58 ++++++------ src/IFoxCAD.Cad/ExtensionMethod/Tools.cs | 2 +- tests/Test/TestPoint.cs | 37 +++++++- tests/Test/TestQuadTree.cs | 20 +++- 5 files changed, 149 insertions(+), 62 deletions(-) diff --git a/src/IFoxCAD.Cad/Algorithms/QuadTree/QuadTree.cs b/src/IFoxCAD.Cad/Algorithms/QuadTree/QuadTree.cs index 6bef72c..ed315f4 100644 --- a/src/IFoxCAD.Cad/Algorithms/QuadTree/QuadTree.cs +++ b/src/IFoxCAD.Cad/Algorithms/QuadTree/QuadTree.cs @@ -23,6 +23,35 @@ */ namespace IFoxCAD.Cad { + class EntityPoint: IComparable> + { + public double _X; + public double _Y; + public TEntity Entity;/*这样岂不是红黑树不允许共点的点?*/ + + public EntityPoint(double x, double y, TEntity entity) + { + _X = x; + _Y = y; + Entity = entity; + } + + public int CompareTo(EntityPoint pt) + { + if (pt == null) + return -1; + if (_X < pt._X) + return -1; + else if (_X > pt._X) + return 1; + else if (_Y < pt._Y)/*x是一样的*/ + return -1; + else if (_Y > pt._Y) + return 1; + return 0;/*全部一样*/ + } + } + /// /// 根节点控制器 /// @@ -39,6 +68,11 @@ public class QuadTree where TEntity : QuadEntity /// 四叉树节点的数目 /// public int Count { get => _rootNode.CountSubTree; } + + /// + /// 点容器(红黑树) + /// + SortedSet> _points; #endregion #region 构造 @@ -46,10 +80,10 @@ public class QuadTree where TEntity : QuadEntity /// 四叉树根节点控制器 /// /// 四叉树矩形范围 - /// 最后一个节点有最小面积 public QuadTree(Rect rect) { _rootNode = new QuadTreeNode(rect, null, 0);//初始化根节点 + _points = new(); } #endregion @@ -60,6 +94,15 @@ public QuadTree(Rect rect) /// public void Insert(TEntity ent) { + /* + * 图元点 是不分裂空间的,加入一个红黑树内部. + */ + if (ent.IsPoint) + { + _points.Add(new(ent._X, ent._Y, ent)); + return; + } + while (!_rootNode.Contains(ent)) { /* @@ -138,8 +181,7 @@ public void Insert(TEntity ent) var depth = insert.Depth; if (depth == 0) throw new ArgumentNullException("插入节点是0,造成错误"); - _rootNode.ForEach(node => - { + _rootNode.ForEach(node => { node.Depth += depth; return false; }); @@ -151,31 +193,6 @@ public void Insert(TEntity ent) _rootNode.Insert(ent); } - /// - /// 通过根节点插入数据项 - /// - /// - public void Insert(List ents) - { - /* - * 图元点 是不分裂空间的,应该插入到目前对应的最小空间. - * 因此插入时候,先让其他图元插入时候分裂节点, - * 最后再把 图元点 们插入. - */ - var pointEntityNum = new List(); - for (int i = 0; i < ents.Count; i++) - { - var ent = ents[i]; - if (ent.IsPoint) - { - pointEntityNum.Add(i); - continue; - } - Insert(ent); - } - for (int i = 0; i < pointEntityNum.Count; i++) - Insert(ents[pointEntityNum[i]]); - } /// /// 查询四叉树,返回给定区域的数据项 @@ -185,8 +202,29 @@ public void Insert(List ents) public List Query(Rect rect, QuadTreeSelectMode selectMode = QuadTreeSelectMode.IntersectsWith) { QuadTreeEvn.SelectMode = selectMode; + var results = new List(); + + //选择图元 _rootNode.Query(rect, results); + + //选择点 + var ptge = _points.GetEnumerator(); + switch (selectMode) + { + case QuadTreeSelectMode.IntersectsWith: + case QuadTreeSelectMode.Contains: + while (ptge.MoveNext()) + { + var cur = ptge.Current; + if (rect._X <= cur._X && cur._X <= rect._Right && + rect._Y <= cur._Y && cur._Y <= rect.Top) + results.Add(cur.Entity); + } + break; + default: + throw new ArgumentException(null, nameof(selectMode)); + } return results; } diff --git a/src/IFoxCAD.Cad/Algorithms/QuadTree/QuadTreeNode.cs b/src/IFoxCAD.Cad/Algorithms/QuadTree/QuadTreeNode.cs index 0c0cd5b..252101d 100644 --- a/src/IFoxCAD.Cad/Algorithms/QuadTree/QuadTreeNode.cs +++ b/src/IFoxCAD.Cad/Algorithms/QuadTree/QuadTreeNode.cs @@ -169,14 +169,14 @@ public QuadTreeNode(Rect box, QuadTreeNode? parent, int depth) return null; } - if (ent.IsPoint) - { - //找到最后一层包含它的节点,然后加入它 - //因此是跳过分裂矩形的,以免造成无限递归 - var minNode = GetMinNode(ent); - minNode.Contents.Add(ent); - return minNode; - } + // if (ent.IsPoint) + // { + // //找到最后一层包含它的节点,然后加入它 + // //因此是跳过分裂矩形的,以免造成无限递归 + // var minNode = GetMinNode(ent); + // minNode.Contents.Add(ent); + // return minNode; + // } #if true2 //方案二: @@ -772,27 +772,27 @@ QuadTreeNode GetMinNode(Rect queryArea) /// /// 所有的点归类到最小包围它的空间 /// - public void PointsToMinNode() - { - ForEach(node => - { - for (int i = 0; i < node.Contents.Count; i++) - { - var ent = node.Contents[i]; - if (ent.IsPoint) - { - //如果最小包含!=当前,就是没有放在最适合的位置 - var queryNode = GetMinNode(ent); - if (queryNode != node) - { - node.Remove(ent); - queryNode.Contents.Add(ent); - } - } - } - return false; - }); - } + //public void PointsToMinNode() + //{ + // ForEach(node => + // { + // for (int i = 0; i < node.Contents.Count; i++) + // { + // var ent = node.Contents[i]; + // if (ent.IsPoint) + // { + // //如果最小包含!=当前,就是没有放在最适合的位置 + // var queryNode = GetMinNode(ent); + // if (queryNode != node) + // { + // node.Remove(ent); + // queryNode.Contents.Add(ent); + // } + // } + // } + // return false; + // }); + //} #endregion #region 方法 diff --git a/src/IFoxCAD.Cad/ExtensionMethod/Tools.cs b/src/IFoxCAD.Cad/ExtensionMethod/Tools.cs index 26c8b6d..418f5fe 100644 --- a/src/IFoxCAD.Cad/ExtensionMethod/Tools.cs +++ b/src/IFoxCAD.Cad/ExtensionMethod/Tools.cs @@ -24,7 +24,7 @@ public static void TestTimes2(int count, string message, Action action) /// 纳秒计时器 /// public static void TestTimes(int count, string message, Action action, - Timer.TimeEnum timeEnum = Timer.TimeEnum.Nanosecond) + Timer.TimeEnum timeEnum = Timer.TimeEnum.Millisecond) { double time = Timer.RunTime(() => { for (int i = 0; i < count; i++) diff --git a/tests/Test/TestPoint.cs b/tests/Test/TestPoint.cs index 7592db9..46efe2e 100644 --- a/tests/Test/TestPoint.cs +++ b/tests/Test/TestPoint.cs @@ -1,7 +1,42 @@ -namespace Test +using System.Diagnostics; + +namespace Test { public class TestPoint { + /// + /// 红黑树排序点集 + /// + [CommandMethod("TestptSortedSet")] + public void TestptSortedSet() + { + var ss1 = new SortedSet(); + ss1.Add(new Point2d(1, 1)); + ss1.Add(new Point2d(4.6, 2)); + ss1.Add(new Point2d(8, 3)); + ss1.Add(new Point2d(4, 3)); + ss1.Add(new Point2d(5, 40)); + ss1.Add(new Point2d(6, 5)); + ss1.Add(new Point2d(1, 6)); + ss1.Add(new Point2d(7, 6)); + ss1.Add(new Point2d(9, 6)); + + /*判断区间,超过就中断*/ + foreach (var item in ss1) + { + if (item.X > 3 && item.X < 7) + { + Debug.WriteLine(item); + } + else if (item.X >= 7) + { + break; + } + } + } + + + [CommandMethod("TestptGethash")] public void TestptGethash() { diff --git a/tests/Test/TestQuadTree.cs b/tests/Test/TestQuadTree.cs index b15733f..fdf9268 100644 --- a/tests/Test/TestQuadTree.cs +++ b/tests/Test/TestQuadTree.cs @@ -28,7 +28,7 @@ public override int GetHashCode() } } -public partial class TestQuadTree +public partial class TestQuadTree { QuadTree _quadTreeRoot; @@ -69,7 +69,9 @@ public void Test_QuadTree() }; tr.CurrentSpace.AddPline(databaseBoundary); - int maximumItems = 30_0000; //生成多少个图元,导致cad会令undo出错(八叉树深度过大 treemax) + //生成多少个图元,导致cad会令undo出错(八叉树深度过大 treemax) + //int maximumItems = 30_0000; + int maximumItems = 1000; //随机图元生成 List ces = new(); //用于随机获取图元 @@ -89,12 +91,24 @@ public void Test_QuadTree() Color = Utility.RandomColor }; ces.Add(ce); + /*加入随机点*/ + var p = edge.MinPoint + edge.MinPoint.GetAsVector(); + entRect = new Rect(p.Point2d(), p.Point2d()); + entId = tr.CurrentSpace.AddEntity(new DBPoint(p)); + var dbPointCe = new CadEntity(entId, entRect) + { + Color = Utility.RandomColor + }; + ces.Add(dbPointCe); } }, Timer.TimeEnum.Millisecond, "画圆消耗时间:");//30万图元±3秒.cad2021 //测试只加入四叉树的时间 Timer.RunTime(() => { - _quadTreeRoot.Insert(ces); + for (int i = 0; i < ces.Count; i++) + { + _quadTreeRoot.Insert(ces[i]); + } }, Timer.TimeEnum.Millisecond, "插入四叉树时间:");//30万图元±0.7秒.cad2021 tr.Editor.WriteMessage($"\n加入图元数量:{maximumItems}"); -- Gitee From cf03292714dca1d5db192a900dffab4ab3346a6c Mon Sep 17 00:00:00 2001 From: liuqihong <540762622@qq.com> Date: Thu, 23 Jun 2022 01:08:41 +0800 Subject: [PATCH 268/675] =?UTF-8?q?=E4=BF=AE=E6=94=B9=E7=BA=A2=E9=BB=91?= =?UTF-8?q?=E6=A0=91=E5=88=A4=E6=96=AD=E4=B8=AD=E6=96=AD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/IFoxCAD.Cad/Algorithms/QuadTree/QuadTree.cs | 12 ++++++++---- tests/Test/TestQuadTree.cs | 7 ++----- 2 files changed, 10 insertions(+), 9 deletions(-) diff --git a/src/IFoxCAD.Cad/Algorithms/QuadTree/QuadTree.cs b/src/IFoxCAD.Cad/Algorithms/QuadTree/QuadTree.cs index ed315f4..2b6c4a9 100644 --- a/src/IFoxCAD.Cad/Algorithms/QuadTree/QuadTree.cs +++ b/src/IFoxCAD.Cad/Algorithms/QuadTree/QuadTree.cs @@ -216,10 +216,14 @@ public List Query(Rect rect, QuadTreeSelectMode selectMode = QuadTreeSe case QuadTreeSelectMode.Contains: while (ptge.MoveNext()) { - var cur = ptge.Current; - if (rect._X <= cur._X && cur._X <= rect._Right && - rect._Y <= cur._Y && cur._Y <= rect.Top) - results.Add(cur.Entity); + var ptEnt = ptge.Current; + if (rect._X <= ptEnt._X && ptEnt._X <= rect._Right) + { + if (rect._Y <= ptEnt._Y && ptEnt._Y <= rect.Top) + results.Add(ptEnt.Entity); + } + else if (ptEnt._X > rect._Right) + break;//超过后面范围就break,因为红黑树已经排序 } break; default: diff --git a/tests/Test/TestQuadTree.cs b/tests/Test/TestQuadTree.cs index fdf9268..78ae391 100644 --- a/tests/Test/TestQuadTree.cs +++ b/tests/Test/TestQuadTree.cs @@ -92,13 +92,10 @@ public void Test_QuadTree() }; ces.Add(ce); /*加入随机点*/ - var p = edge.MinPoint + edge.MinPoint.GetAsVector(); + var p = edge.MinPoint + new Vector3d(10, 10, 0); entRect = new Rect(p.Point2d(), p.Point2d()); entId = tr.CurrentSpace.AddEntity(new DBPoint(p)); - var dbPointCe = new CadEntity(entId, entRect) - { - Color = Utility.RandomColor - }; + var dbPointCe = new CadEntity(entId, entRect); ces.Add(dbPointCe); } }, Timer.TimeEnum.Millisecond, "画圆消耗时间:");//30万图元±3秒.cad2021 -- Gitee From e85599c0851052a69dfe6419cb65653dcc4263f7 Mon Sep 17 00:00:00 2001 From: liuqihong <540762622@qq.com> Date: Thu, 23 Jun 2022 01:15:58 +0800 Subject: [PATCH 269/675] =?UTF-8?q?=E4=BC=98=E5=8C=96=E6=AF=94=E8=BE=83?= =?UTF-8?q?=E7=BB=93=E6=9E=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Algorithms/QuadTree/QuadTree.cs | 35 ++----------------- src/IFoxCAD.Cad/Algorithms/QuadTree/Rect.cs | 20 +++++++++-- 2 files changed, 21 insertions(+), 34 deletions(-) diff --git a/src/IFoxCAD.Cad/Algorithms/QuadTree/QuadTree.cs b/src/IFoxCAD.Cad/Algorithms/QuadTree/QuadTree.cs index 2b6c4a9..9932909 100644 --- a/src/IFoxCAD.Cad/Algorithms/QuadTree/QuadTree.cs +++ b/src/IFoxCAD.Cad/Algorithms/QuadTree/QuadTree.cs @@ -23,35 +23,6 @@ */ namespace IFoxCAD.Cad { - class EntityPoint: IComparable> - { - public double _X; - public double _Y; - public TEntity Entity;/*这样岂不是红黑树不允许共点的点?*/ - - public EntityPoint(double x, double y, TEntity entity) - { - _X = x; - _Y = y; - Entity = entity; - } - - public int CompareTo(EntityPoint pt) - { - if (pt == null) - return -1; - if (_X < pt._X) - return -1; - else if (_X > pt._X) - return 1; - else if (_Y < pt._Y)/*x是一样的*/ - return -1; - else if (_Y > pt._Y) - return 1; - return 0;/*全部一样*/ - } - } - /// /// 根节点控制器 /// @@ -72,7 +43,7 @@ public class QuadTree where TEntity : QuadEntity /// /// 点容器(红黑树) /// - SortedSet> _points; + SortedSet _points; #endregion #region 构造 @@ -99,7 +70,7 @@ public void Insert(TEntity ent) */ if (ent.IsPoint) { - _points.Add(new(ent._X, ent._Y, ent)); + _points.Add(ent); return; } @@ -220,7 +191,7 @@ public List Query(Rect rect, QuadTreeSelectMode selectMode = QuadTreeSe if (rect._X <= ptEnt._X && ptEnt._X <= rect._Right) { if (rect._Y <= ptEnt._Y && ptEnt._Y <= rect.Top) - results.Add(ptEnt.Entity); + results.Add(ptEnt); } else if (ptEnt._X > rect._Right) break;//超过后面范围就break,因为红黑树已经排序 diff --git a/src/IFoxCAD.Cad/Algorithms/QuadTree/Rect.cs b/src/IFoxCAD.Cad/Algorithms/QuadTree/Rect.cs index 29c70b9..3488ca5 100644 --- a/src/IFoxCAD.Cad/Algorithms/QuadTree/Rect.cs +++ b/src/IFoxCAD.Cad/Algorithms/QuadTree/Rect.cs @@ -42,7 +42,7 @@ public int GetHashCode(Point2d obj) [StructLayout(LayoutKind.Sequential)] [DebuggerDisplay("{DebuggerDisplay,nq}")] [DebuggerTypeProxy(typeof(Rect))] -public class Rect : IEquatable +public class Rect : IEquatable, IComparable { [DebuggerBrowsable(DebuggerBrowsableState.Never)] private string DebuggerDisplay => ToString("f4"); @@ -247,7 +247,7 @@ public bool Contains(double x, double y) public bool Contains(Rect rect) { return _X <= rect._X && rect._Right <= _Right && - _Y <= rect._Y && rect._Top <= _Top; + _Y <= rect._Y && rect._Top <= _Top; } /// @@ -581,6 +581,22 @@ public string ToString(string? format = null, IFormatProvider? formatProvider = // $"Right={_Right.ToString(format, formatProvider)}," + // $"Top={_Top.ToString(format, formatProvider)}"; } + + /*为了红黑树,加入这个*/ + public int CompareTo(Rect rect) + { + if (rect == null) + return -1; + if (_X < rect._X) + return -1; + else if (_X > rect._X) + return 1; + else if (_Y < rect._Y)/*x是一样的*/ + return -1; + else if (_Y > rect._Y) + return 1; + return 0;/*全部一样*/ + } #endregion #endregion -- Gitee From 9df735b4d213834c2b409bbe3eb20d481cca3078 Mon Sep 17 00:00:00 2001 From: liuqihong <540762622@qq.com> Date: Sat, 25 Jun 2022 01:26:00 +0800 Subject: [PATCH 270/675] =?UTF-8?q?=E4=BF=AE=E6=94=B9=E6=97=B6=E9=97=B4?= =?UTF-8?q?=E6=B5=8B=E8=AF=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/IFoxCAD.Cad/ExtensionMethod/Tools.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/IFoxCAD.Cad/ExtensionMethod/Tools.cs b/src/IFoxCAD.Cad/ExtensionMethod/Tools.cs index 418f5fe..0bff2cf 100644 --- a/src/IFoxCAD.Cad/ExtensionMethod/Tools.cs +++ b/src/IFoxCAD.Cad/ExtensionMethod/Tools.cs @@ -140,7 +140,7 @@ public void Stop() public double Microsecond => _Second * 1000000.0; public double Nanosecond => _Second * 1000000000.0; - public static double RunTime(Action action, TimeEnum timeEnum = TimeEnum.Nanosecond, string? msg = null) + public static double RunTime(Action action, TimeEnum timeEnum = TimeEnum.Millisecond, string? msg = null) { var nanoSecond = new Timer(); nanoSecond.Start(); -- Gitee From 6a0ccba759377ef3807eb5364e53282b7e8d0184 Mon Sep 17 00:00:00 2001 From: liuqihong <540762622@qq.com> Date: Sat, 25 Jun 2022 01:29:47 +0800 Subject: [PATCH 271/675] =?UTF-8?q?=E5=9B=9B=E5=8F=89=E6=A0=91=E5=8A=A0?= =?UTF-8?q?=E5=85=A5=E7=82=B9=E7=B1=BB=E5=9E=8B=E6=B3=A8=E9=87=8A?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/IFoxCAD.Cad/Algorithms/QuadTree/QuadTree.cs | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/src/IFoxCAD.Cad/Algorithms/QuadTree/QuadTree.cs b/src/IFoxCAD.Cad/Algorithms/QuadTree/QuadTree.cs index 9932909..f6de66b 100644 --- a/src/IFoxCAD.Cad/Algorithms/QuadTree/QuadTree.cs +++ b/src/IFoxCAD.Cad/Algorithms/QuadTree/QuadTree.cs @@ -175,16 +175,20 @@ public List Query(Rect rect, QuadTreeSelectMode selectMode = QuadTreeSe QuadTreeEvn.SelectMode = selectMode; var results = new List(); - //选择图元 _rootNode.Query(rect, results); - //选择点 var ptge = _points.GetEnumerator(); switch (selectMode) { case QuadTreeSelectMode.IntersectsWith: case QuadTreeSelectMode.Contains: + /* 由于红黑树的方法 _points.GetViewBetween() + * 过滤只能过滤X区间,Y区间还是要过滤, + * 那么我就只能用这样的方法加速了 + * + * 而更好的方式是不用红黑树,去加入一个点云数据来进行,可谓是编程无极限.... + */ while (ptge.MoveNext()) { var ptEnt = ptge.Current; @@ -198,7 +202,7 @@ public List Query(Rect rect, QuadTreeSelectMode selectMode = QuadTreeSe } break; default: - throw new ArgumentException(null, nameof(selectMode)); + throw new(nameof(selectMode)); } return results; } -- Gitee From 2e0148dd9b68a95219c7e9f1f797ed7dd8c97ee6 Mon Sep 17 00:00:00 2001 From: liuqihong <540762622@qq.com> Date: Sat, 25 Jun 2022 01:55:32 +0800 Subject: [PATCH 272/675] =?UTF-8?q?=E4=BF=AE=E5=A4=8D=E8=AD=A6=E5=91=8A?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/IFoxCAD.Basal/Sortedset/SR.cs | 7 +++++- src/IFoxCAD.Basal/Sortedset/Sortedset.cs | 24 ++++++++++++++++++- src/IFoxCAD.Basal/Sortedset/ThrowHelper.cs | 6 ++++- src/IFoxCAD.Basal/Sortedset/bithelper.cs | 10 +++++++- .../Algorithms/QuadTree/QuadTree.cs | 10 ++++---- tests/Test/TestQuadTree.cs | 7 +++++- 6 files changed, 54 insertions(+), 10 deletions(-) diff --git a/src/IFoxCAD.Basal/Sortedset/SR.cs b/src/IFoxCAD.Basal/Sortedset/SR.cs index 391d9b7..093de14 100644 --- a/src/IFoxCAD.Basal/Sortedset/SR.cs +++ b/src/IFoxCAD.Basal/Sortedset/SR.cs @@ -1,4 +1,5 @@ #if NET35 +#pragma warning disable CS8603 // 可能返回 null 引用。 namespace System; @@ -82,7 +83,9 @@ public SRDisplayNameAttribute(string name, string resourceSet) /// internal sealed partial class SR { +#pragma warning disable CS8625 // 无法将 null 字面量转换为非 null 的引用类型。 static SR loader = null; +#pragma warning restore CS8625 // 无法将 null 字面量转换为非 null 的引用类型。 ResourceManager resources; internal SR() @@ -898,5 +901,7 @@ internal static string GetString(CultureInfo culture, string name) internal const string Error_InvalidElementFoundForType = "Error_InvalidElementFoundForType"; internal const string Error_ActivitySaveLoadNotCalled = "Error_ActivitySaveLoadNotCalled"; internal const string Error_CanNotBindProperty = "Error_CanNotBindProperty"; -} +} +#pragma warning restore CS8603 // 可能返回 null 引用。 + #endif \ No newline at end of file diff --git a/src/IFoxCAD.Basal/Sortedset/Sortedset.cs b/src/IFoxCAD.Basal/Sortedset/Sortedset.cs index fce371a..9405fc1 100644 --- a/src/IFoxCAD.Basal/Sortedset/Sortedset.cs +++ b/src/IFoxCAD.Basal/Sortedset/Sortedset.cs @@ -1,4 +1,12 @@ #if NET35 +#pragma warning disable CS8603 // 可能返回 null 引用。 +#pragma warning disable CS8601 // 引用类型赋值可能为 null。 +#pragma warning disable CS8618 // 在退出构造函数时,不可为 null 的字段必须包含非 null 值。请考虑声明为可以为 null。 +#pragma warning disable CS8625 // 无法将 null 字面量转换为非 null 的引用类型。 +#pragma warning disable IDE0059 // 不需要赋值 +#pragma warning disable CS8600 // 将 null 字面量或可能为 null 的值转换为非 null 类型。 +#pragma warning disable CS8602 // 解引用可能出现空引用。 +#pragma warning disable CS8604 // 引用类型参数可能为 null。 // #define USING_HASH_SET // ==++== // @@ -199,6 +207,7 @@ public SortedSet(IEnumerable collection, IComparer comparer) #if !FEATURE_NETCORE + protected SortedSet(SerializationInfo info, StreamingContext context) { siInfo = info; @@ -2662,6 +2671,7 @@ private void OnDeserialization(Object sender) private void Intialize() { + current = null; SortedSet.Node node = tree.root; Node next = null, other = null; @@ -2705,6 +2715,7 @@ public bool MoveNext() current = stack.Pop(); SortedSet.Node node = (reverse ? current.Left : current.Right); + Node next = null, other = null; while (node != null) { @@ -2909,5 +2920,16 @@ public override int GetHashCode() } -} +} + + + +#pragma warning restore CS8604 // 引用类型参数可能为 null。 +#pragma warning restore CS8602 // 解引用可能出现空引用。 +#pragma warning restore CS8600 // 将 null 字面量或可能为 null 的值转换为非 null 类型。 +#pragma warning restore IDE0059 // 不需要赋值 +#pragma warning restore CS8625 // 无法将 null 字面量转换为非 null 的引用类型。 +#pragma warning restore CS8618 // 在退出构造函数时,不可为 null 的字段必须包含非 null 值。请考虑声明为可以为 null。 +#pragma warning restore CS8601 // 引用类型赋值可能为 null。 +#pragma warning restore CS8603 // 可能返回 null 引用。 #endif \ No newline at end of file diff --git a/src/IFoxCAD.Basal/Sortedset/ThrowHelper.cs b/src/IFoxCAD.Basal/Sortedset/ThrowHelper.cs index 01ed264..205dd1c 100644 --- a/src/IFoxCAD.Basal/Sortedset/ThrowHelper.cs +++ b/src/IFoxCAD.Basal/Sortedset/ThrowHelper.cs @@ -1,4 +1,7 @@ #if NET35 +#pragma warning disable IDE0059 // 不需要赋值 +#pragma warning disable CS8600 // 将 null 字面量或可能为 null 的值转换为非 null 类型。 + namespace System { // This file defines an internal class used to throw exceptions in BCL code. @@ -374,5 +377,6 @@ internal enum ExceptionResource } } - +#pragma warning restore CS8600 // 将 null 字面量或可能为 null 的值转换为非 null 类型。 +#pragma warning restore IDE0059 // 不需要赋值 #endif \ No newline at end of file diff --git a/src/IFoxCAD.Basal/Sortedset/bithelper.cs b/src/IFoxCAD.Basal/Sortedset/bithelper.cs index f5edff8..d68c4d8 100644 --- a/src/IFoxCAD.Basal/Sortedset/bithelper.cs +++ b/src/IFoxCAD.Basal/Sortedset/bithelper.cs @@ -1,4 +1,8 @@ #if NET35 +#pragma warning disable CS8603 // 可能返回 null 引用。 +#pragma warning disable CS8618 // 在退出构造函数时,不可为 null 的字段必须包含非 null 值。请考虑声明为可以为 null。 + + using System; using System.Collections; using System.Text; @@ -161,5 +165,9 @@ internal static int ToIntArrayLength(int n) } } -} +} + +#pragma warning restore CS8618 // 在退出构造函数时,不可为 null 的字段必须包含非 null 值。请考虑声明为可以为 null。 +#pragma warning restore CS8603 // 可能返回 null 引用。 + #endif \ No newline at end of file diff --git a/src/IFoxCAD.Cad/Algorithms/QuadTree/QuadTree.cs b/src/IFoxCAD.Cad/Algorithms/QuadTree/QuadTree.cs index f6de66b..e808777 100644 --- a/src/IFoxCAD.Cad/Algorithms/QuadTree/QuadTree.cs +++ b/src/IFoxCAD.Cad/Algorithms/QuadTree/QuadTree.cs @@ -127,9 +127,9 @@ public void Insert(TEntity ent) var newRoot = new QuadTreeNode(rectSquare, null, 0); var insert = newRoot.Insert(_rootNode); if (insert is null) - throw new ArgumentException("新根尺寸不对"); + throw new("四叉树:新根尺寸不对"); if (!insert.Equals(_rootNode)) - throw new ArgumentNullException("新旧节点大小不一致,无法连接"); + throw new("四叉树:新旧节点大小不一致,无法连接"); var insPar = insert.Parent; _rootNode.Parent = insPar; @@ -145,13 +145,13 @@ public void Insert(TEntity ent) else if (_rootNode.Equals(insPar.LeftTopTree)) insPar.LeftTopTree = _rootNode; else - throw new ArgumentNullException("新节点不对,无法连接"); + throw new("四叉树:新节点不对,无法连接"); //其后的子节点层数全部增加层数, //要加多少层取决于当前根边界属于新根边界的所在层 var depth = insert.Depth; if (depth == 0) - throw new ArgumentNullException("插入节点是0,造成错误"); + throw new("四叉树:插入节点是0,造成错误"); _rootNode.ForEach(node => { node.Depth += depth; return false; @@ -202,7 +202,7 @@ public List Query(Rect rect, QuadTreeSelectMode selectMode = QuadTreeSe } break; default: - throw new(nameof(selectMode)); + throw new("四叉树:" + nameof(selectMode)); } return results; } diff --git a/tests/Test/TestQuadTree.cs b/tests/Test/TestQuadTree.cs index 78ae391..8c07420 100644 --- a/tests/Test/TestQuadTree.cs +++ b/tests/Test/TestQuadTree.cs @@ -1,5 +1,6 @@ namespace Test; +#pragma warning disable CS8632 // 只能在 "#nullable" 注释上下文内的代码中使用可为 null 的引用类型的注释。 /* * 这里属于用户调用例子, * 调用时候必须要继承它,再提供给四叉树 @@ -27,6 +28,7 @@ public override int GetHashCode() return (base.GetHashCode(), ObjectId.GetHashCode()).GetHashCode(); } } +#pragma warning restore CS8632 // 只能在 "#nullable" 注释上下文内的代码中使用可为 null 的引用类型的注释。 public partial class TestQuadTree { @@ -367,6 +369,7 @@ void Ssget(QuadTreeSelectMode mode) /// /// /// +#pragma warning disable CS8632 // 只能在 "#nullable" 注释上下文内的代码中使用可为 null 的引用类型的注释。 public static Rect? GetCorner(Editor ed) { var optionsA = new PromptPointOptions($"{Environment.NewLine}起点位置:"); @@ -380,12 +383,14 @@ void Ssget(QuadTreeSelectMode mode) }; var pprB = ed.GetCorner(optionsB); if (pprB.Status != PromptStatus.OK) - return null; + return null!; return new Rect(new Point2d(pprA.Value.X, pprA.Value.Y), new Point2d(pprB.Value.X, pprB.Value.Y), true); } +#pragma warning restore CS8632 // 只能在 "#nullable" 注释上下文内的代码中使用可为 null 的引用类型的注释。 + #endregion } -- Gitee From 114f4cd80f1d8054b227c57972ba548898f36408 Mon Sep 17 00:00:00 2001 From: liuqihong <540762622@qq.com> Date: Sat, 25 Jun 2022 02:03:42 +0800 Subject: [PATCH 273/675] =?UTF-8?q?=E5=9B=9B=E5=8F=89=E6=A0=91=E7=9A=84?= =?UTF-8?q?=E9=87=8D=E8=A6=81=E6=B3=A8=E9=87=8A?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- tests/Test/TestQuadTree.cs | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/tests/Test/TestQuadTree.cs b/tests/Test/TestQuadTree.cs index 8c07420..557fe6a 100644 --- a/tests/Test/TestQuadTree.cs +++ b/tests/Test/TestQuadTree.cs @@ -47,8 +47,11 @@ public void Test_QuadTree() { //throw new ArgumentException("画一个矩形"); - //测试时候画个矩形,在矩形内画随机坐标的圆形 - dbExt = new Rect(0, 0, 32525, 32525); + //这个初始值的矩形是很有意义, + //它可以把所有的扩展都弄成2次幂,尤其是Rect类型是long,直接可以位移膨胀 + //但是因为啊惊懒的原因,并没有单独制作这样的矩形,而且非常糟糕的是,c#不支持值类型作为泛型传参. + //还是换c++去搞四叉树吧 + dbExt = new Rect(0, 0, 1 << 10, 1 << 10); } else { -- Gitee From 7be88f7ca7741a68ce2cc7ac42acf6524769c97c Mon Sep 17 00:00:00 2001 From: liuqihong <540762622@qq.com> Date: Sat, 25 Jun 2022 02:09:54 +0800 Subject: [PATCH 274/675] =?UTF-8?q?=E4=BF=AE=E6=94=B9=E6=B3=A8=E9=87=8A?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- tests/Test/TestQuadTree.cs | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/tests/Test/TestQuadTree.cs b/tests/Test/TestQuadTree.cs index 557fe6a..926fbfc 100644 --- a/tests/Test/TestQuadTree.cs +++ b/tests/Test/TestQuadTree.cs @@ -48,9 +48,12 @@ public void Test_QuadTree() //throw new ArgumentException("画一个矩形"); //这个初始值的矩形是很有意义, - //它可以把所有的扩展都弄成2次幂,尤其是Rect类型是long,直接可以位移膨胀 + //主要是Rect有记录重复的double值,是否可以内存复用,以此减少内存大小? + //接着想了一下,Rect可以是int,long,这样可以利用位运算它扩展和缩小, + //最小就是1,并且可以控制四叉树深度,不至于无限递归. + //而且指针长度跟值是一样的,所以就不需要复用了,毕竟跳转一个函数地址挺麻烦的. //但是因为啊惊懒的原因,并没有单独制作这样的矩形,而且非常糟糕的是,c#不支持值类型作为泛型传参. - //还是换c++去搞四叉树吧 + //要么忍着,要么换c++去搞四叉树吧 dbExt = new Rect(0, 0, 1 << 10, 1 << 10); } else -- Gitee From 57081fedbb9243660e3c2522f8df96342a25ac1b Mon Sep 17 00:00:00 2001 From: liuqihong <540762622@qq.com> Date: Sat, 25 Jun 2022 02:17:37 +0800 Subject: [PATCH 275/675] =?UTF-8?q?=E4=BF=AE=E6=94=B9=E6=B3=A8=E9=87=8A2?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- tests/Test/TestQuadTree.cs | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/tests/Test/TestQuadTree.cs b/tests/Test/TestQuadTree.cs index 926fbfc..2b75df4 100644 --- a/tests/Test/TestQuadTree.cs +++ b/tests/Test/TestQuadTree.cs @@ -30,10 +30,13 @@ public override int GetHashCode() } #pragma warning restore CS8632 // 只能在 "#nullable" 注释上下文内的代码中使用可为 null 的引用类型的注释。 + + + + public partial class TestQuadTree { QuadTree _quadTreeRoot; - #region 四叉树创建并加入 [CommandMethod("Test_QuadTree")] public void Test_QuadTree() @@ -52,7 +55,8 @@ public void Test_QuadTree() //接着想了一下,Rect可以是int,long,这样可以利用位运算它扩展和缩小, //最小就是1,并且可以控制四叉树深度,不至于无限递归. //而且指针长度跟值是一样的,所以就不需要复用了,毕竟跳转一个函数地址挺麻烦的. - //但是因为啊惊懒的原因,并没有单独制作这样的矩形,而且非常糟糕的是,c#不支持值类型作为泛型传参. + //但是因为啊惊懒的原因,并没有单独制作这样的矩形, + //而且非常糟糕的是,c#不支持模板约束运算符,使得值类型之间需要通过一层接口来委婉处理,拉低了效率..引用类型倒是无所谓.. //要么忍着,要么换c++去搞四叉树吧 dbExt = new Rect(0, 0, 1 << 10, 1 << 10); } -- Gitee From 5dba4043b1150099bc63693dac9169b6fab745cb Mon Sep 17 00:00:00 2001 From: liuqihong <540762622@qq.com> Date: Sat, 25 Jun 2022 02:19:19 +0800 Subject: [PATCH 276/675] =?UTF-8?q?=E4=BF=AE=E6=94=B9=E6=B3=A8=E9=87=8A3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- tests/Test/TestQuadTree.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/Test/TestQuadTree.cs b/tests/Test/TestQuadTree.cs index 2b75df4..7670eae 100644 --- a/tests/Test/TestQuadTree.cs +++ b/tests/Test/TestQuadTree.cs @@ -51,7 +51,7 @@ public void Test_QuadTree() //throw new ArgumentException("画一个矩形"); //这个初始值的矩形是很有意义, - //主要是Rect有记录重复的double值,是否可以内存复用,以此减少内存大小? + //主要是四叉树分裂过程中产生多个Rect,Rect内有很多重复的double值,是否可以内存复用,以此减少内存大小? //接着想了一下,Rect可以是int,long,这样可以利用位运算它扩展和缩小, //最小就是1,并且可以控制四叉树深度,不至于无限递归. //而且指针长度跟值是一样的,所以就不需要复用了,毕竟跳转一个函数地址挺麻烦的. -- Gitee From 5a8128d4e9bcb09a546c212c22e3579ef8766850 Mon Sep 17 00:00:00 2001 From: DYH <1742647821@qq.com> Date: Tue, 28 Jun 2022 01:47:49 +0000 Subject: [PATCH 277/675] =?UTF-8?q?update=20src/IFoxCAD.Cad/ExtensionMetho?= =?UTF-8?q?d/GeometryEx.cs.=20=E5=A2=9E=E5=8A=A0=E8=8E=B7=E5=8F=96?= =?UTF-8?q?=E4=B8=A4=E4=B8=AA=E7=82=B9=E4=B8=AD=E7=82=B9=E7=9A=84point2d?= =?UTF-8?q?=E6=96=B9=E5=BC=8F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/IFoxCAD.Cad/ExtensionMethod/GeometryEx.cs | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/src/IFoxCAD.Cad/ExtensionMethod/GeometryEx.cs b/src/IFoxCAD.Cad/ExtensionMethod/GeometryEx.cs index 2c1a45a..e35c44f 100644 --- a/src/IFoxCAD.Cad/ExtensionMethod/GeometryEx.cs +++ b/src/IFoxCAD.Cad/ExtensionMethod/GeometryEx.cs @@ -629,6 +629,17 @@ public static Point3d GetMidPointTo(this Point3d pt1, Point3d pt2) { return new Point3d((pt1.X + pt2.X) * 0.5, (pt1.Y + pt2.Y) * 0.5, (pt1.Z + pt2.Z) * 0.5); } + + /// + /// 获取两个点之间的中点 + /// + /// 第一点 + /// 第二点 + /// 返回两个点之间的中点 + public static Point2d GetMidPointTo(this Point2d pt1, Point2d pt2) + { + return new Point2d((pt1.X + pt2.X) * 0.5, (pt1.Y + pt2.Y) * 0.5); + } /// /// 根据世界坐标计算用户坐标 -- Gitee From 547ed12f803bd3d04864ec38a6cde4d8bb96b6a1 Mon Sep 17 00:00:00 2001 From: zewen han Date: Tue, 28 Jun 2022 09:54:05 +0800 Subject: [PATCH 278/675] =?UTF-8?q?=E5=A2=9E=E5=8A=A0foreach=E6=96=B9?= =?UTF-8?q?=E6=B3=95=E7=9A=84=E5=8F=AF=E8=AF=BB=E5=86=99=E9=80=89=E9=A1=B9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/IFoxCAD.Cad/Runtime/SymbolTable.cs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/IFoxCAD.Cad/Runtime/SymbolTable.cs b/src/IFoxCAD.Cad/Runtime/SymbolTable.cs index 27dd4f9..b49557d 100644 --- a/src/IFoxCAD.Cad/Runtime/SymbolTable.cs +++ b/src/IFoxCAD.Cad/Runtime/SymbolTable.cs @@ -269,7 +269,7 @@ public ObjectId GetRecordFrom(SymbolTable table, string name, b { if (table is null) { - throw new ArgumentNullException(nameof(table),"对象为null"); + throw new ArgumentNullException(nameof(table), "对象为null"); } ObjectId rid = this[name]; @@ -309,13 +309,13 @@ internal ObjectId GetRecordFrom(Func> tabl /// 集合值的类型 /// 集合 /// 要运行的委托 - public void ForEach(Action action) + public void ForEach(Action action, OpenMode openMode = OpenMode.ForRead) { //GetRecords().ForEach(re => action(re)); - + foreach (var item in this) { - var record = GetRecord(item); + var record = GetRecord(item, openMode); if (record is not null) { action(record); -- Gitee From 46681e7a733aa640ad810af1a8d16b0ae7140bd9 Mon Sep 17 00:00:00 2001 From: liuqihong <540762622@qq.com> Date: Tue, 5 Jul 2022 18:40:03 +0800 Subject: [PATCH 279/675] =?UTF-8?q?=E4=BF=AE=E6=94=B9lisp=E6=8E=A5?= =?UTF-8?q?=E5=8F=A3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/IFoxCAD.Cad/ExtensionMethod/EditorEx.cs | 130 ++++++++++++++++++-- src/IFoxCAD.Cad/ExtensionMethod/Jig.cs | 2 +- 2 files changed, 118 insertions(+), 14 deletions(-) diff --git a/src/IFoxCAD.Cad/ExtensionMethod/EditorEx.cs b/src/IFoxCAD.Cad/ExtensionMethod/EditorEx.cs index ef01feb..6e058a8 100644 --- a/src/IFoxCAD.Cad/ExtensionMethod/EditorEx.cs +++ b/src/IFoxCAD.Cad/ExtensionMethod/EditorEx.cs @@ -1017,7 +1017,7 @@ public static PromptResult GetString(this Editor ed, string Message, string Defa #region 执行lisp -#if ac2009 +#if NET35 [System.Security.SuppressUnmanagedCodeSecurity] [DllImport("acad.exe", CharSet = CharSet.Unicode, CallingConvention = CallingConvention.Cdecl, EntryPoint = "?acedEvaluateLisp@@YAHPB_WAAPAUresbuf@@@Z")] private static extern int AcedEvaluateLisp(string lispLine, out IntPtr result); @@ -1025,6 +1025,12 @@ public static PromptResult GetString(this Editor ed, string Message, string Defa [DllImport("acad.exe", CallingConvention = CallingConvention.Cdecl, EntryPoint = "acedInvoke")] private static extern int AcedInvoke(IntPtr args, out IntPtr result); #else + /// + /// 高版本此接口不能使用lisp(command "xx"),但是可以直接在自动运行接口上 + /// + /// + /// + /// [System.Security.SuppressUnmanagedCodeSecurity] [DllImport("accore.dll", CharSet = CharSet.Unicode, CallingConvention = CallingConvention.Cdecl, EntryPoint = "?acedEvaluateLisp@@YAHPEB_WAEAPEAUresbuf@@@Z")] private static extern int AcedEvaluateLisp(string lispLine, out IntPtr result); @@ -1032,31 +1038,129 @@ public static PromptResult GetString(this Editor ed, string Message, string Defa [DllImport("accore.dll", CallingConvention = CallingConvention.Cdecl, EntryPoint = "acedInvoke")] private static extern int AcedInvoke(IntPtr args, out IntPtr result); #endif + +#pragma warning disable IDE0060 // 删除未使用的参数 /// /// 发送lisp语句字符串到cad执行 /// /// 编辑器对象 /// lisp语句 /// 缓冲结果,返回值 -#pragma warning disable IDE0060 // 删除未使用的参数 public static ResultBuffer? RunLisp(this Editor ed, string arg) { + /* + * bug: + * cad08调用成功,但是高版本调用时候没有运行成功,使得 !a 没有值 + * 调用方式:(command "CmdTest_RunLisp1") + * + * [CommandMethod("CmdTest_RunLisp")] + * public static void CmdTest_RunLisp() + * { + * var res = SendLisp.RunLisp("(setq a 10)"); + * } + * + * 解决方案: + * 0x01 用异步接口,但是这样是显式调用了: + * (setq thisdrawing (vla-get-activedocument (vlax-get-acad-object)))(vla-SendCommand thisdrawing "CmdTest_RunLisp1 ") + * 0x02 使用 Ads_queueexpr 接口 + */ _ = AcedEvaluateLisp(arg, out IntPtr rb); if (rb != IntPtr.Zero) - { - try - { - var rbb = DisposableWrapper.Create(typeof(ResultBuffer), rb, true) as ResultBuffer; - return rbb; - } - catch - { - return null; - } - } + return DisposableWrapper.Create(typeof(ResultBuffer), rb, true) as ResultBuffer; return null; } #pragma warning restore IDE0060 // 删除未使用的参数 + +#if NET35 + [DllImport("acad.exe", CharSet = CharSet.Auto, CallingConvention = CallingConvention.Cdecl, + EntryPoint = "ads_queueexpr")] + static extern int Ads_queueexpr(string strExpr); +#else + [DllImport("accore.dll", CharSet = CharSet.Auto, CallingConvention = CallingConvention.Cdecl, + EntryPoint = "ads_queueexpr")] + static extern int Ads_queueexpr(string strExpr); +#endif + + /// + /// 发送命令 + /// (用来发送 含有(command)的lisp的)自执行发送lisp都是异步 + /// + public static void SendString(this Editor ed, string str) + { + // 这个在08/12发送lisp不会出错,但是发送bo命令出错了. + // 设置 CommandFlags.Session 可以同步, + // 发送lisp是异步,在自动执行函数上面会异步 + _ = Ads_queueexpr(str + "\n"); + + // var dm = Application.DocumentManager; + // var doc = dm.MdiActiveDocument; + // doc.SendStringToExecute(str + "\n", false, false, false); + } #endregion } + + +#if __发送lisp接口测试 +public class TestSendLisp +{ + [CommandMethod("CmdTest_RunLisp1", CommandFlags.Modal)] + [CommandMethod("CmdTest_RunLisp2", CommandFlags.Transparent)] + [CommandMethod("CmdTest_RunLisp3", CommandFlags.UsePickSet)] + [CommandMethod("CmdTest_RunLisp4", CommandFlags.Redraw)] + [CommandMethod("CmdTest_RunLisp5", CommandFlags.NoPerspective)] + [CommandMethod("CmdTest_RunLisp6", CommandFlags.NoMultiple)] + [CommandMethod("CmdTest_RunLisp7", CommandFlags.NoTileMode)]/*不允许模型使用命令*/ + [CommandMethod("CmdTest_RunLisp8", CommandFlags.NoPaperSpace)]/*不允许布局使用命令*/ + [CommandMethod("CmdTest_RunLisp9", CommandFlags.NoOem)] + [CommandMethod("CmdTest_RunLisp10", CommandFlags.Undefined)]/*不允许调用*/ + [CommandMethod("CmdTest_RunLisp11", CommandFlags.Defun)]/*不允许调用*/ + [CommandMethod("CmdTest_RunLisp12", CommandFlags.NoNewStack)] + [CommandMethod("CmdTest_RunLisp13", CommandFlags.NoInternalLock)] + [CommandMethod("CmdTest_RunLisp14", CommandFlags.DocReadLock)] + [CommandMethod("CmdTest_RunLisp15", CommandFlags.DocExclusiveLock)] + [CommandMethod("CmdTest_RunLisp16", CommandFlags.Session)] + [CommandMethod("CmdTest_RunLisp17", CommandFlags.Interruptible)] + [CommandMethod("CmdTest_RunLisp18", CommandFlags.NoHistory)] + [CommandMethod("CmdTest_RunLisp19", CommandFlags.NoUndoMarker)] + [CommandMethod("CmdTest_RunLisp20", CommandFlags.NoBlockEditor)] + [CommandMethod("CmdTest_RunLisp21", CommandFlags.NoActionRecording)] + [CommandMethod("CmdTest_RunLisp22", CommandFlags.ActionMacro)] +#if !NET35 + [CommandMethod("CmdTest_RunLisp23", CommandFlags.NoInferConstraint)] + [CommandMethod("CmdTest_RunLisp24", CommandFlags.TempShowDynDimension)] +#endif + /* + * bug: + * acad08没有 + * 在高版本使用(command "CmdTest_RunLisp1") AcedEvaluateLisp 接口不会正确运行,使得定义lisp变量失效 + * + * 经过测试,此bug与CommandFlags无关 + * + */ + public static void CmdTest_RunLisp() + { +#if NET35 + // 使用(command "CmdTest_RunLisp1")发送,然后 !a 查看变量,acad08是有值的,高版本是null + var strlisp0 = "(setq a 10)"; + var res0 = Env.Editor.RunLisp(strlisp0); //有lisp的返回值 + + var strlisp1 = "(defun aaa( / )( princ \"aa\" ))"; + var res1 = Env.Editor.RunLisp(strlisp1); //有lisp的返回值 + + var strlisp2 = "(defun bbb( / )( command \"line\" ))"; + var res2 = Env.Editor.RunLisp(strlisp2); //有lisp的返回值 +#else + // 同步 + Env.Editor.SendString("(setq a 10)(princ)"); + Env.Editor.SendString("(princ a)");//成功输出 + + var dm = Application.DocumentManager; + var doc = dm.MdiActiveDocument; + var str = "(setq b 10)(princ)"; + doc.SendStringToExecute(str + "\n", false, false, false);//异步,后发送 + Env.Editor.SendString("(princ b)"); //同步,先发送了,输出是null +#endif + } +} +#endif \ No newline at end of file diff --git a/src/IFoxCAD.Cad/ExtensionMethod/Jig.cs b/src/IFoxCAD.Cad/ExtensionMethod/Jig.cs index d066a50..883d958 100644 --- a/src/IFoxCAD.Cad/ExtensionMethod/Jig.cs +++ b/src/IFoxCAD.Cad/ExtensionMethod/Jig.cs @@ -81,7 +81,7 @@ public class JigEx : DrawJig /// public Entity[] Entitys => _drawEntitys.ToArray(); - Autodesk.AutoCAD.Geometry.Tolerance _tolerance; + readonly Autodesk.AutoCAD.Geometry.Tolerance _tolerance; readonly Queue _drawEntitys;//重复生成的图元,放在这里刷新 readonly Action>? _action; JigPromptPointOptions? _options; -- Gitee From 059327ab108d53c750b8893a14337517c5f3c5cb Mon Sep 17 00:00:00 2001 From: liuqihong <540762622@qq.com> Date: Tue, 5 Jul 2022 20:58:34 +0800 Subject: [PATCH 280/675] =?UTF-8?q?=E5=B0=81=E8=A3=85=E6=89=80=E6=9C=89run?= =?UTF-8?q?lisp=E6=8E=A5=E5=8F=A3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/IFoxCAD.Cad/ExtensionMethod/EditorEx.cs | 202 +++++++++++--------- 1 file changed, 111 insertions(+), 91 deletions(-) diff --git a/src/IFoxCAD.Cad/ExtensionMethod/EditorEx.cs b/src/IFoxCAD.Cad/ExtensionMethod/EditorEx.cs index 6e058a8..690a7aa 100644 --- a/src/IFoxCAD.Cad/ExtensionMethod/EditorEx.cs +++ b/src/IFoxCAD.Cad/ExtensionMethod/EditorEx.cs @@ -1017,91 +1017,96 @@ public static PromptResult GetString(this Editor ed, string Message, string Defa #region 执行lisp -#if NET35 - [System.Security.SuppressUnmanagedCodeSecurity] - [DllImport("acad.exe", CharSet = CharSet.Unicode, CallingConvention = CallingConvention.Cdecl, EntryPoint = "?acedEvaluateLisp@@YAHPB_WAAPAUresbuf@@@Z")] - private static extern int AcedEvaluateLisp(string lispLine, out IntPtr result); - +#if NET35 [DllImport("acad.exe", CallingConvention = CallingConvention.Cdecl, EntryPoint = "acedInvoke")] - private static extern int AcedInvoke(IntPtr args, out IntPtr result); #else - /// - /// 高版本此接口不能使用lisp(command "xx"),但是可以直接在自动运行接口上 - /// - /// - /// - /// + [DllImport("accore.dll", CallingConvention = CallingConvention.Cdecl, EntryPoint = "acedInvoke")] +#endif + static extern int AcedInvoke(IntPtr args, out IntPtr result); + + +#if NET35 + [DllImport("acad.exe", CharSet = CharSet.Unicode, CallingConvention = CallingConvention.Cdecl, + EntryPoint = "?acedEvaluateLisp@@YAHPB_WAAPAUresbuf@@@Z")] +#else + // 高版本此接口不能使用lisp(command "xx"),但是可以直接在自动运行接口上 + [DllImport("accore.dll", CharSet = CharSet.Unicode, CallingConvention = CallingConvention.Cdecl, + EntryPoint = "?acedEvaluateLisp@@YAHPEB_WAEAPEAUresbuf@@@Z")] +#endif [System.Security.SuppressUnmanagedCodeSecurity] - [DllImport("accore.dll", CharSet = CharSet.Unicode, CallingConvention = CallingConvention.Cdecl, EntryPoint = "?acedEvaluateLisp@@YAHPEB_WAEAPEAUresbuf@@@Z")] - private static extern int AcedEvaluateLisp(string lispLine, out IntPtr result); + static extern int AcedEvaluateLisp(string lispLine, out IntPtr result); - [DllImport("accore.dll", CallingConvention = CallingConvention.Cdecl, EntryPoint = "acedInvoke")] - private static extern int AcedInvoke(IntPtr args, out IntPtr result); + +#if NET35 + [DllImport("acad.exe", CharSet = CharSet.Auto, CallingConvention = CallingConvention.Cdecl, + EntryPoint = "ads_queueexpr")] +#else + [DllImport("accore.dll", CharSet = CharSet.Auto, CallingConvention = CallingConvention.Cdecl, + EntryPoint = "ads_queueexpr")] #endif + static extern int Ads_queueexpr(string strExpr); + + public enum RunLispFlag : byte + { + AdsQueueexpr = 1, + AcedEvaluateLisp = 2, + SendStringToExecute = 4, + } -#pragma warning disable IDE0060 // 删除未使用的参数 /// /// 发送lisp语句字符串到cad执行 /// /// 编辑器对象 - /// lisp语句 + /// lisp语句 /// 缓冲结果,返回值 - public static ResultBuffer? RunLisp(this Editor ed, string arg) +#pragma warning disable IDE0060 // 删除未使用的参数 + public static ResultBuffer? RunLisp(this Editor ed, string lispCode, RunLispFlag flag = RunLispFlag.AdsQueueexpr) +#pragma warning restore IDE0060 // 删除未使用的参数 { /* - * bug: - * cad08调用成功,但是高版本调用时候没有运行成功,使得 !a 没有值 - * 调用方式:(command "CmdTest_RunLisp1") - * - * [CommandMethod("CmdTest_RunLisp")] - * public static void CmdTest_RunLisp() - * { - * var res = SendLisp.RunLisp("(setq a 10)"); - * } - * + * 测试命令: + * [CommandMethod("CmdTest_RunLisp")] + * public static void CmdTest_RunLisp() + * { + * var res = SendLisp.RunLisp("(setq a 10)"); + * } + * 调用方式: + * (command "CmdTest_RunLisp1") + * bug说明: + * AcedEvaluateLisp接口在高版本调用时候没有运行成功,使得 !a 没有值 + * 经过测试,cad08调用成功,此bug与CommandFlags无关 * 解决方案: * 0x01 用异步接口,但是这样是显式调用了: * (setq thisdrawing (vla-get-activedocument (vlax-get-acad-object)))(vla-SendCommand thisdrawing "CmdTest_RunLisp1 ") * 0x02 使用 Ads_queueexpr 接口 */ - _ = AcedEvaluateLisp(arg, out IntPtr rb); - if (rb != IntPtr.Zero) - return DisposableWrapper.Create(typeof(ResultBuffer), rb, true) as ResultBuffer; + if (flag == RunLispFlag.AdsQueueexpr) + { + // 这个在08/12发送lisp不会出错,但是发送bo命令出错了. + // 0x01 设置 CommandFlags.Session 可以同步, + // 0x02 自执行发送lisp都是异步,(用来发送 含有(command)的lisp的) + _ = Ads_queueexpr(lispCode + "\n"); + } + if (flag == RunLispFlag.AcedEvaluateLisp) + { + _ = AcedEvaluateLisp(lispCode, out IntPtr rb); + if (rb != IntPtr.Zero) + return DisposableWrapper.Create(typeof(ResultBuffer), rb, true) as ResultBuffer; + } + if (flag == RunLispFlag.SendStringToExecute) + { + var dm = Application.DocumentManager; + var doc = dm.MdiActiveDocument; + doc.SendStringToExecute(lispCode + "\n", false, false, false); + } return null; } -#pragma warning restore IDE0060 // 删除未使用的参数 - - -#if NET35 - [DllImport("acad.exe", CharSet = CharSet.Auto, CallingConvention = CallingConvention.Cdecl, - EntryPoint = "ads_queueexpr")] - static extern int Ads_queueexpr(string strExpr); -#else - [DllImport("accore.dll", CharSet = CharSet.Auto, CallingConvention = CallingConvention.Cdecl, - EntryPoint = "ads_queueexpr")] - static extern int Ads_queueexpr(string strExpr); -#endif - - /// - /// 发送命令 - /// (用来发送 含有(command)的lisp的)自执行发送lisp都是异步 - /// - public static void SendString(this Editor ed, string str) - { - // 这个在08/12发送lisp不会出错,但是发送bo命令出错了. - // 设置 CommandFlags.Session 可以同步, - // 发送lisp是异步,在自动执行函数上面会异步 - _ = Ads_queueexpr(str + "\n"); - - // var dm = Application.DocumentManager; - // var doc = dm.MdiActiveDocument; - // doc.SendStringToExecute(str + "\n", false, false, false); - } #endregion } -#if __发送lisp接口测试 +#region __发送lisp接口测试 +#if true public class TestSendLisp { [CommandMethod("CmdTest_RunLisp1", CommandFlags.Modal)] @@ -1128,39 +1133,54 @@ public class TestSendLisp [CommandMethod("CmdTest_RunLisp22", CommandFlags.ActionMacro)] #if !NET35 [CommandMethod("CmdTest_RunLisp23", CommandFlags.NoInferConstraint)] - [CommandMethod("CmdTest_RunLisp24", CommandFlags.TempShowDynDimension)] + [CommandMethod("CmdTest_RunLisp24", CommandFlags.TempShowDynDimension)] #endif - /* - * bug: - * acad08没有 - * 在高版本使用(command "CmdTest_RunLisp1") AcedEvaluateLisp 接口不会正确运行,使得定义lisp变量失效 - * - * 经过测试,此bug与CommandFlags无关 - * - */ public static void CmdTest_RunLisp() { -#if NET35 - // 使用(command "CmdTest_RunLisp1")发送,然后 !a 查看变量,acad08是有值的,高版本是null - var strlisp0 = "(setq a 10)"; - var res0 = Env.Editor.RunLisp(strlisp0); //有lisp的返回值 - - var strlisp1 = "(defun aaa( / )( princ \"aa\" ))"; - var res1 = Env.Editor.RunLisp(strlisp1); //有lisp的返回值 - - var strlisp2 = "(defun bbb( / )( command \"line\" ))"; - var res2 = Env.Editor.RunLisp(strlisp2); //有lisp的返回值 -#else - // 同步 - Env.Editor.SendString("(setq a 10)(princ)"); - Env.Editor.SendString("(princ a)");//成功输出 - var dm = Application.DocumentManager; var doc = dm.MdiActiveDocument; - var str = "(setq b 10)(princ)"; - doc.SendStringToExecute(str + "\n", false, false, false);//异步,后发送 - Env.Editor.SendString("(princ b)"); //同步,先发送了,输出是null -#endif + var ed = doc.Editor; + //(command "CmdTest_RunLisp1") + var option = new PromptIntegerOptions("输入RunLispFlag枚举值"); + var ppr = ed.GetInteger(option); + if (ppr.Status != PromptStatus.OK) + return; + var flag = (EditorEx.RunLispFlag)ppr.Value; + + if (flag == EditorEx.RunLispFlag.AdsQueueexpr) + { + // 同步 + Env.Editor.RunLisp("(setq a 10)(princ)", + EditorEx.RunLispFlag.AdsQueueexpr); + Env.Editor.RunLisp("(princ a)", + EditorEx.RunLispFlag.AdsQueueexpr);//成功输出 + } + + if (flag == EditorEx.RunLispFlag.AcedEvaluateLisp) + { + // 使用(command "CmdTest_RunLisp1")发送,然后 !a 查看变量,acad08是有值的,高版本是null + var strlisp0 = "(setq a 20)"; + var res0 = Env.Editor.RunLisp(strlisp0, + EditorEx.RunLispFlag.AcedEvaluateLisp); //有lisp的返回值 + + var strlisp1 = "(defun aaa( / )( princ \"aa\" ))"; + var res1 = Env.Editor.RunLisp(strlisp1, + EditorEx.RunLispFlag.AcedEvaluateLisp); //有lisp的返回值 + + var strlisp2 = "(defun bbb( / )( command \"line\" ))"; + var res2 = Env.Editor.RunLisp(strlisp2, + EditorEx.RunLispFlag.AcedEvaluateLisp); //有lisp的返回值 + } + + if (flag == EditorEx.RunLispFlag.SendStringToExecute) + { + var str = "(setq b 40)(princ)"; + Env.Editor.RunLisp(str, + EditorEx.RunLispFlag.SendStringToExecute); //异步,后发送 + Env.Editor.RunLisp("(princ b)", + EditorEx.RunLispFlag.AdsQueueexpr); //同步,先发送了,输出是null + } } -} -#endif \ No newline at end of file +} +#endif +#endregion \ No newline at end of file -- Gitee From a31180101b0a3e225db9ac77c0f9c9756259a4f0 Mon Sep 17 00:00:00 2001 From: liuqihong <540762622@qq.com> Date: Tue, 5 Jul 2022 22:31:06 +0800 Subject: [PATCH 281/675] =?UTF-8?q?=E8=B0=83=E6=95=B4=E6=B5=8B=E8=AF=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/IFoxCAD.Cad/ExtensionMethod/EditorEx.cs | 40 ++++++++++++--------- 1 file changed, 24 insertions(+), 16 deletions(-) diff --git a/src/IFoxCAD.Cad/ExtensionMethod/EditorEx.cs b/src/IFoxCAD.Cad/ExtensionMethod/EditorEx.cs index 690a7aa..741a719 100644 --- a/src/IFoxCAD.Cad/ExtensionMethod/EditorEx.cs +++ b/src/IFoxCAD.Cad/ExtensionMethod/EditorEx.cs @@ -1036,7 +1036,6 @@ public static PromptResult GetString(this Editor ed, string Message, string Defa [System.Security.SuppressUnmanagedCodeSecurity] static extern int AcedEvaluateLisp(string lispLine, out IntPtr result); - #if NET35 [DllImport("acad.exe", CharSet = CharSet.Auto, CallingConvention = CallingConvention.Cdecl, EntryPoint = "ads_queueexpr")] @@ -1080,20 +1079,20 @@ public enum RunLispFlag : byte * (setq thisdrawing (vla-get-activedocument (vlax-get-acad-object)))(vla-SendCommand thisdrawing "CmdTest_RunLisp1 ") * 0x02 使用 Ads_queueexpr 接口 */ - if (flag == RunLispFlag.AdsQueueexpr) + if ((flag & RunLispFlag.AdsQueueexpr) == RunLispFlag.AdsQueueexpr) { // 这个在08/12发送lisp不会出错,但是发送bo命令出错了. // 0x01 设置 CommandFlags.Session 可以同步, // 0x02 自执行发送lisp都是异步,(用来发送 含有(command)的lisp的) _ = Ads_queueexpr(lispCode + "\n"); } - if (flag == RunLispFlag.AcedEvaluateLisp) + if ((flag & RunLispFlag.AcedEvaluateLisp) == RunLispFlag.AcedEvaluateLisp) { _ = AcedEvaluateLisp(lispCode, out IntPtr rb); if (rb != IntPtr.Zero) return DisposableWrapper.Create(typeof(ResultBuffer), rb, true) as ResultBuffer; } - if (flag == RunLispFlag.SendStringToExecute) + if ((flag & RunLispFlag.SendStringToExecute) == RunLispFlag.SendStringToExecute) { var dm = Application.DocumentManager; var doc = dm.MdiActiveDocument; @@ -1106,9 +1105,16 @@ public enum RunLispFlag : byte #region __发送lisp接口测试 -#if true +#if true2 public class TestSendLisp { + [LispFunction("LispTest_RunLisp")] + public static object LispTest_RunLisp(ResultBuffer rb) + { + CmdTest_RunLisp(); + return null; + } + [CommandMethod("CmdTest_RunLisp1", CommandFlags.Modal)] [CommandMethod("CmdTest_RunLisp2", CommandFlags.Transparent)] [CommandMethod("CmdTest_RunLisp3", CommandFlags.UsePickSet)] @@ -1134,13 +1140,15 @@ public class TestSendLisp #if !NET35 [CommandMethod("CmdTest_RunLisp23", CommandFlags.NoInferConstraint)] [CommandMethod("CmdTest_RunLisp24", CommandFlags.TempShowDynDimension)] -#endif +#endif public static void CmdTest_RunLisp() { + // 测试方法1: (command "CmdTest_RunLisp1") + // 测试方式2: (LispTest_RunLisp) + var dm = Application.DocumentManager; var doc = dm.MdiActiveDocument; var ed = doc.Editor; - //(command "CmdTest_RunLisp1") var option = new PromptIntegerOptions("输入RunLispFlag枚举值"); var ppr = ed.GetInteger(option); if (ppr.Status != PromptStatus.OK) @@ -1155,29 +1163,29 @@ public static void CmdTest_RunLisp() Env.Editor.RunLisp("(princ a)", EditorEx.RunLispFlag.AdsQueueexpr);//成功输出 } - - if (flag == EditorEx.RunLispFlag.AcedEvaluateLisp) + else if (flag == EditorEx.RunLispFlag.AcedEvaluateLisp) { // 使用(command "CmdTest_RunLisp1")发送,然后 !a 查看变量,acad08是有值的,高版本是null - var strlisp0 = "(setq a 20)"; + var strlisp0 = "(setq b 20)"; var res0 = Env.Editor.RunLisp(strlisp0, EditorEx.RunLispFlag.AcedEvaluateLisp); //有lisp的返回值 - var strlisp1 = "(defun aaa( / )( princ \"aa\" ))"; + var strlisp1 = "(defun f1( / )(princ \"aa\"))"; var res1 = Env.Editor.RunLisp(strlisp1, EditorEx.RunLispFlag.AcedEvaluateLisp); //有lisp的返回值 - var strlisp2 = "(defun bbb( / )( command \"line\" ))"; + var strlisp2 = "(defun f2( / )(command \"line\"))"; var res2 = Env.Editor.RunLisp(strlisp2, EditorEx.RunLispFlag.AcedEvaluateLisp); //有lisp的返回值 } - - if (flag == EditorEx.RunLispFlag.SendStringToExecute) + else if (flag == EditorEx.RunLispFlag.SendStringToExecute) { - var str = "(setq b 40)(princ)"; + //测试异步 + //(command "CmdTest_RunLisp1")和(LispTest_RunLisp)4都是异步 + var str = "(setq c 40)(princ)"; Env.Editor.RunLisp(str, EditorEx.RunLispFlag.SendStringToExecute); //异步,后发送 - Env.Editor.RunLisp("(princ b)", + Env.Editor.RunLisp("(princ c)", EditorEx.RunLispFlag.AdsQueueexpr); //同步,先发送了,输出是null } } -- Gitee From 2fe16b6d6e3b1bac7cde3d77966c5433ced87a47 Mon Sep 17 00:00:00 2001 From: liuqihong <540762622@qq.com> Date: Tue, 5 Jul 2022 22:49:07 +0800 Subject: [PATCH 282/675] =?UTF-8?q?=E4=BF=AE=E6=94=B9=E6=B3=A8=E9=87=8A?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/IFoxCAD.Cad/ExtensionMethod/EditorEx.cs | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/src/IFoxCAD.Cad/ExtensionMethod/EditorEx.cs b/src/IFoxCAD.Cad/ExtensionMethod/EditorEx.cs index 741a719..9bab944 100644 --- a/src/IFoxCAD.Cad/ExtensionMethod/EditorEx.cs +++ b/src/IFoxCAD.Cad/ExtensionMethod/EditorEx.cs @@ -1016,7 +1016,6 @@ public static PromptResult GetString(this Editor ed, string Message, string Defa #endregion #region 执行lisp - #if NET35 [DllImport("acad.exe", CallingConvention = CallingConvention.Cdecl, EntryPoint = "acedInvoke")] #else @@ -1033,7 +1032,7 @@ public static PromptResult GetString(this Editor ed, string Message, string Defa [DllImport("accore.dll", CharSet = CharSet.Unicode, CallingConvention = CallingConvention.Cdecl, EntryPoint = "?acedEvaluateLisp@@YAHPEB_WAEAPEAUresbuf@@@Z")] #endif - [System.Security.SuppressUnmanagedCodeSecurity] + [System.Security.SuppressUnmanagedCodeSecurity]//初始化默认值 static extern int AcedEvaluateLisp(string lispLine, out IntPtr result); #if NET35 @@ -1067,12 +1066,12 @@ public enum RunLispFlag : byte * [CommandMethod("CmdTest_RunLisp")] * public static void CmdTest_RunLisp() * { - * var res = SendLisp.RunLisp("(setq a 10)"); + * var res = SendLisp.RunLisp("(setq abc 10)"); * } * 调用方式: * (command "CmdTest_RunLisp1") * bug说明: - * AcedEvaluateLisp接口在高版本调用时候没有运行成功,使得 !a 没有值 + * AcedEvaluateLisp接口在高版本调用时候没有运行成功,使得 !abc 没有值 * 经过测试,cad08调用成功,此bug与CommandFlags无关 * 解决方案: * 0x01 用异步接口,但是这样是显式调用了: @@ -1165,7 +1164,7 @@ public static void CmdTest_RunLisp() } else if (flag == EditorEx.RunLispFlag.AcedEvaluateLisp) { - // 使用(command "CmdTest_RunLisp1")发送,然后 !a 查看变量,acad08是有值的,高版本是null + // 使用(command "CmdTest_RunLisp1")发送,然后 !b 查看变量,acad08是有值的,高版本是null var strlisp0 = "(setq b 20)"; var res0 = Env.Editor.RunLisp(strlisp0, EditorEx.RunLispFlag.AcedEvaluateLisp); //有lisp的返回值 -- Gitee From 545955cda15fb4070c774c44ea6cff5e656aed05 Mon Sep 17 00:00:00 2001 From: zewen han Date: Wed, 6 Jul 2022 09:45:33 +0800 Subject: [PATCH 283/675] =?UTF-8?q?commandflags=20=E8=A7=A3=E9=87=8A?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/IFoxCAD.Cad/ExtensionMethod/EditorEx.cs | 35 +++++++++++++++++---- 1 file changed, 29 insertions(+), 6 deletions(-) diff --git a/src/IFoxCAD.Cad/ExtensionMethod/EditorEx.cs b/src/IFoxCAD.Cad/ExtensionMethod/EditorEx.cs index 9bab944..24c7772 100644 --- a/src/IFoxCAD.Cad/ExtensionMethod/EditorEx.cs +++ b/src/IFoxCAD.Cad/ExtensionMethod/EditorEx.cs @@ -1113,33 +1113,56 @@ public static object LispTest_RunLisp(ResultBuffer rb) CmdTest_RunLisp(); return null; } - + // 发出的命令是模式的,只有当CAD发出命令提示或当前没有其他的命令或程序活动的时候才可以被触发 [CommandMethod("CmdTest_RunLisp1", CommandFlags.Modal)] + //命令是透明的,可以在一个命令提示输入的时候触发例如正交切换、zoom等 [CommandMethod("CmdTest_RunLisp2", CommandFlags.Transparent)] + //命令执行前已经选中部分实体、命令将可以使用这些实体 [CommandMethod("CmdTest_RunLisp3", CommandFlags.UsePickSet)] + // 命令执行前已选中部分实体、在命令执行过程中这些标记不会被清除 [CommandMethod("CmdTest_RunLisp4", CommandFlags.Redraw)] + //命令不能在透视图中使用 [CommandMethod("CmdTest_RunLisp5", CommandFlags.NoPerspective)] + //命令不能通过MULTIPLE命令重复触发 [CommandMethod("CmdTest_RunLisp6", CommandFlags.NoMultiple)] - [CommandMethod("CmdTest_RunLisp7", CommandFlags.NoTileMode)]/*不允许模型使用命令*/ - [CommandMethod("CmdTest_RunLisp8", CommandFlags.NoPaperSpace)]/*不允许布局使用命令*/ + //不允许在模型空间使用命令 + [CommandMethod("CmdTest_RunLisp7", CommandFlags.NoTileMode)] + //不允许在布局空间使用命令 + [CommandMethod("CmdTest_RunLisp8", CommandFlags.NoPaperSpace)] + //命令不能在OEM产品中使用 [CommandMethod("CmdTest_RunLisp9", CommandFlags.NoOem)] - [CommandMethod("CmdTest_RunLisp10", CommandFlags.Undefined)]/*不允许调用*/ - [CommandMethod("CmdTest_RunLisp11", CommandFlags.Defun)]/*不允许调用*/ + //不能直接使用命令名调用,必须使用 组名.全局名 调用 + [CommandMethod("CmdTest_RunLisp10", CommandFlags.Undefined)] + // 定义lisp方法。已废弃 请使用lispfunction + [CommandMethod("CmdTest_RunLisp11", CommandFlags.Defun)] + //命令不会被存储在新的命令堆上 [CommandMethod("CmdTest_RunLisp12", CommandFlags.NoNewStack)] + //命令不能被内部锁定 [CommandMethod("CmdTest_RunLisp13", CommandFlags.NoInternalLock)] + //调用命令的文档将会被锁定为只读 [CommandMethod("CmdTest_RunLisp14", CommandFlags.DocReadLock)] + //调用命令的文档将会被锁定,类似document.lockdocument [CommandMethod("CmdTest_RunLisp15", CommandFlags.DocExclusiveLock)] + //命令在CAD运行期间都能使用,而不只是在当前文档 [CommandMethod("CmdTest_RunLisp16", CommandFlags.Session)] + //获取用户输入时,可以与属性面板之类的交互 [CommandMethod("CmdTest_RunLisp17", CommandFlags.Interruptible)] + //命令不会被记录在命令历史记录 [CommandMethod("CmdTest_RunLisp18", CommandFlags.NoHistory)] + //命令不会被UNDO取消 [CommandMethod("CmdTest_RunLisp19", CommandFlags.NoUndoMarker)] + //不能在参照块中使用命令 [CommandMethod("CmdTest_RunLisp20", CommandFlags.NoBlockEditor)] + //不会被动作录制器 捕捉到 [CommandMethod("CmdTest_RunLisp21", CommandFlags.NoActionRecording)] + //会被动作录制器捕捉 [CommandMethod("CmdTest_RunLisp22", CommandFlags.ActionMacro)] #if !NET35 + //推断约束时不能使用命令 [CommandMethod("CmdTest_RunLisp23", CommandFlags.NoInferConstraint)] + //命令允许在选择图元时临时显示动态尺寸 [CommandMethod("CmdTest_RunLisp24", CommandFlags.TempShowDynDimension)] -#endif +#endif public static void CmdTest_RunLisp() { // 测试方法1: (command "CmdTest_RunLisp1") -- Gitee From 086c9e4b6f39872d3f04220ad8ebbbf825df703f Mon Sep 17 00:00:00 2001 From: liuqihong <540762622@qq.com> Date: Sat, 9 Jul 2022 23:41:00 +0800 Subject: [PATCH 284/675] =?UTF-8?q?=E8=B0=83=E6=95=B4=E5=8F=91=E9=80=81lis?= =?UTF-8?q?p=E7=9A=84=E6=B5=8B=E8=AF=95=E5=91=BD=E4=BB=A4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/IFoxCAD.Cad/ExtensionMethod/EditorEx.cs | 132 ++------------------ src/IFoxCAD.Cad/Runtime/AutoRegAssem.cs | 12 +- tests/Test/TestLisp.cs | 122 ++++++++++++++++++ 3 files changed, 140 insertions(+), 126 deletions(-) create mode 100644 tests/Test/TestLisp.cs diff --git a/src/IFoxCAD.Cad/ExtensionMethod/EditorEx.cs b/src/IFoxCAD.Cad/ExtensionMethod/EditorEx.cs index 24c7772..836a673 100644 --- a/src/IFoxCAD.Cad/ExtensionMethod/EditorEx.cs +++ b/src/IFoxCAD.Cad/ExtensionMethod/EditorEx.cs @@ -139,7 +139,7 @@ public static SelectionSet SelectByLineWeight(this Editor editor, LineWeight lin * }; * pso.SsgetAddKeys(dic); * - * //创建选择集过滤器,只选择块对象 + * //创建选择集过滤器,只选择块对象 * var filList = new TypedValue[] { new TypedValue((int)DxfCode.Start, "INSERT") }; * var filter = new SelectionFilter(filList); * ssPsr = ed.GetSelection(pso, filter); @@ -423,7 +423,7 @@ public static void InfoMessageBox(string caption, string format, params object[] } /// - /// 提示信息对话框,默认标题为NFox.Cad + /// 提示信息对话框,默认标题为NFox.Cad /// /// 对话框文本 public static void InfoMessageBox(string message) @@ -473,7 +473,7 @@ public static void WriteMessage(string format, params object[] args) /// /// 判断是否有活动的编辑器对象 /// - /// 有,没有 + /// 有,没有 public static bool HasEditor() { return Application.DocumentManager.MdiActiveDocument is not null @@ -484,7 +484,7 @@ public static bool HasEditor() /// /// 判断是否可以打印字符串 /// - /// 可以打印,不可以打印 + /// 可以打印,不可以打印 public static bool Acceptable() { return HasEditor() @@ -499,7 +499,7 @@ public static bool Acceptable() /// 根据点表返回矢量线的列表 /// /// 点表 - /// 是否闭合, 为闭合, 为不闭合 + /// 是否闭合, 为闭合, 为不闭合 /// public static List GetLines(IEnumerable pnts, bool isClosed) { @@ -537,7 +537,7 @@ public static List GetLines(IEnumerable pnts, bool isClosed /// 编辑器对象 /// 点表 /// 颜色码 - /// 是否闭合, 为闭合, 为不闭合 + /// 是否闭合, 为闭合, 为不闭合 public static void DrawVectors(this Editor editor, IEnumerable pnts, short colorIndex, bool isClosed) { var rlst = @@ -1056,9 +1056,12 @@ public enum RunLispFlag : byte /// /// 编辑器对象 /// lisp语句 + /// 运行方式 /// 缓冲结果,返回值 #pragma warning disable IDE0060 // 删除未使用的参数 - public static ResultBuffer? RunLisp(this Editor ed, string lispCode, RunLispFlag flag = RunLispFlag.AdsQueueexpr) + public static ResultBuffer? RunLisp(this Editor ed, + string lispCode, + RunLispFlag flag = RunLispFlag.AdsQueueexpr) #pragma warning restore IDE0060 // 删除未使用的参数 { /* @@ -1100,117 +1103,4 @@ public enum RunLispFlag : byte return null; } #endregion -} - - -#region __发送lisp接口测试 -#if true2 -public class TestSendLisp -{ - [LispFunction("LispTest_RunLisp")] - public static object LispTest_RunLisp(ResultBuffer rb) - { - CmdTest_RunLisp(); - return null; - } - // 发出的命令是模式的,只有当CAD发出命令提示或当前没有其他的命令或程序活动的时候才可以被触发 - [CommandMethod("CmdTest_RunLisp1", CommandFlags.Modal)] - //命令是透明的,可以在一个命令提示输入的时候触发例如正交切换、zoom等 - [CommandMethod("CmdTest_RunLisp2", CommandFlags.Transparent)] - //命令执行前已经选中部分实体、命令将可以使用这些实体 - [CommandMethod("CmdTest_RunLisp3", CommandFlags.UsePickSet)] - // 命令执行前已选中部分实体、在命令执行过程中这些标记不会被清除 - [CommandMethod("CmdTest_RunLisp4", CommandFlags.Redraw)] - //命令不能在透视图中使用 - [CommandMethod("CmdTest_RunLisp5", CommandFlags.NoPerspective)] - //命令不能通过MULTIPLE命令重复触发 - [CommandMethod("CmdTest_RunLisp6", CommandFlags.NoMultiple)] - //不允许在模型空间使用命令 - [CommandMethod("CmdTest_RunLisp7", CommandFlags.NoTileMode)] - //不允许在布局空间使用命令 - [CommandMethod("CmdTest_RunLisp8", CommandFlags.NoPaperSpace)] - //命令不能在OEM产品中使用 - [CommandMethod("CmdTest_RunLisp9", CommandFlags.NoOem)] - //不能直接使用命令名调用,必须使用 组名.全局名 调用 - [CommandMethod("CmdTest_RunLisp10", CommandFlags.Undefined)] - // 定义lisp方法。已废弃 请使用lispfunction - [CommandMethod("CmdTest_RunLisp11", CommandFlags.Defun)] - //命令不会被存储在新的命令堆上 - [CommandMethod("CmdTest_RunLisp12", CommandFlags.NoNewStack)] - //命令不能被内部锁定 - [CommandMethod("CmdTest_RunLisp13", CommandFlags.NoInternalLock)] - //调用命令的文档将会被锁定为只读 - [CommandMethod("CmdTest_RunLisp14", CommandFlags.DocReadLock)] - //调用命令的文档将会被锁定,类似document.lockdocument - [CommandMethod("CmdTest_RunLisp15", CommandFlags.DocExclusiveLock)] - //命令在CAD运行期间都能使用,而不只是在当前文档 - [CommandMethod("CmdTest_RunLisp16", CommandFlags.Session)] - //获取用户输入时,可以与属性面板之类的交互 - [CommandMethod("CmdTest_RunLisp17", CommandFlags.Interruptible)] - //命令不会被记录在命令历史记录 - [CommandMethod("CmdTest_RunLisp18", CommandFlags.NoHistory)] - //命令不会被UNDO取消 - [CommandMethod("CmdTest_RunLisp19", CommandFlags.NoUndoMarker)] - //不能在参照块中使用命令 - [CommandMethod("CmdTest_RunLisp20", CommandFlags.NoBlockEditor)] - //不会被动作录制器 捕捉到 - [CommandMethod("CmdTest_RunLisp21", CommandFlags.NoActionRecording)] - //会被动作录制器捕捉 - [CommandMethod("CmdTest_RunLisp22", CommandFlags.ActionMacro)] -#if !NET35 - //推断约束时不能使用命令 - [CommandMethod("CmdTest_RunLisp23", CommandFlags.NoInferConstraint)] - //命令允许在选择图元时临时显示动态尺寸 - [CommandMethod("CmdTest_RunLisp24", CommandFlags.TempShowDynDimension)] -#endif - public static void CmdTest_RunLisp() - { - // 测试方法1: (command "CmdTest_RunLisp1") - // 测试方式2: (LispTest_RunLisp) - - var dm = Application.DocumentManager; - var doc = dm.MdiActiveDocument; - var ed = doc.Editor; - var option = new PromptIntegerOptions("输入RunLispFlag枚举值"); - var ppr = ed.GetInteger(option); - if (ppr.Status != PromptStatus.OK) - return; - var flag = (EditorEx.RunLispFlag)ppr.Value; - - if (flag == EditorEx.RunLispFlag.AdsQueueexpr) - { - // 同步 - Env.Editor.RunLisp("(setq a 10)(princ)", - EditorEx.RunLispFlag.AdsQueueexpr); - Env.Editor.RunLisp("(princ a)", - EditorEx.RunLispFlag.AdsQueueexpr);//成功输出 - } - else if (flag == EditorEx.RunLispFlag.AcedEvaluateLisp) - { - // 使用(command "CmdTest_RunLisp1")发送,然后 !b 查看变量,acad08是有值的,高版本是null - var strlisp0 = "(setq b 20)"; - var res0 = Env.Editor.RunLisp(strlisp0, - EditorEx.RunLispFlag.AcedEvaluateLisp); //有lisp的返回值 - - var strlisp1 = "(defun f1( / )(princ \"aa\"))"; - var res1 = Env.Editor.RunLisp(strlisp1, - EditorEx.RunLispFlag.AcedEvaluateLisp); //有lisp的返回值 - - var strlisp2 = "(defun f2( / )(command \"line\"))"; - var res2 = Env.Editor.RunLisp(strlisp2, - EditorEx.RunLispFlag.AcedEvaluateLisp); //有lisp的返回值 - } - else if (flag == EditorEx.RunLispFlag.SendStringToExecute) - { - //测试异步 - //(command "CmdTest_RunLisp1")和(LispTest_RunLisp)4都是异步 - var str = "(setq c 40)(princ)"; - Env.Editor.RunLisp(str, - EditorEx.RunLispFlag.SendStringToExecute); //异步,后发送 - Env.Editor.RunLisp("(princ c)", - EditorEx.RunLispFlag.AdsQueueexpr); //同步,先发送了,输出是null - } - } -} -#endif -#endregion \ No newline at end of file +} \ No newline at end of file diff --git a/src/IFoxCAD.Cad/Runtime/AutoRegAssem.cs b/src/IFoxCAD.Cad/Runtime/AutoRegAssem.cs index a6976b7..52597ef 100644 --- a/src/IFoxCAD.Cad/Runtime/AutoRegAssem.cs +++ b/src/IFoxCAD.Cad/Runtime/AutoRegAssem.cs @@ -60,11 +60,13 @@ public static DirectoryInfo GetDirectory(Assembly assem) public AutoRegAssem() { var assem = Assembly.GetCallingAssembly(); - Info = new(); - Info.Loader = assem.Location; - Info.Fullname = assem.FullName; - Info.Name = assem.GetName().Name; - Info.LoadType = AssemLoadType.Startting; + Info = new() + { + Loader = assem.Location, + Fullname = assem.FullName, + Name = assem.GetName().Name, + LoadType = AssemLoadType.Startting + }; if (!SearchForReg()) RegApp(); diff --git a/tests/Test/TestLisp.cs b/tests/Test/TestLisp.cs new file mode 100644 index 0000000..c6e1f47 --- /dev/null +++ b/tests/Test/TestLisp.cs @@ -0,0 +1,122 @@ +namespace Test +{ + public class TestLisp + { + //定义lisp函数 + [LispFunction("LispTest_RunLisp")] + public static object LispTest_RunLisp(ResultBuffer rb) + { + CmdTest_RunLisp(); + return null!; + } + + //模态命令,只有当CAD发出命令提示或当前没有其他的命令或程序活动的时候才可以被触发 + [CommandMethod("CmdTest_RunLisp1", CommandFlags.Modal)] + //透明命令,可以在一个命令提示输入的时候触发例如正交切换,zoom等 + [CommandMethod("CmdTest_RunLisp2", CommandFlags.Transparent)] + //选择图元之后执行命令将可以从 获取图元 + [CommandMethod("CmdTest_RunLisp3", CommandFlags.UsePickSet)] + //命令执行前已选中部分实体.在命令执行过程中这些标记不会被清除 + [CommandMethod("CmdTest_RunLisp4", CommandFlags.Redraw)] + //命令不能在透视图中使用 + [CommandMethod("CmdTest_RunLisp5", CommandFlags.NoPerspective)] + //命令不能通过 MULTIPLE命令 重复触发 + [CommandMethod("CmdTest_RunLisp6", CommandFlags.NoMultiple)] + //不允许在模型空间使用命令 + [CommandMethod("CmdTest_RunLisp7", CommandFlags.NoTileMode)] + //不允许在布局空间使用命令 + [CommandMethod("CmdTest_RunLisp8", CommandFlags.NoPaperSpace)] + //命令不能在OEM产品中使用 + [CommandMethod("CmdTest_RunLisp9", CommandFlags.NoOem)] + //不能直接使用命令名调用,必须使用 组名.全局名 调用 + [CommandMethod("CmdTest_RunLisp10", CommandFlags.Undefined)] + //定义lisp方法.已废弃 请使用lispfunction + [CommandMethod("CmdTest_RunLisp11", CommandFlags.Defun)] + //命令不会被存储在新的命令堆上 + [CommandMethod("CmdTest_RunLisp12", CommandFlags.NoNewStack)] + //命令不能被内部锁定(命令锁) + [CommandMethod("CmdTest_RunLisp13", CommandFlags.NoInternalLock)] + //调用命令的文档将会被锁定为只读 + [CommandMethod("CmdTest_RunLisp14", CommandFlags.DocReadLock)] + //调用命令的文档将会被锁定,类似document.lockdocument + [CommandMethod("CmdTest_RunLisp15", CommandFlags.DocExclusiveLock)] + //命令在CAD运行期间都能使用,而不只是在当前文档 + [CommandMethod("CmdTest_RunLisp16", CommandFlags.Session)] + //获取用户输入时,可以与属性面板之类的交互 + [CommandMethod("CmdTest_RunLisp17", CommandFlags.Interruptible)] + //命令不会被记录在命令历史记录 + [CommandMethod("CmdTest_RunLisp18", CommandFlags.NoHistory)] + //命令不会被 UNDO取消 + [CommandMethod("CmdTest_RunLisp19", CommandFlags.NoUndoMarker)] + //不能在参照块中使用命令 + [CommandMethod("CmdTest_RunLisp20", CommandFlags.NoBlockEditor)] +#if ac2009 + //acad09增,不会被动作录制器 捕捉到 + [CommandMethod("CmdTest_RunLisp21", CommandFlags.NoActionRecording)] + //acad09增,会被动作录制器捕捉 + [CommandMethod("CmdTest_RunLisp22", CommandFlags.ActionMacro)] +#endif +#if !NET35 + //推断约束时不能使用命令 + [CommandMethod("CmdTest_RunLisp23", CommandFlags.NoInferConstraint)] + //命令允许在选择图元时临时显示动态尺寸 + [CommandMethod("CmdTest_RunLisp24", CommandFlags.TempShowDynDimension)] +#endif + public static void CmdTest_RunLisp() + { + // 测试方法1: (command "CmdTest_RunLisp1") + // 测试方式2: (LispTest_RunLisp) + var dm = Application.DocumentManager; + var doc = dm.MdiActiveDocument; + var ed = doc.Editor; + + var sb = new StringBuilder(); + foreach (var item in Enum.GetValues(typeof(EditorEx.RunLispFlag))) + { + sb.Append((byte)item); + sb.Append(','); + } + sb.Remove(sb.Length - 1, 1); + var option = new PromptIntegerOptions($"\n输入RunLispFlag枚举值:[{sb}]"); + var ppr = ed.GetInteger(option); + + if (ppr.Status != PromptStatus.OK) + return; + var flag = (EditorEx.RunLispFlag)ppr.Value; + + if (flag == EditorEx.RunLispFlag.AdsQueueexpr) + { + // 同步 + Env.Editor.RunLisp("(setq a 10)(princ)", + EditorEx.RunLispFlag.AdsQueueexpr); + Env.Editor.RunLisp("(princ a)", + EditorEx.RunLispFlag.AdsQueueexpr);//成功输出 + } + else if (flag == EditorEx.RunLispFlag.AcedEvaluateLisp) + { + // 使用(command "CmdTest_RunLisp1")发送,然后 !b 查看变量,acad08是有值的,高版本是null + var strlisp0 = "(setq b 20)"; + var res0 = Env.Editor.RunLisp(strlisp0, + EditorEx.RunLispFlag.AcedEvaluateLisp); //有lisp的返回值 + + var strlisp1 = "(defun f1( / )(princ \"aa\"))"; + var res1 = Env.Editor.RunLisp(strlisp1, + EditorEx.RunLispFlag.AcedEvaluateLisp); //有lisp的返回值 + + var strlisp2 = "(defun f2( / )(command \"line\"))"; + var res2 = Env.Editor.RunLisp(strlisp2, + EditorEx.RunLispFlag.AcedEvaluateLisp); //有lisp的返回值 + } + else if (flag == EditorEx.RunLispFlag.SendStringToExecute) + { + //测试异步 + //(command "CmdTest_RunLisp1")和(LispTest_RunLisp)4都是异步 + var str = "(setq c 40)(princ)"; + Env.Editor.RunLisp(str, + EditorEx.RunLispFlag.SendStringToExecute); //异步,后发送 + Env.Editor.RunLisp("(princ c)", + EditorEx.RunLispFlag.AdsQueueexpr); //同步,先发送了,输出是null + } + } + } +} -- Gitee From 664b15f9bdf3569f7422b83195345218d835bb7a Mon Sep 17 00:00:00 2001 From: liuqihong <540762622@qq.com> Date: Sat, 9 Jul 2022 23:41:13 +0800 Subject: [PATCH 285/675] =?UTF-8?q?=E5=A2=9E=E5=8A=A0=E9=81=8D=E5=8E=86?= =?UTF-8?q?=E6=9E=9A=E4=B8=BE?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- tests/TestConsole/Program.cs | 32 ++++++++++++++++++++++++++++++++ 1 file changed, 32 insertions(+) diff --git a/tests/TestConsole/Program.cs b/tests/TestConsole/Program.cs index 40ae94e..4471ae3 100644 --- a/tests/TestConsole/Program.cs +++ b/tests/TestConsole/Program.cs @@ -1,8 +1,30 @@ // See https://aka.ms/new-console-template for more information using System; +using System.Text; using IFoxCAD.Basal; //using static TestConsole.Program; + +/*测试遍历枚举*/ +//Season a = Season.Autumn; +//Console.WriteLine($"Integral value of {a} is {(int)a}"); // output: Integral value of Autumn is 2 +//foreach (var enumItem in Enum.GetValues(typeof(Season))) +// Console.WriteLine((byte)enumItem); + +var sb = new StringBuilder(); +var enums = Enum.GetValues(typeof(Season)).GetEnumerator(); +while (enums.MoveNext()) +{ + sb.Append(((byte)enums.Current).ToString()); + sb.Append(","); +} +Console.WriteLine(sb); + +sb.Remove(sb.Length - 1, 1);//剔除末尾, +//因为有返回值所以容易理解成 sb = sb.Remove(sb.Length - 1, 1); +Console.WriteLine(sb); + + /*下面是元组测试*/ var valuetuple = (1, 2); @@ -19,3 +41,13 @@ Console.WriteLine(item); } Console.ReadLine(); + + +public enum Season : byte +{ + Spring, + Summer, + Autumn, + Winter +} + -- Gitee From 95758a2b2eb8a4a27f8fd270fd824e1a0891cb1d Mon Sep 17 00:00:00 2001 From: liuqihong <540762622@qq.com> Date: Sat, 9 Jul 2022 23:42:55 +0800 Subject: [PATCH 286/675] =?UTF-8?q?=E5=A2=9E=E5=8A=A0=E8=BF=AD=E4=BB=A3?= =?UTF-8?q?=E5=99=A8=E6=8E=A5=E5=8F=A3=E7=9A=84=E8=AF=B4=E6=98=8E?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- tests/TestConsole/Program.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/tests/TestConsole/Program.cs b/tests/TestConsole/Program.cs index 4471ae3..1a42ee5 100644 --- a/tests/TestConsole/Program.cs +++ b/tests/TestConsole/Program.cs @@ -12,6 +12,7 @@ // Console.WriteLine((byte)enumItem); var sb = new StringBuilder(); +/*因为 net framework 没写好的原因,导致直接使用迭代器反而更慢,到了net60就迭代器比foreach更快*/ var enums = Enum.GetValues(typeof(Season)).GetEnumerator(); while (enums.MoveNext()) { -- Gitee From ec28f665d575f066ee8ae89e9c4713638d391a7b Mon Sep 17 00:00:00 2001 From: liuqihong <540762622@qq.com> Date: Sun, 10 Jul 2022 02:57:37 +0800 Subject: [PATCH 287/675] =?UTF-8?q?=E4=BF=AE=E6=94=B9jig=E7=9A=84=E7=A9=BA?= =?UTF-8?q?=E6=A0=BC=E7=BB=93=E6=9D=9F=E6=96=B9=E5=BC=8F/=E6=B7=BB?= =?UTF-8?q?=E5=8A=A0jig=E4=BE=8B=E5=AD=90?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/IFoxCAD.Cad/ExtensionMethod/Jig.cs | 141 ++++++---------- tests/Test/TestJig.cs | 216 ++++++++++++------------- 2 files changed, 156 insertions(+), 201 deletions(-) diff --git a/src/IFoxCAD.Cad/ExtensionMethod/Jig.cs b/src/IFoxCAD.Cad/ExtensionMethod/Jig.cs index 883d958..86ffead 100644 --- a/src/IFoxCAD.Cad/ExtensionMethod/Jig.cs +++ b/src/IFoxCAD.Cad/ExtensionMethod/Jig.cs @@ -1,62 +1,11 @@ /* 封装jig + * 20220710 修改SetOption()的空格结束,并添加例子到IFox * 20220503 cad22需要防止刷新过程中更改队列,08不会有. * 20220326 重绘图元的函数用错了,现在修正过来 * 20211216 加入块表时候做一个差集,剔除临时图元 * 20211209 补充正交变量设置和回收设置 * 作者: 惊惊⎛⎝◕⏝⏝◕。⎠⎞ ⎛⎝≥⏝⏝0⎠⎞ ⎛⎝⓿⏝⏝⓿。⎠⎞ ⎛⎝≥⏝⏝≤⎠⎞ * 博客: https://www.cnblogs.com/JJBox/p/15650770.html - * - * 例子1: - * var ptjig = new JigEx(); - * ptjig.SetOptions(Point3d.Origin); - * var pr = ptjig.Drag(); - * if (pr.Status != PromptStatus.OK) - * return null; - * - * 例子2: - * var ppo1 = new PromptPointOptions(Environment.NewLine + "输入矩形角点1:<空格退出>") - * { - * AllowArbitraryInput = true,//任意输入 - * AllowNone = true //允许回车 - * }; - * var ppr1 = ed.GetPoint(ppo1);//用户点选 - * if (ppr1.Status != PromptStatus.OK) - * return; - * var getPt = ppr1.Value; - * - * var recEntityJig = new JigEx((mousePoint, drawEntitys) => { - * #region 画柜子图形 - * double length = Math.Abs(getPt.X - mousePoint.X); - * double high = Math.Abs(getPt.Y - mousePoint.Y); - * var ent = AddRecToEntity(Point3d.Origin, new Point3d(length, high, 0)); - * drawEntitys.Enqueue(ent); - * #endregion - * }); - * recEntityJig.SetOptions("指定矩形角点:", new Dictionary() { { "Z", "中间(Z)" } ); - * - * bool flag = true; - * while (flag) - * { - * var pr = recEntityJig.Drag(); - * if (string.IsNullOrEmpty(pr.StringResult))//在无输入的时候会等于空 - * flag = false; - * else - * { - * switch (pr.StringResult.ToUpper()) //注意cad保留 https://www.cnblogs.com/JJBox/p/10224631.html - * { - * case "Z": - * ed.WriteMessage("\n您触发了z关键字"); - * break; - * case " ": - * flag = false;//空格结束 - * break; - * } - * } - * } - * //开启事务之后,图元加入数据库 - * db.Action(tr=>{ - * recEntityJig.AddEntityToMsPs(tr); - * }); */ namespace IFoxCAD.Cad; @@ -69,7 +18,7 @@ public class JigEx : DrawJig { #region 成员 /// - /// 事件:默认是图元刷新,其余的:亮显/暗显等等工作自由补充 + /// 事件:亮显/暗显会被刷新冲刷掉,所以这个事件用于补充非刷新的工作 /// public event WorldDrawEvent? WorldDrawEvent; /// @@ -81,9 +30,10 @@ public class JigEx : DrawJig /// public Entity[] Entitys => _drawEntitys.ToArray(); - readonly Autodesk.AutoCAD.Geometry.Tolerance _tolerance; + Action>? _action; + Autodesk.AutoCAD.Geometry.Tolerance _tolerance; + readonly Queue _drawEntitys;//重复生成的图元,放在这里刷新 - readonly Action>? _action; JigPromptPointOptions? _options; //const string _orthomode = "orthomode"; bool _systemVariablesOrthomode = false; //正交修改 @@ -91,25 +41,31 @@ public class JigEx : DrawJig #endregion #region 构造 + /// + /// 在界面绘制图元 + /// + public JigEx() + { + _drawEntitys = new(); + } + /// /// 在界面绘制图元 /// /// - /// 用来频繁执行的回调: 鼠标点,加入显示图元的容器 + /// 用来频繁执行的回调: + /// 鼠标点; + /// 加入显示图元的容器(此处会Dispose图元的new Entity,已在数据库图元利用事件加入) /// /// 鼠标移动的容差 - public JigEx(Action>? action = null, double tolerance = 1e-6) + public JigEx(Action>? action = null, double tolerance = 1e-6) : this() { _action = action; _tolerance = new(tolerance, tolerance); - _drawEntitys = new(); } #endregion #region 方法 - - - /// /// 鼠标配置:基点 /// @@ -128,18 +84,11 @@ public JigPromptPointOptions SetOptions(Point3d basePoint, Env.OrthoMode = true; _systemVariablesOrthomode = true; } - var tmp = new JigPromptPointOptions(Environment.NewLine + msg) - { - Cursor = cursorType, //光标绑定 - UseBasePoint = true, //基点打开 - BasePoint = basePoint, //基点设定 - - //用户输入控件: 由UCS探测用 | 接受三维坐标 - UserInputControls = - UserInputControls.GovernedByUCSDetect | - UserInputControls.Accept3dCoordinates - }; - _options = tmp; + _options = JigPointOptions(); + _options.Message = Environment.NewLine + msg; + _options.Cursor = cursorType; //光标绑定 + _options.UseBasePoint = true; //基点打开 + _options.BasePoint = basePoint; //基点设定 return _options; } @@ -158,14 +107,8 @@ public JigPromptPointOptions SetOptions(string msg, Dictionary? Env.OrthoMode = true; _systemVariablesOrthomode = true; } - - var tmp = new JigPromptPointOptions(Environment.NewLine + msg) - { - //用户输入控件: 由UCS探测用 | 接受三维坐标 - UserInputControls = - UserInputControls.GovernedByUCSDetect | - UserInputControls.Accept3dCoordinates - }; + _options = JigPointOptions(); + _options.Message = Environment.NewLine + msg; //加入关键字,加入时候将空格内容放到最后 string spaceValue = string.Empty; @@ -178,17 +121,16 @@ public JigPromptPointOptions SetOptions(string msg, Dictionary? if (ge.Current.Key == spaceKey) spaceValue = ge.Current.Value; else - tmp.Keywords.Add(ge.Current.Key, ge.Current.Key, ge.Current.Value); + _options.Keywords.Add(ge.Current.Key, ge.Current.Key, ge.Current.Value); } } //要放最后,才能优先触发其他关键字 if (spaceValue != string.Empty) - tmp.Keywords.Add(spaceKey, spaceKey, spaceValue); + _options.Keywords.Add(spaceKey, spaceKey, spaceValue); else - tmp.Keywords.Add(spaceKey, spaceKey, "<空格退出>"); + _options.Keywords.Add(spaceKey, spaceKey, "<空格退出>"); - _options = tmp; return _options; } @@ -199,9 +141,8 @@ public JigPromptPointOptions SetOptions(string msg, Dictionary? /// 正交开关 public void SetOptions(Action action, bool orthomode = false) { - var tmp = new JigPromptPointOptions(); - action.Invoke(tmp); - _options = tmp; + _options = new JigPromptPointOptions(); + action.Invoke(_options); if (orthomode && Env.OrthoMode == false) { @@ -257,7 +198,7 @@ public PromptResult Drag() #region 重写 /// - /// 鼠标频繁采点 + /// 鼠标采样器 /// /// /// 返回状态:令频繁刷新结束 @@ -335,11 +276,27 @@ protected override bool WorldDraw(WorldDraw draw) return true; } #endregion + + static JigPromptPointOptions JigPointOptions() + { + /* 用户输入控制: + * 结束jig的设置,若此时也设置了空格关键字, + * 空格时为 jig.Drag().Status == PromptStatus.None,而不是关键字, + * 所以设计的时候,可以不使用空格关键字. + */ + return new JigPromptPointOptions() + { + UserInputControls = + UserInputControls.GovernedByUCSDetect //由UCS探测用 + | UserInputControls.Accept3dCoordinates //接受三维坐标 + | UserInputControls.NullResponseAccepted //输入了鼠标右键,结束jig + | UserInputControls.AnyBlankTerminatesInput //空格或回车,结束jig; + }; + } } #if false -| UserInputControls.NullResponseAccepted //接受空响应 | UserInputControls.DoNotEchoCancelForCtrlC //不要取消CtrlC的回音 | UserInputControls.DoNotUpdateLastPoint //不要更新最后一点 | UserInputControls.NoDwgLimitsChecking //没有Dwg限制检查 @@ -347,8 +304,8 @@ protected override bool WorldDraw(WorldDraw draw) | UserInputControls.NoNegativeResponseAccepted //不否定回复已被接受 | UserInputControls.Accept3dCoordinates //返回点的三维坐标,是转换坐标系了? | UserInputControls.AcceptMouseUpAsPoint //接受释放按键时的点而不是按下时 -| UserInputControls.AnyBlankTerminatesInput //任何空白终止输入 -| UserInputControls.InitialBlankTerminatesInput //初始空白终止输入 + +| UserInputControls.InitialBlankTerminatesInput //初始 空格或回车,结束jig | UserInputControls.AcceptOtherInputString //接受其他输入字符串 | UserInputControls.NoZDirectionOrtho //无方向正射,直接输入数字时以基点到当前点作为方向 | UserInputControls.UseBasePointElevation //使用基点高程,基点的Z高度探测 diff --git a/tests/Test/TestJig.cs b/tests/Test/TestJig.cs index 343763a..b371b89 100644 --- a/tests/Test/TestJig.cs +++ b/tests/Test/TestJig.cs @@ -1,140 +1,177 @@ - +using System.Windows.Forms; namespace Test; +public class Commands_Jig +{ + //已在数据库的图元如何进入jig + [CommandMethod("TestCmd_jig33")] + public static void TestCmd_jig33() + { + Circle cir; + using var tr = new DBTrans(); + var per = tr.Editor.GetEntity("\n点选圆形:"); + if (per.Status != PromptStatus.OK) + return; + cir = tr.GetObject(per.ObjectId, OpenMode.ForWrite); + + JigEx moveJig = null; + moveJig = new JigEx((mousePoint, drawEntitys) => { + //cir.UpgradeOpen();//已经提权了,所以这里不需要提权 + cir.Move(cir.StartPoint, mousePoint); + //cir.DowngradeOpen(); + + //此处会Dispose图元, + //所以此处不加入已经在数据库的图元,而是加入new Entity的. + //drawEntitys.Enqueue(cir); + }); + moveJig.SetOptions(cir.GeometricExtents.MinPoint); + + //此处不会Dispose图元, + //0x01 此处加入已经在数据的图元 + //0x02 加入不刷新的图元,例如亮显会被刷新冲刷掉 + moveJig.WorldDrawEvent += draw => { + draw.RawGeometry.Draw(cir); + }; -using Autodesk.AutoCAD.ApplicationServices; -using Autodesk.AutoCAD.DatabaseServices; -using Autodesk.AutoCAD.EditorInput; -using Autodesk.AutoCAD.Geometry; -using Autodesk.AutoCAD.Runtime; - -using System; -using System.Windows.Forms; + while (true) + { + var prDrag = moveJig.Drag(); + if (prDrag.Status == PromptStatus.OK) + break; + } + } + -public class Commands -{ + //已在数据库的图元如何进入jig + [CommandMethod("TestCmd_Jig44")] + public void TestCmd_Jig44() + { + using var tr = new DBTrans(); + var per = Env.Editor.GetEntity("\n请选择一条多段线:"); + if (per.Status != PromptStatus.OK) + return; + var ent = tr.GetObject(per.ObjectId, OpenMode.ForWrite); + if (ent is not Polyline pl) + return; - [CommandMethod("loop")] + JigEx jig = null; + jig = new JigEx((mousePoint, drawEntitys) => { + var closestPt = pl.GetClosestPointTo(mousePoint, false); + + /* + * 鼠标采样器执行时修改鼠标基点 + * 原因: 多段线与鼠标垂直点作为BasePoint,jig鼠标点为确定点 + */ + var sop = jig.SetOptions(closestPt); + sop.Keywords.Add("A"); + sop.Keywords.Add(" ");/*这里是无效的,因为jig.SetOptions()内部设置*/ + + var dbtext = new DBText(); + dbtext.SetDatabaseDefaults(); + dbtext.TextString = (pl.GetDistAtPoint(closestPt) * 0.001).ToString("0.00"); + dbtext.Position = closestPt + new Vector3d(0, 300, 0); + dbtext.HorizontalMode = TextHorizontalMode.TextCenter; + dbtext.VerticalMode = TextVerticalMode.TextVerticalMid; + dbtext.AlignmentPoint = closestPt + new Vector3d(0, 300, 0); + dbtext.Height = 200; + + drawEntitys.Enqueue(dbtext); + }); + jig.SetOptions(per.PickedPoint); + + bool flag = true; + while (flag) + { + var pr = jig.Drag(); + if (pr.Status == PromptStatus.Keyword) + { + switch (pr.StringResult) + { + case "A": + tr.Editor.WriteMessage($"\n 您触发了关键字{pr.StringResult}"); + flag = false; + break; + case " ": + tr.Editor.WriteMessage("\n 此句永远不会执行,另见:jig.SetOptions()的JigPPO()内注释"); + flag = false; + break; + } + } + else if (pr.Status != PromptStatus.OK)/*右键空格回车都在这里结束*/ + return; + else + { + flag = false; + } + } - public void Loop() + tr.CurrentSpace.AddEntity(jig.Entitys); + } + [CommandMethod("TestCmd_loop")] + public void TestCmd_loop() { - DocumentCollection dm = - Autodesk.AutoCAD.ApplicationServices.Application.DocumentManager; - Editor ed = dm.MdiActiveDocument.Editor; - // Create and add our message filter - MyMessageFilter filter = new(); - System.Windows.Forms.Application.AddMessageFilter(filter); - // Start the loop - while (true) - { - // Check for user input events - System.Windows.Forms.Application.DoEvents(); - // Check whether the filter has set the flag - if (filter.bCanceled == true) - { - ed.WriteMessage("\nLoop cancelled."); - break; - } - ed.WriteMessage($"\nInside while loop...and {filter.Key}"); - } - // We're done - remove the message filter - System.Windows.Forms.Application.RemoveMessageFilter(filter); - } - - - // Our message filter class - public class MyMessageFilter : IMessageFilter - { - public const int WM_KEYDOWN = 0x0100; - public bool bCanceled = false; public Keys Key { get; private set; } public bool PreFilterMessage(ref Message m) - { - if (m.Msg == WM_KEYDOWN) - { - // Check for the Escape keypress - Keys kc = (Keys)(int)m.WParam & Keys.KeyCode; - if (m.Msg == WM_KEYDOWN && kc == Keys.Escape) - { - bCanceled = true; - } Key = kc; - // Return true to filter all keypresses - return true; - } - // Return false to let other messages through - return false; - } - } - - - - - - - - [CommandMethod("QT")] - static public void QuickText() + [CommandMethod("TestCmd_QuickText")] + static public void TestCmd_QuickText() { Document doc = Autodesk.AutoCAD.ApplicationServices.Application.DocumentManager.MdiActiveDocument; Database db = doc.Database; Editor ed = doc.Editor; - PromptStringOptions pso = new("\nEnter text string"); pso.AllowSpaces = true; PromptResult pr = ed.GetString(pso); - if (pr.Status != PromptStatus.OK) return; - Transaction tr = doc.TransactionManager.StartTransaction(); using (tr) @@ -143,28 +180,20 @@ static public void QuickText() (BlockTableRecord)tr.GetObject( db.CurrentSpaceId, OpenMode.ForWrite ); - // Create the text object, set its normal and contents - DBText txt = new(); txt.Normal = ed.CurrentUserCoordinateSystem. CoordinateSystem3d.Zaxis; txt.TextString = pr.StringResult; - // We'll add the text to the database before jigging // it - this allows alignment adjustments to be // reflected - btr.AppendEntity(txt); tr.AddNewlyCreatedDBObject(txt, true); - // Create our jig - TextPlacementJig pj = new(tr, db, txt); - // Loop as we run our jig, as we may have keywords - PromptStatus stat = PromptStatus.Keyword; while (stat == PromptStatus.Keyword) { @@ -176,22 +205,17 @@ static public void QuickText() ) return; } - tr.Commit(); } } - class TextPlacementJig : EntityJig { // Declare some internal state - readonly Database _db; readonly Transaction _tr; Point3d _position; double _angle, _txtSize; - // Constructor - public TextPlacementJig( Transaction tr, Database db, Entity ent ) : base(ent) @@ -201,24 +225,20 @@ public TextPlacementJig( _angle = 0; _txtSize = 1; } - protected override SamplerStatus Sampler( JigPrompts jp ) { // We acquire a point but with keywords - JigPromptPointOptions po = new( "\nPosition of text" ); - po.UserInputControls = (UserInputControls.Accept3dCoordinates | UserInputControls.NullResponseAccepted | UserInputControls.NoNegativeResponseAccepted | UserInputControls.GovernedByOrthoMode); - po.SetMessageAndKeywords( "\nSpecify position of text or " + "[Bold/Italic/LArger/Smaller/" + @@ -226,35 +246,28 @@ JigPrompts jp "Bold Italic LArger Smaller " + "ROtate90 LEft Middle RIght" ); - PromptPointResult ppr = jp.AcquirePoint(po); - if (ppr.Status == PromptStatus.Keyword) { switch (ppr.StringResult) { case "Bold": { - break; } case "Italic": { - - break; } case "LArger": { // Multiple the text size by two - _txtSize *= 2; break; } case "Smaller": { // Divide the text size by two - _txtSize /= 2; break; } @@ -262,7 +275,6 @@ JigPrompts jp { // To rotate clockwise we subtract 90 degrees and // then normalise the angle between 0 and 360 - _angle -= Math.PI / 2; while (_angle < Math.PI * 2) { @@ -272,53 +284,39 @@ JigPrompts jp } case "LEft": { - - break; } case "RIght": { - - break; } case "Middle": { - - break; } } - return SamplerStatus.OK; } else if (ppr.Status == PromptStatus.OK) { // Check if it has changed or not (reduces flicker) - if ( _position.DistanceTo(ppr.Value) < Tolerance.Global.EqualPoint ) return SamplerStatus.NoChange; - _position = ppr.Value; return SamplerStatus.OK; } - return SamplerStatus.Cancel; } - protected override bool Update() { // Set properties on our text object - DBText txt = (DBText)Entity; - txt.Position = _position; txt.Height = _txtSize; txt.Rotation = _angle; - return true; } } -- Gitee From 481bc5baf80709b388c9a94efc51629587f972c4 Mon Sep 17 00:00:00 2001 From: liuqihong <540762622@qq.com> Date: Sun, 10 Jul 2022 02:59:33 +0800 Subject: [PATCH 288/675] =?UTF-8?q?=E8=A1=A5=E5=85=85=E4=B8=80=E4=B8=AA?= =?UTF-8?q?=E5=8F=AA=E8=AF=BB=E7=AC=A6=E5=8F=B7?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/IFoxCAD.Cad/ExtensionMethod/Jig.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/IFoxCAD.Cad/ExtensionMethod/Jig.cs b/src/IFoxCAD.Cad/ExtensionMethod/Jig.cs index 86ffead..02fc21e 100644 --- a/src/IFoxCAD.Cad/ExtensionMethod/Jig.cs +++ b/src/IFoxCAD.Cad/ExtensionMethod/Jig.cs @@ -30,8 +30,8 @@ public class JigEx : DrawJig /// public Entity[] Entitys => _drawEntitys.ToArray(); - Action>? _action; - Autodesk.AutoCAD.Geometry.Tolerance _tolerance; + readonly Action>? _action; + readonly Autodesk.AutoCAD.Geometry.Tolerance _tolerance; readonly Queue _drawEntitys;//重复生成的图元,放在这里刷新 JigPromptPointOptions? _options; -- Gitee From a3f0e1a5eb91b89fb0d06826041d2359d4188ab8 Mon Sep 17 00:00:00 2001 From: liuqihong <540762622@qq.com> Date: Sun, 10 Jul 2022 03:03:41 +0800 Subject: [PATCH 289/675] =?UTF-8?q?=E4=BF=AE=E6=94=B9jig=E6=B3=A8=E9=87=8A?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/IFoxCAD.Cad/ExtensionMethod/Jig.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/IFoxCAD.Cad/ExtensionMethod/Jig.cs b/src/IFoxCAD.Cad/ExtensionMethod/Jig.cs index 02fc21e..3fb9c61 100644 --- a/src/IFoxCAD.Cad/ExtensionMethod/Jig.cs +++ b/src/IFoxCAD.Cad/ExtensionMethod/Jig.cs @@ -55,7 +55,7 @@ public JigEx() /// /// 用来频繁执行的回调: /// 鼠标点; - /// 加入显示图元的容器(此处会Dispose图元的new Entity,已在数据库图元利用事件加入) + /// 加入新建的图元,鼠标采样期间会Dispose图元的;所以已经在数据库图元利用事件加入,不要在此加入; /// /// 鼠标移动的容差 public JigEx(Action>? action = null, double tolerance = 1e-6) : this() -- Gitee From 5f780d4758c726de6926d6a5a315400452311820 Mon Sep 17 00:00:00 2001 From: liuqihong <540762622@qq.com> Date: Sun, 10 Jul 2022 05:17:40 +0800 Subject: [PATCH 290/675] =?UTF-8?q?=E4=BF=AE=E6=94=B9=E6=96=B0=E5=BB=BA?= =?UTF-8?q?=E5=8D=95=E8=A1=8C=E6=96=87=E5=AD=97=E7=9A=84=E6=96=B9=E5=BC=8F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/IFoxCAD.Cad/ExtensionMethod/Jig.cs | 2 +- .../TextInfo.cs" | 23 +++--- tests/Test/TestJig.cs | 77 +++++++++---------- tests/Test/testblock.cs | 9 ++- 4 files changed, 57 insertions(+), 54 deletions(-) diff --git a/src/IFoxCAD.Cad/ExtensionMethod/Jig.cs b/src/IFoxCAD.Cad/ExtensionMethod/Jig.cs index 3fb9c61..6aef747 100644 --- a/src/IFoxCAD.Cad/ExtensionMethod/Jig.cs +++ b/src/IFoxCAD.Cad/ExtensionMethod/Jig.cs @@ -44,7 +44,7 @@ public class JigEx : DrawJig /// /// 在界面绘制图元 /// - public JigEx() + private JigEx() { _drawEntitys = new(); } diff --git "a/src/IFoxCAD.Cad/ExtensionMethod/\346\226\260\345\273\272\346\226\207\345\255\227/TextInfo.cs" "b/src/IFoxCAD.Cad/ExtensionMethod/\346\226\260\345\273\272\346\226\207\345\255\227/TextInfo.cs" index c652ded..cbbee7c 100644 --- "a/src/IFoxCAD.Cad/ExtensionMethod/\346\226\260\345\273\272\346\226\207\345\255\227/TextInfo.cs" +++ "b/src/IFoxCAD.Cad/ExtensionMethod/\346\226\260\345\273\272\346\226\207\345\255\227/TextInfo.cs" @@ -5,16 +5,16 @@ /// public class TextInfo { - Database? Database; - string? Contents; - Point3d Position; + readonly Database? Database; + readonly string? Contents; + readonly Point3d Position; public string TextJustifyCn => AttachmentPointHelper.Get(TextJustify); - AttachmentPoint TextJustify; - Point3d? AlignmentPoint; + readonly AttachmentPoint TextJustify; + readonly Point3d? AlignmentPoint; - double TextHeight; - ObjectId? TextStyleId; + readonly double TextHeight; + readonly ObjectId? TextStyleId; /// /// 文字信息类 @@ -22,14 +22,14 @@ public class TextInfo /// 内容 /// 基点 /// 对齐方式 - /// 对齐点(对齐方式是左,此参数无效) + /// 对齐点(对齐方式是左,此参数无效,为null不为左就报错) /// 文字样式id /// 文字高度 /// 数据库 public TextInfo(string? contents, Point3d position, AttachmentPoint justify, - Point3d? justifyPoint, + Point3d? justifyPoint = null, ObjectId? textStyleId = null, double textHeight = 2.5, Database? database = null) @@ -37,6 +37,10 @@ public TextInfo(string? contents, Contents = contents; Position = position; TextJustify = justify; + + if (justifyPoint is null && TextJustify != AttachmentPoint.BaseLeft) + throw new ArgumentNullException(nameof(justifyPoint)); + AlignmentPoint = justifyPoint; TextHeight = textHeight; TextStyleId = textStyleId; @@ -64,6 +68,7 @@ public DBText AddDBTextToEntity() acText.TextString = Contents; //内容 acText.Position = Position; //插入点(一定要先设置) acText.Justify = TextJustify; //使他们对齐 + //acText.HorizontalMode if (AlignmentPoint is not null) acText.AlignmentPoint = AlignmentPoint.Value; diff --git a/tests/Test/TestJig.cs b/tests/Test/TestJig.cs index b371b89..10fb77a 100644 --- a/tests/Test/TestJig.cs +++ b/tests/Test/TestJig.cs @@ -41,42 +41,40 @@ public static void TestCmd_jig33() break; } } - - //已在数据库的图元如何进入jig + + //不在数据库的图元如何进入jig [CommandMethod("TestCmd_Jig44")] public void TestCmd_Jig44() { using var tr = new DBTrans(); var per = Env.Editor.GetEntity("\n请选择一条多段线:"); - if (per.Status != PromptStatus.OK) + if (per.Status != PromptStatus.OK) return; var ent = tr.GetObject(per.ObjectId, OpenMode.ForWrite); - if (ent is not Polyline pl) + if (ent is not Polyline pl) return; + /* + * 鼠标采样器执行时修改鼠标基点 + * 原因: 多段线与鼠标垂直点作为 BasePoint ,jig鼠标点为确定点 + * 所以需要先声明再传入指针,但是我发现null也可以. + */ JigEx jig = null; jig = new JigEx((mousePoint, drawEntitys) => { var closestPt = pl.GetClosestPointTo(mousePoint, false); - /* - * 鼠标采样器执行时修改鼠标基点 - * 原因: 多段线与鼠标垂直点作为BasePoint,jig鼠标点为确定点 - */ var sop = jig.SetOptions(closestPt); sop.Keywords.Add("A"); - sop.Keywords.Add(" ");/*这里是无效的,因为jig.SetOptions()内部设置*/ + sop.Keywords.Add(" ");/*这里是无效的,因为jig.SetOptions()内部设置,但是这里设置了会显示,最好注释掉*/ - var dbtext = new DBText(); - dbtext.SetDatabaseDefaults(); - dbtext.TextString = (pl.GetDistAtPoint(closestPt) * 0.001).ToString("0.00"); - dbtext.Position = closestPt + new Vector3d(0, 300, 0); - dbtext.HorizontalMode = TextHorizontalMode.TextCenter; - dbtext.VerticalMode = TextVerticalMode.TextVerticalMid; - dbtext.AlignmentPoint = closestPt + new Vector3d(0, 300, 0); - dbtext.Height = 200; + //生成文字 + var dictString = (pl.GetDistAtPoint(closestPt) * 0.001).ToString("0.00"); + var acText = new TextInfo(dictString, closestPt, AttachmentPoint.BaseLeft, textHeight: 200) + .AddDBTextToEntity(); - drawEntitys.Enqueue(dbtext); + //加入刷新队列 + drawEntitys.Enqueue(acText); }); jig.SetOptions(per.PickedPoint); @@ -93,21 +91,18 @@ public void TestCmd_Jig44() flag = false; break; case " ": - tr.Editor.WriteMessage("\n 此句永远不会执行,另见:jig.SetOptions()的JigPPO()内注释"); + tr.Editor.WriteMessage("\n 此句永远不会执行,另见: jig.SetOptions()的 JigPointOptions()内注释"); flag = false; break; } } - else if (pr.Status != PromptStatus.OK)/*右键空格回车都在这里结束*/ - return; - else - { + else if (pr.Status != PromptStatus.OK)//右键,空格,回车,都在这里结束 + return; + else flag = false; - } } - tr.CurrentSpace.AddEntity(jig.Entitys); - } + } [CommandMethod("TestCmd_loop")] public void TestCmd_loop() @@ -167,9 +162,11 @@ static public void TestCmd_QuickText() Autodesk.AutoCAD.ApplicationServices.Application.DocumentManager.MdiActiveDocument; Database db = doc.Database; Editor ed = doc.Editor; - PromptStringOptions pso = new("\nEnter text string"); - pso.AllowSpaces = true; - PromptResult pr = ed.GetString(pso); + PromptStringOptions pso = new("\nEnter text string") + { + AllowSpaces = true + }; + var pr = ed.GetString(pso); if (pr.Status != PromptStatus.OK) return; Transaction tr = @@ -181,18 +178,18 @@ static public void TestCmd_QuickText() db.CurrentSpaceId, OpenMode.ForWrite ); // Create the text object, set its normal and contents - DBText txt = new(); - txt.Normal = - ed.CurrentUserCoordinateSystem. - CoordinateSystem3d.Zaxis; - txt.TextString = pr.StringResult; - // We'll add the text to the database before jigging - // it - this allows alignment adjustments to be - // reflected - btr.AppendEntity(txt); - tr.AddNewlyCreatedDBObject(txt, true); + + var acText = new TextInfo(pr.StringResult, + Point3d.Origin, + AttachmentPoint.BaseLeft, textHeight: 200) + .AddDBTextToEntity(); + + acText.Normal = ed.CurrentUserCoordinateSystem.CoordinateSystem3d.Zaxis; + btr.AppendEntity(acText); + tr.AddNewlyCreatedDBObject(acText, true); + // Create our jig - TextPlacementJig pj = new(tr, db, txt); + TextPlacementJig pj = new(tr, db, acText); // Loop as we run our jig, as we may have keywords PromptStatus stat = PromptStatus.Keyword; while (stat == PromptStatus.Keyword) diff --git a/tests/Test/testblock.cs b/tests/Test/testblock.cs index 569ff39..4279885 100644 --- a/tests/Test/testblock.cs +++ b/tests/Test/testblock.cs @@ -30,10 +30,11 @@ public void BlockDef() }, () => { - return new List { new Line(new Point3d(0, 0, 0), new Point3d(1, 1, 0)) , - new DBText{ Position = new Point3d(0,0,0), - TextString = "123" - } }; + var acText = new TextInfo("123", Point3d.Origin, AttachmentPoint.BaseLeft); + return new List { + new Line(new Point3d(0, 0, 0), new Point3d(1, 1, 0)), + acText.AddDBTextToEntity() + }; }); } //修改块定义 -- Gitee From da395d40be9e454483cc274ce15f621bdce8cc14 Mon Sep 17 00:00:00 2001 From: liuqihong <540762622@qq.com> Date: Sun, 10 Jul 2022 05:19:40 +0800 Subject: [PATCH 291/675] =?UTF-8?q?=E8=B0=83=E6=95=B4=E6=B5=8B=E8=AF=95?= =?UTF-8?q?=E4=BB=A3=E7=A0=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- tests/Test/testblock.cs | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/tests/Test/testblock.cs b/tests/Test/testblock.cs index 4279885..ef615be 100644 --- a/tests/Test/testblock.cs +++ b/tests/Test/testblock.cs @@ -30,11 +30,11 @@ public void BlockDef() }, () => { - var acText = new TextInfo("123", Point3d.Origin, AttachmentPoint.BaseLeft); - return new List { - new Line(new Point3d(0, 0, 0), new Point3d(1, 1, 0)), - acText.AddDBTextToEntity() - }; + var line = new Line(new Point3d(0, 0, 0), new Point3d(1, 1, 0)); + var acText = new TextInfo("123", Point3d.Origin, AttachmentPoint.BaseLeft) + .AddDBTextToEntity(); + + return new List { line, acText }; }); } //修改块定义 -- Gitee From 54ec64fe57fc39246a58b236f218529d0b9b1935 Mon Sep 17 00:00:00 2001 From: liuqihong <540762622@qq.com> Date: Sun, 10 Jul 2022 21:10:57 +0800 Subject: [PATCH 292/675] =?UTF-8?q?=E4=BF=AE=E6=94=B9=E6=8A=84=E8=BF=87?= =?UTF-8?q?=E6=9D=A5=E7=9A=84jig=E5=AE=9E=E4=BE=8B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../TextInfo.cs" | 12 +++++----- tests/Test/TestJig.cs | 22 ++++++++++--------- 2 files changed, 18 insertions(+), 16 deletions(-) diff --git "a/src/IFoxCAD.Cad/ExtensionMethod/\346\226\260\345\273\272\346\226\207\345\255\227/TextInfo.cs" "b/src/IFoxCAD.Cad/ExtensionMethod/\346\226\260\345\273\272\346\226\207\345\255\227/TextInfo.cs" index cbbee7c..573963b 100644 --- "a/src/IFoxCAD.Cad/ExtensionMethod/\346\226\260\345\273\272\346\226\207\345\255\227/TextInfo.cs" +++ "b/src/IFoxCAD.Cad/ExtensionMethod/\346\226\260\345\273\272\346\226\207\345\255\227/TextInfo.cs" @@ -64,10 +64,10 @@ public DBText AddDBTextToEntity() if (TextStyleId is not null) acText.SetTextStyleId(TextStyleId.Value); - acText.Height = TextHeight; //高度 - acText.TextString = Contents; //内容 - acText.Position = Position; //插入点(一定要先设置) - acText.Justify = TextJustify; //使他们对齐 + acText.Height = TextHeight; //高度 + acText.TextString = Contents; //内容 + acText.Position = Position; //插入点(一定要先设置) + acText.Justify = TextJustify; //使他们对齐 //acText.HorizontalMode if (AlignmentPoint is not null) @@ -99,8 +99,8 @@ public MText AddMTextToEntity() mText.SetTextStyleId(TextStyleId.Value); mText.TextHeight = TextHeight; //高度 - mText.Contents = Contents; //内容 - mText.Location = Position; //插入点(一定要先设置) + mText.Contents = Contents; //内容 + mText.Location = Position; //插入点(一定要先设置) //mText.SetAttachmentMovingLocation(TextJustify); mText.Attachment = TextJustify;//使他们对齐 diff --git a/tests/Test/TestJig.cs b/tests/Test/TestJig.cs index 10fb77a..b4f3eef 100644 --- a/tests/Test/TestJig.cs +++ b/tests/Test/TestJig.cs @@ -1,5 +1,6 @@ using System.Windows.Forms; + namespace Test; public class Commands_Jig @@ -158,10 +159,11 @@ public bool PreFilterMessage(ref Message m) [CommandMethod("TestCmd_QuickText")] static public void TestCmd_QuickText() { - Document doc = - Autodesk.AutoCAD.ApplicationServices.Application.DocumentManager.MdiActiveDocument; - Database db = doc.Database; - Editor ed = doc.Editor; + var dm = Autodesk.AutoCAD.ApplicationServices.Application.DocumentManager; + var doc = dm.MdiActiveDocument; + var db = doc.Database; + var ed = doc.Editor; + PromptStringOptions pso = new("\nEnter text string") { AllowSpaces = true @@ -169,8 +171,8 @@ static public void TestCmd_QuickText() var pr = ed.GetString(pso); if (pr.Status != PromptStatus.OK) return; - Transaction tr = - doc.TransactionManager.StartTransaction(); + + var tr = doc.TransactionManager.StartTransaction(); using (tr) { BlockTableRecord btr = @@ -179,17 +181,17 @@ static public void TestCmd_QuickText() ); // Create the text object, set its normal and contents - var acText = new TextInfo(pr.StringResult, - Point3d.Origin, + var acText = new TextInfo(pr.StringResult, + Point3d.Origin, AttachmentPoint.BaseLeft, textHeight: 200) .AddDBTextToEntity(); - + acText.Normal = ed.CurrentUserCoordinateSystem.CoordinateSystem3d.Zaxis; btr.AppendEntity(acText); tr.AddNewlyCreatedDBObject(acText, true); // Create our jig - TextPlacementJig pj = new(tr, db, acText); + var pj = new TextPlacementJig(tr, db, acText); // Loop as we run our jig, as we may have keywords PromptStatus stat = PromptStatus.Keyword; while (stat == PromptStatus.Keyword) -- Gitee From d3d4ec80368228725883f19679ad0921e54e3499 Mon Sep 17 00:00:00 2001 From: liuqihong <540762622@qq.com> Date: Sun, 10 Jul 2022 21:22:51 +0800 Subject: [PATCH 293/675] =?UTF-8?q?=E5=A2=9E=E5=8A=A0=E8=A1=A8=E8=BE=BE?= =?UTF-8?q?=E5=BC=8F=E6=A0=91=E4=BE=8B=E5=AD=90?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- tests/TestConsole/Program.cs | 46 +++++++++---------- ...50\350\276\276\345\274\217\346\240\221.cs" | 27 +++++++++++ 2 files changed, 50 insertions(+), 23 deletions(-) create mode 100644 "tests/TestConsole/\350\241\250\350\276\276\345\274\217\346\240\221.cs" diff --git a/tests/TestConsole/Program.cs b/tests/TestConsole/Program.cs index 1a42ee5..ea75c31 100644 --- a/tests/TestConsole/Program.cs +++ b/tests/TestConsole/Program.cs @@ -1,11 +1,30 @@ // See https://aka.ms/new-console-template for more information using System; using System.Text; -using IFoxCAD.Basal; -//using static TestConsole.Program; -/*测试遍历枚举*/ +//表达式树例子 +TestConsole.Expression.Demo(); + +#region 元组测试 +var valuetuple = (1, 2); + +Console.WriteLine(valuetuple.ToString()); + +int[] someArray = new int[5] { 1, 2, 3, 4, 5 }; +int lastElement = someArray[^1]; // lastElement = 5 +Console.WriteLine(lastElement); +int midElement = someArray[^3]; +Console.WriteLine(midElement); +var range = someArray[1..3]; +foreach (var item in range) + Console.WriteLine(item); +#endregion + +Console.ReadLine(); + + +#region 测试遍历枚举 //Season a = Season.Autumn; //Console.WriteLine($"Integral value of {a} is {(int)a}"); // output: Integral value of Autumn is 2 //foreach (var enumItem in Enum.GetValues(typeof(Season))) @@ -25,25 +44,6 @@ //因为有返回值所以容易理解成 sb = sb.Remove(sb.Length - 1, 1); Console.WriteLine(sb); - -/*下面是元组测试*/ -var valuetuple = (1, 2); - -Console.WriteLine(valuetuple.ToString()); - -int[] someArray = new int[5] { 1, 2, 3, 4, 5 }; -int lastElement = someArray[^1]; // lastElement = 5 -Console.WriteLine(lastElement); -int midElement = someArray[^3]; -Console.WriteLine(midElement); -var range = someArray[1..3]; -foreach (var item in range) -{ - Console.WriteLine(item); -} -Console.ReadLine(); - - public enum Season : byte { Spring, @@ -51,4 +51,4 @@ public enum Season : byte Autumn, Winter } - +#endregion \ No newline at end of file diff --git "a/tests/TestConsole/\350\241\250\350\276\276\345\274\217\346\240\221.cs" "b/tests/TestConsole/\350\241\250\350\276\276\345\274\217\346\240\221.cs" new file mode 100644 index 0000000..4e9bdf3 --- /dev/null +++ "b/tests/TestConsole/\350\241\250\350\276\276\345\274\217\346\240\221.cs" @@ -0,0 +1,27 @@ +using System; +using System.Linq.Expressions; + +namespace TestConsole +{ + public class Expression + { + /// + /// 表达式树 + /// MSDN链接 + /// + public static void Demo() + { + // 创建表达式树 + Expression> exprTree = num => num < 5; + + // 分解表达式树 + ParameterExpression param = exprTree.Parameters[0]; + BinaryExpression operation = (BinaryExpression)exprTree.Body;//函数体 + ParameterExpression left = (ParameterExpression)operation.Left;//左节点 + ConstantExpression right = (ConstantExpression)operation.Right;//右表达式 + + Console.WriteLine("表达式树例子: {0} => {1} {2} {3}", + param.Name, left.Name, operation.NodeType, right.Value); + } + } +} -- Gitee From 5317578872e1917d18f93e2aa5d975dc9cc3fd42 Mon Sep 17 00:00:00 2001 From: liuqihong <540762622@qq.com> Date: Sun, 10 Jul 2022 22:29:26 +0800 Subject: [PATCH 294/675] =?UTF-8?q?=E5=A2=9E=E5=8A=A0=E8=A1=A8=E8=BE=BE?= =?UTF-8?q?=E5=BC=8F=E6=A0=91=E4=BD=93=E5=86=85=E5=A4=9A=E4=B8=AA=E5=BC=8F?= =?UTF-8?q?=E5=AD=90=E7=9A=84=E4=BE=8B=E5=AD=90?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- tests/TestConsole/Program.cs | 3 +- ...50\350\276\276\345\274\217\346\240\221.cs" | 113 ++++++++++++++++-- 2 files changed, 104 insertions(+), 12 deletions(-) diff --git a/tests/TestConsole/Program.cs b/tests/TestConsole/Program.cs index ea75c31..aa31926 100644 --- a/tests/TestConsole/Program.cs +++ b/tests/TestConsole/Program.cs @@ -4,7 +4,8 @@ //表达式树例子 -TestConsole.Expression.Demo(); +TestConsole.Test_Expression.Demo3(); +//TestConsole.Test_Expression.Demo1(); #region 元组测试 var valuetuple = (1, 2); diff --git "a/tests/TestConsole/\350\241\250\350\276\276\345\274\217\346\240\221.cs" "b/tests/TestConsole/\350\241\250\350\276\276\345\274\217\346\240\221.cs" index 4e9bdf3..3bd409f 100644 --- "a/tests/TestConsole/\350\241\250\350\276\276\345\274\217\346\240\221.cs" +++ "b/tests/TestConsole/\350\241\250\350\276\276\345\274\217\346\240\221.cs" @@ -1,27 +1,118 @@ using System; +using System.Collections.Generic; +using System.Linq; using System.Linq.Expressions; namespace TestConsole { - public class Expression + /// + /// 表达式树 + /// MSDN链接 + /// + public class Test_Expression { - /// - /// 表达式树 - /// MSDN链接 - /// - public static void Demo() + public static void Demo1() { - // 创建表达式树 + // 官方例子:表达式体内只有一个式子 + // 创建表达式树 Expression> exprTree = num => num < 5; // 分解表达式树 - ParameterExpression param = exprTree.Parameters[0]; - BinaryExpression operation = (BinaryExpression)exprTree.Body;//函数体 - ParameterExpression left = (ParameterExpression)operation.Left;//左节点 - ConstantExpression right = (ConstantExpression)operation.Right;//右表达式 + ParameterExpression param = exprTree.Parameters[0];//num + BinaryExpression operation = (BinaryExpression)exprTree.Body;//函数体 {(num < 5)} + ParameterExpression left = (ParameterExpression)operation.Left;//左节点 num + ConstantExpression right = (ConstantExpression)operation.Right;//右表达式 5 Console.WriteLine("表达式树例子: {0} => {1} {2} {3}", param.Name, left.Name, operation.NodeType, right.Value); + Console.Read(); + } + + public static void Demo2() + { + // 这里是会报错的!! 原因就是体内有多个例子需要分解!! + // Expression> exprTree = x => x > 5 && x < 50; + // + // // 分解表达式树 + // ParameterExpression param = exprTree.Parameters[0];// x + // BinaryExpression operation = (BinaryExpression)exprTree.Body;//函数体 {((x > 5) AndAlso (x < 50))} + // + // ParameterExpression left = (ParameterExpression)operation.Left;//左节点 + // ConstantExpression right = (ConstantExpression)operation.Right;//右表达式 + // + // Console.WriteLine("表达式树例子: {0} => {1} {2} {3}", + // param.Name, left.Name, operation.NodeType, right.Value); + } + + //博客园例子,表达式体内有多个式子 + public static void Demo3() + { + List names = new() { "Cai", "Edward", "Beauty" }; + + Expression> lambda0 = item => item.Length > 2; + Expression> lambda1 = item => item.Length < 4; + Expression> lambda2 = name => name.Length > 2 && name.Length < 3; + + var method = ReBuildExpression(lambda0, lambda1); + var query = names.Where(method); + foreach (string n in query) + Console.WriteLine(n); + + Console.Read(); + } + + /// + /// 重构表达式 + /// + /// + /// + /// + static Func ReBuildExpression(Expression> lambd0, + Expression> lambd1) + { + MyExpressionVisitor my = new() + { + Parameter = Expression.Parameter(typeof(string), "name") + }; + + Expression left = my.Modify(lambd0.Body); + Expression right = my.Modify(lambd1.Body); + BinaryExpression expression = Expression.AndAlso(left, right);//就是 && + + //构造一个新的表达式 + var lambda = Expression.Lambda>(expression, my.Parameter); + return lambda.Compile(); + } + } + + + /// + /// 表达式参数分解 + /// 博客园链接 + /// + public class MyExpressionVisitor : ExpressionVisitor + { + /// + /// 公共参数 + /// + public ParameterExpression? Parameter; + /// + /// 返回替换后的参数表达式 + /// + /// + /// + public Expression Modify(Expression exp) + { + return this.Visit(exp); + } + /// + /// 重写参数 + /// + /// + /// + protected override Expression? VisitParameter(ParameterExpression p) + { + return Parameter; } } } -- Gitee From 1e5676dab04682d03aa546d0f3fd2168d3f6db6e Mon Sep 17 00:00:00 2001 From: liuqihong <540762622@qq.com> Date: Sat, 16 Jul 2022 01:27:54 +0800 Subject: [PATCH 295/675] =?UTF-8?q?=E4=BC=98=E5=8C=96=E8=A1=A8=E8=BE=BE?= =?UTF-8?q?=E5=BC=8F=E6=A0=91=E4=BE=8B=E5=AD=90?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- ...50\350\276\276\345\274\217\346\240\221.cs" | 57 +++++++++++++------ 1 file changed, 41 insertions(+), 16 deletions(-) diff --git "a/tests/TestConsole/\350\241\250\350\276\276\345\274\217\346\240\221.cs" "b/tests/TestConsole/\350\241\250\350\276\276\345\274\217\346\240\221.cs" index 3bd409f..f5cc477 100644 --- "a/tests/TestConsole/\350\241\250\350\276\276\345\274\217\346\240\221.cs" +++ "b/tests/TestConsole/\350\241\250\350\276\276\345\274\217\346\240\221.cs" @@ -1,5 +1,6 @@ using System; using System.Collections.Generic; +using System.Diagnostics; using System.Linq; using System.Linq.Expressions; @@ -28,6 +29,7 @@ public static void Demo1() Console.Read(); } + public static void Demo2() { // 这里是会报错的!! 原因就是体内有多个例子需要分解!! @@ -37,51 +39,74 @@ public static void Demo2() // ParameterExpression param = exprTree.Parameters[0];// x // BinaryExpression operation = (BinaryExpression)exprTree.Body;//函数体 {((x > 5) AndAlso (x < 50))} // - // ParameterExpression left = (ParameterExpression)operation.Left;//左节点 - // ConstantExpression right = (ConstantExpression)operation.Right;//右表达式 + // ParameterExpression left = (ParameterExpression)operation.Left;//左节点.......这里报错 + // ConstantExpression right = (ConstantExpression)operation.Right;//右表达式.....这里报错 // // Console.WriteLine("表达式树例子: {0} => {1} {2} {3}", // param.Name, left.Name, operation.NodeType, right.Value); } + //博客园例子,表达式体内有多个式子 public static void Demo3() { List names = new() { "Cai", "Edward", "Beauty" }; + Console.WriteLine("******************一个表达式"); + Expression> lambda2 = name => name.Length > 2 && name.Length < 4; + var method2 = ReBuildExpression(lambda2); + var query2 = names.Where(method2); + foreach (string n in query2) + Console.WriteLine(n); + + Console.WriteLine("******************二个表达式"); Expression> lambda0 = item => item.Length > 2; Expression> lambda1 = item => item.Length < 4; - Expression> lambda2 = name => name.Length > 2 && name.Length < 3; - var method = ReBuildExpression(lambda0, lambda1); var query = names.Where(method); foreach (string n in query) Console.WriteLine(n); - + Console.WriteLine("******************表达式结束"); Console.Read(); } + + static Func ReBuildExpression(Expression> lambda) + { + MyExpressionVisitor my = new() + { + Parameter = Expression.Parameter(typeof(string), "name") + }; + + Expression left = my.Modify(lambda.Body); + //构造一个新的表达式 + var newLambda = Expression.Lambda>(left, my.Parameter); + return newLambda.Compile(); + } + + + /// - /// 重构表达式 + /// 重构表达式_合并 /// - /// - /// + /// 匿名函数表达式1 + /// 匿名函数表达式2 /// - static Func ReBuildExpression(Expression> lambd0, - Expression> lambd1) + static Func ReBuildExpression(Expression> lambda0, + Expression> lambda1) { MyExpressionVisitor my = new() { Parameter = Expression.Parameter(typeof(string), "name") }; - Expression left = my.Modify(lambd0.Body); - Expression right = my.Modify(lambd1.Body); - BinaryExpression expression = Expression.AndAlso(left, right);//就是 && + Expression left = my.Modify(lambda0.Body); + Expression right = my.Modify(lambda1.Body); + var expression = Expression.AndAlso(left, right);//就是 && 合并两个匿名函数 //构造一个新的表达式 - var lambda = Expression.Lambda>(expression, my.Parameter); - return lambda.Compile(); + var newLambda = Expression.Lambda>(expression, my.Parameter); + return newLambda.Compile(); } } @@ -103,7 +128,7 @@ public class MyExpressionVisitor : ExpressionVisitor /// public Expression Modify(Expression exp) { - return this.Visit(exp); + return Visit(exp); } /// /// 重写参数 -- Gitee From baaeb98123162db3ad1259f42caa370a01243d97 Mon Sep 17 00:00:00 2001 From: liuqihong <540762622@qq.com> Date: Sat, 16 Jul 2022 01:28:42 +0800 Subject: [PATCH 296/675] =?UTF-8?q?=E7=A7=BB=E9=99=A4=E8=AD=A6=E5=91=8A?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/IFoxCAD.Cad/IFoxCAD.Cad.csproj | 18 ++++++++++++++++++ tests/Test/Test.csproj | 8 ++++++++ 2 files changed, 26 insertions(+) diff --git a/src/IFoxCAD.Cad/IFoxCAD.Cad.csproj b/src/IFoxCAD.Cad/IFoxCAD.Cad.csproj index d0d9638..74f09b9 100644 --- a/src/IFoxCAD.Cad/IFoxCAD.Cad.csproj +++ b/src/IFoxCAD.Cad/IFoxCAD.Cad.csproj @@ -63,6 +63,24 @@ $(Configuration);ac2015 + + 1701;1702;CS1685 + + + 1701;1702;CS1685 + + + 1701;1702;CS1685 + + + 1701;1702;CS1685 + + + 1701;1702;CS1685 + + + 1701;1702;CS1685 + diff --git a/tests/Test/Test.csproj b/tests/Test/Test.csproj index d74bc94..d6a57d5 100644 --- a/tests/Test/Test.csproj +++ b/tests/Test/Test.csproj @@ -10,6 +10,14 @@ x64 + + 1701;1702;CS1685 + + + + 1701;1702;CS1685 + + -- Gitee From 4a47882161d6eef98b2138ef248a519c3bee5d34 Mon Sep 17 00:00:00 2001 From: liuqihong <540762622@qq.com> Date: Sat, 16 Jul 2022 01:28:57 +0800 Subject: [PATCH 297/675] =?UTF-8?q?=E5=A2=9E=E5=8A=A0=E8=BF=90=E8=A1=8C?= =?UTF-8?q?=E9=A1=BA=E5=BA=8F=E6=B3=A8=E9=87=8A?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/IFoxCAD.Cad/Runtime/AutoRegAssem.cs | 6 +- src/IFoxCAD.Cad/Runtime/IAutoGo.cs | 90 +++++++++++-------------- tests/Test/CmdINI.cs | 23 +++++-- 3 files changed, 57 insertions(+), 62 deletions(-) diff --git a/src/IFoxCAD.Cad/Runtime/AutoRegAssem.cs b/src/IFoxCAD.Cad/Runtime/AutoRegAssem.cs index 52597ef..9526946 100644 --- a/src/IFoxCAD.Cad/Runtime/AutoRegAssem.cs +++ b/src/IFoxCAD.Cad/Runtime/AutoRegAssem.cs @@ -28,8 +28,8 @@ public enum AssemLoadType /// public abstract class AutoRegAssem : IExtensionApplication { - private readonly AutoClass ac; - AssemInfo Info; + readonly AutoClass ac; + readonly AssemInfo Info; /// /// 程序集的路径 @@ -46,7 +46,7 @@ public abstract class AutoRegAssem : IExtensionApplication /// /// 程序集 /// 路径对象 - public static DirectoryInfo GetDirectory(Assembly assem) + public static DirectoryInfo GetDirectory(Assembly? assem) { if (assem is null) throw new(nameof(assem)); diff --git a/src/IFoxCAD.Cad/Runtime/IAutoGo.cs b/src/IFoxCAD.Cad/Runtime/IAutoGo.cs index 58627de..a5297f3 100644 --- a/src/IFoxCAD.Cad/Runtime/IAutoGo.cs +++ b/src/IFoxCAD.Cad/Runtime/IAutoGo.cs @@ -99,17 +99,18 @@ public AutoClass(string DllName) _DllName = DllName; } - //打开cad的时候会自动执行 + //启动cad的时候会自动执行 public void Initialize() { try { - GetAttributeFunctions(); + //收集特性,包括启动时和关闭时 + GetAttributeFunctions(_InitializeList,_TerminateList); + GetInterfaceFunctions(_InitializeList, nameof(Initialize)); //按照 SequenceId 排序_升序 _InitializeList = _InitializeList.OrderBy(runClass => runClass.Sequence).ToList(); AutoClass.RunFunctions(_InitializeList); - _InitializeList.Clear(); } catch (System.Exception) { @@ -126,7 +127,6 @@ public void Terminate() //按照 SequenceId 排序_降序 _TerminateList = _TerminateList.OrderByDescending(runClass => runClass.Sequence).ToList(); AutoClass.RunFunctions(_TerminateList); - _TerminateList.Clear(); } catch (System.Exception) { @@ -163,7 +163,7 @@ public static void AppDomainGetTypes(Action action, string? dllNameWithout if (dllNameWithoutExtension != null && str != dllNameWithoutExtension) continue; - if (str == "AcInfoCenterConn") + if (str == "AcInfoCenterConn")//通讯库 continue; Type[]? types = null; @@ -206,52 +206,44 @@ public static void AppDomainGetTypes(Action action, string? dllNameWithout void GetInterfaceFunctions(List runClassList, string methodName) { const string sqid = nameof(Sequence) + "Id"; - + AppDomainGetTypes(type => { if (type.IsAbstract) - { return; - } + foreach (var inters in type.GetInterfaces()) { - if (inters.Name == nameof(IFoxAutoGo)) - { - Sequence? sequence = null; - MethodInfo? initialize = null; + if (inters.Name != nameof(IFoxAutoGo)) + continue; - foreach (var method in type.GetMethods()) - { - if (method.IsAbstract) - { - continue; - } - if (method.Name == sqid) - { - var obj = method.Invoke(); - if (obj is not null) - { - sequence = (Sequence)obj; - } - continue; - } - else if (method.Name == methodName) - { - initialize = method; - } - if (initialize is not null && sequence is not null) - { - break; - } - } - if (initialize is not null) + Sequence? sequence = null; + MethodInfo? initialize = null; + + foreach (var method in type.GetMethods()) + { + if (method.IsAbstract) + continue; + if (method.Name == sqid) { - var runc = sequence is not null ? - new RunClass(initialize, sequence.Value) : - new RunClass(initialize, Sequence.Last); - runClassList.Add(runc); + var obj = method.Invoke(); + if (obj is not null) + sequence = (Sequence)obj; + continue; } - break; + else if (method.Name == methodName) + initialize = method; + if (initialize is not null && sequence is not null) + break; } + + if (initialize is not null) + { + var runc = sequence is not null ? + new RunClass(initialize, sequence.Value) : + new RunClass(initialize, Sequence.Last); + runClassList.Add(runc); + } + break; } }, _DllName); @@ -260,14 +252,12 @@ void GetInterfaceFunctions(List runClassList, string methodName) /// /// 收集特性下的函数 /// - void GetAttributeFunctions() + void GetAttributeFunctions(List initialize, List terminate) { - AppDomainGetTypes(type => { if (type.IsAbstract) - { return; - } + var mets = type.GetMethods(); for (int ii = 0; ii < mets.Length; ii++) { @@ -279,13 +269,9 @@ void GetAttributeFunctions() { var runc = new RunClass(method, jjAtt.SequenceId); if (jjAtt.IsInitialize) - { - _InitializeList.Add(runc); - } + initialize.Add(runc); else - { - _TerminateList.Add(runc); - } + terminate.Add(runc); break; } } diff --git a/tests/Test/CmdINI.cs b/tests/Test/CmdINI.cs index 009690f..888aaa3 100644 --- a/tests/Test/CmdINI.cs +++ b/tests/Test/CmdINI.cs @@ -2,15 +2,24 @@ * 自动执行接口 * 这里必须要实现一次这个接口,才能使用 IFoxInitialize 特性进行自动执行 */ + +/// +/// 初始化注册表及反射 +/// 运行顺序 +/// 1: 构造函数 +/// 2: 特性..多个 +/// 3: 接口..多个 +/// 4: 构造函数 +/// public class CmdINI : AutoRegAssem { - //public override void Initialize() - //{ - //} - - //public override void Terminate() - //{ - //} + public CmdINI() + { + var dm = Application.DocumentManager; + var doc = dm.MdiActiveDocument; + var ed = doc.Editor; + ed.WriteMessage($"\n 开始自动执行{nameof(CmdINI)} \r\n"); + } } -- Gitee From 9f026a82b82bf1aff7bd16a1739e23bc7dd7769a Mon Sep 17 00:00:00 2001 From: liuqihong <540762622@qq.com> Date: Sat, 16 Jul 2022 01:51:45 +0800 Subject: [PATCH 298/675] =?UTF-8?q?=E4=BC=98=E5=8C=96=E6=B3=A8=E9=87=8A?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/IFoxCAD.Cad/ExtensionMethod/EntityEx.cs | 26 ++++++++------------- src/IFoxCAD.Cad/Runtime/AutoRegAssem.cs | 2 +- tests/Test/CmdINI.cs | 21 +++++++---------- 3 files changed, 20 insertions(+), 29 deletions(-) diff --git a/src/IFoxCAD.Cad/ExtensionMethod/EntityEx.cs b/src/IFoxCAD.Cad/ExtensionMethod/EntityEx.cs index 08b325b..0a331fb 100644 --- a/src/IFoxCAD.Cad/ExtensionMethod/EntityEx.cs +++ b/src/IFoxCAD.Cad/ExtensionMethod/EntityEx.cs @@ -198,8 +198,8 @@ public static void ValidateMirror(this DBText txt) /// 多行文字 /// 存储对象变量 /// 回调函数,用于处理炸散之后的对象 - /// MTextFragment -- 多行文字炸散后的对象 - /// MTextFragmentCallbackStatus -- 回调函数处理的结果 + /// 多行文字炸散后的对象 + /// 回调函数处理的结果 /// public static void ExplodeFragments(this MText mt, T obj, Func mTextFragmentCallback) { @@ -227,9 +227,8 @@ public static string GetUnFormatString(this MText mt) #region 圆弧 /// - /// 根据圆心、起点和终点来创建圆弧(二维) + /// 根据圆心、起点、终点来创建圆弧(二维) /// - /// 圆弧对象 /// 起点 /// 圆心 /// 终点 @@ -260,7 +259,7 @@ public static Arc CreateArc(Point3d startPoint, Point3d pointOnArc, Point3d endP //创建一个几何类的圆弧对象 CircularArc3d geArc = new(startPoint, pointOnArc, endPoint); //将几何类圆弧对象的圆心和半径赋值给圆弧 -#if ac2009 +#if NET35 return (Arc)geArc.ToCurve(); #else return (Arc)Curve.CreateFromGeCurve(geArc); @@ -331,7 +330,6 @@ public static Circle CreateCircle(Point3d startPoint, Point3d endPoint) /// /// 通过圆心,半径绘制圆形 /// - /// 图形数据库 /// 圆心 /// 半径 /// 图形的ObjectId @@ -414,19 +412,15 @@ public static void ClipBlockRef(this BlockReference bref, Point3d pt1, Point3d p /// 属性值字典 public static void ChangeBlockProperty(this BlockReference blockReference, Dictionary propertyNameValues) { - if (blockReference.IsDynamicBlock) + if (!blockReference.IsDynamicBlock) + return; + using (blockReference.ForWrite()) { - using (blockReference.ForWrite()) + foreach (DynamicBlockReferenceProperty item in blockReference.DynamicBlockReferencePropertyCollection) { - foreach (DynamicBlockReferenceProperty item in blockReference.DynamicBlockReferencePropertyCollection) - { - if (propertyNameValues.ContainsKey(item.PropertyName)) - { - item.Value = propertyNameValues[item.PropertyName]; - } - } + if (propertyNameValues.ContainsKey(item.PropertyName)) + item.Value = propertyNameValues[item.PropertyName]; } - } } diff --git a/src/IFoxCAD.Cad/Runtime/AutoRegAssem.cs b/src/IFoxCAD.Cad/Runtime/AutoRegAssem.cs index 9526946..a52a261 100644 --- a/src/IFoxCAD.Cad/Runtime/AutoRegAssem.cs +++ b/src/IFoxCAD.Cad/Runtime/AutoRegAssem.cs @@ -24,7 +24,7 @@ public enum AssemLoadType } /// -/// 初始化程序集信息,并写入注册表 +/// 初始化程序集信息写入注册表并反射特性和接口 /// public abstract class AutoRegAssem : IExtensionApplication { diff --git a/tests/Test/CmdINI.cs b/tests/Test/CmdINI.cs index 888aaa3..ba4b4fa 100644 --- a/tests/Test/CmdINI.cs +++ b/tests/Test/CmdINI.cs @@ -1,15 +1,12 @@ -/* - * 自动执行接口 - * 这里必须要实现一次这个接口,才能使用 IFoxInitialize 特性进行自动执行 - */ - -/// -/// 初始化注册表及反射 -/// 运行顺序 -/// 1: 构造函数 -/// 2: 特性..多个 -/// 3: 接口..多个 -/// 4: 构造函数 +/// +/// 自动执行接口: +/// 用于启动cad后初始化注册表及反射调用以下特性和接口 +/// netload的工程必须继承虚函数后才能使用特性和接口 +/// 运行顺序: +/// 1:构造函数 +/// 2:特性..多个 +/// 3:接口..多个 +/// 4:本类的构造函数 /// public class CmdINI : AutoRegAssem { -- Gitee From 6f68ea811281e8ff996f2ae7ce96626bc2e783ec Mon Sep 17 00:00:00 2001 From: liuqihong <540762622@qq.com> Date: Sat, 16 Jul 2022 22:40:42 +0800 Subject: [PATCH 299/675] =?UTF-8?q?=E4=BF=AE=E6=94=B9=E6=B3=A8=E9=87=8A?= =?UTF-8?q?=E5=8F=8A=E4=BF=AE=E6=94=B9AutoClass=E4=B8=BA=E9=9D=9E=E5=85=AC?= =?UTF-8?q?=E5=BC=80=E7=B1=BB?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/IFoxCAD.Cad/Runtime/AutoRegAssem.cs | 4 ++++ src/IFoxCAD.Cad/Runtime/IAutoGo.cs | 8 +++++--- tests/Test/CmdINI.cs | 4 ++-- 3 files changed, 11 insertions(+), 5 deletions(-) diff --git a/src/IFoxCAD.Cad/Runtime/AutoRegAssem.cs b/src/IFoxCAD.Cad/Runtime/AutoRegAssem.cs index a52a261..58b2d03 100644 --- a/src/IFoxCAD.Cad/Runtime/AutoRegAssem.cs +++ b/src/IFoxCAD.Cad/Runtime/AutoRegAssem.cs @@ -25,6 +25,10 @@ public enum AssemLoadType /// /// 初始化程序集信息写入注册表并反射特性和接口 +/// 启动cad后的执行顺序为: +/// 1:构造函数 +/// 2:特性..(多个) +/// 3:接口..(多个) /// public abstract class AutoRegAssem : IExtensionApplication { diff --git a/src/IFoxCAD.Cad/Runtime/IAutoGo.cs b/src/IFoxCAD.Cad/Runtime/IAutoGo.cs index a5297f3..10a5e7b 100644 --- a/src/IFoxCAD.Cad/Runtime/IAutoGo.cs +++ b/src/IFoxCAD.Cad/Runtime/IAutoGo.cs @@ -81,10 +81,12 @@ public void Run() } /// -/// 此类初始化要在调用类库上面进行一次,否则反射的项目不包含调用类 -/// 也就是谁引用了 谁负责在 接口上实例化 +/// 此类作为加载后cad自动运行接口的一部分,用于反射特性和接口 +/// 启动cad后的执行顺序为: +/// 1:特性..(多个) +/// 2:接口..(多个) /// -public class AutoClass //: IExtensionApplication +class AutoClass { static List _InitializeList = new(); //储存方法用于初始化 static List _TerminateList = new(); //储存方法用于结束释放 diff --git a/tests/Test/CmdINI.cs b/tests/Test/CmdINI.cs index ba4b4fa..e92e5ec 100644 --- a/tests/Test/CmdINI.cs +++ b/tests/Test/CmdINI.cs @@ -1,8 +1,8 @@ /// /// 自动执行接口: -/// 用于启动cad后初始化注册表及反射调用以下特性和接口 +/// 用于启动cad后写入启动注册表及反射调用以下特性和接口 /// netload的工程必须继承虚函数后才能使用特性和接口 -/// 运行顺序: +/// 启动cad后的执行顺序为: /// 1:构造函数 /// 2:特性..多个 /// 3:接口..多个 -- Gitee From b17f7a776a46e32fe31c3a9ba3045538c0d55829 Mon Sep 17 00:00:00 2001 From: liuqihong <540762622@qq.com> Date: Mon, 18 Jul 2022 22:07:55 +0800 Subject: [PATCH 300/675] =?UTF-8?q?=E6=B3=A8=E5=86=8C=E4=B8=AD=E5=BF=83?= =?UTF-8?q?=E5=A2=9E=E5=8A=A0=E5=8F=AF=E9=80=89=E9=85=8D=E7=BD=AE=E9=A1=B9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/IFoxCAD.Cad/Runtime/AssemInfo.cs | 12 ++-- src/IFoxCAD.Cad/Runtime/AutoRegAssem.cs | 76 ++++++++++++++++------- src/IFoxCAD.Cad/Runtime/IAutoGo.cs | 81 ++++++++++++++----------- tests/Test/CmdINI.cs | 6 +- 4 files changed, 112 insertions(+), 63 deletions(-) diff --git a/src/IFoxCAD.Cad/Runtime/AssemInfo.cs b/src/IFoxCAD.Cad/Runtime/AssemInfo.cs index 8b88d88..2a3e136 100644 --- a/src/IFoxCAD.Cad/Runtime/AssemInfo.cs +++ b/src/IFoxCAD.Cad/Runtime/AssemInfo.cs @@ -9,25 +9,25 @@ public struct AssemInfo /// /// 注册名 /// - public string Name { get; set; } + public string Name; /// /// 程序集全名 /// - public string Fullname { get; set; } + public string Fullname; /// /// 程序集路径 /// - public string Loader { get; set; } + public string Loader; /// /// 加载方式 /// - public AssemLoadType LoadType { get; set; } + public AssemLoadType LoadType; /// /// 程序集说明 /// - public string Description { get; set; } -} + public string Description; +} \ No newline at end of file diff --git a/src/IFoxCAD.Cad/Runtime/AutoRegAssem.cs b/src/IFoxCAD.Cad/Runtime/AutoRegAssem.cs index 58b2d03..a6e014d 100644 --- a/src/IFoxCAD.Cad/Runtime/AutoRegAssem.cs +++ b/src/IFoxCAD.Cad/Runtime/AutoRegAssem.cs @@ -23,18 +23,42 @@ public enum AssemLoadType Disabled = 20 } + /// -/// 初始化程序集信息写入注册表并反射特性和接口 +/// 注册中心配置信息 +/// +public enum AutoRegConfig +{ + /// + /// 注册表 + /// + Regedit = 1, + /// + /// 反射特性 + /// + ReflectionAttribute = 2, + /// + /// 反射接口 + /// + ReflectionInterface = 4, +} + +/// +/// 注册中心 +/// 初始化程序集信息写入注册表并反射特性和接口 /// 启动cad后的执行顺序为: -/// 1:构造函数 +/// 1:程序集配置中心构造函数 /// 2:特性..(多个) /// 3:接口..(多个) /// public abstract class AutoRegAssem : IExtensionApplication { - readonly AutoClass ac; - readonly AssemInfo Info; + #region 字段 + readonly AutoReflection _autoRef; + readonly AssemInfo _info; + #endregion + #region 静态方法 /// /// 程序集的路径 /// @@ -43,7 +67,7 @@ public abstract class AutoRegAssem : IExtensionApplication /// /// 程序集的目录 /// - public static DirectoryInfo CurrDirectory => Location.Directory; + public static DirectoryInfo CurrDirectory => Location.Directory; /// /// 获取程序集的目录 @@ -57,14 +81,20 @@ public static DirectoryInfo GetDirectory(Assembly? assem) return new FileInfo(assem.Location).Directory; } + #endregion + #region 构造函数 /// - /// 初始化程序集信息,并写入注册表 + /// 注册中心 /// - public AutoRegAssem() + /// 配置项目 + public AutoRegAssem(AutoRegConfig autoRegConfig/* = + AutoRegConfig.Regedit | + AutoRegConfig.ReflectionInterface | + AutoRegConfig.ReflectionAttribute*/) { - var assem = Assembly.GetCallingAssembly(); - Info = new() + var assem = Assembly.GetCallingAssembly(); + _info = new() { Loader = assem.Location, Fullname = assem.FullName, @@ -72,15 +102,19 @@ public AutoRegAssem() LoadType = AssemLoadType.Startting }; - if (!SearchForReg()) - RegApp(); + if ((autoRegConfig & AutoRegConfig.Regedit) == AutoRegConfig.Regedit) + { + if (!SearchForReg()) + RegApp(); + } //实例化了 AutoClass 之后会自动执行 IFoxAutoGo 接口下面的类, //以及自动执行特性 [IFoxInitialize] //类库用户不在此处进行其他代码,而是实现特性 - ac = new AutoClass(Info.Name); - ac.Initialize(); + _autoRef = new AutoReflection(_info.Name, autoRegConfig); + _autoRef.Initialize(); } + #endregion #region RegApp @@ -102,11 +136,11 @@ bool SearchForReg() return false; var regApps = appkey.GetSubKeyNames(); - if (regApps.Contains(Info.Name)) + if (regApps.Contains(_info.Name)) { //20220409 bug:文件名相同,路径不同,需要判断路径 - var info = appkey.OpenSubKey(Info.Name); - return info.GetValue("LOADER")?.ToString().ToLower() == Info.Loader.ToLower(); + var info = appkey.OpenSubKey(_info.Name); + return info.GetValue("LOADER")?.ToString().ToLower() == _info.Loader.ToLower(); } return false; } @@ -117,10 +151,10 @@ bool SearchForReg() void RegApp() { var appkey = GetAcAppKey(); - var rk = appkey.CreateSubKey(Info.Name); - rk.SetValue("DESCRIPTION", Info.Fullname, RegistryValueKind.String); - rk.SetValue("LOADCTRLS", Info.LoadType, RegistryValueKind.DWord); - rk.SetValue("LOADER", Info.Loader, RegistryValueKind.String); + var rk = appkey.CreateSubKey(_info.Name); + rk.SetValue("DESCRIPTION", _info.Fullname, RegistryValueKind.String); + rk.SetValue("LOADCTRLS", _info.LoadType, RegistryValueKind.DWord); + rk.SetValue("LOADER", _info.Loader, RegistryValueKind.String); rk.SetValue("MANAGED", 1, RegistryValueKind.DWord); appkey.Close(); } @@ -131,7 +165,7 @@ public void Terminate() { } ~AutoRegAssem() { - ac.Terminate(); + _autoRef.Terminate(); } #endregion RegApp } diff --git a/src/IFoxCAD.Cad/Runtime/IAutoGo.cs b/src/IFoxCAD.Cad/Runtime/IAutoGo.cs index 10a5e7b..bf362ad 100644 --- a/src/IFoxCAD.Cad/Runtime/IAutoGo.cs +++ b/src/IFoxCAD.Cad/Runtime/IAutoGo.cs @@ -69,14 +69,7 @@ public RunClass(MethodInfo method, Sequence sequence) /// public void Run() { - try - { - _methodInfo.Invoke(); - } - catch (System.Exception) - { - Debugger.Break(); - } + _methodInfo.Invoke(); } } @@ -86,19 +79,24 @@ public void Run() /// 1:特性..(多个) /// 2:接口..(多个) /// -class AutoClass +public class AutoReflection { static List _InitializeList = new(); //储存方法用于初始化 static List _TerminateList = new(); //储存方法用于结束释放 - readonly string _DllName; + readonly string _dllName; + readonly AutoRegConfig _autoRegConfig; + /// - /// 反射此特性:进行加载时自动运行 + /// 反射执行 + /// 1.特性: + /// 2.接口: /// - /// 约束在此dll进行加速 - public AutoClass(string DllName) + /// 约束在此dll进行加速 + public AutoReflection(string dllName, AutoRegConfig configInfo) { - _DllName = DllName; + _dllName = dllName; + _autoRegConfig = configInfo; } //启动cad的时候会自动执行 @@ -107,12 +105,18 @@ public void Initialize() try { //收集特性,包括启动时和关闭时 - GetAttributeFunctions(_InitializeList,_TerminateList); + if ((_autoRegConfig & AutoRegConfig.ReflectionAttribute) == AutoRegConfig.ReflectionAttribute) + GetAttributeFunctions(_InitializeList, _TerminateList); - GetInterfaceFunctions(_InitializeList, nameof(Initialize)); - //按照 SequenceId 排序_升序 - _InitializeList = _InitializeList.OrderBy(runClass => runClass.Sequence).ToList(); - AutoClass.RunFunctions(_InitializeList); + if ((_autoRegConfig & AutoRegConfig.ReflectionInterface) == AutoRegConfig.ReflectionInterface) + GetInterfaceFunctions(_InitializeList, nameof(Initialize)); + + if (_InitializeList.Count > 0) + { + //按照 SequenceId 排序_升序 + _InitializeList = _InitializeList.OrderBy(runClass => runClass.Sequence).ToList(); + RunFunctions(_InitializeList); + } } catch (System.Exception) { @@ -125,10 +129,15 @@ public void Terminate() { try { - GetInterfaceFunctions(_TerminateList, nameof(Terminate)); - //按照 SequenceId 排序_降序 - _TerminateList = _TerminateList.OrderByDescending(runClass => runClass.Sequence).ToList(); - AutoClass.RunFunctions(_TerminateList); + if ((_autoRegConfig & AutoRegConfig.ReflectionInterface) == AutoRegConfig.ReflectionInterface) + GetInterfaceFunctions(_TerminateList, nameof(Terminate)); + + if (_TerminateList.Count > 0) + { + //按照 SequenceId 排序_降序 + _TerminateList = _TerminateList.OrderByDescending(runClass => runClass.Sequence).ToList(); + RunFunctions(_TerminateList); + } } catch (System.Exception) { @@ -213,18 +222,23 @@ void GetInterfaceFunctions(List runClassList, string methodName) if (type.IsAbstract) return; - foreach (var inters in type.GetInterfaces()) + var ints = type.GetInterfaces(); + for (int sss = 0; sss < ints.Length; sss++) { + var inters = ints[sss]; if (inters.Name != nameof(IFoxAutoGo)) continue; Sequence? sequence = null; MethodInfo? initialize = null; - foreach (var method in type.GetMethods()) + var mets = type.GetMethods(); + for (int jj = 0; jj < mets.Length; jj++) { + var method = mets[jj]; if (method.IsAbstract) continue; + if (method.Name == sqid) { var obj = method.Invoke(); @@ -234,20 +248,19 @@ void GetInterfaceFunctions(List runClassList, string methodName) } else if (method.Name == methodName) initialize = method; + if (initialize is not null && sequence is not null) break; } - if (initialize is not null) - { - var runc = sequence is not null ? - new RunClass(initialize, sequence.Value) : - new RunClass(initialize, Sequence.Last); - runClassList.Add(runc); - } + if (initialize is null) + continue; + + var seq = sequence is null ? Sequence.Last : sequence.Value; + runClassList.Add(new RunClass(initialize, seq)); break; } - }, _DllName); + }, _dllName); } @@ -278,7 +291,7 @@ void GetAttributeFunctions(List initialize, List terminate) } } } - }, _DllName); + }, _dllName); } /// diff --git a/tests/Test/CmdINI.cs b/tests/Test/CmdINI.cs index e92e5ec..caf73ce 100644 --- a/tests/Test/CmdINI.cs +++ b/tests/Test/CmdINI.cs @@ -1,5 +1,5 @@ /// -/// 自动执行接口: +/// 注册中心(自动执行接口): /// 用于启动cad后写入启动注册表及反射调用以下特性和接口 /// netload的工程必须继承虚函数后才能使用特性和接口 /// 启动cad后的执行顺序为: @@ -10,7 +10,9 @@ /// public class CmdINI : AutoRegAssem { - public CmdINI() + public CmdINI() : base(AutoRegConfig.Regedit | + AutoRegConfig.ReflectionInterface | + AutoRegConfig.ReflectionAttribute) { var dm = Application.DocumentManager; var doc = dm.MdiActiveDocument; -- Gitee From c18b4b7f2ff3d22c1fba2ccd0b9384ac5186bc21 Mon Sep 17 00:00:00 2001 From: liuqihong <540762622@qq.com> Date: Mon, 18 Jul 2022 22:38:55 +0800 Subject: [PATCH 301/675] =?UTF-8?q?=E6=9E=9A=E4=B8=BE=E7=AE=80=E5=8C=96?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/IFoxCAD.Cad/Runtime/AutoRegAssem.cs | 13 ++++++------- tests/Test/CmdINI.cs | 4 +--- 2 files changed, 7 insertions(+), 10 deletions(-) diff --git a/src/IFoxCAD.Cad/Runtime/AutoRegAssem.cs b/src/IFoxCAD.Cad/Runtime/AutoRegAssem.cs index a6e014d..113b6d4 100644 --- a/src/IFoxCAD.Cad/Runtime/AutoRegAssem.cs +++ b/src/IFoxCAD.Cad/Runtime/AutoRegAssem.cs @@ -41,8 +41,10 @@ public enum AutoRegConfig /// 反射接口 /// ReflectionInterface = 4, + + All = Regedit | ReflectionAttribute | ReflectionInterface, } - + /// /// 注册中心 /// 初始化程序集信息写入注册表并反射特性和接口 @@ -67,7 +69,7 @@ public abstract class AutoRegAssem : IExtensionApplication /// /// 程序集的目录 /// - public static DirectoryInfo CurrDirectory => Location.Directory; + public static DirectoryInfo CurrDirectory => Location.Directory; /// /// 获取程序集的目录 @@ -88,12 +90,9 @@ public static DirectoryInfo GetDirectory(Assembly? assem) /// 注册中心 /// /// 配置项目 - public AutoRegAssem(AutoRegConfig autoRegConfig/* = - AutoRegConfig.Regedit | - AutoRegConfig.ReflectionInterface | - AutoRegConfig.ReflectionAttribute*/) + public AutoRegAssem(AutoRegConfig autoRegConfig) { - var assem = Assembly.GetCallingAssembly(); + var assem = Assembly.GetCallingAssembly(); _info = new() { Loader = assem.Location, diff --git a/tests/Test/CmdINI.cs b/tests/Test/CmdINI.cs index caf73ce..2257dd8 100644 --- a/tests/Test/CmdINI.cs +++ b/tests/Test/CmdINI.cs @@ -10,9 +10,7 @@ /// public class CmdINI : AutoRegAssem { - public CmdINI() : base(AutoRegConfig.Regedit | - AutoRegConfig.ReflectionInterface | - AutoRegConfig.ReflectionAttribute) + public CmdINI() : base(AutoRegConfig.All) { var dm = Application.DocumentManager; var doc = dm.MdiActiveDocument; -- Gitee From bb4cd9a07d15197416c8cab4bbfde70f5319076e Mon Sep 17 00:00:00 2001 From: liuqihong <540762622@qq.com> Date: Mon, 18 Jul 2022 22:52:07 +0800 Subject: [PATCH 302/675] =?UTF-8?q?=E4=BC=98=E5=8C=96=E4=BB=A3=E7=A0=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/IFoxCAD.Cad/Runtime/IAutoGo.cs | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/src/IFoxCAD.Cad/Runtime/IAutoGo.cs b/src/IFoxCAD.Cad/Runtime/IAutoGo.cs index bf362ad..a8d2747 100644 --- a/src/IFoxCAD.Cad/Runtime/IAutoGo.cs +++ b/src/IFoxCAD.Cad/Runtime/IAutoGo.cs @@ -161,7 +161,8 @@ public static void AppDomainGetTypes(Action action, string? dllNameWithout #if !NET35 //cad2021出现如下报错 //System.NotSupportedException:动态程序集中不支持已调用的成员 - assemblies = assemblies.Where(p => !p.IsDynamic).ToArray(); + //assemblies = assemblies.Where(p => !p.IsDynamic).ToArray();//这个要容器类型转换 + assemblies = Array.FindAll(assemblies, p => !p.IsDynamic); #endif //主程序域 for (int ii = 0; ii < assemblies.Length; ii++) @@ -170,11 +171,10 @@ public static void AppDomainGetTypes(Action action, string? dllNameWithout //获取类型集合,反射时候还依赖其他的dll就会这个错误 //此通讯库要跳过,否则会报错. - var str = Path.GetFileNameWithoutExtension(assembly.Location); - if (dllNameWithoutExtension != null && - str != dllNameWithoutExtension) + var location = Path.GetFileNameWithoutExtension(assembly.Location); + if (dllNameWithoutExtension != null && location != dllNameWithoutExtension) continue; - if (str == "AcInfoCenterConn")//通讯库 + if (location == "AcInfoCenterConn")//通讯库 continue; Type[]? types = null; @@ -186,6 +186,7 @@ public static void AppDomainGetTypes(Action action, string? dllNameWithout if (types is null) continue; + for (int jj = 0; jj < types.Length; jj++) { var type = types[jj]; -- Gitee From ca92ead27f8d5a29667306fc04d7d587ec1acea2 Mon Sep 17 00:00:00 2001 From: liuqihong <540762622@qq.com> Date: Tue, 19 Jul 2022 01:10:53 +0800 Subject: [PATCH 303/675] =?UTF-8?q?=E8=B0=83=E6=95=B4=E6=96=87=E4=BB=B6?= =?UTF-8?q?=E4=BD=8D=E7=BD=AE/=E8=A1=A5=E5=85=85=E5=8D=B8=E8=BD=BD?= =?UTF-8?q?=E6=B3=A8=E5=86=8C=E8=A1=A8=E6=93=8D=E4=BD=9C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/IFoxCAD.Cad/Runtime/AssemInfo.cs | 46 +++++++++- src/IFoxCAD.Cad/Runtime/AutoRegAssem.cs | 94 ++++++++++----------- src/IFoxCAD.Cad/Runtime/IAutoGo.cs | 1 - src/IFoxCAD.Cad/Runtime/MethodInfoHelper.cs | 53 ++++++++++++ src/IFoxCAD.Cad/Runtime/Utils.cs | 52 ------------ tests/Test/CmdINI.cs | 9 ++ 6 files changed, 154 insertions(+), 101 deletions(-) create mode 100644 src/IFoxCAD.Cad/Runtime/MethodInfoHelper.cs diff --git a/src/IFoxCAD.Cad/Runtime/AssemInfo.cs b/src/IFoxCAD.Cad/Runtime/AssemInfo.cs index 2a3e136..c13cc26 100644 --- a/src/IFoxCAD.Cad/Runtime/AssemInfo.cs +++ b/src/IFoxCAD.Cad/Runtime/AssemInfo.cs @@ -30,4 +30,48 @@ public struct AssemInfo /// 程序集说明 /// public string Description; -} \ No newline at end of file +} + + +/// +/// 程序集加载类型 +/// +public enum AssemLoadType +{ + /// + /// 启动 + /// + Startting = 2, + + /// + /// 随命令 + /// + ByCommand = 12, + + /// + /// 无效 + /// + Disabled = 20 +} + + +/// +/// 注册中心配置信息 +/// +public enum AutoRegConfig +{ + /// + /// 注册表 + /// + Regedit = 1, + /// + /// 反射特性 + /// + ReflectionAttribute = 2, + /// + /// 反射接口 + /// + ReflectionInterface = 4, + + All = Regedit | ReflectionAttribute | ReflectionInterface, +} diff --git a/src/IFoxCAD.Cad/Runtime/AutoRegAssem.cs b/src/IFoxCAD.Cad/Runtime/AutoRegAssem.cs index 113b6d4..2f0aeff 100644 --- a/src/IFoxCAD.Cad/Runtime/AutoRegAssem.cs +++ b/src/IFoxCAD.Cad/Runtime/AutoRegAssem.cs @@ -2,49 +2,6 @@ using Registry = Microsoft.Win32.Registry; using RegistryKey = Microsoft.Win32.RegistryKey; -/// -/// 程序集加载类型 -/// -public enum AssemLoadType -{ - /// - /// 启动 - /// - Startting = 2, - - /// - /// 随命令 - /// - ByCommand = 12, - - /// - /// 无效 - /// - Disabled = 20 -} - - -/// -/// 注册中心配置信息 -/// -public enum AutoRegConfig -{ - /// - /// 注册表 - /// - Regedit = 1, - /// - /// 反射特性 - /// - ReflectionAttribute = 2, - /// - /// 反射接口 - /// - ReflectionInterface = 4, - - All = Regedit | ReflectionAttribute | ReflectionInterface, -} - /// /// 注册中心 /// 初始化程序集信息写入注册表并反射特性和接口 @@ -117,17 +74,60 @@ public AutoRegAssem(AutoRegConfig autoRegConfig) #region RegApp - static RegistryKey GetAcAppKey() + /// + /// 获取当前cad注册表位置 + /// + /// 打开权限 + /// + public static RegistryKey GetAcAppKey(bool writable = true) { + RegistryKey? ackey = null; + #if NET35 - string key = HostApplicationServices.Current.RegistryProductRootKey; + string key = HostApplicationServices.Current.RegistryProductRootKey; //这里浩辰读出来是"" +#elif !HC2020 + string key = HostApplicationServices.Current.UserRegistryProductRootKey; +#endif + +#if !HC2020 + ackey = Registry.CurrentUser.OpenSubKey(key, writable); #else - string key = HostApplicationServices.Current.MachineRegistryProductRootKey; + //浩辰 + var s = GrxCAD.DatabaseServices.HostApplicationServices.Current.RegistryProductRootKey;//浩辰奇怪的空值 + string str = CadSystem.Getvar("ACADVER"); + str = Regex.Replace(str, @"[^\d.\d]", ""); + double.TryParse(str, out double a); + // string regedit = @"Software\Gstarsoft\GstarCAD\R" + a.ToString() + @"\zh-CN"; //2019 + // string regedit = @"Software\Gstarsoft\GstarCAD\B" + a.ToString() + @"\zh-CN";//2020 这里是 + string regedit = @"Software\Gstarsoft\GstarCAD\B20\zh-CN";//2020 这里是 + + ackey = Registry.CurrentUser.OpenSubKey(regedit, writable); #endif - var ackey = Registry.CurrentUser.OpenSubKey(key, true); return ackey.CreateSubKey("Applications"); } + /// + /// 卸载自动运行注册表 + /// + public bool UnAcAppKey() + { + var appkey = GetAcAppKey(); + if (appkey.SubKeyCount == 0) + return false; + + var regApps = appkey.GetSubKeyNames(); + foreach (var item in regApps) + { + if (item == _info.Name) + { + appkey.DeleteSubKey(item, false); + break; + } + } + return true; + } + + bool SearchForReg() { var appkey = GetAcAppKey(); diff --git a/src/IFoxCAD.Cad/Runtime/IAutoGo.cs b/src/IFoxCAD.Cad/Runtime/IAutoGo.cs index a8d2747..a2cbbb4 100644 --- a/src/IFoxCAD.Cad/Runtime/IAutoGo.cs +++ b/src/IFoxCAD.Cad/Runtime/IAutoGo.cs @@ -55,7 +55,6 @@ public IFoxInitialize(Sequence sequence = Sequence.Last, bool isInitialize = tru class RunClass { public Sequence Sequence { get; } - readonly MethodInfo _methodInfo; public RunClass(MethodInfo method, Sequence sequence) diff --git a/src/IFoxCAD.Cad/Runtime/MethodInfoHelper.cs b/src/IFoxCAD.Cad/Runtime/MethodInfoHelper.cs new file mode 100644 index 0000000..c64a5f8 --- /dev/null +++ b/src/IFoxCAD.Cad/Runtime/MethodInfoHelper.cs @@ -0,0 +1,53 @@ +namespace IFoxCAD.Cad; + +internal static class MethodInfoHelper +{ + private static readonly Dictionary methodDic = new(); + + /// + /// 执行函数 + /// + /// 函数 + /// 已经外部创建的对象,为空则此处创建 + public static object? Invoke(this MethodInfo methodInfo, object? instance = null) + { + if (methodInfo == null) + throw new ArgumentNullException(nameof(methodInfo)); + + object? result = null; + if (methodInfo.IsStatic) + { + //新函数指针进入此处 + //参数数量一定要匹配,为null则参数个数不同导致报错, + //参数为stirng[],则可以传入object[]代替,其他参数是否还可以实现默认构造? + var paramInfos = methodInfo.GetParameters(); + var args = new List { }; + for (int i = 0; i < paramInfos.Length; i++) + args.Add(null!); + result = methodInfo.Invoke(null, args.ToArray());//静态调用 + } + else + { + //原命令的函数指针进入此处 + //object instance; + if (methodDic.ContainsKey(methodInfo)) + instance = methodDic[methodInfo]; + + if (instance == null) + { + var reftype = methodInfo.ReflectedType; + if (reftype == null) return null; + var fullName = reftype.FullName; //命名空间+类 + if (fullName == null) return null; + var type = reftype.Assembly.GetType(fullName);//通过程序集反射创建类+ + if (type == null) return null; + instance = Activator.CreateInstance(type); + if (!type.IsAbstract)//无法创建抽象类成员 + methodDic.Add(methodInfo, instance); + } + if (instance != null) + result = methodInfo.Invoke(instance, null); //非静态,调用实例化方法 + } + return result; + } +} \ No newline at end of file diff --git a/src/IFoxCAD.Cad/Runtime/Utils.cs b/src/IFoxCAD.Cad/Runtime/Utils.cs index 8693fc1..eb372ab 100644 --- a/src/IFoxCAD.Cad/Runtime/Utils.cs +++ b/src/IFoxCAD.Cad/Runtime/Utils.cs @@ -95,56 +95,4 @@ public static ObjectId TryGetObjectId(Database db, Handle handle) // return vernum + 986; //} -} - -internal static class MethodInfoHelper -{ - private static readonly Dictionary methodDic = new(); - - /// - /// 执行函数 - /// - /// 函数 - /// 已经外部创建的对象,为空则此处创建 - public static object? Invoke(this MethodInfo methodInfo, object? instance = null) - { - if (methodInfo == null) - throw new ArgumentNullException(nameof(methodInfo)); - - object? result = null; - if (methodInfo.IsStatic) - { - //新函数指针进入此处 - //参数数量一定要匹配,为null则参数个数不同导致报错, - //参数为stirng[],则可以传入object[]代替,其他参数是否还可以实现默认构造? - var paramInfos = methodInfo.GetParameters(); - var args = new List { }; - for (int i = 0; i < paramInfos.Length; i++) - args.Add(null!); - result = methodInfo.Invoke(null, args.ToArray());//静态调用 - } - else - { - //原命令的函数指针进入此处 - //object instance; - if (methodDic.ContainsKey(methodInfo)) - instance = methodDic[methodInfo]; - - if (instance == null) - { - var reftype = methodInfo.ReflectedType; - if (reftype == null) return null; - var fullName = reftype.FullName; //命名空间+类 - if (fullName == null) return null; - var type = reftype.Assembly.GetType(fullName);//通过程序集反射创建类+ - if (type == null) return null; - instance = Activator.CreateInstance(type); - if (!type.IsAbstract)//无法创建抽象类成员 - methodDic.Add(methodInfo, instance); - } - if (instance != null) - result = methodInfo.Invoke(instance, null); //非静态,调用实例化方法 - } - return result; - } } \ No newline at end of file diff --git a/tests/Test/CmdINI.cs b/tests/Test/CmdINI.cs index 2257dd8..c44dd64 100644 --- a/tests/Test/CmdINI.cs +++ b/tests/Test/CmdINI.cs @@ -17,6 +17,15 @@ public CmdINI() : base(AutoRegConfig.All) var ed = doc.Editor; ed.WriteMessage($"\n 开始自动执行{nameof(CmdINI)} \r\n"); } + + /// + /// 卸载注册表信息 + /// + [CommandMethod("IFoxUnLoad")] + public void IFoxUnLoad() + { + base.UnAcAppKey(); + } } -- Gitee From 8125d1621085fdb354eb562189bd03141a512e43 Mon Sep 17 00:00:00 2001 From: liuqihong <540762622@qq.com> Date: Tue, 19 Jul 2022 01:20:53 +0800 Subject: [PATCH 304/675] =?UTF-8?q?=E6=9A=B4=E9=9C=B2=E6=B3=A8=E5=86=8C?= =?UTF-8?q?=E5=87=BD=E6=95=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/IFoxCAD.Cad/Runtime/AutoRegAssem.cs | 18 +++++++++--------- tests/Test/CmdINI.cs | 13 +++++++++++-- 2 files changed, 20 insertions(+), 11 deletions(-) diff --git a/src/IFoxCAD.Cad/Runtime/AutoRegAssem.cs b/src/IFoxCAD.Cad/Runtime/AutoRegAssem.cs index 2f0aeff..2876898 100644 --- a/src/IFoxCAD.Cad/Runtime/AutoRegAssem.cs +++ b/src/IFoxCAD.Cad/Runtime/AutoRegAssem.cs @@ -116,18 +116,18 @@ public bool UnAcAppKey() return false; var regApps = appkey.GetSubKeyNames(); - foreach (var item in regApps) + if (regApps.Contains(_info.Name)) { - if (item == _info.Name) - { - appkey.DeleteSubKey(item, false); - break; - } + appkey.DeleteSubKey(_info.Name, false); + return true; } - return true; + return false; } - + /// + /// 是否已经存在注册表 + /// + /// bool SearchForReg() { var appkey = GetAcAppKey(); @@ -147,7 +147,7 @@ bool SearchForReg() /// /// 在注册表写入自动加载的程序集信息 /// - void RegApp() + public void RegApp() { var appkey = GetAcAppKey(); var rk = appkey.CreateSubKey(_info.Name); diff --git a/tests/Test/CmdINI.cs b/tests/Test/CmdINI.cs index c44dd64..44e26b8 100644 --- a/tests/Test/CmdINI.cs +++ b/tests/Test/CmdINI.cs @@ -18,11 +18,20 @@ public CmdINI() : base(AutoRegConfig.All) ed.WriteMessage($"\n 开始自动执行{nameof(CmdINI)} \r\n"); } + //如果netload之后用IFoxUnLoad删除注册表, + //由于不能卸载dll,再netload是无法执行自动接口的, + //所以此时会产生无法再注册的问题...所以需要暴露此注册函数(硬来) + [CommandMethod("IFoxAddReg")] + public void IFoxAddReg() + { + base.RegApp(); + } + /// /// 卸载注册表信息 /// - [CommandMethod("IFoxUnLoad")] - public void IFoxUnLoad() + [CommandMethod("IFoxRemoveReg")] + public void IFoxRemoveReg() { base.UnAcAppKey(); } -- Gitee From 4844be475d84e8c500371ce6c114881680da2436 Mon Sep 17 00:00:00 2001 From: liuqihong <540762622@qq.com> Date: Tue, 19 Jul 2022 08:18:10 +0800 Subject: [PATCH 305/675] =?UTF-8?q?=E8=B0=83=E6=95=B4=E4=BD=8D=E7=BD=AE?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/IFoxCAD.Basal/IFoxCAD.Basal.csproj | 17 ++-- src/IFoxCAD.Cad/IFoxCAD.Cad.csproj | 125 +++++++++++++------------ src/IFoxCAD.WPF/IFoxCAD.WPF.csproj | 17 ++-- tests/Test/Test.csproj | 10 +- tests/TestConsole/TestConsole.csproj | 23 +++-- 5 files changed, 99 insertions(+), 93 deletions(-) diff --git a/src/IFoxCAD.Basal/IFoxCAD.Basal.csproj b/src/IFoxCAD.Basal/IFoxCAD.Basal.csproj index 52f00bf..63d8d56 100644 --- a/src/IFoxCAD.Basal/IFoxCAD.Basal.csproj +++ b/src/IFoxCAD.Basal/IFoxCAD.Basal.csproj @@ -1,9 +1,11 @@ + preview + enable + net35;net40;net45 true - enable 0.3.5.1 InspireFunction xsfhlzh;vicwjb @@ -15,7 +17,6 @@ IFoxCAD;C#;NET;Common;Basal 直接集成元组和索引切片. true - preview true LICENSE true @@ -48,21 +49,21 @@ $(Configuration);ac2015 - 1701;1702; + 1701;1702; - 1701;1702; + 1701;1702; - 1701;1702; + 1701;1702; - 1701;1702; + 1701;1702; - 1701;1702; + 1701;1702; - 1701;1702; + 1701;1702; diff --git a/src/IFoxCAD.Cad/IFoxCAD.Cad.csproj b/src/IFoxCAD.Cad/IFoxCAD.Cad.csproj index 74f09b9..9a025cd 100644 --- a/src/IFoxCAD.Cad/IFoxCAD.Cad.csproj +++ b/src/IFoxCAD.Cad/IFoxCAD.Cad.csproj @@ -1,11 +1,13 @@ + preview + enable + net35;net40;net45 true - enable - true - true + true + true 0.3.6.1 InspireFunction xsfhlzh;vicwjb @@ -18,83 +20,82 @@ 增加四叉树测试. true true - preview true LICENSE true x64 - - - + + + + runtime + + + + + runtime - - - runtime - - + + + runtime + + + 4.3.0 + + + - - - runtime - - - 4.3.0 - - - - - - - DEBUG - - - $(Configuration);ac2009 - - - $(Configuration);ac2013 - - - $(Configuration);ac2015 - - - 1701;1702;CS1685 - - - 1701;1702;CS1685 - - - 1701;1702;CS1685 - - - 1701;1702;CS1685 - - - 1701;1702;CS1685 - - - 1701;1702;CS1685 - - + + DEBUG + + + $(Configuration);ac2009 + + + $(Configuration);ac2013 + + + $(Configuration);ac2015 + + + 1701;1702;CS1685 + + + 1701;1702;CS1685 + + + 1701;1702;CS1685 + + + 1701;1702;CS1685 + + + 1701;1702;CS1685 + + + 1701;1702;CS1685 + + - - True - - + + True + + - + - + - + - + diff --git a/src/IFoxCAD.WPF/IFoxCAD.WPF.csproj b/src/IFoxCAD.WPF/IFoxCAD.WPF.csproj index 8c47b25..9fa74ff 100644 --- a/src/IFoxCAD.WPF/IFoxCAD.WPF.csproj +++ b/src/IFoxCAD.WPF/IFoxCAD.WPF.csproj @@ -1,8 +1,10 @@  + preview + enable + net45 - enable true true true @@ -18,22 +20,21 @@ IFoxCAD;C#;NET;WPF;MVVM git 开启可空类型. - preview - DEBUG;TRACE + DEBUG;TRACE - + - - True - - + + True + + diff --git a/tests/Test/Test.csproj b/tests/Test/Test.csproj index d6a57d5..8e3469a 100644 --- a/tests/Test/Test.csproj +++ b/tests/Test/Test.csproj @@ -3,19 +3,19 @@ preview - + net45 true true - x64 + x64 - 1701;1702;CS1685 + 1701;1702;CS1685 - 1701;1702;CS1685 + 1701;1702;CS1685 @@ -23,6 +23,6 @@ - + diff --git a/tests/TestConsole/TestConsole.csproj b/tests/TestConsole/TestConsole.csproj index c809de4..293a6aa 100644 --- a/tests/TestConsole/TestConsole.csproj +++ b/tests/TestConsole/TestConsole.csproj @@ -1,19 +1,22 @@  - - Exe - net45 - enable - preview - + + preview + enable + + Exe + net45 + enable + preview + + + + + - - - - -- Gitee From 1d7d7b80bd10f221b56e23a909610ed9682cddb3 Mon Sep 17 00:00:00 2001 From: liuqihong <540762622@qq.com> Date: Tue, 19 Jul 2022 08:18:32 +0800 Subject: [PATCH 306/675] =?UTF-8?q?=E4=BC=98=E5=8C=96=E6=B3=A8=E9=87=8A?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- tests/Test/CmdINI.cs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/tests/Test/CmdINI.cs b/tests/Test/CmdINI.cs index 44e26b8..69f62a3 100644 --- a/tests/Test/CmdINI.cs +++ b/tests/Test/CmdINI.cs @@ -18,9 +18,9 @@ public CmdINI() : base(AutoRegConfig.All) ed.WriteMessage($"\n 开始自动执行{nameof(CmdINI)} \r\n"); } - //如果netload之后用IFoxUnLoad删除注册表, - //由于不能卸载dll,再netload是无法执行自动接口的, - //所以此时会产生无法再注册的问题...所以需要暴露此注册函数(硬来) + ///如果netload之后用 删除注册表, + ///由于不是也不能卸载dll,再netload是无法执行自动接口的, + ///所以此时会产生无法再注册的问题...因此需要暴露此注册函数(硬来) [CommandMethod("IFoxAddReg")] public void IFoxAddReg() { -- Gitee From b076dffcffac6e316cd7e8ffd7dbcd8c8d006ea2 Mon Sep 17 00:00:00 2001 From: liuqihong <540762622@qq.com> Date: Tue, 19 Jul 2022 08:22:44 +0800 Subject: [PATCH 307/675] =?UTF-8?q?=E5=A2=9E=E5=8A=A0=E6=97=A5=E5=BF=97?= =?UTF-8?q?=E7=B1=BB(https://gitee.com/inspirefunction/ifoxcad/issues/I53R?= =?UTF-8?q?IR)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/IFoxCAD.Cad/Runtime/Log.cs | 134 +++++++++++++++++++++++++++++++++ 1 file changed, 134 insertions(+) create mode 100644 src/IFoxCAD.Cad/Runtime/Log.cs diff --git a/src/IFoxCAD.Cad/Runtime/Log.cs b/src/IFoxCAD.Cad/Runtime/Log.cs new file mode 100644 index 0000000..fe7b24f --- /dev/null +++ b/src/IFoxCAD.Cad/Runtime/Log.cs @@ -0,0 +1,134 @@ +namespace IFoxCAD.Cad; + +using System; +using System.Diagnostics; +using System.IO; +using System.Text; +using System.Threading; + +public static class Log +{ + /// + /// 读写锁 + /// 当资源处于写入模式时,其他线程写入需要等待本次写入结束之后才能继续写入 + /// + static readonly ReaderWriterLockSlim _logWriteLock = new(); + static readonly object _syncObject = new(); + + /// + /// 日志文件完整路径 + /// + static readonly string _logAddress; + + static Log() + { + lock (_syncObject) + { + //调用静态方法的时候才会到构造函数,而且仅第一次时候才执行一次 + var sb = new StringBuilder(); + sb.Append(Environment.CurrentDirectory); + sb.Append("\\ErrorLog"); + + //新建文件夹 + var path = sb.ToString(); + if (!Directory.Exists(path)) + { + Directory.CreateDirectory(path) + .Attributes = FileAttributes.Normal; //设置文件夹属性为普通 + } + + sb.Append('\\'); + sb.Append(DateTime.Now.ToString("yy-MM-dd")); + sb.Append(".log"); + _logAddress = sb.ToString(); + } + } + + + /// + /// 将异常打印到日志文件 + /// + /// 异常 + /// 备注 + public static string? WriteLog(this System.Exception? ex, object? remarks = null) + { + if (ex == null) + return null; + + try + { + _logWriteLock.EnterWriteLock(); + + lock (_syncObject) + { + var logtxt = new LogTxt(ex, remarks); + //var logtxtJson = Newtonsoft.Json.JsonConvert.SerializeObject(logtxt, Formatting.Indented); + var logtxtJson = logtxt.ToString(); + + //把异常信息输出到文件 + var sw = new StreamWriter(_logAddress, true/*当天日志文件存在就追加,否则就创建*/); + sw.Write(logtxtJson); + sw.Flush(); + sw.Close(); + sw.Dispose(); +#if DEBUG + Debug.WriteLine("错误日志: " + _logAddress); + Debug.Write(logtxtJson); + return logtxtJson; + //Debugger.Break(); + //Debug.Assert(false, "终止进程"); +#endif + } + } + finally + { + _logWriteLock.ExitWriteLock(); + } + } +} + +[Serializable] +public class LogTxt +{ + public string 当前时间; + public string 备注信息; + public string 异常信息; + public string 异常对象; + public string? 触发方法; + public string? 调用堆栈; + + public LogTxt(System.Exception ex, object? remarks = null) + { + // 以不同语言显示日期 + // DateTime.Now.ToString("f", new System.Globalization.CultureInfo("es-ES")) + // DateTime.Now.ToString("f", new System.Globalization.CultureInfo("zh-cn")) + // 为了最小信息熵,所以用这样的格式,并且我喜欢补0 + this.当前时间 = DateTime.Now.ToString("yy-MM-dd hh:mm:ss"); + + if (remarks == null) + this.备注信息 = ""; + else + this.备注信息 = remarks.ToString(); + this.异常信息 = ex.Message; + this.异常对象 = ex.Source; + this.触发方法 = ex.TargetSite?.ToString(); + this.调用堆栈 = ex.StackTrace?.Trim(); + } + + /// 为了不引入json的dll,所以这里自己构造 + public override string ToString() + { + var sb = new StringBuilder(); + sb.Append('{'); + sb.Append(Environment.NewLine); + //为了最小信息熵,所以用这样的格式,并且我喜欢补0 + sb.AppendLine(" \"当前时间\": \"" + 当前时间 + "\""); + sb.AppendLine(" \"备注信息\": \"" + 备注信息 + "\""); + sb.AppendLine(" \"异常信息\": \"" + 异常信息 + "\""); + sb.AppendLine(" \"异常对象\": \"" + 异常对象 + "\""); + sb.AppendLine(" \"触发方法\": \"" + 触发方法 + "\""); + sb.AppendLine(" \"调用堆栈\": \"" + 调用堆栈?.Trim() + "\""); + sb.Append('}'); + return sb.ToString(); + } +} \ No newline at end of file -- Gitee From a28100d5bc5b4cd4a8842f325824c7e5f8d75d63 Mon Sep 17 00:00:00 2001 From: liuqihong <540762622@qq.com> Date: Tue, 19 Jul 2022 09:04:11 +0800 Subject: [PATCH 308/675] =?UTF-8?q?=E8=AF=BB=E5=86=99=E9=94=81=E5=B0=B1?= =?UTF-8?q?=E8=83=BD=E5=AE=8C=E6=88=90=E9=94=81=E5=AE=9A?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/IFoxCAD.Cad/Runtime/Log.cs | 46 ++++++++++++++++++---------------- 1 file changed, 24 insertions(+), 22 deletions(-) diff --git a/src/IFoxCAD.Cad/Runtime/Log.cs b/src/IFoxCAD.Cad/Runtime/Log.cs index fe7b24f..670ac78 100644 --- a/src/IFoxCAD.Cad/Runtime/Log.cs +++ b/src/IFoxCAD.Cad/Runtime/Log.cs @@ -1,5 +1,5 @@ namespace IFoxCAD.Cad; - + using System; using System.Diagnostics; using System.IO; @@ -13,7 +13,6 @@ public static class Log /// 当资源处于写入模式时,其他线程写入需要等待本次写入结束之后才能继续写入 /// static readonly ReaderWriterLockSlim _logWriteLock = new(); - static readonly object _syncObject = new(); /// /// 日志文件完整路径 @@ -22,8 +21,10 @@ public static class Log static Log() { - lock (_syncObject) + try { + _logWriteLock.EnterWriteLock(); + //调用静态方法的时候才会到构造函数,而且仅第一次时候才执行一次 var sb = new StringBuilder(); sb.Append(Environment.CurrentDirectory); @@ -42,6 +43,10 @@ static Log() sb.Append(".log"); _logAddress = sb.ToString(); } + finally + { + _logWriteLock.ExitWriteLock(); + } } @@ -59,27 +64,24 @@ static Log() { _logWriteLock.EnterWriteLock(); - lock (_syncObject) - { - var logtxt = new LogTxt(ex, remarks); - //var logtxtJson = Newtonsoft.Json.JsonConvert.SerializeObject(logtxt, Formatting.Indented); - var logtxtJson = logtxt.ToString(); - - //把异常信息输出到文件 - var sw = new StreamWriter(_logAddress, true/*当天日志文件存在就追加,否则就创建*/); - sw.Write(logtxtJson); - sw.Flush(); - sw.Close(); - sw.Dispose(); + var logtxt = new LogTxt(ex, remarks); + //var logtxtJson = Newtonsoft.Json.JsonConvert.SerializeObject(logtxt, Formatting.Indented); + var logtxtJson = logtxt.ToString(); + + //把异常信息输出到文件 + var sw = new StreamWriter(_logAddress, true/*当天日志文件存在就追加,否则就创建*/); + sw.Write(logtxtJson); + sw.Flush(); + sw.Close(); + sw.Dispose(); #if DEBUG - Debug.WriteLine("错误日志: " + _logAddress); - Debug.Write(logtxtJson); - return logtxtJson; - //Debugger.Break(); - //Debug.Assert(false, "终止进程"); + Debug.WriteLine("错误日志: " + _logAddress); + Debug.Write(logtxtJson); + return logtxtJson; + //Debugger.Break(); + //Debug.Assert(false, "终止进程"); #endif - } - } + } finally { _logWriteLock.ExitWriteLock(); -- Gitee From 66f20095d33324574ab0fdfd6cfb7a96a681111e Mon Sep 17 00:00:00 2001 From: liuqihong <540762622@qq.com> Date: Tue, 19 Jul 2022 21:07:25 +0800 Subject: [PATCH 309/675] =?UTF-8?q?=E4=BC=98=E5=8C=96=E5=AD=97=E7=AC=A6?= =?UTF-8?q?=E4=B8=B2=E6=8B=BC=E6=8E=A5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/IFoxCAD.Cad/Runtime/Log.cs | 85 +++++++++++++++------------------- 1 file changed, 38 insertions(+), 47 deletions(-) diff --git a/src/IFoxCAD.Cad/Runtime/Log.cs b/src/IFoxCAD.Cad/Runtime/Log.cs index 670ac78..9ddd4da 100644 --- a/src/IFoxCAD.Cad/Runtime/Log.cs +++ b/src/IFoxCAD.Cad/Runtime/Log.cs @@ -21,32 +21,25 @@ public static class Log static Log() { - try - { - _logWriteLock.EnterWriteLock(); - - //调用静态方法的时候才会到构造函数,而且仅第一次时候才执行一次 - var sb = new StringBuilder(); - sb.Append(Environment.CurrentDirectory); - sb.Append("\\ErrorLog"); - - //新建文件夹 - var path = sb.ToString(); - if (!Directory.Exists(path)) - { - Directory.CreateDirectory(path) - .Attributes = FileAttributes.Normal; //设置文件夹属性为普通 - } + //微软回复:静态构造函数只会被调用一次, + //并且在它执行完成之前,任何其它线程都不能创建这个类的实例或使用这个类的静态成员 + //https://blog.csdn.net/weixin_34204722/article/details/90095812 + var sb = new StringBuilder(); + sb.Append(Environment.CurrentDirectory); + sb.Append("\\ErrorLog"); - sb.Append('\\'); - sb.Append(DateTime.Now.ToString("yy-MM-dd")); - sb.Append(".log"); - _logAddress = sb.ToString(); - } - finally + //新建文件夹 + var path = sb.ToString(); + if (!Directory.Exists(path)) { - _logWriteLock.ExitWriteLock(); + Directory.CreateDirectory(path) + .Attributes = FileAttributes.Normal; //设置文件夹属性为普通 } + + sb.Append('\\'); + sb.Append(DateTime.Now.ToString("yy-MM-dd")); + sb.Append(".log"); + _logAddress = sb.ToString(); } @@ -55,14 +48,14 @@ static Log() /// /// 异常 /// 备注 - public static string? WriteLog(this System.Exception? ex, object? remarks = null) + public static string? WriteLog(this System.Exception? ex, string? remarks = null, bool printDebugWindow = true) { if (ex == null) return null; try { - _logWriteLock.EnterWriteLock(); + _logWriteLock.EnterWriteLock();// 写模式锁定 读写锁 var logtxt = new LogTxt(ex, remarks); //var logtxtJson = Newtonsoft.Json.JsonConvert.SerializeObject(logtxt, Formatting.Indented); @@ -75,16 +68,19 @@ static Log() sw.Close(); sw.Dispose(); #if DEBUG - Debug.WriteLine("错误日志: " + _logAddress); - Debug.Write(logtxtJson); - return logtxtJson; + if (printDebugWindow) + { + Debug.WriteLine("错误日志: " + _logAddress); + Debug.Write(logtxtJson); + } //Debugger.Break(); //Debug.Assert(false, "终止进程"); #endif - } + return logtxtJson; + } finally { - _logWriteLock.ExitWriteLock(); + _logWriteLock.ExitWriteLock();// 解锁 读写锁 } } } @@ -96,25 +92,21 @@ public class LogTxt public string 备注信息; public string 异常信息; public string 异常对象; - public string? 触发方法; - public string? 调用堆栈; + public string 触发方法; + public string 调用堆栈; - public LogTxt(System.Exception ex, object? remarks = null) + public LogTxt(System.Exception ex, string? remarks = null) { // 以不同语言显示日期 // DateTime.Now.ToString("f", new System.Globalization.CultureInfo("es-ES")) // DateTime.Now.ToString("f", new System.Globalization.CultureInfo("zh-cn")) // 为了最小信息熵,所以用这样的格式,并且我喜欢补0 this.当前时间 = DateTime.Now.ToString("yy-MM-dd hh:mm:ss"); - - if (remarks == null) - this.备注信息 = ""; - else - this.备注信息 = remarks.ToString(); + this.备注信息 = remarks ?? string.Empty; this.异常信息 = ex.Message; this.异常对象 = ex.Source; - this.触发方法 = ex.TargetSite?.ToString(); - this.调用堆栈 = ex.StackTrace?.Trim(); + this.触发方法 = ex.TargetSite == null ? string.Empty : ex.TargetSite.ToString(); + this.调用堆栈 = ex.StackTrace == null ? string.Empty : ex.StackTrace.Trim(); } /// 为了不引入json的dll,所以这里自己构造 @@ -123,13 +115,12 @@ public override string ToString() var sb = new StringBuilder(); sb.Append('{'); sb.Append(Environment.NewLine); - //为了最小信息熵,所以用这样的格式,并且我喜欢补0 - sb.AppendLine(" \"当前时间\": \"" + 当前时间 + "\""); - sb.AppendLine(" \"备注信息\": \"" + 备注信息 + "\""); - sb.AppendLine(" \"异常信息\": \"" + 异常信息 + "\""); - sb.AppendLine(" \"异常对象\": \"" + 异常对象 + "\""); - sb.AppendLine(" \"触发方法\": \"" + 触发方法 + "\""); - sb.AppendLine(" \"调用堆栈\": \"" + 调用堆栈?.Trim() + "\""); + sb.AppendLine($" \"{nameof(当前时间)}\": \"{当前时间}\""); + sb.AppendLine($" \"{nameof(备注信息)}\": \"{备注信息}\""); + sb.AppendLine($" \"{nameof(异常信息)}\": \"{异常信息}\""); + sb.AppendLine($" \"{nameof(异常对象)}\": \"{异常对象}\""); + sb.AppendLine($" \"{nameof(触发方法)}\": \"{触发方法}\""); + sb.AppendLine($" \"{nameof(调用堆栈)}\": \"{调用堆栈}\""); sb.Append('}'); return sb.ToString(); } -- Gitee From 3516204e6f16845f579d1a7382e9086fa1aaca1a Mon Sep 17 00:00:00 2001 From: liuqihong <540762622@qq.com> Date: Wed, 20 Jul 2022 23:07:28 +0800 Subject: [PATCH 310/675] =?UTF-8?q?=E6=89=A9=E5=B1=95=E6=97=A5=E5=BF=97?= =?UTF-8?q?=E6=A1=86=E6=9E=B6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/IFoxCAD.Cad/Runtime/Log.cs | 344 ++++++++++++++++++++++++++++----- 1 file changed, 294 insertions(+), 50 deletions(-) diff --git a/src/IFoxCAD.Cad/Runtime/Log.cs b/src/IFoxCAD.Cad/Runtime/Log.cs index 9ddd4da..ff4ceae 100644 --- a/src/IFoxCAD.Cad/Runtime/Log.cs +++ b/src/IFoxCAD.Cad/Runtime/Log.cs @@ -6,6 +6,288 @@ using System.Text; using System.Threading; +#region 写入日志到不同的环境中 +//https://zhuanlan.zhihu.com/p/338492989 +public abstract class LogBase +{ + public abstract void WriteLog(string? message); + public abstract void WriteLog(Exception? ex); +} + +/// +/// 日志输出环境 +/// +public enum LogTarget +{ + /// + /// 文件 + /// + File = 1, + /// + /// 数据库 + /// + Database = 2, + /// + /// windows日志 + /// + EventLog = 4, +} + +/// +/// 写入到文件中 +/// +public class FileLogger : LogBase +{ + public override void WriteLog(string? message) + { + //把异常信息输出到文件 + var sw = new StreamWriter(LogHelper.LogAddress, true/*当天日志文件存在就追加,否则就创建*/); + sw.Write(message); + sw.Flush(); + sw.Close(); + sw.Dispose(); + } + + public override void WriteLog(Exception? ex) + { + + } +} + +/// +/// 写入到数据库(暂时不支持) +/// +public class DBLogger : LogBase +{ + public override void WriteLog(string? message) + { + throw new NotImplementedException(); + } + public override void WriteLog(Exception? ex) + { + throw new NotImplementedException(); + } +} + +/// +/// 写入到win日志(暂时不支持) +/// +public class EventLogger : LogBase +{ + public override void WriteLog(string? message) + { + throw new NotImplementedException(); + //var eventLog = new EventLog("") + //{ + // Source = "IDGEventLog" + //}; + //eventLog.WriteEntry(message); + } + + public override void WriteLog(Exception? ex) + { + throw new NotImplementedException(); + } +} + +#endregion + +#region 静态方法 +public static class LogHelper +{ + /// + /// 读写锁 + /// 当资源处于写入模式时,其他线程写入需要等待本次写入结束之后才能继续写入 + /// + static readonly ReaderWriterLockSlim _logWriteLock = new(); + + /// + /// 日志文件完整路径 + /// +#pragma warning disable CA2211 // 非常量字段应当不可见 + public static string? LogAddress; +#pragma warning restore CA2211 // 非常量字段应当不可见 + + /// + /// 提供给外部设置log文件保存路径 + /// + /// 空的话就为运行的dll旁边的一个文件夹上 + public static void OptionFile(string? newlogAddress = null) + { + _logWriteLock.EnterWriteLock();// 写模式锁定 读写锁 + try + { + LogAddress = newlogAddress; + if (string.IsNullOrEmpty(LogAddress)) + { + //微软回复:静态构造函数只会被调用一次, + //并且在它执行完成之前,任何其它线程都不能创建这个类的实例或使用这个类的静态成员 + //https://blog.csdn.net/weixin_34204722/article/details/90095812 + var sb = new StringBuilder(); + sb.Append(Environment.CurrentDirectory); + sb.Append("\\ErrorLog"); + + //新建文件夹 + var path = sb.ToString(); + if (!Directory.Exists(path)) + { + Directory.CreateDirectory(path) + .Attributes = FileAttributes.Normal; //设置文件夹属性为普通 + } + + sb.Append('\\'); + sb.Append(DateTime.Now.ToString("yy-MM-dd")); + sb.Append(".log"); + LogAddress = sb.ToString(); + } + } + finally + { + _logWriteLock.ExitWriteLock();// 解锁 读写锁 + } + } + + public static string WriteLog(this string? message, + LogTarget target = LogTarget.File, + bool printDebugWindow = true) + { + if (message == null) + return string.Empty; + return LogAction(null, message, target, printDebugWindow); + } + + public static string WriteLog(this Exception? exception, + LogTarget target = LogTarget.File, + bool printDebugWindow = true) + { + if (exception == null) + return string.Empty; + return LogAction(exception, null, target, printDebugWindow); + } + + public static string WriteLog(this Exception? exception, + string? message, + LogTarget target = LogTarget.File, + bool printDebugWindow = true) + { + if (exception == null) + return string.Empty; + return LogAction(exception, message, target, printDebugWindow); + } + + + + static string LogAction(Exception? ex, + string? message, + LogTarget target, + bool printDebugWindow) + { + if (ex == null && message == null) + return string.Empty; + + if (target == LogTarget.File && LogAddress == null) + OptionFile(); + + try + { + _logWriteLock.EnterWriteLock();// 写模式锁定 读写锁 + + var logtxt = new LogTxt(ex, message); + //var logtxtJson = Newtonsoft.Json.JsonConvert.SerializeObject(logtxt, Formatting.Indented); + var logtxtJson = logtxt?.ToString(); + if (logtxtJson == null) + return string.Empty; + + LogBase? logger; + switch (target) + { + case LogTarget.File: + logger = new FileLogger(); + logger.WriteLog(logtxtJson); + break; + case LogTarget.Database: + logger = new DBLogger(); + logger.WriteLog(logtxtJson); + break; + case LogTarget.EventLog: + logger = new EventLogger(); + logger.WriteLog(logtxtJson); + break; + } + + if (printDebugWindow) + { + Debug.WriteLine("错误日志: " + LogAddress); + Debug.Write(logtxtJson); + //Debugger.Break(); + //Debug.Assert(false, "终止进程"); + } + return logtxtJson; + } + finally + { + _logWriteLock.ExitWriteLock();// 解锁 读写锁 + } + } +} +#endregion + +#region 序列化 +[Serializable] +public class LogTxt +{ + public string 当前时间; + public string? 备注信息; + public string? 异常信息; + public string? 异常对象; + public string? 触发方法; + public string? 调用堆栈; + + LogTxt() + { + // 以不同语言显示日期 + // DateTime.Now.ToString("f", new System.Globalization.CultureInfo("es-ES")) + // DateTime.Now.ToString("f", new System.Globalization.CultureInfo("zh-cn")) + // 为了最小信息熵,所以用这样的格式,并且我喜欢补0 + 当前时间 = DateTime.Now.ToString("yy-MM-dd hh:mm:ss"); + } + + public LogTxt(Exception? ex, string? message) : this() + { + if (ex == null && message == null) + throw new ArgumentNullException(nameof(ex)); + + if (ex != null) + { + 异常信息 = ex.Message; + 异常对象 = ex.Source; + 触发方法 = ex.TargetSite == null ? string.Empty : ex.TargetSite.ToString(); + 调用堆栈 = ex.StackTrace == null ? string.Empty : ex.StackTrace.Trim(); + } + if (message != null) + 备注信息 = message; + } + + /// 为了不引入json的dll,所以这里自己构造 + public override string? ToString() + { + var sb = new StringBuilder(); + sb.Append('{'); + sb.Append(Environment.NewLine); + sb.AppendLine($" \"{nameof(当前时间)}\": \"{当前时间}\""); + sb.AppendLine($" \"{nameof(备注信息)}\": \"{备注信息}\""); + sb.AppendLine($" \"{nameof(异常信息)}\": \"{异常信息}\""); + sb.AppendLine($" \"{nameof(异常对象)}\": \"{异常对象}\""); + sb.AppendLine($" \"{nameof(触发方法)}\": \"{触发方法}\""); + sb.AppendLine($" \"{nameof(调用堆栈)}\": \"{调用堆栈}\""); + sb.Append('}'); + return sb.ToString(); + } +} +#endregion + + +#if false //最简单的实现 public static class Log { /// @@ -48,11 +330,11 @@ static Log() /// /// 异常 /// 备注 - public static string? WriteLog(this System.Exception? ex, string? remarks = null, bool printDebugWindow = true) + /// DEBUG模式打印到vs输出窗口 + public static string? WriteLog(this Exception? ex, + string? remarks = null, + bool printDebugWindow = true) { - if (ex == null) - return null; - try { _logWriteLock.EnterWriteLock();// 写模式锁定 读写锁 @@ -61,21 +343,23 @@ static Log() //var logtxtJson = Newtonsoft.Json.JsonConvert.SerializeObject(logtxt, Formatting.Indented); var logtxtJson = logtxt.ToString(); + if (logtxtJson == null) + return string.Empty; + //把异常信息输出到文件 var sw = new StreamWriter(_logAddress, true/*当天日志文件存在就追加,否则就创建*/); sw.Write(logtxtJson); sw.Flush(); sw.Close(); sw.Dispose(); -#if DEBUG + if (printDebugWindow) { Debug.WriteLine("错误日志: " + _logAddress); Debug.Write(logtxtJson); + //Debugger.Break(); + //Debug.Assert(false, "终止进程"); } - //Debugger.Break(); - //Debug.Assert(false, "终止进程"); -#endif return logtxtJson; } finally @@ -83,45 +367,5 @@ static Log() _logWriteLock.ExitWriteLock();// 解锁 读写锁 } } -} - -[Serializable] -public class LogTxt -{ - public string 当前时间; - public string 备注信息; - public string 异常信息; - public string 异常对象; - public string 触发方法; - public string 调用堆栈; - - public LogTxt(System.Exception ex, string? remarks = null) - { - // 以不同语言显示日期 - // DateTime.Now.ToString("f", new System.Globalization.CultureInfo("es-ES")) - // DateTime.Now.ToString("f", new System.Globalization.CultureInfo("zh-cn")) - // 为了最小信息熵,所以用这样的格式,并且我喜欢补0 - this.当前时间 = DateTime.Now.ToString("yy-MM-dd hh:mm:ss"); - this.备注信息 = remarks ?? string.Empty; - this.异常信息 = ex.Message; - this.异常对象 = ex.Source; - this.触发方法 = ex.TargetSite == null ? string.Empty : ex.TargetSite.ToString(); - this.调用堆栈 = ex.StackTrace == null ? string.Empty : ex.StackTrace.Trim(); - } - - /// 为了不引入json的dll,所以这里自己构造 - public override string ToString() - { - var sb = new StringBuilder(); - sb.Append('{'); - sb.Append(Environment.NewLine); - sb.AppendLine($" \"{nameof(当前时间)}\": \"{当前时间}\""); - sb.AppendLine($" \"{nameof(备注信息)}\": \"{备注信息}\""); - sb.AppendLine($" \"{nameof(异常信息)}\": \"{异常信息}\""); - sb.AppendLine($" \"{nameof(异常对象)}\": \"{异常对象}\""); - sb.AppendLine($" \"{nameof(触发方法)}\": \"{触发方法}\""); - sb.AppendLine($" \"{nameof(调用堆栈)}\": \"{调用堆栈}\""); - sb.Append('}'); - return sb.ToString(); - } -} \ No newline at end of file +} +#endif \ No newline at end of file -- Gitee From 7f9e8a2dcde836c36bacdd8ab2e775b5146df46e Mon Sep 17 00:00:00 2001 From: liuqihong <540762622@qq.com> Date: Wed, 20 Jul 2022 23:27:21 +0800 Subject: [PATCH 311/675] =?UTF-8?q?=E4=BC=98=E5=8C=96=E6=8E=A5=E5=8F=A3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/IFoxCAD.Cad/Runtime/Log.cs | 76 +++++++++++++++++++++++++--------- 1 file changed, 57 insertions(+), 19 deletions(-) diff --git a/src/IFoxCAD.Cad/Runtime/Log.cs b/src/IFoxCAD.Cad/Runtime/Log.cs index ff4ceae..d8b2e62 100644 --- a/src/IFoxCAD.Cad/Runtime/Log.cs +++ b/src/IFoxCAD.Cad/Runtime/Log.cs @@ -10,8 +10,9 @@ //https://zhuanlan.zhihu.com/p/338492989 public abstract class LogBase { - public abstract void WriteLog(string? message); - public abstract void WriteLog(Exception? ex); + public abstract void ReadLog(string message); + public abstract void WriteLog(string message); + public abstract void DeleteLog(string message); } /// @@ -38,6 +39,16 @@ public enum LogTarget /// public class FileLogger : LogBase { + public override void DeleteLog(string message) + { + throw new NotImplementedException(); + } + + public override void ReadLog(string message) + { + throw new NotImplementedException(); + } + public override void WriteLog(string? message) { //把异常信息输出到文件 @@ -47,11 +58,6 @@ public override void WriteLog(string? message) sw.Close(); sw.Dispose(); } - - public override void WriteLog(Exception? ex) - { - - } } /// @@ -59,34 +65,66 @@ public override void WriteLog(Exception? ex) /// public class DBLogger : LogBase { - public override void WriteLog(string? message) + public override void DeleteLog(string message) + { + throw new NotImplementedException(); + } + + public override void ReadLog(string message) { throw new NotImplementedException(); } - public override void WriteLog(Exception? ex) + + public override void WriteLog(string? message) { throw new NotImplementedException(); } } /// -/// 写入到win日志(暂时不支持) +/// 写入到win日志 /// public class EventLogger : LogBase { - public override void WriteLog(string? message) + // https://docs.microsoft.com/en-us/answers/questions/526018/windows-event-log-with-net-5.html + // net50要加 + // 需要win权限 + + public string LogName = "IFoxCadLog"; + public override void DeleteLog(string message) { - throw new NotImplementedException(); - //var eventLog = new EventLog("") - //{ - // Source = "IDGEventLog" - //}; - //eventLog.WriteEntry(message); +#if !NET5_0 && !NET6_0 + if (EventLog.Exists(LogName)) + EventLog.Delete(LogName); +#endif } - public override void WriteLog(Exception? ex) + public override void ReadLog(string message) { - throw new NotImplementedException(); +#if !NET5_0 && !NET6_0 + EventLog eventLog = new(); + eventLog.Log = LogName; + foreach (EventLogEntry entry in eventLog.Entries) + { + //Write your custom code here + } +#endif + } + + public override void WriteLog(string? message) + { +#if !NET5_0 && !NET6_0 + try + { + EventLog eventLog = new(); + eventLog.Source = LogName; + eventLog.WriteEntry(message, EventLogEntryType.Information); + } + catch (System.Security.SecurityException e) + { + throw new Exception("您没有权限写入win日志中"); + } +#endif } } -- Gitee From 0401dc2c5430556714f09ec7962a70bfa14fc79a Mon Sep 17 00:00:00 2001 From: liuqihong <540762622@qq.com> Date: Mon, 25 Jul 2022 20:28:32 +0800 Subject: [PATCH 312/675] =?UTF-8?q?=E4=BC=98=E5=8C=96=E6=A0=BC=E5=BC=8F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/IFoxCAD.Basal/LinqEx.cs | 2 -- src/IFoxCAD.Basal/ListEx.cs | 2 -- 2 files changed, 4 deletions(-) diff --git a/src/IFoxCAD.Basal/LinqEx.cs b/src/IFoxCAD.Basal/LinqEx.cs index b79b4f7..8112e63 100644 --- a/src/IFoxCAD.Basal/LinqEx.cs +++ b/src/IFoxCAD.Basal/LinqEx.cs @@ -296,12 +296,10 @@ internal SpecComparer(Comparison comp) } #region IComparer 成员 - public int Compare(T x, T y) { return _comp(x, y); } - #endregion IComparer 成员 } diff --git a/src/IFoxCAD.Basal/ListEx.cs b/src/IFoxCAD.Basal/ListEx.cs index aebcb71..b0d20c0 100644 --- a/src/IFoxCAD.Basal/ListEx.cs +++ b/src/IFoxCAD.Basal/ListEx.cs @@ -18,10 +18,8 @@ public static bool EqualsAll(this IList a!!, IList b!!, IEqualityCompar comparer ??= EqualityComparer.Default; for (int i = 0; i < a.Count; i++) - { if (!comparer.Equals(a[i], b[i])) return false; - } return true; } } -- Gitee From 3da00abb8a3c9b2ddf5bc48c56731b70216b1d2a Mon Sep 17 00:00:00 2001 From: liuqihong <540762622@qq.com> Date: Tue, 26 Jul 2022 01:38:36 +0800 Subject: [PATCH 313/675] =?UTF-8?q?jig=E4=BA=8B=E4=BB=B6=E9=9A=90=E8=97=8F?= =?UTF-8?q?=E4=BF=AE=E6=94=B9=E4=B8=BADatabaseEntityDraw=E6=8F=90=E4=BE=9B?= =?UTF-8?q?=E5=9B=9E=E8=B0=83=E5=87=BD=E6=95=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/IFoxCAD.Cad/ExtensionMethod/Jig.cs | 34 +++++++++++++++++--------- tests/Test/TestJig.cs | 21 +++++++++++----- 2 files changed, 38 insertions(+), 17 deletions(-) diff --git a/src/IFoxCAD.Cad/ExtensionMethod/Jig.cs b/src/IFoxCAD.Cad/ExtensionMethod/Jig.cs index 6aef747..37e7055 100644 --- a/src/IFoxCAD.Cad/ExtensionMethod/Jig.cs +++ b/src/IFoxCAD.Cad/ExtensionMethod/Jig.cs @@ -20,7 +20,7 @@ public class JigEx : DrawJig /// /// 事件:亮显/暗显会被刷新冲刷掉,所以这个事件用于补充非刷新的工作 /// - public event WorldDrawEvent? WorldDrawEvent; + event WorldDrawEvent? WorldDrawEvent; /// /// 最后的鼠标点,用来确认长度 /// @@ -54,8 +54,8 @@ private JigEx() /// /// /// 用来频繁执行的回调: - /// 鼠标点; - /// 加入新建的图元,鼠标采样期间会Dispose图元的;所以已经在数据库图元利用事件加入,不要在此加入; + /// 鼠标点; + /// 加入新建的图元,鼠标采样期间会Dispose图元的;所以已经在数据库图元利用事件加入,不要在此加入; /// /// 鼠标移动的容差 public JigEx(Action>? action = null, double tolerance = 1e-6) : this() @@ -99,11 +99,12 @@ public JigPromptPointOptions SetOptions(Point3d basePoint, /// 关键字 /// 正交开关 /// - public JigPromptPointOptions SetOptions(string msg, Dictionary? keywords = null, bool orthomode = false) + public JigPromptPointOptions SetOptions(string msg, + Dictionary? keywords = null, + bool orthomode = false) { if (orthomode && Env.OrthoMode == false) { - //CadSystem.Setvar(_orthomode, "1");//1正交,0非正交 //setvar: https://www.cnblogs.com/JJBox/p/10209541.html Env.OrthoMode = true; _systemVariablesOrthomode = true; } @@ -126,6 +127,7 @@ public JigPromptPointOptions SetOptions(string msg, Dictionary? } //要放最后,才能优先触发其他关键字 + ///因为此处空格是无效的 if (spaceValue != string.Empty) _options.Keywords.Add(spaceKey, spaceKey, spaceValue); else @@ -141,15 +143,13 @@ public JigPromptPointOptions SetOptions(string msg, Dictionary? /// 正交开关 public void SetOptions(Action action, bool orthomode = false) { - _options = new JigPromptPointOptions(); - action.Invoke(_options); - if (orthomode && Env.OrthoMode == false) { - //CadSystem.Setvar(_orthomode, "1");//1正交,0非正交 //setvar: https://www.cnblogs.com/JJBox/p/10209541.html Env.OrthoMode = true; _systemVariablesOrthomode = true; } + _options = new JigPromptPointOptions(); + action.Invoke(_options); } /// @@ -163,9 +163,9 @@ public PromptResult Drag() var doc = dm.MdiActiveDocument; var ed = doc.Editor; var dr = ed.Drag(this); + if (_systemVariablesOrthomode) - //CadSystem.Setvar(_orthomode, "0");//1正交,0非正交 //setvar: https://www.cnblogs.com/JJBox/p/10209541.html - Env.OrthoMode = false; + Env.OrthoMode = !Env.OrthoMode; return dr; } @@ -262,6 +262,18 @@ protected override SamplerStatus Sampler(JigPrompts prompts) * 这样才可以保证容器在重绘中不被更改. */ + /// + /// 已经在数据库的图元在此进行重绘 + /// 0x01 此处不加入newEntity的,它们通常是在构造函数的回调上面加入,它们会进行频繁new和Dispose,避免遗忘释放 + /// 0x02 此处用于重绘已经在数据的图元 + /// 0x03 此处用于不刷新的图元进行亮显暗显,因为会被重绘冲刷掉 + /// + /// + public void DatabaseEntityDraw(WorldDrawEvent action) + { + WorldDrawEvent = action; + } + /// /// 重绘图形 /// diff --git a/tests/Test/TestJig.cs b/tests/Test/TestJig.cs index b4f3eef..a171794 100644 --- a/tests/Test/TestJig.cs +++ b/tests/Test/TestJig.cs @@ -16,8 +16,10 @@ public static void TestCmd_jig33() return; cir = tr.GetObject(per.ObjectId, OpenMode.ForWrite); + var oldSp = cir.StartPoint; JigEx moveJig = null; moveJig = new JigEx((mousePoint, drawEntitys) => { + moveJig.SetOptions(oldSp);//回调过程中也可以修改基点 //cir.UpgradeOpen();//已经提权了,所以这里不需要提权 cir.Move(cir.StartPoint, mousePoint); //cir.DowngradeOpen(); @@ -28,12 +30,15 @@ public static void TestCmd_jig33() }); moveJig.SetOptions(cir.GeometricExtents.MinPoint); - //此处不会Dispose图元, - //0x01 此处加入已经在数据的图元 - //0x02 加入不刷新的图元,例如亮显会被刷新冲刷掉 - moveJig.WorldDrawEvent += draw => { + /// + /// 已经在数据库的图元在此进行重绘 + /// 0x01 此处不加入newEntity的,它们通常是在构造函数的回调上面加入,它们会进行频繁new和Dispose,避免遗忘释放 + /// 0x02 此处用于重绘已经在数据的图元 + /// 0x03 此处用于不刷新的图元进行亮显暗显,因为会被重绘冲刷掉 + /// + moveJig.DatabaseEntityDraw(draw => { draw.RawGeometry.Draw(cir); - }; + }); while (true) { @@ -78,6 +83,7 @@ public void TestCmd_Jig44() drawEntitys.Enqueue(acText); }); jig.SetOptions(per.PickedPoint); + // moveJig.SetOptions("测试关键字重载", new Dictionary { { "Z", "中间(Z)" } }); bool flag = true; while (flag) @@ -97,8 +103,11 @@ public void TestCmd_Jig44() break; } } - else if (pr.Status != PromptStatus.OK)//右键,空格,回车,都在这里结束 + else if (pr.Status != PromptStatus.OK)//PromptStatus.None == 右键,空格,回车,都在这里结束 + { + tr.Editor.WriteMessage(Environment.NewLine + pr.Status.ToString()); return; + } else flag = false; } -- Gitee From 46b6accbeb1868f547f4517125bf0175c5437657 Mon Sep 17 00:00:00 2001 From: liuqihong <540762622@qq.com> Date: Tue, 26 Jul 2022 01:51:07 +0800 Subject: [PATCH 314/675] =?UTF-8?q?=E4=BF=AE=E6=94=B9=E6=B3=A8=E9=87=8A?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/IFoxCAD.Cad/ExtensionMethod/Jig.cs | 2 +- tests/Test/TestJig.cs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/IFoxCAD.Cad/ExtensionMethod/Jig.cs b/src/IFoxCAD.Cad/ExtensionMethod/Jig.cs index 37e7055..798f923 100644 --- a/src/IFoxCAD.Cad/ExtensionMethod/Jig.cs +++ b/src/IFoxCAD.Cad/ExtensionMethod/Jig.cs @@ -266,7 +266,7 @@ protected override SamplerStatus Sampler(JigPrompts prompts) /// 已经在数据库的图元在此进行重绘 /// 0x01 此处不加入newEntity的,它们通常是在构造函数的回调上面加入,它们会进行频繁new和Dispose,避免遗忘释放 /// 0x02 此处用于重绘已经在数据的图元 - /// 0x03 此处用于不刷新的图元进行亮显暗显,因为会被重绘冲刷掉 + /// 0x03 此处用于图元亮显暗显,因为会被重绘冲刷掉所以独立出来不重绘,它们也往往已经存在数据库的 /// /// public void DatabaseEntityDraw(WorldDrawEvent action) diff --git a/tests/Test/TestJig.cs b/tests/Test/TestJig.cs index a171794..5ec051b 100644 --- a/tests/Test/TestJig.cs +++ b/tests/Test/TestJig.cs @@ -34,7 +34,7 @@ public static void TestCmd_jig33() /// 已经在数据库的图元在此进行重绘 /// 0x01 此处不加入newEntity的,它们通常是在构造函数的回调上面加入,它们会进行频繁new和Dispose,避免遗忘释放 /// 0x02 此处用于重绘已经在数据的图元 - /// 0x03 此处用于不刷新的图元进行亮显暗显,因为会被重绘冲刷掉 + /// 0x03 此处用于图元亮显暗显,因为会被重绘冲刷掉所以独立出来不重绘,它们也往往已经存在数据库的 /// moveJig.DatabaseEntityDraw(draw => { draw.RawGeometry.Draw(cir); -- Gitee From eeea30ce3dac9cc2ca721f18168c1f0b42d2a26c Mon Sep 17 00:00:00 2001 From: liuqihong <540762622@qq.com> Date: Tue, 26 Jul 2022 04:09:46 +0800 Subject: [PATCH 315/675] =?UTF-8?q?=E4=BF=AE=E6=94=B9jig=E5=8F=98=E9=87=8F?= =?UTF-8?q?=E6=96=B9=E5=BC=8F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/IFoxCAD.Cad/ExtensionMethod/Jig.cs | 86 +++++++++++++++----------- tests/Test/TestJig.cs | 17 ++--- 2 files changed, 54 insertions(+), 49 deletions(-) diff --git a/src/IFoxCAD.Cad/ExtensionMethod/Jig.cs b/src/IFoxCAD.Cad/ExtensionMethod/Jig.cs index 798f923..f0f7a3a 100644 --- a/src/IFoxCAD.Cad/ExtensionMethod/Jig.cs +++ b/src/IFoxCAD.Cad/ExtensionMethod/Jig.cs @@ -1,4 +1,5 @@ /* 封装jig + * 20220726 隐藏事件,利用函数进行数据库图元重绘 * 20220710 修改SetOption()的空格结束,并添加例子到IFox * 20220503 cad22需要防止刷新过程中更改队列,08不会有. * 20220326 重绘图元的函数用错了,现在修正过来 @@ -31,13 +32,27 @@ public class JigEx : DrawJig public Entity[] Entitys => _drawEntitys.ToArray(); readonly Action>? _action; - readonly Autodesk.AutoCAD.Geometry.Tolerance _tolerance; + readonly Tolerance _tolerance; readonly Queue _drawEntitys;//重复生成的图元,放在这里刷新 JigPromptPointOptions? _options; - //const string _orthomode = "orthomode"; - bool _systemVariablesOrthomode = false; //正交修改 bool _worldDrawFlag = false; // 防止刷新过程中更改队列 + + bool _systemVariables_Orthomode = false; //正交修改 + /// + /// 正交修改还原 + /// + bool SystemVariables_Orthomode + { + get => _systemVariables_Orthomode; + set + { + //1正交,0非正交 //setvar: https://www.cnblogs.com/JJBox/p/10209541.html + if (Env.OrthoMode != value) + Env.OrthoMode = _systemVariables_Orthomode = value; + } + } + #endregion #region 构造 @@ -70,20 +85,17 @@ public JigEx(Action>? action = null, double tolerance = 1 /// 鼠标配置:基点 /// /// 基点 - /// 提示信息 /// 光标绑定 + /// 提示信息 /// 正交开关 public JigPromptPointOptions SetOptions(Point3d basePoint, CursorType cursorType = CursorType.RubberBand, string msg = "点选第二点", bool orthomode = false) { - if (orthomode && Env.OrthoMode == false) - { - //CadSystem.Setvar(_orthomode, "1");//1正交,0非正交 //setvar: https://www.cnblogs.com/JJBox/p/10209541.html - Env.OrthoMode = true; - _systemVariablesOrthomode = true; - } + if (orthomode) + SystemVariables_Orthomode = true; + _options = JigPointOptions(); _options.Message = Environment.NewLine + msg; _options.Cursor = cursorType; //光标绑定 @@ -99,15 +111,13 @@ public JigPromptPointOptions SetOptions(Point3d basePoint, /// 关键字 /// 正交开关 /// - public JigPromptPointOptions SetOptions(string msg, + public JigPromptPointOptions SetOptions(string msg, Dictionary? keywords = null, bool orthomode = false) { - if (orthomode && Env.OrthoMode == false) - { - Env.OrthoMode = true; - _systemVariablesOrthomode = true; - } + if (orthomode) + SystemVariables_Orthomode = true; + _options = JigPointOptions(); _options.Message = Environment.NewLine + msg; @@ -126,7 +136,7 @@ public JigPromptPointOptions SetOptions(string msg, } } - //要放最后,才能优先触发其他关键字 + ///要放最后,才能优先触发其他关键字 ///因为此处空格是无效的 if (spaceValue != string.Empty) _options.Keywords.Add(spaceKey, spaceKey, spaceValue); @@ -143,11 +153,9 @@ public JigPromptPointOptions SetOptions(string msg, /// 正交开关 public void SetOptions(Action action, bool orthomode = false) { - if (orthomode && Env.OrthoMode == false) - { - Env.OrthoMode = true; - _systemVariablesOrthomode = true; - } + if (orthomode) + SystemVariables_Orthomode = true; + _options = new JigPromptPointOptions(); action.Invoke(_options); } @@ -164,8 +172,8 @@ public PromptResult Drag() var ed = doc.Editor; var dr = ed.Drag(this); - if (_systemVariablesOrthomode) - Env.OrthoMode = !Env.OrthoMode; + if (SystemVariables_Orthomode) + SystemVariables_Orthomode = !SystemVariables_Orthomode; return dr; } @@ -237,6 +245,23 @@ protected override SamplerStatus Sampler(JigPrompts prompts) return SamplerStatus.OK; } + /// + /// 重绘已在数据库的图元 + /// 0x01 此处不加入newEntity的,它们在构造函数的参数回调处加入,它们会进行频繁new和Dispose从而避免遗忘释放 + /// 0x02 此处用于重绘已经在数据的图元 + /// 0x03 此处用于图元亮显暗显,因为会被重绘冲刷掉所以独立出来不重绘,它们也往往已经存在数据库的 + /// + /// + /// newEntity只会存在一个图元队列中,而数据库图元可以分多个集合 + /// 例如: 集合A亮显时 集合B暗显/集合B亮显时 集合A暗显,所以我没有设计多个"数据库图元集合"存放,而是由用户在构造函数外自行创建 + /// + /// + public void DatabaseEntityDraw(WorldDrawEvent action) + { + WorldDrawEvent = action; + } + + /* WorldDraw 封装外的操作说明: * 0x01 * 我有一个业务是一次性生成四个方向的箭头,因为cad08缺少瞬时图元, @@ -261,19 +286,6 @@ protected override SamplerStatus Sampler(JigPrompts prompts) * 所以只能重绘结束的时候才允许鼠标采集,采集过程的时候不会触发重绘, * 这样才可以保证容器在重绘中不被更改. */ - - /// - /// 已经在数据库的图元在此进行重绘 - /// 0x01 此处不加入newEntity的,它们通常是在构造函数的回调上面加入,它们会进行频繁new和Dispose,避免遗忘释放 - /// 0x02 此处用于重绘已经在数据的图元 - /// 0x03 此处用于图元亮显暗显,因为会被重绘冲刷掉所以独立出来不重绘,它们也往往已经存在数据库的 - /// - /// - public void DatabaseEntityDraw(WorldDrawEvent action) - { - WorldDrawEvent = action; - } - /// /// 重绘图形 /// diff --git a/tests/Test/TestJig.cs b/tests/Test/TestJig.cs index 5ec051b..a456c07 100644 --- a/tests/Test/TestJig.cs +++ b/tests/Test/TestJig.cs @@ -1,7 +1,5 @@ -using System.Windows.Forms; - - -namespace Test; +namespace Test; +using System.Windows.Forms; public class Commands_Jig { @@ -28,14 +26,9 @@ public static void TestCmd_jig33() //所以此处不加入已经在数据库的图元,而是加入new Entity的. //drawEntitys.Enqueue(cir); }); - moveJig.SetOptions(cir.GeometricExtents.MinPoint); + moveJig.SetOptions(cir.GeometricExtents.MinPoint, orthomode: true); - /// - /// 已经在数据库的图元在此进行重绘 - /// 0x01 此处不加入newEntity的,它们通常是在构造函数的回调上面加入,它们会进行频繁new和Dispose,避免遗忘释放 - /// 0x02 此处用于重绘已经在数据的图元 - /// 0x03 此处用于图元亮显暗显,因为会被重绘冲刷掉所以独立出来不重绘,它们也往往已经存在数据库的 - /// + //此处详见方法注释 moveJig.DatabaseEntityDraw(draw => { draw.RawGeometry.Draw(cir); }); @@ -63,7 +56,7 @@ public void TestCmd_Jig44() /* * 鼠标采样器执行时修改鼠标基点 - * 原因: 多段线与鼠标垂直点作为 BasePoint ,jig鼠标点为确定点 + * 原因: 多段线与鼠标垂直点作为 BasePoint,jig鼠标点为确定点 * 所以需要先声明再传入指针,但是我发现null也可以. */ JigEx jig = null; -- Gitee From 7bd1d91c4104955dc7e61a4cf2dc0b84543b9dfb Mon Sep 17 00:00:00 2001 From: liuqihong <540762622@qq.com> Date: Tue, 26 Jul 2022 20:08:48 +0800 Subject: [PATCH 316/675] =?UTF-8?q?=E4=BC=98=E5=8C=96=E6=B3=A8=E9=87=8A?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/IFoxCAD.Cad/Runtime/DBTrans.cs | 50 +++++++++++++++++------------- 1 file changed, 28 insertions(+), 22 deletions(-) diff --git a/src/IFoxCAD.Cad/Runtime/DBTrans.cs b/src/IFoxCAD.Cad/Runtime/DBTrans.cs index 30ce0f0..be6a443 100644 --- a/src/IFoxCAD.Cad/Runtime/DBTrans.cs +++ b/src/IFoxCAD.Cad/Runtime/DBTrans.cs @@ -1,5 +1,8 @@ namespace IFoxCAD.Cad; +/// +/// 事务栈,隐匿事务在数据库其中担任的角色 +/// public class DBTrans : IDisposable { #region 私有字段 @@ -45,10 +48,10 @@ public static DBTrans Top * 所以用AOP方式修复 * * 0x03 - * 经过艰苦卓绝的测试,aop模式由于不能断点调试,所以暂时放弃。 + * 经过艰苦卓绝的测试,aop模式由于不能断点调试,所以暂时放弃。 */ - // 由于大量的函数依赖本属性,强迫用户先开启事务 + // 由于大量的函数依赖本属性,强迫用户先开启事务 if (dBTrans.Count == 0) throw new ArgumentNullException("事务栈没有任何事务,请在调用前创建:" + nameof(DBTrans)); var trans = dBTrans.Peek(); @@ -76,7 +79,8 @@ public static DBTrans Top #region 构造函数 /// - /// 默认构造函数,默认为打开当前文档,默认提交事务 + /// 事务栈 + /// 默认构造函数,默认为打开当前文档,默认提交事务 /// /// 要打开的文档 /// 事务是否提交 @@ -95,7 +99,8 @@ public DBTrans(Document? doc = null, bool commit = true, bool doclock = false) } /// - /// 构造函数,打开数据库,默认提交事务 + /// 事务栈 + /// 打开数据库,默认提交事务 /// /// 要打开的数据库 /// 事务是否提交 @@ -106,21 +111,23 @@ public DBTrans(Database database, bool commit = true) _commit = commit; dBTrans.Push(this); } + /// - /// 构造函数,打开文件,默认提交事务 + /// 事务栈 + /// 打开文件,默认提交事务 /// /// 要打开的文件名 /// 事务是否提交 public DBTrans(string fileName, bool commit = true, FileOpenMode openMode = FileOpenMode.OpenForReadAndWriteNoShare) { -#if ac2009 - if (string.IsNullOrEmpty(fileName.Trim())) - throw new ArgumentNullException(nameof(fileName)); +#if NET35 + if (string.IsNullOrEmpty(fileName?.Trim())) #else - if (string.IsNullOrWhiteSpace(fileName)) - throw new ArgumentNullException(nameof(fileName)); #endif + throw new ArgumentNullException(nameof(fileName)); + + if (File.Exists(fileName)) { var doc = Application.DocumentManager @@ -290,10 +297,10 @@ public static implicit operator Transaction(DBTrans tr) /// /// 要获取的图元对象的类型 /// 对象id - /// 打开模式,默认为只读 - /// 是否打开已删除对象,默认为不打开 - /// 是否打开锁定图层对象,默认为不打开 - /// 图元对象,类型不匹配时返回 + /// 打开模式,默认为只读 + /// 是否打开已删除对象,默认为不打开 + /// 是否打开锁定图层对象,默认为不打开 + /// 图元对象,类型不匹配时返回 public T? GetObject(ObjectId id, OpenMode mode = OpenMode.ForRead, bool openErased = false, @@ -320,9 +327,9 @@ public ObjectId GetObjectId(string handleString) #region 保存文件 /// - /// 保存当前数据库的dwg文件,如果前台打开则按dwg默认版本保存,否则按version参数的版本保存 + /// 保存当前数据库的dwg文件,如果前台打开则按dwg默认版本保存,否则按version参数的版本保存 /// - /// dwg版本,默认为2004 + /// dwg版本,默认为2004 public void SaveDwgFile(DwgVersion version = DwgVersion.AC1800) { bool flag = true; @@ -364,11 +371,11 @@ public void Commit() protected virtual void Dispose(bool disposing) { /* 事务dispose流程: - * 1. 根据传入的参数确定是否提交,true为提交,false为不提交 - * 2. 根据disposedValue的值确定是否重复dispose,false为首次dispose + * 1. 根据传入的参数确定是否提交,true为提交,false为不提交 + * 2. 根据disposedValue的值确定是否重复dispose,false为首次dispose * 3. 如果锁文档就将文档锁dispose - * 4. 不管是否提交,既然进入dispose,就要将事务栈的当前事务弹出 - * 注意这里的事务栈不是cad的事务管理器,而是dbtrans的事务 + * 4. 不管是否提交,既然进入dispose,就要将事务栈的当前事务弹出 + * 注意这里的事务栈不是cad的事务管理器,而是dbtrans的事务 * 5. 清理非托管的字段 */ @@ -381,8 +388,7 @@ protected virtual void Dispose(bool disposing) if (disposing) { - // 释放托管状态(托管对象) - // 调用cad的事务进行提交 + // 调用cad的事务进行提交,释放托管状态(托管对象) Transaction.Commit(); } else -- Gitee From 31856f0aae123cfd754839a71dcac0a943aadbbf Mon Sep 17 00:00:00 2001 From: liuqihong <540762622@qq.com> Date: Tue, 26 Jul 2022 21:03:38 +0800 Subject: [PATCH 317/675] =?UTF-8?q?=E4=BF=AE=E6=94=B9jig=E7=9A=84demo?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/IFoxCAD.Cad/ExtensionMethod/EditorEx.cs | 6 --- src/IFoxCAD.Cad/ExtensionMethod/Jig.cs | 58 ++++++++++----------- tests/Test/TestJig.cs | 32 +++++++++--- 3 files changed, 53 insertions(+), 43 deletions(-) diff --git a/src/IFoxCAD.Cad/ExtensionMethod/EditorEx.cs b/src/IFoxCAD.Cad/ExtensionMethod/EditorEx.cs index 836a673..bbe0846 100644 --- a/src/IFoxCAD.Cad/ExtensionMethod/EditorEx.cs +++ b/src/IFoxCAD.Cad/ExtensionMethod/EditorEx.cs @@ -81,19 +81,13 @@ public static SelectionSet SelectByLineWeight(this Editor editor, LineWeight lin if (keywords is not null) { foreach (var keyword in keywords.Keys) - { pso.Keywords.Add(keyword); - } if (pso.MessageForRemoval is null) - { pso.MessageForAdding = "选择对象"; - } pso.MessageForAdding += $"[{string.Join(" / ", keywords.Keys.ToArray())}]"; pso.KeywordInput += (s, e) => { if (keywords.ContainsKey(e.Input)) - { keywords[e.Input].Invoke(); - } }; } diff --git a/src/IFoxCAD.Cad/ExtensionMethod/Jig.cs b/src/IFoxCAD.Cad/ExtensionMethod/Jig.cs index f0f7a3a..d0a3bdd 100644 --- a/src/IFoxCAD.Cad/ExtensionMethod/Jig.cs +++ b/src/IFoxCAD.Cad/ExtensionMethod/Jig.cs @@ -1,7 +1,7 @@ /* 封装jig * 20220726 隐藏事件,利用函数进行数据库图元重绘 * 20220710 修改SetOption()的空格结束,并添加例子到IFox - * 20220503 cad22需要防止刷新过程中更改队列,08不会有. + * 20220503 cad22需要防止刷新过程中更改队列,是因为允许函数重入导致,08不会有. * 20220326 重绘图元的函数用错了,现在修正过来 * 20211216 加入块表时候做一个差集,剔除临时图元 * 20211209 补充正交变量设置和回收设置 @@ -31,18 +31,16 @@ public class JigEx : DrawJig /// public Entity[] Entitys => _drawEntitys.ToArray(); - readonly Action>? _action; - readonly Tolerance _tolerance; + + readonly Action>? _mouseAction; + readonly Tolerance _tolerance;//容差 readonly Queue _drawEntitys;//重复生成的图元,放在这里刷新 - JigPromptPointOptions? _options; - bool _worldDrawFlag = false; // 防止刷新过程中更改队列 + JigPromptPointOptions? _options;//jig鼠标配置 + bool _worldDrawFlag = false; // 20220503 - bool _systemVariables_Orthomode = false; //正交修改 - /// - /// 正交修改还原 - /// - bool SystemVariables_Orthomode + bool _systemVariables_Orthomode = false; + bool SystemVariables_Orthomode // 正交修改还原 { get => _systemVariables_Orthomode; set @@ -52,7 +50,6 @@ bool SystemVariables_Orthomode Env.OrthoMode = _systemVariables_Orthomode = value; } } - #endregion #region 构造 @@ -75,7 +72,7 @@ private JigEx() /// 鼠标移动的容差 public JigEx(Action>? action = null, double tolerance = 1e-6) : this() { - _action = action; + _mouseAction = action; _tolerance = new(tolerance, tolerance); } #endregion @@ -124,25 +121,27 @@ public JigPromptPointOptions SetOptions(string msg, //加入关键字,加入时候将空格内容放到最后 string spaceValue = string.Empty; const string spaceKey = " "; + if (keywords != null) - { - var ge = keywords.GetEnumerator(); - while (ge.MoveNext()) - { - if (ge.Current.Key == spaceKey) - spaceValue = ge.Current.Value; + foreach (var item in keywords) + if (item.Key == spaceKey) + spaceValue = item.Value; else - _options.Keywords.Add(ge.Current.Key, ge.Current.Key, ge.Current.Value); - } - } + _options.Keywords.Add(item.Key, item.Key, item.Value); + ///因为默认配置函数导致此处空格触发是无效的, + ///但是用户如果想触发,就需要在外部减去默认UserInputControls配置 ///要放最后,才能优先触发其他关键字 - ///因为此处空格是无效的 if (spaceValue != string.Empty) _options.Keywords.Add(spaceKey, spaceKey, spaceValue); else _options.Keywords.Add(spaceKey, spaceKey, "<空格退出>"); + // 外部设置减去配置 + // _options.UserInputControls = + // _options.UserInputControls + // ^ UserInputControls.NullResponseAccepted //输入了鼠标右键,结束jig + // ^ UserInputControls.AnyBlankTerminatesInput; //空格或回车,结束jig; return _options; } @@ -186,22 +185,19 @@ public PromptResult Drag() public IEnumerable? AddEntityToMsPs(DBTrans tr, IEnumerable? removeEntity = null) { - var ents = Entitys; - if (ents.Length == 0) + if (Entitys.Length == 0) return null; var ids = new List(); - IEnumerable es = ents; + IEnumerable es = Entitys; if (removeEntity != null) - es = es.Except(removeEntity); + es = es.Except(removeEntity);//差集 - var ge = es.GetEnumerator(); - while (ge.MoveNext()) - ids.Add(tr.CurrentSpace.AddEntity(ge.Current)); + foreach (var item in es) + ids.Add(tr.CurrentSpace.AddEntity(item)); return ids; } - #endregion #region 重写 @@ -239,7 +235,7 @@ protected override SamplerStatus Sampler(JigPrompts prompts) _drawEntitys.Dequeue().Dispose(); //委托把容器扔出去接收新创建的图元,然后给重绘更新 - _action?.Invoke(mousePointWcs, _drawEntitys); + _mouseAction?.Invoke(mousePointWcs, _drawEntitys); MousePointWcsLast = mousePointWcs; return SamplerStatus.OK; diff --git a/tests/Test/TestJig.cs b/tests/Test/TestJig.cs index a456c07..4eea094 100644 --- a/tests/Test/TestJig.cs +++ b/tests/Test/TestJig.cs @@ -14,6 +14,8 @@ public static void TestCmd_jig33() return; cir = tr.GetObject(per.ObjectId, OpenMode.ForWrite); + if (cir == null) + return; var oldSp = cir.StartPoint; JigEx moveJig = null; moveJig = new JigEx((mousePoint, drawEntitys) => { @@ -60,12 +62,22 @@ public void TestCmd_Jig44() * 所以需要先声明再传入指针,但是我发现null也可以. */ JigEx jig = null; + JigPromptPointOptions options = null; jig = new JigEx((mousePoint, drawEntitys) => { var closestPt = pl.GetClosestPointTo(mousePoint, false); - var sop = jig.SetOptions(closestPt); - sop.Keywords.Add("A"); - sop.Keywords.Add(" ");/*这里是无效的,因为jig.SetOptions()内部设置,但是这里设置了会显示,最好注释掉*/ + //回调过程中SetOptions会覆盖配置,所以如果想增加关键字或者修改基点, + //不要这样做: jig.SetOptions(closestPt) 而是使用底层暴露 + options.BasePoint = closestPt; + bool contains = false; + for (int i = 0; i < options.Keywords.Count; i++) + if (options.Keywords[i].GlobalName == "A") + { + contains = true; + break; + } + if (contains)//避免重复加入同一个关键字 + options.Keywords.Add("A"); //生成文字 var dictString = (pl.GetDistAtPoint(closestPt) * 0.001).ToString("0.00"); @@ -75,8 +87,16 @@ public void TestCmd_Jig44() //加入刷新队列 drawEntitys.Enqueue(acText); }); - jig.SetOptions(per.PickedPoint); - // moveJig.SetOptions("测试关键字重载", new Dictionary { { "Z", "中间(Z)" } }); + + options = jig.SetOptions(per.PickedPoint); + + // 如果没有这个,那么空格只会是 PromptStatus.None 而不是 PromptStatus.Keyword + // 减去配置之后就可以触发空格关键字了 + // options.Keywords.Add(" ", " ", "空格结束啊"); + // if ((options.UserInputControls & UserInputControls.NullResponseAccepted) == UserInputControls.NullResponseAccepted) + // options.UserInputControls ^= UserInputControls.NullResponseAccepted; //输入了鼠标右键,结束jig + // if ((options.UserInputControls & UserInputControls.AnyBlankTerminatesInput) == UserInputControls.AnyBlankTerminatesInput) + // options.UserInputControls ^= UserInputControls.AnyBlankTerminatesInput; //空格或回车,结束jig bool flag = true; while (flag) @@ -91,7 +111,7 @@ public void TestCmd_Jig44() flag = false; break; case " ": - tr.Editor.WriteMessage("\n 此句永远不会执行,另见: jig.SetOptions()的 JigPointOptions()内注释"); + tr.Editor.WriteMessage("\n 触发关键字空格"); flag = false; break; } -- Gitee From 8145bb859f66aee6a0137c756ea4f2ac73c8fdbf Mon Sep 17 00:00:00 2001 From: liuqihong <540762622@qq.com> Date: Tue, 26 Jul 2022 21:05:13 +0800 Subject: [PATCH 318/675] =?UTF-8?q?=E5=B0=91=E4=BA=86=E4=B8=AA=E5=8F=8D?= =?UTF-8?q?=E8=BD=AC=E6=9D=A1=E4=BB=B6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- tests/Test/TestJig.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/Test/TestJig.cs b/tests/Test/TestJig.cs index 4eea094..d2821d1 100644 --- a/tests/Test/TestJig.cs +++ b/tests/Test/TestJig.cs @@ -76,7 +76,7 @@ public void TestCmd_Jig44() contains = true; break; } - if (contains)//避免重复加入同一个关键字 + if (!contains)//避免重复加入同一个关键字 options.Keywords.Add("A"); //生成文字 -- Gitee From a64b9866aa2123d5c00ea598d4e204e0c1b1e875 Mon Sep 17 00:00:00 2001 From: liuqihong <540762622@qq.com> Date: Tue, 26 Jul 2022 21:49:50 +0800 Subject: [PATCH 319/675] =?UTF-8?q?=E5=A2=9E=E5=8A=A0=E5=85=B3=E9=94=AE?= =?UTF-8?q?=E5=AD=97=E5=AE=B9=E5=99=A8=E6=89=A9=E5=B1=95=E6=96=B9=E6=B3=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/IFoxCAD.Cad/ExtensionMethod/Jig.cs | 29 ++++++-- .../ExtensionMethod/KeywordCollectionEx.cs | 68 +++++++++++++++++++ tests/Test/TestJig.cs | 17 ++--- 3 files changed, 94 insertions(+), 20 deletions(-) create mode 100644 src/IFoxCAD.Cad/ExtensionMethod/KeywordCollectionEx.cs diff --git a/src/IFoxCAD.Cad/ExtensionMethod/Jig.cs b/src/IFoxCAD.Cad/ExtensionMethod/Jig.cs index d0a3bdd..edd9f3d 100644 --- a/src/IFoxCAD.Cad/ExtensionMethod/Jig.cs +++ b/src/IFoxCAD.Cad/ExtensionMethod/Jig.cs @@ -121,7 +121,7 @@ public JigPromptPointOptions SetOptions(string msg, //加入关键字,加入时候将空格内容放到最后 string spaceValue = string.Empty; const string spaceKey = " "; - + if (keywords != null) foreach (var item in keywords) if (item.Key == spaceKey) @@ -297,13 +297,13 @@ protected override bool WorldDraw(WorldDraw draw) } #endregion + /// + /// 用户输入控制默认配置 + /// 令jig.Drag().Status == + /// + /// static JigPromptPointOptions JigPointOptions() { - /* 用户输入控制: - * 结束jig的设置,若此时也设置了空格关键字, - * 空格时为 jig.Drag().Status == PromptStatus.None,而不是关键字, - * 所以设计的时候,可以不使用空格关键字. - */ return new JigPromptPointOptions() { UserInputControls = @@ -313,8 +313,23 @@ static JigPromptPointOptions JigPointOptions() | UserInputControls.AnyBlankTerminatesInput //空格或回车,结束jig; }; } -} + /// + /// 空格默认是, + /// 将它设置为 + /// + public void SetSpaceIsKeyword() + { + var opt = _options; + if (opt == null) + throw new ArgumentNullException(nameof(_options)); + + if ((opt.UserInputControls & UserInputControls.NullResponseAccepted) == UserInputControls.NullResponseAccepted) + opt.UserInputControls ^= UserInputControls.NullResponseAccepted; //输入了鼠标右键,结束jig + if ((opt.UserInputControls & UserInputControls.AnyBlankTerminatesInput) == UserInputControls.AnyBlankTerminatesInput) + opt.UserInputControls ^= UserInputControls.AnyBlankTerminatesInput; //空格或回车,结束jig + } +} #if false | UserInputControls.DoNotEchoCancelForCtrlC //不要取消CtrlC的回音 diff --git a/src/IFoxCAD.Cad/ExtensionMethod/KeywordCollectionEx.cs b/src/IFoxCAD.Cad/ExtensionMethod/KeywordCollectionEx.cs new file mode 100644 index 0000000..e562d82 --- /dev/null +++ b/src/IFoxCAD.Cad/ExtensionMethod/KeywordCollectionEx.cs @@ -0,0 +1,68 @@ +namespace IFoxCAD.Cad; + +public static class KeywordCollectionEx +{ + public enum KeywordName + { + GlobalName, + LocalName, + DisplayName, + } + + /// + /// 含有关键字 + /// + /// 集合 + /// 关键字 + /// 关键字容器字段名 + /// true含有 + public static bool Contains(this KeywordCollection collection, string name, + KeywordName keywordName = KeywordName.GlobalName) + { + bool contains = false; + switch (keywordName) + { + case KeywordName.GlobalName: + for (int i = 0; i < collection.Count; i++) + if (collection[i].GlobalName == name) + { + contains = true; + break; + } + break; + case KeywordName.LocalName: + for (int i = 0; i < collection.Count; i++) + if (collection[i].LocalName == name) + { + contains = true; + break; + } + break; + case KeywordName.DisplayName: + for (int i = 0; i < collection.Count; i++) + if (collection[i].DisplayName == name) + { + contains = true; + break; + } + break; + default: + break; + } + return contains; + } + + /// + /// 获取词典, + /// KeywordCollection是允许重复关键字的,没有哈希索引,在重复判断时候要遍历,所以生成一个词典 + /// + /// + /// + public static Dictionary GetDict(this KeywordCollection collection) + { + Dictionary map = new(); + for (int i = 0; i < collection.Count; i++) + map.Add(collection[i].GlobalName, collection[i].DisplayName); + return map; + } +} \ No newline at end of file diff --git a/tests/Test/TestJig.cs b/tests/Test/TestJig.cs index d2821d1..605958c 100644 --- a/tests/Test/TestJig.cs +++ b/tests/Test/TestJig.cs @@ -69,14 +69,9 @@ public void TestCmd_Jig44() //回调过程中SetOptions会覆盖配置,所以如果想增加关键字或者修改基点, //不要这样做: jig.SetOptions(closestPt) 而是使用底层暴露 options.BasePoint = closestPt; - bool contains = false; - for (int i = 0; i < options.Keywords.Count; i++) - if (options.Keywords[i].GlobalName == "A") - { - contains = true; - break; - } - if (!contains)//避免重复加入同一个关键字 + + //需要避免重复加入同一个关键字 + if (!options.Keywords.Contains("A")) options.Keywords.Add("A"); //生成文字 @@ -91,12 +86,8 @@ public void TestCmd_Jig44() options = jig.SetOptions(per.PickedPoint); // 如果没有这个,那么空格只会是 PromptStatus.None 而不是 PromptStatus.Keyword - // 减去配置之后就可以触发空格关键字了 // options.Keywords.Add(" ", " ", "空格结束啊"); - // if ((options.UserInputControls & UserInputControls.NullResponseAccepted) == UserInputControls.NullResponseAccepted) - // options.UserInputControls ^= UserInputControls.NullResponseAccepted; //输入了鼠标右键,结束jig - // if ((options.UserInputControls & UserInputControls.AnyBlankTerminatesInput) == UserInputControls.AnyBlankTerminatesInput) - // options.UserInputControls ^= UserInputControls.AnyBlankTerminatesInput; //空格或回车,结束jig + // jig.SetSpaceIsKeyword(); bool flag = true; while (flag) -- Gitee From 1bb853d43bdd5c6465beee0ebdd528468bbe1c22 Mon Sep 17 00:00:00 2001 From: zewen han Date: Mon, 1 Aug 2022 14:47:27 +0800 Subject: [PATCH 320/675] =?UTF-8?q?=E6=96=B0=E5=A2=9E=E6=98=AF=E5=90=A6?= =?UTF-8?q?=E5=9C=A8UCS=E4=B8=8B=E7=9A=84=E5=88=A4=E6=96=AD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/IFoxCAD.Cad/Runtime/Env.cs | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/src/IFoxCAD.Cad/Runtime/Env.cs b/src/IFoxCAD.Cad/Runtime/Env.cs index 9edeedb..b2c6e5e 100644 --- a/src/IFoxCAD.Cad/Runtime/Env.cs +++ b/src/IFoxCAD.Cad/Runtime/Env.cs @@ -428,7 +428,7 @@ private static string GetName(this T value) return Enum.GetName(typeof(T), value); } -#endregion Enum + #endregion Enum #region 环境变量 /// @@ -456,7 +456,7 @@ public static void SetVar(string varName, object value) Env.Print($"{varName} 是不存在的变量!"); } - + } /// /// 获取系统环境变量 @@ -486,4 +486,9 @@ public static void SetEnv(string var, string? value) /// /// 要打印的对象 public static void Print(object message) => Editor.WriteMessage($"{message}\n"); + /// + /// 判断当前是否在UCS坐标下 + /// + /// Bool + public static bool IsUcs() => (short)GetVar("WORLDUCS") == 0; } -- Gitee From 0b897b727e2826e60f93e994228de4edb809916c Mon Sep 17 00:00:00 2001 From: liuqihong <540762622@qq.com> Date: Wed, 3 Aug 2022 21:29:48 +0800 Subject: [PATCH 321/675] =?UTF-8?q?=E4=BF=AE=E6=94=B9=E6=B3=A8=E9=87=8A?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/IFoxCAD.Cad/ExtensionMethod/KeywordCollectionEx.cs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/IFoxCAD.Cad/ExtensionMethod/KeywordCollectionEx.cs b/src/IFoxCAD.Cad/ExtensionMethod/KeywordCollectionEx.cs index e562d82..0c77702 100644 --- a/src/IFoxCAD.Cad/ExtensionMethod/KeywordCollectionEx.cs +++ b/src/IFoxCAD.Cad/ExtensionMethod/KeywordCollectionEx.cs @@ -12,7 +12,7 @@ public enum KeywordName /// /// 含有关键字 /// - /// 集合 + /// 关键字集合 /// 关键字 /// 关键字容器字段名 /// true含有 @@ -53,8 +53,8 @@ public static bool Contains(this KeywordCollection collection, string name, } /// - /// 获取词典, - /// KeywordCollection是允许重复关键字的,没有哈希索引,在重复判断时候要遍历,所以生成一个词典 + /// 获取词典, + /// KeywordCollection是允许重复关键字的,没有哈希索引,在多次判断时候会遍历多次O(n),所以生成一个词典进行O(1) /// /// /// -- Gitee From e4c3505bedd72c1230e0c3c2efa1c647855103b3 Mon Sep 17 00:00:00 2001 From: liuqihong <540762622@qq.com> Date: Fri, 5 Aug 2022 18:06:31 +0800 Subject: [PATCH 322/675] =?UTF-8?q?=E8=A1=A5=E5=85=85=E6=B3=A8=E9=87=8A?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/IFoxCAD.Cad/ExtensionMethod/Jig.cs | 7 ++----- src/IFoxCAD.Cad/Runtime/Env.cs | 2 -- 2 files changed, 2 insertions(+), 7 deletions(-) diff --git a/src/IFoxCAD.Cad/ExtensionMethod/Jig.cs b/src/IFoxCAD.Cad/ExtensionMethod/Jig.cs index edd9f3d..395ebbd 100644 --- a/src/IFoxCAD.Cad/ExtensionMethod/Jig.cs +++ b/src/IFoxCAD.Cad/ExtensionMethod/Jig.cs @@ -188,15 +188,12 @@ public PromptResult Drag() if (Entitys.Length == 0) return null; - var ids = new List(); IEnumerable es = Entitys; if (removeEntity != null) es = es.Except(removeEntity);//差集 - foreach (var item in es) - ids.Add(tr.CurrentSpace.AddEntity(item)); - - return ids; + //若用户需要指定空间生成,那么利用 Entitys 在仿照此函数进行 + return tr.CurrentSpace.AddEntity(es); } #endregion diff --git a/src/IFoxCAD.Cad/Runtime/Env.cs b/src/IFoxCAD.Cad/Runtime/Env.cs index b2c6e5e..7cd9c20 100644 --- a/src/IFoxCAD.Cad/Runtime/Env.cs +++ b/src/IFoxCAD.Cad/Runtime/Env.cs @@ -453,10 +453,8 @@ public static void SetVar(string varName, object value) } catch (System.Exception) { - Env.Print($"{varName} 是不存在的变量!"); } - } /// /// 获取系统环境变量 -- Gitee From dc30158a05b559f0083f31c5a347c19408a1caa8 Mon Sep 17 00:00:00 2001 From: liuqihong <540762622@qq.com> Date: Fri, 5 Aug 2022 22:48:43 +0800 Subject: [PATCH 323/675] =?UTF-8?q?=E5=A2=9E=E5=8A=A0=E5=A1=AB=E5=85=85?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../ExtensionMethod/BulgeVertexWidth.cs | 94 +++++ src/IFoxCAD.Cad/ExtensionMethod/HatchEx.cs | 365 ++++++++++++++++++ src/IFoxCAD.Cad/ExtensionMethod/PointEx.cs | 35 ++ .../ExtensionMethod/SymbolTableRecordEx.cs | 42 +- 4 files changed, 531 insertions(+), 5 deletions(-) create mode 100644 src/IFoxCAD.Cad/ExtensionMethod/BulgeVertexWidth.cs create mode 100644 src/IFoxCAD.Cad/ExtensionMethod/HatchEx.cs diff --git a/src/IFoxCAD.Cad/ExtensionMethod/BulgeVertexWidth.cs b/src/IFoxCAD.Cad/ExtensionMethod/BulgeVertexWidth.cs new file mode 100644 index 0000000..d44b2d0 --- /dev/null +++ b/src/IFoxCAD.Cad/ExtensionMethod/BulgeVertexWidth.cs @@ -0,0 +1,94 @@ +#if !HC2020 +using Autodesk.AutoCAD.DatabaseServices; +using Autodesk.AutoCAD.Geometry; +#else +using GrxCAD.DatabaseServices; +using GrxCAD.Geometry; +#endif +using System; + +namespace IFoxCAD; + +/// +/// 多段线的顶点,凸度,头宽,尾宽 +/// +[Serializable] +public class BulgeVertexWidth +{ + /// + /// 顶点X + /// + public double X; + /// + /// 顶点Y + /// + public double Y; + /// + /// 凸度 + /// + public double Bulge; + /// + /// 头宽 + /// + public double StartWidth; + /// + /// 尾宽 + /// + public double EndWidth; + + public Point2d Vertex => new(X, Y); + + public BulgeVertexWidth() { } + + /// + /// 多段线的顶点,凸度,头宽,尾宽 + /// + public BulgeVertexWidth(double vertex_X, double vertex_Y, + double bulge = 0, + double startWidth = 0, + double endWidth = 0) + { + X = vertex_X; + Y = vertex_Y; + Bulge = bulge; + StartWidth = startWidth; + EndWidth = endWidth; + } + + /// + /// 多段线的顶点,凸度,头宽,尾宽 + /// + public BulgeVertexWidth(Point2d vertex, + double bulge = 0, + double startWidth = 0, + double endWidth = 0) + : this(vertex.X, vertex.Y, bulge, startWidth, endWidth) + { } + + /// + /// 多段线的顶点,凸度,头宽,尾宽 + /// + public BulgeVertexWidth(BulgeVertex bv) + : this(bv.Vertex.X, bv.Vertex.Y, bv.Bulge) + { } + + /// + /// 多段线的顶点,凸度,头宽,尾宽 + /// + /// 多段线 + /// 子段编号 + public BulgeVertexWidth(Polyline pl, int index) + { + var pt = pl.GetPoint2dAt(index);//这里可以3d + X = pt.X; + Y = pt.Y; + Bulge = pl.GetBulgeAt(index); + StartWidth = pl.GetStartWidthAt(index); + EndWidth = pl.GetEndWidthAt(index); + } + + public BulgeVertex ToBulgeVertex() + { + return new BulgeVertex(Vertex, Bulge); + } +} diff --git a/src/IFoxCAD.Cad/ExtensionMethod/HatchEx.cs b/src/IFoxCAD.Cad/ExtensionMethod/HatchEx.cs new file mode 100644 index 0000000..9457cb4 --- /dev/null +++ b/src/IFoxCAD.Cad/ExtensionMethod/HatchEx.cs @@ -0,0 +1,365 @@ +#if !HC2020 +using Autodesk.AutoCAD.DatabaseServices; +using Autodesk.AutoCAD.Geometry; +using Autodesk.AutoCAD.Colors; +#else +using GrxCAD.DatabaseServices; +using GrxCAD.Geometry; +using GrxCAD.Colors; +#endif +using System.Collections.Generic; +using System; +using IFoxCAD.Cad; + +namespace IFoxCAD; + +/* + * ӵĵһ߽߽,ڶͼı߽硣 + * Ҫⲿ߽,ʹӻΪ HatchLoopTypes.Outermost AppendLoop , + * һ߽类,ͿԼı߽硣 + * ڲ߽ʹô HatchLoopTypes.Default AppendLoop + * + * ߽ʱ,ӵ(߽,߽,߽,߽ͨ....) + * ߽ʱ,ӵ(߽,߽ͨ.....߽,߽ͨ....) + */ + +/// +/// ͼ +/// +public class HatchEx +{ + #region Ա + /// + /// ߽id(ŵһ) + /// + readonly List _boundaryIds; + /// + /// ͼԪ + /// + readonly Hatch _hatch; + /// + /// ߽(˴ֱ=>Ա,Ϊ뷴Ӧ) + /// + readonly bool _boundaryAssociative; + /// + /// :û(̶)//ݶļ + /// + string? _hatchName; + /// + /// ģʽ(Ԥ/û/Զ) + /// + HatchPatternType _patternTypeHatch; + /// + /// ģʽ + /// + GradientPatternType _patternTypeGradient; + /// + /// / + /// + double Scale => _hatch.PatternScale; + /// + /// Ƕ + /// + double Angle => _hatch.PatternAngle; + #endregion + + #region + HatchEx() + { + _hatch = new Hatch(); + _hatch.SetDatabaseDefaults(); + _boundaryIds = new(); + } + + /// + /// ͼ + /// + /// ߽ + /// ԭ + /// + /// Ƕ + public HatchEx(bool boundaryAssociative = true, + Point2d? hatchOrigin = null, + double hatchScale = 1, + double hatchAngle = 0) : this() + { + if (hatchScale <= 0) + throw new ArgumentException("Сڵ0"); + + _hatch.PatternScale = hatchScale;// + _hatch.PatternAngle = hatchAngle;//Ƕ + _boundaryAssociative = boundaryAssociative; + + hatchOrigin ??= Point2d.Origin; + _hatch.Origin = hatchOrigin.Value; //ԭ + } + + /// + /// ͼ + /// + /// ߽ + /// ߽ + /// ԭ + /// + /// Ƕ + public HatchEx(IEnumerable boundaryIds, + bool boundaryAssociative = true, + Point2d? hatchOrigin = null, + double hatchScale = 1, + double hatchAngle = 0) + : this(boundaryAssociative, hatchOrigin, hatchScale, hatchAngle) + { + _boundaryIds.AddRange(boundaryIds); + } + + #endregion + + #region + /// + /// ģʽ1:Ԥ + /// + public HatchEx Mode1PreDefined(string name) + { + _hatchName = name; + _hatch.HatchObjectType = HatchObjectType.HatchObject; //(/) + _patternTypeHatch = HatchPatternType.PreDefined; + return this; + } + + /// + /// ģʽ2:û + /// + /// Ƿ˫ + public HatchEx Mode2UserDefined(bool patternDouble = true) + { + _hatchName = "_USER"; + _hatch.HatchObjectType = HatchObjectType.HatchObject; //(/) + _patternTypeHatch = HatchPatternType.UserDefined; + + _hatch.PatternDouble = patternDouble; //Ƿ˫򣨱д SetHatchPattern ֮ǰ + _hatch.PatternSpace = Scale; //ࣨд SetHatchPattern ֮ǰ + return this; + } + + /// + /// ģʽ3:Զ + /// + /// + public HatchEx Mode3UserDefined(string name) + { + _hatchName = name; + _hatch.HatchObjectType = HatchObjectType.HatchObject; //(/) + _patternTypeHatch = HatchPatternType.CustomDefined; + return this; + } + + /// + /// ģʽ4: + /// + /// + /// ɫʼɫ + /// ɫɫ + /// ƶ + /// ɫֵ + /// ɫ˫ɫ + public HatchEx Mode4Gradient(GradientName name, Color colorStart, Color colorEnd, + float gradientShift = 0, + float shadeTintValue = 0, + bool gradientOneColorMode = false) + { + //entgetֱȻ"SOLID",Ϊ"","" + _hatchName = name.ToString(); + _hatch.HatchObjectType = HatchObjectType.GradientObject; //(/) + _patternTypeGradient = GradientPatternType.PreDefinedGradient;//ģʽ4: + //_patternTypeGradient = GradientPatternType.UserDefinedGradient;//ģʽ5:..ģʽɶ + + //ýɫʼͽɫ + var gColor1 = new GradientColor(colorStart, 0); + var gColor2 = new GradientColor(colorEnd, 1); + _hatch.SetGradientColors(new GradientColor[] { gColor1, gColor2 }); + + _hatch.GradientShift = gradientShift; //ݶλ + _hatch.ShadeTintValue = shadeTintValue; //Ӱɫֵ + _hatch.GradientOneColorMode = gradientOneColorMode;//䵥ɫ/˫ɫ + _hatch.GradientAngle = Angle; //Ƕ + + return this; + } + + /// + /// + /// + /// + /// ݿ + public ObjectId Build(DBTrans tr) + { + //ݿ + var hatchId = tr.CurrentSpace.AddEntity(_hatch); + + //ģʽ:/ + if (_hatch.HatchObjectType == HatchObjectType.GradientObject) + _hatch.SetGradient(_patternTypeGradient, _hatchName); + else + _hatch.SetHatchPattern(_patternTypeHatch, _hatchName); + + //߽,ݿռھͻ + //Ϊ true 뷴Ӧ,˱Ƚ(ά뽫ʮɺ),. + _hatch.Associative = _boundaryAssociative; + + // AppendLoop ؼ,Ͳ + if (_boundaryIds.Count > 0) + AppendLoop(_boundaryIds, HatchLoopTypes.Default); + + //䲢ʾ(߽,쳣) + _hatch.EvaluateHatch(true); + + return hatchId; + } + + /// + /// ִͼԪ޸ + /// + /// ӳʵ + public HatchEx Action(Action action) + { + action(_hatch); + return this; + } + + /// + /// ձ߽缯 + /// + public HatchEx ClearBoundary() + { + _boundaryIds.Clear(); + return this; + } + + /// + /// ɾ߽ͼԪ + /// + public HatchEx EraseBoundary(DBTrans tr) + { + for (int i = 0; i < _boundaryIds.Count; i++) + _boundaryIds[i].Erase(); + return this; + } + + /// + /// ߽ + /// + /// ߽id + /// 뷽ʽ + /// + void AppendLoop(IEnumerable boundaryIds, + HatchLoopTypes hatchLoopTypes = HatchLoopTypes.Default) + { + var obIds = new ObjectIdCollection(); + //߽DZպϵ,Ѿݿ + //պϻ. + foreach (var border in boundaryIds) + { + obIds.Clear(); + obIds.Add(border); + _hatch.AppendLoop(hatchLoopTypes, obIds); + } + obIds.Dispose(); + } + + /// + /// ߽(¸߰汾亯) + /// + /// 㼯 + /// ͹ȼ + /// + /// ݿ + /// 뷽ʽ + /// + public HatchEx AppendLoop(Point2dCollection pts!!, + DoubleCollection bluges, + DBTrans tr, + HatchLoopTypes hatchLoopTypes = HatchLoopTypes.Default) + { + var ptsEnd2End = pts.End2End(); +#if NET35 + _boundaryIds.Add(CreateAddBoundary(ptsEnd2End, bluges, tr)); +#else + //2011API,ԲͼԪ¼߽, + //ͨĻ,߽ _boundaryIds ǿյ,ô Build() ʱҪ˿յ + _hatch.AppendLoop(hatchLoopTypes, ptsEnd2End, bluges); +#endif + return this; + } + +#if NET35 + /// + /// ͨ㼯͹ɱ߽Ķ + /// + /// 㼯 + /// ͹ȼ + /// + /// id + static ObjectId CreateAddBoundary(Point2dCollection? pts, DoubleCollection? bluges, DBTrans tr) + { + if (pts is null) + throw new ArgumentException(null, nameof(pts)); + if (bluges is null) + throw new ArgumentException(null, nameof(bluges)); + + var bvws = new List(); + + var itor1 = pts.GetEnumerator(); + var itor2 = bluges.GetEnumerator(); + while (itor1.MoveNext() && itor2.MoveNext()) + bvws.Add(new BulgeVertexWidth(itor1.Current, itor2.Current)); + + return tr.CurrentSpace.AddPline(bvws); + } +#endif + #endregion + + #region ö + /// + /// ɫͼ + /// + public enum GradientName + { + /// + /// ״ + /// + Linear, + /// + /// Բ״ + /// + Cylinder, + /// + /// Բ״ + /// + Invcylinder, + /// + /// ״ + /// + Spherical, + /// + /// ״ + /// + Invspherical, + /// + /// ״ + /// + Hemisperical, + /// + /// ״ + /// + InvHemisperical, + /// + /// ״ + /// + Curved, + /// + /// ״ + /// + Incurved + } + #endregion +} \ No newline at end of file diff --git a/src/IFoxCAD.Cad/ExtensionMethod/PointEx.cs b/src/IFoxCAD.Cad/ExtensionMethod/PointEx.cs index c534be6..c91ad2b 100644 --- a/src/IFoxCAD.Cad/ExtensionMethod/PointEx.cs +++ b/src/IFoxCAD.Cad/ExtensionMethod/PointEx.cs @@ -43,4 +43,39 @@ public static double GetAngle(this Point2d startPoint, Point2d endPoint) } + + #region 首尾相连 + /// + /// 首尾相连 + /// + public static Point2dCollection End2End(this Point2dCollection ptcol!!) + { + if (ptcol.Count == 0 || ptcol[0].Equals(ptcol[^1]))//首尾相同直接返回 + return ptcol; + + //首尾不同,去加一个到最后 + var lst = new Point2d[ptcol.Count + 1]; + for (int i = 0; i < lst.Length; i++) + lst[i] = ptcol[i]; + lst[^1] = lst[0]; + + return new(lst); + } + /// + /// 首尾相连 + /// + public static Point3dCollection End2End(this Point3dCollection ptcol!!) + { + if (ptcol.Count == 0 || ptcol[0].Equals(ptcol[^1]))//首尾相同直接返回 + return ptcol; + + //首尾不同,去加一个到最后 + var lst = new Point3d[ptcol.Count + 1]; + for (int i = 0; i < lst.Length; i++) + lst[i] = ptcol[i]; + lst[^1] = lst[0]; + + return new(lst); + } + #endregion } \ No newline at end of file diff --git a/src/IFoxCAD.Cad/ExtensionMethod/SymbolTableRecordEx.cs b/src/IFoxCAD.Cad/ExtensionMethod/SymbolTableRecordEx.cs index 2f0a240..3d4ac97 100644 --- a/src/IFoxCAD.Cad/ExtensionMethod/SymbolTableRecordEx.cs +++ b/src/IFoxCAD.Cad/ExtensionMethod/SymbolTableRecordEx.cs @@ -157,6 +157,38 @@ public static ObjectId AddCircle(this BlockTableRecord btr, Point3d p0, Point3d /// 在指定的绘图空间添加轻多段线 /// /// 绘图空间 + /// 多段线信息 + /// 线宽 + /// 是否闭合 + /// 轻多段线属性设置委托 + /// 事务管理器 + /// 轻多段线id + public static ObjectId AddPline(this BlockTableRecord btr, + List bvws, + double? constantWidth = null, + bool isClosed = true, + Action? action = default, + Transaction? trans = default) + { + var pl = new Polyline(); + pl.SetDatabaseDefaults(); + if (constantWidth is not null) + { + for (int i = 0; i < bvws.Count; i++) + pl.AddVertexAt(i, bvws[i].Vertex, bvws[i].Bulge, constantWidth.Value, constantWidth.Value); + } + else + { + for (int i = 0; i < bvws.Count; i++) + pl.AddVertexAt(i, bvws[i].Vertex, bvws[i].Bulge, bvws[i].StartWidth, bvws[i].EndWidth); + } + pl.Closed = isClosed;//闭合 + return btr.AddEnt(pl, action, trans); + } + /// + /// 在指定的绘图空间添加轻多段线 + /// + /// 绘图空间 /// 端点表 /// 凸度表 /// 端点的起始宽度 @@ -172,15 +204,15 @@ public static ObjectId AddPline(this BlockTableRecord btr, Action? action = default, Transaction? trans = default) { - bulges ??= new List(new double[pts.Count]); - startWidths ??= new List(new double[pts.Count]); - endWidths ??= new List(new double[pts.Count]); + bulges ??= new(new double[pts.Count]); + startWidths ??= new(new double[pts.Count]); + endWidths ??= new(new double[pts.Count]); + Polyline pl = new(); pl.SetDatabaseDefaults(); + for (int i = 0; i < pts.Count; i++) - { pl.AddVertexAt(i, pts[i].Point2d(), bulges[i], startWidths[i], endWidths[i]); - } return btr.AddEnt(pl, action, trans); } -- Gitee From 2613f2397294b89eae0fff2256d1251c324806d8 Mon Sep 17 00:00:00 2001 From: liuqihong <540762622@qq.com> Date: Fri, 5 Aug 2022 23:05:55 +0800 Subject: [PATCH 324/675] =?UTF-8?q?=E4=BF=AE=E6=94=B9=E5=8A=A0=E5=85=A5?= =?UTF-8?q?=E7=A9=BA=E9=97=B4=E7=9A=84=E5=8F=82=E6=95=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/IFoxCAD.Cad/ExtensionMethod/HatchEx.cs | 25 +++++++++++----------- src/IFoxCAD.Cad/ExtensionMethod/Jig.cs | 7 +++--- 2 files changed, 15 insertions(+), 17 deletions(-) diff --git a/src/IFoxCAD.Cad/ExtensionMethod/HatchEx.cs b/src/IFoxCAD.Cad/ExtensionMethod/HatchEx.cs index 9457cb4..a6a2647 100644 --- a/src/IFoxCAD.Cad/ExtensionMethod/HatchEx.cs +++ b/src/IFoxCAD.Cad/ExtensionMethod/HatchEx.cs @@ -189,12 +189,11 @@ public HatchEx Mode4Gradient(GradientName name, Color colorStart, Color colorEnd /// /// /// - /// - /// ݿ - public ObjectId Build(DBTrans tr) + /// ˿ռ + public ObjectId Build(BlockTableRecord btrOfAddEntitySpace) { //ݿ - var hatchId = tr.CurrentSpace.AddEntity(_hatch); + var hatchId = btrOfAddEntitySpace.AddEntity(_hatch); //ģʽ:/ if (_hatch.HatchObjectType == HatchObjectType.GradientObject) @@ -238,7 +237,7 @@ public HatchEx ClearBoundary() /// /// ɾ߽ͼԪ /// - public HatchEx EraseBoundary(DBTrans tr) + public HatchEx EraseBoundary() { for (int i = 0; i < _boundaryIds.Count; i++) _boundaryIds[i].Erase(); @@ -250,7 +249,6 @@ public HatchEx EraseBoundary(DBTrans tr) /// /// ߽id /// 뷽ʽ - /// void AppendLoop(IEnumerable boundaryIds, HatchLoopTypes hatchLoopTypes = HatchLoopTypes.Default) { @@ -271,18 +269,17 @@ void AppendLoop(IEnumerable boundaryIds, /// /// 㼯 /// ͹ȼ - /// - /// ݿ + /// ˿ռ /// 뷽ʽ /// public HatchEx AppendLoop(Point2dCollection pts!!, DoubleCollection bluges, - DBTrans tr, + BlockTableRecord btrOfAddEntitySpace, HatchLoopTypes hatchLoopTypes = HatchLoopTypes.Default) { var ptsEnd2End = pts.End2End(); #if NET35 - _boundaryIds.Add(CreateAddBoundary(ptsEnd2End, bluges, tr)); + _boundaryIds.Add(CreateAddBoundary(ptsEnd2End, bluges, btrOfAddEntitySpace)); #else //2011API,ԲͼԪ¼߽, //ͨĻ,߽ _boundaryIds ǿյ,ô Build() ʱҪ˿յ @@ -297,9 +294,11 @@ public HatchEx AppendLoop(Point2dCollection pts!!, /// /// 㼯 /// ͹ȼ - /// + /// ˿ռ /// id - static ObjectId CreateAddBoundary(Point2dCollection? pts, DoubleCollection? bluges, DBTrans tr) + static ObjectId CreateAddBoundary(Point2dCollection? pts, + DoubleCollection? bluges, + BlockTableRecord btrOfAddEntitySpace) { if (pts is null) throw new ArgumentException(null, nameof(pts)); @@ -313,7 +312,7 @@ static ObjectId CreateAddBoundary(Point2dCollection? pts, DoubleCollection? blug while (itor1.MoveNext() && itor2.MoveNext()) bvws.Add(new BulgeVertexWidth(itor1.Current, itor2.Current)); - return tr.CurrentSpace.AddPline(bvws); + return btrOfAddEntitySpace.AddPline(bvws); } #endif #endregion diff --git a/src/IFoxCAD.Cad/ExtensionMethod/Jig.cs b/src/IFoxCAD.Cad/ExtensionMethod/Jig.cs index 395ebbd..d115ebf 100644 --- a/src/IFoxCAD.Cad/ExtensionMethod/Jig.cs +++ b/src/IFoxCAD.Cad/ExtensionMethod/Jig.cs @@ -179,10 +179,10 @@ public PromptResult Drag() /// /// 最后一次的图元加入数据库 /// - /// 事务 + /// 加入此空间 /// 不生成的图元用于排除,例如刷新时候的提示文字 /// - public IEnumerable? AddEntityToMsPs(DBTrans tr, + public IEnumerable? AddEntityToMsPs(BlockTableRecord btrOfAddEntitySpace, IEnumerable? removeEntity = null) { if (Entitys.Length == 0) @@ -192,8 +192,7 @@ public PromptResult Drag() if (removeEntity != null) es = es.Except(removeEntity);//差集 - //若用户需要指定空间生成,那么利用 Entitys 在仿照此函数进行 - return tr.CurrentSpace.AddEntity(es); + return btrOfAddEntitySpace.AddEntity(es); } #endregion -- Gitee From 309e6b77bc9d1375cb3ffb9d869159706e663f66 Mon Sep 17 00:00:00 2001 From: liuqihong <540762622@qq.com> Date: Sat, 6 Aug 2022 07:58:50 +0800 Subject: [PATCH 325/675] =?UTF-8?q?=E5=87=8F=E5=B0=91=E8=BD=AC=E6=8D=A2?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/IFoxCAD.Cad/ExtensionMethod/Jig.cs | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/src/IFoxCAD.Cad/ExtensionMethod/Jig.cs b/src/IFoxCAD.Cad/ExtensionMethod/Jig.cs index d115ebf..b2b2e9a 100644 --- a/src/IFoxCAD.Cad/ExtensionMethod/Jig.cs +++ b/src/IFoxCAD.Cad/ExtensionMethod/Jig.cs @@ -10,7 +10,6 @@ */ namespace IFoxCAD.Cad; - using Autodesk.AutoCAD.GraphicsInterface; using Acap = Application; @@ -181,14 +180,15 @@ public PromptResult Drag() /// /// 加入此空间 /// 不生成的图元用于排除,例如刷新时候的提示文字 - /// + /// 加入数据库的id集合 public IEnumerable? AddEntityToMsPs(BlockTableRecord btrOfAddEntitySpace, - IEnumerable? removeEntity = null) + IEnumerable? removeEntity = null) { - if (Entitys.Length == 0) + //内部用 _drawEntitys 外部用 Entitys,减少一层转换 + if (_drawEntitys.Count == 0) return null; - IEnumerable es = Entitys; + IEnumerable es = _drawEntitys; if (removeEntity != null) es = es.Except(removeEntity);//差集 @@ -253,7 +253,6 @@ public void DatabaseEntityDraw(WorldDrawEvent action) WorldDrawEvent = action; } - /* WorldDraw 封装外的操作说明: * 0x01 * 我有一个业务是一次性生成四个方向的箭头,因为cad08缺少瞬时图元, -- Gitee From 9accad991399c44a6bd36e0d47774c027a9ffc8d Mon Sep 17 00:00:00 2001 From: liuqihong <540762622@qq.com> Date: Sat, 6 Aug 2022 07:59:15 +0800 Subject: [PATCH 326/675] =?UTF-8?q?=E5=A1=AB=E5=85=85=E5=8A=9F=E8=83=BD?= =?UTF-8?q?=E8=A1=A5=E5=85=85?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/IFoxCAD.Cad/Algorithms/QuadTree/Rect.cs | 2 +- .../ExtensionMethod/BulgeVertexWidth.cs | 2 +- .../ExtensionMethod/CollectionEx.cs | 95 +++++ src/IFoxCAD.Cad/ExtensionMethod/Curve3dEx.cs | 67 ++-- src/IFoxCAD.Cad/ExtensionMethod/CurveEx.cs | 55 ++- .../ExtensionMethod/KeywordCollectionEx.cs | 68 ---- src/IFoxCAD.Cad/ExtensionMethod/PointEx.cs | 43 ++- .../ExtensionMethod/SymbolTableRecordEx.cs | 41 +- .../HatchConverter.cs" | 364 ++++++++++++++++++ .../HatchEx.cs" | 20 + .../HatchInfo.cs" | 35 +- 11 files changed, 628 insertions(+), 164 deletions(-) delete mode 100644 src/IFoxCAD.Cad/ExtensionMethod/KeywordCollectionEx.cs create mode 100644 "src/IFoxCAD.Cad/ExtensionMethod/\346\226\260\345\273\272\345\241\253\345\205\205/HatchConverter.cs" create mode 100644 "src/IFoxCAD.Cad/ExtensionMethod/\346\226\260\345\273\272\345\241\253\345\205\205/HatchEx.cs" rename src/IFoxCAD.Cad/ExtensionMethod/HatchEx.cs => "src/IFoxCAD.Cad/ExtensionMethod/\346\226\260\345\273\272\345\241\253\345\205\205/HatchInfo.cs" (93%) diff --git a/src/IFoxCAD.Cad/Algorithms/QuadTree/Rect.cs b/src/IFoxCAD.Cad/Algorithms/QuadTree/Rect.cs index 3488ca5..15e8e11 100644 --- a/src/IFoxCAD.Cad/Algorithms/QuadTree/Rect.cs +++ b/src/IFoxCAD.Cad/Algorithms/QuadTree/Rect.cs @@ -474,7 +474,7 @@ static bool CrossAclockwise(Point2d o, Point2d a, Point2d b) } #if !WinForm - public Autodesk.AutoCAD.DatabaseServices.Entity ToPolyLine() + public Entity ToPolyLine() { var bv = new List(); var pts = ToPoints(); diff --git a/src/IFoxCAD.Cad/ExtensionMethod/BulgeVertexWidth.cs b/src/IFoxCAD.Cad/ExtensionMethod/BulgeVertexWidth.cs index d44b2d0..c6eead5 100644 --- a/src/IFoxCAD.Cad/ExtensionMethod/BulgeVertexWidth.cs +++ b/src/IFoxCAD.Cad/ExtensionMethod/BulgeVertexWidth.cs @@ -90,5 +90,5 @@ public BulgeVertexWidth(Polyline pl, int index) public BulgeVertex ToBulgeVertex() { return new BulgeVertex(Vertex, Bulge); - } + } } diff --git a/src/IFoxCAD.Cad/ExtensionMethod/CollectionEx.cs b/src/IFoxCAD.Cad/ExtensionMethod/CollectionEx.cs index 179c062..bfbed62 100644 --- a/src/IFoxCAD.Cad/ExtensionMethod/CollectionEx.cs +++ b/src/IFoxCAD.Cad/ExtensionMethod/CollectionEx.cs @@ -100,4 +100,99 @@ public static void ForEach(this IEnumerable source, Action action) } + + #region 关键字集合 + public enum KeywordName + { + GlobalName, + LocalName, + DisplayName, + } + + /// + /// 含有关键字 + /// + /// 关键字集合 + /// 关键字 + /// 关键字容器字段名 + /// true含有 + public static bool Contains(this KeywordCollection collection, string name, + KeywordName keywordName = KeywordName.GlobalName) + { + bool contains = false; + switch (keywordName) + { + case KeywordName.GlobalName: + for (int i = 0; i < collection.Count; i++) + if (collection[i].GlobalName == name) + { + contains = true; + break; + } + break; + case KeywordName.LocalName: + for (int i = 0; i < collection.Count; i++) + if (collection[i].LocalName == name) + { + contains = true; + break; + } + break; + case KeywordName.DisplayName: + for (int i = 0; i < collection.Count; i++) + if (collection[i].DisplayName == name) + { + contains = true; + break; + } + break; + default: + break; + } + return contains; + } + + /// + /// 获取词典, + /// KeywordCollection是允许重复关键字的,没有哈希索引,在多次判断时候会遍历多次O(n),所以生成一个词典进行O(1) + /// + /// + /// + public static Dictionary GetDict(this KeywordCollection collection) + { + Dictionary map = new(); + for (int i = 0; i < collection.Count; i++) + map.Add(collection[i].GlobalName, collection[i].DisplayName); + return map; + } + #endregion + + + #region IdMapping + /// + /// 旧块名 + /// + /// + /// + public static List GetKeys(this IdMapping idmap) + { + List ids = new(); + foreach (IdPair item in idmap) + ids.Add(item.Key); + return ids; + } + + /// + /// 新块名 + /// + /// + /// + public static List GetValues(this IdMapping idmap) + { + List ids = new(); + foreach (IdPair item in idmap) + ids.Add(item.Value); + return ids; + } + #endregion } diff --git a/src/IFoxCAD.Cad/ExtensionMethod/Curve3dEx.cs b/src/IFoxCAD.Cad/ExtensionMethod/Curve3dEx.cs index 226e5de..57ca8fd 100644 --- a/src/IFoxCAD.Cad/ExtensionMethod/Curve3dEx.cs +++ b/src/IFoxCAD.Cad/ExtensionMethod/Curve3dEx.cs @@ -29,9 +29,8 @@ public static List GetParamsAtIntersectionPoints(this Curve3d c3d) CurveCurveIntersector3d cci = new(c3d, c3d, Vector3d.ZAxis); List pars = new(); for (int i = 0; i < cci.NumberOfIntersectionPoints; i++) - { pars.AddRange(cci.GetIntersectionParameters(i)); - } + pars.Sort(); return pars; } @@ -174,16 +173,16 @@ public static List GetSplitCurves(this CompositeCurve3d c3d, L { pars[0] = inter.LowerBound; //又包含终点,去除终点 - if (Tolerance.Global.IsEqualPoint(pars[pars.Count - 1], inter.UpperBound)) + if (Tolerance.Global.IsEqualPoint(pars[^1], inter.UpperBound)) { pars.RemoveAt(pars.Count - 1); if (pars.Count == 1) return new List(); } } - else if (Tolerance.Global.IsEqualPoint(pars[pars.Count - 1], inter.UpperBound)) + else if (Tolerance.Global.IsEqualPoint(pars[^1], inter.UpperBound)) { - pars[pars.Count - 1] = inter.UpperBound; + pars[^1] = inter.UpperBound; } //加入第一点以支持反向打断 pars.Add(pars[0]); @@ -200,8 +199,8 @@ public static List GetSplitCurves(this CompositeCurve3d c3d, L pars[0] = inter.LowerBound; else pars.Insert(0, inter.LowerBound); - if (Tolerance.Global.IsEqualPoint(pars[pars.Count - 1], inter.UpperBound)) - pars[pars.Count - 1] = inter.UpperBound; + if (Tolerance.Global.IsEqualPoint(pars[^1], inter.UpperBound)) + pars[^1] = inter.UpperBound; else pars.Add(inter.UpperBound); } @@ -245,7 +244,7 @@ public static List GetSplitCurves(this CompositeCurve3d c3d, L var cus1 = curves[curves.Count - 1].GetCurves(); var cus2 = curves[0].GetCurves(); var cs = cus1.Combine2(cus2); - curves[curves.Count - 1] = new CompositeCurve3d(cs); + curves[^1] = new CompositeCurve3d(cs); curves.RemoveAt(0); } return curves; @@ -260,38 +259,30 @@ public static List GetSplitCurves(this CompositeCurve3d c3d, L { Curve3d[] cs = curve.GetCurves(); if (cs.Length == 0) - { return null; - } - else if (cs.Length == 1) - { + if (cs.Length == 1) return ToCurve(cs[0]); - } - else - { - bool hasNurb = false; - for (int i = 0; i < cs.Length; i++) - { - var c = cs[i]; - if (c is NurbCurve3d || c is EllipticalArc3d) - { - hasNurb = true; - break; - } - } - if (hasNurb) - { - var nc3d = cs[0].ToNurbCurve3d(); - for (int i = 1; i < cs.Length; i++) - nc3d?.JoinWith(cs[i].ToNurbCurve3d()); - return nc3d?.ToCurve(); - } - else + bool hasNurb = false; + + for (int i = 0; i < cs.Length; i++) + { + var c = cs[i]; + if (c is NurbCurve3d || c is EllipticalArc3d) { - return ToPolyline(curve); + hasNurb = true; + break; } } + if (hasNurb) + { + var nc3d = cs[0].ToNurbCurve3d(); + for (int i = 1; i < cs.Length; i++) + nc3d?.JoinWith(cs[i].ToNurbCurve3d()); + return nc3d?.ToCurve(); + } + + return ToPolyline(curve); } /// @@ -301,10 +292,10 @@ public static List GetSplitCurves(this CompositeCurve3d c3d, L /// 实体类多段线 public static Polyline ToPolyline(this CompositeCurve3d cc3d) { - Polyline pl = new() - { - Elevation = cc3d.StartPoint[2] - }; + Polyline pl = new(); + pl.SetDatabaseDefaults(); + pl.Elevation = cc3d.StartPoint[2]; + Plane plane = pl.GetPlane(); Point2d endver = Point2d.Origin; int i = 0; diff --git a/src/IFoxCAD.Cad/ExtensionMethod/CurveEx.cs b/src/IFoxCAD.Cad/ExtensionMethod/CurveEx.cs index bb8b04e..e1349e5 100644 --- a/src/IFoxCAD.Cad/ExtensionMethod/CurveEx.cs +++ b/src/IFoxCAD.Cad/ExtensionMethod/CurveEx.cs @@ -408,9 +408,9 @@ public static NurbCurve3d ToCurve3d(this Spline spl) case Poly2dType.SimplePoly: case Poly2dType.FitCurvePoly: Polyline pl = new(); + pl.SetDatabaseDefaults(); pl.ConvertFrom(pl2d, false); return ToCurve3d(pl); - default: return ToNurbCurve3d(pl2d); } @@ -432,6 +432,7 @@ public static NurbCurve3d ToCurve3d(this Spline spl) case Poly2dType.SimplePoly: case Poly2dType.FitCurvePoly: Polyline pl = new(); + pl.SetDatabaseDefaults(); pl.ConvertFrom(pl2d, false); return ToNurbCurve3d(pl); @@ -579,7 +580,8 @@ public static void ChamferAt(this Polyline polyline, int index, double radius, b if (index < 1 || index > polyline.NumberOfVertices - 2) throw new System.Exception("错误的索引号"); - if (polyline.GetSegmentType(index - 1) != SegmentType.Line || polyline.GetSegmentType(index) != SegmentType.Line) + if (SegmentType.Line != polyline.GetSegmentType(index - 1) || + SegmentType.Line != polyline.GetSegmentType(index)) throw new System.Exception("非直线段不能倒角"); //获取当前索引号的前后两段直线,并组合为Ge复合曲线 @@ -589,7 +591,7 @@ public static void ChamferAt(this Polyline polyline, int index, double radius, b polyline.GetLineSegmentAt(index - 1), polyline.GetLineSegmentAt(index) }; - var cc3d = new CompositeCurve3d(c3ds); + CompositeCurve3d cc3d = new(c3ds); //试倒直角 //子曲线的个数有三种情况: @@ -610,17 +612,14 @@ public static void ChamferAt(this Polyline polyline, int index, double radius, b c3ds = newcc3d!.GetCurves(); if (c3ds.Length == 3) { - c3ds = - cc3d.GetTrimmedOffset - ( - -radius, - Vector3d.ZAxis, - OffsetCurveExtensionType.Chamfer - ); + c3ds = cc3d.GetTrimmedOffset + ( + -radius, + Vector3d.ZAxis, + OffsetCurveExtensionType.Chamfer + ); if (c3ds.Length == 0 || c3ds[0] is LineSegment3d) - { throw new System.Exception("倒角半径过大"); - } } else if (c3ds.Length == 2) { @@ -633,28 +632,28 @@ public static void ChamferAt(this Polyline polyline, int index, double radius, b } //GetTrimmedOffset会生成倒角+偏移,故先反方向倒角,再倒回 - c3ds = - cc3d.GetTrimmedOffset - ( - -radius, - Vector3d.ZAxis, - OffsetCurveExtensionType.Extend - ); + c3ds = cc3d.GetTrimmedOffset + ( + -radius, + Vector3d.ZAxis, + OffsetCurveExtensionType.Extend + ); OffsetCurveExtensionType type = isFillet ? OffsetCurveExtensionType.Fillet : OffsetCurveExtensionType.Chamfer; - c3ds = - c3ds[0].GetTrimmedOffset - ( - radius, - Vector3d.ZAxis, - type - ); + c3ds = c3ds[0].GetTrimmedOffset + ( + radius, + Vector3d.ZAxis, + type + ); //将结果Ge曲线转为Db曲线,并将相关的数值反映到原曲线 - Polyline? plTemp = c3ds[0].ToCurve() as Polyline; + var plTemp = c3ds[0].ToCurve() as Polyline; + if (plTemp == null) + return; polyline.RemoveVertexAt(index); - polyline.AddVertexAt(index, plTemp!.GetPoint2dAt(1), plTemp.GetBulgeAt(1), 0, 0); + polyline.AddVertexAt(index, plTemp.GetPoint2dAt(1), plTemp.GetBulgeAt(1), 0, 0); polyline.AddVertexAt(index + 1, plTemp.GetPoint2dAt(2), 0, 0, 0); } diff --git a/src/IFoxCAD.Cad/ExtensionMethod/KeywordCollectionEx.cs b/src/IFoxCAD.Cad/ExtensionMethod/KeywordCollectionEx.cs deleted file mode 100644 index 0c77702..0000000 --- a/src/IFoxCAD.Cad/ExtensionMethod/KeywordCollectionEx.cs +++ /dev/null @@ -1,68 +0,0 @@ -namespace IFoxCAD.Cad; - -public static class KeywordCollectionEx -{ - public enum KeywordName - { - GlobalName, - LocalName, - DisplayName, - } - - /// - /// 含有关键字 - /// - /// 关键字集合 - /// 关键字 - /// 关键字容器字段名 - /// true含有 - public static bool Contains(this KeywordCollection collection, string name, - KeywordName keywordName = KeywordName.GlobalName) - { - bool contains = false; - switch (keywordName) - { - case KeywordName.GlobalName: - for (int i = 0; i < collection.Count; i++) - if (collection[i].GlobalName == name) - { - contains = true; - break; - } - break; - case KeywordName.LocalName: - for (int i = 0; i < collection.Count; i++) - if (collection[i].LocalName == name) - { - contains = true; - break; - } - break; - case KeywordName.DisplayName: - for (int i = 0; i < collection.Count; i++) - if (collection[i].DisplayName == name) - { - contains = true; - break; - } - break; - default: - break; - } - return contains; - } - - /// - /// 获取词典, - /// KeywordCollection是允许重复关键字的,没有哈希索引,在多次判断时候会遍历多次O(n),所以生成一个词典进行O(1) - /// - /// - /// - public static Dictionary GetDict(this KeywordCollection collection) - { - Dictionary map = new(); - for (int i = 0; i < collection.Count; i++) - map.Add(collection[i].GlobalName, collection[i].DisplayName); - return map; - } -} \ No newline at end of file diff --git a/src/IFoxCAD.Cad/ExtensionMethod/PointEx.cs b/src/IFoxCAD.Cad/ExtensionMethod/PointEx.cs index c91ad2b..889686b 100644 --- a/src/IFoxCAD.Cad/ExtensionMethod/PointEx.cs +++ b/src/IFoxCAD.Cad/ExtensionMethod/PointEx.cs @@ -29,7 +29,8 @@ public static string GetHashString(this Point3d pt, int xyz = 3, int decimalReta /// 弧度值 public static double GetAngle(this Point3d startPoint, Point3d endPoint, Vector3d? direction = null) { - return startPoint.GetVectorTo(endPoint).AngleOnPlane(new Plane(Point3d.Origin, direction ?? Vector3d.ZAxis)); + return startPoint.GetVectorTo(endPoint) + .AngleOnPlane(new Plane(Point3d.Origin, direction ?? Vector3d.ZAxis)); } /// /// 两点计算弧度范围0到2Pi @@ -42,6 +43,46 @@ public static double GetAngle(this Point2d startPoint, Point2d endPoint) return startPoint.GetVectorTo(endPoint).Angle; } + /// + /// 获取中点 + /// + /// + /// + /// + public static Point2d GetCenter(this Point2d a, Point2d b) + { + // (p1 + p2) / 2; //溢出风险 + return new Point2d(a.X * 0.5 + b.X * 0.5, + a.Y * 0.5 + b.Y * 0.5); + } + + /// http://www.lee-mac.com/bulgeconversion.html + /// + /// 求凸度,判断三点是否一条直线上 + /// + /// 圆弧起点 + /// 圆弧腰点 + /// 圆弧尾点 + /// 逆时针为正,顺时针为负 + public static double GetArcBulge(this Point2d arc1, Point2d arc2, Point2d arc3, double tol = 1e-10) + { + double dStartAngle = arc2.GetAngle(arc1); + double dEndAngle = arc2.GetAngle(arc3); + //求的P1P2与P1P3夹角 + var talAngle = (Math.PI - dStartAngle + dEndAngle) / 2; + //凸度==拱高/半弦长==拱高比值/半弦长比值 + //有了比值就不需要拿到拱高值和半弦长值了,因为接下来是相除得凸度 + double bulge = Math.Sin(talAngle) / Math.Cos(talAngle); + + //处理精度 + if (bulge > 0.9999 && bulge < 1.0001) + bulge = 1; + else if (bulge < -0.9999 && bulge > -1.0001) + bulge = -1; + else if (Math.Abs(bulge) < tol) + bulge = 0; + return bulge; + } #region 首尾相连 diff --git a/src/IFoxCAD.Cad/ExtensionMethod/SymbolTableRecordEx.cs b/src/IFoxCAD.Cad/ExtensionMethod/SymbolTableRecordEx.cs index 3d4ac97..2b9b2ee 100644 --- a/src/IFoxCAD.Cad/ExtensionMethod/SymbolTableRecordEx.cs +++ b/src/IFoxCAD.Cad/ExtensionMethod/SymbolTableRecordEx.cs @@ -7,6 +7,39 @@ public static class SymbolTableRecordEx { #region 块表记录 + #region 克隆实体id + /// + /// 克隆id(不允许是未添加数据库的图元)到块表记录(原地克隆) + /// + /// + /// 克隆到当前块表记录,相当于原地克隆 + /// 克隆到目标块表记录内,相当于制作新块 + /// + /// id集合,注意所有成员都要在同一个空间中 + /// 克隆后的id词典 + public static IdMapping EntityClone(this BlockTableRecord btr, + ObjectIdCollection objIds) + { + if (objIds is null || objIds.Count == 0) + throw new ArgumentNullException(nameof(objIds)); + + var db = objIds[0].Database; + IdMapping mapping = new(); + using (btr.ForWrite()) + { + try + { + //深度克隆 + db.DeepCloneObjects(objIds, btr.ObjectId, mapping, false); + //foreach (ObjectId item in blockIds) + // result.Add(mapping[item].Value); //获取克隆键值对(旧块名,新块名) + } + catch { } + } + return mapping; + } + #endregion + #region 添加实体 /// /// 添加实体对象 @@ -97,9 +130,8 @@ public static ObjectId AddEnt(this BlockTableRecord btr, Func action, Tr //transaction ??= DBTrans.Top.Transaction; var ent = action.Invoke(); if (ent is null) - { return ObjectId.Null; - } + return btr.AddEntity(ent, transaction); } @@ -148,9 +180,8 @@ public static ObjectId AddCircle(this BlockTableRecord btr, Point3d p0, Point3d var circle = EntityEx.CreateCircle(p0, p1, p2); //return circle is not null ? btr.AddEnt(circle, action, trans) : throw new ArgumentNullException(nameof(circle), "对象为 null"); if (circle is null) - { throw new ArgumentNullException(nameof(circle), "对象为 null"); - } + return btr.AddEnt(circle, action, trans); } /// @@ -170,7 +201,7 @@ public static ObjectId AddPline(this BlockTableRecord btr, Action? action = default, Transaction? trans = default) { - var pl = new Polyline(); + Polyline pl = new(); pl.SetDatabaseDefaults(); if (constantWidth is not null) { diff --git "a/src/IFoxCAD.Cad/ExtensionMethod/\346\226\260\345\273\272\345\241\253\345\205\205/HatchConverter.cs" "b/src/IFoxCAD.Cad/ExtensionMethod/\346\226\260\345\273\272\345\241\253\345\205\205/HatchConverter.cs" new file mode 100644 index 0000000..077e4e5 --- /dev/null +++ "b/src/IFoxCAD.Cad/ExtensionMethod/\346\226\260\345\273\272\345\241\253\345\205\205/HatchConverter.cs" @@ -0,0 +1,364 @@ +namespace IFoxCAD.Cad; +using Autodesk.AutoCAD.DatabaseServices; +using Autodesk.AutoCAD.Geometry; +using Autodesk.AutoCAD.Colors; +using System.Collections.Generic; +using System; + +using PointV = Point2d; + +/// +/// 填充边界转换器 +/// +public class HatchConverter +{ + #region 辅助类 + /// + /// 生成圆形数据 + /// + class CircleData + { + public PointV Center; + public double Radius; + + /// + /// 生成圆形数据 + /// + /// 对称点1 + /// 对称点2 + public CircleData(PointV symmetryAxisPoint1, PointV symmetryAxisPoint2) + { + Center = symmetryAxisPoint1.GetCenter(symmetryAxisPoint2); + Radius = symmetryAxisPoint1.GetDistanceTo(symmetryAxisPoint2) * 0.5; + } + } + + /// + /// 填充转换器的数据 + /// + class HatchConverterData + { + public List PolyLineData; + public List CircleData; + public List SplineData; + + /// + /// 填充转换器的数据 + /// + public HatchConverterData() + { + PolyLineData = new(); + CircleData = new(); + SplineData = new(); + } + } + #endregion + + #region 成员 + /// + /// 外部只能调用id,否则跨事务造成错误 + /// + public ObjectId OldHatchId + { + get + { + if (_oldHatch is null) + return ObjectId.Null; + return _oldHatch.ObjectId; + } + } + readonly Hatch? _oldHatch; + + readonly List _hcDatas; + /// + /// 生成的填充边界id + /// + public List BoundaryIds; + #endregion + + #region 构造 + /// + /// 填充边界转换器 + /// + HatchConverter() + { + _hcDatas = new(); + BoundaryIds = new(); + } + + /// + /// 填充边界转换器 + /// + /// 需要转化的Hatch对象 + public HatchConverter(Hatch hatch) : this() + { + _oldHatch = hatch; + + //不能在提取信息的时候进行新建cad图元, + //否则cad将会提示遗忘释放 + hatch.ForEach(loop => { + var hcData = new HatchConverterData(); + + bool isCurve2d = true; + if (loop.IsPolyline) + { + //边界是多段线 + HatchLoopIsPolyline(loop, hcData); + isCurve2d = false; + } + else + { + if (loop.Curves.Count == 2)//1是不可能的,大于2的是曲线 + { + //边界是曲线,过滤可能是圆形的情况 + var cir = TwoArcFormOneCircle(loop); + if (cir is not null) + { + hcData.CircleData.Add(cir); + isCurve2d = false; + } + } + } + + //边界是曲线 + if (isCurve2d) + HatchLoopIsCurve2d(loop, hcData); + + _hcDatas.Add(hcData); + }); + } + #endregion + + #region 方法 + /// + /// 多段线处理 + /// + /// 填充边界 + /// 收集图元信息 + static void HatchLoopIsPolyline(HatchLoop loop, HatchConverterData hcData) + { + if (loop is null) + throw new ArgumentNullException(nameof(loop)); + + if (hcData is null) + throw new ArgumentNullException(nameof(hcData)); + + //判断为圆形: + //上下两个圆弧,然后填充,就会生成此种填充 + //顶点数是3,凸度是半圆,两个半圆就是一个圆形 + if (loop.Polyline.Count == 3 && loop.Polyline[0].Bulge == 1 && loop.Polyline[1].Bulge == 1 || + loop.Polyline.Count == 3 && loop.Polyline[0].Bulge == -1 && loop.Polyline[1].Bulge == -1) + { + hcData.CircleData.Add(new CircleData(loop.Polyline[0].Vertex, loop.Polyline[1].Vertex)); + } + else + { + //遍历多段线信息 + var bvc = loop.Polyline; + for (int i = 0; i < bvc.Count; i++) + hcData.PolyLineData.Add(new BulgeVertexWidth(bvc[i])); + } + } + + /// + /// 两个圆弧组成圆形 + /// + /// + /// + static CircleData? TwoArcFormOneCircle(HatchLoop loop) + { + if (loop is null) + throw new ArgumentNullException(nameof(loop)); + + if (loop.Curves.Count != 2) + throw new ArgumentException( + "边界非多段线,而且点数!=2,点数为:" + nameof(loop.Curves.Count) + ";两个矩形交集的时候会出现此情况."); + + CircleData? circular = null; + + //判断为圆形: + //用一条(不是两条)多段线画出两条圆弧为正圆,就会生成此种填充 + //边界为曲线,数量为2,可能是两个半圆曲线,如果是,就加入圆形数据中 + + //第一段 + var getCurves1Pts = loop.Curves[0].GetSamplePoints(3); //曲线取样点分两份(3点) + var mid1Pt = getCurves1Pts[1]; //腰点 + double bulge1 = loop.Curves[0].StartPoint.GetArcBulge(mid1Pt, loop.Curves[0].EndPoint); + + //第二段 + var getCurves2Pts = loop.Curves[1].GetSamplePoints(3); + var mid2Pt = getCurves2Pts[1]; + double bulge2 = loop.Curves[1].StartPoint.GetArcBulge(mid2Pt, loop.Curves[1].EndPoint); + + //第一段上弧&&第二段反弧 || 第一段反弧&&第二段上弧 + if (bulge1 == -1 && bulge2 == -1 || bulge1 == 1 && bulge2 == 1) + circular = new CircleData(loop.Curves[0].StartPoint, loop.Curves[1].StartPoint); //两个起点就是对称点 + + return circular; + } + + /// + /// 处理边界曲线 + /// + /// 填充边界 + /// 收集图元信息 + static void HatchLoopIsCurve2d(HatchLoop loop, HatchConverterData hcData) + { + //取每一段曲线,曲线可能是直线来的,但是圆弧会按照顶点来分段 + int curveIsClosed = 0; + + //遍历边界的多个子段 + foreach (Curve2d curve in loop.Curves) + { + //计数用于实现闭合 + curveIsClosed++; + if (curve is NurbCurve2d spl) + { + //判断为样条曲线: + hcData.SplineData.Add(spl); + continue; + } + + var pts = curve.GetSamplePoints(3); + var midPt = pts[1]; + if (curve.StartPoint.IsEqualTo(curve.EndPoint, new Tolerance(1e-6, 1e-6)))//首尾相同,就是圆形 + { + //判断为圆形: + //获取起点,然后采样三点,中间就是对称点(直径点) + hcData.CircleData.Add(new CircleData(curve.StartPoint, midPt)); + continue; + } + + //判断为多段线,圆弧: + double bulge = curve.StartPoint.GetArcBulge(midPt, curve.EndPoint); + hcData.PolyLineData.Add(new BulgeVertexWidth(curve.StartPoint, bulge)); + + //末尾点,不闭合的情况下就要获取这个 + if (curveIsClosed == loop.Curves.Count) + hcData.PolyLineData.Add(new BulgeVertexWidth(curve.EndPoint, 0)); + } + } + + /// + /// 创建边界图元 + /// + /// 返回图元 + public void CreateBoundaryEntitys(List outEnts) + { + for (int i = 0; i < _hcDatas.Count; i++) + { + var data = _hcDatas[i]; + + //生成边界:多段线 + if (data.PolyLineData.Count > 0) + { + Polyline pl = new(); + pl.SetDatabaseDefaults(); + for (int j = 0; j < data.PolyLineData.Count; j++) + { + pl.AddVertexAt(j, + data.PolyLineData[j].Vertex, + data.PolyLineData[j].Bulge, + data.PolyLineData[j].StartWidth, + data.PolyLineData[j].EndWidth); + } + outEnts.Add(pl); + } + + //生成边界:圆 + data.CircleData.ForEach(item => { + outEnts.Add(new Circle(item.Center.Point3d(), Vector3d.ZAxis, item.Radius)); + }); + + //生成边界:样条曲线 + data.SplineData.ForEach(item => { + outEnts.Add(item.ToCurve()); + }); + } + + if (_oldHatch is not null) + { + outEnts.ForEach(ent => { + ent.Color = _oldHatch.Color; + ent.Layer = _oldHatch.Layer; + }); + } + } + + + /// + /// 创建边界图元和新填充到当前空间 + /// + /// 事务 + /// 数据库 + /// 边界关联 + /// 是否创建填充,false则只创建边界 + /// 新填充id,边界在获取 + public ObjectId CreateBoundarysAndHatchToMsPs(BlockTableRecord btrOfAddEntitySpace, + bool boundaryAssociative = true, + bool createHatchFlag = true, + Transaction? trans = null) + { + //重设边界之前肯定是有边界才可以 + if (BoundaryIds.Count == 0) + { + List boundaryEntitys = new(); + CreateBoundaryEntitys(boundaryEntitys); + boundaryEntitys.ForEach(ent => { + BoundaryIds.Add(btrOfAddEntitySpace.AddEntity(ent)); + }); + } + + if (!createHatchFlag) + return ObjectId.Null; + /* + * 此处为什么要克隆填充,而不是新建填充? + * 因为填充如果是新建的,那么将会丢失基点,概念如下: + * 两个一样的填充,平移其中一个,那么再提取他们的基点会是一样的! + * 所以生成时候就不等同于画面相同. + * 也因为我不知道什么新建方式可以新建一模一样的填充,因此使用了克隆 + * 那么它的平移后的基点在哪里呢? + */ + + var newHatchId = btrOfAddEntitySpace.EntityClone(new ObjectIdCollection(new ObjectId[] { OldHatchId })).GetValues()[0]; + trans ??= DBTrans.Top.Transaction; + var hatchEnt = trans.GetObject(newHatchId, OpenMode.ForWrite) as Hatch; + if (hatchEnt != null) + { + ResetBoundary(hatchEnt, boundaryAssociative); + hatchEnt.DowngradeOpen(); + } + return newHatchId; + } + + + /// + /// 重设边界 + /// + /// + /// 边界关联 + void ResetBoundary(Hatch hatch, + bool boundaryAssociative = true) + { + //删除原有边界 + while (hatch.NumberOfLoops != 0) + hatch.RemoveLoopAt(0); + + hatch.Associative = boundaryAssociative; + + var obIds = new ObjectIdCollection(); + for (int i = 0; i < BoundaryIds.Count; i++) + { + obIds.Clear(); + obIds.Add(BoundaryIds[i]); + //要先添加最外面的边界 + if (i == 0) + hatch.AppendLoop(HatchLoopTypes.Outermost, obIds); + else + hatch.AppendLoop(HatchLoopTypes.Default, obIds); + } + //计算填充并显示 + hatch.EvaluateHatch(true); + } + #endregion +} \ No newline at end of file diff --git "a/src/IFoxCAD.Cad/ExtensionMethod/\346\226\260\345\273\272\345\241\253\345\205\205/HatchEx.cs" "b/src/IFoxCAD.Cad/ExtensionMethod/\346\226\260\345\273\272\345\241\253\345\205\205/HatchEx.cs" new file mode 100644 index 0000000..a6c7fa0 --- /dev/null +++ "b/src/IFoxCAD.Cad/ExtensionMethod/\346\226\260\345\273\272\345\241\253\345\205\205/HatchEx.cs" @@ -0,0 +1,20 @@ +namespace IFoxCAD.Cad; +using Autodesk.AutoCAD.DatabaseServices; +using Autodesk.AutoCAD.Geometry; +using Acap = Autodesk.AutoCAD.ApplicationServices.Application; +using System; +using System.Collections.Generic; + +public static class HatchEx +{ + /// + /// 遍历填充每条边 + /// + /// + /// + public static void ForEach(this Hatch hatch, Action action) + { + for (int i = 0; i < hatch.NumberOfLoops; i++) + action.Invoke(hatch.GetLoopAt(i)); + } +} \ No newline at end of file diff --git a/src/IFoxCAD.Cad/ExtensionMethod/HatchEx.cs "b/src/IFoxCAD.Cad/ExtensionMethod/\346\226\260\345\273\272\345\241\253\345\205\205/HatchInfo.cs" similarity index 93% rename from src/IFoxCAD.Cad/ExtensionMethod/HatchEx.cs rename to "src/IFoxCAD.Cad/ExtensionMethod/\346\226\260\345\273\272\345\241\253\345\205\205/HatchInfo.cs" index a6a2647..5f79306 100644 --- a/src/IFoxCAD.Cad/ExtensionMethod/HatchEx.cs +++ "b/src/IFoxCAD.Cad/ExtensionMethod/\346\226\260\345\273\272\345\241\253\345\205\205/HatchInfo.cs" @@ -1,18 +1,9 @@ -#if !HC2020 +namespace IFoxCAD.Cad; using Autodesk.AutoCAD.DatabaseServices; using Autodesk.AutoCAD.Geometry; using Autodesk.AutoCAD.Colors; -#else -using GrxCAD.DatabaseServices; -using GrxCAD.Geometry; -using GrxCAD.Colors; -#endif using System.Collections.Generic; using System; -using IFoxCAD.Cad; - -namespace IFoxCAD; - /* * ӵĵһ߽߽,ڶͼı߽硣 * Ҫⲿ߽,ʹӻΪ HatchLoopTypes.Outermost AppendLoop , @@ -26,7 +17,7 @@ namespace IFoxCAD; /// /// ͼ /// -public class HatchEx +public class HatchInfo { #region Ա /// @@ -64,7 +55,7 @@ public class HatchEx #endregion #region - HatchEx() + HatchInfo() { _hatch = new Hatch(); _hatch.SetDatabaseDefaults(); @@ -78,7 +69,7 @@ public class HatchEx /// ԭ /// /// Ƕ - public HatchEx(bool boundaryAssociative = true, + public HatchInfo(bool boundaryAssociative = true, Point2d? hatchOrigin = null, double hatchScale = 1, double hatchAngle = 0) : this() @@ -102,7 +93,7 @@ public HatchEx(bool boundaryAssociative = true, /// ԭ /// /// Ƕ - public HatchEx(IEnumerable boundaryIds, + public HatchInfo(IEnumerable boundaryIds, bool boundaryAssociative = true, Point2d? hatchOrigin = null, double hatchScale = 1, @@ -118,7 +109,7 @@ public HatchEx(IEnumerable boundaryIds, /// /// ģʽ1:Ԥ /// - public HatchEx Mode1PreDefined(string name) + public HatchInfo Mode1PreDefined(string name) { _hatchName = name; _hatch.HatchObjectType = HatchObjectType.HatchObject; //(/) @@ -130,7 +121,7 @@ public HatchEx Mode1PreDefined(string name) /// ģʽ2:û /// /// Ƿ˫ - public HatchEx Mode2UserDefined(bool patternDouble = true) + public HatchInfo Mode2UserDefined(bool patternDouble = true) { _hatchName = "_USER"; _hatch.HatchObjectType = HatchObjectType.HatchObject; //(/) @@ -145,7 +136,7 @@ public HatchEx Mode2UserDefined(bool patternDouble = true) /// ģʽ3:Զ /// /// - public HatchEx Mode3UserDefined(string name) + public HatchInfo Mode3UserDefined(string name) { _hatchName = name; _hatch.HatchObjectType = HatchObjectType.HatchObject; //(/) @@ -162,7 +153,7 @@ public HatchEx Mode3UserDefined(string name) /// ƶ /// ɫֵ /// ɫ˫ɫ - public HatchEx Mode4Gradient(GradientName name, Color colorStart, Color colorEnd, + public HatchInfo Mode4Gradient(GradientName name, Color colorStart, Color colorEnd, float gradientShift = 0, float shadeTintValue = 0, bool gradientOneColorMode = false) @@ -219,7 +210,7 @@ public ObjectId Build(BlockTableRecord btrOfAddEntitySpace) /// ִͼԪ޸ /// /// ӳʵ - public HatchEx Action(Action action) + public HatchInfo Action(Action action) { action(_hatch); return this; @@ -228,7 +219,7 @@ public HatchEx Action(Action action) /// /// ձ߽缯 /// - public HatchEx ClearBoundary() + public HatchInfo ClearBoundary() { _boundaryIds.Clear(); return this; @@ -237,7 +228,7 @@ public HatchEx ClearBoundary() /// /// ɾ߽ͼԪ /// - public HatchEx EraseBoundary() + public HatchInfo EraseBoundary() { for (int i = 0; i < _boundaryIds.Count; i++) _boundaryIds[i].Erase(); @@ -272,7 +263,7 @@ void AppendLoop(IEnumerable boundaryIds, /// ˿ռ /// 뷽ʽ /// - public HatchEx AppendLoop(Point2dCollection pts!!, + public HatchInfo AppendLoop(Point2dCollection pts!!, DoubleCollection bluges, BlockTableRecord btrOfAddEntitySpace, HatchLoopTypes hatchLoopTypes = HatchLoopTypes.Default) -- Gitee From ff7f21a7a3e6b2a952de3c1e90a4bba69840755b Mon Sep 17 00:00:00 2001 From: liuqihong <540762622@qq.com> Date: Sat, 6 Aug 2022 08:04:12 +0800 Subject: [PATCH 327/675] =?UTF-8?q?=E4=BC=98=E5=8C=96=E9=A2=91=E7=B9=81?= =?UTF-8?q?=E8=B0=83=E7=94=A8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/IFoxCAD.Cad/ExtensionMethod/PointEx.cs | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/src/IFoxCAD.Cad/ExtensionMethod/PointEx.cs b/src/IFoxCAD.Cad/ExtensionMethod/PointEx.cs index 889686b..b1bae82 100644 --- a/src/IFoxCAD.Cad/ExtensionMethod/PointEx.cs +++ b/src/IFoxCAD.Cad/ExtensionMethod/PointEx.cs @@ -20,6 +20,9 @@ public static string GetHashString(this Point3d pt, int xyz = 3, int decimalReta _ => $"({pt.X.ToString(de)},{pt.Y.ToString(de)},{pt.Z.ToString(de)})" }; } + + //为了频繁触发所以弄个全局变量 + static Plane? _Plane; /// /// 两点计算弧度范围0到2Pi /// @@ -29,8 +32,11 @@ public static string GetHashString(this Point3d pt, int xyz = 3, int decimalReta /// 弧度值 public static double GetAngle(this Point3d startPoint, Point3d endPoint, Vector3d? direction = null) { - return startPoint.GetVectorTo(endPoint) - .AngleOnPlane(new Plane(Point3d.Origin, direction ?? Vector3d.ZAxis)); + if (direction != null) + _Plane = new Plane(Point3d.Origin, direction.Value); + if (_Plane == null) + _Plane = new Plane(Point3d.Origin, Vector3d.ZAxis); + return startPoint.GetVectorTo(endPoint).AngleOnPlane(_Plane); } /// /// 两点计算弧度范围0到2Pi @@ -55,7 +61,7 @@ public static Point2d GetCenter(this Point2d a, Point2d b) return new Point2d(a.X * 0.5 + b.X * 0.5, a.Y * 0.5 + b.Y * 0.5); } - + /// http://www.lee-mac.com/bulgeconversion.html /// /// 求凸度,判断三点是否一条直线上 -- Gitee From 24585af127dae0ec6fdf344d01b2b192dd54a672 Mon Sep 17 00:00:00 2001 From: liuqihong <540762622@qq.com> Date: Sat, 6 Aug 2022 16:03:10 +0800 Subject: [PATCH 328/675] =?UTF-8?q?=E4=BF=AE=E6=94=B9=E5=87=BD=E6=95=B0?= =?UTF-8?q?=E5=90=8D=E7=A7=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/IFoxCAD.Cad/Runtime/AutoRegAssem.cs | 4 ++-- tests/Test/CmdINI.cs | 15 +++++++++++++-- 2 files changed, 15 insertions(+), 4 deletions(-) diff --git a/src/IFoxCAD.Cad/Runtime/AutoRegAssem.cs b/src/IFoxCAD.Cad/Runtime/AutoRegAssem.cs index 2876898..b8e73b8 100644 --- a/src/IFoxCAD.Cad/Runtime/AutoRegAssem.cs +++ b/src/IFoxCAD.Cad/Runtime/AutoRegAssem.cs @@ -107,9 +107,9 @@ public static RegistryKey GetAcAppKey(bool writable = true) } /// - /// 卸载自动运行注册表 + /// 卸载注册表信息 /// - public bool UnAcAppKey() + public bool UnRegApp() { var appkey = GetAcAppKey(); if (appkey.SubKeyCount == 0) diff --git a/tests/Test/CmdINI.cs b/tests/Test/CmdINI.cs index 69f62a3..2e37c1d 100644 --- a/tests/Test/CmdINI.cs +++ b/tests/Test/CmdINI.cs @@ -15,7 +15,7 @@ public CmdINI() : base(AutoRegConfig.All) var dm = Application.DocumentManager; var doc = dm.MdiActiveDocument; var ed = doc.Editor; - ed.WriteMessage($"\n 开始自动执行{nameof(CmdINI)} \r\n"); + ed.WriteMessage($"\n {nameof(CmdINI)}构造函数,开始自动执行\r\n"); } ///如果netload之后用 删除注册表, @@ -25,6 +25,11 @@ public CmdINI() : base(AutoRegConfig.All) public void IFoxAddReg() { base.RegApp(); + + var dm = Application.DocumentManager; + var doc = dm.MdiActiveDocument; + var ed = doc.Editor; + ed.WriteMessage($"\n 加入注册表"); } /// @@ -33,7 +38,13 @@ public void IFoxAddReg() [CommandMethod("IFoxRemoveReg")] public void IFoxRemoveReg() { - base.UnAcAppKey(); + //执行命令的时候会再次执行构造函数(导致初始化两次),但是再次执行就不会了 + base.UnRegApp(); + + var dm = Application.DocumentManager; + var doc = dm.MdiActiveDocument; + var ed = doc.Editor; + ed.WriteMessage($"\n 卸载注册表"); } } -- Gitee From 0bcfcf4aaaf74155b0b40a2cdf334169734cc832 Mon Sep 17 00:00:00 2001 From: liuqihong <540762622@qq.com> Date: Mon, 8 Aug 2022 21:45:26 +0800 Subject: [PATCH 329/675] =?UTF-8?q?=E5=90=8E=E5=8F=B0=E6=89=93=E5=BC=80?= =?UTF-8?q?=E5=9B=BE=E7=BA=B8=E5=B9=B6=E9=95=9C=E5=83=8F=E6=98=AF=E5=90=A6?= =?UTF-8?q?=E5=AD=98=E5=9C=A8=E6=96=87=E5=AD=97=E5=81=8F=E7=A7=BB?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- tests/Test/TestMirrorFile.cs | 33 +++++++++++++++++++++++++++++++++ 1 file changed, 33 insertions(+) create mode 100644 tests/Test/TestMirrorFile.cs diff --git a/tests/Test/TestMirrorFile.cs b/tests/Test/TestMirrorFile.cs new file mode 100644 index 0000000..6736570 --- /dev/null +++ b/tests/Test/TestMirrorFile.cs @@ -0,0 +1,33 @@ +public class MirrorFile +{ + /// + /// 测试:后台打开图纸,镜像文字是否存在文字偏移 + /// 答案:不存在 + /// + [CommandMethod("CmdTest_MirrorFile")] + public static void CmdTest_MirrorFile() + { + const string file = "D:/JX.dwg"; + const string fileSave = "D:/JX222.dwg"; + + using var tr = new DBTrans(file, openMode: FileOpenMode.OpenForReadAndReadShare); + + tr.BlockTable.Change(tr.ModelSpace.ObjectId, ms => { + foreach (ObjectId entId in ms) + { + var text = tr.GetObject(entId, OpenMode.ForRead)!; + if (text is null) + continue; + + text.UpgradeOpen(); + var pos = text.Position; + //text.Move(pos, Point3d.Origin); + //Y轴 + text.Mirror(Point3d.Origin, new Point3d(0, 1, 0)); + //text.Move(Point3d.Origin, pos); + text.DowngradeOpen(); + } + }); + tr.Database.SaveAs(fileSave, DwgVersion.AC1021/*AC1021 AutoCAD 2007/2008/2009.*/); + } +} \ No newline at end of file -- Gitee From 0b2372390a6f95a54148d49e7a0b7f67eaa4793f Mon Sep 17 00:00:00 2001 From: liuqihong <540762622@qq.com> Date: Mon, 8 Aug 2022 22:04:58 +0800 Subject: [PATCH 330/675] =?UTF-8?q?=E7=A7=BB=E9=99=A4=E4=B8=8D=E5=8F=AF?= =?UTF-8?q?=E7=94=A8=E7=9A=84=E8=BE=B9=E7=95=8C=E7=AE=97=E6=B3=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/IFoxCAD.Cad/ExtensionMethod/CurveEx.cs | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/IFoxCAD.Cad/ExtensionMethod/CurveEx.cs b/src/IFoxCAD.Cad/ExtensionMethod/CurveEx.cs index e1349e5..d01c6c0 100644 --- a/src/IFoxCAD.Cad/ExtensionMethod/CurveEx.cs +++ b/src/IFoxCAD.Cad/ExtensionMethod/CurveEx.cs @@ -12,8 +12,7 @@ public static class CurveEx /// 长度 public static double GetLength(this Curve curve) { - return - curve.GetDistanceAtParameter(curve.EndParam); + return curve.GetDistanceAtParameter(curve.EndParam); } /// @@ -660,4 +659,4 @@ public static void ChamferAt(this Polyline polyline, int index, double radius, b #endregion Polyline #endregion Curve -} +} \ No newline at end of file -- Gitee From 3ce14f3ca92f82cf5752821e76add68036b619ae Mon Sep 17 00:00:00 2001 From: liuqihong <540762622@qq.com> Date: Tue, 9 Aug 2022 01:15:05 +0800 Subject: [PATCH 331/675] =?UTF-8?q?=E5=A4=84=E7=90=86=E5=85=8B=E9=9A=86?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../ExtensionMethod/SymbolTableRecordEx.cs | 77 ++- .../Edge.cs" | 606 ---------------- .../TestTopo.cs" | 25 - .../Topo.cs" | 653 ------------------ ...2\346\226\207\347\274\272\351\231\267.png" | Bin 202707 -> 0 bytes src/IFoxCAD.Cad/IFoxCAD.Cad.csproj | 10 + 6 files changed, 63 insertions(+), 1308 deletions(-) delete mode 100644 "src/IFoxCAD.Cad/ExtensionMethod/\351\227\255\345\220\210\350\276\271\347\225\214\347\256\227\346\263\225/Edge.cs" delete mode 100644 "src/IFoxCAD.Cad/ExtensionMethod/\351\227\255\345\220\210\350\276\271\347\225\214\347\256\227\346\263\225/TestTopo.cs" delete mode 100644 "src/IFoxCAD.Cad/ExtensionMethod/\351\227\255\345\220\210\350\276\271\347\225\214\347\256\227\346\263\225/Topo.cs" delete mode 100644 "src/IFoxCAD.Cad/ExtensionMethod/\351\227\255\345\220\210\350\276\271\347\225\214\347\256\227\346\263\225/\350\256\272\346\226\207\347\274\272\351\231\267.png" diff --git a/src/IFoxCAD.Cad/ExtensionMethod/SymbolTableRecordEx.cs b/src/IFoxCAD.Cad/ExtensionMethod/SymbolTableRecordEx.cs index 2b9b2ee..999b178 100644 --- a/src/IFoxCAD.Cad/ExtensionMethod/SymbolTableRecordEx.cs +++ b/src/IFoxCAD.Cad/ExtensionMethod/SymbolTableRecordEx.cs @@ -9,16 +9,17 @@ public static class SymbolTableRecordEx #region 克隆实体id /// - /// 克隆id(不允许是未添加数据库的图元)到块表记录(原地克隆) + /// 克隆id到块表记录 + /// 0x01 此方法不允许是未添加数据库的图元,因此它是id + /// 0x02 若为未添加数据库图元,则利用entity.Clone();同时不需要考虑动态块属性,可以使用entity.GetTransformedCopy /// /// /// 克隆到当前块表记录,相当于原地克隆 /// 克隆到目标块表记录内,相当于制作新块 /// - /// id集合,注意所有成员都要在同一个空间中 + /// 图元id集合,注意所有成员都要在同一个空间中 /// 克隆后的id词典 - public static IdMapping EntityClone(this BlockTableRecord btr, - ObjectIdCollection objIds) + public static IdMapping EntityClone(this BlockTableRecord btr, ObjectIdCollection objIds) { if (objIds is null || objIds.Count == 0) throw new ArgumentNullException(nameof(objIds)); @@ -31,13 +32,33 @@ public static IdMapping EntityClone(this BlockTableRecord btr, { //深度克隆 db.DeepCloneObjects(objIds, btr.ObjectId, mapping, false); + + //不在此提取,为了此函数高频使用 + //获取克隆键值对(旧块名,新块名) //foreach (ObjectId item in blockIds) - // result.Add(mapping[item].Value); //获取克隆键值对(旧块名,新块名) + // result.Add(mapping[item].Value); + } + catch (System.Exception e) + { + e.WriteLog(); } - catch { } } return mapping; } + + /// + /// 克隆图元实体(这个函数有问题,会出现偶尔成功,偶尔失败,拖动过变成匿名块) + /// 若为块则进行设置属性,因此控制动态块属性丢失; + /// + /// 图元 + /// 矩阵 + //public static void EntityTransformedCopy(this Entity ent, Matrix3d matrix) + //{ + // var entNew = ent.GetTransformedCopy(matrix); + // if (ent is BlockReference blockReference) + // entNew.SetPropertiesFrom(blockReference); + //} + #endregion #region 添加实体 @@ -48,7 +69,8 @@ public static IdMapping EntityClone(this BlockTableRecord btr, /// 实体 /// 事务管理器 /// 对象 id - public static ObjectId AddEntity(this BlockTableRecord btr, Entity entity, Transaction? trans = null) + public static ObjectId AddEntity(this BlockTableRecord btr, Entity entity, + Transaction? trans = null) { //if (entity is null) // throw new ArgumentNullException(nameof(entity), "对象为 null"); @@ -71,7 +93,8 @@ public static ObjectId AddEntity(this BlockTableRecord btr, Entity entity, Trans /// 实体集合 /// 事务 /// 对象 id 列表 - public static IEnumerable AddEntity(this BlockTableRecord btr, IEnumerable ents, Transaction? trans = null) where T : Entity + public static IEnumerable AddEntity(this BlockTableRecord btr, IEnumerable ents, + Transaction? trans = null) where T : Entity { //if (ents.Any(ent => ent is null)) // throw new ArgumentNullException(nameof(ents), "实体集合内存在 null 对象"); @@ -144,7 +167,8 @@ public static ObjectId AddEnt(this BlockTableRecord btr, Func action, Tr /// 绘图空间 /// 直线属性设置委托 /// 直线的id - public static ObjectId AddLine(this BlockTableRecord btr, Point3d start, Point3d end, Action? action = default, Transaction? trans = default) + public static ObjectId AddLine(this BlockTableRecord btr, Point3d start, Point3d end, + Action? action = default, Transaction? trans = default) { var line = new Line(start, end); return btr.AddEnt(line, action, trans); @@ -158,7 +182,8 @@ public static ObjectId AddLine(this BlockTableRecord btr, Point3d start, Point3d /// 圆属性设置委托 /// 事务管理器 /// 圆的id - public static ObjectId AddCircle(this BlockTableRecord btr, Point3d center, double radius, Action? action = default, Transaction? trans = default) + public static ObjectId AddCircle(this BlockTableRecord btr, Point3d center, double radius, + Action? action = default, Transaction? trans = default) { var circle = new Circle(center, Vector3d.ZAxis, radius); return btr.AddEnt(circle, action, trans); @@ -175,7 +200,7 @@ public static ObjectId AddCircle(this BlockTableRecord btr, Point3d center, doub /// 事务管理器 /// 三点有外接圆则返回圆的id,否则返回ObjectId.Null public static ObjectId AddCircle(this BlockTableRecord btr, Point3d p0, Point3d p1, Point3d p2, - Action? action = default, Transaction? trans = default) + Action? action = default, Transaction? trans = default) { var circle = EntityEx.CreateCircle(p0, p1, p2); //return circle is not null ? btr.AddEnt(circle, action, trans) : throw new ArgumentNullException(nameof(circle), "对象为 null"); @@ -280,7 +305,9 @@ public static ObjectId AddPline(this BlockTableRecord btr, /// 圆弧属性设置委托 /// 事务管理器 /// 圆弧id - public static ObjectId AddArc(this BlockTableRecord btr, Point3d startPoint, Point3d pointOnArc, Point3d endPoint, Action? action = default, Transaction? trans = default) + public static ObjectId AddArc(this BlockTableRecord btr, + Point3d startPoint, Point3d pointOnArc, Point3d endPoint, + Action? action = default, Transaction? trans = default) { var arc = EntityEx.CreateArc(startPoint, pointOnArc, endPoint); return btr.AddEnt(arc, action, trans); @@ -298,7 +325,9 @@ public static ObjectId AddArc(this BlockTableRecord btr, Point3d startPoint, Poi /// 打开模式 /// 事务 /// 实体集合 - public static IEnumerable GetEntities(this BlockTableRecord btr, OpenMode mode = OpenMode.ForRead, Transaction? trans = default) where T : Entity + public static IEnumerable GetEntities(this BlockTableRecord btr, + OpenMode mode = OpenMode.ForRead, + Transaction? trans = default) where T : Entity { trans ??= DBTrans.Top.Transaction; return @@ -340,7 +369,8 @@ public static IEnumerable> GetObjectIds(this BlockTa /// 块表 /// 事务 /// 绘制顺序表 - public static DrawOrderTable? GetDrawOrderTable(this BlockTableRecord btr, Transaction? trans = default) + public static DrawOrderTable? GetDrawOrderTable(this BlockTableRecord btr, + Transaction? trans = default) { trans ??= DBTrans.Top.Transaction; return trans.GetObject(btr.DrawOrderTableId, OpenMode.ForRead) as DrawOrderTable; @@ -360,10 +390,10 @@ public static IEnumerable> GetObjectIds(this BlockTa /// 事务 /// 块参照对象id public static ObjectId InsertBlock(this BlockTableRecord blockTableRecord, Point3d position, - string blockName, - Scale3d scale = default, - double rotation = default, - Dictionary? atts = default, Transaction? trans = null) + string blockName, + Scale3d scale = default, + double rotation = default, + Dictionary? atts = default, Transaction? trans = null) { trans ??= DBTrans.Top.Transaction; if (!DBTrans.Top.BlockTable.Has(blockName)) @@ -383,10 +413,10 @@ public static ObjectId InsertBlock(this BlockTableRecord blockTableRecord, Point /// 属性字典{Tag,Value},默认为null /// 块参照对象id public static ObjectId InsertBlock(this BlockTableRecord blockTableRecord, Point3d position, - ObjectId blockId, - Scale3d scale = default, - double rotation = default, - Dictionary? atts = default, Transaction? trans = null) + ObjectId blockId, + Scale3d scale = default, + double rotation = default, + Dictionary? atts = default, Transaction? trans = null) { trans ??= DBTrans.Top.Transaction; if (!DBTrans.Top.BlockTable.Has(blockId)) @@ -413,10 +443,9 @@ public static ObjectId InsertBlock(this BlockTableRecord blockTableRecord, Point attref.SetAttributeFromBlock(attdef, blockref.BlockTransform); attref.Position = attdef.Position.TransformBy(blockref.BlockTransform); attref.AdjustAlignment(DBTrans.Top.Database); + if (atts.ContainsKey(attdef.Tag)) - { attref.TextString = atts[attdef.Tag]; - } blockref.AttributeCollection.AppendAttribute(attref); trans.AddNewlyCreatedDBObject(attref, true); diff --git "a/src/IFoxCAD.Cad/ExtensionMethod/\351\227\255\345\220\210\350\276\271\347\225\214\347\256\227\346\263\225/Edge.cs" "b/src/IFoxCAD.Cad/ExtensionMethod/\351\227\255\345\220\210\350\276\271\347\225\214\347\256\227\346\263\225/Edge.cs" deleted file mode 100644 index 1f35410..0000000 --- "a/src/IFoxCAD.Cad/ExtensionMethod/\351\227\255\345\220\210\350\276\271\347\225\214\347\256\227\346\263\225/Edge.cs" +++ /dev/null @@ -1,606 +0,0 @@ -using System.Diagnostics; - -namespace IFoxCAD.Cad; - - -/// -/// Ϣ -/// -public class CurveInfo : Rect -{ - #region - /// - /// ͼԪ - /// - public Curve Curve; - /// - /// ߷ָIJ - /// - public List Paramss; - /// - /// ײ - /// - public CollisionChain? CollisionChain; - - Edge? _Edge; - /// - /// ߽ - /// - public Edge Edge - { - get - { - if (_Edge == null) - _Edge = new Edge(Curve.ToCompositeCurve3d()!); - return _Edge; - } - } - #endregion - - #region - public CurveInfo(Curve curve!!) - { - Curve = curve; - Paramss = new List(); - - //TODO ˴bug:֮ûй - var box = Curve.GeometricExtents; - _X = box.MinPoint.X; - _Y = box.MinPoint.Y; - _Right = box.MaxPoint.X; - _Top = box.MaxPoint.Y; - } - #endregion - - #region - /// - /// ָ, - /// - /// ߷ָIJ - /// - public List Split(List pars1) - { - var edges = new List(); - var c3ds = Edge.GeCurve3d.GetSplitCurves(pars1); - if (c3ds.Count > 0) - edges.AddRange(c3ds.Select(c => new Edge(c))); - return edges; - } - #endregion -} - -/// -/// ײ -/// -public class CollisionChain : List { } - - - -/// -/// ߱ -/// -[DebuggerDisplay("{DebuggerDisplay,nq}")] -[DebuggerTypeProxy(typeof(Edge))] -public class Edge : IEquatable, IFormattable -{ - [DebuggerBrowsable(DebuggerBrowsableState.Never)] - private string DebuggerDisplay => ToString(); - - #region ֶ - public CompositeCurve3d GeCurve3d; - #endregion - - #region - /// - /// (ûаΧ,ToCurve) - /// - public Edge(CompositeCurve3d geCurve3d) - { - GeCurve3d = geCurve3d; - } - #endregion - - #region _Ƚ - public override bool Equals(object? obj) - { - return this.Equals(obj as Edge); - } - public bool Equals(Edge? b) - { - return this.Equals(b, 1e-6);/*ĬϹ==0ݲ,Eqݲ*/ - } - public static bool operator !=(Edge? a, Edge? b) - { - return !(a == b); - } - public static bool operator ==(Edge? a, Edge? b) - { - //˴طʹ==null,Ϊ˴Ƕ - if (b is null) - return a is null; - else if (a is null) - return false; - if (ReferenceEquals(a, b))//ͬһ - return true; - - return a.Equals(b, 0); - } - - /// - /// ȽϺ - /// - public bool Equals(Edge? b, double tolerance = 1e-6) - { - if (b is null) - return false; - if (ReferenceEquals(this, b)) //ͬһ - return true; - - if (tolerance == 0) - return GeCurve3d == b.GeCurve3d; - - var tol = new Tolerance(tolerance, tolerance); - return GeCurve3d.StartPoint.IsEqualTo(b.GeCurve3d.StartPoint, tol) && - GeCurve3d.EndPoint.IsEqualTo(b.GeCurve3d.EndPoint, tol); - } - - public override int GetHashCode() - { - return GeCurve3d.GetHashCode(); - } - - /// - /// Ƚ(,˳) - /// - /// - /// - /// и߷(3ԲصʧЧ,˴4ʼ) - /// ͼ˵ڽdocs - /// - /// صtrue - public bool SplitPointEquals(Edge? b, int splitNum = 4) - { - if (b is null) - return this is null; - else if (this is null) - return false; - if (ReferenceEquals(this, b))//ͬһ - return true; - - //ȡ߳Ȼᾭܶƽ, - //ԲҪ,ֱӲ֮жϾ - - //ָ߲Ҳһ,һ - Point3d[] sp1; - Point3d[] sp2; - -#if NET35 - sp1 = GeCurve3d.GetSamplePoints(splitNum); - sp2 = b.GeCurve3d.GetSamplePoints(splitNum); -#else - var tmp1 = GeCurve3d.GetSamplePoints(splitNum); - var tmp2 = b.GeCurve3d.GetSamplePoints(splitNum); - sp1 = tmp1.Select(a => a.Point).ToArray(); - sp2 = tmp2.Select(a => a.Point).ToArray(); -#endif - //Ϊ߿,Բ֮Ҫ - sp1 = sp1.OrderBy(a => a.X).ThenBy(a => a.Y).ThenBy(a => a.Z).ToArray(); - sp2 = sp2.OrderBy(a => a.X).ThenBy(a => a.Y).ThenBy(a => a.Z).ToArray(); - - for (int i = 0; i < sp1.Length; i++) - { - if (!sp1[i].IsEqualTo(sp2[i], CadTolerance)) - return false; - } - return true; - } - #endregion - - #region -#pragma warning disable CA2211 // dzֶӦɼ - public static Tolerance CadTolerance = new(1e-6, 1e-6); -#pragma warning restore CA2211 // dzֶӦɼ - - /// - /// ˳ - /// - /// - public static void Distinct(List edgesOut) - { - if (edgesOut.Count == 0) - return; - - //EdgeûаΧ,޷ж - //anݽи,ظ,ͬ - Basal.ArrayEx.Deduplication(edgesOut, (first, last) => { - var pta1 = first.GeCurve3d.StartPoint; - var pta2 = first.GeCurve3d.EndPoint; - var ptb1 = last.GeCurve3d.StartPoint; - var ptb2 = last.GeCurve3d.EndPoint; - //˳ || - if ((pta1.IsEqualTo(ptb1, CadTolerance) && pta2.IsEqualTo(ptb2, CadTolerance)) - || - (pta1.IsEqualTo(ptb2, CadTolerance) && pta2.IsEqualTo(ptb1, CadTolerance))) - { - return first.SplitPointEquals(last); - } - return false; - }); - } - #endregion - - #region IFormattable - public override string ToString() - { - return ToString(null, null); - } - - /// - /// תΪַ_ʽʵ - /// - /// - /// - /// - string IFormattable.ToString(string? format, IFormatProvider? formatProvider) - { - return ToString(format, formatProvider); - } - - /// - /// תΪַ_вε - /// - /// - public string ToString(string? format, IFormatProvider? formatProvider = null) - { - var s = new StringBuilder(); - if (format is null) - { - s.Append(nameof(Edge)); - s.Append("{ "); - s.Append("(StartPoint=" + GeCurve3d.StartPoint + "; EndPoint=" + GeCurve3d.EndPoint + ")"); - s.Append(" }\r\n"); - } - return s.ToString(); - } - #endregion -} - -#if true2 -/// -/// (պ/޷ֲ/Խ) -/// -public class PolyEdge : LoopList -{ - /// - /// (պ/޷ֲ/Խ) - /// - /// - public PolyEdge(params Edge[] ps) - { - AddRange(ps); - } - - // ̬ڸƵ㼯ʱڴ泤 - static List pts = new(); - - public Point3d[] Points() - { - pts.Clear(); - - //ΪʱӶ,ԵҲ - var ge = GetEnumerator(); - while (ge.MoveNext()) - { - var sp = ge.Current.GeCurve3d.StartPoint; - if (pts.Count == 0) - pts.Add(sp); - else if (pts[pts.Count - 1] != sp)//Ӷغϵ - pts.Add(sp); - - var ep = ge.Current.GeCurve3d.EndPoint; - if (pts.Count == 0) - pts.Add(ep); - else if (pts[pts.Count - 1] != ep) - pts.Add(ep); - } - return pts.ToArray(); - } - - /// - /// Ӷηض - /// - /// ߼(,ṩȱ) - /// ҵӶ - /// ض - public static PolyEdge? Contains(IEnumerable lst, Edge find) - { - //̫Ч - //Parallel.ForEach(this, item => { - // Parallel.ForEach(item, item2 => { - // if (item2 == find) - // return; - // }); - //}); - - //߼=>=>Ӷ - var ge1 = lst.GetEnumerator(); - while (ge1.MoveNext()) - { - var ge2 = ge1.Current.GetEnumerator(); - while (ge2.MoveNext()) - { - //Ӷͬ:ض,ڼ - if (ge2.Current == find) - return ge1.Current; - } - } - return null; - } -} - -#endif - - - -/// -/// ڽӱڵ -/// -//[DebuggerDisplay("ڵ = {Number}; Count = {Count}; Color = {Color}; Distance = {Distance}; ڵ = {Parent?.Number}")] -[DebuggerDisplay("{DebuggerDisplay,nq}")] -[DebuggerTypeProxy(typeof(BoNode))] -public class BoNode : IFormattable -{ - [DebuggerBrowsable(DebuggerBrowsableState.Never)] - private string DebuggerDisplay => ToString(); - - #region ֶ - /// - /// ɫ - /// - public BoColor Color; - /// - /// ͷ˲ - /// - public int Steps; - /// - /// ڵ - /// - public BoNode? Parent; - /// - /// 㼯 - /// - public List? Meet; - /// - /// ڽڵ - /// - public List Neighbor; - - - /// - /// - /// - public Point3d Point; - /// - /// (DZ) - /// - public List Edges; - /// - /// ڵ - /// - public int Number; - #endregion - - #region - /// - /// ڽӱڵ - /// - /// ڵ - /// - /// - public BoNode(int num, Point3d point, Edge edge) - { - Number = num; - Point = point; - Edges = new(); - Edges.Add(edge); - - Neighbor = new(); - - - Color = BoColor.; - Steps = int.MaxValue; - Parent = null; - Meet?.Clear(); - } - #endregion - - #region - /// - /// ȡձ߼, - /// - /// - /// - /// - public static LoopList GetEdges(LoopList nodes) - { - if (nodes == null || nodes.Count == 0) - throw new ArgumentNullException(nameof(nodes)); - - LoopList result = new(); - //1-2 2-3 3-4 4-1 - var lp = nodes.First; - do - { - var edge = GetEdge(lp!.Value, lp.Next!.Value); - if (edge != null) - result.Add(edge); - lp = lp.Next; - } while (lp != nodes.First); - - return result; - } - - /// - /// ȡa-bڵ֮ - /// - /// - /// - /// - static Edge? GetEdge(BoNode node1, BoNode node2) - { - if (node1 == null || node2 == null) - return null; - - for (int i = 0; i < node1.Edges.Count; i++) - { - var node1Edge = node1.Edges[i]; - for (int j = 0; j < node2.Edges.Count; j++) - { - if (node1Edge == node2.Edges[j]) - return node1Edge; - } - } - return null; - } - #endregion - - #region IFormattable - public override string ToString() - { - return ToString(null, null); - } - - /// - /// תΪַ_ʽʵ - /// - /// - /// - /// - string IFormattable.ToString(string? format, IFormatProvider? formatProvider) - { - return ToString(format, formatProvider); - } - - /// - /// תΪַ_вε - /// - /// - public string ToString(string? format, IFormatProvider? formatProvider = null) - { - var s = new StringBuilder(); - //s.Append($"Count = {Count};"); - if (format is null) - { - s.Append(nameof(BoNode) + $"{Number}"); - s.Append("{ "); - - s.Append("ڵ:("); - for (int i = 0; i < Neighbor.Count; i++) - { - s.Append(this.Neighbor[i].Number); - if (i < Neighbor.Count - 1) - s.Append("--"); - } - s.Append(") "); - - s.Append($"Neighbor.Count={Neighbor.Count}; Color={Color}; Distance={Steps}; ={Parent?.Number}; "); - s.Append($"Point={Point};"); - s.Append(" }\r\n"); - } - return s.ToString(); - } - #endregion - -} - -public enum BoColor -{ - , - , - , - , -} - -[DebuggerDisplay("{DebuggerDisplay,nq}")] -[DebuggerTypeProxy(typeof(CompositeCurve3ds))] -public class CompositeCurve3ds : List, IFormattable -{ - [DebuggerBrowsable(DebuggerBrowsableState.Never)] - private string DebuggerDisplay => ToString(); - -#pragma warning disable CA2211 // dzֶӦɼ - // cadݲ - public static Tolerance CadTolerance = new(1e-6, 1e-6); -#pragma warning restore CA2211 // dzֶӦɼ - - /// - /// ߽ - /// - /// Ҫкõ - /// - public static CompositeCurve3ds OrderByPoints(LoopList pl) - { - CompositeCurve3ds c3ds = new(); - var lp = pl.First; - do - { - //1͵2Ƚ,߲ - var a1 = lp!.Value.GeCurve3d; - var a2 = lp!.Next!.Value.GeCurve3d; - - if (!a1.EndPoint.IsEqualTo(a2.EndPoint, CadTolerance) && //βͬ - !a1.EndPoint.IsEqualTo(a2.StartPoint, CadTolerance)) - a1 = (CompositeCurve3d)a1.GetReverseParameterCurve(); - - c3ds.Add(a1); - lp = lp.Next; - } while (lp != pl.First); - - return c3ds; - } - - - public CompositeCurve3d ToCompositeCurve3d() - { - return new CompositeCurve3d(ToArray()); - } - - #region IFormattable - public override string ToString() - { - return ToString(null, null); - } - - /// - /// תΪַ_ʽʵ - /// - /// - /// - /// - string IFormattable.ToString(string? format, IFormatProvider? formatProvider) - { - return ToString(format, formatProvider); - } - - /// - /// תΪַ_вε - /// - /// - public string ToString(string? format, IFormatProvider? formatProvider = null) - { - var s = new StringBuilder(); - if (format is null) - { - s.Append(nameof(CompositeCurve3ds) + $"{Count}"); - s.Append("{ "); - for (int i = 0; i < Count; i++) - { - s.Append("\r\n(StartPoint=" + this[i].StartPoint + "; EndPoint=" + this[i].EndPoint + ")"); - } - s.Append(" }\r\n"); - } - return s.ToString(); - } - - #endregion -} \ No newline at end of file diff --git "a/src/IFoxCAD.Cad/ExtensionMethod/\351\227\255\345\220\210\350\276\271\347\225\214\347\256\227\346\263\225/TestTopo.cs" "b/src/IFoxCAD.Cad/ExtensionMethod/\351\227\255\345\220\210\350\276\271\347\225\214\347\256\227\346\263\225/TestTopo.cs" deleted file mode 100644 index 815dcb4..0000000 --- "a/src/IFoxCAD.Cad/ExtensionMethod/\351\227\255\345\220\210\350\276\271\347\225\214\347\256\227\346\263\225/TestTopo.cs" +++ /dev/null @@ -1,25 +0,0 @@ -namespace IFoxCAD.Cad.ExtensionMethod -{ - public class CmdTestTopo - { - [CommandMethod("TestTopo")] - public static void TestTopo() - { - using var tr = new DBTrans(); - var ents = Env.Editor.SSGet()?.Value?.GetEntities(); - if (ents == null) - return; - - Tools.TestTimes2(1, "bfs", () => { - var curves = Topo.Create(ents.ToList()!)!; - - if (curves == null || !curves.Any()) - return; - - //改颜色,生成图元 - //curves.ForEach((num, cu) => cu.ForWrite(e => e.ColorIndex = num)); - tr.CurrentSpace.AddEntity(curves); - }); - } - } -} \ No newline at end of file diff --git "a/src/IFoxCAD.Cad/ExtensionMethod/\351\227\255\345\220\210\350\276\271\347\225\214\347\256\227\346\263\225/Topo.cs" "b/src/IFoxCAD.Cad/ExtensionMethod/\351\227\255\345\220\210\350\276\271\347\225\214\347\256\227\346\263\225/Topo.cs" deleted file mode 100644 index f86ab86..0000000 --- "a/src/IFoxCAD.Cad/ExtensionMethod/\351\227\255\345\220\210\350\276\271\347\225\214\347\256\227\346\263\225/Topo.cs" +++ /dev/null @@ -1,653 +0,0 @@ -namespace IFoxCAD.Cad; - -public class Topo -{ - #region 成员 -#pragma warning disable CA2211 // 非常量字段应当不可见 - // cad容差类 - public static Tolerance CadTolerance = new(1e-6, 1e-6); -#pragma warning restore CA2211 // 非常量字段应当不可见 - - // 求交类(每次set自动重置,都会有个新的结果) - static readonly CurveCurveIntersector3d _Cci3d = new(); - public List CurveList; - #endregion - - #region 构造 - /// - /// 获取封闭曲线 - /// - /// - /// - public Topo(List curves) - { - if (curves == null || curves.Count == 0) - throw new ArgumentNullException(nameof(curves)); - - CurveList = new(); - //提取包围盒信息 - for (int i = 0; i < curves.Count; i++) - CurveList.Add(new CurveInfo(curves[i])); - } - - /// - /// 获取曲线集所围成的封闭区域的曲线集 - /// - /// 曲线集 - /// 闭合的曲线集 - public static IEnumerable? Create(List curves) - { - //闭合的曲线集合 - List closedCurve3d = new(); - //零散的边界 - List gs = new(); - //邻接表 - Dictionary boNodes = new(); - - var topo = new Topo(curves); - -/* 项目“IFoxCAD.Cad (net40)”的未合并的更改 -在此之前: - topo.GetEdgesAndnewCurves(topo.CurveList, gs, closedCurve3d); -在此之后: - Topo.GetEdgesAndnewCurves(topo.CurveList, gs, closedCurve3d); -*/ - -/* 项目“IFoxCAD.Cad (net45)”的未合并的更改 -在此之前: - topo.GetEdgesAndnewCurves(topo.CurveList, gs, closedCurve3d); -在此之后: - Topo.GetEdgesAndnewCurves(topo.CurveList, gs, closedCurve3d); -*/ - GetEdgesAndnewCurves(topo.CurveList, gs, closedCurve3d); - -/* 项目“IFoxCAD.Cad (net40)”的未合并的更改 -在此之前: - topo.AdjacencyList(gs, closedCurve3d, boNodes); -在此之后: - Topo.AdjacencyList(gs, closedCurve3d, boNodes); -*/ - -/* 项目“IFoxCAD.Cad (net45)”的未合并的更改 -在此之前: - topo.AdjacencyList(gs, closedCurve3d, boNodes); -在此之后: - Topo.AdjacencyList(gs, closedCurve3d, boNodes); -*/ - AdjacencyList(gs, closedCurve3d, boNodes); - var bos = boNodes.Select(a => a.Value).ToArray(); - - //TODO Topo校验数据 - -/* 项目“IFoxCAD.Cad (net40)”的未合并的更改 -在此之前: - var regions = topo.BreadthFirstSearch(bos); -在此之后: - var regions = Topo.BreadthFirstSearch(bos); -*/ - -/* 项目“IFoxCAD.Cad (net45)”的未合并的更改 -在此之前: - var regions = topo.BreadthFirstSearch(bos); -在此之后: - var regions = Topo.BreadthFirstSearch(bos); -*/ - var regions = BreadthFirstSearch(bos); - if (regions == null || regions.Count == 0) - return null; - - //零碎的曲线在此处生成封闭曲线 - for (int regNum = 0; regNum < regions.Count; regNum++) - { - var pl = BoNode.GetEdges(regions[regNum]); - if (pl.Count == 0) - continue; - var c3ds = CompositeCurve3ds.OrderByPoints(pl); - closedCurve3d.Add(c3ds.ToCompositeCurve3d()); - } - - //因为生成可能导致遗忘释放,所以这里统一生成 - return closedCurve3d.Select(e => e.ToCurve()!).Where(e => e != null).ToList(); - } - #endregion - - #region 方法 - /// - /// 利用交点断分曲线和独立自闭曲线 - /// - /// 传入每组有碰撞的 - /// 传出不自闭的曲线集 - /// 传出自闭曲线集 - public static void GetEdgesAndnewCurves(List infos_In, List edges_Out, List closed_Out) - { - //此处是O(n²) - //曲线a和其他曲线n根据 交点 切割子线(第一次是自交对比) - for (int a = 0; a < infos_In.Count; a++) - { - var curve1 = infos_In[a]; - var gc1 = curve1.Edge.GeCurve3d; - var pars1 = curve1.Paramss; - - for (int n = a; n < infos_In.Count; n++) - { - var curve2 = infos_In[n]; - - //包围盒没有碰撞就直接结束 - if (!curve1.IntersectsWith(curve2)) - continue; - - var gc2 = curve2.Edge.GeCurve3d; - var pars2 = curve2.Paramss; - - _Cci3d.Set(gc1, gc2, Vector3d.ZAxis); - - //计算两条曲线的交点(多个),分别放入对应的交点参数集 - for (int k = 0; k < _Cci3d.NumberOfIntersectionPoints; k++) - { - var pars = _Cci3d.GetIntersectionParameters(k); - pars1.Add(pars[0]);//0是第一条曲线的交点参数 - pars2.Add(pars[1]);//1是第二条曲线的交点参数 - } - } - - if (gc1.IsClosed()) - closed_Out.Add(gc1); - - if (pars1.Count == 0) - continue; - - //有交点参数 - //根据交点参数断分曲线,然后获取边界 - var c3ds = curve1.Split(pars1); - if (c3ds.Count > 0) - { - edges_Out.AddRange(c3ds); - } - else - { - //狐哥写的这里出现的条件是:有曲线参数,但是切分不出来曲线...没懂为什么... - //是这些参数?{参数0位置?头参/尾参/参数不在曲线上?} - edges_Out.Add(curve1.Edge); - } - - //有交点的才消重,无交点必然不重复. - Edge.Distinct(edges_Out); - } - } - - /// - /// 创建邻接表 - /// - /// 传入每组有碰撞的 - /// 传出自闭曲线集 - /// 节点集合返回(交点坐标字符串,节点) - public static void AdjacencyList(List edgesIn, List closed_Out, Dictionary boNodes_Out) - { - int boNumber = 0; - /* - * 邻接表:不重复地将共点(交点)作为标记,然后容器加入边 - */ - for (int i = 0; i < edgesIn.Count; i++) - { - var edge = edgesIn[i]; - //曲线闭合直接提供出去 - if (edge.GeCurve3d.IsClosed()) - { - closed_Out.Add(edge.GeCurve3d); - continue; - } - - //点转字符串作为key - var sp = edge.GeCurve3d.StartPoint.GetHashString(2); - var ep = edge.GeCurve3d.EndPoint.GetHashString(2); - - //词典key是共用此交点 - BoNode spNode; - BoNode epNode; - if (boNodes_Out.ContainsKey(sp)) - spNode = boNodes_Out[sp]; - else - spNode = new BoNode(boNumber++, edge.GeCurve3d.StartPoint, edge); - - if (boNodes_Out.ContainsKey(ep)) - epNode = boNodes_Out[ep]; - else - epNode = new BoNode(boNumber++, edge.GeCurve3d.EndPoint, edge); - - //加入边图元 - if (!spNode.Edges.Contains(edge)) - spNode.Edges.Add(edge); - if (!epNode.Edges.Contains(edge)) - epNode.Edges.Add(edge); - - //加入邻居节点 - if (!spNode.Neighbor.Contains(epNode)) - spNode.Neighbor.Add(epNode); - if (!epNode.Neighbor.Contains(spNode)) - epNode.Neighbor.Add(spNode); - - //加入词典 - if (!boNodes_Out.ContainsKey(sp)) - boNodes_Out.Add(sp, spNode); - if (!boNodes_Out.ContainsKey(ep)) - boNodes_Out.Add(ep, epNode); - } - - // TODO 如果有效率问题,尝试把剪枝去掉, - // 因为后续的分析边界也同样存在剪枝,可以说可有可无 -#if true2 - /* - * 剪枝: - * 共点若只有一次使用,代表此图元是尾巴,可以扔掉. - * 剩下每个点都至少有两条曲线通过(闭合曲线) - */ - - // 尾巴的编号集合 - var reNums = boNodes_Out.Select(a => a.Value) - .Where(b => b.Neighbor.Count == 1)//只有一个点就是尾巴 - .Select(c => c.Number) - .ToList(); - - for (int ii = boNodes_Out.Count - 1; ii >= 0; ii--) - { - //剔除子元素含有尾巴 - var item = boNodes_Out.ElementAt(ii); - var node = item.Value; - for (int jj = node.Neighbor.Count - 1; jj >= 0; jj--) - { - if (reNums.Contains(node.Neighbor[jj].Number)) - node.Neighbor.RemoveAt(jj); - } - - //剔除词典含有尾巴 - if (reNums.Contains(node.Number)) - boNodes_Out.Remove(item.Key); - } -#endif - } - #endregion - - #region 广度 - /// - /// 广度优先算法 - /// 论文链接 - /// - /// 邻接节点集合 - /// 多个面域 - /// - static List> BreadthFirstSearch(BoNode[] boNodes) - { - if (boNodes == null || boNodes.Length == 0) - throw new ArgumentNullException(nameof(boNodes)); - //同一代节点进入队列 - var queue = new Queue(); - - //步骤12:源点无法涉及的点,也就是独立白色的点进行处理 - for (int ssNum = 0; ssNum < boNodes.Length; ssNum++) //O(n) - { - //步骤03:源点 - var s = boNodes[ssNum]; - //步骤12:循环的时候,如果它不白色,表示它被处理过 - if (s.Color != BoColor.白) - continue; - - //步骤04:源点加入队列,进行此源点的邻近点(邻居/儿子们/同一代的点)涂色 - queue.Enqueue(s); - while (queue.Count != 0) //O(n) - { - //步骤05: - var meU = queue.Dequeue(); - - //步骤06 + 步骤10: - //邻近节点(同一代的点)已经在邻接表找到了,这里遍历它们 - meU.Neighbor.ForEach(meNeighborV => { //O(n) - switch (meNeighborV.Color) - { - case BoColor.白://步骤07: - { - //把邻近点都涂灰加入队列, - //下次就是=>步骤04 循环,下一代再进入=>步骤08 - meNeighborV.Color = BoColor.灰; - meNeighborV.Steps = meU.Steps + 1; - meNeighborV.Parent = meU; - queue.Enqueue(meNeighborV); - } - break; - case BoColor.灰://步骤08 - { - if (meNeighborV.Meet == null) - meNeighborV.Meet = new(); - meNeighborV.Meet.Add(meU); - } - break; - case BoColor.黑://步骤09 - { - //论文前面说了曲线它不处理,需要提前获取 - //这里遇到黑色,其实就是腰身闭环(曲线唇形杀回马枪),直接记录环就好了. - //由于有步数,所以不会跨两个的 - //LoopList lst = new(); - //lst.Add(我u); - //lst.Add(子v); - //boss.Add(lst); - } - break; - default: - break; - } - }); - //步骤11 - meU.Color = BoColor.黑; - } - } - return Topo.MeetGetRegions(boNodes); //O(n2) - } - -#if true2 - //逆时针旋转方案,出现了找父节点可能到另一个面域内 - - - - - /// - /// 点积,求值 - /// 1.是两个向量的长度与它们夹角余弦的积 - /// 2.求四个点是否矩形使用 - /// - /// 点 - /// 点 - /// 0夹角0~90度;=0相互垂直;<0夹角90~180度]]> - public static double DotProductValue(this Point3d o, Point3d a, Point3d b) - { - var oa = o.GetVectorTo(a); - var ob = o.GetVectorTo(b); - return oa.DotProduct(ob); - //return (oa._X * ob._X) + (oa._Y * ob._Y) + (oa._Z * ob._Z); - } - - public const double Tau = Math.PI * 2.0; - - /// - /// X轴到向量的弧度,cad的获取的弧度是1PI,所以转换为2PI(上小,下大) - /// - /// 向量 - /// X轴到向量的弧度 - public static double GetAngle2XAxis(this Vector2d ve, double tolerance = 1e-6) - { - //世界重合到用户 Vector3d.XAxis->两点向量 - double al = Vector2d.XAxis.GetAngleTo(ve); - al = ve.Y > 0 ? al : Tau - al; //逆时针为正,大于0是上半圆,小于则是下半圆,如果-负值控制正反 - al = Math.Abs(Tau - al) <= tolerance ? 0 : al; - return al; - } - - - /// - /// 叉积,二维叉乘计算 - /// - /// 传参是向量,表示原点是0,0 - /// 传参是向量,表示原点是0,0 - /// 其模为a与b构成的平行四边形面积 - public static double Cross(Vector2d a, Vector2d b) - { - return a.X * b.Y - a.Y * b.X; - } - - /// - /// 叉积,二维叉乘计算 - /// - /// 原点 - /// oa向量 - /// ob向量,此为判断点 - /// 返回值有正负,表示绕原点四象限的位置变换,也就是有向面积 - public static double Cross(Point2d o, Point2d a, Point2d b) - { - return Cross(o.GetVectorTo(a), o.GetVectorTo(b)); - } - - /// - /// 叉积,逆时针方向为真 - /// - /// 直线点1 - /// 直线点2 - /// 判断点 - /// b点在oa的逆时针 - public static bool CrossAclockwise(Point2d o, Point2d a, Point2d b) - { - return Cross(o, a, b) > -1e-6;//浮点数容差考虑 - } - - - - - /// - /// 在相遇链中提取封闭的区域 - /// - /// - static List> MeetGetRegions(BoNode[] boNodes) - { - List> regions = new(); - - //步骤13 + 步骤18:从所有节点依次取出一个点 - for (int boNums = 0; boNums < boNodes.Length; boNums++) - { - var meetContain = boNodes[boNums]; - //步骤14:跳过 - if (meetContain.Meet == null) - continue; - - //这里就是论文<2 封闭区域分离算法> - //步骤15:新建闭合集 - //存在多个相遇链,论文的图4a和图4b描述这事,分别是左链和右链分享中间 - for (int i = 0; i < meetContain.Meet.Count; i++) - { - LoopList region = new(); - - //相遇节点 - var meetNode = meetContain.Meet[i]; - //if (meetNode.Steps + 1 == meetContain.Steps) //步骤17:步数差1 - // region.Add(meetContain.Parent!); - - region.Add(meetContain); - region.Add(meetNode); - - Init(boNodes); - GetLink(region); - regions.Add(region); - } - } - return regions; - } - - private static void Init(BoNode[] boNodes) - { - for (int i = 0; i < boNodes.Length; i++) - { - boNodes[i].Color = BoColor.白; - } - } - - - /// - /// 相遇点代表环,所以一直点乘左转,直到闭环 - /// - /// - /// - static void GetLink(LoopList region) - { - if (region == null || region.Count == 0) - throw new ArgumentNullException(nameof(region)); - - //最后,最后的上一个 - var a = region.Last!.Previous!.Value; - var centre = region.Last!.Value;//相遇点 - - BoNode? b; - do - { - //获取左转的节点 - b = LeftTurn(a, centre); - if (b == null || b == region.Last!.Value)//剪枝之后这里是不会出现的 - break; - if (b.Color == BoColor.红) - { - //往前寻找腰身闭环处 - var find = region.Find(b); - while (region.Last != find && region.Count != 0) - region.RemoveLast(); - break; - } - b.Color = BoColor.红;//腰身闭环着色 - region.AddFirst(b); - centre = a; - a = b; - } while (true);//回到相遇点就结束 - } - - - /// - /// 左转算法 boNodeCentre=>boNode1=>boNode1.Neighbor - /// - /// 来源 - /// 中心,x轴到它的夹角 - /// 当节点邻居为1时候是空的 - static BoNode? LeftTurn(BoNode boNode1, BoNode boNodeCentre) - { - BoNode? result = null; - double angle = 0.0; - - var boCPt = new Point2d(boNodeCentre.Point.X, boNodeCentre.Point.Y); - var v1 = boCPt.GetVectorTo(new Point2d(boNode1.Point.X, boNode1.Point.Y)); - - for (int i = 0; i < boNode1.Neighbor.Count; i++) - { - var boNode3 = boNode1.Neighbor[i]; - if (boNode3 == boNodeCentre) - continue; - - var v2 = boCPt.GetVectorTo(new Point2d(boNode3.Point.X, boNode3.Point.Y)); - //求夹角最大的,就是左转的节点 - var tmp = PointEx.Tau - v2.GetAngle2XAxis() + v1.GetAngle2XAxis(); - if (tmp > angle) - { - angle = tmp; - result = boNode3; - } - } - return result; - } -#else - /// - /// 在相遇链中提取封闭的区域 - /// - /// - static List> MeetGetRegions(BoNode[] boNodes) - { - List> regions = new(); - - //步骤13 + 步骤18:从所有节点依次取出一个点 - for (int boNums = 0; boNums < boNodes.Length; boNums++) - { - var 含有相遇v0 = boNodes[boNums]; - //步骤14:跳过 - if (含有相遇v0.Meet == null) - continue; - - //这里就是论文<2 封闭区域分离算法> - //步骤15:新建闭合集 - for (int i = 0; i < 含有相遇v0.Meet.Count; i++) - { - LoopList region = new(); - - //存在多个相遇链,论文的图4a和图4b描述这事,分别是左链和右链分享中间 - var 相遇v1 = 含有相遇v0.Meet[i]; - if (含有相遇v0.Steps == 相遇v1.Steps) //步骤16:步数相同,就是同一代 - { - region.Add(含有相遇v0); - region.Add(相遇v1); - } - else if (相遇v1.Steps + 1 == 含有相遇v0.Steps) //步骤17:步数差1 - { - region.Add(含有相遇v0.Parent!); - region.Add(含有相遇v0); - region.Add(相遇v1); - } - else - { - //Debugger.Break();//这里会出现意外吗? - } - GetLink(region); //O(n) - regions.Add(Topo.OrderByRegionLines(region)); //O(n2) - } - } - return regions; - } - - /// - /// 从相遇点开始往上寻找父节点并加入链中 - /// - /// - /// - static void GetLink(LoopList L) - { - if (L == null || L.Count == 0) - throw new ArgumentNullException(nameof(L)); - - var uM = L.First; - var vM = L.Last; - if (uM == vM) - return; - - var u = uM!.Value; - var v = vM!.Value; - - while (u.Parent != null && v.Parent != null) - { - if (u.Parent == v.Parent) - { - //步骤20: - L.AddLast(u.Parent); - break; - } - else - { - //步骤21: - L.AddFirst(u.Parent); - L.AddLast(v.Parent); - u = u.Parent; - v = v.Parent; - } - } - } - - /// - /// 调整线序 - /// - /// - static LoopList OrderByRegionLines(LoopList region) - { - if (region == null || region.Count == 0) - throw new ArgumentNullException(nameof(region)); - - LoopList list = new(); - var boNode = region.First!.Value; - for (int i = 0; i < region.Count; i++)//约束循环找顺序次数 - { - list.Add(boNode); - region.For((itemNum, item) => { - //邻居节点作为目标进入循环 - var boNode2 = item.Value; - if (boNode2 != boNode - && !list.Contains(boNode2) - && boNode.Neighbor.Contains(boNode2)) - { - boNode = boNode2;//进入循环 - return true; - } - return false; - }); - } - return list; - } -#endif - #endregion -} \ No newline at end of file diff --git "a/src/IFoxCAD.Cad/ExtensionMethod/\351\227\255\345\220\210\350\276\271\347\225\214\347\256\227\346\263\225/\350\256\272\346\226\207\347\274\272\351\231\267.png" "b/src/IFoxCAD.Cad/ExtensionMethod/\351\227\255\345\220\210\350\276\271\347\225\214\347\256\227\346\263\225/\350\256\272\346\226\207\347\274\272\351\231\267.png" deleted file mode 100644 index df2895073a33a95aece3bb25f67cdc7c69f171f4..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 202707 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%0AX&xbqHs=FRIZ9g{)LtIH6$I{gu&f7f220}*+h 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

rCsAV>NGe3Ec^hV|vMN7m2!WM85s(OPO~hcS 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 1701;1702;CS1685 + + + + + + + + + + -- Gitee From 044fad430076de8f22f7486c3b88161dc68c2e44 Mon Sep 17 00:00:00 2001 From: liuqihong <540762622@qq.com> Date: Tue, 9 Aug 2022 01:17:00 +0800 Subject: [PATCH 332/675] =?UTF-8?q?=E4=BF=AE=E6=94=B9=E6=B7=B1=E5=BA=A6?= =?UTF-8?q?=E5=85=8B=E9=9A=86=E5=87=BD=E6=95=B0=E5=90=8D?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/IFoxCAD.Cad/ExtensionMethod/SymbolTableRecordEx.cs | 2 +- .../HatchConverter.cs" | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/IFoxCAD.Cad/ExtensionMethod/SymbolTableRecordEx.cs b/src/IFoxCAD.Cad/ExtensionMethod/SymbolTableRecordEx.cs index 999b178..0a594d4 100644 --- a/src/IFoxCAD.Cad/ExtensionMethod/SymbolTableRecordEx.cs +++ b/src/IFoxCAD.Cad/ExtensionMethod/SymbolTableRecordEx.cs @@ -19,7 +19,7 @@ public static class SymbolTableRecordEx /// /// 图元id集合,注意所有成员都要在同一个空间中 /// 克隆后的id词典 - public static IdMapping EntityClone(this BlockTableRecord btr, ObjectIdCollection objIds) + public static IdMapping DeepClone(this BlockTableRecord btr, ObjectIdCollection objIds) { if (objIds is null || objIds.Count == 0) throw new ArgumentNullException(nameof(objIds)); diff --git "a/src/IFoxCAD.Cad/ExtensionMethod/\346\226\260\345\273\272\345\241\253\345\205\205/HatchConverter.cs" "b/src/IFoxCAD.Cad/ExtensionMethod/\346\226\260\345\273\272\345\241\253\345\205\205/HatchConverter.cs" index 077e4e5..b58861e 100644 --- "a/src/IFoxCAD.Cad/ExtensionMethod/\346\226\260\345\273\272\345\241\253\345\205\205/HatchConverter.cs" +++ "b/src/IFoxCAD.Cad/ExtensionMethod/\346\226\260\345\273\272\345\241\253\345\205\205/HatchConverter.cs" @@ -320,7 +320,7 @@ public ObjectId CreateBoundarysAndHatchToMsPs(BlockTableRecord btrOfAddEntitySpa * 那么它的平移后的基点在哪里呢? */ - var newHatchId = btrOfAddEntitySpace.EntityClone(new ObjectIdCollection(new ObjectId[] { OldHatchId })).GetValues()[0]; + var newHatchId = btrOfAddEntitySpace.DeepClone(new ObjectIdCollection(new ObjectId[] { OldHatchId })).GetValues()[0]; trans ??= DBTrans.Top.Transaction; var hatchEnt = trans.GetObject(newHatchId, OpenMode.ForWrite) as Hatch; if (hatchEnt != null) -- Gitee From 6f3a7ca6adc799cbc9b8281b98367da4997fcf80 Mon Sep 17 00:00:00 2001 From: liuqihong <540762622@qq.com> Date: Tue, 9 Aug 2022 01:33:14 +0800 Subject: [PATCH 333/675] =?UTF-8?q?=E6=8F=90=E4=BE=9B=E9=9D=99=E6=80=81?= =?UTF-8?q?=E5=8F=98=E9=87=8F=E6=8E=A7=E5=88=B6=E6=97=A5=E5=BF=97=E6=98=AF?= =?UTF-8?q?=E5=90=A6=E8=BE=93=E5=87=BA?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../ExtensionMethod/SymbolTableRecordEx.cs | 3 +- src/IFoxCAD.Cad/Runtime/Log.cs | 87 ++++++++++--------- 2 files changed, 48 insertions(+), 42 deletions(-) diff --git a/src/IFoxCAD.Cad/ExtensionMethod/SymbolTableRecordEx.cs b/src/IFoxCAD.Cad/ExtensionMethod/SymbolTableRecordEx.cs index 0a594d4..397eac2 100644 --- a/src/IFoxCAD.Cad/ExtensionMethod/SymbolTableRecordEx.cs +++ b/src/IFoxCAD.Cad/ExtensionMethod/SymbolTableRecordEx.cs @@ -23,7 +23,7 @@ public static IdMapping DeepClone(this BlockTableRecord btr, ObjectIdCollection { if (objIds is null || objIds.Count == 0) throw new ArgumentNullException(nameof(objIds)); - + var db = objIds[0].Database; IdMapping mapping = new(); using (btr.ForWrite()) @@ -40,6 +40,7 @@ public static IdMapping DeepClone(this BlockTableRecord btr, ObjectIdCollection } catch (System.Exception e) { + LogHelper.FlagOutVsOutput = true; e.WriteLog(); } } diff --git a/src/IFoxCAD.Cad/Runtime/Log.cs b/src/IFoxCAD.Cad/Runtime/Log.cs index d8b2e62..5f50a51 100644 --- a/src/IFoxCAD.Cad/Runtime/Log.cs +++ b/src/IFoxCAD.Cad/Runtime/Log.cs @@ -116,8 +116,10 @@ public override void WriteLog(string? message) #if !NET5_0 && !NET6_0 try { - EventLog eventLog = new(); - eventLog.Source = LogName; + EventLog eventLog = new() + { + Source = LogName + }; eventLog.WriteEntry(message, EventLogEntryType.Information); } catch (System.Security.SecurityException e) @@ -133,19 +135,28 @@ public override void WriteLog(string? message) #region 静态方法 public static class LogHelper { - ///

- /// 读写锁 - /// 当资源处于写入模式时,其他线程写入需要等待本次写入结束之后才能继续写入 - /// - static readonly ReaderWriterLockSlim _logWriteLock = new(); - +#pragma warning disable CA2211 // 非常量字段应当不可见 /// /// 日志文件完整路径 /// -#pragma warning disable CA2211 // 非常量字段应当不可见 public static string? LogAddress; + /// + /// 输出错误信息到日志文件的开关 + /// + public static bool FlagOutFile = false; + /// + /// 输出错误信息到vs输出窗口的开关 + /// + public static bool FlagOutVsOutput = true; + #pragma warning restore CA2211 // 非常量字段应当不可见 + /// + /// 读写锁 + /// 当资源处于写入模式时,其他线程写入需要等待本次写入结束之后才能继续写入 + /// + static readonly ReaderWriterLockSlim _logWriteLock = new(); + /// /// 提供给外部设置log文件保存路径 /// @@ -186,39 +197,32 @@ public static void OptionFile(string? newlogAddress = null) } public static string WriteLog(this string? message, - LogTarget target = LogTarget.File, - bool printDebugWindow = true) + LogTarget target = LogTarget.File) { if (message == null) return string.Empty; - return LogAction(null, message, target, printDebugWindow); + return LogAction(null, message, target); } public static string WriteLog(this Exception? exception, - LogTarget target = LogTarget.File, - bool printDebugWindow = true) + LogTarget target = LogTarget.File) { if (exception == null) return string.Empty; - return LogAction(exception, null, target, printDebugWindow); + return LogAction(exception, null, target); } - public static string WriteLog(this Exception? exception, - string? message, - LogTarget target = LogTarget.File, - bool printDebugWindow = true) + public static string WriteLog(this Exception? exception, string? message, + LogTarget target = LogTarget.File) { if (exception == null) return string.Empty; - return LogAction(exception, message, target, printDebugWindow); + return LogAction(exception, message, target); } - static string LogAction(Exception? ex, - string? message, - LogTarget target, - bool printDebugWindow) + static string LogAction(Exception? ex, string? message, LogTarget target) { if (ex == null && message == null) return string.Empty; @@ -236,29 +240,30 @@ static string LogAction(Exception? ex, if (logtxtJson == null) return string.Empty; - LogBase? logger; - switch (target) + if (FlagOutFile) { - case LogTarget.File: - logger = new FileLogger(); - logger.WriteLog(logtxtJson); - break; - case LogTarget.Database: - logger = new DBLogger(); - logger.WriteLog(logtxtJson); - break; - case LogTarget.EventLog: - logger = new EventLogger(); - logger.WriteLog(logtxtJson); - break; + LogBase? logger; + switch (target) + { + case LogTarget.File: + logger = new FileLogger(); + logger.WriteLog(logtxtJson); + break; + case LogTarget.Database: + logger = new DBLogger(); + logger.WriteLog(logtxtJson); + break; + case LogTarget.EventLog: + logger = new EventLogger(); + logger.WriteLog(logtxtJson); + break; + } } - if (printDebugWindow) + if (FlagOutVsOutput) { Debug.WriteLine("错误日志: " + LogAddress); Debug.Write(logtxtJson); - //Debugger.Break(); - //Debug.Assert(false, "终止进程"); } return logtxtJson; } -- Gitee From 7d989fccc64ff91f3a753507e6ae6ce9b8da20c6 Mon Sep 17 00:00:00 2001 From: liuqihong <540762622@qq.com> Date: Tue, 9 Aug 2022 01:34:26 +0800 Subject: [PATCH 334/675] =?UTF-8?q?=E4=BF=AE=E6=94=B9=E6=B3=A8=E9=87=8A?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../ExtensionMethod/SymbolTableRecordEx.cs | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/src/IFoxCAD.Cad/ExtensionMethod/SymbolTableRecordEx.cs b/src/IFoxCAD.Cad/ExtensionMethod/SymbolTableRecordEx.cs index 397eac2..6ff5bfd 100644 --- a/src/IFoxCAD.Cad/ExtensionMethod/SymbolTableRecordEx.cs +++ b/src/IFoxCAD.Cad/ExtensionMethod/SymbolTableRecordEx.cs @@ -9,7 +9,7 @@ public static class SymbolTableRecordEx #region 克隆实体id /// - /// 克隆id到块表记录 + /// 深度克隆id到块表记录 /// 0x01 此方法不允许是未添加数据库的图元,因此它是id /// 0x02 若为未添加数据库图元,则利用entity.Clone();同时不需要考虑动态块属性,可以使用entity.GetTransformedCopy /// @@ -30,18 +30,17 @@ public static IdMapping DeepClone(this BlockTableRecord btr, ObjectIdCollection { try { - //深度克隆 db.DeepCloneObjects(objIds, btr.ObjectId, mapping, false); - //不在此提取,为了此函数高频使用 - //获取克隆键值对(旧块名,新块名) - //foreach (ObjectId item in blockIds) - // result.Add(mapping[item].Value); + // 不在此提取,为了此函数被高频调用 + // 获取克隆键值对(旧块名,新块名) + // foreach (ObjectId item in blockIds) + // result.Add(mapping[item].Value); } catch (System.Exception e) { LogHelper.FlagOutVsOutput = true; - e.WriteLog(); + e.WriteLog("深度克隆出错了"); } } return mapping; -- Gitee From 2118856c1a85784b850b1a91f806c4b9ea0a14d2 Mon Sep 17 00:00:00 2001 From: liuqihong <540762622@qq.com> Date: Wed, 10 Aug 2022 20:13:31 +0800 Subject: [PATCH 335/675] =?UTF-8?q?=E4=BF=AE=E5=A4=8D(https://gitee.com/in?= =?UTF-8?q?spirefunction/ifoxcad/issues/I5LGXA)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/IFoxCAD.Cad/ExtensionMethod/EditorEx.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/IFoxCAD.Cad/ExtensionMethod/EditorEx.cs b/src/IFoxCAD.Cad/ExtensionMethod/EditorEx.cs index bbe0846..d1cc6a8 100644 --- a/src/IFoxCAD.Cad/ExtensionMethod/EditorEx.cs +++ b/src/IFoxCAD.Cad/ExtensionMethod/EditorEx.cs @@ -779,7 +779,7 @@ public static Matrix3d GetMatrix(this Editor editor, CoordinateSystemCode from, return (from, to) switch { (CoordinateSystemCode.Wcs, CoordinateSystemCode.Ucs) => editor.GetMatrixFromWcsToUcs(), - (CoordinateSystemCode.Wcs, CoordinateSystemCode.MDcs) => editor.GetMatrixFromMDcsToWcs(), + (CoordinateSystemCode.Wcs, CoordinateSystemCode.MDcs) => editor.GetMatrixFromWcsToMDcs(), (CoordinateSystemCode.Ucs, CoordinateSystemCode.Wcs) => editor.GetMatrixFromUcsToWcs(), (CoordinateSystemCode.Ucs, CoordinateSystemCode.MDcs) => editor.GetMatrixFromUcsToWcs() * editor.GetMatrixFromWcsToMDcs(), (CoordinateSystemCode.MDcs, CoordinateSystemCode.Wcs) => editor.GetMatrixFromMDcsToWcs(), -- Gitee From 4f0c5849f739a3fd11ee51b74e180f165a991234 Mon Sep 17 00:00:00 2001 From: liuqihong <540762622@qq.com> Date: Wed, 10 Aug 2022 20:50:31 +0800 Subject: [PATCH 336/675] =?UTF-8?q?=E7=A7=BB=E9=99=A4=E5=A4=9A=E4=BD=99?= =?UTF-8?q?=E7=9A=84=E5=91=BD=E5=90=8D=E7=A9=BA=E9=97=B4=E5=B9=B6=E4=BC=98?= =?UTF-8?q?=E5=8C=96=E4=BB=A3=E7=A0=81=E6=A0=BC=E5=BC=8F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/IFoxCAD.Cad/Algorithms/Graph/Graph.cs | 5 +- .../Algorithms/QuadTree/QuadTree.cs | 408 +++-- .../Algorithms/QuadTree/QuadTreeEvn.cs | 39 +- .../Algorithms/QuadTree/QuadTreeNode.cs | 1439 ++++++++--------- .../Algorithms/QuadTree/QuadTreeSelectMode.cs | 37 +- .../Algorithms/QuadTree/Utility.cs | 99 +- .../ExtensionMethod/BulgeVertexWidth.cs | 11 +- src/IFoxCAD.Cad/ExtensionMethod/Curve2dEx.cs | 1 - .../ExtensionMethod/DBDictionaryEx.cs | 49 +- src/IFoxCAD.Cad/ExtensionMethod/EntityEx.cs | 21 +- src/IFoxCAD.Cad/ExtensionMethod/GeometryEx.cs | 2 +- src/IFoxCAD.Cad/ExtensionMethod/Jig.cs | 3 +- src/IFoxCAD.Cad/ExtensionMethod/Tools.cs | 303 ++-- .../HatchConverter.cs" | 6 - .../HatchEx.cs" | 7 +- .../HatchInfo.cs" | 6 +- src/IFoxCAD.Cad/GlobalUsings.cs | 3 + src/IFoxCAD.Cad/ResultData/LispDottedPair.cs | 6 +- src/IFoxCAD.Cad/ResultData/LispList.cs | 5 +- src/IFoxCAD.Cad/ResultData/TypedValueList.cs | 1 - src/IFoxCAD.Cad/ResultData/XRecordDataList.cs | 14 +- src/IFoxCAD.Cad/ResultData/XdataList.cs | 18 +- src/IFoxCAD.Cad/Runtime/CadVersion.cs | 1 - src/IFoxCAD.Cad/Runtime/Env.cs | 5 +- src/IFoxCAD.Cad/Runtime/Log.cs | 4 +- src/IFoxCAD.Cad/Runtime/SymbolTable.cs | 36 +- tests/Test/GlobalUsings.cs | 1 + tests/Test/TestDBTrans.cs | 1 + 28 files changed, 1233 insertions(+), 1298 deletions(-) diff --git a/src/IFoxCAD.Cad/Algorithms/Graph/Graph.cs b/src/IFoxCAD.Cad/Algorithms/Graph/Graph.cs index e369b6d..ad1efb5 100644 --- a/src/IFoxCAD.Cad/Algorithms/Graph/Graph.cs +++ b/src/IFoxCAD.Cad/Algorithms/Graph/Graph.cs @@ -1,6 +1,5 @@ -using Exception = System.Exception; -namespace IFoxCAD.Cad; - +namespace IFoxCAD.Cad; +using Exception = System.Exception; /// /// 无权无向图实现 diff --git a/src/IFoxCAD.Cad/Algorithms/QuadTree/QuadTree.cs b/src/IFoxCAD.Cad/Algorithms/QuadTree/QuadTree.cs index e808777..0eb1bf8 100644 --- a/src/IFoxCAD.Cad/Algorithms/QuadTree/QuadTree.cs +++ b/src/IFoxCAD.Cad/Algorithms/QuadTree/QuadTree.cs @@ -1,7 +1,4 @@ -using System; -using System.Collections.Generic; - -/* +/* * 四叉树维基百科 http://en.wikipedia.org/wiki/Quadtree * 四叉树是一种分区空间的算法,更快找出内部或外部给定区域. * 通过一个正交矩形边界进行中心点分裂四个正交矩形, @@ -21,243 +18,242 @@ * 3.接口:约束都要有正交矩形,否则无法调用"包含"方法 * 4.选择模式:模仿cad的窗选和框选 */ -namespace IFoxCAD.Cad +namespace IFoxCAD.Cad; + +/// +/// 根节点控制器 +/// +/// 类型接口约束必须有正交矩形 +public class QuadTree where TEntity : QuadEntity { + #region 成员 /// - /// 根节点控制器 + /// 根节点 /// - /// 类型接口约束必须有正交矩形 - public class QuadTree where TEntity : QuadEntity - { - #region 成员 - /// - /// 根节点 - /// - QuadTreeNode _rootNode; + QuadTreeNode _rootNode; - /// - /// 四叉树节点的数目 - /// - public int Count { get => _rootNode.CountSubTree; } + /// + /// 四叉树节点的数目 + /// + public int Count { get => _rootNode.CountSubTree; } - /// - /// 点容器(红黑树) - /// - SortedSet _points; - #endregion + /// + /// 点容器(红黑树) + /// + SortedSet _points; + #endregion - #region 构造 - /// - /// 四叉树根节点控制器 - /// - /// 四叉树矩形范围 - public QuadTree(Rect rect) + #region 构造 + /// + /// 四叉树根节点控制器 + /// + /// 四叉树矩形范围 + public QuadTree(Rect rect) + { + _rootNode = new QuadTreeNode(rect, null, 0);//初始化根节点 + _points = new(); + } + #endregion + + #region 方法 + /// + /// 通过根节点插入数据项 + /// + /// + public void Insert(TEntity ent) + { + /* + * 图元点 是不分裂空间的,加入一个红黑树内部. + */ + if (ent.IsPoint) { - _rootNode = new QuadTreeNode(rect, null, 0);//初始化根节点 - _points = new(); + _points.Add(ent); + return; } - #endregion - #region 方法 - /// - /// 通过根节点插入数据项 - /// - /// - public void Insert(TEntity ent) + while (!_rootNode.Contains(ent)) { /* - * 图元点 是不分裂空间的,加入一个红黑树内部. + * 四叉树插入时候,如果超出根边界,就需要扩展 + * 扩展时候有一个要求,当前边界要作为扩展边界的一个象限,也就是反向分裂 + * + * 创建新根,计算原根在新根的位置, + * 替换指针:获取新分裂的节点的父节点,判断它哪个儿子是它, + * 替换之后可能仍然不包含图元边界,再循环计算. */ - if (ent.IsPoint) + var sq_Left = _rootNode._X; + var sq_Botton = _rootNode._Y; + var sq_Right = _rootNode._Right; + var sq_Top = _rootNode._Top; + if (ent._Y >= _rootNode._Y)//上↑增殖 { - _points.Add(ent); - return; + if (ent._X >= _rootNode._X) + { + //右上↗增殖 + sq_Right += _rootNode.Width; + sq_Top += _rootNode.Height; + } + else + { + //左上↖增殖 + sq_Left -= _rootNode.Width; + sq_Top += _rootNode.Height; + } } - - while (!_rootNode.Contains(ent)) + else//在下↓ { - /* - * 四叉树插入时候,如果超出根边界,就需要扩展 - * 扩展时候有一个要求,当前边界要作为扩展边界的一个象限,也就是反向分裂 - * - * 创建新根,计算原根在新根的位置, - * 替换指针:获取新分裂的节点的父节点,判断它哪个儿子是它, - * 替换之后可能仍然不包含图元边界,再循环计算. - */ - var sq_Left = _rootNode._X; - var sq_Botton = _rootNode._Y; - var sq_Right = _rootNode._Right; - var sq_Top = _rootNode._Top; - if (ent._Y >= _rootNode._Y)//上↑增殖 + if (ent._X >= _rootNode._X) { - if (ent._X >= _rootNode._X) - { - //右上↗增殖 - sq_Right += _rootNode.Width; - sq_Top += _rootNode.Height; - } - else - { - //左上↖增殖 - sq_Left -= _rootNode.Width; - sq_Top += _rootNode.Height; - } + //右下↘增殖 + sq_Right += _rootNode.Width; + sq_Botton -= _rootNode.Height; } - else//在下↓ + else { - if (ent._X >= _rootNode._X) - { - //右下↘增殖 - sq_Right += _rootNode.Width; - sq_Botton -= _rootNode.Height; - } - else - { - //左下↙增殖 - sq_Left -= _rootNode.Width; - sq_Botton -= _rootNode.Height; - } + //左下↙增殖 + sq_Left -= _rootNode.Width; + sq_Botton -= _rootNode.Height; } - //扩大2次方 - var rectSquare = new Rect(sq_Left, sq_Botton, sq_Right, sq_Top); - - //四叉树的旧根要作为四分之一插入 - //新根中计算原根 - //把 旧根节点 连接到 新根节点 上面,然后新根成为根 - var newRoot = new QuadTreeNode(rectSquare, null, 0); - var insert = newRoot.Insert(_rootNode); - if (insert is null) - throw new("四叉树:新根尺寸不对"); - if (!insert.Equals(_rootNode)) - throw new("四叉树:新旧节点大小不一致,无法连接"); + } + //扩大2次方 + var rectSquare = new Rect(sq_Left, sq_Botton, sq_Right, sq_Top); - var insPar = insert.Parent; - _rootNode.Parent = insPar; - if (insPar is null) - return; + //四叉树的旧根要作为四分之一插入 + //新根中计算原根 + //把 旧根节点 连接到 新根节点 上面,然后新根成为根 + var newRoot = new QuadTreeNode(rectSquare, null, 0); + var insert = newRoot.Insert(_rootNode); + if (insert is null) + throw new("四叉树:新根尺寸不对"); + if (!insert.Equals(_rootNode)) + throw new("四叉树:新旧节点大小不一致,无法连接"); - if (_rootNode.Equals(insPar.RightTopTree)) - insPar.RightTopTree = _rootNode; - else if (_rootNode.Equals(insPar.RightBottomTree)) - insPar.RightBottomTree = _rootNode; - else if (_rootNode.Equals(insPar.LeftBottomTree)) - insPar.LeftBottomTree = _rootNode; - else if (_rootNode.Equals(insPar.LeftTopTree)) - insPar.LeftTopTree = _rootNode; - else - throw new("四叉树:新节点不对,无法连接"); + var insPar = insert.Parent; + _rootNode.Parent = insPar; + if (insPar is null) + return; - //其后的子节点层数全部增加层数, - //要加多少层取决于当前根边界属于新根边界的所在层 - var depth = insert.Depth; - if (depth == 0) - throw new("四叉树:插入节点是0,造成错误"); - _rootNode.ForEach(node => { - node.Depth += depth; - return false; - }); + if (_rootNode.Equals(insPar.RightTopTree)) + insPar.RightTopTree = _rootNode; + else if (_rootNode.Equals(insPar.RightBottomTree)) + insPar.RightBottomTree = _rootNode; + else if (_rootNode.Equals(insPar.LeftBottomTree)) + insPar.LeftBottomTree = _rootNode; + else if (_rootNode.Equals(insPar.LeftTopTree)) + insPar.LeftTopTree = _rootNode; + else + throw new("四叉树:新节点不对,无法连接"); - //交换根控制 - _rootNode = newRoot; - } + //其后的子节点层数全部增加层数, + //要加多少层取决于当前根边界属于新根边界的所在层 + var depth = insert.Depth; + if (depth == 0) + throw new("四叉树:插入节点是0,造成错误"); + _rootNode.ForEach(node => { + node.Depth += depth; + return false; + }); - _rootNode.Insert(ent); + //交换根控制 + _rootNode = newRoot; } + _rootNode.Insert(ent); + } - /// - /// 查询四叉树,返回给定区域的数据项 - /// - /// 矩形选区查询 - /// - public List Query(Rect rect, QuadTreeSelectMode selectMode = QuadTreeSelectMode.IntersectsWith) - { - QuadTreeEvn.SelectMode = selectMode; - var results = new List(); - //选择图元 - _rootNode.Query(rect, results); - //选择点 - var ptge = _points.GetEnumerator(); - switch (selectMode) - { - case QuadTreeSelectMode.IntersectsWith: - case QuadTreeSelectMode.Contains: - /* 由于红黑树的方法 _points.GetViewBetween() - * 过滤只能过滤X区间,Y区间还是要过滤, - * 那么我就只能用这样的方法加速了 - * - * 而更好的方式是不用红黑树,去加入一个点云数据来进行,可谓是编程无极限.... - */ - while (ptge.MoveNext()) - { - var ptEnt = ptge.Current; - if (rect._X <= ptEnt._X && ptEnt._X <= rect._Right) - { - if (rect._Y <= ptEnt._Y && ptEnt._Y <= rect.Top) - results.Add(ptEnt); - } - else if (ptEnt._X > rect._Right) - break;//超过后面范围就break,因为红黑树已经排序 - } - break; - default: - throw new("四叉树:" + nameof(selectMode)); - } - return results; - } + /// + /// 查询四叉树,返回给定区域的数据项 + /// + /// 矩形选区查询 + /// + public List Query(Rect rect, QuadTreeSelectMode selectMode = QuadTreeSelectMode.IntersectsWith) + { + QuadTreeEvn.SelectMode = selectMode; - /// - /// 删除子节点 - /// - /// 根据范围删除 - public void Remove(Rect rect) + var results = new List(); + //选择图元 + _rootNode.Query(rect, results); + //选择点 + var ptge = _points.GetEnumerator(); + switch (selectMode) { - _rootNode.Remove(rect); + case QuadTreeSelectMode.IntersectsWith: + case QuadTreeSelectMode.Contains: + /* 由于红黑树的方法 _points.GetViewBetween() + * 过滤只能过滤X区间,Y区间还是要过滤, + * 那么我就只能用这样的方法加速了 + * + * 而更好的方式是不用红黑树,去加入一个点云数据来进行,可谓是编程无极限.... + */ + while (ptge.MoveNext()) + { + var ptEnt = ptge.Current; + if (rect._X <= ptEnt._X && ptEnt._X <= rect._Right) + { + if (rect._Y <= ptEnt._Y && ptEnt._Y <= rect.Top) + results.Add(ptEnt); + } + else if (ptEnt._X > rect._Right) + break;//超过后面范围就break,因为红黑树已经排序 + } + break; + default: + throw new("四叉树:" + nameof(selectMode)); } + return results; + } - /// - /// 删除子节点 - /// - /// 根据图元删除 - public void Remove(TEntity ent) - { - _rootNode.Remove(ent); - } + /// + /// 删除子节点 + /// + /// 根据范围删除 + public void Remove(Rect rect) + { + _rootNode.Remove(rect); + } - /// - /// 找到附近节点图元 - /// - [Obsolete("找附近节点的并不是最近的图元")] - public TEntity? FindNeibor(Rect rect, QuadTreeFindMode findMode) - { - return _rootNode.FindNeibor(rect, findMode); - } + /// + /// 删除子节点 + /// + /// 根据图元删除 + public void Remove(TEntity ent) + { + _rootNode.Remove(ent); + } - /// - /// 找到附近图元 - /// - /// - /// - public TEntity? FindNearEntity(Rect rect) - { - return _rootNode.FindNearEntity(rect); - } + /// + /// 找到附近节点图元 + /// + [Obsolete("找附近节点的并不是最近的图元")] + public TEntity? FindNeibor(Rect rect, QuadTreeFindMode findMode) + { + return _rootNode.FindNeibor(rect, findMode); + } - /// - /// 执行四叉树中特定的行为 - /// - /// - public void ForEach(QTAction action) - { - _rootNode.ForEach(action); - } + /// + /// 找到附近图元 + /// + /// + /// + public TEntity? FindNearEntity(Rect rect) + { + return _rootNode.FindNearEntity(rect); + } - /// - /// 委托:四叉树节点上执行一个操作 - /// - /// - public delegate bool QTAction(QuadTreeNode obj); - #endregion + /// + /// 执行四叉树中特定的行为 + /// + /// + public void ForEach(QTAction action) + { + _rootNode.ForEach(action); } + + /// + /// 委托:四叉树节点上执行一个操作 + /// + /// + public delegate bool QTAction(QuadTreeNode obj); + #endregion } \ No newline at end of file diff --git a/src/IFoxCAD.Cad/Algorithms/QuadTree/QuadTreeEvn.cs b/src/IFoxCAD.Cad/Algorithms/QuadTree/QuadTreeEvn.cs index 4189512..16d518b 100644 --- a/src/IFoxCAD.Cad/Algorithms/QuadTree/QuadTreeEvn.cs +++ b/src/IFoxCAD.Cad/Algorithms/QuadTree/QuadTreeEvn.cs @@ -1,27 +1,26 @@ #pragma warning disable CA2211 // 非常量字段应当不可见 -namespace IFoxCAD.Cad +namespace IFoxCAD.Cad; + +public class QuadTreeEvn { - public class QuadTreeEvn - { - /// - /// 最小的节点有一个面积(一定要大于0) - /// - public static double MinArea = 1e-6; + /// + /// 最小的节点有一个面积(一定要大于0) + /// + public static double MinArea = 1e-6; - /// - /// 选择模式 - /// - public static QuadTreeSelectMode SelectMode; + /// + /// 选择模式 + /// + public static QuadTreeSelectMode SelectMode; - /// - /// 最大深度 - /// - public static int QuadTreeMaximumDepth = 2046; + /// + /// 最大深度 + /// + public static int QuadTreeMaximumDepth = 2046; - /// - /// 节点内容超过就分裂 - /// - public static int QuadTreeContentsCountSplit = 20; - } + /// + /// 节点内容超过就分裂 + /// + public static int QuadTreeContentsCountSplit = 20; } #pragma warning restore CA2211 // 非常量字段应当不可见 \ No newline at end of file diff --git a/src/IFoxCAD.Cad/Algorithms/QuadTree/QuadTreeNode.cs b/src/IFoxCAD.Cad/Algorithms/QuadTree/QuadTreeNode.cs index 252101d..86cb5b4 100644 --- a/src/IFoxCAD.Cad/Algorithms/QuadTree/QuadTreeNode.cs +++ b/src/IFoxCAD.Cad/Algorithms/QuadTree/QuadTreeNode.cs @@ -1,823 +1,818 @@ -using System; -using System.Collections.Generic; -using System.Linq; - -namespace IFoxCAD.Cad +namespace IFoxCAD.Cad; + +/// +/// 子节点 +/// +/// +public class QuadTreeNode + : Rect + where TEntity : QuadEntity { + #region 成员 + /// + /// 子节点:第一象限:右上↗ + /// + public QuadTreeNode? RightTopTree; + /// + /// 子节点:第二象限:左上↖ + /// + public QuadTreeNode? LeftTopTree; + /// + /// 子节点:第三象限:左下↙ + /// + public QuadTreeNode? LeftBottomTree; + /// + /// 子节点:第四象限:右下↘ + /// + public QuadTreeNode? RightBottomTree; /// - /// 子节点 + /// 所有子节点 /// - /// - public class QuadTreeNode - : Rect - where TEntity : QuadEntity + QuadTreeNode[] Nodes { - #region 成员 - /// - /// 子节点:第一象限:右上↗ - /// - public QuadTreeNode? RightTopTree; - /// - /// 子节点:第二象限:左上↖ - /// - public QuadTreeNode? LeftTopTree; - /// - /// 子节点:第三象限:左下↙ - /// - public QuadTreeNode? LeftBottomTree; - /// - /// 子节点:第四象限:右下↘ - /// - public QuadTreeNode? RightBottomTree; - /// - /// 所有子节点 - /// - QuadTreeNode[] Nodes + get { - get + return new QuadTreeNode[] { - return new QuadTreeNode[] - { - RightTopTree!, - LeftTopTree!, - LeftBottomTree!, - RightBottomTree!, - }; - } - } - /// - /// 所有子节点是空的 - /// - bool NodesIsEmpty => RightTopTree is null && LeftTopTree is null && LeftBottomTree is null && RightBottomTree is null; - - /// - /// 父节点 - /// - public QuadTreeNode? Parent; - /// - /// 节点的在四叉树的深度 - /// - public int Depth; - - // 注意,内容没有限制:这不是 impement 四叉树的标准方法 - /// (节点图元是交叉线压着的,并不是矩形范围内全部,因为这是四叉树的特性决定) - /// - /// 本节点:内容 - /// - public List Contents; - - /// - /// 本节点和旗下所有子节点:内容群 - /// - public void ContentsSubTree(List results) - { - if (Contents is null) - return; - results.AddRange(Contents); - var nodes = Nodes; - for (int i = 0; i < nodes.Length; i++) - nodes[i]?.ContentsSubTree(results); + RightTopTree!, + LeftTopTree!, + LeftBottomTree!, + RightBottomTree!, + }; } + } + /// + /// 所有子节点是空的 + /// + bool NodesIsEmpty => RightTopTree is null && LeftTopTree is null && LeftBottomTree is null && RightBottomTree is null; - /// - /// 本节点和旗下所有子节点:内容群数量 - /// - public int CountSubTree - { - get - { - if (Contents is null) - return 0; - int count = Contents.Count; + /// + /// 父节点 + /// + public QuadTreeNode? Parent; + /// + /// 节点的在四叉树的深度 + /// + public int Depth; - var nodes = Nodes; - for (int i = 0; i < nodes.Length; i++) - { - var node = nodes[i]; - if (node is null) - continue; - count += node.CountSubTree; - } - return count; - } - } - #endregion - - #region 构造 - /// - /// 四叉树节点 - /// - /// 当前节点边界 - /// 父节点 - /// 节点深度 - public QuadTreeNode(Rect box, QuadTreeNode? parent, int depth) - { - _X = box._X; - _Y = box._Y; - _Right = box._Right; - _Top = box._Top; - - Parent = parent; - Depth = depth; - Contents = new(); - } - #endregion - - #region 增 - /// - /// 将原有节点插入用 - /// - /// - internal QuadTreeNode? Insert(Rect rect) - { - if (!Contains(rect)) - return null; + // 注意,内容没有限制:这不是 impement 四叉树的标准方法 + /// (节点图元是交叉线压着的,并不是矩形范围内全部,因为这是四叉树的特性决定) + /// + /// 本节点:内容 + /// + public List Contents; - //四叉树分裂,将当前节点分为四个子节点 - if (NodesIsEmpty) - CreateChildren(); + /// + /// 本节点和旗下所有子节点:内容群 + /// + public void ContentsSubTree(List results) + { + if (Contents is null) + return; + results.AddRange(Contents); + var nodes = Nodes; + for (int i = 0; i < nodes.Length; i++) + nodes[i]?.ContentsSubTree(results); + } + + /// + /// 本节点和旗下所有子节点:内容群数量 + /// + public int CountSubTree + { + get + { + if (Contents is null) + return 0; + int count = Contents.Count; - //当前节点边界 包含 图元包围盒 就插入 - //退出递归:4个节点都不完全包含 - //4个节点的上层 var nodes = Nodes; for (int i = 0; i < nodes.Length; i++) { var node = nodes[i]; if (node is null) continue; - - if (node.Equals(rect)) - { - rect = node; - return node.Insert(rect); - } + count += node.CountSubTree; } - return this; + return count; } + } + #endregion + + #region 构造 + /// + /// 四叉树节点 + /// + /// 当前节点边界 + /// 父节点 + /// 节点深度 + public QuadTreeNode(Rect box, QuadTreeNode? parent, int depth) + { + _X = box._X; + _Y = box._Y; + _Right = box._Right; + _Top = box._Top; + + Parent = parent; + Depth = depth; + Contents = new(); + } + #endregion + + #region 增 + /// + /// 将原有节点插入用 + /// + /// + internal QuadTreeNode? Insert(Rect rect) + { + if (!Contains(rect)) + return null; - /// - /// 将数据项递归插入四叉树 - /// - /// - public QuadTreeNode? Insert(TEntity ent) + //四叉树分裂,将当前节点分为四个子节点 + if (NodesIsEmpty) + CreateChildren(); + + //当前节点边界 包含 图元包围盒 就插入 + //退出递归:4个节点都不完全包含 + //4个节点的上层 + var nodes = Nodes; + for (int i = 0; i < nodes.Length; i++) { - if (!Contains(ent)) + var node = nodes[i]; + if (node is null) + continue; + + if (node.Equals(rect)) { - //Debug.WriteLine("不在四叉树边界范围"); - //Trace.WriteLine("不在四叉树边界范围"); - return null; + rect = node; + return node.Insert(rect); } + } + return this; + } + + /// + /// 将数据项递归插入四叉树 + /// + /// + public QuadTreeNode? Insert(TEntity ent) + { + if (!Contains(ent)) + { + //Debug.WriteLine("不在四叉树边界范围"); + //Trace.WriteLine("不在四叉树边界范围"); + return null; + } - // if (ent.IsPoint) - // { - // //找到最后一层包含它的节点,然后加入它 - // //因此是跳过分裂矩形的,以免造成无限递归 - // var minNode = GetMinNode(ent); - // minNode.Contents.Add(ent); - // return minNode; - // } + // if (ent.IsPoint) + // { + // //找到最后一层包含它的节点,然后加入它 + // //因此是跳过分裂矩形的,以免造成无限递归 + // var minNode = GetMinNode(ent); + // minNode.Contents.Add(ent); + // return minNode; + // } #if true2 - //方案二: - //内容数超过才分裂,防止树深度过高,但是多选过滤时候慢一点 - if (Contents.Count > QuadTreeEvn.QuadTreeContentsCountSplit) + //方案二: + //内容数超过才分裂,防止树深度过高,但是多选过滤时候慢一点 + if (Contents.Count > QuadTreeEvn.QuadTreeContentsCountSplit) + { + //分裂出四个子节点 + if (_nodesIsEmpty) { - //分裂出四个子节点 - if (_nodesIsEmpty) + CreateChildren(); + //分裂之后将当前层的内容扔到四个子节点, + //如果被压着,那么就不会扔到下面 + for (int i = Contents.Count - 1; i >= 0; i--) { - CreateChildren(); - //分裂之后将当前层的内容扔到四个子节点, - //如果被压着,那么就不会扔到下面 - for (int i = Contents.Count - 1; i >= 0; i--) - { - var minNode = GetMinNode(Contents[i].Box); - minNode.Contents.Add(Contents[i]); - Contents.RemoveAt(i); - } + var minNode = GetMinNode(Contents[i].Box); + minNode.Contents.Add(Contents[i]); + Contents.RemoveAt(i); } - else + } + else + { + //没有分裂的话,就递归 + //退出递归:4个节点都不完全包含,内容就是他们的父亲 + var nodes = _Nodes; + for (int i = 0; i < nodes.Length; i++) { - //没有分裂的话,就递归 - //退出递归:4个节点都不完全包含,内容就是他们的父亲 - var nodes = _Nodes; - for (int i = 0; i < nodes.Length; i++) - { - var node = nodes[i]; - if (node is null) - continue; - - //这里需要中断.(匿名方法ForEach无法中断,会造成父节点加入内容) - if (node.Contains(ent)) - return node.Insert(ent); - } + var node = nodes[i]; + if (node is null) + continue; + + //这里需要中断.(匿名方法ForEach无法中断,会造成父节点加入内容) + if (node.Contains(ent)) + return node.Insert(ent); } } + } #else - //方案一:分裂到最细节点 + //方案一:分裂到最细节点 - //分裂出四个子节点 - if (NodesIsEmpty) - CreateChildren(); + //分裂出四个子节点 + if (NodesIsEmpty) + CreateChildren(); - //4个子节点开始递归 - //退出递归:4个节点都不完全包含,内容就是他们的父亲 - var nodes = Nodes; - for (int i = 0; i < nodes.Length; i++) - { - var node = nodes[i]; - if (node is null) - continue; - //这里需要中断.(匿名方法ForEach无法中断,会造成父节点加入内容) - if (node.Contains(ent)) - return node.Insert(ent); - } + //4个子节点开始递归 + //退出递归:4个节点都不完全包含,内容就是他们的父亲 + var nodes = Nodes; + for (int i = 0; i < nodes.Length; i++) + { + var node = nodes[i]; + if (node is null) + continue; + //这里需要中断.(匿名方法ForEach无法中断,会造成父节点加入内容) + if (node.Contains(ent)) + return node.Insert(ent); + } #endif - //为什么要用容器? - //相同包围盒或者四叉树分割线压着多个. - this.Contents.Add(ent); - return this; - } + //为什么要用容器? + //相同包围盒或者四叉树分割线压着多个. + this.Contents.Add(ent); + return this; + } + + /// + /// 创建子节点 + /// + void CreateChildren() + { + // 最小面积控制节点深度,但是这样可能导致树分成高,引起爆栈 + if (Depth > QuadTreeEvn.QuadTreeMaximumDepth) + return; + var recs = RectSplit(this); + var de = Depth + 1; + RightTopTree = new QuadTreeNode(recs[0], this, de); + LeftTopTree = new QuadTreeNode(recs[1], this, de); + LeftBottomTree = new QuadTreeNode(recs[2], this, de); + RightBottomTree = new QuadTreeNode(recs[3], this, de); + } + + /// + /// 矩形分裂为四个 + /// + /// + /// + Rect[] RectSplit(Rect box) + { + var halfWidth = box.Width / 2.0; + var halfHeight = box.Height / 2.0; - /// - /// 创建子节点 - /// - void CreateChildren() + var upperRight = new Rect(box._X + halfWidth, box._Y + halfHeight, box._Right, box._Top); + var upperLeft = new Rect(box._X, box._Y + halfHeight, box._Right - halfWidth, box._Top); + var lowerleft = new Rect(box._X, box._Y, box._Right - halfWidth, box._Top - halfHeight);//基础 + var lowerRight = new Rect(box._X + halfWidth, box._Y, box._Right, box._Top - halfHeight); + + //依照象限顺序输出 + return new Rect[] { upperRight, upperLeft, lowerleft, lowerRight }; + } + #endregion + + #region 删 + /// + /// 删除图元 + /// + /// 根据图元删除 + public bool Remove(TEntity easeEnt) + { + //通过图元id删除无疑是非常低效的, + //1.相当于在所有的容器查找它,但是移除只会移除一次, + // 因此必须要求图元只会加入一次,才能中断检索剩余分支. + //2.这个代价还是太高,因此我们还是要默认条件,图元载入一次之后,不再改动. + //3.不再改动也不太合理,因为cad图元还是可以修改的 + + //1.处理内容 + if (Contents.Remove(easeEnt)) { - // 最小面积控制节点深度,但是这样可能导致树分成高,引起爆栈 - if (Depth > QuadTreeEvn.QuadTreeMaximumDepth) - return; - var recs = RectSplit(this); - var de = Depth + 1; - RightTopTree = new QuadTreeNode(recs[0], this, de); - LeftTopTree = new QuadTreeNode(recs[1], this, de); - LeftBottomTree = new QuadTreeNode(recs[2], this, de); - RightBottomTree = new QuadTreeNode(recs[3], this, de); + if (CountSubTree == 0) + this.Clear(this); + return true; } - /// - /// 矩形分裂为四个 - /// - /// - /// - Rect[] RectSplit(Rect box) + //2.递归子节点移除 + var nodes = Nodes; + for (int i = 0; i < nodes.Length; i++) { - var halfWidth = box.Width / 2.0; - var halfHeight = box.Height / 2.0; + var node = nodes[i]; + if (node is null) + continue; + if (node.Remove(easeEnt)) //递归进入子节点删除内容 + return true; //删除成功就中断其他节点的搜索 + } + return false; + } - var upperRight = new Rect(box._X + halfWidth, box._Y + halfHeight, box._Right, box._Top); - var upperLeft = new Rect(box._X, box._Y + halfHeight, box._Right - halfWidth, box._Top); - var lowerleft = new Rect(box._X, box._Y, box._Right - halfWidth, box._Top - halfHeight);//基础 - var lowerRight = new Rect(box._X + halfWidth, box._Y, box._Right, box._Top - halfHeight); + /// + /// 递归进入最下层节点,然后开始清理 + /// + /// + void Clear(QuadTreeNode node) + { + var nodes = Nodes; + for (int i = 0; i < nodes.Length; i++) + nodes[i]?.Clear(nodes[i]); + + node.Contents.Clear(); + //node.Contents = null;//重复加入时候会出错 + node.RightTopTree = null; + node.LeftTopTree = null; + node.LeftBottomTree = null; + node.RightBottomTree = null; + node.Parent = null; + //node.Box = zoreRect; + } - //依照象限顺序输出 - return new Rect[] { upperRight, upperLeft, lowerleft, lowerRight }; - } - #endregion - - #region 删 - /// - /// 删除图元 - /// - /// 根据图元删除 - public bool Remove(TEntity easeEnt) + /// + /// 删除子节点内容 + /// + /// 根据范围删除 + public void Remove(Rect queryArea) + { + //本节点内容移除 + if (Contents is not null && Contents.Count > 0)//从最上层的根节点开始进入 { - //通过图元id删除无疑是非常低效的, - //1.相当于在所有的容器查找它,但是移除只会移除一次, - // 因此必须要求图元只会加入一次,才能中断检索剩余分支. - //2.这个代价还是太高,因此我们还是要默认条件,图元载入一次之后,不再改动. - //3.不再改动也不太合理,因为cad图元还是可以修改的 - - //1.处理内容 - if (Contents.Remove(easeEnt)) + for (int i = Contents.Count - 1; i >= 0; i--) { - if (CountSubTree == 0) - this.Clear(this); - return true; - } - - //2.递归子节点移除 - var nodes = Nodes; - for (int i = 0; i < nodes.Length; i++) - { - var node = nodes[i]; - if (node is null) - continue; - if (node.Remove(easeEnt)) //递归进入子节点删除内容 - return true; //删除成功就中断其他节点的搜索 + var ent = Contents[i]; + //移除之后,如果容器是0,那么这里不能直接 Contents=null, + //因为此节点下面可能还有节点, + //需要判断了其后数量0才可以清理. + //否则其后还有内容,那么此节点就是仍然可以用的. + if (queryArea.Contains(ent)) + Contents.Remove(ent); } - return false; } - /// - /// 递归进入最下层节点,然后开始清理 - /// - /// - void Clear(QuadTreeNode node) + //同插入一样 + //跳到指定节点再搜索这个节点下面的图元 + var nodes = Nodes; + for (int i = 0; i < nodes.Length; i++) { - var nodes = Nodes; - for (int i = 0; i < nodes.Length; i++) - nodes[i]?.Clear(nodes[i]); - - node.Contents.Clear(); - //node.Contents = null;//重复加入时候会出错 - node.RightTopTree = null; - node.LeftTopTree = null; - node.LeftBottomTree = null; - node.RightBottomTree = null; - node.Parent = null; - //node.Box = zoreRect; - } - - /// - /// 删除子节点内容 - /// - /// 根据范围删除 - public void Remove(Rect queryArea) - { - //本节点内容移除 - if (Contents is not null && Contents.Count > 0)//从最上层的根节点开始进入 + var node = nodes[i]; + if (node is null) + continue; + if (node.NodesIsEmpty) + continue; + + //此节点边界 完全包含 查询区域,则转到该节点,并跳过其余节点(打断此循环) + if (node.Contains(queryArea)) { - for (int i = Contents.Count - 1; i >= 0; i--) - { - var ent = Contents[i]; - //移除之后,如果容器是0,那么这里不能直接 Contents=null, - //因为此节点下面可能还有节点, - //需要判断了其后数量0才可以清理. - //否则其后还有内容,那么此节点就是仍然可以用的. - if (queryArea.Contains(ent)) - Contents.Remove(ent); - } + node.Remove(queryArea); + break; } - - //同插入一样 - //跳到指定节点再搜索这个节点下面的图元 - var nodes = Nodes; - for (int i = 0; i < nodes.Length; i++) + //查询区域 完全包含 此节点边界,提取此节点全部内容 + //跳过分析碰撞,并继续循环搜索其他节点 + if (queryArea.Contains(node)) { - var node = nodes[i]; - if (node is null) - continue; - if (node.NodesIsEmpty) - continue; - - //此节点边界 完全包含 查询区域,则转到该节点,并跳过其余节点(打断此循环) - if (node.Contains(queryArea)) - { - node.Remove(queryArea); - break; - } - //查询区域 完全包含 此节点边界,提取此节点全部内容 - //跳过分析碰撞,并继续循环搜索其他节点 - if (queryArea.Contains(node)) - { - node.Clear(node); - continue; - } - //查询区域 与 此节点四边形边线碰撞 查询该四边形中,并继续循环搜索其他节点 - //1,角点碰撞 2,边界碰撞 - if (node.IntersectsWith(queryArea)) - node.Remove(queryArea); + node.Clear(node); + continue; } - - //本节点内容移除之后,旗下还有内容的话, - //会跳过此处,再进入子节点进行递归,直到最后一个节点 - if (CountSubTree == 0) - Clear(this); + //查询区域 与 此节点四边形边线碰撞 查询该四边形中,并继续循环搜索其他节点 + //1,角点碰撞 2,边界碰撞 + if (node.IntersectsWith(queryArea)) + node.Remove(queryArea); } - #endregion - - #region 查 - /// - /// 查询范围内的实体 - /// - /// 查询矩形 - /// - public void Query(Rect queryArea, List results) - { - GetCurrentContents(queryArea, results); - //遍历子节点 - var nodes = Nodes; - for (int i = 0; i < nodes.Length; i++) - { - var node = nodes[i]; - if (node is null) - continue; - //子节点的4个子节点都是空的, - //那么表示元素会在子节点这一层啊... - if (node.NodesIsEmpty) - continue; + //本节点内容移除之后,旗下还有内容的话, + //会跳过此处,再进入子节点进行递归,直到最后一个节点 + if (CountSubTree == 0) + Clear(this); + } + #endregion - //此节点边界 完全包含 查询区域,则转到该节点,并跳过其余节点(打断此循环) - if (node.Contains(queryArea)) - { - node.Query(queryArea, results); - break; - } - //查询区域 完全包含 此节点边界,提取此节点全部内容 - //跳过分析碰撞,并继续循环搜索其他节点 - if (queryArea.Contains(node)) - { - node.ContentsSubTree(results); - continue; - } - //查询区域 与 此节点四边形边线碰撞 查询该四边形中,并继续循环搜索其他节点 - //1,角点碰撞 2,边界碰撞 - if (node.IntersectsWith(queryArea)) - node.Query(queryArea, results); - } - } + #region 查 + /// + /// 查询范围内的实体 + /// + /// 查询矩形 + /// + public void Query(Rect queryArea, List results) + { + GetCurrentContents(queryArea, results); - /// - /// 获取本节点内容 - /// - /// - /// - void GetCurrentContents(Rect queryArea, List results) + //遍历子节点 + var nodes = Nodes; + for (int i = 0; i < nodes.Length; i++) { - //遍历当前节点内容,加入方式取决于碰撞模式 - if (QuadTreeEvn.SelectMode == QuadTreeSelectMode.IntersectsWith) + var node = nodes[i]; + if (node is null) + continue; + //子节点的4个子节点都是空的, + //那么表示元素会在子节点这一层啊... + if (node.NodesIsEmpty) + continue; + + //此节点边界 完全包含 查询区域,则转到该节点,并跳过其余节点(打断此循环) + if (node.Contains(queryArea)) { - for (int i = 0; i < Contents.Count; i++) - { - var ent = Contents[i]; - if (queryArea.IntersectsWith(ent)) - results.Add(ent); - } + node.Query(queryArea, results); + break; } - else + //查询区域 完全包含 此节点边界,提取此节点全部内容 + //跳过分析碰撞,并继续循环搜索其他节点 + if (queryArea.Contains(node)) { - for (int i = 0; i < Contents.Count; i++) - { - var ent = Contents[i]; - if (queryArea.Contains(ent)) - results.Add(ent); - } + node.ContentsSubTree(results); + continue; } + //查询区域 与 此节点四边形边线碰撞 查询该四边形中,并继续循环搜索其他节点 + //1,角点碰撞 2,边界碰撞 + if (node.IntersectsWith(queryArea)) + node.Query(queryArea, results); } + } - /// - /// 找临近图元 - /// - /// 查找矩形 - /// - public TEntity? FindNearEntity(Rect queryArea) + /// + /// 获取本节点内容 + /// + /// + /// + void GetCurrentContents(Rect queryArea, List results) + { + //遍历当前节点内容,加入方式取决于碰撞模式 + if (QuadTreeEvn.SelectMode == QuadTreeSelectMode.IntersectsWith) { - TEntity? resultEntity = default; - //1.找到 查找矩形 所在的节点,利用此节点的矩形. - var queryNode = GetMinNode(queryArea); - var queryAreaCenter = queryArea.CenterPoint; - - //2.从根开始搜索 - // 如果搜索父亲的父亲的...内容群,它不是距离最近的,只是节点(亲属关系)最近 - // 储存找过的<图元,距离> - var entDic = new Dictionary(); - - var old = QuadTreeEvn.SelectMode; - QuadTreeEvn.SelectMode = QuadTreeSelectMode.IntersectsWith; - while (true) + for (int i = 0; i < Contents.Count; i++) { - //循环找父节点大小 - var hw = queryNode.Width / 2.0; - var hh = queryNode.Height / 2.0; - //3.利用选区中心扩展一个节点边界大小的矩形.从而选择图元 - // 再判断图元的与目标的距离,找到最小距离,即为最近 - var minPt = new Point2d(queryAreaCenter.X - hw, queryAreaCenter.Y - hh); - var maxPt = new Point2d(queryAreaCenter.X + hw, queryAreaCenter.Y + hh); - var ents = new List(); - Query(new Rect(minPt, maxPt), ents); - for (int i = 0; i < ents.Count; i++) - { - var ent = ents[i]; - if (entDic.ContainsKey(ent)) - continue; - var dis = ent.CenterPoint.GetDistanceTo(queryAreaCenter); - if (dis > 1e-6)//剔除本身 - entDic.Add(ent, dis); - } - if (entDic.Count > 0) - { - resultEntity = entDic.OrderBy(a => a.Value).First().Key; - break; - } - if (queryNode.Parent is null)//最顶层就退出 - break; - queryNode = queryNode.Parent;//利用父节点矩形进行变大选区 + var ent = Contents[i]; + if (queryArea.IntersectsWith(ent)) + results.Add(ent); } - QuadTreeEvn.SelectMode = old; - return resultEntity; } - - /// - /// 找临近节点的图元 - /// - /// 查找矩形 - /// 查找什么方向 - /// - [Obsolete("找附近节点的并不是最近的图元")] - public TEntity? FindNeibor(Rect queryArea, QuadTreeFindMode findMode) + else { - TEntity? resultEntity = default; - //1.找到 查找矩形 所在的节点,利用此节点的矩形. - //2.利用节点矩形是分裂的特点,边和边必然贴合. - //3.找到方向 findMode 拥有的节点,然后查找节点的内容 - var queryNode = GetMinNode(queryArea); - - bool whileFlag = true; - //同一个节点可能包含邻居,因为四叉树的加入是图元压线, - //那么就在这里搜就得了,用中心点决定空间位置 - //但是本空间的图元可能都比它矮,无法满足条件 - if (queryNode.CountSubTree > 1) + for (int i = 0; i < Contents.Count; i++) { - resultEntity = GetNearestNeighbor(queryNode, findMode, queryArea); - if (resultEntity is null || resultEntity.CenterPoint == queryArea.CenterPoint) - whileFlag = true; - else - whileFlag = false; + var ent = Contents[i]; + if (queryArea.Contains(ent)) + results.Add(ent); } + } + } - while (whileFlag) + /// + /// 找临近图元 + /// + /// 查找矩形 + /// + public TEntity? FindNearEntity(Rect queryArea) + { + TEntity? resultEntity = default; + //1.找到 查找矩形 所在的节点,利用此节点的矩形. + var queryNode = GetMinNode(queryArea); + var queryAreaCenter = queryArea.CenterPoint; + + //2.从根开始搜索 + // 如果搜索父亲的父亲的...内容群,它不是距离最近的,只是节点(亲属关系)最近 + // 储存找过的<图元,距离> + var entDic = new Dictionary(); + + var old = QuadTreeEvn.SelectMode; + QuadTreeEvn.SelectMode = QuadTreeSelectMode.IntersectsWith; + while (true) + { + //循环找父节点大小 + var hw = queryNode.Width / 2.0; + var hh = queryNode.Height / 2.0; + //3.利用选区中心扩展一个节点边界大小的矩形.从而选择图元 + // 再判断图元的与目标的距离,找到最小距离,即为最近 + var minPt = new Point2d(queryAreaCenter.X - hw, queryAreaCenter.Y - hh); + var maxPt = new Point2d(queryAreaCenter.X + hw, queryAreaCenter.Y + hh); + var ents = new List(); + Query(new Rect(minPt, maxPt), ents); + for (int i = 0; i < ents.Count; i++) { - //同一个父节点是临近的,优先获取 兄弟节点 的内容. - //循环了第二次是北方兄弟的节点, - //但是这不是一个找到临近图元的方法, - //因为临近的可能是父亲的父亲的父亲...另一个函数 FindNearEntity 写 - //本方案也仅仅作为找北方节点 - var parent = queryNode.Parent; - if (parent is not null) - { - switch (findMode) - { - case QuadTreeFindMode.Top: - { - //下格才获取上格,否则导致做了无用功,上格就直接获取邻居了 - if (parent.LeftBottomTree == queryNode || parent.RightBottomTree == queryNode) - resultEntity = GetNearestNeighbor(parent, findMode, queryArea); - } - break; - case QuadTreeFindMode.Bottom: - { - if (parent.LeftTopTree == queryNode || parent.RightTopTree == queryNode) - resultEntity = GetNearestNeighbor(parent, findMode, queryArea); - } - break; - case QuadTreeFindMode.Left: - { - if (parent.RightTopTree == queryNode || parent.RightBottomTree == queryNode) - resultEntity = GetNearestNeighbor(parent, findMode, queryArea); - } - break; - case QuadTreeFindMode.Right: - { - if (parent.LeftTopTree == queryNode || parent.LeftBottomTree == queryNode) - resultEntity = GetNearestNeighbor(parent, findMode, queryArea); - } - break; - } - } - if (resultEntity is not null) - break; - - //通过 所在节点 找 邻居节点, - //拿到 邻居节点 下面的所有内容(图元) - //内容可能是空的,再从邻居那往北找...如果找到了四叉树最外层,仍然没有内容,退出循环 - var neiborNode = FindNeiborNode(queryNode, findMode); - if (neiborNode is null) + var ent = ents[i]; + if (entDic.ContainsKey(ent)) continue; - if (neiborNode.CountSubTree > 0) - { - resultEntity = GetNearestNeighbor(neiborNode, findMode, queryArea); - break; - } - if (neiborNode.Parent is null)//如果找到了四叉树最外层,仍然没有内容,退出循环 - break; - queryNode = neiborNode; + var dis = ent.CenterPoint.GetDistanceTo(queryAreaCenter); + if (dis > 1e-6)//剔除本身 + entDic.Add(ent, dis); } - - return resultEntity; + if (entDic.Count > 0) + { + resultEntity = entDic.OrderBy(a => a.Value).First().Key; + break; + } + if (queryNode.Parent is null)//最顶层就退出 + break; + queryNode = queryNode.Parent;//利用父节点矩形进行变大选区 } + QuadTreeEvn.SelectMode = old; + return resultEntity; + } - /// - /// 查找节点的(本内容和子内容)与(查找面积)矩形中点对比,找到最近一个内容 - /// - /// 查找面积 - /// 查找方向 - /// 查找节点 - /// - TEntity? GetNearestNeighbor(QuadTreeNode queryNode, QuadTreeFindMode findMode, Rect queryArea) + /// + /// 找临近节点的图元 + /// + /// 查找矩形 + /// 查找什么方向 + /// + [Obsolete("找附近节点的并不是最近的图元")] + public TEntity? FindNeibor(Rect queryArea, QuadTreeFindMode findMode) + { + TEntity? resultEntity = default; + //1.找到 查找矩形 所在的节点,利用此节点的矩形. + //2.利用节点矩形是分裂的特点,边和边必然贴合. + //3.找到方向 findMode 拥有的节点,然后查找节点的内容 + var queryNode = GetMinNode(queryArea); + + bool whileFlag = true; + //同一个节点可能包含邻居,因为四叉树的加入是图元压线, + //那么就在这里搜就得了,用中心点决定空间位置 + //但是本空间的图元可能都比它矮,无法满足条件 + if (queryNode.CountSubTree > 1) { - TEntity? results = default; - - var lst = new List(); - var qcent = queryArea.CenterPoint; + resultEntity = GetNearestNeighbor(queryNode, findMode, queryArea); + if (resultEntity is null || resultEntity.CenterPoint == queryArea.CenterPoint) + whileFlag = true; + else + whileFlag = false; + } - switch (findMode) + while (whileFlag) + { + //同一个父节点是临近的,优先获取 兄弟节点 的内容. + //循环了第二次是北方兄弟的节点, + //但是这不是一个找到临近图元的方法, + //因为临近的可能是父亲的父亲的父亲...另一个函数 FindNearEntity 写 + //本方案也仅仅作为找北方节点 + var parent = queryNode.Parent; + if (parent is not null) { - case QuadTreeFindMode.Top: - { - //取出Y比queryArea的还大的一个,是最近的那个 - var qy = qcent.Y; - queryNode.ContentsSubTree(lst); - lst.ForEach(ent => + switch (findMode) + { + case QuadTreeFindMode.Top: { - if (ent.CenterPoint.Y > qy) - lst.Add(ent); - }); - lst = lst.OrderBy(ent => ent.CenterPoint.Y).ToList(); - } - break; - case QuadTreeFindMode.Bottom: - { - var qy = qcent.Y; - queryNode.ContentsSubTree(lst); - lst.ForEach(ent => + //下格才获取上格,否则导致做了无用功,上格就直接获取邻居了 + if (parent.LeftBottomTree == queryNode || parent.RightBottomTree == queryNode) + resultEntity = GetNearestNeighbor(parent, findMode, queryArea); + } + break; + case QuadTreeFindMode.Bottom: { - if (ent.CenterPoint.Y < qy) - lst.Add(ent); - }); - lst = lst.OrderByDescending(ent => ent.CenterPoint.Y).ToList(); - } - break; - case QuadTreeFindMode.Left: - { - var qx = qcent.Y; - queryNode.ContentsSubTree(lst); - lst.ForEach(ent => + if (parent.LeftTopTree == queryNode || parent.RightTopTree == queryNode) + resultEntity = GetNearestNeighbor(parent, findMode, queryArea); + } + break; + case QuadTreeFindMode.Left: { - if (ent.CenterPoint.X > qx) - lst.Add(ent); - }); - lst = lst.OrderBy(ent => ent.CenterPoint.X).ToList(); - } - break; - case QuadTreeFindMode.Right: - { - var qx = qcent.Y; - queryNode.ContentsSubTree(lst); - lst.ForEach(ent => + if (parent.RightTopTree == queryNode || parent.RightBottomTree == queryNode) + resultEntity = GetNearestNeighbor(parent, findMode, queryArea); + } + break; + case QuadTreeFindMode.Right: { - if (ent.CenterPoint.X < qx) - lst.Add(ent); - }); - lst = lst.OrderByDescending(ent => ent.CenterPoint.X).ToList(); - } - break; + if (parent.LeftTopTree == queryNode || parent.LeftBottomTree == queryNode) + resultEntity = GetNearestNeighbor(parent, findMode, queryArea); + } + break; + } } - - if (lst.Count > 0) - return lst[0];//可能就是本体重叠 - return results; - } - - /// - /// 找包含它的最小分支 - /// - /// 查询的矩形 - /// 节点 - QuadTreeNode GetMinNode(Rect queryArea) - { - var nodes = Nodes; - for (int i = 0; i < nodes.Length; i++) + if (resultEntity is not null) + break; + + //通过 所在节点 找 邻居节点, + //拿到 邻居节点 下面的所有内容(图元) + //内容可能是空的,再从邻居那往北找...如果找到了四叉树最外层,仍然没有内容,退出循环 + var neiborNode = FindNeiborNode(queryNode, findMode); + if (neiborNode is null) + continue; + if (neiborNode.CountSubTree > 0) { - var node = nodes[i]; - if (node is null) - continue; - - //边界包含查询面积,那么再递归查询, - //直到最后四个都不包含,那么上一个就是图元所在节点 - if (node.Contains(queryArea)) - return node.GetMinNode(queryArea);//中断后面的范围,才可以返回正确的 + resultEntity = GetNearestNeighbor(neiborNode, findMode, queryArea); + break; } - return this; + if (neiborNode.Parent is null)//如果找到了四叉树最外层,仍然没有内容,退出循环 + break; + queryNode = neiborNode; } - /// - /// 四叉树找邻居节点(相同或更大) - /// - /// 源节点 - /// 方向 - /// - QuadTreeNode? FindNeiborNode(QuadTreeNode tar, QuadTreeFindMode findMode) + return resultEntity; + } + + /// + /// 查找节点的(本内容和子内容)与(查找面积)矩形中点对比,找到最近一个内容 + /// + /// 查找面积 + /// 查找方向 + /// 查找节点 + /// + TEntity? GetNearestNeighbor(QuadTreeNode queryNode, QuadTreeFindMode findMode, Rect queryArea) + { + TEntity? results = default; + + var lst = new List(); + var qcent = queryArea.CenterPoint; + + switch (findMode) { - var parent = tar.Parent; - if (parent is null) - return null; - switch (findMode) - { - case QuadTreeFindMode.Top: + case QuadTreeFindMode.Top: + { + //取出Y比queryArea的还大的一个,是最近的那个 + var qy = qcent.Y; + queryNode.ContentsSubTree(lst); + lst.ForEach(ent => { - //判断当前节点在父节点的位置,如果是在 下格 就取对应的 上格 - if (tar == parent.LeftBottomTree) - return parent.LeftTopTree; - if (tar == parent.RightBottomTree) - return parent.RightTopTree; - //否则就是上格 - //找父节点的北方邻居..也就是在爷节点上面找 - //递归,此时必然是 下格,就必然返回 上格,然后退出递归 - var parentNeibor = FindNeiborNode(parent, QuadTreeFindMode.Top); - //父节点的北方邻居 无 子节点 - if (parentNeibor is null || parentNeibor.RightTopTree is null) - return parentNeibor;//返回父节点的北方邻居,比较大 - //父节点的北方邻居 有 子节点,剩下条件就只有这两 - - // 如果直接返回,那么是(相同或更大), - // 而找邻近图元需要的是这个(相同或更大)下面的图元,在外面对这个格子内图元用坐标分析就好了 - if (tar == parent.LeftTopTree) - return parentNeibor.LeftBottomTree; - return parentNeibor.RightBottomTree; - } - case QuadTreeFindMode.Bottom: + if (ent.CenterPoint.Y > qy) + lst.Add(ent); + }); + lst = lst.OrderBy(ent => ent.CenterPoint.Y).ToList(); + } + break; + case QuadTreeFindMode.Bottom: + { + var qy = qcent.Y; + queryNode.ContentsSubTree(lst); + lst.ForEach(ent => { - if (tar == parent.LeftTopTree) - return parent.LeftBottomTree; - if (tar == parent.RightTopTree) - return parent.RightBottomTree; - var parentNeibor = FindNeiborNode(parent, QuadTreeFindMode.Bottom); - if (parentNeibor is null || parentNeibor.RightTopTree is null) - return parentNeibor; - if (tar == parent.LeftBottomTree) - return parentNeibor.LeftTopTree; - return parentNeibor.RightTopTree; - } - case QuadTreeFindMode.Right: + if (ent.CenterPoint.Y < qy) + lst.Add(ent); + }); + lst = lst.OrderByDescending(ent => ent.CenterPoint.Y).ToList(); + } + break; + case QuadTreeFindMode.Left: + { + var qx = qcent.Y; + queryNode.ContentsSubTree(lst); + lst.ForEach(ent => { - if (tar == parent.LeftTopTree) - return parent.RightTopTree; - if (tar == parent.LeftBottomTree) - return parent.RightBottomTree; - var parentNeibor = FindNeiborNode(parent, QuadTreeFindMode.Right); - if (tar == parent.RightTopTree) - return parentNeibor?.LeftTopTree; - return parentNeibor?.LeftBottomTree; - } - case QuadTreeFindMode.Left: + if (ent.CenterPoint.X > qx) + lst.Add(ent); + }); + lst = lst.OrderBy(ent => ent.CenterPoint.X).ToList(); + } + break; + case QuadTreeFindMode.Right: + { + var qx = qcent.Y; + queryNode.ContentsSubTree(lst); + lst.ForEach(ent => { - if (tar == parent.RightTopTree) - return parent.LeftTopTree; - if (tar == parent.RightBottomTree) - return parent.LeftBottomTree; - var parentNeibor = FindNeiborNode(parent, QuadTreeFindMode.Left); - if (tar == parent.LeftTopTree) - return parentNeibor?.RightTopTree; - return parentNeibor?.RightBottomTree; - } - } - return null; + if (ent.CenterPoint.X < qx) + lst.Add(ent); + }); + lst = lst.OrderByDescending(ent => ent.CenterPoint.X).ToList(); + } + break; } - #endregion - - #region 改 - /// - /// 所有的点归类到最小包围它的空间 - /// - //public void PointsToMinNode() - //{ - // ForEach(node => - // { - // for (int i = 0; i < node.Contents.Count; i++) - // { - // var ent = node.Contents[i]; - // if (ent.IsPoint) - // { - // //如果最小包含!=当前,就是没有放在最适合的位置 - // var queryNode = GetMinNode(ent); - // if (queryNode != node) - // { - // node.Remove(ent); - // queryNode.Contents.Add(ent); - // } - // } - // } - // return false; - // }); - //} - #endregion - - #region 方法 - /// - /// 递归全部节点(提供给根用的,所以是全部) - /// - /// QTAction - public bool ForEach(QuadTree.QTAction action) + + if (lst.Count > 0) + return lst[0];//可能就是本体重叠 + return results; + } + + /// + /// 找包含它的最小分支 + /// + /// 查询的矩形 + /// 节点 + QuadTreeNode GetMinNode(Rect queryArea) + { + var nodes = Nodes; + for (int i = 0; i < nodes.Length; i++) { - //执行本节点 - if (action(this)) - return true; + var node = nodes[i]; + if (node is null) + continue; + + //边界包含查询面积,那么再递归查询, + //直到最后四个都不包含,那么上一个就是图元所在节点 + if (node.Contains(queryArea)) + return node.GetMinNode(queryArea);//中断后面的范围,才可以返回正确的 + } + return this; + } - //递归执行本节点的子节点 - var nodes = Nodes; - for (int i = 0; i < nodes.Length; i++) - { - var node = nodes[i]; - if (node is null) - continue; - if (node.ForEach(action)) - break; - } - return false; + /// + /// 四叉树找邻居节点(相同或更大) + /// + /// 源节点 + /// 方向 + /// + QuadTreeNode? FindNeiborNode(QuadTreeNode tar, QuadTreeFindMode findMode) + { + var parent = tar.Parent; + if (parent is null) + return null; + switch (findMode) + { + case QuadTreeFindMode.Top: + { + //判断当前节点在父节点的位置,如果是在 下格 就取对应的 上格 + if (tar == parent.LeftBottomTree) + return parent.LeftTopTree; + if (tar == parent.RightBottomTree) + return parent.RightTopTree; + //否则就是上格 + //找父节点的北方邻居..也就是在爷节点上面找 + //递归,此时必然是 下格,就必然返回 上格,然后退出递归 + var parentNeibor = FindNeiborNode(parent, QuadTreeFindMode.Top); + //父节点的北方邻居 无 子节点 + if (parentNeibor is null || parentNeibor.RightTopTree is null) + return parentNeibor;//返回父节点的北方邻居,比较大 + //父节点的北方邻居 有 子节点,剩下条件就只有这两 + + // 如果直接返回,那么是(相同或更大), + // 而找邻近图元需要的是这个(相同或更大)下面的图元,在外面对这个格子内图元用坐标分析就好了 + if (tar == parent.LeftTopTree) + return parentNeibor.LeftBottomTree; + return parentNeibor.RightBottomTree; + } + case QuadTreeFindMode.Bottom: + { + if (tar == parent.LeftTopTree) + return parent.LeftBottomTree; + if (tar == parent.RightTopTree) + return parent.RightBottomTree; + var parentNeibor = FindNeiborNode(parent, QuadTreeFindMode.Bottom); + if (parentNeibor is null || parentNeibor.RightTopTree is null) + return parentNeibor; + if (tar == parent.LeftBottomTree) + return parentNeibor.LeftTopTree; + return parentNeibor.RightTopTree; + } + case QuadTreeFindMode.Right: + { + if (tar == parent.LeftTopTree) + return parent.RightTopTree; + if (tar == parent.LeftBottomTree) + return parent.RightBottomTree; + var parentNeibor = FindNeiborNode(parent, QuadTreeFindMode.Right); + if (tar == parent.RightTopTree) + return parentNeibor?.LeftTopTree; + return parentNeibor?.LeftBottomTree; + } + case QuadTreeFindMode.Left: + { + if (tar == parent.RightTopTree) + return parent.LeftTopTree; + if (tar == parent.RightBottomTree) + return parent.LeftBottomTree; + var parentNeibor = FindNeiborNode(parent, QuadTreeFindMode.Left); + if (tar == parent.LeftTopTree) + return parentNeibor?.RightTopTree; + return parentNeibor?.RightBottomTree; + } + } + return null; + } + #endregion + + #region 改 + /// + /// 所有的点归类到最小包围它的空间 + /// + //public void PointsToMinNode() + //{ + // ForEach(node => + // { + // for (int i = 0; i < node.Contents.Count; i++) + // { + // var ent = node.Contents[i]; + // if (ent.IsPoint) + // { + // //如果最小包含!=当前,就是没有放在最适合的位置 + // var queryNode = GetMinNode(ent); + // if (queryNode != node) + // { + // node.Remove(ent); + // queryNode.Contents.Add(ent); + // } + // } + // } + // return false; + // }); + //} + #endregion + + #region 方法 + /// + /// 递归全部节点(提供给根用的,所以是全部) + /// + /// QTAction + public bool ForEach(QuadTree.QTAction action) + { + //执行本节点 + if (action(this)) + return true; + + //递归执行本节点的子节点 + var nodes = Nodes; + for (int i = 0; i < nodes.Length; i++) + { + var node = nodes[i]; + if (node is null) + continue; + if (node.ForEach(action)) + break; } - #endregion + return false; } + #endregion } \ No newline at end of file diff --git a/src/IFoxCAD.Cad/Algorithms/QuadTree/QuadTreeSelectMode.cs b/src/IFoxCAD.Cad/Algorithms/QuadTree/QuadTreeSelectMode.cs index 79cf64f..d180236 100644 --- a/src/IFoxCAD.Cad/Algorithms/QuadTree/QuadTreeSelectMode.cs +++ b/src/IFoxCAD.Cad/Algorithms/QuadTree/QuadTreeSelectMode.cs @@ -1,22 +1,21 @@ -namespace IFoxCAD.Cad +namespace IFoxCAD.Cad; + +/// +/// 四叉树选择模式 +/// +public enum QuadTreeSelectMode { - /// - /// 四叉树选择模式 - /// - public enum QuadTreeSelectMode - { - IntersectsWith, //碰撞到就选中 - Contains, //全包含才选中 - } + IntersectsWith, //碰撞到就选中 + Contains, //全包含才选中 +} - /// - /// 四叉树查找方向 - /// - public enum QuadTreeFindMode - { - Top = 1, //上 - Bottom = 2, //下 - Left = 4, //左 - Right = 8, //右 - } +/// +/// 四叉树查找方向 +/// +public enum QuadTreeFindMode +{ + Top = 1, //上 + Bottom = 2, //下 + Left = 4, //左 + Right = 8, //右 } \ No newline at end of file diff --git a/src/IFoxCAD.Cad/Algorithms/QuadTree/Utility.cs b/src/IFoxCAD.Cad/Algorithms/QuadTree/Utility.cs index 1337d82..2a0be8e 100644 --- a/src/IFoxCAD.Cad/Algorithms/QuadTree/Utility.cs +++ b/src/IFoxCAD.Cad/Algorithms/QuadTree/Utility.cs @@ -1,61 +1,60 @@ using System; -namespace IFoxCAD.Cad +namespace IFoxCAD.Cad; + +public static class Utility { - public static class Utility + /// + /// 带有随机种子的随机数 + /// 为什么这样写随机种子呢 + /// + /// + public static Random GetRandom() { - /// - /// 带有随机种子的随机数 - /// 为什么这样写随机种子呢 - /// - /// - public static Random GetRandom() - { - var tick = DateTime.Now.Ticks; + var tick = DateTime.Now.Ticks; - /* - * 知识准备: - * | 高位64位 | 低位32位 | - * Convert.ToString(int.MaxValue, 2)输出二进制 "1111111111111111111111111111111" 31个;最高位是符号位,所以少1位 - * Convert.ToString(long.MaxValue,2)输出二进制,刚好长一倍 "11111111111111111111111111111111 1111111111111111111111111111111" 63个;最高位是符号位,所以少1位 - * Convert.ToString(0xffffffffL, 2)int.MaxValue再按位多1 "1 1111111111111111111111111111111" 32个;前面的0不会打印出来 - * - * Convert.ToString(long.MaxValue>>32, 2)相当于平移高位的到低位范围,也就是上面少打印的二进制 - * 验证右移是不是高位保留,答案是 - * var a = Convert.ToInt64("101111111111111111111111111111111111111111111111111111111111111", 2); - * Convert.ToString(a >> 32,2); - * - * 解释代码: - * 0x01: - * (int)(long.MaxValue & 0xffffffffL) | (int)(long.MaxValue >> 32); - * Convert.ToString(long.MaxValue & 0xffffffffL, 2)//去掉高位:"11111111111111111111111111111111" 32个,再强转int - * 按位与&是保证符号位肯定是1,其他尽可能为0,高位被去掉只是MaxValue&0的原因,强转才是去掉高位..."尽可能"一词带来第一次随机性 - * 0x02: - * Convert.ToString((long.MaxValue >> 32), 2) //去掉低位: "1111111111111111111111111111111" 31个,再强转int - * 按位或|是尽可能为1..."尽可能"一词带来第二次随机性 - * - */ + /* + * 知识准备: + * | 高位64位 | 低位32位 | + * Convert.ToString(int.MaxValue, 2)输出二进制 "1111111111111111111111111111111" 31个;最高位是符号位,所以少1位 + * Convert.ToString(long.MaxValue,2)输出二进制,刚好长一倍 "11111111111111111111111111111111 1111111111111111111111111111111" 63个;最高位是符号位,所以少1位 + * Convert.ToString(0xffffffffL, 2)int.MaxValue再按位多1 "1 1111111111111111111111111111111" 32个;前面的0不会打印出来 + * + * Convert.ToString(long.MaxValue>>32, 2)相当于平移高位的到低位范围,也就是上面少打印的二进制 + * 验证右移是不是高位保留,答案是 + * var a = Convert.ToInt64("101111111111111111111111111111111111111111111111111111111111111", 2); + * Convert.ToString(a >> 32,2); + * + * 解释代码: + * 0x01: + * (int)(long.MaxValue & 0xffffffffL) | (int)(long.MaxValue >> 32); + * Convert.ToString(long.MaxValue & 0xffffffffL, 2)//去掉高位:"11111111111111111111111111111111" 32个,再强转int + * 按位与&是保证符号位肯定是1,其他尽可能为0,高位被去掉只是MaxValue&0的原因,强转才是去掉高位..."尽可能"一词带来第一次随机性 + * 0x02: + * Convert.ToString((long.MaxValue >> 32), 2) //去掉低位: "1111111111111111111111111111111" 31个,再强转int + * 按位或|是尽可能为1..."尽可能"一词带来第二次随机性 + * + */ - var tickSeeds = (int)(tick & 0xffffffffL) | (int)(tick >> 32); - return new Random(tickSeeds); - } + var tickSeeds = (int)(tick & 0xffffffffL) | (int)(tick >> 32); + return new Random(tickSeeds); + } - /// - /// 随机颜色 - /// - /// - public static System.Drawing.Color RandomColor + /// + /// 随机颜色 + /// + /// + public static System.Drawing.Color RandomColor + { + get { - get - { - var ran = GetRandom(); - int R = ran.Next(255); - int G = ran.Next(255); - int B = ran.Next(255); - B = (R + G > 400) ? R + G - 400 : B;//0 : 380 - R - G; - B = (B > 255) ? 255 : B; - return System.Drawing.Color.FromArgb(R, G, B); - } + var ran = GetRandom(); + int R = ran.Next(255); + int G = ran.Next(255); + int B = ran.Next(255); + B = (R + G > 400) ? R + G - 400 : B;//0 : 380 - R - G; + B = (B > 255) ? 255 : B; + return System.Drawing.Color.FromArgb(R, G, B); } } } \ No newline at end of file diff --git a/src/IFoxCAD.Cad/ExtensionMethod/BulgeVertexWidth.cs b/src/IFoxCAD.Cad/ExtensionMethod/BulgeVertexWidth.cs index c6eead5..dc7abd3 100644 --- a/src/IFoxCAD.Cad/ExtensionMethod/BulgeVertexWidth.cs +++ b/src/IFoxCAD.Cad/ExtensionMethod/BulgeVertexWidth.cs @@ -1,13 +1,4 @@ -#if !HC2020 -using Autodesk.AutoCAD.DatabaseServices; -using Autodesk.AutoCAD.Geometry; -#else -using GrxCAD.DatabaseServices; -using GrxCAD.Geometry; -#endif -using System; - -namespace IFoxCAD; +namespace IFoxCAD; /// /// 多段线的顶点,凸度,头宽,尾宽 diff --git a/src/IFoxCAD.Cad/ExtensionMethod/Curve2dEx.cs b/src/IFoxCAD.Cad/ExtensionMethod/Curve2dEx.cs index d0d35e1..51ff4e2 100644 --- a/src/IFoxCAD.Cad/ExtensionMethod/Curve2dEx.cs +++ b/src/IFoxCAD.Cad/ExtensionMethod/Curve2dEx.cs @@ -3,7 +3,6 @@ /// /// 二维解析类曲线转换为二维实体曲线扩展类 /// - public static class Curve2dEx { #region Curve2d diff --git a/src/IFoxCAD.Cad/ExtensionMethod/DBDictionaryEx.cs b/src/IFoxCAD.Cad/ExtensionMethod/DBDictionaryEx.cs index 7edebf0..2ae0879 100644 --- a/src/IFoxCAD.Cad/ExtensionMethod/DBDictionaryEx.cs +++ b/src/IFoxCAD.Cad/ExtensionMethod/DBDictionaryEx.cs @@ -1,6 +1,5 @@ -using Group = Autodesk.AutoCAD.DatabaseServices.Group; - -namespace IFoxCAD.Cad; +namespace IFoxCAD.Cad; +using Group = Autodesk.AutoCAD.DatabaseServices.Group; /// /// 字典扩展类 @@ -21,9 +20,7 @@ public static IEnumerable GetAllObjects(this DBDictionary dict, DBTrans? t { var ent = trans.GetObject(e.Value); if (ent is not null) - { yield return ent; - } } } @@ -42,9 +39,7 @@ public static IEnumerable GetAllObjects(this DBDictionary dict, DBTrans? t { ObjectId id = dict.GetAt(key); if (!id.IsNull) - { return trans.GetObject(id); - } } return null; } @@ -281,19 +276,15 @@ public static void SetValue(this DataCell cell, CellType type, object value) public static ObjectId AddGroup(this DBDictionary dict, string name, ObjectIdCollection ids) { if (dict.Contains(name)) - { return ObjectId.Null; - } - else + + using (dict.ForWrite()) { - using (dict.ForWrite()) - { - Group g = new(); - g.Append(ids); - dict.SetAt(name, g); - DBTrans.Top.Transaction.AddNewlyCreatedDBObject(g, true); - return g.ObjectId; - } + Group g = new(); + g.Append(ids); + dict.SetAt(name, g); + DBTrans.Top.Transaction.AddNewlyCreatedDBObject(g, true); + return g.ObjectId; } } @@ -306,9 +297,8 @@ public static ObjectId AddGroup(this DBDictionary dict, string name, ObjectIdCol public static ObjectId AddGroup(this DBDictionary dict, string name, IEnumerable ids) { if (dict.Contains(name)) - { return ObjectId.Null; - } + return dict.AddGroup(name, new ObjectIdCollection(ids.ToArray())); } @@ -321,10 +311,8 @@ public static ObjectId AddGroup(this DBDictionary dict, string name, IEnumerable /// 编组集合 public static IEnumerable GetGroups(this DBDictionary dict, Func func) { - return - dict - .GetAllObjects() - .Where(func); + return dict.GetAllObjects() + .Where(func); } /// @@ -334,11 +322,10 @@ public static IEnumerable GetGroups(this DBDictionary dict, Func编组集合 public static IEnumerable GetGroups(this Entity ent) { - return - ent.GetPersistentReactorIds() - .Cast() - .Select(id => id.GetObject()) - .OfType(); + return ent.GetPersistentReactorIds() + .Cast() + .Select(id => id.GetObject()) + .OfType(); } /// @@ -353,9 +340,7 @@ public static List RemoveNullGroup(this DBDictionary dict) { names.Add(g.Name); using (g.ForWrite()) - { g.Erase(); - } } return names; } @@ -376,9 +361,7 @@ public static List RemoveNullGroup(this DBDictionary dict, Func /// 实体图元类扩展函数 @@ -354,14 +353,6 @@ public static Circle CreateCircle(Point3d startPoint, Point3d endPoint) /// 裁剪多边形点表 public static void ClipBlockRef(this BlockReference bref, IEnumerable pt3ds) { - //if (bref is null) - //{ - // throw new ArgumentNullException(nameof(bref)); - //} - //if (pt3ds is null) - //{ - // throw new ArgumentNullException(nameof(pt3ds)); - //} Matrix3d mat = bref.BlockTransform.Inverse(); var pts = pt3ds @@ -384,13 +375,10 @@ public static void ClipBlockRef(this BlockReference bref, IEnumerable p /// 第二角点 public static void ClipBlockRef(this BlockReference bref, Point3d pt1, Point3d pt2) { - //if (bref is null) - //{ - // throw new ArgumentNullException(nameof(bref)); - //} Matrix3d mat = bref.BlockTransform.Inverse(); pt1 = pt1.TransformBy(mat); pt2 = pt2.TransformBy(mat); + Point2dCollection pts = new() { new Point2d(Math.Min(pt1.X, pt2.X), Math.Min(pt1.Y, pt2.Y)), @@ -410,19 +398,18 @@ public static void ClipBlockRef(this BlockReference bref, Point3d pt1, Point3d p /// /// 动态块 /// 属性值字典 - public static void ChangeBlockProperty(this BlockReference blockReference, Dictionary propertyNameValues) + public static void ChangeBlockProperty(this BlockReference blockReference, + Dictionary propertyNameValues) { if (!blockReference.IsDynamicBlock) return; + using (blockReference.ForWrite()) { foreach (DynamicBlockReferenceProperty item in blockReference.DynamicBlockReferencePropertyCollection) - { if (propertyNameValues.ContainsKey(item.PropertyName)) item.Value = propertyNameValues[item.PropertyName]; - } } } - #endregion } diff --git a/src/IFoxCAD.Cad/ExtensionMethod/GeometryEx.cs b/src/IFoxCAD.Cad/ExtensionMethod/GeometryEx.cs index e35c44f..eca3a15 100644 --- a/src/IFoxCAD.Cad/ExtensionMethod/GeometryEx.cs +++ b/src/IFoxCAD.Cad/ExtensionMethod/GeometryEx.cs @@ -1,7 +1,7 @@ namespace IFoxCAD.Cad; using System.Drawing; -using IFoxCAD.Basal; + /// /// 图形扩展类 /// diff --git a/src/IFoxCAD.Cad/ExtensionMethod/Jig.cs b/src/IFoxCAD.Cad/ExtensionMethod/Jig.cs index b2b2e9a..5485e6d 100644 --- a/src/IFoxCAD.Cad/ExtensionMethod/Jig.cs +++ b/src/IFoxCAD.Cad/ExtensionMethod/Jig.cs @@ -10,8 +10,9 @@ */ namespace IFoxCAD.Cad; + +//此命名空间容易引起Polyline等等重义,因此不放入全局空间 using Autodesk.AutoCAD.GraphicsInterface; -using Acap = Application; public delegate void WorldDrawEvent(WorldDraw draw); public class JigEx : DrawJig diff --git a/src/IFoxCAD.Cad/ExtensionMethod/Tools.cs b/src/IFoxCAD.Cad/ExtensionMethod/Tools.cs index 0bff2cf..0e875c5 100644 --- a/src/IFoxCAD.Cad/ExtensionMethod/Tools.cs +++ b/src/IFoxCAD.Cad/ExtensionMethod/Tools.cs @@ -1,189 +1,188 @@ -namespace IFoxCAD.Cad +namespace IFoxCAD.Cad; + +public static class Tools { - public static class Tools + public static void TestTimes2(int count, string message, Action action) { - public static void TestTimes2(int count, string message, Action action) + System.Diagnostics.Stopwatch watch = new(); + watch.Start(); //开始监视代码运行时间 + for (int i = 0; i < count; i++) + action.Invoke();//需要测试的代码 + watch.Stop(); //停止监视 + TimeSpan timespan = watch.Elapsed; //获取当前实例测量得出的总时间 + double time = timespan.TotalMilliseconds; + string name = "毫秒"; + if (timespan.TotalMilliseconds > 1000) { - System.Diagnostics.Stopwatch watch = new(); - watch.Start(); //开始监视代码运行时间 - for (int i = 0; i < count; i++) - action.Invoke();//需要测试的代码 - watch.Stop(); //停止监视 - TimeSpan timespan = watch.Elapsed; //获取当前实例测量得出的总时间 - double time = timespan.TotalMilliseconds; - string name = "毫秒"; - if (timespan.TotalMilliseconds > 1000) - { - time = timespan.TotalSeconds; - name = "秒"; - } - Env.Print($"{message} 代码执行 {count} 次的时间:{time} ({name})"); //总毫秒数 + time = timespan.TotalSeconds; + name = "秒"; } + Env.Print($"{message} 代码执行 {count} 次的时间:{time} ({name})"); //总毫秒数 + } - /// - /// 纳秒计时器 - /// - public static void TestTimes(int count, string message, Action action, - Timer.TimeEnum timeEnum = Timer.TimeEnum.Millisecond) - { - double time = Timer.RunTime(() => { - for (int i = 0; i < count; i++) - action(); - }, timeEnum); - - string timeNameZn = ""; - switch (timeEnum) - { - case Timer.TimeEnum.Second: - timeNameZn = " 秒"; - break; - case Timer.TimeEnum.Millisecond: - timeNameZn = " 毫秒"; - break; - case Timer.TimeEnum.Microsecond: - timeNameZn = " 微秒"; - break; - case Timer.TimeEnum.Nanosecond: - timeNameZn = " 纳秒"; - break; - } + /// + /// 纳秒计时器 + /// + public static void TestTimes(int count, string message, Action action, + Timer.TimeEnum timeEnum = Timer.TimeEnum.Millisecond) + { + double time = Timer.RunTime(() => { + for (int i = 0; i < count; i++) + action(); + }, timeEnum); - Env.Print($"{message} 代码执行 {count} 次的时间:{time} ({timeNameZn})"); + string timeNameZn = ""; + switch (timeEnum) + { + case Timer.TimeEnum.Second: + timeNameZn = " 秒"; + break; + case Timer.TimeEnum.Millisecond: + timeNameZn = " 毫秒"; + break; + case Timer.TimeEnum.Microsecond: + timeNameZn = " 微秒"; + break; + case Timer.TimeEnum.Nanosecond: + timeNameZn = " 纳秒"; + break; } + + Env.Print($"{message} 代码执行 {count} 次的时间:{time} ({timeNameZn})"); } +} - /* - //测试例子,同时验证两个计时器 - var stopwatch = new Stopwatch(); - Timer.RunTime(() => { +/* +//测试例子,同时验证两个计时器 +var stopwatch = new Stopwatch(); +Timer.RunTime(() => { - stopwatch.Start(); - for (int i = 0; i < 10000000; i++) - i++; - stopwatch.Stop(); + stopwatch.Start(); + for (int i = 0; i < 10000000; i++) + i++; + stopwatch.Stop(); - }, Timer.TimeEnum.Millisecond, "运行:"); - Console.WriteLine("运行毫秒:" + stopwatch.ElapsedMilliseconds); - */ +}, Timer.TimeEnum.Millisecond, "运行:"); +Console.WriteLine("运行毫秒:" + stopwatch.ElapsedMilliseconds); + */ - public class Timer +public class Timer +{ + [Flags] + public enum TimeEnum { - [Flags] - public enum TimeEnum - { - /// - /// 秒 - /// - Second, - /// - /// 毫秒 - /// - Millisecond, - /// - /// 微秒 - /// - Microsecond, - /// - /// 纳秒 - /// - Nanosecond, - } - - [DllImport("Kernel32.dll")] - static extern bool QueryPerformanceCounter(out long lpPerformanceCount); - /// - /// 这个函数会检索性能计数器的频率. - /// 性能计数器的频率在系统启动时是固定的,并且在所有处理器上都是一致的 - /// 因此,只需在应用初始化时查询频率,即可缓存结果 - /// 在运行 Windows XP 或更高版本的系统上,该函数将始终成功,因此永远不会返回零 + /// 秒 + /// + Second, + /// + /// 毫秒 + /// + Millisecond, + /// + /// 微秒 /// - /// - /// - [DllImport("Kernel32.dll")] - static extern bool QueryPerformanceFrequency(out long lpFrequency); + Microsecond, + /// + /// 纳秒 + /// + Nanosecond, + } - long _startTime, _stopTime; - long _freq; + [DllImport("Kernel32.dll")] + static extern bool QueryPerformanceCounter(out long lpPerformanceCount); - public Timer() - { - _startTime = 0; - _stopTime = 0; + /// + /// 这个函数会检索性能计数器的频率. + /// 性能计数器的频率在系统启动时是固定的,并且在所有处理器上都是一致的 + /// 因此,只需在应用初始化时查询频率,即可缓存结果 + /// 在运行 Windows XP 或更高版本的系统上,该函数将始终成功,因此永远不会返回零 + /// + /// + /// + [DllImport("Kernel32.dll")] + static extern bool QueryPerformanceFrequency(out long lpFrequency); - if (!QueryPerformanceFrequency(out _freq)) - throw new Win32Exception("不支持高性能计数器"); - } + long _startTime, _stopTime; + long _freq; - /// - /// 开始计时器 - /// - public void Start() - { - System.Threading.Thread.Sleep(0); - QueryPerformanceCounter(out _startTime); - } + public Timer() + { + _startTime = 0; + _stopTime = 0; - /// - /// 停止计时器 - /// - public void Stop() - { - QueryPerformanceCounter(out _stopTime); - _Second = (double)(_stopTime - _startTime) / _freq; - } - double _Second = 0; + if (!QueryPerformanceFrequency(out _freq)) + throw new Win32Exception("不支持高性能计数器"); + } - // 返回计时器经过时间 - public double Second => _Second; - public double Millisecond => _Second * 1000.0; - public double Microsecond => _Second * 1000000.0; - public double Nanosecond => _Second * 1000000000.0; + /// + /// 开始计时器 + /// + public void Start() + { + System.Threading.Thread.Sleep(0); + QueryPerformanceCounter(out _startTime); + } - public static double RunTime(Action action, TimeEnum timeEnum = TimeEnum.Millisecond, string? msg = null) - { - var nanoSecond = new Timer(); - nanoSecond.Start(); - action(); - nanoSecond.Stop(); + /// + /// 停止计时器 + /// + public void Stop() + { + QueryPerformanceCounter(out _stopTime); + _Second = (double)(_stopTime - _startTime) / _freq; + } + double _Second = 0; + + // 返回计时器经过时间 + public double Second => _Second; + public double Millisecond => _Second * 1000.0; + public double Microsecond => _Second * 1000000.0; + public double Nanosecond => _Second * 1000000000.0; - double time = 0; + public static double RunTime(Action action, TimeEnum timeEnum = TimeEnum.Millisecond, string? msg = null) + { + var nanoSecond = new Timer(); + nanoSecond.Start(); + action(); + nanoSecond.Stop(); + + double time = 0; + switch (timeEnum) + { + case TimeEnum.Second: + time = nanoSecond.Second; + break; + case TimeEnum.Millisecond: + time = nanoSecond.Millisecond; + break; + case TimeEnum.Microsecond: + time = nanoSecond.Microsecond; + break; + case TimeEnum.Nanosecond: + time = nanoSecond.Nanosecond; + break; + } + if (msg != null) + { + string timeNameZn = ""; switch (timeEnum) { case TimeEnum.Second: - time = nanoSecond.Second; + timeNameZn = " 秒"; break; case TimeEnum.Millisecond: - time = nanoSecond.Millisecond; + timeNameZn = " 毫秒"; break; case TimeEnum.Microsecond: - time = nanoSecond.Microsecond; + timeNameZn = " 微秒"; break; case TimeEnum.Nanosecond: - time = nanoSecond.Nanosecond; + timeNameZn = " 纳秒"; break; } - if (msg != null) - { - string timeNameZn = ""; - switch (timeEnum) - { - case TimeEnum.Second: - timeNameZn = " 秒"; - break; - case TimeEnum.Millisecond: - timeNameZn = " 毫秒"; - break; - case TimeEnum.Microsecond: - timeNameZn = " 微秒"; - break; - case TimeEnum.Nanosecond: - timeNameZn = " 纳秒"; - break; - } - Env.Print(msg + " " + time + timeNameZn); - } - return time; + Env.Print(msg + " " + time + timeNameZn); } + return time; } } \ No newline at end of file diff --git "a/src/IFoxCAD.Cad/ExtensionMethod/\346\226\260\345\273\272\345\241\253\345\205\205/HatchConverter.cs" "b/src/IFoxCAD.Cad/ExtensionMethod/\346\226\260\345\273\272\345\241\253\345\205\205/HatchConverter.cs" index b58861e..7868bee 100644 --- "a/src/IFoxCAD.Cad/ExtensionMethod/\346\226\260\345\273\272\345\241\253\345\205\205/HatchConverter.cs" +++ "b/src/IFoxCAD.Cad/ExtensionMethod/\346\226\260\345\273\272\345\241\253\345\205\205/HatchConverter.cs" @@ -1,10 +1,4 @@ namespace IFoxCAD.Cad; -using Autodesk.AutoCAD.DatabaseServices; -using Autodesk.AutoCAD.Geometry; -using Autodesk.AutoCAD.Colors; -using System.Collections.Generic; -using System; - using PointV = Point2d; /// diff --git "a/src/IFoxCAD.Cad/ExtensionMethod/\346\226\260\345\273\272\345\241\253\345\205\205/HatchEx.cs" "b/src/IFoxCAD.Cad/ExtensionMethod/\346\226\260\345\273\272\345\241\253\345\205\205/HatchEx.cs" index a6c7fa0..8bfd043 100644 --- "a/src/IFoxCAD.Cad/ExtensionMethod/\346\226\260\345\273\272\345\241\253\345\205\205/HatchEx.cs" +++ "b/src/IFoxCAD.Cad/ExtensionMethod/\346\226\260\345\273\272\345\241\253\345\205\205/HatchEx.cs" @@ -1,9 +1,4 @@ -namespace IFoxCAD.Cad; -using Autodesk.AutoCAD.DatabaseServices; -using Autodesk.AutoCAD.Geometry; -using Acap = Autodesk.AutoCAD.ApplicationServices.Application; -using System; -using System.Collections.Generic; +namespace IFoxCAD.Cad; public static class HatchEx { diff --git "a/src/IFoxCAD.Cad/ExtensionMethod/\346\226\260\345\273\272\345\241\253\345\205\205/HatchInfo.cs" "b/src/IFoxCAD.Cad/ExtensionMethod/\346\226\260\345\273\272\345\241\253\345\205\205/HatchInfo.cs" index 5f79306..1d66417 100644 --- "a/src/IFoxCAD.Cad/ExtensionMethod/\346\226\260\345\273\272\345\241\253\345\205\205/HatchInfo.cs" +++ "b/src/IFoxCAD.Cad/ExtensionMethod/\346\226\260\345\273\272\345\241\253\345\205\205/HatchInfo.cs" @@ -1,9 +1,5 @@ namespace IFoxCAD.Cad; -using Autodesk.AutoCAD.DatabaseServices; -using Autodesk.AutoCAD.Geometry; -using Autodesk.AutoCAD.Colors; -using System.Collections.Generic; -using System; + /* * ӵĵһ߽߽,ڶͼı߽硣 * Ҫⲿ߽,ʹӻΪ HatchLoopTypes.Outermost AppendLoop , diff --git a/src/IFoxCAD.Cad/GlobalUsings.cs b/src/IFoxCAD.Cad/GlobalUsings.cs index 4ca7903..7020dbe 100644 --- a/src/IFoxCAD.Cad/GlobalUsings.cs +++ b/src/IFoxCAD.Cad/GlobalUsings.cs @@ -18,6 +18,9 @@ global using Autodesk.AutoCAD.DatabaseServices; global using Autodesk.AutoCAD.Geometry; global using Autodesk.AutoCAD.Runtime; +global using Acap = Autodesk.AutoCAD.ApplicationServices.Application; +global using Autodesk.AutoCAD.DatabaseServices.Filters; + /// ifoxcad.basal 引用 global using IFoxCAD.Basal; \ No newline at end of file diff --git a/src/IFoxCAD.Cad/ResultData/LispDottedPair.cs b/src/IFoxCAD.Cad/ResultData/LispDottedPair.cs index 97953fc..009d13e 100644 --- a/src/IFoxCAD.Cad/ResultData/LispDottedPair.cs +++ b/src/IFoxCAD.Cad/ResultData/LispDottedPair.cs @@ -1,7 +1,7 @@ namespace IFoxCAD.Cad; /// -/// lisp 点对表的数据封装类 +/// lisp点对表的数据封装类 /// public class LispDottedPair : LispList { @@ -32,6 +32,7 @@ public LispDottedPair(TypedValue left, TypedValue right) } #endregion + #region 重写 /// /// 点对表的值 /// @@ -47,7 +48,8 @@ public override List Value value.InsertRange(1, this); return value; } - } + } + #endregion #region 转换器 diff --git a/src/IFoxCAD.Cad/ResultData/LispList.cs b/src/IFoxCAD.Cad/ResultData/LispList.cs index 1af143a..8c72669 100644 --- a/src/IFoxCAD.Cad/ResultData/LispList.cs +++ b/src/IFoxCAD.Cad/ResultData/LispList.cs @@ -17,6 +17,8 @@ public LispList() { } /// TypedValue 迭代器 public LispList(IEnumerable values) : base(values) { } #endregion + + #region 重写 /// /// lisp 列表的值 /// @@ -32,7 +34,8 @@ public virtual List Value value.InsertRange(1, this); return value; } - } + } + #endregion #region 添加数据 /// diff --git a/src/IFoxCAD.Cad/ResultData/TypedValueList.cs b/src/IFoxCAD.Cad/ResultData/TypedValueList.cs index 2b424c2..896a226 100644 --- a/src/IFoxCAD.Cad/ResultData/TypedValueList.cs +++ b/src/IFoxCAD.Cad/ResultData/TypedValueList.cs @@ -15,7 +15,6 @@ public TypedValueList() { } /// /// public TypedValueList(IEnumerable values) : base(values) { } - #endregion #region 添加数据 diff --git a/src/IFoxCAD.Cad/ResultData/XRecordDataList.cs b/src/IFoxCAD.Cad/ResultData/XRecordDataList.cs index a1dc36f..1fb19d8 100644 --- a/src/IFoxCAD.Cad/ResultData/XRecordDataList.cs +++ b/src/IFoxCAD.Cad/ResultData/XRecordDataList.cs @@ -5,10 +5,18 @@ /// public class XRecordDataList : TypedValueList { - public XRecordDataList() - { - } + #region 构造函数 + /// + /// 扩展字典数据封装类 + /// + public XRecordDataList() { } + + /// + /// 扩展字典数据封装类 + /// public XRecordDataList(IEnumerable values) : base(values) { } + #endregion + #region 添加数据 /// /// 添加数据 diff --git a/src/IFoxCAD.Cad/ResultData/XdataList.cs b/src/IFoxCAD.Cad/ResultData/XdataList.cs index 6d44ee7..a666100 100644 --- a/src/IFoxCAD.Cad/ResultData/XdataList.cs +++ b/src/IFoxCAD.Cad/ResultData/XdataList.cs @@ -5,11 +5,17 @@ /// public class XDataList : TypedValueList { - public XDataList() - { - } + #region 构造函数 + /// + /// 扩展数据封装类 + /// + public XDataList() { } + /// + /// 扩展数据封装类 + /// public XDataList(IEnumerable values) : base(values) { } + #endregion #region 添加数据 /// @@ -20,9 +26,8 @@ public XDataList(IEnumerable values) : base(values) { } public override void Add(int code, object obj) { if (code < 1000 || code > 1071) - { - throw new System.Exception("传入的组码值不是XData有效范围!"); - } + throw new System.Exception("传入的组码值不是XData有效范围!"); + Add(new TypedValue(code, obj)); } @@ -33,7 +38,6 @@ public override void Add(int code, object obj) /// 组码值 public void Add(DxfCode code, object obj) { - Add((int)code, obj); } diff --git a/src/IFoxCAD.Cad/Runtime/CadVersion.cs b/src/IFoxCAD.Cad/Runtime/CadVersion.cs index aeec9e2..2e32c74 100644 --- a/src/IFoxCAD.Cad/Runtime/CadVersion.cs +++ b/src/IFoxCAD.Cad/Runtime/CadVersion.cs @@ -4,7 +4,6 @@ public class CadVersion { - /// /// 主版本 /// diff --git a/src/IFoxCAD.Cad/Runtime/Env.cs b/src/IFoxCAD.Cad/Runtime/Env.cs index 7cd9c20..e5d34dc 100644 --- a/src/IFoxCAD.Cad/Runtime/Env.cs +++ b/src/IFoxCAD.Cad/Runtime/Env.cs @@ -1,7 +1,8 @@ -using Autodesk.AutoCAD.GraphicsSystem; - namespace IFoxCAD.Cad; +//此命名空间容易引起Polyline等等重义,因此不放入全局空间 +using Autodesk.AutoCAD.GraphicsSystem; + /// /// 系统管理类 /// 封装了一些系统 osmode、cmdecho、dimblk 系统变量 diff --git a/src/IFoxCAD.Cad/Runtime/Log.cs b/src/IFoxCAD.Cad/Runtime/Log.cs index 5f50a51..02293e2 100644 --- a/src/IFoxCAD.Cad/Runtime/Log.cs +++ b/src/IFoxCAD.Cad/Runtime/Log.cs @@ -2,8 +2,6 @@ using System; using System.Diagnostics; -using System.IO; -using System.Text; using System.Threading; #region 写入日志到不同的环境中 @@ -124,7 +122,7 @@ public override void WriteLog(string? message) } catch (System.Security.SecurityException e) { - throw new Exception("您没有权限写入win日志中"); + throw new Exception("您没有权限写入win日志中" + e.Message); } #endif } diff --git a/src/IFoxCAD.Cad/Runtime/SymbolTable.cs b/src/IFoxCAD.Cad/Runtime/SymbolTable.cs index b49557d..00bba7b 100644 --- a/src/IFoxCAD.Cad/Runtime/SymbolTable.cs +++ b/src/IFoxCAD.Cad/Runtime/SymbolTable.cs @@ -49,9 +49,7 @@ public ObjectId this[string key] get { if (Has(key)) - { return CurrentSymbolTable[key]; - } return ObjectId.Null; } } @@ -104,17 +102,15 @@ public ObjectId Add(string name, Action? action = null) { ObjectId id = this[name]; if (id.IsNull) + return id; + + var record = new TRecord() { - var record = new TRecord() - { - Name = name - }; - id = Add(record); - using (record.ForWrite()) - { - action?.Invoke(record); - } - } + Name = name + }; + id = Add(record); + using (record.ForWrite()) + action?.Invoke(record); return id; } #endregion @@ -127,10 +123,9 @@ public ObjectId Add(string name, Action? action = null) private static void Remove(TRecord record) { using (record.ForWrite()) - { record.Erase(); - } } + /// /// 删除符号表记录 /// @@ -139,11 +134,9 @@ public void Remove(string name) { var record = GetRecord(name); if (record is not null) - { Remove(record); - } - } + /// /// 删除符号表记录 /// @@ -152,12 +145,8 @@ public void Remove(ObjectId id) { var record = GetRecord(id); if (record is not null) - { Remove(record); - } } - - #endregion #region 修改符号表记录 @@ -317,9 +306,7 @@ public void ForEach(Action action, OpenMode openMode = OpenMode.ForRead { var record = GetRecord(item, openMode); if (record is not null) - { action(record); - } } } #endregion @@ -328,11 +315,8 @@ public void ForEach(Action action, OpenMode openMode = OpenMode.ForRead public IEnumerator GetEnumerator() { - foreach (var id in CurrentSymbolTable) - { yield return id; - } } IEnumerator IEnumerable.GetEnumerator() diff --git a/tests/Test/GlobalUsings.cs b/tests/Test/GlobalUsings.cs index cc64ff0..3940f31 100644 --- a/tests/Test/GlobalUsings.cs +++ b/tests/Test/GlobalUsings.cs @@ -19,6 +19,7 @@ global using Autodesk.AutoCAD.Geometry; global using Autodesk.AutoCAD.Runtime; global using acgi = Autodesk.AutoCAD.GraphicsInterface; +global using acap = Autodesk.AutoCAD.ApplicationServices.Application; /// ifoxcad global using IFoxCAD.Cad; diff --git a/tests/Test/TestDBTrans.cs b/tests/Test/TestDBTrans.cs index 2f14107..230882e 100644 --- a/tests/Test/TestDBTrans.cs +++ b/tests/Test/TestDBTrans.cs @@ -1,4 +1,5 @@ namespace Test; + public class TestTrans { [CommandMethod("testtr")] -- Gitee From 5a3a2ce16cbedb7637dd7dc8fe3d4a5e0aaa4a83 Mon Sep 17 00:00:00 2001 From: liuqihong <540762622@qq.com> Date: Wed, 10 Aug 2022 21:34:38 +0800 Subject: [PATCH 337/675] =?UTF-8?q?=E5=A2=9E=E5=8A=A0=E6=96=87=E5=AD=97?= =?UTF-8?q?=E5=81=8F=E7=A7=BB=E4=BE=8B=E5=AD=90=E5=92=8C=E5=A4=84=E7=90=86?= =?UTF-8?q?=E6=96=B9=E6=A1=88?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/IFoxCAD.Cad/ExtensionMethod/DatabaseEx.cs | 18 ++++++++ src/IFoxCAD.Cad/Runtime/DBTrans.cs | 5 ++- tests/Test/TestMirrorFile.cs | 43 +++++++++++++++++-- 3 files changed, 61 insertions(+), 5 deletions(-) create mode 100644 src/IFoxCAD.Cad/ExtensionMethod/DatabaseEx.cs diff --git a/src/IFoxCAD.Cad/ExtensionMethod/DatabaseEx.cs b/src/IFoxCAD.Cad/ExtensionMethod/DatabaseEx.cs new file mode 100644 index 0000000..a7dbddc --- /dev/null +++ b/src/IFoxCAD.Cad/ExtensionMethod/DatabaseEx.cs @@ -0,0 +1,18 @@ +public static class DatabaseEx +{ + /// + /// 后台开图文字偏移处理 + /// 0x01 此方案利用前台数据库进行处理 + /// 0x02 无时,不能使用 + /// 0x03 例如写了一个exe发送后台处理代码 + /// + /// 后台打开的数据库 + /// 处理后台的任务 + public static void DBTextDeviation(this Database backstageOpenDwg, Action action) + { + var wdb = HostApplicationServices.WorkingDatabase; + HostApplicationServices.WorkingDatabase = backstageOpenDwg; + action?.Invoke(); + HostApplicationServices.WorkingDatabase = wdb; + } +} \ No newline at end of file diff --git a/src/IFoxCAD.Cad/Runtime/DBTrans.cs b/src/IFoxCAD.Cad/Runtime/DBTrans.cs index be6a443..087c665 100644 --- a/src/IFoxCAD.Cad/Runtime/DBTrans.cs +++ b/src/IFoxCAD.Cad/Runtime/DBTrans.cs @@ -118,7 +118,9 @@ public DBTrans(Database database, bool commit = true) /// /// 要打开的文件名 /// 事务是否提交 - public DBTrans(string fileName, bool commit = true, FileOpenMode openMode = FileOpenMode.OpenForReadAndWriteNoShare) + public DBTrans(string fileName, + bool commit = true, + FileOpenMode openMode = FileOpenMode.OpenForReadAndWriteNoShare) { #if NET35 if (string.IsNullOrEmpty(fileName?.Trim())) @@ -351,7 +353,6 @@ public void SaveDwgFile(DwgVersion version = DwgVersion.AC1800) } #endregion - #region idispose接口相关函数 /// /// 取消事务 diff --git a/tests/Test/TestMirrorFile.cs b/tests/Test/TestMirrorFile.cs index 6736570..8e9eed6 100644 --- a/tests/Test/TestMirrorFile.cs +++ b/tests/Test/TestMirrorFile.cs @@ -1,5 +1,8 @@ public class MirrorFile { + const string file = "D:/JX.dwg"; + const string fileSave = "D:/JX222.dwg"; + /// /// 测试:后台打开图纸,镜像文字是否存在文字偏移 /// 答案:不存在 @@ -7,9 +10,6 @@ [CommandMethod("CmdTest_MirrorFile")] public static void CmdTest_MirrorFile() { - const string file = "D:/JX.dwg"; - const string fileSave = "D:/JX222.dwg"; - using var tr = new DBTrans(file, openMode: FileOpenMode.OpenForReadAndReadShare); tr.BlockTable.Change(tr.ModelSpace.ObjectId, ms => { @@ -30,4 +30,41 @@ public static void CmdTest_MirrorFile() }); tr.Database.SaveAs(fileSave, DwgVersion.AC1021/*AC1021 AutoCAD 2007/2008/2009.*/); } + + + [CommandMethod("CmdTest_MirrorFile2")] + public static void CmdTest_MirrorFile2() + { + using var tr = new DBTrans(file, openMode: FileOpenMode.OpenForReadAndReadShare); + + tr.Database.DBTextDeviation(() => { + tr.BlockTable.Change(tr.ModelSpace.ObjectId, ms => { + foreach (ObjectId entId in ms) + { + var entity = tr.GetObject(entId, OpenMode.ForWrite)!; + if (entity is DBText text) + { + text.Mirror(Point3d.Origin, new Point3d(0, 1, 0)); + text.IsMirroredInX = true; //这句将导致文字偏移 + + if (text.VerticalMode == TextVerticalMode.TextBase) + text.VerticalMode = TextVerticalMode.TextBottom; + + text.HorizontalMode = text.HorizontalMode switch + { + TextHorizontalMode.TextLeft => TextHorizontalMode.TextRight, + TextHorizontalMode.TextRight => TextHorizontalMode.TextLeft, + _ => text.HorizontalMode + }; + //Point3d pos = text.GeometricExtents.MidMidPoint(); + //text.Mirror(pos, pos.Polar(text.Rotation+PI/2, 100)); + text.AdjustAlignment(tr.Database); + continue; + } + entity.Mirror(Point3d.Origin, new Point3d(0, 1, 0)); + } + }); + }); + tr.Database.SaveAs(fileSave, DwgVersion.AC1021/*AC1021 AutoCAD 2007/2008/2009.*/); + } } \ No newline at end of file -- Gitee From 6cd85c3d838f1af174e3e484b00c563329b024a4 Mon Sep 17 00:00:00 2001 From: liuqihong <540762622@qq.com> Date: Thu, 11 Aug 2022 10:28:45 +0800 Subject: [PATCH 338/675] =?UTF-8?q?=E4=BC=98=E5=8C=96=E6=B3=A8=E9=87=8A?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/IFoxCAD.Cad/ExtensionMethod/DatabaseEx.cs | 17 +++++++++++------ 1 file changed, 11 insertions(+), 6 deletions(-) diff --git a/src/IFoxCAD.Cad/ExtensionMethod/DatabaseEx.cs b/src/IFoxCAD.Cad/ExtensionMethod/DatabaseEx.cs index a7dbddc..dff7a9e 100644 --- a/src/IFoxCAD.Cad/ExtensionMethod/DatabaseEx.cs +++ b/src/IFoxCAD.Cad/ExtensionMethod/DatabaseEx.cs @@ -1,18 +1,23 @@ -public static class DatabaseEx +namespace IFoxCAD.Cad; + +public static class DatabaseEx { /// /// 后台开图文字偏移处理 /// 0x01 此方案利用前台数据库进行处理 - /// 0x02 无时,不能使用 - /// 0x03 例如写了一个exe发送后台处理代码 + /// 0x02 当关闭所有前台文档时,会出现无时,不能使用(惊惊没有测试过此状态) + /// 0x03 当关闭所有前台文档时,如何发送命令呢?那就是利用跨进程通讯 /// /// 后台打开的数据库 /// 处理后台的任务 public static void DBTextDeviation(this Database backstageOpenDwg, Action action) { var wdb = HostApplicationServices.WorkingDatabase; - HostApplicationServices.WorkingDatabase = backstageOpenDwg; - action?.Invoke(); - HostApplicationServices.WorkingDatabase = wdb; + if (wdb != null) + { + HostApplicationServices.WorkingDatabase = backstageOpenDwg; + action?.Invoke(); + HostApplicationServices.WorkingDatabase = wdb; + } } } \ No newline at end of file -- Gitee From 1079f4097d16dd527883cea7ff8856233775bcd0 Mon Sep 17 00:00:00 2001 From: liuqihong <540762622@qq.com> Date: Thu, 11 Aug 2022 10:28:54 +0800 Subject: [PATCH 339/675] =?UTF-8?q?=E4=BC=98=E5=8C=96=E6=A0=BC=E5=BC=8F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/IFoxCAD.Basal/CLS/Index.cs | 4 +- src/IFoxCAD.Basal/CLS/Range.cs | 3 +- src/IFoxCAD.Basal/CLS/RuntimeHelpers.cs | 53 +++++++------ .../CLS/TupleElementNamesAttribute.cs | 77 +++++++++---------- src/IFoxCAD.Cad/IFoxCAD.Cad.csproj | 12 +-- 5 files changed, 69 insertions(+), 80 deletions(-) diff --git a/src/IFoxCAD.Basal/CLS/Index.cs b/src/IFoxCAD.Basal/CLS/Index.cs index 391e472..97b51e5 100644 --- a/src/IFoxCAD.Basal/CLS/Index.cs +++ b/src/IFoxCAD.Basal/CLS/Index.cs @@ -2,10 +2,10 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. -using System.Runtime.CompilerServices; - namespace System; +using System.Runtime.CompilerServices; + /// Represent a type can be used to index a collection either from the start or the end. /// /// Index is used by the C# compiler to support the new index syntax diff --git a/src/IFoxCAD.Basal/CLS/Range.cs b/src/IFoxCAD.Basal/CLS/Range.cs index d046fe3..221167b 100644 --- a/src/IFoxCAD.Basal/CLS/Range.cs +++ b/src/IFoxCAD.Basal/CLS/Range.cs @@ -2,9 +2,10 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. +namespace System; + using System.Runtime.CompilerServices; -namespace System; /// Represent a range has start and end indexes. /// diff --git a/src/IFoxCAD.Basal/CLS/RuntimeHelpers.cs b/src/IFoxCAD.Basal/CLS/RuntimeHelpers.cs index 0c2692a..b5698b4 100644 --- a/src/IFoxCAD.Basal/CLS/RuntimeHelpers.cs +++ b/src/IFoxCAD.Basal/CLS/RuntimeHelpers.cs @@ -3,40 +3,39 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. -namespace System.Runtime.CompilerServices +namespace System.Runtime.CompilerServices; + +public static class RuntimeHelpers { - public static class RuntimeHelpers + /// + /// Slices the specified array using the specified range. + /// + public static T[] GetSubArray(T[] array!!, Range range) { - /// - /// Slices the specified array using the specified range. - /// - public static T[] GetSubArray(T[] array!!, Range range) - { - (int offset, int length) = range.GetOffsetAndLength(array.Length); - - if (default(T)! != null || typeof(T[]) == array.GetType()) // TODO-NULLABLE: default(T) == null warning (https://github.com/dotnet/roslyn/issues/34757) - { - // We know the type of the array to be exactly T[]. - if (length == 0) - { - //return Array.Empty(); - return new T[0]; - } + (int offset, int length) = range.GetOffsetAndLength(array.Length); - var dest = new T[length]; - Array.Copy(array, offset, dest, 0, length); - return dest; - } - else + if (default(T)! != null || typeof(T[]) == array.GetType()) // TODO-NULLABLE: default(T) == null warning (https://github.com/dotnet/roslyn/issues/34757) + { + // We know the type of the array to be exactly T[]. + if (length == 0) { - // The array is actually a U[] where U:T. - T[] dest = (T[])Array.CreateInstance(array.GetType().GetElementType()!, length); - Array.Copy(array, offset, dest, 0, length); - return dest; + //return Array.Empty(); + return new T[0]; } + + var dest = new T[length]; + Array.Copy(array, offset, dest, 0, length); + return dest; + } + else + { + // The array is actually a U[] where U:T. + T[] dest = (T[])Array.CreateInstance(array.GetType().GetElementType()!, length); + Array.Copy(array, offset, dest, 0, length); + return dest; } + } - } } //#endif \ No newline at end of file diff --git a/src/IFoxCAD.Basal/CLS/TupleElementNamesAttribute.cs b/src/IFoxCAD.Basal/CLS/TupleElementNamesAttribute.cs index 60ac337..bb0716d 100644 --- a/src/IFoxCAD.Basal/CLS/TupleElementNamesAttribute.cs +++ b/src/IFoxCAD.Basal/CLS/TupleElementNamesAttribute.cs @@ -2,48 +2,47 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. -namespace System.Runtime.CompilerServices +namespace System.Runtime.CompilerServices; + +/// +/// Indicates that the use of on a member is meant to be treated as a tuple with element names. +/// +[AttributeUsage(AttributeTargets.Field | AttributeTargets.Parameter | AttributeTargets.Property | AttributeTargets.ReturnValue | AttributeTargets.Class | AttributeTargets.Struct | AttributeTargets.Event)] +public sealed class TupleElementNamesAttribute : Attribute { + private readonly string[] _transformNames; + /// - /// Indicates that the use of on a member is meant to be treated as a tuple with element names. + /// Initializes a new instance of the class. /// - [AttributeUsage(AttributeTargets.Field | AttributeTargets.Parameter | AttributeTargets.Property | AttributeTargets.ReturnValue | AttributeTargets.Class | AttributeTargets.Struct | AttributeTargets.Event)] - public sealed class TupleElementNamesAttribute : Attribute + /// + /// Specifies, in a pre-order depth-first traversal of a type's + /// construction, which occurrences are + /// meant to carry element names. + /// + /// + /// This constructor is meant to be used on types that contain an + /// instantiation of that contains + /// element names. For instance, if C is a generic type with + /// two type parameters, then a use of the constructed type C{, might be intended to + /// treat the first type argument as a tuple with element names and the + /// second as a tuple without element names. In which case, the + /// appropriate attribute specification should use a + /// transformNames value of { "name1", "name2", null, null, + /// null }. + /// + public TupleElementNamesAttribute(string[] transformNames!!) { - private readonly string[] _transformNames; - - /// - /// Initializes a new instance of the class. - /// - /// - /// Specifies, in a pre-order depth-first traversal of a type's - /// construction, which occurrences are - /// meant to carry element names. - /// - /// - /// This constructor is meant to be used on types that contain an - /// instantiation of that contains - /// element names. For instance, if C is a generic type with - /// two type parameters, then a use of the constructed type C{, might be intended to - /// treat the first type argument as a tuple with element names and the - /// second as a tuple without element names. In which case, the - /// appropriate attribute specification should use a - /// transformNames value of { "name1", "name2", null, null, - /// null }. - /// - public TupleElementNamesAttribute(string[] transformNames!!) - { - _transformNames = transformNames; - } - - /// - /// Specifies, in a pre-order depth-first traversal of a type's - /// construction, which elements are - /// meant to carry element names. - /// - public IList TransformNames => _transformNames; + _transformNames = transformNames; } + + /// + /// Specifies, in a pre-order depth-first traversal of a type's + /// construction, which elements are + /// meant to carry element names. + /// + public IList TransformNames => _transformNames; } \ No newline at end of file diff --git a/src/IFoxCAD.Cad/IFoxCAD.Cad.csproj b/src/IFoxCAD.Cad/IFoxCAD.Cad.csproj index 9669ab5..36a58ac 100644 --- a/src/IFoxCAD.Cad/IFoxCAD.Cad.csproj +++ b/src/IFoxCAD.Cad/IFoxCAD.Cad.csproj @@ -82,17 +82,7 @@ 1701;1702;CS1685 - - - - - - - - - - - + True -- Gitee From 6fc5f4abe1bc4378b368577e7e50ac0431f3b383 Mon Sep 17 00:00:00 2001 From: liuqihong <540762622@qq.com> Date: Thu, 11 Aug 2022 11:03:42 +0800 Subject: [PATCH 340/675] =?UTF-8?q?=E4=BC=98=E5=8C=96=E6=80=A7=E8=83=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/IFoxCAD.Cad/ExtensionMethod/CurveEx.cs | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/src/IFoxCAD.Cad/ExtensionMethod/CurveEx.cs b/src/IFoxCAD.Cad/ExtensionMethod/CurveEx.cs index d01c6c0..4056bc5 100644 --- a/src/IFoxCAD.Cad/ExtensionMethod/CurveEx.cs +++ b/src/IFoxCAD.Cad/ExtensionMethod/CurveEx.cs @@ -85,16 +85,16 @@ public static List BreakCurve(this List curves) var geCurves = new List(); // 存储曲线转换后的复合曲线 var paramss = new List>(); // 存储每个曲线的交点参数值 - foreach (var curve in curves) + for (int i = 0; i < curves.Count; i++) { - var cc3d = curve.ToCompositeCurve3d(); + var cc3d = curves[i].ToCompositeCurve3d(); if (cc3d is not null) { geCurves.Add(cc3d); paramss.Add(new List()); } } - + //var oldCurves = new List(); var newCurves = new List(); var cci3d = new CurveCurveIntersector3d(); @@ -123,13 +123,13 @@ public static List BreakCurve(this List curves) var c3ds = gc1.GetSplitCurves(pars1); if (c3ds.Count > 1) { - foreach (CompositeCurve3d c3d in c3ds) + foreach (var c3d in c3ds) { - var c = c3d.ToCurve(); - if (c is not null) + var c3dCur = c3d.ToCurve(); + if (c3dCur is not null) { - c.SetPropertiesFrom(curves[i]); - newCurves.Add(c); + c3dCur.SetPropertiesFrom(curves[i]); + newCurves.Add(c3dCur); } } //oldCurves.Add(curves[i]); -- Gitee From bc8499d1996681d159060a544b6ab71d2f7f2073 Mon Sep 17 00:00:00 2001 From: liuqihong <540762622@qq.com> Date: Thu, 11 Aug 2022 12:29:23 +0800 Subject: [PATCH 341/675] =?UTF-8?q?=E4=BC=BC=E4=B9=8E=E4=B9=8B=E5=89=8D?= =?UTF-8?q?=E6=B7=B7=E5=85=A5=E4=BA=86=E5=A5=87=E6=80=AA=E7=9A=84=E4=B8=9C?= =?UTF-8?q?=E8=A5=BF?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/IFoxCAD.Basal/IFoxCAD.Basal.csproj | 35 -------------------------- 1 file changed, 35 deletions(-) diff --git a/src/IFoxCAD.Basal/IFoxCAD.Basal.csproj b/src/IFoxCAD.Basal/IFoxCAD.Basal.csproj index 63d8d56..73d940d 100644 --- a/src/IFoxCAD.Basal/IFoxCAD.Basal.csproj +++ b/src/IFoxCAD.Basal/IFoxCAD.Basal.csproj @@ -31,39 +31,4 @@ - - - - - DEBUG - - - $(Configuration);ac2009 - - - $(Configuration);ac2013 - - - $(Configuration);ac2015 - - - 1701;1702; - - - 1701;1702; - - - 1701;1702; - - - 1701;1702; - - - 1701;1702; - - - 1701;1702; - -- Gitee From ce1daf68dfc63a230f422f6d03c7a3fc314a0312 Mon Sep 17 00:00:00 2001 From: liuqihong <540762622@qq.com> Date: Thu, 11 Aug 2022 12:37:01 +0800 Subject: [PATCH 342/675] =?UTF-8?q?=E5=BA=9F=E5=BC=83=E6=B6=88=E9=87=8D?= =?UTF-8?q?=E5=87=BD=E6=95=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/IFoxCAD.Basal/ArrayEx.cs | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/src/IFoxCAD.Basal/ArrayEx.cs b/src/IFoxCAD.Basal/ArrayEx.cs index 2491e16..34fb4b7 100644 --- a/src/IFoxCAD.Basal/ArrayEx.cs +++ b/src/IFoxCAD.Basal/ArrayEx.cs @@ -21,11 +21,17 @@ public static T[] Combine2(this T[] a, T[] b) } /// - /// 一维数组消重 + /// 一维数组消重,此函数建议更改为: + /// set = new(); + /// foreach (var item in listInOut) + /// set.Add(item); + /// ]]> /// /// /// 传入有重复成员的数组,传出没有重复的 /// 传出参数1:数组开头;传出参数2:数组结尾;返回值比较结尾为就移除 + [Obsolete] public static void Deduplication(List listInOut, Func func) { for (int i = 0; i < listInOut.Count; i++) -- Gitee From d8017115e5ff5856e8eeb6d2689db3a5bc7a1b72 Mon Sep 17 00:00:00 2001 From: liuqihong <540762622@qq.com> Date: Thu, 11 Aug 2022 22:41:35 +0800 Subject: [PATCH 343/675] =?UTF-8?q?=E8=A1=A5=E5=85=85acad2008=E7=9A=84?= =?UTF-8?q?=E4=BB=A3=E7=A0=81=E4=BE=9Bjjbox2008nuget=E5=8C=85=E7=94=A8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/IFoxCAD.Cad/Algorithms/QuadTree/Rect.cs | 8 +-- src/IFoxCAD.Cad/ExtensionMethod/ObjectIdEx.cs | 16 ++++-- .../ExtensionMethod/SelectionSetEx.cs | 4 +- .../ExtensionMethod/SymbolTableRecordEx.cs | 4 +- src/IFoxCAD.Cad/GlobalUsings.cs | 7 ++- src/IFoxCAD.Cad/IFoxCAD.Cad.csproj | 2 +- src/IFoxCAD.Cad/Runtime/AcadVersion.cs | 1 - src/IFoxCAD.Cad/Runtime/AutoRegAssem.cs | 3 +- src/IFoxCAD.Cad/Runtime/DBTrans.cs | 45 +++++++++++------ src/IFoxCAD.Cad/Runtime/FileOpenMode.cs | 13 +++++ src/IFoxCAD.Cad/Runtime/IAutoGo.cs | 1 + tests/Test/GlobalUsings.cs | 15 +++--- tests/Test/TestMirrorFile.cs | 49 ++++++++++--------- 13 files changed, 105 insertions(+), 63 deletions(-) create mode 100644 src/IFoxCAD.Cad/Runtime/FileOpenMode.cs diff --git a/src/IFoxCAD.Cad/Algorithms/QuadTree/Rect.cs b/src/IFoxCAD.Cad/Algorithms/QuadTree/Rect.cs index 15e8e11..c533ddb 100644 --- a/src/IFoxCAD.Cad/Algorithms/QuadTree/Rect.cs +++ b/src/IFoxCAD.Cad/Algorithms/QuadTree/Rect.cs @@ -548,10 +548,10 @@ public static void XCollision(List box, #region 转换类型 #if !WinForm // 隐式转换(相当于是重载赋值运算符) - public static implicit operator Rect(System.Windows.Rect rect) - { - return new Rect(rect.Left, rect.Bottom, rect.Right, rect.Top); - } + //public static implicit operator Rect(System.Windows.Rect rect) + //{ + // return new Rect(rect.Left, rect.Bottom, rect.Right, rect.Top); + //} public static implicit operator Rect(System.Drawing.RectangleF rect) { return new Rect(rect.Left, rect.Bottom, rect.Right, rect.Top); diff --git a/src/IFoxCAD.Cad/ExtensionMethod/ObjectIdEx.cs b/src/IFoxCAD.Cad/ExtensionMethod/ObjectIdEx.cs index 802acf0..52c9261 100644 --- a/src/IFoxCAD.Cad/ExtensionMethod/ObjectIdEx.cs +++ b/src/IFoxCAD.Cad/ExtensionMethod/ObjectIdEx.cs @@ -46,11 +46,20 @@ public static class ObjectIdEx public static IEnumerable OfType(this IEnumerable ids) where T : DBObject { string dxfName = RXClass.GetClass(typeof(T)).DxfName; - return - ids - .Where(id => id.ObjectClass.DxfName == dxfName); + return ids.Where(id => id.ObjectClass().DxfName == dxfName); } #endregion GetObject + + //Acad08缺少 id.ObjectClass 如何补偿? + public static RXClass ObjectClass(this ObjectId id) + { +#if NET35 + return RXClass.GetClass(id.GetType()); +#else + return id.ObjectClass; +#endif + } + /// /// id是否有效,未被删除 /// @@ -60,6 +69,7 @@ public static bool IsOk(this ObjectId id) { return !id.IsNull && id.IsValid && !id.IsErased && !id.IsEffectivelyErased && id.IsResident; } + /// /// 删除id代表的对象 /// diff --git a/src/IFoxCAD.Cad/ExtensionMethod/SelectionSetEx.cs b/src/IFoxCAD.Cad/ExtensionMethod/SelectionSetEx.cs index 61d1688..dbde0ac 100644 --- a/src/IFoxCAD.Cad/ExtensionMethod/SelectionSetEx.cs +++ b/src/IFoxCAD.Cad/ExtensionMethod/SelectionSetEx.cs @@ -39,7 +39,7 @@ public static IEnumerable GetObjectIds(this SelectionSet ss) where return ss .GetObjectIds() - .Where(id => id.ObjectClass.DxfName == dxfName); + .Where(id => id.ObjectClass().DxfName == dxfName); } /// @@ -52,7 +52,7 @@ public static IEnumerable> GetObjectIdGroup(this Sel return ss .GetObjectIds() - .GroupBy(id => id.ObjectClass.DxfName); + .GroupBy(id => id.ObjectClass().DxfName); } #endregion diff --git a/src/IFoxCAD.Cad/ExtensionMethod/SymbolTableRecordEx.cs b/src/IFoxCAD.Cad/ExtensionMethod/SymbolTableRecordEx.cs index 6ff5bfd..bceeac0 100644 --- a/src/IFoxCAD.Cad/ExtensionMethod/SymbolTableRecordEx.cs +++ b/src/IFoxCAD.Cad/ExtensionMethod/SymbolTableRecordEx.cs @@ -347,7 +347,7 @@ public static IEnumerable GetObjectIds(this BlockTableRecord btr) w { string dxfName = RXClass.GetClass(typeof(T)).DxfName; return btr.Cast() - .Where(id => id.ObjectClass.DxfName == dxfName); + .Where(id => id.ObjectClass().DxfName == dxfName); } /// @@ -360,7 +360,7 @@ public static IEnumerable> GetObjectIds(this BlockTa return btr .Cast() - .GroupBy(id => id.ObjectClass.DxfName); + .GroupBy(id => id.ObjectClass().DxfName); } /// diff --git a/src/IFoxCAD.Cad/GlobalUsings.cs b/src/IFoxCAD.Cad/GlobalUsings.cs index 7020dbe..7051b36 100644 --- a/src/IFoxCAD.Cad/GlobalUsings.cs +++ b/src/IFoxCAD.Cad/GlobalUsings.cs @@ -21,6 +21,11 @@ global using Acap = Autodesk.AutoCAD.ApplicationServices.Application; global using Autodesk.AutoCAD.DatabaseServices.Filters; - + + +global using System.Collections.Specialized; +global using Registry = Microsoft.Win32.Registry; +global using RegistryKey = Microsoft.Win32.RegistryKey; + /// ifoxcad.basal 引用 global using IFoxCAD.Basal; \ No newline at end of file diff --git a/src/IFoxCAD.Cad/IFoxCAD.Cad.csproj b/src/IFoxCAD.Cad/IFoxCAD.Cad.csproj index 36a58ac..1f5e749 100644 --- a/src/IFoxCAD.Cad/IFoxCAD.Cad.csproj +++ b/src/IFoxCAD.Cad/IFoxCAD.Cad.csproj @@ -56,7 +56,7 @@ DEBUG - $(Configuration);ac2009 + $(Configuration);ac2008;ac2009 $(Configuration);ac2013 diff --git a/src/IFoxCAD.Cad/Runtime/AcadVersion.cs b/src/IFoxCAD.Cad/Runtime/AcadVersion.cs index 99bceb2..94c7947 100644 --- a/src/IFoxCAD.Cad/Runtime/AcadVersion.cs +++ b/src/IFoxCAD.Cad/Runtime/AcadVersion.cs @@ -1,5 +1,4 @@ namespace IFoxCAD.Cad; -using Registry = Microsoft.Win32.Registry; /// /// cad版本号类 diff --git a/src/IFoxCAD.Cad/Runtime/AutoRegAssem.cs b/src/IFoxCAD.Cad/Runtime/AutoRegAssem.cs index b8e73b8..540762c 100644 --- a/src/IFoxCAD.Cad/Runtime/AutoRegAssem.cs +++ b/src/IFoxCAD.Cad/Runtime/AutoRegAssem.cs @@ -1,6 +1,5 @@ namespace IFoxCAD.Cad; -using Registry = Microsoft.Win32.Registry; -using RegistryKey = Microsoft.Win32.RegistryKey; + /// /// 注册中心 diff --git a/src/IFoxCAD.Cad/Runtime/DBTrans.cs b/src/IFoxCAD.Cad/Runtime/DBTrans.cs index 087c665..d207edf 100644 --- a/src/IFoxCAD.Cad/Runtime/DBTrans.cs +++ b/src/IFoxCAD.Cad/Runtime/DBTrans.cs @@ -118,23 +118,25 @@ public DBTrans(Database database, bool commit = true) /// /// 要打开的文件名 /// 事务是否提交 + /// 开图模式 + /// 密码 public DBTrans(string fileName, bool commit = true, - FileOpenMode openMode = FileOpenMode.OpenForReadAndWriteNoShare) +#pragma warning disable CS0436 // 类型与导入类型冲突 + FileOpenMode openMode = FileOpenMode.OpenForReadAndWriteNoShare, +#pragma warning restore CS0436 // 类型与导入类型冲突 + string? password = null) { -#if NET35 if (string.IsNullOrEmpty(fileName?.Trim())) -#else - if (string.IsNullOrWhiteSpace(fileName)) -#endif throw new ArgumentNullException(nameof(fileName)); - - if (File.Exists(fileName)) + if (!File.Exists(fileName)) + Database = new Database(true, false); + else { - var doc = Application.DocumentManager - .Cast() - .FirstOrDefault(doc => doc.Name == fileName); + var doc = Acap.DocumentManager + .Cast() + .FirstOrDefault(doc => doc.Name == fileName); if (doc is not null) { Database = doc.Database; @@ -147,14 +149,27 @@ public DBTrans(string fileName, if (Path.GetExtension(fileName).ToLower().Contains("dxf")) Database.DxfIn(fileName, null); else - Database.ReadDwgFile(fileName, openMode, true, null); + { +#if ac2008 + //此处没有一一对应的关系 +#pragma warning disable CS0436 // 类型与导入类型冲突 + var sf = openMode switch + { + FileOpenMode.OpenTryForReadShare => FileShare.Read, + FileOpenMode.OpenForReadAndAllShare => FileShare.ReadWrite, + FileOpenMode.OpenForReadAndWriteNoShare => FileShare.None, + FileOpenMode.OpenForReadAndReadShare => FileShare.ReadWrite, + _ => FileShare.ReadWrite, + }; +#pragma warning restore CS0436 // 类型与导入类型冲突 + Database.ReadDwgFile(fileName, sf, true/*控制读入一个与系统编码不相同的文件时的转换操作*/, password); +#else + Database.ReadDwgFile(fileName, openMode, true/*控制读入一个与系统编码不相同的文件时的转换操作*/, password); +#endif + } Database.CloseInput(true); } } - else - { - Database = new Database(true, false); - } Transaction = Database.TransactionManager.StartTransaction(); _commit = commit; diff --git a/src/IFoxCAD.Cad/Runtime/FileOpenMode.cs b/src/IFoxCAD.Cad/Runtime/FileOpenMode.cs new file mode 100644 index 0000000..5108dd5 --- /dev/null +++ b/src/IFoxCAD.Cad/Runtime/FileOpenMode.cs @@ -0,0 +1,13 @@ +#if ac2008 //NET35 +namespace Autodesk.AutoCAD.DatabaseServices +{ + [Wrapper("AcDbDatabase::OpenMode")] + public enum FileOpenMode + { + OpenTryForReadShare = 4, + OpenForReadAndAllShare = 3, + OpenForReadAndWriteNoShare = 2, + OpenForReadAndReadShare = 1 + } +} +#endif \ No newline at end of file diff --git a/src/IFoxCAD.Cad/Runtime/IAutoGo.cs b/src/IFoxCAD.Cad/Runtime/IAutoGo.cs index a2cbbb4..27ce550 100644 --- a/src/IFoxCAD.Cad/Runtime/IAutoGo.cs +++ b/src/IFoxCAD.Cad/Runtime/IAutoGo.cs @@ -1,4 +1,5 @@ namespace IFoxCAD.Cad; + using System.Diagnostics; /// diff --git a/tests/Test/GlobalUsings.cs b/tests/Test/GlobalUsings.cs index 3940f31..2896cbf 100644 --- a/tests/Test/GlobalUsings.cs +++ b/tests/Test/GlobalUsings.cs @@ -18,16 +18,13 @@ global using Autodesk.AutoCAD.DatabaseServices; global using Autodesk.AutoCAD.Geometry; global using Autodesk.AutoCAD.Runtime; -global using acgi = Autodesk.AutoCAD.GraphicsInterface; -global using acap = Autodesk.AutoCAD.ApplicationServices.Application; +global using Acgi = Autodesk.AutoCAD.GraphicsInterface; +global using Acap = Autodesk.AutoCAD.ApplicationServices.Application; + +global using Registry = Microsoft.Win32.Registry; +global using RegistryKey = Microsoft.Win32.RegistryKey; /// ifoxcad global using IFoxCAD.Cad; global using IFoxCAD.WPF; -global using IFoxCAD.Basal; - - -namespace System.Runtime.CompilerServices -{ - internal static class IsExternalInit { } -} \ No newline at end of file +global using IFoxCAD.Basal; \ No newline at end of file diff --git a/tests/Test/TestMirrorFile.cs b/tests/Test/TestMirrorFile.cs index 8e9eed6..b5f212f 100644 --- a/tests/Test/TestMirrorFile.cs +++ b/tests/Test/TestMirrorFile.cs @@ -12,56 +12,59 @@ public static void CmdTest_MirrorFile() { using var tr = new DBTrans(file, openMode: FileOpenMode.OpenForReadAndReadShare); - tr.BlockTable.Change(tr.ModelSpace.ObjectId, ms => { - foreach (ObjectId entId in ms) + tr.BlockTable.Change(tr.ModelSpace.ObjectId, modelSpace => { + foreach (ObjectId entId in modelSpace) { - var text = tr.GetObject(entId, OpenMode.ForRead)!; - if (text is null) + var dbText = tr.GetObject(entId, OpenMode.ForRead)!; + if (dbText is null) continue; - text.UpgradeOpen(); - var pos = text.Position; + dbText.UpgradeOpen(); + var pos = dbText.Position; //text.Move(pos, Point3d.Origin); //Y轴 - text.Mirror(Point3d.Origin, new Point3d(0, 1, 0)); + dbText.Mirror(Point3d.Origin, new Point3d(0, 1, 0)); //text.Move(Point3d.Origin, pos); - text.DowngradeOpen(); + dbText.DowngradeOpen(); } }); tr.Database.SaveAs(fileSave, DwgVersion.AC1021/*AC1021 AutoCAD 2007/2008/2009.*/); } - + /// + /// 测试:后台设置 dbText.IsMirroredInX 属性会令文字偏移 + /// 答案:存在,并提出解决方案 + /// [CommandMethod("CmdTest_MirrorFile2")] public static void CmdTest_MirrorFile2() { using var tr = new DBTrans(file, openMode: FileOpenMode.OpenForReadAndReadShare); tr.Database.DBTextDeviation(() => { - tr.BlockTable.Change(tr.ModelSpace.ObjectId, ms => { - foreach (ObjectId entId in ms) + + var yaxis = new Point3d(0, 1, 0); + tr.BlockTable.Change(tr.ModelSpace.ObjectId, modelSpace => { + foreach (ObjectId entId in modelSpace) { var entity = tr.GetObject(entId, OpenMode.ForWrite)!; - if (entity is DBText text) + if (entity is DBText dbText) { - text.Mirror(Point3d.Origin, new Point3d(0, 1, 0)); - text.IsMirroredInX = true; //这句将导致文字偏移 + dbText.Mirror(Point3d.Origin, yaxis); + dbText.IsMirroredInX = true; //这句将导致文字偏移 - if (text.VerticalMode == TextVerticalMode.TextBase) - text.VerticalMode = TextVerticalMode.TextBottom; + //指定文字的垂直对齐方式 + if (dbText.VerticalMode == TextVerticalMode.TextBase) + dbText.VerticalMode = TextVerticalMode.TextBottom; - text.HorizontalMode = text.HorizontalMode switch + //指定文字的水平对齐方式 + dbText.HorizontalMode = dbText.HorizontalMode switch { TextHorizontalMode.TextLeft => TextHorizontalMode.TextRight, TextHorizontalMode.TextRight => TextHorizontalMode.TextLeft, - _ => text.HorizontalMode + _ => dbText.HorizontalMode }; - //Point3d pos = text.GeometricExtents.MidMidPoint(); - //text.Mirror(pos, pos.Polar(text.Rotation+PI/2, 100)); - text.AdjustAlignment(tr.Database); - continue; + dbText.AdjustAlignment(tr.Database); } - entity.Mirror(Point3d.Origin, new Point3d(0, 1, 0)); } }); }); -- Gitee From 231a1df376aeac16820eab32ebd99ebf204e21da Mon Sep 17 00:00:00 2001 From: liuqihong <540762622@qq.com> Date: Thu, 11 Aug 2022 22:56:29 +0800 Subject: [PATCH 344/675] =?UTF-8?q?jing=E5=88=86=E6=94=AF=E6=94=AF?= =?UTF-8?q?=E6=8C=81acad08?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .editorconfig | 217 ++ .gitattributes | 2 +- .gitignore | 128 +- IFoxCAD.sln | 14 +- README.md | 163 +- docs/DBTrans.md | 4 +- docs/WPF.md | 2 +- ...4\344\272\214\347\273\264\347\240\201.png" | Bin 0 -> 20425 bytes docs/png/nuget.png | Bin 0 -> 159167 bytes docs/png/nuget1.png | Bin 0 -> 27373 bytes docs/png/standard.png | Bin 0 -> 148673 bytes ...66\346\236\204\350\257\264\346\230\216.md" | 8 +- src/IFoxCAD.Basal/ArrayEx.cs | 50 + src/IFoxCAD.Basal/CLS/Index.cs | 148 + src/IFoxCAD.Basal/CLS/Range.cs | 103 + src/IFoxCAD.Basal/CLS/RuntimeHelpers.cs | 41 + .../CLS/TupleElementNamesAttribute.cs | 48 + src/IFoxCAD.Basal/CLS/ValueTuple.cs | 2144 ++++++++++++ src/IFoxCAD.Basal/DictEx.cs | 21 + src/IFoxCAD.Basal/GlobalUsings.cs | 9 + src/IFoxCAD.Basal/IFoxCAD.Basal.csproj | 75 +- src/IFoxCAD.Basal/LinkedHashMap.cs | 171 + src/IFoxCAD.Basal/LinkedHashSet.cs | 221 ++ src/IFoxCAD.Basal/LinqEx.cs | 543 ++- src/IFoxCAD.Basal/ListEx.cs | 25 + src/IFoxCAD.Basal/LoopList.cs | 1223 +++---- src/IFoxCAD.Basal/Sortedset/ISet.cs | 71 + src/IFoxCAD.Basal/Sortedset/SR.cs | 907 +++++ src/IFoxCAD.Basal/Sortedset/Sortedset.cs | 2935 +++++++++++++++++ src/IFoxCAD.Basal/Sortedset/ThrowHelper.cs | 382 +++ src/IFoxCAD.Basal/Sortedset/bithelper.cs | 173 + src/IFoxCAD.Cad/Algorithms/Graph/Graph.cs | 647 ++++ src/IFoxCAD.Cad/Algorithms/Graph/IGraph.cs | 98 + .../Algorithms/QuadTree/QuadEntity.cs | 25 + .../Algorithms/QuadTree/QuadTree.cs | 259 ++ .../Algorithms/QuadTree/QuadTreeEvn.cs | 26 + .../Algorithms/QuadTree/QuadTreeNode.cs | 818 +++++ .../Algorithms/QuadTree/QuadTreeSelectMode.cs | 21 + src/IFoxCAD.Cad/Algorithms/QuadTree/Rect.cs | 605 ++++ .../Algorithms/QuadTree/Utility.cs | 60 + .../ExtensionMethod/BulgeVertexWidth.cs | 85 + .../ExtensionMethod/CollectionEx.cs | 291 +- src/IFoxCAD.Cad/ExtensionMethod/Curve2dEx.cs | 530 ++- src/IFoxCAD.Cad/ExtensionMethod/Curve3dEx.cs | 920 +++--- src/IFoxCAD.Cad/ExtensionMethod/CurveEx.cs | 1400 +++----- .../ExtensionMethod/DBDictionaryEx.cs | 622 ++-- src/IFoxCAD.Cad/ExtensionMethod/DBObjectEx.cs | 262 +- src/IFoxCAD.Cad/ExtensionMethod/DatabaseEx.cs | 23 + src/IFoxCAD.Cad/ExtensionMethod/EditorEx.cs | 1600 +++++---- src/IFoxCAD.Cad/ExtensionMethod/EntityEx.cs | 730 ++-- src/IFoxCAD.Cad/ExtensionMethod/Enums.cs | 161 +- src/IFoxCAD.Cad/ExtensionMethod/GeometryEx.cs | 1189 +++---- src/IFoxCAD.Cad/ExtensionMethod/Jig.cs | 343 ++ src/IFoxCAD.Cad/ExtensionMethod/ObjEx.cs | 21 + src/IFoxCAD.Cad/ExtensionMethod/ObjectIdEx.cs | 133 +- src/IFoxCAD.Cad/ExtensionMethod/PointEx.cs | 128 + .../ExtensionMethod/SelectionSetEx.cs | 173 +- .../ExtensionMethod/SymbolTableEx.cs | 540 +-- .../ExtensionMethod/SymbolTableRecordEx.cs | 721 ++-- src/IFoxCAD.Cad/ExtensionMethod/Tools.cs | 188 ++ .../HatchConverter.cs" | 358 ++ .../HatchEx.cs" | 15 + .../HatchInfo.cs" | 351 ++ .../AttachmentPointHelper.cs" | 95 + .../TextEntityAdd.cs" | 62 + .../TextInfo.cs" | 178 + src/IFoxCAD.Cad/GlobalUsings.cs | 31 + src/IFoxCAD.Cad/IFoxCAD.Cad.csproj | 51 +- src/IFoxCAD.Cad/IFoxCAD.Cad.csproj.data | 1 - src/IFoxCAD.Cad/ResultData/LispDottedPair.cs | 106 +- src/IFoxCAD.Cad/ResultData/LispList.cs | 353 +- src/IFoxCAD.Cad/ResultData/TypedValueList.cs | 114 +- src/IFoxCAD.Cad/ResultData/XRecordDataList.cs | 111 +- src/IFoxCAD.Cad/ResultData/XdataList.cs | 115 +- src/IFoxCAD.Cad/Runtime/AOP.cs | 99 + src/IFoxCAD.Cad/Runtime/AcadVersion.cs | 164 +- src/IFoxCAD.Cad/Runtime/AssemInfo.cs | 105 +- src/IFoxCAD.Cad/Runtime/AutoRegAssem.cs | 242 +- src/IFoxCAD.Cad/Runtime/CadVersion.cs | 92 + src/IFoxCAD.Cad/Runtime/DBTrans.cs | 699 ++-- src/IFoxCAD.Cad/Runtime/Env.cs | 792 ++--- src/IFoxCAD.Cad/Runtime/FileOpenMode.cs | 13 + src/IFoxCAD.Cad/Runtime/IAutoGo.cs | 307 ++ src/IFoxCAD.Cad/Runtime/Log.cs | 412 +++ src/IFoxCAD.Cad/Runtime/MethodInfoHelper.cs | 53 + src/IFoxCAD.Cad/Runtime/SymbolTable.cs | 544 +-- src/IFoxCAD.Cad/Runtime/Utils.cs | 98 + src/IFoxCAD.Cad/SelectionFilter/OpComp.cs | 138 +- src/IFoxCAD.Cad/SelectionFilter/OpEqual.cs | 148 +- src/IFoxCAD.Cad/SelectionFilter/OpFilter.cs | 582 ++-- src/IFoxCAD.Cad/SelectionFilter/OpList.cs | 272 +- src/IFoxCAD.Cad/SelectionFilter/OpLogi.cs | 205 +- src/IFoxCAD.WPF/Converter.cs | 176 +- src/IFoxCAD.WPF/DependencyObjectExtensions.cs | 52 +- src/IFoxCAD.WPF/EnumSelection.cs | 72 + src/IFoxCAD.WPF/EventBindingExtension.cs | 337 +- src/IFoxCAD.WPF/GlobalUsings.cs | 18 + src/IFoxCAD.WPF/IFoxCAD.WPF.csproj | 67 +- src/IFoxCAD.WPF/RelayCommand.cs | 316 +- src/IFoxCAD.WPF/ViewModelBase.cs | 103 +- tests/Test/CmdINI.cs | 111 + tests/Test/GlobalUsings.cs | 30 + tests/Test/Properties/launchSettings.json | 2 +- tests/Test/Test.cs | 818 ++--- tests/Test/Test.csproj | 29 +- tests/Test/TestAOP.cs | 66 + tests/Test/TestCurve.cs | 158 + tests/Test/TestDBTrans.cs | 78 + tests/Test/TestEnt.cs | 40 + tests/Test/TestFileDatabase.cs | 29 + tests/Test/TestJig.cs | 335 ++ tests/Test/TestLisp.cs | 122 + tests/Test/TestLoop.cs | 36 + tests/Test/TestMirrorFile.cs | 73 + tests/Test/TestPoint.cs | 160 + tests/Test/TestQuadTree.cs | 461 +++ tests/Test/Testid.cs | 74 + tests/Test/testConvexHull.cs | 6 +- tests/Test/testblock.cs | 631 ++++ tests/Test/testeditor.cs | 95 +- tests/Test/testenv.cs | 136 +- tests/Test/testselectfilter.cs | 16 +- tests/Test/wpf/Class1.cs | 13 + tests/Test/wpf/TestView.xaml | 4 +- tests/Test/wpf/TestView.xaml.cs | 2 +- tests/Test/wpf/TestViewModel.cs | 156 +- tests/TestConsole/Program.cs | 55 + tests/TestConsole/RuntimeHelpers.cs | 50 + tests/TestConsole/TestConsole.csproj | 22 + ...50\350\276\276\345\274\217\346\240\221.cs" | 143 + 130 files changed, 25364 insertions(+), 9081 deletions(-) create mode 100644 .editorconfig create mode 100644 "docs/png/ifoxcad\347\224\250\346\210\267\344\272\244\346\265\201\347\276\244\347\276\244\344\272\214\347\273\264\347\240\201.png" create mode 100644 docs/png/nuget.png create mode 100644 docs/png/nuget1.png create mode 100644 docs/png/standard.png create mode 100644 src/IFoxCAD.Basal/ArrayEx.cs create mode 100644 src/IFoxCAD.Basal/CLS/Index.cs create mode 100644 src/IFoxCAD.Basal/CLS/Range.cs create mode 100644 src/IFoxCAD.Basal/CLS/RuntimeHelpers.cs create mode 100644 src/IFoxCAD.Basal/CLS/TupleElementNamesAttribute.cs create mode 100644 src/IFoxCAD.Basal/CLS/ValueTuple.cs create mode 100644 src/IFoxCAD.Basal/DictEx.cs create mode 100644 src/IFoxCAD.Basal/GlobalUsings.cs create mode 100644 src/IFoxCAD.Basal/LinkedHashMap.cs create mode 100644 src/IFoxCAD.Basal/LinkedHashSet.cs create mode 100644 src/IFoxCAD.Basal/ListEx.cs create mode 100644 src/IFoxCAD.Basal/Sortedset/ISet.cs create mode 100644 src/IFoxCAD.Basal/Sortedset/SR.cs create mode 100644 src/IFoxCAD.Basal/Sortedset/Sortedset.cs create mode 100644 src/IFoxCAD.Basal/Sortedset/ThrowHelper.cs create mode 100644 src/IFoxCAD.Basal/Sortedset/bithelper.cs create mode 100644 src/IFoxCAD.Cad/Algorithms/Graph/Graph.cs create mode 100644 src/IFoxCAD.Cad/Algorithms/Graph/IGraph.cs create mode 100644 src/IFoxCAD.Cad/Algorithms/QuadTree/QuadEntity.cs create mode 100644 src/IFoxCAD.Cad/Algorithms/QuadTree/QuadTree.cs create mode 100644 src/IFoxCAD.Cad/Algorithms/QuadTree/QuadTreeEvn.cs create mode 100644 src/IFoxCAD.Cad/Algorithms/QuadTree/QuadTreeNode.cs create mode 100644 src/IFoxCAD.Cad/Algorithms/QuadTree/QuadTreeSelectMode.cs create mode 100644 src/IFoxCAD.Cad/Algorithms/QuadTree/Rect.cs create mode 100644 src/IFoxCAD.Cad/Algorithms/QuadTree/Utility.cs create mode 100644 src/IFoxCAD.Cad/ExtensionMethod/BulgeVertexWidth.cs create mode 100644 src/IFoxCAD.Cad/ExtensionMethod/DatabaseEx.cs create mode 100644 src/IFoxCAD.Cad/ExtensionMethod/Jig.cs create mode 100644 src/IFoxCAD.Cad/ExtensionMethod/ObjEx.cs create mode 100644 src/IFoxCAD.Cad/ExtensionMethod/PointEx.cs create mode 100644 src/IFoxCAD.Cad/ExtensionMethod/Tools.cs create mode 100644 "src/IFoxCAD.Cad/ExtensionMethod/\346\226\260\345\273\272\345\241\253\345\205\205/HatchConverter.cs" create mode 100644 "src/IFoxCAD.Cad/ExtensionMethod/\346\226\260\345\273\272\345\241\253\345\205\205/HatchEx.cs" create mode 100644 "src/IFoxCAD.Cad/ExtensionMethod/\346\226\260\345\273\272\345\241\253\345\205\205/HatchInfo.cs" create mode 100644 "src/IFoxCAD.Cad/ExtensionMethod/\346\226\260\345\273\272\346\226\207\345\255\227/AttachmentPointHelper.cs" create mode 100644 "src/IFoxCAD.Cad/ExtensionMethod/\346\226\260\345\273\272\346\226\207\345\255\227/TextEntityAdd.cs" create mode 100644 "src/IFoxCAD.Cad/ExtensionMethod/\346\226\260\345\273\272\346\226\207\345\255\227/TextInfo.cs" create mode 100644 src/IFoxCAD.Cad/GlobalUsings.cs delete mode 100644 src/IFoxCAD.Cad/IFoxCAD.Cad.csproj.data create mode 100644 src/IFoxCAD.Cad/Runtime/AOP.cs create mode 100644 src/IFoxCAD.Cad/Runtime/CadVersion.cs create mode 100644 src/IFoxCAD.Cad/Runtime/FileOpenMode.cs create mode 100644 src/IFoxCAD.Cad/Runtime/IAutoGo.cs create mode 100644 src/IFoxCAD.Cad/Runtime/Log.cs create mode 100644 src/IFoxCAD.Cad/Runtime/MethodInfoHelper.cs create mode 100644 src/IFoxCAD.Cad/Runtime/Utils.cs create mode 100644 src/IFoxCAD.WPF/EnumSelection.cs create mode 100644 src/IFoxCAD.WPF/GlobalUsings.cs create mode 100644 tests/Test/CmdINI.cs create mode 100644 tests/Test/GlobalUsings.cs create mode 100644 tests/Test/TestAOP.cs create mode 100644 tests/Test/TestCurve.cs create mode 100644 tests/Test/TestDBTrans.cs create mode 100644 tests/Test/TestEnt.cs create mode 100644 tests/Test/TestFileDatabase.cs create mode 100644 tests/Test/TestJig.cs create mode 100644 tests/Test/TestLisp.cs create mode 100644 tests/Test/TestLoop.cs create mode 100644 tests/Test/TestMirrorFile.cs create mode 100644 tests/Test/TestPoint.cs create mode 100644 tests/Test/TestQuadTree.cs create mode 100644 tests/Test/Testid.cs create mode 100644 tests/Test/testblock.cs create mode 100644 tests/Test/wpf/Class1.cs create mode 100644 tests/TestConsole/Program.cs create mode 100644 tests/TestConsole/RuntimeHelpers.cs create mode 100644 tests/TestConsole/TestConsole.csproj create mode 100644 "tests/TestConsole/\350\241\250\350\276\276\345\274\217\346\240\221.cs" diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 0000000..f0012cd --- /dev/null +++ b/.editorconfig @@ -0,0 +1,217 @@ +# 如果要从更高级别的目录继承 .editorconfig 设置,请删除以下行 +root = true + +# c# 文件 +[*.cs] + +#### Core EditorConfig 选项 #### + +# 缩进和间距 +indent_size = 4 +indent_style = space +tab_width = 4 + +# 新行首选项 +end_of_line = crlf +insert_final_newline = false + +#### .NET 编码约定 #### + +# 组织 Using +dotnet_separate_import_directive_groups = false +dotnet_sort_system_directives_first = false +file_header_template = unset + +# this. 和 Me. 首选项 +dotnet_style_qualification_for_event = false +dotnet_style_qualification_for_field = false +dotnet_style_qualification_for_method = false +dotnet_style_qualification_for_property = false + +# 语言关键字与 bcl 类型首选项 +dotnet_style_predefined_type_for_locals_parameters_members = true +dotnet_style_predefined_type_for_member_access = true + +# 括号首选项 +dotnet_style_parentheses_in_arithmetic_binary_operators = always_for_clarity +dotnet_style_parentheses_in_other_binary_operators = always_for_clarity +dotnet_style_parentheses_in_other_operators = never_if_unnecessary +dotnet_style_parentheses_in_relational_binary_operators = always_for_clarity + +# 修饰符首选项 +dotnet_style_require_accessibility_modifiers = for_non_interface_members + +# 表达式级首选项 +dotnet_style_coalesce_expression = true +dotnet_style_collection_initializer = true +dotnet_style_explicit_tuple_names = true +dotnet_style_namespace_match_folder = true +dotnet_style_null_propagation = true +dotnet_style_object_initializer = true +dotnet_style_operator_placement_when_wrapping = beginning_of_line +dotnet_style_prefer_auto_properties = true +dotnet_style_prefer_compound_assignment = true +dotnet_style_prefer_conditional_expression_over_assignment = true +dotnet_style_prefer_conditional_expression_over_return = true +dotnet_style_prefer_inferred_anonymous_type_member_names = true +dotnet_style_prefer_inferred_tuple_names = true +dotnet_style_prefer_is_null_check_over_reference_equality_method = true:error +dotnet_style_prefer_simplified_boolean_expressions = true +dotnet_style_prefer_simplified_interpolation = true + +# 字段首选项 +dotnet_style_readonly_field = true + +# 参数首选项 +dotnet_code_quality_unused_parameters = all + +# 禁止显示首选项 +dotnet_remove_unnecessary_suppression_exclusions = none + +# 新行首选项 +dotnet_style_allow_multiple_blank_lines_experimental = true +dotnet_style_allow_statement_immediately_after_block_experimental = true + +#### c# 编码约定 #### + +# var 首选项 +csharp_style_var_elsewhere = false +csharp_style_var_for_built_in_types = false +csharp_style_var_when_type_is_apparent = false + +# Expression-bodied 成员 +csharp_style_expression_bodied_accessors = true +csharp_style_expression_bodied_constructors = false +csharp_style_expression_bodied_indexers = true +csharp_style_expression_bodied_lambdas = true +csharp_style_expression_bodied_local_functions = false +csharp_style_expression_bodied_methods = false +csharp_style_expression_bodied_operators = false +csharp_style_expression_bodied_properties = true + +# 模式匹配首选项 +csharp_style_pattern_matching_over_as_with_null_check = true +csharp_style_pattern_matching_over_is_with_cast_check = true +csharp_style_prefer_not_pattern = true +csharp_style_prefer_pattern_matching = true +csharp_style_prefer_switch_expression = true + +# Null 检查首选项 +csharp_style_conditional_delegate_call = true + +# 修饰符首选项 +csharp_prefer_static_local_function = true +csharp_preferred_modifier_order = public,private,protected,internal,static,extern,new,virtual,abstract,sealed,override,readonly,unsafe,volatile,async + +# 代码块首选项 +csharp_prefer_braces = true +csharp_prefer_simple_using_statement = true + +# 表达式级首选项 +csharp_prefer_simple_default_expression = true +csharp_style_deconstructed_variable_declaration = true +csharp_style_implicit_object_creation_when_type_is_apparent = true +csharp_style_inlined_variable_declaration = true +csharp_style_pattern_local_over_anonymous_function = true +csharp_style_prefer_index_operator = true +csharp_style_prefer_range_operator = true +csharp_style_throw_expression = true +csharp_style_unused_value_assignment_preference = discard_variable +csharp_style_unused_value_expression_statement_preference = discard_variable + +# "using" 指令首选项 +csharp_using_directive_placement = outside_namespace + +# 新行首选项 +csharp_style_allow_blank_line_after_colon_in_constructor_initializer_experimental = true +csharp_style_allow_blank_lines_between_consecutive_braces_experimental = true +csharp_style_allow_embedded_statements_on_same_line_experimental = true + +#### C# 格式规则 #### + +# 新行首选项 +csharp_new_line_before_catch = true +csharp_new_line_before_else = true +csharp_new_line_before_finally = true +csharp_new_line_before_members_in_anonymous_types = true +csharp_new_line_before_members_in_object_initializers = true +csharp_new_line_before_open_brace = accessors,anonymous_methods,anonymous_types,control_blocks,methods,object_collection_array_initializers,properties,types +csharp_new_line_between_query_expression_clauses = true + +# 缩进首选项 +csharp_indent_block_contents = true +csharp_indent_braces = false +csharp_indent_case_contents = true +csharp_indent_case_contents_when_block = true +csharp_indent_labels = one_less_than_current +csharp_indent_switch_labels = true + +# 空格键首选项 +csharp_space_after_cast = false +csharp_space_after_colon_in_inheritance_clause = true +csharp_space_after_comma = true +csharp_space_after_dot = false +csharp_space_after_keywords_in_control_flow_statements = true +csharp_space_after_semicolon_in_for_statement = true +csharp_space_around_binary_operators = before_and_after +csharp_space_around_declaration_statements = false +csharp_space_before_colon_in_inheritance_clause = true +csharp_space_before_comma = false +csharp_space_before_dot = false +csharp_space_before_open_square_brackets = false +csharp_space_before_semicolon_in_for_statement = false +csharp_space_between_empty_square_brackets = false +csharp_space_between_method_call_empty_parameter_list_parentheses = false +csharp_space_between_method_call_name_and_opening_parenthesis = false +csharp_space_between_method_call_parameter_list_parentheses = false +csharp_space_between_method_declaration_empty_parameter_list_parentheses = false +csharp_space_between_method_declaration_name_and_open_parenthesis = false +csharp_space_between_method_declaration_parameter_list_parentheses = false +csharp_space_between_parentheses = false +csharp_space_between_square_brackets = false + +# 包装首选项 +csharp_preserve_single_line_blocks = true +csharp_preserve_single_line_statements = true + +#### 命名样式 #### + +# 命名规则 + +dotnet_naming_rule.interface_should_be_begins_with_i.severity = suggestion +dotnet_naming_rule.interface_should_be_begins_with_i.symbols = interface +dotnet_naming_rule.interface_should_be_begins_with_i.style = begins_with_i + +dotnet_naming_rule.types_should_be_pascal_case.severity = suggestion +dotnet_naming_rule.types_should_be_pascal_case.symbols = types +dotnet_naming_rule.types_should_be_pascal_case.style = pascal_case + +dotnet_naming_rule.non_field_members_should_be_pascal_case.severity = suggestion +dotnet_naming_rule.non_field_members_should_be_pascal_case.symbols = non_field_members +dotnet_naming_rule.non_field_members_should_be_pascal_case.style = pascal_case + +# 符号规范 + +dotnet_naming_symbols.interface.applicable_kinds = interface +dotnet_naming_symbols.interface.applicable_accessibilities = public, internal, private, protected, protected_internal, private_protected +dotnet_naming_symbols.interface.required_modifiers = + +dotnet_naming_symbols.types.applicable_kinds = class, struct, interface, enum +dotnet_naming_symbols.types.applicable_accessibilities = public, internal, private, protected, protected_internal, private_protected +dotnet_naming_symbols.types.required_modifiers = + +dotnet_naming_symbols.non_field_members.applicable_kinds = property, event, method +dotnet_naming_symbols.non_field_members.applicable_accessibilities = public, internal, private, protected, protected_internal, private_protected +dotnet_naming_symbols.non_field_members.required_modifiers = + +# 命名样式 + +dotnet_naming_style.pascal_case.required_prefix = +dotnet_naming_style.pascal_case.required_suffix = +dotnet_naming_style.pascal_case.word_separator = +dotnet_naming_style.pascal_case.capitalization = pascal_case + +dotnet_naming_style.begins_with_i.required_prefix = I +dotnet_naming_style.begins_with_i.required_suffix = +dotnet_naming_style.begins_with_i.word_separator = +dotnet_naming_style.begins_with_i.capitalization = pascal_case diff --git a/.gitattributes b/.gitattributes index 8bb03aa..63a09c5 100644 --- a/.gitattributes +++ b/.gitattributes @@ -1,4 +1,4 @@ -############################################################################### +############################################################################### # Set default behavior to automatically normalize line endings. ############################################################################### * text=auto diff --git a/.gitignore b/.gitignore index 25cc348..e910608 100644 --- a/.gitignore +++ b/.gitignore @@ -1,11 +1,11 @@ -## Ignore Visual Studio temporary files, build results, and +## Ignore Visual Studio temporary files, build results, and ## files generated by popular Visual Studio add-ons. -## 忽略Visual Studio临时文件、生成结果和由VisualStudio常用插件生成的文件。 +## Visual StudioʱļɽVisualStudioòɵļ ## ## Get latest from https://github.com/github/gitignore/blob/master/VisualStudio.gitignore # User-specific files -# 用户特定文件 +# ûضļ *.rsuser *.suo *.user @@ -13,12 +13,12 @@ *.sln.docstates # User-specific files (MonoDevelop/Xamarin Studio) -# 用户特定文件 (MonoDevelop/Xamarin Studio) +# ûضļ (MonoDevelop/Xamarin Studio) *.userprefs # Build results -# Build 结果 -# 语法:[abc]匹配任何一个列在方括号中的字符(要么匹配一个 a,要么匹配一个 b,要么匹配一个 c)——如 *.[oa]表明Git忽略所有以 .o 或 .a 结尾的文件 +# Build +# ﷨[abc]ƥκһڷеַҪôƥһ aҪôƥһ bҪôƥһ c *.[oa]Git .o .a βļ [Dd]ebug/ [Dd]ebugPublic/ [Rr]elease/ @@ -33,34 +33,34 @@ bld/ [Ll]og/ # Visual Studio 2015/2017 cache/options directory -# Visual Studio 2015/2017 缓存/选项 目录 +# Visual Studio 2015/2017 /ѡ Ŀ¼ .vs/ # Uncomment if you have tasks that create the project's static files in wwwroot -# 如果您有在wwwroot中创建项目静态文件的任务,请取消注释 +# wwwrootдĿ̬ļȡע #wwwroot/ # Visual Studio 2017 auto generated files -# Visual Studio 2017自动生成的文件 +# Visual Studio 2017Զɵļ Generated\ Files/ # MSTest test Results -# MSTest测试结果 +# MSTestԽ [Tt]est[Rr]esult*/ [Bb]uild[Ll]og.* # NUNIT -# NUnit 是 JUnit 的 .NET 版,支持所有 .NET 语言,完全使用 C# 编写,并进行完全重新设计以利用很多高级的 .NET 语言特性,例如定制属性以及其他相关的反射功能。 +# NUnit JUnit .NET 棬֧ .NET ԣȫʹ C# дȫúܶ߼ .NET ԣ綨Լصķ书ܡ *.VisualState.xml TestResult.xml # Build Results of an ATL Project -# ATL项目的生成结果 +# ATLĿɽ [Dd]ebugPS/ [Rr]eleasePS/ dlldata.c # Benchmark Results -# Benchmark结果 +# Benchmark BenchmarkDotNet.Artifacts/ # .NET Core @@ -69,11 +69,11 @@ project.fragment.lock.json artifacts/ # StyleCop -# 代码检测工具 +# ⹤ StyleCopReport.xml # Files built by Visual Studio -# Visual Studio built的文件 +# Visual Studio builtļ *_i.c *_p.c *_h.h @@ -103,11 +103,11 @@ StyleCopReport.xml *.scc # Chutzpah Test files -# Chutzpah测试文件 +# Chutzpahļ _Chutzpah* # Visual C++ cache files -# Visual C++ 缓存文件 +# Visual C++ ļ ipch/ *.aps *.ncb @@ -119,23 +119,23 @@ ipch/ *.VC.VC.opendb # Visual Studio profiler -# 应用程序性能分析工具 +# Ӧóܷ *.psess *.vsp *.vspx *.sap # Visual Studio Trace Files -# Visual Studio 跟踪文件 +# Visual Studio ļ *.e2e # TFS 2012 Local Workspace -# TFS 2012 本地工作区 +# TFS 2012 ع $tf/ # Guidance Automation Toolkit -# 这套工具旨在简化将可重用的代码集成到应用程序的过程,使架构师能将通常需手动执行的一系列开发工作自动化起来。 -# 使用此工具,还能确保重复性的、易出错的开发工作以合理、一致的方式完成,并能缩短软件开发时间。 +# ׹ּڼ򻯽õĴ뼯ɵӦóḶ́ʹܹʦִֶܽͨеһϵпԶ +# ʹô˹ߣȷظԵġ׳ĿԺһµķʽɣʱ䡣 *.gpState # ReSharper is a .NET coding add-in @@ -173,11 +173,11 @@ AutoTest.Net/ .sass-cache/ # Installshield output folder -# Installshield 输出文件夹 +# Installshield ļ [Ee]xpress/ # DocProject is a documentation generator add-in -# DocProject 是一个文档生成器外接程序 +# DocProject һĵӳ DocProject/buildhelp/ DocProject/Help/*.HxT DocProject/Help/*.HxC @@ -195,32 +195,32 @@ publish/ *.azurePubxml # Note: Comment the next line if you want to checkin your web deploy settings, # but database connection strings (with potential passwords) will be unencrypted -# 注意:如果要签入web部署设置,请在下一行添加注释, -# 但是数据库连接字符串(带有可能的密码)将是未加密的 +# ע⣺Ҫǩwebãһעͣ +# ݿַпܵ룩δܵ *.pubxml *.publishproj # Microsoft Azure Web App publish settings. Comment the next line if you want to # checkin your Azure Web App publish settings, but sensitive information contained # in these scripts will be unencrypted -# Microsoft Azure Web App发布设置。 -# 如果您想签入Azure Web App发布设置,请在下一行添加注释,但这些脚本中包含的敏感信息将不加密 +# Microsoft Azure Web Appá +# ǩAzure Web AppãһעͣЩűаϢ PublishScripts/ # NuGet Packages -# NuGet包 +# NuGet *.nupkg # The packages folder can be ignored because of Package Restore -# 由于Package Restore,包文件夹可以忽略 +# Package RestoreļпԺ **/[Pp]ackages/* # except build/, which is used as an MSBuild target. -# 除了build/,它用作MSBuild目标。 +# build/MSBuildĿꡣ !**/[Pp]ackages/build/ # Uncomment if necessary however generally it will be regenerated when needed -# 必要时取消注释,但通常需要时会重新生成注释 +# ҪʱȡעͣͨҪʱע #!**/[Pp]ackages/repositories.config # NuGet v3's project.json files produces more ignorable files -# NuGet v3的project.json文件生成更多可忽略的文件 +# NuGet v3project.jsonļɸɺԵļ *.nuget.props *.nuget.targets @@ -233,7 +233,7 @@ ecf/ rcf/ # Windows Store app package directories and files -# Windows应用商店应用程序包目录和文件 +# WindowsӦ̵ӦóĿ¼ļ AppPackages/ BundleArtifacts/ Package.StoreAssociation.xml @@ -241,16 +241,16 @@ _pkginfo.txt *.appx # Visual Studio cache files -# Visual Studio缓存文件 +# Visual Studioļ # files ending in .cache can be ignored -# 可以忽略以.cache结尾的文件 +# Ժ.cacheβļ *.[Cc]ache # but keep track of directories ending in .cache -# 但要跟踪以.cache结尾的目录 +# Ҫ.cacheβĿ¼ !?*.[Cc]ache/ # Others -# 其他 +# ClientBin/ ~$* *~ @@ -262,16 +262,16 @@ ClientBin/ orleans.codegen.cs # Including strong name files can present a security risk -# 包含强名称文件可能会带来安全风险 +# ǿļܻȫ # (https://github.com/github/gitignore/pull/2483#issue-259490424) #*.snk # Since there are multiple workflows, uncomment next line to ignore bower_components -#由于有多个工作流,取消注释下一行以忽略bower_components +#жȡעһԺbower_components # (https://github.com/github/gitignore/pull/1529#issuecomment-104372622) #bower_components/ # ASP.NET Core default setup: bower directory is configured as wwwroot/lib/ and bower restore is true -#ASP.NET核心默认设置:bower目录配置为wwwroot/lib/并且bower restore为true +#ASP.NETĬãbowerĿ¼Ϊwwwroot/lib/bower restoreΪtrue **/wwwroot/lib/ # RIA/Silverlight projects @@ -280,7 +280,7 @@ Generated_Code/ # Backup & report files from converting an old project file # to a newer Visual Studio version. Backup files are not needed, # because we have git ;-) -#将旧项目文件转换为新的Visual Studio版本的备份和报告文件。不需要备份文件,因为我们有git; +#ĿļתΪµVisual Studio汾ıݺͱļҪļΪgit _UpgradeReport_Files/ Backup*/ UpgradeLog*.XML @@ -294,7 +294,7 @@ ServiceFabricBackup/ *.ndf # Business Intelligence projects -# 商业智能项目 +# ҵĿ *.rdl.data *.bim.layout *.bim_*.settings @@ -304,28 +304,28 @@ ServiceFabricBackup/ FakesAssemblies/ # GhostDoc plugin setting file -# GhostDoc插件设置文件 +# GhostDocļ *.GhostDoc.xml # Node.js Tools for Visual Studio -# 用于Visual Studio的Node.js工具 +# Visual StudioNode.js .ntvs_analysis.dat node_modules/ # Visual Studio 6 build log -# Visual Studio 6生成日志 +# Visual Studio 6־ *.plg # Visual Studio 6 workspace options file -# Visual Studio 6工作区选项文件 +# Visual Studio 6ѡļ *.opt # Visual Studio 6 auto-generated workspace file (contains which files were open etc.) -# Visual Studio 6自动生成的工作区文件(包含打开的文件等) +# Visual Studio 6ԶɵĹļ򿪵ļȣ *.vbw # Visual Studio LightSwitch build output -# Visual Studio LightSwitch生成输出 +# Visual Studio LightSwitch **/*.HTMLClient/GeneratedArtifacts **/*.DesktopClient/GeneratedArtifacts **/*.DesktopClient/ModelManifest.xml @@ -334,7 +334,7 @@ node_modules/ _Pvt_Extensions # Paket dependency manager -# Paket依赖关系管理器 +# Paketϵ .paket/paket.exe paket-files/ @@ -353,7 +353,7 @@ __pycache__/ *.pyc # Cake - Uncomment if you are using it -# Cake-如果你正在使用它,请取消注释 +# Cake-ʹȡע # tools/** # !tools/packages.config @@ -361,7 +361,7 @@ __pycache__/ *.tss # Telerik's JustMock configuration file -# Telerik的JustMock配置文件 +# TelerikJustMockļ *.jmconfig # BizTalk build output @@ -372,29 +372,39 @@ __pycache__/ *.xsd.cs # OpenCover UI analysis results -# OpenCover UI分析结果 +# OpenCover UI OpenCover/ # Azure Stream Analytics local run output -# Azure流分析本地运行输出 +# Azure ASALocalRun/ # MSBuild Binary and Structured Log -# MSBuild二进制和结构化日志 +# MSBuildƺͽṹ־ *.binlog # NVidia Nsight GPU debugger configuration file -# NVidia Nsight GPU调试器配置文件 +# NVidia Nsight GPUļ *.nvuser # MFractors (Xamarin productivity tool) working folder -# MFractors(Xamarin生产力工具)工作文件夹 +# MFractorsXamarinߣļ .mfractor/ # Local History for Visual Studio -# Visual Studio 的本地历史记录 +# Visual Studio ıʷ¼ .localhistory/ # BeatPulse healthcheck temp database -# BeatPulse healthcheck 临时数据库 +# BeatPulse healthcheck ʱݿ healthchecksdb + +#macos +*.DS_Store + +#vscode +.ionide +.vscode + +#ifoxcad +#tests/TestConsole/ \ No newline at end of file diff --git a/IFoxCAD.sln b/IFoxCAD.sln index 309172c..a9e0664 100644 --- a/IFoxCAD.sln +++ b/IFoxCAD.sln @@ -1,15 +1,17 @@  Microsoft Visual Studio Solution File, Format Version 12.00 -# Visual Studio Version 16 -VisualStudioVersion = 16.0.31025.194 +# Visual Studio Version 17 +VisualStudioVersion = 17.1.32113.165 MinimumVisualStudioVersion = 10.0.40219.1 Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "IFoxCAD.Cad", "src\IFoxCAD.Cad\IFoxCAD.Cad.csproj", "{D5FAED7C-A99B-4BEE-A745-45442DC44971}" EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "DBTrans.test", "tests\DBTrans.test\DBTrans.test.csproj", "{B1602568-F023-46C7-B635-3CB281F1EC00}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Test", "tests\Test\Test.csproj", "{B1602568-F023-46C7-B635-3CB281F1EC00}" EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "IFoxCAD.WPF", "src\IFoxCAD.WPF\IFoxCAD.WPF.csproj", "{D820D629-1AB3-4BE3-A772-CB753D98CCB8}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "IFoxCAD.Basal", "src\IFoxCAD.Basal\IFoxCAD.Basal.csproj", "{40BF07C4-100A-4810-A27B-4587CFB38E6E}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "IFoxCAD.Basal", "src\IFoxCAD.Basal\IFoxCAD.Basal.csproj", "{40BF07C4-100A-4810-A27B-4587CFB38E6E}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "TestConsole", "tests\TestConsole\TestConsole.csproj", "{E2873F0B-CAD2-45E8-8FF0-C05C0C6AD955}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution @@ -33,6 +35,10 @@ Global {40BF07C4-100A-4810-A27B-4587CFB38E6E}.Debug|Any CPU.Build.0 = Debug|Any CPU {40BF07C4-100A-4810-A27B-4587CFB38E6E}.Release|Any CPU.ActiveCfg = Release|Any CPU {40BF07C4-100A-4810-A27B-4587CFB38E6E}.Release|Any CPU.Build.0 = Release|Any CPU + {E2873F0B-CAD2-45E8-8FF0-C05C0C6AD955}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {E2873F0B-CAD2-45E8-8FF0-C05C0C6AD955}.Debug|Any CPU.Build.0 = Debug|Any CPU + {E2873F0B-CAD2-45E8-8FF0-C05C0C6AD955}.Release|Any CPU.ActiveCfg = Release|Any CPU + {E2873F0B-CAD2-45E8-8FF0-C05C0C6AD955}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE diff --git a/README.md b/README.md index f312887..6a4b012 100644 --- a/README.md +++ b/README.md @@ -2,53 +2,90 @@ #### 介绍 -基于.NET的Cad二次开发类库 +基于.NET的Cad二次开发类库。 + +可以加群交流: + +![ifoxcad用户交流群群二维码](./docs/png/ifoxcad用户交流群群二维码.png) #### 软件架构及相关说明 - [软件架构说明](/docs/关于IFoxCAD的架构说明.md) - - [扩展函数说明](/docs/关于扩展函数的说明.md) -#### 安装教程 +#### 编译 IFox 源码工程 -1. vs新建net standord 类库 -2. 修改项目TargetFramework为net45,保存重加载项目 -3. 右键项目,管理nuget程序包,搜索ifoxcad,安装最新版就可以了 +由于vs2022抛弃了某几个net版本,所以我们同时安装vs2019和vs2022,然后使用vs2022; +其中的原因是vs2019拥有全部net版本,而vs2022拥有最新的分析器和语法. -#### 使用说明 +#### IFoxCad 项目模版 -1. 快速入门 +可以在vs扩展菜单-管理扩展中搜索ifoxcad,即可安装项目模板。使用项目模版可以方便的创建支持多目标多版本的使用ifoxcad类库的项目和类。如果无法在vs的市场里下载,就去上面的QQ群里下载。 - - 打开vs,新建一个standard类型的类库项目,修改项目文件里的`netcore2.0`为`NET45`。其中的net45,可以改为NET35以上的标准TFM(如:net35、net40、net45、net46、net47等等)。同时可以指定多版本。具体的详细的教程见 [VS通过添加不同引用库,建立多条件编译]( https://www.yuque.com/vicwjb/zqpcd0/ufbwyl)。 - - 右键项目文件,选择管理nuget程序包。 - - 在nuget程序里搜索**ifoxcad**,直接选择最新的版本,然后点击安装**IFoxCAD.Cad**,nuget会自动安装ifoxcad依赖的库。 - - 添加引用 +#### 安装教程 - ```c# - using Autodesk.AutoCAD.ApplicationServices; - using Autodesk.AutoCAD.EditorInput; - using Autodesk.AutoCAD.Runtime; - using Autodesk.AutoCAD.Geometry; - using Autodesk.AutoCAD.DatabaseServices; - using IFoxCAD.Cad; - ``` +1. 新建net standard 类库 +2. 修改项目`.csproj`的`TargetFrameworks`为net45,保存重加载项目,这里需要注意和cad版本对照. +3. 右键项目,管理nuget程序包,搜索ifoxcad,安装最新版就可以了. - - 添加代码 +#### 使用说明 - ```c# - [CommandMethod("hello")] - public void Hello() - { - using var tr = new DBTrans() - var line1 = new Line(new Point3d(0, 0, 0), new Point3d(1, 1, 0)); - tr.CurrentSpace.AddEntity(line1); - } +1. 快速入门 + + - 打开vs,新建一个standard类型的类库项目,**注意,需要选择类型的时候一定要选standard2.0** ![](./docs/png/standard.png) + + - 双击项目,打开项目文件: + + - 修改项目文件里的`netcore2.0`为`NET45`。其中的net45,可以改为NET45以上的标准TFM(如:net45、net46、net47等等)。同时可以指定多版本。具体的详细的教程见 [VS通过添加不同引用库,建立多条件编译]( https://www.yuque.com/vicwjb/zqpcd0/ufbwyl)。 + + - 在 ` xxx ` 中增加 `preview`,主要是为了支持最新的语法,本项目采用了最新的语法编写。项目文件现在的内容类似如下: + + ```xml + + + net45 + preview + + ``` - - 这段代码就是在cad的当前空间内添加了一条直线。 - + + - 右键项目文件,选择管理nuget程序包。![](./docs/png/nuget1.png) + + - 在nuget程序里搜索**ifoxcad**,直接选择最新的版本(如果你是 **net40** 或者 **net35** 的用户,可以安装 **0.1.6** 版本),然后点击安装**IFoxCAD.Cad**,nuget会自动安装ifoxcad依赖的库。(按下图绿色框框里选择浏览,程序包来源选择nuget.org,安装IFoxCAD.Cad包。IFoxCAD.Basal是IFoxCAD.Cad的依赖项会自动安装,如果要开发wpf界面的话,可以安装IFoxCAD.WPF,提供了简单的mvvm支持)![](./docs/png/nuget.png) + + - 添加引用 + + ```c# + using Autodesk.AutoCAD.ApplicationServices; + using Autodesk.AutoCAD.EditorInput; + using Autodesk.AutoCAD.Runtime; + using Autodesk.AutoCAD.Geometry; + using Autodesk.AutoCAD.DatabaseServices; + using IFoxCAD.Cad; + ``` + + - 添加代码 + + ```c# + [CommandMethod("hello")] + public void Hello() + { + using var tr = new DBTrans(); + var line1 = new Line(new Point3d(0, 0, 0), new Point3d(1, 1, 0)); + tr.CurrentSpace.AddEntity(line1); + // 如果你没有添加preview到项目文件里的话:按如下旧语法: + // using(var tr = new DBTrans()) + // { + // var line1 = new Line(new Point3d(0, 0, 0), new Point3d(1, 1, 0)); + // tr.CurrentSpace.AddEntity(line1); + // } + } + ``` + + 这段代码就是在cad的当前空间内添加了一条直线。 + - F6生成,然后打开cad,netload命令将刚刚生成的dll加载。 + - 运行hello命令,然后缩放一下视图,现在一条直线和一个圆已经显示在屏幕上了。 2. [事务管理器用法](/docs/DBTrans.md) @@ -60,23 +97,62 @@ 5. [WPF支持](/docs/WPF.md) 6. 天秀的自动加载与初始化 - - 为了将程序集的初始化和通过写注册表的方式实现自动加载统一设置,减少每次重复的工作量,内裤提供了`AutoRegAssem`抽象类来完成此功能,只要在需要初始化的类继承`AutoRegAssem`类,然后实现`Initialize()` 和`Terminate()`两个函数就可以了。特别强调的是,一个程序集里只能有一个类继承,不管是不是同一个命名空间。 - + + 为了将程序集的初始化和通过写注册表的方式实现自动加载统一设置,减少每次重复的工作量,类裤提供了`AutoRegAssem`抽象类来完成此功能,只要在需要初始化的类继承`AutoRegAssem`类,然后实现`Initialize()` 和`Terminate()`两个函数就可以了。 + 特别强调的是,一个程序集里只能有一个类继承,不管是不是同一个命名空间。 + + 但是为了满足开闭原则,使用特性进行分段初始化是目前最佳选择 + ```c# - public class Test : AutoRegAssem //继承 + using Autodesk.AutoCAD.Runtime; + using IFoxCAD.Cad; + using System; + using System.Reflection; + + /* + * 自动执行接口 + * 这里必须要实现一次这个接口,才能使用 IFoxInitialize 特性进行自动执行 + */ + public class CmdINI : AutoRegAssem { - public override void Initialize() //实现接口函数 - { - throw new NotImplementedException(); + // 这里可以写任何普通的函数,也可以写下面 AutoTest 类里的实现了 IFoxInitialize 特性的初始化函数 + // 继承AutoRegAssem的主要作用是写注册表用来自动加载dll,同时执行实现了 IFoxInitialize 特性的函数 + // 注意这里的自动执行是在cad启动后,加载了dll之后执行,而不是运行命令后执行。 + + [IFoxInitialize] + public void InitOne() + { + //TODO 你想在加载dll之后自动执行的函数 + // 可以随便在哪里类里 可以多次实现 IFoxInitialize 特性 } - public override void Terminate() //实现接口函数 + + } + + //其他的类中的函数: + //实现自动接口之后,在任意一个函数上面使用此特性,减少每次改动 CmdINI 类 + public class AutoTest + { + [IFoxInitialize] + public void Initialize() + { + //TODO 你想在加载dll之后自动执行的函数 + } + [IFoxInitialize] + public void InitTwo() + { + //TODO 你想在加载dll之后自动执行的函数 + // 可以随便在哪里类里 可以多次实现 IFoxInitialize 特性 + } + [IFoxInitialize(isInitialize: false)] // 特性的参数为false的时候就表示卸载时执行的函数 + public void Terminate() { - throw new NotImplementedException(); + //TODO 你想在关闭cad时自动执行的函数 } } ``` - + + + 7. 天秀的打开模式提权 由于cad的对象是有打开模式,是否可写等等,为了安全起见,在处理对象时,一般是用读模式打开,然后需要写数据的时候在提权为写模式,然后在降级到读模式,但是这个过程中,很容易漏掉某些步骤,然后cad崩溃。为了处理这些情况,内裤提供了提权类来保证读写模式的有序转换。 @@ -88,4 +164,5 @@ } //关闭事务自动处理读写模式 ``` -8. 未完待续。。。。 \ No newline at end of file +8. 未完待续。。。。 + diff --git a/docs/DBTrans.md b/docs/DBTrans.md index 29f88dc..5e75860 100644 --- a/docs/DBTrans.md +++ b/docs/DBTrans.md @@ -59,7 +59,7 @@ using (DBTrans tr = new DBTrans()) // 第一步,开启事务 - **向符号表里添加元素** ```c# - using (DBTransaction tr = new DBTransaction()) + using (DBTrans tr = new DBTrans()) { // 第一步,开启事务 var layerTable = tr.LayerTable; // 第二步,获取图层表 @@ -75,7 +75,7 @@ using (DBTrans tr = new DBTrans()) // 第一步,开启事务 想要添加和获取符号表内的某个元素非常的简单: ```c# - using (DBTransaction tr = new DBTransaction()) // 第一步,开启事务 + using (DBTrans tr = new DBTrans()) // 第一步,开启事务 { var layerTable = tr.LayerTable; // 第二步,获取图层表 layerTable.Add("1"); // 第三步,添加名为“1”的图层,即新建图层 diff --git a/docs/WPF.md b/docs/WPF.md index 08d7c28..73f4f11 100644 --- a/docs/WPF.md +++ b/docs/WPF.md @@ -225,7 +225,7 @@ public class TestViewModel : ViewModelBase 首先是在xaml里引入命名空间。 -`xmlns:eb="clr-namespace:IFoxCad.WPF;assembly=IFoxCad"` +`xmlns:eb="clr-namespace:IFoxCAD.WPF;assembly=IFoxCAD.WPF"` 然后 diff --git "a/docs/png/ifoxcad\347\224\250\346\210\267\344\272\244\346\265\201\347\276\244\347\276\244\344\272\214\347\273\264\347\240\201.png" "b/docs/png/ifoxcad\347\224\250\346\210\267\344\272\244\346\265\201\347\276\244\347\276\244\344\272\214\347\273\264\347\240\201.png" new file mode 100644 index 0000000000000000000000000000000000000000..a9d08c483b80f21d4b0ac6a0afe80f9b6b4233e6 GIT binary patch literal 20425 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Ð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 literal 0 HcmV?d00001 diff --git a/docs/png/nuget1.png b/docs/png/nuget1.png new file mode 100644 index 0000000000000000000000000000000000000000..91983ffef3b246ea796296b14fe09963663de184 GIT binary patch literal 27373 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 literal 0 HcmV?d00001 diff --git a/docs/png/standard.png b/docs/png/standard.png new file mode 100644 index 0000000000000000000000000000000000000000..dfc34fecb32120f54436115208a4abaea2dac516 GIT binary patch literal 148673 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%0DO&7ix%Xw#WJ4GhT6iE|`sg)>zmh51hyQTaFv=}2 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 literal 0 HcmV?d00001 diff --git "a/docs/\345\205\263\344\272\216IFoxCAD\347\232\204\346\236\266\346\236\204\350\257\264\346\230\216.md" "b/docs/\345\205\263\344\272\216IFoxCAD\347\232\204\346\236\266\346\236\204\350\257\264\346\230\216.md" index d828220..69a9b79 100644 --- "a/docs/\345\205\263\344\272\216IFoxCAD\347\232\204\346\236\266\346\236\204\350\257\264\346\230\216.md" +++ "b/docs/\345\205\263\344\272\216IFoxCAD\347\232\204\346\236\266\346\236\204\350\257\264\346\230\216.md" @@ -7,10 +7,10 @@ IFoxCAD是基于NFOX类库的重制版,主要是提供一个最小化的内核 ## 一、组织结构图 - IFoxCAD - - IFoxCAD.Cad - cad相关的类库 + - IFoxCAD.Basal - cad以外常用的类库 - LinqEx - linq扩展类 - LoopList - 环链表 - - IFoxCAD.Basal - cad以外常用的类库 + - IFoxCAD.Cad - cad相关的类库 - Runtime - 包含系统级别的功能 - AcadVersion - cad版本号类 - AssemInfo - 程序集信息 @@ -45,7 +45,7 @@ IFoxCAD是基于NFOX类库的重制版,主要是提供一个最小化的内核 ### 2.2 关于DBTrans类的具体构成元素的意义 -DBTrans类里基本的封装就是Transaction,然后是Document、Database、Editor、符号表、命名字典等,而抓这些其实都是cad二次开发关于图元操作经常打交道的概念。 +DBTrans类里基本的封装就是Transaction,然后是Document、Database、Editor、符号表、命名字典等,而这些其实都是cad二次开发关于图元操作经常打交道的概念。 DBTrans的每个实例都具有这些属性,而这些属性就对应于cad的相关类库,通过这些属性就可以对数据进行相应的操作。特别是符号表中最常用的就是块表,通过对块表的操作来实现添加图元等。 @@ -109,7 +109,7 @@ DBTrans的每个实例都具有这些属性,而这些属性就对应于cad的 - Has --- 判断符号表是否有符号表记录的函数 - 。。。 -特殊说明:当符号表为块表时,上述函数实际操作的是块定义、属性定义等。所以为了添加图元,需要特殊写法。 +特殊说明:当符号表为块表时,上述函数实际操作的是块定义、属性定义等。所以为了添加图元,需要特殊写法,原因在于cad的实体都是存在符号表记录里的,通常为模型这个块表记录。 # 慢慢完善,想到哪写到哪。。。 diff --git a/src/IFoxCAD.Basal/ArrayEx.cs b/src/IFoxCAD.Basal/ArrayEx.cs new file mode 100644 index 0000000..34fb4b7 --- /dev/null +++ b/src/IFoxCAD.Basal/ArrayEx.cs @@ -0,0 +1,50 @@ +namespace IFoxCAD.Basal +{ + /* + * 由于linq的函数大部分带有状态机,而cad是一个单机程序, + * 使用状态机会变得缓慢,因此我们设计的时候着重于时间优化, + * 本工具类在着重于数组遍历时候替代linq + */ + public static class ArrayEx + { + ///

+ /// 合并数组 + /// + /// + /// + public static T[] Combine2(this T[] a, T[] b) + { + var c = new T[a.Length + b.Length]; + Array.Copy(a, 0, c, 0, a.Length); + Array.Copy(b, 0, c, a.Length, b.Length); + return c; + } + + /// + /// 一维数组消重,此函数建议更改为: + /// set = new(); + /// foreach (var item in listInOut) + /// set.Add(item); + /// ]]> + /// + /// + /// 传入有重复成员的数组,传出没有重复的 + /// 传出参数1:数组开头;传出参数2:数组结尾;返回值比较结尾为就移除 + [Obsolete] + public static void Deduplication(List listInOut, Func func) + { + for (int i = 0; i < listInOut.Count; i++) + { + var first = listInOut[i]; + for (int j = listInOut.Count - 1; j > i; j--) + { + var last = listInOut[j]; + if (func(first, last)) + listInOut.RemoveAt(j); + } + } + } + + } +} diff --git a/src/IFoxCAD.Basal/CLS/Index.cs b/src/IFoxCAD.Basal/CLS/Index.cs new file mode 100644 index 0000000..97b51e5 --- /dev/null +++ b/src/IFoxCAD.Basal/CLS/Index.cs @@ -0,0 +1,148 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +namespace System; + +using System.Runtime.CompilerServices; + +/// Represent a type can be used to index a collection either from the start or the end. +/// +/// Index is used by the C# compiler to support the new index syntax +/// +/// int[] someArray = new int[5] { 1, 2, 3, 4, 5 } ; +/// int lastElement = someArray[^1]; // lastElement = 5 +/// +/// +public readonly struct Index : IEquatable +{ + private readonly int _value; + + /// Construct an Index using a value and indicating if the index is from the start or from the end. + /// The index value. it has to be zero or positive number. + /// Indicating if the index is from the start or from the end. + /// + /// If the Index constructed from the end, index value 1 means pointing at the last element and index value 0 means pointing at beyond last element. + /// +#if NET45 + [MethodImpl(MethodImplOptions.AggressiveInlining)] +#endif + public Index(int value, bool fromEnd = false) + { + if (value < 0) + { + throw new ArgumentOutOfRangeException(nameof(value), "value must be non-negative"); + } + + if (fromEnd) + _value = ~value; + else + _value = value; + } + + // The following private constructors mainly created for perf reason to avoid the checks + private Index(int value) + { + _value = value; + } + + /// Create an Index pointing at first element. + public static Index Start => new(0); + + /// Create an Index pointing at beyond last element. + public static Index End => new(~0); + + /// Create an Index from the start at the position indicated by the value. + /// The index value from the start. +#if NET45 + [MethodImpl(MethodImplOptions.AggressiveInlining)] +#endif + public static Index FromStart(int value) + { + if (value < 0) + { + throw new ArgumentOutOfRangeException(nameof(value), "value must be non-negative"); + } + + return new Index(value); + } + + /// Create an Index from the end at the position indicated by the value. + /// The index value from the end. +#if NET45 + [MethodImpl(MethodImplOptions.AggressiveInlining)] +#endif + public static Index FromEnd(int value) + { + if (value < 0) + { + throw new ArgumentOutOfRangeException(nameof(value), "value must be non-negative"); + } + + return new Index(~value); + } + + /// Returns the index value. + public int Value + { + get + { + if (_value < 0) + return ~_value; + else + return _value; + } + } + + /// Indicates whether the index is from the start or the end. + public bool IsFromEnd => _value < 0; + + /// Calculate the offset from the start using the giving collection length. + /// The length of the collection that the Index will be used with. length has to be a positive value + /// + /// For performance reason, we don't validate the input length parameter and the returned offset value against negative values. + /// we don't validate either the returned offset is greater than the input length. + /// It is expected Index will be used with collections which always have non negative length/count. If the returned offset is negative and + /// then used to index a collection will get out of range exception which will be same affect as the validation. + /// +#if NET45 + [MethodImpl(MethodImplOptions.AggressiveInlining)] +#endif + public int GetOffset(int length) + { + int offset = _value; + if (IsFromEnd) + { + // offset = length - (~value) + // offset = length + (~(~value) + 1) + // offset = length + value + 1 + + offset += length + 1; + } + return offset; + } + + /// Indicates whether the current Index object is equal to another object of the same type. + /// An object to compare with this object + public override bool Equals(object? value) => value is Index index && _value == index._value; + + /// Indicates whether the current Index object is equal to another Index object. + /// An object to compare with this object + public bool Equals(Index other) => _value == other._value; + + /// Returns the hash code for this instance. + public override int GetHashCode() => _value; + + /// Converts integer number to an Index. + public static implicit operator Index(int value) => FromStart(value); + + /// Converts the value of the current Index object to its equivalent string representation. + public override string ToString() + { + if (IsFromEnd) + return "^" + ((uint)Value).ToString(); + + return ((uint)Value).ToString(); + } +} + diff --git a/src/IFoxCAD.Basal/CLS/Range.cs b/src/IFoxCAD.Basal/CLS/Range.cs new file mode 100644 index 0000000..221167b --- /dev/null +++ b/src/IFoxCAD.Basal/CLS/Range.cs @@ -0,0 +1,103 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +namespace System; + +using System.Runtime.CompilerServices; + + +/// Represent a range has start and end indexes. +/// +/// Range is used by the C# compiler to support the range syntax. +/// +/// int[] someArray = new int[5] { 1, 2, 3, 4, 5 }; +/// int[] subArray1 = someArray[0..2]; // { 1, 2 } +/// int[] subArray2 = someArray[1..^0]; // { 2, 3, 4, 5 } +/// +/// +public readonly struct Range : IEquatable +{ + /// Represent the inclusive start index of the Range. + public Index Start { get; } + + /// Represent the exclusive end index of the Range. + public Index End { get; } + + /// Construct a Range object using the start and end indexes. + /// Represent the inclusive start index of the range. + /// Represent the exclusive end index of the range. + public Range(Index start, Index end) + { + Start = start; + End = end; + } + + /// Indicates whether the current Range object is equal to another object of the same type. + /// An object to compare with this object + public override bool Equals(object? value) => + value is Range r && + r.Start.Equals(Start) && + r.End.Equals(End); + + /// Indicates whether the current Range object is equal to another Range object. + /// An object to compare with this object + public bool Equals(Range other) => other.Start.Equals(Start) && other.End.Equals(End); + + /// Returns the hash code for this instance. + public override int GetHashCode() + { + return Start.GetHashCode() * 31 + End.GetHashCode(); + } + + /// Converts the value of the current Range object to its equivalent string representation. + public override string ToString() + { + return Start + ".." + End; + } + + /// Create a Range object starting from start index to the end of the collection. + public static Range StartAt(Index start) => new(start, Index.End); + + /// Create a Range object starting from first element in the collection to the end Index. + public static Range EndAt(Index end) => new(Index.Start, end); + + /// Create a Range object starting from first element to the end. + public static Range All => new(Index.Start, Index.End); + + /// Calculate the start offset and length of range object using a collection length. + /// The length of the collection that the range will be used with. length has to be a positive value. + /// + /// For performance reason, we don't validate the input length parameter against negative values. + /// It is expected Range will be used with collections which always have non negative length/count. + /// We validate the range is inside the length scope though. + /// +#if NET45 + [MethodImpl(MethodImplOptions.AggressiveInlining)] +#endif + //[CLSCompliant(false)] + public (int Offset, int Length) GetOffsetAndLength(int length) + { + int start; + Index startIndex = Start; + if (startIndex.IsFromEnd) + start = length - startIndex.Value; + else + start = startIndex.Value; + + int end; + Index endIndex = End; + if (endIndex.IsFromEnd) + end = length - endIndex.Value; + else + end = endIndex.Value; + + if ((uint)end > (uint)length || (uint)start > (uint)end) + { + throw new ArgumentOutOfRangeException(nameof(length)); + } + + return (start, end - start); + } +} + diff --git a/src/IFoxCAD.Basal/CLS/RuntimeHelpers.cs b/src/IFoxCAD.Basal/CLS/RuntimeHelpers.cs new file mode 100644 index 0000000..b5698b4 --- /dev/null +++ b/src/IFoxCAD.Basal/CLS/RuntimeHelpers.cs @@ -0,0 +1,41 @@ +//#if NET35 +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +namespace System.Runtime.CompilerServices; + +public static class RuntimeHelpers +{ + /// + /// Slices the specified array using the specified range. + /// + public static T[] GetSubArray(T[] array!!, Range range) + { + (int offset, int length) = range.GetOffsetAndLength(array.Length); + + if (default(T)! != null || typeof(T[]) == array.GetType()) // TODO-NULLABLE: default(T) == null warning (https://github.com/dotnet/roslyn/issues/34757) + { + // We know the type of the array to be exactly T[]. + if (length == 0) + { + //return Array.Empty(); + return new T[0]; + } + + var dest = new T[length]; + Array.Copy(array, offset, dest, 0, length); + return dest; + } + else + { + // The array is actually a U[] where U:T. + T[] dest = (T[])Array.CreateInstance(array.GetType().GetElementType()!, length); + Array.Copy(array, offset, dest, 0, length); + return dest; + } + } + + +} +//#endif \ No newline at end of file diff --git a/src/IFoxCAD.Basal/CLS/TupleElementNamesAttribute.cs b/src/IFoxCAD.Basal/CLS/TupleElementNamesAttribute.cs new file mode 100644 index 0000000..bb0716d --- /dev/null +++ b/src/IFoxCAD.Basal/CLS/TupleElementNamesAttribute.cs @@ -0,0 +1,48 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +namespace System.Runtime.CompilerServices; + +/// +/// Indicates that the use of on a member is meant to be treated as a tuple with element names. +/// +[AttributeUsage(AttributeTargets.Field | AttributeTargets.Parameter | AttributeTargets.Property | AttributeTargets.ReturnValue | AttributeTargets.Class | AttributeTargets.Struct | AttributeTargets.Event)] +public sealed class TupleElementNamesAttribute : Attribute +{ + private readonly string[] _transformNames; + + /// + /// Initializes a new instance of the class. + /// + /// + /// Specifies, in a pre-order depth-first traversal of a type's + /// construction, which occurrences are + /// meant to carry element names. + /// + /// + /// This constructor is meant to be used on types that contain an + /// instantiation of that contains + /// element names. For instance, if C is a generic type with + /// two type parameters, then a use of the constructed type C{, might be intended to + /// treat the first type argument as a tuple with element names and the + /// second as a tuple without element names. In which case, the + /// appropriate attribute specification should use a + /// transformNames value of { "name1", "name2", null, null, + /// null }. + /// + public TupleElementNamesAttribute(string[] transformNames!!) + { + _transformNames = transformNames; + } + + /// + /// Specifies, in a pre-order depth-first traversal of a type's + /// construction, which elements are + /// meant to carry element names. + /// + public IList TransformNames => _transformNames; +} \ No newline at end of file diff --git a/src/IFoxCAD.Basal/CLS/ValueTuple.cs b/src/IFoxCAD.Basal/CLS/ValueTuple.cs new file mode 100644 index 0000000..bece1ed --- /dev/null +++ b/src/IFoxCAD.Basal/CLS/ValueTuple.cs @@ -0,0 +1,2144 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +//#pragma warning disable SA1141 // explicitly not using tuple syntax in tuple implementation + + +using System.Diagnostics; +using System.Numerics.Hashing; +/* + * 惊惊: + * 首先是因为有人想要编译的时候只形成一个dll,然后把元组塞入IFox,同时也补充了net35没有元组的遗憾. + * 而利用nuget元组包必然会形成依赖地狱. + * + * 如果你的工程使用了nuget元组包,就造成了必须要剔除IFox. + * + * 改IFox的元组命名空间倒是可以分离两者,但是 vs编译器 无法识别带其他命名空间的元组. + * 所以元组本身就是冲突的,需要把其他元组卸载掉,由IFox提供. + */ + +#if NET35 +namespace System.Collections +{ + public interface IStructuralComparable + { + int CompareTo(object? other, IComparer comparer); + } + public interface IStructuralEquatable + { + bool Equals(object? other, IEqualityComparer comparer); + int GetHashCode(IEqualityComparer comparer); + } +} +#endif + + + +namespace System.Numerics.Hashing +{ + internal static class HashHelpers + { + public static readonly int RandomSeed = Guid.NewGuid().GetHashCode(); + + public static int Combine(int h1, int h2) + { + unchecked + { + // RyuJIT optimizes this to use the ROL instruction + // Related GitHub pull request: dotnet/coreclr#1830 + + // RyuJIT 对此进行了优化以使用 ROL 指令 + // 相关 GitHub 拉取请求:dotnet/coreclr#1830 + uint rol5 = ((uint)h1 << 5) | ((uint)h1 >> 27); + return ((int)rol5 + h1) ^ h2; + } + } + } +} + + + + +namespace System +{ + //internal static class SR + internal sealed partial class SR + { + // public const string ArgumentException_ValueTupleIncorrectType = "The parameter should be a ValueTuple type of appropriate arity."; + // public const string ArgumentException_ValueTupleLastArgumentNotAValueTuple = "The TRest type argument of ValueTuple`8 must be a ValueTuple."; + public const string ArgumentException_ValueTupleIncorrectType = "该参数应该是适当数量的 ValueTuple 类型."; + public const string ArgumentException_ValueTupleLastArgumentNotAValueTuple = "ValueTuple`8 的 TREST 类型参数必须是 ValueTuple."; + } + + // Helper so we can call some tuple methods recursively without knowing the underlying types. + /// + /// 帮助器,因此我们可以在不知道底层类型的情况下递归调用一些元组方法. + /// + internal interface ITupleInternal + { + int GetHashCode(IEqualityComparer comparer); + int Size { get; } + string ToStringEnd(); + } + + + // The ValueTuple types (from arity 0 to 8) comprise the runtime implementation that underlies tuples in C# and struct tuples in F#. + // Aside from created via language syntax, they are most easily created via the ValueTuple.Create factory methods. + // The System.ValueTuple types differ from the System.Tuple types in that: + // - they are structs rather than classes, + // - they are mutable rather than readonly, and + // - their members (such as Item1, Item2, etc) are fields rather than properties. + /// + /// ValueTuple 类型(从 arity 0 到 8)包含运行时实现,它是 C# 中的元组和 F# 中的结构元组的基础. + /// 除了通过语言语法创建之外,它们最容易通过 ValueTuple.Create 工厂方法创建. + /// System.ValueTuple 类型与 System.Tuple 类型的不同之处在于: + /// - 它们是结构而不是类, + /// - 它们是可变的而不是只读的,并且 + /// - 它们的成员(例如 Item1、Item2 等)是字段而不是属性. + /// + public struct ValueTuple + : IEquatable, IStructuralEquatable, IStructuralComparable, IComparable, IComparable, ITupleInternal + { + /// + /// Returns a value that indicates whether the current instance is equal to a specified object. + /// + /// The object to compare with this instance. + /// if is a . + public override bool Equals(object obj) + { + return obj is ValueTuple; + } + + /// Returns a value indicating whether this instance is equal to a specified value. + /// An instance to compare to this instance. + /// true if has the same value as this instance; otherwise, false. + public bool Equals(ValueTuple other) + { + return true; + } + + bool IStructuralEquatable.Equals(object? other, IEqualityComparer comparer) + { + return other is ValueTuple; + } + + int IComparable.CompareTo(object other) + { + if (other == null) return 1; + + if (other is not ValueTuple) + { + throw new ArgumentException(SR.ArgumentException_ValueTupleIncorrectType, nameof(other)); + } + + return 0; + } + + /// Compares this instance to a specified instance and returns an indication of their relative values. + /// An instance to compare. + /// + /// A signed number indicating the relative values of this instance and . + /// Returns less than zero if this instance is less than , zero if this + /// instance is equal to , and greater than zero if this instance is greater + /// than . + /// + public int CompareTo(ValueTuple other) + { + return 0; + } + + int IStructuralComparable.CompareTo(object? other, IComparer comparer) + { + if (other == null) return 1; + + if (other is not ValueTuple) + { + throw new ArgumentException(SR.ArgumentException_ValueTupleIncorrectType, nameof(other)); + } + + return 0; + } + + /// Returns the hash code for this instance. + /// A 32-bit signed integer hash code. + public override int GetHashCode() + { + return 0; + } + + int IStructuralEquatable.GetHashCode(IEqualityComparer comparer) + { + return 0; + } + + int ITupleInternal.GetHashCode(IEqualityComparer comparer) + { + return 0; + } + + /// + /// Returns a string that represents the value of this instance. + /// + /// The string representation of this instance. + /// + /// The string returned by this method takes the form (). + /// + public override string ToString() + { + return "()"; + } + + string ITupleInternal.ToStringEnd() + { + return ")"; + } + + int ITupleInternal.Size => 0; + + /// Creates a new struct 0-tuple. + /// A 0-tuple. + public static ValueTuple Create() => new(); + + /// Creates a new struct 1-tuple, or singleton. + /// The type of the first component of the tuple. + /// The value of the first component of the tuple. + /// A 1-tuple (singleton) whose value is (item1). + public static ValueTuple Create(T1 item1) => new(item1); + + /// Creates a new struct 2-tuple, or pair. + /// The type of the first component of the tuple. + /// The type of the second component of the tuple. + /// The value of the first component of the tuple. + /// The value of the second component of the tuple. + /// A 2-tuple (pair) whose value is (item1, item2). + public static ValueTuple Create(T1 item1, T2 item2) => new(item1, item2); + + /// Creates a new struct 3-tuple, or triple. + /// The type of the first component of the tuple. + /// The type of the second component of the tuple. + /// The type of the third component of the tuple. + /// The value of the first component of the tuple. + /// The value of the second component of the tuple. + /// The value of the third component of the tuple. + /// A 3-tuple (triple) whose value is (item1, item2, item3). + public static ValueTuple Create(T1 item1, T2 item2, T3 item3) => + new(item1, item2, item3); + + /// Creates a new struct 4-tuple, or quadruple. + /// The type of the first component of the tuple. + /// The type of the second component of the tuple. + /// The type of the third component of the tuple. + /// The type of the fourth component of the tuple. + /// The value of the first component of the tuple. + /// The value of the second component of the tuple. + /// The value of the third component of the tuple. + /// The value of the fourth component of the tuple. + /// A 4-tuple (quadruple) whose value is (item1, item2, item3, item4). + public static ValueTuple Create(T1 item1, T2 item2, T3 item3, T4 item4) => + new(item1, item2, item3, item4); + + /// Creates a new struct 5-tuple, or quintuple. + /// The type of the first component of the tuple. + /// The type of the second component of the tuple. + /// The type of the third component of the tuple. + /// The type of the fourth component of the tuple. + /// The type of the fifth component of the tuple. + /// The value of the first component of the tuple. + /// The value of the second component of the tuple. + /// The value of the third component of the tuple. + /// The value of the fourth component of the tuple. + /// The value of the fifth component of the tuple. + /// A 5-tuple (quintuple) whose value is (item1, item2, item3, item4, item5). + public static ValueTuple Create(T1 item1, T2 item2, T3 item3, T4 item4, T5 item5) => + new(item1, item2, item3, item4, item5); + + /// Creates a new struct 6-tuple, or sextuple. + /// The type of the first component of the tuple. + /// The type of the second component of the tuple. + /// The type of the third component of the tuple. + /// The type of the fourth component of the tuple. + /// The type of the fifth component of the tuple. + /// The type of the sixth component of the tuple. + /// The value of the first component of the tuple. + /// The value of the second component of the tuple. + /// The value of the third component of the tuple. + /// The value of the fourth component of the tuple. + /// The value of the fifth component of the tuple. + /// The value of the sixth component of the tuple. + /// A 6-tuple (sextuple) whose value is (item1, item2, item3, item4, item5, item6). + public static ValueTuple Create(T1 item1, T2 item2, T3 item3, T4 item4, T5 item5, T6 item6) => + new(item1, item2, item3, item4, item5, item6); + + /// Creates a new struct 7-tuple, or septuple. + /// The type of the first component of the tuple. + /// The type of the second component of the tuple. + /// The type of the third component of the tuple. + /// The type of the fourth component of the tuple. + /// The type of the fifth component of the tuple. + /// The type of the sixth component of the tuple. + /// The type of the seventh component of the tuple. + /// The value of the first component of the tuple. + /// The value of the second component of the tuple. + /// The value of the third component of the tuple. + /// The value of the fourth component of the tuple. + /// The value of the fifth component of the tuple. + /// The value of the sixth component of the tuple. + /// The value of the seventh component of the tuple. + /// A 7-tuple (septuple) whose value is (item1, item2, item3, item4, item5, item6, item7). + public static ValueTuple Create(T1 item1, T2 item2, T3 item3, T4 item4, T5 item5, T6 item6, T7 item7) => + new(item1, item2, item3, item4, item5, item6, item7); + + /// Creates a new struct 8-tuple, or octuple. + /// The type of the first component of the tuple. + /// The type of the second component of the tuple. + /// The type of the third component of the tuple. + /// The type of the fourth component of the tuple. + /// The type of the fifth component of the tuple. + /// The type of the sixth component of the tuple. + /// The type of the seventh component of the tuple. + /// The type of the eighth component of the tuple. + /// The value of the first component of the tuple. + /// The value of the second component of the tuple. + /// The value of the third component of the tuple. + /// The value of the fourth component of the tuple. + /// The value of the fifth component of the tuple. + /// The value of the sixth component of the tuple. + /// The value of the seventh component of the tuple. + /// The value of the eighth component of the tuple. + /// An 8-tuple (octuple) whose value is (item1, item2, item3, item4, item5, item6, item7, item8). + public static ValueTuple> Create(T1 item1, T2 item2, T3 item3, T4 item4, T5 item5, T6 item6, T7 item7, T8 item8) => + new(item1, item2, item3, item4, item5, item6, item7, ValueTuple.Create(item8)); + + internal static int CombineHashCodes(int h1, int h2) + { + return HashHelpers.Combine(HashHelpers.Combine(HashHelpers.RandomSeed, h1), h2); + } + + internal static int CombineHashCodes(int h1, int h2, int h3) + { + return HashHelpers.Combine(CombineHashCodes(h1, h2), h3); + } + + internal static int CombineHashCodes(int h1, int h2, int h3, int h4) + { + return HashHelpers.Combine(CombineHashCodes(h1, h2, h3), h4); + } + + internal static int CombineHashCodes(int h1, int h2, int h3, int h4, int h5) + { + return HashHelpers.Combine(CombineHashCodes(h1, h2, h3, h4), h5); + } + + internal static int CombineHashCodes(int h1, int h2, int h3, int h4, int h5, int h6) + { + return HashHelpers.Combine(CombineHashCodes(h1, h2, h3, h4, h5), h6); + } + + internal static int CombineHashCodes(int h1, int h2, int h3, int h4, int h5, int h6, int h7) + { + return HashHelpers.Combine(CombineHashCodes(h1, h2, h3, h4, h5, h6), h7); + } + + internal static int CombineHashCodes(int h1, int h2, int h3, int h4, int h5, int h6, int h7, int h8) + { + return HashHelpers.Combine(CombineHashCodes(h1, h2, h3, h4, h5, h6, h7), h8); + } + } + + /// Represents a 1-tuple, or singleton, as a value type. + /// The type of the tuple's only component. + public struct ValueTuple + : IEquatable>, IStructuralEquatable, IStructuralComparable, IComparable, IComparable>, ITupleInternal + { + /// + /// The current instance's first component. + /// + public T1 Item1; + + /// + /// Initializes a new instance of the value type. + /// + /// The value of the tuple's first component. + public ValueTuple(T1 item1) + { + Item1 = item1; + } + + /// + /// Returns a value that indicates whether the current instance is equal to a specified object. + /// + /// The object to compare with this instance. + /// if the current instance is equal to the specified object; otherwise, . + /// + /// The parameter is considered to be equal to the current instance under the following conditions: + /// + /// It is a value type. + /// Its components are of the same types as those of the current instance. + /// Its components are equal to those of the current instance. Equality is determined by the default object equality comparer for each component. + /// + /// + public override bool Equals(object obj) + { + return obj is ValueTuple tuple && Equals(tuple); + } + + /// + /// Returns a value that indicates whether the current + /// instance is equal to a specified . + /// + /// The tuple to compare with this instance. + /// if the current instance is equal to the specified tuple; otherwise, . + /// + /// The parameter is considered to be equal to the current instance if each of its field + /// is equal to that of the current instance, using the default comparer for that field's type. + /// + public bool Equals(ValueTuple other) + { + return EqualityComparer.Default.Equals(Item1, other.Item1); + } + + bool IStructuralEquatable.Equals(object? other, IEqualityComparer comparer) + { + if (other == null || other is not ValueTuple) return false; + + var objTuple = (ValueTuple)other; + + return comparer.Equals(Item1, objTuple.Item1); + } + + int IComparable.CompareTo(object other) + { + if (other == null) return 1; + + if (other is not ValueTuple) + { + throw new ArgumentException(SR.ArgumentException_ValueTupleIncorrectType, nameof(other)); + } + + var objTuple = (ValueTuple)other; + + return Comparer.Default.Compare(Item1, objTuple.Item1); + } + + /// Compares this instance to a specified instance and returns an indication of their relative values. + /// An instance to compare. + /// + /// A signed number indicating the relative values of this instance and . + /// Returns less than zero if this instance is less than , zero if this + /// instance is equal to , and greater than zero if this instance is greater + /// than . + /// + public int CompareTo(ValueTuple other) + { + return Comparer.Default.Compare(Item1, other.Item1); + } + + int IStructuralComparable.CompareTo(object? other, IComparer comparer) + { + if (other == null) return 1; + + if (other is not ValueTuple) + { + throw new ArgumentException(SR.ArgumentException_ValueTupleIncorrectType, nameof(other)); + } + + var objTuple = (ValueTuple)other; + + return comparer.Compare(Item1, objTuple.Item1); + } + + /// + /// Returns the hash code for the current instance. + /// + /// A 32-bit signed integer hash code. + public override int GetHashCode() + { + return EqualityComparer.Default.GetHashCode(Item1); + } + + int IStructuralEquatable.GetHashCode(IEqualityComparer comparer) + { + return comparer.GetHashCode(Item1); + } + + int ITupleInternal.GetHashCode(IEqualityComparer comparer) + { + return comparer.GetHashCode(Item1); + } + + /// + /// Returns a string that represents the value of this instance. + /// + /// The string representation of this instance. + /// + /// The string returned by this method takes the form (Item1), + /// where Item1 represents the value of . If the field is , + /// it is represented as . + /// + public override string ToString() + { + return "(" + Item1?.ToString() + ")"; + } + + string ITupleInternal.ToStringEnd() + { + return Item1?.ToString() + ")"; + } + + int ITupleInternal.Size => 1; + } + + /// + /// Represents a 2-tuple, or pair, as a value type. + /// + /// The type of the tuple's first component. + /// The type of the tuple's second component. + public struct ValueTuple + : IEquatable>, IStructuralEquatable, IStructuralComparable, IComparable, IComparable>, ITupleInternal + { + /// + /// The current instance's first component. + /// + public T1 Item1; + + /// + /// The current instance's first component. + /// + public T2 Item2; + + /// + /// Initializes a new instance of the value type. + /// + /// The value of the tuple's first component. + /// The value of the tuple's second component. + public ValueTuple(T1 item1, T2 item2) + { + Item1 = item1; + Item2 = item2; + } + + /// + /// Returns a value that indicates whether the current instance is equal to a specified object. + /// + /// The object to compare with this instance. + /// if the current instance is equal to the specified object; otherwise, . + /// + /// + /// The parameter is considered to be equal to the current instance under the following conditions: + /// + /// It is a value type. + /// Its components are of the same types as those of the current instance. + /// Its components are equal to those of the current instance. Equality is determined by the default object equality comparer for each component. + /// + /// + public override bool Equals(object obj) + { + return obj is ValueTuple tuple && Equals(tuple); + } + + /// + /// Returns a value that indicates whether the current instance is equal to a specified . + /// + /// The tuple to compare with this instance. + /// if the current instance is equal to the specified tuple; otherwise, . + /// + /// The parameter is considered to be equal to the current instance if each of its fields + /// are equal to that of the current instance, using the default comparer for that field's type. + /// + public bool Equals(ValueTuple other) + { + return EqualityComparer.Default.Equals(Item1, other.Item1) + && EqualityComparer.Default.Equals(Item2, other.Item2); + } + + /// + /// Returns a value that indicates whether the current instance is equal to a specified object based on a specified comparison method. + /// + /// The object to compare with this instance. + /// An object that defines the method to use to evaluate whether the two objects are equal. + /// if the current instance is equal to the specified object; otherwise, . + /// + /// + /// This member is an explicit interface member implementation. It can be used only when the + /// instance is cast to an interface. + /// + /// The implementation is called only if other is not , + /// and if it can be successfully cast (in C#) or converted (in Visual Basic) to a + /// whose components are of the same types as those of the current instance. The IStructuralEquatable.Equals(Object, IEqualityComparer) method + /// first passes the values of the objects to be compared to the + /// implementation. If this method call returns , the method is + /// called again and passed the values of the two instances. + /// + bool IStructuralEquatable.Equals(object? other, IEqualityComparer comparer) + { + if (other is null or not ValueTuple) return false; + + var objTuple = (ValueTuple)other; + + return comparer.Equals(Item1, objTuple.Item1) + && comparer.Equals(Item2, objTuple.Item2); + } + + int IComparable.CompareTo(object other) + { + if (other == null) return 1; + + if (other is not ValueTuple) + { + throw new ArgumentException(SR.ArgumentException_ValueTupleIncorrectType, nameof(other)); + } + + return CompareTo((ValueTuple)other); + } + + /// Compares this instance to a specified instance and returns an indication of their relative values. + /// An instance to compare. + /// + /// A signed number indicating the relative values of this instance and . + /// Returns less than zero if this instance is less than , zero if this + /// instance is equal to , and greater than zero if this instance is greater + /// than . + /// + public int CompareTo(ValueTuple other) + { + int c = Comparer.Default.Compare(Item1, other.Item1); + if (c != 0) return c; + + return Comparer.Default.Compare(Item2, other.Item2); + } + + int IStructuralComparable.CompareTo(object? other, IComparer comparer) + { + if (other == null) return 1; + + if (other is not ValueTuple) + { + throw new ArgumentException(SR.ArgumentException_ValueTupleIncorrectType, nameof(other)); + } + + var objTuple = (ValueTuple)other; + + int c = comparer.Compare(Item1, objTuple.Item1); + if (c != 0) return c; + + return comparer.Compare(Item2, objTuple.Item2); + } + + /// + /// Returns the hash code for the current instance. + /// + /// A 32-bit signed integer hash code. + public override int GetHashCode() + { + return ValueTuple.CombineHashCodes(EqualityComparer.Default.GetHashCode(Item1), + EqualityComparer.Default.GetHashCode(Item2)); + } + + int IStructuralEquatable.GetHashCode(IEqualityComparer comparer) + { + return GetHashCodeCore(comparer); + } + + private int GetHashCodeCore(IEqualityComparer comparer) + { + return ValueTuple.CombineHashCodes(comparer.GetHashCode(Item1), + comparer.GetHashCode(Item2)); + } + + int ITupleInternal.GetHashCode(IEqualityComparer comparer) + { + return GetHashCodeCore(comparer); + } + + /// + /// Returns a string that represents the value of this instance. + /// + /// The string representation of this instance. + /// + /// The string returned by this method takes the form (Item1, Item2), + /// where Item1 and Item2 represent the values of the + /// and fields. If either field value is , + /// it is represented as . + /// + public override string ToString() + { + return "(" + Item1?.ToString() + ", " + Item2?.ToString() + ")"; + } + + string ITupleInternal.ToStringEnd() + { + return Item1?.ToString() + ", " + Item2?.ToString() + ")"; + } + + int ITupleInternal.Size => 2; + } + + /// + /// Represents a 3-tuple, or triple, as a value type. + /// + /// The type of the tuple's first component. + /// The type of the tuple's second component. + /// The type of the tuple's third component. + public struct ValueTuple + : IEquatable>, IStructuralEquatable, IStructuralComparable, IComparable, IComparable>, ITupleInternal + { + /// + /// The current instance's first component. + /// + public T1 Item1; + /// + /// The current instance's second component. + /// + public T2 Item2; + /// + /// The current instance's third component. + /// + public T3 Item3; + + /// + /// Initializes a new instance of the value type. + /// + /// The value of the tuple's first component. + /// The value of the tuple's second component. + /// The value of the tuple's third component. + public ValueTuple(T1 item1, T2 item2, T3 item3) + { + Item1 = item1; + Item2 = item2; + Item3 = item3; + } + + /// + /// Returns a value that indicates whether the current instance is equal to a specified object. + /// + /// The object to compare with this instance. + /// if the current instance is equal to the specified object; otherwise, . + /// + /// The parameter is considered to be equal to the current instance under the following conditions: + /// + /// It is a value type. + /// Its components are of the same types as those of the current instance. + /// Its components are equal to those of the current instance. Equality is determined by the default object equality comparer for each component. + /// + /// + public override bool Equals(object obj) + { + return obj is ValueTuple tuple && Equals(tuple); + } + + /// + /// Returns a value that indicates whether the current + /// instance is equal to a specified . + /// + /// The tuple to compare with this instance. + /// if the current instance is equal to the specified tuple; otherwise, . + /// + /// The parameter is considered to be equal to the current instance if each of its fields + /// are equal to that of the current instance, using the default comparer for that field's type. + /// + public bool Equals(ValueTuple other) + { + return EqualityComparer.Default.Equals(Item1, other.Item1) + && EqualityComparer.Default.Equals(Item2, other.Item2) + && EqualityComparer.Default.Equals(Item3, other.Item3); + } + + bool IStructuralEquatable.Equals(object? other, IEqualityComparer comparer) + { + if (other == null || other is not ValueTuple) return false; + + var objTuple = (ValueTuple)other; + + return comparer.Equals(Item1, objTuple.Item1) + && comparer.Equals(Item2, objTuple.Item2) + && comparer.Equals(Item3, objTuple.Item3); + } + + int IComparable.CompareTo(object other) + { + if (other == null) return 1; + + if (other is not ValueTuple) + { + throw new ArgumentException(SR.ArgumentException_ValueTupleIncorrectType, nameof(other)); + } + + return CompareTo((ValueTuple)other); + } + + /// Compares this instance to a specified instance and returns an indication of their relative values. + /// An instance to compare. + /// + /// A signed number indicating the relative values of this instance and . + /// Returns less than zero if this instance is less than , zero if this + /// instance is equal to , and greater than zero if this instance is greater + /// than . + /// + public int CompareTo(ValueTuple other) + { + int c = Comparer.Default.Compare(Item1, other.Item1); + if (c != 0) return c; + + c = Comparer.Default.Compare(Item2, other.Item2); + if (c != 0) return c; + + return Comparer.Default.Compare(Item3, other.Item3); + } + + int IStructuralComparable.CompareTo(object? other, IComparer comparer) + { + if (other == null) return 1; + + if (other is not ValueTuple) + { + throw new ArgumentException(SR.ArgumentException_ValueTupleIncorrectType, nameof(other)); + } + + var objTuple = (ValueTuple)other; + + int c = comparer.Compare(Item1, objTuple.Item1); + if (c != 0) return c; + + c = comparer.Compare(Item2, objTuple.Item2); + if (c != 0) return c; + + return comparer.Compare(Item3, objTuple.Item3); + } + + /// + /// Returns the hash code for the current instance. + /// + /// A 32-bit signed integer hash code. + public override int GetHashCode() + { + return ValueTuple.CombineHashCodes(EqualityComparer.Default.GetHashCode(Item1), + EqualityComparer.Default.GetHashCode(Item2), + EqualityComparer.Default.GetHashCode(Item3)); + } + + int IStructuralEquatable.GetHashCode(IEqualityComparer comparer) + { + return GetHashCodeCore(comparer); + } + + private int GetHashCodeCore(IEqualityComparer comparer) + { + return ValueTuple.CombineHashCodes(comparer.GetHashCode(Item1), + comparer.GetHashCode(Item2), + comparer.GetHashCode(Item3)); + } + + int ITupleInternal.GetHashCode(IEqualityComparer comparer) + { + return GetHashCodeCore(comparer); + } + + /// + /// Returns a string that represents the value of this instance. + /// + /// The string representation of this instance. + /// + /// The string returned by this method takes the form (Item1, Item2, Item3). + /// If any field value is , it is represented as . + /// + public override string ToString() + { + return "(" + Item1?.ToString() + ", " + Item2?.ToString() + ", " + Item3?.ToString() + ")"; + } + + string ITupleInternal.ToStringEnd() + { + return Item1?.ToString() + ", " + Item2?.ToString() + ", " + Item3?.ToString() + ")"; + } + + int ITupleInternal.Size => 3; + } + + /// + /// Represents a 4-tuple, or quadruple, as a value type. + /// + /// The type of the tuple's first component. + /// The type of the tuple's second component. + /// The type of the tuple's third component. + /// The type of the tuple's fourth component. + public struct ValueTuple + : IEquatable>, IStructuralEquatable, IStructuralComparable, IComparable, IComparable>, ITupleInternal + { + /// + /// The current instance's first component. + /// + public T1 Item1; + /// + /// The current instance's second component. + /// + public T2 Item2; + /// + /// The current instance's third component. + /// + public T3 Item3; + /// + /// The current instance's fourth component. + /// + public T4 Item4; + + /// + /// Initializes a new instance of the value type. + /// + /// The value of the tuple's first component. + /// The value of the tuple's second component. + /// The value of the tuple's third component. + /// The value of the tuple's fourth component. + public ValueTuple(T1 item1, T2 item2, T3 item3, T4 item4) + { + Item1 = item1; + Item2 = item2; + Item3 = item3; + Item4 = item4; + } + + /// + /// Returns a value that indicates whether the current instance is equal to a specified object. + /// + /// The object to compare with this instance. + /// if the current instance is equal to the specified object; otherwise, . + /// + /// The parameter is considered to be equal to the current instance under the following conditions: + /// + /// It is a value type. + /// Its components are of the same types as those of the current instance. + /// Its components are equal to those of the current instance. Equality is determined by the default object equality comparer for each component. + /// + /// + public override bool Equals(object obj) + { + return obj is ValueTuple tuple && Equals(tuple); + } + + /// + /// Returns a value that indicates whether the current + /// instance is equal to a specified . + /// + /// The tuple to compare with this instance. + /// if the current instance is equal to the specified tuple; otherwise, . + /// + /// The parameter is considered to be equal to the current instance if each of its fields + /// are equal to that of the current instance, using the default comparer for that field's type. + /// + public bool Equals(ValueTuple other) + { + return EqualityComparer.Default.Equals(Item1, other.Item1) + && EqualityComparer.Default.Equals(Item2, other.Item2) + && EqualityComparer.Default.Equals(Item3, other.Item3) + && EqualityComparer.Default.Equals(Item4, other.Item4); + } + + bool IStructuralEquatable.Equals(object? other, IEqualityComparer comparer) + { + if (other == null || other is not ValueTuple) return false; + + var objTuple = (ValueTuple)other; + + return comparer.Equals(Item1, objTuple.Item1) + && comparer.Equals(Item2, objTuple.Item2) + && comparer.Equals(Item3, objTuple.Item3) + && comparer.Equals(Item4, objTuple.Item4); + } + + int IComparable.CompareTo(object other) + { + if (other == null) return 1; + + if (other is not ValueTuple) + { + throw new ArgumentException(SR.ArgumentException_ValueTupleIncorrectType, nameof(other)); + } + + return CompareTo((ValueTuple)other); + } + + /// Compares this instance to a specified instance and returns an indication of their relative values. + /// An instance to compare. + /// + /// A signed number indicating the relative values of this instance and . + /// Returns less than zero if this instance is less than , zero if this + /// instance is equal to , and greater than zero if this instance is greater + /// than . + /// + public int CompareTo(ValueTuple other) + { + int c = Comparer.Default.Compare(Item1, other.Item1); + if (c != 0) return c; + + c = Comparer.Default.Compare(Item2, other.Item2); + if (c != 0) return c; + + c = Comparer.Default.Compare(Item3, other.Item3); + if (c != 0) return c; + + return Comparer.Default.Compare(Item4, other.Item4); + } + + int IStructuralComparable.CompareTo(object? other, IComparer comparer) + { + if (other == null) return 1; + + if (other is not ValueTuple) + { + throw new ArgumentException(SR.ArgumentException_ValueTupleIncorrectType, nameof(other)); + } + + var objTuple = (ValueTuple)other; + + int c = comparer.Compare(Item1, objTuple.Item1); + if (c != 0) return c; + + c = comparer.Compare(Item2, objTuple.Item2); + if (c != 0) return c; + + c = comparer.Compare(Item3, objTuple.Item3); + if (c != 0) return c; + + return comparer.Compare(Item4, objTuple.Item4); + } + + /// + /// Returns the hash code for the current instance. + /// + /// A 32-bit signed integer hash code. + public override int GetHashCode() + { + return ValueTuple.CombineHashCodes(EqualityComparer.Default.GetHashCode(Item1), + EqualityComparer.Default.GetHashCode(Item2), + EqualityComparer.Default.GetHashCode(Item3), + EqualityComparer.Default.GetHashCode(Item4)); + } + + int IStructuralEquatable.GetHashCode(IEqualityComparer comparer) + { + return GetHashCodeCore(comparer); + } + + private int GetHashCodeCore(IEqualityComparer comparer) + { + return ValueTuple.CombineHashCodes(comparer.GetHashCode(Item1), + comparer.GetHashCode(Item2), + comparer.GetHashCode(Item3), + comparer.GetHashCode(Item4)); + } + + int ITupleInternal.GetHashCode(IEqualityComparer comparer) + { + return GetHashCodeCore(comparer); + } + + /// + /// Returns a string that represents the value of this instance. + /// + /// The string representation of this instance. + /// + /// The string returned by this method takes the form (Item1, Item2, Item3, Item4). + /// If any field value is , it is represented as . + /// + public override string ToString() + { + return "(" + Item1?.ToString() + ", " + Item2?.ToString() + ", " + Item3?.ToString() + ", " + Item4?.ToString() + ")"; + } + + string ITupleInternal.ToStringEnd() + { + return Item1?.ToString() + ", " + Item2?.ToString() + ", " + Item3?.ToString() + ", " + Item4?.ToString() + ")"; + } + + int ITupleInternal.Size => 4; + } + + /// + /// Represents a 5-tuple, or quintuple, as a value type. + /// + /// The type of the tuple's first component. + /// The type of the tuple's second component. + /// The type of the tuple's third component. + /// The type of the tuple's fourth component. + /// The type of the tuple's fifth component. + public struct ValueTuple + : IEquatable>, IStructuralEquatable, IStructuralComparable, IComparable, IComparable>, ITupleInternal + { + /// + /// The current instance's first component. + /// + public T1 Item1; + /// + /// The current instance's second component. + /// + public T2 Item2; + /// + /// The current instance's third component. + /// + public T3 Item3; + /// + /// The current instance's fourth component. + /// + public T4 Item4; + /// + /// The current instance's fifth component. + /// + public T5 Item5; + + /// + /// Initializes a new instance of the value type. + /// + /// The value of the tuple's first component. + /// The value of the tuple's second component. + /// The value of the tuple's third component. + /// The value of the tuple's fourth component. + /// The value of the tuple's fifth component. + public ValueTuple(T1 item1, T2 item2, T3 item3, T4 item4, T5 item5) + { + Item1 = item1; + Item2 = item2; + Item3 = item3; + Item4 = item4; + Item5 = item5; + } + + /// + /// Returns a value that indicates whether the current instance is equal to a specified object. + /// + /// The object to compare with this instance. + /// if the current instance is equal to the specified object; otherwise, . + /// + /// The parameter is considered to be equal to the current instance under the following conditions: + /// + /// It is a value type. + /// Its components are of the same types as those of the current instance. + /// Its components are equal to those of the current instance. Equality is determined by the default object equality comparer for each component. + /// + /// + public override bool Equals(object obj) + { + return obj is ValueTuple tuple && Equals(tuple); + } + + /// + /// Returns a value that indicates whether the current + /// instance is equal to a specified . + /// + /// The tuple to compare with this instance. + /// if the current instance is equal to the specified tuple; otherwise, . + /// + /// The parameter is considered to be equal to the current instance if each of its fields + /// are equal to that of the current instance, using the default comparer for that field's type. + /// + public bool Equals(ValueTuple other) + { + return EqualityComparer.Default.Equals(Item1, other.Item1) + && EqualityComparer.Default.Equals(Item2, other.Item2) + && EqualityComparer.Default.Equals(Item3, other.Item3) + && EqualityComparer.Default.Equals(Item4, other.Item4) + && EqualityComparer.Default.Equals(Item5, other.Item5); + } + + bool IStructuralEquatable.Equals(object? other, IEqualityComparer comparer) + { + if (other == null || other is not ValueTuple) return false; + + var objTuple = (ValueTuple)other; + + return comparer.Equals(Item1, objTuple.Item1) + && comparer.Equals(Item2, objTuple.Item2) + && comparer.Equals(Item3, objTuple.Item3) + && comparer.Equals(Item4, objTuple.Item4) + && comparer.Equals(Item5, objTuple.Item5); + } + + int IComparable.CompareTo(object other) + { + if (other == null) return 1; + + if (other is not ValueTuple) + { + throw new ArgumentException(SR.ArgumentException_ValueTupleIncorrectType, nameof(other)); + } + + return CompareTo((ValueTuple)other); + } + + /// Compares this instance to a specified instance and returns an indication of their relative values. + /// An instance to compare. + /// + /// A signed number indicating the relative values of this instance and . + /// Returns less than zero if this instance is less than , zero if this + /// instance is equal to , and greater than zero if this instance is greater + /// than . + /// + public int CompareTo(ValueTuple other) + { + int c = Comparer.Default.Compare(Item1, other.Item1); + if (c != 0) return c; + + c = Comparer.Default.Compare(Item2, other.Item2); + if (c != 0) return c; + + c = Comparer.Default.Compare(Item3, other.Item3); + if (c != 0) return c; + + c = Comparer.Default.Compare(Item4, other.Item4); + if (c != 0) return c; + + return Comparer.Default.Compare(Item5, other.Item5); + } + + int IStructuralComparable.CompareTo(object? other, IComparer comparer) + { + if (other == null) return 1; + + if (other is not ValueTuple) + { + throw new ArgumentException(SR.ArgumentException_ValueTupleIncorrectType, nameof(other)); + } + + var objTuple = (ValueTuple)other; + + int c = comparer.Compare(Item1, objTuple.Item1); + if (c != 0) return c; + + c = comparer.Compare(Item2, objTuple.Item2); + if (c != 0) return c; + + c = comparer.Compare(Item3, objTuple.Item3); + if (c != 0) return c; + + c = comparer.Compare(Item4, objTuple.Item4); + if (c != 0) return c; + + return comparer.Compare(Item5, objTuple.Item5); + } + + /// + /// Returns the hash code for the current instance. + /// + /// A 32-bit signed integer hash code. + public override int GetHashCode() + { + return ValueTuple.CombineHashCodes(EqualityComparer.Default.GetHashCode(Item1), + EqualityComparer.Default.GetHashCode(Item2), + EqualityComparer.Default.GetHashCode(Item3), + EqualityComparer.Default.GetHashCode(Item4), + EqualityComparer.Default.GetHashCode(Item5)); + } + + int IStructuralEquatable.GetHashCode(IEqualityComparer comparer) + { + return GetHashCodeCore(comparer); + } + + private int GetHashCodeCore(IEqualityComparer comparer) + { + return ValueTuple.CombineHashCodes(comparer.GetHashCode(Item1), + comparer.GetHashCode(Item2), + comparer.GetHashCode(Item3), + comparer.GetHashCode(Item4), + comparer.GetHashCode(Item5)); + } + + int ITupleInternal.GetHashCode(IEqualityComparer comparer) + { + return GetHashCodeCore(comparer); + } + + /// + /// Returns a string that represents the value of this instance. + /// + /// The string representation of this instance. + /// + /// The string returned by this method takes the form (Item1, Item2, Item3, Item4, Item5). + /// If any field value is , it is represented as . + /// + public override string ToString() + { + return "(" + Item1?.ToString() + ", " + Item2?.ToString() + ", " + Item3?.ToString() + ", " + Item4?.ToString() + ", " + Item5?.ToString() + ")"; + } + + string ITupleInternal.ToStringEnd() + { + return Item1?.ToString() + ", " + Item2?.ToString() + ", " + Item3?.ToString() + ", " + Item4?.ToString() + ", " + Item5?.ToString() + ")"; + } + + int ITupleInternal.Size => 5; + } + + /// + /// Represents a 6-tuple, or sixtuple, as a value type. + /// + /// The type of the tuple's first component. + /// The type of the tuple's second component. + /// The type of the tuple's third component. + /// The type of the tuple's fourth component. + /// The type of the tuple's fifth component. + /// The type of the tuple's sixth component. + public struct ValueTuple + : IEquatable>, IStructuralEquatable, IStructuralComparable, IComparable, IComparable>, ITupleInternal + { + /// + /// The current instance's first component. + /// + public T1 Item1; + /// + /// The current instance's second component. + /// + public T2 Item2; + /// + /// The current instance's third component. + /// + public T3 Item3; + /// + /// The current instance's fourth component. + /// + public T4 Item4; + /// + /// The current instance's fifth component. + /// + public T5 Item5; + /// + /// The current instance's sixth component. + /// + public T6 Item6; + + /// + /// Initializes a new instance of the value type. + /// + /// The value of the tuple's first component. + /// The value of the tuple's second component. + /// The value of the tuple's third component. + /// The value of the tuple's fourth component. + /// The value of the tuple's fifth component. + /// The value of the tuple's sixth component. + public ValueTuple(T1 item1, T2 item2, T3 item3, T4 item4, T5 item5, T6 item6) + { + Item1 = item1; + Item2 = item2; + Item3 = item3; + Item4 = item4; + Item5 = item5; + Item6 = item6; + } + + /// + /// Returns a value that indicates whether the current instance is equal to a specified object. + /// + /// The object to compare with this instance. + /// if the current instance is equal to the specified object; otherwise, . + /// + /// The parameter is considered to be equal to the current instance under the following conditions: + /// + /// It is a value type. + /// Its components are of the same types as those of the current instance. + /// Its components are equal to those of the current instance. Equality is determined by the default object equality comparer for each component. + /// + /// + public override bool Equals(object obj) + { + return obj is ValueTuple tuple && Equals(tuple); + } + + /// + /// Returns a value that indicates whether the current + /// instance is equal to a specified . + /// + /// The tuple to compare with this instance. + /// if the current instance is equal to the specified tuple; otherwise, . + /// + /// The parameter is considered to be equal to the current instance if each of its fields + /// are equal to that of the current instance, using the default comparer for that field's type. + /// + public bool Equals(ValueTuple other) + { + return EqualityComparer.Default.Equals(Item1, other.Item1) + && EqualityComparer.Default.Equals(Item2, other.Item2) + && EqualityComparer.Default.Equals(Item3, other.Item3) + && EqualityComparer.Default.Equals(Item4, other.Item4) + && EqualityComparer.Default.Equals(Item5, other.Item5) + && EqualityComparer.Default.Equals(Item6, other.Item6); + } + + bool IStructuralEquatable.Equals(object? other, IEqualityComparer comparer) + { + if (other == null || other is not ValueTuple) return false; + + var objTuple = (ValueTuple)other; + + return comparer.Equals(Item1, objTuple.Item1) + && comparer.Equals(Item2, objTuple.Item2) + && comparer.Equals(Item3, objTuple.Item3) + && comparer.Equals(Item4, objTuple.Item4) + && comparer.Equals(Item5, objTuple.Item5) + && comparer.Equals(Item6, objTuple.Item6); + } + + int IComparable.CompareTo(object other) + { + if (other == null) return 1; + + if (other is not ValueTuple) + { + throw new ArgumentException(SR.ArgumentException_ValueTupleIncorrectType, nameof(other)); + } + + return CompareTo((ValueTuple)other); + } + + /// Compares this instance to a specified instance and returns an indication of their relative values. + /// An instance to compare. + /// + /// A signed number indicating the relative values of this instance and . + /// Returns less than zero if this instance is less than , zero if this + /// instance is equal to , and greater than zero if this instance is greater + /// than . + /// + public int CompareTo(ValueTuple other) + { + int c = Comparer.Default.Compare(Item1, other.Item1); + if (c != 0) return c; + + c = Comparer.Default.Compare(Item2, other.Item2); + if (c != 0) return c; + + c = Comparer.Default.Compare(Item3, other.Item3); + if (c != 0) return c; + + c = Comparer.Default.Compare(Item4, other.Item4); + if (c != 0) return c; + + c = Comparer.Default.Compare(Item5, other.Item5); + if (c != 0) return c; + + return Comparer.Default.Compare(Item6, other.Item6); + } + + int IStructuralComparable.CompareTo(object? other, IComparer comparer) + { + if (other == null) return 1; + + if (other is not ValueTuple) + { + throw new ArgumentException(SR.ArgumentException_ValueTupleIncorrectType, nameof(other)); + } + + var objTuple = (ValueTuple)other; + + int c = comparer.Compare(Item1, objTuple.Item1); + if (c != 0) return c; + + c = comparer.Compare(Item2, objTuple.Item2); + if (c != 0) return c; + + c = comparer.Compare(Item3, objTuple.Item3); + if (c != 0) return c; + + c = comparer.Compare(Item4, objTuple.Item4); + if (c != 0) return c; + + c = comparer.Compare(Item5, objTuple.Item5); + if (c != 0) return c; + + return comparer.Compare(Item6, objTuple.Item6); + } + + /// + /// Returns the hash code for the current instance. + /// + /// A 32-bit signed integer hash code. + public override int GetHashCode() + { + return ValueTuple.CombineHashCodes(EqualityComparer.Default.GetHashCode(Item1), + EqualityComparer.Default.GetHashCode(Item2), + EqualityComparer.Default.GetHashCode(Item3), + EqualityComparer.Default.GetHashCode(Item4), + EqualityComparer.Default.GetHashCode(Item5), + EqualityComparer.Default.GetHashCode(Item6)); + } + + int IStructuralEquatable.GetHashCode(IEqualityComparer comparer) + { + return GetHashCodeCore(comparer); + } + + private int GetHashCodeCore(IEqualityComparer comparer) + { + return ValueTuple.CombineHashCodes(comparer.GetHashCode(Item1), + comparer.GetHashCode(Item2), + comparer.GetHashCode(Item3), + comparer.GetHashCode(Item4), + comparer.GetHashCode(Item5), + comparer.GetHashCode(Item6)); + } + + int ITupleInternal.GetHashCode(IEqualityComparer comparer) + { + return GetHashCodeCore(comparer); + } + + /// + /// Returns a string that represents the value of this instance. + /// + /// The string representation of this instance. + /// + /// The string returned by this method takes the form (Item1, Item2, Item3, Item4, Item5, Item6). + /// If any field value is , it is represented as . + /// + public override string ToString() + { + return "(" + Item1?.ToString() + ", " + Item2?.ToString() + ", " + Item3?.ToString() + ", " + Item4?.ToString() + ", " + Item5?.ToString() + ", " + Item6?.ToString() + ")"; + } + + string ITupleInternal.ToStringEnd() + { + return Item1?.ToString() + ", " + Item2?.ToString() + ", " + Item3?.ToString() + ", " + Item4?.ToString() + ", " + Item5?.ToString() + ", " + Item6?.ToString() + ")"; + } + + int ITupleInternal.Size => 6; + } + + /// + /// Represents a 7-tuple, or sentuple, as a value type. + /// + /// The type of the tuple's first component. + /// The type of the tuple's second component. + /// The type of the tuple's third component. + /// The type of the tuple's fourth component. + /// The type of the tuple's fifth component. + /// The type of the tuple's sixth component. + /// The type of the tuple's seventh component. + public struct ValueTuple + : IEquatable>, IStructuralEquatable, IStructuralComparable, IComparable, IComparable>, ITupleInternal + { + /// + /// The current instance's first component. + /// + public T1 Item1; + /// + /// The current instance's second component. + /// + public T2 Item2; + /// + /// The current instance's third component. + /// + public T3 Item3; + /// + /// The current instance's fourth component. + /// + public T4 Item4; + /// + /// The current instance's fifth component. + /// + public T5 Item5; + /// + /// The current instance's sixth component. + /// + public T6 Item6; + /// + /// The current instance's seventh component. + /// + public T7 Item7; + + /// + /// Initializes a new instance of the value type. + /// + /// The value of the tuple's first component. + /// The value of the tuple's second component. + /// The value of the tuple's third component. + /// The value of the tuple's fourth component. + /// The value of the tuple's fifth component. + /// The value of the tuple's sixth component. + /// The value of the tuple's seventh component. + public ValueTuple(T1 item1, T2 item2, T3 item3, T4 item4, T5 item5, T6 item6, T7 item7) + { + Item1 = item1; + Item2 = item2; + Item3 = item3; + Item4 = item4; + Item5 = item5; + Item6 = item6; + Item7 = item7; + } + + /// + /// Returns a value that indicates whether the current instance is equal to a specified object. + /// + /// The object to compare with this instance. + /// if the current instance is equal to the specified object; otherwise, . + /// + /// The parameter is considered to be equal to the current instance under the following conditions: + /// + /// It is a value type. + /// Its components are of the same types as those of the current instance. + /// Its components are equal to those of the current instance. Equality is determined by the default object equality comparer for each component. + /// + /// + public override bool Equals(object obj) + { + return obj is ValueTuple tuple && Equals(tuple); + } + + /// + /// Returns a value that indicates whether the current + /// instance is equal to a specified . + /// + /// The tuple to compare with this instance. + /// if the current instance is equal to the specified tuple; otherwise, . + /// + /// The parameter is considered to be equal to the current instance if each of its fields + /// are equal to that of the current instance, using the default comparer for that field's type. + /// + public bool Equals(ValueTuple other) + { + return EqualityComparer.Default.Equals(Item1, other.Item1) + && EqualityComparer.Default.Equals(Item2, other.Item2) + && EqualityComparer.Default.Equals(Item3, other.Item3) + && EqualityComparer.Default.Equals(Item4, other.Item4) + && EqualityComparer.Default.Equals(Item5, other.Item5) + && EqualityComparer.Default.Equals(Item6, other.Item6) + && EqualityComparer.Default.Equals(Item7, other.Item7); + } + + bool IStructuralEquatable.Equals(object? other, IEqualityComparer comparer) + { + if (other == null || other is not ValueTuple) return false; + + var objTuple = (ValueTuple)other; + + return comparer.Equals(Item1, objTuple.Item1) + && comparer.Equals(Item2, objTuple.Item2) + && comparer.Equals(Item3, objTuple.Item3) + && comparer.Equals(Item4, objTuple.Item4) + && comparer.Equals(Item5, objTuple.Item5) + && comparer.Equals(Item6, objTuple.Item6) + && comparer.Equals(Item7, objTuple.Item7); + } + + int IComparable.CompareTo(object other) + { + if (other == null) return 1; + + if (other is not ValueTuple) + { + throw new ArgumentException(SR.ArgumentException_ValueTupleIncorrectType, nameof(other)); + } + + return CompareTo((ValueTuple)other); + } + + /// Compares this instance to a specified instance and returns an indication of their relative values. + /// An instance to compare. + /// + /// A signed number indicating the relative values of this instance and . + /// Returns less than zero if this instance is less than , zero if this + /// instance is equal to , and greater than zero if this instance is greater + /// than . + /// + public int CompareTo(ValueTuple other) + { + int c = Comparer.Default.Compare(Item1, other.Item1); + if (c != 0) return c; + + c = Comparer.Default.Compare(Item2, other.Item2); + if (c != 0) return c; + + c = Comparer.Default.Compare(Item3, other.Item3); + if (c != 0) return c; + + c = Comparer.Default.Compare(Item4, other.Item4); + if (c != 0) return c; + + c = Comparer.Default.Compare(Item5, other.Item5); + if (c != 0) return c; + + c = Comparer.Default.Compare(Item6, other.Item6); + if (c != 0) return c; + + return Comparer.Default.Compare(Item7, other.Item7); + } + + int IStructuralComparable.CompareTo(object? other, IComparer comparer) + { + if (other == null) return 1; + + if (other is not ValueTuple) + { + throw new ArgumentException(SR.ArgumentException_ValueTupleIncorrectType, nameof(other)); + } + + var objTuple = (ValueTuple)other; + + int c = comparer.Compare(Item1, objTuple.Item1); + if (c != 0) return c; + + c = comparer.Compare(Item2, objTuple.Item2); + if (c != 0) return c; + + c = comparer.Compare(Item3, objTuple.Item3); + if (c != 0) return c; + + c = comparer.Compare(Item4, objTuple.Item4); + if (c != 0) return c; + + c = comparer.Compare(Item5, objTuple.Item5); + if (c != 0) return c; + + c = comparer.Compare(Item6, objTuple.Item6); + if (c != 0) return c; + + return comparer.Compare(Item7, objTuple.Item7); + } + + /// + /// Returns the hash code for the current instance. + /// + /// A 32-bit signed integer hash code. + public override int GetHashCode() + { + return ValueTuple.CombineHashCodes(EqualityComparer.Default.GetHashCode(Item1), + EqualityComparer.Default.GetHashCode(Item2), + EqualityComparer.Default.GetHashCode(Item3), + EqualityComparer.Default.GetHashCode(Item4), + EqualityComparer.Default.GetHashCode(Item5), + EqualityComparer.Default.GetHashCode(Item6), + EqualityComparer.Default.GetHashCode(Item7)); + } + + int IStructuralEquatable.GetHashCode(IEqualityComparer comparer) + { + return GetHashCodeCore(comparer); + } + + private int GetHashCodeCore(IEqualityComparer comparer) + { + return ValueTuple.CombineHashCodes(comparer.GetHashCode(Item1), + comparer.GetHashCode(Item2), + comparer.GetHashCode(Item3), + comparer.GetHashCode(Item4), + comparer.GetHashCode(Item5), + comparer.GetHashCode(Item6), + comparer.GetHashCode(Item7)); + } + + int ITupleInternal.GetHashCode(IEqualityComparer comparer) + { + return GetHashCodeCore(comparer); + } + + /// + /// Returns a string that represents the value of this instance. + /// + /// The string representation of this instance. + /// + /// The string returned by this method takes the form (Item1, Item2, Item3, Item4, Item5, Item6, Item7). + /// If any field value is , it is represented as . + /// + public override string ToString() + { + return "(" + Item1?.ToString() + ", " + Item2?.ToString() + ", " + Item3?.ToString() + ", " + Item4?.ToString() + ", " + Item5?.ToString() + ", " + Item6?.ToString() + ", " + Item7?.ToString() + ")"; + } + + string ITupleInternal.ToStringEnd() + { + return Item1?.ToString() + ", " + Item2?.ToString() + ", " + Item3?.ToString() + ", " + Item4?.ToString() + ", " + Item5?.ToString() + ", " + Item6?.ToString() + ", " + Item7?.ToString() + ")"; + } + + int ITupleInternal.Size => 7; + } + + /// + /// Represents an 8-tuple, or octuple, as a value type. + /// + /// The type of the tuple's first component. + /// The type of the tuple's second component. + /// The type of the tuple's third component. + /// The type of the tuple's fourth component. + /// The type of the tuple's fifth component. + /// The type of the tuple's sixth component. + /// The type of the tuple's seventh component. + /// The type of the tuple's eighth component. + public struct ValueTuple + : IEquatable>, IStructuralEquatable, IStructuralComparable, IComparable, IComparable>, ITupleInternal + where TRest : struct + { + /// + /// The current instance's first component. + /// + public T1 Item1; + /// + /// The current instance's second component. + /// + public T2 Item2; + /// + /// The current instance's third component. + /// + public T3 Item3; + /// + /// The current instance's fourth component. + /// + public T4 Item4; + /// + /// The current instance's fifth component. + /// + public T5 Item5; + /// + /// The current instance's sixth component. + /// + public T6 Item6; + /// + /// The current instance's seventh component. + /// + public T7 Item7; + /// + /// The current instance's eighth component. + /// + public TRest Rest; + + /// + /// Initializes a new instance of the value type. + /// + /// The value of the tuple's first component. + /// The value of the tuple's second component. + /// The value of the tuple's third component. + /// The value of the tuple's fourth component. + /// The value of the tuple's fifth component. + /// The value of the tuple's sixth component. + /// The value of the tuple's seventh component. + /// The value of the tuple's eight component. + public ValueTuple(T1 item1, T2 item2, T3 item3, T4 item4, T5 item5, T6 item6, T7 item7, TRest rest) + { + if (rest is not ITupleInternal) + { + throw new ArgumentException(SR.ArgumentException_ValueTupleLastArgumentNotAValueTuple); + } + + Item1 = item1; + Item2 = item2; + Item3 = item3; + Item4 = item4; + Item5 = item5; + Item6 = item6; + Item7 = item7; + Rest = rest; + } + + /// + /// Returns a value that indicates whether the current instance is equal to a specified object. + /// + /// The object to compare with this instance. + /// if the current instance is equal to the specified object; otherwise, . + /// + /// The parameter is considered to be equal to the current instance under the following conditions: + /// + /// It is a value type. + /// Its components are of the same types as those of the current instance. + /// Its components are equal to those of the current instance. Equality is determined by the default object equality comparer for each component. + /// + /// + public override bool Equals(object obj) + { + return obj is ValueTuple tuple && Equals(tuple); + } + + /// + /// Returns a value that indicates whether the current + /// instance is equal to a specified . + /// + /// The tuple to compare with this instance. + /// if the current instance is equal to the specified tuple; otherwise, . + /// + /// The parameter is considered to be equal to the current instance if each of its fields + /// are equal to that of the current instance, using the default comparer for that field's type. + /// + public bool Equals(ValueTuple other) + { + return EqualityComparer.Default.Equals(Item1, other.Item1) + && EqualityComparer.Default.Equals(Item2, other.Item2) + && EqualityComparer.Default.Equals(Item3, other.Item3) + && EqualityComparer.Default.Equals(Item4, other.Item4) + && EqualityComparer.Default.Equals(Item5, other.Item5) + && EqualityComparer.Default.Equals(Item6, other.Item6) + && EqualityComparer.Default.Equals(Item7, other.Item7) + && EqualityComparer.Default.Equals(Rest, other.Rest); + } + + bool IStructuralEquatable.Equals(object? other, IEqualityComparer comparer) + { + if (other == null || other is not ValueTuple) return false; + + var objTuple = (ValueTuple)other; + + return comparer.Equals(Item1, objTuple.Item1) + && comparer.Equals(Item2, objTuple.Item2) + && comparer.Equals(Item3, objTuple.Item3) + && comparer.Equals(Item4, objTuple.Item4) + && comparer.Equals(Item5, objTuple.Item5) + && comparer.Equals(Item6, objTuple.Item6) + && comparer.Equals(Item7, objTuple.Item7) + && comparer.Equals(Rest, objTuple.Rest); + } + + int IComparable.CompareTo(object other) + { + if (other == null) return 1; + + if (other is not ValueTuple) + { + throw new ArgumentException(SR.ArgumentException_ValueTupleIncorrectType, nameof(other)); + } + + return CompareTo((ValueTuple)other); + } + + /// Compares this instance to a specified instance and returns an indication of their relative values. + /// An instance to compare. + /// + /// A signed number indicating the relative values of this instance and . + /// Returns less than zero if this instance is less than , zero if this + /// instance is equal to , and greater than zero if this instance is greater + /// than . + /// + public int CompareTo(ValueTuple other) + { + int c = Comparer.Default.Compare(Item1, other.Item1); + if (c != 0) return c; + + c = Comparer.Default.Compare(Item2, other.Item2); + if (c != 0) return c; + + c = Comparer.Default.Compare(Item3, other.Item3); + if (c != 0) return c; + + c = Comparer.Default.Compare(Item4, other.Item4); + if (c != 0) return c; + + c = Comparer.Default.Compare(Item5, other.Item5); + if (c != 0) return c; + + c = Comparer.Default.Compare(Item6, other.Item6); + if (c != 0) return c; + + c = Comparer.Default.Compare(Item7, other.Item7); + if (c != 0) return c; + + return Comparer.Default.Compare(Rest, other.Rest); + } + + int IStructuralComparable.CompareTo(object? other, IComparer comparer) + { + if (other == null) return 1; + + if (other is not ValueTuple) + { + throw new ArgumentException(SR.ArgumentException_ValueTupleIncorrectType, nameof(other)); + } + + var objTuple = (ValueTuple)other; + + int c = comparer.Compare(Item1, objTuple.Item1); + if (c != 0) return c; + + c = comparer.Compare(Item2, objTuple.Item2); + if (c != 0) return c; + + c = comparer.Compare(Item3, objTuple.Item3); + if (c != 0) return c; + + c = comparer.Compare(Item4, objTuple.Item4); + if (c != 0) return c; + + c = comparer.Compare(Item5, objTuple.Item5); + if (c != 0) return c; + + c = comparer.Compare(Item6, objTuple.Item6); + if (c != 0) return c; + + c = comparer.Compare(Item7, objTuple.Item7); + if (c != 0) return c; + + return comparer.Compare(Rest, objTuple.Rest); + } + + /// + /// Returns the hash code for the current instance. + /// + /// A 32-bit signed integer hash code. + public override int GetHashCode() + { + // We want to have a limited hash in this case. We'll use the last 8 elements of the tuple + if (Rest is not ITupleInternal rest) + { + return ValueTuple.CombineHashCodes(EqualityComparer.Default.GetHashCode(Item1), + EqualityComparer.Default.GetHashCode(Item2), + EqualityComparer.Default.GetHashCode(Item3), + EqualityComparer.Default.GetHashCode(Item4), + EqualityComparer.Default.GetHashCode(Item5), + EqualityComparer.Default.GetHashCode(Item6), + EqualityComparer.Default.GetHashCode(Item7)); + } + + int size = rest.Size; + if (size >= 8) { return rest.GetHashCode(); } + + // In this case, the rest member has less than 8 elements so we need to combine some our elements with the elements in rest + int k = 8 - size; + switch (k) + { + case 1: + return ValueTuple.CombineHashCodes(EqualityComparer.Default.GetHashCode(Item7), + rest.GetHashCode()); + case 2: + return ValueTuple.CombineHashCodes(EqualityComparer.Default.GetHashCode(Item6), + EqualityComparer.Default.GetHashCode(Item7), + rest.GetHashCode()); + case 3: + return ValueTuple.CombineHashCodes(EqualityComparer.Default.GetHashCode(Item5), + EqualityComparer.Default.GetHashCode(Item6), + EqualityComparer.Default.GetHashCode(Item7), + rest.GetHashCode()); + case 4: + return ValueTuple.CombineHashCodes(EqualityComparer.Default.GetHashCode(Item4), + EqualityComparer.Default.GetHashCode(Item5), + EqualityComparer.Default.GetHashCode(Item6), + EqualityComparer.Default.GetHashCode(Item7), + rest.GetHashCode()); + case 5: + return ValueTuple.CombineHashCodes(EqualityComparer.Default.GetHashCode(Item3), + EqualityComparer.Default.GetHashCode(Item4), + EqualityComparer.Default.GetHashCode(Item5), + EqualityComparer.Default.GetHashCode(Item6), + EqualityComparer.Default.GetHashCode(Item7), + rest.GetHashCode()); + case 6: + return ValueTuple.CombineHashCodes(EqualityComparer.Default.GetHashCode(Item2), + EqualityComparer.Default.GetHashCode(Item3), + EqualityComparer.Default.GetHashCode(Item4), + EqualityComparer.Default.GetHashCode(Item5), + EqualityComparer.Default.GetHashCode(Item6), + EqualityComparer.Default.GetHashCode(Item7), + rest.GetHashCode()); + case 7: + case 8: + return ValueTuple.CombineHashCodes(EqualityComparer.Default.GetHashCode(Item1), + EqualityComparer.Default.GetHashCode(Item2), + EqualityComparer.Default.GetHashCode(Item3), + EqualityComparer.Default.GetHashCode(Item4), + EqualityComparer.Default.GetHashCode(Item5), + EqualityComparer.Default.GetHashCode(Item6), + EqualityComparer.Default.GetHashCode(Item7), + rest.GetHashCode()); + } + + Debug.Assert(false, "Missed all cases for computing ValueTuple hash code"); + return -1; + } + + int IStructuralEquatable.GetHashCode(IEqualityComparer comparer) + { + return GetHashCodeCore(comparer); + } + + private int GetHashCodeCore(IEqualityComparer comparer) + { + // We want to have a limited hash in this case. We'll use the last 8 elements of the tuple + if (Rest is not ITupleInternal rest) + { + return ValueTuple.CombineHashCodes( + comparer.GetHashCode(Item1), + comparer.GetHashCode(Item2), + comparer.GetHashCode(Item3), + comparer.GetHashCode(Item4), + comparer.GetHashCode(Item5), + comparer.GetHashCode(Item6), + comparer.GetHashCode(Item7)); + } + + int size = rest.Size; + if (size >= 8) { return rest.GetHashCode(comparer); } + + // In this case, the rest member has less than 8 elements so we need to combine some our elements with the elements in rest + int k = 8 - size; + switch (k) + { + case 1: + return ValueTuple.CombineHashCodes(comparer.GetHashCode(Item7), rest.GetHashCode(comparer)); + case 2: + return ValueTuple.CombineHashCodes(comparer.GetHashCode(Item6), comparer.GetHashCode(Item7), rest.GetHashCode(comparer)); + case 3: + return ValueTuple.CombineHashCodes(comparer.GetHashCode(Item5), comparer.GetHashCode(Item6), comparer.GetHashCode(Item7), + rest.GetHashCode(comparer)); + case 4: + return ValueTuple.CombineHashCodes(comparer.GetHashCode(Item4), comparer.GetHashCode(Item5), comparer.GetHashCode(Item6), + comparer.GetHashCode(Item7), rest.GetHashCode(comparer)); + case 5: + return ValueTuple.CombineHashCodes(comparer.GetHashCode(Item3), comparer.GetHashCode(Item4), comparer.GetHashCode(Item5), + comparer.GetHashCode(Item6), comparer.GetHashCode(Item7), rest.GetHashCode(comparer)); + case 6: + return ValueTuple.CombineHashCodes(comparer.GetHashCode(Item2), comparer.GetHashCode(Item3), comparer.GetHashCode(Item4), + comparer.GetHashCode(Item5), comparer.GetHashCode(Item6), comparer.GetHashCode(Item7), + rest.GetHashCode(comparer)); + case 7: + case 8: + return ValueTuple.CombineHashCodes(comparer.GetHashCode(Item1), comparer.GetHashCode(Item2), comparer.GetHashCode(Item3), + comparer.GetHashCode(Item4), comparer.GetHashCode(Item5), comparer.GetHashCode(Item6), + comparer.GetHashCode(Item7), rest.GetHashCode(comparer)); + } + + Debug.Assert(false, "Missed all cases for computing ValueTuple hash code"); + return -1; + } + + int ITupleInternal.GetHashCode(IEqualityComparer comparer) + { + return GetHashCodeCore(comparer); + } + + /// + /// Returns a string that represents the value of this instance. + /// + /// The string representation of this instance. + /// + /// The string returned by this method takes the form (Item1, Item2, Item3, Item4, Item5, Item6, Item7, Rest). + /// If any field value is , it is represented as . + /// + public override string ToString() + { + if (Rest is not ITupleInternal rest) + { + return "(" + Item1?.ToString() + ", " + Item2?.ToString() + ", " + Item3?.ToString() + ", " + Item4?.ToString() + ", " + Item5?.ToString() + ", " + Item6?.ToString() + ", " + Item7?.ToString() + ", " + Rest.ToString() + ")"; + } + else + { + return "(" + Item1?.ToString() + ", " + Item2?.ToString() + ", " + Item3?.ToString() + ", " + Item4?.ToString() + ", " + Item5?.ToString() + ", " + Item6?.ToString() + ", " + Item7?.ToString() + ", " + rest.ToStringEnd(); + } + } + + string ITupleInternal.ToStringEnd() + { + if (Rest is not ITupleInternal rest) + { + return Item1?.ToString() + ", " + Item2?.ToString() + ", " + Item3?.ToString() + ", " + Item4?.ToString() + ", " + Item5?.ToString() + ", " + Item6?.ToString() + ", " + Item7?.ToString() + ", " + Rest.ToString() + ")"; + } + else + { + return Item1?.ToString() + ", " + Item2?.ToString() + ", " + Item3?.ToString() + ", " + Item4?.ToString() + ", " + Item5?.ToString() + ", " + Item6?.ToString() + ", " + Item7?.ToString() + ", " + rest.ToStringEnd(); + } + } + + int ITupleInternal.Size + { + get + { + //ITupleInternal? rest = Rest as ITupleInternal; + //return rest == null ? 8 : 7 + rest.Size; + return Rest is not ITupleInternal rest ? 8 : 7 + rest.Size; + } + } + } +} + + + + diff --git a/src/IFoxCAD.Basal/DictEx.cs b/src/IFoxCAD.Basal/DictEx.cs new file mode 100644 index 0000000..6fbccc4 --- /dev/null +++ b/src/IFoxCAD.Basal/DictEx.cs @@ -0,0 +1,21 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; + +namespace IFoxCAD.Basal +{ + public static class DictEx + { + //public static TKey? GetKey(this IDictionary dict!!, TKey key!!) + //{ + // if (dict.ContainsKey(key)) + // { + // foreach (var item in dict.Keys) + // if (key.Equals(item)) + // return item; + // } + // return default; + //} + } +} diff --git a/src/IFoxCAD.Basal/GlobalUsings.cs b/src/IFoxCAD.Basal/GlobalUsings.cs new file mode 100644 index 0000000..15c8a97 --- /dev/null +++ b/src/IFoxCAD.Basal/GlobalUsings.cs @@ -0,0 +1,9 @@ +/// 系统引用 +global using System; +global using System.Collections; +global using System.Collections.Generic; +global using System.IO; +global using System.Linq; +global using System.Text; +global using System.Reflection; + diff --git a/src/IFoxCAD.Basal/IFoxCAD.Basal.csproj b/src/IFoxCAD.Basal/IFoxCAD.Basal.csproj index a15060d..73d940d 100644 --- a/src/IFoxCAD.Basal/IFoxCAD.Basal.csproj +++ b/src/IFoxCAD.Basal/IFoxCAD.Basal.csproj @@ -1,45 +1,34 @@ - - - - preview - enable - - - 1.0.0.* - 1.0.0.0 - False - net35;net40 - 0.1.1 - - true - true - InspireFunction - xsfhlzh;vicwjb - 基于.NET的二次开发基本类库 - InspireFunction - git - https://gitee.com/inspirefunction/ifoxcad.git - - https://gitee.com/inspirefunction/ifoxcad - IFoxCAD;C#;NET;Common;Basal - 增加在net35支持 - true - true - - LICENSE - true - - - - - - - - - True - - - - + + + + preview + enable + + net35;net40;net45 + true + 0.3.5.1 + InspireFunction + xsfhlzh;vicwjb + 基于.NET的二次开发基本类库 + InspireFunction + https://gitee.com/inspirefunction/ifoxcad + https://gitee.com/inspirefunction/ifoxcad.git + git + IFoxCAD;C#;NET;Common;Basal + 直接集成元组和索引切片. + true + true + LICENSE + true + True + none + + + + + True + + + diff --git a/src/IFoxCAD.Basal/LinkedHashMap.cs b/src/IFoxCAD.Basal/LinkedHashMap.cs new file mode 100644 index 0000000..0e6232c --- /dev/null +++ b/src/IFoxCAD.Basal/LinkedHashMap.cs @@ -0,0 +1,171 @@ +namespace IFoxCAD.Basal; + + +/// +/// A least-recently-used cache stored like a dictionary. +/// +/// +/// The type of the key to the cached item +/// +/// +/// The type of the cached item. +/// +/// +/// Derived from https://stackoverflow.com/a/3719378/240845 +/// https://stackoverflow.com/users/240845/mheyman +/// +public class LinkedHashMap +{ + private readonly Dictionary> cacheMap = new(); + + private readonly LinkedList lruList = new(); + + private readonly Action? dispose; + + /// + /// Initializes a new instance of the + /// class. + /// + /// + /// Maximum number of elements to cache. + /// + /// + /// When elements cycle out of the cache, disposes them. May be null. + /// + public LinkedHashMap(int capacity, Action? dispose = null) + { + this.Capacity = capacity; + this.dispose = dispose; + } + + /// + /// Gets the capacity of the cache. + /// + public int Capacity { get; } + + /// Gets the value associated with the specified key. + /// + /// The key of the value to get. + /// + /// + /// When this method returns, contains the value associated with the specified + /// key, if the key is found; otherwise, the default value for the type of the + /// parameter. This parameter is passed + /// uninitialized. + /// + /// + /// true if the + /// contains an element with the specified key; otherwise, false. + /// + public bool TryGetValue(TKey key, out TValue? value) + { + lock (this.cacheMap) + { + if (this.cacheMap.TryGetValue(key, out LinkedListNode.MapItem> node)) + { + value = node.Value.Value; + this.lruList.Remove(node); + this.lruList.AddLast(node); + return true; + } + + value = default; + return false; + } + } + + /// + /// Looks for a value for the matching . If not found, + /// calls to retrieve the value and add it to + /// the cache. + /// + /// + /// The key of the value to look up. + /// + /// + /// Generates a value if one isn't found. + /// + /// + /// The requested value. + /// + public TValue Get(TKey key, Func valueGenerator) + { + lock (this.cacheMap) + { + TValue value; + if (this.cacheMap.TryGetValue(key, out LinkedListNode.MapItem> node)) + { + value = node.Value.Value; + this.lruList.Remove(node); + this.lruList.AddLast(node); + } + else + { + value = valueGenerator(); + if (this.cacheMap.Count >= this.Capacity) + { + this.RemoveFirst(); + } + + MapItem cacheItem = new(key, value); + node = new LinkedListNode(cacheItem); + this.lruList.AddLast(node); + this.cacheMap.Add(key, node); + } + + return value; + } + } + + /// + /// Adds the specified key and value to the dictionary. + /// + /// + /// The key of the element to add. + /// + /// + /// The value of the element to add. The value can be null for reference types. + /// + public void Add(TKey key, TValue value) + { + lock (this.cacheMap) + { + if (this.cacheMap.Count >= this.Capacity) + { + this.RemoveFirst(); + } + + MapItem cacheItem = new(key, value); + LinkedListNode node = new(cacheItem); + this.lruList.AddLast(node); + this.cacheMap.Add(key, node); + } + } + + private void RemoveFirst() + { + // Remove from LRUPriority + LinkedListNode node = this.lruList.First; + this.lruList.RemoveFirst(); + + // Remove from cache + this.cacheMap.Remove(node.Value.Key); + + // dispose + this.dispose?.Invoke(node.Value.Value); + } + + private class MapItem + { + public MapItem(TKey k, TValue v) + { + this.Key = k; + this.Value = v; + } + + public TKey Key { get; } + + public TValue Value { get; } + } +} + diff --git a/src/IFoxCAD.Basal/LinkedHashSet.cs b/src/IFoxCAD.Basal/LinkedHashSet.cs new file mode 100644 index 0000000..8659c24 --- /dev/null +++ b/src/IFoxCAD.Basal/LinkedHashSet.cs @@ -0,0 +1,221 @@ +namespace IFoxCAD.Basal; + +public class LinkedHashSet : ICollection where T : IComparable +{ + private readonly IDictionary> m_Dictionary; + private readonly LoopList m_LinkedList; + + public LinkedHashSet() + { + m_Dictionary = new Dictionary>(); + m_LinkedList = new LoopList(); + } + + public LoopListNode? First => m_LinkedList.First; + + public LoopListNode? Last => m_LinkedList.Last; + + public LoopListNode? MinNode { get; set; } + + public bool Add(T item) + { + if (m_Dictionary.ContainsKey(item)) + return false; + var node = m_LinkedList.AddLast(item); + m_Dictionary.Add(item, node); + + if (MinNode is null) + { + MinNode = node; + } + else + { + if (item.CompareTo(MinNode.Value) < 0) + { + MinNode = node; + } + } + + + + return true; + } + + void ICollection.Add(T item) + { + Add(item); + } + + public LoopListNode AddFirst(T value) + { + if (m_Dictionary.ContainsKey(value)) + { + return m_Dictionary[value]; + } + var node = m_LinkedList.AddFirst(value); + m_Dictionary.Add(value, node); + if (MinNode is null) + { + MinNode = node; + } + else + { + if (value.CompareTo(MinNode.Value) < 0) + { + MinNode = node; + } + } + return node; + } + + public void AddRange(IEnumerable collection) + { + foreach (var item in collection) + { + Add(item); + } + } + + + public void Clear() + { + m_LinkedList.Clear(); + m_Dictionary.Clear(); + } + + public bool Remove(T item) + { + bool found = m_Dictionary.TryGetValue(item, out LoopListNode node); + if (!found) return false; + m_Dictionary.Remove(item); + m_LinkedList.Remove(node); + return true; + } + + public int Count + { + get { return m_Dictionary.Count; } + } + + public void For(LoopListNode from, Action action) + { + var first = from; + var last = from; + if(first is null) return; + + for (int i = 0; i < Count; i++) + { + + action.Invoke(i,first!.Value, last!.Value); + first = first.Next; + last = last.Previous; + } + } + + public List ToList() + { + return m_LinkedList.ToList(); + } + + public IEnumerator GetEnumerator() + { + return m_LinkedList.GetEnumerator(); + } + + IEnumerator IEnumerable.GetEnumerator() + { + return GetEnumerator(); + } + + + public bool Contains(T item) + { + return m_Dictionary.ContainsKey(item); + } + + public void CopyTo(T[] array, int arrayIndex) + { + //m_LinkedList.CopyTo(array, arrayIndex); + return; + } + + public bool SetFirst(LoopListNode node) + { + return m_LinkedList.SetFirst(node); + } + + public LinkedHashSet Clone() + { + var newset = new LinkedHashSet(); + foreach (var item in this) + { + newset.Add(item); + } + return newset; + } + + public virtual bool IsReadOnly + { + get { return m_Dictionary.IsReadOnly; } + } + + public override string ToString() + { + return m_LinkedList.ToString(); + } + + public void UnionWith(IEnumerable other) + { + throw GetNotSupportedDueToSimplification(); + } + + public void IntersectWith(IEnumerable other) + { + throw GetNotSupportedDueToSimplification(); + } + + public void ExceptWith(IEnumerable other) + { + throw GetNotSupportedDueToSimplification(); + } + + public bool IsSubsetOf(IEnumerable other) + { + throw GetNotSupportedDueToSimplification(); + } + + public void SymmetricExceptWith(IEnumerable other) + { + throw GetNotSupportedDueToSimplification(); + } + + public bool IsSupersetOf(IEnumerable other) + { + throw GetNotSupportedDueToSimplification(); + } + + public bool IsProperSupersetOf(IEnumerable other) + { + throw GetNotSupportedDueToSimplification(); + } + + public bool IsProperSubsetOf(IEnumerable other) + { + throw GetNotSupportedDueToSimplification(); + } + + public bool Overlaps(IEnumerable other) + { + throw GetNotSupportedDueToSimplification(); + } + + public bool SetEquals(IEnumerable other) + { + throw GetNotSupportedDueToSimplification(); + } + + private static Exception GetNotSupportedDueToSimplification() + { + return new NotSupportedException("This method is not supported due to simplification of example code."); + } +} diff --git a/src/IFoxCAD.Basal/LinqEx.cs b/src/IFoxCAD.Basal/LinqEx.cs index 7b20401..8112e63 100644 --- a/src/IFoxCAD.Basal/LinqEx.cs +++ b/src/IFoxCAD.Basal/LinqEx.cs @@ -1,342 +1,335 @@ -using System; -using System.Collections.Generic; -using System.Linq; +namespace IFoxCAD.Basal; -namespace IFoxCAD.Linq +/// +/// linq 扩展类 +/// +public static class LinqEx { + #region FindByMax + /// - /// linq 扩展类 + /// 按转换函数找出序列中最大键值的对应值 /// - public static class LinqEx + /// + /// + /// 序列 + /// 转换函数 + /// 最大键值的对应值 + public static TValue FindByMax(this IEnumerable source, Func func) + where TKey : IComparable { - #region FindByMax - - /// - /// 按转换函数找出序列中最大键值的对应值 - /// - /// - /// - /// 序列 - /// 转换函数 - /// 最大键值的对应值 - public static TValue FindByMax(this IEnumerable source, Func func) - where TKey : IComparable - { - var itor = source.GetEnumerator(); - if (!itor.MoveNext()) - throw new ArgumentNullException(); + var itor = source.GetEnumerator(); + if (!itor.MoveNext()) + throw new ArgumentNullException(nameof(source), "对象为 null"); - TValue value = itor.Current; - TKey key = func(value); + TValue value = itor.Current; + TKey key = func(value); - while (itor.MoveNext()) + while (itor.MoveNext()) + { + TKey tkey = func(itor.Current); + if (tkey.CompareTo(key) > 0) { - TKey tkey = func(itor.Current); - if (tkey.CompareTo(key) > 0) - { - key = tkey; - value = itor.Current; - } + key = tkey; + value = itor.Current; } - return value; } + return value; + } - /// - /// 按转换函数找出序列中最大键值的对应值 - /// - /// - /// - /// 序列 - /// 对应的最大键值 - /// 转换函数 - /// 最大键值的对应值 - public static TValue FindByMax(this IEnumerable source, out TKey maxResult, Func func) - where TKey : IComparable - { - var itor = source.GetEnumerator(); - if (!itor.MoveNext()) - throw new ArgumentNullException(); + /// + /// 按转换函数找出序列中最大键值的对应值 + /// + /// + /// + /// 序列 + /// 对应的最大键值 + /// 转换函数 + /// 最大键值的对应值 + public static TValue FindByMax(this IEnumerable source, out TKey maxResult, Func func) + where TKey : IComparable + { + var itor = source.GetEnumerator(); + if (!itor.MoveNext()) + throw new ArgumentNullException(nameof(source), "对象为 null"); - TValue value = itor.Current; - TKey key = func(value); + TValue value = itor.Current; + TKey key = func(value); - while (itor.MoveNext()) + while (itor.MoveNext()) + { + TKey tkey = func(itor.Current); + if (tkey.CompareTo(key) > 0) { - TKey tkey = func(itor.Current); - if (tkey.CompareTo(key) > 0) - { - key = tkey; - value = itor.Current; - } + key = tkey; + value = itor.Current; } - maxResult = key; - return value; } + maxResult = key; + return value; + } - /// - /// 按比较器找出序列中最大键值的对应值 - /// - /// - /// 序列 - /// 比较器 - /// 最大键值的对应值 - public static TValue FindByMax(this IEnumerable source, Comparison comparison) - { - var itor = source.GetEnumerator(); - if (!itor.MoveNext()) - throw new ArgumentNullException(); - - TValue value = itor.Current; + /// + /// 按比较器找出序列中最大键值的对应值 + /// + /// + /// 序列 + /// 比较器 + /// 最大键值的对应值 + public static TValue FindByMax(this IEnumerable source, Comparison comparison) + { + var itor = source.GetEnumerator(); + if (!itor.MoveNext()) + throw new ArgumentNullException(nameof(source), "对象为 null"); - while (itor.MoveNext()) - { - if (comparison(itor.Current, value) > 0) - value = itor.Current; - } - return value; - } + TValue value = itor.Current; - #endregion FindByMax - - #region FindByMin - - /// - /// 按转换函数找出序列中最小键值的对应值 - /// - /// - /// - /// 序列 - /// 对应的最小键值 - /// 转换函数 - /// 最小键值的对应值 - public static TValue FindByMin(this IEnumerable source, out TKey minKey, Func func) - where TKey : IComparable + while (itor.MoveNext()) { - var itor = source.GetEnumerator(); - if (!itor.MoveNext()) - throw new ArgumentNullException(); + if (comparison(itor.Current, value) > 0) + value = itor.Current; + } + return value; + } - TValue value = itor.Current; - TKey key = func(value); + #endregion FindByMax - while (itor.MoveNext()) - { - TKey tkey = func(itor.Current); - if (tkey.CompareTo(key) < 0) - { - key = tkey; - value = itor.Current; - } - } - minKey = key; - return value; - } + #region FindByMin - /// - /// 按转换函数找出序列中最小键值的对应值 - /// - /// - /// - /// 序列 - /// 转换函数 - /// 最小键值的对应值 - public static TValue FindByMin(this IEnumerable source, Func func) - where TKey : IComparable - { - var itor = source.GetEnumerator(); - if (!itor.MoveNext()) - throw new ArgumentNullException(); + /// + /// 按转换函数找出序列中最小键值的对应值 + /// + /// + /// + /// 序列 + /// 对应的最小键值 + /// 转换函数 + /// 最小键值的对应值 + public static TValue FindByMin(this IEnumerable source, out TKey minKey, Func func) + where TKey : IComparable + { + var itor = source.GetEnumerator(); + if (!itor.MoveNext()) + throw new ArgumentNullException(nameof(source), "对象为 null"); - TValue value = itor.Current; - TKey key = func(value); + TValue value = itor.Current; + TKey key = func(value); - while (itor.MoveNext()) + while (itor.MoveNext()) + { + TKey tkey = func(itor.Current); + if (tkey.CompareTo(key) < 0) { - TKey tkey = func(itor.Current); - if (tkey.CompareTo(key) < 0) - { - key = tkey; - value = itor.Current; - } + key = tkey; + value = itor.Current; } - return value; } + minKey = key; + return value; + } - /// - /// 按比较器找出序列中最小键值的对应值 - /// - /// - /// 序列 - /// 比较器 - /// 最小键值的对应值 - public static TValue FindByMin(this IEnumerable source, Comparison comparison) - { - var itor = source.GetEnumerator(); - if (!itor.MoveNext()) - throw new ArgumentNullException(); + /// + /// 按转换函数找出序列中最小键值的对应值 + /// + /// + /// + /// 序列 + /// 转换函数 + /// 最小键值的对应值 + public static TValue FindByMin(this IEnumerable source, Func func) + where TKey : IComparable + { + var itor = source.GetEnumerator(); + if (!itor.MoveNext()) + throw new ArgumentNullException(nameof(source), "对象为 null"); - TValue value = itor.Current; + TValue value = itor.Current; + TKey key = func(value); - while (itor.MoveNext()) + while (itor.MoveNext()) + { + TKey tkey = func(itor.Current); + if (tkey.CompareTo(key) < 0) { - if (comparison(itor.Current, value) < 0) - value = itor.Current; + key = tkey; + value = itor.Current; } - return value; } + return value; + } - #endregion FindByMin + /// + /// 按比较器找出序列中最小键值的对应值 + /// + /// + /// 序列 + /// 比较器 + /// 最小键值的对应值 + public static TValue FindByMin(this IEnumerable source, Comparison comparison) + { + var itor = source.GetEnumerator(); + if (!itor.MoveNext()) + throw new ArgumentNullException(nameof(source), "对象为 null"); - #region FindByExt + TValue value = itor.Current; - /// - /// 按转换函数找出序列中最(小/大)键值的对应值 - /// - /// - /// - /// 序列 - /// 转换函数 - /// 最(小/大)键值的对应值 - public static TValue[] FindByExt(this IEnumerable source, Func func) - where TKey : IComparable + while (itor.MoveNext()) { - var itor = source.GetEnumerator(); - if (!itor.MoveNext()) - throw new ArgumentNullException(); + if (comparison(itor.Current, value) < 0) + value = itor.Current; + } + return value; + } - TValue[] values = new TValue[2]; - values[0] = values[1] = itor.Current; + #endregion FindByMin - TKey[] keys = new TKey[2]; - keys[0] = keys[1] = func(itor.Current); + #region FindByExt - while (itor.MoveNext()) - { - TKey tkey = func(itor.Current); - if (tkey.CompareTo(keys[0]) < 0) - { - keys[0] = tkey; - values[0] = itor.Current; - } - else if (tkey.CompareTo(keys[1]) > 0) - { - keys[1] = tkey; - values[1] = itor.Current; - } - } - return values; - } + /// + /// 按转换函数找出序列中最(小/大)键值的对应值 + /// + /// + /// + /// 序列 + /// 转换函数 + /// 最(小/大)键值的对应值 + public static TValue[] FindByExt(this IEnumerable source, Func func) + where TKey : IComparable + { + var itor = source.GetEnumerator(); + if (!itor.MoveNext()) + throw new ArgumentNullException(nameof(source), "对象为 null"); - /// - /// 按比较器找出序列中最(小/大)键值的对应值 - /// - /// - /// 序列 - /// 比较器 - /// 最(小/大)键值的对应值 - public static TValue[] FindByExt(this IEnumerable source, Comparison comparison) - { - var itor = source.GetEnumerator(); - if (!itor.MoveNext()) - throw new ArgumentNullException(); + TValue[] values = new TValue[2]; + values[0] = values[1] = itor.Current; - TValue[] values = new TValue[2]; - values[0] = values[1] = itor.Current; + TKey[] keys = new TKey[2]; + keys[0] = keys[1] = func(itor.Current); - while (itor.MoveNext()) + while (itor.MoveNext()) + { + TKey tkey = func(itor.Current); + if (tkey.CompareTo(keys[0]) < 0) { - if (comparison(itor.Current, values[0]) < 0) - values[0] = itor.Current; - else if (comparison(itor.Current, values[1]) > 0) - values[1] = itor.Current; + keys[0] = tkey; + values[0] = itor.Current; + } + else if (tkey.CompareTo(keys[1]) > 0) + { + keys[1] = tkey; + values[1] = itor.Current; } - return values; } + return values; + } - /// - /// 按转换函数找出序列中最(小/大)键值的对应键值 - /// - /// - /// - /// 序列 - /// 转换函数 - /// 最(小/大)键值 - public static TKey[] FindExt(this IEnumerable source, Func func) - where TKey : IComparable - { - var itor = source.GetEnumerator(); - if (!itor.MoveNext()) - throw new ArgumentNullException(); + /// + /// 按比较器找出序列中最(小/大)键值的对应值 + /// + /// + /// 序列 + /// 比较器 + /// 最(小/大)键值的对应值 + public static TValue[] FindByExt(this IEnumerable source, Comparison comparison) + { + var itor = source.GetEnumerator(); + if (!itor.MoveNext()) + throw new ArgumentNullException(nameof(source), "对象为 null"); - TKey[] keys = new TKey[2]; - keys[0] = keys[1] = func(itor.Current); + TValue[] values = new TValue[2]; + values[0] = values[1] = itor.Current; - while (itor.MoveNext()) - { - TKey tkey = func(itor.Current); - if (tkey.CompareTo(keys[0]) < 0) - keys[0] = tkey; - else if (tkey.CompareTo(keys[1]) > 0) - keys[1] = tkey; - } - return keys; + while (itor.MoveNext()) + { + if (comparison(itor.Current, values[0]) < 0) + values[0] = itor.Current; + else if (comparison(itor.Current, values[1]) > 0) + values[1] = itor.Current; } + return values; + } - #endregion FindByExt + /// + /// 按转换函数找出序列中最(小/大)键值的对应键值 + /// + /// + /// + /// 序列 + /// 转换函数 + /// 最(小/大)键值 + public static TKey[] FindExt(this IEnumerable source, Func func) + where TKey : IComparable + { + var itor = source.GetEnumerator(); + if (!itor.MoveNext()) + throw new ArgumentNullException(nameof(source), "对象为 null"); - #region Order + TKey[] keys = new TKey[2]; + keys[0] = keys[1] = func(itor.Current); - /// - /// 自定义的比较泛型类 - /// - /// 泛型 - private class SpecComparer : IComparer + while (itor.MoveNext()) { - private Comparison _comp; - - internal SpecComparer(Comparison comp) - { - _comp = comp; - } + TKey tkey = func(itor.Current); + if (tkey.CompareTo(keys[0]) < 0) + keys[0] = tkey; + else if (tkey.CompareTo(keys[1]) > 0) + keys[1] = tkey; + } + return keys; + } - #region IComparer 成员 + #endregion FindByExt - public int Compare(T x, T y) - { - return _comp(x, y); - } + #region Order - #endregion IComparer 成员 - } + /// + /// 自定义的比较泛型类 + /// + /// 泛型 + private class SpecComparer : IComparer + { + private readonly Comparison _comp; - /// - /// 使用指定的比较器将序列按升序排序 - /// - /// 输入泛型 - /// 输出泛型 - /// 序列 - /// 用于从元素中提取键的函数 - /// 比较器 - /// 排序的序列 - public static IOrderedEnumerable OrderBy(this IEnumerable source, Func keySelector, Comparison comparison) + internal SpecComparer(Comparison comp) { - return source.OrderBy(keySelector, new SpecComparer(comparison)); + _comp = comp; } - /// - /// 使用指定的比较器将其后的序列按升序排序 - /// - /// 输入泛型 - /// 输出泛型 - /// 序列 - /// 用于从元素中提取键的函数 - /// 比较器 - /// 排序的序列 - public static IOrderedEnumerable ThenBy(this IOrderedEnumerable source, Func keySelector, Comparison comparison) + #region IComparer 成员 + public int Compare(T x, T y) { - return source.ThenBy(keySelector, new SpecComparer(comparison)); + return _comp(x, y); } + #endregion IComparer 成员 + } + + /// + /// 使用指定的比较器将序列按升序排序 + /// + /// 输入泛型 + /// 输出泛型 + /// 序列 + /// 用于从元素中提取键的函数 + /// 比较器 + /// 排序的序列 + public static IOrderedEnumerable OrderBy(this IEnumerable source, Func keySelector, Comparison comparison) + { + return source.OrderBy(keySelector, new SpecComparer(comparison)); + } - #endregion Order + /// + /// 使用指定的比较器将其后的序列按升序排序 + /// + /// 输入泛型 + /// 输出泛型 + /// 序列 + /// 用于从元素中提取键的函数 + /// 比较器 + /// 排序的序列 + public static IOrderedEnumerable ThenBy(this IOrderedEnumerable source, Func keySelector, Comparison comparison) + { + return source.ThenBy(keySelector, new SpecComparer(comparison)); } + + #endregion Order } diff --git a/src/IFoxCAD.Basal/ListEx.cs b/src/IFoxCAD.Basal/ListEx.cs new file mode 100644 index 0000000..b0d20c0 --- /dev/null +++ b/src/IFoxCAD.Basal/ListEx.cs @@ -0,0 +1,25 @@ + +namespace IFoxCAD.Basal; + +public static class ListEx +{ + public static bool EqualsAll(this IList a, IList b) + { + return EqualsAll(a, b, null); + // there is a slight performance gain in passing null here. + // It is how it is done in other parts of the framework. + } + + public static bool EqualsAll(this IList a!!, IList b!!, IEqualityComparer? comparer) + { + if (a.Count != b.Count) + return false; + + comparer ??= EqualityComparer.Default; + + for (int i = 0; i < a.Count; i++) + if (!comparer.Equals(a[i], b[i])) + return false; + return true; + } +} diff --git a/src/IFoxCAD.Basal/LoopList.cs b/src/IFoxCAD.Basal/LoopList.cs index 2c0b088..c0e021c 100644 --- a/src/IFoxCAD.Basal/LoopList.cs +++ b/src/IFoxCAD.Basal/LoopList.cs @@ -1,281 +1,265 @@ -using System; -using System.Collections; -using System.Collections.Generic; -using System.Text; +namespace IFoxCAD.Basal; -namespace IFoxCAD.Collections +#line hidden //调试的时候跳过它 + +/// +/// 环链表节点 +/// +/// +public class LoopListNode { + #region 成员 /// - /// 环链表节点 - /// - /// - public class LoopListNode - { - #region 成员 - /// - /// 取值 - /// - public T Value; - - /// - /// 上一个节点 - /// - public LoopListNode? Previous { internal set; get; } - - /// - /// 下一个节点 - /// - public LoopListNode? Next { internal set; get; } - - /// - /// 环链表序列 - /// - public LoopList? List { internal set; get; } - #endregion - #region 构造 - /// - /// 环链表节点构造函数 - /// - /// 节点值 - public LoopListNode(T value, LoopList ts) - { - Value = value; - List = ts; - } + /// 取值 + ///
+ public T Value; - /// - /// 获取当前节点的临近节点 - /// - /// 搜索方向标志,为向前搜索,为向后搜索 - /// - public LoopListNode? GetNext(bool forward) - { - return forward ? Next : Previous; - } - #endregion - #region 方法 - /// - /// 无效化成员 - /// - internal void Invalidate() - { - List = null; - Next = null; - Previous = null; - } - #endregion + /// + /// 上一个节点 + /// + public LoopListNode? Previous { internal set; get; } + + /// + /// 下一个节点 + /// + public LoopListNode? Next { internal set; get; } + + /// + /// 环链表序列 + /// + public LoopList? List { internal set; get; } + #endregion + + #region 构造 + /// + /// 环链表节点构造函数 + /// + /// 节点值 + public LoopListNode(T value, LoopList ts) + { + Value = value; + List = ts; } /// - /// 环链表 + /// 获取当前节点的临近节点 /// - /// - public class LoopList : IEnumerable, IFormattable + /// 搜索方向标志,为向前搜索,为向后搜索 + /// + public LoopListNode? GetNext(bool forward) { - #region 成员 - /// - /// 节点数 - /// - public int Count { get; private set; } + return forward ? Next : Previous; + } + #endregion - /// - /// 首节点 - /// - public LoopListNode? First { get; private set; } + #region 方法 + /// + /// 无效化成员 + /// + internal void Invalidate() + { + List = null; + Next = null; + Previous = null; + } + #endregion +} + +/// +/// 环链表 +/// +/// +public class LoopList : IEnumerable, IFormattable +{ + #region 成员 - /// - /// 尾节点 - /// - public LoopListNode? Last => First?.Previous; + /// + /// 节点数 + /// + public int Count { get; private set; } - #endregion - #region 构造 - /// - /// 默认构造函数 - /// - public LoopList() { } + /// + /// 首节点 + /// + public LoopListNode? First { get; private set; } - /// - /// 环链表构造函数 - /// - /// 节点迭代器 - public LoopList(IEnumerable values) - { - var ge = values.GetEnumerator(); - while (ge.MoveNext()) - Add(ge.Current); - } + /// + /// 尾节点 + /// + public LoopListNode? Last => First?.Previous; - #endregion - #region 方法 - /// - /// 设置首节点 - /// - /// 节点 - /// - public bool SetFirst(LoopListNode node) - { - if (!Contains(node)) - return false; - First = node; - return true; - } - /// - /// 交换两个节点的值 - /// - /// 第一个节点 - /// 第二个节点 - public void Swap(LoopListNode node1, LoopListNode node2) - { -#if NET35 - var value = node1.Value; - node1.Value = node2.Value; - node2.Value = value; -#else - (node2.Value, node1.Value) = (node1.Value, node2.Value); -#endif - } - /// - /// 链内翻转 - /// - public void Reverse() - { - var first = First; - if (first is null) - return; - var last = Last; - for (int i = 0; i < Count / 2; i++) - { - Swap(first!, last!); - first = first!.Next; - last = last!.Previous; - } - } + #endregion - /// - /// 清理 - /// - public void Clear() - { - //移除头部,表示链表再也无法遍历得到 - First = null; - Count = 0; - } + #region 构造 + + /// + /// 默认构造函数 + /// + public LoopList() { } + + /// + /// 环链表构造函数 + /// + /// 节点迭代器 + public LoopList(IEnumerable values) + { + var ge = values.GetEnumerator(); + while (ge.MoveNext()) + Add(ge.Current); + } - /// - /// 从头遍历_非迭代器 - /// - /// - public void ForEach(Func, bool> action) + #endregion + + #region 方法 + + /// + /// 设置首节点 + /// + /// 节点 + /// + public bool SetFirst(LoopListNode node) + { + if (!Contains(node)) + return false; + + First = node; + return true; + } + + /// + /// 交换两个节点的值 + /// + /// 第一个节点 + /// 第二个节点 + public void Swap(LoopListNode node1, LoopListNode node2) + { + (node2.Value, node1.Value) = (node1.Value, node2.Value); + } + + /// + /// 链内翻转 + /// + public void Reverse() + { + var first = First; + if (first is null) + return; + var last = Last; + for (int i = 0; i < Count / 2; i++) { - var node = First; - if (node is null) - return; - for (int i = 0; i < Count; i++) - { - if (action(node!)) - break; - node = node!.Next; - } + Swap(first!, last!); + first = first!.Next; + last = last!.Previous; } + } - #region Contains - /// - /// 是否包含节点 - /// - /// - /// - public bool Contains(LoopListNode node) + /// + /// 清理 + /// + public void Clear() + { + //移除头部,表示链表再也无法遍历得到 + First = null; + Count = 0; + } + + /// + /// 从头遍历_非迭代器(此处和通用ForEach冲突,所以内部用) + /// + /// + void ForEach(Func, bool> action) + { + var node = First; + if (node is null) + return; + for (int i = 0; i < Count; i++) { - return node is not null && node.List == this; + if (action(node!)) + break; + node = node!.Next; } + } - /// - /// 是否包含值 - /// - /// - /// - public bool Contains(T value) + /// + /// 从头遍历_非迭代器(扔出计数) + /// + /// + public void For(Func, bool> action) + { + var node = First; + if (node is null) + return; + for (int i = 0; i < Count; i++) { - bool result = false; - ForEach(node => { - if (node.Value!.Equals(value)) - { - result = true; - return true; - } - return false; - }); - return result; + if (action(i, node!)) + break; + node = node!.Next; } + } - /// - /// 查找第一个出现的节点 - /// - /// - /// - public LoopListNode? Find(T value) - { - //LoopListNode result = null; - //ForEach(node => - //{ - // if (node.Value.Equals(t2)) - // { - // result = node; - // return true; - // } - // return false; - //}); - //return result; - - LoopListNode? node = First; - var c = EqualityComparer.Default; - if (node is not null) + #region Contains + + /// + /// 是否包含节点 + /// + /// + /// + public bool Contains(LoopListNode node) + { + return node is not null && node.List == this; + } + + /// + /// 是否包含值 + /// + /// + /// + public bool Contains(T value) + { + bool result = false; + ForEach(node => { + if (node.Value!.Equals(value)) { - if (value is not null) - { - do - { - if (c.Equals(node!.Value, value)) - return node; - node = node.Next; - } while (node != First); - } - else - { - do - { - if (node!.Value is null) - return node; - node = node.Next; - } while (node != First); - } + result = true; + return true; } - return null; - } + return false; + }); + return result; + } - /// - /// 查找所有出现的节点 - /// - /// - /// - public IEnumerable>? Finds(T value) + /// + /// 查找第一个出现的节点 + /// + /// + /// + public LoopListNode? Find(T value) + { + //LoopListNode result = null; + //ForEach(node => + //{ + // if (node.Value.Equals(t2)) + // { + // result = node; + // return true; + // } + // return false; + //}); + //return result; + + LoopListNode? node = First; + var c = EqualityComparer.Default; + if (node is not null) { - LoopListNode? node = First; - if (node is null) - return null; - - List> result = new(); - var c = EqualityComparer.Default; if (value is not null) { do { if (c.Equals(node!.Value, value)) - result.Add(node); + return node; node = node.Next; } while (node != First); } @@ -284,422 +268,475 @@ public bool Contains(T value) do { if (node!.Value is null) - result.Add(node); + return node; node = node.Next; } while (node != First); } - return result; } + return null; + } - /// - /// 获取节点 - /// - /// - /// - public LoopListNode? GetNode(Func func) - { - LoopListNode? result = null; - ForEach(node => { - if (func(node.Value)) - { - result = node; - return true; - } - return false; - }); - return result; - } + /// + /// 查找所有出现的节点 + /// + /// + /// + public IEnumerable>? Finds(T value) + { + LoopListNode? node = First; + if (node is null) + return null; - #endregion - #region Add - /// - /// 在首节点之前插入节点,并设置新节点为首节点 - /// - /// - /// - public LoopListNode AddFirst(T value) + List> result = new(); + var c = EqualityComparer.Default; + if (value is not null) { - var node = new LoopListNode(value, this); - - if (Count == 0) + do { - First = node; - First.Previous = First.Next = node; - } - else - { - LoopListNode last = Last!; - First!.Previous = last.Next = node; - node.Next = First; - node.Previous = last; - First = node; - } - Count++; - return First; + if (c.Equals(node!.Value, value)) + result.Add(node); + node = node.Next; + } while (node != First); } - - /// - /// 在尾节点之后插入节点,并设置新节点为尾节点 - /// - /// - /// - public LoopListNode Add(T value) + else { - var node = new LoopListNode(value, this); - - if (Count == 0) + do { - First = node; - First.Previous = First.Next = node; - } - else + if (node!.Value is null) + result.Add(node); + node = node.Next; + } while (node != First); + } + return result; + } + + /// + /// 获取节点 + /// + /// + /// + public LoopListNode? GetNode(Func func) + { + LoopListNode? result = null; + ForEach(node => { + if (func(node.Value)) { - var last = First!.Previous!; - First.Previous = last.Next = node; - node.Next = First; - node.Previous = last; + result = node; + return true; } - Count++; - return Last!; - } + return false; + }); + return result; + } - /// - /// 在尾节点之后插入节点,并设置新节点为尾节点_此函数仅为与LinkedList同名方法 - /// - /// - /// - public LoopListNode AddLast(T value) - { - return Add(value); - } + #endregion + #region Add - /// - /// 容器内容全部加入到末尾 - /// - /// - public void AddRange(IEnumerable list) - { - var ge = list.GetEnumerator(); - while (ge.MoveNext()) - Add(ge.Current); - } + /// + /// 在首节点之前插入节点,并设置新节点为首节点 + /// + /// + /// + public LoopListNode AddFirst(T value) + { + var node = new LoopListNode(value, this); - /// - /// 前面增加节点 - /// - /// - /// - /// - public LoopListNode AddBefore(LoopListNode node, T value) + if (Count == 0) { - if (node == First) - return AddFirst(value); - - var tnode = new LoopListNode(value, this); - node.Previous!.Next = tnode; - tnode.Previous = node.Previous; - node.Previous = tnode; - tnode.Next = node; - Count++; - return tnode; + First = node; + First.Previous = First.Next = node; } - - /// - /// 后面增加节点 - /// - /// - /// - /// - public LoopListNode AddAfter(LoopListNode node, T value) + else { - var tnode = new LoopListNode(value, this); - node.Next!.Previous = tnode; - tnode.Next = node.Next; - node.Next = tnode; - tnode.Previous = node; - Count++; - return tnode; + LoopListNode last = Last!; + First!.Previous = last.Next = node; + node.Next = First; + node.Previous = last; + First = node; } + Count++; + return First; + } + + /// + /// 在尾节点之后插入节点,并设置新节点为尾节点 + /// + /// + /// + public LoopListNode Add(T value) + { + var node = new LoopListNode(value, this); - #endregion - #region Remove - /// - /// 删除首节点 - /// - /// - public bool RemoveFirst() + if (Count == 0) { - switch (Count) - { - case 0: - return false; - - case 1: - First = null; - break; - - default: - LoopListNode last = Last!; - First = First!.Next; - First!.Previous = last; - last.Next = First; - break; - } - return true; + First = node; + First.Previous = First.Next = node; } - - /// - /// 删除尾节点 - /// - /// - public bool RemoveLast() + else { - switch (Count) - { - case 0: - return false; - - case 1: - First = null; - break; - - default: - LoopListNode last = Last!.Previous!; - last.Next = First; - First!.Previous = last; - break; - } - Count--; - return true; + var last = First!.Previous!; + First.Previous = last.Next = node; + node.Next = First; + node.Previous = last; } + Count++; + return Last!; + } + + /// + /// 在尾节点之后插入节点,并设置新节点为尾节点_此函数仅为与LinkedList同名方法 + /// + /// + /// + public LoopListNode AddLast(T value) + { + return Add(value); + } - /// - /// 删除节点 - /// - /// 指定节点 - /// - public bool Remove(LoopListNode node) + /// + /// 容器内容全部加入到末尾 + /// + /// + public void AddRange(IEnumerable list) + { + var ge = list.GetEnumerator(); + while (ge.MoveNext()) + Add(ge.Current); + } + + /// + /// 前面增加节点 + /// + /// + /// + /// + public LoopListNode AddBefore(LoopListNode node, T value) + { + if (node == First) + return AddFirst(value); + + var tnode = new LoopListNode(value, this); + node.Previous!.Next = tnode; + tnode.Previous = node.Previous; + node.Previous = tnode; + tnode.Next = node; + Count++; + return tnode; + } + + /// + /// 后面增加节点 + /// + /// + /// + /// + public LoopListNode AddAfter(LoopListNode node, T value) + { + var tnode = new LoopListNode(value, this); + node.Next!.Previous = tnode; + tnode.Next = node.Next; + node.Next = tnode; + tnode.Previous = node; + Count++; + return tnode; + } + + #endregion + + #region Remove + + /// + /// 删除首节点 + /// + /// + public bool RemoveFirst() + { + switch (Count) { - if (!Contains(node)) + case 0: return false; - InternalRemove(node); - return true; + + case 1: + First = null; + break; + + default: + LoopListNode last = Last!; + First = First!.Next; + First!.Previous = last; + last.Next = First; + break; } + Count--; + return true; + } - /// - /// 删除节点 - /// - /// 将移除所有含有此值 - /// - public bool Remove(T value) + /// + /// 删除尾节点 + /// + /// + public bool RemoveLast() + { + switch (Count) { - var lst = Finds(value); - if (lst is null) + case 0: return false; - var ge = lst!.GetEnumerator(); - while (ge.MoveNext()) - InternalRemove(ge.Current); - return true; + case 1: + First = null; + break; + + default: + LoopListNode last = Last!.Previous!; + last.Next = First; + First!.Previous = last; + break; } + Count--; + return true; + } - /// - /// 删除节点_内部调用 - /// - /// 此值肯定存在当前链表 - /// - void InternalRemove(LoopListNode node) - { - if (Count == 1 || node == First) - { - RemoveFirst(); - } - else - { - node.Next!.Previous = node.Previous; - node.Previous!.Next = node.Next; - } + /// + /// 删除此参数节点(唯一) + /// + /// 指定节点 + /// + public bool Remove(LoopListNode node) + { + if (!Contains(node)) + return false; + InternalRemove(node); + return true; + } - node.Invalidate(); - Count--; - } + /// + /// 删除含有此参数节点(所有) + /// + /// 将移除所有含有此值 + /// + public bool Remove(T value) + { + var lst = Finds(value); + if (lst is null) + return false; + + var ge = lst!.GetEnumerator(); + while (ge.MoveNext()) + InternalRemove(ge.Current); + return true; + } - #endregion - #region LinkTo - /// - /// 链接两节点,并去除这两个节点间的所有节点 - /// - /// - /// - public void LinkTo(LoopListNode from, LoopListNode to) + /// + /// 删除节点_内部调用 + /// + /// 此值肯定存在当前链表 + /// + void InternalRemove(LoopListNode node) + { + if (Count == 1 || node == First) { - if (from != to && Contains(from) && Contains(to)) - { - LoopListNode node = from.Next!; - bool isFirstChanged = false; - int number = 0; + RemoveFirst();//此处会减数字 + } + else + { + node.Next!.Previous = node.Previous; + node.Previous!.Next = node.Next; + Count--; + } + node.Invalidate(); + } - while (node != to) - { - if (node == First) - isFirstChanged = true; + #endregion - node = node.Next!; - number++; - } + #region LinkTo - from.Next = to; - to.Previous = from; + /// + /// 链接两节点,并去除这两个节点间的所有节点 + /// + /// + /// + public void LinkTo(LoopListNode from, LoopListNode to) + { + if (from != to && Contains(from) && Contains(to)) + { + LoopListNode node = from.Next!; + bool isFirstChanged = false; + int number = 0; - if (number > 0 && isFirstChanged) - First = to; + while (node != to) + { + if (node == First) + isFirstChanged = true; - Count -= number; + node = node.Next!; + number++; } - } - /// - /// 链接两节点,并去除这两个节点间的所有节点 - /// - /// - /// - /// - public void LinkTo(LoopListNode from, LoopListNode to, int number) - { - if (from != to && Contains(from) && Contains(to)) - { - from.Next = to; - to.Previous = from; + from.Next = to; + to.Previous = from; + + if (number > 0 && isFirstChanged) First = to; - Count -= number; - } - } - /// - /// 链接两节点,并去除这两个节点间的所有节点 - /// - /// - /// - /// - /// - public void LinkTo(LoopListNode from, LoopListNode to, int number, bool isFirstChanged) - { - if (from != to && Contains(from) && Contains(to)) - { - from.Next = to; - to.Previous = from; - if (isFirstChanged) - First = to; - Count -= number; - } + Count -= number; } + } - #endregion - #endregion - #region IEnumerable - /// - /// 获取节点的查询器 - /// - /// - /// - public IEnumerable> GetNodes(LoopListNode from) + /// + /// 链接两节点,并去除这两个节点间的所有节点 + /// + /// + /// + /// + public void LinkTo(LoopListNode from, LoopListNode to, int number) + { + if (from != to && Contains(from) && Contains(to)) { - var node = from; - for (int i = 0; i < Count; i++) - { - yield return node!; - node = node!.Next; - } + from.Next = to; + to.Previous = from; + First = to; + Count -= number; } + } - /// - /// 获取节点的查询器 - /// - /// - public IEnumerable> GetNodes() + /// + /// 链接两节点,并去除这两个节点间的所有节点 + /// + /// + /// + /// + /// + public void LinkTo(LoopListNode from, LoopListNode to, int number, bool isFirstChanged) + { + if (from != to && Contains(from) && Contains(to)) { - LoopListNode node = First!; - for (int i = 0; i < Count; i++) - { - yield return node!; - node = node.Next!; - } + from.Next = to; + to.Previous = from; + if (isFirstChanged) + First = to; + Count -= number; } + } + + #endregion - /// - /// 获取节点值的查询器 - /// - /// - public IEnumerator GetEnumerator() + #endregion + + #region IEnumerable + + /// + /// 获取节点的查询器 + /// + /// + /// + public IEnumerable> GetNodes(LoopListNode from) + { + var node = from; + for (int i = 0; i < Count; i++) { - LoopListNode node = First!; - for (int i = 0; i < Count; i++) - { - yield return node!.Value; - node = node.Next!; - } + yield return node!; + node = node!.Next; } + } - IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); - - #region IEnumerable 成员 - IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); - - #endregion IEnumerable 成员 - #endregion - #region IFormattable - /// - /// 转换为字符串_格式化实现 - /// - /// - /// - /// - string IFormattable.ToString(string? format, IFormatProvider? formatProvider) + + + /// + /// 获取节点的查询器 + /// + /// + public IEnumerable> GetNodes() + { + LoopListNode node = First!; + for (int i = 0; i < Count; i++) { - return ToString(format, formatProvider); + yield return node!; + node = node.Next!; } + } - /// - /// 转换为字符串_无参调用 - /// - /// - public override string ToString() + /// + /// 获取节点值的查询器 + /// + /// + public IEnumerator GetEnumerator() + { + LoopListNode node = First!; + for (int i = 0; i < Count; i++) { - return ToString(null, null); + yield return node!.Value; + node = node.Next!; } + } + + IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); + + #region IEnumerable 成员 + + IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); - /// - /// 转换为字符串_有参调用 - /// - /// - public string ToString(string? format, IFormatProvider? formatProvider = null) + #endregion IEnumerable 成员 + + #endregion + + #region IFormattable + /// + /// 转换为字符串_格式化实现 + /// + /// + /// + /// + string IFormattable.ToString(string? format, IFormatProvider? formatProvider) + { + return ToString(format, formatProvider); + } + + /// + /// 转换为字符串_无参调用 + /// + /// + public override string ToString() + { + return ToString(null, null); + } + + /// + /// 转换为字符串_有参调用 + /// + /// + public string ToString(string? format, IFormatProvider? formatProvider = null) + { + var s = new StringBuilder(); + s.Append($"Count = {Count};"); + if (format is null) { - var s = new StringBuilder(); - s.Append($"Count = {Count};"); - if (format is null) - { - s.Append("{ "); - foreach (T value in this) - s.Append($"{value} "); - s.Append(" }"); - } - return s.ToString(); + s.Append("{ "); + foreach (T value in this) + s.Append($"{value} "); + s.Append(" }"); } - #endregion - #region ICloneable - /* 山人说无法分辨ICloneable接口是深浅克隆,因此不要在泛型模板实现克隆函数,让用户自己来 - * 因此约定了:CopyTo(T,index)是深克隆;MemberwiseClone()是浅克隆; - * public object Clone() - * { - * var lst = new LoopList>(); - * ForEach(node => { - * lst.Add(node); - * return false; - * }); - * return lst; - * } - */ - #endregion - } -} \ No newline at end of file + return s.ToString(); + } + #endregion + + #region ICloneable + /* 山人说无法分辨ICloneable接口是深浅克隆, + * 因此不要在泛型模板实现克隆函数, + * 让用户自己来 new(xx)实现浅克隆,所以也不提供Clone()了 + * + * 因此约定了:CopyTo(T,index)是深克隆;MemberwiseClone()是浅克隆; + * public object Clone() + * { + * var lst = new LoopList>(); + * ForEach(node => { + * lst.Add(node); + * return false; + * }); + * return lst; + * } + */ + #endregion +} + +#line default \ No newline at end of file diff --git a/src/IFoxCAD.Basal/Sortedset/ISet.cs b/src/IFoxCAD.Basal/Sortedset/ISet.cs new file mode 100644 index 0000000..549e9f0 --- /dev/null +++ b/src/IFoxCAD.Basal/Sortedset/ISet.cs @@ -0,0 +1,71 @@ +#if NET35 +// ==++== +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +// ==--== +/*============================================================ +** +** Interface: ISet +** +** kimhamil +** +** +** Purpose: Base interface for all generic sets. +** +** +===========================================================*/ +namespace System.Collections.Generic +{ + + using System; + using System.Runtime.CompilerServices; + + + /// + /// Generic collection that guarantees the uniqueness of its elements, as defined + /// by some comparer. It also supports basic set operations such as Union, Intersection, + /// Complement and Exclusive Complement. + /// + public interface ISet : ICollection + { + + //Add ITEM to the set, return true if added, false if duplicate + new bool Add(T item); + + //Transform this set into its union with the IEnumerable other + void UnionWith(IEnumerable other); + + //Transform this set into its intersection with the IEnumberable other + void IntersectWith(IEnumerable other); + + //Transform this set so it contains no elements that are also in other + void ExceptWith(IEnumerable other); + + //Transform this set so it contains elements initially in this or in other, but not both + void SymmetricExceptWith(IEnumerable other); + + //Check if this set is a subset of other + bool IsSubsetOf(IEnumerable other); + + //Check if this set is a superset of other + bool IsSupersetOf(IEnumerable other); + + //Check if this set is a subset of other, but not the same as it + bool IsProperSupersetOf(IEnumerable other); + + //Check if this set is a superset of other, but not the same as it + bool IsProperSubsetOf(IEnumerable other); + + //Check if this set has any elements in common with other + bool Overlaps(IEnumerable other); + + //Check if this set contains the same and only the same elements as other + bool SetEquals(IEnumerable other); + + + + } + +} +#endif \ No newline at end of file diff --git a/src/IFoxCAD.Basal/Sortedset/SR.cs b/src/IFoxCAD.Basal/Sortedset/SR.cs new file mode 100644 index 0000000..093de14 --- /dev/null +++ b/src/IFoxCAD.Basal/Sortedset/SR.cs @@ -0,0 +1,907 @@ +#if NET35 +#pragma warning disable CS8603 // 可能返回 null 引用。 +namespace System; + + +using System; +using System.Reflection; +using System.Globalization; +using System.Resources; +using System.Text; +using System.ComponentModel; +using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; + +[AttributeUsage(AttributeTargets.All)] +internal sealed class SRDescriptionAttribute : DescriptionAttribute +{ + public SRDescriptionAttribute(string description) + { + DescriptionValue = SR.GetString(description); + } + + public SRDescriptionAttribute(string description, string resourceSet) + { + ResourceManager rm = new ResourceManager(resourceSet, Assembly.GetExecutingAssembly()); + DescriptionValue = rm.GetString(description); + System.Diagnostics.Debug.Assert(DescriptionValue != null, string.Format(CultureInfo.CurrentCulture, "String resource {0} not found.", new object[] { description })); + } +} + +[AttributeUsage(AttributeTargets.All)] +internal sealed class SRCategoryAttribute : CategoryAttribute +{ + private string resourceSet = String.Empty; + + public SRCategoryAttribute(string category) + : base(category) + { + } + + public SRCategoryAttribute(string category, string resourceSet) + : base(category) + { + this.resourceSet = resourceSet; + } + + protected override string GetLocalizedString(string value) + { + if (this.resourceSet.Length > 0) + { + ResourceManager rm = new ResourceManager(resourceSet, Assembly.GetExecutingAssembly()); + String localizedString = rm.GetString(value); + System.Diagnostics.Debug.Assert(localizedString != null, string.Format(CultureInfo.CurrentCulture, "String resource {0} not found.", new object[] { value })); + return localizedString; + } + else + { + return SR.GetString(value); + } + } +} + +[AttributeUsage(AttributeTargets.All)] +internal sealed class SRDisplayNameAttribute : DisplayNameAttribute +{ + public SRDisplayNameAttribute(string name) + { + DisplayNameValue = SR.GetString(name); + } + + public SRDisplayNameAttribute(string name, string resourceSet) + { + ResourceManager rm = new ResourceManager(resourceSet, Assembly.GetExecutingAssembly()); + DisplayNameValue = rm.GetString(name); + System.Diagnostics.Debug.Assert(DisplayNameValue != null, string.Format(CultureInfo.CurrentCulture, "String resource {0} not found.", new object[] { name })); + } +} + +/// +/// AutoGenerated resource class. Usage: +/// +/// string s = SR.GetString(SR.MyIdenfitier); +/// +internal sealed partial class SR +{ +#pragma warning disable CS8625 // 无法将 null 字面量转换为非 null 的引用类型。 + static SR loader = null; +#pragma warning restore CS8625 // 无法将 null 字面量转换为非 null 的引用类型。 + ResourceManager resources; + + internal SR() + { + resources = new System.Resources.ResourceManager("System.Workflow.ComponentModel.StringResources", Assembly.GetExecutingAssembly()); + } + + private static SR GetLoader() + { + if (loader == null) + loader = new SR(); + return loader; + } + + private static CultureInfo Culture + { + get { return null/*use ResourceManager default, CultureInfo.CurrentUICulture*/; } + } + + [SuppressMessage("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")] + internal static string GetString(string name, params object[] args) + { + return GetString(SR.Culture, name, args); + } + + [SuppressMessage("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")] + internal static string GetString(CultureInfo culture, string name, params object[] args) + { + SR sys = GetLoader(); + if (sys == null) + return null; + string res = sys.resources.GetString(name, culture); + System.Diagnostics.Debug.Assert(res != null, string.Format(CultureInfo.CurrentCulture, "String resource {0} not found.", new object[] { name })); + if (args != null && args.Length > 0) + { + return string.Format(CultureInfo.CurrentCulture, res, args); + } + else + { + return res; + } + } + + internal static string GetString(string name) + { + return GetString(SR.Culture, name); + } + + internal static string GetString(CultureInfo culture, string name) + { + SR sys = GetLoader(); + if (sys == null) + return null; + string res = sys.resources.GetString(name, culture); + System.Diagnostics.Debug.Assert(res != null, string.Format(CultureInfo.CurrentCulture, "String resource {0} not found.", new object[] { name })); + return res; + } + + // All these strings should be present in StringResources.resx + internal const string Activity = "Activity"; + internal const string Handlers = "Handlers"; + internal const string Conditions = "Conditions"; + internal const string ConditionedActivityConditions = "ConditionedActivityConditions"; + internal const string Correlations = "Correlations"; + internal const string CorrelationSet = "CorrelationSet"; + internal const string NameDescr = "NameDescr"; + internal const string EnabledDescr = "EnabledDescr"; + internal const string DescriptionDescr = "DescriptionDescr"; + internal const string UnlessConditionDescr = "UnlessConditionDescr"; + internal const string InitializeDescr = "InitializeDescr"; + internal const string CatchTypeDescr = "CatchTypeDescr"; + internal const string ExceptionTypeDescr = "ExceptionTypeDescr"; + internal const string FaultDescription = "FaultDescription"; + internal const string FaultTypeDescription = "FaultTypeDescription"; + internal const string ContainingAssemblyDescr = "ContainingAssemblyDescr"; + internal const string ExecutionModeDescr = "ExecutionModeDescr"; + internal const string Error_ReadOnlyTemplateActivity = "Error_ReadOnlyTemplateActivity"; + internal const string Error_TypeNotString = "Error_TypeNotString"; + internal const string Error_InvalidErrorType = "Error_InvalidErrorType"; + internal const string Error_LiteralConversionFailed = "Error_LiteralConversionFailed"; + internal const string Error_TypeNotPrimitive = "Error_TypeNotPrimitive"; + internal const string CompletedCaleeDescr = "CompletedCaleeDescr"; + internal const string ProxyClassDescr = "ProxyClassDescr"; + internal const string ActivitySetDescr = "ActivitySetDescr"; + internal const string VersionDescr = "VersionDescr"; + internal const string ActivationDescr = "ActivationDescr"; + internal const string CorrelationSetsDescr = "CorrelationSetsDescr"; + internal const string CompanionClassDescr = "CompanionClassDescr"; + internal const string TransactionTypeDescr = "TransactionTypeDescr"; + internal const string SynchronizedDescr = "SynchronizedDescr"; + internal const string IsolationLevelDescr = "IsolationLevelDescr"; + internal const string TimeoutDescr = "TimeoutDescr"; + internal const string BatchableDescr = "BatchableDescr"; + internal const string LRTTimeoutDescr = "LRTTimeoutDescr"; + internal const string OnGetCalleeCountDescr = "OnGetCalleeCountDescr"; + internal const string CompensatableActivityDescr = "CompensatableActivityDescr"; + internal const string OnAfterEventDescr = "OnAfterEventDescr"; + internal const string OnBeforeMethodInvokeDescr = "OnBeforeMethodInvokeDescr"; + internal const string AssignedToDescr = "AssignedToDescr"; + internal const string TypeDescr = "TypeDescr"; + internal const string TemplateActivityDescr = "TemplateActivityDescr"; + internal const string ErrorMessageDescr = "ErrorMessageDescr"; + internal const string WebServiceSynchronizedDescr = "WebServiceSynchronizedDescr"; + internal const string CorrelationSetDescr = "CorrelationSetDescr"; + internal const string ExecutionTypeDescr = "ExecutionTypeDescr"; + internal const string RoleDescr = "RoleDescr"; + internal const string OnInitializeClonesDescr = "OnInitializeClonesDescr"; + internal const string CorrelationSetDisplayName = "CorrelationSetDisplayName"; + internal const string PastingActivities = "PastingActivities"; + internal const string DeletingActivities = "DeletingActivities"; + internal const string DragDropActivities = "DragDropActivities"; + internal const string ChangingEnabled = "ChangingEnabled"; + internal const string ChangingHandler = "ChangingHandler"; + internal const string ChangingParameter = "ChangingParameter"; + internal const string CollectionItem = "CollectionItem"; + internal const string AddingConditionalBranch = "AddingConditionalBranch"; + internal const string AddingEventActivity = "AddingEventActivity"; + internal const string AddingListenBranch = "AddingListenBranch"; + internal const string AddingParallelBranch = "AddingParallelBranch"; + internal const string CurrentProject = "CurrentProject"; + internal const string ReferencedAssemblies = "ReferencedAssemblies"; + internal const string CollectionText = "CollectionText"; + internal const string ParameterDescription = "ParameterDescription"; + internal const string InvokeParameterDescription = "InvokeParameterDescription"; + internal const string ParametersDescription = "ParametersDescription"; + internal const string ChangingParameters = "ChangingParameters"; + internal const string Condition = "ConditionRule"; + internal const string MovingActivities = "MovingActivities"; + internal const string MemberNameDescr = "MemberNameDescr"; + internal const string OnScopeInitializedDescr = "OnScopeInitializedDescr"; + internal const string OnGeneratorInitializedDescr = "OnGeneratorInitializedDescr"; + internal const string OnScopeCompletedDescr = "OnScopeCompletedDescr"; + internal const string OnGeneratorCompletedDescr = "OnGeneratorCompletedDescr"; + internal const string DataElementRuntimeTypeDescr = "DataElementRuntimeTypeDescr"; + internal const string RuleConditionReferencesDescr = "RuleConditionReferencesDescr"; + internal const string CreateActivityFromToolbox = "CreateActivityFromToolbox"; + internal const string MoveMultipleActivities = "MoveMultipleActivities"; + internal const string MoveSingleActivity = "MoveSingleActivity"; + internal const string CutMultipleActivities = "CutMultipleActivities"; + internal const string CutSingleActivity = "CutSingleActivity"; + internal const string CutActivity = "CutActivity"; + internal const string FaultActivityDescription = "FaultActivityDescription"; + internal const string NullConditionExpression = "NullConditionExpression"; + internal const string ParameterTypeDescription = "ParameterTypeDescription"; + internal const string ParameterCategory = "ParameterCategory"; + internal const string ParameterDirectionDescription = "ParameterDirectionDescription"; + internal const string ParameterElementDescription = "ParameterElementDescription"; + internal const string ParameterDlgDescription = "ParameterDlgDescription"; + internal const string ParameterDlgHeader = "ParameterDlgHeader"; + internal const string SuspendActivityDescription = "SuspendActivityDescription"; + internal const string SuspendErrorMessageDescr = "SuspendErrorMessageDescr"; + internal const string TerminateActivityDescription = "TerminateActivityDescription"; + internal const string TerminateErrorMessageDescr = "TerminateErrorMessageDescr"; + internal const string DeclarationCategory = "DeclarationCategory"; + internal const string NoValidActivityPropertiesAvailable = "NoValidActivityPropertiesAvailable"; + internal const string ChooseActivityDatasource = "ChooseActivityDatasource"; + internal const string Promote = "Promote"; + internal const string Type = "Type"; + internal const string NoMatchingActivityProperties = "NoMatchingActivityProperties"; + internal const string ActivityBindIDDescription = "ActivityBindIDDescription"; + internal const string ActivityBindPathDescription = "ActivityBindPathDescription"; + internal const string XPathDescription = "XPathDescription"; + internal const string TransformerDescription = "TransformerDescription"; + internal const string CustomPropertiesCollectionFormHeader = "CustomPropertiesCollectionFormHeader"; + internal const string CustomPropertiesCollectionFormDescription = "CustomPropertiesCollectionFormDescription"; + internal const string BaseTypePropertyName = "BaseTypePropertyName"; + internal const string CustomActivityBaseClassTypeFilterProviderDesc = "CustomActivityBaseClassTypeFilterProviderDesc"; + internal const string CustomActivityDesignerTypeFilterProviderDesc = "CustomActivityDesignerTypeFilterProviderDesc"; + internal const string CustomActivityValidatorTypeFilterProviderDesc = "CustomActivityValidatorTypeFilterProviderDesc"; + internal const string CustomActivityExecutorTypeFilterProviderDesc = "CustomActivityExecutorTypeFilterProviderDesc"; + internal const string GenericParameters = "GenericParameters"; + internal const string ToolboxItem = "ToolboxItem"; + internal const string ToolboxItemCompanionClassDesc = "ToolboxItemCompanionClassDesc"; + internal const string Error_SerializationInsufficientState = "Error_SerializationInsufficientState"; + internal const string Error_ActivityHasParent = "Error_ActivityHasParent"; + internal const string Error_CompensantionParentNotScope = "Error_CompensantionParentNotScope"; + internal const string Error_ConditionedActivityParentNotCAG = "Error_ConditionedActivityParentNotCAG"; + internal const string Error_CorrelationTypeNotComparable = "Error_CorrelationTypeNotComparable"; + internal const string Error_ArgumentTypeNotMatchParameter = "Error_ArgumentTypeNotMatchParameter"; + internal const string Error_TypeTypeMismatch = "Error_TypeTypeMismatch"; + internal const string Error_ParameterTypeMismatch = "Error_ParameterTypeMismatch"; + internal const string Error_InvokeParameterTypeMismatch = "Error_InvokeParameterTypeMismatch"; + internal const string Error_ParameterPropertyNotSet = "Error_ParameterPropertyNotSet"; + internal const string Error_DataSourceNameNotSet = "Error_DataSourceNameNotSet"; + internal const string Error_DataSourceInvalidIdentifier = "Error_DataSourceInvalidIdentifier"; + internal const string Error_ParameterTypeNotExist = "Error_ParameterTypeNotExist"; + internal const string Error_InvalidParameterName = "Error_InvalidParameterName"; + internal const string Error_InvalidParameterType = "Error_InvalidParameterType"; + internal const string Error_InvalidParameterElement = "Error_InvalidParameterElement"; + internal const string Error_InvalidPropertyType = "Error_InvalidPropertyType"; + internal const string Error_TypeNotResolvedInMethodName = "Error_TypeNotResolvedInMethodName"; + internal const string Error_DelegateNoInvoke = "Error_DelegateNoInvoke"; + internal const string Error_TypeNotDelegate = "Error_TypeNotDelegate"; + internal const string Error_MethodSignatureMismatch = "Error_MethodSignatureMismatch"; + internal const string Error_MethodReturnTypeMismatch = "Error_MethodReturnTypeMismatch"; + internal const string Error_PropertyNotSet = "Error_PropertyNotSet"; + internal const string Error_ScopeCouldNotBeResolved = "Error_ScopeCouldNotBeResolved"; + internal const string Error_IfElseNotAllIfElseBranchDecl = "Error_ConditionalNotAllConditionalBranchDecl"; + internal const string Error_TypeTypeMismatchAmbiguity = "Error_TypeTypeMismatchAmbiguity"; + internal const string Error_InvalidCorrelationSetDatasource = "Error_InvalidCorrelationSetDatasource"; + internal const string Error_InvalidCorrelationSetType = "Error_InvalidCorrelationSetType"; + internal const string Error_MissingCorrelationParameterAttribute = "Error_MissingCorrelationParameterAttribute"; + internal const string Error_CorrelationTypeNotConsistent = "Error_CorrelationTypeNotConsistent"; + internal const string Error_CorrelationInvalid = "Error_CorrelationInvalid"; + internal const string Error_MissingDelegateMethod = "Error_MissingDelegateMethod"; + internal const string Error_MissingHostInterface = "Error_MissingHostInterface"; + internal const string Error_MissingMethodName = "Error_MissingMethodName"; + internal const string Error_NoBoundType = "Error_NoBoundType"; + internal const string Error_PortTypeNotAnInterface = "Error_PortTypeNotAnInterface"; + internal const string Error_MethodNotExists = "Error_MethodNotExists"; + internal const string Error_InvalidRequestResponseMethod = "Error_InvalidRequestResponseMethod"; + internal const string General_MissingService = "General_MissingService"; + internal const string Error_ScopeDuplicatedNameActivity = "Error_ScopeDuplicatedNameActivity"; + internal const string Error_DuplicatedActivityID = "Error_DuplicatedActivityID"; + internal const string Error_DuplicatedParameterName = "Error_DuplicatedParameterName"; + internal const string Error_ScopeMissingSerializableAttribute = "Error_ScopeMissingSerializableAttribute"; + internal const string Error_FieldNotExists = "Error_FieldNotExists"; + internal const string Error_PropertyNotExists = "Error_PropertyNotExists"; + internal const string Error_FieldTypeMismatch = "Error_FieldTypeMismatch"; + internal const string Error_PropertyTypeMismatch = "Error_PropertyTypeMismatch"; + internal const string Error_TypeNotResolvedInFieldName = "Error_TypeNotResolvedInFieldName"; + internal const string Error_TypeNotResolvedInPropertyName = "Error_TypeNotResolvedInPropertyName"; + internal const string Error_FieldGenericParamTypeMismatch = "Error_FieldGenericParamTypeMismatch"; + internal const string Error_TypeNotResolved = "Error_TypeNotResolved"; + internal const string Error_TypeIsUnboundedGeneric = "Error_TypeIsUnboundedGeneric"; + internal const string Error_MissingRootActivity = "Error_MissingRootActivity"; + internal const string Error_PropertyNotReadable = "Error_PropertyNotReadable"; + internal const string Error_PropertyNotWritable = "Error_PropertyNotWritable"; + internal const string Error_NotCompositeActivity = "Error_NotCompositeActivity"; + internal const string Error_TypeNotExist = "Error_TypeNotExist"; + internal const string Error_ActivityRefNotResolved = "Error_ActivityRefNotResolved"; + internal const string Error_ActivityRefNotMatchType = "Error_ActivityRefNotMatchType"; + internal const string Error_ActivityValidation = "Error_ActivityValidation"; + internal const string Error_ActiveChildExist = "Error_ActiveChildExist"; + internal const string Error_ActiveChildContextExist = "Error_ActiveChildContextExist"; + internal const string Error_CannotCompleteContext = "Error_CannotCompleteContext"; + internal const string Error_NoPasteSupport = "Error_NoPasteSupport"; + internal const string Error_UnknownSerializationStore = "Error_UnknownSerializationStore"; + internal const string Error_MissingCorrelationSet = "Error_MissingCorrelationSet"; + internal const string Error_CreateVariable = "Error_CreateVariable"; + internal const string Error_DuplicateCorrelationSetName = "Error_DuplicateCorrelationSetName"; + internal const string Error_DragDropInvalid = "Error_DragDropInvalid"; + internal const string AddingImplicitActivity = "AddingImplicitActivity"; + internal const string Failure_DoDefaultAction = "Failure_DoDefaultAction"; + internal const string Failure_DoDefaultActionCaption = "Failure_DoDefaultActionCaption"; + internal const string Error_FaultInsideAtomicScope = "Error_FaultInsideAtomicScope"; + internal const string Error_ListenNotMoreThanOneDelay = "Error_ListenNotMoreThanOneDelay"; + internal const string Error_AtomicScopeWithFaultHandlersActivityDecl = "Error_AtomicScopeWithFaultHandlersActivityDecl"; + internal const string Error_AtomicScopeWithCancellationHandlerActivity = "Error_AtomicScopeWithCancellationHandlerActivity"; + internal const string Error_ScopeDuplicateFaultHandlerActivityForAll = "Error_ScopeDuplicateFaultHandlerActivityForAll"; + internal const string Error_ScopeDuplicateFaultHandlerActivityFor = "Error_ScopeDuplicateFaultHandlerActivityFor"; + internal const string Error_AtomicScopeNestedInNonLRT = "Error_AtomicScopeNestedInNonLRT"; + internal const string Error_LRTScopeNestedInNonLRT = "Error_LRTScopeNestedInNonLRT"; + internal const string Error_CAGNotAllChildrenConditioned = "Error_CAGNotAllChildrenConditioned"; + internal const string Error_ConditionedActivityChildCount = "Error_ConditionedActivityChildCount"; + internal const string Error_NegativeValue = "Error_NegativeValue"; + internal const string Error_MethodWithReturnType = "Error_MethodWithReturnType"; + internal const string Error_SendReceiveOrderIncorrect = "Error_SendReceiveOrderIncorrect"; + internal const string Error_ReceiveSendOrderIncorrect = "Error_ReceiveSendOrderIncorrect"; + internal const string Error_CompensateBadNesting = "Error_CompensateBadNesting"; + internal const string Error_ReferencedAssemblyIsInvalid = "Error_ReferencedAssemblyIsInvalid"; + internal const string Error_TypeToXsdConversion = "Error_TypeToXsdConversion"; + internal const string Error_FieldTypeNotResolved = "Error_FieldTypeNotResolved"; + internal const string Error_PropertyTypeNotResolved = "Error_PropertyTypeNotResolved"; + internal const string Error_CouldNotDeserializeXomlFile = "Error_CouldNotDeserializeXomlFile"; + internal const string Error_InternalCompilerError = "Error_InternalCompilerError"; + internal const string Error_TypeNotAsseblyQualified = "Error_TypeNotAsseblyQualified"; + internal const string CompilerWarning_StandardAssemlbyInReferences = "CompilerWarning_StandardAssemlbyInReferences"; + internal const string Error_SuspendInAtomicScope = "Error_SuspendInAtomicScope"; + internal const string Error_InvalidActivityExecutionContext = "Error_InvalidActivityExecutionContext"; + internal const string Error_NoRuntimeAvailable = "Error_NoRuntimeAvailable"; + internal const string Error_CanNotChangeAtRuntime = "Error_CanNotChangeAtRuntime"; + internal const string Error_DataContextNotInitialized = "Error_DataContextNotInitialized"; + internal const string Error_DataContextAlreadyInitialized = "Error_DataContextAlreadyInitialized"; + internal const string Error_ParseActivityNameDoesNotExist = "Error_ParseActivityNameDoesNotExist"; + internal const string Error_NoParameterPropertyDeclared = "Error_NoParameterPropertyDeclared"; + internal const string Error_PropertyInvalidIdentifier = "Error_PropertyInvalidIdentifier"; + internal const string Error_WorkflowDefinitionModified = "Error_WorkflowDefinitionModified"; + internal const string Error_FieldAlreadyExist = "Error_FieldAlreadyExist"; + internal const string Failure_FieldAlreadyExist = "Failure_FieldAlreadyExist"; + internal const string Error_DifferentTypeFieldExists = "Error_DifferentTypeFieldExists"; + internal const string Error_RootActivityTypeInvalid = "Error_RootActivityTypeInvalid"; + internal const string Error_RootActivityTypeInvalid2 = "Error_RootActivityTypeInvalid2"; + internal const string Error_CannotCompile_No_XClass = "Error_CannotCompile_No_XClass"; + internal const string Error_TemplateActivityIsNotActivity = "Error_TemplateActivityIsNotActivity"; + internal const string Error_TypeIsNotRootActivity = "Error_TypeIsNotRootActivity"; + internal const string Error_NoTypeProvider = "Error_NoTypeProvider"; + internal const string Error_NotCodeGeneratorType = "Error_NotCodeGeneratorType"; + internal const string Error_NotDataContext = "Error_NotDataContext"; + internal const string Error_MissingDefaultConstructor = "Error_MissingDefaultConstructor"; + internal const string Error_ContextStackItemMissing = "Error_ContextStackItemMissing"; + internal const string Error_UnexpectedArgumentType = "Error_UnexpectedArgumentType"; + internal const string Error_EmptyArgument = "Error_EmptyArgument"; + internal const string Error_DPAlreadyExist = "Error_DPAlreadyExist"; + internal const string Error_DuplicateDynamicProperty = "Error_DuplicateDynamicProperty"; + internal const string Error_DynamicPropertyTypeValueMismatch = "Error_DynamicPropertyTypeValueMismatch"; + internal const string Error_DynamicPropertyNoSupport = "Error_DynamicPropertyNoSupport"; + internal const string Error_NoContextForDatasource = "Error_NoContextForDatasource"; + internal const string Error_NoContextForDatasourceCaption = "Error_NoContextForDatasourceCaption"; + internal const string Error_DataSourceHasParent = "Error_DataSourceHasParent"; + internal const string OnTaskCompletedDescr = "OnTaskCompletedDescr"; + internal const string OnTaskInitializedDescr = "OnTaskInitializedDescr"; + internal const string Error_InvalidXmlData = "Error_InvalidXmlData"; + internal const string Error_HandlerNotOnRoot = "Error_HandlerNotOnRoot"; + internal const string Error_InvalidArgumentIndex = "Error_InvalidArgumentIndex"; + internal const string Error_UITypeEditorTypeNotUITypeEditor = "Error_UITypeEditorTypeNotUITypeEditor"; + internal const string FilterDescription_UITypeEditor = "FilterDescription_UITypeEditor"; + internal const string Error_UserCodeFilesNotAllowed = "Error_UserCodeFilesNotAllowed"; + internal const string Error_CodeWithinNotAllowed = "Error_CodeWithinNotAllowed"; + internal const string Error_TypeNotAuthorized = "Error_TypeNotAuthorized"; + internal const string Error_CantDetermineBaseType = "Error_CantDetermineBaseType"; + internal const string Error_MultipleSelectNotSupportedForBindAndPromote = "Error_MultipleSelectNotSupportedForBindAndPromote"; + internal const string Error_CantDetermineBaseTypeCaption = "Error_CantDetermineBaseTypeCaption"; + internal const string Error_CantDeterminePropertyBaseType = "Error_CantDeterminePropertyBaseType"; + internal const string Error_NullCustomActivityTypeName = "Error_NullCustomActivityTypeName"; + internal const string Error_InvalidAttribute = "Error_InvalidAttribute"; + internal const string Error_InvalidAttributes = "Error_InvalidAttributes"; + internal const string Error_ConfigFileMissingOrInvalid = "Error_ConfigFileMissingOrInvalid"; + internal const string Error_CantHaveContextActivity = "Error_CantHaveContextActivity"; + internal const string Error_SynchronizedNeedsDataContext = "Error_SynchronizedNeedsDataContext"; + internal const string Error_MoreThanOneFaultHandlersActivityDecl = "Error_MoreThanOneFaultHandlersActivityDecl"; + internal const string Error_MoreThanOneEventHandlersDecl = "Error_MoreThanOneEventHandlersDecl"; + internal const string Error_MoreThanOneCancelHandler = "Error_MoreThanOneCancelHandler"; + internal const string Error_MetaDataInterfaceMissing = "Error_MetaDataInterfaceMissing"; + internal const string Error_NonActivityExecutor = "Error_NonActivityExecutor"; + internal const string Error_DynamicUpdateEvaluation = "Error_DynamicUpdateEvaluation"; + internal const string Error_CollectionHasNullEntry = "Error_CollectionHasNullEntry"; + internal const string Error_MissingContextProperty = "Error_MissingContextProperty"; + internal const string Error_AssociatedDesignerMissing = "Error_AssociatedDesignerMissing"; + internal const string Error_MissingContextActivityProperty = "Error_MissingContextActivityProperty"; + internal const string Error_MissingActivityProperty = "Error_MissingActivityProperty"; + internal const string Error_MissingOwnerTypeProperty = "Error_MissingOwnerTypeProperty"; + internal const string Error_DOIsNotAnActivity = "Error_DOIsNotAnActivity"; + internal const string Error_PropertyCanBeOnlyCleared = "Error_PropertyCanBeOnlyCleared"; + internal const string Error_PropertyDefaultTypeMismatch = "Error_PropertyDefaultTypeMismatch"; + internal const string Error_PropertyDefaultIsReference = "Error_PropertyDefaultIsReference"; + // workflow load errors + internal const string Error_WorkflowLoadFailed = "Error_WorkflowLoadFailed"; + internal const string Error_WorkflowLoadValidationFailed = "Error_WorkflowLoadValidationFailed"; + internal const string Error_WorkflowLoadDeserializationFailed = "Error_WorkflowLoadDeserializationFailed"; + internal const string Error_WorkflowLoadTypeMismatch = "Error_WorkflowLoadTypeMismatch"; + internal const string Error_WorkflowLoadInvalidXoml = "Error_WorkflowLoadInvalidXoml"; + internal const string Error_WorkflowLoadNotValidRootType = "Error_WorkflowLoadNotValidRootType"; + internal const string Error_CantCreateInstanceOfComponent = "Error_CantCreateInstanceOfComponent"; + internal const string Error_NotComponentFactoryType = "Error_NotComponentFactoryType"; + internal const string Error_WorkflowTerminated = "Error_WorkflowTerminated"; + + // serializer errrors + internal const string Error_SerializerAttributesFoundInComplexProperty = "Error_SerializerAttributesFoundInComplexProperty"; + internal const string Error_InvalidDataFound = "Error_InvalidDataFound"; + internal const string Error_InvalidDataFoundForType = "Error_InvalidDataFoundForType"; + internal const string Error_InvalidDataFoundForType1 = "Error_InvalidDataFoundForType1"; + internal const string Error_SerializerTypeNotResolved = "Error_SerializerTypeNotResolved"; + internal const string Error_MarkupSerializerTypeNotResolved = "Error_MarkupSerializerTypeNotResolved"; + internal const string Error_SerializerTypeNotResolvedWithInnerError = "Error_SerializerTypeNotResolvedWithInnerError"; + internal const string Error_SerializerNotAvailable = "Error_SerializerNotAvailable"; + internal const string Error_SerializerNotAvailableForSerialize = "Error_SerializerNotAvailableForSerialize"; + internal const string Error_SerializerCreateInstanceFailed = "Error_SerializerCreateInstanceFailed"; + internal const string Error_SerializerAddChildFailed = "Error_SerializerAddChildFailed"; + internal const string Error_SerializerNoPropertyAvailable = "Error_SerializerNoPropertyAvailable"; + internal const string Error_SerializerPrimitivePropertyReadOnly = "Error_SerializerPrimitivePropertyReadOnly"; + internal const string Error_SerializerCantChangeIsLocked = "Error_SerializerCantChangeIsLocked"; + internal const string Error_SerializerPrimitivePropertySetFailed = "Error_SerializerPrimitivePropertySetFailed"; + internal const string Error_SerializerPropertyGetFailed = "Error_SerializerPropertyGetFailed"; + internal const string Error_SerializerPrimitivePropertyNoLogic = "Error_SerializerPrimitivePropertyNoLogic"; + internal const string Error_SerializerPrimitivePropertyParentIsNull = "Error_SerializerPrimitivePropertyParentIsNull"; + internal const string Error_SerializerComplexPropertySetFailed = "Error_SerializerComplexPropertySetFailed"; + internal const string Error_SerializerNoChildNotion = "Error_SerializerNoChildNotion"; + internal const string Error_SerializerNoDynamicPropertySupport = "Error_SerializerNoDynamicPropertySupport"; + internal const string Error_SerializerNoSerializeLogic = "Error_SerializerNoSerializeLogic"; + internal const string Error_SerializerReadOnlyPropertyAndValueIsNull = "Error_SerializerReadOnlyPropertyAndValueIsNull"; + internal const string Error_SerializerReadOnlyParametersNoChild = "Error_SerializerReadOnlyParametersNoChild"; + internal const string Error_SerializerNotParameterBindingObject = "Error_SerializerNotParameterBindingObject"; + internal const string Error_SerializerThrewException = "Error_SerializerThrewException"; + internal const string Error_ActivityCollectionSerializer = "Error_ActivityCollectionSerializer"; + internal const string Error_MissingClassAttribute = "Error_MissingClassAttribute"; + internal const string Error_MissingClassAttributeValue = "Error_MissingClassAttributeValue"; + internal const string ExecutorCreationFailedErrorMessage = "ExecutorCreationFailedErrorMessage"; + internal const string VariableGetterCode_VB = "VariableGetterCode_VB"; + internal const string VariableGetterCode_CS = "VariableGetterCode_CS"; + internal const string VariableSetterCode_VB = "VariableSetterCode_VB"; + internal const string VariableSetterCode_CS = "VariableSetterCode_CS"; + internal const string StaticVariableGetterCode_VB = "StaticVariableGetterCode_VB"; + internal const string StaticVariableGetterCode_CS = "StaticVariableGetterCode_CS"; + internal const string StaticVariableSetterCode_VB = "StaticVariableSetterCode_VB"; + internal const string StaticVariableSetterCode_CS = "StaticVariableSetterCode_CS"; + internal const string EnterCodeBesidesCode_VB = "EnterCodeBesidesCode_VB"; + internal const string EnterCodeBesidesCode_CS = "EnterCodeBesidesCode_CS"; + internal const string LeaveCodeBesides1Code_VB = "LeaveCodeBesides1Code_VB"; + internal const string LeaveCodeBesides2Code_VB = "LeaveCodeBesides2Code_VB"; + internal const string LeaveCodeBesides1Code_CS = "LeaveCodeBesides1Code_CS"; + internal const string LeaveCodeBesides2Code_CS = "LeaveCodeBesides2Code_CS"; + internal const string VariableSetterName = "VariableSetterName"; + internal const string VariableGetterName = "VariableGetterName"; + internal const string HandlerGetterName = "HandlerGetterName"; + internal const string WorkflowCreatorName = "WorkflowCreatorName"; + internal const string ActivityMethod = "ActivityMethod"; + internal const string CustomActivityPrivateField = "CustomActivityPrivateField"; + internal const string InitializedVariableDeclaration_VB = "InitializedVariableDeclaration_VB"; + internal const string InitializedVariableDeclaration_CS = "InitializedVariableDeclaration_CS"; + internal const string In = "In"; + internal const string Out = "Out"; + internal const string Ref = "Ref"; + internal const string Required = "Required"; + internal const string Optional = "Optional"; + internal const string Parameters = "Parameters"; + internal const string Properties = "Properties"; + internal const string Error_RecursionDetected = "Error_RecursionDetected"; + internal const string Warning_UnverifiedRecursion = "Warning_UnverifiedRecursion"; + internal const string AddConstructorCode = "AddConstructorCode"; + internal const string Error_UninitializedCorrelation = "Error_UninitializedCorrelation"; + internal const string Error_CorrelationAlreadyInitialized = "Error_CorrelationAlreadyInitialized"; + internal const string Error_CorrelatedSendReceiveAtomicScope = "Error_CorrelatedSendReceiveAtomicScope"; + internal const string Warning_ActivityValidation = "Warning_ActivityValidation"; + internal const string Warning_EmptyBehaviourActivity = "Warning_EmptyBehaviourActivity"; + internal const string Error_ParallelActivationNoCorrelation = "Error_ParallelActivationNoCorrelation"; + internal const string Error_MethodNotAccessible = "Error_MethodNotAccessible"; + internal const string Error_FieldNotAccessible = "Error_FieldNotAccessible"; + internal const string Error_PropertyNotAccessible = "Error_PropertyNotAccessible"; + internal const string Error_GenericArgumentsNotAllowed = "Error_GenericArgumentsNotAllowed"; + internal const string Error_InvalidIdentifier = "Error_InvalidIdentifier"; + internal const string Error_InvalidLanguageIdentifier = "Error_InvalidLanguageIdentifier"; + internal const string DuplicateActivityIdentifier = "DuplicateActivityIdentifier"; + internal const string Error_MissingAttribute = "Error_MissingAttribute"; + internal const string Error_LoadUIPropertiesFile = "Error_LoadUIPropertiesFile"; + internal const string Error_SerializerEventGetFailed = "Error_SerializerEventGetFailed"; + internal const string Error_SerializerEventFailed = "Error_SerializerEventFailed"; + internal const string Error_SerializerNoMemberFound = "Error_SerializerNoMemberFound"; + internal const string Error_DynamicEventConflict = "Error_DynamicEventConflict"; + internal const string Error_SerializerMemberSetFailed = "Error_SerializerMemberSetFailed"; + internal const string Error_ContentPropertyCouldNotBeFound = "Error_ContentPropertyCouldNotBeFound"; + internal const string Error_ContentPropertyValueInvalid = "Error_ContentPropertyValueInvalid"; + internal const string Error_ContentPropertyNoSetter = "Error_ContentPropertyNoSetter"; + internal const string Error_ContentCanNotBeConverted = "Error_ContentCanNotBeConverted"; + internal const string Error_ContentPropertyCanNotBeNull = "Error_ContentPropertyCanNotBeNull"; + internal const string Error_SerializerTypeMismatch = "Error_SerializerTypeMismatch"; + internal const string Error_CouldNotAddValueInContentProperty = "Error_CouldNotAddValueInContentProperty"; + internal const string Error_SerializerTypeRequirement = "Error_SerializerTypeRequirement"; + internal const string Error_CanNotAddActivityInBlackBoxActivity = "Error_CanNotAddActivityInBlackBoxActivity"; + internal const string Error_ContentPropertyCanNotSupportCompactFormat = "Error_ContentPropertyCanNotSupportCompactFormat"; + internal const string Error_ContentPropertyNoMultipleContents = "Error_ContentPropertyNoMultipleContents"; + internal const string Error_InternalSerializerError = "Error_InternalSerializerError"; + internal const string Error_DictionarySerializerNonDictionaryObject = "Error_DictionarySerializerNonDictionaryObject"; + internal const string Error_DictionarySerializerKeyNotFound = "Error_DictionarySerializerKeyNotFound"; + internal const string Error_InvalidCancelActivityState = "Error_InvalidCancelActivityState"; + internal const string Error_InvalidCompensateActivityState = "Error_InvalidCompensateActivityState"; + internal const string Error_InvalidCloseActivityState = "Error_InvalidCloseActivityState"; + internal const string Error_SealedPropertyMetadata = "Error_SealedPropertyMetadata"; + internal const string Error_MemberNotFound = "Error_MemberNotFound"; + internal const string Error_EmptyPathValue = "Error_EmptyPathValue"; + internal const string Error_InvalidCompensatingState = "Error_InvalidCompensatingState"; + internal const string Error_InvalidCancelingState = "Error_InvalidCancelingState"; + internal const string Error_InvalidClosingState = "Error_InvalidClosingState"; + internal const string Error_InvalidStateToExecuteChild = "Error_InvalidStateToExecuteChild"; + internal const string Error_InvalidExecutionState = "Error_InvalidExecutionState"; + internal const string Error_InvalidInitializingState = "Error_InvalidInitializingState"; + internal const string Error_InvalidInvokingState = "Error_InvalidInvokingState"; + internal const string Error_NotRegisteredAs = "Error_NotRegisteredAs"; + internal const string Error_AlreadyRegisteredAs = "Error_AlreadyRegisteredAs"; + internal const string Error_InsertingChildControls = "Error_InsertingChildControls"; + internal const string Error_EmptyToolTipRectangle = "Error_EmptyToolTipRectangle"; + internal const string Error_EmptyRectangleValue = "Error_EmptyRectangleValue"; + internal const string Error_InvalidShadowRectangle = "Error_InvalidShadowRectangle"; + internal const string Error_InvalidShadowDepth = "Error_InvalidShadowDepth"; + internal const string Error_InvalidLightSource = "Error_InvalidLightSource"; + internal const string Error_ChangingDock = "Error_ChangingDock"; + internal const string Error_NullOrEmptyValue = "Error_NullOrEmptyValue"; + internal const string Error_InvalidStateImages = "Error_InvalidStateImages"; + internal const string Error_InvalidConnectorSegment = "Error_InvalidConnectorSegment"; + internal const string Error_InvalidConnectorSource = "Error_InvalidConnectorSource"; + internal const string Error_CreatingToolTip = "Error_CreatingToolTip"; + internal const string Error_InvalidDockStyle = "Error_InvalidDockStyle"; + internal const string Error_InvalidConnectorValue = "Error_InvalidConnectorValue"; + internal const string Error_InvalidDesignerVerbValue = "Error_InvalidDesignerVerbValue"; + internal const string Error_InvalidRuntimeType = "Error_InvalidRuntimeType"; + internal const string Error_InvalidArgumentValue = "Error_InvalidArgumentValue"; + internal const string Error_InvalidRadiusValue = "Error_InvalidRadiusValue"; + internal const string ToolTipString = "ToolTipString"; + + //Collection Editor Resources + internal const string CollectionEditorCaption = "CollectionEditorCaption"; + internal const string CollectionEditorProperties = "CollectionEditorProperties"; + internal const string CollectionEditorPropertiesMultiSelect = "CollectionEditorPropertiesMultiSelect"; + internal const string CollectionEditorPropertiesNone = "CollectionEditorPropertiesNone"; + internal const string CollectionEditorCantRemoveItem = "CollectionEditorCantRemoveItem"; + internal const string CollectionEditorUndoBatchDesc = "CollectionEditorUndoBatchDesc"; + internal const string CollectionEditorInheritedReadOnlySelection = "CollectionEditorInheritedReadOnlySelection"; + internal const string Error_ParameterAlreadyExists = "Error_ParameterAlreadyExists"; + internal const string Error_PropertyAlreadyExists = "Error_PropertyAlreadyExists"; + internal const string Error_HiddenPropertyAlreadyExists = "Error_HiddenPropertyAlreadyExists"; + internal const string Error_CorrelationInUse = "Error_CorrelationInUse"; + internal const string Error_ItemNotExists = "Error_ItemNotExists"; + internal const string Error_NoHelpAvailable = "Error_NoHelpAvailable"; + internal const string Error_DuplicateWorkflow = "Error_DuplicateWorkflow"; + internal const string Error_Recursion = "Error_Recursion"; + internal const string Error_RootActivity = "Error_RootActivity"; + internal const string Error_ConditionDefinitionDeserializationFailed = "Error_ConditionDefinitionDeserializationFailed"; + internal const string Error_InvalidConditionDefinition = "Error_InvalidConditionDefinition"; + internal const string SR_InvokeTransactionalFromAtomic = "SR_InvokeTransactionalFromAtomic"; + internal const string Error_SuspendInAtomicCallChain = "Error_SuspendInAtomicCallChain"; + internal const string Error_LiteralPassedToOutRef = "Error_LiteralPassedToOutRef"; + internal const string Error_GeneratorShouldContainSingleActivity = "Error_GeneratorShouldContainSingleActivity"; + internal const string Error_DeclaringPropertyNotSupported = "Error_DeclaringPropertyNotSupported"; + internal const string Error_DeclaringEventNotSupported = "Error_DeclaringEventNotSupported"; + internal const string Error_DynamicEventNotSupported = "Error_DynamicEventNotSupported"; + internal const string Error_DynamicPropertyNotSupported = "Error_DynamicPropertyNotSupported"; + internal const string Error_ParameterTypeResolution = "Error_ParameterTypeResolution"; + + // Dynamic Validations + internal const string Error_DynamicActivity = "Error_DynamicActivity"; + internal const string Error_DynamicActivity2 = "Error_DynamicActivity2"; + internal const string Error_CompilerValidationFailed = "Error_CompilerValidationFailed"; + internal const string Error_RuntimeValidationFailed = "Error_RuntimeValidationFailed"; + internal const string Error_TransactionAlreadyCanceled = "Error_TransactionAlreadyCanceled"; + internal const string Error_RemoveExecutingActivity = "Error_RemoveExecutingActivity"; + internal const string Error_InsideAtomicScope = "Error_InsideAtomicScope"; + internal const string SuspendReason_WorkflowChange = "SuspendReason_WorkflowChange"; + + //type filtering + internal const string FilterDescription_ParameterDeclaration = "FilterDescription_ParameterDeclaration"; + internal const string FilterDescription_GenericArgument = "FilterDescription_GenericArgument"; + + + internal const string LibraryPathIsInvalid = "LibraryPathIsInvalid"; + + // Activity Set + internal const string Error_CreateValidator = "Error_CreateValidator"; + internal const string Error_InvalidPackageFile = "Error_InvalidPackageFile"; + internal const string Error_AddAssemblyRef = "Error_AddAssemblyRef"; + internal const string Error_AssemblyBadImage = "Error_AssemblyBadImage"; + internal const string BindPropertySetterName = "BindPropertySetterName"; + + // Bind validations + internal const string Error_CannotResolveActivity = "Error_CannotResolveActivity"; + internal const string Error_CannotResolveRelativeActivity = "Error_CannotResolveRelativeActivity"; + internal const string Error_PathNotSetForActivitySource = "Error_PathNotSetForActivitySource"; + internal const string Error_InvalidMemberPath = "Error_InvalidMemberPath"; + internal const string Error_TargetTypeMismatch = "Error_TargetTypeMismatch"; + internal const string Warning_ParameterBinding = "Warning_ParameterBinding"; + internal const string Error_ReferencedActivityPropertyNotBind = "Error_ReferencedActivityPropertyNotBind"; + internal const string Error_TargetTypeDataSourcePathMismatch = "Error_TargetTypeDataSourcePathMismatch"; + internal const string Bind_ActivityDataSourceRecursionDetected = "Bind_ActivityDataSourceRecursionDetected"; + internal const string Bind_DuplicateDataSourceNames = "Bind_DuplicateDataSourceNames"; + internal const string Error_PathNotSetForXmlDataSource = "Error_PathNotSetForXmlDataSource"; + internal const string Error_XmlDocumentLoadFailed = "Error_XmlDocumentLoadFailed"; + internal const string Error_XmlDataSourceInvalidPath = "Error_XmlDataSourceInvalidPath"; + internal const string Error_XmlDataSourceMultipleNodes = "Error_XmlDataSourceMultipleNodes"; + internal const string Error_XmlDataSourceInvalidXPath = "Error_XmlDataSourceInvalidXPath"; + internal const string Error_InvalidObjectRefFormat = "Error_InvalidObjectRefFormat"; + internal const string Error_ReadOnlyDataSource = "Error_ReadOnlyDataSource"; + internal const string Error_HandlerReadOnly = "Error_HandlerReadOnly"; + internal const string Error_XmlDataSourceReadOnly = "Error_XmlDataSourceReadOnly"; + internal const string Error_DataSourceNotExist = "Error_DataSourceNotExist"; + internal const string Error_PropertyNoGetter = "Error_PropertyNoGetter"; + internal const string Error_PropertyNoSetter = "Error_PropertyNoSetter"; + internal const string Error_PropertyHasNoGetterDefined = "Error_PropertyHasNoGetterDefined"; + internal const string Error_PropertyHasNoSetterDefined = "Error_PropertyHasNoSetterDefined"; + internal const string Error_PropertyReferenceNoGetter = "Error_PropertyReferenceNoGetter"; + internal const string Error_PropertyReferenceGetterNoAccess = "Error_PropertyReferenceGetterNoAccess"; + internal const string Error_PropertyHasIndexParameters = "Error_PropertyHasIndexParameters"; + internal const string Error_ReadOnlyField = "Error_ReadOnlyField"; + internal const string Error_NoEnclosingContext = "Error_NoEnclosingContext"; + internal const string Error_NestedPersistOnClose = "Error_NestedPersistOnClose"; + internal const string Error_NestedCompensatableActivity = "Error_NestedCompensatableActivity"; + internal const string Error_InvalidActivityForObjectDatasource = "Error_InvalidActivityForObjectDatasource"; + internal const string Error_DataSourceTypeConversionFailed = "Error_DataSourceTypeConversionFailed"; + internal const string Error_BindDialogWrongPropertyType = "Error_BindDialogWrongPropertyType"; + internal const string Error_BindDialogNoValidPropertySelected = "Error_BindDialogNoValidPropertySelected"; + internal const string Error_BindDialogBindNotValid = "Error_BindDialogBindNotValid"; + internal const string Error_BindDialogCanNotBindToItself = "Error_BindDialogCanNotBindToItself"; + internal const string Error_BindActivityReference = "Error_BindActivityReference"; + internal const string Error_NoTargetTypeForMethod = "Error_NoTargetTypeForMethod"; + internal const string Error_MethodDataSourceIsReadOnly = "Error_MethodDataSourceIsReadOnly"; + internal const string Error_NotMethodDataSource = "Error_NotMethodDataSource"; + internal const string Error_MethodDataSourceWithPath = "Error_MethodDataSourceWithPath"; + internal const string Error_PathSyntax = "Error_PathSyntax"; + internal const string Error_UnmatchedParen = "Error_UnmatchedParen"; + internal const string Error_UnmatchedBracket = "Error_UnmatchedBracket"; + internal const string Error_MemberWithSameNameExists = "Error_MemberWithSameNameExists"; + internal const string Error_ActivityIdentifierCanNotBeEmpty = "Error_ActivityIdentifierCanNotBeEmpty"; + internal const string Error_InvalidActivityIdentifier = "Error_InvalidActivityIdentifier"; + internal const string Error_ActivityBindTypeConversionError = "Error_ActivityBindTypeConversionError"; + internal const string EmptyValue = "EmptyValue"; + internal const string Error_PropertyTypeNotDefined = "Error_PropertyTypeNotDefined"; + + internal const string Error_CompilationFailed = "Error_CompilationFailed"; + internal const string Error_MissingCompilationContext = "Error_MissingCompilationContext"; + + internal const string InvokeWorkflowReference_VB = "InvokeWorkflowReference_VB"; + internal const string InvokeWorkflowReference_CS = "InvokeWorkflowReference_CS"; + internal const string Error_InvalidListItem = "Error_InvalidListItem"; + + internal const string ParserMapPINoWhitespace = "ParserMapPINoWhitespace"; + internal const string ParserMapPIBadCharEqual = "ParserMapPIBadCharEqual"; + internal const string ParserMapPIBadCharQuote = "ParserMapPIBadCharQuote"; + internal const string ParserMapPIBadKey = "ParserMapPIBadKey"; + internal const string ParserMapPIMissingKey = "ParserMapPIMissingKey"; + internal const string ParserMapPIKeyNotSet = "ParserMapPIKeyNotSet"; + internal const string ParserMismatchDelimiter = "ParserMismatchDelimiter"; + internal const string ParserDanglingClause = "ParserDanglingClause"; + internal const string UnknownDefinitionTag = "UnknownDefinitionTag"; + internal const string CDATASection = "CDATASection"; + internal const string TextSection = "TextSection"; + internal const string IncorrectSyntax = "IncorrectSyntax"; + internal const string IncorrectTypeSyntax = "IncorrectTypeSyntax"; + internal const string Error_MultipleRootActivityCreator = "Error_MultipleRootActivityCreator"; + internal const string Error_MustHaveParent = "Error_MustHaveParent"; + + // Workflow References + internal const string Error_ReferenceObjNotInitialized = "Error_ReferenceObjNotInitialized"; + internal const string Error_ReferenceInitResourceManager = "Error_ReferenceInitResourceManager"; + internal const string Error_ResourceReferenceGetObject = "Error_ResourceReferenceGetObject"; + internal const string Error_RefBindCantFindRef = "Error_RefBindCantFindRef"; + internal const string Error_RefBindMissingReferenceName = "Error_RefBindMissingReferenceName"; + internal const string Error_RefBindMissingAttribute = "Error_RefBindMissingAttribute"; + internal const string Error_ReferenceLoad = "Error_ReferenceLoad"; + internal const string Error_ReferenceMissingAttribute = "Error_ReferenceMissingAttribute"; + internal const string Error_ReferenceInvalidResourceFile = "Error_ReferenceInvalidResourceFile"; + internal const string Error_ReferenceEmptyName = "Error_ReferenceEmptyName"; + + internal const string HandlerInvokerName = "HandlerInvokerName"; + internal const string HandlerInvokerSwitchPrefix_CS = "HandlerInvokerSwitchPrefix_CS"; + internal const string HandlerInvokerSwitchPrefix_VB = "HandlerInvokerSwitchPrefix_VB"; + internal const string HandlerInvokerSwitchSuffix_CS = "HandlerInvokerSwitchSuffix_CS"; + internal const string HandlerInvokerSwitchSuffix_VB = "HandlerInvokerSwitchSuffix_VB"; + internal const string HandlerInvokerCaseBegin_CS = "HandlerInvokerCaseBegin_CS"; + internal const string HandlerInvokerCaseBegin_VB = "HandlerInvokerCaseBegin_VB"; + + // Activity Category + internal const string Standard = "Standard"; + internal const string Base = "Base"; + + //CustomActivityDesigner + internal const string ValidatorCompanionClassDesc = "ValidatorCompanionClassDesc"; + internal const string ExecutorCompanionClassDesc = "ExecutorCompanionClassDesc"; + internal const string DesignerCompanionClassDesc = "DesignerCompanionClassDesc"; + internal const string CustomActivityBaseTypeDesc = "CustomActivityBaseTypeDesc"; + internal const string ActivityProperties = "ActivityProperties"; + internal const string ActivityPropertiesDesc = "ActivityPropertiesDesc"; + internal const string CompanionClasses = "CompanionClasses"; + internal const string ActivityDesc = "Activity"; + internal const string Error_TypeConversionFailed = "Error_TypeConversionFailed"; + internal const string SupportDataContext = "SupportDataContext"; + internal const string AdvancedCategory = "AdvancedCategory"; + internal const string SupportDataContextDesc = "SupportDataContextDesc"; + internal const string BaseCompanionClassName = "BaseCompanionClassName"; + internal const string BaseCompanionClassDesc = "BaseCompanionClassDesc"; + internal const string Designer = "Designer"; + internal const string Validator = "Validator"; + internal const string Executor = "Executor"; + internal const string BaseActivityType = "BaseActivityType"; + internal const string Error_NotBuiltInActivity = "Error_NotBuiltInActivity"; + internal const string NoChildActivities_Message = "NoChildActivities_Message"; + internal const string NoChildActivities_Caption = "NoChildActivities_Caption"; + internal const string Error_CustomActivityCantCreate = "Error_CustomActivityCantCreate"; + internal const string Error_CantChangeBuiltInActivity = "Error_CantChangeBuiltInActivity"; + internal const string Error_CantAddBeforeBuiltInActivity = "Error_CantAddBeforeBuiltInActivity"; + internal const string Error_CantAddAfterNonBuiltInActivity = "Error_CantAddAfterNonBuiltInActivity"; + internal const string Error_CannotAddRemoveChildActivities = "Error_CannotAddRemoveChildActivities"; + internal const string Error_CantFindBuiltInActivity = "Error_CantFindBuiltInActivity"; + internal const string Error_MissingBaseCompanionClassAttribute = "Error_MissingBaseCompanionClassAttribute"; + internal const string Error_CantFindBuiltInParent = "Error_CantFindBuiltInParent"; + internal const string Error_CantCreateInstanceOfBaseType = "Error_CantCreateInstanceOfBaseType"; + internal const string Error_CustomActivityTypeCouldNotBeFound = "Error_CustomActivityTypeCouldNotBeFound"; + internal const string None = "None"; + internal const string AtomicTransaction = "AtomicTransaction"; + internal const string LocalDataContext = "LocalDataContext"; + internal const string LocalDataContextDesc = "LocalDataContextDesc"; + internal const string CompanionClass = "CompanionClass"; + internal const string Error_AlreadyRootActivity = "Error_AlreadyRootActivity"; + internal const string RootActivityName = "RootActivityName"; + internal const string RootActivityNameDesc = "RootActivityNameDesc"; + internal const string CustomProperties = "CustomProperties"; + internal const string VisibleDescr = "VisibleDescr"; + internal const string EditableDescr = "EditableDescr"; + internal const string Error_CantCreateMethod = "Error_CantCreateMethod"; + internal const string Error_CantEditNullValue = "Error_CantEditNullValue"; + internal const string Error_CompanionTypeNotSet = "Error_CompanionTypeNotSet"; + internal const string Error_CompanionClassNameCanNotBeEmpty = "Error_CompanionClassNameCanNotBeEmpty"; + internal const string Error_CouldNotEmitFieldInLocalDataContext = "Error_CouldNotEmitFieldInLocalDataContext"; + internal const string Error_CouldNotEmitMethodInLocalDataContext = "Error_CouldNotEmitMethodInLocalDataContext"; + internal const string Error_DerivationFromTypeWithLocalDataContext = "Error_DerivationFromTypeWithLocalDataContext"; + internal const string Error_CompanionTypeDerivationError = "Error_CompanionTypeDerivationError"; + internal const string Error_CantCreateDataContextClass = "Error_CantCreateDataContextClass"; + internal const string ArrayExistingBind = "ArrayExistingBind"; + internal const string Error_NoMatchingFieldsOrProperties = "Error_NoMatchingFieldsOrProperties"; + internal const string ChooseFieldPropertyDatasource = "ChooseFieldPropertyDatasource"; + + internal const string SupportsTransaction = "SupportsTransaction"; + internal const string SupportsExceptions = "SupportsExceptions"; + internal const string SupportsCancellationHandlerActivity = "SupportsCancellationHandlerActivity"; + internal const string SupportsEvents = "SupportsEvents"; + internal const string SupportsDataSources = "SupportsDataSources"; + internal const string SupportsCompensationHandler = "SupportsCompensationHandler"; + internal const string SupportsCompensationHandlerDesc = "SupportsCompensationHandlerDesc"; + internal const string SupportsTransactionDesc = "SupportsTransactionDesc"; + internal const string SupportsExceptionsDesc = "SupportsExceptionsDesc"; + internal const string SupportsCancelHandlerDesc = "SupportsCancelHandlerDesc"; + internal const string SupportsEventsDesc = "SupportsEventsDesc"; + internal const string TransactionDesc = "TransactionDesc"; + + internal const string Error_BaseTypeMustBeActivity = "Error_BaseTypeMustBeActivity"; + internal const string ExistingActivityBindTitle = "ExistingActivityBindTitle"; + internal const string ExistingActivityBindLabel = "ExistingActivityBindLabel"; + internal const string ExistingFieldPropBindTitle = "ExistingFieldPropBindTitle"; + internal const string ExistingFieldPropBindLabel = "ExistingFieldPropBindLabel"; + internal const string ProvidesSynchronization = "ProvidesSynchronization"; + internal const string ProvidesSynchronizationDesc = "ProvidesSynchronizationDesc"; + internal const string SynchronizationHandles = "SynchronizationHandles"; + internal const string SynchronizationHandlesDesc = "SynchronizationHandlesDesc"; + + internal const string Error_TransactionAlreadyApplied = "Error_TransactionAlreadyApplied"; + internal const string Error_BindBaseTypeNotSpecified = "Error_BindBaseTypeNotSpecified"; + internal const string NonDelegateTargetType = "NonDelegateTargetType"; + internal const string Error_ClassnameNotInRootNamespace = "Error_ClassnameNotInRootNamespace"; + internal const string Error_CantUseCurrentProjectTypeAsBase = "Error_CantUseCurrentProjectTypeAsBase"; + internal const string Error_UnboundGenericType = "Error_UnboundGenericType"; + internal const string Error_UnboundGenericTypeDataSource = "Error_UnboundGenericTypeDataSource"; + internal const string Error_BaseTypeUnknown = "Error_BaseTypeUnknown"; + internal const string Error_UnconfiguredBind = "Error_UnconfiguredBind"; + internal const string Error_CanNotEmitMemberInLocalDataContext = "Error_CanNotEmitMemberInLocalDataContext"; + internal const string Error_DesignedTypeNotFound = "Error_DesignedTypeNotFound"; + internal const string Error_PathCouldNotBeResolvedToMember = "Error_PathCouldNotBeResolvedToMember"; + internal const string Error_EdittingNullCollection = "Error_EdittingNullCollection"; + internal const string Error_MoreThanOneCompensationDecl = "Error_MoreThanOneCompensationDecl"; + internal const string Error_ParentDoesNotSupportCompensation = "Error_ParentDoesNotSupportCompensation"; + internal const string Error_CantResolveEventHandler = "Error_CantResolveEventHandler"; + internal const string Error_XSDObjectTypeNotSerializable = "Error_XSDObjectTypeNotSerializable"; + internal const string AEC_InvalidActivity = "AEC_InvalidActivity"; + internal const string GetDynamicActivities_InvalidActivity = "GetDynamicActivities_InvalidActivity"; + internal const string AEC_InvalidNestedActivity = "AEC_InvalidNestedActivity"; + internal const string Error_IDNotSetForActivitySource = "Error_IDNotSetForActivitySource"; + internal const string Error_InvalidCustomPropertyName = "Error_InvalidCustomPropertyName"; + internal const string Error_InvalidCustomPropertyType = "Error_InvalidCustomPropertyType"; + + internal const string Error_DPReadOnly = "Error_DPReadOnly"; + internal const string Error_DPMetaPropertyBinding = "Error_DPMetaPropertyBinding"; + internal const string Error_DPSetValueBind = "Error_DPSetValueBind"; + internal const string Error_DPSetValueHandler = "Error_DPSetValueHandler"; + internal const string Error_DPGetValueHandler = "Error_DPGetValueHandler"; + internal const string Error_DPAddHandlerNonEvent = "Error_DPAddHandlerNonEvent"; + internal const string Error_DPAddHandlerMetaProperty = "Error_DPAddHandlerMetaProperty"; + internal const string Error_DPRemoveHandlerBind = "Error_DPRemoveHandlerBind"; + internal const string Error_LanguageNeedsToBeVBCSharp = "Error_LanguageNeedsToBeVBCSharp"; + internal const string Error_TargetFxNotSupported = "Error_TargetFxNotSupported"; + internal const string Error_CantConvertValueValue = "Error_CantConvertValueValue"; + internal const string Error_TypeIsNotValid = "Error_TypeIsNotValid"; + internal const string Error_TypePropertyInvalid = "Error_TypePropertyInvalid"; + internal const string Error_EventCantBeMetaProperty = "Error_EventCantBeMetaProperty"; + internal const string Error_EventMustBeDelegate = "Error_EventMustBeDelegate"; + internal const string Error_DPPropertyTypeMissing = "Error_DPPropertyTypeMissing"; + + internal const string TransactionalContextActivityDescription = "TransactionalContextActivityDescription"; + internal const string CompensatableTransactionalContextActivityDescription = "CompensatableTransactionalContextActivityDescription"; + internal const string SynchronizationScopeActivityDescription = "SynchronizationScopeActivityDescription"; + internal const string SequenceActivityDescription = "SequenceActivityDescription"; + internal const string CompensateActivityDescription = "CompensateActivityDescription"; + internal const string Error_CompensateBadTargetTX = "Error_CompensateBadTargetTX"; + internal const string Error_CancelHandlerParentNotScope = "Error_CancelHandlerParentNotScope"; + internal const string FaultHandlerActivityDescription = "FaultHandlerActivityDescription"; + internal const string Error_ExceptionTypeNotException = "Error_ExceptionTypeNotException"; + internal const string Error_FaultIsNotOfFaultType = "Error_FaultIsNotOfFaultType"; + internal const string Error_FaultTypeNoDefaultConstructor = "Error_FaultTypeNoDefaultConstructor"; + internal const string FilterDescription_FaultHandlerActivity = "FilterDescription_FaultHandlerActivity"; + internal const string Error_FaultHandlerActivityParentNotFaultHandlersActivity = "Error_FaultHandlerActivityParentNotFaultHandlersActivity"; + internal const string Error_FaultHandlerActivityAllMustBeLast = "Error_FaultHandlerActivityAllMustBeLast"; + internal const string Error_FaultHandlersActivityDeclNotAllFaultHandlerActivityDecl = "Error_FaultHandlersActivityDeclNotAllFaultHandlerActivityDecl"; + internal const string Error_FaultHandlerActivityWrongOrder = "Error_FaultHandlerActivityWrongOrder"; + internal const string Error_SenderMustBeActivityExecutionContext = "Error_SenderMustBeActivityExecutionContext"; + internal const string Error_XomlWorkflowHasCode = "Error_XomlWorkflowHasCode"; + internal const string Error_WrongParamForActivityResolveEventArgs = "Error_WrongParamForActivityResolveEventArgs"; + internal const string Error_ValidatorThrewException = "Error_ValidatorThrewException"; + internal const string Error_Missing_CanModifyProperties_True = "Error_Missing_CanModifyProperties_True"; + internal const string Error_Missing_CanModifyProperties_False = "Error_Missing_CanModifyProperties_False"; + internal const string Error_ModelingConstructsCanNotContainModelingConstructs = "Error_ModelingConstructsCanNotContainModelingConstructs"; + internal const string Error_RootIsNotEnabled = "Error_RootIsNotEnabled"; + internal const string Error_MissingSetAccessor = "Error_MissingSetAccessor"; + internal const string Error_MissingAddHandler = "Error_MissingAddHandler"; + internal const string Error_MissingCLRProperty = "Error_MissingCLRProperty"; + + internal const string Error_NotReadOnlyProperty = "Error_NotReadOnlyProperty"; + internal const string Error_InvalidDependencyProperty = "Error_InvalidDependencyProperty"; + internal const string Error_ActivityNameExist = "Error_ActivityNameExist"; + internal const string CannotCreateAttribute = "CannotCreateAttribute"; + internal const string NamespaceAndDeclaringTypeCannotBeNull = "NamespaceAndDeclaringTypeCannotBeNull"; + internal const string NotElementType = "NotElementType"; + + //Layout persistence errors + internal const string Error_LayoutSerializationActivityNotFound = "Error_LayoutSerializationActivityNotFound"; + internal const string Error_LayoutSerializationAssociatedActivityNotFound = "Error_LayoutSerializationAssociatedActivityNotFound"; + internal const string Error_LayoutSerializationPersistenceSupport = "Error_LayoutSerializationPersistenceSupport"; + internal const string Error_LayoutSerializationRootDesignerNotFound = "Error_LayoutSerializationRootDesignerNotFound"; + internal const string Error_ParameterCannotBeEmpty = "Error_ParameterCannotBeEmpty"; + internal const string InvalidExecutionStatus = "InvalidExecutionStatus"; + internal const string Error_LayoutDeserialization = "Error_LayoutDeserialization"; + internal const string Error_LayoutSerialization = "Error_LayoutSerialization"; + + internal const string Error_SerializerStackOverflow = "Error_SerializerStackOverflow"; + internal const string Error_InvalidActivityForWorkflowChanges = "Error_InvalidActivityForWorkflowChanges"; + internal const string Error_InvalidMemberType = "Error_InvalidMemberType"; + internal const string Error_BindPathNullValue = "Error_BindPathNullValue"; + internal const string Error_MarkupExtensionMissingTerminatingCharacter = "Error_MarkupExtensionMissingTerminatingCharacter"; + internal const string Error_MarkupExtensionDeserializeFailed = "Error_MarkupExtensionDeserializeFailed"; + internal const string Error_ApplyDynamicChangeFailed = "Error_ApplyDynamicChangeFailed"; + internal const string Error_ActivityCircularReference = "Error_ActivityCircularReference"; + internal const string Error_ValidatorTypeIsInvalid = "Error_ValidatorTypeIsInvalid"; + internal const string Error_InvalidServiceProvider = "Error_InvalidServiceProvider"; + internal const string Error_InvalidRootForWorkflowChanges = "Error_InvalidRootForWorkflowChanges"; + internal const string Error_ExtraCharacterFoundAtEnd = "Error_ExtraCharacterFoundAtEnd"; + internal const string Error_WorkflowChangesNotSupported = "Error_WorkflowChangesNotSupported"; + internal const string Error_TypeSystemAttributeArgument = "Error_TypeSystemAttributeArgument"; + + internal const string Error_InvalidElementFoundForType = "Error_InvalidElementFoundForType"; + internal const string Error_ActivitySaveLoadNotCalled = "Error_ActivitySaveLoadNotCalled"; + internal const string Error_CanNotBindProperty = "Error_CanNotBindProperty"; +} +#pragma warning restore CS8603 // 可能返回 null 引用。 + +#endif \ No newline at end of file diff --git a/src/IFoxCAD.Basal/Sortedset/Sortedset.cs b/src/IFoxCAD.Basal/Sortedset/Sortedset.cs new file mode 100644 index 0000000..9405fc1 --- /dev/null +++ b/src/IFoxCAD.Basal/Sortedset/Sortedset.cs @@ -0,0 +1,2935 @@ +#if NET35 +#pragma warning disable CS8603 // 可能返回 null 引用。 +#pragma warning disable CS8601 // 引用类型赋值可能为 null。 +#pragma warning disable CS8618 // 在退出构造函数时,不可为 null 的字段必须包含非 null 值。请考虑声明为可以为 null。 +#pragma warning disable CS8625 // 无法将 null 字面量转换为非 null 的引用类型。 +#pragma warning disable IDE0059 // 不需要赋值 +#pragma warning disable CS8600 // 将 null 字面量或可能为 null 的值转换为非 null 类型。 +#pragma warning disable CS8602 // 解引用可能出现空引用。 +#pragma warning disable CS8604 // 引用类型参数可能为 null。 +// #define USING_HASH_SET +// ==++== +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +// ==--== +/*============================================================ +** +** Class: SortedSet +** +** Purpose: A generic sorted set. +** +** Date: August 15, 2008 +** +===========================================================*/ + + +namespace System.Collections.Generic +{ + using System; + using System.Diagnostics; + using System.Diagnostics.CodeAnalysis; + using System.Runtime.Serialization; + + + + // + // A binary search tree is a red-black tree if it satisfies the following red-black properties: + // 1. Every node is either red or black + // 2. Every leaf (nil node) is black + // 3. If a node is red, then both its children are black + // 4. Every simple path from a node to a descendant leaf contains the same number of black nodes + // + // The basic idea of red-black tree is to represent 2-3-4 trees as standard BSTs but to add one extra bit of information + // per node to encode 3-nodes and 4-nodes. + // 4-nodes will be represented as: B + // R R + // 3 -node will be represented as: B or B + // R B B R + // + // For a detailed description of the algorithm, take a look at "Algorithms" by Robert Sedgewick. + // + + internal delegate bool TreeWalkPredicate(SortedSet.Node node); + + internal enum TreeRotation + { + LeftRotation = 1, + RightRotation = 2, + RightLeftRotation = 3, + LeftRightRotation = 4, + } + + [SuppressMessage("Microsoft.Naming", "CA1710:IdentifiersShouldHaveCorrectSuffix", Justification = "by design name choice")] + [DebuggerTypeProxy(nameof(SortedSet))]/*这句改了*/ + [DebuggerDisplay("Count = {Count}")] +#if !FEATURE_NETCORE + [Serializable] + public class SortedSet : ISet, ICollection, ICollection, ISerializable, IDeserializationCallback//, IReadOnlyCollection + { +#else + public class SortedSet : ISet, ICollection, ICollection, IReadOnlyCollection { +#endif //!FEATURE_NETCORE + #region local variables/constants + Node root; + IComparer comparer; + int count; + int version; + private Object _syncRoot; + + private const String ComparerName = "Comparer"; + private const String CountName = "Count"; + private const String ItemsName = "Items"; + private const String VersionName = "Version"; + //needed for enumerator + private const String TreeName = "Tree"; + private const String NodeValueName = "Item"; + private const String EnumStartName = "EnumStarted"; + private const String ReverseName = "Reverse"; + private const String EnumVersionName = "EnumVersion"; + +#if !FEATURE_NETCORE + //needed for TreeSubset + private const String minName = "Min"; + private const String maxName = "Max"; + private const String lBoundActiveName = "lBoundActive"; + private const String uBoundActiveName = "uBoundActive"; + + private SerializationInfo siInfo; //A temporary variable which we need during deserialization. +#endif + internal const int StackAllocThreshold = 100; + + #endregion + + #region Constructors + public SortedSet() + { + this.comparer = Comparer.Default; + + } + + public SortedSet(IComparer comparer) + { + if (comparer == null) + { + this.comparer = Comparer.Default; + } + else + { + this.comparer = comparer; + } + } + + + public SortedSet(IEnumerable collection) : this(collection, Comparer.Default) { } + + public SortedSet(IEnumerable collection, IComparer comparer) + : this(comparer) + { + + if (collection == null) + { + throw new ArgumentNullException("collection"); + } + + // these are explicit type checks in the mould of HashSet. It would have worked better + // with something like an ISorted (we could make this work for SortedList.Keys etc) + SortedSet baseSortedSet = collection as SortedSet; + SortedSet baseTreeSubSet = collection as TreeSubSet; + if (baseSortedSet != null && baseTreeSubSet == null && AreComparersEqual(this, baseSortedSet)) + { + //breadth first traversal to recreate nodes + if (baseSortedSet.Count == 0) + { + count = 0; + version = 0; + root = null; + return; + } + + + //pre order way to replicate nodes + Stack theirStack = new Stack.Node>(2 * log2(baseSortedSet.Count) + 2); + Stack myStack = new Stack.Node>(2 * log2(baseSortedSet.Count) + 2); + Node theirCurrent = baseSortedSet.root; + Node myCurrent = (theirCurrent != null ? new SortedSet.Node(theirCurrent.Item, theirCurrent.IsRed) : null); + root = myCurrent; + while (theirCurrent != null) + { + theirStack.Push(theirCurrent); + myStack.Push(myCurrent); + myCurrent.Left = (theirCurrent.Left != null ? new SortedSet.Node(theirCurrent.Left.Item, theirCurrent.Left.IsRed) : null); + theirCurrent = theirCurrent.Left; + myCurrent = myCurrent.Left; + } + while (theirStack.Count != 0) + { + theirCurrent = theirStack.Pop(); + myCurrent = myStack.Pop(); + Node theirRight = theirCurrent.Right; + Node myRight = null; + if (theirRight != null) + { + myRight = new SortedSet.Node(theirRight.Item, theirRight.IsRed); + } + myCurrent.Right = myRight; + + while (theirRight != null) + { + theirStack.Push(theirRight); + myStack.Push(myRight); + myRight.Left = (theirRight.Left != null ? new SortedSet.Node(theirRight.Left.Item, theirRight.Left.IsRed) : null); + theirRight = theirRight.Left; + myRight = myRight.Left; + } + } + count = baseSortedSet.count; + version = 0; + } + else + { //As it stands, you're doing an NlogN sort of the collection + + List els = new List(collection); + els.Sort(this.comparer); + for (int i = 1; i < els.Count; i++) + { + if (comparer.Compare(els[i], els[i - 1]) == 0) + { + els.RemoveAt(i); + i--; + } + } + root = ConstructRootFromSortedArray(els.ToArray(), 0, els.Count - 1, null); + count = els.Count; + version = 0; + } + } + + +#if !FEATURE_NETCORE + + protected SortedSet(SerializationInfo info, StreamingContext context) + { + siInfo = info; + } +#endif + #endregion + + #region Bulk Operation Helpers + private void AddAllElements(IEnumerable collection) + { + + foreach (T item in collection) + { + if (!this.Contains(item)) + Add(item); + } + } + + private void RemoveAllElements(IEnumerable collection) + { + T min = this.Min; + T max = this.Max; + foreach (T item in collection) + { + if (!(comparer.Compare(item, min) < 0 || comparer.Compare(item, max) > 0) && this.Contains(item)) + this.Remove(item); + } + } + + private bool ContainsAllElements(IEnumerable collection) + { + foreach (T item in collection) + { + if (!this.Contains(item)) + { + return false; + } + } + return true; + } + + // + // Do a in order walk on tree and calls the delegate for each node. + // If the action delegate returns false, stop the walk. + // + // Return true if the entire tree has been walked. + // Otherwise returns false. + // + internal bool InOrderTreeWalk(TreeWalkPredicate action) + { + return InOrderTreeWalk(action, false); + } + + // Allows for the change in traversal direction. Reverse visits nodes in descending order + internal virtual bool InOrderTreeWalk(TreeWalkPredicate action, bool reverse) + { + if (root == null) + { + return true; + } + + // The maximum height of a red-black tree is 2*lg(n+1). + // See page 264 of "Introduction to algorithms" by Thomas H. Cormen + // note: this should be logbase2, but since the stack grows itself, we + // don't want the extra cost + Stack stack = new Stack(2 * (int)(SortedSet.log2(Count + 1))); + Node current = root; + while (current != null) + { + stack.Push(current); + current = (reverse ? current.Right : current.Left); + } + while (stack.Count != 0) + { + current = stack.Pop(); + if (!action(current)) + { + return false; + } + + Node node = (reverse ? current.Left : current.Right); + while (node != null) + { + stack.Push(node); + node = (reverse ? node.Right : node.Left); + } + } + return true; + } + + // + // Do a left to right breadth first walk on tree and + // calls the delegate for each node. + // If the action delegate returns false, stop the walk. + // + // Return true if the entire tree has been walked. + // Otherwise returns false. + // + internal virtual bool BreadthFirstTreeWalk(TreeWalkPredicate action) + { + if (root == null) + { + return true; + } + + List processQueue = new List(); + processQueue.Add(root); + Node current; + + while (processQueue.Count != 0) + { + current = processQueue[0]; + processQueue.RemoveAt(0); + if (!action(current)) + { + return false; + } + if (current.Left != null) + { + processQueue.Add(current.Left); + } + if (current.Right != null) + { + processQueue.Add(current.Right); + } + } + return true; + } + #endregion + + #region Properties + public int Count + { + get + { + VersionCheck(); + return count; + } + } + + public IComparer Comparer + { + get + { + return comparer; + } + } + + bool ICollection.IsReadOnly + { + get + { + return false; + } + } + + bool ICollection.IsSynchronized + { + get + { + return false; + } + } + + object ICollection.SyncRoot + { + get + { + if (_syncRoot == null) + { + System.Threading.Interlocked.CompareExchange(ref _syncRoot, new Object(), null); + } + return _syncRoot; + } + } + #endregion + + #region Subclass helpers + + //virtual function for subclass that needs to update count + internal virtual void VersionCheck() { } + + + //virtual function for subclass that needs to do range checks + internal virtual bool IsWithinRange(T item) + { + return true; + + } + #endregion + + #region ICollection Members + /// + /// Add the value ITEM to the tree, returns true if added, false if duplicate + /// + /// item to be added + public bool Add(T item) + { + return AddIfNotPresent(item); + } + + void ICollection.Add(T item) + { + AddIfNotPresent(item); + } + + + /// + /// Adds ITEM to the tree if not already present. Returns TRUE if value was successfully added + /// or FALSE if it is a duplicate + /// + internal virtual bool AddIfNotPresent(T item) + { + if (root == null) + { // empty tree + root = new Node(item, false); + count = 1; + version++; + return true; + } + + // + // Search for a node at bottom to insert the new node. + // If we can guanratee the node we found is not a 4-node, it would be easy to do insertion. + // We split 4-nodes along the search path. + // + Node current = root; + Node parent = null; + Node grandParent = null; + Node greatGrandParent = null; + + //even if we don't actually add to the set, we may be altering its structure (by doing rotations + //and such). so update version to disable any enumerators/subsets working on it + version++; + + + int order = 0; + while (current != null) + { + order = comparer.Compare(item, current.Item); + if (order == 0) + { + // We could have changed root node to red during the search process. + // We need to set it to black before we return. + root.IsRed = false; + return false; + } + + // split a 4-node into two 2-nodes + if (Is4Node(current)) + { + Split4Node(current); + // We could have introduced two consecutive red nodes after split. Fix that by rotation. + if (IsRed(parent)) + { + InsertionBalance(current, ref parent, grandParent, greatGrandParent); + } + } + greatGrandParent = grandParent; + grandParent = parent; + parent = current; + current = (order < 0) ? current.Left : current.Right; + } + + Debug.Assert(parent != null, "Parent node cannot be null here!"); + // ready to insert the new node + Node node = new Node(item); + if (order > 0) + { + parent.Right = node; + } + else + { + parent.Left = node; + } + + // the new node will be red, so we will need to adjust the colors if parent node is also red + if (parent.IsRed) + { + InsertionBalance(node, ref parent, grandParent, greatGrandParent); + } + + // Root node is always black + root.IsRed = false; + ++count; + return true; + } + + /// + /// Remove the T ITEM from this SortedSet. Returns true if successfully removed. + /// + /// + /// + public bool Remove(T item) + { + return this.DoRemove(item); // hack so it can be made non-virtual + } + + internal virtual bool DoRemove(T item) + { + + if (root == null) + { + return false; + } + + + // Search for a node and then find its succesor. + // Then copy the item from the succesor to the matching node and delete the successor. + // If a node doesn't have a successor, we can replace it with its left child (if not empty.) + // or delete the matching node. + // + // In top-down implementation, it is important to make sure the node to be deleted is not a 2-node. + // Following code will make sure the node on the path is not a 2 Node. + + //even if we don't actually remove from the set, we may be altering its structure (by doing rotations + //and such). so update version to disable any enumerators/subsets working on it + version++; + + Node current = root; + Node parent = null; + Node grandParent = null; + Node match = null; + Node parentOfMatch = null; + bool foundMatch = false; + while (current != null) + { + if (Is2Node(current)) + { // fix up 2-Node + if (parent == null) + { // current is root. Mark it as red + current.IsRed = true; + } + else + { + Node sibling = GetSibling(current, parent); + if (sibling.IsRed) + { + // If parent is a 3-node, flip the orientation of the red link. + // We can acheive this by a single rotation + // This case is converted to one of other cased below. + Debug.Assert(!parent.IsRed, "parent must be a black node!"); + if (parent.Right == sibling) + { + RotateLeft(parent); + } + else + { + RotateRight(parent); + } + + parent.IsRed = true; + sibling.IsRed = false; // parent's color + // sibling becomes child of grandParent or root after rotation. Update link from grandParent or root + ReplaceChildOfNodeOrRoot(grandParent, parent, sibling); + // sibling will become grandParent of current node + grandParent = sibling; + if (parent == match) + { + parentOfMatch = sibling; + } + + // update sibling, this is necessary for following processing + sibling = (parent.Left == current) ? parent.Right : parent.Left; + } + Debug.Assert(sibling != null || sibling.IsRed == false, "sibling must not be null and it must be black!"); + + if (Is2Node(sibling)) + { + Merge2Nodes(parent, current, sibling); + } + else + { + // current is a 2-node and sibling is either a 3-node or a 4-node. + // We can change the color of current to red by some rotation. + TreeRotation rotation = RotationNeeded(parent, current, sibling); + Node newGrandParent = null; + switch (rotation) + { + case TreeRotation.RightRotation: + Debug.Assert(parent.Left == sibling, "sibling must be left child of parent!"); + Debug.Assert(sibling.Left.IsRed, "Left child of sibling must be red!"); + sibling.Left.IsRed = false; + newGrandParent = RotateRight(parent); + break; + case TreeRotation.LeftRotation: + Debug.Assert(parent.Right == sibling, "sibling must be left child of parent!"); + Debug.Assert(sibling.Right.IsRed, "Right child of sibling must be red!"); + sibling.Right.IsRed = false; + newGrandParent = RotateLeft(parent); + break; + + case TreeRotation.RightLeftRotation: + Debug.Assert(parent.Right == sibling, "sibling must be left child of parent!"); + Debug.Assert(sibling.Left.IsRed, "Left child of sibling must be red!"); + newGrandParent = RotateRightLeft(parent); + break; + + case TreeRotation.LeftRightRotation: + Debug.Assert(parent.Left == sibling, "sibling must be left child of parent!"); + Debug.Assert(sibling.Right.IsRed, "Right child of sibling must be red!"); + newGrandParent = RotateLeftRight(parent); + break; + } + + newGrandParent.IsRed = parent.IsRed; + parent.IsRed = false; + current.IsRed = true; + ReplaceChildOfNodeOrRoot(grandParent, parent, newGrandParent); + if (parent == match) + { + parentOfMatch = newGrandParent; + } + grandParent = newGrandParent; + } + } + } + + // we don't need to compare any more once we found the match + int order = foundMatch ? -1 : comparer.Compare(item, current.Item); + if (order == 0) + { + // save the matching node + foundMatch = true; + match = current; + parentOfMatch = parent; + } + + grandParent = parent; + parent = current; + + if (order < 0) + { + current = current.Left; + } + else + { + current = current.Right; // continue the search in right sub tree after we find a match + } + } + + // move successor to the matching node position and replace links + if (match != null) + { + ReplaceNode(match, parentOfMatch, parent, grandParent); + --count; + } + + if (root != null) + { + root.IsRed = false; + } + return foundMatch; + } + + public virtual void Clear() + { + root = null; + count = 0; + ++version; + } + + + public virtual bool Contains(T item) + { + + return FindNode(item) != null; + } + + + + + public void CopyTo(T[] array) { CopyTo(array, 0, Count); } + + public void CopyTo(T[] array, int index) { CopyTo(array, index, Count); } + + public void CopyTo(T[] array, int index, int count) + { + if (array == null) + { + ThrowHelper.ThrowArgumentNullException(ExceptionArgument.array); + } + + if (index < 0) + { + ThrowHelper.ThrowArgumentOutOfRangeException(ExceptionArgument.index); + } + + if (count < 0) + { + throw new ArgumentOutOfRangeException("count", SR.GetString("SR.ArgumentOutOfRange_NeedNonNegNum")); + } + + // will array, starting at arrayIndex, be able to hold elements? Note: not + // checking arrayIndex >= array.Length (consistency with list of allowing + // count of 0; subsequent check takes care of the rest) + if (index > array.Length || count > array.Length - index) + { + throw new ArgumentException(SR.GetString("SR.Arg_ArrayPlusOffTooSmall")); + } + //upper bound + count += index; + + InOrderTreeWalk(delegate (Node node) + { + if (index >= count) + { + return false; + } + else + { + array[index++] = node.Item; + return true; + } + }); + } + + void ICollection.CopyTo(Array array, int index) + { + if (array == null) + { + ThrowHelper.ThrowArgumentNullException(ExceptionArgument.array); + } + + if (array.Rank != 1) + { + ThrowHelper.ThrowArgumentException(ExceptionResource.Arg_RankMultiDimNotSupported); + } + + if (array.GetLowerBound(0) != 0) + { + ThrowHelper.ThrowArgumentException(ExceptionResource.Arg_NonZeroLowerBound); + } + + if (index < 0) + { + ThrowHelper.ThrowArgumentOutOfRangeException(ExceptionArgument.arrayIndex, ExceptionResource.ArgumentOutOfRange_NeedNonNegNum); + } + + if (array.Length - index < Count) + { + ThrowHelper.ThrowArgumentException(ExceptionResource.Arg_ArrayPlusOffTooSmall); + } + + T[] tarray = array as T[]; + if (tarray != null) + { + CopyTo(tarray, index); + } + else + { + object[] objects = array as object[]; + if (objects == null) + { + ThrowHelper.ThrowArgumentException(ExceptionResource.Argument_InvalidArrayType); + } + + try + { + InOrderTreeWalk(delegate (Node node) { objects[index++] = node.Item; return true; }); + } + catch (ArrayTypeMismatchException) + { + ThrowHelper.ThrowArgumentException(ExceptionResource.Argument_InvalidArrayType); + } + } + } + + #endregion + + #region IEnumerable members + public Enumerator GetEnumerator() + { + return new Enumerator(this); + } + + + + + IEnumerator IEnumerable.GetEnumerator() + { + return new Enumerator(this); + } + + IEnumerator IEnumerable.GetEnumerator() + { + return new Enumerator(this); + } + #endregion + + #region Tree Specific Operations + + private static Node GetSibling(Node node, Node parent) + { + if (parent.Left == node) + { + return parent.Right; + } + return parent.Left; + } + + // After calling InsertionBalance, we need to make sure current and parent up-to-date. + // It doesn't matter if we keep grandParent and greatGrantParent up-to-date + // because we won't need to split again in the next node. + // By the time we need to split again, everything will be correctly set. + // + private void InsertionBalance(Node current, ref Node parent, Node grandParent, Node greatGrandParent) + { + Debug.Assert(grandParent != null, "Grand parent cannot be null here!"); + bool parentIsOnRight = (grandParent.Right == parent); + bool currentIsOnRight = (parent.Right == current); + + Node newChildOfGreatGrandParent; + if (parentIsOnRight == currentIsOnRight) + { // same orientation, single rotation + newChildOfGreatGrandParent = currentIsOnRight ? RotateLeft(grandParent) : RotateRight(grandParent); + } + else + { // different orientaton, double rotation + newChildOfGreatGrandParent = currentIsOnRight ? RotateLeftRight(grandParent) : RotateRightLeft(grandParent); + // current node now becomes the child of greatgrandparent + parent = greatGrandParent; + } + // grand parent will become a child of either parent of current. + grandParent.IsRed = true; + newChildOfGreatGrandParent.IsRed = false; + + ReplaceChildOfNodeOrRoot(greatGrandParent, grandParent, newChildOfGreatGrandParent); + } + + private static bool Is2Node(Node node) + { + Debug.Assert(node != null, "node cannot be null!"); + return IsBlack(node) && IsNullOrBlack(node.Left) && IsNullOrBlack(node.Right); + } + + private static bool Is4Node(Node node) + { + return IsRed(node.Left) && IsRed(node.Right); + } + + private static bool IsBlack(Node node) + { + return (node != null && !node.IsRed); + } + + private static bool IsNullOrBlack(Node node) + { + return (node == null || !node.IsRed); + } + + private static bool IsRed(Node node) + { + return (node != null && node.IsRed); + } + + private static void Merge2Nodes(Node parent, Node child1, Node child2) + { + Debug.Assert(IsRed(parent), "parent must be be red"); + // combing two 2-nodes into a 4-node + parent.IsRed = false; + child1.IsRed = true; + child2.IsRed = true; + } + + // Replace the child of a parent node. + // If the parent node is null, replace the root. + private void ReplaceChildOfNodeOrRoot(Node parent, Node child, Node newChild) + { + if (parent != null) + { + if (parent.Left == child) + { + parent.Left = newChild; + } + else + { + parent.Right = newChild; + } + } + else + { + root = newChild; + } + } + + // Replace the matching node with its succesor. + private void ReplaceNode(Node match, Node parentOfMatch, Node succesor, Node parentOfSuccesor) + { + if (succesor == match) + { // this node has no successor, should only happen if right child of matching node is null. + Debug.Assert(match.Right == null, "Right child must be null!"); + succesor = match.Left; + } + else + { + Debug.Assert(parentOfSuccesor != null, "parent of successor cannot be null!"); + Debug.Assert(succesor.Left == null, "Left child of succesor must be null!"); + Debug.Assert((succesor.Right == null && succesor.IsRed) || (succesor.Right.IsRed && !succesor.IsRed), "Succesor must be in valid state"); + if (succesor.Right != null) + { + succesor.Right.IsRed = false; + } + + if (parentOfSuccesor != match) + { // detach succesor from its parent and set its right child + parentOfSuccesor.Left = succesor.Right; + succesor.Right = match.Right; + } + + succesor.Left = match.Left; + } + + if (succesor != null) + { + succesor.IsRed = match.IsRed; + } + + ReplaceChildOfNodeOrRoot(parentOfMatch, match, succesor); + + } + + internal virtual Node FindNode(T item) + { + Node current = root; + while (current != null) + { + int order = comparer.Compare(item, current.Item); + if (order == 0) + { + return current; + } + else + { + current = (order < 0) ? current.Left : current.Right; + } + } + + return null; + } + + //used for bithelpers. Note that this implementation is completely different + //from the Subset's. The two should not be mixed. This indexes as if the tree were an array. + //http://en.wikipedia.org/wiki/Binary_Tree#Methods_for_storing_binary_trees + internal virtual int InternalIndexOf(T item) + { + Node current = root; + int count = 0; + while (current != null) + { + int order = comparer.Compare(item, current.Item); + if (order == 0) + { + return count; + } + else + { + current = (order < 0) ? current.Left : current.Right; + count = (order < 0) ? (2 * count + 1) : (2 * count + 2); + } + } + return -1; + } + + + + internal Node FindRange(T from, T to) + { + return FindRange(from, to, true, true); + } + internal Node FindRange(T from, T to, bool lowerBoundActive, bool upperBoundActive) + { + Node current = root; + while (current != null) + { + if (lowerBoundActive && comparer.Compare(from, current.Item) > 0) + { + current = current.Right; + } + else + { + if (upperBoundActive && comparer.Compare(to, current.Item) < 0) + { + current = current.Left; + } + else + { + return current; + } + } + } + + return null; + } + + internal void UpdateVersion() + { + ++version; + } + + + private static Node RotateLeft(Node node) + { + Node x = node.Right; + node.Right = x.Left; + x.Left = node; + return x; + } + + private static Node RotateLeftRight(Node node) + { + Node child = node.Left; + Node grandChild = child.Right; + + node.Left = grandChild.Right; + grandChild.Right = node; + child.Right = grandChild.Left; + grandChild.Left = child; + return grandChild; + } + + private static Node RotateRight(Node node) + { + Node x = node.Left; + node.Left = x.Right; + x.Right = node; + return x; + } + + private static Node RotateRightLeft(Node node) + { + Node child = node.Right; + Node grandChild = child.Left; + + node.Right = grandChild.Left; + grandChild.Left = node; + child.Left = grandChild.Right; + grandChild.Right = child; + return grandChild; + } + /// + /// Testing counter that can track rotations + /// + + + private static TreeRotation RotationNeeded(Node parent, Node current, Node sibling) + { + Debug.Assert(IsRed(sibling.Left) || IsRed(sibling.Right), "sibling must have at least one red child"); + if (IsRed(sibling.Left)) + { + if (parent.Left == current) + { + return TreeRotation.RightLeftRotation; + } + return TreeRotation.RightRotation; + } + else + { + if (parent.Left == current) + { + return TreeRotation.LeftRotation; + } + return TreeRotation.LeftRightRotation; + } + } + + /// + /// Used for deep equality of SortedSet testing + /// + /// + public static IEqualityComparer> CreateSetComparer() + { + return new SortedSetEqualityComparer(); + } + + /// + /// Create a new set comparer for this set, where this set's members' equality is defined by the + /// memberEqualityComparer. Note that this equality comparer's definition of equality must be the + /// same as this set's Comparer's definition of equality + /// + public static IEqualityComparer> CreateSetComparer(IEqualityComparer memberEqualityComparer) + { + return new SortedSetEqualityComparer(memberEqualityComparer); + } + + + /// + /// Decides whether these sets are the same, given the comparer. If the EC's are the same, we can + /// just use SetEquals, but if they aren't then we have to manually check with the given comparer + /// + internal static bool SortedSetEquals(SortedSet set1, SortedSet set2, IComparer comparer) + { + // handle null cases first + if (set1 == null) + { + return (set2 == null); + } + else if (set2 == null) + { + // set1 != null + return false; + } + + if (AreComparersEqual(set1, set2)) + { + if (set1.Count != set2.Count) + return false; + + return set1.SetEquals(set2); + } + else + { + bool found = false; + foreach (T item1 in set1) + { + found = false; + foreach (T item2 in set2) + { + if (comparer.Compare(item1, item2) == 0) + { + found = true; + break; + } + } + if (!found) + return false; + } + return true; + } + + } + + + //This is a little frustrating because we can't support more sorted structures + private static bool AreComparersEqual(SortedSet set1, SortedSet set2) + { + return set1.Comparer.Equals(set2.Comparer); + } + + + private static void Split4Node(Node node) + { + node.IsRed = true; + node.Left.IsRed = false; + node.Right.IsRed = false; + } + + /// + /// Copies this to an array. Used for DebugView + /// + /// + internal T[] ToArray() + { + T[] newArray = new T[Count]; + CopyTo(newArray); + return newArray; + } + + + #endregion + + #region ISet Members + + /// + /// Transform this set into its union with the IEnumerable OTHER + ///Attempts to insert each element and rejects it if it exists. + /// NOTE: The caller object is important as UnionWith uses the Comparator + ///associated with THIS to check equality + /// Throws ArgumentNullException if OTHER is null + /// + /// + public void UnionWith(IEnumerable other) + { + if (other == null) + { + throw new ArgumentNullException("other"); + } + + SortedSet s = other as SortedSet; + TreeSubSet t = this as TreeSubSet; + + if (t != null) + VersionCheck(); + + if (s != null && t == null && this.count == 0) + { + SortedSet dummy = new SortedSet(s, this.comparer); + this.root = dummy.root; + this.count = dummy.count; + this.version++; + return; + } + + + if (s != null && t == null && AreComparersEqual(this, s) && (s.Count > this.Count / 2)) + { //this actually hurts if N is much greater than M the /2 is arbitrary + //first do a merge sort to an array. + T[] merged = new T[s.Count + this.Count]; + int c = 0; + Enumerator mine = this.GetEnumerator(); + Enumerator theirs = s.GetEnumerator(); + bool mineEnded = !mine.MoveNext(), theirsEnded = !theirs.MoveNext(); + while (!mineEnded && !theirsEnded) + { + int comp = Comparer.Compare(mine.Current, theirs.Current); + if (comp < 0) + { + merged[c++] = mine.Current; + mineEnded = !mine.MoveNext(); + } + else if (comp == 0) + { + merged[c++] = theirs.Current; + mineEnded = !mine.MoveNext(); + theirsEnded = !theirs.MoveNext(); + } + else + { + merged[c++] = theirs.Current; + theirsEnded = !theirs.MoveNext(); + } + } + + if (!mineEnded || !theirsEnded) + { + Enumerator remaining = (mineEnded ? theirs : mine); + do + { + merged[c++] = remaining.Current; + } while (remaining.MoveNext()); + } + + //now merged has all c elements + + //safe to gc the root, we have all the elements + root = null; + + + root = SortedSet.ConstructRootFromSortedArray(merged, 0, c - 1, null); + count = c; + version++; + } + else + { + AddAllElements(other); + } + } + + + private static Node ConstructRootFromSortedArray(T[] arr, int startIndex, int endIndex, Node redNode) + { + + + + //what does this do? + //you're given a sorted array... say 1 2 3 4 5 6 + //2 cases: + // If there are odd # of elements, pick the middle element (in this case 4), and compute + // its left and right branches + // If there are even # of elements, pick the left middle element, save the right middle element + // and call the function on the rest + // 1 2 3 4 5 6 -> pick 3, save 4 and call the fn on 1,2 and 5,6 + // now add 4 as a red node to the lowest element on the right branch + // 3 3 + // 1 5 -> 1 5 + // 2 6 2 4 6 + // As we're adding to the leftmost of the right branch, nesting will not hurt the red-black properties + // Leaf nodes are red if they have no sibling (if there are 2 nodes or if a node trickles + // down to the bottom + + + //the iterative way to do this ends up wasting more space than it saves in stack frames (at + //least in what i tried) + //so we're doing this recursively + //base cases are described below + int size = endIndex - startIndex + 1; + if (size == 0) + { + return null; + } + Node root = null; + if (size == 1) + { + root = new Node(arr[startIndex], false); + if (redNode != null) + { + root.Left = redNode; + } + } + else if (size == 2) + { + root = new Node(arr[startIndex], false); + root.Right = new Node(arr[endIndex], false); + root.Right.IsRed = true; + if (redNode != null) + { + root.Left = redNode; + } + } + else if (size == 3) + { + root = new Node(arr[startIndex + 1], false); + root.Left = new Node(arr[startIndex], false); + root.Right = new Node(arr[endIndex], false); + if (redNode != null) + { + root.Left.Left = redNode; + + } + } + else + { + int midpt = ((startIndex + endIndex) / 2); + root = new Node(arr[midpt], false); + root.Left = ConstructRootFromSortedArray(arr, startIndex, midpt - 1, redNode); + if (size % 2 == 0) + { + root.Right = ConstructRootFromSortedArray(arr, midpt + 2, endIndex, new Node(arr[midpt + 1], true)); + } + else + { + root.Right = ConstructRootFromSortedArray(arr, midpt + 1, endIndex, null); + } + } + return root; + + } + + + /// + /// Transform this set into its intersection with the IEnumerable OTHER + /// NOTE: The caller object is important as IntersectionWith uses the + /// comparator associated with THIS to check equality + /// Throws ArgumentNullException if OTHER is null + /// + /// + public virtual void IntersectWith(IEnumerable other) + { + if (other == null) + { + throw new ArgumentNullException("other"); + } + + if (Count == 0) + return; + + //HashSet optimizations can't be done until equality comparers and comparers are related + + //Technically, this would work as well with an ISorted + SortedSet s = other as SortedSet; + TreeSubSet t = this as TreeSubSet; + if (t != null) + VersionCheck(); + //only let this happen if i am also a SortedSet, not a SubSet + if (s != null && t == null && AreComparersEqual(this, s)) + { + + + //first do a merge sort to an array. + T[] merged = new T[this.Count]; + int c = 0; + Enumerator mine = this.GetEnumerator(); + Enumerator theirs = s.GetEnumerator(); + bool mineEnded = !mine.MoveNext(), theirsEnded = !theirs.MoveNext(); + T max = Max; + T min = Min; + + while (!mineEnded && !theirsEnded && Comparer.Compare(theirs.Current, max) <= 0) + { + int comp = Comparer.Compare(mine.Current, theirs.Current); + if (comp < 0) + { + mineEnded = !mine.MoveNext(); + } + else if (comp == 0) + { + merged[c++] = theirs.Current; + mineEnded = !mine.MoveNext(); + theirsEnded = !theirs.MoveNext(); + } + else + { + theirsEnded = !theirs.MoveNext(); + } + } + + //now merged has all c elements + + //safe to gc the root, we have all the elements + root = null; + + root = SortedSet.ConstructRootFromSortedArray(merged, 0, c - 1, null); + count = c; + version++; + } + else + { + IntersectWithEnumerable(other); + } + } + + internal virtual void IntersectWithEnumerable(IEnumerable other) + { + // + List toSave = new List(this.Count); + foreach (T item in other) + { + if (this.Contains(item)) + { + toSave.Add(item); + this.Remove(item); + } + } + this.Clear(); + AddAllElements(toSave); + + } + + + + /// + /// Transform this set into its complement with the IEnumerable OTHER + /// NOTE: The caller object is important as ExceptWith uses the + /// comparator associated with THIS to check equality + /// Throws ArgumentNullException if OTHER is null + /// + /// + public void ExceptWith(IEnumerable other) + { + if (other == null) + { + throw new ArgumentNullException("other"); + } + + if (count == 0) + return; + + if (other == this) + { + this.Clear(); + return; + } + + SortedSet asSorted = other as SortedSet; + + if (asSorted != null && AreComparersEqual(this, asSorted)) + { + //outside range, no point doing anything + if (!(comparer.Compare(asSorted.Max, this.Min) < 0 || comparer.Compare(asSorted.Min, this.Max) > 0)) + { + T min = this.Min; + T max = this.Max; + foreach (T item in other) + { + if (comparer.Compare(item, min) < 0) + continue; + if (comparer.Compare(item, max) > 0) + break; + Remove(item); + } + } + + } + else + { + RemoveAllElements(other); + } + } + + /// + /// Transform this set so it contains elements in THIS or OTHER but not both + /// NOTE: The caller object is important as SymmetricExceptWith uses the + /// comparator associated with THIS to check equality + /// Throws ArgumentNullException if OTHER is null + /// + /// + public void SymmetricExceptWith(IEnumerable other) + { + if (other == null) + { + throw new ArgumentNullException("other"); + } + + if (this.Count == 0) + { + this.UnionWith(other); + return; + } + + if (other == this) + { + this.Clear(); + return; + } + + + SortedSet asSorted = other as SortedSet; + +#if USING_HASH_SET + HashSet asHash = other as HashSet; +#endif + if (asSorted != null && AreComparersEqual(this, asSorted)) + { + SymmetricExceptWithSameEC(asSorted); + } +#if USING_HASH_SET + else if (asHash != null && this.comparer.Equals(Comparer.Default) && asHash.Comparer.Equals(EqualityComparer.Default)) { + SymmetricExceptWithSameEC(asHash); + } +#endif + else + { + //need perf improvement on this + T[] elements = (new List(other)).ToArray(); + Array.Sort(elements, this.Comparer); + SymmetricExceptWithSameEC(elements); + } + } + + //OTHER must be a set + internal void SymmetricExceptWithSameEC(ISet other) + { + foreach (T item in other) + { + //yes, it is classier to say + //if (!this.Remove(item))this.Add(item); + //but this ends up saving on rotations + if (this.Contains(item)) + { + this.Remove(item); + } + else + { + this.Add(item); + } + } + } + + //OTHER must be a sorted array + internal void SymmetricExceptWithSameEC(T[] other) + { + if (other.Length == 0) + { + return; + } + T last = other[0]; + for (int i = 0; i < other.Length; i++) + { + while (i < other.Length && i != 0 && comparer.Compare(other[i], last) == 0) + i++; + if (i >= other.Length) + break; + if (this.Contains(other[i])) + { + this.Remove(other[i]); + } + else + { + this.Add(other[i]); + } + last = other[i]; + } + } + + + /// + /// Checks whether this Tree is a subset of the IEnumerable other + /// + /// + /// + [System.Security.SecuritySafeCritical] + public bool IsSubsetOf(IEnumerable other) + { + if (other == null) + { + throw new ArgumentNullException("other"); + } + + if (Count == 0) + return true; + + + SortedSet asSorted = other as SortedSet; + if (asSorted != null && AreComparersEqual(this, asSorted)) + { + if (this.Count > asSorted.Count) + return false; + return IsSubsetOfSortedSetWithSameEC(asSorted); + } + else + { + //worst case: mark every element in my set and see if i've counted all + //O(MlogN) + + ElementCount result = CheckUniqueAndUnfoundElements(other, false); + return (result.uniqueCount == Count && result.unfoundCount >= 0); + } + } + + private bool IsSubsetOfSortedSetWithSameEC(SortedSet asSorted) + { + SortedSet prunedOther = asSorted.GetViewBetween(this.Min, this.Max); + foreach (T item in this) + { + if (!prunedOther.Contains(item)) + return false; + } + return true; + + } + + + /// + /// Checks whether this Tree is a proper subset of the IEnumerable other + /// + /// + /// + [System.Security.SecuritySafeCritical] + public bool IsProperSubsetOf(IEnumerable other) + { + if (other == null) + { + throw new ArgumentNullException("other"); + } + + if ((other as ICollection) != null) + { + if (Count == 0) + return (other as ICollection).Count > 0; + } + + +#if USING_HASH_SET + //do it one way for HashSets + HashSet asHash = other as HashSet; + if (asHash != null && comparer.Equals(Comparer.Default) && asHash.Comparer.Equals(EqualityComparer.Default)) { + return asHash.IsProperSupersetOf(this); + } +#endif + //another for sorted sets with the same comparer + SortedSet asSorted = other as SortedSet; + if (asSorted != null && AreComparersEqual(this, asSorted)) + { + if (this.Count >= asSorted.Count) + return false; + return IsSubsetOfSortedSetWithSameEC(asSorted); + } + + + //worst case: mark every element in my set and see if i've counted all + //O(MlogN). + ElementCount result = CheckUniqueAndUnfoundElements(other, false); + return (result.uniqueCount == Count && result.unfoundCount > 0); + } + + + /// + /// Checks whether this Tree is a super set of the IEnumerable other + /// + /// + /// + public bool IsSupersetOf(IEnumerable other) + { + if (other == null) + { + throw new ArgumentNullException("other"); + } + + if ((other as ICollection) != null && (other as ICollection).Count == 0) + return true; + + //do it one way for HashSets +#if USING_HASH_SET + HashSet asHash = other as HashSet; + if (asHash != null && comparer.Equals(Comparer.Default) && asHash.Comparer.Equals(EqualityComparer.Default)) { + return asHash.IsSubsetOf(this); + } +#endif + //another for sorted sets with the same comparer + SortedSet asSorted = other as SortedSet; + if (asSorted != null && AreComparersEqual(this, asSorted)) + { + if (this.Count < asSorted.Count) + return false; + SortedSet pruned = GetViewBetween(asSorted.Min, asSorted.Max); + foreach (T item in asSorted) + { + if (!pruned.Contains(item)) + return false; + } + return true; + } + //and a third for everything else + return ContainsAllElements(other); + } + + /// + /// Checks whether this Tree is a proper super set of the IEnumerable other + /// + /// + /// + [System.Security.SecuritySafeCritical] + public bool IsProperSupersetOf(IEnumerable other) + { + if (other == null) + { + throw new ArgumentNullException("other"); + } + + if (Count == 0) + return false; + + if ((other as ICollection) != null && (other as ICollection).Count == 0) + return true; + +#if USING_HASH_SET + //do it one way for HashSets + + HashSet asHash = other as HashSet; + if (asHash != null && comparer.Equals(Comparer.Default) && asHash.Comparer.Equals(EqualityComparer.Default)) { + return asHash.IsProperSubsetOf(this); + } +#endif + //another way for sorted sets + SortedSet asSorted = other as SortedSet; + if (asSorted != null && AreComparersEqual(asSorted, this)) + { + if (asSorted.Count >= this.Count) + return false; + SortedSet pruned = GetViewBetween(asSorted.Min, asSorted.Max); + foreach (T item in asSorted) + { + if (!pruned.Contains(item)) + return false; + } + return true; + } + + + //worst case: mark every element in my set and see if i've counted all + //O(MlogN) + //slight optimization, put it into a HashSet and then check can do it in O(N+M) + //but slower in better cases + wastes space + ElementCount result = CheckUniqueAndUnfoundElements(other, true); + return (result.uniqueCount < Count && result.unfoundCount == 0); + } + + + + /// + /// Checks whether this Tree has all elements in common with IEnumerable other + /// + /// + /// + [System.Security.SecuritySafeCritical] + public bool SetEquals(IEnumerable other) + { + if (other == null) + { + throw new ArgumentNullException("other"); + } + +#if USING_HASH_SET + HashSet asHash = other as HashSet; + if (asHash != null && comparer.Equals(Comparer.Default) && asHash.Comparer.Equals(EqualityComparer.Default)) { + return asHash.SetEquals(this); + } +#endif + SortedSet asSorted = other as SortedSet; + if (asSorted != null && AreComparersEqual(this, asSorted)) + { + IEnumerator mine = this.GetEnumerator(); + IEnumerator theirs = asSorted.GetEnumerator(); + bool mineEnded = !mine.MoveNext(); + bool theirsEnded = !theirs.MoveNext(); + while (!mineEnded && !theirsEnded) + { + if (Comparer.Compare(mine.Current, theirs.Current) != 0) + { + return false; + } + mineEnded = !mine.MoveNext(); + theirsEnded = !theirs.MoveNext(); + } + return mineEnded && theirsEnded; + } + + //worst case: mark every element in my set and see if i've counted all + //O(N) by size of other + ElementCount result = CheckUniqueAndUnfoundElements(other, true); + return (result.uniqueCount == Count && result.unfoundCount == 0); + } + + + + /// + /// Checks whether this Tree has any elements in common with IEnumerable other + /// + /// + /// + public bool Overlaps(IEnumerable other) + { + if (other == null) + { + throw new ArgumentNullException("other"); + } + + if (this.Count == 0) + return false; + + if ((other as ICollection != null) && (other as ICollection).Count == 0) + return false; + + SortedSet asSorted = other as SortedSet; + if (asSorted != null && AreComparersEqual(this, asSorted) && (comparer.Compare(Min, asSorted.Max) > 0 || comparer.Compare(Max, asSorted.Min) < 0)) + { + return false; + } +#if USING_HASH_SET + HashSet asHash = other as HashSet; + if (asHash != null && comparer.Equals(Comparer.Default) && asHash.Comparer.Equals(EqualityComparer.Default)) { + return asHash.Overlaps(this); + } +#endif + foreach (T item in other) + { + if (this.Contains(item)) + { + return true; + } + } + return false; + } + + /// + /// This works similar to HashSet's CheckUniqueAndUnfound (description below), except that the bit + /// array maps differently than in the HashSet. We can only use this for the bulk boolean checks. + /// + /// Determines counts that can be used to determine equality, subset, and superset. This + /// is only used when other is an IEnumerable and not a HashSet. If other is a HashSet + /// these properties can be checked faster without use of marking because we can assume + /// other has no duplicates. + /// + /// The following count checks are performed by callers: + /// 1. Equals: checks if unfoundCount = 0 and uniqueFoundCount = Count; i.e. everything + /// in other is in this and everything in this is in other + /// 2. Subset: checks if unfoundCount >= 0 and uniqueFoundCount = Count; i.e. other may + /// have elements not in this and everything in this is in other + /// 3. Proper subset: checks if unfoundCount > 0 and uniqueFoundCount = Count; i.e + /// other must have at least one element not in this and everything in this is in other + /// 4. Proper superset: checks if unfound count = 0 and uniqueFoundCount strictly less + /// than Count; i.e. everything in other was in this and this had at least one element + /// not contained in other. + /// + /// An earlier implementation used delegates to perform these checks rather than returning + /// an ElementCount struct; however this was changed due to the perf overhead of delegates. + /// + /// + /// Allows us to finish faster for equals and proper superset + /// because unfoundCount must be 0. + /// + // + // + // + // + // + // + [System.Security.SecurityCritical] + private unsafe ElementCount CheckUniqueAndUnfoundElements(IEnumerable other, bool returnIfUnfound) + { + ElementCount result; + + // need special case in case this has no elements. + if (Count == 0) + { + int numElementsInOther = 0; + foreach (T item in other) + { + numElementsInOther++; + // break right away, all we want to know is whether other has 0 or 1 elements + break; + } + result.uniqueCount = 0; + result.unfoundCount = numElementsInOther; + return result; + } + + + int originalLastIndex = Count; + int intArrayLength = BitHelper.ToIntArrayLength(originalLastIndex); + + BitHelper bitHelper; + if (intArrayLength <= StackAllocThreshold) + { + int* bitArrayPtr = stackalloc int[intArrayLength]; + bitHelper = new BitHelper(bitArrayPtr, intArrayLength); + } + else + { + int[] bitArray = new int[intArrayLength]; + bitHelper = new BitHelper(bitArray, intArrayLength); + } + + // count of items in other not found in this + int unfoundCount = 0; + // count of unique items in other found in this + int uniqueFoundCount = 0; + + foreach (T item in other) + { + int index = InternalIndexOf(item); + if (index >= 0) + { + if (!bitHelper.IsMarked(index)) + { + // item hasn't been seen yet + bitHelper.MarkBit(index); + uniqueFoundCount++; + } + } + else + { + unfoundCount++; + if (returnIfUnfound) + { + break; + } + } + } + + result.uniqueCount = uniqueFoundCount; + result.unfoundCount = unfoundCount; + return result; + } + public int RemoveWhere(Predicate match) + { + if (match == null) + { + throw new ArgumentNullException("match"); + } + List matches = new List(this.Count); + + BreadthFirstTreeWalk(delegate (Node n) + { + if (match(n.Item)) + { + matches.Add(n.Item); + } + return true; + }); + // reverse breadth first to (try to) incur low cost + int actuallyRemoved = 0; + for (int i = matches.Count - 1; i >= 0; i--) + { + if (this.Remove(matches[i])) + { + actuallyRemoved++; + } + } + + return actuallyRemoved; + + } + + + #endregion + + #region ISorted Members + + + public T Min + { + get + { + T ret = default(T); + InOrderTreeWalk(delegate (SortedSet.Node n) { ret = n.Item; return false; }); + return ret; + } + } + + public T Max + { + get + { + T ret = default(T); + InOrderTreeWalk(delegate (SortedSet.Node n) { ret = n.Item; return false; }, true); + return ret; + } + } + + public IEnumerable Reverse() + { + Enumerator e = new Enumerator(this, true); + while (e.MoveNext()) + { + yield return e.Current; + } + } + + + /// + /// Returns a subset of this tree ranging from values lBound to uBound + /// Any changes made to the subset reflect in the actual tree + /// + /// Lowest Value allowed in the subset + /// Highest Value allowed in the subset + public virtual SortedSet GetViewBetween(T lowerValue, T upperValue) + { + if (Comparer.Compare(lowerValue, upperValue) > 0) + { + throw new ArgumentException("lowerBound is greater than upperBound"); + } + return new TreeSubSet(this, lowerValue, upperValue, true, true); + } + +#if DEBUG + + /// + /// debug status to be checked whenever any operation is called + /// + /// + internal virtual bool versionUpToDate() + { + return true; + } +#endif + + + /// + /// This class represents a subset view into the tree. Any changes to this view + /// are reflected in the actual tree. Uses the Comparator of the underlying tree. + /// + /// +#if !FEATURE_NETCORE + [Serializable] + internal sealed class TreeSubSet : SortedSet, ISerializable, IDeserializationCallback + { +#else + internal sealed class TreeSubSet : SortedSet { +#endif + SortedSet underlying; + T min, max; + //these exist for unbounded collections + //for instance, you could allow this subset to be defined for i>10. The set will throw if + //anything <=10 is added, but there is no upperbound. These features Head(), Tail(), were punted + //in the spec, and are not available, but the framework is there to make them available at some point. + bool lBoundActive, uBoundActive; + //used to see if the count is out of date + + +#if DEBUG + internal override bool versionUpToDate() + { + return (this.version == underlying.version); + } +#endif + + public TreeSubSet(SortedSet Underlying, T Min, T Max, bool lowerBoundActive, bool upperBoundActive) + : base(Underlying.Comparer) + { + underlying = Underlying; + min = Min; + max = Max; + lBoundActive = lowerBoundActive; + uBoundActive = upperBoundActive; + root = underlying.FindRange(min, max, lBoundActive, uBoundActive); // root is first element within range + count = 0; + version = -1; + VersionCheckImpl(); + } + +#if !FEATURE_NETCORE + /// + /// For serialization and deserialization + /// + private TreeSubSet() + { + comparer = null; + } + + + [SuppressMessage("Microsoft.Usage", "CA2236:CallBaseClassMethodsOnISerializableTypes", Justification = "special case TreeSubSet serialization")] + private TreeSubSet(SerializationInfo info, StreamingContext context) + { + siInfo = info; + OnDeserializationImpl(info); + } +#endif // !FEATURE_NETCORE + + /// + /// Additions to this tree need to be added to the underlying tree as well + /// + + internal override bool AddIfNotPresent(T item) + { + + if (!IsWithinRange(item)) + { + ThrowHelper.ThrowArgumentOutOfRangeException(ExceptionArgument.collection); + } + + bool ret = underlying.AddIfNotPresent(item); + VersionCheck(); +#if DEBUG + Debug.Assert(this.versionUpToDate() && this.root == this.underlying.FindRange(min, max)); +#endif + + return ret; + } + + + public override bool Contains(T item) + { + VersionCheck(); +#if DEBUG + Debug.Assert(this.versionUpToDate() && this.root == this.underlying.FindRange(min, max)); +#endif + return base.Contains(item); + } + + internal override bool DoRemove(T item) + { // todo: uppercase this and others + + if (!IsWithinRange(item)) + { + return false; + } + + bool ret = underlying.Remove(item); + VersionCheck(); +#if DEBUG + Debug.Assert(this.versionUpToDate() && this.root == this.underlying.FindRange(min, max)); +#endif + return ret; + } + + public override void Clear() + { + + + if (count == 0) + { + return; + } + + List toRemove = new List(); + BreadthFirstTreeWalk(delegate (Node n) { toRemove.Add(n.Item); return true; }); + while (toRemove.Count != 0) + { + underlying.Remove(toRemove[toRemove.Count - 1]); + toRemove.RemoveAt(toRemove.Count - 1); + } + root = null; + count = 0; + version = underlying.version; + } + + + internal override bool IsWithinRange(T item) + { + + int comp = (lBoundActive ? Comparer.Compare(min, item) : -1); + if (comp > 0) + { + return false; + } + comp = (uBoundActive ? Comparer.Compare(max, item) : 1); + if (comp < 0) + { + return false; + } + return true; + } + + internal override bool InOrderTreeWalk(TreeWalkPredicate action, Boolean reverse) + { + VersionCheck(); + + if (root == null) + { + return true; + } + + // The maximum height of a red-black tree is 2*lg(n+1). + // See page 264 of "Introduction to algorithms" by Thomas H. Cormen + Stack stack = new Stack(2 * (int)SortedSet.log2(count + 1)); //this is not exactly right if count is out of date, but the stack can grow + Node current = root; + while (current != null) + { + if (IsWithinRange(current.Item)) + { + stack.Push(current); + current = (reverse ? current.Right : current.Left); + } + else if (lBoundActive && Comparer.Compare(min, current.Item) > 0) + { + current = current.Right; + } + else + { + current = current.Left; + } + } + + while (stack.Count != 0) + { + current = stack.Pop(); + if (!action(current)) + { + return false; + } + + Node node = (reverse ? current.Left : current.Right); + while (node != null) + { + if (IsWithinRange(node.Item)) + { + stack.Push(node); + node = (reverse ? node.Right : node.Left); + } + else if (lBoundActive && Comparer.Compare(min, node.Item) > 0) + { + node = node.Right; + } + else + { + node = node.Left; + } + } + } + return true; + } + + internal override bool BreadthFirstTreeWalk(TreeWalkPredicate action) + { + VersionCheck(); + + if (root == null) + { + return true; + } + + List processQueue = new List(); + processQueue.Add(root); + Node current; + + while (processQueue.Count != 0) + { + current = processQueue[0]; + processQueue.RemoveAt(0); + if (IsWithinRange(current.Item) && !action(current)) + { + return false; + } + if (current.Left != null && (!lBoundActive || Comparer.Compare(min, current.Item) < 0)) + { + processQueue.Add(current.Left); + } + if (current.Right != null && (!uBoundActive || Comparer.Compare(max, current.Item) > 0)) + { + processQueue.Add(current.Right); + } + + } + return true; + } + + internal override SortedSet.Node FindNode(T item) + { + + if (!IsWithinRange(item)) + { + return null; + } + VersionCheck(); +#if DEBUG + Debug.Assert(this.versionUpToDate() && this.root == this.underlying.FindRange(min, max)); +#endif + return base.FindNode(item); + } + + //this does indexing in an inefficient way compared to the actual sortedset, but it saves a + //lot of space + internal override int InternalIndexOf(T item) + { + int count = -1; + foreach (T i in this) + { + count++; + if (Comparer.Compare(item, i) == 0) + return count; + } +#if DEBUG + Debug.Assert(this.versionUpToDate() && this.root == this.underlying.FindRange(min, max)); +#endif + return -1; + } + /// + /// checks whether this subset is out of date. updates if necessary. + /// + internal override void VersionCheck() + { + VersionCheckImpl(); + } + + private void VersionCheckImpl() + { + Debug.Assert(underlying != null, "Underlying set no longer exists"); + if (this.version != underlying.version) + { + this.root = underlying.FindRange(min, max, lBoundActive, uBoundActive); + this.version = underlying.version; + count = 0; + InOrderTreeWalk(delegate (Node n) { count++; return true; }); + } + } + + + + //This passes functionality down to the underlying tree, clipping edges if necessary + //There's nothing gained by having a nested subset. May as well draw it from the base + //Cannot increase the bounds of the subset, can only decrease it + public override SortedSet GetViewBetween(T lowerValue, T upperValue) + { + + if (lBoundActive && Comparer.Compare(min, lowerValue) > 0) + { + //lBound = min; + throw new ArgumentOutOfRangeException("lowerValue"); + } + if (uBoundActive && Comparer.Compare(max, upperValue) < 0) + { + //uBound = max; + throw new ArgumentOutOfRangeException("upperValue"); + } + TreeSubSet ret = (TreeSubSet)underlying.GetViewBetween(lowerValue, upperValue); + return ret; + } + + internal override void IntersectWithEnumerable(IEnumerable other) + { + + List toSave = new List(this.Count); + foreach (T item in other) + { + if (this.Contains(item)) + { + toSave.Add(item); + this.Remove(item); + } + } + this.Clear(); + this.AddAllElements(toSave); +#if DEBUG + Debug.Assert(this.versionUpToDate() && this.root == this.underlying.FindRange(min, max)); +#endif + } + +#if !FEATURE_NETCORE + void ISerializable.GetObjectData(SerializationInfo info, StreamingContext context) + { + GetObjectData(info, context); + } + + protected override void GetObjectData(SerializationInfo info, StreamingContext context) + { + if (info == null) + { + ThrowHelper.ThrowArgumentNullException(ExceptionArgument.info); + } + info.AddValue(maxName, max, typeof(T)); + info.AddValue(minName, min, typeof(T)); + info.AddValue(lBoundActiveName, lBoundActive); + info.AddValue(uBoundActiveName, uBoundActive); + base.GetObjectData(info, context); + } + + void IDeserializationCallback.OnDeserialization(Object sender) + { + //don't do anything here as its already been done by the constructor + //OnDeserialization(sender); + + } + + protected override void OnDeserialization(Object sender) + { + OnDeserializationImpl(sender); + } + + private void OnDeserializationImpl(Object sender) + { + if (siInfo == null) + { + ThrowHelper.ThrowSerializationException(ExceptionResource.Serialization_InvalidOnDeser); + } + + comparer = (IComparer)siInfo.GetValue(ComparerName, typeof(IComparer)); + int savedCount = siInfo.GetInt32(CountName); + max = (T)siInfo.GetValue(maxName, typeof(T)); + min = (T)siInfo.GetValue(minName, typeof(T)); + lBoundActive = siInfo.GetBoolean(lBoundActiveName); + uBoundActive = siInfo.GetBoolean(uBoundActiveName); + underlying = new SortedSet(); + + if (savedCount != 0) + { + T[] items = (T[])siInfo.GetValue(ItemsName, typeof(T[])); + + if (items == null) + { + ThrowHelper.ThrowSerializationException(ExceptionResource.Serialization_MissingValues); + } + + for (int i = 0; i < items.Length; i++) + { + underlying.Add(items[i]); + } + } + underlying.version = siInfo.GetInt32(VersionName); + count = underlying.count; + version = underlying.version - 1; + VersionCheck(); //this should update the count to be right and update root to be right + + if (count != savedCount) + { + ThrowHelper.ThrowSerializationException(ExceptionResource.Serialization_MismatchedCount); + } + siInfo = null; + + } +#endif // !FEATURE_NETCORE + + + + + } + + + #endregion + + #region Serialization methods + +#if !FEATURE_NETCORE + // LinkDemand here is unnecessary as this is a methodimpl and linkdemand from the interface should suffice + void ISerializable.GetObjectData(SerializationInfo info, StreamingContext context) + { + GetObjectData(info, context); + } + + protected virtual void GetObjectData(SerializationInfo info, StreamingContext context) + { + if (info == null) + { + ThrowHelper.ThrowArgumentNullException(ExceptionArgument.info); + } + + info.AddValue(CountName, count); //This is the length of the bucket array. + info.AddValue(ComparerName, comparer, typeof(IComparer)); + info.AddValue(VersionName, version); + + if (root != null) + { + T[] items = new T[Count]; + CopyTo(items, 0); + info.AddValue(ItemsName, items, typeof(T[])); + } + } + + void IDeserializationCallback.OnDeserialization(Object sender) + { + OnDeserialization(sender); + } + + protected virtual void OnDeserialization(Object sender) + { + if (comparer != null) + { + return; //Somebody had a dependency on this class and fixed us up before the ObjectManager got to it. + } + + if (siInfo == null) + { + ThrowHelper.ThrowSerializationException(ExceptionResource.Serialization_InvalidOnDeser); + } + + comparer = (IComparer)siInfo.GetValue(ComparerName, typeof(IComparer)); + int savedCount = siInfo.GetInt32(CountName); + + if (savedCount != 0) + { + T[] items = (T[])siInfo.GetValue(ItemsName, typeof(T[])); + + if (items == null) + { + ThrowHelper.ThrowSerializationException(ExceptionResource.Serialization_MissingValues); + } + + for (int i = 0; i < items.Length; i++) + { + Add(items[i]); + } + } + + version = siInfo.GetInt32(VersionName); + if (count != savedCount) + { + ThrowHelper.ThrowSerializationException(ExceptionResource.Serialization_MismatchedCount); + } + siInfo = null; + } +#endif //!FEATURE_NETCORE + #endregion + + #region Helper Classes + internal class Node + { + public bool IsRed; + public T Item; + public Node Left; + public Node Right; + + public Node(T item) + { + // The default color will be red, we never need to create a black node directly. + this.Item = item; + IsRed = true; + } + + public Node(T item, bool isRed) + { + // The default color will be red, we never need to create a black node directly. + this.Item = item; + this.IsRed = isRed; + } + } + + [SuppressMessage("Microsoft.Performance", "CA1815:OverrideEqualsAndOperatorEqualsOnValueTypes", Justification = "not an expected scenario")] +#if !FEATURE_NETCORE + [Serializable] + public struct Enumerator : IEnumerator, IEnumerator, ISerializable, IDeserializationCallback + { +#else + public struct Enumerator : IEnumerator, IEnumerator { +#endif + private SortedSet tree; + private int version; + + + private Stack.Node> stack; + private SortedSet.Node current; + static SortedSet.Node dummyNode = new SortedSet.Node(default(T)); + + private bool reverse; + +#if !FEATURE_NETCORE + private SerializationInfo siInfo; +#endif + internal Enumerator(SortedSet set) + { + tree = set; + //this is a hack to make sure that the underlying subset has not been changed since + // + tree.VersionCheck(); + + version = tree.version; + + // 2lg(n + 1) is the maximum height + stack = new Stack.Node>(2 * (int)SortedSet.log2(set.Count + 1)); + current = null; + reverse = false; +#if !FEATURE_NETCORE + siInfo = null; +#endif + Intialize(); + } + + internal Enumerator(SortedSet set, bool reverse) + { + tree = set; + //this is a hack to make sure that the underlying subset has not been changed since + // + tree.VersionCheck(); + version = tree.version; + + // 2lg(n + 1) is the maximum height + stack = new Stack.Node>(2 * (int)SortedSet.log2(set.Count + 1)); + current = null; + this.reverse = reverse; +#if !FEATURE_NETCORE + siInfo = null; +#endif + Intialize(); + + } + +#if !FEATURE_NETCORE + private Enumerator(SerializationInfo info, StreamingContext context) + { + tree = null; + version = -1; + current = null; + reverse = false; + stack = null; + this.siInfo = info; + } + + void ISerializable.GetObjectData(SerializationInfo info, StreamingContext context) + { + GetObjectData(info, context); + } + + private void GetObjectData(SerializationInfo info, StreamingContext context) + { + if (info == null) + { + ThrowHelper.ThrowArgumentNullException(ExceptionArgument.info); + + } + info.AddValue(TreeName, tree, typeof(SortedSet)); + info.AddValue(EnumVersionName, version); + info.AddValue(ReverseName, reverse); + info.AddValue(EnumStartName, !NotStartedOrEnded); + info.AddValue(NodeValueName, (current == null ? dummyNode.Item : current.Item), typeof(T)); + } + + void IDeserializationCallback.OnDeserialization(Object sender) + { + OnDeserialization(sender); + } + + private void OnDeserialization(Object sender) + { + if (siInfo == null) + { + ThrowHelper.ThrowSerializationException(ExceptionResource.Serialization_InvalidOnDeser); + } + + tree = (SortedSet)siInfo.GetValue(TreeName, typeof(SortedSet)); + version = siInfo.GetInt32(EnumVersionName); + reverse = siInfo.GetBoolean(ReverseName); + bool EnumStarted = siInfo.GetBoolean(EnumStartName); + stack = new Stack.Node>(2 * (int)SortedSet.log2(tree.Count + 1)); + current = null; + if (EnumStarted) + { + T item = (T)siInfo.GetValue(NodeValueName, typeof(T)); + Intialize(); + //go until it reaches the value we want + while (this.MoveNext()) + { + if (tree.Comparer.Compare(this.Current, item) == 0) + break; + } + } + + + } +#endif //!FEATURE_NETCORE + + + private void Intialize() + { + + + current = null; + SortedSet.Node node = tree.root; + Node next = null, other = null; + while (node != null) + { + next = (reverse ? node.Right : node.Left); + other = (reverse ? node.Left : node.Right); + if (tree.IsWithinRange(node.Item)) + { + stack.Push(node); + node = next; + } + else if (next == null || !tree.IsWithinRange(next.Item)) + { + node = other; + } + else + { + node = next; + } + } + } + + public bool MoveNext() + { + + //this is a hack to make sure that the underlying subset has not been changed since + // + tree.VersionCheck(); + + if (version != tree.version) + { + ThrowHelper.ThrowInvalidOperationException(ExceptionResource.InvalidOperation_EnumFailedVersion); + } + + if (stack.Count == 0) + { + current = null; + return false; + } + + current = stack.Pop(); + SortedSet.Node node = (reverse ? current.Left : current.Right); + + Node next = null, other = null; + while (node != null) + { + next = (reverse ? node.Right : node.Left); + other = (reverse ? node.Left : node.Right); + if (tree.IsWithinRange(node.Item)) + { + stack.Push(node); + node = next; + } + else if (other == null || !tree.IsWithinRange(other.Item)) + { + node = next; + } + else + { + node = other; + } + } + return true; + } + + public void Dispose() + { + } + + public T Current + { + get + { + if (current != null) + { + return current.Item; + } + return default(T); + } + } + + object IEnumerator.Current + { + get + { + if (current == null) + { + ThrowHelper.ThrowInvalidOperationException(ExceptionResource.InvalidOperation_EnumOpCantHappen); + } + + return current.Item; + } + } + + internal bool NotStartedOrEnded + { + get + { + return current == null; + } + } + + internal void Reset() + { + if (version != tree.version) + { + ThrowHelper.ThrowInvalidOperationException(ExceptionResource.InvalidOperation_EnumFailedVersion); + } + + stack.Clear(); + Intialize(); + } + + void IEnumerator.Reset() + { + Reset(); + } + + + + + } + + + + internal struct ElementCount + { + internal int uniqueCount; + internal int unfoundCount; + } + #endregion + + #region misc + + /// + /// Searches the set for a given value and returns the equal value it finds, if any. + /// + /// The value to search for. + /// The value from the set that the search found, or the default value of when the search yielded no match. + /// A value indicating whether the search was successful. + /// + /// This can be useful when you want to reuse a previously stored reference instead of + /// a newly constructed one (so that more sharing of references can occur) or to look up + /// a value that has more complete data than the value you currently have, although their + /// comparer functions indicate they are equal. + /// + public bool TryGetValue(T equalValue, out T actualValue) + { + Node node = FindNode(equalValue); + if (node != null) + { + actualValue = node.Item; + return true; + } + actualValue = default(T); + return false; + } + + // used for set checking operations (using enumerables) that rely on counting + private static int log2(int value) + { + //Contract.Requires(value>0) + int c = 0; + while (value > 0) + { + c++; + value >>= 1; + } + return c; + } + #endregion + + + } + + /// + /// A class that generates an IEqualityComparer for this SortedSet. Requires that the definition of + /// equality defined by the IComparer for this SortedSet be consistent with the default IEqualityComparer + /// for the type T. If not, such an IEqualityComparer should be provided through the constructor. + /// + internal class SortedSetEqualityComparer : IEqualityComparer> + { + private IComparer comparer; + private IEqualityComparer e_comparer; + + public SortedSetEqualityComparer() : this(null, null) { } + + public SortedSetEqualityComparer(IComparer comparer) : this(comparer, null) { } + + public SortedSetEqualityComparer(IEqualityComparer memberEqualityComparer) : this(null, memberEqualityComparer) { } + + /// + /// Create a new SetEqualityComparer, given a comparer for member order and another for member equality (these + /// must be consistent in their definition of equality) + /// + public SortedSetEqualityComparer(IComparer comparer, IEqualityComparer memberEqualityComparer) + { + if (comparer == null) + this.comparer = Comparer.Default; + else + this.comparer = comparer; + if (memberEqualityComparer == null) + e_comparer = EqualityComparer.Default; + else + e_comparer = memberEqualityComparer; + } + + + // using comparer to keep equals properties in tact; don't want to choose one of the comparers + public bool Equals(SortedSet x, SortedSet y) + { + return SortedSet.SortedSetEquals(x, y, comparer); + } + //IMPORTANT: this part uses the fact that GetHashCode() is consistent with the notion of equality in + //the set + public int GetHashCode(SortedSet obj) + { + int hashCode = 0; + if (obj != null) + { + foreach (T t in obj) + { + hashCode = hashCode ^ (e_comparer.GetHashCode(t) & 0x7FFFFFFF); + } + } // else returns hashcode of 0 for null HashSets + return hashCode; + } + + // Equals method for the comparer itself. + public override bool Equals(Object obj) + { + SortedSetEqualityComparer comparer = obj as SortedSetEqualityComparer; + if (comparer == null) + { + return false; + } + return (this.comparer == comparer.comparer); + } + + public override int GetHashCode() + { + return comparer.GetHashCode() ^ e_comparer.GetHashCode(); + } + + + } + +} + + + +#pragma warning restore CS8604 // 引用类型参数可能为 null。 +#pragma warning restore CS8602 // 解引用可能出现空引用。 +#pragma warning restore CS8600 // 将 null 字面量或可能为 null 的值转换为非 null 类型。 +#pragma warning restore IDE0059 // 不需要赋值 +#pragma warning restore CS8625 // 无法将 null 字面量转换为非 null 的引用类型。 +#pragma warning restore CS8618 // 在退出构造函数时,不可为 null 的字段必须包含非 null 值。请考虑声明为可以为 null。 +#pragma warning restore CS8601 // 引用类型赋值可能为 null。 +#pragma warning restore CS8603 // 可能返回 null 引用。 +#endif \ No newline at end of file diff --git a/src/IFoxCAD.Basal/Sortedset/ThrowHelper.cs b/src/IFoxCAD.Basal/Sortedset/ThrowHelper.cs new file mode 100644 index 0000000..205dd1c --- /dev/null +++ b/src/IFoxCAD.Basal/Sortedset/ThrowHelper.cs @@ -0,0 +1,382 @@ +#if NET35 +#pragma warning disable IDE0059 // 不需要赋值 +#pragma warning disable CS8600 // 将 null 字面量或可能为 null 的值转换为非 null 类型。 + +namespace System +{ + // This file defines an internal class used to throw exceptions in BCL code. + // The main purpose is to reduce code size. + // + // The old way to throw an exception generates quite a lot IL code and assembly code. + // Following is an example: + // C# source + // throw new ArgumentNullException("key", SR.GetString("ArgumentNull_Key")); + // IL code: + // IL_0003: ldstr "key" + // IL_0008: ldstr "ArgumentNull_Key" + // IL_000d: call string System.Environment::GetResourceString(string) + // IL_0012: newobj instance void System.ArgumentNullException::.ctor(string,string) + // IL_0017: throw + // which is 21bytes in IL. + // + // So we want to get rid of the ldstr and call to Environment.GetResource in IL. + // In order to do that, I created two enums: ExceptionResource, ExceptionArgument to represent the + // argument name and resource name in a small integer. The source code will be changed to + // ThrowHelper.ThrowArgumentNullException(ExceptionArgument.key, ExceptionResource.ArgumentNull_Key); + // + // The IL code will be 7 bytes. + // IL_0008: ldc.i4.4 + // IL_0009: ldc.i4.4 + // IL_000a: call void System.ThrowHelper::ThrowArgumentNullException(valuetype System.ExceptionArgument) + // IL_000f: ldarg.0 + // + // This will also reduce the Jitted code size a lot. + // + // It is very important we do this for generic classes because we can easily generate the same code + // multiple times for different instantiation. + // + // < + + + + + + + + + + +#if !SILVERLIGHT + using System.Runtime.Serialization; +#endif + + using System.Diagnostics; + internal static class ThrowHelper + { + internal static void ThrowWrongKeyTypeArgumentException(object key, Type targetType) + { + throw new ArgumentException(SR.GetString("SR.Arg_WrongType", key, targetType), "key"); + } + + internal static void ThrowWrongValueTypeArgumentException(object value, Type targetType) + { + throw new ArgumentException(SR.GetString("SR.Arg_WrongType", value, targetType), "value"); + } + + internal static void ThrowKeyNotFoundException() + { + throw new System.Collections.Generic.KeyNotFoundException(); + } + + internal static void ThrowArgumentException(ExceptionResource resource) + { + throw new ArgumentException(SR.GetString(GetResourceName(resource))); + } + + internal static void ThrowArgumentNullException(ExceptionArgument argument) + { + throw new ArgumentNullException(GetArgumentName(argument)); + } + + internal static void ThrowArgumentOutOfRangeException(ExceptionArgument argument) + { + throw new ArgumentOutOfRangeException(GetArgumentName(argument)); + } + + internal static void ThrowArgumentOutOfRangeException(ExceptionArgument argument, ExceptionResource resource) + { + throw new ArgumentOutOfRangeException(GetArgumentName(argument), SR.GetString(GetResourceName(resource))); + } + + internal static void ThrowInvalidOperationException(ExceptionResource resource) + { + throw new InvalidOperationException(SR.GetString(GetResourceName(resource))); + } + +#if !SILVERLIGHT + internal static void ThrowSerializationException(ExceptionResource resource) + { + throw new SerializationException(SR.GetString(GetResourceName(resource))); + } +#endif + + internal static void ThrowNotSupportedException(ExceptionResource resource) + { + throw new NotSupportedException(SR.GetString(GetResourceName(resource))); + } + + // Allow nulls for reference types and Nullable, but not for value types. + internal static void IfNullAndNullsAreIllegalThenThrow(object value, ExceptionArgument argName) + { + // Note that default(T) is not equal to null for value types except when T is Nullable. + if (value == null && !(default(T) == null)) + ThrowHelper.ThrowArgumentNullException(argName); + } + + // + // This function will convert an ExceptionArgument enum value to the argument name string. + // + internal static string GetArgumentName(ExceptionArgument argument) + { + string argumentName = null; + + switch (argument) + { + case ExceptionArgument.array: + argumentName = "array"; + break; + + case ExceptionArgument.arrayIndex: + argumentName = "arrayIndex"; + break; + + case ExceptionArgument.capacity: + argumentName = "capacity"; + break; + + case ExceptionArgument.collection: + argumentName = "collection"; + break; + + case ExceptionArgument.converter: + argumentName = "converter"; + break; + + case ExceptionArgument.count: + argumentName = "count"; + break; + + case ExceptionArgument.dictionary: + argumentName = "dictionary"; + break; + + case ExceptionArgument.index: + argumentName = "index"; + break; + + case ExceptionArgument.info: + argumentName = "info"; + break; + + case ExceptionArgument.key: + argumentName = "key"; + break; + + case ExceptionArgument.match: + argumentName = "match"; + break; + + case ExceptionArgument.obj: + argumentName = "obj"; + break; + + case ExceptionArgument.queue: + argumentName = "queue"; + break; + + case ExceptionArgument.stack: + argumentName = "stack"; + break; + + case ExceptionArgument.startIndex: + argumentName = "startIndex"; + break; + + case ExceptionArgument.value: + argumentName = "value"; + break; + + case ExceptionArgument.item: + argumentName = "item"; + break; + + default: + Debug.Assert(false, "The enum value is not defined, please checked ExceptionArgumentName Enum."); + return string.Empty; + } + + return argumentName; + } + + // + // This function will convert an ExceptionResource enum value to the resource string. + // + internal static string GetResourceName(ExceptionResource resource) + { + string resourceName = null; + + switch (resource) + { + case ExceptionResource.Argument_ImplementIComparable: + resourceName = "SR.Argument_ImplementIComparable"; + break; + + case ExceptionResource.Argument_AddingDuplicate: + resourceName = "SR.Argument_AddingDuplicate"; + break; + + case ExceptionResource.ArgumentOutOfRange_Index: + resourceName = "SR.ArgumentOutOfRange_Index"; + break; + + case ExceptionResource.ArgumentOutOfRange_NeedNonNegNum: + resourceName = "SR.ArgumentOutOfRange_NeedNonNegNum"; + break; + + case ExceptionResource.ArgumentOutOfRange_NeedNonNegNumRequired: + resourceName = "SR.ArgumentOutOfRange_NeedNonNegNumRequired"; + break; + + case ExceptionResource.ArgumentOutOfRange_SmallCapacity: + resourceName = "SR.ArgumentOutOfRange_SmallCapacity"; + break; + + case ExceptionResource.Arg_ArrayPlusOffTooSmall: + resourceName = "SR.Arg_ArrayPlusOffTooSmall"; + break; + + case ExceptionResource.Arg_RankMultiDimNotSupported: + resourceName = "SR.Arg_MultiRank"; + break; + + case ExceptionResource.Arg_NonZeroLowerBound: + resourceName = "SR.Arg_NonZeroLowerBound"; + break; + + case ExceptionResource.Argument_InvalidArrayType: + resourceName = "SR.Invalid_Array_Type"; + break; + + case ExceptionResource.Argument_InvalidOffLen: + resourceName = "SR.Argument_InvalidOffLen"; + break; + + case ExceptionResource.InvalidOperation_CannotRemoveFromStackOrQueue: + resourceName = "SR.InvalidOperation_CannotRemoveFromStackOrQueue"; + break; + + case ExceptionResource.InvalidOperation_EmptyCollection: + resourceName = "SR.InvalidOperation_EmptyCollection"; + break; + + case ExceptionResource.InvalidOperation_EmptyQueue: + resourceName = "SR.InvalidOperation_EmptyQueue"; + break; + + case ExceptionResource.InvalidOperation_EnumOpCantHappen: + resourceName = "SR.InvalidOperation_EnumOpCantHappen"; + break; + + case ExceptionResource.InvalidOperation_EnumFailedVersion: + resourceName = "SR.InvalidOperation_EnumFailedVersion"; + break; + + case ExceptionResource.InvalidOperation_EmptyStack: + resourceName = "SR.InvalidOperation_EmptyStack"; + break; + + case ExceptionResource.InvalidOperation_EnumNotStarted: + resourceName = "SR.InvalidOperation_EnumNotStarted"; + break; + + case ExceptionResource.InvalidOperation_EnumEnded: + resourceName = "SR.InvalidOperation_EnumEnded"; + break; + + case ExceptionResource.NotSupported_KeyCollectionSet: + resourceName = "SR.NotSupported_KeyCollectionSet"; + break; + + case ExceptionResource.NotSupported_SortedListNestedWrite: + resourceName = "SR.NotSupported_SortedListNestedWrite"; + break; + +#if !SILVERLIGHT + case ExceptionResource.Serialization_InvalidOnDeser: + resourceName = "SR.Serialization_InvalidOnDeser"; + break; + + case ExceptionResource.Serialization_MissingValues: + resourceName = "SR.Serialization_MissingValues"; + break; + + case ExceptionResource.Serialization_MismatchedCount: + resourceName = "SR.Serialization_MismatchedCount"; + break; +#endif + + case ExceptionResource.NotSupported_ValueCollectionSet: + resourceName = "SR.NotSupported_ValueCollectionSet"; + break; + + default: + Debug.Assert(false, "The enum value is not defined, please checked ExceptionArgumentName Enum."); + return string.Empty; + } + + return resourceName; + } + + } + + // + // The convention for this enum is using the argument name as the enum name + // + internal enum ExceptionArgument + { + obj, + dictionary, + array, + info, + key, + collection, + match, + converter, + queue, + stack, + capacity, + index, + startIndex, + value, + count, + arrayIndex, + item, + } + + // + // The convention for this enum is using the resource name as the enum name + // + internal enum ExceptionResource + { + Argument_ImplementIComparable, + ArgumentOutOfRange_NeedNonNegNum, + ArgumentOutOfRange_NeedNonNegNumRequired, + Arg_ArrayPlusOffTooSmall, + Argument_AddingDuplicate, + Serialization_InvalidOnDeser, + Serialization_MismatchedCount, + Serialization_MissingValues, + Arg_RankMultiDimNotSupported, + Arg_NonZeroLowerBound, + Argument_InvalidArrayType, + NotSupported_KeyCollectionSet, + ArgumentOutOfRange_SmallCapacity, + ArgumentOutOfRange_Index, + Argument_InvalidOffLen, + NotSupported_ReadOnlyCollection, + InvalidOperation_CannotRemoveFromStackOrQueue, + InvalidOperation_EmptyCollection, + InvalidOperation_EmptyQueue, + InvalidOperation_EnumOpCantHappen, + InvalidOperation_EnumFailedVersion, + InvalidOperation_EmptyStack, + InvalidOperation_EnumNotStarted, + InvalidOperation_EnumEnded, + NotSupported_SortedListNestedWrite, + NotSupported_ValueCollectionSet, + } +} + +#pragma warning restore CS8600 // 将 null 字面量或可能为 null 的值转换为非 null 类型。 +#pragma warning restore IDE0059 // 不需要赋值 +#endif \ No newline at end of file diff --git a/src/IFoxCAD.Basal/Sortedset/bithelper.cs b/src/IFoxCAD.Basal/Sortedset/bithelper.cs new file mode 100644 index 0000000..d68c4d8 --- /dev/null +++ b/src/IFoxCAD.Basal/Sortedset/bithelper.cs @@ -0,0 +1,173 @@ +#if NET35 +#pragma warning disable CS8603 // 可能返回 null 引用。 +#pragma warning disable CS8618 // 在退出构造函数时,不可为 null 的字段必须包含非 null 值。请考虑声明为可以为 null。 + + +using System; +using System.Collections; +using System.Text; + +namespace System.Collections.Generic +{ + + /// + /// ABOUT: + /// Helps with operations that rely on bit marking to indicate whether an item in the + /// collection should be added, removed, visited already, etc. + /// + /// BitHelper doesn't allocate the array; you must pass in an array or ints allocated on the + /// stack or heap. ToIntArrayLength() tells you the int array size you must allocate. + /// + /// USAGE: + /// Suppose you need to represent a bit array of length (i.e. logical bit array length) + /// BIT_ARRAY_LENGTH. Then this is the suggested way to instantiate BitHelper: + /// *************************************************************************** + /// int intArrayLength = BitHelper.ToIntArrayLength(BIT_ARRAY_LENGTH); + /// BitHelper bitHelper; + /// if (intArrayLength less than stack alloc threshold) + /// int* m_arrayPtr = stackalloc int[intArrayLength]; + /// bitHelper = new BitHelper(m_arrayPtr, intArrayLength); + /// else + /// int[] m_arrayPtr = new int[intArrayLength]; + /// bitHelper = new BitHelper(m_arrayPtr, intArrayLength); + /// *************************************************************************** + /// + /// IMPORTANT: + /// The second ctor args, length, should be specified as the length of the int array, not + /// the logical bit array. Because length is used for bounds checking into the int array, + /// it's especially important to get this correct for the stackalloc version. See the code + /// samples above; this is the value gotten from ToIntArrayLength(). + /// + /// The length ctor argument is the only exception; for other methods -- MarkBit and + /// IsMarked -- pass in values as indices into the logical bit array, and it will be mapped + /// to the position within the array of ints. + /// + /// + + + + + unsafe internal class BitHelper + { // should not be serialized + + private const byte MarkedBitFlag = 1; + private const byte IntSize = 32; + + // m_length of underlying int array (not logical bit array) + private int m_length; + + // ptr to stack alloc'd array of ints + [System.Security.SecurityCritical] + private int* m_arrayPtr; + + // array of ints + private int[] m_array; + + // whether to operate on stack alloc'd or heap alloc'd array + private bool useStackAlloc; + + /// + /// Instantiates a BitHelper with a heap alloc'd array of ints + /// + /// int array to hold bits + /// length of int array + // + // + // + // + [System.Security.SecurityCritical] + internal BitHelper(int* bitArrayPtr, int length) + { + this.m_arrayPtr = bitArrayPtr; + this.m_length = length; + useStackAlloc = true; + } + + /// + /// Instantiates a BitHelper with a heap alloc'd array of ints + /// + /// int array to hold bits + /// length of int array + internal BitHelper(int[] bitArray, int length) + { + this.m_array = bitArray; + this.m_length = length; + } + + /// + /// Mark bit at specified position + /// + /// + // + // + // + [System.Security.SecurityCritical] + internal unsafe void MarkBit(int bitPosition) + { + if (useStackAlloc) + { + int bitArrayIndex = bitPosition / IntSize; + if (bitArrayIndex < m_length && bitArrayIndex >= 0) + { + m_arrayPtr[bitArrayIndex] |= (MarkedBitFlag << (bitPosition % IntSize)); + } + } + else + { + int bitArrayIndex = bitPosition / IntSize; + if (bitArrayIndex < m_length && bitArrayIndex >= 0) + { + m_array[bitArrayIndex] |= (MarkedBitFlag << (bitPosition % IntSize)); + } + } + } + + /// + /// Is bit at specified position marked? + /// + /// + /// + // + // + // + [System.Security.SecurityCritical] + internal unsafe bool IsMarked(int bitPosition) + { + if (useStackAlloc) + { + int bitArrayIndex = bitPosition / IntSize; + if (bitArrayIndex < m_length && bitArrayIndex >= 0) + { + return ((m_arrayPtr[bitArrayIndex] & (MarkedBitFlag << (bitPosition % IntSize))) != 0); + } + return false; + } + else + { + int bitArrayIndex = bitPosition / IntSize; + if (bitArrayIndex < m_length && bitArrayIndex >= 0) + { + return ((m_array[bitArrayIndex] & (MarkedBitFlag << (bitPosition % IntSize))) != 0); + } + return false; + } + } + + /// + /// How many ints must be allocated to represent n bits. Returns (n+31)/32, but + /// avoids overflow + /// + /// + /// + internal static int ToIntArrayLength(int n) + { + return n > 0 ? ((n - 1) / IntSize + 1) : 0; + } + + } +} + +#pragma warning restore CS8618 // 在退出构造函数时,不可为 null 的字段必须包含非 null 值。请考虑声明为可以为 null。 +#pragma warning restore CS8603 // 可能返回 null 引用。 + +#endif \ No newline at end of file diff --git a/src/IFoxCAD.Cad/Algorithms/Graph/Graph.cs b/src/IFoxCAD.Cad/Algorithms/Graph/Graph.cs new file mode 100644 index 0000000..ad1efb5 --- /dev/null +++ b/src/IFoxCAD.Cad/Algorithms/Graph/Graph.cs @@ -0,0 +1,647 @@ +namespace IFoxCAD.Cad; +using Exception = System.Exception; + +/// +/// 无权无向图实现 +/// IEnumerable 枚举所有顶点; +/// +public sealed class Graph : IGraph, IEnumerable +{ + #region 字段及属性 + /// + /// 存储所有节点的字典,key为顶点的类型,value为邻接表,类型是hashset,不可重复添加点 + /// + /// + readonly Dictionary> vertices = new(); + /// + /// 邻接边表,key为顶点的类型,value为邻接边表,类型是hashset,不可重复添加边 + /// + readonly Dictionary> edges = new(); + /// + /// 为加快索引,引入hash检索 + /// + readonly Dictionary vertexs = new(); + + public int VerticesCount => vertices.Count; + + /// + /// Returns a reference vertex. + /// Time complexity: O(1). + /// + private IGraphVertex? ReferenceVertex + { + get + { + using (var enumerator = vertexs.GetEnumerator()) + { + if (enumerator.MoveNext()) + { + return enumerator.Current.Value; + } + } + + return null; + } + } + IGraphVertex? IGraph.ReferenceVertex => ReferenceVertex; + /// + /// 目前点增加点的顺序号,这个点号不随删点而减少的 + /// + private int insertCount; + #endregion + + #region 构造函数 + public Graph() + { + insertCount = 0; // 每次新建对象就将顶点顺序号归零 + } + #endregion + + #region 顶点及边_增 + /// + /// 向该图添加一个新顶点,但是无边; + /// + /// 点 + /// 创建的顶点 + public IGraphVertex AddVertex(Point3d pt) + { + var str = pt.GetHashString(); + if (vertexs.ContainsKey(str)) + return vertexs[str]; + + var vertex = new GraphVertex(pt, insertCount++); + vertices.Add(vertex, new HashSet()); + edges.Add(vertex, new HashSet()); + + vertexs[str] = vertex; + + return vertex; + } + + /// + /// 向该图添加一个边; + /// + /// + public void AddEdge(Curve3d curve!!) + { + var start = AddVertex(curve.StartPoint); + var end = AddVertex(curve.EndPoint); + + // 添加起点的邻接表和邻接边 + vertices[start].Add(end); + edges[start].Add(new GraphEdge(end, curve)); + + // 为了保证点顺序,每个点的邻接边必须按起点-终点,所以添加曲线终点时,将添加一个方向的曲线 + var curtmp = (Curve3d)curve.Clone(); + curtmp = curtmp.GetReverseParameterCurve(); + + // 添加终点的邻接表和邻接边 + vertices[end].Add(start); + edges[end].Add(new GraphEdge(start, curtmp)); + } + #endregion + + #region 顶点及边_删 + /// + /// 从此图中删除现有顶点; + /// + /// 点 + public void RemoveVertex(Point3d pt) + { + var str = pt.GetHashString(); + if (vertexs.ContainsKey(str)) + { + var vertex = vertexs[str]; + + // 删除邻接表里的vertex点,先删除后面的遍历可以少一轮 + vertices.Remove(vertex!); + + // 删除其他顶点的邻接表里的vertex点 + foreach (var item in vertices.Values) + item.Remove(vertex!); + + // 删除邻接边表里的vertex点,先删除后面的遍历可以少一轮 + edges.Remove(vertex!); + + // 删除其他顶点的邻接边表的指向vertex的边 + foreach (var item in edges.Values) + { + item.RemoveWhere(x => vertex.Equals(x.TargetVertex)); + //foreach (var edge in item) + //{ + // if (vertex.Equals(edge.TargetVertex)) + // item.Remove(edge); + //} + } + vertexs.Remove(str); + } + } + + /// + /// 从此图中删除一条边; + /// + /// 曲线 + public void RemoveEdge(Curve3d curve!!) + { + RemoveVertex(curve.StartPoint); + RemoveVertex(curve.EndPoint); + } + #endregion + + #region 顶点和边_查 + /// + /// 我们在给定的来源和目的地之间是否有边? + /// + /// 起点 + /// 终点 + /// 有边返回 ,反之返回 + public bool HasEdge(IGraphVertex source, IGraphVertex dest) + { + if (!vertices.ContainsKey(source) || !vertices.ContainsKey(dest)) + throw new ArgumentException("源或目标不在此图中;"); + + foreach (var item in edges[source]) + { + if (item.TargetVertex == dest) + return true; + } + return false; + } + + /// + /// 获取边 + /// + /// 起点 + /// 终点 + /// + /// 传入的点不在图中时抛出参数异常 + public IEdge? GetEdge(IGraphVertex source, IGraphVertex dest) + { + if (!vertices.ContainsKey(source) || !vertices.ContainsKey(dest)) + throw new ArgumentException("源或目标不在此图中;"); + + foreach (var item in edges[source]) + { + if (item.TargetVertex.Equals(dest)) + return item; + } + return null; + } + + /// + /// 是否存在顶点,此函数目前未发现有啥用 + /// + /// 顶点 + /// 存在顶点返回 ,反之返回 + public bool ContainsVertex(IGraphVertex value) + { + return vertices.ContainsKey(value); + } + #endregion + + #region 获取邻接表和曲线 + /// + /// 获取顶点的邻接表 + /// + /// 顶点 + /// 邻接表 + public HashSet GetAdjacencyList(IGraphVertex vertex) + { + return vertices[vertex]; + } + + /// + /// 获取顶点的邻接边表 + /// + /// 顶点 + /// 邻接边表 + public HashSet GetAdjacencyEdge(IGraphVertex vertex) + { + return edges[vertex]; + } + + /// + /// 根据顶点表获取曲线集合 + /// + /// 顶点表 + /// 曲线表 + public List GetCurves(List graphVertices) + { + var curves = new List(); + for (int i = 0; i < graphVertices.Count - 1; i++) + { + var cur = graphVertices[i]; + var next = graphVertices[i + 1]; + var edge = GetEdge(cur, next); + if (edge is not null) + curves.Add(edge.TargetEdge); + } + var lastedge = GetEdge(graphVertices[^1], graphVertices[0]); + if (lastedge is not null) + curves.Add(lastedge.TargetEdge); + + return curves; + } + #endregion + + #region 克隆及接口实现 + /// + /// 克隆此图;目测是深克隆 + /// + public Graph Clone() + { + var newGraph = new Graph(); + + foreach (var vertex in edges.Values) + foreach (var item in vertex) + newGraph.AddEdge(item.TargetEdge); + + return newGraph; + } + + IGraph IGraph.Clone() + { + return Clone(); + } + + public IEnumerator GetEnumerator() + { + return VerticesAsEnumberable.GetEnumerator(); + } + + IEnumerator? IEnumerable.GetEnumerator() + { + return GetEnumerator() as IEnumerator; + } + + public IEnumerable VerticesAsEnumberable => + vertices.Select(x => x.Key); + #endregion + + #region 方法 + /// + /// 输出点的邻接表的可读字符串 + /// + /// + public string ToReadable() + { + int i = 1; + string output = string.Empty; + foreach (var node in vertices) + { + var adjacents = string.Empty; + + output = string.Format("{1}\r\n{0}-{2}: [", i, output, node.Key.Data.ToString()); + + foreach (var adjacentNode in node.Value) + adjacents = string.Format("{0}{1},", adjacents, adjacentNode.Data.ToString()); + + if (adjacents.Length > 0) + adjacents = adjacents.TrimEnd(new char[] { ',', ' ' }); + + output = string.Format("{0}{1}]", output, adjacents); + i++; + } + return output; + } + #endregion +} + + +/// +/// 邻接表图实现的顶点; +/// IEnumerable 枚举所有邻接点; +/// +public sealed class GraphVertex : IGraphVertex, IEquatable, IComparable, IComparable +{ + #region 属性 + public Point3d Data { get; set; } + public int Index { get; set; } + #endregion + + #region 构造 + /// + /// 邻接表图实现的顶点 + /// + /// 点 + /// 所在节点索引 + public GraphVertex(Point3d value, int index) + { + Data = value; + Index = index; + } + #endregion + + #region 重载运算符_比较 + public bool Equals(IGraphVertex other) + { + return Index == other.Index; + } + + public override bool Equals(object obj) + { + if (obj is null) + return false; + if (obj is not IGraphVertex vertex) + return false; + else + return Equals(vertex); + } + + public override int GetHashCode() + { + return Index; + } + + public int CompareTo(IGraphVertex other) + { + if (Equals(other)) + return 0; + + if (Index < other.Index) + return -1; + else + return 1; + } + + int IComparable.CompareTo(IGraphVertex other) + { + return CompareTo(other); + } + + public int CompareTo(object obj) + { + if (obj is null) + return 1; + + try + { + var other = (GraphVertex)obj; + return CompareTo(other); + } + catch (Exception) + { + throw new ArgumentException("Object is not a IGraphVertex"); + } + } + + public static bool operator ==(GraphVertex person1, GraphVertex person2) + { + if (person1 is null || person2 is null) + return Equals(person1, person2); + + return person1.Equals(person2); + } + + public static bool operator !=(GraphVertex person1, GraphVertex person2) + { + if (person1 is null || person2 is null) + return !Equals(person1, person2); + + return !person1.Equals(person2); + } + #endregion +} + + +/// +/// 无向图中边的定义 +/// +public sealed class GraphEdge : IEdge, IEquatable +{ + #region 属性 + public IGraphVertex TargetVertex { get; set; } + public Curve3d TargetEdge { get; set; } + #endregion + + #region 构造 + /// + /// 无向图中边的定义 + /// + /// 下一点 + /// 下一点之间的曲线 + public GraphEdge(IGraphVertex target, Curve3d edge) + { + TargetVertex = target; + TargetEdge = edge; + } + #endregion + + #region 重载运算符_比较 + public bool Equals(GraphEdge other) + { + if (other is null) + return false; + return TargetVertex == other.TargetVertex && + TargetEdge == other.TargetEdge; + } + public override bool Equals(object obj) + { + if (obj is null) + return false; + if (obj is not GraphEdge personObj) + return false; + else + return Equals(personObj); + } + + public override int GetHashCode() + { + return (TargetVertex.GetHashCode(), TargetEdge.GetHashCode()).GetHashCode(); + } + public static bool operator ==(GraphEdge person1, GraphEdge person2) + { + if (person1 is null || person2 is null) + return Equals(person1, person2); + + return person1.Equals(person2); + } + public static bool operator !=(GraphEdge person1, GraphEdge person2) + { + if (person1 is null || person2 is null) + return !Equals(person1, person2); + + return !person1.Equals(person2); + } + #endregion +} + + +/// +/// 深度优先搜索; +/// +public sealed class DepthFirst +{ + #region 公共方法 +/// +/// 存储所有的边 +/// +#if true + public List> Curve3ds { get; } = new(); +#else + public List> Curve3ds { get; } = new(); +#endif + private HashSet Curved { get; } = new(); + + + /// + /// 找出所有的路径 + /// + /// 图 + public void FindAll(IGraph graph) + { + var total = new HashSet(); + //var graphtmp = graph.Clone(); + foreach (var item in graph.VerticesAsEnumberable) + { + Dfs(graph, new LinkedHashSet { item },total); + total.Add(item); + } + } +#endregion + +#region 内部方法 + /// + /// 递归 DFS; + /// + /// 图 + /// 已经遍历的路径 +#if true + void Dfs(IGraph graph, LinkedHashSet visited, HashSet totalVisited) + { + var adjlist = graph.GetAdjacencyList(/*startNode*/ visited.First!.Value); // O(1) + foreach (var nextNode in adjlist) // O(n) + { + if (totalVisited.Contains(nextNode)) + { + continue; + } + // 如果下一个点未遍历过 + if (!visited.Contains(nextNode)) // O(1) + { + // 将下一点加入路径集合,并进行下一次递归 + var sub = new LinkedHashSet { nextNode }; + sub.AddRange(visited); // O(n) + Dfs(graph, sub,totalVisited); + } + // 如果下一点遍历过,并且路径大于2,说明已经找到起点 + else if (visited.Count > 2 && nextNode.Equals(visited.Last!.Value)) + { + // 将重复的路径进行过滤,并把新的路径存入结果 + var curstr = Gethashstring(visited); // O(n) + if (Isnew(curstr)) // O(1) + { + Curve3ds.Add(visited); + Curved.Add(curstr.Item1); + } + } + } + } + + + + +#else + + void Dfs(IGraph graph, List visited) + { + var startNode = visited[0]; + IGraphVertex nextNode; + List sub; + + var adjlist = graph.GetAdjacencyList(startNode).ToList(); // O(n) + for (int i = 0; i < adjlist.Count; i++) // O(n) + { + nextNode = adjlist[i]; + + // 如果下一个点未遍历过 + if (!visited.Contains(nextNode)) // O(n) + { + // 将下一点加入路径集合,并进行下一次递归 + sub = new List { nextNode }; + sub.AddRange(visited); // O(n) + Dfs(graph, sub); + } + + // 如果下一点遍历过,并且路径大于2,说明已经找到起点 + else if (visited.Count > 2 && nextNode.Equals(visited[^1])) + { + // 将重复的路径进行过滤,并把新的路径存入结果 + var cur = RotateToSmallest(visited); // O(n) + var inv = Invert(cur,cur[0]); // O(n) + + var curstr = Gethashstring(cur,inv); + //Env.Print(curstr); + if (Isnew(curstr)) + { + Curve3ds.Add(cur); + Curved.Add(curstr.Item1); + } + } + } + } +#endif + + + + + + + + + /// + /// 将列表旋转到最小的值为列表起点 + /// + /// + /// + static List RotateToSmallest(List lst) + { + var index = lst.IndexOf(lst.Min()); + return lst.Skip(index).Concat(lst.Take(index)).ToList(); + } + + /// + /// 将列表反向,并旋转到起点为最小值 + /// + /// + /// + static List Invert(List lst, IGraphVertex vertex) + { + var tmp = lst.ToList(); + tmp.Reverse(); + var index = tmp.IndexOf(vertex); + return tmp.Skip(index).Concat(lst.Take(index)).ToList(); + } + + static (string,string) Gethashstring(List pathone, List pathtwo) + { + var one = new string[pathone.Count]; + var two = new string[pathtwo.Count]; + for (int i = 0; i < pathone.Count; i++) + { + one[i] = pathone[i].Index.ToString(); + two[i] = pathtwo[i].Index.ToString(); + } + return (string.Join("-", one), string.Join("-", two)); + } + + static (string, string) Gethashstring(LinkedHashSet path) + { + var one = new string[path.Count]; + var two = new string[path.Count]; + path.For(path.MinNode!, (i, ver1, ver2) => { + one[i] = ver1.Index.ToString(); + two[i] = ver2.Index.ToString(); + }); + return (string.Join("-", one), string.Join("-", two)); + } + + + bool Isnew((string,string) path) + { + return !Curved.Contains(path.Item1) && !Curved.Contains(path.Item2); + } + + +#endregion +} \ No newline at end of file diff --git a/src/IFoxCAD.Cad/Algorithms/Graph/IGraph.cs b/src/IFoxCAD.Cad/Algorithms/Graph/IGraph.cs new file mode 100644 index 0000000..73547b7 --- /dev/null +++ b/src/IFoxCAD.Cad/Algorithms/Graph/IGraph.cs @@ -0,0 +1,98 @@ +namespace IFoxCAD.Cad; + +/// +/// 无向图 +/// +public interface IGraph +{ + /// + /// 顶点的数量 + /// + /// + int VerticesCount { get; } + + /// + /// 是否存在顶点 + /// + /// 顶点键 + /// + bool ContainsVertex(IGraphVertex key); + + /// + /// 顶点的迭代器 + /// + /// + IEnumerable VerticesAsEnumberable { get; } + + /// + /// 是否有边 + /// + /// 源顶点 + /// 目的顶点 + /// + bool HasEdge(IGraphVertex source, IGraphVertex destination); + /// + /// 图克隆函数 + /// + /// + IGraph Clone(); + /// + /// 获取边 + /// + /// + /// + /// + IEdge? GetEdge(IGraphVertex source, IGraphVertex dest); + /// + /// 邻接表 + /// + /// + /// + HashSet GetAdjacencyList(IGraphVertex vertex); + /// + /// 邻接边表 + /// + /// + /// + HashSet GetAdjacencyEdge(IGraphVertex vertex); + IGraphVertex? ReferenceVertex { get; } + + void RemoveVertex(Point3d pt); + void RemoveEdge(Curve3d curve); + +} + +/// +/// 无向图顶点 +/// +/// 顶点数据类型 +public interface IGraphVertex : IComparable +{ + /// + /// 顶点的键 + /// + /// + int Index { get; set; } + + /// + /// 顶点的数据 + /// + Point3d Data { get; } +} +/// +/// 无向图边 +/// +public interface IEdge +{ + /// + /// 边 + /// + Curve3d TargetEdge { get; } + /// + /// 目标顶点 + /// + IGraphVertex TargetVertex { get; } +} + + + diff --git a/src/IFoxCAD.Cad/Algorithms/QuadTree/QuadEntity.cs b/src/IFoxCAD.Cad/Algorithms/QuadTree/QuadEntity.cs new file mode 100644 index 0000000..c5f7f11 --- /dev/null +++ b/src/IFoxCAD.Cad/Algorithms/QuadTree/QuadEntity.cs @@ -0,0 +1,25 @@ +namespace IFoxCAD.Cad; + +/* + * 这个类存在的意义是为了不暴露Rect类字段 + * 同时利用了Rect类字段的快速 + * 提供到外面去再继承 + */ + +/// +/// 四叉树图元 +/// +public class QuadEntity : Rect +{ + /// + /// 四叉树图元 + /// + /// 包围盒 + public QuadEntity(Rect box) + { + _X = box._X; + _Y = box._Y; + _Top = box._Top; + _Right = box._Right; + } +} \ No newline at end of file diff --git a/src/IFoxCAD.Cad/Algorithms/QuadTree/QuadTree.cs b/src/IFoxCAD.Cad/Algorithms/QuadTree/QuadTree.cs new file mode 100644 index 0000000..0eb1bf8 --- /dev/null +++ b/src/IFoxCAD.Cad/Algorithms/QuadTree/QuadTree.cs @@ -0,0 +1,259 @@ +/* + * 四叉树维基百科 http://en.wikipedia.org/wiki/Quadtree + * 四叉树是一种分区空间的算法,更快找出内部或外部给定区域. + * 通过一个正交矩形边界进行中心点分裂四个正交矩形, + * 插入时候会一直分裂四个正交矩形, + * 当分裂四个节点都无法单独拥有 图元包围盒 就停止分裂,并且你属于这四个节点的父亲. + * (不包含就是面积少了,就这么一句话看代码看半天), + * 还可以通过限制树的深度实现加速. + * + * 第一版: https://www.codeproject.com/script/Articles/ViewDownloads.aspx?aid=30535 + * + * 第二版: 找邻居 + * https://blog.csdn.net/dive_shallow/article/details/112438050 + * https://geidav.wordpress.com/2017/12/02/advanced-octrees-4-finding-neighbor-nodes/ + * + * 1.根节点:控制根节点从而控制所有节点 + * 2.子节点:包含自身根节点,插入矩形的时候进行递归分裂自身,和实现查找. + * 3.接口:约束都要有正交矩形,否则无法调用"包含"方法 + * 4.选择模式:模仿cad的窗选和框选 + */ +namespace IFoxCAD.Cad; + +/// +/// 根节点控制器 +/// +/// 类型接口约束必须有正交矩形 +public class QuadTree where TEntity : QuadEntity +{ + #region 成员 + /// + /// 根节点 + /// + QuadTreeNode _rootNode; + + /// + /// 四叉树节点的数目 + /// + public int Count { get => _rootNode.CountSubTree; } + + /// + /// 点容器(红黑树) + /// + SortedSet _points; + #endregion + + #region 构造 + /// + /// 四叉树根节点控制器 + /// + /// 四叉树矩形范围 + public QuadTree(Rect rect) + { + _rootNode = new QuadTreeNode(rect, null, 0);//初始化根节点 + _points = new(); + } + #endregion + + #region 方法 + /// + /// 通过根节点插入数据项 + /// + /// + public void Insert(TEntity ent) + { + /* + * 图元点 是不分裂空间的,加入一个红黑树内部. + */ + if (ent.IsPoint) + { + _points.Add(ent); + return; + } + + while (!_rootNode.Contains(ent)) + { + /* + * 四叉树插入时候,如果超出根边界,就需要扩展 + * 扩展时候有一个要求,当前边界要作为扩展边界的一个象限,也就是反向分裂 + * + * 创建新根,计算原根在新根的位置, + * 替换指针:获取新分裂的节点的父节点,判断它哪个儿子是它, + * 替换之后可能仍然不包含图元边界,再循环计算. + */ + var sq_Left = _rootNode._X; + var sq_Botton = _rootNode._Y; + var sq_Right = _rootNode._Right; + var sq_Top = _rootNode._Top; + if (ent._Y >= _rootNode._Y)//上↑增殖 + { + if (ent._X >= _rootNode._X) + { + //右上↗增殖 + sq_Right += _rootNode.Width; + sq_Top += _rootNode.Height; + } + else + { + //左上↖增殖 + sq_Left -= _rootNode.Width; + sq_Top += _rootNode.Height; + } + } + else//在下↓ + { + if (ent._X >= _rootNode._X) + { + //右下↘增殖 + sq_Right += _rootNode.Width; + sq_Botton -= _rootNode.Height; + } + else + { + //左下↙增殖 + sq_Left -= _rootNode.Width; + sq_Botton -= _rootNode.Height; + } + } + //扩大2次方 + var rectSquare = new Rect(sq_Left, sq_Botton, sq_Right, sq_Top); + + //四叉树的旧根要作为四分之一插入 + //新根中计算原根 + //把 旧根节点 连接到 新根节点 上面,然后新根成为根 + var newRoot = new QuadTreeNode(rectSquare, null, 0); + var insert = newRoot.Insert(_rootNode); + if (insert is null) + throw new("四叉树:新根尺寸不对"); + if (!insert.Equals(_rootNode)) + throw new("四叉树:新旧节点大小不一致,无法连接"); + + var insPar = insert.Parent; + _rootNode.Parent = insPar; + if (insPar is null) + return; + + if (_rootNode.Equals(insPar.RightTopTree)) + insPar.RightTopTree = _rootNode; + else if (_rootNode.Equals(insPar.RightBottomTree)) + insPar.RightBottomTree = _rootNode; + else if (_rootNode.Equals(insPar.LeftBottomTree)) + insPar.LeftBottomTree = _rootNode; + else if (_rootNode.Equals(insPar.LeftTopTree)) + insPar.LeftTopTree = _rootNode; + else + throw new("四叉树:新节点不对,无法连接"); + + //其后的子节点层数全部增加层数, + //要加多少层取决于当前根边界属于新根边界的所在层 + var depth = insert.Depth; + if (depth == 0) + throw new("四叉树:插入节点是0,造成错误"); + _rootNode.ForEach(node => { + node.Depth += depth; + return false; + }); + + //交换根控制 + _rootNode = newRoot; + } + + _rootNode.Insert(ent); + } + + + /// + /// 查询四叉树,返回给定区域的数据项 + /// + /// 矩形选区查询 + /// + public List Query(Rect rect, QuadTreeSelectMode selectMode = QuadTreeSelectMode.IntersectsWith) + { + QuadTreeEvn.SelectMode = selectMode; + + var results = new List(); + //选择图元 + _rootNode.Query(rect, results); + //选择点 + var ptge = _points.GetEnumerator(); + switch (selectMode) + { + case QuadTreeSelectMode.IntersectsWith: + case QuadTreeSelectMode.Contains: + /* 由于红黑树的方法 _points.GetViewBetween() + * 过滤只能过滤X区间,Y区间还是要过滤, + * 那么我就只能用这样的方法加速了 + * + * 而更好的方式是不用红黑树,去加入一个点云数据来进行,可谓是编程无极限.... + */ + while (ptge.MoveNext()) + { + var ptEnt = ptge.Current; + if (rect._X <= ptEnt._X && ptEnt._X <= rect._Right) + { + if (rect._Y <= ptEnt._Y && ptEnt._Y <= rect.Top) + results.Add(ptEnt); + } + else if (ptEnt._X > rect._Right) + break;//超过后面范围就break,因为红黑树已经排序 + } + break; + default: + throw new("四叉树:" + nameof(selectMode)); + } + return results; + } + + /// + /// 删除子节点 + /// + /// 根据范围删除 + public void Remove(Rect rect) + { + _rootNode.Remove(rect); + } + + /// + /// 删除子节点 + /// + /// 根据图元删除 + public void Remove(TEntity ent) + { + _rootNode.Remove(ent); + } + + /// + /// 找到附近节点图元 + /// + [Obsolete("找附近节点的并不是最近的图元")] + public TEntity? FindNeibor(Rect rect, QuadTreeFindMode findMode) + { + return _rootNode.FindNeibor(rect, findMode); + } + + /// + /// 找到附近图元 + /// + /// + /// + public TEntity? FindNearEntity(Rect rect) + { + return _rootNode.FindNearEntity(rect); + } + + /// + /// 执行四叉树中特定的行为 + /// + /// + public void ForEach(QTAction action) + { + _rootNode.ForEach(action); + } + + /// + /// 委托:四叉树节点上执行一个操作 + /// + /// + public delegate bool QTAction(QuadTreeNode obj); + #endregion +} \ No newline at end of file diff --git a/src/IFoxCAD.Cad/Algorithms/QuadTree/QuadTreeEvn.cs b/src/IFoxCAD.Cad/Algorithms/QuadTree/QuadTreeEvn.cs new file mode 100644 index 0000000..16d518b --- /dev/null +++ b/src/IFoxCAD.Cad/Algorithms/QuadTree/QuadTreeEvn.cs @@ -0,0 +1,26 @@ +#pragma warning disable CA2211 // 非常量字段应当不可见 +namespace IFoxCAD.Cad; + +public class QuadTreeEvn +{ + /// + /// 最小的节点有一个面积(一定要大于0) + /// + public static double MinArea = 1e-6; + + /// + /// 选择模式 + /// + public static QuadTreeSelectMode SelectMode; + + /// + /// 最大深度 + /// + public static int QuadTreeMaximumDepth = 2046; + + /// + /// 节点内容超过就分裂 + /// + public static int QuadTreeContentsCountSplit = 20; +} +#pragma warning restore CA2211 // 非常量字段应当不可见 \ No newline at end of file diff --git a/src/IFoxCAD.Cad/Algorithms/QuadTree/QuadTreeNode.cs b/src/IFoxCAD.Cad/Algorithms/QuadTree/QuadTreeNode.cs new file mode 100644 index 0000000..86cb5b4 --- /dev/null +++ b/src/IFoxCAD.Cad/Algorithms/QuadTree/QuadTreeNode.cs @@ -0,0 +1,818 @@ +namespace IFoxCAD.Cad; + +/// +/// 子节点 +/// +/// +public class QuadTreeNode + : Rect + where TEntity : QuadEntity +{ + #region 成员 + /// + /// 子节点:第一象限:右上↗ + /// + public QuadTreeNode? RightTopTree; + /// + /// 子节点:第二象限:左上↖ + /// + public QuadTreeNode? LeftTopTree; + /// + /// 子节点:第三象限:左下↙ + /// + public QuadTreeNode? LeftBottomTree; + /// + /// 子节点:第四象限:右下↘ + /// + public QuadTreeNode? RightBottomTree; + /// + /// 所有子节点 + /// + QuadTreeNode[] Nodes + { + get + { + return new QuadTreeNode[] + { + RightTopTree!, + LeftTopTree!, + LeftBottomTree!, + RightBottomTree!, + }; + } + } + /// + /// 所有子节点是空的 + /// + bool NodesIsEmpty => RightTopTree is null && LeftTopTree is null && LeftBottomTree is null && RightBottomTree is null; + + /// + /// 父节点 + /// + public QuadTreeNode? Parent; + /// + /// 节点的在四叉树的深度 + /// + public int Depth; + + // 注意,内容没有限制:这不是 impement 四叉树的标准方法 + /// (节点图元是交叉线压着的,并不是矩形范围内全部,因为这是四叉树的特性决定) + /// + /// 本节点:内容 + /// + public List Contents; + + /// + /// 本节点和旗下所有子节点:内容群 + /// + public void ContentsSubTree(List results) + { + if (Contents is null) + return; + results.AddRange(Contents); + var nodes = Nodes; + for (int i = 0; i < nodes.Length; i++) + nodes[i]?.ContentsSubTree(results); + } + + /// + /// 本节点和旗下所有子节点:内容群数量 + /// + public int CountSubTree + { + get + { + if (Contents is null) + return 0; + int count = Contents.Count; + + var nodes = Nodes; + for (int i = 0; i < nodes.Length; i++) + { + var node = nodes[i]; + if (node is null) + continue; + count += node.CountSubTree; + } + return count; + } + } + #endregion + + #region 构造 + /// + /// 四叉树节点 + /// + /// 当前节点边界 + /// 父节点 + /// 节点深度 + public QuadTreeNode(Rect box, QuadTreeNode? parent, int depth) + { + _X = box._X; + _Y = box._Y; + _Right = box._Right; + _Top = box._Top; + + Parent = parent; + Depth = depth; + Contents = new(); + } + #endregion + + #region 增 + /// + /// 将原有节点插入用 + /// + /// + internal QuadTreeNode? Insert(Rect rect) + { + if (!Contains(rect)) + return null; + + //四叉树分裂,将当前节点分为四个子节点 + if (NodesIsEmpty) + CreateChildren(); + + //当前节点边界 包含 图元包围盒 就插入 + //退出递归:4个节点都不完全包含 + //4个节点的上层 + var nodes = Nodes; + for (int i = 0; i < nodes.Length; i++) + { + var node = nodes[i]; + if (node is null) + continue; + + if (node.Equals(rect)) + { + rect = node; + return node.Insert(rect); + } + } + return this; + } + + /// + /// 将数据项递归插入四叉树 + /// + /// + public QuadTreeNode? Insert(TEntity ent) + { + if (!Contains(ent)) + { + //Debug.WriteLine("不在四叉树边界范围"); + //Trace.WriteLine("不在四叉树边界范围"); + return null; + } + + // if (ent.IsPoint) + // { + // //找到最后一层包含它的节点,然后加入它 + // //因此是跳过分裂矩形的,以免造成无限递归 + // var minNode = GetMinNode(ent); + // minNode.Contents.Add(ent); + // return minNode; + // } + +#if true2 + //方案二: + //内容数超过才分裂,防止树深度过高,但是多选过滤时候慢一点 + if (Contents.Count > QuadTreeEvn.QuadTreeContentsCountSplit) + { + //分裂出四个子节点 + if (_nodesIsEmpty) + { + CreateChildren(); + //分裂之后将当前层的内容扔到四个子节点, + //如果被压着,那么就不会扔到下面 + for (int i = Contents.Count - 1; i >= 0; i--) + { + var minNode = GetMinNode(Contents[i].Box); + minNode.Contents.Add(Contents[i]); + Contents.RemoveAt(i); + } + } + else + { + //没有分裂的话,就递归 + //退出递归:4个节点都不完全包含,内容就是他们的父亲 + var nodes = _Nodes; + for (int i = 0; i < nodes.Length; i++) + { + var node = nodes[i]; + if (node is null) + continue; + + //这里需要中断.(匿名方法ForEach无法中断,会造成父节点加入内容) + if (node.Contains(ent)) + return node.Insert(ent); + } + } + } +#else + //方案一:分裂到最细节点 + + //分裂出四个子节点 + if (NodesIsEmpty) + CreateChildren(); + + //4个子节点开始递归 + //退出递归:4个节点都不完全包含,内容就是他们的父亲 + var nodes = Nodes; + for (int i = 0; i < nodes.Length; i++) + { + var node = nodes[i]; + if (node is null) + continue; + //这里需要中断.(匿名方法ForEach无法中断,会造成父节点加入内容) + if (node.Contains(ent)) + return node.Insert(ent); + } +#endif + + //为什么要用容器? + //相同包围盒或者四叉树分割线压着多个. + this.Contents.Add(ent); + return this; + } + + /// + /// 创建子节点 + /// + void CreateChildren() + { + // 最小面积控制节点深度,但是这样可能导致树分成高,引起爆栈 + if (Depth > QuadTreeEvn.QuadTreeMaximumDepth) + return; + var recs = RectSplit(this); + var de = Depth + 1; + RightTopTree = new QuadTreeNode(recs[0], this, de); + LeftTopTree = new QuadTreeNode(recs[1], this, de); + LeftBottomTree = new QuadTreeNode(recs[2], this, de); + RightBottomTree = new QuadTreeNode(recs[3], this, de); + } + + /// + /// 矩形分裂为四个 + /// + /// + /// + Rect[] RectSplit(Rect box) + { + var halfWidth = box.Width / 2.0; + var halfHeight = box.Height / 2.0; + + var upperRight = new Rect(box._X + halfWidth, box._Y + halfHeight, box._Right, box._Top); + var upperLeft = new Rect(box._X, box._Y + halfHeight, box._Right - halfWidth, box._Top); + var lowerleft = new Rect(box._X, box._Y, box._Right - halfWidth, box._Top - halfHeight);//基础 + var lowerRight = new Rect(box._X + halfWidth, box._Y, box._Right, box._Top - halfHeight); + + //依照象限顺序输出 + return new Rect[] { upperRight, upperLeft, lowerleft, lowerRight }; + } + #endregion + + #region 删 + /// + /// 删除图元 + /// + /// 根据图元删除 + public bool Remove(TEntity easeEnt) + { + //通过图元id删除无疑是非常低效的, + //1.相当于在所有的容器查找它,但是移除只会移除一次, + // 因此必须要求图元只会加入一次,才能中断检索剩余分支. + //2.这个代价还是太高,因此我们还是要默认条件,图元载入一次之后,不再改动. + //3.不再改动也不太合理,因为cad图元还是可以修改的 + + //1.处理内容 + if (Contents.Remove(easeEnt)) + { + if (CountSubTree == 0) + this.Clear(this); + return true; + } + + //2.递归子节点移除 + var nodes = Nodes; + for (int i = 0; i < nodes.Length; i++) + { + var node = nodes[i]; + if (node is null) + continue; + if (node.Remove(easeEnt)) //递归进入子节点删除内容 + return true; //删除成功就中断其他节点的搜索 + } + return false; + } + + /// + /// 递归进入最下层节点,然后开始清理 + /// + /// + void Clear(QuadTreeNode node) + { + var nodes = Nodes; + for (int i = 0; i < nodes.Length; i++) + nodes[i]?.Clear(nodes[i]); + + node.Contents.Clear(); + //node.Contents = null;//重复加入时候会出错 + node.RightTopTree = null; + node.LeftTopTree = null; + node.LeftBottomTree = null; + node.RightBottomTree = null; + node.Parent = null; + //node.Box = zoreRect; + } + + /// + /// 删除子节点内容 + /// + /// 根据范围删除 + public void Remove(Rect queryArea) + { + //本节点内容移除 + if (Contents is not null && Contents.Count > 0)//从最上层的根节点开始进入 + { + for (int i = Contents.Count - 1; i >= 0; i--) + { + var ent = Contents[i]; + //移除之后,如果容器是0,那么这里不能直接 Contents=null, + //因为此节点下面可能还有节点, + //需要判断了其后数量0才可以清理. + //否则其后还有内容,那么此节点就是仍然可以用的. + if (queryArea.Contains(ent)) + Contents.Remove(ent); + } + } + + //同插入一样 + //跳到指定节点再搜索这个节点下面的图元 + var nodes = Nodes; + for (int i = 0; i < nodes.Length; i++) + { + var node = nodes[i]; + if (node is null) + continue; + if (node.NodesIsEmpty) + continue; + + //此节点边界 完全包含 查询区域,则转到该节点,并跳过其余节点(打断此循环) + if (node.Contains(queryArea)) + { + node.Remove(queryArea); + break; + } + //查询区域 完全包含 此节点边界,提取此节点全部内容 + //跳过分析碰撞,并继续循环搜索其他节点 + if (queryArea.Contains(node)) + { + node.Clear(node); + continue; + } + //查询区域 与 此节点四边形边线碰撞 查询该四边形中,并继续循环搜索其他节点 + //1,角点碰撞 2,边界碰撞 + if (node.IntersectsWith(queryArea)) + node.Remove(queryArea); + } + + //本节点内容移除之后,旗下还有内容的话, + //会跳过此处,再进入子节点进行递归,直到最后一个节点 + if (CountSubTree == 0) + Clear(this); + } + #endregion + + #region 查 + /// + /// 查询范围内的实体 + /// + /// 查询矩形 + /// + public void Query(Rect queryArea, List results) + { + GetCurrentContents(queryArea, results); + + //遍历子节点 + var nodes = Nodes; + for (int i = 0; i < nodes.Length; i++) + { + var node = nodes[i]; + if (node is null) + continue; + //子节点的4个子节点都是空的, + //那么表示元素会在子节点这一层啊... + if (node.NodesIsEmpty) + continue; + + //此节点边界 完全包含 查询区域,则转到该节点,并跳过其余节点(打断此循环) + if (node.Contains(queryArea)) + { + node.Query(queryArea, results); + break; + } + //查询区域 完全包含 此节点边界,提取此节点全部内容 + //跳过分析碰撞,并继续循环搜索其他节点 + if (queryArea.Contains(node)) + { + node.ContentsSubTree(results); + continue; + } + //查询区域 与 此节点四边形边线碰撞 查询该四边形中,并继续循环搜索其他节点 + //1,角点碰撞 2,边界碰撞 + if (node.IntersectsWith(queryArea)) + node.Query(queryArea, results); + } + } + + /// + /// 获取本节点内容 + /// + /// + /// + void GetCurrentContents(Rect queryArea, List results) + { + //遍历当前节点内容,加入方式取决于碰撞模式 + if (QuadTreeEvn.SelectMode == QuadTreeSelectMode.IntersectsWith) + { + for (int i = 0; i < Contents.Count; i++) + { + var ent = Contents[i]; + if (queryArea.IntersectsWith(ent)) + results.Add(ent); + } + } + else + { + for (int i = 0; i < Contents.Count; i++) + { + var ent = Contents[i]; + if (queryArea.Contains(ent)) + results.Add(ent); + } + } + } + + /// + /// 找临近图元 + /// + /// 查找矩形 + /// + public TEntity? FindNearEntity(Rect queryArea) + { + TEntity? resultEntity = default; + //1.找到 查找矩形 所在的节点,利用此节点的矩形. + var queryNode = GetMinNode(queryArea); + var queryAreaCenter = queryArea.CenterPoint; + + //2.从根开始搜索 + // 如果搜索父亲的父亲的...内容群,它不是距离最近的,只是节点(亲属关系)最近 + // 储存找过的<图元,距离> + var entDic = new Dictionary(); + + var old = QuadTreeEvn.SelectMode; + QuadTreeEvn.SelectMode = QuadTreeSelectMode.IntersectsWith; + while (true) + { + //循环找父节点大小 + var hw = queryNode.Width / 2.0; + var hh = queryNode.Height / 2.0; + //3.利用选区中心扩展一个节点边界大小的矩形.从而选择图元 + // 再判断图元的与目标的距离,找到最小距离,即为最近 + var minPt = new Point2d(queryAreaCenter.X - hw, queryAreaCenter.Y - hh); + var maxPt = new Point2d(queryAreaCenter.X + hw, queryAreaCenter.Y + hh); + var ents = new List(); + Query(new Rect(minPt, maxPt), ents); + for (int i = 0; i < ents.Count; i++) + { + var ent = ents[i]; + if (entDic.ContainsKey(ent)) + continue; + var dis = ent.CenterPoint.GetDistanceTo(queryAreaCenter); + if (dis > 1e-6)//剔除本身 + entDic.Add(ent, dis); + } + if (entDic.Count > 0) + { + resultEntity = entDic.OrderBy(a => a.Value).First().Key; + break; + } + if (queryNode.Parent is null)//最顶层就退出 + break; + queryNode = queryNode.Parent;//利用父节点矩形进行变大选区 + } + QuadTreeEvn.SelectMode = old; + return resultEntity; + } + + /// + /// 找临近节点的图元 + /// + /// 查找矩形 + /// 查找什么方向 + /// + [Obsolete("找附近节点的并不是最近的图元")] + public TEntity? FindNeibor(Rect queryArea, QuadTreeFindMode findMode) + { + TEntity? resultEntity = default; + //1.找到 查找矩形 所在的节点,利用此节点的矩形. + //2.利用节点矩形是分裂的特点,边和边必然贴合. + //3.找到方向 findMode 拥有的节点,然后查找节点的内容 + var queryNode = GetMinNode(queryArea); + + bool whileFlag = true; + //同一个节点可能包含邻居,因为四叉树的加入是图元压线, + //那么就在这里搜就得了,用中心点决定空间位置 + //但是本空间的图元可能都比它矮,无法满足条件 + if (queryNode.CountSubTree > 1) + { + resultEntity = GetNearestNeighbor(queryNode, findMode, queryArea); + if (resultEntity is null || resultEntity.CenterPoint == queryArea.CenterPoint) + whileFlag = true; + else + whileFlag = false; + } + + while (whileFlag) + { + //同一个父节点是临近的,优先获取 兄弟节点 的内容. + //循环了第二次是北方兄弟的节点, + //但是这不是一个找到临近图元的方法, + //因为临近的可能是父亲的父亲的父亲...另一个函数 FindNearEntity 写 + //本方案也仅仅作为找北方节点 + var parent = queryNode.Parent; + if (parent is not null) + { + switch (findMode) + { + case QuadTreeFindMode.Top: + { + //下格才获取上格,否则导致做了无用功,上格就直接获取邻居了 + if (parent.LeftBottomTree == queryNode || parent.RightBottomTree == queryNode) + resultEntity = GetNearestNeighbor(parent, findMode, queryArea); + } + break; + case QuadTreeFindMode.Bottom: + { + if (parent.LeftTopTree == queryNode || parent.RightTopTree == queryNode) + resultEntity = GetNearestNeighbor(parent, findMode, queryArea); + } + break; + case QuadTreeFindMode.Left: + { + if (parent.RightTopTree == queryNode || parent.RightBottomTree == queryNode) + resultEntity = GetNearestNeighbor(parent, findMode, queryArea); + } + break; + case QuadTreeFindMode.Right: + { + if (parent.LeftTopTree == queryNode || parent.LeftBottomTree == queryNode) + resultEntity = GetNearestNeighbor(parent, findMode, queryArea); + } + break; + } + } + if (resultEntity is not null) + break; + + //通过 所在节点 找 邻居节点, + //拿到 邻居节点 下面的所有内容(图元) + //内容可能是空的,再从邻居那往北找...如果找到了四叉树最外层,仍然没有内容,退出循环 + var neiborNode = FindNeiborNode(queryNode, findMode); + if (neiborNode is null) + continue; + if (neiborNode.CountSubTree > 0) + { + resultEntity = GetNearestNeighbor(neiborNode, findMode, queryArea); + break; + } + if (neiborNode.Parent is null)//如果找到了四叉树最外层,仍然没有内容,退出循环 + break; + queryNode = neiborNode; + } + + return resultEntity; + } + + /// + /// 查找节点的(本内容和子内容)与(查找面积)矩形中点对比,找到最近一个内容 + /// + /// 查找面积 + /// 查找方向 + /// 查找节点 + /// + TEntity? GetNearestNeighbor(QuadTreeNode queryNode, QuadTreeFindMode findMode, Rect queryArea) + { + TEntity? results = default; + + var lst = new List(); + var qcent = queryArea.CenterPoint; + + switch (findMode) + { + case QuadTreeFindMode.Top: + { + //取出Y比queryArea的还大的一个,是最近的那个 + var qy = qcent.Y; + queryNode.ContentsSubTree(lst); + lst.ForEach(ent => + { + if (ent.CenterPoint.Y > qy) + lst.Add(ent); + }); + lst = lst.OrderBy(ent => ent.CenterPoint.Y).ToList(); + } + break; + case QuadTreeFindMode.Bottom: + { + var qy = qcent.Y; + queryNode.ContentsSubTree(lst); + lst.ForEach(ent => + { + if (ent.CenterPoint.Y < qy) + lst.Add(ent); + }); + lst = lst.OrderByDescending(ent => ent.CenterPoint.Y).ToList(); + } + break; + case QuadTreeFindMode.Left: + { + var qx = qcent.Y; + queryNode.ContentsSubTree(lst); + lst.ForEach(ent => + { + if (ent.CenterPoint.X > qx) + lst.Add(ent); + }); + lst = lst.OrderBy(ent => ent.CenterPoint.X).ToList(); + } + break; + case QuadTreeFindMode.Right: + { + var qx = qcent.Y; + queryNode.ContentsSubTree(lst); + lst.ForEach(ent => + { + if (ent.CenterPoint.X < qx) + lst.Add(ent); + }); + lst = lst.OrderByDescending(ent => ent.CenterPoint.X).ToList(); + } + break; + } + + if (lst.Count > 0) + return lst[0];//可能就是本体重叠 + return results; + } + + /// + /// 找包含它的最小分支 + /// + /// 查询的矩形 + /// 节点 + QuadTreeNode GetMinNode(Rect queryArea) + { + var nodes = Nodes; + for (int i = 0; i < nodes.Length; i++) + { + var node = nodes[i]; + if (node is null) + continue; + + //边界包含查询面积,那么再递归查询, + //直到最后四个都不包含,那么上一个就是图元所在节点 + if (node.Contains(queryArea)) + return node.GetMinNode(queryArea);//中断后面的范围,才可以返回正确的 + } + return this; + } + + /// + /// 四叉树找邻居节点(相同或更大) + /// + /// 源节点 + /// 方向 + /// + QuadTreeNode? FindNeiborNode(QuadTreeNode tar, QuadTreeFindMode findMode) + { + var parent = tar.Parent; + if (parent is null) + return null; + switch (findMode) + { + case QuadTreeFindMode.Top: + { + //判断当前节点在父节点的位置,如果是在 下格 就取对应的 上格 + if (tar == parent.LeftBottomTree) + return parent.LeftTopTree; + if (tar == parent.RightBottomTree) + return parent.RightTopTree; + //否则就是上格 + //找父节点的北方邻居..也就是在爷节点上面找 + //递归,此时必然是 下格,就必然返回 上格,然后退出递归 + var parentNeibor = FindNeiborNode(parent, QuadTreeFindMode.Top); + //父节点的北方邻居 无 子节点 + if (parentNeibor is null || parentNeibor.RightTopTree is null) + return parentNeibor;//返回父节点的北方邻居,比较大 + //父节点的北方邻居 有 子节点,剩下条件就只有这两 + + // 如果直接返回,那么是(相同或更大), + // 而找邻近图元需要的是这个(相同或更大)下面的图元,在外面对这个格子内图元用坐标分析就好了 + if (tar == parent.LeftTopTree) + return parentNeibor.LeftBottomTree; + return parentNeibor.RightBottomTree; + } + case QuadTreeFindMode.Bottom: + { + if (tar == parent.LeftTopTree) + return parent.LeftBottomTree; + if (tar == parent.RightTopTree) + return parent.RightBottomTree; + var parentNeibor = FindNeiborNode(parent, QuadTreeFindMode.Bottom); + if (parentNeibor is null || parentNeibor.RightTopTree is null) + return parentNeibor; + if (tar == parent.LeftBottomTree) + return parentNeibor.LeftTopTree; + return parentNeibor.RightTopTree; + } + case QuadTreeFindMode.Right: + { + if (tar == parent.LeftTopTree) + return parent.RightTopTree; + if (tar == parent.LeftBottomTree) + return parent.RightBottomTree; + var parentNeibor = FindNeiborNode(parent, QuadTreeFindMode.Right); + if (tar == parent.RightTopTree) + return parentNeibor?.LeftTopTree; + return parentNeibor?.LeftBottomTree; + } + case QuadTreeFindMode.Left: + { + if (tar == parent.RightTopTree) + return parent.LeftTopTree; + if (tar == parent.RightBottomTree) + return parent.LeftBottomTree; + var parentNeibor = FindNeiborNode(parent, QuadTreeFindMode.Left); + if (tar == parent.LeftTopTree) + return parentNeibor?.RightTopTree; + return parentNeibor?.RightBottomTree; + } + } + return null; + } + #endregion + + #region 改 + /// + /// 所有的点归类到最小包围它的空间 + /// + //public void PointsToMinNode() + //{ + // ForEach(node => + // { + // for (int i = 0; i < node.Contents.Count; i++) + // { + // var ent = node.Contents[i]; + // if (ent.IsPoint) + // { + // //如果最小包含!=当前,就是没有放在最适合的位置 + // var queryNode = GetMinNode(ent); + // if (queryNode != node) + // { + // node.Remove(ent); + // queryNode.Contents.Add(ent); + // } + // } + // } + // return false; + // }); + //} + #endregion + + #region 方法 + /// + /// 递归全部节点(提供给根用的,所以是全部) + /// + /// QTAction + public bool ForEach(QuadTree.QTAction action) + { + //执行本节点 + if (action(this)) + return true; + + //递归执行本节点的子节点 + var nodes = Nodes; + for (int i = 0; i < nodes.Length; i++) + { + var node = nodes[i]; + if (node is null) + continue; + if (node.ForEach(action)) + break; + } + return false; + } + #endregion +} \ No newline at end of file diff --git a/src/IFoxCAD.Cad/Algorithms/QuadTree/QuadTreeSelectMode.cs b/src/IFoxCAD.Cad/Algorithms/QuadTree/QuadTreeSelectMode.cs new file mode 100644 index 0000000..d180236 --- /dev/null +++ b/src/IFoxCAD.Cad/Algorithms/QuadTree/QuadTreeSelectMode.cs @@ -0,0 +1,21 @@ +namespace IFoxCAD.Cad; + +/// +/// 四叉树选择模式 +/// +public enum QuadTreeSelectMode +{ + IntersectsWith, //碰撞到就选中 + Contains, //全包含才选中 +} + +/// +/// 四叉树查找方向 +/// +public enum QuadTreeFindMode +{ + Top = 1, //上 + Bottom = 2, //下 + Left = 4, //左 + Right = 8, //右 +} \ No newline at end of file diff --git a/src/IFoxCAD.Cad/Algorithms/QuadTree/Rect.cs b/src/IFoxCAD.Cad/Algorithms/QuadTree/Rect.cs new file mode 100644 index 0000000..c533ddb --- /dev/null +++ b/src/IFoxCAD.Cad/Algorithms/QuadTree/Rect.cs @@ -0,0 +1,605 @@ +using System.Diagnostics; + +namespace IFoxCAD.Cad; + +/// +/// Linq Distinct 消重比较两点在容差范围内就去除 +/// +public class TolerancePoint2d : IEqualityComparer +{ + readonly double _tolerance; + public TolerancePoint2d(double tolerance = 1e-6) + { + _tolerance = tolerance; + } + + public bool Equals(Point2d a, Point2d b)//Point3d是struct不会为null + { + /*默认规则是==是0容差,Eq是有容差*/ + // 方形限定 + // 在 0~1e-6 范围实现 圆形限定 则计算部分在浮点数6位后,没有啥意义 + // 在 0~1e-6 范围实现 从时间和CPU消耗来说,圆形限定 都没有 方形限定 的好 + if (_tolerance <= 1e-6) + return Math.Abs(a.X - b.X) <= _tolerance && Math.Abs(a.Y - b.Y) <= _tolerance; + + // 圆形限定 + // DistanceTo 分别对XYZ进行了一次乘法,也是总数3次乘法,然后求了一次平方根 + // (X86.CPU.FSQRT指令用的牛顿迭代法/软件层面可以使用快速平方根....我还以为CPU会采取快速平方根这样的取表操作) + return a.IsEqualTo(b, new Tolerance(_tolerance, _tolerance)); + } + + public int GetHashCode(Point2d obj) + { + //结构体直接返回 obj.GetHashCode(); Point3d ToleranceDistinct3d + //因为结构体是用可值叠加来判断?或者因为结构体兼备了一些享元模式的状态? + //而类是构造的指针,所以取哈希值要改成x+y+z..s给Equals判断用,+是会溢出,所以用^ + return (int)obj.X ^ (int)obj.Y;// ^ (int)obj.Z; + } +} + + +[Serializable] +[StructLayout(LayoutKind.Sequential)] +[DebuggerDisplay("{DebuggerDisplay,nq}")] +[DebuggerTypeProxy(typeof(Rect))] +public class Rect : IEquatable, IComparable +{ + [DebuggerBrowsable(DebuggerBrowsableState.Never)] + private string DebuggerDisplay => ToString("f4"); + +#pragma warning disable CA2211 // 非常量字段应当不可见 + public static TolerancePoint2d RectTolerance = new(1e-6); + public static Tolerance CadTolerance = new(1e-6, 1e-6); +#pragma warning restore CA2211 // 非常量字段应当不可见 + + #region 字段 + //这里的成员不要用{get}封装成属性,否则会导致跳转了一次函数, + //10w图元将会从187毫秒变成400毫秒 + //不用 protected 否则子类传入Rect对象进来无法用 + internal double _X; + internal double _Y; + internal double _Right; + internal double _Top; + #endregion + + #region 成员 + public double X => _X; + public double Y => _Y; + public double Left => _X; + public double Bottom => _Y; + public double Right => _Right; + public double Top => _Top; + + public double Width => _Right - _X; + public double Height => _Top - _Y; + public double Area + { + get + { + var ar = (_Right - _X) * (_Top - _Y); + return ar < 1e-10 ? 0 : ar; + } + } + + public Point2d MinPoint => LeftLower; + public Point2d MaxPoint => RightUpper; + public Point2d CenterPoint => Midst; + + /// + /// 左下Min + /// + public Point2d LeftLower => new(_X, _Y); + + /// + /// 左中 + /// + public Point2d LeftMidst => new(_X, Midst.Y); + + /// + /// 左上 + /// + public Point2d LeftUpper => new(_X, _Top); + + /// + /// 右上Max + /// + public Point2d RightUpper => new(_Right, _Top); + + /// + /// 右中 + /// + public Point2d RightMidst => new(_Right, Midst.Y); + + /// + /// 右下 + /// + public Point2d RightBottom => new(_Right, _Y); + + /// + /// 中间 + /// + public Point2d Midst => new(((_Right - _X) * 0.5) + _X, ((_Top - _Y) * 0.5) + _Y); + + /// + /// 中上 + /// + public Point2d MidstUpper => new(Midst.X, _Top); + + /// + /// 中下 + /// + public Point2d MidstBottom => new(Midst.X, _Y); + + /// + /// 是一个点 + /// 水平或垂直直线包围盒是面积是0,所以面积是0不一定是点 + /// + public bool IsPoint => Math.Abs(_X - _Right) < 1e-10 && Math.Abs(_Y - _Top) < 1e-10; + #endregion + + #region 构造 + public Rect() { } + + /// + /// 矩形类 + /// + /// 左 + /// 下 + /// 右 + /// 上 + public Rect(double left, double bottom, double right, double top) + { + _X = left; + _Y = bottom; + _Right = right; + _Top = top; + } + + /// + /// 构造矩形类 + /// + /// + /// + /// 是否检查大小 + public Rect(Point2d p1, Point2d p3, bool check = false) + { + if (check) + { + _X = Math.Min(p1.X, p3.X); + _Y = Math.Min(p1.Y, p3.Y); + _Right = Math.Max(p1.X, p3.X); + _Top = Math.Max(p1.Y, p3.Y); + } + else + { + _X = p1.X; + _Y = p1.Y; + _Right = p3.X; + _Top = p3.Y; + } + } + #endregion + + #region 重载运算符_比较 + public override bool Equals(object? obj) + { + return this.Equals(obj as Rect); + } + public bool Equals(Rect? b) + { + return this.Equals(b, 1e-6);/*默认规则是==是0容差,Eq是有容差*/ + } + public static bool operator !=(Rect? a, Rect? b) + { + return !(a == b); + } + public static bool operator ==(Rect? a, Rect? b) + { + //此处地方不允许使用==null,因为此处是定义 + if (b is null) + return a is null; + else if (a is null) + return false; + if (ReferenceEquals(a, b))//同一对象 + return true; + + return a.Equals(b, 0); + } + + /// + /// 比较核心 + /// + public bool Equals(Rect? b, double tolerance = 1e-6) + { + if (b is null) + return false; + if (ReferenceEquals(this, b)) //同一对象 + return true; + + return Math.Abs(_X - b._X) < tolerance && + Math.Abs(_Right - b._Right) < tolerance && + Math.Abs(_Top - b._Top) < tolerance && + Math.Abs(_Y - b._Y) < tolerance; + } + + public override int GetHashCode() + { + return (((int)_X ^ (int)_Y).GetHashCode() ^ (int)_Right).GetHashCode() ^ (int)_Top; + } + #endregion + + #region 包含 + public bool Contains(Point2d Point2d) + { + return Contains(Point2d.X, Point2d.Y); + } + public bool Contains(double x, double y) + { + return _X <= x && x <= _Right && + _Y <= y && y <= _Top; + } + + /// + /// 四个点都在内部就是包含 + /// + /// + /// + public bool Contains(Rect rect) + { + return _X <= rect._X && rect._Right <= _Right && + _Y <= rect._Y && rect._Top <= _Top; + } + + /// + /// 一个点在内部就是碰撞 + /// + /// + /// + public bool IntersectsWith(Rect rect) + { + return rect._X <= _Right && _X <= rect._Right && + rect._Top >= _Y && rect._Y <= _Top; + } + #endregion + + #region 方法 + /// + /// 获取共点 + /// + /// + public Point2d[] GetCommonPoint(Rect other) + { + return ToPoints().Intersect(other.ToPoints(), RectTolerance).ToArray(); + } + + public Point2d[] ToPoints() + { + Point2d a = MinPoint;//min + Point2d b = new(_Right, _Y); + Point2d c = MaxPoint;//max + Point2d d = new(_X, _Top); + return new Point2d[] { a, b, c, d }; + } + + public (Point2d boxMin, Point2d boxRigthDown, Point2d boxMax, Point2d boxLeftUp) ToPoints4() + { + Point2d a = MinPoint;//min + Point2d b = new(_Right, _Y); + Point2d c = MaxPoint;//max + Point2d d = new(_X, _Top); + return (a, b, c, d); + } + + /// + /// 四周膨胀 + /// + /// + public Rect Expand(double d) + { + return new Rect(_X - d, _Y - d, _Right + d, _Top + d); + } + + /// + /// 是否矩形(带角度) + /// + /// + /// + public static bool IsRectAngle(List? ptList, double tolerance = 1e-8) + { + if (ptList == null) + throw new ArgumentNullException(nameof(ptList)); + + var pts = ptList.ToList(); + /* 消重,不这里设置,否则这不是一个正确的单元测试 + * //var ptList = pts.Distinct().ToList(); + * var ptList = pts.DistinctExBy((a, b) => a.DistanceTo(b) < 1e-6).ToList(); + */ + if (ptList.Count == 5) + { + //首尾点相同移除最后 + if (pts[0].IsEqualTo(pts[^1], CadTolerance)) + pts.RemoveAt(pts.Count - 1); + } + if (pts.Count != 4) + return false; + + //最快的方案 + //点乘求值法:(为了处理 正梯形/平行四边形 需要三次) + //这里的容差要在1e-8内,因为点乘的三次浮点数乘法会令精度变低 + var dot = DotProductValue(pts[0], pts[1], pts[3]); + if (Math.Abs(dot) < tolerance) + { + dot = DotProductValue(pts[1], pts[2], pts[0]); + if (Math.Abs(dot) < tolerance) + { + dot = DotProductValue(pts[2], pts[3], pts[1]); + return Math.Abs(dot) < tolerance; + } + } + return false; + } + + /// + /// 点积,求值 + /// 1.是两个向量的长度与它们夹角余弦的积 + /// 2.求四个点是否矩形使用 + /// + /// 原点 + /// 点 + /// 点 + /// 0方向相同,夹角0~90度;=0相互垂直;<0方向相反,夹角90~180度]]> + static double DotProductValue(Point2d o, Point2d a, Point2d b) + { + var oa = o.GetVectorTo(a); + var ob = o.GetVectorTo(b); + return (oa.X * ob.X) + (oa.Y * ob.Y); + } + + /// + /// 是否轴向矩形(无角度) + /// + public static bool IsRect(List? ptList, double tolerance = 1e-10) + { + if (ptList == null) + throw new ArgumentNullException(nameof(ptList)); + + var pts = ptList.ToList(); + if (ptList.Count == 5) + { + //首尾点相同移除最后 + if (pts[0].IsEqualTo(pts[^1], CadTolerance)) + pts.RemoveAt(pts.Count - 1); + } + if (pts.Count != 4) + return false; + + return Math.Abs(pts[0].X - pts[3].X) < tolerance && + Math.Abs(pts[0].Y - pts[1].Y) < tolerance && + Math.Abs(pts[1].X - pts[2].X) < tolerance && + Math.Abs(pts[2].Y - pts[3].Y) < tolerance; + } + + /// + /// 获取点集的包围盒的最小点和最大点(无角度) + /// + /// + public static (Point2d boxMin, Point2d boxMax) GetMinMax(IEnumerable pts) + { + var xMin = double.MaxValue; + var xMax = double.MinValue; + var yMin = double.MaxValue; + var yMax = double.MinValue; + //var zMin = double.MaxValue; + //var zMax = double.MinValue; + + pts.ForEach(p => { + xMin = Math.Min(p.X, xMin); + xMax = Math.Max(p.X, xMax); + yMin = Math.Min(p.Y, yMin); + yMax = Math.Max(p.Y, yMax); + //zMin = Math.Min(p.Z, zMin); + //zMax = Math.Max(p.Z, zMax); + }); + return (new Point2d(xMin, yMin), new Point2d(xMax, yMax)); + } + + /// + /// 矩形点序逆时针排列,将min点[0],max点是[3](带角度) + /// + /// + /// + public static bool RectAnglePointOrder(List? pts) + { + if (pts == null) + throw new ArgumentNullException(nameof(pts)); + + if (!Rect.IsRectAngle(pts)) + return false; + + //获取min和max点(非包围盒) + pts = pts.OrderBy(a => a.X).ThenBy(a => a.Y).ToList(); + var minPt = pts.First(); + var maxPt = pts.Last(); + var link = new LoopList(); + link.AddRange(pts); + + pts.Clear(); + //排序这四个点,左下/右下/右上/左上 + var node = link.Find(minPt); + for (int i = 0; i < 4; i++) + { + pts.Add(node!.Value); + node = node.Next; + } + //保证是逆时针 + var isAcw = CrossAclockwise(pts[0], pts[1], pts[2]); + if (!isAcw) + (pts[3], pts[1]) = (pts[1], pts[3]); + return true; + } + + /// + /// 叉积,二维叉乘计算 + /// + /// 传参是向量,表示原点是0,0 + /// 传参是向量,表示原点是0,0 + /// 其模为a与b构成的平行四边形面积 + static double Cross(Vector2d a, Vector2d b) + { + return a.X * b.Y - a.Y * b.X; + } + + /// + /// 叉积,二维叉乘计算 + /// + /// 原点 + /// oa向量 + /// ob向量,此为判断点 + /// 返回值有正负,表示绕原点四象限的位置变换,也就是有向面积 + static double Cross(Point2d o, Point2d a, Point2d b) + { + return Cross(o.GetVectorTo(a), o.GetVectorTo(b)); + } + + /// + /// 叉积,逆时针方向为真 + /// + /// 直线点1 + /// 直线点2 + /// 判断点 + /// b点在oa的逆时针 + static bool CrossAclockwise(Point2d o, Point2d a, Point2d b) + { + return Cross(o, a, b) > -1e-6;//浮点数容差考虑 + } + +#if !WinForm + public Entity ToPolyLine() + { + var bv = new List(); + var pts = ToPoints(); + Polyline pl = new(); + pl.SetDatabaseDefaults(); + pts.ForEach((i, vertex) => { + pl.AddVertexAt(i, vertex, 0, 0, 0); + }); + return pl; + } +#endif + + /// + /// 列扫碰撞检测(碰撞算法) + /// 比四叉树还快哦~ + /// + /// + /// 继承Rect的集合 + /// 先处理集合每一个成员;返回true就跳过后续委托 + /// 碰撞,返回两个碰撞的成员;返回true就跳过后续委托 + /// 后处理集合每一个成员 + public static void XCollision(List box, + Func firstProcessing, + Func collisionProcessing, + Action lastProcessing) where T : Rect + { + //先排序X:不需要Y排序,因为Y的上下浮动不共X .ThenBy(a => a.Box.Y) + //因为先排序就可以有序遍历x区间,超过就break,达到更快 + box = box.OrderBy(a => a._X).ToList(); + + //遍历所有图元 + for (int i = 0; i < box.Count; i++) + { + var oneRect = box[i]; + if (firstProcessing(oneRect)) + continue; + + bool actionlast = true; + + //搜索范围要在 one 的头尾中间的部分 + for (int j = i + 1; j < box.Count; j++) + { + var twoRect = box[j]; + //x碰撞:矩形2的Left 在 矩形1[Left-Right]闭区间;穿过的话,也必然有自己的Left因此不需要处理 + if (oneRect._X <= twoRect._X && twoRect._X <= oneRect._Right) + { + //y碰撞,那就是真的碰撞了 + if ((oneRect._Top >= twoRect._Top && twoRect._Top >= oneRect._Y) /*包容上边*/ + || (oneRect._Top >= twoRect._Y && twoRect._Y >= oneRect._Y) /*包容下边*/ + || (twoRect._Top >= oneRect._Top && oneRect._Y >= twoRect._Y)) /*穿过*/ + { + if (collisionProcessing(oneRect, twoRect)) + actionlast = false; + } + //这里想中断y高过它的无意义比较, + //但是必须排序Y,而排序Y必须同X,而这里不是同X(而是同X区间),所以不能中断 + //而做到X区间排序,就必须创造一个集合,再排序这个集合, + //会导致每个图元都拥有一次X区间集合,开销更巨大(因此放弃). + } + else + break;//因为已经排序了,后续的必然超过 x碰撞区间 + } + + if (actionlast) + lastProcessing(oneRect); + } + } + + #endregion + + #region 转换类型 +#if !WinForm + // 隐式转换(相当于是重载赋值运算符) + //public static implicit operator Rect(System.Windows.Rect rect) + //{ + // return new Rect(rect.Left, rect.Bottom, rect.Right, rect.Top); + //} + public static implicit operator Rect(System.Drawing.RectangleF rect) + { + return new Rect(rect.Left, rect.Bottom, rect.Right, rect.Top); + } + public static implicit operator Rect(System.Drawing.Rectangle rect) + { + return new Rect(rect.Left, rect.Bottom, rect.Right, rect.Top); + } +#endif + + #region ToString + public sealed override string ToString() + { + return ToString(null, null); + } + public string ToString(IFormatProvider? provider) + { + return ToString(null, provider); + } + public string ToString(string? format = null, IFormatProvider? formatProvider = null) + { + return $"({_X.ToString(format, formatProvider)},{_Y.ToString(format, formatProvider)})," + + $"({_Right.ToString(format, formatProvider)},{_Top.ToString(format, formatProvider)})"; + + // return $"X={_X.ToString(format, formatProvider)}," + + // $"Y={_Y.ToString(format, formatProvider)}," + + // $"Right={_Right.ToString(format, formatProvider)}," + + // $"Top={_Top.ToString(format, formatProvider)}"; + } + + /*为了红黑树,加入这个*/ + public int CompareTo(Rect rect) + { + if (rect == null) + return -1; + if (_X < rect._X) + return -1; + else if (_X > rect._X) + return 1; + else if (_Y < rect._Y)/*x是一样的*/ + return -1; + else if (_Y > rect._Y) + return 1; + return 0;/*全部一样*/ + } + #endregion + + #endregion + + +} diff --git a/src/IFoxCAD.Cad/Algorithms/QuadTree/Utility.cs b/src/IFoxCAD.Cad/Algorithms/QuadTree/Utility.cs new file mode 100644 index 0000000..2a0be8e --- /dev/null +++ b/src/IFoxCAD.Cad/Algorithms/QuadTree/Utility.cs @@ -0,0 +1,60 @@ +using System; + +namespace IFoxCAD.Cad; + +public static class Utility +{ + /// + /// 带有随机种子的随机数 + /// 为什么这样写随机种子呢 + /// + /// + public static Random GetRandom() + { + var tick = DateTime.Now.Ticks; + + /* + * 知识准备: + * | 高位64位 | 低位32位 | + * Convert.ToString(int.MaxValue, 2)输出二进制 "1111111111111111111111111111111" 31个;最高位是符号位,所以少1位 + * Convert.ToString(long.MaxValue,2)输出二进制,刚好长一倍 "11111111111111111111111111111111 1111111111111111111111111111111" 63个;最高位是符号位,所以少1位 + * Convert.ToString(0xffffffffL, 2)int.MaxValue再按位多1 "1 1111111111111111111111111111111" 32个;前面的0不会打印出来 + * + * Convert.ToString(long.MaxValue>>32, 2)相当于平移高位的到低位范围,也就是上面少打印的二进制 + * 验证右移是不是高位保留,答案是 + * var a = Convert.ToInt64("101111111111111111111111111111111111111111111111111111111111111", 2); + * Convert.ToString(a >> 32,2); + * + * 解释代码: + * 0x01: + * (int)(long.MaxValue & 0xffffffffL) | (int)(long.MaxValue >> 32); + * Convert.ToString(long.MaxValue & 0xffffffffL, 2)//去掉高位:"11111111111111111111111111111111" 32个,再强转int + * 按位与&是保证符号位肯定是1,其他尽可能为0,高位被去掉只是MaxValue&0的原因,强转才是去掉高位..."尽可能"一词带来第一次随机性 + * 0x02: + * Convert.ToString((long.MaxValue >> 32), 2) //去掉低位: "1111111111111111111111111111111" 31个,再强转int + * 按位或|是尽可能为1..."尽可能"一词带来第二次随机性 + * + */ + + var tickSeeds = (int)(tick & 0xffffffffL) | (int)(tick >> 32); + return new Random(tickSeeds); + } + + /// + /// 随机颜色 + /// + /// + public static System.Drawing.Color RandomColor + { + get + { + var ran = GetRandom(); + int R = ran.Next(255); + int G = ran.Next(255); + int B = ran.Next(255); + B = (R + G > 400) ? R + G - 400 : B;//0 : 380 - R - G; + B = (B > 255) ? 255 : B; + return System.Drawing.Color.FromArgb(R, G, B); + } + } +} \ No newline at end of file diff --git a/src/IFoxCAD.Cad/ExtensionMethod/BulgeVertexWidth.cs b/src/IFoxCAD.Cad/ExtensionMethod/BulgeVertexWidth.cs new file mode 100644 index 0000000..dc7abd3 --- /dev/null +++ b/src/IFoxCAD.Cad/ExtensionMethod/BulgeVertexWidth.cs @@ -0,0 +1,85 @@ +namespace IFoxCAD; + +/// +/// 多段线的顶点,凸度,头宽,尾宽 +/// +[Serializable] +public class BulgeVertexWidth +{ + /// + /// 顶点X + /// + public double X; + /// + /// 顶点Y + /// + public double Y; + /// + /// 凸度 + /// + public double Bulge; + /// + /// 头宽 + /// + public double StartWidth; + /// + /// 尾宽 + /// + public double EndWidth; + + public Point2d Vertex => new(X, Y); + + public BulgeVertexWidth() { } + + /// + /// 多段线的顶点,凸度,头宽,尾宽 + /// + public BulgeVertexWidth(double vertex_X, double vertex_Y, + double bulge = 0, + double startWidth = 0, + double endWidth = 0) + { + X = vertex_X; + Y = vertex_Y; + Bulge = bulge; + StartWidth = startWidth; + EndWidth = endWidth; + } + + /// + /// 多段线的顶点,凸度,头宽,尾宽 + /// + public BulgeVertexWidth(Point2d vertex, + double bulge = 0, + double startWidth = 0, + double endWidth = 0) + : this(vertex.X, vertex.Y, bulge, startWidth, endWidth) + { } + + /// + /// 多段线的顶点,凸度,头宽,尾宽 + /// + public BulgeVertexWidth(BulgeVertex bv) + : this(bv.Vertex.X, bv.Vertex.Y, bv.Bulge) + { } + + /// + /// 多段线的顶点,凸度,头宽,尾宽 + /// + /// 多段线 + /// 子段编号 + public BulgeVertexWidth(Polyline pl, int index) + { + var pt = pl.GetPoint2dAt(index);//这里可以3d + X = pt.X; + Y = pt.Y; + Bulge = pl.GetBulgeAt(index); + StartWidth = pl.GetStartWidthAt(index); + EndWidth = pl.GetEndWidthAt(index); + } + + public BulgeVertex ToBulgeVertex() + { + return new BulgeVertex(Vertex, Bulge); + } +} diff --git a/src/IFoxCAD.Cad/ExtensionMethod/CollectionEx.cs b/src/IFoxCAD.Cad/ExtensionMethod/CollectionEx.cs index 406a33d..bfbed62 100644 --- a/src/IFoxCAD.Cad/ExtensionMethod/CollectionEx.cs +++ b/src/IFoxCAD.Cad/ExtensionMethod/CollectionEx.cs @@ -1,135 +1,198 @@ -using Autodesk.AutoCAD.DatabaseServices; -using Autodesk.AutoCAD.Geometry; -using System; -using System.Collections.Generic; -using System.Collections.Specialized; -using System.Linq; - -namespace IFoxCAD.Cad +namespace IFoxCAD.Cad; + +/// +/// 集合扩展类 +/// +public static class CollectionEx { /// - /// 集合扩展类 + /// 对象id迭代器转换为集合 /// - public static class CollectionEx + /// 对象id的迭代器 + /// 对象id集合 + public static ObjectIdCollection ToCollection(this IEnumerable ids) { - /// - /// 对象id迭代器转换为集合 - /// - /// 对象id的迭代器 - /// 对象id集合 - public static ObjectIdCollection ToCollection(this IEnumerable ids) - { - return new ObjectIdCollection(ids.ToArray()); - } + return new ObjectIdCollection(ids.ToArray()); + } - /// - /// 实体迭代器转换为集合 - /// - /// 对象类型 - /// 实体对象的迭代器 - /// 实体集合 - public static DBObjectCollection ToCollection(this IEnumerable objs) where T : DBObject - { - DBObjectCollection objCol = new(); - foreach (T obj in objs) - objCol.Add(obj); - return objCol; - } + /// + /// 实体迭代器转换为集合 + /// + /// 对象类型 + /// 实体对象的迭代器 + /// 实体集合 + public static DBObjectCollection ToCollection(this IEnumerable objs) where T : DBObject + { + DBObjectCollection objCol = new(); + foreach (T obj in objs) + objCol.Add(obj); + return objCol; + } - /// - /// double 数值迭代器转换为 double 数值集合 - /// - /// double 数值迭代器 - /// double 数值集合 - public static DoubleCollection ToCollection(this IEnumerable doubles) - { - return new DoubleCollection(doubles.ToArray()); - } + /// + /// double 数值迭代器转换为 double 数值集合 + /// + /// double 数值迭代器 + /// double 数值集合 + public static DoubleCollection ToCollection(this IEnumerable doubles) + { + return new DoubleCollection(doubles.ToArray()); + } - /// - /// 二维点迭代器转换为二维点集合 - /// - /// 二维点迭代器 - /// 二维点集合 - public static Point2dCollection ToCollection(this IEnumerable pts) - { - return new Point2dCollection(pts.ToArray()); - } + /// + /// 二维点迭代器转换为二维点集合 + /// + /// 二维点迭代器 + /// 二维点集合 + public static Point2dCollection ToCollection(this IEnumerable pts) + { + return new Point2dCollection(pts.ToArray()); + } - /// - /// 三维点迭代器转换为三维点集合 - /// - /// 三维点迭代器 - /// 三维点集合 - public static Point3dCollection ToCollection(this IEnumerable pts) - { - return new Point3dCollection(pts.ToArray()); - } + /// + /// 三维点迭代器转换为三维点集合 + /// + /// 三维点迭代器 + /// 三维点集合 + public static Point3dCollection ToCollection(this IEnumerable pts) + { + return new Point3dCollection(pts.ToArray()); + } + + /// + /// 对象id集合转换为对象id列表 + /// + /// 对象id集合 + /// 对象id列表 + public static List ToList(this ObjectIdCollection ids) + { + return ids.Cast().ToList(); + } - /// - /// 对象id集合转换为对象id列表 - /// - /// 对象id集合 - /// 对象id列表 - public static List ToList(this ObjectIdCollection ids) - { - return ids.Cast().ToList(); - } - public static List ToList(this StringCollection strs) + /// + /// 遍历集合的迭代器,执行action委托 + /// + /// 集合值的类型 + /// 集合 + /// 要运行的委托 + public static void ForEach(this IEnumerable source, Action action) + { + foreach (var element in source) { - return strs.Cast().ToList(); + action?.Invoke(element); } - - /* Cast不进行过滤,而是直接强转 - var s = new System.Collections.ArrayList(); - s.Add("1"); - s.Add("a"); - s.Add(5666); - - var aaa = s.Cast().ToList(); - System.InvalidCastException: 指定的转换无效。 - + List..ctor(IEnumerable) - + System.Linq.Enumerable.ToList(IEnumerable) - - var aaa = s.Cast().ToList(); - System.InvalidCastException: 无法将类型为“System.Int32”的对象强制转换为类型“System.String”。 - + List..ctor(IEnumerable) - + System.Linq.Enumerable.ToList(IEnumerable) - */ - - /// - /// 遍历集合的迭代器,执行action委托 - /// - /// 集合值的类型 - /// 集合 - /// 要运行的委托 - public static void ForEach(this IEnumerable source, Action action) + } + /// + /// 同时遍历集合索引和值的迭代器,执行action委托 + /// + /// 集合值的类型 + /// 集合 + /// 要运行的委托 + public static void ForEach(this IEnumerable source, Action action) + { + int i = 0; + foreach (var item in source) { - if (action is null) - throw new ArgumentNullException(nameof(action)); - foreach (var element in source) - action.Invoke(element); + action?.Invoke(i, item); + i++; } + } + - /// - /// 同时遍历集合索引和值的迭代器,执行action委托 - /// - /// 集合值的类型 - /// 集合 - /// 要运行的委托 - public static void ForEach(this IEnumerable source, Action action) + #region 关键字集合 + public enum KeywordName + { + GlobalName, + LocalName, + DisplayName, + } + + /// + /// 含有关键字 + /// + /// 关键字集合 + /// 关键字 + /// 关键字容器字段名 + /// true含有 + public static bool Contains(this KeywordCollection collection, string name, + KeywordName keywordName = KeywordName.GlobalName) + { + bool contains = false; + switch (keywordName) { - if (action is null) - throw new ArgumentNullException(nameof(action)); - int i = 0; - foreach (var item in source) - { - action.Invoke(i, item); - i++; - } + case KeywordName.GlobalName: + for (int i = 0; i < collection.Count; i++) + if (collection[i].GlobalName == name) + { + contains = true; + break; + } + break; + case KeywordName.LocalName: + for (int i = 0; i < collection.Count; i++) + if (collection[i].LocalName == name) + { + contains = true; + break; + } + break; + case KeywordName.DisplayName: + for (int i = 0; i < collection.Count; i++) + if (collection[i].DisplayName == name) + { + contains = true; + break; + } + break; + default: + break; } + return contains; + } + + /// + /// 获取词典, + /// KeywordCollection是允许重复关键字的,没有哈希索引,在多次判断时候会遍历多次O(n),所以生成一个词典进行O(1) + /// + /// + /// + public static Dictionary GetDict(this KeywordCollection collection) + { + Dictionary map = new(); + for (int i = 0; i < collection.Count; i++) + map.Add(collection[i].GlobalName, collection[i].DisplayName); + return map; + } + #endregion + + #region IdMapping + /// + /// 旧块名 + /// + /// + /// + public static List GetKeys(this IdMapping idmap) + { + List ids = new(); + foreach (IdPair item in idmap) + ids.Add(item.Key); + return ids; } + + /// + /// 新块名 + /// + /// + /// + public static List GetValues(this IdMapping idmap) + { + List ids = new(); + foreach (IdPair item in idmap) + ids.Add(item.Value); + return ids; + } + #endregion } diff --git a/src/IFoxCAD.Cad/ExtensionMethod/Curve2dEx.cs b/src/IFoxCAD.Cad/ExtensionMethod/Curve2dEx.cs index 7acb61e..51ff4e2 100644 --- a/src/IFoxCAD.Cad/ExtensionMethod/Curve2dEx.cs +++ b/src/IFoxCAD.Cad/ExtensionMethod/Curve2dEx.cs @@ -1,318 +1,312 @@ -using Autodesk.AutoCAD.DatabaseServices; -using Autodesk.AutoCAD.Geometry; -using System; +namespace IFoxCAD.Cad; -namespace IFoxCAD.Cad +/// +/// 二维解析类曲线转换为二维实体曲线扩展类 +/// +public static class Curve2dEx { + #region Curve2d + /// - /// 二维解析类曲线转换为二维实体曲线扩展类 + /// 按矩阵转换Ge2d曲线为Db曲线 /// - - public static class Curve2dEx + /// Ge2d曲线 + /// 曲线转换矩阵 + /// Db曲线 + public static Curve? ToCurve(this Curve2d curve, Matrix3d mat) { - #region Curve2d - - /// - /// 按矩阵转换Ge2d曲线为Db曲线 - /// - /// Ge2d曲线 - /// 曲线转换矩阵 - /// Db曲线 - public static Curve? ToCurve(this Curve2d curve, Matrix3d mat) + return curve switch { - return curve switch - { - LineSegment2d li => ToCurve(li, mat), - NurbCurve2d nu => ToCurve(nu, mat), - EllipticalArc2d el => ToCurve(el, mat), - CircularArc2d ci => ToCurve(ci, mat), - PolylineCurve2d po => ToCurve(po, mat), - Line2d l2 => ToCurve(l2, mat), - CompositeCurve2d co => ToCurve(co, mat), - _ => null - }; - } + LineSegment2d li => ToCurve(li, mat), + NurbCurve2d nu => ToCurve(nu, mat), + EllipticalArc2d el => ToCurve(el, mat), + CircularArc2d ci => ToCurve(ci, mat), + PolylineCurve2d po => ToCurve(po, mat), + Line2d l2 => ToCurve(l2, mat), + CompositeCurve2d co => ToCurve(co, mat), + _ => null + }; + } + + #endregion Curve2d - #endregion Curve2d + #region CircularArc2d - #region CircularArc2d + /// + /// 判断点是否位于圆内及圆上 + /// + /// 二维解析类圆弧对象 + /// 二维点 + /// 位于圆内及圆上返回 ,反之返回 + public static bool IsIn(this CircularArc2d ca2d, Point2d pnt) + { + return ca2d.IsOn(pnt) || ca2d.IsInside(pnt); + } - /// - /// 判断点是否位于圆内及圆上 - /// - /// 二维解析类圆弧对象 - /// 二维点 - /// 位于圆内及圆上返回 ,反之返回 - public static bool IsIn(this CircularArc2d ca2d, Point2d pnt) + /// + /// 将二维解析类圆弧转换为实体圆或者圆弧,然后进行矩阵变换 + /// + /// 二维解析类圆弧对象 + /// 变换矩阵 + /// 实体圆或者圆弧 + public static Curve ToCurve(this CircularArc2d ca2d, Matrix3d mat) + { + Curve c = ToCurve(ca2d); + c.TransformBy(mat); + return c; + } + + /// + /// 将二维解析类圆弧转换为实体圆或者圆弧 + /// + /// 二维解析类圆弧对象 + /// 实体圆或者圆弧 + public static Curve ToCurve(this CircularArc2d ca2d) + { + if (ca2d.IsClosed()) { - return ca2d.IsOn(pnt) || ca2d.IsInside(pnt); + return ToCircle(ca2d); } - - /// - /// 将二维解析类圆弧转换为实体圆或者圆弧,然后进行矩阵变换 - /// - /// 二维解析类圆弧对象 - /// 变换矩阵 - /// 实体圆或者圆弧 - public static Curve ToCurve(this CircularArc2d ca2d, Matrix3d mat) + else { - Curve c = ToCurve(ca2d); - c.TransformBy(mat); - return c; + return ToArc(ca2d); } + } + + /// + /// 将二维解析类圆弧转换为实体圆 + /// + /// 二维解析类圆弧对象 + /// 实体圆 + public static Circle ToCircle(this CircularArc2d c2d) + { + return + new Circle( + new Point3d(new Plane(), c2d.Center), + new Vector3d(0, 0, 1), + c2d.Radius); + } + + /// + /// 将二维解析类圆弧转换为实体圆弧 + /// + /// 二维解析类圆弧对象 + /// 圆弧 + public static Arc ToArc(this CircularArc2d a2d) + { + double startangle, endangle; + double refangle = a2d.ReferenceVector.Angle; - /// - /// 将二维解析类圆弧转换为实体圆或者圆弧 - /// - /// 二维解析类圆弧对象 - /// 实体圆或者圆弧 - public static Curve ToCurve(this CircularArc2d ca2d) + if (a2d.IsClockWise) { - if (ca2d.IsClosed()) - { - return ToCircle(ca2d); - } - else - { - return ToArc(ca2d); - } + startangle = -a2d.EndAngle - refangle; + endangle = -a2d.StartAngle - refangle; } - - /// - /// 将二维解析类圆弧转换为实体圆 - /// - /// 二维解析类圆弧对象 - /// 实体圆 - public static Circle ToCircle(this CircularArc2d c2d) + else { - return - new Circle( - new Point3d(new Plane(), c2d.Center), - new Vector3d(0, 0, 1), - c2d.Radius); + startangle = a2d.StartAngle + refangle; + endangle = a2d.EndAngle + refangle; } - /// - /// 将二维解析类圆弧转换为实体圆弧 - /// - /// 二维解析类圆弧对象 - /// 圆弧 - public static Arc ToArc(this CircularArc2d a2d) - { - double startangle, endangle; - double refangle = a2d.ReferenceVector.Angle; + return + new Arc( + new Point3d(new Plane(), a2d.Center), + Vector3d.ZAxis, + a2d.Radius, + startangle, + endangle); + } - if (a2d.IsClockWise) + #endregion CircularArc2d + + #region EllipticalArc2d + + //椭圆弧 + /// + /// 将二维解析类椭圆弧转换为实体椭圆弧,然后进行矩阵变换 + /// + /// 二维解析类椭圆弧对象 + /// 变换矩阵 + /// 实体椭圆弧 + public static Ellipse ToCurve(this EllipticalArc2d ea2d, Matrix3d mat) + { + Ellipse e = ToCurve(ea2d); + e.TransformBy(mat); + return e; + } + + /// + /// 将二维解析类椭圆弧转换为实体椭圆弧 + /// + /// 二维解析类椭圆弧对象 + /// 实体椭圆弧 + public static Ellipse ToCurve(this EllipticalArc2d ea2d) + { + Plane plane = new(); + Ellipse ell = + new( + new Point3d(plane, ea2d.Center), + new Vector3d(0, 0, 1), + new Vector3d(plane, ea2d.MajorAxis) * ea2d.MajorRadius, + ea2d.MinorRadius / ea2d.MajorRadius, + 0, + Math.PI * 2); + if (!ea2d.IsClosed()) + { + if (ea2d.IsClockWise) { - startangle = -a2d.EndAngle - refangle; - endangle = -a2d.StartAngle - refangle; + ell.StartAngle = -ell.GetAngleAtParameter(ea2d.EndAngle); + ell.EndAngle = -ell.GetAngleAtParameter(ea2d.StartAngle); } else { - startangle = a2d.StartAngle + refangle; - endangle = a2d.EndAngle + refangle; + ell.StartAngle = ell.GetAngleAtParameter(ea2d.StartAngle); + ell.EndAngle = ell.GetAngleAtParameter(ea2d.EndAngle); } - - return - new Arc( - new Point3d(new Plane(), a2d.Center), - Vector3d.ZAxis, - a2d.Radius, - startangle, - endangle); } + return ell; + } - #endregion CircularArc2d - - #region EllipticalArc2d + #endregion EllipticalArc2d - //椭圆弧 - /// - /// 将二维解析类椭圆弧转换为实体椭圆弧,然后进行矩阵变换 - /// - /// 二维解析类椭圆弧对象 - /// 变换矩阵 - /// 实体椭圆弧 - public static Ellipse ToCurve(this EllipticalArc2d ea2d, Matrix3d mat) - { - Ellipse e = ToCurve(ea2d); - e.TransformBy(mat); - return e; - } + #region Line2d - /// - /// 将二维解析类椭圆弧转换为实体椭圆弧 - /// - /// 二维解析类椭圆弧对象 - /// 实体椭圆弧 - public static Ellipse ToCurve(this EllipticalArc2d ea2d) - { - Plane plane = new Plane(); - Ellipse ell = - new Ellipse( - new Point3d(plane, ea2d.Center), - new Vector3d(0, 0, 1), - new Vector3d(plane, ea2d.MajorAxis) * ea2d.MajorRadius, - ea2d.MinorRadius / ea2d.MajorRadius, - 0, - Math.PI * 2); - if (!ea2d.IsClosed()) + /// + /// 将二维解析类直线转换为实体类构造线 + /// + /// 二维解析类直线 + /// 实体类构造线 + public static Xline ToCurve(this Line2d line2d) + { + Plane plane = new(); + return + new Xline { - if (ea2d.IsClockWise) - { - ell.StartAngle = -ell.GetAngleAtParameter(ea2d.EndAngle); - ell.EndAngle = -ell.GetAngleAtParameter(ea2d.StartAngle); - } - else - { - ell.StartAngle = ell.GetAngleAtParameter(ea2d.StartAngle); - ell.EndAngle = ell.GetAngleAtParameter(ea2d.EndAngle); - } - } - return ell; - } - - #endregion EllipticalArc2d - - #region Line2d + BasePoint = new Point3d(plane, line2d.PointOnLine), + SecondPoint = new Point3d(plane, line2d.PointOnLine + line2d.Direction) + }; + } - /// - /// 将二维解析类直线转换为实体类构造线 - /// - /// 二维解析类直线 - /// 实体类构造线 - public static Xline ToCurve(this Line2d line2d) - { - var plane = new Plane(); - return - new Xline - { - BasePoint = new Point3d(plane, line2d.PointOnLine), - SecondPoint = new Point3d(plane, line2d.PointOnLine + line2d.Direction) - }; - } + /// + /// 将二维解析类直线转换为实体类构造线,然后进行矩阵变换 + /// + /// 二维解析类直线 + /// 变换矩阵 + /// 实体类构造线 + public static Xline ToCurve(this Line2d line2d, Matrix3d mat) + { + Xline xl = ToCurve(line2d); + xl.TransformBy(mat); + return xl; + } - /// - /// 将二维解析类直线转换为实体类构造线,然后进行矩阵变换 - /// - /// 二维解析类直线 - /// 变换矩阵 - /// 实体类构造线 - public static Xline ToCurve(this Line2d line2d, Matrix3d mat) - { - Xline xl = ToCurve(line2d); - xl.TransformBy(mat); - return xl; - } + /// + /// 将二维解析类构造线转换为二维解析类线段 + /// + /// 二维解析类构造线 + /// 起点参数 + /// 终点参数 + /// 二维解析类线段 + public static LineSegment2d ToLineSegment2d(this Line2d line2d, double fromParameter, double toParameter) + { + return + new LineSegment2d + ( + line2d.EvaluatePoint(fromParameter), + line2d.EvaluatePoint(toParameter) + ); + } - /// - /// 将二维解析类构造线转换为二维解析类线段 - /// - /// 二维解析类构造线 - /// 起点参数 - /// 终点参数 - /// 二维解析类线段 - public static LineSegment2d ToLineSegment2d(this Line2d line2d, double fromParameter, double toParameter) - { - return - new LineSegment2d - ( - line2d.EvaluatePoint(fromParameter), - line2d.EvaluatePoint(toParameter) - ); - } + #endregion Line2d - #endregion Line2d + #region LineSegment2d - #region LineSegment2d + /// + /// 将二维解析类线段转换为实体类直线,并进行矩阵变换 + /// + /// 二维解析类线段 + /// 变换矩阵 + /// 实体类直线 + public static Line ToCurve(this LineSegment2d ls2d, Matrix3d mat) + { + Line l = ToCurve(ls2d); + l.TransformBy(mat); + return l; + } - /// - /// 将二维解析类线段转换为实体类直线,并进行矩阵变换 - /// - /// 二维解析类线段 - /// 变换矩阵 - /// 实体类直线 - public static Line ToCurve(this LineSegment2d ls2d, Matrix3d mat) - { - Line l = ToCurve(ls2d); - l.TransformBy(mat); - return l; - } + /// + /// 将二维解析类线段转换为实体类直线 + /// + /// 二维解析类线段 + /// 实体类直线 + public static Line ToCurve(this LineSegment2d ls2d) + { + Plane plane = new(); + return + new Line( + new Point3d(plane, ls2d.StartPoint), + new Point3d(plane, ls2d.EndPoint)); - /// - /// 将二维解析类线段转换为实体类直线 - /// - /// 二维解析类线段 - /// 实体类直线 - public static Line ToCurve(this LineSegment2d ls2d) - { - Plane plane = new Plane(); - return - new Line( - new Point3d(plane, ls2d.StartPoint), - new Point3d(plane, ls2d.EndPoint)); + } - } + #endregion LineSegment2d - #endregion LineSegment2d + #region NurbCurve2d - #region NurbCurve2d + /// + /// 将二维解析类BURB曲线转换为实体类样条曲线,并进行矩阵变换 + /// + /// 二维解析类BURB曲线 + /// 变换矩阵 + /// 实体类样条曲线 + public static Spline ToCurve(this NurbCurve2d nc2d, Matrix3d mat) + { + Spline spl = ToCurve(nc2d); + spl.TransformBy(mat); + return spl; + } - /// - /// 将二维解析类BURB曲线转换为实体类样条曲线,并进行矩阵变换 - /// - /// 二维解析类BURB曲线 - /// 变换矩阵 - /// 实体类样条曲线 - public static Spline ToCurve(this NurbCurve2d nc2d, Matrix3d mat) + /// + /// 将二维解析类BURB曲线转换为实体类样条曲线 + /// + /// 二维解析类BURB曲线 + /// 实体类样条曲线 + public static Spline ToCurve(this NurbCurve2d nc2d) + { + int i; + Plane plane = new(); + Point3dCollection ctlpnts = new(); + for (i = 0; i < nc2d.NumControlPoints; i++) { - Spline spl = ToCurve(nc2d); - spl.TransformBy(mat); - return spl; + ctlpnts.Add(new Point3d(plane, nc2d.GetControlPointAt(i))); } - /// - /// 将二维解析类BURB曲线转换为实体类样条曲线 - /// - /// 二维解析类BURB曲线 - /// 实体类样条曲线 - public static Spline ToCurve(this NurbCurve2d nc2d) + DoubleCollection knots = new(); + foreach (double knot in nc2d.Knots) { - int i; - Plane plane = new Plane(); - Point3dCollection ctlpnts = new Point3dCollection(); - for (i = 0; i < nc2d.NumControlPoints; i++) - { - ctlpnts.Add(new Point3d(plane, nc2d.GetControlPointAt(i))); - } - - DoubleCollection knots = new DoubleCollection(); - foreach (double knot in nc2d.Knots) - { - knots.Add(knot); - } - - DoubleCollection weights = new DoubleCollection(); - for (i = 0; i < nc2d.NumWeights; i++) - { - weights.Add(nc2d.GetWeightAt(i)); - } + knots.Add(knot); + } - NurbCurve2dData ncdata = nc2d.DefinitionData; - - return - new Spline( - ncdata.Degree, - ncdata.Rational, - nc2d.IsClosed(), - ncdata.Periodic, - ctlpnts, - knots, - weights, - 0, - nc2d.Knots.Tolerance); + DoubleCollection weights = new(); + for (i = 0; i < nc2d.NumWeights; i++) + { + weights.Add(nc2d.GetWeightAt(i)); } - #endregion NurbCurve2d + NurbCurve2dData ncdata = nc2d.DefinitionData; + + return + new Spline( + ncdata.Degree, + ncdata.Rational, + nc2d.IsClosed(), + ncdata.Periodic, + ctlpnts, + knots, + weights, + 0, + nc2d.Knots.Tolerance); } + + #endregion NurbCurve2d } diff --git a/src/IFoxCAD.Cad/ExtensionMethod/Curve3dEx.cs b/src/IFoxCAD.Cad/ExtensionMethod/Curve3dEx.cs index 9d477ba..57ca8fd 100644 --- a/src/IFoxCAD.Cad/ExtensionMethod/Curve3dEx.cs +++ b/src/IFoxCAD.Cad/ExtensionMethod/Curve3dEx.cs @@ -1,567 +1,557 @@ -using Autodesk.AutoCAD.DatabaseServices; -using Autodesk.AutoCAD.Geometry; -using System; -using System.Collections.Generic; -using System.Linq; +namespace IFoxCAD.Cad; -namespace IFoxCAD.Cad +/// +/// 三维解析类曲线转换为三维实体曲线扩展类 +/// +public static class Curve3dEx { /// - /// 三维解析类曲线转换为三维实体曲线扩展类 + /// 判断两个浮点数是否相等 /// - public static class Curve3dEx + /// 容差 + /// 第一个数 + /// 第二个数 + /// 两个数的差值的绝对值小于容差返回 ,反之返回 + public static bool IsEqualPoint(this Tolerance tol, double d1, double d2) { - /// - /// 判断两个浮点数是否相等 - /// - /// 容差 - /// 第一个数 - /// 第二个数 - /// 两个数的差值的绝对值小于容差返回 ,反之返回 - public static bool IsEqualPoint(this Tolerance tol, double d1, double d2) - { - return Math.Abs(d1 - d2) < tol.EqualPoint; - } + return Math.Abs(d1 - d2) < tol.EqualPoint; + } - #region Curve3d + #region Curve3d - /// - /// 获取三维解析类曲线(自交曲线)的交点参数 - /// - /// 三维解析类曲线 - /// 曲线参数的列表 - public static List GetParamsAtIntersectionPoints(this Curve3d c3d) - { - CurveCurveIntersector3d cci = new CurveCurveIntersector3d(c3d, c3d, Vector3d.ZAxis); - List pars = new List(); - for (int i = 0; i < cci.NumberOfIntersectionPoints; i++) - { - pars.AddRange(cci.GetIntersectionParameters(i)); - } - pars.Sort(); - return pars; - } + /// + /// 获取三维解析类曲线(自交曲线)的交点参数 + /// + /// 三维解析类曲线 + /// 曲线参数的列表 + public static List GetParamsAtIntersectionPoints(this Curve3d c3d) + { + CurveCurveIntersector3d cci = new(c3d, c3d, Vector3d.ZAxis); + List pars = new(); + for (int i = 0; i < cci.NumberOfIntersectionPoints; i++) + pars.AddRange(cci.GetIntersectionParameters(i)); - /// - /// 获取三维解析类子曲线 - /// - /// 三维解析类曲线 - /// 子段曲线起点参数 - /// 子段曲线终点参数 - /// 三维解析类曲线 - public static Curve3d GetSubCurve(this Curve3d curve, double from, double to) + pars.Sort(); + return pars; + } + + /// + /// 获取三维解析类子曲线 + /// + /// 三维解析类曲线 + /// 子段曲线起点参数 + /// 子段曲线终点参数 + /// 三维解析类曲线 + public static Curve3d GetSubCurve(this Curve3d curve, double from, double to) + { + Interval inter = curve.GetInterval(); + bool atStart = Tolerance.Global.IsEqualPoint(inter.LowerBound, from); + bool atEnd = Tolerance.Global.IsEqualPoint(inter.UpperBound, to); + if (atStart && atEnd) + return (Curve3d)curve.Clone(); + if (curve is NurbCurve3d) { - Interval inter = curve.GetInterval(); - bool atStart = Tolerance.Global.IsEqualPoint(inter.LowerBound, from); - bool atEnd = Tolerance.Global.IsEqualPoint(inter.UpperBound, to); - if (atStart && atEnd) - return (Curve3d)curve.Clone(); - if (curve is NurbCurve3d) + if (from < to) { - if (from < to) + NurbCurve3d clone = (NurbCurve3d)curve.Clone(); + if (atStart || atEnd) { - NurbCurve3d clone = (NurbCurve3d)curve.Clone(); - if (atStart || atEnd) - { - clone.HardTrimByParams(from, to); - return clone; - } - else - { - clone.HardTrimByParams(inter.LowerBound, to); - clone.HardTrimByParams(from, to); - return clone; - } + clone.HardTrimByParams(from, to); + return clone; } else { - NurbCurve3d clone1 = (NurbCurve3d)curve.Clone(); - clone1.HardTrimByParams(from, inter.UpperBound); - NurbCurve3d clone2 = (NurbCurve3d)curve.Clone(); - clone2.HardTrimByParams(inter.LowerBound, to); - clone1.JoinWith(clone2); - return clone1; + clone.HardTrimByParams(inter.LowerBound, to); + clone.HardTrimByParams(from, to); + return clone; } } else { - Curve3d clone = (Curve3d)curve.Clone(); - clone.SetInterval(new Interval(from, to, Tolerance.Global.EqualPoint)); - return clone; + NurbCurve3d clone1 = (NurbCurve3d)curve.Clone(); + clone1.HardTrimByParams(from, inter.UpperBound); + NurbCurve3d clone2 = (NurbCurve3d)curve.Clone(); + clone2.HardTrimByParams(inter.LowerBound, to); + clone1.JoinWith(clone2); + return clone1; } } - - /// - /// 将三维解析类曲线转换为三维实体类曲线 - /// - /// 三维解析类曲线 - /// 三维实体类曲线 - public static Curve ToCurve(this Curve3d curve) + else { - return curve switch - { - CompositeCurve3d co => ToCurve(co), - LineSegment3d li => ToCurve(li), - EllipticalArc3d el => ToCurve(el), - CircularArc3d ci => ToCurve(ci), - NurbCurve3d nu => ToCurve(nu), - PolylineCurve3d pl => ToCurve(pl), - Line3d l3 => ToCurve(l3), - _ => null - }; + Curve3d clone = (Curve3d)curve.Clone(); + clone.SetInterval(new Interval(from, to, Tolerance.Global.EqualPoint)); + return clone; } + } - /// - /// 将三维解析类曲线转换为三维解析类Nurb曲线 - /// - /// 三维解析类曲线 - /// 三维解析类Nurb曲线 - public static NurbCurve3d ToNurbCurve3d(this Curve3d curve) + /// + /// 将三维解析类曲线转换为三维实体类曲线 + /// + /// 三维解析类曲线 + /// 三维实体类曲线 + public static Curve? ToCurve(this Curve3d curve) + { + return curve switch { - return curve switch - { - LineSegment3d line => new NurbCurve3d(line), - EllipticalArc3d el => new NurbCurve3d(el), - CircularArc3d cir => new NurbCurve3d(ToEllipticalArc3d(cir)), - NurbCurve3d nur => nur, - PolylineCurve3d pl => new NurbCurve3d(3, pl, false), - _ => null - }; - } + CompositeCurve3d co => ToCurve(co), + LineSegment3d li => ToCurve(li), + EllipticalArc3d el => ToCurve(el), + CircularArc3d ci => ToCurve(ci), + NurbCurve3d nu => ToCurve(nu), + PolylineCurve3d pl => ToCurve(pl), + Line3d l3 => ToCurve(l3), + _ => null + }; + } + + /// + /// 将三维解析类曲线转换为三维解析类Nurb曲线 + /// + /// 三维解析类曲线 + /// 三维解析类Nurb曲线 + public static NurbCurve3d? ToNurbCurve3d(this Curve3d curve) + { + return curve switch + { + LineSegment3d line => new NurbCurve3d(line), + EllipticalArc3d el => new NurbCurve3d(el), + CircularArc3d cir => new NurbCurve3d(ToEllipticalArc3d(cir)), + NurbCurve3d nur => nur, + PolylineCurve3d pl => new NurbCurve3d(3, pl, false), + _ => null + }; + } - #endregion Curve3d + #endregion Curve3d - #region CompositeCurve3d + #region CompositeCurve3d - /// - /// 判断是否为圆和椭圆 - /// - /// 三维解析类曲线 - /// 完整圆及完整的椭圆返回 ,反之返回 - public static bool IsCircular(this Curve3d curve) + /// + /// 判断是否为圆和椭圆 + /// + /// 三维解析类曲线 + /// 完整圆及完整的椭圆返回 ,反之返回 + public static bool IsCircular(this Curve3d curve) + { + return curve switch { - return curve switch - { - CircularArc3d or EllipticalArc3d => curve.IsClosed(), - _ => false - }; - } + CircularArc3d or EllipticalArc3d => curve.IsClosed(), + _ => false + }; + } - /// - /// 将三维复合曲线按曲线参数分割 - /// - /// 三维复合曲线 - /// 曲线参数列表 - /// 三维复合曲线列表 - public static List GetSplitCurves(this CompositeCurve3d c3d, List pars) + /// + /// 将三维复合曲线按曲线参数分割 + /// + /// 三维复合曲线 + /// 曲线参数列表 + /// 三维复合曲线列表 + public static List GetSplitCurves(this CompositeCurve3d c3d, List pars) + { + //曲线参数剔除重复的 + pars.Sort(); + for (int i = pars.Count - 1; i > 0; i--) { - Interval inter = c3d.GetInterval(); - Curve3d[] c3ds = c3d.GetCurves(); - pars.Sort(); - for (int i = pars.Count - 1; i > 0; i--) - { - if (Tolerance.Global.IsEqualPoint(pars[i], pars[i - 1])) - pars.RemoveAt(i); - } + if (Tolerance.Global.IsEqualPoint(pars[i], pars[i - 1])) + pars.RemoveAt(i); + } - if (pars.Count == 0) - return new List(); - if (c3ds.Length == 1 && c3ds[0].IsClosed()) + if (pars.Count == 0) + return new List(); + + //这个是曲线参数类 + var inter = c3d.GetInterval(); + //曲线们 + var c3ds = c3d.GetCurves(); + if (c3ds.Length == 1 && c3ds[0].IsClosed()) + { + //闭合曲线不允许打断于一点 + if (pars.Count > 1) { - //闭合曲线不允许打断于一点 - if (pars.Count > 1) + //如果包含起点 + if (Tolerance.Global.IsEqualPoint(pars[0], inter.LowerBound)) { - //如果包含起点 - if (Tolerance.Global.IsEqualPoint(pars[0], inter.LowerBound)) - { - pars[0] = inter.LowerBound; - //又包含终点,去除终点 - if (Tolerance.Global.IsEqualPoint(pars[pars.Count - 1], inter.UpperBound)) - { - pars.RemoveAt(pars.Count - 1); - if (pars.Count == 1) - return new List(); - } - } - else if (Tolerance.Global.IsEqualPoint(pars[pars.Count - 1], inter.UpperBound)) + pars[0] = inter.LowerBound; + //又包含终点,去除终点 + if (Tolerance.Global.IsEqualPoint(pars[^1], inter.UpperBound)) { - pars[pars.Count - 1] = inter.UpperBound; + pars.RemoveAt(pars.Count - 1); + if (pars.Count == 1) + return new List(); } - //加入第一点以支持反向打断 - pars.Add(pars[0]); } - else + else if (Tolerance.Global.IsEqualPoint(pars[^1], inter.UpperBound)) { - return new List(); + pars[^1] = inter.UpperBound; } + //加入第一点以支持反向打断 + pars.Add(pars[0]); } else { - //非闭合曲线加入起点和终点 - if (Tolerance.Global.IsEqualPoint(pars[0], inter.LowerBound)) - pars[0] = inter.LowerBound; - else - pars.Insert(0, inter.LowerBound); - if (Tolerance.Global.IsEqualPoint(pars[pars.Count - 1], inter.UpperBound)) - pars[pars.Count - 1] = inter.UpperBound; - else - pars.Add(inter.UpperBound); - } - - List curves = new List(); - for (int i = 0; i < pars.Count - 1; i++) - { - List cc3ds = new List(); - //复合曲线参数转换到包含曲线参数 - CompositeParameter cp1 = c3d.GlobalToLocalParameter(pars[i]); - CompositeParameter cp2 = c3d.GlobalToLocalParameter(pars[i + 1]); - if (cp1.SegmentIndex == cp2.SegmentIndex) - { - cc3ds.Add( - c3ds[cp1.SegmentIndex].GetSubCurve( - cp1.LocalParameter, - cp2.LocalParameter)); - } - else - { - inter = c3ds[cp1.SegmentIndex].GetInterval(); - cc3ds.Add( - c3ds[cp1.SegmentIndex].GetSubCurve( - cp1.LocalParameter, - inter.UpperBound)); - for (int j = cp1.SegmentIndex + 1; j < cp2.SegmentIndex; j++) - { - cc3ds.Add((Curve3d)c3ds[j].Clone()); - } - inter = c3ds[cp2.SegmentIndex].GetInterval(); - cc3ds.Add( - c3ds[cp2.SegmentIndex].GetSubCurve( - inter.LowerBound, - cp2.LocalParameter)); - } - curves.Add(new CompositeCurve3d(cc3ds.ToArray())); - } - - if (c3d.IsClosed() && c3ds.Length > 1) - { - var cs = curves[curves.Count - 1].GetCurves().ToList(); - cs.AddRange(curves[0].GetCurves()); - curves[curves.Count - 1] = new CompositeCurve3d(cs.ToArray()); - curves.RemoveAt(0); + return new List(); } - - return curves; + } + else + { + //非闭合曲线加入起点和终点 + if (Tolerance.Global.IsEqualPoint(pars[0], inter.LowerBound)) + pars[0] = inter.LowerBound; + else + pars.Insert(0, inter.LowerBound); + if (Tolerance.Global.IsEqualPoint(pars[^1], inter.UpperBound)) + pars[^1] = inter.UpperBound; + else + pars.Add(inter.UpperBound); } - /// - /// 将复合曲线转换为实体类曲线 - /// - /// 三维复合曲线 - /// 实体曲线 - public static Curve ToCurve(this CompositeCurve3d curve) + List curves = new(); + for (int i = 0; i < pars.Count - 1; i++) { - Curve3d[] cs = curve.GetCurves(); - if (cs.Length == 0) + List cc3ds = new(); + //复合曲线参数转换到包含曲线参数 + var cp1 = c3d.GlobalToLocalParameter(pars[i]); + var cp2 = c3d.GlobalToLocalParameter(pars[i + 1]); + if (cp1.SegmentIndex == cp2.SegmentIndex) { - return null; - } - else if (cs.Length == 1) - { - return ToCurve(cs[0]); + cc3ds.Add( + c3ds[cp1.SegmentIndex].GetSubCurve( + cp1.LocalParameter, + cp2.LocalParameter)); } else { - bool hasNurb = false; - - foreach (var c in cs) - { - if (c is NurbCurve3d || c is EllipticalArc3d) - { - hasNurb = true; - break; - } - } - if (hasNurb) - { - NurbCurve3d nc3d = cs[0].ToNurbCurve3d(); - for (int i = 1; i < cs.Length; i++) - nc3d.JoinWith(cs[i].ToNurbCurve3d()); - return nc3d.ToCurve(); - } - else + inter = c3ds[cp1.SegmentIndex].GetInterval(); + cc3ds.Add( + c3ds[cp1.SegmentIndex].GetSubCurve( + cp1.LocalParameter, + inter.UpperBound)); + for (int j = cp1.SegmentIndex + 1; j < cp2.SegmentIndex; j++) { - return ToPolyline(curve); + cc3ds.Add((Curve3d)c3ds[j].Clone()); } + inter = c3ds[cp2.SegmentIndex].GetInterval(); + cc3ds.Add( + c3ds[cp2.SegmentIndex].GetSubCurve( + inter.LowerBound, + cp2.LocalParameter)); } + curves.Add(new CompositeCurve3d(cc3ds.ToArray())); } - /// - /// 将三维复合曲线转换为实体类多段线 - /// - /// 三维复合曲线 - /// 实体类多段线 - public static Polyline ToPolyline(this CompositeCurve3d cc3d) + if (c3d.IsClosed() && c3ds.Length > 1) { - Polyline pl = new Polyline(); - pl.Elevation = cc3d.StartPoint[2]; - Plane plane = pl.GetPlane(); - Point2d endver = Point2d.Origin; - int i = 0; - foreach (Curve3d c3d in cc3d.GetCurves()) - { - if (c3d is CircularArc3d) - { - CircularArc3d ca3d = (CircularArc3d)c3d; - double b = Math.Tan(0.25 * (ca3d.EndAngle - ca3d.StartAngle)) * ca3d.Normal[2]; - pl.AddVertexAt(i, c3d.StartPoint.Convert2d(plane), b, 0, 0); - endver = c3d.EndPoint.Convert2d(plane); - } - else - { - pl.AddVertexAt(i, c3d.StartPoint.Convert2d(plane), 0, 0, 0); - endver = c3d.EndPoint.Convert2d(plane); - } - i++; - } - pl.AddVertexAt(i, endver, 0, 0, 0); - return pl; + var cus1 = curves[curves.Count - 1].GetCurves(); + var cus2 = curves[0].GetCurves(); + var cs = cus1.Combine2(cus2); + curves[^1] = new CompositeCurve3d(cs); + curves.RemoveAt(0); } + return curves; + } - #endregion CompositeCurve3d - - #region Line3d + /// + /// 将复合曲线转换为实体类曲线 + /// + /// 三维复合曲线 + /// 实体曲线 + public static Curve? ToCurve(this CompositeCurve3d curve) + { + Curve3d[] cs = curve.GetCurves(); + if (cs.Length == 0) + return null; + if (cs.Length == 1) + return ToCurve(cs[0]); - /// - /// 将解析类三维构造线转换为实体类构造线 - /// - /// 解析类三维构造线 - /// 实体类构造线 - public static Xline ToCurve(this Line3d line3d) - { - return - new Xline - { - BasePoint = line3d.PointOnLine, - SecondPoint = line3d.PointOnLine + line3d.Direction - }; - } + bool hasNurb = false; - /// - /// 将三维解析类构造线转换为三维解析类线段 - /// - /// 三维解析类构造线 - /// 起点参数 - /// 终点参数 - /// 三维解析类线段 - public static LineSegment3d ToLineSegment3d(this Line3d line3d, double fromParameter, double toParameter) + for (int i = 0; i < cs.Length; i++) { - return - new LineSegment3d - ( - line3d.EvaluatePoint(fromParameter), - line3d.EvaluatePoint(toParameter) - ); + var c = cs[i]; + if (c is NurbCurve3d || c is EllipticalArc3d) + { + hasNurb = true; + break; + } } - - #endregion Line3d - - #region LineSegment3d - - /// - /// 将三维解析类线段转换为实体类直线 - /// - /// 三维解析类线段 - /// 实体类直线 - public static Line ToCurve(this LineSegment3d lineSeg3d) + if (hasNurb) { - return new Line(lineSeg3d.StartPoint, lineSeg3d.EndPoint); + var nc3d = cs[0].ToNurbCurve3d(); + for (int i = 1; i < cs.Length; i++) + nc3d?.JoinWith(cs[i].ToNurbCurve3d()); + return nc3d?.ToCurve(); } - #endregion LineSegment3d - - #region CircularArc3d + return ToPolyline(curve); + } - /// - /// 将三维解析类圆/弧转换为实体圆/弧 - /// - /// 三维解析类圆/弧 - /// 实体圆/弧 - public static Curve ToCurve(this CircularArc3d ca3d) + /// + /// 将三维复合曲线转换为实体类多段线 + /// + /// 三维复合曲线 + /// 实体类多段线 + public static Polyline ToPolyline(this CompositeCurve3d cc3d) + { + Polyline pl = new(); + pl.SetDatabaseDefaults(); + pl.Elevation = cc3d.StartPoint[2]; + + Plane plane = pl.GetPlane(); + Point2d endver = Point2d.Origin; + int i = 0; + foreach (Curve3d c3d in cc3d.GetCurves()) { - if (ca3d.IsClosed()) + if (c3d is CircularArc3d ca3d) { - return ToCircle(ca3d); + double b = Math.Tan(0.25 * (ca3d.EndAngle - ca3d.StartAngle)) * ca3d.Normal[2]; + pl.AddVertexAt(i, c3d.StartPoint.Convert2d(plane), b, 0, 0); + endver = c3d.EndPoint.Convert2d(plane); } else { - return ToArc(ca3d); + pl.AddVertexAt(i, c3d.StartPoint.Convert2d(plane), 0, 0, 0); + endver = c3d.EndPoint.Convert2d(plane); } + i++; } + pl.AddVertexAt(i, endver, 0, 0, 0); + return pl; + } - /// - /// 将三维解析类圆/弧转换为实体圆 - /// - /// 三维解析类圆/弧 - /// 实体圆 - public static Circle ToCircle(this CircularArc3d ca3d) => - new Circle(ca3d.Center, ca3d.Normal, ca3d.Radius); - - /// - /// 将三维解析类圆/弧转换为实体圆弧 - /// - /// 三维解析类圆/弧 - /// 实体圆弧 - public static Arc ToArc(this CircularArc3d ca3d) - { - //必须新建,而不能直接使用GetPlane()获取 - double angle = ca3d.ReferenceVector.AngleOnPlane(new Plane(ca3d.Center, ca3d.Normal)); - return new Arc(ca3d.Center, ca3d.Normal, ca3d.Radius, ca3d.StartAngle + angle, ca3d.EndAngle + angle); - } + #endregion CompositeCurve3d + + #region Line3d - /// - /// 将三维解析类圆/弧转换为三维解析类椭圆弧 - /// - /// 三维解析类圆/弧 - /// 三维解析类椭圆弧 - public static EllipticalArc3d ToEllipticalArc3d(this CircularArc3d ca3d) + /// + /// 将解析类三维构造线转换为实体类构造线 + /// + /// 解析类三维构造线 + /// 实体类构造线 + public static Xline ToCurve(this Line3d line3d) + { + return + new Xline + { + BasePoint = line3d.PointOnLine, + SecondPoint = line3d.PointOnLine + line3d.Direction + }; + } + + /// + /// 将三维解析类构造线转换为三维解析类线段 + /// + /// 三维解析类构造线 + /// 起点参数 + /// 终点参数 + /// 三维解析类线段 + public static LineSegment3d ToLineSegment3d(this Line3d line3d, double fromParameter, double toParameter) + { + return + new LineSegment3d + ( + line3d.EvaluatePoint(fromParameter), + line3d.EvaluatePoint(toParameter) + ); + } + + #endregion Line3d + + #region LineSegment3d + + /// + /// 将三维解析类线段转换为实体类直线 + /// + /// 三维解析类线段 + /// 实体类直线 + public static Line ToCurve(this LineSegment3d lineSeg3d) + { + return new Line(lineSeg3d.StartPoint, lineSeg3d.EndPoint); + } + + #endregion LineSegment3d + + #region CircularArc3d + + /// + /// 将三维解析类圆/弧转换为实体圆/弧 + /// + /// 三维解析类圆/弧 + /// 实体圆/弧 + public static Curve ToCurve(this CircularArc3d ca3d) + { + if (ca3d.IsClosed()) { - Vector3d zaxis = ca3d.Normal; - Vector3d xaxis = ca3d.ReferenceVector; - Vector3d yaxis = zaxis.CrossProduct(xaxis); - - return - new EllipticalArc3d( - ca3d.Center, - xaxis, - yaxis, - ca3d.Radius, - ca3d.Radius, - ca3d.StartAngle, - ca3d.EndAngle); + return ToCircle(ca3d); } - - /// - /// 将三维解析类圆/弧转换为三维解析类Nurb曲线 - /// - /// 三维解析类圆/弧 - /// 三维解析类Nurb曲线 - public static NurbCurve3d ToNurbCurve3d(this CircularArc3d ca3d) + else { - EllipticalArc3d ea3d = ToEllipticalArc3d(ca3d); - NurbCurve3d nc3d = new NurbCurve3d(ea3d); - return nc3d; + return ToArc(ca3d); } + } + + /// + /// 将三维解析类圆/弧转换为实体圆 + /// + /// 三维解析类圆/弧 + /// 实体圆 + public static Circle ToCircle(this CircularArc3d ca3d) => + new(ca3d.Center, ca3d.Normal, ca3d.Radius); - #endregion CircularArc3d + /// + /// 将三维解析类圆/弧转换为实体圆弧 + /// + /// 三维解析类圆/弧 + /// 实体圆弧 + public static Arc ToArc(this CircularArc3d ca3d) + { + //必须新建,而不能直接使用GetPlane()获取 + double angle = ca3d.ReferenceVector.AngleOnPlane(new Plane(ca3d.Center, ca3d.Normal)); + return new Arc(ca3d.Center, ca3d.Normal, ca3d.Radius, ca3d.StartAngle + angle, ca3d.EndAngle + angle); + } - #region EllipticalArc3d + /// + /// 将三维解析类圆/弧转换为三维解析类椭圆弧 + /// + /// 三维解析类圆/弧 + /// 三维解析类椭圆弧 + public static EllipticalArc3d ToEllipticalArc3d(this CircularArc3d ca3d) + { + Vector3d zaxis = ca3d.Normal; + Vector3d xaxis = ca3d.ReferenceVector; + Vector3d yaxis = zaxis.CrossProduct(xaxis); + + return + new EllipticalArc3d( + ca3d.Center, + xaxis, + yaxis, + ca3d.Radius, + ca3d.Radius, + ca3d.StartAngle, + ca3d.EndAngle); + } - /// - /// 将三维解析类椭圆弧转换为实体类椭圆弧 - /// - /// 三维解析类椭圆弧 - /// 实体类椭圆弧 - public static Ellipse ToCurve(this EllipticalArc3d ea3d) + /// + /// 将三维解析类圆/弧转换为三维解析类Nurb曲线 + /// + /// 三维解析类圆/弧 + /// 三维解析类Nurb曲线 + public static NurbCurve3d ToNurbCurve3d(this CircularArc3d ca3d) + { + EllipticalArc3d ea3d = ToEllipticalArc3d(ca3d); + NurbCurve3d nc3d = new(ea3d); + return nc3d; + } + + #endregion CircularArc3d + + #region EllipticalArc3d + + /// + /// 将三维解析类椭圆弧转换为实体类椭圆弧 + /// + /// 三维解析类椭圆弧 + /// 实体类椭圆弧 + public static Ellipse ToCurve(this EllipticalArc3d ea3d) + { + Ellipse ell = + new( + ea3d.Center, + ea3d.Normal, + ea3d.MajorAxis * ea3d.MajorRadius, + ea3d.MinorRadius / ea3d.MajorRadius, + 0, + Math.PI * 2); + //Ge椭圆角度就是Db椭圆的参数 + if (!ea3d.IsClosed()) { - Ellipse ell = - new Ellipse( - ea3d.Center, - ea3d.Normal, - ea3d.MajorAxis * ea3d.MajorRadius, - ea3d.MinorRadius / ea3d.MajorRadius, - 0, - Math.PI * 2); - //Ge椭圆角度就是Db椭圆的参数 - if (!ea3d.IsClosed()) - { - ell.StartAngle = ell.GetAngleAtParameter(ea3d.StartAngle); - ell.EndAngle = ell.GetAngleAtParameter(ea3d.EndAngle); - } - return ell; + ell.StartAngle = ell.GetAngleAtParameter(ea3d.StartAngle); + ell.EndAngle = ell.GetAngleAtParameter(ea3d.EndAngle); } + return ell; + } - #endregion EllipticalArc3d + #endregion EllipticalArc3d - #region NurbCurve3d + #region NurbCurve3d - /// - /// 将三维解析类Nurb曲线转换为实体类样条曲线 - /// - /// 三维解析类Nurb曲线 - /// 实体类样条曲线 - public static Spline ToCurve(this NurbCurve3d nc3d) + /// + /// 将三维解析类Nurb曲线转换为实体类样条曲线 + /// + /// 三维解析类Nurb曲线 + /// 实体类样条曲线 + public static Spline ToCurve(this NurbCurve3d nc3d) + { + Spline spl; + if (nc3d.HasFitData) { - Spline spl = null; - if (nc3d.HasFitData) + NurbCurve3dFitData fdata = nc3d.FitData; + if (fdata.TangentsExist) { - NurbCurve3dFitData fdata = nc3d.FitData; - if (fdata.TangentsExist) - { - spl = new Spline( - fdata.FitPoints, - fdata.StartTangent, - fdata.EndTangent, - nc3d.Order, - fdata.FitTolerance.EqualPoint); - } - else - { - spl = new Spline( - fdata.FitPoints, - nc3d.Order, - fdata.FitTolerance.EqualPoint); - } + spl = new Spline( + fdata.FitPoints, + fdata.StartTangent, + fdata.EndTangent, + nc3d.Order, + fdata.FitTolerance.EqualPoint); } else { - DoubleCollection knots = new DoubleCollection(); - foreach (double knot in nc3d.Knots) - knots.Add(knot); - - NurbCurve3dData ncdata = nc3d.DefinitionData; - spl = new Spline( - ncdata.Degree, - ncdata.Rational, - nc3d.IsClosed(), - ncdata.Periodic, - ncdata.ControlPoints, - knots, - ncdata.Weights, - Tolerance.Global.EqualPoint, - ncdata.Knots.Tolerance); + fdata.FitPoints, + nc3d.Order, + fdata.FitTolerance.EqualPoint); } - return spl; } + else + { + DoubleCollection knots = new(); + foreach (double knot in nc3d.Knots) + knots.Add(knot); + + NurbCurve3dData ncdata = nc3d.DefinitionData; + + spl = new Spline( + ncdata.Degree, + ncdata.Rational, + nc3d.IsClosed(), + ncdata.Periodic, + ncdata.ControlPoints, + knots, + ncdata.Weights, + Tolerance.Global.EqualPoint, + ncdata.Knots.Tolerance); + } + return spl; + } - #endregion NurbCurve3d - - #region PolylineCurve3d + #endregion NurbCurve3d - /// - /// 将三维解析类多段线转换为实体类三维多段线 - /// - /// 三维解析类多段线 - /// 实体类三维多段线 - public static Polyline3d ToCurve(this PolylineCurve3d pl3d) - { - Point3dCollection pnts = new Point3dCollection(); + #region PolylineCurve3d - for (int i = 0; i < pl3d.NumberOfControlPoints; i++) - { - pnts.Add(pl3d.ControlPointAt(i)); - } + /// + /// 将三维解析类多段线转换为实体类三维多段线 + /// + /// 三维解析类多段线 + /// 实体类三维多段线 + public static Polyline3d ToCurve(this PolylineCurve3d pl3d) + { + Point3dCollection pnts = new(); - bool closed = false; - int n = pnts.Count - 1; - if (pnts[0] == pnts[n]) - { - pnts.RemoveAt(n); - closed = true; - } - return new Polyline3d(Poly3dType.SimplePoly, pnts, closed); + for (int i = 0; i < pl3d.NumberOfControlPoints; i++) + { + pnts.Add(pl3d.ControlPointAt(i)); } - #endregion PolylineCurve3d + bool closed = false; + int n = pnts.Count - 1; + if (pnts[0] == pnts[n]) + { + pnts.RemoveAt(n); + closed = true; + } + return new Polyline3d(Poly3dType.SimplePoly, pnts, closed); } + + #endregion PolylineCurve3d } diff --git a/src/IFoxCAD.Cad/ExtensionMethod/CurveEx.cs b/src/IFoxCAD.Cad/ExtensionMethod/CurveEx.cs index 26fbd18..4056bc5 100644 --- a/src/IFoxCAD.Cad/ExtensionMethod/CurveEx.cs +++ b/src/IFoxCAD.Cad/ExtensionMethod/CurveEx.cs @@ -1,984 +1,662 @@ -using Autodesk.AutoCAD.DatabaseServices; -using Autodesk.AutoCAD.Geometry; -using IFoxCAD.Collections; -using System; -using System.Collections.Generic; -using System.Linq; - -namespace IFoxCAD.Cad +namespace IFoxCAD.Cad; + +/// +/// 实体类曲线扩展类 +/// +public static class CurveEx { /// - /// 实体类曲线扩展类 + /// 曲线长度 /// - public static class CurveEx + /// 曲线 + /// 长度 + public static double GetLength(this Curve curve) { - /// - /// 曲线长度 - /// - /// 曲线 - /// 长度 - public static double GetLength(this Curve curve) - { - return - curve.GetDistanceAtParameter(curve.EndParam); - } + return curve.GetDistanceAtParameter(curve.EndParam); + } - /// - /// 获取分割曲线集合 - /// - /// 曲线 - /// 打断参数表 - /// 打断后曲线的集合 - public static IEnumerable GetSplitCurves(this Curve curve, IEnumerable pars) + /// + /// 获取分割曲线集合 + /// + /// 曲线 + /// 打断参数表 + /// 打断后曲线的集合 + public static IEnumerable GetSplitCurves(this Curve curve, IEnumerable pars) + { + return + curve + .GetSplitCurves(new DoubleCollection(pars.ToArray())) + .Cast(); + } + + /// + /// 获取分割曲线集合 + /// + /// 曲线 + /// 打断点表 + /// 打断后曲线的集合 + public static IEnumerable GetSplitCurves(this Curve curve, IEnumerable points) + { + return + curve + .GetSplitCurves(new Point3dCollection(points.ToArray())) + .Cast(); + } + + /// + /// 获取曲线集所围成的封闭区域的曲线集,注意此函数不能处理平行边(两个点及两条线组成的闭合环) + /// + /// 曲线集合 + /// 所有的闭合环的曲线集合 + public static IEnumerable GetAllCycle(this IEnumerable curves) + { + // 新建图 + var graph = new Graph(); + foreach (var curve in curves) { - return - curve - .GetSplitCurves(new DoubleCollection(pars.ToArray())) - .Cast(); +#if NET35 + graph.AddEdge(curve.ToCurve3d()!); +#else + graph.AddEdge(curve.GetGeCurve()); +#endif } - - /// - /// 获取分割曲线集合 - /// - /// 曲线 - /// 打断点表 - /// 打断后曲线的集合 - public static IEnumerable GetSplitCurves(this Curve curve, IEnumerable points) + //新建 dfs + var dfs = new DepthFirst(); + // 查询全部的 闭合环 + dfs.FindAll(graph); + // 遍历闭合环的列表,将每个闭合环转换为实体曲线 + var res = new List(); + foreach (var item in dfs.Curve3ds) { - return - curve - .GetSplitCurves(new Point3dCollection(points.ToArray())) - .Cast(); + var curve = graph.GetCurves(item.ToList()).ToArray(); + var comcur = new CompositeCurve3d(curve).ToCurve(); + if (comcur is not null) + res.Add(comcur); } + return res; + } + /// + /// 曲线打断 + /// + /// 曲线列表 + /// 打断后的曲线列表 + public static List BreakCurve(this List curves) + { + var geCurves = new List(); // 存储曲线转换后的复合曲线 + var paramss = new List>(); // 存储每个曲线的交点参数值 - private struct EdgeItem : IEquatable + for (int i = 0; i < curves.Count; i++) { - public Edge Edge; - public bool Forward; - - public EdgeItem(Edge edge, bool forward) + var cc3d = curves[i].ToCompositeCurve3d(); + if (cc3d is not null) { - Edge = edge; - Forward = forward; + geCurves.Add(cc3d); + paramss.Add(new List()); } + } + + //var oldCurves = new List(); + var newCurves = new List(); + var cci3d = new CurveCurveIntersector3d(); - public CompositeCurve3d GetCurve() + for (int i = 0; i < curves.Count; i++) + { + var gc1 = geCurves[i]; + var pars1 = paramss[i]; //引用 + for (int j = i; j < curves.Count; j++) { - var cc3d = Edge.Curve; - if (Forward) - return cc3d; - else - { - cc3d = (CompositeCurve3d)cc3d.Clone(); - return cc3d.GetReverseParameterCurve() as CompositeCurve3d; - } - } + var gc2 = geCurves[j]; + var pars2 = paramss[j]; // 引用 - public bool Equals(EdgeItem other) - { - return - Edge == other.Edge && - Forward == other.Forward; - } + cci3d.Set(gc1, gc2, Vector3d.ZAxis); - public void FindRegion(List edges, List> regions) - { - var region = new LoopList(); - var edgeItem = this; - region.Add(edgeItem); - var edgeItem2 = this.GetNext(edges); - if (edgeItem2.Edge is not null) + for (int k = 0; k < cci3d.NumberOfIntersectionPoints; k++) { - bool hasList = false; - foreach (var edgeList2 in regions) - { - var node = edgeList2.GetNode(e => e.Equals(edgeItem)); - if (node is not null) - { - if (node.Next.Value.Equals(edgeItem2)) - { - hasList = true; - break; - } - } - } - if (!hasList) - { - while (edgeItem2.Edge is not null) - { - if (edgeItem2.Edge == edgeItem.Edge) - break; - region.Add(edgeItem2); - edgeItem2 = edgeItem2.GetNext(edges); - } - if (edgeItem2.Edge == edgeItem.Edge) - regions.Add(region); - } + var pars = cci3d.GetIntersectionParameters(k); + pars1.Add(pars[0]); // 引用修改会同步到源对象 + pars2.Add(pars[1]); // 引用修改会同步到源对象 } } - public EdgeItem GetNext(List edges) + if (pars1.Count > 0) { - Vector3d vec; - int next; - if (Forward) + var c3ds = gc1.GetSplitCurves(pars1); + if (c3ds.Count > 1) { - vec = Edge.GetEndVector(); - next = Edge.EndIndex; - } - else - { - vec = Edge.GetStartVector(); - next = Edge.StartIndex; - } - - EdgeItem item = new EdgeItem(); - Vector3d vec2, vec3 = new Vector3d(); - double angle = 0; - bool hasNext = false; - bool forward = false; - foreach (var edge in edges) - { - if (edge.IsNext(Edge, next, ref vec3, ref forward)) + foreach (var c3d in c3ds) { - if (hasNext) - { - var angle2 = vec.GetAngleTo(vec3, Vector3d.ZAxis); - if (angle2 < angle) - { - vec2 = vec3; - angle = angle2; - item.Edge = edge; - item.Forward = forward; - } - } - else + var c3dCur = c3d.ToCurve(); + if (c3dCur is not null) { - vec2 = vec3; - angle = vec.GetAngleTo(vec2, Vector3d.ZAxis); - item.Edge = edge; - item.Forward = forward; - hasNext = true; + c3dCur.SetPropertiesFrom(curves[i]); + newCurves.Add(c3dCur); } } + //oldCurves.Add(curves[i]); } - return item; - } - - public override string ToString() - { - return - Forward ? - string.Format("{0}-{1}", Edge.StartIndex, Edge.EndIndex) : - string.Format("{0}-{1}", Edge.EndIndex, Edge.StartIndex); } } - private class Edge - { - public CompositeCurve3d Curve; - public int StartIndex; - public int EndIndex; + return newCurves; + } - public Vector3d GetStartVector() - { - var inter = Curve.GetInterval(); - PointOnCurve3d poc = new PointOnCurve3d(Curve, inter.LowerBound); - return poc.GetDerivative(1); - } + //转换DBCurve为GeCurved - public Vector3d GetEndVector() - { - var inter = Curve.GetInterval(); - PointOnCurve3d poc = new PointOnCurve3d(Curve, inter.UpperBound); - return -poc.GetDerivative(1); - } + #region Curve - public bool IsNext(Edge edge, int index, ref Vector3d vec, ref bool forward) - { - if (edge != this) - { - if (StartIndex == index) - { - vec = GetStartVector(); - forward = true; - return true; - } - else if (EndIndex == index) - { - vec = GetEndVector(); - forward = false; - return true; - } - } - return false; - } - } - - public static List Topo(List curves) + /// + /// 将曲线转换为ge曲线,此函数将在未来淘汰,二惊加油 + /// + /// 曲线 + /// ge曲线 + public static Curve3d? ToCurve3d(this Curve curve) + { + return curve switch { - //首先按交点分解为Ge曲线集 - List geCurves = new List(); - List> paramss = new List>(); - - foreach (var curve in curves) - { - var cc3d = curve.ToCompositeCurve3d(); - if (cc3d is not null) - { - geCurves.Add(cc3d); - paramss.Add(new List()); - } - } - - List edges = new List(); - CurveCurveIntersector3d cci3d = new CurveCurveIntersector3d(); - List newCurves = new List(); - - for (int i = 0; i < curves.Count; i++) - { - CompositeCurve3d gc1 = geCurves[i]; - List pars1 = paramss[i]; - for (int j = i; j < curves.Count; j++) - { - CompositeCurve3d gc2 = geCurves[j]; - List pars2 = paramss[j]; + Line li => ToCurve3d(li), + Circle ci => ToCurve3d(ci), + Arc arc => ToCurve3d(arc), + Ellipse el => ToCurve3d(el), + Polyline pl => ToCurve3d(pl), + Polyline2d pl2 => ToCurve3d(pl2), + Polyline3d pl3 => ToCurve3d(pl3), + Spline sp => ToCurve3d(sp), + _ => null + }; + } - cci3d.Set(gc1, gc2, Vector3d.ZAxis); + /// + /// 将曲线转换为复合曲线 + /// + /// 曲线 + /// 复合曲线 + public static CompositeCurve3d? ToCompositeCurve3d(this Curve curve) + { + return curve switch + { + Line li => new CompositeCurve3d(new Curve3d[] { ToCurve3d(li) }), + Circle ci => new CompositeCurve3d(new Curve3d[] { ToCurve3d(ci) }), + Arc arc => new CompositeCurve3d(new Curve3d[] { ToCurve3d(arc) }), + Ellipse el => new CompositeCurve3d(new Curve3d[] { ToCurve3d(el) }), + Polyline pl => new CompositeCurve3d(new Curve3d[] { ToCurve3d(pl) }), + + Polyline2d pl2 => new CompositeCurve3d(new Curve3d[] { ToCurve3d(pl2)! }), + Polyline3d pl3 => new CompositeCurve3d(new Curve3d[] { ToCurve3d(pl3) }), + Spline sp => new CompositeCurve3d(new Curve3d[] { ToCurve3d(sp) }), + _ => null + }; + } - for (int k = 0; k < cci3d.NumberOfIntersectionPoints; k++) - { - double[] pars = cci3d.GetIntersectionParameters(k); - pars1.Add(pars[0]); - pars2.Add(pars[1]); - } - } + /// + /// 将曲线转换为Nurb曲线 + /// + /// 曲线 + /// Nurb曲线 + public static NurbCurve3d? ToNurbCurve3d(this Curve curve) + { + return curve switch + { + Line li => ToNurbCurve3d(li), + Circle ci => ToNurbCurve3d(ci), + Arc arc => ToNurbCurve3d(arc), + Ellipse el => ToNurbCurve3d(el), + Polyline pl => ToNurbCurve3d(pl), + Polyline2d pl2 => ToNurbCurve3d(pl2), + Polyline3d pl3 => ToNurbCurve3d(pl3), + Spline sp => ToNurbCurve3d(sp), + _ => null + }; + } - if (pars1.Count > 0) - { - List c3ds = gc1.GetSplitCurves(pars1); - if (c3ds.Count > 0) - { - edges.AddRange( - c3ds.Select(c => new Edge { Curve = c })); - } - else if (gc1.IsClosed()) - { - newCurves.Add(gc1.ToCurve()); - } - else - { - edges.Add(new Edge { Curve = gc1 }); - } - } - else if (gc1.IsClosed()) - { - newCurves.Add(gc1.ToCurve()); - } - } + #region Line - //构建边的邻接表 - var knots = new List(); - var nums = new List(); - var closedEdges = new List(); + /// + /// 将直线转换为ge直线 + /// + /// 直线 + /// ge直线 + public static LineSegment3d ToCurve3d(this Line line) + { + return new LineSegment3d(line.StartPoint, line.EndPoint); + } - foreach (var edge in edges) - { - if (edge.Curve.IsClosed()) - { - closedEdges.Add(edge); - } - else - { - if (knots.Contains(edge.Curve.StartPoint)) - { - edge.StartIndex = - knots.IndexOf(edge.Curve.StartPoint); - nums[edge.StartIndex]++; - } - else - { - knots.Add(edge.Curve.StartPoint); - nums.Add(1); - edge.StartIndex = knots.Count - 1; - } + /// + /// 将直线转换为Nurb曲线 + /// + /// 直线 + /// Nurb曲线 + public static NurbCurve3d ToNurbCurve3d(this Line line) + { + return new NurbCurve3d(ToCurve3d(line)); + } - if (knots.Contains(edge.Curve.EndPoint)) - { - edge.EndIndex = - knots.IndexOf(edge.Curve.EndPoint); - nums[edge.EndIndex]++; - } - else - { - knots.Add(edge.Curve.EndPoint); - nums.Add(1); - edge.EndIndex = knots.Count - 1; - } - } - } + #endregion Line - newCurves.AddRange(closedEdges.Select(e => e.Curve.ToCurve())); + #region Circle - edges = - edges - .Except(closedEdges) - .Where(e => nums[e.StartIndex] > 1 && nums[e.EndIndex] > 1) - .ToList(); + /// + /// 将圆转换为ge圆弧曲线 + /// + /// 圆 + /// ge圆弧曲线 + public static CircularArc3d ToCurve3d(this Circle cir) + { + return + new CircularArc3d( + cir.Center, + cir.Normal, + cir.Radius); + } - foreach (var edge in edges.Except(closedEdges)) - { - if (nums[edge.StartIndex] == 1 || nums[edge.EndIndex] == 1) - { - if (nums[edge.StartIndex] == 1 && nums[edge.EndIndex] == 1) - { - nums[edge.StartIndex] = 0; - nums[edge.EndIndex] = 0; - } - else - { - int next = -1; - if (nums[edge.StartIndex] == 1) - { - nums[edge.StartIndex] = 0; - nums[next = edge.EndIndex]--; - } - else - { - nums[edge.EndIndex] = 0; - nums[next = edge.StartIndex]--; - } - } - } - } + /// + /// 将圆转换为ge椭圆曲线 + /// + /// 圆 + /// ge椭圆曲线 + public static EllipticalArc3d ToEllipticalArc3d(this Circle cir) + { + return ToCurve3d(cir).ToEllipticalArc3d(); + } - List> regions = new List>(); - foreach (var edge in edges) - { - var edgeItem = new EdgeItem(edge, true); - edgeItem.FindRegion(edges, regions); - edgeItem = new EdgeItem(edge, false); - edgeItem.FindRegion(edges, regions); - } + /// + /// 将圆转换为Nurb曲线 + /// + /// 圆 + /// Nurb曲线 + public static NurbCurve3d ToNurbCurve3d(this Circle cir) + { + return new NurbCurve3d(ToEllipticalArc3d(cir)); + } - for (int i = 0; i < regions.Count; i++) - { - for (int j = i + 1; j < regions.Count;) - { - bool eq = false; - if (regions[i].Count == regions[j].Count) - { - var node = regions[i].First; - var curve = node.Value.Edge.Curve; - var node2 = regions[j].GetNode(e => e.Edge.Curve == curve); - if (eq = node2 is not null) - { - var b = node.Value.Forward; - var b2 = node2.Value.Forward; - for (int k = 1; k < regions[i].Count; k++) - { - node = node.GetNext(b); - node2 = node2.GetNext(b2); - if (node.Value.Edge.Curve != node2.Value.Edge.Curve) - { - eq = false; - break; - } - } - } - } - if (eq) - regions.RemoveAt(j); - else - j++; - } - } + #endregion Circle - foreach (var region in regions) - { - var cs3ds = - region - .Select(e => e.GetCurve()) - .ToArray(); - newCurves.Add(new CompositeCurve3d(cs3ds.ToArray()).ToCurve()); - } + #region Arc - return newCurves; - } + /// + /// 将圆弧转换为ge圆弧曲线 + /// + /// 圆弧 + /// ge圆弧曲线 + public static CircularArc3d ToCurve3d(this Arc arc) + { + Plane plane = new(arc.Center, arc.Normal); + + return + new CircularArc3d( + arc.Center, + arc.Normal, + plane.GetCoordinateSystem().Xaxis, + arc.Radius, + arc.StartAngle, + arc.EndAngle + ); + } - /// - /// 曲线打断 - /// - /// 曲线列表 - /// 打断后的曲线列表 - public static List BreakCurve(List curves) - { - List geCurves = new List(); - List> paramss = new List>(); + /// + /// 将圆弧转换为ge椭圆曲线 + /// + /// 圆弧 + /// ge椭圆曲线 + public static EllipticalArc3d ToEllipticalArc3d(this Arc arc) + { + return ToCurve3d(arc).ToEllipticalArc3d(); + } - foreach (var curve in curves) - { - var cc3d = curve.ToCompositeCurve3d(); - if (cc3d is not null) - { - geCurves.Add(cc3d); - paramss.Add(new List()); - } - } + /// + /// 将圆弧转换为三维Nurb曲线 + /// + /// 圆弧 + /// 三维Nurb曲线 + public static NurbCurve3d ToNurbCurve3d(this Arc arc) + { + return new NurbCurve3d(ToEllipticalArc3d(arc)); + } - List oldCurves = new List(); - List newCurves = new List(); - CurveCurveIntersector3d cci3d = new CurveCurveIntersector3d(); + #endregion Arc - for (int i = 0; i < curves.Count; i++) - { - CompositeCurve3d gc1 = geCurves[i]; - List pars1 = paramss[i]; - for (int j = i; j < curves.Count; j++) - { - CompositeCurve3d gc2 = geCurves[j]; - List pars2 = paramss[j]; + #region Ellipse - cci3d.Set(gc1, gc2, Vector3d.ZAxis); + /// + /// 将椭圆转换为三维ge椭圆曲线 + /// + /// 椭圆 + /// 三维ge椭圆曲线 + public static EllipticalArc3d ToCurve3d(this Ellipse ell) + { + return + new EllipticalArc3d( + ell.Center, + ell.MajorAxis.GetNormal(), + ell.MinorAxis.GetNormal(), + ell.MajorRadius, + ell.MinorRadius, + ell.StartParam, + ell.EndParam); + } - for (int k = 0; k < cci3d.NumberOfIntersectionPoints; k++) - { - double[] pars = cci3d.GetIntersectionParameters(k); - pars1.Add(pars[0]); - pars2.Add(pars[1]); - } - } + /// + /// 将椭圆转换为三维Nurb曲线 + /// + /// 椭圆 + /// 三维Nurb曲线 + public static NurbCurve3d ToNurbCurve3d(this Ellipse ell) + { + return new NurbCurve3d(ToCurve3d(ell)); + } - if (pars1.Count > 0) - { - List c3ds = gc1.GetSplitCurves(pars1); - if (c3ds.Count > 1) - { - foreach (CompositeCurve3d c3d in c3ds) - { - Curve c = c3d.ToCurve(); - if (c is not null) - { - c.SetPropertiesFrom(curves[i]); - newCurves.Add(c); - } - } - oldCurves.Add(curves[i]); - } - } - } - curves = oldCurves; - return newCurves; - } + #endregion Ellipse - //转换DBCurve为GeCurved + #region Spline - #region Curve + /// + /// 将样条曲线转换为三维Nurb曲线 + /// + /// 样条曲线 + /// 三维Nurb曲线 + public static NurbCurve3d ToCurve3d(this Spline spl) + { + NurbCurve3d nc3d; + NurbsData ndata = spl.NurbsData; + KnotCollection knots = new(); + foreach (Double knot in ndata.GetKnots()) + knots.Add(knot); - /// - /// 将曲线转换为ge曲线,此函数将在未来淘汰,二惊加油 - /// - /// 曲线 - /// ge曲线 - [Obsolete("请使用Cad自带的 GetGeCurve 函数!")] - public static Curve3d? ToCurve3d(this Curve curve) + if (ndata.Rational) { - return curve switch - { - Line li => ToCurve3d(li), - Circle ci => ToCurve3d(ci), - Arc arc => ToCurve3d(arc), - Ellipse el => ToCurve3d(el), - Polyline pl => ToCurve3d(pl), - Polyline2d pl2 => ToCurve3d(pl2), - Polyline3d pl3 => ToCurve3d(pl3), - Spline sp => ToCurve3d(sp), - _ => null - }; + nc3d = + new NurbCurve3d( + ndata.Degree, + knots, + ndata.GetControlPoints(), + ndata.GetWeights(), + ndata.Periodic); } - - /// - /// 将曲线转换为复合曲线 - /// - /// 曲线 - /// 复合曲线 - public static CompositeCurve3d? ToCompositeCurve3d(this Curve curve) + else { - return curve switch - { - Line li => new CompositeCurve3d(new Curve3d[] { ToCurve3d(li) }), - Circle ci => new CompositeCurve3d(new Curve3d[] { ToCurve3d(ci) }), - Arc arc => new CompositeCurve3d(new Curve3d[] { ToCurve3d(arc) }), - Ellipse el => new CompositeCurve3d(new Curve3d[] { ToCurve3d(el) }), - Polyline pl => new CompositeCurve3d(new Curve3d[] { ToCurve3d(pl) }), - - Polyline2d pl2 => new CompositeCurve3d(new Curve3d[] { ToCurve3d(pl2) }), - Polyline3d pl3 => new CompositeCurve3d(new Curve3d[] { ToCurve3d(pl3) }), - Spline sp => new CompositeCurve3d(new Curve3d[] { ToCurve3d(sp) }), - _ => null - }; + nc3d = + new NurbCurve3d( + ndata.Degree, + knots, + ndata.GetControlPoints(), + ndata.Periodic); } - /// - /// 将曲线转换为Nurb曲线 - /// - /// 曲线 - /// Nurb曲线 - public static NurbCurve3d? ToNurbCurve3d(this Curve curve) + if (spl.HasFitData) { - return curve switch - { - Line li => ToNurbCurve3d(li), - Circle ci => ToNurbCurve3d(ci), - Arc arc => ToNurbCurve3d(arc), - Ellipse el => ToNurbCurve3d(el), - Polyline pl => ToNurbCurve3d(pl), - Polyline2d pl2 => ToNurbCurve3d(pl2), - Polyline3d pl3 => ToNurbCurve3d(pl3), - Spline sp => ToNurbCurve3d(sp), - _ => null - }; + var fdata = spl.FitData; + var vec = new Vector3d(); + if (fdata.TangentsExist && (fdata.StartTangent != vec || fdata.EndTangent != vec)) + nc3d.SetFitData(fdata.GetFitPoints(), fdata.StartTangent, fdata.EndTangent); } + return nc3d; + } - #region Line + #endregion Spline - /// - /// 将直线转换为ge直线 - /// - /// 直线 - /// ge直线 - public static LineSegment3d ToCurve3d(this Line line) - { - return new LineSegment3d(line.StartPoint, line.EndPoint); - } + #region Polyline2d - /// - /// 将直线转换为Nurb曲线 - /// - /// 直线 - /// Nurb曲线 - public static NurbCurve3d ToNurbCurve3d(this Line line) + /// + /// 将二维多段线转换为三维ge曲线 + /// + /// 二维多段线 + /// 三维ge曲线 + public static Curve3d? ToCurve3d(this Polyline2d pl2d) + { + switch (pl2d.PolyType) { - return new NurbCurve3d(ToCurve3d(line)); + case Poly2dType.SimplePoly: + case Poly2dType.FitCurvePoly: + Polyline pl = new(); + pl.SetDatabaseDefaults(); + pl.ConvertFrom(pl2d, false); + return ToCurve3d(pl); + default: + return ToNurbCurve3d(pl2d); } - #endregion Line - - #region Circle - - /// - /// 将圆转换为ge圆弧曲线 - /// - /// 圆 - /// ge圆弧曲线 - public static CircularArc3d ToCurve3d(this Circle cir) - { - return - new CircularArc3d( - cir.Center, - cir.Normal, - cir.Radius); - } + //Polyline pl = new Polyline(); + //pl.ConvertFrom(pl2d, false); + //return ToCurve3d(pl); + } - /// - /// 将圆转换为ge椭圆曲线 - /// - /// 圆 - /// ge椭圆曲线 - public static EllipticalArc3d ToEllipticalArc3d(this Circle cir) + /// + /// 将二维多段线转换为三维Nurb曲线 + /// + /// 二维多段线 + /// 三维Nurb曲线 + public static NurbCurve3d? ToNurbCurve3d(this Polyline2d pl2d) + { + switch (pl2d.PolyType) { - return ToCurve3d(cir).ToEllipticalArc3d(); + case Poly2dType.SimplePoly: + case Poly2dType.FitCurvePoly: + Polyline pl = new(); + pl.SetDatabaseDefaults(); + pl.ConvertFrom(pl2d, false); + return ToNurbCurve3d(pl); + + default: + return ToCurve3d(pl2d.Spline); } + } - /// - /// 将圆转换为Nurb曲线 - /// - /// 圆 - /// Nurb曲线 - public static NurbCurve3d ToNurbCurve3d(this Circle cir) + /// + /// 将二维多段线转换为三维ge多段线 + /// + /// 二维多段线 + /// 三维ge多段线 + public static PolylineCurve3d ToPolylineCurve3d(this Polyline2d pl) + { + Point3dCollection pnts = new(); + foreach (Vertex2d ver in pl) { - return new NurbCurve3d(ToEllipticalArc3d(cir)); + pnts.Add(ver.Position); } + return new PolylineCurve3d(pnts); + } - #endregion Circle - - #region Arc + #endregion Polyline2d - /// - /// 将圆弧转换为ge圆弧曲线 - /// - /// 圆弧 - /// ge圆弧曲线 - public static CircularArc3d ToCurve3d(this Arc arc) - { - Plane plane = new Plane(arc.Center, arc.Normal); - - return - new CircularArc3d( - arc.Center, - arc.Normal, - plane.GetCoordinateSystem().Xaxis, - arc.Radius, - arc.StartAngle, - arc.EndAngle - ); - } + #region Polyline3d - /// - /// 将圆弧转换为ge椭圆曲线 - /// - /// 圆弧 - /// ge椭圆曲线 - public static EllipticalArc3d ToEllipticalArc3d(this Arc arc) - { - return ToCurve3d(arc).ToEllipticalArc3d(); - } - - /// - /// 将圆弧转换为三维Nurb曲线 - /// - /// 圆弧 - /// 三维Nurb曲线 - public static NurbCurve3d ToNurbCurve3d(this Arc arc) + /// + /// 将三维多段线转换为三维曲线 + /// + /// 三维多段线 + /// 三维曲线 + public static Curve3d ToCurve3d(this Polyline3d pl3d) + { + return pl3d.PolyType switch { - return new NurbCurve3d(ToEllipticalArc3d(arc)); - } - - #endregion Arc + Poly3dType.SimplePoly => ToPolylineCurve3d(pl3d), + _ => ToNurbCurve3d(pl3d), + }; + } - #region Ellipse + /// + /// 将三维多段线转换为三维Nurb曲线 + /// + /// 三维多段线 + /// 三维Nurb曲线 + public static NurbCurve3d ToNurbCurve3d(this Polyline3d pl3d) + { + return ToCurve3d(pl3d.Spline); + } - /// - /// 将椭圆转换为三维ge椭圆曲线 - /// - /// 椭圆 - /// 三维ge椭圆曲线 - public static EllipticalArc3d ToCurve3d(this Ellipse ell) + /// + /// 将三维多段线转换为三维ge多段线 + /// + /// 三维多段线 + /// 三维ge多段线 + public static PolylineCurve3d ToPolylineCurve3d(this Polyline3d pl) + { + Point3dCollection pnts = new(); + foreach (ObjectId id in pl) { - return - new EllipticalArc3d( - ell.Center, - ell.MajorAxis.GetNormal(), - ell.MinorAxis.GetNormal(), - ell.MajorRadius, - ell.MinorRadius, - ell.StartParam, - ell.EndParam); + PolylineVertex3d ver = (PolylineVertex3d)id.GetObject(OpenMode.ForRead); + pnts.Add(ver.Position); } + return new PolylineCurve3d(pnts); + } - /// - /// 将椭圆转换为三维Nurb曲线 - /// - /// 椭圆 - /// 三维Nurb曲线 - public static NurbCurve3d ToNurbCurve3d(this Ellipse ell) - { - return new NurbCurve3d(ToCurve3d(ell)); - } + #endregion Polyline3d - #endregion Ellipse + #region Polyline - #region Spline + /// + /// 多段线转换为复合曲线 + /// + /// 多段线对象 + /// 复合曲线对象 + public static CompositeCurve3d ToCurve3d(this Polyline pl) + { + List c3ds = new(); - /// - /// 将样条曲线转换为三维Nurb曲线 - /// - /// 样条曲线 - /// 三维Nurb曲线 - public static NurbCurve3d ToCurve3d(this Spline spl) + for (int i = 0; i < pl.NumberOfVertices; i++) { - NurbCurve3d nc3d; - NurbsData ndata = spl.NurbsData; - KnotCollection knots = new KnotCollection(); - foreach (Double knot in ndata.GetKnots()) - knots.Add(knot); - - if (ndata.Rational) + switch (pl.GetSegmentType(i)) { - nc3d = - new NurbCurve3d( - ndata.Degree, - knots, - ndata.GetControlPoints(), - ndata.GetWeights(), - ndata.Periodic); - } - else - { - nc3d = - new NurbCurve3d( - ndata.Degree, - knots, - ndata.GetControlPoints(), - ndata.Periodic); - } - - if (spl.HasFitData) - { - var fdata = spl.FitData; - var vec = new Vector3d(); - if (fdata.TangentsExist && (fdata.StartTangent != vec || fdata.EndTangent != vec)) - nc3d.SetFitData(fdata.GetFitPoints(), fdata.StartTangent, fdata.EndTangent); - } - return nc3d; - } - - #endregion Spline + case SegmentType.Line: + c3ds.Add(pl.GetLineSegmentAt(i)); + break; - #region Polyline2d - - /// - /// 将二维多段线转换为三维ge曲线 - /// - /// 二维多段线 - /// 三维ge曲线 - public static Curve3d ToCurve3d(this Polyline2d pl2d) - { - switch (pl2d.PolyType) - { - case Poly2dType.SimplePoly: - case Poly2dType.FitCurvePoly: - Polyline pl = new Polyline(); - pl.ConvertFrom(pl2d, false); - return ToCurve3d(pl); + case SegmentType.Arc: + c3ds.Add(pl.GetArcSegmentAt(i)); + break; default: - return ToNurbCurve3d(pl2d); + break; } - - //Polyline pl = new Polyline(); - //pl.ConvertFrom(pl2d, false); - //return ToCurve3d(pl); } + return new CompositeCurve3d(c3ds.ToArray()); + } - /// - /// 将二维多段线转换为三维Nurb曲线 - /// - /// 二维多段线 - /// 三维Nurb曲线 - public static NurbCurve3d ToNurbCurve3d(this Polyline2d pl2d) + /// + /// 多段线转换为Nurb曲线 + /// + /// 多段线 + /// Nurb曲线 + public static NurbCurve3d? ToNurbCurve3d(this Polyline pl) + { + NurbCurve3d? nc3d = null; + for (int i = 0; i < pl.NumberOfVertices; i++) { - switch (pl2d.PolyType) + NurbCurve3d? nc3dtemp = null; + switch (pl.GetSegmentType(i)) { - case Poly2dType.SimplePoly: - case Poly2dType.FitCurvePoly: - Polyline pl = new Polyline(); - pl.ConvertFrom(pl2d, false); - return ToNurbCurve3d(pl); + case SegmentType.Line: + nc3dtemp = new NurbCurve3d(pl.GetLineSegmentAt(i)); + break; + + case SegmentType.Arc: + nc3dtemp = pl.GetArcSegmentAt(i).ToNurbCurve3d(); + break; default: - return ToCurve3d(pl2d.Spline); + break; } - } - - /// - /// 将二维多段线转换为三维ge多段线 - /// - /// 二维多段线 - /// 三维ge多段线 - public static PolylineCurve3d ToPolylineCurve3d(this Polyline2d pl) - { - var pnts = new Point3dCollection(); - foreach (Vertex2d ver in pl) - pnts.Add(ver.Position); - return new PolylineCurve3d(pnts); - } - #endregion Polyline2d - - #region Polyline3d - - /// - /// 将三维多段线转换为三维曲线 - /// - /// 三维多段线 - /// 三维曲线 - public static Curve3d ToCurve3d(this Polyline3d pl3d) - { - return pl3d.PolyType switch - { - Poly3dType.SimplePoly => ToPolylineCurve3d(pl3d), - _ => ToNurbCurve3d(pl3d), - }; - } - - /// - /// 将三维多段线转换为三维Nurb曲线 - /// - /// 三维多段线 - /// 三维Nurb曲线 - public static NurbCurve3d ToNurbCurve3d(this Polyline3d pl3d) - { - return ToCurve3d(pl3d.Spline); - } - - /// - /// 将三维多段线转换为三维ge多段线 - /// - /// 三维多段线 - /// 三维ge多段线 - public static PolylineCurve3d ToPolylineCurve3d(this Polyline3d pl) - { - Point3dCollection pnts = new Point3dCollection(); - foreach (ObjectId id in pl) + if (nc3d is null) { - PolylineVertex3d ver = (PolylineVertex3d)id.GetObject(OpenMode.ForRead); - pnts.Add(ver.Position); + nc3d = nc3dtemp; } - return new PolylineCurve3d(pnts); - } - - #endregion Polyline3d - - #region Polyline - - /// - /// 多段线转换为复合曲线 - /// - /// 多段线对象 - /// 复合曲线对象 - public static CompositeCurve3d ToCurve3d(this Polyline pl) - { - List c3ds = new List(); - - for (int i = 0; i < pl.NumberOfVertices; i++) + else if (nc3dtemp is not null) { - switch (pl.GetSegmentType(i)) - { - case SegmentType.Line: - c3ds.Add(pl.GetLineSegmentAt(i)); - break; - - case SegmentType.Arc: - c3ds.Add(pl.GetArcSegmentAt(i)); - break; - - default: - break; - } + nc3d.JoinWith(nc3dtemp); } - return new CompositeCurve3d(c3ds.ToArray()); } + return nc3d; + } - /// - /// 多段线转换为Nurb曲线 - /// - /// 多段线 - /// Nurb曲线 - public static NurbCurve3d ToNurbCurve3d(this Polyline pl) - { - NurbCurve3d nc3d = null; - for (int i = 0; i < pl.NumberOfVertices; i++) - { - NurbCurve3d nc3dtemp = null; - switch (pl.GetSegmentType(i)) - { - case SegmentType.Line: - nc3dtemp = new NurbCurve3d(pl.GetLineSegmentAt(i)); - break; + /// + /// 为优化多段线倒角 + /// + /// 优化多段线 + /// 顶点索引号 + /// 倒角半径 + /// 倒角类型 + public static void ChamferAt(this Polyline polyline, int index, double radius, bool isFillet) + { + if (index < 1 || index > polyline.NumberOfVertices - 2) + throw new System.Exception("错误的索引号"); - case SegmentType.Arc: - nc3dtemp = pl.GetArcSegmentAt(i).ToNurbCurve3d(); - break; + if (SegmentType.Line != polyline.GetSegmentType(index - 1) || + SegmentType.Line != polyline.GetSegmentType(index)) + throw new System.Exception("非直线段不能倒角"); - default: - break; - } - if (nc3d is null) + //获取当前索引号的前后两段直线,并组合为Ge复合曲线 + Curve3d[] c3ds = + new Curve3d[] { - nc3d = nc3dtemp; - } - else if (nc3dtemp is not null) - { - nc3d.JoinWith(nc3dtemp); - } - } - return nc3d; - } - - /// - /// 为优化多段线倒角 - /// - /// 优化多段线 - /// 顶点索引号 - /// 倒角半径 - /// 倒角类型 - public static void ChamferAt(this Polyline polyline, int index, double radius, bool isFillet) - { - if (index < 1 || index > polyline.NumberOfVertices - 2) - throw new System.Exception("错误的索引号"); - - if (polyline.GetSegmentType(index - 1) != SegmentType.Line || - polyline.GetSegmentType(index) != SegmentType.Line) - throw new System.Exception("非直线段不能倒角"); - - //获取当前索引号的前后两段直线,并组合为Ge复合曲线 - var c3ds = new Curve3d[] - { - polyline.GetLineSegmentAt(index - 1), - polyline.GetLineSegmentAt(index) - }; - var cc3d = new CompositeCurve3d(c3ds); - - //试倒直角 - //子曲线的个数有三种情况: - //1、=3时倒角方向正确 - //2、=2时倒角方向相反 - //3、=0或为直线时失败 - c3ds = cc3d.GetTrimmedOffset + polyline.GetLineSegmentAt(index - 1), + polyline.GetLineSegmentAt(index) + }; + CompositeCurve3d cc3d = new(c3ds); + + //试倒直角 + //子曲线的个数有三种情况: + //1、=3时倒角方向正确 + //2、=2时倒角方向相反 + //3、=0或为直线时失败 + c3ds = + cc3d.GetTrimmedOffset ( radius, Vector3d.ZAxis, OffsetCurveExtensionType.Chamfer ); - if (c3ds.Length > 0 && c3ds[0] is CompositeCurve3d newcc3d) - { - c3ds = newcc3d.GetCurves(); - if (c3ds.Length == 3) - { - c3ds = cc3d.GetTrimmedOffset - ( - -radius, - Vector3d.ZAxis, - OffsetCurveExtensionType.Chamfer - ); - if (c3ds.Length == 0 || c3ds[0] is LineSegment3d) - throw new System.Exception("倒角半径过大"); - } - else if (c3ds.Length == 2) - { - radius = -radius; - } + if (c3ds.Length > 0 && c3ds[0] is CompositeCurve3d) + { + var newcc3d = c3ds[0] as CompositeCurve3d; + c3ds = newcc3d!.GetCurves(); + if (c3ds.Length == 3) + { + c3ds = cc3d.GetTrimmedOffset + ( + -radius, + Vector3d.ZAxis, + OffsetCurveExtensionType.Chamfer + ); + if (c3ds.Length == 0 || c3ds[0] is LineSegment3d) + throw new System.Exception("倒角半径过大"); } - else + else if (c3ds.Length == 2) { - throw new System.Exception("倒角半径过大"); + radius = -radius; } - - //GetTrimmedOffset会生成倒角+偏移,故先反方向倒角,再倒回 - c3ds = cc3d.GetTrimmedOffset - ( - -radius, - Vector3d.ZAxis, - OffsetCurveExtensionType.Extend - ); - var type = isFillet ? OffsetCurveExtensionType.Fillet : OffsetCurveExtensionType.Chamfer; - c3ds = c3ds[0].GetTrimmedOffset - ( - radius, - Vector3d.ZAxis, - type - ); - - //将结果Ge曲线转为Db曲线,并将相关的数值反映到原曲线 - var plTemp = c3ds[0].ToCurve() as Polyline; - polyline.RemoveVertexAt(index); - polyline.AddVertexAt(index, plTemp.GetPoint2dAt(1), plTemp.GetBulgeAt(1), 0, 0); - polyline.AddVertexAt(index + 1, plTemp.GetPoint2dAt(2), 0, 0, 0); + } + else + { + throw new System.Exception("倒角半径过大"); } - #endregion Polyline - - #endregion Curve + //GetTrimmedOffset会生成倒角+偏移,故先反方向倒角,再倒回 + c3ds = cc3d.GetTrimmedOffset + ( + -radius, + Vector3d.ZAxis, + OffsetCurveExtensionType.Extend + ); + OffsetCurveExtensionType type = + isFillet ? + OffsetCurveExtensionType.Fillet : OffsetCurveExtensionType.Chamfer; + c3ds = c3ds[0].GetTrimmedOffset + ( + radius, + Vector3d.ZAxis, + type + ); + + //将结果Ge曲线转为Db曲线,并将相关的数值反映到原曲线 + var plTemp = c3ds[0].ToCurve() as Polyline; + if (plTemp == null) + return; + polyline.RemoveVertexAt(index); + polyline.AddVertexAt(index, plTemp.GetPoint2dAt(1), plTemp.GetBulgeAt(1), 0, 0); + polyline.AddVertexAt(index + 1, plTemp.GetPoint2dAt(2), 0, 0, 0); } -} + + #endregion Polyline + + #endregion Curve +} \ No newline at end of file diff --git a/src/IFoxCAD.Cad/ExtensionMethod/DBDictionaryEx.cs b/src/IFoxCAD.Cad/ExtensionMethod/DBDictionaryEx.cs index d4fba82..2ae0879 100644 --- a/src/IFoxCAD.Cad/ExtensionMethod/DBDictionaryEx.cs +++ b/src/IFoxCAD.Cad/ExtensionMethod/DBDictionaryEx.cs @@ -1,389 +1,372 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using Autodesk.AutoCAD.DatabaseServices; -using Autodesk.AutoCAD.Geometry; - -namespace IFoxCAD.Cad +namespace IFoxCAD.Cad; +using Group = Autodesk.AutoCAD.DatabaseServices.Group; + +/// +/// 字典扩展类 +/// +public static class DBDictionaryEx { /// - /// 字典扩展类 + /// 获取字典里的全部对象 /// - public static class DBDictionaryEx + /// 对象类型的泛型 + /// 字典 + /// 事务 + /// 对象迭代器 + public static IEnumerable GetAllObjects(this DBDictionary dict, DBTrans? trans = null) where T : DBObject { - /// - /// 获取字典里的全部对象 - /// - /// 对象类型的泛型 - /// 字典 - /// 事务 - /// 对象迭代器 - public static IEnumerable GetAllObjects(this DBDictionary dict, Transaction trans = null) where T : DBObject + trans ??= DBTrans.Top; + foreach (DBDictionaryEntry e in dict) { - trans ??= DBTrans.Top.Transaction; - foreach (DBDictionaryEntry e in dict) - { - yield return trans.GetObject(e.Value, OpenMode.ForRead) as T; - } + var ent = trans.GetObject(e.Value); + if (ent is not null) + yield return ent; } + } - /// - /// 获取字典内指定key的对象 - /// - /// 对象类型的泛型 - /// 字典 - /// 事务 - /// 指定的键值 - /// T 类型的对象 - public static T GetAt(this DBDictionary dict, string key, Transaction trans = null) where T : DBObject + /// + /// 获取字典内指定key的对象 + /// + /// 对象类型的泛型 + /// 字典 + /// 事务 + /// 指定的键值 + /// T 类型的对象 + public static T? GetAt(this DBDictionary dict, string key, DBTrans? trans = null) where T : DBObject + { + trans ??= DBTrans.Top; + if (dict.Contains(key)) { - trans ??= DBTrans.Top.Transaction; - if (dict.Contains(key)) - { - ObjectId id = dict.GetAt(key); - if (!id.IsNull) - { - return trans.GetObject(id, OpenMode.ForRead) as T; - } - } - return null; + ObjectId id = dict.GetAt(key); + if (!id.IsNull) + return trans.GetObject(id); } + return null; + } - /// - /// 添加条目(键值对)到字典 - /// - /// 对象类型 - /// 字典 - /// 事务 - /// 键 - /// 值 - public static void SetAt(this DBDictionary dict, string key, T obj, Transaction tr = null) where T : DBObject + /// + /// 添加条目(键值对)到字典 + /// + /// 对象类型 + /// 字典 + /// 事务 + /// 键 + /// 值 + public static void SetAt(this DBDictionary dict, string key, T obj, Transaction? trans = null) where T : DBObject + { + trans ??= DBTrans.Top.Transaction; + using (dict.ForWrite()) { - tr ??= DBTrans.Top.Transaction; - using (dict.ForWrite()) - { - dict.SetAt(key, obj); - tr.AddNewlyCreatedDBObject(obj, true); - } + dict.SetAt(key, obj); + trans.AddNewlyCreatedDBObject(obj, true); } + } - #region XRecord + #region XRecord - /// - /// 从字典中获取扩展数据 - /// - /// 字典 - /// 键值 - /// 扩展数据 - public static XRecordDataList GetXRecord(this DBDictionary dict, string key) - { - Xrecord rec = dict.GetAt(key); - if (rec is not null) - return rec.Data; - return null; - } + /// + /// 从字典中获取扩展数据 + /// + /// 字典 + /// 键值 + /// 扩展数据 + public static XRecordDataList? GetXRecord(this DBDictionary dict, string key) + { + Xrecord? rec = dict.GetAt(key); + if (rec is not null) + return rec.Data; + return null; + } - /// - /// 保存扩展数据到字典 - /// - /// 扩展数据 - /// 字典 - /// 键值 - public static void SetXRecord(this DBDictionary dict, string key, XRecordDataList rb) - { - using var data = new Xrecord { Data = rb }; - dict.SetAt(key, data); - } - #endregion - - /// - /// 获取扩展字典 - /// - /// 对象 - /// 事务 - /// 扩展字典对象 - public static DBDictionary GetXDictionary(this DBObject obj, Transaction trans = null) + /// + /// 保存扩展数据到字典 + /// + /// 扩展数据 + /// 字典 + /// 键值 + public static void SetXRecord(this DBDictionary dict, string key, XRecordDataList rb) + { + using var data = new Xrecord { Data = rb }; + dict.SetAt(key, data); + } + #endregion + + /// + /// 获取扩展字典 + /// + /// 对象 + /// 事务 + /// 扩展字典对象 + public static DBDictionary? GetXDictionary(this DBObject obj, DBTrans? trans = null) + { + trans ??= DBTrans.Top; + ObjectId id = obj.ExtensionDictionary; + if (id.IsNull) { - trans ??= DBTrans.Top.Transaction; - ObjectId id = obj.ExtensionDictionary; - if (id.IsNull) + using (obj.ForWrite()) { - using (obj.ForWrite()) - { - obj.CreateExtensionDictionary(); - } - - id = obj.ExtensionDictionary; + obj.CreateExtensionDictionary(); } - return id.GetObject(tr: trans); + + id = obj.ExtensionDictionary; } + return id.GetObject(tr: trans); + } - #region 数据表 + #region 数据表 - /// - /// 创建数据表 - /// - /// 原数据类型的字典 - /// 表元素(二维数组) - /// 数据表 - public static DataTable CreateDataTable(Dictionary colTypes, object[,] content) + /// + /// 创建数据表 + /// + /// 原数据类型的字典 + /// 表元素(二维数组) + /// 数据表 + public static DataTable CreateDataTable(Dictionary colTypes, object[,] content) + { + DataTable table = new(); + foreach (var t in colTypes) + table.AppendColumn(t.Value, t.Key); + var ncol = colTypes.Count; + var nrow = content.GetLength(0); + var types = new CellType[ncol]; + colTypes.Values.CopyTo(types, 0); + for (int i = 0; i < nrow; i++) { - DataTable table = new(); - foreach (var t in colTypes) - table.AppendColumn(t.Value, t.Key); - var ncol = colTypes.Count; - var nrow = content.GetLength(0); - var types = new CellType[ncol]; - colTypes.Values.CopyTo(types, 0); - for (int i = 0; i < nrow; i++) + DataCellCollection row = new(); + for (int j = 0; j < ncol; j++) { - DataCellCollection row = new(); - for (int j = 0; j < ncol; j++) - { - var cell = new DataCell(); - cell.SetValue(types[j], content[i, j]); - row.Add(cell); - } - table.AppendRow(row, true); + var cell = new DataCell(); + cell.SetValue(types[j], content[i, j]); + row.Add(cell); } - return table; + table.AppendRow(row, true); } + return table; + } - /// - /// 设定单元格数据 - /// - /// 单元格 - /// 类型 - /// 数据 - public static void SetValue(this DataCell cell, CellType type, object value) - { + /// + /// 设定单元格数据 + /// + /// 单元格 + /// 类型 + /// 数据 + public static void SetValue(this DataCell cell, CellType type, object value) + { - switch (type) - { - case CellType.Bool: - cell.SetBool((bool)value); - break; + switch (type) + { + case CellType.Bool: + cell.SetBool((bool)value); + break; - case CellType.CharPtr: - cell.SetString((string)value); - break; + case CellType.CharPtr: + cell.SetString((string)value); + break; - case CellType.Integer: - cell.SetInteger((int)value); - break; + case CellType.Integer: + cell.SetInteger((int)value); + break; - case CellType.Double: - cell.SetDouble((double)value); - break; + case CellType.Double: + cell.SetDouble((double)value); + break; - case CellType.ObjectId: - cell.SetObjectId((ObjectId)value); - break; + case CellType.ObjectId: + cell.SetObjectId((ObjectId)value); + break; - case CellType.Point: - cell.SetPoint((Point3d)value); - break; + case CellType.Point: + cell.SetPoint((Point3d)value); + break; - case CellType.Vector: - cell.SetVector((Vector3d)value); - break; + case CellType.Vector: + cell.SetVector((Vector3d)value); + break; - case CellType.HardOwnerId: - cell.SetHardOwnershipId((ObjectId)value); - break; + case CellType.HardOwnerId: + cell.SetHardOwnershipId((ObjectId)value); + break; - case CellType.HardPtrId: - cell.SetHardPointerId((ObjectId)value); - break; + case CellType.HardPtrId: + cell.SetHardPointerId((ObjectId)value); + break; - case CellType.SoftOwnerId: - cell.SetSoftOwnershipId((ObjectId)value); - break; + case CellType.SoftOwnerId: + cell.SetSoftOwnershipId((ObjectId)value); + break; - case CellType.SoftPtrId: - cell.SetSoftPointerId((ObjectId)value); - break; - } + case CellType.SoftPtrId: + cell.SetSoftPointerId((ObjectId)value); + break; } - #endregion - - #region 子字典 - /// - /// 获取子字典 - /// - /// 根字典 - /// 事务 - /// 是否创建子字典 - /// 键值列表 - /// 字典 - public static DBDictionary GetSubDictionary(this DBDictionary dict, bool createSubDictionary, IEnumerable dictNames, Transaction trans = null) + } + #endregion + + #region 子字典 + /// + /// 获取子字典 + /// + /// 根字典 + /// 事务 + /// 是否创建子字典 + /// 键值列表 + /// 字典 + public static DBDictionary? GetSubDictionary(this DBDictionary dict, bool createSubDictionary, IEnumerable dictNames, DBTrans? trans = null) + { + DBDictionary? newdict = null; + trans ??= DBTrans.Top; + if (createSubDictionary) { - trans ??= DBTrans.Top.Transaction; - if (createSubDictionary) - { - using (dict.ForWrite()) - dict.TreatElementsAsHard = true; + using (dict.ForWrite()) + dict.TreatElementsAsHard = true; - foreach (string name in dictNames) - { - if (dict.Contains(name)) - { - dict = dict.GetAt(name, trans); - } - else - { - DBDictionary subDict = new(); - dict.SetAt(name, subDict, trans); - dict = subDict; - dict.TreatElementsAsHard = true; - } - } - } - else + foreach (string name in dictNames) { - foreach (string name in dictNames) + if (dict!.Contains(name)) { - if (dict.Contains(name)) - dict = dict.GetAt(name, trans); - else - return null; + newdict = dict.GetAt(name, trans); } - } - - return dict; - } - - - ///// - ///// 获取对象扩展字典的子字典 - ///// - ///// 对象 - ///// 事务 - ///// 是否创建子字典 - ///// 键值列表 - ///// 字典 - //public static DBDictionary GetSubDictionary(this DBObject obj, bool createSubDictionary, params string[] dictNames) - //{ - // return obj.GetXDictionary().GetSubDictionary(createSubDictionary, dictNames); - //} - - #endregion - - #region 组字典 - /// - /// 添加编组 - /// - /// 组名 - /// 实体Id集合 - /// 编组Id - public static ObjectId AddGroup(this DBDictionary dict, string name, ObjectIdCollection ids) - { - if (dict.Contains(name)) - { - return ObjectId.Null; - } - else - { - using (dict.ForWrite()) + else { - Group g = new(); - g.Append(ids); - dict.SetAt(name, g); - DBTrans.Top.Transaction.AddNewlyCreatedDBObject(g, true); - return g.ObjectId; + DBDictionary subDict = new(); + dict.SetAt(name, subDict, trans); + newdict = subDict; + newdict.TreatElementsAsHard = true; } } } - - /// - /// 添加编组 - /// - /// 组名 - /// 实体Id集合 - /// 编组Id - public static ObjectId AddGroup(this DBDictionary dict, string name, IEnumerable ids) + else { - if (dict.Contains(name)) + foreach (string name in dictNames) { - return ObjectId.Null; + if (dict.Contains(name)) + newdict = dict.GetAt(name, trans); + else + return null; } - return dict.AddGroup(name, new ObjectIdCollection(ids.ToArray())); } + return newdict; + } - /// - /// 按选择条件获取编组集合 - /// - /// 选择条件,过滤函数 - /// g.NumEntities < 2);]]> - /// 编组集合 - public static IEnumerable GetGroups(this DBDictionary dict, Func func) + + ///// + ///// 获取对象扩展字典的子字典 + ///// + ///// 对象 + ///// 事务 + ///// 是否创建子字典 + ///// 键值列表 + ///// 字典 + //public static DBDictionary GetSubDictionary(this DBObject obj, bool createSubDictionary, params string[] dictNames) + //{ + // return obj.GetXDictionary().GetSubDictionary(createSubDictionary, dictNames); + //} + + #endregion + + #region 组字典 + /// + /// 添加编组 + /// + /// 组名 + /// 实体Id集合 + /// 编组Id + public static ObjectId AddGroup(this DBDictionary dict, string name, ObjectIdCollection ids) + { + if (dict.Contains(name)) + return ObjectId.Null; + + using (dict.ForWrite()) { - return - dict - .GetAllObjects() - .Where(func); + Group g = new(); + g.Append(ids); + dict.SetAt(name, g); + DBTrans.Top.Transaction.AddNewlyCreatedDBObject(g, true); + return g.ObjectId; } + } + + /// + /// 添加编组 + /// + /// 组名 + /// 实体Id集合 + /// 编组Id + public static ObjectId AddGroup(this DBDictionary dict, string name, IEnumerable ids) + { + if (dict.Contains(name)) + return ObjectId.Null; + + return dict.AddGroup(name, new ObjectIdCollection(ids.ToArray())); + } - /// - /// 返回实体的所在编组的集合 - /// - /// 图元实体 - /// 编组集合 - public static IEnumerable GetGroups(this Entity ent) + + /// + /// 按选择条件获取编组集合 + /// + /// 选择条件,过滤函数 + /// g.NumEntities < 2);]]> + /// 编组集合 + public static IEnumerable GetGroups(this DBDictionary dict, Func func) + { + return dict.GetAllObjects() + .Where(func); + } + + /// + /// 返回实体的所在编组的集合 + /// + /// 图元实体 + /// 编组集合 + public static IEnumerable GetGroups(this Entity ent) + { + return ent.GetPersistentReactorIds() + .Cast() + .Select(id => id.GetObject()) + .OfType(); + } + + /// + /// 移除所有的空组 + /// + /// 被移除编组的名称集合 + public static List RemoveNullGroup(this DBDictionary dict) + { + var groups = dict.GetGroups(g => g.NumEntities < 2); + List names = new(); + foreach (Group g in groups) { - return - ent.GetPersistentReactorIds() - .Cast() - .Select(id => id.GetObject()) - .OfType(); + names.Add(g.Name); + using (g.ForWrite()) + g.Erase(); } + return names; + } - /// - /// 移除所有的空组 - /// - /// 被移除编组的名称集合 - public static List RemoveNullGroup(this DBDictionary dict) + /// + /// 移除所有空组 + /// + /// 过滤条件,过滤要删除的组名的规则函数 + /// RemoveNullGroup(g => g.StartsWith("hah")) + /// 被移除编组的名称集合 + public static List RemoveNullGroup(this DBDictionary dict, Func func) + { + var groups = dict.GetGroups(g => g.NumEntities < 2); + List names = new(); + foreach (Group g in groups) { - var groups = dict.GetGroups(g => g.NumEntities < 2); - List names = new(); - foreach (Group g in groups) + if (func(g.Name)) { names.Add(g.Name); using (g.ForWrite()) - { g.Erase(); - } - } - return names; - } - - /// - /// 移除所有空组 - /// - /// 过滤条件,过滤要删除的组名的规则函数 - /// RemoveNullGroup(g => g.StartsWith("hah")) - /// 被移除编组的名称集合 - public static List RemoveNullGroup(this DBDictionary dict, Func func) - { - var groups = dict.GetGroups(g => g.NumEntities < 2); - List names = new(); - foreach (Group g in groups) - { - if (func(g.Name)) - { - names.Add(g.Name); - using (g.ForWrite()) - { - g.Erase(); - } - } } - return names; } - #endregion + return names; + } + #endregion @@ -404,5 +387,4 @@ public static List RemoveNullGroup(this DBDictionary dict, Func +/// 实体对象扩展类 +/// +public static class DBObjectEx { + #region Xdata扩展 /// - /// 实体对象扩展类 + /// 删除扩展数据 /// - public static class DBObjectEx + /// 对象实例 + /// 应用程序名称 + /// 要删除数据的组码 + public static void RemoveXData(this DBObject obj, string appName, DxfCode dxfCode) { - #region Xdata扩展 - /// - /// 删除扩展数据 - /// - /// 对象实例 - /// 应用程序名称 - /// 要删除数据的组码 - public static void RemoveXData(this DBObject obj, string appName, DxfCode dxfCode) + XDataList data = obj.XData; + var indexlst = new List(); + bool flag = false; + for (int i = 0; i < data.Count; i++) { - XDataList data = obj.XData; - var indexlst = new List(); - bool flag = false; - for (int i = 0; i < data.Count; i++) + if (data[i].TypeCode == (int)DxfCode.ExtendedDataRegAppName && data[i].Value.ToString() == appName) { - if (data[i].TypeCode == (int)DxfCode.ExtendedDataRegAppName && data[i].Value.ToString() == appName) - { - flag = true; - } - if (flag) - { - if (data[i].TypeCode == (int)DxfCode.ExtendedDataRegAppName && data[i].Value.ToString() != appName) - break; - if (data[i].TypeCode == (int)dxfCode) - { - indexlst.Add(i); - } - } + flag = true; } - foreach (var item in indexlst) + if (flag) { - data.RemoveAt(item); + if (data[i].TypeCode == (int)DxfCode.ExtendedDataRegAppName && data[i].Value.ToString() != appName) + break; + if (data[i].TypeCode == (int)dxfCode) + { + indexlst.Add(i); + } } + } + foreach (var item in indexlst) + { + data.RemoveAt(item); + } - using (obj.ForWrite()) - { - obj.XData = data; - } + using (obj.ForWrite()) + { + obj.XData = data; } - /// - /// 修改扩展数据 - /// - /// 对象实例 - /// 应用程序名称 - /// 要修改数据的组码 - /// 新的数据 - public static void ChangeXData(this DBObject obj, string appName, DxfCode dxfCode, object newvalue) + } + /// + /// 修改扩展数据 + /// + /// 对象实例 + /// 应用程序名称 + /// 要修改数据的组码 + /// 新的数据 + public static void ChangeXData(this DBObject obj, string appName, DxfCode dxfCode, object newvalue) + { + XDataList data = obj.XData; + bool flag = false; + for (int i = 0; i < data.Count; i++) { - XDataList data = obj.XData; - bool flag = false; - for (int i = 0; i < data.Count; i++) + if (data[i].TypeCode == (int)DxfCode.ExtendedDataRegAppName && data[i].Value.ToString() == appName) { - if (data[i].TypeCode == (int)DxfCode.ExtendedDataRegAppName && data[i].Value.ToString() == appName) - { - flag = true; - } - if (flag) + flag = true; + } + if (flag) + { + if (data[i].TypeCode == (int)DxfCode.ExtendedDataRegAppName && data[i].Value.ToString() != appName) + break; + if (data[i].TypeCode == (int)dxfCode) { - if (data[i].TypeCode == (int)DxfCode.ExtendedDataRegAppName && data[i].Value.ToString() != appName) - break; - if (data[i].TypeCode == (int)dxfCode) - { - data[i] = new TypedValue((int)dxfCode, newvalue); - } + data[i] = new TypedValue((int)dxfCode, newvalue); } } + } - using (obj.ForWrite()) - { - obj.XData = data; - } + using (obj.ForWrite()) + { + obj.XData = data; } - #endregion + } + #endregion - #region 读写模式切换 + #region 读写模式切换 - /// - /// 实体自动管理读写函数 - /// - /// 实体类型 - /// 实体对象 - /// 操作委托 - public static void ForWrite(this T obj, Action action) where T : DBObject + /// + /// 实体自动管理读写函数 + /// + /// 实体类型 + /// 实体对象 + /// 操作委托 + public static void ForWrite(this T obj, Action action) where T : DBObject + { + var _isNotifyEnabled = obj.IsNotifyEnabled; + var _isWriteEnabled = obj.IsWriteEnabled; + if (_isNotifyEnabled) { - var _isNotifyEnabled = obj.IsNotifyEnabled; - var _isWriteEnabled = obj.IsWriteEnabled; - if (_isNotifyEnabled) - { - obj.UpgradeFromNotify(); - } - else if (!_isWriteEnabled) - { - obj.UpgradeOpen(); - } - action?.Invoke(obj); - if (_isNotifyEnabled) - { - obj.DowngradeToNotify(_isWriteEnabled); - } - else if (!_isWriteEnabled) - { - obj.DowngradeOpen(); - } + obj.UpgradeFromNotify(); } - - /// - /// 打开模式提权 - /// - /// 实体对象 - /// 提权类对象 - public static UpgradeOpenManager ForWrite(this DBObject obj) + else if (!_isWriteEnabled) { - return new UpgradeOpenManager(obj); + obj.UpgradeOpen(); } - - /// - /// 提权类 - /// - public class UpgradeOpenManager : IDisposable + action?.Invoke(obj); + if (_isNotifyEnabled) + { + obj.DowngradeToNotify(_isWriteEnabled); + } + else if (!_isWriteEnabled) { - private readonly DBObject _obj; - private readonly bool _isNotifyEnabled; - private readonly bool _isWriteEnabled; + obj.DowngradeOpen(); + } + } - internal UpgradeOpenManager(DBObject obj) - { - _obj = obj; - _isNotifyEnabled = _obj.IsNotifyEnabled; - _isWriteEnabled = _obj.IsWriteEnabled; - if (_isNotifyEnabled) - _obj.UpgradeFromNotify(); - else if (!_isWriteEnabled) - _obj.UpgradeOpen(); - } + /// + /// 打开模式提权 + /// + /// 实体对象 + /// 提权类对象 + public static UpgradeOpenManager ForWrite(this DBObject obj) + { + return new UpgradeOpenManager(obj); + } - #region IDisposable 成员 + /// + /// 提权类 + /// + public class UpgradeOpenManager : IDisposable + { + private readonly DBObject _obj; + private readonly bool _isNotifyEnabled; + private readonly bool _isWriteEnabled; - /// - /// 注销函数 - /// - public void Dispose() - { - if (_isNotifyEnabled) - _obj.DowngradeToNotify(_isWriteEnabled); - else if (!_isWriteEnabled) - _obj.DowngradeOpen(); - GC.SuppressFinalize(this); - } + internal UpgradeOpenManager(DBObject obj) + { + _obj = obj; + _isNotifyEnabled = _obj.IsNotifyEnabled; + _isWriteEnabled = _obj.IsWriteEnabled; + if (_isNotifyEnabled) + _obj.UpgradeFromNotify(); + else if (!_isWriteEnabled) + _obj.UpgradeOpen(); + } - #endregion IDisposable 成员 + #region IDisposable 成员 + + /// + /// 注销函数 + /// + public void Dispose() + { + if (_isNotifyEnabled) + _obj.DowngradeToNotify(_isWriteEnabled); + else if (!_isWriteEnabled) + _obj.DowngradeOpen(); + GC.SuppressFinalize(this); } - #endregion + + #endregion IDisposable 成员 } + #endregion } diff --git a/src/IFoxCAD.Cad/ExtensionMethod/DatabaseEx.cs b/src/IFoxCAD.Cad/ExtensionMethod/DatabaseEx.cs new file mode 100644 index 0000000..dff7a9e --- /dev/null +++ b/src/IFoxCAD.Cad/ExtensionMethod/DatabaseEx.cs @@ -0,0 +1,23 @@ +namespace IFoxCAD.Cad; + +public static class DatabaseEx +{ + /// + /// 后台开图文字偏移处理 + /// 0x01 此方案利用前台数据库进行处理 + /// 0x02 当关闭所有前台文档时,会出现无时,不能使用(惊惊没有测试过此状态) + /// 0x03 当关闭所有前台文档时,如何发送命令呢?那就是利用跨进程通讯 + /// + /// 后台打开的数据库 + /// 处理后台的任务 + public static void DBTextDeviation(this Database backstageOpenDwg, Action action) + { + var wdb = HostApplicationServices.WorkingDatabase; + if (wdb != null) + { + HostApplicationServices.WorkingDatabase = backstageOpenDwg; + action?.Invoke(); + HostApplicationServices.WorkingDatabase = wdb; + } + } +} \ No newline at end of file diff --git a/src/IFoxCAD.Cad/ExtensionMethod/EditorEx.cs b/src/IFoxCAD.Cad/ExtensionMethod/EditorEx.cs index 9745eb9..d1cc6a8 100644 --- a/src/IFoxCAD.Cad/ExtensionMethod/EditorEx.cs +++ b/src/IFoxCAD.Cad/ExtensionMethod/EditorEx.cs @@ -1,752 +1,1100 @@ -using Autodesk.AutoCAD.ApplicationServices; -using Autodesk.AutoCAD.DatabaseServices; -using Autodesk.AutoCAD.EditorInput; -using Autodesk.AutoCAD.Geometry; -using Autodesk.AutoCAD.Runtime; -using System; -using System.Collections.Generic; -using System.Runtime.InteropServices; -using System.Linq; - -namespace IFoxCAD.Cad +namespace IFoxCAD.Cad; + +/// +/// 命令行扩展类 +/// +public static class EditorEx { + #region 选择集 /// - /// 命令行扩展类 + /// 选择穿过一个点的对象 /// - public static class EditorEx + /// 命令行对象 + /// 点 + /// 过滤器 + /// 选择集结果类 + public static PromptSelectionResult SelectAtPoint(this Editor editor, Point3d point, SelectionFilter? filter = default) { - #region 选择集 - /// - /// 选择穿过一个点的对象 - /// - /// 命令行对象 - /// 点 - /// 过滤器 - /// 选择集结果类 - public static PromptSelectionResult SelectAtPoint(this Editor editor, Point3d point, SelectionFilter filter = default) - { - return editor.SelectCrossingWindow(point, point, filter); - } + return editor.SelectCrossingWindow(point, point, filter); + } - /// - /// 根据线宽创建图层选择集 - /// - /// 命令行对象 - /// 线宽 - /// 图层选择集 - public static SelectionSet SelectByLineWeight(this Editor editor, LineWeight lineWeight) - { - OpFilter filter = new OpEqual(370, lineWeight); + /// + /// 根据线宽创建图层选择集 + /// + /// 命令行对象 + /// 线宽 + /// 图层选择集 + public static SelectionSet SelectByLineWeight(this Editor editor, LineWeight lineWeight) + { + OpFilter filter = new OpEqual(370, lineWeight); - var lays = - DBTrans.Top.LayerTable - .GetRecords() - .Where(ltr => ltr.LineWeight == lineWeight) - .Select(ltr => ltr.Name) - .ToArray(); + var lays = + DBTrans.Top.LayerTable + .GetRecords() + .Where(ltr => ltr.LineWeight == lineWeight) + .Select(ltr => ltr.Name) + .ToArray(); - if (lays.Length > 0) - { - filter = - new OpOr - { + if (lays.Length > 0) + { + filter = + new OpOr + { filter, new OpAnd { { 8, string.Join(",", lays) }, { 370, LineWeight.ByLayer } } - }; - } - - PromptSelectionResult res = editor.SelectAll(filter); - return res.Value; + }; } - #endregion - #region Info + PromptSelectionResult res = editor.SelectAll(filter); + return res.Value; + } - /// - /// 带错误提示对话框的打印信息函数 - /// - /// 带格式项的字符串 - /// 指定格式化的对象数组 - public static void StreamMessage(string format, params object[] args) + public static PromptSelectionResult? SSGet(this Editor editor, string? mode = null, SelectionFilter? filter = null, string[]? messages = null, Dictionary? keywords = null) + { + var pso = new PromptSelectionOptions(); + PromptSelectionResult? ss = null; + if (mode is not null) { - StreamMessage(string.Format(format, args)); - }// + mode = mode.ToUpper(); + pso.SinglePickInSpace = mode.Contains(":A"); + pso.RejectObjectsFromNonCurrentSpace = mode.Contains(":C"); + pso.AllowDuplicates = mode.Contains(":D"); + pso.SelectEverythingInAperture = mode.Contains(":E"); + pso.RejectObjectsOnLockedLayers = mode.Contains(":L"); + pso.PrepareOptionalDetails = mode.Contains(":N"); + pso.SingleOnly = mode.Contains(":S"); + pso.RejectPaperspaceViewport = mode.Contains(":V"); + pso.AllowSubSelections = mode.Contains("-A"); + pso.ForceSubSelections = mode.Contains("-F"); - /// - /// 带错误提示对话框的打印信息函数 - /// - /// 打印信息 - public static void StreamMessage(string message) + } + if (messages is not null) { - try - { - if (HasEditor()) - WriteMessage(message); - else - InfoMessageBox(message); - } - catch (System.Exception ex) - { - Message(ex); - } - }// + pso.MessageForAdding = messages[0]; + pso.MessageForRemoval = messages[1]; + } - /// - /// 异常信息对话框 - /// - /// 异常 - public static void Message(System.Exception ex) + if (keywords is not null) { - try - { - System.Windows.Forms.MessageBox.Show( - ex.ToString(), - "Error", - System.Windows.Forms.MessageBoxButtons.OK, - System.Windows.Forms.MessageBoxIcon.Error); - } - catch - { - } - }// + foreach (var keyword in keywords.Keys) + pso.Keywords.Add(keyword); + if (pso.MessageForRemoval is null) + pso.MessageForAdding = "选择对象"; + pso.MessageForAdding += $"[{string.Join(" / ", keywords.Keys.ToArray())}]"; + pso.KeywordInput += (s, e) => { + if (keywords.ContainsKey(e.Input)) + keywords[e.Input].Invoke(); + }; - /// - /// 提示信息对话框 - /// - /// 对话框的标题 - /// 对话框文本 - public static void InfoMessageBox(string caption, string message) + } + try { - try - { - System.Windows.Forms.MessageBox.Show( - message, - caption, - System.Windows.Forms.MessageBoxButtons.OK, - System.Windows.Forms.MessageBoxIcon.Information); - } - catch (System.Exception ex) - { - Message(ex); - } - }// - - /// - /// 提示信息对话框 - /// - /// 对话框的标题 - /// 带格式化项的对话框文本 - /// 指定格式化的对象数组 - public static void InfoMessageBox(string caption, string format, params object[] args) + if (filter is not null) + ss = editor.GetSelection(pso, filter); + else + ss = editor.GetSelection(pso); + } + catch (Autodesk.AutoCAD.Runtime.Exception e) { - InfoMessageBox(caption, string.Format(format, args)); + + editor.WriteMessage($"\nKey is {e.Message}"); } + return ss; + } - /// - /// 提示信息对话框,默认标题为NFox.Cad - /// - /// 对话框文本 - public static void InfoMessageBox(string message) - { - InfoMessageBox("NFox.Cad", message); - }// - /// - /// 提示信息对话框 - /// - /// 带格式化项的对话框文本 - /// 指定格式化的对象数组 - public static void InfoMessageBox(string format, params object[] args) - { - InfoMessageBox(string.Format(format, args)); - }// + /* + * //定义选择集选项 + * var pso = new PromptSelectionOptions + * { + * AllowDuplicates = false, //重复选择 + * }; + * + * //getai遍历全图选择块有用到 + * var dic = new Dictionary() { + * { "Z,全部同名", ()=> { + * getai = BlockHelper.EnumAttIdentical.AllBlockName; + * SendEsc.Esc(); + * }}, + * { "X,动态块显示", ()=> { + * getai = BlockHelper.EnumAttIdentical.Display; + * }}, + * { "V,属性值-默认", ()=> { + * getai = BlockHelper.EnumAttIdentical.DisplayAndTagText; + * }}, + * //允许以下操作,相同的会加入前面的 + * //{ "V,属性值-默认|X,啊啊啊啊", ()=> { + * + * //}}, + * }; + * pso.SsgetAddKeys(dic); + * + * //创建选择集过滤器,只选择块对象 + * var filList = new TypedValue[] { new TypedValue((int)DxfCode.Start, "INSERT") }; + * var filter = new SelectionFilter(filList); + * ssPsr = ed.GetSelection(pso, filter); + */ - /// - /// 命令行打印字符串 - /// - /// 字符串 - public static void WriteMessage(string message) + /// + /// 添加选择集关键字和回调 + /// + /// 选择集配置 + /// 关键字,回调委托 + /// + public static void SsgetAddKeys(this PromptSelectionOptions pso, + Dictionary dicActions) + { + Dictionary tmp = new(); + // 后缀名的|号切割,移除掉,组合成新的加入tmp + for (int i = dicActions.Count - 1; i >= 0; i--) { - try + var pair = dicActions.ElementAt(i); + var key = pair.Key; + var keySp = key.Split('|'); + if (keySp.Length < 2) + continue; + + for (int j = 0; j < keySp.Length; j++) { - if (Acceptable()) - Application.DocumentManager.MdiActiveDocument.Editor.WriteMessage("\n" + message); + var item = keySp[j]; + // 防止多个后缀通过|符越过词典约束同名 + // 后缀(key)含有,而且Action(value)不同,就把Action(value)累加到后面. + if (dicActions.ContainsKey(item)) + { + if (dicActions[item] != dicActions[key]) + dicActions[item] += dicActions[key]; + } else - return; + tmp.Add(item, dicActions[key]); } - catch (System.Exception ex) + dicActions.Remove(key); + } + + foreach (var item in tmp) + dicActions.Add(item.Key, item.Value); + + //去除关键字重复的,把重复的执行动作移动到前面 + for (int i = 0; i < dicActions.Count; i++) + { + var pair1 = dicActions.ElementAt(i); + var key1 = pair1.Key; + + for (int j = dicActions.Count - 1; j > i; j--) { - Message(ex); + var pair2 = dicActions.ElementAt(j); + var key2 = pair2.Key; + + if (key1.Split(',')[0] == key2.Split(',')[0]) + { + if (dicActions[key1] != dicActions[key2]) + dicActions[key1] += dicActions[key2]; + dicActions.Remove(key2); + } } - }// + } - /// - /// 命令行打印字符串 - /// - /// 带格式化项的文本 - /// 指定格式化的对象数组 - public static void WriteMessage(string format, params object[] args) + foreach (var item in dicActions) { - WriteMessage(string.Format(format, args)); + var keySplitS = item.Key.Split(new string[] { ",", "|" }, StringSplitOptions.RemoveEmptyEntries); + for (int i = 0; i < keySplitS.Length; i += 2) + pso.Keywords.Add(keySplitS[i], keySplitS[i], + keySplitS[i + 1] + "(" + keySplitS[i] + ")"); } - /// - /// 判断是否有活动的编辑器对象 - /// - /// 有,没有 - public static bool HasEditor() + //回调的时候我想用Dict的O(1)索引, + //但是此函数内进行new Dictionary() 在函数栈释放的时候,它被释放掉了. + //因此 dicActions 参数的生命周期 + tmp = new(dicActions); + dicActions.Clear(); + foreach (var item in tmp) + dicActions.Add(item.Key.Split(',')[0], item.Value); + + var keyWords = pso.Keywords; + //从选择集命令中显示关键字 + pso.MessageForAdding = keyWords.GetDisplayString(true); + //关键字回调事件 ssget关键字 + pso.KeywordInput += (sender, e) => { + dicActions[e.Input].Invoke(); + }; + } + + + + + + + //#region 即时选择样板 + + ///// + ///// 即时选择,框选更新关键字 + ///// + //public static void SelectTest() + //{ + // Env.Editor.WriteMessage("\n[白嫖工具]--测试"); + // //激活选中事件 + // Env.Editor.SelectionAdded += SelectTest_SelectionAdded; + // //初始化坐标系 + // Env.Editor.CurrentUserCoordinateSystem = Matrix3d.Identity; + + // //创建过滤器 + // var sf = new OpEqual(0, "arc"); + // var pso = new PromptSelectionOptions + // { + // MessageForAdding = "\n请选择对象:" + // }; + + // pso.Keywords.Add("Z"); + // pso.Keywords.Add("X"); + // pso.Keywords.Add("Q"); + // //注册关键字 + // pso.KeywordInput += SelectTest_KeywordInput; + // try + // { + // //用户选择 + // var psr = Env.Editor.GetSelection(pso, sf); + // //处理代码 + + + // } + // catch (Exception ex)//捕获关键字 + // { + // if (ex.Message == "XuError") + // { + // //关闭关键字事件 + // pso.KeywordInput -= SelectTest_KeywordInput; + // //关闭选中事件 + // Env.Editor.SelectionAdded -= SelectTest_SelectionAdded; + // //重新调用自身 + // ZengLiangYuanJiao(); + // } + // } + // //关闭关键字事件 + // pso.KeywordInput -= SelectTest_KeywordInput; + // //关闭选中事件 + // Env.Editor.SelectionAdded -= SelectTest_SelectionAdded; + //} + + ///// + ///// 即时选择 + ///// + ///// + ///// + //private static void SelectTest_SelectionAdded(object sender, SelectionAddedEventArgs e) + //{ + // //关闭选中事件 + // Env.Editor.SelectionAdded -= SelectTest_SelectionAdded; + // using (var tr = new DBTrans()) + // { + // //处理代码 + // for (int i = 0; i < e.AddedObjects.Count; i++) + // { + + + // //处理完移除已处理的对象 + // e.Remove(i); + // } + // } + // //激活选中事件 + // Env.Editor.SelectionAdded += SelectTest_SelectionAdded; + //} + + ///// + ///// 关键字响应 + ///// + ///// + ///// + //private static void SelectTest_KeywordInput(object sender, SelectionTextInputEventArgs e) + //{ + // //获取关键字 + // switch (e.Input) + // { + // case "Z": + // { + // break; + // } + // case "X": + // { + // break; + // } + + // case "Q": + // { + // break; + // } + // } + // //抛出异常,用于更新提示信息 + // throw new ArgumentException("XuError"); + //} + + + //#endregion + #endregion + + #region Info + + /// + /// 带错误提示对话框的打印信息函数 + /// + /// 带格式项的字符串 + /// 指定格式化的对象数组 + public static void StreamMessage(string format, params object[] args) + { + StreamMessage(string.Format(format, args)); + } + + /// + /// 带错误提示对话框的打印信息函数 + /// + /// 打印信息 + public static void StreamMessage(string message) + { + try { - return Application.DocumentManager.MdiActiveDocument is not null - && Application.DocumentManager.Count != 0 - && Application.DocumentManager.MdiActiveDocument.Editor is not null; - }// + if (HasEditor()) + WriteMessage(message); + else + InfoMessageBox(message); + } + catch (System.Exception ex) + { + Message(ex); + } + } - /// - /// 判断是否可以打印字符串 - /// - /// 可以打印,不可以打印 - public static bool Acceptable() + /// + /// 异常信息对话框 + /// + /// 异常 + public static void Message(System.Exception ex) + { + try { - return HasEditor() - && !Application.DocumentManager.MdiActiveDocument.Editor.IsDragging; - }// + System.Windows.Forms.MessageBox.Show( + ex.ToString(), + "Error", + System.Windows.Forms.MessageBoxButtons.OK, + System.Windows.Forms.MessageBoxIcon.Error); + } + catch + { + } + } + + /// + /// 提示信息对话框 + /// + /// 对话框的标题 + /// 对话框文本 + public static void InfoMessageBox(string caption, string message) + { + try + { + System.Windows.Forms.MessageBox.Show( + message, + caption, + System.Windows.Forms.MessageBoxButtons.OK, + System.Windows.Forms.MessageBoxIcon.Information); + } + catch (System.Exception ex) + { + Message(ex); + } + } - #endregion Info + /// + /// 提示信息对话框 + /// + /// 对话框的标题 + /// 带格式化项的对话框文本 + /// 指定格式化的对象数组 + public static void InfoMessageBox(string caption, string format, params object[] args) + { + InfoMessageBox(caption, string.Format(format, args)); + } + + /// + /// 提示信息对话框,默认标题为NFox.Cad + /// + /// 对话框文本 + public static void InfoMessageBox(string message) + { + InfoMessageBox("NFox.Cad", message); + } - #region 画矢量线 + /// + /// 提示信息对话框 + /// + /// 带格式化项的对话框文本 + /// 指定格式化的对象数组 + public static void InfoMessageBox(string format, params object[] args) + { + InfoMessageBox(string.Format(format, args)); + } - /// - /// 根据点表返回矢量线的列表 - /// - /// 点表 - /// 是否闭合, 为闭合, 为不闭合 - /// - public static List GetLines(IEnumerable pnts, bool isClosed) + /// + /// 命令行打印字符串 + /// + /// 字符串 + public static void WriteMessage(string message) + { + try + { + if (Acceptable()) + Application.DocumentManager.MdiActiveDocument.Editor.WriteMessage("\n" + message); + else + return; + } + catch (System.Exception ex) { + Message(ex); + } + } - var itor = pnts.GetEnumerator(); - if (!itor.MoveNext()) - return new List(); + /// + /// 命令行打印字符串 + /// + /// 带格式化项的文本 + /// 指定格式化的对象数组 + public static void WriteMessage(string format, params object[] args) + { + WriteMessage(string.Format(format, args)); + } - List values = new(); + /// + /// 判断是否有活动的编辑器对象 + /// + /// 有,没有 + public static bool HasEditor() + { + return Application.DocumentManager.MdiActiveDocument is not null + && Application.DocumentManager.Count != 0 + && Application.DocumentManager.MdiActiveDocument.Editor is not null; + } - TypedValue tvFirst = new((int)LispDataType.Point2d, itor.Current); - TypedValue tv1; - TypedValue tv2 = tvFirst; + /// + /// 判断是否可以打印字符串 + /// + /// 可以打印,不可以打印 + public static bool Acceptable() + { + return HasEditor() + && !Application.DocumentManager.MdiActiveDocument.Editor.IsDragging; + } - while (itor.MoveNext()) - { - tv1 = tv2; - tv2 = new TypedValue((int)LispDataType.Point2d, itor.Current); - values.Add(tv1); - values.Add(tv2); - } + #endregion Info - if (isClosed) - { - values.Add(tv2); - values.Add(tvFirst); - } + #region 画矢量线 - return values; - } + /// + /// 根据点表返回矢量线的列表 + /// + /// 点表 + /// 是否闭合, 为闭合, 为不闭合 + /// + public static List GetLines(IEnumerable pnts, bool isClosed) + { - /// - /// 画矢量线 - /// - /// 编辑器对象 - /// 点表 - /// 颜色码 - /// 是否闭合, 为闭合, 为不闭合 - public static void DrawVectors(this Editor editor, IEnumerable pnts, short colorIndex, bool isClosed) - { - var rlst = - new LispList { { LispDataType.Int16, colorIndex } }; - rlst.AddRange(GetLines(pnts, isClosed)); - editor.DrawVectors(rlst, editor.CurrentUserCoordinateSystem); - } + var itor = pnts.GetEnumerator(); + if (!itor.MoveNext()) + return new List(); + + List values = new(); + + TypedValue tvFirst = new((int)LispDataType.Point2d, itor.Current); + TypedValue tv1; + TypedValue tv2 = tvFirst; - /// - /// 画矢量线 - /// - /// 编辑器对象 - /// 点表 - /// 颜色码 - public static void DrawVectors(this Editor editor, IEnumerable pnts, short colorIndex) + while (itor.MoveNext()) { - editor.DrawVectors(pnts, colorIndex, false); + tv1 = tv2; + tv2 = new TypedValue((int)LispDataType.Point2d, itor.Current); + values.Add(tv1); + values.Add(tv2); } - /// - /// 用矢量线画近似圆(正多边形) - /// - /// 编辑器对象 - /// 点表 - /// 颜色码 - /// 半径 - /// 多边形边的个数 - public static void DrawCircles(this Editor editor, IEnumerable pnts, short colorIndex, double radius, int numEdges) + if (isClosed) { - var rlst = - new LispList { { LispDataType.Int16, colorIndex } }; + values.Add(tv2); + values.Add(tvFirst); + } - foreach (Point2d pnt in pnts) - { - Vector2d vec = Vector2d.XAxis * radius; - double angle = Math.PI * 2 / numEdges; + return values; + } - List tpnts = new() - { - pnt + vec - }; - for (int i = 1; i < numEdges; i++) - { - tpnts.Add(pnt + vec.RotateBy(angle * i)); - } + /// + /// 画矢量线 + /// + /// 编辑器对象 + /// 点表 + /// 颜色码 + /// 是否闭合, 为闭合, 为不闭合 + public static void DrawVectors(this Editor editor, IEnumerable pnts, short colorIndex, bool isClosed) + { + var rlst = + new LispList { { LispDataType.Int16, colorIndex } }; + rlst.AddRange(GetLines(pnts, isClosed)); + editor.DrawVectors(rlst, editor.CurrentUserCoordinateSystem); + } - rlst.AddRange(GetLines(tpnts, true)); - } - editor.DrawVectors(rlst, editor.CurrentUserCoordinateSystem); - } + /// + /// 画矢量线 + /// + /// 编辑器对象 + /// 点表 + /// 颜色码 + public static void DrawVectors(this Editor editor, IEnumerable pnts, short colorIndex) + { + editor.DrawVectors(pnts, colorIndex, false); + } - /// - /// 用矢量线画近似圆(正多边形) - /// - /// 编辑器对象 - /// 点 - /// 颜色码 - /// 半径 - /// 多边形边的个数 - public static void DrawCircle(this Editor editor, Point2d pnt, short colorIndex, double radius, int numEdges) + /// + /// 用矢量线画近似圆(正多边形) + /// + /// 编辑器对象 + /// 点表 + /// 颜色码 + /// 半径 + /// 多边形边的个数 + public static void DrawCircles(this Editor editor, IEnumerable pnts, short colorIndex, double radius, int numEdges) + { + var rlst = + new LispList { { LispDataType.Int16, colorIndex } }; + + foreach (Point2d pnt in pnts) { Vector2d vec = Vector2d.XAxis * radius; double angle = Math.PI * 2 / numEdges; - List pnts = new() + List tpnts = new() { pnt + vec }; for (int i = 1; i < numEdges; i++) { - pnts.Add(pnt + vec.RotateBy(angle * i)); + tpnts.Add(pnt + vec.RotateBy(angle * i)); } - editor.DrawVectors(pnts, colorIndex, true); + rlst.AddRange(GetLines(tpnts, true)); } + editor.DrawVectors(rlst, editor.CurrentUserCoordinateSystem); + } - #endregion - - #region 矩阵 + /// + /// 用矢量线画近似圆(正多边形) + /// + /// 编辑器对象 + /// 点 + /// 颜色码 + /// 半径 + /// 多边形边的个数 + public static void DrawCircle(this Editor editor, Point2d pnt, short colorIndex, double radius, int numEdges) + { + Vector2d vec = Vector2d.XAxis * radius; + double angle = Math.PI * 2 / numEdges; - /// - /// 获取UCS到WCS的矩阵 - /// - /// 命令行对象 - /// 变换矩阵 - public static Matrix3d GetMatrixFromUcsToWcs(this Editor editor) + List pnts = new() { - return editor.CurrentUserCoordinateSystem; - } - - /// - /// 获取WCS到UCS的矩阵 - /// - /// 命令行对象 - /// 变换矩阵 - public static Matrix3d GetMatrixFromWcsToUcs(this Editor editor) + pnt + vec + }; + for (int i = 1; i < numEdges; i++) { - return editor.CurrentUserCoordinateSystem.Inverse(); + pnts.Add(pnt + vec.RotateBy(angle * i)); } - /// - /// 获取MDCS(模型空间)到WCS的矩阵 - /// - /// 命令行对象 - /// 变换矩阵 - public static Matrix3d GetMatrixFromMDcsToWcs(this Editor editor) - { - Matrix3d mat; - using ViewTableRecord vtr = editor.GetCurrentView(); - mat = Matrix3d.PlaneToWorld(vtr.ViewDirection); - mat = Matrix3d.Displacement(vtr.Target - Point3d.Origin) * mat; - return Matrix3d.Rotation(-vtr.ViewTwist, vtr.ViewDirection, vtr.Target) * mat; - } + editor.DrawVectors(pnts, colorIndex, true); + } - /// - /// 获取WCS到MDCS(模型空间)的矩阵 - /// - /// 命令行对象 - /// 变换矩阵 - public static Matrix3d GetMatrixFromWcsToMDcs(this Editor editor) - { - return editor.GetMatrixFromMDcsToWcs().Inverse(); - } + #endregion - /// - /// 获取MDCS(模型空间)到PDCS(图纸空间)的矩阵 - /// - /// 命令行对象 - /// 变换矩阵 - public static Matrix3d GetMatrixFromMDcsToPDcs(this Editor editor) - { - if ((short)Env.GetVar("TILEMODE") == 1) - throw new Autodesk.AutoCAD.Runtime.Exception(ErrorStatus.InvalidInput, "Espace papier uniquement"); + #region 矩阵 + + /// + /// 获取UCS到WCS的矩阵 + /// + /// 命令行对象 + /// 变换矩阵 + public static Matrix3d GetMatrixFromUcsToWcs(this Editor editor) + { + return editor.CurrentUserCoordinateSystem; + } + + /// + /// 获取WCS到UCS的矩阵 + /// + /// 命令行对象 + /// 变换矩阵 + public static Matrix3d GetMatrixFromWcsToUcs(this Editor editor) + { + return editor.CurrentUserCoordinateSystem.Inverse(); + } + + /// + /// 获取MDCS(模型空间)到WCS的矩阵 + /// + /// 命令行对象 + /// 变换矩阵 + public static Matrix3d GetMatrixFromMDcsToWcs(this Editor editor) + { + Matrix3d mat; + using ViewTableRecord vtr = editor.GetCurrentView(); + mat = Matrix3d.PlaneToWorld(vtr.ViewDirection); + mat = Matrix3d.Displacement(vtr.Target - Point3d.Origin) * mat; + return Matrix3d.Rotation(-vtr.ViewTwist, vtr.ViewDirection, vtr.Target) * mat; + } + + /// + /// 获取WCS到MDCS(模型空间)的矩阵 + /// + /// 命令行对象 + /// 变换矩阵 + public static Matrix3d GetMatrixFromWcsToMDcs(this Editor editor) + { + return editor.GetMatrixFromMDcsToWcs().Inverse(); + } + + /// + /// 获取MDCS(模型空间)到PDCS(图纸空间)的矩阵 + /// + /// 命令行对象 + /// 变换矩阵 + public static Matrix3d GetMatrixFromMDcsToPDcs(this Editor editor) + { + if ((short)Env.GetVar("TILEMODE") == 1) + throw new Autodesk.AutoCAD.Runtime.Exception(ErrorStatus.InvalidInput, "Espace papier uniquement"); - Database db = editor.Document.Database; - Matrix3d mat; - using (Transaction tr = db.TransactionManager.StartTransaction()) + Database db = editor.Document.Database; + Matrix3d mat; + using (Transaction tr = db.TransactionManager.StartTransaction()) + { + Viewport? vp = tr.GetObject(editor.CurrentViewportObjectId, OpenMode.ForRead) as Viewport; + if (vp?.Number == 1) { - Viewport vp = tr.GetObject(editor.CurrentViewportObjectId, OpenMode.ForRead) as Viewport; - if (vp.Number == 1) + try { - try - { - editor.SwitchToModelSpace(); - vp = tr.GetObject(editor.CurrentViewportObjectId, OpenMode.ForRead) as Viewport; - editor.SwitchToPaperSpace(); - } - catch - { - throw new Autodesk.AutoCAD.Runtime.Exception(ErrorStatus.InvalidInput, "Aucun fenêtre active"); - } + editor.SwitchToModelSpace(); + vp = tr.GetObject(editor.CurrentViewportObjectId, OpenMode.ForRead) as Viewport; + editor.SwitchToPaperSpace(); + } + catch + { + throw new Autodesk.AutoCAD.Runtime.Exception(ErrorStatus.InvalidInput, "Aucun fenêtre active"); } - Point3d vCtr = new(vp.ViewCenter.X, vp.ViewCenter.Y, 0.0); - mat = Matrix3d.Displacement(vCtr.GetAsVector().Negate()); - mat = Matrix3d.Displacement(vp.CenterPoint.GetAsVector()) * mat; - mat = Matrix3d.Scaling(vp.CustomScale, vp.CenterPoint) * mat; - tr.Commit(); } - return mat; + Point3d vCtr = new(vp!.ViewCenter.X, vp.ViewCenter.Y, 0.0); + mat = Matrix3d.Displacement(vCtr.GetAsVector().Negate()); + mat = Matrix3d.Displacement(vp.CenterPoint.GetAsVector()) * mat; + mat = Matrix3d.Scaling(vp.CustomScale, vp.CenterPoint) * mat; + tr.Commit(); } + return mat; + } + + /// + /// 获取PDCS(图纸空间)到MDCS(模型空间)的矩阵 + /// + /// 命令行对象 + /// 变换矩阵 + public static Matrix3d GetMatrixFromPDcsToMDcs(this Editor editor) + { + return editor.GetMatrixFromMDcsToPDcs().Inverse(); + } - /// - /// 获取PDCS(图纸空间)到MDCS(模型空间)的矩阵 - /// - /// 命令行对象 - /// 变换矩阵 - public static Matrix3d GetMatrixFromPDcsToMDcs(this Editor editor) + /// + /// 获取变换矩阵 + /// + /// 命令行对象 + /// 源坐标系 + /// 目标坐标系 + /// 变换矩阵 + public static Matrix3d GetMatrix(this Editor editor, CoordinateSystemCode from, CoordinateSystemCode to) + { +#if ac2009 + switch (from) { - return editor.GetMatrixFromMDcsToPDcs().Inverse(); - } + case CoordinateSystemCode.Wcs: + switch (to) + { + case CoordinateSystemCode.Ucs: + return editor.GetMatrixFromWcsToUcs(); + + case CoordinateSystemCode.MDcs: + return editor.GetMatrixFromMDcsToWcs(); + + case CoordinateSystemCode.PDcs: + throw new Autodesk.AutoCAD.Runtime.Exception( + ErrorStatus.InvalidInput, + "To be used only with DCS"); + } + break; + case CoordinateSystemCode.Ucs: + switch (to) + { + case CoordinateSystemCode.Wcs: + return editor.GetMatrixFromUcsToWcs(); - /// - /// 获取变换矩阵 - /// - /// 命令行对象 - /// 源坐标系 - /// 目标坐标系 - /// 变换矩阵 - public static Matrix3d GetMatrix(this Editor editor, CoordinateSystemCode from, CoordinateSystemCode to) + case CoordinateSystemCode.MDcs: + return editor.GetMatrixFromUcsToWcs() * editor.GetMatrixFromWcsToMDcs(); + + case CoordinateSystemCode.PDcs: + throw new Autodesk.AutoCAD.Runtime.Exception( + ErrorStatus.InvalidInput, + "To be used only with DCS"); + } + break; + case CoordinateSystemCode.MDcs: + switch (to) + { + case CoordinateSystemCode.Wcs: + return editor.GetMatrixFromMDcsToWcs(); + + case CoordinateSystemCode.Ucs: + return editor.GetMatrixFromMDcsToWcs() * editor.GetMatrixFromWcsToUcs(); + + case CoordinateSystemCode.PDcs: + return editor.GetMatrixFromMDcsToPDcs(); + } + break; + case CoordinateSystemCode.PDcs: + switch (to) + { + case CoordinateSystemCode.Wcs: + throw new Autodesk.AutoCAD.Runtime.Exception( + ErrorStatus.InvalidInput, + "To be used only with DCS"); + case CoordinateSystemCode.Ucs: + throw new Autodesk.AutoCAD.Runtime.Exception( + ErrorStatus.InvalidInput, + "To be used only with DCS"); + case CoordinateSystemCode.MDcs: + return editor.GetMatrixFromPDcsToMDcs(); + } + break; + } + return Matrix3d.Identity; +#else + return (from, to) switch { -#if ac2009 - switch (from) - { - case CoordinateSystemCode.Wcs: - switch (to) - { - case CoordinateSystemCode.Ucs: - return editor.GetMatrixFromWcsToUcs(); - - case CoordinateSystemCode.MDcs: - return editor.GetMatrixFromMDcsToWcs(); - - case CoordinateSystemCode.PDcs: - throw new Autodesk.AutoCAD.Runtime.Exception( - ErrorStatus.InvalidInput, - "To be used only with DCS"); - } - break; - case CoordinateSystemCode.Ucs: - switch (to) - { - case CoordinateSystemCode.Wcs: - return editor.GetMatrixFromUcsToWcs(); - - case CoordinateSystemCode.MDcs: - return editor.GetMatrixFromUcsToWcs() * editor.GetMatrixFromWcsToMDcs(); - - case CoordinateSystemCode.PDcs: - throw new Autodesk.AutoCAD.Runtime.Exception( - ErrorStatus.InvalidInput, - "To be used only with DCS"); - } - break; - case CoordinateSystemCode.MDcs: - switch (to) - { - case CoordinateSystemCode.Wcs: - return editor.GetMatrixFromMDcsToWcs(); - - case CoordinateSystemCode.Ucs: - return editor.GetMatrixFromMDcsToWcs() * editor.GetMatrixFromWcsToUcs(); - - case CoordinateSystemCode.PDcs: - return editor.GetMatrixFromMDcsToPDcs(); - } - break; - case CoordinateSystemCode.PDcs: - switch (to) - { - case CoordinateSystemCode.Wcs: - throw new Autodesk.AutoCAD.Runtime.Exception( - ErrorStatus.InvalidInput, - "To be used only with DCS"); - case CoordinateSystemCode.Ucs: - throw new Autodesk.AutoCAD.Runtime.Exception( - ErrorStatus.InvalidInput, - "To be used only with DCS"); - case CoordinateSystemCode.MDcs: - return editor.GetMatrixFromPDcsToMDcs(); - } - break; - } - return Matrix3d.Identity; -#elif ac2013 - return (from, to) switch - { - (CoordinateSystemCode.Wcs, CoordinateSystemCode.Ucs) => editor.GetMatrixFromWcsToUcs(), - (CoordinateSystemCode.Wcs, CoordinateSystemCode.MDcs) => editor.GetMatrixFromMDcsToWcs(), - (CoordinateSystemCode.Ucs, CoordinateSystemCode.Wcs) => editor.GetMatrixFromUcsToWcs(), - (CoordinateSystemCode.Ucs, CoordinateSystemCode.MDcs) => editor.GetMatrixFromUcsToWcs() * editor.GetMatrixFromWcsToMDcs(), - (CoordinateSystemCode.MDcs, CoordinateSystemCode.Wcs) => editor.GetMatrixFromMDcsToWcs(), - (CoordinateSystemCode.MDcs, CoordinateSystemCode.Ucs) => editor.GetMatrixFromMDcsToWcs() * editor.GetMatrixFromWcsToUcs(), - (CoordinateSystemCode.MDcs, CoordinateSystemCode.PDcs) => editor.GetMatrixFromMDcsToPDcs(), - (CoordinateSystemCode.PDcs, CoordinateSystemCode.MDcs) => editor.GetMatrixFromPDcsToMDcs(), - (CoordinateSystemCode.PDcs, CoordinateSystemCode.Wcs or CoordinateSystemCode.Ucs) - or (CoordinateSystemCode.Wcs or CoordinateSystemCode.Ucs, CoordinateSystemCode.PDcs) => throw new Autodesk.AutoCAD.Runtime.Exception(ErrorStatus.InvalidInput,"To be used only with DCS"), - (_, _) => Matrix3d.Identity - }; + (CoordinateSystemCode.Wcs, CoordinateSystemCode.Ucs) => editor.GetMatrixFromWcsToUcs(), + (CoordinateSystemCode.Wcs, CoordinateSystemCode.MDcs) => editor.GetMatrixFromWcsToMDcs(), + (CoordinateSystemCode.Ucs, CoordinateSystemCode.Wcs) => editor.GetMatrixFromUcsToWcs(), + (CoordinateSystemCode.Ucs, CoordinateSystemCode.MDcs) => editor.GetMatrixFromUcsToWcs() * editor.GetMatrixFromWcsToMDcs(), + (CoordinateSystemCode.MDcs, CoordinateSystemCode.Wcs) => editor.GetMatrixFromMDcsToWcs(), + (CoordinateSystemCode.MDcs, CoordinateSystemCode.Ucs) => editor.GetMatrixFromMDcsToWcs() * editor.GetMatrixFromWcsToUcs(), + (CoordinateSystemCode.MDcs, CoordinateSystemCode.PDcs) => editor.GetMatrixFromMDcsToPDcs(), + (CoordinateSystemCode.PDcs, CoordinateSystemCode.MDcs) => editor.GetMatrixFromPDcsToMDcs(), + (CoordinateSystemCode.PDcs, CoordinateSystemCode.Wcs or CoordinateSystemCode.Ucs) + or (CoordinateSystemCode.Wcs or CoordinateSystemCode.Ucs, CoordinateSystemCode.PDcs) => throw new Autodesk.AutoCAD.Runtime.Exception(ErrorStatus.InvalidInput, "To be used only with DCS"), + (_, _) => Matrix3d.Identity + }; #endif - } + } - #endregion + #endregion - #region 缩放 + #region 缩放 - /// - /// 缩放窗口范围 - /// - /// 命令行对象 - /// 窗口左下点 - /// 窗口右上点 - public static void ZoomWindow(this Editor ed, Point3d minPoint, Point3d maxPoint) + /// + /// 缩放窗口范围 + /// + /// 命令行对象 + /// 窗口左下点 + /// 窗口右上点 + public static void ZoomWindow(this Editor ed, Point3d minPoint, Point3d maxPoint) + { + ViewTableRecord cvtr = ed.GetCurrentView(); + ViewTableRecord vtr = new(); + vtr.CopyFrom(cvtr); + + Point3d[] oldpnts = new Point3d[] { minPoint, maxPoint }; + Point3d[] pnts = new Point3d[8]; + Point3d[] dpnts = new Point3d[8]; + for (int i = 0; i < 2; i++) { - ViewTableRecord cvtr = ed.GetCurrentView(); - ViewTableRecord vtr = new(); - vtr.CopyFrom(cvtr); - - Point3d[] oldpnts = new Point3d[] { minPoint, maxPoint }; - Point3d[] pnts = new Point3d[8]; - Point3d[] dpnts = new Point3d[8]; - for (int i = 0; i < 2; i++) + for (int j = 0; j < 2; j++) { - for (int j = 0; j < 2; j++) + for (int k = 0; k < 2; k++) { - for (int k = 0; k < 2; k++) - { - int n = i * 4 + j * 2 + k; - pnts[n] = new Point3d(oldpnts[i][0], oldpnts[j][1], oldpnts[k][2]); - dpnts[n] = pnts[n].TransformBy(ed.GetMatrixFromWcsToMDcs()); - } + int n = i * 4 + j * 2 + k; + pnts[n] = new Point3d(oldpnts[i][0], oldpnts[j][1], oldpnts[k][2]); + dpnts[n] = pnts[n].TransformBy(ed.GetMatrixFromWcsToMDcs()); } } - double xmin, xmax, ymin, ymax; - xmin = xmax = dpnts[0][0]; - ymin = ymax = dpnts[0][1]; - for (int i = 1; i < 8; i++) - { - xmin = Math.Min(xmin, dpnts[i][0]); - xmax = Math.Max(xmax, dpnts[i][0]); - ymin = Math.Min(ymin, dpnts[i][1]); - ymax = Math.Max(ymax, dpnts[i][1]); - } - - vtr.Width = xmax - xmin; - vtr.Height = ymax - ymin; - vtr.CenterPoint = (dpnts[0] + (dpnts[7] - dpnts[0]) / 2).Convert2d(new Plane()); - - ed.SetCurrentView(vtr); - ed.Regen(); } - - /// - /// 缩放窗口范围 - /// - /// 命令行对象 - /// 窗口范围点 - public static void ZoomWindow(this Editor ed, Extents3d ext) + double xmin, xmax, ymin, ymax; + xmin = xmax = dpnts[0][0]; + ymin = ymax = dpnts[0][1]; + for (int i = 1; i < 8; i++) { - ZoomWindow(ed, ext.MinPoint, ext.MaxPoint); + xmin = Math.Min(xmin, dpnts[i][0]); + xmax = Math.Max(xmax, dpnts[i][0]); + ymin = Math.Min(ymin, dpnts[i][1]); + ymax = Math.Max(ymax, dpnts[i][1]); } - /// - /// 缩放比例 - /// - /// 命令行对象 - /// 中心点 - /// 窗口宽 - /// 窗口高 - public static void Zoom(this Editor ed, Point3d CenPt, double width, double height) - { - using ViewTableRecord view = ed.GetCurrentView(); - view.Width = width; - view.Height = height; - view.CenterPoint = new Point2d(CenPt.X, CenPt.Y); - ed.SetCurrentView(view);//更新当前视图 - } + vtr.Width = xmax - xmin; + vtr.Height = ymax - ymin; + vtr.CenterPoint = (dpnts[0] + (dpnts[7] - dpnts[0]) / 2).Convert2d(new Plane()); - /// - ///缩放窗口范围 - /// - /// 命令行对象 - /// 第一点 - /// 对角点 - /// 偏移距离 - public static void ZoomWindow(this Editor ed, Point3d lpt, Point3d rpt, double offsetDist = 0.00) - { - Extents3d extents = new(); - extents.AddPoint(lpt); - extents.AddPoint(rpt); - rpt = extents.MaxPoint + new Vector3d(offsetDist, offsetDist, 0); - lpt = extents.MinPoint - new Vector3d(offsetDist, offsetDist, 0); - Vector3d ver = rpt - lpt; - ed.Zoom(lpt + ver / 2, ver.X, ver.Y); - } + ed.SetCurrentView(vtr); + ed.Regen(); + } - /// - /// 动态缩放 - /// - /// 命令行对象 - /// 偏移距离 - public static void ZoomExtents(this Editor ed, double offsetDist = 0.00) - { - var db = ed.Document.Database; - db.UpdateExt(true); - ed.ZoomWindow(db.Extmax, db.Extmin, offsetDist); - } + /// + /// 缩放窗口范围 + /// + /// 命令行对象 + /// 窗口范围点 + public static void ZoomWindow(this Editor ed, Extents3d ext) + { + ZoomWindow(ed, ext.MinPoint, ext.MaxPoint); + } - /// - /// 根据实体对象的范围显示视图 - /// - /// 命令行对象 - /// Entity对象 - /// 偏移距离 - public static void ZoomObject(this Editor ed, Entity ent, double offsetDist = 0.00) - { - Extents3d ext = ent.GeometricExtents; - ed.ZoomWindow(ext.MinPoint, ext.MaxPoint, offsetDist); - } + /// + /// 缩放比例 + /// + /// 命令行对象 + /// 中心点 + /// 窗口宽 + /// 窗口高 + public static void Zoom(this Editor ed, Point3d CenPt, double width, double height) + { + using ViewTableRecord view = ed.GetCurrentView(); + view.Width = width; + view.Height = height; + view.CenterPoint = new Point2d(CenPt.X, CenPt.Y); + ed.SetCurrentView(view);//更新当前视图 + } + + /// + ///缩放窗口范围 + /// + /// 命令行对象 + /// 第一点 + /// 对角点 + /// 偏移距离 + public static void ZoomWindow(this Editor ed, Point3d lpt, Point3d rpt, double offsetDist = 0.00) + { + Extents3d extents = new(); + extents.AddPoint(lpt); + extents.AddPoint(rpt); + rpt = extents.MaxPoint + new Vector3d(offsetDist, offsetDist, 0); + lpt = extents.MinPoint - new Vector3d(offsetDist, offsetDist, 0); + Vector3d ver = rpt - lpt; + ed.Zoom(lpt + ver / 2, ver.X, ver.Y); + } + + + /// + /// 获取有效的数据库范围 + /// + /// 数据库 + /// 容差值:图元包围盒会超过数据库边界,用此参数扩大边界 + /// + public static Extents3d? GetValidExtents3d(this Database db, double extention = 1e-6) + { + db.UpdateExt(true);//更新当前模型空间的范围 + var ve = new Vector3d(extention, extention, extention); + // 数据库没有图元的时候,min是大,max是小,导致新建出错 + // 数据如下: + // min.X == 1E20 && min.Y == 1E20 && min.Z == 1E20 && + // max.X == -1E20 && max.Y == -1E20 && max.Z == -1E20) + var a = db.Extmin; + var b = db.Extmax; + if (a.X < b.X && a.Y < b.Y) + return new Extents3d(db.Extmin - ve, db.Extmax + ve); + + return null; + } + + /// + /// 动态缩放 + /// + /// 命令行对象 + /// 偏移距离 + public static void ZoomExtents(this Editor ed, double offsetDist = 0.00) + { + Database db = ed.Document.Database; + // db.UpdateExt(true); //GetValidExtents3d内提供了 + var dbExtent = db.GetValidExtents3d(); + if (dbExtent == null) + ed.ZoomWindow(Point3d.Origin, new Point3d(1, 1, 0), offsetDist); + else + ed.ZoomWindow(db.Extmin, db.Extmax, offsetDist); + } + + /// + /// 根据实体对象的范围显示视图 + /// + /// 命令行对象 + /// Entity对象 + /// 偏移距离 + public static void ZoomObject(this Editor ed, Entity ent, double offsetDist = 0.00) + { + Extents3d ext = ent.GeometricExtents; + ed.ZoomWindow(ext.MinPoint, ext.MaxPoint, offsetDist); + } - #endregion + #endregion - #region Get交互类 + #region Get交互类 - /// - /// 获取Point - /// - /// 命令行对象 - /// 提示信息 - /// 提示使用的基点 - /// - public static PromptPointResult GetPoint(this Editor ed, string Message, Point3d BasePoint) + /// + /// 获取Point + /// + /// 命令行对象 + /// 提示信息 + /// 提示使用的基点 + /// + public static PromptPointResult GetPoint(this Editor ed, string Message, Point3d BasePoint) + { + PromptPointOptions ptOp = new(Message) { - PromptPointOptions ptOp = new(Message) - { - BasePoint = BasePoint, - UseBasePoint = true - }; - return ed.GetPoint(ptOp); - } + BasePoint = BasePoint, + UseBasePoint = true + }; + return ed.GetPoint(ptOp); + } - /// - /// 获取double值 - /// - /// 命令行对象 - /// 提示信息 - /// double默认值 - /// - public static PromptDoubleResult GetDouble(this Editor ed, string Message, double DefaultValue = 1.0) + /// + /// 获取double值 + /// + /// 命令行对象 + /// 提示信息 + /// double默认值 + /// + public static PromptDoubleResult GetDouble(this Editor ed, string Message, double DefaultValue = 1.0) + { + PromptDoubleOptions douOp = new(Message) { - PromptDoubleOptions douOp = new(Message) - { - DefaultValue = DefaultValue - }; - return ed.GetDouble(douOp); - } + DefaultValue = DefaultValue + }; + return ed.GetDouble(douOp); + } - /// - /// 获取int值 - /// - /// 命令行对象 - /// 提示信息 - /// double默认值 - /// - public static PromptIntegerResult GetInteger(this Editor ed, string Message, int DefaultValue = 1) + /// + /// 获取int值 + /// + /// 命令行对象 + /// 提示信息 + /// double默认值 + /// + public static PromptIntegerResult GetInteger(this Editor ed, string Message, int DefaultValue = 1) + { + PromptIntegerOptions douOp = new(Message) { - PromptIntegerOptions douOp = new(Message) - { - DefaultValue = DefaultValue - }; - return ed.GetInteger(douOp); - } + DefaultValue = DefaultValue + }; + return ed.GetInteger(douOp); + } - /// - /// 获取string值 - /// - /// 命令行对象 - /// 提示信息 - /// string默认值 - /// - public static PromptResult GetString(this Editor ed, string Message, string DefaultValue = "") + /// + /// 获取string值 + /// + /// 命令行对象 + /// 提示信息 + /// string默认值 + /// + public static PromptResult GetString(this Editor ed, string Message, string DefaultValue = "") + { + PromptStringOptions strOp = new(Message) { - PromptStringOptions strOp = new(Message) - { - DefaultValue = DefaultValue - }; - return ed.GetString(strOp); - } + DefaultValue = DefaultValue + }; + return ed.GetString(strOp); + } - #endregion Get交互类 + #endregion - #region 执行lisp + #region 执行lisp +#if NET35 + [DllImport("acad.exe", CallingConvention = CallingConvention.Cdecl, EntryPoint = "acedInvoke")] +#else + [DllImport("accore.dll", CallingConvention = CallingConvention.Cdecl, EntryPoint = "acedInvoke")] +#endif + static extern int AcedInvoke(IntPtr args, out IntPtr result); -#if ac2009 - [System.Security.SuppressUnmanagedCodeSecurity] - [DllImport("acad.exe", CharSet = CharSet.Unicode, CallingConvention = CallingConvention.Cdecl, EntryPoint = "?acedEvaluateLisp@@YAHPB_WAAPAUresbuf@@@Z")] - private static extern int AcedEvaluateLisp(string lispLine, out IntPtr result); - [DllImport("acad.exe", CallingConvention = CallingConvention.Cdecl, EntryPoint = "acedInvoke")] - private static extern int AcedInvoke(IntPtr args, out IntPtr result); +#if NET35 + [DllImport("acad.exe", CharSet = CharSet.Unicode, CallingConvention = CallingConvention.Cdecl, + EntryPoint = "?acedEvaluateLisp@@YAHPB_WAAPAUresbuf@@@Z")] #else - [System.Security.SuppressUnmanagedCodeSecurity] - [DllImport("accore.dll", CharSet = CharSet.Unicode, CallingConvention = CallingConvention.Cdecl, EntryPoint = "?acedEvaluateLisp@@YAHPEB_WAEAPEAUresbuf@@@Z")] - private static extern int AcedEvaluateLisp(string lispLine, out IntPtr result); + // 高版本此接口不能使用lisp(command "xx"),但是可以直接在自动运行接口上 + [DllImport("accore.dll", CharSet = CharSet.Unicode, CallingConvention = CallingConvention.Cdecl, + EntryPoint = "?acedEvaluateLisp@@YAHPEB_WAEAPEAUresbuf@@@Z")] +#endif + [System.Security.SuppressUnmanagedCodeSecurity]//初始化默认值 + static extern int AcedEvaluateLisp(string lispLine, out IntPtr result); - [DllImport("accore.dll", CallingConvention = CallingConvention.Cdecl, EntryPoint = "acedInvoke")] - private static extern int AcedInvoke(IntPtr args, out IntPtr result); +#if NET35 + [DllImport("acad.exe", CharSet = CharSet.Auto, CallingConvention = CallingConvention.Cdecl, + EntryPoint = "ads_queueexpr")] +#else + [DllImport("accore.dll", CharSet = CharSet.Auto, CallingConvention = CallingConvention.Cdecl, + EntryPoint = "ads_queueexpr")] #endif - /// - /// 发送lisp语句字符串到cad执行 - /// - /// 编辑器对象 - /// lisp语句 - /// 缓冲结果,返回值 - public static ResultBuffer RunLisp(this Editor ed, string arg) - { - AcedEvaluateLisp(arg, out IntPtr rb); - try - { - if (rb != IntPtr.Zero) - return DisposableWrapper.Create(typeof(ResultBuffer), rb, true) as ResultBuffer; - } - catch - { } - return null; + static extern int Ads_queueexpr(string strExpr); + + public enum RunLispFlag : byte + { + AdsQueueexpr = 1, + AcedEvaluateLisp = 2, + SendStringToExecute = 4, + } + + /// + /// 发送lisp语句字符串到cad执行 + /// + /// 编辑器对象 + /// lisp语句 + /// 运行方式 + /// 缓冲结果,返回值 +#pragma warning disable IDE0060 // 删除未使用的参数 + public static ResultBuffer? RunLisp(this Editor ed, + string lispCode, + RunLispFlag flag = RunLispFlag.AdsQueueexpr) +#pragma warning restore IDE0060 // 删除未使用的参数 + { + /* + * 测试命令: + * [CommandMethod("CmdTest_RunLisp")] + * public static void CmdTest_RunLisp() + * { + * var res = SendLisp.RunLisp("(setq abc 10)"); + * } + * 调用方式: + * (command "CmdTest_RunLisp1") + * bug说明: + * AcedEvaluateLisp接口在高版本调用时候没有运行成功,使得 !abc 没有值 + * 经过测试,cad08调用成功,此bug与CommandFlags无关 + * 解决方案: + * 0x01 用异步接口,但是这样是显式调用了: + * (setq thisdrawing (vla-get-activedocument (vlax-get-acad-object)))(vla-SendCommand thisdrawing "CmdTest_RunLisp1 ") + * 0x02 使用 Ads_queueexpr 接口 + */ + if ((flag & RunLispFlag.AdsQueueexpr) == RunLispFlag.AdsQueueexpr) + { + // 这个在08/12发送lisp不会出错,但是发送bo命令出错了. + // 0x01 设置 CommandFlags.Session 可以同步, + // 0x02 自执行发送lisp都是异步,(用来发送 含有(command)的lisp的) + _ = Ads_queueexpr(lispCode + "\n"); + } + if ((flag & RunLispFlag.AcedEvaluateLisp) == RunLispFlag.AcedEvaluateLisp) + { + _ = AcedEvaluateLisp(lispCode, out IntPtr rb); + if (rb != IntPtr.Zero) + return DisposableWrapper.Create(typeof(ResultBuffer), rb, true) as ResultBuffer; + } + if ((flag & RunLispFlag.SendStringToExecute) == RunLispFlag.SendStringToExecute) + { + var dm = Application.DocumentManager; + var doc = dm.MdiActiveDocument; + doc.SendStringToExecute(lispCode + "\n", false, false, false); } - #endregion 执行lisp + return null; } -} + #endregion +} \ No newline at end of file diff --git a/src/IFoxCAD.Cad/ExtensionMethod/EntityEx.cs b/src/IFoxCAD.Cad/ExtensionMethod/EntityEx.cs index c14b566..9f9fc55 100644 --- a/src/IFoxCAD.Cad/ExtensionMethod/EntityEx.cs +++ b/src/IFoxCAD.Cad/ExtensionMethod/EntityEx.cs @@ -1,409 +1,415 @@ -using Autodesk.AutoCAD.DatabaseServices; -using Autodesk.AutoCAD.DatabaseServices.Filters; -using Autodesk.AutoCAD.Geometry; +namespace IFoxCAD.Cad; -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; - -namespace IFoxCAD.Cad +/// +/// 实体图元类扩展函数 +/// +public static class EntityEx { + #region 实体刷新 /// - /// 实体图元类扩展函数 + /// 刷新实体显示 /// - public static class EntityEx + /// 实体对象 + public static void Flush(this Entity entity, DBTrans? trans = null) { - #region 实体刷新 - /// - /// 刷新实体显示 - /// - /// 实体对象 - public static void Flush(this Entity entity, Transaction trans = null) - { - if (entity is null) - { - throw new ArgumentNullException(nameof(entity)); - } - if (trans is null) - { - trans = DBTrans.Top.Transaction; - } - entity.RecordGraphicsModified(true); - trans.TransactionManager.QueueForGraphicsFlush(); - } + //if (entity is null) + //{ + // throw new ArgumentNullException(nameof(entity)); + //} + trans ??= DBTrans.Top; + entity.RecordGraphicsModified(true); + trans.Transaction.TransactionManager.QueueForGraphicsFlush(); + trans.Document?.TransactionManager.FlushGraphics(); + } - /// - /// 刷新实体显示 - /// - /// 实体id - public static void Flush(this ObjectId id) => Flush(DBTrans.Top.GetObject(id)); - #endregion - - #region 多段线端点坐标 - /// - /// 获取二维多段线的端点坐标 - /// - /// 二维多段线 - /// 事务 - /// 端点坐标集合 - public static IEnumerable GetPoints(this Polyline2d pl2d, Transaction tr = null) - { - tr ??= DBTrans.Top.Transaction; - foreach (ObjectId id in pl2d) - { - yield return ((Vertex2d)tr.GetObject(id, OpenMode.ForRead)).Position; - } - } + /// + /// 刷新实体显示 + /// + /// 实体id + public static void Flush(this ObjectId id) => Flush(DBTrans.Top.GetObject(id)!); + #endregion - /// - /// 获取三维多段线的端点坐标 - /// - /// 三维多段线 - /// 事务 - /// 端点坐标集合 - public static IEnumerable GetPoints(this Polyline3d pl3d, Transaction tr = null) + #region 多段线端点坐标 + /// + /// 获取二维多段线的端点坐标 + /// + /// 二维多段线 + /// 事务 + /// 端点坐标集合 + public static IEnumerable GetPoints(this Polyline2d pl2d, DBTrans? tr = null) + { + tr ??= DBTrans.Top; + foreach (ObjectId id in pl2d) { - tr ??= DBTrans.Top.Transaction; - foreach (ObjectId id in pl3d) - { - yield return ((PolylineVertex3d)tr.GetObject(id, OpenMode.ForRead)).Position; - } + yield return tr.GetObject(id)!.Position; } + } - /// - /// 获取多段线的端点坐标 - /// - /// 多段线 - /// 端点坐标集合 - public static IEnumerable GetPoints(this Polyline pl) + /// + /// 获取三维多段线的端点坐标 + /// + /// 三维多段线 + /// 事务 + /// 端点坐标集合 + public static IEnumerable GetPoints(this Polyline3d pl3d, DBTrans? tr = null) + { + tr ??= DBTrans.Top; + foreach (ObjectId id in pl3d) { - return - Enumerable - .Range(0, pl.NumberOfVertices) - .Select(i => pl.GetPoint3dAt(i)); + yield return tr.GetObject(id, OpenMode.ForRead)!.Position; } - #endregion + } - #region TransformBy + /// + /// 获取多段线的端点坐标 + /// + /// 多段线 + /// 端点坐标集合 + public static IEnumerable GetPoints(this Polyline pl) + { + return + Enumerable + .Range(0, pl.NumberOfVertices) + .Select(i => pl.GetPoint3dAt(i)); + } + #endregion - /// - /// 移动实体 - /// - /// 实体 - /// 基点 - /// 目标点 - public static void Move(this Entity ent, Point3d from, Point3d to) - { - ent.TransformBy(Matrix3d.Displacement(to - from)); - } + #region 实体线性变换 - /// - /// 缩放实体 - /// - /// 实体 - /// 缩放基点坐标 - /// 缩放比例 - public static void Scale(this Entity ent, Point3d center, double scaleValue) - { - ent.TransformBy(Matrix3d.Scaling(scaleValue, center)); - } + /// + /// 移动实体 + /// + /// 实体 + /// 基点 + /// 目标点 + public static void Move(this Entity ent, Point3d from, Point3d to) + { + ent.TransformBy(Matrix3d.Displacement(to - from)); + } - /// - /// 旋转实体 - /// - /// 实体 - /// 旋转中心 - /// 转角 - /// 旋转平面的法向矢量 - public static void Rotation(this Entity ent, Point3d center, double angle, Vector3d normal) - { - ent.TransformBy(Matrix3d.Rotation(angle, normal, center)); - } + /// + /// 缩放实体 + /// + /// 实体 + /// 缩放基点坐标 + /// 缩放比例 + public static void Scale(this Entity ent, Point3d center, double scaleValue) + { + ent.TransformBy(Matrix3d.Scaling(scaleValue, center)); + } - /// - /// 在XY平面内旋转实体 - /// - /// 实体 - /// 旋转中心 - /// 转角 - public static void Rotation(this Entity ent, Point3d center, double angle) - { - ent.TransformBy(Matrix3d.Rotation(angle, Vector3d.ZAxis.TransformBy(ent.Ecs), center)); - } + /// + /// 旋转实体 + /// + /// 实体 + /// 旋转中心 + /// 转角,弧度制,正数为顺时针 + /// 旋转平面的法向矢量 + public static void Rotation(this Entity ent, Point3d center, double angle, Vector3d normal) + { + ent.TransformBy(Matrix3d.Rotation(angle, normal, center)); + } - /// - /// 按对称轴镜像实体 - /// - /// 实体 - /// 对称轴起点 - /// 对称轴终点 - public static void Mirror(this Entity ent, Point3d startPoint, Point3d endPoint) - { - ent.TransformBy(Matrix3d.Mirroring(new Line3d(startPoint, endPoint))); - } + /// + /// 在XY平面内旋转实体 + /// + /// 实体 + /// 旋转中心 + /// 转角,弧度制,正数为顺时针 + public static void Rotation(this Entity ent, Point3d center, double angle) + { + ent.TransformBy(Matrix3d.Rotation(angle, Vector3d.ZAxis.TransformBy(ent.Ecs), center)); + } - /// - /// 按对称面镜像实体 - /// - /// 实体 - /// 对称平面 - public static void Mirror(this Entity ent, Plane plane) - { - ent.TransformBy(Matrix3d.Mirroring(plane)); - } + /// + /// 按对称轴镜像实体 + /// + /// 实体 + /// 对称轴起点 + /// 对称轴终点 + public static void Mirror(this Entity ent, Point3d startPoint, Point3d endPoint) + { + ent.TransformBy(Matrix3d.Mirroring(new Line3d(startPoint, endPoint))); + } - /// - /// 按对称点镜像实体 - /// - /// 实体 - /// 对称点 - public static void Mirror(this Entity ent, Point3d basePoint) - { - ent.TransformBy(Matrix3d.Mirroring(basePoint)); - } + /// + /// 按对称面镜像实体 + /// + /// 实体 + /// 对称平面 + public static void Mirror(this Entity ent, Plane plane) + { + ent.TransformBy(Matrix3d.Mirroring(plane)); + } - #endregion + /// + /// 按对称点镜像实体 + /// + /// 实体 + /// 对称点 + public static void Mirror(this Entity ent, Point3d basePoint) + { + ent.TransformBy(Matrix3d.Mirroring(basePoint)); + } + + #endregion - #region 实体范围 - /// - /// 获取实体集合的范围 - /// - /// 实体迭代器 - /// 实体集合的范围 - public static Extents3d GetExtents(this IEnumerable ents) + #region 实体范围 + /// + /// 获取实体集合的范围 + /// + /// 实体迭代器 + /// 实体集合的范围 + public static Extents3d GetExtents(this IEnumerable ents) + { + var ext = new Extents3d(); + foreach (var item in ents) { - var it = ents.GetEnumerator(); - var ext = it.Current.GeometricExtents; - while (it.MoveNext()) - ext.AddExtents(it.Current.GeometricExtents); - return ext; + ext.AddExtents(item.GeometricExtents); } - #endregion + return ext; + } + #endregion - #region 单行文字 + #region 单行文字 - /// - /// 更正单行文字的镜像属性 - /// - /// 单行文字 - public static void ValidateMirror(this DBText txt) - { - if (!txt.Database.Mirrtext) - { - txt.IsMirroredInX = false; - txt.IsMirroredInY = false; - } - } - #endregion - - #region 多行文字 - - /// - /// 炸散多行文字 - /// - /// 存储多行文字炸散之后的对象的类型 - /// 多行文字 - /// 存储对象变量 - /// 回调函数,用于处理炸散之后的对象 - /// MTextFragment -- 多行文字炸散后的对象 - /// MTextFragmentCallbackStatus -- 回调函数处理的结果 - /// - public static void ExplodeFragments(this MText mt, T obj, Func mTextFragmentCallback) + /// + /// 更正单行文字的镜像属性 + /// + /// 单行文字 + public static void ValidateMirror(this DBText txt) + { + if (!txt.Database.Mirrtext) { - mt.ExplodeFragments((f, o) => mTextFragmentCallback(f, (T)o), obj); + txt.IsMirroredInX = false; + txt.IsMirroredInY = false; } + } + #endregion - /// - /// 获取多行文字的无格式文本 - /// - /// 多行文字 - /// 文本 - public static string GetUnFormatString(this MText mt) - { - List strs = new(); - mt.ExplodeFragments( - strs, - (f, o) => { - o.Add(f.Text); - return MTextFragmentCallbackStatus.Continue; - }); - return string.Join("", strs.ToArray()); - } - #endregion - - #region 圆弧 - - /// - /// 根据圆心、起点和终点来创建圆弧(二维) - /// - /// 圆弧对象 - /// 起点 - /// 圆心 - /// 终点 - /// 圆弧 - public static Arc CreateArcSCE(Point3d startPoint, Point3d centerPoint, Point3d endPoint) - { - Arc arc = new(); - arc.Center = centerPoint; - arc.Radius = centerPoint.DistanceTo(startPoint); - Vector2d startVector = new(startPoint.X - centerPoint.X, startPoint.Y - centerPoint.Y); - Vector2d endVector = new(endPoint.X - centerPoint.X, endPoint.Y - centerPoint.Y); - //计算起始和终止角度 - arc.StartAngle = startVector.Angle; - arc.EndAngle = endVector.Angle; - return arc; - } - /// - /// 三点法创建圆弧(二维) - /// - /// 圆弧对象 - /// 起点 - /// 圆弧上的点 - /// 终点 - /// 圆弧 - public static Arc CreateArc(Point3d startPoint, Point3d pointOnArc, Point3d endPoint) - { - //创建一个几何类的圆弧对象 - CircularArc3d geArc = new(startPoint, pointOnArc, endPoint); - //将几何类圆弧对象的圆心和半径赋值给圆弧 -#if ac2009 - return geArc.ToArc(); -#elif ac2013 - return (Arc)Curve.CreateFromGeCurve(geArc); + #region 多行文字 + + /// + /// 炸散多行文字 + /// + /// 存储多行文字炸散之后的对象的类型 + /// 多行文字 + /// 存储对象变量 + /// 回调函数,用于处理炸散之后的对象 + /// 多行文字炸散后的对象 + /// 回调函数处理的结果 + /// + public static void ExplodeFragments(this MText mt, T obj, Func mTextFragmentCallback) + { + mt.ExplodeFragments((f, o) => mTextFragmentCallback(f, (T)o), obj); + } + + /// + /// 获取多行文字的无格式文本 + /// + /// 多行文字 + /// 文本 + public static string GetUnFormatString(this MText mt) + { + List strs = new(); + mt.ExplodeFragments( + strs, + (f, o) => { + o.Add(f.Text); + return MTextFragmentCallbackStatus.Continue; + }); + return string.Join("", strs.ToArray()); + } + #endregion + + #region 圆弧 + + /// + /// 根据圆心、起点、终点来创建圆弧(二维) + /// + /// 起点 + /// 圆心 + /// 终点 + /// 圆弧 + public static Arc CreateArcSCE(Point3d startPoint, Point3d centerPoint, Point3d endPoint) + { + Arc arc = new(); + arc.SetDatabaseDefaults(); + arc.Center = centerPoint; + arc.Radius = centerPoint.DistanceTo(startPoint); + Vector2d startVector = new(startPoint.X - centerPoint.X, startPoint.Y - centerPoint.Y); + Vector2d endVector = new(endPoint.X - centerPoint.X, endPoint.Y - centerPoint.Y); + //计算起始和终止角度 + arc.StartAngle = startVector.Angle; + arc.EndAngle = endVector.Angle; + return arc; + } + /// + /// 三点法创建圆弧(二维) + /// + /// 圆弧对象 + /// 起点 + /// 圆弧上的点 + /// 终点 + /// 圆弧 + public static Arc CreateArc(Point3d startPoint, Point3d pointOnArc, Point3d endPoint) + { + //创建一个几何类的圆弧对象 + CircularArc3d geArc = new(startPoint, pointOnArc, endPoint); + //将几何类圆弧对象的圆心和半径赋值给圆弧 +#if NET35 + return (Arc)geArc.ToCurve(); +#else + return (Arc)Curve.CreateFromGeCurve(geArc); #endif - } + } - /// - /// 根据起点、圆心和圆弧角度创建圆弧(二维) - /// - /// 圆弧对象 - /// 起点 - /// 圆心 - /// 圆弧角度 - /// 圆弧 - public static Arc CreateArc(Point3d startPoint, Point3d centerPoint, double angle) - { - Arc arc = new(); - arc.Center = centerPoint; - arc.Radius = centerPoint.DistanceTo(startPoint); - Vector2d startVector = new(startPoint.X - centerPoint.X, startPoint.Y - centerPoint.Y); - arc.StartAngle = startVector.Angle; - arc.EndAngle = startVector.Angle + angle; - return arc; - } + /// + /// 根据起点、圆心和圆弧角度创建圆弧(二维) + /// + /// 圆弧对象 + /// 起点 + /// 圆心 + /// 圆弧角度 + /// 圆弧 + public static Arc CreateArc(Point3d startPoint, Point3d centerPoint, double angle) + { + Arc arc = new(); + arc.SetDatabaseDefaults(); + arc.Center = centerPoint; + arc.Radius = centerPoint.DistanceTo(startPoint); + Vector2d startVector = new(startPoint.X - centerPoint.X, startPoint.Y - centerPoint.Y); + arc.StartAngle = startVector.Angle; + arc.EndAngle = startVector.Angle + angle; + return arc; + } - #endregion + #endregion - #region 圆 + #region 圆 - /// - /// 两点创建圆(两点中点为圆心) - /// - /// 起点 - /// 终点 - /// - public static Circle CreateCircle(Point3d startPoint, Point3d endPoint) - { - Circle circle = new(); - circle.Center = startPoint.GetMidPointTo(endPoint); - circle.Radius = startPoint.DistanceTo(endPoint) * 0.5; - return circle; - } + /// + /// 两点创建圆(两点中点为圆心) + /// + /// 起点 + /// 终点 + /// + public static Circle CreateCircle(Point3d startPoint, Point3d endPoint) + { + Circle circle = new(); + circle.SetDatabaseDefaults(); + circle.Center = startPoint.GetMidPointTo(endPoint); + circle.Radius = startPoint.DistanceTo(endPoint) * 0.5; + return circle; + } - /// - /// 三点法创建圆(失败则返回Null) - /// - /// 第一点 - /// 第二点 - /// 第三点 - /// - public static Circle CreateCircle(Point3d pt1, Point3d PointV, Point3d pt3) - { - //先判断三点是否共线,得到pt1点指向PointV、PointV点的矢量 - Vector3d va = pt1.GetVectorTo(PointV); - Vector3d vb = pt1.GetVectorTo(pt3); - //如两矢量夹角为0或180度(π弧度),则三点共线. - if (va.GetAngleTo(vb) == 0 | va.GetAngleTo(vb) == Math.PI) - { - return null; - } - else - { - //创建一个几何类的圆弧对象 - CircularArc3d geArc = new(pt1, PointV, pt3); - geArc.ToCircle(); - return geArc.ToCircle(); - } - } + /// + /// 三点法创建圆(失败则返回Null) + /// + /// 第一点 + /// 第二点 + /// 第三点 + /// + public static Circle? CreateCircle(Point3d pt1, Point3d pt2, Point3d pt3) + { + //先判断三点是否共线,得到pt1点指向pt2、pt2点的矢量 + Vector3d va = pt1.GetVectorTo(pt2); + Vector3d vb = pt1.GetVectorTo(pt3); + //如两矢量夹角为0或180度(π弧度),则三点共线. + if (va.GetAngleTo(vb) == 0 | va.GetAngleTo(vb) == Math.PI) + return null; + + //创建一个几何类的圆弧对象 + CircularArc3d geArc = new(pt1, pt2, pt3); + geArc.ToCircle(); + return geArc.ToCircle(); + } + + /// + /// 通过圆心,半径绘制圆形 + /// + /// 圆心 + /// 半径 + /// 图形的ObjectId + public static Circle? CreateCircle(Point3d center, double radius, double vex = 0, double vey = 0, double vez = 1) + { + return new Circle(center, new Vector3d(vex, vey, vez), radius);//平面法向量XY方向 + } - #endregion + #endregion - #region 块参照 + #region 块参照 - #region 裁剪块参照 + #region 裁剪块参照 - private const string filterDictName = "ACAD_FILTER"; - private const string spatialName = "SPATIAL"; + private const string filterDictName = "ACAD_FILTER"; + private const string spatialName = "SPATIAL"; - /// - /// 裁剪块参照 - /// - /// 块参照 - /// 裁剪多边形点表 - public static void ClipBlockRef(this BlockReference bref, IEnumerable pt3ds) + /// + /// 裁剪块参照 + /// + /// 块参照 + /// 裁剪多边形点表 + public static void ClipBlockRef(this BlockReference bref, IEnumerable pt3ds) + { + Matrix3d mat = bref.BlockTransform.Inverse(); + var pts = + pt3ds + .Select(p => p.TransformBy(mat)) + .Select(p => new Point2d(p.X, p.Y)) + .ToCollection(); + + SpatialFilterDefinition sfd = new(pts, Vector3d.ZAxis, 0.0, 0.0, 0.0, true); + using SpatialFilter sf = new() { Definition = sfd }; + var dict = bref.GetXDictionary()!.GetSubDictionary(true, new string[] { filterDictName })!; + dict.SetAt(spatialName, sf); + //SetToDictionary(dict, spatialName, sf); + } + + /// + /// 裁剪块参照 + /// + /// 块参照 + /// 第一角点 + /// 第二角点 + public static void ClipBlockRef(this BlockReference bref, Point3d pt1, Point3d pt2) + { + Matrix3d mat = bref.BlockTransform.Inverse(); + pt1 = pt1.TransformBy(mat); + pt2 = pt2.TransformBy(mat); + + Point2dCollection pts = new() { - if (bref is null) - { - throw new ArgumentNullException(nameof(bref)); - } - if (pt3ds is null) - { - throw new ArgumentNullException(nameof(pt3ds)); - } - Matrix3d mat = bref.BlockTransform.Inverse(); - var pts = - pt3ds - .Select(p => p.TransformBy(mat)) - .Select(p => new Point2d(p.X, p.Y)) - .ToCollection(); - - SpatialFilterDefinition sfd = new(pts, Vector3d.ZAxis, 0.0, 0.0, 0.0, true); - using SpatialFilter sf = new() { Definition = sfd }; - var dict = bref.GetXDictionary().GetSubDictionary(true, new string[] { filterDictName }); - dict.SetAt(spatialName, sf); - //SetToDictionary(dict, spatialName, sf); - } + new Point2d(Math.Min(pt1.X, pt2.X), Math.Min(pt1.Y, pt2.Y)), + new Point2d(Math.Max(pt1.X, pt2.X), Math.Max(pt1.Y, pt2.Y)) + }; + + SpatialFilterDefinition sfd = new(pts, Vector3d.ZAxis, 0.0, 0.0, 0.0, true); + using SpatialFilter sf = new() { Definition = sfd }; + var dict = bref.GetXDictionary()!.GetSubDictionary(true, new string[] { filterDictName })!; + dict.SetAt(spatialName, sf); + //SetToDictionary(dict, spatialName, sf); + } + #endregion + + /// + /// 更新动态块属性值 + /// + /// 动态块 + /// 属性值字典 + public static void ChangeBlockProperty(this BlockReference blockReference, + Dictionary propertyNameValues) + { + if (!blockReference.IsDynamicBlock) + return; - /// - /// 裁剪块参照 - /// - /// 块参照 - /// 第一角点 - /// 第二角点 - public static void ClipBlockRef(this BlockReference bref, Point3d pt1, Point3d PointV) + using (blockReference.ForWrite()) { - if (bref is null) - { - throw new ArgumentNullException(nameof(bref)); - } - Matrix3d mat = bref.BlockTransform.Inverse(); - pt1 = pt1.TransformBy(mat); - PointV = PointV.TransformBy(mat); - Point2dCollection pts = new() - { - new Point2d(Math.Min(pt1.X, PointV.X), Math.Min(pt1.Y, PointV.Y)), - new Point2d(Math.Max(pt1.X, PointV.X), Math.Max(pt1.Y, PointV.Y)) - }; - - SpatialFilterDefinition sfd = new(pts, Vector3d.ZAxis, 0.0, 0.0, 0.0, true); - using SpatialFilter sf = new() { Definition = sfd }; - var dict = bref.GetXDictionary().GetSubDictionary(true, new string[] { filterDictName }); - dict.SetAt(spatialName, sf); - //SetToDictionary(dict, spatialName, sf); + foreach (DynamicBlockReferenceProperty item in blockReference.DynamicBlockReferencePropertyCollection) + if (propertyNameValues.ContainsKey(item.PropertyName)) + item.Value = propertyNameValues[item.PropertyName]; } - #endregion - #endregion } + #endregion } diff --git a/src/IFoxCAD.Cad/ExtensionMethod/Enums.cs b/src/IFoxCAD.Cad/ExtensionMethod/Enums.cs index 55b108c..fc0fbaf 100644 --- a/src/IFoxCAD.Cad/ExtensionMethod/Enums.cs +++ b/src/IFoxCAD.Cad/ExtensionMethod/Enums.cs @@ -1,80 +1,107 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; +namespace IFoxCAD.Cad; -namespace IFoxCAD.Cad +/// +/// 坐标系类型枚举 +/// +public enum CoordinateSystemCode { /// - /// 坐标系类型枚举 + /// 世界坐标系 /// - public enum CoordinateSystemCode - { - /// - /// 世界坐标系 - /// - Wcs = 0, - - /// - /// 用户坐标系 - /// - Ucs, - - /// - /// 模型空间坐标系 - /// - MDcs, - - /// - /// 图纸空间坐标系 - /// - PDcs - } + Wcs = 0, /// - /// 方向的枚举 + /// 用户坐标系 /// - public enum OrientationType - { - /// - /// 左转或逆时针 - /// - CounterClockWise, - /// - /// 右转或顺时针 - /// - ClockWise, - /// - /// 重合或平行 - /// - Parallel - } + Ucs, /// - /// 点与多边形的关系类型枚举 + /// 模型空间坐标系 /// - public enum PointOnRegionType - { - /// - /// 多边形内部 - /// - Inside, - - /// - /// 多边形上 - /// - On, - - /// - /// 多边形外 - /// - Outside, - - /// - /// 错误 - /// - Error - } + MDcs, + /// + /// 图纸空间坐标系 + /// + PDcs +} +/// +/// 方向的枚举 +/// +public enum OrientationType +{ + /// + /// 左转或逆时针 + /// + CounterClockWise, + /// + /// 右转或顺时针 + /// + ClockWise, + /// + /// 重合或平行 + /// + Parallel } + +/// +/// 点与多边形的关系类型枚举 +/// +public enum PointOnRegionType +{ + /// + /// 多边形内部 + /// + Inside, + + /// + /// 多边形上 + /// + On, + + /// + /// 多边形外 + /// + Outside, + + /// + /// 错误 + /// + Error +} + + + +public enum FontTTF +{ + [Description("宋体.ttf")] + 宋体, + [Description("simfang.ttf")] + 仿宋, + [Description("FSGB2312.ttf")] + 仿宋GB2312, + [Description("Arial.ttf")] + Arial, + [Description("Romans")] + Romans +} + + + +public static class EnumHelper +{ + public static string GetDesc(this Enum val) + { + var type = val.GetType(); + var memberInfo = type.GetMember(val.ToString()); + var attributes = memberInfo[0].GetCustomAttributes(typeof(DescriptionAttribute), false); + //如果没有定义描述,就把当前枚举值的对应名称返回 + if (attributes is null || attributes.Length != 1) + { + return val.ToString(); + } + return ((DescriptionAttribute)attributes.Single()).Description; + } +} + diff --git a/src/IFoxCAD.Cad/ExtensionMethod/GeometryEx.cs b/src/IFoxCAD.Cad/ExtensionMethod/GeometryEx.cs index 8f2a7a1..eca3a15 100644 --- a/src/IFoxCAD.Cad/ExtensionMethod/GeometryEx.cs +++ b/src/IFoxCAD.Cad/ExtensionMethod/GeometryEx.cs @@ -1,665 +1,682 @@ -using Autodesk.AutoCAD.DatabaseServices; -using Autodesk.AutoCAD.Geometry; -using System; -using System.Collections.Generic; +namespace IFoxCAD.Cad; + using System.Drawing; -using System.Linq; -using IFoxCAD.Collections; -using IFoxCAD.Linq; -namespace IFoxCAD.Cad + +/// +/// 图形扩展类 +/// +public static class GeometryEx { + + #region Point&Circle + /// - /// 图形扩展类 + /// 判断点与多边形的关系 /// - public static class GeometryEx + /// 多边形顶点集合 + /// 点 + /// 点与多边形的关系 + public static PointOnRegionType PointOnRegion(this IEnumerable pts, Point2d pt) { - public static double DistanceTo(this Point2d pt1, Point2d PointV) - { - return pt1.GetDistanceTo(PointV); - } - - #region Point&Circle + //遍历点集并生成首尾连接的多边形 + var ptlst = new LoopList(pts); + if (ptlst.Count < 3) + return PointOnRegionType.Error; + + var ls2ds = new List(); + foreach (var node in ptlst.GetNodes()) + { + ls2ds.Add(new LineSegment2d(node.Value, node.Next!.Value)); + } + var cc2d = new CompositeCurve2d(ls2ds.ToArray()); + + //在多边形上? + if (cc2d.IsOn(pt)) + return PointOnRegionType.On; + + //在最小包围矩形外? + var bb2d = cc2d.BoundBlock; + if (!bb2d.Contains(pt)) + return PointOnRegionType.Outside; + + // + bool flag = false; + foreach (var node in ptlst.GetNodes()) + { + var pt1 = node.Value; + var pt2 = node.Next!.Value; + if (pt.Y < pt1.Y && pt.Y < pt2.Y) + continue; + if (pt1.X < pt.X && pt2.X < pt.X) + continue; + Vector2d vec = pt2 - pt1; + double t = (pt.X - pt1.X) / vec.X; + double y = t * vec.Y + pt1.Y; + if (y < pt.Y && t >= 0 && t <= 1) + flag = !flag; + } + return + flag ? + PointOnRegionType.Inside : PointOnRegionType.Outside; + } - /// - /// 判断点与多边形的关系 - /// - /// 多边形顶点集合 - /// 点 - /// 点与多边形的关系 - public static PointOnRegionType PointOnRegion(this IEnumerable pts, Point2d pt) - { - //遍历点集并生成首尾连接的多边形 - var ptlst = new LoopList(pts); - if (ptlst.Count < 3) - return PointOnRegionType.Error; + /// + /// 判断点与多边形的关系 + /// + /// 多边形顶点集合 + /// 点 + /// 点与多边形的关系 + public static PointOnRegionType PointOnRegion(this IEnumerable pts, Point3d pt) + { + //遍历点集并生成首尾连接的多边形 + var ptlst = new LoopList(pts); + if (ptlst.First!.Value == ptlst.Last!.Value) + ptlst.RemoveLast(); + if (ptlst.Count < 3) + return PointOnRegionType.Error; + + var ls3ds = new List(); + foreach (var node in ptlst.GetNodes()) + { + ls3ds.Add(new LineSegment3d(node.Value, node.Next!.Value)); + } + var cc3d = new CompositeCurve3d(ls3ds.ToArray()); + + //在多边形上? + if (cc3d.IsOn(pt)) + return PointOnRegionType.On; + + //在最小包围矩形外? + var bb2d = cc3d.BoundBlock; + if (!bb2d.Contains(pt)) + return PointOnRegionType.Outside; + + // + bool flag = false; + foreach (var node in ptlst.GetNodes()) + { + var pt1 = node.Value; + var pt2 = node.Next!.Value; + if (pt.Y < pt1.Y && pt.Y < pt2.Y) + continue; + if (pt1.X < pt.X && pt2.X < pt.X) + continue; + Vector3d vec = pt2 - pt1; + double t = (pt.X - pt1.X) / vec.X; + double y = t * vec.Y + pt1.Y; + if (y < pt.Y && t >= 0 && t <= 1) + flag = !flag; + } + return + flag ? + PointOnRegionType.Inside : PointOnRegionType.Outside; + } - var ls2ds = new List(); - foreach (var node in ptlst.GetNodes()) - { - ls2ds.Add(new LineSegment2d(node.Value, node.Next.Value)); - } - var cc2d = new CompositeCurve2d(ls2ds.ToArray()); + /// + /// 按两点返回最小包围圆 + /// + /// 基准点 + /// 基准点 + /// 输出圆上的点 + /// 解析类圆对象 + public static CircularArc2d GetMinCircle(Point2d pt1, Point2d pt2, out LoopList ptlst) + { + ptlst = new LoopList { pt1, pt2 }; + return + new CircularArc2d + ( + (pt1 + pt2.GetAsVector()) / 2, + pt1.GetDistanceTo(pt2) / 2 + ); + } - //在多边形上? - if (cc2d.IsOn(pt)) - return PointOnRegionType.On; + /// + /// 按三点返回最小包围圆 + /// + /// 基准点 + /// 基准点 + /// 基准点 + /// 输出圆上的点 + /// 解析类圆对象 + public static CircularArc2d GetMinCircle(Point2d pt1, Point2d pt2, Point2d pt3, out LoopList ptlst) + { + ptlst = new LoopList { pt1, pt2, pt3 }; - //在最小包围矩形外? - var bb2d = cc2d.BoundBlock; - if (!bb2d.Contains(pt)) - return PointOnRegionType.Outside; + //遍历各点与下一点的向量长度,找到距离最大的两个点 + LoopListNode maxNode = + ptlst.GetNodes().FindByMax + ( + out double maxLength, + node => node.Value.GetDistanceTo(node.Next!.Value) + ); - // - bool flag = false; - foreach (var node in ptlst.GetNodes()) - { - var pt1 = node.Value; - var PointV = node.Next.Value; - if (pt.Y < pt1.Y && pt.Y < PointV.Y) - continue; - if (pt1.X < pt.X && PointV.X < pt.X) - continue; - Vector2d vec = PointV - pt1; - double t = (pt.X - pt1.X) / vec.X; - double y = t * vec.Y + pt1.Y; - if (y < pt.Y && t >= 0 && t <= 1) - flag = !flag; - } - return - flag ? - PointOnRegionType.Inside : PointOnRegionType.Outside; - } + //以两点做最小包围圆 + CircularArc2d ca2d = + GetMinCircle(maxNode.Value, maxNode.Next!.Value, out LoopList tptlst); - /// - /// 判断点与多边形的关系 - /// - /// 多边形顶点集合 - /// 点 - /// 点与多边形的关系 - public static PointOnRegionType PointOnRegion(this IEnumerable pts, Point3d pt) + //如果另一点属于该圆 + if (ca2d.IsIn(maxNode.Previous!.Value)) { - //遍历点集并生成首尾连接的多边形 - var ptlst = new LoopList(pts); - if (ptlst.First.Value == ptlst.Last.Value) - ptlst.RemoveLast(); - if (ptlst.Count < 3) - return PointOnRegionType.Error; - - var ls3ds = new List(); - foreach (var node in ptlst.GetNodes()) - { - ls3ds.Add(new LineSegment3d(node.Value, node.Next.Value)); - } - var cc3d = new CompositeCurve3d(ls3ds.ToArray()); + //返回 + ptlst = tptlst; + return ca2d; + } + //否则按三点做圆 + //ptlst.SetFirst(maxNode); + ptlst = new LoopList { maxNode.Value, maxNode.Next.Value, maxNode.Previous.Value }; + ca2d = new CircularArc2d(pt1, pt2, pt3); + ca2d.SetAngles(0, Math.PI * 2); + return ca2d; + } - //在多边形上? - if (cc3d.IsOn(pt)) - return PointOnRegionType.On; + /// + /// 按四点返回最小包围圆 + /// + /// 基准点 + /// 基准点 + /// 基准点 + /// 基准点 + /// 输出圆上的点 + /// 解析类圆对象 + public static CircularArc2d? GetMinCircle(Point2d pt1, Point2d pt2, Point2d pt3, Point2d pt4, out LoopList? ptlst) + { + var iniptlst = new LoopList() { pt1, pt2, pt3, pt4 }; + ptlst = null; + CircularArc2d? ca2d = null; - //在最小包围矩形外? - var bb2d = cc3d.BoundBlock; - if (!bb2d.Contains(pt)) - return PointOnRegionType.Outside; + //遍历C43的组合,环链表的优势在这里 + foreach (LoopListNode firstNode in iniptlst.GetNodes()) + { + //获取各组合下三点的最小包围圆 + var secondNode = firstNode.Next; + var thirdNode = secondNode!.Next; + CircularArc2d tca2d = GetMinCircle(firstNode.Value, secondNode.Value, thirdNode!.Value, out LoopList tptlst); - // - bool flag = false; - foreach (var node in ptlst.GetNodes()) + //如果另一点属于该圆,并且半径小于当前值就把它做为候选解 + if (tca2d.IsIn(firstNode.Previous!.Value)) { - var pt1 = node.Value; - var PointV = node.Next.Value; - if (pt.Y < pt1.Y && pt.Y < PointV.Y) - continue; - if (pt1.X < pt.X && PointV.X < pt.X) - continue; - Vector3d vec = PointV - pt1; - double t = (pt.X - pt1.X) / vec.X; - double y = t * vec.Y + pt1.Y; - if (y < pt.Y && t >= 0 && t <= 1) - flag = !flag; + if (ca2d is null || tca2d.Radius < ca2d.Radius) + { + ca2d = tca2d; + ptlst = tptlst; + } } - return - flag ? - PointOnRegionType.Inside : PointOnRegionType.Outside; } - /// - /// 按两点返回最小包围圆 - /// - /// 基准点 - /// 基准点 - /// 输出圆上的点 - /// 解析类圆对象 - public static CircularArc2d GetMinCircle(Point2d pt1, Point2d PointV, out LoopList ptlst) - { - ptlst = new LoopList { pt1, PointV }; - return - new CircularArc2d - ( - (pt1 + PointV.GetAsVector()) / 2, - pt1.DistanceTo(PointV) / 2 - ); - } + //返回直径最小的圆 + return ca2d; + } - /// - /// 按三点返回最小包围圆 - /// - /// 基准点 - /// 基准点 - /// 基准点 - /// 输出圆上的点 - /// 解析类圆对象 - public static CircularArc2d GetMinCircle(Point2d pt1, Point2d PointV, Point2d pt3, out LoopList ptlst) - { - ptlst = new LoopList { pt1, PointV, pt3 }; - - //遍历各点与下一点的向量长度,找到距离最大的两个点 - double maxLength; - LoopListNode maxNode = - ptlst.GetNodes().FindByMax - ( - out maxLength, - node => node.Value.DistanceTo(node.Next.Value) - ); - - //以两点做最小包围圆 - LoopList tptlst; - CircularArc2d ca2d = - GetMinCircle(maxNode.Value, maxNode.Next.Value, out tptlst); - - //如果另一点属于该圆 - if (ca2d.IsIn(maxNode.Previous.Value)) - { - //返回 - ptlst = tptlst; - return ca2d; - } + /// + /// 计算三点围成的有向面积 + /// + /// 基准点 + /// 第一点 + /// 第二点 + /// 三点围成的三角形的有向面积 + private static double CalArea(Point2d ptBase, Point2d pt1, Point2d pt2) + { + return (pt2 - ptBase).DotProduct((pt1 - ptBase).GetPerpendicularVector()) / 2; + } + /// + /// 计算三点围成的三角形的真实面积 + /// + /// 基准点 + /// 第一点 + /// 第二点 + /// 三点围成的三角形的真实面积 + public static double GetArea(this Point2d ptBase, Point2d pt1, Point2d pt2) + { + return Math.Abs(CalArea(ptBase, pt1, pt2)); + } - //否则按三点做圆 - ptlst.SetFirst(maxNode); - ca2d = new CircularArc2d(pt1, PointV, pt3); - ca2d.SetAngles(0, Math.PI * 2); - return ca2d; - } + /// + /// 判断三点是否为逆时针,也就是说判断三点是否为左转 + /// + /// 基点 + /// 第一点 + /// 第二点 + /// OrientationType 类型值 + public static OrientationType IsClockWise(this Point2d ptBase, Point2d pt1, Point2d pt2) + { - /// - /// 按四点返回最小包围圆 - /// - /// 基准点 - /// 基准点 - /// 基准点 - /// 基准点 - /// 输出圆上的点 - /// 解析类圆对象 - public static CircularArc2d GetMinCircle(Point2d pt1, Point2d PointV, Point2d pt3, Point2d pt4, out LoopList ptlst) + return CalArea(ptBase, pt1, pt2) switch { - LoopList iniptlst = - new LoopList { pt1, PointV, pt3, pt4 }; - ptlst = null; - CircularArc2d ca2d = null; - - //遍历C43的组合,环链表的优势在这里 - foreach (LoopListNode firstNode in iniptlst.GetNodes()) - { - //获取各组合下三点的最小包围圆 - LoopListNode secondNode = firstNode.Next; - LoopListNode thirdNode = secondNode.Next; - LoopList tptlst; - CircularArc2d tca2d = GetMinCircle(firstNode.Value, secondNode.Value, thirdNode.Value, out tptlst); - - //如果另一点属于该圆,并且半径小于当前值就把它做为候选解 - if (tca2d.IsIn(firstNode.Previous.Value)) - { - if (ca2d is null || tca2d.Radius < ca2d.Radius) - { - ca2d = tca2d; - ptlst = tptlst; - } - } - } + > 0 => OrientationType.CounterClockWise, + < 0 => OrientationType.ClockWise, + _ => OrientationType.Parallel + }; + } - //返回直径最小的圆 - return ca2d; - } + /// + /// 计算两个二维向量围成的平行四边形的有向面积 + /// + /// 基向量 + /// 向量 + /// 有向面积 + private static double CalArea(Vector2d vecBase, Vector2d vec) + { + return vec.DotProduct(vecBase.GetPerpendicularVector()) / 2; + } - /// - /// 计算三点围成的有向面积 - /// - /// 基准点 - /// 第一点 - /// 第二点 - /// 三点围成的三角形的有向面积 - private static double CalArea(Point2d ptBase, Point2d pt1, Point2d PointV) - { - return (PointV - ptBase).DotProduct((pt1 - ptBase).GetPerpendicularVector()) / 2; - } - /// - /// 计算三点围成的三角形的真实面积 - /// - /// 基准点 - /// 第一点 - /// 第二点 - /// 三点围成的三角形的真实面积 - public static double GetArea(this Point2d ptBase, Point2d pt1, Point2d PointV) - { - return Math.Abs(CalArea(ptBase, pt1, PointV)); - } + /// + /// 计算两个二维向量围成的平行四边形的真实面积 + /// + /// 基向量 + /// 向量 + /// 真实面积 + public static double GetArea(Vector2d vecBase, Vector2d vec) + { + return Math.Abs(CalArea(vecBase, vec)); + } - /// - /// 判断三点是否为逆时针,也就是说判断三点是否为左转 - /// - /// 基点 - /// 第一点 - /// 第二点 - /// OrientationType 类型值 - public static OrientationType IsClockWise(this Point2d ptBase, Point2d pt1, Point2d PointV) + /// + /// 判断两个二维向量是否左转 + /// + /// 基向量 + /// 向量 + /// OrientationType 类型值 + public static OrientationType IsClockWise(Vector2d vecBase, Vector2d vec) + { + return CalArea(vecBase, vec) switch { + > 0 => OrientationType.CounterClockWise, + < 0 => OrientationType.ClockWise, + _ => OrientationType.Parallel + }; + } - return CalArea(ptBase, pt1, PointV) switch - { + #region PointList - > 0 => OrientationType.CounterClockWise, - < 0 => OrientationType.ClockWise, - _ => OrientationType.Parallel - }; - } + /// + /// 计算点集的有向面积 + /// + /// 点集 + /// 有向面积 + private static double CalArea(IEnumerable pnts) + { + IEnumerator itor = pnts.GetEnumerator(); + if (!itor.MoveNext()) + throw new ArgumentNullException(nameof(pnts)); + Point2d start = itor.Current; + Point2d p1, p2 = start; + double area = 0; - /// - /// 计算两个二维向量围成的平行四边形的有向面积 - /// - /// 基向量 - /// 向量 - /// 有向面积 - private static double CalArea(Vector2d vecBase, Vector2d vec) + while (itor.MoveNext()) { - return vec.DotProduct(vecBase.GetPerpendicularVector()) / 2; + p1 = p2; + p2 = itor.Current; + area += (p1.X * p2.Y - p2.X * p1.Y); } - /// - /// 计算两个二维向量围成的平行四边形的真实面积 - /// - /// 基向量 - /// 向量 - /// 真实面积 - public static double GetArea(Vector2d vecBase, Vector2d vec) + area = (area + (p2.X * start.Y - start.X * p2.Y)) / 2.0; + return area; + } + /// + /// 计算点集的真实面积 + /// + /// 点集 + /// 面积 + public static double GetArea(this IEnumerable pnts) + { + return Math.Abs(CalArea(pnts)); + } + + /// + /// 判断点集的点序 + /// + /// 点集 + /// OrientationType 类型值 + public static OrientationType IsClockWise(this IEnumerable pnts) + { + return CalArea(pnts) switch { - return Math.Abs(CalArea(vecBase, vec)); - } + < 0 => OrientationType.ClockWise, + > 0 => OrientationType.CounterClockWise, + _ => OrientationType.Parallel + }; + } - /// - /// 判断两个二维向量是否左转 - /// - /// 基向量 - /// 向量 - /// OrientationType 类型值 - public static OrientationType IsClockWise(Vector2d vecBase, Vector2d vec) + /// + /// 按点集返回最小包围圆 + /// + /// 点集 + /// 输出圆上的点 + /// 解析类圆对象 + public static CircularArc2d? GetMinCircle(this List pnts, out LoopList? ptlst) + { + //点数较小时直接返回 + switch (pnts.Count) { - return CalArea(vecBase, vec) switch - { - > 0 => OrientationType.CounterClockWise, - < 0 => OrientationType.ClockWise, - _ => OrientationType.Parallel - }; - } + case 0: + ptlst = null; + return null; - #region PointList + case 1: + ptlst = new LoopList { pnts[0] }; + return new CircularArc2d(pnts[0], 0); - /// - /// 计算点集的有向面积 - /// - /// 点集 - /// 有向面积 - private static double CalArea(IEnumerable pnts) - { - IEnumerator itor = pnts.GetEnumerator(); - if (!itor.MoveNext()) - throw new ArgumentNullException(nameof(pnts)); - Point2d start = itor.Current; - Point2d p1, p2 = start; - double area = 0; - - while (itor.MoveNext()) - { - p1 = p2; - p2 = itor.Current; - area += (p1.X * p2.Y - p2.X * p1.Y); - } + case 2: + return GetMinCircle(pnts[0], pnts[1], out ptlst); - area = (area + (p2.X * start.Y - start.X * p2.Y)) / 2.0; - return area; - } - /// - /// 计算点集的真实面积 - /// - /// 点集 - /// 面积 - public static double GetArea(this IEnumerable pnts) - { - return Math.Abs(CalArea(pnts)); - } + case 3: + return GetMinCircle(pnts[0], pnts[1], pnts[2], out ptlst); - /// - /// 判断点集的点序 - /// - /// 点集 - /// OrientationType 类型值 - public static OrientationType IsClockWise(this IEnumerable pnts) - { - return CalArea(pnts) switch - { - < 0 => OrientationType.ClockWise, - > 0 => OrientationType.CounterClockWise, - _ => OrientationType.Parallel - }; + case 4: + return GetMinCircle(pnts[0], pnts[1], pnts[2], pnts[3], out ptlst); } - /// - /// 按点集返回最小包围圆 - /// - /// 点集 - /// 输出圆上的点 - /// 解析类圆对象 - public static CircularArc2d GetMinCircle(this List pnts, out LoopList ptlst) - { - //点数较小时直接返回 - switch (pnts.Count) - { - case 0: - ptlst = null; - return null; + //按前三点计算最小包围圆 + Point2d[] tpnts = new Point2d[4]; + pnts.CopyTo(0, tpnts, 0, 3); + CircularArc2d? ca2d = GetMinCircle(tpnts[0], tpnts[1], tpnts[2], out ptlst); - case 1: - ptlst = new LoopList { pnts[0] }; - return new CircularArc2d(pnts[0], 0); + //找到点集中距离圆心的最远点为第四点 + tpnts[3] = pnts.FindByMax(pnt => ca2d.Center.GetDistanceTo(pnt)); - case 2: - return GetMinCircle(pnts[0], pnts[1], out ptlst); - - case 3: - return GetMinCircle(pnts[0], pnts[1], pnts[2], out ptlst); + //如果最远点属于圆结束 + while (!ca2d.IsIn(tpnts[3])) + { + //如果最远点不属于圆,按此四点计算最小包围圆 + ca2d = GetMinCircle(tpnts[0], tpnts[1], tpnts[2], tpnts[3], out ptlst); - case 4: - return GetMinCircle(pnts[0], pnts[1], pnts[2], pnts[3], out ptlst); + //将结果作为新的前三点 + if (ptlst!.Count == 3) + { + tpnts[2] = ptlst.Last!.Value; } - - //按前三点计算最小包围圆 - Point2d[] tpnts = new Point2d[4]; - pnts.CopyTo(0, tpnts, 0, 3); - CircularArc2d ca2d = GetMinCircle(tpnts[0], tpnts[1], tpnts[2], out ptlst); - - //找到点集中距离圆心的最远点为第四点 - tpnts[3] = pnts.FindByMax(pnt => ca2d.Center.DistanceTo(pnt)); - - //如果最远点属于圆结束 - while (!ca2d.IsIn(tpnts[3])) + else { - //如果最远点不属于圆,按此四点计算最小包围圆 - ca2d = GetMinCircle(tpnts[0], tpnts[1], tpnts[2], tpnts[3], out ptlst); - - //将结果作为新的前三点 - if (ptlst.Count == 3) - { - tpnts[2] = ptlst.Last.Value; - } - else - { - //第三点取另两点中距离圆心较远的点 - //按算法中描述的任选其中一点的话,还是无法收敛...... - tpnts[2] = - tpnts.Except(ptlst) - .FindByMax(pnt => ca2d.Center.DistanceTo(pnt)); - } - tpnts[0] = ptlst.First.Value; - tpnts[1] = ptlst.First.Next.Value; - - //按此三点计算最小包围圆 - ca2d = GetMinCircle(tpnts[0], tpnts[1], tpnts[2], out ptlst); - - //找到点集中圆心的最远点为第四点 - tpnts[3] = pnts.FindByMax(pnt => ca2d.Center.DistanceTo(pnt)); + //第三点取另两点中距离圆心较远的点 + //按算法中描述的任选其中一点的话,还是无法收敛...... + tpnts[2] = + tpnts.Except(ptlst) + .FindByMax(pnt => ca2d!.Center.GetDistanceTo(pnt)); } + tpnts[0] = ptlst.First!.Value; + tpnts[1] = ptlst.First.Next!.Value; - return ca2d; + //按此三点计算最小包围圆 + ca2d = GetMinCircle(tpnts[0], tpnts[1], tpnts[2], out ptlst); + + //找到点集中圆心的最远点为第四点 + tpnts[3] = pnts.FindByMax(pnt => ca2d.Center.GetDistanceTo(pnt)); } - /// - /// 获取点集的凸包 - /// - /// 点集 - /// 凸包 - public static List ConvexHull(this List points) - { - if (points is null) - return null; + return ca2d; + } - if (points.Count <= 1) - return points; + /// + /// 获取点集的凸包 + /// + /// 点集 + /// 凸包 + public static List? ConvexHull(this List points) + { + if (points is null) + return null; - int n = points.Count, k = 0; - List H = new(new Point2d[2 * n]); + if (points.Count <= 1) + return points; - points.Sort((a, b) => - a.X == b.X ? a.Y.CompareTo(b.Y) : a.X.CompareTo(b.X)); + int n = points.Count, k = 0; + List H = new(new Point2d[2 * n]); - // Build lower hull - for (int i = 0; i < n; ++i) - { - while (k >= 2 && IsClockWise(H[k - 2], H[k - 1], points[i]) == OrientationType.CounterClockWise) - k--; - H[k++] = points[i]; - } + points.Sort((a, b) => + a.X == b.X ? a.Y.CompareTo(b.Y) : a.X.CompareTo(b.X)); - // Build upper hull - for (int i = n - 2, t = k + 1; i >= 0; i--) - { - while (k >= t && IsClockWise(H[k - 2], H[k - 1], points[i]) == OrientationType.CounterClockWise) - k--; - H[k++] = points[i]; - } - return H.Take(k - 1).ToList(); + // Build lower hull + for (int i = 0; i < n; ++i) + { + while (k >= 2 && IsClockWise(H[k - 2], H[k - 1], points[i]) == OrientationType.CounterClockWise) + k--; + H[k++] = points[i]; } + // Build upper hull + for (int i = n - 2, t = k + 1; i >= 0; i--) + { + while (k >= t && IsClockWise(H[k - 2], H[k - 1], points[i]) == OrientationType.CounterClockWise) + k--; + H[k++] = points[i]; + } + return H.Take(k - 1).ToList(); + } - #endregion PointList - #endregion Point&Circle + #endregion PointList - #region Ucs + #endregion Point&Circle - /// - /// ucs到wcs的点变换 - /// - /// 点 - /// 变换后的点 - public static Point3d Ucs2Wcs(this Point3d point) - { - return point.TransformBy(Env.Editor.CurrentUserCoordinateSystem); - } + #region Ucs - /// - /// wcs到ucs的点变换 - /// - /// 点 - /// 变换后的点 - public static Point3d Wcs2Ucs(this Point3d point) - { - return point.TransformBy(Env.Editor.CurrentUserCoordinateSystem.Inverse()); - } + /// + /// ucs到wcs的点变换 + /// + /// 点 + /// 变换后的点 + public static Point3d Ucs2Wcs(this Point3d point) + { + return point.TransformBy(Env.Editor.CurrentUserCoordinateSystem); + } - /// - /// ucs到wcs的向量变换 - /// - /// 向量 - /// 变换后的向量 - public static Vector3d Ucs2Wcs(this Vector3d vec) - { - return vec.TransformBy(Env.Editor.CurrentUserCoordinateSystem); - } + /// + /// wcs到ucs的点变换 + /// + /// 点 + /// 变换后的点 + public static Point3d Wcs2Ucs(this Point3d point) + { + return point.TransformBy(Env.Editor.CurrentUserCoordinateSystem.Inverse()); + } - /// - /// wcs到ucs的向量变换 - /// - /// 向量 - /// 变换后的向量 - public static Vector3d Wcs2Ucs(this Vector3d vec) - { - return vec.TransformBy(Env.Editor.CurrentUserCoordinateSystem.Inverse()); - } + /// + /// ucs到wcs的向量变换 + /// + /// 向量 + /// 变换后的向量 + public static Vector3d Ucs2Wcs(this Vector3d vec) + { + return vec.TransformBy(Env.Editor.CurrentUserCoordinateSystem); + } - /// - /// 模拟 trans 函数 - /// - /// 点 - /// 源坐标系 - /// 目标坐标系 - /// 变换后的点 - public static Point3d Trans(this Point3d point, CoordinateSystemCode from, CoordinateSystemCode to) - { - return Env.Editor.GetMatrix(from, to) * point; - } + /// + /// wcs到ucs的向量变换 + /// + /// 向量 + /// 变换后的向量 + public static Vector3d Wcs2Ucs(this Vector3d vec) + { + return vec.TransformBy(Env.Editor.CurrentUserCoordinateSystem.Inverse()); + } - /// - /// 模拟 trans 函数 - /// - /// 向量 - /// 源坐标系 - /// 目标坐标系 - /// 变换后的向量 - public static Vector3d Trans(this Vector3d vec, CoordinateSystemCode from, CoordinateSystemCode to) - { - return vec.TransformBy(Env.Editor.GetMatrix(from, to)); - } + /// + /// 模拟 trans 函数 + /// + /// 点 + /// 源坐标系 + /// 目标坐标系 + /// 变换后的点 + public static Point3d Trans(this Point3d point, CoordinateSystemCode from, CoordinateSystemCode to) + { + return Env.Editor.GetMatrix(from, to) * point; + } - /// - /// wcs到dcs的点变换 - /// - /// 点 - /// 是否为图纸空间 - /// 变换后的点 - public static Point3d Wcs2Dcs(this Point3d point, bool atPaperSpace) - { - return - Trans( - point, - CoordinateSystemCode.Wcs, atPaperSpace ? CoordinateSystemCode.PDcs : CoordinateSystemCode.MDcs - ); - } + /// + /// 模拟 trans 函数 + /// + /// 向量 + /// 源坐标系 + /// 目标坐标系 + /// 变换后的向量 + public static Vector3d Trans(this Vector3d vec, CoordinateSystemCode from, CoordinateSystemCode to) + { + return vec.TransformBy(Env.Editor.GetMatrix(from, to)); + } - /// - /// wcs到dcs的向量变换 - /// - /// 向量 - /// 是否为图纸空间 - /// 变换后的向量 - public static Vector3d Wcs2Dcs(this Vector3d vec, bool atPaperSpace) - { - return - Trans( - vec, - CoordinateSystemCode.Wcs, atPaperSpace ? CoordinateSystemCode.PDcs : CoordinateSystemCode.MDcs - ); - } + /// + /// wcs到dcs的点变换 + /// + /// 点 + /// 是否为图纸空间 + /// 变换后的点 + public static Point3d Wcs2Dcs(this Point3d point, bool atPaperSpace) + { + return + Trans( + point, + CoordinateSystemCode.Wcs, atPaperSpace ? CoordinateSystemCode.PDcs : CoordinateSystemCode.MDcs + ); + } + + /// + /// wcs到dcs的向量变换 + /// + /// 向量 + /// 是否为图纸空间 + /// 变换后的向量 + public static Vector3d Wcs2Dcs(this Vector3d vec, bool atPaperSpace) + { + return + Trans( + vec, + CoordinateSystemCode.Wcs, atPaperSpace ? CoordinateSystemCode.PDcs : CoordinateSystemCode.MDcs + ); + } - #endregion Ucs + #endregion Ucs - /// - /// 返回不等比例变换矩阵 - /// - /// 基点 - /// x方向比例 - /// y方向比例 - /// z方向比例 - /// 三维矩阵 - public static Matrix3d GetScaleMatrix(this Point3d point, double x, double y, double z) - { - double[] matdata = new double[16]; - matdata[0] = x; - matdata[3] = point.X * (1 - x); - matdata[5] = y; - matdata[7] = point.Y * (1 - y); - matdata[10] = z; - matdata[11] = point.Z * (1 - z); - matdata[15] = 1; - return new Matrix3d(matdata); - } + /// + /// 返回不等比例变换矩阵 + /// + /// 基点 + /// x方向比例 + /// y方向比例 + /// z方向比例 + /// 三维矩阵 + public static Matrix3d GetScaleMatrix(this Point3d point, double x, double y, double z) + { + double[] matdata = new double[16]; + matdata[0] = x; + matdata[3] = point.X * (1 - x); + matdata[5] = y; + matdata[7] = point.Y * (1 - y); + matdata[10] = z; + matdata[11] = point.Z * (1 - z); + matdata[15] = 1; + return new Matrix3d(matdata); + } - /// - /// 获取坐标范围的大小 - /// - /// 坐标范围 - /// 尺寸对象 - public static Size GetSize(this Extents3d ext) - { - int width = (int)Math.Floor(ext.MaxPoint.X - ext.MinPoint.X); - int height = (int)Math.Ceiling(ext.MaxPoint.Y - ext.MinPoint.Y); - return new Size(width, height); - } + /// + /// 获取坐标范围的大小 + /// + /// 坐标范围 + /// 尺寸对象 + public static Size GetSize(this Extents3d ext) + { + int width = (int)Math.Floor(ext.MaxPoint.X - ext.MinPoint.X); + int height = (int)Math.Ceiling(ext.MaxPoint.Y - ext.MinPoint.Y); + return new Size(width, height); + } - /// - /// 将三维点转换为二维点 - /// - /// 三维点 - /// 二维点 - public static Point2d Point2d(this Point3d pt) - { - return new Point2d(pt.X, pt.Y); - } - /// - /// 将三维点集转换为二维点集 - /// - /// 三维点集 - /// 二维点集 - public static IEnumerable Point2d(this IEnumerable pts) - { - return pts.Select(pt => pt.Point2d()); - } - /// - /// 将二维点转换为三维点 - /// - /// 二维点 - /// 三维点 - public static Point3d Point3d(this Point2d pt) - { - return new Point3d(pt.X, pt.Y, 0); - } + /// + /// 将三维点转换为二维点 + /// + /// 三维点 + /// 二维点 + public static Point2d Point2d(this Point3d pt) + { + return new Point2d(pt.X, pt.Y); + } + /// + /// 将三维点集转换为二维点集 + /// + /// 三维点集 + /// 二维点集 + public static IEnumerable Point2d(this IEnumerable pts) + { + return pts.Select(pt => pt.Point2d()); + } + /// + /// 将二维点转换为三维点 + /// + /// 二维点 + /// 三维点 + public static Point3d Point3d(this Point2d pt) + { + return new Point3d(pt.X, pt.Y, 0); + } + /// + /// 将二维点转换为三维点 + /// + /// 二维点 + /// Z值 + /// 三维点 + public static Point3d Point3d(this Point2d pt,double z) + { + return new Point3d(pt.X, pt.Y, z); + } - /// - /// 获取两个点之间的中点 - /// - /// 第一点 - /// 第二点 - /// 返回两个点之间的中点 - public static Point3d GetMidPointTo(this Point3d pt1, Point3d PointV) - { - return new Point3d((pt1.X + PointV.X) * 0.5, (pt1.Y + PointV.Y) * 0.5, (pt1.Z + PointV.Z) * 0.5); - } + /// + /// 获取两个点之间的中点 + /// + /// 第一点 + /// 第二点 + /// 返回两个点之间的中点 + public static Point3d GetMidPointTo(this Point3d pt1, Point3d pt2) + { + return new Point3d((pt1.X + pt2.X) * 0.5, (pt1.Y + pt2.Y) * 0.5, (pt1.Z + pt2.Z) * 0.5); + } + + /// + /// 获取两个点之间的中点 + /// + /// 第一点 + /// 第二点 + /// 返回两个点之间的中点 + public static Point2d GetMidPointTo(this Point2d pt1, Point2d pt2) + { + return new Point2d((pt1.X + pt2.X) * 0.5, (pt1.Y + pt2.Y) * 0.5); + } - /// - /// 根据世界坐标计算用户坐标 - /// - /// 基点世界坐标 - /// 基点用户坐标 - /// 目标世界坐标 - /// 坐标网旋转角,按x轴正向逆时针弧度 - /// 目标用户坐标 - public static Point3d TransPoint(this Point3d basePt, Point3d userPt, Point3d transPt, double ang) - { - Matrix3d transMat = Matrix3d.Displacement(userPt - basePt); - Matrix3d roMat = Matrix3d.Rotation(-ang, Vector3d.ZAxis, userPt); - return transPt.TransformBy(roMat * transMat); - } - /// - /// 计算指定距离和角度的点 - /// - /// 本函数仅适用于x-y平面 - /// 基点 - /// 角度,x轴正向逆时针弧度 - /// 距离 - /// 目标点 - public static Point3d Polar(this Point3d pt, double ang, double len) - { - return pt + Vector3d.XAxis.RotateBy(ang, Vector3d.ZAxis) * len; - } + /// + /// 根据世界坐标计算用户坐标 + /// + /// 基点世界坐标 + /// 基点用户坐标 + /// 目标世界坐标 + /// 坐标网旋转角,按x轴正向逆时针弧度 + /// 目标用户坐标 + public static Point3d TransPoint(this Point3d basePt, Point3d userPt, Point3d transPt, double ang) + { + Matrix3d transMat = Matrix3d.Displacement(userPt - basePt); + Matrix3d roMat = Matrix3d.Rotation(-ang, Vector3d.ZAxis, userPt); + return transPt.TransformBy(roMat * transMat); + } + /// + /// 计算指定距离和角度的点 + /// + /// 本函数仅适用于x-y平面 + /// 基点 + /// 角度,x轴正向逆时针弧度 + /// 距离 + /// 目标点 + public static Point3d Polar(this Point3d pt, double ang, double len) + { + return pt + Vector3d.XAxis.RotateBy(ang, Vector3d.ZAxis) * len; + } + /// + /// 计算指定距离和角度的点 + /// + /// 本函数仅适用于x-y平面 + /// 基点 + /// 角度,x轴正向逆时针弧度 + /// 距离 + /// 目标点 + public static Point2d Polar(this Point2d pt, double ang, double len) + { + return pt + Vector2d.XAxis.RotateBy(ang) * len; } } diff --git a/src/IFoxCAD.Cad/ExtensionMethod/Jig.cs b/src/IFoxCAD.Cad/ExtensionMethod/Jig.cs new file mode 100644 index 0000000..5485e6d --- /dev/null +++ b/src/IFoxCAD.Cad/ExtensionMethod/Jig.cs @@ -0,0 +1,343 @@ +/* 封装jig + * 20220726 隐藏事件,利用函数进行数据库图元重绘 + * 20220710 修改SetOption()的空格结束,并添加例子到IFox + * 20220503 cad22需要防止刷新过程中更改队列,是因为允许函数重入导致,08不会有. + * 20220326 重绘图元的函数用错了,现在修正过来 + * 20211216 加入块表时候做一个差集,剔除临时图元 + * 20211209 补充正交变量设置和回收设置 + * 作者: 惊惊⎛⎝◕⏝⏝◕。⎠⎞ ⎛⎝≥⏝⏝0⎠⎞ ⎛⎝⓿⏝⏝⓿。⎠⎞ ⎛⎝≥⏝⏝≤⎠⎞ + * 博客: https://www.cnblogs.com/JJBox/p/15650770.html + */ + +namespace IFoxCAD.Cad; + +//此命名空间容易引起Polyline等等重义,因此不放入全局空间 +using Autodesk.AutoCAD.GraphicsInterface; + +public delegate void WorldDrawEvent(WorldDraw draw); +public class JigEx : DrawJig +{ + #region 成员 + /// + /// 事件:亮显/暗显会被刷新冲刷掉,所以这个事件用于补充非刷新的工作 + /// + event WorldDrawEvent? WorldDrawEvent; + /// + /// 最后的鼠标点,用来确认长度 + /// + public Point3d MousePointWcsLast; + /// + /// 最后的图元,用来生成 + /// + public Entity[] Entitys => _drawEntitys.ToArray(); + + + readonly Action>? _mouseAction; + readonly Tolerance _tolerance;//容差 + + readonly Queue _drawEntitys;//重复生成的图元,放在这里刷新 + JigPromptPointOptions? _options;//jig鼠标配置 + bool _worldDrawFlag = false; // 20220503 + + bool _systemVariables_Orthomode = false; + bool SystemVariables_Orthomode // 正交修改还原 + { + get => _systemVariables_Orthomode; + set + { + //1正交,0非正交 //setvar: https://www.cnblogs.com/JJBox/p/10209541.html + if (Env.OrthoMode != value) + Env.OrthoMode = _systemVariables_Orthomode = value; + } + } + #endregion + + #region 构造 + /// + /// 在界面绘制图元 + /// + private JigEx() + { + _drawEntitys = new(); + } + + /// + /// 在界面绘制图元 + /// + /// + /// 用来频繁执行的回调: + /// 鼠标点; + /// 加入新建的图元,鼠标采样期间会Dispose图元的;所以已经在数据库图元利用事件加入,不要在此加入; + /// + /// 鼠标移动的容差 + public JigEx(Action>? action = null, double tolerance = 1e-6) : this() + { + _mouseAction = action; + _tolerance = new(tolerance, tolerance); + } + #endregion + + #region 方法 + /// + /// 鼠标配置:基点 + /// + /// 基点 + /// 光标绑定 + /// 提示信息 + /// 正交开关 + public JigPromptPointOptions SetOptions(Point3d basePoint, + CursorType cursorType = CursorType.RubberBand, + string msg = "点选第二点", + bool orthomode = false) + { + if (orthomode) + SystemVariables_Orthomode = true; + + _options = JigPointOptions(); + _options.Message = Environment.NewLine + msg; + _options.Cursor = cursorType; //光标绑定 + _options.UseBasePoint = true; //基点打开 + _options.BasePoint = basePoint; //基点设定 + return _options; + } + + /// + /// 鼠标配置:提示信息,关键字 + /// + /// 信息 + /// 关键字 + /// 正交开关 + /// + public JigPromptPointOptions SetOptions(string msg, + Dictionary? keywords = null, + bool orthomode = false) + { + if (orthomode) + SystemVariables_Orthomode = true; + + _options = JigPointOptions(); + _options.Message = Environment.NewLine + msg; + + //加入关键字,加入时候将空格内容放到最后 + string spaceValue = string.Empty; + const string spaceKey = " "; + + if (keywords != null) + foreach (var item in keywords) + if (item.Key == spaceKey) + spaceValue = item.Value; + else + _options.Keywords.Add(item.Key, item.Key, item.Value); + + ///因为默认配置函数导致此处空格触发是无效的, + ///但是用户如果想触发,就需要在外部减去默认UserInputControls配置 + ///要放最后,才能优先触发其他关键字 + if (spaceValue != string.Empty) + _options.Keywords.Add(spaceKey, spaceKey, spaceValue); + else + _options.Keywords.Add(spaceKey, spaceKey, "<空格退出>"); + + // 外部设置减去配置 + // _options.UserInputControls = + // _options.UserInputControls + // ^ UserInputControls.NullResponseAccepted //输入了鼠标右键,结束jig + // ^ UserInputControls.AnyBlankTerminatesInput; //空格或回车,结束jig; + return _options; + } + + /// + /// 鼠标配置:自定义 + /// + /// + /// 正交开关 + public void SetOptions(Action action, bool orthomode = false) + { + if (orthomode) + SystemVariables_Orthomode = true; + + _options = new JigPromptPointOptions(); + action.Invoke(_options); + } + + /// + /// 执行 + /// + /// + public PromptResult Drag() + { + //jig功能必然是当前前台文档,所以封装内部更好调用 + var dm = Acap.DocumentManager; + var doc = dm.MdiActiveDocument; + var ed = doc.Editor; + var dr = ed.Drag(this); + + if (SystemVariables_Orthomode) + SystemVariables_Orthomode = !SystemVariables_Orthomode; + return dr; + } + + /// + /// 最后一次的图元加入数据库 + /// + /// 加入此空间 + /// 不生成的图元用于排除,例如刷新时候的提示文字 + /// 加入数据库的id集合 + public IEnumerable? AddEntityToMsPs(BlockTableRecord btrOfAddEntitySpace, + IEnumerable? removeEntity = null) + { + //内部用 _drawEntitys 外部用 Entitys,减少一层转换 + if (_drawEntitys.Count == 0) + return null; + + IEnumerable es = _drawEntitys; + if (removeEntity != null) + es = es.Except(removeEntity);//差集 + + return btrOfAddEntitySpace.AddEntity(es); + } + #endregion + + #region 重写 + /// + /// 鼠标采样器 + /// + /// + /// 返回状态:令频繁刷新结束 + protected override SamplerStatus Sampler(JigPrompts prompts) + { + if (_worldDrawFlag) + return SamplerStatus.NoChange;//OK的时候拖动鼠标与否都不出现图元 + + if (_options is null) + throw new NullReferenceException(nameof(_options)); + + var pro = prompts.AcquirePoint(_options); + if (pro.Status == PromptStatus.Keyword) + return SamplerStatus.OK; + else if (pro.Status != PromptStatus.OK) + return SamplerStatus.Cancel; + + //上次鼠标点不同(一定要这句,不然图元刷新太快会看到奇怪的边线) + var mousePointWcs = pro.Value; + + //== 是比较类字段,但是最好转为哈希比较. + //IsEqualTo 是方形判断(仅加法),但是cad是距离. + //Distance 是圆形判断(会求平方根,使用了牛顿迭代), + //大量数据(十万以上/频繁刷新)面前会显得非常慢. + if (mousePointWcs.IsEqualTo(MousePointWcsLast, _tolerance)) + return SamplerStatus.NoChange; + + //上次循环的缓冲区图元清理,否则将会在vs输出遗忘 Dispose + while (_drawEntitys.Count > 0) + _drawEntitys.Dequeue().Dispose(); + + //委托把容器扔出去接收新创建的图元,然后给重绘更新 + _mouseAction?.Invoke(mousePointWcs, _drawEntitys); + MousePointWcsLast = mousePointWcs; + + return SamplerStatus.OK; + } + + /// + /// 重绘已在数据库的图元 + /// 0x01 此处不加入newEntity的,它们在构造函数的参数回调处加入,它们会进行频繁new和Dispose从而避免遗忘释放 + /// 0x02 此处用于重绘已经在数据的图元 + /// 0x03 此处用于图元亮显暗显,因为会被重绘冲刷掉所以独立出来不重绘,它们也往往已经存在数据库的 + /// + /// + /// newEntity只会存在一个图元队列中,而数据库图元可以分多个集合 + /// 例如: 集合A亮显时 集合B暗显/集合B亮显时 集合A暗显,所以我没有设计多个"数据库图元集合"存放,而是由用户在构造函数外自行创建 + /// + /// + public void DatabaseEntityDraw(WorldDrawEvent action) + { + WorldDrawEvent = action; + } + + /* WorldDraw 封装外的操作说明: + * 0x01 + * 我有一个业务是一次性生成四个方向的箭头,因为cad08缺少瞬时图元, + * 那么可以先提交一次事务,再开一个事务,把Entity传给jig,最后选择删除部分. + * 虽然这个是可行的方案,但是Entity穿越事务本身来说是非必要不使用的. + * 0x02 + * 四个箭头最近鼠标的亮显,其余淡显, + * 在jig使用淡显ent.Unhighlight和亮显ent.Highlight() + * 需要绕过重绘,否则重绘将导致图元频闪,令这两个操作失效, + * 此时需要自定义一个集合 EntityList (不使用本函数的_drawEntitys) + * 再将 EntityList 传给 WorldDrawEvent 事件,事件内实现亮显和淡显. + * 0x03 + * draw.Geometry.Draw(_drawEntitys[i]); + * 此函数有问题,acad08克隆一份数组也可以用来刷新, + * 而arx上面的jig只能一次改一个,所以可以用此函数. + * 起因是此函数属于异步刷新, + * 同步上下文的刷新是 RawGeometry + * 0x04 + * cad22测试出现,08不会, + * draw.RawGeometry.Draw(ent);会跳到 Sampler(),所以设置 _worldDrawFlag + * 但是禁止重绘重入的话(令图元不频繁重绘),那么鼠标停着的时候就看不见图元, + * 所以只能重绘结束的时候才允许鼠标采集,采集过程的时候不会触发重绘, + * 这样才可以保证容器在重绘中不被更改. + */ + /// + /// 重绘图形 + /// + protected override bool WorldDraw(WorldDraw draw) + { + _worldDrawFlag = true; + WorldDrawEvent?.Invoke(draw); + _drawEntitys.ForEach(ent => { + draw.RawGeometry.Draw(ent); + }); + _worldDrawFlag = false; + return true; + } + #endregion + + /// + /// 用户输入控制默认配置 + /// 令jig.Drag().Status == + /// + /// + static JigPromptPointOptions JigPointOptions() + { + return new JigPromptPointOptions() + { + UserInputControls = + UserInputControls.GovernedByUCSDetect //由UCS探测用 + | UserInputControls.Accept3dCoordinates //接受三维坐标 + | UserInputControls.NullResponseAccepted //输入了鼠标右键,结束jig + | UserInputControls.AnyBlankTerminatesInput //空格或回车,结束jig; + }; + } + + /// + /// 空格默认是, + /// 将它设置为 + /// + public void SetSpaceIsKeyword() + { + var opt = _options; + if (opt == null) + throw new ArgumentNullException(nameof(_options)); + + if ((opt.UserInputControls & UserInputControls.NullResponseAccepted) == UserInputControls.NullResponseAccepted) + opt.UserInputControls ^= UserInputControls.NullResponseAccepted; //输入了鼠标右键,结束jig + if ((opt.UserInputControls & UserInputControls.AnyBlankTerminatesInput) == UserInputControls.AnyBlankTerminatesInput) + opt.UserInputControls ^= UserInputControls.AnyBlankTerminatesInput; //空格或回车,结束jig + } +} + +#if false +| UserInputControls.DoNotEchoCancelForCtrlC //不要取消CtrlC的回音 +| UserInputControls.DoNotUpdateLastPoint //不要更新最后一点 +| UserInputControls.NoDwgLimitsChecking //没有Dwg限制检查 +| UserInputControls.NoZeroResponseAccepted //接受非零响应 +| UserInputControls.NoNegativeResponseAccepted //不否定回复已被接受 +| UserInputControls.Accept3dCoordinates //返回点的三维坐标,是转换坐标系了? +| UserInputControls.AcceptMouseUpAsPoint //接受释放按键时的点而不是按下时 + +| UserInputControls.InitialBlankTerminatesInput //初始 空格或回车,结束jig +| UserInputControls.AcceptOtherInputString //接受其他输入字符串 +| UserInputControls.NoZDirectionOrtho //无方向正射,直接输入数字时以基点到当前点作为方向 +| UserInputControls.UseBasePointElevation //使用基点高程,基点的Z高度探测 +#endif \ No newline at end of file diff --git a/src/IFoxCAD.Cad/ExtensionMethod/ObjEx.cs b/src/IFoxCAD.Cad/ExtensionMethod/ObjEx.cs new file mode 100644 index 0000000..c831959 --- /dev/null +++ b/src/IFoxCAD.Cad/ExtensionMethod/ObjEx.cs @@ -0,0 +1,21 @@ +namespace IFoxCAD.Cad; + +public static class ObjEx +{ + /// + /// cad的打印 + /// + /// + public static void Print(this object obj) + { + Application.DocumentManager.MdiActiveDocument.Editor.WriteMessage($"{obj}\n"); + } + /// + /// 系统的打印 + /// + /// + public static void PrintLine(this object obj) + { + Console.WriteLine(obj.ToString()); + } +} diff --git a/src/IFoxCAD.Cad/ExtensionMethod/ObjectIdEx.cs b/src/IFoxCAD.Cad/ExtensionMethod/ObjectIdEx.cs index 2a89bd5..52c9261 100644 --- a/src/IFoxCAD.Cad/ExtensionMethod/ObjectIdEx.cs +++ b/src/IFoxCAD.Cad/ExtensionMethod/ObjectIdEx.cs @@ -1,70 +1,89 @@ -using Autodesk.AutoCAD.DatabaseServices; -using Autodesk.AutoCAD.Runtime; -using System.Collections.Generic; -using System.Linq; +namespace IFoxCAD.Cad; -namespace IFoxCAD.Cad +/// +/// 对象id扩展类 +/// +public static class ObjectIdEx { + #region GetObject + /// - /// 对象id扩展类 + /// 获取指定类型对象 /// - public static class ObjectIdEx + /// 指定的泛型 + /// 对象id + /// 事务 + /// 打开模式 + /// 打开删除对象 + /// 指定类型对象 + public static T? GetObject(this ObjectId id, OpenMode mode = OpenMode.ForRead, bool openErased = false, Transaction? tr = default) where T : DBObject { - #region GetObject - - /// - /// 获取指定类型对象 - /// - /// 指定的泛型 - /// 对象id - /// 事务 - /// 打开模式 - /// 打开删除对象 - /// 指定类型对象 - public static T GetObject(this ObjectId id, - OpenMode mode = OpenMode.ForRead, bool openErased = false, Transaction tr = default) where T : DBObject - { - tr ??= DBTrans.Top.Transaction; - return tr.GetObject(id, mode, openErased) as T; - } - - /// - /// 获取指定类型对象集合 - /// - /// 指定的泛型 - /// 对象id集合 - /// 事务 - /// 打开模式 - /// 打开删除对象 - /// 指定类型对象集合 - public static IEnumerable GetObject(this IEnumerable ids, - OpenMode mode = OpenMode.ForRead, bool openErased = false, Transaction tr = default) where T : DBObject - { - return ids.Select(id => id.GetObject(mode, openErased, tr)); - } + tr ??= DBTrans.Top.Transaction; + //tr = Env.GetTrans(tr); + return tr.GetObject(id, mode, openErased) as T; + } - /// - /// 返回符合类型的对象id - /// - /// 对象类型 - /// 对象id集合 - /// 对象id集合 - public static IEnumerable OfType(this IEnumerable ids) where T : DBObject - { - string dxfName = RXClass.GetClass(typeof(T)).DxfName; - return ids.Where(id => id.ObjectClass().DxfName == dxfName); - } + /// + /// 获取指定类型对象集合 + /// + /// 指定的泛型 + /// 对象id集合 + /// 事务 + /// 打开模式 + /// 打开删除对象 + /// 指定类型对象集合 + public static IEnumerable GetObject(this IEnumerable ids, OpenMode mode = OpenMode.ForRead, bool openErased = false, Transaction? tr = default) where T : DBObject + { + return ids.Select(id => id.GetObject(mode, openErased, tr)); + } - //Acad08缺少 id.ObjectClass 如何补偿? - public static RXClass ObjectClass(this ObjectId id) - { + /// + /// 返回符合类型的对象id + /// + /// 对象类型 + /// 对象id集合 + /// 对象id集合 + public static IEnumerable OfType(this IEnumerable ids) where T : DBObject + { + string dxfName = RXClass.GetClass(typeof(T)).DxfName; + return ids.Where(id => id.ObjectClass().DxfName == dxfName); + } + #endregion GetObject + + //Acad08缺少 id.ObjectClass 如何补偿? + public static RXClass ObjectClass(this ObjectId id) + { #if NET35 - return RXClass.GetClass(id.GetType()); + return RXClass.GetClass(id.GetType()); #else - return id.ObjectClass; + return id.ObjectClass; #endif - } - #endregion GetObject + } + + /// + /// id是否有效,未被删除 + /// + /// 对象id + /// id有效返回 ,反之返回 + public static bool IsOk(this ObjectId id) + { + return !id.IsNull && id.IsValid && !id.IsErased && !id.IsEffectivelyErased && id.IsResident; + } + /// + /// 删除id代表的对象 + /// + /// 对象id + public static void Erase(this ObjectId id) + { + if (id.IsOk()) + { + var ent = id.GetObject()!; + using (ent.ForWrite()) + { + ent.Erase(); + }// 第一种读写权限自动转换写法 + //Env.Editor.Regen(); + } } } diff --git a/src/IFoxCAD.Cad/ExtensionMethod/PointEx.cs b/src/IFoxCAD.Cad/ExtensionMethod/PointEx.cs new file mode 100644 index 0000000..b1bae82 --- /dev/null +++ b/src/IFoxCAD.Cad/ExtensionMethod/PointEx.cs @@ -0,0 +1,128 @@ +namespace IFoxCAD.Cad; + +public static class PointEx +{ + + /// + /// 获取点的hash字符串,同时可以作为pt的字符串表示 + /// + /// 点 + /// 指示计算几维坐标的标志,1为计算x,2为计算x,y,其他为计算x,y,z + /// 保留的小数位数 + /// hash字符串 + public static string GetHashString(this Point3d pt, int xyz = 3, int decimalRetain = 6) + { + var de = $"f{decimalRetain}"; + return xyz switch + { + 1 => $"({pt.X.ToString(de)})", + 2 => $"({pt.X.ToString(de)},{pt.Y.ToString(de)})", + _ => $"({pt.X.ToString(de)},{pt.Y.ToString(de)},{pt.Z.ToString(de)})" + }; + } + + //为了频繁触发所以弄个全局变量 + static Plane? _Plane; + /// + /// 两点计算弧度范围0到2Pi + /// + /// 起点 + /// 终点 + /// 方向 + /// 弧度值 + public static double GetAngle(this Point3d startPoint, Point3d endPoint, Vector3d? direction = null) + { + if (direction != null) + _Plane = new Plane(Point3d.Origin, direction.Value); + if (_Plane == null) + _Plane = new Plane(Point3d.Origin, Vector3d.ZAxis); + return startPoint.GetVectorTo(endPoint).AngleOnPlane(_Plane); + } + /// + /// 两点计算弧度范围0到2Pi + /// + /// 起点 + /// 终点 + /// 弧度值 + public static double GetAngle(this Point2d startPoint, Point2d endPoint) + { + return startPoint.GetVectorTo(endPoint).Angle; + } + + /// + /// 获取中点 + /// + /// + /// + /// + public static Point2d GetCenter(this Point2d a, Point2d b) + { + // (p1 + p2) / 2; //溢出风险 + return new Point2d(a.X * 0.5 + b.X * 0.5, + a.Y * 0.5 + b.Y * 0.5); + } + + /// http://www.lee-mac.com/bulgeconversion.html + /// + /// 求凸度,判断三点是否一条直线上 + /// + /// 圆弧起点 + /// 圆弧腰点 + /// 圆弧尾点 + /// 逆时针为正,顺时针为负 + public static double GetArcBulge(this Point2d arc1, Point2d arc2, Point2d arc3, double tol = 1e-10) + { + double dStartAngle = arc2.GetAngle(arc1); + double dEndAngle = arc2.GetAngle(arc3); + //求的P1P2与P1P3夹角 + var talAngle = (Math.PI - dStartAngle + dEndAngle) / 2; + //凸度==拱高/半弦长==拱高比值/半弦长比值 + //有了比值就不需要拿到拱高值和半弦长值了,因为接下来是相除得凸度 + double bulge = Math.Sin(talAngle) / Math.Cos(talAngle); + + //处理精度 + if (bulge > 0.9999 && bulge < 1.0001) + bulge = 1; + else if (bulge < -0.9999 && bulge > -1.0001) + bulge = -1; + else if (Math.Abs(bulge) < tol) + bulge = 0; + return bulge; + } + + + #region 首尾相连 + /// + /// 首尾相连 + /// + public static Point2dCollection End2End(this Point2dCollection ptcol!!) + { + if (ptcol.Count == 0 || ptcol[0].Equals(ptcol[^1]))//首尾相同直接返回 + return ptcol; + + //首尾不同,去加一个到最后 + var lst = new Point2d[ptcol.Count + 1]; + for (int i = 0; i < lst.Length; i++) + lst[i] = ptcol[i]; + lst[^1] = lst[0]; + + return new(lst); + } + /// + /// 首尾相连 + /// + public static Point3dCollection End2End(this Point3dCollection ptcol!!) + { + if (ptcol.Count == 0 || ptcol[0].Equals(ptcol[^1]))//首尾相同直接返回 + return ptcol; + + //首尾不同,去加一个到最后 + var lst = new Point3d[ptcol.Count + 1]; + for (int i = 0; i < lst.Length; i++) + lst[i] = ptcol[i]; + lst[^1] = lst[0]; + + return new(lst); + } + #endregion +} \ No newline at end of file diff --git a/src/IFoxCAD.Cad/ExtensionMethod/SelectionSetEx.cs b/src/IFoxCAD.Cad/ExtensionMethod/SelectionSetEx.cs index 9ede888..dbde0ac 100644 --- a/src/IFoxCAD.Cad/ExtensionMethod/SelectionSetEx.cs +++ b/src/IFoxCAD.Cad/ExtensionMethod/SelectionSetEx.cs @@ -1,106 +1,101 @@ -using Autodesk.AutoCAD.DatabaseServices; -using Autodesk.AutoCAD.EditorInput; -using Autodesk.AutoCAD.Runtime; -using System; -using System.Collections.Generic; -using System.Linq; +namespace IFoxCAD.Cad; -namespace IFoxCAD.Cad +/// +/// 选择集扩展类 +/// +public static class SelectionSetEx { - + #region 获取对象id /// - /// 选择集扩展类 + /// 获取已选择的对象 /// - public static class SelectionSetEx + /// 选择集 + /// 已选择的对象集合 + public static IEnumerable GetSelectedObjects(this SelectionSet ss) { - #region 获取对象id - /// - /// 获取已选择的对象 - /// - /// 选择集 - /// 已选择的对象集合 - public static IEnumerable GetSelectedObjects(this SelectionSet ss) - { - return ss.Cast(); - } + return ss.Cast(); + } - /// - /// 获取已选择的对象 - /// - /// 已选择的对象泛型 - /// 选择集 - /// 已选择的对象集合 - public static IEnumerable GetSelectObjects(this SelectionSet ss) where T : SelectedObject - { - return ss.Cast().OfType(); - } + /// + /// 获取已选择的对象 + /// + /// 已选择的对象泛型 + /// 选择集 + /// 已选择的对象集合 + public static IEnumerable GetSelectObjects(this SelectionSet ss) where T : SelectedObject + { + return ss.Cast().OfType(); + } - /// - /// 从选择集中获取对象id - /// - /// 图元类型 - /// 选择集 - /// 已选择的对象id集合 - public static IEnumerable GetObjectIds(this SelectionSet ss) where T : Entity - { - string dxfName = RXClass.GetClass(typeof(T)).DxfName; - return - ss - .GetObjectIds() - .Where(id => id.ObjectClass().DxfName == dxfName); - } + /// + /// 从选择集中获取对象id + /// + /// 图元类型 + /// 选择集 + /// 已选择的对象id集合 + public static IEnumerable GetObjectIds(this SelectionSet ss) where T : Entity + { + string dxfName = RXClass.GetClass(typeof(T)).DxfName; + return + ss + .GetObjectIds() + .Where(id => id.ObjectClass().DxfName == dxfName); + } - /// - /// 将选择集的对象按类型分组 - /// - /// 选择集 - /// 分组后的类型/对象id集合 - public static IEnumerable> GetObjectIdGroup(this SelectionSet ss) - { - return - ss - .GetObjectIds() - .GroupBy(id => id.ObjectClass().DxfName); - } - #endregion + /// + /// 将选择集的对象按类型分组 + /// + /// 选择集 + /// 分组后的类型/对象id集合 + public static IEnumerable> GetObjectIdGroup(this SelectionSet ss) + { + return + ss + .GetObjectIds() + .GroupBy(id => id.ObjectClass().DxfName); + } + #endregion - #region 获取实体对象 + #region 获取实体对象 - /// - /// 获取指定类型图元 - /// - /// 指定类型 - /// 选择集 - /// 事务 - /// 打开模式 - /// 图元集合 - public static IEnumerable GetEntities(this SelectionSet ss, OpenMode openMode = OpenMode.ForRead, Transaction tr = default) where T : Entity - { - return - ss - .GetObjectIds() - .Select(id => tr.GetObject(id, openMode) as T); - } + /// + /// 获取指定类型图元 + /// + /// 指定类型 + /// 选择集 + /// 事务 + /// 打开模式 + /// 图元集合 + public static IEnumerable GetEntities(this SelectionSet ss, OpenMode openMode = OpenMode.ForRead, DBTrans? tr = default) where T : Entity + { + if (ss is null) + throw new ArgumentNullException(nameof(ss)); - #endregion + tr ??= DBTrans.Top; + return + ss + .GetObjectIds() + .Select(id => tr.GetObject(id, openMode)) + .Where(ent => ent != null); + } - #region ForEach + #endregion - /// - /// 遍历选择集 - /// - /// 指定图元类型 - /// 选择集 - /// 事务 - /// 打开模式 - /// 处理函数 - public static void ForEach(this SelectionSet ss, Action action, OpenMode openMode = OpenMode.ForRead, Transaction tr = default) where T : Entity + #region ForEach + /// + /// 遍历选择集 + /// + /// 指定图元类型 + /// 选择集 + /// 事务 + /// 打开模式 + /// 处理函数 + public static void ForEach(this SelectionSet ss, Action action, OpenMode openMode = OpenMode.ForRead, DBTrans? tr = default) where T : Entity + { + foreach (T? ent in ss.GetEntities(openMode, tr)) { - foreach (T ent in ss.GetEntities(openMode, tr)) - { - action?.Invoke(ent); - } + action?.Invoke(ent); } - #endregion } + #endregion } diff --git a/src/IFoxCAD.Cad/ExtensionMethod/SymbolTableEx.cs b/src/IFoxCAD.Cad/ExtensionMethod/SymbolTableEx.cs index 327f8ba..3c0ace3 100644 --- a/src/IFoxCAD.Cad/ExtensionMethod/SymbolTableEx.cs +++ b/src/IFoxCAD.Cad/ExtensionMethod/SymbolTableEx.cs @@ -1,81 +1,73 @@ -using System; -using System.Collections.Generic; -using System.IO; -using System.Linq; -using System.Text; +namespace IFoxCAD.Cad; - -using Autodesk.AutoCAD.Colors; -using Autodesk.AutoCAD.DatabaseServices; -using Autodesk.AutoCAD.Geometry; - -namespace IFoxCAD.Cad +/// +/// 符号表类扩展函数 +/// +public static class SymbolTableEx { + #region 图层表 + /// + /// 添加图层 + /// + /// 图层符号表 + /// 图层名 + /// 图层颜色 + /// 图层id + public static ObjectId Add(this SymbolTable table, string name, Color color) + { + return table.Add(name, lt => lt.Color = color); + } /// - /// 符号表类扩展函数 + /// 添加图层 /// - public static class SymbolTableEx + /// 图层符号表 + /// 图层名 + /// 图层颜色索引值 + /// 图层id + public static ObjectId Add(this SymbolTable table, string name, int colorIndex) { - #region 图层表 - /// - /// 添加图层 - /// - /// 图层符号表 - /// 图层名 - /// 图层颜色 - /// 图层id - public static ObjectId Add(this SymbolTable table, string name, Color color) + colorIndex %= 256;//防止输入的颜色超出256 + colorIndex = Math.Abs(colorIndex);//防止负数 + return table.Add(name, lt => lt.Color = Color.FromColorIndex(ColorMethod.ByColor, (short)colorIndex)); + } + /// + /// 更改图层名 + /// + /// 图层符号表 + /// 旧图层名 + /// 新图层名 + public static ObjectId Rename(this SymbolTable table, string Oldname, string NewName) + { + if (table.Has(Oldname)) { - return table.Add(name, lt => lt.Color = color); + table.Change(Oldname, ly => + { + ly.Name = NewName; + } + ); + return table[NewName]; } - /// - /// 添加图层 - /// - /// 图层符号表 - /// 图层名 - /// 图层颜色索引值 - /// 图层id - public static ObjectId Add(this SymbolTable table, string name, int colorIndex) + else { - colorIndex %= 256; //防止输入的颜色超出256 - colorIndex = Math.Abs(colorIndex);//防止负数 - return table.Add(name, lt => lt.Color = Color.FromColorIndex(ColorMethod.ByColor, (short)colorIndex)); + return ObjectId.Null; } - /// - /// 更改图层名 - /// - /// 图层符号表 - /// 旧图层名 - /// 新图层名 - public static ObjectId Rename(this SymbolTable table, string Oldname, string NewName) + } + /// + /// 删除图层 + /// + /// 层表 + /// 图层名 + /// 成功返回 ,失败返回 + public static bool Delete(this SymbolTable table, string name) + { + if (name == "0" || name == "Defpoints" || !table.Has(name) || table[name] == DBTrans.Top.Database.Clayer) { - if (table.Has(Oldname)) - { - table.Change(Oldname, ly => { - ly.Name = NewName; - } - ); - return table[NewName]; - } - else - { - return ObjectId.Null; - } + return false; } - /// - /// 删除图层 - /// - /// 层表 - /// 图层名 - /// 成功返回 ,失败返回 - public static bool Delete(this SymbolTable table, string name) + table.CurrentSymbolTable.GenerateUsageData(); + var ltr = table.GetRecord(name); + if (ltr is not null) { - if (name == "0" || name == "Defpoints" || !table.Has(name) || table[name] == DBTrans.Top.Database.Clayer) - { - return false; - } - table.CurrentSymbolTable.GenerateUsageData(); - var ltr = table.GetRecord(name); if (ltr.IsUsed) { return false; @@ -86,191 +78,303 @@ public static bool Delete(this SymbolTable table, } return true; } - #endregion - - #region 块表 - /// - /// 添加块定义 - /// - /// 块表 - /// 块名 - /// 对所添加块表的委托n - /// 添加图元的委托 - /// 添加属性定义的委托 - /// 块定义id - /// TODO: 需要测试匿名块等特殊的块是否能定义 - public static ObjectId Add(this SymbolTable table, string name, Action action = null, Func> ents = null, Func> attdef = null) - { - return table.Add(name, btr => { - action?.Invoke(btr); - var entsres = ents?.Invoke(); - if (entsres is not null) - { - btr.AddEntity(entsres); - } - var adddefres = attdef?.Invoke(); - if (adddefres is not null) - { - btr.AddEntity(adddefres); - } - //if (ents is not null) - //{ - // btr.AddEntity(ents?.Invoke()); - //} - //if (attdef is not null) - //{ - // btr.AddEntity(attdef?.Invoke()); - //} - }); - } - /// - /// 添加块定义 - /// - /// 块表 - /// 块名 - /// 图元 - /// 属性定义 - /// - public static ObjectId Add(this SymbolTable table, string name, IEnumerable ents = null, IEnumerable attdef = null) - { - return table.Add(name, null, () => { return ents; }, () => { return attdef; }); - } + return false; + } + #endregion - /// - /// 添加块定义 - /// - /// 块表 - /// 块名 - /// 图元(包括属性) - /// - public static ObjectId Add(this SymbolTable table, string name, params Entity[] ents) + #region 块表 + /// + /// 添加块定义 + /// + /// 块表 + /// 块名 + /// 对所添加块表的委托n + /// 添加图元的委托 + /// 添加属性定义的委托 + /// 块定义id + /// TODO: 需要测试匿名块等特殊的块是否能定义 + public static ObjectId Add(this SymbolTable table, string name, Action? action = null, Func>? ents = null, Func>? attdef = null) + { + return table.Add(name, btr => { - return table.Add(name, null, () => { return ents; }); - } + action?.Invoke(btr); + var entsres = ents?.Invoke(); + if (entsres is not null) + { + btr.AddEntity(entsres); + } + var adddefres = attdef?.Invoke(); + if (adddefres is not null) + { + btr.AddEntity(adddefres); + } - /// - /// 从文件中获取块定义 - /// - /// 块表 - /// 文件名 - /// 是否覆盖 - /// 块定义Id - public static ObjectId GetBlockFrom(this SymbolTable table, string fileName, bool over) + }); + } + /// + /// 添加块定义 + /// + /// 块表 + /// 块名 + /// 图元 + /// 属性定义 + /// + public static ObjectId Add(this SymbolTable table, string name, IEnumerable? ents = null, IEnumerable? attdef = null) + { + return table.Add(name, btr => { - //FileInfo fi = new(fileName); - //string blkdefname = fi.Name; - //if (blkdefname.Contains(".")) - //{ - // blkdefname = blkdefname.Substring(0, blkdefname.LastIndexOf('.')); - //} + if (ents is not null) + { + btr.AddEntity(ents); + } + if (attdef is not null) + { + btr.AddEntity(attdef); + } + }); + } - string blkdefname = SymbolUtilityServices.RepairSymbolName(SymbolUtilityServices.GetSymbolNameFromPathName(fileName, "dwg"), false); + /// + /// 添加块定义 + /// + /// 块表 + /// 块名 + /// 图元(包括属性) + /// + public static ObjectId Add(this SymbolTable table, string name, params Entity[] ents) + { + return table.Add(name, null, () => { return ents; }); + } - ObjectId id = table[blkdefname]; - bool has = id != ObjectId.Null; - if ((has && over) || !has) + /// + /// 添加属性到块定义 + /// + /// 块表 + /// 块定义id + /// 属性列表 + public static void AddAttsToBlocks(this SymbolTable table, ObjectId id, List atts) + { + table.Change(id, btr => + { + var attTags = new List(); + btr.GetEntities() + .ForEach(def => attTags.Add(def.Tag.ToUpper())); + foreach (AttributeDefinition att in atts) { - Database db = new(); - db.ReadDwgFile(fileName, FileShare.Read, true, null); - id = table.Database.Insert(BlockTableRecord.ModelSpace, blkdefname, db, false); + if (!attTags.Contains(att.Tag.ToUpper())) + { + btr.AddEntity(att); + } } + }); + } + /// + /// 添加属性到块定义 + /// + /// 块表 + /// 块定义名字 + /// 属性列表 + public static void AddAttsToBlocks(this SymbolTable table, string name, List atts) + { + table.Change(name, btr => + { + var attTags = new List(); + btr.GetEntities() + .ForEach(def => attTags.Add(def.Tag.ToUpper())); + foreach (AttributeDefinition att in atts) + { + if (!attTags.Contains(att.Tag.ToUpper())) + { + btr.AddEntity(att); + } + } + }); + } - return id; - } - + /// + /// 从文件中获取块定义 + /// + /// 块表 + /// 文件名 + /// 是否覆盖 + /// 块定义Id + public static ObjectId GetBlockFrom(this SymbolTable table, string fileName, bool over) + { + //FileInfo fi = new(fileName); + //string blkdefname = fi.Name; + //if (blkdefname.Contains(".")) + //{ + // blkdefname = blkdefname.Substring(0, blkdefname.LastIndexOf('.')); + //} + string blkdefname = SymbolUtilityServices.RepairSymbolName(SymbolUtilityServices.GetSymbolNameFromPathName(fileName, "dwg"), false); - /// - /// 从文件中获取块定义 - /// - /// 块表 - /// 文件名 - /// 块定义名 - /// 是否覆盖 - /// 块定义Id - public static ObjectId GetBlockFrom(this SymbolTable table, string fileName, string blockName, bool over) + ObjectId id = table[blkdefname]; + bool has = id != ObjectId.Null; + if ((has && over) || !has) { - return - table.GetRecordFrom( - t => t.BlockTable, - fileName, - blockName, - over); + Database db = new(false, true); + db.ReadDwgFile(fileName, FileShare.Read, true, null); + db.CloseInput(true); + id = table.Database.Insert(BlockTableRecord.ModelSpace, blkdefname, db, false); } - #endregion + + return id; + } - #region 线型表 - /// - /// 添加线型 - /// - /// 线型表 - /// 线型名 - /// 线型说明 - /// 线型长度 - /// 笔画长度数组 - /// 线型id - public static ObjectId Add(this SymbolTable table, string name, string description, double length, double[] dash) - { - return table.Add( - name, - ltt => { - ltt.AsciiDescription = description; - ltt.PatternLength = length; //线型的总长度 + + /// + /// 从文件中获取块定义 + /// + /// 块表 + /// 文件名 + /// 块定义名 + /// 是否覆盖 + /// 块定义Id + public static ObjectId GetBlockFrom(this SymbolTable table, string fileName, string blockName, bool over) + { + return + table.GetRecordFrom( + t => t.BlockTable, + fileName, + blockName, + over); + } + #endregion + + + #region 线型表 + /// + /// 添加线型 + /// + /// 线型表 + /// 线型名 + /// 线型说明 + /// 线型长度 + /// 笔画长度数组 + /// 线型id + public static ObjectId Add(this SymbolTable table, string name, string description, double length, double[] dash) + { + return table.Add( + name, + ltt => + { + ltt.AsciiDescription = description; + ltt.PatternLength = length; //线型的总长度 ltt.NumDashes = dash.Length; //组成线型的笔画数目 for (int i = 0; i < dash.Length; i++) - { - ltt.SetDashLengthAt(i, dash[i]); - } + { + ltt.SetDashLengthAt(i, dash[i]); + } //ltt.SetDashLengthAt(0, 0.5); //0.5个单位的划线 //ltt.SetDashLengthAt(1, -0.25); //0.25个单位的空格 //ltt.SetDashLengthAt(2, 0); // 一个点 //ltt.SetDashLengthAt(3, -0.25); //0.25个单位的空格 } - ); - } - #endregion + ); + } + #endregion - #region 文字样式表 - /// - /// 添加文字样式记录 - /// - /// 文字样式表 - /// 文字样式名 - /// 字体名 - /// 宽度比例 - /// 文字样式Id - public static ObjectId Add(this SymbolTable table, string textStyleName, string font, double xscale) + #region 文字样式表 + /// + /// 添加文字样式记录 + /// + /// 文字样式表 + /// 文字样式名 + /// 字体名 + /// 宽度比例 + /// 文字样式Id + public static ObjectId Add(this SymbolTable table, + string textStyleName, + string font, + double xscale = 1.0) + { + return + table.Add( + textStyleName, + tstr => + { + tstr.Name = textStyleName; + tstr.FileName = font; + tstr.XScale = xscale; + }); + } + /// + /// 添加文字样式记录 + /// + /// 文字样式表 + /// 文字样式名 + /// 字体名枚举 + /// 宽度比例 + /// 文字样式Id + public static ObjectId Add(this SymbolTable table, string textStyleName, FontTTF fontTTF, double xscale = 1.0) + { + return table.Add(textStyleName, fontTTF.GetDesc(), xscale); + } + + /// + ///

添加文字样式记录,如果存在就默认强制替换

+ /// 此函数为了 而设 + ///
+ /// 文字样式表 + /// 文字样式名 + /// 字体名 + /// 大字体名 + /// 宽度比例 + /// 高度 + /// 是否强制替换 + /// 文字样式Id + public static ObjectId AddWithChange(this SymbolTable table, + string textStyleName, + string smallFont, + string bigFont = "", + double xScale = 1, + double height = 0, + bool forceChange = true) + { + if (forceChange && table.Has(textStyleName)) { - return - table.Add( - textStyleName, - tstr => { - tstr.Name = textStyleName; - tstr.FileName = font; - tstr.XScale = xscale; - }); + table.Change(textStyleName, ttr => + { + ttr.FileName = smallFont; + ttr.XScale = xScale; + ttr.TextSize = height; + if (bigFont != "") + { + ttr.BigFontFileName = bigFont; + } + }); + return table[textStyleName]; } - #endregion + return table.Add(textStyleName, ttr => + { + ttr.FileName = smallFont; + ttr.XScale = xScale; + ttr.TextSize = height; + }); + } - #region 注册应用程序表 - #endregion + #endregion - #region 标注样式表 + #region 注册应用程序表 - #endregion + #endregion - #region 用户坐标系表 + #region 标注样式表 - #endregion + #endregion - #region 视图表 + #region 用户坐标系表 - #endregion + #endregion - #region 视口表 + #region 视图表 - #endregion - } + #endregion + + #region 视口表 + + #endregion } diff --git a/src/IFoxCAD.Cad/ExtensionMethod/SymbolTableRecordEx.cs b/src/IFoxCAD.Cad/ExtensionMethod/SymbolTableRecordEx.cs index c66b2e2..bceeac0 100644 --- a/src/IFoxCAD.Cad/ExtensionMethod/SymbolTableRecordEx.cs +++ b/src/IFoxCAD.Cad/ExtensionMethod/SymbolTableRecordEx.cs @@ -1,353 +1,460 @@ -using System; -using System.Collections.Generic; -using System.IO; -using System.Linq; -using System.Text; +namespace IFoxCAD.Cad; - -using Autodesk.AutoCAD.Colors; -using Autodesk.AutoCAD.DatabaseServices; -using Autodesk.AutoCAD.Geometry; -using Autodesk.AutoCAD.Runtime; - -namespace IFoxCAD.Cad +/// +/// 符号表记录扩展类 +/// +public static class SymbolTableRecordEx { + #region 块表记录 + + #region 克隆实体id /// - /// 符号表记录扩展类 + /// 深度克隆id到块表记录 + /// 0x01 此方法不允许是未添加数据库的图元,因此它是id + /// 0x02 若为未添加数据库图元,则利用entity.Clone();同时不需要考虑动态块属性,可以使用entity.GetTransformedCopy /// - public static class SymbolTableRecordEx + /// + /// 克隆到当前块表记录,相当于原地克隆 + /// 克隆到目标块表记录内,相当于制作新块 + /// + /// 图元id集合,注意所有成员都要在同一个空间中 + /// 克隆后的id词典 + public static IdMapping DeepClone(this BlockTableRecord btr, ObjectIdCollection objIds) { - - - #region 块表记录 - - #region 添加实体 - /// - /// 添加实体对象 - /// - /// 块表记录 - /// 实体 - /// 事务管理器 - /// 对象 id - public static ObjectId AddEntity(this BlockTableRecord btr, Entity entity, Transaction tr = null) + if (objIds is null || objIds.Count == 0) + throw new ArgumentNullException(nameof(objIds)); + + var db = objIds[0].Database; + IdMapping mapping = new(); + using (btr.ForWrite()) { - if (entity is null) - throw new ArgumentNullException(nameof(entity), "对象为 null"); + try + { + db.DeepCloneObjects(objIds, btr.ObjectId, mapping, false); - ObjectId id; - tr ??= DBTrans.Top.Transaction; - using (btr.ForWrite()) + // 不在此提取,为了此函数被高频调用 + // 获取克隆键值对(旧块名,新块名) + // foreach (ObjectId item in blockIds) + // result.Add(mapping[item].Value); + } + catch (System.Exception e) { - id = btr.AppendEntity(entity); - tr.AddNewlyCreatedDBObject(entity, true); + LogHelper.FlagOutVsOutput = true; + e.WriteLog("深度克隆出错了"); } - return id; } + return mapping; + } - /// - /// 添加实体集合 - /// - /// 实体类型 - /// 块表记录 - /// 事务 - /// 实体集合 - /// 对象 id 列表 - public static IEnumerable AddEntity(this BlockTableRecord btr, IEnumerable ents, Transaction tr = null) where T : Entity - { - if (ents.Any(ent => ent is null)) - throw new ArgumentNullException(nameof(ents), "实体集合内存在 null 对象"); + /// + /// 克隆图元实体(这个函数有问题,会出现偶尔成功,偶尔失败,拖动过变成匿名块) + /// 若为块则进行设置属性,因此控制动态块属性丢失; + /// + /// 图元 + /// 矩阵 + //public static void EntityTransformedCopy(this Entity ent, Matrix3d matrix) + //{ + // var entNew = ent.GetTransformedCopy(matrix); + // if (ent is BlockReference blockReference) + // entNew.SetPropertiesFrom(blockReference); + //} - tr ??= DBTrans.Top.Transaction; - using (btr.ForWrite()) - { - return ents - .Select( - ent => { - ObjectId id = btr.AppendEntity(ent); - tr.AddNewlyCreatedDBObject(ent, true); - return id; - }) - .ToList(); - } - } - /// - /// 添加多个实体 - /// - /// 块表记录 - /// 实体集合 - /// 对象 id 列表 - public static IEnumerable AddEntity(this BlockTableRecord btr, params Entity[] ents) - { - return btr.AddEntity(ents, null); - } - #endregion + #endregion - #region 添加图元 - /// - /// 在指定绘图空间添加图元 - /// - /// 图元类型 - /// 绘图空间 - /// 图元对象 - /// 图元属性设置委托 - /// 事务管理器 - /// 图元id - private static ObjectId AddEnt(this BlockTableRecord btr, T ent, Action action, Transaction trans) where T : Entity - { - trans ??= DBTrans.Top.Transaction; - action?.Invoke(ent); - return btr.AddEntity(ent, trans); - } + #region 添加实体 + /// + /// 添加实体对象 + /// + /// 块表记录 + /// 实体 + /// 事务管理器 + /// 对象 id + public static ObjectId AddEntity(this BlockTableRecord btr, Entity entity, + Transaction? trans = null) + { + //if (entity is null) + // throw new ArgumentNullException(nameof(entity), "对象为 null"); - /// - /// 在指定绘图空间添加直线 - /// - /// 事务管理器 - /// 起点 - /// 终点 - /// 绘图空间 - /// 直线属性设置委托 - /// 直线的id - public static ObjectId AddLine(this BlockTableRecord btr, Point3d start, Point3d end, Action action = default, Transaction trans = default) + ObjectId id; + trans ??= DBTrans.Top.Transaction; + using (btr.ForWrite()) { - var line = new Line(start, end); - return btr.AddEnt(line, action, trans); + id = btr.AppendEntity(entity); + trans.AddNewlyCreatedDBObject(entity, true); } - /// - /// 在指定绘图空间X-Y平面添加圆 - /// - /// 绘图空间 - /// 圆心 - /// 半径 - /// 圆属性设置委托 - /// 事务管理器 - /// 圆的id - public static ObjectId AddCircle(this BlockTableRecord btr, Point3d center, double radius, Action action = default, Transaction trans = default) + return id; + } + + /// + /// 添加实体集合 + /// + /// 实体类型 + /// 块表记录 + /// 实体集合 + /// 事务 + /// 对象 id 列表 + public static IEnumerable AddEntity(this BlockTableRecord btr, IEnumerable ents, + Transaction? trans = null) where T : Entity + { + //if (ents.Any(ent => ent is null)) + // throw new ArgumentNullException(nameof(ents), "实体集合内存在 null 对象"); + + trans ??= DBTrans.Top.Transaction; + using (btr.ForWrite()) { - var circle = new Circle(center, Vector3d.ZAxis, radius); - return btr.AddEnt(circle, action, trans); + return ents + .Select( + ent => { + ObjectId id = btr.AppendEntity(ent); + trans.AddNewlyCreatedDBObject(ent, true); + return id; + }) + .ToList(); } + } - /// - /// 在指定绘图空间X-Y平面3点画外接圆 - /// - /// 绘图空间 - /// 第一点 - /// 第二点 - /// 第三点 - /// 圆属性设置委托 - /// 事务管理器 - /// 三点有外接圆则返回圆的id,否则返回ObjectId.Null - public static ObjectId AddCircle(this BlockTableRecord btr, Point3d p0, Point3d p1, Point3d p2, - Action action = default, Transaction trans = default) + /// + /// 添加多个实体 + /// + /// 块表记录 + /// 实体集合 + /// 对象 id 列表 + public static IEnumerable AddEntity(this BlockTableRecord btr, params Entity[] ents) + { + return btr.AddEntity(ents, null); + } + #endregion + + #region 添加图元 + /// + /// 在指定绘图空间添加图元 + /// + /// 图元类型 + /// 绘图空间 + /// 图元对象 + /// 图元属性设置委托 + /// 事务管理器 + /// 图元id + private static ObjectId AddEnt(this BlockTableRecord btr, T ent, Action? action, Transaction? trans) where T : Entity + { + //trans ??= DBTrans.Top.Transaction; + action?.Invoke(ent); + return btr.AddEntity(ent, trans); + } + /// + /// 委托式的添加图元 + /// + /// 块表 + /// 返回图元的委托 + /// 事务 + /// 图元id,如果委托返回 null,则为 ObjectId.Null + public static ObjectId AddEnt(this BlockTableRecord btr, Func action, Transaction? transaction) + { + //transaction ??= DBTrans.Top.Transaction; + var ent = action.Invoke(); + if (ent is null) + return ObjectId.Null; + + return btr.AddEntity(ent, transaction); + } + + /// + /// 在指定绘图空间添加直线 + /// + /// 事务管理器 + /// 起点 + /// 终点 + /// 绘图空间 + /// 直线属性设置委托 + /// 直线的id + public static ObjectId AddLine(this BlockTableRecord btr, Point3d start, Point3d end, + Action? action = default, Transaction? trans = default) + { + var line = new Line(start, end); + return btr.AddEnt(line, action, trans); + } + /// + /// 在指定绘图空间X-Y平面添加圆 + /// + /// 绘图空间 + /// 圆心 + /// 半径 + /// 圆属性设置委托 + /// 事务管理器 + /// 圆的id + public static ObjectId AddCircle(this BlockTableRecord btr, Point3d center, double radius, + Action? action = default, Transaction? trans = default) + { + var circle = new Circle(center, Vector3d.ZAxis, radius); + return btr.AddEnt(circle, action, trans); + } + + /// + /// 在指定绘图空间X-Y平面3点画外接圆 + /// + /// 绘图空间 + /// 第一点 + /// 第二点 + /// 第三点 + /// 圆属性设置委托 + /// 事务管理器 + /// 三点有外接圆则返回圆的id,否则返回ObjectId.Null + public static ObjectId AddCircle(this BlockTableRecord btr, Point3d p0, Point3d p1, Point3d p2, + Action? action = default, Transaction? trans = default) + { + var circle = EntityEx.CreateCircle(p0, p1, p2); + //return circle is not null ? btr.AddEnt(circle, action, trans) : throw new ArgumentNullException(nameof(circle), "对象为 null"); + if (circle is null) + throw new ArgumentNullException(nameof(circle), "对象为 null"); + + return btr.AddEnt(circle, action, trans); + } + /// + /// 在指定的绘图空间添加轻多段线 + /// + /// 绘图空间 + /// 多段线信息 + /// 线宽 + /// 是否闭合 + /// 轻多段线属性设置委托 + /// 事务管理器 + /// 轻多段线id + public static ObjectId AddPline(this BlockTableRecord btr, + List bvws, + double? constantWidth = null, + bool isClosed = true, + Action? action = default, + Transaction? trans = default) + { + Polyline pl = new(); + pl.SetDatabaseDefaults(); + if (constantWidth is not null) { - Circle circle = EntityEx.CreateCircle(p0, p1, p2); - return circle is not null ? btr.AddEnt(circle, action, trans) : throw new ArgumentNullException(nameof(circle), "对象为 null"); + for (int i = 0; i < bvws.Count; i++) + pl.AddVertexAt(i, bvws[i].Vertex, bvws[i].Bulge, constantWidth.Value, constantWidth.Value); } - /// - /// 在指定的绘图空间添加轻多段线 - /// - /// 绘图空间 - /// 端点表 - /// 凸度表 - /// 端点的起始宽度 - /// 端点的终止宽度 - /// 轻多段线属性设置委托 - /// 事务管理器 - /// 轻多段线id - public static ObjectId AddPline(this BlockTableRecord btr, List pts, List bulges = default, List startWidths = default, List endWidths = default, Action action = default, Transaction trans = default) + else { - bulges ??= new List(new double[pts.Count]); - startWidths ??= new List(new double[pts.Count]); - endWidths ??= new List(new double[pts.Count]); - Polyline pl = new(); - for (int i = 0; i < pts.Count; i++) - { - pl.AddVertexAt(i, pts[i].Point2d(), bulges[i], startWidths[i], endWidths[i]); - } - return btr.AddEnt(pl, action, trans); + for (int i = 0; i < bvws.Count; i++) + pl.AddVertexAt(i, bvws[i].Vertex, bvws[i].Bulge, bvws[i].StartWidth, bvws[i].EndWidth); } + pl.Closed = isClosed;//闭合 + return btr.AddEnt(pl, action, trans); + } + /// + /// 在指定的绘图空间添加轻多段线 + /// + /// 绘图空间 + /// 端点表 + /// 凸度表 + /// 端点的起始宽度 + /// 端点的终止宽度 + /// 轻多段线属性设置委托 + /// 事务管理器 + /// 轻多段线id + public static ObjectId AddPline(this BlockTableRecord btr, + List pts, + List? bulges = default, + List? startWidths = default, + List? endWidths = default, + Action? action = default, + Transaction? trans = default) + { + bulges ??= new(new double[pts.Count]); + startWidths ??= new(new double[pts.Count]); + endWidths ??= new(new double[pts.Count]); -#if ac2013 - /// - /// 在指定的绘图空间添加轻多段线 - /// - /// 绘图空间 - /// 端点表,利用元组(Point3d pt, double bulge, double startWidth, double endWidth) - /// 轻多段线属性设置委托 - /// 事务管理器 - /// 轻多段线id - public static ObjectId AddPline(this BlockTableRecord btr, List<(Point3d pt, double bulge, double startWidth, double endWidth)> pts, Action action = default, Transaction trans = default) - { + Polyline pl = new(); + pl.SetDatabaseDefaults(); - Polyline pl = new(); - pts.ForEach((i, vertex) => - { - pl.AddVertexAt(i, vertex.pt.Point2d(), vertex.bulge, vertex.startWidth, vertex.endWidth); - }); + for (int i = 0; i < pts.Count; i++) + pl.AddVertexAt(i, pts[i].Point2d(), bulges[i], startWidths[i], endWidths[i]); + return btr.AddEnt(pl, action, trans); + } - return btr.AddEnt(pl, action, trans); - } -#endif + /// + /// 在指定的绘图空间添加轻多段线 + /// + /// 绘图空间 + /// 端点表,利用元组(Point3d pt, double bulge, double startWidth, double endWidth) + /// 轻多段线属性设置委托 + /// 事务管理器 + /// 轻多段线id + public static ObjectId AddPline(this BlockTableRecord btr, + List<(Point3d pt, double bulge, double startWidth, double endWidth)> pts, + Action? action = default, + Transaction? trans = default) + { + Polyline pl = new(); + pl.SetDatabaseDefaults(); + pts.ForEach((i, vertex) => { + pl.AddVertexAt(i, vertex.pt.Point2d(), vertex.bulge, vertex.startWidth, vertex.endWidth); + }); - /// - /// 在指定绘图空间X-Y平面3点画圆弧 - /// - /// 绘图空间 - /// 圆弧起点 - /// 圆弧上的点 - /// 圆弧终点 - /// 圆弧属性设置委托 - /// 事务管理器 - /// 圆弧id - public static ObjectId AddArc(this BlockTableRecord btr, Point3d startPoint, Point3d pointOnArc, Point3d endPoint, Action action = default, Transaction trans = default) - { - var arc = EntityEx.CreateArc(startPoint, pointOnArc, endPoint); - return btr.AddEnt(arc, action, trans); - } - #endregion + return btr.AddEnt(pl, action, trans); + } - #region 获取实体/实体id - /// - /// 获取块表记录内的指定类型的实体 - /// - /// 实体类型 - /// 块表记录 - /// 事务 - /// 打开模式 - /// 实体集合 - public static IEnumerable GetEntities(this BlockTableRecord btr, OpenMode mode = OpenMode.ForRead, Transaction trans = null) where T : Entity - { - trans ??= DBTrans.Top.Transaction; - return - btr - .Cast() - .Select(id => trans.GetObject(id, mode)) - .OfType(); - } + /// + /// 在指定绘图空间X-Y平面3点画圆弧 + /// + /// 绘图空间 + /// 圆弧起点 + /// 圆弧上的点 + /// 圆弧终点 + /// 圆弧属性设置委托 + /// 事务管理器 + /// 圆弧id + public static ObjectId AddArc(this BlockTableRecord btr, + Point3d startPoint, Point3d pointOnArc, Point3d endPoint, + Action? action = default, Transaction? trans = default) + { + var arc = EntityEx.CreateArc(startPoint, pointOnArc, endPoint); + return btr.AddEnt(arc, action, trans); + } - /// - /// 按类型获取实体Id,AutoCad2010以上版本支持 - /// - /// 实体类型 - /// 块表记录 - /// 实体Id集合 - public static IEnumerable GetObjectIds(this BlockTableRecord btr) where T : Entity - { - string dxfName = RXClass.GetClass(typeof(T)).DxfName; - return btr.Cast() - .Where(id => id.ObjectClass().DxfName == dxfName); - } + // todo: 所有涉及默认无参构造的实体类型,都需要调用SetDatabaseDefaults(); + #endregion - /// - /// 按类型获取实体Id的分组 - /// - /// 块表记录 - /// 实体Id分组 - public static IEnumerable> GetObjectIds(this BlockTableRecord btr) - { - return - btr - .Cast() - .GroupBy(id => id.ObjectClass().DxfName); - } + #region 获取实体/实体id + /// + /// 获取块表记录内的指定类型的实体 + /// + /// 实体类型 + /// 块表记录 + /// 打开模式 + /// 事务 + /// 实体集合 + public static IEnumerable GetEntities(this BlockTableRecord btr, + OpenMode mode = OpenMode.ForRead, + Transaction? trans = default) where T : Entity + { + trans ??= DBTrans.Top.Transaction; + return + btr + .Cast() + .Select(id => trans.GetObject(id, mode)) + .OfType(); + } - /// - /// 获取绘制顺序表 - /// - /// 块表 - /// 事务 - /// 绘制顺序表 - public static DrawOrderTable GetDrawOrderTable(this BlockTableRecord btr, Transaction tr = null) - { - tr ??= DBTrans.Top.Transaction; - return tr.GetObject(btr.DrawOrderTableId, OpenMode.ForRead) as DrawOrderTable; - } + /// + /// 按类型获取实体Id,AutoCad2010以上版本支持 + /// + /// 实体类型 + /// 块表记录 + /// 实体Id集合 + public static IEnumerable GetObjectIds(this BlockTableRecord btr) where T : Entity + { + string dxfName = RXClass.GetClass(typeof(T)).DxfName; + return btr.Cast() + .Where(id => id.ObjectClass().DxfName == dxfName); + } - #endregion + /// + /// 按类型获取实体Id的分组 + /// + /// 块表记录 + /// 实体Id分组 + public static IEnumerable> GetObjectIds(this BlockTableRecord btr) + { + return + btr + .Cast() + .GroupBy(id => id.ObjectClass().DxfName); + } - #region 插入块参照 + /// + /// 获取绘制顺序表 + /// + /// 块表 + /// 事务 + /// 绘制顺序表 + public static DrawOrderTable? GetDrawOrderTable(this BlockTableRecord btr, + Transaction? trans = default) + { + trans ??= DBTrans.Top.Transaction; + return trans.GetObject(btr.DrawOrderTableId, OpenMode.ForRead) as DrawOrderTable; + } + #endregion - /// - /// 插入块参照 - /// - /// 插入点 - /// 块名 - /// 块插入比例,默认为1 - /// 块插入旋转角(弧度),默认为0 - /// 属性字典{Tag,Value},默认为null - /// 块参照对象id - public static ObjectId InsertBlock(this BlockTableRecord blockTableRecord, Point3d position, - string blockName, - Scale3d scale = default, - double rotation = default, - Dictionary atts = default, Transaction trans = null) + #region 插入块参照 + /// + /// 插入块参照 + /// + /// 块表记录 + /// 插入点 + /// 块名 + /// 块插入比例,默认为1 + /// 块插入旋转角(弧度),默认为0 + /// 属性字典{Tag,Value},默认为null + /// 事务 + /// 块参照对象id + public static ObjectId InsertBlock(this BlockTableRecord blockTableRecord, Point3d position, + string blockName, + Scale3d scale = default, + double rotation = default, + Dictionary? atts = default, Transaction? trans = null) + { + trans ??= DBTrans.Top.Transaction; + if (!DBTrans.Top.BlockTable.Has(blockName)) { - trans ??= DBTrans.Top.Transaction; - if (!DBTrans.Top.BlockTable.Has(blockName)) - { - DBTrans.Top.Editor.WriteMessage($"\n不存在名字为{blockName}的块定义。"); - return ObjectId.Null; - } - return blockTableRecord.InsertBlock(position, DBTrans.Top.BlockTable[blockName], scale, rotation, atts, trans); + DBTrans.Top.Editor?.WriteMessage($"\n不存在名字为{blockName}的块定义。"); + return ObjectId.Null; } - /// - /// 插入块参照 - /// - /// 插入点 - /// 块定义id - /// 块插入比例,默认为1 - /// 块插入旋转角(弧度),默认为0 - /// 属性字典{Tag,Value},默认为null - /// 块参照对象id - public static ObjectId InsertBlock(this BlockTableRecord blockTableRecord, Point3d position, - ObjectId blockId, - Scale3d scale = default, - double rotation = default, - Dictionary atts = default, Transaction tr = null) + return blockTableRecord.InsertBlock(position, DBTrans.Top.BlockTable[blockName], scale, rotation, atts, trans); + } + /// + /// 插入块参照 + /// + /// 插入点 + /// 块定义id + /// 块插入比例,默认为1 + /// 块插入旋转角(弧度),默认为0 + /// 属性字典{Tag,Value},默认为null + /// 块参照对象id + public static ObjectId InsertBlock(this BlockTableRecord blockTableRecord, Point3d position, + ObjectId blockId, + Scale3d scale = default, + double rotation = default, + Dictionary? atts = default, Transaction? trans = null) + { + trans ??= DBTrans.Top.Transaction; + if (!DBTrans.Top.BlockTable.Has(blockId)) { - tr ??= DBTrans.Top.Transaction; - if (!DBTrans.Top.BlockTable.Has(blockId)) - { - DBTrans.Top.Editor.WriteMessage($"\n不存在名字为{DBTrans.Top.GetObject(blockId).Name}的块定义。"); - return ObjectId.Null; - } - using var blockref = new BlockReference(position, blockId) - { - ScaleFactors = scale, - Rotation = rotation - }; - var objid = blockTableRecord.AddEntity(blockref); - if (atts != default) + DBTrans.Top.Editor?.WriteMessage($"\n不存在块定义。"); + return ObjectId.Null; + } + using var blockref = new BlockReference(position, blockId) + { + ScaleFactors = scale, + Rotation = rotation + }; + var objid = blockTableRecord.AddEntity(blockref); + if (atts != default) + { + var btr = DBTrans.Top.GetObject(blockref.BlockTableRecord)!; + if (btr.HasAttributeDefinitions) { - var btr = DBTrans.Top.GetObject(blockref.BlockTableRecord); - if (btr.HasAttributeDefinitions) + var attdefs = btr.GetEntities(); + foreach (var attdef in attdefs) { - var attdefs = btr - .GetEntities() - .Where(attdef => !(attdef.Constant || attdef.Invisible)); - foreach (var attdef in attdefs) - { - using AttributeReference attref = new(); - attref.SetAttributeFromBlock(attdef, blockref.BlockTransform); - attref.Position = attdef.Position.TransformBy(blockref.BlockTransform); - attref.AdjustAlignment(DBTrans.Top.Database); - if (atts.ContainsKey(attdef.Tag)) - attref.TextString = atts[attdef.Tag]; + using AttributeReference attref = new(); + attref.SetDatabaseDefaults(); + attref.SetAttributeFromBlock(attdef, blockref.BlockTransform); + attref.Position = attdef.Position.TransformBy(blockref.BlockTransform); + attref.AdjustAlignment(DBTrans.Top.Database); + + if (atts.ContainsKey(attdef.Tag)) + attref.TextString = atts[attdef.Tag]; - blockref.AttributeCollection.AppendAttribute(attref); - tr.AddNewlyCreatedDBObject(attref, true); - } + blockref.AttributeCollection.AppendAttribute(attref); + trans.AddNewlyCreatedDBObject(attref, true); } } - return objid; } - - #endregion - #endregion - - - + return objid; } + #endregion + + #endregion } diff --git a/src/IFoxCAD.Cad/ExtensionMethod/Tools.cs b/src/IFoxCAD.Cad/ExtensionMethod/Tools.cs new file mode 100644 index 0000000..0e875c5 --- /dev/null +++ b/src/IFoxCAD.Cad/ExtensionMethod/Tools.cs @@ -0,0 +1,188 @@ +namespace IFoxCAD.Cad; + +public static class Tools +{ + public static void TestTimes2(int count, string message, Action action) + { + System.Diagnostics.Stopwatch watch = new(); + watch.Start(); //开始监视代码运行时间 + for (int i = 0; i < count; i++) + action.Invoke();//需要测试的代码 + watch.Stop(); //停止监视 + TimeSpan timespan = watch.Elapsed; //获取当前实例测量得出的总时间 + double time = timespan.TotalMilliseconds; + string name = "毫秒"; + if (timespan.TotalMilliseconds > 1000) + { + time = timespan.TotalSeconds; + name = "秒"; + } + Env.Print($"{message} 代码执行 {count} 次的时间:{time} ({name})"); //总毫秒数 + } + + /// + /// 纳秒计时器 + /// + public static void TestTimes(int count, string message, Action action, + Timer.TimeEnum timeEnum = Timer.TimeEnum.Millisecond) + { + double time = Timer.RunTime(() => { + for (int i = 0; i < count; i++) + action(); + }, timeEnum); + + string timeNameZn = ""; + switch (timeEnum) + { + case Timer.TimeEnum.Second: + timeNameZn = " 秒"; + break; + case Timer.TimeEnum.Millisecond: + timeNameZn = " 毫秒"; + break; + case Timer.TimeEnum.Microsecond: + timeNameZn = " 微秒"; + break; + case Timer.TimeEnum.Nanosecond: + timeNameZn = " 纳秒"; + break; + } + + Env.Print($"{message} 代码执行 {count} 次的时间:{time} ({timeNameZn})"); + } +} + +/* +//测试例子,同时验证两个计时器 +var stopwatch = new Stopwatch(); +Timer.RunTime(() => { + + stopwatch.Start(); + for (int i = 0; i < 10000000; i++) + i++; + stopwatch.Stop(); + +}, Timer.TimeEnum.Millisecond, "运行:"); +Console.WriteLine("运行毫秒:" + stopwatch.ElapsedMilliseconds); + */ + +public class Timer +{ + [Flags] + public enum TimeEnum + { + /// + /// 秒 + /// + Second, + /// + /// 毫秒 + /// + Millisecond, + /// + /// 微秒 + /// + Microsecond, + /// + /// 纳秒 + /// + Nanosecond, + } + + [DllImport("Kernel32.dll")] + static extern bool QueryPerformanceCounter(out long lpPerformanceCount); + + /// + /// 这个函数会检索性能计数器的频率. + /// 性能计数器的频率在系统启动时是固定的,并且在所有处理器上都是一致的 + /// 因此,只需在应用初始化时查询频率,即可缓存结果 + /// 在运行 Windows XP 或更高版本的系统上,该函数将始终成功,因此永远不会返回零 + /// + /// + /// + [DllImport("Kernel32.dll")] + static extern bool QueryPerformanceFrequency(out long lpFrequency); + + long _startTime, _stopTime; + long _freq; + + public Timer() + { + _startTime = 0; + _stopTime = 0; + + if (!QueryPerformanceFrequency(out _freq)) + throw new Win32Exception("不支持高性能计数器"); + } + + /// + /// 开始计时器 + /// + public void Start() + { + System.Threading.Thread.Sleep(0); + QueryPerformanceCounter(out _startTime); + } + + /// + /// 停止计时器 + /// + public void Stop() + { + QueryPerformanceCounter(out _stopTime); + _Second = (double)(_stopTime - _startTime) / _freq; + } + double _Second = 0; + + // 返回计时器经过时间 + public double Second => _Second; + public double Millisecond => _Second * 1000.0; + public double Microsecond => _Second * 1000000.0; + public double Nanosecond => _Second * 1000000000.0; + + public static double RunTime(Action action, TimeEnum timeEnum = TimeEnum.Millisecond, string? msg = null) + { + var nanoSecond = new Timer(); + nanoSecond.Start(); + action(); + nanoSecond.Stop(); + + double time = 0; + switch (timeEnum) + { + case TimeEnum.Second: + time = nanoSecond.Second; + break; + case TimeEnum.Millisecond: + time = nanoSecond.Millisecond; + break; + case TimeEnum.Microsecond: + time = nanoSecond.Microsecond; + break; + case TimeEnum.Nanosecond: + time = nanoSecond.Nanosecond; + break; + } + if (msg != null) + { + string timeNameZn = ""; + switch (timeEnum) + { + case TimeEnum.Second: + timeNameZn = " 秒"; + break; + case TimeEnum.Millisecond: + timeNameZn = " 毫秒"; + break; + case TimeEnum.Microsecond: + timeNameZn = " 微秒"; + break; + case TimeEnum.Nanosecond: + timeNameZn = " 纳秒"; + break; + } + Env.Print(msg + " " + time + timeNameZn); + } + return time; + } +} \ No newline at end of file diff --git "a/src/IFoxCAD.Cad/ExtensionMethod/\346\226\260\345\273\272\345\241\253\345\205\205/HatchConverter.cs" "b/src/IFoxCAD.Cad/ExtensionMethod/\346\226\260\345\273\272\345\241\253\345\205\205/HatchConverter.cs" new file mode 100644 index 0000000..7868bee --- /dev/null +++ "b/src/IFoxCAD.Cad/ExtensionMethod/\346\226\260\345\273\272\345\241\253\345\205\205/HatchConverter.cs" @@ -0,0 +1,358 @@ +namespace IFoxCAD.Cad; +using PointV = Point2d; + +/// +/// 填充边界转换器 +/// +public class HatchConverter +{ + #region 辅助类 + /// + /// 生成圆形数据 + /// + class CircleData + { + public PointV Center; + public double Radius; + + /// + /// 生成圆形数据 + /// + /// 对称点1 + /// 对称点2 + public CircleData(PointV symmetryAxisPoint1, PointV symmetryAxisPoint2) + { + Center = symmetryAxisPoint1.GetCenter(symmetryAxisPoint2); + Radius = symmetryAxisPoint1.GetDistanceTo(symmetryAxisPoint2) * 0.5; + } + } + + /// + /// 填充转换器的数据 + /// + class HatchConverterData + { + public List PolyLineData; + public List CircleData; + public List SplineData; + + /// + /// 填充转换器的数据 + /// + public HatchConverterData() + { + PolyLineData = new(); + CircleData = new(); + SplineData = new(); + } + } + #endregion + + #region 成员 + /// + /// 外部只能调用id,否则跨事务造成错误 + /// + public ObjectId OldHatchId + { + get + { + if (_oldHatch is null) + return ObjectId.Null; + return _oldHatch.ObjectId; + } + } + readonly Hatch? _oldHatch; + + readonly List _hcDatas; + /// + /// 生成的填充边界id + /// + public List BoundaryIds; + #endregion + + #region 构造 + /// + /// 填充边界转换器 + /// + HatchConverter() + { + _hcDatas = new(); + BoundaryIds = new(); + } + + /// + /// 填充边界转换器 + /// + /// 需要转化的Hatch对象 + public HatchConverter(Hatch hatch) : this() + { + _oldHatch = hatch; + + //不能在提取信息的时候进行新建cad图元, + //否则cad将会提示遗忘释放 + hatch.ForEach(loop => { + var hcData = new HatchConverterData(); + + bool isCurve2d = true; + if (loop.IsPolyline) + { + //边界是多段线 + HatchLoopIsPolyline(loop, hcData); + isCurve2d = false; + } + else + { + if (loop.Curves.Count == 2)//1是不可能的,大于2的是曲线 + { + //边界是曲线,过滤可能是圆形的情况 + var cir = TwoArcFormOneCircle(loop); + if (cir is not null) + { + hcData.CircleData.Add(cir); + isCurve2d = false; + } + } + } + + //边界是曲线 + if (isCurve2d) + HatchLoopIsCurve2d(loop, hcData); + + _hcDatas.Add(hcData); + }); + } + #endregion + + #region 方法 + /// + /// 多段线处理 + /// + /// 填充边界 + /// 收集图元信息 + static void HatchLoopIsPolyline(HatchLoop loop, HatchConverterData hcData) + { + if (loop is null) + throw new ArgumentNullException(nameof(loop)); + + if (hcData is null) + throw new ArgumentNullException(nameof(hcData)); + + //判断为圆形: + //上下两个圆弧,然后填充,就会生成此种填充 + //顶点数是3,凸度是半圆,两个半圆就是一个圆形 + if (loop.Polyline.Count == 3 && loop.Polyline[0].Bulge == 1 && loop.Polyline[1].Bulge == 1 || + loop.Polyline.Count == 3 && loop.Polyline[0].Bulge == -1 && loop.Polyline[1].Bulge == -1) + { + hcData.CircleData.Add(new CircleData(loop.Polyline[0].Vertex, loop.Polyline[1].Vertex)); + } + else + { + //遍历多段线信息 + var bvc = loop.Polyline; + for (int i = 0; i < bvc.Count; i++) + hcData.PolyLineData.Add(new BulgeVertexWidth(bvc[i])); + } + } + + /// + /// 两个圆弧组成圆形 + /// + /// + /// + static CircleData? TwoArcFormOneCircle(HatchLoop loop) + { + if (loop is null) + throw new ArgumentNullException(nameof(loop)); + + if (loop.Curves.Count != 2) + throw new ArgumentException( + "边界非多段线,而且点数!=2,点数为:" + nameof(loop.Curves.Count) + ";两个矩形交集的时候会出现此情况."); + + CircleData? circular = null; + + //判断为圆形: + //用一条(不是两条)多段线画出两条圆弧为正圆,就会生成此种填充 + //边界为曲线,数量为2,可能是两个半圆曲线,如果是,就加入圆形数据中 + + //第一段 + var getCurves1Pts = loop.Curves[0].GetSamplePoints(3); //曲线取样点分两份(3点) + var mid1Pt = getCurves1Pts[1]; //腰点 + double bulge1 = loop.Curves[0].StartPoint.GetArcBulge(mid1Pt, loop.Curves[0].EndPoint); + + //第二段 + var getCurves2Pts = loop.Curves[1].GetSamplePoints(3); + var mid2Pt = getCurves2Pts[1]; + double bulge2 = loop.Curves[1].StartPoint.GetArcBulge(mid2Pt, loop.Curves[1].EndPoint); + + //第一段上弧&&第二段反弧 || 第一段反弧&&第二段上弧 + if (bulge1 == -1 && bulge2 == -1 || bulge1 == 1 && bulge2 == 1) + circular = new CircleData(loop.Curves[0].StartPoint, loop.Curves[1].StartPoint); //两个起点就是对称点 + + return circular; + } + + /// + /// 处理边界曲线 + /// + /// 填充边界 + /// 收集图元信息 + static void HatchLoopIsCurve2d(HatchLoop loop, HatchConverterData hcData) + { + //取每一段曲线,曲线可能是直线来的,但是圆弧会按照顶点来分段 + int curveIsClosed = 0; + + //遍历边界的多个子段 + foreach (Curve2d curve in loop.Curves) + { + //计数用于实现闭合 + curveIsClosed++; + if (curve is NurbCurve2d spl) + { + //判断为样条曲线: + hcData.SplineData.Add(spl); + continue; + } + + var pts = curve.GetSamplePoints(3); + var midPt = pts[1]; + if (curve.StartPoint.IsEqualTo(curve.EndPoint, new Tolerance(1e-6, 1e-6)))//首尾相同,就是圆形 + { + //判断为圆形: + //获取起点,然后采样三点,中间就是对称点(直径点) + hcData.CircleData.Add(new CircleData(curve.StartPoint, midPt)); + continue; + } + + //判断为多段线,圆弧: + double bulge = curve.StartPoint.GetArcBulge(midPt, curve.EndPoint); + hcData.PolyLineData.Add(new BulgeVertexWidth(curve.StartPoint, bulge)); + + //末尾点,不闭合的情况下就要获取这个 + if (curveIsClosed == loop.Curves.Count) + hcData.PolyLineData.Add(new BulgeVertexWidth(curve.EndPoint, 0)); + } + } + + /// + /// 创建边界图元 + /// + /// 返回图元 + public void CreateBoundaryEntitys(List outEnts) + { + for (int i = 0; i < _hcDatas.Count; i++) + { + var data = _hcDatas[i]; + + //生成边界:多段线 + if (data.PolyLineData.Count > 0) + { + Polyline pl = new(); + pl.SetDatabaseDefaults(); + for (int j = 0; j < data.PolyLineData.Count; j++) + { + pl.AddVertexAt(j, + data.PolyLineData[j].Vertex, + data.PolyLineData[j].Bulge, + data.PolyLineData[j].StartWidth, + data.PolyLineData[j].EndWidth); + } + outEnts.Add(pl); + } + + //生成边界:圆 + data.CircleData.ForEach(item => { + outEnts.Add(new Circle(item.Center.Point3d(), Vector3d.ZAxis, item.Radius)); + }); + + //生成边界:样条曲线 + data.SplineData.ForEach(item => { + outEnts.Add(item.ToCurve()); + }); + } + + if (_oldHatch is not null) + { + outEnts.ForEach(ent => { + ent.Color = _oldHatch.Color; + ent.Layer = _oldHatch.Layer; + }); + } + } + + + /// + /// 创建边界图元和新填充到当前空间 + /// + /// 事务 + /// 数据库 + /// 边界关联 + /// 是否创建填充,false则只创建边界 + /// 新填充id,边界在获取 + public ObjectId CreateBoundarysAndHatchToMsPs(BlockTableRecord btrOfAddEntitySpace, + bool boundaryAssociative = true, + bool createHatchFlag = true, + Transaction? trans = null) + { + //重设边界之前肯定是有边界才可以 + if (BoundaryIds.Count == 0) + { + List boundaryEntitys = new(); + CreateBoundaryEntitys(boundaryEntitys); + boundaryEntitys.ForEach(ent => { + BoundaryIds.Add(btrOfAddEntitySpace.AddEntity(ent)); + }); + } + + if (!createHatchFlag) + return ObjectId.Null; + /* + * 此处为什么要克隆填充,而不是新建填充? + * 因为填充如果是新建的,那么将会丢失基点,概念如下: + * 两个一样的填充,平移其中一个,那么再提取他们的基点会是一样的! + * 所以生成时候就不等同于画面相同. + * 也因为我不知道什么新建方式可以新建一模一样的填充,因此使用了克隆 + * 那么它的平移后的基点在哪里呢? + */ + + var newHatchId = btrOfAddEntitySpace.DeepClone(new ObjectIdCollection(new ObjectId[] { OldHatchId })).GetValues()[0]; + trans ??= DBTrans.Top.Transaction; + var hatchEnt = trans.GetObject(newHatchId, OpenMode.ForWrite) as Hatch; + if (hatchEnt != null) + { + ResetBoundary(hatchEnt, boundaryAssociative); + hatchEnt.DowngradeOpen(); + } + return newHatchId; + } + + + /// + /// 重设边界 + /// + /// + /// 边界关联 + void ResetBoundary(Hatch hatch, + bool boundaryAssociative = true) + { + //删除原有边界 + while (hatch.NumberOfLoops != 0) + hatch.RemoveLoopAt(0); + + hatch.Associative = boundaryAssociative; + + var obIds = new ObjectIdCollection(); + for (int i = 0; i < BoundaryIds.Count; i++) + { + obIds.Clear(); + obIds.Add(BoundaryIds[i]); + //要先添加最外面的边界 + if (i == 0) + hatch.AppendLoop(HatchLoopTypes.Outermost, obIds); + else + hatch.AppendLoop(HatchLoopTypes.Default, obIds); + } + //计算填充并显示 + hatch.EvaluateHatch(true); + } + #endregion +} \ No newline at end of file diff --git "a/src/IFoxCAD.Cad/ExtensionMethod/\346\226\260\345\273\272\345\241\253\345\205\205/HatchEx.cs" "b/src/IFoxCAD.Cad/ExtensionMethod/\346\226\260\345\273\272\345\241\253\345\205\205/HatchEx.cs" new file mode 100644 index 0000000..8bfd043 --- /dev/null +++ "b/src/IFoxCAD.Cad/ExtensionMethod/\346\226\260\345\273\272\345\241\253\345\205\205/HatchEx.cs" @@ -0,0 +1,15 @@ +namespace IFoxCAD.Cad; + +public static class HatchEx +{ + /// + /// 遍历填充每条边 + /// + /// + /// + public static void ForEach(this Hatch hatch, Action action) + { + for (int i = 0; i < hatch.NumberOfLoops; i++) + action.Invoke(hatch.GetLoopAt(i)); + } +} \ No newline at end of file diff --git "a/src/IFoxCAD.Cad/ExtensionMethod/\346\226\260\345\273\272\345\241\253\345\205\205/HatchInfo.cs" "b/src/IFoxCAD.Cad/ExtensionMethod/\346\226\260\345\273\272\345\241\253\345\205\205/HatchInfo.cs" new file mode 100644 index 0000000..1d66417 --- /dev/null +++ "b/src/IFoxCAD.Cad/ExtensionMethod/\346\226\260\345\273\272\345\241\253\345\205\205/HatchInfo.cs" @@ -0,0 +1,351 @@ +namespace IFoxCAD.Cad; + +/* + * ӵĵһ߽߽,ڶͼı߽硣 + * Ҫⲿ߽,ʹӻΪ HatchLoopTypes.Outermost AppendLoop , + * һ߽类,ͿԼı߽硣 + * ڲ߽ʹô HatchLoopTypes.Default AppendLoop + * + * ߽ʱ,ӵ(߽,߽,߽,߽ͨ....) + * ߽ʱ,ӵ(߽,߽ͨ.....߽,߽ͨ....) + */ + +/// +/// ͼ +/// +public class HatchInfo +{ + #region Ա + /// + /// ߽id(ŵһ) + /// + readonly List _boundaryIds; + /// + /// ͼԪ + /// + readonly Hatch _hatch; + /// + /// ߽(˴ֱ=>Ա,Ϊ뷴Ӧ) + /// + readonly bool _boundaryAssociative; + /// + /// :û(̶)//ݶļ + /// + string? _hatchName; + /// + /// ģʽ(Ԥ/û/Զ) + /// + HatchPatternType _patternTypeHatch; + /// + /// ģʽ + /// + GradientPatternType _patternTypeGradient; + /// + /// / + /// + double Scale => _hatch.PatternScale; + /// + /// Ƕ + /// + double Angle => _hatch.PatternAngle; + #endregion + + #region + HatchInfo() + { + _hatch = new Hatch(); + _hatch.SetDatabaseDefaults(); + _boundaryIds = new(); + } + + /// + /// ͼ + /// + /// ߽ + /// ԭ + /// + /// Ƕ + public HatchInfo(bool boundaryAssociative = true, + Point2d? hatchOrigin = null, + double hatchScale = 1, + double hatchAngle = 0) : this() + { + if (hatchScale <= 0) + throw new ArgumentException("Сڵ0"); + + _hatch.PatternScale = hatchScale;// + _hatch.PatternAngle = hatchAngle;//Ƕ + _boundaryAssociative = boundaryAssociative; + + hatchOrigin ??= Point2d.Origin; + _hatch.Origin = hatchOrigin.Value; //ԭ + } + + /// + /// ͼ + /// + /// ߽ + /// ߽ + /// ԭ + /// + /// Ƕ + public HatchInfo(IEnumerable boundaryIds, + bool boundaryAssociative = true, + Point2d? hatchOrigin = null, + double hatchScale = 1, + double hatchAngle = 0) + : this(boundaryAssociative, hatchOrigin, hatchScale, hatchAngle) + { + _boundaryIds.AddRange(boundaryIds); + } + + #endregion + + #region + /// + /// ģʽ1:Ԥ + /// + public HatchInfo Mode1PreDefined(string name) + { + _hatchName = name; + _hatch.HatchObjectType = HatchObjectType.HatchObject; //(/) + _patternTypeHatch = HatchPatternType.PreDefined; + return this; + } + + /// + /// ģʽ2:û + /// + /// Ƿ˫ + public HatchInfo Mode2UserDefined(bool patternDouble = true) + { + _hatchName = "_USER"; + _hatch.HatchObjectType = HatchObjectType.HatchObject; //(/) + _patternTypeHatch = HatchPatternType.UserDefined; + + _hatch.PatternDouble = patternDouble; //Ƿ˫򣨱д SetHatchPattern ֮ǰ + _hatch.PatternSpace = Scale; //ࣨд SetHatchPattern ֮ǰ + return this; + } + + /// + /// ģʽ3:Զ + /// + /// + public HatchInfo Mode3UserDefined(string name) + { + _hatchName = name; + _hatch.HatchObjectType = HatchObjectType.HatchObject; //(/) + _patternTypeHatch = HatchPatternType.CustomDefined; + return this; + } + + /// + /// ģʽ4: + /// + /// + /// ɫʼɫ + /// ɫɫ + /// ƶ + /// ɫֵ + /// ɫ˫ɫ + public HatchInfo Mode4Gradient(GradientName name, Color colorStart, Color colorEnd, + float gradientShift = 0, + float shadeTintValue = 0, + bool gradientOneColorMode = false) + { + //entgetֱȻ"SOLID",Ϊ"","" + _hatchName = name.ToString(); + _hatch.HatchObjectType = HatchObjectType.GradientObject; //(/) + _patternTypeGradient = GradientPatternType.PreDefinedGradient;//ģʽ4: + //_patternTypeGradient = GradientPatternType.UserDefinedGradient;//ģʽ5:..ģʽɶ + + //ýɫʼͽɫ + var gColor1 = new GradientColor(colorStart, 0); + var gColor2 = new GradientColor(colorEnd, 1); + _hatch.SetGradientColors(new GradientColor[] { gColor1, gColor2 }); + + _hatch.GradientShift = gradientShift; //ݶλ + _hatch.ShadeTintValue = shadeTintValue; //Ӱɫֵ + _hatch.GradientOneColorMode = gradientOneColorMode;//䵥ɫ/˫ɫ + _hatch.GradientAngle = Angle; //Ƕ + + return this; + } + + /// + /// + /// + /// ˿ռ + public ObjectId Build(BlockTableRecord btrOfAddEntitySpace) + { + //ݿ + var hatchId = btrOfAddEntitySpace.AddEntity(_hatch); + + //ģʽ:/ + if (_hatch.HatchObjectType == HatchObjectType.GradientObject) + _hatch.SetGradient(_patternTypeGradient, _hatchName); + else + _hatch.SetHatchPattern(_patternTypeHatch, _hatchName); + + //߽,ݿռھͻ + //Ϊ true 뷴Ӧ,˱Ƚ(ά뽫ʮɺ),. + _hatch.Associative = _boundaryAssociative; + + // AppendLoop ؼ,Ͳ + if (_boundaryIds.Count > 0) + AppendLoop(_boundaryIds, HatchLoopTypes.Default); + + //䲢ʾ(߽,쳣) + _hatch.EvaluateHatch(true); + + return hatchId; + } + + /// + /// ִͼԪ޸ + /// + /// ӳʵ + public HatchInfo Action(Action action) + { + action(_hatch); + return this; + } + + /// + /// ձ߽缯 + /// + public HatchInfo ClearBoundary() + { + _boundaryIds.Clear(); + return this; + } + + /// + /// ɾ߽ͼԪ + /// + public HatchInfo EraseBoundary() + { + for (int i = 0; i < _boundaryIds.Count; i++) + _boundaryIds[i].Erase(); + return this; + } + + /// + /// ߽ + /// + /// ߽id + /// 뷽ʽ + void AppendLoop(IEnumerable boundaryIds, + HatchLoopTypes hatchLoopTypes = HatchLoopTypes.Default) + { + var obIds = new ObjectIdCollection(); + //߽DZպϵ,Ѿݿ + //պϻ. + foreach (var border in boundaryIds) + { + obIds.Clear(); + obIds.Add(border); + _hatch.AppendLoop(hatchLoopTypes, obIds); + } + obIds.Dispose(); + } + + /// + /// ߽(¸߰汾亯) + /// + /// 㼯 + /// ͹ȼ + /// ˿ռ + /// 뷽ʽ + /// + public HatchInfo AppendLoop(Point2dCollection pts!!, + DoubleCollection bluges, + BlockTableRecord btrOfAddEntitySpace, + HatchLoopTypes hatchLoopTypes = HatchLoopTypes.Default) + { + var ptsEnd2End = pts.End2End(); +#if NET35 + _boundaryIds.Add(CreateAddBoundary(ptsEnd2End, bluges, btrOfAddEntitySpace)); +#else + //2011API,ԲͼԪ¼߽, + //ͨĻ,߽ _boundaryIds ǿյ,ô Build() ʱҪ˿յ + _hatch.AppendLoop(hatchLoopTypes, ptsEnd2End, bluges); +#endif + return this; + } + +#if NET35 + /// + /// ͨ㼯͹ɱ߽Ķ + /// + /// 㼯 + /// ͹ȼ + /// ˿ռ + /// id + static ObjectId CreateAddBoundary(Point2dCollection? pts, + DoubleCollection? bluges, + BlockTableRecord btrOfAddEntitySpace) + { + if (pts is null) + throw new ArgumentException(null, nameof(pts)); + if (bluges is null) + throw new ArgumentException(null, nameof(bluges)); + + var bvws = new List(); + + var itor1 = pts.GetEnumerator(); + var itor2 = bluges.GetEnumerator(); + while (itor1.MoveNext() && itor2.MoveNext()) + bvws.Add(new BulgeVertexWidth(itor1.Current, itor2.Current)); + + return btrOfAddEntitySpace.AddPline(bvws); + } +#endif + #endregion + + #region ö + /// + /// ɫͼ + /// + public enum GradientName + { + /// + /// ״ + /// + Linear, + /// + /// Բ״ + /// + Cylinder, + /// + /// Բ״ + /// + Invcylinder, + /// + /// ״ + /// + Spherical, + /// + /// ״ + /// + Invspherical, + /// + /// ״ + /// + Hemisperical, + /// + /// ״ + /// + InvHemisperical, + /// + /// ״ + /// + Curved, + /// + /// ״ + /// + Incurved + } + #endregion +} \ No newline at end of file diff --git "a/src/IFoxCAD.Cad/ExtensionMethod/\346\226\260\345\273\272\346\226\207\345\255\227/AttachmentPointHelper.cs" "b/src/IFoxCAD.Cad/ExtensionMethod/\346\226\260\345\273\272\346\226\207\345\255\227/AttachmentPointHelper.cs" new file mode 100644 index 0000000..989721c --- /dev/null +++ "b/src/IFoxCAD.Cad/ExtensionMethod/\346\226\260\345\273\272\346\226\207\345\255\227/AttachmentPointHelper.cs" @@ -0,0 +1,95 @@ +namespace IFoxCAD.Cad; + +public static class AttachmentPointHelper +{ + static readonly Dictionary _alignment = new() + { + { "左上", AttachmentPoint.TopLeft }, + { "中上", AttachmentPoint.TopCenter },//单行的对齐 + { "右上", AttachmentPoint.TopRight }, + + { "左中", AttachmentPoint.MiddleLeft }, + { "正中", AttachmentPoint.MiddleCenter },//多行的正中 + { "右中", AttachmentPoint.MiddleRight }, + + { "左对齐", AttachmentPoint.BaseLeft },//※优先(放在前面优先获取) + { "左", AttachmentPoint.BaseLeft }, + + { "中间", AttachmentPoint.BaseMid }, + + { "右对齐", AttachmentPoint.BaseRight },//※优先(放在前面优先获取) + { "右", AttachmentPoint.BaseRight }, + + { "左下", AttachmentPoint.BottomLeft }, + { "中下", AttachmentPoint.BottomCenter }, + { "右下", AttachmentPoint.BottomRight }, + + { "对齐", AttachmentPoint.BaseAlign },//※优先(放在前面优先获取) + { "调整", AttachmentPoint.BaseAlign }, + + { "居中", AttachmentPoint.BaseCenter },//单行的中 + { "铺满", AttachmentPoint.BaseFit }, + }; + + /// + /// 输入文字获得对齐方式 + /// + /// + /// + public static AttachmentPoint Get(string key) + { + return _alignment[key]; + } + + /// + /// 输入对齐方式获得文字 + /// + /// + /// + public static string Get(AttachmentPoint value) + { + return _alignment.FirstOrDefault(q => q.Value == value).Key; + } +} + +#if false +//反射描述 +//这些东西cad没有用到啊...所以不纳入了 +public enum AttachmentPoint2 +{ + [Description("下对齐")] + BottomAlign = 14, + [Description("中对齐")] + MiddleAlign = 15,//0xF + [Description("上对齐")] + TopAlign = 16,//0x10 + [Description("下铺满")] + BottomFit = 18, + [Description("中铺满")] + MiddleFit = 19, + [Description("上铺满")] + TopFit = 20, + [Description("下居中")] + BottomMid = 22, + [Description("中居中")] + MiddleMid = 23, + [Description("下居中")] + TopMid = 24, +} + +public static Dictionary GetEnumDic(Type enumType) +{ + Dictionary dic = new(); + var fieldinfos = enumType.GetFields(); + for (int i = 0; i < fieldinfos.Length; i++) + { + var field = fieldinfos[i]; + if (field.FieldType.IsEnum) + { + var objs = field.GetCustomAttributes(typeof(DescriptionAttribute), false); + dic.Add(field.Name, ((DescriptionAttribute)objs[0]).Description); + } + } + return dic; +} +#endif diff --git "a/src/IFoxCAD.Cad/ExtensionMethod/\346\226\260\345\273\272\346\226\207\345\255\227/TextEntityAdd.cs" "b/src/IFoxCAD.Cad/ExtensionMethod/\346\226\260\345\273\272\346\226\207\345\255\227/TextEntityAdd.cs" new file mode 100644 index 0000000..5b37a31 --- /dev/null +++ "b/src/IFoxCAD.Cad/ExtensionMethod/\346\226\260\345\273\272\346\226\207\345\255\227/TextEntityAdd.cs" @@ -0,0 +1,62 @@ +namespace IFoxCAD.Cad; + +public static partial class EntityAdd +{ + /// + /// 创建单行文字 + /// + /// 数据库 + /// 内容 + /// 插入点 + /// 字体高度 + /// 文字样式 + /// 对齐方式 + /// 对齐点,因样式 可能无效 + /// + public static Entity AddDBTextToEntity(this Database db, + string textContents, + Point3d position, + double textHigh = 2.5, + ObjectId? textStyleId = null, + AttachmentPoint justify = AttachmentPoint.BaseLeft, + Point3d? justifyPoint = null) + { + var TextInfo = new TextInfo( + textContents, + position, + justify, + justifyPoint, + textStyleId, + textHigh, + db); + return TextInfo.AddDBTextToEntity(); + } + + /// + /// 新建多行文字 + /// + /// 数据库 + /// 内容 + /// 插入点 + /// 字体高度 + /// 文字样式 + /// 对齐方式 + /// + public static Entity AddMTextToEntity(this Database db, + string textContents, + Point3d position, + double textHigh = 2.5, + ObjectId? textStyleId = null, + AttachmentPoint justify = AttachmentPoint.BaseLeft) + { + var TextInfo = new TextInfo( + textContents, + position, + justify, + null, + textStyleId, + textHigh, + db); + return TextInfo.AddMTextToEntity(); + } +} diff --git "a/src/IFoxCAD.Cad/ExtensionMethod/\346\226\260\345\273\272\346\226\207\345\255\227/TextInfo.cs" "b/src/IFoxCAD.Cad/ExtensionMethod/\346\226\260\345\273\272\346\226\207\345\255\227/TextInfo.cs" new file mode 100644 index 0000000..573963b --- /dev/null +++ "b/src/IFoxCAD.Cad/ExtensionMethod/\346\226\260\345\273\272\346\226\207\345\255\227/TextInfo.cs" @@ -0,0 +1,178 @@ +namespace IFoxCAD.Cad; + +/// +/// 文字信息类 +/// +public class TextInfo +{ + readonly Database? Database; + readonly string? Contents; + readonly Point3d Position; + + public string TextJustifyCn => AttachmentPointHelper.Get(TextJustify); + readonly AttachmentPoint TextJustify; + readonly Point3d? AlignmentPoint; + + readonly double TextHeight; + readonly ObjectId? TextStyleId; + + /// + /// 文字信息类 + /// + /// 内容 + /// 基点 + /// 对齐方式 + /// 对齐点(对齐方式是左,此参数无效,为null不为左就报错) + /// 文字样式id + /// 文字高度 + /// 数据库 + public TextInfo(string? contents, + Point3d position, + AttachmentPoint justify, + Point3d? justifyPoint = null, + ObjectId? textStyleId = null, + double textHeight = 2.5, + Database? database = null) + { + Contents = contents; + Position = position; + TextJustify = justify; + + if (justifyPoint is null && TextJustify != AttachmentPoint.BaseLeft) + throw new ArgumentNullException(nameof(justifyPoint)); + + AlignmentPoint = justifyPoint; + TextHeight = textHeight; + TextStyleId = textStyleId; + Database = database; + } + + /// + /// 创建单行文字 + /// + public DBText AddDBTextToEntity() + { + if (string.IsNullOrEmpty(Contents)) + throw new ArgumentNullException(nameof(Contents) + "创建文字无内容"); + + var acText = new DBText(); + acText.SetDatabaseDefaults(); + + if (Database is not null) + acText.SetDatabaseDefaults(Database);//我的默认值是填满的,所以可以不需要 + + if (TextStyleId is not null) + acText.SetTextStyleId(TextStyleId.Value); + + acText.Height = TextHeight; //高度 + acText.TextString = Contents; //内容 + acText.Position = Position; //插入点(一定要先设置) + acText.Justify = TextJustify; //使他们对齐 + //acText.HorizontalMode + + if (AlignmentPoint is not null) + acText.AlignmentPoint = AlignmentPoint.Value; + else if (acText.Justify != AttachmentPoint.BaseLeft) + acText.AlignmentPoint = Position; + + if (Database is not null) + acText.AdjustAlignment(Database); + return acText; + } + + /// + /// 创建多行文字 + /// + /// + public MText AddMTextToEntity() + { + if (string.IsNullOrEmpty(Contents)) + throw new ArgumentNullException(nameof(Contents) + "创建文字无内容"); + + var mText = new MText(); + mText.SetDatabaseDefaults(); + + if (Database is not null) + mText.SetDatabaseDefaults(Database); + + if (TextStyleId is not null) + mText.SetTextStyleId(TextStyleId.Value); + + mText.TextHeight = TextHeight; //高度 + mText.Contents = Contents; //内容 + mText.Location = Position; //插入点(一定要先设置) + + //mText.SetAttachmentMovingLocation(TextJustify); + mText.Attachment = TextJustify;//使他们对齐 + + return mText; + } +} + +//反射设定对象的文字样式id +public static partial class TextInfoHelper +{ + /// + /// 设置文字样式id + /// + /// 单行文字 + /// 文字样式表记录id + public static void SetTextStyleId(this DBText acText, ObjectId ltrObjectId) + { + SetEntityTxtStyleId(acText, ltrObjectId); + } + + /// + /// 设置文字样式id + /// + /// 多行文字 + /// 文字样式表记录id + public static void SetTextStyleId(this MText acText, ObjectId ltrObjectId) + { + SetEntityTxtStyleId(acText, ltrObjectId); + } + + static void SetEntityTxtStyleId(Entity acText, ObjectId ltrObjectId) + { + GetTextStyleIdType(acText)?.SetValue(acText, ltrObjectId, null); + } + + /// + /// 获取文字样式id + /// + public static ObjectId GetTextStyleId(this DBText acText) + { + return GetEntityTxtStyleId(acText); + } + + /// + /// 获取文字样式id + /// + public static ObjectId GetTextStyleId(this MText acText) + { + return GetEntityTxtStyleId(acText); + } + + static ObjectId GetEntityTxtStyleId(Entity acText) + { + var result = ObjectId.Null; + var id = GetTextStyleIdType(acText)?.GetValue(acText, null); + if (id != null) + result = (ObjectId)id; + return result; + } + + static PropertyInfo? _textStyleId = null; + static PropertyInfo GetTextStyleIdType(Entity acText) + { + if (_textStyleId == null) + { + var entType = acText.GetType(); + var prs = entType.GetProperties(); + _textStyleId = prs.FirstOrDefault(a => a.Name == "TextStyle");//反射获取属性 + if (_textStyleId == null) + _textStyleId = prs.FirstOrDefault(a => a.Name == "TextStyleId");//反射获取属性 + } + return _textStyleId; + } +} diff --git a/src/IFoxCAD.Cad/GlobalUsings.cs b/src/IFoxCAD.Cad/GlobalUsings.cs new file mode 100644 index 0000000..7051b36 --- /dev/null +++ b/src/IFoxCAD.Cad/GlobalUsings.cs @@ -0,0 +1,31 @@ +/// 系统引用 +global using System; +global using System.Collections; +global using System.Collections.Generic; +global using System.IO; +global using System.Linq; +global using System.Text; +global using System.Reflection; +global using System.Text.RegularExpressions; +global using Microsoft.Win32; +global using System.ComponentModel; +global using System.Runtime.InteropServices; + +/// autocad 引用 +global using Autodesk.AutoCAD.ApplicationServices; +global using Autodesk.AutoCAD.EditorInput; +global using Autodesk.AutoCAD.Colors; +global using Autodesk.AutoCAD.DatabaseServices; +global using Autodesk.AutoCAD.Geometry; +global using Autodesk.AutoCAD.Runtime; +global using Acap = Autodesk.AutoCAD.ApplicationServices.Application; + +global using Autodesk.AutoCAD.DatabaseServices.Filters; + + +global using System.Collections.Specialized; +global using Registry = Microsoft.Win32.Registry; +global using RegistryKey = Microsoft.Win32.RegistryKey; + +/// ifoxcad.basal 引用 +global using IFoxCAD.Basal; \ No newline at end of file diff --git a/src/IFoxCAD.Cad/IFoxCAD.Cad.csproj b/src/IFoxCAD.Cad/IFoxCAD.Cad.csproj index d742bef..d72026f 100644 --- a/src/IFoxCAD.Cad/IFoxCAD.Cad.csproj +++ b/src/IFoxCAD.Cad/IFoxCAD.Cad.csproj @@ -4,7 +4,7 @@ preview enable - net35;net40 + net35;net40;net45 true 0.1.3 InspireFunction @@ -23,28 +23,37 @@ true - - - - - - runtime - - - - - DEBUG - - - $(Configuration);ac2009 - - - $(Configuration);ac2013 - + + + + + + + + - + + + + + + + DEBUG + + + $(Configuration);ac2008;ac2009 + + + $(Configuration);ac2013 + + + $(Configuration);ac2015 + + + + True @@ -56,7 +65,7 @@ - + diff --git a/src/IFoxCAD.Cad/IFoxCAD.Cad.csproj.data b/src/IFoxCAD.Cad/IFoxCAD.Cad.csproj.data deleted file mode 100644 index 5f28270..0000000 --- a/src/IFoxCAD.Cad/IFoxCAD.Cad.csproj.data +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/src/IFoxCAD.Cad/ResultData/LispDottedPair.cs b/src/IFoxCAD.Cad/ResultData/LispDottedPair.cs index 0aa2f69..009d13e 100644 --- a/src/IFoxCAD.Cad/ResultData/LispDottedPair.cs +++ b/src/IFoxCAD.Cad/ResultData/LispDottedPair.cs @@ -1,72 +1,68 @@ -using Autodesk.AutoCAD.DatabaseServices; -using Autodesk.AutoCAD.Runtime; +namespace IFoxCAD.Cad; -using System.Collections.Generic; - -namespace IFoxCAD.Cad +/// +/// lisp点对表的数据封装类 +/// +public class LispDottedPair : LispList { + #region 构造函数 /// - /// lisp 点对表的数据封装类 + /// 默认无参构造函数 /// - public class LispDottedPair : LispList + public LispDottedPair() { - #region 构造函数 - /// - /// 默认无参构造函数 - /// - public LispDottedPair() - { - } + } - /// - /// 构造函数 - /// - /// TypedValue 迭代器 - public LispDottedPair(IEnumerable values) : base(values) - { - } - /// - /// 构造函数 - /// - /// 点对表左数 - /// 点对表右数 - public LispDottedPair(TypedValue left, TypedValue right) - { - Add(left); - Add(right); - } - #endregion + /// + /// 构造函数 + /// + /// TypedValue 迭代器 + public LispDottedPair(IEnumerable values) : base(values) + { + } + /// + /// 构造函数 + /// + /// 点对表左数 + /// 点对表右数 + public LispDottedPair(TypedValue left, TypedValue right) + { + Add(left); + Add(right); + } + #endregion - /// - /// 点对表的值 - /// - public override List Value + #region 重写 + /// + /// 点对表的值 + /// + public override List Value + { + get { - get - { - var value = new List + var value = new List { new TypedValue((int)LispDataType.ListBegin,-1), new TypedValue((int)LispDataType.DottedPair,-1) }; - value.InsertRange(1, this); - return value; - } + value.InsertRange(1, this); + return value; } + } + #endregion - #region 转换器 + #region 转换器 - /// - /// LispDottedPair 隐式转换到 TypedValue 数组 - /// - /// TypedValueList 实例 - public static implicit operator TypedValue[](LispDottedPair values) => values.Value.ToArray(); - /// - /// LispDottedPair 隐式转换到 ResultBuffer - /// - /// TypedValueList 实例 - public static implicit operator ResultBuffer(LispDottedPair values) => new(values.Value.ToArray()); + /// + /// LispDottedPair 隐式转换到 TypedValue 数组 + /// + /// TypedValueList 实例 + public static implicit operator TypedValue[](LispDottedPair values) => values.Value.ToArray(); + /// + /// LispDottedPair 隐式转换到 ResultBuffer + /// + /// TypedValueList 实例 + public static implicit operator ResultBuffer(LispDottedPair values) => new(values.Value.ToArray()); - #endregion - } + #endregion } diff --git a/src/IFoxCAD.Cad/ResultData/LispList.cs b/src/IFoxCAD.Cad/ResultData/LispList.cs index 4742d38..8c72669 100644 --- a/src/IFoxCAD.Cad/ResultData/LispList.cs +++ b/src/IFoxCAD.Cad/ResultData/LispList.cs @@ -1,202 +1,195 @@ -using Autodesk.AutoCAD.DatabaseServices; -using Autodesk.AutoCAD.EditorInput; -using Autodesk.AutoCAD.Geometry; -using Autodesk.AutoCAD.Runtime; -using System; -using System.Collections; -using System.Collections.Generic; -using System.Linq; +namespace IFoxCAD.Cad; -namespace IFoxCAD.Cad +/// +/// lisp数据封装类 +/// +public class LispList : TypedValueList { + #region 构造函数 /// - /// lisp数据封装类 + /// 默认构造函数 /// - public class LispList : TypedValueList - { - #region 构造函数 - /// - /// 默认构造函数 - /// - public LispList() { } + public LispList() { } + + /// + /// 构造函数 + /// + /// TypedValue 迭代器 + public LispList(IEnumerable values) : base(values) { } + #endregion - /// - /// 构造函数 - /// - /// TypedValue 迭代器 - public LispList(IEnumerable values) : base(values) { } - #endregion - /// - /// lisp 列表的值 - /// - public virtual List Value + #region 重写 + /// + /// lisp 列表的值 + /// + public virtual List Value + { + get { - get - { - var value = new List + var value = new List { new TypedValue((int)LispDataType.ListBegin,-1), new TypedValue((int)LispDataType.ListEnd,-1) }; - value.InsertRange(1, this); - return value; - } + value.InsertRange(1, this); + return value; } + } + #endregion - #region 添加数据 - /// - /// 添加数据 - /// - /// 组码 - /// 组码值 - public override void Add(int code, object obj) + #region 添加数据 + /// + /// 添加数据 + /// + /// 组码 + /// 组码值 + public override void Add(int code, object? obj) + { + if (code < 5000) { - if (code < 5000) - { - throw new System.Exception("传入的组码值不是 lisp数据 有效范围!"); - } - Add(new TypedValue(code, obj)); + throw new System.Exception("传入的组码值不是 lisp数据 有效范围!"); } + Add(new TypedValue(code, obj)); + } - /// - /// 添加数据 - /// - /// dxfcode枚举值 - /// 组码值 - public void Add(LispDataType code, object obj) - { - Add((int)code, obj); - } - /// - /// 添加数据,参数为true时添加 lisp 中的 T,false时添加 lisp 中的 nil - /// - /// bool 型的数据 - public void Add(bool value) - { - if (value) - { - Add(LispDataType.T_atom, true); - } - else - { - Add(LispDataType.Nil, null); - } - } - /// - /// 添加字符串 - /// - /// 字符串 - public void Add(string value) - { - Add(LispDataType.Text, value); - } - /// - /// 添加短整型数 - /// - /// 短整型数 - public void Add(short value) - { - Add(LispDataType.Int16, value); - } - /// - /// 添加整型数 - /// - /// 整型数 - public void Add(int value) - { - Add(LispDataType.Int32, value); - } - /// - /// 添加浮点数 - /// - /// 浮点数 - public void Add(double value) - { - Add(LispDataType.Double, value); - } - /// - /// 添加对象id - /// - /// 对象id - public void Add(ObjectId value) - { - Add(LispDataType.ObjectId, value); - } - /// - /// 添加选择集 - /// - /// 选择集 - public void Add(SelectionSet value) - { - Add(LispDataType.SelectionSet, value); - } - /// - /// 添加二维点 - /// - /// 二维点 - public void Add(Point2d value) - { - Add(LispDataType.Point2d, value); - } - /// - /// 添加三维点 - /// - /// 三维点 - public void Add(Point3d value) - { - Add(LispDataType.Point3d, value); - } - /// - /// 添加二维点 - /// - /// X - /// Y - public void Add(double x, double y) - { - Add(LispDataType.Point2d, new Point2d(x, y)); - } - /// - /// 添加三维点 - /// - /// X - /// Y - /// Z - public void Add(double x, double y, double z) + /// + /// 添加数据 + /// + /// dxfcode枚举值 + /// 组码值 + public void Add(LispDataType code, object? obj) + { + Add((int)code, obj); + } + /// + /// 添加数据,参数为true时添加 lisp 中的 T,false时添加 lisp 中的 nil + /// + /// bool 型的数据 + public void Add(bool value) + { + if (value) { - Add(LispDataType.Point3d, new Point3d(x, y, z)); + Add(LispDataType.T_atom, true); } - /// - /// 添加列表 - /// - /// lisp 列表 - public void Add(LispList value) + else { - this.AddRange(value.Value); + Add(LispDataType.Nil, null); } + } + /// + /// 添加字符串 + /// + /// 字符串 + public void Add(string value) + { + Add(LispDataType.Text, value); + } + /// + /// 添加短整型数 + /// + /// 短整型数 + public void Add(short value) + { + Add(LispDataType.Int16, value); + } + /// + /// 添加整型数 + /// + /// 整型数 + public void Add(int value) + { + Add(LispDataType.Int32, value); + } + /// + /// 添加浮点数 + /// + /// 浮点数 + public void Add(double value) + { + Add(LispDataType.Double, value); + } + /// + /// 添加对象id + /// + /// 对象id + public void Add(ObjectId value) + { + Add(LispDataType.ObjectId, value); + } + /// + /// 添加选择集 + /// + /// 选择集 + public void Add(SelectionSet value) + { + Add(LispDataType.SelectionSet, value); + } + /// + /// 添加二维点 + /// + /// 二维点 + public void Add(Point2d value) + { + Add(LispDataType.Point2d, value); + } + /// + /// 添加三维点 + /// + /// 三维点 + public void Add(Point3d value) + { + Add(LispDataType.Point3d, value); + } + /// + /// 添加二维点 + /// + /// X + /// Y + public void Add(double x, double y) + { + Add(LispDataType.Point2d, new Point2d(x, y)); + } + /// + /// 添加三维点 + /// + /// X + /// Y + /// Z + public void Add(double x, double y, double z) + { + Add(LispDataType.Point3d, new Point3d(x, y, z)); + } + /// + /// 添加列表 + /// + /// lisp 列表 + public void Add(LispList value) + { + this.AddRange(value.Value); + } - #endregion + #endregion - #region 转换器 - /// - /// ResultBuffer 隐式转换到 LispList - /// - /// ResultBuffer 实例 - public static implicit operator LispList(ResultBuffer buffer) => new(buffer.AsArray()); - /// - /// LispList 隐式转换到 TypedValue 数组 - /// - /// TypedValueList 实例 - public static implicit operator TypedValue[](LispList values) => values.Value.ToArray(); - /// - /// LispList 隐式转换到 ResultBuffer - /// - /// TypedValueList 实例 - public static implicit operator ResultBuffer(LispList values) => new(values.Value.ToArray()); - /// - /// TypedValue 数组隐式转换到 LispList - /// - /// TypedValue 数组 - public static implicit operator LispList(TypedValue[] values) => new(values); - #endregion - } + #region 转换器 + /// + /// ResultBuffer 隐式转换到 LispList + /// + /// ResultBuffer 实例 + public static implicit operator LispList(ResultBuffer buffer) => new(buffer.AsArray()); + /// + /// LispList 隐式转换到 TypedValue 数组 + /// + /// TypedValueList 实例 + public static implicit operator TypedValue[](LispList values) => values.Value.ToArray(); + /// + /// LispList 隐式转换到 ResultBuffer + /// + /// TypedValueList 实例 + public static implicit operator ResultBuffer(LispList values) => new(values.Value.ToArray()); + /// + /// TypedValue 数组隐式转换到 LispList + /// + /// TypedValue 数组 + public static implicit operator LispList(TypedValue[] values) => new(values); + #endregion } diff --git a/src/IFoxCAD.Cad/ResultData/TypedValueList.cs b/src/IFoxCAD.Cad/ResultData/TypedValueList.cs index 2fccce5..896a226 100644 --- a/src/IFoxCAD.Cad/ResultData/TypedValueList.cs +++ b/src/IFoxCAD.Cad/ResultData/TypedValueList.cs @@ -1,69 +1,63 @@ -using Autodesk.AutoCAD.DatabaseServices; +namespace IFoxCAD.Cad; -using System.Collections.Generic; - -namespace IFoxCAD.Cad +/// +/// 用于集中管理扩展数据/扩展字典/resultbuffer的类 +/// +public class TypedValueList : List { + #region 构造函数 /// - /// 用于集中管理扩展数据/扩展字典/resultbuffer的类 + /// 默认无参构造函数 /// - public class TypedValueList : List - { - #region 构造函数 - /// - /// 默认无参构造函数 - /// - public TypedValueList() { } - /// - /// 采用 TypedValue 迭代器构造 TypedValueList - /// - /// - public TypedValueList(IEnumerable values) : base(values) { } - - #endregion + public TypedValueList() { } + /// + /// 采用 TypedValue 迭代器构造 TypedValueList + /// + /// + public TypedValueList(IEnumerable values) : base(values) { } + #endregion - #region 添加数据 - /// - /// 添加数据 - /// - /// 组码 - /// 组码值 - public virtual void Add(int code, object obj) - { - Add(new TypedValue(code, obj)); - } + #region 添加数据 + /// + /// 添加数据 + /// + /// 组码 + /// 组码值 + public virtual void Add(int code, object obj) + { + Add(new TypedValue(code, obj)); + } - #endregion + #endregion - #region 转换器 - /// - /// ResultBuffer 隐式转换到 TypedValueList - /// - /// ResultBuffer 实例 - public static implicit operator TypedValueList(ResultBuffer buffer) => new(buffer.AsArray()); - /// - /// TypedValueList 隐式转换到 TypedValue 数组 - /// - /// TypedValueList 实例 - public static implicit operator TypedValue[](TypedValueList values) => values.ToArray(); - /// - /// TypedValueList 隐式转换到 ResultBuffer - /// - /// TypedValueList 实例 - public static implicit operator ResultBuffer(TypedValueList values) => new(values); - /// - /// TypedValue 数组隐式转换到 TypedValueList - /// - /// TypedValue 数组 - public static implicit operator TypedValueList(TypedValue[] values) => new(values); - /// - /// 转换为字符串 - /// - /// ResultBuffer 字符串 - public override string ToString() - { - return new ResultBuffer(this).ToString(); - } - #endregion + #region 转换器 + /// + /// ResultBuffer 隐式转换到 TypedValueList + /// + /// ResultBuffer 实例 + public static implicit operator TypedValueList(ResultBuffer buffer) => new(buffer.AsArray()); + /// + /// TypedValueList 隐式转换到 TypedValue 数组 + /// + /// TypedValueList 实例 + public static implicit operator TypedValue[](TypedValueList values) => values.ToArray(); + /// + /// TypedValueList 隐式转换到 ResultBuffer + /// + /// TypedValueList 实例 + public static implicit operator ResultBuffer(TypedValueList values) => new(values); + /// + /// TypedValue 数组隐式转换到 TypedValueList + /// + /// TypedValue 数组 + public static implicit operator TypedValueList(TypedValue[] values) => new(values); + /// + /// 转换为字符串 + /// + /// ResultBuffer 字符串 + public override string ToString() + { + return new ResultBuffer(this).ToString(); } + #endregion } diff --git a/src/IFoxCAD.Cad/ResultData/XRecordDataList.cs b/src/IFoxCAD.Cad/ResultData/XRecordDataList.cs index 0364f9a..1fb19d8 100644 --- a/src/IFoxCAD.Cad/ResultData/XRecordDataList.cs +++ b/src/IFoxCAD.Cad/ResultData/XRecordDataList.cs @@ -1,65 +1,68 @@ -using Autodesk.AutoCAD.DatabaseServices; +namespace IFoxCAD.Cad; -using System.Collections.Generic; - -namespace IFoxCAD.Cad +/// +/// 扩展字典数据封装类 +/// +public class XRecordDataList : TypedValueList { + #region 构造函数 /// /// 扩展字典数据封装类 /// - public class XRecordDataList : TypedValueList - { - public XRecordDataList() - { - } - public XRecordDataList(IEnumerable values) : base(values) { } - #region 添加数据 - /// - /// 添加数据 - /// - /// 组码 - /// 组码值 - public override void Add(int code, object obj) - { - if (code >= 1000) - { - throw new System.Exception("传入的组码值不是 XRecordData 有效范围!"); - } - Add(new TypedValue(code, obj)); - } + public XRecordDataList() { } - /// - /// 添加数据 - /// - /// dxfcode枚举值 - /// 组码值 - public void Add(DxfCode code, object obj) + /// + /// 扩展字典数据封装类 + /// + public XRecordDataList(IEnumerable values) : base(values) { } + #endregion + + #region 添加数据 + /// + /// 添加数据 + /// + /// 组码 + /// 组码值 + public override void Add(int code, object obj) + { + if (code >= 1000) { - Add((int)code, obj); + throw new System.Exception("传入的组码值不是 XRecordData 有效范围!"); } - #endregion + Add(new TypedValue(code, obj)); + } - #region 转换器 - /// - /// ResultBuffer 隐式转换到 XRecordDataList - /// - /// ResultBuffer 实例 - public static implicit operator XRecordDataList(ResultBuffer buffer) => new(buffer.AsArray()); - /// - /// XRecordDataList 隐式转换到 TypedValue 数组 - /// - /// TypedValueList 实例 - public static implicit operator TypedValue[](XRecordDataList values) => values.ToArray(); - /// - /// XRecordDataList 隐式转换到 ResultBuffer - /// - /// TypedValueList 实例s - public static implicit operator ResultBuffer(XRecordDataList values) => new(values); - /// - /// TypedValue 数组隐式转换到 XRecordDataList - /// - /// TypedValue 数组 - public static implicit operator XRecordDataList(TypedValue[] values) => new(values); - #endregion + /// + /// 添加数据 + /// + /// dxfcode枚举值 + /// 组码值 + public void Add(DxfCode code, object obj) + { + Add((int)code, obj); } + #endregion + + #region 转换器 + /// + /// ResultBuffer 隐式转换到 XRecordDataList + /// + /// ResultBuffer 实例 + public static implicit operator XRecordDataList(ResultBuffer buffer) => new(buffer.AsArray()); + /// + /// XRecordDataList 隐式转换到 TypedValue 数组 + /// + /// TypedValueList 实例 + public static implicit operator TypedValue[](XRecordDataList values) => values.ToArray(); + /// + /// XRecordDataList 隐式转换到 ResultBuffer + /// + /// TypedValueList 实例s + public static implicit operator ResultBuffer(XRecordDataList values) => new(values); + /// + /// TypedValue 数组隐式转换到 XRecordDataList + /// + /// TypedValue 数组 + public static implicit operator XRecordDataList(TypedValue[] values) => new(values); + #endregion } diff --git a/src/IFoxCAD.Cad/ResultData/XdataList.cs b/src/IFoxCAD.Cad/ResultData/XdataList.cs index e22ffe2..a666100 100644 --- a/src/IFoxCAD.Cad/ResultData/XdataList.cs +++ b/src/IFoxCAD.Cad/ResultData/XdataList.cs @@ -1,71 +1,68 @@ -using Autodesk.AutoCAD.DatabaseServices; +namespace IFoxCAD.Cad; -using System.Collections.Generic; -using System.Linq; - -namespace IFoxCAD.Cad +/// +/// 扩展数据封装类 +/// +public class XDataList : TypedValueList { - + #region 构造函数 /// /// 扩展数据封装类 /// - public class XDataList : TypedValueList - { - public XDataList() - { - } + public XDataList() { } - public XDataList(IEnumerable values) : base(values) { } + /// + /// 扩展数据封装类 + /// + public XDataList(IEnumerable values) : base(values) { } + #endregion - #region 添加数据 - /// - /// 添加数据 - /// - /// 组码 - /// 组码值 - public override void Add(int code, object obj) - { - if (code < 1000 || code > 1071) - { - throw new System.Exception("传入的组码值不是XData有效范围!"); - } - Add(new TypedValue(code, obj)); - } + #region 添加数据 + /// + /// 添加数据 + /// + /// 组码 + /// 组码值 + public override void Add(int code, object obj) + { + if (code < 1000 || code > 1071) + throw new System.Exception("传入的组码值不是XData有效范围!"); - /// - /// 添加数据 - /// - /// dxfcode枚举值 - /// 组码值 - public void Add(DxfCode code, object obj) - { + Add(new TypedValue(code, obj)); + } - Add((int)code, obj); - } + /// + /// 添加数据 + /// + /// dxfcode枚举值 + /// 组码值 + public void Add(DxfCode code, object obj) + { + Add((int)code, obj); + } - #endregion + #endregion - #region 转换器 - /// - /// ResultBuffer 隐式转换到 XDataList - /// - /// ResultBuffer 实例 - public static implicit operator XDataList(ResultBuffer buffer) => new(buffer.AsArray()); - /// - /// XDataList 隐式转换到 TypedValue 数组 - /// - /// TypedValueList 实例 - public static implicit operator TypedValue[](XDataList values) => values.ToArray(); - /// - /// XDataList 隐式转换到 ResultBuffer - /// - /// TypedValueList 实例 - public static implicit operator ResultBuffer(XDataList values) => new(values); - /// - /// TypedValue 数组隐式转换到 XDataList - /// - /// TypedValue 数组 - public static implicit operator XDataList(TypedValue[] values) => new(values); - #endregion - } + #region 转换器 + /// + /// ResultBuffer 隐式转换到 XDataList + /// + /// ResultBuffer 实例 + public static implicit operator XDataList(ResultBuffer buffer) => new(buffer.AsArray()); + /// + /// XDataList 隐式转换到 TypedValue 数组 + /// + /// TypedValueList 实例 + public static implicit operator TypedValue[](XDataList values) => values.ToArray(); + /// + /// XDataList 隐式转换到 ResultBuffer + /// + /// TypedValueList 实例 + public static implicit operator ResultBuffer(XDataList values) => new(values); + /// + /// TypedValue 数组隐式转换到 XDataList + /// + /// TypedValue 数组 + public static implicit operator XDataList(TypedValue[] values) => new(values); + #endregion } diff --git a/src/IFoxCAD.Cad/Runtime/AOP.cs b/src/IFoxCAD.Cad/Runtime/AOP.cs new file mode 100644 index 0000000..ac8270b --- /dev/null +++ b/src/IFoxCAD.Cad/Runtime/AOP.cs @@ -0,0 +1,99 @@ +//namespace IFoxCAD.Cad; +//using HarmonyLib; + +//public class IFoxRefuseInjectionTransaction : Attribute +//{ +// /// +// /// 拒绝注入事务 +// /// +// public IFoxRefuseInjectionTransaction() +// { +// } +//} + +//public class AOP +//{ +// /// +// /// 在此命名空间下的命令末尾注入清空事务栈函数 +// /// +// public static void Run(string nameSpace) +// { +// Dictionary cmdDic = new(); +// AutoClass.AppDomainGetTypes(type => { +// if (type.Namespace != nameSpace) +// return; +// //类上面特性 +// if (type.IsClass) +// { +// var attr = type.GetCustomAttributes(true); +// if (RefuseInjectionTransaction(attr)) +// return; +// } + +// //函数上面特性 +// var mets = type.GetMethods();//获得它的成员函数 +// for (int ii = 0; ii < mets.Length; ii++) +// { +// var method = mets[ii]; +// //找到特性,特性下面的方法要是Public,否则就被编译器优化掉了. +// var attr = method.GetCustomAttributes(true); +// for (int jj = 0; jj < attr.Length; jj++) +// if (attr[jj] is CommandMethodAttribute cmdAtt) +// { +// if (!RefuseInjectionTransaction(attr)) +// cmdDic.Add(cmdAtt.GlobalName, (cmdAtt, type, method)); +// } +// } +// }); + +// //运行的命令写在了Test.dll,当然不是ifox.cad类库内了.... +// if (cmdDic.Count == 0) +// return; + +// var harmony = new Harmony(nameSpace); +// var mPrefix = SymbolExtensions.GetMethodInfo(() => IFoxCmdAddFirst());//进入函数前 +// var mPostfix = SymbolExtensions.GetMethodInfo(() => IFoxCmdAddLast());//进入函数后 +// var mp1 = new HarmonyMethod(mPrefix); +// var mp2 = new HarmonyMethod(mPostfix); + +// foreach (var item in cmdDic) +// { +// //原函数执行(空间type,函数名) +// var mOriginal = AccessTools.Method(item.Value.MetType, item.Value.MetInfo.Name); +// //mOriginal.Invoke(); +// //新函数执行:创造两个函数加入里面 +// var newMet = harmony.Patch(mOriginal, mp1, mp2); +// //newMet.Invoke(); +// } +// } + +// /// +// /// 拒绝注入事务 +// /// +// /// 属性 +// /// +// private static bool RefuseInjectionTransaction(object[] attr) +// { +// bool refuseInjectionTransaction = false; +// for (int kk = 0; kk < attr.Length; kk++) +// { +// if (attr[kk] is IFoxRefuseInjectionTransaction) +// { +// refuseInjectionTransaction = true; +// break; +// } +// } +// return refuseInjectionTransaction; +// } + +// public static void IFoxCmdAddFirst() +// { +// //此生命周期会在静态事务栈上面,被无限延长 +// var _ = DBTrans.Top; +// } + +// public static void IFoxCmdAddLast() +// { +// DBTrans.FinishDatabase(); +// } +//} \ No newline at end of file diff --git a/src/IFoxCAD.Cad/Runtime/AcadVersion.cs b/src/IFoxCAD.Cad/Runtime/AcadVersion.cs index 7be301f..94c7947 100644 --- a/src/IFoxCAD.Cad/Runtime/AcadVersion.cs +++ b/src/IFoxCAD.Cad/Runtime/AcadVersion.cs @@ -1,130 +1,74 @@ -using Microsoft.Win32; -using System; -using System.Collections.Generic; -using System.Reflection; -using System.Text.RegularExpressions; +namespace IFoxCAD.Cad; -namespace IFoxCAD.Cad +/// +/// cad版本号类 +/// +public static class AcadVersion { + private static readonly string _pattern = @"Autodesk\\AutoCAD\\R(\d+)\.(\d+)\\.*?"; + /// - /// cad版本号类 + /// 所有安装的cad的版本号 /// - public class AcadVersion + public static List Versions { - /// - /// 主版本 - /// - public int Major - { private set; get; } - - /// - /// 次版本 - /// - public int Minor - { private set; get; } - - /// - /// 版本号 - /// - public double ProgId => double.Parse($"{Major}.{Minor}"); - - /// - /// 注册表名称 - /// - public string ProductName - { private set; get; } - - /// - /// 注册表位置 - /// - public string ProductRootKey - { private set; get; } - - private static readonly string _pattern = @"Autodesk\\AutoCAD\\R(\d+)\.(\d+)\\.*?"; - - private static List _versions; - - /// - /// 所有安装的cad的版本号 - /// - public static List Versions + get { - get + + string[] copys = + Registry.LocalMachine + .OpenSubKey(@"SOFTWARE\Autodesk\Hardcopy") + .GetValueNames(); + var _versions = new List(); + foreach (var rootkey in copys) { - if (_versions is null) + if (Regex.IsMatch(rootkey, _pattern)) { - string[] copys = - Registry.LocalMachine - .OpenSubKey(@"SOFTWARE\Autodesk\Hardcopy") - .GetValueNames(); - _versions = new List(); - foreach (var rootkey in copys) - { - if (Regex.IsMatch(rootkey, _pattern)) + var gs = Regex.Match(rootkey, _pattern).Groups; + var ver = + new CadVersion { - var gs = Regex.Match(rootkey, _pattern).Groups; - var ver = - new AcadVersion - { - ProductRootKey = rootkey, - ProductName = - Registry.LocalMachine - .OpenSubKey("SOFTWARE") - .OpenSubKey(rootkey) - .GetValue("ProductName") - .ToString(), + ProductRootKey = rootkey, + ProductName = + Registry.LocalMachine + .OpenSubKey("SOFTWARE") + .OpenSubKey(rootkey) + .GetValue("ProductName") + .ToString(), - Major = int.Parse(gs[1].Value), - Minor = int.Parse(gs[2].Value), - }; + Major = int.Parse(gs[1].Value), + Minor = int.Parse(gs[2].Value), + }; - _versions.Add(ver); - } - } + _versions.Add(ver); } - return _versions; } + return _versions; } + } - /// 已打开的cad的版本号 - /// 已打开cad的application对象 - /// cad版本号对象 - public static AcadVersion FromApp(object app) - { - if (app is null) - { - throw new ArgumentNullException(nameof(app)); - } - - string acver = - app.GetType() - .InvokeMember( - "Version", - BindingFlags.GetProperty, - null, - app, - new object[0]).ToString(); - - var gs = Regex.Match(acver, @"(\d+)\.(\d+).*?").Groups; - int major = int.Parse(gs[1].Value); - int minor = int.Parse(gs[2].Value); - foreach (var ver in Versions) - { - if (ver.Major == major && ver.Minor == minor) - return ver; - } - - return null; - } + /// 已打开的cad的版本号 + /// 已打开cad的application对象 + /// cad版本号对象 + public static CadVersion? FromApp(object app!!) + { + string acver = + app.GetType() + .InvokeMember( + "Version", + BindingFlags.GetProperty, + null, + app, + new object[0]).ToString(); - /// - /// 转换为字符串 - /// - /// 表示版本号的字符串 - public override string ToString() + var gs = Regex.Match(acver, @"(\d+)\.(\d+).*?").Groups; + int major = int.Parse(gs[1].Value); + int minor = int.Parse(gs[2].Value); + foreach (var ver in Versions) { - return - $"名称:{ProductName}\n版本号:{ProgId}\n注册表位置:{ProductRootKey}"; + if (ver.Major == major && ver.Minor == minor) + return ver; } + return null; } } diff --git a/src/IFoxCAD.Cad/Runtime/AssemInfo.cs b/src/IFoxCAD.Cad/Runtime/AssemInfo.cs index 62b70f8..c13cc26 100644 --- a/src/IFoxCAD.Cad/Runtime/AssemInfo.cs +++ b/src/IFoxCAD.Cad/Runtime/AssemInfo.cs @@ -1,36 +1,77 @@ -using System; +namespace IFoxCAD.Cad; -namespace IFoxCAD.Cad +/// +/// 程序集信息 +/// +[Serializable] +public struct AssemInfo { /// - /// 程序集信息 - /// - [Serializable] - public struct AssemInfo - { - /// - /// 注册名 - /// - public string Name { get; set; } - - /// - /// 程序集全名 - /// - public string Fullname { get; set; } - - /// - /// 程序集路径 - /// - public string Loader { get; set; } - - /// - /// 加载方式 - /// - public AssemLoadType LoadType { get; set; } - - /// - /// 程序集说明 - /// - public string Description { get; set; } - } + /// 注册名 + ///
+ public string Name; + + /// + /// 程序集全名 + /// + public string Fullname; + + /// + /// 程序集路径 + /// + public string Loader; + + /// + /// 加载方式 + /// + public AssemLoadType LoadType; + + /// + /// 程序集说明 + /// + public string Description; +} + + +/// +/// 程序集加载类型 +/// +public enum AssemLoadType +{ + /// + /// 启动 + /// + Startting = 2, + + /// + /// 随命令 + /// + ByCommand = 12, + + /// + /// 无效 + /// + Disabled = 20 +} + + +/// +/// 注册中心配置信息 +/// +public enum AutoRegConfig +{ + /// + /// 注册表 + /// + Regedit = 1, + /// + /// 反射特性 + /// + ReflectionAttribute = 2, + /// + /// 反射接口 + /// + ReflectionInterface = 4, + + All = Regedit | ReflectionAttribute | ReflectionInterface, } diff --git a/src/IFoxCAD.Cad/Runtime/AutoRegAssem.cs b/src/IFoxCAD.Cad/Runtime/AutoRegAssem.cs index 111be63..540762c 100644 --- a/src/IFoxCAD.Cad/Runtime/AutoRegAssem.cs +++ b/src/IFoxCAD.Cad/Runtime/AutoRegAssem.cs @@ -1,131 +1,169 @@ -using Autodesk.AutoCAD.DatabaseServices; -using Microsoft.Win32; -using System; -using System.IO; -using System.Linq; -using System.Reflection; -using CadRuntime = Autodesk.AutoCAD.Runtime; - -namespace IFoxCAD.Cad +namespace IFoxCAD.Cad; + + +/// +/// 注册中心 +/// 初始化程序集信息写入注册表并反射特性和接口 +/// 启动cad后的执行顺序为: +/// 1:程序集配置中心构造函数 +/// 2:特性..(多个) +/// 3:接口..(多个) +/// +public abstract class AutoRegAssem : IExtensionApplication { + #region 字段 + readonly AutoReflection _autoRef; + readonly AssemInfo _info; + #endregion + + #region 静态方法 + /// + /// 程序集的路径 + /// + public static FileInfo Location => new(Assembly.GetCallingAssembly().Location); + + /// + /// 程序集的目录 + /// + public static DirectoryInfo CurrDirectory => Location.Directory; + /// - /// 程序集加载类型 + /// 获取程序集的目录 /// - public enum AssemLoadType + /// 程序集 + /// 路径对象 + public static DirectoryInfo GetDirectory(Assembly? assem) { - /// - /// 启动 - /// - Startting = 2, - - /// - /// 随命令 - /// - ByCommand = 12, - - /// - /// 无效 - /// - Disabled = 20 + if (assem is null) + throw new(nameof(assem)); + + return new FileInfo(assem.Location).Directory; } + #endregion + #region 构造函数 /// - /// 自动加载程序集的抽象类,继承自 IExtensionApplication 接口 + /// 注册中心 /// - public abstract class AutoRegAssem : CadRuntime.IExtensionApplication + /// 配置项目 + public AutoRegAssem(AutoRegConfig autoRegConfig) { - private AssemInfo _info = new(); - - /// - /// 程序集的路径 - /// - public static FileInfo Location => new(Assembly.GetCallingAssembly().Location); - - /// - /// 程序集的目录 - /// - public static DirectoryInfo CurrDirectory => Location.Directory; - - /// - /// 获取程序集的目录 - /// - /// 程序集 - /// 路径对象 - public static DirectoryInfo GetDirectory(Assembly assem) + var assem = Assembly.GetCallingAssembly(); + _info = new() { - if (assem is null) - { - throw new(nameof(assem)); - } - return new FileInfo(assem.Location).Directory; - } + Loader = assem.Location, + Fullname = assem.FullName, + Name = assem.GetName().Name, + LoadType = AssemLoadType.Startting + }; - /// - /// 初始化程序集信息 - /// - public AutoRegAssem() + if ((autoRegConfig & AutoRegConfig.Regedit) == AutoRegConfig.Regedit) { - Assembly assem = Assembly.GetCallingAssembly(); - _info.Loader = assem.Location; - _info.Fullname = assem.FullName; - _info.Name = assem.GetName().Name; - _info.LoadType = AssemLoadType.Startting; - if (!SearchForReg()) - { RegApp(); - } } - #region RegApp + //实例化了 AutoClass 之后会自动执行 IFoxAutoGo 接口下面的类, + //以及自动执行特性 [IFoxInitialize] + //类库用户不在此处进行其他代码,而是实现特性 + _autoRef = new AutoReflection(_info.Name, autoRegConfig); + _autoRef.Initialize(); + } + #endregion - private static RegistryKey GetAcAppKey() - { -#if ac2009 - string key = HostApplicationServices.Current.RegistryProductRootKey; -#elif ac2013 - string key = HostApplicationServices.Current.MachineRegistryProductRootKey; + #region RegApp + + /// + /// 获取当前cad注册表位置 + /// + /// 打开权限 + /// + public static RegistryKey GetAcAppKey(bool writable = true) + { + RegistryKey? ackey = null; + +#if NET35 + string key = HostApplicationServices.Current.RegistryProductRootKey; //这里浩辰读出来是"" +#elif !HC2020 + string key = HostApplicationServices.Current.UserRegistryProductRootKey; #endif - RegistryKey ackey = - Registry.CurrentUser.OpenSubKey(key, true); - return ackey.CreateSubKey("Applications"); - } - private bool SearchForReg() - { - RegistryKey appkey = GetAcAppKey(); - var regApps = appkey.GetSubKeyNames(); - return regApps.Contains(_info.Name); - } +#if !HC2020 + ackey = Registry.CurrentUser.OpenSubKey(key, writable); +#else + //浩辰 + var s = GrxCAD.DatabaseServices.HostApplicationServices.Current.RegistryProductRootKey;//浩辰奇怪的空值 + string str = CadSystem.Getvar("ACADVER"); + str = Regex.Replace(str, @"[^\d.\d]", ""); + double.TryParse(str, out double a); + // string regedit = @"Software\Gstarsoft\GstarCAD\R" + a.ToString() + @"\zh-CN"; //2019 + // string regedit = @"Software\Gstarsoft\GstarCAD\B" + a.ToString() + @"\zh-CN";//2020 这里是 + string regedit = @"Software\Gstarsoft\GstarCAD\B20\zh-CN";//2020 这里是 + + ackey = Registry.CurrentUser.OpenSubKey(regedit, writable); +#endif + return ackey.CreateSubKey("Applications"); + } + + /// + /// 卸载注册表信息 + /// + public bool UnRegApp() + { + var appkey = GetAcAppKey(); + if (appkey.SubKeyCount == 0) + return false; - /// - /// 在注册表写入自动加载的程序集信息 - /// - public void RegApp() + var regApps = appkey.GetSubKeyNames(); + if (regApps.Contains(_info.Name)) { - RegistryKey appkey = GetAcAppKey(); - RegistryKey rk = appkey.CreateSubKey(_info.Name); - rk.SetValue("DESCRIPTION", _info.Fullname, RegistryValueKind.String); - rk.SetValue("LOADCTRLS", _info.LoadType, RegistryValueKind.DWord); - rk.SetValue("LOADER", _info.Loader, RegistryValueKind.String); - rk.SetValue("MANAGED", 1, RegistryValueKind.DWord); - appkey.Close(); + appkey.DeleteSubKey(_info.Name, false); + return true; } + return false; + } - #endregion RegApp + /// + /// 是否已经存在注册表 + /// + /// + bool SearchForReg() + { + var appkey = GetAcAppKey(); + if (appkey.SubKeyCount == 0) + return false; - #region IExtensionApplication 成员 + var regApps = appkey.GetSubKeyNames(); + if (regApps.Contains(_info.Name)) + { + //20220409 bug:文件名相同,路径不同,需要判断路径 + var info = appkey.OpenSubKey(_info.Name); + return info.GetValue("LOADER")?.ToString().ToLower() == _info.Loader.ToLower(); + } + return false; + } - /// - /// 初始化函数 - /// - public abstract void Initialize(); + /// + /// 在注册表写入自动加载的程序集信息 + /// + public void RegApp() + { + var appkey = GetAcAppKey(); + var rk = appkey.CreateSubKey(_info.Name); + rk.SetValue("DESCRIPTION", _info.Fullname, RegistryValueKind.String); + rk.SetValue("LOADCTRLS", _info.LoadType, RegistryValueKind.DWord); + rk.SetValue("LOADER", _info.Loader, RegistryValueKind.String); + rk.SetValue("MANAGED", 1, RegistryValueKind.DWord); + appkey.Close(); + } - /// - /// 结束函数 - /// - public abstract void Terminate(); + //这里的是不会自动执行的 + public void Initialize() { } + public void Terminate() { } - #endregion IExtensionApplication 成员 + ~AutoRegAssem() + { + _autoRef.Terminate(); } + #endregion RegApp } diff --git a/src/IFoxCAD.Cad/Runtime/CadVersion.cs b/src/IFoxCAD.Cad/Runtime/CadVersion.cs new file mode 100644 index 0000000..2e32c74 --- /dev/null +++ b/src/IFoxCAD.Cad/Runtime/CadVersion.cs @@ -0,0 +1,92 @@ +namespace IFoxCAD.Cad; + +#if ac2009 + +public class CadVersion +{ + /// + /// 主版本 + /// + public int Major { get; set; } + + /// + /// 次版本 + /// + public int Minor { get; set; } + + /// + /// 版本号 + /// + public double ProgId => double.Parse($"{Major}.{Minor}"); + + /// + /// 注册表名称 + /// + public string? ProductName { get; set; } + + /// + /// 注册表位置 + /// + public string? ProductRootKey { get; set; } + + /// + /// 转换为字符串 + /// + /// 表示版本号的字符串 + public override string ToString() + { + return $"名称:{ProductName}\n版本号:{ProgId}\n注册表位置:{ProductRootKey}"; + } + // public override bool Equals(object obj) + // { + // return base.Equals(obj); + // } + + // public override int GetHashCode() + // { + // return base.GetHashCode(); + // } + + // //public override string ToString() + // //{ + // // return base.ToString(); + // //} +} +#else +public record CadVersion +{ + /// + /// 主版本 + /// + public int Major; + + /// + /// 次版本 + /// + public int Minor; + + /// + /// 版本号 + /// + public double ProgId => double.Parse($"{Major}.{Minor}"); + + /// + /// 注册表名称 + /// + public string? ProductName; + + /// + /// 注册表位置 + /// + public string? ProductRootKey; + + /// + /// 转换为字符串 + /// + /// 表示版本号的字符串 + public override string ToString() + { + return $"名称:{ProductName}\n版本号:{ProgId}\n注册表位置:{ProductRootKey}"; + } +} +#endif \ No newline at end of file diff --git a/src/IFoxCAD.Cad/Runtime/DBTrans.cs b/src/IFoxCAD.Cad/Runtime/DBTrans.cs index 9507f17..d207edf 100644 --- a/src/IFoxCAD.Cad/Runtime/DBTrans.cs +++ b/src/IFoxCAD.Cad/Runtime/DBTrans.cs @@ -1,319 +1,440 @@ -using System; -using System.Collections.Generic; -using Autodesk.AutoCAD.DatabaseServices; -using Autodesk.AutoCAD.EditorInput; -using Autodesk.AutoCAD.ApplicationServices; -using System.IO; - -namespace IFoxCAD.Cad -{ - public class DBTrans : IDisposable - { - #region 私有字段 - /// - /// 文档锁 - /// - private DocumentLock documentLock = default; - /// - /// 是否释放资源 - /// - private bool disposedValue; - /// - /// 是否提交事务 - /// - private bool _commit; - /// - /// 事务栈 - /// - private static readonly Stack dBTrans = new(); - #endregion - - #region 公开属性 - /// - /// 返回当前事务 - /// - public static DBTrans Top => dBTrans.Peek(); - /// - /// 数据库 - /// - public Database Database { get; private set; } - /// - /// 文档 - /// - public Document Document { get; private set; } - /// - /// 命令行 - /// - public Editor Editor { get; private set; } - /// - /// 事务管理器 - /// - public Transaction Transaction { get; private set; } - - #endregion - - #region 构造函数 - /// - /// 默认构造函数,默认为打开当前文档,默认提交事务 - /// - /// 要打开的文档 - /// 事务是否提交 - public DBTrans(Document doc = null, bool commit = true, bool doclock = false) - { - doc ??= Application.DocumentManager.MdiActiveDocument; - Document = doc; - Database = Document.Database; - Editor = Document.Editor; - Init(commit, doclock); - } +namespace IFoxCAD.Cad; - /// - /// 构造函数,打开数据库,默认提交事务 - /// - /// 要打开的数据库 - /// 事务是否提交 - public DBTrans(Database database, bool commit = true) - { - Database = database; - Init(commit, false); - } - /// - /// 构造函数,打开文件,默认提交事务 - /// - /// 要打开的文件名 - /// 事务是否提交 - public DBTrans(string fileName, bool commit = true) - { - Database = new Database(false, true); - Database.ReadDwgFile(fileName, FileShare.Read, true, null); - Database.CloseInput(true); - Init(commit, false); - } - /// - /// 初始化事务及事务队列、提交模式 - /// - /// 提交模式 - private void Init(bool commit, bool doclock) - { - if (doclock) - { - documentLock = Document.LockDocument(); - } - Transaction = Database.TransactionManager.StartTransaction(); - _commit = commit; - dBTrans.Push(this); - } +/// +/// 事务栈,隐匿事务在数据库其中担任的角色 +/// +public class DBTrans : IDisposable +{ + #region 私有字段 + /// + /// 文档锁 + /// + private readonly DocumentLock? documentLock; + /// + /// 是否释放资源 + /// + private bool disposedValue; + /// + /// 是否提交事务 + /// + private readonly bool _commit; + /// + /// 事务栈 + /// + private static readonly Stack dBTrans = new(); + #endregion - #endregion - - #region 符号表 - - /// - /// 块表 - /// - public SymbolTable BlockTable => new(this, Database.BlockTableId); - /// - /// 当前绘图空间 - /// - public BlockTableRecord CurrentSpace => BlockTable.GetRecord(Database.CurrentSpaceId); - /// - /// 模型空间 - /// - public BlockTableRecord ModelSpace => BlockTable.GetRecord(BlockTable.CurrentSymbolTable[BlockTableRecord.ModelSpace]); - /// - /// 图纸空间 - /// - public BlockTableRecord PaperSpace => BlockTable.GetRecord(BlockTable.CurrentSymbolTable[BlockTableRecord.PaperSpace]); - /// - /// 层表 - /// - public SymbolTable LayerTable => new(this, Database.LayerTableId); - /// - /// 文字样式表 - /// - public SymbolTable TextStyleTable => new(this, Database.TextStyleTableId); - - /// - /// 注册应用程序表 - /// - public SymbolTable RegAppTable => new(this, Database.RegAppTableId); - - /// - /// 标注样式表 - /// - public SymbolTable DimStyleTable => new(this, Database.DimStyleTableId); - - /// - /// 线型表 - /// - public SymbolTable LinetypeTable => new(this, Database.LinetypeTableId); - - /// - /// 用户坐标系表 - /// - public SymbolTable UcsTable => new(this, Database.UcsTableId); - - /// - /// 视图表 - /// - public SymbolTable ViewTable => new(this, Database.ViewTableId); - - /// - /// 视口表 - /// - public SymbolTable ViewportTable => new(this, Database.ViewportTableId); - #endregion - - #region 字典 - //TODO: 补充关于扩展字典,命名对象字典,组字典,多线样式字典等对象字典的属性 - /// - /// 命名对象字典 - /// - public DBDictionary NamedObjectsDict => GetObject(Database.NamedObjectsDictionaryId); - /// - /// 组字典 - /// - public DBDictionary GroupDict => GetObject(Database.GroupDictionaryId); - /// - /// 多重引线样式字典 - /// - public DBDictionary MLeaderStyleDict => GetObject(Database.MLeaderStyleDictionaryId); - /// - /// 多线样式字典 - /// - public DBDictionary MLStyleDict => GetObject(Database.MLStyleDictionaryId); - /// - /// 材质字典 - /// - public DBDictionary MaterialDict => GetObject(Database.MaterialDictionaryId); - /// - /// 表格样式字典 - /// - public DBDictionary TableStyleDict => GetObject(Database.TableStyleDictionaryId); - /// - /// 视觉样式字典 - /// - public DBDictionary VisualStyleDict => GetObject(Database.VisualStyleDictionaryId); - /// - /// 颜色字典 - /// - public DBDictionary ColorDict => GetObject(Database.ColorDictionaryId); - /// - /// 打印设置字典 - /// - public DBDictionary PlotSettingsDict => GetObject(Database.PlotSettingsDictionaryId); - /// - /// 打印样式表名字典 - /// - public DBDictionary PlotStyleNameDict => GetObject(Database.PlotStyleNameDictionaryId); - /// - /// 布局字典 - /// - public DBDictionary LayoutDict => GetObject(Database.LayoutDictionaryId); - /// - /// 数据链接字典 - /// - public DBDictionary DataLinkDict => GetObject(Database.DataLinkDictionaryId); -#if ac2013 - /// - /// 详细视图样式字典 - /// - public DBDictionary DetailViewStyleDict => GetObject(Database.DetailViewStyleDictionaryId); - /// - /// 剖面视图样式字典 - /// - public DBDictionary SectionViewStyleDict => GetObject(Database.SectionViewStyleDictionaryId); -#endif - #endregion - - #region 获取对象 - /// - /// 根据对象id获取图元对象 - /// - /// 要获取的图元对象的类型 - /// 对象id - /// 打开模式,默认为只读 - /// 是否打开已删除对象,默认为不打开 - /// 是否打开锁定图层对象,默认为不打开 - /// 图元对象,类型不匹配时返回 - public T GetObject(ObjectId id, - OpenMode mode = OpenMode.ForRead, - bool openErased = false, - bool forceOpenOnLockedLayer = false) where T : DBObject + #region 公开属性 + /// + /// 返回当前事务 + /// + public static DBTrans Top + { + get { - return Transaction.GetObject(id, mode, openErased, forceOpenOnLockedLayer) as T; - } + /* + * 0x01 + * 事务栈上面有事务,这个事务属于当前文档, + * 那么直接提交原本事务然后再开一个(一直把栈前面的同数据库提交清空) + * 那不就发生跨事务读取图元了吗?....否决 + * + * 0x02 + * 跨文档事务出错 Autodesk.AutoCAD.Runtime.Exception:“eNotFromThisDocument” + * Curves.GetEntities()会从Top获取事务(Top会new一个),此时会是当前文档; + * 然后命令文中发生了 using var tr = new DBTrans(); + * 当退出命令此事务释放,但是从来不释放Top, + * 然后我新建了一个文档,再进行命令=>又进入Top,Top返回了前一个文档的事务 + * 因此所以无法清理栈,所以Dispose不触发,导致无法刷新图元和Ctrl+Z出错 + * 所以用AOP方式修复 + * + * 0x03 + * 经过艰苦卓绝的测试,aop模式由于不能断点调试,所以暂时放弃。 + */ - /// - /// 根据对象句柄字符串获取对象Id - /// - /// 句柄字符串 - /// 对象id - public ObjectId GetObjectId(string handleString) - { - var hanle = new Handle(Convert.ToInt64(handleString, 16)); - return Database.GetObjectId(false, hanle, 0); + // 由于大量的函数依赖本属性,强迫用户先开启事务 + if (dBTrans.Count == 0) + throw new ArgumentNullException("事务栈没有任何事务,请在调用前创建:" + nameof(DBTrans)); + var trans = dBTrans.Peek(); + return trans; } + } - #endregion + /// + /// 文档 + /// + public Document? Document { get; private set; } + /// + /// 命令行 + /// + public Editor? Editor { get; private set; } + /// + /// 事务管理器 + /// + public Transaction Transaction { get; private set; } + /// + /// 数据库 + /// + public Database Database { get; private set; } + #endregion + #region 构造函数 + /// + /// 事务栈 + /// 默认构造函数,默认为打开当前文档,默认提交事务 + /// + /// 要打开的文档 + /// 事务是否提交 + /// 是否锁文档 + public DBTrans(Document? doc = null, bool commit = true, bool doclock = false) + { + Document = doc ?? Application.DocumentManager.MdiActiveDocument; + Database = Document.Database; + Editor = Document.Editor; + Transaction = Database.TransactionManager.StartTransaction(); + _commit = commit; + if (doclock) + documentLock = Document.LockDocument(); + dBTrans.Push(this); + } - #region idispose接口相关函数 + /// + /// 事务栈 + /// 打开数据库,默认提交事务 + /// + /// 要打开的数据库 + /// 事务是否提交 + public DBTrans(Database database, bool commit = true) + { + Database = database; + Transaction = Database.TransactionManager.StartTransaction(); + _commit = commit; + dBTrans.Push(this); + } - public void Abort() - { - Transaction.Abort(); - } + /// + /// 事务栈 + /// 打开文件,默认提交事务 + /// + /// 要打开的文件名 + /// 事务是否提交 + /// 开图模式 + /// 密码 + public DBTrans(string fileName, + bool commit = true, +#pragma warning disable CS0436 // 类型与导入类型冲突 + FileOpenMode openMode = FileOpenMode.OpenForReadAndWriteNoShare, +#pragma warning restore CS0436 // 类型与导入类型冲突 + string? password = null) + { + if (string.IsNullOrEmpty(fileName?.Trim())) + throw new ArgumentNullException(nameof(fileName)); - public void Commit() + if (!File.Exists(fileName)) + Database = new Database(true, false); + else { - if (_commit) + var doc = Acap.DocumentManager + .Cast() + .FirstOrDefault(doc => doc.Name == fileName); + if (doc is not null) { - Transaction.Commit(); + Database = doc.Database; + Document = doc; + Editor = doc.Editor; } else { - Abort(); + Database = new Database(false, true); + if (Path.GetExtension(fileName).ToLower().Contains("dxf")) + Database.DxfIn(fileName, null); + else + { +#if ac2008 + //此处没有一一对应的关系 +#pragma warning disable CS0436 // 类型与导入类型冲突 + var sf = openMode switch + { + FileOpenMode.OpenTryForReadShare => FileShare.Read, + FileOpenMode.OpenForReadAndAllShare => FileShare.ReadWrite, + FileOpenMode.OpenForReadAndWriteNoShare => FileShare.None, + FileOpenMode.OpenForReadAndReadShare => FileShare.ReadWrite, + _ => FileShare.ReadWrite, + }; +#pragma warning restore CS0436 // 类型与导入类型冲突 + Database.ReadDwgFile(fileName, sf, true/*控制读入一个与系统编码不相同的文件时的转换操作*/, password); +#else + Database.ReadDwgFile(fileName, openMode, true/*控制读入一个与系统编码不相同的文件时的转换操作*/, password); +#endif + } + Database.CloseInput(true); } - } - protected virtual void Dispose(bool disposing) + Transaction = Database.TransactionManager.StartTransaction(); + _commit = commit; + dBTrans.Push(this); + } + #endregion + + #region 类型转换 + /// + /// 隐式转换为Transaction + /// + /// 事务管理器 + /// 事务管理器 + public static implicit operator Transaction(DBTrans tr) + { + return tr.Transaction; + } + #endregion + + #region 符号表 + + /// + /// 块表 + /// + public SymbolTable BlockTable => new(this, Database.BlockTableId); + /// + /// 当前绘图空间 + /// + public BlockTableRecord CurrentSpace => BlockTable.GetRecord(Database.CurrentSpaceId)!; + /// + /// 模型空间 + /// + public BlockTableRecord ModelSpace => BlockTable.GetRecord(BlockTable.CurrentSymbolTable[BlockTableRecord.ModelSpace])!; + /// + /// 图纸空间 + /// + public BlockTableRecord PaperSpace => BlockTable.GetRecord(BlockTable.CurrentSymbolTable[BlockTableRecord.PaperSpace])!; + /// + /// 层表 + /// + public SymbolTable LayerTable => new(this, Database.LayerTableId); + /// + /// 文字样式表 + /// + public SymbolTable TextStyleTable => new(this, Database.TextStyleTableId); + + /// + /// 注册应用程序表 + /// + public SymbolTable RegAppTable => new(this, Database.RegAppTableId); + + /// + /// 标注样式表 + /// + public SymbolTable DimStyleTable => new(this, Database.DimStyleTableId); + + /// + /// 线型表 + /// + public SymbolTable LinetypeTable => new(this, Database.LinetypeTableId); + + /// + /// 用户坐标系表 + /// + public SymbolTable UcsTable => new(this, Database.UcsTableId); + + /// + /// 视图表 + /// + public SymbolTable ViewTable => new(this, Database.ViewTableId); + + /// + /// 视口表 + /// + public SymbolTable ViewportTable => new(this, Database.ViewportTableId); + #endregion + + #region 字典 + /// + /// 命名对象字典 + /// + public DBDictionary NamedObjectsDict => GetObject(Database.NamedObjectsDictionaryId)!; + /// + /// 组字典 + /// + public DBDictionary GroupDict => GetObject(Database.GroupDictionaryId)!; + /// + /// 多重引线样式字典 + /// + public DBDictionary MLeaderStyleDict => GetObject(Database.MLeaderStyleDictionaryId)!; + /// + /// 多线样式字典 + /// + public DBDictionary MLStyleDict => GetObject(Database.MLStyleDictionaryId)!; + /// + /// 材质字典 + /// + public DBDictionary MaterialDict => GetObject(Database.MaterialDictionaryId)!; + /// + /// 表格样式字典 + /// + public DBDictionary TableStyleDict => GetObject(Database.TableStyleDictionaryId)!; + /// + /// 视觉样式字典 + /// + public DBDictionary VisualStyleDict => GetObject(Database.VisualStyleDictionaryId)!; + /// + /// 颜色字典 + /// + public DBDictionary ColorDict => GetObject(Database.ColorDictionaryId)!; + /// + /// 打印设置字典 + /// + public DBDictionary PlotSettingsDict => GetObject(Database.PlotSettingsDictionaryId)!; + /// + /// 打印样式表名字典 + /// + public DBDictionary PlotStyleNameDict => GetObject(Database.PlotStyleNameDictionaryId)!; + /// + /// 布局字典 + /// + public DBDictionary LayoutDict => GetObject(Database.LayoutDictionaryId)!; + /// + /// 数据链接字典 + /// + public DBDictionary DataLinkDict => GetObject(Database.DataLinkDictionaryId)!; +#if !ac2009 + /// + /// 详细视图样式字典 + /// + public DBDictionary DetailViewStyleDict => GetObject(Database.DetailViewStyleDictionaryId)!; + /// + /// 剖面视图样式字典 + /// + public DBDictionary SectionViewStyleDict => GetObject(Database.SectionViewStyleDictionaryId)!; +#endif + #endregion + + #region 获取对象 + /// + /// 根据对象id获取图元对象 + /// + /// 要获取的图元对象的类型 + /// 对象id + /// 打开模式,默认为只读 + /// 是否打开已删除对象,默认为不打开 + /// 是否打开锁定图层对象,默认为不打开 + /// 图元对象,类型不匹配时返回 + public T? GetObject(ObjectId id, + OpenMode mode = OpenMode.ForRead, + bool openErased = false, + bool forceOpenOnLockedLayer = false) where T : DBObject + { + return Transaction.GetObject(id, mode, openErased, forceOpenOnLockedLayer) as T; + } + + /// + /// 根据对象句柄字符串获取对象Id + /// + /// 句柄字符串 + /// 对象id + public ObjectId GetObjectId(string handleString) + { + var hanle = new Handle(Convert.ToInt64(handleString, 16)); + //return Database.GetObjectId(false, hanle, 0); + return Helper.TryGetObjectId(Database, hanle); + } + + + + #endregion + + #region 保存文件 + /// + /// 保存当前数据库的dwg文件,如果前台打开则按dwg默认版本保存,否则按version参数的版本保存 + /// + /// dwg版本,默认为2004 + public void SaveDwgFile(DwgVersion version = DwgVersion.AC1800) + { + bool flag = true; + foreach (Document doc in Application.DocumentManager) { - if (!disposedValue) + // 前台开图,使用命令保存 + if (doc.Database.Filename == this.Database.Filename) { - if (disposing) - { - // 释放托管状态(托管对象) - Commit(); - dBTrans.Pop(); - if (!Transaction.IsDisposed) - { - Transaction.Dispose(); - } - documentLock?.Dispose(); - } - - // 释放未托管的资源(未托管的对象)并替代终结器 - // 将大型字段设置为 null - disposedValue = true; + doc.SendStringToExecute("_qsave\n", false, true, true); //不需要切换文档 + flag = false; + break; } } - - // 仅当“Dispose(bool disposing)”拥有用于释放未托管资源的代码时才替代终结器 - ~DBTrans() + if (flag) { - // 不要更改此代码。请将清理代码放入“Dispose(bool disposing)”方法中 - Dispose(disposing: false); + // 后台开图,用数据库保存 + Database.SaveAs(Database.Filename, version); } + } + #endregion - public void Dispose() + #region idispose接口相关函数 + /// + /// 取消事务 + /// + public void Abort() + { + Dispose(false); + } + /// + /// 提交事务 + /// + public void Commit() + { + Dispose(true); + } + + protected virtual void Dispose(bool disposing) + { + /* 事务dispose流程: + * 1. 根据传入的参数确定是否提交,true为提交,false为不提交 + * 2. 根据disposedValue的值确定是否重复dispose,false为首次dispose + * 3. 如果锁文档就将文档锁dispose + * 4. 不管是否提交,既然进入dispose,就要将事务栈的当前事务弹出 + * 注意这里的事务栈不是cad的事务管理器,而是dbtrans的事务 + * 5. 清理非托管的字段 + */ + + if (disposedValue) + return; + + // 释放未托管的资源(未托管的对象)并替代终结器 + // 将大型字段设置为 null + disposedValue = true; + + if (disposing) + { + // 调用cad的事务进行提交,释放托管状态(托管对象) + Transaction.Commit(); + } + else { - // 不要更改此代码。请将清理代码放入“Dispose(bool disposing)”方法中 - Dispose(disposing: true); - GC.SuppressFinalize(this); + // 否则取消所有的修改 + Transaction.Abort(); } - #endregion + // 调用 cad事务的dispose进行销毁 + if (!Transaction.IsDisposed) + Transaction.Dispose(); + + // 调用文档锁dispose + documentLock?.Dispose(); + + // 将事务栈的当前dbtrans弹栈 + dBTrans.Pop(); + } + + // 仅当“Dispose(bool disposing)”拥有用于释放未托管资源的代码时才替代终结器 + ~DBTrans() + { + // 不要更改此代码。请将清理代码放入“Dispose(bool disposing)”方法中 + Dispose(disposing: false); + } + + public void Dispose() + { + // 不要更改此代码。请将清理代码放入“Dispose(bool disposing)”方法中 + Dispose(disposing: true); + GC.SuppressFinalize(this); } + #endregion } diff --git a/src/IFoxCAD.Cad/Runtime/Env.cs b/src/IFoxCAD.Cad/Runtime/Env.cs index 0d490e1..c7c6e92 100644 --- a/src/IFoxCAD.Cad/Runtime/Env.cs +++ b/src/IFoxCAD.Cad/Runtime/Env.cs @@ -1,453 +1,493 @@ -using Autodesk.AutoCAD.ApplicationServices; -using Autodesk.AutoCAD.DatabaseServices; -using Autodesk.AutoCAD.EditorInput; +namespace IFoxCAD.Cad; + +//此命名空间容易引起Polyline等等重义,因此不放入全局空间 using Autodesk.AutoCAD.GraphicsSystem; -using System; -namespace IFoxCAD.Cad +/// +/// 系统管理类 +/// 封装了一些系统 osmode、cmdecho、dimblk 系统变量 +/// 封装了常用的 文档 编辑器 数据库等对象为静态变量 +/// 封装了配置页面的注册表信息获取函数 +/// +public static class Env { + #region Goal + /// - /// 系统管理类 - /// 封装了一些系统 osmode、cmdecho、dimblk 系统变量 - /// 封装了常用的 文档 编辑器 数据库等对象为静态变量 - /// 封装了配置页面的注册表信息获取函数 + /// 当前的数据库 /// - public static class Env + public static Database Database => HostApplicationServices.WorkingDatabase; + + /// + /// 当前文档 + /// + public static Document Document => Application.DocumentManager.MdiActiveDocument; + + /// + /// 编辑器对象 + /// + public static Editor Editor => Document.Editor; + + /// + /// 图形管理器 + /// + public static Manager GsManager => Document.GraphicsManager; + + #endregion Goal + + #region Preferences + + /// + /// 获取当前配置的数据 + /// + /// 小节名 + /// 数据名 + /// 对象 + public static object GetCurrentProfileProperty(string subSectionName, string propertyName) + { + UserConfigurationManager ucm = Application.UserConfigurationManager; + IConfigurationSection cpf = ucm.OpenCurrentProfile(); + IConfigurationSection ss = cpf.OpenSubsection(subSectionName); + return ss.ReadProperty(propertyName, ""); + } + + /// + /// 获取对话框配置的数据 + /// + /// 对话框对象 + /// 配置项 + public static IConfigurationSection GetDialogSection(object dialog) { - #region Goal + UserConfigurationManager ucm = Application.UserConfigurationManager; + IConfigurationSection ds = ucm.OpenDialogSection(dialog); + return ds; + } + /// + /// 获取公共配置的数据 + /// + /// 数据名 + /// 配置项 + public static IConfigurationSection GetGlobalSection(string propertyName) + { + UserConfigurationManager ucm = Application.UserConfigurationManager; + IConfigurationSection gs = ucm.OpenGlobalSection(); + IConfigurationSection ss = gs.OpenSubsection(propertyName); + return ss; + } + + #endregion Preferences + + #region Enum + + /// + /// 控制在AutoLISP的command函数运行时AutoCAD是否回显提示和输入, 为显示, 为不显示 + /// + public static bool CmdEcho + { + get => Convert.ToInt16(Application.GetSystemVariable("cmdecho")) == 1; + set => Application.SetSystemVariable("cmdecho", Convert.ToInt16(value)); + } + + /// + /// 控制在光标是否为正交模式, 为打开正交, 为关闭正交 + /// + public static bool OrthoMode + { + get => Convert.ToInt16(Application.GetSystemVariable("ORTHOMODE")) == 1; + set => Application.SetSystemVariable("ORTHOMODE", Convert.ToInt16(value)); + } + + #region Dimblk + + /// + /// 标注箭头类型 + /// + public enum DimblkType + { /// - /// 当前的数据库 + /// 实心闭合 /// - public static Database CurrentDatabase - { - get - { - return HostApplicationServices.WorkingDatabase; - } - } + Defult, /// - /// 当前文档 + /// 点 /// - public static Document ActiveDocument - { - get - { - return Application.DocumentManager.MdiActiveDocument; - } - } + Dot, /// - /// 编辑器对象 + /// 小点 /// - public static Editor Editor - { - get - { - return ActiveDocument.Editor; - } - } + DotSmall, /// - /// 图形管理器 + /// 空心点 /// - public static Manager GsManager - { - get - { - return ActiveDocument.GraphicsManager; - } - } + DotBlank, - #endregion Goal + /// + /// 原点标记 + /// + Origin, - #region Preferences + /// + /// 原点标记2 + /// + Origin2, /// - /// 获取当前配置的数据 + /// 打开 /// - /// 小节名 - /// 数据名 - /// 对象 - public static object GetCurrentProfileProperty(string subSectionName, string propertyName) - { - UserConfigurationManager ucm = Application.UserConfigurationManager; - IConfigurationSection cpf = ucm.OpenCurrentProfile(); - IConfigurationSection ss = cpf.OpenSubsection(subSectionName); - return ss.ReadProperty(propertyName, ""); - } + Open, /// - /// 获取对话框配置的数据 + /// 直角 /// - /// 对话框对象 - /// 配置项 - public static IConfigurationSection GetDialogSection(object dialog) - { - UserConfigurationManager ucm = Application.UserConfigurationManager; - IConfigurationSection ds = ucm.OpenDialogSection(dialog); - return ds; - } + Open90, /// - /// 获取公共配置的数据 + /// 30度角 /// - /// 数据名 - /// 配置项 - public static IConfigurationSection GetGlobalSection(string propertyName) - { - UserConfigurationManager ucm = Application.UserConfigurationManager; - IConfigurationSection gs = ucm.OpenGlobalSection(); - IConfigurationSection ss = gs.OpenSubsection(propertyName); - return ss; - } + Open30, - #endregion Preferences + /// + /// 闭合 + /// + Closed, - #region Enum + /// + /// 空心小点 + /// + Small, /// - /// 控制在AutoLISP的command函数运行时AutoCAD是否回显提示和输入, 为显示, 为不显示 + /// 无 /// - public static bool CmdEcho - { - get => Convert.ToInt16(Application.GetSystemVariable("cmdecho")) == 1; - set => Application.SetSystemVariable("cmdecho", Convert.ToInt16(value)); - } + None, - #region Dimblk + /// + /// 倾斜 + /// + Oblique, /// - /// 标注箭头类型 + /// 实心框 /// - public enum DimblkType - { - /// - /// 实心闭合 - /// - Defult, - - /// - /// 点 - /// - Dot, - - /// - /// 小点 - /// - DotSmall, - - /// - /// 空心点 - /// - DotBlank, - - /// - /// 原点标记 - /// - Origin, - - /// - /// 原点标记2 - /// - Origin2, - - /// - /// 打开 - /// - Open, - - /// - /// 直角 - /// - Open90, - - /// - /// 30度角 - /// - Open30, - - /// - /// 闭合 - /// - Closed, - - /// - /// 空心小点 - /// - Small, - - /// - /// 无 - /// - None, - - /// - /// 倾斜 - /// - Oblique, - - /// - /// 实心框 - /// - BoxFilled, - - /// - /// 方框 - /// - BoxBlank, - - /// - /// 空心闭合 - /// - ClosedBlank, - - /// - /// 实心基准三角形 - /// - DatumFilled, - - /// - /// 基准三角形 - /// - DatumBlank, - - /// - /// 完整标记 - /// - Integral, - - /// - /// 建筑标记 - /// - ArchTick, - } + BoxFilled, /// - /// 标注箭头属性 + /// 方框 /// - public static DimblkType Dimblk - { - get - { - string s = (string)Application.GetSystemVariable("dimblk"); - if (string.IsNullOrEmpty(s)) - { - return DimblkType.Defult; - } - else - { - return s.ToEnum(); - } - } - set - { - string s = GetDimblkName(value); - Application.SetSystemVariable("dimblk", s); - } - } + BoxBlank, /// - /// 获取标注箭头名 + /// 空心闭合 /// - /// 标注箭头类型 - /// 箭头名 - public static string GetDimblkName(DimblkType dimblk) - { - return - dimblk == DimblkType.Defult - ? - "." - : - "_" + dimblk.GetName(); - } + ClosedBlank, /// - /// 获取标注箭头ID + /// 实心基准三角形 /// - /// 标注箭头类型 - /// 箭头ID - public static ObjectId GetDimblkId(DimblkType dimblk) - { - DimblkType oldDimblk = Dimblk; - Dimblk = dimblk; - ObjectId id = HostApplicationServices.WorkingDatabase.Dimblk; - Dimblk = oldDimblk; - return id; - } + DatumFilled, - #endregion Dimblk + /// + /// 基准三角形 + /// + DatumBlank, - #region OsMode + /// + /// 完整标记 + /// + Integral, /// - /// 捕捉模式系统变量类型 + /// 建筑标记 /// - public enum OSModeType + ArchTick + } + + private static readonly Dictionary dimdescdict = new() + { + { "实心闭合", DimblkType.Defult }, + { "点", DimblkType.Dot }, + { "小点", DimblkType.DotSmall }, + { "空心点", DimblkType.DotBlank }, + { "原点标记", DimblkType.Origin }, + { "原点标记 2", DimblkType.Origin2 }, + { "打开", DimblkType.Open }, + { "直角", DimblkType.Open90 }, + { "30 度角", DimblkType.Open30 }, + { "闭合", DimblkType.Closed }, + { "空心小点", DimblkType.Small }, + { "无", DimblkType.None }, + { "倾斜", DimblkType.Oblique }, + { "实心框", DimblkType.BoxFilled }, + { "方框", DimblkType.BoxBlank }, + { "空心闭合", DimblkType.ClosedBlank }, + { "实心基准三角形", DimblkType.DatumFilled }, + { "基准三角形", DimblkType.DatumBlank }, + { "完整标记", DimblkType.Integral }, + { "建筑标记", DimblkType.ArchTick }, + + { "", DimblkType.Defult }, + { "_DOT", DimblkType.Dot }, + { "_DOTSMALL", DimblkType.DotSmall }, + { "_DOTBLANK", DimblkType.DotBlank }, + { "_ORIGIN", DimblkType.Origin }, + { "_ORIGIN2", DimblkType.Origin2 }, + { "_OPEN", DimblkType.Open }, + { "_OPEN90", DimblkType.Open90 }, + { "_OPEN30", DimblkType.Open30 }, + { "_CLOSED", DimblkType.Closed }, + { "_SMALL", DimblkType.Small }, + { "_NONE", DimblkType.None }, + { "_OBLIQUE", DimblkType.Oblique }, + { "_BOXFILLED", DimblkType.BoxFilled }, + { "_BOXBLANK", DimblkType.BoxBlank }, + { "_CLOSEDBLANK", DimblkType.ClosedBlank }, + { "_DATUMFILLED", DimblkType.DatumFilled }, + { "_DATUMBLANK", DimblkType.DatumBlank }, + { "_INTEGRAL", DimblkType.Integral }, + { "_ARCHTICK", DimblkType.ArchTick }, + }; + + + + /// + /// 标注箭头属性 + /// + public static DimblkType Dimblk + { + get + { + string s = ((string)Application.GetSystemVariable("dimblk")).ToUpper(); + //if (string.IsNullOrEmpty(s)) + //{ + // return DimblkType.Defult; + //} + //else + //{ + // if (dimdescdict.TryGetValue(s, out DimblkType value)) + // { + // return value; + // } + // return s.ToEnum(); + // //return s.FromDescName(); + //} + return dimdescdict[s]; + } + set { - /// - /// 无 - /// - None = 0, - - /// - /// 端点 - /// - End = 1, - - /// - /// 中点 - /// - Middle = 2, - - /// - /// 圆心 - /// - Center = 4, - - /// - /// 节点 - /// - Node = 8, - - /// - /// 象限点 - /// - Quadrant = 16, - - /// - /// 交点 - /// - Intersection = 32, - - /// - /// 插入点 - /// - Insert = 64, - - /// - /// 垂足 - /// - Pedal = 128, - - /// - /// 切点 - /// - Tangent = 256, - - /// - /// 最近点 - /// - Nearest = 512, - - /// - /// 几何中心 - /// - Quick = 1024, - - /// - /// 外观交点 - /// - Appearance = 2048, - - /// - /// 延伸 - /// - Extension = 4096, - - /// - /// 平行 - /// - Parallel = 8192 + string s = GetDimblkName(value); + Application.SetSystemVariable("dimblk", s); } + } + + /// + /// 获取标注箭头名 + /// + /// 标注箭头类型 + /// 箭头名 + public static string GetDimblkName(DimblkType dimblk) + { + return + dimblk == DimblkType.Defult + ? + "." + : + "_" + dimblk.GetName(); + } + /// + /// 获取标注箭头ID + /// + /// 标注箭头类型 + /// 箭头ID + public static ObjectId GetDimblkId(DimblkType dimblk) + { + DimblkType oldDimblk = Dimblk; + Dimblk = dimblk; + ObjectId id = HostApplicationServices.WorkingDatabase.Dimblk; + Dimblk = oldDimblk; + return id; + } + + #endregion Dimblk + + #region OsMode + + /// + /// 捕捉模式系统变量类型 + /// + public enum OSModeType + { /// - /// 捕捉模式系统变量 + /// 无 /// - public static OSModeType OSMode - { - get - { - return (OSModeType)Convert.ToInt16(Application.GetSystemVariable("osmode")); - } - set - { - Application.SetSystemVariable("osmode", (int)value); - } - } + None = 0, + /// - /// 捕捉模式osm1是否包含osm2 + /// 端点 /// - /// 原模式 - /// 要比较的模式 - /// 包含时返回 ,不包含时返回 - public static bool Include(this OSModeType osm1, OSModeType osm2) - { - return (osm1 & osm2) == osm2; - } - #endregion OsMode + End = 1, - private static T ToEnum(this string value) - { - return (T)Enum.Parse(typeof(T), value, true); - } + /// + /// 中点 + /// + Middle = 2, - private static string GetName(this T value) - { - return Enum.GetName(typeof(T), value); - } + /// + /// 圆心 + /// + Center = 4, - #endregion Enum + /// + /// 节点 + /// + Node = 8, - #region 环境变量 /// - /// 获取cad变量 + /// 象限点 /// - /// 变量名 - /// 变量值 - public static object GetVar(string varName) - { - return Application.GetSystemVariable(varName); - } + Quadrant = 16, + /// - /// 设置cad变量 + /// 交点 /// - /// 变量名 - /// 变量值 - public static void SetVar(string varName, object value) - { - Application.SetSystemVariable(varName, value); - } -#nullable enable + Intersection = 32, + /// - /// 获取系统环境变量 + /// 插入点 /// - /// 变量名 - /// 指定的环境变量的值;或者如果找不到环境变量,则返回 null - public static string? GetEnv(string var) - { - //从当前进程或者从当前用户或本地计算机的 Windows 操作系统注册表项检索环境变量的值 - return Environment.GetEnvironmentVariable(var); - } + Insert = 64, + /// - /// 设置系统环境变量 + /// 垂足 /// - /// 变量名 - /// 变量值 - public static void SetEnv(string var, string? value) - { - //创建、修改或删除当前进程中或者为当前用户或本地计算机保留的 Windows 操作系统注册表项中存储的环境变量 - Environment.SetEnvironmentVariable(var, value); - } -#nullable disable - #endregion + Pedal = 128, + /// + /// 切点 + /// + Tangent = 256, + + /// + /// 最近点 + /// + Nearest = 512, + + /// + /// 几何中心 + /// + Quick = 1024, + + /// + /// 外观交点 + /// + Appearance = 2048, /// - /// 命令行打印,会自动调用对象的toString函数 + /// 延伸 /// - /// 要打印的对象 - public static void Print(object message) => Editor.WriteMessage($"{message}\n"); + Extension = 4096, + + /// + /// 平行 + /// + Parallel = 8192 + } + + /// + /// 捕捉模式系统变量 + /// + public static OSModeType OSMode + { + get + { + return (OSModeType)Convert.ToInt16(Application.GetSystemVariable("osmode")); + } + set + { + Application.SetSystemVariable("osmode", (int)value); + } + } + /// + /// 捕捉模式osm1是否包含osm2 + /// + /// 原模式 + /// 要比较的模式 + /// 包含时返回 ,不包含时返回 + public static bool Include(this OSModeType osm1, OSModeType osm2) + { + return (osm1 & osm2) == osm2; + } + #endregion OsMode + + + private static string GetName(this T value) + { + return Enum.GetName(typeof(T), value); + } + + #endregion Enum + + #region 环境变量 + /// + /// 获取cad变量 + /// + /// 变量名 + /// 变量值 + public static object GetVar(string varName) + { + return Application.GetSystemVariable(varName); + } + /// + /// 设置cad变量 + /// + /// 变量名 + /// 变量值 + public static void SetVar(string varName, object value) + { + try + { + Application.SetSystemVariable(varName, value); + } + catch (System.Exception) + { + Env.Print($"{varName} 是不存在的变量!"); + } + } + /// + /// 获取系统环境变量 + /// + /// 变量名 + /// 指定的环境变量的值;或者如果找不到环境变量,则返回 null + public static string? GetEnv(string var) + { + //从当前进程或者从当前用户或本地计算机的 Windows 操作系统注册表项检索环境变量的值 + return Environment.GetEnvironmentVariable(var); + } + /// + /// 设置系统环境变量 + /// + /// 变量名 + /// 变量值 + public static void SetEnv(string var, string? value) + { + //创建、修改或删除当前进程中或者为当前用户或本地计算机保留的 Windows 操作系统注册表项中存储的环境变量 + Environment.SetEnvironmentVariable(var, value); } + #endregion + + + /// + /// 命令行打印,会自动调用对象的toString函数 + /// + /// 要打印的对象 + public static void Print(object message) => Editor.WriteMessage($"{message}\n"); + /// + /// 判断当前是否在UCS坐标下 + /// + /// Bool + public static bool IsUcs() => (short)GetVar("WORLDUCS") == 0; } diff --git a/src/IFoxCAD.Cad/Runtime/FileOpenMode.cs b/src/IFoxCAD.Cad/Runtime/FileOpenMode.cs new file mode 100644 index 0000000..5108dd5 --- /dev/null +++ b/src/IFoxCAD.Cad/Runtime/FileOpenMode.cs @@ -0,0 +1,13 @@ +#if ac2008 //NET35 +namespace Autodesk.AutoCAD.DatabaseServices +{ + [Wrapper("AcDbDatabase::OpenMode")] + public enum FileOpenMode + { + OpenTryForReadShare = 4, + OpenForReadAndAllShare = 3, + OpenForReadAndWriteNoShare = 2, + OpenForReadAndReadShare = 1 + } +} +#endif \ No newline at end of file diff --git a/src/IFoxCAD.Cad/Runtime/IAutoGo.cs b/src/IFoxCAD.Cad/Runtime/IAutoGo.cs new file mode 100644 index 0000000..27ce550 --- /dev/null +++ b/src/IFoxCAD.Cad/Runtime/IAutoGo.cs @@ -0,0 +1,307 @@ +namespace IFoxCAD.Cad; + +using System.Diagnostics; + +/// +/// 加载时优先级 +/// +[Flags] +public enum Sequence : byte +{ + First,// 最先 + Last, // 最后 +} + +/// +/// 加载时自动执行接口 +/// +public interface IFoxAutoGo +{ + // 控制加载顺序 + Sequence SequenceId(); + // 关闭cad的时候会自动执行 + void Terminate(); + // 打开cad的时候会自动执行 + void Initialize(); +} + +/// +/// 加载时自动执行特性 +/// +[AttributeUsage(AttributeTargets.Method)] +public class IFoxInitialize : Attribute +{ + /// + /// 优先级 + /// + internal Sequence SequenceId; + /// + /// 用于初始化;用于结束回收 + /// + internal bool IsInitialize; + /// + /// 用于初始化和结束回收 + /// + /// 优先级 + /// 用于初始化;用于结束回收 + public IFoxInitialize(Sequence sequence = Sequence.Last, bool isInitialize = true) + { + SequenceId = sequence; + IsInitialize = isInitialize; + } +} + +//为了解决IExtensionApplication在一个dll内无法多次实现接口的关系 +//所以在这里反射加载所有的 IAutoGo ,以达到能分开写"启动运行"函数的目的 +class RunClass +{ + public Sequence Sequence { get; } + readonly MethodInfo _methodInfo; + + public RunClass(MethodInfo method, Sequence sequence) + { + _methodInfo = method; + Sequence = sequence; + } + + /// + /// 运行方法 + /// + public void Run() + { + _methodInfo.Invoke(); + } +} + +/// +/// 此类作为加载后cad自动运行接口的一部分,用于反射特性和接口 +/// 启动cad后的执行顺序为: +/// 1:特性..(多个) +/// 2:接口..(多个) +/// +public class AutoReflection +{ + static List _InitializeList = new(); //储存方法用于初始化 + static List _TerminateList = new(); //储存方法用于结束释放 + + readonly string _dllName; + readonly AutoRegConfig _autoRegConfig; + + /// + /// 反射执行 + /// 1.特性: + /// 2.接口: + /// + /// 约束在此dll进行加速 + public AutoReflection(string dllName, AutoRegConfig configInfo) + { + _dllName = dllName; + _autoRegConfig = configInfo; + } + + //启动cad的时候会自动执行 + public void Initialize() + { + try + { + //收集特性,包括启动时和关闭时 + if ((_autoRegConfig & AutoRegConfig.ReflectionAttribute) == AutoRegConfig.ReflectionAttribute) + GetAttributeFunctions(_InitializeList, _TerminateList); + + if ((_autoRegConfig & AutoRegConfig.ReflectionInterface) == AutoRegConfig.ReflectionInterface) + GetInterfaceFunctions(_InitializeList, nameof(Initialize)); + + if (_InitializeList.Count > 0) + { + //按照 SequenceId 排序_升序 + _InitializeList = _InitializeList.OrderBy(runClass => runClass.Sequence).ToList(); + RunFunctions(_InitializeList); + } + } + catch (System.Exception) + { + Debugger.Break(); + } + } + + //关闭cad的时候会自动执行 + public void Terminate() + { + try + { + if ((_autoRegConfig & AutoRegConfig.ReflectionInterface) == AutoRegConfig.ReflectionInterface) + GetInterfaceFunctions(_TerminateList, nameof(Terminate)); + + if (_TerminateList.Count > 0) + { + //按照 SequenceId 排序_降序 + _TerminateList = _TerminateList.OrderByDescending(runClass => runClass.Sequence).ToList(); + RunFunctions(_TerminateList); + } + } + catch (System.Exception) + { + Debugger.Break(); + } + } + + /// + /// 遍历程序域下所有类型 + /// + /// 输出每个成员执行 + /// 过滤此dll,不含后缀 + public static void AppDomainGetTypes(Action action, string? dllNameWithoutExtension = null) + { +#if DEBUG + int error = 0; +#endif + try + { + var assemblies = AppDomain.CurrentDomain.GetAssemblies(); +#if !NET35 + //cad2021出现如下报错 + //System.NotSupportedException:动态程序集中不支持已调用的成员 + //assemblies = assemblies.Where(p => !p.IsDynamic).ToArray();//这个要容器类型转换 + assemblies = Array.FindAll(assemblies, p => !p.IsDynamic); +#endif + //主程序域 + for (int ii = 0; ii < assemblies.Length; ii++) + { + var assembly = assemblies[ii]; + + //获取类型集合,反射时候还依赖其他的dll就会这个错误 + //此通讯库要跳过,否则会报错. + var location = Path.GetFileNameWithoutExtension(assembly.Location); + if (dllNameWithoutExtension != null && location != dllNameWithoutExtension) + continue; + if (location == "AcInfoCenterConn")//通讯库 + continue; + + Type[]? types = null; + try + { + types = assembly.GetTypes(); + } + catch (ReflectionTypeLoadException) { continue; } + + if (types is null) + continue; + + for (int jj = 0; jj < types.Length; jj++) + { + var type = types[jj]; + if (type is not null) + { +#if DEBUG + ++error; +#endif + action(type); + } + } + } + } + catch (System.Exception e) + { +#if DEBUG + Debug.WriteLine($"出错:{nameof(AppDomainGetTypes)};计数{error};错误信息:{e.Message}"); + Debugger.Break(); +#endif + } + } + + /// + /// 收集接口下的函数 + /// + /// 储存要运行的方法 + /// 查找方法名 + /// + void GetInterfaceFunctions(List runClassList, string methodName) + { + const string sqid = nameof(Sequence) + "Id"; + + AppDomainGetTypes(type => { + if (type.IsAbstract) + return; + + var ints = type.GetInterfaces(); + for (int sss = 0; sss < ints.Length; sss++) + { + var inters = ints[sss]; + if (inters.Name != nameof(IFoxAutoGo)) + continue; + + Sequence? sequence = null; + MethodInfo? initialize = null; + + var mets = type.GetMethods(); + for (int jj = 0; jj < mets.Length; jj++) + { + var method = mets[jj]; + if (method.IsAbstract) + continue; + + if (method.Name == sqid) + { + var obj = method.Invoke(); + if (obj is not null) + sequence = (Sequence)obj; + continue; + } + else if (method.Name == methodName) + initialize = method; + + if (initialize is not null && sequence is not null) + break; + } + + if (initialize is null) + continue; + + var seq = sequence is null ? Sequence.Last : sequence.Value; + runClassList.Add(new RunClass(initialize, seq)); + break; + } + }, _dllName); + + } + + /// + /// 收集特性下的函数 + /// + void GetAttributeFunctions(List initialize, List terminate) + { + AppDomainGetTypes(type => { + if (type.IsAbstract) + return; + + var mets = type.GetMethods(); + for (int ii = 0; ii < mets.Length; ii++) + { + var method = mets[ii]; + var attr = method.GetCustomAttributes(true); + for (int jj = 0; jj < attr.Length; jj++) + { + if (attr[jj] is IFoxInitialize jjAtt) + { + var runc = new RunClass(method, jjAtt.SequenceId); + if (jjAtt.IsInitialize) + initialize.Add(runc); + else + terminate.Add(runc); + break; + } + } + } + }, _dllName); + } + + /// + /// 执行收集到的函数 + /// + static void RunFunctions(List runClassList) + { + for (int i = 0; i < runClassList.Count; i++) + runClassList[i].Run(); + runClassList.Clear(); + } +} \ No newline at end of file diff --git a/src/IFoxCAD.Cad/Runtime/Log.cs b/src/IFoxCAD.Cad/Runtime/Log.cs new file mode 100644 index 0000000..02293e2 --- /dev/null +++ b/src/IFoxCAD.Cad/Runtime/Log.cs @@ -0,0 +1,412 @@ +namespace IFoxCAD.Cad; + +using System; +using System.Diagnostics; +using System.Threading; + +#region 写入日志到不同的环境中 +//https://zhuanlan.zhihu.com/p/338492989 +public abstract class LogBase +{ + public abstract void ReadLog(string message); + public abstract void WriteLog(string message); + public abstract void DeleteLog(string message); +} + +/// +/// 日志输出环境 +/// +public enum LogTarget +{ + /// + /// 文件 + /// + File = 1, + /// + /// 数据库 + /// + Database = 2, + /// + /// windows日志 + /// + EventLog = 4, +} + +/// +/// 写入到文件中 +/// +public class FileLogger : LogBase +{ + public override void DeleteLog(string message) + { + throw new NotImplementedException(); + } + + public override void ReadLog(string message) + { + throw new NotImplementedException(); + } + + public override void WriteLog(string? message) + { + //把异常信息输出到文件 + var sw = new StreamWriter(LogHelper.LogAddress, true/*当天日志文件存在就追加,否则就创建*/); + sw.Write(message); + sw.Flush(); + sw.Close(); + sw.Dispose(); + } +} + +/// +/// 写入到数据库(暂时不支持) +/// +public class DBLogger : LogBase +{ + public override void DeleteLog(string message) + { + throw new NotImplementedException(); + } + + public override void ReadLog(string message) + { + throw new NotImplementedException(); + } + + public override void WriteLog(string? message) + { + throw new NotImplementedException(); + } +} + +/// +/// 写入到win日志 +/// +public class EventLogger : LogBase +{ + // https://docs.microsoft.com/en-us/answers/questions/526018/windows-event-log-with-net-5.html + // net50要加 + // 需要win权限 + + public string LogName = "IFoxCadLog"; + public override void DeleteLog(string message) + { +#if !NET5_0 && !NET6_0 + if (EventLog.Exists(LogName)) + EventLog.Delete(LogName); +#endif + } + + public override void ReadLog(string message) + { +#if !NET5_0 && !NET6_0 + EventLog eventLog = new(); + eventLog.Log = LogName; + foreach (EventLogEntry entry in eventLog.Entries) + { + //Write your custom code here + } +#endif + } + + public override void WriteLog(string? message) + { +#if !NET5_0 && !NET6_0 + try + { + EventLog eventLog = new() + { + Source = LogName + }; + eventLog.WriteEntry(message, EventLogEntryType.Information); + } + catch (System.Security.SecurityException e) + { + throw new Exception("您没有权限写入win日志中" + e.Message); + } +#endif + } +} + +#endregion + +#region 静态方法 +public static class LogHelper +{ +#pragma warning disable CA2211 // 非常量字段应当不可见 + /// + /// 日志文件完整路径 + /// + public static string? LogAddress; + /// + /// 输出错误信息到日志文件的开关 + /// + public static bool FlagOutFile = false; + /// + /// 输出错误信息到vs输出窗口的开关 + /// + public static bool FlagOutVsOutput = true; + +#pragma warning restore CA2211 // 非常量字段应当不可见 + + /// + /// 读写锁 + /// 当资源处于写入模式时,其他线程写入需要等待本次写入结束之后才能继续写入 + /// + static readonly ReaderWriterLockSlim _logWriteLock = new(); + + /// + /// 提供给外部设置log文件保存路径 + /// + /// 空的话就为运行的dll旁边的一个文件夹上 + public static void OptionFile(string? newlogAddress = null) + { + _logWriteLock.EnterWriteLock();// 写模式锁定 读写锁 + try + { + LogAddress = newlogAddress; + if (string.IsNullOrEmpty(LogAddress)) + { + //微软回复:静态构造函数只会被调用一次, + //并且在它执行完成之前,任何其它线程都不能创建这个类的实例或使用这个类的静态成员 + //https://blog.csdn.net/weixin_34204722/article/details/90095812 + var sb = new StringBuilder(); + sb.Append(Environment.CurrentDirectory); + sb.Append("\\ErrorLog"); + + //新建文件夹 + var path = sb.ToString(); + if (!Directory.Exists(path)) + { + Directory.CreateDirectory(path) + .Attributes = FileAttributes.Normal; //设置文件夹属性为普通 + } + + sb.Append('\\'); + sb.Append(DateTime.Now.ToString("yy-MM-dd")); + sb.Append(".log"); + LogAddress = sb.ToString(); + } + } + finally + { + _logWriteLock.ExitWriteLock();// 解锁 读写锁 + } + } + + public static string WriteLog(this string? message, + LogTarget target = LogTarget.File) + { + if (message == null) + return string.Empty; + return LogAction(null, message, target); + } + + public static string WriteLog(this Exception? exception, + LogTarget target = LogTarget.File) + { + if (exception == null) + return string.Empty; + return LogAction(exception, null, target); + } + + public static string WriteLog(this Exception? exception, string? message, + LogTarget target = LogTarget.File) + { + if (exception == null) + return string.Empty; + return LogAction(exception, message, target); + } + + + + static string LogAction(Exception? ex, string? message, LogTarget target) + { + if (ex == null && message == null) + return string.Empty; + + if (target == LogTarget.File && LogAddress == null) + OptionFile(); + + try + { + _logWriteLock.EnterWriteLock();// 写模式锁定 读写锁 + + var logtxt = new LogTxt(ex, message); + //var logtxtJson = Newtonsoft.Json.JsonConvert.SerializeObject(logtxt, Formatting.Indented); + var logtxtJson = logtxt?.ToString(); + if (logtxtJson == null) + return string.Empty; + + if (FlagOutFile) + { + LogBase? logger; + switch (target) + { + case LogTarget.File: + logger = new FileLogger(); + logger.WriteLog(logtxtJson); + break; + case LogTarget.Database: + logger = new DBLogger(); + logger.WriteLog(logtxtJson); + break; + case LogTarget.EventLog: + logger = new EventLogger(); + logger.WriteLog(logtxtJson); + break; + } + } + + if (FlagOutVsOutput) + { + Debug.WriteLine("错误日志: " + LogAddress); + Debug.Write(logtxtJson); + } + return logtxtJson; + } + finally + { + _logWriteLock.ExitWriteLock();// 解锁 读写锁 + } + } +} +#endregion + +#region 序列化 +[Serializable] +public class LogTxt +{ + public string 当前时间; + public string? 备注信息; + public string? 异常信息; + public string? 异常对象; + public string? 触发方法; + public string? 调用堆栈; + + LogTxt() + { + // 以不同语言显示日期 + // DateTime.Now.ToString("f", new System.Globalization.CultureInfo("es-ES")) + // DateTime.Now.ToString("f", new System.Globalization.CultureInfo("zh-cn")) + // 为了最小信息熵,所以用这样的格式,并且我喜欢补0 + 当前时间 = DateTime.Now.ToString("yy-MM-dd hh:mm:ss"); + } + + public LogTxt(Exception? ex, string? message) : this() + { + if (ex == null && message == null) + throw new ArgumentNullException(nameof(ex)); + + if (ex != null) + { + 异常信息 = ex.Message; + 异常对象 = ex.Source; + 触发方法 = ex.TargetSite == null ? string.Empty : ex.TargetSite.ToString(); + 调用堆栈 = ex.StackTrace == null ? string.Empty : ex.StackTrace.Trim(); + } + if (message != null) + 备注信息 = message; + } + + /// 为了不引入json的dll,所以这里自己构造 + public override string? ToString() + { + var sb = new StringBuilder(); + sb.Append('{'); + sb.Append(Environment.NewLine); + sb.AppendLine($" \"{nameof(当前时间)}\": \"{当前时间}\""); + sb.AppendLine($" \"{nameof(备注信息)}\": \"{备注信息}\""); + sb.AppendLine($" \"{nameof(异常信息)}\": \"{异常信息}\""); + sb.AppendLine($" \"{nameof(异常对象)}\": \"{异常对象}\""); + sb.AppendLine($" \"{nameof(触发方法)}\": \"{触发方法}\""); + sb.AppendLine($" \"{nameof(调用堆栈)}\": \"{调用堆栈}\""); + sb.Append('}'); + return sb.ToString(); + } +} +#endregion + + +#if false //最简单的实现 +public static class Log +{ + /// + /// 读写锁 + /// 当资源处于写入模式时,其他线程写入需要等待本次写入结束之后才能继续写入 + /// + static readonly ReaderWriterLockSlim _logWriteLock = new(); + + /// + /// 日志文件完整路径 + /// + static readonly string _logAddress; + + static Log() + { + //微软回复:静态构造函数只会被调用一次, + //并且在它执行完成之前,任何其它线程都不能创建这个类的实例或使用这个类的静态成员 + //https://blog.csdn.net/weixin_34204722/article/details/90095812 + var sb = new StringBuilder(); + sb.Append(Environment.CurrentDirectory); + sb.Append("\\ErrorLog"); + + //新建文件夹 + var path = sb.ToString(); + if (!Directory.Exists(path)) + { + Directory.CreateDirectory(path) + .Attributes = FileAttributes.Normal; //设置文件夹属性为普通 + } + + sb.Append('\\'); + sb.Append(DateTime.Now.ToString("yy-MM-dd")); + sb.Append(".log"); + _logAddress = sb.ToString(); + } + + + /// + /// 将异常打印到日志文件 + /// + /// 异常 + /// 备注 + /// DEBUG模式打印到vs输出窗口 + public static string? WriteLog(this Exception? ex, + string? remarks = null, + bool printDebugWindow = true) + { + try + { + _logWriteLock.EnterWriteLock();// 写模式锁定 读写锁 + + var logtxt = new LogTxt(ex, remarks); + //var logtxtJson = Newtonsoft.Json.JsonConvert.SerializeObject(logtxt, Formatting.Indented); + var logtxtJson = logtxt.ToString(); + + if (logtxtJson == null) + return string.Empty; + + //把异常信息输出到文件 + var sw = new StreamWriter(_logAddress, true/*当天日志文件存在就追加,否则就创建*/); + sw.Write(logtxtJson); + sw.Flush(); + sw.Close(); + sw.Dispose(); + + if (printDebugWindow) + { + Debug.WriteLine("错误日志: " + _logAddress); + Debug.Write(logtxtJson); + //Debugger.Break(); + //Debug.Assert(false, "终止进程"); + } + return logtxtJson; + } + finally + { + _logWriteLock.ExitWriteLock();// 解锁 读写锁 + } + } +} +#endif \ No newline at end of file diff --git a/src/IFoxCAD.Cad/Runtime/MethodInfoHelper.cs b/src/IFoxCAD.Cad/Runtime/MethodInfoHelper.cs new file mode 100644 index 0000000..c64a5f8 --- /dev/null +++ b/src/IFoxCAD.Cad/Runtime/MethodInfoHelper.cs @@ -0,0 +1,53 @@ +namespace IFoxCAD.Cad; + +internal static class MethodInfoHelper +{ + private static readonly Dictionary methodDic = new(); + + /// + /// 执行函数 + /// + /// 函数 + /// 已经外部创建的对象,为空则此处创建 + public static object? Invoke(this MethodInfo methodInfo, object? instance = null) + { + if (methodInfo == null) + throw new ArgumentNullException(nameof(methodInfo)); + + object? result = null; + if (methodInfo.IsStatic) + { + //新函数指针进入此处 + //参数数量一定要匹配,为null则参数个数不同导致报错, + //参数为stirng[],则可以传入object[]代替,其他参数是否还可以实现默认构造? + var paramInfos = methodInfo.GetParameters(); + var args = new List { }; + for (int i = 0; i < paramInfos.Length; i++) + args.Add(null!); + result = methodInfo.Invoke(null, args.ToArray());//静态调用 + } + else + { + //原命令的函数指针进入此处 + //object instance; + if (methodDic.ContainsKey(methodInfo)) + instance = methodDic[methodInfo]; + + if (instance == null) + { + var reftype = methodInfo.ReflectedType; + if (reftype == null) return null; + var fullName = reftype.FullName; //命名空间+类 + if (fullName == null) return null; + var type = reftype.Assembly.GetType(fullName);//通过程序集反射创建类+ + if (type == null) return null; + instance = Activator.CreateInstance(type); + if (!type.IsAbstract)//无法创建抽象类成员 + methodDic.Add(methodInfo, instance); + } + if (instance != null) + result = methodInfo.Invoke(instance, null); //非静态,调用实例化方法 + } + return result; + } +} \ No newline at end of file diff --git a/src/IFoxCAD.Cad/Runtime/SymbolTable.cs b/src/IFoxCAD.Cad/Runtime/SymbolTable.cs index c319b5e..a31e0ac 100644 --- a/src/IFoxCAD.Cad/Runtime/SymbolTable.cs +++ b/src/IFoxCAD.Cad/Runtime/SymbolTable.cs @@ -1,317 +1,327 @@ -using System; -using System.Collections; -using System.Collections.Generic; -using System.Linq; -using Autodesk.AutoCAD.DatabaseServices; -namespace IFoxCAD.Cad +namespace IFoxCAD.Cad; + +public class SymbolTable : IEnumerable + where TTable : SymbolTable + where TRecord : SymbolTableRecord, new() { - public class SymbolTable : IEnumerable - where TTable : SymbolTable - where TRecord : SymbolTableRecord, new() - { - #region 程序集内部属性 - /// - /// 事务管理器 - /// - internal DBTrans DTrans { get; private set; } - /// - /// 数据库 - /// - internal Database Database { get; private set; } + #region 程序集内部属性 + /// + /// 事务管理器 + /// + internal DBTrans DTrans { get; private set; } + /// + /// 数据库 + /// + internal Database Database { get; private set; } - #endregion + #endregion - #region 公开属性 - /// - /// 当前符号表 - /// - public TTable CurrentSymbolTable { get; private set; } - #endregion + #region 公开属性 + /// + /// 当前符号表 + /// + public TTable CurrentSymbolTable { get; private set; } + #endregion - #region 构造函数 - /// - /// 构造函数,初始化Trans和CurrentSymbolTable属性 - /// - /// 事务管理器 - /// 符号表id - internal SymbolTable(DBTrans tr, ObjectId tableId) - { - DTrans = tr; - CurrentSymbolTable = DTrans.GetObject(tableId); - } + #region 构造函数 + /// + /// 构造函数,初始化Trans和CurrentSymbolTable属性 + /// + /// 事务管理器 + /// 符号表id + internal SymbolTable(DBTrans tr, ObjectId tableId) + { + DTrans = tr; + Database = tr.Database; + CurrentSymbolTable = DTrans.GetObject(tableId)!; + } - #endregion + #endregion - #region 索引器 - /// - /// 索引器 - /// - /// 对象名称 - /// 对象的id - public ObjectId this[string key] + #region 索引器 + /// + /// 索引器 + /// + /// 对象名称 + /// 对象的id + public ObjectId this[string key] + { + get { - get - { - if (Has(key)) - { - return CurrentSymbolTable[key]; - } - return ObjectId.Null; - } + if (Has(key)) + return CurrentSymbolTable[key]; + return ObjectId.Null; } - #endregion + } + #endregion - #region Has - /// - /// 判断是否存在符号表记录 - /// - /// 记录名 - /// 存在返回 , 不存在返回 - public bool Has(string key) - { - return CurrentSymbolTable.Has(key); - } - /// - /// 判断是否存在符号表记录 - /// - /// 记录id - /// 存在返回 , 不存在返回 - public bool Has(ObjectId objectId) - { - return CurrentSymbolTable.Has(objectId); - } - #endregion + #region Has + /// + /// 判断是否存在符号表记录 + /// + /// 记录名 + /// 存在返回 , 不存在返回 + public bool Has(string key) + { + return CurrentSymbolTable.Has(key); + } + /// + /// 判断是否存在符号表记录 + /// + /// 记录id + /// 存在返回 , 不存在返回 + public bool Has(ObjectId objectId) + { + return CurrentSymbolTable.Has(objectId); + } + #endregion - #region 添加符号表记录 - /// - /// 添加符号表记录 - /// - /// 符号表记录 - /// 对象id - private ObjectId Add(TRecord record) + #region 添加符号表记录 + /// + /// 添加符号表记录 + /// + /// 符号表记录 + /// 对象id + private ObjectId Add(TRecord record) + { + ObjectId id; + using (CurrentSymbolTable.ForWrite()) { - ObjectId id; - using (CurrentSymbolTable.ForWrite()) - { - id = CurrentSymbolTable.Add(record); - DTrans.Transaction.AddNewlyCreatedDBObject(record, true); - } - return id; + id = CurrentSymbolTable.Add(record); + DTrans.Transaction.AddNewlyCreatedDBObject(record, true); } - /// - /// 添加符号表记录 - /// - /// 符号表记录名 - /// 符号表记录处理函数的无返回值委托 - /// 对象id - public ObjectId Add(string name, Action action = null) - { - ObjectId id = this[name]; - if (id.IsNull) - { - TRecord record = new() - { - Name = name - }; - id = Add(record); - using (record.ForWrite()) - { - action?.Invoke(record); - } - } + return id; + } + /// + /// 添加符号表记录 + /// + /// 符号表记录名 + /// 符号表记录处理函数的无返回值委托 + /// 对象id + public ObjectId Add(string name, Action? action = null) + { + ObjectId id = this[name]; + if (id.IsNull) return id; - } - #endregion - #region 删除符号表记录 - /// - /// 删除符号表记录 - /// - /// 符号表记录对象 - private static void Remove(TRecord record) + var record = new TRecord() { - using (record.ForWrite()) - { - record.Erase(); - } - } - /// - /// 删除符号表记录 - /// - /// 符号表记录名 - public void Remove(string name) - { - TRecord record = GetRecord(name); - if (record is not null) - { - Remove(record); - } + Name = name + }; + id = Add(record); + using (record.ForWrite()) + action?.Invoke(record); + return id; + } + #endregion - } - /// - /// 删除符号表记录 - /// - /// 符号表记录对象id - public void Remove(ObjectId id) - { - TRecord record = GetRecord(id); - if (record is not null) - { - Remove(record); - } - } + #region 删除符号表记录 + /// + /// 删除符号表记录 + /// + /// 符号表记录对象 + private static void Remove(TRecord record) + { + using (record.ForWrite()) + record.Erase(); + } + /// + /// 删除符号表记录 + /// + /// 符号表记录名 + public void Remove(string name) + { + var record = GetRecord(name); + if (record is not null) + Remove(record); + } - #endregion + /// + /// 删除符号表记录 + /// + /// 符号表记录对象id + public void Remove(ObjectId id) + { + var record = GetRecord(id); + if (record is not null) + Remove(record); + } + #endregion - #region 修改符号表记录 - /// - /// 修改符号表 - /// - /// 符号表记录 - /// 修改委托 - private static void Change(TRecord record, Action action) + #region 修改符号表记录 + /// + /// 修改符号表 + /// + /// 符号表记录 + /// 修改委托 + private static void Change(TRecord record, Action action) + { + using (record.ForWrite()) { - using (record.ForWrite()) - { - action?.Invoke(record); - } + action.Invoke(record); } - /// - /// 修改符号表 - /// - /// 符号表记录名 - /// 修改委托 - public void Change(string name, Action action) + // 调用regen()函数可能会导致卡顿 + //Env.Editor.Regen(); + } + /// + /// 修改符号表 + /// + /// 符号表记录名 + /// 修改委托 + public void Change(string name, Action action) + { + var record = GetRecord(name); + if (record is not null) { - var record = GetRecord(name); - if (record is not null) - { - Change(record, action); - } + Change(record, action); } - /// - /// 修改符号表 - /// - /// 符号表记录id - /// 修改委托 - public void Change(ObjectId id, Action action) + } + /// + /// 修改符号表 + /// + /// 符号表记录id + /// 修改委托 + public void Change(ObjectId id, Action action) + { + var record = GetRecord(id); + if (record is not null) { - var record = GetRecord(id); - if (record is not null) - { - Change(record, action); - } + Change(record, action); } - #endregion + } + #endregion - #region 获取符号表记录 - /// - /// 获取符号表记录 - /// - /// 符号表记录的id - /// 打开模式,默认为只读 - /// 符号表记录 - public TRecord GetRecord(ObjectId id, OpenMode openMode = OpenMode.ForRead) => id.IsNull ? null : DTrans.GetObject(id, openMode); + #region 获取符号表记录 + /// + /// 获取符号表记录 + /// + /// 符号表记录的id + /// 打开模式,默认为只读 + /// 符号表记录 + public TRecord? GetRecord(ObjectId id, OpenMode openMode = OpenMode.ForRead) => /*id.IsNull ? null : */DTrans.GetObject(id, openMode); - /// - /// 获取符号表记录 - /// - /// 符号表记录名 - /// 打开模式,默认为只读 - /// 符号表记录 - public TRecord GetRecord(string name, OpenMode openMode = OpenMode.ForRead) => GetRecord(this[name], openMode); + /// + /// 获取符号表记录 + /// + /// 符号表记录名 + /// 打开模式,默认为只读 + /// 符号表记录 + public TRecord? GetRecord(string name, OpenMode openMode = OpenMode.ForRead) => GetRecord(this[name], openMode); - /// - /// 获取符号表记录 - /// - /// 符号表记录集合 - public IEnumerable GetRecords() - { - return this.Select(id => GetRecord(id)); - } - - /// - /// 获取符号表记录的名字集合 - /// - /// 记录的名字集合 - public IEnumerable GetRecordNames() => this.Select(id => GetRecord(id).Name); - /// - /// 获取符合过滤条件的符号表记录名字集合 - /// - /// 过滤器委托 - /// 记录的名字集合 - public IEnumerable GetRecordNames(Func filter) + /// + /// 获取符号表记录 + /// + /// 符号表记录集合 + public IEnumerable GetRecords() + { + foreach (var item in this) { - foreach (var id in this) + var record = GetRecord(item); + if (record is not null) { - var record = GetRecord(id); - if (filter.Invoke(record)) - { - yield return record.Name; - } + yield return record; } } + } - /// - /// 从源数据库拷贝符号表记录 - /// - /// 符号表 - /// 符号表记录名 - /// 是否覆盖, 为覆盖, 为不覆盖 - /// 对象id - public ObjectId GetRecordFrom(SymbolTable table, string name, bool over) + /// + /// 获取符号表记录的名字集合 + /// + /// 记录的名字集合 + public IEnumerable GetRecordNames() => GetRecords().Select(record => record.Name); + /// + /// 获取符合过滤条件的符号表记录名字集合 + /// + /// 过滤器委托 + /// 记录的名字集合 + public IEnumerable GetRecordNames(Func filter) + { + foreach (var item in this) { - if (table is null) - { - throw new ArgumentNullException(nameof(table)); - } - - ObjectId rid = this[name]; - bool has = rid != ObjectId.Null; - if ((has && over) || !has) + var record = GetRecord(item); + if (record is not null && filter.Invoke(record)) { - ObjectId id = table[name]; - using IdMapping idm = new(); - using (ObjectIdCollection ids = new() { id }) - { - table.Database.WblockCloneObjects(ids, CurrentSymbolTable.Id, idm, DuplicateRecordCloning.Replace, false); - } - rid = idm[id].Value; + yield return record.Name; } - return rid; } + } - /// - /// 从文件拷贝符号表记录 - /// - /// 符号表过滤器 - /// 文件名 - /// 符号表记录名 - /// 是否覆盖, 为覆盖, 为不覆盖 - /// 对象id - internal ObjectId GetRecordFrom(Func> tableSelector, string fileName, string name, bool over) + /// + /// 从源数据库拷贝符号表记录 + /// + /// 符号表 + /// 符号表记录名 + /// 是否覆盖, 为覆盖, 为不覆盖 + /// 对象id + public ObjectId GetRecordFrom(SymbolTable table, string name, bool over) + { + if (table is null) { - using var tr = new DBTrans(fileName); - return GetRecordFrom(tableSelector(tr), name, over); + throw new ArgumentNullException(nameof(table), "对象为null"); } - #endregion - #region IEnumerable 成员 - - public IEnumerator GetEnumerator() + ObjectId rid = this[name]; + bool has = rid != ObjectId.Null; + if ((has && over) || !has) { - - foreach (var id in CurrentSymbolTable) + ObjectId id = table[name]; + using IdMapping idm = new(); + using (ObjectIdCollection ids = new() { id }) { - yield return id; + table.Database.WblockCloneObjects(ids, CurrentSymbolTable.Id, idm, DuplicateRecordCloning.Replace, false); } + rid = idm[id].Value; } + return rid; + } + + /// + /// 从文件拷贝符号表记录 + /// + /// 符号表过滤器 + /// 文件名 + /// 符号表记录名 + /// 是否覆盖, 为覆盖, 为不覆盖 + /// 对象id + internal ObjectId GetRecordFrom(Func> tableSelector, string fileName, string name, bool over) + { + using var tr = new DBTrans(fileName); + return GetRecordFrom(tableSelector(tr), name, over); + } + #endregion + + #region 遍历 + /// + /// 遍历集合的迭代器,执行action委托 + /// + /// 集合值的类型 + /// 集合 + /// 要运行的委托 + public void ForEach(Action action, OpenMode openMode = OpenMode.ForRead) + { + //GetRecords().ForEach(re => action(re)); - IEnumerator IEnumerable.GetEnumerator() + foreach (var item in this) { - return GetEnumerator(); + var record = GetRecord(item, openMode); + if (record is not null) + action(record); } - #endregion } + #endregion + + #region IEnumerable 成员 + + public IEnumerator GetEnumerator() + { + foreach (var id in CurrentSymbolTable) + yield return id; + } + + IEnumerator IEnumerable.GetEnumerator() + { + return GetEnumerator(); + } + #endregion } diff --git a/src/IFoxCAD.Cad/Runtime/Utils.cs b/src/IFoxCAD.Cad/Runtime/Utils.cs new file mode 100644 index 0000000..eb372ab --- /dev/null +++ b/src/IFoxCAD.Cad/Runtime/Utils.cs @@ -0,0 +1,98 @@ +using System; + +namespace IFoxCAD.Cad; + +public class Helper +{ + /* + * id = db.GetObjectId(false, handle, 0); + * 参数意义: db.GetObjectId(如果没有找到就创建,句柄号,标记..将来备用) + * 在vs的输出会一直抛出: + * 引发的异常:“Autodesk.AutoCAD.Runtime.Exception”(位于 AcdbMgd.dll 中) + * "eUnknownHandle" + * 这就是为什么慢的原因,所以直接运行就好了!而Debug还是需要用arx的API替代. + */ + + [System.Security.SuppressUnmanagedCodeSecurity] + [DllImport("acdb17.dll", CallingConvention = CallingConvention.ThisCall/*08的调用约定 高版本是__cdecl*/, + EntryPoint = "?getAcDbObjectId@AcDbDatabase@@QAE?AW4ErrorStatus@Acad@@AAVAcDbObjectId@@_NABVAcDbHandle@@K@Z")] + extern static int getAcDbObjectId17x32(IntPtr db, out ObjectId id, [MarshalAs(UnmanagedType.U1)] bool createnew, ref Handle h, uint reserved); + + [System.Security.SuppressUnmanagedCodeSecurity] + [DllImport("acdb17.dll", CallingConvention = CallingConvention.ThisCall/*08的调用约定 高版本是__cdecl*/, + EntryPoint = "?getAcDbObjectId@AcDbDatabase@@QEAA?AW4ErrorStatus@Acad@@AEAVAcDbObjectId@@_NAEBVAcDbHandle@@K@Z")] + extern static int getAcDbObjectId17x64(IntPtr db, out ObjectId id, [MarshalAs(UnmanagedType.U1)] bool createnew, ref Handle h, uint reserved); + + [System.Security.SuppressUnmanagedCodeSecurity] + [DllImport("acdb18.dll", CallingConvention = CallingConvention.ThisCall/*08的调用约定 高版本是__cdecl*/, + EntryPoint = "?getAcDbObjectId@AcDbDatabase@@QAE?AW4ErrorStatus@Acad@@AAVAcDbObjectId@@_NABVAcDbHandle@@K@Z")] + extern static int getAcDbObjectId18x32(IntPtr db, out ObjectId id, [MarshalAs(UnmanagedType.U1)] bool createnew, ref Handle h, uint reserved); + + [System.Security.SuppressUnmanagedCodeSecurity] + [DllImport("acdb18.dll", CallingConvention = CallingConvention.ThisCall/*08的调用约定 高版本是__cdecl*/, + EntryPoint = "?getAcDbObjectId@AcDbDatabase@@QEAA?AW4ErrorStatus@Acad@@AEAVAcDbObjectId@@_NAEBVAcDbHandle@@K@Z")] + extern static int getAcDbObjectId18x64(IntPtr db, out ObjectId id, [MarshalAs(UnmanagedType.U1)] bool createnew, ref Handle h, uint reserved); + + /// + /// 句柄转id,NET35(08~12)专用的 + /// + /// 数据库 + /// 句柄 + /// 返回的id + /// 不存在则创建 + /// 保留,用于未来 + /// 成功0,其他值都是错误.可以强转ErrorStatus + static int GetAcDbObjectId(IntPtr db, Handle handle, out ObjectId id, bool createIfNotFound = false, uint reserved = 0) + { + id = ObjectId.Null; + switch (Application.Version.Major) + { + case 17: + { + if (IntPtr.Size == 4) + return getAcDbObjectId17x32(db, out id, createIfNotFound, ref handle, reserved); + else + return getAcDbObjectId17x64(db, out id, createIfNotFound, ref handle, reserved); + } + case 18: + { + if (IntPtr.Size == 4) + return getAcDbObjectId18x32(db, out id, createIfNotFound, ref handle, reserved); + else + return getAcDbObjectId18x64(db, out id, createIfNotFound, ref handle, reserved); + } + } + return -1; + } + + /// + /// 句柄转id + /// + /// 数据库 + /// 句柄 + /// id + public static ObjectId TryGetObjectId(Database db, Handle handle) + { +#if !NET35 + //高版本直接利用 + var es = db.TryGetObjectId(handle, out ObjectId id); + //if (!es) +#else + var es = GetAcDbObjectId(db.UnmanagedObject, handle, out ObjectId id); + //if (ErrorStatus.OK != (ErrorStatus)es) +#endif + return id; + } + + //public static int GetCadFileVersion(string filename) + //{ + // var bytes = File.ReadAllBytes(filename); + // var headstr = Encoding.Default.GetString(bytes)[0..6]; + // if (!headstr.StartsWith("AC")) return 0; + // var vernum = int.Parse(headstr.Replace("AC", "")); + // var a = Enum.Parse(typeof(DwgVersion), "AC1800"); + // Enum.TryParse() + // return vernum + 986; + + //} +} \ No newline at end of file diff --git a/src/IFoxCAD.Cad/SelectionFilter/OpComp.cs b/src/IFoxCAD.Cad/SelectionFilter/OpComp.cs index 567be46..9727b72 100644 --- a/src/IFoxCAD.Cad/SelectionFilter/OpComp.cs +++ b/src/IFoxCAD.Cad/SelectionFilter/OpComp.cs @@ -1,83 +1,79 @@ -using Autodesk.AutoCAD.DatabaseServices; -using System.Collections.Generic; +namespace IFoxCAD.Cad; -namespace IFoxCAD.Cad +/// +/// 比较运算符类 +/// +public class OpComp : OpEqual { /// - /// 比较运算符类 + /// 比较运算符,如: + /// "<=" + /// 以及合并比较运算符: + /// "<=,<=,=" /// - public class OpComp : OpEqual - { - /// - /// 比较运算符,如: - /// "<=" - /// 以及合并比较运算符: - /// "<=,<=,=" - /// - public string Content { get; } + public string Content { get; } - /// - /// 符号名 - /// - public override string Name - { - get { return "Comp"; } - } + /// + /// 符号名 + /// + public override string Name + { + get { return "Comp"; } + } - /// - /// 比较运算符类构造函数 - /// - /// 运算符 - /// 数据 - public OpComp(string content, TypedValue value) - : base(value) - { - Content = content; - } + /// + /// 比较运算符类构造函数 + /// + /// 运算符 + /// 数据 + public OpComp(string content, TypedValue value) + : base(value) + { + Content = content; + } - /// - /// 比较运算符类构造函数 - /// - /// 运算符 - /// 组码 - public OpComp(string content, int code) - : base(code) - { - Content = content; - } + /// + /// 比较运算符类构造函数 + /// + /// 运算符 + /// 组码 + public OpComp(string content, int code) + : base(code) + { + Content = content; + } - /// - /// 比较运算符类构造函数 - /// - /// 运算符 - /// 组码 - /// 组码值 - public OpComp(string content, int code, object value) - : base(code, value) - { - Content = content; - } + /// + /// 比较运算符类构造函数 + /// + /// 运算符 + /// 组码 + /// 组码值 + public OpComp(string content, int code, object value) + : base(code, value) + { + Content = content; + } - /// - /// 比较运算符类构造函数 - /// - /// 运算符 - /// 组码 - /// 组码值 - public OpComp(string content, DxfCode code, object value) - : base(code, value) - { - Content = content; - } + /// + /// 比较运算符类构造函数 + /// + /// 运算符 + /// 组码 + /// 组码值 + public OpComp(string content, DxfCode code, object value) + : base(code, value) + { + Content = content; + } - /// - /// 获取过滤器数据迭代器 - /// - /// TypedValue迭代器 - public override IEnumerable GetValues() - { - yield return new TypedValue(-4, Content); - yield return Value; - } + /// + /// 获取过滤器数据迭代器 + /// + /// TypedValue迭代器 + public override IEnumerable GetValues() + { + yield return new TypedValue(-4, Content); + yield return Value; } } diff --git a/src/IFoxCAD.Cad/SelectionFilter/OpEqual.cs b/src/IFoxCAD.Cad/SelectionFilter/OpEqual.cs index 76dc738..370a0c1 100644 --- a/src/IFoxCAD.Cad/SelectionFilter/OpEqual.cs +++ b/src/IFoxCAD.Cad/SelectionFilter/OpEqual.cs @@ -1,90 +1,86 @@ -using Autodesk.AutoCAD.DatabaseServices; -using System.Collections.Generic; +namespace IFoxCAD.Cad; -namespace IFoxCAD.Cad +/// +/// 相等运算符类 +/// +public class OpEqual : OpFilter { /// - /// 相等运算符类 + /// 组码与匹配值的TypedValue类型值 /// - public class OpEqual : OpFilter - { - /// - /// 组码与匹配值的TypedValue类型值 - /// - public TypedValue Value { get; private set; } + public TypedValue Value { get; private set; } - /// - /// 符号名 - /// - public override string Name - { - get { return "Equal"; } - } + /// + /// 符号名 + /// + public override string Name + { + get { return "Equal"; } + } - /// - /// 相等运算符类构造函数 - /// - /// 组码 - public OpEqual(int code) - { - Value = new TypedValue(code); - } + /// + /// 相等运算符类构造函数 + /// + /// 组码 + public OpEqual(int code) + { + Value = new TypedValue(code); + } - /// - /// 相等运算符类构造函数 - /// - /// 组码 - /// 组码值 - public OpEqual(int code, object value) - { - Value = new TypedValue(code, value); - } + /// + /// 相等运算符类构造函数 + /// + /// 组码 + /// 组码值 + public OpEqual(int code, object value) + { + Value = new TypedValue(code, value); + } - /// - /// 相等运算符类构造函数 - /// - /// 组码 - /// 组码值 - public OpEqual(DxfCode code, object value) - { - Value = new TypedValue((int)code, value); - } + /// + /// 相等运算符类构造函数 + /// + /// 组码 + /// 组码值 + public OpEqual(DxfCode code, object value) + { + Value = new TypedValue((int)code, value); + } - /// - /// 相等运算符类构造函数 - /// - /// 组码与组码值的TypedValue类型值 - internal OpEqual(TypedValue value) - { - Value = value; - } + /// + /// 相等运算符类构造函数 + /// + /// 组码与组码值的TypedValue类型值 + internal OpEqual(TypedValue value) + { + Value = value; + } - /// - /// 过滤器数据迭代器 - /// - /// TypedValue迭代器 - public override IEnumerable GetValues() - { - yield return Value; - } + /// + /// 过滤器数据迭代器 + /// + /// TypedValue迭代器 + public override IEnumerable GetValues() + { + yield return Value; + } - /// - /// 设置数据 - /// - /// 组码值 - public void SetValue(object value) - { - Value = new TypedValue(Value.TypeCode, value); - } + /// + /// 设置数据 + /// + /// 组码值 + public void SetValue(object value) + { + Value = new TypedValue(Value.TypeCode, value); + } - /// - /// 设置数据 - /// - /// 组码 - /// 组码值 - public void SetValue(int code, object value) - { - Value = new TypedValue(code, value); - } + /// + /// 设置数据 + /// + /// 组码 + /// 组码值 + public void SetValue(int code, object value) + { + Value = new TypedValue(code, value); } } diff --git a/src/IFoxCAD.Cad/SelectionFilter/OpFilter.cs b/src/IFoxCAD.Cad/SelectionFilter/OpFilter.cs index cd73129..a698aeb 100644 --- a/src/IFoxCAD.Cad/SelectionFilter/OpFilter.cs +++ b/src/IFoxCAD.Cad/SelectionFilter/OpFilter.cs @@ -1,342 +1,346 @@ -using Autodesk.AutoCAD.DatabaseServices; -using Autodesk.AutoCAD.EditorInput; -using Autodesk.AutoCAD.Geometry; -using System; -using System.Collections.Generic; -using System.Linq; - -namespace IFoxCAD.Cad +namespace IFoxCAD.Cad; + +/// +/// 选择集过滤器抽象类 +/// +public abstract class OpFilter { /// - /// 选择集过滤器抽象类 + /// 过滤器的名字 + /// + public abstract string Name { get; } + + /// + /// 获取TypedValue类型的值的迭代器的抽象方法,子类必须重写 + /// + /// TypedValue迭代器 + public abstract IEnumerable GetValues(); + + /// + /// 非操作符,返回的是OpFilter类型变量的 属性 + /// + /// OpFilter类型变量 + /// OpFilter对象 + public static OpFilter operator !(OpFilter item) + { + return item.Not; + } + + /// + /// 只读属性,表示这个过滤器取反 + /// + public OpFilter Not + { + get { return new OpNot(this); } + } + + /// + /// 过滤器值转换为 TypedValue 类型数组 + /// + /// TypedValue数组 + public TypedValue[] ToArray() + { + return GetValues().ToArray(); + } + + /// + /// 隐式类型转换,将自定义的过滤器转换为 Autocad 认识的选择集过滤器 + /// + /// 过滤器对象 + /// + /// 选择集过滤器. + /// + public static implicit operator SelectionFilter(OpFilter item) + { + return new SelectionFilter(item.ToArray()); + } + + /// + /// 转换为字符串 + /// + /// 字符串 + public override string ToString() + { + string s = ""; + foreach (var value in GetValues()) + s += value.ToString(); + return s; + } + + /// + /// 构建过滤器 + /// + /// + /// 举两个利用构建函数创建选择集过滤的例子 + /// + /// !(e.Dxf(0) == "line" & e.Dxf(8) == "0") + /// | e.Dxf(0) != "circle" & e.Dxf(8) == "2" & e.Dxf(10) >= p); + /// + /// 例子2: + /// var f2 = OpFilter.Bulid( + /// e => e.Or( + /// !e.And(e.Dxf(0) == "line", e.Dxf(8) == "0"), + /// e.And(e.Dxf(0) != "circle", e.Dxf(8) == "2", + /// e.Dxf(10) <= new Point3d(10, 10, 0)))); + /// ]]> + /// + /// 构建过滤器的函数委托 + /// 过滤器 + public static OpFilter Bulid(Func func) + { + return func(new Op()).Filter!; + } + + #region Operator + + /// + /// 过滤器操作符类 /// - public abstract class OpFilter + public class Op { /// - /// 过滤器的名字 + /// 过滤器属性 /// - public abstract string Name { get; } + internal OpFilter? Filter { get; private set; } + + internal Op() + { + } + + private Op(OpFilter filter) + { + Filter = filter; + } /// - /// 获取TypedValue类型的值的迭代器的抽象方法,子类必须重写 + /// AND 操作符 /// - /// TypedValue迭代器 - public abstract IEnumerable GetValues(); + /// 操作符类型的可变参数 + /// Op对象 +#pragma warning disable CA1822 // 将成员标记为 static + public Op And(params Op[] args) +#pragma warning restore CA1822 // 将成员标记为 static + { + var filter = new OpAnd(); + foreach (var op in args) + filter.Add(op.Filter!); + return new Op(filter); + } /// - /// 非操作符,返回的是OpFilter类型变量的 属性 + /// or 操作符 /// - /// OpFilter类型变量 - /// OpFilter对象 - public static OpFilter operator !(OpFilter item) + /// 操作符类型的可变参数 + /// Op对象 +#pragma warning disable CA1822 // 将成员标记为 static + public Op Or(params Op[] args) +#pragma warning restore CA1822 // 将成员标记为 static { - return item.Not; + var filter = new OpOr(); + foreach (var op in args) + filter.Add(op.Filter!); + return new Op(filter); } /// - /// 只读属性,表示这个过滤器取反 + /// dxf 操作符,此函数只能用于过滤器中,不是组码操作函数 /// - public OpFilter Not + /// 组码 + /// Op对象 +#pragma warning disable CA1822 // 将成员标记为 static + public Op Dxf(int code) +#pragma warning restore CA1822 // 将成员标记为 static { - get { return new OpNot(this); } + return new Op(new OpEqual(code)); } /// - /// 过滤器值转换为 TypedValue 类型数组 + /// dxf 操作符,此函数只能用于过滤器中,不是组码操作函数 /// - /// TypedValue数组 - public TypedValue[] ToArray() + /// 组码 + /// 关系运算符的值,比如">,>,=" + /// Op对象 +#pragma warning disable CA1822 // 将成员标记为 static + public Op Dxf(int code, string content) +#pragma warning restore CA1822 // 将成员标记为 static { - return GetValues().ToArray(); + return new Op(new OpComp(content, code)); } /// - /// 隐式类型转换,将自定义的过滤器转换为 Autocad 认识的选择集过滤器 + /// 非操作符 /// - /// 过滤器对象 - /// - /// 选择集过滤器. - /// - public static implicit operator SelectionFilter(OpFilter item) + /// 过滤器操作符对象 + /// Op对象 + public static Op operator !(Op right) { - return new SelectionFilter(item.ToArray()); + right.Filter = !right.Filter!; + return right; } /// - /// 转换为字符串 + /// 相等操作符 /// - /// 字符串 - public override string ToString() + /// 过滤器操作符对象 + /// 数据 + /// Op对象 + public static Op operator ==(Op left, object right) { - string s = ""; - foreach (var value in GetValues()) - s += value.ToString(); - return s; + var eq = (OpEqual)left.Filter!; + eq.SetValue(right); + return left; } /// - /// 构建过滤器 + /// 不等操作符 /// - /// - /// 举两个利用构建函数创建选择集过滤的例子 - /// - /// !(e.Dxf(0) == "line" & e.Dxf(8) == "0") - /// | e.Dxf(0) != "circle" & e.Dxf(8) == "2" & e.Dxf(10) >= p); - /// - /// 例子2: - /// var f2 = OpFilter.Bulid( - /// e => e.Or( - /// !e.And(e.Dxf(0) == "line", e.Dxf(8) == "0"), - /// e.And(e.Dxf(0) != "circle", e.Dxf(8) == "2", - /// e.Dxf(10) <= new Point3d(10, 10, 0)))); - /// ]]> - /// - /// 构建过滤器的函数委托 - /// 过滤器 - public static OpFilter Bulid(Func func) + /// 过滤器操作符对象 + /// 数据 + /// Op对象 + public static Op operator !=(Op left, object right) { - return func(new OpFilter.Op()).Filter; + var eq = (OpEqual)left.Filter!; + eq.SetValue(right); + left.Filter = eq.Not; + return left; } - #region Operator + private static Op GetCompOp(string content, Op left, object right) + { + var eq = (OpEqual)left.Filter!; + var comp = new OpComp(content, eq.Value.TypeCode, right); + return new Op(comp); + } /// - /// 过滤器操作符类 + /// 大于操作符 /// - public class Op + /// 过滤器操作符对象 + /// 数据 + /// Op对象 + public static Op operator >(Op left, object right) { - /// - /// 过滤器属性 - /// - internal OpFilter Filter { get; private set; } + return GetCompOp(">", left, right); + } - internal Op() - { - } + /// + /// 小于操作符 + /// + /// 过滤器操作符对象 + /// 数据 + /// Op对象 + public static Op operator <(Op left, object right) + { + return GetCompOp("<", left, right); + } - private Op(OpFilter filter) - { - Filter = filter; - } - - /// - /// AND 操作符 - /// - /// 操作符类型的可变参数 - /// Op对象 - public Op And(params Op[] args) - { - var filter = new OpAnd(); - foreach (var op in args) - filter.Add(op.Filter); - return new Op(filter); - } - - /// - /// or 操作符 - /// - /// 操作符类型的可变参数 - /// Op对象 - public Op Or(params Op[] args) - { - var filter = new OpOr(); - foreach (var op in args) - filter.Add(op.Filter); - return new Op(filter); - } - - /// - /// dxf 操作符,此函数只能用于过滤器中,不是组码操作函数 - /// - /// 组码 - /// Op对象 - public Op Dxf(int code) - { - return new Op(new OpEqual(code)); - } - - /// - /// dxf 操作符,此函数只能用于过滤器中,不是组码操作函数 - /// - /// 组码 - /// 关系运算符的值,比如">,>,=" - /// Op对象 - public Op Dxf(int code, string content) - { - return new Op(new OpComp(content, code)); - } - - /// - /// 非操作符 - /// - /// 过滤器操作符对象 - /// Op对象 - public static Op operator !(Op right) - { - right.Filter = !right.Filter; - return right; - } - - /// - /// 相等操作符 - /// - /// 过滤器操作符对象 - /// 数据 - /// Op对象 - public static Op operator ==(Op left, object right) - { - var eq = (OpEqual)left.Filter; - eq.SetValue(right); - return left; - } - - /// - /// 不等操作符 - /// - /// 过滤器操作符对象 - /// 数据 - /// Op对象 - public static Op operator !=(Op left, object right) - { - var eq = (OpEqual)left.Filter; - eq.SetValue(right); - left.Filter = eq.Not; - return left; - } + /// + /// 大于等于操作符 + /// + /// 过滤器操作符对象 + /// 数据 + /// Op对象 + public static Op operator >=(Op left, object right) + { + return GetCompOp(">=", left, right); + } - private static Op GetCompOp(string content, Op left, object right) - { - var eq = (OpEqual)left.Filter; - var comp = new OpComp(content, eq.Value.TypeCode, right); - return new Op(comp); - } - - /// - /// 大于操作符 - /// - /// 过滤器操作符对象 - /// 数据 - /// Op对象 - public static Op operator >(Op left, object right) - { - return GetCompOp(">", left, right); - } - - /// - /// 小于操作符 - /// - /// 过滤器操作符对象 - /// 数据 - /// Op对象 - public static Op operator <(Op left, object right) - { - return GetCompOp("<", left, right); - } - - /// - /// 大于等于操作符 - /// - /// 过滤器操作符对象 - /// 数据 - /// Op对象 - public static Op operator >=(Op left, object right) - { - return GetCompOp(">=", left, right); - } - - /// - /// 小于等于操作符 - /// - /// 过滤器操作符对象 - /// 数据 - /// Op对象 - public static Op operator <=(Op left, object right) - { - return GetCompOp("<=", left, right); - } - - /// - /// 大于等于操作符 - /// - /// 过滤器操作符对象 - /// 点 - /// Op对象 - public static Op operator >=(Op left, Point3d right) - { - return GetCompOp(">,>,*", left, right); - } - - /// - /// 小于等于操作符 - /// - /// 过滤器操作符对象 - /// 点 - /// Op对象 - public static Op operator <=(Op left, Point3d right) - { - return GetCompOp("<,<,*", left, right); - } - - /// - /// 并操作符 - /// - /// 过滤器操作符对象 - /// 过滤器操作符对象 - /// Op对象 - public static Op operator &(Op left, Op right) - { - var filter = new OpAnd(); - filter.Add(left.Filter); - filter.Add(right.Filter); - return new Op(filter); - } - - /// - /// 或操作符 - /// - /// 过滤器操作符对象 - /// 过滤器操作符对象 - /// Op对象 - public static Op operator |(Op left, Op right) + /// + /// 小于等于操作符 + /// + /// 过滤器操作符对象 + /// 数据 + /// Op对象 + public static Op operator <=(Op left, object right) + { + return GetCompOp("<=", left, right); + } + + /// + /// 大于等于操作符 + /// + /// 过滤器操作符对象 + /// 点 + /// Op对象 + public static Op operator >=(Op left, Point3d right) + { + return GetCompOp(">,>,*", left, right); + } + + /// + /// 小于等于操作符 + /// + /// 过滤器操作符对象 + /// 点 + /// Op对象 + public static Op operator <=(Op left, Point3d right) + { + return GetCompOp("<,<,*", left, right); + } + + /// + /// 并操作符 + /// + /// 过滤器操作符对象 + /// 过滤器操作符对象 + /// Op对象 + public static Op operator &(Op left, Op right) + { + var filter = new OpAnd { - var filter = new OpOr(); - filter.Add(left.Filter); - filter.Add(right.Filter); - return new Op(filter); - } - - /// - /// 异或操作符 - /// - /// 过滤器操作符对象 - /// 过滤器操作符对象 - /// Op对象 - public static Op operator ^(Op left, Op right) + left.Filter!, + right.Filter! + }; + return new Op(filter); + } + + /// + /// 或操作符 + /// + /// 过滤器操作符对象 + /// 过滤器操作符对象 + /// Op对象 + public static Op operator |(Op left, Op right) + { + var filter = new OpOr { - var filter = new OpXor(left.Filter, right.Filter); - return new Op(filter); - } - - /// - /// 比较函数 - /// - /// 对象 - /// - /// 是否相等 - /// - public override bool Equals(object obj) => base.Equals(obj); - - /// - /// 获取HashCode - /// - /// HashCode - public override int GetHashCode() => base.GetHashCode(); + left.Filter!, + right.Filter! + }; + return new Op(filter); } - #endregion Operator + /// + /// 异或操作符 + /// + /// 过滤器操作符对象 + /// 过滤器操作符对象 + /// Op对象 + public static Op operator ^(Op left, Op right) + { + var filter = new OpXor(left.Filter!, right.Filter!); + return new Op(filter); + } + + /// + /// 比较函数 + /// + /// 对象 + /// + /// 是否相等 + /// + public override bool Equals(object obj) => base.Equals(obj); + + /// + /// 获取HashCode + /// + /// HashCode + public override int GetHashCode() => base.GetHashCode(); } + + #endregion Operator } diff --git a/src/IFoxCAD.Cad/SelectionFilter/OpList.cs b/src/IFoxCAD.Cad/SelectionFilter/OpList.cs index 06a5408..59d1d1f 100644 --- a/src/IFoxCAD.Cad/SelectionFilter/OpList.cs +++ b/src/IFoxCAD.Cad/SelectionFilter/OpList.cs @@ -1,170 +1,166 @@ -using Autodesk.AutoCAD.DatabaseServices; -using System.Collections.Generic; +namespace IFoxCAD.Cad; -namespace IFoxCAD.Cad +/// +/// 逻辑操作符的列表抽象类 +/// +public abstract class OpList : OpLogi { /// - /// 逻辑操作符的列表抽象类 + /// 过滤器列表 /// - public abstract class OpList : OpLogi + protected List _lst = new(); + + /// + /// 添加过滤器条件的虚函数,子类可以重写 + /// + /// 举个利用这个类及其子类创建选择集过滤的例子 + /// + /// ,>,*" } + /// }, + /// }; + /// ]]> + /// + /// 过滤器对象 + public virtual void Add(OpFilter value) { - /// - /// 过滤器列表 - /// - protected List _lst = new(); + _lst.Add(value); + } - /// - /// 添加过滤器条件的虚函数,子类可以重写 - /// - /// 举个利用这个类及其子类创建选择集过滤的例子 - /// - /// ,>,*" } - /// }, - /// }; - /// ]]> - /// - /// 过滤器对象 - public virtual void Add(OpFilter value) - { - _lst.Add(value); - } + /// + /// 添加过滤条件 + /// + /// 逻辑非~ + /// 组码 + /// 组码值 + public void Add(string speccode, int code, object value) + { + if (speccode == "~") + _lst.Add(new OpEqual(code, value).Not); + } - /// - /// 添加过滤条件 - /// - /// 逻辑非~ - /// 组码 - /// 组码值 - public void Add(string speccode, int code, object value) - { - if (speccode == "~") - _lst.Add(new OpEqual(code, value).Not); - } + /// + /// 添加过滤条件 + /// + /// 组码 + /// 组码值 + public void Add(int code, object value) + { + _lst.Add(new OpEqual(code, value)); + } - /// - /// 添加过滤条件 - /// - /// 组码 - /// 组码值 - public void Add(int code, object value) - { - _lst.Add(new OpEqual(code, value)); - } + /// + /// 添加过滤条件 + /// + /// 组码 + /// 组码值 + public void Add(DxfCode code, object value) + { + _lst.Add(new OpEqual(code, value)); + } - /// - /// 添加过滤条件 - /// - /// 组码 - /// 组码值 - public void Add(DxfCode code, object value) - { - _lst.Add(new OpEqual(code, value)); - } + /// + /// 添加过滤条件 + /// + /// 组码 + /// 组码值 + /// 比较运算符 + public void Add(int code, object value, string comp) + { + _lst.Add(new OpComp(comp, code, value)); + } - /// - /// 添加过滤条件 - /// - /// 组码 - /// 组码值 - /// 比较运算符 - public void Add(int code, object value, string comp) - { - _lst.Add(new OpComp(comp, code, value)); - } + /// + /// 添加过滤条件 + /// + /// 组码 + /// 组码值 + /// 比较运算符 + public void Add(DxfCode code, object value, string comp) + { + _lst.Add(new OpComp(comp, code, value)); + } - /// - /// 添加过滤条件 - /// - /// 组码 - /// 组码值 - /// 比较运算符 - public void Add(DxfCode code, object value, string comp) - { - _lst.Add(new OpComp(comp, code, value)); - } + /// + /// 过滤器迭代器 + /// + /// OpFilter迭代器 + public override IEnumerator GetEnumerator() + { + foreach (var value in _lst) + yield return value; + } +} - /// - /// 过滤器迭代器 - /// - /// OpFilter迭代器 - public override IEnumerator GetEnumerator() - { - foreach (var value in _lst) - yield return value; - } +/// +/// 逻辑与类 +/// +public class OpAnd : OpList +{ + /// + /// 符号名 + /// + public override string Name + { + get { return "And"; } } /// - /// 逻辑与类 + /// 添加过滤条件 /// - public class OpAnd : OpList + /// 过滤器对象 + public override void Add(OpFilter value) { - /// - /// 符号名 - /// - public override string Name + if (value is OpAnd opand) { - get { return "And"; } + foreach (var item in opand) + _lst.Add(item); } - - /// - /// 添加过滤条件 - /// - /// 过滤器对象 - public override void Add(OpFilter value) + else { - if (value is OpAnd opand) - { - foreach (var item in opand) - _lst.Add(item); - } - else - { - _lst.Add(value); - } + _lst.Add(value); } } +} + +/// +/// 逻辑或类 +/// +public class OpOr : OpList +{ + /// + /// 符号名 + /// + public override string Name + { + get { return "Or"; } + } /// - /// 逻辑或类 + /// 添加过滤条件 /// - public class OpOr : OpList + /// 过滤器对象 + public override void Add(OpFilter value) { - /// - /// 符号名 - /// - public override string Name + if (value is OpOr opor) { - get { return "Or"; } + foreach (var item in opor) + _lst.Add(item); } - - /// - /// 添加过滤条件 - /// - /// 过滤器对象 - public override void Add(OpFilter value) + else { - if (value is OpOr opor) - { - foreach (var item in opor) - _lst.Add(item); - } - else - { - _lst.Add(value); - } + _lst.Add(value); } } } \ No newline at end of file diff --git a/src/IFoxCAD.Cad/SelectionFilter/OpLogi.cs b/src/IFoxCAD.Cad/SelectionFilter/OpLogi.cs index 3fa0785..b61583d 100644 --- a/src/IFoxCAD.Cad/SelectionFilter/OpLogi.cs +++ b/src/IFoxCAD.Cad/SelectionFilter/OpLogi.cs @@ -1,133 +1,128 @@ -using Autodesk.AutoCAD.DatabaseServices; -using System.Collections; -using System.Collections.Generic; +namespace IFoxCAD.Cad; -namespace IFoxCAD.Cad +/// +/// 过滤器逻辑运算符抽象类 +/// +public abstract class OpLogi : OpFilter, IEnumerable { /// - /// 过滤器逻辑运算符抽象类 + /// 返回-4组码的开始内容 /// - public abstract class OpLogi : OpFilter, IEnumerable + public TypedValue First { - /// - /// 返回-4组码的开始内容 - /// - public TypedValue First - { - get { return new TypedValue(-4, $"<{Name}"); } - } - - /// - /// 返回-4组码的结束内容 - /// - public TypedValue Last - { - get { return new TypedValue(-4, $"{Name}>"); } - } - - /// - /// 获取过滤条件 - /// - /// TypedValue迭代器 - public override IEnumerable GetValues() - { - yield return First; - foreach (var item in this) - { - foreach (var value in item.GetValues()) - yield return value; - } - yield return Last; - } + get { return new TypedValue(-4, $"<{Name}"); } + } - /// - /// 获取迭代器 - /// - /// OpFilter迭代器 - public abstract IEnumerator GetEnumerator(); + /// + /// 返回-4组码的结束内容 + /// + public TypedValue Last + { + get { return new TypedValue(-4, $"{Name}>"); } + } - IEnumerator IEnumerable.GetEnumerator() + /// + /// 获取过滤条件 + /// + /// TypedValue迭代器 + public override IEnumerable GetValues() + { + yield return First; + foreach (var item in this) { - return GetEnumerator(); + foreach (var value in item.GetValues()) + yield return value; } + yield return Last; } /// - /// 逻辑非类 + /// 获取迭代器 /// - public class OpNot : OpLogi + /// OpFilter迭代器 + public abstract IEnumerator GetEnumerator(); + + IEnumerator IEnumerable.GetEnumerator() { - private OpFilter Value { get; } + return GetEnumerator(); + } +} - /// - /// 逻辑非类构造函数 - /// - /// OpFilter数据 - public OpNot(OpFilter value) - { - Value = value; - } +/// +/// 逻辑非类 +/// +public class OpNot : OpLogi +{ + private OpFilter Value { get; } - /// - /// 符号名 - /// - public override string Name - { - get { return "Not"; } - } + /// + /// 逻辑非类构造函数 + /// + /// OpFilter数据 + public OpNot(OpFilter value) + { + Value = value; + } - /// - /// 获取迭代器 - /// - /// OpFilter迭代器 - public override IEnumerator GetEnumerator() - { - yield return Value; - } + /// + /// 符号名 + /// + public override string Name + { + get { return "Not"; } } /// - /// 逻辑异或类 + /// 获取迭代器 /// - public class OpXor : OpLogi + /// OpFilter迭代器 + public override IEnumerator GetEnumerator() { - /// - /// 左操作数 - /// - public OpFilter Left { get; } + yield return Value; + } +} - /// - /// 右操作数 - /// - public OpFilter Right { get; } +/// +/// 逻辑异或类 +/// +public class OpXor : OpLogi +{ + /// + /// 左操作数 + /// + public OpFilter Left { get; } - /// - /// 逻辑异或类构造函数 - /// - /// 左操作数 - /// 右操作数 - public OpXor(OpFilter left, OpFilter right) - { - Left = left; - Right = right; - } + /// + /// 右操作数 + /// + public OpFilter Right { get; } - /// - /// 符号名 - /// - public override string Name - { - get { return "Xor"; } - } + /// + /// 逻辑异或类构造函数 + /// + /// 左操作数 + /// 右操作数 + public OpXor(OpFilter left, OpFilter right) + { + Left = left; + Right = right; + } - /// - /// 获取迭代器 - /// - /// 选择集过滤器类型迭代器 - public override IEnumerator GetEnumerator() - { - yield return Left; - yield return Right; - } + /// + /// 符号名 + /// + public override string Name + { + get { return "Xor"; } + } + + /// + /// 获取迭代器 + /// + /// 选择集过滤器类型迭代器 + public override IEnumerator GetEnumerator() + { + yield return Left; + yield return Right; } } \ No newline at end of file diff --git a/src/IFoxCAD.WPF/Converter.cs b/src/IFoxCAD.WPF/Converter.cs index 7682212..828ece9 100644 --- a/src/IFoxCAD.WPF/Converter.cs +++ b/src/IFoxCAD.WPF/Converter.cs @@ -1,108 +1,98 @@ -using System; -using System.Collections.Generic; -using System.Globalization; -using System.Linq; -using System.Text; +namespace IFoxCAD.WPF; -using System.Windows.Data; - - -namespace IFoxCAD.WPF +/// +/// 字符串到整数的转换器 +/// +public class StringToIntConverter : IValueConverter +{ + /// + /// 字符串转换到整数 + /// + /// 绑定源生成的值 + /// 绑定目标属性的类型 + /// 要使用的转换器参数 + /// 要用在转换器中的区域性 + /// 转换后的值。 如果该方法返回 null,则使用有效的 null 值。 + public object Convert(object value, Type targetType, object parameter, CultureInfo culture) + { + string? a = value as string; + _ = int.TryParse(a, out int b); + return b; + } + /// + /// 整数转换到字符串 + /// + /// 绑定目标生成的值 + /// 要转换为的类型 + /// 要使用的转换器参数 + /// 要用在转换器中的区域性 + /// 转换后的值。 如果该方法返回 null,则使用有效的 null 值。 + public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture) + { + return value.ToString(); + } +} +/// +/// 字符串到实数的转换器 +/// +public class StringToDoubleConverter : IValueConverter { /// - /// 字符串到整数的转换器 + /// 字符串转换到实数 /// - public class StringToIntConverter : IValueConverter + /// 绑定源生成的值 + /// 绑定目标属性的类型 + /// 要使用的转换器参数 + /// 要用在转换器中的区域性 + /// 转换后的值。 如果该方法返回 null,则使用有效的 null 值。 + public object Convert(object value, Type targetType, object parameter, CultureInfo culture) { - /// - /// 字符串转换到整数 - /// - /// 绑定源生成的值 - /// 绑定目标属性的类型 - /// 要使用的转换器参数 - /// 要用在转换器中的区域性 - /// 转换后的值。 如果该方法返回 null,则使用有效的 null 值。 - public object Convert(object value, Type targetType, object parameter, CultureInfo culture) - { - string a = value as string; - _ = int.TryParse(a, out int b); - return b; - } - /// - /// 整数转换到字符串 - /// - /// 绑定目标生成的值 - /// 要转换为的类型 - /// 要使用的转换器参数 - /// 要用在转换器中的区域性 - /// 转换后的值。 如果该方法返回 null,则使用有效的 null 值。 - public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture) - { - return value.ToString(); - } + string? a = value as string; + _ = double.TryParse(a, out double b); + return b; } /// - /// 字符串到实数的转换器 + /// 实数转换到字符串 + /// + /// 绑定目标生成的值 + /// 要转换为的类型 + /// 要使用的转换器参数 + /// 要用在转换器中的区域性 + /// 转换后的值。 如果该方法返回 null,则使用有效的 null 值。 + public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture) + { + return value.ToString(); + } +} +/// +/// 整数到字符串的转换器 +/// +public class IntToStringConverter : IValueConverter +{ + /// + /// 整数转换到字符串 /// - public class StringToDoubleConverter : IValueConverter + /// 绑定源生成的值 + /// 绑定目标属性的类型 + /// 要使用的转换器参数 + /// 要用在转换器中的区域性 + /// 转换后的值。 如果该方法返回 null,则使用有效的 null 值。 + public object Convert(object value, Type targetType, object parameter, CultureInfo culture) { - /// - /// 字符串转换到实数 - /// - /// 绑定源生成的值 - /// 绑定目标属性的类型 - /// 要使用的转换器参数 - /// 要用在转换器中的区域性 - /// 转换后的值。 如果该方法返回 null,则使用有效的 null 值。 - public object Convert(object value, Type targetType, object parameter, CultureInfo culture) - { - string a = value as string; - _ = double.TryParse(a, out double b); - return b; - } - /// - /// 实数转换到字符串 - /// - /// 绑定目标生成的值 - /// 要转换为的类型 - /// 要使用的转换器参数 - /// 要用在转换器中的区域性 - /// 转换后的值。 如果该方法返回 null,则使用有效的 null 值。 - public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture) - { - return value.ToString(); - } + return value.ToString(); } /// - /// 整数到字符串的转换器 + /// 字符串转换到整数 /// - public class IntToStringConverter : IValueConverter + /// 绑定目标生成的值 + /// 要转换为的类型 + /// 要使用的转换器参数 + /// 要用在转换器中的区域性 + /// 转换后的值。 如果该方法返回 null,则使用有效的 null 值。 + public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture) { - /// - /// 整数转换到字符串 - /// - /// 绑定源生成的值 - /// 绑定目标属性的类型 - /// 要使用的转换器参数 - /// 要用在转换器中的区域性 - /// 转换后的值。 如果该方法返回 null,则使用有效的 null 值。 - public object Convert(object value, Type targetType, object parameter, CultureInfo culture) - { - return value.ToString(); - } - /// - /// 字符串转换到整数 - /// - /// 绑定目标生成的值 - /// 要转换为的类型 - /// 要使用的转换器参数 - /// 要用在转换器中的区域性 - /// 转换后的值。 如果该方法返回 null,则使用有效的 null 值。 - public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture) - { - string a = value as string; - _ = int.TryParse(a, out int b); - return b; - } + string? a = value as string; + _ = int.TryParse(a, out int b); + return b; } } diff --git a/src/IFoxCAD.WPF/DependencyObjectExtensions.cs b/src/IFoxCAD.WPF/DependencyObjectExtensions.cs index 39cd184..596961d 100644 --- a/src/IFoxCAD.WPF/DependencyObjectExtensions.cs +++ b/src/IFoxCAD.WPF/DependencyObjectExtensions.cs @@ -1,40 +1,34 @@ -using System.Windows; -using System.Windows.Media; +namespace IFoxCAD.WPF; -namespace IFoxCAD.WPF +/// +/// 依赖属性扩展类 +/// +public static class DependencyObjectExtensions { /// - /// 依赖属性扩展类 + /// 获取父对象依赖属性 /// - public static class DependencyObjectExtensions + /// 子对象 + /// 依赖属性 + public static DependencyObject? GetParentObject(this DependencyObject child) { - /// - /// 获取父对象依赖属性 - /// - /// 子对象 - /// 依赖属性 - public static DependencyObject? GetParentObject(this DependencyObject child) - { - if (child is null) - return null; - - if (child is ContentElement contentElement) - { - var parent = ContentOperations.GetParent(contentElement); - if (parent is not null) return parent; + if (child is null) return null; - var fce = contentElement as FrameworkContentElement; - return fce?.Parent; - } + if (child is ContentElement contentElement) + { + DependencyObject parent = ContentOperations.GetParent(contentElement); + if (parent is not null) return parent; - if (child is FrameworkElement frameworkElement) - { - var parent = frameworkElement.Parent; - if (parent is not null) - return parent; - } + FrameworkContentElement? fce = contentElement as FrameworkContentElement; + return fce?.Parent; + } - return VisualTreeHelper.GetParent(child); + if (child is FrameworkElement frameworkElement) + { + DependencyObject parent = frameworkElement.Parent; + if (parent is not null) return parent; } + + return VisualTreeHelper.GetParent(child); } } diff --git a/src/IFoxCAD.WPF/EnumSelection.cs b/src/IFoxCAD.WPF/EnumSelection.cs new file mode 100644 index 0000000..d964351 --- /dev/null +++ b/src/IFoxCAD.WPF/EnumSelection.cs @@ -0,0 +1,72 @@ +namespace IFoxCAD.WPF; +public class EnumSelection : INotifyPropertyChanged where T : struct, IComparable, IFormattable, IConvertible +{ + private T value; // stored value of the Enum + private readonly bool isFlagged; // Enum uses flags? + private readonly bool canDeselect; // Can be deselected? (Radio buttons cannot deselect, checkboxes can) + private readonly T blankValue; // what is considered the "blank" value if it can be deselected? + + public EnumSelection(T value) : this(value, false, default) { } + public EnumSelection(T value, bool canDeselect) : this(value, canDeselect, default) { } + public EnumSelection(T value, T blankValue) : this(value, true, blankValue) { } + public EnumSelection(T value, bool canDeselect, T blankValue) + { + if (!typeof(T).IsEnum) throw new ArgumentException($"{nameof(T)} must be an enum type"); // I really wish there was a way to constrain generic types to enums... + isFlagged = typeof(T).IsDefined(typeof(FlagsAttribute), false); + + this.value = value; + this.canDeselect = canDeselect; + this.blankValue = blankValue; + } + + public T Value + { + get { return value; } + set + { + if (this.value.Equals(value)) return; + this.value = value; + OnPropertyChanged(); + OnPropertyChanged("Item[]"); // Notify that the indexer property has changed + } + } + + [IndexerName("Item")] + public bool this[T key] + { + get + { + int iKey = (int)(object)key; + return isFlagged ? ((int)(object)value & iKey) == iKey : value.Equals(key); + } + set + { + if (isFlagged) + { + int iValue = (int)(object)this.value; + int iKey = (int)(object)key; + + if (((iValue & iKey) == iKey) == value) return; + + if (value) + Value = (T)(object)(iValue | iKey); + else + Value = (T)(object)(iValue & ~iKey); + } + else + { + if (this.value.Equals(key) == value) return; + if (!value && !canDeselect) return; + + Value = value ? key : blankValue; + } + } + } + + public event PropertyChangedEventHandler? PropertyChanged; + + private void OnPropertyChanged([CallerMemberName] string propertyName = "") + { + PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName)); + } +} \ No newline at end of file diff --git a/src/IFoxCAD.WPF/EventBindingExtension.cs b/src/IFoxCAD.WPF/EventBindingExtension.cs index 9298c07..eb58c75 100644 --- a/src/IFoxCAD.WPF/EventBindingExtension.cs +++ b/src/IFoxCAD.WPF/EventBindingExtension.cs @@ -1,221 +1,210 @@ -using System; -using System.ComponentModel; -using System.Reflection.Emit; -using System.Windows.Input; -using System.Windows.Markup; -using System.Windows; -using System.Reflection; +namespace IFoxCAD.WPF; - -namespace IFoxCAD.WPF +/// +/// 事件绑定标签类 +/// +/// +public class EventBindingExtension : MarkupExtension { /// - /// 事件绑定标签类 + /// 命令属性 + /// + public string? Command { get; set; } + /// + /// 命令参数属性 + /// + public string? CommandParameter { get; set; } + /// + /// 当在派生类中实现时,返回用作此标记扩展的目标属性值的对象。 /// - /// - public class EventBindingExtension : MarkupExtension + /// 可为标记扩展提供服务的服务提供程序帮助程序。 + /// + /// 要在应用了扩展的属性上设置的对象值。 + /// + /// + /// + public override object? ProvideValue(IServiceProvider serviceProvider) { - /// - /// 命令属性 - /// - public string Command { get; set; } - /// - /// 命令参数属性 - /// - public string CommandParameter { get; set; } - /// - /// 当在派生类中实现时,返回用作此标记扩展的目标属性值的对象。 - /// - /// 可为标记扩展提供服务的服务提供程序帮助程序。 - /// - /// 要在应用了扩展的属性上设置的对象值。 - /// - /// - /// - public override object ProvideValue(IServiceProvider serviceProvider) + if (serviceProvider is null) { - if (serviceProvider is null) - { - throw new ArgumentNullException(nameof(serviceProvider)); - } - if (!(serviceProvider.GetService(typeof(IProvideValueTarget)) is IProvideValueTarget targetProvider)) - { - throw new InvalidOperationException(); - } - - if (!(targetProvider.TargetObject is FrameworkElement targetObject)) - { - throw new InvalidOperationException(); - } - - var memberInfo = targetProvider.TargetProperty as MemberInfo; - if (memberInfo is null) - { - throw new InvalidOperationException(); - } + throw new ArgumentNullException(nameof(serviceProvider)); + } + if (serviceProvider.GetService(typeof(IProvideValueTarget)) is not IProvideValueTarget targetProvider) + { + throw new InvalidOperationException(); + } - if (string.IsNullOrWhiteSpace(Command)) - { - Command = memberInfo.Name.Replace("Add", ""); - if (Command.Contains("Handler")) - { - Command = Command.Replace("Handler", "Command"); - } - else - { - Command += "Command"; - } - } + if (targetProvider.TargetObject is not FrameworkElement targetObject) + { + throw new InvalidOperationException(); + } - return CreateHandler(memberInfo, Command, targetObject.GetType()); + if (targetProvider.TargetProperty is not MemberInfo memberInfo) + { + throw new InvalidOperationException(); } - private Type GetEventHandlerType(MemberInfo memberInfo) + if (string.IsNullOrWhiteSpace(Command)) { - Type eventHandlerType = null; - if (memberInfo is EventInfo) + Command = memberInfo.Name.Replace("Add", ""); + if (Command.Contains("Handler")) { - var info = memberInfo as EventInfo; - var eventInfo = info; - eventHandlerType = eventInfo.EventHandlerType; + Command = Command.Replace("Handler", "Command"); } - else if (memberInfo is MethodInfo) + else { - var info = memberInfo as MethodInfo; - var methodInfo = info; - ParameterInfo[] pars = methodInfo.GetParameters(); - eventHandlerType = pars[1].ParameterType; + Command += "Command"; } - - return eventHandlerType; } - private object CreateHandler(MemberInfo memberInfo, string cmdName, Type targetType) + return CreateHandler(memberInfo, Command!, targetObject.GetType()); + } + + private Type? GetEventHandlerType(MemberInfo memberInfo) + { + Type? eventHandlerType = null; + if (memberInfo is EventInfo eventInfo) + { + //var info = memberInfo as EventInfo; + //var eventInfo = info; + eventHandlerType = eventInfo.EventHandlerType; + } + else if (memberInfo is MethodInfo methodInfo) { - Type eventHandlerType = GetEventHandlerType(memberInfo); + //var info = memberInfo as MethodInfo; + //var methodInfo = info; + ParameterInfo[] pars = methodInfo.GetParameters(); + eventHandlerType = pars[1].ParameterType; + } - if (eventHandlerType is null) return null; + return eventHandlerType; + } - var handlerInfo = eventHandlerType.GetMethod("Invoke"); - var method = new DynamicMethod("", handlerInfo.ReturnType, - new Type[] - { - handlerInfo.GetParameters()[0].ParameterType, - handlerInfo.GetParameters()[1].ParameterType, - }); +#pragma warning disable IDE0060 // 删除未使用的参数 + private object? CreateHandler(MemberInfo memberInfo, string cmdName, Type targetType) +#pragma warning restore IDE0060 // 删除未使用的参数 + { + Type? eventHandlerType = GetEventHandlerType(memberInfo); - var gen = method.GetILGenerator(); - gen.Emit(OpCodes.Ldarg, 0); - gen.Emit(OpCodes.Ldarg, 1); - gen.Emit(OpCodes.Ldstr, cmdName); - if (CommandParameter is null) - { - gen.Emit(OpCodes.Ldnull); - } - else + if (eventHandlerType is null) return null; + + var handlerInfo = eventHandlerType.GetMethod("Invoke"); + var method = new DynamicMethod("", handlerInfo.ReturnType, + new Type[] { - gen.Emit(OpCodes.Ldstr, CommandParameter); - } - gen.Emit(OpCodes.Call, getMethod); - gen.Emit(OpCodes.Ret); + handlerInfo.GetParameters()[0].ParameterType, + handlerInfo.GetParameters()[1].ParameterType, + }); - return method.CreateDelegate(eventHandlerType); + var gen = method.GetILGenerator(); + gen.Emit(OpCodes.Ldarg, 0); + gen.Emit(OpCodes.Ldarg, 1); + gen.Emit(OpCodes.Ldstr, cmdName); + if (CommandParameter is null) + { + gen.Emit(OpCodes.Ldnull); } - - static readonly MethodInfo getMethod = typeof(EventBindingExtension).GetMethod("HandlerIntern", new Type[] { typeof(object), typeof(object), typeof(string), typeof(string) }); - - static void Handler(object sender, object args) + else { - HandlerIntern(sender, args, "cmd", null); + gen.Emit(OpCodes.Ldstr, CommandParameter); } - /// - /// Handlers the intern. - /// - /// The sender. - /// The arguments. - /// Name of the command. - /// The command parameter. - public static void HandlerIntern(object sender, object args, string cmdName, string commandParameter) + gen.Emit(OpCodes.Call, getMethod); + gen.Emit(OpCodes.Ret); + + return method.CreateDelegate(eventHandlerType); + } + + static readonly MethodInfo getMethod = typeof(EventBindingExtension).GetMethod("HandlerIntern", new Type[] { typeof(object), typeof(object), typeof(string), typeof(string) }); + +#pragma warning disable IDE0051 // 删除未使用的私有成员 + static void Handler(object sender, object args) +#pragma warning restore IDE0051 // 删除未使用的私有成员 + { + HandlerIntern(sender, args, "cmd", null); + } + /// + /// Handlers the intern. + /// + /// The sender. + /// The arguments. + /// Name of the command. + /// The command parameter. + public static void HandlerIntern(object sender, object args, string cmdName, string? commandParameter) + { + if (sender is FrameworkElement fe) { - var fe = sender as FrameworkElement; - if (fe is not null) + var cmd = GetCommand(fe, cmdName); + object? commandParam = null; + if (!string.IsNullOrWhiteSpace(commandParameter)) + { + commandParam = GetCommandParameter(fe, args, commandParameter!); + } + if ((cmd is not null) && cmd.CanExecute(commandParam)) { - ICommand cmd = GetCommand(fe, cmdName); - object commandParam = null; - if (!string.IsNullOrWhiteSpace(commandParameter)) - { - commandParam = GetCommandParameter(fe, args, commandParameter); - } - if ((cmd is not null) && cmd.CanExecute(commandParam)) - { - cmd.Execute(commandParam); - } + cmd.Execute(commandParam); } } + } - internal static ICommand GetCommand(FrameworkElement target, string cmdName) - { - var vm = FindViewModel(target); - if (vm is null) return null; + internal static ICommand? GetCommand(FrameworkElement target, string cmdName) + { + var vm = FindViewModel(target); + if (vm is null) return null; - var vmType = vm.GetType(); - var cmdProp = vmType.GetProperty(cmdName); - if (cmdProp is not null) - return cmdProp.GetValue(vm) as ICommand; + var vmType = vm.GetType(); + var cmdProp = vmType.GetProperty(cmdName); + if (cmdProp is not null) + { + return cmdProp.GetValue(vm) as ICommand; + } #if DEBUG - throw new Exception("EventBinding path error: '" + cmdName + "' property not found on '" + vmType + "' 'DelegateCommand'"); + throw new Exception("EventBinding path error: '" + cmdName + "' property not found on '" + vmType + "' 'DelegateCommand'"); #endif - return null; - } - internal static object GetCommandParameter(FrameworkElement target, object args, string commandParameter) - { - var classify = commandParameter.Split('.'); - object ret; - switch (classify[0]) - { - case "$e": - ret = args; - break; - case "$this": - ret = classify.Length > 1 ? FollowPropertyPath(target, commandParameter.Replace("$this.", ""), target.GetType()) : target; - break; - default: - ret = commandParameter; - break; - } - return ret; - } +#pragma warning disable CS0162 // 检测到无法访问的代码 + return null; +#pragma warning restore CS0162 // 检测到无法访问的代码 + } - internal static ViewModelBase? FindViewModel(FrameworkElement? target) + internal static object GetCommandParameter(FrameworkElement target, object args, string commandParameter) + { + var classify = commandParameter.Split('.'); + object ret = classify[0] switch { - if (target is null) - return null; + "$e" => args, + "$this" => classify.Length > 1 ? FollowPropertyPath(target, commandParameter.Replace("$this.", ""), target.GetType()) : target, + _ => commandParameter, + }; + return ret; + } - if (target.DataContext is ViewModelBase vm) - return vm; + internal static ViewModelBase? FindViewModel(FrameworkElement? target) + { + if (target is null) return null; - var parent = target.GetParentObject() as FrameworkElement; - return FindViewModel(parent); - } + if (target.DataContext is ViewModelBase vm) return vm; - internal static object FollowPropertyPath(object target, string path, Type valueType = null) - { - if (target is null) throw new ArgumentNullException(nameof(target)); - if (path is null) throw new ArgumentNullException(nameof(path)); + var parent = target.GetParentObject() as FrameworkElement; - Type currentType = valueType ?? target.GetType(); + return FindViewModel(parent); + } - foreach (string propertyName in path.Split('.')) - { - PropertyInfo property = currentType.GetProperty(propertyName); - if (property is null) throw new NullReferenceException("property"); + internal static object FollowPropertyPath(object target, string path, Type? valueType = null) + { + if (target is null) throw new ArgumentNullException(nameof(target)); + if (path is null) throw new ArgumentNullException(nameof(path)); - target = property.GetValue(target); - currentType = property.PropertyType; - } - return target; + Type currentType = valueType ?? target.GetType(); + + foreach (string propertyName in path.Split('.')) + { + PropertyInfo property = currentType.GetProperty(propertyName); + if (property is null) throw new NullReferenceException("property"); + + target = property.GetValue(target); + currentType = property.PropertyType; } + return target; } } diff --git a/src/IFoxCAD.WPF/GlobalUsings.cs b/src/IFoxCAD.WPF/GlobalUsings.cs new file mode 100644 index 0000000..8ab21a7 --- /dev/null +++ b/src/IFoxCAD.WPF/GlobalUsings.cs @@ -0,0 +1,18 @@ +/// 系统引用 +global using System; +global using System.Collections; +global using System.Collections.Generic; +global using System.IO; +global using System.Linq; +global using System.ComponentModel; +global using System.Runtime.CompilerServices; +global using System.Diagnostics; +global using System.Windows; +global using System.Windows.Input; +global using System.Reflection.Emit; +global using System.Windows.Markup; +global using System.Reflection; +global using System.Windows.Media; +global using System.Globalization; +global using System.Windows.Data; + diff --git a/src/IFoxCAD.WPF/IFoxCAD.WPF.csproj b/src/IFoxCAD.WPF/IFoxCAD.WPF.csproj index 297d6ce..9fa74ff 100644 --- a/src/IFoxCAD.WPF/IFoxCAD.WPF.csproj +++ b/src/IFoxCAD.WPF/IFoxCAD.WPF.csproj @@ -1,43 +1,40 @@  - - - preview - enable + + preview + enable - - NET45;NET46;NET47;NET48; + net45 + true + true + true + true + 0.3.0 + xsfhlzh;vicwjb + InspireFunction + WPF的简单MVVM模式开发类库 + InspireFunction + LICENSE + https://gitee.com/inspirefunction/ifoxcad + https://gitee.com/inspirefunction/ifoxcad.git + IFoxCAD;C#;NET;WPF;MVVM + git + 开启可空类型. + - true - true - true - true - 0.1.0 - xsfhlzh;vicwjb - InspireFunction - WPF的简单MVVM模式开发类库 - InspireFunction - LICENSE - https://gitee.com/inspirefunction/ifoxcad - https://gitee.com/inspirefunction/ifoxcad.git - IFoxCAD;C#;NET;WPF;MVVM - git - 基于NFOX类库的重构版本 - + + DEBUG;TRACE + - - DEBUG;TRACE - + + + - - - - - - - True - - - + + + True + + + diff --git a/src/IFoxCAD.WPF/RelayCommand.cs b/src/IFoxCAD.WPF/RelayCommand.cs index cb4f118..72fabca 100644 --- a/src/IFoxCAD.WPF/RelayCommand.cs +++ b/src/IFoxCAD.WPF/RelayCommand.cs @@ -1,205 +1,199 @@ using Microsoft.Xaml.Behaviors; -using System; -using System.Diagnostics; -using System.Windows; -using System.Windows.Input; -namespace IFoxCAD.WPF +namespace IFoxCAD.WPF; + +/// +/// 命令基类 +/// +/// +public class RelayCommand : ICommand { + readonly Func? _canExecute; + readonly Action _execute; /// - /// 命令基类 + /// 初始化 类. /// - /// - public class RelayCommand : ICommand + /// 执行函数 + public RelayCommand(Action execute) : this(execute, null) { - readonly Func _canExecute; - readonly Action _execute; - /// - /// 初始化 类. - /// - /// 执行函数 - public RelayCommand(Action execute):this(execute,null) - { - } - /// - /// 初始化 类. - /// - /// 执行函数委托 - /// 是否可执行函数委托 - /// execute - public RelayCommand(Action execute,Func canExecute) - { - _execute = execute ?? throw new ArgumentNullException(nameof(execute)); - _canExecute = canExecute; - } + } + /// + /// 初始化 类. + /// + /// 执行函数委托 + /// 是否可执行函数委托 + /// execute + public RelayCommand(Action execute, Func? canExecute) + { + _execute = execute ?? throw new ArgumentNullException(nameof(execute)); + _canExecute = canExecute; + } - /// - /// 当出现影响是否应执行该命令的更改时发生。 - /// - public event EventHandler CanExecuteChanged + /// + /// 当出现影响是否应执行该命令的更改时发生。 + /// + public event EventHandler CanExecuteChanged + { + add { - add - { - if (_canExecute is not null) - { - CommandManager.RequerySuggested += value; - } - } - remove + if (_canExecute is not null) { - if (_canExecute is not null) - { - CommandManager.RequerySuggested -= value; - } + CommandManager.RequerySuggested += value; } } - /// - /// 定义确定此命令是否可在其当前状态下执行的方法。 - /// - /// 此命令使用的数据。 如果此命令不需要传递数据,则该对象可以设置为 。 - /// - /// 如果可执行此命令,则为 ;否则为 。 - /// - [DebuggerStepThrough] - public bool CanExecute(object parameter) + remove { - return _canExecute is null ? true : _canExecute(parameter); - } - /// - /// 定义在调用此命令时要调用的方法。 - /// - /// 此命令使用的数据。 如果此命令不需要传递数据,则该对象可以设置为 。 - public void Execute(object parameter) - { - _execute(parameter); + if (_canExecute is not null) + { + CommandManager.RequerySuggested -= value; + } } } + /// + /// 定义确定此命令是否可在其当前状态下执行的方法。 + /// + /// 此命令使用的数据。 如果此命令不需要传递数据,则该对象可以设置为 。 + /// + /// 如果可执行此命令,则为 ;否则为 。 + /// + [DebuggerStepThrough] + public bool CanExecute(object parameter) + { + return _canExecute is null || _canExecute(parameter); + } + /// + /// 定义在调用此命令时要调用的方法。 + /// + /// 此命令使用的数据。 如果此命令不需要传递数据,则该对象可以设置为 。 + public void Execute(object parameter) + { + _execute(parameter); + } +} +/// +/// 命令泛型基类 +/// +/// 事件类型 +/// +public class RelayCommand : ICommand +{ + readonly Func _canExecute; + readonly Action _execute; /// - /// 命令泛型基类 + /// 初始化 类。 /// - /// 事件类型 - /// - public class RelayCommand : ICommand + /// 执行函数 + public RelayCommand(Action execute) : this(execute, (o) => true) { - readonly Func _canExecute; - readonly Action _execute; - /// - /// 初始化 类。 - /// - /// 执行函数 - public RelayCommand(Action execute) : this(execute, (o)=>true) - { - } + } - /// - /// 初始化 类。 - /// - /// 执行函数委托 - /// 是否可执行函数委托 - /// execute - public RelayCommand(Action execute, Func canExecute) - { - _execute = execute ?? throw new ArgumentNullException(nameof(execute)); - _canExecute = canExecute; - } - /// - /// 当出现影响是否应执行该命令的更改时发生。 - /// - public event EventHandler CanExecuteChanged + /// + /// 初始化 类。 + /// + /// 执行函数委托 + /// 是否可执行函数委托 + /// execute + public RelayCommand(Action execute, Func canExecute) + { + _execute = execute ?? throw new ArgumentNullException(nameof(execute)); + _canExecute = canExecute; + } + /// + /// 当出现影响是否应执行该命令的更改时发生。 + /// + public event EventHandler CanExecuteChanged + { + add { - add - { - if (_canExecute is not null) - { - CommandManager.RequerySuggested += value; - } - } - remove + if (_canExecute is not null) { - if (_canExecute is not null) - { - CommandManager.RequerySuggested -= value; - } + CommandManager.RequerySuggested += value; } } - /// - /// 定义确定此命令是否可在其当前状态下执行的方法。 - /// - /// 此命令使用的数据。 如果此命令不需要传递数据,则该对象可以设置为 。 - /// - /// 如果可执行此命令,则为 ;否则为 。 - /// - public bool CanExecute(object parameter) + remove { - if (_canExecute is null) + if (_canExecute is not null) { - return true; + CommandManager.RequerySuggested -= value; } - return _canExecute((T)parameter); } - /// - /// 定义在调用此命令时要调用的方法。 - /// - /// 此命令使用的数据。 如果此命令不需要传递数据,则该对象可以设置为 。 - public void Execute(object parameter) + } + /// + /// 定义确定此命令是否可在其当前状态下执行的方法。 + /// + /// 此命令使用的数据。 如果此命令不需要传递数据,则该对象可以设置为 。 + /// + /// 如果可执行此命令,则为 ;否则为 。 + /// + public bool CanExecute(object parameter) + { + if (_canExecute is null) { - if (_execute is not null && CanExecute(parameter)) - { - _execute((T)parameter); - } + return true; } + return _canExecute((T)parameter); } - /// - /// 事件命令 + /// 定义在调用此命令时要调用的方法。 /// - public class EventCommand : TriggerAction + /// 此命令使用的数据。 如果此命令不需要传递数据,则该对象可以设置为 。 + public void Execute(object parameter) { - /// - /// 执行动作 - /// - /// 要执行的动作参数, 如果动作为提供参数,就设置为null - protected override void Invoke(object parameter) + if (_execute is not null && CanExecute(parameter)) { - if (CommandParameter is not null) - { - parameter = CommandParameter; - } - if (Command is not null) - { - Command.Execute(parameter); - } + _execute((T)parameter); } - /// - /// 事件 - /// - public ICommand Command + } +} + +/// +/// 事件命令 +/// +public class EventCommand : TriggerAction +{ + /// + /// 执行动作 + /// + /// 要执行的动作参数, 如果动作为提供参数,就设置为null + protected override void Invoke(object parameter) + { + if (CommandParameter is not null) { - get { return (ICommand)GetValue(CommandProperty); } - set { SetValue(CommandProperty, value); } + parameter = CommandParameter; } - /// - /// 事件属性 - /// - public static readonly DependencyProperty CommandProperty = - DependencyProperty.Register("Command", typeof(ICommand), typeof(EventCommand), new PropertyMetadata(null)); - - /// - /// 事件参数,如果为空,将自动传入事件的真实参数 - /// - public object CommandParameter + if (Command is not null) { - get { return (object)GetValue(CommandParameterProperty); } - set { SetValue(CommandParameterProperty, value); } + Command.Execute(parameter); } - /// - /// 事件参数属性 - /// - public static readonly DependencyProperty CommandParameterProperty = - DependencyProperty.Register("CommandParameter", typeof(object), typeof(EventCommand), new PropertyMetadata(null)); } + /// + /// 事件 + /// + public ICommand Command + { + get { return (ICommand)GetValue(CommandProperty); } + set { SetValue(CommandProperty, value); } + } + /// + /// 事件属性 + /// + public static readonly DependencyProperty CommandProperty = + DependencyProperty.Register("Command", typeof(ICommand), typeof(EventCommand), new PropertyMetadata(null)); + /// + /// 事件参数,如果为空,将自动传入事件的真实参数 + /// + public object CommandParameter + { + get { return (object)GetValue(CommandParameterProperty); } + set { SetValue(CommandParameterProperty, value); } + } + /// + /// 事件参数属性 + /// + public static readonly DependencyProperty CommandParameterProperty = + DependencyProperty.Register("CommandParameter", typeof(object), typeof(EventCommand), new PropertyMetadata(null)); } diff --git a/src/IFoxCAD.WPF/ViewModelBase.cs b/src/IFoxCAD.WPF/ViewModelBase.cs index 0d0a2dc..c992e81 100644 --- a/src/IFoxCAD.WPF/ViewModelBase.cs +++ b/src/IFoxCAD.WPF/ViewModelBase.cs @@ -1,63 +1,58 @@ -using System; -using System.ComponentModel; -using System.Runtime.CompilerServices; +namespace IFoxCAD.WPF; -namespace IFoxCAD.WPF +/// +/// ViewModel基类 +/// +/// +public class ViewModelBase : INotifyPropertyChanged { /// - /// ViewModel基类 + /// 属性值更改事件。 /// - /// - public class ViewModelBase : INotifyPropertyChanged + public event PropertyChangedEventHandler? PropertyChanged; + /// + /// 属性改变时调用 + /// + /// 属性名 + public void OnPropertyChanged([CallerMemberName] string propertyName = "") + { + + PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName)); + } + /// + /// 设置属性函数,自动通知属性改变事件 + /// + /// 属性类型 + /// 属性 + /// 属性值 + /// 属性名 + /// 成功返回 ,反之 + protected virtual bool Set(ref T storage, T value, [CallerMemberName] string propertyName = "") { - /// - /// 属性值更改事件。 - /// - public event PropertyChangedEventHandler PropertyChanged; - /// - /// 属性改变时调用 - /// - /// 属性名 - public void OnPropertyChanged([CallerMemberName]string propertyName = "") - { - - PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName)); - } - /// - /// 设置属性函数,自动通知属性改变事件 - /// - /// 属性类型 - /// 属性 - /// 属性值 - /// 属性名 - /// 成功返回 ,反之 - protected virtual bool Set(ref T storage, T value, [CallerMemberName]string propertyName = null) - { - if (object.Equals(storage, value)) return false; + if (object.Equals(storage, value)) return false; - storage = value; - this.OnPropertyChanged(propertyName); + storage = value; + this.OnPropertyChanged(propertyName); - return true; - } - /// - /// 创建命令 - /// - /// 要调用的命令函数委托 - /// WPF命令 - protected RelayCommand CreateCommand(Action executeMethod) - { - return CreateCommand(executeMethod, (o) => true); - } - /// - /// 创建命令 - /// - /// 要调用的命令函数委托 - /// 命令是否可以执行的委托 - /// WPF命令 - protected RelayCommand CreateCommand(Action executeMethod, Func canExecuteMethod) - { - return new RelayCommand(executeMethod, canExecuteMethod); - } + return true; + } + /// + /// 创建命令 + /// + /// 要调用的命令函数委托 + /// WPF命令 + protected RelayCommand CreateCommand(Action executeMethod) + { + return CreateCommand(executeMethod, (o) => true); + } + /// + /// 创建命令 + /// + /// 要调用的命令函数委托 + /// 命令是否可以执行的委托 + /// WPF命令 + protected RelayCommand CreateCommand(Action executeMethod, Func canExecuteMethod) + { + return new RelayCommand(executeMethod, canExecuteMethod); } } diff --git a/tests/Test/CmdINI.cs b/tests/Test/CmdINI.cs new file mode 100644 index 0000000..2e37c1d --- /dev/null +++ b/tests/Test/CmdINI.cs @@ -0,0 +1,111 @@ +/// +/// 注册中心(自动执行接口): +/// 用于启动cad后写入启动注册表及反射调用以下特性和接口 +/// netload的工程必须继承虚函数后才能使用特性和接口 +/// 启动cad后的执行顺序为: +/// 1:构造函数 +/// 2:特性..多个 +/// 3:接口..多个 +/// 4:本类的构造函数 +/// +public class CmdINI : AutoRegAssem +{ + public CmdINI() : base(AutoRegConfig.All) + { + var dm = Application.DocumentManager; + var doc = dm.MdiActiveDocument; + var ed = doc.Editor; + ed.WriteMessage($"\n {nameof(CmdINI)}构造函数,开始自动执行\r\n"); + } + + ///如果netload之后用 删除注册表, + ///由于不是也不能卸载dll,再netload是无法执行自动接口的, + ///所以此时会产生无法再注册的问题...因此需要暴露此注册函数(硬来) + [CommandMethod("IFoxAddReg")] + public void IFoxAddReg() + { + base.RegApp(); + + var dm = Application.DocumentManager; + var doc = dm.MdiActiveDocument; + var ed = doc.Editor; + ed.WriteMessage($"\n 加入注册表"); + } + + /// + /// 卸载注册表信息 + /// + [CommandMethod("IFoxRemoveReg")] + public void IFoxRemoveReg() + { + //执行命令的时候会再次执行构造函数(导致初始化两次),但是再次执行就不会了 + base.UnRegApp(); + + var dm = Application.DocumentManager; + var doc = dm.MdiActiveDocument; + var ed = doc.Editor; + ed.WriteMessage($"\n 卸载注册表"); + } +} + + +/* + * 自动执行特性例子: + */ +public class Cmd_IFoxInitialize +{ + [IFoxInitialize] + public void NameCasual() + { + var dm = Application.DocumentManager; + var doc = dm.MdiActiveDocument; + var ed = doc.Editor; + ed.WriteMessage("\n 开始自动执行 可以分开多个类和多个函数 \r\n"); + } + + [IFoxInitialize] + public void NameCasualtest() + { + var dm = Application.DocumentManager; + var doc = dm.MdiActiveDocument; + var ed = doc.Editor; + ed.WriteMessage("\n 开始自动执行 又一次测试 \r\n"); + } + + [IFoxInitialize] + public void Initialize() + { + var dm = Application.DocumentManager; + var doc = dm.MdiActiveDocument; + var ed = doc.Editor; + ed.WriteMessage("\n 开始自动执行 Initialize \r\n"); + } + + [IFoxInitialize(isInitialize: false)] + public void Terminate() + { + //try + //{ + // var dm = Application.DocumentManager; + // var doc = dm.MdiActiveDocument; + // var ed = doc.Editor; //注意此时编辑器已经回收,所以此句没用,并引发错误 + // ed.WriteMessage("\n 结束自动执行 Terminate \r\n"); + //} + //catch (System.Exception) + //{ + //} + } + + //[IFoxInitialize] + //public void Initialize() + //{ + // //文档管理器将比此接口前创建,因此此句会执行 + // Application.DocumentManager.MdiActiveDocument.Editor.WriteMessage("\nload...."); + //} + //[IFoxInitialize(Sequence.First, false)] + //public void Terminate() + //{ + // //文档管理器将比此接口前死亡,因此此句不会执行 + // Application.DocumentManager.MdiActiveDocument?.Editor.WriteMessage("\nunload...."); + //} +} \ No newline at end of file diff --git a/tests/Test/GlobalUsings.cs b/tests/Test/GlobalUsings.cs new file mode 100644 index 0000000..2896cbf --- /dev/null +++ b/tests/Test/GlobalUsings.cs @@ -0,0 +1,30 @@ +/// 系统引用 +global using System; +global using System.Collections; +global using System.Collections.Generic; +global using System.IO; +global using System.Linq; +global using System.Text; +global using System.Reflection; +global using System.Text.RegularExpressions; +global using Microsoft.Win32; +global using System.ComponentModel; +global using System.Runtime.CompilerServices; + +/// autocad 引用 +global using Autodesk.AutoCAD.ApplicationServices; +global using Autodesk.AutoCAD.EditorInput; +global using Autodesk.AutoCAD.Colors; +global using Autodesk.AutoCAD.DatabaseServices; +global using Autodesk.AutoCAD.Geometry; +global using Autodesk.AutoCAD.Runtime; +global using Acgi = Autodesk.AutoCAD.GraphicsInterface; +global using Acap = Autodesk.AutoCAD.ApplicationServices.Application; + +global using Registry = Microsoft.Win32.Registry; +global using RegistryKey = Microsoft.Win32.RegistryKey; + +/// ifoxcad +global using IFoxCAD.Cad; +global using IFoxCAD.WPF; +global using IFoxCAD.Basal; \ No newline at end of file diff --git a/tests/Test/Properties/launchSettings.json b/tests/Test/Properties/launchSettings.json index 1ecc024..4b464c3 100644 --- a/tests/Test/Properties/launchSettings.json +++ b/tests/Test/Properties/launchSettings.json @@ -1,6 +1,6 @@ { "profiles": { - "DBTrans.test": { + "Test": { "commandName": "Executable", "executablePath": "C:\\Program Files\\Autodesk\\AutoCAD 2021\\acad.exe", "commandLineArgs": "/nologo", diff --git a/tests/Test/Test.cs b/tests/Test/Test.cs index 78746f4..bc45757 100644 --- a/tests/Test/Test.cs +++ b/tests/Test/Test.cs @@ -1,193 +1,179 @@ -using Autodesk.AutoCAD.ApplicationServices; -using Autodesk.AutoCAD.Colors; -using Autodesk.AutoCAD.DatabaseServices; -using Autodesk.AutoCAD.EditorInput; -using Autodesk.AutoCAD.Geometry; -using Autodesk.AutoCAD.Runtime; +#pragma warning disable CS0219 // 变量已被赋值,但从未使用过它的值 +namespace Test; -using IFoxCAD.Cad; - -using System; -using System.Collections.Generic; -using System.IO; -using System.Linq; - -using test.wpf; - -namespace test +public class Test { - public class Test + [CommandMethod("dbtest")] + public void Dbtest() { - [CommandMethod("dbtest")] - public void Dbtest() + using var tr = new DBTrans(); + tr.Editor.WriteMessage("\n测试 Editor 属性是否工作!"); + tr.Editor.WriteMessage("\n----------开始测试--------------"); + tr.Editor.WriteMessage("\n测试document属性是否工作"); + if (tr.Document == Getdoc()) { - using var tr = new DBTrans(); - tr.Editor.WriteMessage("\n测试 Editor 属性是否工作!"); - tr.Editor.WriteMessage("\n----------开始测试--------------"); - tr.Editor.WriteMessage("\n测试document属性是否工作"); - if (tr.Document == Getdoc()) - { - tr.Editor.WriteMessage("\ndocument 正常"); - } - tr.Editor.WriteMessage("\n测试database属性是否工作"); - if (tr.Database == Getdb()) - { - tr.Editor.WriteMessage("\ndatabase 正常"); - } - - Line line = new(new Point3d(0, 0, 0), new Point3d(1, 1, 0)); - Circle circle = new(new Point3d(0, 0, 0), Vector3d.ZAxis, 2); - //var lienid = tr.AddEntity(line); - //var cirid = tr.AddEntity(circle); - //var linent = tr.GetObject(lienid); - //var lineent = tr.GetObject(cirid); - //var linee = tr.GetObject(cirid); //经测试,类型不匹配,返回null - //var dd = tr.GetObject(lienid); - //List ds = new() { linee, dd }; + tr.Editor.WriteMessage("\ndocument 正常"); } - - //add entity test - [CommandMethod("addent")] - public void Addent() + tr.Editor.WriteMessage("\n测试database属性是否工作"); + if (tr.Database == Getdb()) { - using var tr = new DBTrans(); - Line line = new(new Point3d(0, 0, 0), new Point3d(1, 1, 0)); - tr.CurrentSpace.AddEntity(line); - Line line1 = new(new Point3d(10, 10, 0), new Point3d(41, 1, 0)); - tr.ModelSpace.AddEntity(line1); - Line line2 = new(new Point3d(-10, 10, 0), new Point3d(41, 1, 0)); - tr.PaperSpace.AddEntity(line2); + tr.Editor.WriteMessage("\ndatabase 正常"); } - [CommandMethod("drawarc")] - public void drawarc() - { - using var tr = new DBTrans(); - Arc arc1 = EntityEx.CreateArcSCE(new Point3d(2, 0, 0), new Point3d(0, 0, 0), new Point3d(0, 2, 0));//起点,圆心,终点 - Arc arc2 = EntityEx.CreateArc(new Point3d(4, 0, 0), new Point3d(0, 0, 0), Math.PI / 2); //起点,圆心,弧度 - Arc arc3 = EntityEx.CreateArc(new Point3d(1, 0, 0), new Point3d(0, 0, 0), new Point3d(0, 1, 0)); //起点,圆上一点,终点 - tr.CurrentSpace.AddEntity(arc1, arc2, arc3); - tr.CurrentSpace.AddArc(new Point3d(0, 0, 0), new Point3d(1, 1, 0), new Point3d(2, 0, 0));//起点,圆上一点,终点 - } + Line line = new(new Point3d(0, 0, 0), new Point3d(1, 1, 0)); + Circle circle = new(new Point3d(0, 0, 0), Vector3d.ZAxis, 2); + //var lienid = tr.AddEntity(line); + //var cirid = tr.AddEntity(circle); + //var linent = tr.GetObject(lienid); + //var lineent = tr.GetObject(cirid); + //var linee = tr.GetObject(cirid); //经测试,类型不匹配,返回null + //var dd = tr.GetObject(lienid); + //List ds = new() { linee, dd }; + //tr.CurrentSpace.AddEntity(line,tr); + } - [CommandMethod("drawcircle")] - public void draCircle() + //add entity test + [CommandMethod("addent")] + public void Addent() + { + using var tr = new DBTrans(); + Line line = new(new Point3d(0, 0, 0), new Point3d(1, 1, 0)); + tr.CurrentSpace.AddEntity(line); + Line line1 = new(new Point3d(10, 10, 0), new Point3d(41, 1, 0)); + tr.ModelSpace.AddEntity(line1); + Line line2 = new(new Point3d(-10, 10, 0), new Point3d(41, 1, 0)); + tr.PaperSpace.AddEntity(line2); + } + + [CommandMethod("drawarc")] + public void Drawarc() + { + using var tr = new DBTrans(); + Arc arc1 = EntityEx.CreateArcSCE(new Point3d(2, 0, 0), new Point3d(0, 0, 0), new Point3d(0, 2, 0));//起点,圆心,终点 + Arc arc2 = EntityEx.CreateArc(new Point3d(4, 0, 0), new Point3d(0, 0, 0), Math.PI / 2); //起点,圆心,弧度 + Arc arc3 = EntityEx.CreateArc(new Point3d(1, 0, 0), new Point3d(0, 0, 0), new Point3d(0, 1, 0)); //起点,圆上一点,终点 + tr.CurrentSpace.AddEntity(arc1, arc2, arc3); + tr.CurrentSpace.AddArc(new Point3d(0, 0, 0), new Point3d(1, 1, 0), new Point3d(2, 0, 0));//起点,圆上一点,终点 + } + + [CommandMethod("drawcircle")] + public void DraCircle() + { + using var tr = new DBTrans(); + Circle circle1 = EntityEx.CreateCircle(new Point3d(0, 0, 0), new Point3d(1, 0, 0)); //起点,终点 + Circle circle2 = EntityEx.CreateCircle(new Point3d(-2, 0, 0), new Point3d(2, 0, 0), new Point3d(0, 2, 0));//三点画圆,成功 + Circle circle3 = EntityEx.CreateCircle(new Point3d(-2, 0, 0), new Point3d(0, 0, 0), new Point3d(2, 0, 0));//起点,圆心,终点,失败 + tr.CurrentSpace.AddEntity(circle1, circle2); + if (circle3 is not null) { - using var tr = new DBTrans(); - Circle circle1 = EntityEx.CreateCircle(new Point3d(0, 0, 0), new Point3d(1, 0, 0)); //起点,终点 - Circle circle2 = EntityEx.CreateCircle(new Point3d(-2, 0, 0), new Point3d(2, 0, 0), new Point3d(0, 2, 0));//三点画圆,成功 - Circle circle3 = EntityEx.CreateCircle(new Point3d(-2, 0, 0), new Point3d(0, 0, 0), new Point3d(2, 0, 0));//起点,圆心,终点,失败 - tr.CurrentSpace.AddEntity(circle1, circle2); - if (circle3 is not null) - tr.CurrentSpace.AddEntity(circle3); - else - { - tr.Editor.WriteMessage("三点画圆失败"); - return; - } tr.CurrentSpace.AddEntity(circle3); - tr.CurrentSpace.AddCircle(new Point3d(0, 0, 0), new Point3d(1, 1, 0), new Point3d(2, 0, 0));//三点画圆,成功 - tr.CurrentSpace.AddCircle(new Point3d(0, 0, 0), new Point3d(1, 1, 0), new Point3d(2, 2, 0));//起点,圆上一点,终点(共线) } - - [CommandMethod("layertest")] - public void Layertest() + else { - using var tr = new DBTrans(); - tr.LayerTable.Add("1"); - tr.LayerTable.Add("2", lt => - { - lt.Color = Color.FromColorIndex(ColorMethod.ByColor, 1); - lt.LineWeight = LineWeight.LineWeight030; - - }); - tr.LayerTable.Remove("3"); - tr.LayerTable.Delete("0"); - tr.LayerTable.Change("4", lt => - { - lt.Color = Color.FromColorIndex(ColorMethod.ByColor, 2); - }); + tr.Editor.WriteMessage("三点画圆失败"); } + tr.CurrentSpace.AddEntity(circle3); + tr.CurrentSpace.AddCircle(new Point3d(0, 0, 0), new Point3d(1, 1, 0), new Point3d(2, 0, 0));//三点画圆,成功 + tr.CurrentSpace.AddCircle(new Point3d(0, 0, 0), new Point3d(1, 1, 0), new Point3d(2, 2, 0));//起点,圆上一点,终点(共线) + } + [CommandMethod("layertest")] + public void Layertest() + { + using var tr = new DBTrans(); + tr.LayerTable.Add("1"); + tr.LayerTable.Add("2", lt => { + lt.Color = Color.FromColorIndex(ColorMethod.ByColor, 1); + lt.LineWeight = LineWeight.LineWeight030; + + }); + tr.LayerTable.Remove("3"); + tr.LayerTable.Delete("0"); + tr.LayerTable.Change("4", lt => { + lt.Color = Color.FromColorIndex(ColorMethod.ByColor, 2); + }); + } - //添加图层 - [CommandMethod("layerAdd1")] - public void Layertest1() - { - using var tr = new DBTrans(); - tr.LayerTable.Add("test1", Color.FromColorIndex(ColorMethod.ByColor, 1)); - } - //添加图层 - [CommandMethod("layerAdd2")] - public void Layertest2() - { - using var tr = new DBTrans(); - tr.LayerTable.Add("test2", 2); - //tr.LayerTable["3"] = new LayerTableRecord(); - } - //删除图层 - [CommandMethod("layerdel")] - public void LayerDel() - { - using var tr = new DBTrans(); - Env.Editor.WriteMessage(tr.LayerTable.Delete("0").ToString()); //删除图层 0 - Env.Editor.WriteMessage(tr.LayerTable.Delete("Defpoints").ToString());//删除图层 Defpoints - Env.Editor.WriteMessage(tr.LayerTable.Delete("1").ToString()); //删除不存在的图层 1 - Env.Editor.WriteMessage(tr.LayerTable.Delete("2").ToString()); //删除有图元的图层 2 - Env.Editor.WriteMessage(tr.LayerTable.Delete("3").ToString()); //删除图层 3 + //添加图层 + [CommandMethod("layerAdd1")] + public void Layertest1() + { + using var tr = new DBTrans(); + tr.LayerTable.Add("test1", Color.FromColorIndex(ColorMethod.ByColor, 1)); + } - tr.LayerTable.Remove("2"); //测试是否能强制删除 - } + //添加图层 + [CommandMethod("layerAdd2")] + public void Layertest2() + { + using var tr = new DBTrans(); + tr.LayerTable.Add("test2", 2); + //tr.LayerTable["3"] = new LayerTableRecord(); + } + //删除图层 + [CommandMethod("layerdel")] + public void LayerDel() + { + using var tr = new DBTrans(); + Env.Editor.WriteMessage(tr.LayerTable.Delete("0").ToString()); //删除图层 0 + Env.Editor.WriteMessage(tr.LayerTable.Delete("Defpoints").ToString());//删除图层 Defpoints + Env.Editor.WriteMessage(tr.LayerTable.Delete("1").ToString()); //删除不存在的图层 1 + Env.Editor.WriteMessage(tr.LayerTable.Delete("2").ToString()); //删除有图元的图层 2 + Env.Editor.WriteMessage(tr.LayerTable.Delete("3").ToString()); //删除图层 3 + + tr.LayerTable.Remove("2"); //测试是否能强制删除 + } - //添加直线 - [CommandMethod("linedemo1")] - public void AddLine1() - { - using var tr = new DBTrans(); - // tr.ModelSpace.AddEnt(line); - // tr.ModelSpace.AddEnts(line,circle); - - // tr.PaperSpace.AddEnt(line); - // tr.PaperSpace.AddEnts(line,circle); - - // tr.addent(btr,line); - // tr.addents(btr,line,circle); - - - // tr.BlockTable.Add(new BlockTableRecord(), line => - // { - // line. - // }); - Line line1 = new(new Point3d(0, 0, 0), new Point3d(1, 1, 0)); - Line line2 = new(new Point3d(0, 0, 0), new Point3d(1, 1, 0)); - Line line3 = new(new Point3d(1, 1, 0), new Point3d(3, 3, 0)); - Circle circle = new Circle(new Point3d(0, 0, 0), Vector3d.ZAxis, 10); - tr.CurrentSpace.AddEntity(line1); - tr.CurrentSpace.AddEntity(line2, line3, circle); - } + //添加直线 + [CommandMethod("linedemo1")] + public void AddLine1() + { + using var tr = new DBTrans(); + // tr.ModelSpace.AddEnt(line); + // tr.ModelSpace.AddEnts(line,circle); + + // tr.PaperSpace.AddEnt(line); + // tr.PaperSpace.AddEnts(line,circle); + + // tr.addent(btr,line); + // tr.addents(btr,line,circle); + + + // tr.BlockTable.Add(new BlockTableRecord(), line => + // { + // line. + // }); + Line line1 = new(new Point3d(0, 0, 0), new Point3d(1, 1, 0)); + Line line2 = new(new Point3d(0, 0, 0), new Point3d(1, 1, 0)); + Line line3 = new(new Point3d(1, 1, 0), new Point3d(3, 3, 0)); + Circle circle = new(new Point3d(0, 0, 0), Vector3d.ZAxis, 10); + tr.CurrentSpace.AddEntity(line1); + tr.CurrentSpace.AddEntity(line2, line3, circle); + } - //增加多段线1 - [CommandMethod("Pldemo1")] - public void AddPolyline1() - { - using var tr = new DBTrans(); - Polyline pl = new Polyline(); - pl.AddVertexAt(0, new Point2d(0, 0), 0, 0, 0); - pl.AddVertexAt(1, new Point2d(10, 10), 0, 0, 0); - pl.AddVertexAt(2, new Point2d(20, 20), 0, 0, 0); - pl.AddVertexAt(3, new Point2d(30, 30), 0, 0, 0); - pl.AddVertexAt(4, new Point2d(40, 40), 0, 0, 0); - pl.Closed = true; - pl.Color = Color.FromColorIndex(ColorMethod.ByColor, 6); - tr.CurrentSpace.AddEntity(pl); - } + //增加多段线1 + [CommandMethod("Pldemo1")] + public void AddPolyline1() + { + using var tr = new DBTrans(); + Polyline pl = new(); + pl.SetDatabaseDefaults(); + pl.AddVertexAt(0, new Point2d(0, 0), 0, 0, 0); + pl.AddVertexAt(1, new Point2d(10, 10), 0, 0, 0); + pl.AddVertexAt(2, new Point2d(20, 20), 0, 0, 0); + pl.AddVertexAt(3, new Point2d(30, 30), 0, 0, 0); + pl.AddVertexAt(4, new Point2d(40, 40), 0, 0, 0); + pl.Closed = true; + pl.Color = Color.FromColorIndex(ColorMethod.ByColor, 6); + tr.CurrentSpace.AddEntity(pl); + } - //增加多段线2 - [CommandMethod("pldemo2")] - public void Addpl2() - { - var pts = new List<(Point3d, double, double, double)> + //增加多段线2 + [CommandMethod("pldemo2")] + public void Addpl2() + { + var pts = new List<(Point3d, double, double, double)> { (new Point3d(0,0,0),0,0,0), (new Point3d(10,0,0),0,0,0), @@ -195,154 +181,28 @@ public void Addpl2() (new Point3d(0,10,0),0,0,0), (new Point3d(5,5,0),0,0,0) }; - using var tr = new DBTrans(); - tr.CurrentSpace.AddPline(pts); - } - - //块定义 - [CommandMethod("blockdef")] - public void BlockDef() - { - using var tr = new DBTrans(); - //var line = new Line(new Point3d(0, 0, 0), new Point3d(1, 1, 0)); - tr.BlockTable.Add("test", - btr => - { - btr.Origin = new Point3d(0, 0, 0); - }, - () => //图元 - { - return new List { new Line(new Point3d(0, 0, 0), new Point3d(1, 1, 0)) }; - }, - () => //属性定义 - { - var id1 = new AttributeDefinition() { Position = new Point3d(0, 0, 0), Tag = "start", Height = 0.2 }; - var id2 = new AttributeDefinition() { Position = new Point3d(1, 1, 0), Tag = "end", Height = 0.2 }; - return new List { id1, id2 }; - } - ); - //ObjectId objectId = tr.BlockTable.Add("a");//新建块 - //objectId.GetObject().AddEntity();//测试添加空实体 - } - //修改块定义 - [CommandMethod("blockdefchange")] - public void BlockDefChange() - { - using var tr = new DBTrans(); - //var line = new Line(new Point3d(0, 0, 0), new Point3d(1, 1, 0)); - tr.BlockTable.Change("test", btr => - { - btr.Origin = new Point3d(5, 5, 0); - btr.AddEntity(new Circle(new Point3d(0, 0, 0), Vector3d.ZAxis, 2)); - btr.GetEntities() - .ToList() - .ForEach(e => e.Flush()); //刷新块显示 - - }); - tr.Editor.Regen(); - } - - [CommandMethod("insertblockdef")] - public void InsertBlockDef() - { - using var tr = new DBTrans(); - var line1 = new Line(new Point3d(0, 0, 0), new Point3d(1, 1, 0)); - var line2 = new Line(new Point3d(0, 0, 0), new Point3d(-1, 1, 0)); - var att1 = new AttributeDefinition() { Position = new Point3d(10, 10, 0), Tag = "tagTest1", Height = 1, TextString = "valueTest1" }; - var att2 = new AttributeDefinition() { Position = new Point3d(10, 12, 0), Tag = "tagTest2", Height = 1, TextString = "valueTest2" }; - tr.BlockTable.Add("test1", line1, line2, att1, att2); - - - var ents = new List(); - var line5 = new Line(new Point3d(0, 0, 0), new Point3d(1, 1, 0)); - var line6 = new Line(new Point3d(0, 0, 0), new Point3d(-1, 1, 0)); - ents.Add(line5); - ents.Add(line6); - tr.BlockTable.Add("test44", ents); - - - var line3 = new Line(new Point3d(5, 5, 0), new Point3d(6, 6, 0)); - var line4 = new Line(new Point3d(5, 5, 0), new Point3d(-6, 6, 0)); - var att3 = new AttributeDefinition() { Position = new Point3d(10, 14, 0), Tag = "tagTest3", Height = 1, TextString = "valueTest3" }; - var att4 = new AttributeDefinition() { Position = new Point3d(10, 16, 0), Tag = "tagTest4", Height = 1, TextString = "valueTest4" }; - tr.BlockTable.Add("test2", new List { line3, line4 }, new List { att3, att4 }); - //tr.CurrentSpace.InsertBlock(new Point3d(4, 4, 0), "test1"); // 测试默认 - //tr.CurrentSpace.InsertBlock(new Point3d(4, 4, 0), "test2"); - //tr.CurrentSpace.InsertBlock(new Point3d(4, 4, 0), "test3"); //测试插入不存在的块定义 - //tr.CurrentSpace.InsertBlock(new Point3d(0, 0, 0), "test1", new Scale3d(2)); // 测试放大2倍 - //tr.CurrentSpace.InsertBlock(new Point3d(4, 4, 0), "test1", new Scale3d(2), Math.PI / 4); // 测试放大2倍,旋转45度 - - var def1 = new Dictionary - { - { "tagTest1", "1" }, - { "tagTest2", "2" } - }; - tr.CurrentSpace.InsertBlock(new Point3d(0, 0, 0), "test1", atts: def1); - var def2 = new Dictionary - { - { "tagTest3", "1" }, - { "tagTest4", "" } - }; - tr.CurrentSpace.InsertBlock(new Point3d(10, 10, 0), "test2", atts: def2); - tr.CurrentSpace.InsertBlock(new Point3d(-10, 0, 0), "test44"); - } - - [CommandMethod("testblocknullbug")] - public void TestBlockNullBug() - { - using var tr = new DBTrans(); + using var tr = new DBTrans(); + tr.CurrentSpace.AddPline(pts); + } - var ents = new List(); - var line5 = new Line(new Point3d(0, 0, 0), new Point3d(1, 1, 0)); - var line6 = new Line(new Point3d(0, 0, 0), new Point3d(-1, 1, 0)); - ents.Add(line5); - ents.Add(line6); - tr.BlockTable.Add("test44", ents); - tr.CurrentSpace.InsertBlock(new Point3d(0, 0, 0), "test44"); - } - [CommandMethod("testclip")] - public void TestClipBlock() - { - using var tr = new DBTrans(); - tr.BlockTable.Add("test1", - btr => - { - btr.Origin = new Point3d(0, 0, 0); - btr.AddEntity(new Line(new Point3d(0, 0, 0), new Point3d(10, 10, 0)), - new Line(new Point3d(10, 10, 0), new Point3d(10, 0, 0)) - ); - } - ); - //tr.BlockTable.Add("hah"); - var id = tr.CurrentSpace.InsertBlock(new Point3d(0, 0, 0), "test1"); - var bref = tr.GetObject(id); - var pts = new List { new Point3d(3, 3, 0), new Point3d(7, 3, 0), new Point3d(7, 7, 0), new Point3d(3, 7, 0) }; - bref.ClipBlockRef(pts); - var id1 = tr.CurrentSpace.InsertBlock(new Point3d(20, 20, 0), "test1"); - var bref1 = tr.GetObject(id); - - bref1.ClipBlockRef(new Point3d(13, 13, 0), new Point3d(17, 17, 0)); - } + // 测试扩展数据 + [CommandMethod("addxdata")] + public void AddXdata() + { + using var tr = new DBTrans(); + var appname = "myapp"; + tr.RegAppTable.Add(appname); // add函数会默认的在存在这个名字的时候返回这个名字的regapp的id,不存在就新建 + tr.RegAppTable.Add("myapp2"); - // 测试扩展数据 - [CommandMethod("addxdata")] - public void AddXdata() + var line = new Line(new Point3d(0, 0, 0), new Point3d(1, 1, 0)) { - using var tr = new DBTrans(); - var appname = "myapp"; - - tr.RegAppTable.Add(appname); // add函数会默认的在存在这个名字的时候返回这个名字的regapp的id,不存在就新建 - tr.RegAppTable.Add("myapp2"); - - var line = new Line(new Point3d(0, 0, 0), new Point3d(1, 1, 0)) - { - XData = new XDataList() + XData = new XDataList() { { DxfCode.ExtendedDataRegAppName, appname }, //可以用dxfcode和int表示组码 { DxfCode.ExtendedDataAsciiString, "hahhahah" }, @@ -351,278 +211,150 @@ public void AddXdata() { DxfCode.ExtendedDataAsciiString, "hahhahah" }, {1070, 12 } } - }; - - tr.CurrentSpace.AddEntity(line); - } - - [CommandMethod("getxdata")] - public void GetXdata() - { - var doc = Application.DocumentManager.MdiActiveDocument; - var ed = doc.Editor; - - var res = ed.GetEntity("\n select the entity:"); - if (res.Status == PromptStatus.OK) - { - using var tr = new DBTrans(); - var data = tr.GetObject(res.ObjectId).XData; - ed.WriteMessage(data.ToString()); - } - } - - [CommandMethod("changexdata")] - public void Changexdata() - { - var doc = Application.DocumentManager.MdiActiveDocument; - var ed = doc.Editor; - var appname = "myapp"; - var res = ed.GetEntity("\n select the entity:"); - if (res.Status == PromptStatus.OK) - { - using var tr = new DBTrans(); - var data = tr.GetObject(res.ObjectId); - data.ChangeXData(appname, DxfCode.ExtendedDataAsciiString, "change"); + }; - ed.WriteMessage(data.XData.ToString()); - } - } - [CommandMethod("removexdata")] - public void Removexdata() - { - var doc = Application.DocumentManager.MdiActiveDocument; - var ed = doc.Editor; - var appname = "myapp"; - var res = ed.GetEntity("\n select the entity:"); - if (res.Status == PromptStatus.OK) - { - using var tr = new DBTrans(); - var data = tr.GetObject(res.ObjectId); - data.RemoveXData(appname, DxfCode.ExtendedDataAsciiString); + tr.CurrentSpace.AddEntity(line); + } - ed.WriteMessage(data.XData.ToString()); - } - } + [CommandMethod("getxdata")] + public void GetXdata() + { + var doc = Application.DocumentManager.MdiActiveDocument; + var ed = doc.Editor; + using var tr = new DBTrans(); + tr.RegAppTable.ForEach(id => + id.GetObject().Name.Print()); + tr.RegAppTable.GetRecords().ForEach(rec => rec.Name.Print()); + tr.RegAppTable.GetRecordNames().ForEach(name => name.Print()); + tr.RegAppTable.ForEach(re => re.Name.Print()); + + //var res = ed.GetEntity("\n select the entity:"); + //if (res.Status == PromptStatus.OK) + //{ + // using var tr = new DBTrans(); + // tr.RegAppTable.ForEach(id => id.GetObject().Print()); + // var data = tr.GetObject(res.ObjectId).XData; + // ed.WriteMessage(data.ToString()); + //} + } - [CommandMethod("PrintLayerName")] - public void PrintLayerName() + [CommandMethod("changexdata")] + public void Changexdata() + { + var doc = Application.DocumentManager.MdiActiveDocument; + var ed = doc.Editor; + var appname = "myapp"; + var res = ed.GetEntity("\n select the entity:"); + if (res.Status == PromptStatus.OK) { using var tr = new DBTrans(); - foreach (var layerRecord in tr.LayerTable.GetRecords()) - { - tr.Editor.WriteMessage(layerRecord.Name); - } - - } - + var data = tr.GetObject(res.ObjectId); + data.ChangeXData(appname, DxfCode.ExtendedDataAsciiString, "change"); - [CommandMethod("testwpf")] - public void TestWPf() - { - - var test = new TestView(); - Application.ShowModalWindow(test); + ed.WriteMessage(data.XData.ToString()); } - - [CommandMethod("testpt")] - public void TestPt() + } + [CommandMethod("removexdata")] + public void Removexdata() + { + var doc = Application.DocumentManager.MdiActiveDocument; + var ed = doc.Editor; + var appname = "myapp"; + var res = ed.GetEntity("\n select the entity:"); + if (res.Status == PromptStatus.OK) { - //var pt = Env.Editor.GetPoint("pick pt:").Value; - //var pl = Env.Editor.GetEntity("pick pl").ObjectId; - var tr1 = HostApplicationServices.WorkingDatabase.TransactionManager.TopTransaction; - using var tr2 = new DBTrans(); - var tr3 = HostApplicationServices.WorkingDatabase.TransactionManager.TopTransaction; - var tr6 = Application.DocumentManager.MdiActiveDocument.TransactionManager.TopTransaction; - Env.Print(tr2.Transaction == tr3); - Env.Print(tr3 == tr6); - using var tr4 = new DBTrans(); - var tr5 = HostApplicationServices.WorkingDatabase.TransactionManager.TopTransaction; - var tr7 = Application.DocumentManager.MdiActiveDocument.TransactionManager.TopTransaction; - Env.Print(tr4.Transaction == tr5); - Env.Print(tr5 == tr7); - var trm = HostApplicationServices.WorkingDatabase.TransactionManager; - //var ptt = tr.GetObject(pl).GetClosestPointTo(pt,false); - //var pt1 = new Point3d(0, 0.00000000000001, 0); - //var pt2 = new Point3d(0, 0.00001, 0); - //Env.Print(Tolerance.Global.EqualPoint); - //Env.Print(pt1.IsEqualTo(pt2).ToString()); - //Env.Print(pt1.IsEqualTo(pt2,new Tolerance(0.0,1e-6)).ToString()); - //Env.Print((pt1 == pt2).ToString()); - //Env.Print((pt1 != pt2).ToString()); - - + using var tr = new DBTrans(); + var data = tr.GetObject(res.ObjectId); + data.RemoveXData(appname, DxfCode.ExtendedDataAsciiString); + ed.WriteMessage(data.XData.ToString()); } + } - - public Database Getdb() - { - var db = Application.DocumentManager.MdiActiveDocument.Database; - return db; - } - - - public Document Getdoc() + [CommandMethod("PrintLayerName")] + public void PrintLayerName() + { + using var tr = new DBTrans(); + foreach (var layerRecord in tr.LayerTable.GetRecords()) { - var doc = Application.DocumentManager.MdiActiveDocument; - return doc; + tr.Editor.WriteMessage(layerRecord.Name); } - //public override void Initialize() - //{ - // Application.DocumentManager.MdiActiveDocument.Editor.WriteMessage("\nload...."); - //} - - //public override void Terminate() - //{ - // Application.DocumentManager.MdiActiveDocument.Editor.WriteMessage("\nunload...."); - //} } - public class BlockImportClass + [CommandMethod("testrec")] + public void TestRec() { + Point2d p1 = new(10000.2, 100000.5); + Point2d p2 = new(15000.9, 100000.5); + Point2d p3 = new(15000.9, 105000.7); + Point2d p4 = new(10000.2, 105000.7); - [CommandMethod("CBLL")] - public void cbll() - { - string filename = @"C:\Users\vic\Desktop\Drawing1.dwg"; - using var tr = new DBTrans(); - using var tr1 = new DBTrans(filename); - //tr.BlockTable.GetBlockFrom(filename, true); - string blkdefname = SymbolUtilityServices.RepairSymbolName(SymbolUtilityServices.GetSymbolNameFromPathName(filename, "dwg"), false); - tr.Database.Insert(blkdefname, tr1.Database, false); //插入了块定义,未插入块参照 + var p12 = p2 - p1; + var p23 = p3 - p2; + var p34 = p4 - p3; + var p41 = p1 - p4; + var p13 = p3 - p1; + var p24 = p4 - p2; - } + const double pi90 = Math.PI / 2; - [CommandMethod("CBL")] - public void CombineBlocksIntoLibrary() - { - Document doc = - Application.DocumentManager.MdiActiveDocument; - Editor ed = doc.Editor; - Database destDb = doc.Database; + Tools.TestTimes(1000000, "对角线", () => { + var result = false; + if (Math.Abs(p13.Length - p24.Length) <= 1e8) + { + result = p41.IsParallelTo(p12); + } - // Get name of folder from which to load and import blocks + }); - PromptResult pr = - ed.GetString("\nEnter the folder of source drawings: "); + Tools.TestTimes(1000000, "三次点乘", () => { + var result = false; - if (pr.Status != PromptStatus.OK) - return; - string pathName = pr.StringResult; + if (Math.Abs(p12.DotProduct(p23)) < 1e8 && + Math.Abs(p23.DotProduct(p34)) < 1e8 && + Math.Abs(p34.DotProduct(p41)) < 1e8) + { + result = true; + } - // Check the folder exists + }); - if (!Directory.Exists(pathName)) + Tools.TestTimes(1000000, "三次垂直", () => { + var result = false; + if (p12.IsParallelTo(p23) && + p23.IsParallelTo(p34) && + p34.IsParallelTo(p41)) { - ed.WriteMessage( - "\nDirectory does not exist: {0}", pathName - ); - return; + result = true; } - // Get the names of our DWG files in that folder + }); - string[] fileNames = Directory.GetFiles(pathName, "*.dwg"); - // A counter for the files we've imported + } - int imported = 0, failed = 0; - // For each file in our list - foreach (string fileName in fileNames) - { - // Double-check we have a DWG file (probably unnecessary) - if (fileName.EndsWith( - ".dwg", - StringComparison.InvariantCultureIgnoreCase - ) - ) - { - // Catch exceptions at the file level to allow skipping - - try - { - // Suggestion from Thorsten Meinecke... - - string destName = - SymbolUtilityServices.GetSymbolNameFromPathName( - fileName, "dwg" - ); - - // And from Dan Glassman... - - destName = - SymbolUtilityServices.RepairSymbolName( - destName, false - ); - - // Create a source database to load the DWG into - - using (Database db = new Database(false, true)) - { - // Read the DWG into our side database - - db.ReadDwgFile(fileName, FileShare.Read, true, ""); - bool isAnno = db.AnnotativeDwg; - - // Insert it into the destination database as - // a named block definition - - ObjectId btrId = destDb.Insert( - destName, - db, - false - ); - - if (isAnno) - { - // If an annotative block, open the resultant BTR - // and set its annotative definition status - - Transaction tr = - destDb.TransactionManager.StartTransaction(); - using (tr) - { - BlockTableRecord btr = - (BlockTableRecord)tr.GetObject( - btrId, - OpenMode.ForWrite - ); - btr.Annotative = AnnotativeStates.True; - tr.Commit(); - } - } - - // Print message and increment imported block counter - - ed.WriteMessage("\nImported from \"{0}\".", fileName); - imported++; - } - } - catch (System.Exception ex) - { - ed.WriteMessage( - "\nProblem importing \"{0}\": {1} - file skipped.", - fileName, ex.Message - ); - failed++; - } - } - } - ed.WriteMessage( - "\nImported block definitions from {0} files{1} in " + - "\"{2}\" into the current drawing.", - imported, - failed > 0 ? " (" + failed + " failed)" : "", - pathName - ); - } + + public Database Getdb() + { + var db = Application.DocumentManager.MdiActiveDocument.Database; + return db; } + + public Document Getdoc() + { + var doc = Application.DocumentManager.MdiActiveDocument; + return doc; + } } + + + +#pragma warning restore CS0219 // 变量已被赋值,但从未使用过它的值 diff --git a/tests/Test/Test.csproj b/tests/Test/Test.csproj index fa8ed34..8e3469a 100644 --- a/tests/Test/Test.csproj +++ b/tests/Test/Test.csproj @@ -1,32 +1,22 @@  - preview - enable + - - 1.0.0.* - 1.0.0.0 - False - git - - - NET45; + net45 true true + x64 - - - - - - + + 1701;1702;CS1685 + - - - + + 1701;1702;CS1685 + @@ -34,4 +24,5 @@ + diff --git a/tests/Test/TestAOP.cs b/tests/Test/TestAOP.cs new file mode 100644 index 0000000..b5b5c92 --- /dev/null +++ b/tests/Test/TestAOP.cs @@ -0,0 +1,66 @@ +//被注入的函数将不能使用断点, +//因此用户要充分了解才能使用 +#if false +/* + * 类库用户想侵入的命名空间是用户的, + * 所以需要用户手动进行AOP.Run(), + * 默认情况不侵入用户的命令,必须用户手动启用此功能; + * 启动执行策略之后,侵入命名空间下的命令, + * 此时有拒绝特性的策略保证括免,因为用户肯定是想少写一个事务注入的特性; + */ +public class AutoAOP +{ + [IFoxInitialize] + public void Initialize() + { + AOP.Run(nameof(Test)); + } +} + +namespace Test +{ + /* + * 天秀的事务注入,让你告别事务处理 + * https://www.cnblogs.com/JJBox/p/16157578.html + */ + public class AopTestClass + { + //类不拒绝,这里拒绝 + [IFoxRefuseInjectionTransaction] + [CommandMethod("IFoxRefuseInjectionTransaction")] + public void IFoxRefuseInjectionTransaction() + { + } + + //不拒绝 + [CommandMethod("InjectionTransaction")] + public void InjectionTransaction() + { + //怎么用事务呢? + //直接用 DBTrans.Top + var dBTrans = new DBTrans(); + dBTrans.Commit(); + } + } + + //拒绝注入事务,写类上,则方法全都拒绝 + [IFoxRefuseInjectionTransaction] + public class AopTestClassRefuseInjection + { + //此时这个也是拒绝的..这里加特性只是无所谓 + [IFoxRefuseInjectionTransaction] + [CommandMethod("IFoxRefuseInjectionTransaction2")] + public void IFoxRefuseInjectionTransaction2() + { + //拒绝注入就要自己开事务,通常用在循环提交事务上面. + //另见 报错0x02 https://www.cnblogs.com/JJBox/p/10798940.html + using var tr = new DBTrans(); + } + + [CommandMethod("InjectionTransaction2")] + public void InjectionTransaction2() + { + } + } +} +#endif \ No newline at end of file diff --git a/tests/Test/TestCurve.cs b/tests/Test/TestCurve.cs new file mode 100644 index 0000000..4d5cd96 --- /dev/null +++ b/tests/Test/TestCurve.cs @@ -0,0 +1,158 @@ +using System.Security.Policy; + +namespace Test +{ + public class TestGraph + { + [CommandMethod("testpointindict")] + public void TestPointInDict() + { + var pt1 = new Point3d(0.0255, 0.452, 0); + var pt2 = new Point3d(0.0255001, 0.452003, 0); + var pt3 = new Point3d(0.0255002, 0.4520001, 0); + var pt4 = new Point3d(0.0255450, 0.45287893, 0); + var pt5 = new Point3d(0.02554935, 0.452092375, 0); + var dict = new Dictionary + { + { pt1, 1 }, + { pt2, 2 }, + { pt3, 3 }, + { pt4, 4 }, + { pt5, 5 } + }; + Env.Print(dict[pt1]); + } + + [CommandMethod("testgraph")] + public void TestGraph1() + { + using var tr = new DBTrans(); + var ents = Env.Editor.SSGet()?.Value?.GetEntities(); + if (ents == null) + return; + Tools.TestTimes2(1, "new", () => { + + + var res = ents.GetAllCycle(); + + //res.ForEach((i, t) => t.ForWrite(e => e.ColorIndex = i + 1)); + Env.Print(res.Count()); + tr.CurrentSpace.AddEntity(res); + }); + + } + + [CommandMethod("testgraphspeed")] + public void TestGraphspeed() + { + using var tr = new DBTrans(); + var ents = Env.Editor.SSGet()?.Value?.GetEntities(); + if (ents == null) + return; + + + var graph = new IFoxCAD.Cad.Graph(); // 为了调试先把图的访问改为internal + + foreach (var curve in ents) + { + + graph.AddEdge(curve.GetGeCurve()); + + + } + //新建 dfs + var dfs = new DepthFirst(); +#if true + Tools.TestTimes2(100, "new", () => { + // 查询全部的 闭合环 + dfs.FindAll(graph); + }); + Tools.TestTimes2(1000, "new", () => { + // 查询全部的 闭合环 + dfs.FindAll(graph); + }); +#else + Tools.TestTimes2(100, "old", () => { + // 查询全部的 闭合环 + dfs.FindAll(graph); + }); + Tools.TestTimes2(1000, "old", () => { + // 查询全部的 闭合环 + dfs.FindAll(graph); + }); +#endif + //res.ForEach((i, t) => t.ForWrite(e => e.ColorIndex = i + 1)); + + //tr.CurrentSpace.AddEntity(res); + + } + } + + + + + public class TestCurve + { + [CommandMethod("testbreakcurve")] + public void TestBreakCurve() + { + using var tr = new DBTrans(); + var ents = Env.Editor.SSGet().Value.GetEntities(); + var tt = CurveEx.BreakCurve(ents.ToList()); + tt.ForEach(t => t.ForWrite(e => e.ColorIndex = 1)); + tr.CurrentSpace.AddEntity(tt); + } + + [CommandMethod("testCurveCurveIntersector3d")] + public void TestCurveCurveIntersector3d() + { + using var tr = new DBTrans(); + var ents = Env.Editor.SSGet().Value.GetEntities() + .Select(e => e.ToCompositeCurve3d()).ToList(); + + var cci3d = new CurveCurveIntersector3d(); + + + for (int i = 0; i < ents.Count; i++) + { + var gc1 = ents[i]; + var int1 = gc1.GetInterval(); + //var pars1 = paramss[i]; + for (int j = i; j < ents.Count; j++) + { + var gc2 = ents[j]; + //var pars2 = paramss[j]; + var int2 = gc2.GetInterval(); + cci3d.Set(gc1, gc2, int1, int2, Vector3d.ZAxis); + var d = cci3d.OverlapCount(); + var a = cci3d.GetIntersectionRanges(); + Env.Print($"{a[0].LowerBound}-{a[0].UpperBound} and {a[1].LowerBound}-{a[1].UpperBound}"); + for (int m = 0; m < d; m++) + { + var b = cci3d.GetOverlapRanges(m); + Env.Print($"{b[0].LowerBound}-{b[0].UpperBound} and {b[1].LowerBound}-{b[1].UpperBound}"); + } + + for (int k = 0; k < cci3d.NumberOfIntersectionPoints; k++) + { + //var a = cci3d.GetOverlapRanges(k); + //var b = cci3d.IsTangential(k); + //var c = cci3d.IsTransversal(k); + //var d = cci3d.OverlapCount(); + //var e = cci3d.OverlapDirection(); + var pt = cci3d.GetIntersectionParameters(k); + var pts = cci3d.GetIntersectionPoint(k); + Env.Print(pts); + } + + + + } + + } + // var tt = CurveEx.Topo(ents.ToList()); + //tt.ForEach(t => t.ForWrite(e => e.ColorIndex = 1)); + //tr.CurrentSpace.AddEntity(tt); + } + } +} \ No newline at end of file diff --git a/tests/Test/TestDBTrans.cs b/tests/Test/TestDBTrans.cs new file mode 100644 index 0000000..230882e --- /dev/null +++ b/tests/Test/TestDBTrans.cs @@ -0,0 +1,78 @@ +namespace Test; + +public class TestTrans +{ + [CommandMethod("testtr")] + public void Testtr() + { + string filename = @"C:\Users\vic\Desktop\test.dwg"; + using var tr = new DBTrans(filename); + tr.ModelSpace.AddCircle(new Point3d(10, 10, 0), 20); + //tr.Database.SaveAs(filename,DwgVersion.Current); + tr.SaveDwgFile(); + } + [CommandMethod("testifoxcommit")] + public void Testifoxcommit() + { + + using var tr = new DBTrans(); + tr.ModelSpace.AddCircle(new Point3d(0, 0, 0), 20); + tr.Abort(); + //tr.Commit(); + } + + // AOP 应用 预计示例: + // 1. 无参数 + //[AOP] + //[CommandMethod("TESTAOP")] + //public void testaop() + //{ + // // 不用 using var tr = new DBTrans(); + // var tr = DBTrans.Top; + // tr.ModelSpace.AddCircle(new Point3d(0, 0, 0), 20); + //} + + // 2. 有参数 + //[AOP("file")] + //[CommandMethod("TESTAOP")] + //public void testaop() + //{ + // // 不用 using var tr = new DBTrans(file); + // var tr = DBTrans.Top; + // tr.ModelSpace.AddCircle(new Point3d(0, 0, 0), 20); + //} + + + [CommandMethod("testpt")] + public void TestPt() + { + //var pt = Env.Editor.GetPoint("pick pt:").Value; + //var pl = Env.Editor.GetEntity("pick pl").ObjectId; + + var tr1 = HostApplicationServices.WorkingDatabase.TransactionManager.TopTransaction; + using var tr2 = new DBTrans(); + var tr3 = HostApplicationServices.WorkingDatabase.TransactionManager.TopTransaction; + var tr6 = Application.DocumentManager.MdiActiveDocument.TransactionManager.TopTransaction; + Env.Print(tr2.Transaction == tr3); + Env.Print(tr3 == tr6); + using var tr4 = new DBTrans(); + var tr5 = HostApplicationServices.WorkingDatabase.TransactionManager.TopTransaction; + var tr7 = Application.DocumentManager.MdiActiveDocument.TransactionManager.TopTransaction; + Env.Print(tr4.Transaction == tr5); + Env.Print(tr5 == tr7); + var trm = HostApplicationServices.WorkingDatabase.TransactionManager; + + //var ptt = tr.GetObject(pl).GetClosestPointTo(pt,false); + //var pt1 = new Point3d(0, 0.00000000000001, 0); + //var pt2 = new Point3d(0, 0.00001, 0); + //Env.Print(Tolerance.Global.EqualPoint); + //Env.Print(pt1.IsEqualTo(pt2).ToString()); + //Env.Print(pt1.IsEqualTo(pt2,new Tolerance(0.0,1e-6)).ToString()); + //Env.Print((pt1 == pt2).ToString()); + //Env.Print((pt1 != pt2).ToString()); + + + + } + +} diff --git a/tests/Test/TestEnt.cs b/tests/Test/TestEnt.cs new file mode 100644 index 0000000..e3cccf7 --- /dev/null +++ b/tests/Test/TestEnt.cs @@ -0,0 +1,40 @@ +namespace Test; + +public class TestEnt +{ + [CommandMethod("TestEntRoration")] + public void TestEntRoration() + { + var line = new Line(new(0,0,0),new(100,0,0)); + + using var tr = new DBTrans(); + tr.CurrentSpace.AddEntity(line); + var line2 = line.Clone() as Line; + tr.CurrentSpace.AddEntity(line2); + line2.Rotation(new(100, 0, 0), Math.PI / 2); + + + } + + + [CommandMethod("Testtypespeed")] + public void TestTypeSpeed() + { + var line = new Line(); + var line1 = line as Entity; + Tools.TestTimes(100000, "is 匹配:", () => + { + var t = line1 is Line; + }); + Tools.TestTimes(100000, "name 匹配:", () => + { + //var t = line.GetType().Name; + var tt = line1.GetType().Name == nameof(Line); + }); + Tools.TestTimes(100000, "dxfname 匹配:", () => + { + //var t = line.GetType().Name; + var tt = line1.GetRXClass().DxfName == nameof(Line); + }); + } +} diff --git a/tests/Test/TestFileDatabase.cs b/tests/Test/TestFileDatabase.cs new file mode 100644 index 0000000..7493f30 --- /dev/null +++ b/tests/Test/TestFileDatabase.cs @@ -0,0 +1,29 @@ +/************************************************************** +*作者:Leon +*创建时间:2022/2/11 9:55:32 +**************************************************************/ +namespace Test +{ + public class TestFileDatabase + { + [CommandMethod("Test_FileDatabaseInit")] + public void TestDatabase() + { + try + { + var fileName = @"C:\Users\Administrator\Desktop\合并详图测试BUG.dwg"; + using DBTrans trans = new(fileName); + trans.ModelSpace.AddEntity(new Line(new(0, 0, 0), new(1000, 1000, 0))); + if (trans.Document is not null && trans.Document.IsActive) + trans.Document.SendStringToExecute("_qsave\n", false, true, true); + else + trans.Database.SaveAs(fileName, DwgVersion.AC1021); + } + catch (System.Exception e) + { + System.Windows.MessageBox.Show(e.Message); + } + + } + } +} \ No newline at end of file diff --git a/tests/Test/TestJig.cs b/tests/Test/TestJig.cs new file mode 100644 index 0000000..605958c --- /dev/null +++ b/tests/Test/TestJig.cs @@ -0,0 +1,335 @@ +namespace Test; +using System.Windows.Forms; + +public class Commands_Jig +{ + //已在数据库的图元如何进入jig + [CommandMethod("TestCmd_jig33")] + public static void TestCmd_jig33() + { + Circle cir; + using var tr = new DBTrans(); + var per = tr.Editor.GetEntity("\n点选圆形:"); + if (per.Status != PromptStatus.OK) + return; + cir = tr.GetObject(per.ObjectId, OpenMode.ForWrite); + + if (cir == null) + return; + var oldSp = cir.StartPoint; + JigEx moveJig = null; + moveJig = new JigEx((mousePoint, drawEntitys) => { + moveJig.SetOptions(oldSp);//回调过程中也可以修改基点 + //cir.UpgradeOpen();//已经提权了,所以这里不需要提权 + cir.Move(cir.StartPoint, mousePoint); + //cir.DowngradeOpen(); + + //此处会Dispose图元, + //所以此处不加入已经在数据库的图元,而是加入new Entity的. + //drawEntitys.Enqueue(cir); + }); + moveJig.SetOptions(cir.GeometricExtents.MinPoint, orthomode: true); + + //此处详见方法注释 + moveJig.DatabaseEntityDraw(draw => { + draw.RawGeometry.Draw(cir); + }); + + while (true) + { + var prDrag = moveJig.Drag(); + if (prDrag.Status == PromptStatus.OK) + break; + } + } + + + //不在数据库的图元如何进入jig + [CommandMethod("TestCmd_Jig44")] + public void TestCmd_Jig44() + { + using var tr = new DBTrans(); + var per = Env.Editor.GetEntity("\n请选择一条多段线:"); + if (per.Status != PromptStatus.OK) + return; + var ent = tr.GetObject(per.ObjectId, OpenMode.ForWrite); + if (ent is not Polyline pl) + return; + + /* + * 鼠标采样器执行时修改鼠标基点 + * 原因: 多段线与鼠标垂直点作为 BasePoint,jig鼠标点为确定点 + * 所以需要先声明再传入指针,但是我发现null也可以. + */ + JigEx jig = null; + JigPromptPointOptions options = null; + jig = new JigEx((mousePoint, drawEntitys) => { + var closestPt = pl.GetClosestPointTo(mousePoint, false); + + //回调过程中SetOptions会覆盖配置,所以如果想增加关键字或者修改基点, + //不要这样做: jig.SetOptions(closestPt) 而是使用底层暴露 + options.BasePoint = closestPt; + + //需要避免重复加入同一个关键字 + if (!options.Keywords.Contains("A")) + options.Keywords.Add("A"); + + //生成文字 + var dictString = (pl.GetDistAtPoint(closestPt) * 0.001).ToString("0.00"); + var acText = new TextInfo(dictString, closestPt, AttachmentPoint.BaseLeft, textHeight: 200) + .AddDBTextToEntity(); + + //加入刷新队列 + drawEntitys.Enqueue(acText); + }); + + options = jig.SetOptions(per.PickedPoint); + + // 如果没有这个,那么空格只会是 PromptStatus.None 而不是 PromptStatus.Keyword + // options.Keywords.Add(" ", " ", "空格结束啊"); + // jig.SetSpaceIsKeyword(); + + bool flag = true; + while (flag) + { + var pr = jig.Drag(); + if (pr.Status == PromptStatus.Keyword) + { + switch (pr.StringResult) + { + case "A": + tr.Editor.WriteMessage($"\n 您触发了关键字{pr.StringResult}"); + flag = false; + break; + case " ": + tr.Editor.WriteMessage("\n 触发关键字空格"); + flag = false; + break; + } + } + else if (pr.Status != PromptStatus.OK)//PromptStatus.None == 右键,空格,回车,都在这里结束 + { + tr.Editor.WriteMessage(Environment.NewLine + pr.Status.ToString()); + return; + } + else + flag = false; + } + tr.CurrentSpace.AddEntity(jig.Entitys); + } + + [CommandMethod("TestCmd_loop")] + public void TestCmd_loop() + { + DocumentCollection dm = + Autodesk.AutoCAD.ApplicationServices.Application.DocumentManager; + Editor ed = dm.MdiActiveDocument.Editor; + // Create and add our message filter + MyMessageFilter filter = new(); + System.Windows.Forms.Application.AddMessageFilter(filter); + // Start the loop + while (true) + { + // Check for user input events + System.Windows.Forms.Application.DoEvents(); + // Check whether the filter has set the flag + if (filter.bCanceled == true) + { + ed.WriteMessage("\nLoop cancelled."); + break; + } + ed.WriteMessage($"\nInside while loop...and {filter.Key}"); + } + // We're done - remove the message filter + System.Windows.Forms.Application.RemoveMessageFilter(filter); + } + // Our message filter class + public class MyMessageFilter : IMessageFilter + { + public const int WM_KEYDOWN = 0x0100; + public bool bCanceled = false; + public Keys Key { get; private set; } + public bool PreFilterMessage(ref Message m) + { + if (m.Msg == WM_KEYDOWN) + { + // Check for the Escape keypress + Keys kc = (Keys)(int)m.WParam & Keys.KeyCode; + if (m.Msg == WM_KEYDOWN && kc == Keys.Escape) + { + bCanceled = true; + } + Key = kc; + // Return true to filter all keypresses + return true; + } + // Return false to let other messages through + return false; + } + } + + + [CommandMethod("TestCmd_QuickText")] + static public void TestCmd_QuickText() + { + var dm = Autodesk.AutoCAD.ApplicationServices.Application.DocumentManager; + var doc = dm.MdiActiveDocument; + var db = doc.Database; + var ed = doc.Editor; + + PromptStringOptions pso = new("\nEnter text string") + { + AllowSpaces = true + }; + var pr = ed.GetString(pso); + if (pr.Status != PromptStatus.OK) + return; + + var tr = doc.TransactionManager.StartTransaction(); + using (tr) + { + BlockTableRecord btr = + (BlockTableRecord)tr.GetObject( + db.CurrentSpaceId, OpenMode.ForWrite + ); + // Create the text object, set its normal and contents + + var acText = new TextInfo(pr.StringResult, + Point3d.Origin, + AttachmentPoint.BaseLeft, textHeight: 200) + .AddDBTextToEntity(); + + acText.Normal = ed.CurrentUserCoordinateSystem.CoordinateSystem3d.Zaxis; + btr.AppendEntity(acText); + tr.AddNewlyCreatedDBObject(acText, true); + + // Create our jig + var pj = new TextPlacementJig(tr, db, acText); + // Loop as we run our jig, as we may have keywords + PromptStatus stat = PromptStatus.Keyword; + while (stat == PromptStatus.Keyword) + { + PromptResult res = ed.Drag(pj); + stat = res.Status; + if ( + stat != PromptStatus.OK && + stat != PromptStatus.Keyword + ) + return; + } + tr.Commit(); + } + } + class TextPlacementJig : EntityJig + { + // Declare some internal state + readonly Database _db; + readonly Transaction _tr; + Point3d _position; + double _angle, _txtSize; + // Constructor + public TextPlacementJig( + Transaction tr, Database db, Entity ent + ) : base(ent) + { + _db = db; + _tr = tr; + _angle = 0; + _txtSize = 1; + } + protected override SamplerStatus Sampler( + JigPrompts jp + ) + { + // We acquire a point but with keywords + JigPromptPointOptions po = + new( + "\nPosition of text" + ); + po.UserInputControls = + (UserInputControls.Accept3dCoordinates | + UserInputControls.NullResponseAccepted | + UserInputControls.NoNegativeResponseAccepted | + UserInputControls.GovernedByOrthoMode); + po.SetMessageAndKeywords( + "\nSpecify position of text or " + + "[Bold/Italic/LArger/Smaller/" + + "ROtate90/LEft/Middle/RIght]: ", + "Bold Italic LArger Smaller " + + "ROtate90 LEft Middle RIght" + ); + PromptPointResult ppr = jp.AcquirePoint(po); + if (ppr.Status == PromptStatus.Keyword) + { + switch (ppr.StringResult) + { + case "Bold": + { + break; + } + case "Italic": + { + break; + } + case "LArger": + { + // Multiple the text size by two + _txtSize *= 2; + break; + } + case "Smaller": + { + // Divide the text size by two + _txtSize /= 2; + break; + } + case "ROtate90": + { + // To rotate clockwise we subtract 90 degrees and + // then normalise the angle between 0 and 360 + _angle -= Math.PI / 2; + while (_angle < Math.PI * 2) + { + _angle += Math.PI * 2; + } + break; + } + case "LEft": + { + break; + } + case "RIght": + { + break; + } + case "Middle": + { + break; + } + } + return SamplerStatus.OK; + } + else if (ppr.Status == PromptStatus.OK) + { + // Check if it has changed or not (reduces flicker) + if ( + _position.DistanceTo(ppr.Value) < + Tolerance.Global.EqualPoint + ) + return SamplerStatus.NoChange; + _position = ppr.Value; + return SamplerStatus.OK; + } + return SamplerStatus.Cancel; + } + protected override bool Update() + { + // Set properties on our text object + DBText txt = (DBText)Entity; + txt.Position = _position; + txt.Height = _txtSize; + txt.Rotation = _angle; + return true; + } + } +} diff --git a/tests/Test/TestLisp.cs b/tests/Test/TestLisp.cs new file mode 100644 index 0000000..c6e1f47 --- /dev/null +++ b/tests/Test/TestLisp.cs @@ -0,0 +1,122 @@ +namespace Test +{ + public class TestLisp + { + //定义lisp函数 + [LispFunction("LispTest_RunLisp")] + public static object LispTest_RunLisp(ResultBuffer rb) + { + CmdTest_RunLisp(); + return null!; + } + + //模态命令,只有当CAD发出命令提示或当前没有其他的命令或程序活动的时候才可以被触发 + [CommandMethod("CmdTest_RunLisp1", CommandFlags.Modal)] + //透明命令,可以在一个命令提示输入的时候触发例如正交切换,zoom等 + [CommandMethod("CmdTest_RunLisp2", CommandFlags.Transparent)] + //选择图元之后执行命令将可以从 获取图元 + [CommandMethod("CmdTest_RunLisp3", CommandFlags.UsePickSet)] + //命令执行前已选中部分实体.在命令执行过程中这些标记不会被清除 + [CommandMethod("CmdTest_RunLisp4", CommandFlags.Redraw)] + //命令不能在透视图中使用 + [CommandMethod("CmdTest_RunLisp5", CommandFlags.NoPerspective)] + //命令不能通过 MULTIPLE命令 重复触发 + [CommandMethod("CmdTest_RunLisp6", CommandFlags.NoMultiple)] + //不允许在模型空间使用命令 + [CommandMethod("CmdTest_RunLisp7", CommandFlags.NoTileMode)] + //不允许在布局空间使用命令 + [CommandMethod("CmdTest_RunLisp8", CommandFlags.NoPaperSpace)] + //命令不能在OEM产品中使用 + [CommandMethod("CmdTest_RunLisp9", CommandFlags.NoOem)] + //不能直接使用命令名调用,必须使用 组名.全局名 调用 + [CommandMethod("CmdTest_RunLisp10", CommandFlags.Undefined)] + //定义lisp方法.已废弃 请使用lispfunction + [CommandMethod("CmdTest_RunLisp11", CommandFlags.Defun)] + //命令不会被存储在新的命令堆上 + [CommandMethod("CmdTest_RunLisp12", CommandFlags.NoNewStack)] + //命令不能被内部锁定(命令锁) + [CommandMethod("CmdTest_RunLisp13", CommandFlags.NoInternalLock)] + //调用命令的文档将会被锁定为只读 + [CommandMethod("CmdTest_RunLisp14", CommandFlags.DocReadLock)] + //调用命令的文档将会被锁定,类似document.lockdocument + [CommandMethod("CmdTest_RunLisp15", CommandFlags.DocExclusiveLock)] + //命令在CAD运行期间都能使用,而不只是在当前文档 + [CommandMethod("CmdTest_RunLisp16", CommandFlags.Session)] + //获取用户输入时,可以与属性面板之类的交互 + [CommandMethod("CmdTest_RunLisp17", CommandFlags.Interruptible)] + //命令不会被记录在命令历史记录 + [CommandMethod("CmdTest_RunLisp18", CommandFlags.NoHistory)] + //命令不会被 UNDO取消 + [CommandMethod("CmdTest_RunLisp19", CommandFlags.NoUndoMarker)] + //不能在参照块中使用命令 + [CommandMethod("CmdTest_RunLisp20", CommandFlags.NoBlockEditor)] +#if ac2009 + //acad09增,不会被动作录制器 捕捉到 + [CommandMethod("CmdTest_RunLisp21", CommandFlags.NoActionRecording)] + //acad09增,会被动作录制器捕捉 + [CommandMethod("CmdTest_RunLisp22", CommandFlags.ActionMacro)] +#endif +#if !NET35 + //推断约束时不能使用命令 + [CommandMethod("CmdTest_RunLisp23", CommandFlags.NoInferConstraint)] + //命令允许在选择图元时临时显示动态尺寸 + [CommandMethod("CmdTest_RunLisp24", CommandFlags.TempShowDynDimension)] +#endif + public static void CmdTest_RunLisp() + { + // 测试方法1: (command "CmdTest_RunLisp1") + // 测试方式2: (LispTest_RunLisp) + var dm = Application.DocumentManager; + var doc = dm.MdiActiveDocument; + var ed = doc.Editor; + + var sb = new StringBuilder(); + foreach (var item in Enum.GetValues(typeof(EditorEx.RunLispFlag))) + { + sb.Append((byte)item); + sb.Append(','); + } + sb.Remove(sb.Length - 1, 1); + var option = new PromptIntegerOptions($"\n输入RunLispFlag枚举值:[{sb}]"); + var ppr = ed.GetInteger(option); + + if (ppr.Status != PromptStatus.OK) + return; + var flag = (EditorEx.RunLispFlag)ppr.Value; + + if (flag == EditorEx.RunLispFlag.AdsQueueexpr) + { + // 同步 + Env.Editor.RunLisp("(setq a 10)(princ)", + EditorEx.RunLispFlag.AdsQueueexpr); + Env.Editor.RunLisp("(princ a)", + EditorEx.RunLispFlag.AdsQueueexpr);//成功输出 + } + else if (flag == EditorEx.RunLispFlag.AcedEvaluateLisp) + { + // 使用(command "CmdTest_RunLisp1")发送,然后 !b 查看变量,acad08是有值的,高版本是null + var strlisp0 = "(setq b 20)"; + var res0 = Env.Editor.RunLisp(strlisp0, + EditorEx.RunLispFlag.AcedEvaluateLisp); //有lisp的返回值 + + var strlisp1 = "(defun f1( / )(princ \"aa\"))"; + var res1 = Env.Editor.RunLisp(strlisp1, + EditorEx.RunLispFlag.AcedEvaluateLisp); //有lisp的返回值 + + var strlisp2 = "(defun f2( / )(command \"line\"))"; + var res2 = Env.Editor.RunLisp(strlisp2, + EditorEx.RunLispFlag.AcedEvaluateLisp); //有lisp的返回值 + } + else if (flag == EditorEx.RunLispFlag.SendStringToExecute) + { + //测试异步 + //(command "CmdTest_RunLisp1")和(LispTest_RunLisp)4都是异步 + var str = "(setq c 40)(princ)"; + Env.Editor.RunLisp(str, + EditorEx.RunLispFlag.SendStringToExecute); //异步,后发送 + Env.Editor.RunLisp("(princ c)", + EditorEx.RunLispFlag.AdsQueueexpr); //同步,先发送了,输出是null + } + } + } +} diff --git a/tests/Test/TestLoop.cs b/tests/Test/TestLoop.cs new file mode 100644 index 0000000..8992df9 --- /dev/null +++ b/tests/Test/TestLoop.cs @@ -0,0 +1,36 @@ +using IFoxCAD.Basal; + +using System.Diagnostics.CodeAnalysis; +namespace Test +{ + public class TestLoop + { + [CommandMethod("testloop")] + public void Testloop() + { + var loop = new LoopList + { + 0, + 1, + 2, + 3, + 4, + 5 + }; + + + + + Env.Print(loop); + + loop.SetFirst(loop.Last); + Env.Print(loop); + Env.Print(loop.Min()); + loop.SetFirst(new LoopListNode (loop.Min() ,loop)); + Env.Print(loop); + + + + } + } +} diff --git a/tests/Test/TestMirrorFile.cs b/tests/Test/TestMirrorFile.cs new file mode 100644 index 0000000..b5f212f --- /dev/null +++ b/tests/Test/TestMirrorFile.cs @@ -0,0 +1,73 @@ +public class MirrorFile +{ + const string file = "D:/JX.dwg"; + const string fileSave = "D:/JX222.dwg"; + + /// + /// 测试:后台打开图纸,镜像文字是否存在文字偏移 + /// 答案:不存在 + /// + [CommandMethod("CmdTest_MirrorFile")] + public static void CmdTest_MirrorFile() + { + using var tr = new DBTrans(file, openMode: FileOpenMode.OpenForReadAndReadShare); + + tr.BlockTable.Change(tr.ModelSpace.ObjectId, modelSpace => { + foreach (ObjectId entId in modelSpace) + { + var dbText = tr.GetObject(entId, OpenMode.ForRead)!; + if (dbText is null) + continue; + + dbText.UpgradeOpen(); + var pos = dbText.Position; + //text.Move(pos, Point3d.Origin); + //Y轴 + dbText.Mirror(Point3d.Origin, new Point3d(0, 1, 0)); + //text.Move(Point3d.Origin, pos); + dbText.DowngradeOpen(); + } + }); + tr.Database.SaveAs(fileSave, DwgVersion.AC1021/*AC1021 AutoCAD 2007/2008/2009.*/); + } + + /// + /// 测试:后台设置 dbText.IsMirroredInX 属性会令文字偏移 + /// 答案:存在,并提出解决方案 + /// + [CommandMethod("CmdTest_MirrorFile2")] + public static void CmdTest_MirrorFile2() + { + using var tr = new DBTrans(file, openMode: FileOpenMode.OpenForReadAndReadShare); + + tr.Database.DBTextDeviation(() => { + + var yaxis = new Point3d(0, 1, 0); + tr.BlockTable.Change(tr.ModelSpace.ObjectId, modelSpace => { + foreach (ObjectId entId in modelSpace) + { + var entity = tr.GetObject(entId, OpenMode.ForWrite)!; + if (entity is DBText dbText) + { + dbText.Mirror(Point3d.Origin, yaxis); + dbText.IsMirroredInX = true; //这句将导致文字偏移 + + //指定文字的垂直对齐方式 + if (dbText.VerticalMode == TextVerticalMode.TextBase) + dbText.VerticalMode = TextVerticalMode.TextBottom; + + //指定文字的水平对齐方式 + dbText.HorizontalMode = dbText.HorizontalMode switch + { + TextHorizontalMode.TextLeft => TextHorizontalMode.TextRight, + TextHorizontalMode.TextRight => TextHorizontalMode.TextLeft, + _ => dbText.HorizontalMode + }; + dbText.AdjustAlignment(tr.Database); + } + } + }); + }); + tr.Database.SaveAs(fileSave, DwgVersion.AC1021/*AC1021 AutoCAD 2007/2008/2009.*/); + } +} \ No newline at end of file diff --git a/tests/Test/TestPoint.cs b/tests/Test/TestPoint.cs new file mode 100644 index 0000000..46efe2e --- /dev/null +++ b/tests/Test/TestPoint.cs @@ -0,0 +1,160 @@ +using System.Diagnostics; + +namespace Test +{ + public class TestPoint + { + /// + /// 红黑树排序点集 + /// + [CommandMethod("TestptSortedSet")] + public void TestptSortedSet() + { + var ss1 = new SortedSet(); + ss1.Add(new Point2d(1, 1)); + ss1.Add(new Point2d(4.6, 2)); + ss1.Add(new Point2d(8, 3)); + ss1.Add(new Point2d(4, 3)); + ss1.Add(new Point2d(5, 40)); + ss1.Add(new Point2d(6, 5)); + ss1.Add(new Point2d(1, 6)); + ss1.Add(new Point2d(7, 6)); + ss1.Add(new Point2d(9, 6)); + + /*判断区间,超过就中断*/ + foreach (var item in ss1) + { + if (item.X > 3 && item.X < 7) + { + Debug.WriteLine(item); + } + else if (item.X >= 7) + { + break; + } + } + } + + + + [CommandMethod("TestptGethash")] + public void TestptGethash() + { + // test + var pt = Env.Editor.GetPoint("pick pt").Value; + //Tools.TestTimes2(1_000_000, "新语法", () => { + // pt.GetHashString2(); + //}); + Tools.TestTimes2(1_000_000, "旧语法", () => { + pt.GetHashString(); + }); + } + + [CommandMethod("Testpoint3d")] + public void TestPoint3d() + { + Env.Print($"4位小数的hash:{new Point3d(0.0_001, 0.0_002, 0.0).GetHashCode()}"); + Env.Print($"5位小数的hash:{new Point3d(0.00_001, 0.00_002, 0.0).GetHashCode()}"); + Env.Print($"6位小数的hash:{new Point3d(0.000_001, 0.000_002, 0.0).GetHashCode()}"); + Env.Print($"7位小数的hash:{new Point3d(0.000_0_001, 0.000_0_002, 0.0).GetHashCode()}"); + Env.Print($"8位小数的hash:{new Point3d(0.000_00_001, 0.000_00_002, 0.0).GetHashCode()}"); + Env.Print($"9位小数的hash:{new Point3d(0.000_000_001, 0.000_000_002, 0.0).GetHashCode()}"); + Env.Print($"10位小数的hash:{new Point3d(0.000_000_0001, 0.000_000_0002, 0.0).GetHashCode()}"); + Env.Print($"10位小数的hash:{new Point3d(0.000_000_0001, 0.000_000_0001, 0.0).GetHashCode()}"); + + Env.Print($"11位小数的hash:{new Point3d(0.000_000_000_01, 0.000_000_000_02, 0.0).GetHashCode()}"); + Env.Print($"11位小数的hash:{new Point3d(0.000_000_000_01, 0.000_000_000_01, 0.0).GetHashCode()}"); + + Env.Print($"12位小数的hash:{new Point3d(0.000_000_000_001, 0.000_000_000_002, 0.0).GetHashCode()}"); + Env.Print($"12位小数的hash:{new Point3d(0.000_000_000_001, 0.000_000_000_001, 0.0).GetHashCode()}"); + + Env.Print($"13位小数的hash:{new Point3d(0.000_000_000_0001, 0.000_000_000_0002, 0.0).GetHashCode()}"); + Env.Print($"13位小数的hash:{new Point3d(0.000_000_000_0001, 0.000_000_000_0001, 0.0).GetHashCode()}"); + + Env.Print($"14位小数的hash:{new Point3d(0.000_000_000_000_01, 0.000_000_000_000_02, 0.0).GetHashCode()}"); + Env.Print($"14位小数的hash:{new Point3d(0.000_000_000_000_01, 0.000_000_000_000_01, 0.0).GetHashCode()}"); + + Env.Print($"15位小数的hash:{new Point3d(0.000_000_000_000_001, 0.000_000_000_000_002, 0.0).GetHashCode()}"); + Env.Print($"15位小数的hash:{new Point3d(0.000_000_000_000_001, 0.000_000_000_000_001, 0.0).GetHashCode()}"); + + Env.Print($"16位小数的hash:{new Point3d(0.000_000_000_000_000_1, 0.000_000_000_000_000_2, 0.0).GetHashCode()}"); + Env.Print($"16位小数的hash:{new Point3d(0.000_000_000_000_000_1, 0.000_000_000_000_000_1, 0.0).GetHashCode()}"); + + Env.Print($"17位小数的hash:{new Point3d(0.000_000_000_000_000_01, 0.000_000_000_000_000_02, 0.0).GetHashCode()}"); + Env.Print($"17位小数的hash:{new Point3d(0.000_000_000_000_000_01, 0.000_000_000_000_000_01, 0.0).GetHashCode()}"); + + Env.Print($"18位小数的hash:{new Point3d(0.000_000_000_000_000_001, 0.000_000_000_000_000_002, 0.0).GetHashCode()}"); + Env.Print($"18位小数的hash:{new Point3d(0.000_000_000_000_000_001, 0.000_000_000_000_000_001, 0.0).GetHashCode()}"); + + Env.Print($"19位小数的hash:{new Point3d(0.000_000_000_000_000_000_1, 0.000_000_000_000_000_000_2, 0.0).GetHashCode()}"); + Env.Print($"19位小数的hash:{new Point3d(0.000_000_000_000_000_000_1, 0.000_000_000_000_000_000_1, 0.0).GetHashCode()}"); + + Env.Print($"20位小数的hash:{new Point3d(0.000_000_000_000_000_000_01, 0.000_000_000_000_000_000_02, 0.0).GetHashCode()}"); + Env.Print($"20位小数的hash:{new Point3d(0.000_000_000_000_000_000_01, 0.000_000_000_000_000_000_01, 0.0).GetHashCode()}"); + } + + [CommandMethod("Testlistequalspeed")] + public void Testlistequalspeed() + { + var lst1 = new List { 1, 2, 3, 4 }; + var lst2 = new List { 1, 2, 3, 4}; + lst1.EqualsAll(null); + Tools.TestTimes2(1000000, "eqaulspeed:", () => { + lst1.EqualsAll(lst2); + }); + + + } + + [CommandMethod("Testcontains")] + public void Testcontains() + { + // test list and dict contains speed + var lst = new List { 1, 2, 3, 4 , 5,6,7,8,9,10, 11,12,13,14,15,16,17,18,19,20}; + var hashset = new HashSet { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20 }; + var dict = new Dictionary + { + { 1, 0 }, + { 2, 1 }, + { 3, 2 }, + { 4, 3 }, + { 5, 4 }, + { 6, 5 }, + { 7, 6 }, + { 8, 7 }, + { 9, 8 }, + { 10, 9 }, + { 11, 11 }, + { 12, 12 }, + { 13, 13 }, + { 14, 14 }, + { 15, 15 }, + { 16, 16 }, + { 17, 17 }, + { 18, 18 }, + { 19, 19 }, + { 20, 20 }, + }; + + Tools.TestTimes2(100_0000, "list:", () => { + lst.Contains(20); + }); + + Tools.TestTimes2(100_0000, "hashset:", () => { + hashset.Contains(20); + }); + + Tools.TestTimes2(100_0000, "dict:", () => { + dict.ContainsKey(20); + }); + + } + + + + + + } + + +} \ No newline at end of file diff --git a/tests/Test/TestQuadTree.cs b/tests/Test/TestQuadTree.cs new file mode 100644 index 0000000..7670eae --- /dev/null +++ b/tests/Test/TestQuadTree.cs @@ -0,0 +1,461 @@ +namespace Test; + +#pragma warning disable CS8632 // 只能在 "#nullable" 注释上下文内的代码中使用可为 null 的引用类型的注释。 +/* + * 这里属于用户调用例子, + * 调用时候必须要继承它,再提供给四叉树 + * 主要是用户可以扩展属性 + */ +public class CadEntity : QuadEntity +{ + public ObjectId ObjectId; + //这里加入其他字段 + public List? Link;//碰撞链 + public System.Drawing.Color Color; + public double Angle; + public CadEntity(ObjectId objectId, Rect box) : base(box) + { + ObjectId = objectId; + } + public int CompareTo(CadEntity? other) + { + if (other == null) + return -1; + return GetHashCode() ^ other.GetHashCode(); + } + public override int GetHashCode() + { + return (base.GetHashCode(), ObjectId.GetHashCode()).GetHashCode(); + } +} +#pragma warning restore CS8632 // 只能在 "#nullable" 注释上下文内的代码中使用可为 null 的引用类型的注释。 + + + + + +public partial class TestQuadTree +{ + QuadTree _quadTreeRoot; + #region 四叉树创建并加入 + [CommandMethod("Test_QuadTree")] + public void Test_QuadTree() + { + using var tr = new DBTrans(); + + Rect dbExt; + //使用数据库边界来进行 + var dbExtent = tr.Database.GetValidExtents3d(); + if (dbExtent == null) + { + //throw new ArgumentException("画一个矩形"); + + //这个初始值的矩形是很有意义, + //主要是四叉树分裂过程中产生多个Rect,Rect内有很多重复的double值,是否可以内存复用,以此减少内存大小? + //接着想了一下,Rect可以是int,long,这样可以利用位运算它扩展和缩小, + //最小就是1,并且可以控制四叉树深度,不至于无限递归. + //而且指针长度跟值是一样的,所以就不需要复用了,毕竟跳转一个函数地址挺麻烦的. + //但是因为啊惊懒的原因,并没有单独制作这样的矩形, + //而且非常糟糕的是,c#不支持模板约束运算符,使得值类型之间需要通过一层接口来委婉处理,拉低了效率..引用类型倒是无所谓.. + //要么忍着,要么换c++去搞四叉树吧 + dbExt = new Rect(0, 0, 1 << 10, 1 << 10); + } + else + { + var a = new Point2d(dbExtent.Value.MinPoint.X, dbExtent.Value.MinPoint.Y); + var b = new Point2d(dbExtent.Value.MaxPoint.X, dbExtent.Value.MaxPoint.Y); + dbExt = new Rect(a, b); + } + + //创建四叉树 + _quadTreeRoot = new QuadTree(dbExt); + + //数据库边界 + var pl = dbExt.ToPoints(); + var databaseBoundary = new List<(Point3d, double, double, double)> + { + (new Point3d(pl[0].X,pl[0].Y,0),0,0,0), + (new Point3d(pl[1].X,pl[1].Y,0),0,0,0), + (new Point3d(pl[2].X,pl[2].Y,0),0,0,0), + (new Point3d(pl[3].X,pl[3].Y,0),0,0,0), + }; + tr.CurrentSpace.AddPline(databaseBoundary); + + //生成多少个图元,导致cad会令undo出错(八叉树深度过大 treemax) + //int maximumItems = 30_0000; + int maximumItems = 1000; + + //随机图元生成 + List ces = new(); //用于随机获取图元 + Timer.RunTime(() => { + //生成外边界和随机圆形 + var grc = GenerateRandomCircle(maximumItems, dbExt); + foreach (var ent in grc) + { + //初始化图元颜色 + ent.ColorIndex = 1; //Color.FromRgb(0, 0, 0);//黑色 + var edge = ent.GeometricExtents; + //四叉树数据 + var entRect = new Rect(edge.MinPoint.X, edge.MinPoint.Y, edge.MaxPoint.X, edge.MaxPoint.Y); + var entId = tr.CurrentSpace.AddEntity(ent); + var ce = new CadEntity(entId, entRect) + { + Color = Utility.RandomColor + }; + ces.Add(ce); + /*加入随机点*/ + var p = edge.MinPoint + new Vector3d(10, 10, 0); + entRect = new Rect(p.Point2d(), p.Point2d()); + entId = tr.CurrentSpace.AddEntity(new DBPoint(p)); + var dbPointCe = new CadEntity(entId, entRect); + ces.Add(dbPointCe); + } + }, Timer.TimeEnum.Millisecond, "画圆消耗时间:");//30万图元±3秒.cad2021 + + //测试只加入四叉树的时间 + Timer.RunTime(() => { + for (int i = 0; i < ces.Count; i++) + { + _quadTreeRoot.Insert(ces[i]); + } + }, Timer.TimeEnum.Millisecond, "插入四叉树时间:");//30万图元±0.7秒.cad2021 + + tr.Editor.WriteMessage($"\n加入图元数量:{maximumItems}"); + } + + /// + /// 创建随机圆形 + /// + /// 创建数量 + /// 数据库边界 + static IEnumerable GenerateRandomCircle(int createNumber, Rect dbExt) + { + var x1 = (int)dbExt.X; + var x2 = (int)(dbExt.X + dbExt.Width); + var y1 = (int)dbExt.Y; + var y2 = (int)(dbExt.Y + dbExt.Height); + + var rand = Utility.GetRandom(); + for (int i = 0; i < createNumber; i++) + { + var x = rand.Next(x1, x2) + rand.NextDouble(); + var y = rand.Next(y1, y2) + rand.NextDouble(); + yield return EntityEx.CreateCircle(new Point3d(x, y, 0), rand.Next(1, 100)); //起点,终点 + } + } + + /*TODO 啊惊: 有点懒不想改了*/ +#if true2 + + //选择加入到四叉树 + [CommandMethod("CmdTest_QuadTree21")] + public void CmdTest_QuadTree21() + { + var dm = Acap.DocumentManager; + var doc = dm.MdiActiveDocument; + var db = doc.Database; + var ed = doc.Editor; + ed.WriteMessage("\n选择单个图元加入已有的四叉树"); + + var ss = ed.Ssget(); + if (ss.Count == 0) + return; + + AddQuadTreeRoot(db, ed, ss); + } + + //自动加入全图到四叉树 + [CommandMethod("CmdTest_QuadTree20")] + public void CmdTest_QuadTree20() + { + var dm = Acap.DocumentManager; + var doc = dm.MdiActiveDocument; + var db = doc.Database; + var ed = doc.Editor; + ed.WriteMessage("\n自动加入全图到四叉树"); + + var ss = new List(); + int entnum = 0; + var time1 = Timer.RunTime(() => { + db.Action(tr => { + db.TraverseBlockTable(tr, btRec => { + if (!btRec.IsLayout)//布局跳过 + return false; + + foreach (var item in btRec) + { + //var ent = item.ToEntity(tr); + ss.Add(item); + ++entnum;//图元数量:100000, 遍历全图时间:0.216秒 CmdTest_QuadTree2 + } + return false; + }); + }); + }); + ed.WriteMessage($"\n图元数量:{entnum}, 遍历全图时间:{time1 / 1000.0}秒"); + + //清空原有的 + _quadTreeRoot = null; + AddQuadTreeRoot(db, ed, ss); + } + + void AddQuadTreeRoot(Database db, Editor ed, List ss) + { + if (_quadTreeRoot is null) + { + ed.WriteMessage("\n四叉树是空的,重新初始化"); + + Rect dbExt; + //使用数据库边界来进行 + var dbExtent = db.GetValidExtents3d(); + if (dbExtent == null) + { + //throw new ArgumentException("画一个矩形"); + + //测试时候画个矩形,在矩形内画随机坐标的圆形 + dbExt = new Rect(0, 0, 32525, 32525); + } + else + { + dbExt = new Rect(dbExtent.Value.MinPoint.Point2d(), dbExtent.Value.MaxPoint.Point2d()); + } + _quadTreeRoot = new(dbExt); + } + + /* 测试: + * 为了测试删除内容释放了分支,再重复加入是否报错 + * 先创建 CmdTest_QuadTree1 + * 再减去 CmdTest_QuadTree0 + * 然后原有黑色边界,再生成边界 CmdTest_Create00,对比删除效果. + * 然后加入 CmdTest_QuadTree2 + * 然后原有黑色边界,再生成边界 CmdTest_Create00,对比删除效果. + */ + + List ces = new(); + db.Action(tr => { + ss.ForEach(entId => { + var ent = entId.ToEntity(tr); + if (ent is null) + return; + var edge = new EdgeEntity(ent); + //四叉树数据 + var ce = new CadEntity(entId, edge.Edge) + { + Color = Utility.RandomColor + }; + ces.Add(ce); + + edge.Dispose(); + }); + }); + + var time2 = Timer.RunTime(() => { + _quadTreeRoot.Insert(ces); + }); + ed.WriteMessage($"\n图元数量:{ces.Count}, 加入四叉树时间:{time2 / 1000.0}秒"); + } +#endif + + #endregion + + /*TODO 啊惊: 有点懒不想改了*/ +#if true2 + + #region 节点边界显示 + //四叉树减去节点 + [CommandMethod("CmdTest_QuadTree0")] + public void CmdTest_QuadTree0() + { + var dm = Acap.DocumentManager; + var doc = dm.MdiActiveDocument; + //var db = doc.Database; + var ed = doc.Editor; + ed.WriteMessage("\n四叉树减区"); + + if (_quadTreeRoot is null) + { + ed.WriteMessage("\n四叉树是空的"); + return; + } + var rect = GetCorner(ed); + if (rect is null) + return; + _quadTreeRoot.Remove(rect); + } + + //创建节点边界 + [CommandMethod("CmdTest_QuadTree00")] + public void CmdTest_CreateNodesRect() + { + var dm = Acap.DocumentManager; + var doc = dm.MdiActiveDocument; + var db = doc.Database; + var ed = doc.Editor; + ed.WriteMessage("\n创建边界"); + + if (_quadTreeRoot is null) + { + ed.WriteMessage("\n四叉树是空的"); + return; + } + + //此处发现了一个事务处理的bug,提交数量过多的时候,会导致 ctrl+z 无法回滚, + //需要把事务放在循环体内部 + //报错: 0x6B00500A (msvcr80.dll)处(位于 acad.exe 中)引发的异常: 0xC0000005: 写入位置 0xFFE00000 时发生访问冲突。 + //画出所有的四叉树节点边界,因为事务放在外面引起 + var nodeRects = new List(); + _quadTreeRoot.ForEach(node => { + nodeRects.Add(node); + return false; + }); + var rectIds = new List(); + foreach (var item in nodeRects)//Count = 97341 当数量接近这个量级 + { + db.Action(tr => { + var pts = item.ToPoints(); + var rec = EntityAdd.AddPolyLineToEntity(pts.ToPoint2d()); + rec.ColorIndex = 250; + rectIds.Add(tr.AddEntityToMsPs(db, rec)); + }); + } + db.Action(tr => { + db.CoverGroup(tr, rectIds); + }); + + //获取四叉树深度 + int dep = 0; + _quadTreeRoot.ForEach(node => { + dep = dep > node.Depth ? dep : node.Depth; + return false; + }); + ed.WriteMessage($"\n四叉树深度是: {dep}"); + } + #endregion + +#endif + + #region 四叉树查询节点 + //选择范围改图元颜色 + [CommandMethod("CmdTest_QuadTree3")] + public void CmdTest_QuadTree3() + { + Ssget(QuadTreeSelectMode.IntersectsWith); + } + + [CommandMethod("CmdTest_QuadTree4")] + public void CmdTest_QuadTree4() + { + Ssget(QuadTreeSelectMode.Contains); + } + + /// + /// 改颜色 + /// + /// + void Ssget(QuadTreeSelectMode mode) + { + using var tr = new DBTrans(); + + if (_quadTreeRoot is null) + return; + var rect = GetCorner(tr.Editor); + if (rect is null) + return; + + tr.Editor.WriteMessage("选择模式:" + mode); + + //仿选择集 + var ces = _quadTreeRoot.Query(rect, mode); + ces.ForEach(item => { + var ent = tr.GetObject(item.ObjectId, OpenMode.ForWrite); + ent.Color = Color.FromColor(item.Color); + ent.DowngradeOpen(); + ent.Dispose(); + }); + } + + /// + /// 交互获取 + /// + /// + /// +#pragma warning disable CS8632 // 只能在 "#nullable" 注释上下文内的代码中使用可为 null 的引用类型的注释。 + public static Rect? GetCorner(Editor ed) + { + var optionsA = new PromptPointOptions($"{Environment.NewLine}起点位置:"); + var pprA = ed.GetPoint(optionsA); + if (pprA.Status != PromptStatus.OK) + return null; + var optionsB = new PromptCornerOptions(Environment.NewLine + "输入矩形角点2:", pprA.Value) + { + UseDashedLine = true,//使用虚线 + AllowNone = true,//回车 + }; + var pprB = ed.GetCorner(optionsB); + if (pprB.Status != PromptStatus.OK) + return null!; + + return new Rect(new Point2d(pprA.Value.X, pprA.Value.Y), + new Point2d(pprB.Value.X, pprB.Value.Y), + true); + } +#pragma warning restore CS8632 // 只能在 "#nullable" 注释上下文内的代码中使用可为 null 的引用类型的注释。 + + #endregion +} + +//public partial class TestQuadTree +//{ +// public void Cmd_tt6() +// { +// using var tr = new DBTrans(); +// var ed = tr.Editor; +// //创建四叉树,默认参数无所谓 +// var TreeRoot = new QuadTree(new Rect(0, 0, 32525, 32525)); + +// var fil = OpFilter.Bulid(e => e.Dxf(0) == "LINE"); +// var psr = ed.SSGet("\n 选择需要连接的直线", fil); +// if (psr.Status != PromptStatus.OK) return; +// var LineEnts = new List(psr.Value.GetEntities(OpenMode.ForWrite)!); +// //将实体插入到四岔树 +// foreach (var line in LineEnts) +// { +// var edge = line.GeometricExtents; +// var entRect = new Rect(edge.MinPoint.X, edge.MinPoint.Y, edge.MaxPoint.X, edge.MaxPoint.Y); +// var ce = new CadEntity(line.Id, entRect) +// { +// //四叉树数据 +// Angle = line.Angle +// }; +// TreeRoot.Insert(ce); +// } + +// var ppo = new PromptPointOptions(Environment.NewLine + "\n指定标注点:<空格退出>") +// { +// AllowArbitraryInput = true,//任意输入 +// AllowNone = true //允许回车 +// }; +// var ppr = ed.GetPoint(ppo);//用户点选 +// if (ppr.Status != PromptStatus.OK) +// return; +// var rect = new Rect(ppr.Value.Point2d(), 100, 100); +// tr.CurrentSpace.AddEntity(rect.ToPolyLine());//显示选择靶标范围 + +// var nent = TreeRoot.FindNearEntity(rect);//查询最近实体,按逆时针 +// var ent = tr.GetObject(nent.ObjectId, OpenMode.ForWrite);//打开实体 +// ent.ColorIndex = Utility.GetRandom().Next(1, 256);//1~256随机色 +// ent.DowngradeOpen();//实体降级 +// ent.Dispose(); + +// var res = TreeRoot.Query(rect, QuadTreeSelectMode.IntersectsWith);//查询选择靶标范围相碰的ID +// res.ForEach(item => { +// if (item.Angle == 0 || item.Angle == Math.PI) //过滤直线角度为0或180的直线 +// { +// var ent = tr.GetObject(item.ObjectId, OpenMode.ForWrite); +// ent.ColorIndex = Utility.GetRandom().Next(1, 7); +// ent.DowngradeOpen(); +// ent.Dispose(); +// } +// }); +// } +//} \ No newline at end of file diff --git a/tests/Test/Testid.cs b/tests/Test/Testid.cs new file mode 100644 index 0000000..d93b823 --- /dev/null +++ b/tests/Test/Testid.cs @@ -0,0 +1,74 @@ +namespace Test +{ + public class Testid + { + [CommandMethod("testid")] + public void TestId() + { + using var tr = new DBTrans(); + Line line = new(new Point3d(0, 0, 0), new Point3d(1, 1, 0)); + tr.CurrentSpace.AddEntity(line); + tr.Dispose(); + + var res = Env.Editor.GetEntity("\npick ent:"); + if (res.Status == PromptStatus.OK) + { + res.ObjectId.Erase(); + } + //using (var tr = new DBTrans()) + //{ + // var res = Env.Editor.GetEntity("\npick ent:"); + // if(res.Status == PromptStatus.OK) + // { + // res.ObjectId.Erase(); + // } + + //} + } + + [CommandMethod("testmycommand")] + public void TestMyCommand() + { + using var dbtrans = new DBTrans(Env.Document, true, false); + using var trans = Env.Database.TransactionManager.StartTransaction(); + + var l1 = new Line(new Point3d(0, 0, 0), new Point3d(100, 100, 0)); + var blkred = trans.GetObject(Env.Database.CurrentSpaceId, OpenMode.ForWrite) as BlockTableRecord; + blkred.AppendEntity(l1); + trans.AddNewlyCreatedDBObject(l1, true); + trans.Commit(); + //dbtrans.Dispose(); + } + [CommandMethod("testtextstyle")] + public void TestTextStyle() + { + using var tr = new DBTrans(); + tr.TextStyleTable.Add("宋体", "宋体.ttf", 0.8); + + tr.TextStyleTable.Add("宋体1", FontTTF.宋体, 0.8); + tr.TextStyleTable.Add("仿宋体", FontTTF.仿宋, 0.8); + tr.TextStyleTable.Add("fsgb2312", FontTTF.仿宋GB2312, 0.8); + tr.TextStyleTable.Add("arial", FontTTF.Arial, 0.8); + tr.TextStyleTable.Add("romas", FontTTF.Romans, 0.8); + + + + tr.TextStyleTable.Add("daziti", ttr => + { + ttr.FileName = "ascii.shx"; + ttr.BigFontFileName = "gbcbig.shx"; + }); + } + + [CommandMethod("testtextstylechange")] + public void TestTextStyleChange() + { + using var tr = new DBTrans(); + + + tr.TextStyleTable.AddWithChange("宋体1", "simfang.ttf", height: 5); + tr.TextStyleTable.AddWithChange("仿宋体", "宋体.ttf"); + tr.TextStyleTable.AddWithChange("fsgb2312", "Romans", "gbcbig"); + } + } +} diff --git a/tests/Test/testConvexHull.cs b/tests/Test/testConvexHull.cs index ee792c6..19c3da7 100644 --- a/tests/Test/testConvexHull.cs +++ b/tests/Test/testConvexHull.cs @@ -10,12 +10,12 @@ using Autodesk.AutoCAD.DatabaseServices; using Autodesk.AutoCAD.Colors; -namespace test +namespace Test { - public class testConvexHull + public class TestConvexHull { [CommandMethod("testch")] - public void testch() + public void Testch() { //using var tr = new DBTrans(); //var pts = new List(); diff --git a/tests/Test/testblock.cs b/tests/Test/testblock.cs new file mode 100644 index 0000000..ef615be --- /dev/null +++ b/tests/Test/testblock.cs @@ -0,0 +1,631 @@ +namespace Test; +public class TestBlock +{ + //块定义 + [CommandMethod("blockdef")] + public void BlockDef() + { + using var tr = new DBTrans(); + //var line = new Line(new Point3d(0, 0, 0), new Point3d(1, 1, 0)); + tr.BlockTable.Add("test", + btr => { + btr.Origin = new Point3d(0, 0, 0); + }, + () => //图元 + { + return new List { new Line(new Point3d(0, 0, 0), new Point3d(1, 1, 0)) }; + }, + () => //属性定义 + { + var id1 = new AttributeDefinition() { Position = new Point3d(0, 0, 0), Tag = "start", Height = 0.2 }; + var id2 = new AttributeDefinition() { Position = new Point3d(1, 1, 0), Tag = "end", Height = 0.2 }; + return new List { id1, id2 }; + } + ); + //ObjectId objectId = tr.BlockTable.Add("a");//新建块 + //objectId.GetObject().AddEntity();//测试添加空实体 + tr.BlockTable.Add("test1", + btr => { + btr.Origin = new Point3d(0, 0, 0); + + }, + () => { + var line = new Line(new Point3d(0, 0, 0), new Point3d(1, 1, 0)); + var acText = new TextInfo("123", Point3d.Origin, AttachmentPoint.BaseLeft) + .AddDBTextToEntity(); + + return new List { line, acText }; + }); + } + //修改块定义 + [CommandMethod("blockdefchange")] + public void BlockDefChange() + { + using var tr = new DBTrans(); + //var line = new Line(new Point3d(0, 0, 0), new Point3d(1, 1, 0)); + //tr.BlockTable.Change("test", btr => + //{ + // btr.Origin = new Point3d(5, 5, 0); + // btr.AddEntity(new Circle(new Point3d(0, 0, 0), Vector3d.ZAxis, 2)); + // btr.GetEntities() + // .ToList() + // .ForEach(e => e.Flush()); //刷新块显示 + + //}); + + + + + tr.BlockTable.Change("test", btr => { + foreach (var id in btr) + { + var ent = tr.GetObject(id); + using (ent.ForWrite()) + { + if (ent is Dimension dBText) + { + dBText.DimensionText = "234"; + dBText.RecomputeDimensionBlock(true); + } + + if (ent is Hatch hatch) + { + hatch.ColorIndex = 0; + + } + + + } + + } + }); + tr.Editor.Regen(); + } + + [CommandMethod("insertblockdef")] + public void InsertBlockDef() + { + using var tr = new DBTrans(); + var line1 = new Line(new Point3d(0, 0, 0), new Point3d(1, 1, 0)); + var line2 = new Line(new Point3d(0, 0, 0), new Point3d(-1, 1, 0)); + var att1 = new AttributeDefinition() { Position = new Point3d(10, 10, 0), Tag = "tagTest1", Height = 1, TextString = "valueTest1" }; + var att2 = new AttributeDefinition() { Position = new Point3d(10, 12, 0), Tag = "tagTest2", Height = 1, TextString = "valueTest2" }; + tr.BlockTable.Add("test1", line1, line2, att1, att2); + + + var ents = new List(); + var line5 = new Line(new Point3d(0, 0, 0), new Point3d(1, 1, 0)); + var line6 = new Line(new Point3d(0, 0, 0), new Point3d(-1, 1, 0)); + ents.Add(line5); + ents.Add(line6); + tr.BlockTable.Add("test44", ents); + + + var line3 = new Line(new Point3d(5, 5, 0), new Point3d(6, 6, 0)); + var line4 = new Line(new Point3d(5, 5, 0), new Point3d(-6, 6, 0)); + var att3 = new AttributeDefinition() { Position = new Point3d(10, 14, 0), Tag = "tagTest3", Height = 1, TextString = "valueTest3" }; + var att4 = new AttributeDefinition() { Position = new Point3d(10, 16, 0), Tag = "tagTest4", Height = 1, TextString = "valueTest4" }; + tr.BlockTable.Add("test2", new List { line3, line4 }, new List { att3, att4 }); + //tr.CurrentSpace.InsertBlock(new Point3d(4, 4, 0), "test1"); // 测试默认 + //tr.CurrentSpace.InsertBlock(new Point3d(4, 4, 0), "test2"); + //tr.CurrentSpace.InsertBlock(new Point3d(4, 4, 0), "test3"); //测试插入不存在的块定义 + //tr.CurrentSpace.InsertBlock(new Point3d(0, 0, 0), "test1", new Scale3d(2)); // 测试放大2倍 + //tr.CurrentSpace.InsertBlock(new Point3d(4, 4, 0), "test1", new Scale3d(2), Math.PI / 4); // 测试放大2倍,旋转45度 + + var def1 = new Dictionary + { + { "tagTest1", "1" }, + { "tagTest2", "2" } + }; + tr.CurrentSpace.InsertBlock(new Point3d(0, 0, 0), "test1", atts: def1); + var def2 = new Dictionary + { + { "tagTest3", "1" }, + { "tagTest4", "" } + }; + tr.CurrentSpace.InsertBlock(new Point3d(10, 10, 0), "test2", atts: def2); + tr.CurrentSpace.InsertBlock(new Point3d(-10, 0, 0), "test44"); + } + + [CommandMethod("addattsdef")] + public void AddAttsDef() + { + using var tr = new DBTrans(); + var blockid = Env.Editor.GetEntity("pick block:").ObjectId; + var blockref = tr.GetObject(blockid).BlockTableRecord; + + var att1 = new AttributeDefinition() { Position = new Point3d(20, 20, 0), Tag = "addtagTest1", Height = 1, TextString = "valueTest1" }; + var att2 = new AttributeDefinition() { Position = new Point3d(10, 12, 0), Tag = "tagTest2", Height = 1, TextString = "valueTest2" }; + + tr.BlockTable.AddAttsToBlocks(blockref, new List { att1, att2 }); + } + + [CommandMethod("testblocknullbug")] + public void TestBlockNullBug() + { + using var tr = new DBTrans(); + + var ents = new List(); + var line5 = new Line(new Point3d(0, 0, 0), new Point3d(1, 1, 0)); + var line6 = new Line(new Point3d(0, 0, 0), new Point3d(-1, 1, 0)); + ents.Add(line5); + ents.Add(line6); + tr.BlockTable.Add("test44", ents); + tr.CurrentSpace.InsertBlock(new Point3d(0, 0, 0), "test44"); + } + + [CommandMethod("test_block_file")] + public void TestBlockFile() + { + var tr = new DBTrans(); + var id = tr.BlockTable.GetBlockFrom(@"C:\Users\vic\Desktop\test.dwg", false); + tr.CurrentSpace.InsertBlock(Point3d.Origin, id); + } + + + [CommandMethod("testclip")] + public void TestClipBlock() + { + using var tr = new DBTrans(); + tr.BlockTable.Add("test1", + btr => { + btr.Origin = new Point3d(0, 0, 0); + btr.AddEntity(new Line(new Point3d(0, 0, 0), new Point3d(10, 10, 0)), + new Line(new Point3d(10, 10, 0), new Point3d(10, 0, 0)) + ); + } + ); + //tr.BlockTable.Add("hah"); + var id = tr.CurrentSpace.InsertBlock(new Point3d(0, 0, 0), "test1"); + var bref = tr.GetObject(id); + var pts = new List { new Point3d(3, 3, 0), new Point3d(7, 3, 0), new Point3d(7, 7, 0), new Point3d(3, 7, 0) }; + bref.ClipBlockRef(pts); + + var id1 = tr.CurrentSpace.InsertBlock(new Point3d(20, 20, 0), "test1"); + var bref1 = tr.GetObject(id); + + bref1.ClipBlockRef(new Point3d(13, 13, 0), new Point3d(17, 17, 0)); + } + + /// + /// 给用户的测试程序,不知道对错 + /// + [CommandMethod("test_block_ej")] + public void EJ() + { + using (var tr = new DBTrans()) + { + + //Point3d.Origin.AddBellowToModelSpace(100, 100, 5, 3, 30);//画波纹管 + + //Database db2 = new Database(false, true); + //string fullFileName = @".\MyBlockDwgFile\001.dwg"; + //db2.ReadDwgFile(fullFileName, System.IO.FileShare.Read, true, null); + //db2.CloseInput(true); + //string blockName = "test"; + //if (!tr.BlockTable.Has(blockName)) + //{ + // //tr.Database.Insert(blockName, db2, false);//插入块 + // db.Insert(blockName, db2, false); + + //} + + string fullFileName = @"C:\Users\vic\Desktop\001.dwg"; + var blockdef = tr.BlockTable.GetBlockFrom(fullFileName, false); + + tr.Database.Clayer = tr.LayerTable["0"];//当前图层切换为0图层 + tr.LayerTable.Change(tr.Database.Clayer, ltr => { + ltr.Color = Color.FromColorIndex(ColorMethod.ByAci, 2); //ColorMethod.ByAci可以让我们使用AutoCAD ACI颜色索引……这里为2(表示黄色) + }); + + ObjectId id = tr.ModelSpace.InsertBlock(Point3d.Origin, blockdef);//插入块参照 + + + + + + var entTest = tr.GetObject(id); + entTest.Draw(); + + } + + using var tr2 = new DBTrans(); + PromptEntityOptions PEO = new("\n请选择一个块"); + PEO.SetRejectMessage("\n对象必须是块"); + PEO.AddAllowedClass(typeof(BlockReference), true); + + PromptEntityResult PER = Env.Editor.GetEntity(PEO); + if (PER.Status != PromptStatus.OK) + { + return; + } + + var Bref = tr2.GetObject(PER.ObjectId); + //var BTR = tr.GetObject(Bref.BlockTableRecord, OpenMode.ForWrite); + ////如果知道块名字BTRName + //BlockTableRecord BTR = tr.GetObject(tr.BlockTable[blockName], OpenMode.ForWrite); + + var btr = tr2.BlockTable[Bref.Name]; + + tr2.BlockTable.Change(btr, ltr => { + + foreach (ObjectId OID in ltr) + { + var Ent = tr2.GetObject(OID); + using (Ent.ForWrite()) + { + if (Ent is MText mText) + { + switch (mText.Text) + { + case "$$A": + mText.Contents = "hahaha"; + break; + case "$$B": + ; + break; + default: + ; + break; + } + + }; + if (Ent is DBText dBText) { dBText.TextString = "haha"; }; + if (Ent is Dimension dimension) + { + switch (dimension.DimensionText) + { + case "$$pipeLen": + dimension.DimensionText = "350"; + dimension.RecomputeDimensionBlock(true); + break; + default: + break; + } + }; + } + + + } + + }); + + + tr2.Editor.Regen(); + + + } + + [CommandMethod("W_KSZK")] + public void QuickBlockDef() + { + //Database db = HostApplicationServices.WorkingDatabase; + Editor ed = Application.DocumentManager.MdiActiveDocument.Editor; + PromptSelectionOptions promptOpt = new() + { + MessageForAdding = "请选择需要快速制作块的对象" + }; + string blockName = "W_BLOCK_" + DateTime.Now.ToString("yyyyMMdd_HHmmss"); + //var rss = ed.GetSelection(promptOpt); + var rss = Env.Editor.GetSelection(promptOpt); + using var tr = new DBTrans(); + if (rss.Status == PromptStatus.OK) + { + //SelectionSet ss = rss.Value; + //ObjectId[] ids = ss.GetObjectIds(); + //var ents = new List>(); + //var extents = new Extents3d(); + //foreach (var id in ids) + //{ + // Entity ent = tr.GetObject(id); + // if (ent is null) + // continue; + // try + // { + // extents.AddExtents(ent.GeometricExtents); + // var order = id.Handle.Value; + // var newEnt = ent.Clone() as Entity; + // ents.Add(new KeyValuePair(newEnt, order)); + // ent.UpgradeOpen(); + // ent.Erase(); + // ent.DowngradeOpen(); + // } + // catch (System.Exception exc) + // { + // ed.WriteMessage(exc.Message); + // } + //} + //ents = ents.OrderBy(x => x.Value).ToList(); + var ents = rss.Value.GetEntities(); + //ents.ForEach(ent => extents.AddExtents(ent.GeometricExtents)); + var extents = ents.GetExtents(); + + Point3d pt = extents.MinPoint; + Matrix3d matrix = Matrix3d.Displacement(Point3d.Origin - pt); + //var newEnts = new List(); + //foreach (var ent in ents) + //{ + // var newEnt = ent.Key; + // newEnt.TransformBy(matrix); + // newEnts.Add(newEnt); + //} + //if (tr.BlockTable.Has(blockName)) + //{ + // Application.ShowAlertDialog(Environment.NewLine + "块名重复,程序退出!"); + // return; + //} + ents.ForEach(ent => + ent.ForWrite(e => e.TransformBy(matrix))); + //var newents = ents.Select(ent => + //{ + // var maping = new IdMapping(); + // return ent.DeepClone(ent, maping, true) as Entity; + //}); + var newents = ents.Select(ent => ent.Clone() as Entity); + + //ents.ForEach(ent => ent.ForWrite(e => e.Erase(true))); // 删除实体就会卡死,比较奇怪,估计是Clone()函数的问题 + // 经过测试不是删除的问题 + var btrId = tr.BlockTable.Add(blockName, newents); + ents.ForEach(ent => ent.ForWrite(e => e.Erase(true))); + var bId = tr.CurrentSpace.InsertBlock(pt, blockName); + //tr.GetObject(bId, OpenMode.ForWrite).Move(Point3d.Origin, Point3d.Origin); + //var ed = Application.DocumentManager.MdiActiveDocument.Editor; + //ed.Regen(); + //tr.Editor.Regen(); + // 调用regen() 卡死 + } + //tr.Editor.Regen(); + //ed.Regen(); + //using (var tr = new DBTrans()) + //{ + // tr.CurrentSpace.InsertBlock(Point3d.Origin, blockName); + // tr.Editor.Regen(); + //} + } + + [CommandMethod("testquickblockdef")] + public void TestQuickBlockDef() + { + Database db = HostApplicationServices.WorkingDatabase; + Editor ed = Application.DocumentManager.MdiActiveDocument.Editor; + PromptSelectionOptions promptOpt = new() + { + MessageForAdding = "请选择需要快速制作块的对象" + }; + string blockName = "W_BLOCK_" + DateTime.Now.ToString("yyyyMMdd_HHmmss"); + var rss = ed.GetSelection(promptOpt); + //var rss = Env.Editor.GetSelection(promptOpt); + if (rss.Status != PromptStatus.OK) + { + return; + } + + using var tr = db.TransactionManager.StartTransaction(); + var ids = rss.Value.GetObjectIds(); + var bt = tr.GetObject(db.BlockTableId, OpenMode.ForRead) as BlockTable; + var btr = new BlockTableRecord + { + Name = blockName + }; + foreach (var item in ids) + { + var ent = tr.GetObject(item, OpenMode.ForRead) as Entity; + + btr.AppendEntity(ent.Clone() as Entity); + ent.ForWrite(e => e.Erase(true)); + } + bt.UpgradeOpen(); + bt.Add(btr); + tr.AddNewlyCreatedDBObject(btr, true); + bt.DowngradeOpen(); + // tr.Commit(); + //} + + //using (var tr1 = db.TransactionManager.StartTransaction()) + //{ + //var bt = tr1.GetObject(db.BlockTableId, OpenMode.ForRead) as BlockTable; + var btr1 = tr.GetObject(db.CurrentSpaceId, OpenMode.ForWrite) as BlockTableRecord; + var br = new BlockReference(Point3d.Origin, bt[blockName]) + { + ScaleFactors = default + }; + btr1.AppendEntity(br); + tr.AddNewlyCreatedDBObject(br, true); + btr1.DowngradeOpen(); + ed.Regen(); + tr.Commit(); + //ed.Regen(); + + } + + public void TestWblock() + { + var curdb = HostApplicationServices.WorkingDatabase; + PromptSelectionOptions opts = new(); + opts.MessageForAdding = "选择对象"; + var ss = Env.Editor.GetSelection(opts).Value; + var ids = new ObjectIdCollection(ss.GetObjectIds()); + var db = curdb.Wblock(ids, Point3d.Origin); + db.SaveAs(@"c:\test.dwg", DwgVersion.Current); + } + + public void TestChangeDynameicBlock() + { + var pro = new Dictionary + { + { "haha", 1 } + }; + var blockid = Env.Editor.GetEntity("选择个块").ObjectId; + using var tr = new DBTrans(); + var blockref = tr.GetObject(blockid); + blockref.ChangeBlockProperty(pro); + // 这是第一个函数的用法 + } + + [CommandMethod("TestBack")] + public void TestBack() + { + string dir = Environment.GetFolderPath(Environment.SpecialFolder.DesktopDirectory); + string dwg = dir + "\\test.dwg"; + if (!File.Exists(dwg)) + { + System.Windows.Forms.MessageBox.Show(dwg, "你还没有创建此文件"); + return; + } + + using var tr = new DBTrans(dwg); + tr.ModelSpace.GetEntities().ForEach(ent => { + ent.ForWrite(e => e.ColorIndex = 3); + }); + tr.Database.SaveAs(dwg, DwgVersion.Current); + + tr.ModelSpace.GetEntities().ForEach(ent => { + ent.ForWrite(e => e.ColorIndex = 4); + }); + tr.Database.SaveAs(dwg, DwgVersion.Current); + + } + +} + +public class BlockImportClass +{ + + [CommandMethod("CBLL")] + public void Cbll() + { + string filename = @"C:\Users\vic\Desktop\Drawing1.dwg"; + using var tr = new DBTrans(); + using var tr1 = new DBTrans(filename); + //tr.BlockTable.GetBlockFrom(filename, true); + string blkdefname = SymbolUtilityServices.RepairSymbolName(SymbolUtilityServices.GetSymbolNameFromPathName(filename, "dwg"), false); + tr.Database.Insert(blkdefname, tr1.Database, false); //插入了块定义,未插入块参照 + } + + + [CommandMethod("CBL")] + public void CombineBlocksIntoLibrary() + { + Document doc = + Application.DocumentManager.MdiActiveDocument; + Editor ed = doc.Editor; + Database destDb = doc.Database; + + // Get name of folder from which to load and import blocks + + PromptResult pr = + ed.GetString("\nEnter the folder of source drawings: "); + + if (pr.Status != PromptStatus.OK) + return; + string pathName = pr.StringResult; + + // Check the folder exists + + if (!Directory.Exists(pathName)) + { + ed.WriteMessage( + "\nDirectory does not exist: {0}", pathName + ); + return; + } + + // Get the names of our DWG files in that folder + + string[] fileNames = Directory.GetFiles(pathName, "*.dwg"); + + // A counter for the files we've imported + + int imported = 0, failed = 0; + + // For each file in our list + + foreach (string fileName in fileNames) + { + // Double-check we have a DWG file (probably unnecessary) + + if (fileName.EndsWith( + ".dwg", + StringComparison.InvariantCultureIgnoreCase + ) + ) + { + // Catch exceptions at the file level to allow skipping + + try + { + // Suggestion from Thorsten Meinecke... + + string destName = + SymbolUtilityServices.GetSymbolNameFromPathName( + fileName, "dwg" + ); + + // And from Dan Glassman... + + destName = + SymbolUtilityServices.RepairSymbolName( + destName, false + ); + + // Create a source database to load the DWG into + + using Database db = new(false, true); + // Read the DWG into our side database + + db.ReadDwgFile(fileName, FileShare.Read, true, ""); + bool isAnno = db.AnnotativeDwg; + + // Insert it into the destination database as + // a named block definition + + ObjectId btrId = destDb.Insert( + destName, + db, + false + ); + + if (isAnno) + { + // If an annotative block, open the resultant BTR + // and set its annotative definition status + + Transaction tr = + destDb.TransactionManager.StartTransaction(); + using (tr) + { + BlockTableRecord btr = + (BlockTableRecord)tr.GetObject( + btrId, + OpenMode.ForWrite + ); + btr.Annotative = AnnotativeStates.True; + tr.Commit(); + } + } + + // Print message and increment imported block counter + + ed.WriteMessage("\nImported from \"{0}\".", fileName); + imported++; + } + catch (System.Exception ex) + { + ed.WriteMessage( + "\nProblem importing \"{0}\": {1} - file skipped.", + fileName, ex.Message + ); + failed++; + } + } + } + + ed.WriteMessage( + "\nImported block definitions from {0} files{1} in " + + "\"{2}\" into the current drawing.", + imported, + failed > 0 ? " (" + failed + " failed)" : "", + pathName + ); + } +} \ No newline at end of file diff --git a/tests/Test/testeditor.cs b/tests/Test/testeditor.cs index 397d5c4..5c5ac13 100644 --- a/tests/Test/testeditor.cs +++ b/tests/Test/testeditor.cs @@ -1,38 +1,69 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; -using Autodesk.AutoCAD.ApplicationServices; -using Autodesk.AutoCAD.DatabaseServices; -using Autodesk.AutoCAD.EditorInput; -using Autodesk.AutoCAD.Geometry; -using Autodesk.AutoCAD.Runtime; -using IFoxCAD.Cad; -namespace test +namespace Test; + +public class Testeditor { - public class testeditor + [CommandMethod("tested")] + public void Tested() + { + var pts = new List + { + new Point2d(0,0), + new Point2d(0,1), + new Point2d(1,1), + new Point2d(1,0) + }; + var res = EditorEx.GetLines(pts, false); + var res1 = EditorEx.GetLines(pts, true); + var res2 = pts.Select(pt => new TypedValue((int)LispDataType.Point2d, pt)).ToList(); + + Editor ed = Application.DocumentManager.MdiActiveDocument.Editor; + var pt = ed.GetPoint("qudiam", new Point3d(0, 0, 0)); + var d = ed.GetDouble("qudoule"); + var i = ed.GetInteger("quint"); + var s = ed.GetString("qustr"); + Env.Editor.WriteMessage(""); + } + [CommandMethod("testzoom")] + public void Testzoom() { - [CommandMethod("tested")] - public void tested() + using var tr = new DBTrans(); + var res = Env.Editor.GetEntity("\npick ent:"); + if (res.Status == Autodesk.AutoCAD.EditorInput.PromptStatus.OK) { - var pts = new List - { - new Point2d(0,0), - new Point2d(0,1), - new Point2d(1,1), - new Point2d(1,0) - }; - var res = EditorEx.GetLines(pts, false); - var res1 = EditorEx.GetLines(pts, true); - var res2 = pts.Select(pt => new TypedValue((int)LispDataType.Point2d, pt)).ToList(); - - Editor ed = Application.DocumentManager.MdiActiveDocument.Editor; - var pt = ed.GetPoint("qudiam", new Point3d(0, 0, 0)); - var d = ed.GetDouble("qudoule"); - var i = ed.GetInteger("quint"); - var s = ed.GetString("qustr"); - Env.Editor.WriteMessage(""); + Env.Editor.ZoomObject(res.ObjectId.GetObject()); } + + + } + [CommandMethod("testzoomextent")] + public void Testzoomextent() + { + //using var tr = new DBTrans(); + //var res = Env.Editor.GetEntity("\npick ent:"); + //if (res.Status == Autodesk.AutoCAD.EditorInput.PromptStatus.OK) + //{ + // Env.Editor.ZoomObject(res.ObjectId.GetObject()); + //} + + Env.Editor.ZoomExtents(); + } + + [CommandMethod("testssget")] + public void Testssget() + { + var action_a = () => { Env.Print("this is a"); }; + var action_b = () => { Env.Print("this is b"); }; + + var keyword = new Dictionary + { + { "A", action_a }, + { "B", action_b } + }; + + var ss = Env.Editor.SSGet( ":S", + messages: new string[2] { "get", "del" }, + keywords: keyword); + + Env.Print(ss); } } diff --git a/tests/Test/testenv.cs b/tests/Test/testenv.cs index 6cd7c9b..05ffc5c 100644 --- a/tests/Test/testenv.cs +++ b/tests/Test/testenv.cs @@ -1,71 +1,89 @@ -using IFoxCAD.Cad; -using Autodesk.AutoCAD.Runtime; -using Autodesk.AutoCAD.ApplicationServices; -using Autodesk.AutoCAD.DatabaseServices; +namespace Test; -namespace test +public class Testenv { - public class testenv + [CommandMethod("testenum")] + public void Testenum() { - [CommandMethod("testenum")] - public void testenum() - { - Env.CmdEcho = true; - } - [CommandMethod("testenum1")] - public void testenum1() - { - Env.CmdEcho = false; - } + + Env.CmdEcho = true; + + } + [CommandMethod("testenum1")] + public void Testenum1() + { + + Env.CmdEcho = false; + + } - [CommandMethod("testdimblk")] - public void testdimblk() - { + [CommandMethod("testdimblk")] + public void Testdimblk() + { - Env.Dimblk = Env.DimblkType.Dot; - Env.Dimblk = Env.DimblkType.Defult; + Env.Dimblk = Env.DimblkType.Dot; + Env.Dimblk = Env.DimblkType.Defult; + Env.Dimblk = Env.DimblkType.Oblique; - } - [CommandMethod("testdimblk1")] - public void testdimblk1() - { - var dim = Env.Dimblk; - Env.Editor.WriteMessage(dim.ToString()); + } + [CommandMethod("testdimblk1")] + public void Testdimblk1() + { + var dim = Env.Dimblk; + Env.Editor.WriteMessage(dim.ToString()); - } + } - [CommandMethod("testosmode")] - public void testosmode() - { - // 设置osmode变量,多个值用逻辑或 - Env.OSMode = Env.OSModeType.End | Env.OSModeType.Middle; - // 也可以直接写数值,进行强转 - Env.OSMode = (Env.OSModeType)5179; - // 追加模式 - Env.OSMode |= Env.OSModeType.Center; - //检查是否有某个模式 - var os = Env.OSMode.Include(Env.OSModeType.Center); - // 取消某个模式 - Env.OSMode ^= Env.OSModeType.Center; - Env.Editor.WriteMessage(Env.OSMode.ToString()); - } - [CommandMethod("testosmode1")] - public void testosmode1() - { - var dim = Env.OSMode; - Env.Editor.WriteMessage(dim.ToString()); + [CommandMethod("testosmode")] + public void Testosmode() + { + // 设置osmode变量,多个值用逻辑或 + Env.OSMode = Env.OSModeType.End | Env.OSModeType.Middle; + // 也可以直接写数值,进行强转 + Env.OSMode = (Env.OSModeType)5179; + // 追加模式 + Env.OSMode |= Env.OSModeType.Center; + //检查是否有某个模式 + var os = Env.OSMode.Include(Env.OSModeType.Center); + // 取消某个模式 + Env.OSMode ^= Env.OSModeType.Center; + Env.Editor.WriteMessage(Env.OSMode.ToString()); + } + [CommandMethod("testosmode1")] + public void Testosmode1() + { + var dim = Env.OSMode; + Env.Editor.WriteMessage(dim.ToString()); - } + } + + [CommandMethod("testcadver")] + public void Testcadver() + { + //Env.Print(AcadVersion.Versions); + AcadVersion.Versions.ForEach(v => Env.Print(v)); + AcadVersion.FromApp(Application.AcadApplication).Print(); + 1.Print(); + "1".Print(); + + } - [CommandMethod("testzoom")] - public void testzoom() - { - using var tr = new DBTrans(); - var res = Env.Editor.GetEntity("\npick ent:"); - if (res.Status == Autodesk.AutoCAD.EditorInput.PromptStatus.OK) - { - Env.Editor.ZoomObject(res.ObjectId.GetObject()); - } - } + [CommandMethod("TestGetVar")] + public void TestGetVar() + { + // test getvar + var a = Env.GetVar("dbmod"); + a.Print(); + Env.SetVar("dbmod1", 1); + } + + [CommandMethod("TestDwgVersion")] + public void TestDwgVersion() + { + // + //string filename = @"C:\Users\vic\Desktop\test.dwg"; + //var a = Helper.GetCadFileVersion(filename); + //a.Print(); + //((DwgVersion)a).Print(); } } diff --git a/tests/Test/testselectfilter.cs b/tests/Test/testselectfilter.cs index 9b658ee..3c403a9 100644 --- a/tests/Test/testselectfilter.cs +++ b/tests/Test/testselectfilter.cs @@ -10,20 +10,20 @@ using IFoxCAD.Cad; using Autodesk.AutoCAD.EditorInput; -namespace test +namespace Test { - public class testselectfilter + public class Testselectfilter { [CommandMethod("testfilter")] - public void testfilter() + public void Testfilter() { - + var p = new Point3d(10, 10, 0); var f = OpFilter.Bulid( - e => !(e.Dxf(0) == "line" & e.Dxf(8) == "0") + e =>!(e.Dxf(0) == "line" & e.Dxf(8) == "0") | e.Dxf(0) != "circle" & e.Dxf(8) == "2" & e.Dxf(10) >= p); - - + + var f2 = OpFilter.Bulid( e => e.Or( !e.And(e.Dxf(0) == "line", e.Dxf(8) == "0"), @@ -37,7 +37,7 @@ public void testfilter() } [CommandMethod("testselectanpoint")] - public void testselectanpoint() + public void Testselectanpoint() { var sel2 = Env.Editor.SelectAtPoint(new Point3d(0, 0, 0)); Env.Editor.WriteMessage(""); diff --git a/tests/Test/wpf/Class1.cs b/tests/Test/wpf/Class1.cs new file mode 100644 index 0000000..4358668 --- /dev/null +++ b/tests/Test/wpf/Class1.cs @@ -0,0 +1,13 @@ +namespace Test.wpf +{ + public class Class1 + { + [CommandMethod("testwpf")] + public void TestWPf() + { + + var test = new TestView(); + Application.ShowModalWindow(test); + } + } +} diff --git a/tests/Test/wpf/TestView.xaml b/tests/Test/wpf/TestView.xaml index 42cb304..bd75779 100644 --- a/tests/Test/wpf/TestView.xaml +++ b/tests/Test/wpf/TestView.xaml @@ -1,9 +1,9 @@ - /// TestView.xaml 的交互逻辑 diff --git a/tests/Test/wpf/TestViewModel.cs b/tests/Test/wpf/TestViewModel.cs index dc6fce0..4cb7563 100644 --- a/tests/Test/wpf/TestViewModel.cs +++ b/tests/Test/wpf/TestViewModel.cs @@ -1,119 +1,113 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; + using System.Windows; using System.Windows.Input; -using IFoxCAD.WPF; -namespace test.wpf +namespace Test.wpf; + +class TestViewModel : ViewModelBase { - class TestViewModel : ViewModelBase - { - - private string name; + + private string name; - public string Name - { - get { return name; } - set { Set(ref name, value); } - } + public string Name + { + get { return name; } + set { Set(ref name, value); } + } - private RelayCommand clickCommand; + private RelayCommand clickCommand; - public RelayCommand ClickCommand + public RelayCommand ClickCommand + { + get { - get + if (clickCommand is null) { - if (clickCommand is null) - { - clickCommand = new( - execute => Name = "hello " + Name, - can => !string.IsNullOrEmpty(Name)); - } - return clickCommand; + clickCommand = new( + execute => Name = "hello " + Name, + can => !string.IsNullOrEmpty(Name)); } + return clickCommand; } + } - private bool receiveMouseMove; + private bool receiveMouseMove; - public bool ReceiveMouseMove - { - get { return receiveMouseMove; } - set { Set(ref receiveMouseMove, value); } - } + public bool ReceiveMouseMove + { + get { return receiveMouseMove; } + set { Set(ref receiveMouseMove, value); } + } - private string tipText; + private string tipText; - public string TipText - { - get { return tipText; } - set { Set(ref tipText, value); } - } + public string TipText + { + get { return tipText; } + set { Set(ref tipText, value); } + } - private RelayCommand loadedCommand; - public RelayCommand LoadCommand + private RelayCommand loadedCommand; + public RelayCommand LoadCommand + { + get { - get + if (loadedCommand is null) { - if (loadedCommand is null) - { - loadedCommand = new( - execute => MessageBox.Show("程序加载完毕")); - } - return loadedCommand; + loadedCommand = new( + execute => MessageBox.Show("程序加载完毕")); } + return loadedCommand; } + } - private RelayCommand mouseMoveCommand; + private RelayCommand mouseMoveCommand; - public RelayCommand MouseMoveCommand + public RelayCommand MouseMoveCommand + { + get { - get + if (mouseMoveCommand is null) { - if (mouseMoveCommand is null) - { - mouseMoveCommand = new( - execute => + mouseMoveCommand = new( + execute => + { + var pt = execute.GetPosition(execute.Device.Target); + var left = "左键放开"; + var mid = "中键放开"; + var right = "右键放开"; + + if (execute.LeftButton == MouseButtonState.Pressed) + { + left = "左键放下"; + } + if (execute.MiddleButton == MouseButtonState.Pressed) + { + mid = "中键放下"; + } + if (execute.RightButton == MouseButtonState.Pressed) { - var pt = execute.GetPosition(execute.Device.Target); - var left = "左键放开"; - var mid = "中键放开"; - var right = "右键放开"; - - if (execute.LeftButton == MouseButtonState.Pressed) - { - left = "左键放下"; - } - if (execute.MiddleButton == MouseButtonState.Pressed) - { - mid = "中键放下"; - } - if (execute.RightButton == MouseButtonState.Pressed) - { - right = "右键放下"; - } - TipText = $"当前鼠标位置:X={pt.X},Y={pt.Y}。当前鼠标状态:{left}、{mid}、{right}"; - }, - can => ReceiveMouseMove); - } - return mouseMoveCommand; + right = "右键放下"; + } + TipText = $"当前鼠标位置:X={pt.X},Y={pt.Y}。当前鼠标状态:{left}、{mid}、{right}"; + }, + can => ReceiveMouseMove); } + return mouseMoveCommand; } + } - public TestViewModel() - { - Name = "world"; - } + public TestViewModel() + { + Name = "world"; + } - } } diff --git a/tests/TestConsole/Program.cs b/tests/TestConsole/Program.cs new file mode 100644 index 0000000..aa31926 --- /dev/null +++ b/tests/TestConsole/Program.cs @@ -0,0 +1,55 @@ +// See https://aka.ms/new-console-template for more information +using System; +using System.Text; + + +//表达式树例子 +TestConsole.Test_Expression.Demo3(); +//TestConsole.Test_Expression.Demo1(); + +#region 元组测试 +var valuetuple = (1, 2); + +Console.WriteLine(valuetuple.ToString()); + +int[] someArray = new int[5] { 1, 2, 3, 4, 5 }; +int lastElement = someArray[^1]; // lastElement = 5 +Console.WriteLine(lastElement); +int midElement = someArray[^3]; +Console.WriteLine(midElement); +var range = someArray[1..3]; +foreach (var item in range) + Console.WriteLine(item); +#endregion + +Console.ReadLine(); + + +#region 测试遍历枚举 +//Season a = Season.Autumn; +//Console.WriteLine($"Integral value of {a} is {(int)a}"); // output: Integral value of Autumn is 2 +//foreach (var enumItem in Enum.GetValues(typeof(Season))) +// Console.WriteLine((byte)enumItem); + +var sb = new StringBuilder(); +/*因为 net framework 没写好的原因,导致直接使用迭代器反而更慢,到了net60就迭代器比foreach更快*/ +var enums = Enum.GetValues(typeof(Season)).GetEnumerator(); +while (enums.MoveNext()) +{ + sb.Append(((byte)enums.Current).ToString()); + sb.Append(","); +} +Console.WriteLine(sb); + +sb.Remove(sb.Length - 1, 1);//剔除末尾, +//因为有返回值所以容易理解成 sb = sb.Remove(sb.Length - 1, 1); +Console.WriteLine(sb); + +public enum Season : byte +{ + Spring, + Summer, + Autumn, + Winter +} +#endregion \ No newline at end of file diff --git a/tests/TestConsole/RuntimeHelpers.cs b/tests/TestConsole/RuntimeHelpers.cs new file mode 100644 index 0000000..9e09b29 --- /dev/null +++ b/tests/TestConsole/RuntimeHelpers.cs @@ -0,0 +1,50 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +// 如果要用range的语法比如 a[1..3],那么需要将本文件复制到你的项目里 +#if true +namespace System.Runtime.CompilerServices +{ + internal static class RuntimeHelpers + { + /// + /// Slices the specified array using the specified range. + /// + public static T[] GetSubArray(T[] array, Range range) + { + if (array == null) + { + throw new ArgumentNullException(); + } + + (int offset, int length) = range.GetOffsetAndLength(array.Length); + + if (default(T)! != null || typeof(T[]) == array.GetType()) // TODO-NULLABLE: default(T) == null warning (https://github.com/dotnet/roslyn/issues/34757) + { + // We know the type of the array to be exactly T[]. + + if (length == 0) + { + //return Array.Empty(); + return new T[0]; + + + } + + var dest = new T[length]; + Array.Copy(array, offset, dest, 0, length); + return dest; + } + else + { + // The array is actually a U[] where U:T. + T[] dest = (T[])Array.CreateInstance(array.GetType().GetElementType()!, length); + Array.Copy(array, offset, dest, 0, length); + return dest; + } + } + } +} + +#endif \ No newline at end of file diff --git a/tests/TestConsole/TestConsole.csproj b/tests/TestConsole/TestConsole.csproj new file mode 100644 index 0000000..293a6aa --- /dev/null +++ b/tests/TestConsole/TestConsole.csproj @@ -0,0 +1,22 @@ + + + + preview + enable + + Exe + net45 + enable + preview + + + + + + + + + + + + diff --git "a/tests/TestConsole/\350\241\250\350\276\276\345\274\217\346\240\221.cs" "b/tests/TestConsole/\350\241\250\350\276\276\345\274\217\346\240\221.cs" new file mode 100644 index 0000000..f5cc477 --- /dev/null +++ "b/tests/TestConsole/\350\241\250\350\276\276\345\274\217\346\240\221.cs" @@ -0,0 +1,143 @@ +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.Linq; +using System.Linq.Expressions; + +namespace TestConsole +{ + /// + /// 表达式树 + /// MSDN链接 + /// + public class Test_Expression + { + public static void Demo1() + { + // 官方例子:表达式体内只有一个式子 + // 创建表达式树 + Expression> exprTree = num => num < 5; + + // 分解表达式树 + ParameterExpression param = exprTree.Parameters[0];//num + BinaryExpression operation = (BinaryExpression)exprTree.Body;//函数体 {(num < 5)} + ParameterExpression left = (ParameterExpression)operation.Left;//左节点 num + ConstantExpression right = (ConstantExpression)operation.Right;//右表达式 5 + + Console.WriteLine("表达式树例子: {0} => {1} {2} {3}", + param.Name, left.Name, operation.NodeType, right.Value); + Console.Read(); + } + + + public static void Demo2() + { + // 这里是会报错的!! 原因就是体内有多个例子需要分解!! + // Expression> exprTree = x => x > 5 && x < 50; + // + // // 分解表达式树 + // ParameterExpression param = exprTree.Parameters[0];// x + // BinaryExpression operation = (BinaryExpression)exprTree.Body;//函数体 {((x > 5) AndAlso (x < 50))} + // + // ParameterExpression left = (ParameterExpression)operation.Left;//左节点.......这里报错 + // ConstantExpression right = (ConstantExpression)operation.Right;//右表达式.....这里报错 + // + // Console.WriteLine("表达式树例子: {0} => {1} {2} {3}", + // param.Name, left.Name, operation.NodeType, right.Value); + } + + + //博客园例子,表达式体内有多个式子 + public static void Demo3() + { + List names = new() { "Cai", "Edward", "Beauty" }; + + Console.WriteLine("******************一个表达式"); + Expression> lambda2 = name => name.Length > 2 && name.Length < 4; + var method2 = ReBuildExpression(lambda2); + var query2 = names.Where(method2); + foreach (string n in query2) + Console.WriteLine(n); + + Console.WriteLine("******************二个表达式"); + Expression> lambda0 = item => item.Length > 2; + Expression> lambda1 = item => item.Length < 4; + var method = ReBuildExpression(lambda0, lambda1); + var query = names.Where(method); + foreach (string n in query) + Console.WriteLine(n); + Console.WriteLine("******************表达式结束"); + Console.Read(); + } + + + static Func ReBuildExpression(Expression> lambda) + { + MyExpressionVisitor my = new() + { + Parameter = Expression.Parameter(typeof(string), "name") + }; + + Expression left = my.Modify(lambda.Body); + //构造一个新的表达式 + var newLambda = Expression.Lambda>(left, my.Parameter); + return newLambda.Compile(); + } + + + + /// + /// 重构表达式_合并 + /// + /// 匿名函数表达式1 + /// 匿名函数表达式2 + /// + static Func ReBuildExpression(Expression> lambda0, + Expression> lambda1) + { + MyExpressionVisitor my = new() + { + Parameter = Expression.Parameter(typeof(string), "name") + }; + + Expression left = my.Modify(lambda0.Body); + Expression right = my.Modify(lambda1.Body); + var expression = Expression.AndAlso(left, right);//就是 && 合并两个匿名函数 + + //构造一个新的表达式 + var newLambda = Expression.Lambda>(expression, my.Parameter); + return newLambda.Compile(); + } + } + + + /// + /// 表达式参数分解 + /// 博客园链接 + /// + public class MyExpressionVisitor : ExpressionVisitor + { + /// + /// 公共参数 + /// + public ParameterExpression? Parameter; + /// + /// 返回替换后的参数表达式 + /// + /// + /// + public Expression Modify(Expression exp) + { + return Visit(exp); + } + /// + /// 重写参数 + /// + /// + /// + protected override Expression? VisitParameter(ParameterExpression p) + { + return Parameter; + } + } +} -- Gitee From 116278f6cccb6ae2db18284c94d8d2721e9bb7e1 Mon Sep 17 00:00:00 2001 From: liuqihong <540762622@qq.com> Date: Thu, 11 Aug 2022 23:09:37 +0800 Subject: [PATCH 345/675] =?UTF-8?q?=E9=99=A4=E4=BA=86=E5=8C=85=E4=B9=8B?= =?UTF-8?q?=E5=A4=96=E5=85=B6=E4=BB=96=E4=BF=9D=E6=8C=81=E5=92=8Cdev?= =?UTF-8?q?=E5=88=86=E6=94=AF=E4=B8=80=E6=A0=B7?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/IFoxCAD.Cad/IFoxCAD.Cad.csproj | 95 +++++++++++++++++------------- 1 file changed, 55 insertions(+), 40 deletions(-) diff --git a/src/IFoxCAD.Cad/IFoxCAD.Cad.csproj b/src/IFoxCAD.Cad/IFoxCAD.Cad.csproj index d72026f..8aab0f3 100644 --- a/src/IFoxCAD.Cad/IFoxCAD.Cad.csproj +++ b/src/IFoxCAD.Cad/IFoxCAD.Cad.csproj @@ -1,27 +1,30 @@ - - - - preview - enable + - net35;net40;net45 - true - 0.1.3 - InspireFunction - xsfhlzh;vicwjb - 基于.NET的Cad二次开发类库 - InspireFunction - https://gitee.com/inspirefunction/ifoxcad - https://gitee.com/inspirefunction/ifoxcad.git - git - IFoxCAD;CAD;AutoCad;C#;NET - Optimize and add multiple functions. - true - true - true - LICENSE - true - + + preview + enable + + net35;net40;net45 + true + true + true + 0.3.6.1 + InspireFunction + xsfhlzh;vicwjb + 基于.NET的Cad二次开发类库 + InspireFunction + https://gitee.com/inspirefunction/ifoxcad + https://gitee.com/inspirefunction/ifoxcad.git + git + IFoxCAD;CAD;AutoCad;C#;NET + 增加四叉树测试. + true + true + true + LICENSE + true + x64 + @@ -51,25 +54,37 @@ $(Configuration);ac2015 - + + 1701;1702;CS1685 + + + 1701;1702;CS1685 + + + 1701;1702;CS1685 + + + 1701;1702;CS1685 + + + 1701;1702;CS1685 + + + 1701;1702;CS1685 + - - True - - - - - - - - - - - + + True + + + - - - + + + + -- Gitee From 3d9c19f40d4190e6e5fe182308546074a56661b6 Mon Sep 17 00:00:00 2001 From: liuqihong <540762622@qq.com> Date: Fri, 12 Aug 2022 05:33:25 +0000 Subject: [PATCH 346/675] =?UTF-8?q?=E5=88=A0=E9=99=A4ac2008=E6=A0=87?= =?UTF-8?q?=E8=AE=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: liuqihong <540762622@qq.com> --- src/IFoxCAD.Cad/IFoxCAD.Cad.csproj | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/IFoxCAD.Cad/IFoxCAD.Cad.csproj b/src/IFoxCAD.Cad/IFoxCAD.Cad.csproj index 1f5e749..36a58ac 100644 --- a/src/IFoxCAD.Cad/IFoxCAD.Cad.csproj +++ b/src/IFoxCAD.Cad/IFoxCAD.Cad.csproj @@ -56,7 +56,7 @@ DEBUG - $(Configuration);ac2008;ac2009 + $(Configuration);ac2009 $(Configuration);ac2013 -- Gitee From 158e8a121771d532faf83f76a6fb680204c3e304 Mon Sep 17 00:00:00 2001 From: liuqihong <540762622@qq.com> Date: Fri, 12 Aug 2022 14:16:07 +0800 Subject: [PATCH 347/675] =?UTF-8?q?=E7=A7=BB=E9=99=A4=E5=8F=8C=E6=84=9F?= =?UTF-8?q?=E5=8F=B9=E5=8F=B7?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/IFoxCAD.Basal/CLS/RuntimeHelpers.cs | 5 ++- .../CLS/TupleElementNamesAttribute.cs | 7 +++- src/IFoxCAD.Basal/ListEx.cs | 9 ++++-- src/IFoxCAD.Cad/Algorithms/Graph/Graph.cs | 32 +++++++++++-------- src/IFoxCAD.Cad/ExtensionMethod/PointEx.cs | 12 +++++-- .../HatchInfo.cs" | 11 ++++--- src/IFoxCAD.Cad/Runtime/AcadVersion.cs | 24 ++++++++------ tests/Test/TestLisp.cs | 8 ++--- 8 files changed, 71 insertions(+), 37 deletions(-) diff --git a/src/IFoxCAD.Basal/CLS/RuntimeHelpers.cs b/src/IFoxCAD.Basal/CLS/RuntimeHelpers.cs index b5698b4..a446c59 100644 --- a/src/IFoxCAD.Basal/CLS/RuntimeHelpers.cs +++ b/src/IFoxCAD.Basal/CLS/RuntimeHelpers.cs @@ -10,8 +10,11 @@ public static class RuntimeHelpers /// /// Slices the specified array using the specified range. /// - public static T[] GetSubArray(T[] array!!, Range range) + public static T[] GetSubArray(T[] array, Range range) { + if (array == null) + throw new ArgumentNullException(nameof(array)); + (int offset, int length) = range.GetOffsetAndLength(array.Length); if (default(T)! != null || typeof(T[]) == array.GetType()) // TODO-NULLABLE: default(T) == null warning (https://github.com/dotnet/roslyn/issues/34757) diff --git a/src/IFoxCAD.Basal/CLS/TupleElementNamesAttribute.cs b/src/IFoxCAD.Basal/CLS/TupleElementNamesAttribute.cs index bb0716d..69670b4 100644 --- a/src/IFoxCAD.Basal/CLS/TupleElementNamesAttribute.cs +++ b/src/IFoxCAD.Basal/CLS/TupleElementNamesAttribute.cs @@ -2,6 +2,8 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. +using IFoxCAD.Basal; + namespace System.Runtime.CompilerServices; /// @@ -34,8 +36,11 @@ public sealed class TupleElementNamesAttribute : Attribute /// transformNames value of { "name1", "name2", null, null, /// null }. /// - public TupleElementNamesAttribute(string[] transformNames!!) + public TupleElementNamesAttribute(string[] transformNames) { + if (transformNames == null) + throw new ArgumentNullException(nameof(transformNames)); + _transformNames = transformNames; } diff --git a/src/IFoxCAD.Basal/ListEx.cs b/src/IFoxCAD.Basal/ListEx.cs index b0d20c0..4ead97b 100644 --- a/src/IFoxCAD.Basal/ListEx.cs +++ b/src/IFoxCAD.Basal/ListEx.cs @@ -5,13 +5,18 @@ public static class ListEx { public static bool EqualsAll(this IList a, IList b) { - return EqualsAll(a, b, null); + return EqualsAll(a, b, null); // there is a slight performance gain in passing null here. // It is how it is done in other parts of the framework. } - public static bool EqualsAll(this IList a!!, IList b!!, IEqualityComparer? comparer) + public static bool EqualsAll(this IList a, IList b, IEqualityComparer? comparer) { + if (a == null) + return b == null; + else if (b == null) + return false; + if (a.Count != b.Count) return false; diff --git a/src/IFoxCAD.Cad/Algorithms/Graph/Graph.cs b/src/IFoxCAD.Cad/Algorithms/Graph/Graph.cs index ad1efb5..c1d606f 100644 --- a/src/IFoxCAD.Cad/Algorithms/Graph/Graph.cs +++ b/src/IFoxCAD.Cad/Algorithms/Graph/Graph.cs @@ -82,8 +82,11 @@ public IGraphVertex AddVertex(Point3d pt) /// 向该图添加一个边; /// /// - public void AddEdge(Curve3d curve!!) + public void AddEdge(Curve3d curve) { + if (curve == null) + throw new ArgumentNullException(nameof(curve)); + var start = AddVertex(curve.StartPoint); var end = AddVertex(curve.EndPoint); @@ -141,8 +144,11 @@ public void RemoveVertex(Point3d pt) /// 从此图中删除一条边; /// /// 曲线 - public void RemoveEdge(Curve3d curve!!) + public void RemoveEdge(Curve3d curve) { + if (curve == null) + throw new ArgumentNullException(nameof(curve)); + RemoveVertex(curve.StartPoint); RemoveVertex(curve.EndPoint); } @@ -303,7 +309,7 @@ public string ToReadable() i++; } return output; - } + } #endregion } @@ -473,9 +479,9 @@ public override int GetHashCode() public sealed class DepthFirst { #region 公共方法 -/// -/// 存储所有的边 -/// + /// + /// 存储所有的边 + /// #if true public List> Curve3ds { get; } = new(); #else @@ -494,13 +500,13 @@ public void FindAll(IGraph graph) //var graphtmp = graph.Clone(); foreach (var item in graph.VerticesAsEnumberable) { - Dfs(graph, new LinkedHashSet { item },total); + Dfs(graph, new LinkedHashSet { item }, total); total.Add(item); } } -#endregion + #endregion -#region 内部方法 + #region 内部方法 /// /// 递归 DFS; /// @@ -522,7 +528,7 @@ void Dfs(IGraph graph, LinkedHashSet visited, HashSet { nextNode }; sub.AddRange(visited); // O(n) - Dfs(graph, sub,totalVisited); + Dfs(graph, sub, totalVisited); } // 如果下一点遍历过,并且路径大于2,说明已经找到起点 else if (visited.Count > 2 && nextNode.Equals(visited.Last!.Value)) @@ -613,7 +619,7 @@ static List Invert(List lst, IGraphVertex vertex) return tmp.Skip(index).Concat(lst.Take(index)).ToList(); } - static (string,string) Gethashstring(List pathone, List pathtwo) + static (string, string) Gethashstring(List pathone, List pathtwo) { var one = new string[pathone.Count]; var two = new string[pathtwo.Count]; @@ -637,11 +643,11 @@ static List Invert(List lst, IGraphVertex vertex) } - bool Isnew((string,string) path) + bool Isnew((string, string) path) { return !Curved.Contains(path.Item1) && !Curved.Contains(path.Item2); } -#endregion + #endregion } \ No newline at end of file diff --git a/src/IFoxCAD.Cad/ExtensionMethod/PointEx.cs b/src/IFoxCAD.Cad/ExtensionMethod/PointEx.cs index b1bae82..cf71f5f 100644 --- a/src/IFoxCAD.Cad/ExtensionMethod/PointEx.cs +++ b/src/IFoxCAD.Cad/ExtensionMethod/PointEx.cs @@ -1,3 +1,5 @@ +using Autodesk.AutoCAD.DatabaseServices; + namespace IFoxCAD.Cad; public static class PointEx @@ -95,8 +97,11 @@ public static double GetArcBulge(this Point2d arc1, Point2d arc2, Point2d arc3, /// /// 首尾相连 /// - public static Point2dCollection End2End(this Point2dCollection ptcol!!) + public static Point2dCollection End2End(this Point2dCollection ptcol) { + if (ptcol == null) + throw new ArgumentNullException(nameof(ptcol)); + if (ptcol.Count == 0 || ptcol[0].Equals(ptcol[^1]))//首尾相同直接返回 return ptcol; @@ -111,8 +116,11 @@ public static Point2dCollection End2End(this Point2dCollection ptcol!!) /// /// 首尾相连 /// - public static Point3dCollection End2End(this Point3dCollection ptcol!!) + public static Point3dCollection End2End(this Point3dCollection ptcol) { + if (ptcol == null) + throw new ArgumentNullException(nameof(ptcol)); + if (ptcol.Count == 0 || ptcol[0].Equals(ptcol[^1]))//首尾相同直接返回 return ptcol; diff --git "a/src/IFoxCAD.Cad/ExtensionMethod/\346\226\260\345\273\272\345\241\253\345\205\205/HatchInfo.cs" "b/src/IFoxCAD.Cad/ExtensionMethod/\346\226\260\345\273\272\345\241\253\345\205\205/HatchInfo.cs" index 1d66417..1226479 100644 --- "a/src/IFoxCAD.Cad/ExtensionMethod/\346\226\260\345\273\272\345\241\253\345\205\205/HatchInfo.cs" +++ "b/src/IFoxCAD.Cad/ExtensionMethod/\346\226\260\345\273\272\345\241\253\345\205\205/HatchInfo.cs" @@ -259,11 +259,14 @@ void AppendLoop(IEnumerable boundaryIds, /// ˿ռ /// 뷽ʽ /// - public HatchInfo AppendLoop(Point2dCollection pts!!, - DoubleCollection bluges, - BlockTableRecord btrOfAddEntitySpace, - HatchLoopTypes hatchLoopTypes = HatchLoopTypes.Default) + public HatchInfo AppendLoop(Point2dCollection pts, + DoubleCollection bluges, + BlockTableRecord btrOfAddEntitySpace, + HatchLoopTypes hatchLoopTypes = HatchLoopTypes.Default) { + if (pts == null) + throw new ArgumentNullException(nameof(pts)); + var ptsEnd2End = pts.End2End(); #if NET35 _boundaryIds.Add(CreateAddBoundary(ptsEnd2End, bluges, btrOfAddEntitySpace)); diff --git a/src/IFoxCAD.Cad/Runtime/AcadVersion.cs b/src/IFoxCAD.Cad/Runtime/AcadVersion.cs index 94c7947..01376c8 100644 --- a/src/IFoxCAD.Cad/Runtime/AcadVersion.cs +++ b/src/IFoxCAD.Cad/Runtime/AcadVersion.cs @@ -1,4 +1,6 @@ -namespace IFoxCAD.Cad; +using Autodesk.AutoCAD.Windows.ToolPalette; + +namespace IFoxCAD.Cad; /// /// cad版本号类 @@ -50,16 +52,18 @@ public static List Versions /// 已打开的cad的版本号 /// 已打开cad的application对象 /// cad版本号对象 - public static CadVersion? FromApp(object app!!) + public static CadVersion? FromApp(object app) { - string acver = - app.GetType() - .InvokeMember( - "Version", - BindingFlags.GetProperty, - null, - app, - new object[0]).ToString(); + if (app == null) + throw new ArgumentNullException(nameof(app)); + + string acver = app.GetType() + .InvokeMember( + "Version", + BindingFlags.GetProperty, + null, + app, + new object[0]).ToString(); var gs = Regex.Match(acver, @"(\d+)\.(\d+).*?").Groups; int major = int.Parse(gs[1].Value); diff --git a/tests/Test/TestLisp.cs b/tests/Test/TestLisp.cs index c6e1f47..f657286 100644 --- a/tests/Test/TestLisp.cs +++ b/tests/Test/TestLisp.cs @@ -51,10 +51,10 @@ public static object LispTest_RunLisp(ResultBuffer rb) //不能在参照块中使用命令 [CommandMethod("CmdTest_RunLisp20", CommandFlags.NoBlockEditor)] #if ac2009 - //acad09增,不会被动作录制器 捕捉到 - [CommandMethod("CmdTest_RunLisp21", CommandFlags.NoActionRecording)] - //acad09增,会被动作录制器捕捉 - [CommandMethod("CmdTest_RunLisp22", CommandFlags.ActionMacro)] + //acad09增,不会被动作录制器 捕捉到 + [CommandMethod("CmdTest_RunLisp21", CommandFlags.NoActionRecording)] + //acad09增,会被动作录制器捕捉 + [CommandMethod("CmdTest_RunLisp22", CommandFlags.ActionMacro)] #endif #if !NET35 //推断约束时不能使用命令 -- Gitee From c1dd6bcc47a7e7a63296ef3640a472ffee4d5f81 Mon Sep 17 00:00:00 2001 From: liuqihong <540762622@qq.com> Date: Fri, 12 Aug 2022 14:38:23 +0800 Subject: [PATCH 348/675] =?UTF-8?q?=E7=A7=BB=E9=99=A4=E6=89=80=E6=9C=89Aut?= =?UTF-8?q?odesk.AutoCAD=E5=91=BD=E5=90=8D=E7=A9=BA=E9=97=B4=E5=88=B0?= =?UTF-8?q?=E5=85=A8=E5=B1=80=E6=96=B9=E4=BE=BF=E7=A7=BB=E6=A4=8D=E5=88=B0?= =?UTF-8?q?=E5=85=B6=E4=BB=96cad?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../ExtensionMethod/DBDictionaryEx.cs | 1 - src/IFoxCAD.Cad/ExtensionMethod/EditorEx.cs | 29 +++++--------- src/IFoxCAD.Cad/ExtensionMethod/Jig.cs | 11 ++--- src/IFoxCAD.Cad/ExtensionMethod/PointEx.cs | 2 - src/IFoxCAD.Cad/GlobalUsings.cs | 10 +++++ src/IFoxCAD.Cad/Runtime/AcadVersion.cs | 40 ++++++++----------- src/IFoxCAD.Cad/Runtime/Env.cs | 3 -- tests/Test/TestJig.cs | 5 +-- tests/Test/testConvexHull.cs | 16 +------- tests/Test/testeditor.cs | 4 +- tests/Test/testselectfilter.cs | 14 +------ 11 files changed, 48 insertions(+), 87 deletions(-) diff --git a/src/IFoxCAD.Cad/ExtensionMethod/DBDictionaryEx.cs b/src/IFoxCAD.Cad/ExtensionMethod/DBDictionaryEx.cs index 2ae0879..89dc2d5 100644 --- a/src/IFoxCAD.Cad/ExtensionMethod/DBDictionaryEx.cs +++ b/src/IFoxCAD.Cad/ExtensionMethod/DBDictionaryEx.cs @@ -1,5 +1,4 @@ namespace IFoxCAD.Cad; -using Group = Autodesk.AutoCAD.DatabaseServices.Group; /// /// 字典扩展类 diff --git a/src/IFoxCAD.Cad/ExtensionMethod/EditorEx.cs b/src/IFoxCAD.Cad/ExtensionMethod/EditorEx.cs index d1cc6a8..47f06f0 100644 --- a/src/IFoxCAD.Cad/ExtensionMethod/EditorEx.cs +++ b/src/IFoxCAD.Cad/ExtensionMethod/EditorEx.cs @@ -98,9 +98,8 @@ public static SelectionSet SelectByLineWeight(this Editor editor, LineWeight lin else ss = editor.GetSelection(pso); } - catch (Autodesk.AutoCAD.Runtime.Exception e) - { - + catch (Exception e) + { editor.WriteMessage($"\nKey is {e.Message}"); } return ss; @@ -664,13 +663,13 @@ public static Matrix3d GetMatrixFromWcsToMDcs(this Editor editor) public static Matrix3d GetMatrixFromMDcsToPDcs(this Editor editor) { if ((short)Env.GetVar("TILEMODE") == 1) - throw new Autodesk.AutoCAD.Runtime.Exception(ErrorStatus.InvalidInput, "Espace papier uniquement"); + throw new ArgumentException("TILEMODE == 1..Espace papier uniquement"); Database db = editor.Document.Database; Matrix3d mat; using (Transaction tr = db.TransactionManager.StartTransaction()) { - Viewport? vp = tr.GetObject(editor.CurrentViewportObjectId, OpenMode.ForRead) as Viewport; + var vp = tr.GetObject(editor.CurrentViewportObjectId, OpenMode.ForRead) as Viewport; if (vp?.Number == 1) { try @@ -681,7 +680,7 @@ public static Matrix3d GetMatrixFromMDcsToPDcs(this Editor editor) } catch { - throw new Autodesk.AutoCAD.Runtime.Exception(ErrorStatus.InvalidInput, "Aucun fenêtre active"); + throw new Exception("Aucun fenêtre active...ErrorStatus.InvalidInput"); } } Point3d vCtr = new(vp!.ViewCenter.X, vp.ViewCenter.Y, 0.0); @@ -725,9 +724,7 @@ public static Matrix3d GetMatrix(this Editor editor, CoordinateSystemCode from, return editor.GetMatrixFromMDcsToWcs(); case CoordinateSystemCode.PDcs: - throw new Autodesk.AutoCAD.Runtime.Exception( - ErrorStatus.InvalidInput, - "To be used only with DCS"); + throw new Exception("To be used only with DCS...ErrorStatus.InvalidInput"); } break; case CoordinateSystemCode.Ucs: @@ -740,9 +737,7 @@ public static Matrix3d GetMatrix(this Editor editor, CoordinateSystemCode from, return editor.GetMatrixFromUcsToWcs() * editor.GetMatrixFromWcsToMDcs(); case CoordinateSystemCode.PDcs: - throw new Autodesk.AutoCAD.Runtime.Exception( - ErrorStatus.InvalidInput, - "To be used only with DCS"); + throw new Exception("To be used only with DCS... ErrorStatus.InvalidInput"); } break; case CoordinateSystemCode.MDcs: @@ -762,13 +757,9 @@ public static Matrix3d GetMatrix(this Editor editor, CoordinateSystemCode from, switch (to) { case CoordinateSystemCode.Wcs: - throw new Autodesk.AutoCAD.Runtime.Exception( - ErrorStatus.InvalidInput, - "To be used only with DCS"); + throw new Exception("To be used only with DCS... ErrorStatus.InvalidInput"); case CoordinateSystemCode.Ucs: - throw new Autodesk.AutoCAD.Runtime.Exception( - ErrorStatus.InvalidInput, - "To be used only with DCS"); + throw new Exception("To be used only with DCS... ErrorStatus.InvalidInput"); case CoordinateSystemCode.MDcs: return editor.GetMatrixFromPDcsToMDcs(); } @@ -787,7 +778,7 @@ public static Matrix3d GetMatrix(this Editor editor, CoordinateSystemCode from, (CoordinateSystemCode.MDcs, CoordinateSystemCode.PDcs) => editor.GetMatrixFromMDcsToPDcs(), (CoordinateSystemCode.PDcs, CoordinateSystemCode.MDcs) => editor.GetMatrixFromPDcsToMDcs(), (CoordinateSystemCode.PDcs, CoordinateSystemCode.Wcs or CoordinateSystemCode.Ucs) - or (CoordinateSystemCode.Wcs or CoordinateSystemCode.Ucs, CoordinateSystemCode.PDcs) => throw new Autodesk.AutoCAD.Runtime.Exception(ErrorStatus.InvalidInput, "To be used only with DCS"), + or (CoordinateSystemCode.Wcs or CoordinateSystemCode.Ucs, CoordinateSystemCode.PDcs) => throw new Exception("To be used only with DCS...ErrorStatus.InvalidInput"), (_, _) => Matrix3d.Identity }; #endif diff --git a/src/IFoxCAD.Cad/ExtensionMethod/Jig.cs b/src/IFoxCAD.Cad/ExtensionMethod/Jig.cs index 5485e6d..b9182ae 100644 --- a/src/IFoxCAD.Cad/ExtensionMethod/Jig.cs +++ b/src/IFoxCAD.Cad/ExtensionMethod/Jig.cs @@ -1,4 +1,6 @@ -/* 封装jig +namespace IFoxCAD.Cad; + +/* 封装jig * 20220726 隐藏事件,利用函数进行数据库图元重绘 * 20220710 修改SetOption()的空格结束,并添加例子到IFox * 20220503 cad22需要防止刷新过程中更改队列,是因为允许函数重入导致,08不会有. @@ -9,11 +11,6 @@ * 博客: https://www.cnblogs.com/JJBox/p/15650770.html */ -namespace IFoxCAD.Cad; - -//此命名空间容易引起Polyline等等重义,因此不放入全局空间 -using Autodesk.AutoCAD.GraphicsInterface; - public delegate void WorldDrawEvent(WorldDraw draw); public class JigEx : DrawJig { @@ -264,7 +261,7 @@ public void DatabaseEntityDraw(WorldDrawEvent action) * 在jig使用淡显ent.Unhighlight和亮显ent.Highlight() * 需要绕过重绘,否则重绘将导致图元频闪,令这两个操作失效, * 此时需要自定义一个集合 EntityList (不使用本函数的_drawEntitys) - * 再将 EntityList 传给 WorldDrawEvent 事件,事件内实现亮显和淡显. + * 再将 EntityList 传给 WorldDrawEvent 事件,事件内实现亮显和淡显(事件已经利用 DatabaseEntityDraw函数进行提供). * 0x03 * draw.Geometry.Draw(_drawEntitys[i]); * 此函数有问题,acad08克隆一份数组也可以用来刷新, diff --git a/src/IFoxCAD.Cad/ExtensionMethod/PointEx.cs b/src/IFoxCAD.Cad/ExtensionMethod/PointEx.cs index cf71f5f..4898d50 100644 --- a/src/IFoxCAD.Cad/ExtensionMethod/PointEx.cs +++ b/src/IFoxCAD.Cad/ExtensionMethod/PointEx.cs @@ -1,5 +1,3 @@ -using Autodesk.AutoCAD.DatabaseServices; - namespace IFoxCAD.Cad; public static class PointEx diff --git a/src/IFoxCAD.Cad/GlobalUsings.cs b/src/IFoxCAD.Cad/GlobalUsings.cs index 7051b36..a125299 100644 --- a/src/IFoxCAD.Cad/GlobalUsings.cs +++ b/src/IFoxCAD.Cad/GlobalUsings.cs @@ -11,6 +11,8 @@ global using System.ComponentModel; global using System.Runtime.InteropServices; +global using Exception = System.Exception; + /// autocad 引用 global using Autodesk.AutoCAD.ApplicationServices; global using Autodesk.AutoCAD.EditorInput; @@ -21,7 +23,15 @@ global using Acap = Autodesk.AutoCAD.ApplicationServices.Application; global using Autodesk.AutoCAD.DatabaseServices.Filters; +global using Autodesk.AutoCAD; + +//jig此命名空间容易引起Polyline等等重义,因此不放入全局空间 +//using Autodesk.AutoCAD.GraphicsInterface; +global using WorldDraw = Autodesk.AutoCAD.GraphicsInterface.WorldDraw; +global using Manager = Autodesk.AutoCAD.GraphicsSystem.Manager; +global using Group = Autodesk.AutoCAD.DatabaseServices.Group; +global using Viewport = Autodesk.AutoCAD.DatabaseServices.Viewport; global using System.Collections.Specialized; global using Registry = Microsoft.Win32.Registry; diff --git a/src/IFoxCAD.Cad/Runtime/AcadVersion.cs b/src/IFoxCAD.Cad/Runtime/AcadVersion.cs index 01376c8..f8f5273 100644 --- a/src/IFoxCAD.Cad/Runtime/AcadVersion.cs +++ b/src/IFoxCAD.Cad/Runtime/AcadVersion.cs @@ -1,6 +1,4 @@ -using Autodesk.AutoCAD.Windows.ToolPalette; - -namespace IFoxCAD.Cad; +namespace IFoxCAD.Cad; /// /// cad版本号类 @@ -16,34 +14,30 @@ public static List Versions { get { - - string[] copys = - Registry.LocalMachine - .OpenSubKey(@"SOFTWARE\Autodesk\Hardcopy") - .GetValueNames(); + string[] copys = Registry.LocalMachine + .OpenSubKey(@"SOFTWARE\Autodesk\Hardcopy") + .GetValueNames(); + var _versions = new List(); foreach (var rootkey in copys) { - if (Regex.IsMatch(rootkey, _pattern)) + if (!Regex.IsMatch(rootkey, _pattern)) + continue; + + var gs = Regex.Match(rootkey, _pattern).Groups; + var ver = new CadVersion { - var gs = Regex.Match(rootkey, _pattern).Groups; - var ver = - new CadVersion - { - ProductRootKey = rootkey, - ProductName = - Registry.LocalMachine + ProductRootKey = rootkey, + ProductName = Registry.LocalMachine .OpenSubKey("SOFTWARE") .OpenSubKey(rootkey) .GetValue("ProductName") .ToString(), - Major = int.Parse(gs[1].Value), - Minor = int.Parse(gs[2].Value), - }; - - _versions.Add(ver); - } + Major = int.Parse(gs[1].Value), + Minor = int.Parse(gs[2].Value), + }; + _versions.Add(ver); } return _versions; } @@ -57,7 +51,7 @@ public static List Versions if (app == null) throw new ArgumentNullException(nameof(app)); - string acver = app.GetType() + string acver = app.GetType() .InvokeMember( "Version", BindingFlags.GetProperty, diff --git a/src/IFoxCAD.Cad/Runtime/Env.cs b/src/IFoxCAD.Cad/Runtime/Env.cs index e5d34dc..379504b 100644 --- a/src/IFoxCAD.Cad/Runtime/Env.cs +++ b/src/IFoxCAD.Cad/Runtime/Env.cs @@ -1,8 +1,5 @@ namespace IFoxCAD.Cad; -//此命名空间容易引起Polyline等等重义,因此不放入全局空间 -using Autodesk.AutoCAD.GraphicsSystem; - /// /// 系统管理类 /// 封装了一些系统 osmode、cmdecho、dimblk 系统变量 diff --git a/tests/Test/TestJig.cs b/tests/Test/TestJig.cs index 605958c..0fb1310 100644 --- a/tests/Test/TestJig.cs +++ b/tests/Test/TestJig.cs @@ -121,8 +121,7 @@ public void TestCmd_Jig44() [CommandMethod("TestCmd_loop")] public void TestCmd_loop() { - DocumentCollection dm = - Autodesk.AutoCAD.ApplicationServices.Application.DocumentManager; + DocumentCollection dm = Acap.DocumentManager; Editor ed = dm.MdiActiveDocument.Editor; // Create and add our message filter MyMessageFilter filter = new(); @@ -172,7 +171,7 @@ public bool PreFilterMessage(ref Message m) [CommandMethod("TestCmd_QuickText")] static public void TestCmd_QuickText() { - var dm = Autodesk.AutoCAD.ApplicationServices.Application.DocumentManager; + var dm = Acap.DocumentManager; var doc = dm.MdiActiveDocument; var db = doc.Database; var ed = doc.Editor; diff --git a/tests/Test/testConvexHull.cs b/tests/Test/testConvexHull.cs index 19c3da7..c4b9496 100644 --- a/tests/Test/testConvexHull.cs +++ b/tests/Test/testConvexHull.cs @@ -1,16 +1,4 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; - -using Autodesk.AutoCAD.Runtime; -using Autodesk.AutoCAD.Geometry; -using IFoxCAD.Cad; -using Autodesk.AutoCAD.DatabaseServices; -using Autodesk.AutoCAD.Colors; - -namespace Test +namespace Test { public class TestConvexHull { @@ -23,7 +11,7 @@ public void Testch() //while (flag) //{ // var pt = tr.Editor.GetPoint("qudian"); - // if (pt.Status == Autodesk.AutoCAD.EditorInput.PromptStatus.OK) + // if (pt.Status == PromptStatus.OK) // { // pts.Add(pt.Value); // tr.CurrentSpace.AddEntity(new DBPoint(pt.Value)); diff --git a/tests/Test/testeditor.cs b/tests/Test/testeditor.cs index 5c5ac13..7d610cf 100644 --- a/tests/Test/testeditor.cs +++ b/tests/Test/testeditor.cs @@ -28,7 +28,7 @@ public void Testzoom() { using var tr = new DBTrans(); var res = Env.Editor.GetEntity("\npick ent:"); - if (res.Status == Autodesk.AutoCAD.EditorInput.PromptStatus.OK) + if (res.Status == PromptStatus.OK) { Env.Editor.ZoomObject(res.ObjectId.GetObject()); } @@ -40,7 +40,7 @@ public void Testzoomextent() { //using var tr = new DBTrans(); //var res = Env.Editor.GetEntity("\npick ent:"); - //if (res.Status == Autodesk.AutoCAD.EditorInput.PromptStatus.OK) + //if (res.Status == PromptStatus.OK) //{ // Env.Editor.ZoomObject(res.ObjectId.GetObject()); //} diff --git a/tests/Test/testselectfilter.cs b/tests/Test/testselectfilter.cs index 3c403a9..d4056c9 100644 --- a/tests/Test/testselectfilter.cs +++ b/tests/Test/testselectfilter.cs @@ -1,16 +1,4 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; - -using Autodesk.AutoCAD.Geometry; -using Autodesk.AutoCAD.Runtime; -using Autodesk.AutoCAD.DatabaseServices; -using IFoxCAD.Cad; -using Autodesk.AutoCAD.EditorInput; - -namespace Test +namespace Test { public class Testselectfilter { -- Gitee From a9efe02fa01a7a5fba5f84c0714e4331f823d916 Mon Sep 17 00:00:00 2001 From: liuqihong <540762622@qq.com> Date: Fri, 12 Aug 2022 14:42:54 +0800 Subject: [PATCH 349/675] =?UTF-8?q?=E4=BF=AE=E6=94=B9=E5=8F=8C=E6=84=9F?= =?UTF-8?q?=E5=8F=B9=E5=8F=B7=E5=92=8C=E7=A7=BB=E5=8A=A8Autocad=E5=91=BD?= =?UTF-8?q?=E5=90=8D=E7=A9=BA=E9=97=B4=E5=88=B0=E5=85=A8=E5=B1=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/IFoxCAD.Basal/CLS/RuntimeHelpers.cs | 5 +- .../CLS/TupleElementNamesAttribute.cs | 7 ++- src/IFoxCAD.Basal/ListEx.cs | 9 +++- src/IFoxCAD.Cad/Algorithms/Graph/Graph.cs | 32 ++++++----- .../ExtensionMethod/DBDictionaryEx.cs | 1 - src/IFoxCAD.Cad/ExtensionMethod/EditorEx.cs | 29 ++++------ src/IFoxCAD.Cad/ExtensionMethod/Jig.cs | 11 ++-- src/IFoxCAD.Cad/ExtensionMethod/PointEx.cs | 10 +++- .../HatchInfo.cs" | 11 ++-- src/IFoxCAD.Cad/GlobalUsings.cs | 10 ++++ src/IFoxCAD.Cad/IFoxCAD.Cad.csproj.data | 0 src/IFoxCAD.Cad/Runtime/AcadVersion.cs | 54 +++++++++---------- src/IFoxCAD.Cad/Runtime/Env.cs | 5 +- src/IFoxCAD.Cad/Runtime/SymbolTable.cs | 2 +- src/IFoxCAD.Cad/SelectionFilter/OpList.cs | 2 +- src/IFoxCAD.Cad/SelectionFilter/OpLogi.cs | 2 +- 16 files changed, 105 insertions(+), 85 deletions(-) create mode 100644 src/IFoxCAD.Cad/IFoxCAD.Cad.csproj.data diff --git a/src/IFoxCAD.Basal/CLS/RuntimeHelpers.cs b/src/IFoxCAD.Basal/CLS/RuntimeHelpers.cs index b5698b4..a446c59 100644 --- a/src/IFoxCAD.Basal/CLS/RuntimeHelpers.cs +++ b/src/IFoxCAD.Basal/CLS/RuntimeHelpers.cs @@ -10,8 +10,11 @@ public static class RuntimeHelpers /// /// Slices the specified array using the specified range. /// - public static T[] GetSubArray(T[] array!!, Range range) + public static T[] GetSubArray(T[] array, Range range) { + if (array == null) + throw new ArgumentNullException(nameof(array)); + (int offset, int length) = range.GetOffsetAndLength(array.Length); if (default(T)! != null || typeof(T[]) == array.GetType()) // TODO-NULLABLE: default(T) == null warning (https://github.com/dotnet/roslyn/issues/34757) diff --git a/src/IFoxCAD.Basal/CLS/TupleElementNamesAttribute.cs b/src/IFoxCAD.Basal/CLS/TupleElementNamesAttribute.cs index bb0716d..69670b4 100644 --- a/src/IFoxCAD.Basal/CLS/TupleElementNamesAttribute.cs +++ b/src/IFoxCAD.Basal/CLS/TupleElementNamesAttribute.cs @@ -2,6 +2,8 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. +using IFoxCAD.Basal; + namespace System.Runtime.CompilerServices; /// @@ -34,8 +36,11 @@ public sealed class TupleElementNamesAttribute : Attribute /// transformNames value of { "name1", "name2", null, null, /// null }. /// - public TupleElementNamesAttribute(string[] transformNames!!) + public TupleElementNamesAttribute(string[] transformNames) { + if (transformNames == null) + throw new ArgumentNullException(nameof(transformNames)); + _transformNames = transformNames; } diff --git a/src/IFoxCAD.Basal/ListEx.cs b/src/IFoxCAD.Basal/ListEx.cs index b0d20c0..4ead97b 100644 --- a/src/IFoxCAD.Basal/ListEx.cs +++ b/src/IFoxCAD.Basal/ListEx.cs @@ -5,13 +5,18 @@ public static class ListEx { public static bool EqualsAll(this IList a, IList b) { - return EqualsAll(a, b, null); + return EqualsAll(a, b, null); // there is a slight performance gain in passing null here. // It is how it is done in other parts of the framework. } - public static bool EqualsAll(this IList a!!, IList b!!, IEqualityComparer? comparer) + public static bool EqualsAll(this IList a, IList b, IEqualityComparer? comparer) { + if (a == null) + return b == null; + else if (b == null) + return false; + if (a.Count != b.Count) return false; diff --git a/src/IFoxCAD.Cad/Algorithms/Graph/Graph.cs b/src/IFoxCAD.Cad/Algorithms/Graph/Graph.cs index ad1efb5..c1d606f 100644 --- a/src/IFoxCAD.Cad/Algorithms/Graph/Graph.cs +++ b/src/IFoxCAD.Cad/Algorithms/Graph/Graph.cs @@ -82,8 +82,11 @@ public IGraphVertex AddVertex(Point3d pt) /// 向该图添加一个边; /// /// - public void AddEdge(Curve3d curve!!) + public void AddEdge(Curve3d curve) { + if (curve == null) + throw new ArgumentNullException(nameof(curve)); + var start = AddVertex(curve.StartPoint); var end = AddVertex(curve.EndPoint); @@ -141,8 +144,11 @@ public void RemoveVertex(Point3d pt) /// 从此图中删除一条边; /// /// 曲线 - public void RemoveEdge(Curve3d curve!!) + public void RemoveEdge(Curve3d curve) { + if (curve == null) + throw new ArgumentNullException(nameof(curve)); + RemoveVertex(curve.StartPoint); RemoveVertex(curve.EndPoint); } @@ -303,7 +309,7 @@ public string ToReadable() i++; } return output; - } + } #endregion } @@ -473,9 +479,9 @@ public override int GetHashCode() public sealed class DepthFirst { #region 公共方法 -/// -/// 存储所有的边 -/// + /// + /// 存储所有的边 + /// #if true public List> Curve3ds { get; } = new(); #else @@ -494,13 +500,13 @@ public void FindAll(IGraph graph) //var graphtmp = graph.Clone(); foreach (var item in graph.VerticesAsEnumberable) { - Dfs(graph, new LinkedHashSet { item },total); + Dfs(graph, new LinkedHashSet { item }, total); total.Add(item); } } -#endregion + #endregion -#region 内部方法 + #region 内部方法 /// /// 递归 DFS; /// @@ -522,7 +528,7 @@ void Dfs(IGraph graph, LinkedHashSet visited, HashSet { nextNode }; sub.AddRange(visited); // O(n) - Dfs(graph, sub,totalVisited); + Dfs(graph, sub, totalVisited); } // 如果下一点遍历过,并且路径大于2,说明已经找到起点 else if (visited.Count > 2 && nextNode.Equals(visited.Last!.Value)) @@ -613,7 +619,7 @@ static List Invert(List lst, IGraphVertex vertex) return tmp.Skip(index).Concat(lst.Take(index)).ToList(); } - static (string,string) Gethashstring(List pathone, List pathtwo) + static (string, string) Gethashstring(List pathone, List pathtwo) { var one = new string[pathone.Count]; var two = new string[pathtwo.Count]; @@ -637,11 +643,11 @@ static List Invert(List lst, IGraphVertex vertex) } - bool Isnew((string,string) path) + bool Isnew((string, string) path) { return !Curved.Contains(path.Item1) && !Curved.Contains(path.Item2); } -#endregion + #endregion } \ No newline at end of file diff --git a/src/IFoxCAD.Cad/ExtensionMethod/DBDictionaryEx.cs b/src/IFoxCAD.Cad/ExtensionMethod/DBDictionaryEx.cs index 2ae0879..89dc2d5 100644 --- a/src/IFoxCAD.Cad/ExtensionMethod/DBDictionaryEx.cs +++ b/src/IFoxCAD.Cad/ExtensionMethod/DBDictionaryEx.cs @@ -1,5 +1,4 @@ namespace IFoxCAD.Cad; -using Group = Autodesk.AutoCAD.DatabaseServices.Group; /// /// 字典扩展类 diff --git a/src/IFoxCAD.Cad/ExtensionMethod/EditorEx.cs b/src/IFoxCAD.Cad/ExtensionMethod/EditorEx.cs index d1cc6a8..47f06f0 100644 --- a/src/IFoxCAD.Cad/ExtensionMethod/EditorEx.cs +++ b/src/IFoxCAD.Cad/ExtensionMethod/EditorEx.cs @@ -98,9 +98,8 @@ public static SelectionSet SelectByLineWeight(this Editor editor, LineWeight lin else ss = editor.GetSelection(pso); } - catch (Autodesk.AutoCAD.Runtime.Exception e) - { - + catch (Exception e) + { editor.WriteMessage($"\nKey is {e.Message}"); } return ss; @@ -664,13 +663,13 @@ public static Matrix3d GetMatrixFromWcsToMDcs(this Editor editor) public static Matrix3d GetMatrixFromMDcsToPDcs(this Editor editor) { if ((short)Env.GetVar("TILEMODE") == 1) - throw new Autodesk.AutoCAD.Runtime.Exception(ErrorStatus.InvalidInput, "Espace papier uniquement"); + throw new ArgumentException("TILEMODE == 1..Espace papier uniquement"); Database db = editor.Document.Database; Matrix3d mat; using (Transaction tr = db.TransactionManager.StartTransaction()) { - Viewport? vp = tr.GetObject(editor.CurrentViewportObjectId, OpenMode.ForRead) as Viewport; + var vp = tr.GetObject(editor.CurrentViewportObjectId, OpenMode.ForRead) as Viewport; if (vp?.Number == 1) { try @@ -681,7 +680,7 @@ public static Matrix3d GetMatrixFromMDcsToPDcs(this Editor editor) } catch { - throw new Autodesk.AutoCAD.Runtime.Exception(ErrorStatus.InvalidInput, "Aucun fenêtre active"); + throw new Exception("Aucun fenêtre active...ErrorStatus.InvalidInput"); } } Point3d vCtr = new(vp!.ViewCenter.X, vp.ViewCenter.Y, 0.0); @@ -725,9 +724,7 @@ public static Matrix3d GetMatrix(this Editor editor, CoordinateSystemCode from, return editor.GetMatrixFromMDcsToWcs(); case CoordinateSystemCode.PDcs: - throw new Autodesk.AutoCAD.Runtime.Exception( - ErrorStatus.InvalidInput, - "To be used only with DCS"); + throw new Exception("To be used only with DCS...ErrorStatus.InvalidInput"); } break; case CoordinateSystemCode.Ucs: @@ -740,9 +737,7 @@ public static Matrix3d GetMatrix(this Editor editor, CoordinateSystemCode from, return editor.GetMatrixFromUcsToWcs() * editor.GetMatrixFromWcsToMDcs(); case CoordinateSystemCode.PDcs: - throw new Autodesk.AutoCAD.Runtime.Exception( - ErrorStatus.InvalidInput, - "To be used only with DCS"); + throw new Exception("To be used only with DCS... ErrorStatus.InvalidInput"); } break; case CoordinateSystemCode.MDcs: @@ -762,13 +757,9 @@ public static Matrix3d GetMatrix(this Editor editor, CoordinateSystemCode from, switch (to) { case CoordinateSystemCode.Wcs: - throw new Autodesk.AutoCAD.Runtime.Exception( - ErrorStatus.InvalidInput, - "To be used only with DCS"); + throw new Exception("To be used only with DCS... ErrorStatus.InvalidInput"); case CoordinateSystemCode.Ucs: - throw new Autodesk.AutoCAD.Runtime.Exception( - ErrorStatus.InvalidInput, - "To be used only with DCS"); + throw new Exception("To be used only with DCS... ErrorStatus.InvalidInput"); case CoordinateSystemCode.MDcs: return editor.GetMatrixFromPDcsToMDcs(); } @@ -787,7 +778,7 @@ public static Matrix3d GetMatrix(this Editor editor, CoordinateSystemCode from, (CoordinateSystemCode.MDcs, CoordinateSystemCode.PDcs) => editor.GetMatrixFromMDcsToPDcs(), (CoordinateSystemCode.PDcs, CoordinateSystemCode.MDcs) => editor.GetMatrixFromPDcsToMDcs(), (CoordinateSystemCode.PDcs, CoordinateSystemCode.Wcs or CoordinateSystemCode.Ucs) - or (CoordinateSystemCode.Wcs or CoordinateSystemCode.Ucs, CoordinateSystemCode.PDcs) => throw new Autodesk.AutoCAD.Runtime.Exception(ErrorStatus.InvalidInput, "To be used only with DCS"), + or (CoordinateSystemCode.Wcs or CoordinateSystemCode.Ucs, CoordinateSystemCode.PDcs) => throw new Exception("To be used only with DCS...ErrorStatus.InvalidInput"), (_, _) => Matrix3d.Identity }; #endif diff --git a/src/IFoxCAD.Cad/ExtensionMethod/Jig.cs b/src/IFoxCAD.Cad/ExtensionMethod/Jig.cs index 5485e6d..b9182ae 100644 --- a/src/IFoxCAD.Cad/ExtensionMethod/Jig.cs +++ b/src/IFoxCAD.Cad/ExtensionMethod/Jig.cs @@ -1,4 +1,6 @@ -/* 封装jig +namespace IFoxCAD.Cad; + +/* 封装jig * 20220726 隐藏事件,利用函数进行数据库图元重绘 * 20220710 修改SetOption()的空格结束,并添加例子到IFox * 20220503 cad22需要防止刷新过程中更改队列,是因为允许函数重入导致,08不会有. @@ -9,11 +11,6 @@ * 博客: https://www.cnblogs.com/JJBox/p/15650770.html */ -namespace IFoxCAD.Cad; - -//此命名空间容易引起Polyline等等重义,因此不放入全局空间 -using Autodesk.AutoCAD.GraphicsInterface; - public delegate void WorldDrawEvent(WorldDraw draw); public class JigEx : DrawJig { @@ -264,7 +261,7 @@ public void DatabaseEntityDraw(WorldDrawEvent action) * 在jig使用淡显ent.Unhighlight和亮显ent.Highlight() * 需要绕过重绘,否则重绘将导致图元频闪,令这两个操作失效, * 此时需要自定义一个集合 EntityList (不使用本函数的_drawEntitys) - * 再将 EntityList 传给 WorldDrawEvent 事件,事件内实现亮显和淡显. + * 再将 EntityList 传给 WorldDrawEvent 事件,事件内实现亮显和淡显(事件已经利用 DatabaseEntityDraw函数进行提供). * 0x03 * draw.Geometry.Draw(_drawEntitys[i]); * 此函数有问题,acad08克隆一份数组也可以用来刷新, diff --git a/src/IFoxCAD.Cad/ExtensionMethod/PointEx.cs b/src/IFoxCAD.Cad/ExtensionMethod/PointEx.cs index b1bae82..4898d50 100644 --- a/src/IFoxCAD.Cad/ExtensionMethod/PointEx.cs +++ b/src/IFoxCAD.Cad/ExtensionMethod/PointEx.cs @@ -95,8 +95,11 @@ public static double GetArcBulge(this Point2d arc1, Point2d arc2, Point2d arc3, /// /// 首尾相连 /// - public static Point2dCollection End2End(this Point2dCollection ptcol!!) + public static Point2dCollection End2End(this Point2dCollection ptcol) { + if (ptcol == null) + throw new ArgumentNullException(nameof(ptcol)); + if (ptcol.Count == 0 || ptcol[0].Equals(ptcol[^1]))//首尾相同直接返回 return ptcol; @@ -111,8 +114,11 @@ public static Point2dCollection End2End(this Point2dCollection ptcol!!) /// /// 首尾相连 /// - public static Point3dCollection End2End(this Point3dCollection ptcol!!) + public static Point3dCollection End2End(this Point3dCollection ptcol) { + if (ptcol == null) + throw new ArgumentNullException(nameof(ptcol)); + if (ptcol.Count == 0 || ptcol[0].Equals(ptcol[^1]))//首尾相同直接返回 return ptcol; diff --git "a/src/IFoxCAD.Cad/ExtensionMethod/\346\226\260\345\273\272\345\241\253\345\205\205/HatchInfo.cs" "b/src/IFoxCAD.Cad/ExtensionMethod/\346\226\260\345\273\272\345\241\253\345\205\205/HatchInfo.cs" index 1d66417..1226479 100644 --- "a/src/IFoxCAD.Cad/ExtensionMethod/\346\226\260\345\273\272\345\241\253\345\205\205/HatchInfo.cs" +++ "b/src/IFoxCAD.Cad/ExtensionMethod/\346\226\260\345\273\272\345\241\253\345\205\205/HatchInfo.cs" @@ -259,11 +259,14 @@ void AppendLoop(IEnumerable boundaryIds, /// ˿ռ /// 뷽ʽ /// - public HatchInfo AppendLoop(Point2dCollection pts!!, - DoubleCollection bluges, - BlockTableRecord btrOfAddEntitySpace, - HatchLoopTypes hatchLoopTypes = HatchLoopTypes.Default) + public HatchInfo AppendLoop(Point2dCollection pts, + DoubleCollection bluges, + BlockTableRecord btrOfAddEntitySpace, + HatchLoopTypes hatchLoopTypes = HatchLoopTypes.Default) { + if (pts == null) + throw new ArgumentNullException(nameof(pts)); + var ptsEnd2End = pts.End2End(); #if NET35 _boundaryIds.Add(CreateAddBoundary(ptsEnd2End, bluges, btrOfAddEntitySpace)); diff --git a/src/IFoxCAD.Cad/GlobalUsings.cs b/src/IFoxCAD.Cad/GlobalUsings.cs index 7051b36..a125299 100644 --- a/src/IFoxCAD.Cad/GlobalUsings.cs +++ b/src/IFoxCAD.Cad/GlobalUsings.cs @@ -11,6 +11,8 @@ global using System.ComponentModel; global using System.Runtime.InteropServices; +global using Exception = System.Exception; + /// autocad 引用 global using Autodesk.AutoCAD.ApplicationServices; global using Autodesk.AutoCAD.EditorInput; @@ -21,7 +23,15 @@ global using Acap = Autodesk.AutoCAD.ApplicationServices.Application; global using Autodesk.AutoCAD.DatabaseServices.Filters; +global using Autodesk.AutoCAD; + +//jig此命名空间容易引起Polyline等等重义,因此不放入全局空间 +//using Autodesk.AutoCAD.GraphicsInterface; +global using WorldDraw = Autodesk.AutoCAD.GraphicsInterface.WorldDraw; +global using Manager = Autodesk.AutoCAD.GraphicsSystem.Manager; +global using Group = Autodesk.AutoCAD.DatabaseServices.Group; +global using Viewport = Autodesk.AutoCAD.DatabaseServices.Viewport; global using System.Collections.Specialized; global using Registry = Microsoft.Win32.Registry; diff --git a/src/IFoxCAD.Cad/IFoxCAD.Cad.csproj.data b/src/IFoxCAD.Cad/IFoxCAD.Cad.csproj.data new file mode 100644 index 0000000..e69de29 diff --git a/src/IFoxCAD.Cad/Runtime/AcadVersion.cs b/src/IFoxCAD.Cad/Runtime/AcadVersion.cs index 94c7947..f8f5273 100644 --- a/src/IFoxCAD.Cad/Runtime/AcadVersion.cs +++ b/src/IFoxCAD.Cad/Runtime/AcadVersion.cs @@ -14,34 +14,30 @@ public static List Versions { get { - - string[] copys = - Registry.LocalMachine - .OpenSubKey(@"SOFTWARE\Autodesk\Hardcopy") - .GetValueNames(); + string[] copys = Registry.LocalMachine + .OpenSubKey(@"SOFTWARE\Autodesk\Hardcopy") + .GetValueNames(); + var _versions = new List(); foreach (var rootkey in copys) { - if (Regex.IsMatch(rootkey, _pattern)) + if (!Regex.IsMatch(rootkey, _pattern)) + continue; + + var gs = Regex.Match(rootkey, _pattern).Groups; + var ver = new CadVersion { - var gs = Regex.Match(rootkey, _pattern).Groups; - var ver = - new CadVersion - { - ProductRootKey = rootkey, - ProductName = - Registry.LocalMachine + ProductRootKey = rootkey, + ProductName = Registry.LocalMachine .OpenSubKey("SOFTWARE") .OpenSubKey(rootkey) .GetValue("ProductName") .ToString(), - Major = int.Parse(gs[1].Value), - Minor = int.Parse(gs[2].Value), - }; - - _versions.Add(ver); - } + Major = int.Parse(gs[1].Value), + Minor = int.Parse(gs[2].Value), + }; + _versions.Add(ver); } return _versions; } @@ -50,16 +46,18 @@ public static List Versions /// 已打开的cad的版本号 /// 已打开cad的application对象 /// cad版本号对象 - public static CadVersion? FromApp(object app!!) + public static CadVersion? FromApp(object app) { - string acver = - app.GetType() - .InvokeMember( - "Version", - BindingFlags.GetProperty, - null, - app, - new object[0]).ToString(); + if (app == null) + throw new ArgumentNullException(nameof(app)); + + string acver = app.GetType() + .InvokeMember( + "Version", + BindingFlags.GetProperty, + null, + app, + new object[0]).ToString(); var gs = Regex.Match(acver, @"(\d+)\.(\d+).*?").Groups; int major = int.Parse(gs[1].Value); diff --git a/src/IFoxCAD.Cad/Runtime/Env.cs b/src/IFoxCAD.Cad/Runtime/Env.cs index c7c6e92..379504b 100644 --- a/src/IFoxCAD.Cad/Runtime/Env.cs +++ b/src/IFoxCAD.Cad/Runtime/Env.cs @@ -1,7 +1,4 @@ -namespace IFoxCAD.Cad; - -//此命名空间容易引起Polyline等等重义,因此不放入全局空间 -using Autodesk.AutoCAD.GraphicsSystem; +namespace IFoxCAD.Cad; /// /// 系统管理类 diff --git a/src/IFoxCAD.Cad/Runtime/SymbolTable.cs b/src/IFoxCAD.Cad/Runtime/SymbolTable.cs index a31e0ac..00bba7b 100644 --- a/src/IFoxCAD.Cad/Runtime/SymbolTable.cs +++ b/src/IFoxCAD.Cad/Runtime/SymbolTable.cs @@ -1,4 +1,4 @@ -namespace IFoxCAD.Cad; +namespace IFoxCAD.Cad; public class SymbolTable : IEnumerable where TTable : SymbolTable diff --git a/src/IFoxCAD.Cad/SelectionFilter/OpList.cs b/src/IFoxCAD.Cad/SelectionFilter/OpList.cs index 59d1d1f..3a02513 100644 --- a/src/IFoxCAD.Cad/SelectionFilter/OpList.cs +++ b/src/IFoxCAD.Cad/SelectionFilter/OpList.cs @@ -163,4 +163,4 @@ public override void Add(OpFilter value) _lst.Add(value); } } -} \ No newline at end of file +} diff --git a/src/IFoxCAD.Cad/SelectionFilter/OpLogi.cs b/src/IFoxCAD.Cad/SelectionFilter/OpLogi.cs index b61583d..4227608 100644 --- a/src/IFoxCAD.Cad/SelectionFilter/OpLogi.cs +++ b/src/IFoxCAD.Cad/SelectionFilter/OpLogi.cs @@ -125,4 +125,4 @@ public override IEnumerator GetEnumerator() yield return Left; yield return Right; } -} \ No newline at end of file +} -- Gitee From f03f397f4970a1df9bd05279ac91eb28c1ceab3d Mon Sep 17 00:00:00 2001 From: liuqihong <540762622@qq.com> Date: Sat, 13 Aug 2022 00:03:55 +0800 Subject: [PATCH 350/675] =?UTF-8?q?=E9=99=A4=E4=BA=86IFoxCAD.Cad.csproj?= =?UTF-8?q?=E4=B8=8D=E4=B8=80=E6=A0=B7=E5=85=B6=E4=BB=96=E9=83=BD=E4=B8=80?= =?UTF-8?q?=E6=A0=B7?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .editorconfig | 217 ++ .gitattributes | 2 +- .gitignore | 128 +- IFoxCAD.sln | 14 +- README.md | 163 +- docs/DBTrans.md | 4 +- docs/WPF.md | 2 +- ...4\344\272\214\347\273\264\347\240\201.png" | Bin 0 -> 20425 bytes docs/png/nuget.png | Bin 0 -> 159167 bytes docs/png/nuget1.png | Bin 0 -> 27373 bytes docs/png/standard.png | Bin 0 -> 148673 bytes ...66\346\236\204\350\257\264\346\230\216.md" | 8 +- src/IFoxCAD.Basal/ArrayEx.cs | 50 + src/IFoxCAD.Basal/CLS/Index.cs | 148 + src/IFoxCAD.Basal/CLS/Range.cs | 103 + src/IFoxCAD.Basal/CLS/RuntimeHelpers.cs | 44 + .../CLS/TupleElementNamesAttribute.cs | 53 + src/IFoxCAD.Basal/CLS/ValueTuple.cs | 2144 ++++++++++++ src/IFoxCAD.Basal/DictEx.cs | 21 + src/IFoxCAD.Basal/GlobalUsings.cs | 9 + src/IFoxCAD.Basal/IFoxCAD.Basal.csproj | 75 +- src/IFoxCAD.Basal/LinkedHashMap.cs | 171 + src/IFoxCAD.Basal/LinkedHashSet.cs | 221 ++ src/IFoxCAD.Basal/LinqEx.cs | 543 ++- src/IFoxCAD.Basal/ListEx.cs | 30 + src/IFoxCAD.Basal/LoopList.cs | 1223 +++---- src/IFoxCAD.Basal/Sortedset/ISet.cs | 71 + src/IFoxCAD.Basal/Sortedset/SR.cs | 907 +++++ src/IFoxCAD.Basal/Sortedset/Sortedset.cs | 2935 +++++++++++++++++ src/IFoxCAD.Basal/Sortedset/ThrowHelper.cs | 382 +++ src/IFoxCAD.Basal/Sortedset/bithelper.cs | 173 + src/IFoxCAD.Cad/Algorithms/Graph/Graph.cs | 653 ++++ src/IFoxCAD.Cad/Algorithms/Graph/IGraph.cs | 98 + .../Algorithms/QuadTree/QuadEntity.cs | 25 + .../Algorithms/QuadTree/QuadTree.cs | 259 ++ .../Algorithms/QuadTree/QuadTreeEvn.cs | 26 + .../Algorithms/QuadTree/QuadTreeNode.cs | 818 +++++ .../Algorithms/QuadTree/QuadTreeSelectMode.cs | 21 + src/IFoxCAD.Cad/Algorithms/QuadTree/Rect.cs | 605 ++++ .../Algorithms/QuadTree/Utility.cs | 60 + .../ExtensionMethod/BulgeVertexWidth.cs | 85 + .../ExtensionMethod/CollectionEx.cs | 291 +- src/IFoxCAD.Cad/ExtensionMethod/Curve2dEx.cs | 530 ++- src/IFoxCAD.Cad/ExtensionMethod/Curve3dEx.cs | 920 +++--- src/IFoxCAD.Cad/ExtensionMethod/CurveEx.cs | 1400 +++----- .../ExtensionMethod/DBDictionaryEx.cs | 621 ++-- src/IFoxCAD.Cad/ExtensionMethod/DBObjectEx.cs | 262 +- src/IFoxCAD.Cad/ExtensionMethod/DatabaseEx.cs | 23 + src/IFoxCAD.Cad/ExtensionMethod/EditorEx.cs | 1591 +++++---- src/IFoxCAD.Cad/ExtensionMethod/EntityEx.cs | 730 ++-- src/IFoxCAD.Cad/ExtensionMethod/Enums.cs | 161 +- src/IFoxCAD.Cad/ExtensionMethod/GeometryEx.cs | 1189 +++---- src/IFoxCAD.Cad/ExtensionMethod/Jig.cs | 340 ++ src/IFoxCAD.Cad/ExtensionMethod/ObjEx.cs | 21 + src/IFoxCAD.Cad/ExtensionMethod/ObjectIdEx.cs | 133 +- src/IFoxCAD.Cad/ExtensionMethod/PointEx.cs | 134 + .../ExtensionMethod/SelectionSetEx.cs | 173 +- .../ExtensionMethod/SymbolTableEx.cs | 540 +-- .../ExtensionMethod/SymbolTableRecordEx.cs | 721 ++-- src/IFoxCAD.Cad/ExtensionMethod/Tools.cs | 188 ++ .../HatchConverter.cs" | 358 ++ .../HatchEx.cs" | 15 + .../HatchInfo.cs" | 354 ++ .../AttachmentPointHelper.cs" | 95 + .../TextEntityAdd.cs" | 62 + .../TextInfo.cs" | 178 + src/IFoxCAD.Cad/GlobalUsings.cs | 41 + src/IFoxCAD.Cad/IFoxCAD.Cad.csproj | 136 +- src/IFoxCAD.Cad/IFoxCAD.Cad.csproj.data | 1 - src/IFoxCAD.Cad/ResultData/LispDottedPair.cs | 106 +- src/IFoxCAD.Cad/ResultData/LispList.cs | 353 +- src/IFoxCAD.Cad/ResultData/TypedValueList.cs | 114 +- src/IFoxCAD.Cad/ResultData/XRecordDataList.cs | 111 +- src/IFoxCAD.Cad/ResultData/XdataList.cs | 115 +- src/IFoxCAD.Cad/Runtime/AOP.cs | 99 + src/IFoxCAD.Cad/Runtime/AcadVersion.cs | 166 +- src/IFoxCAD.Cad/Runtime/AssemInfo.cs | 105 +- src/IFoxCAD.Cad/Runtime/AutoRegAssem.cs | 242 +- src/IFoxCAD.Cad/Runtime/CadVersion.cs | 92 + src/IFoxCAD.Cad/Runtime/DBTrans.cs | 699 ++-- src/IFoxCAD.Cad/Runtime/Env.cs | 793 ++--- src/IFoxCAD.Cad/Runtime/FileOpenMode.cs | 13 + src/IFoxCAD.Cad/Runtime/IAutoGo.cs | 307 ++ src/IFoxCAD.Cad/Runtime/Log.cs | 412 +++ src/IFoxCAD.Cad/Runtime/MethodInfoHelper.cs | 53 + src/IFoxCAD.Cad/Runtime/SymbolTable.cs | 544 +-- src/IFoxCAD.Cad/Runtime/Utils.cs | 98 + src/IFoxCAD.Cad/SelectionFilter/OpComp.cs | 138 +- src/IFoxCAD.Cad/SelectionFilter/OpEqual.cs | 148 +- src/IFoxCAD.Cad/SelectionFilter/OpFilter.cs | 582 ++-- src/IFoxCAD.Cad/SelectionFilter/OpList.cs | 274 +- src/IFoxCAD.Cad/SelectionFilter/OpLogi.cs | 207 +- src/IFoxCAD.WPF/Converter.cs | 176 +- src/IFoxCAD.WPF/DependencyObjectExtensions.cs | 52 +- src/IFoxCAD.WPF/EnumSelection.cs | 72 + src/IFoxCAD.WPF/EventBindingExtension.cs | 337 +- src/IFoxCAD.WPF/GlobalUsings.cs | 18 + src/IFoxCAD.WPF/IFoxCAD.WPF.csproj | 67 +- src/IFoxCAD.WPF/RelayCommand.cs | 316 +- src/IFoxCAD.WPF/ViewModelBase.cs | 103 +- tests/Test/CmdINI.cs | 111 + tests/Test/GlobalUsings.cs | 30 + tests/Test/Properties/launchSettings.json | 2 +- tests/Test/Test.cs | 818 ++--- tests/Test/Test.csproj | 29 +- tests/Test/TestAOP.cs | 66 + tests/Test/TestCurve.cs | 158 + tests/Test/TestDBTrans.cs | 78 + tests/Test/TestEnt.cs | 40 + tests/Test/TestFileDatabase.cs | 29 + tests/Test/TestJig.cs | 334 ++ tests/Test/TestLisp.cs | 122 + tests/Test/TestLoop.cs | 36 + tests/Test/TestMirrorFile.cs | 73 + tests/Test/TestPoint.cs | 160 + tests/Test/TestQuadTree.cs | 461 +++ tests/Test/Testid.cs | 74 + tests/Test/testConvexHull.cs | 20 +- tests/Test/testblock.cs | 631 ++++ tests/Test/testeditor.cs | 95 +- tests/Test/testenv.cs | 136 +- tests/Test/testselectfilter.cs | 28 +- tests/Test/wpf/Class1.cs | 13 + tests/Test/wpf/TestView.xaml | 4 +- tests/Test/wpf/TestView.xaml.cs | 2 +- tests/Test/wpf/TestViewModel.cs | 156 +- tests/TestConsole/Program.cs | 55 + tests/TestConsole/RuntimeHelpers.cs | 50 + tests/TestConsole/TestConsole.csproj | 22 + ...50\350\276\276\345\274\217\346\240\221.cs" | 143 + 130 files changed, 25440 insertions(+), 9147 deletions(-) create mode 100644 .editorconfig create mode 100644 "docs/png/ifoxcad\347\224\250\346\210\267\344\272\244\346\265\201\347\276\244\347\276\244\344\272\214\347\273\264\347\240\201.png" create mode 100644 docs/png/nuget.png create mode 100644 docs/png/nuget1.png create mode 100644 docs/png/standard.png create mode 100644 src/IFoxCAD.Basal/ArrayEx.cs create mode 100644 src/IFoxCAD.Basal/CLS/Index.cs create mode 100644 src/IFoxCAD.Basal/CLS/Range.cs create mode 100644 src/IFoxCAD.Basal/CLS/RuntimeHelpers.cs create mode 100644 src/IFoxCAD.Basal/CLS/TupleElementNamesAttribute.cs create mode 100644 src/IFoxCAD.Basal/CLS/ValueTuple.cs create mode 100644 src/IFoxCAD.Basal/DictEx.cs create mode 100644 src/IFoxCAD.Basal/GlobalUsings.cs create mode 100644 src/IFoxCAD.Basal/LinkedHashMap.cs create mode 100644 src/IFoxCAD.Basal/LinkedHashSet.cs create mode 100644 src/IFoxCAD.Basal/ListEx.cs create mode 100644 src/IFoxCAD.Basal/Sortedset/ISet.cs create mode 100644 src/IFoxCAD.Basal/Sortedset/SR.cs create mode 100644 src/IFoxCAD.Basal/Sortedset/Sortedset.cs create mode 100644 src/IFoxCAD.Basal/Sortedset/ThrowHelper.cs create mode 100644 src/IFoxCAD.Basal/Sortedset/bithelper.cs create mode 100644 src/IFoxCAD.Cad/Algorithms/Graph/Graph.cs create mode 100644 src/IFoxCAD.Cad/Algorithms/Graph/IGraph.cs create mode 100644 src/IFoxCAD.Cad/Algorithms/QuadTree/QuadEntity.cs create mode 100644 src/IFoxCAD.Cad/Algorithms/QuadTree/QuadTree.cs create mode 100644 src/IFoxCAD.Cad/Algorithms/QuadTree/QuadTreeEvn.cs create mode 100644 src/IFoxCAD.Cad/Algorithms/QuadTree/QuadTreeNode.cs create mode 100644 src/IFoxCAD.Cad/Algorithms/QuadTree/QuadTreeSelectMode.cs create mode 100644 src/IFoxCAD.Cad/Algorithms/QuadTree/Rect.cs create mode 100644 src/IFoxCAD.Cad/Algorithms/QuadTree/Utility.cs create mode 100644 src/IFoxCAD.Cad/ExtensionMethod/BulgeVertexWidth.cs create mode 100644 src/IFoxCAD.Cad/ExtensionMethod/DatabaseEx.cs create mode 100644 src/IFoxCAD.Cad/ExtensionMethod/Jig.cs create mode 100644 src/IFoxCAD.Cad/ExtensionMethod/ObjEx.cs create mode 100644 src/IFoxCAD.Cad/ExtensionMethod/PointEx.cs create mode 100644 src/IFoxCAD.Cad/ExtensionMethod/Tools.cs create mode 100644 "src/IFoxCAD.Cad/ExtensionMethod/\346\226\260\345\273\272\345\241\253\345\205\205/HatchConverter.cs" create mode 100644 "src/IFoxCAD.Cad/ExtensionMethod/\346\226\260\345\273\272\345\241\253\345\205\205/HatchEx.cs" create mode 100644 "src/IFoxCAD.Cad/ExtensionMethod/\346\226\260\345\273\272\345\241\253\345\205\205/HatchInfo.cs" create mode 100644 "src/IFoxCAD.Cad/ExtensionMethod/\346\226\260\345\273\272\346\226\207\345\255\227/AttachmentPointHelper.cs" create mode 100644 "src/IFoxCAD.Cad/ExtensionMethod/\346\226\260\345\273\272\346\226\207\345\255\227/TextEntityAdd.cs" create mode 100644 "src/IFoxCAD.Cad/ExtensionMethod/\346\226\260\345\273\272\346\226\207\345\255\227/TextInfo.cs" create mode 100644 src/IFoxCAD.Cad/GlobalUsings.cs delete mode 100644 src/IFoxCAD.Cad/IFoxCAD.Cad.csproj.data create mode 100644 src/IFoxCAD.Cad/Runtime/AOP.cs create mode 100644 src/IFoxCAD.Cad/Runtime/CadVersion.cs create mode 100644 src/IFoxCAD.Cad/Runtime/FileOpenMode.cs create mode 100644 src/IFoxCAD.Cad/Runtime/IAutoGo.cs create mode 100644 src/IFoxCAD.Cad/Runtime/Log.cs create mode 100644 src/IFoxCAD.Cad/Runtime/MethodInfoHelper.cs create mode 100644 src/IFoxCAD.Cad/Runtime/Utils.cs create mode 100644 src/IFoxCAD.WPF/EnumSelection.cs create mode 100644 src/IFoxCAD.WPF/GlobalUsings.cs create mode 100644 tests/Test/CmdINI.cs create mode 100644 tests/Test/GlobalUsings.cs create mode 100644 tests/Test/TestAOP.cs create mode 100644 tests/Test/TestCurve.cs create mode 100644 tests/Test/TestDBTrans.cs create mode 100644 tests/Test/TestEnt.cs create mode 100644 tests/Test/TestFileDatabase.cs create mode 100644 tests/Test/TestJig.cs create mode 100644 tests/Test/TestLisp.cs create mode 100644 tests/Test/TestLoop.cs create mode 100644 tests/Test/TestMirrorFile.cs create mode 100644 tests/Test/TestPoint.cs create mode 100644 tests/Test/TestQuadTree.cs create mode 100644 tests/Test/Testid.cs create mode 100644 tests/Test/testblock.cs create mode 100644 tests/Test/wpf/Class1.cs create mode 100644 tests/TestConsole/Program.cs create mode 100644 tests/TestConsole/RuntimeHelpers.cs create mode 100644 tests/TestConsole/TestConsole.csproj create mode 100644 "tests/TestConsole/\350\241\250\350\276\276\345\274\217\346\240\221.cs" diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 0000000..f0012cd --- /dev/null +++ b/.editorconfig @@ -0,0 +1,217 @@ +# 如果要从更高级别的目录继承 .editorconfig 设置,请删除以下行 +root = true + +# c# 文件 +[*.cs] + +#### Core EditorConfig 选项 #### + +# 缩进和间距 +indent_size = 4 +indent_style = space +tab_width = 4 + +# 新行首选项 +end_of_line = crlf +insert_final_newline = false + +#### .NET 编码约定 #### + +# 组织 Using +dotnet_separate_import_directive_groups = false +dotnet_sort_system_directives_first = false +file_header_template = unset + +# this. 和 Me. 首选项 +dotnet_style_qualification_for_event = false +dotnet_style_qualification_for_field = false +dotnet_style_qualification_for_method = false +dotnet_style_qualification_for_property = false + +# 语言关键字与 bcl 类型首选项 +dotnet_style_predefined_type_for_locals_parameters_members = true +dotnet_style_predefined_type_for_member_access = true + +# 括号首选项 +dotnet_style_parentheses_in_arithmetic_binary_operators = always_for_clarity +dotnet_style_parentheses_in_other_binary_operators = always_for_clarity +dotnet_style_parentheses_in_other_operators = never_if_unnecessary +dotnet_style_parentheses_in_relational_binary_operators = always_for_clarity + +# 修饰符首选项 +dotnet_style_require_accessibility_modifiers = for_non_interface_members + +# 表达式级首选项 +dotnet_style_coalesce_expression = true +dotnet_style_collection_initializer = true +dotnet_style_explicit_tuple_names = true +dotnet_style_namespace_match_folder = true +dotnet_style_null_propagation = true +dotnet_style_object_initializer = true +dotnet_style_operator_placement_when_wrapping = beginning_of_line +dotnet_style_prefer_auto_properties = true +dotnet_style_prefer_compound_assignment = true +dotnet_style_prefer_conditional_expression_over_assignment = true +dotnet_style_prefer_conditional_expression_over_return = true +dotnet_style_prefer_inferred_anonymous_type_member_names = true +dotnet_style_prefer_inferred_tuple_names = true +dotnet_style_prefer_is_null_check_over_reference_equality_method = true:error +dotnet_style_prefer_simplified_boolean_expressions = true +dotnet_style_prefer_simplified_interpolation = true + +# 字段首选项 +dotnet_style_readonly_field = true + +# 参数首选项 +dotnet_code_quality_unused_parameters = all + +# 禁止显示首选项 +dotnet_remove_unnecessary_suppression_exclusions = none + +# 新行首选项 +dotnet_style_allow_multiple_blank_lines_experimental = true +dotnet_style_allow_statement_immediately_after_block_experimental = true + +#### c# 编码约定 #### + +# var 首选项 +csharp_style_var_elsewhere = false +csharp_style_var_for_built_in_types = false +csharp_style_var_when_type_is_apparent = false + +# Expression-bodied 成员 +csharp_style_expression_bodied_accessors = true +csharp_style_expression_bodied_constructors = false +csharp_style_expression_bodied_indexers = true +csharp_style_expression_bodied_lambdas = true +csharp_style_expression_bodied_local_functions = false +csharp_style_expression_bodied_methods = false +csharp_style_expression_bodied_operators = false +csharp_style_expression_bodied_properties = true + +# 模式匹配首选项 +csharp_style_pattern_matching_over_as_with_null_check = true +csharp_style_pattern_matching_over_is_with_cast_check = true +csharp_style_prefer_not_pattern = true +csharp_style_prefer_pattern_matching = true +csharp_style_prefer_switch_expression = true + +# Null 检查首选项 +csharp_style_conditional_delegate_call = true + +# 修饰符首选项 +csharp_prefer_static_local_function = true +csharp_preferred_modifier_order = public,private,protected,internal,static,extern,new,virtual,abstract,sealed,override,readonly,unsafe,volatile,async + +# 代码块首选项 +csharp_prefer_braces = true +csharp_prefer_simple_using_statement = true + +# 表达式级首选项 +csharp_prefer_simple_default_expression = true +csharp_style_deconstructed_variable_declaration = true +csharp_style_implicit_object_creation_when_type_is_apparent = true +csharp_style_inlined_variable_declaration = true +csharp_style_pattern_local_over_anonymous_function = true +csharp_style_prefer_index_operator = true +csharp_style_prefer_range_operator = true +csharp_style_throw_expression = true +csharp_style_unused_value_assignment_preference = discard_variable +csharp_style_unused_value_expression_statement_preference = discard_variable + +# "using" 指令首选项 +csharp_using_directive_placement = outside_namespace + +# 新行首选项 +csharp_style_allow_blank_line_after_colon_in_constructor_initializer_experimental = true +csharp_style_allow_blank_lines_between_consecutive_braces_experimental = true +csharp_style_allow_embedded_statements_on_same_line_experimental = true + +#### C# 格式规则 #### + +# 新行首选项 +csharp_new_line_before_catch = true +csharp_new_line_before_else = true +csharp_new_line_before_finally = true +csharp_new_line_before_members_in_anonymous_types = true +csharp_new_line_before_members_in_object_initializers = true +csharp_new_line_before_open_brace = accessors,anonymous_methods,anonymous_types,control_blocks,methods,object_collection_array_initializers,properties,types +csharp_new_line_between_query_expression_clauses = true + +# 缩进首选项 +csharp_indent_block_contents = true +csharp_indent_braces = false +csharp_indent_case_contents = true +csharp_indent_case_contents_when_block = true +csharp_indent_labels = one_less_than_current +csharp_indent_switch_labels = true + +# 空格键首选项 +csharp_space_after_cast = false +csharp_space_after_colon_in_inheritance_clause = true +csharp_space_after_comma = true +csharp_space_after_dot = false +csharp_space_after_keywords_in_control_flow_statements = true +csharp_space_after_semicolon_in_for_statement = true +csharp_space_around_binary_operators = before_and_after +csharp_space_around_declaration_statements = false +csharp_space_before_colon_in_inheritance_clause = true +csharp_space_before_comma = false +csharp_space_before_dot = false +csharp_space_before_open_square_brackets = false +csharp_space_before_semicolon_in_for_statement = false +csharp_space_between_empty_square_brackets = false +csharp_space_between_method_call_empty_parameter_list_parentheses = false +csharp_space_between_method_call_name_and_opening_parenthesis = false +csharp_space_between_method_call_parameter_list_parentheses = false +csharp_space_between_method_declaration_empty_parameter_list_parentheses = false +csharp_space_between_method_declaration_name_and_open_parenthesis = false +csharp_space_between_method_declaration_parameter_list_parentheses = false +csharp_space_between_parentheses = false +csharp_space_between_square_brackets = false + +# 包装首选项 +csharp_preserve_single_line_blocks = true +csharp_preserve_single_line_statements = true + +#### 命名样式 #### + +# 命名规则 + +dotnet_naming_rule.interface_should_be_begins_with_i.severity = suggestion +dotnet_naming_rule.interface_should_be_begins_with_i.symbols = interface +dotnet_naming_rule.interface_should_be_begins_with_i.style = begins_with_i + +dotnet_naming_rule.types_should_be_pascal_case.severity = suggestion +dotnet_naming_rule.types_should_be_pascal_case.symbols = types +dotnet_naming_rule.types_should_be_pascal_case.style = pascal_case + +dotnet_naming_rule.non_field_members_should_be_pascal_case.severity = suggestion +dotnet_naming_rule.non_field_members_should_be_pascal_case.symbols = non_field_members +dotnet_naming_rule.non_field_members_should_be_pascal_case.style = pascal_case + +# 符号规范 + +dotnet_naming_symbols.interface.applicable_kinds = interface +dotnet_naming_symbols.interface.applicable_accessibilities = public, internal, private, protected, protected_internal, private_protected +dotnet_naming_symbols.interface.required_modifiers = + +dotnet_naming_symbols.types.applicable_kinds = class, struct, interface, enum +dotnet_naming_symbols.types.applicable_accessibilities = public, internal, private, protected, protected_internal, private_protected +dotnet_naming_symbols.types.required_modifiers = + +dotnet_naming_symbols.non_field_members.applicable_kinds = property, event, method +dotnet_naming_symbols.non_field_members.applicable_accessibilities = public, internal, private, protected, protected_internal, private_protected +dotnet_naming_symbols.non_field_members.required_modifiers = + +# 命名样式 + +dotnet_naming_style.pascal_case.required_prefix = +dotnet_naming_style.pascal_case.required_suffix = +dotnet_naming_style.pascal_case.word_separator = +dotnet_naming_style.pascal_case.capitalization = pascal_case + +dotnet_naming_style.begins_with_i.required_prefix = I +dotnet_naming_style.begins_with_i.required_suffix = +dotnet_naming_style.begins_with_i.word_separator = +dotnet_naming_style.begins_with_i.capitalization = pascal_case diff --git a/.gitattributes b/.gitattributes index 8bb03aa..63a09c5 100644 --- a/.gitattributes +++ b/.gitattributes @@ -1,4 +1,4 @@ -############################################################################### +############################################################################### # Set default behavior to automatically normalize line endings. ############################################################################### * text=auto diff --git a/.gitignore b/.gitignore index 25cc348..e910608 100644 --- a/.gitignore +++ b/.gitignore @@ -1,11 +1,11 @@ -## Ignore Visual Studio temporary files, build results, and +## Ignore Visual Studio temporary files, build results, and ## files generated by popular Visual Studio add-ons. -## 忽略Visual Studio临时文件、生成结果和由VisualStudio常用插件生成的文件。 +## Visual StudioʱļɽVisualStudioòɵļ ## ## Get latest from https://github.com/github/gitignore/blob/master/VisualStudio.gitignore # User-specific files -# 用户特定文件 +# ûضļ *.rsuser *.suo *.user @@ -13,12 +13,12 @@ *.sln.docstates # User-specific files (MonoDevelop/Xamarin Studio) -# 用户特定文件 (MonoDevelop/Xamarin Studio) +# ûضļ (MonoDevelop/Xamarin Studio) *.userprefs # Build results -# Build 结果 -# 语法:[abc]匹配任何一个列在方括号中的字符(要么匹配一个 a,要么匹配一个 b,要么匹配一个 c)——如 *.[oa]表明Git忽略所有以 .o 或 .a 结尾的文件 +# Build +# ﷨[abc]ƥκһڷеַҪôƥһ aҪôƥһ bҪôƥһ c *.[oa]Git .o .a βļ [Dd]ebug/ [Dd]ebugPublic/ [Rr]elease/ @@ -33,34 +33,34 @@ bld/ [Ll]og/ # Visual Studio 2015/2017 cache/options directory -# Visual Studio 2015/2017 缓存/选项 目录 +# Visual Studio 2015/2017 /ѡ Ŀ¼ .vs/ # Uncomment if you have tasks that create the project's static files in wwwroot -# 如果您有在wwwroot中创建项目静态文件的任务,请取消注释 +# wwwrootдĿ̬ļȡע #wwwroot/ # Visual Studio 2017 auto generated files -# Visual Studio 2017自动生成的文件 +# Visual Studio 2017Զɵļ Generated\ Files/ # MSTest test Results -# MSTest测试结果 +# MSTestԽ [Tt]est[Rr]esult*/ [Bb]uild[Ll]og.* # NUNIT -# NUnit 是 JUnit 的 .NET 版,支持所有 .NET 语言,完全使用 C# 编写,并进行完全重新设计以利用很多高级的 .NET 语言特性,例如定制属性以及其他相关的反射功能。 +# NUnit JUnit .NET 棬֧ .NET ԣȫʹ C# дȫúܶ߼ .NET ԣ綨Լصķ书ܡ *.VisualState.xml TestResult.xml # Build Results of an ATL Project -# ATL项目的生成结果 +# ATLĿɽ [Dd]ebugPS/ [Rr]eleasePS/ dlldata.c # Benchmark Results -# Benchmark结果 +# Benchmark BenchmarkDotNet.Artifacts/ # .NET Core @@ -69,11 +69,11 @@ project.fragment.lock.json artifacts/ # StyleCop -# 代码检测工具 +# ⹤ StyleCopReport.xml # Files built by Visual Studio -# Visual Studio built的文件 +# Visual Studio builtļ *_i.c *_p.c *_h.h @@ -103,11 +103,11 @@ StyleCopReport.xml *.scc # Chutzpah Test files -# Chutzpah测试文件 +# Chutzpahļ _Chutzpah* # Visual C++ cache files -# Visual C++ 缓存文件 +# Visual C++ ļ ipch/ *.aps *.ncb @@ -119,23 +119,23 @@ ipch/ *.VC.VC.opendb # Visual Studio profiler -# 应用程序性能分析工具 +# Ӧóܷ *.psess *.vsp *.vspx *.sap # Visual Studio Trace Files -# Visual Studio 跟踪文件 +# Visual Studio ļ *.e2e # TFS 2012 Local Workspace -# TFS 2012 本地工作区 +# TFS 2012 ع $tf/ # Guidance Automation Toolkit -# 这套工具旨在简化将可重用的代码集成到应用程序的过程,使架构师能将通常需手动执行的一系列开发工作自动化起来。 -# 使用此工具,还能确保重复性的、易出错的开发工作以合理、一致的方式完成,并能缩短软件开发时间。 +# ׹ּڼ򻯽õĴ뼯ɵӦóḶ́ʹܹʦִֶܽͨеһϵпԶ +# ʹô˹ߣȷظԵġ׳ĿԺһµķʽɣʱ䡣 *.gpState # ReSharper is a .NET coding add-in @@ -173,11 +173,11 @@ AutoTest.Net/ .sass-cache/ # Installshield output folder -# Installshield 输出文件夹 +# Installshield ļ [Ee]xpress/ # DocProject is a documentation generator add-in -# DocProject 是一个文档生成器外接程序 +# DocProject һĵӳ DocProject/buildhelp/ DocProject/Help/*.HxT DocProject/Help/*.HxC @@ -195,32 +195,32 @@ publish/ *.azurePubxml # Note: Comment the next line if you want to checkin your web deploy settings, # but database connection strings (with potential passwords) will be unencrypted -# 注意:如果要签入web部署设置,请在下一行添加注释, -# 但是数据库连接字符串(带有可能的密码)将是未加密的 +# ע⣺Ҫǩwebãһעͣ +# ݿַпܵ룩δܵ *.pubxml *.publishproj # Microsoft Azure Web App publish settings. Comment the next line if you want to # checkin your Azure Web App publish settings, but sensitive information contained # in these scripts will be unencrypted -# Microsoft Azure Web App发布设置。 -# 如果您想签入Azure Web App发布设置,请在下一行添加注释,但这些脚本中包含的敏感信息将不加密 +# Microsoft Azure Web Appá +# ǩAzure Web AppãһעͣЩűаϢ PublishScripts/ # NuGet Packages -# NuGet包 +# NuGet *.nupkg # The packages folder can be ignored because of Package Restore -# 由于Package Restore,包文件夹可以忽略 +# Package RestoreļпԺ **/[Pp]ackages/* # except build/, which is used as an MSBuild target. -# 除了build/,它用作MSBuild目标。 +# build/MSBuildĿꡣ !**/[Pp]ackages/build/ # Uncomment if necessary however generally it will be regenerated when needed -# 必要时取消注释,但通常需要时会重新生成注释 +# ҪʱȡעͣͨҪʱע #!**/[Pp]ackages/repositories.config # NuGet v3's project.json files produces more ignorable files -# NuGet v3的project.json文件生成更多可忽略的文件 +# NuGet v3project.jsonļɸɺԵļ *.nuget.props *.nuget.targets @@ -233,7 +233,7 @@ ecf/ rcf/ # Windows Store app package directories and files -# Windows应用商店应用程序包目录和文件 +# WindowsӦ̵ӦóĿ¼ļ AppPackages/ BundleArtifacts/ Package.StoreAssociation.xml @@ -241,16 +241,16 @@ _pkginfo.txt *.appx # Visual Studio cache files -# Visual Studio缓存文件 +# Visual Studioļ # files ending in .cache can be ignored -# 可以忽略以.cache结尾的文件 +# Ժ.cacheβļ *.[Cc]ache # but keep track of directories ending in .cache -# 但要跟踪以.cache结尾的目录 +# Ҫ.cacheβĿ¼ !?*.[Cc]ache/ # Others -# 其他 +# ClientBin/ ~$* *~ @@ -262,16 +262,16 @@ ClientBin/ orleans.codegen.cs # Including strong name files can present a security risk -# 包含强名称文件可能会带来安全风险 +# ǿļܻȫ # (https://github.com/github/gitignore/pull/2483#issue-259490424) #*.snk # Since there are multiple workflows, uncomment next line to ignore bower_components -#由于有多个工作流,取消注释下一行以忽略bower_components +#жȡעһԺbower_components # (https://github.com/github/gitignore/pull/1529#issuecomment-104372622) #bower_components/ # ASP.NET Core default setup: bower directory is configured as wwwroot/lib/ and bower restore is true -#ASP.NET核心默认设置:bower目录配置为wwwroot/lib/并且bower restore为true +#ASP.NETĬãbowerĿ¼Ϊwwwroot/lib/bower restoreΪtrue **/wwwroot/lib/ # RIA/Silverlight projects @@ -280,7 +280,7 @@ Generated_Code/ # Backup & report files from converting an old project file # to a newer Visual Studio version. Backup files are not needed, # because we have git ;-) -#将旧项目文件转换为新的Visual Studio版本的备份和报告文件。不需要备份文件,因为我们有git; +#ĿļתΪµVisual Studio汾ıݺͱļҪļΪgit _UpgradeReport_Files/ Backup*/ UpgradeLog*.XML @@ -294,7 +294,7 @@ ServiceFabricBackup/ *.ndf # Business Intelligence projects -# 商业智能项目 +# ҵĿ *.rdl.data *.bim.layout *.bim_*.settings @@ -304,28 +304,28 @@ ServiceFabricBackup/ FakesAssemblies/ # GhostDoc plugin setting file -# GhostDoc插件设置文件 +# GhostDocļ *.GhostDoc.xml # Node.js Tools for Visual Studio -# 用于Visual Studio的Node.js工具 +# Visual StudioNode.js .ntvs_analysis.dat node_modules/ # Visual Studio 6 build log -# Visual Studio 6生成日志 +# Visual Studio 6־ *.plg # Visual Studio 6 workspace options file -# Visual Studio 6工作区选项文件 +# Visual Studio 6ѡļ *.opt # Visual Studio 6 auto-generated workspace file (contains which files were open etc.) -# Visual Studio 6自动生成的工作区文件(包含打开的文件等) +# Visual Studio 6ԶɵĹļ򿪵ļȣ *.vbw # Visual Studio LightSwitch build output -# Visual Studio LightSwitch生成输出 +# Visual Studio LightSwitch **/*.HTMLClient/GeneratedArtifacts **/*.DesktopClient/GeneratedArtifacts **/*.DesktopClient/ModelManifest.xml @@ -334,7 +334,7 @@ node_modules/ _Pvt_Extensions # Paket dependency manager -# Paket依赖关系管理器 +# Paketϵ .paket/paket.exe paket-files/ @@ -353,7 +353,7 @@ __pycache__/ *.pyc # Cake - Uncomment if you are using it -# Cake-如果你正在使用它,请取消注释 +# Cake-ʹȡע # tools/** # !tools/packages.config @@ -361,7 +361,7 @@ __pycache__/ *.tss # Telerik's JustMock configuration file -# Telerik的JustMock配置文件 +# TelerikJustMockļ *.jmconfig # BizTalk build output @@ -372,29 +372,39 @@ __pycache__/ *.xsd.cs # OpenCover UI analysis results -# OpenCover UI分析结果 +# OpenCover UI OpenCover/ # Azure Stream Analytics local run output -# Azure流分析本地运行输出 +# Azure ASALocalRun/ # MSBuild Binary and Structured Log -# MSBuild二进制和结构化日志 +# MSBuildƺͽṹ־ *.binlog # NVidia Nsight GPU debugger configuration file -# NVidia Nsight GPU调试器配置文件 +# NVidia Nsight GPUļ *.nvuser # MFractors (Xamarin productivity tool) working folder -# MFractors(Xamarin生产力工具)工作文件夹 +# MFractorsXamarinߣļ .mfractor/ # Local History for Visual Studio -# Visual Studio 的本地历史记录 +# Visual Studio ıʷ¼ .localhistory/ # BeatPulse healthcheck temp database -# BeatPulse healthcheck 临时数据库 +# BeatPulse healthcheck ʱݿ healthchecksdb + +#macos +*.DS_Store + +#vscode +.ionide +.vscode + +#ifoxcad +#tests/TestConsole/ \ No newline at end of file diff --git a/IFoxCAD.sln b/IFoxCAD.sln index 309172c..a9e0664 100644 --- a/IFoxCAD.sln +++ b/IFoxCAD.sln @@ -1,15 +1,17 @@  Microsoft Visual Studio Solution File, Format Version 12.00 -# Visual Studio Version 16 -VisualStudioVersion = 16.0.31025.194 +# Visual Studio Version 17 +VisualStudioVersion = 17.1.32113.165 MinimumVisualStudioVersion = 10.0.40219.1 Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "IFoxCAD.Cad", "src\IFoxCAD.Cad\IFoxCAD.Cad.csproj", "{D5FAED7C-A99B-4BEE-A745-45442DC44971}" EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "DBTrans.test", "tests\DBTrans.test\DBTrans.test.csproj", "{B1602568-F023-46C7-B635-3CB281F1EC00}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Test", "tests\Test\Test.csproj", "{B1602568-F023-46C7-B635-3CB281F1EC00}" EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "IFoxCAD.WPF", "src\IFoxCAD.WPF\IFoxCAD.WPF.csproj", "{D820D629-1AB3-4BE3-A772-CB753D98CCB8}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "IFoxCAD.Basal", "src\IFoxCAD.Basal\IFoxCAD.Basal.csproj", "{40BF07C4-100A-4810-A27B-4587CFB38E6E}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "IFoxCAD.Basal", "src\IFoxCAD.Basal\IFoxCAD.Basal.csproj", "{40BF07C4-100A-4810-A27B-4587CFB38E6E}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "TestConsole", "tests\TestConsole\TestConsole.csproj", "{E2873F0B-CAD2-45E8-8FF0-C05C0C6AD955}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution @@ -33,6 +35,10 @@ Global {40BF07C4-100A-4810-A27B-4587CFB38E6E}.Debug|Any CPU.Build.0 = Debug|Any CPU {40BF07C4-100A-4810-A27B-4587CFB38E6E}.Release|Any CPU.ActiveCfg = Release|Any CPU {40BF07C4-100A-4810-A27B-4587CFB38E6E}.Release|Any CPU.Build.0 = Release|Any CPU + {E2873F0B-CAD2-45E8-8FF0-C05C0C6AD955}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {E2873F0B-CAD2-45E8-8FF0-C05C0C6AD955}.Debug|Any CPU.Build.0 = Debug|Any CPU + {E2873F0B-CAD2-45E8-8FF0-C05C0C6AD955}.Release|Any CPU.ActiveCfg = Release|Any CPU + {E2873F0B-CAD2-45E8-8FF0-C05C0C6AD955}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE diff --git a/README.md b/README.md index f312887..6a4b012 100644 --- a/README.md +++ b/README.md @@ -2,53 +2,90 @@ #### 介绍 -基于.NET的Cad二次开发类库 +基于.NET的Cad二次开发类库。 + +可以加群交流: + +![ifoxcad用户交流群群二维码](./docs/png/ifoxcad用户交流群群二维码.png) #### 软件架构及相关说明 - [软件架构说明](/docs/关于IFoxCAD的架构说明.md) - - [扩展函数说明](/docs/关于扩展函数的说明.md) -#### 安装教程 +#### 编译 IFox 源码工程 -1. vs新建net standord 类库 -2. 修改项目TargetFramework为net45,保存重加载项目 -3. 右键项目,管理nuget程序包,搜索ifoxcad,安装最新版就可以了 +由于vs2022抛弃了某几个net版本,所以我们同时安装vs2019和vs2022,然后使用vs2022; +其中的原因是vs2019拥有全部net版本,而vs2022拥有最新的分析器和语法. -#### 使用说明 +#### IFoxCad 项目模版 -1. 快速入门 +可以在vs扩展菜单-管理扩展中搜索ifoxcad,即可安装项目模板。使用项目模版可以方便的创建支持多目标多版本的使用ifoxcad类库的项目和类。如果无法在vs的市场里下载,就去上面的QQ群里下载。 - - 打开vs,新建一个standard类型的类库项目,修改项目文件里的`netcore2.0`为`NET45`。其中的net45,可以改为NET35以上的标准TFM(如:net35、net40、net45、net46、net47等等)。同时可以指定多版本。具体的详细的教程见 [VS通过添加不同引用库,建立多条件编译]( https://www.yuque.com/vicwjb/zqpcd0/ufbwyl)。 - - 右键项目文件,选择管理nuget程序包。 - - 在nuget程序里搜索**ifoxcad**,直接选择最新的版本,然后点击安装**IFoxCAD.Cad**,nuget会自动安装ifoxcad依赖的库。 - - 添加引用 +#### 安装教程 - ```c# - using Autodesk.AutoCAD.ApplicationServices; - using Autodesk.AutoCAD.EditorInput; - using Autodesk.AutoCAD.Runtime; - using Autodesk.AutoCAD.Geometry; - using Autodesk.AutoCAD.DatabaseServices; - using IFoxCAD.Cad; - ``` +1. 新建net standard 类库 +2. 修改项目`.csproj`的`TargetFrameworks`为net45,保存重加载项目,这里需要注意和cad版本对照. +3. 右键项目,管理nuget程序包,搜索ifoxcad,安装最新版就可以了. - - 添加代码 +#### 使用说明 - ```c# - [CommandMethod("hello")] - public void Hello() - { - using var tr = new DBTrans() - var line1 = new Line(new Point3d(0, 0, 0), new Point3d(1, 1, 0)); - tr.CurrentSpace.AddEntity(line1); - } +1. 快速入门 + + - 打开vs,新建一个standard类型的类库项目,**注意,需要选择类型的时候一定要选standard2.0** ![](./docs/png/standard.png) + + - 双击项目,打开项目文件: + + - 修改项目文件里的`netcore2.0`为`NET45`。其中的net45,可以改为NET45以上的标准TFM(如:net45、net46、net47等等)。同时可以指定多版本。具体的详细的教程见 [VS通过添加不同引用库,建立多条件编译]( https://www.yuque.com/vicwjb/zqpcd0/ufbwyl)。 + + - 在 ` xxx ` 中增加 `preview`,主要是为了支持最新的语法,本项目采用了最新的语法编写。项目文件现在的内容类似如下: + + ```xml + + + net45 + preview + + ``` - - 这段代码就是在cad的当前空间内添加了一条直线。 - + + - 右键项目文件,选择管理nuget程序包。![](./docs/png/nuget1.png) + + - 在nuget程序里搜索**ifoxcad**,直接选择最新的版本(如果你是 **net40** 或者 **net35** 的用户,可以安装 **0.1.6** 版本),然后点击安装**IFoxCAD.Cad**,nuget会自动安装ifoxcad依赖的库。(按下图绿色框框里选择浏览,程序包来源选择nuget.org,安装IFoxCAD.Cad包。IFoxCAD.Basal是IFoxCAD.Cad的依赖项会自动安装,如果要开发wpf界面的话,可以安装IFoxCAD.WPF,提供了简单的mvvm支持)![](./docs/png/nuget.png) + + - 添加引用 + + ```c# + using Autodesk.AutoCAD.ApplicationServices; + using Autodesk.AutoCAD.EditorInput; + using Autodesk.AutoCAD.Runtime; + using Autodesk.AutoCAD.Geometry; + using Autodesk.AutoCAD.DatabaseServices; + using IFoxCAD.Cad; + ``` + + - 添加代码 + + ```c# + [CommandMethod("hello")] + public void Hello() + { + using var tr = new DBTrans(); + var line1 = new Line(new Point3d(0, 0, 0), new Point3d(1, 1, 0)); + tr.CurrentSpace.AddEntity(line1); + // 如果你没有添加preview到项目文件里的话:按如下旧语法: + // using(var tr = new DBTrans()) + // { + // var line1 = new Line(new Point3d(0, 0, 0), new Point3d(1, 1, 0)); + // tr.CurrentSpace.AddEntity(line1); + // } + } + ``` + + 这段代码就是在cad的当前空间内添加了一条直线。 + - F6生成,然后打开cad,netload命令将刚刚生成的dll加载。 + - 运行hello命令,然后缩放一下视图,现在一条直线和一个圆已经显示在屏幕上了。 2. [事务管理器用法](/docs/DBTrans.md) @@ -60,23 +97,62 @@ 5. [WPF支持](/docs/WPF.md) 6. 天秀的自动加载与初始化 - - 为了将程序集的初始化和通过写注册表的方式实现自动加载统一设置,减少每次重复的工作量,内裤提供了`AutoRegAssem`抽象类来完成此功能,只要在需要初始化的类继承`AutoRegAssem`类,然后实现`Initialize()` 和`Terminate()`两个函数就可以了。特别强调的是,一个程序集里只能有一个类继承,不管是不是同一个命名空间。 - + + 为了将程序集的初始化和通过写注册表的方式实现自动加载统一设置,减少每次重复的工作量,类裤提供了`AutoRegAssem`抽象类来完成此功能,只要在需要初始化的类继承`AutoRegAssem`类,然后实现`Initialize()` 和`Terminate()`两个函数就可以了。 + 特别强调的是,一个程序集里只能有一个类继承,不管是不是同一个命名空间。 + + 但是为了满足开闭原则,使用特性进行分段初始化是目前最佳选择 + ```c# - public class Test : AutoRegAssem //继承 + using Autodesk.AutoCAD.Runtime; + using IFoxCAD.Cad; + using System; + using System.Reflection; + + /* + * 自动执行接口 + * 这里必须要实现一次这个接口,才能使用 IFoxInitialize 特性进行自动执行 + */ + public class CmdINI : AutoRegAssem { - public override void Initialize() //实现接口函数 - { - throw new NotImplementedException(); + // 这里可以写任何普通的函数,也可以写下面 AutoTest 类里的实现了 IFoxInitialize 特性的初始化函数 + // 继承AutoRegAssem的主要作用是写注册表用来自动加载dll,同时执行实现了 IFoxInitialize 特性的函数 + // 注意这里的自动执行是在cad启动后,加载了dll之后执行,而不是运行命令后执行。 + + [IFoxInitialize] + public void InitOne() + { + //TODO 你想在加载dll之后自动执行的函数 + // 可以随便在哪里类里 可以多次实现 IFoxInitialize 特性 } - public override void Terminate() //实现接口函数 + + } + + //其他的类中的函数: + //实现自动接口之后,在任意一个函数上面使用此特性,减少每次改动 CmdINI 类 + public class AutoTest + { + [IFoxInitialize] + public void Initialize() + { + //TODO 你想在加载dll之后自动执行的函数 + } + [IFoxInitialize] + public void InitTwo() + { + //TODO 你想在加载dll之后自动执行的函数 + // 可以随便在哪里类里 可以多次实现 IFoxInitialize 特性 + } + [IFoxInitialize(isInitialize: false)] // 特性的参数为false的时候就表示卸载时执行的函数 + public void Terminate() { - throw new NotImplementedException(); + //TODO 你想在关闭cad时自动执行的函数 } } ``` - + + + 7. 天秀的打开模式提权 由于cad的对象是有打开模式,是否可写等等,为了安全起见,在处理对象时,一般是用读模式打开,然后需要写数据的时候在提权为写模式,然后在降级到读模式,但是这个过程中,很容易漏掉某些步骤,然后cad崩溃。为了处理这些情况,内裤提供了提权类来保证读写模式的有序转换。 @@ -88,4 +164,5 @@ } //关闭事务自动处理读写模式 ``` -8. 未完待续。。。。 \ No newline at end of file +8. 未完待续。。。。 + diff --git a/docs/DBTrans.md b/docs/DBTrans.md index 29f88dc..5e75860 100644 --- a/docs/DBTrans.md +++ b/docs/DBTrans.md @@ -59,7 +59,7 @@ using (DBTrans tr = new DBTrans()) // 第一步,开启事务 - **向符号表里添加元素** ```c# - using (DBTransaction tr = new DBTransaction()) + using (DBTrans tr = new DBTrans()) { // 第一步,开启事务 var layerTable = tr.LayerTable; // 第二步,获取图层表 @@ -75,7 +75,7 @@ using (DBTrans tr = new DBTrans()) // 第一步,开启事务 想要添加和获取符号表内的某个元素非常的简单: ```c# - using (DBTransaction tr = new DBTransaction()) // 第一步,开启事务 + using (DBTrans tr = new DBTrans()) // 第一步,开启事务 { var layerTable = tr.LayerTable; // 第二步,获取图层表 layerTable.Add("1"); // 第三步,添加名为“1”的图层,即新建图层 diff --git a/docs/WPF.md b/docs/WPF.md index 08d7c28..73f4f11 100644 --- a/docs/WPF.md +++ b/docs/WPF.md @@ -225,7 +225,7 @@ public class TestViewModel : ViewModelBase 首先是在xaml里引入命名空间。 -`xmlns:eb="clr-namespace:IFoxCad.WPF;assembly=IFoxCad"` +`xmlns:eb="clr-namespace:IFoxCAD.WPF;assembly=IFoxCAD.WPF"` 然后 diff --git "a/docs/png/ifoxcad\347\224\250\346\210\267\344\272\244\346\265\201\347\276\244\347\276\244\344\272\214\347\273\264\347\240\201.png" "b/docs/png/ifoxcad\347\224\250\346\210\267\344\272\244\346\265\201\347\276\244\347\276\244\344\272\214\347\273\264\347\240\201.png" new file mode 100644 index 0000000000000000000000000000000000000000..a9d08c483b80f21d4b0ac6a0afe80f9b6b4233e6 GIT binary patch literal 20425 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Ð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 literal 0 HcmV?d00001 diff --git a/docs/png/nuget1.png b/docs/png/nuget1.png new file mode 100644 index 0000000000000000000000000000000000000000..91983ffef3b246ea796296b14fe09963663de184 GIT binary patch literal 27373 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 literal 0 HcmV?d00001 diff --git a/docs/png/standard.png b/docs/png/standard.png new file mode 100644 index 0000000000000000000000000000000000000000..dfc34fecb32120f54436115208a4abaea2dac516 GIT binary patch literal 148673 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%0DO&7ix%Xw#WJ4GhT6iE|`sg)>zmh51hyQTaFv=}2 zj`nUW|9WiI7rvAeD0qd>$6Z>Y`#zSt4nr+|XtExeZDEdJ3%T;KlEfH#+;*fxRJntY z7XW7n=31fCYfDN9+prRZX&7aJT)f|ZmuMby++b=Y~Esdi*4$U>R9aQi@`X6WOOX}T)tO_{#%a$V1V#_`SG>> zf^k^eN6T-&KFKYhg^yUfRhb!Se6(lId1gSLMFM&$W31%Hk6TpM4GvNU_%w=M;LVFj zajfa{NOjaYt>U@O1|=b6Jx*b-Do>u3Vs8Yreru=~Sl%6kXsqoHg%v*{_%RWIQ|)aE zSy`kzE7ZrQysaAug!RQRf`>$eu5><7gWs3{V>3r3(VMAZYZI{q=}Pv!bPczPun}13@(YOg z)CG@a9?e1f)kVF$c?3N_o(g^N9Pz|;*)DiWa&&V&vB9S@s&HRBnRnfDw?#MKBf%38{7OSGPzzlrn&B*!wKfMGJpztdP}+j zJh;TcjMz?bk%bk6t+Z-AmWDIuPD036DgNyBj$@<0qoG4D8cFe;YNL}0%z!j&;)k!u z6p7F~>dr0(?St9;+^@tjv-wr69+=8G=?l9qP$=P4gI+7)YK4bU!w ziaFu9b`ZuKg+OK+VQxyRkkR;XQ|gc$Z~K2pN}Da&@jmQZ(=NgS1lGAQSkml0_U>cE zO>73m0L>;`EarxT-O+O8@>$?$19mDY2|13)T;Ty>B(Qf;k$%`Eey(>mPnG6r$N;W) zX{XG#*gpEDK+oH{8?0I*k%B~M_J+>Tme`P)21E+(n*Th2*rsv#Y2_|54rNPdYcCG+ z4B_O%n$e=AVB_L`TAy;|R8$VmFh0dv01a7*b7^F!+Vkt%ddrv!$KUqF>}$BuegJK` zUlAo)c)rNKQYH|l^mjjkv1>G(D?dIVq57(L=m$bTt0lo9tSgur*4=mNTU8f<#!C!> z!DcW%K|uri_U`>K1HN@@X=Z}^RaelvBN0sKXfU2Yq59Ng9r?QvoHv+bMQ!dnI4Jc3 zHu47d=<^?D(5ZWkvXP5-nz8EP6YuXhYMl~k&{E7nfSPwX1FddnFGODmoUYQ-tQ*D9 z>mJ|eRU(_DRlY-H1ib5)G+NqTxM#XM^PL{1(bL9v@bvw+|2}>{p0rcAR`j)l$vAIH zMJ***_uf6qf*YK~qqXTrf1Xlze7b(27#6@cX|iAO5dxVqN5U2o0Btwtz$95pu3lRB zdckx&s5kqve?mWWTThuoPJ1%!t;Lk?l`EtCnN?@Mvx_H~+$=Oz>QGF|1#5tq;iP?@ zP1Z2XbRx7nCe-SOX_=M2LqI0n!%hrX-!5}}@j@QhR1jF+t!GB`31_`W}ZMma*G$B zBNjX-LpeeG`j{Db*l1+sgxjP25&m(`nF0Hes8Pmzrxm$0&f|?~)#1un7JvbfpXXmen+sC@gw|`q$M#XWmMW zWdR6bU*V`)a{Wil7k>2Hd9h5Jguc36nzRA4SDK;EC9D8D(YH;8#)0<0+(aCF&+)_U zC3e7y*|^GZjyvwB5p|B2{UX84}H)%p}fGAU^cI_OG4DrwNth~b{hpg zY|8gzm^|+A1yAAw%Y5Ymj@NO=7sw4-SZM75eeX+&5rF)h=iqj?kqZLuXcr;|bU{sq zd&+R0lfY%|ajBJdxkF^d-<>YT7SVll<6sj(Y>E3RA|f+#SS;Yv5g$2dEV8e{dt@}x zNOR6mW#W(gt1|@@<%V^G&9MPYP84!URFv~jIkV8wc0n_25=F6hlYRLt&@ zZ98(2VJ4J)zVe6=P^Y52V_-W-$Ls{>#S~Ok6;5s+-4n1tQOY~Mox5t3yxh~A@WJKm z1+zxZ~S&XS(wyD>_m+Y;|5p<9F*_4Wu@s59!nzGUX2P z2n@KRSe(jN)e>HU5Q^!Dji#ZZNmkdntF-`kz&30k#E8PuM@}L?_*0s%r`BM>9s%7& z%B+tlD4hsn-m{CGyMb^jXz-qRpVDGogeVmF5=F8ZTb~&@2h@CFPG^ZDR*TI%U&pM2 z&;xR(^O11G_Z#;&uJF++Mgzdac31tXHobo~|(z|ZV zkkXZ*HaS-4?3~!V!;{(A==8!3*4|fAz^TB$msy+c8<$Wx5yn4no)tW_#EV0{Kf)$vfg473N zDXOIG+|wTY)6%^aBZPF~+KVt!0pf?CbUWy`K&C?20YqI&5<1*Px}gGHLo=*A&E01M zZ5j)!*+XPI*m^O{HH`TcK&En7-dEr%$C2VFPt1`hl<1V1O+ZWEoitje`jqQJ_s=sw ze8Ne?sQ+%kmf0rAvn#x_9DgC=tjF$~oNm{5AMUZ`P-_io*(WS~h=ht!>=CAEmPdi% zhrxYE%wuS`%3nt*>c54Sobkx;HX6r{sSpDT36+Q%#g+oYg>Db(^&ajf&ug&d*R~G`op~D)J zcm$!f%{CGaNt>2#zndS)dK1iAeY0+XJ6#a|A0D9|o-I2Bi6F5URO;%8FL7`Vj*dJg z&TWOUq=n)$;X)XrU<*+%EW?!d!q=y38HJEhW-x>qS6Kt8Usy?_ni**=_%uwm`ve}0 zDlh9E3nWy-R%L9xhE}}YGA33G2juQY_xjZ)^B=~x%IY?mX;pz4Lkvsrm#2RTm|za? z-)V+D$!Wpwv=C%mE~xIY{g+RFP)1*H_GJ_@t-`5^gTB~MQ-x~_WNmKunTgM!G(_Ca zO$R)xkS501?{xk0n|Cu|taTM?k#)17hMhEKwsAWFh8d5juu3Np3Q>ln2=F$ny3W9x z$-=$lk6_(xo~cf>TcAGG5qOyv`48Mm96PMrpn-8PD6?#I{zhP2ICXMvoOC~cGIIg{q>6G7Km(zX>fQfQW=Pg|*;>}s{5 za6LWHv938ofD@$bH8@Uz&9bX{Gja@d`qF*bu~qydmA(mx6n1BM0m?MN^1x3@3<%DW zC7x_Y<~Zm0+;Pfh@n}4EKFfVnigtBD67V*M?dZE(kP^POz1Oe7b2K;;QxM{`2Z^(g z`;LD6SKIO6!Bfn{`rh6Q6KY?+kjb~85gdW4ZV+JHxRR_^UlV3F;D7rlDM^@u827{| zoYNHN^qmVe?S8oMofEM9vk7L?+-OREK5PW!hT-fRO0Vz=rJ~4yt}*MviAZJN@RL4Ip|gN_okio2lC3>Yx?fXa@fOe!-R67VfSbR3 zd`zgOUZ_n*j6Jzq#xicAEtQJ>TX6n-LY^?*hysfauEeZ%25)5l<^fyjGXK{$mluJ(VJNRQG?#mzi7-LfSPdwS2WCk zV{@U>dwJO;I>N`jYYcTd)fDjmfDRY9fm1I?<%2P%sMbP^XHph8|I_*ZIH7U^Vt?k8 zl^JLQ4o^XdSHze%v0_LA6IeUq+#NwMUNOW*yO%vU-w#m&dl>gIE?R%nWUFli%BGYrhTV##`{|a_Lz=_tqMX1l zyCs8V?`t1x8o(A>8j&+>&?N%qE^9=nDc~ddD>q~bbF3JnT0--Zeh9gs`%xSPiA@8= z`8-Jgu?}#RNu`3PimgjsZ;WH_BKNXwvmgI~{JSXXIk^p*U%{1p>(^CeDRKy`hzbdP zws(TV3nHtbcoM*xVK>$V%ySW>g985%oXUUon})d*q|HO_7s_H}vwkbky!?Esfl{iB z4Jpk!qXF_f-tnuX`L3ca05h$P&+%l4Xb>ZibFz%oYs$+-&=g4G0Hf z-R+RB{9ju(r)(B;y0j<<)}Qn@mZ(vHjiT$VnNl%z`dL-EIs z|8!~H=djkn9;NDQe0gxp)HcPu>)MK6>JgaPXVsb}@k`{_)zix-HL7r$oyXOHh+m3= znSSdk=xH{g>3+Oo-tArQ$eqO|obfA1j_l_iq?Zjb%&dIm+dFY{e@rHH2CuCz^TaO` zDmT=sm!uL2v4Pb6w_gOxZyT+IM}qBc$@G8E zyo=nDuRgbLAJ;TC-Cy=h_;^zP9^jM(%r^xelVNE}X$C!HOr@2VgSbV-!aA~t1LWn& zIIW3fRFz8UgU2%J=nCeecFUR*ZkU{kQ3H9X6KrCg>ZzCXKJaPCQ_kg?)^sJWHIPkm zMUiFWdYPGFe0$`#*H=fAEF+K<|9N4|G6h`)c*K=Xz~G)8@%;}NSbTU3Ki8~&fnC>q zBZml7e|h0yeN&M@0eNX#w5`;gJZfm_(x}w?2rJ5~s0g-i!({QmJsV>)BH`%gyU#8+ z=&>+Ol(@_Bo}s~7rD4zOVH3=@B^QH{xx^5=+SWX8BU_0v31*n#uqF*xxg>5ywfcF2 zvakT#*h=3B{RA3CJd7Bras~UW*cwr;@9TI>aYwayv>)W`#sHSG#-Gz5P zkj8)(;nd~EjoKk{YWb@Ec0-LH9=Oew<^w2W-z~-75V9uDrRmI8^P5 z3P|jy`F^-Z7zp*F{NEh>`+^ zE_K|N>vB3|#0o9ijwgBG?%fVMB(i#Gk?h3#h8djJ-OuP*?L4D(R@u%%j9<>w_vD^S zDC<%g3MN^=imUxpjp)v6(uV{z4;zuzbAG!8Kb*ag&c7*b<2z+(&mYtm+RY7CFb(OA z7%)>#xQq!v34$%^k3-aahtb$B>)`9dd4az>SYBTjw#Q51HXN*O)cC&PtPd(qjGT9J zIp>`jMVMLGUE>(5O32#%WnoV$=By?nMo2>i9*BtdAtyL9 z7JOmHl;Y-{TlbJ#eySapt=y!%(#!V0h`ju&sF-p4$$b|_cE?JXOZPRGKXWcITIPTr zEmUrsaJwDT+Pf_o@KD8Io z_YR+HS4~3PeMUNx7g#X9vp|Cbb8Df2_^ww%xa?}G>GJO5oK4N}hf1QMrVe7w6b5T? zo<&0Ir5lmN(qB{pA&y9^*N~&iY0rm8xig`(>*vcO`n*Pl(#PUEe@1HFZMEqg5z49m z<%pVFX*YGs<%63B)T50uecl!EUfNdnY%Rx|#jwKiSu~9RwoHT9bh_Z%*bW-Ui%8>g z_ik})Ukrg3o1@RYIn+~fGs27gCbrvy7a5TO#T=6M9d2fq(JCC_9&qUNX~~XbS2^SP z$BpHVepVbsj3k3*vS}5b%BS!26|d6nmV2#4-qzdbn&Lv9J8(Pd~ll?^^Ll!W#?*#(V0pt`FZha zwHTQ9P|X{UVcLrqcwExQR#N-V#Ur=!$A*Ovm|qtE!p^C4xO|aefD&TW8hYESVCDkx z_Y3o+WgY5N90YfQuwAjG`bN5=exA(nE*>AwVGfH#*M1 z^UOJOzPumaFaEa4CTp#G-EH0FTK!&1O|MxGu(KA>4$#UAgkXFf{9VGZeeX@KUL0jIelUR z{#kFzZQ#6T7TKC4*2ZKyCZ$bNy>U$UZ9F@`ABJt)@t~;^g&jtVm;cy_W3KfY#ztmr z)!Ai|>LQ?X>@#;7h2~tZd~Cu*9^q)eT^jpkPO&tWTJDbt@9NIx^SZDuq8L&~Ro~!> zuEU9oh0MT34AK%oO6@Akm04)mlAWe7*`ahizf~x?p#ny?Gq;TM5N|{_`EHVfi_TPj zQRX4c1Q#R-N9b{QV7IG2Mp}LBB8>$VX2Wu`Umpw(woUV^kFaPb)wP0ilC@UA(Wh(dTj#}VZU zq5kn?>sXyh9!tDy&Z)L@KOBsSqawNK-M?Gy*n5$lv#i=p^A1v!tio?z4f0Tm+z0C7 z*823q!_)Dp&nt%?Al5HKJjz!LgR3eQC5?F0=IIH7E4QmdH&kd-v|OT)ztber?qyUN z`_l`Bs_$0zUa)jH2d$;AKA;k7KgV*7Q*NV1+Lwjrhz)6(Q>BJDT2Fj~_?6!Ip62nu zMe;0L-3Dz$zLFaEJj(i#i}oDI4G1E=KrtRdI$MP)}c% z;eA^pHPCAwcQelb_0pdqDZb%mU*yOPf}XYC$Eu1lNUh%@(ZbhedLU|W#ky)z#lh09 zzdK{1jC`GbrIsFDj`DmS2jwXC7mUHUF70Eq*3=|t(B6wFLo*0H??};KsCM8r=DHTM z`4uTA@Vh>2(6oEIx9sTAkroZXohP?kJZK}Js0istn;SG4%`Xdy!{*CTj=`vskyYB@ zQQRb_%(P@BaY^*poyfRLT$>p@O`Orc%Dcl^pHs5gx`3Taj#TK-{C1LtIz=l#y|Zn4|`yun}v;@pG($f{6vF z>A7*O-%YXFXMQWzJy;itsfMMfD0d29k1ofH7}Bjq{*2F+ZH=+e2ue71GCUeKQG6>b%kc+j=5tM&Cgq@B2E4SjQJ$8}_3zP?t`K}1G*Jj@a6JyW+%Jy9iT7}o&7Lr}>p{u=GL@1N zu=SFDoP2V4o*NxEU`DSl7${RZn*{g6btpg^;TB^iuWpX$n&Dx=kxZQPHI_k)%^p)a zx*+&Lcr&_pzi$qLS~l2`BbF0bxVh(T)CMz5K4)2>Qc%rb%fKIe}@;m#+7e9etrgImKaqk7NEb} zL&Hp4zX*KjNKUl8&!JG3qL0b`-37n}yd9U!Ul?#pj5l;o#h#SsL8_J!9YIGOX}2E* zd{Wmxc!pEEoNXO<6ju6Lxyol$$Z;;IM1B_BIP-exQ~IoGR+gRv+MuRJBoQTgC5QJn z4!VV*A&2*QnlCIM;ZTR$w0GLvobs7^M_Ls!dtjYmukKhajJ{pJV$TPuP|z2+F5;rD zUUfnUZ8K(u*ERMWD)>Cq;Jr%bdf55r_!wXSt6a^+kKD9zff81U;<`gr7#*wnSZ+A~8?dM#wU3j*aJLmJ^=0;vhW9WTdgv?i^M) z3^K1Wto(EnDl12Nk^p+?*F~!sl^Na=9w7G$EVTXU4w~`JL`8^Mo0f;L^!e}D6USdP zGzSxy^n~s|@|1)01db?_^T*MIZk~w3i4i5Q8iHk7<_b%LHZDI0Vwe?I9+qX_60^`D z&!6-Kr&+4{JUYbou}kPxV9=RjWaUw^PqMc9 z;dkp@mRQGPhg#fb$*8g%JjJC~!^m9PSr9$=ey(_Z4*Q%(W*|UB6j@%;IUrh^4ZqDF zrttdG4ANL>`VUM_1Ou7ZumpMesM(Q z^O!4xto!r??lUzMIDy6Dsg>^#?mUo zkO+zHu#wdBvkfnv6(Xp%*(cq~ouTUTHyThpO(@^3yCND4 zjjXClF(AD`6Gis~;XMc?0>au4T4Z33Hqd{(Z+*U@XVX$=VZAZ8`Cwq;rl1iM=JPqF zIM~8`)Sv16I;1A-%iZvf`OO;hfDwy%!UrGWn#~{U_1j*r`R2Z!oq81TLabT&O4`{~ z5agjODLer@Q5R-p$kk7Ks(9!%k7(tM(E>I~&p+*BvucOJ}UTsnFJ;gy6)+p?I@AJ9qb0FS==GP4eVVl$f z&zTHo8&b}f#xe(*-T?BH567|L0R7f%0$u!2lxPB!BVq7Ejje}q@VvI*_(g&xUW?3w ztG4ctU-lSXq*%>iok+Km^2i#II2HOY99PPoco^m-jA+*cO_mims3mW>^)A3s;m<@6 zNo_wIOQ01I-kw&JwNoaIL|L|2?j5^7BX^+L>DDvU`x^{!P3#997MH$hMli+-qOI2k}h@wJYSDB1ybBRreddGL;a00&+ zgDxvu8~g%fP7ZB*eaZ6A#Qsgdr!4nHxrp!ZDG!)xwM6aZQeVUr5i@x*-$9&Z8H9^9 z?NzxYLaD`PRWBS)+_JM|;Z^4@`uu?9sn-@_^2;lXYQfj5 z@{w?x_Ofu*#;WC4E1Fy?wwx?V zNlQ7hh*Oqc@lTHCR7Q=RQjqAq#p&eojBn+Im{%FlvsJvtM- zCkv4v^u?*LAa*3V`CY3mQM=7VUE5jLmY5lO6O!=q_-!%)hBdnNnc65_wYvv{n=d_u1 zO9-?vPu1^ZCbu$)%x`@%we~QSWTJ z`#M)>KLO?~A=*+XB=4e%=))12*Negob5AkW0(oI>W;JxmgUR96WiQo^wSC~;#}`S@ z-aXJ*11&4Ghgor>&NVp^HoQXYwMAE!dFi;c>g@h-gu`D|blcnb0to88?A&K6FpLf{9;i!X&96)+`)Af{A+8<{ z2~W(RO%@l!oj?Hl_dEewmmHUeb}0AAM;w4ISOsY;?Vw)jNg!e*yRZAsPbDQ9;n#`! zwL8B2+f~{56xjfX8TrUFmYs9o;nw{1ZkZU+ZB*uz?f)Mc@Ba)2M`&0Blkoff-R`W< zf4v4Y!~rFT;6Yi##($e+ZUwOXhHH2Kn#(`U5BMyyY`f8OF0FW1`|3Z(Aq4(y3+J}UqkC=tzpkYWpm$kzAOV1__}@qO1IU9r zd+NXQwL9jwy%tk5fZk@^DwG}l0RNE;`TpPkFW52iRfFPxa%0b4)}s}qIAw*4k%zg)DN80meg zVsQxVH2=12#sM4J_!VY!eivDDAFytEe4yz+K~?>cCV4Z@6!aUfx!+NHk5lYr@r{wr zCvEbPl7<+^A@5u%@uzMD9R@eD=De_TduU%AyF$5gJ|1^Hfj3oKP4jBEkoDfK9Ua-` zu;6Y^x$iNTALL8tc^G7BF#m+%U6wl>JU_cX*0DZs{Eouq+oR!!1Nb=w;&RcQM1FHTwM(v#R_uHeq-bk$MGV+V;^ZmjMEA_Kz%hUX?H@ zK=$`<(qZW_FDyHTYA=U$jGvgr$tGG!cUjcv^5VP~%Ds}6iCPt0|{knIt&eXi^89w7wiYrKbjm50L`UM9VAlEQz1jjxVvh9*)x%g*u2 zxwpgiDf`5|7&{YaHW3ndj5lNQqs9Dh0lD>7CTmY2b1$e*C$Hv#w) zZ`5~5OqW|tmnR`7KcbBjYtFRDB~g`};U-x7=iA+v7ajvDtR_=*?)QIFVS+$HkZ{lY z@b_N8e|lRZG_sXn%2y%$>)Z9??*U92K`hwK%xedF{WpD|7<=RawN0e zxZ6X%zynaB8W&nHay+x6lRl$M1`3yIB63~n*{56qfCJv`9eZ|EUv~G7nICNW%2}DP z_uh&OATv1u9#ba~)`60uPm3pm7?w<%^s?gL2{wg7=l!#m`vTn^AoJ<(j*h*pNm?9~ z=%`R=Om4vIjtke`C>qkS05;aVeb=YkCs%Jz|vb#FO@lnLVkeN@qALkgt z52MT0!)3meA+&rraH#9U9YwL-x?f{>-!eD#c|ynqBtf@ZrBzamc|QP0Bt1)Yy8DV1 z?bt~mpdN78p6K5C#Gio2ncdN1;h@&W>Fgp;F!lr*FdB7nk<-t!Jbp^W}Jj zG|fV`ylqt`gR{<=#nITYJE=6W}6BE%3qebrNw7i7SoZ9d&^=4{hc#0uCI`I%mVUW;~?>x&eb(q&|-JB zI*Cm2&}M|FhhqiXUVa!o@+G>oVQZD{*~y@LfwU5zgG^|jQ@z4Bt!E}D!bux@N=S~4 z&2CvB{Y^?bJ-WiYNFOVE5BCjDalzVF@d2oqqUlz~F`U7ViFwh!jC-2`GIOJS+uOe@*A zr!h#b23J0@8LD8*4|)gu!>;CB_5w#NJ}!cv z07e{;t3L3yIT+4+tHGQ{;Acf(pAst7t4Bc1P$()63n;5l$Jk?`J`jm^< zX;nKzx5l;Gm6ss${Xrf~nH<9T@|4G2=&on1taFi5IjvRGPntXDeq{{Ts3U`7CvRt; zq9H=dl=N#6wCfc~UovBhaLPByl#jz|V5hdernAZ^miY8ZN%8>9;gdkA>Yc_`>P=jN z$%n3hgB?p~7q!{V$IQ@IQ9%)wXB&>MP# z-FkzYHZ|Tgo5S=`QocR@jZ)BX<%+f2$H4~c*dhNvedI#=c+S1~V=gxp7%8z3Al# zgrO^GZ>!~pYoKV_xG9%et5En#u42ejC?@bOEVeeRZ?A>=g-@Qffl0+GcUp+j7F^z~ z1@^;%$fi3&;lqoU!G_HeQvn=2_2&LmvyiTe(;PFzHB+ODP5zHkY~oc1%+(*}Y1D2= z4hIY*Z&Is91fUsM^}LK^xQal7#hL@0TKXLOcsxug9$p)AZz^i*Al(eINyVZpB&%HM z?i{%%N+o=yWdq2Q@Zbmp4bStR;6v>St#fL$!GuEbu=aT&s&~Cot6%o~okUfJx zZ|p%>Gf+8;M6S9I7UUNbF6v{cR{e~vqy$#txME5v*3*&5ID**5Etru4{DcudwGz%< z9Mntg_9uTTUrYb1p7FmKgTs`Y-}UBY1G79rX{uhp#O4n-ilMUeT&E zTd(;o9%Ou)tU9cd_Iz<`wm$XT=~hTL2J3iHu8+> zm_?R{mT4L5vKi>6>JCwb_Q=6`1Kn@W>1X zIT}`$gX~>&&9YwNlffxv;5RjZ3m-{Q9{?gj>gO!ovca}*?p`e=H+s@p^x^pGB4$uV zWdnq_l9vEr-(_vL9$I?FZ^J$PZ3|x*w@@vg7qf)Ph|IF&3SDDxu}e4mI%NNuj^VB_DDxWFRt&n+i5><5MHD zEVjTdu!50b%cpJFX|v;V_fC}{t{N`Di`M$VI^wd{~6rLlDLTN6;^0ub~fWKBN+?`{XQK53__wUK=`P^^e8 zerMcQc|j;2z4 z>4>x(GWUgTxcZgJN}!r}Ld}DIil|U(6`nQc5~&)@U(}4jPj$XwE#>2`6)8jF^?2^0 z#@u=}8j}DJs}Z&VbL$k>acah1-} zN!(I*IpNXoeSZ^KWANe|K+o9EEM^s~yF7$Fk)|wU)t9m9acVsSSmxflMA{D1 zAvxo8)eObFfw6u`yD`Y6zj!FkirVTdZQkPVa>bojcR=haN3HADqL_}b(GK`v)*W*FFADKAm5D19zwj} z0#)F)MsExPCG;gM+a?|FcXtIdAVmPsn+`0e11Aa_<$MI&kHg8gsE%`9L{#?5)7xB^ zAMZCy6v{NGyvkF(E#HRdwc#{@6 z%})=d&s!Ct4VFiAAAM6mhrXPe)c4sCW2Vx;i(?= zLP=2vggnZvI9rdM%L<3~NBeXLOnpruni*~#`{2ZJVJvT{uz+s6{$4l+XY0AdcfXci zL@>o$NVBQKHYuqwEb|4_w0JjCC4%}A00vV0LjA1Nt)uA)ul+om{)JwRawUCHtAsL< zzmnXD#TLaYr;jx{F~X-|oj6&OaQgh{4BK%@+wx-n!@eI*h3}oSwy>n%Z-%e`6ajLA zL&4^uA37F^{z(+uS}=9%Vfj%H^ArOEc;|Zf9;5~76(V88M^3*Zad~9nZt%CfXio=g zH0I+K!fs8qU8u(iI7P<&LSy6`e5uv>gr0~M5mTLu8fe1t@-I?m{XF#5bCx;$H_?N{rX zh$v|t{3BLz9n8YU=)-a3-46Ej9uu))Wf%Cu@8gO+0TNBc>x{XkS%xE>Gd!dHX2bMA zV?uco%-%KlwxMzbYIqIphXF1(Ka=RIjzAG-_JUElFQIPJ2*87srQatXFZX{C=@&k% za4{M|Ho%)nZ>dMqh6ZVONiImmS$mSIejIehKegHh^>H$tI}kM3%#)UuKUP*X*n}<*yA{c5Q{wYCf?6kf3hhH{ z)60yKI5VQeUv*iXK6^#ShF8&6Nb8NGBAr)xT+rDAZEbSmlLeqFo^~(U@9U_HC=<^0 zkh3n&1Y1`YENa=x;VD-fwp=pyW1HW1t}piY<7&5Ii=DNAt?8Vp{!y*;x4r%A2?V-) zx%oM%7H#*_-EZfsnOs2g9sbql!p^}V&C5hLqmrr-nPA6qL?gNeg1sZqa%6x zlewOr-bxR&Oo#Wfdwco+7NuKwL9P1VqjUp}-oLQCn#p?Et-5pWz%d}Oi&t)E4w7Y`H^&oYnlZ&kIlv>J zTZJZ|zy}ftzF4a8rnXyU(%+qUM8Wf=XNf~6?9rW$mIj1K#WN|@i=u;{LJC_z-5Jz!*4x#|<8yvOUw~+!lroQy&4e{P{<3z4RxQIR*XO zEkysfOak~^ZZ?}#kQX0UJY=*ttT@=WN>bJBiZv3A^NM>x4)Oj6);_E>SO8$eu_ZfPqD2VE>lpV*y|$y*d8}I_%g- zQD9gTyW9VohXgp_{{oP13=CV;QuQ4=@gIryCsP89C-+YrwEMIF|1sXvt9GZ(8rrr8 z{l|vGxoXAUf@E2LN9>Z?<_46M3u?$Q9cysR!;Q!~Nx zYorVCE9V@5q{()6*_{5d-+RZ4>3lP`&8MrMd`;=PXD@xSB>iFQuHSLKsqt#y>(#_9 z%(E5G7e$BPbpfV35a7zB=6!p1=%n0xkw8zZk4MMtUs;HT>Me1CO5*nf#CM5Rq}IN- zhchUs=E;Z&YlVb*CX$$luHNp}aQ?-v>i5v$?Tgg^`b{yZ9)*REJpl?0S>a!zW{>!+ zkF3<%POJ+P=k~ueBu!md+DDa5Km2AJr};18dB-zD_Braq7#SM($<%7q(Y;Cv9Ho6V zjK3^|I~}2M6yX1i7JG}Gf8yowfJtcF&Ao|Jt zu2Y)}E2QmXPqe^@-Eyz^jqU1QjYeR3C`jt!4jw2eX}JbmdbZIs?BPBtp-^pzkkRu( zzD!~5+@axe10w}LS8Uba!V6=e}PkZV|dA{n3(fD5ZAsPx#lJ)V{PG-hbb! zwexQ@L@akAmX3JdjByeQCv0v$3ND5z(^m?=F8X;|^BT5Jil$laCOELWkrhXBV_hw> z4vX!g?ub-wcFFnhdHP44>CDTLsbvkZEI1EO!=$+YVy8mGWQUY9B#{e%)$%>Gn=1iS zQlK-20$JxFj%&?HP9|{ zudk@Cc&55d;^s$3@!A!%3Ue9g?~LO&!v|5m(oFy)#U&DdC0HgL+g#eSvE4R_lN!Do z$S0QiVTTUfmLpl9l*&(QO>rj>_<&$#Dxg>MLH*VaKpAY9$m`h>;x( zf)iElUHS!Lg-)4hFV4pgt zzg1!UrK&VqF!O`4cpjmR#Q?XZ5&8T*Wwbh#shR7gEN0N!g0W)O;%2K_ocUl8ZplRa zc~q^=s~bsvZzjb*U*fsl1TB;UHlY+<7a*i#lY{NcXzhFCB&;pAQK;=cz!g|fw3~J|Iws;zU!IRD#3aK|w z^+#3mwdk`;#X3~|$89V|+adf(CEI~rU3%Q_&VK1>1yaU3s)k4d7BkQu1)zD>$`R~o z9*i%Y>@bV8PLsIp_KJ9lG1cj&NmN-IOjcQYyk85h{pEZmR^CwV-7c8|jMOP$oU=-n zrn`(vgSK5If2VgNDLLIr`Y-rJ4-ZPZ9N86ghNgnN6CxZo5!^NUIGtzNbZqc>3`H_s z;y{L}e{W60*=CxSepv#iPbQ+%7X9gJ%9)k@D@!upa7F$iw@8oRb_LUC6(f#alJrYt z%99TnmnNu1ww}JhLUk5a{eM9x4yL)ii1}zHtqZ!87?e$r!LE3i(5g&Fs}#yXHfH91 z3^OvaCq93Zm~5t+G!4Cwom4BAr!riO%U5ADHhBm=O%KP#IV2_GrH-*D5*fvj>Mn&9YX9d^f1-6dD z__zm^EgzrZxJ*zh_o(l5EY_|-ir(>|IBsn?Y`6z+gX#OW9^ zcVoZ<5x5A~;cgMy4s*XJ62;y`8&LPhhprT@9)sXK0|LS}Yyt?{BqNS8*JzM25&UdF zyM;_2YuJefd{DlabJ<-@(@wV;Th2H)7E|~VDQB)Z(TuoJAXKFjsncO{M#F_Mkwtil z4Pz=(nU8eY$BG79shwrsFvBH+lyg)SjX3nTYy-w!r0V|HU+ENvFbp`!59fGz zqr)bI=sDdguLZvXoihhv3qwt~s2h}Qs)_s-QdLJvl4IA)JI_uz971nG{k%m@v4 zHRigdoUNZhc~jalyIs9YG?m_yjsr^kA3cUE-6K}kg*beCJg86#KoFij?PJ#k;Ga zsrJnl9>&9$bja_^Hsqgp_hDhXtNXZT^Z~Vi3AAz9b;SU}$gV3>aRF!)&N*TVz4H|+ z*)CjW@yfIr?{eRcLmtQsL{*Lae9bgtApf#wU)9_737s%IzgHV;CjO=T_w~t~%7xtj z2?25QWRLA5u(8fgC7@v!1L85=T(!0Sv}|D3iPvpkVmwZ`XA%18Gpz>Y*T@<^r&(5D zR|e7@^pVq(-&e(2%5DnoYXokh`_Rc{WHIvF%5MM0>?gpZFzdQZ*%?NOOg@n5-7dP| zR~eiI!36m&+YIK^=|fQZqI6zq^^b8+p48!+rsPa;-4jtU^MedEULJ%^u}-8W2Ue_- zc6U&wWh8Ts#)E7qZ!0O(8YQ|?bqBxxXxRFp=e}L|1Ygp3+p(oHqLBePOUw$lZhQ4$ z6{LZJ)z<6)l=3k*yZFQuE~-?AYKZ*omJt;zFHym{*PYmL*Om4ztN;WtH7Mq z;2IoN%0cYkHQ)`NDv_7+6#xe1#RT*9t1M2myZVy3y7DD#Wu(&qfK<52nRJ`}3A2@; z7<8+t5|ojWNs9+tx@-fw5mRV?kMZqkWLy6=cCjHf|8@(2C&HGv zbOP@#+gmPZ0bn-%KhBrIP z`|pcJs!%U?a{Q`ksvh2_6dv3cB2Io`sBG0Y~{owK)`}Jb`W|aCae&lWwwu|?^5kK-UaN4Jc$jAO$@GjDO zyQ?3+_7b&Yn7@zrJDz$p0kR4HF=g~`Fa8hS)qj#_o0PyUGsx(npa1nToPV^@yV>JA zwKUj(1D`*4#?{@a`2U`=Mm-SBNG6Beg6z_byF}nSS^p2IlJ75GNC6n#J4@Pix82@B z!XG;-xLq)y>3;ZMZ^D@b!lmGfrR&fg3a$jv0$3#cT27X|vbDH+(%ZtX!16Rw4*Y=6 zu{O?FD34hKQ0{9kB}+Go)IKb)sJpDj{{u=#2~FDtrc+|k?Z3)XdOq;aL2`Yz4W(1( zvz=^}+X3RqJnL5(p*n_Zz27;lA0#sB?)5evv;5&~AQ!PZ#5F@`4t-km;d678gzvn^ zWtUixgTKKTyg28Cumpubw$iUjxJ!!k4`WMowNvA_zHievWkoi)!W^<%o9b4+(zT8L zIniFJ=imGRbM@)1?9P2&o!XO^(+RWv&RIi0O4J?A?Ytbhx_fsJ1US?#95FKTbG9pK z6dSw-<#}4g_DP@TD769bj_YhIEFt1rk?r;gmZlI7N5_%%^B-$?`JMR2*|;j|LJA?? zIOR)A0j*jfD+A@piJ~&^=@Hv|fUgGkI(|L+;5IHP|9<^Z@41Z6WNnADP-&~ua{*se zg+pe33Yav@DTBNWWqbbfl2xn!&!GEKDqEp`3DZs`PE+XOQJVX-Y^fmgV&dXj+t(#R z2#zkDa0|!ue{#ws%R;&0V=KAF#?rrwJlRn@vJlLEWDc8{Jg??PGw9&2s06OCPjJS{L0MZWb zm~ycTu^$&pPYWW^5#AfL>s=Ym!b{(UV7Uh1QX92K#4ba2;>Zj#*Y)ZFYgA$s5Bu$z zlT?Bq`BO`>5H9xBf#9d^nokQHSGg#ylojsbDC2Q|)w^gm3KLZ(@{7JmngJOetq zZEYkU1_Hh5*}X-gsU&9^7cKDxK`VSsAq?f5QJBv!m~=v1|v7B@q$(gTXUQS3D!Mm$BX7gOocTJh>r(4hF z#Xbg^^0o|U)1f2Mew{eRt0$nBggg}XpR&4adNI`i3_Jg02!F`EFsTal1 za(?EQmcc(VfaM{PR zVbyg19vlk*yP#@X^e~G6$-ohXJo3|w>)6TR)&X2E^N&?~%X4ot0UK=JC;telxdLz4sU-VOV; zr$2~O+;((=?E^AlRDD>q`%8K|WLj*ZgZ)6j%gu&v#Hq6*o~_)I<+ zRmyhf>PeR1nV$jRsi%%nW#%zOKuM2kEl){%km0X{FVFa%b}mJHRQE@B#i}A}=F$d( zJ4uz#gff}T@usenc(`3bMUu=7p(!fPfwLF@sw~JZ1{X(^v0f99-w^ZA0^$ov`FGbo zJ;>auwu6H_eHOG(@*JzG4<8GOQAX32tq+Bl)qUu@k<|otEjt#=hA+w@vM|G+z`f$=Aw#2=L{!g^HF=mvVk)d^t^eOcI7Q>1Y~S z=O>?-b%AOQ71Y2#8t6+_@r04)SNXoso6#sZq{xM(%A?fQTy>(7tifVAV&5n??DyQX z87BO8buBTfmrjkfcgV?!Y#|VMzz|JIW|L-n2D$t# z$IKko$V>8n01*=9w0VXH>5d?Tq>VJeoT2TFlCdg*?iQUI!GV53H zTed3;`&UrQ6&#ytPBu5|*^S%8WMqv}U3K2*y6NT~3d)bTe|2BMzElL__AmPNmoHQF zCF6gW2DwWW6vV_Fd362Vp1#*cm`B{acfjJ6kLvTDy?)~&Zk1_VK9mxuxW(97kZtat zhSlOm-I_P)9qX_ObvG(9n+BU+Q*omv4g-p1L~V4iS(&1!a!w;+4-2cw5$+NL7X&Q{ zc;0t_TSOzG<=Me`B|XV_c0+MJxF59A8^?e8Dc9GfJ6|(M-dnGURFA&&kh&bBbHa0U zkUaE8U-op5T5bi=8wG_8BPgiqgtm~UISsDz6Aj}dLn}{2Em-v5%F4F+3&tn7^p`nY zVq9jiV>akMh~<4gmEMV3aL4Jh_C9gSIuEUXSGfLBu#r8j@zp)+U>Ex|tGmH%*>1P$ zQk8IpfI60T%gjBo}m^`N|gvY7~HBKOHD(UI) z26C=HQO%xF_Vf3pRfV%e&N16GIKD0O`+Bc0Ncm15RG4=*dcja#1u9YvylGM0D11;kg^D&Y^R`ixp71d`lH=(4R5I-G!QSHk~F zlcj1}&aI^ewHHYe141W)$}SoV>oZJFLh*GPaCf4sqx*w^(tsSkgSW(vGMk%-Y zWt{!DBgf2h(=T=zpL7+P#xRM0;*s2b+<7SXjt1`;ZPiq_f)8GJ^M{q zzAf2Kmz6i$JQYkPU}s$nRQ2>D`slAV&Uviah0e6nJTSa6%biAZ$=3A1(A?(suOnY7 zU;n=EG_9@@n+IVY2C;?ux!Xfn5x?o*gNu~?^2Rf4R@eW8$JEFAikdTHzgvL(LN#@g z;7`2-HmT*O`3|1_{7l|#b-)7E2(^ebrK>#7wzy)0UeF(|(R~QkCEu>Kbi+B?kSRiPO)#c-0jKxzepH+NpA2yYw6Cm9(F3gi7mwH(|3xsVx23VAqtVi3-#!GR=@O1xHt)j3{W)sMss16 z^QEh%{Ylq!*qfBj1Ras*g|NQ(x|IiIOHs;9L&CG+CkGAtl3nrfhK3|w^!&YG28)weq!v%0fl!HGn zfjh{RRHUh_G@V8m`r`s#*9K2htDW(anVX?zip$=|DsgSaT$5xfUhCHKqOMRo07mSs4&|XlH z^4%?-CDtKx>pkXcyZTxmIaSeb%Ub#*9oF z3f4(sviI_VBzZR`>afRshJ|AHU0mx?tzC#|FLalbla)6g*&|QUA^vK4XG*Q&6?=GJ zd^)EEMSa3gMO;HK75w=lzYwZ9pKzjb9Xu4Pt-!4-(6_M9|Da}nP6MhvKfZMA{w;M# z0?+~G=rj$k)Ibt)4P3z#iwT93V)Jo{rUk8=?(XJM8on?ORc2=y(p>`Sv_nE;_3X0b$5mhmx8JL-ajo=4WMltF0pN!;XgHOm0=`y zY-!Dw{t1~eU_l}z2@XZd9d+-W&rDAm*feUU+iF`w9=HOgXnrboHT3l zOXfhvS>jwPB~OXJb9FZ3tT|1BVByd}K9RAof~b|g>(&v+X>#OHp<(q6690p+%+GCJy@pDTnc>Yjx;ge083kf1%Xj*6OfCv(I%jJvf9>Z*%#lWkgE!Yf*v zJO}-TVs7xpOOvN7M(N(0#i7Il7i4X&>aGpIM)JHLqo$FWD9871Ap#vWL6;mUdVP65 zs66ktH4K}JzE;pmdTxW3D+7z~2q_DtFoGCV9a?$&V1r{>rH`un5-H0yLGBxEQnyLd zo?l7*rh~szCljGDB2>$HHom^~D*725LY#_T3n@{Zil&4d%i+pciu6}|XE0_V+(UoB z%8RI2Vxgfr`@3A-Y)8stL?^49lU-lKZae6#Vx3*uN?*gTPc|~zm52-v%&;Vhu7cNa z6}v!!t^JAVp|iGe9o0=;;#cPOzZqnvA53GSDyM_80-}d1Isg)g z%~&lnpW_`!j5e|6JTTr3Urg+F==KSfVCauH_&phJzS+YcD-qcJ{<$04v9?>`$*Lj7 zw%A#qOG@c-(c^Zh^qz z{vA5z!2I=GwN%3!K6AP>zoal!9z?C6I~M;qs@-gE)M+?9t3hHFCja)NFrlrgTSR!# zsmLjL-^nzVhK@qVyOTcy_k^d0;K?@O6ooS>$N>6)r@wp{HYM%e^bN;y^(~L9?V>SPm3K z^q5jCGXw{s_h*y-&$-X5`{KTM-r?_u-|^X-j$y<}UG>ih*F6AOYe(+o6gxs|mT+_1%evFeX|#SaDrB{1WWp%S{nFBt zoZ9koxhir;D_&y|(>~KU%ca!1tC!5L4_^;ZZGEnYIyBtX!Ny|`ZY>o-9CfcPekyWm z^BjIe?4=sg)grecF6F=%1qf_M5QGt@DpxvDQ^6Dl&KP9Le&OyC>9w{Fyo*2|%MT=; z*=wySgPm%QRM10F<}BjNf>n#3o_=%7+SL5E4QElXiq&CszT0Lz2%l!rw_9@}uyZ#v zsdIgIJxG>^@u9t#+!e>j4sPq@woRmP;g;|& zJG{z82H_Qq+HaJdi@8>w+n)n2w_o8_e%lPqDCy{)^E>oK*xAbhYI7p*gcvv^H@fdp zn&_`$u1}ixz{UHM+ri#^#9!{j@=v)zijQ~)uI!Kvu=fBO-0GR<3~Q>rm@V(MPwbR2 zWX_tghPGYHHb=a7hrDN*`(7BYA9aNfS#Q2bRI3MHJjl?WUh!=H&qn>|G`AGi5i(Ve zP>w5v2_xt(z4>;6J~r*s2Yn2}4I`wgpRT6H?j{S}aRdaYl+*4`m0n=dZ{x>otQ2JI z8)>jc{~Fk}mOj0MMmYD7QFLJ^gizuNQm86lEGzcii@wTQCj~40BE|MpC;(Iqb?rO% zO};Oi)s52cT0ZDQ2?z``mVNc3^~aMFGrC6?*dYeQ{#Y&z(Vmqsr`REAd6#QckmbBZ z!+WBuTjf1m-(M}j?4ArU5YX)RGPOY7((Bt{gIkMfD7LV z91{xm-QRN<%>tYxxhD`C52ZIgrg|E0D;oI*|jW^(~ z&bt~q>>w0$>cr1l`8sMI?_tRNLS=dX2Gdus`kVBm^%8ZIoFe!ifk^~hVBtodoQEIV24vk&ndyU~s7Y!BDJUPEQ=!p+13g}?w8u+5qX z`tVDk7neCA`=ii`Oq8Z2GWnt*sUEF8}_2zWebcS_|0F_xVX+*4O_# z692y?tN`AJ&=!fy_kSz-)t6GDg&{U2s)* zF!_ZE(amPnvF+8VOS|CsmWmh=ZR*?|{%#-d*95nS=hkm(Fjmb0swuKYue56D&Ev*h zNV4Uab1qOT|Cv5K^dzW@OzyRJ(~rebzeeR4!HhEp36#w@`hYQxdU(laR-Jw=Tp*1# zoQv$|5IfH7(+tAaoPQbM;$Cnu!Pked?=>=GNiDOr#snyw|9p$4YDvlBo}K>QV3G~;oq5z^hyO$sVdl^2rUl- z{-XU7)M%-3V6N)6e6fv|iaD!90#zh(mtG*w`S)BMl282gE6~ncndIm-9w&bdIedns zrTYkTeP_`S7aPO-G!9v`XP?;6{&v3jI&c~XiKW0rDr{${0$s1dl{}6;5K_f+>G%YxlBq|xy!M}SMOCOMhTxuPOMZRBSSshyc9WVa^ zLtqaN)#zK-=FA;0?=|so9ASZ`pATk+4)3*9JXjU`^*{l(5;{tDzFJ8?9Z;b5F_|!{ z-pLguIShBl3!~nh@urLBb%rV$wJ&3a3_&70G)WkYyo5iJJCu%Jf_t~emy9I@x*?)I zHiwe%6XEJR*r!josl=(c^El;MG*LJy)$k(*OLCtphBL-lwHlfJ(@GKv>)!E`2xjJP z>89l@Gol)GhUpLQWM?n+5*+Q>`)3$h&gFvxmVKl9*Eke>;m_zl0PT6`BwO!C;0J(U zpHz~RnlzLf)fK$@W{du0t!L85{3INAetNEjK3_&hSscQm@Lu^8V8prL?wjt1$F$l> zi|N_f<(j)|*7}`$UN7gkwHrHb;O>@#S2;fMDspAsr1@c3X`r&S?>D9z1$N#P>R0`f zs9v+Ld&4I8Su*~TKw&b14C9!mN*0@%ccMx~*@COtddRRgP3&8&%*B|~B=z|x1jqe6 z`9gBmiA|L5ng&X}uS@V>v6A*%UTIac_1gSzOth~=lrJS(<{$e`Fwy$}BHBH8{VGtB z`h!Q*ocEmaplM%wbG~?p8~Q$pAh*_NmKzH4jS6jL)+inl?7_abNO4^mgoSGz)k25L z+P^GTuqXQshs5|Z154c=jdtmb%Pv zIGlUL@%_H98;?9*R*nYs^Wu6hT6Jyj`gXHR%&H#SJ&xEWi@ER|ieza!Q@39v_p7%6 z3L43%&^mRs?LbA_-9D%XpCFneWrr?BV+kTh`D*wX3D&I;J~fMW&~EbpZP*ADBBUr& zx1Z?8+klXwICtI2e`Xq|EO<3b-??TN0kA@5iPoFGiR@*tRA@$boGZywUtfwqu<+ga z3a$H1N}WPd=P>M~rwGQ$X^63?XDP_2MF+uA8&5x`MYxw1Py;T-c=@_0ob^Ty!dglw z?pSac!;YcBs;}`)R8pYu?}b1Ar#yUkU2*YTeH;JwGz|U$AH0#n!eA#e$XZR0ZcESt zU-NrtNiseL6jnJ#)u8zfhwYB+YwWn_hEaj5s_73$eM4Jx+%KC8-S48&-){nZ7&$D3 z>bz?}fN+Z2kAh&6HKtp1FBf^Jp8E57pUeVE&Y*csDkSZ?dbH0xEv6NwFmkI1pfmq2 zc(!$on}%=2^8vvCrlWi8GqIPQ!3ll)1F`s>$z=dzVf_E-~Z5+s4J#nUAs`H1=2>1vHrkhH?ZV?nIwP;pIYluyz!4Lk4Tm9(yRyP5I`$oqU!wwd&I=ptlXhZ(Ok(|;0W@uoTO zl0iG?%=>%I71|CKT3pdi%mdrnUw0OvK2|*MnZMVoy}s1xc^OEN;78lezHA?mB%@gp z-|@cV?FJjP>Pj#;MWD(Fq#e^5m(}ibbQturqE-#@!#pyG(opncQw0+;%$AtvDz!MC z4srVDzOY@RRra>qqa)_~Z1I*`hPX*Fd4< zg>Zf@E_g8TNVd^%FvFqqBIZVy%wMuQ$ai~W5^N57Q9sFPkOLu&T4)UO17_;6mYU=r z-(~@1gL+kwlXJ}Sw54qykGMeJVhKX*Qvbro+^JVu&g84`EP?$1CnVv!wZGB4X?ik#Bqp4o>v?)8}m zMSIXF+SGcqBO20^N4a<@l-V9$)$3o^m3?=Xlrly!vQL!T?BJ2X33t`Bmd&uI)u^-d z)DmPH>_NX8FjTe7L>%(aDVv^UTN-)0<#TMq{e~i+pQ9PQ~l@v z01kWP9tJP2SUt?5@u{~xi_do3Y5sC_q?j&$J}2elQEZlCn(E1kT}1nQ6(FQ9r6Kpk z9=YYlfwLW|zfDFggw%3MJCsA&th)#*dHRqDC0|u{&;Q1_tNQV_vB#6{nlQIM|C;>x zThlfvbIAeMGRG6k>q9eJcA-sjP%Y305rp*T`~eP*@l}9FFXuzE-d-xM;7t3Jm4b9>q-Y@$6$k1aLpGKnnV%qqeO2@4{w`8W3R5>h;>NmOg`;|o6ME zTeS#J8^R@uA){*SBEGdjVZ8$=xJ%ha^Rn!dgJD)H+^F?LETqWfDEet|=fnWik(OlK zPn7Gv2SGj`_c8~k^>5#klvuM=Y=YnXz?Xjdo6&e|17O*oXmwKmP5{e-Hp%2h&8^&2 z#gyyH?aF)YZU;a%YxnJWa=J~jNG=JZrey+y59#))^vi55=}_yxYED_^`-0HBC7rZj zhr@c;T-(T#r6EEWL>X1-P>yoJE}c!jBt_u>2 z>C+-QRbkq0qyai3ied7D!pqj$mg`J?)58I65>cl7$7dYqKj{OhXn`O{+K#miPb=o> z7t#=+nIwabw~O3y18c)gllis1=V>xKT#`Q(R`~5MG}=vZ{q3;hC9fM#xc-#>Qj(7+ z4wZWEB+6g=GVO1jQJciFF)IFGwUsVaEj%lty`;?{wbv~d0cl^~<*fF+tgB`c)_VAo z52{kzpmS_zI;u|@B2Xw+>Qg30p4vlpK?!pS^S_?!H}%p8+4ha~qg%EeGv!wh&#=T# z9uVZMdJ*~CoMw#Rc+ zZB8_U7wbo_Q1hX19=ukXS9L9+qzdxNfm#J+mC2XX5VA<+sl1#D{~}(<%z+gd&)*J9 zguU-?8 zEKU9nr}Ty7U(%uZ_0>SW@V;(!HMX_PICnsnu zjG;qg7E`O2%S-tt-$%kE)eOgrtc>-uCVY;FpK+~QONlrA*6A@n%H?0F?~Au||oaWtV&9!X~MHn2u-sqdT=@!b4w`Qqj@v(N~?dO zB5v5;5q@P+?tapsMuIYdWdd`tY@hcu{<@t5b@ie0ycSwy&IXeX&`&_d_~P)<;jKc*Ylv)KQ)0RpmVs??5E;<8xSC& zlW7<^oR1!Sv^a1GaGdAzZ~D?vJFkti{RG{UcyD*n1a=`ae-za^;mCpk9^kuWZ!N#4 zR#VJdYa0ImbNZnX0AdNCNO+^oc^d)8dtU+VjpgKS#PpjI7d6Ntq25M|ecy7i2=I=Q zS1fx+*LH^f{@pw3ksVlA(kSrG7x(=(>t2bLS)~*;+p>n%1<~O?NVsf&bz;JO!(*Ky z0y8CT^dPt(tQ1)A>9X^u4LE-nzB_-@%Wkhiu;Diiunf0eDK)po&dKFi02=VqPx6Pm zgrVbYgY2713r!1CgM$;%Ourj3k7}*$-#_#9kt$W(I&QoX#kV}q zzsA&;u@%6SNOcQOGWocmV`ltuG1jsX$6FN5Y+6`kvnz>R-UaS>%FG71p;^m@*OuG> za&J`Z<0aiCi2gnO5}+gfvk4ge0SL|)=uAYM|0Zz(`#@3>)uCa|f6IPw#z+IcBwS>% zD|r=DTa!Dw=KUv;boS^bWWaDE+8pawmTq%=Xy?Ek+jnV{Ns~`X41#${-Tzs_ z98--pw+d(Wkn;Py#6tnsn@&iob)q`dvJEvR*?{-oX*A(!3=Lt)LNjTRJXLgiGB&_v z$nd!P$?S09`=<$ApK#H2o&Ml_a9z;ud~gg?JK08SDF4lo-f>{#!@$PK2S9A&*>`y9 z0r2{8wE7{~D2V@V^aTJhiYeQA>nl=?Z5)NeCYfdZ2AiG+cmDTwHC0ipa&W z1@-Q$t~Vcl3k_sonnIAS6V+wY@!zD;4-Ecz5`722?;-7xdh>sK`<+|E04*DK^nf_C zikVkmmP<(i!X$sLLgBtkihuiX7YIPdV=q4ZafEN1{CM)eLoLJPe~#LXp+a#`y6Gmv6L5w;{Up3a0r|0> z6aMJ~XyM&pw&;d;m(i$A_B9%QW=SwfTAVr>#jJ;@NAV1K4F51Lf(M(KgWWb1_h*+y z<;HV>?|5_P#(QEfMxIC-ja{|MY!ocb_YayAJ}jls5HmELTSZsT=y-Hu4n;1Mq1ET$ z$S8pnXh{lqK1ko-i0(Y?akq&<7uBJagriC+G+rO5u6ov&Fh=#x4q_;pw<}(ZuGSI# z926P=EE%HqpA<30(bOsdf|G*1>7UvhrdQ|F+USu>#yDutcKkoV-q{QC*^xBN%;G%U zdk#K}G>W7NI({n~EF7$4&%xHfgRV(Puao_o8}a%ibm^T{8iMHS-q>HXr8U2-U1CrY z=h=1-@*0@$u;-@aCD39XNW)m@5Y@SE8|KOL zvh3N{NIl%-nJB*sKTw^(0I>$oXk7AIuDdPvC$q=Zh53VwNC7%xrmxkE=#UzcnRD#a zn0Asw!>8dvWYkC6(~@(@s)lKWrFnMq8an!IU3Jeea zrur0v)JiHBawk*s7Tr|na};C%WGEPKMy9HDm0Nyu$d)xj=}{mN>d+M^reAkunt<~s z1bENng(x4~&>Y*UPB6VGY7*J8$fpZm`jS}9{)@eneJz9C{`vi-eH0|;x~gH=4174a zEWZQ1OXd}2@bPjEd(A3T$U#q+nVLuI4bBE6?+I~o)DM`w0I_u3L6R({FT71I-5P}I zd2{Q5T$*q(f*(2~->OO8$3yqnF z>WgXm*aJZFxOA#}r`Wwh_TQ(U7d=;Bc%JC9gj9;0gpDt~Tbl3nCDEt)k)fQaT*I^1 z(>&DMy^V!VH=}(7eGfkdA5JORtmY+rK8R0!R#XE0BYlJo@kgHVf1m83e(4=136HU* zd4eygrJd9dS?wqF8aB+0NHkJkryPt5f|4}f-5)V%S6}#tAMFEP$4IGc_Jgba>^d+3 zL(xX*l1Pl+Jk&mJP>&e3Kd?w#nt4NA*l6dfdBJz7o9EVHYjGv{zDhK;`=YRz4Wu%L z6$<;H<${;QMjqcMz%*T1Pfu5vW#yghHlu_lC9_@G;F{ zowi*9i{xCVJh}CZT_rU{!0?G5u%d3>TnIJ2peRlO)lKN^xl}YDY&bd8T28Ci z>OZB55t>%i08VHWBa~5~1QdS~V9w+ASAB2S$PJ@{*ttn=0$jBz2kOn)m?%#%+G;CE zEZ>a7spp0yffn*Wbx;T3D7rI>RGn4Ya;5)RYvvPJuyO)-DC@I#@byF^JCpT%6P`(T~=A5 zd41#f6{xtTc^FsP#aMu|A-?njm&W2^>f0h@+j#GNB`=a zd}2?Kt;SsPaP3vgo>9_S-{-N5egV9&?k5<9c2cd8SkV<#g zJbQKioW)xi7ukcMtGYvW)qB65tN||<*-mnuh2aVk4mhC%wZ>}1L=3cRT`Pdx$BpJp zF~1{v?3b`s&8*|Xr^xAquHhN{Os9fqY2gt)P4t0;m}mDdjrY&-NDD*8hV67XQq;&} zNIRhy=3ThyrwowGtAp=~^Prv7%ueTK->65h#Jn5Tdih|RWk4?G%I7Uh z&pK^=bep=1_d!32R(u}VRjqhtSxT4DC9vhHKxLk`D$}J^`xNW5daEmgQi*Aq&bi^4 zxXesv^6wmKe?3?t)SCeAxLr!UXunB}>2TXN7jm5)MdD0X(-cf#BeNSv?-}b(wydEG z9}HSWvaCAs3;t14HP%}C%4ZF$UwfdZ>;I3CH5zZR0L_!i~1B@JB02|2~#Rx9Lo&Uisk(Z~uAA73qPb6tC96c&i=h z)(wjETSIkj0Xl3~+a+b;+gO#WxZC0~f~W$B*oikiA!5i0Q45%%g_5GUkjYwsOYsuN zSk%luLm1EQFk+P~^|Gx*sm9@M74sjLBEtmK$$65#e~@0*6=PD>j98(7CJZ*u1DGVEQ?by)C?Oep?Z8+<@epz5-$Cl7O znWDpSOe~q}+Z-}DnXOhY$9aNLWZ_}nL#;;GF-UU#hCrNZ0yVhTuMe7PMml=G^q971=3p`B zZA>jkz#W68`yxR*+_9-Bjjt7_UqGxYwOmnh_lc319IUV&$()R2sAyV{-Lht19O~kV z6x0c7wr*HdoDr(g&R?Q2t#Ux;5gNME^_0);3gXs>UiR<0ONmuVX~&&}vHj^?*DVSM zSLboUgM8__1(5KD&b-rokp96kRy?(QlkaxE7IA(NBjdS8=p`X&fi)OwG@V;k4IwA)A2=75cI?IB_hfbK0=`C^+1=pb z3HKer|EUER$dxzD#0+-OKEd2-Rz_j(il1<^t~~&(%J_^BF)SZ*9@&lrN=~$amY+_DBB}pE#&C~SeRi<9@Lu} zszC8UO5K9#4^f6uvCx|?e6q^0U`K-SJg%WGMn#NGH|uHvCn&mCxG09M?xP%m@KfpjeQF^ zW%CIi1jOL`>*lTC`tQ83uBd|thCsF<7e2bd9K~*FGV-gW8tBCz88^i_Q@CxGAG+Ae z_KuqH#`NFqP1H-c^dY7owNkJ8n!i-WF~N?cv~XOBz+OZQ5RS#|F0Z$n!uZEVz7Yi& zi81rq_0lQ1diy=a?W#kEJ|=MCyalYaChpp!@#r` z7d>G4t*iqm4ryAH8vfX-l!16}s+5Kp-5xdSeD8Hfuje|Oo;Q<`9OE5*bjgRF5|y?g z^n`_h1%KO@nWsIeJts?NKA01x%1K!Dv~ViigVZ`yk8v{^@6rdugkw5{@x>5wmlD|@ zucmi_V$WeE3GZYt6!<1sVNtmZY4p)T9eC*3*W$7*-VHb#dtafrd@fbFZ!*11XC3%a zGxZEw2o>@)9=R+@<`nxq>pCY3Ei+AW(=yXg`$ycoURTPsh9s1vqDZ2@)zc^2{Msn} zs<_uYytYc(sfO2eA%T9KmKI^YVjkD^rXrx`!U}eo|Fuu*bGgiI3F+TK0_0-n;AfM} z+h`0820XzY=voKaCwN}wWQM4%d{M|-Auv`eC3T6`UbcUyh@~639J=M_?>~fDgwaKY zApjko(^l>gj%sf~r;j(=euo7bunUN#WYry_tW1CRd&dWtj#bp`egj^i!Lm{6> z;J$p?cI3DJ6Q|sjbzQzVR_CCz*o&rksV#pU+~EduI&|KRzpG80A(#LC-<%J%vmSlc zb}133R*jwb8Dt|syt{af_MWsamAs!p(DYLJ?gqQtMcZ`_c8P7g%qtr$05ZI??}?$< zDz1f1Rm+R#wv1jDdkhGpk&)%ab@uNRTcyct-TK?b&wJx`OEbj0j?X7Q1nBgJ<8OWy z69a-h-OERT?~p7IuYv&1vgU9AZzblBN8(tk*bW4_YpN4`)4vvCf5*@*_XWg!QZvRFIUxYzQ&UKeX%YbfJ>X*O&lN3I4K_H;U11-;q*IfP|y$ zaZ47r*uNj?+lL*8Ms#e6C%@NS0?Rk8TxYPva&HJ^aT|E?gN?|sar7ahsXGefJP8TO zc$X2IF8U_?m{xffVw~JCQmbF}v_F{ycnFhhW8+z_Vy0OQZp-Wf&Nx)?)_;M8{^974 zUUe5L(v5^Da^qrQYaNPY;ZCq4>PL+xgO{E1V^_9jQvqEH{_LuY7FI2VRAhX0#G0no zrQ}8|cowb^!?NG0k!GIt$g;iStu)^qi?L2u#vHi6X^|HVqX&^3kmGD()a)9tlgnB^ zn~^%uloyW~gKC6i`97*=YTYL{n_+4q@pF0VgU{x8p0ciaqPh05%Jz1Uz=G%5nlyRT zfnkK1x$ijM7)UnndQfo;q~_@TTI(d(m8IwIwXnUW07c4x)5dvOypvBi-m}_viFsJ< z_7^g~uzcR$mVznq^V;N`?z2fNO50S;T1B@gt4F`tawW10(0e^T3?kuM_=6c!zsO7$ zC58)m&9i$uRPBEGtfrsb?pCF5tr74nRo-G`H z8cWG!)u3L7YzuDOt^)VpSsdCS5b)D^s_D0>4&IF|M3EQJT3nuW6AonzQF5A6%Nu$984&cVy=VTIi-!k``Un zi;jBsYzRNP$N60Y@UuIMxq((;_A8yKSg1Y)A&khVsF+VX7Lj;AI8&P*8f89G^_qp7 z)MJ-m!C@gStr2v~9SvO<5lV1&_W*`S=3k%&#j9KuK3SZgKUE=ojB8)jS0BSzGre#K zQt{Ptid6+95PRy4o|pvElTeR(!(+HC+W`$2#+s#31(e}#UXG}<4?l0IYU{SOzBjgk zCa+dD)_oTnSwZkC^4#7>D2# z3>U%e80Idxy^%tHCxN%qblwfS)*SU+czUo05A}Po-S^F62_?)OE?i1VH#*=DrJIM6Zp!R&$N43##9ly4k z`|7JpWs46sO~%a1#T+}+{gc*6Xa+n_23afVrLyw4>aBo7yp5Gj#e^I`gWVw7ey7V z02G?)qz!y8L;^HYwDh%>-sQID{?*wmZ7j3!SWSP8A^)Il&F1&AK;-iJo>3E{7smZ2 zH6qm(d};wqfqrWr3_gzU*4%5U%Ia8Kx*1c>EuMMOFXy-Zwz=Fa7Q56?h`DD-vR!xTH(+ydtc9C?s;;JzLHogla>~zUY`JAFtJ3IXT~j;Xd>&)OzK3 zdE5LG`FF?5)a|da6ez>0I)ZTi)A9vtU8<`7*1tm9Ye>Pv4OZjkE9>l4zbF78?GDr@ z?C@^%vuNv zXe%XF4$tdEn?Ms%S{i8cd3VPLBHA?=%r28MggiZ0 zJ{O`89GMxKXsyM#gE;*bzq!VccrXELh)=KfQXsY#Hz;4RY^*FNo49Z4o4jjr&kDYn zmPvt@L{xFzx7VnXUsTO}dTe-WygX_Ul&a&|L9k&}V--Hm^j#<~z$0oP9jwwe=dZS7 zJAm%?dymB|9S+Ph|Km}V_jsi0q5NSF4ku;!0QsxiY zzb?Ccjj)xJ!PC2;aEz#7KEugd?(q1oI!sl$!Y*=6vWC|~Vk~C*wi`r&42=1Zs)dLk z0}9yW#((TMMH30KMh7G#fan`2Oh{AM=z7VN^1xSCTv zbR%_M5VHq113p6T^?izkd!99Ffw7*n)MK#o*r6v3Pw%Gq)YM13YV_XinKQo%#!W>Q z{-X5FR}Qm3#B9U8w}Bs}Ynz-u`{2M{GgX-HFfiXl#+{d5TB0I%tM@qKl%qgZ!}*%X zzR{GWL|7_AyJpiCZF49a;#m}5i+Sd-fTgD9WdlYqcIf+})Ti03GdNX7C*LvJ8K<6r zTj?5#q}WGKTYm&*8qW+IC$U0RapZH2<=3bVjA9Wi{`jTfY=k0c@x5|FOYWOG%<|ry= z9JSY~51X&Jay~|x=}tBsr*L2#4E5{O7)cah7r04_fooT(N$2J=`KL7~4%KJYtn(Jl z9^R*!PXAkLqulU#iwGw11mZMdx*+qL?73^kz4ZR@5Id{WqcZz}fgkT5+&^x9yD&V% zWeJRNQb@5qnPPl%R5(0o5 zs5*Avs_vJqdY7$*AFH4DI+Fz!RxsuzAE*;$`(3|(k~YQ18X0i=T~?9?HhsVmXAG^v z9I#g{njU!^BFP#}O94NhcltSA4_m2UB73xLbua z)$#Ii2IWrab^R0(tfPvb!t(q)-h=fkr*mw&X8w`DzT0{AV1J{as?7iOaXTb^(E?Kg zc@{(Ap~yesp0zI z*90Bu;9ya2^3K*>1?#_R#|%I_j_uMZ`w`YZb|m`iI3dt>&+{n}j4?uA9rxreEFxNf zd05nVqdoGz(bi%pK~ASXCFHWUr%{8IOR<|yl0k>6R*4FSbn%oK=&bYikfuyN%1!EM zdSSo+D|_Tc2dNf0N3aWc)a#F>nVNU={w7jSwj(nW*db(D(cEVJ z(LZkF-I5T>aJGOGVW)2ykk*Vs4u_V#StH2F9Noc43+$gc71g=BbWcs|JL}W&X4P_n zYEf|r1M^I7eO~-dTw0(j)?{}uj_%gLWzJizaK!Gmg!e28w437Q32pdZTEw-4vim6Y z`+la4cBk{1XYTzmlVM5<#;b64^z)rYXS*v|e)N~K_WBn?;fQ`gC2JbH;T&~L!@jap zh}i8P>~sy_N&~rv4v4tvd$?z+pYSv-tBX-}>hb}-5f5-B*3BCweVA}4~{-NACp zcK8ye8nFdfsX(u4{zpV@L{f^p8FOSNZ zs2N}N33~S7CZ250{-Kg%!Ah(7qY;z`d$^8CVfNQkj@3Lb#DA<1Dfab;C64%k)DWl6 zP%=&SpR%8~A=`)d9+%st&h%zga*X=fY*&_C;l!@1R*OMgnQ37c4O4uedSF5Fv3GRcQ#`p3u`T>FyL`ltU79SaZ1a5Z%f`BmgDwngh?MAvx&xL%@_# z7rOni%=!5nanRYcAmUq+%;ox1ft2hOYUSxNW|YlOPZxTLhWc1)WF!TvlGEYnSM``w ztoa)?_@LxD7a*HM=K3o+T%keDD==LTomeOR-9!0lB#ca14Ab?*BlTg3QivA54^vEN z6!>KG&mhTl?oVU?eCxh1j$*qsk$)ae>mxvDo-E6itcKkMW4x?(Ow$t z19-a2$11qZcax?)0vl0AZ)HteP@(18@AVkX;lF z8u0`2^fT8-@(jT|hXrr!caY9nzI$>pGIK>4Q+FJ8&v#z;1g?&{Eizc~qh*dy(tO zP7;3u$VRwrrHt1ySyim^HVAc}rfTx-I!|JFp{tZ0Wb;Yj_X-398>RB%;#u6I?OcHw zNi&}B6>}HXiqh|oiw9xKDb<{*GM>Gt9fejl&V#`byCVp!b({OT=AZu#t__{cvG&rK z;M6kg+iTv{pFQkK+|+kryzHwR!2trRSpVYR#Rz>Z+bu^l5}tnPxmjpwqsYVPtfgaB z+eLKamd*Fvwmj}F&9ONBsFv(kSIIEC6%>fFE&x05Yf3VkYp<>#J1nVT`X(dPWd0U* zx$K3v2EVClC%d5v+z3f5epNB)*GI4!yN`{`DfyebT+DA@tot3e*WbW5#3K<@Jv=@; z(GX5(e@M<5|2?|ih!rR0*^1h**T1w9Y3`;ef|IHB@7F2Nc2bcu%=*rnWc6)!74>bU zOsMGFC=V7;O-c?9{#IrCIo!9=IR|_Vx^KtTmOFO(jjt2EX8;PKL+#mE(I%^rvq z#uKwY;(BdFa0(%wRGabGeh&YBPJ_hXxt|bJ z*=mP@VQ#rg43`HZ#~g!DDMVAOp~aO)@_qaWd7;@2u1bTzwSBjGg#W97%S%r@Dk+q# z0!#YWxj+LfO;Fz{VwZP$ZR#18aXWQ!Ar(hsUBsj|qPTlp{nKv~DZ~N8&(S zsOniNzBZw|nDy;}TJRdEqU%`Yd#;oncrcvvbz(;=aP6PNlPjX1{_-leu4ex$=jF7* z&T9$g*D+VFuIh%?*%{e(RYE>ivRmfOAeL^@aT`UeW~+OuT%yxxNx%e-PoF1n+!jXA zRDF7sg+2o4+_WMUuSONcfipL7M`7V*-$thmjzoFeA=a#S{G5GEWqOZLl9-)BoLK`gpDaH%`}K z^rM9CW6~ZDqUX<3=-Z*DPjY9fdTq=pneySL*l0zHJ5k+*Vd$4Umd?^#luJ?dB^3*C zlaJ)vcVZ!~`Xw0ezE3prWHE8_N#sHd{(5hhnp+5WmJy0X-xbX$lY?&zsA$FW4s{}v z5b{MwA~=^owQxwmm-wv5KN87VpLz?jErv{H{xtv{FfAeXH*`xODwzGI<-?a~x8tvX zizIPb=jWKXm}j_5^*wU61~e4w^zHyn6E|N1O%sb5 ztu0fR-U*#*nT8P>+wNSHU32O z<^rrk<)L^EZ3rrZXy{iG#l$~ic`{0uqn?VwlWa))f^D!q(#eX{QlK?+_at7 z>vB35h^IiMJ+<23^H3>&H{}%=@IMgY*#$*+A4Z8MK0P)>w|j3Qwe+^@&^3g!R<)c$ z)81SzD4gBKUNwolB6zh=DRp}|`>kxu->4ddOpI@H#>Z2@oQI8SKpPS+WG7kXM;p>F z0|JN8f_()A;{2`a5k05sL-LPHKgRz9E1G(=-lhdPm*0*C_Ik}gLyf^kJ5wLv%Dj|g zsGsx_BbFOQ{s0<9LO+~9NqtYt&GC2l@bMN_o>6+y7B&!m19LS*!fW2+~muyWYrGq%;HN<&LBBp?8wZy z5nxnGy5(3`;WtCaBLYyCAMVwO*D(crl5p_8VmTf{(8@?w_vc~%EV$~J)bAh#;GuEL za^D)Nr#ARU-`c-SF8L4Hy!I~O7{%vb{EvY>0kaZTjV#n4HHr)B#7`YiH(F@Ty0T7l zwefrJ@}T$j()nO zcCOI5q*9y)_f&*N6Bb?~uZB!w8$QK^49)B_))R8cgwYsI&0h34ce!;NF`O-2$FqGD*XKa$VplHgtJXlx@w(B?qXR|gRj zKSVxV+8pM(ryRSW>dd6aAgR9KrGLm^<-5t@;u#e9Pf(>YT7#WB11-0$BR&@3W@!~91TM2vTT5PiF z67hETHMT}Xz1c2$`D-f4q*3rJygLRp-q-I&O*1vvU-8fcmFq>m5G8v8wDCP$RDMn4 z_7Br#S1M2p_G=1$I(ihic%ZXE0pbN74={qMl;eT!9^6JRlsima<9ECFYJQb`21_335qn-iiIOK zW+G*%!u7nJU7QXoHQ1{8w9>bY3zn-7$KUknh{N9RR1VltO~xiUGhd>)UJBYqTGe;Z z!_A&p$t~Y}5*;d(VB9K`K4+sAZ3iw5Syi&cxg@)8{kK^FB;;a)a8rYbLu|{T>2Gm7 zR9!+Bu(gYBG;sxjxhg8zeh{BiDFA%=?#iz1Ch}p%p_z zgyXM(J#SAy0&GVE>7}%J#bK0bmrZj}JW1o)aE4~{a4|?C#UbMI(j6O}W5!i#SAI0X zMBfB@{B|#FzPd+DrAP`6S+BIG0n_!8_K&pPZE;pU%O0j+1lg2&D8ogBTC@uTLRK5x zWw$0-oPMM7dDunfNVR?s`Z=~7F>FY_jVmg-(a&8^%FU_iqIbRU^*#zlb`G1<%H9y>+Xq#$@rGs^+|HxU97mw!pL@0`qg`|GhcB*N((|lZP&xNq zwz6$TJ{ehYhs0TRPrxn-noB3@E%VlTCyKT9DadJbk&4#SDv4fZC@`-*tKa__=8{L} z7O03h>&3!*975kf+om|dF8}VG1Wf_zdXH(gRQq2|V8s+2$*w4Bt#1RvDM{lf;;JzN z%YgK}U6d8|sHb{r3;27NJly>8!0YJhLX{_$F#`T@Gq>%g1yyt3g69m`|Za} zVKB`z-_O1Lp-DzUE~O@y4a^HzqIDOkaPD?6gQZmDc7x&acGf8nzAoWqJ@XR+sB_#9 zYa2$TUAQ!9JfAD9$4!t*HqkQMuORv{TjQNqq(xTHws+lN+ z6FD$EW29#OBANVz9?^Nc0Q2Ig=1m+j@HHqDo|QbMylqDGDq+~#0b>EXT23w6`Tm;| z=W%YiN&ctl|A0rg+TteU!uMo%?YI+Mc_*N97nH^N=uiwm}OpX^mK)A zvv8;1yA-v0GN<(2nytX$Qz47kcE_gQY{|gj}-6 zyfA-qX?QrNT!I0^P3qW|C%5~W%J};1v~7AaX6JpeP6^Rb}inrFR^JNC`dk5K)1dQNRXB7nNQD1VRZA z6eUV%3M7ya0wOIT2`vdEBzXrNF>`0$eec6t>%Fz!{T2e8v(G+z|NH;9z1#g^NJW}e zgG6?bw>*Wdkpp`(5d2}j*WSspsqGa*lI~^i?uQTfmk+cIhETsCVICM?J>sE2=npEj z@<39|_yty{$2W@A4F!;;_3L8dk@x zMvReW$H&9WWd2Y!1$OCBykl$j4&VK(l*t1|Y9?$sgL$-ZBC#U?fNg@oojVe>_j zCX|#;X)%75k{jWW1TkzPCPVFgJ8Vk%tjMU2j14ictC=-u)wRRhli53)4Qm*s1m6xP z`Q7&qheBQ^7PmheYF+9*`+Y3`bfm;yzwKf>-fUX$ju4;-gMP}N+An#74;(yWRIA>X z+X(f&^EM~9QNt(Z4@0+_Iz8kg((N*8S%v&=-flv>xA890F+)Y?q{4@%yU^-p^9}Bc z)d!*8WgnU}jYoFVI(M5C=xy)DDqBH~QjK31mL);Tyjp{tx=t{iYUp)TmIWdwI2*ev z)3N*frVg9JxC65%t*?H!QNI4xCAHZYyyA(?DLG}XuPa$TOcyp%>cW*ru*dXny!0_1 z@Y`1_t-IJ=Rw#M?e)ysCGLkPrMzJUouR&@+9T+rymL>beP(7-6x}Sx5Lo}R6+^%X* z4zpNt+)zXcu)~9R-*0EW1CZcF`w*1XQGNT+{88d&Yl!-zE4>BV8w^=>sB8dgYJF4YzVFvxy{ybJv*^h@B?jKS#%Ji& z5ChVrRY=>KXuOzVZ?9V4!Bj-qr>_bVO=&+IrDIv{FSNS5D|C{PdCkm@tOcyTNAP@y zmb?QmxbL|#7>taI;^@s&ff8-z$;}}enpiso-~sN#0^ZtxvM!F9B5by7P}T_ zT0r`f!9HmJ#K`@~E9zPt&*u%7`Igw;O)Z zoTwM9ufv+_Yx3=<9p3G8DL5bwoyXRBwZeKNc?{bASJvafy?$=VwnZuKiMn%-26z;X zGdM9bE#77Zjn!ycVw-ETx5Ze zi_G-NvTLjB;~$a4wc*Nx^HQHDw6JKXbB1T7Zl0n$jc z$>{D?(#UYtdK7O|EgzB_vbsV4>4T9G;H*}xB{+&YOn))Q4Ae^^w>ikeqn zcXjhyAoL==$B(=!DgGlk{q^=P4M4=WA?fiyjb-(={~HuA75|$lz|r{Mn)=`FTMTp5q z#E_PANVc5$;C}MIFLBflT{`sO@~*Vc*-r{(ANv=n zoOvpFecQV0pZ6Z$tGE7^shb+=rN!CjcDegwo*PO+*Ss^(XnEy%P~G+JC294XBQe*i z7N9k|F8(Sf0Z(Ia#-qAu6gn%I3G37sGV}m`LQ%bMb|Z)Ag)zCnn%!5wGX7 zYx+jU<(xxC@=CK@Gh0*qL+AIvz4NhZU$*ib*8L@@ln&7Tpgk{$B5g;*?LRlrGJWm) z3D!UlFF9YIRX7?B=OpI`Cx$P-Od!mE&Pt@H+Y6~1oym^EcLWf#IJKbqhIgZTnUx-{ zLsEBXT&ZJsG73 zI6_OG>V>w%K5iOcw0DZ|;#`zr?$$FGWB9_^HAhRdh-ZAUAyL>scl&KcKBK!AOu#=* z?OPMQ4j@-5bidQI5SgxI>DtiQ&z3hl73N8?g%}o0TZ*~jf;G#Wsk;b&vB0qBYArReHTMjMm>))n!K$w ze!b0r_1L(Qp8v_)o*-8e$`PAzRJ!EAZ;spX3G$d}wj>OjU|)qvpv86jhmT1`!s$-6 zMz_(E4z901cN_I;4!5hc=KzKHgB8It!S&XPH*UHo=6&JTjNC#{EsIUzM00ldF*Nm znlX4+iQkG@>K{##5>kETF+o0#!~GKU87G5_hLvPD9PT z?|&+8@2ajW-LQH6J>)v6>Fj5H)189pwi2ZU6C0iFj|u*`pOS(0jVgOWMWdV{L6_Hq zeVRBJ27R6uHSw(Q^>oRbG*n9>PQ3=-+!jYb}rNf9z$W0wuhDO$^>uTxy9^sFzopuiv@OFe58Tl!mu_pz(yDe)dR}Uq zPLB9^LX6YY7YOwit} zxy=2dB)plYu9>bue`V1)(DoL7q(pwBgpy|9@jk}#q;Hp4Uyf~o#NohrF4Dm3r&;aj zF&nef;?9a;aOZRLPaotQ)sWWaeC)0;RCfuC6n?}(()k39eY~SJm}(48Thu1SuhjAx z7l?kf<=XI7%=iuRT}raHikZ5pmpiqeJM)nSfq?khC+Dlq)!uVvH5M4=fIFg$eCPUy z+^)uy8$wh`c#l5QhXb9p4*8(4AYY+>6C~n9=cZ2rmEI!th0re**y-_S>`0CK$jzo+ z>|52@Zs)o*+uKzL?bj8wL*!F1$&EeRy zz_ggsPBgK|d8s2Q-AIG<>N|w?sOheNOtm8IE^VYzeg@bc`uT*$D|?g_CrVPgM3_#(1f#usSQB^buxf*VRc1@a%MSXSfr6tZ+kDX-x)zqcfyalD*Jhn5I_>) zG5%#34{1w~_(i(AFN~WQ4dGROAe^RjySizGB|vH{DM{L65n$z+&XOMJ6&)H9^lV(` zva)wP#9$2s5r~mWg{037aT+lguQ8UUcv+OxdX#c@bF`>aG6@t{MeMZ_RqbYC6J6O% z21fY$42!Z%1O-tRdX<6*@L3k}2z<1V(mOWsRVkUiPn(2Tu3rGpDaGgS)n zX9^3pK=F)l-v9gBL%*&j$9+0sZoe48n||r+C7_JS9+&zK~Y^+x%bMgG~l>UCqhWb zSG34{Q5EHb9m&%+@op(g5a79Yz`!So1wB`FF9u+7SWjW=vHPDI+i3eHd*11O`k=0i z-aC)C3Cwbs*W8;)xFANz&^qhR_`UsB=d6UQbbSrS^xT5(v06}Yrx!dGCTnPs;l}51~tgJxRiR_MA@b>gJniO+Icz0nI^!`g9v(zbj)m#3u{Oi z>dT4CUVNFkJFsJS$wf|H;EvrW29mR7jPzDf|^+2H~t*3tFZQM@ksj`wJ<@ubuu zPZ*9+`fz6$PJu!77B=^maDzcignj6t9IGav*t4 z=~!HSBf|EKHU_c+S~&%qMW&SPG#tCfXEJI5L?Hqggyk!oA&yY>Noz!t4k6P3-tnrQ zRP=QC@N0X8#I)npVz1jB=qc<73+X1Ssjh?)dR%SE!=+b7+pNYTE(P+py~0;NfUPf? zFEdO)G^;0WQ8;2q|H1{2Pxj7)6p+~;n+=tZPtoqO zXJy?Mcs}{+-h5rr(TzJo*~o0Y-33|bK`0>fuRaLh{wj+idsi2LkvNHwG1V>K|1X45l9r%V0jOoSf}q>6M!kudimp zfska&&i+h;RQ+k1%Yvw|?N2*npxg1Fui_JYyWbm|T$-9r^|_ArI~}yb*F)M-si|of znR)xoN@QCGl3{UXp6-6@i{g&EyZ-1GJmXR$-fl)NrR8Y-<-&iQxljsgFMuFF=H`l-(7f>(imP6jB@# zjmjW~IKe(}oO=VC_A0afLTX*=^BO&&+6Z-Hmarj)pP`j3nG)BEDul?o@)etX2WQjM zJ*{*^pWo&RFtqI{ZKB`p^}8e{Aunx{w*bzuI^^(ryz-dD&)9^_ZJra0%e@c!GBBI1 z)eY5=+FrpBmhaG}u_B~04uF-+Sp9Gg0Snp#P>`lqhr&b_79fpB#b~>B0gpoZessV<-h!3$kYf6JESL_XI`5G^}L*HrT zzdSN9UX!0QD8D;Rii?_7qA%=Qc}6gRnyJ4lb3WzozY_VTe}(_=19d}QDPB@fd%-<3=*I}BL zIsPay@@@B&X64aQlgq=H_{Y3k<_h<@DHNu}Mr--2TK}g7d)eX0S9D8sspPYqdP}GH)p{hLq%2wdhy^)6wP>liPXD1J$8G`q%h+9h zzV6bM+vPr_5XwB3Tx^!GM>)t3#)$v!Y#1y_MSF@2uHg z;MLTA8cW6Vj@{^P7%m)|=wnu}xg;4!P87cco8MadhbhT@Pg@qt_BD&=H}s$-c)Ga3 zhaOm}6;>vBDo_o12rb$7pQdAq#B*2TO&edZ?lxJO$U%66g*&D8ga z8nU6@_0nw8TQ^Ohj}TajF`bYW-xyxCTpHr0<>_% z7nK@s_~4;M2>%#LTedZsl|`3HaG5Ih8GOz!X$uVEJd zU?Op_?vz<6=ThcNj1g%2Hwo*qf*cBuO8C(qduI6FdM9q9YDVTP#9+Q`R2>7v9(AJh z#?JG|g@*J47W}4^#v(K$jaKH#Sn-gG@jT@fhl!W|6G5h0J+yoGo{5-aEXC;tDXiB) z6U;_K5Q;d&spT)18%wQDl}u+AiMRN7_CE%>mcUQiJ2%T@o7;KVhFn^*;kbQ(kAkWH5U5Y znKxZv-BDRAiDbqK9o?q7vzimb!yNZ21(h80m$cM6)GcKQEWpiKzXq5uH8*u;OMS?{ zhkLTeVC0XbGy0%GLTIdsGp~pdvr%iyPdhbqR9Jb*EA$ap_0c?3jZvdOue!a0=M1{1h#GS~Nad=n9m&Y5h zQ-*}a$gQf04!*|HZ}T>-Pd1z=9!M|D*3_~*veg6)5qbjDJcNB~+1d3ir?F;d>KI|P z#n5Bs+wM&$<#JDH#-hj$FQ2YJO8z8V`BekqFi$Bj(d}4p#$b6cNK3guUeY7K^H8Rp zZx4)uWpmZT#^e2mrZd^KmhOHiERaw?c&#(@xEjc%1~ko~G3LVELoJ`DJ%3?2j&r&X;w`)?-=gj zorJGB?z?Hnay51Wy1vrU`T$L3*i8;?-!If<3>-a#0Rki;v`zMW2{iBH*={6rVY&E`p!ac{ zrHA~IpNi-&b>iH!tbThu^FjfkyFL71XmMrRgJ#Jaam2DNA154F$u*pH7_Q#e#asg62*3Twl zrYl=d1P$7pA|Z5k{>G+N18uiaQsaqRxbWdm`1wD*`ct-mc=-^nvq!{MxGL$bY*gxF z5o&kC^Qcz@wJ+r5v9xGARuR2o>_u>Ef;}`s|C!_VGNa6~I!{otp3l$*YM)iS$Hxu$ z9SObu=?;0>eThi!Bqh=%TNYer8ta~gca5Z>jRO&{eD1`vTLh9$JyvK~5kNS}9Pl;o z>cg)PPJ*|2x@AZ_j0IdrAL8+Ph&n=x-&B0O5}s8rtZbAw#0(YD>XOY#M{ya&2J5QX zW88#}pCPd(T`FEUv zI_TgXNa2ZNd@E-9LRbMsL#LW+iL1ZJ8jhI2cA5jH+Q$iZ4$~k+LD+^>JGVjQ0~g5c_1(dXN-oB^pEkh z`R`#*BdX5TqvEL%=%Wa0iP(TFK;_e!)Nq(J^(pgcnZdJ5yJ}24z%UfYjMxr)!m)OT zx1vC|Q5YpI?7fE32Yz+or`YB`W=?ak`NUNnEpq7Sv|i9B%m%H~KS92kIxv9H#GoZ< z924;XG!Gt!UYP0)-$Uzv8`~?9m1>VY^m@oEfdPp7-a#D} z?;!Twz5}Eh*2OLUSEewSZOaK{9CBUqvJ&P#4dygj#5$0k=Ah^LEX25Vx80baI3lFP znDkZFG#>*hRzjAjlRq|L^g5XW*%unFRms&bb6q73>0onypNUq)b}zXj4Y)XFvG_0P zmF;oU8umwX&I%1V#+hJyx<&b8w)8HmLjWNq_grs78PmyoUA*LnN_ZxA*ktq5fGp!A zs;^E~Rc3@TiF?*7CK$?u6oGJ6=857RCnOGSd-G+tl}ojeeWA{mtlPQH)Wr4IG3Us= z12>=d8ccYUm@YY1c0M!RS1uoDbSSwzXh@}R(E4z(c(MjQLy4KMxmTWMj+)s0r9nb% zUZDe|`DJl1g*8;NLQ8?wl~T|v(a@;Jx#y-c0#z&^aINf{l#FjgP`MX?_SuT4Kk>tS zK4mEOl^TR2HZjcDdPW`luEyPY1Rb1v&FGIq^MjR#Q@M)a!o?icy%5$jytuAJaAnq+ zz(c!=>UV70QeF2u5N7CLU&%fanf8gTJ<|S^o!@&Iw9TgXWkjISKsEA{9X74c3aJ^0 zsOIHoXhHJ^LFsBmGc`jpQ@&2%5{;~d!aI1i_b5SQ3dHaL)`DlqBMji#iW7wbOE|#; z4W9<)rOuZsCCP={gtf8gpqeXYy;A3t7mSA;5WI;G;dhEP8;Z1x+nbAB36559Ehr_C zrG&vrUl`xLg+J8BPlg>f7sGKgv$>@nQE^6m=7j`fk1lJB>ky!{z$4V8-BJP)hAqXg zWm@v0a7>^onuyY73PAN7_4(i{AK+cv##<`{w|;H&r+)XMfIOiO*0NYci+h_r|4OB@ zC0XnBHN_Pc*H3f(>8_w2g5S`2w;VIKxxDwJFWaX6sgxIO9m#z1^A~dzGIxmA!~ua)q*A5N?6+x)W9RQ69%w&8 zi+rEWiZmm~24FLXDrD%Ju%`$Q%5$WM%3lii0-H-4bjMiWm9!YD{GZ zdh1PG^+4PpXYWWzlt;I*!;ssoJrHms7A(s!iZTW++2oj#Q}2o~m`9&Hz`PsAIZJZy zOx2&jD1|kCP^*@rWgzcNd+y_y?2#m}?Z!ELv%U7bxMV}heMe?)4p$&XGxXw6ff(0oBsUh`O7!$J;w{AFg=Ol{@~oVNU^u~3U^Fyiix9h3Q(;$ad7^a z+V)>wQ6yBh-Er52`X*hJH-A0b;Ez&BS8(m<>}+(3_UwEl_@XpXb_PBah*2Qbvkc3 z`bt8VX zW9hWi)c5@B7z7@M;gG7oB|X{K08b&&y6@Q!Phq;E*=(k+bJNEhHaiC4{<$Mv&m44; zW0-_@p^k1RxuRyZVWp{AuAt}Oh&Q(dg2-o)*K_rkJEt@61%VHD7hVLwD)Pu2mmE-2 z-jJ;O{C^HY4&-;y}SF*_me%@jYr@b{s?Qg{Y@`|W1qb|vO*;Gy>S=jlqX3eS|E zLshsEhs&t@7wvLy;=?=BIgX7J$kKDU6`B(rRrfjFy8=K9@U92d6iOE58Vg;5Q3Xiz zV^FRW#}!Xf!`GlX)ezF5R+J+6ygT?<58JlTepEr-pwxd8`K}?XP?!gHj<_aZH7x6DdE6fhmjoE1fn{kYxyhZrmLlje52ib8V(m9>WYaweA-br4m^ z=SK$9tT7nGX6L;2TT~FRhZZws?=5Xb-^tAhN25xC5Ps8vr@J@qJo0lfEx?L_l-$WZ z>(9ct-*H>6OW1Mqi`2&J+_Tf(uJc`}p%9=E2cBlGfN8JZE!liz{~lsUjzM=%-;Y^XKW)8XrL<^?l_48CST zS6Bdq#tDh?J*uM|InY#O`Zbu^Sq~_)66dr0@69PI!nygkGwb(BeCQ1L5Z9dQpZs{( zl?3AAA7o~zBCulbrn9ZT3jM^&4C{RMQSO9W@PNkMUVI{Up5aA>zryj!2!`eTOrxT;j%Eons;+hc(Y2Q1e$_MR*F=+<_P z0yCt$u~JE~nYZkHH>%ZSmTLfiURtP|poP%#*Grk|^5}&yg*~T)Y7->}7K+(4g~5= zBte>(pV5%faEuB~z3XNljBfPWjd*L_2RZAUk7kEJ#V@`73my3iS_mgbQlSv$EkxS^ zVnVBqbHs)Wl%CAD`hdQ=K&K7^tNQX~9nn)Q#7)ee>p!LZa{M|n*J?_jjkS9Ca2{Wh z>c)L2^_y3$J?xDB*kYI>P7P!?;c%ad(Ajv)$vOW4w4k$B|7alN{1!ug0mPhV&cY_s z`73;udlg(m(tK!p_%H5gjKWk{8W0!R@4>@2nJlVXY%98X0ov%CCr~^KPaZTQxst~D zKJTkP2pX8!E39fVW%vGFMz@_bTLXRD8U=YY& zsewU+;=^$-fjc}JX5|kf6Bp?hzY_PU;9H2Zarla9ocV}tN#?i zI*;QHM;18gJ7ilE*Hk(Crb@vAy?N2X7g9YFtkaOOyFy3dZC$E zPOxUGhC=4KRqah_jrRJ6SQHZ%ENn<^hcq@Mz=obcP7x61G*;YJ{@wz1e&!KKrerkn zcvJ}!y4>KVP}4wvEWqy16^4>`V_#69uCR6{rouJ9{Y^=Ese8$|E}eDtc#4-L+{}z` zGu~9-YT`8@i0TgpQ3c*zJwupk*mPQ-pjMU344`hQ*6WSyEsZE0QcX41*^bID`vx%m z1}(Q94E+%w*xDZv9k~eWiP!3cInZ{!v6)GgvXDuVyv$_fk4OFK2T{eu*d{#O-_dd@m9~P26;3Kk-$wM+37+C~F;GiaLsJ2c2*Ca-G!6TBDha zdwQ6nIv9J(^|b@B@NpElb#S=%43!5fcNjqF>BbwK@i1ih3(2n*0FWH&)Zx!I_=&85e#L0X z^8^VC>(jw;KL>p+ncQLlw0J+487dcIauQK<&TS<0B##(4SVe$TxC% zD^+9&^tB(3%Rh|$_*tNBVeRD||2%Jh{=*h?V6L7K$OZp(uAc1$T5@i`lKk6~ize;j zfLaH=cA8JG$VmS_b|WB@XL9EHUus+b^B-=0g2sV&?%g+;5ao^wBskb+y}!lA z&iqo_*?oD?JU^mh(RAKGFW-Z2{{5Vc@~#y<+f?8B4RiNxRY&iOo+tCkbvO<^{``wx z@i)I)qH&y%l*Ds=SII`=clUm00cc!QMjjogpRM$b-LpY^baQ>a2}f)$dmO&oq$fOY z=btm?A_OLr2ak^z;blbM-Ek`ITG-M?WL<{h;tR~&0dh4#uLRBD($KUrJ+x3Klmg>G zm+Ck}UC?Y^dw$}Z&%DIXy;m<*%zk{<3E`$g*gZ2Ylx_Rgg_y=SXLP#@dY+r&J?yf3 zzn{G;_eXQxMFX}3m~+}RE+FZFNM#AoRQz)WCX%UQqz9)%$tm!6)|pdVq27xr3`{To zUAEAc6Qdd#Wi=)Ul-g5V|A}pQ+(E)UQ7XVRlkslMpaV_Ic#)r+lWv{|_1)fwVEYbj-r2IZYc7C(}$OFt6#YwckK%mb?j zt+#$GJSl`|8`Glg{=8Il%Ss(Jo~3CgvOl(*8=mBy2Q7B?wgXS#6G1szubkn`w#HQI zzy)b9W$RMh4QL$N%qD^s39)nK&Fd|h&QltCs^*KnBQ8&!1Y}f>qyMN#f07GxeJ3d9 zkHbryy~t(9LYzZ^S6SWotp?}dv4=S8R!sXWzdr6(zt*V*D5rO7XzgCA05GH!3{!JeW+kCX%^8(S*PKE^Olkd24?pB z%FMnr=`kr>o!PJFa)f-?EZW8glX>szYhH#HTxqUlj+V|kMl8T8kd@IuU#_Q3a1{K8xPpbLmM4ZbHWZK@=9@Gg5`r4W%lvSxKDF` z+>W3IF~tQv%UZOP^bF7MhiCkgg8sWSezwAjG|K(=e+mp2&UyvjGi(rO86!+&Y;a4sQ@2FSp!|khVu!+>H!=K z;KNh^e3+f#f<_F-dHV2G`><0srukY;WDpLA(yZI+^&)Bhsi( zRdSLL?hPZcw`p!EsQ?J;w02OIBZb{1ToUT1kEBHrKbhf>*M2$_ig>C=F7 zFn^{9MR;lJ)kt)>>6jQ?$~8Ac=Xhn6FQS`x5k-XqmDJ5K3#`zooWKM+;AT%|SVNoJ z9HIr#5BwfV@=w(8DvmQGJ>rfYX#gYi3{B0gFHi|rJ*GVr^<5Z8KUry!<@Q#uLmyBxdqAC9G^dcV(&lfHLfN+!m+ z)A5`cwtPUAWO86+TYp$!r+5TT@UP~QkLqmG3~uz2?4CP}?+gCtt^HxZ3-5IPx4cNH z;O*uq35(m7iKUL!N)okdqvcoD|IWWi!V6&bKslh-phlCVlyn~Iwbz|Dm6s2ted6}b z-cOb=;i>B>NAfmM_;-rmkgF`udck8SY`X0xe8HcqkvotWcV;eK(hu1l&bS|3QkV^K zT^kfr(r2P)S8dQ*m3mPLbZ#mU*7nRRcIIh4Rb^*^!S=%2@FcIwMHSCg^C=^*d8tRY zE>J|B08)Ek>!g8aaH^Bi%}XAJ1LD;Z9b0mHAGArDc@6GRunM)KJR-(MYh0~m6_3gs zjk}ZCE@@JD97%ltpz~?epH!Hjf+{SlrMK6#--1w2b&udC&pq29HrfDCVXkk1ta0RA zlTQiXv6$kE%6&V()ji)ywiF(6)}T>lL3qc8mhMtD{A`c z9PaMip7H5*`!DS!CCe7SJ#>TMrr%2{R31&-OXgfho>ab6I#YhG_W?%ib$As7#R6Bj zlj?K1*KdRnabee~Xa}&ev>-LWL8sNTQtY$j1Ee0p>nG1UJ)UT`n=N(>dWW$4D%p9y ziKc;_w*TGQ{U;9m^W$s*yCh{+Z6{L@;~aU(ejvCYx6xOsuetWS3X9wC5=&Kdgt&@W zDz}>-iy5kK%s=_)d9nMyt(q+<>d(%c7_nB+)6ujKXgl3$mswdNo4Vt@#cy__mw!&) zdm?cwtbJYLS1e2;vZ=k=7n?SFb5O%jGjZ0qJcD=OLRco3ACbo>=9b~B9z7Inbr&3v zbIfgEvuer$p1F?y+zabRT*olCj6_W*0!8rOB!EjcAj)|`8u5Q|a`xd zp+t&IQv1nLgWEP11c9>H(%s(%d8*gK7JCpPTDM21z_0x zO!(-Y+kDK^+nNhEYHOllkU5+`E{r-i{ z6eJPk8(*ye&$wNb5iqDyq8CsQXeyB`z3HHt@drKur9}#D6@K9PPU}6J6-?_0rd+zM zqvwlSIPyi-xUt56n>7;O?p~eE^^tM^q>qT$y9X^wFD|mvJ2}tU9KG>d<2SDG8dH#G zSce*P-d<#+3kc-vQI1>=gT)|pj~IWP!rQ|A$-5vvJpx?9<`H@$lwc;&B3s_7Ll3>pSo_{)iUI*JdOo+U0NZ&Gs-SYYoy zR|n<(D_b9p@?H;^+4E{kn`BP@ba?C&=qniJ$toD8N9rO;pT$dOu7F`a8I(~`ip~&R zC9vU3@B+JRviildBW8v3Y|eo-y7xZmi-k^qY-9;a2Y@`_=dBHzCtBUyjeI{x5?5{0 zQ!qg6PdTsCTm1(3(44xpV5_^zaWows*M| zDmY~o@`R+mr*89P5xpMeN@yzcaz`C}0KwV3w}u|M*%=Q1$rJAt>+E$MJDw#+?NJ_0KzoWg5!2w@vB(XtlW56{&j3~^{ZkRhX!t2uWF1;aNIb!D*5js;wHsA5b zPL_>}VwF1jeLPmo5#n{ z>F-f=xuO-ePxx58Nbk(og?TDaEOjIwyyAD^*KV6j;kX5Y2d;-%3*9q zu6CG8@DOavWLccKoTAp{ixl_U+aB1kJz9XY)t{8(tvx#28WpEO;8)))D z8}Oc>!5s+UdrHg6L45V;V)}|>-ZuLWF42UP?|V^?XiYSKClF6a-#pXAYbvL#qsKFv zfP=9r#|F8=;)sG+y-#BczJeZl6d2JPGIU-Ragr~-V-QTVb9r)1i5iPln z*~kghA$J*vFg9j)m3(UNXxw%P(Gykee=C;uj6PbyDC}u0T5%>nc@Sm)ubjz=GTm`b zVD?f_$duqJPoFrjc+9bW5H_w3KM?_2n&U=$hTM+b;QvzP#3fGeHXHkbHu!F8D=<{6 z)uFxyYW1ZUkN1c&M|sB{4kV>W|EvJZpLAYc%BJ)Dz@t2&;4>v8g^3Z~3Z^b8yE+H< z#@(ttI4~s7$kHZXZ38AD54f`B>Xk=UuFOo^BMP+Hu-C5;r_vWlmT8(guxt8 zOgzs_Hk{|-QE%(O}}ioG9?dJrliNiPU=TYBl%lQBSp&V z{RL4ZaCFPoQv^rAvJ<@VE}!Uf?ty)DUaDkp;~XiD5kQDzX?{DAWAZVDsX6r@DiN{iCy8;W74LZm5G^nOyl1`Zs~+j874NwYf8ok%_!2m3 z>nk9p7BSZ58YP|QgP2B-u)lP=<5tG*1B^ZUUQbn4!S{eeKRnut1OM&9ci&@06aft| z@7p_UpNB8qYNgh_?4P|AHjb-%$-L4L^3Ex0nD=Xq?(@czgOvlRUPC~5B;^4R9+3`C z!ym5xZffyVfihpAm-ju`qyhYe0L}Q@y2;T?M}XZ;eV&iM6WEo={tM=;!^%?>^*>+)l=FlYW#~D zfL{mr?WEP<*PB!R_NyQ7eogcgupp+aJXeL?swNcq0JJ-c^{qn>$BHi1zdd7f5TKlb zlr_HFd;d92-(>J{;E$pm{aQqgz`qU91;~s->yy&{@e<#4pWHJbNY#9-cI3Z~{Wx%t zu{QaN@qc|aTL4^8xQoo7-+vwZGe8ey`s5z`+mnlq|9{Kst3deQvI17g{|}axLxjty z#$V)~_b1WAJ?>uNRD^*GnKj$+mCOQk z>{L|yM3H0C>oW}hBPj&7Y~Iv|3?Ew%u?VA+)~~eJ8SAqxYk>G?p(%`7E1)!v6_Q^n z2n$A84Yd=aoOV2h`^GwCr57H{^E~JY*=3JNEw=Q1f)h{kjckFC0UtQR%2V^kB;A13 zLtbfBRF)U6bQul!dw~1&OM-sg@UQ-B(cE=+p+nX>+9%21BkTZlv<48Wjg3`5iKJ|r z>&1jrXTE`TOkr5N@18sX10!f;)C|p5Fw{Abf<=o$iRx<;9^~;pUyku+eBQz+BSKIc!8(fYP3Leo zZS2)R@MxQ)fCTQUhRE;$gnDXIr1rPEmapondF34254Z`1C}A$Ij0hhYN9Z^S7qSIz z9Mz~VoujPqSsrMyCw!o>2)S-UNYp4La(cEc4$i7caL$gAk#IkwE{Oq`rhNV85%f;I?R7I`0NiI)rUNN^>!^Nh`D?l`Xe>gyamW& zhRnQ}6z#1;tFEF>P(M&7sA@S$9(N+`dOp0pyW~@E;hK<-Mueycdbk25{c)g*eZY`z ztJVRekM-u8DS8O5xaQG^JOd<}j7?AOQ&`nT(H_8p@-SJ=a;&_DVIzr~7f8L2Wed%z zs(T*b+dr-Jtrj|lU1|*fWu`KbTN*pZe>D|%QJDPOnCLNsF_~&^K*IPni8@hES=NR_wpjYboQ<5lXIq= zO(n!FzwedgeC|%J7(Nc&7TdLPt2wxUESJKg0l5}1cO5t1;L(?H1-r3;m=@ zCv!PrqCJ%KTx}0E>T>9*?U8w%W_z7E*E>_a`m-}_d2vMJerH86#$ZR)ct)Mlwgl8# z4+dKEmeFlHju|(6y+uWac_Dp ziaBSOHdVX+sDxiDM*4eI^K^9c()naSsV zk}9m!TWfp{A(?$-rpUF@;o!~Ehnnh!q{qQ261K$#kp4bYO*>5-LeuAJnHXwLVQqd! z8G-WgnS{g~T6(bS>EO)NDRRtI^ihe1#RIC@y>mK4f+tU?9mPS$K>k0)-BLfW zYXa>o_$U=b5wuXk<%--dt}mP?p8f!XoH3uEcU2YBT@~#+OiIFc?BBB9(NYN~m-5)< zR_1l&2I*Kxk=)!%cpx9!)<4~IcGQ8v=`oA)c=(|_q8icU|~k2Q1q{B=XhOVpceuQUirEz`t*TzhtR!JByKAI zq9ExVx=N9Z1v0RUkw##*%!uC(UsR-Qv3$3eFt{B}xFJ2feL(3Rwh8_CN`DDU* z`Y|E}<-B&XA>vi3GeY@!M?v*Xc=1+tFkM_PZs_7)^TM@fr#X@y; z1r-sLUW2g8(m@ElDGNvmAP|t27!grvx=L><(n~-f^r8r;5RejDAQ6#H5?Tl(Bzgbn zDna()JRjco!+Xy9DQ6h}b7$tx@0PjuP6ZaWtfd-3t#s{nN9AbTisqM&ys4Q!~msg3M|*2vNVu!Jax`Vp;YRPHaAhebo2$X&ssuks_*rg z+r066v8Y=Ik->a)Geo~y0zA6q3Gk4{huBNv@nw8u|Gxt)ZMV5>O(QF0LS11%>TwcO zp{aj}obkD@W-MYSN`@FGoaphcEWi~qSJ}6ETdQR4gP3dL|FVPC668rzQpVUeEfLMV z2}^XkT$k@3ll37X{DKuZ77C`fr=Darp}LV)_!-j(27v^6MrIE^>+{t)Ho2=usNZ}z z&dAMl&@C*x;h8OWTsK2DuOUJ9Y;g^W5)CB*Wp)^ulaL3dc#u5Vn9M4FWDs;Z;M^1Q z6kwr+!{GCXAR~;}k-TJ1k>jtKrDt82_Ga|!h5A+l;UM!2n8ctScW0Rx!*Elj9I`FeT+?}}5f zD1<|}7B}6=l%h{)^=0Dd?-ThG(LIUX!Tk}O{b`Ky(_(>hy!A5n${3%BN$tdEu$pc{ z;LxhBw?78()FhH@L|As1{pwcBrAN`N3ssA!jiORGz1>vN_nVsEJ(K5&$=%W-r+B%uvOHEME?+E_EuU)Hj2c)dXi$mBf=q^2wNMQ01a36wZSFN52pF(z zC}R*Pw*@p4smI2&dcT%~C^D^a77_RQcUEE*hvhNI7ocM|`DyXx^!&oN?Fpw+tF54@ z*;-Ht^|7dAfQFj28D(NiFtX@RY10TMx=MOMvAIm2dU-}&5W1g*dJVwk68+^G3ubO+ zv$uNAhQ8;(X*_%&Z71hZ^yBO{qf&i@BLx5T^>?xf=^(<{#%!OeiO{MQovL@LcBo;b z()jQ;B-d1n%y5@Cy`2UpOP|Q0z0@}S%_=wf-K9dYZ2p~|(L13|_nm@<==oxkJ5`*b z?3uS}B&z3sBL#MHPpS}Uwv5H+eSBtVt`at~bpBo7ImJISRZm3}+p<1d8V{&tE;xe_ z7AV-?qlUOA&MA3%zazhJoLk0%;HQu0OMMV}L2?qi-EKBm^Iuo5AYA0U15U;U?5sQI z%)Pszk7|QF^v*8IrC7qy)vBaX8jpxqZ9%2J9gGX>C1MvQ`!xufpBDVf49TjS2n&^Y zwA08&yIXdC13GiciE`v#0z=D!$OkG&w`n>p6E#5Li7LlWm=-X%U`}Xpp$l9f!FTwaL`NRP z+VYIV;gjS#465bXiQ<+j=jU-l=e{O(k2>Q2uC3nHnG#MT)d%n*Z~X}J0Srlr6+@En zXlzY{b{Furb}GL4nW#YF@@xm{N$f51v92P$)4z?PH0(Z%^v4z(i7lc6v*ceO<#^$7~3Kau=LqAg#2MFvRa^3*5P z6+%v5I(tb3P%zXEFBBVFD9HKs0#^i`9VM(AQK|R=WZkn6Q?zQeeLfH1ck35$hdm20 z*|E_@CAbge2v{qO>tas#=GC6tO!V@51!cn?b;G;jdycPSo92P(_xQ8wKhn%r)-2zH zP35{H^tCdkQ*X87Kepo}aG%-T=5e0oIHsQzJ)E|{KO*3VmQ z?}MMBzZSMTG$<30F54@jVWiziF8`n`BS;a8RQlf4Sh?0a;{qb}9P3y@q9U3=ZAs2B&I9%xIp=_j>n!(hmf)Jt@3yWK0)s-7;u@Fj>>Rgdj(-wMnh6bV zHc0GlyH~WdeyS>f&hi^4mSqWm^LhnX8DGZ?Q!&d!ox8vqt*eI1ccO0s5R{ekG^Zbz&@{|7tF`*S37|-%4wAQ zd@NwIa!K)Yf9b;S+Fy8Bh}|!(hG2iSWRhk;UpVlFGYhwJpmuuw8i4(I8mPjhQ0e5t zawpdEtkxbx>m{Q2nSulLAxBGau43R4?J0KF&jS{CGaz1C`%iaVUtQN=LDAC&->L70whA+2s)L{I|~Ks@VJMohz#eyr=ne# z3u3!VNgQDY)tfZBNy$nme&T)f=@F#V$%lqcEVzu@t$;9%UH@#cY1(o9IT8s~M6h)JOWE z4GGLow~p&j#X8gbaQt&U3ojOeQ3`Yio#TDWk0C3ChV8Mip>Vwad&SkqmH;FUy+hI+ z2u(3$;eP4Oh+W_xn--IJ>kPX^s*nm{N+l+)*NL+<=Sw@EN0XB)fid>QqA~uUf#UE7 z9a&4O(fXe?gi9V8Rn6Kq((pg(AM+qdbs+^#P#RDPSW4u)#wM-^D@LRh>06O9z2E!& zR%-B;jP?9hvTOF>4itm~11Z7C6cW<7U7fv!$y6;@#>3t{J^f}o^PMy`B-{qTVFLS{#uQY}e@R5^} zU7+tN+bi78p#fYk7J-oSV+@0WLhjD^EMN>Pc;;LtJ0+Y- zhUItubx1sq>phA9`2yuWoDQE;o}JQBV`4!!#waOybE-sho{TQP6#UiPMm-jIv|f1v zcj|@^3!=)Mf9y6N%xsIeFi`hG7kWlht1h{6RHvN(c0F89u5rZ%^=Ku3iLUe~GWyCg z60=IihbtdvD@QT*6r%QVMim2$`0;#&td}K??nRp%w*m2J=*F1Ds=RPyE`qygo5w$$ z|5Q|QW>2Cw(XntM6m1?lSZUHA!CkahM<3K3V0p}Fl-F#8YPd;OWK(msdGZTuj~oKT zRcA-yrD47SFRQ+Vj^zHralGzxQ&P07Kpx$#CP2g%uHKh@vAdv{rvm8+ui%M0ZCP{5 zu&g?pM}O3bHoTgM06e(@xWg7W-F`+60&^GG{q;cQQ9y6fR?N@rxu4b4>;{ROo@L*9 zu*){;2I#OlFoBGfYAvs+*A0UV`IkQh>o|Lk`$fX;m|+Xe5ARZ(eG(A^cCG7R&$?(< zvd4ssO<3^-8hjg?@mbWTM7?J;#jt;StPP~O55GD@y#kU?r!h=@Vwo?dIQ z>3*SbAwVnB1{<&%9kyTkFo2aW%pHbSK^ySheKW)$AVym7y)3dtI-c^{NQ=uB?N&8- zpSzr&-U#yV#HsM}F27e*c-gdc&0bF?H#R+l3hkm8jdkqPcQT*6mxZ|+6>A&!cP!k; z--X+=#Y>zSs&E&@)_Zm{I8GzR@bsH$*;Km}L+;q6xY-Xn3^hnxQO-rpOnGq+_pwsj zz_vKsr6??3wzK~gq=IjgK#V58lh^pC=lA(c1_0lO>c#Xu81#ZK^DDXY5-FyDW`pD3 zqN#xNUBx;t;99SI(&$enE213vv$S^~SV%MRpg*6K=lmCSGtlkB&n;r;e_7E2a$IFpYG(EY<)>ssURMnoilq3!=v8_KGVA`ZP-YlQP6Dqh9!L$mq${+kWJzJqX{=A zD**3oB1IW&1M zP~b@)R|D=|RayR}I>lzT6t->)kAbmju=k#zEQf5nth~Owp{PTD{`o9_q>ALn>~ADp z=$^n7AKEYQNnO-<@Gmo)^XAw;lnn4bU`kmF8()xK#HUX+hzQUgkW6Utb$U$JdNyH&Hn2=X$GcSCIx>x0 zYQtNOh(cE}c)zg_Tw3Y_iv3`;Q}SGa&FZwFG!HDF>-Pu%fn-Qw!qp6CC{Zk%qBBlT2M z86Vh9A)>~kr0w@sN{A5h4x+$gB@dN~-*zk%AbXD#g4MUi6AI{eF4De4F}jUS>ts=` z#@OH}&h`a+^R$72Nl>RYIh}kgtJ@{Janug`$=C|0U@gU$b)lmMh4cyN9`NR89#F!C zoK=Az7R464fV=aK(D{zbX))=S43PuXA4$;*w~?zebzYhKUND43M*=i;f_GXtlo&2Y zES>N((09dipbQ>qH$xqESumNzl(auon<(uf{=BXNA zh0f#xRYrJFgbJh+6{lq~sUM{hFxmQAOAZ1`Z=P-@H|Gq@JLqkam2@hS$URXe&VQ36#9wMEP&UIDZtxxVt{nU}f^eN0h5({um%lI6!|{w20- zwJ=fe=IegVS%S#ehn!!9JG80bfNYag$R^XmKN{_xw8$B8KM2Rf%WjdpKSzB$tub1;E#8pzcG}g2Or;un&SK^dJzMFM}WwXXjfU)Guip}kHG~eUW=-{n9z-h z^Ah3Ps=NaCXodQ6dMtKvo3}t|b|W%eLQYc7Er)Tz8UC%lV8iP{^S5o!n&0QdqQ04q z*b#JE0=TCZ#9ua-Qb;wB+29A?_>*L%Fe0l4~WllA6yY^^|M!Qdv>M_<3Ns!1#$m#cmeBlB=7 z2^#RrUFZ(zuF4`+-YO=o~V35#I?Jx*l%&x z7Dr^{z5#zKS64EC6g4%UX(p>Zh@KP7;MrY#s5Gr;2sted7>U=q{kRK39^n(Jd#>F$ zN-Nb7woRl$q~%D9$I#i>a`1DmY1h1QWrgs_YxpaE z2Mb>c?MS2qAAkZyZX7i)3dQ$ZWw)f|Eu3G4uLLk~4o#}usjOMPh^=fpUtUvOe6dd? z!)L*}+c^5T+T9}m2uJ1WNxwKjB#xt92gWJz)k%@cb=ofQL{K4=;P4}KIIRs*06$IyxjA{t7q>zhtVP~f! zoUMCTUjx!6Tyo}^PV8J0zuypRp2 z!wf2*B3)F0C@50PA-js-Ebw($N1C0J&Ul3EDzVyTfVI+RRJ+7VZT?mSJO5d4*nCO1 zOVwW9Xoz#7&7}EUUxuzHE=6CcGHck+i zUV)@!v4WU3ZNz5KL>XfqD0x!@MCHiT?|uO)1S(eLdASo4gu12kz-jls*!bD50bH6_{dZ62r=$%>FTRJ`ZhA3D;B$vy};CU$QRi)*eWACMw@tmG|L zy#qk#@nfa8&(1-f8(>F-g?PBL3ePMb-}NOc(N;N8R$#Pgs`Tt;d$jM=c4drVUuJt( zrf7z8N~ej0Cc1zWfTtBod!$M@$l>>0#Ribe@1js)>~X^}l3AS;WEWL3sw_&z`)#Ey zi3yoc$JG5GMo;5Q|6A`LL{hu&H<;(hZ-N zF|!JVC)}plc9yP`QY8WfOfCCvoL|(mew|^k*o%y|IT}$w3^0#UYmhws2k)gnxjC)7&NuWQ;;gkbBk;?@o?<8DbUa#Q znF3g=;@o+`({wYkLR%8BQE_27m)1$!Jexd?Y8~lRX6a;a0|Gv)sJ1`;Z`hv5f>^W) z)CsFwYDQx{g+Jn0btb>IthzD(i7)KAuM||Cea{y=)q3gjs3i5)L5Sg*@<(*p#-w*W z2|u5J`Clh#j|z6`v4kf46n!i|wyz|#zi)E+w93vb!2Kg~P!qT(*4AUK;Lh@ifBq*T za3!JrtF6>c7J0h{Rs>x~VC{+tdH6JR z{mfZf{x$b=-5jlfh#csnzgdumV$ny#~z$Zj1F8}J!{JBgkKSTj-LH3H;_c~td zr}n*t;~IctkD~{kv*Osg&M`oJ;l!7(IM&UamHz*$Y*o5&LKU)xL8YY;O#@5BYuy&r z{fSMlEBx)YUb;&hvq#B$qa%kOhT6gR=-iv!llV?Sf^Z`-siTPTGujz&iJJlY2>IZ+ zCd?PObFHqZk@H@v<{d{<2mX8(rzBnA=)J@s&gy0OW>$>cv_+jZld@buyA3G;!3Wff zn>I-asQq!-pz<=MvuOA2HJSf_ra%B#xMVUL%6b@pyXUshk4;uF=wVI4>M^9hnl}Nu z4|v{#K_!+uwGx>eIB$G_1&j!!4GfGb&W)$!FEUc3$?ba1{^DsK@eI3oy10COyt|+d zAt}zwM0-u%KOi9welLxmFfb~gc<-?X3q&e?4&C)Hng3G<&C8kBC`aK8`)^VDCXp-2 zB{>u7*nMx@PRA%Hz6VzDiwoAlRODwYJ}}NDws=Ozz|-;KHR+#sYR%VY{@1R_{1=O8 zem`I!bGS~+hoB)x%yckvj|WN$JpYmSzT$KSV#AXXip5s1rGQg>k3GEFY92w-L^pTN zjiM+6F9pS)2c<8)uAxQ(G5*~===W9PG?Ux4Z+8XTmp=Yld#*G+&0-oYrzM9pT|Eif zD;)lJU(1is_ivH6hxSXu51&i>DyQE%>z#T*1&WsF<_V)TeWO1!T}ecn(?TTL9T0^$U}0k!8eOt!^A^iLchg(9 z{U#_;8hPzT=Ie*UJy%c#%AM-PpCe@d9{TtsYg_$QPR6MR>R~tj${6m+bjv?k=JM*D zl8w-%7Pti1JoJO?{g`3Q=e;z^jybu)R$O7Bn8#>KqQ^)=Y-^^qU5ii4+^kQFmPUaso)XV#Fm(1P*CDl5#Cd?DloUWZ+ zWz;Z$b-9PTjN2%DvHJ5cSpKnII*@Xu85~Tw3kganT1=$WU!T}uY^7Nn-K}0%vjXe=VGC|ljYAS zKvub^y<&x|mH)Dz4G&uuX@Z~3@hCF;TtZyDUA{cqbT5mLB(VBO%Gd*7Ql}23#K)S+SgR|F0Q395x3JtH3OsKKx)>fiaRM^M&NqjweRf%wjy5aJp-ULF#H` zF(UGCzB2T5`dTTGuYlO$nuTa?gwNsAtYDNH>EYxZwgS8dcJM9E*gf~ffQ_EjN6YNU z3D4!aW0(jd8!?AJB+G~^_IIkF6fhBwm@BM>5W#t`R2m$O^xTU4u-PfadXCCxn;`%a~Fd@lkt0Hv(UG?@6+V6 z(DG5X%RGE=KAW6t0~0+HCOs1{mdNFtyLULVvPGMIR#Nx|p~tss=Y^hUD<<$As2!%{ zT&?9~e#Ia7@=h<{=;Z6_?c!|cV}3j`O2Y8N%#YYjsxj0~pn{>!z8X~tlq>rBlvmgS z2~Kmk?kIln&4QcN7ypEiX^E1fqhB5C77NclpZ# zC|8M#CbbecC{i%h#fcvZ(aL7l%CqO_CI-KhWkrkpS8P7Ef8oNtx~>BR3USOm34Ax` z9W*l8v~V1D{c&SrV{~2J4t z@Zx=ZM()#;a9jCV^ZL>%Ip}fwp4DQPM})0|CSKYreJ>IEj}|+2r*_(~5IgREPyUvH zO7AQ^^U6)sIaN!&*e#v%L>|78Y4Ms<#a!D)z;D4d{!GK{@r zj`48l5Bajk_<(O+NCst7_6BF_3B0Y&5DPb#tRB5{o5{Fr%`-ZCu4_tn@AEm2XQYr> znd#Q*TNlEo`-w*|fpS0;=&kFHF;^cXK`P{w6;@5eeBdr};>%Pq>m@ZJa{qgTiNrJE zEtngakt`mcE4KGymfmkR;@CIjFZuEE^@Jw;kVOtwfibq*N}dqKgX6Gzf1RXaMV*~a zo%QCco376#hRphemN_RhZN9}3I%Lk^yMFUtK4wM zfeTdBebDKSe)^IL517(=s)7OAfHG`z5wTt29*>LlU?x>$yg~e7{zI&6k^Ky6y^# zo-BfBdRRe2%WypVM{7%`RA$0EE7>IOc9?rtA4y)SUQ7z?UQF6jr$+J(b8m7t7>Kgf zY*@wg=ecaS7kyo^Z6d4yQ>1jla1+jJDfnaLU2Lzi#K)0Yna>|dKA}n%-y(Z_z4{$L z9N7~{|EmWhoa@5(*;;~sid*l{Z?HOyq6}<*qplSFwHYcK(NiV-PPo_9;~T{p#C$K#FOkBEx1jrA`~)AdvpPwIwx zm%^5Wtb}!jBioj!uLG{0NG1So6P9Yj&mq8;qRpdU8ez%XL{pjCp5+kk>n;I3}vG48NlY>P@bUpi52s%~rWb(aD z*e7w&(cZHk|FAwb>VfTGZ}PGA>`zXvS*&(el~?6p*{V3e5|$jwSrhNzX}BW7n?F>m zX2g|DEBMU5i@v>YV!GJa=a{Nx;&FV_=bYQ1k#l@92`!s#FsJ>#M#c^rmA?6 zG@)r?!I5nv({hlZ#L^Xkek#6svt9pDGXe*{j}UH$szgx)~86w;8_R&RhyD)>>#M>Nem=V~}-gb&yz57bI1Eo+_Q5;F7HpFj$;E~M`H zI=3Vi+&tsAxbzTTCIWuy?<)T^1qkVEHMT6^Zq@JES7DpH*gO|In(vGDX4&DiL&g=e z6O+^qPVhgKn*Nb2gLU8X>#b7PFS5_{-2vkx)!ghS>H+XL zfDhoRbP1~l{Y^f^9eDuGERcn^OV(&n)cjTtY?MW9R85m&UBmY`SiVWX+K)S@Ciu@b z^y|QEJo-&P{p-}1bZ$ZleGPp$7rHZ)wLUZ|=GPoRup9jru1{InJk^Hp!_vI!g@x#&BzM&g0ao))7w_dfV1QXei6g;ynPMTb&9z!2RS}O z5xsyF)R%QlA2(%TAEiP7xa_KmSu)o)A+ECfo;A$OOHEPTmmX~;9}|A``p_l50$E~Y zw3VhRPTGJ2S%dO(sWa!SD?!SM_bE3@-CxC@L!42@diu&p)%h0i|7KzS8 zCOfh!=WA&FcCwU)fSqmlAi-$L2FE|UtOp-%>m^`w!CksIAd)cPVZ(P=(0q`}4J#|{mY5bP~5qW?EyyB8RmF13n zeYgMEYGr5tW2+VA0P+8nt!mqM2pP9Ek`Tw1&Olykt?(kRiW30c>j7V6jbjr|Va|Jdds|AQc9dV! zY5UZ$D(+10O7GeJCvf>|FnpE$sa!(eGTy9eEPC5!%L{TuiVkxj=qYsR5*#6hUmo#X zUMwgo(8I&otlVcroE z(0yrpN}p%AQvB90uD^xbC)0xG`<##^W!XQU6u^||rM{SCh0p8@Zc{xo57|{8 ziDq!65f$uDL8dOe?X(FLC5Nd5ceGkbKl4?U+LCqVX@vO&rptub-PDpKKbPMAeaVcr zcXohn&LdJYtL{6V8r>Mh3(o6n$>5IQQHX;r57qEP&suo41&!y3wet7~CntEqFNmQ> zkJ~3axsi9uAY<}Dxd?W#zQ~?Jbaw{P$LBnpCZ-Bo)GNFQ>aH@Q2`3Nx2=gk+E51tT zxj7SaUxhvUY4{FH4kE0;|Ne!FsiTc8gBh|X{|u|XDu)r30135%;G$Iv)Dt}qVb9=! zPAQIkjIx-(5Z}PjbA9pi@;X@xpm;O7)*rk+<}(UxB!>|N%^{iNV7QCi1h@BBuP(6K786SZ(|8heo7teYhnLbgUq-w$9%+Pc0Fls**{EpjP0MJWqoC< zBvS)^W%C}^`U@D&_ix$}7hhO$nSLmtg(_si{im5?0hK$zR({0M^g&)m?}%epAjm(n z62{XpfiN(d4%f|j)CiZFFR-mfnwm;09hFflT#5bVI?OTaY zhhGx?-*we5G5qtWWljMK^^WyMR&(lHK!NWr_H5Sr-Yhi#FuH-{NdkjsZGGHz z-6VjN5vRLg;a^458sb;t{k-5DVQU(Hod+TgP}L>gWJUcV4*wzN`^X3)pfgs0g0HUU z0hSgeEnu7DtikKTJXpV_L;-Zhfn=3atc~l&el7rPvsQlAhBcu6W2m?y@(WnIFwCg>#q0ou}B%AD^`1tjtu(TL&1#YX(HNU>N zp(qsvc{B7zWL;O&~cx97P} zt^WW^ix?nq?QeJ-Vrg2xo7upNIu=1Dl2`v{H2|T#YaU5R4Tj65f^K^wpVF=FYwtFQ zzJ4SOv3{DXEx;=wl8%|+)b(Sowm2Sq0FJlvb5)q!mb`cs6hQn?9OtW8-Y1`w>|YX1 z^sP*~HL%REEcamgW}$9fDVQMRxVI6=NfW926fcz#U^US$HK$T31i9iQbBZ`L^vF$$ z3Bs{rRF(A0lC4>TR~jLo=LWeOR15;!2d^FIxf;WT7%i>!sw!p3b+;T50C8QOdRXh# zC3~ONpN40{NTTmouV?#r2hNER9qd0G_{o_+35 zv93)RV=#5x>>6P0jWa;P4U*SylS)&QT^O*w`tWab5=nw=bn}kXSM;2ZjyC#SOLhk)(+9$=z#XTsD%b%GRMcyqGsp} z({kkPMn|6Et{O4+mj&u%WC*G&Br;Fx2t=gmdK2C@R&8hlwdc7qY8 z4~}sT;^>qkz+(!4r(LkSiEtC|?WfKx@4+nuMub}W-JYZQ+4jmF^A+r)*FJ@I@I;6@>07v{ipc6S&A+&8~%mzP8{YRf=lt(2i{k#Ni`aV>p*gFeK zl&^_2T}7yG^eKgG?c&HwuTZpdhL}0maI2OboC{}+XUlVQsocw?;6xt~7$fmcXSY|F zNidVlwqJL2c3Pg=Z5D~ck%5&zqzG_#ose~jkwv=3ZUZ@m0M?pq(rloaNDF& zB~{GbPUEqMgbeI+U886>;_*CbiZfy3hIgZ|%Z!nSoVi!<%&8hAr_i6H{ItEUbZ#M| zeU;)(XDR|(mu)KuFDSoEf^{6B-}nDEI0{4}b>!_zJgOyeI7~L3L*J(2i;hd4W|%1X zMc^qEH9#@dKIMyte)&m7dud?hI9!^|m@T?mqpk-sykNWbZh#ukoq(_o-x|hePa%C9 ziOFskD-k@M3%iVilVU{4>5!< zK0S3L%`WiGLSz=(-g$Gz9yx2`=JN`Y*2 zrw~X$2qq)&6nw-x$AyuUrGXbAR-{JbZh_UMC^R|Ki{)N!3IPnidi7rJh7ul9P*uTA zp}Tkz9pAg9e@6KH$Rj1_MWX3OChBADy}Dce5)&UVf5J8Rbz#d#V-sq(4&@QVx4Q$0 zH*kJKTg-&M^LopFd-;#}bjwFi(|O~EhuZBx9&dzP`}&EL7lHZ)F|N)_=}N`JhhDK< zdas=iz>ZC)Kh->sCem)o76cH--+VoUA4_kIG@DO?K`IW;Tx!>pU>OQV}(l4_T~!K>)JJ&1MHhMj;%F)zRC!gHU9Y*^d<=@@fxK| zhMBO`o-7KLyQ@RcMc6P?@ls8>hwIRsLJ5aqI^%0aRgq@4zKt^*Mt6RsN3II2WXSrP`E>I!gf?R=j&BL{ z@=?nQ{uOT88hr^w&IY^Vc;NQ9 z4h$LP3s!%Q2Ex5iK3?-1fx{+5(5@T{A2O64w4gM^GS1{7r6G8=WP`Z?(a@EE)i&eteEWuk6#%(*`U<$1&N#|r`fGC@GGtzQXl0&VyG*T4QaVM-c+^Y6dq`fs^b r)#U%U>mLO8?=b!Y9sj>$7n6Tc6526%x9N{fz(2Js+JEFJTi^XZFauyo literal 0 HcmV?d00001 diff --git "a/docs/\345\205\263\344\272\216IFoxCAD\347\232\204\346\236\266\346\236\204\350\257\264\346\230\216.md" "b/docs/\345\205\263\344\272\216IFoxCAD\347\232\204\346\236\266\346\236\204\350\257\264\346\230\216.md" index d828220..69a9b79 100644 --- "a/docs/\345\205\263\344\272\216IFoxCAD\347\232\204\346\236\266\346\236\204\350\257\264\346\230\216.md" +++ "b/docs/\345\205\263\344\272\216IFoxCAD\347\232\204\346\236\266\346\236\204\350\257\264\346\230\216.md" @@ -7,10 +7,10 @@ IFoxCAD是基于NFOX类库的重制版,主要是提供一个最小化的内核 ## 一、组织结构图 - IFoxCAD - - IFoxCAD.Cad - cad相关的类库 + - IFoxCAD.Basal - cad以外常用的类库 - LinqEx - linq扩展类 - LoopList - 环链表 - - IFoxCAD.Basal - cad以外常用的类库 + - IFoxCAD.Cad - cad相关的类库 - Runtime - 包含系统级别的功能 - AcadVersion - cad版本号类 - AssemInfo - 程序集信息 @@ -45,7 +45,7 @@ IFoxCAD是基于NFOX类库的重制版,主要是提供一个最小化的内核 ### 2.2 关于DBTrans类的具体构成元素的意义 -DBTrans类里基本的封装就是Transaction,然后是Document、Database、Editor、符号表、命名字典等,而抓这些其实都是cad二次开发关于图元操作经常打交道的概念。 +DBTrans类里基本的封装就是Transaction,然后是Document、Database、Editor、符号表、命名字典等,而这些其实都是cad二次开发关于图元操作经常打交道的概念。 DBTrans的每个实例都具有这些属性,而这些属性就对应于cad的相关类库,通过这些属性就可以对数据进行相应的操作。特别是符号表中最常用的就是块表,通过对块表的操作来实现添加图元等。 @@ -109,7 +109,7 @@ DBTrans的每个实例都具有这些属性,而这些属性就对应于cad的 - Has --- 判断符号表是否有符号表记录的函数 - 。。。 -特殊说明:当符号表为块表时,上述函数实际操作的是块定义、属性定义等。所以为了添加图元,需要特殊写法。 +特殊说明:当符号表为块表时,上述函数实际操作的是块定义、属性定义等。所以为了添加图元,需要特殊写法,原因在于cad的实体都是存在符号表记录里的,通常为模型这个块表记录。 # 慢慢完善,想到哪写到哪。。。 diff --git a/src/IFoxCAD.Basal/ArrayEx.cs b/src/IFoxCAD.Basal/ArrayEx.cs new file mode 100644 index 0000000..34fb4b7 --- /dev/null +++ b/src/IFoxCAD.Basal/ArrayEx.cs @@ -0,0 +1,50 @@ +namespace IFoxCAD.Basal +{ + /* + * 由于linq的函数大部分带有状态机,而cad是一个单机程序, + * 使用状态机会变得缓慢,因此我们设计的时候着重于时间优化, + * 本工具类在着重于数组遍历时候替代linq + */ + public static class ArrayEx + { + ///

+ /// 合并数组 + /// + /// + /// + public static T[] Combine2(this T[] a, T[] b) + { + var c = new T[a.Length + b.Length]; + Array.Copy(a, 0, c, 0, a.Length); + Array.Copy(b, 0, c, a.Length, b.Length); + return c; + } + + /// + /// 一维数组消重,此函数建议更改为: + /// set = new(); + /// foreach (var item in listInOut) + /// set.Add(item); + /// ]]> + /// + /// + /// 传入有重复成员的数组,传出没有重复的 + /// 传出参数1:数组开头;传出参数2:数组结尾;返回值比较结尾为就移除 + [Obsolete] + public static void Deduplication(List listInOut, Func func) + { + for (int i = 0; i < listInOut.Count; i++) + { + var first = listInOut[i]; + for (int j = listInOut.Count - 1; j > i; j--) + { + var last = listInOut[j]; + if (func(first, last)) + listInOut.RemoveAt(j); + } + } + } + + } +} diff --git a/src/IFoxCAD.Basal/CLS/Index.cs b/src/IFoxCAD.Basal/CLS/Index.cs new file mode 100644 index 0000000..97b51e5 --- /dev/null +++ b/src/IFoxCAD.Basal/CLS/Index.cs @@ -0,0 +1,148 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +namespace System; + +using System.Runtime.CompilerServices; + +/// Represent a type can be used to index a collection either from the start or the end. +/// +/// Index is used by the C# compiler to support the new index syntax +/// +/// int[] someArray = new int[5] { 1, 2, 3, 4, 5 } ; +/// int lastElement = someArray[^1]; // lastElement = 5 +/// +/// +public readonly struct Index : IEquatable +{ + private readonly int _value; + + /// Construct an Index using a value and indicating if the index is from the start or from the end. + /// The index value. it has to be zero or positive number. + /// Indicating if the index is from the start or from the end. + /// + /// If the Index constructed from the end, index value 1 means pointing at the last element and index value 0 means pointing at beyond last element. + /// +#if NET45 + [MethodImpl(MethodImplOptions.AggressiveInlining)] +#endif + public Index(int value, bool fromEnd = false) + { + if (value < 0) + { + throw new ArgumentOutOfRangeException(nameof(value), "value must be non-negative"); + } + + if (fromEnd) + _value = ~value; + else + _value = value; + } + + // The following private constructors mainly created for perf reason to avoid the checks + private Index(int value) + { + _value = value; + } + + /// Create an Index pointing at first element. + public static Index Start => new(0); + + /// Create an Index pointing at beyond last element. + public static Index End => new(~0); + + /// Create an Index from the start at the position indicated by the value. + /// The index value from the start. +#if NET45 + [MethodImpl(MethodImplOptions.AggressiveInlining)] +#endif + public static Index FromStart(int value) + { + if (value < 0) + { + throw new ArgumentOutOfRangeException(nameof(value), "value must be non-negative"); + } + + return new Index(value); + } + + /// Create an Index from the end at the position indicated by the value. + /// The index value from the end. +#if NET45 + [MethodImpl(MethodImplOptions.AggressiveInlining)] +#endif + public static Index FromEnd(int value) + { + if (value < 0) + { + throw new ArgumentOutOfRangeException(nameof(value), "value must be non-negative"); + } + + return new Index(~value); + } + + /// Returns the index value. + public int Value + { + get + { + if (_value < 0) + return ~_value; + else + return _value; + } + } + + /// Indicates whether the index is from the start or the end. + public bool IsFromEnd => _value < 0; + + /// Calculate the offset from the start using the giving collection length. + /// The length of the collection that the Index will be used with. length has to be a positive value + /// + /// For performance reason, we don't validate the input length parameter and the returned offset value against negative values. + /// we don't validate either the returned offset is greater than the input length. + /// It is expected Index will be used with collections which always have non negative length/count. If the returned offset is negative and + /// then used to index a collection will get out of range exception which will be same affect as the validation. + /// +#if NET45 + [MethodImpl(MethodImplOptions.AggressiveInlining)] +#endif + public int GetOffset(int length) + { + int offset = _value; + if (IsFromEnd) + { + // offset = length - (~value) + // offset = length + (~(~value) + 1) + // offset = length + value + 1 + + offset += length + 1; + } + return offset; + } + + /// Indicates whether the current Index object is equal to another object of the same type. + /// An object to compare with this object + public override bool Equals(object? value) => value is Index index && _value == index._value; + + /// Indicates whether the current Index object is equal to another Index object. + /// An object to compare with this object + public bool Equals(Index other) => _value == other._value; + + /// Returns the hash code for this instance. + public override int GetHashCode() => _value; + + /// Converts integer number to an Index. + public static implicit operator Index(int value) => FromStart(value); + + /// Converts the value of the current Index object to its equivalent string representation. + public override string ToString() + { + if (IsFromEnd) + return "^" + ((uint)Value).ToString(); + + return ((uint)Value).ToString(); + } +} + diff --git a/src/IFoxCAD.Basal/CLS/Range.cs b/src/IFoxCAD.Basal/CLS/Range.cs new file mode 100644 index 0000000..221167b --- /dev/null +++ b/src/IFoxCAD.Basal/CLS/Range.cs @@ -0,0 +1,103 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +namespace System; + +using System.Runtime.CompilerServices; + + +/// Represent a range has start and end indexes. +/// +/// Range is used by the C# compiler to support the range syntax. +/// +/// int[] someArray = new int[5] { 1, 2, 3, 4, 5 }; +/// int[] subArray1 = someArray[0..2]; // { 1, 2 } +/// int[] subArray2 = someArray[1..^0]; // { 2, 3, 4, 5 } +/// +/// +public readonly struct Range : IEquatable +{ + /// Represent the inclusive start index of the Range. + public Index Start { get; } + + /// Represent the exclusive end index of the Range. + public Index End { get; } + + /// Construct a Range object using the start and end indexes. + /// Represent the inclusive start index of the range. + /// Represent the exclusive end index of the range. + public Range(Index start, Index end) + { + Start = start; + End = end; + } + + /// Indicates whether the current Range object is equal to another object of the same type. + /// An object to compare with this object + public override bool Equals(object? value) => + value is Range r && + r.Start.Equals(Start) && + r.End.Equals(End); + + /// Indicates whether the current Range object is equal to another Range object. + /// An object to compare with this object + public bool Equals(Range other) => other.Start.Equals(Start) && other.End.Equals(End); + + /// Returns the hash code for this instance. + public override int GetHashCode() + { + return Start.GetHashCode() * 31 + End.GetHashCode(); + } + + /// Converts the value of the current Range object to its equivalent string representation. + public override string ToString() + { + return Start + ".." + End; + } + + /// Create a Range object starting from start index to the end of the collection. + public static Range StartAt(Index start) => new(start, Index.End); + + /// Create a Range object starting from first element in the collection to the end Index. + public static Range EndAt(Index end) => new(Index.Start, end); + + /// Create a Range object starting from first element to the end. + public static Range All => new(Index.Start, Index.End); + + /// Calculate the start offset and length of range object using a collection length. + /// The length of the collection that the range will be used with. length has to be a positive value. + /// + /// For performance reason, we don't validate the input length parameter against negative values. + /// It is expected Range will be used with collections which always have non negative length/count. + /// We validate the range is inside the length scope though. + /// +#if NET45 + [MethodImpl(MethodImplOptions.AggressiveInlining)] +#endif + //[CLSCompliant(false)] + public (int Offset, int Length) GetOffsetAndLength(int length) + { + int start; + Index startIndex = Start; + if (startIndex.IsFromEnd) + start = length - startIndex.Value; + else + start = startIndex.Value; + + int end; + Index endIndex = End; + if (endIndex.IsFromEnd) + end = length - endIndex.Value; + else + end = endIndex.Value; + + if ((uint)end > (uint)length || (uint)start > (uint)end) + { + throw new ArgumentOutOfRangeException(nameof(length)); + } + + return (start, end - start); + } +} + diff --git a/src/IFoxCAD.Basal/CLS/RuntimeHelpers.cs b/src/IFoxCAD.Basal/CLS/RuntimeHelpers.cs new file mode 100644 index 0000000..a446c59 --- /dev/null +++ b/src/IFoxCAD.Basal/CLS/RuntimeHelpers.cs @@ -0,0 +1,44 @@ +//#if NET35 +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +namespace System.Runtime.CompilerServices; + +public static class RuntimeHelpers +{ + /// + /// Slices the specified array using the specified range. + /// + public static T[] GetSubArray(T[] array, Range range) + { + if (array == null) + throw new ArgumentNullException(nameof(array)); + + (int offset, int length) = range.GetOffsetAndLength(array.Length); + + if (default(T)! != null || typeof(T[]) == array.GetType()) // TODO-NULLABLE: default(T) == null warning (https://github.com/dotnet/roslyn/issues/34757) + { + // We know the type of the array to be exactly T[]. + if (length == 0) + { + //return Array.Empty(); + return new T[0]; + } + + var dest = new T[length]; + Array.Copy(array, offset, dest, 0, length); + return dest; + } + else + { + // The array is actually a U[] where U:T. + T[] dest = (T[])Array.CreateInstance(array.GetType().GetElementType()!, length); + Array.Copy(array, offset, dest, 0, length); + return dest; + } + } + + +} +//#endif \ No newline at end of file diff --git a/src/IFoxCAD.Basal/CLS/TupleElementNamesAttribute.cs b/src/IFoxCAD.Basal/CLS/TupleElementNamesAttribute.cs new file mode 100644 index 0000000..69670b4 --- /dev/null +++ b/src/IFoxCAD.Basal/CLS/TupleElementNamesAttribute.cs @@ -0,0 +1,53 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using IFoxCAD.Basal; + +namespace System.Runtime.CompilerServices; + +/// +/// Indicates that the use of on a member is meant to be treated as a tuple with element names. +/// +[AttributeUsage(AttributeTargets.Field | AttributeTargets.Parameter | AttributeTargets.Property | AttributeTargets.ReturnValue | AttributeTargets.Class | AttributeTargets.Struct | AttributeTargets.Event)] +public sealed class TupleElementNamesAttribute : Attribute +{ + private readonly string[] _transformNames; + + /// + /// Initializes a new instance of the class. + /// + /// + /// Specifies, in a pre-order depth-first traversal of a type's + /// construction, which occurrences are + /// meant to carry element names. + /// + /// + /// This constructor is meant to be used on types that contain an + /// instantiation of that contains + /// element names. For instance, if C is a generic type with + /// two type parameters, then a use of the constructed type C{, might be intended to + /// treat the first type argument as a tuple with element names and the + /// second as a tuple without element names. In which case, the + /// appropriate attribute specification should use a + /// transformNames value of { "name1", "name2", null, null, + /// null }. + /// + public TupleElementNamesAttribute(string[] transformNames) + { + if (transformNames == null) + throw new ArgumentNullException(nameof(transformNames)); + + _transformNames = transformNames; + } + + /// + /// Specifies, in a pre-order depth-first traversal of a type's + /// construction, which elements are + /// meant to carry element names. + /// + public IList TransformNames => _transformNames; +} \ No newline at end of file diff --git a/src/IFoxCAD.Basal/CLS/ValueTuple.cs b/src/IFoxCAD.Basal/CLS/ValueTuple.cs new file mode 100644 index 0000000..bece1ed --- /dev/null +++ b/src/IFoxCAD.Basal/CLS/ValueTuple.cs @@ -0,0 +1,2144 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +//#pragma warning disable SA1141 // explicitly not using tuple syntax in tuple implementation + + +using System.Diagnostics; +using System.Numerics.Hashing; +/* + * 惊惊: + * 首先是因为有人想要编译的时候只形成一个dll,然后把元组塞入IFox,同时也补充了net35没有元组的遗憾. + * 而利用nuget元组包必然会形成依赖地狱. + * + * 如果你的工程使用了nuget元组包,就造成了必须要剔除IFox. + * + * 改IFox的元组命名空间倒是可以分离两者,但是 vs编译器 无法识别带其他命名空间的元组. + * 所以元组本身就是冲突的,需要把其他元组卸载掉,由IFox提供. + */ + +#if NET35 +namespace System.Collections +{ + public interface IStructuralComparable + { + int CompareTo(object? other, IComparer comparer); + } + public interface IStructuralEquatable + { + bool Equals(object? other, IEqualityComparer comparer); + int GetHashCode(IEqualityComparer comparer); + } +} +#endif + + + +namespace System.Numerics.Hashing +{ + internal static class HashHelpers + { + public static readonly int RandomSeed = Guid.NewGuid().GetHashCode(); + + public static int Combine(int h1, int h2) + { + unchecked + { + // RyuJIT optimizes this to use the ROL instruction + // Related GitHub pull request: dotnet/coreclr#1830 + + // RyuJIT 对此进行了优化以使用 ROL 指令 + // 相关 GitHub 拉取请求:dotnet/coreclr#1830 + uint rol5 = ((uint)h1 << 5) | ((uint)h1 >> 27); + return ((int)rol5 + h1) ^ h2; + } + } + } +} + + + + +namespace System +{ + //internal static class SR + internal sealed partial class SR + { + // public const string ArgumentException_ValueTupleIncorrectType = "The parameter should be a ValueTuple type of appropriate arity."; + // public const string ArgumentException_ValueTupleLastArgumentNotAValueTuple = "The TRest type argument of ValueTuple`8 must be a ValueTuple."; + public const string ArgumentException_ValueTupleIncorrectType = "该参数应该是适当数量的 ValueTuple 类型."; + public const string ArgumentException_ValueTupleLastArgumentNotAValueTuple = "ValueTuple`8 的 TREST 类型参数必须是 ValueTuple."; + } + + // Helper so we can call some tuple methods recursively without knowing the underlying types. + /// + /// 帮助器,因此我们可以在不知道底层类型的情况下递归调用一些元组方法. + /// + internal interface ITupleInternal + { + int GetHashCode(IEqualityComparer comparer); + int Size { get; } + string ToStringEnd(); + } + + + // The ValueTuple types (from arity 0 to 8) comprise the runtime implementation that underlies tuples in C# and struct tuples in F#. + // Aside from created via language syntax, they are most easily created via the ValueTuple.Create factory methods. + // The System.ValueTuple types differ from the System.Tuple types in that: + // - they are structs rather than classes, + // - they are mutable rather than readonly, and + // - their members (such as Item1, Item2, etc) are fields rather than properties. + /// + /// ValueTuple 类型(从 arity 0 到 8)包含运行时实现,它是 C# 中的元组和 F# 中的结构元组的基础. + /// 除了通过语言语法创建之外,它们最容易通过 ValueTuple.Create 工厂方法创建. + /// System.ValueTuple 类型与 System.Tuple 类型的不同之处在于: + /// - 它们是结构而不是类, + /// - 它们是可变的而不是只读的,并且 + /// - 它们的成员(例如 Item1、Item2 等)是字段而不是属性. + /// + public struct ValueTuple + : IEquatable, IStructuralEquatable, IStructuralComparable, IComparable, IComparable, ITupleInternal + { + /// + /// Returns a value that indicates whether the current instance is equal to a specified object. + /// + /// The object to compare with this instance. + /// if is a . + public override bool Equals(object obj) + { + return obj is ValueTuple; + } + + /// Returns a value indicating whether this instance is equal to a specified value. + /// An instance to compare to this instance. + /// true if has the same value as this instance; otherwise, false. + public bool Equals(ValueTuple other) + { + return true; + } + + bool IStructuralEquatable.Equals(object? other, IEqualityComparer comparer) + { + return other is ValueTuple; + } + + int IComparable.CompareTo(object other) + { + if (other == null) return 1; + + if (other is not ValueTuple) + { + throw new ArgumentException(SR.ArgumentException_ValueTupleIncorrectType, nameof(other)); + } + + return 0; + } + + /// Compares this instance to a specified instance and returns an indication of their relative values. + /// An instance to compare. + /// + /// A signed number indicating the relative values of this instance and . + /// Returns less than zero if this instance is less than , zero if this + /// instance is equal to , and greater than zero if this instance is greater + /// than . + /// + public int CompareTo(ValueTuple other) + { + return 0; + } + + int IStructuralComparable.CompareTo(object? other, IComparer comparer) + { + if (other == null) return 1; + + if (other is not ValueTuple) + { + throw new ArgumentException(SR.ArgumentException_ValueTupleIncorrectType, nameof(other)); + } + + return 0; + } + + /// Returns the hash code for this instance. + /// A 32-bit signed integer hash code. + public override int GetHashCode() + { + return 0; + } + + int IStructuralEquatable.GetHashCode(IEqualityComparer comparer) + { + return 0; + } + + int ITupleInternal.GetHashCode(IEqualityComparer comparer) + { + return 0; + } + + /// + /// Returns a string that represents the value of this instance. + /// + /// The string representation of this instance. + /// + /// The string returned by this method takes the form (). + /// + public override string ToString() + { + return "()"; + } + + string ITupleInternal.ToStringEnd() + { + return ")"; + } + + int ITupleInternal.Size => 0; + + /// Creates a new struct 0-tuple. + /// A 0-tuple. + public static ValueTuple Create() => new(); + + /// Creates a new struct 1-tuple, or singleton. + /// The type of the first component of the tuple. + /// The value of the first component of the tuple. + /// A 1-tuple (singleton) whose value is (item1). + public static ValueTuple Create(T1 item1) => new(item1); + + /// Creates a new struct 2-tuple, or pair. + /// The type of the first component of the tuple. + /// The type of the second component of the tuple. + /// The value of the first component of the tuple. + /// The value of the second component of the tuple. + /// A 2-tuple (pair) whose value is (item1, item2). + public static ValueTuple Create(T1 item1, T2 item2) => new(item1, item2); + + /// Creates a new struct 3-tuple, or triple. + /// The type of the first component of the tuple. + /// The type of the second component of the tuple. + /// The type of the third component of the tuple. + /// The value of the first component of the tuple. + /// The value of the second component of the tuple. + /// The value of the third component of the tuple. + /// A 3-tuple (triple) whose value is (item1, item2, item3). + public static ValueTuple Create(T1 item1, T2 item2, T3 item3) => + new(item1, item2, item3); + + /// Creates a new struct 4-tuple, or quadruple. + /// The type of the first component of the tuple. + /// The type of the second component of the tuple. + /// The type of the third component of the tuple. + /// The type of the fourth component of the tuple. + /// The value of the first component of the tuple. + /// The value of the second component of the tuple. + /// The value of the third component of the tuple. + /// The value of the fourth component of the tuple. + /// A 4-tuple (quadruple) whose value is (item1, item2, item3, item4). + public static ValueTuple Create(T1 item1, T2 item2, T3 item3, T4 item4) => + new(item1, item2, item3, item4); + + /// Creates a new struct 5-tuple, or quintuple. + /// The type of the first component of the tuple. + /// The type of the second component of the tuple. + /// The type of the third component of the tuple. + /// The type of the fourth component of the tuple. + /// The type of the fifth component of the tuple. + /// The value of the first component of the tuple. + /// The value of the second component of the tuple. + /// The value of the third component of the tuple. + /// The value of the fourth component of the tuple. + /// The value of the fifth component of the tuple. + /// A 5-tuple (quintuple) whose value is (item1, item2, item3, item4, item5). + public static ValueTuple Create(T1 item1, T2 item2, T3 item3, T4 item4, T5 item5) => + new(item1, item2, item3, item4, item5); + + /// Creates a new struct 6-tuple, or sextuple. + /// The type of the first component of the tuple. + /// The type of the second component of the tuple. + /// The type of the third component of the tuple. + /// The type of the fourth component of the tuple. + /// The type of the fifth component of the tuple. + /// The type of the sixth component of the tuple. + /// The value of the first component of the tuple. + /// The value of the second component of the tuple. + /// The value of the third component of the tuple. + /// The value of the fourth component of the tuple. + /// The value of the fifth component of the tuple. + /// The value of the sixth component of the tuple. + /// A 6-tuple (sextuple) whose value is (item1, item2, item3, item4, item5, item6). + public static ValueTuple Create(T1 item1, T2 item2, T3 item3, T4 item4, T5 item5, T6 item6) => + new(item1, item2, item3, item4, item5, item6); + + /// Creates a new struct 7-tuple, or septuple. + /// The type of the first component of the tuple. + /// The type of the second component of the tuple. + /// The type of the third component of the tuple. + /// The type of the fourth component of the tuple. + /// The type of the fifth component of the tuple. + /// The type of the sixth component of the tuple. + /// The type of the seventh component of the tuple. + /// The value of the first component of the tuple. + /// The value of the second component of the tuple. + /// The value of the third component of the tuple. + /// The value of the fourth component of the tuple. + /// The value of the fifth component of the tuple. + /// The value of the sixth component of the tuple. + /// The value of the seventh component of the tuple. + /// A 7-tuple (septuple) whose value is (item1, item2, item3, item4, item5, item6, item7). + public static ValueTuple Create(T1 item1, T2 item2, T3 item3, T4 item4, T5 item5, T6 item6, T7 item7) => + new(item1, item2, item3, item4, item5, item6, item7); + + /// Creates a new struct 8-tuple, or octuple. + /// The type of the first component of the tuple. + /// The type of the second component of the tuple. + /// The type of the third component of the tuple. + /// The type of the fourth component of the tuple. + /// The type of the fifth component of the tuple. + /// The type of the sixth component of the tuple. + /// The type of the seventh component of the tuple. + /// The type of the eighth component of the tuple. + /// The value of the first component of the tuple. + /// The value of the second component of the tuple. + /// The value of the third component of the tuple. + /// The value of the fourth component of the tuple. + /// The value of the fifth component of the tuple. + /// The value of the sixth component of the tuple. + /// The value of the seventh component of the tuple. + /// The value of the eighth component of the tuple. + /// An 8-tuple (octuple) whose value is (item1, item2, item3, item4, item5, item6, item7, item8). + public static ValueTuple> Create(T1 item1, T2 item2, T3 item3, T4 item4, T5 item5, T6 item6, T7 item7, T8 item8) => + new(item1, item2, item3, item4, item5, item6, item7, ValueTuple.Create(item8)); + + internal static int CombineHashCodes(int h1, int h2) + { + return HashHelpers.Combine(HashHelpers.Combine(HashHelpers.RandomSeed, h1), h2); + } + + internal static int CombineHashCodes(int h1, int h2, int h3) + { + return HashHelpers.Combine(CombineHashCodes(h1, h2), h3); + } + + internal static int CombineHashCodes(int h1, int h2, int h3, int h4) + { + return HashHelpers.Combine(CombineHashCodes(h1, h2, h3), h4); + } + + internal static int CombineHashCodes(int h1, int h2, int h3, int h4, int h5) + { + return HashHelpers.Combine(CombineHashCodes(h1, h2, h3, h4), h5); + } + + internal static int CombineHashCodes(int h1, int h2, int h3, int h4, int h5, int h6) + { + return HashHelpers.Combine(CombineHashCodes(h1, h2, h3, h4, h5), h6); + } + + internal static int CombineHashCodes(int h1, int h2, int h3, int h4, int h5, int h6, int h7) + { + return HashHelpers.Combine(CombineHashCodes(h1, h2, h3, h4, h5, h6), h7); + } + + internal static int CombineHashCodes(int h1, int h2, int h3, int h4, int h5, int h6, int h7, int h8) + { + return HashHelpers.Combine(CombineHashCodes(h1, h2, h3, h4, h5, h6, h7), h8); + } + } + + /// Represents a 1-tuple, or singleton, as a value type. + /// The type of the tuple's only component. + public struct ValueTuple + : IEquatable>, IStructuralEquatable, IStructuralComparable, IComparable, IComparable>, ITupleInternal + { + /// + /// The current instance's first component. + /// + public T1 Item1; + + /// + /// Initializes a new instance of the value type. + /// + /// The value of the tuple's first component. + public ValueTuple(T1 item1) + { + Item1 = item1; + } + + /// + /// Returns a value that indicates whether the current instance is equal to a specified object. + /// + /// The object to compare with this instance. + /// if the current instance is equal to the specified object; otherwise, . + /// + /// The parameter is considered to be equal to the current instance under the following conditions: + /// + /// It is a value type. + /// Its components are of the same types as those of the current instance. + /// Its components are equal to those of the current instance. Equality is determined by the default object equality comparer for each component. + /// + /// + public override bool Equals(object obj) + { + return obj is ValueTuple tuple && Equals(tuple); + } + + /// + /// Returns a value that indicates whether the current + /// instance is equal to a specified . + /// + /// The tuple to compare with this instance. + /// if the current instance is equal to the specified tuple; otherwise, . + /// + /// The parameter is considered to be equal to the current instance if each of its field + /// is equal to that of the current instance, using the default comparer for that field's type. + /// + public bool Equals(ValueTuple other) + { + return EqualityComparer.Default.Equals(Item1, other.Item1); + } + + bool IStructuralEquatable.Equals(object? other, IEqualityComparer comparer) + { + if (other == null || other is not ValueTuple) return false; + + var objTuple = (ValueTuple)other; + + return comparer.Equals(Item1, objTuple.Item1); + } + + int IComparable.CompareTo(object other) + { + if (other == null) return 1; + + if (other is not ValueTuple) + { + throw new ArgumentException(SR.ArgumentException_ValueTupleIncorrectType, nameof(other)); + } + + var objTuple = (ValueTuple)other; + + return Comparer.Default.Compare(Item1, objTuple.Item1); + } + + /// Compares this instance to a specified instance and returns an indication of their relative values. + /// An instance to compare. + /// + /// A signed number indicating the relative values of this instance and . + /// Returns less than zero if this instance is less than , zero if this + /// instance is equal to , and greater than zero if this instance is greater + /// than . + /// + public int CompareTo(ValueTuple other) + { + return Comparer.Default.Compare(Item1, other.Item1); + } + + int IStructuralComparable.CompareTo(object? other, IComparer comparer) + { + if (other == null) return 1; + + if (other is not ValueTuple) + { + throw new ArgumentException(SR.ArgumentException_ValueTupleIncorrectType, nameof(other)); + } + + var objTuple = (ValueTuple)other; + + return comparer.Compare(Item1, objTuple.Item1); + } + + /// + /// Returns the hash code for the current instance. + /// + /// A 32-bit signed integer hash code. + public override int GetHashCode() + { + return EqualityComparer.Default.GetHashCode(Item1); + } + + int IStructuralEquatable.GetHashCode(IEqualityComparer comparer) + { + return comparer.GetHashCode(Item1); + } + + int ITupleInternal.GetHashCode(IEqualityComparer comparer) + { + return comparer.GetHashCode(Item1); + } + + /// + /// Returns a string that represents the value of this instance. + /// + /// The string representation of this instance. + /// + /// The string returned by this method takes the form (Item1), + /// where Item1 represents the value of . If the field is , + /// it is represented as . + /// + public override string ToString() + { + return "(" + Item1?.ToString() + ")"; + } + + string ITupleInternal.ToStringEnd() + { + return Item1?.ToString() + ")"; + } + + int ITupleInternal.Size => 1; + } + + /// + /// Represents a 2-tuple, or pair, as a value type. + /// + /// The type of the tuple's first component. + /// The type of the tuple's second component. + public struct ValueTuple + : IEquatable>, IStructuralEquatable, IStructuralComparable, IComparable, IComparable>, ITupleInternal + { + /// + /// The current instance's first component. + /// + public T1 Item1; + + /// + /// The current instance's first component. + /// + public T2 Item2; + + /// + /// Initializes a new instance of the value type. + /// + /// The value of the tuple's first component. + /// The value of the tuple's second component. + public ValueTuple(T1 item1, T2 item2) + { + Item1 = item1; + Item2 = item2; + } + + /// + /// Returns a value that indicates whether the current instance is equal to a specified object. + /// + /// The object to compare with this instance. + /// if the current instance is equal to the specified object; otherwise, . + /// + /// + /// The parameter is considered to be equal to the current instance under the following conditions: + /// + /// It is a value type. + /// Its components are of the same types as those of the current instance. + /// Its components are equal to those of the current instance. Equality is determined by the default object equality comparer for each component. + /// + /// + public override bool Equals(object obj) + { + return obj is ValueTuple tuple && Equals(tuple); + } + + /// + /// Returns a value that indicates whether the current instance is equal to a specified . + /// + /// The tuple to compare with this instance. + /// if the current instance is equal to the specified tuple; otherwise, . + /// + /// The parameter is considered to be equal to the current instance if each of its fields + /// are equal to that of the current instance, using the default comparer for that field's type. + /// + public bool Equals(ValueTuple other) + { + return EqualityComparer.Default.Equals(Item1, other.Item1) + && EqualityComparer.Default.Equals(Item2, other.Item2); + } + + /// + /// Returns a value that indicates whether the current instance is equal to a specified object based on a specified comparison method. + /// + /// The object to compare with this instance. + /// An object that defines the method to use to evaluate whether the two objects are equal. + /// if the current instance is equal to the specified object; otherwise, . + /// + /// + /// This member is an explicit interface member implementation. It can be used only when the + /// instance is cast to an interface. + /// + /// The implementation is called only if other is not , + /// and if it can be successfully cast (in C#) or converted (in Visual Basic) to a + /// whose components are of the same types as those of the current instance. The IStructuralEquatable.Equals(Object, IEqualityComparer) method + /// first passes the values of the objects to be compared to the + /// implementation. If this method call returns , the method is + /// called again and passed the values of the two instances. + /// + bool IStructuralEquatable.Equals(object? other, IEqualityComparer comparer) + { + if (other is null or not ValueTuple) return false; + + var objTuple = (ValueTuple)other; + + return comparer.Equals(Item1, objTuple.Item1) + && comparer.Equals(Item2, objTuple.Item2); + } + + int IComparable.CompareTo(object other) + { + if (other == null) return 1; + + if (other is not ValueTuple) + { + throw new ArgumentException(SR.ArgumentException_ValueTupleIncorrectType, nameof(other)); + } + + return CompareTo((ValueTuple)other); + } + + /// Compares this instance to a specified instance and returns an indication of their relative values. + /// An instance to compare. + /// + /// A signed number indicating the relative values of this instance and . + /// Returns less than zero if this instance is less than , zero if this + /// instance is equal to , and greater than zero if this instance is greater + /// than . + /// + public int CompareTo(ValueTuple other) + { + int c = Comparer.Default.Compare(Item1, other.Item1); + if (c != 0) return c; + + return Comparer.Default.Compare(Item2, other.Item2); + } + + int IStructuralComparable.CompareTo(object? other, IComparer comparer) + { + if (other == null) return 1; + + if (other is not ValueTuple) + { + throw new ArgumentException(SR.ArgumentException_ValueTupleIncorrectType, nameof(other)); + } + + var objTuple = (ValueTuple)other; + + int c = comparer.Compare(Item1, objTuple.Item1); + if (c != 0) return c; + + return comparer.Compare(Item2, objTuple.Item2); + } + + /// + /// Returns the hash code for the current instance. + /// + /// A 32-bit signed integer hash code. + public override int GetHashCode() + { + return ValueTuple.CombineHashCodes(EqualityComparer.Default.GetHashCode(Item1), + EqualityComparer.Default.GetHashCode(Item2)); + } + + int IStructuralEquatable.GetHashCode(IEqualityComparer comparer) + { + return GetHashCodeCore(comparer); + } + + private int GetHashCodeCore(IEqualityComparer comparer) + { + return ValueTuple.CombineHashCodes(comparer.GetHashCode(Item1), + comparer.GetHashCode(Item2)); + } + + int ITupleInternal.GetHashCode(IEqualityComparer comparer) + { + return GetHashCodeCore(comparer); + } + + /// + /// Returns a string that represents the value of this instance. + /// + /// The string representation of this instance. + /// + /// The string returned by this method takes the form (Item1, Item2), + /// where Item1 and Item2 represent the values of the + /// and fields. If either field value is , + /// it is represented as . + /// + public override string ToString() + { + return "(" + Item1?.ToString() + ", " + Item2?.ToString() + ")"; + } + + string ITupleInternal.ToStringEnd() + { + return Item1?.ToString() + ", " + Item2?.ToString() + ")"; + } + + int ITupleInternal.Size => 2; + } + + /// + /// Represents a 3-tuple, or triple, as a value type. + /// + /// The type of the tuple's first component. + /// The type of the tuple's second component. + /// The type of the tuple's third component. + public struct ValueTuple + : IEquatable>, IStructuralEquatable, IStructuralComparable, IComparable, IComparable>, ITupleInternal + { + /// + /// The current instance's first component. + /// + public T1 Item1; + /// + /// The current instance's second component. + /// + public T2 Item2; + /// + /// The current instance's third component. + /// + public T3 Item3; + + /// + /// Initializes a new instance of the value type. + /// + /// The value of the tuple's first component. + /// The value of the tuple's second component. + /// The value of the tuple's third component. + public ValueTuple(T1 item1, T2 item2, T3 item3) + { + Item1 = item1; + Item2 = item2; + Item3 = item3; + } + + /// + /// Returns a value that indicates whether the current instance is equal to a specified object. + /// + /// The object to compare with this instance. + /// if the current instance is equal to the specified object; otherwise, . + /// + /// The parameter is considered to be equal to the current instance under the following conditions: + /// + /// It is a value type. + /// Its components are of the same types as those of the current instance. + /// Its components are equal to those of the current instance. Equality is determined by the default object equality comparer for each component. + /// + /// + public override bool Equals(object obj) + { + return obj is ValueTuple tuple && Equals(tuple); + } + + /// + /// Returns a value that indicates whether the current + /// instance is equal to a specified . + /// + /// The tuple to compare with this instance. + /// if the current instance is equal to the specified tuple; otherwise, . + /// + /// The parameter is considered to be equal to the current instance if each of its fields + /// are equal to that of the current instance, using the default comparer for that field's type. + /// + public bool Equals(ValueTuple other) + { + return EqualityComparer.Default.Equals(Item1, other.Item1) + && EqualityComparer.Default.Equals(Item2, other.Item2) + && EqualityComparer.Default.Equals(Item3, other.Item3); + } + + bool IStructuralEquatable.Equals(object? other, IEqualityComparer comparer) + { + if (other == null || other is not ValueTuple) return false; + + var objTuple = (ValueTuple)other; + + return comparer.Equals(Item1, objTuple.Item1) + && comparer.Equals(Item2, objTuple.Item2) + && comparer.Equals(Item3, objTuple.Item3); + } + + int IComparable.CompareTo(object other) + { + if (other == null) return 1; + + if (other is not ValueTuple) + { + throw new ArgumentException(SR.ArgumentException_ValueTupleIncorrectType, nameof(other)); + } + + return CompareTo((ValueTuple)other); + } + + /// Compares this instance to a specified instance and returns an indication of their relative values. + /// An instance to compare. + /// + /// A signed number indicating the relative values of this instance and . + /// Returns less than zero if this instance is less than , zero if this + /// instance is equal to , and greater than zero if this instance is greater + /// than . + /// + public int CompareTo(ValueTuple other) + { + int c = Comparer.Default.Compare(Item1, other.Item1); + if (c != 0) return c; + + c = Comparer.Default.Compare(Item2, other.Item2); + if (c != 0) return c; + + return Comparer.Default.Compare(Item3, other.Item3); + } + + int IStructuralComparable.CompareTo(object? other, IComparer comparer) + { + if (other == null) return 1; + + if (other is not ValueTuple) + { + throw new ArgumentException(SR.ArgumentException_ValueTupleIncorrectType, nameof(other)); + } + + var objTuple = (ValueTuple)other; + + int c = comparer.Compare(Item1, objTuple.Item1); + if (c != 0) return c; + + c = comparer.Compare(Item2, objTuple.Item2); + if (c != 0) return c; + + return comparer.Compare(Item3, objTuple.Item3); + } + + /// + /// Returns the hash code for the current instance. + /// + /// A 32-bit signed integer hash code. + public override int GetHashCode() + { + return ValueTuple.CombineHashCodes(EqualityComparer.Default.GetHashCode(Item1), + EqualityComparer.Default.GetHashCode(Item2), + EqualityComparer.Default.GetHashCode(Item3)); + } + + int IStructuralEquatable.GetHashCode(IEqualityComparer comparer) + { + return GetHashCodeCore(comparer); + } + + private int GetHashCodeCore(IEqualityComparer comparer) + { + return ValueTuple.CombineHashCodes(comparer.GetHashCode(Item1), + comparer.GetHashCode(Item2), + comparer.GetHashCode(Item3)); + } + + int ITupleInternal.GetHashCode(IEqualityComparer comparer) + { + return GetHashCodeCore(comparer); + } + + /// + /// Returns a string that represents the value of this instance. + /// + /// The string representation of this instance. + /// + /// The string returned by this method takes the form (Item1, Item2, Item3). + /// If any field value is , it is represented as . + /// + public override string ToString() + { + return "(" + Item1?.ToString() + ", " + Item2?.ToString() + ", " + Item3?.ToString() + ")"; + } + + string ITupleInternal.ToStringEnd() + { + return Item1?.ToString() + ", " + Item2?.ToString() + ", " + Item3?.ToString() + ")"; + } + + int ITupleInternal.Size => 3; + } + + /// + /// Represents a 4-tuple, or quadruple, as a value type. + /// + /// The type of the tuple's first component. + /// The type of the tuple's second component. + /// The type of the tuple's third component. + /// The type of the tuple's fourth component. + public struct ValueTuple + : IEquatable>, IStructuralEquatable, IStructuralComparable, IComparable, IComparable>, ITupleInternal + { + /// + /// The current instance's first component. + /// + public T1 Item1; + /// + /// The current instance's second component. + /// + public T2 Item2; + /// + /// The current instance's third component. + /// + public T3 Item3; + /// + /// The current instance's fourth component. + /// + public T4 Item4; + + /// + /// Initializes a new instance of the value type. + /// + /// The value of the tuple's first component. + /// The value of the tuple's second component. + /// The value of the tuple's third component. + /// The value of the tuple's fourth component. + public ValueTuple(T1 item1, T2 item2, T3 item3, T4 item4) + { + Item1 = item1; + Item2 = item2; + Item3 = item3; + Item4 = item4; + } + + /// + /// Returns a value that indicates whether the current instance is equal to a specified object. + /// + /// The object to compare with this instance. + /// if the current instance is equal to the specified object; otherwise, . + /// + /// The parameter is considered to be equal to the current instance under the following conditions: + /// + /// It is a value type. + /// Its components are of the same types as those of the current instance. + /// Its components are equal to those of the current instance. Equality is determined by the default object equality comparer for each component. + /// + /// + public override bool Equals(object obj) + { + return obj is ValueTuple tuple && Equals(tuple); + } + + /// + /// Returns a value that indicates whether the current + /// instance is equal to a specified . + /// + /// The tuple to compare with this instance. + /// if the current instance is equal to the specified tuple; otherwise, . + /// + /// The parameter is considered to be equal to the current instance if each of its fields + /// are equal to that of the current instance, using the default comparer for that field's type. + /// + public bool Equals(ValueTuple other) + { + return EqualityComparer.Default.Equals(Item1, other.Item1) + && EqualityComparer.Default.Equals(Item2, other.Item2) + && EqualityComparer.Default.Equals(Item3, other.Item3) + && EqualityComparer.Default.Equals(Item4, other.Item4); + } + + bool IStructuralEquatable.Equals(object? other, IEqualityComparer comparer) + { + if (other == null || other is not ValueTuple) return false; + + var objTuple = (ValueTuple)other; + + return comparer.Equals(Item1, objTuple.Item1) + && comparer.Equals(Item2, objTuple.Item2) + && comparer.Equals(Item3, objTuple.Item3) + && comparer.Equals(Item4, objTuple.Item4); + } + + int IComparable.CompareTo(object other) + { + if (other == null) return 1; + + if (other is not ValueTuple) + { + throw new ArgumentException(SR.ArgumentException_ValueTupleIncorrectType, nameof(other)); + } + + return CompareTo((ValueTuple)other); + } + + /// Compares this instance to a specified instance and returns an indication of their relative values. + /// An instance to compare. + /// + /// A signed number indicating the relative values of this instance and . + /// Returns less than zero if this instance is less than , zero if this + /// instance is equal to , and greater than zero if this instance is greater + /// than . + /// + public int CompareTo(ValueTuple other) + { + int c = Comparer.Default.Compare(Item1, other.Item1); + if (c != 0) return c; + + c = Comparer.Default.Compare(Item2, other.Item2); + if (c != 0) return c; + + c = Comparer.Default.Compare(Item3, other.Item3); + if (c != 0) return c; + + return Comparer.Default.Compare(Item4, other.Item4); + } + + int IStructuralComparable.CompareTo(object? other, IComparer comparer) + { + if (other == null) return 1; + + if (other is not ValueTuple) + { + throw new ArgumentException(SR.ArgumentException_ValueTupleIncorrectType, nameof(other)); + } + + var objTuple = (ValueTuple)other; + + int c = comparer.Compare(Item1, objTuple.Item1); + if (c != 0) return c; + + c = comparer.Compare(Item2, objTuple.Item2); + if (c != 0) return c; + + c = comparer.Compare(Item3, objTuple.Item3); + if (c != 0) return c; + + return comparer.Compare(Item4, objTuple.Item4); + } + + /// + /// Returns the hash code for the current instance. + /// + /// A 32-bit signed integer hash code. + public override int GetHashCode() + { + return ValueTuple.CombineHashCodes(EqualityComparer.Default.GetHashCode(Item1), + EqualityComparer.Default.GetHashCode(Item2), + EqualityComparer.Default.GetHashCode(Item3), + EqualityComparer.Default.GetHashCode(Item4)); + } + + int IStructuralEquatable.GetHashCode(IEqualityComparer comparer) + { + return GetHashCodeCore(comparer); + } + + private int GetHashCodeCore(IEqualityComparer comparer) + { + return ValueTuple.CombineHashCodes(comparer.GetHashCode(Item1), + comparer.GetHashCode(Item2), + comparer.GetHashCode(Item3), + comparer.GetHashCode(Item4)); + } + + int ITupleInternal.GetHashCode(IEqualityComparer comparer) + { + return GetHashCodeCore(comparer); + } + + /// + /// Returns a string that represents the value of this instance. + /// + /// The string representation of this instance. + /// + /// The string returned by this method takes the form (Item1, Item2, Item3, Item4). + /// If any field value is , it is represented as . + /// + public override string ToString() + { + return "(" + Item1?.ToString() + ", " + Item2?.ToString() + ", " + Item3?.ToString() + ", " + Item4?.ToString() + ")"; + } + + string ITupleInternal.ToStringEnd() + { + return Item1?.ToString() + ", " + Item2?.ToString() + ", " + Item3?.ToString() + ", " + Item4?.ToString() + ")"; + } + + int ITupleInternal.Size => 4; + } + + /// + /// Represents a 5-tuple, or quintuple, as a value type. + /// + /// The type of the tuple's first component. + /// The type of the tuple's second component. + /// The type of the tuple's third component. + /// The type of the tuple's fourth component. + /// The type of the tuple's fifth component. + public struct ValueTuple + : IEquatable>, IStructuralEquatable, IStructuralComparable, IComparable, IComparable>, ITupleInternal + { + /// + /// The current instance's first component. + /// + public T1 Item1; + /// + /// The current instance's second component. + /// + public T2 Item2; + /// + /// The current instance's third component. + /// + public T3 Item3; + /// + /// The current instance's fourth component. + /// + public T4 Item4; + /// + /// The current instance's fifth component. + /// + public T5 Item5; + + /// + /// Initializes a new instance of the value type. + /// + /// The value of the tuple's first component. + /// The value of the tuple's second component. + /// The value of the tuple's third component. + /// The value of the tuple's fourth component. + /// The value of the tuple's fifth component. + public ValueTuple(T1 item1, T2 item2, T3 item3, T4 item4, T5 item5) + { + Item1 = item1; + Item2 = item2; + Item3 = item3; + Item4 = item4; + Item5 = item5; + } + + /// + /// Returns a value that indicates whether the current instance is equal to a specified object. + /// + /// The object to compare with this instance. + /// if the current instance is equal to the specified object; otherwise, . + /// + /// The parameter is considered to be equal to the current instance under the following conditions: + /// + /// It is a value type. + /// Its components are of the same types as those of the current instance. + /// Its components are equal to those of the current instance. Equality is determined by the default object equality comparer for each component. + /// + /// + public override bool Equals(object obj) + { + return obj is ValueTuple tuple && Equals(tuple); + } + + /// + /// Returns a value that indicates whether the current + /// instance is equal to a specified . + /// + /// The tuple to compare with this instance. + /// if the current instance is equal to the specified tuple; otherwise, . + /// + /// The parameter is considered to be equal to the current instance if each of its fields + /// are equal to that of the current instance, using the default comparer for that field's type. + /// + public bool Equals(ValueTuple other) + { + return EqualityComparer.Default.Equals(Item1, other.Item1) + && EqualityComparer.Default.Equals(Item2, other.Item2) + && EqualityComparer.Default.Equals(Item3, other.Item3) + && EqualityComparer.Default.Equals(Item4, other.Item4) + && EqualityComparer.Default.Equals(Item5, other.Item5); + } + + bool IStructuralEquatable.Equals(object? other, IEqualityComparer comparer) + { + if (other == null || other is not ValueTuple) return false; + + var objTuple = (ValueTuple)other; + + return comparer.Equals(Item1, objTuple.Item1) + && comparer.Equals(Item2, objTuple.Item2) + && comparer.Equals(Item3, objTuple.Item3) + && comparer.Equals(Item4, objTuple.Item4) + && comparer.Equals(Item5, objTuple.Item5); + } + + int IComparable.CompareTo(object other) + { + if (other == null) return 1; + + if (other is not ValueTuple) + { + throw new ArgumentException(SR.ArgumentException_ValueTupleIncorrectType, nameof(other)); + } + + return CompareTo((ValueTuple)other); + } + + /// Compares this instance to a specified instance and returns an indication of their relative values. + /// An instance to compare. + /// + /// A signed number indicating the relative values of this instance and . + /// Returns less than zero if this instance is less than , zero if this + /// instance is equal to , and greater than zero if this instance is greater + /// than . + /// + public int CompareTo(ValueTuple other) + { + int c = Comparer.Default.Compare(Item1, other.Item1); + if (c != 0) return c; + + c = Comparer.Default.Compare(Item2, other.Item2); + if (c != 0) return c; + + c = Comparer.Default.Compare(Item3, other.Item3); + if (c != 0) return c; + + c = Comparer.Default.Compare(Item4, other.Item4); + if (c != 0) return c; + + return Comparer.Default.Compare(Item5, other.Item5); + } + + int IStructuralComparable.CompareTo(object? other, IComparer comparer) + { + if (other == null) return 1; + + if (other is not ValueTuple) + { + throw new ArgumentException(SR.ArgumentException_ValueTupleIncorrectType, nameof(other)); + } + + var objTuple = (ValueTuple)other; + + int c = comparer.Compare(Item1, objTuple.Item1); + if (c != 0) return c; + + c = comparer.Compare(Item2, objTuple.Item2); + if (c != 0) return c; + + c = comparer.Compare(Item3, objTuple.Item3); + if (c != 0) return c; + + c = comparer.Compare(Item4, objTuple.Item4); + if (c != 0) return c; + + return comparer.Compare(Item5, objTuple.Item5); + } + + /// + /// Returns the hash code for the current instance. + /// + /// A 32-bit signed integer hash code. + public override int GetHashCode() + { + return ValueTuple.CombineHashCodes(EqualityComparer.Default.GetHashCode(Item1), + EqualityComparer.Default.GetHashCode(Item2), + EqualityComparer.Default.GetHashCode(Item3), + EqualityComparer.Default.GetHashCode(Item4), + EqualityComparer.Default.GetHashCode(Item5)); + } + + int IStructuralEquatable.GetHashCode(IEqualityComparer comparer) + { + return GetHashCodeCore(comparer); + } + + private int GetHashCodeCore(IEqualityComparer comparer) + { + return ValueTuple.CombineHashCodes(comparer.GetHashCode(Item1), + comparer.GetHashCode(Item2), + comparer.GetHashCode(Item3), + comparer.GetHashCode(Item4), + comparer.GetHashCode(Item5)); + } + + int ITupleInternal.GetHashCode(IEqualityComparer comparer) + { + return GetHashCodeCore(comparer); + } + + /// + /// Returns a string that represents the value of this instance. + /// + /// The string representation of this instance. + /// + /// The string returned by this method takes the form (Item1, Item2, Item3, Item4, Item5). + /// If any field value is , it is represented as . + /// + public override string ToString() + { + return "(" + Item1?.ToString() + ", " + Item2?.ToString() + ", " + Item3?.ToString() + ", " + Item4?.ToString() + ", " + Item5?.ToString() + ")"; + } + + string ITupleInternal.ToStringEnd() + { + return Item1?.ToString() + ", " + Item2?.ToString() + ", " + Item3?.ToString() + ", " + Item4?.ToString() + ", " + Item5?.ToString() + ")"; + } + + int ITupleInternal.Size => 5; + } + + /// + /// Represents a 6-tuple, or sixtuple, as a value type. + /// + /// The type of the tuple's first component. + /// The type of the tuple's second component. + /// The type of the tuple's third component. + /// The type of the tuple's fourth component. + /// The type of the tuple's fifth component. + /// The type of the tuple's sixth component. + public struct ValueTuple + : IEquatable>, IStructuralEquatable, IStructuralComparable, IComparable, IComparable>, ITupleInternal + { + /// + /// The current instance's first component. + /// + public T1 Item1; + /// + /// The current instance's second component. + /// + public T2 Item2; + /// + /// The current instance's third component. + /// + public T3 Item3; + /// + /// The current instance's fourth component. + /// + public T4 Item4; + /// + /// The current instance's fifth component. + /// + public T5 Item5; + /// + /// The current instance's sixth component. + /// + public T6 Item6; + + /// + /// Initializes a new instance of the value type. + /// + /// The value of the tuple's first component. + /// The value of the tuple's second component. + /// The value of the tuple's third component. + /// The value of the tuple's fourth component. + /// The value of the tuple's fifth component. + /// The value of the tuple's sixth component. + public ValueTuple(T1 item1, T2 item2, T3 item3, T4 item4, T5 item5, T6 item6) + { + Item1 = item1; + Item2 = item2; + Item3 = item3; + Item4 = item4; + Item5 = item5; + Item6 = item6; + } + + /// + /// Returns a value that indicates whether the current instance is equal to a specified object. + /// + /// The object to compare with this instance. + /// if the current instance is equal to the specified object; otherwise, . + /// + /// The parameter is considered to be equal to the current instance under the following conditions: + /// + /// It is a value type. + /// Its components are of the same types as those of the current instance. + /// Its components are equal to those of the current instance. Equality is determined by the default object equality comparer for each component. + /// + /// + public override bool Equals(object obj) + { + return obj is ValueTuple tuple && Equals(tuple); + } + + /// + /// Returns a value that indicates whether the current + /// instance is equal to a specified . + /// + /// The tuple to compare with this instance. + /// if the current instance is equal to the specified tuple; otherwise, . + /// + /// The parameter is considered to be equal to the current instance if each of its fields + /// are equal to that of the current instance, using the default comparer for that field's type. + /// + public bool Equals(ValueTuple other) + { + return EqualityComparer.Default.Equals(Item1, other.Item1) + && EqualityComparer.Default.Equals(Item2, other.Item2) + && EqualityComparer.Default.Equals(Item3, other.Item3) + && EqualityComparer.Default.Equals(Item4, other.Item4) + && EqualityComparer.Default.Equals(Item5, other.Item5) + && EqualityComparer.Default.Equals(Item6, other.Item6); + } + + bool IStructuralEquatable.Equals(object? other, IEqualityComparer comparer) + { + if (other == null || other is not ValueTuple) return false; + + var objTuple = (ValueTuple)other; + + return comparer.Equals(Item1, objTuple.Item1) + && comparer.Equals(Item2, objTuple.Item2) + && comparer.Equals(Item3, objTuple.Item3) + && comparer.Equals(Item4, objTuple.Item4) + && comparer.Equals(Item5, objTuple.Item5) + && comparer.Equals(Item6, objTuple.Item6); + } + + int IComparable.CompareTo(object other) + { + if (other == null) return 1; + + if (other is not ValueTuple) + { + throw new ArgumentException(SR.ArgumentException_ValueTupleIncorrectType, nameof(other)); + } + + return CompareTo((ValueTuple)other); + } + + /// Compares this instance to a specified instance and returns an indication of their relative values. + /// An instance to compare. + /// + /// A signed number indicating the relative values of this instance and . + /// Returns less than zero if this instance is less than , zero if this + /// instance is equal to , and greater than zero if this instance is greater + /// than . + /// + public int CompareTo(ValueTuple other) + { + int c = Comparer.Default.Compare(Item1, other.Item1); + if (c != 0) return c; + + c = Comparer.Default.Compare(Item2, other.Item2); + if (c != 0) return c; + + c = Comparer.Default.Compare(Item3, other.Item3); + if (c != 0) return c; + + c = Comparer.Default.Compare(Item4, other.Item4); + if (c != 0) return c; + + c = Comparer.Default.Compare(Item5, other.Item5); + if (c != 0) return c; + + return Comparer.Default.Compare(Item6, other.Item6); + } + + int IStructuralComparable.CompareTo(object? other, IComparer comparer) + { + if (other == null) return 1; + + if (other is not ValueTuple) + { + throw new ArgumentException(SR.ArgumentException_ValueTupleIncorrectType, nameof(other)); + } + + var objTuple = (ValueTuple)other; + + int c = comparer.Compare(Item1, objTuple.Item1); + if (c != 0) return c; + + c = comparer.Compare(Item2, objTuple.Item2); + if (c != 0) return c; + + c = comparer.Compare(Item3, objTuple.Item3); + if (c != 0) return c; + + c = comparer.Compare(Item4, objTuple.Item4); + if (c != 0) return c; + + c = comparer.Compare(Item5, objTuple.Item5); + if (c != 0) return c; + + return comparer.Compare(Item6, objTuple.Item6); + } + + /// + /// Returns the hash code for the current instance. + /// + /// A 32-bit signed integer hash code. + public override int GetHashCode() + { + return ValueTuple.CombineHashCodes(EqualityComparer.Default.GetHashCode(Item1), + EqualityComparer.Default.GetHashCode(Item2), + EqualityComparer.Default.GetHashCode(Item3), + EqualityComparer.Default.GetHashCode(Item4), + EqualityComparer.Default.GetHashCode(Item5), + EqualityComparer.Default.GetHashCode(Item6)); + } + + int IStructuralEquatable.GetHashCode(IEqualityComparer comparer) + { + return GetHashCodeCore(comparer); + } + + private int GetHashCodeCore(IEqualityComparer comparer) + { + return ValueTuple.CombineHashCodes(comparer.GetHashCode(Item1), + comparer.GetHashCode(Item2), + comparer.GetHashCode(Item3), + comparer.GetHashCode(Item4), + comparer.GetHashCode(Item5), + comparer.GetHashCode(Item6)); + } + + int ITupleInternal.GetHashCode(IEqualityComparer comparer) + { + return GetHashCodeCore(comparer); + } + + /// + /// Returns a string that represents the value of this instance. + /// + /// The string representation of this instance. + /// + /// The string returned by this method takes the form (Item1, Item2, Item3, Item4, Item5, Item6). + /// If any field value is , it is represented as . + /// + public override string ToString() + { + return "(" + Item1?.ToString() + ", " + Item2?.ToString() + ", " + Item3?.ToString() + ", " + Item4?.ToString() + ", " + Item5?.ToString() + ", " + Item6?.ToString() + ")"; + } + + string ITupleInternal.ToStringEnd() + { + return Item1?.ToString() + ", " + Item2?.ToString() + ", " + Item3?.ToString() + ", " + Item4?.ToString() + ", " + Item5?.ToString() + ", " + Item6?.ToString() + ")"; + } + + int ITupleInternal.Size => 6; + } + + /// + /// Represents a 7-tuple, or sentuple, as a value type. + /// + /// The type of the tuple's first component. + /// The type of the tuple's second component. + /// The type of the tuple's third component. + /// The type of the tuple's fourth component. + /// The type of the tuple's fifth component. + /// The type of the tuple's sixth component. + /// The type of the tuple's seventh component. + public struct ValueTuple + : IEquatable>, IStructuralEquatable, IStructuralComparable, IComparable, IComparable>, ITupleInternal + { + /// + /// The current instance's first component. + /// + public T1 Item1; + /// + /// The current instance's second component. + /// + public T2 Item2; + /// + /// The current instance's third component. + /// + public T3 Item3; + /// + /// The current instance's fourth component. + /// + public T4 Item4; + /// + /// The current instance's fifth component. + /// + public T5 Item5; + /// + /// The current instance's sixth component. + /// + public T6 Item6; + /// + /// The current instance's seventh component. + /// + public T7 Item7; + + /// + /// Initializes a new instance of the value type. + /// + /// The value of the tuple's first component. + /// The value of the tuple's second component. + /// The value of the tuple's third component. + /// The value of the tuple's fourth component. + /// The value of the tuple's fifth component. + /// The value of the tuple's sixth component. + /// The value of the tuple's seventh component. + public ValueTuple(T1 item1, T2 item2, T3 item3, T4 item4, T5 item5, T6 item6, T7 item7) + { + Item1 = item1; + Item2 = item2; + Item3 = item3; + Item4 = item4; + Item5 = item5; + Item6 = item6; + Item7 = item7; + } + + /// + /// Returns a value that indicates whether the current instance is equal to a specified object. + /// + /// The object to compare with this instance. + /// if the current instance is equal to the specified object; otherwise, . + /// + /// The parameter is considered to be equal to the current instance under the following conditions: + /// + /// It is a value type. + /// Its components are of the same types as those of the current instance. + /// Its components are equal to those of the current instance. Equality is determined by the default object equality comparer for each component. + /// + /// + public override bool Equals(object obj) + { + return obj is ValueTuple tuple && Equals(tuple); + } + + /// + /// Returns a value that indicates whether the current + /// instance is equal to a specified . + /// + /// The tuple to compare with this instance. + /// if the current instance is equal to the specified tuple; otherwise, . + /// + /// The parameter is considered to be equal to the current instance if each of its fields + /// are equal to that of the current instance, using the default comparer for that field's type. + /// + public bool Equals(ValueTuple other) + { + return EqualityComparer.Default.Equals(Item1, other.Item1) + && EqualityComparer.Default.Equals(Item2, other.Item2) + && EqualityComparer.Default.Equals(Item3, other.Item3) + && EqualityComparer.Default.Equals(Item4, other.Item4) + && EqualityComparer.Default.Equals(Item5, other.Item5) + && EqualityComparer.Default.Equals(Item6, other.Item6) + && EqualityComparer.Default.Equals(Item7, other.Item7); + } + + bool IStructuralEquatable.Equals(object? other, IEqualityComparer comparer) + { + if (other == null || other is not ValueTuple) return false; + + var objTuple = (ValueTuple)other; + + return comparer.Equals(Item1, objTuple.Item1) + && comparer.Equals(Item2, objTuple.Item2) + && comparer.Equals(Item3, objTuple.Item3) + && comparer.Equals(Item4, objTuple.Item4) + && comparer.Equals(Item5, objTuple.Item5) + && comparer.Equals(Item6, objTuple.Item6) + && comparer.Equals(Item7, objTuple.Item7); + } + + int IComparable.CompareTo(object other) + { + if (other == null) return 1; + + if (other is not ValueTuple) + { + throw new ArgumentException(SR.ArgumentException_ValueTupleIncorrectType, nameof(other)); + } + + return CompareTo((ValueTuple)other); + } + + /// Compares this instance to a specified instance and returns an indication of their relative values. + /// An instance to compare. + /// + /// A signed number indicating the relative values of this instance and . + /// Returns less than zero if this instance is less than , zero if this + /// instance is equal to , and greater than zero if this instance is greater + /// than . + /// + public int CompareTo(ValueTuple other) + { + int c = Comparer.Default.Compare(Item1, other.Item1); + if (c != 0) return c; + + c = Comparer.Default.Compare(Item2, other.Item2); + if (c != 0) return c; + + c = Comparer.Default.Compare(Item3, other.Item3); + if (c != 0) return c; + + c = Comparer.Default.Compare(Item4, other.Item4); + if (c != 0) return c; + + c = Comparer.Default.Compare(Item5, other.Item5); + if (c != 0) return c; + + c = Comparer.Default.Compare(Item6, other.Item6); + if (c != 0) return c; + + return Comparer.Default.Compare(Item7, other.Item7); + } + + int IStructuralComparable.CompareTo(object? other, IComparer comparer) + { + if (other == null) return 1; + + if (other is not ValueTuple) + { + throw new ArgumentException(SR.ArgumentException_ValueTupleIncorrectType, nameof(other)); + } + + var objTuple = (ValueTuple)other; + + int c = comparer.Compare(Item1, objTuple.Item1); + if (c != 0) return c; + + c = comparer.Compare(Item2, objTuple.Item2); + if (c != 0) return c; + + c = comparer.Compare(Item3, objTuple.Item3); + if (c != 0) return c; + + c = comparer.Compare(Item4, objTuple.Item4); + if (c != 0) return c; + + c = comparer.Compare(Item5, objTuple.Item5); + if (c != 0) return c; + + c = comparer.Compare(Item6, objTuple.Item6); + if (c != 0) return c; + + return comparer.Compare(Item7, objTuple.Item7); + } + + /// + /// Returns the hash code for the current instance. + /// + /// A 32-bit signed integer hash code. + public override int GetHashCode() + { + return ValueTuple.CombineHashCodes(EqualityComparer.Default.GetHashCode(Item1), + EqualityComparer.Default.GetHashCode(Item2), + EqualityComparer.Default.GetHashCode(Item3), + EqualityComparer.Default.GetHashCode(Item4), + EqualityComparer.Default.GetHashCode(Item5), + EqualityComparer.Default.GetHashCode(Item6), + EqualityComparer.Default.GetHashCode(Item7)); + } + + int IStructuralEquatable.GetHashCode(IEqualityComparer comparer) + { + return GetHashCodeCore(comparer); + } + + private int GetHashCodeCore(IEqualityComparer comparer) + { + return ValueTuple.CombineHashCodes(comparer.GetHashCode(Item1), + comparer.GetHashCode(Item2), + comparer.GetHashCode(Item3), + comparer.GetHashCode(Item4), + comparer.GetHashCode(Item5), + comparer.GetHashCode(Item6), + comparer.GetHashCode(Item7)); + } + + int ITupleInternal.GetHashCode(IEqualityComparer comparer) + { + return GetHashCodeCore(comparer); + } + + /// + /// Returns a string that represents the value of this instance. + /// + /// The string representation of this instance. + /// + /// The string returned by this method takes the form (Item1, Item2, Item3, Item4, Item5, Item6, Item7). + /// If any field value is , it is represented as . + /// + public override string ToString() + { + return "(" + Item1?.ToString() + ", " + Item2?.ToString() + ", " + Item3?.ToString() + ", " + Item4?.ToString() + ", " + Item5?.ToString() + ", " + Item6?.ToString() + ", " + Item7?.ToString() + ")"; + } + + string ITupleInternal.ToStringEnd() + { + return Item1?.ToString() + ", " + Item2?.ToString() + ", " + Item3?.ToString() + ", " + Item4?.ToString() + ", " + Item5?.ToString() + ", " + Item6?.ToString() + ", " + Item7?.ToString() + ")"; + } + + int ITupleInternal.Size => 7; + } + + /// + /// Represents an 8-tuple, or octuple, as a value type. + /// + /// The type of the tuple's first component. + /// The type of the tuple's second component. + /// The type of the tuple's third component. + /// The type of the tuple's fourth component. + /// The type of the tuple's fifth component. + /// The type of the tuple's sixth component. + /// The type of the tuple's seventh component. + /// The type of the tuple's eighth component. + public struct ValueTuple + : IEquatable>, IStructuralEquatable, IStructuralComparable, IComparable, IComparable>, ITupleInternal + where TRest : struct + { + /// + /// The current instance's first component. + /// + public T1 Item1; + /// + /// The current instance's second component. + /// + public T2 Item2; + /// + /// The current instance's third component. + /// + public T3 Item3; + /// + /// The current instance's fourth component. + /// + public T4 Item4; + /// + /// The current instance's fifth component. + /// + public T5 Item5; + /// + /// The current instance's sixth component. + /// + public T6 Item6; + /// + /// The current instance's seventh component. + /// + public T7 Item7; + /// + /// The current instance's eighth component. + /// + public TRest Rest; + + /// + /// Initializes a new instance of the value type. + /// + /// The value of the tuple's first component. + /// The value of the tuple's second component. + /// The value of the tuple's third component. + /// The value of the tuple's fourth component. + /// The value of the tuple's fifth component. + /// The value of the tuple's sixth component. + /// The value of the tuple's seventh component. + /// The value of the tuple's eight component. + public ValueTuple(T1 item1, T2 item2, T3 item3, T4 item4, T5 item5, T6 item6, T7 item7, TRest rest) + { + if (rest is not ITupleInternal) + { + throw new ArgumentException(SR.ArgumentException_ValueTupleLastArgumentNotAValueTuple); + } + + Item1 = item1; + Item2 = item2; + Item3 = item3; + Item4 = item4; + Item5 = item5; + Item6 = item6; + Item7 = item7; + Rest = rest; + } + + /// + /// Returns a value that indicates whether the current instance is equal to a specified object. + /// + /// The object to compare with this instance. + /// if the current instance is equal to the specified object; otherwise, . + /// + /// The parameter is considered to be equal to the current instance under the following conditions: + /// + /// It is a value type. + /// Its components are of the same types as those of the current instance. + /// Its components are equal to those of the current instance. Equality is determined by the default object equality comparer for each component. + /// + /// + public override bool Equals(object obj) + { + return obj is ValueTuple tuple && Equals(tuple); + } + + /// + /// Returns a value that indicates whether the current + /// instance is equal to a specified . + /// + /// The tuple to compare with this instance. + /// if the current instance is equal to the specified tuple; otherwise, . + /// + /// The parameter is considered to be equal to the current instance if each of its fields + /// are equal to that of the current instance, using the default comparer for that field's type. + /// + public bool Equals(ValueTuple other) + { + return EqualityComparer.Default.Equals(Item1, other.Item1) + && EqualityComparer.Default.Equals(Item2, other.Item2) + && EqualityComparer.Default.Equals(Item3, other.Item3) + && EqualityComparer.Default.Equals(Item4, other.Item4) + && EqualityComparer.Default.Equals(Item5, other.Item5) + && EqualityComparer.Default.Equals(Item6, other.Item6) + && EqualityComparer.Default.Equals(Item7, other.Item7) + && EqualityComparer.Default.Equals(Rest, other.Rest); + } + + bool IStructuralEquatable.Equals(object? other, IEqualityComparer comparer) + { + if (other == null || other is not ValueTuple) return false; + + var objTuple = (ValueTuple)other; + + return comparer.Equals(Item1, objTuple.Item1) + && comparer.Equals(Item2, objTuple.Item2) + && comparer.Equals(Item3, objTuple.Item3) + && comparer.Equals(Item4, objTuple.Item4) + && comparer.Equals(Item5, objTuple.Item5) + && comparer.Equals(Item6, objTuple.Item6) + && comparer.Equals(Item7, objTuple.Item7) + && comparer.Equals(Rest, objTuple.Rest); + } + + int IComparable.CompareTo(object other) + { + if (other == null) return 1; + + if (other is not ValueTuple) + { + throw new ArgumentException(SR.ArgumentException_ValueTupleIncorrectType, nameof(other)); + } + + return CompareTo((ValueTuple)other); + } + + /// Compares this instance to a specified instance and returns an indication of their relative values. + /// An instance to compare. + /// + /// A signed number indicating the relative values of this instance and . + /// Returns less than zero if this instance is less than , zero if this + /// instance is equal to , and greater than zero if this instance is greater + /// than . + /// + public int CompareTo(ValueTuple other) + { + int c = Comparer.Default.Compare(Item1, other.Item1); + if (c != 0) return c; + + c = Comparer.Default.Compare(Item2, other.Item2); + if (c != 0) return c; + + c = Comparer.Default.Compare(Item3, other.Item3); + if (c != 0) return c; + + c = Comparer.Default.Compare(Item4, other.Item4); + if (c != 0) return c; + + c = Comparer.Default.Compare(Item5, other.Item5); + if (c != 0) return c; + + c = Comparer.Default.Compare(Item6, other.Item6); + if (c != 0) return c; + + c = Comparer.Default.Compare(Item7, other.Item7); + if (c != 0) return c; + + return Comparer.Default.Compare(Rest, other.Rest); + } + + int IStructuralComparable.CompareTo(object? other, IComparer comparer) + { + if (other == null) return 1; + + if (other is not ValueTuple) + { + throw new ArgumentException(SR.ArgumentException_ValueTupleIncorrectType, nameof(other)); + } + + var objTuple = (ValueTuple)other; + + int c = comparer.Compare(Item1, objTuple.Item1); + if (c != 0) return c; + + c = comparer.Compare(Item2, objTuple.Item2); + if (c != 0) return c; + + c = comparer.Compare(Item3, objTuple.Item3); + if (c != 0) return c; + + c = comparer.Compare(Item4, objTuple.Item4); + if (c != 0) return c; + + c = comparer.Compare(Item5, objTuple.Item5); + if (c != 0) return c; + + c = comparer.Compare(Item6, objTuple.Item6); + if (c != 0) return c; + + c = comparer.Compare(Item7, objTuple.Item7); + if (c != 0) return c; + + return comparer.Compare(Rest, objTuple.Rest); + } + + /// + /// Returns the hash code for the current instance. + /// + /// A 32-bit signed integer hash code. + public override int GetHashCode() + { + // We want to have a limited hash in this case. We'll use the last 8 elements of the tuple + if (Rest is not ITupleInternal rest) + { + return ValueTuple.CombineHashCodes(EqualityComparer.Default.GetHashCode(Item1), + EqualityComparer.Default.GetHashCode(Item2), + EqualityComparer.Default.GetHashCode(Item3), + EqualityComparer.Default.GetHashCode(Item4), + EqualityComparer.Default.GetHashCode(Item5), + EqualityComparer.Default.GetHashCode(Item6), + EqualityComparer.Default.GetHashCode(Item7)); + } + + int size = rest.Size; + if (size >= 8) { return rest.GetHashCode(); } + + // In this case, the rest member has less than 8 elements so we need to combine some our elements with the elements in rest + int k = 8 - size; + switch (k) + { + case 1: + return ValueTuple.CombineHashCodes(EqualityComparer.Default.GetHashCode(Item7), + rest.GetHashCode()); + case 2: + return ValueTuple.CombineHashCodes(EqualityComparer.Default.GetHashCode(Item6), + EqualityComparer.Default.GetHashCode(Item7), + rest.GetHashCode()); + case 3: + return ValueTuple.CombineHashCodes(EqualityComparer.Default.GetHashCode(Item5), + EqualityComparer.Default.GetHashCode(Item6), + EqualityComparer.Default.GetHashCode(Item7), + rest.GetHashCode()); + case 4: + return ValueTuple.CombineHashCodes(EqualityComparer.Default.GetHashCode(Item4), + EqualityComparer.Default.GetHashCode(Item5), + EqualityComparer.Default.GetHashCode(Item6), + EqualityComparer.Default.GetHashCode(Item7), + rest.GetHashCode()); + case 5: + return ValueTuple.CombineHashCodes(EqualityComparer.Default.GetHashCode(Item3), + EqualityComparer.Default.GetHashCode(Item4), + EqualityComparer.Default.GetHashCode(Item5), + EqualityComparer.Default.GetHashCode(Item6), + EqualityComparer.Default.GetHashCode(Item7), + rest.GetHashCode()); + case 6: + return ValueTuple.CombineHashCodes(EqualityComparer.Default.GetHashCode(Item2), + EqualityComparer.Default.GetHashCode(Item3), + EqualityComparer.Default.GetHashCode(Item4), + EqualityComparer.Default.GetHashCode(Item5), + EqualityComparer.Default.GetHashCode(Item6), + EqualityComparer.Default.GetHashCode(Item7), + rest.GetHashCode()); + case 7: + case 8: + return ValueTuple.CombineHashCodes(EqualityComparer.Default.GetHashCode(Item1), + EqualityComparer.Default.GetHashCode(Item2), + EqualityComparer.Default.GetHashCode(Item3), + EqualityComparer.Default.GetHashCode(Item4), + EqualityComparer.Default.GetHashCode(Item5), + EqualityComparer.Default.GetHashCode(Item6), + EqualityComparer.Default.GetHashCode(Item7), + rest.GetHashCode()); + } + + Debug.Assert(false, "Missed all cases for computing ValueTuple hash code"); + return -1; + } + + int IStructuralEquatable.GetHashCode(IEqualityComparer comparer) + { + return GetHashCodeCore(comparer); + } + + private int GetHashCodeCore(IEqualityComparer comparer) + { + // We want to have a limited hash in this case. We'll use the last 8 elements of the tuple + if (Rest is not ITupleInternal rest) + { + return ValueTuple.CombineHashCodes( + comparer.GetHashCode(Item1), + comparer.GetHashCode(Item2), + comparer.GetHashCode(Item3), + comparer.GetHashCode(Item4), + comparer.GetHashCode(Item5), + comparer.GetHashCode(Item6), + comparer.GetHashCode(Item7)); + } + + int size = rest.Size; + if (size >= 8) { return rest.GetHashCode(comparer); } + + // In this case, the rest member has less than 8 elements so we need to combine some our elements with the elements in rest + int k = 8 - size; + switch (k) + { + case 1: + return ValueTuple.CombineHashCodes(comparer.GetHashCode(Item7), rest.GetHashCode(comparer)); + case 2: + return ValueTuple.CombineHashCodes(comparer.GetHashCode(Item6), comparer.GetHashCode(Item7), rest.GetHashCode(comparer)); + case 3: + return ValueTuple.CombineHashCodes(comparer.GetHashCode(Item5), comparer.GetHashCode(Item6), comparer.GetHashCode(Item7), + rest.GetHashCode(comparer)); + case 4: + return ValueTuple.CombineHashCodes(comparer.GetHashCode(Item4), comparer.GetHashCode(Item5), comparer.GetHashCode(Item6), + comparer.GetHashCode(Item7), rest.GetHashCode(comparer)); + case 5: + return ValueTuple.CombineHashCodes(comparer.GetHashCode(Item3), comparer.GetHashCode(Item4), comparer.GetHashCode(Item5), + comparer.GetHashCode(Item6), comparer.GetHashCode(Item7), rest.GetHashCode(comparer)); + case 6: + return ValueTuple.CombineHashCodes(comparer.GetHashCode(Item2), comparer.GetHashCode(Item3), comparer.GetHashCode(Item4), + comparer.GetHashCode(Item5), comparer.GetHashCode(Item6), comparer.GetHashCode(Item7), + rest.GetHashCode(comparer)); + case 7: + case 8: + return ValueTuple.CombineHashCodes(comparer.GetHashCode(Item1), comparer.GetHashCode(Item2), comparer.GetHashCode(Item3), + comparer.GetHashCode(Item4), comparer.GetHashCode(Item5), comparer.GetHashCode(Item6), + comparer.GetHashCode(Item7), rest.GetHashCode(comparer)); + } + + Debug.Assert(false, "Missed all cases for computing ValueTuple hash code"); + return -1; + } + + int ITupleInternal.GetHashCode(IEqualityComparer comparer) + { + return GetHashCodeCore(comparer); + } + + /// + /// Returns a string that represents the value of this instance. + /// + /// The string representation of this instance. + /// + /// The string returned by this method takes the form (Item1, Item2, Item3, Item4, Item5, Item6, Item7, Rest). + /// If any field value is , it is represented as . + /// + public override string ToString() + { + if (Rest is not ITupleInternal rest) + { + return "(" + Item1?.ToString() + ", " + Item2?.ToString() + ", " + Item3?.ToString() + ", " + Item4?.ToString() + ", " + Item5?.ToString() + ", " + Item6?.ToString() + ", " + Item7?.ToString() + ", " + Rest.ToString() + ")"; + } + else + { + return "(" + Item1?.ToString() + ", " + Item2?.ToString() + ", " + Item3?.ToString() + ", " + Item4?.ToString() + ", " + Item5?.ToString() + ", " + Item6?.ToString() + ", " + Item7?.ToString() + ", " + rest.ToStringEnd(); + } + } + + string ITupleInternal.ToStringEnd() + { + if (Rest is not ITupleInternal rest) + { + return Item1?.ToString() + ", " + Item2?.ToString() + ", " + Item3?.ToString() + ", " + Item4?.ToString() + ", " + Item5?.ToString() + ", " + Item6?.ToString() + ", " + Item7?.ToString() + ", " + Rest.ToString() + ")"; + } + else + { + return Item1?.ToString() + ", " + Item2?.ToString() + ", " + Item3?.ToString() + ", " + Item4?.ToString() + ", " + Item5?.ToString() + ", " + Item6?.ToString() + ", " + Item7?.ToString() + ", " + rest.ToStringEnd(); + } + } + + int ITupleInternal.Size + { + get + { + //ITupleInternal? rest = Rest as ITupleInternal; + //return rest == null ? 8 : 7 + rest.Size; + return Rest is not ITupleInternal rest ? 8 : 7 + rest.Size; + } + } + } +} + + + + diff --git a/src/IFoxCAD.Basal/DictEx.cs b/src/IFoxCAD.Basal/DictEx.cs new file mode 100644 index 0000000..6fbccc4 --- /dev/null +++ b/src/IFoxCAD.Basal/DictEx.cs @@ -0,0 +1,21 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; + +namespace IFoxCAD.Basal +{ + public static class DictEx + { + //public static TKey? GetKey(this IDictionary dict!!, TKey key!!) + //{ + // if (dict.ContainsKey(key)) + // { + // foreach (var item in dict.Keys) + // if (key.Equals(item)) + // return item; + // } + // return default; + //} + } +} diff --git a/src/IFoxCAD.Basal/GlobalUsings.cs b/src/IFoxCAD.Basal/GlobalUsings.cs new file mode 100644 index 0000000..15c8a97 --- /dev/null +++ b/src/IFoxCAD.Basal/GlobalUsings.cs @@ -0,0 +1,9 @@ +/// 系统引用 +global using System; +global using System.Collections; +global using System.Collections.Generic; +global using System.IO; +global using System.Linq; +global using System.Text; +global using System.Reflection; + diff --git a/src/IFoxCAD.Basal/IFoxCAD.Basal.csproj b/src/IFoxCAD.Basal/IFoxCAD.Basal.csproj index a15060d..73d940d 100644 --- a/src/IFoxCAD.Basal/IFoxCAD.Basal.csproj +++ b/src/IFoxCAD.Basal/IFoxCAD.Basal.csproj @@ -1,45 +1,34 @@ - - - - preview - enable - - - 1.0.0.* - 1.0.0.0 - False - net35;net40 - 0.1.1 - - true - true - InspireFunction - xsfhlzh;vicwjb - 基于.NET的二次开发基本类库 - InspireFunction - git - https://gitee.com/inspirefunction/ifoxcad.git - - https://gitee.com/inspirefunction/ifoxcad - IFoxCAD;C#;NET;Common;Basal - 增加在net35支持 - true - true - - LICENSE - true - - - - - - - - - True - - - - + + + + preview + enable + + net35;net40;net45 + true + 0.3.5.1 + InspireFunction + xsfhlzh;vicwjb + 基于.NET的二次开发基本类库 + InspireFunction + https://gitee.com/inspirefunction/ifoxcad + https://gitee.com/inspirefunction/ifoxcad.git + git + IFoxCAD;C#;NET;Common;Basal + 直接集成元组和索引切片. + true + true + LICENSE + true + True + none + + + + + True + + + diff --git a/src/IFoxCAD.Basal/LinkedHashMap.cs b/src/IFoxCAD.Basal/LinkedHashMap.cs new file mode 100644 index 0000000..0e6232c --- /dev/null +++ b/src/IFoxCAD.Basal/LinkedHashMap.cs @@ -0,0 +1,171 @@ +namespace IFoxCAD.Basal; + + +/// +/// A least-recently-used cache stored like a dictionary. +/// +/// +/// The type of the key to the cached item +/// +/// +/// The type of the cached item. +/// +/// +/// Derived from https://stackoverflow.com/a/3719378/240845 +/// https://stackoverflow.com/users/240845/mheyman +/// +public class LinkedHashMap +{ + private readonly Dictionary> cacheMap = new(); + + private readonly LinkedList lruList = new(); + + private readonly Action? dispose; + + /// + /// Initializes a new instance of the + /// class. + /// + /// + /// Maximum number of elements to cache. + /// + /// + /// When elements cycle out of the cache, disposes them. May be null. + /// + public LinkedHashMap(int capacity, Action? dispose = null) + { + this.Capacity = capacity; + this.dispose = dispose; + } + + /// + /// Gets the capacity of the cache. + /// + public int Capacity { get; } + + /// Gets the value associated with the specified key. + /// + /// The key of the value to get. + /// + /// + /// When this method returns, contains the value associated with the specified + /// key, if the key is found; otherwise, the default value for the type of the + /// parameter. This parameter is passed + /// uninitialized. + /// + /// + /// true if the + /// contains an element with the specified key; otherwise, false. + /// + public bool TryGetValue(TKey key, out TValue? value) + { + lock (this.cacheMap) + { + if (this.cacheMap.TryGetValue(key, out LinkedListNode.MapItem> node)) + { + value = node.Value.Value; + this.lruList.Remove(node); + this.lruList.AddLast(node); + return true; + } + + value = default; + return false; + } + } + + /// + /// Looks for a value for the matching . If not found, + /// calls to retrieve the value and add it to + /// the cache. + /// + /// + /// The key of the value to look up. + /// + /// + /// Generates a value if one isn't found. + /// + /// + /// The requested value. + /// + public TValue Get(TKey key, Func valueGenerator) + { + lock (this.cacheMap) + { + TValue value; + if (this.cacheMap.TryGetValue(key, out LinkedListNode.MapItem> node)) + { + value = node.Value.Value; + this.lruList.Remove(node); + this.lruList.AddLast(node); + } + else + { + value = valueGenerator(); + if (this.cacheMap.Count >= this.Capacity) + { + this.RemoveFirst(); + } + + MapItem cacheItem = new(key, value); + node = new LinkedListNode(cacheItem); + this.lruList.AddLast(node); + this.cacheMap.Add(key, node); + } + + return value; + } + } + + /// + /// Adds the specified key and value to the dictionary. + /// + /// + /// The key of the element to add. + /// + /// + /// The value of the element to add. The value can be null for reference types. + /// + public void Add(TKey key, TValue value) + { + lock (this.cacheMap) + { + if (this.cacheMap.Count >= this.Capacity) + { + this.RemoveFirst(); + } + + MapItem cacheItem = new(key, value); + LinkedListNode node = new(cacheItem); + this.lruList.AddLast(node); + this.cacheMap.Add(key, node); + } + } + + private void RemoveFirst() + { + // Remove from LRUPriority + LinkedListNode node = this.lruList.First; + this.lruList.RemoveFirst(); + + // Remove from cache + this.cacheMap.Remove(node.Value.Key); + + // dispose + this.dispose?.Invoke(node.Value.Value); + } + + private class MapItem + { + public MapItem(TKey k, TValue v) + { + this.Key = k; + this.Value = v; + } + + public TKey Key { get; } + + public TValue Value { get; } + } +} + diff --git a/src/IFoxCAD.Basal/LinkedHashSet.cs b/src/IFoxCAD.Basal/LinkedHashSet.cs new file mode 100644 index 0000000..8659c24 --- /dev/null +++ b/src/IFoxCAD.Basal/LinkedHashSet.cs @@ -0,0 +1,221 @@ +namespace IFoxCAD.Basal; + +public class LinkedHashSet : ICollection where T : IComparable +{ + private readonly IDictionary> m_Dictionary; + private readonly LoopList m_LinkedList; + + public LinkedHashSet() + { + m_Dictionary = new Dictionary>(); + m_LinkedList = new LoopList(); + } + + public LoopListNode? First => m_LinkedList.First; + + public LoopListNode? Last => m_LinkedList.Last; + + public LoopListNode? MinNode { get; set; } + + public bool Add(T item) + { + if (m_Dictionary.ContainsKey(item)) + return false; + var node = m_LinkedList.AddLast(item); + m_Dictionary.Add(item, node); + + if (MinNode is null) + { + MinNode = node; + } + else + { + if (item.CompareTo(MinNode.Value) < 0) + { + MinNode = node; + } + } + + + + return true; + } + + void ICollection.Add(T item) + { + Add(item); + } + + public LoopListNode AddFirst(T value) + { + if (m_Dictionary.ContainsKey(value)) + { + return m_Dictionary[value]; + } + var node = m_LinkedList.AddFirst(value); + m_Dictionary.Add(value, node); + if (MinNode is null) + { + MinNode = node; + } + else + { + if (value.CompareTo(MinNode.Value) < 0) + { + MinNode = node; + } + } + return node; + } + + public void AddRange(IEnumerable collection) + { + foreach (var item in collection) + { + Add(item); + } + } + + + public void Clear() + { + m_LinkedList.Clear(); + m_Dictionary.Clear(); + } + + public bool Remove(T item) + { + bool found = m_Dictionary.TryGetValue(item, out LoopListNode node); + if (!found) return false; + m_Dictionary.Remove(item); + m_LinkedList.Remove(node); + return true; + } + + public int Count + { + get { return m_Dictionary.Count; } + } + + public void For(LoopListNode from, Action action) + { + var first = from; + var last = from; + if(first is null) return; + + for (int i = 0; i < Count; i++) + { + + action.Invoke(i,first!.Value, last!.Value); + first = first.Next; + last = last.Previous; + } + } + + public List ToList() + { + return m_LinkedList.ToList(); + } + + public IEnumerator GetEnumerator() + { + return m_LinkedList.GetEnumerator(); + } + + IEnumerator IEnumerable.GetEnumerator() + { + return GetEnumerator(); + } + + + public bool Contains(T item) + { + return m_Dictionary.ContainsKey(item); + } + + public void CopyTo(T[] array, int arrayIndex) + { + //m_LinkedList.CopyTo(array, arrayIndex); + return; + } + + public bool SetFirst(LoopListNode node) + { + return m_LinkedList.SetFirst(node); + } + + public LinkedHashSet Clone() + { + var newset = new LinkedHashSet(); + foreach (var item in this) + { + newset.Add(item); + } + return newset; + } + + public virtual bool IsReadOnly + { + get { return m_Dictionary.IsReadOnly; } + } + + public override string ToString() + { + return m_LinkedList.ToString(); + } + + public void UnionWith(IEnumerable other) + { + throw GetNotSupportedDueToSimplification(); + } + + public void IntersectWith(IEnumerable other) + { + throw GetNotSupportedDueToSimplification(); + } + + public void ExceptWith(IEnumerable other) + { + throw GetNotSupportedDueToSimplification(); + } + + public bool IsSubsetOf(IEnumerable other) + { + throw GetNotSupportedDueToSimplification(); + } + + public void SymmetricExceptWith(IEnumerable other) + { + throw GetNotSupportedDueToSimplification(); + } + + public bool IsSupersetOf(IEnumerable other) + { + throw GetNotSupportedDueToSimplification(); + } + + public bool IsProperSupersetOf(IEnumerable other) + { + throw GetNotSupportedDueToSimplification(); + } + + public bool IsProperSubsetOf(IEnumerable other) + { + throw GetNotSupportedDueToSimplification(); + } + + public bool Overlaps(IEnumerable other) + { + throw GetNotSupportedDueToSimplification(); + } + + public bool SetEquals(IEnumerable other) + { + throw GetNotSupportedDueToSimplification(); + } + + private static Exception GetNotSupportedDueToSimplification() + { + return new NotSupportedException("This method is not supported due to simplification of example code."); + } +} diff --git a/src/IFoxCAD.Basal/LinqEx.cs b/src/IFoxCAD.Basal/LinqEx.cs index 7b20401..8112e63 100644 --- a/src/IFoxCAD.Basal/LinqEx.cs +++ b/src/IFoxCAD.Basal/LinqEx.cs @@ -1,342 +1,335 @@ -using System; -using System.Collections.Generic; -using System.Linq; +namespace IFoxCAD.Basal; -namespace IFoxCAD.Linq +/// +/// linq 扩展类 +/// +public static class LinqEx { + #region FindByMax + /// - /// linq 扩展类 + /// 按转换函数找出序列中最大键值的对应值 /// - public static class LinqEx + /// + /// + /// 序列 + /// 转换函数 + /// 最大键值的对应值 + public static TValue FindByMax(this IEnumerable source, Func func) + where TKey : IComparable { - #region FindByMax - - /// - /// 按转换函数找出序列中最大键值的对应值 - /// - /// - /// - /// 序列 - /// 转换函数 - /// 最大键值的对应值 - public static TValue FindByMax(this IEnumerable source, Func func) - where TKey : IComparable - { - var itor = source.GetEnumerator(); - if (!itor.MoveNext()) - throw new ArgumentNullException(); + var itor = source.GetEnumerator(); + if (!itor.MoveNext()) + throw new ArgumentNullException(nameof(source), "对象为 null"); - TValue value = itor.Current; - TKey key = func(value); + TValue value = itor.Current; + TKey key = func(value); - while (itor.MoveNext()) + while (itor.MoveNext()) + { + TKey tkey = func(itor.Current); + if (tkey.CompareTo(key) > 0) { - TKey tkey = func(itor.Current); - if (tkey.CompareTo(key) > 0) - { - key = tkey; - value = itor.Current; - } + key = tkey; + value = itor.Current; } - return value; } + return value; + } - /// - /// 按转换函数找出序列中最大键值的对应值 - /// - /// - /// - /// 序列 - /// 对应的最大键值 - /// 转换函数 - /// 最大键值的对应值 - public static TValue FindByMax(this IEnumerable source, out TKey maxResult, Func func) - where TKey : IComparable - { - var itor = source.GetEnumerator(); - if (!itor.MoveNext()) - throw new ArgumentNullException(); + /// + /// 按转换函数找出序列中最大键值的对应值 + /// + /// + /// + /// 序列 + /// 对应的最大键值 + /// 转换函数 + /// 最大键值的对应值 + public static TValue FindByMax(this IEnumerable source, out TKey maxResult, Func func) + where TKey : IComparable + { + var itor = source.GetEnumerator(); + if (!itor.MoveNext()) + throw new ArgumentNullException(nameof(source), "对象为 null"); - TValue value = itor.Current; - TKey key = func(value); + TValue value = itor.Current; + TKey key = func(value); - while (itor.MoveNext()) + while (itor.MoveNext()) + { + TKey tkey = func(itor.Current); + if (tkey.CompareTo(key) > 0) { - TKey tkey = func(itor.Current); - if (tkey.CompareTo(key) > 0) - { - key = tkey; - value = itor.Current; - } + key = tkey; + value = itor.Current; } - maxResult = key; - return value; } + maxResult = key; + return value; + } - /// - /// 按比较器找出序列中最大键值的对应值 - /// - /// - /// 序列 - /// 比较器 - /// 最大键值的对应值 - public static TValue FindByMax(this IEnumerable source, Comparison comparison) - { - var itor = source.GetEnumerator(); - if (!itor.MoveNext()) - throw new ArgumentNullException(); - - TValue value = itor.Current; + /// + /// 按比较器找出序列中最大键值的对应值 + /// + /// + /// 序列 + /// 比较器 + /// 最大键值的对应值 + public static TValue FindByMax(this IEnumerable source, Comparison comparison) + { + var itor = source.GetEnumerator(); + if (!itor.MoveNext()) + throw new ArgumentNullException(nameof(source), "对象为 null"); - while (itor.MoveNext()) - { - if (comparison(itor.Current, value) > 0) - value = itor.Current; - } - return value; - } + TValue value = itor.Current; - #endregion FindByMax - - #region FindByMin - - /// - /// 按转换函数找出序列中最小键值的对应值 - /// - /// - /// - /// 序列 - /// 对应的最小键值 - /// 转换函数 - /// 最小键值的对应值 - public static TValue FindByMin(this IEnumerable source, out TKey minKey, Func func) - where TKey : IComparable + while (itor.MoveNext()) { - var itor = source.GetEnumerator(); - if (!itor.MoveNext()) - throw new ArgumentNullException(); + if (comparison(itor.Current, value) > 0) + value = itor.Current; + } + return value; + } - TValue value = itor.Current; - TKey key = func(value); + #endregion FindByMax - while (itor.MoveNext()) - { - TKey tkey = func(itor.Current); - if (tkey.CompareTo(key) < 0) - { - key = tkey; - value = itor.Current; - } - } - minKey = key; - return value; - } + #region FindByMin - /// - /// 按转换函数找出序列中最小键值的对应值 - /// - /// - /// - /// 序列 - /// 转换函数 - /// 最小键值的对应值 - public static TValue FindByMin(this IEnumerable source, Func func) - where TKey : IComparable - { - var itor = source.GetEnumerator(); - if (!itor.MoveNext()) - throw new ArgumentNullException(); + /// + /// 按转换函数找出序列中最小键值的对应值 + /// + /// + /// + /// 序列 + /// 对应的最小键值 + /// 转换函数 + /// 最小键值的对应值 + public static TValue FindByMin(this IEnumerable source, out TKey minKey, Func func) + where TKey : IComparable + { + var itor = source.GetEnumerator(); + if (!itor.MoveNext()) + throw new ArgumentNullException(nameof(source), "对象为 null"); - TValue value = itor.Current; - TKey key = func(value); + TValue value = itor.Current; + TKey key = func(value); - while (itor.MoveNext()) + while (itor.MoveNext()) + { + TKey tkey = func(itor.Current); + if (tkey.CompareTo(key) < 0) { - TKey tkey = func(itor.Current); - if (tkey.CompareTo(key) < 0) - { - key = tkey; - value = itor.Current; - } + key = tkey; + value = itor.Current; } - return value; } + minKey = key; + return value; + } - /// - /// 按比较器找出序列中最小键值的对应值 - /// - /// - /// 序列 - /// 比较器 - /// 最小键值的对应值 - public static TValue FindByMin(this IEnumerable source, Comparison comparison) - { - var itor = source.GetEnumerator(); - if (!itor.MoveNext()) - throw new ArgumentNullException(); + /// + /// 按转换函数找出序列中最小键值的对应值 + /// + /// + /// + /// 序列 + /// 转换函数 + /// 最小键值的对应值 + public static TValue FindByMin(this IEnumerable source, Func func) + where TKey : IComparable + { + var itor = source.GetEnumerator(); + if (!itor.MoveNext()) + throw new ArgumentNullException(nameof(source), "对象为 null"); - TValue value = itor.Current; + TValue value = itor.Current; + TKey key = func(value); - while (itor.MoveNext()) + while (itor.MoveNext()) + { + TKey tkey = func(itor.Current); + if (tkey.CompareTo(key) < 0) { - if (comparison(itor.Current, value) < 0) - value = itor.Current; + key = tkey; + value = itor.Current; } - return value; } + return value; + } - #endregion FindByMin + /// + /// 按比较器找出序列中最小键值的对应值 + /// + /// + /// 序列 + /// 比较器 + /// 最小键值的对应值 + public static TValue FindByMin(this IEnumerable source, Comparison comparison) + { + var itor = source.GetEnumerator(); + if (!itor.MoveNext()) + throw new ArgumentNullException(nameof(source), "对象为 null"); - #region FindByExt + TValue value = itor.Current; - /// - /// 按转换函数找出序列中最(小/大)键值的对应值 - /// - /// - /// - /// 序列 - /// 转换函数 - /// 最(小/大)键值的对应值 - public static TValue[] FindByExt(this IEnumerable source, Func func) - where TKey : IComparable + while (itor.MoveNext()) { - var itor = source.GetEnumerator(); - if (!itor.MoveNext()) - throw new ArgumentNullException(); + if (comparison(itor.Current, value) < 0) + value = itor.Current; + } + return value; + } - TValue[] values = new TValue[2]; - values[0] = values[1] = itor.Current; + #endregion FindByMin - TKey[] keys = new TKey[2]; - keys[0] = keys[1] = func(itor.Current); + #region FindByExt - while (itor.MoveNext()) - { - TKey tkey = func(itor.Current); - if (tkey.CompareTo(keys[0]) < 0) - { - keys[0] = tkey; - values[0] = itor.Current; - } - else if (tkey.CompareTo(keys[1]) > 0) - { - keys[1] = tkey; - values[1] = itor.Current; - } - } - return values; - } + /// + /// 按转换函数找出序列中最(小/大)键值的对应值 + /// + /// + /// + /// 序列 + /// 转换函数 + /// 最(小/大)键值的对应值 + public static TValue[] FindByExt(this IEnumerable source, Func func) + where TKey : IComparable + { + var itor = source.GetEnumerator(); + if (!itor.MoveNext()) + throw new ArgumentNullException(nameof(source), "对象为 null"); - /// - /// 按比较器找出序列中最(小/大)键值的对应值 - /// - /// - /// 序列 - /// 比较器 - /// 最(小/大)键值的对应值 - public static TValue[] FindByExt(this IEnumerable source, Comparison comparison) - { - var itor = source.GetEnumerator(); - if (!itor.MoveNext()) - throw new ArgumentNullException(); + TValue[] values = new TValue[2]; + values[0] = values[1] = itor.Current; - TValue[] values = new TValue[2]; - values[0] = values[1] = itor.Current; + TKey[] keys = new TKey[2]; + keys[0] = keys[1] = func(itor.Current); - while (itor.MoveNext()) + while (itor.MoveNext()) + { + TKey tkey = func(itor.Current); + if (tkey.CompareTo(keys[0]) < 0) { - if (comparison(itor.Current, values[0]) < 0) - values[0] = itor.Current; - else if (comparison(itor.Current, values[1]) > 0) - values[1] = itor.Current; + keys[0] = tkey; + values[0] = itor.Current; + } + else if (tkey.CompareTo(keys[1]) > 0) + { + keys[1] = tkey; + values[1] = itor.Current; } - return values; } + return values; + } - /// - /// 按转换函数找出序列中最(小/大)键值的对应键值 - /// - /// - /// - /// 序列 - /// 转换函数 - /// 最(小/大)键值 - public static TKey[] FindExt(this IEnumerable source, Func func) - where TKey : IComparable - { - var itor = source.GetEnumerator(); - if (!itor.MoveNext()) - throw new ArgumentNullException(); + /// + /// 按比较器找出序列中最(小/大)键值的对应值 + /// + /// + /// 序列 + /// 比较器 + /// 最(小/大)键值的对应值 + public static TValue[] FindByExt(this IEnumerable source, Comparison comparison) + { + var itor = source.GetEnumerator(); + if (!itor.MoveNext()) + throw new ArgumentNullException(nameof(source), "对象为 null"); - TKey[] keys = new TKey[2]; - keys[0] = keys[1] = func(itor.Current); + TValue[] values = new TValue[2]; + values[0] = values[1] = itor.Current; - while (itor.MoveNext()) - { - TKey tkey = func(itor.Current); - if (tkey.CompareTo(keys[0]) < 0) - keys[0] = tkey; - else if (tkey.CompareTo(keys[1]) > 0) - keys[1] = tkey; - } - return keys; + while (itor.MoveNext()) + { + if (comparison(itor.Current, values[0]) < 0) + values[0] = itor.Current; + else if (comparison(itor.Current, values[1]) > 0) + values[1] = itor.Current; } + return values; + } - #endregion FindByExt + /// + /// 按转换函数找出序列中最(小/大)键值的对应键值 + /// + /// + /// + /// 序列 + /// 转换函数 + /// 最(小/大)键值 + public static TKey[] FindExt(this IEnumerable source, Func func) + where TKey : IComparable + { + var itor = source.GetEnumerator(); + if (!itor.MoveNext()) + throw new ArgumentNullException(nameof(source), "对象为 null"); - #region Order + TKey[] keys = new TKey[2]; + keys[0] = keys[1] = func(itor.Current); - /// - /// 自定义的比较泛型类 - /// - /// 泛型 - private class SpecComparer : IComparer + while (itor.MoveNext()) { - private Comparison _comp; - - internal SpecComparer(Comparison comp) - { - _comp = comp; - } + TKey tkey = func(itor.Current); + if (tkey.CompareTo(keys[0]) < 0) + keys[0] = tkey; + else if (tkey.CompareTo(keys[1]) > 0) + keys[1] = tkey; + } + return keys; + } - #region IComparer 成员 + #endregion FindByExt - public int Compare(T x, T y) - { - return _comp(x, y); - } + #region Order - #endregion IComparer 成员 - } + /// + /// 自定义的比较泛型类 + /// + /// 泛型 + private class SpecComparer : IComparer + { + private readonly Comparison _comp; - /// - /// 使用指定的比较器将序列按升序排序 - /// - /// 输入泛型 - /// 输出泛型 - /// 序列 - /// 用于从元素中提取键的函数 - /// 比较器 - /// 排序的序列 - public static IOrderedEnumerable OrderBy(this IEnumerable source, Func keySelector, Comparison comparison) + internal SpecComparer(Comparison comp) { - return source.OrderBy(keySelector, new SpecComparer(comparison)); + _comp = comp; } - /// - /// 使用指定的比较器将其后的序列按升序排序 - /// - /// 输入泛型 - /// 输出泛型 - /// 序列 - /// 用于从元素中提取键的函数 - /// 比较器 - /// 排序的序列 - public static IOrderedEnumerable ThenBy(this IOrderedEnumerable source, Func keySelector, Comparison comparison) + #region IComparer 成员 + public int Compare(T x, T y) { - return source.ThenBy(keySelector, new SpecComparer(comparison)); + return _comp(x, y); } + #endregion IComparer 成员 + } + + /// + /// 使用指定的比较器将序列按升序排序 + /// + /// 输入泛型 + /// 输出泛型 + /// 序列 + /// 用于从元素中提取键的函数 + /// 比较器 + /// 排序的序列 + public static IOrderedEnumerable OrderBy(this IEnumerable source, Func keySelector, Comparison comparison) + { + return source.OrderBy(keySelector, new SpecComparer(comparison)); + } - #endregion Order + /// + /// 使用指定的比较器将其后的序列按升序排序 + /// + /// 输入泛型 + /// 输出泛型 + /// 序列 + /// 用于从元素中提取键的函数 + /// 比较器 + /// 排序的序列 + public static IOrderedEnumerable ThenBy(this IOrderedEnumerable source, Func keySelector, Comparison comparison) + { + return source.ThenBy(keySelector, new SpecComparer(comparison)); } + + #endregion Order } diff --git a/src/IFoxCAD.Basal/ListEx.cs b/src/IFoxCAD.Basal/ListEx.cs new file mode 100644 index 0000000..4ead97b --- /dev/null +++ b/src/IFoxCAD.Basal/ListEx.cs @@ -0,0 +1,30 @@ + +namespace IFoxCAD.Basal; + +public static class ListEx +{ + public static bool EqualsAll(this IList a, IList b) + { + return EqualsAll(a, b, null); + // there is a slight performance gain in passing null here. + // It is how it is done in other parts of the framework. + } + + public static bool EqualsAll(this IList a, IList b, IEqualityComparer? comparer) + { + if (a == null) + return b == null; + else if (b == null) + return false; + + if (a.Count != b.Count) + return false; + + comparer ??= EqualityComparer.Default; + + for (int i = 0; i < a.Count; i++) + if (!comparer.Equals(a[i], b[i])) + return false; + return true; + } +} diff --git a/src/IFoxCAD.Basal/LoopList.cs b/src/IFoxCAD.Basal/LoopList.cs index 2c0b088..c0e021c 100644 --- a/src/IFoxCAD.Basal/LoopList.cs +++ b/src/IFoxCAD.Basal/LoopList.cs @@ -1,281 +1,265 @@ -using System; -using System.Collections; -using System.Collections.Generic; -using System.Text; +namespace IFoxCAD.Basal; -namespace IFoxCAD.Collections +#line hidden //调试的时候跳过它 + +/// +/// 环链表节点 +/// +/// +public class LoopListNode { + #region 成员 /// - /// 环链表节点 - /// - /// - public class LoopListNode - { - #region 成员 - /// - /// 取值 - /// - public T Value; - - /// - /// 上一个节点 - /// - public LoopListNode? Previous { internal set; get; } - - /// - /// 下一个节点 - /// - public LoopListNode? Next { internal set; get; } - - /// - /// 环链表序列 - /// - public LoopList? List { internal set; get; } - #endregion - #region 构造 - /// - /// 环链表节点构造函数 - /// - /// 节点值 - public LoopListNode(T value, LoopList ts) - { - Value = value; - List = ts; - } + /// 取值 + ///
+ public T Value; - /// - /// 获取当前节点的临近节点 - /// - /// 搜索方向标志,为向前搜索,为向后搜索 - /// - public LoopListNode? GetNext(bool forward) - { - return forward ? Next : Previous; - } - #endregion - #region 方法 - /// - /// 无效化成员 - /// - internal void Invalidate() - { - List = null; - Next = null; - Previous = null; - } - #endregion + /// + /// 上一个节点 + /// + public LoopListNode? Previous { internal set; get; } + + /// + /// 下一个节点 + /// + public LoopListNode? Next { internal set; get; } + + /// + /// 环链表序列 + /// + public LoopList? List { internal set; get; } + #endregion + + #region 构造 + /// + /// 环链表节点构造函数 + /// + /// 节点值 + public LoopListNode(T value, LoopList ts) + { + Value = value; + List = ts; } /// - /// 环链表 + /// 获取当前节点的临近节点 /// - /// - public class LoopList : IEnumerable, IFormattable + /// 搜索方向标志,为向前搜索,为向后搜索 + /// + public LoopListNode? GetNext(bool forward) { - #region 成员 - /// - /// 节点数 - /// - public int Count { get; private set; } + return forward ? Next : Previous; + } + #endregion - /// - /// 首节点 - /// - public LoopListNode? First { get; private set; } + #region 方法 + /// + /// 无效化成员 + /// + internal void Invalidate() + { + List = null; + Next = null; + Previous = null; + } + #endregion +} + +/// +/// 环链表 +/// +/// +public class LoopList : IEnumerable, IFormattable +{ + #region 成员 - /// - /// 尾节点 - /// - public LoopListNode? Last => First?.Previous; + /// + /// 节点数 + /// + public int Count { get; private set; } - #endregion - #region 构造 - /// - /// 默认构造函数 - /// - public LoopList() { } + /// + /// 首节点 + /// + public LoopListNode? First { get; private set; } - /// - /// 环链表构造函数 - /// - /// 节点迭代器 - public LoopList(IEnumerable values) - { - var ge = values.GetEnumerator(); - while (ge.MoveNext()) - Add(ge.Current); - } + /// + /// 尾节点 + /// + public LoopListNode? Last => First?.Previous; - #endregion - #region 方法 - /// - /// 设置首节点 - /// - /// 节点 - /// - public bool SetFirst(LoopListNode node) - { - if (!Contains(node)) - return false; - First = node; - return true; - } - /// - /// 交换两个节点的值 - /// - /// 第一个节点 - /// 第二个节点 - public void Swap(LoopListNode node1, LoopListNode node2) - { -#if NET35 - var value = node1.Value; - node1.Value = node2.Value; - node2.Value = value; -#else - (node2.Value, node1.Value) = (node1.Value, node2.Value); -#endif - } - /// - /// 链内翻转 - /// - public void Reverse() - { - var first = First; - if (first is null) - return; - var last = Last; - for (int i = 0; i < Count / 2; i++) - { - Swap(first!, last!); - first = first!.Next; - last = last!.Previous; - } - } + #endregion - /// - /// 清理 - /// - public void Clear() - { - //移除头部,表示链表再也无法遍历得到 - First = null; - Count = 0; - } + #region 构造 + + /// + /// 默认构造函数 + /// + public LoopList() { } + + /// + /// 环链表构造函数 + /// + /// 节点迭代器 + public LoopList(IEnumerable values) + { + var ge = values.GetEnumerator(); + while (ge.MoveNext()) + Add(ge.Current); + } - /// - /// 从头遍历_非迭代器 - /// - /// - public void ForEach(Func, bool> action) + #endregion + + #region 方法 + + /// + /// 设置首节点 + /// + /// 节点 + /// + public bool SetFirst(LoopListNode node) + { + if (!Contains(node)) + return false; + + First = node; + return true; + } + + /// + /// 交换两个节点的值 + /// + /// 第一个节点 + /// 第二个节点 + public void Swap(LoopListNode node1, LoopListNode node2) + { + (node2.Value, node1.Value) = (node1.Value, node2.Value); + } + + /// + /// 链内翻转 + /// + public void Reverse() + { + var first = First; + if (first is null) + return; + var last = Last; + for (int i = 0; i < Count / 2; i++) { - var node = First; - if (node is null) - return; - for (int i = 0; i < Count; i++) - { - if (action(node!)) - break; - node = node!.Next; - } + Swap(first!, last!); + first = first!.Next; + last = last!.Previous; } + } - #region Contains - /// - /// 是否包含节点 - /// - /// - /// - public bool Contains(LoopListNode node) + /// + /// 清理 + /// + public void Clear() + { + //移除头部,表示链表再也无法遍历得到 + First = null; + Count = 0; + } + + /// + /// 从头遍历_非迭代器(此处和通用ForEach冲突,所以内部用) + /// + /// + void ForEach(Func, bool> action) + { + var node = First; + if (node is null) + return; + for (int i = 0; i < Count; i++) { - return node is not null && node.List == this; + if (action(node!)) + break; + node = node!.Next; } + } - /// - /// 是否包含值 - /// - /// - /// - public bool Contains(T value) + /// + /// 从头遍历_非迭代器(扔出计数) + /// + /// + public void For(Func, bool> action) + { + var node = First; + if (node is null) + return; + for (int i = 0; i < Count; i++) { - bool result = false; - ForEach(node => { - if (node.Value!.Equals(value)) - { - result = true; - return true; - } - return false; - }); - return result; + if (action(i, node!)) + break; + node = node!.Next; } + } - /// - /// 查找第一个出现的节点 - /// - /// - /// - public LoopListNode? Find(T value) - { - //LoopListNode result = null; - //ForEach(node => - //{ - // if (node.Value.Equals(t2)) - // { - // result = node; - // return true; - // } - // return false; - //}); - //return result; - - LoopListNode? node = First; - var c = EqualityComparer.Default; - if (node is not null) + #region Contains + + /// + /// 是否包含节点 + /// + /// + /// + public bool Contains(LoopListNode node) + { + return node is not null && node.List == this; + } + + /// + /// 是否包含值 + /// + /// + /// + public bool Contains(T value) + { + bool result = false; + ForEach(node => { + if (node.Value!.Equals(value)) { - if (value is not null) - { - do - { - if (c.Equals(node!.Value, value)) - return node; - node = node.Next; - } while (node != First); - } - else - { - do - { - if (node!.Value is null) - return node; - node = node.Next; - } while (node != First); - } + result = true; + return true; } - return null; - } + return false; + }); + return result; + } - /// - /// 查找所有出现的节点 - /// - /// - /// - public IEnumerable>? Finds(T value) + /// + /// 查找第一个出现的节点 + /// + /// + /// + public LoopListNode? Find(T value) + { + //LoopListNode result = null; + //ForEach(node => + //{ + // if (node.Value.Equals(t2)) + // { + // result = node; + // return true; + // } + // return false; + //}); + //return result; + + LoopListNode? node = First; + var c = EqualityComparer.Default; + if (node is not null) { - LoopListNode? node = First; - if (node is null) - return null; - - List> result = new(); - var c = EqualityComparer.Default; if (value is not null) { do { if (c.Equals(node!.Value, value)) - result.Add(node); + return node; node = node.Next; } while (node != First); } @@ -284,422 +268,475 @@ public bool Contains(T value) do { if (node!.Value is null) - result.Add(node); + return node; node = node.Next; } while (node != First); } - return result; } + return null; + } - /// - /// 获取节点 - /// - /// - /// - public LoopListNode? GetNode(Func func) - { - LoopListNode? result = null; - ForEach(node => { - if (func(node.Value)) - { - result = node; - return true; - } - return false; - }); - return result; - } + /// + /// 查找所有出现的节点 + /// + /// + /// + public IEnumerable>? Finds(T value) + { + LoopListNode? node = First; + if (node is null) + return null; - #endregion - #region Add - /// - /// 在首节点之前插入节点,并设置新节点为首节点 - /// - /// - /// - public LoopListNode AddFirst(T value) + List> result = new(); + var c = EqualityComparer.Default; + if (value is not null) { - var node = new LoopListNode(value, this); - - if (Count == 0) + do { - First = node; - First.Previous = First.Next = node; - } - else - { - LoopListNode last = Last!; - First!.Previous = last.Next = node; - node.Next = First; - node.Previous = last; - First = node; - } - Count++; - return First; + if (c.Equals(node!.Value, value)) + result.Add(node); + node = node.Next; + } while (node != First); } - - /// - /// 在尾节点之后插入节点,并设置新节点为尾节点 - /// - /// - /// - public LoopListNode Add(T value) + else { - var node = new LoopListNode(value, this); - - if (Count == 0) + do { - First = node; - First.Previous = First.Next = node; - } - else + if (node!.Value is null) + result.Add(node); + node = node.Next; + } while (node != First); + } + return result; + } + + /// + /// 获取节点 + /// + /// + /// + public LoopListNode? GetNode(Func func) + { + LoopListNode? result = null; + ForEach(node => { + if (func(node.Value)) { - var last = First!.Previous!; - First.Previous = last.Next = node; - node.Next = First; - node.Previous = last; + result = node; + return true; } - Count++; - return Last!; - } + return false; + }); + return result; + } - /// - /// 在尾节点之后插入节点,并设置新节点为尾节点_此函数仅为与LinkedList同名方法 - /// - /// - /// - public LoopListNode AddLast(T value) - { - return Add(value); - } + #endregion + #region Add - /// - /// 容器内容全部加入到末尾 - /// - /// - public void AddRange(IEnumerable list) - { - var ge = list.GetEnumerator(); - while (ge.MoveNext()) - Add(ge.Current); - } + /// + /// 在首节点之前插入节点,并设置新节点为首节点 + /// + /// + /// + public LoopListNode AddFirst(T value) + { + var node = new LoopListNode(value, this); - /// - /// 前面增加节点 - /// - /// - /// - /// - public LoopListNode AddBefore(LoopListNode node, T value) + if (Count == 0) { - if (node == First) - return AddFirst(value); - - var tnode = new LoopListNode(value, this); - node.Previous!.Next = tnode; - tnode.Previous = node.Previous; - node.Previous = tnode; - tnode.Next = node; - Count++; - return tnode; + First = node; + First.Previous = First.Next = node; } - - /// - /// 后面增加节点 - /// - /// - /// - /// - public LoopListNode AddAfter(LoopListNode node, T value) + else { - var tnode = new LoopListNode(value, this); - node.Next!.Previous = tnode; - tnode.Next = node.Next; - node.Next = tnode; - tnode.Previous = node; - Count++; - return tnode; + LoopListNode last = Last!; + First!.Previous = last.Next = node; + node.Next = First; + node.Previous = last; + First = node; } + Count++; + return First; + } + + /// + /// 在尾节点之后插入节点,并设置新节点为尾节点 + /// + /// + /// + public LoopListNode Add(T value) + { + var node = new LoopListNode(value, this); - #endregion - #region Remove - /// - /// 删除首节点 - /// - /// - public bool RemoveFirst() + if (Count == 0) { - switch (Count) - { - case 0: - return false; - - case 1: - First = null; - break; - - default: - LoopListNode last = Last!; - First = First!.Next; - First!.Previous = last; - last.Next = First; - break; - } - return true; + First = node; + First.Previous = First.Next = node; } - - /// - /// 删除尾节点 - /// - /// - public bool RemoveLast() + else { - switch (Count) - { - case 0: - return false; - - case 1: - First = null; - break; - - default: - LoopListNode last = Last!.Previous!; - last.Next = First; - First!.Previous = last; - break; - } - Count--; - return true; + var last = First!.Previous!; + First.Previous = last.Next = node; + node.Next = First; + node.Previous = last; } + Count++; + return Last!; + } + + /// + /// 在尾节点之后插入节点,并设置新节点为尾节点_此函数仅为与LinkedList同名方法 + /// + /// + /// + public LoopListNode AddLast(T value) + { + return Add(value); + } - /// - /// 删除节点 - /// - /// 指定节点 - /// - public bool Remove(LoopListNode node) + /// + /// 容器内容全部加入到末尾 + /// + /// + public void AddRange(IEnumerable list) + { + var ge = list.GetEnumerator(); + while (ge.MoveNext()) + Add(ge.Current); + } + + /// + /// 前面增加节点 + /// + /// + /// + /// + public LoopListNode AddBefore(LoopListNode node, T value) + { + if (node == First) + return AddFirst(value); + + var tnode = new LoopListNode(value, this); + node.Previous!.Next = tnode; + tnode.Previous = node.Previous; + node.Previous = tnode; + tnode.Next = node; + Count++; + return tnode; + } + + /// + /// 后面增加节点 + /// + /// + /// + /// + public LoopListNode AddAfter(LoopListNode node, T value) + { + var tnode = new LoopListNode(value, this); + node.Next!.Previous = tnode; + tnode.Next = node.Next; + node.Next = tnode; + tnode.Previous = node; + Count++; + return tnode; + } + + #endregion + + #region Remove + + /// + /// 删除首节点 + /// + /// + public bool RemoveFirst() + { + switch (Count) { - if (!Contains(node)) + case 0: return false; - InternalRemove(node); - return true; + + case 1: + First = null; + break; + + default: + LoopListNode last = Last!; + First = First!.Next; + First!.Previous = last; + last.Next = First; + break; } + Count--; + return true; + } - /// - /// 删除节点 - /// - /// 将移除所有含有此值 - /// - public bool Remove(T value) + /// + /// 删除尾节点 + /// + /// + public bool RemoveLast() + { + switch (Count) { - var lst = Finds(value); - if (lst is null) + case 0: return false; - var ge = lst!.GetEnumerator(); - while (ge.MoveNext()) - InternalRemove(ge.Current); - return true; + case 1: + First = null; + break; + + default: + LoopListNode last = Last!.Previous!; + last.Next = First; + First!.Previous = last; + break; } + Count--; + return true; + } - /// - /// 删除节点_内部调用 - /// - /// 此值肯定存在当前链表 - /// - void InternalRemove(LoopListNode node) - { - if (Count == 1 || node == First) - { - RemoveFirst(); - } - else - { - node.Next!.Previous = node.Previous; - node.Previous!.Next = node.Next; - } + /// + /// 删除此参数节点(唯一) + /// + /// 指定节点 + /// + public bool Remove(LoopListNode node) + { + if (!Contains(node)) + return false; + InternalRemove(node); + return true; + } - node.Invalidate(); - Count--; - } + /// + /// 删除含有此参数节点(所有) + /// + /// 将移除所有含有此值 + /// + public bool Remove(T value) + { + var lst = Finds(value); + if (lst is null) + return false; + + var ge = lst!.GetEnumerator(); + while (ge.MoveNext()) + InternalRemove(ge.Current); + return true; + } - #endregion - #region LinkTo - /// - /// 链接两节点,并去除这两个节点间的所有节点 - /// - /// - /// - public void LinkTo(LoopListNode from, LoopListNode to) + /// + /// 删除节点_内部调用 + /// + /// 此值肯定存在当前链表 + /// + void InternalRemove(LoopListNode node) + { + if (Count == 1 || node == First) { - if (from != to && Contains(from) && Contains(to)) - { - LoopListNode node = from.Next!; - bool isFirstChanged = false; - int number = 0; + RemoveFirst();//此处会减数字 + } + else + { + node.Next!.Previous = node.Previous; + node.Previous!.Next = node.Next; + Count--; + } + node.Invalidate(); + } - while (node != to) - { - if (node == First) - isFirstChanged = true; + #endregion - node = node.Next!; - number++; - } + #region LinkTo - from.Next = to; - to.Previous = from; + /// + /// 链接两节点,并去除这两个节点间的所有节点 + /// + /// + /// + public void LinkTo(LoopListNode from, LoopListNode to) + { + if (from != to && Contains(from) && Contains(to)) + { + LoopListNode node = from.Next!; + bool isFirstChanged = false; + int number = 0; - if (number > 0 && isFirstChanged) - First = to; + while (node != to) + { + if (node == First) + isFirstChanged = true; - Count -= number; + node = node.Next!; + number++; } - } - /// - /// 链接两节点,并去除这两个节点间的所有节点 - /// - /// - /// - /// - public void LinkTo(LoopListNode from, LoopListNode to, int number) - { - if (from != to && Contains(from) && Contains(to)) - { - from.Next = to; - to.Previous = from; + from.Next = to; + to.Previous = from; + + if (number > 0 && isFirstChanged) First = to; - Count -= number; - } - } - /// - /// 链接两节点,并去除这两个节点间的所有节点 - /// - /// - /// - /// - /// - public void LinkTo(LoopListNode from, LoopListNode to, int number, bool isFirstChanged) - { - if (from != to && Contains(from) && Contains(to)) - { - from.Next = to; - to.Previous = from; - if (isFirstChanged) - First = to; - Count -= number; - } + Count -= number; } + } - #endregion - #endregion - #region IEnumerable - /// - /// 获取节点的查询器 - /// - /// - /// - public IEnumerable> GetNodes(LoopListNode from) + /// + /// 链接两节点,并去除这两个节点间的所有节点 + /// + /// + /// + /// + public void LinkTo(LoopListNode from, LoopListNode to, int number) + { + if (from != to && Contains(from) && Contains(to)) { - var node = from; - for (int i = 0; i < Count; i++) - { - yield return node!; - node = node!.Next; - } + from.Next = to; + to.Previous = from; + First = to; + Count -= number; } + } - /// - /// 获取节点的查询器 - /// - /// - public IEnumerable> GetNodes() + /// + /// 链接两节点,并去除这两个节点间的所有节点 + /// + /// + /// + /// + /// + public void LinkTo(LoopListNode from, LoopListNode to, int number, bool isFirstChanged) + { + if (from != to && Contains(from) && Contains(to)) { - LoopListNode node = First!; - for (int i = 0; i < Count; i++) - { - yield return node!; - node = node.Next!; - } + from.Next = to; + to.Previous = from; + if (isFirstChanged) + First = to; + Count -= number; } + } + + #endregion - /// - /// 获取节点值的查询器 - /// - /// - public IEnumerator GetEnumerator() + #endregion + + #region IEnumerable + + /// + /// 获取节点的查询器 + /// + /// + /// + public IEnumerable> GetNodes(LoopListNode from) + { + var node = from; + for (int i = 0; i < Count; i++) { - LoopListNode node = First!; - for (int i = 0; i < Count; i++) - { - yield return node!.Value; - node = node.Next!; - } + yield return node!; + node = node!.Next; } + } - IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); - - #region IEnumerable 成员 - IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); - - #endregion IEnumerable 成员 - #endregion - #region IFormattable - /// - /// 转换为字符串_格式化实现 - /// - /// - /// - /// - string IFormattable.ToString(string? format, IFormatProvider? formatProvider) + + + /// + /// 获取节点的查询器 + /// + /// + public IEnumerable> GetNodes() + { + LoopListNode node = First!; + for (int i = 0; i < Count; i++) { - return ToString(format, formatProvider); + yield return node!; + node = node.Next!; } + } - /// - /// 转换为字符串_无参调用 - /// - /// - public override string ToString() + /// + /// 获取节点值的查询器 + /// + /// + public IEnumerator GetEnumerator() + { + LoopListNode node = First!; + for (int i = 0; i < Count; i++) { - return ToString(null, null); + yield return node!.Value; + node = node.Next!; } + } + + IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); + + #region IEnumerable 成员 + + IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); - /// - /// 转换为字符串_有参调用 - /// - /// - public string ToString(string? format, IFormatProvider? formatProvider = null) + #endregion IEnumerable 成员 + + #endregion + + #region IFormattable + /// + /// 转换为字符串_格式化实现 + /// + /// + /// + /// + string IFormattable.ToString(string? format, IFormatProvider? formatProvider) + { + return ToString(format, formatProvider); + } + + /// + /// 转换为字符串_无参调用 + /// + /// + public override string ToString() + { + return ToString(null, null); + } + + /// + /// 转换为字符串_有参调用 + /// + /// + public string ToString(string? format, IFormatProvider? formatProvider = null) + { + var s = new StringBuilder(); + s.Append($"Count = {Count};"); + if (format is null) { - var s = new StringBuilder(); - s.Append($"Count = {Count};"); - if (format is null) - { - s.Append("{ "); - foreach (T value in this) - s.Append($"{value} "); - s.Append(" }"); - } - return s.ToString(); + s.Append("{ "); + foreach (T value in this) + s.Append($"{value} "); + s.Append(" }"); } - #endregion - #region ICloneable - /* 山人说无法分辨ICloneable接口是深浅克隆,因此不要在泛型模板实现克隆函数,让用户自己来 - * 因此约定了:CopyTo(T,index)是深克隆;MemberwiseClone()是浅克隆; - * public object Clone() - * { - * var lst = new LoopList>(); - * ForEach(node => { - * lst.Add(node); - * return false; - * }); - * return lst; - * } - */ - #endregion - } -} \ No newline at end of file + return s.ToString(); + } + #endregion + + #region ICloneable + /* 山人说无法分辨ICloneable接口是深浅克隆, + * 因此不要在泛型模板实现克隆函数, + * 让用户自己来 new(xx)实现浅克隆,所以也不提供Clone()了 + * + * 因此约定了:CopyTo(T,index)是深克隆;MemberwiseClone()是浅克隆; + * public object Clone() + * { + * var lst = new LoopList>(); + * ForEach(node => { + * lst.Add(node); + * return false; + * }); + * return lst; + * } + */ + #endregion +} + +#line default \ No newline at end of file diff --git a/src/IFoxCAD.Basal/Sortedset/ISet.cs b/src/IFoxCAD.Basal/Sortedset/ISet.cs new file mode 100644 index 0000000..549e9f0 --- /dev/null +++ b/src/IFoxCAD.Basal/Sortedset/ISet.cs @@ -0,0 +1,71 @@ +#if NET35 +// ==++== +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +// ==--== +/*============================================================ +** +** Interface: ISet +** +** kimhamil +** +** +** Purpose: Base interface for all generic sets. +** +** +===========================================================*/ +namespace System.Collections.Generic +{ + + using System; + using System.Runtime.CompilerServices; + + + /// + /// Generic collection that guarantees the uniqueness of its elements, as defined + /// by some comparer. It also supports basic set operations such as Union, Intersection, + /// Complement and Exclusive Complement. + /// + public interface ISet : ICollection + { + + //Add ITEM to the set, return true if added, false if duplicate + new bool Add(T item); + + //Transform this set into its union with the IEnumerable other + void UnionWith(IEnumerable other); + + //Transform this set into its intersection with the IEnumberable other + void IntersectWith(IEnumerable other); + + //Transform this set so it contains no elements that are also in other + void ExceptWith(IEnumerable other); + + //Transform this set so it contains elements initially in this or in other, but not both + void SymmetricExceptWith(IEnumerable other); + + //Check if this set is a subset of other + bool IsSubsetOf(IEnumerable other); + + //Check if this set is a superset of other + bool IsSupersetOf(IEnumerable other); + + //Check if this set is a subset of other, but not the same as it + bool IsProperSupersetOf(IEnumerable other); + + //Check if this set is a superset of other, but not the same as it + bool IsProperSubsetOf(IEnumerable other); + + //Check if this set has any elements in common with other + bool Overlaps(IEnumerable other); + + //Check if this set contains the same and only the same elements as other + bool SetEquals(IEnumerable other); + + + + } + +} +#endif \ No newline at end of file diff --git a/src/IFoxCAD.Basal/Sortedset/SR.cs b/src/IFoxCAD.Basal/Sortedset/SR.cs new file mode 100644 index 0000000..093de14 --- /dev/null +++ b/src/IFoxCAD.Basal/Sortedset/SR.cs @@ -0,0 +1,907 @@ +#if NET35 +#pragma warning disable CS8603 // 可能返回 null 引用。 +namespace System; + + +using System; +using System.Reflection; +using System.Globalization; +using System.Resources; +using System.Text; +using System.ComponentModel; +using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; + +[AttributeUsage(AttributeTargets.All)] +internal sealed class SRDescriptionAttribute : DescriptionAttribute +{ + public SRDescriptionAttribute(string description) + { + DescriptionValue = SR.GetString(description); + } + + public SRDescriptionAttribute(string description, string resourceSet) + { + ResourceManager rm = new ResourceManager(resourceSet, Assembly.GetExecutingAssembly()); + DescriptionValue = rm.GetString(description); + System.Diagnostics.Debug.Assert(DescriptionValue != null, string.Format(CultureInfo.CurrentCulture, "String resource {0} not found.", new object[] { description })); + } +} + +[AttributeUsage(AttributeTargets.All)] +internal sealed class SRCategoryAttribute : CategoryAttribute +{ + private string resourceSet = String.Empty; + + public SRCategoryAttribute(string category) + : base(category) + { + } + + public SRCategoryAttribute(string category, string resourceSet) + : base(category) + { + this.resourceSet = resourceSet; + } + + protected override string GetLocalizedString(string value) + { + if (this.resourceSet.Length > 0) + { + ResourceManager rm = new ResourceManager(resourceSet, Assembly.GetExecutingAssembly()); + String localizedString = rm.GetString(value); + System.Diagnostics.Debug.Assert(localizedString != null, string.Format(CultureInfo.CurrentCulture, "String resource {0} not found.", new object[] { value })); + return localizedString; + } + else + { + return SR.GetString(value); + } + } +} + +[AttributeUsage(AttributeTargets.All)] +internal sealed class SRDisplayNameAttribute : DisplayNameAttribute +{ + public SRDisplayNameAttribute(string name) + { + DisplayNameValue = SR.GetString(name); + } + + public SRDisplayNameAttribute(string name, string resourceSet) + { + ResourceManager rm = new ResourceManager(resourceSet, Assembly.GetExecutingAssembly()); + DisplayNameValue = rm.GetString(name); + System.Diagnostics.Debug.Assert(DisplayNameValue != null, string.Format(CultureInfo.CurrentCulture, "String resource {0} not found.", new object[] { name })); + } +} + +/// +/// AutoGenerated resource class. Usage: +/// +/// string s = SR.GetString(SR.MyIdenfitier); +/// +internal sealed partial class SR +{ +#pragma warning disable CS8625 // 无法将 null 字面量转换为非 null 的引用类型。 + static SR loader = null; +#pragma warning restore CS8625 // 无法将 null 字面量转换为非 null 的引用类型。 + ResourceManager resources; + + internal SR() + { + resources = new System.Resources.ResourceManager("System.Workflow.ComponentModel.StringResources", Assembly.GetExecutingAssembly()); + } + + private static SR GetLoader() + { + if (loader == null) + loader = new SR(); + return loader; + } + + private static CultureInfo Culture + { + get { return null/*use ResourceManager default, CultureInfo.CurrentUICulture*/; } + } + + [SuppressMessage("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")] + internal static string GetString(string name, params object[] args) + { + return GetString(SR.Culture, name, args); + } + + [SuppressMessage("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")] + internal static string GetString(CultureInfo culture, string name, params object[] args) + { + SR sys = GetLoader(); + if (sys == null) + return null; + string res = sys.resources.GetString(name, culture); + System.Diagnostics.Debug.Assert(res != null, string.Format(CultureInfo.CurrentCulture, "String resource {0} not found.", new object[] { name })); + if (args != null && args.Length > 0) + { + return string.Format(CultureInfo.CurrentCulture, res, args); + } + else + { + return res; + } + } + + internal static string GetString(string name) + { + return GetString(SR.Culture, name); + } + + internal static string GetString(CultureInfo culture, string name) + { + SR sys = GetLoader(); + if (sys == null) + return null; + string res = sys.resources.GetString(name, culture); + System.Diagnostics.Debug.Assert(res != null, string.Format(CultureInfo.CurrentCulture, "String resource {0} not found.", new object[] { name })); + return res; + } + + // All these strings should be present in StringResources.resx + internal const string Activity = "Activity"; + internal const string Handlers = "Handlers"; + internal const string Conditions = "Conditions"; + internal const string ConditionedActivityConditions = "ConditionedActivityConditions"; + internal const string Correlations = "Correlations"; + internal const string CorrelationSet = "CorrelationSet"; + internal const string NameDescr = "NameDescr"; + internal const string EnabledDescr = "EnabledDescr"; + internal const string DescriptionDescr = "DescriptionDescr"; + internal const string UnlessConditionDescr = "UnlessConditionDescr"; + internal const string InitializeDescr = "InitializeDescr"; + internal const string CatchTypeDescr = "CatchTypeDescr"; + internal const string ExceptionTypeDescr = "ExceptionTypeDescr"; + internal const string FaultDescription = "FaultDescription"; + internal const string FaultTypeDescription = "FaultTypeDescription"; + internal const string ContainingAssemblyDescr = "ContainingAssemblyDescr"; + internal const string ExecutionModeDescr = "ExecutionModeDescr"; + internal const string Error_ReadOnlyTemplateActivity = "Error_ReadOnlyTemplateActivity"; + internal const string Error_TypeNotString = "Error_TypeNotString"; + internal const string Error_InvalidErrorType = "Error_InvalidErrorType"; + internal const string Error_LiteralConversionFailed = "Error_LiteralConversionFailed"; + internal const string Error_TypeNotPrimitive = "Error_TypeNotPrimitive"; + internal const string CompletedCaleeDescr = "CompletedCaleeDescr"; + internal const string ProxyClassDescr = "ProxyClassDescr"; + internal const string ActivitySetDescr = "ActivitySetDescr"; + internal const string VersionDescr = "VersionDescr"; + internal const string ActivationDescr = "ActivationDescr"; + internal const string CorrelationSetsDescr = "CorrelationSetsDescr"; + internal const string CompanionClassDescr = "CompanionClassDescr"; + internal const string TransactionTypeDescr = "TransactionTypeDescr"; + internal const string SynchronizedDescr = "SynchronizedDescr"; + internal const string IsolationLevelDescr = "IsolationLevelDescr"; + internal const string TimeoutDescr = "TimeoutDescr"; + internal const string BatchableDescr = "BatchableDescr"; + internal const string LRTTimeoutDescr = "LRTTimeoutDescr"; + internal const string OnGetCalleeCountDescr = "OnGetCalleeCountDescr"; + internal const string CompensatableActivityDescr = "CompensatableActivityDescr"; + internal const string OnAfterEventDescr = "OnAfterEventDescr"; + internal const string OnBeforeMethodInvokeDescr = "OnBeforeMethodInvokeDescr"; + internal const string AssignedToDescr = "AssignedToDescr"; + internal const string TypeDescr = "TypeDescr"; + internal const string TemplateActivityDescr = "TemplateActivityDescr"; + internal const string ErrorMessageDescr = "ErrorMessageDescr"; + internal const string WebServiceSynchronizedDescr = "WebServiceSynchronizedDescr"; + internal const string CorrelationSetDescr = "CorrelationSetDescr"; + internal const string ExecutionTypeDescr = "ExecutionTypeDescr"; + internal const string RoleDescr = "RoleDescr"; + internal const string OnInitializeClonesDescr = "OnInitializeClonesDescr"; + internal const string CorrelationSetDisplayName = "CorrelationSetDisplayName"; + internal const string PastingActivities = "PastingActivities"; + internal const string DeletingActivities = "DeletingActivities"; + internal const string DragDropActivities = "DragDropActivities"; + internal const string ChangingEnabled = "ChangingEnabled"; + internal const string ChangingHandler = "ChangingHandler"; + internal const string ChangingParameter = "ChangingParameter"; + internal const string CollectionItem = "CollectionItem"; + internal const string AddingConditionalBranch = "AddingConditionalBranch"; + internal const string AddingEventActivity = "AddingEventActivity"; + internal const string AddingListenBranch = "AddingListenBranch"; + internal const string AddingParallelBranch = "AddingParallelBranch"; + internal const string CurrentProject = "CurrentProject"; + internal const string ReferencedAssemblies = "ReferencedAssemblies"; + internal const string CollectionText = "CollectionText"; + internal const string ParameterDescription = "ParameterDescription"; + internal const string InvokeParameterDescription = "InvokeParameterDescription"; + internal const string ParametersDescription = "ParametersDescription"; + internal const string ChangingParameters = "ChangingParameters"; + internal const string Condition = "ConditionRule"; + internal const string MovingActivities = "MovingActivities"; + internal const string MemberNameDescr = "MemberNameDescr"; + internal const string OnScopeInitializedDescr = "OnScopeInitializedDescr"; + internal const string OnGeneratorInitializedDescr = "OnGeneratorInitializedDescr"; + internal const string OnScopeCompletedDescr = "OnScopeCompletedDescr"; + internal const string OnGeneratorCompletedDescr = "OnGeneratorCompletedDescr"; + internal const string DataElementRuntimeTypeDescr = "DataElementRuntimeTypeDescr"; + internal const string RuleConditionReferencesDescr = "RuleConditionReferencesDescr"; + internal const string CreateActivityFromToolbox = "CreateActivityFromToolbox"; + internal const string MoveMultipleActivities = "MoveMultipleActivities"; + internal const string MoveSingleActivity = "MoveSingleActivity"; + internal const string CutMultipleActivities = "CutMultipleActivities"; + internal const string CutSingleActivity = "CutSingleActivity"; + internal const string CutActivity = "CutActivity"; + internal const string FaultActivityDescription = "FaultActivityDescription"; + internal const string NullConditionExpression = "NullConditionExpression"; + internal const string ParameterTypeDescription = "ParameterTypeDescription"; + internal const string ParameterCategory = "ParameterCategory"; + internal const string ParameterDirectionDescription = "ParameterDirectionDescription"; + internal const string ParameterElementDescription = "ParameterElementDescription"; + internal const string ParameterDlgDescription = "ParameterDlgDescription"; + internal const string ParameterDlgHeader = "ParameterDlgHeader"; + internal const string SuspendActivityDescription = "SuspendActivityDescription"; + internal const string SuspendErrorMessageDescr = "SuspendErrorMessageDescr"; + internal const string TerminateActivityDescription = "TerminateActivityDescription"; + internal const string TerminateErrorMessageDescr = "TerminateErrorMessageDescr"; + internal const string DeclarationCategory = "DeclarationCategory"; + internal const string NoValidActivityPropertiesAvailable = "NoValidActivityPropertiesAvailable"; + internal const string ChooseActivityDatasource = "ChooseActivityDatasource"; + internal const string Promote = "Promote"; + internal const string Type = "Type"; + internal const string NoMatchingActivityProperties = "NoMatchingActivityProperties"; + internal const string ActivityBindIDDescription = "ActivityBindIDDescription"; + internal const string ActivityBindPathDescription = "ActivityBindPathDescription"; + internal const string XPathDescription = "XPathDescription"; + internal const string TransformerDescription = "TransformerDescription"; + internal const string CustomPropertiesCollectionFormHeader = "CustomPropertiesCollectionFormHeader"; + internal const string CustomPropertiesCollectionFormDescription = "CustomPropertiesCollectionFormDescription"; + internal const string BaseTypePropertyName = "BaseTypePropertyName"; + internal const string CustomActivityBaseClassTypeFilterProviderDesc = "CustomActivityBaseClassTypeFilterProviderDesc"; + internal const string CustomActivityDesignerTypeFilterProviderDesc = "CustomActivityDesignerTypeFilterProviderDesc"; + internal const string CustomActivityValidatorTypeFilterProviderDesc = "CustomActivityValidatorTypeFilterProviderDesc"; + internal const string CustomActivityExecutorTypeFilterProviderDesc = "CustomActivityExecutorTypeFilterProviderDesc"; + internal const string GenericParameters = "GenericParameters"; + internal const string ToolboxItem = "ToolboxItem"; + internal const string ToolboxItemCompanionClassDesc = "ToolboxItemCompanionClassDesc"; + internal const string Error_SerializationInsufficientState = "Error_SerializationInsufficientState"; + internal const string Error_ActivityHasParent = "Error_ActivityHasParent"; + internal const string Error_CompensantionParentNotScope = "Error_CompensantionParentNotScope"; + internal const string Error_ConditionedActivityParentNotCAG = "Error_ConditionedActivityParentNotCAG"; + internal const string Error_CorrelationTypeNotComparable = "Error_CorrelationTypeNotComparable"; + internal const string Error_ArgumentTypeNotMatchParameter = "Error_ArgumentTypeNotMatchParameter"; + internal const string Error_TypeTypeMismatch = "Error_TypeTypeMismatch"; + internal const string Error_ParameterTypeMismatch = "Error_ParameterTypeMismatch"; + internal const string Error_InvokeParameterTypeMismatch = "Error_InvokeParameterTypeMismatch"; + internal const string Error_ParameterPropertyNotSet = "Error_ParameterPropertyNotSet"; + internal const string Error_DataSourceNameNotSet = "Error_DataSourceNameNotSet"; + internal const string Error_DataSourceInvalidIdentifier = "Error_DataSourceInvalidIdentifier"; + internal const string Error_ParameterTypeNotExist = "Error_ParameterTypeNotExist"; + internal const string Error_InvalidParameterName = "Error_InvalidParameterName"; + internal const string Error_InvalidParameterType = "Error_InvalidParameterType"; + internal const string Error_InvalidParameterElement = "Error_InvalidParameterElement"; + internal const string Error_InvalidPropertyType = "Error_InvalidPropertyType"; + internal const string Error_TypeNotResolvedInMethodName = "Error_TypeNotResolvedInMethodName"; + internal const string Error_DelegateNoInvoke = "Error_DelegateNoInvoke"; + internal const string Error_TypeNotDelegate = "Error_TypeNotDelegate"; + internal const string Error_MethodSignatureMismatch = "Error_MethodSignatureMismatch"; + internal const string Error_MethodReturnTypeMismatch = "Error_MethodReturnTypeMismatch"; + internal const string Error_PropertyNotSet = "Error_PropertyNotSet"; + internal const string Error_ScopeCouldNotBeResolved = "Error_ScopeCouldNotBeResolved"; + internal const string Error_IfElseNotAllIfElseBranchDecl = "Error_ConditionalNotAllConditionalBranchDecl"; + internal const string Error_TypeTypeMismatchAmbiguity = "Error_TypeTypeMismatchAmbiguity"; + internal const string Error_InvalidCorrelationSetDatasource = "Error_InvalidCorrelationSetDatasource"; + internal const string Error_InvalidCorrelationSetType = "Error_InvalidCorrelationSetType"; + internal const string Error_MissingCorrelationParameterAttribute = "Error_MissingCorrelationParameterAttribute"; + internal const string Error_CorrelationTypeNotConsistent = "Error_CorrelationTypeNotConsistent"; + internal const string Error_CorrelationInvalid = "Error_CorrelationInvalid"; + internal const string Error_MissingDelegateMethod = "Error_MissingDelegateMethod"; + internal const string Error_MissingHostInterface = "Error_MissingHostInterface"; + internal const string Error_MissingMethodName = "Error_MissingMethodName"; + internal const string Error_NoBoundType = "Error_NoBoundType"; + internal const string Error_PortTypeNotAnInterface = "Error_PortTypeNotAnInterface"; + internal const string Error_MethodNotExists = "Error_MethodNotExists"; + internal const string Error_InvalidRequestResponseMethod = "Error_InvalidRequestResponseMethod"; + internal const string General_MissingService = "General_MissingService"; + internal const string Error_ScopeDuplicatedNameActivity = "Error_ScopeDuplicatedNameActivity"; + internal const string Error_DuplicatedActivityID = "Error_DuplicatedActivityID"; + internal const string Error_DuplicatedParameterName = "Error_DuplicatedParameterName"; + internal const string Error_ScopeMissingSerializableAttribute = "Error_ScopeMissingSerializableAttribute"; + internal const string Error_FieldNotExists = "Error_FieldNotExists"; + internal const string Error_PropertyNotExists = "Error_PropertyNotExists"; + internal const string Error_FieldTypeMismatch = "Error_FieldTypeMismatch"; + internal const string Error_PropertyTypeMismatch = "Error_PropertyTypeMismatch"; + internal const string Error_TypeNotResolvedInFieldName = "Error_TypeNotResolvedInFieldName"; + internal const string Error_TypeNotResolvedInPropertyName = "Error_TypeNotResolvedInPropertyName"; + internal const string Error_FieldGenericParamTypeMismatch = "Error_FieldGenericParamTypeMismatch"; + internal const string Error_TypeNotResolved = "Error_TypeNotResolved"; + internal const string Error_TypeIsUnboundedGeneric = "Error_TypeIsUnboundedGeneric"; + internal const string Error_MissingRootActivity = "Error_MissingRootActivity"; + internal const string Error_PropertyNotReadable = "Error_PropertyNotReadable"; + internal const string Error_PropertyNotWritable = "Error_PropertyNotWritable"; + internal const string Error_NotCompositeActivity = "Error_NotCompositeActivity"; + internal const string Error_TypeNotExist = "Error_TypeNotExist"; + internal const string Error_ActivityRefNotResolved = "Error_ActivityRefNotResolved"; + internal const string Error_ActivityRefNotMatchType = "Error_ActivityRefNotMatchType"; + internal const string Error_ActivityValidation = "Error_ActivityValidation"; + internal const string Error_ActiveChildExist = "Error_ActiveChildExist"; + internal const string Error_ActiveChildContextExist = "Error_ActiveChildContextExist"; + internal const string Error_CannotCompleteContext = "Error_CannotCompleteContext"; + internal const string Error_NoPasteSupport = "Error_NoPasteSupport"; + internal const string Error_UnknownSerializationStore = "Error_UnknownSerializationStore"; + internal const string Error_MissingCorrelationSet = "Error_MissingCorrelationSet"; + internal const string Error_CreateVariable = "Error_CreateVariable"; + internal const string Error_DuplicateCorrelationSetName = "Error_DuplicateCorrelationSetName"; + internal const string Error_DragDropInvalid = "Error_DragDropInvalid"; + internal const string AddingImplicitActivity = "AddingImplicitActivity"; + internal const string Failure_DoDefaultAction = "Failure_DoDefaultAction"; + internal const string Failure_DoDefaultActionCaption = "Failure_DoDefaultActionCaption"; + internal const string Error_FaultInsideAtomicScope = "Error_FaultInsideAtomicScope"; + internal const string Error_ListenNotMoreThanOneDelay = "Error_ListenNotMoreThanOneDelay"; + internal const string Error_AtomicScopeWithFaultHandlersActivityDecl = "Error_AtomicScopeWithFaultHandlersActivityDecl"; + internal const string Error_AtomicScopeWithCancellationHandlerActivity = "Error_AtomicScopeWithCancellationHandlerActivity"; + internal const string Error_ScopeDuplicateFaultHandlerActivityForAll = "Error_ScopeDuplicateFaultHandlerActivityForAll"; + internal const string Error_ScopeDuplicateFaultHandlerActivityFor = "Error_ScopeDuplicateFaultHandlerActivityFor"; + internal const string Error_AtomicScopeNestedInNonLRT = "Error_AtomicScopeNestedInNonLRT"; + internal const string Error_LRTScopeNestedInNonLRT = "Error_LRTScopeNestedInNonLRT"; + internal const string Error_CAGNotAllChildrenConditioned = "Error_CAGNotAllChildrenConditioned"; + internal const string Error_ConditionedActivityChildCount = "Error_ConditionedActivityChildCount"; + internal const string Error_NegativeValue = "Error_NegativeValue"; + internal const string Error_MethodWithReturnType = "Error_MethodWithReturnType"; + internal const string Error_SendReceiveOrderIncorrect = "Error_SendReceiveOrderIncorrect"; + internal const string Error_ReceiveSendOrderIncorrect = "Error_ReceiveSendOrderIncorrect"; + internal const string Error_CompensateBadNesting = "Error_CompensateBadNesting"; + internal const string Error_ReferencedAssemblyIsInvalid = "Error_ReferencedAssemblyIsInvalid"; + internal const string Error_TypeToXsdConversion = "Error_TypeToXsdConversion"; + internal const string Error_FieldTypeNotResolved = "Error_FieldTypeNotResolved"; + internal const string Error_PropertyTypeNotResolved = "Error_PropertyTypeNotResolved"; + internal const string Error_CouldNotDeserializeXomlFile = "Error_CouldNotDeserializeXomlFile"; + internal const string Error_InternalCompilerError = "Error_InternalCompilerError"; + internal const string Error_TypeNotAsseblyQualified = "Error_TypeNotAsseblyQualified"; + internal const string CompilerWarning_StandardAssemlbyInReferences = "CompilerWarning_StandardAssemlbyInReferences"; + internal const string Error_SuspendInAtomicScope = "Error_SuspendInAtomicScope"; + internal const string Error_InvalidActivityExecutionContext = "Error_InvalidActivityExecutionContext"; + internal const string Error_NoRuntimeAvailable = "Error_NoRuntimeAvailable"; + internal const string Error_CanNotChangeAtRuntime = "Error_CanNotChangeAtRuntime"; + internal const string Error_DataContextNotInitialized = "Error_DataContextNotInitialized"; + internal const string Error_DataContextAlreadyInitialized = "Error_DataContextAlreadyInitialized"; + internal const string Error_ParseActivityNameDoesNotExist = "Error_ParseActivityNameDoesNotExist"; + internal const string Error_NoParameterPropertyDeclared = "Error_NoParameterPropertyDeclared"; + internal const string Error_PropertyInvalidIdentifier = "Error_PropertyInvalidIdentifier"; + internal const string Error_WorkflowDefinitionModified = "Error_WorkflowDefinitionModified"; + internal const string Error_FieldAlreadyExist = "Error_FieldAlreadyExist"; + internal const string Failure_FieldAlreadyExist = "Failure_FieldAlreadyExist"; + internal const string Error_DifferentTypeFieldExists = "Error_DifferentTypeFieldExists"; + internal const string Error_RootActivityTypeInvalid = "Error_RootActivityTypeInvalid"; + internal const string Error_RootActivityTypeInvalid2 = "Error_RootActivityTypeInvalid2"; + internal const string Error_CannotCompile_No_XClass = "Error_CannotCompile_No_XClass"; + internal const string Error_TemplateActivityIsNotActivity = "Error_TemplateActivityIsNotActivity"; + internal const string Error_TypeIsNotRootActivity = "Error_TypeIsNotRootActivity"; + internal const string Error_NoTypeProvider = "Error_NoTypeProvider"; + internal const string Error_NotCodeGeneratorType = "Error_NotCodeGeneratorType"; + internal const string Error_NotDataContext = "Error_NotDataContext"; + internal const string Error_MissingDefaultConstructor = "Error_MissingDefaultConstructor"; + internal const string Error_ContextStackItemMissing = "Error_ContextStackItemMissing"; + internal const string Error_UnexpectedArgumentType = "Error_UnexpectedArgumentType"; + internal const string Error_EmptyArgument = "Error_EmptyArgument"; + internal const string Error_DPAlreadyExist = "Error_DPAlreadyExist"; + internal const string Error_DuplicateDynamicProperty = "Error_DuplicateDynamicProperty"; + internal const string Error_DynamicPropertyTypeValueMismatch = "Error_DynamicPropertyTypeValueMismatch"; + internal const string Error_DynamicPropertyNoSupport = "Error_DynamicPropertyNoSupport"; + internal const string Error_NoContextForDatasource = "Error_NoContextForDatasource"; + internal const string Error_NoContextForDatasourceCaption = "Error_NoContextForDatasourceCaption"; + internal const string Error_DataSourceHasParent = "Error_DataSourceHasParent"; + internal const string OnTaskCompletedDescr = "OnTaskCompletedDescr"; + internal const string OnTaskInitializedDescr = "OnTaskInitializedDescr"; + internal const string Error_InvalidXmlData = "Error_InvalidXmlData"; + internal const string Error_HandlerNotOnRoot = "Error_HandlerNotOnRoot"; + internal const string Error_InvalidArgumentIndex = "Error_InvalidArgumentIndex"; + internal const string Error_UITypeEditorTypeNotUITypeEditor = "Error_UITypeEditorTypeNotUITypeEditor"; + internal const string FilterDescription_UITypeEditor = "FilterDescription_UITypeEditor"; + internal const string Error_UserCodeFilesNotAllowed = "Error_UserCodeFilesNotAllowed"; + internal const string Error_CodeWithinNotAllowed = "Error_CodeWithinNotAllowed"; + internal const string Error_TypeNotAuthorized = "Error_TypeNotAuthorized"; + internal const string Error_CantDetermineBaseType = "Error_CantDetermineBaseType"; + internal const string Error_MultipleSelectNotSupportedForBindAndPromote = "Error_MultipleSelectNotSupportedForBindAndPromote"; + internal const string Error_CantDetermineBaseTypeCaption = "Error_CantDetermineBaseTypeCaption"; + internal const string Error_CantDeterminePropertyBaseType = "Error_CantDeterminePropertyBaseType"; + internal const string Error_NullCustomActivityTypeName = "Error_NullCustomActivityTypeName"; + internal const string Error_InvalidAttribute = "Error_InvalidAttribute"; + internal const string Error_InvalidAttributes = "Error_InvalidAttributes"; + internal const string Error_ConfigFileMissingOrInvalid = "Error_ConfigFileMissingOrInvalid"; + internal const string Error_CantHaveContextActivity = "Error_CantHaveContextActivity"; + internal const string Error_SynchronizedNeedsDataContext = "Error_SynchronizedNeedsDataContext"; + internal const string Error_MoreThanOneFaultHandlersActivityDecl = "Error_MoreThanOneFaultHandlersActivityDecl"; + internal const string Error_MoreThanOneEventHandlersDecl = "Error_MoreThanOneEventHandlersDecl"; + internal const string Error_MoreThanOneCancelHandler = "Error_MoreThanOneCancelHandler"; + internal const string Error_MetaDataInterfaceMissing = "Error_MetaDataInterfaceMissing"; + internal const string Error_NonActivityExecutor = "Error_NonActivityExecutor"; + internal const string Error_DynamicUpdateEvaluation = "Error_DynamicUpdateEvaluation"; + internal const string Error_CollectionHasNullEntry = "Error_CollectionHasNullEntry"; + internal const string Error_MissingContextProperty = "Error_MissingContextProperty"; + internal const string Error_AssociatedDesignerMissing = "Error_AssociatedDesignerMissing"; + internal const string Error_MissingContextActivityProperty = "Error_MissingContextActivityProperty"; + internal const string Error_MissingActivityProperty = "Error_MissingActivityProperty"; + internal const string Error_MissingOwnerTypeProperty = "Error_MissingOwnerTypeProperty"; + internal const string Error_DOIsNotAnActivity = "Error_DOIsNotAnActivity"; + internal const string Error_PropertyCanBeOnlyCleared = "Error_PropertyCanBeOnlyCleared"; + internal const string Error_PropertyDefaultTypeMismatch = "Error_PropertyDefaultTypeMismatch"; + internal const string Error_PropertyDefaultIsReference = "Error_PropertyDefaultIsReference"; + // workflow load errors + internal const string Error_WorkflowLoadFailed = "Error_WorkflowLoadFailed"; + internal const string Error_WorkflowLoadValidationFailed = "Error_WorkflowLoadValidationFailed"; + internal const string Error_WorkflowLoadDeserializationFailed = "Error_WorkflowLoadDeserializationFailed"; + internal const string Error_WorkflowLoadTypeMismatch = "Error_WorkflowLoadTypeMismatch"; + internal const string Error_WorkflowLoadInvalidXoml = "Error_WorkflowLoadInvalidXoml"; + internal const string Error_WorkflowLoadNotValidRootType = "Error_WorkflowLoadNotValidRootType"; + internal const string Error_CantCreateInstanceOfComponent = "Error_CantCreateInstanceOfComponent"; + internal const string Error_NotComponentFactoryType = "Error_NotComponentFactoryType"; + internal const string Error_WorkflowTerminated = "Error_WorkflowTerminated"; + + // serializer errrors + internal const string Error_SerializerAttributesFoundInComplexProperty = "Error_SerializerAttributesFoundInComplexProperty"; + internal const string Error_InvalidDataFound = "Error_InvalidDataFound"; + internal const string Error_InvalidDataFoundForType = "Error_InvalidDataFoundForType"; + internal const string Error_InvalidDataFoundForType1 = "Error_InvalidDataFoundForType1"; + internal const string Error_SerializerTypeNotResolved = "Error_SerializerTypeNotResolved"; + internal const string Error_MarkupSerializerTypeNotResolved = "Error_MarkupSerializerTypeNotResolved"; + internal const string Error_SerializerTypeNotResolvedWithInnerError = "Error_SerializerTypeNotResolvedWithInnerError"; + internal const string Error_SerializerNotAvailable = "Error_SerializerNotAvailable"; + internal const string Error_SerializerNotAvailableForSerialize = "Error_SerializerNotAvailableForSerialize"; + internal const string Error_SerializerCreateInstanceFailed = "Error_SerializerCreateInstanceFailed"; + internal const string Error_SerializerAddChildFailed = "Error_SerializerAddChildFailed"; + internal const string Error_SerializerNoPropertyAvailable = "Error_SerializerNoPropertyAvailable"; + internal const string Error_SerializerPrimitivePropertyReadOnly = "Error_SerializerPrimitivePropertyReadOnly"; + internal const string Error_SerializerCantChangeIsLocked = "Error_SerializerCantChangeIsLocked"; + internal const string Error_SerializerPrimitivePropertySetFailed = "Error_SerializerPrimitivePropertySetFailed"; + internal const string Error_SerializerPropertyGetFailed = "Error_SerializerPropertyGetFailed"; + internal const string Error_SerializerPrimitivePropertyNoLogic = "Error_SerializerPrimitivePropertyNoLogic"; + internal const string Error_SerializerPrimitivePropertyParentIsNull = "Error_SerializerPrimitivePropertyParentIsNull"; + internal const string Error_SerializerComplexPropertySetFailed = "Error_SerializerComplexPropertySetFailed"; + internal const string Error_SerializerNoChildNotion = "Error_SerializerNoChildNotion"; + internal const string Error_SerializerNoDynamicPropertySupport = "Error_SerializerNoDynamicPropertySupport"; + internal const string Error_SerializerNoSerializeLogic = "Error_SerializerNoSerializeLogic"; + internal const string Error_SerializerReadOnlyPropertyAndValueIsNull = "Error_SerializerReadOnlyPropertyAndValueIsNull"; + internal const string Error_SerializerReadOnlyParametersNoChild = "Error_SerializerReadOnlyParametersNoChild"; + internal const string Error_SerializerNotParameterBindingObject = "Error_SerializerNotParameterBindingObject"; + internal const string Error_SerializerThrewException = "Error_SerializerThrewException"; + internal const string Error_ActivityCollectionSerializer = "Error_ActivityCollectionSerializer"; + internal const string Error_MissingClassAttribute = "Error_MissingClassAttribute"; + internal const string Error_MissingClassAttributeValue = "Error_MissingClassAttributeValue"; + internal const string ExecutorCreationFailedErrorMessage = "ExecutorCreationFailedErrorMessage"; + internal const string VariableGetterCode_VB = "VariableGetterCode_VB"; + internal const string VariableGetterCode_CS = "VariableGetterCode_CS"; + internal const string VariableSetterCode_VB = "VariableSetterCode_VB"; + internal const string VariableSetterCode_CS = "VariableSetterCode_CS"; + internal const string StaticVariableGetterCode_VB = "StaticVariableGetterCode_VB"; + internal const string StaticVariableGetterCode_CS = "StaticVariableGetterCode_CS"; + internal const string StaticVariableSetterCode_VB = "StaticVariableSetterCode_VB"; + internal const string StaticVariableSetterCode_CS = "StaticVariableSetterCode_CS"; + internal const string EnterCodeBesidesCode_VB = "EnterCodeBesidesCode_VB"; + internal const string EnterCodeBesidesCode_CS = "EnterCodeBesidesCode_CS"; + internal const string LeaveCodeBesides1Code_VB = "LeaveCodeBesides1Code_VB"; + internal const string LeaveCodeBesides2Code_VB = "LeaveCodeBesides2Code_VB"; + internal const string LeaveCodeBesides1Code_CS = "LeaveCodeBesides1Code_CS"; + internal const string LeaveCodeBesides2Code_CS = "LeaveCodeBesides2Code_CS"; + internal const string VariableSetterName = "VariableSetterName"; + internal const string VariableGetterName = "VariableGetterName"; + internal const string HandlerGetterName = "HandlerGetterName"; + internal const string WorkflowCreatorName = "WorkflowCreatorName"; + internal const string ActivityMethod = "ActivityMethod"; + internal const string CustomActivityPrivateField = "CustomActivityPrivateField"; + internal const string InitializedVariableDeclaration_VB = "InitializedVariableDeclaration_VB"; + internal const string InitializedVariableDeclaration_CS = "InitializedVariableDeclaration_CS"; + internal const string In = "In"; + internal const string Out = "Out"; + internal const string Ref = "Ref"; + internal const string Required = "Required"; + internal const string Optional = "Optional"; + internal const string Parameters = "Parameters"; + internal const string Properties = "Properties"; + internal const string Error_RecursionDetected = "Error_RecursionDetected"; + internal const string Warning_UnverifiedRecursion = "Warning_UnverifiedRecursion"; + internal const string AddConstructorCode = "AddConstructorCode"; + internal const string Error_UninitializedCorrelation = "Error_UninitializedCorrelation"; + internal const string Error_CorrelationAlreadyInitialized = "Error_CorrelationAlreadyInitialized"; + internal const string Error_CorrelatedSendReceiveAtomicScope = "Error_CorrelatedSendReceiveAtomicScope"; + internal const string Warning_ActivityValidation = "Warning_ActivityValidation"; + internal const string Warning_EmptyBehaviourActivity = "Warning_EmptyBehaviourActivity"; + internal const string Error_ParallelActivationNoCorrelation = "Error_ParallelActivationNoCorrelation"; + internal const string Error_MethodNotAccessible = "Error_MethodNotAccessible"; + internal const string Error_FieldNotAccessible = "Error_FieldNotAccessible"; + internal const string Error_PropertyNotAccessible = "Error_PropertyNotAccessible"; + internal const string Error_GenericArgumentsNotAllowed = "Error_GenericArgumentsNotAllowed"; + internal const string Error_InvalidIdentifier = "Error_InvalidIdentifier"; + internal const string Error_InvalidLanguageIdentifier = "Error_InvalidLanguageIdentifier"; + internal const string DuplicateActivityIdentifier = "DuplicateActivityIdentifier"; + internal const string Error_MissingAttribute = "Error_MissingAttribute"; + internal const string Error_LoadUIPropertiesFile = "Error_LoadUIPropertiesFile"; + internal const string Error_SerializerEventGetFailed = "Error_SerializerEventGetFailed"; + internal const string Error_SerializerEventFailed = "Error_SerializerEventFailed"; + internal const string Error_SerializerNoMemberFound = "Error_SerializerNoMemberFound"; + internal const string Error_DynamicEventConflict = "Error_DynamicEventConflict"; + internal const string Error_SerializerMemberSetFailed = "Error_SerializerMemberSetFailed"; + internal const string Error_ContentPropertyCouldNotBeFound = "Error_ContentPropertyCouldNotBeFound"; + internal const string Error_ContentPropertyValueInvalid = "Error_ContentPropertyValueInvalid"; + internal const string Error_ContentPropertyNoSetter = "Error_ContentPropertyNoSetter"; + internal const string Error_ContentCanNotBeConverted = "Error_ContentCanNotBeConverted"; + internal const string Error_ContentPropertyCanNotBeNull = "Error_ContentPropertyCanNotBeNull"; + internal const string Error_SerializerTypeMismatch = "Error_SerializerTypeMismatch"; + internal const string Error_CouldNotAddValueInContentProperty = "Error_CouldNotAddValueInContentProperty"; + internal const string Error_SerializerTypeRequirement = "Error_SerializerTypeRequirement"; + internal const string Error_CanNotAddActivityInBlackBoxActivity = "Error_CanNotAddActivityInBlackBoxActivity"; + internal const string Error_ContentPropertyCanNotSupportCompactFormat = "Error_ContentPropertyCanNotSupportCompactFormat"; + internal const string Error_ContentPropertyNoMultipleContents = "Error_ContentPropertyNoMultipleContents"; + internal const string Error_InternalSerializerError = "Error_InternalSerializerError"; + internal const string Error_DictionarySerializerNonDictionaryObject = "Error_DictionarySerializerNonDictionaryObject"; + internal const string Error_DictionarySerializerKeyNotFound = "Error_DictionarySerializerKeyNotFound"; + internal const string Error_InvalidCancelActivityState = "Error_InvalidCancelActivityState"; + internal const string Error_InvalidCompensateActivityState = "Error_InvalidCompensateActivityState"; + internal const string Error_InvalidCloseActivityState = "Error_InvalidCloseActivityState"; + internal const string Error_SealedPropertyMetadata = "Error_SealedPropertyMetadata"; + internal const string Error_MemberNotFound = "Error_MemberNotFound"; + internal const string Error_EmptyPathValue = "Error_EmptyPathValue"; + internal const string Error_InvalidCompensatingState = "Error_InvalidCompensatingState"; + internal const string Error_InvalidCancelingState = "Error_InvalidCancelingState"; + internal const string Error_InvalidClosingState = "Error_InvalidClosingState"; + internal const string Error_InvalidStateToExecuteChild = "Error_InvalidStateToExecuteChild"; + internal const string Error_InvalidExecutionState = "Error_InvalidExecutionState"; + internal const string Error_InvalidInitializingState = "Error_InvalidInitializingState"; + internal const string Error_InvalidInvokingState = "Error_InvalidInvokingState"; + internal const string Error_NotRegisteredAs = "Error_NotRegisteredAs"; + internal const string Error_AlreadyRegisteredAs = "Error_AlreadyRegisteredAs"; + internal const string Error_InsertingChildControls = "Error_InsertingChildControls"; + internal const string Error_EmptyToolTipRectangle = "Error_EmptyToolTipRectangle"; + internal const string Error_EmptyRectangleValue = "Error_EmptyRectangleValue"; + internal const string Error_InvalidShadowRectangle = "Error_InvalidShadowRectangle"; + internal const string Error_InvalidShadowDepth = "Error_InvalidShadowDepth"; + internal const string Error_InvalidLightSource = "Error_InvalidLightSource"; + internal const string Error_ChangingDock = "Error_ChangingDock"; + internal const string Error_NullOrEmptyValue = "Error_NullOrEmptyValue"; + internal const string Error_InvalidStateImages = "Error_InvalidStateImages"; + internal const string Error_InvalidConnectorSegment = "Error_InvalidConnectorSegment"; + internal const string Error_InvalidConnectorSource = "Error_InvalidConnectorSource"; + internal const string Error_CreatingToolTip = "Error_CreatingToolTip"; + internal const string Error_InvalidDockStyle = "Error_InvalidDockStyle"; + internal const string Error_InvalidConnectorValue = "Error_InvalidConnectorValue"; + internal const string Error_InvalidDesignerVerbValue = "Error_InvalidDesignerVerbValue"; + internal const string Error_InvalidRuntimeType = "Error_InvalidRuntimeType"; + internal const string Error_InvalidArgumentValue = "Error_InvalidArgumentValue"; + internal const string Error_InvalidRadiusValue = "Error_InvalidRadiusValue"; + internal const string ToolTipString = "ToolTipString"; + + //Collection Editor Resources + internal const string CollectionEditorCaption = "CollectionEditorCaption"; + internal const string CollectionEditorProperties = "CollectionEditorProperties"; + internal const string CollectionEditorPropertiesMultiSelect = "CollectionEditorPropertiesMultiSelect"; + internal const string CollectionEditorPropertiesNone = "CollectionEditorPropertiesNone"; + internal const string CollectionEditorCantRemoveItem = "CollectionEditorCantRemoveItem"; + internal const string CollectionEditorUndoBatchDesc = "CollectionEditorUndoBatchDesc"; + internal const string CollectionEditorInheritedReadOnlySelection = "CollectionEditorInheritedReadOnlySelection"; + internal const string Error_ParameterAlreadyExists = "Error_ParameterAlreadyExists"; + internal const string Error_PropertyAlreadyExists = "Error_PropertyAlreadyExists"; + internal const string Error_HiddenPropertyAlreadyExists = "Error_HiddenPropertyAlreadyExists"; + internal const string Error_CorrelationInUse = "Error_CorrelationInUse"; + internal const string Error_ItemNotExists = "Error_ItemNotExists"; + internal const string Error_NoHelpAvailable = "Error_NoHelpAvailable"; + internal const string Error_DuplicateWorkflow = "Error_DuplicateWorkflow"; + internal const string Error_Recursion = "Error_Recursion"; + internal const string Error_RootActivity = "Error_RootActivity"; + internal const string Error_ConditionDefinitionDeserializationFailed = "Error_ConditionDefinitionDeserializationFailed"; + internal const string Error_InvalidConditionDefinition = "Error_InvalidConditionDefinition"; + internal const string SR_InvokeTransactionalFromAtomic = "SR_InvokeTransactionalFromAtomic"; + internal const string Error_SuspendInAtomicCallChain = "Error_SuspendInAtomicCallChain"; + internal const string Error_LiteralPassedToOutRef = "Error_LiteralPassedToOutRef"; + internal const string Error_GeneratorShouldContainSingleActivity = "Error_GeneratorShouldContainSingleActivity"; + internal const string Error_DeclaringPropertyNotSupported = "Error_DeclaringPropertyNotSupported"; + internal const string Error_DeclaringEventNotSupported = "Error_DeclaringEventNotSupported"; + internal const string Error_DynamicEventNotSupported = "Error_DynamicEventNotSupported"; + internal const string Error_DynamicPropertyNotSupported = "Error_DynamicPropertyNotSupported"; + internal const string Error_ParameterTypeResolution = "Error_ParameterTypeResolution"; + + // Dynamic Validations + internal const string Error_DynamicActivity = "Error_DynamicActivity"; + internal const string Error_DynamicActivity2 = "Error_DynamicActivity2"; + internal const string Error_CompilerValidationFailed = "Error_CompilerValidationFailed"; + internal const string Error_RuntimeValidationFailed = "Error_RuntimeValidationFailed"; + internal const string Error_TransactionAlreadyCanceled = "Error_TransactionAlreadyCanceled"; + internal const string Error_RemoveExecutingActivity = "Error_RemoveExecutingActivity"; + internal const string Error_InsideAtomicScope = "Error_InsideAtomicScope"; + internal const string SuspendReason_WorkflowChange = "SuspendReason_WorkflowChange"; + + //type filtering + internal const string FilterDescription_ParameterDeclaration = "FilterDescription_ParameterDeclaration"; + internal const string FilterDescription_GenericArgument = "FilterDescription_GenericArgument"; + + + internal const string LibraryPathIsInvalid = "LibraryPathIsInvalid"; + + // Activity Set + internal const string Error_CreateValidator = "Error_CreateValidator"; + internal const string Error_InvalidPackageFile = "Error_InvalidPackageFile"; + internal const string Error_AddAssemblyRef = "Error_AddAssemblyRef"; + internal const string Error_AssemblyBadImage = "Error_AssemblyBadImage"; + internal const string BindPropertySetterName = "BindPropertySetterName"; + + // Bind validations + internal const string Error_CannotResolveActivity = "Error_CannotResolveActivity"; + internal const string Error_CannotResolveRelativeActivity = "Error_CannotResolveRelativeActivity"; + internal const string Error_PathNotSetForActivitySource = "Error_PathNotSetForActivitySource"; + internal const string Error_InvalidMemberPath = "Error_InvalidMemberPath"; + internal const string Error_TargetTypeMismatch = "Error_TargetTypeMismatch"; + internal const string Warning_ParameterBinding = "Warning_ParameterBinding"; + internal const string Error_ReferencedActivityPropertyNotBind = "Error_ReferencedActivityPropertyNotBind"; + internal const string Error_TargetTypeDataSourcePathMismatch = "Error_TargetTypeDataSourcePathMismatch"; + internal const string Bind_ActivityDataSourceRecursionDetected = "Bind_ActivityDataSourceRecursionDetected"; + internal const string Bind_DuplicateDataSourceNames = "Bind_DuplicateDataSourceNames"; + internal const string Error_PathNotSetForXmlDataSource = "Error_PathNotSetForXmlDataSource"; + internal const string Error_XmlDocumentLoadFailed = "Error_XmlDocumentLoadFailed"; + internal const string Error_XmlDataSourceInvalidPath = "Error_XmlDataSourceInvalidPath"; + internal const string Error_XmlDataSourceMultipleNodes = "Error_XmlDataSourceMultipleNodes"; + internal const string Error_XmlDataSourceInvalidXPath = "Error_XmlDataSourceInvalidXPath"; + internal const string Error_InvalidObjectRefFormat = "Error_InvalidObjectRefFormat"; + internal const string Error_ReadOnlyDataSource = "Error_ReadOnlyDataSource"; + internal const string Error_HandlerReadOnly = "Error_HandlerReadOnly"; + internal const string Error_XmlDataSourceReadOnly = "Error_XmlDataSourceReadOnly"; + internal const string Error_DataSourceNotExist = "Error_DataSourceNotExist"; + internal const string Error_PropertyNoGetter = "Error_PropertyNoGetter"; + internal const string Error_PropertyNoSetter = "Error_PropertyNoSetter"; + internal const string Error_PropertyHasNoGetterDefined = "Error_PropertyHasNoGetterDefined"; + internal const string Error_PropertyHasNoSetterDefined = "Error_PropertyHasNoSetterDefined"; + internal const string Error_PropertyReferenceNoGetter = "Error_PropertyReferenceNoGetter"; + internal const string Error_PropertyReferenceGetterNoAccess = "Error_PropertyReferenceGetterNoAccess"; + internal const string Error_PropertyHasIndexParameters = "Error_PropertyHasIndexParameters"; + internal const string Error_ReadOnlyField = "Error_ReadOnlyField"; + internal const string Error_NoEnclosingContext = "Error_NoEnclosingContext"; + internal const string Error_NestedPersistOnClose = "Error_NestedPersistOnClose"; + internal const string Error_NestedCompensatableActivity = "Error_NestedCompensatableActivity"; + internal const string Error_InvalidActivityForObjectDatasource = "Error_InvalidActivityForObjectDatasource"; + internal const string Error_DataSourceTypeConversionFailed = "Error_DataSourceTypeConversionFailed"; + internal const string Error_BindDialogWrongPropertyType = "Error_BindDialogWrongPropertyType"; + internal const string Error_BindDialogNoValidPropertySelected = "Error_BindDialogNoValidPropertySelected"; + internal const string Error_BindDialogBindNotValid = "Error_BindDialogBindNotValid"; + internal const string Error_BindDialogCanNotBindToItself = "Error_BindDialogCanNotBindToItself"; + internal const string Error_BindActivityReference = "Error_BindActivityReference"; + internal const string Error_NoTargetTypeForMethod = "Error_NoTargetTypeForMethod"; + internal const string Error_MethodDataSourceIsReadOnly = "Error_MethodDataSourceIsReadOnly"; + internal const string Error_NotMethodDataSource = "Error_NotMethodDataSource"; + internal const string Error_MethodDataSourceWithPath = "Error_MethodDataSourceWithPath"; + internal const string Error_PathSyntax = "Error_PathSyntax"; + internal const string Error_UnmatchedParen = "Error_UnmatchedParen"; + internal const string Error_UnmatchedBracket = "Error_UnmatchedBracket"; + internal const string Error_MemberWithSameNameExists = "Error_MemberWithSameNameExists"; + internal const string Error_ActivityIdentifierCanNotBeEmpty = "Error_ActivityIdentifierCanNotBeEmpty"; + internal const string Error_InvalidActivityIdentifier = "Error_InvalidActivityIdentifier"; + internal const string Error_ActivityBindTypeConversionError = "Error_ActivityBindTypeConversionError"; + internal const string EmptyValue = "EmptyValue"; + internal const string Error_PropertyTypeNotDefined = "Error_PropertyTypeNotDefined"; + + internal const string Error_CompilationFailed = "Error_CompilationFailed"; + internal const string Error_MissingCompilationContext = "Error_MissingCompilationContext"; + + internal const string InvokeWorkflowReference_VB = "InvokeWorkflowReference_VB"; + internal const string InvokeWorkflowReference_CS = "InvokeWorkflowReference_CS"; + internal const string Error_InvalidListItem = "Error_InvalidListItem"; + + internal const string ParserMapPINoWhitespace = "ParserMapPINoWhitespace"; + internal const string ParserMapPIBadCharEqual = "ParserMapPIBadCharEqual"; + internal const string ParserMapPIBadCharQuote = "ParserMapPIBadCharQuote"; + internal const string ParserMapPIBadKey = "ParserMapPIBadKey"; + internal const string ParserMapPIMissingKey = "ParserMapPIMissingKey"; + internal const string ParserMapPIKeyNotSet = "ParserMapPIKeyNotSet"; + internal const string ParserMismatchDelimiter = "ParserMismatchDelimiter"; + internal const string ParserDanglingClause = "ParserDanglingClause"; + internal const string UnknownDefinitionTag = "UnknownDefinitionTag"; + internal const string CDATASection = "CDATASection"; + internal const string TextSection = "TextSection"; + internal const string IncorrectSyntax = "IncorrectSyntax"; + internal const string IncorrectTypeSyntax = "IncorrectTypeSyntax"; + internal const string Error_MultipleRootActivityCreator = "Error_MultipleRootActivityCreator"; + internal const string Error_MustHaveParent = "Error_MustHaveParent"; + + // Workflow References + internal const string Error_ReferenceObjNotInitialized = "Error_ReferenceObjNotInitialized"; + internal const string Error_ReferenceInitResourceManager = "Error_ReferenceInitResourceManager"; + internal const string Error_ResourceReferenceGetObject = "Error_ResourceReferenceGetObject"; + internal const string Error_RefBindCantFindRef = "Error_RefBindCantFindRef"; + internal const string Error_RefBindMissingReferenceName = "Error_RefBindMissingReferenceName"; + internal const string Error_RefBindMissingAttribute = "Error_RefBindMissingAttribute"; + internal const string Error_ReferenceLoad = "Error_ReferenceLoad"; + internal const string Error_ReferenceMissingAttribute = "Error_ReferenceMissingAttribute"; + internal const string Error_ReferenceInvalidResourceFile = "Error_ReferenceInvalidResourceFile"; + internal const string Error_ReferenceEmptyName = "Error_ReferenceEmptyName"; + + internal const string HandlerInvokerName = "HandlerInvokerName"; + internal const string HandlerInvokerSwitchPrefix_CS = "HandlerInvokerSwitchPrefix_CS"; + internal const string HandlerInvokerSwitchPrefix_VB = "HandlerInvokerSwitchPrefix_VB"; + internal const string HandlerInvokerSwitchSuffix_CS = "HandlerInvokerSwitchSuffix_CS"; + internal const string HandlerInvokerSwitchSuffix_VB = "HandlerInvokerSwitchSuffix_VB"; + internal const string HandlerInvokerCaseBegin_CS = "HandlerInvokerCaseBegin_CS"; + internal const string HandlerInvokerCaseBegin_VB = "HandlerInvokerCaseBegin_VB"; + + // Activity Category + internal const string Standard = "Standard"; + internal const string Base = "Base"; + + //CustomActivityDesigner + internal const string ValidatorCompanionClassDesc = "ValidatorCompanionClassDesc"; + internal const string ExecutorCompanionClassDesc = "ExecutorCompanionClassDesc"; + internal const string DesignerCompanionClassDesc = "DesignerCompanionClassDesc"; + internal const string CustomActivityBaseTypeDesc = "CustomActivityBaseTypeDesc"; + internal const string ActivityProperties = "ActivityProperties"; + internal const string ActivityPropertiesDesc = "ActivityPropertiesDesc"; + internal const string CompanionClasses = "CompanionClasses"; + internal const string ActivityDesc = "Activity"; + internal const string Error_TypeConversionFailed = "Error_TypeConversionFailed"; + internal const string SupportDataContext = "SupportDataContext"; + internal const string AdvancedCategory = "AdvancedCategory"; + internal const string SupportDataContextDesc = "SupportDataContextDesc"; + internal const string BaseCompanionClassName = "BaseCompanionClassName"; + internal const string BaseCompanionClassDesc = "BaseCompanionClassDesc"; + internal const string Designer = "Designer"; + internal const string Validator = "Validator"; + internal const string Executor = "Executor"; + internal const string BaseActivityType = "BaseActivityType"; + internal const string Error_NotBuiltInActivity = "Error_NotBuiltInActivity"; + internal const string NoChildActivities_Message = "NoChildActivities_Message"; + internal const string NoChildActivities_Caption = "NoChildActivities_Caption"; + internal const string Error_CustomActivityCantCreate = "Error_CustomActivityCantCreate"; + internal const string Error_CantChangeBuiltInActivity = "Error_CantChangeBuiltInActivity"; + internal const string Error_CantAddBeforeBuiltInActivity = "Error_CantAddBeforeBuiltInActivity"; + internal const string Error_CantAddAfterNonBuiltInActivity = "Error_CantAddAfterNonBuiltInActivity"; + internal const string Error_CannotAddRemoveChildActivities = "Error_CannotAddRemoveChildActivities"; + internal const string Error_CantFindBuiltInActivity = "Error_CantFindBuiltInActivity"; + internal const string Error_MissingBaseCompanionClassAttribute = "Error_MissingBaseCompanionClassAttribute"; + internal const string Error_CantFindBuiltInParent = "Error_CantFindBuiltInParent"; + internal const string Error_CantCreateInstanceOfBaseType = "Error_CantCreateInstanceOfBaseType"; + internal const string Error_CustomActivityTypeCouldNotBeFound = "Error_CustomActivityTypeCouldNotBeFound"; + internal const string None = "None"; + internal const string AtomicTransaction = "AtomicTransaction"; + internal const string LocalDataContext = "LocalDataContext"; + internal const string LocalDataContextDesc = "LocalDataContextDesc"; + internal const string CompanionClass = "CompanionClass"; + internal const string Error_AlreadyRootActivity = "Error_AlreadyRootActivity"; + internal const string RootActivityName = "RootActivityName"; + internal const string RootActivityNameDesc = "RootActivityNameDesc"; + internal const string CustomProperties = "CustomProperties"; + internal const string VisibleDescr = "VisibleDescr"; + internal const string EditableDescr = "EditableDescr"; + internal const string Error_CantCreateMethod = "Error_CantCreateMethod"; + internal const string Error_CantEditNullValue = "Error_CantEditNullValue"; + internal const string Error_CompanionTypeNotSet = "Error_CompanionTypeNotSet"; + internal const string Error_CompanionClassNameCanNotBeEmpty = "Error_CompanionClassNameCanNotBeEmpty"; + internal const string Error_CouldNotEmitFieldInLocalDataContext = "Error_CouldNotEmitFieldInLocalDataContext"; + internal const string Error_CouldNotEmitMethodInLocalDataContext = "Error_CouldNotEmitMethodInLocalDataContext"; + internal const string Error_DerivationFromTypeWithLocalDataContext = "Error_DerivationFromTypeWithLocalDataContext"; + internal const string Error_CompanionTypeDerivationError = "Error_CompanionTypeDerivationError"; + internal const string Error_CantCreateDataContextClass = "Error_CantCreateDataContextClass"; + internal const string ArrayExistingBind = "ArrayExistingBind"; + internal const string Error_NoMatchingFieldsOrProperties = "Error_NoMatchingFieldsOrProperties"; + internal const string ChooseFieldPropertyDatasource = "ChooseFieldPropertyDatasource"; + + internal const string SupportsTransaction = "SupportsTransaction"; + internal const string SupportsExceptions = "SupportsExceptions"; + internal const string SupportsCancellationHandlerActivity = "SupportsCancellationHandlerActivity"; + internal const string SupportsEvents = "SupportsEvents"; + internal const string SupportsDataSources = "SupportsDataSources"; + internal const string SupportsCompensationHandler = "SupportsCompensationHandler"; + internal const string SupportsCompensationHandlerDesc = "SupportsCompensationHandlerDesc"; + internal const string SupportsTransactionDesc = "SupportsTransactionDesc"; + internal const string SupportsExceptionsDesc = "SupportsExceptionsDesc"; + internal const string SupportsCancelHandlerDesc = "SupportsCancelHandlerDesc"; + internal const string SupportsEventsDesc = "SupportsEventsDesc"; + internal const string TransactionDesc = "TransactionDesc"; + + internal const string Error_BaseTypeMustBeActivity = "Error_BaseTypeMustBeActivity"; + internal const string ExistingActivityBindTitle = "ExistingActivityBindTitle"; + internal const string ExistingActivityBindLabel = "ExistingActivityBindLabel"; + internal const string ExistingFieldPropBindTitle = "ExistingFieldPropBindTitle"; + internal const string ExistingFieldPropBindLabel = "ExistingFieldPropBindLabel"; + internal const string ProvidesSynchronization = "ProvidesSynchronization"; + internal const string ProvidesSynchronizationDesc = "ProvidesSynchronizationDesc"; + internal const string SynchronizationHandles = "SynchronizationHandles"; + internal const string SynchronizationHandlesDesc = "SynchronizationHandlesDesc"; + + internal const string Error_TransactionAlreadyApplied = "Error_TransactionAlreadyApplied"; + internal const string Error_BindBaseTypeNotSpecified = "Error_BindBaseTypeNotSpecified"; + internal const string NonDelegateTargetType = "NonDelegateTargetType"; + internal const string Error_ClassnameNotInRootNamespace = "Error_ClassnameNotInRootNamespace"; + internal const string Error_CantUseCurrentProjectTypeAsBase = "Error_CantUseCurrentProjectTypeAsBase"; + internal const string Error_UnboundGenericType = "Error_UnboundGenericType"; + internal const string Error_UnboundGenericTypeDataSource = "Error_UnboundGenericTypeDataSource"; + internal const string Error_BaseTypeUnknown = "Error_BaseTypeUnknown"; + internal const string Error_UnconfiguredBind = "Error_UnconfiguredBind"; + internal const string Error_CanNotEmitMemberInLocalDataContext = "Error_CanNotEmitMemberInLocalDataContext"; + internal const string Error_DesignedTypeNotFound = "Error_DesignedTypeNotFound"; + internal const string Error_PathCouldNotBeResolvedToMember = "Error_PathCouldNotBeResolvedToMember"; + internal const string Error_EdittingNullCollection = "Error_EdittingNullCollection"; + internal const string Error_MoreThanOneCompensationDecl = "Error_MoreThanOneCompensationDecl"; + internal const string Error_ParentDoesNotSupportCompensation = "Error_ParentDoesNotSupportCompensation"; + internal const string Error_CantResolveEventHandler = "Error_CantResolveEventHandler"; + internal const string Error_XSDObjectTypeNotSerializable = "Error_XSDObjectTypeNotSerializable"; + internal const string AEC_InvalidActivity = "AEC_InvalidActivity"; + internal const string GetDynamicActivities_InvalidActivity = "GetDynamicActivities_InvalidActivity"; + internal const string AEC_InvalidNestedActivity = "AEC_InvalidNestedActivity"; + internal const string Error_IDNotSetForActivitySource = "Error_IDNotSetForActivitySource"; + internal const string Error_InvalidCustomPropertyName = "Error_InvalidCustomPropertyName"; + internal const string Error_InvalidCustomPropertyType = "Error_InvalidCustomPropertyType"; + + internal const string Error_DPReadOnly = "Error_DPReadOnly"; + internal const string Error_DPMetaPropertyBinding = "Error_DPMetaPropertyBinding"; + internal const string Error_DPSetValueBind = "Error_DPSetValueBind"; + internal const string Error_DPSetValueHandler = "Error_DPSetValueHandler"; + internal const string Error_DPGetValueHandler = "Error_DPGetValueHandler"; + internal const string Error_DPAddHandlerNonEvent = "Error_DPAddHandlerNonEvent"; + internal const string Error_DPAddHandlerMetaProperty = "Error_DPAddHandlerMetaProperty"; + internal const string Error_DPRemoveHandlerBind = "Error_DPRemoveHandlerBind"; + internal const string Error_LanguageNeedsToBeVBCSharp = "Error_LanguageNeedsToBeVBCSharp"; + internal const string Error_TargetFxNotSupported = "Error_TargetFxNotSupported"; + internal const string Error_CantConvertValueValue = "Error_CantConvertValueValue"; + internal const string Error_TypeIsNotValid = "Error_TypeIsNotValid"; + internal const string Error_TypePropertyInvalid = "Error_TypePropertyInvalid"; + internal const string Error_EventCantBeMetaProperty = "Error_EventCantBeMetaProperty"; + internal const string Error_EventMustBeDelegate = "Error_EventMustBeDelegate"; + internal const string Error_DPPropertyTypeMissing = "Error_DPPropertyTypeMissing"; + + internal const string TransactionalContextActivityDescription = "TransactionalContextActivityDescription"; + internal const string CompensatableTransactionalContextActivityDescription = "CompensatableTransactionalContextActivityDescription"; + internal const string SynchronizationScopeActivityDescription = "SynchronizationScopeActivityDescription"; + internal const string SequenceActivityDescription = "SequenceActivityDescription"; + internal const string CompensateActivityDescription = "CompensateActivityDescription"; + internal const string Error_CompensateBadTargetTX = "Error_CompensateBadTargetTX"; + internal const string Error_CancelHandlerParentNotScope = "Error_CancelHandlerParentNotScope"; + internal const string FaultHandlerActivityDescription = "FaultHandlerActivityDescription"; + internal const string Error_ExceptionTypeNotException = "Error_ExceptionTypeNotException"; + internal const string Error_FaultIsNotOfFaultType = "Error_FaultIsNotOfFaultType"; + internal const string Error_FaultTypeNoDefaultConstructor = "Error_FaultTypeNoDefaultConstructor"; + internal const string FilterDescription_FaultHandlerActivity = "FilterDescription_FaultHandlerActivity"; + internal const string Error_FaultHandlerActivityParentNotFaultHandlersActivity = "Error_FaultHandlerActivityParentNotFaultHandlersActivity"; + internal const string Error_FaultHandlerActivityAllMustBeLast = "Error_FaultHandlerActivityAllMustBeLast"; + internal const string Error_FaultHandlersActivityDeclNotAllFaultHandlerActivityDecl = "Error_FaultHandlersActivityDeclNotAllFaultHandlerActivityDecl"; + internal const string Error_FaultHandlerActivityWrongOrder = "Error_FaultHandlerActivityWrongOrder"; + internal const string Error_SenderMustBeActivityExecutionContext = "Error_SenderMustBeActivityExecutionContext"; + internal const string Error_XomlWorkflowHasCode = "Error_XomlWorkflowHasCode"; + internal const string Error_WrongParamForActivityResolveEventArgs = "Error_WrongParamForActivityResolveEventArgs"; + internal const string Error_ValidatorThrewException = "Error_ValidatorThrewException"; + internal const string Error_Missing_CanModifyProperties_True = "Error_Missing_CanModifyProperties_True"; + internal const string Error_Missing_CanModifyProperties_False = "Error_Missing_CanModifyProperties_False"; + internal const string Error_ModelingConstructsCanNotContainModelingConstructs = "Error_ModelingConstructsCanNotContainModelingConstructs"; + internal const string Error_RootIsNotEnabled = "Error_RootIsNotEnabled"; + internal const string Error_MissingSetAccessor = "Error_MissingSetAccessor"; + internal const string Error_MissingAddHandler = "Error_MissingAddHandler"; + internal const string Error_MissingCLRProperty = "Error_MissingCLRProperty"; + + internal const string Error_NotReadOnlyProperty = "Error_NotReadOnlyProperty"; + internal const string Error_InvalidDependencyProperty = "Error_InvalidDependencyProperty"; + internal const string Error_ActivityNameExist = "Error_ActivityNameExist"; + internal const string CannotCreateAttribute = "CannotCreateAttribute"; + internal const string NamespaceAndDeclaringTypeCannotBeNull = "NamespaceAndDeclaringTypeCannotBeNull"; + internal const string NotElementType = "NotElementType"; + + //Layout persistence errors + internal const string Error_LayoutSerializationActivityNotFound = "Error_LayoutSerializationActivityNotFound"; + internal const string Error_LayoutSerializationAssociatedActivityNotFound = "Error_LayoutSerializationAssociatedActivityNotFound"; + internal const string Error_LayoutSerializationPersistenceSupport = "Error_LayoutSerializationPersistenceSupport"; + internal const string Error_LayoutSerializationRootDesignerNotFound = "Error_LayoutSerializationRootDesignerNotFound"; + internal const string Error_ParameterCannotBeEmpty = "Error_ParameterCannotBeEmpty"; + internal const string InvalidExecutionStatus = "InvalidExecutionStatus"; + internal const string Error_LayoutDeserialization = "Error_LayoutDeserialization"; + internal const string Error_LayoutSerialization = "Error_LayoutSerialization"; + + internal const string Error_SerializerStackOverflow = "Error_SerializerStackOverflow"; + internal const string Error_InvalidActivityForWorkflowChanges = "Error_InvalidActivityForWorkflowChanges"; + internal const string Error_InvalidMemberType = "Error_InvalidMemberType"; + internal const string Error_BindPathNullValue = "Error_BindPathNullValue"; + internal const string Error_MarkupExtensionMissingTerminatingCharacter = "Error_MarkupExtensionMissingTerminatingCharacter"; + internal const string Error_MarkupExtensionDeserializeFailed = "Error_MarkupExtensionDeserializeFailed"; + internal const string Error_ApplyDynamicChangeFailed = "Error_ApplyDynamicChangeFailed"; + internal const string Error_ActivityCircularReference = "Error_ActivityCircularReference"; + internal const string Error_ValidatorTypeIsInvalid = "Error_ValidatorTypeIsInvalid"; + internal const string Error_InvalidServiceProvider = "Error_InvalidServiceProvider"; + internal const string Error_InvalidRootForWorkflowChanges = "Error_InvalidRootForWorkflowChanges"; + internal const string Error_ExtraCharacterFoundAtEnd = "Error_ExtraCharacterFoundAtEnd"; + internal const string Error_WorkflowChangesNotSupported = "Error_WorkflowChangesNotSupported"; + internal const string Error_TypeSystemAttributeArgument = "Error_TypeSystemAttributeArgument"; + + internal const string Error_InvalidElementFoundForType = "Error_InvalidElementFoundForType"; + internal const string Error_ActivitySaveLoadNotCalled = "Error_ActivitySaveLoadNotCalled"; + internal const string Error_CanNotBindProperty = "Error_CanNotBindProperty"; +} +#pragma warning restore CS8603 // 可能返回 null 引用。 + +#endif \ No newline at end of file diff --git a/src/IFoxCAD.Basal/Sortedset/Sortedset.cs b/src/IFoxCAD.Basal/Sortedset/Sortedset.cs new file mode 100644 index 0000000..9405fc1 --- /dev/null +++ b/src/IFoxCAD.Basal/Sortedset/Sortedset.cs @@ -0,0 +1,2935 @@ +#if NET35 +#pragma warning disable CS8603 // 可能返回 null 引用。 +#pragma warning disable CS8601 // 引用类型赋值可能为 null。 +#pragma warning disable CS8618 // 在退出构造函数时,不可为 null 的字段必须包含非 null 值。请考虑声明为可以为 null。 +#pragma warning disable CS8625 // 无法将 null 字面量转换为非 null 的引用类型。 +#pragma warning disable IDE0059 // 不需要赋值 +#pragma warning disable CS8600 // 将 null 字面量或可能为 null 的值转换为非 null 类型。 +#pragma warning disable CS8602 // 解引用可能出现空引用。 +#pragma warning disable CS8604 // 引用类型参数可能为 null。 +// #define USING_HASH_SET +// ==++== +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +// ==--== +/*============================================================ +** +** Class: SortedSet +** +** Purpose: A generic sorted set. +** +** Date: August 15, 2008 +** +===========================================================*/ + + +namespace System.Collections.Generic +{ + using System; + using System.Diagnostics; + using System.Diagnostics.CodeAnalysis; + using System.Runtime.Serialization; + + + + // + // A binary search tree is a red-black tree if it satisfies the following red-black properties: + // 1. Every node is either red or black + // 2. Every leaf (nil node) is black + // 3. If a node is red, then both its children are black + // 4. Every simple path from a node to a descendant leaf contains the same number of black nodes + // + // The basic idea of red-black tree is to represent 2-3-4 trees as standard BSTs but to add one extra bit of information + // per node to encode 3-nodes and 4-nodes. + // 4-nodes will be represented as: B + // R R + // 3 -node will be represented as: B or B + // R B B R + // + // For a detailed description of the algorithm, take a look at "Algorithms" by Robert Sedgewick. + // + + internal delegate bool TreeWalkPredicate(SortedSet.Node node); + + internal enum TreeRotation + { + LeftRotation = 1, + RightRotation = 2, + RightLeftRotation = 3, + LeftRightRotation = 4, + } + + [SuppressMessage("Microsoft.Naming", "CA1710:IdentifiersShouldHaveCorrectSuffix", Justification = "by design name choice")] + [DebuggerTypeProxy(nameof(SortedSet))]/*这句改了*/ + [DebuggerDisplay("Count = {Count}")] +#if !FEATURE_NETCORE + [Serializable] + public class SortedSet : ISet, ICollection, ICollection, ISerializable, IDeserializationCallback//, IReadOnlyCollection + { +#else + public class SortedSet : ISet, ICollection, ICollection, IReadOnlyCollection { +#endif //!FEATURE_NETCORE + #region local variables/constants + Node root; + IComparer comparer; + int count; + int version; + private Object _syncRoot; + + private const String ComparerName = "Comparer"; + private const String CountName = "Count"; + private const String ItemsName = "Items"; + private const String VersionName = "Version"; + //needed for enumerator + private const String TreeName = "Tree"; + private const String NodeValueName = "Item"; + private const String EnumStartName = "EnumStarted"; + private const String ReverseName = "Reverse"; + private const String EnumVersionName = "EnumVersion"; + +#if !FEATURE_NETCORE + //needed for TreeSubset + private const String minName = "Min"; + private const String maxName = "Max"; + private const String lBoundActiveName = "lBoundActive"; + private const String uBoundActiveName = "uBoundActive"; + + private SerializationInfo siInfo; //A temporary variable which we need during deserialization. +#endif + internal const int StackAllocThreshold = 100; + + #endregion + + #region Constructors + public SortedSet() + { + this.comparer = Comparer.Default; + + } + + public SortedSet(IComparer comparer) + { + if (comparer == null) + { + this.comparer = Comparer.Default; + } + else + { + this.comparer = comparer; + } + } + + + public SortedSet(IEnumerable collection) : this(collection, Comparer.Default) { } + + public SortedSet(IEnumerable collection, IComparer comparer) + : this(comparer) + { + + if (collection == null) + { + throw new ArgumentNullException("collection"); + } + + // these are explicit type checks in the mould of HashSet. It would have worked better + // with something like an ISorted (we could make this work for SortedList.Keys etc) + SortedSet baseSortedSet = collection as SortedSet; + SortedSet baseTreeSubSet = collection as TreeSubSet; + if (baseSortedSet != null && baseTreeSubSet == null && AreComparersEqual(this, baseSortedSet)) + { + //breadth first traversal to recreate nodes + if (baseSortedSet.Count == 0) + { + count = 0; + version = 0; + root = null; + return; + } + + + //pre order way to replicate nodes + Stack theirStack = new Stack.Node>(2 * log2(baseSortedSet.Count) + 2); + Stack myStack = new Stack.Node>(2 * log2(baseSortedSet.Count) + 2); + Node theirCurrent = baseSortedSet.root; + Node myCurrent = (theirCurrent != null ? new SortedSet.Node(theirCurrent.Item, theirCurrent.IsRed) : null); + root = myCurrent; + while (theirCurrent != null) + { + theirStack.Push(theirCurrent); + myStack.Push(myCurrent); + myCurrent.Left = (theirCurrent.Left != null ? new SortedSet.Node(theirCurrent.Left.Item, theirCurrent.Left.IsRed) : null); + theirCurrent = theirCurrent.Left; + myCurrent = myCurrent.Left; + } + while (theirStack.Count != 0) + { + theirCurrent = theirStack.Pop(); + myCurrent = myStack.Pop(); + Node theirRight = theirCurrent.Right; + Node myRight = null; + if (theirRight != null) + { + myRight = new SortedSet.Node(theirRight.Item, theirRight.IsRed); + } + myCurrent.Right = myRight; + + while (theirRight != null) + { + theirStack.Push(theirRight); + myStack.Push(myRight); + myRight.Left = (theirRight.Left != null ? new SortedSet.Node(theirRight.Left.Item, theirRight.Left.IsRed) : null); + theirRight = theirRight.Left; + myRight = myRight.Left; + } + } + count = baseSortedSet.count; + version = 0; + } + else + { //As it stands, you're doing an NlogN sort of the collection + + List els = new List(collection); + els.Sort(this.comparer); + for (int i = 1; i < els.Count; i++) + { + if (comparer.Compare(els[i], els[i - 1]) == 0) + { + els.RemoveAt(i); + i--; + } + } + root = ConstructRootFromSortedArray(els.ToArray(), 0, els.Count - 1, null); + count = els.Count; + version = 0; + } + } + + +#if !FEATURE_NETCORE + + protected SortedSet(SerializationInfo info, StreamingContext context) + { + siInfo = info; + } +#endif + #endregion + + #region Bulk Operation Helpers + private void AddAllElements(IEnumerable collection) + { + + foreach (T item in collection) + { + if (!this.Contains(item)) + Add(item); + } + } + + private void RemoveAllElements(IEnumerable collection) + { + T min = this.Min; + T max = this.Max; + foreach (T item in collection) + { + if (!(comparer.Compare(item, min) < 0 || comparer.Compare(item, max) > 0) && this.Contains(item)) + this.Remove(item); + } + } + + private bool ContainsAllElements(IEnumerable collection) + { + foreach (T item in collection) + { + if (!this.Contains(item)) + { + return false; + } + } + return true; + } + + // + // Do a in order walk on tree and calls the delegate for each node. + // If the action delegate returns false, stop the walk. + // + // Return true if the entire tree has been walked. + // Otherwise returns false. + // + internal bool InOrderTreeWalk(TreeWalkPredicate action) + { + return InOrderTreeWalk(action, false); + } + + // Allows for the change in traversal direction. Reverse visits nodes in descending order + internal virtual bool InOrderTreeWalk(TreeWalkPredicate action, bool reverse) + { + if (root == null) + { + return true; + } + + // The maximum height of a red-black tree is 2*lg(n+1). + // See page 264 of "Introduction to algorithms" by Thomas H. Cormen + // note: this should be logbase2, but since the stack grows itself, we + // don't want the extra cost + Stack stack = new Stack(2 * (int)(SortedSet.log2(Count + 1))); + Node current = root; + while (current != null) + { + stack.Push(current); + current = (reverse ? current.Right : current.Left); + } + while (stack.Count != 0) + { + current = stack.Pop(); + if (!action(current)) + { + return false; + } + + Node node = (reverse ? current.Left : current.Right); + while (node != null) + { + stack.Push(node); + node = (reverse ? node.Right : node.Left); + } + } + return true; + } + + // + // Do a left to right breadth first walk on tree and + // calls the delegate for each node. + // If the action delegate returns false, stop the walk. + // + // Return true if the entire tree has been walked. + // Otherwise returns false. + // + internal virtual bool BreadthFirstTreeWalk(TreeWalkPredicate action) + { + if (root == null) + { + return true; + } + + List processQueue = new List(); + processQueue.Add(root); + Node current; + + while (processQueue.Count != 0) + { + current = processQueue[0]; + processQueue.RemoveAt(0); + if (!action(current)) + { + return false; + } + if (current.Left != null) + { + processQueue.Add(current.Left); + } + if (current.Right != null) + { + processQueue.Add(current.Right); + } + } + return true; + } + #endregion + + #region Properties + public int Count + { + get + { + VersionCheck(); + return count; + } + } + + public IComparer Comparer + { + get + { + return comparer; + } + } + + bool ICollection.IsReadOnly + { + get + { + return false; + } + } + + bool ICollection.IsSynchronized + { + get + { + return false; + } + } + + object ICollection.SyncRoot + { + get + { + if (_syncRoot == null) + { + System.Threading.Interlocked.CompareExchange(ref _syncRoot, new Object(), null); + } + return _syncRoot; + } + } + #endregion + + #region Subclass helpers + + //virtual function for subclass that needs to update count + internal virtual void VersionCheck() { } + + + //virtual function for subclass that needs to do range checks + internal virtual bool IsWithinRange(T item) + { + return true; + + } + #endregion + + #region ICollection Members + /// + /// Add the value ITEM to the tree, returns true if added, false if duplicate + /// + /// item to be added + public bool Add(T item) + { + return AddIfNotPresent(item); + } + + void ICollection.Add(T item) + { + AddIfNotPresent(item); + } + + + /// + /// Adds ITEM to the tree if not already present. Returns TRUE if value was successfully added + /// or FALSE if it is a duplicate + /// + internal virtual bool AddIfNotPresent(T item) + { + if (root == null) + { // empty tree + root = new Node(item, false); + count = 1; + version++; + return true; + } + + // + // Search for a node at bottom to insert the new node. + // If we can guanratee the node we found is not a 4-node, it would be easy to do insertion. + // We split 4-nodes along the search path. + // + Node current = root; + Node parent = null; + Node grandParent = null; + Node greatGrandParent = null; + + //even if we don't actually add to the set, we may be altering its structure (by doing rotations + //and such). so update version to disable any enumerators/subsets working on it + version++; + + + int order = 0; + while (current != null) + { + order = comparer.Compare(item, current.Item); + if (order == 0) + { + // We could have changed root node to red during the search process. + // We need to set it to black before we return. + root.IsRed = false; + return false; + } + + // split a 4-node into two 2-nodes + if (Is4Node(current)) + { + Split4Node(current); + // We could have introduced two consecutive red nodes after split. Fix that by rotation. + if (IsRed(parent)) + { + InsertionBalance(current, ref parent, grandParent, greatGrandParent); + } + } + greatGrandParent = grandParent; + grandParent = parent; + parent = current; + current = (order < 0) ? current.Left : current.Right; + } + + Debug.Assert(parent != null, "Parent node cannot be null here!"); + // ready to insert the new node + Node node = new Node(item); + if (order > 0) + { + parent.Right = node; + } + else + { + parent.Left = node; + } + + // the new node will be red, so we will need to adjust the colors if parent node is also red + if (parent.IsRed) + { + InsertionBalance(node, ref parent, grandParent, greatGrandParent); + } + + // Root node is always black + root.IsRed = false; + ++count; + return true; + } + + /// + /// Remove the T ITEM from this SortedSet. Returns true if successfully removed. + /// + /// + /// + public bool Remove(T item) + { + return this.DoRemove(item); // hack so it can be made non-virtual + } + + internal virtual bool DoRemove(T item) + { + + if (root == null) + { + return false; + } + + + // Search for a node and then find its succesor. + // Then copy the item from the succesor to the matching node and delete the successor. + // If a node doesn't have a successor, we can replace it with its left child (if not empty.) + // or delete the matching node. + // + // In top-down implementation, it is important to make sure the node to be deleted is not a 2-node. + // Following code will make sure the node on the path is not a 2 Node. + + //even if we don't actually remove from the set, we may be altering its structure (by doing rotations + //and such). so update version to disable any enumerators/subsets working on it + version++; + + Node current = root; + Node parent = null; + Node grandParent = null; + Node match = null; + Node parentOfMatch = null; + bool foundMatch = false; + while (current != null) + { + if (Is2Node(current)) + { // fix up 2-Node + if (parent == null) + { // current is root. Mark it as red + current.IsRed = true; + } + else + { + Node sibling = GetSibling(current, parent); + if (sibling.IsRed) + { + // If parent is a 3-node, flip the orientation of the red link. + // We can acheive this by a single rotation + // This case is converted to one of other cased below. + Debug.Assert(!parent.IsRed, "parent must be a black node!"); + if (parent.Right == sibling) + { + RotateLeft(parent); + } + else + { + RotateRight(parent); + } + + parent.IsRed = true; + sibling.IsRed = false; // parent's color + // sibling becomes child of grandParent or root after rotation. Update link from grandParent or root + ReplaceChildOfNodeOrRoot(grandParent, parent, sibling); + // sibling will become grandParent of current node + grandParent = sibling; + if (parent == match) + { + parentOfMatch = sibling; + } + + // update sibling, this is necessary for following processing + sibling = (parent.Left == current) ? parent.Right : parent.Left; + } + Debug.Assert(sibling != null || sibling.IsRed == false, "sibling must not be null and it must be black!"); + + if (Is2Node(sibling)) + { + Merge2Nodes(parent, current, sibling); + } + else + { + // current is a 2-node and sibling is either a 3-node or a 4-node. + // We can change the color of current to red by some rotation. + TreeRotation rotation = RotationNeeded(parent, current, sibling); + Node newGrandParent = null; + switch (rotation) + { + case TreeRotation.RightRotation: + Debug.Assert(parent.Left == sibling, "sibling must be left child of parent!"); + Debug.Assert(sibling.Left.IsRed, "Left child of sibling must be red!"); + sibling.Left.IsRed = false; + newGrandParent = RotateRight(parent); + break; + case TreeRotation.LeftRotation: + Debug.Assert(parent.Right == sibling, "sibling must be left child of parent!"); + Debug.Assert(sibling.Right.IsRed, "Right child of sibling must be red!"); + sibling.Right.IsRed = false; + newGrandParent = RotateLeft(parent); + break; + + case TreeRotation.RightLeftRotation: + Debug.Assert(parent.Right == sibling, "sibling must be left child of parent!"); + Debug.Assert(sibling.Left.IsRed, "Left child of sibling must be red!"); + newGrandParent = RotateRightLeft(parent); + break; + + case TreeRotation.LeftRightRotation: + Debug.Assert(parent.Left == sibling, "sibling must be left child of parent!"); + Debug.Assert(sibling.Right.IsRed, "Right child of sibling must be red!"); + newGrandParent = RotateLeftRight(parent); + break; + } + + newGrandParent.IsRed = parent.IsRed; + parent.IsRed = false; + current.IsRed = true; + ReplaceChildOfNodeOrRoot(grandParent, parent, newGrandParent); + if (parent == match) + { + parentOfMatch = newGrandParent; + } + grandParent = newGrandParent; + } + } + } + + // we don't need to compare any more once we found the match + int order = foundMatch ? -1 : comparer.Compare(item, current.Item); + if (order == 0) + { + // save the matching node + foundMatch = true; + match = current; + parentOfMatch = parent; + } + + grandParent = parent; + parent = current; + + if (order < 0) + { + current = current.Left; + } + else + { + current = current.Right; // continue the search in right sub tree after we find a match + } + } + + // move successor to the matching node position and replace links + if (match != null) + { + ReplaceNode(match, parentOfMatch, parent, grandParent); + --count; + } + + if (root != null) + { + root.IsRed = false; + } + return foundMatch; + } + + public virtual void Clear() + { + root = null; + count = 0; + ++version; + } + + + public virtual bool Contains(T item) + { + + return FindNode(item) != null; + } + + + + + public void CopyTo(T[] array) { CopyTo(array, 0, Count); } + + public void CopyTo(T[] array, int index) { CopyTo(array, index, Count); } + + public void CopyTo(T[] array, int index, int count) + { + if (array == null) + { + ThrowHelper.ThrowArgumentNullException(ExceptionArgument.array); + } + + if (index < 0) + { + ThrowHelper.ThrowArgumentOutOfRangeException(ExceptionArgument.index); + } + + if (count < 0) + { + throw new ArgumentOutOfRangeException("count", SR.GetString("SR.ArgumentOutOfRange_NeedNonNegNum")); + } + + // will array, starting at arrayIndex, be able to hold elements? Note: not + // checking arrayIndex >= array.Length (consistency with list of allowing + // count of 0; subsequent check takes care of the rest) + if (index > array.Length || count > array.Length - index) + { + throw new ArgumentException(SR.GetString("SR.Arg_ArrayPlusOffTooSmall")); + } + //upper bound + count += index; + + InOrderTreeWalk(delegate (Node node) + { + if (index >= count) + { + return false; + } + else + { + array[index++] = node.Item; + return true; + } + }); + } + + void ICollection.CopyTo(Array array, int index) + { + if (array == null) + { + ThrowHelper.ThrowArgumentNullException(ExceptionArgument.array); + } + + if (array.Rank != 1) + { + ThrowHelper.ThrowArgumentException(ExceptionResource.Arg_RankMultiDimNotSupported); + } + + if (array.GetLowerBound(0) != 0) + { + ThrowHelper.ThrowArgumentException(ExceptionResource.Arg_NonZeroLowerBound); + } + + if (index < 0) + { + ThrowHelper.ThrowArgumentOutOfRangeException(ExceptionArgument.arrayIndex, ExceptionResource.ArgumentOutOfRange_NeedNonNegNum); + } + + if (array.Length - index < Count) + { + ThrowHelper.ThrowArgumentException(ExceptionResource.Arg_ArrayPlusOffTooSmall); + } + + T[] tarray = array as T[]; + if (tarray != null) + { + CopyTo(tarray, index); + } + else + { + object[] objects = array as object[]; + if (objects == null) + { + ThrowHelper.ThrowArgumentException(ExceptionResource.Argument_InvalidArrayType); + } + + try + { + InOrderTreeWalk(delegate (Node node) { objects[index++] = node.Item; return true; }); + } + catch (ArrayTypeMismatchException) + { + ThrowHelper.ThrowArgumentException(ExceptionResource.Argument_InvalidArrayType); + } + } + } + + #endregion + + #region IEnumerable members + public Enumerator GetEnumerator() + { + return new Enumerator(this); + } + + + + + IEnumerator IEnumerable.GetEnumerator() + { + return new Enumerator(this); + } + + IEnumerator IEnumerable.GetEnumerator() + { + return new Enumerator(this); + } + #endregion + + #region Tree Specific Operations + + private static Node GetSibling(Node node, Node parent) + { + if (parent.Left == node) + { + return parent.Right; + } + return parent.Left; + } + + // After calling InsertionBalance, we need to make sure current and parent up-to-date. + // It doesn't matter if we keep grandParent and greatGrantParent up-to-date + // because we won't need to split again in the next node. + // By the time we need to split again, everything will be correctly set. + // + private void InsertionBalance(Node current, ref Node parent, Node grandParent, Node greatGrandParent) + { + Debug.Assert(grandParent != null, "Grand parent cannot be null here!"); + bool parentIsOnRight = (grandParent.Right == parent); + bool currentIsOnRight = (parent.Right == current); + + Node newChildOfGreatGrandParent; + if (parentIsOnRight == currentIsOnRight) + { // same orientation, single rotation + newChildOfGreatGrandParent = currentIsOnRight ? RotateLeft(grandParent) : RotateRight(grandParent); + } + else + { // different orientaton, double rotation + newChildOfGreatGrandParent = currentIsOnRight ? RotateLeftRight(grandParent) : RotateRightLeft(grandParent); + // current node now becomes the child of greatgrandparent + parent = greatGrandParent; + } + // grand parent will become a child of either parent of current. + grandParent.IsRed = true; + newChildOfGreatGrandParent.IsRed = false; + + ReplaceChildOfNodeOrRoot(greatGrandParent, grandParent, newChildOfGreatGrandParent); + } + + private static bool Is2Node(Node node) + { + Debug.Assert(node != null, "node cannot be null!"); + return IsBlack(node) && IsNullOrBlack(node.Left) && IsNullOrBlack(node.Right); + } + + private static bool Is4Node(Node node) + { + return IsRed(node.Left) && IsRed(node.Right); + } + + private static bool IsBlack(Node node) + { + return (node != null && !node.IsRed); + } + + private static bool IsNullOrBlack(Node node) + { + return (node == null || !node.IsRed); + } + + private static bool IsRed(Node node) + { + return (node != null && node.IsRed); + } + + private static void Merge2Nodes(Node parent, Node child1, Node child2) + { + Debug.Assert(IsRed(parent), "parent must be be red"); + // combing two 2-nodes into a 4-node + parent.IsRed = false; + child1.IsRed = true; + child2.IsRed = true; + } + + // Replace the child of a parent node. + // If the parent node is null, replace the root. + private void ReplaceChildOfNodeOrRoot(Node parent, Node child, Node newChild) + { + if (parent != null) + { + if (parent.Left == child) + { + parent.Left = newChild; + } + else + { + parent.Right = newChild; + } + } + else + { + root = newChild; + } + } + + // Replace the matching node with its succesor. + private void ReplaceNode(Node match, Node parentOfMatch, Node succesor, Node parentOfSuccesor) + { + if (succesor == match) + { // this node has no successor, should only happen if right child of matching node is null. + Debug.Assert(match.Right == null, "Right child must be null!"); + succesor = match.Left; + } + else + { + Debug.Assert(parentOfSuccesor != null, "parent of successor cannot be null!"); + Debug.Assert(succesor.Left == null, "Left child of succesor must be null!"); + Debug.Assert((succesor.Right == null && succesor.IsRed) || (succesor.Right.IsRed && !succesor.IsRed), "Succesor must be in valid state"); + if (succesor.Right != null) + { + succesor.Right.IsRed = false; + } + + if (parentOfSuccesor != match) + { // detach succesor from its parent and set its right child + parentOfSuccesor.Left = succesor.Right; + succesor.Right = match.Right; + } + + succesor.Left = match.Left; + } + + if (succesor != null) + { + succesor.IsRed = match.IsRed; + } + + ReplaceChildOfNodeOrRoot(parentOfMatch, match, succesor); + + } + + internal virtual Node FindNode(T item) + { + Node current = root; + while (current != null) + { + int order = comparer.Compare(item, current.Item); + if (order == 0) + { + return current; + } + else + { + current = (order < 0) ? current.Left : current.Right; + } + } + + return null; + } + + //used for bithelpers. Note that this implementation is completely different + //from the Subset's. The two should not be mixed. This indexes as if the tree were an array. + //http://en.wikipedia.org/wiki/Binary_Tree#Methods_for_storing_binary_trees + internal virtual int InternalIndexOf(T item) + { + Node current = root; + int count = 0; + while (current != null) + { + int order = comparer.Compare(item, current.Item); + if (order == 0) + { + return count; + } + else + { + current = (order < 0) ? current.Left : current.Right; + count = (order < 0) ? (2 * count + 1) : (2 * count + 2); + } + } + return -1; + } + + + + internal Node FindRange(T from, T to) + { + return FindRange(from, to, true, true); + } + internal Node FindRange(T from, T to, bool lowerBoundActive, bool upperBoundActive) + { + Node current = root; + while (current != null) + { + if (lowerBoundActive && comparer.Compare(from, current.Item) > 0) + { + current = current.Right; + } + else + { + if (upperBoundActive && comparer.Compare(to, current.Item) < 0) + { + current = current.Left; + } + else + { + return current; + } + } + } + + return null; + } + + internal void UpdateVersion() + { + ++version; + } + + + private static Node RotateLeft(Node node) + { + Node x = node.Right; + node.Right = x.Left; + x.Left = node; + return x; + } + + private static Node RotateLeftRight(Node node) + { + Node child = node.Left; + Node grandChild = child.Right; + + node.Left = grandChild.Right; + grandChild.Right = node; + child.Right = grandChild.Left; + grandChild.Left = child; + return grandChild; + } + + private static Node RotateRight(Node node) + { + Node x = node.Left; + node.Left = x.Right; + x.Right = node; + return x; + } + + private static Node RotateRightLeft(Node node) + { + Node child = node.Right; + Node grandChild = child.Left; + + node.Right = grandChild.Left; + grandChild.Left = node; + child.Left = grandChild.Right; + grandChild.Right = child; + return grandChild; + } + /// + /// Testing counter that can track rotations + /// + + + private static TreeRotation RotationNeeded(Node parent, Node current, Node sibling) + { + Debug.Assert(IsRed(sibling.Left) || IsRed(sibling.Right), "sibling must have at least one red child"); + if (IsRed(sibling.Left)) + { + if (parent.Left == current) + { + return TreeRotation.RightLeftRotation; + } + return TreeRotation.RightRotation; + } + else + { + if (parent.Left == current) + { + return TreeRotation.LeftRotation; + } + return TreeRotation.LeftRightRotation; + } + } + + /// + /// Used for deep equality of SortedSet testing + /// + /// + public static IEqualityComparer> CreateSetComparer() + { + return new SortedSetEqualityComparer(); + } + + /// + /// Create a new set comparer for this set, where this set's members' equality is defined by the + /// memberEqualityComparer. Note that this equality comparer's definition of equality must be the + /// same as this set's Comparer's definition of equality + /// + public static IEqualityComparer> CreateSetComparer(IEqualityComparer memberEqualityComparer) + { + return new SortedSetEqualityComparer(memberEqualityComparer); + } + + + /// + /// Decides whether these sets are the same, given the comparer. If the EC's are the same, we can + /// just use SetEquals, but if they aren't then we have to manually check with the given comparer + /// + internal static bool SortedSetEquals(SortedSet set1, SortedSet set2, IComparer comparer) + { + // handle null cases first + if (set1 == null) + { + return (set2 == null); + } + else if (set2 == null) + { + // set1 != null + return false; + } + + if (AreComparersEqual(set1, set2)) + { + if (set1.Count != set2.Count) + return false; + + return set1.SetEquals(set2); + } + else + { + bool found = false; + foreach (T item1 in set1) + { + found = false; + foreach (T item2 in set2) + { + if (comparer.Compare(item1, item2) == 0) + { + found = true; + break; + } + } + if (!found) + return false; + } + return true; + } + + } + + + //This is a little frustrating because we can't support more sorted structures + private static bool AreComparersEqual(SortedSet set1, SortedSet set2) + { + return set1.Comparer.Equals(set2.Comparer); + } + + + private static void Split4Node(Node node) + { + node.IsRed = true; + node.Left.IsRed = false; + node.Right.IsRed = false; + } + + /// + /// Copies this to an array. Used for DebugView + /// + /// + internal T[] ToArray() + { + T[] newArray = new T[Count]; + CopyTo(newArray); + return newArray; + } + + + #endregion + + #region ISet Members + + /// + /// Transform this set into its union with the IEnumerable OTHER + ///Attempts to insert each element and rejects it if it exists. + /// NOTE: The caller object is important as UnionWith uses the Comparator + ///associated with THIS to check equality + /// Throws ArgumentNullException if OTHER is null + /// + /// + public void UnionWith(IEnumerable other) + { + if (other == null) + { + throw new ArgumentNullException("other"); + } + + SortedSet s = other as SortedSet; + TreeSubSet t = this as TreeSubSet; + + if (t != null) + VersionCheck(); + + if (s != null && t == null && this.count == 0) + { + SortedSet dummy = new SortedSet(s, this.comparer); + this.root = dummy.root; + this.count = dummy.count; + this.version++; + return; + } + + + if (s != null && t == null && AreComparersEqual(this, s) && (s.Count > this.Count / 2)) + { //this actually hurts if N is much greater than M the /2 is arbitrary + //first do a merge sort to an array. + T[] merged = new T[s.Count + this.Count]; + int c = 0; + Enumerator mine = this.GetEnumerator(); + Enumerator theirs = s.GetEnumerator(); + bool mineEnded = !mine.MoveNext(), theirsEnded = !theirs.MoveNext(); + while (!mineEnded && !theirsEnded) + { + int comp = Comparer.Compare(mine.Current, theirs.Current); + if (comp < 0) + { + merged[c++] = mine.Current; + mineEnded = !mine.MoveNext(); + } + else if (comp == 0) + { + merged[c++] = theirs.Current; + mineEnded = !mine.MoveNext(); + theirsEnded = !theirs.MoveNext(); + } + else + { + merged[c++] = theirs.Current; + theirsEnded = !theirs.MoveNext(); + } + } + + if (!mineEnded || !theirsEnded) + { + Enumerator remaining = (mineEnded ? theirs : mine); + do + { + merged[c++] = remaining.Current; + } while (remaining.MoveNext()); + } + + //now merged has all c elements + + //safe to gc the root, we have all the elements + root = null; + + + root = SortedSet.ConstructRootFromSortedArray(merged, 0, c - 1, null); + count = c; + version++; + } + else + { + AddAllElements(other); + } + } + + + private static Node ConstructRootFromSortedArray(T[] arr, int startIndex, int endIndex, Node redNode) + { + + + + //what does this do? + //you're given a sorted array... say 1 2 3 4 5 6 + //2 cases: + // If there are odd # of elements, pick the middle element (in this case 4), and compute + // its left and right branches + // If there are even # of elements, pick the left middle element, save the right middle element + // and call the function on the rest + // 1 2 3 4 5 6 -> pick 3, save 4 and call the fn on 1,2 and 5,6 + // now add 4 as a red node to the lowest element on the right branch + // 3 3 + // 1 5 -> 1 5 + // 2 6 2 4 6 + // As we're adding to the leftmost of the right branch, nesting will not hurt the red-black properties + // Leaf nodes are red if they have no sibling (if there are 2 nodes or if a node trickles + // down to the bottom + + + //the iterative way to do this ends up wasting more space than it saves in stack frames (at + //least in what i tried) + //so we're doing this recursively + //base cases are described below + int size = endIndex - startIndex + 1; + if (size == 0) + { + return null; + } + Node root = null; + if (size == 1) + { + root = new Node(arr[startIndex], false); + if (redNode != null) + { + root.Left = redNode; + } + } + else if (size == 2) + { + root = new Node(arr[startIndex], false); + root.Right = new Node(arr[endIndex], false); + root.Right.IsRed = true; + if (redNode != null) + { + root.Left = redNode; + } + } + else if (size == 3) + { + root = new Node(arr[startIndex + 1], false); + root.Left = new Node(arr[startIndex], false); + root.Right = new Node(arr[endIndex], false); + if (redNode != null) + { + root.Left.Left = redNode; + + } + } + else + { + int midpt = ((startIndex + endIndex) / 2); + root = new Node(arr[midpt], false); + root.Left = ConstructRootFromSortedArray(arr, startIndex, midpt - 1, redNode); + if (size % 2 == 0) + { + root.Right = ConstructRootFromSortedArray(arr, midpt + 2, endIndex, new Node(arr[midpt + 1], true)); + } + else + { + root.Right = ConstructRootFromSortedArray(arr, midpt + 1, endIndex, null); + } + } + return root; + + } + + + /// + /// Transform this set into its intersection with the IEnumerable OTHER + /// NOTE: The caller object is important as IntersectionWith uses the + /// comparator associated with THIS to check equality + /// Throws ArgumentNullException if OTHER is null + /// + /// + public virtual void IntersectWith(IEnumerable other) + { + if (other == null) + { + throw new ArgumentNullException("other"); + } + + if (Count == 0) + return; + + //HashSet optimizations can't be done until equality comparers and comparers are related + + //Technically, this would work as well with an ISorted + SortedSet s = other as SortedSet; + TreeSubSet t = this as TreeSubSet; + if (t != null) + VersionCheck(); + //only let this happen if i am also a SortedSet, not a SubSet + if (s != null && t == null && AreComparersEqual(this, s)) + { + + + //first do a merge sort to an array. + T[] merged = new T[this.Count]; + int c = 0; + Enumerator mine = this.GetEnumerator(); + Enumerator theirs = s.GetEnumerator(); + bool mineEnded = !mine.MoveNext(), theirsEnded = !theirs.MoveNext(); + T max = Max; + T min = Min; + + while (!mineEnded && !theirsEnded && Comparer.Compare(theirs.Current, max) <= 0) + { + int comp = Comparer.Compare(mine.Current, theirs.Current); + if (comp < 0) + { + mineEnded = !mine.MoveNext(); + } + else if (comp == 0) + { + merged[c++] = theirs.Current; + mineEnded = !mine.MoveNext(); + theirsEnded = !theirs.MoveNext(); + } + else + { + theirsEnded = !theirs.MoveNext(); + } + } + + //now merged has all c elements + + //safe to gc the root, we have all the elements + root = null; + + root = SortedSet.ConstructRootFromSortedArray(merged, 0, c - 1, null); + count = c; + version++; + } + else + { + IntersectWithEnumerable(other); + } + } + + internal virtual void IntersectWithEnumerable(IEnumerable other) + { + // + List toSave = new List(this.Count); + foreach (T item in other) + { + if (this.Contains(item)) + { + toSave.Add(item); + this.Remove(item); + } + } + this.Clear(); + AddAllElements(toSave); + + } + + + + /// + /// Transform this set into its complement with the IEnumerable OTHER + /// NOTE: The caller object is important as ExceptWith uses the + /// comparator associated with THIS to check equality + /// Throws ArgumentNullException if OTHER is null + /// + /// + public void ExceptWith(IEnumerable other) + { + if (other == null) + { + throw new ArgumentNullException("other"); + } + + if (count == 0) + return; + + if (other == this) + { + this.Clear(); + return; + } + + SortedSet asSorted = other as SortedSet; + + if (asSorted != null && AreComparersEqual(this, asSorted)) + { + //outside range, no point doing anything + if (!(comparer.Compare(asSorted.Max, this.Min) < 0 || comparer.Compare(asSorted.Min, this.Max) > 0)) + { + T min = this.Min; + T max = this.Max; + foreach (T item in other) + { + if (comparer.Compare(item, min) < 0) + continue; + if (comparer.Compare(item, max) > 0) + break; + Remove(item); + } + } + + } + else + { + RemoveAllElements(other); + } + } + + /// + /// Transform this set so it contains elements in THIS or OTHER but not both + /// NOTE: The caller object is important as SymmetricExceptWith uses the + /// comparator associated with THIS to check equality + /// Throws ArgumentNullException if OTHER is null + /// + /// + public void SymmetricExceptWith(IEnumerable other) + { + if (other == null) + { + throw new ArgumentNullException("other"); + } + + if (this.Count == 0) + { + this.UnionWith(other); + return; + } + + if (other == this) + { + this.Clear(); + return; + } + + + SortedSet asSorted = other as SortedSet; + +#if USING_HASH_SET + HashSet asHash = other as HashSet; +#endif + if (asSorted != null && AreComparersEqual(this, asSorted)) + { + SymmetricExceptWithSameEC(asSorted); + } +#if USING_HASH_SET + else if (asHash != null && this.comparer.Equals(Comparer.Default) && asHash.Comparer.Equals(EqualityComparer.Default)) { + SymmetricExceptWithSameEC(asHash); + } +#endif + else + { + //need perf improvement on this + T[] elements = (new List(other)).ToArray(); + Array.Sort(elements, this.Comparer); + SymmetricExceptWithSameEC(elements); + } + } + + //OTHER must be a set + internal void SymmetricExceptWithSameEC(ISet other) + { + foreach (T item in other) + { + //yes, it is classier to say + //if (!this.Remove(item))this.Add(item); + //but this ends up saving on rotations + if (this.Contains(item)) + { + this.Remove(item); + } + else + { + this.Add(item); + } + } + } + + //OTHER must be a sorted array + internal void SymmetricExceptWithSameEC(T[] other) + { + if (other.Length == 0) + { + return; + } + T last = other[0]; + for (int i = 0; i < other.Length; i++) + { + while (i < other.Length && i != 0 && comparer.Compare(other[i], last) == 0) + i++; + if (i >= other.Length) + break; + if (this.Contains(other[i])) + { + this.Remove(other[i]); + } + else + { + this.Add(other[i]); + } + last = other[i]; + } + } + + + /// + /// Checks whether this Tree is a subset of the IEnumerable other + /// + /// + /// + [System.Security.SecuritySafeCritical] + public bool IsSubsetOf(IEnumerable other) + { + if (other == null) + { + throw new ArgumentNullException("other"); + } + + if (Count == 0) + return true; + + + SortedSet asSorted = other as SortedSet; + if (asSorted != null && AreComparersEqual(this, asSorted)) + { + if (this.Count > asSorted.Count) + return false; + return IsSubsetOfSortedSetWithSameEC(asSorted); + } + else + { + //worst case: mark every element in my set and see if i've counted all + //O(MlogN) + + ElementCount result = CheckUniqueAndUnfoundElements(other, false); + return (result.uniqueCount == Count && result.unfoundCount >= 0); + } + } + + private bool IsSubsetOfSortedSetWithSameEC(SortedSet asSorted) + { + SortedSet prunedOther = asSorted.GetViewBetween(this.Min, this.Max); + foreach (T item in this) + { + if (!prunedOther.Contains(item)) + return false; + } + return true; + + } + + + /// + /// Checks whether this Tree is a proper subset of the IEnumerable other + /// + /// + /// + [System.Security.SecuritySafeCritical] + public bool IsProperSubsetOf(IEnumerable other) + { + if (other == null) + { + throw new ArgumentNullException("other"); + } + + if ((other as ICollection) != null) + { + if (Count == 0) + return (other as ICollection).Count > 0; + } + + +#if USING_HASH_SET + //do it one way for HashSets + HashSet asHash = other as HashSet; + if (asHash != null && comparer.Equals(Comparer.Default) && asHash.Comparer.Equals(EqualityComparer.Default)) { + return asHash.IsProperSupersetOf(this); + } +#endif + //another for sorted sets with the same comparer + SortedSet asSorted = other as SortedSet; + if (asSorted != null && AreComparersEqual(this, asSorted)) + { + if (this.Count >= asSorted.Count) + return false; + return IsSubsetOfSortedSetWithSameEC(asSorted); + } + + + //worst case: mark every element in my set and see if i've counted all + //O(MlogN). + ElementCount result = CheckUniqueAndUnfoundElements(other, false); + return (result.uniqueCount == Count && result.unfoundCount > 0); + } + + + /// + /// Checks whether this Tree is a super set of the IEnumerable other + /// + /// + /// + public bool IsSupersetOf(IEnumerable other) + { + if (other == null) + { + throw new ArgumentNullException("other"); + } + + if ((other as ICollection) != null && (other as ICollection).Count == 0) + return true; + + //do it one way for HashSets +#if USING_HASH_SET + HashSet asHash = other as HashSet; + if (asHash != null && comparer.Equals(Comparer.Default) && asHash.Comparer.Equals(EqualityComparer.Default)) { + return asHash.IsSubsetOf(this); + } +#endif + //another for sorted sets with the same comparer + SortedSet asSorted = other as SortedSet; + if (asSorted != null && AreComparersEqual(this, asSorted)) + { + if (this.Count < asSorted.Count) + return false; + SortedSet pruned = GetViewBetween(asSorted.Min, asSorted.Max); + foreach (T item in asSorted) + { + if (!pruned.Contains(item)) + return false; + } + return true; + } + //and a third for everything else + return ContainsAllElements(other); + } + + /// + /// Checks whether this Tree is a proper super set of the IEnumerable other + /// + /// + /// + [System.Security.SecuritySafeCritical] + public bool IsProperSupersetOf(IEnumerable other) + { + if (other == null) + { + throw new ArgumentNullException("other"); + } + + if (Count == 0) + return false; + + if ((other as ICollection) != null && (other as ICollection).Count == 0) + return true; + +#if USING_HASH_SET + //do it one way for HashSets + + HashSet asHash = other as HashSet; + if (asHash != null && comparer.Equals(Comparer.Default) && asHash.Comparer.Equals(EqualityComparer.Default)) { + return asHash.IsProperSubsetOf(this); + } +#endif + //another way for sorted sets + SortedSet asSorted = other as SortedSet; + if (asSorted != null && AreComparersEqual(asSorted, this)) + { + if (asSorted.Count >= this.Count) + return false; + SortedSet pruned = GetViewBetween(asSorted.Min, asSorted.Max); + foreach (T item in asSorted) + { + if (!pruned.Contains(item)) + return false; + } + return true; + } + + + //worst case: mark every element in my set and see if i've counted all + //O(MlogN) + //slight optimization, put it into a HashSet and then check can do it in O(N+M) + //but slower in better cases + wastes space + ElementCount result = CheckUniqueAndUnfoundElements(other, true); + return (result.uniqueCount < Count && result.unfoundCount == 0); + } + + + + /// + /// Checks whether this Tree has all elements in common with IEnumerable other + /// + /// + /// + [System.Security.SecuritySafeCritical] + public bool SetEquals(IEnumerable other) + { + if (other == null) + { + throw new ArgumentNullException("other"); + } + +#if USING_HASH_SET + HashSet asHash = other as HashSet; + if (asHash != null && comparer.Equals(Comparer.Default) && asHash.Comparer.Equals(EqualityComparer.Default)) { + return asHash.SetEquals(this); + } +#endif + SortedSet asSorted = other as SortedSet; + if (asSorted != null && AreComparersEqual(this, asSorted)) + { + IEnumerator mine = this.GetEnumerator(); + IEnumerator theirs = asSorted.GetEnumerator(); + bool mineEnded = !mine.MoveNext(); + bool theirsEnded = !theirs.MoveNext(); + while (!mineEnded && !theirsEnded) + { + if (Comparer.Compare(mine.Current, theirs.Current) != 0) + { + return false; + } + mineEnded = !mine.MoveNext(); + theirsEnded = !theirs.MoveNext(); + } + return mineEnded && theirsEnded; + } + + //worst case: mark every element in my set and see if i've counted all + //O(N) by size of other + ElementCount result = CheckUniqueAndUnfoundElements(other, true); + return (result.uniqueCount == Count && result.unfoundCount == 0); + } + + + + /// + /// Checks whether this Tree has any elements in common with IEnumerable other + /// + /// + /// + public bool Overlaps(IEnumerable other) + { + if (other == null) + { + throw new ArgumentNullException("other"); + } + + if (this.Count == 0) + return false; + + if ((other as ICollection != null) && (other as ICollection).Count == 0) + return false; + + SortedSet asSorted = other as SortedSet; + if (asSorted != null && AreComparersEqual(this, asSorted) && (comparer.Compare(Min, asSorted.Max) > 0 || comparer.Compare(Max, asSorted.Min) < 0)) + { + return false; + } +#if USING_HASH_SET + HashSet asHash = other as HashSet; + if (asHash != null && comparer.Equals(Comparer.Default) && asHash.Comparer.Equals(EqualityComparer.Default)) { + return asHash.Overlaps(this); + } +#endif + foreach (T item in other) + { + if (this.Contains(item)) + { + return true; + } + } + return false; + } + + /// + /// This works similar to HashSet's CheckUniqueAndUnfound (description below), except that the bit + /// array maps differently than in the HashSet. We can only use this for the bulk boolean checks. + /// + /// Determines counts that can be used to determine equality, subset, and superset. This + /// is only used when other is an IEnumerable and not a HashSet. If other is a HashSet + /// these properties can be checked faster without use of marking because we can assume + /// other has no duplicates. + /// + /// The following count checks are performed by callers: + /// 1. Equals: checks if unfoundCount = 0 and uniqueFoundCount = Count; i.e. everything + /// in other is in this and everything in this is in other + /// 2. Subset: checks if unfoundCount >= 0 and uniqueFoundCount = Count; i.e. other may + /// have elements not in this and everything in this is in other + /// 3. Proper subset: checks if unfoundCount > 0 and uniqueFoundCount = Count; i.e + /// other must have at least one element not in this and everything in this is in other + /// 4. Proper superset: checks if unfound count = 0 and uniqueFoundCount strictly less + /// than Count; i.e. everything in other was in this and this had at least one element + /// not contained in other. + /// + /// An earlier implementation used delegates to perform these checks rather than returning + /// an ElementCount struct; however this was changed due to the perf overhead of delegates. + /// + /// + /// Allows us to finish faster for equals and proper superset + /// because unfoundCount must be 0. + /// + // + // + // + // + // + // + [System.Security.SecurityCritical] + private unsafe ElementCount CheckUniqueAndUnfoundElements(IEnumerable other, bool returnIfUnfound) + { + ElementCount result; + + // need special case in case this has no elements. + if (Count == 0) + { + int numElementsInOther = 0; + foreach (T item in other) + { + numElementsInOther++; + // break right away, all we want to know is whether other has 0 or 1 elements + break; + } + result.uniqueCount = 0; + result.unfoundCount = numElementsInOther; + return result; + } + + + int originalLastIndex = Count; + int intArrayLength = BitHelper.ToIntArrayLength(originalLastIndex); + + BitHelper bitHelper; + if (intArrayLength <= StackAllocThreshold) + { + int* bitArrayPtr = stackalloc int[intArrayLength]; + bitHelper = new BitHelper(bitArrayPtr, intArrayLength); + } + else + { + int[] bitArray = new int[intArrayLength]; + bitHelper = new BitHelper(bitArray, intArrayLength); + } + + // count of items in other not found in this + int unfoundCount = 0; + // count of unique items in other found in this + int uniqueFoundCount = 0; + + foreach (T item in other) + { + int index = InternalIndexOf(item); + if (index >= 0) + { + if (!bitHelper.IsMarked(index)) + { + // item hasn't been seen yet + bitHelper.MarkBit(index); + uniqueFoundCount++; + } + } + else + { + unfoundCount++; + if (returnIfUnfound) + { + break; + } + } + } + + result.uniqueCount = uniqueFoundCount; + result.unfoundCount = unfoundCount; + return result; + } + public int RemoveWhere(Predicate match) + { + if (match == null) + { + throw new ArgumentNullException("match"); + } + List matches = new List(this.Count); + + BreadthFirstTreeWalk(delegate (Node n) + { + if (match(n.Item)) + { + matches.Add(n.Item); + } + return true; + }); + // reverse breadth first to (try to) incur low cost + int actuallyRemoved = 0; + for (int i = matches.Count - 1; i >= 0; i--) + { + if (this.Remove(matches[i])) + { + actuallyRemoved++; + } + } + + return actuallyRemoved; + + } + + + #endregion + + #region ISorted Members + + + public T Min + { + get + { + T ret = default(T); + InOrderTreeWalk(delegate (SortedSet.Node n) { ret = n.Item; return false; }); + return ret; + } + } + + public T Max + { + get + { + T ret = default(T); + InOrderTreeWalk(delegate (SortedSet.Node n) { ret = n.Item; return false; }, true); + return ret; + } + } + + public IEnumerable Reverse() + { + Enumerator e = new Enumerator(this, true); + while (e.MoveNext()) + { + yield return e.Current; + } + } + + + /// + /// Returns a subset of this tree ranging from values lBound to uBound + /// Any changes made to the subset reflect in the actual tree + /// + /// Lowest Value allowed in the subset + /// Highest Value allowed in the subset + public virtual SortedSet GetViewBetween(T lowerValue, T upperValue) + { + if (Comparer.Compare(lowerValue, upperValue) > 0) + { + throw new ArgumentException("lowerBound is greater than upperBound"); + } + return new TreeSubSet(this, lowerValue, upperValue, true, true); + } + +#if DEBUG + + /// + /// debug status to be checked whenever any operation is called + /// + /// + internal virtual bool versionUpToDate() + { + return true; + } +#endif + + + /// + /// This class represents a subset view into the tree. Any changes to this view + /// are reflected in the actual tree. Uses the Comparator of the underlying tree. + /// + /// +#if !FEATURE_NETCORE + [Serializable] + internal sealed class TreeSubSet : SortedSet, ISerializable, IDeserializationCallback + { +#else + internal sealed class TreeSubSet : SortedSet { +#endif + SortedSet underlying; + T min, max; + //these exist for unbounded collections + //for instance, you could allow this subset to be defined for i>10. The set will throw if + //anything <=10 is added, but there is no upperbound. These features Head(), Tail(), were punted + //in the spec, and are not available, but the framework is there to make them available at some point. + bool lBoundActive, uBoundActive; + //used to see if the count is out of date + + +#if DEBUG + internal override bool versionUpToDate() + { + return (this.version == underlying.version); + } +#endif + + public TreeSubSet(SortedSet Underlying, T Min, T Max, bool lowerBoundActive, bool upperBoundActive) + : base(Underlying.Comparer) + { + underlying = Underlying; + min = Min; + max = Max; + lBoundActive = lowerBoundActive; + uBoundActive = upperBoundActive; + root = underlying.FindRange(min, max, lBoundActive, uBoundActive); // root is first element within range + count = 0; + version = -1; + VersionCheckImpl(); + } + +#if !FEATURE_NETCORE + /// + /// For serialization and deserialization + /// + private TreeSubSet() + { + comparer = null; + } + + + [SuppressMessage("Microsoft.Usage", "CA2236:CallBaseClassMethodsOnISerializableTypes", Justification = "special case TreeSubSet serialization")] + private TreeSubSet(SerializationInfo info, StreamingContext context) + { + siInfo = info; + OnDeserializationImpl(info); + } +#endif // !FEATURE_NETCORE + + /// + /// Additions to this tree need to be added to the underlying tree as well + /// + + internal override bool AddIfNotPresent(T item) + { + + if (!IsWithinRange(item)) + { + ThrowHelper.ThrowArgumentOutOfRangeException(ExceptionArgument.collection); + } + + bool ret = underlying.AddIfNotPresent(item); + VersionCheck(); +#if DEBUG + Debug.Assert(this.versionUpToDate() && this.root == this.underlying.FindRange(min, max)); +#endif + + return ret; + } + + + public override bool Contains(T item) + { + VersionCheck(); +#if DEBUG + Debug.Assert(this.versionUpToDate() && this.root == this.underlying.FindRange(min, max)); +#endif + return base.Contains(item); + } + + internal override bool DoRemove(T item) + { // todo: uppercase this and others + + if (!IsWithinRange(item)) + { + return false; + } + + bool ret = underlying.Remove(item); + VersionCheck(); +#if DEBUG + Debug.Assert(this.versionUpToDate() && this.root == this.underlying.FindRange(min, max)); +#endif + return ret; + } + + public override void Clear() + { + + + if (count == 0) + { + return; + } + + List toRemove = new List(); + BreadthFirstTreeWalk(delegate (Node n) { toRemove.Add(n.Item); return true; }); + while (toRemove.Count != 0) + { + underlying.Remove(toRemove[toRemove.Count - 1]); + toRemove.RemoveAt(toRemove.Count - 1); + } + root = null; + count = 0; + version = underlying.version; + } + + + internal override bool IsWithinRange(T item) + { + + int comp = (lBoundActive ? Comparer.Compare(min, item) : -1); + if (comp > 0) + { + return false; + } + comp = (uBoundActive ? Comparer.Compare(max, item) : 1); + if (comp < 0) + { + return false; + } + return true; + } + + internal override bool InOrderTreeWalk(TreeWalkPredicate action, Boolean reverse) + { + VersionCheck(); + + if (root == null) + { + return true; + } + + // The maximum height of a red-black tree is 2*lg(n+1). + // See page 264 of "Introduction to algorithms" by Thomas H. Cormen + Stack stack = new Stack(2 * (int)SortedSet.log2(count + 1)); //this is not exactly right if count is out of date, but the stack can grow + Node current = root; + while (current != null) + { + if (IsWithinRange(current.Item)) + { + stack.Push(current); + current = (reverse ? current.Right : current.Left); + } + else if (lBoundActive && Comparer.Compare(min, current.Item) > 0) + { + current = current.Right; + } + else + { + current = current.Left; + } + } + + while (stack.Count != 0) + { + current = stack.Pop(); + if (!action(current)) + { + return false; + } + + Node node = (reverse ? current.Left : current.Right); + while (node != null) + { + if (IsWithinRange(node.Item)) + { + stack.Push(node); + node = (reverse ? node.Right : node.Left); + } + else if (lBoundActive && Comparer.Compare(min, node.Item) > 0) + { + node = node.Right; + } + else + { + node = node.Left; + } + } + } + return true; + } + + internal override bool BreadthFirstTreeWalk(TreeWalkPredicate action) + { + VersionCheck(); + + if (root == null) + { + return true; + } + + List processQueue = new List(); + processQueue.Add(root); + Node current; + + while (processQueue.Count != 0) + { + current = processQueue[0]; + processQueue.RemoveAt(0); + if (IsWithinRange(current.Item) && !action(current)) + { + return false; + } + if (current.Left != null && (!lBoundActive || Comparer.Compare(min, current.Item) < 0)) + { + processQueue.Add(current.Left); + } + if (current.Right != null && (!uBoundActive || Comparer.Compare(max, current.Item) > 0)) + { + processQueue.Add(current.Right); + } + + } + return true; + } + + internal override SortedSet.Node FindNode(T item) + { + + if (!IsWithinRange(item)) + { + return null; + } + VersionCheck(); +#if DEBUG + Debug.Assert(this.versionUpToDate() && this.root == this.underlying.FindRange(min, max)); +#endif + return base.FindNode(item); + } + + //this does indexing in an inefficient way compared to the actual sortedset, but it saves a + //lot of space + internal override int InternalIndexOf(T item) + { + int count = -1; + foreach (T i in this) + { + count++; + if (Comparer.Compare(item, i) == 0) + return count; + } +#if DEBUG + Debug.Assert(this.versionUpToDate() && this.root == this.underlying.FindRange(min, max)); +#endif + return -1; + } + /// + /// checks whether this subset is out of date. updates if necessary. + /// + internal override void VersionCheck() + { + VersionCheckImpl(); + } + + private void VersionCheckImpl() + { + Debug.Assert(underlying != null, "Underlying set no longer exists"); + if (this.version != underlying.version) + { + this.root = underlying.FindRange(min, max, lBoundActive, uBoundActive); + this.version = underlying.version; + count = 0; + InOrderTreeWalk(delegate (Node n) { count++; return true; }); + } + } + + + + //This passes functionality down to the underlying tree, clipping edges if necessary + //There's nothing gained by having a nested subset. May as well draw it from the base + //Cannot increase the bounds of the subset, can only decrease it + public override SortedSet GetViewBetween(T lowerValue, T upperValue) + { + + if (lBoundActive && Comparer.Compare(min, lowerValue) > 0) + { + //lBound = min; + throw new ArgumentOutOfRangeException("lowerValue"); + } + if (uBoundActive && Comparer.Compare(max, upperValue) < 0) + { + //uBound = max; + throw new ArgumentOutOfRangeException("upperValue"); + } + TreeSubSet ret = (TreeSubSet)underlying.GetViewBetween(lowerValue, upperValue); + return ret; + } + + internal override void IntersectWithEnumerable(IEnumerable other) + { + + List toSave = new List(this.Count); + foreach (T item in other) + { + if (this.Contains(item)) + { + toSave.Add(item); + this.Remove(item); + } + } + this.Clear(); + this.AddAllElements(toSave); +#if DEBUG + Debug.Assert(this.versionUpToDate() && this.root == this.underlying.FindRange(min, max)); +#endif + } + +#if !FEATURE_NETCORE + void ISerializable.GetObjectData(SerializationInfo info, StreamingContext context) + { + GetObjectData(info, context); + } + + protected override void GetObjectData(SerializationInfo info, StreamingContext context) + { + if (info == null) + { + ThrowHelper.ThrowArgumentNullException(ExceptionArgument.info); + } + info.AddValue(maxName, max, typeof(T)); + info.AddValue(minName, min, typeof(T)); + info.AddValue(lBoundActiveName, lBoundActive); + info.AddValue(uBoundActiveName, uBoundActive); + base.GetObjectData(info, context); + } + + void IDeserializationCallback.OnDeserialization(Object sender) + { + //don't do anything here as its already been done by the constructor + //OnDeserialization(sender); + + } + + protected override void OnDeserialization(Object sender) + { + OnDeserializationImpl(sender); + } + + private void OnDeserializationImpl(Object sender) + { + if (siInfo == null) + { + ThrowHelper.ThrowSerializationException(ExceptionResource.Serialization_InvalidOnDeser); + } + + comparer = (IComparer)siInfo.GetValue(ComparerName, typeof(IComparer)); + int savedCount = siInfo.GetInt32(CountName); + max = (T)siInfo.GetValue(maxName, typeof(T)); + min = (T)siInfo.GetValue(minName, typeof(T)); + lBoundActive = siInfo.GetBoolean(lBoundActiveName); + uBoundActive = siInfo.GetBoolean(uBoundActiveName); + underlying = new SortedSet(); + + if (savedCount != 0) + { + T[] items = (T[])siInfo.GetValue(ItemsName, typeof(T[])); + + if (items == null) + { + ThrowHelper.ThrowSerializationException(ExceptionResource.Serialization_MissingValues); + } + + for (int i = 0; i < items.Length; i++) + { + underlying.Add(items[i]); + } + } + underlying.version = siInfo.GetInt32(VersionName); + count = underlying.count; + version = underlying.version - 1; + VersionCheck(); //this should update the count to be right and update root to be right + + if (count != savedCount) + { + ThrowHelper.ThrowSerializationException(ExceptionResource.Serialization_MismatchedCount); + } + siInfo = null; + + } +#endif // !FEATURE_NETCORE + + + + + } + + + #endregion + + #region Serialization methods + +#if !FEATURE_NETCORE + // LinkDemand here is unnecessary as this is a methodimpl and linkdemand from the interface should suffice + void ISerializable.GetObjectData(SerializationInfo info, StreamingContext context) + { + GetObjectData(info, context); + } + + protected virtual void GetObjectData(SerializationInfo info, StreamingContext context) + { + if (info == null) + { + ThrowHelper.ThrowArgumentNullException(ExceptionArgument.info); + } + + info.AddValue(CountName, count); //This is the length of the bucket array. + info.AddValue(ComparerName, comparer, typeof(IComparer)); + info.AddValue(VersionName, version); + + if (root != null) + { + T[] items = new T[Count]; + CopyTo(items, 0); + info.AddValue(ItemsName, items, typeof(T[])); + } + } + + void IDeserializationCallback.OnDeserialization(Object sender) + { + OnDeserialization(sender); + } + + protected virtual void OnDeserialization(Object sender) + { + if (comparer != null) + { + return; //Somebody had a dependency on this class and fixed us up before the ObjectManager got to it. + } + + if (siInfo == null) + { + ThrowHelper.ThrowSerializationException(ExceptionResource.Serialization_InvalidOnDeser); + } + + comparer = (IComparer)siInfo.GetValue(ComparerName, typeof(IComparer)); + int savedCount = siInfo.GetInt32(CountName); + + if (savedCount != 0) + { + T[] items = (T[])siInfo.GetValue(ItemsName, typeof(T[])); + + if (items == null) + { + ThrowHelper.ThrowSerializationException(ExceptionResource.Serialization_MissingValues); + } + + for (int i = 0; i < items.Length; i++) + { + Add(items[i]); + } + } + + version = siInfo.GetInt32(VersionName); + if (count != savedCount) + { + ThrowHelper.ThrowSerializationException(ExceptionResource.Serialization_MismatchedCount); + } + siInfo = null; + } +#endif //!FEATURE_NETCORE + #endregion + + #region Helper Classes + internal class Node + { + public bool IsRed; + public T Item; + public Node Left; + public Node Right; + + public Node(T item) + { + // The default color will be red, we never need to create a black node directly. + this.Item = item; + IsRed = true; + } + + public Node(T item, bool isRed) + { + // The default color will be red, we never need to create a black node directly. + this.Item = item; + this.IsRed = isRed; + } + } + + [SuppressMessage("Microsoft.Performance", "CA1815:OverrideEqualsAndOperatorEqualsOnValueTypes", Justification = "not an expected scenario")] +#if !FEATURE_NETCORE + [Serializable] + public struct Enumerator : IEnumerator, IEnumerator, ISerializable, IDeserializationCallback + { +#else + public struct Enumerator : IEnumerator, IEnumerator { +#endif + private SortedSet tree; + private int version; + + + private Stack.Node> stack; + private SortedSet.Node current; + static SortedSet.Node dummyNode = new SortedSet.Node(default(T)); + + private bool reverse; + +#if !FEATURE_NETCORE + private SerializationInfo siInfo; +#endif + internal Enumerator(SortedSet set) + { + tree = set; + //this is a hack to make sure that the underlying subset has not been changed since + // + tree.VersionCheck(); + + version = tree.version; + + // 2lg(n + 1) is the maximum height + stack = new Stack.Node>(2 * (int)SortedSet.log2(set.Count + 1)); + current = null; + reverse = false; +#if !FEATURE_NETCORE + siInfo = null; +#endif + Intialize(); + } + + internal Enumerator(SortedSet set, bool reverse) + { + tree = set; + //this is a hack to make sure that the underlying subset has not been changed since + // + tree.VersionCheck(); + version = tree.version; + + // 2lg(n + 1) is the maximum height + stack = new Stack.Node>(2 * (int)SortedSet.log2(set.Count + 1)); + current = null; + this.reverse = reverse; +#if !FEATURE_NETCORE + siInfo = null; +#endif + Intialize(); + + } + +#if !FEATURE_NETCORE + private Enumerator(SerializationInfo info, StreamingContext context) + { + tree = null; + version = -1; + current = null; + reverse = false; + stack = null; + this.siInfo = info; + } + + void ISerializable.GetObjectData(SerializationInfo info, StreamingContext context) + { + GetObjectData(info, context); + } + + private void GetObjectData(SerializationInfo info, StreamingContext context) + { + if (info == null) + { + ThrowHelper.ThrowArgumentNullException(ExceptionArgument.info); + + } + info.AddValue(TreeName, tree, typeof(SortedSet)); + info.AddValue(EnumVersionName, version); + info.AddValue(ReverseName, reverse); + info.AddValue(EnumStartName, !NotStartedOrEnded); + info.AddValue(NodeValueName, (current == null ? dummyNode.Item : current.Item), typeof(T)); + } + + void IDeserializationCallback.OnDeserialization(Object sender) + { + OnDeserialization(sender); + } + + private void OnDeserialization(Object sender) + { + if (siInfo == null) + { + ThrowHelper.ThrowSerializationException(ExceptionResource.Serialization_InvalidOnDeser); + } + + tree = (SortedSet)siInfo.GetValue(TreeName, typeof(SortedSet)); + version = siInfo.GetInt32(EnumVersionName); + reverse = siInfo.GetBoolean(ReverseName); + bool EnumStarted = siInfo.GetBoolean(EnumStartName); + stack = new Stack.Node>(2 * (int)SortedSet.log2(tree.Count + 1)); + current = null; + if (EnumStarted) + { + T item = (T)siInfo.GetValue(NodeValueName, typeof(T)); + Intialize(); + //go until it reaches the value we want + while (this.MoveNext()) + { + if (tree.Comparer.Compare(this.Current, item) == 0) + break; + } + } + + + } +#endif //!FEATURE_NETCORE + + + private void Intialize() + { + + + current = null; + SortedSet.Node node = tree.root; + Node next = null, other = null; + while (node != null) + { + next = (reverse ? node.Right : node.Left); + other = (reverse ? node.Left : node.Right); + if (tree.IsWithinRange(node.Item)) + { + stack.Push(node); + node = next; + } + else if (next == null || !tree.IsWithinRange(next.Item)) + { + node = other; + } + else + { + node = next; + } + } + } + + public bool MoveNext() + { + + //this is a hack to make sure that the underlying subset has not been changed since + // + tree.VersionCheck(); + + if (version != tree.version) + { + ThrowHelper.ThrowInvalidOperationException(ExceptionResource.InvalidOperation_EnumFailedVersion); + } + + if (stack.Count == 0) + { + current = null; + return false; + } + + current = stack.Pop(); + SortedSet.Node node = (reverse ? current.Left : current.Right); + + Node next = null, other = null; + while (node != null) + { + next = (reverse ? node.Right : node.Left); + other = (reverse ? node.Left : node.Right); + if (tree.IsWithinRange(node.Item)) + { + stack.Push(node); + node = next; + } + else if (other == null || !tree.IsWithinRange(other.Item)) + { + node = next; + } + else + { + node = other; + } + } + return true; + } + + public void Dispose() + { + } + + public T Current + { + get + { + if (current != null) + { + return current.Item; + } + return default(T); + } + } + + object IEnumerator.Current + { + get + { + if (current == null) + { + ThrowHelper.ThrowInvalidOperationException(ExceptionResource.InvalidOperation_EnumOpCantHappen); + } + + return current.Item; + } + } + + internal bool NotStartedOrEnded + { + get + { + return current == null; + } + } + + internal void Reset() + { + if (version != tree.version) + { + ThrowHelper.ThrowInvalidOperationException(ExceptionResource.InvalidOperation_EnumFailedVersion); + } + + stack.Clear(); + Intialize(); + } + + void IEnumerator.Reset() + { + Reset(); + } + + + + + } + + + + internal struct ElementCount + { + internal int uniqueCount; + internal int unfoundCount; + } + #endregion + + #region misc + + /// + /// Searches the set for a given value and returns the equal value it finds, if any. + /// + /// The value to search for. + /// The value from the set that the search found, or the default value of when the search yielded no match. + /// A value indicating whether the search was successful. + /// + /// This can be useful when you want to reuse a previously stored reference instead of + /// a newly constructed one (so that more sharing of references can occur) or to look up + /// a value that has more complete data than the value you currently have, although their + /// comparer functions indicate they are equal. + /// + public bool TryGetValue(T equalValue, out T actualValue) + { + Node node = FindNode(equalValue); + if (node != null) + { + actualValue = node.Item; + return true; + } + actualValue = default(T); + return false; + } + + // used for set checking operations (using enumerables) that rely on counting + private static int log2(int value) + { + //Contract.Requires(value>0) + int c = 0; + while (value > 0) + { + c++; + value >>= 1; + } + return c; + } + #endregion + + + } + + /// + /// A class that generates an IEqualityComparer for this SortedSet. Requires that the definition of + /// equality defined by the IComparer for this SortedSet be consistent with the default IEqualityComparer + /// for the type T. If not, such an IEqualityComparer should be provided through the constructor. + /// + internal class SortedSetEqualityComparer : IEqualityComparer> + { + private IComparer comparer; + private IEqualityComparer e_comparer; + + public SortedSetEqualityComparer() : this(null, null) { } + + public SortedSetEqualityComparer(IComparer comparer) : this(comparer, null) { } + + public SortedSetEqualityComparer(IEqualityComparer memberEqualityComparer) : this(null, memberEqualityComparer) { } + + /// + /// Create a new SetEqualityComparer, given a comparer for member order and another for member equality (these + /// must be consistent in their definition of equality) + /// + public SortedSetEqualityComparer(IComparer comparer, IEqualityComparer memberEqualityComparer) + { + if (comparer == null) + this.comparer = Comparer.Default; + else + this.comparer = comparer; + if (memberEqualityComparer == null) + e_comparer = EqualityComparer.Default; + else + e_comparer = memberEqualityComparer; + } + + + // using comparer to keep equals properties in tact; don't want to choose one of the comparers + public bool Equals(SortedSet x, SortedSet y) + { + return SortedSet.SortedSetEquals(x, y, comparer); + } + //IMPORTANT: this part uses the fact that GetHashCode() is consistent with the notion of equality in + //the set + public int GetHashCode(SortedSet obj) + { + int hashCode = 0; + if (obj != null) + { + foreach (T t in obj) + { + hashCode = hashCode ^ (e_comparer.GetHashCode(t) & 0x7FFFFFFF); + } + } // else returns hashcode of 0 for null HashSets + return hashCode; + } + + // Equals method for the comparer itself. + public override bool Equals(Object obj) + { + SortedSetEqualityComparer comparer = obj as SortedSetEqualityComparer; + if (comparer == null) + { + return false; + } + return (this.comparer == comparer.comparer); + } + + public override int GetHashCode() + { + return comparer.GetHashCode() ^ e_comparer.GetHashCode(); + } + + + } + +} + + + +#pragma warning restore CS8604 // 引用类型参数可能为 null。 +#pragma warning restore CS8602 // 解引用可能出现空引用。 +#pragma warning restore CS8600 // 将 null 字面量或可能为 null 的值转换为非 null 类型。 +#pragma warning restore IDE0059 // 不需要赋值 +#pragma warning restore CS8625 // 无法将 null 字面量转换为非 null 的引用类型。 +#pragma warning restore CS8618 // 在退出构造函数时,不可为 null 的字段必须包含非 null 值。请考虑声明为可以为 null。 +#pragma warning restore CS8601 // 引用类型赋值可能为 null。 +#pragma warning restore CS8603 // 可能返回 null 引用。 +#endif \ No newline at end of file diff --git a/src/IFoxCAD.Basal/Sortedset/ThrowHelper.cs b/src/IFoxCAD.Basal/Sortedset/ThrowHelper.cs new file mode 100644 index 0000000..205dd1c --- /dev/null +++ b/src/IFoxCAD.Basal/Sortedset/ThrowHelper.cs @@ -0,0 +1,382 @@ +#if NET35 +#pragma warning disable IDE0059 // 不需要赋值 +#pragma warning disable CS8600 // 将 null 字面量或可能为 null 的值转换为非 null 类型。 + +namespace System +{ + // This file defines an internal class used to throw exceptions in BCL code. + // The main purpose is to reduce code size. + // + // The old way to throw an exception generates quite a lot IL code and assembly code. + // Following is an example: + // C# source + // throw new ArgumentNullException("key", SR.GetString("ArgumentNull_Key")); + // IL code: + // IL_0003: ldstr "key" + // IL_0008: ldstr "ArgumentNull_Key" + // IL_000d: call string System.Environment::GetResourceString(string) + // IL_0012: newobj instance void System.ArgumentNullException::.ctor(string,string) + // IL_0017: throw + // which is 21bytes in IL. + // + // So we want to get rid of the ldstr and call to Environment.GetResource in IL. + // In order to do that, I created two enums: ExceptionResource, ExceptionArgument to represent the + // argument name and resource name in a small integer. The source code will be changed to + // ThrowHelper.ThrowArgumentNullException(ExceptionArgument.key, ExceptionResource.ArgumentNull_Key); + // + // The IL code will be 7 bytes. + // IL_0008: ldc.i4.4 + // IL_0009: ldc.i4.4 + // IL_000a: call void System.ThrowHelper::ThrowArgumentNullException(valuetype System.ExceptionArgument) + // IL_000f: ldarg.0 + // + // This will also reduce the Jitted code size a lot. + // + // It is very important we do this for generic classes because we can easily generate the same code + // multiple times for different instantiation. + // + // < + + + + + + + + + + +#if !SILVERLIGHT + using System.Runtime.Serialization; +#endif + + using System.Diagnostics; + internal static class ThrowHelper + { + internal static void ThrowWrongKeyTypeArgumentException(object key, Type targetType) + { + throw new ArgumentException(SR.GetString("SR.Arg_WrongType", key, targetType), "key"); + } + + internal static void ThrowWrongValueTypeArgumentException(object value, Type targetType) + { + throw new ArgumentException(SR.GetString("SR.Arg_WrongType", value, targetType), "value"); + } + + internal static void ThrowKeyNotFoundException() + { + throw new System.Collections.Generic.KeyNotFoundException(); + } + + internal static void ThrowArgumentException(ExceptionResource resource) + { + throw new ArgumentException(SR.GetString(GetResourceName(resource))); + } + + internal static void ThrowArgumentNullException(ExceptionArgument argument) + { + throw new ArgumentNullException(GetArgumentName(argument)); + } + + internal static void ThrowArgumentOutOfRangeException(ExceptionArgument argument) + { + throw new ArgumentOutOfRangeException(GetArgumentName(argument)); + } + + internal static void ThrowArgumentOutOfRangeException(ExceptionArgument argument, ExceptionResource resource) + { + throw new ArgumentOutOfRangeException(GetArgumentName(argument), SR.GetString(GetResourceName(resource))); + } + + internal static void ThrowInvalidOperationException(ExceptionResource resource) + { + throw new InvalidOperationException(SR.GetString(GetResourceName(resource))); + } + +#if !SILVERLIGHT + internal static void ThrowSerializationException(ExceptionResource resource) + { + throw new SerializationException(SR.GetString(GetResourceName(resource))); + } +#endif + + internal static void ThrowNotSupportedException(ExceptionResource resource) + { + throw new NotSupportedException(SR.GetString(GetResourceName(resource))); + } + + // Allow nulls for reference types and Nullable, but not for value types. + internal static void IfNullAndNullsAreIllegalThenThrow(object value, ExceptionArgument argName) + { + // Note that default(T) is not equal to null for value types except when T is Nullable. + if (value == null && !(default(T) == null)) + ThrowHelper.ThrowArgumentNullException(argName); + } + + // + // This function will convert an ExceptionArgument enum value to the argument name string. + // + internal static string GetArgumentName(ExceptionArgument argument) + { + string argumentName = null; + + switch (argument) + { + case ExceptionArgument.array: + argumentName = "array"; + break; + + case ExceptionArgument.arrayIndex: + argumentName = "arrayIndex"; + break; + + case ExceptionArgument.capacity: + argumentName = "capacity"; + break; + + case ExceptionArgument.collection: + argumentName = "collection"; + break; + + case ExceptionArgument.converter: + argumentName = "converter"; + break; + + case ExceptionArgument.count: + argumentName = "count"; + break; + + case ExceptionArgument.dictionary: + argumentName = "dictionary"; + break; + + case ExceptionArgument.index: + argumentName = "index"; + break; + + case ExceptionArgument.info: + argumentName = "info"; + break; + + case ExceptionArgument.key: + argumentName = "key"; + break; + + case ExceptionArgument.match: + argumentName = "match"; + break; + + case ExceptionArgument.obj: + argumentName = "obj"; + break; + + case ExceptionArgument.queue: + argumentName = "queue"; + break; + + case ExceptionArgument.stack: + argumentName = "stack"; + break; + + case ExceptionArgument.startIndex: + argumentName = "startIndex"; + break; + + case ExceptionArgument.value: + argumentName = "value"; + break; + + case ExceptionArgument.item: + argumentName = "item"; + break; + + default: + Debug.Assert(false, "The enum value is not defined, please checked ExceptionArgumentName Enum."); + return string.Empty; + } + + return argumentName; + } + + // + // This function will convert an ExceptionResource enum value to the resource string. + // + internal static string GetResourceName(ExceptionResource resource) + { + string resourceName = null; + + switch (resource) + { + case ExceptionResource.Argument_ImplementIComparable: + resourceName = "SR.Argument_ImplementIComparable"; + break; + + case ExceptionResource.Argument_AddingDuplicate: + resourceName = "SR.Argument_AddingDuplicate"; + break; + + case ExceptionResource.ArgumentOutOfRange_Index: + resourceName = "SR.ArgumentOutOfRange_Index"; + break; + + case ExceptionResource.ArgumentOutOfRange_NeedNonNegNum: + resourceName = "SR.ArgumentOutOfRange_NeedNonNegNum"; + break; + + case ExceptionResource.ArgumentOutOfRange_NeedNonNegNumRequired: + resourceName = "SR.ArgumentOutOfRange_NeedNonNegNumRequired"; + break; + + case ExceptionResource.ArgumentOutOfRange_SmallCapacity: + resourceName = "SR.ArgumentOutOfRange_SmallCapacity"; + break; + + case ExceptionResource.Arg_ArrayPlusOffTooSmall: + resourceName = "SR.Arg_ArrayPlusOffTooSmall"; + break; + + case ExceptionResource.Arg_RankMultiDimNotSupported: + resourceName = "SR.Arg_MultiRank"; + break; + + case ExceptionResource.Arg_NonZeroLowerBound: + resourceName = "SR.Arg_NonZeroLowerBound"; + break; + + case ExceptionResource.Argument_InvalidArrayType: + resourceName = "SR.Invalid_Array_Type"; + break; + + case ExceptionResource.Argument_InvalidOffLen: + resourceName = "SR.Argument_InvalidOffLen"; + break; + + case ExceptionResource.InvalidOperation_CannotRemoveFromStackOrQueue: + resourceName = "SR.InvalidOperation_CannotRemoveFromStackOrQueue"; + break; + + case ExceptionResource.InvalidOperation_EmptyCollection: + resourceName = "SR.InvalidOperation_EmptyCollection"; + break; + + case ExceptionResource.InvalidOperation_EmptyQueue: + resourceName = "SR.InvalidOperation_EmptyQueue"; + break; + + case ExceptionResource.InvalidOperation_EnumOpCantHappen: + resourceName = "SR.InvalidOperation_EnumOpCantHappen"; + break; + + case ExceptionResource.InvalidOperation_EnumFailedVersion: + resourceName = "SR.InvalidOperation_EnumFailedVersion"; + break; + + case ExceptionResource.InvalidOperation_EmptyStack: + resourceName = "SR.InvalidOperation_EmptyStack"; + break; + + case ExceptionResource.InvalidOperation_EnumNotStarted: + resourceName = "SR.InvalidOperation_EnumNotStarted"; + break; + + case ExceptionResource.InvalidOperation_EnumEnded: + resourceName = "SR.InvalidOperation_EnumEnded"; + break; + + case ExceptionResource.NotSupported_KeyCollectionSet: + resourceName = "SR.NotSupported_KeyCollectionSet"; + break; + + case ExceptionResource.NotSupported_SortedListNestedWrite: + resourceName = "SR.NotSupported_SortedListNestedWrite"; + break; + +#if !SILVERLIGHT + case ExceptionResource.Serialization_InvalidOnDeser: + resourceName = "SR.Serialization_InvalidOnDeser"; + break; + + case ExceptionResource.Serialization_MissingValues: + resourceName = "SR.Serialization_MissingValues"; + break; + + case ExceptionResource.Serialization_MismatchedCount: + resourceName = "SR.Serialization_MismatchedCount"; + break; +#endif + + case ExceptionResource.NotSupported_ValueCollectionSet: + resourceName = "SR.NotSupported_ValueCollectionSet"; + break; + + default: + Debug.Assert(false, "The enum value is not defined, please checked ExceptionArgumentName Enum."); + return string.Empty; + } + + return resourceName; + } + + } + + // + // The convention for this enum is using the argument name as the enum name + // + internal enum ExceptionArgument + { + obj, + dictionary, + array, + info, + key, + collection, + match, + converter, + queue, + stack, + capacity, + index, + startIndex, + value, + count, + arrayIndex, + item, + } + + // + // The convention for this enum is using the resource name as the enum name + // + internal enum ExceptionResource + { + Argument_ImplementIComparable, + ArgumentOutOfRange_NeedNonNegNum, + ArgumentOutOfRange_NeedNonNegNumRequired, + Arg_ArrayPlusOffTooSmall, + Argument_AddingDuplicate, + Serialization_InvalidOnDeser, + Serialization_MismatchedCount, + Serialization_MissingValues, + Arg_RankMultiDimNotSupported, + Arg_NonZeroLowerBound, + Argument_InvalidArrayType, + NotSupported_KeyCollectionSet, + ArgumentOutOfRange_SmallCapacity, + ArgumentOutOfRange_Index, + Argument_InvalidOffLen, + NotSupported_ReadOnlyCollection, + InvalidOperation_CannotRemoveFromStackOrQueue, + InvalidOperation_EmptyCollection, + InvalidOperation_EmptyQueue, + InvalidOperation_EnumOpCantHappen, + InvalidOperation_EnumFailedVersion, + InvalidOperation_EmptyStack, + InvalidOperation_EnumNotStarted, + InvalidOperation_EnumEnded, + NotSupported_SortedListNestedWrite, + NotSupported_ValueCollectionSet, + } +} + +#pragma warning restore CS8600 // 将 null 字面量或可能为 null 的值转换为非 null 类型。 +#pragma warning restore IDE0059 // 不需要赋值 +#endif \ No newline at end of file diff --git a/src/IFoxCAD.Basal/Sortedset/bithelper.cs b/src/IFoxCAD.Basal/Sortedset/bithelper.cs new file mode 100644 index 0000000..d68c4d8 --- /dev/null +++ b/src/IFoxCAD.Basal/Sortedset/bithelper.cs @@ -0,0 +1,173 @@ +#if NET35 +#pragma warning disable CS8603 // 可能返回 null 引用。 +#pragma warning disable CS8618 // 在退出构造函数时,不可为 null 的字段必须包含非 null 值。请考虑声明为可以为 null。 + + +using System; +using System.Collections; +using System.Text; + +namespace System.Collections.Generic +{ + + /// + /// ABOUT: + /// Helps with operations that rely on bit marking to indicate whether an item in the + /// collection should be added, removed, visited already, etc. + /// + /// BitHelper doesn't allocate the array; you must pass in an array or ints allocated on the + /// stack or heap. ToIntArrayLength() tells you the int array size you must allocate. + /// + /// USAGE: + /// Suppose you need to represent a bit array of length (i.e. logical bit array length) + /// BIT_ARRAY_LENGTH. Then this is the suggested way to instantiate BitHelper: + /// *************************************************************************** + /// int intArrayLength = BitHelper.ToIntArrayLength(BIT_ARRAY_LENGTH); + /// BitHelper bitHelper; + /// if (intArrayLength less than stack alloc threshold) + /// int* m_arrayPtr = stackalloc int[intArrayLength]; + /// bitHelper = new BitHelper(m_arrayPtr, intArrayLength); + /// else + /// int[] m_arrayPtr = new int[intArrayLength]; + /// bitHelper = new BitHelper(m_arrayPtr, intArrayLength); + /// *************************************************************************** + /// + /// IMPORTANT: + /// The second ctor args, length, should be specified as the length of the int array, not + /// the logical bit array. Because length is used for bounds checking into the int array, + /// it's especially important to get this correct for the stackalloc version. See the code + /// samples above; this is the value gotten from ToIntArrayLength(). + /// + /// The length ctor argument is the only exception; for other methods -- MarkBit and + /// IsMarked -- pass in values as indices into the logical bit array, and it will be mapped + /// to the position within the array of ints. + /// + /// + + + + + unsafe internal class BitHelper + { // should not be serialized + + private const byte MarkedBitFlag = 1; + private const byte IntSize = 32; + + // m_length of underlying int array (not logical bit array) + private int m_length; + + // ptr to stack alloc'd array of ints + [System.Security.SecurityCritical] + private int* m_arrayPtr; + + // array of ints + private int[] m_array; + + // whether to operate on stack alloc'd or heap alloc'd array + private bool useStackAlloc; + + /// + /// Instantiates a BitHelper with a heap alloc'd array of ints + /// + /// int array to hold bits + /// length of int array + // + // + // + // + [System.Security.SecurityCritical] + internal BitHelper(int* bitArrayPtr, int length) + { + this.m_arrayPtr = bitArrayPtr; + this.m_length = length; + useStackAlloc = true; + } + + /// + /// Instantiates a BitHelper with a heap alloc'd array of ints + /// + /// int array to hold bits + /// length of int array + internal BitHelper(int[] bitArray, int length) + { + this.m_array = bitArray; + this.m_length = length; + } + + /// + /// Mark bit at specified position + /// + /// + // + // + // + [System.Security.SecurityCritical] + internal unsafe void MarkBit(int bitPosition) + { + if (useStackAlloc) + { + int bitArrayIndex = bitPosition / IntSize; + if (bitArrayIndex < m_length && bitArrayIndex >= 0) + { + m_arrayPtr[bitArrayIndex] |= (MarkedBitFlag << (bitPosition % IntSize)); + } + } + else + { + int bitArrayIndex = bitPosition / IntSize; + if (bitArrayIndex < m_length && bitArrayIndex >= 0) + { + m_array[bitArrayIndex] |= (MarkedBitFlag << (bitPosition % IntSize)); + } + } + } + + /// + /// Is bit at specified position marked? + /// + /// + /// + // + // + // + [System.Security.SecurityCritical] + internal unsafe bool IsMarked(int bitPosition) + { + if (useStackAlloc) + { + int bitArrayIndex = bitPosition / IntSize; + if (bitArrayIndex < m_length && bitArrayIndex >= 0) + { + return ((m_arrayPtr[bitArrayIndex] & (MarkedBitFlag << (bitPosition % IntSize))) != 0); + } + return false; + } + else + { + int bitArrayIndex = bitPosition / IntSize; + if (bitArrayIndex < m_length && bitArrayIndex >= 0) + { + return ((m_array[bitArrayIndex] & (MarkedBitFlag << (bitPosition % IntSize))) != 0); + } + return false; + } + } + + /// + /// How many ints must be allocated to represent n bits. Returns (n+31)/32, but + /// avoids overflow + /// + /// + /// + internal static int ToIntArrayLength(int n) + { + return n > 0 ? ((n - 1) / IntSize + 1) : 0; + } + + } +} + +#pragma warning restore CS8618 // 在退出构造函数时,不可为 null 的字段必须包含非 null 值。请考虑声明为可以为 null。 +#pragma warning restore CS8603 // 可能返回 null 引用。 + +#endif \ No newline at end of file diff --git a/src/IFoxCAD.Cad/Algorithms/Graph/Graph.cs b/src/IFoxCAD.Cad/Algorithms/Graph/Graph.cs new file mode 100644 index 0000000..c1d606f --- /dev/null +++ b/src/IFoxCAD.Cad/Algorithms/Graph/Graph.cs @@ -0,0 +1,653 @@ +namespace IFoxCAD.Cad; +using Exception = System.Exception; + +/// +/// 无权无向图实现 +/// IEnumerable 枚举所有顶点; +/// +public sealed class Graph : IGraph, IEnumerable +{ + #region 字段及属性 + /// + /// 存储所有节点的字典,key为顶点的类型,value为邻接表,类型是hashset,不可重复添加点 + /// + /// + readonly Dictionary> vertices = new(); + /// + /// 邻接边表,key为顶点的类型,value为邻接边表,类型是hashset,不可重复添加边 + /// + readonly Dictionary> edges = new(); + /// + /// 为加快索引,引入hash检索 + /// + readonly Dictionary vertexs = new(); + + public int VerticesCount => vertices.Count; + + /// + /// Returns a reference vertex. + /// Time complexity: O(1). + /// + private IGraphVertex? ReferenceVertex + { + get + { + using (var enumerator = vertexs.GetEnumerator()) + { + if (enumerator.MoveNext()) + { + return enumerator.Current.Value; + } + } + + return null; + } + } + IGraphVertex? IGraph.ReferenceVertex => ReferenceVertex; + /// + /// 目前点增加点的顺序号,这个点号不随删点而减少的 + /// + private int insertCount; + #endregion + + #region 构造函数 + public Graph() + { + insertCount = 0; // 每次新建对象就将顶点顺序号归零 + } + #endregion + + #region 顶点及边_增 + /// + /// 向该图添加一个新顶点,但是无边; + /// + /// 点 + /// 创建的顶点 + public IGraphVertex AddVertex(Point3d pt) + { + var str = pt.GetHashString(); + if (vertexs.ContainsKey(str)) + return vertexs[str]; + + var vertex = new GraphVertex(pt, insertCount++); + vertices.Add(vertex, new HashSet()); + edges.Add(vertex, new HashSet()); + + vertexs[str] = vertex; + + return vertex; + } + + /// + /// 向该图添加一个边; + /// + /// + public void AddEdge(Curve3d curve) + { + if (curve == null) + throw new ArgumentNullException(nameof(curve)); + + var start = AddVertex(curve.StartPoint); + var end = AddVertex(curve.EndPoint); + + // 添加起点的邻接表和邻接边 + vertices[start].Add(end); + edges[start].Add(new GraphEdge(end, curve)); + + // 为了保证点顺序,每个点的邻接边必须按起点-终点,所以添加曲线终点时,将添加一个方向的曲线 + var curtmp = (Curve3d)curve.Clone(); + curtmp = curtmp.GetReverseParameterCurve(); + + // 添加终点的邻接表和邻接边 + vertices[end].Add(start); + edges[end].Add(new GraphEdge(start, curtmp)); + } + #endregion + + #region 顶点及边_删 + /// + /// 从此图中删除现有顶点; + /// + /// 点 + public void RemoveVertex(Point3d pt) + { + var str = pt.GetHashString(); + if (vertexs.ContainsKey(str)) + { + var vertex = vertexs[str]; + + // 删除邻接表里的vertex点,先删除后面的遍历可以少一轮 + vertices.Remove(vertex!); + + // 删除其他顶点的邻接表里的vertex点 + foreach (var item in vertices.Values) + item.Remove(vertex!); + + // 删除邻接边表里的vertex点,先删除后面的遍历可以少一轮 + edges.Remove(vertex!); + + // 删除其他顶点的邻接边表的指向vertex的边 + foreach (var item in edges.Values) + { + item.RemoveWhere(x => vertex.Equals(x.TargetVertex)); + //foreach (var edge in item) + //{ + // if (vertex.Equals(edge.TargetVertex)) + // item.Remove(edge); + //} + } + vertexs.Remove(str); + } + } + + /// + /// 从此图中删除一条边; + /// + /// 曲线 + public void RemoveEdge(Curve3d curve) + { + if (curve == null) + throw new ArgumentNullException(nameof(curve)); + + RemoveVertex(curve.StartPoint); + RemoveVertex(curve.EndPoint); + } + #endregion + + #region 顶点和边_查 + /// + /// 我们在给定的来源和目的地之间是否有边? + /// + /// 起点 + /// 终点 + /// 有边返回 ,反之返回 + public bool HasEdge(IGraphVertex source, IGraphVertex dest) + { + if (!vertices.ContainsKey(source) || !vertices.ContainsKey(dest)) + throw new ArgumentException("源或目标不在此图中;"); + + foreach (var item in edges[source]) + { + if (item.TargetVertex == dest) + return true; + } + return false; + } + + /// + /// 获取边 + /// + /// 起点 + /// 终点 + /// + /// 传入的点不在图中时抛出参数异常 + public IEdge? GetEdge(IGraphVertex source, IGraphVertex dest) + { + if (!vertices.ContainsKey(source) || !vertices.ContainsKey(dest)) + throw new ArgumentException("源或目标不在此图中;"); + + foreach (var item in edges[source]) + { + if (item.TargetVertex.Equals(dest)) + return item; + } + return null; + } + + /// + /// 是否存在顶点,此函数目前未发现有啥用 + /// + /// 顶点 + /// 存在顶点返回 ,反之返回 + public bool ContainsVertex(IGraphVertex value) + { + return vertices.ContainsKey(value); + } + #endregion + + #region 获取邻接表和曲线 + /// + /// 获取顶点的邻接表 + /// + /// 顶点 + /// 邻接表 + public HashSet GetAdjacencyList(IGraphVertex vertex) + { + return vertices[vertex]; + } + + /// + /// 获取顶点的邻接边表 + /// + /// 顶点 + /// 邻接边表 + public HashSet GetAdjacencyEdge(IGraphVertex vertex) + { + return edges[vertex]; + } + + /// + /// 根据顶点表获取曲线集合 + /// + /// 顶点表 + /// 曲线表 + public List GetCurves(List graphVertices) + { + var curves = new List(); + for (int i = 0; i < graphVertices.Count - 1; i++) + { + var cur = graphVertices[i]; + var next = graphVertices[i + 1]; + var edge = GetEdge(cur, next); + if (edge is not null) + curves.Add(edge.TargetEdge); + } + var lastedge = GetEdge(graphVertices[^1], graphVertices[0]); + if (lastedge is not null) + curves.Add(lastedge.TargetEdge); + + return curves; + } + #endregion + + #region 克隆及接口实现 + /// + /// 克隆此图;目测是深克隆 + /// + public Graph Clone() + { + var newGraph = new Graph(); + + foreach (var vertex in edges.Values) + foreach (var item in vertex) + newGraph.AddEdge(item.TargetEdge); + + return newGraph; + } + + IGraph IGraph.Clone() + { + return Clone(); + } + + public IEnumerator GetEnumerator() + { + return VerticesAsEnumberable.GetEnumerator(); + } + + IEnumerator? IEnumerable.GetEnumerator() + { + return GetEnumerator() as IEnumerator; + } + + public IEnumerable VerticesAsEnumberable => + vertices.Select(x => x.Key); + #endregion + + #region 方法 + /// + /// 输出点的邻接表的可读字符串 + /// + /// + public string ToReadable() + { + int i = 1; + string output = string.Empty; + foreach (var node in vertices) + { + var adjacents = string.Empty; + + output = string.Format("{1}\r\n{0}-{2}: [", i, output, node.Key.Data.ToString()); + + foreach (var adjacentNode in node.Value) + adjacents = string.Format("{0}{1},", adjacents, adjacentNode.Data.ToString()); + + if (adjacents.Length > 0) + adjacents = adjacents.TrimEnd(new char[] { ',', ' ' }); + + output = string.Format("{0}{1}]", output, adjacents); + i++; + } + return output; + } + #endregion +} + + +/// +/// 邻接表图实现的顶点; +/// IEnumerable 枚举所有邻接点; +/// +public sealed class GraphVertex : IGraphVertex, IEquatable, IComparable, IComparable +{ + #region 属性 + public Point3d Data { get; set; } + public int Index { get; set; } + #endregion + + #region 构造 + /// + /// 邻接表图实现的顶点 + /// + /// 点 + /// 所在节点索引 + public GraphVertex(Point3d value, int index) + { + Data = value; + Index = index; + } + #endregion + + #region 重载运算符_比较 + public bool Equals(IGraphVertex other) + { + return Index == other.Index; + } + + public override bool Equals(object obj) + { + if (obj is null) + return false; + if (obj is not IGraphVertex vertex) + return false; + else + return Equals(vertex); + } + + public override int GetHashCode() + { + return Index; + } + + public int CompareTo(IGraphVertex other) + { + if (Equals(other)) + return 0; + + if (Index < other.Index) + return -1; + else + return 1; + } + + int IComparable.CompareTo(IGraphVertex other) + { + return CompareTo(other); + } + + public int CompareTo(object obj) + { + if (obj is null) + return 1; + + try + { + var other = (GraphVertex)obj; + return CompareTo(other); + } + catch (Exception) + { + throw new ArgumentException("Object is not a IGraphVertex"); + } + } + + public static bool operator ==(GraphVertex person1, GraphVertex person2) + { + if (person1 is null || person2 is null) + return Equals(person1, person2); + + return person1.Equals(person2); + } + + public static bool operator !=(GraphVertex person1, GraphVertex person2) + { + if (person1 is null || person2 is null) + return !Equals(person1, person2); + + return !person1.Equals(person2); + } + #endregion +} + + +/// +/// 无向图中边的定义 +/// +public sealed class GraphEdge : IEdge, IEquatable +{ + #region 属性 + public IGraphVertex TargetVertex { get; set; } + public Curve3d TargetEdge { get; set; } + #endregion + + #region 构造 + /// + /// 无向图中边的定义 + /// + /// 下一点 + /// 下一点之间的曲线 + public GraphEdge(IGraphVertex target, Curve3d edge) + { + TargetVertex = target; + TargetEdge = edge; + } + #endregion + + #region 重载运算符_比较 + public bool Equals(GraphEdge other) + { + if (other is null) + return false; + return TargetVertex == other.TargetVertex && + TargetEdge == other.TargetEdge; + } + public override bool Equals(object obj) + { + if (obj is null) + return false; + if (obj is not GraphEdge personObj) + return false; + else + return Equals(personObj); + } + + public override int GetHashCode() + { + return (TargetVertex.GetHashCode(), TargetEdge.GetHashCode()).GetHashCode(); + } + public static bool operator ==(GraphEdge person1, GraphEdge person2) + { + if (person1 is null || person2 is null) + return Equals(person1, person2); + + return person1.Equals(person2); + } + public static bool operator !=(GraphEdge person1, GraphEdge person2) + { + if (person1 is null || person2 is null) + return !Equals(person1, person2); + + return !person1.Equals(person2); + } + #endregion +} + + +/// +/// 深度优先搜索; +/// +public sealed class DepthFirst +{ + #region 公共方法 + /// + /// 存储所有的边 + /// +#if true + public List> Curve3ds { get; } = new(); +#else + public List> Curve3ds { get; } = new(); +#endif + private HashSet Curved { get; } = new(); + + + /// + /// 找出所有的路径 + /// + /// 图 + public void FindAll(IGraph graph) + { + var total = new HashSet(); + //var graphtmp = graph.Clone(); + foreach (var item in graph.VerticesAsEnumberable) + { + Dfs(graph, new LinkedHashSet { item }, total); + total.Add(item); + } + } + #endregion + + #region 内部方法 + /// + /// 递归 DFS; + /// + /// 图 + /// 已经遍历的路径 +#if true + void Dfs(IGraph graph, LinkedHashSet visited, HashSet totalVisited) + { + var adjlist = graph.GetAdjacencyList(/*startNode*/ visited.First!.Value); // O(1) + foreach (var nextNode in adjlist) // O(n) + { + if (totalVisited.Contains(nextNode)) + { + continue; + } + // 如果下一个点未遍历过 + if (!visited.Contains(nextNode)) // O(1) + { + // 将下一点加入路径集合,并进行下一次递归 + var sub = new LinkedHashSet { nextNode }; + sub.AddRange(visited); // O(n) + Dfs(graph, sub, totalVisited); + } + // 如果下一点遍历过,并且路径大于2,说明已经找到起点 + else if (visited.Count > 2 && nextNode.Equals(visited.Last!.Value)) + { + // 将重复的路径进行过滤,并把新的路径存入结果 + var curstr = Gethashstring(visited); // O(n) + if (Isnew(curstr)) // O(1) + { + Curve3ds.Add(visited); + Curved.Add(curstr.Item1); + } + } + } + } + + + + +#else + + void Dfs(IGraph graph, List visited) + { + var startNode = visited[0]; + IGraphVertex nextNode; + List sub; + + var adjlist = graph.GetAdjacencyList(startNode).ToList(); // O(n) + for (int i = 0; i < adjlist.Count; i++) // O(n) + { + nextNode = adjlist[i]; + + // 如果下一个点未遍历过 + if (!visited.Contains(nextNode)) // O(n) + { + // 将下一点加入路径集合,并进行下一次递归 + sub = new List { nextNode }; + sub.AddRange(visited); // O(n) + Dfs(graph, sub); + } + + // 如果下一点遍历过,并且路径大于2,说明已经找到起点 + else if (visited.Count > 2 && nextNode.Equals(visited[^1])) + { + // 将重复的路径进行过滤,并把新的路径存入结果 + var cur = RotateToSmallest(visited); // O(n) + var inv = Invert(cur,cur[0]); // O(n) + + var curstr = Gethashstring(cur,inv); + //Env.Print(curstr); + if (Isnew(curstr)) + { + Curve3ds.Add(cur); + Curved.Add(curstr.Item1); + } + } + } + } +#endif + + + + + + + + + /// + /// 将列表旋转到最小的值为列表起点 + /// + /// + /// + static List RotateToSmallest(List lst) + { + var index = lst.IndexOf(lst.Min()); + return lst.Skip(index).Concat(lst.Take(index)).ToList(); + } + + /// + /// 将列表反向,并旋转到起点为最小值 + /// + /// + /// + static List Invert(List lst, IGraphVertex vertex) + { + var tmp = lst.ToList(); + tmp.Reverse(); + var index = tmp.IndexOf(vertex); + return tmp.Skip(index).Concat(lst.Take(index)).ToList(); + } + + static (string, string) Gethashstring(List pathone, List pathtwo) + { + var one = new string[pathone.Count]; + var two = new string[pathtwo.Count]; + for (int i = 0; i < pathone.Count; i++) + { + one[i] = pathone[i].Index.ToString(); + two[i] = pathtwo[i].Index.ToString(); + } + return (string.Join("-", one), string.Join("-", two)); + } + + static (string, string) Gethashstring(LinkedHashSet path) + { + var one = new string[path.Count]; + var two = new string[path.Count]; + path.For(path.MinNode!, (i, ver1, ver2) => { + one[i] = ver1.Index.ToString(); + two[i] = ver2.Index.ToString(); + }); + return (string.Join("-", one), string.Join("-", two)); + } + + + bool Isnew((string, string) path) + { + return !Curved.Contains(path.Item1) && !Curved.Contains(path.Item2); + } + + + #endregion +} \ No newline at end of file diff --git a/src/IFoxCAD.Cad/Algorithms/Graph/IGraph.cs b/src/IFoxCAD.Cad/Algorithms/Graph/IGraph.cs new file mode 100644 index 0000000..73547b7 --- /dev/null +++ b/src/IFoxCAD.Cad/Algorithms/Graph/IGraph.cs @@ -0,0 +1,98 @@ +namespace IFoxCAD.Cad; + +/// +/// 无向图 +/// +public interface IGraph +{ + /// + /// 顶点的数量 + /// + /// + int VerticesCount { get; } + + /// + /// 是否存在顶点 + /// + /// 顶点键 + /// + bool ContainsVertex(IGraphVertex key); + + /// + /// 顶点的迭代器 + /// + /// + IEnumerable VerticesAsEnumberable { get; } + + /// + /// 是否有边 + /// + /// 源顶点 + /// 目的顶点 + /// + bool HasEdge(IGraphVertex source, IGraphVertex destination); + /// + /// 图克隆函数 + /// + /// + IGraph Clone(); + /// + /// 获取边 + /// + /// + /// + /// + IEdge? GetEdge(IGraphVertex source, IGraphVertex dest); + /// + /// 邻接表 + /// + /// + /// + HashSet GetAdjacencyList(IGraphVertex vertex); + /// + /// 邻接边表 + /// + /// + /// + HashSet GetAdjacencyEdge(IGraphVertex vertex); + IGraphVertex? ReferenceVertex { get; } + + void RemoveVertex(Point3d pt); + void RemoveEdge(Curve3d curve); + +} + +/// +/// 无向图顶点 +/// +/// 顶点数据类型 +public interface IGraphVertex : IComparable +{ + /// + /// 顶点的键 + /// + /// + int Index { get; set; } + + /// + /// 顶点的数据 + /// + Point3d Data { get; } +} +/// +/// 无向图边 +/// +public interface IEdge +{ + /// + /// 边 + /// + Curve3d TargetEdge { get; } + /// + /// 目标顶点 + /// + IGraphVertex TargetVertex { get; } +} + + + diff --git a/src/IFoxCAD.Cad/Algorithms/QuadTree/QuadEntity.cs b/src/IFoxCAD.Cad/Algorithms/QuadTree/QuadEntity.cs new file mode 100644 index 0000000..c5f7f11 --- /dev/null +++ b/src/IFoxCAD.Cad/Algorithms/QuadTree/QuadEntity.cs @@ -0,0 +1,25 @@ +namespace IFoxCAD.Cad; + +/* + * 这个类存在的意义是为了不暴露Rect类字段 + * 同时利用了Rect类字段的快速 + * 提供到外面去再继承 + */ + +/// +/// 四叉树图元 +/// +public class QuadEntity : Rect +{ + /// + /// 四叉树图元 + /// + /// 包围盒 + public QuadEntity(Rect box) + { + _X = box._X; + _Y = box._Y; + _Top = box._Top; + _Right = box._Right; + } +} \ No newline at end of file diff --git a/src/IFoxCAD.Cad/Algorithms/QuadTree/QuadTree.cs b/src/IFoxCAD.Cad/Algorithms/QuadTree/QuadTree.cs new file mode 100644 index 0000000..0eb1bf8 --- /dev/null +++ b/src/IFoxCAD.Cad/Algorithms/QuadTree/QuadTree.cs @@ -0,0 +1,259 @@ +/* + * 四叉树维基百科 http://en.wikipedia.org/wiki/Quadtree + * 四叉树是一种分区空间的算法,更快找出内部或外部给定区域. + * 通过一个正交矩形边界进行中心点分裂四个正交矩形, + * 插入时候会一直分裂四个正交矩形, + * 当分裂四个节点都无法单独拥有 图元包围盒 就停止分裂,并且你属于这四个节点的父亲. + * (不包含就是面积少了,就这么一句话看代码看半天), + * 还可以通过限制树的深度实现加速. + * + * 第一版: https://www.codeproject.com/script/Articles/ViewDownloads.aspx?aid=30535 + * + * 第二版: 找邻居 + * https://blog.csdn.net/dive_shallow/article/details/112438050 + * https://geidav.wordpress.com/2017/12/02/advanced-octrees-4-finding-neighbor-nodes/ + * + * 1.根节点:控制根节点从而控制所有节点 + * 2.子节点:包含自身根节点,插入矩形的时候进行递归分裂自身,和实现查找. + * 3.接口:约束都要有正交矩形,否则无法调用"包含"方法 + * 4.选择模式:模仿cad的窗选和框选 + */ +namespace IFoxCAD.Cad; + +/// +/// 根节点控制器 +/// +/// 类型接口约束必须有正交矩形 +public class QuadTree where TEntity : QuadEntity +{ + #region 成员 + /// + /// 根节点 + /// + QuadTreeNode _rootNode; + + /// + /// 四叉树节点的数目 + /// + public int Count { get => _rootNode.CountSubTree; } + + /// + /// 点容器(红黑树) + /// + SortedSet _points; + #endregion + + #region 构造 + /// + /// 四叉树根节点控制器 + /// + /// 四叉树矩形范围 + public QuadTree(Rect rect) + { + _rootNode = new QuadTreeNode(rect, null, 0);//初始化根节点 + _points = new(); + } + #endregion + + #region 方法 + /// + /// 通过根节点插入数据项 + /// + /// + public void Insert(TEntity ent) + { + /* + * 图元点 是不分裂空间的,加入一个红黑树内部. + */ + if (ent.IsPoint) + { + _points.Add(ent); + return; + } + + while (!_rootNode.Contains(ent)) + { + /* + * 四叉树插入时候,如果超出根边界,就需要扩展 + * 扩展时候有一个要求,当前边界要作为扩展边界的一个象限,也就是反向分裂 + * + * 创建新根,计算原根在新根的位置, + * 替换指针:获取新分裂的节点的父节点,判断它哪个儿子是它, + * 替换之后可能仍然不包含图元边界,再循环计算. + */ + var sq_Left = _rootNode._X; + var sq_Botton = _rootNode._Y; + var sq_Right = _rootNode._Right; + var sq_Top = _rootNode._Top; + if (ent._Y >= _rootNode._Y)//上↑增殖 + { + if (ent._X >= _rootNode._X) + { + //右上↗增殖 + sq_Right += _rootNode.Width; + sq_Top += _rootNode.Height; + } + else + { + //左上↖增殖 + sq_Left -= _rootNode.Width; + sq_Top += _rootNode.Height; + } + } + else//在下↓ + { + if (ent._X >= _rootNode._X) + { + //右下↘增殖 + sq_Right += _rootNode.Width; + sq_Botton -= _rootNode.Height; + } + else + { + //左下↙增殖 + sq_Left -= _rootNode.Width; + sq_Botton -= _rootNode.Height; + } + } + //扩大2次方 + var rectSquare = new Rect(sq_Left, sq_Botton, sq_Right, sq_Top); + + //四叉树的旧根要作为四分之一插入 + //新根中计算原根 + //把 旧根节点 连接到 新根节点 上面,然后新根成为根 + var newRoot = new QuadTreeNode(rectSquare, null, 0); + var insert = newRoot.Insert(_rootNode); + if (insert is null) + throw new("四叉树:新根尺寸不对"); + if (!insert.Equals(_rootNode)) + throw new("四叉树:新旧节点大小不一致,无法连接"); + + var insPar = insert.Parent; + _rootNode.Parent = insPar; + if (insPar is null) + return; + + if (_rootNode.Equals(insPar.RightTopTree)) + insPar.RightTopTree = _rootNode; + else if (_rootNode.Equals(insPar.RightBottomTree)) + insPar.RightBottomTree = _rootNode; + else if (_rootNode.Equals(insPar.LeftBottomTree)) + insPar.LeftBottomTree = _rootNode; + else if (_rootNode.Equals(insPar.LeftTopTree)) + insPar.LeftTopTree = _rootNode; + else + throw new("四叉树:新节点不对,无法连接"); + + //其后的子节点层数全部增加层数, + //要加多少层取决于当前根边界属于新根边界的所在层 + var depth = insert.Depth; + if (depth == 0) + throw new("四叉树:插入节点是0,造成错误"); + _rootNode.ForEach(node => { + node.Depth += depth; + return false; + }); + + //交换根控制 + _rootNode = newRoot; + } + + _rootNode.Insert(ent); + } + + + /// + /// 查询四叉树,返回给定区域的数据项 + /// + /// 矩形选区查询 + /// + public List Query(Rect rect, QuadTreeSelectMode selectMode = QuadTreeSelectMode.IntersectsWith) + { + QuadTreeEvn.SelectMode = selectMode; + + var results = new List(); + //选择图元 + _rootNode.Query(rect, results); + //选择点 + var ptge = _points.GetEnumerator(); + switch (selectMode) + { + case QuadTreeSelectMode.IntersectsWith: + case QuadTreeSelectMode.Contains: + /* 由于红黑树的方法 _points.GetViewBetween() + * 过滤只能过滤X区间,Y区间还是要过滤, + * 那么我就只能用这样的方法加速了 + * + * 而更好的方式是不用红黑树,去加入一个点云数据来进行,可谓是编程无极限.... + */ + while (ptge.MoveNext()) + { + var ptEnt = ptge.Current; + if (rect._X <= ptEnt._X && ptEnt._X <= rect._Right) + { + if (rect._Y <= ptEnt._Y && ptEnt._Y <= rect.Top) + results.Add(ptEnt); + } + else if (ptEnt._X > rect._Right) + break;//超过后面范围就break,因为红黑树已经排序 + } + break; + default: + throw new("四叉树:" + nameof(selectMode)); + } + return results; + } + + /// + /// 删除子节点 + /// + /// 根据范围删除 + public void Remove(Rect rect) + { + _rootNode.Remove(rect); + } + + /// + /// 删除子节点 + /// + /// 根据图元删除 + public void Remove(TEntity ent) + { + _rootNode.Remove(ent); + } + + /// + /// 找到附近节点图元 + /// + [Obsolete("找附近节点的并不是最近的图元")] + public TEntity? FindNeibor(Rect rect, QuadTreeFindMode findMode) + { + return _rootNode.FindNeibor(rect, findMode); + } + + /// + /// 找到附近图元 + /// + /// + /// + public TEntity? FindNearEntity(Rect rect) + { + return _rootNode.FindNearEntity(rect); + } + + /// + /// 执行四叉树中特定的行为 + /// + /// + public void ForEach(QTAction action) + { + _rootNode.ForEach(action); + } + + /// + /// 委托:四叉树节点上执行一个操作 + /// + /// + public delegate bool QTAction(QuadTreeNode obj); + #endregion +} \ No newline at end of file diff --git a/src/IFoxCAD.Cad/Algorithms/QuadTree/QuadTreeEvn.cs b/src/IFoxCAD.Cad/Algorithms/QuadTree/QuadTreeEvn.cs new file mode 100644 index 0000000..16d518b --- /dev/null +++ b/src/IFoxCAD.Cad/Algorithms/QuadTree/QuadTreeEvn.cs @@ -0,0 +1,26 @@ +#pragma warning disable CA2211 // 非常量字段应当不可见 +namespace IFoxCAD.Cad; + +public class QuadTreeEvn +{ + /// + /// 最小的节点有一个面积(一定要大于0) + /// + public static double MinArea = 1e-6; + + /// + /// 选择模式 + /// + public static QuadTreeSelectMode SelectMode; + + /// + /// 最大深度 + /// + public static int QuadTreeMaximumDepth = 2046; + + /// + /// 节点内容超过就分裂 + /// + public static int QuadTreeContentsCountSplit = 20; +} +#pragma warning restore CA2211 // 非常量字段应当不可见 \ No newline at end of file diff --git a/src/IFoxCAD.Cad/Algorithms/QuadTree/QuadTreeNode.cs b/src/IFoxCAD.Cad/Algorithms/QuadTree/QuadTreeNode.cs new file mode 100644 index 0000000..86cb5b4 --- /dev/null +++ b/src/IFoxCAD.Cad/Algorithms/QuadTree/QuadTreeNode.cs @@ -0,0 +1,818 @@ +namespace IFoxCAD.Cad; + +/// +/// 子节点 +/// +/// +public class QuadTreeNode + : Rect + where TEntity : QuadEntity +{ + #region 成员 + /// + /// 子节点:第一象限:右上↗ + /// + public QuadTreeNode? RightTopTree; + /// + /// 子节点:第二象限:左上↖ + /// + public QuadTreeNode? LeftTopTree; + /// + /// 子节点:第三象限:左下↙ + /// + public QuadTreeNode? LeftBottomTree; + /// + /// 子节点:第四象限:右下↘ + /// + public QuadTreeNode? RightBottomTree; + /// + /// 所有子节点 + /// + QuadTreeNode[] Nodes + { + get + { + return new QuadTreeNode[] + { + RightTopTree!, + LeftTopTree!, + LeftBottomTree!, + RightBottomTree!, + }; + } + } + /// + /// 所有子节点是空的 + /// + bool NodesIsEmpty => RightTopTree is null && LeftTopTree is null && LeftBottomTree is null && RightBottomTree is null; + + /// + /// 父节点 + /// + public QuadTreeNode? Parent; + /// + /// 节点的在四叉树的深度 + /// + public int Depth; + + // 注意,内容没有限制:这不是 impement 四叉树的标准方法 + /// (节点图元是交叉线压着的,并不是矩形范围内全部,因为这是四叉树的特性决定) + /// + /// 本节点:内容 + /// + public List Contents; + + /// + /// 本节点和旗下所有子节点:内容群 + /// + public void ContentsSubTree(List results) + { + if (Contents is null) + return; + results.AddRange(Contents); + var nodes = Nodes; + for (int i = 0; i < nodes.Length; i++) + nodes[i]?.ContentsSubTree(results); + } + + /// + /// 本节点和旗下所有子节点:内容群数量 + /// + public int CountSubTree + { + get + { + if (Contents is null) + return 0; + int count = Contents.Count; + + var nodes = Nodes; + for (int i = 0; i < nodes.Length; i++) + { + var node = nodes[i]; + if (node is null) + continue; + count += node.CountSubTree; + } + return count; + } + } + #endregion + + #region 构造 + /// + /// 四叉树节点 + /// + /// 当前节点边界 + /// 父节点 + /// 节点深度 + public QuadTreeNode(Rect box, QuadTreeNode? parent, int depth) + { + _X = box._X; + _Y = box._Y; + _Right = box._Right; + _Top = box._Top; + + Parent = parent; + Depth = depth; + Contents = new(); + } + #endregion + + #region 增 + /// + /// 将原有节点插入用 + /// + /// + internal QuadTreeNode? Insert(Rect rect) + { + if (!Contains(rect)) + return null; + + //四叉树分裂,将当前节点分为四个子节点 + if (NodesIsEmpty) + CreateChildren(); + + //当前节点边界 包含 图元包围盒 就插入 + //退出递归:4个节点都不完全包含 + //4个节点的上层 + var nodes = Nodes; + for (int i = 0; i < nodes.Length; i++) + { + var node = nodes[i]; + if (node is null) + continue; + + if (node.Equals(rect)) + { + rect = node; + return node.Insert(rect); + } + } + return this; + } + + /// + /// 将数据项递归插入四叉树 + /// + /// + public QuadTreeNode? Insert(TEntity ent) + { + if (!Contains(ent)) + { + //Debug.WriteLine("不在四叉树边界范围"); + //Trace.WriteLine("不在四叉树边界范围"); + return null; + } + + // if (ent.IsPoint) + // { + // //找到最后一层包含它的节点,然后加入它 + // //因此是跳过分裂矩形的,以免造成无限递归 + // var minNode = GetMinNode(ent); + // minNode.Contents.Add(ent); + // return minNode; + // } + +#if true2 + //方案二: + //内容数超过才分裂,防止树深度过高,但是多选过滤时候慢一点 + if (Contents.Count > QuadTreeEvn.QuadTreeContentsCountSplit) + { + //分裂出四个子节点 + if (_nodesIsEmpty) + { + CreateChildren(); + //分裂之后将当前层的内容扔到四个子节点, + //如果被压着,那么就不会扔到下面 + for (int i = Contents.Count - 1; i >= 0; i--) + { + var minNode = GetMinNode(Contents[i].Box); + minNode.Contents.Add(Contents[i]); + Contents.RemoveAt(i); + } + } + else + { + //没有分裂的话,就递归 + //退出递归:4个节点都不完全包含,内容就是他们的父亲 + var nodes = _Nodes; + for (int i = 0; i < nodes.Length; i++) + { + var node = nodes[i]; + if (node is null) + continue; + + //这里需要中断.(匿名方法ForEach无法中断,会造成父节点加入内容) + if (node.Contains(ent)) + return node.Insert(ent); + } + } + } +#else + //方案一:分裂到最细节点 + + //分裂出四个子节点 + if (NodesIsEmpty) + CreateChildren(); + + //4个子节点开始递归 + //退出递归:4个节点都不完全包含,内容就是他们的父亲 + var nodes = Nodes; + for (int i = 0; i < nodes.Length; i++) + { + var node = nodes[i]; + if (node is null) + continue; + //这里需要中断.(匿名方法ForEach无法中断,会造成父节点加入内容) + if (node.Contains(ent)) + return node.Insert(ent); + } +#endif + + //为什么要用容器? + //相同包围盒或者四叉树分割线压着多个. + this.Contents.Add(ent); + return this; + } + + /// + /// 创建子节点 + /// + void CreateChildren() + { + // 最小面积控制节点深度,但是这样可能导致树分成高,引起爆栈 + if (Depth > QuadTreeEvn.QuadTreeMaximumDepth) + return; + var recs = RectSplit(this); + var de = Depth + 1; + RightTopTree = new QuadTreeNode(recs[0], this, de); + LeftTopTree = new QuadTreeNode(recs[1], this, de); + LeftBottomTree = new QuadTreeNode(recs[2], this, de); + RightBottomTree = new QuadTreeNode(recs[3], this, de); + } + + /// + /// 矩形分裂为四个 + /// + /// + /// + Rect[] RectSplit(Rect box) + { + var halfWidth = box.Width / 2.0; + var halfHeight = box.Height / 2.0; + + var upperRight = new Rect(box._X + halfWidth, box._Y + halfHeight, box._Right, box._Top); + var upperLeft = new Rect(box._X, box._Y + halfHeight, box._Right - halfWidth, box._Top); + var lowerleft = new Rect(box._X, box._Y, box._Right - halfWidth, box._Top - halfHeight);//基础 + var lowerRight = new Rect(box._X + halfWidth, box._Y, box._Right, box._Top - halfHeight); + + //依照象限顺序输出 + return new Rect[] { upperRight, upperLeft, lowerleft, lowerRight }; + } + #endregion + + #region 删 + /// + /// 删除图元 + /// + /// 根据图元删除 + public bool Remove(TEntity easeEnt) + { + //通过图元id删除无疑是非常低效的, + //1.相当于在所有的容器查找它,但是移除只会移除一次, + // 因此必须要求图元只会加入一次,才能中断检索剩余分支. + //2.这个代价还是太高,因此我们还是要默认条件,图元载入一次之后,不再改动. + //3.不再改动也不太合理,因为cad图元还是可以修改的 + + //1.处理内容 + if (Contents.Remove(easeEnt)) + { + if (CountSubTree == 0) + this.Clear(this); + return true; + } + + //2.递归子节点移除 + var nodes = Nodes; + for (int i = 0; i < nodes.Length; i++) + { + var node = nodes[i]; + if (node is null) + continue; + if (node.Remove(easeEnt)) //递归进入子节点删除内容 + return true; //删除成功就中断其他节点的搜索 + } + return false; + } + + /// + /// 递归进入最下层节点,然后开始清理 + /// + /// + void Clear(QuadTreeNode node) + { + var nodes = Nodes; + for (int i = 0; i < nodes.Length; i++) + nodes[i]?.Clear(nodes[i]); + + node.Contents.Clear(); + //node.Contents = null;//重复加入时候会出错 + node.RightTopTree = null; + node.LeftTopTree = null; + node.LeftBottomTree = null; + node.RightBottomTree = null; + node.Parent = null; + //node.Box = zoreRect; + } + + /// + /// 删除子节点内容 + /// + /// 根据范围删除 + public void Remove(Rect queryArea) + { + //本节点内容移除 + if (Contents is not null && Contents.Count > 0)//从最上层的根节点开始进入 + { + for (int i = Contents.Count - 1; i >= 0; i--) + { + var ent = Contents[i]; + //移除之后,如果容器是0,那么这里不能直接 Contents=null, + //因为此节点下面可能还有节点, + //需要判断了其后数量0才可以清理. + //否则其后还有内容,那么此节点就是仍然可以用的. + if (queryArea.Contains(ent)) + Contents.Remove(ent); + } + } + + //同插入一样 + //跳到指定节点再搜索这个节点下面的图元 + var nodes = Nodes; + for (int i = 0; i < nodes.Length; i++) + { + var node = nodes[i]; + if (node is null) + continue; + if (node.NodesIsEmpty) + continue; + + //此节点边界 完全包含 查询区域,则转到该节点,并跳过其余节点(打断此循环) + if (node.Contains(queryArea)) + { + node.Remove(queryArea); + break; + } + //查询区域 完全包含 此节点边界,提取此节点全部内容 + //跳过分析碰撞,并继续循环搜索其他节点 + if (queryArea.Contains(node)) + { + node.Clear(node); + continue; + } + //查询区域 与 此节点四边形边线碰撞 查询该四边形中,并继续循环搜索其他节点 + //1,角点碰撞 2,边界碰撞 + if (node.IntersectsWith(queryArea)) + node.Remove(queryArea); + } + + //本节点内容移除之后,旗下还有内容的话, + //会跳过此处,再进入子节点进行递归,直到最后一个节点 + if (CountSubTree == 0) + Clear(this); + } + #endregion + + #region 查 + /// + /// 查询范围内的实体 + /// + /// 查询矩形 + /// + public void Query(Rect queryArea, List results) + { + GetCurrentContents(queryArea, results); + + //遍历子节点 + var nodes = Nodes; + for (int i = 0; i < nodes.Length; i++) + { + var node = nodes[i]; + if (node is null) + continue; + //子节点的4个子节点都是空的, + //那么表示元素会在子节点这一层啊... + if (node.NodesIsEmpty) + continue; + + //此节点边界 完全包含 查询区域,则转到该节点,并跳过其余节点(打断此循环) + if (node.Contains(queryArea)) + { + node.Query(queryArea, results); + break; + } + //查询区域 完全包含 此节点边界,提取此节点全部内容 + //跳过分析碰撞,并继续循环搜索其他节点 + if (queryArea.Contains(node)) + { + node.ContentsSubTree(results); + continue; + } + //查询区域 与 此节点四边形边线碰撞 查询该四边形中,并继续循环搜索其他节点 + //1,角点碰撞 2,边界碰撞 + if (node.IntersectsWith(queryArea)) + node.Query(queryArea, results); + } + } + + /// + /// 获取本节点内容 + /// + /// + /// + void GetCurrentContents(Rect queryArea, List results) + { + //遍历当前节点内容,加入方式取决于碰撞模式 + if (QuadTreeEvn.SelectMode == QuadTreeSelectMode.IntersectsWith) + { + for (int i = 0; i < Contents.Count; i++) + { + var ent = Contents[i]; + if (queryArea.IntersectsWith(ent)) + results.Add(ent); + } + } + else + { + for (int i = 0; i < Contents.Count; i++) + { + var ent = Contents[i]; + if (queryArea.Contains(ent)) + results.Add(ent); + } + } + } + + /// + /// 找临近图元 + /// + /// 查找矩形 + /// + public TEntity? FindNearEntity(Rect queryArea) + { + TEntity? resultEntity = default; + //1.找到 查找矩形 所在的节点,利用此节点的矩形. + var queryNode = GetMinNode(queryArea); + var queryAreaCenter = queryArea.CenterPoint; + + //2.从根开始搜索 + // 如果搜索父亲的父亲的...内容群,它不是距离最近的,只是节点(亲属关系)最近 + // 储存找过的<图元,距离> + var entDic = new Dictionary(); + + var old = QuadTreeEvn.SelectMode; + QuadTreeEvn.SelectMode = QuadTreeSelectMode.IntersectsWith; + while (true) + { + //循环找父节点大小 + var hw = queryNode.Width / 2.0; + var hh = queryNode.Height / 2.0; + //3.利用选区中心扩展一个节点边界大小的矩形.从而选择图元 + // 再判断图元的与目标的距离,找到最小距离,即为最近 + var minPt = new Point2d(queryAreaCenter.X - hw, queryAreaCenter.Y - hh); + var maxPt = new Point2d(queryAreaCenter.X + hw, queryAreaCenter.Y + hh); + var ents = new List(); + Query(new Rect(minPt, maxPt), ents); + for (int i = 0; i < ents.Count; i++) + { + var ent = ents[i]; + if (entDic.ContainsKey(ent)) + continue; + var dis = ent.CenterPoint.GetDistanceTo(queryAreaCenter); + if (dis > 1e-6)//剔除本身 + entDic.Add(ent, dis); + } + if (entDic.Count > 0) + { + resultEntity = entDic.OrderBy(a => a.Value).First().Key; + break; + } + if (queryNode.Parent is null)//最顶层就退出 + break; + queryNode = queryNode.Parent;//利用父节点矩形进行变大选区 + } + QuadTreeEvn.SelectMode = old; + return resultEntity; + } + + /// + /// 找临近节点的图元 + /// + /// 查找矩形 + /// 查找什么方向 + /// + [Obsolete("找附近节点的并不是最近的图元")] + public TEntity? FindNeibor(Rect queryArea, QuadTreeFindMode findMode) + { + TEntity? resultEntity = default; + //1.找到 查找矩形 所在的节点,利用此节点的矩形. + //2.利用节点矩形是分裂的特点,边和边必然贴合. + //3.找到方向 findMode 拥有的节点,然后查找节点的内容 + var queryNode = GetMinNode(queryArea); + + bool whileFlag = true; + //同一个节点可能包含邻居,因为四叉树的加入是图元压线, + //那么就在这里搜就得了,用中心点决定空间位置 + //但是本空间的图元可能都比它矮,无法满足条件 + if (queryNode.CountSubTree > 1) + { + resultEntity = GetNearestNeighbor(queryNode, findMode, queryArea); + if (resultEntity is null || resultEntity.CenterPoint == queryArea.CenterPoint) + whileFlag = true; + else + whileFlag = false; + } + + while (whileFlag) + { + //同一个父节点是临近的,优先获取 兄弟节点 的内容. + //循环了第二次是北方兄弟的节点, + //但是这不是一个找到临近图元的方法, + //因为临近的可能是父亲的父亲的父亲...另一个函数 FindNearEntity 写 + //本方案也仅仅作为找北方节点 + var parent = queryNode.Parent; + if (parent is not null) + { + switch (findMode) + { + case QuadTreeFindMode.Top: + { + //下格才获取上格,否则导致做了无用功,上格就直接获取邻居了 + if (parent.LeftBottomTree == queryNode || parent.RightBottomTree == queryNode) + resultEntity = GetNearestNeighbor(parent, findMode, queryArea); + } + break; + case QuadTreeFindMode.Bottom: + { + if (parent.LeftTopTree == queryNode || parent.RightTopTree == queryNode) + resultEntity = GetNearestNeighbor(parent, findMode, queryArea); + } + break; + case QuadTreeFindMode.Left: + { + if (parent.RightTopTree == queryNode || parent.RightBottomTree == queryNode) + resultEntity = GetNearestNeighbor(parent, findMode, queryArea); + } + break; + case QuadTreeFindMode.Right: + { + if (parent.LeftTopTree == queryNode || parent.LeftBottomTree == queryNode) + resultEntity = GetNearestNeighbor(parent, findMode, queryArea); + } + break; + } + } + if (resultEntity is not null) + break; + + //通过 所在节点 找 邻居节点, + //拿到 邻居节点 下面的所有内容(图元) + //内容可能是空的,再从邻居那往北找...如果找到了四叉树最外层,仍然没有内容,退出循环 + var neiborNode = FindNeiborNode(queryNode, findMode); + if (neiborNode is null) + continue; + if (neiborNode.CountSubTree > 0) + { + resultEntity = GetNearestNeighbor(neiborNode, findMode, queryArea); + break; + } + if (neiborNode.Parent is null)//如果找到了四叉树最外层,仍然没有内容,退出循环 + break; + queryNode = neiborNode; + } + + return resultEntity; + } + + /// + /// 查找节点的(本内容和子内容)与(查找面积)矩形中点对比,找到最近一个内容 + /// + /// 查找面积 + /// 查找方向 + /// 查找节点 + /// + TEntity? GetNearestNeighbor(QuadTreeNode queryNode, QuadTreeFindMode findMode, Rect queryArea) + { + TEntity? results = default; + + var lst = new List(); + var qcent = queryArea.CenterPoint; + + switch (findMode) + { + case QuadTreeFindMode.Top: + { + //取出Y比queryArea的还大的一个,是最近的那个 + var qy = qcent.Y; + queryNode.ContentsSubTree(lst); + lst.ForEach(ent => + { + if (ent.CenterPoint.Y > qy) + lst.Add(ent); + }); + lst = lst.OrderBy(ent => ent.CenterPoint.Y).ToList(); + } + break; + case QuadTreeFindMode.Bottom: + { + var qy = qcent.Y; + queryNode.ContentsSubTree(lst); + lst.ForEach(ent => + { + if (ent.CenterPoint.Y < qy) + lst.Add(ent); + }); + lst = lst.OrderByDescending(ent => ent.CenterPoint.Y).ToList(); + } + break; + case QuadTreeFindMode.Left: + { + var qx = qcent.Y; + queryNode.ContentsSubTree(lst); + lst.ForEach(ent => + { + if (ent.CenterPoint.X > qx) + lst.Add(ent); + }); + lst = lst.OrderBy(ent => ent.CenterPoint.X).ToList(); + } + break; + case QuadTreeFindMode.Right: + { + var qx = qcent.Y; + queryNode.ContentsSubTree(lst); + lst.ForEach(ent => + { + if (ent.CenterPoint.X < qx) + lst.Add(ent); + }); + lst = lst.OrderByDescending(ent => ent.CenterPoint.X).ToList(); + } + break; + } + + if (lst.Count > 0) + return lst[0];//可能就是本体重叠 + return results; + } + + /// + /// 找包含它的最小分支 + /// + /// 查询的矩形 + /// 节点 + QuadTreeNode GetMinNode(Rect queryArea) + { + var nodes = Nodes; + for (int i = 0; i < nodes.Length; i++) + { + var node = nodes[i]; + if (node is null) + continue; + + //边界包含查询面积,那么再递归查询, + //直到最后四个都不包含,那么上一个就是图元所在节点 + if (node.Contains(queryArea)) + return node.GetMinNode(queryArea);//中断后面的范围,才可以返回正确的 + } + return this; + } + + /// + /// 四叉树找邻居节点(相同或更大) + /// + /// 源节点 + /// 方向 + /// + QuadTreeNode? FindNeiborNode(QuadTreeNode tar, QuadTreeFindMode findMode) + { + var parent = tar.Parent; + if (parent is null) + return null; + switch (findMode) + { + case QuadTreeFindMode.Top: + { + //判断当前节点在父节点的位置,如果是在 下格 就取对应的 上格 + if (tar == parent.LeftBottomTree) + return parent.LeftTopTree; + if (tar == parent.RightBottomTree) + return parent.RightTopTree; + //否则就是上格 + //找父节点的北方邻居..也就是在爷节点上面找 + //递归,此时必然是 下格,就必然返回 上格,然后退出递归 + var parentNeibor = FindNeiborNode(parent, QuadTreeFindMode.Top); + //父节点的北方邻居 无 子节点 + if (parentNeibor is null || parentNeibor.RightTopTree is null) + return parentNeibor;//返回父节点的北方邻居,比较大 + //父节点的北方邻居 有 子节点,剩下条件就只有这两 + + // 如果直接返回,那么是(相同或更大), + // 而找邻近图元需要的是这个(相同或更大)下面的图元,在外面对这个格子内图元用坐标分析就好了 + if (tar == parent.LeftTopTree) + return parentNeibor.LeftBottomTree; + return parentNeibor.RightBottomTree; + } + case QuadTreeFindMode.Bottom: + { + if (tar == parent.LeftTopTree) + return parent.LeftBottomTree; + if (tar == parent.RightTopTree) + return parent.RightBottomTree; + var parentNeibor = FindNeiborNode(parent, QuadTreeFindMode.Bottom); + if (parentNeibor is null || parentNeibor.RightTopTree is null) + return parentNeibor; + if (tar == parent.LeftBottomTree) + return parentNeibor.LeftTopTree; + return parentNeibor.RightTopTree; + } + case QuadTreeFindMode.Right: + { + if (tar == parent.LeftTopTree) + return parent.RightTopTree; + if (tar == parent.LeftBottomTree) + return parent.RightBottomTree; + var parentNeibor = FindNeiborNode(parent, QuadTreeFindMode.Right); + if (tar == parent.RightTopTree) + return parentNeibor?.LeftTopTree; + return parentNeibor?.LeftBottomTree; + } + case QuadTreeFindMode.Left: + { + if (tar == parent.RightTopTree) + return parent.LeftTopTree; + if (tar == parent.RightBottomTree) + return parent.LeftBottomTree; + var parentNeibor = FindNeiborNode(parent, QuadTreeFindMode.Left); + if (tar == parent.LeftTopTree) + return parentNeibor?.RightTopTree; + return parentNeibor?.RightBottomTree; + } + } + return null; + } + #endregion + + #region 改 + /// + /// 所有的点归类到最小包围它的空间 + /// + //public void PointsToMinNode() + //{ + // ForEach(node => + // { + // for (int i = 0; i < node.Contents.Count; i++) + // { + // var ent = node.Contents[i]; + // if (ent.IsPoint) + // { + // //如果最小包含!=当前,就是没有放在最适合的位置 + // var queryNode = GetMinNode(ent); + // if (queryNode != node) + // { + // node.Remove(ent); + // queryNode.Contents.Add(ent); + // } + // } + // } + // return false; + // }); + //} + #endregion + + #region 方法 + /// + /// 递归全部节点(提供给根用的,所以是全部) + /// + /// QTAction + public bool ForEach(QuadTree.QTAction action) + { + //执行本节点 + if (action(this)) + return true; + + //递归执行本节点的子节点 + var nodes = Nodes; + for (int i = 0; i < nodes.Length; i++) + { + var node = nodes[i]; + if (node is null) + continue; + if (node.ForEach(action)) + break; + } + return false; + } + #endregion +} \ No newline at end of file diff --git a/src/IFoxCAD.Cad/Algorithms/QuadTree/QuadTreeSelectMode.cs b/src/IFoxCAD.Cad/Algorithms/QuadTree/QuadTreeSelectMode.cs new file mode 100644 index 0000000..d180236 --- /dev/null +++ b/src/IFoxCAD.Cad/Algorithms/QuadTree/QuadTreeSelectMode.cs @@ -0,0 +1,21 @@ +namespace IFoxCAD.Cad; + +/// +/// 四叉树选择模式 +/// +public enum QuadTreeSelectMode +{ + IntersectsWith, //碰撞到就选中 + Contains, //全包含才选中 +} + +/// +/// 四叉树查找方向 +/// +public enum QuadTreeFindMode +{ + Top = 1, //上 + Bottom = 2, //下 + Left = 4, //左 + Right = 8, //右 +} \ No newline at end of file diff --git a/src/IFoxCAD.Cad/Algorithms/QuadTree/Rect.cs b/src/IFoxCAD.Cad/Algorithms/QuadTree/Rect.cs new file mode 100644 index 0000000..c533ddb --- /dev/null +++ b/src/IFoxCAD.Cad/Algorithms/QuadTree/Rect.cs @@ -0,0 +1,605 @@ +using System.Diagnostics; + +namespace IFoxCAD.Cad; + +/// +/// Linq Distinct 消重比较两点在容差范围内就去除 +/// +public class TolerancePoint2d : IEqualityComparer +{ + readonly double _tolerance; + public TolerancePoint2d(double tolerance = 1e-6) + { + _tolerance = tolerance; + } + + public bool Equals(Point2d a, Point2d b)//Point3d是struct不会为null + { + /*默认规则是==是0容差,Eq是有容差*/ + // 方形限定 + // 在 0~1e-6 范围实现 圆形限定 则计算部分在浮点数6位后,没有啥意义 + // 在 0~1e-6 范围实现 从时间和CPU消耗来说,圆形限定 都没有 方形限定 的好 + if (_tolerance <= 1e-6) + return Math.Abs(a.X - b.X) <= _tolerance && Math.Abs(a.Y - b.Y) <= _tolerance; + + // 圆形限定 + // DistanceTo 分别对XYZ进行了一次乘法,也是总数3次乘法,然后求了一次平方根 + // (X86.CPU.FSQRT指令用的牛顿迭代法/软件层面可以使用快速平方根....我还以为CPU会采取快速平方根这样的取表操作) + return a.IsEqualTo(b, new Tolerance(_tolerance, _tolerance)); + } + + public int GetHashCode(Point2d obj) + { + //结构体直接返回 obj.GetHashCode(); Point3d ToleranceDistinct3d + //因为结构体是用可值叠加来判断?或者因为结构体兼备了一些享元模式的状态? + //而类是构造的指针,所以取哈希值要改成x+y+z..s给Equals判断用,+是会溢出,所以用^ + return (int)obj.X ^ (int)obj.Y;// ^ (int)obj.Z; + } +} + + +[Serializable] +[StructLayout(LayoutKind.Sequential)] +[DebuggerDisplay("{DebuggerDisplay,nq}")] +[DebuggerTypeProxy(typeof(Rect))] +public class Rect : IEquatable, IComparable +{ + [DebuggerBrowsable(DebuggerBrowsableState.Never)] + private string DebuggerDisplay => ToString("f4"); + +#pragma warning disable CA2211 // 非常量字段应当不可见 + public static TolerancePoint2d RectTolerance = new(1e-6); + public static Tolerance CadTolerance = new(1e-6, 1e-6); +#pragma warning restore CA2211 // 非常量字段应当不可见 + + #region 字段 + //这里的成员不要用{get}封装成属性,否则会导致跳转了一次函数, + //10w图元将会从187毫秒变成400毫秒 + //不用 protected 否则子类传入Rect对象进来无法用 + internal double _X; + internal double _Y; + internal double _Right; + internal double _Top; + #endregion + + #region 成员 + public double X => _X; + public double Y => _Y; + public double Left => _X; + public double Bottom => _Y; + public double Right => _Right; + public double Top => _Top; + + public double Width => _Right - _X; + public double Height => _Top - _Y; + public double Area + { + get + { + var ar = (_Right - _X) * (_Top - _Y); + return ar < 1e-10 ? 0 : ar; + } + } + + public Point2d MinPoint => LeftLower; + public Point2d MaxPoint => RightUpper; + public Point2d CenterPoint => Midst; + + /// + /// 左下Min + /// + public Point2d LeftLower => new(_X, _Y); + + /// + /// 左中 + /// + public Point2d LeftMidst => new(_X, Midst.Y); + + /// + /// 左上 + /// + public Point2d LeftUpper => new(_X, _Top); + + /// + /// 右上Max + /// + public Point2d RightUpper => new(_Right, _Top); + + /// + /// 右中 + /// + public Point2d RightMidst => new(_Right, Midst.Y); + + /// + /// 右下 + /// + public Point2d RightBottom => new(_Right, _Y); + + /// + /// 中间 + /// + public Point2d Midst => new(((_Right - _X) * 0.5) + _X, ((_Top - _Y) * 0.5) + _Y); + + /// + /// 中上 + /// + public Point2d MidstUpper => new(Midst.X, _Top); + + /// + /// 中下 + /// + public Point2d MidstBottom => new(Midst.X, _Y); + + /// + /// 是一个点 + /// 水平或垂直直线包围盒是面积是0,所以面积是0不一定是点 + /// + public bool IsPoint => Math.Abs(_X - _Right) < 1e-10 && Math.Abs(_Y - _Top) < 1e-10; + #endregion + + #region 构造 + public Rect() { } + + /// + /// 矩形类 + /// + /// 左 + /// 下 + /// 右 + /// 上 + public Rect(double left, double bottom, double right, double top) + { + _X = left; + _Y = bottom; + _Right = right; + _Top = top; + } + + /// + /// 构造矩形类 + /// + /// + /// + /// 是否检查大小 + public Rect(Point2d p1, Point2d p3, bool check = false) + { + if (check) + { + _X = Math.Min(p1.X, p3.X); + _Y = Math.Min(p1.Y, p3.Y); + _Right = Math.Max(p1.X, p3.X); + _Top = Math.Max(p1.Y, p3.Y); + } + else + { + _X = p1.X; + _Y = p1.Y; + _Right = p3.X; + _Top = p3.Y; + } + } + #endregion + + #region 重载运算符_比较 + public override bool Equals(object? obj) + { + return this.Equals(obj as Rect); + } + public bool Equals(Rect? b) + { + return this.Equals(b, 1e-6);/*默认规则是==是0容差,Eq是有容差*/ + } + public static bool operator !=(Rect? a, Rect? b) + { + return !(a == b); + } + public static bool operator ==(Rect? a, Rect? b) + { + //此处地方不允许使用==null,因为此处是定义 + if (b is null) + return a is null; + else if (a is null) + return false; + if (ReferenceEquals(a, b))//同一对象 + return true; + + return a.Equals(b, 0); + } + + /// + /// 比较核心 + /// + public bool Equals(Rect? b, double tolerance = 1e-6) + { + if (b is null) + return false; + if (ReferenceEquals(this, b)) //同一对象 + return true; + + return Math.Abs(_X - b._X) < tolerance && + Math.Abs(_Right - b._Right) < tolerance && + Math.Abs(_Top - b._Top) < tolerance && + Math.Abs(_Y - b._Y) < tolerance; + } + + public override int GetHashCode() + { + return (((int)_X ^ (int)_Y).GetHashCode() ^ (int)_Right).GetHashCode() ^ (int)_Top; + } + #endregion + + #region 包含 + public bool Contains(Point2d Point2d) + { + return Contains(Point2d.X, Point2d.Y); + } + public bool Contains(double x, double y) + { + return _X <= x && x <= _Right && + _Y <= y && y <= _Top; + } + + /// + /// 四个点都在内部就是包含 + /// + /// + /// + public bool Contains(Rect rect) + { + return _X <= rect._X && rect._Right <= _Right && + _Y <= rect._Y && rect._Top <= _Top; + } + + /// + /// 一个点在内部就是碰撞 + /// + /// + /// + public bool IntersectsWith(Rect rect) + { + return rect._X <= _Right && _X <= rect._Right && + rect._Top >= _Y && rect._Y <= _Top; + } + #endregion + + #region 方法 + /// + /// 获取共点 + /// + /// + public Point2d[] GetCommonPoint(Rect other) + { + return ToPoints().Intersect(other.ToPoints(), RectTolerance).ToArray(); + } + + public Point2d[] ToPoints() + { + Point2d a = MinPoint;//min + Point2d b = new(_Right, _Y); + Point2d c = MaxPoint;//max + Point2d d = new(_X, _Top); + return new Point2d[] { a, b, c, d }; + } + + public (Point2d boxMin, Point2d boxRigthDown, Point2d boxMax, Point2d boxLeftUp) ToPoints4() + { + Point2d a = MinPoint;//min + Point2d b = new(_Right, _Y); + Point2d c = MaxPoint;//max + Point2d d = new(_X, _Top); + return (a, b, c, d); + } + + /// + /// 四周膨胀 + /// + /// + public Rect Expand(double d) + { + return new Rect(_X - d, _Y - d, _Right + d, _Top + d); + } + + /// + /// 是否矩形(带角度) + /// + /// + /// + public static bool IsRectAngle(List? ptList, double tolerance = 1e-8) + { + if (ptList == null) + throw new ArgumentNullException(nameof(ptList)); + + var pts = ptList.ToList(); + /* 消重,不这里设置,否则这不是一个正确的单元测试 + * //var ptList = pts.Distinct().ToList(); + * var ptList = pts.DistinctExBy((a, b) => a.DistanceTo(b) < 1e-6).ToList(); + */ + if (ptList.Count == 5) + { + //首尾点相同移除最后 + if (pts[0].IsEqualTo(pts[^1], CadTolerance)) + pts.RemoveAt(pts.Count - 1); + } + if (pts.Count != 4) + return false; + + //最快的方案 + //点乘求值法:(为了处理 正梯形/平行四边形 需要三次) + //这里的容差要在1e-8内,因为点乘的三次浮点数乘法会令精度变低 + var dot = DotProductValue(pts[0], pts[1], pts[3]); + if (Math.Abs(dot) < tolerance) + { + dot = DotProductValue(pts[1], pts[2], pts[0]); + if (Math.Abs(dot) < tolerance) + { + dot = DotProductValue(pts[2], pts[3], pts[1]); + return Math.Abs(dot) < tolerance; + } + } + return false; + } + + /// + /// 点积,求值 + /// 1.是两个向量的长度与它们夹角余弦的积 + /// 2.求四个点是否矩形使用 + /// + /// 原点 + /// 点 + /// 点 + /// 0方向相同,夹角0~90度;=0相互垂直;<0方向相反,夹角90~180度]]> + static double DotProductValue(Point2d o, Point2d a, Point2d b) + { + var oa = o.GetVectorTo(a); + var ob = o.GetVectorTo(b); + return (oa.X * ob.X) + (oa.Y * ob.Y); + } + + /// + /// 是否轴向矩形(无角度) + /// + public static bool IsRect(List? ptList, double tolerance = 1e-10) + { + if (ptList == null) + throw new ArgumentNullException(nameof(ptList)); + + var pts = ptList.ToList(); + if (ptList.Count == 5) + { + //首尾点相同移除最后 + if (pts[0].IsEqualTo(pts[^1], CadTolerance)) + pts.RemoveAt(pts.Count - 1); + } + if (pts.Count != 4) + return false; + + return Math.Abs(pts[0].X - pts[3].X) < tolerance && + Math.Abs(pts[0].Y - pts[1].Y) < tolerance && + Math.Abs(pts[1].X - pts[2].X) < tolerance && + Math.Abs(pts[2].Y - pts[3].Y) < tolerance; + } + + /// + /// 获取点集的包围盒的最小点和最大点(无角度) + /// + /// + public static (Point2d boxMin, Point2d boxMax) GetMinMax(IEnumerable pts) + { + var xMin = double.MaxValue; + var xMax = double.MinValue; + var yMin = double.MaxValue; + var yMax = double.MinValue; + //var zMin = double.MaxValue; + //var zMax = double.MinValue; + + pts.ForEach(p => { + xMin = Math.Min(p.X, xMin); + xMax = Math.Max(p.X, xMax); + yMin = Math.Min(p.Y, yMin); + yMax = Math.Max(p.Y, yMax); + //zMin = Math.Min(p.Z, zMin); + //zMax = Math.Max(p.Z, zMax); + }); + return (new Point2d(xMin, yMin), new Point2d(xMax, yMax)); + } + + /// + /// 矩形点序逆时针排列,将min点[0],max点是[3](带角度) + /// + /// + /// + public static bool RectAnglePointOrder(List? pts) + { + if (pts == null) + throw new ArgumentNullException(nameof(pts)); + + if (!Rect.IsRectAngle(pts)) + return false; + + //获取min和max点(非包围盒) + pts = pts.OrderBy(a => a.X).ThenBy(a => a.Y).ToList(); + var minPt = pts.First(); + var maxPt = pts.Last(); + var link = new LoopList(); + link.AddRange(pts); + + pts.Clear(); + //排序这四个点,左下/右下/右上/左上 + var node = link.Find(minPt); + for (int i = 0; i < 4; i++) + { + pts.Add(node!.Value); + node = node.Next; + } + //保证是逆时针 + var isAcw = CrossAclockwise(pts[0], pts[1], pts[2]); + if (!isAcw) + (pts[3], pts[1]) = (pts[1], pts[3]); + return true; + } + + /// + /// 叉积,二维叉乘计算 + /// + /// 传参是向量,表示原点是0,0 + /// 传参是向量,表示原点是0,0 + /// 其模为a与b构成的平行四边形面积 + static double Cross(Vector2d a, Vector2d b) + { + return a.X * b.Y - a.Y * b.X; + } + + /// + /// 叉积,二维叉乘计算 + /// + /// 原点 + /// oa向量 + /// ob向量,此为判断点 + /// 返回值有正负,表示绕原点四象限的位置变换,也就是有向面积 + static double Cross(Point2d o, Point2d a, Point2d b) + { + return Cross(o.GetVectorTo(a), o.GetVectorTo(b)); + } + + /// + /// 叉积,逆时针方向为真 + /// + /// 直线点1 + /// 直线点2 + /// 判断点 + /// b点在oa的逆时针 + static bool CrossAclockwise(Point2d o, Point2d a, Point2d b) + { + return Cross(o, a, b) > -1e-6;//浮点数容差考虑 + } + +#if !WinForm + public Entity ToPolyLine() + { + var bv = new List(); + var pts = ToPoints(); + Polyline pl = new(); + pl.SetDatabaseDefaults(); + pts.ForEach((i, vertex) => { + pl.AddVertexAt(i, vertex, 0, 0, 0); + }); + return pl; + } +#endif + + /// + /// 列扫碰撞检测(碰撞算法) + /// 比四叉树还快哦~ + /// + /// + /// 继承Rect的集合 + /// 先处理集合每一个成员;返回true就跳过后续委托 + /// 碰撞,返回两个碰撞的成员;返回true就跳过后续委托 + /// 后处理集合每一个成员 + public static void XCollision(List box, + Func firstProcessing, + Func collisionProcessing, + Action lastProcessing) where T : Rect + { + //先排序X:不需要Y排序,因为Y的上下浮动不共X .ThenBy(a => a.Box.Y) + //因为先排序就可以有序遍历x区间,超过就break,达到更快 + box = box.OrderBy(a => a._X).ToList(); + + //遍历所有图元 + for (int i = 0; i < box.Count; i++) + { + var oneRect = box[i]; + if (firstProcessing(oneRect)) + continue; + + bool actionlast = true; + + //搜索范围要在 one 的头尾中间的部分 + for (int j = i + 1; j < box.Count; j++) + { + var twoRect = box[j]; + //x碰撞:矩形2的Left 在 矩形1[Left-Right]闭区间;穿过的话,也必然有自己的Left因此不需要处理 + if (oneRect._X <= twoRect._X && twoRect._X <= oneRect._Right) + { + //y碰撞,那就是真的碰撞了 + if ((oneRect._Top >= twoRect._Top && twoRect._Top >= oneRect._Y) /*包容上边*/ + || (oneRect._Top >= twoRect._Y && twoRect._Y >= oneRect._Y) /*包容下边*/ + || (twoRect._Top >= oneRect._Top && oneRect._Y >= twoRect._Y)) /*穿过*/ + { + if (collisionProcessing(oneRect, twoRect)) + actionlast = false; + } + //这里想中断y高过它的无意义比较, + //但是必须排序Y,而排序Y必须同X,而这里不是同X(而是同X区间),所以不能中断 + //而做到X区间排序,就必须创造一个集合,再排序这个集合, + //会导致每个图元都拥有一次X区间集合,开销更巨大(因此放弃). + } + else + break;//因为已经排序了,后续的必然超过 x碰撞区间 + } + + if (actionlast) + lastProcessing(oneRect); + } + } + + #endregion + + #region 转换类型 +#if !WinForm + // 隐式转换(相当于是重载赋值运算符) + //public static implicit operator Rect(System.Windows.Rect rect) + //{ + // return new Rect(rect.Left, rect.Bottom, rect.Right, rect.Top); + //} + public static implicit operator Rect(System.Drawing.RectangleF rect) + { + return new Rect(rect.Left, rect.Bottom, rect.Right, rect.Top); + } + public static implicit operator Rect(System.Drawing.Rectangle rect) + { + return new Rect(rect.Left, rect.Bottom, rect.Right, rect.Top); + } +#endif + + #region ToString + public sealed override string ToString() + { + return ToString(null, null); + } + public string ToString(IFormatProvider? provider) + { + return ToString(null, provider); + } + public string ToString(string? format = null, IFormatProvider? formatProvider = null) + { + return $"({_X.ToString(format, formatProvider)},{_Y.ToString(format, formatProvider)})," + + $"({_Right.ToString(format, formatProvider)},{_Top.ToString(format, formatProvider)})"; + + // return $"X={_X.ToString(format, formatProvider)}," + + // $"Y={_Y.ToString(format, formatProvider)}," + + // $"Right={_Right.ToString(format, formatProvider)}," + + // $"Top={_Top.ToString(format, formatProvider)}"; + } + + /*为了红黑树,加入这个*/ + public int CompareTo(Rect rect) + { + if (rect == null) + return -1; + if (_X < rect._X) + return -1; + else if (_X > rect._X) + return 1; + else if (_Y < rect._Y)/*x是一样的*/ + return -1; + else if (_Y > rect._Y) + return 1; + return 0;/*全部一样*/ + } + #endregion + + #endregion + + +} diff --git a/src/IFoxCAD.Cad/Algorithms/QuadTree/Utility.cs b/src/IFoxCAD.Cad/Algorithms/QuadTree/Utility.cs new file mode 100644 index 0000000..2a0be8e --- /dev/null +++ b/src/IFoxCAD.Cad/Algorithms/QuadTree/Utility.cs @@ -0,0 +1,60 @@ +using System; + +namespace IFoxCAD.Cad; + +public static class Utility +{ + /// + /// 带有随机种子的随机数 + /// 为什么这样写随机种子呢 + /// + /// + public static Random GetRandom() + { + var tick = DateTime.Now.Ticks; + + /* + * 知识准备: + * | 高位64位 | 低位32位 | + * Convert.ToString(int.MaxValue, 2)输出二进制 "1111111111111111111111111111111" 31个;最高位是符号位,所以少1位 + * Convert.ToString(long.MaxValue,2)输出二进制,刚好长一倍 "11111111111111111111111111111111 1111111111111111111111111111111" 63个;最高位是符号位,所以少1位 + * Convert.ToString(0xffffffffL, 2)int.MaxValue再按位多1 "1 1111111111111111111111111111111" 32个;前面的0不会打印出来 + * + * Convert.ToString(long.MaxValue>>32, 2)相当于平移高位的到低位范围,也就是上面少打印的二进制 + * 验证右移是不是高位保留,答案是 + * var a = Convert.ToInt64("101111111111111111111111111111111111111111111111111111111111111", 2); + * Convert.ToString(a >> 32,2); + * + * 解释代码: + * 0x01: + * (int)(long.MaxValue & 0xffffffffL) | (int)(long.MaxValue >> 32); + * Convert.ToString(long.MaxValue & 0xffffffffL, 2)//去掉高位:"11111111111111111111111111111111" 32个,再强转int + * 按位与&是保证符号位肯定是1,其他尽可能为0,高位被去掉只是MaxValue&0的原因,强转才是去掉高位..."尽可能"一词带来第一次随机性 + * 0x02: + * Convert.ToString((long.MaxValue >> 32), 2) //去掉低位: "1111111111111111111111111111111" 31个,再强转int + * 按位或|是尽可能为1..."尽可能"一词带来第二次随机性 + * + */ + + var tickSeeds = (int)(tick & 0xffffffffL) | (int)(tick >> 32); + return new Random(tickSeeds); + } + + /// + /// 随机颜色 + /// + /// + public static System.Drawing.Color RandomColor + { + get + { + var ran = GetRandom(); + int R = ran.Next(255); + int G = ran.Next(255); + int B = ran.Next(255); + B = (R + G > 400) ? R + G - 400 : B;//0 : 380 - R - G; + B = (B > 255) ? 255 : B; + return System.Drawing.Color.FromArgb(R, G, B); + } + } +} \ No newline at end of file diff --git a/src/IFoxCAD.Cad/ExtensionMethod/BulgeVertexWidth.cs b/src/IFoxCAD.Cad/ExtensionMethod/BulgeVertexWidth.cs new file mode 100644 index 0000000..dc7abd3 --- /dev/null +++ b/src/IFoxCAD.Cad/ExtensionMethod/BulgeVertexWidth.cs @@ -0,0 +1,85 @@ +namespace IFoxCAD; + +/// +/// 多段线的顶点,凸度,头宽,尾宽 +/// +[Serializable] +public class BulgeVertexWidth +{ + /// + /// 顶点X + /// + public double X; + /// + /// 顶点Y + /// + public double Y; + /// + /// 凸度 + /// + public double Bulge; + /// + /// 头宽 + /// + public double StartWidth; + /// + /// 尾宽 + /// + public double EndWidth; + + public Point2d Vertex => new(X, Y); + + public BulgeVertexWidth() { } + + /// + /// 多段线的顶点,凸度,头宽,尾宽 + /// + public BulgeVertexWidth(double vertex_X, double vertex_Y, + double bulge = 0, + double startWidth = 0, + double endWidth = 0) + { + X = vertex_X; + Y = vertex_Y; + Bulge = bulge; + StartWidth = startWidth; + EndWidth = endWidth; + } + + /// + /// 多段线的顶点,凸度,头宽,尾宽 + /// + public BulgeVertexWidth(Point2d vertex, + double bulge = 0, + double startWidth = 0, + double endWidth = 0) + : this(vertex.X, vertex.Y, bulge, startWidth, endWidth) + { } + + /// + /// 多段线的顶点,凸度,头宽,尾宽 + /// + public BulgeVertexWidth(BulgeVertex bv) + : this(bv.Vertex.X, bv.Vertex.Y, bv.Bulge) + { } + + /// + /// 多段线的顶点,凸度,头宽,尾宽 + /// + /// 多段线 + /// 子段编号 + public BulgeVertexWidth(Polyline pl, int index) + { + var pt = pl.GetPoint2dAt(index);//这里可以3d + X = pt.X; + Y = pt.Y; + Bulge = pl.GetBulgeAt(index); + StartWidth = pl.GetStartWidthAt(index); + EndWidth = pl.GetEndWidthAt(index); + } + + public BulgeVertex ToBulgeVertex() + { + return new BulgeVertex(Vertex, Bulge); + } +} diff --git a/src/IFoxCAD.Cad/ExtensionMethod/CollectionEx.cs b/src/IFoxCAD.Cad/ExtensionMethod/CollectionEx.cs index 406a33d..bfbed62 100644 --- a/src/IFoxCAD.Cad/ExtensionMethod/CollectionEx.cs +++ b/src/IFoxCAD.Cad/ExtensionMethod/CollectionEx.cs @@ -1,135 +1,198 @@ -using Autodesk.AutoCAD.DatabaseServices; -using Autodesk.AutoCAD.Geometry; -using System; -using System.Collections.Generic; -using System.Collections.Specialized; -using System.Linq; - -namespace IFoxCAD.Cad +namespace IFoxCAD.Cad; + +/// +/// 集合扩展类 +/// +public static class CollectionEx { /// - /// 集合扩展类 + /// 对象id迭代器转换为集合 /// - public static class CollectionEx + /// 对象id的迭代器 + /// 对象id集合 + public static ObjectIdCollection ToCollection(this IEnumerable ids) { - /// - /// 对象id迭代器转换为集合 - /// - /// 对象id的迭代器 - /// 对象id集合 - public static ObjectIdCollection ToCollection(this IEnumerable ids) - { - return new ObjectIdCollection(ids.ToArray()); - } + return new ObjectIdCollection(ids.ToArray()); + } - /// - /// 实体迭代器转换为集合 - /// - /// 对象类型 - /// 实体对象的迭代器 - /// 实体集合 - public static DBObjectCollection ToCollection(this IEnumerable objs) where T : DBObject - { - DBObjectCollection objCol = new(); - foreach (T obj in objs) - objCol.Add(obj); - return objCol; - } + /// + /// 实体迭代器转换为集合 + /// + /// 对象类型 + /// 实体对象的迭代器 + /// 实体集合 + public static DBObjectCollection ToCollection(this IEnumerable objs) where T : DBObject + { + DBObjectCollection objCol = new(); + foreach (T obj in objs) + objCol.Add(obj); + return objCol; + } - /// - /// double 数值迭代器转换为 double 数值集合 - /// - /// double 数值迭代器 - /// double 数值集合 - public static DoubleCollection ToCollection(this IEnumerable doubles) - { - return new DoubleCollection(doubles.ToArray()); - } + /// + /// double 数值迭代器转换为 double 数值集合 + /// + /// double 数值迭代器 + /// double 数值集合 + public static DoubleCollection ToCollection(this IEnumerable doubles) + { + return new DoubleCollection(doubles.ToArray()); + } - /// - /// 二维点迭代器转换为二维点集合 - /// - /// 二维点迭代器 - /// 二维点集合 - public static Point2dCollection ToCollection(this IEnumerable pts) - { - return new Point2dCollection(pts.ToArray()); - } + /// + /// 二维点迭代器转换为二维点集合 + /// + /// 二维点迭代器 + /// 二维点集合 + public static Point2dCollection ToCollection(this IEnumerable pts) + { + return new Point2dCollection(pts.ToArray()); + } - /// - /// 三维点迭代器转换为三维点集合 - /// - /// 三维点迭代器 - /// 三维点集合 - public static Point3dCollection ToCollection(this IEnumerable pts) - { - return new Point3dCollection(pts.ToArray()); - } + /// + /// 三维点迭代器转换为三维点集合 + /// + /// 三维点迭代器 + /// 三维点集合 + public static Point3dCollection ToCollection(this IEnumerable pts) + { + return new Point3dCollection(pts.ToArray()); + } + + /// + /// 对象id集合转换为对象id列表 + /// + /// 对象id集合 + /// 对象id列表 + public static List ToList(this ObjectIdCollection ids) + { + return ids.Cast().ToList(); + } - /// - /// 对象id集合转换为对象id列表 - /// - /// 对象id集合 - /// 对象id列表 - public static List ToList(this ObjectIdCollection ids) - { - return ids.Cast().ToList(); - } - public static List ToList(this StringCollection strs) + /// + /// 遍历集合的迭代器,执行action委托 + /// + /// 集合值的类型 + /// 集合 + /// 要运行的委托 + public static void ForEach(this IEnumerable source, Action action) + { + foreach (var element in source) { - return strs.Cast().ToList(); + action?.Invoke(element); } - - /* Cast不进行过滤,而是直接强转 - var s = new System.Collections.ArrayList(); - s.Add("1"); - s.Add("a"); - s.Add(5666); - - var aaa = s.Cast().ToList(); - System.InvalidCastException: 指定的转换无效。 - + List..ctor(IEnumerable) - + System.Linq.Enumerable.ToList(IEnumerable) - - var aaa = s.Cast().ToList(); - System.InvalidCastException: 无法将类型为“System.Int32”的对象强制转换为类型“System.String”。 - + List..ctor(IEnumerable) - + System.Linq.Enumerable.ToList(IEnumerable) - */ - - /// - /// 遍历集合的迭代器,执行action委托 - /// - /// 集合值的类型 - /// 集合 - /// 要运行的委托 - public static void ForEach(this IEnumerable source, Action action) + } + /// + /// 同时遍历集合索引和值的迭代器,执行action委托 + /// + /// 集合值的类型 + /// 集合 + /// 要运行的委托 + public static void ForEach(this IEnumerable source, Action action) + { + int i = 0; + foreach (var item in source) { - if (action is null) - throw new ArgumentNullException(nameof(action)); - foreach (var element in source) - action.Invoke(element); + action?.Invoke(i, item); + i++; } + } + - /// - /// 同时遍历集合索引和值的迭代器,执行action委托 - /// - /// 集合值的类型 - /// 集合 - /// 要运行的委托 - public static void ForEach(this IEnumerable source, Action action) + #region 关键字集合 + public enum KeywordName + { + GlobalName, + LocalName, + DisplayName, + } + + /// + /// 含有关键字 + /// + /// 关键字集合 + /// 关键字 + /// 关键字容器字段名 + /// true含有 + public static bool Contains(this KeywordCollection collection, string name, + KeywordName keywordName = KeywordName.GlobalName) + { + bool contains = false; + switch (keywordName) { - if (action is null) - throw new ArgumentNullException(nameof(action)); - int i = 0; - foreach (var item in source) - { - action.Invoke(i, item); - i++; - } + case KeywordName.GlobalName: + for (int i = 0; i < collection.Count; i++) + if (collection[i].GlobalName == name) + { + contains = true; + break; + } + break; + case KeywordName.LocalName: + for (int i = 0; i < collection.Count; i++) + if (collection[i].LocalName == name) + { + contains = true; + break; + } + break; + case KeywordName.DisplayName: + for (int i = 0; i < collection.Count; i++) + if (collection[i].DisplayName == name) + { + contains = true; + break; + } + break; + default: + break; } + return contains; + } + + /// + /// 获取词典, + /// KeywordCollection是允许重复关键字的,没有哈希索引,在多次判断时候会遍历多次O(n),所以生成一个词典进行O(1) + /// + /// + /// + public static Dictionary GetDict(this KeywordCollection collection) + { + Dictionary map = new(); + for (int i = 0; i < collection.Count; i++) + map.Add(collection[i].GlobalName, collection[i].DisplayName); + return map; + } + #endregion + + #region IdMapping + /// + /// 旧块名 + /// + /// + /// + public static List GetKeys(this IdMapping idmap) + { + List ids = new(); + foreach (IdPair item in idmap) + ids.Add(item.Key); + return ids; } + + /// + /// 新块名 + /// + /// + /// + public static List GetValues(this IdMapping idmap) + { + List ids = new(); + foreach (IdPair item in idmap) + ids.Add(item.Value); + return ids; + } + #endregion } diff --git a/src/IFoxCAD.Cad/ExtensionMethod/Curve2dEx.cs b/src/IFoxCAD.Cad/ExtensionMethod/Curve2dEx.cs index 7acb61e..51ff4e2 100644 --- a/src/IFoxCAD.Cad/ExtensionMethod/Curve2dEx.cs +++ b/src/IFoxCAD.Cad/ExtensionMethod/Curve2dEx.cs @@ -1,318 +1,312 @@ -using Autodesk.AutoCAD.DatabaseServices; -using Autodesk.AutoCAD.Geometry; -using System; +namespace IFoxCAD.Cad; -namespace IFoxCAD.Cad +/// +/// 二维解析类曲线转换为二维实体曲线扩展类 +/// +public static class Curve2dEx { + #region Curve2d + /// - /// 二维解析类曲线转换为二维实体曲线扩展类 + /// 按矩阵转换Ge2d曲线为Db曲线 /// - - public static class Curve2dEx + /// Ge2d曲线 + /// 曲线转换矩阵 + /// Db曲线 + public static Curve? ToCurve(this Curve2d curve, Matrix3d mat) { - #region Curve2d - - /// - /// 按矩阵转换Ge2d曲线为Db曲线 - /// - /// Ge2d曲线 - /// 曲线转换矩阵 - /// Db曲线 - public static Curve? ToCurve(this Curve2d curve, Matrix3d mat) + return curve switch { - return curve switch - { - LineSegment2d li => ToCurve(li, mat), - NurbCurve2d nu => ToCurve(nu, mat), - EllipticalArc2d el => ToCurve(el, mat), - CircularArc2d ci => ToCurve(ci, mat), - PolylineCurve2d po => ToCurve(po, mat), - Line2d l2 => ToCurve(l2, mat), - CompositeCurve2d co => ToCurve(co, mat), - _ => null - }; - } + LineSegment2d li => ToCurve(li, mat), + NurbCurve2d nu => ToCurve(nu, mat), + EllipticalArc2d el => ToCurve(el, mat), + CircularArc2d ci => ToCurve(ci, mat), + PolylineCurve2d po => ToCurve(po, mat), + Line2d l2 => ToCurve(l2, mat), + CompositeCurve2d co => ToCurve(co, mat), + _ => null + }; + } + + #endregion Curve2d - #endregion Curve2d + #region CircularArc2d - #region CircularArc2d + /// + /// 判断点是否位于圆内及圆上 + /// + /// 二维解析类圆弧对象 + /// 二维点 + /// 位于圆内及圆上返回 ,反之返回 + public static bool IsIn(this CircularArc2d ca2d, Point2d pnt) + { + return ca2d.IsOn(pnt) || ca2d.IsInside(pnt); + } - /// - /// 判断点是否位于圆内及圆上 - /// - /// 二维解析类圆弧对象 - /// 二维点 - /// 位于圆内及圆上返回 ,反之返回 - public static bool IsIn(this CircularArc2d ca2d, Point2d pnt) + /// + /// 将二维解析类圆弧转换为实体圆或者圆弧,然后进行矩阵变换 + /// + /// 二维解析类圆弧对象 + /// 变换矩阵 + /// 实体圆或者圆弧 + public static Curve ToCurve(this CircularArc2d ca2d, Matrix3d mat) + { + Curve c = ToCurve(ca2d); + c.TransformBy(mat); + return c; + } + + /// + /// 将二维解析类圆弧转换为实体圆或者圆弧 + /// + /// 二维解析类圆弧对象 + /// 实体圆或者圆弧 + public static Curve ToCurve(this CircularArc2d ca2d) + { + if (ca2d.IsClosed()) { - return ca2d.IsOn(pnt) || ca2d.IsInside(pnt); + return ToCircle(ca2d); } - - /// - /// 将二维解析类圆弧转换为实体圆或者圆弧,然后进行矩阵变换 - /// - /// 二维解析类圆弧对象 - /// 变换矩阵 - /// 实体圆或者圆弧 - public static Curve ToCurve(this CircularArc2d ca2d, Matrix3d mat) + else { - Curve c = ToCurve(ca2d); - c.TransformBy(mat); - return c; + return ToArc(ca2d); } + } + + /// + /// 将二维解析类圆弧转换为实体圆 + /// + /// 二维解析类圆弧对象 + /// 实体圆 + public static Circle ToCircle(this CircularArc2d c2d) + { + return + new Circle( + new Point3d(new Plane(), c2d.Center), + new Vector3d(0, 0, 1), + c2d.Radius); + } + + /// + /// 将二维解析类圆弧转换为实体圆弧 + /// + /// 二维解析类圆弧对象 + /// 圆弧 + public static Arc ToArc(this CircularArc2d a2d) + { + double startangle, endangle; + double refangle = a2d.ReferenceVector.Angle; - /// - /// 将二维解析类圆弧转换为实体圆或者圆弧 - /// - /// 二维解析类圆弧对象 - /// 实体圆或者圆弧 - public static Curve ToCurve(this CircularArc2d ca2d) + if (a2d.IsClockWise) { - if (ca2d.IsClosed()) - { - return ToCircle(ca2d); - } - else - { - return ToArc(ca2d); - } + startangle = -a2d.EndAngle - refangle; + endangle = -a2d.StartAngle - refangle; } - - /// - /// 将二维解析类圆弧转换为实体圆 - /// - /// 二维解析类圆弧对象 - /// 实体圆 - public static Circle ToCircle(this CircularArc2d c2d) + else { - return - new Circle( - new Point3d(new Plane(), c2d.Center), - new Vector3d(0, 0, 1), - c2d.Radius); + startangle = a2d.StartAngle + refangle; + endangle = a2d.EndAngle + refangle; } - /// - /// 将二维解析类圆弧转换为实体圆弧 - /// - /// 二维解析类圆弧对象 - /// 圆弧 - public static Arc ToArc(this CircularArc2d a2d) - { - double startangle, endangle; - double refangle = a2d.ReferenceVector.Angle; + return + new Arc( + new Point3d(new Plane(), a2d.Center), + Vector3d.ZAxis, + a2d.Radius, + startangle, + endangle); + } - if (a2d.IsClockWise) + #endregion CircularArc2d + + #region EllipticalArc2d + + //椭圆弧 + /// + /// 将二维解析类椭圆弧转换为实体椭圆弧,然后进行矩阵变换 + /// + /// 二维解析类椭圆弧对象 + /// 变换矩阵 + /// 实体椭圆弧 + public static Ellipse ToCurve(this EllipticalArc2d ea2d, Matrix3d mat) + { + Ellipse e = ToCurve(ea2d); + e.TransformBy(mat); + return e; + } + + /// + /// 将二维解析类椭圆弧转换为实体椭圆弧 + /// + /// 二维解析类椭圆弧对象 + /// 实体椭圆弧 + public static Ellipse ToCurve(this EllipticalArc2d ea2d) + { + Plane plane = new(); + Ellipse ell = + new( + new Point3d(plane, ea2d.Center), + new Vector3d(0, 0, 1), + new Vector3d(plane, ea2d.MajorAxis) * ea2d.MajorRadius, + ea2d.MinorRadius / ea2d.MajorRadius, + 0, + Math.PI * 2); + if (!ea2d.IsClosed()) + { + if (ea2d.IsClockWise) { - startangle = -a2d.EndAngle - refangle; - endangle = -a2d.StartAngle - refangle; + ell.StartAngle = -ell.GetAngleAtParameter(ea2d.EndAngle); + ell.EndAngle = -ell.GetAngleAtParameter(ea2d.StartAngle); } else { - startangle = a2d.StartAngle + refangle; - endangle = a2d.EndAngle + refangle; + ell.StartAngle = ell.GetAngleAtParameter(ea2d.StartAngle); + ell.EndAngle = ell.GetAngleAtParameter(ea2d.EndAngle); } - - return - new Arc( - new Point3d(new Plane(), a2d.Center), - Vector3d.ZAxis, - a2d.Radius, - startangle, - endangle); } + return ell; + } - #endregion CircularArc2d - - #region EllipticalArc2d + #endregion EllipticalArc2d - //椭圆弧 - /// - /// 将二维解析类椭圆弧转换为实体椭圆弧,然后进行矩阵变换 - /// - /// 二维解析类椭圆弧对象 - /// 变换矩阵 - /// 实体椭圆弧 - public static Ellipse ToCurve(this EllipticalArc2d ea2d, Matrix3d mat) - { - Ellipse e = ToCurve(ea2d); - e.TransformBy(mat); - return e; - } + #region Line2d - /// - /// 将二维解析类椭圆弧转换为实体椭圆弧 - /// - /// 二维解析类椭圆弧对象 - /// 实体椭圆弧 - public static Ellipse ToCurve(this EllipticalArc2d ea2d) - { - Plane plane = new Plane(); - Ellipse ell = - new Ellipse( - new Point3d(plane, ea2d.Center), - new Vector3d(0, 0, 1), - new Vector3d(plane, ea2d.MajorAxis) * ea2d.MajorRadius, - ea2d.MinorRadius / ea2d.MajorRadius, - 0, - Math.PI * 2); - if (!ea2d.IsClosed()) + /// + /// 将二维解析类直线转换为实体类构造线 + /// + /// 二维解析类直线 + /// 实体类构造线 + public static Xline ToCurve(this Line2d line2d) + { + Plane plane = new(); + return + new Xline { - if (ea2d.IsClockWise) - { - ell.StartAngle = -ell.GetAngleAtParameter(ea2d.EndAngle); - ell.EndAngle = -ell.GetAngleAtParameter(ea2d.StartAngle); - } - else - { - ell.StartAngle = ell.GetAngleAtParameter(ea2d.StartAngle); - ell.EndAngle = ell.GetAngleAtParameter(ea2d.EndAngle); - } - } - return ell; - } - - #endregion EllipticalArc2d - - #region Line2d + BasePoint = new Point3d(plane, line2d.PointOnLine), + SecondPoint = new Point3d(plane, line2d.PointOnLine + line2d.Direction) + }; + } - /// - /// 将二维解析类直线转换为实体类构造线 - /// - /// 二维解析类直线 - /// 实体类构造线 - public static Xline ToCurve(this Line2d line2d) - { - var plane = new Plane(); - return - new Xline - { - BasePoint = new Point3d(plane, line2d.PointOnLine), - SecondPoint = new Point3d(plane, line2d.PointOnLine + line2d.Direction) - }; - } + /// + /// 将二维解析类直线转换为实体类构造线,然后进行矩阵变换 + /// + /// 二维解析类直线 + /// 变换矩阵 + /// 实体类构造线 + public static Xline ToCurve(this Line2d line2d, Matrix3d mat) + { + Xline xl = ToCurve(line2d); + xl.TransformBy(mat); + return xl; + } - /// - /// 将二维解析类直线转换为实体类构造线,然后进行矩阵变换 - /// - /// 二维解析类直线 - /// 变换矩阵 - /// 实体类构造线 - public static Xline ToCurve(this Line2d line2d, Matrix3d mat) - { - Xline xl = ToCurve(line2d); - xl.TransformBy(mat); - return xl; - } + /// + /// 将二维解析类构造线转换为二维解析类线段 + /// + /// 二维解析类构造线 + /// 起点参数 + /// 终点参数 + /// 二维解析类线段 + public static LineSegment2d ToLineSegment2d(this Line2d line2d, double fromParameter, double toParameter) + { + return + new LineSegment2d + ( + line2d.EvaluatePoint(fromParameter), + line2d.EvaluatePoint(toParameter) + ); + } - /// - /// 将二维解析类构造线转换为二维解析类线段 - /// - /// 二维解析类构造线 - /// 起点参数 - /// 终点参数 - /// 二维解析类线段 - public static LineSegment2d ToLineSegment2d(this Line2d line2d, double fromParameter, double toParameter) - { - return - new LineSegment2d - ( - line2d.EvaluatePoint(fromParameter), - line2d.EvaluatePoint(toParameter) - ); - } + #endregion Line2d - #endregion Line2d + #region LineSegment2d - #region LineSegment2d + /// + /// 将二维解析类线段转换为实体类直线,并进行矩阵变换 + /// + /// 二维解析类线段 + /// 变换矩阵 + /// 实体类直线 + public static Line ToCurve(this LineSegment2d ls2d, Matrix3d mat) + { + Line l = ToCurve(ls2d); + l.TransformBy(mat); + return l; + } - /// - /// 将二维解析类线段转换为实体类直线,并进行矩阵变换 - /// - /// 二维解析类线段 - /// 变换矩阵 - /// 实体类直线 - public static Line ToCurve(this LineSegment2d ls2d, Matrix3d mat) - { - Line l = ToCurve(ls2d); - l.TransformBy(mat); - return l; - } + /// + /// 将二维解析类线段转换为实体类直线 + /// + /// 二维解析类线段 + /// 实体类直线 + public static Line ToCurve(this LineSegment2d ls2d) + { + Plane plane = new(); + return + new Line( + new Point3d(plane, ls2d.StartPoint), + new Point3d(plane, ls2d.EndPoint)); - /// - /// 将二维解析类线段转换为实体类直线 - /// - /// 二维解析类线段 - /// 实体类直线 - public static Line ToCurve(this LineSegment2d ls2d) - { - Plane plane = new Plane(); - return - new Line( - new Point3d(plane, ls2d.StartPoint), - new Point3d(plane, ls2d.EndPoint)); + } - } + #endregion LineSegment2d - #endregion LineSegment2d + #region NurbCurve2d - #region NurbCurve2d + /// + /// 将二维解析类BURB曲线转换为实体类样条曲线,并进行矩阵变换 + /// + /// 二维解析类BURB曲线 + /// 变换矩阵 + /// 实体类样条曲线 + public static Spline ToCurve(this NurbCurve2d nc2d, Matrix3d mat) + { + Spline spl = ToCurve(nc2d); + spl.TransformBy(mat); + return spl; + } - /// - /// 将二维解析类BURB曲线转换为实体类样条曲线,并进行矩阵变换 - /// - /// 二维解析类BURB曲线 - /// 变换矩阵 - /// 实体类样条曲线 - public static Spline ToCurve(this NurbCurve2d nc2d, Matrix3d mat) + /// + /// 将二维解析类BURB曲线转换为实体类样条曲线 + /// + /// 二维解析类BURB曲线 + /// 实体类样条曲线 + public static Spline ToCurve(this NurbCurve2d nc2d) + { + int i; + Plane plane = new(); + Point3dCollection ctlpnts = new(); + for (i = 0; i < nc2d.NumControlPoints; i++) { - Spline spl = ToCurve(nc2d); - spl.TransformBy(mat); - return spl; + ctlpnts.Add(new Point3d(plane, nc2d.GetControlPointAt(i))); } - /// - /// 将二维解析类BURB曲线转换为实体类样条曲线 - /// - /// 二维解析类BURB曲线 - /// 实体类样条曲线 - public static Spline ToCurve(this NurbCurve2d nc2d) + DoubleCollection knots = new(); + foreach (double knot in nc2d.Knots) { - int i; - Plane plane = new Plane(); - Point3dCollection ctlpnts = new Point3dCollection(); - for (i = 0; i < nc2d.NumControlPoints; i++) - { - ctlpnts.Add(new Point3d(plane, nc2d.GetControlPointAt(i))); - } - - DoubleCollection knots = new DoubleCollection(); - foreach (double knot in nc2d.Knots) - { - knots.Add(knot); - } - - DoubleCollection weights = new DoubleCollection(); - for (i = 0; i < nc2d.NumWeights; i++) - { - weights.Add(nc2d.GetWeightAt(i)); - } + knots.Add(knot); + } - NurbCurve2dData ncdata = nc2d.DefinitionData; - - return - new Spline( - ncdata.Degree, - ncdata.Rational, - nc2d.IsClosed(), - ncdata.Periodic, - ctlpnts, - knots, - weights, - 0, - nc2d.Knots.Tolerance); + DoubleCollection weights = new(); + for (i = 0; i < nc2d.NumWeights; i++) + { + weights.Add(nc2d.GetWeightAt(i)); } - #endregion NurbCurve2d + NurbCurve2dData ncdata = nc2d.DefinitionData; + + return + new Spline( + ncdata.Degree, + ncdata.Rational, + nc2d.IsClosed(), + ncdata.Periodic, + ctlpnts, + knots, + weights, + 0, + nc2d.Knots.Tolerance); } + + #endregion NurbCurve2d } diff --git a/src/IFoxCAD.Cad/ExtensionMethod/Curve3dEx.cs b/src/IFoxCAD.Cad/ExtensionMethod/Curve3dEx.cs index 9d477ba..57ca8fd 100644 --- a/src/IFoxCAD.Cad/ExtensionMethod/Curve3dEx.cs +++ b/src/IFoxCAD.Cad/ExtensionMethod/Curve3dEx.cs @@ -1,567 +1,557 @@ -using Autodesk.AutoCAD.DatabaseServices; -using Autodesk.AutoCAD.Geometry; -using System; -using System.Collections.Generic; -using System.Linq; +namespace IFoxCAD.Cad; -namespace IFoxCAD.Cad +/// +/// 三维解析类曲线转换为三维实体曲线扩展类 +/// +public static class Curve3dEx { /// - /// 三维解析类曲线转换为三维实体曲线扩展类 + /// 判断两个浮点数是否相等 /// - public static class Curve3dEx + /// 容差 + /// 第一个数 + /// 第二个数 + /// 两个数的差值的绝对值小于容差返回 ,反之返回 + public static bool IsEqualPoint(this Tolerance tol, double d1, double d2) { - /// - /// 判断两个浮点数是否相等 - /// - /// 容差 - /// 第一个数 - /// 第二个数 - /// 两个数的差值的绝对值小于容差返回 ,反之返回 - public static bool IsEqualPoint(this Tolerance tol, double d1, double d2) - { - return Math.Abs(d1 - d2) < tol.EqualPoint; - } + return Math.Abs(d1 - d2) < tol.EqualPoint; + } - #region Curve3d + #region Curve3d - /// - /// 获取三维解析类曲线(自交曲线)的交点参数 - /// - /// 三维解析类曲线 - /// 曲线参数的列表 - public static List GetParamsAtIntersectionPoints(this Curve3d c3d) - { - CurveCurveIntersector3d cci = new CurveCurveIntersector3d(c3d, c3d, Vector3d.ZAxis); - List pars = new List(); - for (int i = 0; i < cci.NumberOfIntersectionPoints; i++) - { - pars.AddRange(cci.GetIntersectionParameters(i)); - } - pars.Sort(); - return pars; - } + /// + /// 获取三维解析类曲线(自交曲线)的交点参数 + /// + /// 三维解析类曲线 + /// 曲线参数的列表 + public static List GetParamsAtIntersectionPoints(this Curve3d c3d) + { + CurveCurveIntersector3d cci = new(c3d, c3d, Vector3d.ZAxis); + List pars = new(); + for (int i = 0; i < cci.NumberOfIntersectionPoints; i++) + pars.AddRange(cci.GetIntersectionParameters(i)); - /// - /// 获取三维解析类子曲线 - /// - /// 三维解析类曲线 - /// 子段曲线起点参数 - /// 子段曲线终点参数 - /// 三维解析类曲线 - public static Curve3d GetSubCurve(this Curve3d curve, double from, double to) + pars.Sort(); + return pars; + } + + /// + /// 获取三维解析类子曲线 + /// + /// 三维解析类曲线 + /// 子段曲线起点参数 + /// 子段曲线终点参数 + /// 三维解析类曲线 + public static Curve3d GetSubCurve(this Curve3d curve, double from, double to) + { + Interval inter = curve.GetInterval(); + bool atStart = Tolerance.Global.IsEqualPoint(inter.LowerBound, from); + bool atEnd = Tolerance.Global.IsEqualPoint(inter.UpperBound, to); + if (atStart && atEnd) + return (Curve3d)curve.Clone(); + if (curve is NurbCurve3d) { - Interval inter = curve.GetInterval(); - bool atStart = Tolerance.Global.IsEqualPoint(inter.LowerBound, from); - bool atEnd = Tolerance.Global.IsEqualPoint(inter.UpperBound, to); - if (atStart && atEnd) - return (Curve3d)curve.Clone(); - if (curve is NurbCurve3d) + if (from < to) { - if (from < to) + NurbCurve3d clone = (NurbCurve3d)curve.Clone(); + if (atStart || atEnd) { - NurbCurve3d clone = (NurbCurve3d)curve.Clone(); - if (atStart || atEnd) - { - clone.HardTrimByParams(from, to); - return clone; - } - else - { - clone.HardTrimByParams(inter.LowerBound, to); - clone.HardTrimByParams(from, to); - return clone; - } + clone.HardTrimByParams(from, to); + return clone; } else { - NurbCurve3d clone1 = (NurbCurve3d)curve.Clone(); - clone1.HardTrimByParams(from, inter.UpperBound); - NurbCurve3d clone2 = (NurbCurve3d)curve.Clone(); - clone2.HardTrimByParams(inter.LowerBound, to); - clone1.JoinWith(clone2); - return clone1; + clone.HardTrimByParams(inter.LowerBound, to); + clone.HardTrimByParams(from, to); + return clone; } } else { - Curve3d clone = (Curve3d)curve.Clone(); - clone.SetInterval(new Interval(from, to, Tolerance.Global.EqualPoint)); - return clone; + NurbCurve3d clone1 = (NurbCurve3d)curve.Clone(); + clone1.HardTrimByParams(from, inter.UpperBound); + NurbCurve3d clone2 = (NurbCurve3d)curve.Clone(); + clone2.HardTrimByParams(inter.LowerBound, to); + clone1.JoinWith(clone2); + return clone1; } } - - /// - /// 将三维解析类曲线转换为三维实体类曲线 - /// - /// 三维解析类曲线 - /// 三维实体类曲线 - public static Curve ToCurve(this Curve3d curve) + else { - return curve switch - { - CompositeCurve3d co => ToCurve(co), - LineSegment3d li => ToCurve(li), - EllipticalArc3d el => ToCurve(el), - CircularArc3d ci => ToCurve(ci), - NurbCurve3d nu => ToCurve(nu), - PolylineCurve3d pl => ToCurve(pl), - Line3d l3 => ToCurve(l3), - _ => null - }; + Curve3d clone = (Curve3d)curve.Clone(); + clone.SetInterval(new Interval(from, to, Tolerance.Global.EqualPoint)); + return clone; } + } - /// - /// 将三维解析类曲线转换为三维解析类Nurb曲线 - /// - /// 三维解析类曲线 - /// 三维解析类Nurb曲线 - public static NurbCurve3d ToNurbCurve3d(this Curve3d curve) + /// + /// 将三维解析类曲线转换为三维实体类曲线 + /// + /// 三维解析类曲线 + /// 三维实体类曲线 + public static Curve? ToCurve(this Curve3d curve) + { + return curve switch { - return curve switch - { - LineSegment3d line => new NurbCurve3d(line), - EllipticalArc3d el => new NurbCurve3d(el), - CircularArc3d cir => new NurbCurve3d(ToEllipticalArc3d(cir)), - NurbCurve3d nur => nur, - PolylineCurve3d pl => new NurbCurve3d(3, pl, false), - _ => null - }; - } + CompositeCurve3d co => ToCurve(co), + LineSegment3d li => ToCurve(li), + EllipticalArc3d el => ToCurve(el), + CircularArc3d ci => ToCurve(ci), + NurbCurve3d nu => ToCurve(nu), + PolylineCurve3d pl => ToCurve(pl), + Line3d l3 => ToCurve(l3), + _ => null + }; + } + + /// + /// 将三维解析类曲线转换为三维解析类Nurb曲线 + /// + /// 三维解析类曲线 + /// 三维解析类Nurb曲线 + public static NurbCurve3d? ToNurbCurve3d(this Curve3d curve) + { + return curve switch + { + LineSegment3d line => new NurbCurve3d(line), + EllipticalArc3d el => new NurbCurve3d(el), + CircularArc3d cir => new NurbCurve3d(ToEllipticalArc3d(cir)), + NurbCurve3d nur => nur, + PolylineCurve3d pl => new NurbCurve3d(3, pl, false), + _ => null + }; + } - #endregion Curve3d + #endregion Curve3d - #region CompositeCurve3d + #region CompositeCurve3d - /// - /// 判断是否为圆和椭圆 - /// - /// 三维解析类曲线 - /// 完整圆及完整的椭圆返回 ,反之返回 - public static bool IsCircular(this Curve3d curve) + /// + /// 判断是否为圆和椭圆 + /// + /// 三维解析类曲线 + /// 完整圆及完整的椭圆返回 ,反之返回 + public static bool IsCircular(this Curve3d curve) + { + return curve switch { - return curve switch - { - CircularArc3d or EllipticalArc3d => curve.IsClosed(), - _ => false - }; - } + CircularArc3d or EllipticalArc3d => curve.IsClosed(), + _ => false + }; + } - /// - /// 将三维复合曲线按曲线参数分割 - /// - /// 三维复合曲线 - /// 曲线参数列表 - /// 三维复合曲线列表 - public static List GetSplitCurves(this CompositeCurve3d c3d, List pars) + /// + /// 将三维复合曲线按曲线参数分割 + /// + /// 三维复合曲线 + /// 曲线参数列表 + /// 三维复合曲线列表 + public static List GetSplitCurves(this CompositeCurve3d c3d, List pars) + { + //曲线参数剔除重复的 + pars.Sort(); + for (int i = pars.Count - 1; i > 0; i--) { - Interval inter = c3d.GetInterval(); - Curve3d[] c3ds = c3d.GetCurves(); - pars.Sort(); - for (int i = pars.Count - 1; i > 0; i--) - { - if (Tolerance.Global.IsEqualPoint(pars[i], pars[i - 1])) - pars.RemoveAt(i); - } + if (Tolerance.Global.IsEqualPoint(pars[i], pars[i - 1])) + pars.RemoveAt(i); + } - if (pars.Count == 0) - return new List(); - if (c3ds.Length == 1 && c3ds[0].IsClosed()) + if (pars.Count == 0) + return new List(); + + //这个是曲线参数类 + var inter = c3d.GetInterval(); + //曲线们 + var c3ds = c3d.GetCurves(); + if (c3ds.Length == 1 && c3ds[0].IsClosed()) + { + //闭合曲线不允许打断于一点 + if (pars.Count > 1) { - //闭合曲线不允许打断于一点 - if (pars.Count > 1) + //如果包含起点 + if (Tolerance.Global.IsEqualPoint(pars[0], inter.LowerBound)) { - //如果包含起点 - if (Tolerance.Global.IsEqualPoint(pars[0], inter.LowerBound)) - { - pars[0] = inter.LowerBound; - //又包含终点,去除终点 - if (Tolerance.Global.IsEqualPoint(pars[pars.Count - 1], inter.UpperBound)) - { - pars.RemoveAt(pars.Count - 1); - if (pars.Count == 1) - return new List(); - } - } - else if (Tolerance.Global.IsEqualPoint(pars[pars.Count - 1], inter.UpperBound)) + pars[0] = inter.LowerBound; + //又包含终点,去除终点 + if (Tolerance.Global.IsEqualPoint(pars[^1], inter.UpperBound)) { - pars[pars.Count - 1] = inter.UpperBound; + pars.RemoveAt(pars.Count - 1); + if (pars.Count == 1) + return new List(); } - //加入第一点以支持反向打断 - pars.Add(pars[0]); } - else + else if (Tolerance.Global.IsEqualPoint(pars[^1], inter.UpperBound)) { - return new List(); + pars[^1] = inter.UpperBound; } + //加入第一点以支持反向打断 + pars.Add(pars[0]); } else { - //非闭合曲线加入起点和终点 - if (Tolerance.Global.IsEqualPoint(pars[0], inter.LowerBound)) - pars[0] = inter.LowerBound; - else - pars.Insert(0, inter.LowerBound); - if (Tolerance.Global.IsEqualPoint(pars[pars.Count - 1], inter.UpperBound)) - pars[pars.Count - 1] = inter.UpperBound; - else - pars.Add(inter.UpperBound); - } - - List curves = new List(); - for (int i = 0; i < pars.Count - 1; i++) - { - List cc3ds = new List(); - //复合曲线参数转换到包含曲线参数 - CompositeParameter cp1 = c3d.GlobalToLocalParameter(pars[i]); - CompositeParameter cp2 = c3d.GlobalToLocalParameter(pars[i + 1]); - if (cp1.SegmentIndex == cp2.SegmentIndex) - { - cc3ds.Add( - c3ds[cp1.SegmentIndex].GetSubCurve( - cp1.LocalParameter, - cp2.LocalParameter)); - } - else - { - inter = c3ds[cp1.SegmentIndex].GetInterval(); - cc3ds.Add( - c3ds[cp1.SegmentIndex].GetSubCurve( - cp1.LocalParameter, - inter.UpperBound)); - for (int j = cp1.SegmentIndex + 1; j < cp2.SegmentIndex; j++) - { - cc3ds.Add((Curve3d)c3ds[j].Clone()); - } - inter = c3ds[cp2.SegmentIndex].GetInterval(); - cc3ds.Add( - c3ds[cp2.SegmentIndex].GetSubCurve( - inter.LowerBound, - cp2.LocalParameter)); - } - curves.Add(new CompositeCurve3d(cc3ds.ToArray())); - } - - if (c3d.IsClosed() && c3ds.Length > 1) - { - var cs = curves[curves.Count - 1].GetCurves().ToList(); - cs.AddRange(curves[0].GetCurves()); - curves[curves.Count - 1] = new CompositeCurve3d(cs.ToArray()); - curves.RemoveAt(0); + return new List(); } - - return curves; + } + else + { + //非闭合曲线加入起点和终点 + if (Tolerance.Global.IsEqualPoint(pars[0], inter.LowerBound)) + pars[0] = inter.LowerBound; + else + pars.Insert(0, inter.LowerBound); + if (Tolerance.Global.IsEqualPoint(pars[^1], inter.UpperBound)) + pars[^1] = inter.UpperBound; + else + pars.Add(inter.UpperBound); } - /// - /// 将复合曲线转换为实体类曲线 - /// - /// 三维复合曲线 - /// 实体曲线 - public static Curve ToCurve(this CompositeCurve3d curve) + List curves = new(); + for (int i = 0; i < pars.Count - 1; i++) { - Curve3d[] cs = curve.GetCurves(); - if (cs.Length == 0) + List cc3ds = new(); + //复合曲线参数转换到包含曲线参数 + var cp1 = c3d.GlobalToLocalParameter(pars[i]); + var cp2 = c3d.GlobalToLocalParameter(pars[i + 1]); + if (cp1.SegmentIndex == cp2.SegmentIndex) { - return null; - } - else if (cs.Length == 1) - { - return ToCurve(cs[0]); + cc3ds.Add( + c3ds[cp1.SegmentIndex].GetSubCurve( + cp1.LocalParameter, + cp2.LocalParameter)); } else { - bool hasNurb = false; - - foreach (var c in cs) - { - if (c is NurbCurve3d || c is EllipticalArc3d) - { - hasNurb = true; - break; - } - } - if (hasNurb) - { - NurbCurve3d nc3d = cs[0].ToNurbCurve3d(); - for (int i = 1; i < cs.Length; i++) - nc3d.JoinWith(cs[i].ToNurbCurve3d()); - return nc3d.ToCurve(); - } - else + inter = c3ds[cp1.SegmentIndex].GetInterval(); + cc3ds.Add( + c3ds[cp1.SegmentIndex].GetSubCurve( + cp1.LocalParameter, + inter.UpperBound)); + for (int j = cp1.SegmentIndex + 1; j < cp2.SegmentIndex; j++) { - return ToPolyline(curve); + cc3ds.Add((Curve3d)c3ds[j].Clone()); } + inter = c3ds[cp2.SegmentIndex].GetInterval(); + cc3ds.Add( + c3ds[cp2.SegmentIndex].GetSubCurve( + inter.LowerBound, + cp2.LocalParameter)); } + curves.Add(new CompositeCurve3d(cc3ds.ToArray())); } - /// - /// 将三维复合曲线转换为实体类多段线 - /// - /// 三维复合曲线 - /// 实体类多段线 - public static Polyline ToPolyline(this CompositeCurve3d cc3d) + if (c3d.IsClosed() && c3ds.Length > 1) { - Polyline pl = new Polyline(); - pl.Elevation = cc3d.StartPoint[2]; - Plane plane = pl.GetPlane(); - Point2d endver = Point2d.Origin; - int i = 0; - foreach (Curve3d c3d in cc3d.GetCurves()) - { - if (c3d is CircularArc3d) - { - CircularArc3d ca3d = (CircularArc3d)c3d; - double b = Math.Tan(0.25 * (ca3d.EndAngle - ca3d.StartAngle)) * ca3d.Normal[2]; - pl.AddVertexAt(i, c3d.StartPoint.Convert2d(plane), b, 0, 0); - endver = c3d.EndPoint.Convert2d(plane); - } - else - { - pl.AddVertexAt(i, c3d.StartPoint.Convert2d(plane), 0, 0, 0); - endver = c3d.EndPoint.Convert2d(plane); - } - i++; - } - pl.AddVertexAt(i, endver, 0, 0, 0); - return pl; + var cus1 = curves[curves.Count - 1].GetCurves(); + var cus2 = curves[0].GetCurves(); + var cs = cus1.Combine2(cus2); + curves[^1] = new CompositeCurve3d(cs); + curves.RemoveAt(0); } + return curves; + } - #endregion CompositeCurve3d - - #region Line3d + /// + /// 将复合曲线转换为实体类曲线 + /// + /// 三维复合曲线 + /// 实体曲线 + public static Curve? ToCurve(this CompositeCurve3d curve) + { + Curve3d[] cs = curve.GetCurves(); + if (cs.Length == 0) + return null; + if (cs.Length == 1) + return ToCurve(cs[0]); - /// - /// 将解析类三维构造线转换为实体类构造线 - /// - /// 解析类三维构造线 - /// 实体类构造线 - public static Xline ToCurve(this Line3d line3d) - { - return - new Xline - { - BasePoint = line3d.PointOnLine, - SecondPoint = line3d.PointOnLine + line3d.Direction - }; - } + bool hasNurb = false; - /// - /// 将三维解析类构造线转换为三维解析类线段 - /// - /// 三维解析类构造线 - /// 起点参数 - /// 终点参数 - /// 三维解析类线段 - public static LineSegment3d ToLineSegment3d(this Line3d line3d, double fromParameter, double toParameter) + for (int i = 0; i < cs.Length; i++) { - return - new LineSegment3d - ( - line3d.EvaluatePoint(fromParameter), - line3d.EvaluatePoint(toParameter) - ); + var c = cs[i]; + if (c is NurbCurve3d || c is EllipticalArc3d) + { + hasNurb = true; + break; + } } - - #endregion Line3d - - #region LineSegment3d - - /// - /// 将三维解析类线段转换为实体类直线 - /// - /// 三维解析类线段 - /// 实体类直线 - public static Line ToCurve(this LineSegment3d lineSeg3d) + if (hasNurb) { - return new Line(lineSeg3d.StartPoint, lineSeg3d.EndPoint); + var nc3d = cs[0].ToNurbCurve3d(); + for (int i = 1; i < cs.Length; i++) + nc3d?.JoinWith(cs[i].ToNurbCurve3d()); + return nc3d?.ToCurve(); } - #endregion LineSegment3d - - #region CircularArc3d + return ToPolyline(curve); + } - /// - /// 将三维解析类圆/弧转换为实体圆/弧 - /// - /// 三维解析类圆/弧 - /// 实体圆/弧 - public static Curve ToCurve(this CircularArc3d ca3d) + /// + /// 将三维复合曲线转换为实体类多段线 + /// + /// 三维复合曲线 + /// 实体类多段线 + public static Polyline ToPolyline(this CompositeCurve3d cc3d) + { + Polyline pl = new(); + pl.SetDatabaseDefaults(); + pl.Elevation = cc3d.StartPoint[2]; + + Plane plane = pl.GetPlane(); + Point2d endver = Point2d.Origin; + int i = 0; + foreach (Curve3d c3d in cc3d.GetCurves()) { - if (ca3d.IsClosed()) + if (c3d is CircularArc3d ca3d) { - return ToCircle(ca3d); + double b = Math.Tan(0.25 * (ca3d.EndAngle - ca3d.StartAngle)) * ca3d.Normal[2]; + pl.AddVertexAt(i, c3d.StartPoint.Convert2d(plane), b, 0, 0); + endver = c3d.EndPoint.Convert2d(plane); } else { - return ToArc(ca3d); + pl.AddVertexAt(i, c3d.StartPoint.Convert2d(plane), 0, 0, 0); + endver = c3d.EndPoint.Convert2d(plane); } + i++; } + pl.AddVertexAt(i, endver, 0, 0, 0); + return pl; + } - /// - /// 将三维解析类圆/弧转换为实体圆 - /// - /// 三维解析类圆/弧 - /// 实体圆 - public static Circle ToCircle(this CircularArc3d ca3d) => - new Circle(ca3d.Center, ca3d.Normal, ca3d.Radius); - - /// - /// 将三维解析类圆/弧转换为实体圆弧 - /// - /// 三维解析类圆/弧 - /// 实体圆弧 - public static Arc ToArc(this CircularArc3d ca3d) - { - //必须新建,而不能直接使用GetPlane()获取 - double angle = ca3d.ReferenceVector.AngleOnPlane(new Plane(ca3d.Center, ca3d.Normal)); - return new Arc(ca3d.Center, ca3d.Normal, ca3d.Radius, ca3d.StartAngle + angle, ca3d.EndAngle + angle); - } + #endregion CompositeCurve3d + + #region Line3d - /// - /// 将三维解析类圆/弧转换为三维解析类椭圆弧 - /// - /// 三维解析类圆/弧 - /// 三维解析类椭圆弧 - public static EllipticalArc3d ToEllipticalArc3d(this CircularArc3d ca3d) + /// + /// 将解析类三维构造线转换为实体类构造线 + /// + /// 解析类三维构造线 + /// 实体类构造线 + public static Xline ToCurve(this Line3d line3d) + { + return + new Xline + { + BasePoint = line3d.PointOnLine, + SecondPoint = line3d.PointOnLine + line3d.Direction + }; + } + + /// + /// 将三维解析类构造线转换为三维解析类线段 + /// + /// 三维解析类构造线 + /// 起点参数 + /// 终点参数 + /// 三维解析类线段 + public static LineSegment3d ToLineSegment3d(this Line3d line3d, double fromParameter, double toParameter) + { + return + new LineSegment3d + ( + line3d.EvaluatePoint(fromParameter), + line3d.EvaluatePoint(toParameter) + ); + } + + #endregion Line3d + + #region LineSegment3d + + /// + /// 将三维解析类线段转换为实体类直线 + /// + /// 三维解析类线段 + /// 实体类直线 + public static Line ToCurve(this LineSegment3d lineSeg3d) + { + return new Line(lineSeg3d.StartPoint, lineSeg3d.EndPoint); + } + + #endregion LineSegment3d + + #region CircularArc3d + + /// + /// 将三维解析类圆/弧转换为实体圆/弧 + /// + /// 三维解析类圆/弧 + /// 实体圆/弧 + public static Curve ToCurve(this CircularArc3d ca3d) + { + if (ca3d.IsClosed()) { - Vector3d zaxis = ca3d.Normal; - Vector3d xaxis = ca3d.ReferenceVector; - Vector3d yaxis = zaxis.CrossProduct(xaxis); - - return - new EllipticalArc3d( - ca3d.Center, - xaxis, - yaxis, - ca3d.Radius, - ca3d.Radius, - ca3d.StartAngle, - ca3d.EndAngle); + return ToCircle(ca3d); } - - /// - /// 将三维解析类圆/弧转换为三维解析类Nurb曲线 - /// - /// 三维解析类圆/弧 - /// 三维解析类Nurb曲线 - public static NurbCurve3d ToNurbCurve3d(this CircularArc3d ca3d) + else { - EllipticalArc3d ea3d = ToEllipticalArc3d(ca3d); - NurbCurve3d nc3d = new NurbCurve3d(ea3d); - return nc3d; + return ToArc(ca3d); } + } + + /// + /// 将三维解析类圆/弧转换为实体圆 + /// + /// 三维解析类圆/弧 + /// 实体圆 + public static Circle ToCircle(this CircularArc3d ca3d) => + new(ca3d.Center, ca3d.Normal, ca3d.Radius); - #endregion CircularArc3d + /// + /// 将三维解析类圆/弧转换为实体圆弧 + /// + /// 三维解析类圆/弧 + /// 实体圆弧 + public static Arc ToArc(this CircularArc3d ca3d) + { + //必须新建,而不能直接使用GetPlane()获取 + double angle = ca3d.ReferenceVector.AngleOnPlane(new Plane(ca3d.Center, ca3d.Normal)); + return new Arc(ca3d.Center, ca3d.Normal, ca3d.Radius, ca3d.StartAngle + angle, ca3d.EndAngle + angle); + } - #region EllipticalArc3d + /// + /// 将三维解析类圆/弧转换为三维解析类椭圆弧 + /// + /// 三维解析类圆/弧 + /// 三维解析类椭圆弧 + public static EllipticalArc3d ToEllipticalArc3d(this CircularArc3d ca3d) + { + Vector3d zaxis = ca3d.Normal; + Vector3d xaxis = ca3d.ReferenceVector; + Vector3d yaxis = zaxis.CrossProduct(xaxis); + + return + new EllipticalArc3d( + ca3d.Center, + xaxis, + yaxis, + ca3d.Radius, + ca3d.Radius, + ca3d.StartAngle, + ca3d.EndAngle); + } - /// - /// 将三维解析类椭圆弧转换为实体类椭圆弧 - /// - /// 三维解析类椭圆弧 - /// 实体类椭圆弧 - public static Ellipse ToCurve(this EllipticalArc3d ea3d) + /// + /// 将三维解析类圆/弧转换为三维解析类Nurb曲线 + /// + /// 三维解析类圆/弧 + /// 三维解析类Nurb曲线 + public static NurbCurve3d ToNurbCurve3d(this CircularArc3d ca3d) + { + EllipticalArc3d ea3d = ToEllipticalArc3d(ca3d); + NurbCurve3d nc3d = new(ea3d); + return nc3d; + } + + #endregion CircularArc3d + + #region EllipticalArc3d + + /// + /// 将三维解析类椭圆弧转换为实体类椭圆弧 + /// + /// 三维解析类椭圆弧 + /// 实体类椭圆弧 + public static Ellipse ToCurve(this EllipticalArc3d ea3d) + { + Ellipse ell = + new( + ea3d.Center, + ea3d.Normal, + ea3d.MajorAxis * ea3d.MajorRadius, + ea3d.MinorRadius / ea3d.MajorRadius, + 0, + Math.PI * 2); + //Ge椭圆角度就是Db椭圆的参数 + if (!ea3d.IsClosed()) { - Ellipse ell = - new Ellipse( - ea3d.Center, - ea3d.Normal, - ea3d.MajorAxis * ea3d.MajorRadius, - ea3d.MinorRadius / ea3d.MajorRadius, - 0, - Math.PI * 2); - //Ge椭圆角度就是Db椭圆的参数 - if (!ea3d.IsClosed()) - { - ell.StartAngle = ell.GetAngleAtParameter(ea3d.StartAngle); - ell.EndAngle = ell.GetAngleAtParameter(ea3d.EndAngle); - } - return ell; + ell.StartAngle = ell.GetAngleAtParameter(ea3d.StartAngle); + ell.EndAngle = ell.GetAngleAtParameter(ea3d.EndAngle); } + return ell; + } - #endregion EllipticalArc3d + #endregion EllipticalArc3d - #region NurbCurve3d + #region NurbCurve3d - /// - /// 将三维解析类Nurb曲线转换为实体类样条曲线 - /// - /// 三维解析类Nurb曲线 - /// 实体类样条曲线 - public static Spline ToCurve(this NurbCurve3d nc3d) + /// + /// 将三维解析类Nurb曲线转换为实体类样条曲线 + /// + /// 三维解析类Nurb曲线 + /// 实体类样条曲线 + public static Spline ToCurve(this NurbCurve3d nc3d) + { + Spline spl; + if (nc3d.HasFitData) { - Spline spl = null; - if (nc3d.HasFitData) + NurbCurve3dFitData fdata = nc3d.FitData; + if (fdata.TangentsExist) { - NurbCurve3dFitData fdata = nc3d.FitData; - if (fdata.TangentsExist) - { - spl = new Spline( - fdata.FitPoints, - fdata.StartTangent, - fdata.EndTangent, - nc3d.Order, - fdata.FitTolerance.EqualPoint); - } - else - { - spl = new Spline( - fdata.FitPoints, - nc3d.Order, - fdata.FitTolerance.EqualPoint); - } + spl = new Spline( + fdata.FitPoints, + fdata.StartTangent, + fdata.EndTangent, + nc3d.Order, + fdata.FitTolerance.EqualPoint); } else { - DoubleCollection knots = new DoubleCollection(); - foreach (double knot in nc3d.Knots) - knots.Add(knot); - - NurbCurve3dData ncdata = nc3d.DefinitionData; - spl = new Spline( - ncdata.Degree, - ncdata.Rational, - nc3d.IsClosed(), - ncdata.Periodic, - ncdata.ControlPoints, - knots, - ncdata.Weights, - Tolerance.Global.EqualPoint, - ncdata.Knots.Tolerance); + fdata.FitPoints, + nc3d.Order, + fdata.FitTolerance.EqualPoint); } - return spl; } + else + { + DoubleCollection knots = new(); + foreach (double knot in nc3d.Knots) + knots.Add(knot); + + NurbCurve3dData ncdata = nc3d.DefinitionData; + + spl = new Spline( + ncdata.Degree, + ncdata.Rational, + nc3d.IsClosed(), + ncdata.Periodic, + ncdata.ControlPoints, + knots, + ncdata.Weights, + Tolerance.Global.EqualPoint, + ncdata.Knots.Tolerance); + } + return spl; + } - #endregion NurbCurve3d - - #region PolylineCurve3d + #endregion NurbCurve3d - /// - /// 将三维解析类多段线转换为实体类三维多段线 - /// - /// 三维解析类多段线 - /// 实体类三维多段线 - public static Polyline3d ToCurve(this PolylineCurve3d pl3d) - { - Point3dCollection pnts = new Point3dCollection(); + #region PolylineCurve3d - for (int i = 0; i < pl3d.NumberOfControlPoints; i++) - { - pnts.Add(pl3d.ControlPointAt(i)); - } + /// + /// 将三维解析类多段线转换为实体类三维多段线 + /// + /// 三维解析类多段线 + /// 实体类三维多段线 + public static Polyline3d ToCurve(this PolylineCurve3d pl3d) + { + Point3dCollection pnts = new(); - bool closed = false; - int n = pnts.Count - 1; - if (pnts[0] == pnts[n]) - { - pnts.RemoveAt(n); - closed = true; - } - return new Polyline3d(Poly3dType.SimplePoly, pnts, closed); + for (int i = 0; i < pl3d.NumberOfControlPoints; i++) + { + pnts.Add(pl3d.ControlPointAt(i)); } - #endregion PolylineCurve3d + bool closed = false; + int n = pnts.Count - 1; + if (pnts[0] == pnts[n]) + { + pnts.RemoveAt(n); + closed = true; + } + return new Polyline3d(Poly3dType.SimplePoly, pnts, closed); } + + #endregion PolylineCurve3d } diff --git a/src/IFoxCAD.Cad/ExtensionMethod/CurveEx.cs b/src/IFoxCAD.Cad/ExtensionMethod/CurveEx.cs index 26fbd18..4056bc5 100644 --- a/src/IFoxCAD.Cad/ExtensionMethod/CurveEx.cs +++ b/src/IFoxCAD.Cad/ExtensionMethod/CurveEx.cs @@ -1,984 +1,662 @@ -using Autodesk.AutoCAD.DatabaseServices; -using Autodesk.AutoCAD.Geometry; -using IFoxCAD.Collections; -using System; -using System.Collections.Generic; -using System.Linq; - -namespace IFoxCAD.Cad +namespace IFoxCAD.Cad; + +/// +/// 实体类曲线扩展类 +/// +public static class CurveEx { /// - /// 实体类曲线扩展类 + /// 曲线长度 /// - public static class CurveEx + /// 曲线 + /// 长度 + public static double GetLength(this Curve curve) { - /// - /// 曲线长度 - /// - /// 曲线 - /// 长度 - public static double GetLength(this Curve curve) - { - return - curve.GetDistanceAtParameter(curve.EndParam); - } + return curve.GetDistanceAtParameter(curve.EndParam); + } - /// - /// 获取分割曲线集合 - /// - /// 曲线 - /// 打断参数表 - /// 打断后曲线的集合 - public static IEnumerable GetSplitCurves(this Curve curve, IEnumerable pars) + /// + /// 获取分割曲线集合 + /// + /// 曲线 + /// 打断参数表 + /// 打断后曲线的集合 + public static IEnumerable GetSplitCurves(this Curve curve, IEnumerable pars) + { + return + curve + .GetSplitCurves(new DoubleCollection(pars.ToArray())) + .Cast(); + } + + /// + /// 获取分割曲线集合 + /// + /// 曲线 + /// 打断点表 + /// 打断后曲线的集合 + public static IEnumerable GetSplitCurves(this Curve curve, IEnumerable points) + { + return + curve + .GetSplitCurves(new Point3dCollection(points.ToArray())) + .Cast(); + } + + /// + /// 获取曲线集所围成的封闭区域的曲线集,注意此函数不能处理平行边(两个点及两条线组成的闭合环) + /// + /// 曲线集合 + /// 所有的闭合环的曲线集合 + public static IEnumerable GetAllCycle(this IEnumerable curves) + { + // 新建图 + var graph = new Graph(); + foreach (var curve in curves) { - return - curve - .GetSplitCurves(new DoubleCollection(pars.ToArray())) - .Cast(); +#if NET35 + graph.AddEdge(curve.ToCurve3d()!); +#else + graph.AddEdge(curve.GetGeCurve()); +#endif } - - /// - /// 获取分割曲线集合 - /// - /// 曲线 - /// 打断点表 - /// 打断后曲线的集合 - public static IEnumerable GetSplitCurves(this Curve curve, IEnumerable points) + //新建 dfs + var dfs = new DepthFirst(); + // 查询全部的 闭合环 + dfs.FindAll(graph); + // 遍历闭合环的列表,将每个闭合环转换为实体曲线 + var res = new List(); + foreach (var item in dfs.Curve3ds) { - return - curve - .GetSplitCurves(new Point3dCollection(points.ToArray())) - .Cast(); + var curve = graph.GetCurves(item.ToList()).ToArray(); + var comcur = new CompositeCurve3d(curve).ToCurve(); + if (comcur is not null) + res.Add(comcur); } + return res; + } + /// + /// 曲线打断 + /// + /// 曲线列表 + /// 打断后的曲线列表 + public static List BreakCurve(this List curves) + { + var geCurves = new List(); // 存储曲线转换后的复合曲线 + var paramss = new List>(); // 存储每个曲线的交点参数值 - private struct EdgeItem : IEquatable + for (int i = 0; i < curves.Count; i++) { - public Edge Edge; - public bool Forward; - - public EdgeItem(Edge edge, bool forward) + var cc3d = curves[i].ToCompositeCurve3d(); + if (cc3d is not null) { - Edge = edge; - Forward = forward; + geCurves.Add(cc3d); + paramss.Add(new List()); } + } + + //var oldCurves = new List(); + var newCurves = new List(); + var cci3d = new CurveCurveIntersector3d(); - public CompositeCurve3d GetCurve() + for (int i = 0; i < curves.Count; i++) + { + var gc1 = geCurves[i]; + var pars1 = paramss[i]; //引用 + for (int j = i; j < curves.Count; j++) { - var cc3d = Edge.Curve; - if (Forward) - return cc3d; - else - { - cc3d = (CompositeCurve3d)cc3d.Clone(); - return cc3d.GetReverseParameterCurve() as CompositeCurve3d; - } - } + var gc2 = geCurves[j]; + var pars2 = paramss[j]; // 引用 - public bool Equals(EdgeItem other) - { - return - Edge == other.Edge && - Forward == other.Forward; - } + cci3d.Set(gc1, gc2, Vector3d.ZAxis); - public void FindRegion(List edges, List> regions) - { - var region = new LoopList(); - var edgeItem = this; - region.Add(edgeItem); - var edgeItem2 = this.GetNext(edges); - if (edgeItem2.Edge is not null) + for (int k = 0; k < cci3d.NumberOfIntersectionPoints; k++) { - bool hasList = false; - foreach (var edgeList2 in regions) - { - var node = edgeList2.GetNode(e => e.Equals(edgeItem)); - if (node is not null) - { - if (node.Next.Value.Equals(edgeItem2)) - { - hasList = true; - break; - } - } - } - if (!hasList) - { - while (edgeItem2.Edge is not null) - { - if (edgeItem2.Edge == edgeItem.Edge) - break; - region.Add(edgeItem2); - edgeItem2 = edgeItem2.GetNext(edges); - } - if (edgeItem2.Edge == edgeItem.Edge) - regions.Add(region); - } + var pars = cci3d.GetIntersectionParameters(k); + pars1.Add(pars[0]); // 引用修改会同步到源对象 + pars2.Add(pars[1]); // 引用修改会同步到源对象 } } - public EdgeItem GetNext(List edges) + if (pars1.Count > 0) { - Vector3d vec; - int next; - if (Forward) + var c3ds = gc1.GetSplitCurves(pars1); + if (c3ds.Count > 1) { - vec = Edge.GetEndVector(); - next = Edge.EndIndex; - } - else - { - vec = Edge.GetStartVector(); - next = Edge.StartIndex; - } - - EdgeItem item = new EdgeItem(); - Vector3d vec2, vec3 = new Vector3d(); - double angle = 0; - bool hasNext = false; - bool forward = false; - foreach (var edge in edges) - { - if (edge.IsNext(Edge, next, ref vec3, ref forward)) + foreach (var c3d in c3ds) { - if (hasNext) - { - var angle2 = vec.GetAngleTo(vec3, Vector3d.ZAxis); - if (angle2 < angle) - { - vec2 = vec3; - angle = angle2; - item.Edge = edge; - item.Forward = forward; - } - } - else + var c3dCur = c3d.ToCurve(); + if (c3dCur is not null) { - vec2 = vec3; - angle = vec.GetAngleTo(vec2, Vector3d.ZAxis); - item.Edge = edge; - item.Forward = forward; - hasNext = true; + c3dCur.SetPropertiesFrom(curves[i]); + newCurves.Add(c3dCur); } } + //oldCurves.Add(curves[i]); } - return item; - } - - public override string ToString() - { - return - Forward ? - string.Format("{0}-{1}", Edge.StartIndex, Edge.EndIndex) : - string.Format("{0}-{1}", Edge.EndIndex, Edge.StartIndex); } } - private class Edge - { - public CompositeCurve3d Curve; - public int StartIndex; - public int EndIndex; + return newCurves; + } - public Vector3d GetStartVector() - { - var inter = Curve.GetInterval(); - PointOnCurve3d poc = new PointOnCurve3d(Curve, inter.LowerBound); - return poc.GetDerivative(1); - } + //转换DBCurve为GeCurved - public Vector3d GetEndVector() - { - var inter = Curve.GetInterval(); - PointOnCurve3d poc = new PointOnCurve3d(Curve, inter.UpperBound); - return -poc.GetDerivative(1); - } + #region Curve - public bool IsNext(Edge edge, int index, ref Vector3d vec, ref bool forward) - { - if (edge != this) - { - if (StartIndex == index) - { - vec = GetStartVector(); - forward = true; - return true; - } - else if (EndIndex == index) - { - vec = GetEndVector(); - forward = false; - return true; - } - } - return false; - } - } - - public static List Topo(List curves) + /// + /// 将曲线转换为ge曲线,此函数将在未来淘汰,二惊加油 + /// + /// 曲线 + /// ge曲线 + public static Curve3d? ToCurve3d(this Curve curve) + { + return curve switch { - //首先按交点分解为Ge曲线集 - List geCurves = new List(); - List> paramss = new List>(); - - foreach (var curve in curves) - { - var cc3d = curve.ToCompositeCurve3d(); - if (cc3d is not null) - { - geCurves.Add(cc3d); - paramss.Add(new List()); - } - } - - List edges = new List(); - CurveCurveIntersector3d cci3d = new CurveCurveIntersector3d(); - List newCurves = new List(); - - for (int i = 0; i < curves.Count; i++) - { - CompositeCurve3d gc1 = geCurves[i]; - List pars1 = paramss[i]; - for (int j = i; j < curves.Count; j++) - { - CompositeCurve3d gc2 = geCurves[j]; - List pars2 = paramss[j]; + Line li => ToCurve3d(li), + Circle ci => ToCurve3d(ci), + Arc arc => ToCurve3d(arc), + Ellipse el => ToCurve3d(el), + Polyline pl => ToCurve3d(pl), + Polyline2d pl2 => ToCurve3d(pl2), + Polyline3d pl3 => ToCurve3d(pl3), + Spline sp => ToCurve3d(sp), + _ => null + }; + } - cci3d.Set(gc1, gc2, Vector3d.ZAxis); + /// + /// 将曲线转换为复合曲线 + /// + /// 曲线 + /// 复合曲线 + public static CompositeCurve3d? ToCompositeCurve3d(this Curve curve) + { + return curve switch + { + Line li => new CompositeCurve3d(new Curve3d[] { ToCurve3d(li) }), + Circle ci => new CompositeCurve3d(new Curve3d[] { ToCurve3d(ci) }), + Arc arc => new CompositeCurve3d(new Curve3d[] { ToCurve3d(arc) }), + Ellipse el => new CompositeCurve3d(new Curve3d[] { ToCurve3d(el) }), + Polyline pl => new CompositeCurve3d(new Curve3d[] { ToCurve3d(pl) }), + + Polyline2d pl2 => new CompositeCurve3d(new Curve3d[] { ToCurve3d(pl2)! }), + Polyline3d pl3 => new CompositeCurve3d(new Curve3d[] { ToCurve3d(pl3) }), + Spline sp => new CompositeCurve3d(new Curve3d[] { ToCurve3d(sp) }), + _ => null + }; + } - for (int k = 0; k < cci3d.NumberOfIntersectionPoints; k++) - { - double[] pars = cci3d.GetIntersectionParameters(k); - pars1.Add(pars[0]); - pars2.Add(pars[1]); - } - } + /// + /// 将曲线转换为Nurb曲线 + /// + /// 曲线 + /// Nurb曲线 + public static NurbCurve3d? ToNurbCurve3d(this Curve curve) + { + return curve switch + { + Line li => ToNurbCurve3d(li), + Circle ci => ToNurbCurve3d(ci), + Arc arc => ToNurbCurve3d(arc), + Ellipse el => ToNurbCurve3d(el), + Polyline pl => ToNurbCurve3d(pl), + Polyline2d pl2 => ToNurbCurve3d(pl2), + Polyline3d pl3 => ToNurbCurve3d(pl3), + Spline sp => ToNurbCurve3d(sp), + _ => null + }; + } - if (pars1.Count > 0) - { - List c3ds = gc1.GetSplitCurves(pars1); - if (c3ds.Count > 0) - { - edges.AddRange( - c3ds.Select(c => new Edge { Curve = c })); - } - else if (gc1.IsClosed()) - { - newCurves.Add(gc1.ToCurve()); - } - else - { - edges.Add(new Edge { Curve = gc1 }); - } - } - else if (gc1.IsClosed()) - { - newCurves.Add(gc1.ToCurve()); - } - } + #region Line - //构建边的邻接表 - var knots = new List(); - var nums = new List(); - var closedEdges = new List(); + /// + /// 将直线转换为ge直线 + /// + /// 直线 + /// ge直线 + public static LineSegment3d ToCurve3d(this Line line) + { + return new LineSegment3d(line.StartPoint, line.EndPoint); + } - foreach (var edge in edges) - { - if (edge.Curve.IsClosed()) - { - closedEdges.Add(edge); - } - else - { - if (knots.Contains(edge.Curve.StartPoint)) - { - edge.StartIndex = - knots.IndexOf(edge.Curve.StartPoint); - nums[edge.StartIndex]++; - } - else - { - knots.Add(edge.Curve.StartPoint); - nums.Add(1); - edge.StartIndex = knots.Count - 1; - } + /// + /// 将直线转换为Nurb曲线 + /// + /// 直线 + /// Nurb曲线 + public static NurbCurve3d ToNurbCurve3d(this Line line) + { + return new NurbCurve3d(ToCurve3d(line)); + } - if (knots.Contains(edge.Curve.EndPoint)) - { - edge.EndIndex = - knots.IndexOf(edge.Curve.EndPoint); - nums[edge.EndIndex]++; - } - else - { - knots.Add(edge.Curve.EndPoint); - nums.Add(1); - edge.EndIndex = knots.Count - 1; - } - } - } + #endregion Line - newCurves.AddRange(closedEdges.Select(e => e.Curve.ToCurve())); + #region Circle - edges = - edges - .Except(closedEdges) - .Where(e => nums[e.StartIndex] > 1 && nums[e.EndIndex] > 1) - .ToList(); + /// + /// 将圆转换为ge圆弧曲线 + /// + /// 圆 + /// ge圆弧曲线 + public static CircularArc3d ToCurve3d(this Circle cir) + { + return + new CircularArc3d( + cir.Center, + cir.Normal, + cir.Radius); + } - foreach (var edge in edges.Except(closedEdges)) - { - if (nums[edge.StartIndex] == 1 || nums[edge.EndIndex] == 1) - { - if (nums[edge.StartIndex] == 1 && nums[edge.EndIndex] == 1) - { - nums[edge.StartIndex] = 0; - nums[edge.EndIndex] = 0; - } - else - { - int next = -1; - if (nums[edge.StartIndex] == 1) - { - nums[edge.StartIndex] = 0; - nums[next = edge.EndIndex]--; - } - else - { - nums[edge.EndIndex] = 0; - nums[next = edge.StartIndex]--; - } - } - } - } + /// + /// 将圆转换为ge椭圆曲线 + /// + /// 圆 + /// ge椭圆曲线 + public static EllipticalArc3d ToEllipticalArc3d(this Circle cir) + { + return ToCurve3d(cir).ToEllipticalArc3d(); + } - List> regions = new List>(); - foreach (var edge in edges) - { - var edgeItem = new EdgeItem(edge, true); - edgeItem.FindRegion(edges, regions); - edgeItem = new EdgeItem(edge, false); - edgeItem.FindRegion(edges, regions); - } + /// + /// 将圆转换为Nurb曲线 + /// + /// 圆 + /// Nurb曲线 + public static NurbCurve3d ToNurbCurve3d(this Circle cir) + { + return new NurbCurve3d(ToEllipticalArc3d(cir)); + } - for (int i = 0; i < regions.Count; i++) - { - for (int j = i + 1; j < regions.Count;) - { - bool eq = false; - if (regions[i].Count == regions[j].Count) - { - var node = regions[i].First; - var curve = node.Value.Edge.Curve; - var node2 = regions[j].GetNode(e => e.Edge.Curve == curve); - if (eq = node2 is not null) - { - var b = node.Value.Forward; - var b2 = node2.Value.Forward; - for (int k = 1; k < regions[i].Count; k++) - { - node = node.GetNext(b); - node2 = node2.GetNext(b2); - if (node.Value.Edge.Curve != node2.Value.Edge.Curve) - { - eq = false; - break; - } - } - } - } - if (eq) - regions.RemoveAt(j); - else - j++; - } - } + #endregion Circle - foreach (var region in regions) - { - var cs3ds = - region - .Select(e => e.GetCurve()) - .ToArray(); - newCurves.Add(new CompositeCurve3d(cs3ds.ToArray()).ToCurve()); - } + #region Arc - return newCurves; - } + /// + /// 将圆弧转换为ge圆弧曲线 + /// + /// 圆弧 + /// ge圆弧曲线 + public static CircularArc3d ToCurve3d(this Arc arc) + { + Plane plane = new(arc.Center, arc.Normal); + + return + new CircularArc3d( + arc.Center, + arc.Normal, + plane.GetCoordinateSystem().Xaxis, + arc.Radius, + arc.StartAngle, + arc.EndAngle + ); + } - /// - /// 曲线打断 - /// - /// 曲线列表 - /// 打断后的曲线列表 - public static List BreakCurve(List curves) - { - List geCurves = new List(); - List> paramss = new List>(); + /// + /// 将圆弧转换为ge椭圆曲线 + /// + /// 圆弧 + /// ge椭圆曲线 + public static EllipticalArc3d ToEllipticalArc3d(this Arc arc) + { + return ToCurve3d(arc).ToEllipticalArc3d(); + } - foreach (var curve in curves) - { - var cc3d = curve.ToCompositeCurve3d(); - if (cc3d is not null) - { - geCurves.Add(cc3d); - paramss.Add(new List()); - } - } + /// + /// 将圆弧转换为三维Nurb曲线 + /// + /// 圆弧 + /// 三维Nurb曲线 + public static NurbCurve3d ToNurbCurve3d(this Arc arc) + { + return new NurbCurve3d(ToEllipticalArc3d(arc)); + } - List oldCurves = new List(); - List newCurves = new List(); - CurveCurveIntersector3d cci3d = new CurveCurveIntersector3d(); + #endregion Arc - for (int i = 0; i < curves.Count; i++) - { - CompositeCurve3d gc1 = geCurves[i]; - List pars1 = paramss[i]; - for (int j = i; j < curves.Count; j++) - { - CompositeCurve3d gc2 = geCurves[j]; - List pars2 = paramss[j]; + #region Ellipse - cci3d.Set(gc1, gc2, Vector3d.ZAxis); + /// + /// 将椭圆转换为三维ge椭圆曲线 + /// + /// 椭圆 + /// 三维ge椭圆曲线 + public static EllipticalArc3d ToCurve3d(this Ellipse ell) + { + return + new EllipticalArc3d( + ell.Center, + ell.MajorAxis.GetNormal(), + ell.MinorAxis.GetNormal(), + ell.MajorRadius, + ell.MinorRadius, + ell.StartParam, + ell.EndParam); + } - for (int k = 0; k < cci3d.NumberOfIntersectionPoints; k++) - { - double[] pars = cci3d.GetIntersectionParameters(k); - pars1.Add(pars[0]); - pars2.Add(pars[1]); - } - } + /// + /// 将椭圆转换为三维Nurb曲线 + /// + /// 椭圆 + /// 三维Nurb曲线 + public static NurbCurve3d ToNurbCurve3d(this Ellipse ell) + { + return new NurbCurve3d(ToCurve3d(ell)); + } - if (pars1.Count > 0) - { - List c3ds = gc1.GetSplitCurves(pars1); - if (c3ds.Count > 1) - { - foreach (CompositeCurve3d c3d in c3ds) - { - Curve c = c3d.ToCurve(); - if (c is not null) - { - c.SetPropertiesFrom(curves[i]); - newCurves.Add(c); - } - } - oldCurves.Add(curves[i]); - } - } - } - curves = oldCurves; - return newCurves; - } + #endregion Ellipse - //转换DBCurve为GeCurved + #region Spline - #region Curve + /// + /// 将样条曲线转换为三维Nurb曲线 + /// + /// 样条曲线 + /// 三维Nurb曲线 + public static NurbCurve3d ToCurve3d(this Spline spl) + { + NurbCurve3d nc3d; + NurbsData ndata = spl.NurbsData; + KnotCollection knots = new(); + foreach (Double knot in ndata.GetKnots()) + knots.Add(knot); - /// - /// 将曲线转换为ge曲线,此函数将在未来淘汰,二惊加油 - /// - /// 曲线 - /// ge曲线 - [Obsolete("请使用Cad自带的 GetGeCurve 函数!")] - public static Curve3d? ToCurve3d(this Curve curve) + if (ndata.Rational) { - return curve switch - { - Line li => ToCurve3d(li), - Circle ci => ToCurve3d(ci), - Arc arc => ToCurve3d(arc), - Ellipse el => ToCurve3d(el), - Polyline pl => ToCurve3d(pl), - Polyline2d pl2 => ToCurve3d(pl2), - Polyline3d pl3 => ToCurve3d(pl3), - Spline sp => ToCurve3d(sp), - _ => null - }; + nc3d = + new NurbCurve3d( + ndata.Degree, + knots, + ndata.GetControlPoints(), + ndata.GetWeights(), + ndata.Periodic); } - - /// - /// 将曲线转换为复合曲线 - /// - /// 曲线 - /// 复合曲线 - public static CompositeCurve3d? ToCompositeCurve3d(this Curve curve) + else { - return curve switch - { - Line li => new CompositeCurve3d(new Curve3d[] { ToCurve3d(li) }), - Circle ci => new CompositeCurve3d(new Curve3d[] { ToCurve3d(ci) }), - Arc arc => new CompositeCurve3d(new Curve3d[] { ToCurve3d(arc) }), - Ellipse el => new CompositeCurve3d(new Curve3d[] { ToCurve3d(el) }), - Polyline pl => new CompositeCurve3d(new Curve3d[] { ToCurve3d(pl) }), - - Polyline2d pl2 => new CompositeCurve3d(new Curve3d[] { ToCurve3d(pl2) }), - Polyline3d pl3 => new CompositeCurve3d(new Curve3d[] { ToCurve3d(pl3) }), - Spline sp => new CompositeCurve3d(new Curve3d[] { ToCurve3d(sp) }), - _ => null - }; + nc3d = + new NurbCurve3d( + ndata.Degree, + knots, + ndata.GetControlPoints(), + ndata.Periodic); } - /// - /// 将曲线转换为Nurb曲线 - /// - /// 曲线 - /// Nurb曲线 - public static NurbCurve3d? ToNurbCurve3d(this Curve curve) + if (spl.HasFitData) { - return curve switch - { - Line li => ToNurbCurve3d(li), - Circle ci => ToNurbCurve3d(ci), - Arc arc => ToNurbCurve3d(arc), - Ellipse el => ToNurbCurve3d(el), - Polyline pl => ToNurbCurve3d(pl), - Polyline2d pl2 => ToNurbCurve3d(pl2), - Polyline3d pl3 => ToNurbCurve3d(pl3), - Spline sp => ToNurbCurve3d(sp), - _ => null - }; + var fdata = spl.FitData; + var vec = new Vector3d(); + if (fdata.TangentsExist && (fdata.StartTangent != vec || fdata.EndTangent != vec)) + nc3d.SetFitData(fdata.GetFitPoints(), fdata.StartTangent, fdata.EndTangent); } + return nc3d; + } - #region Line + #endregion Spline - /// - /// 将直线转换为ge直线 - /// - /// 直线 - /// ge直线 - public static LineSegment3d ToCurve3d(this Line line) - { - return new LineSegment3d(line.StartPoint, line.EndPoint); - } + #region Polyline2d - /// - /// 将直线转换为Nurb曲线 - /// - /// 直线 - /// Nurb曲线 - public static NurbCurve3d ToNurbCurve3d(this Line line) + /// + /// 将二维多段线转换为三维ge曲线 + /// + /// 二维多段线 + /// 三维ge曲线 + public static Curve3d? ToCurve3d(this Polyline2d pl2d) + { + switch (pl2d.PolyType) { - return new NurbCurve3d(ToCurve3d(line)); + case Poly2dType.SimplePoly: + case Poly2dType.FitCurvePoly: + Polyline pl = new(); + pl.SetDatabaseDefaults(); + pl.ConvertFrom(pl2d, false); + return ToCurve3d(pl); + default: + return ToNurbCurve3d(pl2d); } - #endregion Line - - #region Circle - - /// - /// 将圆转换为ge圆弧曲线 - /// - /// 圆 - /// ge圆弧曲线 - public static CircularArc3d ToCurve3d(this Circle cir) - { - return - new CircularArc3d( - cir.Center, - cir.Normal, - cir.Radius); - } + //Polyline pl = new Polyline(); + //pl.ConvertFrom(pl2d, false); + //return ToCurve3d(pl); + } - /// - /// 将圆转换为ge椭圆曲线 - /// - /// 圆 - /// ge椭圆曲线 - public static EllipticalArc3d ToEllipticalArc3d(this Circle cir) + /// + /// 将二维多段线转换为三维Nurb曲线 + /// + /// 二维多段线 + /// 三维Nurb曲线 + public static NurbCurve3d? ToNurbCurve3d(this Polyline2d pl2d) + { + switch (pl2d.PolyType) { - return ToCurve3d(cir).ToEllipticalArc3d(); + case Poly2dType.SimplePoly: + case Poly2dType.FitCurvePoly: + Polyline pl = new(); + pl.SetDatabaseDefaults(); + pl.ConvertFrom(pl2d, false); + return ToNurbCurve3d(pl); + + default: + return ToCurve3d(pl2d.Spline); } + } - /// - /// 将圆转换为Nurb曲线 - /// - /// 圆 - /// Nurb曲线 - public static NurbCurve3d ToNurbCurve3d(this Circle cir) + /// + /// 将二维多段线转换为三维ge多段线 + /// + /// 二维多段线 + /// 三维ge多段线 + public static PolylineCurve3d ToPolylineCurve3d(this Polyline2d pl) + { + Point3dCollection pnts = new(); + foreach (Vertex2d ver in pl) { - return new NurbCurve3d(ToEllipticalArc3d(cir)); + pnts.Add(ver.Position); } + return new PolylineCurve3d(pnts); + } - #endregion Circle - - #region Arc + #endregion Polyline2d - /// - /// 将圆弧转换为ge圆弧曲线 - /// - /// 圆弧 - /// ge圆弧曲线 - public static CircularArc3d ToCurve3d(this Arc arc) - { - Plane plane = new Plane(arc.Center, arc.Normal); - - return - new CircularArc3d( - arc.Center, - arc.Normal, - plane.GetCoordinateSystem().Xaxis, - arc.Radius, - arc.StartAngle, - arc.EndAngle - ); - } + #region Polyline3d - /// - /// 将圆弧转换为ge椭圆曲线 - /// - /// 圆弧 - /// ge椭圆曲线 - public static EllipticalArc3d ToEllipticalArc3d(this Arc arc) - { - return ToCurve3d(arc).ToEllipticalArc3d(); - } - - /// - /// 将圆弧转换为三维Nurb曲线 - /// - /// 圆弧 - /// 三维Nurb曲线 - public static NurbCurve3d ToNurbCurve3d(this Arc arc) + /// + /// 将三维多段线转换为三维曲线 + /// + /// 三维多段线 + /// 三维曲线 + public static Curve3d ToCurve3d(this Polyline3d pl3d) + { + return pl3d.PolyType switch { - return new NurbCurve3d(ToEllipticalArc3d(arc)); - } - - #endregion Arc + Poly3dType.SimplePoly => ToPolylineCurve3d(pl3d), + _ => ToNurbCurve3d(pl3d), + }; + } - #region Ellipse + /// + /// 将三维多段线转换为三维Nurb曲线 + /// + /// 三维多段线 + /// 三维Nurb曲线 + public static NurbCurve3d ToNurbCurve3d(this Polyline3d pl3d) + { + return ToCurve3d(pl3d.Spline); + } - /// - /// 将椭圆转换为三维ge椭圆曲线 - /// - /// 椭圆 - /// 三维ge椭圆曲线 - public static EllipticalArc3d ToCurve3d(this Ellipse ell) + /// + /// 将三维多段线转换为三维ge多段线 + /// + /// 三维多段线 + /// 三维ge多段线 + public static PolylineCurve3d ToPolylineCurve3d(this Polyline3d pl) + { + Point3dCollection pnts = new(); + foreach (ObjectId id in pl) { - return - new EllipticalArc3d( - ell.Center, - ell.MajorAxis.GetNormal(), - ell.MinorAxis.GetNormal(), - ell.MajorRadius, - ell.MinorRadius, - ell.StartParam, - ell.EndParam); + PolylineVertex3d ver = (PolylineVertex3d)id.GetObject(OpenMode.ForRead); + pnts.Add(ver.Position); } + return new PolylineCurve3d(pnts); + } - /// - /// 将椭圆转换为三维Nurb曲线 - /// - /// 椭圆 - /// 三维Nurb曲线 - public static NurbCurve3d ToNurbCurve3d(this Ellipse ell) - { - return new NurbCurve3d(ToCurve3d(ell)); - } + #endregion Polyline3d - #endregion Ellipse + #region Polyline - #region Spline + /// + /// 多段线转换为复合曲线 + /// + /// 多段线对象 + /// 复合曲线对象 + public static CompositeCurve3d ToCurve3d(this Polyline pl) + { + List c3ds = new(); - /// - /// 将样条曲线转换为三维Nurb曲线 - /// - /// 样条曲线 - /// 三维Nurb曲线 - public static NurbCurve3d ToCurve3d(this Spline spl) + for (int i = 0; i < pl.NumberOfVertices; i++) { - NurbCurve3d nc3d; - NurbsData ndata = spl.NurbsData; - KnotCollection knots = new KnotCollection(); - foreach (Double knot in ndata.GetKnots()) - knots.Add(knot); - - if (ndata.Rational) + switch (pl.GetSegmentType(i)) { - nc3d = - new NurbCurve3d( - ndata.Degree, - knots, - ndata.GetControlPoints(), - ndata.GetWeights(), - ndata.Periodic); - } - else - { - nc3d = - new NurbCurve3d( - ndata.Degree, - knots, - ndata.GetControlPoints(), - ndata.Periodic); - } - - if (spl.HasFitData) - { - var fdata = spl.FitData; - var vec = new Vector3d(); - if (fdata.TangentsExist && (fdata.StartTangent != vec || fdata.EndTangent != vec)) - nc3d.SetFitData(fdata.GetFitPoints(), fdata.StartTangent, fdata.EndTangent); - } - return nc3d; - } - - #endregion Spline + case SegmentType.Line: + c3ds.Add(pl.GetLineSegmentAt(i)); + break; - #region Polyline2d - - /// - /// 将二维多段线转换为三维ge曲线 - /// - /// 二维多段线 - /// 三维ge曲线 - public static Curve3d ToCurve3d(this Polyline2d pl2d) - { - switch (pl2d.PolyType) - { - case Poly2dType.SimplePoly: - case Poly2dType.FitCurvePoly: - Polyline pl = new Polyline(); - pl.ConvertFrom(pl2d, false); - return ToCurve3d(pl); + case SegmentType.Arc: + c3ds.Add(pl.GetArcSegmentAt(i)); + break; default: - return ToNurbCurve3d(pl2d); + break; } - - //Polyline pl = new Polyline(); - //pl.ConvertFrom(pl2d, false); - //return ToCurve3d(pl); } + return new CompositeCurve3d(c3ds.ToArray()); + } - /// - /// 将二维多段线转换为三维Nurb曲线 - /// - /// 二维多段线 - /// 三维Nurb曲线 - public static NurbCurve3d ToNurbCurve3d(this Polyline2d pl2d) + /// + /// 多段线转换为Nurb曲线 + /// + /// 多段线 + /// Nurb曲线 + public static NurbCurve3d? ToNurbCurve3d(this Polyline pl) + { + NurbCurve3d? nc3d = null; + for (int i = 0; i < pl.NumberOfVertices; i++) { - switch (pl2d.PolyType) + NurbCurve3d? nc3dtemp = null; + switch (pl.GetSegmentType(i)) { - case Poly2dType.SimplePoly: - case Poly2dType.FitCurvePoly: - Polyline pl = new Polyline(); - pl.ConvertFrom(pl2d, false); - return ToNurbCurve3d(pl); + case SegmentType.Line: + nc3dtemp = new NurbCurve3d(pl.GetLineSegmentAt(i)); + break; + + case SegmentType.Arc: + nc3dtemp = pl.GetArcSegmentAt(i).ToNurbCurve3d(); + break; default: - return ToCurve3d(pl2d.Spline); + break; } - } - - /// - /// 将二维多段线转换为三维ge多段线 - /// - /// 二维多段线 - /// 三维ge多段线 - public static PolylineCurve3d ToPolylineCurve3d(this Polyline2d pl) - { - var pnts = new Point3dCollection(); - foreach (Vertex2d ver in pl) - pnts.Add(ver.Position); - return new PolylineCurve3d(pnts); - } - #endregion Polyline2d - - #region Polyline3d - - /// - /// 将三维多段线转换为三维曲线 - /// - /// 三维多段线 - /// 三维曲线 - public static Curve3d ToCurve3d(this Polyline3d pl3d) - { - return pl3d.PolyType switch - { - Poly3dType.SimplePoly => ToPolylineCurve3d(pl3d), - _ => ToNurbCurve3d(pl3d), - }; - } - - /// - /// 将三维多段线转换为三维Nurb曲线 - /// - /// 三维多段线 - /// 三维Nurb曲线 - public static NurbCurve3d ToNurbCurve3d(this Polyline3d pl3d) - { - return ToCurve3d(pl3d.Spline); - } - - /// - /// 将三维多段线转换为三维ge多段线 - /// - /// 三维多段线 - /// 三维ge多段线 - public static PolylineCurve3d ToPolylineCurve3d(this Polyline3d pl) - { - Point3dCollection pnts = new Point3dCollection(); - foreach (ObjectId id in pl) + if (nc3d is null) { - PolylineVertex3d ver = (PolylineVertex3d)id.GetObject(OpenMode.ForRead); - pnts.Add(ver.Position); + nc3d = nc3dtemp; } - return new PolylineCurve3d(pnts); - } - - #endregion Polyline3d - - #region Polyline - - /// - /// 多段线转换为复合曲线 - /// - /// 多段线对象 - /// 复合曲线对象 - public static CompositeCurve3d ToCurve3d(this Polyline pl) - { - List c3ds = new List(); - - for (int i = 0; i < pl.NumberOfVertices; i++) + else if (nc3dtemp is not null) { - switch (pl.GetSegmentType(i)) - { - case SegmentType.Line: - c3ds.Add(pl.GetLineSegmentAt(i)); - break; - - case SegmentType.Arc: - c3ds.Add(pl.GetArcSegmentAt(i)); - break; - - default: - break; - } + nc3d.JoinWith(nc3dtemp); } - return new CompositeCurve3d(c3ds.ToArray()); } + return nc3d; + } - /// - /// 多段线转换为Nurb曲线 - /// - /// 多段线 - /// Nurb曲线 - public static NurbCurve3d ToNurbCurve3d(this Polyline pl) - { - NurbCurve3d nc3d = null; - for (int i = 0; i < pl.NumberOfVertices; i++) - { - NurbCurve3d nc3dtemp = null; - switch (pl.GetSegmentType(i)) - { - case SegmentType.Line: - nc3dtemp = new NurbCurve3d(pl.GetLineSegmentAt(i)); - break; + /// + /// 为优化多段线倒角 + /// + /// 优化多段线 + /// 顶点索引号 + /// 倒角半径 + /// 倒角类型 + public static void ChamferAt(this Polyline polyline, int index, double radius, bool isFillet) + { + if (index < 1 || index > polyline.NumberOfVertices - 2) + throw new System.Exception("错误的索引号"); - case SegmentType.Arc: - nc3dtemp = pl.GetArcSegmentAt(i).ToNurbCurve3d(); - break; + if (SegmentType.Line != polyline.GetSegmentType(index - 1) || + SegmentType.Line != polyline.GetSegmentType(index)) + throw new System.Exception("非直线段不能倒角"); - default: - break; - } - if (nc3d is null) + //获取当前索引号的前后两段直线,并组合为Ge复合曲线 + Curve3d[] c3ds = + new Curve3d[] { - nc3d = nc3dtemp; - } - else if (nc3dtemp is not null) - { - nc3d.JoinWith(nc3dtemp); - } - } - return nc3d; - } - - /// - /// 为优化多段线倒角 - /// - /// 优化多段线 - /// 顶点索引号 - /// 倒角半径 - /// 倒角类型 - public static void ChamferAt(this Polyline polyline, int index, double radius, bool isFillet) - { - if (index < 1 || index > polyline.NumberOfVertices - 2) - throw new System.Exception("错误的索引号"); - - if (polyline.GetSegmentType(index - 1) != SegmentType.Line || - polyline.GetSegmentType(index) != SegmentType.Line) - throw new System.Exception("非直线段不能倒角"); - - //获取当前索引号的前后两段直线,并组合为Ge复合曲线 - var c3ds = new Curve3d[] - { - polyline.GetLineSegmentAt(index - 1), - polyline.GetLineSegmentAt(index) - }; - var cc3d = new CompositeCurve3d(c3ds); - - //试倒直角 - //子曲线的个数有三种情况: - //1、=3时倒角方向正确 - //2、=2时倒角方向相反 - //3、=0或为直线时失败 - c3ds = cc3d.GetTrimmedOffset + polyline.GetLineSegmentAt(index - 1), + polyline.GetLineSegmentAt(index) + }; + CompositeCurve3d cc3d = new(c3ds); + + //试倒直角 + //子曲线的个数有三种情况: + //1、=3时倒角方向正确 + //2、=2时倒角方向相反 + //3、=0或为直线时失败 + c3ds = + cc3d.GetTrimmedOffset ( radius, Vector3d.ZAxis, OffsetCurveExtensionType.Chamfer ); - if (c3ds.Length > 0 && c3ds[0] is CompositeCurve3d newcc3d) - { - c3ds = newcc3d.GetCurves(); - if (c3ds.Length == 3) - { - c3ds = cc3d.GetTrimmedOffset - ( - -radius, - Vector3d.ZAxis, - OffsetCurveExtensionType.Chamfer - ); - if (c3ds.Length == 0 || c3ds[0] is LineSegment3d) - throw new System.Exception("倒角半径过大"); - } - else if (c3ds.Length == 2) - { - radius = -radius; - } + if (c3ds.Length > 0 && c3ds[0] is CompositeCurve3d) + { + var newcc3d = c3ds[0] as CompositeCurve3d; + c3ds = newcc3d!.GetCurves(); + if (c3ds.Length == 3) + { + c3ds = cc3d.GetTrimmedOffset + ( + -radius, + Vector3d.ZAxis, + OffsetCurveExtensionType.Chamfer + ); + if (c3ds.Length == 0 || c3ds[0] is LineSegment3d) + throw new System.Exception("倒角半径过大"); } - else + else if (c3ds.Length == 2) { - throw new System.Exception("倒角半径过大"); + radius = -radius; } - - //GetTrimmedOffset会生成倒角+偏移,故先反方向倒角,再倒回 - c3ds = cc3d.GetTrimmedOffset - ( - -radius, - Vector3d.ZAxis, - OffsetCurveExtensionType.Extend - ); - var type = isFillet ? OffsetCurveExtensionType.Fillet : OffsetCurveExtensionType.Chamfer; - c3ds = c3ds[0].GetTrimmedOffset - ( - radius, - Vector3d.ZAxis, - type - ); - - //将结果Ge曲线转为Db曲线,并将相关的数值反映到原曲线 - var plTemp = c3ds[0].ToCurve() as Polyline; - polyline.RemoveVertexAt(index); - polyline.AddVertexAt(index, plTemp.GetPoint2dAt(1), plTemp.GetBulgeAt(1), 0, 0); - polyline.AddVertexAt(index + 1, plTemp.GetPoint2dAt(2), 0, 0, 0); + } + else + { + throw new System.Exception("倒角半径过大"); } - #endregion Polyline - - #endregion Curve + //GetTrimmedOffset会生成倒角+偏移,故先反方向倒角,再倒回 + c3ds = cc3d.GetTrimmedOffset + ( + -radius, + Vector3d.ZAxis, + OffsetCurveExtensionType.Extend + ); + OffsetCurveExtensionType type = + isFillet ? + OffsetCurveExtensionType.Fillet : OffsetCurveExtensionType.Chamfer; + c3ds = c3ds[0].GetTrimmedOffset + ( + radius, + Vector3d.ZAxis, + type + ); + + //将结果Ge曲线转为Db曲线,并将相关的数值反映到原曲线 + var plTemp = c3ds[0].ToCurve() as Polyline; + if (plTemp == null) + return; + polyline.RemoveVertexAt(index); + polyline.AddVertexAt(index, plTemp.GetPoint2dAt(1), plTemp.GetBulgeAt(1), 0, 0); + polyline.AddVertexAt(index + 1, plTemp.GetPoint2dAt(2), 0, 0, 0); } -} + + #endregion Polyline + + #endregion Curve +} \ No newline at end of file diff --git a/src/IFoxCAD.Cad/ExtensionMethod/DBDictionaryEx.cs b/src/IFoxCAD.Cad/ExtensionMethod/DBDictionaryEx.cs index d4fba82..89dc2d5 100644 --- a/src/IFoxCAD.Cad/ExtensionMethod/DBDictionaryEx.cs +++ b/src/IFoxCAD.Cad/ExtensionMethod/DBDictionaryEx.cs @@ -1,389 +1,371 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using Autodesk.AutoCAD.DatabaseServices; -using Autodesk.AutoCAD.Geometry; - -namespace IFoxCAD.Cad +namespace IFoxCAD.Cad; + +/// +/// 字典扩展类 +/// +public static class DBDictionaryEx { /// - /// 字典扩展类 + /// 获取字典里的全部对象 /// - public static class DBDictionaryEx + /// 对象类型的泛型 + /// 字典 + /// 事务 + /// 对象迭代器 + public static IEnumerable GetAllObjects(this DBDictionary dict, DBTrans? trans = null) where T : DBObject { - /// - /// 获取字典里的全部对象 - /// - /// 对象类型的泛型 - /// 字典 - /// 事务 - /// 对象迭代器 - public static IEnumerable GetAllObjects(this DBDictionary dict, Transaction trans = null) where T : DBObject + trans ??= DBTrans.Top; + foreach (DBDictionaryEntry e in dict) { - trans ??= DBTrans.Top.Transaction; - foreach (DBDictionaryEntry e in dict) - { - yield return trans.GetObject(e.Value, OpenMode.ForRead) as T; - } + var ent = trans.GetObject(e.Value); + if (ent is not null) + yield return ent; } + } - /// - /// 获取字典内指定key的对象 - /// - /// 对象类型的泛型 - /// 字典 - /// 事务 - /// 指定的键值 - /// T 类型的对象 - public static T GetAt(this DBDictionary dict, string key, Transaction trans = null) where T : DBObject + /// + /// 获取字典内指定key的对象 + /// + /// 对象类型的泛型 + /// 字典 + /// 事务 + /// 指定的键值 + /// T 类型的对象 + public static T? GetAt(this DBDictionary dict, string key, DBTrans? trans = null) where T : DBObject + { + trans ??= DBTrans.Top; + if (dict.Contains(key)) { - trans ??= DBTrans.Top.Transaction; - if (dict.Contains(key)) - { - ObjectId id = dict.GetAt(key); - if (!id.IsNull) - { - return trans.GetObject(id, OpenMode.ForRead) as T; - } - } - return null; + ObjectId id = dict.GetAt(key); + if (!id.IsNull) + return trans.GetObject(id); } + return null; + } - /// - /// 添加条目(键值对)到字典 - /// - /// 对象类型 - /// 字典 - /// 事务 - /// 键 - /// 值 - public static void SetAt(this DBDictionary dict, string key, T obj, Transaction tr = null) where T : DBObject + /// + /// 添加条目(键值对)到字典 + /// + /// 对象类型 + /// 字典 + /// 事务 + /// 键 + /// 值 + public static void SetAt(this DBDictionary dict, string key, T obj, Transaction? trans = null) where T : DBObject + { + trans ??= DBTrans.Top.Transaction; + using (dict.ForWrite()) { - tr ??= DBTrans.Top.Transaction; - using (dict.ForWrite()) - { - dict.SetAt(key, obj); - tr.AddNewlyCreatedDBObject(obj, true); - } + dict.SetAt(key, obj); + trans.AddNewlyCreatedDBObject(obj, true); } + } - #region XRecord + #region XRecord - /// - /// 从字典中获取扩展数据 - /// - /// 字典 - /// 键值 - /// 扩展数据 - public static XRecordDataList GetXRecord(this DBDictionary dict, string key) - { - Xrecord rec = dict.GetAt(key); - if (rec is not null) - return rec.Data; - return null; - } + /// + /// 从字典中获取扩展数据 + /// + /// 字典 + /// 键值 + /// 扩展数据 + public static XRecordDataList? GetXRecord(this DBDictionary dict, string key) + { + Xrecord? rec = dict.GetAt(key); + if (rec is not null) + return rec.Data; + return null; + } - /// - /// 保存扩展数据到字典 - /// - /// 扩展数据 - /// 字典 - /// 键值 - public static void SetXRecord(this DBDictionary dict, string key, XRecordDataList rb) - { - using var data = new Xrecord { Data = rb }; - dict.SetAt(key, data); - } - #endregion - - /// - /// 获取扩展字典 - /// - /// 对象 - /// 事务 - /// 扩展字典对象 - public static DBDictionary GetXDictionary(this DBObject obj, Transaction trans = null) + /// + /// 保存扩展数据到字典 + /// + /// 扩展数据 + /// 字典 + /// 键值 + public static void SetXRecord(this DBDictionary dict, string key, XRecordDataList rb) + { + using var data = new Xrecord { Data = rb }; + dict.SetAt(key, data); + } + #endregion + + /// + /// 获取扩展字典 + /// + /// 对象 + /// 事务 + /// 扩展字典对象 + public static DBDictionary? GetXDictionary(this DBObject obj, DBTrans? trans = null) + { + trans ??= DBTrans.Top; + ObjectId id = obj.ExtensionDictionary; + if (id.IsNull) { - trans ??= DBTrans.Top.Transaction; - ObjectId id = obj.ExtensionDictionary; - if (id.IsNull) + using (obj.ForWrite()) { - using (obj.ForWrite()) - { - obj.CreateExtensionDictionary(); - } - - id = obj.ExtensionDictionary; + obj.CreateExtensionDictionary(); } - return id.GetObject(tr: trans); + + id = obj.ExtensionDictionary; } + return id.GetObject(tr: trans); + } - #region 数据表 + #region 数据表 - /// - /// 创建数据表 - /// - /// 原数据类型的字典 - /// 表元素(二维数组) - /// 数据表 - public static DataTable CreateDataTable(Dictionary colTypes, object[,] content) + /// + /// 创建数据表 + /// + /// 原数据类型的字典 + /// 表元素(二维数组) + /// 数据表 + public static DataTable CreateDataTable(Dictionary colTypes, object[,] content) + { + DataTable table = new(); + foreach (var t in colTypes) + table.AppendColumn(t.Value, t.Key); + var ncol = colTypes.Count; + var nrow = content.GetLength(0); + var types = new CellType[ncol]; + colTypes.Values.CopyTo(types, 0); + for (int i = 0; i < nrow; i++) { - DataTable table = new(); - foreach (var t in colTypes) - table.AppendColumn(t.Value, t.Key); - var ncol = colTypes.Count; - var nrow = content.GetLength(0); - var types = new CellType[ncol]; - colTypes.Values.CopyTo(types, 0); - for (int i = 0; i < nrow; i++) + DataCellCollection row = new(); + for (int j = 0; j < ncol; j++) { - DataCellCollection row = new(); - for (int j = 0; j < ncol; j++) - { - var cell = new DataCell(); - cell.SetValue(types[j], content[i, j]); - row.Add(cell); - } - table.AppendRow(row, true); + var cell = new DataCell(); + cell.SetValue(types[j], content[i, j]); + row.Add(cell); } - return table; + table.AppendRow(row, true); } + return table; + } - /// - /// 设定单元格数据 - /// - /// 单元格 - /// 类型 - /// 数据 - public static void SetValue(this DataCell cell, CellType type, object value) - { + /// + /// 设定单元格数据 + /// + /// 单元格 + /// 类型 + /// 数据 + public static void SetValue(this DataCell cell, CellType type, object value) + { - switch (type) - { - case CellType.Bool: - cell.SetBool((bool)value); - break; + switch (type) + { + case CellType.Bool: + cell.SetBool((bool)value); + break; - case CellType.CharPtr: - cell.SetString((string)value); - break; + case CellType.CharPtr: + cell.SetString((string)value); + break; - case CellType.Integer: - cell.SetInteger((int)value); - break; + case CellType.Integer: + cell.SetInteger((int)value); + break; - case CellType.Double: - cell.SetDouble((double)value); - break; + case CellType.Double: + cell.SetDouble((double)value); + break; - case CellType.ObjectId: - cell.SetObjectId((ObjectId)value); - break; + case CellType.ObjectId: + cell.SetObjectId((ObjectId)value); + break; - case CellType.Point: - cell.SetPoint((Point3d)value); - break; + case CellType.Point: + cell.SetPoint((Point3d)value); + break; - case CellType.Vector: - cell.SetVector((Vector3d)value); - break; + case CellType.Vector: + cell.SetVector((Vector3d)value); + break; - case CellType.HardOwnerId: - cell.SetHardOwnershipId((ObjectId)value); - break; + case CellType.HardOwnerId: + cell.SetHardOwnershipId((ObjectId)value); + break; - case CellType.HardPtrId: - cell.SetHardPointerId((ObjectId)value); - break; + case CellType.HardPtrId: + cell.SetHardPointerId((ObjectId)value); + break; - case CellType.SoftOwnerId: - cell.SetSoftOwnershipId((ObjectId)value); - break; + case CellType.SoftOwnerId: + cell.SetSoftOwnershipId((ObjectId)value); + break; - case CellType.SoftPtrId: - cell.SetSoftPointerId((ObjectId)value); - break; - } + case CellType.SoftPtrId: + cell.SetSoftPointerId((ObjectId)value); + break; } - #endregion - - #region 子字典 - /// - /// 获取子字典 - /// - /// 根字典 - /// 事务 - /// 是否创建子字典 - /// 键值列表 - /// 字典 - public static DBDictionary GetSubDictionary(this DBDictionary dict, bool createSubDictionary, IEnumerable dictNames, Transaction trans = null) + } + #endregion + + #region 子字典 + /// + /// 获取子字典 + /// + /// 根字典 + /// 事务 + /// 是否创建子字典 + /// 键值列表 + /// 字典 + public static DBDictionary? GetSubDictionary(this DBDictionary dict, bool createSubDictionary, IEnumerable dictNames, DBTrans? trans = null) + { + DBDictionary? newdict = null; + trans ??= DBTrans.Top; + if (createSubDictionary) { - trans ??= DBTrans.Top.Transaction; - if (createSubDictionary) - { - using (dict.ForWrite()) - dict.TreatElementsAsHard = true; + using (dict.ForWrite()) + dict.TreatElementsAsHard = true; - foreach (string name in dictNames) - { - if (dict.Contains(name)) - { - dict = dict.GetAt(name, trans); - } - else - { - DBDictionary subDict = new(); - dict.SetAt(name, subDict, trans); - dict = subDict; - dict.TreatElementsAsHard = true; - } - } - } - else + foreach (string name in dictNames) { - foreach (string name in dictNames) + if (dict!.Contains(name)) { - if (dict.Contains(name)) - dict = dict.GetAt(name, trans); - else - return null; + newdict = dict.GetAt(name, trans); } - } - - return dict; - } - - - ///// - ///// 获取对象扩展字典的子字典 - ///// - ///// 对象 - ///// 事务 - ///// 是否创建子字典 - ///// 键值列表 - ///// 字典 - //public static DBDictionary GetSubDictionary(this DBObject obj, bool createSubDictionary, params string[] dictNames) - //{ - // return obj.GetXDictionary().GetSubDictionary(createSubDictionary, dictNames); - //} - - #endregion - - #region 组字典 - /// - /// 添加编组 - /// - /// 组名 - /// 实体Id集合 - /// 编组Id - public static ObjectId AddGroup(this DBDictionary dict, string name, ObjectIdCollection ids) - { - if (dict.Contains(name)) - { - return ObjectId.Null; - } - else - { - using (dict.ForWrite()) + else { - Group g = new(); - g.Append(ids); - dict.SetAt(name, g); - DBTrans.Top.Transaction.AddNewlyCreatedDBObject(g, true); - return g.ObjectId; + DBDictionary subDict = new(); + dict.SetAt(name, subDict, trans); + newdict = subDict; + newdict.TreatElementsAsHard = true; } } } - - /// - /// 添加编组 - /// - /// 组名 - /// 实体Id集合 - /// 编组Id - public static ObjectId AddGroup(this DBDictionary dict, string name, IEnumerable ids) + else { - if (dict.Contains(name)) + foreach (string name in dictNames) { - return ObjectId.Null; + if (dict.Contains(name)) + newdict = dict.GetAt(name, trans); + else + return null; } - return dict.AddGroup(name, new ObjectIdCollection(ids.ToArray())); } + return newdict; + } - /// - /// 按选择条件获取编组集合 - /// - /// 选择条件,过滤函数 - /// g.NumEntities < 2);]]> - /// 编组集合 - public static IEnumerable GetGroups(this DBDictionary dict, Func func) + + ///// + ///// 获取对象扩展字典的子字典 + ///// + ///// 对象 + ///// 事务 + ///// 是否创建子字典 + ///// 键值列表 + ///// 字典 + //public static DBDictionary GetSubDictionary(this DBObject obj, bool createSubDictionary, params string[] dictNames) + //{ + // return obj.GetXDictionary().GetSubDictionary(createSubDictionary, dictNames); + //} + + #endregion + + #region 组字典 + /// + /// 添加编组 + /// + /// 组名 + /// 实体Id集合 + /// 编组Id + public static ObjectId AddGroup(this DBDictionary dict, string name, ObjectIdCollection ids) + { + if (dict.Contains(name)) + return ObjectId.Null; + + using (dict.ForWrite()) { - return - dict - .GetAllObjects() - .Where(func); + Group g = new(); + g.Append(ids); + dict.SetAt(name, g); + DBTrans.Top.Transaction.AddNewlyCreatedDBObject(g, true); + return g.ObjectId; } + } + + /// + /// 添加编组 + /// + /// 组名 + /// 实体Id集合 + /// 编组Id + public static ObjectId AddGroup(this DBDictionary dict, string name, IEnumerable ids) + { + if (dict.Contains(name)) + return ObjectId.Null; + + return dict.AddGroup(name, new ObjectIdCollection(ids.ToArray())); + } - /// - /// 返回实体的所在编组的集合 - /// - /// 图元实体 - /// 编组集合 - public static IEnumerable GetGroups(this Entity ent) + + /// + /// 按选择条件获取编组集合 + /// + /// 选择条件,过滤函数 + /// g.NumEntities < 2);]]> + /// 编组集合 + public static IEnumerable GetGroups(this DBDictionary dict, Func func) + { + return dict.GetAllObjects() + .Where(func); + } + + /// + /// 返回实体的所在编组的集合 + /// + /// 图元实体 + /// 编组集合 + public static IEnumerable GetGroups(this Entity ent) + { + return ent.GetPersistentReactorIds() + .Cast() + .Select(id => id.GetObject()) + .OfType(); + } + + /// + /// 移除所有的空组 + /// + /// 被移除编组的名称集合 + public static List RemoveNullGroup(this DBDictionary dict) + { + var groups = dict.GetGroups(g => g.NumEntities < 2); + List names = new(); + foreach (Group g in groups) { - return - ent.GetPersistentReactorIds() - .Cast() - .Select(id => id.GetObject()) - .OfType(); + names.Add(g.Name); + using (g.ForWrite()) + g.Erase(); } + return names; + } - /// - /// 移除所有的空组 - /// - /// 被移除编组的名称集合 - public static List RemoveNullGroup(this DBDictionary dict) + /// + /// 移除所有空组 + /// + /// 过滤条件,过滤要删除的组名的规则函数 + /// RemoveNullGroup(g => g.StartsWith("hah")) + /// 被移除编组的名称集合 + public static List RemoveNullGroup(this DBDictionary dict, Func func) + { + var groups = dict.GetGroups(g => g.NumEntities < 2); + List names = new(); + foreach (Group g in groups) { - var groups = dict.GetGroups(g => g.NumEntities < 2); - List names = new(); - foreach (Group g in groups) + if (func(g.Name)) { names.Add(g.Name); using (g.ForWrite()) - { g.Erase(); - } - } - return names; - } - - /// - /// 移除所有空组 - /// - /// 过滤条件,过滤要删除的组名的规则函数 - /// RemoveNullGroup(g => g.StartsWith("hah")) - /// 被移除编组的名称集合 - public static List RemoveNullGroup(this DBDictionary dict, Func func) - { - var groups = dict.GetGroups(g => g.NumEntities < 2); - List names = new(); - foreach (Group g in groups) - { - if (func(g.Name)) - { - names.Add(g.Name); - using (g.ForWrite()) - { - g.Erase(); - } - } } - return names; } - #endregion + return names; + } + #endregion @@ -404,5 +386,4 @@ public static List RemoveNullGroup(this DBDictionary dict, Func +/// 实体对象扩展类 +/// +public static class DBObjectEx { + #region Xdata扩展 /// - /// 实体对象扩展类 + /// 删除扩展数据 /// - public static class DBObjectEx + /// 对象实例 + /// 应用程序名称 + /// 要删除数据的组码 + public static void RemoveXData(this DBObject obj, string appName, DxfCode dxfCode) { - #region Xdata扩展 - /// - /// 删除扩展数据 - /// - /// 对象实例 - /// 应用程序名称 - /// 要删除数据的组码 - public static void RemoveXData(this DBObject obj, string appName, DxfCode dxfCode) + XDataList data = obj.XData; + var indexlst = new List(); + bool flag = false; + for (int i = 0; i < data.Count; i++) { - XDataList data = obj.XData; - var indexlst = new List(); - bool flag = false; - for (int i = 0; i < data.Count; i++) + if (data[i].TypeCode == (int)DxfCode.ExtendedDataRegAppName && data[i].Value.ToString() == appName) { - if (data[i].TypeCode == (int)DxfCode.ExtendedDataRegAppName && data[i].Value.ToString() == appName) - { - flag = true; - } - if (flag) - { - if (data[i].TypeCode == (int)DxfCode.ExtendedDataRegAppName && data[i].Value.ToString() != appName) - break; - if (data[i].TypeCode == (int)dxfCode) - { - indexlst.Add(i); - } - } + flag = true; } - foreach (var item in indexlst) + if (flag) { - data.RemoveAt(item); + if (data[i].TypeCode == (int)DxfCode.ExtendedDataRegAppName && data[i].Value.ToString() != appName) + break; + if (data[i].TypeCode == (int)dxfCode) + { + indexlst.Add(i); + } } + } + foreach (var item in indexlst) + { + data.RemoveAt(item); + } - using (obj.ForWrite()) - { - obj.XData = data; - } + using (obj.ForWrite()) + { + obj.XData = data; } - /// - /// 修改扩展数据 - /// - /// 对象实例 - /// 应用程序名称 - /// 要修改数据的组码 - /// 新的数据 - public static void ChangeXData(this DBObject obj, string appName, DxfCode dxfCode, object newvalue) + } + /// + /// 修改扩展数据 + /// + /// 对象实例 + /// 应用程序名称 + /// 要修改数据的组码 + /// 新的数据 + public static void ChangeXData(this DBObject obj, string appName, DxfCode dxfCode, object newvalue) + { + XDataList data = obj.XData; + bool flag = false; + for (int i = 0; i < data.Count; i++) { - XDataList data = obj.XData; - bool flag = false; - for (int i = 0; i < data.Count; i++) + if (data[i].TypeCode == (int)DxfCode.ExtendedDataRegAppName && data[i].Value.ToString() == appName) { - if (data[i].TypeCode == (int)DxfCode.ExtendedDataRegAppName && data[i].Value.ToString() == appName) - { - flag = true; - } - if (flag) + flag = true; + } + if (flag) + { + if (data[i].TypeCode == (int)DxfCode.ExtendedDataRegAppName && data[i].Value.ToString() != appName) + break; + if (data[i].TypeCode == (int)dxfCode) { - if (data[i].TypeCode == (int)DxfCode.ExtendedDataRegAppName && data[i].Value.ToString() != appName) - break; - if (data[i].TypeCode == (int)dxfCode) - { - data[i] = new TypedValue((int)dxfCode, newvalue); - } + data[i] = new TypedValue((int)dxfCode, newvalue); } } + } - using (obj.ForWrite()) - { - obj.XData = data; - } + using (obj.ForWrite()) + { + obj.XData = data; } - #endregion + } + #endregion - #region 读写模式切换 + #region 读写模式切换 - /// - /// 实体自动管理读写函数 - /// - /// 实体类型 - /// 实体对象 - /// 操作委托 - public static void ForWrite(this T obj, Action action) where T : DBObject + /// + /// 实体自动管理读写函数 + /// + /// 实体类型 + /// 实体对象 + /// 操作委托 + public static void ForWrite(this T obj, Action action) where T : DBObject + { + var _isNotifyEnabled = obj.IsNotifyEnabled; + var _isWriteEnabled = obj.IsWriteEnabled; + if (_isNotifyEnabled) { - var _isNotifyEnabled = obj.IsNotifyEnabled; - var _isWriteEnabled = obj.IsWriteEnabled; - if (_isNotifyEnabled) - { - obj.UpgradeFromNotify(); - } - else if (!_isWriteEnabled) - { - obj.UpgradeOpen(); - } - action?.Invoke(obj); - if (_isNotifyEnabled) - { - obj.DowngradeToNotify(_isWriteEnabled); - } - else if (!_isWriteEnabled) - { - obj.DowngradeOpen(); - } + obj.UpgradeFromNotify(); } - - /// - /// 打开模式提权 - /// - /// 实体对象 - /// 提权类对象 - public static UpgradeOpenManager ForWrite(this DBObject obj) + else if (!_isWriteEnabled) { - return new UpgradeOpenManager(obj); + obj.UpgradeOpen(); } - - /// - /// 提权类 - /// - public class UpgradeOpenManager : IDisposable + action?.Invoke(obj); + if (_isNotifyEnabled) + { + obj.DowngradeToNotify(_isWriteEnabled); + } + else if (!_isWriteEnabled) { - private readonly DBObject _obj; - private readonly bool _isNotifyEnabled; - private readonly bool _isWriteEnabled; + obj.DowngradeOpen(); + } + } - internal UpgradeOpenManager(DBObject obj) - { - _obj = obj; - _isNotifyEnabled = _obj.IsNotifyEnabled; - _isWriteEnabled = _obj.IsWriteEnabled; - if (_isNotifyEnabled) - _obj.UpgradeFromNotify(); - else if (!_isWriteEnabled) - _obj.UpgradeOpen(); - } + /// + /// 打开模式提权 + /// + /// 实体对象 + /// 提权类对象 + public static UpgradeOpenManager ForWrite(this DBObject obj) + { + return new UpgradeOpenManager(obj); + } - #region IDisposable 成员 + /// + /// 提权类 + /// + public class UpgradeOpenManager : IDisposable + { + private readonly DBObject _obj; + private readonly bool _isNotifyEnabled; + private readonly bool _isWriteEnabled; - /// - /// 注销函数 - /// - public void Dispose() - { - if (_isNotifyEnabled) - _obj.DowngradeToNotify(_isWriteEnabled); - else if (!_isWriteEnabled) - _obj.DowngradeOpen(); - GC.SuppressFinalize(this); - } + internal UpgradeOpenManager(DBObject obj) + { + _obj = obj; + _isNotifyEnabled = _obj.IsNotifyEnabled; + _isWriteEnabled = _obj.IsWriteEnabled; + if (_isNotifyEnabled) + _obj.UpgradeFromNotify(); + else if (!_isWriteEnabled) + _obj.UpgradeOpen(); + } - #endregion IDisposable 成员 + #region IDisposable 成员 + + /// + /// 注销函数 + /// + public void Dispose() + { + if (_isNotifyEnabled) + _obj.DowngradeToNotify(_isWriteEnabled); + else if (!_isWriteEnabled) + _obj.DowngradeOpen(); + GC.SuppressFinalize(this); } - #endregion + + #endregion IDisposable 成员 } + #endregion } diff --git a/src/IFoxCAD.Cad/ExtensionMethod/DatabaseEx.cs b/src/IFoxCAD.Cad/ExtensionMethod/DatabaseEx.cs new file mode 100644 index 0000000..dff7a9e --- /dev/null +++ b/src/IFoxCAD.Cad/ExtensionMethod/DatabaseEx.cs @@ -0,0 +1,23 @@ +namespace IFoxCAD.Cad; + +public static class DatabaseEx +{ + /// + /// 后台开图文字偏移处理 + /// 0x01 此方案利用前台数据库进行处理 + /// 0x02 当关闭所有前台文档时,会出现无时,不能使用(惊惊没有测试过此状态) + /// 0x03 当关闭所有前台文档时,如何发送命令呢?那就是利用跨进程通讯 + /// + /// 后台打开的数据库 + /// 处理后台的任务 + public static void DBTextDeviation(this Database backstageOpenDwg, Action action) + { + var wdb = HostApplicationServices.WorkingDatabase; + if (wdb != null) + { + HostApplicationServices.WorkingDatabase = backstageOpenDwg; + action?.Invoke(); + HostApplicationServices.WorkingDatabase = wdb; + } + } +} \ No newline at end of file diff --git a/src/IFoxCAD.Cad/ExtensionMethod/EditorEx.cs b/src/IFoxCAD.Cad/ExtensionMethod/EditorEx.cs index 9745eb9..47f06f0 100644 --- a/src/IFoxCAD.Cad/ExtensionMethod/EditorEx.cs +++ b/src/IFoxCAD.Cad/ExtensionMethod/EditorEx.cs @@ -1,752 +1,1091 @@ -using Autodesk.AutoCAD.ApplicationServices; -using Autodesk.AutoCAD.DatabaseServices; -using Autodesk.AutoCAD.EditorInput; -using Autodesk.AutoCAD.Geometry; -using Autodesk.AutoCAD.Runtime; -using System; -using System.Collections.Generic; -using System.Runtime.InteropServices; -using System.Linq; - -namespace IFoxCAD.Cad +namespace IFoxCAD.Cad; + +/// +/// 命令行扩展类 +/// +public static class EditorEx { + #region 选择集 /// - /// 命令行扩展类 + /// 选择穿过一个点的对象 /// - public static class EditorEx + /// 命令行对象 + /// 点 + /// 过滤器 + /// 选择集结果类 + public static PromptSelectionResult SelectAtPoint(this Editor editor, Point3d point, SelectionFilter? filter = default) { - #region 选择集 - /// - /// 选择穿过一个点的对象 - /// - /// 命令行对象 - /// 点 - /// 过滤器 - /// 选择集结果类 - public static PromptSelectionResult SelectAtPoint(this Editor editor, Point3d point, SelectionFilter filter = default) - { - return editor.SelectCrossingWindow(point, point, filter); - } + return editor.SelectCrossingWindow(point, point, filter); + } - /// - /// 根据线宽创建图层选择集 - /// - /// 命令行对象 - /// 线宽 - /// 图层选择集 - public static SelectionSet SelectByLineWeight(this Editor editor, LineWeight lineWeight) - { - OpFilter filter = new OpEqual(370, lineWeight); + /// + /// 根据线宽创建图层选择集 + /// + /// 命令行对象 + /// 线宽 + /// 图层选择集 + public static SelectionSet SelectByLineWeight(this Editor editor, LineWeight lineWeight) + { + OpFilter filter = new OpEqual(370, lineWeight); - var lays = - DBTrans.Top.LayerTable - .GetRecords() - .Where(ltr => ltr.LineWeight == lineWeight) - .Select(ltr => ltr.Name) - .ToArray(); + var lays = + DBTrans.Top.LayerTable + .GetRecords() + .Where(ltr => ltr.LineWeight == lineWeight) + .Select(ltr => ltr.Name) + .ToArray(); - if (lays.Length > 0) - { - filter = - new OpOr - { + if (lays.Length > 0) + { + filter = + new OpOr + { filter, new OpAnd { { 8, string.Join(",", lays) }, { 370, LineWeight.ByLayer } } - }; - } + }; + } + + PromptSelectionResult res = editor.SelectAll(filter); + return res.Value; + } - PromptSelectionResult res = editor.SelectAll(filter); - return res.Value; + public static PromptSelectionResult? SSGet(this Editor editor, string? mode = null, SelectionFilter? filter = null, string[]? messages = null, Dictionary? keywords = null) + { + var pso = new PromptSelectionOptions(); + PromptSelectionResult? ss = null; + if (mode is not null) + { + mode = mode.ToUpper(); + pso.SinglePickInSpace = mode.Contains(":A"); + pso.RejectObjectsFromNonCurrentSpace = mode.Contains(":C"); + pso.AllowDuplicates = mode.Contains(":D"); + pso.SelectEverythingInAperture = mode.Contains(":E"); + pso.RejectObjectsOnLockedLayers = mode.Contains(":L"); + pso.PrepareOptionalDetails = mode.Contains(":N"); + pso.SingleOnly = mode.Contains(":S"); + pso.RejectPaperspaceViewport = mode.Contains(":V"); + pso.AllowSubSelections = mode.Contains("-A"); + pso.ForceSubSelections = mode.Contains("-F"); + + } + if (messages is not null) + { + pso.MessageForAdding = messages[0]; + pso.MessageForRemoval = messages[1]; } - #endregion - #region Info + if (keywords is not null) + { + foreach (var keyword in keywords.Keys) + pso.Keywords.Add(keyword); + if (pso.MessageForRemoval is null) + pso.MessageForAdding = "选择对象"; + pso.MessageForAdding += $"[{string.Join(" / ", keywords.Keys.ToArray())}]"; + pso.KeywordInput += (s, e) => { + if (keywords.ContainsKey(e.Input)) + keywords[e.Input].Invoke(); + }; - /// - /// 带错误提示对话框的打印信息函数 - /// - /// 带格式项的字符串 - /// 指定格式化的对象数组 - public static void StreamMessage(string format, params object[] args) + } + try { - StreamMessage(string.Format(format, args)); - }// + if (filter is not null) + ss = editor.GetSelection(pso, filter); + else + ss = editor.GetSelection(pso); + } + catch (Exception e) + { + editor.WriteMessage($"\nKey is {e.Message}"); + } + return ss; + } + - /// - /// 带错误提示对话框的打印信息函数 - /// - /// 打印信息 - public static void StreamMessage(string message) + /* + * //定义选择集选项 + * var pso = new PromptSelectionOptions + * { + * AllowDuplicates = false, //重复选择 + * }; + * + * //getai遍历全图选择块有用到 + * var dic = new Dictionary() { + * { "Z,全部同名", ()=> { + * getai = BlockHelper.EnumAttIdentical.AllBlockName; + * SendEsc.Esc(); + * }}, + * { "X,动态块显示", ()=> { + * getai = BlockHelper.EnumAttIdentical.Display; + * }}, + * { "V,属性值-默认", ()=> { + * getai = BlockHelper.EnumAttIdentical.DisplayAndTagText; + * }}, + * //允许以下操作,相同的会加入前面的 + * //{ "V,属性值-默认|X,啊啊啊啊", ()=> { + * + * //}}, + * }; + * pso.SsgetAddKeys(dic); + * + * //创建选择集过滤器,只选择块对象 + * var filList = new TypedValue[] { new TypedValue((int)DxfCode.Start, "INSERT") }; + * var filter = new SelectionFilter(filList); + * ssPsr = ed.GetSelection(pso, filter); + */ + + /// + /// 添加选择集关键字和回调 + /// + /// 选择集配置 + /// 关键字,回调委托 + /// + public static void SsgetAddKeys(this PromptSelectionOptions pso, + Dictionary dicActions) + { + Dictionary tmp = new(); + // 后缀名的|号切割,移除掉,组合成新的加入tmp + for (int i = dicActions.Count - 1; i >= 0; i--) { - try + var pair = dicActions.ElementAt(i); + var key = pair.Key; + var keySp = key.Split('|'); + if (keySp.Length < 2) + continue; + + for (int j = 0; j < keySp.Length; j++) { - if (HasEditor()) - WriteMessage(message); + var item = keySp[j]; + // 防止多个后缀通过|符越过词典约束同名 + // 后缀(key)含有,而且Action(value)不同,就把Action(value)累加到后面. + if (dicActions.ContainsKey(item)) + { + if (dicActions[item] != dicActions[key]) + dicActions[item] += dicActions[key]; + } else - InfoMessageBox(message); + tmp.Add(item, dicActions[key]); } - catch (System.Exception ex) - { - Message(ex); - } - }// + dicActions.Remove(key); + } - /// - /// 异常信息对话框 - /// - /// 异常 - public static void Message(System.Exception ex) - { - try - { - System.Windows.Forms.MessageBox.Show( - ex.ToString(), - "Error", - System.Windows.Forms.MessageBoxButtons.OK, - System.Windows.Forms.MessageBoxIcon.Error); - } - catch - { - } - }// + foreach (var item in tmp) + dicActions.Add(item.Key, item.Value); - /// - /// 提示信息对话框 - /// - /// 对话框的标题 - /// 对话框文本 - public static void InfoMessageBox(string caption, string message) + //去除关键字重复的,把重复的执行动作移动到前面 + for (int i = 0; i < dicActions.Count; i++) { - try - { - System.Windows.Forms.MessageBox.Show( - message, - caption, - System.Windows.Forms.MessageBoxButtons.OK, - System.Windows.Forms.MessageBoxIcon.Information); - } - catch (System.Exception ex) + var pair1 = dicActions.ElementAt(i); + var key1 = pair1.Key; + + for (int j = dicActions.Count - 1; j > i; j--) { - Message(ex); + var pair2 = dicActions.ElementAt(j); + var key2 = pair2.Key; + + if (key1.Split(',')[0] == key2.Split(',')[0]) + { + if (dicActions[key1] != dicActions[key2]) + dicActions[key1] += dicActions[key2]; + dicActions.Remove(key2); + } } - }// + } - /// - /// 提示信息对话框 - /// - /// 对话框的标题 - /// 带格式化项的对话框文本 - /// 指定格式化的对象数组 - public static void InfoMessageBox(string caption, string format, params object[] args) + foreach (var item in dicActions) { - InfoMessageBox(caption, string.Format(format, args)); + var keySplitS = item.Key.Split(new string[] { ",", "|" }, StringSplitOptions.RemoveEmptyEntries); + for (int i = 0; i < keySplitS.Length; i += 2) + pso.Keywords.Add(keySplitS[i], keySplitS[i], + keySplitS[i + 1] + "(" + keySplitS[i] + ")"); } - /// - /// 提示信息对话框,默认标题为NFox.Cad - /// - /// 对话框文本 - public static void InfoMessageBox(string message) - { - InfoMessageBox("NFox.Cad", message); - }// + //回调的时候我想用Dict的O(1)索引, + //但是此函数内进行new Dictionary() 在函数栈释放的时候,它被释放掉了. + //因此 dicActions 参数的生命周期 + tmp = new(dicActions); + dicActions.Clear(); + foreach (var item in tmp) + dicActions.Add(item.Key.Split(',')[0], item.Value); + + var keyWords = pso.Keywords; + //从选择集命令中显示关键字 + pso.MessageForAdding = keyWords.GetDisplayString(true); + //关键字回调事件 ssget关键字 + pso.KeywordInput += (sender, e) => { + dicActions[e.Input].Invoke(); + }; + } + - /// - /// 提示信息对话框 - /// - /// 带格式化项的对话框文本 - /// 指定格式化的对象数组 - public static void InfoMessageBox(string format, params object[] args) - { - InfoMessageBox(string.Format(format, args)); - }// - /// - /// 命令行打印字符串 - /// - /// 字符串 - public static void WriteMessage(string message) - { - try - { - if (Acceptable()) - Application.DocumentManager.MdiActiveDocument.Editor.WriteMessage("\n" + message); - else - return; - } - catch (System.Exception ex) - { - Message(ex); - } - }// - /// - /// 命令行打印字符串 - /// - /// 带格式化项的文本 - /// 指定格式化的对象数组 - public static void WriteMessage(string format, params object[] args) + + + //#region 即时选择样板 + + ///// + ///// 即时选择,框选更新关键字 + ///// + //public static void SelectTest() + //{ + // Env.Editor.WriteMessage("\n[白嫖工具]--测试"); + // //激活选中事件 + // Env.Editor.SelectionAdded += SelectTest_SelectionAdded; + // //初始化坐标系 + // Env.Editor.CurrentUserCoordinateSystem = Matrix3d.Identity; + + // //创建过滤器 + // var sf = new OpEqual(0, "arc"); + // var pso = new PromptSelectionOptions + // { + // MessageForAdding = "\n请选择对象:" + // }; + + // pso.Keywords.Add("Z"); + // pso.Keywords.Add("X"); + // pso.Keywords.Add("Q"); + // //注册关键字 + // pso.KeywordInput += SelectTest_KeywordInput; + // try + // { + // //用户选择 + // var psr = Env.Editor.GetSelection(pso, sf); + // //处理代码 + + + // } + // catch (Exception ex)//捕获关键字 + // { + // if (ex.Message == "XuError") + // { + // //关闭关键字事件 + // pso.KeywordInput -= SelectTest_KeywordInput; + // //关闭选中事件 + // Env.Editor.SelectionAdded -= SelectTest_SelectionAdded; + // //重新调用自身 + // ZengLiangYuanJiao(); + // } + // } + // //关闭关键字事件 + // pso.KeywordInput -= SelectTest_KeywordInput; + // //关闭选中事件 + // Env.Editor.SelectionAdded -= SelectTest_SelectionAdded; + //} + + ///// + ///// 即时选择 + ///// + ///// + ///// + //private static void SelectTest_SelectionAdded(object sender, SelectionAddedEventArgs e) + //{ + // //关闭选中事件 + // Env.Editor.SelectionAdded -= SelectTest_SelectionAdded; + // using (var tr = new DBTrans()) + // { + // //处理代码 + // for (int i = 0; i < e.AddedObjects.Count; i++) + // { + + + // //处理完移除已处理的对象 + // e.Remove(i); + // } + // } + // //激活选中事件 + // Env.Editor.SelectionAdded += SelectTest_SelectionAdded; + //} + + ///// + ///// 关键字响应 + ///// + ///// + ///// + //private static void SelectTest_KeywordInput(object sender, SelectionTextInputEventArgs e) + //{ + // //获取关键字 + // switch (e.Input) + // { + // case "Z": + // { + // break; + // } + // case "X": + // { + // break; + // } + + // case "Q": + // { + // break; + // } + // } + // //抛出异常,用于更新提示信息 + // throw new ArgumentException("XuError"); + //} + + + //#endregion + #endregion + + #region Info + + /// + /// 带错误提示对话框的打印信息函数 + /// + /// 带格式项的字符串 + /// 指定格式化的对象数组 + public static void StreamMessage(string format, params object[] args) + { + StreamMessage(string.Format(format, args)); + } + + /// + /// 带错误提示对话框的打印信息函数 + /// + /// 打印信息 + public static void StreamMessage(string message) + { + try + { + if (HasEditor()) + WriteMessage(message); + else + InfoMessageBox(message); + } + catch (System.Exception ex) { - WriteMessage(string.Format(format, args)); + Message(ex); } + } - /// - /// 判断是否有活动的编辑器对象 - /// - /// 有,没有 - public static bool HasEditor() + /// + /// 异常信息对话框 + /// + /// 异常 + public static void Message(System.Exception ex) + { + try + { + System.Windows.Forms.MessageBox.Show( + ex.ToString(), + "Error", + System.Windows.Forms.MessageBoxButtons.OK, + System.Windows.Forms.MessageBoxIcon.Error); + } + catch { - return Application.DocumentManager.MdiActiveDocument is not null - && Application.DocumentManager.Count != 0 - && Application.DocumentManager.MdiActiveDocument.Editor is not null; - }// + } + } - /// - /// 判断是否可以打印字符串 - /// - /// 可以打印,不可以打印 - public static bool Acceptable() + /// + /// 提示信息对话框 + /// + /// 对话框的标题 + /// 对话框文本 + public static void InfoMessageBox(string caption, string message) + { + try { - return HasEditor() - && !Application.DocumentManager.MdiActiveDocument.Editor.IsDragging; - }// + System.Windows.Forms.MessageBox.Show( + message, + caption, + System.Windows.Forms.MessageBoxButtons.OK, + System.Windows.Forms.MessageBoxIcon.Information); + } + catch (System.Exception ex) + { + Message(ex); + } + } + + /// + /// 提示信息对话框 + /// + /// 对话框的标题 + /// 带格式化项的对话框文本 + /// 指定格式化的对象数组 + public static void InfoMessageBox(string caption, string format, params object[] args) + { + InfoMessageBox(caption, string.Format(format, args)); + } - #endregion Info + /// + /// 提示信息对话框,默认标题为NFox.Cad + /// + /// 对话框文本 + public static void InfoMessageBox(string message) + { + InfoMessageBox("NFox.Cad", message); + } - #region 画矢量线 + /// + /// 提示信息对话框 + /// + /// 带格式化项的对话框文本 + /// 指定格式化的对象数组 + public static void InfoMessageBox(string format, params object[] args) + { + InfoMessageBox(string.Format(format, args)); + } - /// - /// 根据点表返回矢量线的列表 - /// - /// 点表 - /// 是否闭合, 为闭合, 为不闭合 - /// - public static List GetLines(IEnumerable pnts, bool isClosed) + /// + /// 命令行打印字符串 + /// + /// 字符串 + public static void WriteMessage(string message) + { + try { + if (Acceptable()) + Application.DocumentManager.MdiActiveDocument.Editor.WriteMessage("\n" + message); + else + return; + } + catch (System.Exception ex) + { + Message(ex); + } + } - var itor = pnts.GetEnumerator(); - if (!itor.MoveNext()) - return new List(); + /// + /// 命令行打印字符串 + /// + /// 带格式化项的文本 + /// 指定格式化的对象数组 + public static void WriteMessage(string format, params object[] args) + { + WriteMessage(string.Format(format, args)); + } - List values = new(); + /// + /// 判断是否有活动的编辑器对象 + /// + /// 有,没有 + public static bool HasEditor() + { + return Application.DocumentManager.MdiActiveDocument is not null + && Application.DocumentManager.Count != 0 + && Application.DocumentManager.MdiActiveDocument.Editor is not null; + } - TypedValue tvFirst = new((int)LispDataType.Point2d, itor.Current); - TypedValue tv1; - TypedValue tv2 = tvFirst; + /// + /// 判断是否可以打印字符串 + /// + /// 可以打印,不可以打印 + public static bool Acceptable() + { + return HasEditor() + && !Application.DocumentManager.MdiActiveDocument.Editor.IsDragging; + } - while (itor.MoveNext()) - { - tv1 = tv2; - tv2 = new TypedValue((int)LispDataType.Point2d, itor.Current); - values.Add(tv1); - values.Add(tv2); - } + #endregion Info - if (isClosed) - { - values.Add(tv2); - values.Add(tvFirst); - } + #region 画矢量线 - return values; - } + /// + /// 根据点表返回矢量线的列表 + /// + /// 点表 + /// 是否闭合, 为闭合, 为不闭合 + /// + public static List GetLines(IEnumerable pnts, bool isClosed) + { - /// - /// 画矢量线 - /// - /// 编辑器对象 - /// 点表 - /// 颜色码 - /// 是否闭合, 为闭合, 为不闭合 - public static void DrawVectors(this Editor editor, IEnumerable pnts, short colorIndex, bool isClosed) - { - var rlst = - new LispList { { LispDataType.Int16, colorIndex } }; - rlst.AddRange(GetLines(pnts, isClosed)); - editor.DrawVectors(rlst, editor.CurrentUserCoordinateSystem); - } + var itor = pnts.GetEnumerator(); + if (!itor.MoveNext()) + return new List(); - /// - /// 画矢量线 - /// - /// 编辑器对象 - /// 点表 - /// 颜色码 - public static void DrawVectors(this Editor editor, IEnumerable pnts, short colorIndex) + List values = new(); + + TypedValue tvFirst = new((int)LispDataType.Point2d, itor.Current); + TypedValue tv1; + TypedValue tv2 = tvFirst; + + while (itor.MoveNext()) { - editor.DrawVectors(pnts, colorIndex, false); + tv1 = tv2; + tv2 = new TypedValue((int)LispDataType.Point2d, itor.Current); + values.Add(tv1); + values.Add(tv2); } - /// - /// 用矢量线画近似圆(正多边形) - /// - /// 编辑器对象 - /// 点表 - /// 颜色码 - /// 半径 - /// 多边形边的个数 - public static void DrawCircles(this Editor editor, IEnumerable pnts, short colorIndex, double radius, int numEdges) + if (isClosed) { - var rlst = - new LispList { { LispDataType.Int16, colorIndex } }; + values.Add(tv2); + values.Add(tvFirst); + } - foreach (Point2d pnt in pnts) - { - Vector2d vec = Vector2d.XAxis * radius; - double angle = Math.PI * 2 / numEdges; + return values; + } - List tpnts = new() - { - pnt + vec - }; - for (int i = 1; i < numEdges; i++) - { - tpnts.Add(pnt + vec.RotateBy(angle * i)); - } + /// + /// 画矢量线 + /// + /// 编辑器对象 + /// 点表 + /// 颜色码 + /// 是否闭合, 为闭合, 为不闭合 + public static void DrawVectors(this Editor editor, IEnumerable pnts, short colorIndex, bool isClosed) + { + var rlst = + new LispList { { LispDataType.Int16, colorIndex } }; + rlst.AddRange(GetLines(pnts, isClosed)); + editor.DrawVectors(rlst, editor.CurrentUserCoordinateSystem); + } - rlst.AddRange(GetLines(tpnts, true)); - } - editor.DrawVectors(rlst, editor.CurrentUserCoordinateSystem); - } + /// + /// 画矢量线 + /// + /// 编辑器对象 + /// 点表 + /// 颜色码 + public static void DrawVectors(this Editor editor, IEnumerable pnts, short colorIndex) + { + editor.DrawVectors(pnts, colorIndex, false); + } - /// - /// 用矢量线画近似圆(正多边形) - /// - /// 编辑器对象 - /// 点 - /// 颜色码 - /// 半径 - /// 多边形边的个数 - public static void DrawCircle(this Editor editor, Point2d pnt, short colorIndex, double radius, int numEdges) + /// + /// 用矢量线画近似圆(正多边形) + /// + /// 编辑器对象 + /// 点表 + /// 颜色码 + /// 半径 + /// 多边形边的个数 + public static void DrawCircles(this Editor editor, IEnumerable pnts, short colorIndex, double radius, int numEdges) + { + var rlst = + new LispList { { LispDataType.Int16, colorIndex } }; + + foreach (Point2d pnt in pnts) { Vector2d vec = Vector2d.XAxis * radius; double angle = Math.PI * 2 / numEdges; - List pnts = new() + List tpnts = new() { pnt + vec }; for (int i = 1; i < numEdges; i++) { - pnts.Add(pnt + vec.RotateBy(angle * i)); + tpnts.Add(pnt + vec.RotateBy(angle * i)); } - editor.DrawVectors(pnts, colorIndex, true); + rlst.AddRange(GetLines(tpnts, true)); } + editor.DrawVectors(rlst, editor.CurrentUserCoordinateSystem); + } - #endregion - - #region 矩阵 + /// + /// 用矢量线画近似圆(正多边形) + /// + /// 编辑器对象 + /// 点 + /// 颜色码 + /// 半径 + /// 多边形边的个数 + public static void DrawCircle(this Editor editor, Point2d pnt, short colorIndex, double radius, int numEdges) + { + Vector2d vec = Vector2d.XAxis * radius; + double angle = Math.PI * 2 / numEdges; - /// - /// 获取UCS到WCS的矩阵 - /// - /// 命令行对象 - /// 变换矩阵 - public static Matrix3d GetMatrixFromUcsToWcs(this Editor editor) + List pnts = new() { - return editor.CurrentUserCoordinateSystem; - } - - /// - /// 获取WCS到UCS的矩阵 - /// - /// 命令行对象 - /// 变换矩阵 - public static Matrix3d GetMatrixFromWcsToUcs(this Editor editor) + pnt + vec + }; + for (int i = 1; i < numEdges; i++) { - return editor.CurrentUserCoordinateSystem.Inverse(); + pnts.Add(pnt + vec.RotateBy(angle * i)); } - /// - /// 获取MDCS(模型空间)到WCS的矩阵 - /// - /// 命令行对象 - /// 变换矩阵 - public static Matrix3d GetMatrixFromMDcsToWcs(this Editor editor) - { - Matrix3d mat; - using ViewTableRecord vtr = editor.GetCurrentView(); - mat = Matrix3d.PlaneToWorld(vtr.ViewDirection); - mat = Matrix3d.Displacement(vtr.Target - Point3d.Origin) * mat; - return Matrix3d.Rotation(-vtr.ViewTwist, vtr.ViewDirection, vtr.Target) * mat; - } + editor.DrawVectors(pnts, colorIndex, true); + } - /// - /// 获取WCS到MDCS(模型空间)的矩阵 - /// - /// 命令行对象 - /// 变换矩阵 - public static Matrix3d GetMatrixFromWcsToMDcs(this Editor editor) - { - return editor.GetMatrixFromMDcsToWcs().Inverse(); - } + #endregion - /// - /// 获取MDCS(模型空间)到PDCS(图纸空间)的矩阵 - /// - /// 命令行对象 - /// 变换矩阵 - public static Matrix3d GetMatrixFromMDcsToPDcs(this Editor editor) - { - if ((short)Env.GetVar("TILEMODE") == 1) - throw new Autodesk.AutoCAD.Runtime.Exception(ErrorStatus.InvalidInput, "Espace papier uniquement"); + #region 矩阵 + + /// + /// 获取UCS到WCS的矩阵 + /// + /// 命令行对象 + /// 变换矩阵 + public static Matrix3d GetMatrixFromUcsToWcs(this Editor editor) + { + return editor.CurrentUserCoordinateSystem; + } + + /// + /// 获取WCS到UCS的矩阵 + /// + /// 命令行对象 + /// 变换矩阵 + public static Matrix3d GetMatrixFromWcsToUcs(this Editor editor) + { + return editor.CurrentUserCoordinateSystem.Inverse(); + } + + /// + /// 获取MDCS(模型空间)到WCS的矩阵 + /// + /// 命令行对象 + /// 变换矩阵 + public static Matrix3d GetMatrixFromMDcsToWcs(this Editor editor) + { + Matrix3d mat; + using ViewTableRecord vtr = editor.GetCurrentView(); + mat = Matrix3d.PlaneToWorld(vtr.ViewDirection); + mat = Matrix3d.Displacement(vtr.Target - Point3d.Origin) * mat; + return Matrix3d.Rotation(-vtr.ViewTwist, vtr.ViewDirection, vtr.Target) * mat; + } + + /// + /// 获取WCS到MDCS(模型空间)的矩阵 + /// + /// 命令行对象 + /// 变换矩阵 + public static Matrix3d GetMatrixFromWcsToMDcs(this Editor editor) + { + return editor.GetMatrixFromMDcsToWcs().Inverse(); + } + + /// + /// 获取MDCS(模型空间)到PDCS(图纸空间)的矩阵 + /// + /// 命令行对象 + /// 变换矩阵 + public static Matrix3d GetMatrixFromMDcsToPDcs(this Editor editor) + { + if ((short)Env.GetVar("TILEMODE") == 1) + throw new ArgumentException("TILEMODE == 1..Espace papier uniquement"); - Database db = editor.Document.Database; - Matrix3d mat; - using (Transaction tr = db.TransactionManager.StartTransaction()) + Database db = editor.Document.Database; + Matrix3d mat; + using (Transaction tr = db.TransactionManager.StartTransaction()) + { + var vp = tr.GetObject(editor.CurrentViewportObjectId, OpenMode.ForRead) as Viewport; + if (vp?.Number == 1) { - Viewport vp = tr.GetObject(editor.CurrentViewportObjectId, OpenMode.ForRead) as Viewport; - if (vp.Number == 1) + try + { + editor.SwitchToModelSpace(); + vp = tr.GetObject(editor.CurrentViewportObjectId, OpenMode.ForRead) as Viewport; + editor.SwitchToPaperSpace(); + } + catch { - try - { - editor.SwitchToModelSpace(); - vp = tr.GetObject(editor.CurrentViewportObjectId, OpenMode.ForRead) as Viewport; - editor.SwitchToPaperSpace(); - } - catch - { - throw new Autodesk.AutoCAD.Runtime.Exception(ErrorStatus.InvalidInput, "Aucun fenêtre active"); - } + throw new Exception("Aucun fenêtre active...ErrorStatus.InvalidInput"); } - Point3d vCtr = new(vp.ViewCenter.X, vp.ViewCenter.Y, 0.0); - mat = Matrix3d.Displacement(vCtr.GetAsVector().Negate()); - mat = Matrix3d.Displacement(vp.CenterPoint.GetAsVector()) * mat; - mat = Matrix3d.Scaling(vp.CustomScale, vp.CenterPoint) * mat; - tr.Commit(); } - return mat; + Point3d vCtr = new(vp!.ViewCenter.X, vp.ViewCenter.Y, 0.0); + mat = Matrix3d.Displacement(vCtr.GetAsVector().Negate()); + mat = Matrix3d.Displacement(vp.CenterPoint.GetAsVector()) * mat; + mat = Matrix3d.Scaling(vp.CustomScale, vp.CenterPoint) * mat; + tr.Commit(); } + return mat; + } + + /// + /// 获取PDCS(图纸空间)到MDCS(模型空间)的矩阵 + /// + /// 命令行对象 + /// 变换矩阵 + public static Matrix3d GetMatrixFromPDcsToMDcs(this Editor editor) + { + return editor.GetMatrixFromMDcsToPDcs().Inverse(); + } - /// - /// 获取PDCS(图纸空间)到MDCS(模型空间)的矩阵 - /// - /// 命令行对象 - /// 变换矩阵 - public static Matrix3d GetMatrixFromPDcsToMDcs(this Editor editor) + /// + /// 获取变换矩阵 + /// + /// 命令行对象 + /// 源坐标系 + /// 目标坐标系 + /// 变换矩阵 + public static Matrix3d GetMatrix(this Editor editor, CoordinateSystemCode from, CoordinateSystemCode to) + { +#if ac2009 + switch (from) { - return editor.GetMatrixFromMDcsToPDcs().Inverse(); - } + case CoordinateSystemCode.Wcs: + switch (to) + { + case CoordinateSystemCode.Ucs: + return editor.GetMatrixFromWcsToUcs(); + + case CoordinateSystemCode.MDcs: + return editor.GetMatrixFromMDcsToWcs(); + + case CoordinateSystemCode.PDcs: + throw new Exception("To be used only with DCS...ErrorStatus.InvalidInput"); + } + break; + case CoordinateSystemCode.Ucs: + switch (to) + { + case CoordinateSystemCode.Wcs: + return editor.GetMatrixFromUcsToWcs(); + + case CoordinateSystemCode.MDcs: + return editor.GetMatrixFromUcsToWcs() * editor.GetMatrixFromWcsToMDcs(); - /// - /// 获取变换矩阵 - /// - /// 命令行对象 - /// 源坐标系 - /// 目标坐标系 - /// 变换矩阵 - public static Matrix3d GetMatrix(this Editor editor, CoordinateSystemCode from, CoordinateSystemCode to) + case CoordinateSystemCode.PDcs: + throw new Exception("To be used only with DCS... ErrorStatus.InvalidInput"); + } + break; + case CoordinateSystemCode.MDcs: + switch (to) + { + case CoordinateSystemCode.Wcs: + return editor.GetMatrixFromMDcsToWcs(); + + case CoordinateSystemCode.Ucs: + return editor.GetMatrixFromMDcsToWcs() * editor.GetMatrixFromWcsToUcs(); + + case CoordinateSystemCode.PDcs: + return editor.GetMatrixFromMDcsToPDcs(); + } + break; + case CoordinateSystemCode.PDcs: + switch (to) + { + case CoordinateSystemCode.Wcs: + throw new Exception("To be used only with DCS... ErrorStatus.InvalidInput"); + case CoordinateSystemCode.Ucs: + throw new Exception("To be used only with DCS... ErrorStatus.InvalidInput"); + case CoordinateSystemCode.MDcs: + return editor.GetMatrixFromPDcsToMDcs(); + } + break; + } + return Matrix3d.Identity; +#else + return (from, to) switch { -#if ac2009 - switch (from) - { - case CoordinateSystemCode.Wcs: - switch (to) - { - case CoordinateSystemCode.Ucs: - return editor.GetMatrixFromWcsToUcs(); - - case CoordinateSystemCode.MDcs: - return editor.GetMatrixFromMDcsToWcs(); - - case CoordinateSystemCode.PDcs: - throw new Autodesk.AutoCAD.Runtime.Exception( - ErrorStatus.InvalidInput, - "To be used only with DCS"); - } - break; - case CoordinateSystemCode.Ucs: - switch (to) - { - case CoordinateSystemCode.Wcs: - return editor.GetMatrixFromUcsToWcs(); - - case CoordinateSystemCode.MDcs: - return editor.GetMatrixFromUcsToWcs() * editor.GetMatrixFromWcsToMDcs(); - - case CoordinateSystemCode.PDcs: - throw new Autodesk.AutoCAD.Runtime.Exception( - ErrorStatus.InvalidInput, - "To be used only with DCS"); - } - break; - case CoordinateSystemCode.MDcs: - switch (to) - { - case CoordinateSystemCode.Wcs: - return editor.GetMatrixFromMDcsToWcs(); - - case CoordinateSystemCode.Ucs: - return editor.GetMatrixFromMDcsToWcs() * editor.GetMatrixFromWcsToUcs(); - - case CoordinateSystemCode.PDcs: - return editor.GetMatrixFromMDcsToPDcs(); - } - break; - case CoordinateSystemCode.PDcs: - switch (to) - { - case CoordinateSystemCode.Wcs: - throw new Autodesk.AutoCAD.Runtime.Exception( - ErrorStatus.InvalidInput, - "To be used only with DCS"); - case CoordinateSystemCode.Ucs: - throw new Autodesk.AutoCAD.Runtime.Exception( - ErrorStatus.InvalidInput, - "To be used only with DCS"); - case CoordinateSystemCode.MDcs: - return editor.GetMatrixFromPDcsToMDcs(); - } - break; - } - return Matrix3d.Identity; -#elif ac2013 - return (from, to) switch - { - (CoordinateSystemCode.Wcs, CoordinateSystemCode.Ucs) => editor.GetMatrixFromWcsToUcs(), - (CoordinateSystemCode.Wcs, CoordinateSystemCode.MDcs) => editor.GetMatrixFromMDcsToWcs(), - (CoordinateSystemCode.Ucs, CoordinateSystemCode.Wcs) => editor.GetMatrixFromUcsToWcs(), - (CoordinateSystemCode.Ucs, CoordinateSystemCode.MDcs) => editor.GetMatrixFromUcsToWcs() * editor.GetMatrixFromWcsToMDcs(), - (CoordinateSystemCode.MDcs, CoordinateSystemCode.Wcs) => editor.GetMatrixFromMDcsToWcs(), - (CoordinateSystemCode.MDcs, CoordinateSystemCode.Ucs) => editor.GetMatrixFromMDcsToWcs() * editor.GetMatrixFromWcsToUcs(), - (CoordinateSystemCode.MDcs, CoordinateSystemCode.PDcs) => editor.GetMatrixFromMDcsToPDcs(), - (CoordinateSystemCode.PDcs, CoordinateSystemCode.MDcs) => editor.GetMatrixFromPDcsToMDcs(), - (CoordinateSystemCode.PDcs, CoordinateSystemCode.Wcs or CoordinateSystemCode.Ucs) - or (CoordinateSystemCode.Wcs or CoordinateSystemCode.Ucs, CoordinateSystemCode.PDcs) => throw new Autodesk.AutoCAD.Runtime.Exception(ErrorStatus.InvalidInput,"To be used only with DCS"), - (_, _) => Matrix3d.Identity - }; + (CoordinateSystemCode.Wcs, CoordinateSystemCode.Ucs) => editor.GetMatrixFromWcsToUcs(), + (CoordinateSystemCode.Wcs, CoordinateSystemCode.MDcs) => editor.GetMatrixFromWcsToMDcs(), + (CoordinateSystemCode.Ucs, CoordinateSystemCode.Wcs) => editor.GetMatrixFromUcsToWcs(), + (CoordinateSystemCode.Ucs, CoordinateSystemCode.MDcs) => editor.GetMatrixFromUcsToWcs() * editor.GetMatrixFromWcsToMDcs(), + (CoordinateSystemCode.MDcs, CoordinateSystemCode.Wcs) => editor.GetMatrixFromMDcsToWcs(), + (CoordinateSystemCode.MDcs, CoordinateSystemCode.Ucs) => editor.GetMatrixFromMDcsToWcs() * editor.GetMatrixFromWcsToUcs(), + (CoordinateSystemCode.MDcs, CoordinateSystemCode.PDcs) => editor.GetMatrixFromMDcsToPDcs(), + (CoordinateSystemCode.PDcs, CoordinateSystemCode.MDcs) => editor.GetMatrixFromPDcsToMDcs(), + (CoordinateSystemCode.PDcs, CoordinateSystemCode.Wcs or CoordinateSystemCode.Ucs) + or (CoordinateSystemCode.Wcs or CoordinateSystemCode.Ucs, CoordinateSystemCode.PDcs) => throw new Exception("To be used only with DCS...ErrorStatus.InvalidInput"), + (_, _) => Matrix3d.Identity + }; #endif - } + } - #endregion + #endregion - #region 缩放 + #region 缩放 - /// - /// 缩放窗口范围 - /// - /// 命令行对象 - /// 窗口左下点 - /// 窗口右上点 - public static void ZoomWindow(this Editor ed, Point3d minPoint, Point3d maxPoint) + /// + /// 缩放窗口范围 + /// + /// 命令行对象 + /// 窗口左下点 + /// 窗口右上点 + public static void ZoomWindow(this Editor ed, Point3d minPoint, Point3d maxPoint) + { + ViewTableRecord cvtr = ed.GetCurrentView(); + ViewTableRecord vtr = new(); + vtr.CopyFrom(cvtr); + + Point3d[] oldpnts = new Point3d[] { minPoint, maxPoint }; + Point3d[] pnts = new Point3d[8]; + Point3d[] dpnts = new Point3d[8]; + for (int i = 0; i < 2; i++) { - ViewTableRecord cvtr = ed.GetCurrentView(); - ViewTableRecord vtr = new(); - vtr.CopyFrom(cvtr); - - Point3d[] oldpnts = new Point3d[] { minPoint, maxPoint }; - Point3d[] pnts = new Point3d[8]; - Point3d[] dpnts = new Point3d[8]; - for (int i = 0; i < 2; i++) + for (int j = 0; j < 2; j++) { - for (int j = 0; j < 2; j++) + for (int k = 0; k < 2; k++) { - for (int k = 0; k < 2; k++) - { - int n = i * 4 + j * 2 + k; - pnts[n] = new Point3d(oldpnts[i][0], oldpnts[j][1], oldpnts[k][2]); - dpnts[n] = pnts[n].TransformBy(ed.GetMatrixFromWcsToMDcs()); - } + int n = i * 4 + j * 2 + k; + pnts[n] = new Point3d(oldpnts[i][0], oldpnts[j][1], oldpnts[k][2]); + dpnts[n] = pnts[n].TransformBy(ed.GetMatrixFromWcsToMDcs()); } } - double xmin, xmax, ymin, ymax; - xmin = xmax = dpnts[0][0]; - ymin = ymax = dpnts[0][1]; - for (int i = 1; i < 8; i++) - { - xmin = Math.Min(xmin, dpnts[i][0]); - xmax = Math.Max(xmax, dpnts[i][0]); - ymin = Math.Min(ymin, dpnts[i][1]); - ymax = Math.Max(ymax, dpnts[i][1]); - } - - vtr.Width = xmax - xmin; - vtr.Height = ymax - ymin; - vtr.CenterPoint = (dpnts[0] + (dpnts[7] - dpnts[0]) / 2).Convert2d(new Plane()); - - ed.SetCurrentView(vtr); - ed.Regen(); } - - /// - /// 缩放窗口范围 - /// - /// 命令行对象 - /// 窗口范围点 - public static void ZoomWindow(this Editor ed, Extents3d ext) + double xmin, xmax, ymin, ymax; + xmin = xmax = dpnts[0][0]; + ymin = ymax = dpnts[0][1]; + for (int i = 1; i < 8; i++) { - ZoomWindow(ed, ext.MinPoint, ext.MaxPoint); + xmin = Math.Min(xmin, dpnts[i][0]); + xmax = Math.Max(xmax, dpnts[i][0]); + ymin = Math.Min(ymin, dpnts[i][1]); + ymax = Math.Max(ymax, dpnts[i][1]); } - /// - /// 缩放比例 - /// - /// 命令行对象 - /// 中心点 - /// 窗口宽 - /// 窗口高 - public static void Zoom(this Editor ed, Point3d CenPt, double width, double height) - { - using ViewTableRecord view = ed.GetCurrentView(); - view.Width = width; - view.Height = height; - view.CenterPoint = new Point2d(CenPt.X, CenPt.Y); - ed.SetCurrentView(view);//更新当前视图 - } + vtr.Width = xmax - xmin; + vtr.Height = ymax - ymin; + vtr.CenterPoint = (dpnts[0] + (dpnts[7] - dpnts[0]) / 2).Convert2d(new Plane()); - /// - ///缩放窗口范围 - /// - /// 命令行对象 - /// 第一点 - /// 对角点 - /// 偏移距离 - public static void ZoomWindow(this Editor ed, Point3d lpt, Point3d rpt, double offsetDist = 0.00) - { - Extents3d extents = new(); - extents.AddPoint(lpt); - extents.AddPoint(rpt); - rpt = extents.MaxPoint + new Vector3d(offsetDist, offsetDist, 0); - lpt = extents.MinPoint - new Vector3d(offsetDist, offsetDist, 0); - Vector3d ver = rpt - lpt; - ed.Zoom(lpt + ver / 2, ver.X, ver.Y); - } + ed.SetCurrentView(vtr); + ed.Regen(); + } - /// - /// 动态缩放 - /// - /// 命令行对象 - /// 偏移距离 - public static void ZoomExtents(this Editor ed, double offsetDist = 0.00) - { - var db = ed.Document.Database; - db.UpdateExt(true); - ed.ZoomWindow(db.Extmax, db.Extmin, offsetDist); - } + /// + /// 缩放窗口范围 + /// + /// 命令行对象 + /// 窗口范围点 + public static void ZoomWindow(this Editor ed, Extents3d ext) + { + ZoomWindow(ed, ext.MinPoint, ext.MaxPoint); + } - /// - /// 根据实体对象的范围显示视图 - /// - /// 命令行对象 - /// Entity对象 - /// 偏移距离 - public static void ZoomObject(this Editor ed, Entity ent, double offsetDist = 0.00) - { - Extents3d ext = ent.GeometricExtents; - ed.ZoomWindow(ext.MinPoint, ext.MaxPoint, offsetDist); - } + /// + /// 缩放比例 + /// + /// 命令行对象 + /// 中心点 + /// 窗口宽 + /// 窗口高 + public static void Zoom(this Editor ed, Point3d CenPt, double width, double height) + { + using ViewTableRecord view = ed.GetCurrentView(); + view.Width = width; + view.Height = height; + view.CenterPoint = new Point2d(CenPt.X, CenPt.Y); + ed.SetCurrentView(view);//更新当前视图 + } + + /// + ///缩放窗口范围 + /// + /// 命令行对象 + /// 第一点 + /// 对角点 + /// 偏移距离 + public static void ZoomWindow(this Editor ed, Point3d lpt, Point3d rpt, double offsetDist = 0.00) + { + Extents3d extents = new(); + extents.AddPoint(lpt); + extents.AddPoint(rpt); + rpt = extents.MaxPoint + new Vector3d(offsetDist, offsetDist, 0); + lpt = extents.MinPoint - new Vector3d(offsetDist, offsetDist, 0); + Vector3d ver = rpt - lpt; + ed.Zoom(lpt + ver / 2, ver.X, ver.Y); + } + + + /// + /// 获取有效的数据库范围 + /// + /// 数据库 + /// 容差值:图元包围盒会超过数据库边界,用此参数扩大边界 + /// + public static Extents3d? GetValidExtents3d(this Database db, double extention = 1e-6) + { + db.UpdateExt(true);//更新当前模型空间的范围 + var ve = new Vector3d(extention, extention, extention); + // 数据库没有图元的时候,min是大,max是小,导致新建出错 + // 数据如下: + // min.X == 1E20 && min.Y == 1E20 && min.Z == 1E20 && + // max.X == -1E20 && max.Y == -1E20 && max.Z == -1E20) + var a = db.Extmin; + var b = db.Extmax; + if (a.X < b.X && a.Y < b.Y) + return new Extents3d(db.Extmin - ve, db.Extmax + ve); + + return null; + } + + /// + /// 动态缩放 + /// + /// 命令行对象 + /// 偏移距离 + public static void ZoomExtents(this Editor ed, double offsetDist = 0.00) + { + Database db = ed.Document.Database; + // db.UpdateExt(true); //GetValidExtents3d内提供了 + var dbExtent = db.GetValidExtents3d(); + if (dbExtent == null) + ed.ZoomWindow(Point3d.Origin, new Point3d(1, 1, 0), offsetDist); + else + ed.ZoomWindow(db.Extmin, db.Extmax, offsetDist); + } + + /// + /// 根据实体对象的范围显示视图 + /// + /// 命令行对象 + /// Entity对象 + /// 偏移距离 + public static void ZoomObject(this Editor ed, Entity ent, double offsetDist = 0.00) + { + Extents3d ext = ent.GeometricExtents; + ed.ZoomWindow(ext.MinPoint, ext.MaxPoint, offsetDist); + } - #endregion + #endregion - #region Get交互类 + #region Get交互类 - /// - /// 获取Point - /// - /// 命令行对象 - /// 提示信息 - /// 提示使用的基点 - /// - public static PromptPointResult GetPoint(this Editor ed, string Message, Point3d BasePoint) + /// + /// 获取Point + /// + /// 命令行对象 + /// 提示信息 + /// 提示使用的基点 + /// + public static PromptPointResult GetPoint(this Editor ed, string Message, Point3d BasePoint) + { + PromptPointOptions ptOp = new(Message) { - PromptPointOptions ptOp = new(Message) - { - BasePoint = BasePoint, - UseBasePoint = true - }; - return ed.GetPoint(ptOp); - } + BasePoint = BasePoint, + UseBasePoint = true + }; + return ed.GetPoint(ptOp); + } - /// - /// 获取double值 - /// - /// 命令行对象 - /// 提示信息 - /// double默认值 - /// - public static PromptDoubleResult GetDouble(this Editor ed, string Message, double DefaultValue = 1.0) + /// + /// 获取double值 + /// + /// 命令行对象 + /// 提示信息 + /// double默认值 + /// + public static PromptDoubleResult GetDouble(this Editor ed, string Message, double DefaultValue = 1.0) + { + PromptDoubleOptions douOp = new(Message) { - PromptDoubleOptions douOp = new(Message) - { - DefaultValue = DefaultValue - }; - return ed.GetDouble(douOp); - } + DefaultValue = DefaultValue + }; + return ed.GetDouble(douOp); + } - /// - /// 获取int值 - /// - /// 命令行对象 - /// 提示信息 - /// double默认值 - /// - public static PromptIntegerResult GetInteger(this Editor ed, string Message, int DefaultValue = 1) + /// + /// 获取int值 + /// + /// 命令行对象 + /// 提示信息 + /// double默认值 + /// + public static PromptIntegerResult GetInteger(this Editor ed, string Message, int DefaultValue = 1) + { + PromptIntegerOptions douOp = new(Message) { - PromptIntegerOptions douOp = new(Message) - { - DefaultValue = DefaultValue - }; - return ed.GetInteger(douOp); - } + DefaultValue = DefaultValue + }; + return ed.GetInteger(douOp); + } - /// - /// 获取string值 - /// - /// 命令行对象 - /// 提示信息 - /// string默认值 - /// - public static PromptResult GetString(this Editor ed, string Message, string DefaultValue = "") + /// + /// 获取string值 + /// + /// 命令行对象 + /// 提示信息 + /// string默认值 + /// + public static PromptResult GetString(this Editor ed, string Message, string DefaultValue = "") + { + PromptStringOptions strOp = new(Message) { - PromptStringOptions strOp = new(Message) - { - DefaultValue = DefaultValue - }; - return ed.GetString(strOp); - } + DefaultValue = DefaultValue + }; + return ed.GetString(strOp); + } - #endregion Get交互类 + #endregion - #region 执行lisp + #region 执行lisp +#if NET35 + [DllImport("acad.exe", CallingConvention = CallingConvention.Cdecl, EntryPoint = "acedInvoke")] +#else + [DllImport("accore.dll", CallingConvention = CallingConvention.Cdecl, EntryPoint = "acedInvoke")] +#endif + static extern int AcedInvoke(IntPtr args, out IntPtr result); -#if ac2009 - [System.Security.SuppressUnmanagedCodeSecurity] - [DllImport("acad.exe", CharSet = CharSet.Unicode, CallingConvention = CallingConvention.Cdecl, EntryPoint = "?acedEvaluateLisp@@YAHPB_WAAPAUresbuf@@@Z")] - private static extern int AcedEvaluateLisp(string lispLine, out IntPtr result); - [DllImport("acad.exe", CallingConvention = CallingConvention.Cdecl, EntryPoint = "acedInvoke")] - private static extern int AcedInvoke(IntPtr args, out IntPtr result); +#if NET35 + [DllImport("acad.exe", CharSet = CharSet.Unicode, CallingConvention = CallingConvention.Cdecl, + EntryPoint = "?acedEvaluateLisp@@YAHPB_WAAPAUresbuf@@@Z")] #else - [System.Security.SuppressUnmanagedCodeSecurity] - [DllImport("accore.dll", CharSet = CharSet.Unicode, CallingConvention = CallingConvention.Cdecl, EntryPoint = "?acedEvaluateLisp@@YAHPEB_WAEAPEAUresbuf@@@Z")] - private static extern int AcedEvaluateLisp(string lispLine, out IntPtr result); + // 高版本此接口不能使用lisp(command "xx"),但是可以直接在自动运行接口上 + [DllImport("accore.dll", CharSet = CharSet.Unicode, CallingConvention = CallingConvention.Cdecl, + EntryPoint = "?acedEvaluateLisp@@YAHPEB_WAEAPEAUresbuf@@@Z")] +#endif + [System.Security.SuppressUnmanagedCodeSecurity]//初始化默认值 + static extern int AcedEvaluateLisp(string lispLine, out IntPtr result); - [DllImport("accore.dll", CallingConvention = CallingConvention.Cdecl, EntryPoint = "acedInvoke")] - private static extern int AcedInvoke(IntPtr args, out IntPtr result); +#if NET35 + [DllImport("acad.exe", CharSet = CharSet.Auto, CallingConvention = CallingConvention.Cdecl, + EntryPoint = "ads_queueexpr")] +#else + [DllImport("accore.dll", CharSet = CharSet.Auto, CallingConvention = CallingConvention.Cdecl, + EntryPoint = "ads_queueexpr")] #endif - /// - /// 发送lisp语句字符串到cad执行 - /// - /// 编辑器对象 - /// lisp语句 - /// 缓冲结果,返回值 - public static ResultBuffer RunLisp(this Editor ed, string arg) - { - AcedEvaluateLisp(arg, out IntPtr rb); - try - { - if (rb != IntPtr.Zero) - return DisposableWrapper.Create(typeof(ResultBuffer), rb, true) as ResultBuffer; - } - catch - { } - return null; + static extern int Ads_queueexpr(string strExpr); + + public enum RunLispFlag : byte + { + AdsQueueexpr = 1, + AcedEvaluateLisp = 2, + SendStringToExecute = 4, + } + + /// + /// 发送lisp语句字符串到cad执行 + /// + /// 编辑器对象 + /// lisp语句 + /// 运行方式 + /// 缓冲结果,返回值 +#pragma warning disable IDE0060 // 删除未使用的参数 + public static ResultBuffer? RunLisp(this Editor ed, + string lispCode, + RunLispFlag flag = RunLispFlag.AdsQueueexpr) +#pragma warning restore IDE0060 // 删除未使用的参数 + { + /* + * 测试命令: + * [CommandMethod("CmdTest_RunLisp")] + * public static void CmdTest_RunLisp() + * { + * var res = SendLisp.RunLisp("(setq abc 10)"); + * } + * 调用方式: + * (command "CmdTest_RunLisp1") + * bug说明: + * AcedEvaluateLisp接口在高版本调用时候没有运行成功,使得 !abc 没有值 + * 经过测试,cad08调用成功,此bug与CommandFlags无关 + * 解决方案: + * 0x01 用异步接口,但是这样是显式调用了: + * (setq thisdrawing (vla-get-activedocument (vlax-get-acad-object)))(vla-SendCommand thisdrawing "CmdTest_RunLisp1 ") + * 0x02 使用 Ads_queueexpr 接口 + */ + if ((flag & RunLispFlag.AdsQueueexpr) == RunLispFlag.AdsQueueexpr) + { + // 这个在08/12发送lisp不会出错,但是发送bo命令出错了. + // 0x01 设置 CommandFlags.Session 可以同步, + // 0x02 自执行发送lisp都是异步,(用来发送 含有(command)的lisp的) + _ = Ads_queueexpr(lispCode + "\n"); + } + if ((flag & RunLispFlag.AcedEvaluateLisp) == RunLispFlag.AcedEvaluateLisp) + { + _ = AcedEvaluateLisp(lispCode, out IntPtr rb); + if (rb != IntPtr.Zero) + return DisposableWrapper.Create(typeof(ResultBuffer), rb, true) as ResultBuffer; + } + if ((flag & RunLispFlag.SendStringToExecute) == RunLispFlag.SendStringToExecute) + { + var dm = Application.DocumentManager; + var doc = dm.MdiActiveDocument; + doc.SendStringToExecute(lispCode + "\n", false, false, false); } - #endregion 执行lisp + return null; } -} + #endregion +} \ No newline at end of file diff --git a/src/IFoxCAD.Cad/ExtensionMethod/EntityEx.cs b/src/IFoxCAD.Cad/ExtensionMethod/EntityEx.cs index c14b566..9f9fc55 100644 --- a/src/IFoxCAD.Cad/ExtensionMethod/EntityEx.cs +++ b/src/IFoxCAD.Cad/ExtensionMethod/EntityEx.cs @@ -1,409 +1,415 @@ -using Autodesk.AutoCAD.DatabaseServices; -using Autodesk.AutoCAD.DatabaseServices.Filters; -using Autodesk.AutoCAD.Geometry; +namespace IFoxCAD.Cad; -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; - -namespace IFoxCAD.Cad +/// +/// 实体图元类扩展函数 +/// +public static class EntityEx { + #region 实体刷新 /// - /// 实体图元类扩展函数 + /// 刷新实体显示 /// - public static class EntityEx + /// 实体对象 + public static void Flush(this Entity entity, DBTrans? trans = null) { - #region 实体刷新 - /// - /// 刷新实体显示 - /// - /// 实体对象 - public static void Flush(this Entity entity, Transaction trans = null) - { - if (entity is null) - { - throw new ArgumentNullException(nameof(entity)); - } - if (trans is null) - { - trans = DBTrans.Top.Transaction; - } - entity.RecordGraphicsModified(true); - trans.TransactionManager.QueueForGraphicsFlush(); - } + //if (entity is null) + //{ + // throw new ArgumentNullException(nameof(entity)); + //} + trans ??= DBTrans.Top; + entity.RecordGraphicsModified(true); + trans.Transaction.TransactionManager.QueueForGraphicsFlush(); + trans.Document?.TransactionManager.FlushGraphics(); + } - /// - /// 刷新实体显示 - /// - /// 实体id - public static void Flush(this ObjectId id) => Flush(DBTrans.Top.GetObject(id)); - #endregion - - #region 多段线端点坐标 - /// - /// 获取二维多段线的端点坐标 - /// - /// 二维多段线 - /// 事务 - /// 端点坐标集合 - public static IEnumerable GetPoints(this Polyline2d pl2d, Transaction tr = null) - { - tr ??= DBTrans.Top.Transaction; - foreach (ObjectId id in pl2d) - { - yield return ((Vertex2d)tr.GetObject(id, OpenMode.ForRead)).Position; - } - } + /// + /// 刷新实体显示 + /// + /// 实体id + public static void Flush(this ObjectId id) => Flush(DBTrans.Top.GetObject(id)!); + #endregion - /// - /// 获取三维多段线的端点坐标 - /// - /// 三维多段线 - /// 事务 - /// 端点坐标集合 - public static IEnumerable GetPoints(this Polyline3d pl3d, Transaction tr = null) + #region 多段线端点坐标 + /// + /// 获取二维多段线的端点坐标 + /// + /// 二维多段线 + /// 事务 + /// 端点坐标集合 + public static IEnumerable GetPoints(this Polyline2d pl2d, DBTrans? tr = null) + { + tr ??= DBTrans.Top; + foreach (ObjectId id in pl2d) { - tr ??= DBTrans.Top.Transaction; - foreach (ObjectId id in pl3d) - { - yield return ((PolylineVertex3d)tr.GetObject(id, OpenMode.ForRead)).Position; - } + yield return tr.GetObject(id)!.Position; } + } - /// - /// 获取多段线的端点坐标 - /// - /// 多段线 - /// 端点坐标集合 - public static IEnumerable GetPoints(this Polyline pl) + /// + /// 获取三维多段线的端点坐标 + /// + /// 三维多段线 + /// 事务 + /// 端点坐标集合 + public static IEnumerable GetPoints(this Polyline3d pl3d, DBTrans? tr = null) + { + tr ??= DBTrans.Top; + foreach (ObjectId id in pl3d) { - return - Enumerable - .Range(0, pl.NumberOfVertices) - .Select(i => pl.GetPoint3dAt(i)); + yield return tr.GetObject(id, OpenMode.ForRead)!.Position; } - #endregion + } - #region TransformBy + /// + /// 获取多段线的端点坐标 + /// + /// 多段线 + /// 端点坐标集合 + public static IEnumerable GetPoints(this Polyline pl) + { + return + Enumerable + .Range(0, pl.NumberOfVertices) + .Select(i => pl.GetPoint3dAt(i)); + } + #endregion - /// - /// 移动实体 - /// - /// 实体 - /// 基点 - /// 目标点 - public static void Move(this Entity ent, Point3d from, Point3d to) - { - ent.TransformBy(Matrix3d.Displacement(to - from)); - } + #region 实体线性变换 - /// - /// 缩放实体 - /// - /// 实体 - /// 缩放基点坐标 - /// 缩放比例 - public static void Scale(this Entity ent, Point3d center, double scaleValue) - { - ent.TransformBy(Matrix3d.Scaling(scaleValue, center)); - } + /// + /// 移动实体 + /// + /// 实体 + /// 基点 + /// 目标点 + public static void Move(this Entity ent, Point3d from, Point3d to) + { + ent.TransformBy(Matrix3d.Displacement(to - from)); + } - /// - /// 旋转实体 - /// - /// 实体 - /// 旋转中心 - /// 转角 - /// 旋转平面的法向矢量 - public static void Rotation(this Entity ent, Point3d center, double angle, Vector3d normal) - { - ent.TransformBy(Matrix3d.Rotation(angle, normal, center)); - } + /// + /// 缩放实体 + /// + /// 实体 + /// 缩放基点坐标 + /// 缩放比例 + public static void Scale(this Entity ent, Point3d center, double scaleValue) + { + ent.TransformBy(Matrix3d.Scaling(scaleValue, center)); + } - /// - /// 在XY平面内旋转实体 - /// - /// 实体 - /// 旋转中心 - /// 转角 - public static void Rotation(this Entity ent, Point3d center, double angle) - { - ent.TransformBy(Matrix3d.Rotation(angle, Vector3d.ZAxis.TransformBy(ent.Ecs), center)); - } + /// + /// 旋转实体 + /// + /// 实体 + /// 旋转中心 + /// 转角,弧度制,正数为顺时针 + /// 旋转平面的法向矢量 + public static void Rotation(this Entity ent, Point3d center, double angle, Vector3d normal) + { + ent.TransformBy(Matrix3d.Rotation(angle, normal, center)); + } - /// - /// 按对称轴镜像实体 - /// - /// 实体 - /// 对称轴起点 - /// 对称轴终点 - public static void Mirror(this Entity ent, Point3d startPoint, Point3d endPoint) - { - ent.TransformBy(Matrix3d.Mirroring(new Line3d(startPoint, endPoint))); - } + /// + /// 在XY平面内旋转实体 + /// + /// 实体 + /// 旋转中心 + /// 转角,弧度制,正数为顺时针 + public static void Rotation(this Entity ent, Point3d center, double angle) + { + ent.TransformBy(Matrix3d.Rotation(angle, Vector3d.ZAxis.TransformBy(ent.Ecs), center)); + } - /// - /// 按对称面镜像实体 - /// - /// 实体 - /// 对称平面 - public static void Mirror(this Entity ent, Plane plane) - { - ent.TransformBy(Matrix3d.Mirroring(plane)); - } + /// + /// 按对称轴镜像实体 + /// + /// 实体 + /// 对称轴起点 + /// 对称轴终点 + public static void Mirror(this Entity ent, Point3d startPoint, Point3d endPoint) + { + ent.TransformBy(Matrix3d.Mirroring(new Line3d(startPoint, endPoint))); + } - /// - /// 按对称点镜像实体 - /// - /// 实体 - /// 对称点 - public static void Mirror(this Entity ent, Point3d basePoint) - { - ent.TransformBy(Matrix3d.Mirroring(basePoint)); - } + /// + /// 按对称面镜像实体 + /// + /// 实体 + /// 对称平面 + public static void Mirror(this Entity ent, Plane plane) + { + ent.TransformBy(Matrix3d.Mirroring(plane)); + } - #endregion + /// + /// 按对称点镜像实体 + /// + /// 实体 + /// 对称点 + public static void Mirror(this Entity ent, Point3d basePoint) + { + ent.TransformBy(Matrix3d.Mirroring(basePoint)); + } + + #endregion - #region 实体范围 - /// - /// 获取实体集合的范围 - /// - /// 实体迭代器 - /// 实体集合的范围 - public static Extents3d GetExtents(this IEnumerable ents) + #region 实体范围 + /// + /// 获取实体集合的范围 + /// + /// 实体迭代器 + /// 实体集合的范围 + public static Extents3d GetExtents(this IEnumerable ents) + { + var ext = new Extents3d(); + foreach (var item in ents) { - var it = ents.GetEnumerator(); - var ext = it.Current.GeometricExtents; - while (it.MoveNext()) - ext.AddExtents(it.Current.GeometricExtents); - return ext; + ext.AddExtents(item.GeometricExtents); } - #endregion + return ext; + } + #endregion - #region 单行文字 + #region 单行文字 - /// - /// 更正单行文字的镜像属性 - /// - /// 单行文字 - public static void ValidateMirror(this DBText txt) - { - if (!txt.Database.Mirrtext) - { - txt.IsMirroredInX = false; - txt.IsMirroredInY = false; - } - } - #endregion - - #region 多行文字 - - /// - /// 炸散多行文字 - /// - /// 存储多行文字炸散之后的对象的类型 - /// 多行文字 - /// 存储对象变量 - /// 回调函数,用于处理炸散之后的对象 - /// MTextFragment -- 多行文字炸散后的对象 - /// MTextFragmentCallbackStatus -- 回调函数处理的结果 - /// - public static void ExplodeFragments(this MText mt, T obj, Func mTextFragmentCallback) + /// + /// 更正单行文字的镜像属性 + /// + /// 单行文字 + public static void ValidateMirror(this DBText txt) + { + if (!txt.Database.Mirrtext) { - mt.ExplodeFragments((f, o) => mTextFragmentCallback(f, (T)o), obj); + txt.IsMirroredInX = false; + txt.IsMirroredInY = false; } + } + #endregion - /// - /// 获取多行文字的无格式文本 - /// - /// 多行文字 - /// 文本 - public static string GetUnFormatString(this MText mt) - { - List strs = new(); - mt.ExplodeFragments( - strs, - (f, o) => { - o.Add(f.Text); - return MTextFragmentCallbackStatus.Continue; - }); - return string.Join("", strs.ToArray()); - } - #endregion - - #region 圆弧 - - /// - /// 根据圆心、起点和终点来创建圆弧(二维) - /// - /// 圆弧对象 - /// 起点 - /// 圆心 - /// 终点 - /// 圆弧 - public static Arc CreateArcSCE(Point3d startPoint, Point3d centerPoint, Point3d endPoint) - { - Arc arc = new(); - arc.Center = centerPoint; - arc.Radius = centerPoint.DistanceTo(startPoint); - Vector2d startVector = new(startPoint.X - centerPoint.X, startPoint.Y - centerPoint.Y); - Vector2d endVector = new(endPoint.X - centerPoint.X, endPoint.Y - centerPoint.Y); - //计算起始和终止角度 - arc.StartAngle = startVector.Angle; - arc.EndAngle = endVector.Angle; - return arc; - } - /// - /// 三点法创建圆弧(二维) - /// - /// 圆弧对象 - /// 起点 - /// 圆弧上的点 - /// 终点 - /// 圆弧 - public static Arc CreateArc(Point3d startPoint, Point3d pointOnArc, Point3d endPoint) - { - //创建一个几何类的圆弧对象 - CircularArc3d geArc = new(startPoint, pointOnArc, endPoint); - //将几何类圆弧对象的圆心和半径赋值给圆弧 -#if ac2009 - return geArc.ToArc(); -#elif ac2013 - return (Arc)Curve.CreateFromGeCurve(geArc); + #region 多行文字 + + /// + /// 炸散多行文字 + /// + /// 存储多行文字炸散之后的对象的类型 + /// 多行文字 + /// 存储对象变量 + /// 回调函数,用于处理炸散之后的对象 + /// 多行文字炸散后的对象 + /// 回调函数处理的结果 + /// + public static void ExplodeFragments(this MText mt, T obj, Func mTextFragmentCallback) + { + mt.ExplodeFragments((f, o) => mTextFragmentCallback(f, (T)o), obj); + } + + /// + /// 获取多行文字的无格式文本 + /// + /// 多行文字 + /// 文本 + public static string GetUnFormatString(this MText mt) + { + List strs = new(); + mt.ExplodeFragments( + strs, + (f, o) => { + o.Add(f.Text); + return MTextFragmentCallbackStatus.Continue; + }); + return string.Join("", strs.ToArray()); + } + #endregion + + #region 圆弧 + + /// + /// 根据圆心、起点、终点来创建圆弧(二维) + /// + /// 起点 + /// 圆心 + /// 终点 + /// 圆弧 + public static Arc CreateArcSCE(Point3d startPoint, Point3d centerPoint, Point3d endPoint) + { + Arc arc = new(); + arc.SetDatabaseDefaults(); + arc.Center = centerPoint; + arc.Radius = centerPoint.DistanceTo(startPoint); + Vector2d startVector = new(startPoint.X - centerPoint.X, startPoint.Y - centerPoint.Y); + Vector2d endVector = new(endPoint.X - centerPoint.X, endPoint.Y - centerPoint.Y); + //计算起始和终止角度 + arc.StartAngle = startVector.Angle; + arc.EndAngle = endVector.Angle; + return arc; + } + /// + /// 三点法创建圆弧(二维) + /// + /// 圆弧对象 + /// 起点 + /// 圆弧上的点 + /// 终点 + /// 圆弧 + public static Arc CreateArc(Point3d startPoint, Point3d pointOnArc, Point3d endPoint) + { + //创建一个几何类的圆弧对象 + CircularArc3d geArc = new(startPoint, pointOnArc, endPoint); + //将几何类圆弧对象的圆心和半径赋值给圆弧 +#if NET35 + return (Arc)geArc.ToCurve(); +#else + return (Arc)Curve.CreateFromGeCurve(geArc); #endif - } + } - /// - /// 根据起点、圆心和圆弧角度创建圆弧(二维) - /// - /// 圆弧对象 - /// 起点 - /// 圆心 - /// 圆弧角度 - /// 圆弧 - public static Arc CreateArc(Point3d startPoint, Point3d centerPoint, double angle) - { - Arc arc = new(); - arc.Center = centerPoint; - arc.Radius = centerPoint.DistanceTo(startPoint); - Vector2d startVector = new(startPoint.X - centerPoint.X, startPoint.Y - centerPoint.Y); - arc.StartAngle = startVector.Angle; - arc.EndAngle = startVector.Angle + angle; - return arc; - } + /// + /// 根据起点、圆心和圆弧角度创建圆弧(二维) + /// + /// 圆弧对象 + /// 起点 + /// 圆心 + /// 圆弧角度 + /// 圆弧 + public static Arc CreateArc(Point3d startPoint, Point3d centerPoint, double angle) + { + Arc arc = new(); + arc.SetDatabaseDefaults(); + arc.Center = centerPoint; + arc.Radius = centerPoint.DistanceTo(startPoint); + Vector2d startVector = new(startPoint.X - centerPoint.X, startPoint.Y - centerPoint.Y); + arc.StartAngle = startVector.Angle; + arc.EndAngle = startVector.Angle + angle; + return arc; + } - #endregion + #endregion - #region 圆 + #region 圆 - /// - /// 两点创建圆(两点中点为圆心) - /// - /// 起点 - /// 终点 - /// - public static Circle CreateCircle(Point3d startPoint, Point3d endPoint) - { - Circle circle = new(); - circle.Center = startPoint.GetMidPointTo(endPoint); - circle.Radius = startPoint.DistanceTo(endPoint) * 0.5; - return circle; - } + /// + /// 两点创建圆(两点中点为圆心) + /// + /// 起点 + /// 终点 + /// + public static Circle CreateCircle(Point3d startPoint, Point3d endPoint) + { + Circle circle = new(); + circle.SetDatabaseDefaults(); + circle.Center = startPoint.GetMidPointTo(endPoint); + circle.Radius = startPoint.DistanceTo(endPoint) * 0.5; + return circle; + } - /// - /// 三点法创建圆(失败则返回Null) - /// - /// 第一点 - /// 第二点 - /// 第三点 - /// - public static Circle CreateCircle(Point3d pt1, Point3d PointV, Point3d pt3) - { - //先判断三点是否共线,得到pt1点指向PointV、PointV点的矢量 - Vector3d va = pt1.GetVectorTo(PointV); - Vector3d vb = pt1.GetVectorTo(pt3); - //如两矢量夹角为0或180度(π弧度),则三点共线. - if (va.GetAngleTo(vb) == 0 | va.GetAngleTo(vb) == Math.PI) - { - return null; - } - else - { - //创建一个几何类的圆弧对象 - CircularArc3d geArc = new(pt1, PointV, pt3); - geArc.ToCircle(); - return geArc.ToCircle(); - } - } + /// + /// 三点法创建圆(失败则返回Null) + /// + /// 第一点 + /// 第二点 + /// 第三点 + /// + public static Circle? CreateCircle(Point3d pt1, Point3d pt2, Point3d pt3) + { + //先判断三点是否共线,得到pt1点指向pt2、pt2点的矢量 + Vector3d va = pt1.GetVectorTo(pt2); + Vector3d vb = pt1.GetVectorTo(pt3); + //如两矢量夹角为0或180度(π弧度),则三点共线. + if (va.GetAngleTo(vb) == 0 | va.GetAngleTo(vb) == Math.PI) + return null; + + //创建一个几何类的圆弧对象 + CircularArc3d geArc = new(pt1, pt2, pt3); + geArc.ToCircle(); + return geArc.ToCircle(); + } + + /// + /// 通过圆心,半径绘制圆形 + /// + /// 圆心 + /// 半径 + /// 图形的ObjectId + public static Circle? CreateCircle(Point3d center, double radius, double vex = 0, double vey = 0, double vez = 1) + { + return new Circle(center, new Vector3d(vex, vey, vez), radius);//平面法向量XY方向 + } - #endregion + #endregion - #region 块参照 + #region 块参照 - #region 裁剪块参照 + #region 裁剪块参照 - private const string filterDictName = "ACAD_FILTER"; - private const string spatialName = "SPATIAL"; + private const string filterDictName = "ACAD_FILTER"; + private const string spatialName = "SPATIAL"; - /// - /// 裁剪块参照 - /// - /// 块参照 - /// 裁剪多边形点表 - public static void ClipBlockRef(this BlockReference bref, IEnumerable pt3ds) + /// + /// 裁剪块参照 + /// + /// 块参照 + /// 裁剪多边形点表 + public static void ClipBlockRef(this BlockReference bref, IEnumerable pt3ds) + { + Matrix3d mat = bref.BlockTransform.Inverse(); + var pts = + pt3ds + .Select(p => p.TransformBy(mat)) + .Select(p => new Point2d(p.X, p.Y)) + .ToCollection(); + + SpatialFilterDefinition sfd = new(pts, Vector3d.ZAxis, 0.0, 0.0, 0.0, true); + using SpatialFilter sf = new() { Definition = sfd }; + var dict = bref.GetXDictionary()!.GetSubDictionary(true, new string[] { filterDictName })!; + dict.SetAt(spatialName, sf); + //SetToDictionary(dict, spatialName, sf); + } + + /// + /// 裁剪块参照 + /// + /// 块参照 + /// 第一角点 + /// 第二角点 + public static void ClipBlockRef(this BlockReference bref, Point3d pt1, Point3d pt2) + { + Matrix3d mat = bref.BlockTransform.Inverse(); + pt1 = pt1.TransformBy(mat); + pt2 = pt2.TransformBy(mat); + + Point2dCollection pts = new() { - if (bref is null) - { - throw new ArgumentNullException(nameof(bref)); - } - if (pt3ds is null) - { - throw new ArgumentNullException(nameof(pt3ds)); - } - Matrix3d mat = bref.BlockTransform.Inverse(); - var pts = - pt3ds - .Select(p => p.TransformBy(mat)) - .Select(p => new Point2d(p.X, p.Y)) - .ToCollection(); - - SpatialFilterDefinition sfd = new(pts, Vector3d.ZAxis, 0.0, 0.0, 0.0, true); - using SpatialFilter sf = new() { Definition = sfd }; - var dict = bref.GetXDictionary().GetSubDictionary(true, new string[] { filterDictName }); - dict.SetAt(spatialName, sf); - //SetToDictionary(dict, spatialName, sf); - } + new Point2d(Math.Min(pt1.X, pt2.X), Math.Min(pt1.Y, pt2.Y)), + new Point2d(Math.Max(pt1.X, pt2.X), Math.Max(pt1.Y, pt2.Y)) + }; + + SpatialFilterDefinition sfd = new(pts, Vector3d.ZAxis, 0.0, 0.0, 0.0, true); + using SpatialFilter sf = new() { Definition = sfd }; + var dict = bref.GetXDictionary()!.GetSubDictionary(true, new string[] { filterDictName })!; + dict.SetAt(spatialName, sf); + //SetToDictionary(dict, spatialName, sf); + } + #endregion + + /// + /// 更新动态块属性值 + /// + /// 动态块 + /// 属性值字典 + public static void ChangeBlockProperty(this BlockReference blockReference, + Dictionary propertyNameValues) + { + if (!blockReference.IsDynamicBlock) + return; - /// - /// 裁剪块参照 - /// - /// 块参照 - /// 第一角点 - /// 第二角点 - public static void ClipBlockRef(this BlockReference bref, Point3d pt1, Point3d PointV) + using (blockReference.ForWrite()) { - if (bref is null) - { - throw new ArgumentNullException(nameof(bref)); - } - Matrix3d mat = bref.BlockTransform.Inverse(); - pt1 = pt1.TransformBy(mat); - PointV = PointV.TransformBy(mat); - Point2dCollection pts = new() - { - new Point2d(Math.Min(pt1.X, PointV.X), Math.Min(pt1.Y, PointV.Y)), - new Point2d(Math.Max(pt1.X, PointV.X), Math.Max(pt1.Y, PointV.Y)) - }; - - SpatialFilterDefinition sfd = new(pts, Vector3d.ZAxis, 0.0, 0.0, 0.0, true); - using SpatialFilter sf = new() { Definition = sfd }; - var dict = bref.GetXDictionary().GetSubDictionary(true, new string[] { filterDictName }); - dict.SetAt(spatialName, sf); - //SetToDictionary(dict, spatialName, sf); + foreach (DynamicBlockReferenceProperty item in blockReference.DynamicBlockReferencePropertyCollection) + if (propertyNameValues.ContainsKey(item.PropertyName)) + item.Value = propertyNameValues[item.PropertyName]; } - #endregion - #endregion } + #endregion } diff --git a/src/IFoxCAD.Cad/ExtensionMethod/Enums.cs b/src/IFoxCAD.Cad/ExtensionMethod/Enums.cs index 55b108c..fc0fbaf 100644 --- a/src/IFoxCAD.Cad/ExtensionMethod/Enums.cs +++ b/src/IFoxCAD.Cad/ExtensionMethod/Enums.cs @@ -1,80 +1,107 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; +namespace IFoxCAD.Cad; -namespace IFoxCAD.Cad +/// +/// 坐标系类型枚举 +/// +public enum CoordinateSystemCode { /// - /// 坐标系类型枚举 + /// 世界坐标系 /// - public enum CoordinateSystemCode - { - /// - /// 世界坐标系 - /// - Wcs = 0, - - /// - /// 用户坐标系 - /// - Ucs, - - /// - /// 模型空间坐标系 - /// - MDcs, - - /// - /// 图纸空间坐标系 - /// - PDcs - } + Wcs = 0, /// - /// 方向的枚举 + /// 用户坐标系 /// - public enum OrientationType - { - /// - /// 左转或逆时针 - /// - CounterClockWise, - /// - /// 右转或顺时针 - /// - ClockWise, - /// - /// 重合或平行 - /// - Parallel - } + Ucs, /// - /// 点与多边形的关系类型枚举 + /// 模型空间坐标系 /// - public enum PointOnRegionType - { - /// - /// 多边形内部 - /// - Inside, - - /// - /// 多边形上 - /// - On, - - /// - /// 多边形外 - /// - Outside, - - /// - /// 错误 - /// - Error - } + MDcs, + /// + /// 图纸空间坐标系 + /// + PDcs +} +/// +/// 方向的枚举 +/// +public enum OrientationType +{ + /// + /// 左转或逆时针 + /// + CounterClockWise, + /// + /// 右转或顺时针 + /// + ClockWise, + /// + /// 重合或平行 + /// + Parallel } + +/// +/// 点与多边形的关系类型枚举 +/// +public enum PointOnRegionType +{ + /// + /// 多边形内部 + /// + Inside, + + /// + /// 多边形上 + /// + On, + + /// + /// 多边形外 + /// + Outside, + + /// + /// 错误 + /// + Error +} + + + +public enum FontTTF +{ + [Description("宋体.ttf")] + 宋体, + [Description("simfang.ttf")] + 仿宋, + [Description("FSGB2312.ttf")] + 仿宋GB2312, + [Description("Arial.ttf")] + Arial, + [Description("Romans")] + Romans +} + + + +public static class EnumHelper +{ + public static string GetDesc(this Enum val) + { + var type = val.GetType(); + var memberInfo = type.GetMember(val.ToString()); + var attributes = memberInfo[0].GetCustomAttributes(typeof(DescriptionAttribute), false); + //如果没有定义描述,就把当前枚举值的对应名称返回 + if (attributes is null || attributes.Length != 1) + { + return val.ToString(); + } + return ((DescriptionAttribute)attributes.Single()).Description; + } +} + diff --git a/src/IFoxCAD.Cad/ExtensionMethod/GeometryEx.cs b/src/IFoxCAD.Cad/ExtensionMethod/GeometryEx.cs index 8f2a7a1..eca3a15 100644 --- a/src/IFoxCAD.Cad/ExtensionMethod/GeometryEx.cs +++ b/src/IFoxCAD.Cad/ExtensionMethod/GeometryEx.cs @@ -1,665 +1,682 @@ -using Autodesk.AutoCAD.DatabaseServices; -using Autodesk.AutoCAD.Geometry; -using System; -using System.Collections.Generic; +namespace IFoxCAD.Cad; + using System.Drawing; -using System.Linq; -using IFoxCAD.Collections; -using IFoxCAD.Linq; -namespace IFoxCAD.Cad + +/// +/// 图形扩展类 +/// +public static class GeometryEx { + + #region Point&Circle + /// - /// 图形扩展类 + /// 判断点与多边形的关系 /// - public static class GeometryEx + /// 多边形顶点集合 + /// 点 + /// 点与多边形的关系 + public static PointOnRegionType PointOnRegion(this IEnumerable pts, Point2d pt) { - public static double DistanceTo(this Point2d pt1, Point2d PointV) - { - return pt1.GetDistanceTo(PointV); - } - - #region Point&Circle + //遍历点集并生成首尾连接的多边形 + var ptlst = new LoopList(pts); + if (ptlst.Count < 3) + return PointOnRegionType.Error; + + var ls2ds = new List(); + foreach (var node in ptlst.GetNodes()) + { + ls2ds.Add(new LineSegment2d(node.Value, node.Next!.Value)); + } + var cc2d = new CompositeCurve2d(ls2ds.ToArray()); + + //在多边形上? + if (cc2d.IsOn(pt)) + return PointOnRegionType.On; + + //在最小包围矩形外? + var bb2d = cc2d.BoundBlock; + if (!bb2d.Contains(pt)) + return PointOnRegionType.Outside; + + // + bool flag = false; + foreach (var node in ptlst.GetNodes()) + { + var pt1 = node.Value; + var pt2 = node.Next!.Value; + if (pt.Y < pt1.Y && pt.Y < pt2.Y) + continue; + if (pt1.X < pt.X && pt2.X < pt.X) + continue; + Vector2d vec = pt2 - pt1; + double t = (pt.X - pt1.X) / vec.X; + double y = t * vec.Y + pt1.Y; + if (y < pt.Y && t >= 0 && t <= 1) + flag = !flag; + } + return + flag ? + PointOnRegionType.Inside : PointOnRegionType.Outside; + } - /// - /// 判断点与多边形的关系 - /// - /// 多边形顶点集合 - /// 点 - /// 点与多边形的关系 - public static PointOnRegionType PointOnRegion(this IEnumerable pts, Point2d pt) - { - //遍历点集并生成首尾连接的多边形 - var ptlst = new LoopList(pts); - if (ptlst.Count < 3) - return PointOnRegionType.Error; + /// + /// 判断点与多边形的关系 + /// + /// 多边形顶点集合 + /// 点 + /// 点与多边形的关系 + public static PointOnRegionType PointOnRegion(this IEnumerable pts, Point3d pt) + { + //遍历点集并生成首尾连接的多边形 + var ptlst = new LoopList(pts); + if (ptlst.First!.Value == ptlst.Last!.Value) + ptlst.RemoveLast(); + if (ptlst.Count < 3) + return PointOnRegionType.Error; + + var ls3ds = new List(); + foreach (var node in ptlst.GetNodes()) + { + ls3ds.Add(new LineSegment3d(node.Value, node.Next!.Value)); + } + var cc3d = new CompositeCurve3d(ls3ds.ToArray()); + + //在多边形上? + if (cc3d.IsOn(pt)) + return PointOnRegionType.On; + + //在最小包围矩形外? + var bb2d = cc3d.BoundBlock; + if (!bb2d.Contains(pt)) + return PointOnRegionType.Outside; + + // + bool flag = false; + foreach (var node in ptlst.GetNodes()) + { + var pt1 = node.Value; + var pt2 = node.Next!.Value; + if (pt.Y < pt1.Y && pt.Y < pt2.Y) + continue; + if (pt1.X < pt.X && pt2.X < pt.X) + continue; + Vector3d vec = pt2 - pt1; + double t = (pt.X - pt1.X) / vec.X; + double y = t * vec.Y + pt1.Y; + if (y < pt.Y && t >= 0 && t <= 1) + flag = !flag; + } + return + flag ? + PointOnRegionType.Inside : PointOnRegionType.Outside; + } - var ls2ds = new List(); - foreach (var node in ptlst.GetNodes()) - { - ls2ds.Add(new LineSegment2d(node.Value, node.Next.Value)); - } - var cc2d = new CompositeCurve2d(ls2ds.ToArray()); + /// + /// 按两点返回最小包围圆 + /// + /// 基准点 + /// 基准点 + /// 输出圆上的点 + /// 解析类圆对象 + public static CircularArc2d GetMinCircle(Point2d pt1, Point2d pt2, out LoopList ptlst) + { + ptlst = new LoopList { pt1, pt2 }; + return + new CircularArc2d + ( + (pt1 + pt2.GetAsVector()) / 2, + pt1.GetDistanceTo(pt2) / 2 + ); + } - //在多边形上? - if (cc2d.IsOn(pt)) - return PointOnRegionType.On; + /// + /// 按三点返回最小包围圆 + /// + /// 基准点 + /// 基准点 + /// 基准点 + /// 输出圆上的点 + /// 解析类圆对象 + public static CircularArc2d GetMinCircle(Point2d pt1, Point2d pt2, Point2d pt3, out LoopList ptlst) + { + ptlst = new LoopList { pt1, pt2, pt3 }; - //在最小包围矩形外? - var bb2d = cc2d.BoundBlock; - if (!bb2d.Contains(pt)) - return PointOnRegionType.Outside; + //遍历各点与下一点的向量长度,找到距离最大的两个点 + LoopListNode maxNode = + ptlst.GetNodes().FindByMax + ( + out double maxLength, + node => node.Value.GetDistanceTo(node.Next!.Value) + ); - // - bool flag = false; - foreach (var node in ptlst.GetNodes()) - { - var pt1 = node.Value; - var PointV = node.Next.Value; - if (pt.Y < pt1.Y && pt.Y < PointV.Y) - continue; - if (pt1.X < pt.X && PointV.X < pt.X) - continue; - Vector2d vec = PointV - pt1; - double t = (pt.X - pt1.X) / vec.X; - double y = t * vec.Y + pt1.Y; - if (y < pt.Y && t >= 0 && t <= 1) - flag = !flag; - } - return - flag ? - PointOnRegionType.Inside : PointOnRegionType.Outside; - } + //以两点做最小包围圆 + CircularArc2d ca2d = + GetMinCircle(maxNode.Value, maxNode.Next!.Value, out LoopList tptlst); - /// - /// 判断点与多边形的关系 - /// - /// 多边形顶点集合 - /// 点 - /// 点与多边形的关系 - public static PointOnRegionType PointOnRegion(this IEnumerable pts, Point3d pt) + //如果另一点属于该圆 + if (ca2d.IsIn(maxNode.Previous!.Value)) { - //遍历点集并生成首尾连接的多边形 - var ptlst = new LoopList(pts); - if (ptlst.First.Value == ptlst.Last.Value) - ptlst.RemoveLast(); - if (ptlst.Count < 3) - return PointOnRegionType.Error; - - var ls3ds = new List(); - foreach (var node in ptlst.GetNodes()) - { - ls3ds.Add(new LineSegment3d(node.Value, node.Next.Value)); - } - var cc3d = new CompositeCurve3d(ls3ds.ToArray()); + //返回 + ptlst = tptlst; + return ca2d; + } + //否则按三点做圆 + //ptlst.SetFirst(maxNode); + ptlst = new LoopList { maxNode.Value, maxNode.Next.Value, maxNode.Previous.Value }; + ca2d = new CircularArc2d(pt1, pt2, pt3); + ca2d.SetAngles(0, Math.PI * 2); + return ca2d; + } - //在多边形上? - if (cc3d.IsOn(pt)) - return PointOnRegionType.On; + /// + /// 按四点返回最小包围圆 + /// + /// 基准点 + /// 基准点 + /// 基准点 + /// 基准点 + /// 输出圆上的点 + /// 解析类圆对象 + public static CircularArc2d? GetMinCircle(Point2d pt1, Point2d pt2, Point2d pt3, Point2d pt4, out LoopList? ptlst) + { + var iniptlst = new LoopList() { pt1, pt2, pt3, pt4 }; + ptlst = null; + CircularArc2d? ca2d = null; - //在最小包围矩形外? - var bb2d = cc3d.BoundBlock; - if (!bb2d.Contains(pt)) - return PointOnRegionType.Outside; + //遍历C43的组合,环链表的优势在这里 + foreach (LoopListNode firstNode in iniptlst.GetNodes()) + { + //获取各组合下三点的最小包围圆 + var secondNode = firstNode.Next; + var thirdNode = secondNode!.Next; + CircularArc2d tca2d = GetMinCircle(firstNode.Value, secondNode.Value, thirdNode!.Value, out LoopList tptlst); - // - bool flag = false; - foreach (var node in ptlst.GetNodes()) + //如果另一点属于该圆,并且半径小于当前值就把它做为候选解 + if (tca2d.IsIn(firstNode.Previous!.Value)) { - var pt1 = node.Value; - var PointV = node.Next.Value; - if (pt.Y < pt1.Y && pt.Y < PointV.Y) - continue; - if (pt1.X < pt.X && PointV.X < pt.X) - continue; - Vector3d vec = PointV - pt1; - double t = (pt.X - pt1.X) / vec.X; - double y = t * vec.Y + pt1.Y; - if (y < pt.Y && t >= 0 && t <= 1) - flag = !flag; + if (ca2d is null || tca2d.Radius < ca2d.Radius) + { + ca2d = tca2d; + ptlst = tptlst; + } } - return - flag ? - PointOnRegionType.Inside : PointOnRegionType.Outside; } - /// - /// 按两点返回最小包围圆 - /// - /// 基准点 - /// 基准点 - /// 输出圆上的点 - /// 解析类圆对象 - public static CircularArc2d GetMinCircle(Point2d pt1, Point2d PointV, out LoopList ptlst) - { - ptlst = new LoopList { pt1, PointV }; - return - new CircularArc2d - ( - (pt1 + PointV.GetAsVector()) / 2, - pt1.DistanceTo(PointV) / 2 - ); - } + //返回直径最小的圆 + return ca2d; + } - /// - /// 按三点返回最小包围圆 - /// - /// 基准点 - /// 基准点 - /// 基准点 - /// 输出圆上的点 - /// 解析类圆对象 - public static CircularArc2d GetMinCircle(Point2d pt1, Point2d PointV, Point2d pt3, out LoopList ptlst) - { - ptlst = new LoopList { pt1, PointV, pt3 }; - - //遍历各点与下一点的向量长度,找到距离最大的两个点 - double maxLength; - LoopListNode maxNode = - ptlst.GetNodes().FindByMax - ( - out maxLength, - node => node.Value.DistanceTo(node.Next.Value) - ); - - //以两点做最小包围圆 - LoopList tptlst; - CircularArc2d ca2d = - GetMinCircle(maxNode.Value, maxNode.Next.Value, out tptlst); - - //如果另一点属于该圆 - if (ca2d.IsIn(maxNode.Previous.Value)) - { - //返回 - ptlst = tptlst; - return ca2d; - } + /// + /// 计算三点围成的有向面积 + /// + /// 基准点 + /// 第一点 + /// 第二点 + /// 三点围成的三角形的有向面积 + private static double CalArea(Point2d ptBase, Point2d pt1, Point2d pt2) + { + return (pt2 - ptBase).DotProduct((pt1 - ptBase).GetPerpendicularVector()) / 2; + } + /// + /// 计算三点围成的三角形的真实面积 + /// + /// 基准点 + /// 第一点 + /// 第二点 + /// 三点围成的三角形的真实面积 + public static double GetArea(this Point2d ptBase, Point2d pt1, Point2d pt2) + { + return Math.Abs(CalArea(ptBase, pt1, pt2)); + } - //否则按三点做圆 - ptlst.SetFirst(maxNode); - ca2d = new CircularArc2d(pt1, PointV, pt3); - ca2d.SetAngles(0, Math.PI * 2); - return ca2d; - } + /// + /// 判断三点是否为逆时针,也就是说判断三点是否为左转 + /// + /// 基点 + /// 第一点 + /// 第二点 + /// OrientationType 类型值 + public static OrientationType IsClockWise(this Point2d ptBase, Point2d pt1, Point2d pt2) + { - /// - /// 按四点返回最小包围圆 - /// - /// 基准点 - /// 基准点 - /// 基准点 - /// 基准点 - /// 输出圆上的点 - /// 解析类圆对象 - public static CircularArc2d GetMinCircle(Point2d pt1, Point2d PointV, Point2d pt3, Point2d pt4, out LoopList ptlst) + return CalArea(ptBase, pt1, pt2) switch { - LoopList iniptlst = - new LoopList { pt1, PointV, pt3, pt4 }; - ptlst = null; - CircularArc2d ca2d = null; - - //遍历C43的组合,环链表的优势在这里 - foreach (LoopListNode firstNode in iniptlst.GetNodes()) - { - //获取各组合下三点的最小包围圆 - LoopListNode secondNode = firstNode.Next; - LoopListNode thirdNode = secondNode.Next; - LoopList tptlst; - CircularArc2d tca2d = GetMinCircle(firstNode.Value, secondNode.Value, thirdNode.Value, out tptlst); - - //如果另一点属于该圆,并且半径小于当前值就把它做为候选解 - if (tca2d.IsIn(firstNode.Previous.Value)) - { - if (ca2d is null || tca2d.Radius < ca2d.Radius) - { - ca2d = tca2d; - ptlst = tptlst; - } - } - } + > 0 => OrientationType.CounterClockWise, + < 0 => OrientationType.ClockWise, + _ => OrientationType.Parallel + }; + } - //返回直径最小的圆 - return ca2d; - } + /// + /// 计算两个二维向量围成的平行四边形的有向面积 + /// + /// 基向量 + /// 向量 + /// 有向面积 + private static double CalArea(Vector2d vecBase, Vector2d vec) + { + return vec.DotProduct(vecBase.GetPerpendicularVector()) / 2; + } - /// - /// 计算三点围成的有向面积 - /// - /// 基准点 - /// 第一点 - /// 第二点 - /// 三点围成的三角形的有向面积 - private static double CalArea(Point2d ptBase, Point2d pt1, Point2d PointV) - { - return (PointV - ptBase).DotProduct((pt1 - ptBase).GetPerpendicularVector()) / 2; - } - /// - /// 计算三点围成的三角形的真实面积 - /// - /// 基准点 - /// 第一点 - /// 第二点 - /// 三点围成的三角形的真实面积 - public static double GetArea(this Point2d ptBase, Point2d pt1, Point2d PointV) - { - return Math.Abs(CalArea(ptBase, pt1, PointV)); - } + /// + /// 计算两个二维向量围成的平行四边形的真实面积 + /// + /// 基向量 + /// 向量 + /// 真实面积 + public static double GetArea(Vector2d vecBase, Vector2d vec) + { + return Math.Abs(CalArea(vecBase, vec)); + } - /// - /// 判断三点是否为逆时针,也就是说判断三点是否为左转 - /// - /// 基点 - /// 第一点 - /// 第二点 - /// OrientationType 类型值 - public static OrientationType IsClockWise(this Point2d ptBase, Point2d pt1, Point2d PointV) + /// + /// 判断两个二维向量是否左转 + /// + /// 基向量 + /// 向量 + /// OrientationType 类型值 + public static OrientationType IsClockWise(Vector2d vecBase, Vector2d vec) + { + return CalArea(vecBase, vec) switch { + > 0 => OrientationType.CounterClockWise, + < 0 => OrientationType.ClockWise, + _ => OrientationType.Parallel + }; + } - return CalArea(ptBase, pt1, PointV) switch - { + #region PointList - > 0 => OrientationType.CounterClockWise, - < 0 => OrientationType.ClockWise, - _ => OrientationType.Parallel - }; - } + /// + /// 计算点集的有向面积 + /// + /// 点集 + /// 有向面积 + private static double CalArea(IEnumerable pnts) + { + IEnumerator itor = pnts.GetEnumerator(); + if (!itor.MoveNext()) + throw new ArgumentNullException(nameof(pnts)); + Point2d start = itor.Current; + Point2d p1, p2 = start; + double area = 0; - /// - /// 计算两个二维向量围成的平行四边形的有向面积 - /// - /// 基向量 - /// 向量 - /// 有向面积 - private static double CalArea(Vector2d vecBase, Vector2d vec) + while (itor.MoveNext()) { - return vec.DotProduct(vecBase.GetPerpendicularVector()) / 2; + p1 = p2; + p2 = itor.Current; + area += (p1.X * p2.Y - p2.X * p1.Y); } - /// - /// 计算两个二维向量围成的平行四边形的真实面积 - /// - /// 基向量 - /// 向量 - /// 真实面积 - public static double GetArea(Vector2d vecBase, Vector2d vec) + area = (area + (p2.X * start.Y - start.X * p2.Y)) / 2.0; + return area; + } + /// + /// 计算点集的真实面积 + /// + /// 点集 + /// 面积 + public static double GetArea(this IEnumerable pnts) + { + return Math.Abs(CalArea(pnts)); + } + + /// + /// 判断点集的点序 + /// + /// 点集 + /// OrientationType 类型值 + public static OrientationType IsClockWise(this IEnumerable pnts) + { + return CalArea(pnts) switch { - return Math.Abs(CalArea(vecBase, vec)); - } + < 0 => OrientationType.ClockWise, + > 0 => OrientationType.CounterClockWise, + _ => OrientationType.Parallel + }; + } - /// - /// 判断两个二维向量是否左转 - /// - /// 基向量 - /// 向量 - /// OrientationType 类型值 - public static OrientationType IsClockWise(Vector2d vecBase, Vector2d vec) + /// + /// 按点集返回最小包围圆 + /// + /// 点集 + /// 输出圆上的点 + /// 解析类圆对象 + public static CircularArc2d? GetMinCircle(this List pnts, out LoopList? ptlst) + { + //点数较小时直接返回 + switch (pnts.Count) { - return CalArea(vecBase, vec) switch - { - > 0 => OrientationType.CounterClockWise, - < 0 => OrientationType.ClockWise, - _ => OrientationType.Parallel - }; - } + case 0: + ptlst = null; + return null; - #region PointList + case 1: + ptlst = new LoopList { pnts[0] }; + return new CircularArc2d(pnts[0], 0); - /// - /// 计算点集的有向面积 - /// - /// 点集 - /// 有向面积 - private static double CalArea(IEnumerable pnts) - { - IEnumerator itor = pnts.GetEnumerator(); - if (!itor.MoveNext()) - throw new ArgumentNullException(nameof(pnts)); - Point2d start = itor.Current; - Point2d p1, p2 = start; - double area = 0; - - while (itor.MoveNext()) - { - p1 = p2; - p2 = itor.Current; - area += (p1.X * p2.Y - p2.X * p1.Y); - } + case 2: + return GetMinCircle(pnts[0], pnts[1], out ptlst); - area = (area + (p2.X * start.Y - start.X * p2.Y)) / 2.0; - return area; - } - /// - /// 计算点集的真实面积 - /// - /// 点集 - /// 面积 - public static double GetArea(this IEnumerable pnts) - { - return Math.Abs(CalArea(pnts)); - } + case 3: + return GetMinCircle(pnts[0], pnts[1], pnts[2], out ptlst); - /// - /// 判断点集的点序 - /// - /// 点集 - /// OrientationType 类型值 - public static OrientationType IsClockWise(this IEnumerable pnts) - { - return CalArea(pnts) switch - { - < 0 => OrientationType.ClockWise, - > 0 => OrientationType.CounterClockWise, - _ => OrientationType.Parallel - }; + case 4: + return GetMinCircle(pnts[0], pnts[1], pnts[2], pnts[3], out ptlst); } - /// - /// 按点集返回最小包围圆 - /// - /// 点集 - /// 输出圆上的点 - /// 解析类圆对象 - public static CircularArc2d GetMinCircle(this List pnts, out LoopList ptlst) - { - //点数较小时直接返回 - switch (pnts.Count) - { - case 0: - ptlst = null; - return null; + //按前三点计算最小包围圆 + Point2d[] tpnts = new Point2d[4]; + pnts.CopyTo(0, tpnts, 0, 3); + CircularArc2d? ca2d = GetMinCircle(tpnts[0], tpnts[1], tpnts[2], out ptlst); - case 1: - ptlst = new LoopList { pnts[0] }; - return new CircularArc2d(pnts[0], 0); + //找到点集中距离圆心的最远点为第四点 + tpnts[3] = pnts.FindByMax(pnt => ca2d.Center.GetDistanceTo(pnt)); - case 2: - return GetMinCircle(pnts[0], pnts[1], out ptlst); - - case 3: - return GetMinCircle(pnts[0], pnts[1], pnts[2], out ptlst); + //如果最远点属于圆结束 + while (!ca2d.IsIn(tpnts[3])) + { + //如果最远点不属于圆,按此四点计算最小包围圆 + ca2d = GetMinCircle(tpnts[0], tpnts[1], tpnts[2], tpnts[3], out ptlst); - case 4: - return GetMinCircle(pnts[0], pnts[1], pnts[2], pnts[3], out ptlst); + //将结果作为新的前三点 + if (ptlst!.Count == 3) + { + tpnts[2] = ptlst.Last!.Value; } - - //按前三点计算最小包围圆 - Point2d[] tpnts = new Point2d[4]; - pnts.CopyTo(0, tpnts, 0, 3); - CircularArc2d ca2d = GetMinCircle(tpnts[0], tpnts[1], tpnts[2], out ptlst); - - //找到点集中距离圆心的最远点为第四点 - tpnts[3] = pnts.FindByMax(pnt => ca2d.Center.DistanceTo(pnt)); - - //如果最远点属于圆结束 - while (!ca2d.IsIn(tpnts[3])) + else { - //如果最远点不属于圆,按此四点计算最小包围圆 - ca2d = GetMinCircle(tpnts[0], tpnts[1], tpnts[2], tpnts[3], out ptlst); - - //将结果作为新的前三点 - if (ptlst.Count == 3) - { - tpnts[2] = ptlst.Last.Value; - } - else - { - //第三点取另两点中距离圆心较远的点 - //按算法中描述的任选其中一点的话,还是无法收敛...... - tpnts[2] = - tpnts.Except(ptlst) - .FindByMax(pnt => ca2d.Center.DistanceTo(pnt)); - } - tpnts[0] = ptlst.First.Value; - tpnts[1] = ptlst.First.Next.Value; - - //按此三点计算最小包围圆 - ca2d = GetMinCircle(tpnts[0], tpnts[1], tpnts[2], out ptlst); - - //找到点集中圆心的最远点为第四点 - tpnts[3] = pnts.FindByMax(pnt => ca2d.Center.DistanceTo(pnt)); + //第三点取另两点中距离圆心较远的点 + //按算法中描述的任选其中一点的话,还是无法收敛...... + tpnts[2] = + tpnts.Except(ptlst) + .FindByMax(pnt => ca2d!.Center.GetDistanceTo(pnt)); } + tpnts[0] = ptlst.First!.Value; + tpnts[1] = ptlst.First.Next!.Value; - return ca2d; + //按此三点计算最小包围圆 + ca2d = GetMinCircle(tpnts[0], tpnts[1], tpnts[2], out ptlst); + + //找到点集中圆心的最远点为第四点 + tpnts[3] = pnts.FindByMax(pnt => ca2d.Center.GetDistanceTo(pnt)); } - /// - /// 获取点集的凸包 - /// - /// 点集 - /// 凸包 - public static List ConvexHull(this List points) - { - if (points is null) - return null; + return ca2d; + } - if (points.Count <= 1) - return points; + /// + /// 获取点集的凸包 + /// + /// 点集 + /// 凸包 + public static List? ConvexHull(this List points) + { + if (points is null) + return null; - int n = points.Count, k = 0; - List H = new(new Point2d[2 * n]); + if (points.Count <= 1) + return points; - points.Sort((a, b) => - a.X == b.X ? a.Y.CompareTo(b.Y) : a.X.CompareTo(b.X)); + int n = points.Count, k = 0; + List H = new(new Point2d[2 * n]); - // Build lower hull - for (int i = 0; i < n; ++i) - { - while (k >= 2 && IsClockWise(H[k - 2], H[k - 1], points[i]) == OrientationType.CounterClockWise) - k--; - H[k++] = points[i]; - } + points.Sort((a, b) => + a.X == b.X ? a.Y.CompareTo(b.Y) : a.X.CompareTo(b.X)); - // Build upper hull - for (int i = n - 2, t = k + 1; i >= 0; i--) - { - while (k >= t && IsClockWise(H[k - 2], H[k - 1], points[i]) == OrientationType.CounterClockWise) - k--; - H[k++] = points[i]; - } - return H.Take(k - 1).ToList(); + // Build lower hull + for (int i = 0; i < n; ++i) + { + while (k >= 2 && IsClockWise(H[k - 2], H[k - 1], points[i]) == OrientationType.CounterClockWise) + k--; + H[k++] = points[i]; } + // Build upper hull + for (int i = n - 2, t = k + 1; i >= 0; i--) + { + while (k >= t && IsClockWise(H[k - 2], H[k - 1], points[i]) == OrientationType.CounterClockWise) + k--; + H[k++] = points[i]; + } + return H.Take(k - 1).ToList(); + } - #endregion PointList - #endregion Point&Circle + #endregion PointList - #region Ucs + #endregion Point&Circle - /// - /// ucs到wcs的点变换 - /// - /// 点 - /// 变换后的点 - public static Point3d Ucs2Wcs(this Point3d point) - { - return point.TransformBy(Env.Editor.CurrentUserCoordinateSystem); - } + #region Ucs - /// - /// wcs到ucs的点变换 - /// - /// 点 - /// 变换后的点 - public static Point3d Wcs2Ucs(this Point3d point) - { - return point.TransformBy(Env.Editor.CurrentUserCoordinateSystem.Inverse()); - } + /// + /// ucs到wcs的点变换 + /// + /// 点 + /// 变换后的点 + public static Point3d Ucs2Wcs(this Point3d point) + { + return point.TransformBy(Env.Editor.CurrentUserCoordinateSystem); + } - /// - /// ucs到wcs的向量变换 - /// - /// 向量 - /// 变换后的向量 - public static Vector3d Ucs2Wcs(this Vector3d vec) - { - return vec.TransformBy(Env.Editor.CurrentUserCoordinateSystem); - } + /// + /// wcs到ucs的点变换 + /// + /// 点 + /// 变换后的点 + public static Point3d Wcs2Ucs(this Point3d point) + { + return point.TransformBy(Env.Editor.CurrentUserCoordinateSystem.Inverse()); + } - /// - /// wcs到ucs的向量变换 - /// - /// 向量 - /// 变换后的向量 - public static Vector3d Wcs2Ucs(this Vector3d vec) - { - return vec.TransformBy(Env.Editor.CurrentUserCoordinateSystem.Inverse()); - } + /// + /// ucs到wcs的向量变换 + /// + /// 向量 + /// 变换后的向量 + public static Vector3d Ucs2Wcs(this Vector3d vec) + { + return vec.TransformBy(Env.Editor.CurrentUserCoordinateSystem); + } - /// - /// 模拟 trans 函数 - /// - /// 点 - /// 源坐标系 - /// 目标坐标系 - /// 变换后的点 - public static Point3d Trans(this Point3d point, CoordinateSystemCode from, CoordinateSystemCode to) - { - return Env.Editor.GetMatrix(from, to) * point; - } + /// + /// wcs到ucs的向量变换 + /// + /// 向量 + /// 变换后的向量 + public static Vector3d Wcs2Ucs(this Vector3d vec) + { + return vec.TransformBy(Env.Editor.CurrentUserCoordinateSystem.Inverse()); + } - /// - /// 模拟 trans 函数 - /// - /// 向量 - /// 源坐标系 - /// 目标坐标系 - /// 变换后的向量 - public static Vector3d Trans(this Vector3d vec, CoordinateSystemCode from, CoordinateSystemCode to) - { - return vec.TransformBy(Env.Editor.GetMatrix(from, to)); - } + /// + /// 模拟 trans 函数 + /// + /// 点 + /// 源坐标系 + /// 目标坐标系 + /// 变换后的点 + public static Point3d Trans(this Point3d point, CoordinateSystemCode from, CoordinateSystemCode to) + { + return Env.Editor.GetMatrix(from, to) * point; + } - /// - /// wcs到dcs的点变换 - /// - /// 点 - /// 是否为图纸空间 - /// 变换后的点 - public static Point3d Wcs2Dcs(this Point3d point, bool atPaperSpace) - { - return - Trans( - point, - CoordinateSystemCode.Wcs, atPaperSpace ? CoordinateSystemCode.PDcs : CoordinateSystemCode.MDcs - ); - } + /// + /// 模拟 trans 函数 + /// + /// 向量 + /// 源坐标系 + /// 目标坐标系 + /// 变换后的向量 + public static Vector3d Trans(this Vector3d vec, CoordinateSystemCode from, CoordinateSystemCode to) + { + return vec.TransformBy(Env.Editor.GetMatrix(from, to)); + } - /// - /// wcs到dcs的向量变换 - /// - /// 向量 - /// 是否为图纸空间 - /// 变换后的向量 - public static Vector3d Wcs2Dcs(this Vector3d vec, bool atPaperSpace) - { - return - Trans( - vec, - CoordinateSystemCode.Wcs, atPaperSpace ? CoordinateSystemCode.PDcs : CoordinateSystemCode.MDcs - ); - } + /// + /// wcs到dcs的点变换 + /// + /// 点 + /// 是否为图纸空间 + /// 变换后的点 + public static Point3d Wcs2Dcs(this Point3d point, bool atPaperSpace) + { + return + Trans( + point, + CoordinateSystemCode.Wcs, atPaperSpace ? CoordinateSystemCode.PDcs : CoordinateSystemCode.MDcs + ); + } + + /// + /// wcs到dcs的向量变换 + /// + /// 向量 + /// 是否为图纸空间 + /// 变换后的向量 + public static Vector3d Wcs2Dcs(this Vector3d vec, bool atPaperSpace) + { + return + Trans( + vec, + CoordinateSystemCode.Wcs, atPaperSpace ? CoordinateSystemCode.PDcs : CoordinateSystemCode.MDcs + ); + } - #endregion Ucs + #endregion Ucs - /// - /// 返回不等比例变换矩阵 - /// - /// 基点 - /// x方向比例 - /// y方向比例 - /// z方向比例 - /// 三维矩阵 - public static Matrix3d GetScaleMatrix(this Point3d point, double x, double y, double z) - { - double[] matdata = new double[16]; - matdata[0] = x; - matdata[3] = point.X * (1 - x); - matdata[5] = y; - matdata[7] = point.Y * (1 - y); - matdata[10] = z; - matdata[11] = point.Z * (1 - z); - matdata[15] = 1; - return new Matrix3d(matdata); - } + /// + /// 返回不等比例变换矩阵 + /// + /// 基点 + /// x方向比例 + /// y方向比例 + /// z方向比例 + /// 三维矩阵 + public static Matrix3d GetScaleMatrix(this Point3d point, double x, double y, double z) + { + double[] matdata = new double[16]; + matdata[0] = x; + matdata[3] = point.X * (1 - x); + matdata[5] = y; + matdata[7] = point.Y * (1 - y); + matdata[10] = z; + matdata[11] = point.Z * (1 - z); + matdata[15] = 1; + return new Matrix3d(matdata); + } - /// - /// 获取坐标范围的大小 - /// - /// 坐标范围 - /// 尺寸对象 - public static Size GetSize(this Extents3d ext) - { - int width = (int)Math.Floor(ext.MaxPoint.X - ext.MinPoint.X); - int height = (int)Math.Ceiling(ext.MaxPoint.Y - ext.MinPoint.Y); - return new Size(width, height); - } + /// + /// 获取坐标范围的大小 + /// + /// 坐标范围 + /// 尺寸对象 + public static Size GetSize(this Extents3d ext) + { + int width = (int)Math.Floor(ext.MaxPoint.X - ext.MinPoint.X); + int height = (int)Math.Ceiling(ext.MaxPoint.Y - ext.MinPoint.Y); + return new Size(width, height); + } - /// - /// 将三维点转换为二维点 - /// - /// 三维点 - /// 二维点 - public static Point2d Point2d(this Point3d pt) - { - return new Point2d(pt.X, pt.Y); - } - /// - /// 将三维点集转换为二维点集 - /// - /// 三维点集 - /// 二维点集 - public static IEnumerable Point2d(this IEnumerable pts) - { - return pts.Select(pt => pt.Point2d()); - } - /// - /// 将二维点转换为三维点 - /// - /// 二维点 - /// 三维点 - public static Point3d Point3d(this Point2d pt) - { - return new Point3d(pt.X, pt.Y, 0); - } + /// + /// 将三维点转换为二维点 + /// + /// 三维点 + /// 二维点 + public static Point2d Point2d(this Point3d pt) + { + return new Point2d(pt.X, pt.Y); + } + /// + /// 将三维点集转换为二维点集 + /// + /// 三维点集 + /// 二维点集 + public static IEnumerable Point2d(this IEnumerable pts) + { + return pts.Select(pt => pt.Point2d()); + } + /// + /// 将二维点转换为三维点 + /// + /// 二维点 + /// 三维点 + public static Point3d Point3d(this Point2d pt) + { + return new Point3d(pt.X, pt.Y, 0); + } + /// + /// 将二维点转换为三维点 + /// + /// 二维点 + /// Z值 + /// 三维点 + public static Point3d Point3d(this Point2d pt,double z) + { + return new Point3d(pt.X, pt.Y, z); + } - /// - /// 获取两个点之间的中点 - /// - /// 第一点 - /// 第二点 - /// 返回两个点之间的中点 - public static Point3d GetMidPointTo(this Point3d pt1, Point3d PointV) - { - return new Point3d((pt1.X + PointV.X) * 0.5, (pt1.Y + PointV.Y) * 0.5, (pt1.Z + PointV.Z) * 0.5); - } + /// + /// 获取两个点之间的中点 + /// + /// 第一点 + /// 第二点 + /// 返回两个点之间的中点 + public static Point3d GetMidPointTo(this Point3d pt1, Point3d pt2) + { + return new Point3d((pt1.X + pt2.X) * 0.5, (pt1.Y + pt2.Y) * 0.5, (pt1.Z + pt2.Z) * 0.5); + } + + /// + /// 获取两个点之间的中点 + /// + /// 第一点 + /// 第二点 + /// 返回两个点之间的中点 + public static Point2d GetMidPointTo(this Point2d pt1, Point2d pt2) + { + return new Point2d((pt1.X + pt2.X) * 0.5, (pt1.Y + pt2.Y) * 0.5); + } - /// - /// 根据世界坐标计算用户坐标 - /// - /// 基点世界坐标 - /// 基点用户坐标 - /// 目标世界坐标 - /// 坐标网旋转角,按x轴正向逆时针弧度 - /// 目标用户坐标 - public static Point3d TransPoint(this Point3d basePt, Point3d userPt, Point3d transPt, double ang) - { - Matrix3d transMat = Matrix3d.Displacement(userPt - basePt); - Matrix3d roMat = Matrix3d.Rotation(-ang, Vector3d.ZAxis, userPt); - return transPt.TransformBy(roMat * transMat); - } - /// - /// 计算指定距离和角度的点 - /// - /// 本函数仅适用于x-y平面 - /// 基点 - /// 角度,x轴正向逆时针弧度 - /// 距离 - /// 目标点 - public static Point3d Polar(this Point3d pt, double ang, double len) - { - return pt + Vector3d.XAxis.RotateBy(ang, Vector3d.ZAxis) * len; - } + /// + /// 根据世界坐标计算用户坐标 + /// + /// 基点世界坐标 + /// 基点用户坐标 + /// 目标世界坐标 + /// 坐标网旋转角,按x轴正向逆时针弧度 + /// 目标用户坐标 + public static Point3d TransPoint(this Point3d basePt, Point3d userPt, Point3d transPt, double ang) + { + Matrix3d transMat = Matrix3d.Displacement(userPt - basePt); + Matrix3d roMat = Matrix3d.Rotation(-ang, Vector3d.ZAxis, userPt); + return transPt.TransformBy(roMat * transMat); + } + /// + /// 计算指定距离和角度的点 + /// + /// 本函数仅适用于x-y平面 + /// 基点 + /// 角度,x轴正向逆时针弧度 + /// 距离 + /// 目标点 + public static Point3d Polar(this Point3d pt, double ang, double len) + { + return pt + Vector3d.XAxis.RotateBy(ang, Vector3d.ZAxis) * len; + } + /// + /// 计算指定距离和角度的点 + /// + /// 本函数仅适用于x-y平面 + /// 基点 + /// 角度,x轴正向逆时针弧度 + /// 距离 + /// 目标点 + public static Point2d Polar(this Point2d pt, double ang, double len) + { + return pt + Vector2d.XAxis.RotateBy(ang) * len; } } diff --git a/src/IFoxCAD.Cad/ExtensionMethod/Jig.cs b/src/IFoxCAD.Cad/ExtensionMethod/Jig.cs new file mode 100644 index 0000000..b9182ae --- /dev/null +++ b/src/IFoxCAD.Cad/ExtensionMethod/Jig.cs @@ -0,0 +1,340 @@ +namespace IFoxCAD.Cad; + +/* 封装jig + * 20220726 隐藏事件,利用函数进行数据库图元重绘 + * 20220710 修改SetOption()的空格结束,并添加例子到IFox + * 20220503 cad22需要防止刷新过程中更改队列,是因为允许函数重入导致,08不会有. + * 20220326 重绘图元的函数用错了,现在修正过来 + * 20211216 加入块表时候做一个差集,剔除临时图元 + * 20211209 补充正交变量设置和回收设置 + * 作者: 惊惊⎛⎝◕⏝⏝◕。⎠⎞ ⎛⎝≥⏝⏝0⎠⎞ ⎛⎝⓿⏝⏝⓿。⎠⎞ ⎛⎝≥⏝⏝≤⎠⎞ + * 博客: https://www.cnblogs.com/JJBox/p/15650770.html + */ + +public delegate void WorldDrawEvent(WorldDraw draw); +public class JigEx : DrawJig +{ + #region 成员 + /// + /// 事件:亮显/暗显会被刷新冲刷掉,所以这个事件用于补充非刷新的工作 + /// + event WorldDrawEvent? WorldDrawEvent; + /// + /// 最后的鼠标点,用来确认长度 + /// + public Point3d MousePointWcsLast; + /// + /// 最后的图元,用来生成 + /// + public Entity[] Entitys => _drawEntitys.ToArray(); + + + readonly Action>? _mouseAction; + readonly Tolerance _tolerance;//容差 + + readonly Queue _drawEntitys;//重复生成的图元,放在这里刷新 + JigPromptPointOptions? _options;//jig鼠标配置 + bool _worldDrawFlag = false; // 20220503 + + bool _systemVariables_Orthomode = false; + bool SystemVariables_Orthomode // 正交修改还原 + { + get => _systemVariables_Orthomode; + set + { + //1正交,0非正交 //setvar: https://www.cnblogs.com/JJBox/p/10209541.html + if (Env.OrthoMode != value) + Env.OrthoMode = _systemVariables_Orthomode = value; + } + } + #endregion + + #region 构造 + /// + /// 在界面绘制图元 + /// + private JigEx() + { + _drawEntitys = new(); + } + + /// + /// 在界面绘制图元 + /// + /// + /// 用来频繁执行的回调: + /// 鼠标点; + /// 加入新建的图元,鼠标采样期间会Dispose图元的;所以已经在数据库图元利用事件加入,不要在此加入; + /// + /// 鼠标移动的容差 + public JigEx(Action>? action = null, double tolerance = 1e-6) : this() + { + _mouseAction = action; + _tolerance = new(tolerance, tolerance); + } + #endregion + + #region 方法 + /// + /// 鼠标配置:基点 + /// + /// 基点 + /// 光标绑定 + /// 提示信息 + /// 正交开关 + public JigPromptPointOptions SetOptions(Point3d basePoint, + CursorType cursorType = CursorType.RubberBand, + string msg = "点选第二点", + bool orthomode = false) + { + if (orthomode) + SystemVariables_Orthomode = true; + + _options = JigPointOptions(); + _options.Message = Environment.NewLine + msg; + _options.Cursor = cursorType; //光标绑定 + _options.UseBasePoint = true; //基点打开 + _options.BasePoint = basePoint; //基点设定 + return _options; + } + + /// + /// 鼠标配置:提示信息,关键字 + /// + /// 信息 + /// 关键字 + /// 正交开关 + /// + public JigPromptPointOptions SetOptions(string msg, + Dictionary? keywords = null, + bool orthomode = false) + { + if (orthomode) + SystemVariables_Orthomode = true; + + _options = JigPointOptions(); + _options.Message = Environment.NewLine + msg; + + //加入关键字,加入时候将空格内容放到最后 + string spaceValue = string.Empty; + const string spaceKey = " "; + + if (keywords != null) + foreach (var item in keywords) + if (item.Key == spaceKey) + spaceValue = item.Value; + else + _options.Keywords.Add(item.Key, item.Key, item.Value); + + ///因为默认配置函数导致此处空格触发是无效的, + ///但是用户如果想触发,就需要在外部减去默认UserInputControls配置 + ///要放最后,才能优先触发其他关键字 + if (spaceValue != string.Empty) + _options.Keywords.Add(spaceKey, spaceKey, spaceValue); + else + _options.Keywords.Add(spaceKey, spaceKey, "<空格退出>"); + + // 外部设置减去配置 + // _options.UserInputControls = + // _options.UserInputControls + // ^ UserInputControls.NullResponseAccepted //输入了鼠标右键,结束jig + // ^ UserInputControls.AnyBlankTerminatesInput; //空格或回车,结束jig; + return _options; + } + + /// + /// 鼠标配置:自定义 + /// + /// + /// 正交开关 + public void SetOptions(Action action, bool orthomode = false) + { + if (orthomode) + SystemVariables_Orthomode = true; + + _options = new JigPromptPointOptions(); + action.Invoke(_options); + } + + /// + /// 执行 + /// + /// + public PromptResult Drag() + { + //jig功能必然是当前前台文档,所以封装内部更好调用 + var dm = Acap.DocumentManager; + var doc = dm.MdiActiveDocument; + var ed = doc.Editor; + var dr = ed.Drag(this); + + if (SystemVariables_Orthomode) + SystemVariables_Orthomode = !SystemVariables_Orthomode; + return dr; + } + + /// + /// 最后一次的图元加入数据库 + /// + /// 加入此空间 + /// 不生成的图元用于排除,例如刷新时候的提示文字 + /// 加入数据库的id集合 + public IEnumerable? AddEntityToMsPs(BlockTableRecord btrOfAddEntitySpace, + IEnumerable? removeEntity = null) + { + //内部用 _drawEntitys 外部用 Entitys,减少一层转换 + if (_drawEntitys.Count == 0) + return null; + + IEnumerable es = _drawEntitys; + if (removeEntity != null) + es = es.Except(removeEntity);//差集 + + return btrOfAddEntitySpace.AddEntity(es); + } + #endregion + + #region 重写 + /// + /// 鼠标采样器 + /// + /// + /// 返回状态:令频繁刷新结束 + protected override SamplerStatus Sampler(JigPrompts prompts) + { + if (_worldDrawFlag) + return SamplerStatus.NoChange;//OK的时候拖动鼠标与否都不出现图元 + + if (_options is null) + throw new NullReferenceException(nameof(_options)); + + var pro = prompts.AcquirePoint(_options); + if (pro.Status == PromptStatus.Keyword) + return SamplerStatus.OK; + else if (pro.Status != PromptStatus.OK) + return SamplerStatus.Cancel; + + //上次鼠标点不同(一定要这句,不然图元刷新太快会看到奇怪的边线) + var mousePointWcs = pro.Value; + + //== 是比较类字段,但是最好转为哈希比较. + //IsEqualTo 是方形判断(仅加法),但是cad是距离. + //Distance 是圆形判断(会求平方根,使用了牛顿迭代), + //大量数据(十万以上/频繁刷新)面前会显得非常慢. + if (mousePointWcs.IsEqualTo(MousePointWcsLast, _tolerance)) + return SamplerStatus.NoChange; + + //上次循环的缓冲区图元清理,否则将会在vs输出遗忘 Dispose + while (_drawEntitys.Count > 0) + _drawEntitys.Dequeue().Dispose(); + + //委托把容器扔出去接收新创建的图元,然后给重绘更新 + _mouseAction?.Invoke(mousePointWcs, _drawEntitys); + MousePointWcsLast = mousePointWcs; + + return SamplerStatus.OK; + } + + /// + /// 重绘已在数据库的图元 + /// 0x01 此处不加入newEntity的,它们在构造函数的参数回调处加入,它们会进行频繁new和Dispose从而避免遗忘释放 + /// 0x02 此处用于重绘已经在数据的图元 + /// 0x03 此处用于图元亮显暗显,因为会被重绘冲刷掉所以独立出来不重绘,它们也往往已经存在数据库的 + /// + /// + /// newEntity只会存在一个图元队列中,而数据库图元可以分多个集合 + /// 例如: 集合A亮显时 集合B暗显/集合B亮显时 集合A暗显,所以我没有设计多个"数据库图元集合"存放,而是由用户在构造函数外自行创建 + /// + /// + public void DatabaseEntityDraw(WorldDrawEvent action) + { + WorldDrawEvent = action; + } + + /* WorldDraw 封装外的操作说明: + * 0x01 + * 我有一个业务是一次性生成四个方向的箭头,因为cad08缺少瞬时图元, + * 那么可以先提交一次事务,再开一个事务,把Entity传给jig,最后选择删除部分. + * 虽然这个是可行的方案,但是Entity穿越事务本身来说是非必要不使用的. + * 0x02 + * 四个箭头最近鼠标的亮显,其余淡显, + * 在jig使用淡显ent.Unhighlight和亮显ent.Highlight() + * 需要绕过重绘,否则重绘将导致图元频闪,令这两个操作失效, + * 此时需要自定义一个集合 EntityList (不使用本函数的_drawEntitys) + * 再将 EntityList 传给 WorldDrawEvent 事件,事件内实现亮显和淡显(事件已经利用 DatabaseEntityDraw函数进行提供). + * 0x03 + * draw.Geometry.Draw(_drawEntitys[i]); + * 此函数有问题,acad08克隆一份数组也可以用来刷新, + * 而arx上面的jig只能一次改一个,所以可以用此函数. + * 起因是此函数属于异步刷新, + * 同步上下文的刷新是 RawGeometry + * 0x04 + * cad22测试出现,08不会, + * draw.RawGeometry.Draw(ent);会跳到 Sampler(),所以设置 _worldDrawFlag + * 但是禁止重绘重入的话(令图元不频繁重绘),那么鼠标停着的时候就看不见图元, + * 所以只能重绘结束的时候才允许鼠标采集,采集过程的时候不会触发重绘, + * 这样才可以保证容器在重绘中不被更改. + */ + /// + /// 重绘图形 + /// + protected override bool WorldDraw(WorldDraw draw) + { + _worldDrawFlag = true; + WorldDrawEvent?.Invoke(draw); + _drawEntitys.ForEach(ent => { + draw.RawGeometry.Draw(ent); + }); + _worldDrawFlag = false; + return true; + } + #endregion + + /// + /// 用户输入控制默认配置 + /// 令jig.Drag().Status == + /// + /// + static JigPromptPointOptions JigPointOptions() + { + return new JigPromptPointOptions() + { + UserInputControls = + UserInputControls.GovernedByUCSDetect //由UCS探测用 + | UserInputControls.Accept3dCoordinates //接受三维坐标 + | UserInputControls.NullResponseAccepted //输入了鼠标右键,结束jig + | UserInputControls.AnyBlankTerminatesInput //空格或回车,结束jig; + }; + } + + /// + /// 空格默认是, + /// 将它设置为 + /// + public void SetSpaceIsKeyword() + { + var opt = _options; + if (opt == null) + throw new ArgumentNullException(nameof(_options)); + + if ((opt.UserInputControls & UserInputControls.NullResponseAccepted) == UserInputControls.NullResponseAccepted) + opt.UserInputControls ^= UserInputControls.NullResponseAccepted; //输入了鼠标右键,结束jig + if ((opt.UserInputControls & UserInputControls.AnyBlankTerminatesInput) == UserInputControls.AnyBlankTerminatesInput) + opt.UserInputControls ^= UserInputControls.AnyBlankTerminatesInput; //空格或回车,结束jig + } +} + +#if false +| UserInputControls.DoNotEchoCancelForCtrlC //不要取消CtrlC的回音 +| UserInputControls.DoNotUpdateLastPoint //不要更新最后一点 +| UserInputControls.NoDwgLimitsChecking //没有Dwg限制检查 +| UserInputControls.NoZeroResponseAccepted //接受非零响应 +| UserInputControls.NoNegativeResponseAccepted //不否定回复已被接受 +| UserInputControls.Accept3dCoordinates //返回点的三维坐标,是转换坐标系了? +| UserInputControls.AcceptMouseUpAsPoint //接受释放按键时的点而不是按下时 + +| UserInputControls.InitialBlankTerminatesInput //初始 空格或回车,结束jig +| UserInputControls.AcceptOtherInputString //接受其他输入字符串 +| UserInputControls.NoZDirectionOrtho //无方向正射,直接输入数字时以基点到当前点作为方向 +| UserInputControls.UseBasePointElevation //使用基点高程,基点的Z高度探测 +#endif \ No newline at end of file diff --git a/src/IFoxCAD.Cad/ExtensionMethod/ObjEx.cs b/src/IFoxCAD.Cad/ExtensionMethod/ObjEx.cs new file mode 100644 index 0000000..c831959 --- /dev/null +++ b/src/IFoxCAD.Cad/ExtensionMethod/ObjEx.cs @@ -0,0 +1,21 @@ +namespace IFoxCAD.Cad; + +public static class ObjEx +{ + /// + /// cad的打印 + /// + /// + public static void Print(this object obj) + { + Application.DocumentManager.MdiActiveDocument.Editor.WriteMessage($"{obj}\n"); + } + /// + /// 系统的打印 + /// + /// + public static void PrintLine(this object obj) + { + Console.WriteLine(obj.ToString()); + } +} diff --git a/src/IFoxCAD.Cad/ExtensionMethod/ObjectIdEx.cs b/src/IFoxCAD.Cad/ExtensionMethod/ObjectIdEx.cs index 2a89bd5..52c9261 100644 --- a/src/IFoxCAD.Cad/ExtensionMethod/ObjectIdEx.cs +++ b/src/IFoxCAD.Cad/ExtensionMethod/ObjectIdEx.cs @@ -1,70 +1,89 @@ -using Autodesk.AutoCAD.DatabaseServices; -using Autodesk.AutoCAD.Runtime; -using System.Collections.Generic; -using System.Linq; +namespace IFoxCAD.Cad; -namespace IFoxCAD.Cad +/// +/// 对象id扩展类 +/// +public static class ObjectIdEx { + #region GetObject + /// - /// 对象id扩展类 + /// 获取指定类型对象 /// - public static class ObjectIdEx + /// 指定的泛型 + /// 对象id + /// 事务 + /// 打开模式 + /// 打开删除对象 + /// 指定类型对象 + public static T? GetObject(this ObjectId id, OpenMode mode = OpenMode.ForRead, bool openErased = false, Transaction? tr = default) where T : DBObject { - #region GetObject - - /// - /// 获取指定类型对象 - /// - /// 指定的泛型 - /// 对象id - /// 事务 - /// 打开模式 - /// 打开删除对象 - /// 指定类型对象 - public static T GetObject(this ObjectId id, - OpenMode mode = OpenMode.ForRead, bool openErased = false, Transaction tr = default) where T : DBObject - { - tr ??= DBTrans.Top.Transaction; - return tr.GetObject(id, mode, openErased) as T; - } - - /// - /// 获取指定类型对象集合 - /// - /// 指定的泛型 - /// 对象id集合 - /// 事务 - /// 打开模式 - /// 打开删除对象 - /// 指定类型对象集合 - public static IEnumerable GetObject(this IEnumerable ids, - OpenMode mode = OpenMode.ForRead, bool openErased = false, Transaction tr = default) where T : DBObject - { - return ids.Select(id => id.GetObject(mode, openErased, tr)); - } + tr ??= DBTrans.Top.Transaction; + //tr = Env.GetTrans(tr); + return tr.GetObject(id, mode, openErased) as T; + } - /// - /// 返回符合类型的对象id - /// - /// 对象类型 - /// 对象id集合 - /// 对象id集合 - public static IEnumerable OfType(this IEnumerable ids) where T : DBObject - { - string dxfName = RXClass.GetClass(typeof(T)).DxfName; - return ids.Where(id => id.ObjectClass().DxfName == dxfName); - } + /// + /// 获取指定类型对象集合 + /// + /// 指定的泛型 + /// 对象id集合 + /// 事务 + /// 打开模式 + /// 打开删除对象 + /// 指定类型对象集合 + public static IEnumerable GetObject(this IEnumerable ids, OpenMode mode = OpenMode.ForRead, bool openErased = false, Transaction? tr = default) where T : DBObject + { + return ids.Select(id => id.GetObject(mode, openErased, tr)); + } - //Acad08缺少 id.ObjectClass 如何补偿? - public static RXClass ObjectClass(this ObjectId id) - { + /// + /// 返回符合类型的对象id + /// + /// 对象类型 + /// 对象id集合 + /// 对象id集合 + public static IEnumerable OfType(this IEnumerable ids) where T : DBObject + { + string dxfName = RXClass.GetClass(typeof(T)).DxfName; + return ids.Where(id => id.ObjectClass().DxfName == dxfName); + } + #endregion GetObject + + //Acad08缺少 id.ObjectClass 如何补偿? + public static RXClass ObjectClass(this ObjectId id) + { #if NET35 - return RXClass.GetClass(id.GetType()); + return RXClass.GetClass(id.GetType()); #else - return id.ObjectClass; + return id.ObjectClass; #endif - } - #endregion GetObject + } + + /// + /// id是否有效,未被删除 + /// + /// 对象id + /// id有效返回 ,反之返回 + public static bool IsOk(this ObjectId id) + { + return !id.IsNull && id.IsValid && !id.IsErased && !id.IsEffectivelyErased && id.IsResident; + } + /// + /// 删除id代表的对象 + /// + /// 对象id + public static void Erase(this ObjectId id) + { + if (id.IsOk()) + { + var ent = id.GetObject()!; + using (ent.ForWrite()) + { + ent.Erase(); + }// 第一种读写权限自动转换写法 + //Env.Editor.Regen(); + } } } diff --git a/src/IFoxCAD.Cad/ExtensionMethod/PointEx.cs b/src/IFoxCAD.Cad/ExtensionMethod/PointEx.cs new file mode 100644 index 0000000..4898d50 --- /dev/null +++ b/src/IFoxCAD.Cad/ExtensionMethod/PointEx.cs @@ -0,0 +1,134 @@ +namespace IFoxCAD.Cad; + +public static class PointEx +{ + + /// + /// 获取点的hash字符串,同时可以作为pt的字符串表示 + /// + /// 点 + /// 指示计算几维坐标的标志,1为计算x,2为计算x,y,其他为计算x,y,z + /// 保留的小数位数 + /// hash字符串 + public static string GetHashString(this Point3d pt, int xyz = 3, int decimalRetain = 6) + { + var de = $"f{decimalRetain}"; + return xyz switch + { + 1 => $"({pt.X.ToString(de)})", + 2 => $"({pt.X.ToString(de)},{pt.Y.ToString(de)})", + _ => $"({pt.X.ToString(de)},{pt.Y.ToString(de)},{pt.Z.ToString(de)})" + }; + } + + //为了频繁触发所以弄个全局变量 + static Plane? _Plane; + /// + /// 两点计算弧度范围0到2Pi + /// + /// 起点 + /// 终点 + /// 方向 + /// 弧度值 + public static double GetAngle(this Point3d startPoint, Point3d endPoint, Vector3d? direction = null) + { + if (direction != null) + _Plane = new Plane(Point3d.Origin, direction.Value); + if (_Plane == null) + _Plane = new Plane(Point3d.Origin, Vector3d.ZAxis); + return startPoint.GetVectorTo(endPoint).AngleOnPlane(_Plane); + } + /// + /// 两点计算弧度范围0到2Pi + /// + /// 起点 + /// 终点 + /// 弧度值 + public static double GetAngle(this Point2d startPoint, Point2d endPoint) + { + return startPoint.GetVectorTo(endPoint).Angle; + } + + /// + /// 获取中点 + /// + /// + /// + /// + public static Point2d GetCenter(this Point2d a, Point2d b) + { + // (p1 + p2) / 2; //溢出风险 + return new Point2d(a.X * 0.5 + b.X * 0.5, + a.Y * 0.5 + b.Y * 0.5); + } + + /// http://www.lee-mac.com/bulgeconversion.html + /// + /// 求凸度,判断三点是否一条直线上 + /// + /// 圆弧起点 + /// 圆弧腰点 + /// 圆弧尾点 + /// 逆时针为正,顺时针为负 + public static double GetArcBulge(this Point2d arc1, Point2d arc2, Point2d arc3, double tol = 1e-10) + { + double dStartAngle = arc2.GetAngle(arc1); + double dEndAngle = arc2.GetAngle(arc3); + //求的P1P2与P1P3夹角 + var talAngle = (Math.PI - dStartAngle + dEndAngle) / 2; + //凸度==拱高/半弦长==拱高比值/半弦长比值 + //有了比值就不需要拿到拱高值和半弦长值了,因为接下来是相除得凸度 + double bulge = Math.Sin(talAngle) / Math.Cos(talAngle); + + //处理精度 + if (bulge > 0.9999 && bulge < 1.0001) + bulge = 1; + else if (bulge < -0.9999 && bulge > -1.0001) + bulge = -1; + else if (Math.Abs(bulge) < tol) + bulge = 0; + return bulge; + } + + + #region 首尾相连 + /// + /// 首尾相连 + /// + public static Point2dCollection End2End(this Point2dCollection ptcol) + { + if (ptcol == null) + throw new ArgumentNullException(nameof(ptcol)); + + if (ptcol.Count == 0 || ptcol[0].Equals(ptcol[^1]))//首尾相同直接返回 + return ptcol; + + //首尾不同,去加一个到最后 + var lst = new Point2d[ptcol.Count + 1]; + for (int i = 0; i < lst.Length; i++) + lst[i] = ptcol[i]; + lst[^1] = lst[0]; + + return new(lst); + } + /// + /// 首尾相连 + /// + public static Point3dCollection End2End(this Point3dCollection ptcol) + { + if (ptcol == null) + throw new ArgumentNullException(nameof(ptcol)); + + if (ptcol.Count == 0 || ptcol[0].Equals(ptcol[^1]))//首尾相同直接返回 + return ptcol; + + //首尾不同,去加一个到最后 + var lst = new Point3d[ptcol.Count + 1]; + for (int i = 0; i < lst.Length; i++) + lst[i] = ptcol[i]; + lst[^1] = lst[0]; + + return new(lst); + } + #endregion +} \ No newline at end of file diff --git a/src/IFoxCAD.Cad/ExtensionMethod/SelectionSetEx.cs b/src/IFoxCAD.Cad/ExtensionMethod/SelectionSetEx.cs index 9ede888..dbde0ac 100644 --- a/src/IFoxCAD.Cad/ExtensionMethod/SelectionSetEx.cs +++ b/src/IFoxCAD.Cad/ExtensionMethod/SelectionSetEx.cs @@ -1,106 +1,101 @@ -using Autodesk.AutoCAD.DatabaseServices; -using Autodesk.AutoCAD.EditorInput; -using Autodesk.AutoCAD.Runtime; -using System; -using System.Collections.Generic; -using System.Linq; +namespace IFoxCAD.Cad; -namespace IFoxCAD.Cad +/// +/// 选择集扩展类 +/// +public static class SelectionSetEx { - + #region 获取对象id /// - /// 选择集扩展类 + /// 获取已选择的对象 /// - public static class SelectionSetEx + /// 选择集 + /// 已选择的对象集合 + public static IEnumerable GetSelectedObjects(this SelectionSet ss) { - #region 获取对象id - /// - /// 获取已选择的对象 - /// - /// 选择集 - /// 已选择的对象集合 - public static IEnumerable GetSelectedObjects(this SelectionSet ss) - { - return ss.Cast(); - } + return ss.Cast(); + } - /// - /// 获取已选择的对象 - /// - /// 已选择的对象泛型 - /// 选择集 - /// 已选择的对象集合 - public static IEnumerable GetSelectObjects(this SelectionSet ss) where T : SelectedObject - { - return ss.Cast().OfType(); - } + /// + /// 获取已选择的对象 + /// + /// 已选择的对象泛型 + /// 选择集 + /// 已选择的对象集合 + public static IEnumerable GetSelectObjects(this SelectionSet ss) where T : SelectedObject + { + return ss.Cast().OfType(); + } - /// - /// 从选择集中获取对象id - /// - /// 图元类型 - /// 选择集 - /// 已选择的对象id集合 - public static IEnumerable GetObjectIds(this SelectionSet ss) where T : Entity - { - string dxfName = RXClass.GetClass(typeof(T)).DxfName; - return - ss - .GetObjectIds() - .Where(id => id.ObjectClass().DxfName == dxfName); - } + /// + /// 从选择集中获取对象id + /// + /// 图元类型 + /// 选择集 + /// 已选择的对象id集合 + public static IEnumerable GetObjectIds(this SelectionSet ss) where T : Entity + { + string dxfName = RXClass.GetClass(typeof(T)).DxfName; + return + ss + .GetObjectIds() + .Where(id => id.ObjectClass().DxfName == dxfName); + } - /// - /// 将选择集的对象按类型分组 - /// - /// 选择集 - /// 分组后的类型/对象id集合 - public static IEnumerable> GetObjectIdGroup(this SelectionSet ss) - { - return - ss - .GetObjectIds() - .GroupBy(id => id.ObjectClass().DxfName); - } - #endregion + /// + /// 将选择集的对象按类型分组 + /// + /// 选择集 + /// 分组后的类型/对象id集合 + public static IEnumerable> GetObjectIdGroup(this SelectionSet ss) + { + return + ss + .GetObjectIds() + .GroupBy(id => id.ObjectClass().DxfName); + } + #endregion - #region 获取实体对象 + #region 获取实体对象 - /// - /// 获取指定类型图元 - /// - /// 指定类型 - /// 选择集 - /// 事务 - /// 打开模式 - /// 图元集合 - public static IEnumerable GetEntities(this SelectionSet ss, OpenMode openMode = OpenMode.ForRead, Transaction tr = default) where T : Entity - { - return - ss - .GetObjectIds() - .Select(id => tr.GetObject(id, openMode) as T); - } + /// + /// 获取指定类型图元 + /// + /// 指定类型 + /// 选择集 + /// 事务 + /// 打开模式 + /// 图元集合 + public static IEnumerable GetEntities(this SelectionSet ss, OpenMode openMode = OpenMode.ForRead, DBTrans? tr = default) where T : Entity + { + if (ss is null) + throw new ArgumentNullException(nameof(ss)); - #endregion + tr ??= DBTrans.Top; + return + ss + .GetObjectIds() + .Select(id => tr.GetObject(id, openMode)) + .Where(ent => ent != null); + } - #region ForEach + #endregion - /// - /// 遍历选择集 - /// - /// 指定图元类型 - /// 选择集 - /// 事务 - /// 打开模式 - /// 处理函数 - public static void ForEach(this SelectionSet ss, Action action, OpenMode openMode = OpenMode.ForRead, Transaction tr = default) where T : Entity + #region ForEach + /// + /// 遍历选择集 + /// + /// 指定图元类型 + /// 选择集 + /// 事务 + /// 打开模式 + /// 处理函数 + public static void ForEach(this SelectionSet ss, Action action, OpenMode openMode = OpenMode.ForRead, DBTrans? tr = default) where T : Entity + { + foreach (T? ent in ss.GetEntities(openMode, tr)) { - foreach (T ent in ss.GetEntities(openMode, tr)) - { - action?.Invoke(ent); - } + action?.Invoke(ent); } - #endregion } + #endregion } diff --git a/src/IFoxCAD.Cad/ExtensionMethod/SymbolTableEx.cs b/src/IFoxCAD.Cad/ExtensionMethod/SymbolTableEx.cs index 327f8ba..3c0ace3 100644 --- a/src/IFoxCAD.Cad/ExtensionMethod/SymbolTableEx.cs +++ b/src/IFoxCAD.Cad/ExtensionMethod/SymbolTableEx.cs @@ -1,81 +1,73 @@ -using System; -using System.Collections.Generic; -using System.IO; -using System.Linq; -using System.Text; +namespace IFoxCAD.Cad; - -using Autodesk.AutoCAD.Colors; -using Autodesk.AutoCAD.DatabaseServices; -using Autodesk.AutoCAD.Geometry; - -namespace IFoxCAD.Cad +/// +/// 符号表类扩展函数 +/// +public static class SymbolTableEx { + #region 图层表 + /// + /// 添加图层 + /// + /// 图层符号表 + /// 图层名 + /// 图层颜色 + /// 图层id + public static ObjectId Add(this SymbolTable table, string name, Color color) + { + return table.Add(name, lt => lt.Color = color); + } /// - /// 符号表类扩展函数 + /// 添加图层 /// - public static class SymbolTableEx + /// 图层符号表 + /// 图层名 + /// 图层颜色索引值 + /// 图层id + public static ObjectId Add(this SymbolTable table, string name, int colorIndex) { - #region 图层表 - /// - /// 添加图层 - /// - /// 图层符号表 - /// 图层名 - /// 图层颜色 - /// 图层id - public static ObjectId Add(this SymbolTable table, string name, Color color) + colorIndex %= 256;//防止输入的颜色超出256 + colorIndex = Math.Abs(colorIndex);//防止负数 + return table.Add(name, lt => lt.Color = Color.FromColorIndex(ColorMethod.ByColor, (short)colorIndex)); + } + /// + /// 更改图层名 + /// + /// 图层符号表 + /// 旧图层名 + /// 新图层名 + public static ObjectId Rename(this SymbolTable table, string Oldname, string NewName) + { + if (table.Has(Oldname)) { - return table.Add(name, lt => lt.Color = color); + table.Change(Oldname, ly => + { + ly.Name = NewName; + } + ); + return table[NewName]; } - /// - /// 添加图层 - /// - /// 图层符号表 - /// 图层名 - /// 图层颜色索引值 - /// 图层id - public static ObjectId Add(this SymbolTable table, string name, int colorIndex) + else { - colorIndex %= 256; //防止输入的颜色超出256 - colorIndex = Math.Abs(colorIndex);//防止负数 - return table.Add(name, lt => lt.Color = Color.FromColorIndex(ColorMethod.ByColor, (short)colorIndex)); + return ObjectId.Null; } - /// - /// 更改图层名 - /// - /// 图层符号表 - /// 旧图层名 - /// 新图层名 - public static ObjectId Rename(this SymbolTable table, string Oldname, string NewName) + } + /// + /// 删除图层 + /// + /// 层表 + /// 图层名 + /// 成功返回 ,失败返回 + public static bool Delete(this SymbolTable table, string name) + { + if (name == "0" || name == "Defpoints" || !table.Has(name) || table[name] == DBTrans.Top.Database.Clayer) { - if (table.Has(Oldname)) - { - table.Change(Oldname, ly => { - ly.Name = NewName; - } - ); - return table[NewName]; - } - else - { - return ObjectId.Null; - } + return false; } - /// - /// 删除图层 - /// - /// 层表 - /// 图层名 - /// 成功返回 ,失败返回 - public static bool Delete(this SymbolTable table, string name) + table.CurrentSymbolTable.GenerateUsageData(); + var ltr = table.GetRecord(name); + if (ltr is not null) { - if (name == "0" || name == "Defpoints" || !table.Has(name) || table[name] == DBTrans.Top.Database.Clayer) - { - return false; - } - table.CurrentSymbolTable.GenerateUsageData(); - var ltr = table.GetRecord(name); if (ltr.IsUsed) { return false; @@ -86,191 +78,303 @@ public static bool Delete(this SymbolTable table, } return true; } - #endregion - - #region 块表 - /// - /// 添加块定义 - /// - /// 块表 - /// 块名 - /// 对所添加块表的委托n - /// 添加图元的委托 - /// 添加属性定义的委托 - /// 块定义id - /// TODO: 需要测试匿名块等特殊的块是否能定义 - public static ObjectId Add(this SymbolTable table, string name, Action action = null, Func> ents = null, Func> attdef = null) - { - return table.Add(name, btr => { - action?.Invoke(btr); - var entsres = ents?.Invoke(); - if (entsres is not null) - { - btr.AddEntity(entsres); - } - var adddefres = attdef?.Invoke(); - if (adddefres is not null) - { - btr.AddEntity(adddefres); - } - //if (ents is not null) - //{ - // btr.AddEntity(ents?.Invoke()); - //} - //if (attdef is not null) - //{ - // btr.AddEntity(attdef?.Invoke()); - //} - }); - } - /// - /// 添加块定义 - /// - /// 块表 - /// 块名 - /// 图元 - /// 属性定义 - /// - public static ObjectId Add(this SymbolTable table, string name, IEnumerable ents = null, IEnumerable attdef = null) - { - return table.Add(name, null, () => { return ents; }, () => { return attdef; }); - } + return false; + } + #endregion - /// - /// 添加块定义 - /// - /// 块表 - /// 块名 - /// 图元(包括属性) - /// - public static ObjectId Add(this SymbolTable table, string name, params Entity[] ents) + #region 块表 + /// + /// 添加块定义 + /// + /// 块表 + /// 块名 + /// 对所添加块表的委托n + /// 添加图元的委托 + /// 添加属性定义的委托 + /// 块定义id + /// TODO: 需要测试匿名块等特殊的块是否能定义 + public static ObjectId Add(this SymbolTable table, string name, Action? action = null, Func>? ents = null, Func>? attdef = null) + { + return table.Add(name, btr => { - return table.Add(name, null, () => { return ents; }); - } + action?.Invoke(btr); + var entsres = ents?.Invoke(); + if (entsres is not null) + { + btr.AddEntity(entsres); + } + var adddefres = attdef?.Invoke(); + if (adddefres is not null) + { + btr.AddEntity(adddefres); + } - /// - /// 从文件中获取块定义 - /// - /// 块表 - /// 文件名 - /// 是否覆盖 - /// 块定义Id - public static ObjectId GetBlockFrom(this SymbolTable table, string fileName, bool over) + }); + } + /// + /// 添加块定义 + /// + /// 块表 + /// 块名 + /// 图元 + /// 属性定义 + /// + public static ObjectId Add(this SymbolTable table, string name, IEnumerable? ents = null, IEnumerable? attdef = null) + { + return table.Add(name, btr => { - //FileInfo fi = new(fileName); - //string blkdefname = fi.Name; - //if (blkdefname.Contains(".")) - //{ - // blkdefname = blkdefname.Substring(0, blkdefname.LastIndexOf('.')); - //} + if (ents is not null) + { + btr.AddEntity(ents); + } + if (attdef is not null) + { + btr.AddEntity(attdef); + } + }); + } - string blkdefname = SymbolUtilityServices.RepairSymbolName(SymbolUtilityServices.GetSymbolNameFromPathName(fileName, "dwg"), false); + /// + /// 添加块定义 + /// + /// 块表 + /// 块名 + /// 图元(包括属性) + /// + public static ObjectId Add(this SymbolTable table, string name, params Entity[] ents) + { + return table.Add(name, null, () => { return ents; }); + } - ObjectId id = table[blkdefname]; - bool has = id != ObjectId.Null; - if ((has && over) || !has) + /// + /// 添加属性到块定义 + /// + /// 块表 + /// 块定义id + /// 属性列表 + public static void AddAttsToBlocks(this SymbolTable table, ObjectId id, List atts) + { + table.Change(id, btr => + { + var attTags = new List(); + btr.GetEntities() + .ForEach(def => attTags.Add(def.Tag.ToUpper())); + foreach (AttributeDefinition att in atts) { - Database db = new(); - db.ReadDwgFile(fileName, FileShare.Read, true, null); - id = table.Database.Insert(BlockTableRecord.ModelSpace, blkdefname, db, false); + if (!attTags.Contains(att.Tag.ToUpper())) + { + btr.AddEntity(att); + } } + }); + } + /// + /// 添加属性到块定义 + /// + /// 块表 + /// 块定义名字 + /// 属性列表 + public static void AddAttsToBlocks(this SymbolTable table, string name, List atts) + { + table.Change(name, btr => + { + var attTags = new List(); + btr.GetEntities() + .ForEach(def => attTags.Add(def.Tag.ToUpper())); + foreach (AttributeDefinition att in atts) + { + if (!attTags.Contains(att.Tag.ToUpper())) + { + btr.AddEntity(att); + } + } + }); + } - return id; - } - + /// + /// 从文件中获取块定义 + /// + /// 块表 + /// 文件名 + /// 是否覆盖 + /// 块定义Id + public static ObjectId GetBlockFrom(this SymbolTable table, string fileName, bool over) + { + //FileInfo fi = new(fileName); + //string blkdefname = fi.Name; + //if (blkdefname.Contains(".")) + //{ + // blkdefname = blkdefname.Substring(0, blkdefname.LastIndexOf('.')); + //} + string blkdefname = SymbolUtilityServices.RepairSymbolName(SymbolUtilityServices.GetSymbolNameFromPathName(fileName, "dwg"), false); - /// - /// 从文件中获取块定义 - /// - /// 块表 - /// 文件名 - /// 块定义名 - /// 是否覆盖 - /// 块定义Id - public static ObjectId GetBlockFrom(this SymbolTable table, string fileName, string blockName, bool over) + ObjectId id = table[blkdefname]; + bool has = id != ObjectId.Null; + if ((has && over) || !has) { - return - table.GetRecordFrom( - t => t.BlockTable, - fileName, - blockName, - over); + Database db = new(false, true); + db.ReadDwgFile(fileName, FileShare.Read, true, null); + db.CloseInput(true); + id = table.Database.Insert(BlockTableRecord.ModelSpace, blkdefname, db, false); } - #endregion + + return id; + } - #region 线型表 - /// - /// 添加线型 - /// - /// 线型表 - /// 线型名 - /// 线型说明 - /// 线型长度 - /// 笔画长度数组 - /// 线型id - public static ObjectId Add(this SymbolTable table, string name, string description, double length, double[] dash) - { - return table.Add( - name, - ltt => { - ltt.AsciiDescription = description; - ltt.PatternLength = length; //线型的总长度 + + /// + /// 从文件中获取块定义 + /// + /// 块表 + /// 文件名 + /// 块定义名 + /// 是否覆盖 + /// 块定义Id + public static ObjectId GetBlockFrom(this SymbolTable table, string fileName, string blockName, bool over) + { + return + table.GetRecordFrom( + t => t.BlockTable, + fileName, + blockName, + over); + } + #endregion + + + #region 线型表 + /// + /// 添加线型 + /// + /// 线型表 + /// 线型名 + /// 线型说明 + /// 线型长度 + /// 笔画长度数组 + /// 线型id + public static ObjectId Add(this SymbolTable table, string name, string description, double length, double[] dash) + { + return table.Add( + name, + ltt => + { + ltt.AsciiDescription = description; + ltt.PatternLength = length; //线型的总长度 ltt.NumDashes = dash.Length; //组成线型的笔画数目 for (int i = 0; i < dash.Length; i++) - { - ltt.SetDashLengthAt(i, dash[i]); - } + { + ltt.SetDashLengthAt(i, dash[i]); + } //ltt.SetDashLengthAt(0, 0.5); //0.5个单位的划线 //ltt.SetDashLengthAt(1, -0.25); //0.25个单位的空格 //ltt.SetDashLengthAt(2, 0); // 一个点 //ltt.SetDashLengthAt(3, -0.25); //0.25个单位的空格 } - ); - } - #endregion + ); + } + #endregion - #region 文字样式表 - /// - /// 添加文字样式记录 - /// - /// 文字样式表 - /// 文字样式名 - /// 字体名 - /// 宽度比例 - /// 文字样式Id - public static ObjectId Add(this SymbolTable table, string textStyleName, string font, double xscale) + #region 文字样式表 + /// + /// 添加文字样式记录 + /// + /// 文字样式表 + /// 文字样式名 + /// 字体名 + /// 宽度比例 + /// 文字样式Id + public static ObjectId Add(this SymbolTable table, + string textStyleName, + string font, + double xscale = 1.0) + { + return + table.Add( + textStyleName, + tstr => + { + tstr.Name = textStyleName; + tstr.FileName = font; + tstr.XScale = xscale; + }); + } + /// + /// 添加文字样式记录 + /// + /// 文字样式表 + /// 文字样式名 + /// 字体名枚举 + /// 宽度比例 + /// 文字样式Id + public static ObjectId Add(this SymbolTable table, string textStyleName, FontTTF fontTTF, double xscale = 1.0) + { + return table.Add(textStyleName, fontTTF.GetDesc(), xscale); + } + + /// + ///

添加文字样式记录,如果存在就默认强制替换

+ /// 此函数为了 而设 + ///
+ /// 文字样式表 + /// 文字样式名 + /// 字体名 + /// 大字体名 + /// 宽度比例 + /// 高度 + /// 是否强制替换 + /// 文字样式Id + public static ObjectId AddWithChange(this SymbolTable table, + string textStyleName, + string smallFont, + string bigFont = "", + double xScale = 1, + double height = 0, + bool forceChange = true) + { + if (forceChange && table.Has(textStyleName)) { - return - table.Add( - textStyleName, - tstr => { - tstr.Name = textStyleName; - tstr.FileName = font; - tstr.XScale = xscale; - }); + table.Change(textStyleName, ttr => + { + ttr.FileName = smallFont; + ttr.XScale = xScale; + ttr.TextSize = height; + if (bigFont != "") + { + ttr.BigFontFileName = bigFont; + } + }); + return table[textStyleName]; } - #endregion + return table.Add(textStyleName, ttr => + { + ttr.FileName = smallFont; + ttr.XScale = xScale; + ttr.TextSize = height; + }); + } - #region 注册应用程序表 - #endregion + #endregion - #region 标注样式表 + #region 注册应用程序表 - #endregion + #endregion - #region 用户坐标系表 + #region 标注样式表 - #endregion + #endregion - #region 视图表 + #region 用户坐标系表 - #endregion + #endregion - #region 视口表 + #region 视图表 - #endregion - } + #endregion + + #region 视口表 + + #endregion } diff --git a/src/IFoxCAD.Cad/ExtensionMethod/SymbolTableRecordEx.cs b/src/IFoxCAD.Cad/ExtensionMethod/SymbolTableRecordEx.cs index c66b2e2..bceeac0 100644 --- a/src/IFoxCAD.Cad/ExtensionMethod/SymbolTableRecordEx.cs +++ b/src/IFoxCAD.Cad/ExtensionMethod/SymbolTableRecordEx.cs @@ -1,353 +1,460 @@ -using System; -using System.Collections.Generic; -using System.IO; -using System.Linq; -using System.Text; +namespace IFoxCAD.Cad; - -using Autodesk.AutoCAD.Colors; -using Autodesk.AutoCAD.DatabaseServices; -using Autodesk.AutoCAD.Geometry; -using Autodesk.AutoCAD.Runtime; - -namespace IFoxCAD.Cad +/// +/// 符号表记录扩展类 +/// +public static class SymbolTableRecordEx { + #region 块表记录 + + #region 克隆实体id /// - /// 符号表记录扩展类 + /// 深度克隆id到块表记录 + /// 0x01 此方法不允许是未添加数据库的图元,因此它是id + /// 0x02 若为未添加数据库图元,则利用entity.Clone();同时不需要考虑动态块属性,可以使用entity.GetTransformedCopy /// - public static class SymbolTableRecordEx + /// + /// 克隆到当前块表记录,相当于原地克隆 + /// 克隆到目标块表记录内,相当于制作新块 + /// + /// 图元id集合,注意所有成员都要在同一个空间中 + /// 克隆后的id词典 + public static IdMapping DeepClone(this BlockTableRecord btr, ObjectIdCollection objIds) { - - - #region 块表记录 - - #region 添加实体 - /// - /// 添加实体对象 - /// - /// 块表记录 - /// 实体 - /// 事务管理器 - /// 对象 id - public static ObjectId AddEntity(this BlockTableRecord btr, Entity entity, Transaction tr = null) + if (objIds is null || objIds.Count == 0) + throw new ArgumentNullException(nameof(objIds)); + + var db = objIds[0].Database; + IdMapping mapping = new(); + using (btr.ForWrite()) { - if (entity is null) - throw new ArgumentNullException(nameof(entity), "对象为 null"); + try + { + db.DeepCloneObjects(objIds, btr.ObjectId, mapping, false); - ObjectId id; - tr ??= DBTrans.Top.Transaction; - using (btr.ForWrite()) + // 不在此提取,为了此函数被高频调用 + // 获取克隆键值对(旧块名,新块名) + // foreach (ObjectId item in blockIds) + // result.Add(mapping[item].Value); + } + catch (System.Exception e) { - id = btr.AppendEntity(entity); - tr.AddNewlyCreatedDBObject(entity, true); + LogHelper.FlagOutVsOutput = true; + e.WriteLog("深度克隆出错了"); } - return id; } + return mapping; + } - /// - /// 添加实体集合 - /// - /// 实体类型 - /// 块表记录 - /// 事务 - /// 实体集合 - /// 对象 id 列表 - public static IEnumerable AddEntity(this BlockTableRecord btr, IEnumerable ents, Transaction tr = null) where T : Entity - { - if (ents.Any(ent => ent is null)) - throw new ArgumentNullException(nameof(ents), "实体集合内存在 null 对象"); + /// + /// 克隆图元实体(这个函数有问题,会出现偶尔成功,偶尔失败,拖动过变成匿名块) + /// 若为块则进行设置属性,因此控制动态块属性丢失; + /// + /// 图元 + /// 矩阵 + //public static void EntityTransformedCopy(this Entity ent, Matrix3d matrix) + //{ + // var entNew = ent.GetTransformedCopy(matrix); + // if (ent is BlockReference blockReference) + // entNew.SetPropertiesFrom(blockReference); + //} - tr ??= DBTrans.Top.Transaction; - using (btr.ForWrite()) - { - return ents - .Select( - ent => { - ObjectId id = btr.AppendEntity(ent); - tr.AddNewlyCreatedDBObject(ent, true); - return id; - }) - .ToList(); - } - } - /// - /// 添加多个实体 - /// - /// 块表记录 - /// 实体集合 - /// 对象 id 列表 - public static IEnumerable AddEntity(this BlockTableRecord btr, params Entity[] ents) - { - return btr.AddEntity(ents, null); - } - #endregion + #endregion - #region 添加图元 - /// - /// 在指定绘图空间添加图元 - /// - /// 图元类型 - /// 绘图空间 - /// 图元对象 - /// 图元属性设置委托 - /// 事务管理器 - /// 图元id - private static ObjectId AddEnt(this BlockTableRecord btr, T ent, Action action, Transaction trans) where T : Entity - { - trans ??= DBTrans.Top.Transaction; - action?.Invoke(ent); - return btr.AddEntity(ent, trans); - } + #region 添加实体 + /// + /// 添加实体对象 + /// + /// 块表记录 + /// 实体 + /// 事务管理器 + /// 对象 id + public static ObjectId AddEntity(this BlockTableRecord btr, Entity entity, + Transaction? trans = null) + { + //if (entity is null) + // throw new ArgumentNullException(nameof(entity), "对象为 null"); - /// - /// 在指定绘图空间添加直线 - /// - /// 事务管理器 - /// 起点 - /// 终点 - /// 绘图空间 - /// 直线属性设置委托 - /// 直线的id - public static ObjectId AddLine(this BlockTableRecord btr, Point3d start, Point3d end, Action action = default, Transaction trans = default) + ObjectId id; + trans ??= DBTrans.Top.Transaction; + using (btr.ForWrite()) { - var line = new Line(start, end); - return btr.AddEnt(line, action, trans); + id = btr.AppendEntity(entity); + trans.AddNewlyCreatedDBObject(entity, true); } - /// - /// 在指定绘图空间X-Y平面添加圆 - /// - /// 绘图空间 - /// 圆心 - /// 半径 - /// 圆属性设置委托 - /// 事务管理器 - /// 圆的id - public static ObjectId AddCircle(this BlockTableRecord btr, Point3d center, double radius, Action action = default, Transaction trans = default) + return id; + } + + /// + /// 添加实体集合 + /// + /// 实体类型 + /// 块表记录 + /// 实体集合 + /// 事务 + /// 对象 id 列表 + public static IEnumerable AddEntity(this BlockTableRecord btr, IEnumerable ents, + Transaction? trans = null) where T : Entity + { + //if (ents.Any(ent => ent is null)) + // throw new ArgumentNullException(nameof(ents), "实体集合内存在 null 对象"); + + trans ??= DBTrans.Top.Transaction; + using (btr.ForWrite()) { - var circle = new Circle(center, Vector3d.ZAxis, radius); - return btr.AddEnt(circle, action, trans); + return ents + .Select( + ent => { + ObjectId id = btr.AppendEntity(ent); + trans.AddNewlyCreatedDBObject(ent, true); + return id; + }) + .ToList(); } + } - /// - /// 在指定绘图空间X-Y平面3点画外接圆 - /// - /// 绘图空间 - /// 第一点 - /// 第二点 - /// 第三点 - /// 圆属性设置委托 - /// 事务管理器 - /// 三点有外接圆则返回圆的id,否则返回ObjectId.Null - public static ObjectId AddCircle(this BlockTableRecord btr, Point3d p0, Point3d p1, Point3d p2, - Action action = default, Transaction trans = default) + /// + /// 添加多个实体 + /// + /// 块表记录 + /// 实体集合 + /// 对象 id 列表 + public static IEnumerable AddEntity(this BlockTableRecord btr, params Entity[] ents) + { + return btr.AddEntity(ents, null); + } + #endregion + + #region 添加图元 + /// + /// 在指定绘图空间添加图元 + /// + /// 图元类型 + /// 绘图空间 + /// 图元对象 + /// 图元属性设置委托 + /// 事务管理器 + /// 图元id + private static ObjectId AddEnt(this BlockTableRecord btr, T ent, Action? action, Transaction? trans) where T : Entity + { + //trans ??= DBTrans.Top.Transaction; + action?.Invoke(ent); + return btr.AddEntity(ent, trans); + } + /// + /// 委托式的添加图元 + /// + /// 块表 + /// 返回图元的委托 + /// 事务 + /// 图元id,如果委托返回 null,则为 ObjectId.Null + public static ObjectId AddEnt(this BlockTableRecord btr, Func action, Transaction? transaction) + { + //transaction ??= DBTrans.Top.Transaction; + var ent = action.Invoke(); + if (ent is null) + return ObjectId.Null; + + return btr.AddEntity(ent, transaction); + } + + /// + /// 在指定绘图空间添加直线 + /// + /// 事务管理器 + /// 起点 + /// 终点 + /// 绘图空间 + /// 直线属性设置委托 + /// 直线的id + public static ObjectId AddLine(this BlockTableRecord btr, Point3d start, Point3d end, + Action? action = default, Transaction? trans = default) + { + var line = new Line(start, end); + return btr.AddEnt(line, action, trans); + } + /// + /// 在指定绘图空间X-Y平面添加圆 + /// + /// 绘图空间 + /// 圆心 + /// 半径 + /// 圆属性设置委托 + /// 事务管理器 + /// 圆的id + public static ObjectId AddCircle(this BlockTableRecord btr, Point3d center, double radius, + Action? action = default, Transaction? trans = default) + { + var circle = new Circle(center, Vector3d.ZAxis, radius); + return btr.AddEnt(circle, action, trans); + } + + /// + /// 在指定绘图空间X-Y平面3点画外接圆 + /// + /// 绘图空间 + /// 第一点 + /// 第二点 + /// 第三点 + /// 圆属性设置委托 + /// 事务管理器 + /// 三点有外接圆则返回圆的id,否则返回ObjectId.Null + public static ObjectId AddCircle(this BlockTableRecord btr, Point3d p0, Point3d p1, Point3d p2, + Action? action = default, Transaction? trans = default) + { + var circle = EntityEx.CreateCircle(p0, p1, p2); + //return circle is not null ? btr.AddEnt(circle, action, trans) : throw new ArgumentNullException(nameof(circle), "对象为 null"); + if (circle is null) + throw new ArgumentNullException(nameof(circle), "对象为 null"); + + return btr.AddEnt(circle, action, trans); + } + /// + /// 在指定的绘图空间添加轻多段线 + /// + /// 绘图空间 + /// 多段线信息 + /// 线宽 + /// 是否闭合 + /// 轻多段线属性设置委托 + /// 事务管理器 + /// 轻多段线id + public static ObjectId AddPline(this BlockTableRecord btr, + List bvws, + double? constantWidth = null, + bool isClosed = true, + Action? action = default, + Transaction? trans = default) + { + Polyline pl = new(); + pl.SetDatabaseDefaults(); + if (constantWidth is not null) { - Circle circle = EntityEx.CreateCircle(p0, p1, p2); - return circle is not null ? btr.AddEnt(circle, action, trans) : throw new ArgumentNullException(nameof(circle), "对象为 null"); + for (int i = 0; i < bvws.Count; i++) + pl.AddVertexAt(i, bvws[i].Vertex, bvws[i].Bulge, constantWidth.Value, constantWidth.Value); } - /// - /// 在指定的绘图空间添加轻多段线 - /// - /// 绘图空间 - /// 端点表 - /// 凸度表 - /// 端点的起始宽度 - /// 端点的终止宽度 - /// 轻多段线属性设置委托 - /// 事务管理器 - /// 轻多段线id - public static ObjectId AddPline(this BlockTableRecord btr, List pts, List bulges = default, List startWidths = default, List endWidths = default, Action action = default, Transaction trans = default) + else { - bulges ??= new List(new double[pts.Count]); - startWidths ??= new List(new double[pts.Count]); - endWidths ??= new List(new double[pts.Count]); - Polyline pl = new(); - for (int i = 0; i < pts.Count; i++) - { - pl.AddVertexAt(i, pts[i].Point2d(), bulges[i], startWidths[i], endWidths[i]); - } - return btr.AddEnt(pl, action, trans); + for (int i = 0; i < bvws.Count; i++) + pl.AddVertexAt(i, bvws[i].Vertex, bvws[i].Bulge, bvws[i].StartWidth, bvws[i].EndWidth); } + pl.Closed = isClosed;//闭合 + return btr.AddEnt(pl, action, trans); + } + /// + /// 在指定的绘图空间添加轻多段线 + /// + /// 绘图空间 + /// 端点表 + /// 凸度表 + /// 端点的起始宽度 + /// 端点的终止宽度 + /// 轻多段线属性设置委托 + /// 事务管理器 + /// 轻多段线id + public static ObjectId AddPline(this BlockTableRecord btr, + List pts, + List? bulges = default, + List? startWidths = default, + List? endWidths = default, + Action? action = default, + Transaction? trans = default) + { + bulges ??= new(new double[pts.Count]); + startWidths ??= new(new double[pts.Count]); + endWidths ??= new(new double[pts.Count]); -#if ac2013 - /// - /// 在指定的绘图空间添加轻多段线 - /// - /// 绘图空间 - /// 端点表,利用元组(Point3d pt, double bulge, double startWidth, double endWidth) - /// 轻多段线属性设置委托 - /// 事务管理器 - /// 轻多段线id - public static ObjectId AddPline(this BlockTableRecord btr, List<(Point3d pt, double bulge, double startWidth, double endWidth)> pts, Action action = default, Transaction trans = default) - { + Polyline pl = new(); + pl.SetDatabaseDefaults(); - Polyline pl = new(); - pts.ForEach((i, vertex) => - { - pl.AddVertexAt(i, vertex.pt.Point2d(), vertex.bulge, vertex.startWidth, vertex.endWidth); - }); + for (int i = 0; i < pts.Count; i++) + pl.AddVertexAt(i, pts[i].Point2d(), bulges[i], startWidths[i], endWidths[i]); + return btr.AddEnt(pl, action, trans); + } - return btr.AddEnt(pl, action, trans); - } -#endif + /// + /// 在指定的绘图空间添加轻多段线 + /// + /// 绘图空间 + /// 端点表,利用元组(Point3d pt, double bulge, double startWidth, double endWidth) + /// 轻多段线属性设置委托 + /// 事务管理器 + /// 轻多段线id + public static ObjectId AddPline(this BlockTableRecord btr, + List<(Point3d pt, double bulge, double startWidth, double endWidth)> pts, + Action? action = default, + Transaction? trans = default) + { + Polyline pl = new(); + pl.SetDatabaseDefaults(); + pts.ForEach((i, vertex) => { + pl.AddVertexAt(i, vertex.pt.Point2d(), vertex.bulge, vertex.startWidth, vertex.endWidth); + }); - /// - /// 在指定绘图空间X-Y平面3点画圆弧 - /// - /// 绘图空间 - /// 圆弧起点 - /// 圆弧上的点 - /// 圆弧终点 - /// 圆弧属性设置委托 - /// 事务管理器 - /// 圆弧id - public static ObjectId AddArc(this BlockTableRecord btr, Point3d startPoint, Point3d pointOnArc, Point3d endPoint, Action action = default, Transaction trans = default) - { - var arc = EntityEx.CreateArc(startPoint, pointOnArc, endPoint); - return btr.AddEnt(arc, action, trans); - } - #endregion + return btr.AddEnt(pl, action, trans); + } - #region 获取实体/实体id - /// - /// 获取块表记录内的指定类型的实体 - /// - /// 实体类型 - /// 块表记录 - /// 事务 - /// 打开模式 - /// 实体集合 - public static IEnumerable GetEntities(this BlockTableRecord btr, OpenMode mode = OpenMode.ForRead, Transaction trans = null) where T : Entity - { - trans ??= DBTrans.Top.Transaction; - return - btr - .Cast() - .Select(id => trans.GetObject(id, mode)) - .OfType(); - } + /// + /// 在指定绘图空间X-Y平面3点画圆弧 + /// + /// 绘图空间 + /// 圆弧起点 + /// 圆弧上的点 + /// 圆弧终点 + /// 圆弧属性设置委托 + /// 事务管理器 + /// 圆弧id + public static ObjectId AddArc(this BlockTableRecord btr, + Point3d startPoint, Point3d pointOnArc, Point3d endPoint, + Action? action = default, Transaction? trans = default) + { + var arc = EntityEx.CreateArc(startPoint, pointOnArc, endPoint); + return btr.AddEnt(arc, action, trans); + } - /// - /// 按类型获取实体Id,AutoCad2010以上版本支持 - /// - /// 实体类型 - /// 块表记录 - /// 实体Id集合 - public static IEnumerable GetObjectIds(this BlockTableRecord btr) where T : Entity - { - string dxfName = RXClass.GetClass(typeof(T)).DxfName; - return btr.Cast() - .Where(id => id.ObjectClass().DxfName == dxfName); - } + // todo: 所有涉及默认无参构造的实体类型,都需要调用SetDatabaseDefaults(); + #endregion - /// - /// 按类型获取实体Id的分组 - /// - /// 块表记录 - /// 实体Id分组 - public static IEnumerable> GetObjectIds(this BlockTableRecord btr) - { - return - btr - .Cast() - .GroupBy(id => id.ObjectClass().DxfName); - } + #region 获取实体/实体id + /// + /// 获取块表记录内的指定类型的实体 + /// + /// 实体类型 + /// 块表记录 + /// 打开模式 + /// 事务 + /// 实体集合 + public static IEnumerable GetEntities(this BlockTableRecord btr, + OpenMode mode = OpenMode.ForRead, + Transaction? trans = default) where T : Entity + { + trans ??= DBTrans.Top.Transaction; + return + btr + .Cast() + .Select(id => trans.GetObject(id, mode)) + .OfType(); + } - /// - /// 获取绘制顺序表 - /// - /// 块表 - /// 事务 - /// 绘制顺序表 - public static DrawOrderTable GetDrawOrderTable(this BlockTableRecord btr, Transaction tr = null) - { - tr ??= DBTrans.Top.Transaction; - return tr.GetObject(btr.DrawOrderTableId, OpenMode.ForRead) as DrawOrderTable; - } + /// + /// 按类型获取实体Id,AutoCad2010以上版本支持 + /// + /// 实体类型 + /// 块表记录 + /// 实体Id集合 + public static IEnumerable GetObjectIds(this BlockTableRecord btr) where T : Entity + { + string dxfName = RXClass.GetClass(typeof(T)).DxfName; + return btr.Cast() + .Where(id => id.ObjectClass().DxfName == dxfName); + } - #endregion + /// + /// 按类型获取实体Id的分组 + /// + /// 块表记录 + /// 实体Id分组 + public static IEnumerable> GetObjectIds(this BlockTableRecord btr) + { + return + btr + .Cast() + .GroupBy(id => id.ObjectClass().DxfName); + } - #region 插入块参照 + /// + /// 获取绘制顺序表 + /// + /// 块表 + /// 事务 + /// 绘制顺序表 + public static DrawOrderTable? GetDrawOrderTable(this BlockTableRecord btr, + Transaction? trans = default) + { + trans ??= DBTrans.Top.Transaction; + return trans.GetObject(btr.DrawOrderTableId, OpenMode.ForRead) as DrawOrderTable; + } + #endregion - /// - /// 插入块参照 - /// - /// 插入点 - /// 块名 - /// 块插入比例,默认为1 - /// 块插入旋转角(弧度),默认为0 - /// 属性字典{Tag,Value},默认为null - /// 块参照对象id - public static ObjectId InsertBlock(this BlockTableRecord blockTableRecord, Point3d position, - string blockName, - Scale3d scale = default, - double rotation = default, - Dictionary atts = default, Transaction trans = null) + #region 插入块参照 + /// + /// 插入块参照 + /// + /// 块表记录 + /// 插入点 + /// 块名 + /// 块插入比例,默认为1 + /// 块插入旋转角(弧度),默认为0 + /// 属性字典{Tag,Value},默认为null + /// 事务 + /// 块参照对象id + public static ObjectId InsertBlock(this BlockTableRecord blockTableRecord, Point3d position, + string blockName, + Scale3d scale = default, + double rotation = default, + Dictionary? atts = default, Transaction? trans = null) + { + trans ??= DBTrans.Top.Transaction; + if (!DBTrans.Top.BlockTable.Has(blockName)) { - trans ??= DBTrans.Top.Transaction; - if (!DBTrans.Top.BlockTable.Has(blockName)) - { - DBTrans.Top.Editor.WriteMessage($"\n不存在名字为{blockName}的块定义。"); - return ObjectId.Null; - } - return blockTableRecord.InsertBlock(position, DBTrans.Top.BlockTable[blockName], scale, rotation, atts, trans); + DBTrans.Top.Editor?.WriteMessage($"\n不存在名字为{blockName}的块定义。"); + return ObjectId.Null; } - /// - /// 插入块参照 - /// - /// 插入点 - /// 块定义id - /// 块插入比例,默认为1 - /// 块插入旋转角(弧度),默认为0 - /// 属性字典{Tag,Value},默认为null - /// 块参照对象id - public static ObjectId InsertBlock(this BlockTableRecord blockTableRecord, Point3d position, - ObjectId blockId, - Scale3d scale = default, - double rotation = default, - Dictionary atts = default, Transaction tr = null) + return blockTableRecord.InsertBlock(position, DBTrans.Top.BlockTable[blockName], scale, rotation, atts, trans); + } + /// + /// 插入块参照 + /// + /// 插入点 + /// 块定义id + /// 块插入比例,默认为1 + /// 块插入旋转角(弧度),默认为0 + /// 属性字典{Tag,Value},默认为null + /// 块参照对象id + public static ObjectId InsertBlock(this BlockTableRecord blockTableRecord, Point3d position, + ObjectId blockId, + Scale3d scale = default, + double rotation = default, + Dictionary? atts = default, Transaction? trans = null) + { + trans ??= DBTrans.Top.Transaction; + if (!DBTrans.Top.BlockTable.Has(blockId)) { - tr ??= DBTrans.Top.Transaction; - if (!DBTrans.Top.BlockTable.Has(blockId)) - { - DBTrans.Top.Editor.WriteMessage($"\n不存在名字为{DBTrans.Top.GetObject(blockId).Name}的块定义。"); - return ObjectId.Null; - } - using var blockref = new BlockReference(position, blockId) - { - ScaleFactors = scale, - Rotation = rotation - }; - var objid = blockTableRecord.AddEntity(blockref); - if (atts != default) + DBTrans.Top.Editor?.WriteMessage($"\n不存在块定义。"); + return ObjectId.Null; + } + using var blockref = new BlockReference(position, blockId) + { + ScaleFactors = scale, + Rotation = rotation + }; + var objid = blockTableRecord.AddEntity(blockref); + if (atts != default) + { + var btr = DBTrans.Top.GetObject(blockref.BlockTableRecord)!; + if (btr.HasAttributeDefinitions) { - var btr = DBTrans.Top.GetObject(blockref.BlockTableRecord); - if (btr.HasAttributeDefinitions) + var attdefs = btr.GetEntities(); + foreach (var attdef in attdefs) { - var attdefs = btr - .GetEntities() - .Where(attdef => !(attdef.Constant || attdef.Invisible)); - foreach (var attdef in attdefs) - { - using AttributeReference attref = new(); - attref.SetAttributeFromBlock(attdef, blockref.BlockTransform); - attref.Position = attdef.Position.TransformBy(blockref.BlockTransform); - attref.AdjustAlignment(DBTrans.Top.Database); - if (atts.ContainsKey(attdef.Tag)) - attref.TextString = atts[attdef.Tag]; + using AttributeReference attref = new(); + attref.SetDatabaseDefaults(); + attref.SetAttributeFromBlock(attdef, blockref.BlockTransform); + attref.Position = attdef.Position.TransformBy(blockref.BlockTransform); + attref.AdjustAlignment(DBTrans.Top.Database); + + if (atts.ContainsKey(attdef.Tag)) + attref.TextString = atts[attdef.Tag]; - blockref.AttributeCollection.AppendAttribute(attref); - tr.AddNewlyCreatedDBObject(attref, true); - } + blockref.AttributeCollection.AppendAttribute(attref); + trans.AddNewlyCreatedDBObject(attref, true); } } - return objid; } - - #endregion - #endregion - - - + return objid; } + #endregion + + #endregion } diff --git a/src/IFoxCAD.Cad/ExtensionMethod/Tools.cs b/src/IFoxCAD.Cad/ExtensionMethod/Tools.cs new file mode 100644 index 0000000..0e875c5 --- /dev/null +++ b/src/IFoxCAD.Cad/ExtensionMethod/Tools.cs @@ -0,0 +1,188 @@ +namespace IFoxCAD.Cad; + +public static class Tools +{ + public static void TestTimes2(int count, string message, Action action) + { + System.Diagnostics.Stopwatch watch = new(); + watch.Start(); //开始监视代码运行时间 + for (int i = 0; i < count; i++) + action.Invoke();//需要测试的代码 + watch.Stop(); //停止监视 + TimeSpan timespan = watch.Elapsed; //获取当前实例测量得出的总时间 + double time = timespan.TotalMilliseconds; + string name = "毫秒"; + if (timespan.TotalMilliseconds > 1000) + { + time = timespan.TotalSeconds; + name = "秒"; + } + Env.Print($"{message} 代码执行 {count} 次的时间:{time} ({name})"); //总毫秒数 + } + + /// + /// 纳秒计时器 + /// + public static void TestTimes(int count, string message, Action action, + Timer.TimeEnum timeEnum = Timer.TimeEnum.Millisecond) + { + double time = Timer.RunTime(() => { + for (int i = 0; i < count; i++) + action(); + }, timeEnum); + + string timeNameZn = ""; + switch (timeEnum) + { + case Timer.TimeEnum.Second: + timeNameZn = " 秒"; + break; + case Timer.TimeEnum.Millisecond: + timeNameZn = " 毫秒"; + break; + case Timer.TimeEnum.Microsecond: + timeNameZn = " 微秒"; + break; + case Timer.TimeEnum.Nanosecond: + timeNameZn = " 纳秒"; + break; + } + + Env.Print($"{message} 代码执行 {count} 次的时间:{time} ({timeNameZn})"); + } +} + +/* +//测试例子,同时验证两个计时器 +var stopwatch = new Stopwatch(); +Timer.RunTime(() => { + + stopwatch.Start(); + for (int i = 0; i < 10000000; i++) + i++; + stopwatch.Stop(); + +}, Timer.TimeEnum.Millisecond, "运行:"); +Console.WriteLine("运行毫秒:" + stopwatch.ElapsedMilliseconds); + */ + +public class Timer +{ + [Flags] + public enum TimeEnum + { + /// + /// 秒 + /// + Second, + /// + /// 毫秒 + /// + Millisecond, + /// + /// 微秒 + /// + Microsecond, + /// + /// 纳秒 + /// + Nanosecond, + } + + [DllImport("Kernel32.dll")] + static extern bool QueryPerformanceCounter(out long lpPerformanceCount); + + /// + /// 这个函数会检索性能计数器的频率. + /// 性能计数器的频率在系统启动时是固定的,并且在所有处理器上都是一致的 + /// 因此,只需在应用初始化时查询频率,即可缓存结果 + /// 在运行 Windows XP 或更高版本的系统上,该函数将始终成功,因此永远不会返回零 + /// + /// + /// + [DllImport("Kernel32.dll")] + static extern bool QueryPerformanceFrequency(out long lpFrequency); + + long _startTime, _stopTime; + long _freq; + + public Timer() + { + _startTime = 0; + _stopTime = 0; + + if (!QueryPerformanceFrequency(out _freq)) + throw new Win32Exception("不支持高性能计数器"); + } + + /// + /// 开始计时器 + /// + public void Start() + { + System.Threading.Thread.Sleep(0); + QueryPerformanceCounter(out _startTime); + } + + /// + /// 停止计时器 + /// + public void Stop() + { + QueryPerformanceCounter(out _stopTime); + _Second = (double)(_stopTime - _startTime) / _freq; + } + double _Second = 0; + + // 返回计时器经过时间 + public double Second => _Second; + public double Millisecond => _Second * 1000.0; + public double Microsecond => _Second * 1000000.0; + public double Nanosecond => _Second * 1000000000.0; + + public static double RunTime(Action action, TimeEnum timeEnum = TimeEnum.Millisecond, string? msg = null) + { + var nanoSecond = new Timer(); + nanoSecond.Start(); + action(); + nanoSecond.Stop(); + + double time = 0; + switch (timeEnum) + { + case TimeEnum.Second: + time = nanoSecond.Second; + break; + case TimeEnum.Millisecond: + time = nanoSecond.Millisecond; + break; + case TimeEnum.Microsecond: + time = nanoSecond.Microsecond; + break; + case TimeEnum.Nanosecond: + time = nanoSecond.Nanosecond; + break; + } + if (msg != null) + { + string timeNameZn = ""; + switch (timeEnum) + { + case TimeEnum.Second: + timeNameZn = " 秒"; + break; + case TimeEnum.Millisecond: + timeNameZn = " 毫秒"; + break; + case TimeEnum.Microsecond: + timeNameZn = " 微秒"; + break; + case TimeEnum.Nanosecond: + timeNameZn = " 纳秒"; + break; + } + Env.Print(msg + " " + time + timeNameZn); + } + return time; + } +} \ No newline at end of file diff --git "a/src/IFoxCAD.Cad/ExtensionMethod/\346\226\260\345\273\272\345\241\253\345\205\205/HatchConverter.cs" "b/src/IFoxCAD.Cad/ExtensionMethod/\346\226\260\345\273\272\345\241\253\345\205\205/HatchConverter.cs" new file mode 100644 index 0000000..7868bee --- /dev/null +++ "b/src/IFoxCAD.Cad/ExtensionMethod/\346\226\260\345\273\272\345\241\253\345\205\205/HatchConverter.cs" @@ -0,0 +1,358 @@ +namespace IFoxCAD.Cad; +using PointV = Point2d; + +/// +/// 填充边界转换器 +/// +public class HatchConverter +{ + #region 辅助类 + /// + /// 生成圆形数据 + /// + class CircleData + { + public PointV Center; + public double Radius; + + /// + /// 生成圆形数据 + /// + /// 对称点1 + /// 对称点2 + public CircleData(PointV symmetryAxisPoint1, PointV symmetryAxisPoint2) + { + Center = symmetryAxisPoint1.GetCenter(symmetryAxisPoint2); + Radius = symmetryAxisPoint1.GetDistanceTo(symmetryAxisPoint2) * 0.5; + } + } + + /// + /// 填充转换器的数据 + /// + class HatchConverterData + { + public List PolyLineData; + public List CircleData; + public List SplineData; + + /// + /// 填充转换器的数据 + /// + public HatchConverterData() + { + PolyLineData = new(); + CircleData = new(); + SplineData = new(); + } + } + #endregion + + #region 成员 + /// + /// 外部只能调用id,否则跨事务造成错误 + /// + public ObjectId OldHatchId + { + get + { + if (_oldHatch is null) + return ObjectId.Null; + return _oldHatch.ObjectId; + } + } + readonly Hatch? _oldHatch; + + readonly List _hcDatas; + /// + /// 生成的填充边界id + /// + public List BoundaryIds; + #endregion + + #region 构造 + /// + /// 填充边界转换器 + /// + HatchConverter() + { + _hcDatas = new(); + BoundaryIds = new(); + } + + /// + /// 填充边界转换器 + /// + /// 需要转化的Hatch对象 + public HatchConverter(Hatch hatch) : this() + { + _oldHatch = hatch; + + //不能在提取信息的时候进行新建cad图元, + //否则cad将会提示遗忘释放 + hatch.ForEach(loop => { + var hcData = new HatchConverterData(); + + bool isCurve2d = true; + if (loop.IsPolyline) + { + //边界是多段线 + HatchLoopIsPolyline(loop, hcData); + isCurve2d = false; + } + else + { + if (loop.Curves.Count == 2)//1是不可能的,大于2的是曲线 + { + //边界是曲线,过滤可能是圆形的情况 + var cir = TwoArcFormOneCircle(loop); + if (cir is not null) + { + hcData.CircleData.Add(cir); + isCurve2d = false; + } + } + } + + //边界是曲线 + if (isCurve2d) + HatchLoopIsCurve2d(loop, hcData); + + _hcDatas.Add(hcData); + }); + } + #endregion + + #region 方法 + /// + /// 多段线处理 + /// + /// 填充边界 + /// 收集图元信息 + static void HatchLoopIsPolyline(HatchLoop loop, HatchConverterData hcData) + { + if (loop is null) + throw new ArgumentNullException(nameof(loop)); + + if (hcData is null) + throw new ArgumentNullException(nameof(hcData)); + + //判断为圆形: + //上下两个圆弧,然后填充,就会生成此种填充 + //顶点数是3,凸度是半圆,两个半圆就是一个圆形 + if (loop.Polyline.Count == 3 && loop.Polyline[0].Bulge == 1 && loop.Polyline[1].Bulge == 1 || + loop.Polyline.Count == 3 && loop.Polyline[0].Bulge == -1 && loop.Polyline[1].Bulge == -1) + { + hcData.CircleData.Add(new CircleData(loop.Polyline[0].Vertex, loop.Polyline[1].Vertex)); + } + else + { + //遍历多段线信息 + var bvc = loop.Polyline; + for (int i = 0; i < bvc.Count; i++) + hcData.PolyLineData.Add(new BulgeVertexWidth(bvc[i])); + } + } + + /// + /// 两个圆弧组成圆形 + /// + /// + /// + static CircleData? TwoArcFormOneCircle(HatchLoop loop) + { + if (loop is null) + throw new ArgumentNullException(nameof(loop)); + + if (loop.Curves.Count != 2) + throw new ArgumentException( + "边界非多段线,而且点数!=2,点数为:" + nameof(loop.Curves.Count) + ";两个矩形交集的时候会出现此情况."); + + CircleData? circular = null; + + //判断为圆形: + //用一条(不是两条)多段线画出两条圆弧为正圆,就会生成此种填充 + //边界为曲线,数量为2,可能是两个半圆曲线,如果是,就加入圆形数据中 + + //第一段 + var getCurves1Pts = loop.Curves[0].GetSamplePoints(3); //曲线取样点分两份(3点) + var mid1Pt = getCurves1Pts[1]; //腰点 + double bulge1 = loop.Curves[0].StartPoint.GetArcBulge(mid1Pt, loop.Curves[0].EndPoint); + + //第二段 + var getCurves2Pts = loop.Curves[1].GetSamplePoints(3); + var mid2Pt = getCurves2Pts[1]; + double bulge2 = loop.Curves[1].StartPoint.GetArcBulge(mid2Pt, loop.Curves[1].EndPoint); + + //第一段上弧&&第二段反弧 || 第一段反弧&&第二段上弧 + if (bulge1 == -1 && bulge2 == -1 || bulge1 == 1 && bulge2 == 1) + circular = new CircleData(loop.Curves[0].StartPoint, loop.Curves[1].StartPoint); //两个起点就是对称点 + + return circular; + } + + /// + /// 处理边界曲线 + /// + /// 填充边界 + /// 收集图元信息 + static void HatchLoopIsCurve2d(HatchLoop loop, HatchConverterData hcData) + { + //取每一段曲线,曲线可能是直线来的,但是圆弧会按照顶点来分段 + int curveIsClosed = 0; + + //遍历边界的多个子段 + foreach (Curve2d curve in loop.Curves) + { + //计数用于实现闭合 + curveIsClosed++; + if (curve is NurbCurve2d spl) + { + //判断为样条曲线: + hcData.SplineData.Add(spl); + continue; + } + + var pts = curve.GetSamplePoints(3); + var midPt = pts[1]; + if (curve.StartPoint.IsEqualTo(curve.EndPoint, new Tolerance(1e-6, 1e-6)))//首尾相同,就是圆形 + { + //判断为圆形: + //获取起点,然后采样三点,中间就是对称点(直径点) + hcData.CircleData.Add(new CircleData(curve.StartPoint, midPt)); + continue; + } + + //判断为多段线,圆弧: + double bulge = curve.StartPoint.GetArcBulge(midPt, curve.EndPoint); + hcData.PolyLineData.Add(new BulgeVertexWidth(curve.StartPoint, bulge)); + + //末尾点,不闭合的情况下就要获取这个 + if (curveIsClosed == loop.Curves.Count) + hcData.PolyLineData.Add(new BulgeVertexWidth(curve.EndPoint, 0)); + } + } + + /// + /// 创建边界图元 + /// + /// 返回图元 + public void CreateBoundaryEntitys(List outEnts) + { + for (int i = 0; i < _hcDatas.Count; i++) + { + var data = _hcDatas[i]; + + //生成边界:多段线 + if (data.PolyLineData.Count > 0) + { + Polyline pl = new(); + pl.SetDatabaseDefaults(); + for (int j = 0; j < data.PolyLineData.Count; j++) + { + pl.AddVertexAt(j, + data.PolyLineData[j].Vertex, + data.PolyLineData[j].Bulge, + data.PolyLineData[j].StartWidth, + data.PolyLineData[j].EndWidth); + } + outEnts.Add(pl); + } + + //生成边界:圆 + data.CircleData.ForEach(item => { + outEnts.Add(new Circle(item.Center.Point3d(), Vector3d.ZAxis, item.Radius)); + }); + + //生成边界:样条曲线 + data.SplineData.ForEach(item => { + outEnts.Add(item.ToCurve()); + }); + } + + if (_oldHatch is not null) + { + outEnts.ForEach(ent => { + ent.Color = _oldHatch.Color; + ent.Layer = _oldHatch.Layer; + }); + } + } + + + /// + /// 创建边界图元和新填充到当前空间 + /// + /// 事务 + /// 数据库 + /// 边界关联 + /// 是否创建填充,false则只创建边界 + /// 新填充id,边界在获取 + public ObjectId CreateBoundarysAndHatchToMsPs(BlockTableRecord btrOfAddEntitySpace, + bool boundaryAssociative = true, + bool createHatchFlag = true, + Transaction? trans = null) + { + //重设边界之前肯定是有边界才可以 + if (BoundaryIds.Count == 0) + { + List boundaryEntitys = new(); + CreateBoundaryEntitys(boundaryEntitys); + boundaryEntitys.ForEach(ent => { + BoundaryIds.Add(btrOfAddEntitySpace.AddEntity(ent)); + }); + } + + if (!createHatchFlag) + return ObjectId.Null; + /* + * 此处为什么要克隆填充,而不是新建填充? + * 因为填充如果是新建的,那么将会丢失基点,概念如下: + * 两个一样的填充,平移其中一个,那么再提取他们的基点会是一样的! + * 所以生成时候就不等同于画面相同. + * 也因为我不知道什么新建方式可以新建一模一样的填充,因此使用了克隆 + * 那么它的平移后的基点在哪里呢? + */ + + var newHatchId = btrOfAddEntitySpace.DeepClone(new ObjectIdCollection(new ObjectId[] { OldHatchId })).GetValues()[0]; + trans ??= DBTrans.Top.Transaction; + var hatchEnt = trans.GetObject(newHatchId, OpenMode.ForWrite) as Hatch; + if (hatchEnt != null) + { + ResetBoundary(hatchEnt, boundaryAssociative); + hatchEnt.DowngradeOpen(); + } + return newHatchId; + } + + + /// + /// 重设边界 + /// + /// + /// 边界关联 + void ResetBoundary(Hatch hatch, + bool boundaryAssociative = true) + { + //删除原有边界 + while (hatch.NumberOfLoops != 0) + hatch.RemoveLoopAt(0); + + hatch.Associative = boundaryAssociative; + + var obIds = new ObjectIdCollection(); + for (int i = 0; i < BoundaryIds.Count; i++) + { + obIds.Clear(); + obIds.Add(BoundaryIds[i]); + //要先添加最外面的边界 + if (i == 0) + hatch.AppendLoop(HatchLoopTypes.Outermost, obIds); + else + hatch.AppendLoop(HatchLoopTypes.Default, obIds); + } + //计算填充并显示 + hatch.EvaluateHatch(true); + } + #endregion +} \ No newline at end of file diff --git "a/src/IFoxCAD.Cad/ExtensionMethod/\346\226\260\345\273\272\345\241\253\345\205\205/HatchEx.cs" "b/src/IFoxCAD.Cad/ExtensionMethod/\346\226\260\345\273\272\345\241\253\345\205\205/HatchEx.cs" new file mode 100644 index 0000000..8bfd043 --- /dev/null +++ "b/src/IFoxCAD.Cad/ExtensionMethod/\346\226\260\345\273\272\345\241\253\345\205\205/HatchEx.cs" @@ -0,0 +1,15 @@ +namespace IFoxCAD.Cad; + +public static class HatchEx +{ + /// + /// 遍历填充每条边 + /// + /// + /// + public static void ForEach(this Hatch hatch, Action action) + { + for (int i = 0; i < hatch.NumberOfLoops; i++) + action.Invoke(hatch.GetLoopAt(i)); + } +} \ No newline at end of file diff --git "a/src/IFoxCAD.Cad/ExtensionMethod/\346\226\260\345\273\272\345\241\253\345\205\205/HatchInfo.cs" "b/src/IFoxCAD.Cad/ExtensionMethod/\346\226\260\345\273\272\345\241\253\345\205\205/HatchInfo.cs" new file mode 100644 index 0000000..1226479 --- /dev/null +++ "b/src/IFoxCAD.Cad/ExtensionMethod/\346\226\260\345\273\272\345\241\253\345\205\205/HatchInfo.cs" @@ -0,0 +1,354 @@ +namespace IFoxCAD.Cad; + +/* + * ӵĵһ߽߽,ڶͼı߽硣 + * Ҫⲿ߽,ʹӻΪ HatchLoopTypes.Outermost AppendLoop , + * һ߽类,ͿԼı߽硣 + * ڲ߽ʹô HatchLoopTypes.Default AppendLoop + * + * ߽ʱ,ӵ(߽,߽,߽,߽ͨ....) + * ߽ʱ,ӵ(߽,߽ͨ.....߽,߽ͨ....) + */ + +/// +/// ͼ +/// +public class HatchInfo +{ + #region Ա + /// + /// ߽id(ŵһ) + /// + readonly List _boundaryIds; + /// + /// ͼԪ + /// + readonly Hatch _hatch; + /// + /// ߽(˴ֱ=>Ա,Ϊ뷴Ӧ) + /// + readonly bool _boundaryAssociative; + /// + /// :û(̶)//ݶļ + /// + string? _hatchName; + /// + /// ģʽ(Ԥ/û/Զ) + /// + HatchPatternType _patternTypeHatch; + /// + /// ģʽ + /// + GradientPatternType _patternTypeGradient; + /// + /// / + /// + double Scale => _hatch.PatternScale; + /// + /// Ƕ + /// + double Angle => _hatch.PatternAngle; + #endregion + + #region + HatchInfo() + { + _hatch = new Hatch(); + _hatch.SetDatabaseDefaults(); + _boundaryIds = new(); + } + + /// + /// ͼ + /// + /// ߽ + /// ԭ + /// + /// Ƕ + public HatchInfo(bool boundaryAssociative = true, + Point2d? hatchOrigin = null, + double hatchScale = 1, + double hatchAngle = 0) : this() + { + if (hatchScale <= 0) + throw new ArgumentException("Сڵ0"); + + _hatch.PatternScale = hatchScale;// + _hatch.PatternAngle = hatchAngle;//Ƕ + _boundaryAssociative = boundaryAssociative; + + hatchOrigin ??= Point2d.Origin; + _hatch.Origin = hatchOrigin.Value; //ԭ + } + + /// + /// ͼ + /// + /// ߽ + /// ߽ + /// ԭ + /// + /// Ƕ + public HatchInfo(IEnumerable boundaryIds, + bool boundaryAssociative = true, + Point2d? hatchOrigin = null, + double hatchScale = 1, + double hatchAngle = 0) + : this(boundaryAssociative, hatchOrigin, hatchScale, hatchAngle) + { + _boundaryIds.AddRange(boundaryIds); + } + + #endregion + + #region + /// + /// ģʽ1:Ԥ + /// + public HatchInfo Mode1PreDefined(string name) + { + _hatchName = name; + _hatch.HatchObjectType = HatchObjectType.HatchObject; //(/) + _patternTypeHatch = HatchPatternType.PreDefined; + return this; + } + + /// + /// ģʽ2:û + /// + /// Ƿ˫ + public HatchInfo Mode2UserDefined(bool patternDouble = true) + { + _hatchName = "_USER"; + _hatch.HatchObjectType = HatchObjectType.HatchObject; //(/) + _patternTypeHatch = HatchPatternType.UserDefined; + + _hatch.PatternDouble = patternDouble; //Ƿ˫򣨱д SetHatchPattern ֮ǰ + _hatch.PatternSpace = Scale; //ࣨд SetHatchPattern ֮ǰ + return this; + } + + /// + /// ģʽ3:Զ + /// + /// + public HatchInfo Mode3UserDefined(string name) + { + _hatchName = name; + _hatch.HatchObjectType = HatchObjectType.HatchObject; //(/) + _patternTypeHatch = HatchPatternType.CustomDefined; + return this; + } + + /// + /// ģʽ4: + /// + /// + /// ɫʼɫ + /// ɫɫ + /// ƶ + /// ɫֵ + /// ɫ˫ɫ + public HatchInfo Mode4Gradient(GradientName name, Color colorStart, Color colorEnd, + float gradientShift = 0, + float shadeTintValue = 0, + bool gradientOneColorMode = false) + { + //entgetֱȻ"SOLID",Ϊ"","" + _hatchName = name.ToString(); + _hatch.HatchObjectType = HatchObjectType.GradientObject; //(/) + _patternTypeGradient = GradientPatternType.PreDefinedGradient;//ģʽ4: + //_patternTypeGradient = GradientPatternType.UserDefinedGradient;//ģʽ5:..ģʽɶ + + //ýɫʼͽɫ + var gColor1 = new GradientColor(colorStart, 0); + var gColor2 = new GradientColor(colorEnd, 1); + _hatch.SetGradientColors(new GradientColor[] { gColor1, gColor2 }); + + _hatch.GradientShift = gradientShift; //ݶλ + _hatch.ShadeTintValue = shadeTintValue; //Ӱɫֵ + _hatch.GradientOneColorMode = gradientOneColorMode;//䵥ɫ/˫ɫ + _hatch.GradientAngle = Angle; //Ƕ + + return this; + } + + /// + /// + /// + /// ˿ռ + public ObjectId Build(BlockTableRecord btrOfAddEntitySpace) + { + //ݿ + var hatchId = btrOfAddEntitySpace.AddEntity(_hatch); + + //ģʽ:/ + if (_hatch.HatchObjectType == HatchObjectType.GradientObject) + _hatch.SetGradient(_patternTypeGradient, _hatchName); + else + _hatch.SetHatchPattern(_patternTypeHatch, _hatchName); + + //߽,ݿռھͻ + //Ϊ true 뷴Ӧ,˱Ƚ(ά뽫ʮɺ),. + _hatch.Associative = _boundaryAssociative; + + // AppendLoop ؼ,Ͳ + if (_boundaryIds.Count > 0) + AppendLoop(_boundaryIds, HatchLoopTypes.Default); + + //䲢ʾ(߽,쳣) + _hatch.EvaluateHatch(true); + + return hatchId; + } + + /// + /// ִͼԪ޸ + /// + /// ӳʵ + public HatchInfo Action(Action action) + { + action(_hatch); + return this; + } + + /// + /// ձ߽缯 + /// + public HatchInfo ClearBoundary() + { + _boundaryIds.Clear(); + return this; + } + + /// + /// ɾ߽ͼԪ + /// + public HatchInfo EraseBoundary() + { + for (int i = 0; i < _boundaryIds.Count; i++) + _boundaryIds[i].Erase(); + return this; + } + + /// + /// ߽ + /// + /// ߽id + /// 뷽ʽ + void AppendLoop(IEnumerable boundaryIds, + HatchLoopTypes hatchLoopTypes = HatchLoopTypes.Default) + { + var obIds = new ObjectIdCollection(); + //߽DZպϵ,Ѿݿ + //պϻ. + foreach (var border in boundaryIds) + { + obIds.Clear(); + obIds.Add(border); + _hatch.AppendLoop(hatchLoopTypes, obIds); + } + obIds.Dispose(); + } + + /// + /// ߽(¸߰汾亯) + /// + /// 㼯 + /// ͹ȼ + /// ˿ռ + /// 뷽ʽ + /// + public HatchInfo AppendLoop(Point2dCollection pts, + DoubleCollection bluges, + BlockTableRecord btrOfAddEntitySpace, + HatchLoopTypes hatchLoopTypes = HatchLoopTypes.Default) + { + if (pts == null) + throw new ArgumentNullException(nameof(pts)); + + var ptsEnd2End = pts.End2End(); +#if NET35 + _boundaryIds.Add(CreateAddBoundary(ptsEnd2End, bluges, btrOfAddEntitySpace)); +#else + //2011API,ԲͼԪ¼߽, + //ͨĻ,߽ _boundaryIds ǿյ,ô Build() ʱҪ˿յ + _hatch.AppendLoop(hatchLoopTypes, ptsEnd2End, bluges); +#endif + return this; + } + +#if NET35 + /// + /// ͨ㼯͹ɱ߽Ķ + /// + /// 㼯 + /// ͹ȼ + /// ˿ռ + /// id + static ObjectId CreateAddBoundary(Point2dCollection? pts, + DoubleCollection? bluges, + BlockTableRecord btrOfAddEntitySpace) + { + if (pts is null) + throw new ArgumentException(null, nameof(pts)); + if (bluges is null) + throw new ArgumentException(null, nameof(bluges)); + + var bvws = new List(); + + var itor1 = pts.GetEnumerator(); + var itor2 = bluges.GetEnumerator(); + while (itor1.MoveNext() && itor2.MoveNext()) + bvws.Add(new BulgeVertexWidth(itor1.Current, itor2.Current)); + + return btrOfAddEntitySpace.AddPline(bvws); + } +#endif + #endregion + + #region ö + /// + /// ɫͼ + /// + public enum GradientName + { + /// + /// ״ + /// + Linear, + /// + /// Բ״ + /// + Cylinder, + /// + /// Բ״ + /// + Invcylinder, + /// + /// ״ + /// + Spherical, + /// + /// ״ + /// + Invspherical, + /// + /// ״ + /// + Hemisperical, + /// + /// ״ + /// + InvHemisperical, + /// + /// ״ + /// + Curved, + /// + /// ״ + /// + Incurved + } + #endregion +} \ No newline at end of file diff --git "a/src/IFoxCAD.Cad/ExtensionMethod/\346\226\260\345\273\272\346\226\207\345\255\227/AttachmentPointHelper.cs" "b/src/IFoxCAD.Cad/ExtensionMethod/\346\226\260\345\273\272\346\226\207\345\255\227/AttachmentPointHelper.cs" new file mode 100644 index 0000000..989721c --- /dev/null +++ "b/src/IFoxCAD.Cad/ExtensionMethod/\346\226\260\345\273\272\346\226\207\345\255\227/AttachmentPointHelper.cs" @@ -0,0 +1,95 @@ +namespace IFoxCAD.Cad; + +public static class AttachmentPointHelper +{ + static readonly Dictionary _alignment = new() + { + { "左上", AttachmentPoint.TopLeft }, + { "中上", AttachmentPoint.TopCenter },//单行的对齐 + { "右上", AttachmentPoint.TopRight }, + + { "左中", AttachmentPoint.MiddleLeft }, + { "正中", AttachmentPoint.MiddleCenter },//多行的正中 + { "右中", AttachmentPoint.MiddleRight }, + + { "左对齐", AttachmentPoint.BaseLeft },//※优先(放在前面优先获取) + { "左", AttachmentPoint.BaseLeft }, + + { "中间", AttachmentPoint.BaseMid }, + + { "右对齐", AttachmentPoint.BaseRight },//※优先(放在前面优先获取) + { "右", AttachmentPoint.BaseRight }, + + { "左下", AttachmentPoint.BottomLeft }, + { "中下", AttachmentPoint.BottomCenter }, + { "右下", AttachmentPoint.BottomRight }, + + { "对齐", AttachmentPoint.BaseAlign },//※优先(放在前面优先获取) + { "调整", AttachmentPoint.BaseAlign }, + + { "居中", AttachmentPoint.BaseCenter },//单行的中 + { "铺满", AttachmentPoint.BaseFit }, + }; + + /// + /// 输入文字获得对齐方式 + /// + /// + /// + public static AttachmentPoint Get(string key) + { + return _alignment[key]; + } + + /// + /// 输入对齐方式获得文字 + /// + /// + /// + public static string Get(AttachmentPoint value) + { + return _alignment.FirstOrDefault(q => q.Value == value).Key; + } +} + +#if false +//反射描述 +//这些东西cad没有用到啊...所以不纳入了 +public enum AttachmentPoint2 +{ + [Description("下对齐")] + BottomAlign = 14, + [Description("中对齐")] + MiddleAlign = 15,//0xF + [Description("上对齐")] + TopAlign = 16,//0x10 + [Description("下铺满")] + BottomFit = 18, + [Description("中铺满")] + MiddleFit = 19, + [Description("上铺满")] + TopFit = 20, + [Description("下居中")] + BottomMid = 22, + [Description("中居中")] + MiddleMid = 23, + [Description("下居中")] + TopMid = 24, +} + +public static Dictionary GetEnumDic(Type enumType) +{ + Dictionary dic = new(); + var fieldinfos = enumType.GetFields(); + for (int i = 0; i < fieldinfos.Length; i++) + { + var field = fieldinfos[i]; + if (field.FieldType.IsEnum) + { + var objs = field.GetCustomAttributes(typeof(DescriptionAttribute), false); + dic.Add(field.Name, ((DescriptionAttribute)objs[0]).Description); + } + } + return dic; +} +#endif diff --git "a/src/IFoxCAD.Cad/ExtensionMethod/\346\226\260\345\273\272\346\226\207\345\255\227/TextEntityAdd.cs" "b/src/IFoxCAD.Cad/ExtensionMethod/\346\226\260\345\273\272\346\226\207\345\255\227/TextEntityAdd.cs" new file mode 100644 index 0000000..5b37a31 --- /dev/null +++ "b/src/IFoxCAD.Cad/ExtensionMethod/\346\226\260\345\273\272\346\226\207\345\255\227/TextEntityAdd.cs" @@ -0,0 +1,62 @@ +namespace IFoxCAD.Cad; + +public static partial class EntityAdd +{ + /// + /// 创建单行文字 + /// + /// 数据库 + /// 内容 + /// 插入点 + /// 字体高度 + /// 文字样式 + /// 对齐方式 + /// 对齐点,因样式 可能无效 + /// + public static Entity AddDBTextToEntity(this Database db, + string textContents, + Point3d position, + double textHigh = 2.5, + ObjectId? textStyleId = null, + AttachmentPoint justify = AttachmentPoint.BaseLeft, + Point3d? justifyPoint = null) + { + var TextInfo = new TextInfo( + textContents, + position, + justify, + justifyPoint, + textStyleId, + textHigh, + db); + return TextInfo.AddDBTextToEntity(); + } + + /// + /// 新建多行文字 + /// + /// 数据库 + /// 内容 + /// 插入点 + /// 字体高度 + /// 文字样式 + /// 对齐方式 + /// + public static Entity AddMTextToEntity(this Database db, + string textContents, + Point3d position, + double textHigh = 2.5, + ObjectId? textStyleId = null, + AttachmentPoint justify = AttachmentPoint.BaseLeft) + { + var TextInfo = new TextInfo( + textContents, + position, + justify, + null, + textStyleId, + textHigh, + db); + return TextInfo.AddMTextToEntity(); + } +} diff --git "a/src/IFoxCAD.Cad/ExtensionMethod/\346\226\260\345\273\272\346\226\207\345\255\227/TextInfo.cs" "b/src/IFoxCAD.Cad/ExtensionMethod/\346\226\260\345\273\272\346\226\207\345\255\227/TextInfo.cs" new file mode 100644 index 0000000..573963b --- /dev/null +++ "b/src/IFoxCAD.Cad/ExtensionMethod/\346\226\260\345\273\272\346\226\207\345\255\227/TextInfo.cs" @@ -0,0 +1,178 @@ +namespace IFoxCAD.Cad; + +/// +/// 文字信息类 +/// +public class TextInfo +{ + readonly Database? Database; + readonly string? Contents; + readonly Point3d Position; + + public string TextJustifyCn => AttachmentPointHelper.Get(TextJustify); + readonly AttachmentPoint TextJustify; + readonly Point3d? AlignmentPoint; + + readonly double TextHeight; + readonly ObjectId? TextStyleId; + + /// + /// 文字信息类 + /// + /// 内容 + /// 基点 + /// 对齐方式 + /// 对齐点(对齐方式是左,此参数无效,为null不为左就报错) + /// 文字样式id + /// 文字高度 + /// 数据库 + public TextInfo(string? contents, + Point3d position, + AttachmentPoint justify, + Point3d? justifyPoint = null, + ObjectId? textStyleId = null, + double textHeight = 2.5, + Database? database = null) + { + Contents = contents; + Position = position; + TextJustify = justify; + + if (justifyPoint is null && TextJustify != AttachmentPoint.BaseLeft) + throw new ArgumentNullException(nameof(justifyPoint)); + + AlignmentPoint = justifyPoint; + TextHeight = textHeight; + TextStyleId = textStyleId; + Database = database; + } + + /// + /// 创建单行文字 + /// + public DBText AddDBTextToEntity() + { + if (string.IsNullOrEmpty(Contents)) + throw new ArgumentNullException(nameof(Contents) + "创建文字无内容"); + + var acText = new DBText(); + acText.SetDatabaseDefaults(); + + if (Database is not null) + acText.SetDatabaseDefaults(Database);//我的默认值是填满的,所以可以不需要 + + if (TextStyleId is not null) + acText.SetTextStyleId(TextStyleId.Value); + + acText.Height = TextHeight; //高度 + acText.TextString = Contents; //内容 + acText.Position = Position; //插入点(一定要先设置) + acText.Justify = TextJustify; //使他们对齐 + //acText.HorizontalMode + + if (AlignmentPoint is not null) + acText.AlignmentPoint = AlignmentPoint.Value; + else if (acText.Justify != AttachmentPoint.BaseLeft) + acText.AlignmentPoint = Position; + + if (Database is not null) + acText.AdjustAlignment(Database); + return acText; + } + + /// + /// 创建多行文字 + /// + /// + public MText AddMTextToEntity() + { + if (string.IsNullOrEmpty(Contents)) + throw new ArgumentNullException(nameof(Contents) + "创建文字无内容"); + + var mText = new MText(); + mText.SetDatabaseDefaults(); + + if (Database is not null) + mText.SetDatabaseDefaults(Database); + + if (TextStyleId is not null) + mText.SetTextStyleId(TextStyleId.Value); + + mText.TextHeight = TextHeight; //高度 + mText.Contents = Contents; //内容 + mText.Location = Position; //插入点(一定要先设置) + + //mText.SetAttachmentMovingLocation(TextJustify); + mText.Attachment = TextJustify;//使他们对齐 + + return mText; + } +} + +//反射设定对象的文字样式id +public static partial class TextInfoHelper +{ + /// + /// 设置文字样式id + /// + /// 单行文字 + /// 文字样式表记录id + public static void SetTextStyleId(this DBText acText, ObjectId ltrObjectId) + { + SetEntityTxtStyleId(acText, ltrObjectId); + } + + /// + /// 设置文字样式id + /// + /// 多行文字 + /// 文字样式表记录id + public static void SetTextStyleId(this MText acText, ObjectId ltrObjectId) + { + SetEntityTxtStyleId(acText, ltrObjectId); + } + + static void SetEntityTxtStyleId(Entity acText, ObjectId ltrObjectId) + { + GetTextStyleIdType(acText)?.SetValue(acText, ltrObjectId, null); + } + + /// + /// 获取文字样式id + /// + public static ObjectId GetTextStyleId(this DBText acText) + { + return GetEntityTxtStyleId(acText); + } + + /// + /// 获取文字样式id + /// + public static ObjectId GetTextStyleId(this MText acText) + { + return GetEntityTxtStyleId(acText); + } + + static ObjectId GetEntityTxtStyleId(Entity acText) + { + var result = ObjectId.Null; + var id = GetTextStyleIdType(acText)?.GetValue(acText, null); + if (id != null) + result = (ObjectId)id; + return result; + } + + static PropertyInfo? _textStyleId = null; + static PropertyInfo GetTextStyleIdType(Entity acText) + { + if (_textStyleId == null) + { + var entType = acText.GetType(); + var prs = entType.GetProperties(); + _textStyleId = prs.FirstOrDefault(a => a.Name == "TextStyle");//反射获取属性 + if (_textStyleId == null) + _textStyleId = prs.FirstOrDefault(a => a.Name == "TextStyleId");//反射获取属性 + } + return _textStyleId; + } +} diff --git a/src/IFoxCAD.Cad/GlobalUsings.cs b/src/IFoxCAD.Cad/GlobalUsings.cs new file mode 100644 index 0000000..a125299 --- /dev/null +++ b/src/IFoxCAD.Cad/GlobalUsings.cs @@ -0,0 +1,41 @@ +/// 系统引用 +global using System; +global using System.Collections; +global using System.Collections.Generic; +global using System.IO; +global using System.Linq; +global using System.Text; +global using System.Reflection; +global using System.Text.RegularExpressions; +global using Microsoft.Win32; +global using System.ComponentModel; +global using System.Runtime.InteropServices; + +global using Exception = System.Exception; + +/// autocad 引用 +global using Autodesk.AutoCAD.ApplicationServices; +global using Autodesk.AutoCAD.EditorInput; +global using Autodesk.AutoCAD.Colors; +global using Autodesk.AutoCAD.DatabaseServices; +global using Autodesk.AutoCAD.Geometry; +global using Autodesk.AutoCAD.Runtime; +global using Acap = Autodesk.AutoCAD.ApplicationServices.Application; + +global using Autodesk.AutoCAD.DatabaseServices.Filters; +global using Autodesk.AutoCAD; + +//jig此命名空间容易引起Polyline等等重义,因此不放入全局空间 +//using Autodesk.AutoCAD.GraphicsInterface; +global using WorldDraw = Autodesk.AutoCAD.GraphicsInterface.WorldDraw; +global using Manager = Autodesk.AutoCAD.GraphicsSystem.Manager; + +global using Group = Autodesk.AutoCAD.DatabaseServices.Group; +global using Viewport = Autodesk.AutoCAD.DatabaseServices.Viewport; + +global using System.Collections.Specialized; +global using Registry = Microsoft.Win32.Registry; +global using RegistryKey = Microsoft.Win32.RegistryKey; + +/// ifoxcad.basal 引用 +global using IFoxCAD.Basal; \ No newline at end of file diff --git a/src/IFoxCAD.Cad/IFoxCAD.Cad.csproj b/src/IFoxCAD.Cad/IFoxCAD.Cad.csproj index d742bef..ac9bf4e 100644 --- a/src/IFoxCAD.Cad/IFoxCAD.Cad.csproj +++ b/src/IFoxCAD.Cad/IFoxCAD.Cad.csproj @@ -1,66 +1,90 @@ - - - - preview - enable + - net35;net40 - true - 0.1.3 - InspireFunction - xsfhlzh;vicwjb - 基于.NET的Cad二次开发类库 - InspireFunction - https://gitee.com/inspirefunction/ifoxcad - https://gitee.com/inspirefunction/ifoxcad.git - git - IFoxCAD;CAD;AutoCad;C#;NET - Optimize and add multiple functions. - true - true - true - LICENSE - true - + + preview + enable - - - - - - runtime - - - + net35;net40;net45 + true + true + true + 0.3.6.1 + InspireFunction + xsfhlzh;vicwjb + 基于.NET的Cad二次开发类库 + InspireFunction + https://gitee.com/inspirefunction/ifoxcad + https://gitee.com/inspirefunction/ifoxcad.git + git + IFoxCAD;CAD;AutoCad;C#;NET + 增加四叉树测试. + true + true + true + LICENSE + true + x64 + - - DEBUG - - - $(Configuration);ac2009 - - - $(Configuration);ac2013 - + + + + - - - True - - - + + + + - - - + + + + - - - + + DEBUG + + + $(Configuration);ac2008;ac2009 + + + $(Configuration);ac2013 + + + $(Configuration);ac2015 + + + 1701;1702;CS1685 + + + 1701;1702;CS1685 + + + 1701;1702;CS1685 + + + 1701;1702;CS1685 + + + 1701;1702;CS1685 + + + 1701;1702;CS1685 + - - - + + + True + + + + + + + + diff --git a/src/IFoxCAD.Cad/IFoxCAD.Cad.csproj.data b/src/IFoxCAD.Cad/IFoxCAD.Cad.csproj.data deleted file mode 100644 index 5f28270..0000000 --- a/src/IFoxCAD.Cad/IFoxCAD.Cad.csproj.data +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/src/IFoxCAD.Cad/ResultData/LispDottedPair.cs b/src/IFoxCAD.Cad/ResultData/LispDottedPair.cs index 0aa2f69..009d13e 100644 --- a/src/IFoxCAD.Cad/ResultData/LispDottedPair.cs +++ b/src/IFoxCAD.Cad/ResultData/LispDottedPair.cs @@ -1,72 +1,68 @@ -using Autodesk.AutoCAD.DatabaseServices; -using Autodesk.AutoCAD.Runtime; +namespace IFoxCAD.Cad; -using System.Collections.Generic; - -namespace IFoxCAD.Cad +/// +/// lisp点对表的数据封装类 +/// +public class LispDottedPair : LispList { + #region 构造函数 /// - /// lisp 点对表的数据封装类 + /// 默认无参构造函数 /// - public class LispDottedPair : LispList + public LispDottedPair() { - #region 构造函数 - /// - /// 默认无参构造函数 - /// - public LispDottedPair() - { - } + } - /// - /// 构造函数 - /// - /// TypedValue 迭代器 - public LispDottedPair(IEnumerable values) : base(values) - { - } - /// - /// 构造函数 - /// - /// 点对表左数 - /// 点对表右数 - public LispDottedPair(TypedValue left, TypedValue right) - { - Add(left); - Add(right); - } - #endregion + /// + /// 构造函数 + /// + /// TypedValue 迭代器 + public LispDottedPair(IEnumerable values) : base(values) + { + } + /// + /// 构造函数 + /// + /// 点对表左数 + /// 点对表右数 + public LispDottedPair(TypedValue left, TypedValue right) + { + Add(left); + Add(right); + } + #endregion - /// - /// 点对表的值 - /// - public override List Value + #region 重写 + /// + /// 点对表的值 + /// + public override List Value + { + get { - get - { - var value = new List + var value = new List { new TypedValue((int)LispDataType.ListBegin,-1), new TypedValue((int)LispDataType.DottedPair,-1) }; - value.InsertRange(1, this); - return value; - } + value.InsertRange(1, this); + return value; } + } + #endregion - #region 转换器 + #region 转换器 - /// - /// LispDottedPair 隐式转换到 TypedValue 数组 - /// - /// TypedValueList 实例 - public static implicit operator TypedValue[](LispDottedPair values) => values.Value.ToArray(); - /// - /// LispDottedPair 隐式转换到 ResultBuffer - /// - /// TypedValueList 实例 - public static implicit operator ResultBuffer(LispDottedPair values) => new(values.Value.ToArray()); + /// + /// LispDottedPair 隐式转换到 TypedValue 数组 + /// + /// TypedValueList 实例 + public static implicit operator TypedValue[](LispDottedPair values) => values.Value.ToArray(); + /// + /// LispDottedPair 隐式转换到 ResultBuffer + /// + /// TypedValueList 实例 + public static implicit operator ResultBuffer(LispDottedPair values) => new(values.Value.ToArray()); - #endregion - } + #endregion } diff --git a/src/IFoxCAD.Cad/ResultData/LispList.cs b/src/IFoxCAD.Cad/ResultData/LispList.cs index 4742d38..8c72669 100644 --- a/src/IFoxCAD.Cad/ResultData/LispList.cs +++ b/src/IFoxCAD.Cad/ResultData/LispList.cs @@ -1,202 +1,195 @@ -using Autodesk.AutoCAD.DatabaseServices; -using Autodesk.AutoCAD.EditorInput; -using Autodesk.AutoCAD.Geometry; -using Autodesk.AutoCAD.Runtime; -using System; -using System.Collections; -using System.Collections.Generic; -using System.Linq; +namespace IFoxCAD.Cad; -namespace IFoxCAD.Cad +/// +/// lisp数据封装类 +/// +public class LispList : TypedValueList { + #region 构造函数 /// - /// lisp数据封装类 + /// 默认构造函数 /// - public class LispList : TypedValueList - { - #region 构造函数 - /// - /// 默认构造函数 - /// - public LispList() { } + public LispList() { } + + /// + /// 构造函数 + /// + /// TypedValue 迭代器 + public LispList(IEnumerable values) : base(values) { } + #endregion - /// - /// 构造函数 - /// - /// TypedValue 迭代器 - public LispList(IEnumerable values) : base(values) { } - #endregion - /// - /// lisp 列表的值 - /// - public virtual List Value + #region 重写 + /// + /// lisp 列表的值 + /// + public virtual List Value + { + get { - get - { - var value = new List + var value = new List { new TypedValue((int)LispDataType.ListBegin,-1), new TypedValue((int)LispDataType.ListEnd,-1) }; - value.InsertRange(1, this); - return value; - } + value.InsertRange(1, this); + return value; } + } + #endregion - #region 添加数据 - /// - /// 添加数据 - /// - /// 组码 - /// 组码值 - public override void Add(int code, object obj) + #region 添加数据 + /// + /// 添加数据 + /// + /// 组码 + /// 组码值 + public override void Add(int code, object? obj) + { + if (code < 5000) { - if (code < 5000) - { - throw new System.Exception("传入的组码值不是 lisp数据 有效范围!"); - } - Add(new TypedValue(code, obj)); + throw new System.Exception("传入的组码值不是 lisp数据 有效范围!"); } + Add(new TypedValue(code, obj)); + } - /// - /// 添加数据 - /// - /// dxfcode枚举值 - /// 组码值 - public void Add(LispDataType code, object obj) - { - Add((int)code, obj); - } - /// - /// 添加数据,参数为true时添加 lisp 中的 T,false时添加 lisp 中的 nil - /// - /// bool 型的数据 - public void Add(bool value) - { - if (value) - { - Add(LispDataType.T_atom, true); - } - else - { - Add(LispDataType.Nil, null); - } - } - /// - /// 添加字符串 - /// - /// 字符串 - public void Add(string value) - { - Add(LispDataType.Text, value); - } - /// - /// 添加短整型数 - /// - /// 短整型数 - public void Add(short value) - { - Add(LispDataType.Int16, value); - } - /// - /// 添加整型数 - /// - /// 整型数 - public void Add(int value) - { - Add(LispDataType.Int32, value); - } - /// - /// 添加浮点数 - /// - /// 浮点数 - public void Add(double value) - { - Add(LispDataType.Double, value); - } - /// - /// 添加对象id - /// - /// 对象id - public void Add(ObjectId value) - { - Add(LispDataType.ObjectId, value); - } - /// - /// 添加选择集 - /// - /// 选择集 - public void Add(SelectionSet value) - { - Add(LispDataType.SelectionSet, value); - } - /// - /// 添加二维点 - /// - /// 二维点 - public void Add(Point2d value) - { - Add(LispDataType.Point2d, value); - } - /// - /// 添加三维点 - /// - /// 三维点 - public void Add(Point3d value) - { - Add(LispDataType.Point3d, value); - } - /// - /// 添加二维点 - /// - /// X - /// Y - public void Add(double x, double y) - { - Add(LispDataType.Point2d, new Point2d(x, y)); - } - /// - /// 添加三维点 - /// - /// X - /// Y - /// Z - public void Add(double x, double y, double z) + /// + /// 添加数据 + /// + /// dxfcode枚举值 + /// 组码值 + public void Add(LispDataType code, object? obj) + { + Add((int)code, obj); + } + /// + /// 添加数据,参数为true时添加 lisp 中的 T,false时添加 lisp 中的 nil + /// + /// bool 型的数据 + public void Add(bool value) + { + if (value) { - Add(LispDataType.Point3d, new Point3d(x, y, z)); + Add(LispDataType.T_atom, true); } - /// - /// 添加列表 - /// - /// lisp 列表 - public void Add(LispList value) + else { - this.AddRange(value.Value); + Add(LispDataType.Nil, null); } + } + /// + /// 添加字符串 + /// + /// 字符串 + public void Add(string value) + { + Add(LispDataType.Text, value); + } + /// + /// 添加短整型数 + /// + /// 短整型数 + public void Add(short value) + { + Add(LispDataType.Int16, value); + } + /// + /// 添加整型数 + /// + /// 整型数 + public void Add(int value) + { + Add(LispDataType.Int32, value); + } + /// + /// 添加浮点数 + /// + /// 浮点数 + public void Add(double value) + { + Add(LispDataType.Double, value); + } + /// + /// 添加对象id + /// + /// 对象id + public void Add(ObjectId value) + { + Add(LispDataType.ObjectId, value); + } + /// + /// 添加选择集 + /// + /// 选择集 + public void Add(SelectionSet value) + { + Add(LispDataType.SelectionSet, value); + } + /// + /// 添加二维点 + /// + /// 二维点 + public void Add(Point2d value) + { + Add(LispDataType.Point2d, value); + } + /// + /// 添加三维点 + /// + /// 三维点 + public void Add(Point3d value) + { + Add(LispDataType.Point3d, value); + } + /// + /// 添加二维点 + /// + /// X + /// Y + public void Add(double x, double y) + { + Add(LispDataType.Point2d, new Point2d(x, y)); + } + /// + /// 添加三维点 + /// + /// X + /// Y + /// Z + public void Add(double x, double y, double z) + { + Add(LispDataType.Point3d, new Point3d(x, y, z)); + } + /// + /// 添加列表 + /// + /// lisp 列表 + public void Add(LispList value) + { + this.AddRange(value.Value); + } - #endregion + #endregion - #region 转换器 - /// - /// ResultBuffer 隐式转换到 LispList - /// - /// ResultBuffer 实例 - public static implicit operator LispList(ResultBuffer buffer) => new(buffer.AsArray()); - /// - /// LispList 隐式转换到 TypedValue 数组 - /// - /// TypedValueList 实例 - public static implicit operator TypedValue[](LispList values) => values.Value.ToArray(); - /// - /// LispList 隐式转换到 ResultBuffer - /// - /// TypedValueList 实例 - public static implicit operator ResultBuffer(LispList values) => new(values.Value.ToArray()); - /// - /// TypedValue 数组隐式转换到 LispList - /// - /// TypedValue 数组 - public static implicit operator LispList(TypedValue[] values) => new(values); - #endregion - } + #region 转换器 + /// + /// ResultBuffer 隐式转换到 LispList + /// + /// ResultBuffer 实例 + public static implicit operator LispList(ResultBuffer buffer) => new(buffer.AsArray()); + /// + /// LispList 隐式转换到 TypedValue 数组 + /// + /// TypedValueList 实例 + public static implicit operator TypedValue[](LispList values) => values.Value.ToArray(); + /// + /// LispList 隐式转换到 ResultBuffer + /// + /// TypedValueList 实例 + public static implicit operator ResultBuffer(LispList values) => new(values.Value.ToArray()); + /// + /// TypedValue 数组隐式转换到 LispList + /// + /// TypedValue 数组 + public static implicit operator LispList(TypedValue[] values) => new(values); + #endregion } diff --git a/src/IFoxCAD.Cad/ResultData/TypedValueList.cs b/src/IFoxCAD.Cad/ResultData/TypedValueList.cs index 2fccce5..896a226 100644 --- a/src/IFoxCAD.Cad/ResultData/TypedValueList.cs +++ b/src/IFoxCAD.Cad/ResultData/TypedValueList.cs @@ -1,69 +1,63 @@ -using Autodesk.AutoCAD.DatabaseServices; +namespace IFoxCAD.Cad; -using System.Collections.Generic; - -namespace IFoxCAD.Cad +/// +/// 用于集中管理扩展数据/扩展字典/resultbuffer的类 +/// +public class TypedValueList : List { + #region 构造函数 /// - /// 用于集中管理扩展数据/扩展字典/resultbuffer的类 + /// 默认无参构造函数 /// - public class TypedValueList : List - { - #region 构造函数 - /// - /// 默认无参构造函数 - /// - public TypedValueList() { } - /// - /// 采用 TypedValue 迭代器构造 TypedValueList - /// - /// - public TypedValueList(IEnumerable values) : base(values) { } - - #endregion + public TypedValueList() { } + /// + /// 采用 TypedValue 迭代器构造 TypedValueList + /// + /// + public TypedValueList(IEnumerable values) : base(values) { } + #endregion - #region 添加数据 - /// - /// 添加数据 - /// - /// 组码 - /// 组码值 - public virtual void Add(int code, object obj) - { - Add(new TypedValue(code, obj)); - } + #region 添加数据 + /// + /// 添加数据 + /// + /// 组码 + /// 组码值 + public virtual void Add(int code, object obj) + { + Add(new TypedValue(code, obj)); + } - #endregion + #endregion - #region 转换器 - /// - /// ResultBuffer 隐式转换到 TypedValueList - /// - /// ResultBuffer 实例 - public static implicit operator TypedValueList(ResultBuffer buffer) => new(buffer.AsArray()); - /// - /// TypedValueList 隐式转换到 TypedValue 数组 - /// - /// TypedValueList 实例 - public static implicit operator TypedValue[](TypedValueList values) => values.ToArray(); - /// - /// TypedValueList 隐式转换到 ResultBuffer - /// - /// TypedValueList 实例 - public static implicit operator ResultBuffer(TypedValueList values) => new(values); - /// - /// TypedValue 数组隐式转换到 TypedValueList - /// - /// TypedValue 数组 - public static implicit operator TypedValueList(TypedValue[] values) => new(values); - /// - /// 转换为字符串 - /// - /// ResultBuffer 字符串 - public override string ToString() - { - return new ResultBuffer(this).ToString(); - } - #endregion + #region 转换器 + /// + /// ResultBuffer 隐式转换到 TypedValueList + /// + /// ResultBuffer 实例 + public static implicit operator TypedValueList(ResultBuffer buffer) => new(buffer.AsArray()); + /// + /// TypedValueList 隐式转换到 TypedValue 数组 + /// + /// TypedValueList 实例 + public static implicit operator TypedValue[](TypedValueList values) => values.ToArray(); + /// + /// TypedValueList 隐式转换到 ResultBuffer + /// + /// TypedValueList 实例 + public static implicit operator ResultBuffer(TypedValueList values) => new(values); + /// + /// TypedValue 数组隐式转换到 TypedValueList + /// + /// TypedValue 数组 + public static implicit operator TypedValueList(TypedValue[] values) => new(values); + /// + /// 转换为字符串 + /// + /// ResultBuffer 字符串 + public override string ToString() + { + return new ResultBuffer(this).ToString(); } + #endregion } diff --git a/src/IFoxCAD.Cad/ResultData/XRecordDataList.cs b/src/IFoxCAD.Cad/ResultData/XRecordDataList.cs index 0364f9a..1fb19d8 100644 --- a/src/IFoxCAD.Cad/ResultData/XRecordDataList.cs +++ b/src/IFoxCAD.Cad/ResultData/XRecordDataList.cs @@ -1,65 +1,68 @@ -using Autodesk.AutoCAD.DatabaseServices; +namespace IFoxCAD.Cad; -using System.Collections.Generic; - -namespace IFoxCAD.Cad +/// +/// 扩展字典数据封装类 +/// +public class XRecordDataList : TypedValueList { + #region 构造函数 /// /// 扩展字典数据封装类 /// - public class XRecordDataList : TypedValueList - { - public XRecordDataList() - { - } - public XRecordDataList(IEnumerable values) : base(values) { } - #region 添加数据 - /// - /// 添加数据 - /// - /// 组码 - /// 组码值 - public override void Add(int code, object obj) - { - if (code >= 1000) - { - throw new System.Exception("传入的组码值不是 XRecordData 有效范围!"); - } - Add(new TypedValue(code, obj)); - } + public XRecordDataList() { } - /// - /// 添加数据 - /// - /// dxfcode枚举值 - /// 组码值 - public void Add(DxfCode code, object obj) + /// + /// 扩展字典数据封装类 + /// + public XRecordDataList(IEnumerable values) : base(values) { } + #endregion + + #region 添加数据 + /// + /// 添加数据 + /// + /// 组码 + /// 组码值 + public override void Add(int code, object obj) + { + if (code >= 1000) { - Add((int)code, obj); + throw new System.Exception("传入的组码值不是 XRecordData 有效范围!"); } - #endregion + Add(new TypedValue(code, obj)); + } - #region 转换器 - /// - /// ResultBuffer 隐式转换到 XRecordDataList - /// - /// ResultBuffer 实例 - public static implicit operator XRecordDataList(ResultBuffer buffer) => new(buffer.AsArray()); - /// - /// XRecordDataList 隐式转换到 TypedValue 数组 - /// - /// TypedValueList 实例 - public static implicit operator TypedValue[](XRecordDataList values) => values.ToArray(); - /// - /// XRecordDataList 隐式转换到 ResultBuffer - /// - /// TypedValueList 实例s - public static implicit operator ResultBuffer(XRecordDataList values) => new(values); - /// - /// TypedValue 数组隐式转换到 XRecordDataList - /// - /// TypedValue 数组 - public static implicit operator XRecordDataList(TypedValue[] values) => new(values); - #endregion + /// + /// 添加数据 + /// + /// dxfcode枚举值 + /// 组码值 + public void Add(DxfCode code, object obj) + { + Add((int)code, obj); } + #endregion + + #region 转换器 + /// + /// ResultBuffer 隐式转换到 XRecordDataList + /// + /// ResultBuffer 实例 + public static implicit operator XRecordDataList(ResultBuffer buffer) => new(buffer.AsArray()); + /// + /// XRecordDataList 隐式转换到 TypedValue 数组 + /// + /// TypedValueList 实例 + public static implicit operator TypedValue[](XRecordDataList values) => values.ToArray(); + /// + /// XRecordDataList 隐式转换到 ResultBuffer + /// + /// TypedValueList 实例s + public static implicit operator ResultBuffer(XRecordDataList values) => new(values); + /// + /// TypedValue 数组隐式转换到 XRecordDataList + /// + /// TypedValue 数组 + public static implicit operator XRecordDataList(TypedValue[] values) => new(values); + #endregion } diff --git a/src/IFoxCAD.Cad/ResultData/XdataList.cs b/src/IFoxCAD.Cad/ResultData/XdataList.cs index e22ffe2..a666100 100644 --- a/src/IFoxCAD.Cad/ResultData/XdataList.cs +++ b/src/IFoxCAD.Cad/ResultData/XdataList.cs @@ -1,71 +1,68 @@ -using Autodesk.AutoCAD.DatabaseServices; +namespace IFoxCAD.Cad; -using System.Collections.Generic; -using System.Linq; - -namespace IFoxCAD.Cad +/// +/// 扩展数据封装类 +/// +public class XDataList : TypedValueList { - + #region 构造函数 /// /// 扩展数据封装类 /// - public class XDataList : TypedValueList - { - public XDataList() - { - } + public XDataList() { } - public XDataList(IEnumerable values) : base(values) { } + /// + /// 扩展数据封装类 + /// + public XDataList(IEnumerable values) : base(values) { } + #endregion - #region 添加数据 - /// - /// 添加数据 - /// - /// 组码 - /// 组码值 - public override void Add(int code, object obj) - { - if (code < 1000 || code > 1071) - { - throw new System.Exception("传入的组码值不是XData有效范围!"); - } - Add(new TypedValue(code, obj)); - } + #region 添加数据 + /// + /// 添加数据 + /// + /// 组码 + /// 组码值 + public override void Add(int code, object obj) + { + if (code < 1000 || code > 1071) + throw new System.Exception("传入的组码值不是XData有效范围!"); - /// - /// 添加数据 - /// - /// dxfcode枚举值 - /// 组码值 - public void Add(DxfCode code, object obj) - { + Add(new TypedValue(code, obj)); + } - Add((int)code, obj); - } + /// + /// 添加数据 + /// + /// dxfcode枚举值 + /// 组码值 + public void Add(DxfCode code, object obj) + { + Add((int)code, obj); + } - #endregion + #endregion - #region 转换器 - /// - /// ResultBuffer 隐式转换到 XDataList - /// - /// ResultBuffer 实例 - public static implicit operator XDataList(ResultBuffer buffer) => new(buffer.AsArray()); - /// - /// XDataList 隐式转换到 TypedValue 数组 - /// - /// TypedValueList 实例 - public static implicit operator TypedValue[](XDataList values) => values.ToArray(); - /// - /// XDataList 隐式转换到 ResultBuffer - /// - /// TypedValueList 实例 - public static implicit operator ResultBuffer(XDataList values) => new(values); - /// - /// TypedValue 数组隐式转换到 XDataList - /// - /// TypedValue 数组 - public static implicit operator XDataList(TypedValue[] values) => new(values); - #endregion - } + #region 转换器 + /// + /// ResultBuffer 隐式转换到 XDataList + /// + /// ResultBuffer 实例 + public static implicit operator XDataList(ResultBuffer buffer) => new(buffer.AsArray()); + /// + /// XDataList 隐式转换到 TypedValue 数组 + /// + /// TypedValueList 实例 + public static implicit operator TypedValue[](XDataList values) => values.ToArray(); + /// + /// XDataList 隐式转换到 ResultBuffer + /// + /// TypedValueList 实例 + public static implicit operator ResultBuffer(XDataList values) => new(values); + /// + /// TypedValue 数组隐式转换到 XDataList + /// + /// TypedValue 数组 + public static implicit operator XDataList(TypedValue[] values) => new(values); + #endregion } diff --git a/src/IFoxCAD.Cad/Runtime/AOP.cs b/src/IFoxCAD.Cad/Runtime/AOP.cs new file mode 100644 index 0000000..ac8270b --- /dev/null +++ b/src/IFoxCAD.Cad/Runtime/AOP.cs @@ -0,0 +1,99 @@ +//namespace IFoxCAD.Cad; +//using HarmonyLib; + +//public class IFoxRefuseInjectionTransaction : Attribute +//{ +// /// +// /// 拒绝注入事务 +// /// +// public IFoxRefuseInjectionTransaction() +// { +// } +//} + +//public class AOP +//{ +// /// +// /// 在此命名空间下的命令末尾注入清空事务栈函数 +// /// +// public static void Run(string nameSpace) +// { +// Dictionary cmdDic = new(); +// AutoClass.AppDomainGetTypes(type => { +// if (type.Namespace != nameSpace) +// return; +// //类上面特性 +// if (type.IsClass) +// { +// var attr = type.GetCustomAttributes(true); +// if (RefuseInjectionTransaction(attr)) +// return; +// } + +// //函数上面特性 +// var mets = type.GetMethods();//获得它的成员函数 +// for (int ii = 0; ii < mets.Length; ii++) +// { +// var method = mets[ii]; +// //找到特性,特性下面的方法要是Public,否则就被编译器优化掉了. +// var attr = method.GetCustomAttributes(true); +// for (int jj = 0; jj < attr.Length; jj++) +// if (attr[jj] is CommandMethodAttribute cmdAtt) +// { +// if (!RefuseInjectionTransaction(attr)) +// cmdDic.Add(cmdAtt.GlobalName, (cmdAtt, type, method)); +// } +// } +// }); + +// //运行的命令写在了Test.dll,当然不是ifox.cad类库内了.... +// if (cmdDic.Count == 0) +// return; + +// var harmony = new Harmony(nameSpace); +// var mPrefix = SymbolExtensions.GetMethodInfo(() => IFoxCmdAddFirst());//进入函数前 +// var mPostfix = SymbolExtensions.GetMethodInfo(() => IFoxCmdAddLast());//进入函数后 +// var mp1 = new HarmonyMethod(mPrefix); +// var mp2 = new HarmonyMethod(mPostfix); + +// foreach (var item in cmdDic) +// { +// //原函数执行(空间type,函数名) +// var mOriginal = AccessTools.Method(item.Value.MetType, item.Value.MetInfo.Name); +// //mOriginal.Invoke(); +// //新函数执行:创造两个函数加入里面 +// var newMet = harmony.Patch(mOriginal, mp1, mp2); +// //newMet.Invoke(); +// } +// } + +// /// +// /// 拒绝注入事务 +// /// +// /// 属性 +// /// +// private static bool RefuseInjectionTransaction(object[] attr) +// { +// bool refuseInjectionTransaction = false; +// for (int kk = 0; kk < attr.Length; kk++) +// { +// if (attr[kk] is IFoxRefuseInjectionTransaction) +// { +// refuseInjectionTransaction = true; +// break; +// } +// } +// return refuseInjectionTransaction; +// } + +// public static void IFoxCmdAddFirst() +// { +// //此生命周期会在静态事务栈上面,被无限延长 +// var _ = DBTrans.Top; +// } + +// public static void IFoxCmdAddLast() +// { +// DBTrans.FinishDatabase(); +// } +//} \ No newline at end of file diff --git a/src/IFoxCAD.Cad/Runtime/AcadVersion.cs b/src/IFoxCAD.Cad/Runtime/AcadVersion.cs index 7be301f..f8f5273 100644 --- a/src/IFoxCAD.Cad/Runtime/AcadVersion.cs +++ b/src/IFoxCAD.Cad/Runtime/AcadVersion.cs @@ -1,130 +1,72 @@ -using Microsoft.Win32; -using System; -using System.Collections.Generic; -using System.Reflection; -using System.Text.RegularExpressions; +namespace IFoxCAD.Cad; -namespace IFoxCAD.Cad +/// +/// cad版本号类 +/// +public static class AcadVersion { + private static readonly string _pattern = @"Autodesk\\AutoCAD\\R(\d+)\.(\d+)\\.*?"; + /// - /// cad版本号类 + /// 所有安装的cad的版本号 /// - public class AcadVersion + public static List Versions { - /// - /// 主版本 - /// - public int Major - { private set; get; } - - /// - /// 次版本 - /// - public int Minor - { private set; get; } - - /// - /// 版本号 - /// - public double ProgId => double.Parse($"{Major}.{Minor}"); - - /// - /// 注册表名称 - /// - public string ProductName - { private set; get; } - - /// - /// 注册表位置 - /// - public string ProductRootKey - { private set; get; } - - private static readonly string _pattern = @"Autodesk\\AutoCAD\\R(\d+)\.(\d+)\\.*?"; - - private static List _versions; - - /// - /// 所有安装的cad的版本号 - /// - public static List Versions + get { - get + string[] copys = Registry.LocalMachine + .OpenSubKey(@"SOFTWARE\Autodesk\Hardcopy") + .GetValueNames(); + + var _versions = new List(); + foreach (var rootkey in copys) { - if (_versions is null) - { - string[] copys = - Registry.LocalMachine - .OpenSubKey(@"SOFTWARE\Autodesk\Hardcopy") - .GetValueNames(); - _versions = new List(); - foreach (var rootkey in copys) - { - if (Regex.IsMatch(rootkey, _pattern)) - { - var gs = Regex.Match(rootkey, _pattern).Groups; - var ver = - new AcadVersion - { - ProductRootKey = rootkey, - ProductName = - Registry.LocalMachine - .OpenSubKey("SOFTWARE") - .OpenSubKey(rootkey) - .GetValue("ProductName") - .ToString(), + if (!Regex.IsMatch(rootkey, _pattern)) + continue; - Major = int.Parse(gs[1].Value), - Minor = int.Parse(gs[2].Value), - }; + var gs = Regex.Match(rootkey, _pattern).Groups; + var ver = new CadVersion + { + ProductRootKey = rootkey, + ProductName = Registry.LocalMachine + .OpenSubKey("SOFTWARE") + .OpenSubKey(rootkey) + .GetValue("ProductName") + .ToString(), - _versions.Add(ver); - } - } - } - return _versions; + Major = int.Parse(gs[1].Value), + Minor = int.Parse(gs[2].Value), + }; + _versions.Add(ver); } + return _versions; } + } - /// 已打开的cad的版本号 - /// 已打开cad的application对象 - /// cad版本号对象 - public static AcadVersion FromApp(object app) - { - if (app is null) - { - throw new ArgumentNullException(nameof(app)); - } - - string acver = - app.GetType() - .InvokeMember( - "Version", - BindingFlags.GetProperty, - null, - app, - new object[0]).ToString(); - - var gs = Regex.Match(acver, @"(\d+)\.(\d+).*?").Groups; - int major = int.Parse(gs[1].Value); - int minor = int.Parse(gs[2].Value); - foreach (var ver in Versions) - { - if (ver.Major == major && ver.Minor == minor) - return ver; - } + /// 已打开的cad的版本号 + /// 已打开cad的application对象 + /// cad版本号对象 + public static CadVersion? FromApp(object app) + { + if (app == null) + throw new ArgumentNullException(nameof(app)); - return null; - } + string acver = app.GetType() + .InvokeMember( + "Version", + BindingFlags.GetProperty, + null, + app, + new object[0]).ToString(); - /// - /// 转换为字符串 - /// - /// 表示版本号的字符串 - public override string ToString() + var gs = Regex.Match(acver, @"(\d+)\.(\d+).*?").Groups; + int major = int.Parse(gs[1].Value); + int minor = int.Parse(gs[2].Value); + foreach (var ver in Versions) { - return - $"名称:{ProductName}\n版本号:{ProgId}\n注册表位置:{ProductRootKey}"; + if (ver.Major == major && ver.Minor == minor) + return ver; } + return null; } } diff --git a/src/IFoxCAD.Cad/Runtime/AssemInfo.cs b/src/IFoxCAD.Cad/Runtime/AssemInfo.cs index 62b70f8..c13cc26 100644 --- a/src/IFoxCAD.Cad/Runtime/AssemInfo.cs +++ b/src/IFoxCAD.Cad/Runtime/AssemInfo.cs @@ -1,36 +1,77 @@ -using System; +namespace IFoxCAD.Cad; -namespace IFoxCAD.Cad +/// +/// 程序集信息 +/// +[Serializable] +public struct AssemInfo { /// - /// 程序集信息 - /// - [Serializable] - public struct AssemInfo - { - /// - /// 注册名 - /// - public string Name { get; set; } - - /// - /// 程序集全名 - /// - public string Fullname { get; set; } - - /// - /// 程序集路径 - /// - public string Loader { get; set; } - - /// - /// 加载方式 - /// - public AssemLoadType LoadType { get; set; } - - /// - /// 程序集说明 - /// - public string Description { get; set; } - } + /// 注册名 + ///
+ public string Name; + + /// + /// 程序集全名 + /// + public string Fullname; + + /// + /// 程序集路径 + /// + public string Loader; + + /// + /// 加载方式 + /// + public AssemLoadType LoadType; + + /// + /// 程序集说明 + /// + public string Description; +} + + +/// +/// 程序集加载类型 +/// +public enum AssemLoadType +{ + /// + /// 启动 + /// + Startting = 2, + + /// + /// 随命令 + /// + ByCommand = 12, + + /// + /// 无效 + /// + Disabled = 20 +} + + +/// +/// 注册中心配置信息 +/// +public enum AutoRegConfig +{ + /// + /// 注册表 + /// + Regedit = 1, + /// + /// 反射特性 + /// + ReflectionAttribute = 2, + /// + /// 反射接口 + /// + ReflectionInterface = 4, + + All = Regedit | ReflectionAttribute | ReflectionInterface, } diff --git a/src/IFoxCAD.Cad/Runtime/AutoRegAssem.cs b/src/IFoxCAD.Cad/Runtime/AutoRegAssem.cs index 111be63..540762c 100644 --- a/src/IFoxCAD.Cad/Runtime/AutoRegAssem.cs +++ b/src/IFoxCAD.Cad/Runtime/AutoRegAssem.cs @@ -1,131 +1,169 @@ -using Autodesk.AutoCAD.DatabaseServices; -using Microsoft.Win32; -using System; -using System.IO; -using System.Linq; -using System.Reflection; -using CadRuntime = Autodesk.AutoCAD.Runtime; - -namespace IFoxCAD.Cad +namespace IFoxCAD.Cad; + + +/// +/// 注册中心 +/// 初始化程序集信息写入注册表并反射特性和接口 +/// 启动cad后的执行顺序为: +/// 1:程序集配置中心构造函数 +/// 2:特性..(多个) +/// 3:接口..(多个) +/// +public abstract class AutoRegAssem : IExtensionApplication { + #region 字段 + readonly AutoReflection _autoRef; + readonly AssemInfo _info; + #endregion + + #region 静态方法 + /// + /// 程序集的路径 + /// + public static FileInfo Location => new(Assembly.GetCallingAssembly().Location); + + /// + /// 程序集的目录 + /// + public static DirectoryInfo CurrDirectory => Location.Directory; + /// - /// 程序集加载类型 + /// 获取程序集的目录 /// - public enum AssemLoadType + /// 程序集 + /// 路径对象 + public static DirectoryInfo GetDirectory(Assembly? assem) { - /// - /// 启动 - /// - Startting = 2, - - /// - /// 随命令 - /// - ByCommand = 12, - - /// - /// 无效 - /// - Disabled = 20 + if (assem is null) + throw new(nameof(assem)); + + return new FileInfo(assem.Location).Directory; } + #endregion + #region 构造函数 /// - /// 自动加载程序集的抽象类,继承自 IExtensionApplication 接口 + /// 注册中心 /// - public abstract class AutoRegAssem : CadRuntime.IExtensionApplication + /// 配置项目 + public AutoRegAssem(AutoRegConfig autoRegConfig) { - private AssemInfo _info = new(); - - /// - /// 程序集的路径 - /// - public static FileInfo Location => new(Assembly.GetCallingAssembly().Location); - - /// - /// 程序集的目录 - /// - public static DirectoryInfo CurrDirectory => Location.Directory; - - /// - /// 获取程序集的目录 - /// - /// 程序集 - /// 路径对象 - public static DirectoryInfo GetDirectory(Assembly assem) + var assem = Assembly.GetCallingAssembly(); + _info = new() { - if (assem is null) - { - throw new(nameof(assem)); - } - return new FileInfo(assem.Location).Directory; - } + Loader = assem.Location, + Fullname = assem.FullName, + Name = assem.GetName().Name, + LoadType = AssemLoadType.Startting + }; - /// - /// 初始化程序集信息 - /// - public AutoRegAssem() + if ((autoRegConfig & AutoRegConfig.Regedit) == AutoRegConfig.Regedit) { - Assembly assem = Assembly.GetCallingAssembly(); - _info.Loader = assem.Location; - _info.Fullname = assem.FullName; - _info.Name = assem.GetName().Name; - _info.LoadType = AssemLoadType.Startting; - if (!SearchForReg()) - { RegApp(); - } } - #region RegApp + //实例化了 AutoClass 之后会自动执行 IFoxAutoGo 接口下面的类, + //以及自动执行特性 [IFoxInitialize] + //类库用户不在此处进行其他代码,而是实现特性 + _autoRef = new AutoReflection(_info.Name, autoRegConfig); + _autoRef.Initialize(); + } + #endregion - private static RegistryKey GetAcAppKey() - { -#if ac2009 - string key = HostApplicationServices.Current.RegistryProductRootKey; -#elif ac2013 - string key = HostApplicationServices.Current.MachineRegistryProductRootKey; + #region RegApp + + /// + /// 获取当前cad注册表位置 + /// + /// 打开权限 + /// + public static RegistryKey GetAcAppKey(bool writable = true) + { + RegistryKey? ackey = null; + +#if NET35 + string key = HostApplicationServices.Current.RegistryProductRootKey; //这里浩辰读出来是"" +#elif !HC2020 + string key = HostApplicationServices.Current.UserRegistryProductRootKey; #endif - RegistryKey ackey = - Registry.CurrentUser.OpenSubKey(key, true); - return ackey.CreateSubKey("Applications"); - } - private bool SearchForReg() - { - RegistryKey appkey = GetAcAppKey(); - var regApps = appkey.GetSubKeyNames(); - return regApps.Contains(_info.Name); - } +#if !HC2020 + ackey = Registry.CurrentUser.OpenSubKey(key, writable); +#else + //浩辰 + var s = GrxCAD.DatabaseServices.HostApplicationServices.Current.RegistryProductRootKey;//浩辰奇怪的空值 + string str = CadSystem.Getvar("ACADVER"); + str = Regex.Replace(str, @"[^\d.\d]", ""); + double.TryParse(str, out double a); + // string regedit = @"Software\Gstarsoft\GstarCAD\R" + a.ToString() + @"\zh-CN"; //2019 + // string regedit = @"Software\Gstarsoft\GstarCAD\B" + a.ToString() + @"\zh-CN";//2020 这里是 + string regedit = @"Software\Gstarsoft\GstarCAD\B20\zh-CN";//2020 这里是 + + ackey = Registry.CurrentUser.OpenSubKey(regedit, writable); +#endif + return ackey.CreateSubKey("Applications"); + } + + /// + /// 卸载注册表信息 + /// + public bool UnRegApp() + { + var appkey = GetAcAppKey(); + if (appkey.SubKeyCount == 0) + return false; - /// - /// 在注册表写入自动加载的程序集信息 - /// - public void RegApp() + var regApps = appkey.GetSubKeyNames(); + if (regApps.Contains(_info.Name)) { - RegistryKey appkey = GetAcAppKey(); - RegistryKey rk = appkey.CreateSubKey(_info.Name); - rk.SetValue("DESCRIPTION", _info.Fullname, RegistryValueKind.String); - rk.SetValue("LOADCTRLS", _info.LoadType, RegistryValueKind.DWord); - rk.SetValue("LOADER", _info.Loader, RegistryValueKind.String); - rk.SetValue("MANAGED", 1, RegistryValueKind.DWord); - appkey.Close(); + appkey.DeleteSubKey(_info.Name, false); + return true; } + return false; + } - #endregion RegApp + /// + /// 是否已经存在注册表 + /// + /// + bool SearchForReg() + { + var appkey = GetAcAppKey(); + if (appkey.SubKeyCount == 0) + return false; - #region IExtensionApplication 成员 + var regApps = appkey.GetSubKeyNames(); + if (regApps.Contains(_info.Name)) + { + //20220409 bug:文件名相同,路径不同,需要判断路径 + var info = appkey.OpenSubKey(_info.Name); + return info.GetValue("LOADER")?.ToString().ToLower() == _info.Loader.ToLower(); + } + return false; + } - /// - /// 初始化函数 - /// - public abstract void Initialize(); + /// + /// 在注册表写入自动加载的程序集信息 + /// + public void RegApp() + { + var appkey = GetAcAppKey(); + var rk = appkey.CreateSubKey(_info.Name); + rk.SetValue("DESCRIPTION", _info.Fullname, RegistryValueKind.String); + rk.SetValue("LOADCTRLS", _info.LoadType, RegistryValueKind.DWord); + rk.SetValue("LOADER", _info.Loader, RegistryValueKind.String); + rk.SetValue("MANAGED", 1, RegistryValueKind.DWord); + appkey.Close(); + } - /// - /// 结束函数 - /// - public abstract void Terminate(); + //这里的是不会自动执行的 + public void Initialize() { } + public void Terminate() { } - #endregion IExtensionApplication 成员 + ~AutoRegAssem() + { + _autoRef.Terminate(); } + #endregion RegApp } diff --git a/src/IFoxCAD.Cad/Runtime/CadVersion.cs b/src/IFoxCAD.Cad/Runtime/CadVersion.cs new file mode 100644 index 0000000..2e32c74 --- /dev/null +++ b/src/IFoxCAD.Cad/Runtime/CadVersion.cs @@ -0,0 +1,92 @@ +namespace IFoxCAD.Cad; + +#if ac2009 + +public class CadVersion +{ + /// + /// 主版本 + /// + public int Major { get; set; } + + /// + /// 次版本 + /// + public int Minor { get; set; } + + /// + /// 版本号 + /// + public double ProgId => double.Parse($"{Major}.{Minor}"); + + /// + /// 注册表名称 + /// + public string? ProductName { get; set; } + + /// + /// 注册表位置 + /// + public string? ProductRootKey { get; set; } + + /// + /// 转换为字符串 + /// + /// 表示版本号的字符串 + public override string ToString() + { + return $"名称:{ProductName}\n版本号:{ProgId}\n注册表位置:{ProductRootKey}"; + } + // public override bool Equals(object obj) + // { + // return base.Equals(obj); + // } + + // public override int GetHashCode() + // { + // return base.GetHashCode(); + // } + + // //public override string ToString() + // //{ + // // return base.ToString(); + // //} +} +#else +public record CadVersion +{ + /// + /// 主版本 + /// + public int Major; + + /// + /// 次版本 + /// + public int Minor; + + /// + /// 版本号 + /// + public double ProgId => double.Parse($"{Major}.{Minor}"); + + /// + /// 注册表名称 + /// + public string? ProductName; + + /// + /// 注册表位置 + /// + public string? ProductRootKey; + + /// + /// 转换为字符串 + /// + /// 表示版本号的字符串 + public override string ToString() + { + return $"名称:{ProductName}\n版本号:{ProgId}\n注册表位置:{ProductRootKey}"; + } +} +#endif \ No newline at end of file diff --git a/src/IFoxCAD.Cad/Runtime/DBTrans.cs b/src/IFoxCAD.Cad/Runtime/DBTrans.cs index 9507f17..d207edf 100644 --- a/src/IFoxCAD.Cad/Runtime/DBTrans.cs +++ b/src/IFoxCAD.Cad/Runtime/DBTrans.cs @@ -1,319 +1,440 @@ -using System; -using System.Collections.Generic; -using Autodesk.AutoCAD.DatabaseServices; -using Autodesk.AutoCAD.EditorInput; -using Autodesk.AutoCAD.ApplicationServices; -using System.IO; - -namespace IFoxCAD.Cad -{ - public class DBTrans : IDisposable - { - #region 私有字段 - /// - /// 文档锁 - /// - private DocumentLock documentLock = default; - /// - /// 是否释放资源 - /// - private bool disposedValue; - /// - /// 是否提交事务 - /// - private bool _commit; - /// - /// 事务栈 - /// - private static readonly Stack dBTrans = new(); - #endregion - - #region 公开属性 - /// - /// 返回当前事务 - /// - public static DBTrans Top => dBTrans.Peek(); - /// - /// 数据库 - /// - public Database Database { get; private set; } - /// - /// 文档 - /// - public Document Document { get; private set; } - /// - /// 命令行 - /// - public Editor Editor { get; private set; } - /// - /// 事务管理器 - /// - public Transaction Transaction { get; private set; } - - #endregion - - #region 构造函数 - /// - /// 默认构造函数,默认为打开当前文档,默认提交事务 - /// - /// 要打开的文档 - /// 事务是否提交 - public DBTrans(Document doc = null, bool commit = true, bool doclock = false) - { - doc ??= Application.DocumentManager.MdiActiveDocument; - Document = doc; - Database = Document.Database; - Editor = Document.Editor; - Init(commit, doclock); - } +namespace IFoxCAD.Cad; - /// - /// 构造函数,打开数据库,默认提交事务 - /// - /// 要打开的数据库 - /// 事务是否提交 - public DBTrans(Database database, bool commit = true) - { - Database = database; - Init(commit, false); - } - /// - /// 构造函数,打开文件,默认提交事务 - /// - /// 要打开的文件名 - /// 事务是否提交 - public DBTrans(string fileName, bool commit = true) - { - Database = new Database(false, true); - Database.ReadDwgFile(fileName, FileShare.Read, true, null); - Database.CloseInput(true); - Init(commit, false); - } - /// - /// 初始化事务及事务队列、提交模式 - /// - /// 提交模式 - private void Init(bool commit, bool doclock) - { - if (doclock) - { - documentLock = Document.LockDocument(); - } - Transaction = Database.TransactionManager.StartTransaction(); - _commit = commit; - dBTrans.Push(this); - } +/// +/// 事务栈,隐匿事务在数据库其中担任的角色 +/// +public class DBTrans : IDisposable +{ + #region 私有字段 + /// + /// 文档锁 + /// + private readonly DocumentLock? documentLock; + /// + /// 是否释放资源 + /// + private bool disposedValue; + /// + /// 是否提交事务 + /// + private readonly bool _commit; + /// + /// 事务栈 + /// + private static readonly Stack dBTrans = new(); + #endregion - #endregion - - #region 符号表 - - /// - /// 块表 - /// - public SymbolTable BlockTable => new(this, Database.BlockTableId); - /// - /// 当前绘图空间 - /// - public BlockTableRecord CurrentSpace => BlockTable.GetRecord(Database.CurrentSpaceId); - /// - /// 模型空间 - /// - public BlockTableRecord ModelSpace => BlockTable.GetRecord(BlockTable.CurrentSymbolTable[BlockTableRecord.ModelSpace]); - /// - /// 图纸空间 - /// - public BlockTableRecord PaperSpace => BlockTable.GetRecord(BlockTable.CurrentSymbolTable[BlockTableRecord.PaperSpace]); - /// - /// 层表 - /// - public SymbolTable LayerTable => new(this, Database.LayerTableId); - /// - /// 文字样式表 - /// - public SymbolTable TextStyleTable => new(this, Database.TextStyleTableId); - - /// - /// 注册应用程序表 - /// - public SymbolTable RegAppTable => new(this, Database.RegAppTableId); - - /// - /// 标注样式表 - /// - public SymbolTable DimStyleTable => new(this, Database.DimStyleTableId); - - /// - /// 线型表 - /// - public SymbolTable LinetypeTable => new(this, Database.LinetypeTableId); - - /// - /// 用户坐标系表 - /// - public SymbolTable UcsTable => new(this, Database.UcsTableId); - - /// - /// 视图表 - /// - public SymbolTable ViewTable => new(this, Database.ViewTableId); - - /// - /// 视口表 - /// - public SymbolTable ViewportTable => new(this, Database.ViewportTableId); - #endregion - - #region 字典 - //TODO: 补充关于扩展字典,命名对象字典,组字典,多线样式字典等对象字典的属性 - /// - /// 命名对象字典 - /// - public DBDictionary NamedObjectsDict => GetObject(Database.NamedObjectsDictionaryId); - /// - /// 组字典 - /// - public DBDictionary GroupDict => GetObject(Database.GroupDictionaryId); - /// - /// 多重引线样式字典 - /// - public DBDictionary MLeaderStyleDict => GetObject(Database.MLeaderStyleDictionaryId); - /// - /// 多线样式字典 - /// - public DBDictionary MLStyleDict => GetObject(Database.MLStyleDictionaryId); - /// - /// 材质字典 - /// - public DBDictionary MaterialDict => GetObject(Database.MaterialDictionaryId); - /// - /// 表格样式字典 - /// - public DBDictionary TableStyleDict => GetObject(Database.TableStyleDictionaryId); - /// - /// 视觉样式字典 - /// - public DBDictionary VisualStyleDict => GetObject(Database.VisualStyleDictionaryId); - /// - /// 颜色字典 - /// - public DBDictionary ColorDict => GetObject(Database.ColorDictionaryId); - /// - /// 打印设置字典 - /// - public DBDictionary PlotSettingsDict => GetObject(Database.PlotSettingsDictionaryId); - /// - /// 打印样式表名字典 - /// - public DBDictionary PlotStyleNameDict => GetObject(Database.PlotStyleNameDictionaryId); - /// - /// 布局字典 - /// - public DBDictionary LayoutDict => GetObject(Database.LayoutDictionaryId); - /// - /// 数据链接字典 - /// - public DBDictionary DataLinkDict => GetObject(Database.DataLinkDictionaryId); -#if ac2013 - /// - /// 详细视图样式字典 - /// - public DBDictionary DetailViewStyleDict => GetObject(Database.DetailViewStyleDictionaryId); - /// - /// 剖面视图样式字典 - /// - public DBDictionary SectionViewStyleDict => GetObject(Database.SectionViewStyleDictionaryId); -#endif - #endregion - - #region 获取对象 - /// - /// 根据对象id获取图元对象 - /// - /// 要获取的图元对象的类型 - /// 对象id - /// 打开模式,默认为只读 - /// 是否打开已删除对象,默认为不打开 - /// 是否打开锁定图层对象,默认为不打开 - /// 图元对象,类型不匹配时返回 - public T GetObject(ObjectId id, - OpenMode mode = OpenMode.ForRead, - bool openErased = false, - bool forceOpenOnLockedLayer = false) where T : DBObject + #region 公开属性 + /// + /// 返回当前事务 + /// + public static DBTrans Top + { + get { - return Transaction.GetObject(id, mode, openErased, forceOpenOnLockedLayer) as T; - } + /* + * 0x01 + * 事务栈上面有事务,这个事务属于当前文档, + * 那么直接提交原本事务然后再开一个(一直把栈前面的同数据库提交清空) + * 那不就发生跨事务读取图元了吗?....否决 + * + * 0x02 + * 跨文档事务出错 Autodesk.AutoCAD.Runtime.Exception:“eNotFromThisDocument” + * Curves.GetEntities()会从Top获取事务(Top会new一个),此时会是当前文档; + * 然后命令文中发生了 using var tr = new DBTrans(); + * 当退出命令此事务释放,但是从来不释放Top, + * 然后我新建了一个文档,再进行命令=>又进入Top,Top返回了前一个文档的事务 + * 因此所以无法清理栈,所以Dispose不触发,导致无法刷新图元和Ctrl+Z出错 + * 所以用AOP方式修复 + * + * 0x03 + * 经过艰苦卓绝的测试,aop模式由于不能断点调试,所以暂时放弃。 + */ - /// - /// 根据对象句柄字符串获取对象Id - /// - /// 句柄字符串 - /// 对象id - public ObjectId GetObjectId(string handleString) - { - var hanle = new Handle(Convert.ToInt64(handleString, 16)); - return Database.GetObjectId(false, hanle, 0); + // 由于大量的函数依赖本属性,强迫用户先开启事务 + if (dBTrans.Count == 0) + throw new ArgumentNullException("事务栈没有任何事务,请在调用前创建:" + nameof(DBTrans)); + var trans = dBTrans.Peek(); + return trans; } + } - #endregion + /// + /// 文档 + /// + public Document? Document { get; private set; } + /// + /// 命令行 + /// + public Editor? Editor { get; private set; } + /// + /// 事务管理器 + /// + public Transaction Transaction { get; private set; } + /// + /// 数据库 + /// + public Database Database { get; private set; } + #endregion + #region 构造函数 + /// + /// 事务栈 + /// 默认构造函数,默认为打开当前文档,默认提交事务 + /// + /// 要打开的文档 + /// 事务是否提交 + /// 是否锁文档 + public DBTrans(Document? doc = null, bool commit = true, bool doclock = false) + { + Document = doc ?? Application.DocumentManager.MdiActiveDocument; + Database = Document.Database; + Editor = Document.Editor; + Transaction = Database.TransactionManager.StartTransaction(); + _commit = commit; + if (doclock) + documentLock = Document.LockDocument(); + dBTrans.Push(this); + } - #region idispose接口相关函数 + /// + /// 事务栈 + /// 打开数据库,默认提交事务 + /// + /// 要打开的数据库 + /// 事务是否提交 + public DBTrans(Database database, bool commit = true) + { + Database = database; + Transaction = Database.TransactionManager.StartTransaction(); + _commit = commit; + dBTrans.Push(this); + } - public void Abort() - { - Transaction.Abort(); - } + /// + /// 事务栈 + /// 打开文件,默认提交事务 + /// + /// 要打开的文件名 + /// 事务是否提交 + /// 开图模式 + /// 密码 + public DBTrans(string fileName, + bool commit = true, +#pragma warning disable CS0436 // 类型与导入类型冲突 + FileOpenMode openMode = FileOpenMode.OpenForReadAndWriteNoShare, +#pragma warning restore CS0436 // 类型与导入类型冲突 + string? password = null) + { + if (string.IsNullOrEmpty(fileName?.Trim())) + throw new ArgumentNullException(nameof(fileName)); - public void Commit() + if (!File.Exists(fileName)) + Database = new Database(true, false); + else { - if (_commit) + var doc = Acap.DocumentManager + .Cast() + .FirstOrDefault(doc => doc.Name == fileName); + if (doc is not null) { - Transaction.Commit(); + Database = doc.Database; + Document = doc; + Editor = doc.Editor; } else { - Abort(); + Database = new Database(false, true); + if (Path.GetExtension(fileName).ToLower().Contains("dxf")) + Database.DxfIn(fileName, null); + else + { +#if ac2008 + //此处没有一一对应的关系 +#pragma warning disable CS0436 // 类型与导入类型冲突 + var sf = openMode switch + { + FileOpenMode.OpenTryForReadShare => FileShare.Read, + FileOpenMode.OpenForReadAndAllShare => FileShare.ReadWrite, + FileOpenMode.OpenForReadAndWriteNoShare => FileShare.None, + FileOpenMode.OpenForReadAndReadShare => FileShare.ReadWrite, + _ => FileShare.ReadWrite, + }; +#pragma warning restore CS0436 // 类型与导入类型冲突 + Database.ReadDwgFile(fileName, sf, true/*控制读入一个与系统编码不相同的文件时的转换操作*/, password); +#else + Database.ReadDwgFile(fileName, openMode, true/*控制读入一个与系统编码不相同的文件时的转换操作*/, password); +#endif + } + Database.CloseInput(true); } - } - protected virtual void Dispose(bool disposing) + Transaction = Database.TransactionManager.StartTransaction(); + _commit = commit; + dBTrans.Push(this); + } + #endregion + + #region 类型转换 + /// + /// 隐式转换为Transaction + /// + /// 事务管理器 + /// 事务管理器 + public static implicit operator Transaction(DBTrans tr) + { + return tr.Transaction; + } + #endregion + + #region 符号表 + + /// + /// 块表 + /// + public SymbolTable BlockTable => new(this, Database.BlockTableId); + /// + /// 当前绘图空间 + /// + public BlockTableRecord CurrentSpace => BlockTable.GetRecord(Database.CurrentSpaceId)!; + /// + /// 模型空间 + /// + public BlockTableRecord ModelSpace => BlockTable.GetRecord(BlockTable.CurrentSymbolTable[BlockTableRecord.ModelSpace])!; + /// + /// 图纸空间 + /// + public BlockTableRecord PaperSpace => BlockTable.GetRecord(BlockTable.CurrentSymbolTable[BlockTableRecord.PaperSpace])!; + /// + /// 层表 + /// + public SymbolTable LayerTable => new(this, Database.LayerTableId); + /// + /// 文字样式表 + /// + public SymbolTable TextStyleTable => new(this, Database.TextStyleTableId); + + /// + /// 注册应用程序表 + /// + public SymbolTable RegAppTable => new(this, Database.RegAppTableId); + + /// + /// 标注样式表 + /// + public SymbolTable DimStyleTable => new(this, Database.DimStyleTableId); + + /// + /// 线型表 + /// + public SymbolTable LinetypeTable => new(this, Database.LinetypeTableId); + + /// + /// 用户坐标系表 + /// + public SymbolTable UcsTable => new(this, Database.UcsTableId); + + /// + /// 视图表 + /// + public SymbolTable ViewTable => new(this, Database.ViewTableId); + + /// + /// 视口表 + /// + public SymbolTable ViewportTable => new(this, Database.ViewportTableId); + #endregion + + #region 字典 + /// + /// 命名对象字典 + /// + public DBDictionary NamedObjectsDict => GetObject(Database.NamedObjectsDictionaryId)!; + /// + /// 组字典 + /// + public DBDictionary GroupDict => GetObject(Database.GroupDictionaryId)!; + /// + /// 多重引线样式字典 + /// + public DBDictionary MLeaderStyleDict => GetObject(Database.MLeaderStyleDictionaryId)!; + /// + /// 多线样式字典 + /// + public DBDictionary MLStyleDict => GetObject(Database.MLStyleDictionaryId)!; + /// + /// 材质字典 + /// + public DBDictionary MaterialDict => GetObject(Database.MaterialDictionaryId)!; + /// + /// 表格样式字典 + /// + public DBDictionary TableStyleDict => GetObject(Database.TableStyleDictionaryId)!; + /// + /// 视觉样式字典 + /// + public DBDictionary VisualStyleDict => GetObject(Database.VisualStyleDictionaryId)!; + /// + /// 颜色字典 + /// + public DBDictionary ColorDict => GetObject(Database.ColorDictionaryId)!; + /// + /// 打印设置字典 + /// + public DBDictionary PlotSettingsDict => GetObject(Database.PlotSettingsDictionaryId)!; + /// + /// 打印样式表名字典 + /// + public DBDictionary PlotStyleNameDict => GetObject(Database.PlotStyleNameDictionaryId)!; + /// + /// 布局字典 + /// + public DBDictionary LayoutDict => GetObject(Database.LayoutDictionaryId)!; + /// + /// 数据链接字典 + /// + public DBDictionary DataLinkDict => GetObject(Database.DataLinkDictionaryId)!; +#if !ac2009 + /// + /// 详细视图样式字典 + /// + public DBDictionary DetailViewStyleDict => GetObject(Database.DetailViewStyleDictionaryId)!; + /// + /// 剖面视图样式字典 + /// + public DBDictionary SectionViewStyleDict => GetObject(Database.SectionViewStyleDictionaryId)!; +#endif + #endregion + + #region 获取对象 + /// + /// 根据对象id获取图元对象 + /// + /// 要获取的图元对象的类型 + /// 对象id + /// 打开模式,默认为只读 + /// 是否打开已删除对象,默认为不打开 + /// 是否打开锁定图层对象,默认为不打开 + /// 图元对象,类型不匹配时返回 + public T? GetObject(ObjectId id, + OpenMode mode = OpenMode.ForRead, + bool openErased = false, + bool forceOpenOnLockedLayer = false) where T : DBObject + { + return Transaction.GetObject(id, mode, openErased, forceOpenOnLockedLayer) as T; + } + + /// + /// 根据对象句柄字符串获取对象Id + /// + /// 句柄字符串 + /// 对象id + public ObjectId GetObjectId(string handleString) + { + var hanle = new Handle(Convert.ToInt64(handleString, 16)); + //return Database.GetObjectId(false, hanle, 0); + return Helper.TryGetObjectId(Database, hanle); + } + + + + #endregion + + #region 保存文件 + /// + /// 保存当前数据库的dwg文件,如果前台打开则按dwg默认版本保存,否则按version参数的版本保存 + /// + /// dwg版本,默认为2004 + public void SaveDwgFile(DwgVersion version = DwgVersion.AC1800) + { + bool flag = true; + foreach (Document doc in Application.DocumentManager) { - if (!disposedValue) + // 前台开图,使用命令保存 + if (doc.Database.Filename == this.Database.Filename) { - if (disposing) - { - // 释放托管状态(托管对象) - Commit(); - dBTrans.Pop(); - if (!Transaction.IsDisposed) - { - Transaction.Dispose(); - } - documentLock?.Dispose(); - } - - // 释放未托管的资源(未托管的对象)并替代终结器 - // 将大型字段设置为 null - disposedValue = true; + doc.SendStringToExecute("_qsave\n", false, true, true); //不需要切换文档 + flag = false; + break; } } - - // 仅当“Dispose(bool disposing)”拥有用于释放未托管资源的代码时才替代终结器 - ~DBTrans() + if (flag) { - // 不要更改此代码。请将清理代码放入“Dispose(bool disposing)”方法中 - Dispose(disposing: false); + // 后台开图,用数据库保存 + Database.SaveAs(Database.Filename, version); } + } + #endregion - public void Dispose() + #region idispose接口相关函数 + /// + /// 取消事务 + /// + public void Abort() + { + Dispose(false); + } + /// + /// 提交事务 + /// + public void Commit() + { + Dispose(true); + } + + protected virtual void Dispose(bool disposing) + { + /* 事务dispose流程: + * 1. 根据传入的参数确定是否提交,true为提交,false为不提交 + * 2. 根据disposedValue的值确定是否重复dispose,false为首次dispose + * 3. 如果锁文档就将文档锁dispose + * 4. 不管是否提交,既然进入dispose,就要将事务栈的当前事务弹出 + * 注意这里的事务栈不是cad的事务管理器,而是dbtrans的事务 + * 5. 清理非托管的字段 + */ + + if (disposedValue) + return; + + // 释放未托管的资源(未托管的对象)并替代终结器 + // 将大型字段设置为 null + disposedValue = true; + + if (disposing) + { + // 调用cad的事务进行提交,释放托管状态(托管对象) + Transaction.Commit(); + } + else { - // 不要更改此代码。请将清理代码放入“Dispose(bool disposing)”方法中 - Dispose(disposing: true); - GC.SuppressFinalize(this); + // 否则取消所有的修改 + Transaction.Abort(); } - #endregion + // 调用 cad事务的dispose进行销毁 + if (!Transaction.IsDisposed) + Transaction.Dispose(); + + // 调用文档锁dispose + documentLock?.Dispose(); + + // 将事务栈的当前dbtrans弹栈 + dBTrans.Pop(); + } + + // 仅当“Dispose(bool disposing)”拥有用于释放未托管资源的代码时才替代终结器 + ~DBTrans() + { + // 不要更改此代码。请将清理代码放入“Dispose(bool disposing)”方法中 + Dispose(disposing: false); + } + + public void Dispose() + { + // 不要更改此代码。请将清理代码放入“Dispose(bool disposing)”方法中 + Dispose(disposing: true); + GC.SuppressFinalize(this); } + #endregion } diff --git a/src/IFoxCAD.Cad/Runtime/Env.cs b/src/IFoxCAD.Cad/Runtime/Env.cs index 0d490e1..379504b 100644 --- a/src/IFoxCAD.Cad/Runtime/Env.cs +++ b/src/IFoxCAD.Cad/Runtime/Env.cs @@ -1,453 +1,490 @@ -using Autodesk.AutoCAD.ApplicationServices; -using Autodesk.AutoCAD.DatabaseServices; -using Autodesk.AutoCAD.EditorInput; -using Autodesk.AutoCAD.GraphicsSystem; -using System; - -namespace IFoxCAD.Cad +namespace IFoxCAD.Cad; + +/// +/// 系统管理类 +/// 封装了一些系统 osmode、cmdecho、dimblk 系统变量 +/// 封装了常用的 文档 编辑器 数据库等对象为静态变量 +/// 封装了配置页面的注册表信息获取函数 +/// +public static class Env { + #region Goal + /// - /// 系统管理类 - /// 封装了一些系统 osmode、cmdecho、dimblk 系统变量 - /// 封装了常用的 文档 编辑器 数据库等对象为静态变量 - /// 封装了配置页面的注册表信息获取函数 + /// 当前的数据库 /// - public static class Env + public static Database Database => HostApplicationServices.WorkingDatabase; + + /// + /// 当前文档 + /// + public static Document Document => Application.DocumentManager.MdiActiveDocument; + + /// + /// 编辑器对象 + /// + public static Editor Editor => Document.Editor; + + /// + /// 图形管理器 + /// + public static Manager GsManager => Document.GraphicsManager; + + #endregion Goal + + #region Preferences + + /// + /// 获取当前配置的数据 + /// + /// 小节名 + /// 数据名 + /// 对象 + public static object GetCurrentProfileProperty(string subSectionName, string propertyName) + { + UserConfigurationManager ucm = Application.UserConfigurationManager; + IConfigurationSection cpf = ucm.OpenCurrentProfile(); + IConfigurationSection ss = cpf.OpenSubsection(subSectionName); + return ss.ReadProperty(propertyName, ""); + } + + /// + /// 获取对话框配置的数据 + /// + /// 对话框对象 + /// 配置项 + public static IConfigurationSection GetDialogSection(object dialog) { - #region Goal + UserConfigurationManager ucm = Application.UserConfigurationManager; + IConfigurationSection ds = ucm.OpenDialogSection(dialog); + return ds; + } + /// + /// 获取公共配置的数据 + /// + /// 数据名 + /// 配置项 + public static IConfigurationSection GetGlobalSection(string propertyName) + { + UserConfigurationManager ucm = Application.UserConfigurationManager; + IConfigurationSection gs = ucm.OpenGlobalSection(); + IConfigurationSection ss = gs.OpenSubsection(propertyName); + return ss; + } + + #endregion Preferences + + #region Enum + + /// + /// 控制在AutoLISP的command函数运行时AutoCAD是否回显提示和输入, 为显示, 为不显示 + /// + public static bool CmdEcho + { + get => Convert.ToInt16(Application.GetSystemVariable("cmdecho")) == 1; + set => Application.SetSystemVariable("cmdecho", Convert.ToInt16(value)); + } + + /// + /// 控制在光标是否为正交模式, 为打开正交, 为关闭正交 + /// + public static bool OrthoMode + { + get => Convert.ToInt16(Application.GetSystemVariable("ORTHOMODE")) == 1; + set => Application.SetSystemVariable("ORTHOMODE", Convert.ToInt16(value)); + } + + #region Dimblk + + /// + /// 标注箭头类型 + /// + public enum DimblkType + { /// - /// 当前的数据库 + /// 实心闭合 /// - public static Database CurrentDatabase - { - get - { - return HostApplicationServices.WorkingDatabase; - } - } + Defult, /// - /// 当前文档 + /// 点 /// - public static Document ActiveDocument - { - get - { - return Application.DocumentManager.MdiActiveDocument; - } - } + Dot, /// - /// 编辑器对象 + /// 小点 /// - public static Editor Editor - { - get - { - return ActiveDocument.Editor; - } - } + DotSmall, /// - /// 图形管理器 + /// 空心点 /// - public static Manager GsManager - { - get - { - return ActiveDocument.GraphicsManager; - } - } + DotBlank, - #endregion Goal + /// + /// 原点标记 + /// + Origin, - #region Preferences + /// + /// 原点标记2 + /// + Origin2, /// - /// 获取当前配置的数据 + /// 打开 /// - /// 小节名 - /// 数据名 - /// 对象 - public static object GetCurrentProfileProperty(string subSectionName, string propertyName) - { - UserConfigurationManager ucm = Application.UserConfigurationManager; - IConfigurationSection cpf = ucm.OpenCurrentProfile(); - IConfigurationSection ss = cpf.OpenSubsection(subSectionName); - return ss.ReadProperty(propertyName, ""); - } + Open, /// - /// 获取对话框配置的数据 + /// 直角 /// - /// 对话框对象 - /// 配置项 - public static IConfigurationSection GetDialogSection(object dialog) - { - UserConfigurationManager ucm = Application.UserConfigurationManager; - IConfigurationSection ds = ucm.OpenDialogSection(dialog); - return ds; - } + Open90, /// - /// 获取公共配置的数据 + /// 30度角 /// - /// 数据名 - /// 配置项 - public static IConfigurationSection GetGlobalSection(string propertyName) - { - UserConfigurationManager ucm = Application.UserConfigurationManager; - IConfigurationSection gs = ucm.OpenGlobalSection(); - IConfigurationSection ss = gs.OpenSubsection(propertyName); - return ss; - } + Open30, - #endregion Preferences + /// + /// 闭合 + /// + Closed, - #region Enum + /// + /// 空心小点 + /// + Small, /// - /// 控制在AutoLISP的command函数运行时AutoCAD是否回显提示和输入, 为显示, 为不显示 + /// 无 /// - public static bool CmdEcho - { - get => Convert.ToInt16(Application.GetSystemVariable("cmdecho")) == 1; - set => Application.SetSystemVariable("cmdecho", Convert.ToInt16(value)); - } + None, - #region Dimblk + /// + /// 倾斜 + /// + Oblique, /// - /// 标注箭头类型 + /// 实心框 /// - public enum DimblkType - { - /// - /// 实心闭合 - /// - Defult, - - /// - /// 点 - /// - Dot, - - /// - /// 小点 - /// - DotSmall, - - /// - /// 空心点 - /// - DotBlank, - - /// - /// 原点标记 - /// - Origin, - - /// - /// 原点标记2 - /// - Origin2, - - /// - /// 打开 - /// - Open, - - /// - /// 直角 - /// - Open90, - - /// - /// 30度角 - /// - Open30, - - /// - /// 闭合 - /// - Closed, - - /// - /// 空心小点 - /// - Small, - - /// - /// 无 - /// - None, - - /// - /// 倾斜 - /// - Oblique, - - /// - /// 实心框 - /// - BoxFilled, - - /// - /// 方框 - /// - BoxBlank, - - /// - /// 空心闭合 - /// - ClosedBlank, - - /// - /// 实心基准三角形 - /// - DatumFilled, - - /// - /// 基准三角形 - /// - DatumBlank, - - /// - /// 完整标记 - /// - Integral, - - /// - /// 建筑标记 - /// - ArchTick, - } + BoxFilled, /// - /// 标注箭头属性 + /// 方框 /// - public static DimblkType Dimblk - { - get - { - string s = (string)Application.GetSystemVariable("dimblk"); - if (string.IsNullOrEmpty(s)) - { - return DimblkType.Defult; - } - else - { - return s.ToEnum(); - } - } - set - { - string s = GetDimblkName(value); - Application.SetSystemVariable("dimblk", s); - } - } + BoxBlank, /// - /// 获取标注箭头名 + /// 空心闭合 /// - /// 标注箭头类型 - /// 箭头名 - public static string GetDimblkName(DimblkType dimblk) - { - return - dimblk == DimblkType.Defult - ? - "." - : - "_" + dimblk.GetName(); - } + ClosedBlank, /// - /// 获取标注箭头ID + /// 实心基准三角形 /// - /// 标注箭头类型 - /// 箭头ID - public static ObjectId GetDimblkId(DimblkType dimblk) - { - DimblkType oldDimblk = Dimblk; - Dimblk = dimblk; - ObjectId id = HostApplicationServices.WorkingDatabase.Dimblk; - Dimblk = oldDimblk; - return id; - } + DatumFilled, - #endregion Dimblk + /// + /// 基准三角形 + /// + DatumBlank, - #region OsMode + /// + /// 完整标记 + /// + Integral, /// - /// 捕捉模式系统变量类型 + /// 建筑标记 /// - public enum OSModeType + ArchTick + } + + private static readonly Dictionary dimdescdict = new() + { + { "实心闭合", DimblkType.Defult }, + { "点", DimblkType.Dot }, + { "小点", DimblkType.DotSmall }, + { "空心点", DimblkType.DotBlank }, + { "原点标记", DimblkType.Origin }, + { "原点标记 2", DimblkType.Origin2 }, + { "打开", DimblkType.Open }, + { "直角", DimblkType.Open90 }, + { "30 度角", DimblkType.Open30 }, + { "闭合", DimblkType.Closed }, + { "空心小点", DimblkType.Small }, + { "无", DimblkType.None }, + { "倾斜", DimblkType.Oblique }, + { "实心框", DimblkType.BoxFilled }, + { "方框", DimblkType.BoxBlank }, + { "空心闭合", DimblkType.ClosedBlank }, + { "实心基准三角形", DimblkType.DatumFilled }, + { "基准三角形", DimblkType.DatumBlank }, + { "完整标记", DimblkType.Integral }, + { "建筑标记", DimblkType.ArchTick }, + + { "", DimblkType.Defult }, + { "_DOT", DimblkType.Dot }, + { "_DOTSMALL", DimblkType.DotSmall }, + { "_DOTBLANK", DimblkType.DotBlank }, + { "_ORIGIN", DimblkType.Origin }, + { "_ORIGIN2", DimblkType.Origin2 }, + { "_OPEN", DimblkType.Open }, + { "_OPEN90", DimblkType.Open90 }, + { "_OPEN30", DimblkType.Open30 }, + { "_CLOSED", DimblkType.Closed }, + { "_SMALL", DimblkType.Small }, + { "_NONE", DimblkType.None }, + { "_OBLIQUE", DimblkType.Oblique }, + { "_BOXFILLED", DimblkType.BoxFilled }, + { "_BOXBLANK", DimblkType.BoxBlank }, + { "_CLOSEDBLANK", DimblkType.ClosedBlank }, + { "_DATUMFILLED", DimblkType.DatumFilled }, + { "_DATUMBLANK", DimblkType.DatumBlank }, + { "_INTEGRAL", DimblkType.Integral }, + { "_ARCHTICK", DimblkType.ArchTick }, + }; + + + + /// + /// 标注箭头属性 + /// + public static DimblkType Dimblk + { + get + { + string s = ((string)Application.GetSystemVariable("dimblk")).ToUpper(); + //if (string.IsNullOrEmpty(s)) + //{ + // return DimblkType.Defult; + //} + //else + //{ + // if (dimdescdict.TryGetValue(s, out DimblkType value)) + // { + // return value; + // } + // return s.ToEnum(); + // //return s.FromDescName(); + //} + return dimdescdict[s]; + } + set { - /// - /// 无 - /// - None = 0, - - /// - /// 端点 - /// - End = 1, - - /// - /// 中点 - /// - Middle = 2, - - /// - /// 圆心 - /// - Center = 4, - - /// - /// 节点 - /// - Node = 8, - - /// - /// 象限点 - /// - Quadrant = 16, - - /// - /// 交点 - /// - Intersection = 32, - - /// - /// 插入点 - /// - Insert = 64, - - /// - /// 垂足 - /// - Pedal = 128, - - /// - /// 切点 - /// - Tangent = 256, - - /// - /// 最近点 - /// - Nearest = 512, - - /// - /// 几何中心 - /// - Quick = 1024, - - /// - /// 外观交点 - /// - Appearance = 2048, - - /// - /// 延伸 - /// - Extension = 4096, - - /// - /// 平行 - /// - Parallel = 8192 + string s = GetDimblkName(value); + Application.SetSystemVariable("dimblk", s); } + } + + /// + /// 获取标注箭头名 + /// + /// 标注箭头类型 + /// 箭头名 + public static string GetDimblkName(DimblkType dimblk) + { + return + dimblk == DimblkType.Defult + ? + "." + : + "_" + dimblk.GetName(); + } + /// + /// 获取标注箭头ID + /// + /// 标注箭头类型 + /// 箭头ID + public static ObjectId GetDimblkId(DimblkType dimblk) + { + DimblkType oldDimblk = Dimblk; + Dimblk = dimblk; + ObjectId id = HostApplicationServices.WorkingDatabase.Dimblk; + Dimblk = oldDimblk; + return id; + } + + #endregion Dimblk + + #region OsMode + + /// + /// 捕捉模式系统变量类型 + /// + public enum OSModeType + { /// - /// 捕捉模式系统变量 + /// 无 /// - public static OSModeType OSMode - { - get - { - return (OSModeType)Convert.ToInt16(Application.GetSystemVariable("osmode")); - } - set - { - Application.SetSystemVariable("osmode", (int)value); - } - } + None = 0, + /// - /// 捕捉模式osm1是否包含osm2 + /// 端点 /// - /// 原模式 - /// 要比较的模式 - /// 包含时返回 ,不包含时返回 - public static bool Include(this OSModeType osm1, OSModeType osm2) - { - return (osm1 & osm2) == osm2; - } - #endregion OsMode + End = 1, - private static T ToEnum(this string value) - { - return (T)Enum.Parse(typeof(T), value, true); - } + /// + /// 中点 + /// + Middle = 2, - private static string GetName(this T value) - { - return Enum.GetName(typeof(T), value); - } + /// + /// 圆心 + /// + Center = 4, - #endregion Enum + /// + /// 节点 + /// + Node = 8, - #region 环境变量 /// - /// 获取cad变量 + /// 象限点 /// - /// 变量名 - /// 变量值 - public static object GetVar(string varName) - { - return Application.GetSystemVariable(varName); - } + Quadrant = 16, + /// - /// 设置cad变量 + /// 交点 /// - /// 变量名 - /// 变量值 - public static void SetVar(string varName, object value) - { - Application.SetSystemVariable(varName, value); - } -#nullable enable + Intersection = 32, + /// - /// 获取系统环境变量 + /// 插入点 /// - /// 变量名 - /// 指定的环境变量的值;或者如果找不到环境变量,则返回 null - public static string? GetEnv(string var) - { - //从当前进程或者从当前用户或本地计算机的 Windows 操作系统注册表项检索环境变量的值 - return Environment.GetEnvironmentVariable(var); - } + Insert = 64, + /// - /// 设置系统环境变量 + /// 垂足 /// - /// 变量名 - /// 变量值 - public static void SetEnv(string var, string? value) - { - //创建、修改或删除当前进程中或者为当前用户或本地计算机保留的 Windows 操作系统注册表项中存储的环境变量 - Environment.SetEnvironmentVariable(var, value); - } -#nullable disable - #endregion + Pedal = 128, + /// + /// 切点 + /// + Tangent = 256, + + /// + /// 最近点 + /// + Nearest = 512, + + /// + /// 几何中心 + /// + Quick = 1024, + + /// + /// 外观交点 + /// + Appearance = 2048, /// - /// 命令行打印,会自动调用对象的toString函数 + /// 延伸 /// - /// 要打印的对象 - public static void Print(object message) => Editor.WriteMessage($"{message}\n"); + Extension = 4096, + + /// + /// 平行 + /// + Parallel = 8192 + } + + /// + /// 捕捉模式系统变量 + /// + public static OSModeType OSMode + { + get + { + return (OSModeType)Convert.ToInt16(Application.GetSystemVariable("osmode")); + } + set + { + Application.SetSystemVariable("osmode", (int)value); + } + } + /// + /// 捕捉模式osm1是否包含osm2 + /// + /// 原模式 + /// 要比较的模式 + /// 包含时返回 ,不包含时返回 + public static bool Include(this OSModeType osm1, OSModeType osm2) + { + return (osm1 & osm2) == osm2; + } + #endregion OsMode + + + private static string GetName(this T value) + { + return Enum.GetName(typeof(T), value); + } + + #endregion Enum + + #region 环境变量 + /// + /// 获取cad变量 + /// + /// 变量名 + /// 变量值 + public static object GetVar(string varName) + { + return Application.GetSystemVariable(varName); + } + /// + /// 设置cad变量 + /// + /// 变量名 + /// 变量值 + public static void SetVar(string varName, object value) + { + try + { + Application.SetSystemVariable(varName, value); + } + catch (System.Exception) + { + Env.Print($"{varName} 是不存在的变量!"); + } + } + /// + /// 获取系统环境变量 + /// + /// 变量名 + /// 指定的环境变量的值;或者如果找不到环境变量,则返回 null + public static string? GetEnv(string var) + { + //从当前进程或者从当前用户或本地计算机的 Windows 操作系统注册表项检索环境变量的值 + return Environment.GetEnvironmentVariable(var); + } + /// + /// 设置系统环境变量 + /// + /// 变量名 + /// 变量值 + public static void SetEnv(string var, string? value) + { + //创建、修改或删除当前进程中或者为当前用户或本地计算机保留的 Windows 操作系统注册表项中存储的环境变量 + Environment.SetEnvironmentVariable(var, value); } + #endregion + + + /// + /// 命令行打印,会自动调用对象的toString函数 + /// + /// 要打印的对象 + public static void Print(object message) => Editor.WriteMessage($"{message}\n"); + /// + /// 判断当前是否在UCS坐标下 + /// + /// Bool + public static bool IsUcs() => (short)GetVar("WORLDUCS") == 0; } diff --git a/src/IFoxCAD.Cad/Runtime/FileOpenMode.cs b/src/IFoxCAD.Cad/Runtime/FileOpenMode.cs new file mode 100644 index 0000000..5108dd5 --- /dev/null +++ b/src/IFoxCAD.Cad/Runtime/FileOpenMode.cs @@ -0,0 +1,13 @@ +#if ac2008 //NET35 +namespace Autodesk.AutoCAD.DatabaseServices +{ + [Wrapper("AcDbDatabase::OpenMode")] + public enum FileOpenMode + { + OpenTryForReadShare = 4, + OpenForReadAndAllShare = 3, + OpenForReadAndWriteNoShare = 2, + OpenForReadAndReadShare = 1 + } +} +#endif \ No newline at end of file diff --git a/src/IFoxCAD.Cad/Runtime/IAutoGo.cs b/src/IFoxCAD.Cad/Runtime/IAutoGo.cs new file mode 100644 index 0000000..27ce550 --- /dev/null +++ b/src/IFoxCAD.Cad/Runtime/IAutoGo.cs @@ -0,0 +1,307 @@ +namespace IFoxCAD.Cad; + +using System.Diagnostics; + +/// +/// 加载时优先级 +/// +[Flags] +public enum Sequence : byte +{ + First,// 最先 + Last, // 最后 +} + +/// +/// 加载时自动执行接口 +/// +public interface IFoxAutoGo +{ + // 控制加载顺序 + Sequence SequenceId(); + // 关闭cad的时候会自动执行 + void Terminate(); + // 打开cad的时候会自动执行 + void Initialize(); +} + +/// +/// 加载时自动执行特性 +/// +[AttributeUsage(AttributeTargets.Method)] +public class IFoxInitialize : Attribute +{ + /// + /// 优先级 + /// + internal Sequence SequenceId; + /// + /// 用于初始化;用于结束回收 + /// + internal bool IsInitialize; + /// + /// 用于初始化和结束回收 + /// + /// 优先级 + /// 用于初始化;用于结束回收 + public IFoxInitialize(Sequence sequence = Sequence.Last, bool isInitialize = true) + { + SequenceId = sequence; + IsInitialize = isInitialize; + } +} + +//为了解决IExtensionApplication在一个dll内无法多次实现接口的关系 +//所以在这里反射加载所有的 IAutoGo ,以达到能分开写"启动运行"函数的目的 +class RunClass +{ + public Sequence Sequence { get; } + readonly MethodInfo _methodInfo; + + public RunClass(MethodInfo method, Sequence sequence) + { + _methodInfo = method; + Sequence = sequence; + } + + /// + /// 运行方法 + /// + public void Run() + { + _methodInfo.Invoke(); + } +} + +/// +/// 此类作为加载后cad自动运行接口的一部分,用于反射特性和接口 +/// 启动cad后的执行顺序为: +/// 1:特性..(多个) +/// 2:接口..(多个) +/// +public class AutoReflection +{ + static List _InitializeList = new(); //储存方法用于初始化 + static List _TerminateList = new(); //储存方法用于结束释放 + + readonly string _dllName; + readonly AutoRegConfig _autoRegConfig; + + /// + /// 反射执行 + /// 1.特性: + /// 2.接口: + /// + /// 约束在此dll进行加速 + public AutoReflection(string dllName, AutoRegConfig configInfo) + { + _dllName = dllName; + _autoRegConfig = configInfo; + } + + //启动cad的时候会自动执行 + public void Initialize() + { + try + { + //收集特性,包括启动时和关闭时 + if ((_autoRegConfig & AutoRegConfig.ReflectionAttribute) == AutoRegConfig.ReflectionAttribute) + GetAttributeFunctions(_InitializeList, _TerminateList); + + if ((_autoRegConfig & AutoRegConfig.ReflectionInterface) == AutoRegConfig.ReflectionInterface) + GetInterfaceFunctions(_InitializeList, nameof(Initialize)); + + if (_InitializeList.Count > 0) + { + //按照 SequenceId 排序_升序 + _InitializeList = _InitializeList.OrderBy(runClass => runClass.Sequence).ToList(); + RunFunctions(_InitializeList); + } + } + catch (System.Exception) + { + Debugger.Break(); + } + } + + //关闭cad的时候会自动执行 + public void Terminate() + { + try + { + if ((_autoRegConfig & AutoRegConfig.ReflectionInterface) == AutoRegConfig.ReflectionInterface) + GetInterfaceFunctions(_TerminateList, nameof(Terminate)); + + if (_TerminateList.Count > 0) + { + //按照 SequenceId 排序_降序 + _TerminateList = _TerminateList.OrderByDescending(runClass => runClass.Sequence).ToList(); + RunFunctions(_TerminateList); + } + } + catch (System.Exception) + { + Debugger.Break(); + } + } + + /// + /// 遍历程序域下所有类型 + /// + /// 输出每个成员执行 + /// 过滤此dll,不含后缀 + public static void AppDomainGetTypes(Action action, string? dllNameWithoutExtension = null) + { +#if DEBUG + int error = 0; +#endif + try + { + var assemblies = AppDomain.CurrentDomain.GetAssemblies(); +#if !NET35 + //cad2021出现如下报错 + //System.NotSupportedException:动态程序集中不支持已调用的成员 + //assemblies = assemblies.Where(p => !p.IsDynamic).ToArray();//这个要容器类型转换 + assemblies = Array.FindAll(assemblies, p => !p.IsDynamic); +#endif + //主程序域 + for (int ii = 0; ii < assemblies.Length; ii++) + { + var assembly = assemblies[ii]; + + //获取类型集合,反射时候还依赖其他的dll就会这个错误 + //此通讯库要跳过,否则会报错. + var location = Path.GetFileNameWithoutExtension(assembly.Location); + if (dllNameWithoutExtension != null && location != dllNameWithoutExtension) + continue; + if (location == "AcInfoCenterConn")//通讯库 + continue; + + Type[]? types = null; + try + { + types = assembly.GetTypes(); + } + catch (ReflectionTypeLoadException) { continue; } + + if (types is null) + continue; + + for (int jj = 0; jj < types.Length; jj++) + { + var type = types[jj]; + if (type is not null) + { +#if DEBUG + ++error; +#endif + action(type); + } + } + } + } + catch (System.Exception e) + { +#if DEBUG + Debug.WriteLine($"出错:{nameof(AppDomainGetTypes)};计数{error};错误信息:{e.Message}"); + Debugger.Break(); +#endif + } + } + + /// + /// 收集接口下的函数 + /// + /// 储存要运行的方法 + /// 查找方法名 + /// + void GetInterfaceFunctions(List runClassList, string methodName) + { + const string sqid = nameof(Sequence) + "Id"; + + AppDomainGetTypes(type => { + if (type.IsAbstract) + return; + + var ints = type.GetInterfaces(); + for (int sss = 0; sss < ints.Length; sss++) + { + var inters = ints[sss]; + if (inters.Name != nameof(IFoxAutoGo)) + continue; + + Sequence? sequence = null; + MethodInfo? initialize = null; + + var mets = type.GetMethods(); + for (int jj = 0; jj < mets.Length; jj++) + { + var method = mets[jj]; + if (method.IsAbstract) + continue; + + if (method.Name == sqid) + { + var obj = method.Invoke(); + if (obj is not null) + sequence = (Sequence)obj; + continue; + } + else if (method.Name == methodName) + initialize = method; + + if (initialize is not null && sequence is not null) + break; + } + + if (initialize is null) + continue; + + var seq = sequence is null ? Sequence.Last : sequence.Value; + runClassList.Add(new RunClass(initialize, seq)); + break; + } + }, _dllName); + + } + + /// + /// 收集特性下的函数 + /// + void GetAttributeFunctions(List initialize, List terminate) + { + AppDomainGetTypes(type => { + if (type.IsAbstract) + return; + + var mets = type.GetMethods(); + for (int ii = 0; ii < mets.Length; ii++) + { + var method = mets[ii]; + var attr = method.GetCustomAttributes(true); + for (int jj = 0; jj < attr.Length; jj++) + { + if (attr[jj] is IFoxInitialize jjAtt) + { + var runc = new RunClass(method, jjAtt.SequenceId); + if (jjAtt.IsInitialize) + initialize.Add(runc); + else + terminate.Add(runc); + break; + } + } + } + }, _dllName); + } + + /// + /// 执行收集到的函数 + /// + static void RunFunctions(List runClassList) + { + for (int i = 0; i < runClassList.Count; i++) + runClassList[i].Run(); + runClassList.Clear(); + } +} \ No newline at end of file diff --git a/src/IFoxCAD.Cad/Runtime/Log.cs b/src/IFoxCAD.Cad/Runtime/Log.cs new file mode 100644 index 0000000..02293e2 --- /dev/null +++ b/src/IFoxCAD.Cad/Runtime/Log.cs @@ -0,0 +1,412 @@ +namespace IFoxCAD.Cad; + +using System; +using System.Diagnostics; +using System.Threading; + +#region 写入日志到不同的环境中 +//https://zhuanlan.zhihu.com/p/338492989 +public abstract class LogBase +{ + public abstract void ReadLog(string message); + public abstract void WriteLog(string message); + public abstract void DeleteLog(string message); +} + +/// +/// 日志输出环境 +/// +public enum LogTarget +{ + /// + /// 文件 + /// + File = 1, + /// + /// 数据库 + /// + Database = 2, + /// + /// windows日志 + /// + EventLog = 4, +} + +/// +/// 写入到文件中 +/// +public class FileLogger : LogBase +{ + public override void DeleteLog(string message) + { + throw new NotImplementedException(); + } + + public override void ReadLog(string message) + { + throw new NotImplementedException(); + } + + public override void WriteLog(string? message) + { + //把异常信息输出到文件 + var sw = new StreamWriter(LogHelper.LogAddress, true/*当天日志文件存在就追加,否则就创建*/); + sw.Write(message); + sw.Flush(); + sw.Close(); + sw.Dispose(); + } +} + +/// +/// 写入到数据库(暂时不支持) +/// +public class DBLogger : LogBase +{ + public override void DeleteLog(string message) + { + throw new NotImplementedException(); + } + + public override void ReadLog(string message) + { + throw new NotImplementedException(); + } + + public override void WriteLog(string? message) + { + throw new NotImplementedException(); + } +} + +/// +/// 写入到win日志 +/// +public class EventLogger : LogBase +{ + // https://docs.microsoft.com/en-us/answers/questions/526018/windows-event-log-with-net-5.html + // net50要加 + // 需要win权限 + + public string LogName = "IFoxCadLog"; + public override void DeleteLog(string message) + { +#if !NET5_0 && !NET6_0 + if (EventLog.Exists(LogName)) + EventLog.Delete(LogName); +#endif + } + + public override void ReadLog(string message) + { +#if !NET5_0 && !NET6_0 + EventLog eventLog = new(); + eventLog.Log = LogName; + foreach (EventLogEntry entry in eventLog.Entries) + { + //Write your custom code here + } +#endif + } + + public override void WriteLog(string? message) + { +#if !NET5_0 && !NET6_0 + try + { + EventLog eventLog = new() + { + Source = LogName + }; + eventLog.WriteEntry(message, EventLogEntryType.Information); + } + catch (System.Security.SecurityException e) + { + throw new Exception("您没有权限写入win日志中" + e.Message); + } +#endif + } +} + +#endregion + +#region 静态方法 +public static class LogHelper +{ +#pragma warning disable CA2211 // 非常量字段应当不可见 + /// + /// 日志文件完整路径 + /// + public static string? LogAddress; + /// + /// 输出错误信息到日志文件的开关 + /// + public static bool FlagOutFile = false; + /// + /// 输出错误信息到vs输出窗口的开关 + /// + public static bool FlagOutVsOutput = true; + +#pragma warning restore CA2211 // 非常量字段应当不可见 + + /// + /// 读写锁 + /// 当资源处于写入模式时,其他线程写入需要等待本次写入结束之后才能继续写入 + /// + static readonly ReaderWriterLockSlim _logWriteLock = new(); + + /// + /// 提供给外部设置log文件保存路径 + /// + /// 空的话就为运行的dll旁边的一个文件夹上 + public static void OptionFile(string? newlogAddress = null) + { + _logWriteLock.EnterWriteLock();// 写模式锁定 读写锁 + try + { + LogAddress = newlogAddress; + if (string.IsNullOrEmpty(LogAddress)) + { + //微软回复:静态构造函数只会被调用一次, + //并且在它执行完成之前,任何其它线程都不能创建这个类的实例或使用这个类的静态成员 + //https://blog.csdn.net/weixin_34204722/article/details/90095812 + var sb = new StringBuilder(); + sb.Append(Environment.CurrentDirectory); + sb.Append("\\ErrorLog"); + + //新建文件夹 + var path = sb.ToString(); + if (!Directory.Exists(path)) + { + Directory.CreateDirectory(path) + .Attributes = FileAttributes.Normal; //设置文件夹属性为普通 + } + + sb.Append('\\'); + sb.Append(DateTime.Now.ToString("yy-MM-dd")); + sb.Append(".log"); + LogAddress = sb.ToString(); + } + } + finally + { + _logWriteLock.ExitWriteLock();// 解锁 读写锁 + } + } + + public static string WriteLog(this string? message, + LogTarget target = LogTarget.File) + { + if (message == null) + return string.Empty; + return LogAction(null, message, target); + } + + public static string WriteLog(this Exception? exception, + LogTarget target = LogTarget.File) + { + if (exception == null) + return string.Empty; + return LogAction(exception, null, target); + } + + public static string WriteLog(this Exception? exception, string? message, + LogTarget target = LogTarget.File) + { + if (exception == null) + return string.Empty; + return LogAction(exception, message, target); + } + + + + static string LogAction(Exception? ex, string? message, LogTarget target) + { + if (ex == null && message == null) + return string.Empty; + + if (target == LogTarget.File && LogAddress == null) + OptionFile(); + + try + { + _logWriteLock.EnterWriteLock();// 写模式锁定 读写锁 + + var logtxt = new LogTxt(ex, message); + //var logtxtJson = Newtonsoft.Json.JsonConvert.SerializeObject(logtxt, Formatting.Indented); + var logtxtJson = logtxt?.ToString(); + if (logtxtJson == null) + return string.Empty; + + if (FlagOutFile) + { + LogBase? logger; + switch (target) + { + case LogTarget.File: + logger = new FileLogger(); + logger.WriteLog(logtxtJson); + break; + case LogTarget.Database: + logger = new DBLogger(); + logger.WriteLog(logtxtJson); + break; + case LogTarget.EventLog: + logger = new EventLogger(); + logger.WriteLog(logtxtJson); + break; + } + } + + if (FlagOutVsOutput) + { + Debug.WriteLine("错误日志: " + LogAddress); + Debug.Write(logtxtJson); + } + return logtxtJson; + } + finally + { + _logWriteLock.ExitWriteLock();// 解锁 读写锁 + } + } +} +#endregion + +#region 序列化 +[Serializable] +public class LogTxt +{ + public string 当前时间; + public string? 备注信息; + public string? 异常信息; + public string? 异常对象; + public string? 触发方法; + public string? 调用堆栈; + + LogTxt() + { + // 以不同语言显示日期 + // DateTime.Now.ToString("f", new System.Globalization.CultureInfo("es-ES")) + // DateTime.Now.ToString("f", new System.Globalization.CultureInfo("zh-cn")) + // 为了最小信息熵,所以用这样的格式,并且我喜欢补0 + 当前时间 = DateTime.Now.ToString("yy-MM-dd hh:mm:ss"); + } + + public LogTxt(Exception? ex, string? message) : this() + { + if (ex == null && message == null) + throw new ArgumentNullException(nameof(ex)); + + if (ex != null) + { + 异常信息 = ex.Message; + 异常对象 = ex.Source; + 触发方法 = ex.TargetSite == null ? string.Empty : ex.TargetSite.ToString(); + 调用堆栈 = ex.StackTrace == null ? string.Empty : ex.StackTrace.Trim(); + } + if (message != null) + 备注信息 = message; + } + + /// 为了不引入json的dll,所以这里自己构造 + public override string? ToString() + { + var sb = new StringBuilder(); + sb.Append('{'); + sb.Append(Environment.NewLine); + sb.AppendLine($" \"{nameof(当前时间)}\": \"{当前时间}\""); + sb.AppendLine($" \"{nameof(备注信息)}\": \"{备注信息}\""); + sb.AppendLine($" \"{nameof(异常信息)}\": \"{异常信息}\""); + sb.AppendLine($" \"{nameof(异常对象)}\": \"{异常对象}\""); + sb.AppendLine($" \"{nameof(触发方法)}\": \"{触发方法}\""); + sb.AppendLine($" \"{nameof(调用堆栈)}\": \"{调用堆栈}\""); + sb.Append('}'); + return sb.ToString(); + } +} +#endregion + + +#if false //最简单的实现 +public static class Log +{ + /// + /// 读写锁 + /// 当资源处于写入模式时,其他线程写入需要等待本次写入结束之后才能继续写入 + /// + static readonly ReaderWriterLockSlim _logWriteLock = new(); + + /// + /// 日志文件完整路径 + /// + static readonly string _logAddress; + + static Log() + { + //微软回复:静态构造函数只会被调用一次, + //并且在它执行完成之前,任何其它线程都不能创建这个类的实例或使用这个类的静态成员 + //https://blog.csdn.net/weixin_34204722/article/details/90095812 + var sb = new StringBuilder(); + sb.Append(Environment.CurrentDirectory); + sb.Append("\\ErrorLog"); + + //新建文件夹 + var path = sb.ToString(); + if (!Directory.Exists(path)) + { + Directory.CreateDirectory(path) + .Attributes = FileAttributes.Normal; //设置文件夹属性为普通 + } + + sb.Append('\\'); + sb.Append(DateTime.Now.ToString("yy-MM-dd")); + sb.Append(".log"); + _logAddress = sb.ToString(); + } + + + /// + /// 将异常打印到日志文件 + /// + /// 异常 + /// 备注 + /// DEBUG模式打印到vs输出窗口 + public static string? WriteLog(this Exception? ex, + string? remarks = null, + bool printDebugWindow = true) + { + try + { + _logWriteLock.EnterWriteLock();// 写模式锁定 读写锁 + + var logtxt = new LogTxt(ex, remarks); + //var logtxtJson = Newtonsoft.Json.JsonConvert.SerializeObject(logtxt, Formatting.Indented); + var logtxtJson = logtxt.ToString(); + + if (logtxtJson == null) + return string.Empty; + + //把异常信息输出到文件 + var sw = new StreamWriter(_logAddress, true/*当天日志文件存在就追加,否则就创建*/); + sw.Write(logtxtJson); + sw.Flush(); + sw.Close(); + sw.Dispose(); + + if (printDebugWindow) + { + Debug.WriteLine("错误日志: " + _logAddress); + Debug.Write(logtxtJson); + //Debugger.Break(); + //Debug.Assert(false, "终止进程"); + } + return logtxtJson; + } + finally + { + _logWriteLock.ExitWriteLock();// 解锁 读写锁 + } + } +} +#endif \ No newline at end of file diff --git a/src/IFoxCAD.Cad/Runtime/MethodInfoHelper.cs b/src/IFoxCAD.Cad/Runtime/MethodInfoHelper.cs new file mode 100644 index 0000000..c64a5f8 --- /dev/null +++ b/src/IFoxCAD.Cad/Runtime/MethodInfoHelper.cs @@ -0,0 +1,53 @@ +namespace IFoxCAD.Cad; + +internal static class MethodInfoHelper +{ + private static readonly Dictionary methodDic = new(); + + /// + /// 执行函数 + /// + /// 函数 + /// 已经外部创建的对象,为空则此处创建 + public static object? Invoke(this MethodInfo methodInfo, object? instance = null) + { + if (methodInfo == null) + throw new ArgumentNullException(nameof(methodInfo)); + + object? result = null; + if (methodInfo.IsStatic) + { + //新函数指针进入此处 + //参数数量一定要匹配,为null则参数个数不同导致报错, + //参数为stirng[],则可以传入object[]代替,其他参数是否还可以实现默认构造? + var paramInfos = methodInfo.GetParameters(); + var args = new List { }; + for (int i = 0; i < paramInfos.Length; i++) + args.Add(null!); + result = methodInfo.Invoke(null, args.ToArray());//静态调用 + } + else + { + //原命令的函数指针进入此处 + //object instance; + if (methodDic.ContainsKey(methodInfo)) + instance = methodDic[methodInfo]; + + if (instance == null) + { + var reftype = methodInfo.ReflectedType; + if (reftype == null) return null; + var fullName = reftype.FullName; //命名空间+类 + if (fullName == null) return null; + var type = reftype.Assembly.GetType(fullName);//通过程序集反射创建类+ + if (type == null) return null; + instance = Activator.CreateInstance(type); + if (!type.IsAbstract)//无法创建抽象类成员 + methodDic.Add(methodInfo, instance); + } + if (instance != null) + result = methodInfo.Invoke(instance, null); //非静态,调用实例化方法 + } + return result; + } +} \ No newline at end of file diff --git a/src/IFoxCAD.Cad/Runtime/SymbolTable.cs b/src/IFoxCAD.Cad/Runtime/SymbolTable.cs index c319b5e..00bba7b 100644 --- a/src/IFoxCAD.Cad/Runtime/SymbolTable.cs +++ b/src/IFoxCAD.Cad/Runtime/SymbolTable.cs @@ -1,317 +1,327 @@ -using System; -using System.Collections; -using System.Collections.Generic; -using System.Linq; -using Autodesk.AutoCAD.DatabaseServices; -namespace IFoxCAD.Cad +namespace IFoxCAD.Cad; + +public class SymbolTable : IEnumerable + where TTable : SymbolTable + where TRecord : SymbolTableRecord, new() { - public class SymbolTable : IEnumerable - where TTable : SymbolTable - where TRecord : SymbolTableRecord, new() - { - #region 程序集内部属性 - /// - /// 事务管理器 - /// - internal DBTrans DTrans { get; private set; } - /// - /// 数据库 - /// - internal Database Database { get; private set; } + #region 程序集内部属性 + /// + /// 事务管理器 + /// + internal DBTrans DTrans { get; private set; } + /// + /// 数据库 + /// + internal Database Database { get; private set; } - #endregion + #endregion - #region 公开属性 - /// - /// 当前符号表 - /// - public TTable CurrentSymbolTable { get; private set; } - #endregion + #region 公开属性 + /// + /// 当前符号表 + /// + public TTable CurrentSymbolTable { get; private set; } + #endregion - #region 构造函数 - /// - /// 构造函数,初始化Trans和CurrentSymbolTable属性 - /// - /// 事务管理器 - /// 符号表id - internal SymbolTable(DBTrans tr, ObjectId tableId) - { - DTrans = tr; - CurrentSymbolTable = DTrans.GetObject(tableId); - } + #region 构造函数 + /// + /// 构造函数,初始化Trans和CurrentSymbolTable属性 + /// + /// 事务管理器 + /// 符号表id + internal SymbolTable(DBTrans tr, ObjectId tableId) + { + DTrans = tr; + Database = tr.Database; + CurrentSymbolTable = DTrans.GetObject(tableId)!; + } - #endregion + #endregion - #region 索引器 - /// - /// 索引器 - /// - /// 对象名称 - /// 对象的id - public ObjectId this[string key] + #region 索引器 + /// + /// 索引器 + /// + /// 对象名称 + /// 对象的id + public ObjectId this[string key] + { + get { - get - { - if (Has(key)) - { - return CurrentSymbolTable[key]; - } - return ObjectId.Null; - } + if (Has(key)) + return CurrentSymbolTable[key]; + return ObjectId.Null; } - #endregion + } + #endregion - #region Has - /// - /// 判断是否存在符号表记录 - /// - /// 记录名 - /// 存在返回 , 不存在返回 - public bool Has(string key) - { - return CurrentSymbolTable.Has(key); - } - /// - /// 判断是否存在符号表记录 - /// - /// 记录id - /// 存在返回 , 不存在返回 - public bool Has(ObjectId objectId) - { - return CurrentSymbolTable.Has(objectId); - } - #endregion + #region Has + /// + /// 判断是否存在符号表记录 + /// + /// 记录名 + /// 存在返回 , 不存在返回 + public bool Has(string key) + { + return CurrentSymbolTable.Has(key); + } + /// + /// 判断是否存在符号表记录 + /// + /// 记录id + /// 存在返回 , 不存在返回 + public bool Has(ObjectId objectId) + { + return CurrentSymbolTable.Has(objectId); + } + #endregion - #region 添加符号表记录 - /// - /// 添加符号表记录 - /// - /// 符号表记录 - /// 对象id - private ObjectId Add(TRecord record) + #region 添加符号表记录 + /// + /// 添加符号表记录 + /// + /// 符号表记录 + /// 对象id + private ObjectId Add(TRecord record) + { + ObjectId id; + using (CurrentSymbolTable.ForWrite()) { - ObjectId id; - using (CurrentSymbolTable.ForWrite()) - { - id = CurrentSymbolTable.Add(record); - DTrans.Transaction.AddNewlyCreatedDBObject(record, true); - } - return id; + id = CurrentSymbolTable.Add(record); + DTrans.Transaction.AddNewlyCreatedDBObject(record, true); } - /// - /// 添加符号表记录 - /// - /// 符号表记录名 - /// 符号表记录处理函数的无返回值委托 - /// 对象id - public ObjectId Add(string name, Action action = null) - { - ObjectId id = this[name]; - if (id.IsNull) - { - TRecord record = new() - { - Name = name - }; - id = Add(record); - using (record.ForWrite()) - { - action?.Invoke(record); - } - } + return id; + } + /// + /// 添加符号表记录 + /// + /// 符号表记录名 + /// 符号表记录处理函数的无返回值委托 + /// 对象id + public ObjectId Add(string name, Action? action = null) + { + ObjectId id = this[name]; + if (id.IsNull) return id; - } - #endregion - #region 删除符号表记录 - /// - /// 删除符号表记录 - /// - /// 符号表记录对象 - private static void Remove(TRecord record) + var record = new TRecord() { - using (record.ForWrite()) - { - record.Erase(); - } - } - /// - /// 删除符号表记录 - /// - /// 符号表记录名 - public void Remove(string name) - { - TRecord record = GetRecord(name); - if (record is not null) - { - Remove(record); - } + Name = name + }; + id = Add(record); + using (record.ForWrite()) + action?.Invoke(record); + return id; + } + #endregion - } - /// - /// 删除符号表记录 - /// - /// 符号表记录对象id - public void Remove(ObjectId id) - { - TRecord record = GetRecord(id); - if (record is not null) - { - Remove(record); - } - } + #region 删除符号表记录 + /// + /// 删除符号表记录 + /// + /// 符号表记录对象 + private static void Remove(TRecord record) + { + using (record.ForWrite()) + record.Erase(); + } + /// + /// 删除符号表记录 + /// + /// 符号表记录名 + public void Remove(string name) + { + var record = GetRecord(name); + if (record is not null) + Remove(record); + } - #endregion + /// + /// 删除符号表记录 + /// + /// 符号表记录对象id + public void Remove(ObjectId id) + { + var record = GetRecord(id); + if (record is not null) + Remove(record); + } + #endregion - #region 修改符号表记录 - /// - /// 修改符号表 - /// - /// 符号表记录 - /// 修改委托 - private static void Change(TRecord record, Action action) + #region 修改符号表记录 + /// + /// 修改符号表 + /// + /// 符号表记录 + /// 修改委托 + private static void Change(TRecord record, Action action) + { + using (record.ForWrite()) { - using (record.ForWrite()) - { - action?.Invoke(record); - } + action.Invoke(record); } - /// - /// 修改符号表 - /// - /// 符号表记录名 - /// 修改委托 - public void Change(string name, Action action) + // 调用regen()函数可能会导致卡顿 + //Env.Editor.Regen(); + } + /// + /// 修改符号表 + /// + /// 符号表记录名 + /// 修改委托 + public void Change(string name, Action action) + { + var record = GetRecord(name); + if (record is not null) { - var record = GetRecord(name); - if (record is not null) - { - Change(record, action); - } + Change(record, action); } - /// - /// 修改符号表 - /// - /// 符号表记录id - /// 修改委托 - public void Change(ObjectId id, Action action) + } + /// + /// 修改符号表 + /// + /// 符号表记录id + /// 修改委托 + public void Change(ObjectId id, Action action) + { + var record = GetRecord(id); + if (record is not null) { - var record = GetRecord(id); - if (record is not null) - { - Change(record, action); - } + Change(record, action); } - #endregion + } + #endregion - #region 获取符号表记录 - /// - /// 获取符号表记录 - /// - /// 符号表记录的id - /// 打开模式,默认为只读 - /// 符号表记录 - public TRecord GetRecord(ObjectId id, OpenMode openMode = OpenMode.ForRead) => id.IsNull ? null : DTrans.GetObject(id, openMode); + #region 获取符号表记录 + /// + /// 获取符号表记录 + /// + /// 符号表记录的id + /// 打开模式,默认为只读 + /// 符号表记录 + public TRecord? GetRecord(ObjectId id, OpenMode openMode = OpenMode.ForRead) => /*id.IsNull ? null : */DTrans.GetObject(id, openMode); - /// - /// 获取符号表记录 - /// - /// 符号表记录名 - /// 打开模式,默认为只读 - /// 符号表记录 - public TRecord GetRecord(string name, OpenMode openMode = OpenMode.ForRead) => GetRecord(this[name], openMode); + /// + /// 获取符号表记录 + /// + /// 符号表记录名 + /// 打开模式,默认为只读 + /// 符号表记录 + public TRecord? GetRecord(string name, OpenMode openMode = OpenMode.ForRead) => GetRecord(this[name], openMode); - /// - /// 获取符号表记录 - /// - /// 符号表记录集合 - public IEnumerable GetRecords() - { - return this.Select(id => GetRecord(id)); - } - - /// - /// 获取符号表记录的名字集合 - /// - /// 记录的名字集合 - public IEnumerable GetRecordNames() => this.Select(id => GetRecord(id).Name); - /// - /// 获取符合过滤条件的符号表记录名字集合 - /// - /// 过滤器委托 - /// 记录的名字集合 - public IEnumerable GetRecordNames(Func filter) + /// + /// 获取符号表记录 + /// + /// 符号表记录集合 + public IEnumerable GetRecords() + { + foreach (var item in this) { - foreach (var id in this) + var record = GetRecord(item); + if (record is not null) { - var record = GetRecord(id); - if (filter.Invoke(record)) - { - yield return record.Name; - } + yield return record; } } + } - /// - /// 从源数据库拷贝符号表记录 - /// - /// 符号表 - /// 符号表记录名 - /// 是否覆盖, 为覆盖, 为不覆盖 - /// 对象id - public ObjectId GetRecordFrom(SymbolTable table, string name, bool over) + /// + /// 获取符号表记录的名字集合 + /// + /// 记录的名字集合 + public IEnumerable GetRecordNames() => GetRecords().Select(record => record.Name); + /// + /// 获取符合过滤条件的符号表记录名字集合 + /// + /// 过滤器委托 + /// 记录的名字集合 + public IEnumerable GetRecordNames(Func filter) + { + foreach (var item in this) { - if (table is null) - { - throw new ArgumentNullException(nameof(table)); - } - - ObjectId rid = this[name]; - bool has = rid != ObjectId.Null; - if ((has && over) || !has) + var record = GetRecord(item); + if (record is not null && filter.Invoke(record)) { - ObjectId id = table[name]; - using IdMapping idm = new(); - using (ObjectIdCollection ids = new() { id }) - { - table.Database.WblockCloneObjects(ids, CurrentSymbolTable.Id, idm, DuplicateRecordCloning.Replace, false); - } - rid = idm[id].Value; + yield return record.Name; } - return rid; } + } - /// - /// 从文件拷贝符号表记录 - /// - /// 符号表过滤器 - /// 文件名 - /// 符号表记录名 - /// 是否覆盖, 为覆盖, 为不覆盖 - /// 对象id - internal ObjectId GetRecordFrom(Func> tableSelector, string fileName, string name, bool over) + /// + /// 从源数据库拷贝符号表记录 + /// + /// 符号表 + /// 符号表记录名 + /// 是否覆盖, 为覆盖, 为不覆盖 + /// 对象id + public ObjectId GetRecordFrom(SymbolTable table, string name, bool over) + { + if (table is null) { - using var tr = new DBTrans(fileName); - return GetRecordFrom(tableSelector(tr), name, over); + throw new ArgumentNullException(nameof(table), "对象为null"); } - #endregion - #region IEnumerable 成员 - - public IEnumerator GetEnumerator() + ObjectId rid = this[name]; + bool has = rid != ObjectId.Null; + if ((has && over) || !has) { - - foreach (var id in CurrentSymbolTable) + ObjectId id = table[name]; + using IdMapping idm = new(); + using (ObjectIdCollection ids = new() { id }) { - yield return id; + table.Database.WblockCloneObjects(ids, CurrentSymbolTable.Id, idm, DuplicateRecordCloning.Replace, false); } + rid = idm[id].Value; } + return rid; + } + + /// + /// 从文件拷贝符号表记录 + /// + /// 符号表过滤器 + /// 文件名 + /// 符号表记录名 + /// 是否覆盖, 为覆盖, 为不覆盖 + /// 对象id + internal ObjectId GetRecordFrom(Func> tableSelector, string fileName, string name, bool over) + { + using var tr = new DBTrans(fileName); + return GetRecordFrom(tableSelector(tr), name, over); + } + #endregion + + #region 遍历 + /// + /// 遍历集合的迭代器,执行action委托 + /// + /// 集合值的类型 + /// 集合 + /// 要运行的委托 + public void ForEach(Action action, OpenMode openMode = OpenMode.ForRead) + { + //GetRecords().ForEach(re => action(re)); - IEnumerator IEnumerable.GetEnumerator() + foreach (var item in this) { - return GetEnumerator(); + var record = GetRecord(item, openMode); + if (record is not null) + action(record); } - #endregion } + #endregion + + #region IEnumerable 成员 + + public IEnumerator GetEnumerator() + { + foreach (var id in CurrentSymbolTable) + yield return id; + } + + IEnumerator IEnumerable.GetEnumerator() + { + return GetEnumerator(); + } + #endregion } diff --git a/src/IFoxCAD.Cad/Runtime/Utils.cs b/src/IFoxCAD.Cad/Runtime/Utils.cs new file mode 100644 index 0000000..eb372ab --- /dev/null +++ b/src/IFoxCAD.Cad/Runtime/Utils.cs @@ -0,0 +1,98 @@ +using System; + +namespace IFoxCAD.Cad; + +public class Helper +{ + /* + * id = db.GetObjectId(false, handle, 0); + * 参数意义: db.GetObjectId(如果没有找到就创建,句柄号,标记..将来备用) + * 在vs的输出会一直抛出: + * 引发的异常:“Autodesk.AutoCAD.Runtime.Exception”(位于 AcdbMgd.dll 中) + * "eUnknownHandle" + * 这就是为什么慢的原因,所以直接运行就好了!而Debug还是需要用arx的API替代. + */ + + [System.Security.SuppressUnmanagedCodeSecurity] + [DllImport("acdb17.dll", CallingConvention = CallingConvention.ThisCall/*08的调用约定 高版本是__cdecl*/, + EntryPoint = "?getAcDbObjectId@AcDbDatabase@@QAE?AW4ErrorStatus@Acad@@AAVAcDbObjectId@@_NABVAcDbHandle@@K@Z")] + extern static int getAcDbObjectId17x32(IntPtr db, out ObjectId id, [MarshalAs(UnmanagedType.U1)] bool createnew, ref Handle h, uint reserved); + + [System.Security.SuppressUnmanagedCodeSecurity] + [DllImport("acdb17.dll", CallingConvention = CallingConvention.ThisCall/*08的调用约定 高版本是__cdecl*/, + EntryPoint = "?getAcDbObjectId@AcDbDatabase@@QEAA?AW4ErrorStatus@Acad@@AEAVAcDbObjectId@@_NAEBVAcDbHandle@@K@Z")] + extern static int getAcDbObjectId17x64(IntPtr db, out ObjectId id, [MarshalAs(UnmanagedType.U1)] bool createnew, ref Handle h, uint reserved); + + [System.Security.SuppressUnmanagedCodeSecurity] + [DllImport("acdb18.dll", CallingConvention = CallingConvention.ThisCall/*08的调用约定 高版本是__cdecl*/, + EntryPoint = "?getAcDbObjectId@AcDbDatabase@@QAE?AW4ErrorStatus@Acad@@AAVAcDbObjectId@@_NABVAcDbHandle@@K@Z")] + extern static int getAcDbObjectId18x32(IntPtr db, out ObjectId id, [MarshalAs(UnmanagedType.U1)] bool createnew, ref Handle h, uint reserved); + + [System.Security.SuppressUnmanagedCodeSecurity] + [DllImport("acdb18.dll", CallingConvention = CallingConvention.ThisCall/*08的调用约定 高版本是__cdecl*/, + EntryPoint = "?getAcDbObjectId@AcDbDatabase@@QEAA?AW4ErrorStatus@Acad@@AEAVAcDbObjectId@@_NAEBVAcDbHandle@@K@Z")] + extern static int getAcDbObjectId18x64(IntPtr db, out ObjectId id, [MarshalAs(UnmanagedType.U1)] bool createnew, ref Handle h, uint reserved); + + /// + /// 句柄转id,NET35(08~12)专用的 + /// + /// 数据库 + /// 句柄 + /// 返回的id + /// 不存在则创建 + /// 保留,用于未来 + /// 成功0,其他值都是错误.可以强转ErrorStatus + static int GetAcDbObjectId(IntPtr db, Handle handle, out ObjectId id, bool createIfNotFound = false, uint reserved = 0) + { + id = ObjectId.Null; + switch (Application.Version.Major) + { + case 17: + { + if (IntPtr.Size == 4) + return getAcDbObjectId17x32(db, out id, createIfNotFound, ref handle, reserved); + else + return getAcDbObjectId17x64(db, out id, createIfNotFound, ref handle, reserved); + } + case 18: + { + if (IntPtr.Size == 4) + return getAcDbObjectId18x32(db, out id, createIfNotFound, ref handle, reserved); + else + return getAcDbObjectId18x64(db, out id, createIfNotFound, ref handle, reserved); + } + } + return -1; + } + + /// + /// 句柄转id + /// + /// 数据库 + /// 句柄 + /// id + public static ObjectId TryGetObjectId(Database db, Handle handle) + { +#if !NET35 + //高版本直接利用 + var es = db.TryGetObjectId(handle, out ObjectId id); + //if (!es) +#else + var es = GetAcDbObjectId(db.UnmanagedObject, handle, out ObjectId id); + //if (ErrorStatus.OK != (ErrorStatus)es) +#endif + return id; + } + + //public static int GetCadFileVersion(string filename) + //{ + // var bytes = File.ReadAllBytes(filename); + // var headstr = Encoding.Default.GetString(bytes)[0..6]; + // if (!headstr.StartsWith("AC")) return 0; + // var vernum = int.Parse(headstr.Replace("AC", "")); + // var a = Enum.Parse(typeof(DwgVersion), "AC1800"); + // Enum.TryParse() + // return vernum + 986; + + //} +} \ No newline at end of file diff --git a/src/IFoxCAD.Cad/SelectionFilter/OpComp.cs b/src/IFoxCAD.Cad/SelectionFilter/OpComp.cs index 567be46..9727b72 100644 --- a/src/IFoxCAD.Cad/SelectionFilter/OpComp.cs +++ b/src/IFoxCAD.Cad/SelectionFilter/OpComp.cs @@ -1,83 +1,79 @@ -using Autodesk.AutoCAD.DatabaseServices; -using System.Collections.Generic; +namespace IFoxCAD.Cad; -namespace IFoxCAD.Cad +/// +/// 比较运算符类 +/// +public class OpComp : OpEqual { /// - /// 比较运算符类 + /// 比较运算符,如: + /// "<=" + /// 以及合并比较运算符: + /// "<=,<=,=" /// - public class OpComp : OpEqual - { - /// - /// 比较运算符,如: - /// "<=" - /// 以及合并比较运算符: - /// "<=,<=,=" - /// - public string Content { get; } + public string Content { get; } - /// - /// 符号名 - /// - public override string Name - { - get { return "Comp"; } - } + /// + /// 符号名 + /// + public override string Name + { + get { return "Comp"; } + } - /// - /// 比较运算符类构造函数 - /// - /// 运算符 - /// 数据 - public OpComp(string content, TypedValue value) - : base(value) - { - Content = content; - } + /// + /// 比较运算符类构造函数 + /// + /// 运算符 + /// 数据 + public OpComp(string content, TypedValue value) + : base(value) + { + Content = content; + } - /// - /// 比较运算符类构造函数 - /// - /// 运算符 - /// 组码 - public OpComp(string content, int code) - : base(code) - { - Content = content; - } + /// + /// 比较运算符类构造函数 + /// + /// 运算符 + /// 组码 + public OpComp(string content, int code) + : base(code) + { + Content = content; + } - /// - /// 比较运算符类构造函数 - /// - /// 运算符 - /// 组码 - /// 组码值 - public OpComp(string content, int code, object value) - : base(code, value) - { - Content = content; - } + /// + /// 比较运算符类构造函数 + /// + /// 运算符 + /// 组码 + /// 组码值 + public OpComp(string content, int code, object value) + : base(code, value) + { + Content = content; + } - /// - /// 比较运算符类构造函数 - /// - /// 运算符 - /// 组码 - /// 组码值 - public OpComp(string content, DxfCode code, object value) - : base(code, value) - { - Content = content; - } + /// + /// 比较运算符类构造函数 + /// + /// 运算符 + /// 组码 + /// 组码值 + public OpComp(string content, DxfCode code, object value) + : base(code, value) + { + Content = content; + } - /// - /// 获取过滤器数据迭代器 - /// - /// TypedValue迭代器 - public override IEnumerable GetValues() - { - yield return new TypedValue(-4, Content); - yield return Value; - } + /// + /// 获取过滤器数据迭代器 + /// + /// TypedValue迭代器 + public override IEnumerable GetValues() + { + yield return new TypedValue(-4, Content); + yield return Value; } } diff --git a/src/IFoxCAD.Cad/SelectionFilter/OpEqual.cs b/src/IFoxCAD.Cad/SelectionFilter/OpEqual.cs index 76dc738..370a0c1 100644 --- a/src/IFoxCAD.Cad/SelectionFilter/OpEqual.cs +++ b/src/IFoxCAD.Cad/SelectionFilter/OpEqual.cs @@ -1,90 +1,86 @@ -using Autodesk.AutoCAD.DatabaseServices; -using System.Collections.Generic; +namespace IFoxCAD.Cad; -namespace IFoxCAD.Cad +/// +/// 相等运算符类 +/// +public class OpEqual : OpFilter { /// - /// 相等运算符类 + /// 组码与匹配值的TypedValue类型值 /// - public class OpEqual : OpFilter - { - /// - /// 组码与匹配值的TypedValue类型值 - /// - public TypedValue Value { get; private set; } + public TypedValue Value { get; private set; } - /// - /// 符号名 - /// - public override string Name - { - get { return "Equal"; } - } + /// + /// 符号名 + /// + public override string Name + { + get { return "Equal"; } + } - /// - /// 相等运算符类构造函数 - /// - /// 组码 - public OpEqual(int code) - { - Value = new TypedValue(code); - } + /// + /// 相等运算符类构造函数 + /// + /// 组码 + public OpEqual(int code) + { + Value = new TypedValue(code); + } - /// - /// 相等运算符类构造函数 - /// - /// 组码 - /// 组码值 - public OpEqual(int code, object value) - { - Value = new TypedValue(code, value); - } + /// + /// 相等运算符类构造函数 + /// + /// 组码 + /// 组码值 + public OpEqual(int code, object value) + { + Value = new TypedValue(code, value); + } - /// - /// 相等运算符类构造函数 - /// - /// 组码 - /// 组码值 - public OpEqual(DxfCode code, object value) - { - Value = new TypedValue((int)code, value); - } + /// + /// 相等运算符类构造函数 + /// + /// 组码 + /// 组码值 + public OpEqual(DxfCode code, object value) + { + Value = new TypedValue((int)code, value); + } - /// - /// 相等运算符类构造函数 - /// - /// 组码与组码值的TypedValue类型值 - internal OpEqual(TypedValue value) - { - Value = value; - } + /// + /// 相等运算符类构造函数 + /// + /// 组码与组码值的TypedValue类型值 + internal OpEqual(TypedValue value) + { + Value = value; + } - /// - /// 过滤器数据迭代器 - /// - /// TypedValue迭代器 - public override IEnumerable GetValues() - { - yield return Value; - } + /// + /// 过滤器数据迭代器 + /// + /// TypedValue迭代器 + public override IEnumerable GetValues() + { + yield return Value; + } - /// - /// 设置数据 - /// - /// 组码值 - public void SetValue(object value) - { - Value = new TypedValue(Value.TypeCode, value); - } + /// + /// 设置数据 + /// + /// 组码值 + public void SetValue(object value) + { + Value = new TypedValue(Value.TypeCode, value); + } - /// - /// 设置数据 - /// - /// 组码 - /// 组码值 - public void SetValue(int code, object value) - { - Value = new TypedValue(code, value); - } + /// + /// 设置数据 + /// + /// 组码 + /// 组码值 + public void SetValue(int code, object value) + { + Value = new TypedValue(code, value); } } diff --git a/src/IFoxCAD.Cad/SelectionFilter/OpFilter.cs b/src/IFoxCAD.Cad/SelectionFilter/OpFilter.cs index cd73129..a698aeb 100644 --- a/src/IFoxCAD.Cad/SelectionFilter/OpFilter.cs +++ b/src/IFoxCAD.Cad/SelectionFilter/OpFilter.cs @@ -1,342 +1,346 @@ -using Autodesk.AutoCAD.DatabaseServices; -using Autodesk.AutoCAD.EditorInput; -using Autodesk.AutoCAD.Geometry; -using System; -using System.Collections.Generic; -using System.Linq; - -namespace IFoxCAD.Cad +namespace IFoxCAD.Cad; + +/// +/// 选择集过滤器抽象类 +/// +public abstract class OpFilter { /// - /// 选择集过滤器抽象类 + /// 过滤器的名字 + /// + public abstract string Name { get; } + + /// + /// 获取TypedValue类型的值的迭代器的抽象方法,子类必须重写 + /// + /// TypedValue迭代器 + public abstract IEnumerable GetValues(); + + /// + /// 非操作符,返回的是OpFilter类型变量的 属性 + /// + /// OpFilter类型变量 + /// OpFilter对象 + public static OpFilter operator !(OpFilter item) + { + return item.Not; + } + + /// + /// 只读属性,表示这个过滤器取反 + /// + public OpFilter Not + { + get { return new OpNot(this); } + } + + /// + /// 过滤器值转换为 TypedValue 类型数组 + /// + /// TypedValue数组 + public TypedValue[] ToArray() + { + return GetValues().ToArray(); + } + + /// + /// 隐式类型转换,将自定义的过滤器转换为 Autocad 认识的选择集过滤器 + /// + /// 过滤器对象 + /// + /// 选择集过滤器. + /// + public static implicit operator SelectionFilter(OpFilter item) + { + return new SelectionFilter(item.ToArray()); + } + + /// + /// 转换为字符串 + /// + /// 字符串 + public override string ToString() + { + string s = ""; + foreach (var value in GetValues()) + s += value.ToString(); + return s; + } + + /// + /// 构建过滤器 + /// + /// + /// 举两个利用构建函数创建选择集过滤的例子 + /// + /// !(e.Dxf(0) == "line" & e.Dxf(8) == "0") + /// | e.Dxf(0) != "circle" & e.Dxf(8) == "2" & e.Dxf(10) >= p); + /// + /// 例子2: + /// var f2 = OpFilter.Bulid( + /// e => e.Or( + /// !e.And(e.Dxf(0) == "line", e.Dxf(8) == "0"), + /// e.And(e.Dxf(0) != "circle", e.Dxf(8) == "2", + /// e.Dxf(10) <= new Point3d(10, 10, 0)))); + /// ]]> + /// + /// 构建过滤器的函数委托 + /// 过滤器 + public static OpFilter Bulid(Func func) + { + return func(new Op()).Filter!; + } + + #region Operator + + /// + /// 过滤器操作符类 /// - public abstract class OpFilter + public class Op { /// - /// 过滤器的名字 + /// 过滤器属性 /// - public abstract string Name { get; } + internal OpFilter? Filter { get; private set; } + + internal Op() + { + } + + private Op(OpFilter filter) + { + Filter = filter; + } /// - /// 获取TypedValue类型的值的迭代器的抽象方法,子类必须重写 + /// AND 操作符 /// - /// TypedValue迭代器 - public abstract IEnumerable GetValues(); + /// 操作符类型的可变参数 + /// Op对象 +#pragma warning disable CA1822 // 将成员标记为 static + public Op And(params Op[] args) +#pragma warning restore CA1822 // 将成员标记为 static + { + var filter = new OpAnd(); + foreach (var op in args) + filter.Add(op.Filter!); + return new Op(filter); + } /// - /// 非操作符,返回的是OpFilter类型变量的 属性 + /// or 操作符 /// - /// OpFilter类型变量 - /// OpFilter对象 - public static OpFilter operator !(OpFilter item) + /// 操作符类型的可变参数 + /// Op对象 +#pragma warning disable CA1822 // 将成员标记为 static + public Op Or(params Op[] args) +#pragma warning restore CA1822 // 将成员标记为 static { - return item.Not; + var filter = new OpOr(); + foreach (var op in args) + filter.Add(op.Filter!); + return new Op(filter); } /// - /// 只读属性,表示这个过滤器取反 + /// dxf 操作符,此函数只能用于过滤器中,不是组码操作函数 /// - public OpFilter Not + /// 组码 + /// Op对象 +#pragma warning disable CA1822 // 将成员标记为 static + public Op Dxf(int code) +#pragma warning restore CA1822 // 将成员标记为 static { - get { return new OpNot(this); } + return new Op(new OpEqual(code)); } /// - /// 过滤器值转换为 TypedValue 类型数组 + /// dxf 操作符,此函数只能用于过滤器中,不是组码操作函数 /// - /// TypedValue数组 - public TypedValue[] ToArray() + /// 组码 + /// 关系运算符的值,比如">,>,=" + /// Op对象 +#pragma warning disable CA1822 // 将成员标记为 static + public Op Dxf(int code, string content) +#pragma warning restore CA1822 // 将成员标记为 static { - return GetValues().ToArray(); + return new Op(new OpComp(content, code)); } /// - /// 隐式类型转换,将自定义的过滤器转换为 Autocad 认识的选择集过滤器 + /// 非操作符 /// - /// 过滤器对象 - /// - /// 选择集过滤器. - /// - public static implicit operator SelectionFilter(OpFilter item) + /// 过滤器操作符对象 + /// Op对象 + public static Op operator !(Op right) { - return new SelectionFilter(item.ToArray()); + right.Filter = !right.Filter!; + return right; } /// - /// 转换为字符串 + /// 相等操作符 /// - /// 字符串 - public override string ToString() + /// 过滤器操作符对象 + /// 数据 + /// Op对象 + public static Op operator ==(Op left, object right) { - string s = ""; - foreach (var value in GetValues()) - s += value.ToString(); - return s; + var eq = (OpEqual)left.Filter!; + eq.SetValue(right); + return left; } /// - /// 构建过滤器 + /// 不等操作符 /// - /// - /// 举两个利用构建函数创建选择集过滤的例子 - /// - /// !(e.Dxf(0) == "line" & e.Dxf(8) == "0") - /// | e.Dxf(0) != "circle" & e.Dxf(8) == "2" & e.Dxf(10) >= p); - /// - /// 例子2: - /// var f2 = OpFilter.Bulid( - /// e => e.Or( - /// !e.And(e.Dxf(0) == "line", e.Dxf(8) == "0"), - /// e.And(e.Dxf(0) != "circle", e.Dxf(8) == "2", - /// e.Dxf(10) <= new Point3d(10, 10, 0)))); - /// ]]> - /// - /// 构建过滤器的函数委托 - /// 过滤器 - public static OpFilter Bulid(Func func) + /// 过滤器操作符对象 + /// 数据 + /// Op对象 + public static Op operator !=(Op left, object right) { - return func(new OpFilter.Op()).Filter; + var eq = (OpEqual)left.Filter!; + eq.SetValue(right); + left.Filter = eq.Not; + return left; } - #region Operator + private static Op GetCompOp(string content, Op left, object right) + { + var eq = (OpEqual)left.Filter!; + var comp = new OpComp(content, eq.Value.TypeCode, right); + return new Op(comp); + } /// - /// 过滤器操作符类 + /// 大于操作符 /// - public class Op + /// 过滤器操作符对象 + /// 数据 + /// Op对象 + public static Op operator >(Op left, object right) { - /// - /// 过滤器属性 - /// - internal OpFilter Filter { get; private set; } + return GetCompOp(">", left, right); + } - internal Op() - { - } + /// + /// 小于操作符 + /// + /// 过滤器操作符对象 + /// 数据 + /// Op对象 + public static Op operator <(Op left, object right) + { + return GetCompOp("<", left, right); + } - private Op(OpFilter filter) - { - Filter = filter; - } - - /// - /// AND 操作符 - /// - /// 操作符类型的可变参数 - /// Op对象 - public Op And(params Op[] args) - { - var filter = new OpAnd(); - foreach (var op in args) - filter.Add(op.Filter); - return new Op(filter); - } - - /// - /// or 操作符 - /// - /// 操作符类型的可变参数 - /// Op对象 - public Op Or(params Op[] args) - { - var filter = new OpOr(); - foreach (var op in args) - filter.Add(op.Filter); - return new Op(filter); - } - - /// - /// dxf 操作符,此函数只能用于过滤器中,不是组码操作函数 - /// - /// 组码 - /// Op对象 - public Op Dxf(int code) - { - return new Op(new OpEqual(code)); - } - - /// - /// dxf 操作符,此函数只能用于过滤器中,不是组码操作函数 - /// - /// 组码 - /// 关系运算符的值,比如">,>,=" - /// Op对象 - public Op Dxf(int code, string content) - { - return new Op(new OpComp(content, code)); - } - - /// - /// 非操作符 - /// - /// 过滤器操作符对象 - /// Op对象 - public static Op operator !(Op right) - { - right.Filter = !right.Filter; - return right; - } - - /// - /// 相等操作符 - /// - /// 过滤器操作符对象 - /// 数据 - /// Op对象 - public static Op operator ==(Op left, object right) - { - var eq = (OpEqual)left.Filter; - eq.SetValue(right); - return left; - } - - /// - /// 不等操作符 - /// - /// 过滤器操作符对象 - /// 数据 - /// Op对象 - public static Op operator !=(Op left, object right) - { - var eq = (OpEqual)left.Filter; - eq.SetValue(right); - left.Filter = eq.Not; - return left; - } + /// + /// 大于等于操作符 + /// + /// 过滤器操作符对象 + /// 数据 + /// Op对象 + public static Op operator >=(Op left, object right) + { + return GetCompOp(">=", left, right); + } - private static Op GetCompOp(string content, Op left, object right) - { - var eq = (OpEqual)left.Filter; - var comp = new OpComp(content, eq.Value.TypeCode, right); - return new Op(comp); - } - - /// - /// 大于操作符 - /// - /// 过滤器操作符对象 - /// 数据 - /// Op对象 - public static Op operator >(Op left, object right) - { - return GetCompOp(">", left, right); - } - - /// - /// 小于操作符 - /// - /// 过滤器操作符对象 - /// 数据 - /// Op对象 - public static Op operator <(Op left, object right) - { - return GetCompOp("<", left, right); - } - - /// - /// 大于等于操作符 - /// - /// 过滤器操作符对象 - /// 数据 - /// Op对象 - public static Op operator >=(Op left, object right) - { - return GetCompOp(">=", left, right); - } - - /// - /// 小于等于操作符 - /// - /// 过滤器操作符对象 - /// 数据 - /// Op对象 - public static Op operator <=(Op left, object right) - { - return GetCompOp("<=", left, right); - } - - /// - /// 大于等于操作符 - /// - /// 过滤器操作符对象 - /// 点 - /// Op对象 - public static Op operator >=(Op left, Point3d right) - { - return GetCompOp(">,>,*", left, right); - } - - /// - /// 小于等于操作符 - /// - /// 过滤器操作符对象 - /// 点 - /// Op对象 - public static Op operator <=(Op left, Point3d right) - { - return GetCompOp("<,<,*", left, right); - } - - /// - /// 并操作符 - /// - /// 过滤器操作符对象 - /// 过滤器操作符对象 - /// Op对象 - public static Op operator &(Op left, Op right) - { - var filter = new OpAnd(); - filter.Add(left.Filter); - filter.Add(right.Filter); - return new Op(filter); - } - - /// - /// 或操作符 - /// - /// 过滤器操作符对象 - /// 过滤器操作符对象 - /// Op对象 - public static Op operator |(Op left, Op right) + /// + /// 小于等于操作符 + /// + /// 过滤器操作符对象 + /// 数据 + /// Op对象 + public static Op operator <=(Op left, object right) + { + return GetCompOp("<=", left, right); + } + + /// + /// 大于等于操作符 + /// + /// 过滤器操作符对象 + /// 点 + /// Op对象 + public static Op operator >=(Op left, Point3d right) + { + return GetCompOp(">,>,*", left, right); + } + + /// + /// 小于等于操作符 + /// + /// 过滤器操作符对象 + /// 点 + /// Op对象 + public static Op operator <=(Op left, Point3d right) + { + return GetCompOp("<,<,*", left, right); + } + + /// + /// 并操作符 + /// + /// 过滤器操作符对象 + /// 过滤器操作符对象 + /// Op对象 + public static Op operator &(Op left, Op right) + { + var filter = new OpAnd { - var filter = new OpOr(); - filter.Add(left.Filter); - filter.Add(right.Filter); - return new Op(filter); - } - - /// - /// 异或操作符 - /// - /// 过滤器操作符对象 - /// 过滤器操作符对象 - /// Op对象 - public static Op operator ^(Op left, Op right) + left.Filter!, + right.Filter! + }; + return new Op(filter); + } + + /// + /// 或操作符 + /// + /// 过滤器操作符对象 + /// 过滤器操作符对象 + /// Op对象 + public static Op operator |(Op left, Op right) + { + var filter = new OpOr { - var filter = new OpXor(left.Filter, right.Filter); - return new Op(filter); - } - - /// - /// 比较函数 - /// - /// 对象 - /// - /// 是否相等 - /// - public override bool Equals(object obj) => base.Equals(obj); - - /// - /// 获取HashCode - /// - /// HashCode - public override int GetHashCode() => base.GetHashCode(); + left.Filter!, + right.Filter! + }; + return new Op(filter); } - #endregion Operator + /// + /// 异或操作符 + /// + /// 过滤器操作符对象 + /// 过滤器操作符对象 + /// Op对象 + public static Op operator ^(Op left, Op right) + { + var filter = new OpXor(left.Filter!, right.Filter!); + return new Op(filter); + } + + /// + /// 比较函数 + /// + /// 对象 + /// + /// 是否相等 + /// + public override bool Equals(object obj) => base.Equals(obj); + + /// + /// 获取HashCode + /// + /// HashCode + public override int GetHashCode() => base.GetHashCode(); } + + #endregion Operator } diff --git a/src/IFoxCAD.Cad/SelectionFilter/OpList.cs b/src/IFoxCAD.Cad/SelectionFilter/OpList.cs index 06a5408..3a02513 100644 --- a/src/IFoxCAD.Cad/SelectionFilter/OpList.cs +++ b/src/IFoxCAD.Cad/SelectionFilter/OpList.cs @@ -1,170 +1,166 @@ -using Autodesk.AutoCAD.DatabaseServices; -using System.Collections.Generic; +namespace IFoxCAD.Cad; -namespace IFoxCAD.Cad +/// +/// 逻辑操作符的列表抽象类 +/// +public abstract class OpList : OpLogi { /// - /// 逻辑操作符的列表抽象类 + /// 过滤器列表 /// - public abstract class OpList : OpLogi + protected List _lst = new(); + + /// + /// 添加过滤器条件的虚函数,子类可以重写 + /// + /// 举个利用这个类及其子类创建选择集过滤的例子 + /// + /// ,>,*" } + /// }, + /// }; + /// ]]> + /// + /// 过滤器对象 + public virtual void Add(OpFilter value) { - /// - /// 过滤器列表 - /// - protected List _lst = new(); + _lst.Add(value); + } - /// - /// 添加过滤器条件的虚函数,子类可以重写 - /// - /// 举个利用这个类及其子类创建选择集过滤的例子 - /// - /// ,>,*" } - /// }, - /// }; - /// ]]> - /// - /// 过滤器对象 - public virtual void Add(OpFilter value) - { - _lst.Add(value); - } + /// + /// 添加过滤条件 + /// + /// 逻辑非~ + /// 组码 + /// 组码值 + public void Add(string speccode, int code, object value) + { + if (speccode == "~") + _lst.Add(new OpEqual(code, value).Not); + } - /// - /// 添加过滤条件 - /// - /// 逻辑非~ - /// 组码 - /// 组码值 - public void Add(string speccode, int code, object value) - { - if (speccode == "~") - _lst.Add(new OpEqual(code, value).Not); - } + /// + /// 添加过滤条件 + /// + /// 组码 + /// 组码值 + public void Add(int code, object value) + { + _lst.Add(new OpEqual(code, value)); + } - /// - /// 添加过滤条件 - /// - /// 组码 - /// 组码值 - public void Add(int code, object value) - { - _lst.Add(new OpEqual(code, value)); - } + /// + /// 添加过滤条件 + /// + /// 组码 + /// 组码值 + public void Add(DxfCode code, object value) + { + _lst.Add(new OpEqual(code, value)); + } - /// - /// 添加过滤条件 - /// - /// 组码 - /// 组码值 - public void Add(DxfCode code, object value) - { - _lst.Add(new OpEqual(code, value)); - } + /// + /// 添加过滤条件 + /// + /// 组码 + /// 组码值 + /// 比较运算符 + public void Add(int code, object value, string comp) + { + _lst.Add(new OpComp(comp, code, value)); + } - /// - /// 添加过滤条件 - /// - /// 组码 - /// 组码值 - /// 比较运算符 - public void Add(int code, object value, string comp) - { - _lst.Add(new OpComp(comp, code, value)); - } + /// + /// 添加过滤条件 + /// + /// 组码 + /// 组码值 + /// 比较运算符 + public void Add(DxfCode code, object value, string comp) + { + _lst.Add(new OpComp(comp, code, value)); + } - /// - /// 添加过滤条件 - /// - /// 组码 - /// 组码值 - /// 比较运算符 - public void Add(DxfCode code, object value, string comp) - { - _lst.Add(new OpComp(comp, code, value)); - } + /// + /// 过滤器迭代器 + /// + /// OpFilter迭代器 + public override IEnumerator GetEnumerator() + { + foreach (var value in _lst) + yield return value; + } +} - /// - /// 过滤器迭代器 - /// - /// OpFilter迭代器 - public override IEnumerator GetEnumerator() - { - foreach (var value in _lst) - yield return value; - } +/// +/// 逻辑与类 +/// +public class OpAnd : OpList +{ + /// + /// 符号名 + /// + public override string Name + { + get { return "And"; } } /// - /// 逻辑与类 + /// 添加过滤条件 /// - public class OpAnd : OpList + /// 过滤器对象 + public override void Add(OpFilter value) { - /// - /// 符号名 - /// - public override string Name + if (value is OpAnd opand) { - get { return "And"; } + foreach (var item in opand) + _lst.Add(item); } - - /// - /// 添加过滤条件 - /// - /// 过滤器对象 - public override void Add(OpFilter value) + else { - if (value is OpAnd opand) - { - foreach (var item in opand) - _lst.Add(item); - } - else - { - _lst.Add(value); - } + _lst.Add(value); } } +} + +/// +/// 逻辑或类 +/// +public class OpOr : OpList +{ + /// + /// 符号名 + /// + public override string Name + { + get { return "Or"; } + } /// - /// 逻辑或类 + /// 添加过滤条件 /// - public class OpOr : OpList + /// 过滤器对象 + public override void Add(OpFilter value) { - /// - /// 符号名 - /// - public override string Name + if (value is OpOr opor) { - get { return "Or"; } + foreach (var item in opor) + _lst.Add(item); } - - /// - /// 添加过滤条件 - /// - /// 过滤器对象 - public override void Add(OpFilter value) + else { - if (value is OpOr opor) - { - foreach (var item in opor) - _lst.Add(item); - } - else - { - _lst.Add(value); - } + _lst.Add(value); } } -} \ No newline at end of file +} diff --git a/src/IFoxCAD.Cad/SelectionFilter/OpLogi.cs b/src/IFoxCAD.Cad/SelectionFilter/OpLogi.cs index 3fa0785..4227608 100644 --- a/src/IFoxCAD.Cad/SelectionFilter/OpLogi.cs +++ b/src/IFoxCAD.Cad/SelectionFilter/OpLogi.cs @@ -1,133 +1,128 @@ -using Autodesk.AutoCAD.DatabaseServices; -using System.Collections; -using System.Collections.Generic; +namespace IFoxCAD.Cad; -namespace IFoxCAD.Cad +/// +/// 过滤器逻辑运算符抽象类 +/// +public abstract class OpLogi : OpFilter, IEnumerable { /// - /// 过滤器逻辑运算符抽象类 + /// 返回-4组码的开始内容 /// - public abstract class OpLogi : OpFilter, IEnumerable + public TypedValue First { - /// - /// 返回-4组码的开始内容 - /// - public TypedValue First - { - get { return new TypedValue(-4, $"<{Name}"); } - } - - /// - /// 返回-4组码的结束内容 - /// - public TypedValue Last - { - get { return new TypedValue(-4, $"{Name}>"); } - } - - /// - /// 获取过滤条件 - /// - /// TypedValue迭代器 - public override IEnumerable GetValues() - { - yield return First; - foreach (var item in this) - { - foreach (var value in item.GetValues()) - yield return value; - } - yield return Last; - } + get { return new TypedValue(-4, $"<{Name}"); } + } - /// - /// 获取迭代器 - /// - /// OpFilter迭代器 - public abstract IEnumerator GetEnumerator(); + /// + /// 返回-4组码的结束内容 + /// + public TypedValue Last + { + get { return new TypedValue(-4, $"{Name}>"); } + } - IEnumerator IEnumerable.GetEnumerator() + /// + /// 获取过滤条件 + /// + /// TypedValue迭代器 + public override IEnumerable GetValues() + { + yield return First; + foreach (var item in this) { - return GetEnumerator(); + foreach (var value in item.GetValues()) + yield return value; } + yield return Last; } /// - /// 逻辑非类 + /// 获取迭代器 /// - public class OpNot : OpLogi + /// OpFilter迭代器 + public abstract IEnumerator GetEnumerator(); + + IEnumerator IEnumerable.GetEnumerator() { - private OpFilter Value { get; } + return GetEnumerator(); + } +} - /// - /// 逻辑非类构造函数 - /// - /// OpFilter数据 - public OpNot(OpFilter value) - { - Value = value; - } +/// +/// 逻辑非类 +/// +public class OpNot : OpLogi +{ + private OpFilter Value { get; } - /// - /// 符号名 - /// - public override string Name - { - get { return "Not"; } - } + /// + /// 逻辑非类构造函数 + /// + /// OpFilter数据 + public OpNot(OpFilter value) + { + Value = value; + } - /// - /// 获取迭代器 - /// - /// OpFilter迭代器 - public override IEnumerator GetEnumerator() - { - yield return Value; - } + /// + /// 符号名 + /// + public override string Name + { + get { return "Not"; } } /// - /// 逻辑异或类 + /// 获取迭代器 /// - public class OpXor : OpLogi + /// OpFilter迭代器 + public override IEnumerator GetEnumerator() { - /// - /// 左操作数 - /// - public OpFilter Left { get; } + yield return Value; + } +} - /// - /// 右操作数 - /// - public OpFilter Right { get; } +/// +/// 逻辑异或类 +/// +public class OpXor : OpLogi +{ + /// + /// 左操作数 + /// + public OpFilter Left { get; } - /// - /// 逻辑异或类构造函数 - /// - /// 左操作数 - /// 右操作数 - public OpXor(OpFilter left, OpFilter right) - { - Left = left; - Right = right; - } + /// + /// 右操作数 + /// + public OpFilter Right { get; } - /// - /// 符号名 - /// - public override string Name - { - get { return "Xor"; } - } + /// + /// 逻辑异或类构造函数 + /// + /// 左操作数 + /// 右操作数 + public OpXor(OpFilter left, OpFilter right) + { + Left = left; + Right = right; + } - /// - /// 获取迭代器 - /// - /// 选择集过滤器类型迭代器 - public override IEnumerator GetEnumerator() - { - yield return Left; - yield return Right; - } + /// + /// 符号名 + /// + public override string Name + { + get { return "Xor"; } + } + + /// + /// 获取迭代器 + /// + /// 选择集过滤器类型迭代器 + public override IEnumerator GetEnumerator() + { + yield return Left; + yield return Right; } -} \ No newline at end of file +} diff --git a/src/IFoxCAD.WPF/Converter.cs b/src/IFoxCAD.WPF/Converter.cs index 7682212..828ece9 100644 --- a/src/IFoxCAD.WPF/Converter.cs +++ b/src/IFoxCAD.WPF/Converter.cs @@ -1,108 +1,98 @@ -using System; -using System.Collections.Generic; -using System.Globalization; -using System.Linq; -using System.Text; +namespace IFoxCAD.WPF; -using System.Windows.Data; - - -namespace IFoxCAD.WPF +/// +/// 字符串到整数的转换器 +/// +public class StringToIntConverter : IValueConverter +{ + /// + /// 字符串转换到整数 + /// + /// 绑定源生成的值 + /// 绑定目标属性的类型 + /// 要使用的转换器参数 + /// 要用在转换器中的区域性 + /// 转换后的值。 如果该方法返回 null,则使用有效的 null 值。 + public object Convert(object value, Type targetType, object parameter, CultureInfo culture) + { + string? a = value as string; + _ = int.TryParse(a, out int b); + return b; + } + /// + /// 整数转换到字符串 + /// + /// 绑定目标生成的值 + /// 要转换为的类型 + /// 要使用的转换器参数 + /// 要用在转换器中的区域性 + /// 转换后的值。 如果该方法返回 null,则使用有效的 null 值。 + public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture) + { + return value.ToString(); + } +} +/// +/// 字符串到实数的转换器 +/// +public class StringToDoubleConverter : IValueConverter { /// - /// 字符串到整数的转换器 + /// 字符串转换到实数 /// - public class StringToIntConverter : IValueConverter + /// 绑定源生成的值 + /// 绑定目标属性的类型 + /// 要使用的转换器参数 + /// 要用在转换器中的区域性 + /// 转换后的值。 如果该方法返回 null,则使用有效的 null 值。 + public object Convert(object value, Type targetType, object parameter, CultureInfo culture) { - /// - /// 字符串转换到整数 - /// - /// 绑定源生成的值 - /// 绑定目标属性的类型 - /// 要使用的转换器参数 - /// 要用在转换器中的区域性 - /// 转换后的值。 如果该方法返回 null,则使用有效的 null 值。 - public object Convert(object value, Type targetType, object parameter, CultureInfo culture) - { - string a = value as string; - _ = int.TryParse(a, out int b); - return b; - } - /// - /// 整数转换到字符串 - /// - /// 绑定目标生成的值 - /// 要转换为的类型 - /// 要使用的转换器参数 - /// 要用在转换器中的区域性 - /// 转换后的值。 如果该方法返回 null,则使用有效的 null 值。 - public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture) - { - return value.ToString(); - } + string? a = value as string; + _ = double.TryParse(a, out double b); + return b; } /// - /// 字符串到实数的转换器 + /// 实数转换到字符串 + /// + /// 绑定目标生成的值 + /// 要转换为的类型 + /// 要使用的转换器参数 + /// 要用在转换器中的区域性 + /// 转换后的值。 如果该方法返回 null,则使用有效的 null 值。 + public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture) + { + return value.ToString(); + } +} +/// +/// 整数到字符串的转换器 +/// +public class IntToStringConverter : IValueConverter +{ + /// + /// 整数转换到字符串 /// - public class StringToDoubleConverter : IValueConverter + /// 绑定源生成的值 + /// 绑定目标属性的类型 + /// 要使用的转换器参数 + /// 要用在转换器中的区域性 + /// 转换后的值。 如果该方法返回 null,则使用有效的 null 值。 + public object Convert(object value, Type targetType, object parameter, CultureInfo culture) { - /// - /// 字符串转换到实数 - /// - /// 绑定源生成的值 - /// 绑定目标属性的类型 - /// 要使用的转换器参数 - /// 要用在转换器中的区域性 - /// 转换后的值。 如果该方法返回 null,则使用有效的 null 值。 - public object Convert(object value, Type targetType, object parameter, CultureInfo culture) - { - string a = value as string; - _ = double.TryParse(a, out double b); - return b; - } - /// - /// 实数转换到字符串 - /// - /// 绑定目标生成的值 - /// 要转换为的类型 - /// 要使用的转换器参数 - /// 要用在转换器中的区域性 - /// 转换后的值。 如果该方法返回 null,则使用有效的 null 值。 - public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture) - { - return value.ToString(); - } + return value.ToString(); } /// - /// 整数到字符串的转换器 + /// 字符串转换到整数 /// - public class IntToStringConverter : IValueConverter + /// 绑定目标生成的值 + /// 要转换为的类型 + /// 要使用的转换器参数 + /// 要用在转换器中的区域性 + /// 转换后的值。 如果该方法返回 null,则使用有效的 null 值。 + public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture) { - /// - /// 整数转换到字符串 - /// - /// 绑定源生成的值 - /// 绑定目标属性的类型 - /// 要使用的转换器参数 - /// 要用在转换器中的区域性 - /// 转换后的值。 如果该方法返回 null,则使用有效的 null 值。 - public object Convert(object value, Type targetType, object parameter, CultureInfo culture) - { - return value.ToString(); - } - /// - /// 字符串转换到整数 - /// - /// 绑定目标生成的值 - /// 要转换为的类型 - /// 要使用的转换器参数 - /// 要用在转换器中的区域性 - /// 转换后的值。 如果该方法返回 null,则使用有效的 null 值。 - public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture) - { - string a = value as string; - _ = int.TryParse(a, out int b); - return b; - } + string? a = value as string; + _ = int.TryParse(a, out int b); + return b; } } diff --git a/src/IFoxCAD.WPF/DependencyObjectExtensions.cs b/src/IFoxCAD.WPF/DependencyObjectExtensions.cs index 39cd184..596961d 100644 --- a/src/IFoxCAD.WPF/DependencyObjectExtensions.cs +++ b/src/IFoxCAD.WPF/DependencyObjectExtensions.cs @@ -1,40 +1,34 @@ -using System.Windows; -using System.Windows.Media; +namespace IFoxCAD.WPF; -namespace IFoxCAD.WPF +/// +/// 依赖属性扩展类 +/// +public static class DependencyObjectExtensions { /// - /// 依赖属性扩展类 + /// 获取父对象依赖属性 /// - public static class DependencyObjectExtensions + /// 子对象 + /// 依赖属性 + public static DependencyObject? GetParentObject(this DependencyObject child) { - /// - /// 获取父对象依赖属性 - /// - /// 子对象 - /// 依赖属性 - public static DependencyObject? GetParentObject(this DependencyObject child) - { - if (child is null) - return null; - - if (child is ContentElement contentElement) - { - var parent = ContentOperations.GetParent(contentElement); - if (parent is not null) return parent; + if (child is null) return null; - var fce = contentElement as FrameworkContentElement; - return fce?.Parent; - } + if (child is ContentElement contentElement) + { + DependencyObject parent = ContentOperations.GetParent(contentElement); + if (parent is not null) return parent; - if (child is FrameworkElement frameworkElement) - { - var parent = frameworkElement.Parent; - if (parent is not null) - return parent; - } + FrameworkContentElement? fce = contentElement as FrameworkContentElement; + return fce?.Parent; + } - return VisualTreeHelper.GetParent(child); + if (child is FrameworkElement frameworkElement) + { + DependencyObject parent = frameworkElement.Parent; + if (parent is not null) return parent; } + + return VisualTreeHelper.GetParent(child); } } diff --git a/src/IFoxCAD.WPF/EnumSelection.cs b/src/IFoxCAD.WPF/EnumSelection.cs new file mode 100644 index 0000000..d964351 --- /dev/null +++ b/src/IFoxCAD.WPF/EnumSelection.cs @@ -0,0 +1,72 @@ +namespace IFoxCAD.WPF; +public class EnumSelection : INotifyPropertyChanged where T : struct, IComparable, IFormattable, IConvertible +{ + private T value; // stored value of the Enum + private readonly bool isFlagged; // Enum uses flags? + private readonly bool canDeselect; // Can be deselected? (Radio buttons cannot deselect, checkboxes can) + private readonly T blankValue; // what is considered the "blank" value if it can be deselected? + + public EnumSelection(T value) : this(value, false, default) { } + public EnumSelection(T value, bool canDeselect) : this(value, canDeselect, default) { } + public EnumSelection(T value, T blankValue) : this(value, true, blankValue) { } + public EnumSelection(T value, bool canDeselect, T blankValue) + { + if (!typeof(T).IsEnum) throw new ArgumentException($"{nameof(T)} must be an enum type"); // I really wish there was a way to constrain generic types to enums... + isFlagged = typeof(T).IsDefined(typeof(FlagsAttribute), false); + + this.value = value; + this.canDeselect = canDeselect; + this.blankValue = blankValue; + } + + public T Value + { + get { return value; } + set + { + if (this.value.Equals(value)) return; + this.value = value; + OnPropertyChanged(); + OnPropertyChanged("Item[]"); // Notify that the indexer property has changed + } + } + + [IndexerName("Item")] + public bool this[T key] + { + get + { + int iKey = (int)(object)key; + return isFlagged ? ((int)(object)value & iKey) == iKey : value.Equals(key); + } + set + { + if (isFlagged) + { + int iValue = (int)(object)this.value; + int iKey = (int)(object)key; + + if (((iValue & iKey) == iKey) == value) return; + + if (value) + Value = (T)(object)(iValue | iKey); + else + Value = (T)(object)(iValue & ~iKey); + } + else + { + if (this.value.Equals(key) == value) return; + if (!value && !canDeselect) return; + + Value = value ? key : blankValue; + } + } + } + + public event PropertyChangedEventHandler? PropertyChanged; + + private void OnPropertyChanged([CallerMemberName] string propertyName = "") + { + PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName)); + } +} \ No newline at end of file diff --git a/src/IFoxCAD.WPF/EventBindingExtension.cs b/src/IFoxCAD.WPF/EventBindingExtension.cs index 9298c07..eb58c75 100644 --- a/src/IFoxCAD.WPF/EventBindingExtension.cs +++ b/src/IFoxCAD.WPF/EventBindingExtension.cs @@ -1,221 +1,210 @@ -using System; -using System.ComponentModel; -using System.Reflection.Emit; -using System.Windows.Input; -using System.Windows.Markup; -using System.Windows; -using System.Reflection; +namespace IFoxCAD.WPF; - -namespace IFoxCAD.WPF +/// +/// 事件绑定标签类 +/// +/// +public class EventBindingExtension : MarkupExtension { /// - /// 事件绑定标签类 + /// 命令属性 + /// + public string? Command { get; set; } + /// + /// 命令参数属性 + /// + public string? CommandParameter { get; set; } + /// + /// 当在派生类中实现时,返回用作此标记扩展的目标属性值的对象。 /// - /// - public class EventBindingExtension : MarkupExtension + /// 可为标记扩展提供服务的服务提供程序帮助程序。 + /// + /// 要在应用了扩展的属性上设置的对象值。 + /// + /// + /// + public override object? ProvideValue(IServiceProvider serviceProvider) { - /// - /// 命令属性 - /// - public string Command { get; set; } - /// - /// 命令参数属性 - /// - public string CommandParameter { get; set; } - /// - /// 当在派生类中实现时,返回用作此标记扩展的目标属性值的对象。 - /// - /// 可为标记扩展提供服务的服务提供程序帮助程序。 - /// - /// 要在应用了扩展的属性上设置的对象值。 - /// - /// - /// - public override object ProvideValue(IServiceProvider serviceProvider) + if (serviceProvider is null) { - if (serviceProvider is null) - { - throw new ArgumentNullException(nameof(serviceProvider)); - } - if (!(serviceProvider.GetService(typeof(IProvideValueTarget)) is IProvideValueTarget targetProvider)) - { - throw new InvalidOperationException(); - } - - if (!(targetProvider.TargetObject is FrameworkElement targetObject)) - { - throw new InvalidOperationException(); - } - - var memberInfo = targetProvider.TargetProperty as MemberInfo; - if (memberInfo is null) - { - throw new InvalidOperationException(); - } + throw new ArgumentNullException(nameof(serviceProvider)); + } + if (serviceProvider.GetService(typeof(IProvideValueTarget)) is not IProvideValueTarget targetProvider) + { + throw new InvalidOperationException(); + } - if (string.IsNullOrWhiteSpace(Command)) - { - Command = memberInfo.Name.Replace("Add", ""); - if (Command.Contains("Handler")) - { - Command = Command.Replace("Handler", "Command"); - } - else - { - Command += "Command"; - } - } + if (targetProvider.TargetObject is not FrameworkElement targetObject) + { + throw new InvalidOperationException(); + } - return CreateHandler(memberInfo, Command, targetObject.GetType()); + if (targetProvider.TargetProperty is not MemberInfo memberInfo) + { + throw new InvalidOperationException(); } - private Type GetEventHandlerType(MemberInfo memberInfo) + if (string.IsNullOrWhiteSpace(Command)) { - Type eventHandlerType = null; - if (memberInfo is EventInfo) + Command = memberInfo.Name.Replace("Add", ""); + if (Command.Contains("Handler")) { - var info = memberInfo as EventInfo; - var eventInfo = info; - eventHandlerType = eventInfo.EventHandlerType; + Command = Command.Replace("Handler", "Command"); } - else if (memberInfo is MethodInfo) + else { - var info = memberInfo as MethodInfo; - var methodInfo = info; - ParameterInfo[] pars = methodInfo.GetParameters(); - eventHandlerType = pars[1].ParameterType; + Command += "Command"; } - - return eventHandlerType; } - private object CreateHandler(MemberInfo memberInfo, string cmdName, Type targetType) + return CreateHandler(memberInfo, Command!, targetObject.GetType()); + } + + private Type? GetEventHandlerType(MemberInfo memberInfo) + { + Type? eventHandlerType = null; + if (memberInfo is EventInfo eventInfo) + { + //var info = memberInfo as EventInfo; + //var eventInfo = info; + eventHandlerType = eventInfo.EventHandlerType; + } + else if (memberInfo is MethodInfo methodInfo) { - Type eventHandlerType = GetEventHandlerType(memberInfo); + //var info = memberInfo as MethodInfo; + //var methodInfo = info; + ParameterInfo[] pars = methodInfo.GetParameters(); + eventHandlerType = pars[1].ParameterType; + } - if (eventHandlerType is null) return null; + return eventHandlerType; + } - var handlerInfo = eventHandlerType.GetMethod("Invoke"); - var method = new DynamicMethod("", handlerInfo.ReturnType, - new Type[] - { - handlerInfo.GetParameters()[0].ParameterType, - handlerInfo.GetParameters()[1].ParameterType, - }); +#pragma warning disable IDE0060 // 删除未使用的参数 + private object? CreateHandler(MemberInfo memberInfo, string cmdName, Type targetType) +#pragma warning restore IDE0060 // 删除未使用的参数 + { + Type? eventHandlerType = GetEventHandlerType(memberInfo); - var gen = method.GetILGenerator(); - gen.Emit(OpCodes.Ldarg, 0); - gen.Emit(OpCodes.Ldarg, 1); - gen.Emit(OpCodes.Ldstr, cmdName); - if (CommandParameter is null) - { - gen.Emit(OpCodes.Ldnull); - } - else + if (eventHandlerType is null) return null; + + var handlerInfo = eventHandlerType.GetMethod("Invoke"); + var method = new DynamicMethod("", handlerInfo.ReturnType, + new Type[] { - gen.Emit(OpCodes.Ldstr, CommandParameter); - } - gen.Emit(OpCodes.Call, getMethod); - gen.Emit(OpCodes.Ret); + handlerInfo.GetParameters()[0].ParameterType, + handlerInfo.GetParameters()[1].ParameterType, + }); - return method.CreateDelegate(eventHandlerType); + var gen = method.GetILGenerator(); + gen.Emit(OpCodes.Ldarg, 0); + gen.Emit(OpCodes.Ldarg, 1); + gen.Emit(OpCodes.Ldstr, cmdName); + if (CommandParameter is null) + { + gen.Emit(OpCodes.Ldnull); } - - static readonly MethodInfo getMethod = typeof(EventBindingExtension).GetMethod("HandlerIntern", new Type[] { typeof(object), typeof(object), typeof(string), typeof(string) }); - - static void Handler(object sender, object args) + else { - HandlerIntern(sender, args, "cmd", null); + gen.Emit(OpCodes.Ldstr, CommandParameter); } - /// - /// Handlers the intern. - /// - /// The sender. - /// The arguments. - /// Name of the command. - /// The command parameter. - public static void HandlerIntern(object sender, object args, string cmdName, string commandParameter) + gen.Emit(OpCodes.Call, getMethod); + gen.Emit(OpCodes.Ret); + + return method.CreateDelegate(eventHandlerType); + } + + static readonly MethodInfo getMethod = typeof(EventBindingExtension).GetMethod("HandlerIntern", new Type[] { typeof(object), typeof(object), typeof(string), typeof(string) }); + +#pragma warning disable IDE0051 // 删除未使用的私有成员 + static void Handler(object sender, object args) +#pragma warning restore IDE0051 // 删除未使用的私有成员 + { + HandlerIntern(sender, args, "cmd", null); + } + /// + /// Handlers the intern. + /// + /// The sender. + /// The arguments. + /// Name of the command. + /// The command parameter. + public static void HandlerIntern(object sender, object args, string cmdName, string? commandParameter) + { + if (sender is FrameworkElement fe) { - var fe = sender as FrameworkElement; - if (fe is not null) + var cmd = GetCommand(fe, cmdName); + object? commandParam = null; + if (!string.IsNullOrWhiteSpace(commandParameter)) + { + commandParam = GetCommandParameter(fe, args, commandParameter!); + } + if ((cmd is not null) && cmd.CanExecute(commandParam)) { - ICommand cmd = GetCommand(fe, cmdName); - object commandParam = null; - if (!string.IsNullOrWhiteSpace(commandParameter)) - { - commandParam = GetCommandParameter(fe, args, commandParameter); - } - if ((cmd is not null) && cmd.CanExecute(commandParam)) - { - cmd.Execute(commandParam); - } + cmd.Execute(commandParam); } } + } - internal static ICommand GetCommand(FrameworkElement target, string cmdName) - { - var vm = FindViewModel(target); - if (vm is null) return null; + internal static ICommand? GetCommand(FrameworkElement target, string cmdName) + { + var vm = FindViewModel(target); + if (vm is null) return null; - var vmType = vm.GetType(); - var cmdProp = vmType.GetProperty(cmdName); - if (cmdProp is not null) - return cmdProp.GetValue(vm) as ICommand; + var vmType = vm.GetType(); + var cmdProp = vmType.GetProperty(cmdName); + if (cmdProp is not null) + { + return cmdProp.GetValue(vm) as ICommand; + } #if DEBUG - throw new Exception("EventBinding path error: '" + cmdName + "' property not found on '" + vmType + "' 'DelegateCommand'"); + throw new Exception("EventBinding path error: '" + cmdName + "' property not found on '" + vmType + "' 'DelegateCommand'"); #endif - return null; - } - internal static object GetCommandParameter(FrameworkElement target, object args, string commandParameter) - { - var classify = commandParameter.Split('.'); - object ret; - switch (classify[0]) - { - case "$e": - ret = args; - break; - case "$this": - ret = classify.Length > 1 ? FollowPropertyPath(target, commandParameter.Replace("$this.", ""), target.GetType()) : target; - break; - default: - ret = commandParameter; - break; - } - return ret; - } +#pragma warning disable CS0162 // 检测到无法访问的代码 + return null; +#pragma warning restore CS0162 // 检测到无法访问的代码 + } - internal static ViewModelBase? FindViewModel(FrameworkElement? target) + internal static object GetCommandParameter(FrameworkElement target, object args, string commandParameter) + { + var classify = commandParameter.Split('.'); + object ret = classify[0] switch { - if (target is null) - return null; + "$e" => args, + "$this" => classify.Length > 1 ? FollowPropertyPath(target, commandParameter.Replace("$this.", ""), target.GetType()) : target, + _ => commandParameter, + }; + return ret; + } - if (target.DataContext is ViewModelBase vm) - return vm; + internal static ViewModelBase? FindViewModel(FrameworkElement? target) + { + if (target is null) return null; - var parent = target.GetParentObject() as FrameworkElement; - return FindViewModel(parent); - } + if (target.DataContext is ViewModelBase vm) return vm; - internal static object FollowPropertyPath(object target, string path, Type valueType = null) - { - if (target is null) throw new ArgumentNullException(nameof(target)); - if (path is null) throw new ArgumentNullException(nameof(path)); + var parent = target.GetParentObject() as FrameworkElement; - Type currentType = valueType ?? target.GetType(); + return FindViewModel(parent); + } - foreach (string propertyName in path.Split('.')) - { - PropertyInfo property = currentType.GetProperty(propertyName); - if (property is null) throw new NullReferenceException("property"); + internal static object FollowPropertyPath(object target, string path, Type? valueType = null) + { + if (target is null) throw new ArgumentNullException(nameof(target)); + if (path is null) throw new ArgumentNullException(nameof(path)); - target = property.GetValue(target); - currentType = property.PropertyType; - } - return target; + Type currentType = valueType ?? target.GetType(); + + foreach (string propertyName in path.Split('.')) + { + PropertyInfo property = currentType.GetProperty(propertyName); + if (property is null) throw new NullReferenceException("property"); + + target = property.GetValue(target); + currentType = property.PropertyType; } + return target; } } diff --git a/src/IFoxCAD.WPF/GlobalUsings.cs b/src/IFoxCAD.WPF/GlobalUsings.cs new file mode 100644 index 0000000..8ab21a7 --- /dev/null +++ b/src/IFoxCAD.WPF/GlobalUsings.cs @@ -0,0 +1,18 @@ +/// 系统引用 +global using System; +global using System.Collections; +global using System.Collections.Generic; +global using System.IO; +global using System.Linq; +global using System.ComponentModel; +global using System.Runtime.CompilerServices; +global using System.Diagnostics; +global using System.Windows; +global using System.Windows.Input; +global using System.Reflection.Emit; +global using System.Windows.Markup; +global using System.Reflection; +global using System.Windows.Media; +global using System.Globalization; +global using System.Windows.Data; + diff --git a/src/IFoxCAD.WPF/IFoxCAD.WPF.csproj b/src/IFoxCAD.WPF/IFoxCAD.WPF.csproj index 297d6ce..9fa74ff 100644 --- a/src/IFoxCAD.WPF/IFoxCAD.WPF.csproj +++ b/src/IFoxCAD.WPF/IFoxCAD.WPF.csproj @@ -1,43 +1,40 @@  - - - preview - enable + + preview + enable - - NET45;NET46;NET47;NET48; + net45 + true + true + true + true + 0.3.0 + xsfhlzh;vicwjb + InspireFunction + WPF的简单MVVM模式开发类库 + InspireFunction + LICENSE + https://gitee.com/inspirefunction/ifoxcad + https://gitee.com/inspirefunction/ifoxcad.git + IFoxCAD;C#;NET;WPF;MVVM + git + 开启可空类型. + - true - true - true - true - 0.1.0 - xsfhlzh;vicwjb - InspireFunction - WPF的简单MVVM模式开发类库 - InspireFunction - LICENSE - https://gitee.com/inspirefunction/ifoxcad - https://gitee.com/inspirefunction/ifoxcad.git - IFoxCAD;C#;NET;WPF;MVVM - git - 基于NFOX类库的重构版本 - + + DEBUG;TRACE + - - DEBUG;TRACE - + + + - - - - - - - True - - - + + + True + + + diff --git a/src/IFoxCAD.WPF/RelayCommand.cs b/src/IFoxCAD.WPF/RelayCommand.cs index cb4f118..72fabca 100644 --- a/src/IFoxCAD.WPF/RelayCommand.cs +++ b/src/IFoxCAD.WPF/RelayCommand.cs @@ -1,205 +1,199 @@ using Microsoft.Xaml.Behaviors; -using System; -using System.Diagnostics; -using System.Windows; -using System.Windows.Input; -namespace IFoxCAD.WPF +namespace IFoxCAD.WPF; + +/// +/// 命令基类 +/// +/// +public class RelayCommand : ICommand { + readonly Func? _canExecute; + readonly Action _execute; /// - /// 命令基类 + /// 初始化 类. /// - /// - public class RelayCommand : ICommand + /// 执行函数 + public RelayCommand(Action execute) : this(execute, null) { - readonly Func _canExecute; - readonly Action _execute; - /// - /// 初始化 类. - /// - /// 执行函数 - public RelayCommand(Action execute):this(execute,null) - { - } - /// - /// 初始化 类. - /// - /// 执行函数委托 - /// 是否可执行函数委托 - /// execute - public RelayCommand(Action execute,Func canExecute) - { - _execute = execute ?? throw new ArgumentNullException(nameof(execute)); - _canExecute = canExecute; - } + } + /// + /// 初始化 类. + /// + /// 执行函数委托 + /// 是否可执行函数委托 + /// execute + public RelayCommand(Action execute, Func? canExecute) + { + _execute = execute ?? throw new ArgumentNullException(nameof(execute)); + _canExecute = canExecute; + } - /// - /// 当出现影响是否应执行该命令的更改时发生。 - /// - public event EventHandler CanExecuteChanged + /// + /// 当出现影响是否应执行该命令的更改时发生。 + /// + public event EventHandler CanExecuteChanged + { + add { - add - { - if (_canExecute is not null) - { - CommandManager.RequerySuggested += value; - } - } - remove + if (_canExecute is not null) { - if (_canExecute is not null) - { - CommandManager.RequerySuggested -= value; - } + CommandManager.RequerySuggested += value; } } - /// - /// 定义确定此命令是否可在其当前状态下执行的方法。 - /// - /// 此命令使用的数据。 如果此命令不需要传递数据,则该对象可以设置为 。 - /// - /// 如果可执行此命令,则为 ;否则为 。 - /// - [DebuggerStepThrough] - public bool CanExecute(object parameter) + remove { - return _canExecute is null ? true : _canExecute(parameter); - } - /// - /// 定义在调用此命令时要调用的方法。 - /// - /// 此命令使用的数据。 如果此命令不需要传递数据,则该对象可以设置为 。 - public void Execute(object parameter) - { - _execute(parameter); + if (_canExecute is not null) + { + CommandManager.RequerySuggested -= value; + } } } + /// + /// 定义确定此命令是否可在其当前状态下执行的方法。 + /// + /// 此命令使用的数据。 如果此命令不需要传递数据,则该对象可以设置为 。 + /// + /// 如果可执行此命令,则为 ;否则为 。 + /// + [DebuggerStepThrough] + public bool CanExecute(object parameter) + { + return _canExecute is null || _canExecute(parameter); + } + /// + /// 定义在调用此命令时要调用的方法。 + /// + /// 此命令使用的数据。 如果此命令不需要传递数据,则该对象可以设置为 。 + public void Execute(object parameter) + { + _execute(parameter); + } +} +/// +/// 命令泛型基类 +/// +/// 事件类型 +/// +public class RelayCommand : ICommand +{ + readonly Func _canExecute; + readonly Action _execute; /// - /// 命令泛型基类 + /// 初始化 类。 /// - /// 事件类型 - /// - public class RelayCommand : ICommand + /// 执行函数 + public RelayCommand(Action execute) : this(execute, (o) => true) { - readonly Func _canExecute; - readonly Action _execute; - /// - /// 初始化 类。 - /// - /// 执行函数 - public RelayCommand(Action execute) : this(execute, (o)=>true) - { - } + } - /// - /// 初始化 类。 - /// - /// 执行函数委托 - /// 是否可执行函数委托 - /// execute - public RelayCommand(Action execute, Func canExecute) - { - _execute = execute ?? throw new ArgumentNullException(nameof(execute)); - _canExecute = canExecute; - } - /// - /// 当出现影响是否应执行该命令的更改时发生。 - /// - public event EventHandler CanExecuteChanged + /// + /// 初始化 类。 + /// + /// 执行函数委托 + /// 是否可执行函数委托 + /// execute + public RelayCommand(Action execute, Func canExecute) + { + _execute = execute ?? throw new ArgumentNullException(nameof(execute)); + _canExecute = canExecute; + } + /// + /// 当出现影响是否应执行该命令的更改时发生。 + /// + public event EventHandler CanExecuteChanged + { + add { - add - { - if (_canExecute is not null) - { - CommandManager.RequerySuggested += value; - } - } - remove + if (_canExecute is not null) { - if (_canExecute is not null) - { - CommandManager.RequerySuggested -= value; - } + CommandManager.RequerySuggested += value; } } - /// - /// 定义确定此命令是否可在其当前状态下执行的方法。 - /// - /// 此命令使用的数据。 如果此命令不需要传递数据,则该对象可以设置为 。 - /// - /// 如果可执行此命令,则为 ;否则为 。 - /// - public bool CanExecute(object parameter) + remove { - if (_canExecute is null) + if (_canExecute is not null) { - return true; + CommandManager.RequerySuggested -= value; } - return _canExecute((T)parameter); } - /// - /// 定义在调用此命令时要调用的方法。 - /// - /// 此命令使用的数据。 如果此命令不需要传递数据,则该对象可以设置为 。 - public void Execute(object parameter) + } + /// + /// 定义确定此命令是否可在其当前状态下执行的方法。 + /// + /// 此命令使用的数据。 如果此命令不需要传递数据,则该对象可以设置为 。 + /// + /// 如果可执行此命令,则为 ;否则为 。 + /// + public bool CanExecute(object parameter) + { + if (_canExecute is null) { - if (_execute is not null && CanExecute(parameter)) - { - _execute((T)parameter); - } + return true; } + return _canExecute((T)parameter); } - /// - /// 事件命令 + /// 定义在调用此命令时要调用的方法。 /// - public class EventCommand : TriggerAction + /// 此命令使用的数据。 如果此命令不需要传递数据,则该对象可以设置为 。 + public void Execute(object parameter) { - /// - /// 执行动作 - /// - /// 要执行的动作参数, 如果动作为提供参数,就设置为null - protected override void Invoke(object parameter) + if (_execute is not null && CanExecute(parameter)) { - if (CommandParameter is not null) - { - parameter = CommandParameter; - } - if (Command is not null) - { - Command.Execute(parameter); - } + _execute((T)parameter); } - /// - /// 事件 - /// - public ICommand Command + } +} + +/// +/// 事件命令 +/// +public class EventCommand : TriggerAction +{ + /// + /// 执行动作 + /// + /// 要执行的动作参数, 如果动作为提供参数,就设置为null + protected override void Invoke(object parameter) + { + if (CommandParameter is not null) { - get { return (ICommand)GetValue(CommandProperty); } - set { SetValue(CommandProperty, value); } + parameter = CommandParameter; } - /// - /// 事件属性 - /// - public static readonly DependencyProperty CommandProperty = - DependencyProperty.Register("Command", typeof(ICommand), typeof(EventCommand), new PropertyMetadata(null)); - - /// - /// 事件参数,如果为空,将自动传入事件的真实参数 - /// - public object CommandParameter + if (Command is not null) { - get { return (object)GetValue(CommandParameterProperty); } - set { SetValue(CommandParameterProperty, value); } + Command.Execute(parameter); } - /// - /// 事件参数属性 - /// - public static readonly DependencyProperty CommandParameterProperty = - DependencyProperty.Register("CommandParameter", typeof(object), typeof(EventCommand), new PropertyMetadata(null)); } + /// + /// 事件 + /// + public ICommand Command + { + get { return (ICommand)GetValue(CommandProperty); } + set { SetValue(CommandProperty, value); } + } + /// + /// 事件属性 + /// + public static readonly DependencyProperty CommandProperty = + DependencyProperty.Register("Command", typeof(ICommand), typeof(EventCommand), new PropertyMetadata(null)); + /// + /// 事件参数,如果为空,将自动传入事件的真实参数 + /// + public object CommandParameter + { + get { return (object)GetValue(CommandParameterProperty); } + set { SetValue(CommandParameterProperty, value); } + } + /// + /// 事件参数属性 + /// + public static readonly DependencyProperty CommandParameterProperty = + DependencyProperty.Register("CommandParameter", typeof(object), typeof(EventCommand), new PropertyMetadata(null)); } diff --git a/src/IFoxCAD.WPF/ViewModelBase.cs b/src/IFoxCAD.WPF/ViewModelBase.cs index 0d0a2dc..c992e81 100644 --- a/src/IFoxCAD.WPF/ViewModelBase.cs +++ b/src/IFoxCAD.WPF/ViewModelBase.cs @@ -1,63 +1,58 @@ -using System; -using System.ComponentModel; -using System.Runtime.CompilerServices; +namespace IFoxCAD.WPF; -namespace IFoxCAD.WPF +/// +/// ViewModel基类 +/// +/// +public class ViewModelBase : INotifyPropertyChanged { /// - /// ViewModel基类 + /// 属性值更改事件。 /// - /// - public class ViewModelBase : INotifyPropertyChanged + public event PropertyChangedEventHandler? PropertyChanged; + /// + /// 属性改变时调用 + /// + /// 属性名 + public void OnPropertyChanged([CallerMemberName] string propertyName = "") + { + + PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName)); + } + /// + /// 设置属性函数,自动通知属性改变事件 + /// + /// 属性类型 + /// 属性 + /// 属性值 + /// 属性名 + /// 成功返回 ,反之 + protected virtual bool Set(ref T storage, T value, [CallerMemberName] string propertyName = "") { - /// - /// 属性值更改事件。 - /// - public event PropertyChangedEventHandler PropertyChanged; - /// - /// 属性改变时调用 - /// - /// 属性名 - public void OnPropertyChanged([CallerMemberName]string propertyName = "") - { - - PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName)); - } - /// - /// 设置属性函数,自动通知属性改变事件 - /// - /// 属性类型 - /// 属性 - /// 属性值 - /// 属性名 - /// 成功返回 ,反之 - protected virtual bool Set(ref T storage, T value, [CallerMemberName]string propertyName = null) - { - if (object.Equals(storage, value)) return false; + if (object.Equals(storage, value)) return false; - storage = value; - this.OnPropertyChanged(propertyName); + storage = value; + this.OnPropertyChanged(propertyName); - return true; - } - /// - /// 创建命令 - /// - /// 要调用的命令函数委托 - /// WPF命令 - protected RelayCommand CreateCommand(Action executeMethod) - { - return CreateCommand(executeMethod, (o) => true); - } - /// - /// 创建命令 - /// - /// 要调用的命令函数委托 - /// 命令是否可以执行的委托 - /// WPF命令 - protected RelayCommand CreateCommand(Action executeMethod, Func canExecuteMethod) - { - return new RelayCommand(executeMethod, canExecuteMethod); - } + return true; + } + /// + /// 创建命令 + /// + /// 要调用的命令函数委托 + /// WPF命令 + protected RelayCommand CreateCommand(Action executeMethod) + { + return CreateCommand(executeMethod, (o) => true); + } + /// + /// 创建命令 + /// + /// 要调用的命令函数委托 + /// 命令是否可以执行的委托 + /// WPF命令 + protected RelayCommand CreateCommand(Action executeMethod, Func canExecuteMethod) + { + return new RelayCommand(executeMethod, canExecuteMethod); } } diff --git a/tests/Test/CmdINI.cs b/tests/Test/CmdINI.cs new file mode 100644 index 0000000..2e37c1d --- /dev/null +++ b/tests/Test/CmdINI.cs @@ -0,0 +1,111 @@ +/// +/// 注册中心(自动执行接口): +/// 用于启动cad后写入启动注册表及反射调用以下特性和接口 +/// netload的工程必须继承虚函数后才能使用特性和接口 +/// 启动cad后的执行顺序为: +/// 1:构造函数 +/// 2:特性..多个 +/// 3:接口..多个 +/// 4:本类的构造函数 +/// +public class CmdINI : AutoRegAssem +{ + public CmdINI() : base(AutoRegConfig.All) + { + var dm = Application.DocumentManager; + var doc = dm.MdiActiveDocument; + var ed = doc.Editor; + ed.WriteMessage($"\n {nameof(CmdINI)}构造函数,开始自动执行\r\n"); + } + + ///如果netload之后用 删除注册表, + ///由于不是也不能卸载dll,再netload是无法执行自动接口的, + ///所以此时会产生无法再注册的问题...因此需要暴露此注册函数(硬来) + [CommandMethod("IFoxAddReg")] + public void IFoxAddReg() + { + base.RegApp(); + + var dm = Application.DocumentManager; + var doc = dm.MdiActiveDocument; + var ed = doc.Editor; + ed.WriteMessage($"\n 加入注册表"); + } + + /// + /// 卸载注册表信息 + /// + [CommandMethod("IFoxRemoveReg")] + public void IFoxRemoveReg() + { + //执行命令的时候会再次执行构造函数(导致初始化两次),但是再次执行就不会了 + base.UnRegApp(); + + var dm = Application.DocumentManager; + var doc = dm.MdiActiveDocument; + var ed = doc.Editor; + ed.WriteMessage($"\n 卸载注册表"); + } +} + + +/* + * 自动执行特性例子: + */ +public class Cmd_IFoxInitialize +{ + [IFoxInitialize] + public void NameCasual() + { + var dm = Application.DocumentManager; + var doc = dm.MdiActiveDocument; + var ed = doc.Editor; + ed.WriteMessage("\n 开始自动执行 可以分开多个类和多个函数 \r\n"); + } + + [IFoxInitialize] + public void NameCasualtest() + { + var dm = Application.DocumentManager; + var doc = dm.MdiActiveDocument; + var ed = doc.Editor; + ed.WriteMessage("\n 开始自动执行 又一次测试 \r\n"); + } + + [IFoxInitialize] + public void Initialize() + { + var dm = Application.DocumentManager; + var doc = dm.MdiActiveDocument; + var ed = doc.Editor; + ed.WriteMessage("\n 开始自动执行 Initialize \r\n"); + } + + [IFoxInitialize(isInitialize: false)] + public void Terminate() + { + //try + //{ + // var dm = Application.DocumentManager; + // var doc = dm.MdiActiveDocument; + // var ed = doc.Editor; //注意此时编辑器已经回收,所以此句没用,并引发错误 + // ed.WriteMessage("\n 结束自动执行 Terminate \r\n"); + //} + //catch (System.Exception) + //{ + //} + } + + //[IFoxInitialize] + //public void Initialize() + //{ + // //文档管理器将比此接口前创建,因此此句会执行 + // Application.DocumentManager.MdiActiveDocument.Editor.WriteMessage("\nload...."); + //} + //[IFoxInitialize(Sequence.First, false)] + //public void Terminate() + //{ + // //文档管理器将比此接口前死亡,因此此句不会执行 + // Application.DocumentManager.MdiActiveDocument?.Editor.WriteMessage("\nunload...."); + //} +} \ No newline at end of file diff --git a/tests/Test/GlobalUsings.cs b/tests/Test/GlobalUsings.cs new file mode 100644 index 0000000..2896cbf --- /dev/null +++ b/tests/Test/GlobalUsings.cs @@ -0,0 +1,30 @@ +/// 系统引用 +global using System; +global using System.Collections; +global using System.Collections.Generic; +global using System.IO; +global using System.Linq; +global using System.Text; +global using System.Reflection; +global using System.Text.RegularExpressions; +global using Microsoft.Win32; +global using System.ComponentModel; +global using System.Runtime.CompilerServices; + +/// autocad 引用 +global using Autodesk.AutoCAD.ApplicationServices; +global using Autodesk.AutoCAD.EditorInput; +global using Autodesk.AutoCAD.Colors; +global using Autodesk.AutoCAD.DatabaseServices; +global using Autodesk.AutoCAD.Geometry; +global using Autodesk.AutoCAD.Runtime; +global using Acgi = Autodesk.AutoCAD.GraphicsInterface; +global using Acap = Autodesk.AutoCAD.ApplicationServices.Application; + +global using Registry = Microsoft.Win32.Registry; +global using RegistryKey = Microsoft.Win32.RegistryKey; + +/// ifoxcad +global using IFoxCAD.Cad; +global using IFoxCAD.WPF; +global using IFoxCAD.Basal; \ No newline at end of file diff --git a/tests/Test/Properties/launchSettings.json b/tests/Test/Properties/launchSettings.json index 1ecc024..4b464c3 100644 --- a/tests/Test/Properties/launchSettings.json +++ b/tests/Test/Properties/launchSettings.json @@ -1,6 +1,6 @@ { "profiles": { - "DBTrans.test": { + "Test": { "commandName": "Executable", "executablePath": "C:\\Program Files\\Autodesk\\AutoCAD 2021\\acad.exe", "commandLineArgs": "/nologo", diff --git a/tests/Test/Test.cs b/tests/Test/Test.cs index 78746f4..bc45757 100644 --- a/tests/Test/Test.cs +++ b/tests/Test/Test.cs @@ -1,193 +1,179 @@ -using Autodesk.AutoCAD.ApplicationServices; -using Autodesk.AutoCAD.Colors; -using Autodesk.AutoCAD.DatabaseServices; -using Autodesk.AutoCAD.EditorInput; -using Autodesk.AutoCAD.Geometry; -using Autodesk.AutoCAD.Runtime; +#pragma warning disable CS0219 // 变量已被赋值,但从未使用过它的值 +namespace Test; -using IFoxCAD.Cad; - -using System; -using System.Collections.Generic; -using System.IO; -using System.Linq; - -using test.wpf; - -namespace test +public class Test { - public class Test + [CommandMethod("dbtest")] + public void Dbtest() { - [CommandMethod("dbtest")] - public void Dbtest() + using var tr = new DBTrans(); + tr.Editor.WriteMessage("\n测试 Editor 属性是否工作!"); + tr.Editor.WriteMessage("\n----------开始测试--------------"); + tr.Editor.WriteMessage("\n测试document属性是否工作"); + if (tr.Document == Getdoc()) { - using var tr = new DBTrans(); - tr.Editor.WriteMessage("\n测试 Editor 属性是否工作!"); - tr.Editor.WriteMessage("\n----------开始测试--------------"); - tr.Editor.WriteMessage("\n测试document属性是否工作"); - if (tr.Document == Getdoc()) - { - tr.Editor.WriteMessage("\ndocument 正常"); - } - tr.Editor.WriteMessage("\n测试database属性是否工作"); - if (tr.Database == Getdb()) - { - tr.Editor.WriteMessage("\ndatabase 正常"); - } - - Line line = new(new Point3d(0, 0, 0), new Point3d(1, 1, 0)); - Circle circle = new(new Point3d(0, 0, 0), Vector3d.ZAxis, 2); - //var lienid = tr.AddEntity(line); - //var cirid = tr.AddEntity(circle); - //var linent = tr.GetObject(lienid); - //var lineent = tr.GetObject(cirid); - //var linee = tr.GetObject(cirid); //经测试,类型不匹配,返回null - //var dd = tr.GetObject(lienid); - //List ds = new() { linee, dd }; + tr.Editor.WriteMessage("\ndocument 正常"); } - - //add entity test - [CommandMethod("addent")] - public void Addent() + tr.Editor.WriteMessage("\n测试database属性是否工作"); + if (tr.Database == Getdb()) { - using var tr = new DBTrans(); - Line line = new(new Point3d(0, 0, 0), new Point3d(1, 1, 0)); - tr.CurrentSpace.AddEntity(line); - Line line1 = new(new Point3d(10, 10, 0), new Point3d(41, 1, 0)); - tr.ModelSpace.AddEntity(line1); - Line line2 = new(new Point3d(-10, 10, 0), new Point3d(41, 1, 0)); - tr.PaperSpace.AddEntity(line2); + tr.Editor.WriteMessage("\ndatabase 正常"); } - [CommandMethod("drawarc")] - public void drawarc() - { - using var tr = new DBTrans(); - Arc arc1 = EntityEx.CreateArcSCE(new Point3d(2, 0, 0), new Point3d(0, 0, 0), new Point3d(0, 2, 0));//起点,圆心,终点 - Arc arc2 = EntityEx.CreateArc(new Point3d(4, 0, 0), new Point3d(0, 0, 0), Math.PI / 2); //起点,圆心,弧度 - Arc arc3 = EntityEx.CreateArc(new Point3d(1, 0, 0), new Point3d(0, 0, 0), new Point3d(0, 1, 0)); //起点,圆上一点,终点 - tr.CurrentSpace.AddEntity(arc1, arc2, arc3); - tr.CurrentSpace.AddArc(new Point3d(0, 0, 0), new Point3d(1, 1, 0), new Point3d(2, 0, 0));//起点,圆上一点,终点 - } + Line line = new(new Point3d(0, 0, 0), new Point3d(1, 1, 0)); + Circle circle = new(new Point3d(0, 0, 0), Vector3d.ZAxis, 2); + //var lienid = tr.AddEntity(line); + //var cirid = tr.AddEntity(circle); + //var linent = tr.GetObject(lienid); + //var lineent = tr.GetObject(cirid); + //var linee = tr.GetObject(cirid); //经测试,类型不匹配,返回null + //var dd = tr.GetObject(lienid); + //List ds = new() { linee, dd }; + //tr.CurrentSpace.AddEntity(line,tr); + } - [CommandMethod("drawcircle")] - public void draCircle() + //add entity test + [CommandMethod("addent")] + public void Addent() + { + using var tr = new DBTrans(); + Line line = new(new Point3d(0, 0, 0), new Point3d(1, 1, 0)); + tr.CurrentSpace.AddEntity(line); + Line line1 = new(new Point3d(10, 10, 0), new Point3d(41, 1, 0)); + tr.ModelSpace.AddEntity(line1); + Line line2 = new(new Point3d(-10, 10, 0), new Point3d(41, 1, 0)); + tr.PaperSpace.AddEntity(line2); + } + + [CommandMethod("drawarc")] + public void Drawarc() + { + using var tr = new DBTrans(); + Arc arc1 = EntityEx.CreateArcSCE(new Point3d(2, 0, 0), new Point3d(0, 0, 0), new Point3d(0, 2, 0));//起点,圆心,终点 + Arc arc2 = EntityEx.CreateArc(new Point3d(4, 0, 0), new Point3d(0, 0, 0), Math.PI / 2); //起点,圆心,弧度 + Arc arc3 = EntityEx.CreateArc(new Point3d(1, 0, 0), new Point3d(0, 0, 0), new Point3d(0, 1, 0)); //起点,圆上一点,终点 + tr.CurrentSpace.AddEntity(arc1, arc2, arc3); + tr.CurrentSpace.AddArc(new Point3d(0, 0, 0), new Point3d(1, 1, 0), new Point3d(2, 0, 0));//起点,圆上一点,终点 + } + + [CommandMethod("drawcircle")] + public void DraCircle() + { + using var tr = new DBTrans(); + Circle circle1 = EntityEx.CreateCircle(new Point3d(0, 0, 0), new Point3d(1, 0, 0)); //起点,终点 + Circle circle2 = EntityEx.CreateCircle(new Point3d(-2, 0, 0), new Point3d(2, 0, 0), new Point3d(0, 2, 0));//三点画圆,成功 + Circle circle3 = EntityEx.CreateCircle(new Point3d(-2, 0, 0), new Point3d(0, 0, 0), new Point3d(2, 0, 0));//起点,圆心,终点,失败 + tr.CurrentSpace.AddEntity(circle1, circle2); + if (circle3 is not null) { - using var tr = new DBTrans(); - Circle circle1 = EntityEx.CreateCircle(new Point3d(0, 0, 0), new Point3d(1, 0, 0)); //起点,终点 - Circle circle2 = EntityEx.CreateCircle(new Point3d(-2, 0, 0), new Point3d(2, 0, 0), new Point3d(0, 2, 0));//三点画圆,成功 - Circle circle3 = EntityEx.CreateCircle(new Point3d(-2, 0, 0), new Point3d(0, 0, 0), new Point3d(2, 0, 0));//起点,圆心,终点,失败 - tr.CurrentSpace.AddEntity(circle1, circle2); - if (circle3 is not null) - tr.CurrentSpace.AddEntity(circle3); - else - { - tr.Editor.WriteMessage("三点画圆失败"); - return; - } tr.CurrentSpace.AddEntity(circle3); - tr.CurrentSpace.AddCircle(new Point3d(0, 0, 0), new Point3d(1, 1, 0), new Point3d(2, 0, 0));//三点画圆,成功 - tr.CurrentSpace.AddCircle(new Point3d(0, 0, 0), new Point3d(1, 1, 0), new Point3d(2, 2, 0));//起点,圆上一点,终点(共线) } - - [CommandMethod("layertest")] - public void Layertest() + else { - using var tr = new DBTrans(); - tr.LayerTable.Add("1"); - tr.LayerTable.Add("2", lt => - { - lt.Color = Color.FromColorIndex(ColorMethod.ByColor, 1); - lt.LineWeight = LineWeight.LineWeight030; - - }); - tr.LayerTable.Remove("3"); - tr.LayerTable.Delete("0"); - tr.LayerTable.Change("4", lt => - { - lt.Color = Color.FromColorIndex(ColorMethod.ByColor, 2); - }); + tr.Editor.WriteMessage("三点画圆失败"); } + tr.CurrentSpace.AddEntity(circle3); + tr.CurrentSpace.AddCircle(new Point3d(0, 0, 0), new Point3d(1, 1, 0), new Point3d(2, 0, 0));//三点画圆,成功 + tr.CurrentSpace.AddCircle(new Point3d(0, 0, 0), new Point3d(1, 1, 0), new Point3d(2, 2, 0));//起点,圆上一点,终点(共线) + } + [CommandMethod("layertest")] + public void Layertest() + { + using var tr = new DBTrans(); + tr.LayerTable.Add("1"); + tr.LayerTable.Add("2", lt => { + lt.Color = Color.FromColorIndex(ColorMethod.ByColor, 1); + lt.LineWeight = LineWeight.LineWeight030; + + }); + tr.LayerTable.Remove("3"); + tr.LayerTable.Delete("0"); + tr.LayerTable.Change("4", lt => { + lt.Color = Color.FromColorIndex(ColorMethod.ByColor, 2); + }); + } - //添加图层 - [CommandMethod("layerAdd1")] - public void Layertest1() - { - using var tr = new DBTrans(); - tr.LayerTable.Add("test1", Color.FromColorIndex(ColorMethod.ByColor, 1)); - } - //添加图层 - [CommandMethod("layerAdd2")] - public void Layertest2() - { - using var tr = new DBTrans(); - tr.LayerTable.Add("test2", 2); - //tr.LayerTable["3"] = new LayerTableRecord(); - } - //删除图层 - [CommandMethod("layerdel")] - public void LayerDel() - { - using var tr = new DBTrans(); - Env.Editor.WriteMessage(tr.LayerTable.Delete("0").ToString()); //删除图层 0 - Env.Editor.WriteMessage(tr.LayerTable.Delete("Defpoints").ToString());//删除图层 Defpoints - Env.Editor.WriteMessage(tr.LayerTable.Delete("1").ToString()); //删除不存在的图层 1 - Env.Editor.WriteMessage(tr.LayerTable.Delete("2").ToString()); //删除有图元的图层 2 - Env.Editor.WriteMessage(tr.LayerTable.Delete("3").ToString()); //删除图层 3 + //添加图层 + [CommandMethod("layerAdd1")] + public void Layertest1() + { + using var tr = new DBTrans(); + tr.LayerTable.Add("test1", Color.FromColorIndex(ColorMethod.ByColor, 1)); + } - tr.LayerTable.Remove("2"); //测试是否能强制删除 - } + //添加图层 + [CommandMethod("layerAdd2")] + public void Layertest2() + { + using var tr = new DBTrans(); + tr.LayerTable.Add("test2", 2); + //tr.LayerTable["3"] = new LayerTableRecord(); + } + //删除图层 + [CommandMethod("layerdel")] + public void LayerDel() + { + using var tr = new DBTrans(); + Env.Editor.WriteMessage(tr.LayerTable.Delete("0").ToString()); //删除图层 0 + Env.Editor.WriteMessage(tr.LayerTable.Delete("Defpoints").ToString());//删除图层 Defpoints + Env.Editor.WriteMessage(tr.LayerTable.Delete("1").ToString()); //删除不存在的图层 1 + Env.Editor.WriteMessage(tr.LayerTable.Delete("2").ToString()); //删除有图元的图层 2 + Env.Editor.WriteMessage(tr.LayerTable.Delete("3").ToString()); //删除图层 3 + + tr.LayerTable.Remove("2"); //测试是否能强制删除 + } - //添加直线 - [CommandMethod("linedemo1")] - public void AddLine1() - { - using var tr = new DBTrans(); - // tr.ModelSpace.AddEnt(line); - // tr.ModelSpace.AddEnts(line,circle); - - // tr.PaperSpace.AddEnt(line); - // tr.PaperSpace.AddEnts(line,circle); - - // tr.addent(btr,line); - // tr.addents(btr,line,circle); - - - // tr.BlockTable.Add(new BlockTableRecord(), line => - // { - // line. - // }); - Line line1 = new(new Point3d(0, 0, 0), new Point3d(1, 1, 0)); - Line line2 = new(new Point3d(0, 0, 0), new Point3d(1, 1, 0)); - Line line3 = new(new Point3d(1, 1, 0), new Point3d(3, 3, 0)); - Circle circle = new Circle(new Point3d(0, 0, 0), Vector3d.ZAxis, 10); - tr.CurrentSpace.AddEntity(line1); - tr.CurrentSpace.AddEntity(line2, line3, circle); - } + //添加直线 + [CommandMethod("linedemo1")] + public void AddLine1() + { + using var tr = new DBTrans(); + // tr.ModelSpace.AddEnt(line); + // tr.ModelSpace.AddEnts(line,circle); + + // tr.PaperSpace.AddEnt(line); + // tr.PaperSpace.AddEnts(line,circle); + + // tr.addent(btr,line); + // tr.addents(btr,line,circle); + + + // tr.BlockTable.Add(new BlockTableRecord(), line => + // { + // line. + // }); + Line line1 = new(new Point3d(0, 0, 0), new Point3d(1, 1, 0)); + Line line2 = new(new Point3d(0, 0, 0), new Point3d(1, 1, 0)); + Line line3 = new(new Point3d(1, 1, 0), new Point3d(3, 3, 0)); + Circle circle = new(new Point3d(0, 0, 0), Vector3d.ZAxis, 10); + tr.CurrentSpace.AddEntity(line1); + tr.CurrentSpace.AddEntity(line2, line3, circle); + } - //增加多段线1 - [CommandMethod("Pldemo1")] - public void AddPolyline1() - { - using var tr = new DBTrans(); - Polyline pl = new Polyline(); - pl.AddVertexAt(0, new Point2d(0, 0), 0, 0, 0); - pl.AddVertexAt(1, new Point2d(10, 10), 0, 0, 0); - pl.AddVertexAt(2, new Point2d(20, 20), 0, 0, 0); - pl.AddVertexAt(3, new Point2d(30, 30), 0, 0, 0); - pl.AddVertexAt(4, new Point2d(40, 40), 0, 0, 0); - pl.Closed = true; - pl.Color = Color.FromColorIndex(ColorMethod.ByColor, 6); - tr.CurrentSpace.AddEntity(pl); - } + //增加多段线1 + [CommandMethod("Pldemo1")] + public void AddPolyline1() + { + using var tr = new DBTrans(); + Polyline pl = new(); + pl.SetDatabaseDefaults(); + pl.AddVertexAt(0, new Point2d(0, 0), 0, 0, 0); + pl.AddVertexAt(1, new Point2d(10, 10), 0, 0, 0); + pl.AddVertexAt(2, new Point2d(20, 20), 0, 0, 0); + pl.AddVertexAt(3, new Point2d(30, 30), 0, 0, 0); + pl.AddVertexAt(4, new Point2d(40, 40), 0, 0, 0); + pl.Closed = true; + pl.Color = Color.FromColorIndex(ColorMethod.ByColor, 6); + tr.CurrentSpace.AddEntity(pl); + } - //增加多段线2 - [CommandMethod("pldemo2")] - public void Addpl2() - { - var pts = new List<(Point3d, double, double, double)> + //增加多段线2 + [CommandMethod("pldemo2")] + public void Addpl2() + { + var pts = new List<(Point3d, double, double, double)> { (new Point3d(0,0,0),0,0,0), (new Point3d(10,0,0),0,0,0), @@ -195,154 +181,28 @@ public void Addpl2() (new Point3d(0,10,0),0,0,0), (new Point3d(5,5,0),0,0,0) }; - using var tr = new DBTrans(); - tr.CurrentSpace.AddPline(pts); - } - - //块定义 - [CommandMethod("blockdef")] - public void BlockDef() - { - using var tr = new DBTrans(); - //var line = new Line(new Point3d(0, 0, 0), new Point3d(1, 1, 0)); - tr.BlockTable.Add("test", - btr => - { - btr.Origin = new Point3d(0, 0, 0); - }, - () => //图元 - { - return new List { new Line(new Point3d(0, 0, 0), new Point3d(1, 1, 0)) }; - }, - () => //属性定义 - { - var id1 = new AttributeDefinition() { Position = new Point3d(0, 0, 0), Tag = "start", Height = 0.2 }; - var id2 = new AttributeDefinition() { Position = new Point3d(1, 1, 0), Tag = "end", Height = 0.2 }; - return new List { id1, id2 }; - } - ); - //ObjectId objectId = tr.BlockTable.Add("a");//新建块 - //objectId.GetObject().AddEntity();//测试添加空实体 - } - //修改块定义 - [CommandMethod("blockdefchange")] - public void BlockDefChange() - { - using var tr = new DBTrans(); - //var line = new Line(new Point3d(0, 0, 0), new Point3d(1, 1, 0)); - tr.BlockTable.Change("test", btr => - { - btr.Origin = new Point3d(5, 5, 0); - btr.AddEntity(new Circle(new Point3d(0, 0, 0), Vector3d.ZAxis, 2)); - btr.GetEntities() - .ToList() - .ForEach(e => e.Flush()); //刷新块显示 - - }); - tr.Editor.Regen(); - } - - [CommandMethod("insertblockdef")] - public void InsertBlockDef() - { - using var tr = new DBTrans(); - var line1 = new Line(new Point3d(0, 0, 0), new Point3d(1, 1, 0)); - var line2 = new Line(new Point3d(0, 0, 0), new Point3d(-1, 1, 0)); - var att1 = new AttributeDefinition() { Position = new Point3d(10, 10, 0), Tag = "tagTest1", Height = 1, TextString = "valueTest1" }; - var att2 = new AttributeDefinition() { Position = new Point3d(10, 12, 0), Tag = "tagTest2", Height = 1, TextString = "valueTest2" }; - tr.BlockTable.Add("test1", line1, line2, att1, att2); - - - var ents = new List(); - var line5 = new Line(new Point3d(0, 0, 0), new Point3d(1, 1, 0)); - var line6 = new Line(new Point3d(0, 0, 0), new Point3d(-1, 1, 0)); - ents.Add(line5); - ents.Add(line6); - tr.BlockTable.Add("test44", ents); - - - var line3 = new Line(new Point3d(5, 5, 0), new Point3d(6, 6, 0)); - var line4 = new Line(new Point3d(5, 5, 0), new Point3d(-6, 6, 0)); - var att3 = new AttributeDefinition() { Position = new Point3d(10, 14, 0), Tag = "tagTest3", Height = 1, TextString = "valueTest3" }; - var att4 = new AttributeDefinition() { Position = new Point3d(10, 16, 0), Tag = "tagTest4", Height = 1, TextString = "valueTest4" }; - tr.BlockTable.Add("test2", new List { line3, line4 }, new List { att3, att4 }); - //tr.CurrentSpace.InsertBlock(new Point3d(4, 4, 0), "test1"); // 测试默认 - //tr.CurrentSpace.InsertBlock(new Point3d(4, 4, 0), "test2"); - //tr.CurrentSpace.InsertBlock(new Point3d(4, 4, 0), "test3"); //测试插入不存在的块定义 - //tr.CurrentSpace.InsertBlock(new Point3d(0, 0, 0), "test1", new Scale3d(2)); // 测试放大2倍 - //tr.CurrentSpace.InsertBlock(new Point3d(4, 4, 0), "test1", new Scale3d(2), Math.PI / 4); // 测试放大2倍,旋转45度 - - var def1 = new Dictionary - { - { "tagTest1", "1" }, - { "tagTest2", "2" } - }; - tr.CurrentSpace.InsertBlock(new Point3d(0, 0, 0), "test1", atts: def1); - var def2 = new Dictionary - { - { "tagTest3", "1" }, - { "tagTest4", "" } - }; - tr.CurrentSpace.InsertBlock(new Point3d(10, 10, 0), "test2", atts: def2); - tr.CurrentSpace.InsertBlock(new Point3d(-10, 0, 0), "test44"); - } - - [CommandMethod("testblocknullbug")] - public void TestBlockNullBug() - { - using var tr = new DBTrans(); + using var tr = new DBTrans(); + tr.CurrentSpace.AddPline(pts); + } - var ents = new List(); - var line5 = new Line(new Point3d(0, 0, 0), new Point3d(1, 1, 0)); - var line6 = new Line(new Point3d(0, 0, 0), new Point3d(-1, 1, 0)); - ents.Add(line5); - ents.Add(line6); - tr.BlockTable.Add("test44", ents); - tr.CurrentSpace.InsertBlock(new Point3d(0, 0, 0), "test44"); - } - [CommandMethod("testclip")] - public void TestClipBlock() - { - using var tr = new DBTrans(); - tr.BlockTable.Add("test1", - btr => - { - btr.Origin = new Point3d(0, 0, 0); - btr.AddEntity(new Line(new Point3d(0, 0, 0), new Point3d(10, 10, 0)), - new Line(new Point3d(10, 10, 0), new Point3d(10, 0, 0)) - ); - } - ); - //tr.BlockTable.Add("hah"); - var id = tr.CurrentSpace.InsertBlock(new Point3d(0, 0, 0), "test1"); - var bref = tr.GetObject(id); - var pts = new List { new Point3d(3, 3, 0), new Point3d(7, 3, 0), new Point3d(7, 7, 0), new Point3d(3, 7, 0) }; - bref.ClipBlockRef(pts); - var id1 = tr.CurrentSpace.InsertBlock(new Point3d(20, 20, 0), "test1"); - var bref1 = tr.GetObject(id); - - bref1.ClipBlockRef(new Point3d(13, 13, 0), new Point3d(17, 17, 0)); - } + // 测试扩展数据 + [CommandMethod("addxdata")] + public void AddXdata() + { + using var tr = new DBTrans(); + var appname = "myapp"; + tr.RegAppTable.Add(appname); // add函数会默认的在存在这个名字的时候返回这个名字的regapp的id,不存在就新建 + tr.RegAppTable.Add("myapp2"); - // 测试扩展数据 - [CommandMethod("addxdata")] - public void AddXdata() + var line = new Line(new Point3d(0, 0, 0), new Point3d(1, 1, 0)) { - using var tr = new DBTrans(); - var appname = "myapp"; - - tr.RegAppTable.Add(appname); // add函数会默认的在存在这个名字的时候返回这个名字的regapp的id,不存在就新建 - tr.RegAppTable.Add("myapp2"); - - var line = new Line(new Point3d(0, 0, 0), new Point3d(1, 1, 0)) - { - XData = new XDataList() + XData = new XDataList() { { DxfCode.ExtendedDataRegAppName, appname }, //可以用dxfcode和int表示组码 { DxfCode.ExtendedDataAsciiString, "hahhahah" }, @@ -351,278 +211,150 @@ public void AddXdata() { DxfCode.ExtendedDataAsciiString, "hahhahah" }, {1070, 12 } } - }; - - tr.CurrentSpace.AddEntity(line); - } - - [CommandMethod("getxdata")] - public void GetXdata() - { - var doc = Application.DocumentManager.MdiActiveDocument; - var ed = doc.Editor; - - var res = ed.GetEntity("\n select the entity:"); - if (res.Status == PromptStatus.OK) - { - using var tr = new DBTrans(); - var data = tr.GetObject(res.ObjectId).XData; - ed.WriteMessage(data.ToString()); - } - } - - [CommandMethod("changexdata")] - public void Changexdata() - { - var doc = Application.DocumentManager.MdiActiveDocument; - var ed = doc.Editor; - var appname = "myapp"; - var res = ed.GetEntity("\n select the entity:"); - if (res.Status == PromptStatus.OK) - { - using var tr = new DBTrans(); - var data = tr.GetObject(res.ObjectId); - data.ChangeXData(appname, DxfCode.ExtendedDataAsciiString, "change"); + }; - ed.WriteMessage(data.XData.ToString()); - } - } - [CommandMethod("removexdata")] - public void Removexdata() - { - var doc = Application.DocumentManager.MdiActiveDocument; - var ed = doc.Editor; - var appname = "myapp"; - var res = ed.GetEntity("\n select the entity:"); - if (res.Status == PromptStatus.OK) - { - using var tr = new DBTrans(); - var data = tr.GetObject(res.ObjectId); - data.RemoveXData(appname, DxfCode.ExtendedDataAsciiString); + tr.CurrentSpace.AddEntity(line); + } - ed.WriteMessage(data.XData.ToString()); - } - } + [CommandMethod("getxdata")] + public void GetXdata() + { + var doc = Application.DocumentManager.MdiActiveDocument; + var ed = doc.Editor; + using var tr = new DBTrans(); + tr.RegAppTable.ForEach(id => + id.GetObject().Name.Print()); + tr.RegAppTable.GetRecords().ForEach(rec => rec.Name.Print()); + tr.RegAppTable.GetRecordNames().ForEach(name => name.Print()); + tr.RegAppTable.ForEach(re => re.Name.Print()); + + //var res = ed.GetEntity("\n select the entity:"); + //if (res.Status == PromptStatus.OK) + //{ + // using var tr = new DBTrans(); + // tr.RegAppTable.ForEach(id => id.GetObject().Print()); + // var data = tr.GetObject(res.ObjectId).XData; + // ed.WriteMessage(data.ToString()); + //} + } - [CommandMethod("PrintLayerName")] - public void PrintLayerName() + [CommandMethod("changexdata")] + public void Changexdata() + { + var doc = Application.DocumentManager.MdiActiveDocument; + var ed = doc.Editor; + var appname = "myapp"; + var res = ed.GetEntity("\n select the entity:"); + if (res.Status == PromptStatus.OK) { using var tr = new DBTrans(); - foreach (var layerRecord in tr.LayerTable.GetRecords()) - { - tr.Editor.WriteMessage(layerRecord.Name); - } - - } - + var data = tr.GetObject(res.ObjectId); + data.ChangeXData(appname, DxfCode.ExtendedDataAsciiString, "change"); - [CommandMethod("testwpf")] - public void TestWPf() - { - - var test = new TestView(); - Application.ShowModalWindow(test); + ed.WriteMessage(data.XData.ToString()); } - - [CommandMethod("testpt")] - public void TestPt() + } + [CommandMethod("removexdata")] + public void Removexdata() + { + var doc = Application.DocumentManager.MdiActiveDocument; + var ed = doc.Editor; + var appname = "myapp"; + var res = ed.GetEntity("\n select the entity:"); + if (res.Status == PromptStatus.OK) { - //var pt = Env.Editor.GetPoint("pick pt:").Value; - //var pl = Env.Editor.GetEntity("pick pl").ObjectId; - var tr1 = HostApplicationServices.WorkingDatabase.TransactionManager.TopTransaction; - using var tr2 = new DBTrans(); - var tr3 = HostApplicationServices.WorkingDatabase.TransactionManager.TopTransaction; - var tr6 = Application.DocumentManager.MdiActiveDocument.TransactionManager.TopTransaction; - Env.Print(tr2.Transaction == tr3); - Env.Print(tr3 == tr6); - using var tr4 = new DBTrans(); - var tr5 = HostApplicationServices.WorkingDatabase.TransactionManager.TopTransaction; - var tr7 = Application.DocumentManager.MdiActiveDocument.TransactionManager.TopTransaction; - Env.Print(tr4.Transaction == tr5); - Env.Print(tr5 == tr7); - var trm = HostApplicationServices.WorkingDatabase.TransactionManager; - //var ptt = tr.GetObject(pl).GetClosestPointTo(pt,false); - //var pt1 = new Point3d(0, 0.00000000000001, 0); - //var pt2 = new Point3d(0, 0.00001, 0); - //Env.Print(Tolerance.Global.EqualPoint); - //Env.Print(pt1.IsEqualTo(pt2).ToString()); - //Env.Print(pt1.IsEqualTo(pt2,new Tolerance(0.0,1e-6)).ToString()); - //Env.Print((pt1 == pt2).ToString()); - //Env.Print((pt1 != pt2).ToString()); - - + using var tr = new DBTrans(); + var data = tr.GetObject(res.ObjectId); + data.RemoveXData(appname, DxfCode.ExtendedDataAsciiString); + ed.WriteMessage(data.XData.ToString()); } + } - - public Database Getdb() - { - var db = Application.DocumentManager.MdiActiveDocument.Database; - return db; - } - - - public Document Getdoc() + [CommandMethod("PrintLayerName")] + public void PrintLayerName() + { + using var tr = new DBTrans(); + foreach (var layerRecord in tr.LayerTable.GetRecords()) { - var doc = Application.DocumentManager.MdiActiveDocument; - return doc; + tr.Editor.WriteMessage(layerRecord.Name); } - //public override void Initialize() - //{ - // Application.DocumentManager.MdiActiveDocument.Editor.WriteMessage("\nload...."); - //} - - //public override void Terminate() - //{ - // Application.DocumentManager.MdiActiveDocument.Editor.WriteMessage("\nunload...."); - //} } - public class BlockImportClass + [CommandMethod("testrec")] + public void TestRec() { + Point2d p1 = new(10000.2, 100000.5); + Point2d p2 = new(15000.9, 100000.5); + Point2d p3 = new(15000.9, 105000.7); + Point2d p4 = new(10000.2, 105000.7); - [CommandMethod("CBLL")] - public void cbll() - { - string filename = @"C:\Users\vic\Desktop\Drawing1.dwg"; - using var tr = new DBTrans(); - using var tr1 = new DBTrans(filename); - //tr.BlockTable.GetBlockFrom(filename, true); - string blkdefname = SymbolUtilityServices.RepairSymbolName(SymbolUtilityServices.GetSymbolNameFromPathName(filename, "dwg"), false); - tr.Database.Insert(blkdefname, tr1.Database, false); //插入了块定义,未插入块参照 + var p12 = p2 - p1; + var p23 = p3 - p2; + var p34 = p4 - p3; + var p41 = p1 - p4; + var p13 = p3 - p1; + var p24 = p4 - p2; - } + const double pi90 = Math.PI / 2; - [CommandMethod("CBL")] - public void CombineBlocksIntoLibrary() - { - Document doc = - Application.DocumentManager.MdiActiveDocument; - Editor ed = doc.Editor; - Database destDb = doc.Database; + Tools.TestTimes(1000000, "对角线", () => { + var result = false; + if (Math.Abs(p13.Length - p24.Length) <= 1e8) + { + result = p41.IsParallelTo(p12); + } - // Get name of folder from which to load and import blocks + }); - PromptResult pr = - ed.GetString("\nEnter the folder of source drawings: "); + Tools.TestTimes(1000000, "三次点乘", () => { + var result = false; - if (pr.Status != PromptStatus.OK) - return; - string pathName = pr.StringResult; + if (Math.Abs(p12.DotProduct(p23)) < 1e8 && + Math.Abs(p23.DotProduct(p34)) < 1e8 && + Math.Abs(p34.DotProduct(p41)) < 1e8) + { + result = true; + } - // Check the folder exists + }); - if (!Directory.Exists(pathName)) + Tools.TestTimes(1000000, "三次垂直", () => { + var result = false; + if (p12.IsParallelTo(p23) && + p23.IsParallelTo(p34) && + p34.IsParallelTo(p41)) { - ed.WriteMessage( - "\nDirectory does not exist: {0}", pathName - ); - return; + result = true; } - // Get the names of our DWG files in that folder + }); - string[] fileNames = Directory.GetFiles(pathName, "*.dwg"); - // A counter for the files we've imported + } - int imported = 0, failed = 0; - // For each file in our list - foreach (string fileName in fileNames) - { - // Double-check we have a DWG file (probably unnecessary) - if (fileName.EndsWith( - ".dwg", - StringComparison.InvariantCultureIgnoreCase - ) - ) - { - // Catch exceptions at the file level to allow skipping - - try - { - // Suggestion from Thorsten Meinecke... - - string destName = - SymbolUtilityServices.GetSymbolNameFromPathName( - fileName, "dwg" - ); - - // And from Dan Glassman... - - destName = - SymbolUtilityServices.RepairSymbolName( - destName, false - ); - - // Create a source database to load the DWG into - - using (Database db = new Database(false, true)) - { - // Read the DWG into our side database - - db.ReadDwgFile(fileName, FileShare.Read, true, ""); - bool isAnno = db.AnnotativeDwg; - - // Insert it into the destination database as - // a named block definition - - ObjectId btrId = destDb.Insert( - destName, - db, - false - ); - - if (isAnno) - { - // If an annotative block, open the resultant BTR - // and set its annotative definition status - - Transaction tr = - destDb.TransactionManager.StartTransaction(); - using (tr) - { - BlockTableRecord btr = - (BlockTableRecord)tr.GetObject( - btrId, - OpenMode.ForWrite - ); - btr.Annotative = AnnotativeStates.True; - tr.Commit(); - } - } - - // Print message and increment imported block counter - - ed.WriteMessage("\nImported from \"{0}\".", fileName); - imported++; - } - } - catch (System.Exception ex) - { - ed.WriteMessage( - "\nProblem importing \"{0}\": {1} - file skipped.", - fileName, ex.Message - ); - failed++; - } - } - } - ed.WriteMessage( - "\nImported block definitions from {0} files{1} in " + - "\"{2}\" into the current drawing.", - imported, - failed > 0 ? " (" + failed + " failed)" : "", - pathName - ); - } + + public Database Getdb() + { + var db = Application.DocumentManager.MdiActiveDocument.Database; + return db; } + + public Document Getdoc() + { + var doc = Application.DocumentManager.MdiActiveDocument; + return doc; + } } + + + +#pragma warning restore CS0219 // 变量已被赋值,但从未使用过它的值 diff --git a/tests/Test/Test.csproj b/tests/Test/Test.csproj index fa8ed34..8e3469a 100644 --- a/tests/Test/Test.csproj +++ b/tests/Test/Test.csproj @@ -1,32 +1,22 @@  - preview - enable + - - 1.0.0.* - 1.0.0.0 - False - git - - - NET45; + net45 true true + x64 - - - - - - + + 1701;1702;CS1685 + - - - + + 1701;1702;CS1685 + @@ -34,4 +24,5 @@ + diff --git a/tests/Test/TestAOP.cs b/tests/Test/TestAOP.cs new file mode 100644 index 0000000..b5b5c92 --- /dev/null +++ b/tests/Test/TestAOP.cs @@ -0,0 +1,66 @@ +//被注入的函数将不能使用断点, +//因此用户要充分了解才能使用 +#if false +/* + * 类库用户想侵入的命名空间是用户的, + * 所以需要用户手动进行AOP.Run(), + * 默认情况不侵入用户的命令,必须用户手动启用此功能; + * 启动执行策略之后,侵入命名空间下的命令, + * 此时有拒绝特性的策略保证括免,因为用户肯定是想少写一个事务注入的特性; + */ +public class AutoAOP +{ + [IFoxInitialize] + public void Initialize() + { + AOP.Run(nameof(Test)); + } +} + +namespace Test +{ + /* + * 天秀的事务注入,让你告别事务处理 + * https://www.cnblogs.com/JJBox/p/16157578.html + */ + public class AopTestClass + { + //类不拒绝,这里拒绝 + [IFoxRefuseInjectionTransaction] + [CommandMethod("IFoxRefuseInjectionTransaction")] + public void IFoxRefuseInjectionTransaction() + { + } + + //不拒绝 + [CommandMethod("InjectionTransaction")] + public void InjectionTransaction() + { + //怎么用事务呢? + //直接用 DBTrans.Top + var dBTrans = new DBTrans(); + dBTrans.Commit(); + } + } + + //拒绝注入事务,写类上,则方法全都拒绝 + [IFoxRefuseInjectionTransaction] + public class AopTestClassRefuseInjection + { + //此时这个也是拒绝的..这里加特性只是无所谓 + [IFoxRefuseInjectionTransaction] + [CommandMethod("IFoxRefuseInjectionTransaction2")] + public void IFoxRefuseInjectionTransaction2() + { + //拒绝注入就要自己开事务,通常用在循环提交事务上面. + //另见 报错0x02 https://www.cnblogs.com/JJBox/p/10798940.html + using var tr = new DBTrans(); + } + + [CommandMethod("InjectionTransaction2")] + public void InjectionTransaction2() + { + } + } +} +#endif \ No newline at end of file diff --git a/tests/Test/TestCurve.cs b/tests/Test/TestCurve.cs new file mode 100644 index 0000000..4d5cd96 --- /dev/null +++ b/tests/Test/TestCurve.cs @@ -0,0 +1,158 @@ +using System.Security.Policy; + +namespace Test +{ + public class TestGraph + { + [CommandMethod("testpointindict")] + public void TestPointInDict() + { + var pt1 = new Point3d(0.0255, 0.452, 0); + var pt2 = new Point3d(0.0255001, 0.452003, 0); + var pt3 = new Point3d(0.0255002, 0.4520001, 0); + var pt4 = new Point3d(0.0255450, 0.45287893, 0); + var pt5 = new Point3d(0.02554935, 0.452092375, 0); + var dict = new Dictionary + { + { pt1, 1 }, + { pt2, 2 }, + { pt3, 3 }, + { pt4, 4 }, + { pt5, 5 } + }; + Env.Print(dict[pt1]); + } + + [CommandMethod("testgraph")] + public void TestGraph1() + { + using var tr = new DBTrans(); + var ents = Env.Editor.SSGet()?.Value?.GetEntities(); + if (ents == null) + return; + Tools.TestTimes2(1, "new", () => { + + + var res = ents.GetAllCycle(); + + //res.ForEach((i, t) => t.ForWrite(e => e.ColorIndex = i + 1)); + Env.Print(res.Count()); + tr.CurrentSpace.AddEntity(res); + }); + + } + + [CommandMethod("testgraphspeed")] + public void TestGraphspeed() + { + using var tr = new DBTrans(); + var ents = Env.Editor.SSGet()?.Value?.GetEntities(); + if (ents == null) + return; + + + var graph = new IFoxCAD.Cad.Graph(); // 为了调试先把图的访问改为internal + + foreach (var curve in ents) + { + + graph.AddEdge(curve.GetGeCurve()); + + + } + //新建 dfs + var dfs = new DepthFirst(); +#if true + Tools.TestTimes2(100, "new", () => { + // 查询全部的 闭合环 + dfs.FindAll(graph); + }); + Tools.TestTimes2(1000, "new", () => { + // 查询全部的 闭合环 + dfs.FindAll(graph); + }); +#else + Tools.TestTimes2(100, "old", () => { + // 查询全部的 闭合环 + dfs.FindAll(graph); + }); + Tools.TestTimes2(1000, "old", () => { + // 查询全部的 闭合环 + dfs.FindAll(graph); + }); +#endif + //res.ForEach((i, t) => t.ForWrite(e => e.ColorIndex = i + 1)); + + //tr.CurrentSpace.AddEntity(res); + + } + } + + + + + public class TestCurve + { + [CommandMethod("testbreakcurve")] + public void TestBreakCurve() + { + using var tr = new DBTrans(); + var ents = Env.Editor.SSGet().Value.GetEntities(); + var tt = CurveEx.BreakCurve(ents.ToList()); + tt.ForEach(t => t.ForWrite(e => e.ColorIndex = 1)); + tr.CurrentSpace.AddEntity(tt); + } + + [CommandMethod("testCurveCurveIntersector3d")] + public void TestCurveCurveIntersector3d() + { + using var tr = new DBTrans(); + var ents = Env.Editor.SSGet().Value.GetEntities() + .Select(e => e.ToCompositeCurve3d()).ToList(); + + var cci3d = new CurveCurveIntersector3d(); + + + for (int i = 0; i < ents.Count; i++) + { + var gc1 = ents[i]; + var int1 = gc1.GetInterval(); + //var pars1 = paramss[i]; + for (int j = i; j < ents.Count; j++) + { + var gc2 = ents[j]; + //var pars2 = paramss[j]; + var int2 = gc2.GetInterval(); + cci3d.Set(gc1, gc2, int1, int2, Vector3d.ZAxis); + var d = cci3d.OverlapCount(); + var a = cci3d.GetIntersectionRanges(); + Env.Print($"{a[0].LowerBound}-{a[0].UpperBound} and {a[1].LowerBound}-{a[1].UpperBound}"); + for (int m = 0; m < d; m++) + { + var b = cci3d.GetOverlapRanges(m); + Env.Print($"{b[0].LowerBound}-{b[0].UpperBound} and {b[1].LowerBound}-{b[1].UpperBound}"); + } + + for (int k = 0; k < cci3d.NumberOfIntersectionPoints; k++) + { + //var a = cci3d.GetOverlapRanges(k); + //var b = cci3d.IsTangential(k); + //var c = cci3d.IsTransversal(k); + //var d = cci3d.OverlapCount(); + //var e = cci3d.OverlapDirection(); + var pt = cci3d.GetIntersectionParameters(k); + var pts = cci3d.GetIntersectionPoint(k); + Env.Print(pts); + } + + + + } + + } + // var tt = CurveEx.Topo(ents.ToList()); + //tt.ForEach(t => t.ForWrite(e => e.ColorIndex = 1)); + //tr.CurrentSpace.AddEntity(tt); + } + } +} \ No newline at end of file diff --git a/tests/Test/TestDBTrans.cs b/tests/Test/TestDBTrans.cs new file mode 100644 index 0000000..230882e --- /dev/null +++ b/tests/Test/TestDBTrans.cs @@ -0,0 +1,78 @@ +namespace Test; + +public class TestTrans +{ + [CommandMethod("testtr")] + public void Testtr() + { + string filename = @"C:\Users\vic\Desktop\test.dwg"; + using var tr = new DBTrans(filename); + tr.ModelSpace.AddCircle(new Point3d(10, 10, 0), 20); + //tr.Database.SaveAs(filename,DwgVersion.Current); + tr.SaveDwgFile(); + } + [CommandMethod("testifoxcommit")] + public void Testifoxcommit() + { + + using var tr = new DBTrans(); + tr.ModelSpace.AddCircle(new Point3d(0, 0, 0), 20); + tr.Abort(); + //tr.Commit(); + } + + // AOP 应用 预计示例: + // 1. 无参数 + //[AOP] + //[CommandMethod("TESTAOP")] + //public void testaop() + //{ + // // 不用 using var tr = new DBTrans(); + // var tr = DBTrans.Top; + // tr.ModelSpace.AddCircle(new Point3d(0, 0, 0), 20); + //} + + // 2. 有参数 + //[AOP("file")] + //[CommandMethod("TESTAOP")] + //public void testaop() + //{ + // // 不用 using var tr = new DBTrans(file); + // var tr = DBTrans.Top; + // tr.ModelSpace.AddCircle(new Point3d(0, 0, 0), 20); + //} + + + [CommandMethod("testpt")] + public void TestPt() + { + //var pt = Env.Editor.GetPoint("pick pt:").Value; + //var pl = Env.Editor.GetEntity("pick pl").ObjectId; + + var tr1 = HostApplicationServices.WorkingDatabase.TransactionManager.TopTransaction; + using var tr2 = new DBTrans(); + var tr3 = HostApplicationServices.WorkingDatabase.TransactionManager.TopTransaction; + var tr6 = Application.DocumentManager.MdiActiveDocument.TransactionManager.TopTransaction; + Env.Print(tr2.Transaction == tr3); + Env.Print(tr3 == tr6); + using var tr4 = new DBTrans(); + var tr5 = HostApplicationServices.WorkingDatabase.TransactionManager.TopTransaction; + var tr7 = Application.DocumentManager.MdiActiveDocument.TransactionManager.TopTransaction; + Env.Print(tr4.Transaction == tr5); + Env.Print(tr5 == tr7); + var trm = HostApplicationServices.WorkingDatabase.TransactionManager; + + //var ptt = tr.GetObject(pl).GetClosestPointTo(pt,false); + //var pt1 = new Point3d(0, 0.00000000000001, 0); + //var pt2 = new Point3d(0, 0.00001, 0); + //Env.Print(Tolerance.Global.EqualPoint); + //Env.Print(pt1.IsEqualTo(pt2).ToString()); + //Env.Print(pt1.IsEqualTo(pt2,new Tolerance(0.0,1e-6)).ToString()); + //Env.Print((pt1 == pt2).ToString()); + //Env.Print((pt1 != pt2).ToString()); + + + + } + +} diff --git a/tests/Test/TestEnt.cs b/tests/Test/TestEnt.cs new file mode 100644 index 0000000..e3cccf7 --- /dev/null +++ b/tests/Test/TestEnt.cs @@ -0,0 +1,40 @@ +namespace Test; + +public class TestEnt +{ + [CommandMethod("TestEntRoration")] + public void TestEntRoration() + { + var line = new Line(new(0,0,0),new(100,0,0)); + + using var tr = new DBTrans(); + tr.CurrentSpace.AddEntity(line); + var line2 = line.Clone() as Line; + tr.CurrentSpace.AddEntity(line2); + line2.Rotation(new(100, 0, 0), Math.PI / 2); + + + } + + + [CommandMethod("Testtypespeed")] + public void TestTypeSpeed() + { + var line = new Line(); + var line1 = line as Entity; + Tools.TestTimes(100000, "is 匹配:", () => + { + var t = line1 is Line; + }); + Tools.TestTimes(100000, "name 匹配:", () => + { + //var t = line.GetType().Name; + var tt = line1.GetType().Name == nameof(Line); + }); + Tools.TestTimes(100000, "dxfname 匹配:", () => + { + //var t = line.GetType().Name; + var tt = line1.GetRXClass().DxfName == nameof(Line); + }); + } +} diff --git a/tests/Test/TestFileDatabase.cs b/tests/Test/TestFileDatabase.cs new file mode 100644 index 0000000..7493f30 --- /dev/null +++ b/tests/Test/TestFileDatabase.cs @@ -0,0 +1,29 @@ +/************************************************************** +*作者:Leon +*创建时间:2022/2/11 9:55:32 +**************************************************************/ +namespace Test +{ + public class TestFileDatabase + { + [CommandMethod("Test_FileDatabaseInit")] + public void TestDatabase() + { + try + { + var fileName = @"C:\Users\Administrator\Desktop\合并详图测试BUG.dwg"; + using DBTrans trans = new(fileName); + trans.ModelSpace.AddEntity(new Line(new(0, 0, 0), new(1000, 1000, 0))); + if (trans.Document is not null && trans.Document.IsActive) + trans.Document.SendStringToExecute("_qsave\n", false, true, true); + else + trans.Database.SaveAs(fileName, DwgVersion.AC1021); + } + catch (System.Exception e) + { + System.Windows.MessageBox.Show(e.Message); + } + + } + } +} \ No newline at end of file diff --git a/tests/Test/TestJig.cs b/tests/Test/TestJig.cs new file mode 100644 index 0000000..0fb1310 --- /dev/null +++ b/tests/Test/TestJig.cs @@ -0,0 +1,334 @@ +namespace Test; +using System.Windows.Forms; + +public class Commands_Jig +{ + //已在数据库的图元如何进入jig + [CommandMethod("TestCmd_jig33")] + public static void TestCmd_jig33() + { + Circle cir; + using var tr = new DBTrans(); + var per = tr.Editor.GetEntity("\n点选圆形:"); + if (per.Status != PromptStatus.OK) + return; + cir = tr.GetObject(per.ObjectId, OpenMode.ForWrite); + + if (cir == null) + return; + var oldSp = cir.StartPoint; + JigEx moveJig = null; + moveJig = new JigEx((mousePoint, drawEntitys) => { + moveJig.SetOptions(oldSp);//回调过程中也可以修改基点 + //cir.UpgradeOpen();//已经提权了,所以这里不需要提权 + cir.Move(cir.StartPoint, mousePoint); + //cir.DowngradeOpen(); + + //此处会Dispose图元, + //所以此处不加入已经在数据库的图元,而是加入new Entity的. + //drawEntitys.Enqueue(cir); + }); + moveJig.SetOptions(cir.GeometricExtents.MinPoint, orthomode: true); + + //此处详见方法注释 + moveJig.DatabaseEntityDraw(draw => { + draw.RawGeometry.Draw(cir); + }); + + while (true) + { + var prDrag = moveJig.Drag(); + if (prDrag.Status == PromptStatus.OK) + break; + } + } + + + //不在数据库的图元如何进入jig + [CommandMethod("TestCmd_Jig44")] + public void TestCmd_Jig44() + { + using var tr = new DBTrans(); + var per = Env.Editor.GetEntity("\n请选择一条多段线:"); + if (per.Status != PromptStatus.OK) + return; + var ent = tr.GetObject(per.ObjectId, OpenMode.ForWrite); + if (ent is not Polyline pl) + return; + + /* + * 鼠标采样器执行时修改鼠标基点 + * 原因: 多段线与鼠标垂直点作为 BasePoint,jig鼠标点为确定点 + * 所以需要先声明再传入指针,但是我发现null也可以. + */ + JigEx jig = null; + JigPromptPointOptions options = null; + jig = new JigEx((mousePoint, drawEntitys) => { + var closestPt = pl.GetClosestPointTo(mousePoint, false); + + //回调过程中SetOptions会覆盖配置,所以如果想增加关键字或者修改基点, + //不要这样做: jig.SetOptions(closestPt) 而是使用底层暴露 + options.BasePoint = closestPt; + + //需要避免重复加入同一个关键字 + if (!options.Keywords.Contains("A")) + options.Keywords.Add("A"); + + //生成文字 + var dictString = (pl.GetDistAtPoint(closestPt) * 0.001).ToString("0.00"); + var acText = new TextInfo(dictString, closestPt, AttachmentPoint.BaseLeft, textHeight: 200) + .AddDBTextToEntity(); + + //加入刷新队列 + drawEntitys.Enqueue(acText); + }); + + options = jig.SetOptions(per.PickedPoint); + + // 如果没有这个,那么空格只会是 PromptStatus.None 而不是 PromptStatus.Keyword + // options.Keywords.Add(" ", " ", "空格结束啊"); + // jig.SetSpaceIsKeyword(); + + bool flag = true; + while (flag) + { + var pr = jig.Drag(); + if (pr.Status == PromptStatus.Keyword) + { + switch (pr.StringResult) + { + case "A": + tr.Editor.WriteMessage($"\n 您触发了关键字{pr.StringResult}"); + flag = false; + break; + case " ": + tr.Editor.WriteMessage("\n 触发关键字空格"); + flag = false; + break; + } + } + else if (pr.Status != PromptStatus.OK)//PromptStatus.None == 右键,空格,回车,都在这里结束 + { + tr.Editor.WriteMessage(Environment.NewLine + pr.Status.ToString()); + return; + } + else + flag = false; + } + tr.CurrentSpace.AddEntity(jig.Entitys); + } + + [CommandMethod("TestCmd_loop")] + public void TestCmd_loop() + { + DocumentCollection dm = Acap.DocumentManager; + Editor ed = dm.MdiActiveDocument.Editor; + // Create and add our message filter + MyMessageFilter filter = new(); + System.Windows.Forms.Application.AddMessageFilter(filter); + // Start the loop + while (true) + { + // Check for user input events + System.Windows.Forms.Application.DoEvents(); + // Check whether the filter has set the flag + if (filter.bCanceled == true) + { + ed.WriteMessage("\nLoop cancelled."); + break; + } + ed.WriteMessage($"\nInside while loop...and {filter.Key}"); + } + // We're done - remove the message filter + System.Windows.Forms.Application.RemoveMessageFilter(filter); + } + // Our message filter class + public class MyMessageFilter : IMessageFilter + { + public const int WM_KEYDOWN = 0x0100; + public bool bCanceled = false; + public Keys Key { get; private set; } + public bool PreFilterMessage(ref Message m) + { + if (m.Msg == WM_KEYDOWN) + { + // Check for the Escape keypress + Keys kc = (Keys)(int)m.WParam & Keys.KeyCode; + if (m.Msg == WM_KEYDOWN && kc == Keys.Escape) + { + bCanceled = true; + } + Key = kc; + // Return true to filter all keypresses + return true; + } + // Return false to let other messages through + return false; + } + } + + + [CommandMethod("TestCmd_QuickText")] + static public void TestCmd_QuickText() + { + var dm = Acap.DocumentManager; + var doc = dm.MdiActiveDocument; + var db = doc.Database; + var ed = doc.Editor; + + PromptStringOptions pso = new("\nEnter text string") + { + AllowSpaces = true + }; + var pr = ed.GetString(pso); + if (pr.Status != PromptStatus.OK) + return; + + var tr = doc.TransactionManager.StartTransaction(); + using (tr) + { + BlockTableRecord btr = + (BlockTableRecord)tr.GetObject( + db.CurrentSpaceId, OpenMode.ForWrite + ); + // Create the text object, set its normal and contents + + var acText = new TextInfo(pr.StringResult, + Point3d.Origin, + AttachmentPoint.BaseLeft, textHeight: 200) + .AddDBTextToEntity(); + + acText.Normal = ed.CurrentUserCoordinateSystem.CoordinateSystem3d.Zaxis; + btr.AppendEntity(acText); + tr.AddNewlyCreatedDBObject(acText, true); + + // Create our jig + var pj = new TextPlacementJig(tr, db, acText); + // Loop as we run our jig, as we may have keywords + PromptStatus stat = PromptStatus.Keyword; + while (stat == PromptStatus.Keyword) + { + PromptResult res = ed.Drag(pj); + stat = res.Status; + if ( + stat != PromptStatus.OK && + stat != PromptStatus.Keyword + ) + return; + } + tr.Commit(); + } + } + class TextPlacementJig : EntityJig + { + // Declare some internal state + readonly Database _db; + readonly Transaction _tr; + Point3d _position; + double _angle, _txtSize; + // Constructor + public TextPlacementJig( + Transaction tr, Database db, Entity ent + ) : base(ent) + { + _db = db; + _tr = tr; + _angle = 0; + _txtSize = 1; + } + protected override SamplerStatus Sampler( + JigPrompts jp + ) + { + // We acquire a point but with keywords + JigPromptPointOptions po = + new( + "\nPosition of text" + ); + po.UserInputControls = + (UserInputControls.Accept3dCoordinates | + UserInputControls.NullResponseAccepted | + UserInputControls.NoNegativeResponseAccepted | + UserInputControls.GovernedByOrthoMode); + po.SetMessageAndKeywords( + "\nSpecify position of text or " + + "[Bold/Italic/LArger/Smaller/" + + "ROtate90/LEft/Middle/RIght]: ", + "Bold Italic LArger Smaller " + + "ROtate90 LEft Middle RIght" + ); + PromptPointResult ppr = jp.AcquirePoint(po); + if (ppr.Status == PromptStatus.Keyword) + { + switch (ppr.StringResult) + { + case "Bold": + { + break; + } + case "Italic": + { + break; + } + case "LArger": + { + // Multiple the text size by two + _txtSize *= 2; + break; + } + case "Smaller": + { + // Divide the text size by two + _txtSize /= 2; + break; + } + case "ROtate90": + { + // To rotate clockwise we subtract 90 degrees and + // then normalise the angle between 0 and 360 + _angle -= Math.PI / 2; + while (_angle < Math.PI * 2) + { + _angle += Math.PI * 2; + } + break; + } + case "LEft": + { + break; + } + case "RIght": + { + break; + } + case "Middle": + { + break; + } + } + return SamplerStatus.OK; + } + else if (ppr.Status == PromptStatus.OK) + { + // Check if it has changed or not (reduces flicker) + if ( + _position.DistanceTo(ppr.Value) < + Tolerance.Global.EqualPoint + ) + return SamplerStatus.NoChange; + _position = ppr.Value; + return SamplerStatus.OK; + } + return SamplerStatus.Cancel; + } + protected override bool Update() + { + // Set properties on our text object + DBText txt = (DBText)Entity; + txt.Position = _position; + txt.Height = _txtSize; + txt.Rotation = _angle; + return true; + } + } +} diff --git a/tests/Test/TestLisp.cs b/tests/Test/TestLisp.cs new file mode 100644 index 0000000..f657286 --- /dev/null +++ b/tests/Test/TestLisp.cs @@ -0,0 +1,122 @@ +namespace Test +{ + public class TestLisp + { + //定义lisp函数 + [LispFunction("LispTest_RunLisp")] + public static object LispTest_RunLisp(ResultBuffer rb) + { + CmdTest_RunLisp(); + return null!; + } + + //模态命令,只有当CAD发出命令提示或当前没有其他的命令或程序活动的时候才可以被触发 + [CommandMethod("CmdTest_RunLisp1", CommandFlags.Modal)] + //透明命令,可以在一个命令提示输入的时候触发例如正交切换,zoom等 + [CommandMethod("CmdTest_RunLisp2", CommandFlags.Transparent)] + //选择图元之后执行命令将可以从 获取图元 + [CommandMethod("CmdTest_RunLisp3", CommandFlags.UsePickSet)] + //命令执行前已选中部分实体.在命令执行过程中这些标记不会被清除 + [CommandMethod("CmdTest_RunLisp4", CommandFlags.Redraw)] + //命令不能在透视图中使用 + [CommandMethod("CmdTest_RunLisp5", CommandFlags.NoPerspective)] + //命令不能通过 MULTIPLE命令 重复触发 + [CommandMethod("CmdTest_RunLisp6", CommandFlags.NoMultiple)] + //不允许在模型空间使用命令 + [CommandMethod("CmdTest_RunLisp7", CommandFlags.NoTileMode)] + //不允许在布局空间使用命令 + [CommandMethod("CmdTest_RunLisp8", CommandFlags.NoPaperSpace)] + //命令不能在OEM产品中使用 + [CommandMethod("CmdTest_RunLisp9", CommandFlags.NoOem)] + //不能直接使用命令名调用,必须使用 组名.全局名 调用 + [CommandMethod("CmdTest_RunLisp10", CommandFlags.Undefined)] + //定义lisp方法.已废弃 请使用lispfunction + [CommandMethod("CmdTest_RunLisp11", CommandFlags.Defun)] + //命令不会被存储在新的命令堆上 + [CommandMethod("CmdTest_RunLisp12", CommandFlags.NoNewStack)] + //命令不能被内部锁定(命令锁) + [CommandMethod("CmdTest_RunLisp13", CommandFlags.NoInternalLock)] + //调用命令的文档将会被锁定为只读 + [CommandMethod("CmdTest_RunLisp14", CommandFlags.DocReadLock)] + //调用命令的文档将会被锁定,类似document.lockdocument + [CommandMethod("CmdTest_RunLisp15", CommandFlags.DocExclusiveLock)] + //命令在CAD运行期间都能使用,而不只是在当前文档 + [CommandMethod("CmdTest_RunLisp16", CommandFlags.Session)] + //获取用户输入时,可以与属性面板之类的交互 + [CommandMethod("CmdTest_RunLisp17", CommandFlags.Interruptible)] + //命令不会被记录在命令历史记录 + [CommandMethod("CmdTest_RunLisp18", CommandFlags.NoHistory)] + //命令不会被 UNDO取消 + [CommandMethod("CmdTest_RunLisp19", CommandFlags.NoUndoMarker)] + //不能在参照块中使用命令 + [CommandMethod("CmdTest_RunLisp20", CommandFlags.NoBlockEditor)] +#if ac2009 + //acad09增,不会被动作录制器 捕捉到 + [CommandMethod("CmdTest_RunLisp21", CommandFlags.NoActionRecording)] + //acad09增,会被动作录制器捕捉 + [CommandMethod("CmdTest_RunLisp22", CommandFlags.ActionMacro)] +#endif +#if !NET35 + //推断约束时不能使用命令 + [CommandMethod("CmdTest_RunLisp23", CommandFlags.NoInferConstraint)] + //命令允许在选择图元时临时显示动态尺寸 + [CommandMethod("CmdTest_RunLisp24", CommandFlags.TempShowDynDimension)] +#endif + public static void CmdTest_RunLisp() + { + // 测试方法1: (command "CmdTest_RunLisp1") + // 测试方式2: (LispTest_RunLisp) + var dm = Application.DocumentManager; + var doc = dm.MdiActiveDocument; + var ed = doc.Editor; + + var sb = new StringBuilder(); + foreach (var item in Enum.GetValues(typeof(EditorEx.RunLispFlag))) + { + sb.Append((byte)item); + sb.Append(','); + } + sb.Remove(sb.Length - 1, 1); + var option = new PromptIntegerOptions($"\n输入RunLispFlag枚举值:[{sb}]"); + var ppr = ed.GetInteger(option); + + if (ppr.Status != PromptStatus.OK) + return; + var flag = (EditorEx.RunLispFlag)ppr.Value; + + if (flag == EditorEx.RunLispFlag.AdsQueueexpr) + { + // 同步 + Env.Editor.RunLisp("(setq a 10)(princ)", + EditorEx.RunLispFlag.AdsQueueexpr); + Env.Editor.RunLisp("(princ a)", + EditorEx.RunLispFlag.AdsQueueexpr);//成功输出 + } + else if (flag == EditorEx.RunLispFlag.AcedEvaluateLisp) + { + // 使用(command "CmdTest_RunLisp1")发送,然后 !b 查看变量,acad08是有值的,高版本是null + var strlisp0 = "(setq b 20)"; + var res0 = Env.Editor.RunLisp(strlisp0, + EditorEx.RunLispFlag.AcedEvaluateLisp); //有lisp的返回值 + + var strlisp1 = "(defun f1( / )(princ \"aa\"))"; + var res1 = Env.Editor.RunLisp(strlisp1, + EditorEx.RunLispFlag.AcedEvaluateLisp); //有lisp的返回值 + + var strlisp2 = "(defun f2( / )(command \"line\"))"; + var res2 = Env.Editor.RunLisp(strlisp2, + EditorEx.RunLispFlag.AcedEvaluateLisp); //有lisp的返回值 + } + else if (flag == EditorEx.RunLispFlag.SendStringToExecute) + { + //测试异步 + //(command "CmdTest_RunLisp1")和(LispTest_RunLisp)4都是异步 + var str = "(setq c 40)(princ)"; + Env.Editor.RunLisp(str, + EditorEx.RunLispFlag.SendStringToExecute); //异步,后发送 + Env.Editor.RunLisp("(princ c)", + EditorEx.RunLispFlag.AdsQueueexpr); //同步,先发送了,输出是null + } + } + } +} diff --git a/tests/Test/TestLoop.cs b/tests/Test/TestLoop.cs new file mode 100644 index 0000000..8992df9 --- /dev/null +++ b/tests/Test/TestLoop.cs @@ -0,0 +1,36 @@ +using IFoxCAD.Basal; + +using System.Diagnostics.CodeAnalysis; +namespace Test +{ + public class TestLoop + { + [CommandMethod("testloop")] + public void Testloop() + { + var loop = new LoopList + { + 0, + 1, + 2, + 3, + 4, + 5 + }; + + + + + Env.Print(loop); + + loop.SetFirst(loop.Last); + Env.Print(loop); + Env.Print(loop.Min()); + loop.SetFirst(new LoopListNode (loop.Min() ,loop)); + Env.Print(loop); + + + + } + } +} diff --git a/tests/Test/TestMirrorFile.cs b/tests/Test/TestMirrorFile.cs new file mode 100644 index 0000000..b5f212f --- /dev/null +++ b/tests/Test/TestMirrorFile.cs @@ -0,0 +1,73 @@ +public class MirrorFile +{ + const string file = "D:/JX.dwg"; + const string fileSave = "D:/JX222.dwg"; + + /// + /// 测试:后台打开图纸,镜像文字是否存在文字偏移 + /// 答案:不存在 + /// + [CommandMethod("CmdTest_MirrorFile")] + public static void CmdTest_MirrorFile() + { + using var tr = new DBTrans(file, openMode: FileOpenMode.OpenForReadAndReadShare); + + tr.BlockTable.Change(tr.ModelSpace.ObjectId, modelSpace => { + foreach (ObjectId entId in modelSpace) + { + var dbText = tr.GetObject(entId, OpenMode.ForRead)!; + if (dbText is null) + continue; + + dbText.UpgradeOpen(); + var pos = dbText.Position; + //text.Move(pos, Point3d.Origin); + //Y轴 + dbText.Mirror(Point3d.Origin, new Point3d(0, 1, 0)); + //text.Move(Point3d.Origin, pos); + dbText.DowngradeOpen(); + } + }); + tr.Database.SaveAs(fileSave, DwgVersion.AC1021/*AC1021 AutoCAD 2007/2008/2009.*/); + } + + /// + /// 测试:后台设置 dbText.IsMirroredInX 属性会令文字偏移 + /// 答案:存在,并提出解决方案 + /// + [CommandMethod("CmdTest_MirrorFile2")] + public static void CmdTest_MirrorFile2() + { + using var tr = new DBTrans(file, openMode: FileOpenMode.OpenForReadAndReadShare); + + tr.Database.DBTextDeviation(() => { + + var yaxis = new Point3d(0, 1, 0); + tr.BlockTable.Change(tr.ModelSpace.ObjectId, modelSpace => { + foreach (ObjectId entId in modelSpace) + { + var entity = tr.GetObject(entId, OpenMode.ForWrite)!; + if (entity is DBText dbText) + { + dbText.Mirror(Point3d.Origin, yaxis); + dbText.IsMirroredInX = true; //这句将导致文字偏移 + + //指定文字的垂直对齐方式 + if (dbText.VerticalMode == TextVerticalMode.TextBase) + dbText.VerticalMode = TextVerticalMode.TextBottom; + + //指定文字的水平对齐方式 + dbText.HorizontalMode = dbText.HorizontalMode switch + { + TextHorizontalMode.TextLeft => TextHorizontalMode.TextRight, + TextHorizontalMode.TextRight => TextHorizontalMode.TextLeft, + _ => dbText.HorizontalMode + }; + dbText.AdjustAlignment(tr.Database); + } + } + }); + }); + tr.Database.SaveAs(fileSave, DwgVersion.AC1021/*AC1021 AutoCAD 2007/2008/2009.*/); + } +} \ No newline at end of file diff --git a/tests/Test/TestPoint.cs b/tests/Test/TestPoint.cs new file mode 100644 index 0000000..46efe2e --- /dev/null +++ b/tests/Test/TestPoint.cs @@ -0,0 +1,160 @@ +using System.Diagnostics; + +namespace Test +{ + public class TestPoint + { + /// + /// 红黑树排序点集 + /// + [CommandMethod("TestptSortedSet")] + public void TestptSortedSet() + { + var ss1 = new SortedSet(); + ss1.Add(new Point2d(1, 1)); + ss1.Add(new Point2d(4.6, 2)); + ss1.Add(new Point2d(8, 3)); + ss1.Add(new Point2d(4, 3)); + ss1.Add(new Point2d(5, 40)); + ss1.Add(new Point2d(6, 5)); + ss1.Add(new Point2d(1, 6)); + ss1.Add(new Point2d(7, 6)); + ss1.Add(new Point2d(9, 6)); + + /*判断区间,超过就中断*/ + foreach (var item in ss1) + { + if (item.X > 3 && item.X < 7) + { + Debug.WriteLine(item); + } + else if (item.X >= 7) + { + break; + } + } + } + + + + [CommandMethod("TestptGethash")] + public void TestptGethash() + { + // test + var pt = Env.Editor.GetPoint("pick pt").Value; + //Tools.TestTimes2(1_000_000, "新语法", () => { + // pt.GetHashString2(); + //}); + Tools.TestTimes2(1_000_000, "旧语法", () => { + pt.GetHashString(); + }); + } + + [CommandMethod("Testpoint3d")] + public void TestPoint3d() + { + Env.Print($"4位小数的hash:{new Point3d(0.0_001, 0.0_002, 0.0).GetHashCode()}"); + Env.Print($"5位小数的hash:{new Point3d(0.00_001, 0.00_002, 0.0).GetHashCode()}"); + Env.Print($"6位小数的hash:{new Point3d(0.000_001, 0.000_002, 0.0).GetHashCode()}"); + Env.Print($"7位小数的hash:{new Point3d(0.000_0_001, 0.000_0_002, 0.0).GetHashCode()}"); + Env.Print($"8位小数的hash:{new Point3d(0.000_00_001, 0.000_00_002, 0.0).GetHashCode()}"); + Env.Print($"9位小数的hash:{new Point3d(0.000_000_001, 0.000_000_002, 0.0).GetHashCode()}"); + Env.Print($"10位小数的hash:{new Point3d(0.000_000_0001, 0.000_000_0002, 0.0).GetHashCode()}"); + Env.Print($"10位小数的hash:{new Point3d(0.000_000_0001, 0.000_000_0001, 0.0).GetHashCode()}"); + + Env.Print($"11位小数的hash:{new Point3d(0.000_000_000_01, 0.000_000_000_02, 0.0).GetHashCode()}"); + Env.Print($"11位小数的hash:{new Point3d(0.000_000_000_01, 0.000_000_000_01, 0.0).GetHashCode()}"); + + Env.Print($"12位小数的hash:{new Point3d(0.000_000_000_001, 0.000_000_000_002, 0.0).GetHashCode()}"); + Env.Print($"12位小数的hash:{new Point3d(0.000_000_000_001, 0.000_000_000_001, 0.0).GetHashCode()}"); + + Env.Print($"13位小数的hash:{new Point3d(0.000_000_000_0001, 0.000_000_000_0002, 0.0).GetHashCode()}"); + Env.Print($"13位小数的hash:{new Point3d(0.000_000_000_0001, 0.000_000_000_0001, 0.0).GetHashCode()}"); + + Env.Print($"14位小数的hash:{new Point3d(0.000_000_000_000_01, 0.000_000_000_000_02, 0.0).GetHashCode()}"); + Env.Print($"14位小数的hash:{new Point3d(0.000_000_000_000_01, 0.000_000_000_000_01, 0.0).GetHashCode()}"); + + Env.Print($"15位小数的hash:{new Point3d(0.000_000_000_000_001, 0.000_000_000_000_002, 0.0).GetHashCode()}"); + Env.Print($"15位小数的hash:{new Point3d(0.000_000_000_000_001, 0.000_000_000_000_001, 0.0).GetHashCode()}"); + + Env.Print($"16位小数的hash:{new Point3d(0.000_000_000_000_000_1, 0.000_000_000_000_000_2, 0.0).GetHashCode()}"); + Env.Print($"16位小数的hash:{new Point3d(0.000_000_000_000_000_1, 0.000_000_000_000_000_1, 0.0).GetHashCode()}"); + + Env.Print($"17位小数的hash:{new Point3d(0.000_000_000_000_000_01, 0.000_000_000_000_000_02, 0.0).GetHashCode()}"); + Env.Print($"17位小数的hash:{new Point3d(0.000_000_000_000_000_01, 0.000_000_000_000_000_01, 0.0).GetHashCode()}"); + + Env.Print($"18位小数的hash:{new Point3d(0.000_000_000_000_000_001, 0.000_000_000_000_000_002, 0.0).GetHashCode()}"); + Env.Print($"18位小数的hash:{new Point3d(0.000_000_000_000_000_001, 0.000_000_000_000_000_001, 0.0).GetHashCode()}"); + + Env.Print($"19位小数的hash:{new Point3d(0.000_000_000_000_000_000_1, 0.000_000_000_000_000_000_2, 0.0).GetHashCode()}"); + Env.Print($"19位小数的hash:{new Point3d(0.000_000_000_000_000_000_1, 0.000_000_000_000_000_000_1, 0.0).GetHashCode()}"); + + Env.Print($"20位小数的hash:{new Point3d(0.000_000_000_000_000_000_01, 0.000_000_000_000_000_000_02, 0.0).GetHashCode()}"); + Env.Print($"20位小数的hash:{new Point3d(0.000_000_000_000_000_000_01, 0.000_000_000_000_000_000_01, 0.0).GetHashCode()}"); + } + + [CommandMethod("Testlistequalspeed")] + public void Testlistequalspeed() + { + var lst1 = new List { 1, 2, 3, 4 }; + var lst2 = new List { 1, 2, 3, 4}; + lst1.EqualsAll(null); + Tools.TestTimes2(1000000, "eqaulspeed:", () => { + lst1.EqualsAll(lst2); + }); + + + } + + [CommandMethod("Testcontains")] + public void Testcontains() + { + // test list and dict contains speed + var lst = new List { 1, 2, 3, 4 , 5,6,7,8,9,10, 11,12,13,14,15,16,17,18,19,20}; + var hashset = new HashSet { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20 }; + var dict = new Dictionary + { + { 1, 0 }, + { 2, 1 }, + { 3, 2 }, + { 4, 3 }, + { 5, 4 }, + { 6, 5 }, + { 7, 6 }, + { 8, 7 }, + { 9, 8 }, + { 10, 9 }, + { 11, 11 }, + { 12, 12 }, + { 13, 13 }, + { 14, 14 }, + { 15, 15 }, + { 16, 16 }, + { 17, 17 }, + { 18, 18 }, + { 19, 19 }, + { 20, 20 }, + }; + + Tools.TestTimes2(100_0000, "list:", () => { + lst.Contains(20); + }); + + Tools.TestTimes2(100_0000, "hashset:", () => { + hashset.Contains(20); + }); + + Tools.TestTimes2(100_0000, "dict:", () => { + dict.ContainsKey(20); + }); + + } + + + + + + } + + +} \ No newline at end of file diff --git a/tests/Test/TestQuadTree.cs b/tests/Test/TestQuadTree.cs new file mode 100644 index 0000000..7670eae --- /dev/null +++ b/tests/Test/TestQuadTree.cs @@ -0,0 +1,461 @@ +namespace Test; + +#pragma warning disable CS8632 // 只能在 "#nullable" 注释上下文内的代码中使用可为 null 的引用类型的注释。 +/* + * 这里属于用户调用例子, + * 调用时候必须要继承它,再提供给四叉树 + * 主要是用户可以扩展属性 + */ +public class CadEntity : QuadEntity +{ + public ObjectId ObjectId; + //这里加入其他字段 + public List? Link;//碰撞链 + public System.Drawing.Color Color; + public double Angle; + public CadEntity(ObjectId objectId, Rect box) : base(box) + { + ObjectId = objectId; + } + public int CompareTo(CadEntity? other) + { + if (other == null) + return -1; + return GetHashCode() ^ other.GetHashCode(); + } + public override int GetHashCode() + { + return (base.GetHashCode(), ObjectId.GetHashCode()).GetHashCode(); + } +} +#pragma warning restore CS8632 // 只能在 "#nullable" 注释上下文内的代码中使用可为 null 的引用类型的注释。 + + + + + +public partial class TestQuadTree +{ + QuadTree _quadTreeRoot; + #region 四叉树创建并加入 + [CommandMethod("Test_QuadTree")] + public void Test_QuadTree() + { + using var tr = new DBTrans(); + + Rect dbExt; + //使用数据库边界来进行 + var dbExtent = tr.Database.GetValidExtents3d(); + if (dbExtent == null) + { + //throw new ArgumentException("画一个矩形"); + + //这个初始值的矩形是很有意义, + //主要是四叉树分裂过程中产生多个Rect,Rect内有很多重复的double值,是否可以内存复用,以此减少内存大小? + //接着想了一下,Rect可以是int,long,这样可以利用位运算它扩展和缩小, + //最小就是1,并且可以控制四叉树深度,不至于无限递归. + //而且指针长度跟值是一样的,所以就不需要复用了,毕竟跳转一个函数地址挺麻烦的. + //但是因为啊惊懒的原因,并没有单独制作这样的矩形, + //而且非常糟糕的是,c#不支持模板约束运算符,使得值类型之间需要通过一层接口来委婉处理,拉低了效率..引用类型倒是无所谓.. + //要么忍着,要么换c++去搞四叉树吧 + dbExt = new Rect(0, 0, 1 << 10, 1 << 10); + } + else + { + var a = new Point2d(dbExtent.Value.MinPoint.X, dbExtent.Value.MinPoint.Y); + var b = new Point2d(dbExtent.Value.MaxPoint.X, dbExtent.Value.MaxPoint.Y); + dbExt = new Rect(a, b); + } + + //创建四叉树 + _quadTreeRoot = new QuadTree(dbExt); + + //数据库边界 + var pl = dbExt.ToPoints(); + var databaseBoundary = new List<(Point3d, double, double, double)> + { + (new Point3d(pl[0].X,pl[0].Y,0),0,0,0), + (new Point3d(pl[1].X,pl[1].Y,0),0,0,0), + (new Point3d(pl[2].X,pl[2].Y,0),0,0,0), + (new Point3d(pl[3].X,pl[3].Y,0),0,0,0), + }; + tr.CurrentSpace.AddPline(databaseBoundary); + + //生成多少个图元,导致cad会令undo出错(八叉树深度过大 treemax) + //int maximumItems = 30_0000; + int maximumItems = 1000; + + //随机图元生成 + List ces = new(); //用于随机获取图元 + Timer.RunTime(() => { + //生成外边界和随机圆形 + var grc = GenerateRandomCircle(maximumItems, dbExt); + foreach (var ent in grc) + { + //初始化图元颜色 + ent.ColorIndex = 1; //Color.FromRgb(0, 0, 0);//黑色 + var edge = ent.GeometricExtents; + //四叉树数据 + var entRect = new Rect(edge.MinPoint.X, edge.MinPoint.Y, edge.MaxPoint.X, edge.MaxPoint.Y); + var entId = tr.CurrentSpace.AddEntity(ent); + var ce = new CadEntity(entId, entRect) + { + Color = Utility.RandomColor + }; + ces.Add(ce); + /*加入随机点*/ + var p = edge.MinPoint + new Vector3d(10, 10, 0); + entRect = new Rect(p.Point2d(), p.Point2d()); + entId = tr.CurrentSpace.AddEntity(new DBPoint(p)); + var dbPointCe = new CadEntity(entId, entRect); + ces.Add(dbPointCe); + } + }, Timer.TimeEnum.Millisecond, "画圆消耗时间:");//30万图元±3秒.cad2021 + + //测试只加入四叉树的时间 + Timer.RunTime(() => { + for (int i = 0; i < ces.Count; i++) + { + _quadTreeRoot.Insert(ces[i]); + } + }, Timer.TimeEnum.Millisecond, "插入四叉树时间:");//30万图元±0.7秒.cad2021 + + tr.Editor.WriteMessage($"\n加入图元数量:{maximumItems}"); + } + + /// + /// 创建随机圆形 + /// + /// 创建数量 + /// 数据库边界 + static IEnumerable GenerateRandomCircle(int createNumber, Rect dbExt) + { + var x1 = (int)dbExt.X; + var x2 = (int)(dbExt.X + dbExt.Width); + var y1 = (int)dbExt.Y; + var y2 = (int)(dbExt.Y + dbExt.Height); + + var rand = Utility.GetRandom(); + for (int i = 0; i < createNumber; i++) + { + var x = rand.Next(x1, x2) + rand.NextDouble(); + var y = rand.Next(y1, y2) + rand.NextDouble(); + yield return EntityEx.CreateCircle(new Point3d(x, y, 0), rand.Next(1, 100)); //起点,终点 + } + } + + /*TODO 啊惊: 有点懒不想改了*/ +#if true2 + + //选择加入到四叉树 + [CommandMethod("CmdTest_QuadTree21")] + public void CmdTest_QuadTree21() + { + var dm = Acap.DocumentManager; + var doc = dm.MdiActiveDocument; + var db = doc.Database; + var ed = doc.Editor; + ed.WriteMessage("\n选择单个图元加入已有的四叉树"); + + var ss = ed.Ssget(); + if (ss.Count == 0) + return; + + AddQuadTreeRoot(db, ed, ss); + } + + //自动加入全图到四叉树 + [CommandMethod("CmdTest_QuadTree20")] + public void CmdTest_QuadTree20() + { + var dm = Acap.DocumentManager; + var doc = dm.MdiActiveDocument; + var db = doc.Database; + var ed = doc.Editor; + ed.WriteMessage("\n自动加入全图到四叉树"); + + var ss = new List(); + int entnum = 0; + var time1 = Timer.RunTime(() => { + db.Action(tr => { + db.TraverseBlockTable(tr, btRec => { + if (!btRec.IsLayout)//布局跳过 + return false; + + foreach (var item in btRec) + { + //var ent = item.ToEntity(tr); + ss.Add(item); + ++entnum;//图元数量:100000, 遍历全图时间:0.216秒 CmdTest_QuadTree2 + } + return false; + }); + }); + }); + ed.WriteMessage($"\n图元数量:{entnum}, 遍历全图时间:{time1 / 1000.0}秒"); + + //清空原有的 + _quadTreeRoot = null; + AddQuadTreeRoot(db, ed, ss); + } + + void AddQuadTreeRoot(Database db, Editor ed, List ss) + { + if (_quadTreeRoot is null) + { + ed.WriteMessage("\n四叉树是空的,重新初始化"); + + Rect dbExt; + //使用数据库边界来进行 + var dbExtent = db.GetValidExtents3d(); + if (dbExtent == null) + { + //throw new ArgumentException("画一个矩形"); + + //测试时候画个矩形,在矩形内画随机坐标的圆形 + dbExt = new Rect(0, 0, 32525, 32525); + } + else + { + dbExt = new Rect(dbExtent.Value.MinPoint.Point2d(), dbExtent.Value.MaxPoint.Point2d()); + } + _quadTreeRoot = new(dbExt); + } + + /* 测试: + * 为了测试删除内容释放了分支,再重复加入是否报错 + * 先创建 CmdTest_QuadTree1 + * 再减去 CmdTest_QuadTree0 + * 然后原有黑色边界,再生成边界 CmdTest_Create00,对比删除效果. + * 然后加入 CmdTest_QuadTree2 + * 然后原有黑色边界,再生成边界 CmdTest_Create00,对比删除效果. + */ + + List ces = new(); + db.Action(tr => { + ss.ForEach(entId => { + var ent = entId.ToEntity(tr); + if (ent is null) + return; + var edge = new EdgeEntity(ent); + //四叉树数据 + var ce = new CadEntity(entId, edge.Edge) + { + Color = Utility.RandomColor + }; + ces.Add(ce); + + edge.Dispose(); + }); + }); + + var time2 = Timer.RunTime(() => { + _quadTreeRoot.Insert(ces); + }); + ed.WriteMessage($"\n图元数量:{ces.Count}, 加入四叉树时间:{time2 / 1000.0}秒"); + } +#endif + + #endregion + + /*TODO 啊惊: 有点懒不想改了*/ +#if true2 + + #region 节点边界显示 + //四叉树减去节点 + [CommandMethod("CmdTest_QuadTree0")] + public void CmdTest_QuadTree0() + { + var dm = Acap.DocumentManager; + var doc = dm.MdiActiveDocument; + //var db = doc.Database; + var ed = doc.Editor; + ed.WriteMessage("\n四叉树减区"); + + if (_quadTreeRoot is null) + { + ed.WriteMessage("\n四叉树是空的"); + return; + } + var rect = GetCorner(ed); + if (rect is null) + return; + _quadTreeRoot.Remove(rect); + } + + //创建节点边界 + [CommandMethod("CmdTest_QuadTree00")] + public void CmdTest_CreateNodesRect() + { + var dm = Acap.DocumentManager; + var doc = dm.MdiActiveDocument; + var db = doc.Database; + var ed = doc.Editor; + ed.WriteMessage("\n创建边界"); + + if (_quadTreeRoot is null) + { + ed.WriteMessage("\n四叉树是空的"); + return; + } + + //此处发现了一个事务处理的bug,提交数量过多的时候,会导致 ctrl+z 无法回滚, + //需要把事务放在循环体内部 + //报错: 0x6B00500A (msvcr80.dll)处(位于 acad.exe 中)引发的异常: 0xC0000005: 写入位置 0xFFE00000 时发生访问冲突。 + //画出所有的四叉树节点边界,因为事务放在外面引起 + var nodeRects = new List(); + _quadTreeRoot.ForEach(node => { + nodeRects.Add(node); + return false; + }); + var rectIds = new List(); + foreach (var item in nodeRects)//Count = 97341 当数量接近这个量级 + { + db.Action(tr => { + var pts = item.ToPoints(); + var rec = EntityAdd.AddPolyLineToEntity(pts.ToPoint2d()); + rec.ColorIndex = 250; + rectIds.Add(tr.AddEntityToMsPs(db, rec)); + }); + } + db.Action(tr => { + db.CoverGroup(tr, rectIds); + }); + + //获取四叉树深度 + int dep = 0; + _quadTreeRoot.ForEach(node => { + dep = dep > node.Depth ? dep : node.Depth; + return false; + }); + ed.WriteMessage($"\n四叉树深度是: {dep}"); + } + #endregion + +#endif + + #region 四叉树查询节点 + //选择范围改图元颜色 + [CommandMethod("CmdTest_QuadTree3")] + public void CmdTest_QuadTree3() + { + Ssget(QuadTreeSelectMode.IntersectsWith); + } + + [CommandMethod("CmdTest_QuadTree4")] + public void CmdTest_QuadTree4() + { + Ssget(QuadTreeSelectMode.Contains); + } + + /// + /// 改颜色 + /// + /// + void Ssget(QuadTreeSelectMode mode) + { + using var tr = new DBTrans(); + + if (_quadTreeRoot is null) + return; + var rect = GetCorner(tr.Editor); + if (rect is null) + return; + + tr.Editor.WriteMessage("选择模式:" + mode); + + //仿选择集 + var ces = _quadTreeRoot.Query(rect, mode); + ces.ForEach(item => { + var ent = tr.GetObject(item.ObjectId, OpenMode.ForWrite); + ent.Color = Color.FromColor(item.Color); + ent.DowngradeOpen(); + ent.Dispose(); + }); + } + + /// + /// 交互获取 + /// + /// + /// +#pragma warning disable CS8632 // 只能在 "#nullable" 注释上下文内的代码中使用可为 null 的引用类型的注释。 + public static Rect? GetCorner(Editor ed) + { + var optionsA = new PromptPointOptions($"{Environment.NewLine}起点位置:"); + var pprA = ed.GetPoint(optionsA); + if (pprA.Status != PromptStatus.OK) + return null; + var optionsB = new PromptCornerOptions(Environment.NewLine + "输入矩形角点2:", pprA.Value) + { + UseDashedLine = true,//使用虚线 + AllowNone = true,//回车 + }; + var pprB = ed.GetCorner(optionsB); + if (pprB.Status != PromptStatus.OK) + return null!; + + return new Rect(new Point2d(pprA.Value.X, pprA.Value.Y), + new Point2d(pprB.Value.X, pprB.Value.Y), + true); + } +#pragma warning restore CS8632 // 只能在 "#nullable" 注释上下文内的代码中使用可为 null 的引用类型的注释。 + + #endregion +} + +//public partial class TestQuadTree +//{ +// public void Cmd_tt6() +// { +// using var tr = new DBTrans(); +// var ed = tr.Editor; +// //创建四叉树,默认参数无所谓 +// var TreeRoot = new QuadTree(new Rect(0, 0, 32525, 32525)); + +// var fil = OpFilter.Bulid(e => e.Dxf(0) == "LINE"); +// var psr = ed.SSGet("\n 选择需要连接的直线", fil); +// if (psr.Status != PromptStatus.OK) return; +// var LineEnts = new List(psr.Value.GetEntities(OpenMode.ForWrite)!); +// //将实体插入到四岔树 +// foreach (var line in LineEnts) +// { +// var edge = line.GeometricExtents; +// var entRect = new Rect(edge.MinPoint.X, edge.MinPoint.Y, edge.MaxPoint.X, edge.MaxPoint.Y); +// var ce = new CadEntity(line.Id, entRect) +// { +// //四叉树数据 +// Angle = line.Angle +// }; +// TreeRoot.Insert(ce); +// } + +// var ppo = new PromptPointOptions(Environment.NewLine + "\n指定标注点:<空格退出>") +// { +// AllowArbitraryInput = true,//任意输入 +// AllowNone = true //允许回车 +// }; +// var ppr = ed.GetPoint(ppo);//用户点选 +// if (ppr.Status != PromptStatus.OK) +// return; +// var rect = new Rect(ppr.Value.Point2d(), 100, 100); +// tr.CurrentSpace.AddEntity(rect.ToPolyLine());//显示选择靶标范围 + +// var nent = TreeRoot.FindNearEntity(rect);//查询最近实体,按逆时针 +// var ent = tr.GetObject(nent.ObjectId, OpenMode.ForWrite);//打开实体 +// ent.ColorIndex = Utility.GetRandom().Next(1, 256);//1~256随机色 +// ent.DowngradeOpen();//实体降级 +// ent.Dispose(); + +// var res = TreeRoot.Query(rect, QuadTreeSelectMode.IntersectsWith);//查询选择靶标范围相碰的ID +// res.ForEach(item => { +// if (item.Angle == 0 || item.Angle == Math.PI) //过滤直线角度为0或180的直线 +// { +// var ent = tr.GetObject(item.ObjectId, OpenMode.ForWrite); +// ent.ColorIndex = Utility.GetRandom().Next(1, 7); +// ent.DowngradeOpen(); +// ent.Dispose(); +// } +// }); +// } +//} \ No newline at end of file diff --git a/tests/Test/Testid.cs b/tests/Test/Testid.cs new file mode 100644 index 0000000..d93b823 --- /dev/null +++ b/tests/Test/Testid.cs @@ -0,0 +1,74 @@ +namespace Test +{ + public class Testid + { + [CommandMethod("testid")] + public void TestId() + { + using var tr = new DBTrans(); + Line line = new(new Point3d(0, 0, 0), new Point3d(1, 1, 0)); + tr.CurrentSpace.AddEntity(line); + tr.Dispose(); + + var res = Env.Editor.GetEntity("\npick ent:"); + if (res.Status == PromptStatus.OK) + { + res.ObjectId.Erase(); + } + //using (var tr = new DBTrans()) + //{ + // var res = Env.Editor.GetEntity("\npick ent:"); + // if(res.Status == PromptStatus.OK) + // { + // res.ObjectId.Erase(); + // } + + //} + } + + [CommandMethod("testmycommand")] + public void TestMyCommand() + { + using var dbtrans = new DBTrans(Env.Document, true, false); + using var trans = Env.Database.TransactionManager.StartTransaction(); + + var l1 = new Line(new Point3d(0, 0, 0), new Point3d(100, 100, 0)); + var blkred = trans.GetObject(Env.Database.CurrentSpaceId, OpenMode.ForWrite) as BlockTableRecord; + blkred.AppendEntity(l1); + trans.AddNewlyCreatedDBObject(l1, true); + trans.Commit(); + //dbtrans.Dispose(); + } + [CommandMethod("testtextstyle")] + public void TestTextStyle() + { + using var tr = new DBTrans(); + tr.TextStyleTable.Add("宋体", "宋体.ttf", 0.8); + + tr.TextStyleTable.Add("宋体1", FontTTF.宋体, 0.8); + tr.TextStyleTable.Add("仿宋体", FontTTF.仿宋, 0.8); + tr.TextStyleTable.Add("fsgb2312", FontTTF.仿宋GB2312, 0.8); + tr.TextStyleTable.Add("arial", FontTTF.Arial, 0.8); + tr.TextStyleTable.Add("romas", FontTTF.Romans, 0.8); + + + + tr.TextStyleTable.Add("daziti", ttr => + { + ttr.FileName = "ascii.shx"; + ttr.BigFontFileName = "gbcbig.shx"; + }); + } + + [CommandMethod("testtextstylechange")] + public void TestTextStyleChange() + { + using var tr = new DBTrans(); + + + tr.TextStyleTable.AddWithChange("宋体1", "simfang.ttf", height: 5); + tr.TextStyleTable.AddWithChange("仿宋体", "宋体.ttf"); + tr.TextStyleTable.AddWithChange("fsgb2312", "Romans", "gbcbig"); + } + } +} diff --git a/tests/Test/testConvexHull.cs b/tests/Test/testConvexHull.cs index ee792c6..c4b9496 100644 --- a/tests/Test/testConvexHull.cs +++ b/tests/Test/testConvexHull.cs @@ -1,21 +1,9 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; - -using Autodesk.AutoCAD.Runtime; -using Autodesk.AutoCAD.Geometry; -using IFoxCAD.Cad; -using Autodesk.AutoCAD.DatabaseServices; -using Autodesk.AutoCAD.Colors; - -namespace test +namespace Test { - public class testConvexHull + public class TestConvexHull { [CommandMethod("testch")] - public void testch() + public void Testch() { //using var tr = new DBTrans(); //var pts = new List(); @@ -23,7 +11,7 @@ public void testch() //while (flag) //{ // var pt = tr.Editor.GetPoint("qudian"); - // if (pt.Status == Autodesk.AutoCAD.EditorInput.PromptStatus.OK) + // if (pt.Status == PromptStatus.OK) // { // pts.Add(pt.Value); // tr.CurrentSpace.AddEntity(new DBPoint(pt.Value)); diff --git a/tests/Test/testblock.cs b/tests/Test/testblock.cs new file mode 100644 index 0000000..ef615be --- /dev/null +++ b/tests/Test/testblock.cs @@ -0,0 +1,631 @@ +namespace Test; +public class TestBlock +{ + //块定义 + [CommandMethod("blockdef")] + public void BlockDef() + { + using var tr = new DBTrans(); + //var line = new Line(new Point3d(0, 0, 0), new Point3d(1, 1, 0)); + tr.BlockTable.Add("test", + btr => { + btr.Origin = new Point3d(0, 0, 0); + }, + () => //图元 + { + return new List { new Line(new Point3d(0, 0, 0), new Point3d(1, 1, 0)) }; + }, + () => //属性定义 + { + var id1 = new AttributeDefinition() { Position = new Point3d(0, 0, 0), Tag = "start", Height = 0.2 }; + var id2 = new AttributeDefinition() { Position = new Point3d(1, 1, 0), Tag = "end", Height = 0.2 }; + return new List { id1, id2 }; + } + ); + //ObjectId objectId = tr.BlockTable.Add("a");//新建块 + //objectId.GetObject().AddEntity();//测试添加空实体 + tr.BlockTable.Add("test1", + btr => { + btr.Origin = new Point3d(0, 0, 0); + + }, + () => { + var line = new Line(new Point3d(0, 0, 0), new Point3d(1, 1, 0)); + var acText = new TextInfo("123", Point3d.Origin, AttachmentPoint.BaseLeft) + .AddDBTextToEntity(); + + return new List { line, acText }; + }); + } + //修改块定义 + [CommandMethod("blockdefchange")] + public void BlockDefChange() + { + using var tr = new DBTrans(); + //var line = new Line(new Point3d(0, 0, 0), new Point3d(1, 1, 0)); + //tr.BlockTable.Change("test", btr => + //{ + // btr.Origin = new Point3d(5, 5, 0); + // btr.AddEntity(new Circle(new Point3d(0, 0, 0), Vector3d.ZAxis, 2)); + // btr.GetEntities() + // .ToList() + // .ForEach(e => e.Flush()); //刷新块显示 + + //}); + + + + + tr.BlockTable.Change("test", btr => { + foreach (var id in btr) + { + var ent = tr.GetObject(id); + using (ent.ForWrite()) + { + if (ent is Dimension dBText) + { + dBText.DimensionText = "234"; + dBText.RecomputeDimensionBlock(true); + } + + if (ent is Hatch hatch) + { + hatch.ColorIndex = 0; + + } + + + } + + } + }); + tr.Editor.Regen(); + } + + [CommandMethod("insertblockdef")] + public void InsertBlockDef() + { + using var tr = new DBTrans(); + var line1 = new Line(new Point3d(0, 0, 0), new Point3d(1, 1, 0)); + var line2 = new Line(new Point3d(0, 0, 0), new Point3d(-1, 1, 0)); + var att1 = new AttributeDefinition() { Position = new Point3d(10, 10, 0), Tag = "tagTest1", Height = 1, TextString = "valueTest1" }; + var att2 = new AttributeDefinition() { Position = new Point3d(10, 12, 0), Tag = "tagTest2", Height = 1, TextString = "valueTest2" }; + tr.BlockTable.Add("test1", line1, line2, att1, att2); + + + var ents = new List(); + var line5 = new Line(new Point3d(0, 0, 0), new Point3d(1, 1, 0)); + var line6 = new Line(new Point3d(0, 0, 0), new Point3d(-1, 1, 0)); + ents.Add(line5); + ents.Add(line6); + tr.BlockTable.Add("test44", ents); + + + var line3 = new Line(new Point3d(5, 5, 0), new Point3d(6, 6, 0)); + var line4 = new Line(new Point3d(5, 5, 0), new Point3d(-6, 6, 0)); + var att3 = new AttributeDefinition() { Position = new Point3d(10, 14, 0), Tag = "tagTest3", Height = 1, TextString = "valueTest3" }; + var att4 = new AttributeDefinition() { Position = new Point3d(10, 16, 0), Tag = "tagTest4", Height = 1, TextString = "valueTest4" }; + tr.BlockTable.Add("test2", new List { line3, line4 }, new List { att3, att4 }); + //tr.CurrentSpace.InsertBlock(new Point3d(4, 4, 0), "test1"); // 测试默认 + //tr.CurrentSpace.InsertBlock(new Point3d(4, 4, 0), "test2"); + //tr.CurrentSpace.InsertBlock(new Point3d(4, 4, 0), "test3"); //测试插入不存在的块定义 + //tr.CurrentSpace.InsertBlock(new Point3d(0, 0, 0), "test1", new Scale3d(2)); // 测试放大2倍 + //tr.CurrentSpace.InsertBlock(new Point3d(4, 4, 0), "test1", new Scale3d(2), Math.PI / 4); // 测试放大2倍,旋转45度 + + var def1 = new Dictionary + { + { "tagTest1", "1" }, + { "tagTest2", "2" } + }; + tr.CurrentSpace.InsertBlock(new Point3d(0, 0, 0), "test1", atts: def1); + var def2 = new Dictionary + { + { "tagTest3", "1" }, + { "tagTest4", "" } + }; + tr.CurrentSpace.InsertBlock(new Point3d(10, 10, 0), "test2", atts: def2); + tr.CurrentSpace.InsertBlock(new Point3d(-10, 0, 0), "test44"); + } + + [CommandMethod("addattsdef")] + public void AddAttsDef() + { + using var tr = new DBTrans(); + var blockid = Env.Editor.GetEntity("pick block:").ObjectId; + var blockref = tr.GetObject(blockid).BlockTableRecord; + + var att1 = new AttributeDefinition() { Position = new Point3d(20, 20, 0), Tag = "addtagTest1", Height = 1, TextString = "valueTest1" }; + var att2 = new AttributeDefinition() { Position = new Point3d(10, 12, 0), Tag = "tagTest2", Height = 1, TextString = "valueTest2" }; + + tr.BlockTable.AddAttsToBlocks(blockref, new List { att1, att2 }); + } + + [CommandMethod("testblocknullbug")] + public void TestBlockNullBug() + { + using var tr = new DBTrans(); + + var ents = new List(); + var line5 = new Line(new Point3d(0, 0, 0), new Point3d(1, 1, 0)); + var line6 = new Line(new Point3d(0, 0, 0), new Point3d(-1, 1, 0)); + ents.Add(line5); + ents.Add(line6); + tr.BlockTable.Add("test44", ents); + tr.CurrentSpace.InsertBlock(new Point3d(0, 0, 0), "test44"); + } + + [CommandMethod("test_block_file")] + public void TestBlockFile() + { + var tr = new DBTrans(); + var id = tr.BlockTable.GetBlockFrom(@"C:\Users\vic\Desktop\test.dwg", false); + tr.CurrentSpace.InsertBlock(Point3d.Origin, id); + } + + + [CommandMethod("testclip")] + public void TestClipBlock() + { + using var tr = new DBTrans(); + tr.BlockTable.Add("test1", + btr => { + btr.Origin = new Point3d(0, 0, 0); + btr.AddEntity(new Line(new Point3d(0, 0, 0), new Point3d(10, 10, 0)), + new Line(new Point3d(10, 10, 0), new Point3d(10, 0, 0)) + ); + } + ); + //tr.BlockTable.Add("hah"); + var id = tr.CurrentSpace.InsertBlock(new Point3d(0, 0, 0), "test1"); + var bref = tr.GetObject(id); + var pts = new List { new Point3d(3, 3, 0), new Point3d(7, 3, 0), new Point3d(7, 7, 0), new Point3d(3, 7, 0) }; + bref.ClipBlockRef(pts); + + var id1 = tr.CurrentSpace.InsertBlock(new Point3d(20, 20, 0), "test1"); + var bref1 = tr.GetObject(id); + + bref1.ClipBlockRef(new Point3d(13, 13, 0), new Point3d(17, 17, 0)); + } + + /// + /// 给用户的测试程序,不知道对错 + /// + [CommandMethod("test_block_ej")] + public void EJ() + { + using (var tr = new DBTrans()) + { + + //Point3d.Origin.AddBellowToModelSpace(100, 100, 5, 3, 30);//画波纹管 + + //Database db2 = new Database(false, true); + //string fullFileName = @".\MyBlockDwgFile\001.dwg"; + //db2.ReadDwgFile(fullFileName, System.IO.FileShare.Read, true, null); + //db2.CloseInput(true); + //string blockName = "test"; + //if (!tr.BlockTable.Has(blockName)) + //{ + // //tr.Database.Insert(blockName, db2, false);//插入块 + // db.Insert(blockName, db2, false); + + //} + + string fullFileName = @"C:\Users\vic\Desktop\001.dwg"; + var blockdef = tr.BlockTable.GetBlockFrom(fullFileName, false); + + tr.Database.Clayer = tr.LayerTable["0"];//当前图层切换为0图层 + tr.LayerTable.Change(tr.Database.Clayer, ltr => { + ltr.Color = Color.FromColorIndex(ColorMethod.ByAci, 2); //ColorMethod.ByAci可以让我们使用AutoCAD ACI颜色索引……这里为2(表示黄色) + }); + + ObjectId id = tr.ModelSpace.InsertBlock(Point3d.Origin, blockdef);//插入块参照 + + + + + + var entTest = tr.GetObject(id); + entTest.Draw(); + + } + + using var tr2 = new DBTrans(); + PromptEntityOptions PEO = new("\n请选择一个块"); + PEO.SetRejectMessage("\n对象必须是块"); + PEO.AddAllowedClass(typeof(BlockReference), true); + + PromptEntityResult PER = Env.Editor.GetEntity(PEO); + if (PER.Status != PromptStatus.OK) + { + return; + } + + var Bref = tr2.GetObject(PER.ObjectId); + //var BTR = tr.GetObject(Bref.BlockTableRecord, OpenMode.ForWrite); + ////如果知道块名字BTRName + //BlockTableRecord BTR = tr.GetObject(tr.BlockTable[blockName], OpenMode.ForWrite); + + var btr = tr2.BlockTable[Bref.Name]; + + tr2.BlockTable.Change(btr, ltr => { + + foreach (ObjectId OID in ltr) + { + var Ent = tr2.GetObject(OID); + using (Ent.ForWrite()) + { + if (Ent is MText mText) + { + switch (mText.Text) + { + case "$$A": + mText.Contents = "hahaha"; + break; + case "$$B": + ; + break; + default: + ; + break; + } + + }; + if (Ent is DBText dBText) { dBText.TextString = "haha"; }; + if (Ent is Dimension dimension) + { + switch (dimension.DimensionText) + { + case "$$pipeLen": + dimension.DimensionText = "350"; + dimension.RecomputeDimensionBlock(true); + break; + default: + break; + } + }; + } + + + } + + }); + + + tr2.Editor.Regen(); + + + } + + [CommandMethod("W_KSZK")] + public void QuickBlockDef() + { + //Database db = HostApplicationServices.WorkingDatabase; + Editor ed = Application.DocumentManager.MdiActiveDocument.Editor; + PromptSelectionOptions promptOpt = new() + { + MessageForAdding = "请选择需要快速制作块的对象" + }; + string blockName = "W_BLOCK_" + DateTime.Now.ToString("yyyyMMdd_HHmmss"); + //var rss = ed.GetSelection(promptOpt); + var rss = Env.Editor.GetSelection(promptOpt); + using var tr = new DBTrans(); + if (rss.Status == PromptStatus.OK) + { + //SelectionSet ss = rss.Value; + //ObjectId[] ids = ss.GetObjectIds(); + //var ents = new List>(); + //var extents = new Extents3d(); + //foreach (var id in ids) + //{ + // Entity ent = tr.GetObject(id); + // if (ent is null) + // continue; + // try + // { + // extents.AddExtents(ent.GeometricExtents); + // var order = id.Handle.Value; + // var newEnt = ent.Clone() as Entity; + // ents.Add(new KeyValuePair(newEnt, order)); + // ent.UpgradeOpen(); + // ent.Erase(); + // ent.DowngradeOpen(); + // } + // catch (System.Exception exc) + // { + // ed.WriteMessage(exc.Message); + // } + //} + //ents = ents.OrderBy(x => x.Value).ToList(); + var ents = rss.Value.GetEntities(); + //ents.ForEach(ent => extents.AddExtents(ent.GeometricExtents)); + var extents = ents.GetExtents(); + + Point3d pt = extents.MinPoint; + Matrix3d matrix = Matrix3d.Displacement(Point3d.Origin - pt); + //var newEnts = new List(); + //foreach (var ent in ents) + //{ + // var newEnt = ent.Key; + // newEnt.TransformBy(matrix); + // newEnts.Add(newEnt); + //} + //if (tr.BlockTable.Has(blockName)) + //{ + // Application.ShowAlertDialog(Environment.NewLine + "块名重复,程序退出!"); + // return; + //} + ents.ForEach(ent => + ent.ForWrite(e => e.TransformBy(matrix))); + //var newents = ents.Select(ent => + //{ + // var maping = new IdMapping(); + // return ent.DeepClone(ent, maping, true) as Entity; + //}); + var newents = ents.Select(ent => ent.Clone() as Entity); + + //ents.ForEach(ent => ent.ForWrite(e => e.Erase(true))); // 删除实体就会卡死,比较奇怪,估计是Clone()函数的问题 + // 经过测试不是删除的问题 + var btrId = tr.BlockTable.Add(blockName, newents); + ents.ForEach(ent => ent.ForWrite(e => e.Erase(true))); + var bId = tr.CurrentSpace.InsertBlock(pt, blockName); + //tr.GetObject(bId, OpenMode.ForWrite).Move(Point3d.Origin, Point3d.Origin); + //var ed = Application.DocumentManager.MdiActiveDocument.Editor; + //ed.Regen(); + //tr.Editor.Regen(); + // 调用regen() 卡死 + } + //tr.Editor.Regen(); + //ed.Regen(); + //using (var tr = new DBTrans()) + //{ + // tr.CurrentSpace.InsertBlock(Point3d.Origin, blockName); + // tr.Editor.Regen(); + //} + } + + [CommandMethod("testquickblockdef")] + public void TestQuickBlockDef() + { + Database db = HostApplicationServices.WorkingDatabase; + Editor ed = Application.DocumentManager.MdiActiveDocument.Editor; + PromptSelectionOptions promptOpt = new() + { + MessageForAdding = "请选择需要快速制作块的对象" + }; + string blockName = "W_BLOCK_" + DateTime.Now.ToString("yyyyMMdd_HHmmss"); + var rss = ed.GetSelection(promptOpt); + //var rss = Env.Editor.GetSelection(promptOpt); + if (rss.Status != PromptStatus.OK) + { + return; + } + + using var tr = db.TransactionManager.StartTransaction(); + var ids = rss.Value.GetObjectIds(); + var bt = tr.GetObject(db.BlockTableId, OpenMode.ForRead) as BlockTable; + var btr = new BlockTableRecord + { + Name = blockName + }; + foreach (var item in ids) + { + var ent = tr.GetObject(item, OpenMode.ForRead) as Entity; + + btr.AppendEntity(ent.Clone() as Entity); + ent.ForWrite(e => e.Erase(true)); + } + bt.UpgradeOpen(); + bt.Add(btr); + tr.AddNewlyCreatedDBObject(btr, true); + bt.DowngradeOpen(); + // tr.Commit(); + //} + + //using (var tr1 = db.TransactionManager.StartTransaction()) + //{ + //var bt = tr1.GetObject(db.BlockTableId, OpenMode.ForRead) as BlockTable; + var btr1 = tr.GetObject(db.CurrentSpaceId, OpenMode.ForWrite) as BlockTableRecord; + var br = new BlockReference(Point3d.Origin, bt[blockName]) + { + ScaleFactors = default + }; + btr1.AppendEntity(br); + tr.AddNewlyCreatedDBObject(br, true); + btr1.DowngradeOpen(); + ed.Regen(); + tr.Commit(); + //ed.Regen(); + + } + + public void TestWblock() + { + var curdb = HostApplicationServices.WorkingDatabase; + PromptSelectionOptions opts = new(); + opts.MessageForAdding = "选择对象"; + var ss = Env.Editor.GetSelection(opts).Value; + var ids = new ObjectIdCollection(ss.GetObjectIds()); + var db = curdb.Wblock(ids, Point3d.Origin); + db.SaveAs(@"c:\test.dwg", DwgVersion.Current); + } + + public void TestChangeDynameicBlock() + { + var pro = new Dictionary + { + { "haha", 1 } + }; + var blockid = Env.Editor.GetEntity("选择个块").ObjectId; + using var tr = new DBTrans(); + var blockref = tr.GetObject(blockid); + blockref.ChangeBlockProperty(pro); + // 这是第一个函数的用法 + } + + [CommandMethod("TestBack")] + public void TestBack() + { + string dir = Environment.GetFolderPath(Environment.SpecialFolder.DesktopDirectory); + string dwg = dir + "\\test.dwg"; + if (!File.Exists(dwg)) + { + System.Windows.Forms.MessageBox.Show(dwg, "你还没有创建此文件"); + return; + } + + using var tr = new DBTrans(dwg); + tr.ModelSpace.GetEntities().ForEach(ent => { + ent.ForWrite(e => e.ColorIndex = 3); + }); + tr.Database.SaveAs(dwg, DwgVersion.Current); + + tr.ModelSpace.GetEntities().ForEach(ent => { + ent.ForWrite(e => e.ColorIndex = 4); + }); + tr.Database.SaveAs(dwg, DwgVersion.Current); + + } + +} + +public class BlockImportClass +{ + + [CommandMethod("CBLL")] + public void Cbll() + { + string filename = @"C:\Users\vic\Desktop\Drawing1.dwg"; + using var tr = new DBTrans(); + using var tr1 = new DBTrans(filename); + //tr.BlockTable.GetBlockFrom(filename, true); + string blkdefname = SymbolUtilityServices.RepairSymbolName(SymbolUtilityServices.GetSymbolNameFromPathName(filename, "dwg"), false); + tr.Database.Insert(blkdefname, tr1.Database, false); //插入了块定义,未插入块参照 + } + + + [CommandMethod("CBL")] + public void CombineBlocksIntoLibrary() + { + Document doc = + Application.DocumentManager.MdiActiveDocument; + Editor ed = doc.Editor; + Database destDb = doc.Database; + + // Get name of folder from which to load and import blocks + + PromptResult pr = + ed.GetString("\nEnter the folder of source drawings: "); + + if (pr.Status != PromptStatus.OK) + return; + string pathName = pr.StringResult; + + // Check the folder exists + + if (!Directory.Exists(pathName)) + { + ed.WriteMessage( + "\nDirectory does not exist: {0}", pathName + ); + return; + } + + // Get the names of our DWG files in that folder + + string[] fileNames = Directory.GetFiles(pathName, "*.dwg"); + + // A counter for the files we've imported + + int imported = 0, failed = 0; + + // For each file in our list + + foreach (string fileName in fileNames) + { + // Double-check we have a DWG file (probably unnecessary) + + if (fileName.EndsWith( + ".dwg", + StringComparison.InvariantCultureIgnoreCase + ) + ) + { + // Catch exceptions at the file level to allow skipping + + try + { + // Suggestion from Thorsten Meinecke... + + string destName = + SymbolUtilityServices.GetSymbolNameFromPathName( + fileName, "dwg" + ); + + // And from Dan Glassman... + + destName = + SymbolUtilityServices.RepairSymbolName( + destName, false + ); + + // Create a source database to load the DWG into + + using Database db = new(false, true); + // Read the DWG into our side database + + db.ReadDwgFile(fileName, FileShare.Read, true, ""); + bool isAnno = db.AnnotativeDwg; + + // Insert it into the destination database as + // a named block definition + + ObjectId btrId = destDb.Insert( + destName, + db, + false + ); + + if (isAnno) + { + // If an annotative block, open the resultant BTR + // and set its annotative definition status + + Transaction tr = + destDb.TransactionManager.StartTransaction(); + using (tr) + { + BlockTableRecord btr = + (BlockTableRecord)tr.GetObject( + btrId, + OpenMode.ForWrite + ); + btr.Annotative = AnnotativeStates.True; + tr.Commit(); + } + } + + // Print message and increment imported block counter + + ed.WriteMessage("\nImported from \"{0}\".", fileName); + imported++; + } + catch (System.Exception ex) + { + ed.WriteMessage( + "\nProblem importing \"{0}\": {1} - file skipped.", + fileName, ex.Message + ); + failed++; + } + } + } + + ed.WriteMessage( + "\nImported block definitions from {0} files{1} in " + + "\"{2}\" into the current drawing.", + imported, + failed > 0 ? " (" + failed + " failed)" : "", + pathName + ); + } +} \ No newline at end of file diff --git a/tests/Test/testeditor.cs b/tests/Test/testeditor.cs index 397d5c4..7d610cf 100644 --- a/tests/Test/testeditor.cs +++ b/tests/Test/testeditor.cs @@ -1,38 +1,69 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; -using Autodesk.AutoCAD.ApplicationServices; -using Autodesk.AutoCAD.DatabaseServices; -using Autodesk.AutoCAD.EditorInput; -using Autodesk.AutoCAD.Geometry; -using Autodesk.AutoCAD.Runtime; -using IFoxCAD.Cad; -namespace test +namespace Test; + +public class Testeditor { - public class testeditor + [CommandMethod("tested")] + public void Tested() + { + var pts = new List + { + new Point2d(0,0), + new Point2d(0,1), + new Point2d(1,1), + new Point2d(1,0) + }; + var res = EditorEx.GetLines(pts, false); + var res1 = EditorEx.GetLines(pts, true); + var res2 = pts.Select(pt => new TypedValue((int)LispDataType.Point2d, pt)).ToList(); + + Editor ed = Application.DocumentManager.MdiActiveDocument.Editor; + var pt = ed.GetPoint("qudiam", new Point3d(0, 0, 0)); + var d = ed.GetDouble("qudoule"); + var i = ed.GetInteger("quint"); + var s = ed.GetString("qustr"); + Env.Editor.WriteMessage(""); + } + [CommandMethod("testzoom")] + public void Testzoom() { - [CommandMethod("tested")] - public void tested() + using var tr = new DBTrans(); + var res = Env.Editor.GetEntity("\npick ent:"); + if (res.Status == PromptStatus.OK) { - var pts = new List - { - new Point2d(0,0), - new Point2d(0,1), - new Point2d(1,1), - new Point2d(1,0) - }; - var res = EditorEx.GetLines(pts, false); - var res1 = EditorEx.GetLines(pts, true); - var res2 = pts.Select(pt => new TypedValue((int)LispDataType.Point2d, pt)).ToList(); - - Editor ed = Application.DocumentManager.MdiActiveDocument.Editor; - var pt = ed.GetPoint("qudiam", new Point3d(0, 0, 0)); - var d = ed.GetDouble("qudoule"); - var i = ed.GetInteger("quint"); - var s = ed.GetString("qustr"); - Env.Editor.WriteMessage(""); + Env.Editor.ZoomObject(res.ObjectId.GetObject()); } + + + } + [CommandMethod("testzoomextent")] + public void Testzoomextent() + { + //using var tr = new DBTrans(); + //var res = Env.Editor.GetEntity("\npick ent:"); + //if (res.Status == PromptStatus.OK) + //{ + // Env.Editor.ZoomObject(res.ObjectId.GetObject()); + //} + + Env.Editor.ZoomExtents(); + } + + [CommandMethod("testssget")] + public void Testssget() + { + var action_a = () => { Env.Print("this is a"); }; + var action_b = () => { Env.Print("this is b"); }; + + var keyword = new Dictionary + { + { "A", action_a }, + { "B", action_b } + }; + + var ss = Env.Editor.SSGet( ":S", + messages: new string[2] { "get", "del" }, + keywords: keyword); + + Env.Print(ss); } } diff --git a/tests/Test/testenv.cs b/tests/Test/testenv.cs index 6cd7c9b..05ffc5c 100644 --- a/tests/Test/testenv.cs +++ b/tests/Test/testenv.cs @@ -1,71 +1,89 @@ -using IFoxCAD.Cad; -using Autodesk.AutoCAD.Runtime; -using Autodesk.AutoCAD.ApplicationServices; -using Autodesk.AutoCAD.DatabaseServices; +namespace Test; -namespace test +public class Testenv { - public class testenv + [CommandMethod("testenum")] + public void Testenum() { - [CommandMethod("testenum")] - public void testenum() - { - Env.CmdEcho = true; - } - [CommandMethod("testenum1")] - public void testenum1() - { - Env.CmdEcho = false; - } + + Env.CmdEcho = true; + + } + [CommandMethod("testenum1")] + public void Testenum1() + { + + Env.CmdEcho = false; + + } - [CommandMethod("testdimblk")] - public void testdimblk() - { + [CommandMethod("testdimblk")] + public void Testdimblk() + { - Env.Dimblk = Env.DimblkType.Dot; - Env.Dimblk = Env.DimblkType.Defult; + Env.Dimblk = Env.DimblkType.Dot; + Env.Dimblk = Env.DimblkType.Defult; + Env.Dimblk = Env.DimblkType.Oblique; - } - [CommandMethod("testdimblk1")] - public void testdimblk1() - { - var dim = Env.Dimblk; - Env.Editor.WriteMessage(dim.ToString()); + } + [CommandMethod("testdimblk1")] + public void Testdimblk1() + { + var dim = Env.Dimblk; + Env.Editor.WriteMessage(dim.ToString()); - } + } - [CommandMethod("testosmode")] - public void testosmode() - { - // 设置osmode变量,多个值用逻辑或 - Env.OSMode = Env.OSModeType.End | Env.OSModeType.Middle; - // 也可以直接写数值,进行强转 - Env.OSMode = (Env.OSModeType)5179; - // 追加模式 - Env.OSMode |= Env.OSModeType.Center; - //检查是否有某个模式 - var os = Env.OSMode.Include(Env.OSModeType.Center); - // 取消某个模式 - Env.OSMode ^= Env.OSModeType.Center; - Env.Editor.WriteMessage(Env.OSMode.ToString()); - } - [CommandMethod("testosmode1")] - public void testosmode1() - { - var dim = Env.OSMode; - Env.Editor.WriteMessage(dim.ToString()); + [CommandMethod("testosmode")] + public void Testosmode() + { + // 设置osmode变量,多个值用逻辑或 + Env.OSMode = Env.OSModeType.End | Env.OSModeType.Middle; + // 也可以直接写数值,进行强转 + Env.OSMode = (Env.OSModeType)5179; + // 追加模式 + Env.OSMode |= Env.OSModeType.Center; + //检查是否有某个模式 + var os = Env.OSMode.Include(Env.OSModeType.Center); + // 取消某个模式 + Env.OSMode ^= Env.OSModeType.Center; + Env.Editor.WriteMessage(Env.OSMode.ToString()); + } + [CommandMethod("testosmode1")] + public void Testosmode1() + { + var dim = Env.OSMode; + Env.Editor.WriteMessage(dim.ToString()); - } + } + + [CommandMethod("testcadver")] + public void Testcadver() + { + //Env.Print(AcadVersion.Versions); + AcadVersion.Versions.ForEach(v => Env.Print(v)); + AcadVersion.FromApp(Application.AcadApplication).Print(); + 1.Print(); + "1".Print(); + + } - [CommandMethod("testzoom")] - public void testzoom() - { - using var tr = new DBTrans(); - var res = Env.Editor.GetEntity("\npick ent:"); - if (res.Status == Autodesk.AutoCAD.EditorInput.PromptStatus.OK) - { - Env.Editor.ZoomObject(res.ObjectId.GetObject()); - } - } + [CommandMethod("TestGetVar")] + public void TestGetVar() + { + // test getvar + var a = Env.GetVar("dbmod"); + a.Print(); + Env.SetVar("dbmod1", 1); + } + + [CommandMethod("TestDwgVersion")] + public void TestDwgVersion() + { + // + //string filename = @"C:\Users\vic\Desktop\test.dwg"; + //var a = Helper.GetCadFileVersion(filename); + //a.Print(); + //((DwgVersion)a).Print(); } } diff --git a/tests/Test/testselectfilter.cs b/tests/Test/testselectfilter.cs index 9b658ee..d4056c9 100644 --- a/tests/Test/testselectfilter.cs +++ b/tests/Test/testselectfilter.cs @@ -1,29 +1,17 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; - -using Autodesk.AutoCAD.Geometry; -using Autodesk.AutoCAD.Runtime; -using Autodesk.AutoCAD.DatabaseServices; -using IFoxCAD.Cad; -using Autodesk.AutoCAD.EditorInput; - -namespace test +namespace Test { - public class testselectfilter + public class Testselectfilter { [CommandMethod("testfilter")] - public void testfilter() + public void Testfilter() { - + var p = new Point3d(10, 10, 0); var f = OpFilter.Bulid( - e => !(e.Dxf(0) == "line" & e.Dxf(8) == "0") + e =>!(e.Dxf(0) == "line" & e.Dxf(8) == "0") | e.Dxf(0) != "circle" & e.Dxf(8) == "2" & e.Dxf(10) >= p); - - + + var f2 = OpFilter.Bulid( e => e.Or( !e.And(e.Dxf(0) == "line", e.Dxf(8) == "0"), @@ -37,7 +25,7 @@ public void testfilter() } [CommandMethod("testselectanpoint")] - public void testselectanpoint() + public void Testselectanpoint() { var sel2 = Env.Editor.SelectAtPoint(new Point3d(0, 0, 0)); Env.Editor.WriteMessage(""); diff --git a/tests/Test/wpf/Class1.cs b/tests/Test/wpf/Class1.cs new file mode 100644 index 0000000..4358668 --- /dev/null +++ b/tests/Test/wpf/Class1.cs @@ -0,0 +1,13 @@ +namespace Test.wpf +{ + public class Class1 + { + [CommandMethod("testwpf")] + public void TestWPf() + { + + var test = new TestView(); + Application.ShowModalWindow(test); + } + } +} diff --git a/tests/Test/wpf/TestView.xaml b/tests/Test/wpf/TestView.xaml index 42cb304..bd75779 100644 --- a/tests/Test/wpf/TestView.xaml +++ b/tests/Test/wpf/TestView.xaml @@ -1,9 +1,9 @@ - /// TestView.xaml 的交互逻辑 diff --git a/tests/Test/wpf/TestViewModel.cs b/tests/Test/wpf/TestViewModel.cs index dc6fce0..4cb7563 100644 --- a/tests/Test/wpf/TestViewModel.cs +++ b/tests/Test/wpf/TestViewModel.cs @@ -1,119 +1,113 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; + using System.Windows; using System.Windows.Input; -using IFoxCAD.WPF; -namespace test.wpf +namespace Test.wpf; + +class TestViewModel : ViewModelBase { - class TestViewModel : ViewModelBase - { - - private string name; + + private string name; - public string Name - { - get { return name; } - set { Set(ref name, value); } - } + public string Name + { + get { return name; } + set { Set(ref name, value); } + } - private RelayCommand clickCommand; + private RelayCommand clickCommand; - public RelayCommand ClickCommand + public RelayCommand ClickCommand + { + get { - get + if (clickCommand is null) { - if (clickCommand is null) - { - clickCommand = new( - execute => Name = "hello " + Name, - can => !string.IsNullOrEmpty(Name)); - } - return clickCommand; + clickCommand = new( + execute => Name = "hello " + Name, + can => !string.IsNullOrEmpty(Name)); } + return clickCommand; } + } - private bool receiveMouseMove; + private bool receiveMouseMove; - public bool ReceiveMouseMove - { - get { return receiveMouseMove; } - set { Set(ref receiveMouseMove, value); } - } + public bool ReceiveMouseMove + { + get { return receiveMouseMove; } + set { Set(ref receiveMouseMove, value); } + } - private string tipText; + private string tipText; - public string TipText - { - get { return tipText; } - set { Set(ref tipText, value); } - } + public string TipText + { + get { return tipText; } + set { Set(ref tipText, value); } + } - private RelayCommand loadedCommand; - public RelayCommand LoadCommand + private RelayCommand loadedCommand; + public RelayCommand LoadCommand + { + get { - get + if (loadedCommand is null) { - if (loadedCommand is null) - { - loadedCommand = new( - execute => MessageBox.Show("程序加载完毕")); - } - return loadedCommand; + loadedCommand = new( + execute => MessageBox.Show("程序加载完毕")); } + return loadedCommand; } + } - private RelayCommand mouseMoveCommand; + private RelayCommand mouseMoveCommand; - public RelayCommand MouseMoveCommand + public RelayCommand MouseMoveCommand + { + get { - get + if (mouseMoveCommand is null) { - if (mouseMoveCommand is null) - { - mouseMoveCommand = new( - execute => + mouseMoveCommand = new( + execute => + { + var pt = execute.GetPosition(execute.Device.Target); + var left = "左键放开"; + var mid = "中键放开"; + var right = "右键放开"; + + if (execute.LeftButton == MouseButtonState.Pressed) + { + left = "左键放下"; + } + if (execute.MiddleButton == MouseButtonState.Pressed) + { + mid = "中键放下"; + } + if (execute.RightButton == MouseButtonState.Pressed) { - var pt = execute.GetPosition(execute.Device.Target); - var left = "左键放开"; - var mid = "中键放开"; - var right = "右键放开"; - - if (execute.LeftButton == MouseButtonState.Pressed) - { - left = "左键放下"; - } - if (execute.MiddleButton == MouseButtonState.Pressed) - { - mid = "中键放下"; - } - if (execute.RightButton == MouseButtonState.Pressed) - { - right = "右键放下"; - } - TipText = $"当前鼠标位置:X={pt.X},Y={pt.Y}。当前鼠标状态:{left}、{mid}、{right}"; - }, - can => ReceiveMouseMove); - } - return mouseMoveCommand; + right = "右键放下"; + } + TipText = $"当前鼠标位置:X={pt.X},Y={pt.Y}。当前鼠标状态:{left}、{mid}、{right}"; + }, + can => ReceiveMouseMove); } + return mouseMoveCommand; } + } - public TestViewModel() - { - Name = "world"; - } + public TestViewModel() + { + Name = "world"; + } - } } diff --git a/tests/TestConsole/Program.cs b/tests/TestConsole/Program.cs new file mode 100644 index 0000000..aa31926 --- /dev/null +++ b/tests/TestConsole/Program.cs @@ -0,0 +1,55 @@ +// See https://aka.ms/new-console-template for more information +using System; +using System.Text; + + +//表达式树例子 +TestConsole.Test_Expression.Demo3(); +//TestConsole.Test_Expression.Demo1(); + +#region 元组测试 +var valuetuple = (1, 2); + +Console.WriteLine(valuetuple.ToString()); + +int[] someArray = new int[5] { 1, 2, 3, 4, 5 }; +int lastElement = someArray[^1]; // lastElement = 5 +Console.WriteLine(lastElement); +int midElement = someArray[^3]; +Console.WriteLine(midElement); +var range = someArray[1..3]; +foreach (var item in range) + Console.WriteLine(item); +#endregion + +Console.ReadLine(); + + +#region 测试遍历枚举 +//Season a = Season.Autumn; +//Console.WriteLine($"Integral value of {a} is {(int)a}"); // output: Integral value of Autumn is 2 +//foreach (var enumItem in Enum.GetValues(typeof(Season))) +// Console.WriteLine((byte)enumItem); + +var sb = new StringBuilder(); +/*因为 net framework 没写好的原因,导致直接使用迭代器反而更慢,到了net60就迭代器比foreach更快*/ +var enums = Enum.GetValues(typeof(Season)).GetEnumerator(); +while (enums.MoveNext()) +{ + sb.Append(((byte)enums.Current).ToString()); + sb.Append(","); +} +Console.WriteLine(sb); + +sb.Remove(sb.Length - 1, 1);//剔除末尾, +//因为有返回值所以容易理解成 sb = sb.Remove(sb.Length - 1, 1); +Console.WriteLine(sb); + +public enum Season : byte +{ + Spring, + Summer, + Autumn, + Winter +} +#endregion \ No newline at end of file diff --git a/tests/TestConsole/RuntimeHelpers.cs b/tests/TestConsole/RuntimeHelpers.cs new file mode 100644 index 0000000..9e09b29 --- /dev/null +++ b/tests/TestConsole/RuntimeHelpers.cs @@ -0,0 +1,50 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +// 如果要用range的语法比如 a[1..3],那么需要将本文件复制到你的项目里 +#if true +namespace System.Runtime.CompilerServices +{ + internal static class RuntimeHelpers + { + /// + /// Slices the specified array using the specified range. + /// + public static T[] GetSubArray(T[] array, Range range) + { + if (array == null) + { + throw new ArgumentNullException(); + } + + (int offset, int length) = range.GetOffsetAndLength(array.Length); + + if (default(T)! != null || typeof(T[]) == array.GetType()) // TODO-NULLABLE: default(T) == null warning (https://github.com/dotnet/roslyn/issues/34757) + { + // We know the type of the array to be exactly T[]. + + if (length == 0) + { + //return Array.Empty(); + return new T[0]; + + + } + + var dest = new T[length]; + Array.Copy(array, offset, dest, 0, length); + return dest; + } + else + { + // The array is actually a U[] where U:T. + T[] dest = (T[])Array.CreateInstance(array.GetType().GetElementType()!, length); + Array.Copy(array, offset, dest, 0, length); + return dest; + } + } + } +} + +#endif \ No newline at end of file diff --git a/tests/TestConsole/TestConsole.csproj b/tests/TestConsole/TestConsole.csproj new file mode 100644 index 0000000..293a6aa --- /dev/null +++ b/tests/TestConsole/TestConsole.csproj @@ -0,0 +1,22 @@ + + + + preview + enable + + Exe + net45 + enable + preview + + + + + + + + + + + + diff --git "a/tests/TestConsole/\350\241\250\350\276\276\345\274\217\346\240\221.cs" "b/tests/TestConsole/\350\241\250\350\276\276\345\274\217\346\240\221.cs" new file mode 100644 index 0000000..f5cc477 --- /dev/null +++ "b/tests/TestConsole/\350\241\250\350\276\276\345\274\217\346\240\221.cs" @@ -0,0 +1,143 @@ +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.Linq; +using System.Linq.Expressions; + +namespace TestConsole +{ + /// + /// 表达式树 + /// MSDN链接 + /// + public class Test_Expression + { + public static void Demo1() + { + // 官方例子:表达式体内只有一个式子 + // 创建表达式树 + Expression> exprTree = num => num < 5; + + // 分解表达式树 + ParameterExpression param = exprTree.Parameters[0];//num + BinaryExpression operation = (BinaryExpression)exprTree.Body;//函数体 {(num < 5)} + ParameterExpression left = (ParameterExpression)operation.Left;//左节点 num + ConstantExpression right = (ConstantExpression)operation.Right;//右表达式 5 + + Console.WriteLine("表达式树例子: {0} => {1} {2} {3}", + param.Name, left.Name, operation.NodeType, right.Value); + Console.Read(); + } + + + public static void Demo2() + { + // 这里是会报错的!! 原因就是体内有多个例子需要分解!! + // Expression> exprTree = x => x > 5 && x < 50; + // + // // 分解表达式树 + // ParameterExpression param = exprTree.Parameters[0];// x + // BinaryExpression operation = (BinaryExpression)exprTree.Body;//函数体 {((x > 5) AndAlso (x < 50))} + // + // ParameterExpression left = (ParameterExpression)operation.Left;//左节点.......这里报错 + // ConstantExpression right = (ConstantExpression)operation.Right;//右表达式.....这里报错 + // + // Console.WriteLine("表达式树例子: {0} => {1} {2} {3}", + // param.Name, left.Name, operation.NodeType, right.Value); + } + + + //博客园例子,表达式体内有多个式子 + public static void Demo3() + { + List names = new() { "Cai", "Edward", "Beauty" }; + + Console.WriteLine("******************一个表达式"); + Expression> lambda2 = name => name.Length > 2 && name.Length < 4; + var method2 = ReBuildExpression(lambda2); + var query2 = names.Where(method2); + foreach (string n in query2) + Console.WriteLine(n); + + Console.WriteLine("******************二个表达式"); + Expression> lambda0 = item => item.Length > 2; + Expression> lambda1 = item => item.Length < 4; + var method = ReBuildExpression(lambda0, lambda1); + var query = names.Where(method); + foreach (string n in query) + Console.WriteLine(n); + Console.WriteLine("******************表达式结束"); + Console.Read(); + } + + + static Func ReBuildExpression(Expression> lambda) + { + MyExpressionVisitor my = new() + { + Parameter = Expression.Parameter(typeof(string), "name") + }; + + Expression left = my.Modify(lambda.Body); + //构造一个新的表达式 + var newLambda = Expression.Lambda>(left, my.Parameter); + return newLambda.Compile(); + } + + + + /// + /// 重构表达式_合并 + /// + /// 匿名函数表达式1 + /// 匿名函数表达式2 + /// + static Func ReBuildExpression(Expression> lambda0, + Expression> lambda1) + { + MyExpressionVisitor my = new() + { + Parameter = Expression.Parameter(typeof(string), "name") + }; + + Expression left = my.Modify(lambda0.Body); + Expression right = my.Modify(lambda1.Body); + var expression = Expression.AndAlso(left, right);//就是 && 合并两个匿名函数 + + //构造一个新的表达式 + var newLambda = Expression.Lambda>(expression, my.Parameter); + return newLambda.Compile(); + } + } + + + /// + /// 表达式参数分解 + /// 博客园链接 + /// + public class MyExpressionVisitor : ExpressionVisitor + { + /// + /// 公共参数 + /// + public ParameterExpression? Parameter; + /// + /// 返回替换后的参数表达式 + /// + /// + /// + public Expression Modify(Expression exp) + { + return Visit(exp); + } + /// + /// 重写参数 + /// + /// + /// + protected override Expression? VisitParameter(ParameterExpression p) + { + return Parameter; + } + } +} -- Gitee From bee1a423e5ca2bc5e64c3b0ab2d8bf0c79dc9325 Mon Sep 17 00:00:00 2001 From: liuqihong <540762622@qq.com> Date: Sat, 13 Aug 2022 00:06:22 +0800 Subject: [PATCH 351/675] =?UTF-8?q?=E5=88=A0=E9=99=A4=E5=A4=9A=E4=BD=99?= =?UTF-8?q?=E7=9A=84.data=E6=96=87=E4=BB=B6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/IFoxCAD.Cad/IFoxCAD.Cad.csproj | 1 - src/IFoxCAD.Cad/IFoxCAD.Cad.csproj.data | 0 2 files changed, 1 deletion(-) delete mode 100644 src/IFoxCAD.Cad/IFoxCAD.Cad.csproj.data diff --git a/src/IFoxCAD.Cad/IFoxCAD.Cad.csproj b/src/IFoxCAD.Cad/IFoxCAD.Cad.csproj index 36a58ac..c5759bc 100644 --- a/src/IFoxCAD.Cad/IFoxCAD.Cad.csproj +++ b/src/IFoxCAD.Cad/IFoxCAD.Cad.csproj @@ -97,5 +97,4 @@ - diff --git a/src/IFoxCAD.Cad/IFoxCAD.Cad.csproj.data b/src/IFoxCAD.Cad/IFoxCAD.Cad.csproj.data deleted file mode 100644 index e69de29..0000000 -- Gitee From ea0db93ad5db86e6e428983c5b6078c6ec5622b5 Mon Sep 17 00:00:00 2001 From: liuqihong <540762622@qq.com> Date: Sat, 13 Aug 2022 00:13:41 +0800 Subject: [PATCH 352/675] =?UTF-8?q?=E5=A4=84=E7=90=86=E5=86=B2=E7=AA=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/IFoxCAD.Cad/IFoxCAD.Cad.csproj.data | 0 tests/Test/TestJig.cs | 9 --------- tests/Test/TestLisp.cs | 7 ------- tests/Test/testConvexHull.cs | 16 ---------------- tests/Test/testeditor.cs | 8 -------- tests/Test/testselectfilter.cs | 16 ---------------- 6 files changed, 56 deletions(-) delete mode 100644 src/IFoxCAD.Cad/IFoxCAD.Cad.csproj.data diff --git a/src/IFoxCAD.Cad/IFoxCAD.Cad.csproj.data b/src/IFoxCAD.Cad/IFoxCAD.Cad.csproj.data deleted file mode 100644 index e69de29..0000000 diff --git a/tests/Test/TestJig.cs b/tests/Test/TestJig.cs index 0fdf1a0..0fb1310 100644 --- a/tests/Test/TestJig.cs +++ b/tests/Test/TestJig.cs @@ -121,12 +121,7 @@ public void TestCmd_Jig44() [CommandMethod("TestCmd_loop")] public void TestCmd_loop() { -<<<<<<< HEAD DocumentCollection dm = Acap.DocumentManager; -======= - DocumentCollection dm = - Autodesk.AutoCAD.ApplicationServices.Application.DocumentManager; ->>>>>>> a9efe02fa01a7a5fba5f84c0714e4331f823d916 Editor ed = dm.MdiActiveDocument.Editor; // Create and add our message filter MyMessageFilter filter = new(); @@ -176,11 +171,7 @@ public bool PreFilterMessage(ref Message m) [CommandMethod("TestCmd_QuickText")] static public void TestCmd_QuickText() { -<<<<<<< HEAD var dm = Acap.DocumentManager; -======= - var dm = Autodesk.AutoCAD.ApplicationServices.Application.DocumentManager; ->>>>>>> a9efe02fa01a7a5fba5f84c0714e4331f823d916 var doc = dm.MdiActiveDocument; var db = doc.Database; var ed = doc.Editor; diff --git a/tests/Test/TestLisp.cs b/tests/Test/TestLisp.cs index 616d96e..f657286 100644 --- a/tests/Test/TestLisp.cs +++ b/tests/Test/TestLisp.cs @@ -51,17 +51,10 @@ public static object LispTest_RunLisp(ResultBuffer rb) //不能在参照块中使用命令 [CommandMethod("CmdTest_RunLisp20", CommandFlags.NoBlockEditor)] #if ac2009 -<<<<<<< HEAD //acad09增,不会被动作录制器 捕捉到 [CommandMethod("CmdTest_RunLisp21", CommandFlags.NoActionRecording)] //acad09增,会被动作录制器捕捉 [CommandMethod("CmdTest_RunLisp22", CommandFlags.ActionMacro)] -======= - //acad09增,不会被动作录制器 捕捉到 - [CommandMethod("CmdTest_RunLisp21", CommandFlags.NoActionRecording)] - //acad09增,会被动作录制器捕捉 - [CommandMethod("CmdTest_RunLisp22", CommandFlags.ActionMacro)] ->>>>>>> a9efe02fa01a7a5fba5f84c0714e4331f823d916 #endif #if !NET35 //推断约束时不能使用命令 diff --git a/tests/Test/testConvexHull.cs b/tests/Test/testConvexHull.cs index e984c32..e031562 100644 --- a/tests/Test/testConvexHull.cs +++ b/tests/Test/testConvexHull.cs @@ -1,20 +1,4 @@ -<<<<<<< HEAD -namespace Test -======= -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; - -using Autodesk.AutoCAD.Runtime; -using Autodesk.AutoCAD.Geometry; -using IFoxCAD.Cad; -using Autodesk.AutoCAD.DatabaseServices; -using Autodesk.AutoCAD.Colors; - namespace Test ->>>>>>> a9efe02fa01a7a5fba5f84c0714e4331f823d916 { public class TestConvexHull { diff --git a/tests/Test/testeditor.cs b/tests/Test/testeditor.cs index ab8525f..7d610cf 100644 --- a/tests/Test/testeditor.cs +++ b/tests/Test/testeditor.cs @@ -28,11 +28,7 @@ public void Testzoom() { using var tr = new DBTrans(); var res = Env.Editor.GetEntity("\npick ent:"); -<<<<<<< HEAD if (res.Status == PromptStatus.OK) -======= - if (res.Status == Autodesk.AutoCAD.EditorInput.PromptStatus.OK) ->>>>>>> a9efe02fa01a7a5fba5f84c0714e4331f823d916 { Env.Editor.ZoomObject(res.ObjectId.GetObject()); } @@ -44,11 +40,7 @@ public void Testzoomextent() { //using var tr = new DBTrans(); //var res = Env.Editor.GetEntity("\npick ent:"); -<<<<<<< HEAD //if (res.Status == PromptStatus.OK) -======= - //if (res.Status == Autodesk.AutoCAD.EditorInput.PromptStatus.OK) ->>>>>>> a9efe02fa01a7a5fba5f84c0714e4331f823d916 //{ // Env.Editor.ZoomObject(res.ObjectId.GetObject()); //} diff --git a/tests/Test/testselectfilter.cs b/tests/Test/testselectfilter.cs index 78b1a7a..d49f278 100644 --- a/tests/Test/testselectfilter.cs +++ b/tests/Test/testselectfilter.cs @@ -1,20 +1,4 @@ -<<<<<<< HEAD -namespace Test -======= -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; - -using Autodesk.AutoCAD.Geometry; -using Autodesk.AutoCAD.Runtime; -using Autodesk.AutoCAD.DatabaseServices; -using IFoxCAD.Cad; -using Autodesk.AutoCAD.EditorInput; - namespace Test ->>>>>>> a9efe02fa01a7a5fba5f84c0714e4331f823d916 { public class Testselectfilter { -- Gitee From 5307ad087ad3a5eb00774f4575eb04c301d20225 Mon Sep 17 00:00:00 2001 From: liuqihong <540762622@qq.com> Date: Sat, 13 Aug 2022 01:27:58 +0800 Subject: [PATCH 353/675] =?UTF-8?q?=E5=A4=9A=E4=BA=86=E4=B8=80=E4=B8=AA?= =?UTF-8?q?=E7=9B=B8=E5=AF=B9=E8=B7=AF=E5=BE=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/IFoxCAD.Cad/IFoxCAD.Cad.csproj | 1 - 1 file changed, 1 deletion(-) diff --git a/src/IFoxCAD.Cad/IFoxCAD.Cad.csproj b/src/IFoxCAD.Cad/IFoxCAD.Cad.csproj index 4768677..ac9bf4e 100644 --- a/src/IFoxCAD.Cad/IFoxCAD.Cad.csproj +++ b/src/IFoxCAD.Cad/IFoxCAD.Cad.csproj @@ -86,6 +86,5 @@ - -- Gitee From 237b3a3bfbd3b6a5cde34a9b3f9d2b454467d118 Mon Sep 17 00:00:00 2001 From: liuqihong <540762622@qq.com> Date: Sat, 13 Aug 2022 20:32:07 +0800 Subject: [PATCH 354/675] =?UTF-8?q?=E4=B8=BA=E4=BA=86=E5=A4=9A=E5=B7=A5?= =?UTF-8?q?=E7=A8=8B=E6=B5=8B=E8=AF=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- IFoxCAD.sln | 48 +++++++-- .../Algorithms/Graph/Graph.cs | 0 .../Algorithms/Graph/IGraph.cs | 0 .../Algorithms/QuadTree/QuadEntity.cs | 0 .../Algorithms/QuadTree/QuadTree.cs | 0 .../Algorithms/QuadTree/QuadTreeEvn.cs | 0 .../Algorithms/QuadTree/QuadTreeNode.cs | 0 .../Algorithms/QuadTree/QuadTreeSelectMode.cs | 0 .../Algorithms/QuadTree/Rect.cs | 0 .../Algorithms/QuadTree/Utility.cs | 0 .../ExtensionMethod/BulgeVertexWidth.cs | 0 .../ExtensionMethod/CollectionEx.cs | 0 .../ExtensionMethod/Curve2dEx.cs | 0 .../ExtensionMethod/Curve3dEx.cs | 0 .../ExtensionMethod/CurveEx.cs | 0 .../ExtensionMethod/DBDictionaryEx.cs | 0 .../ExtensionMethod/DBObjectEx.cs | 0 .../ExtensionMethod/DatabaseEx.cs | 0 .../ExtensionMethod/EditorEx.cs | 0 .../ExtensionMethod/EntityEx.cs | 0 .../ExtensionMethod/Enums.cs | 0 .../ExtensionMethod/GeometryEx.cs | 0 .../ExtensionMethod/Jig.cs | 0 .../ExtensionMethod/ObjEx.cs | 0 .../ExtensionMethod/ObjectIdEx.cs | 0 .../ExtensionMethod/PointEx.cs | 0 .../ExtensionMethod/SelectionSetEx.cs | 0 .../ExtensionMethod/SymbolTableEx.cs | 0 .../ExtensionMethod/SymbolTableRecordEx.cs | 0 .../ExtensionMethod/Tools.cs | 0 .../HatchConverter.cs" | 0 .../HatchEx.cs" | 0 .../HatchInfo.cs" | 0 .../AttachmentPointHelper.cs" | 0 .../TextEntityAdd.cs" | 0 .../TextInfo.cs" | 0 .../IFoxCAD.Cad.Shared.projitems | 71 ++++++++++++ .../IFoxCAD.Cad.Shared.shproj | 13 +++ .../ResultData/LispDottedPair.cs | 0 .../ResultData/LispList.cs | 0 .../ResultData/TypedValueList.cs | 0 .../ResultData/XRecordDataList.cs | 0 .../ResultData/XdataList.cs | 0 .../Runtime/AOP.cs | 0 .../Runtime/AcadVersion.cs | 0 .../Runtime/AssemInfo.cs | 0 .../Runtime/AutoRegAssem.cs | 0 .../Runtime/CadVersion.cs | 0 .../Runtime/DBTrans.cs | 0 .../Runtime/Env.cs | 0 .../Runtime/FileOpenMode.cs | 0 .../Runtime/IAutoGo.cs | 0 .../Runtime/Log.cs | 0 .../Runtime/MethodInfoHelper.cs | 0 .../Runtime/SymbolTable.cs | 0 .../Runtime/Utils.cs | 0 .../SelectionFilter/OpComp.cs | 0 .../SelectionFilter/OpEqual.cs | 0 .../SelectionFilter/OpFilter.cs | 0 .../SelectionFilter/OpList.cs | 0 .../SelectionFilter/OpLogi.cs | 0 ...oxCAD.Cad.csproj => IFoxCAD.Acad08.csproj} | 2 + src/IFoxCAD.Cad/IFoxCAD.Acad09plus.csproj | 102 ++++++++++++++++++ src/IFoxCAD.Cad/IFoxCAD.Gcad.csproj | 94 ++++++++++++++++ src/IFoxCAD.Cad/IFoxCAD.ZWcad.csproj | 94 ++++++++++++++++ tests/Test/Test.csproj | 4 +- 66 files changed, 419 insertions(+), 9 deletions(-) rename src/{IFoxCAD.Cad => IFoxCAD.Cad.Shared}/Algorithms/Graph/Graph.cs (100%) rename src/{IFoxCAD.Cad => IFoxCAD.Cad.Shared}/Algorithms/Graph/IGraph.cs (100%) rename src/{IFoxCAD.Cad => IFoxCAD.Cad.Shared}/Algorithms/QuadTree/QuadEntity.cs (100%) rename src/{IFoxCAD.Cad => IFoxCAD.Cad.Shared}/Algorithms/QuadTree/QuadTree.cs (100%) rename src/{IFoxCAD.Cad => IFoxCAD.Cad.Shared}/Algorithms/QuadTree/QuadTreeEvn.cs (100%) rename src/{IFoxCAD.Cad => IFoxCAD.Cad.Shared}/Algorithms/QuadTree/QuadTreeNode.cs (100%) rename src/{IFoxCAD.Cad => IFoxCAD.Cad.Shared}/Algorithms/QuadTree/QuadTreeSelectMode.cs (100%) rename src/{IFoxCAD.Cad => IFoxCAD.Cad.Shared}/Algorithms/QuadTree/Rect.cs (100%) rename src/{IFoxCAD.Cad => IFoxCAD.Cad.Shared}/Algorithms/QuadTree/Utility.cs (100%) rename src/{IFoxCAD.Cad => IFoxCAD.Cad.Shared}/ExtensionMethod/BulgeVertexWidth.cs (100%) rename src/{IFoxCAD.Cad => IFoxCAD.Cad.Shared}/ExtensionMethod/CollectionEx.cs (100%) rename src/{IFoxCAD.Cad => IFoxCAD.Cad.Shared}/ExtensionMethod/Curve2dEx.cs (100%) rename src/{IFoxCAD.Cad => IFoxCAD.Cad.Shared}/ExtensionMethod/Curve3dEx.cs (100%) rename src/{IFoxCAD.Cad => IFoxCAD.Cad.Shared}/ExtensionMethod/CurveEx.cs (100%) rename src/{IFoxCAD.Cad => IFoxCAD.Cad.Shared}/ExtensionMethod/DBDictionaryEx.cs (100%) rename src/{IFoxCAD.Cad => IFoxCAD.Cad.Shared}/ExtensionMethod/DBObjectEx.cs (100%) rename src/{IFoxCAD.Cad => IFoxCAD.Cad.Shared}/ExtensionMethod/DatabaseEx.cs (100%) rename src/{IFoxCAD.Cad => IFoxCAD.Cad.Shared}/ExtensionMethod/EditorEx.cs (100%) rename src/{IFoxCAD.Cad => IFoxCAD.Cad.Shared}/ExtensionMethod/EntityEx.cs (100%) rename src/{IFoxCAD.Cad => IFoxCAD.Cad.Shared}/ExtensionMethod/Enums.cs (100%) rename src/{IFoxCAD.Cad => IFoxCAD.Cad.Shared}/ExtensionMethod/GeometryEx.cs (100%) rename src/{IFoxCAD.Cad => IFoxCAD.Cad.Shared}/ExtensionMethod/Jig.cs (100%) rename src/{IFoxCAD.Cad => IFoxCAD.Cad.Shared}/ExtensionMethod/ObjEx.cs (100%) rename src/{IFoxCAD.Cad => IFoxCAD.Cad.Shared}/ExtensionMethod/ObjectIdEx.cs (100%) rename src/{IFoxCAD.Cad => IFoxCAD.Cad.Shared}/ExtensionMethod/PointEx.cs (100%) rename src/{IFoxCAD.Cad => IFoxCAD.Cad.Shared}/ExtensionMethod/SelectionSetEx.cs (100%) rename src/{IFoxCAD.Cad => IFoxCAD.Cad.Shared}/ExtensionMethod/SymbolTableEx.cs (100%) rename src/{IFoxCAD.Cad => IFoxCAD.Cad.Shared}/ExtensionMethod/SymbolTableRecordEx.cs (100%) rename src/{IFoxCAD.Cad => IFoxCAD.Cad.Shared}/ExtensionMethod/Tools.cs (100%) rename "src/IFoxCAD.Cad/ExtensionMethod/\346\226\260\345\273\272\345\241\253\345\205\205/HatchConverter.cs" => "src/IFoxCAD.Cad.Shared/ExtensionMethod/\346\226\260\345\273\272\345\241\253\345\205\205/HatchConverter.cs" (100%) rename "src/IFoxCAD.Cad/ExtensionMethod/\346\226\260\345\273\272\345\241\253\345\205\205/HatchEx.cs" => "src/IFoxCAD.Cad.Shared/ExtensionMethod/\346\226\260\345\273\272\345\241\253\345\205\205/HatchEx.cs" (100%) rename "src/IFoxCAD.Cad/ExtensionMethod/\346\226\260\345\273\272\345\241\253\345\205\205/HatchInfo.cs" => "src/IFoxCAD.Cad.Shared/ExtensionMethod/\346\226\260\345\273\272\345\241\253\345\205\205/HatchInfo.cs" (100%) rename "src/IFoxCAD.Cad/ExtensionMethod/\346\226\260\345\273\272\346\226\207\345\255\227/AttachmentPointHelper.cs" => "src/IFoxCAD.Cad.Shared/ExtensionMethod/\346\226\260\345\273\272\346\226\207\345\255\227/AttachmentPointHelper.cs" (100%) rename "src/IFoxCAD.Cad/ExtensionMethod/\346\226\260\345\273\272\346\226\207\345\255\227/TextEntityAdd.cs" => "src/IFoxCAD.Cad.Shared/ExtensionMethod/\346\226\260\345\273\272\346\226\207\345\255\227/TextEntityAdd.cs" (100%) rename "src/IFoxCAD.Cad/ExtensionMethod/\346\226\260\345\273\272\346\226\207\345\255\227/TextInfo.cs" => "src/IFoxCAD.Cad.Shared/ExtensionMethod/\346\226\260\345\273\272\346\226\207\345\255\227/TextInfo.cs" (100%) create mode 100644 src/IFoxCAD.Cad.Shared/IFoxCAD.Cad.Shared.projitems create mode 100644 src/IFoxCAD.Cad.Shared/IFoxCAD.Cad.Shared.shproj rename src/{IFoxCAD.Cad => IFoxCAD.Cad.Shared}/ResultData/LispDottedPair.cs (100%) rename src/{IFoxCAD.Cad => IFoxCAD.Cad.Shared}/ResultData/LispList.cs (100%) rename src/{IFoxCAD.Cad => IFoxCAD.Cad.Shared}/ResultData/TypedValueList.cs (100%) rename src/{IFoxCAD.Cad => IFoxCAD.Cad.Shared}/ResultData/XRecordDataList.cs (100%) rename src/{IFoxCAD.Cad => IFoxCAD.Cad.Shared}/ResultData/XdataList.cs (100%) rename src/{IFoxCAD.Cad => IFoxCAD.Cad.Shared}/Runtime/AOP.cs (100%) rename src/{IFoxCAD.Cad => IFoxCAD.Cad.Shared}/Runtime/AcadVersion.cs (100%) rename src/{IFoxCAD.Cad => IFoxCAD.Cad.Shared}/Runtime/AssemInfo.cs (100%) rename src/{IFoxCAD.Cad => IFoxCAD.Cad.Shared}/Runtime/AutoRegAssem.cs (100%) rename src/{IFoxCAD.Cad => IFoxCAD.Cad.Shared}/Runtime/CadVersion.cs (100%) rename src/{IFoxCAD.Cad => IFoxCAD.Cad.Shared}/Runtime/DBTrans.cs (100%) rename src/{IFoxCAD.Cad => IFoxCAD.Cad.Shared}/Runtime/Env.cs (100%) rename src/{IFoxCAD.Cad => IFoxCAD.Cad.Shared}/Runtime/FileOpenMode.cs (100%) rename src/{IFoxCAD.Cad => IFoxCAD.Cad.Shared}/Runtime/IAutoGo.cs (100%) rename src/{IFoxCAD.Cad => IFoxCAD.Cad.Shared}/Runtime/Log.cs (100%) rename src/{IFoxCAD.Cad => IFoxCAD.Cad.Shared}/Runtime/MethodInfoHelper.cs (100%) rename src/{IFoxCAD.Cad => IFoxCAD.Cad.Shared}/Runtime/SymbolTable.cs (100%) rename src/{IFoxCAD.Cad => IFoxCAD.Cad.Shared}/Runtime/Utils.cs (100%) rename src/{IFoxCAD.Cad => IFoxCAD.Cad.Shared}/SelectionFilter/OpComp.cs (100%) rename src/{IFoxCAD.Cad => IFoxCAD.Cad.Shared}/SelectionFilter/OpEqual.cs (100%) rename src/{IFoxCAD.Cad => IFoxCAD.Cad.Shared}/SelectionFilter/OpFilter.cs (100%) rename src/{IFoxCAD.Cad => IFoxCAD.Cad.Shared}/SelectionFilter/OpList.cs (100%) rename src/{IFoxCAD.Cad => IFoxCAD.Cad.Shared}/SelectionFilter/OpLogi.cs (100%) rename src/IFoxCAD.Cad/{IFoxCAD.Cad.csproj => IFoxCAD.Acad08.csproj} (97%) create mode 100644 src/IFoxCAD.Cad/IFoxCAD.Acad09plus.csproj create mode 100644 src/IFoxCAD.Cad/IFoxCAD.Gcad.csproj create mode 100644 src/IFoxCAD.Cad/IFoxCAD.ZWcad.csproj diff --git a/IFoxCAD.sln b/IFoxCAD.sln index a9e0664..aef4706 100644 --- a/IFoxCAD.sln +++ b/IFoxCAD.sln @@ -3,8 +3,6 @@ Microsoft Visual Studio Solution File, Format Version 12.00 # Visual Studio Version 17 VisualStudioVersion = 17.1.32113.165 MinimumVisualStudioVersion = 10.0.40219.1 -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "IFoxCAD.Cad", "src\IFoxCAD.Cad\IFoxCAD.Cad.csproj", "{D5FAED7C-A99B-4BEE-A745-45442DC44971}" -EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Test", "tests\Test\Test.csproj", "{B1602568-F023-46C7-B635-3CB281F1EC00}" EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "IFoxCAD.WPF", "src\IFoxCAD.WPF\IFoxCAD.WPF.csproj", "{D820D629-1AB3-4BE3-A772-CB753D98CCB8}" @@ -13,16 +11,24 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "IFoxCAD.Basal", "src\IFoxCA EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "TestConsole", "tests\TestConsole\TestConsole.csproj", "{E2873F0B-CAD2-45E8-8FF0-C05C0C6AD955}" EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "IFoxCAD.Gcad", "src\IFoxCAD.Cad\IFoxCAD.Gcad.csproj", "{D7756AF6-601D-40C2-97E9-940E5AFC2E08}" +EndProject +Project("{D954291E-2A0B-460D-934E-DC6B0785DB48}") = "IFoxCAD.Cad.Shared", "src\IFoxCAD.Cad.Shared\IFoxCAD.Cad.Shared.shproj", "{82FB8450-B971-4E30-859F-4B2DDB81F590}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Cad", "Cad", "{465C4E39-FBA2-417A-AB31-BDC01601F420}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "IFoxCAD.Acad08", "src\IFoxCAD.Cad\IFoxCAD.Acad08.csproj", "{F5C0DA54-2031-436D-B6FA-03C745B98862}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "IFoxCAD.Acad09plus", "src\IFoxCAD.Cad\IFoxCAD.Acad09plus.csproj", "{34213E53-C0F5-49DF-9FB2-0A5D861013EF}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "IFoxCAD.ZWcad", "src\IFoxCAD.Cad\IFoxCAD.ZWcad.csproj", "{C6FC723B-B4CD-475A-BBA7-4FE5CE82AC5A}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU Release|Any CPU = Release|Any CPU EndGlobalSection GlobalSection(ProjectConfigurationPlatforms) = postSolution - {D5FAED7C-A99B-4BEE-A745-45442DC44971}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {D5FAED7C-A99B-4BEE-A745-45442DC44971}.Debug|Any CPU.Build.0 = Debug|Any CPU - {D5FAED7C-A99B-4BEE-A745-45442DC44971}.Release|Any CPU.ActiveCfg = Release|Any CPU - {D5FAED7C-A99B-4BEE-A745-45442DC44971}.Release|Any CPU.Build.0 = Release|Any CPU {B1602568-F023-46C7-B635-3CB281F1EC00}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {B1602568-F023-46C7-B635-3CB281F1EC00}.Debug|Any CPU.Build.0 = Debug|Any CPU {B1602568-F023-46C7-B635-3CB281F1EC00}.Release|Any CPU.ActiveCfg = Release|Any CPU @@ -39,11 +45,41 @@ Global {E2873F0B-CAD2-45E8-8FF0-C05C0C6AD955}.Debug|Any CPU.Build.0 = Debug|Any CPU {E2873F0B-CAD2-45E8-8FF0-C05C0C6AD955}.Release|Any CPU.ActiveCfg = Release|Any CPU {E2873F0B-CAD2-45E8-8FF0-C05C0C6AD955}.Release|Any CPU.Build.0 = Release|Any CPU + {D7756AF6-601D-40C2-97E9-940E5AFC2E08}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {D7756AF6-601D-40C2-97E9-940E5AFC2E08}.Debug|Any CPU.Build.0 = Debug|Any CPU + {D7756AF6-601D-40C2-97E9-940E5AFC2E08}.Release|Any CPU.ActiveCfg = Release|Any CPU + {D7756AF6-601D-40C2-97E9-940E5AFC2E08}.Release|Any CPU.Build.0 = Release|Any CPU + {F5C0DA54-2031-436D-B6FA-03C745B98862}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {F5C0DA54-2031-436D-B6FA-03C745B98862}.Debug|Any CPU.Build.0 = Debug|Any CPU + {F5C0DA54-2031-436D-B6FA-03C745B98862}.Release|Any CPU.ActiveCfg = Release|Any CPU + {F5C0DA54-2031-436D-B6FA-03C745B98862}.Release|Any CPU.Build.0 = Release|Any CPU + {34213E53-C0F5-49DF-9FB2-0A5D861013EF}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {34213E53-C0F5-49DF-9FB2-0A5D861013EF}.Debug|Any CPU.Build.0 = Debug|Any CPU + {34213E53-C0F5-49DF-9FB2-0A5D861013EF}.Release|Any CPU.ActiveCfg = Release|Any CPU + {34213E53-C0F5-49DF-9FB2-0A5D861013EF}.Release|Any CPU.Build.0 = Release|Any CPU + {C6FC723B-B4CD-475A-BBA7-4FE5CE82AC5A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {C6FC723B-B4CD-475A-BBA7-4FE5CE82AC5A}.Debug|Any CPU.Build.0 = Debug|Any CPU + {C6FC723B-B4CD-475A-BBA7-4FE5CE82AC5A}.Release|Any CPU.ActiveCfg = Release|Any CPU + {C6FC723B-B4CD-475A-BBA7-4FE5CE82AC5A}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE EndGlobalSection + GlobalSection(NestedProjects) = preSolution + {D7756AF6-601D-40C2-97E9-940E5AFC2E08} = {465C4E39-FBA2-417A-AB31-BDC01601F420} + {82FB8450-B971-4E30-859F-4B2DDB81F590} = {465C4E39-FBA2-417A-AB31-BDC01601F420} + {F5C0DA54-2031-436D-B6FA-03C745B98862} = {465C4E39-FBA2-417A-AB31-BDC01601F420} + {34213E53-C0F5-49DF-9FB2-0A5D861013EF} = {465C4E39-FBA2-417A-AB31-BDC01601F420} + {C6FC723B-B4CD-475A-BBA7-4FE5CE82AC5A} = {465C4E39-FBA2-417A-AB31-BDC01601F420} + EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {31D6C754-CF6B-4AB0-9861-6923DD312350} EndGlobalSection + GlobalSection(SharedMSBuildProjectFiles) = preSolution + src\IFoxCAD.Cad.Shared\IFoxCAD.Cad.Shared.projitems*{34213e53-c0f5-49df-9fb2-0a5d861013ef}*SharedItemsImports = 5 + src\IFoxCAD.Cad.Shared\IFoxCAD.Cad.Shared.projitems*{82fb8450-b971-4e30-859f-4b2ddb81f590}*SharedItemsImports = 13 + src\IFoxCAD.Cad.Shared\IFoxCAD.Cad.Shared.projitems*{c6fc723b-b4cd-475a-bba7-4fe5ce82ac5a}*SharedItemsImports = 5 + src\IFoxCAD.Cad.Shared\IFoxCAD.Cad.Shared.projitems*{d7756af6-601d-40c2-97e9-940e5afc2e08}*SharedItemsImports = 5 + src\IFoxCAD.Cad.Shared\IFoxCAD.Cad.Shared.projitems*{f5c0da54-2031-436d-b6fa-03c745b98862}*SharedItemsImports = 5 + EndGlobalSection EndGlobal diff --git a/src/IFoxCAD.Cad/Algorithms/Graph/Graph.cs b/src/IFoxCAD.Cad.Shared/Algorithms/Graph/Graph.cs similarity index 100% rename from src/IFoxCAD.Cad/Algorithms/Graph/Graph.cs rename to src/IFoxCAD.Cad.Shared/Algorithms/Graph/Graph.cs diff --git a/src/IFoxCAD.Cad/Algorithms/Graph/IGraph.cs b/src/IFoxCAD.Cad.Shared/Algorithms/Graph/IGraph.cs similarity index 100% rename from src/IFoxCAD.Cad/Algorithms/Graph/IGraph.cs rename to src/IFoxCAD.Cad.Shared/Algorithms/Graph/IGraph.cs diff --git a/src/IFoxCAD.Cad/Algorithms/QuadTree/QuadEntity.cs b/src/IFoxCAD.Cad.Shared/Algorithms/QuadTree/QuadEntity.cs similarity index 100% rename from src/IFoxCAD.Cad/Algorithms/QuadTree/QuadEntity.cs rename to src/IFoxCAD.Cad.Shared/Algorithms/QuadTree/QuadEntity.cs diff --git a/src/IFoxCAD.Cad/Algorithms/QuadTree/QuadTree.cs b/src/IFoxCAD.Cad.Shared/Algorithms/QuadTree/QuadTree.cs similarity index 100% rename from src/IFoxCAD.Cad/Algorithms/QuadTree/QuadTree.cs rename to src/IFoxCAD.Cad.Shared/Algorithms/QuadTree/QuadTree.cs diff --git a/src/IFoxCAD.Cad/Algorithms/QuadTree/QuadTreeEvn.cs b/src/IFoxCAD.Cad.Shared/Algorithms/QuadTree/QuadTreeEvn.cs similarity index 100% rename from src/IFoxCAD.Cad/Algorithms/QuadTree/QuadTreeEvn.cs rename to src/IFoxCAD.Cad.Shared/Algorithms/QuadTree/QuadTreeEvn.cs diff --git a/src/IFoxCAD.Cad/Algorithms/QuadTree/QuadTreeNode.cs b/src/IFoxCAD.Cad.Shared/Algorithms/QuadTree/QuadTreeNode.cs similarity index 100% rename from src/IFoxCAD.Cad/Algorithms/QuadTree/QuadTreeNode.cs rename to src/IFoxCAD.Cad.Shared/Algorithms/QuadTree/QuadTreeNode.cs diff --git a/src/IFoxCAD.Cad/Algorithms/QuadTree/QuadTreeSelectMode.cs b/src/IFoxCAD.Cad.Shared/Algorithms/QuadTree/QuadTreeSelectMode.cs similarity index 100% rename from src/IFoxCAD.Cad/Algorithms/QuadTree/QuadTreeSelectMode.cs rename to src/IFoxCAD.Cad.Shared/Algorithms/QuadTree/QuadTreeSelectMode.cs diff --git a/src/IFoxCAD.Cad/Algorithms/QuadTree/Rect.cs b/src/IFoxCAD.Cad.Shared/Algorithms/QuadTree/Rect.cs similarity index 100% rename from src/IFoxCAD.Cad/Algorithms/QuadTree/Rect.cs rename to src/IFoxCAD.Cad.Shared/Algorithms/QuadTree/Rect.cs diff --git a/src/IFoxCAD.Cad/Algorithms/QuadTree/Utility.cs b/src/IFoxCAD.Cad.Shared/Algorithms/QuadTree/Utility.cs similarity index 100% rename from src/IFoxCAD.Cad/Algorithms/QuadTree/Utility.cs rename to src/IFoxCAD.Cad.Shared/Algorithms/QuadTree/Utility.cs diff --git a/src/IFoxCAD.Cad/ExtensionMethod/BulgeVertexWidth.cs b/src/IFoxCAD.Cad.Shared/ExtensionMethod/BulgeVertexWidth.cs similarity index 100% rename from src/IFoxCAD.Cad/ExtensionMethod/BulgeVertexWidth.cs rename to src/IFoxCAD.Cad.Shared/ExtensionMethod/BulgeVertexWidth.cs diff --git a/src/IFoxCAD.Cad/ExtensionMethod/CollectionEx.cs b/src/IFoxCAD.Cad.Shared/ExtensionMethod/CollectionEx.cs similarity index 100% rename from src/IFoxCAD.Cad/ExtensionMethod/CollectionEx.cs rename to src/IFoxCAD.Cad.Shared/ExtensionMethod/CollectionEx.cs diff --git a/src/IFoxCAD.Cad/ExtensionMethod/Curve2dEx.cs b/src/IFoxCAD.Cad.Shared/ExtensionMethod/Curve2dEx.cs similarity index 100% rename from src/IFoxCAD.Cad/ExtensionMethod/Curve2dEx.cs rename to src/IFoxCAD.Cad.Shared/ExtensionMethod/Curve2dEx.cs diff --git a/src/IFoxCAD.Cad/ExtensionMethod/Curve3dEx.cs b/src/IFoxCAD.Cad.Shared/ExtensionMethod/Curve3dEx.cs similarity index 100% rename from src/IFoxCAD.Cad/ExtensionMethod/Curve3dEx.cs rename to src/IFoxCAD.Cad.Shared/ExtensionMethod/Curve3dEx.cs diff --git a/src/IFoxCAD.Cad/ExtensionMethod/CurveEx.cs b/src/IFoxCAD.Cad.Shared/ExtensionMethod/CurveEx.cs similarity index 100% rename from src/IFoxCAD.Cad/ExtensionMethod/CurveEx.cs rename to src/IFoxCAD.Cad.Shared/ExtensionMethod/CurveEx.cs diff --git a/src/IFoxCAD.Cad/ExtensionMethod/DBDictionaryEx.cs b/src/IFoxCAD.Cad.Shared/ExtensionMethod/DBDictionaryEx.cs similarity index 100% rename from src/IFoxCAD.Cad/ExtensionMethod/DBDictionaryEx.cs rename to src/IFoxCAD.Cad.Shared/ExtensionMethod/DBDictionaryEx.cs diff --git a/src/IFoxCAD.Cad/ExtensionMethod/DBObjectEx.cs b/src/IFoxCAD.Cad.Shared/ExtensionMethod/DBObjectEx.cs similarity index 100% rename from src/IFoxCAD.Cad/ExtensionMethod/DBObjectEx.cs rename to src/IFoxCAD.Cad.Shared/ExtensionMethod/DBObjectEx.cs diff --git a/src/IFoxCAD.Cad/ExtensionMethod/DatabaseEx.cs b/src/IFoxCAD.Cad.Shared/ExtensionMethod/DatabaseEx.cs similarity index 100% rename from src/IFoxCAD.Cad/ExtensionMethod/DatabaseEx.cs rename to src/IFoxCAD.Cad.Shared/ExtensionMethod/DatabaseEx.cs diff --git a/src/IFoxCAD.Cad/ExtensionMethod/EditorEx.cs b/src/IFoxCAD.Cad.Shared/ExtensionMethod/EditorEx.cs similarity index 100% rename from src/IFoxCAD.Cad/ExtensionMethod/EditorEx.cs rename to src/IFoxCAD.Cad.Shared/ExtensionMethod/EditorEx.cs diff --git a/src/IFoxCAD.Cad/ExtensionMethod/EntityEx.cs b/src/IFoxCAD.Cad.Shared/ExtensionMethod/EntityEx.cs similarity index 100% rename from src/IFoxCAD.Cad/ExtensionMethod/EntityEx.cs rename to src/IFoxCAD.Cad.Shared/ExtensionMethod/EntityEx.cs diff --git a/src/IFoxCAD.Cad/ExtensionMethod/Enums.cs b/src/IFoxCAD.Cad.Shared/ExtensionMethod/Enums.cs similarity index 100% rename from src/IFoxCAD.Cad/ExtensionMethod/Enums.cs rename to src/IFoxCAD.Cad.Shared/ExtensionMethod/Enums.cs diff --git a/src/IFoxCAD.Cad/ExtensionMethod/GeometryEx.cs b/src/IFoxCAD.Cad.Shared/ExtensionMethod/GeometryEx.cs similarity index 100% rename from src/IFoxCAD.Cad/ExtensionMethod/GeometryEx.cs rename to src/IFoxCAD.Cad.Shared/ExtensionMethod/GeometryEx.cs diff --git a/src/IFoxCAD.Cad/ExtensionMethod/Jig.cs b/src/IFoxCAD.Cad.Shared/ExtensionMethod/Jig.cs similarity index 100% rename from src/IFoxCAD.Cad/ExtensionMethod/Jig.cs rename to src/IFoxCAD.Cad.Shared/ExtensionMethod/Jig.cs diff --git a/src/IFoxCAD.Cad/ExtensionMethod/ObjEx.cs b/src/IFoxCAD.Cad.Shared/ExtensionMethod/ObjEx.cs similarity index 100% rename from src/IFoxCAD.Cad/ExtensionMethod/ObjEx.cs rename to src/IFoxCAD.Cad.Shared/ExtensionMethod/ObjEx.cs diff --git a/src/IFoxCAD.Cad/ExtensionMethod/ObjectIdEx.cs b/src/IFoxCAD.Cad.Shared/ExtensionMethod/ObjectIdEx.cs similarity index 100% rename from src/IFoxCAD.Cad/ExtensionMethod/ObjectIdEx.cs rename to src/IFoxCAD.Cad.Shared/ExtensionMethod/ObjectIdEx.cs diff --git a/src/IFoxCAD.Cad/ExtensionMethod/PointEx.cs b/src/IFoxCAD.Cad.Shared/ExtensionMethod/PointEx.cs similarity index 100% rename from src/IFoxCAD.Cad/ExtensionMethod/PointEx.cs rename to src/IFoxCAD.Cad.Shared/ExtensionMethod/PointEx.cs diff --git a/src/IFoxCAD.Cad/ExtensionMethod/SelectionSetEx.cs b/src/IFoxCAD.Cad.Shared/ExtensionMethod/SelectionSetEx.cs similarity index 100% rename from src/IFoxCAD.Cad/ExtensionMethod/SelectionSetEx.cs rename to src/IFoxCAD.Cad.Shared/ExtensionMethod/SelectionSetEx.cs diff --git a/src/IFoxCAD.Cad/ExtensionMethod/SymbolTableEx.cs b/src/IFoxCAD.Cad.Shared/ExtensionMethod/SymbolTableEx.cs similarity index 100% rename from src/IFoxCAD.Cad/ExtensionMethod/SymbolTableEx.cs rename to src/IFoxCAD.Cad.Shared/ExtensionMethod/SymbolTableEx.cs diff --git a/src/IFoxCAD.Cad/ExtensionMethod/SymbolTableRecordEx.cs b/src/IFoxCAD.Cad.Shared/ExtensionMethod/SymbolTableRecordEx.cs similarity index 100% rename from src/IFoxCAD.Cad/ExtensionMethod/SymbolTableRecordEx.cs rename to src/IFoxCAD.Cad.Shared/ExtensionMethod/SymbolTableRecordEx.cs diff --git a/src/IFoxCAD.Cad/ExtensionMethod/Tools.cs b/src/IFoxCAD.Cad.Shared/ExtensionMethod/Tools.cs similarity index 100% rename from src/IFoxCAD.Cad/ExtensionMethod/Tools.cs rename to src/IFoxCAD.Cad.Shared/ExtensionMethod/Tools.cs diff --git "a/src/IFoxCAD.Cad/ExtensionMethod/\346\226\260\345\273\272\345\241\253\345\205\205/HatchConverter.cs" "b/src/IFoxCAD.Cad.Shared/ExtensionMethod/\346\226\260\345\273\272\345\241\253\345\205\205/HatchConverter.cs" similarity index 100% rename from "src/IFoxCAD.Cad/ExtensionMethod/\346\226\260\345\273\272\345\241\253\345\205\205/HatchConverter.cs" rename to "src/IFoxCAD.Cad.Shared/ExtensionMethod/\346\226\260\345\273\272\345\241\253\345\205\205/HatchConverter.cs" diff --git "a/src/IFoxCAD.Cad/ExtensionMethod/\346\226\260\345\273\272\345\241\253\345\205\205/HatchEx.cs" "b/src/IFoxCAD.Cad.Shared/ExtensionMethod/\346\226\260\345\273\272\345\241\253\345\205\205/HatchEx.cs" similarity index 100% rename from "src/IFoxCAD.Cad/ExtensionMethod/\346\226\260\345\273\272\345\241\253\345\205\205/HatchEx.cs" rename to "src/IFoxCAD.Cad.Shared/ExtensionMethod/\346\226\260\345\273\272\345\241\253\345\205\205/HatchEx.cs" diff --git "a/src/IFoxCAD.Cad/ExtensionMethod/\346\226\260\345\273\272\345\241\253\345\205\205/HatchInfo.cs" "b/src/IFoxCAD.Cad.Shared/ExtensionMethod/\346\226\260\345\273\272\345\241\253\345\205\205/HatchInfo.cs" similarity index 100% rename from "src/IFoxCAD.Cad/ExtensionMethod/\346\226\260\345\273\272\345\241\253\345\205\205/HatchInfo.cs" rename to "src/IFoxCAD.Cad.Shared/ExtensionMethod/\346\226\260\345\273\272\345\241\253\345\205\205/HatchInfo.cs" diff --git "a/src/IFoxCAD.Cad/ExtensionMethod/\346\226\260\345\273\272\346\226\207\345\255\227/AttachmentPointHelper.cs" "b/src/IFoxCAD.Cad.Shared/ExtensionMethod/\346\226\260\345\273\272\346\226\207\345\255\227/AttachmentPointHelper.cs" similarity index 100% rename from "src/IFoxCAD.Cad/ExtensionMethod/\346\226\260\345\273\272\346\226\207\345\255\227/AttachmentPointHelper.cs" rename to "src/IFoxCAD.Cad.Shared/ExtensionMethod/\346\226\260\345\273\272\346\226\207\345\255\227/AttachmentPointHelper.cs" diff --git "a/src/IFoxCAD.Cad/ExtensionMethod/\346\226\260\345\273\272\346\226\207\345\255\227/TextEntityAdd.cs" "b/src/IFoxCAD.Cad.Shared/ExtensionMethod/\346\226\260\345\273\272\346\226\207\345\255\227/TextEntityAdd.cs" similarity index 100% rename from "src/IFoxCAD.Cad/ExtensionMethod/\346\226\260\345\273\272\346\226\207\345\255\227/TextEntityAdd.cs" rename to "src/IFoxCAD.Cad.Shared/ExtensionMethod/\346\226\260\345\273\272\346\226\207\345\255\227/TextEntityAdd.cs" diff --git "a/src/IFoxCAD.Cad/ExtensionMethod/\346\226\260\345\273\272\346\226\207\345\255\227/TextInfo.cs" "b/src/IFoxCAD.Cad.Shared/ExtensionMethod/\346\226\260\345\273\272\346\226\207\345\255\227/TextInfo.cs" similarity index 100% rename from "src/IFoxCAD.Cad/ExtensionMethod/\346\226\260\345\273\272\346\226\207\345\255\227/TextInfo.cs" rename to "src/IFoxCAD.Cad.Shared/ExtensionMethod/\346\226\260\345\273\272\346\226\207\345\255\227/TextInfo.cs" diff --git a/src/IFoxCAD.Cad.Shared/IFoxCAD.Cad.Shared.projitems b/src/IFoxCAD.Cad.Shared/IFoxCAD.Cad.Shared.projitems new file mode 100644 index 0000000..33c7ddd --- /dev/null +++ b/src/IFoxCAD.Cad.Shared/IFoxCAD.Cad.Shared.projitems @@ -0,0 +1,71 @@ + + + + $(MSBuildAllProjects);$(MSBuildThisFileFullPath) + true + 82fb8450-b971-4e30-859f-4b2ddb81f590 + + + IFoxCAD.Cad.Shared + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/IFoxCAD.Cad.Shared/IFoxCAD.Cad.Shared.shproj b/src/IFoxCAD.Cad.Shared/IFoxCAD.Cad.Shared.shproj new file mode 100644 index 0000000..75a3d5e --- /dev/null +++ b/src/IFoxCAD.Cad.Shared/IFoxCAD.Cad.Shared.shproj @@ -0,0 +1,13 @@ + + + + 82fb8450-b971-4e30-859f-4b2ddb81f590 + 14.0 + + + + + + + + diff --git a/src/IFoxCAD.Cad/ResultData/LispDottedPair.cs b/src/IFoxCAD.Cad.Shared/ResultData/LispDottedPair.cs similarity index 100% rename from src/IFoxCAD.Cad/ResultData/LispDottedPair.cs rename to src/IFoxCAD.Cad.Shared/ResultData/LispDottedPair.cs diff --git a/src/IFoxCAD.Cad/ResultData/LispList.cs b/src/IFoxCAD.Cad.Shared/ResultData/LispList.cs similarity index 100% rename from src/IFoxCAD.Cad/ResultData/LispList.cs rename to src/IFoxCAD.Cad.Shared/ResultData/LispList.cs diff --git a/src/IFoxCAD.Cad/ResultData/TypedValueList.cs b/src/IFoxCAD.Cad.Shared/ResultData/TypedValueList.cs similarity index 100% rename from src/IFoxCAD.Cad/ResultData/TypedValueList.cs rename to src/IFoxCAD.Cad.Shared/ResultData/TypedValueList.cs diff --git a/src/IFoxCAD.Cad/ResultData/XRecordDataList.cs b/src/IFoxCAD.Cad.Shared/ResultData/XRecordDataList.cs similarity index 100% rename from src/IFoxCAD.Cad/ResultData/XRecordDataList.cs rename to src/IFoxCAD.Cad.Shared/ResultData/XRecordDataList.cs diff --git a/src/IFoxCAD.Cad/ResultData/XdataList.cs b/src/IFoxCAD.Cad.Shared/ResultData/XdataList.cs similarity index 100% rename from src/IFoxCAD.Cad/ResultData/XdataList.cs rename to src/IFoxCAD.Cad.Shared/ResultData/XdataList.cs diff --git a/src/IFoxCAD.Cad/Runtime/AOP.cs b/src/IFoxCAD.Cad.Shared/Runtime/AOP.cs similarity index 100% rename from src/IFoxCAD.Cad/Runtime/AOP.cs rename to src/IFoxCAD.Cad.Shared/Runtime/AOP.cs diff --git a/src/IFoxCAD.Cad/Runtime/AcadVersion.cs b/src/IFoxCAD.Cad.Shared/Runtime/AcadVersion.cs similarity index 100% rename from src/IFoxCAD.Cad/Runtime/AcadVersion.cs rename to src/IFoxCAD.Cad.Shared/Runtime/AcadVersion.cs diff --git a/src/IFoxCAD.Cad/Runtime/AssemInfo.cs b/src/IFoxCAD.Cad.Shared/Runtime/AssemInfo.cs similarity index 100% rename from src/IFoxCAD.Cad/Runtime/AssemInfo.cs rename to src/IFoxCAD.Cad.Shared/Runtime/AssemInfo.cs diff --git a/src/IFoxCAD.Cad/Runtime/AutoRegAssem.cs b/src/IFoxCAD.Cad.Shared/Runtime/AutoRegAssem.cs similarity index 100% rename from src/IFoxCAD.Cad/Runtime/AutoRegAssem.cs rename to src/IFoxCAD.Cad.Shared/Runtime/AutoRegAssem.cs diff --git a/src/IFoxCAD.Cad/Runtime/CadVersion.cs b/src/IFoxCAD.Cad.Shared/Runtime/CadVersion.cs similarity index 100% rename from src/IFoxCAD.Cad/Runtime/CadVersion.cs rename to src/IFoxCAD.Cad.Shared/Runtime/CadVersion.cs diff --git a/src/IFoxCAD.Cad/Runtime/DBTrans.cs b/src/IFoxCAD.Cad.Shared/Runtime/DBTrans.cs similarity index 100% rename from src/IFoxCAD.Cad/Runtime/DBTrans.cs rename to src/IFoxCAD.Cad.Shared/Runtime/DBTrans.cs diff --git a/src/IFoxCAD.Cad/Runtime/Env.cs b/src/IFoxCAD.Cad.Shared/Runtime/Env.cs similarity index 100% rename from src/IFoxCAD.Cad/Runtime/Env.cs rename to src/IFoxCAD.Cad.Shared/Runtime/Env.cs diff --git a/src/IFoxCAD.Cad/Runtime/FileOpenMode.cs b/src/IFoxCAD.Cad.Shared/Runtime/FileOpenMode.cs similarity index 100% rename from src/IFoxCAD.Cad/Runtime/FileOpenMode.cs rename to src/IFoxCAD.Cad.Shared/Runtime/FileOpenMode.cs diff --git a/src/IFoxCAD.Cad/Runtime/IAutoGo.cs b/src/IFoxCAD.Cad.Shared/Runtime/IAutoGo.cs similarity index 100% rename from src/IFoxCAD.Cad/Runtime/IAutoGo.cs rename to src/IFoxCAD.Cad.Shared/Runtime/IAutoGo.cs diff --git a/src/IFoxCAD.Cad/Runtime/Log.cs b/src/IFoxCAD.Cad.Shared/Runtime/Log.cs similarity index 100% rename from src/IFoxCAD.Cad/Runtime/Log.cs rename to src/IFoxCAD.Cad.Shared/Runtime/Log.cs diff --git a/src/IFoxCAD.Cad/Runtime/MethodInfoHelper.cs b/src/IFoxCAD.Cad.Shared/Runtime/MethodInfoHelper.cs similarity index 100% rename from src/IFoxCAD.Cad/Runtime/MethodInfoHelper.cs rename to src/IFoxCAD.Cad.Shared/Runtime/MethodInfoHelper.cs diff --git a/src/IFoxCAD.Cad/Runtime/SymbolTable.cs b/src/IFoxCAD.Cad.Shared/Runtime/SymbolTable.cs similarity index 100% rename from src/IFoxCAD.Cad/Runtime/SymbolTable.cs rename to src/IFoxCAD.Cad.Shared/Runtime/SymbolTable.cs diff --git a/src/IFoxCAD.Cad/Runtime/Utils.cs b/src/IFoxCAD.Cad.Shared/Runtime/Utils.cs similarity index 100% rename from src/IFoxCAD.Cad/Runtime/Utils.cs rename to src/IFoxCAD.Cad.Shared/Runtime/Utils.cs diff --git a/src/IFoxCAD.Cad/SelectionFilter/OpComp.cs b/src/IFoxCAD.Cad.Shared/SelectionFilter/OpComp.cs similarity index 100% rename from src/IFoxCAD.Cad/SelectionFilter/OpComp.cs rename to src/IFoxCAD.Cad.Shared/SelectionFilter/OpComp.cs diff --git a/src/IFoxCAD.Cad/SelectionFilter/OpEqual.cs b/src/IFoxCAD.Cad.Shared/SelectionFilter/OpEqual.cs similarity index 100% rename from src/IFoxCAD.Cad/SelectionFilter/OpEqual.cs rename to src/IFoxCAD.Cad.Shared/SelectionFilter/OpEqual.cs diff --git a/src/IFoxCAD.Cad/SelectionFilter/OpFilter.cs b/src/IFoxCAD.Cad.Shared/SelectionFilter/OpFilter.cs similarity index 100% rename from src/IFoxCAD.Cad/SelectionFilter/OpFilter.cs rename to src/IFoxCAD.Cad.Shared/SelectionFilter/OpFilter.cs diff --git a/src/IFoxCAD.Cad/SelectionFilter/OpList.cs b/src/IFoxCAD.Cad.Shared/SelectionFilter/OpList.cs similarity index 100% rename from src/IFoxCAD.Cad/SelectionFilter/OpList.cs rename to src/IFoxCAD.Cad.Shared/SelectionFilter/OpList.cs diff --git a/src/IFoxCAD.Cad/SelectionFilter/OpLogi.cs b/src/IFoxCAD.Cad.Shared/SelectionFilter/OpLogi.cs similarity index 100% rename from src/IFoxCAD.Cad/SelectionFilter/OpLogi.cs rename to src/IFoxCAD.Cad.Shared/SelectionFilter/OpLogi.cs diff --git a/src/IFoxCAD.Cad/IFoxCAD.Cad.csproj b/src/IFoxCAD.Cad/IFoxCAD.Acad08.csproj similarity index 97% rename from src/IFoxCAD.Cad/IFoxCAD.Cad.csproj rename to src/IFoxCAD.Cad/IFoxCAD.Acad08.csproj index ac9bf4e..82097c2 100644 --- a/src/IFoxCAD.Cad/IFoxCAD.Cad.csproj +++ b/src/IFoxCAD.Cad/IFoxCAD.Acad08.csproj @@ -87,4 +87,6 @@ + + diff --git a/src/IFoxCAD.Cad/IFoxCAD.Acad09plus.csproj b/src/IFoxCAD.Cad/IFoxCAD.Acad09plus.csproj new file mode 100644 index 0000000..7e5eeee --- /dev/null +++ b/src/IFoxCAD.Cad/IFoxCAD.Acad09plus.csproj @@ -0,0 +1,102 @@ + + + + preview + enable + + net35;net40;net45 + true + true + true + 0.3.6.1 + InspireFunction + xsfhlzh;vicwjb + 基于.NET的Cad二次开发类库 + InspireFunction + https://gitee.com/inspirefunction/ifoxcad + https://gitee.com/inspirefunction/ifoxcad.git + git + IFoxCAD;CAD;AutoCad;C#;NET + 增加四叉树测试. + true + true + true + LICENSE + true + x64 + + + + + runtime + + + + + + runtime + + + + + + runtime + + + 4.3.0 + + + + + + + + DEBUG + + + $(Configuration);ac2009 + + + $(Configuration);ac2013 + + + $(Configuration);ac2015 + + + 1701;1702;CS1685 + + + 1701;1702;CS1685 + + + 1701;1702;CS1685 + + + 1701;1702;CS1685 + + + 1701;1702;CS1685 + + + 1701;1702;CS1685 + + + + + True + + + + + + + + + + + + diff --git a/src/IFoxCAD.Cad/IFoxCAD.Gcad.csproj b/src/IFoxCAD.Cad/IFoxCAD.Gcad.csproj new file mode 100644 index 0000000..797e090 --- /dev/null +++ b/src/IFoxCAD.Cad/IFoxCAD.Gcad.csproj @@ -0,0 +1,94 @@ + + + + preview + enable + + net35;net40;net45 + true + true + true + 0.3.6.1 + InspireFunction + xsfhlzh;vicwjb + 基于.NET的Cad二次开发类库 + InspireFunction + https://gitee.com/inspirefunction/ifoxcad + https://gitee.com/inspirefunction/ifoxcad.git + git + IFoxCAD;CAD;AutoCad;C#;NET + 增加四叉树测试. + true + true + true + LICENSE + true + x64 + + + + + + + + + + + + + + + + + + + + DEBUG + + + $(Configuration);ac2008;ac2009 + + + $(Configuration);ac2013 + + + $(Configuration);ac2015 + + + 1701;1702;CS1685 + + + 1701;1702;CS1685 + + + 1701;1702;CS1685 + + + 1701;1702;CS1685 + + + 1701;1702;CS1685 + + + 1701;1702;CS1685 + + + + + True + + + + + + + + + + + + + + diff --git a/src/IFoxCAD.Cad/IFoxCAD.ZWcad.csproj b/src/IFoxCAD.Cad/IFoxCAD.ZWcad.csproj new file mode 100644 index 0000000..797e090 --- /dev/null +++ b/src/IFoxCAD.Cad/IFoxCAD.ZWcad.csproj @@ -0,0 +1,94 @@ + + + + preview + enable + + net35;net40;net45 + true + true + true + 0.3.6.1 + InspireFunction + xsfhlzh;vicwjb + 基于.NET的Cad二次开发类库 + InspireFunction + https://gitee.com/inspirefunction/ifoxcad + https://gitee.com/inspirefunction/ifoxcad.git + git + IFoxCAD;CAD;AutoCad;C#;NET + 增加四叉树测试. + true + true + true + LICENSE + true + x64 + + + + + + + + + + + + + + + + + + + + DEBUG + + + $(Configuration);ac2008;ac2009 + + + $(Configuration);ac2013 + + + $(Configuration);ac2015 + + + 1701;1702;CS1685 + + + 1701;1702;CS1685 + + + 1701;1702;CS1685 + + + 1701;1702;CS1685 + + + 1701;1702;CS1685 + + + 1701;1702;CS1685 + + + + + True + + + + + + + + + + + + + + diff --git a/tests/Test/Test.csproj b/tests/Test/Test.csproj index 8e3469a..640d26c 100644 --- a/tests/Test/Test.csproj +++ b/tests/Test/Test.csproj @@ -19,10 +19,8 @@ - + - - -- Gitee From 5142af1f8c7b84e23ea09dba5c39e8eb30e8b1b6 Mon Sep 17 00:00:00 2001 From: FanJianWei <369034346@qq.com> Date: Sat, 13 Aug 2022 21:00:30 +0800 Subject: [PATCH 355/675] =?UTF-8?q?=E5=B0=8F=E8=B4=B1=E8=B4=B1=E6=8F=90?= =?UTF-8?q?=E4=BA=A4=E6=B5=8B=E8=AF=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- ...6\254\241\346\217\220\344\272\244\346\265\213\350\257\225.txt" | 0 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 "\345\260\217\350\264\261\350\264\261\344\270\200\346\254\241\346\217\220\344\272\244\346\265\213\350\257\225.txt" diff --git "a/\345\260\217\350\264\261\350\264\261\344\270\200\346\254\241\346\217\220\344\272\244\346\265\213\350\257\225.txt" "b/\345\260\217\350\264\261\350\264\261\344\270\200\346\254\241\346\217\220\344\272\244\346\265\213\350\257\225.txt" new file mode 100644 index 0000000..e69de29 -- Gitee From 26ead1835b46c23a0c6db9b0ecc8462ad3010728 Mon Sep 17 00:00:00 2001 From: liuqihong <540762622@qq.com> Date: Sun, 14 Aug 2022 20:30:10 +0800 Subject: [PATCH 356/675] =?UTF-8?q?idIsnull=E6=94=B9=E9=94=99=E4=BA=86?= =?UTF-8?q?=E8=AE=A2=E6=AD=A3=E5=9B=9E=E6=9D=A5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/IFoxCAD.Cad/Runtime/SymbolTable.cs | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/src/IFoxCAD.Cad/Runtime/SymbolTable.cs b/src/IFoxCAD.Cad/Runtime/SymbolTable.cs index 00bba7b..434625e 100644 --- a/src/IFoxCAD.Cad/Runtime/SymbolTable.cs +++ b/src/IFoxCAD.Cad/Runtime/SymbolTable.cs @@ -102,15 +102,15 @@ public ObjectId Add(string name, Action? action = null) { ObjectId id = this[name]; if (id.IsNull) - return id; - - var record = new TRecord() { - Name = name - }; - id = Add(record); - using (record.ForWrite()) - action?.Invoke(record); + var record = new TRecord() + { + Name = name + }; + id = Add(record); + using (record.ForWrite()) + action?.Invoke(record); + } return id; } #endregion -- Gitee From e020dedcaf26cb3f11ce88514e1775fa792681b2 Mon Sep 17 00:00:00 2001 From: liuqihong <540762622@qq.com> Date: Sun, 14 Aug 2022 20:38:19 +0800 Subject: [PATCH 357/675] =?UTF-8?q?=E8=AE=A2=E6=AD=A3idisnull?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/IFoxCAD.Cad.Shared/Runtime/SymbolTable.cs | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/src/IFoxCAD.Cad.Shared/Runtime/SymbolTable.cs b/src/IFoxCAD.Cad.Shared/Runtime/SymbolTable.cs index 00bba7b..434625e 100644 --- a/src/IFoxCAD.Cad.Shared/Runtime/SymbolTable.cs +++ b/src/IFoxCAD.Cad.Shared/Runtime/SymbolTable.cs @@ -102,15 +102,15 @@ public ObjectId Add(string name, Action? action = null) { ObjectId id = this[name]; if (id.IsNull) - return id; - - var record = new TRecord() { - Name = name - }; - id = Add(record); - using (record.ForWrite()) - action?.Invoke(record); + var record = new TRecord() + { + Name = name + }; + id = Add(record); + using (record.ForWrite()) + action?.Invoke(record); + } return id; } #endregion -- Gitee From fddbc3f0f7896961f92a9453cc875b9f1852e9d4 Mon Sep 17 00:00:00 2001 From: liuqihong <540762622@qq.com> Date: Sun, 14 Aug 2022 21:55:44 +0800 Subject: [PATCH 358/675] =?UTF-8?q?=E8=A1=A5=E5=85=85=E5=AD=90=E6=A8=A1?= =?UTF-8?q?=E5=9D=97=E8=AF=B4=E6=98=8E?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 32 ++++++++++++++++++++++++-------- 1 file changed, 24 insertions(+), 8 deletions(-) diff --git a/README.md b/README.md index 6a4b012..4d5cceb 100644 --- a/README.md +++ b/README.md @@ -18,6 +18,22 @@ 由于vs2022抛弃了某几个net版本,所以我们同时安装vs2019和vs2022,然后使用vs2022; 其中的原因是vs2019拥有全部net版本,而vs2022拥有最新的分析器和语法. +#### 让IFox作为您的子模块 + +在IFox的jing分支里面有一个多工程分支,您可以利用此作为您的[git项目子模块](https://www.cnblogs.com/JJBox/p/13876501.html#_label13). + +子模块是以`共享工程`的方式加入到您的工程的,其为`IFoxCAD.Cad.Shared`: + +1. 千万不要用`IFoxCAD.Cad`内的工程作为引用,否则您将遭遇cad加载失效. + +2. 一些全局命名空间的缺少,我们也建议您使用全局命名空间来补充, + 您只需要按照`IFoxCAD.Cad`的`GlobalUsings.cs`文件一样添加就好了. +3. 若您使用acad08版本,需要在您的工程中设置`ac2008`和`ac2009`(大小写敏感)两个预定义标签. + 方能启用08工程中缺少的09工程才有的类. + 同时我们在`IFoxCAD.Cad`中提供了这两个例子. + + + #### IFoxCad 项目模版 可以在vs扩展菜单-管理扩展中搜索ifoxcad,即可安装项目模板。使用项目模版可以方便的创建支持多目标多版本的使用ifoxcad类库的项目和类。如果无法在vs的市场里下载,就去上面的QQ群里下载。 @@ -51,7 +67,7 @@ - 右键项目文件,选择管理nuget程序包。![](./docs/png/nuget1.png) - - 在nuget程序里搜索**ifoxcad**,直接选择最新的版本(如果你是 **net40** 或者 **net35** 的用户,可以安装 **0.1.6** 版本),然后点击安装**IFoxCAD.Cad**,nuget会自动安装ifoxcad依赖的库。(按下图绿色框框里选择浏览,程序包来源选择nuget.org,安装IFoxCAD.Cad包。IFoxCAD.Basal是IFoxCAD.Cad的依赖项会自动安装,如果要开发wpf界面的话,可以安装IFoxCAD.WPF,提供了简单的mvvm支持)![](./docs/png/nuget.png) + - 在nuget程序里搜索**ifoxcad**,直接选择最新的版本(如果您是 **net40** 或者 **net35** 的用户,可以安装 **0.1.6** 版本),然后点击安装**IFoxCAD.Cad**,nuget会自动安装ifoxcad依赖的库。(按下图绿色框框里选择浏览,程序包来源选择nuget.org,安装IFoxCAD.Cad包。IFoxCAD.Basal是IFoxCAD.Cad的依赖项会自动安装,如果要开发wpf界面的话,可以安装IFoxCAD.WPF,提供了简单的mvvm支持)![](./docs/png/nuget.png) - 添加引用 @@ -73,7 +89,7 @@ using var tr = new DBTrans(); var line1 = new Line(new Point3d(0, 0, 0), new Point3d(1, 1, 0)); tr.CurrentSpace.AddEntity(line1); - // 如果你没有添加preview到项目文件里的话:按如下旧语法: + // 如果您没有添加preview到项目文件里的话:按如下旧语法: // using(var tr = new DBTrans()) // { // var line1 = new Line(new Point3d(0, 0, 0), new Point3d(1, 1, 0)); @@ -118,14 +134,14 @@ // 这里可以写任何普通的函数,也可以写下面 AutoTest 类里的实现了 IFoxInitialize 特性的初始化函数 // 继承AutoRegAssem的主要作用是写注册表用来自动加载dll,同时执行实现了 IFoxInitialize 特性的函数 // 注意这里的自动执行是在cad启动后,加载了dll之后执行,而不是运行命令后执行。 - + [IFoxInitialize] public void InitOne() { - //TODO 你想在加载dll之后自动执行的函数 + //TODO 您想在加载dll之后自动执行的函数 // 可以随便在哪里类里 可以多次实现 IFoxInitialize 特性 } - + } //其他的类中的函数: @@ -135,18 +151,18 @@ [IFoxInitialize] public void Initialize() { - //TODO 你想在加载dll之后自动执行的函数 + //TODO 您想在加载dll之后自动执行的函数 } [IFoxInitialize] public void InitTwo() { - //TODO 你想在加载dll之后自动执行的函数 + //TODO 您想在加载dll之后自动执行的函数 // 可以随便在哪里类里 可以多次实现 IFoxInitialize 特性 } [IFoxInitialize(isInitialize: false)] // 特性的参数为false的时候就表示卸载时执行的函数 public void Terminate() { - //TODO 你想在关闭cad时自动执行的函数 + //TODO 您想在关闭cad时自动执行的函数 } } ``` -- Gitee From f9fbce0ab6b4945da6574aa2f5900fa0a09beafd Mon Sep 17 00:00:00 2001 From: liuqihong <540762622@qq.com> Date: Sun, 14 Aug 2022 21:56:13 +0800 Subject: [PATCH 359/675] =?UTF-8?q?=E8=A1=A5=E5=85=85=E5=AD=90=E6=A8=A1?= =?UTF-8?q?=E5=9D=97=E8=AF=B4=E6=98=8E?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 32 ++++++++++++++----- ...0\344\272\244\346\265\213\350\257\225.txt" | 0 2 files changed, 24 insertions(+), 8 deletions(-) delete mode 100644 "\345\260\217\350\264\261\350\264\261\344\270\200\346\254\241\346\217\220\344\272\244\346\265\213\350\257\225.txt" diff --git a/README.md b/README.md index 6a4b012..4d5cceb 100644 --- a/README.md +++ b/README.md @@ -18,6 +18,22 @@ 由于vs2022抛弃了某几个net版本,所以我们同时安装vs2019和vs2022,然后使用vs2022; 其中的原因是vs2019拥有全部net版本,而vs2022拥有最新的分析器和语法. +#### 让IFox作为您的子模块 + +在IFox的jing分支里面有一个多工程分支,您可以利用此作为您的[git项目子模块](https://www.cnblogs.com/JJBox/p/13876501.html#_label13). + +子模块是以`共享工程`的方式加入到您的工程的,其为`IFoxCAD.Cad.Shared`: + +1. 千万不要用`IFoxCAD.Cad`内的工程作为引用,否则您将遭遇cad加载失效. + +2. 一些全局命名空间的缺少,我们也建议您使用全局命名空间来补充, + 您只需要按照`IFoxCAD.Cad`的`GlobalUsings.cs`文件一样添加就好了. +3. 若您使用acad08版本,需要在您的工程中设置`ac2008`和`ac2009`(大小写敏感)两个预定义标签. + 方能启用08工程中缺少的09工程才有的类. + 同时我们在`IFoxCAD.Cad`中提供了这两个例子. + + + #### IFoxCad 项目模版 可以在vs扩展菜单-管理扩展中搜索ifoxcad,即可安装项目模板。使用项目模版可以方便的创建支持多目标多版本的使用ifoxcad类库的项目和类。如果无法在vs的市场里下载,就去上面的QQ群里下载。 @@ -51,7 +67,7 @@ - 右键项目文件,选择管理nuget程序包。![](./docs/png/nuget1.png) - - 在nuget程序里搜索**ifoxcad**,直接选择最新的版本(如果你是 **net40** 或者 **net35** 的用户,可以安装 **0.1.6** 版本),然后点击安装**IFoxCAD.Cad**,nuget会自动安装ifoxcad依赖的库。(按下图绿色框框里选择浏览,程序包来源选择nuget.org,安装IFoxCAD.Cad包。IFoxCAD.Basal是IFoxCAD.Cad的依赖项会自动安装,如果要开发wpf界面的话,可以安装IFoxCAD.WPF,提供了简单的mvvm支持)![](./docs/png/nuget.png) + - 在nuget程序里搜索**ifoxcad**,直接选择最新的版本(如果您是 **net40** 或者 **net35** 的用户,可以安装 **0.1.6** 版本),然后点击安装**IFoxCAD.Cad**,nuget会自动安装ifoxcad依赖的库。(按下图绿色框框里选择浏览,程序包来源选择nuget.org,安装IFoxCAD.Cad包。IFoxCAD.Basal是IFoxCAD.Cad的依赖项会自动安装,如果要开发wpf界面的话,可以安装IFoxCAD.WPF,提供了简单的mvvm支持)![](./docs/png/nuget.png) - 添加引用 @@ -73,7 +89,7 @@ using var tr = new DBTrans(); var line1 = new Line(new Point3d(0, 0, 0), new Point3d(1, 1, 0)); tr.CurrentSpace.AddEntity(line1); - // 如果你没有添加preview到项目文件里的话:按如下旧语法: + // 如果您没有添加preview到项目文件里的话:按如下旧语法: // using(var tr = new DBTrans()) // { // var line1 = new Line(new Point3d(0, 0, 0), new Point3d(1, 1, 0)); @@ -118,14 +134,14 @@ // 这里可以写任何普通的函数,也可以写下面 AutoTest 类里的实现了 IFoxInitialize 特性的初始化函数 // 继承AutoRegAssem的主要作用是写注册表用来自动加载dll,同时执行实现了 IFoxInitialize 特性的函数 // 注意这里的自动执行是在cad启动后,加载了dll之后执行,而不是运行命令后执行。 - + [IFoxInitialize] public void InitOne() { - //TODO 你想在加载dll之后自动执行的函数 + //TODO 您想在加载dll之后自动执行的函数 // 可以随便在哪里类里 可以多次实现 IFoxInitialize 特性 } - + } //其他的类中的函数: @@ -135,18 +151,18 @@ [IFoxInitialize] public void Initialize() { - //TODO 你想在加载dll之后自动执行的函数 + //TODO 您想在加载dll之后自动执行的函数 } [IFoxInitialize] public void InitTwo() { - //TODO 你想在加载dll之后自动执行的函数 + //TODO 您想在加载dll之后自动执行的函数 // 可以随便在哪里类里 可以多次实现 IFoxInitialize 特性 } [IFoxInitialize(isInitialize: false)] // 特性的参数为false的时候就表示卸载时执行的函数 public void Terminate() { - //TODO 你想在关闭cad时自动执行的函数 + //TODO 您想在关闭cad时自动执行的函数 } } ``` diff --git "a/\345\260\217\350\264\261\350\264\261\344\270\200\346\254\241\346\217\220\344\272\244\346\265\213\350\257\225.txt" "b/\345\260\217\350\264\261\350\264\261\344\270\200\346\254\241\346\217\220\344\272\244\346\265\213\350\257\225.txt" deleted file mode 100644 index e69de29..0000000 -- Gitee From 76d50fac9d3c45d2414d7af58942bdf967065a9f Mon Sep 17 00:00:00 2001 From: liuqihong <540762622@qq.com> Date: Sun, 14 Aug 2022 22:07:51 +0800 Subject: [PATCH 360/675] =?UTF-8?q?=E4=BF=AE=E6=94=B9=E8=AF=AD=E6=B3=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 4d5cceb..3c49e93 100644 --- a/README.md +++ b/README.md @@ -18,9 +18,9 @@ 由于vs2022抛弃了某几个net版本,所以我们同时安装vs2019和vs2022,然后使用vs2022; 其中的原因是vs2019拥有全部net版本,而vs2022拥有最新的分析器和语法. -#### 让IFox作为您的子模块 +#### 让 IFox 作为您的子模块 -在IFox的jing分支里面有一个多工程分支,您可以利用此作为您的[git项目子模块](https://www.cnblogs.com/JJBox/p/13876501.html#_label13). +IFox的`jing分支`是一个多cad版本分支,您可以利用此作为您的[git项目子模块](https://www.cnblogs.com/JJBox/p/13876501.html#_label13). 子模块是以`共享工程`的方式加入到您的工程的,其为`IFoxCAD.Cad.Shared`: -- Gitee From fdc9682166540950463dd73dc1be18ac7d782034 Mon Sep 17 00:00:00 2001 From: liuqihong <540762622@qq.com> Date: Sun, 14 Aug 2022 22:08:11 +0800 Subject: [PATCH 361/675] =?UTF-8?q?=E4=BF=AE=E6=94=B9=E8=AF=AD=E6=B3=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 4d5cceb..3c49e93 100644 --- a/README.md +++ b/README.md @@ -18,9 +18,9 @@ 由于vs2022抛弃了某几个net版本,所以我们同时安装vs2019和vs2022,然后使用vs2022; 其中的原因是vs2019拥有全部net版本,而vs2022拥有最新的分析器和语法. -#### 让IFox作为您的子模块 +#### 让 IFox 作为您的子模块 -在IFox的jing分支里面有一个多工程分支,您可以利用此作为您的[git项目子模块](https://www.cnblogs.com/JJBox/p/13876501.html#_label13). +IFox的`jing分支`是一个多cad版本分支,您可以利用此作为您的[git项目子模块](https://www.cnblogs.com/JJBox/p/13876501.html#_label13). 子模块是以`共享工程`的方式加入到您的工程的,其为`IFoxCAD.Cad.Shared`: -- Gitee From e485e34f354526a6f8d1042572825dc51f595e4f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E4=BA=91=E5=82=B2=E5=B4=96-=E5=BF=98=E9=9C=84?= <702099480@qq.com> Date: Fri, 12 Aug 2022 03:19:11 +0000 Subject: [PATCH 362/675] =?UTF-8?q?fixed=20210dcfe=20from=20https://gitee.?= =?UTF-8?q?com/inspirefunction/ifoxcad/pulls/33=20update=20src/IFoxCAD.Cad?= =?UTF-8?q?/ExtensionMethod/CurveEx.cs.=20=E6=B7=BB=E5=8A=A0=E6=89=93?= =?UTF-8?q?=E6=96=AD=E7=82=B9=E8=A1=A8(=E5=8F=82=E6=95=B0=E8=A1=A8)?= =?UTF-8?q?=E6=98=AF=E5=90=A6=E6=8E=92=E5=BA=8F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: 云傲崖-忘霄 <702099480@qq.com> --- .../ExtensionMethod/CurveEx.cs | 61 ++++++++++++++++++- 1 file changed, 58 insertions(+), 3 deletions(-) diff --git a/src/IFoxCAD.Cad.Shared/ExtensionMethod/CurveEx.cs b/src/IFoxCAD.Cad.Shared/ExtensionMethod/CurveEx.cs index 4056bc5..2a73a76 100644 --- a/src/IFoxCAD.Cad.Shared/ExtensionMethod/CurveEx.cs +++ b/src/IFoxCAD.Cad.Shared/ExtensionMethod/CurveEx.cs @@ -1,4 +1,4 @@ -namespace IFoxCAD.Cad; +namespace IFoxCAD.Cad; /// /// 实体类曲线扩展类 @@ -20,15 +20,45 @@ public static double GetLength(this Curve curve) /// /// 曲线 /// 打断参数表 + /// 参数表排序委托 + /// + /// 默认: 按所提供的参数表进行分割打断
+ /// 否则:按委托排序后的参数表进行分割打断 + ///
+ /// /// 打断后曲线的集合 - public static IEnumerable GetSplitCurves(this Curve curve, IEnumerable pars) + public static IEnumerable GetSplitCurves(this Curve curve, + IEnumerable pars, + Func, IEnumerable> func = null) { + if (func != null) + pars = func.Invoke(pars); return curve .GetSplitCurves(new DoubleCollection(pars.ToArray())) .Cast(); } - + + /// + /// 获取分割曲线集合 + /// + /// 曲线 + /// 打断参数表 + /// 对参数表是否进行排序 + /// + /// :按参数值升序排序;
+ /// :不排序,默认值 + ///
+ /// + /// 打断后曲线的集合 + public static IEnumerable GetSplitCurves(this Curve curve, IEnumerable pars, bool isOrder = false) + { + return + curve + .GetSplitCurves(new DoubleCollection(isOrder ? pars.OrderBy(x => x).ToArray() : pars.ToArray())) + .Cast(); + } + /// /// 获取分割曲线集合 /// @@ -42,6 +72,31 @@ public static IEnumerable GetSplitCurves(this Curve curve, IEnumerable(); } + + /// + /// 获取分割曲线集合 + /// + /// 曲线 + /// 打断点表 + /// 对点表是否进行排序 + /// + /// :按参数值升序排序;
+ /// :不排序,默认值 + ///
+ /// + /// 打断后曲线的集合 + public static IEnumerable GetSplitCurves(this Curve curve, + IEnumerable points, + bool isOrder = false) + { + if (isOrder) + points = points.OrderBy(point => curve.GetParameterAtPoint( + curve.GetClosestPointTo(point, false))); + return + curve + .GetSplitCurves(new Point3dCollection(points.ToArray())) + .Cast(); + } /// /// 获取曲线集所围成的封闭区域的曲线集,注意此函数不能处理平行边(两个点及两条线组成的闭合环) -- Gitee From 6f5b4f54d00c2390e5e9974528c8287374f5c686 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E4=BA=91=E5=82=B2=E5=B4=96-=E5=BF=98=E9=9C=84?= <702099480@qq.com> Date: Fri, 12 Aug 2022 03:19:11 +0000 Subject: [PATCH 363/675] =?UTF-8?q?update=20src/IFoxCAD.Cad/ExtensionMetho?= =?UTF-8?q?d/CurveEx.cs.=20=E6=B7=BB=E5=8A=A0=E6=89=93=E6=96=AD=E7=82=B9?= =?UTF-8?q?=E8=A1=A8(=E5=8F=82=E6=95=B0=E8=A1=A8)=E6=98=AF=E5=90=A6?= =?UTF-8?q?=E6=8E=92=E5=BA=8F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: 云傲崖-忘霄 <702099480@qq.com> --- src/IFoxCAD.Cad/ExtensionMethod/CurveEx.cs | 61 ++++++++++++++++++++-- 1 file changed, 58 insertions(+), 3 deletions(-) diff --git a/src/IFoxCAD.Cad/ExtensionMethod/CurveEx.cs b/src/IFoxCAD.Cad/ExtensionMethod/CurveEx.cs index 4056bc5..2a73a76 100644 --- a/src/IFoxCAD.Cad/ExtensionMethod/CurveEx.cs +++ b/src/IFoxCAD.Cad/ExtensionMethod/CurveEx.cs @@ -1,4 +1,4 @@ -namespace IFoxCAD.Cad; +namespace IFoxCAD.Cad; /// /// 实体类曲线扩展类 @@ -20,15 +20,45 @@ public static double GetLength(this Curve curve) /// /// 曲线 /// 打断参数表 + /// 参数表排序委托 + /// + /// 默认: 按所提供的参数表进行分割打断
+ /// 否则:按委托排序后的参数表进行分割打断 + ///
+ /// /// 打断后曲线的集合 - public static IEnumerable GetSplitCurves(this Curve curve, IEnumerable pars) + public static IEnumerable GetSplitCurves(this Curve curve, + IEnumerable pars, + Func, IEnumerable> func = null) { + if (func != null) + pars = func.Invoke(pars); return curve .GetSplitCurves(new DoubleCollection(pars.ToArray())) .Cast(); } - + + /// + /// 获取分割曲线集合 + /// + /// 曲线 + /// 打断参数表 + /// 对参数表是否进行排序 + /// + /// :按参数值升序排序;
+ /// :不排序,默认值 + ///
+ /// + /// 打断后曲线的集合 + public static IEnumerable GetSplitCurves(this Curve curve, IEnumerable pars, bool isOrder = false) + { + return + curve + .GetSplitCurves(new DoubleCollection(isOrder ? pars.OrderBy(x => x).ToArray() : pars.ToArray())) + .Cast(); + } + /// /// 获取分割曲线集合 /// @@ -42,6 +72,31 @@ public static IEnumerable GetSplitCurves(this Curve curve, IEnumerable(); } + + /// + /// 获取分割曲线集合 + /// + /// 曲线 + /// 打断点表 + /// 对点表是否进行排序 + /// + /// :按参数值升序排序;
+ /// :不排序,默认值 + ///
+ /// + /// 打断后曲线的集合 + public static IEnumerable GetSplitCurves(this Curve curve, + IEnumerable points, + bool isOrder = false) + { + if (isOrder) + points = points.OrderBy(point => curve.GetParameterAtPoint( + curve.GetClosestPointTo(point, false))); + return + curve + .GetSplitCurves(new Point3dCollection(points.ToArray())) + .Cast(); + } /// /// 获取曲线集所围成的封闭区域的曲线集,注意此函数不能处理平行边(两个点及两条线组成的闭合环) -- Gitee From d0215b8113fe39358372983e7168fa5a2229f8aa Mon Sep 17 00:00:00 2001 From: liuqihong <540762622@qq.com> Date: Tue, 16 Aug 2022 20:08:11 +0800 Subject: [PATCH 364/675] =?UTF-8?q?=E4=BF=AE=E6=94=B9=E9=94=99=E8=AF=AF(ht?= =?UTF-8?q?tps://gitee.com/inspirefunction/ifoxcad/issues/I5MHMO)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/IFoxCAD.Cad.Shared/SelectionFilter/OpFilter.cs | 6 +++--- tests/Test/TestQuadTree.cs | 2 +- tests/Test/testselectfilter.cs | 4 ++-- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/src/IFoxCAD.Cad.Shared/SelectionFilter/OpFilter.cs b/src/IFoxCAD.Cad.Shared/SelectionFilter/OpFilter.cs index a698aeb..225b68e 100644 --- a/src/IFoxCAD.Cad.Shared/SelectionFilter/OpFilter.cs +++ b/src/IFoxCAD.Cad.Shared/SelectionFilter/OpFilter.cs @@ -76,12 +76,12 @@ public override string ToString() /// !(e.Dxf(0) == "line" & e.Dxf(8) == "0") /// | e.Dxf(0) != "circle" & e.Dxf(8) == "2" & e.Dxf(10) >= p); /// /// 例子2: - /// var f2 = OpFilter.Bulid( + /// var f2 = OpFilter.Build( /// e => e.Or( /// !e.And(e.Dxf(0) == "line", e.Dxf(8) == "0"), /// e.And(e.Dxf(0) != "circle", e.Dxf(8) == "2", @@ -90,7 +90,7 @@ public override string ToString() /// /// 构建过滤器的函数委托 /// 过滤器 - public static OpFilter Bulid(Func func) + public static OpFilter Build(Func func) { return func(new Op()).Filter!; } diff --git a/tests/Test/TestQuadTree.cs b/tests/Test/TestQuadTree.cs index 7670eae..6c5d8b4 100644 --- a/tests/Test/TestQuadTree.cs +++ b/tests/Test/TestQuadTree.cs @@ -413,7 +413,7 @@ void Ssget(QuadTreeSelectMode mode) // //创建四叉树,默认参数无所谓 // var TreeRoot = new QuadTree(new Rect(0, 0, 32525, 32525)); -// var fil = OpFilter.Bulid(e => e.Dxf(0) == "LINE"); +// var fil = OpFilter.Build(e => e.Dxf(0) == "LINE"); // var psr = ed.SSGet("\n 选择需要连接的直线", fil); // if (psr.Status != PromptStatus.OK) return; // var LineEnts = new List(psr.Value.GetEntities(OpenMode.ForWrite)!); diff --git a/tests/Test/testselectfilter.cs b/tests/Test/testselectfilter.cs index d49f278..312b563 100644 --- a/tests/Test/testselectfilter.cs +++ b/tests/Test/testselectfilter.cs @@ -7,12 +7,12 @@ public void Testfilter() { var p = new Point3d(10, 10, 0); - var f = OpFilter.Bulid( + var f = OpFilter.Build( e =>!(e.Dxf(0) == "line" & e.Dxf(8) == "0") | e.Dxf(0) != "circle" & e.Dxf(8) == "2" & e.Dxf(10) >= p); - var f2 = OpFilter.Bulid( + var f2 = OpFilter.Build( e => e.Or( !e.And(e.Dxf(0) == "line", e.Dxf(8) == "0"), e.And(e.Dxf(0) != "circle", e.Dxf(8) == "2", -- Gitee From 1b6fca50725bd84952995d90cb531b2b2a8ec4ae Mon Sep 17 00:00:00 2001 From: liuqihong <540762622@qq.com> Date: Tue, 16 Aug 2022 20:20:38 +0800 Subject: [PATCH 365/675] =?UTF-8?q?=E4=B8=BA=E4=BA=86=E9=98=B2=E6=AD=A2?= =?UTF-8?q?=E5=90=8C=E6=97=B6=E7=BC=96=E8=AF=91=E6=97=B6=E5=80=99=E4=BA=A7?= =?UTF-8?q?=E7=94=9F=E5=8D=A0=E7=94=A8=E9=94=99=E8=AF=AF=E6=89=80=E4=BB=A5?= =?UTF-8?q?=E4=BF=AE=E6=94=B9=E4=BA=86=E7=9B=B8=E5=AF=B9=E8=B7=AF=E5=BE=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- IFoxCAD.sln | 36 ++++++++-------- .../{ => IFoxCAD.Aacad08}/GlobalUsings.cs | 0 .../IFoxCAD.Acad08.csproj | 13 +++--- .../IFoxCAD.Aacad09plus/GlobalUsings.cs | 41 +++++++++++++++++++ .../IFoxCAD.Acad09plus.csproj | 6 +-- src/IFoxCAD.Cad/IFoxCAD.Gcad/GlobalUsings.cs | 41 +++++++++++++++++++ .../{ => IFoxCAD.Gcad}/IFoxCAD.Gcad.csproj | 6 +-- src/IFoxCAD.Cad/IFoxCAD.ZWcad/GlobalUsings.cs | 41 +++++++++++++++++++ .../{ => IFoxCAD.ZWcad}/IFoxCAD.ZWcad.csproj | 6 +-- tests/Test/Test.csproj | 2 +- 10 files changed, 155 insertions(+), 37 deletions(-) rename src/IFoxCAD.Cad/{ => IFoxCAD.Aacad08}/GlobalUsings.cs (100%) rename src/IFoxCAD.Cad/{ => IFoxCAD.Aacad08}/IFoxCAD.Acad08.csproj (92%) create mode 100644 src/IFoxCAD.Cad/IFoxCAD.Aacad09plus/GlobalUsings.cs rename src/IFoxCAD.Cad/{ => IFoxCAD.Aacad09plus}/IFoxCAD.Acad09plus.csproj (95%) create mode 100644 src/IFoxCAD.Cad/IFoxCAD.Gcad/GlobalUsings.cs rename src/IFoxCAD.Cad/{ => IFoxCAD.Gcad}/IFoxCAD.Gcad.csproj (95%) create mode 100644 src/IFoxCAD.Cad/IFoxCAD.ZWcad/GlobalUsings.cs rename src/IFoxCAD.Cad/{ => IFoxCAD.ZWcad}/IFoxCAD.ZWcad.csproj (95%) diff --git a/IFoxCAD.sln b/IFoxCAD.sln index aef4706..43c5de7 100644 --- a/IFoxCAD.sln +++ b/IFoxCAD.sln @@ -11,17 +11,17 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "IFoxCAD.Basal", "src\IFoxCA EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "TestConsole", "tests\TestConsole\TestConsole.csproj", "{E2873F0B-CAD2-45E8-8FF0-C05C0C6AD955}" EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "IFoxCAD.Gcad", "src\IFoxCAD.Cad\IFoxCAD.Gcad.csproj", "{D7756AF6-601D-40C2-97E9-940E5AFC2E08}" +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Cad", "Cad", "{465C4E39-FBA2-417A-AB31-BDC01601F420}" EndProject Project("{D954291E-2A0B-460D-934E-DC6B0785DB48}") = "IFoxCAD.Cad.Shared", "src\IFoxCAD.Cad.Shared\IFoxCAD.Cad.Shared.shproj", "{82FB8450-B971-4E30-859F-4B2DDB81F590}" EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Cad", "Cad", "{465C4E39-FBA2-417A-AB31-BDC01601F420}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "IFoxCAD.Gcad", "src\IFoxCAD.Cad\IFoxCAD.Gcad\IFoxCAD.Gcad.csproj", "{D7756AF6-601D-40C2-97E9-940E5AFC2E08}" EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "IFoxCAD.Acad08", "src\IFoxCAD.Cad\IFoxCAD.Acad08.csproj", "{F5C0DA54-2031-436D-B6FA-03C745B98862}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "IFoxCAD.ZWcad", "src\IFoxCAD.Cad\IFoxCAD.ZWcad\IFoxCAD.ZWcad.csproj", "{C6FC723B-B4CD-475A-BBA7-4FE5CE82AC5A}" EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "IFoxCAD.Acad09plus", "src\IFoxCAD.Cad\IFoxCAD.Acad09plus.csproj", "{34213E53-C0F5-49DF-9FB2-0A5D861013EF}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "IFoxCAD.Acad08", "src\IFoxCAD.Cad\IFoxCAD.Aacad08\IFoxCAD.Acad08.csproj", "{36F87D06-88B3-45E3-A2A8-0FC737B25428}" EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "IFoxCAD.ZWcad", "src\IFoxCAD.Cad\IFoxCAD.ZWcad.csproj", "{C6FC723B-B4CD-475A-BBA7-4FE5CE82AC5A}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "IFoxCAD.Acad09plus", "src\IFoxCAD.Cad\IFoxCAD.Aacad09plus\IFoxCAD.Acad09plus.csproj", "{CC46E4E2-90DF-48F5-B3D7-83FAFED58F2A}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution @@ -49,37 +49,37 @@ Global {D7756AF6-601D-40C2-97E9-940E5AFC2E08}.Debug|Any CPU.Build.0 = Debug|Any CPU {D7756AF6-601D-40C2-97E9-940E5AFC2E08}.Release|Any CPU.ActiveCfg = Release|Any CPU {D7756AF6-601D-40C2-97E9-940E5AFC2E08}.Release|Any CPU.Build.0 = Release|Any CPU - {F5C0DA54-2031-436D-B6FA-03C745B98862}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {F5C0DA54-2031-436D-B6FA-03C745B98862}.Debug|Any CPU.Build.0 = Debug|Any CPU - {F5C0DA54-2031-436D-B6FA-03C745B98862}.Release|Any CPU.ActiveCfg = Release|Any CPU - {F5C0DA54-2031-436D-B6FA-03C745B98862}.Release|Any CPU.Build.0 = Release|Any CPU - {34213E53-C0F5-49DF-9FB2-0A5D861013EF}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {34213E53-C0F5-49DF-9FB2-0A5D861013EF}.Debug|Any CPU.Build.0 = Debug|Any CPU - {34213E53-C0F5-49DF-9FB2-0A5D861013EF}.Release|Any CPU.ActiveCfg = Release|Any CPU - {34213E53-C0F5-49DF-9FB2-0A5D861013EF}.Release|Any CPU.Build.0 = Release|Any CPU {C6FC723B-B4CD-475A-BBA7-4FE5CE82AC5A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {C6FC723B-B4CD-475A-BBA7-4FE5CE82AC5A}.Debug|Any CPU.Build.0 = Debug|Any CPU {C6FC723B-B4CD-475A-BBA7-4FE5CE82AC5A}.Release|Any CPU.ActiveCfg = Release|Any CPU {C6FC723B-B4CD-475A-BBA7-4FE5CE82AC5A}.Release|Any CPU.Build.0 = Release|Any CPU + {36F87D06-88B3-45E3-A2A8-0FC737B25428}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {36F87D06-88B3-45E3-A2A8-0FC737B25428}.Debug|Any CPU.Build.0 = Debug|Any CPU + {36F87D06-88B3-45E3-A2A8-0FC737B25428}.Release|Any CPU.ActiveCfg = Release|Any CPU + {36F87D06-88B3-45E3-A2A8-0FC737B25428}.Release|Any CPU.Build.0 = Release|Any CPU + {CC46E4E2-90DF-48F5-B3D7-83FAFED58F2A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {CC46E4E2-90DF-48F5-B3D7-83FAFED58F2A}.Debug|Any CPU.Build.0 = Debug|Any CPU + {CC46E4E2-90DF-48F5-B3D7-83FAFED58F2A}.Release|Any CPU.ActiveCfg = Release|Any CPU + {CC46E4E2-90DF-48F5-B3D7-83FAFED58F2A}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE EndGlobalSection GlobalSection(NestedProjects) = preSolution - {D7756AF6-601D-40C2-97E9-940E5AFC2E08} = {465C4E39-FBA2-417A-AB31-BDC01601F420} {82FB8450-B971-4E30-859F-4B2DDB81F590} = {465C4E39-FBA2-417A-AB31-BDC01601F420} - {F5C0DA54-2031-436D-B6FA-03C745B98862} = {465C4E39-FBA2-417A-AB31-BDC01601F420} - {34213E53-C0F5-49DF-9FB2-0A5D861013EF} = {465C4E39-FBA2-417A-AB31-BDC01601F420} + {D7756AF6-601D-40C2-97E9-940E5AFC2E08} = {465C4E39-FBA2-417A-AB31-BDC01601F420} {C6FC723B-B4CD-475A-BBA7-4FE5CE82AC5A} = {465C4E39-FBA2-417A-AB31-BDC01601F420} + {36F87D06-88B3-45E3-A2A8-0FC737B25428} = {465C4E39-FBA2-417A-AB31-BDC01601F420} + {CC46E4E2-90DF-48F5-B3D7-83FAFED58F2A} = {465C4E39-FBA2-417A-AB31-BDC01601F420} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {31D6C754-CF6B-4AB0-9861-6923DD312350} EndGlobalSection GlobalSection(SharedMSBuildProjectFiles) = preSolution - src\IFoxCAD.Cad.Shared\IFoxCAD.Cad.Shared.projitems*{34213e53-c0f5-49df-9fb2-0a5d861013ef}*SharedItemsImports = 5 + src\IFoxCAD.Cad.Shared\IFoxCAD.Cad.Shared.projitems*{36f87d06-88b3-45e3-a2a8-0fc737b25428}*SharedItemsImports = 5 src\IFoxCAD.Cad.Shared\IFoxCAD.Cad.Shared.projitems*{82fb8450-b971-4e30-859f-4b2ddb81f590}*SharedItemsImports = 13 src\IFoxCAD.Cad.Shared\IFoxCAD.Cad.Shared.projitems*{c6fc723b-b4cd-475a-bba7-4fe5ce82ac5a}*SharedItemsImports = 5 + src\IFoxCAD.Cad.Shared\IFoxCAD.Cad.Shared.projitems*{cc46e4e2-90df-48f5-b3d7-83fafed58f2a}*SharedItemsImports = 5 src\IFoxCAD.Cad.Shared\IFoxCAD.Cad.Shared.projitems*{d7756af6-601d-40c2-97e9-940e5afc2e08}*SharedItemsImports = 5 - src\IFoxCAD.Cad.Shared\IFoxCAD.Cad.Shared.projitems*{f5c0da54-2031-436d-b6fa-03c745b98862}*SharedItemsImports = 5 EndGlobalSection EndGlobal diff --git a/src/IFoxCAD.Cad/GlobalUsings.cs b/src/IFoxCAD.Cad/IFoxCAD.Aacad08/GlobalUsings.cs similarity index 100% rename from src/IFoxCAD.Cad/GlobalUsings.cs rename to src/IFoxCAD.Cad/IFoxCAD.Aacad08/GlobalUsings.cs diff --git a/src/IFoxCAD.Cad/IFoxCAD.Acad08.csproj b/src/IFoxCAD.Cad/IFoxCAD.Aacad08/IFoxCAD.Acad08.csproj similarity index 92% rename from src/IFoxCAD.Cad/IFoxCAD.Acad08.csproj rename to src/IFoxCAD.Cad/IFoxCAD.Aacad08/IFoxCAD.Acad08.csproj index 82097c2..d1bffe0 100644 --- a/src/IFoxCAD.Cad/IFoxCAD.Acad08.csproj +++ b/src/IFoxCAD.Cad/IFoxCAD.Aacad08/IFoxCAD.Acad08.csproj @@ -72,12 +72,11 @@ 1701;1702;CS1685 - - - True - - + + + True + - + - + diff --git a/src/IFoxCAD.Cad/IFoxCAD.Aacad09plus/GlobalUsings.cs b/src/IFoxCAD.Cad/IFoxCAD.Aacad09plus/GlobalUsings.cs new file mode 100644 index 0000000..a125299 --- /dev/null +++ b/src/IFoxCAD.Cad/IFoxCAD.Aacad09plus/GlobalUsings.cs @@ -0,0 +1,41 @@ +/// 系统引用 +global using System; +global using System.Collections; +global using System.Collections.Generic; +global using System.IO; +global using System.Linq; +global using System.Text; +global using System.Reflection; +global using System.Text.RegularExpressions; +global using Microsoft.Win32; +global using System.ComponentModel; +global using System.Runtime.InteropServices; + +global using Exception = System.Exception; + +/// autocad 引用 +global using Autodesk.AutoCAD.ApplicationServices; +global using Autodesk.AutoCAD.EditorInput; +global using Autodesk.AutoCAD.Colors; +global using Autodesk.AutoCAD.DatabaseServices; +global using Autodesk.AutoCAD.Geometry; +global using Autodesk.AutoCAD.Runtime; +global using Acap = Autodesk.AutoCAD.ApplicationServices.Application; + +global using Autodesk.AutoCAD.DatabaseServices.Filters; +global using Autodesk.AutoCAD; + +//jig此命名空间容易引起Polyline等等重义,因此不放入全局空间 +//using Autodesk.AutoCAD.GraphicsInterface; +global using WorldDraw = Autodesk.AutoCAD.GraphicsInterface.WorldDraw; +global using Manager = Autodesk.AutoCAD.GraphicsSystem.Manager; + +global using Group = Autodesk.AutoCAD.DatabaseServices.Group; +global using Viewport = Autodesk.AutoCAD.DatabaseServices.Viewport; + +global using System.Collections.Specialized; +global using Registry = Microsoft.Win32.Registry; +global using RegistryKey = Microsoft.Win32.RegistryKey; + +/// ifoxcad.basal 引用 +global using IFoxCAD.Basal; \ No newline at end of file diff --git a/src/IFoxCAD.Cad/IFoxCAD.Acad09plus.csproj b/src/IFoxCAD.Cad/IFoxCAD.Aacad09plus/IFoxCAD.Acad09plus.csproj similarity index 95% rename from src/IFoxCAD.Cad/IFoxCAD.Acad09plus.csproj rename to src/IFoxCAD.Cad/IFoxCAD.Aacad09plus/IFoxCAD.Acad09plus.csproj index 7e5eeee..eada737 100644 --- a/src/IFoxCAD.Cad/IFoxCAD.Acad09plus.csproj +++ b/src/IFoxCAD.Cad/IFoxCAD.Aacad09plus/IFoxCAD.Acad09plus.csproj @@ -84,7 +84,7 @@ - + True @@ -95,8 +95,8 @@ --> - + - + diff --git a/src/IFoxCAD.Cad/IFoxCAD.Gcad/GlobalUsings.cs b/src/IFoxCAD.Cad/IFoxCAD.Gcad/GlobalUsings.cs new file mode 100644 index 0000000..a125299 --- /dev/null +++ b/src/IFoxCAD.Cad/IFoxCAD.Gcad/GlobalUsings.cs @@ -0,0 +1,41 @@ +/// 系统引用 +global using System; +global using System.Collections; +global using System.Collections.Generic; +global using System.IO; +global using System.Linq; +global using System.Text; +global using System.Reflection; +global using System.Text.RegularExpressions; +global using Microsoft.Win32; +global using System.ComponentModel; +global using System.Runtime.InteropServices; + +global using Exception = System.Exception; + +/// autocad 引用 +global using Autodesk.AutoCAD.ApplicationServices; +global using Autodesk.AutoCAD.EditorInput; +global using Autodesk.AutoCAD.Colors; +global using Autodesk.AutoCAD.DatabaseServices; +global using Autodesk.AutoCAD.Geometry; +global using Autodesk.AutoCAD.Runtime; +global using Acap = Autodesk.AutoCAD.ApplicationServices.Application; + +global using Autodesk.AutoCAD.DatabaseServices.Filters; +global using Autodesk.AutoCAD; + +//jig此命名空间容易引起Polyline等等重义,因此不放入全局空间 +//using Autodesk.AutoCAD.GraphicsInterface; +global using WorldDraw = Autodesk.AutoCAD.GraphicsInterface.WorldDraw; +global using Manager = Autodesk.AutoCAD.GraphicsSystem.Manager; + +global using Group = Autodesk.AutoCAD.DatabaseServices.Group; +global using Viewport = Autodesk.AutoCAD.DatabaseServices.Viewport; + +global using System.Collections.Specialized; +global using Registry = Microsoft.Win32.Registry; +global using RegistryKey = Microsoft.Win32.RegistryKey; + +/// ifoxcad.basal 引用 +global using IFoxCAD.Basal; \ No newline at end of file diff --git a/src/IFoxCAD.Cad/IFoxCAD.Gcad.csproj b/src/IFoxCAD.Cad/IFoxCAD.Gcad/IFoxCAD.Gcad.csproj similarity index 95% rename from src/IFoxCAD.Cad/IFoxCAD.Gcad.csproj rename to src/IFoxCAD.Cad/IFoxCAD.Gcad/IFoxCAD.Gcad.csproj index 797e090..a1b6b9b 100644 --- a/src/IFoxCAD.Cad/IFoxCAD.Gcad.csproj +++ b/src/IFoxCAD.Cad/IFoxCAD.Gcad/IFoxCAD.Gcad.csproj @@ -74,7 +74,7 @@ - + True @@ -85,10 +85,8 @@ --> - + - - diff --git a/src/IFoxCAD.Cad/IFoxCAD.ZWcad/GlobalUsings.cs b/src/IFoxCAD.Cad/IFoxCAD.ZWcad/GlobalUsings.cs new file mode 100644 index 0000000..a125299 --- /dev/null +++ b/src/IFoxCAD.Cad/IFoxCAD.ZWcad/GlobalUsings.cs @@ -0,0 +1,41 @@ +/// 系统引用 +global using System; +global using System.Collections; +global using System.Collections.Generic; +global using System.IO; +global using System.Linq; +global using System.Text; +global using System.Reflection; +global using System.Text.RegularExpressions; +global using Microsoft.Win32; +global using System.ComponentModel; +global using System.Runtime.InteropServices; + +global using Exception = System.Exception; + +/// autocad 引用 +global using Autodesk.AutoCAD.ApplicationServices; +global using Autodesk.AutoCAD.EditorInput; +global using Autodesk.AutoCAD.Colors; +global using Autodesk.AutoCAD.DatabaseServices; +global using Autodesk.AutoCAD.Geometry; +global using Autodesk.AutoCAD.Runtime; +global using Acap = Autodesk.AutoCAD.ApplicationServices.Application; + +global using Autodesk.AutoCAD.DatabaseServices.Filters; +global using Autodesk.AutoCAD; + +//jig此命名空间容易引起Polyline等等重义,因此不放入全局空间 +//using Autodesk.AutoCAD.GraphicsInterface; +global using WorldDraw = Autodesk.AutoCAD.GraphicsInterface.WorldDraw; +global using Manager = Autodesk.AutoCAD.GraphicsSystem.Manager; + +global using Group = Autodesk.AutoCAD.DatabaseServices.Group; +global using Viewport = Autodesk.AutoCAD.DatabaseServices.Viewport; + +global using System.Collections.Specialized; +global using Registry = Microsoft.Win32.Registry; +global using RegistryKey = Microsoft.Win32.RegistryKey; + +/// ifoxcad.basal 引用 +global using IFoxCAD.Basal; \ No newline at end of file diff --git a/src/IFoxCAD.Cad/IFoxCAD.ZWcad.csproj b/src/IFoxCAD.Cad/IFoxCAD.ZWcad/IFoxCAD.ZWcad.csproj similarity index 95% rename from src/IFoxCAD.Cad/IFoxCAD.ZWcad.csproj rename to src/IFoxCAD.Cad/IFoxCAD.ZWcad/IFoxCAD.ZWcad.csproj index 797e090..a1b6b9b 100644 --- a/src/IFoxCAD.Cad/IFoxCAD.ZWcad.csproj +++ b/src/IFoxCAD.Cad/IFoxCAD.ZWcad/IFoxCAD.ZWcad.csproj @@ -74,7 +74,7 @@ - + True @@ -85,10 +85,8 @@ --> - + - - diff --git a/tests/Test/Test.csproj b/tests/Test/Test.csproj index 640d26c..33cb53e 100644 --- a/tests/Test/Test.csproj +++ b/tests/Test/Test.csproj @@ -19,7 +19,7 @@ - + -- Gitee From 87133ca06a5df15058e8412cf2dbb491355ce60b Mon Sep 17 00:00:00 2001 From: liuqihong <540762622@qq.com> Date: Tue, 16 Aug 2022 20:27:43 +0800 Subject: [PATCH 366/675] =?UTF-8?q?=E4=BC=98=E5=8C=96=E9=93=BE=E6=8E=A5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 2 +- src/IFoxCAD.Cad/IFoxCAD.Aacad08/IFoxCAD.Acad08.csproj | 2 +- src/IFoxCAD.Cad/IFoxCAD.Aacad09plus/IFoxCAD.Acad09plus.csproj | 1 + 3 files changed, 3 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 3c49e93..1796e2a 100644 --- a/README.md +++ b/README.md @@ -20,7 +20,7 @@ #### 让 IFox 作为您的子模块 -IFox的`jing分支`是一个多cad版本分支,您可以利用此作为您的[git项目子模块](https://www.cnblogs.com/JJBox/p/13876501.html#_label13). +IFox的[jing分支](https://gitee.com/inspirefunction/ifoxcad/tree/jing/)是一个多cad版本分支,您可以利用此作为您的[git项目子模块](https://www.cnblogs.com/JJBox/p/13876501.html#_label13). 子模块是以`共享工程`的方式加入到您的工程的,其为`IFoxCAD.Cad.Shared`: diff --git a/src/IFoxCAD.Cad/IFoxCAD.Aacad08/IFoxCAD.Acad08.csproj b/src/IFoxCAD.Cad/IFoxCAD.Aacad08/IFoxCAD.Acad08.csproj index d1bffe0..dedffc6 100644 --- a/src/IFoxCAD.Cad/IFoxCAD.Aacad08/IFoxCAD.Acad08.csproj +++ b/src/IFoxCAD.Cad/IFoxCAD.Aacad08/IFoxCAD.Acad08.csproj @@ -23,7 +23,7 @@ true LICENSE true - x64 + diff --git a/src/IFoxCAD.Cad/IFoxCAD.Aacad09plus/IFoxCAD.Acad09plus.csproj b/src/IFoxCAD.Cad/IFoxCAD.Aacad09plus/IFoxCAD.Acad09plus.csproj index eada737..59247bf 100644 --- a/src/IFoxCAD.Cad/IFoxCAD.Aacad09plus/IFoxCAD.Acad09plus.csproj +++ b/src/IFoxCAD.Cad/IFoxCAD.Aacad09plus/IFoxCAD.Acad09plus.csproj @@ -96,6 +96,7 @@ + -- Gitee From 485fd4f738a2f6758bbe1842f0c7f7bb21a931b3 Mon Sep 17 00:00:00 2001 From: liuqihong <540762622@qq.com> Date: Tue, 16 Aug 2022 20:28:25 +0800 Subject: [PATCH 367/675] =?UTF-8?q?=E4=BC=98=E5=8C=96=E9=93=BE=E6=8E=A5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 3c49e93..1796e2a 100644 --- a/README.md +++ b/README.md @@ -20,7 +20,7 @@ #### 让 IFox 作为您的子模块 -IFox的`jing分支`是一个多cad版本分支,您可以利用此作为您的[git项目子模块](https://www.cnblogs.com/JJBox/p/13876501.html#_label13). +IFox的[jing分支](https://gitee.com/inspirefunction/ifoxcad/tree/jing/)是一个多cad版本分支,您可以利用此作为您的[git项目子模块](https://www.cnblogs.com/JJBox/p/13876501.html#_label13). 子模块是以`共享工程`的方式加入到您的工程的,其为`IFoxCAD.Cad.Shared`: -- Gitee From 1b4e1c8454b60a287401b41778e8f0277943699d Mon Sep 17 00:00:00 2001 From: liuqihong <540762622@qq.com> Date: Tue, 16 Aug 2022 20:36:12 +0800 Subject: [PATCH 368/675] =?UTF-8?q?=E4=BF=AE=E6=94=B9=E9=94=99=E8=AF=AF?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/IFoxCAD.Cad/IFoxCAD.Aacad09plus/IFoxCAD.Acad09plus.csproj | 1 - 1 file changed, 1 deletion(-) diff --git a/src/IFoxCAD.Cad/IFoxCAD.Aacad09plus/IFoxCAD.Acad09plus.csproj b/src/IFoxCAD.Cad/IFoxCAD.Aacad09plus/IFoxCAD.Acad09plus.csproj index 59247bf..eada737 100644 --- a/src/IFoxCAD.Cad/IFoxCAD.Aacad09plus/IFoxCAD.Acad09plus.csproj +++ b/src/IFoxCAD.Cad/IFoxCAD.Aacad09plus/IFoxCAD.Acad09plus.csproj @@ -96,7 +96,6 @@ - -- Gitee From 7bfc0529ac03626b3d51bee6e72f953880b10fe5 Mon Sep 17 00:00:00 2001 From: liuqihong <540762622@qq.com> Date: Tue, 16 Aug 2022 21:53:39 +0800 Subject: [PATCH 369/675] =?UTF-8?q?=E5=A4=84=E7=90=86=E7=A9=BA=E7=B1=BB?= =?UTF-8?q?=E5=9E=8B=E6=A3=80=E6=9F=A5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/IFoxCAD.Cad.Shared/ExtensionMethod/CurveEx.cs | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/src/IFoxCAD.Cad.Shared/ExtensionMethod/CurveEx.cs b/src/IFoxCAD.Cad.Shared/ExtensionMethod/CurveEx.cs index 2a73a76..3e9ead4 100644 --- a/src/IFoxCAD.Cad.Shared/ExtensionMethod/CurveEx.cs +++ b/src/IFoxCAD.Cad.Shared/ExtensionMethod/CurveEx.cs @@ -29,8 +29,11 @@ public static double GetLength(this Curve curve) /// 打断后曲线的集合 public static IEnumerable GetSplitCurves(this Curve curve, IEnumerable pars, - Func, IEnumerable> func = null) + Func, IEnumerable>? func = null) { + if (pars == null) + throw new ArgumentNullException(nameof(pars)); + if (func != null) pars = func.Invoke(pars); return @@ -38,7 +41,7 @@ public static IEnumerable GetSplitCurves(this Curve curve, .GetSplitCurves(new DoubleCollection(pars.ToArray())) .Cast(); } - + /// /// 获取分割曲线集合 /// @@ -58,7 +61,7 @@ public static IEnumerable GetSplitCurves(this Curve curve, IEnumerable x).ToArray() : pars.ToArray())) .Cast(); } - + /// /// 获取分割曲线集合 /// @@ -72,7 +75,7 @@ public static IEnumerable GetSplitCurves(this Curve curve, IEnumerable(); } - + /// /// 获取分割曲线集合 /// @@ -149,7 +152,7 @@ public static List BreakCurve(this List curves) paramss.Add(new List()); } } - + //var oldCurves = new List(); var newCurves = new List(); var cci3d = new CurveCurveIntersector3d(); -- Gitee From 741b126bb680db1c71087af98975b7caa2542d93 Mon Sep 17 00:00:00 2001 From: liuqihong <540762622@qq.com> Date: Tue, 16 Aug 2022 22:05:30 +0800 Subject: [PATCH 370/675] =?UTF-8?q?=E5=A4=84=E7=90=86=E5=AD=97=E7=AC=A6?= =?UTF-8?q?=E4=B8=B2=E9=A2=91=E7=B9=81=E6=89=A7=E8=A1=8C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../SelectionFilter/OpFilter.cs | 23 ++++++++----------- 1 file changed, 9 insertions(+), 14 deletions(-) diff --git a/src/IFoxCAD.Cad.Shared/SelectionFilter/OpFilter.cs b/src/IFoxCAD.Cad.Shared/SelectionFilter/OpFilter.cs index 225b68e..48843f4 100644 --- a/src/IFoxCAD.Cad.Shared/SelectionFilter/OpFilter.cs +++ b/src/IFoxCAD.Cad.Shared/SelectionFilter/OpFilter.cs @@ -10,6 +10,11 @@ public abstract class OpFilter /// public abstract string Name { get; } + /// + /// 只读属性,表示这个过滤器取反 + /// + public OpFilter Not => new OpNot(this); + /// /// 获取TypedValue类型的值的迭代器的抽象方法,子类必须重写 /// @@ -26,14 +31,6 @@ public abstract class OpFilter return item.Not; } - /// - /// 只读属性,表示这个过滤器取反 - /// - public OpFilter Not - { - get { return new OpNot(this); } - } - /// /// 过滤器值转换为 TypedValue 类型数组 /// @@ -61,10 +58,10 @@ public static implicit operator SelectionFilter(OpFilter item) /// 字符串 public override string ToString() { - string s = ""; + var sb = new StringBuilder(); foreach (var value in GetValues()) - s += value.ToString(); - return s; + sb.Append(value); + return sb.ToString(); } /// @@ -107,9 +104,7 @@ public class Op /// internal OpFilter? Filter { get; private set; } - internal Op() - { - } + internal Op() { } private Op(OpFilter filter) { -- Gitee From 268e6d12762cd7b5af0b35c6ae0702c808b9fa3d Mon Sep 17 00:00:00 2001 From: vicwjb <403009819@qq.com> Date: Tue, 16 Aug 2022 22:24:09 +0800 Subject: [PATCH 371/675] =?UTF-8?q?=E5=8F=91=E5=B8=83=E6=96=B0=E7=89=88?= =?UTF-8?q?=E6=9C=AC=E5=88=B0nuget?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/IFoxCAD.Basal/IFoxCAD.Basal.csproj | 4 +- src/IFoxCAD.Cad/IFoxCAD.Cad.csproj | 107 ++++++++++--------------- 2 files changed, 45 insertions(+), 66 deletions(-) diff --git a/src/IFoxCAD.Basal/IFoxCAD.Basal.csproj b/src/IFoxCAD.Basal/IFoxCAD.Basal.csproj index 73d940d..41ed7c1 100644 --- a/src/IFoxCAD.Basal/IFoxCAD.Basal.csproj +++ b/src/IFoxCAD.Basal/IFoxCAD.Basal.csproj @@ -6,7 +6,7 @@ net35;net40;net45 true - 0.3.5.1 + 0.3.6 InspireFunction xsfhlzh;vicwjb 基于.NET的二次开发基本类库 @@ -15,7 +15,7 @@ https://gitee.com/inspirefunction/ifoxcad.git git IFoxCAD;C#;NET;Common;Basal - 直接集成元组和索引切片. + 直接sortedset. true true LICENSE diff --git a/src/IFoxCAD.Cad/IFoxCAD.Cad.csproj b/src/IFoxCAD.Cad/IFoxCAD.Cad.csproj index c5759bc..07fad40 100644 --- a/src/IFoxCAD.Cad/IFoxCAD.Cad.csproj +++ b/src/IFoxCAD.Cad/IFoxCAD.Cad.csproj @@ -1,14 +1,12 @@ - preview - enable - net35;net40;net45 true - true - true - 0.3.6.1 + enable + true + true + 0.3.7 InspireFunction xsfhlzh;vicwjb 基于.NET的Cad二次开发类库 @@ -17,84 +15,65 @@ https://gitee.com/inspirefunction/ifoxcad.git git IFoxCAD;CAD;AutoCad;C#;NET - 增加四叉树测试. + 增加很多东西. true true + preview true LICENSE true x64 - - - + + + runtime - - - runtime - - - - - - runtime - - - 4.3.0 - - - + + + runtime + + - - - DEBUG - - - $(Configuration);ac2009 - - - $(Configuration);ac2013 - - - $(Configuration);ac2015 - - - 1701;1702;CS1685 - - - 1701;1702;CS1685 - - - 1701;1702;CS1685 - - - 1701;1702;CS1685 - - - 1701;1702;CS1685 - - - 1701;1702;CS1685 - - + + DEBUG + + + $(Configuration);ac2009 + + + $(Configuration);ac2013 + + + $(Configuration);ac2015 + + - - True - - + + True + + - + - + - + + -- Gitee From 364b4bdfd09916b7d29a20ce36319c06887b5870 Mon Sep 17 00:00:00 2001 From: liuqihong <540762622@qq.com> Date: Tue, 16 Aug 2022 22:45:29 +0800 Subject: [PATCH 372/675] =?UTF-8?q?=E4=BF=9D=E6=8C=81=E5=92=8Cdev=E5=8F=91?= =?UTF-8?q?=E5=B8=83=E4=B8=80=E6=A0=B7?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../IFoxCAD.Aacad08/IFoxCAD.Acad08.csproj | 30 ++++--------------- .../IFoxCAD.Acad09plus.csproj | 30 ++++--------------- 2 files changed, 11 insertions(+), 49 deletions(-) diff --git a/src/IFoxCAD.Cad/IFoxCAD.Aacad08/IFoxCAD.Acad08.csproj b/src/IFoxCAD.Cad/IFoxCAD.Aacad08/IFoxCAD.Acad08.csproj index dedffc6..cf9665d 100644 --- a/src/IFoxCAD.Cad/IFoxCAD.Aacad08/IFoxCAD.Acad08.csproj +++ b/src/IFoxCAD.Cad/IFoxCAD.Aacad08/IFoxCAD.Acad08.csproj @@ -1,25 +1,24 @@  - preview - enable - net35;net40;net45 true + enable true true - 0.3.6.1 + 0.3.7 InspireFunction - xsfhlzh;vicwjb + xsfhlzh;vicwjb;liuqihong 基于.NET的Cad二次开发类库 InspireFunction https://gitee.com/inspirefunction/ifoxcad https://gitee.com/inspirefunction/ifoxcad.git git IFoxCAD;CAD;AutoCad;C#;NET - 增加四叉树测试. + 增加很多东西. true true + preview true LICENSE true @@ -54,24 +53,7 @@ $(Configuration);ac2015 - - 1701;1702;CS1685 - - - 1701;1702;CS1685 - - - 1701;1702;CS1685 - - - 1701;1702;CS1685 - - - 1701;1702;CS1685 - - - 1701;1702;CS1685 - + diff --git a/src/IFoxCAD.Cad/IFoxCAD.Aacad09plus/IFoxCAD.Acad09plus.csproj b/src/IFoxCAD.Cad/IFoxCAD.Aacad09plus/IFoxCAD.Acad09plus.csproj index eada737..7852e32 100644 --- a/src/IFoxCAD.Cad/IFoxCAD.Aacad09plus/IFoxCAD.Acad09plus.csproj +++ b/src/IFoxCAD.Cad/IFoxCAD.Aacad09plus/IFoxCAD.Acad09plus.csproj @@ -1,25 +1,24 @@  - preview - enable - net35;net40;net45 true + enable true true - 0.3.6.1 + 0.3.7 InspireFunction - xsfhlzh;vicwjb + xsfhlzh;vicwjb;liuqihong 基于.NET的Cad二次开发类库 InspireFunction https://gitee.com/inspirefunction/ifoxcad https://gitee.com/inspirefunction/ifoxcad.git git IFoxCAD;CAD;AutoCad;C#;NET - 增加四叉树测试. + 增加很多东西. true true + preview true LICENSE true @@ -64,25 +63,6 @@ $(Configuration);ac2015 - - 1701;1702;CS1685 - - - 1701;1702;CS1685 - - - 1701;1702;CS1685 - - - 1701;1702;CS1685 - - - 1701;1702;CS1685 - - - 1701;1702;CS1685 - - True -- Gitee From c641f8da5d00f0a7511a529f52ebe251399ebdc7 Mon Sep 17 00:00:00 2001 From: liuqihong <540762622@qq.com> Date: Tue, 16 Aug 2022 22:45:55 +0800 Subject: [PATCH 373/675] =?UTF-8?q?=E4=BF=AE=E6=94=B9=E5=91=BD=E5=90=8D?= =?UTF-8?q?=E7=A9=BA=E9=97=B4=E6=96=B9=E5=BC=8F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/IFoxCAD.Basal/ArrayEx.cs | 81 ++++++++++++++++++------------------ src/IFoxCAD.Basal/DictEx.cs | 30 ++++++------- src/IFoxCAD.Basal/ListEx.cs | 10 ++--- 3 files changed, 57 insertions(+), 64 deletions(-) diff --git a/src/IFoxCAD.Basal/ArrayEx.cs b/src/IFoxCAD.Basal/ArrayEx.cs index 34fb4b7..2ac37fa 100644 --- a/src/IFoxCAD.Basal/ArrayEx.cs +++ b/src/IFoxCAD.Basal/ArrayEx.cs @@ -1,50 +1,49 @@ -namespace IFoxCAD.Basal +namespace IFoxCAD.Basal; + +/* + * 由于linq的函数大部分带有状态机,而cad是一个单机程序, + * 使用状态机会变得缓慢,因此我们设计的时候着重于时间优化, + * 本工具类在着重于数组遍历时候替代linq + */ +public static class ArrayEx { - /* - * 由于linq的函数大部分带有状态机,而cad是一个单机程序, - * 使用状态机会变得缓慢,因此我们设计的时候着重于时间优化, - * 本工具类在着重于数组遍历时候替代linq - */ - public static class ArrayEx + /// + /// 合并数组 + /// + /// + /// + public static T[] Combine2(this T[] a, T[] b) { - /// - /// 合并数组 - /// - /// - /// - public static T[] Combine2(this T[] a, T[] b) - { - var c = new T[a.Length + b.Length]; - Array.Copy(a, 0, c, 0, a.Length); - Array.Copy(b, 0, c, a.Length, b.Length); - return c; - } + var c = new T[a.Length + b.Length]; + Array.Copy(a, 0, c, 0, a.Length); + Array.Copy(b, 0, c, a.Length, b.Length); + return c; + } - /// - /// 一维数组消重,此函数建议更改为: - /// set = new(); - /// foreach (var item in listInOut) - /// set.Add(item); - /// ]]> - /// - /// - /// 传入有重复成员的数组,传出没有重复的 - /// 传出参数1:数组开头;传出参数2:数组结尾;返回值比较结尾为就移除 - [Obsolete] - public static void Deduplication(List listInOut, Func func) + /// + /// 一维数组消重,此函数建议更改为: + /// set = new(); + /// foreach (var item in listInOut) + /// set.Add(item); + /// ]]> + /// + /// + /// 传入有重复成员的数组,传出没有重复的 + /// 传出参数1:数组开头;传出参数2:数组结尾;返回值比较结尾为就移除 + [Obsolete] + public static void Deduplication(List listInOut, Func func) + { + for (int i = 0; i < listInOut.Count; i++) { - for (int i = 0; i < listInOut.Count; i++) + var first = listInOut[i]; + for (int j = listInOut.Count - 1; j > i; j--) { - var first = listInOut[i]; - for (int j = listInOut.Count - 1; j > i; j--) - { - var last = listInOut[j]; - if (func(first, last)) - listInOut.RemoveAt(j); - } + var last = listInOut[j]; + if (func(first, last)) + listInOut.RemoveAt(j); } } - } + } diff --git a/src/IFoxCAD.Basal/DictEx.cs b/src/IFoxCAD.Basal/DictEx.cs index 6fbccc4..7ff153f 100644 --- a/src/IFoxCAD.Basal/DictEx.cs +++ b/src/IFoxCAD.Basal/DictEx.cs @@ -1,21 +1,15 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; +namespace IFoxCAD.Basal; -namespace IFoxCAD.Basal +public static class DictEx { - public static class DictEx - { - //public static TKey? GetKey(this IDictionary dict!!, TKey key!!) - //{ - // if (dict.ContainsKey(key)) - // { - // foreach (var item in dict.Keys) - // if (key.Equals(item)) - // return item; - // } - // return default; - //} - } + //public static TKey? GetKey(this IDictionary dict!!, TKey key!!) + //{ + // if (dict.ContainsKey(key)) + // { + // foreach (var item in dict.Keys) + // if (key.Equals(item)) + // return item; + // } + // return default; + //} } diff --git a/src/IFoxCAD.Basal/ListEx.cs b/src/IFoxCAD.Basal/ListEx.cs index 4ead97b..528b6ad 100644 --- a/src/IFoxCAD.Basal/ListEx.cs +++ b/src/IFoxCAD.Basal/ListEx.cs @@ -1,5 +1,5 @@ - -namespace IFoxCAD.Basal; +namespace IFoxCAD.Basal; + public static class ListEx { @@ -12,9 +12,9 @@ public static bool EqualsAll(this IList a, IList b) public static bool EqualsAll(this IList a, IList b, IEqualityComparer? comparer) { - if (a == null) - return b == null; - else if (b == null) + if (a is null) + return b is null; + else if (b is null) return false; if (a.Count != b.Count) -- Gitee From 2c00d7388302ab33da1d0057282d47745c0eb773 Mon Sep 17 00:00:00 2001 From: liuqihong <540762622@qq.com> Date: Tue, 16 Aug 2022 22:48:49 +0800 Subject: [PATCH 374/675] =?UTF-8?q?=E4=BF=AE=E6=94=B9=E5=91=BD=E5=90=8D?= =?UTF-8?q?=E7=A9=BA=E9=97=B4=E6=96=B9=E5=BC=8F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- tests/Test/CmdINI.cs | 4 +- tests/Test/Test.cs | 4 +- tests/Test/TestAOP.cs | 4 +- tests/Test/TestCurve.cs | 237 +++++++++++++++--------------- tests/Test/TestFileDatabase.cs | 40 ++--- tests/Test/TestJig.cs | 1 + tests/Test/TestLoop.cs | 45 +++--- tests/Test/TestMirrorFile.cs | 4 +- tests/Test/TestPoint.cs | 259 ++++++++++++++++----------------- tests/Test/Testid.cs | 119 ++++++++------- tests/Test/testConvexHull.cs | 144 +++++++++--------- tests/Test/testblock.cs | 1 + tests/Test/testselectfilter.cs | 53 ++++--- 13 files changed, 459 insertions(+), 456 deletions(-) diff --git a/tests/Test/CmdINI.cs b/tests/Test/CmdINI.cs index 2e37c1d..74536a2 100644 --- a/tests/Test/CmdINI.cs +++ b/tests/Test/CmdINI.cs @@ -1,4 +1,6 @@ -/// +namespace Test; + +/// /// 注册中心(自动执行接口): /// 用于启动cad后写入启动注册表及反射调用以下特性和接口 /// netload的工程必须继承虚函数后才能使用特性和接口 diff --git a/tests/Test/Test.cs b/tests/Test/Test.cs index bc45757..c1d70eb 100644 --- a/tests/Test/Test.cs +++ b/tests/Test/Test.cs @@ -1,5 +1,5 @@ -#pragma warning disable CS0219 // 变量已被赋值,但从未使用过它的值 -namespace Test; +namespace Test; +#pragma warning disable CS0219 // 变量已被赋值,但从未使用过它的值 public class Test { diff --git a/tests/Test/TestAOP.cs b/tests/Test/TestAOP.cs index b5b5c92..b1b807a 100644 --- a/tests/Test/TestAOP.cs +++ b/tests/Test/TestAOP.cs @@ -1,4 +1,6 @@ -//被注入的函数将不能使用断点, +namespace Test; + +//被注入的函数将不能使用断点, //因此用户要充分了解才能使用 #if false /* diff --git a/tests/Test/TestCurve.cs b/tests/Test/TestCurve.cs index 4d5cd96..a8572d6 100644 --- a/tests/Test/TestCurve.cs +++ b/tests/Test/TestCurve.cs @@ -1,158 +1,155 @@ -using System.Security.Policy; +namespace Test; -namespace Test +public class TestGraph { - public class TestGraph + [CommandMethod("testpointindict")] + public void TestPointInDict() { - [CommandMethod("testpointindict")] - public void TestPointInDict() + var pt1 = new Point3d(0.0255, 0.452, 0); + var pt2 = new Point3d(0.0255001, 0.452003, 0); + var pt3 = new Point3d(0.0255002, 0.4520001, 0); + var pt4 = new Point3d(0.0255450, 0.45287893, 0); + var pt5 = new Point3d(0.02554935, 0.452092375, 0); + var dict = new Dictionary { - var pt1 = new Point3d(0.0255, 0.452, 0); - var pt2 = new Point3d(0.0255001, 0.452003, 0); - var pt3 = new Point3d(0.0255002, 0.4520001, 0); - var pt4 = new Point3d(0.0255450, 0.45287893, 0); - var pt5 = new Point3d(0.02554935, 0.452092375, 0); - var dict = new Dictionary - { - { pt1, 1 }, - { pt2, 2 }, - { pt3, 3 }, - { pt4, 4 }, - { pt5, 5 } - }; - Env.Print(dict[pt1]); - } + { pt1, 1 }, + { pt2, 2 }, + { pt3, 3 }, + { pt4, 4 }, + { pt5, 5 } + }; + Env.Print(dict[pt1]); + } - [CommandMethod("testgraph")] - public void TestGraph1() - { - using var tr = new DBTrans(); - var ents = Env.Editor.SSGet()?.Value?.GetEntities(); - if (ents == null) - return; - Tools.TestTimes2(1, "new", () => { - - - var res = ents.GetAllCycle(); - - //res.ForEach((i, t) => t.ForWrite(e => e.ColorIndex = i + 1)); - Env.Print(res.Count()); - tr.CurrentSpace.AddEntity(res); - }); + [CommandMethod("testgraph")] + public void TestGraph1() + { + using var tr = new DBTrans(); + var ents = Env.Editor.SSGet()?.Value?.GetEntities(); + if (ents == null) + return; + Tools.TestTimes2(1, "new", () => { + + + var res = ents.GetAllCycle(); - } + //res.ForEach((i, t) => t.ForWrite(e => e.ColorIndex = i + 1)); + Env.Print(res.Count()); + tr.CurrentSpace.AddEntity(res); + }); - [CommandMethod("testgraphspeed")] - public void TestGraphspeed() - { - using var tr = new DBTrans(); - var ents = Env.Editor.SSGet()?.Value?.GetEntities(); - if (ents == null) - return; + } + [CommandMethod("testgraphspeed")] + public void TestGraphspeed() + { + using var tr = new DBTrans(); + var ents = Env.Editor.SSGet()?.Value?.GetEntities(); + if (ents == null) + return; - var graph = new IFoxCAD.Cad.Graph(); // 为了调试先把图的访问改为internal - foreach (var curve in ents) - { + var graph = new IFoxCAD.Cad.Graph(); // 为了调试先把图的访问改为internal - graph.AddEdge(curve.GetGeCurve()); + foreach (var curve in ents) + { + graph.AddEdge(curve.GetGeCurve()); - } - //新建 dfs - var dfs = new DepthFirst(); + + } + //新建 dfs + var dfs = new DepthFirst(); #if true - Tools.TestTimes2(100, "new", () => { - // 查询全部的 闭合环 - dfs.FindAll(graph); - }); - Tools.TestTimes2(1000, "new", () => { - // 查询全部的 闭合环 - dfs.FindAll(graph); - }); + Tools.TestTimes2(100, "new", () => { + // 查询全部的 闭合环 + dfs.FindAll(graph); + }); + Tools.TestTimes2(1000, "new", () => { + // 查询全部的 闭合环 + dfs.FindAll(graph); + }); #else - Tools.TestTimes2(100, "old", () => { - // 查询全部的 闭合环 - dfs.FindAll(graph); - }); - Tools.TestTimes2(1000, "old", () => { - // 查询全部的 闭合环 - dfs.FindAll(graph); - }); + Tools.TestTimes2(100, "old", () => { + // 查询全部的 闭合环 + dfs.FindAll(graph); + }); + Tools.TestTimes2(1000, "old", () => { + // 查询全部的 闭合环 + dfs.FindAll(graph); + }); #endif - //res.ForEach((i, t) => t.ForWrite(e => e.ColorIndex = i + 1)); + //res.ForEach((i, t) => t.ForWrite(e => e.ColorIndex = i + 1)); - //tr.CurrentSpace.AddEntity(res); + //tr.CurrentSpace.AddEntity(res); - } } +} - public class TestCurve +public class TestCurve +{ + [CommandMethod("testbreakcurve")] + public void TestBreakCurve() { - [CommandMethod("testbreakcurve")] - public void TestBreakCurve() - { - using var tr = new DBTrans(); - var ents = Env.Editor.SSGet().Value.GetEntities(); - var tt = CurveEx.BreakCurve(ents.ToList()); - tt.ForEach(t => t.ForWrite(e => e.ColorIndex = 1)); - tr.CurrentSpace.AddEntity(tt); - } + using var tr = new DBTrans(); + var ents = Env.Editor.SSGet().Value.GetEntities(); + var tt = CurveEx.BreakCurve(ents.ToList()); + tt.ForEach(t => t.ForWrite(e => e.ColorIndex = 1)); + tr.CurrentSpace.AddEntity(tt); + } - [CommandMethod("testCurveCurveIntersector3d")] - public void TestCurveCurveIntersector3d() - { - using var tr = new DBTrans(); - var ents = Env.Editor.SSGet().Value.GetEntities() - .Select(e => e.ToCompositeCurve3d()).ToList(); + [CommandMethod("testCurveCurveIntersector3d")] + public void TestCurveCurveIntersector3d() + { + using var tr = new DBTrans(); + var ents = Env.Editor.SSGet().Value.GetEntities() + .Select(e => e.ToCompositeCurve3d()).ToList(); - var cci3d = new CurveCurveIntersector3d(); + var cci3d = new CurveCurveIntersector3d(); - for (int i = 0; i < ents.Count; i++) + for (int i = 0; i < ents.Count; i++) + { + var gc1 = ents[i]; + var int1 = gc1.GetInterval(); + //var pars1 = paramss[i]; + for (int j = i; j < ents.Count; j++) { - var gc1 = ents[i]; - var int1 = gc1.GetInterval(); - //var pars1 = paramss[i]; - for (int j = i; j < ents.Count; j++) + var gc2 = ents[j]; + //var pars2 = paramss[j]; + var int2 = gc2.GetInterval(); + cci3d.Set(gc1, gc2, int1, int2, Vector3d.ZAxis); + var d = cci3d.OverlapCount(); + var a = cci3d.GetIntersectionRanges(); + Env.Print($"{a[0].LowerBound}-{a[0].UpperBound} and {a[1].LowerBound}-{a[1].UpperBound}"); + for (int m = 0; m < d; m++) { - var gc2 = ents[j]; - //var pars2 = paramss[j]; - var int2 = gc2.GetInterval(); - cci3d.Set(gc1, gc2, int1, int2, Vector3d.ZAxis); - var d = cci3d.OverlapCount(); - var a = cci3d.GetIntersectionRanges(); - Env.Print($"{a[0].LowerBound}-{a[0].UpperBound} and {a[1].LowerBound}-{a[1].UpperBound}"); - for (int m = 0; m < d; m++) - { - var b = cci3d.GetOverlapRanges(m); - Env.Print($"{b[0].LowerBound}-{b[0].UpperBound} and {b[1].LowerBound}-{b[1].UpperBound}"); - } - - for (int k = 0; k < cci3d.NumberOfIntersectionPoints; k++) - { - //var a = cci3d.GetOverlapRanges(k); - //var b = cci3d.IsTangential(k); - //var c = cci3d.IsTransversal(k); - //var d = cci3d.OverlapCount(); - //var e = cci3d.OverlapDirection(); - var pt = cci3d.GetIntersectionParameters(k); - var pts = cci3d.GetIntersectionPoint(k); - Env.Print(pts); - } + var b = cci3d.GetOverlapRanges(m); + Env.Print($"{b[0].LowerBound}-{b[0].UpperBound} and {b[1].LowerBound}-{b[1].UpperBound}"); + } + for (int k = 0; k < cci3d.NumberOfIntersectionPoints; k++) + { + //var a = cci3d.GetOverlapRanges(k); + //var b = cci3d.IsTangential(k); + //var c = cci3d.IsTransversal(k); + //var d = cci3d.OverlapCount(); + //var e = cci3d.OverlapDirection(); + var pt = cci3d.GetIntersectionParameters(k); + var pts = cci3d.GetIntersectionPoint(k); + Env.Print(pts); + } - } } - // var tt = CurveEx.Topo(ents.ToList()); - //tt.ForEach(t => t.ForWrite(e => e.ColorIndex = 1)); - //tr.CurrentSpace.AddEntity(tt); + } + // var tt = CurveEx.Topo(ents.ToList()); + //tt.ForEach(t => t.ForWrite(e => e.ColorIndex = 1)); + //tr.CurrentSpace.AddEntity(tt); } } \ No newline at end of file diff --git a/tests/Test/TestFileDatabase.cs b/tests/Test/TestFileDatabase.cs index 7493f30..6759e83 100644 --- a/tests/Test/TestFileDatabase.cs +++ b/tests/Test/TestFileDatabase.cs @@ -1,29 +1,29 @@ -/************************************************************** +namespace Test; + +/************************************************************** *作者:Leon *创建时间:2022/2/11 9:55:32 **************************************************************/ -namespace Test + +public class TestFileDatabase { - public class TestFileDatabase + [CommandMethod("Test_FileDatabaseInit")] + public void TestDatabase() { - [CommandMethod("Test_FileDatabaseInit")] - public void TestDatabase() + try { - try - { - var fileName = @"C:\Users\Administrator\Desktop\合并详图测试BUG.dwg"; - using DBTrans trans = new(fileName); - trans.ModelSpace.AddEntity(new Line(new(0, 0, 0), new(1000, 1000, 0))); - if (trans.Document is not null && trans.Document.IsActive) - trans.Document.SendStringToExecute("_qsave\n", false, true, true); - else - trans.Database.SaveAs(fileName, DwgVersion.AC1021); - } - catch (System.Exception e) - { - System.Windows.MessageBox.Show(e.Message); - } - + var fileName = @"C:\Users\Administrator\Desktop\合并详图测试BUG.dwg"; + using DBTrans trans = new(fileName); + trans.ModelSpace.AddEntity(new Line(new(0, 0, 0), new(1000, 1000, 0))); + if (trans.Document is not null && trans.Document.IsActive) + trans.Document.SendStringToExecute("_qsave\n", false, true, true); + else + trans.Database.SaveAs(fileName, DwgVersion.AC1021); } + catch (System.Exception e) + { + System.Windows.MessageBox.Show(e.Message); + } + } } \ No newline at end of file diff --git a/tests/Test/TestJig.cs b/tests/Test/TestJig.cs index 0fb1310..918795c 100644 --- a/tests/Test/TestJig.cs +++ b/tests/Test/TestJig.cs @@ -1,4 +1,5 @@ namespace Test; + using System.Windows.Forms; public class Commands_Jig diff --git a/tests/Test/TestLoop.cs b/tests/Test/TestLoop.cs index 8992df9..5978803 100644 --- a/tests/Test/TestLoop.cs +++ b/tests/Test/TestLoop.cs @@ -1,36 +1,37 @@ -using IFoxCAD.Basal; +namespace Test; + + +using IFoxCAD.Basal; using System.Diagnostics.CodeAnalysis; -namespace Test + +public class TestLoop { - public class TestLoop + [CommandMethod("testloop")] + public void Testloop() { - [CommandMethod("testloop")] - public void Testloop() + var loop = new LoopList { - var loop = new LoopList - { - 0, - 1, - 2, - 3, - 4, - 5 - }; + 0, + 1, + 2, + 3, + 4, + 5 + }; - + - Env.Print(loop); + Env.Print(loop); - loop.SetFirst(loop.Last); - Env.Print(loop); - Env.Print(loop.Min()); - loop.SetFirst(new LoopListNode (loop.Min() ,loop)); - Env.Print(loop); + loop.SetFirst(loop.Last); + Env.Print(loop); + Env.Print(loop.Min()); + loop.SetFirst(new LoopListNode (loop.Min() ,loop)); + Env.Print(loop); - } } } diff --git a/tests/Test/TestMirrorFile.cs b/tests/Test/TestMirrorFile.cs index b5f212f..388b0d1 100644 --- a/tests/Test/TestMirrorFile.cs +++ b/tests/Test/TestMirrorFile.cs @@ -1,4 +1,6 @@ -public class MirrorFile +namespace Test; + +public class MirrorFile { const string file = "D:/JX.dwg"; const string fileSave = "D:/JX222.dwg"; diff --git a/tests/Test/TestPoint.cs b/tests/Test/TestPoint.cs index 46efe2e..ab94c41 100644 --- a/tests/Test/TestPoint.cs +++ b/tests/Test/TestPoint.cs @@ -1,160 +1,159 @@ -using System.Diagnostics; +namespace Test; -namespace Test + +using System.Diagnostics; + + +public class TestPoint { - public class TestPoint + /// + /// 红黑树排序点集 + /// + [CommandMethod("TestptSortedSet")] + public void TestptSortedSet() { - /// - /// 红黑树排序点集 - /// - [CommandMethod("TestptSortedSet")] - public void TestptSortedSet() + var ss1 = new SortedSet(); + ss1.Add(new Point2d(1, 1)); + ss1.Add(new Point2d(4.6, 2)); + ss1.Add(new Point2d(8, 3)); + ss1.Add(new Point2d(4, 3)); + ss1.Add(new Point2d(5, 40)); + ss1.Add(new Point2d(6, 5)); + ss1.Add(new Point2d(1, 6)); + ss1.Add(new Point2d(7, 6)); + ss1.Add(new Point2d(9, 6)); + + /*判断区间,超过就中断*/ + foreach (var item in ss1) { - var ss1 = new SortedSet(); - ss1.Add(new Point2d(1, 1)); - ss1.Add(new Point2d(4.6, 2)); - ss1.Add(new Point2d(8, 3)); - ss1.Add(new Point2d(4, 3)); - ss1.Add(new Point2d(5, 40)); - ss1.Add(new Point2d(6, 5)); - ss1.Add(new Point2d(1, 6)); - ss1.Add(new Point2d(7, 6)); - ss1.Add(new Point2d(9, 6)); - - /*判断区间,超过就中断*/ - foreach (var item in ss1) + if (item.X > 3 && item.X < 7) { - if (item.X > 3 && item.X < 7) - { - Debug.WriteLine(item); - } - else if (item.X >= 7) - { - break; - } + Debug.WriteLine(item); + } + else if (item.X >= 7) + { + break; } } + } - [CommandMethod("TestptGethash")] - public void TestptGethash() - { - // test - var pt = Env.Editor.GetPoint("pick pt").Value; - //Tools.TestTimes2(1_000_000, "新语法", () => { - // pt.GetHashString2(); - //}); - Tools.TestTimes2(1_000_000, "旧语法", () => { - pt.GetHashString(); - }); - } + [CommandMethod("TestptGethash")] + public void TestptGethash() + { + // test + var pt = Env.Editor.GetPoint("pick pt").Value; + //Tools.TestTimes2(1_000_000, "新语法", () => { + // pt.GetHashString2(); + //}); + Tools.TestTimes2(1_000_000, "旧语法", () => { + pt.GetHashString(); + }); + } - [CommandMethod("Testpoint3d")] - public void TestPoint3d() - { - Env.Print($"4位小数的hash:{new Point3d(0.0_001, 0.0_002, 0.0).GetHashCode()}"); - Env.Print($"5位小数的hash:{new Point3d(0.00_001, 0.00_002, 0.0).GetHashCode()}"); - Env.Print($"6位小数的hash:{new Point3d(0.000_001, 0.000_002, 0.0).GetHashCode()}"); - Env.Print($"7位小数的hash:{new Point3d(0.000_0_001, 0.000_0_002, 0.0).GetHashCode()}"); - Env.Print($"8位小数的hash:{new Point3d(0.000_00_001, 0.000_00_002, 0.0).GetHashCode()}"); - Env.Print($"9位小数的hash:{new Point3d(0.000_000_001, 0.000_000_002, 0.0).GetHashCode()}"); - Env.Print($"10位小数的hash:{new Point3d(0.000_000_0001, 0.000_000_0002, 0.0).GetHashCode()}"); - Env.Print($"10位小数的hash:{new Point3d(0.000_000_0001, 0.000_000_0001, 0.0).GetHashCode()}"); + [CommandMethod("Testpoint3d")] + public void TestPoint3d() + { + Env.Print($"4位小数的hash:{new Point3d(0.0_001, 0.0_002, 0.0).GetHashCode()}"); + Env.Print($"5位小数的hash:{new Point3d(0.00_001, 0.00_002, 0.0).GetHashCode()}"); + Env.Print($"6位小数的hash:{new Point3d(0.000_001, 0.000_002, 0.0).GetHashCode()}"); + Env.Print($"7位小数的hash:{new Point3d(0.000_0_001, 0.000_0_002, 0.0).GetHashCode()}"); + Env.Print($"8位小数的hash:{new Point3d(0.000_00_001, 0.000_00_002, 0.0).GetHashCode()}"); + Env.Print($"9位小数的hash:{new Point3d(0.000_000_001, 0.000_000_002, 0.0).GetHashCode()}"); + Env.Print($"10位小数的hash:{new Point3d(0.000_000_0001, 0.000_000_0002, 0.0).GetHashCode()}"); + Env.Print($"10位小数的hash:{new Point3d(0.000_000_0001, 0.000_000_0001, 0.0).GetHashCode()}"); - Env.Print($"11位小数的hash:{new Point3d(0.000_000_000_01, 0.000_000_000_02, 0.0).GetHashCode()}"); - Env.Print($"11位小数的hash:{new Point3d(0.000_000_000_01, 0.000_000_000_01, 0.0).GetHashCode()}"); + Env.Print($"11位小数的hash:{new Point3d(0.000_000_000_01, 0.000_000_000_02, 0.0).GetHashCode()}"); + Env.Print($"11位小数的hash:{new Point3d(0.000_000_000_01, 0.000_000_000_01, 0.0).GetHashCode()}"); - Env.Print($"12位小数的hash:{new Point3d(0.000_000_000_001, 0.000_000_000_002, 0.0).GetHashCode()}"); - Env.Print($"12位小数的hash:{new Point3d(0.000_000_000_001, 0.000_000_000_001, 0.0).GetHashCode()}"); + Env.Print($"12位小数的hash:{new Point3d(0.000_000_000_001, 0.000_000_000_002, 0.0).GetHashCode()}"); + Env.Print($"12位小数的hash:{new Point3d(0.000_000_000_001, 0.000_000_000_001, 0.0).GetHashCode()}"); - Env.Print($"13位小数的hash:{new Point3d(0.000_000_000_0001, 0.000_000_000_0002, 0.0).GetHashCode()}"); - Env.Print($"13位小数的hash:{new Point3d(0.000_000_000_0001, 0.000_000_000_0001, 0.0).GetHashCode()}"); + Env.Print($"13位小数的hash:{new Point3d(0.000_000_000_0001, 0.000_000_000_0002, 0.0).GetHashCode()}"); + Env.Print($"13位小数的hash:{new Point3d(0.000_000_000_0001, 0.000_000_000_0001, 0.0).GetHashCode()}"); - Env.Print($"14位小数的hash:{new Point3d(0.000_000_000_000_01, 0.000_000_000_000_02, 0.0).GetHashCode()}"); - Env.Print($"14位小数的hash:{new Point3d(0.000_000_000_000_01, 0.000_000_000_000_01, 0.0).GetHashCode()}"); + Env.Print($"14位小数的hash:{new Point3d(0.000_000_000_000_01, 0.000_000_000_000_02, 0.0).GetHashCode()}"); + Env.Print($"14位小数的hash:{new Point3d(0.000_000_000_000_01, 0.000_000_000_000_01, 0.0).GetHashCode()}"); - Env.Print($"15位小数的hash:{new Point3d(0.000_000_000_000_001, 0.000_000_000_000_002, 0.0).GetHashCode()}"); - Env.Print($"15位小数的hash:{new Point3d(0.000_000_000_000_001, 0.000_000_000_000_001, 0.0).GetHashCode()}"); + Env.Print($"15位小数的hash:{new Point3d(0.000_000_000_000_001, 0.000_000_000_000_002, 0.0).GetHashCode()}"); + Env.Print($"15位小数的hash:{new Point3d(0.000_000_000_000_001, 0.000_000_000_000_001, 0.0).GetHashCode()}"); - Env.Print($"16位小数的hash:{new Point3d(0.000_000_000_000_000_1, 0.000_000_000_000_000_2, 0.0).GetHashCode()}"); - Env.Print($"16位小数的hash:{new Point3d(0.000_000_000_000_000_1, 0.000_000_000_000_000_1, 0.0).GetHashCode()}"); + Env.Print($"16位小数的hash:{new Point3d(0.000_000_000_000_000_1, 0.000_000_000_000_000_2, 0.0).GetHashCode()}"); + Env.Print($"16位小数的hash:{new Point3d(0.000_000_000_000_000_1, 0.000_000_000_000_000_1, 0.0).GetHashCode()}"); - Env.Print($"17位小数的hash:{new Point3d(0.000_000_000_000_000_01, 0.000_000_000_000_000_02, 0.0).GetHashCode()}"); - Env.Print($"17位小数的hash:{new Point3d(0.000_000_000_000_000_01, 0.000_000_000_000_000_01, 0.0).GetHashCode()}"); + Env.Print($"17位小数的hash:{new Point3d(0.000_000_000_000_000_01, 0.000_000_000_000_000_02, 0.0).GetHashCode()}"); + Env.Print($"17位小数的hash:{new Point3d(0.000_000_000_000_000_01, 0.000_000_000_000_000_01, 0.0).GetHashCode()}"); - Env.Print($"18位小数的hash:{new Point3d(0.000_000_000_000_000_001, 0.000_000_000_000_000_002, 0.0).GetHashCode()}"); - Env.Print($"18位小数的hash:{new Point3d(0.000_000_000_000_000_001, 0.000_000_000_000_000_001, 0.0).GetHashCode()}"); + Env.Print($"18位小数的hash:{new Point3d(0.000_000_000_000_000_001, 0.000_000_000_000_000_002, 0.0).GetHashCode()}"); + Env.Print($"18位小数的hash:{new Point3d(0.000_000_000_000_000_001, 0.000_000_000_000_000_001, 0.0).GetHashCode()}"); - Env.Print($"19位小数的hash:{new Point3d(0.000_000_000_000_000_000_1, 0.000_000_000_000_000_000_2, 0.0).GetHashCode()}"); - Env.Print($"19位小数的hash:{new Point3d(0.000_000_000_000_000_000_1, 0.000_000_000_000_000_000_1, 0.0).GetHashCode()}"); + Env.Print($"19位小数的hash:{new Point3d(0.000_000_000_000_000_000_1, 0.000_000_000_000_000_000_2, 0.0).GetHashCode()}"); + Env.Print($"19位小数的hash:{new Point3d(0.000_000_000_000_000_000_1, 0.000_000_000_000_000_000_1, 0.0).GetHashCode()}"); - Env.Print($"20位小数的hash:{new Point3d(0.000_000_000_000_000_000_01, 0.000_000_000_000_000_000_02, 0.0).GetHashCode()}"); - Env.Print($"20位小数的hash:{new Point3d(0.000_000_000_000_000_000_01, 0.000_000_000_000_000_000_01, 0.0).GetHashCode()}"); - } + Env.Print($"20位小数的hash:{new Point3d(0.000_000_000_000_000_000_01, 0.000_000_000_000_000_000_02, 0.0).GetHashCode()}"); + Env.Print($"20位小数的hash:{new Point3d(0.000_000_000_000_000_000_01, 0.000_000_000_000_000_000_01, 0.0).GetHashCode()}"); + } - [CommandMethod("Testlistequalspeed")] - public void Testlistequalspeed() - { - var lst1 = new List { 1, 2, 3, 4 }; - var lst2 = new List { 1, 2, 3, 4}; - lst1.EqualsAll(null); - Tools.TestTimes2(1000000, "eqaulspeed:", () => { - lst1.EqualsAll(lst2); - }); - + [CommandMethod("Testlistequalspeed")] + public void Testlistequalspeed() + { + var lst1 = new List { 1, 2, 3, 4 }; + var lst2 = new List { 1, 2, 3, 4}; + lst1.EqualsAll(null); + Tools.TestTimes2(1000000, "eqaulspeed:", () => { + lst1.EqualsAll(lst2); + }); + - } + } - [CommandMethod("Testcontains")] - public void Testcontains() + [CommandMethod("Testcontains")] + public void Testcontains() + { + // test list and dict contains speed + var lst = new List { 1, 2, 3, 4 , 5,6,7,8,9,10, 11,12,13,14,15,16,17,18,19,20}; + var hashset = new HashSet { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20 }; + var dict = new Dictionary { - // test list and dict contains speed - var lst = new List { 1, 2, 3, 4 , 5,6,7,8,9,10, 11,12,13,14,15,16,17,18,19,20}; - var hashset = new HashSet { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20 }; - var dict = new Dictionary - { - { 1, 0 }, - { 2, 1 }, - { 3, 2 }, - { 4, 3 }, - { 5, 4 }, - { 6, 5 }, - { 7, 6 }, - { 8, 7 }, - { 9, 8 }, - { 10, 9 }, - { 11, 11 }, - { 12, 12 }, - { 13, 13 }, - { 14, 14 }, - { 15, 15 }, - { 16, 16 }, - { 17, 17 }, - { 18, 18 }, - { 19, 19 }, - { 20, 20 }, - }; - - Tools.TestTimes2(100_0000, "list:", () => { - lst.Contains(20); - }); - - Tools.TestTimes2(100_0000, "hashset:", () => { - hashset.Contains(20); - }); - - Tools.TestTimes2(100_0000, "dict:", () => { - dict.ContainsKey(20); - }); + { 1, 0 }, + { 2, 1 }, + { 3, 2 }, + { 4, 3 }, + { 5, 4 }, + { 6, 5 }, + { 7, 6 }, + { 8, 7 }, + { 9, 8 }, + { 10, 9 }, + { 11, 11 }, + { 12, 12 }, + { 13, 13 }, + { 14, 14 }, + { 15, 15 }, + { 16, 16 }, + { 17, 17 }, + { 18, 18 }, + { 19, 19 }, + { 20, 20 }, + }; + + Tools.TestTimes2(100_0000, "list:", () => { + lst.Contains(20); + }); + + Tools.TestTimes2(100_0000, "hashset:", () => { + hashset.Contains(20); + }); + + Tools.TestTimes2(100_0000, "dict:", () => { + dict.ContainsKey(20); + }); + + } + - } - - - } - - } \ No newline at end of file diff --git a/tests/Test/Testid.cs b/tests/Test/Testid.cs index d93b823..e61553b 100644 --- a/tests/Test/Testid.cs +++ b/tests/Test/Testid.cs @@ -1,74 +1,73 @@ -namespace Test +namespace Test; + +public class Testid { - public class Testid + [CommandMethod("testid")] + public void TestId() { - [CommandMethod("testid")] - public void TestId() - { - using var tr = new DBTrans(); - Line line = new(new Point3d(0, 0, 0), new Point3d(1, 1, 0)); - tr.CurrentSpace.AddEntity(line); - tr.Dispose(); - - var res = Env.Editor.GetEntity("\npick ent:"); - if (res.Status == PromptStatus.OK) - { - res.ObjectId.Erase(); - } - //using (var tr = new DBTrans()) - //{ - // var res = Env.Editor.GetEntity("\npick ent:"); - // if(res.Status == PromptStatus.OK) - // { - // res.ObjectId.Erase(); - // } + using var tr = new DBTrans(); + Line line = new(new Point3d(0, 0, 0), new Point3d(1, 1, 0)); + tr.CurrentSpace.AddEntity(line); + tr.Dispose(); - //} - } - - [CommandMethod("testmycommand")] - public void TestMyCommand() + var res = Env.Editor.GetEntity("\npick ent:"); + if (res.Status == PromptStatus.OK) { - using var dbtrans = new DBTrans(Env.Document, true, false); - using var trans = Env.Database.TransactionManager.StartTransaction(); - - var l1 = new Line(new Point3d(0, 0, 0), new Point3d(100, 100, 0)); - var blkred = trans.GetObject(Env.Database.CurrentSpaceId, OpenMode.ForWrite) as BlockTableRecord; - blkred.AppendEntity(l1); - trans.AddNewlyCreatedDBObject(l1, true); - trans.Commit(); - //dbtrans.Dispose(); + res.ObjectId.Erase(); } - [CommandMethod("testtextstyle")] - public void TestTextStyle() - { - using var tr = new DBTrans(); - tr.TextStyleTable.Add("宋体", "宋体.ttf", 0.8); + //using (var tr = new DBTrans()) + //{ + // var res = Env.Editor.GetEntity("\npick ent:"); + // if(res.Status == PromptStatus.OK) + // { + // res.ObjectId.Erase(); + // } + + //} + } + + [CommandMethod("testmycommand")] + public void TestMyCommand() + { + using var dbtrans = new DBTrans(Env.Document, true, false); + using var trans = Env.Database.TransactionManager.StartTransaction(); - tr.TextStyleTable.Add("宋体1", FontTTF.宋体, 0.8); - tr.TextStyleTable.Add("仿宋体", FontTTF.仿宋, 0.8); - tr.TextStyleTable.Add("fsgb2312", FontTTF.仿宋GB2312, 0.8); - tr.TextStyleTable.Add("arial", FontTTF.Arial, 0.8); - tr.TextStyleTable.Add("romas", FontTTF.Romans, 0.8); + var l1 = new Line(new Point3d(0, 0, 0), new Point3d(100, 100, 0)); + var blkred = trans.GetObject(Env.Database.CurrentSpaceId, OpenMode.ForWrite) as BlockTableRecord; + blkred.AppendEntity(l1); + trans.AddNewlyCreatedDBObject(l1, true); + trans.Commit(); + //dbtrans.Dispose(); + } + [CommandMethod("testtextstyle")] + public void TestTextStyle() + { + using var tr = new DBTrans(); + tr.TextStyleTable.Add("宋体", "宋体.ttf", 0.8); + tr.TextStyleTable.Add("宋体1", FontTTF.宋体, 0.8); + tr.TextStyleTable.Add("仿宋体", FontTTF.仿宋, 0.8); + tr.TextStyleTable.Add("fsgb2312", FontTTF.仿宋GB2312, 0.8); + tr.TextStyleTable.Add("arial", FontTTF.Arial, 0.8); + tr.TextStyleTable.Add("romas", FontTTF.Romans, 0.8); - tr.TextStyleTable.Add("daziti", ttr => - { - ttr.FileName = "ascii.shx"; - ttr.BigFontFileName = "gbcbig.shx"; - }); - } - [CommandMethod("testtextstylechange")] - public void TestTextStyleChange() + tr.TextStyleTable.Add("daziti", ttr => { - using var tr = new DBTrans(); - + ttr.FileName = "ascii.shx"; + ttr.BigFontFileName = "gbcbig.shx"; + }); + } - tr.TextStyleTable.AddWithChange("宋体1", "simfang.ttf", height: 5); - tr.TextStyleTable.AddWithChange("仿宋体", "宋体.ttf"); - tr.TextStyleTable.AddWithChange("fsgb2312", "Romans", "gbcbig"); - } + [CommandMethod("testtextstylechange")] + public void TestTextStyleChange() + { + using var tr = new DBTrans(); + + + tr.TextStyleTable.AddWithChange("宋体1", "simfang.ttf", height: 5); + tr.TextStyleTable.AddWithChange("仿宋体", "宋体.ttf"); + tr.TextStyleTable.AddWithChange("fsgb2312", "Romans", "gbcbig"); } } diff --git a/tests/Test/testConvexHull.cs b/tests/Test/testConvexHull.cs index e031562..405ce2d 100644 --- a/tests/Test/testConvexHull.cs +++ b/tests/Test/testConvexHull.cs @@ -1,76 +1,76 @@ -namespace Test +namespace Test; + + +public class TestConvexHull { - public class TestConvexHull + [CommandMethod("testch")] + public void Testch() { - [CommandMethod("testch")] - public void Testch() - { - //using var tr = new DBTrans(); - //var pts = new List(); - //var flag = true; - //while (flag) - //{ - // var pt = tr.Editor.GetPoint("qudian"); - // if (pt.Status == PromptStatus.OK) - // { - // pts.Add(pt.Value); - // tr.CurrentSpace.AddEntity(new DBPoint(pt.Value)); - // } - // else - // { - // flag = false; - // } - - //} - - //var ptt = ConvexHull.GetConvexHull(pts); - - //Polyline pl = new Polyline(); - //for (int i = 0; i < ptt.Count; i++) - //{ - // pl.AddVertexAt(i, ptt[i].Point2d(), 0, 0, 0); - //} - ////pl.AddVertexAt(0, new Point2d(0, 0), 0, 0, 0); - ////pl.AddVertexAt(1, new Point2d(10, 10), 0, 0, 0); - ////pl.AddVertexAt(2, new Point2d(20, 20), 0, 0, 0); - ////pl.AddVertexAt(3, new Point2d(30, 30), 0, 0, 0); - ////pl.AddVertexAt(4, new Point2d(40, 40), 0, 0, 0); - //pl.Closed = true; - //pl.Color = Color.FromColorIndex(ColorMethod.ByColor, 6); - //tr.CurrentSpace.AddEntity(pl); - - //var a1 = GeometryEx.GetArea(new Point2d(0, 0), new Point2d(1, 0), new Point2d(1, 1)); - //var a2 = ConvexHull.cross(new Point3d(0, 0, 0), new Point3d(1, 0, 0), new Point3d(1, 1, 0)); - //tr.Editor.WriteMessage(a1.ToString()); - //tr.Editor.WriteMessage(a2.ToString()); - - - //var vec1 = new Vector2d(1, 1); - //var vec2 = new Vector2d(-1, 1); - - //var vec3 = vec1.GetPerpendicularVector(); - //var vec4 = vec2.GetPerpendicularVector(); - - //var area1 = vec2.DotProduct(vec1.GetPerpendicularVector()); - //var area2 = vec1.DotProduct(vec2.GetPerpendicularVector()); - - //var area3 = vec2.DotProduct(vec1); - //var area4 = vec1.DotProduct(vec2); - - var area5 = GeometryEx.GetArea(new List { new Point2d(0, 0), new Point2d(1, 1), new Point2d(-1, 1) }); - - var area6 = GeometryEx.GetArea(new List { new Point2d(0, 0), new Point2d(-1, 1), new Point2d(1, 1) }); - //Env.Editor.WriteMessage($"vec1 的法向量= {vec3} \n"); - //Env.Editor.WriteMessage($"vec2 的法向量= {vec4} \n"); - - //Env.Editor.WriteMessage($"vec2 点乘 vec1的法向量= {area1} \n"); - //Env.Editor.WriteMessage($"vec1 点乘 vec2的法向量= {area2} \n"); - - //Env.Editor.WriteMessage($"vec2 点乘 vec1= {area3} \n"); - //Env.Editor.WriteMessage($"vec1 点乘 vec2= {area4} \n"); - - Env.Editor.WriteMessage($"点集的有向面积:{area5} \n"); - Env.Editor.WriteMessage($"点集的有向面积:{area6} \n"); - } + //using var tr = new DBTrans(); + //var pts = new List(); + //var flag = true; + //while (flag) + //{ + // var pt = tr.Editor.GetPoint("qudian"); + // if (pt.Status == PromptStatus.OK) + // { + // pts.Add(pt.Value); + // tr.CurrentSpace.AddEntity(new DBPoint(pt.Value)); + // } + // else + // { + // flag = false; + // } + + //} + + //var ptt = ConvexHull.GetConvexHull(pts); + + //Polyline pl = new Polyline(); + //for (int i = 0; i < ptt.Count; i++) + //{ + // pl.AddVertexAt(i, ptt[i].Point2d(), 0, 0, 0); + //} + ////pl.AddVertexAt(0, new Point2d(0, 0), 0, 0, 0); + ////pl.AddVertexAt(1, new Point2d(10, 10), 0, 0, 0); + ////pl.AddVertexAt(2, new Point2d(20, 20), 0, 0, 0); + ////pl.AddVertexAt(3, new Point2d(30, 30), 0, 0, 0); + ////pl.AddVertexAt(4, new Point2d(40, 40), 0, 0, 0); + //pl.Closed = true; + //pl.Color = Color.FromColorIndex(ColorMethod.ByColor, 6); + //tr.CurrentSpace.AddEntity(pl); + + //var a1 = GeometryEx.GetArea(new Point2d(0, 0), new Point2d(1, 0), new Point2d(1, 1)); + //var a2 = ConvexHull.cross(new Point3d(0, 0, 0), new Point3d(1, 0, 0), new Point3d(1, 1, 0)); + //tr.Editor.WriteMessage(a1.ToString()); + //tr.Editor.WriteMessage(a2.ToString()); + + + //var vec1 = new Vector2d(1, 1); + //var vec2 = new Vector2d(-1, 1); + + //var vec3 = vec1.GetPerpendicularVector(); + //var vec4 = vec2.GetPerpendicularVector(); + + //var area1 = vec2.DotProduct(vec1.GetPerpendicularVector()); + //var area2 = vec1.DotProduct(vec2.GetPerpendicularVector()); + + //var area3 = vec2.DotProduct(vec1); + //var area4 = vec1.DotProduct(vec2); + + var area5 = GeometryEx.GetArea(new List { new Point2d(0, 0), new Point2d(1, 1), new Point2d(-1, 1) }); + + var area6 = GeometryEx.GetArea(new List { new Point2d(0, 0), new Point2d(-1, 1), new Point2d(1, 1) }); + //Env.Editor.WriteMessage($"vec1 的法向量= {vec3} \n"); + //Env.Editor.WriteMessage($"vec2 的法向量= {vec4} \n"); + + //Env.Editor.WriteMessage($"vec2 点乘 vec1的法向量= {area1} \n"); + //Env.Editor.WriteMessage($"vec1 点乘 vec2的法向量= {area2} \n"); + + //Env.Editor.WriteMessage($"vec2 点乘 vec1= {area3} \n"); + //Env.Editor.WriteMessage($"vec1 点乘 vec2= {area4} \n"); + + Env.Editor.WriteMessage($"点集的有向面积:{area5} \n"); + Env.Editor.WriteMessage($"点集的有向面积:{area6} \n"); } } diff --git a/tests/Test/testblock.cs b/tests/Test/testblock.cs index ef615be..b80924e 100644 --- a/tests/Test/testblock.cs +++ b/tests/Test/testblock.cs @@ -1,4 +1,5 @@ namespace Test; + public class TestBlock { //块定义 diff --git a/tests/Test/testselectfilter.cs b/tests/Test/testselectfilter.cs index 312b563..dab975c 100644 --- a/tests/Test/testselectfilter.cs +++ b/tests/Test/testselectfilter.cs @@ -1,34 +1,33 @@ -namespace Test +namespace Test; + +public class Testselectfilter { - public class Testselectfilter + [CommandMethod("testfilter")] + public void Testfilter() { - [CommandMethod("testfilter")] - public void Testfilter() - { - - var p = new Point3d(10, 10, 0); - var f = OpFilter.Build( - e =>!(e.Dxf(0) == "line" & e.Dxf(8) == "0") - | e.Dxf(0) != "circle" & e.Dxf(8) == "2" & e.Dxf(10) >= p); - - - var f2 = OpFilter.Build( - e => e.Or( - !e.And(e.Dxf(0) == "line", e.Dxf(8) == "0"), - e.And(e.Dxf(0) != "circle", e.Dxf(8) == "2", - e.Dxf(10) <= new Point3d(10, 10, 0)))); + + var p = new Point3d(10, 10, 0); + var f = OpFilter.Build( + e =>!(e.Dxf(0) == "line" & e.Dxf(8) == "0") + | e.Dxf(0) != "circle" & e.Dxf(8) == "2" & e.Dxf(10) >= p); + + + var f2 = OpFilter.Build( + e => e.Or( + !e.And(e.Dxf(0) == "line", e.Dxf(8) == "0"), + e.And(e.Dxf(0) != "circle", e.Dxf(8) == "2", + e.Dxf(10) <= new Point3d(10, 10, 0)))); - SelectionFilter f3 = f; - SelectionFilter f4 = f2; + SelectionFilter f3 = f; + SelectionFilter f4 = f2; - Env.Editor.WriteMessage(""); - } + Env.Editor.WriteMessage(""); + } - [CommandMethod("testselectanpoint")] - public void Testselectanpoint() - { - var sel2 = Env.Editor.SelectAtPoint(new Point3d(0, 0, 0)); - Env.Editor.WriteMessage(""); - } + [CommandMethod("testselectanpoint")] + public void Testselectanpoint() + { + var sel2 = Env.Editor.SelectAtPoint(new Point3d(0, 0, 0)); + Env.Editor.WriteMessage(""); } } -- Gitee From 06b2f8d324a5a485a06ac67f6dcb5ab37d0a4a85 Mon Sep 17 00:00:00 2001 From: vicwjb <403009819@qq.com> Date: Tue, 16 Aug 2022 23:01:34 +0800 Subject: [PATCH 375/675] =?UTF-8?q?=E6=B6=88=E7=81=AD=E8=AD=A6=E5=91=8A?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../CLS/TupleElementNamesAttribute.cs | 5 +- src/IFoxCAD.Basal/Sortedset/Sortedset.cs | 137 +++++++++--------- src/IFoxCAD.Cad/ExtensionMethod/CurveEx.cs | 7 +- 3 files changed, 72 insertions(+), 77 deletions(-) diff --git a/src/IFoxCAD.Basal/CLS/TupleElementNamesAttribute.cs b/src/IFoxCAD.Basal/CLS/TupleElementNamesAttribute.cs index 69670b4..9c3afb3 100644 --- a/src/IFoxCAD.Basal/CLS/TupleElementNamesAttribute.cs +++ b/src/IFoxCAD.Basal/CLS/TupleElementNamesAttribute.cs @@ -38,10 +38,7 @@ public sealed class TupleElementNamesAttribute : Attribute /// public TupleElementNamesAttribute(string[] transformNames) { - if (transformNames == null) - throw new ArgumentNullException(nameof(transformNames)); - - _transformNames = transformNames; + _transformNames = transformNames ?? throw new ArgumentNullException(nameof(transformNames)); } /// diff --git a/src/IFoxCAD.Basal/Sortedset/Sortedset.cs b/src/IFoxCAD.Basal/Sortedset/Sortedset.cs index 9405fc1..fc55659 100644 --- a/src/IFoxCAD.Basal/Sortedset/Sortedset.cs +++ b/src/IFoxCAD.Basal/Sortedset/Sortedset.cs @@ -126,7 +126,6 @@ public SortedSet(IEnumerable collection) : this(collection, Comparer.Defau public SortedSet(IEnumerable collection, IComparer comparer) : this(comparer) { - if (collection == null) { throw new ArgumentNullException("collection"); @@ -134,9 +133,8 @@ public SortedSet(IEnumerable collection, IComparer comparer) // these are explicit type checks in the mould of HashSet. It would have worked better // with something like an ISorted (we could make this work for SortedList.Keys etc) - SortedSet baseSortedSet = collection as SortedSet; SortedSet baseTreeSubSet = collection as TreeSubSet; - if (baseSortedSet != null && baseTreeSubSet == null && AreComparersEqual(this, baseSortedSet)) + if (collection is SortedSet baseSortedSet && baseTreeSubSet == null && AreComparersEqual(this, baseSortedSet)) { //breadth first traversal to recreate nodes if (baseSortedSet.Count == 0) @@ -149,8 +147,8 @@ public SortedSet(IEnumerable collection, IComparer comparer) //pre order way to replicate nodes - Stack theirStack = new Stack.Node>(2 * log2(baseSortedSet.Count) + 2); - Stack myStack = new Stack.Node>(2 * log2(baseSortedSet.Count) + 2); + Stack theirStack = new(2 * Log2(baseSortedSet.Count) + 2); + Stack myStack = new(2 * Log2(baseSortedSet.Count) + 2); Node theirCurrent = baseSortedSet.root; Node myCurrent = (theirCurrent != null ? new SortedSet.Node(theirCurrent.Item, theirCurrent.IsRed) : null); root = myCurrent; @@ -189,7 +187,7 @@ public SortedSet(IEnumerable collection, IComparer comparer) else { //As it stands, you're doing an NlogN sort of the collection - List els = new List(collection); + List els = new(collection); els.Sort(this.comparer); for (int i = 1; i < els.Count; i++) { @@ -273,7 +271,7 @@ internal virtual bool InOrderTreeWalk(TreeWalkPredicate action, bool reverse) // See page 264 of "Introduction to algorithms" by Thomas H. Cormen // note: this should be logbase2, but since the stack grows itself, we // don't want the extra cost - Stack stack = new Stack(2 * (int)(SortedSet.log2(Count + 1))); + Stack stack = new(2 * (int)(SortedSet.Log2(Count + 1))); Node current = root; while (current != null) { @@ -313,8 +311,10 @@ internal virtual bool BreadthFirstTreeWalk(TreeWalkPredicate action) return true; } - List processQueue = new List(); - processQueue.Add(root); + List processQueue = new() + { + root + }; Node current; while (processQueue.Count != 0) @@ -474,7 +474,7 @@ internal virtual bool AddIfNotPresent(T item) Debug.Assert(parent != null, "Parent node cannot be null here!"); // ready to insert the new node - Node node = new Node(item); + Node node = new(item); if (order > 0) { parent.Right = node; @@ -752,8 +752,7 @@ void ICollection.CopyTo(Array array, int index) ThrowHelper.ThrowArgumentException(ExceptionResource.Arg_ArrayPlusOffTooSmall); } - T[] tarray = array as T[]; - if (tarray != null) + if (array is T[] tarray) { CopyTo(tarray, index); } @@ -1194,7 +1193,7 @@ public void UnionWith(IEnumerable other) if (s != null && t == null && this.count == 0) { - SortedSet dummy = new SortedSet(s, this.comparer); + SortedSet dummy = new(s, this.comparer); this.root = dummy.root; this.count = dummy.count; this.version++; @@ -1299,8 +1298,10 @@ private static Node ConstructRootFromSortedArray(T[] arr, int startIndex, int en } else if (size == 2) { - root = new Node(arr[startIndex], false); - root.Right = new Node(arr[endIndex], false); + root = new Node(arr[startIndex], false) + { + Right = new Node(arr[endIndex], false) + }; root.Right.IsRed = true; if (redNode != null) { @@ -1309,9 +1310,11 @@ private static Node ConstructRootFromSortedArray(T[] arr, int startIndex, int en } else if (size == 3) { - root = new Node(arr[startIndex + 1], false); - root.Left = new Node(arr[startIndex], false); - root.Right = new Node(arr[endIndex], false); + root = new Node(arr[startIndex + 1], false) + { + Left = new Node(arr[startIndex], false), + Right = new Node(arr[endIndex], false) + }; if (redNode != null) { root.Left.Left = redNode; @@ -1321,8 +1324,10 @@ private static Node ConstructRootFromSortedArray(T[] arr, int startIndex, int en else { int midpt = ((startIndex + endIndex) / 2); - root = new Node(arr[midpt], false); - root.Left = ConstructRootFromSortedArray(arr, startIndex, midpt - 1, redNode); + root = new Node(arr[midpt], false) + { + Left = ConstructRootFromSortedArray(arr, startIndex, midpt - 1, redNode) + }; if (size % 2 == 0) { root.Right = ConstructRootFromSortedArray(arr, midpt + 2, endIndex, new Node(arr[midpt + 1], true)); @@ -1357,12 +1362,11 @@ public virtual void IntersectWith(IEnumerable other) //HashSet optimizations can't be done until equality comparers and comparers are related //Technically, this would work as well with an ISorted - SortedSet s = other as SortedSet; TreeSubSet t = this as TreeSubSet; if (t != null) VersionCheck(); //only let this happen if i am also a SortedSet, not a SubSet - if (s != null && t == null && AreComparersEqual(this, s)) + if (other is SortedSet s && t == null && AreComparersEqual(this, s)) { @@ -1412,7 +1416,7 @@ public virtual void IntersectWith(IEnumerable other) internal virtual void IntersectWithEnumerable(IEnumerable other) { // - List toSave = new List(this.Count); + List toSave = new(this.Count); foreach (T item in other) { if (this.Contains(item)) @@ -1451,9 +1455,8 @@ public void ExceptWith(IEnumerable other) return; } - SortedSet asSorted = other as SortedSet; - if (asSorted != null && AreComparersEqual(this, asSorted)) + if (other is SortedSet asSorted && AreComparersEqual(this, asSorted)) { //outside range, no point doing anything if (!(comparer.Compare(asSorted.Max, this.Min) < 0 || comparer.Compare(asSorted.Min, this.Max) > 0)) @@ -1504,12 +1507,11 @@ public void SymmetricExceptWith(IEnumerable other) } - SortedSet asSorted = other as SortedSet; #if USING_HASH_SET HashSet asHash = other as HashSet; #endif - if (asSorted != null && AreComparersEqual(this, asSorted)) + if (other is SortedSet asSorted && AreComparersEqual(this, asSorted)) { SymmetricExceptWithSameEC(asSorted); } @@ -1590,8 +1592,7 @@ public bool IsSubsetOf(IEnumerable other) return true; - SortedSet asSorted = other as SortedSet; - if (asSorted != null && AreComparersEqual(this, asSorted)) + if (other is SortedSet asSorted && AreComparersEqual(this, asSorted)) { if (this.Count > asSorted.Count) return false; @@ -1648,8 +1649,7 @@ public bool IsProperSubsetOf(IEnumerable other) } #endif //another for sorted sets with the same comparer - SortedSet asSorted = other as SortedSet; - if (asSorted != null && AreComparersEqual(this, asSorted)) + if (other is SortedSet asSorted && AreComparersEqual(this, asSorted)) { if (this.Count >= asSorted.Count) return false; @@ -1687,8 +1687,7 @@ public bool IsSupersetOf(IEnumerable other) } #endif //another for sorted sets with the same comparer - SortedSet asSorted = other as SortedSet; - if (asSorted != null && AreComparersEqual(this, asSorted)) + if (other is SortedSet asSorted && AreComparersEqual(this, asSorted)) { if (this.Count < asSorted.Count) return false; @@ -1732,8 +1731,7 @@ public bool IsProperSupersetOf(IEnumerable other) } #endif //another way for sorted sets - SortedSet asSorted = other as SortedSet; - if (asSorted != null && AreComparersEqual(asSorted, this)) + if (other is SortedSet asSorted && AreComparersEqual(asSorted, this)) { if (asSorted.Count >= this.Count) return false; @@ -1776,8 +1774,7 @@ public bool SetEquals(IEnumerable other) return asHash.SetEquals(this); } #endif - SortedSet asSorted = other as SortedSet; - if (asSorted != null && AreComparersEqual(this, asSorted)) + if (other is SortedSet asSorted && AreComparersEqual(this, asSorted)) { IEnumerator mine = this.GetEnumerator(); IEnumerator theirs = asSorted.GetEnumerator(); @@ -1821,8 +1818,7 @@ public bool Overlaps(IEnumerable other) if ((other as ICollection != null) && (other as ICollection).Count == 0) return false; - SortedSet asSorted = other as SortedSet; - if (asSorted != null && AreComparersEqual(this, asSorted) && (comparer.Compare(Min, asSorted.Max) > 0 || comparer.Compare(Max, asSorted.Min) < 0)) + if (other is SortedSet asSorted && AreComparersEqual(this, asSorted) && (comparer.Compare(Min, asSorted.Max) > 0 || comparer.Compare(Max, asSorted.Min) < 0)) { return false; } @@ -1948,7 +1944,7 @@ public int RemoveWhere(Predicate match) { throw new ArgumentNullException("match"); } - List matches = new List(this.Count); + List matches = new(this.Count); BreadthFirstTreeWalk(delegate (Node n) { @@ -1982,7 +1978,7 @@ public T Min { get { - T ret = default(T); + T ret = default; InOrderTreeWalk(delegate (SortedSet.Node n) { ret = n.Item; return false; }); return ret; } @@ -1992,7 +1988,7 @@ public T Max { get { - T ret = default(T); + T ret = default; InOrderTreeWalk(delegate (SortedSet.Node n) { ret = n.Item; return false; }, true); return ret; } @@ -2000,7 +1996,7 @@ public T Max public IEnumerable Reverse() { - Enumerator e = new Enumerator(this, true); + Enumerator e = new(this, true); while (e.MoveNext()) { yield return e.Current; @@ -2029,7 +2025,7 @@ public virtual SortedSet GetViewBetween(T lowerValue, T upperValue) /// debug status to be checked whenever any operation is called /// /// - internal virtual bool versionUpToDate() + internal virtual bool VersionUpToDate() { return true; } @@ -2059,7 +2055,7 @@ internal sealed class TreeSubSet : SortedSet { #if DEBUG - internal override bool versionUpToDate() + internal override bool VersionUpToDate() { return (this.version == underlying.version); } @@ -2112,7 +2108,7 @@ internal override bool AddIfNotPresent(T item) bool ret = underlying.AddIfNotPresent(item); VersionCheck(); #if DEBUG - Debug.Assert(this.versionUpToDate() && this.root == this.underlying.FindRange(min, max)); + Debug.Assert(this.VersionUpToDate() && this.root == this.underlying.FindRange(min, max)); #endif return ret; @@ -2123,7 +2119,7 @@ public override bool Contains(T item) { VersionCheck(); #if DEBUG - Debug.Assert(this.versionUpToDate() && this.root == this.underlying.FindRange(min, max)); + Debug.Assert(this.VersionUpToDate() && this.root == this.underlying.FindRange(min, max)); #endif return base.Contains(item); } @@ -2139,7 +2135,7 @@ internal override bool DoRemove(T item) bool ret = underlying.Remove(item); VersionCheck(); #if DEBUG - Debug.Assert(this.versionUpToDate() && this.root == this.underlying.FindRange(min, max)); + Debug.Assert(this.VersionUpToDate() && this.root == this.underlying.FindRange(min, max)); #endif return ret; } @@ -2153,11 +2149,11 @@ public override void Clear() return; } - List toRemove = new List(); + List toRemove = new(); BreadthFirstTreeWalk(delegate (Node n) { toRemove.Add(n.Item); return true; }); while (toRemove.Count != 0) { - underlying.Remove(toRemove[toRemove.Count - 1]); + underlying.Remove(toRemove[^1]); toRemove.RemoveAt(toRemove.Count - 1); } root = null; @@ -2193,7 +2189,7 @@ internal override bool InOrderTreeWalk(TreeWalkPredicate action, Boolean reve // The maximum height of a red-black tree is 2*lg(n+1). // See page 264 of "Introduction to algorithms" by Thomas H. Cormen - Stack stack = new Stack(2 * (int)SortedSet.log2(count + 1)); //this is not exactly right if count is out of date, but the stack can grow + Stack stack = new(2 * (int)SortedSet.Log2(count + 1)); //this is not exactly right if count is out of date, but the stack can grow Node current = root; while (current != null) { @@ -2250,8 +2246,10 @@ internal override bool BreadthFirstTreeWalk(TreeWalkPredicate action) return true; } - List processQueue = new List(); - processQueue.Add(root); + List processQueue = new() + { + root + }; Node current; while (processQueue.Count != 0) @@ -2284,7 +2282,7 @@ internal override SortedSet.Node FindNode(T item) } VersionCheck(); #if DEBUG - Debug.Assert(this.versionUpToDate() && this.root == this.underlying.FindRange(min, max)); + Debug.Assert(this.VersionUpToDate() && this.root == this.underlying.FindRange(min, max)); #endif return base.FindNode(item); } @@ -2301,7 +2299,7 @@ internal override int InternalIndexOf(T item) return count; } #if DEBUG - Debug.Assert(this.versionUpToDate() && this.root == this.underlying.FindRange(min, max)); + Debug.Assert(this.VersionUpToDate() && this.root == this.underlying.FindRange(min, max)); #endif return -1; } @@ -2350,7 +2348,7 @@ public override SortedSet GetViewBetween(T lowerValue, T upperValue) internal override void IntersectWithEnumerable(IEnumerable other) { - List toSave = new List(this.Count); + List toSave = new(this.Count); foreach (T item in other) { if (this.Contains(item)) @@ -2362,7 +2360,7 @@ internal override void IntersectWithEnumerable(IEnumerable other) this.Clear(); this.AddAllElements(toSave); #if DEBUG - Debug.Assert(this.versionUpToDate() && this.root == this.underlying.FindRange(min, max)); + Debug.Assert(this.VersionUpToDate() && this.root == this.underlying.FindRange(min, max)); #endif } @@ -2558,12 +2556,12 @@ public struct Enumerator : IEnumerator, IEnumerator { private Stack.Node> stack; private SortedSet.Node current; - static SortedSet.Node dummyNode = new SortedSet.Node(default(T)); + static readonly SortedSet.Node dummyNode = new(default); private bool reverse; #if !FEATURE_NETCORE - private SerializationInfo siInfo; + private readonly SerializationInfo siInfo; #endif internal Enumerator(SortedSet set) { @@ -2575,7 +2573,7 @@ internal Enumerator(SortedSet set) version = tree.version; // 2lg(n + 1) is the maximum height - stack = new Stack.Node>(2 * (int)SortedSet.log2(set.Count + 1)); + stack = new Stack.Node>(2 * (int)SortedSet.Log2(set.Count + 1)); current = null; reverse = false; #if !FEATURE_NETCORE @@ -2593,7 +2591,7 @@ internal Enumerator(SortedSet set, bool reverse) version = tree.version; // 2lg(n + 1) is the maximum height - stack = new Stack.Node>(2 * (int)SortedSet.log2(set.Count + 1)); + stack = new Stack.Node>(2 * (int)SortedSet.Log2(set.Count + 1)); current = null; this.reverse = reverse; #if !FEATURE_NETCORE @@ -2649,7 +2647,7 @@ private void OnDeserialization(Object sender) version = siInfo.GetInt32(EnumVersionName); reverse = siInfo.GetBoolean(ReverseName); bool EnumStarted = siInfo.GetBoolean(EnumStartName); - stack = new Stack.Node>(2 * (int)SortedSet.log2(tree.Count + 1)); + stack = new Stack.Node>(2 * (int)SortedSet.Log2(tree.Count + 1)); current = null; if (EnumStarted) { @@ -2750,7 +2748,7 @@ public T Current { return current.Item; } - return default(T); + return default; } } @@ -2827,12 +2825,12 @@ public bool TryGetValue(T equalValue, out T actualValue) actualValue = node.Item; return true; } - actualValue = default(T); + actualValue = default; return false; } // used for set checking operations (using enumerables) that rely on counting - private static int log2(int value) + private static int Log2(int value) { //Contract.Requires(value>0) int c = 0; @@ -2855,8 +2853,8 @@ private static int log2(int value) /// internal class SortedSetEqualityComparer : IEqualityComparer> { - private IComparer comparer; - private IEqualityComparer e_comparer; + private readonly IComparer comparer; + private readonly IEqualityComparer e_comparer; public SortedSetEqualityComparer() : this(null, null) { } @@ -2895,7 +2893,7 @@ public int GetHashCode(SortedSet obj) { foreach (T t in obj) { - hashCode = hashCode ^ (e_comparer.GetHashCode(t) & 0x7FFFFFFF); + hashCode ^= (e_comparer.GetHashCode(t) & 0x7FFFFFFF); } } // else returns hashcode of 0 for null HashSets return hashCode; @@ -2904,8 +2902,7 @@ public int GetHashCode(SortedSet obj) // Equals method for the comparer itself. public override bool Equals(Object obj) { - SortedSetEqualityComparer comparer = obj as SortedSetEqualityComparer; - if (comparer == null) + if (obj is not SortedSetEqualityComparer comparer) { return false; } diff --git a/src/IFoxCAD.Cad/ExtensionMethod/CurveEx.cs b/src/IFoxCAD.Cad/ExtensionMethod/CurveEx.cs index 2a73a76..a57c153 100644 --- a/src/IFoxCAD.Cad/ExtensionMethod/CurveEx.cs +++ b/src/IFoxCAD.Cad/ExtensionMethod/CurveEx.cs @@ -27,9 +27,10 @@ public static double GetLength(this Curve curve) /// /// /// 打断后曲线的集合 - public static IEnumerable GetSplitCurves(this Curve curve, - IEnumerable pars, - Func, IEnumerable> func = null) + public static IEnumerable GetSplitCurves(this Curve curve, + IEnumerable pars, + Func, + IEnumerable>? func = null) { if (func != null) pars = func.Invoke(pars); -- Gitee From 32b49d2fa641517e763ca2759a9b78c81ad6d95a Mon Sep 17 00:00:00 2001 From: liuqihong <540762622@qq.com> Date: Tue, 16 Aug 2022 23:43:40 +0800 Subject: [PATCH 376/675] =?UTF-8?q?=E4=B8=80=E4=B8=AAbug=E7=AD=89=E5=BE=85?= =?UTF-8?q?=E8=A7=A3=E5=86=B3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../ExtensionMethod/DBObjectEx.cs | 46 ++-- .../IFoxCAD.Gcad/IFoxCAD.Gcad.csproj | 93 +------- .../IFoxCAD.ZWcad/IFoxCAD.ZWcad.csproj | 93 +------- tests/Test/TestLisp.cs | 213 +++++++++--------- 4 files changed, 128 insertions(+), 317 deletions(-) diff --git a/src/IFoxCAD.Cad.Shared/ExtensionMethod/DBObjectEx.cs b/src/IFoxCAD.Cad.Shared/ExtensionMethod/DBObjectEx.cs index 07bba27..0142fe8 100644 --- a/src/IFoxCAD.Cad.Shared/ExtensionMethod/DBObjectEx.cs +++ b/src/IFoxCAD.Cad.Shared/ExtensionMethod/DBObjectEx.cs @@ -14,34 +14,33 @@ public static class DBObjectEx /// 要删除数据的组码 public static void RemoveXData(this DBObject obj, string appName, DxfCode dxfCode) { + if (obj.XData == null) + return; XDataList data = obj.XData; var indexlst = new List(); bool flag = false; + + // TODO 惊惊:山人来解决 1逻辑错误ifB是ifA的取反 2flag无用 3考虑用forr倒序删除片段 for (int i = 0; i < data.Count; i++) { - if (data[i].TypeCode == (int)DxfCode.ExtendedDataRegAppName && data[i].Value.ToString() == appName) - { + if (data[i].TypeCode == (int)DxfCode.ExtendedDataRegAppName && + data[i].Value.ToString() == appName) flag = true; - } + if (flag) { - if (data[i].TypeCode == (int)DxfCode.ExtendedDataRegAppName && data[i].Value.ToString() != appName) + if (data[i].TypeCode == (int)DxfCode.ExtendedDataRegAppName && + data[i].Value.ToString() != appName) break; if (data[i].TypeCode == (int)dxfCode) - { indexlst.Add(i); - } } } foreach (var item in indexlst) - { data.RemoveAt(item); - } using (obj.ForWrite()) - { obj.XData = data; - } } /// /// 修改扩展数据 @@ -52,29 +51,30 @@ public static void RemoveXData(this DBObject obj, string appName, DxfCode dxfCod /// 新的数据 public static void ChangeXData(this DBObject obj, string appName, DxfCode dxfCode, object newvalue) { + if (obj.XData == null) + return; XDataList data = obj.XData; bool flag = false; + + // TODO 惊惊:山人来解决 1逻辑错误ifB是ifA的取反 2flag无用 for (int i = 0; i < data.Count; i++) { - if (data[i].TypeCode == (int)DxfCode.ExtendedDataRegAppName && data[i].Value.ToString() == appName) - { + if (data[i].TypeCode == (int)DxfCode.ExtendedDataRegAppName && + data[i].Value.ToString() == appName) flag = true; - } + if (flag) { - if (data[i].TypeCode == (int)DxfCode.ExtendedDataRegAppName && data[i].Value.ToString() != appName) + if (data[i].TypeCode == (int)DxfCode.ExtendedDataRegAppName && + data[i].Value.ToString() != appName) break; if (data[i].TypeCode == (int)dxfCode) - { data[i] = new TypedValue((int)dxfCode, newvalue); - } } } using (obj.ForWrite()) - { obj.XData = data; - } } #endregion @@ -91,22 +91,16 @@ public static void ForWrite(this T obj, Action action) where T : DBObject var _isNotifyEnabled = obj.IsNotifyEnabled; var _isWriteEnabled = obj.IsWriteEnabled; if (_isNotifyEnabled) - { obj.UpgradeFromNotify(); - } else if (!_isWriteEnabled) - { obj.UpgradeOpen(); - } + action?.Invoke(obj); + if (_isNotifyEnabled) - { obj.DowngradeToNotify(_isWriteEnabled); - } else if (!_isWriteEnabled) - { obj.DowngradeOpen(); - } } /// diff --git a/src/IFoxCAD.Cad/IFoxCAD.Gcad/IFoxCAD.Gcad.csproj b/src/IFoxCAD.Cad/IFoxCAD.Gcad/IFoxCAD.Gcad.csproj index a1b6b9b..5f28270 100644 --- a/src/IFoxCAD.Cad/IFoxCAD.Gcad/IFoxCAD.Gcad.csproj +++ b/src/IFoxCAD.Cad/IFoxCAD.Gcad/IFoxCAD.Gcad.csproj @@ -1,92 +1 @@ - - - - preview - enable - - net35;net40;net45 - true - true - true - 0.3.6.1 - InspireFunction - xsfhlzh;vicwjb - 基于.NET的Cad二次开发类库 - InspireFunction - https://gitee.com/inspirefunction/ifoxcad - https://gitee.com/inspirefunction/ifoxcad.git - git - IFoxCAD;CAD;AutoCad;C#;NET - 增加四叉树测试. - true - true - true - LICENSE - true - x64 - - - - - - - - - - - - - - - - - - - - DEBUG - - - $(Configuration);ac2008;ac2009 - - - $(Configuration);ac2013 - - - $(Configuration);ac2015 - - - 1701;1702;CS1685 - - - 1701;1702;CS1685 - - - 1701;1702;CS1685 - - - 1701;1702;CS1685 - - - 1701;1702;CS1685 - - - 1701;1702;CS1685 - - - - - True - - - - - - - - - - - - + \ No newline at end of file diff --git a/src/IFoxCAD.Cad/IFoxCAD.ZWcad/IFoxCAD.ZWcad.csproj b/src/IFoxCAD.Cad/IFoxCAD.ZWcad/IFoxCAD.ZWcad.csproj index a1b6b9b..5f28270 100644 --- a/src/IFoxCAD.Cad/IFoxCAD.ZWcad/IFoxCAD.ZWcad.csproj +++ b/src/IFoxCAD.Cad/IFoxCAD.ZWcad/IFoxCAD.ZWcad.csproj @@ -1,92 +1 @@ - - - - preview - enable - - net35;net40;net45 - true - true - true - 0.3.6.1 - InspireFunction - xsfhlzh;vicwjb - 基于.NET的Cad二次开发类库 - InspireFunction - https://gitee.com/inspirefunction/ifoxcad - https://gitee.com/inspirefunction/ifoxcad.git - git - IFoxCAD;CAD;AutoCad;C#;NET - 增加四叉树测试. - true - true - true - LICENSE - true - x64 - - - - - - - - - - - - - - - - - - - - DEBUG - - - $(Configuration);ac2008;ac2009 - - - $(Configuration);ac2013 - - - $(Configuration);ac2015 - - - 1701;1702;CS1685 - - - 1701;1702;CS1685 - - - 1701;1702;CS1685 - - - 1701;1702;CS1685 - - - 1701;1702;CS1685 - - - 1701;1702;CS1685 - - - - - True - - - - - - - - - - - - + \ No newline at end of file diff --git a/tests/Test/TestLisp.cs b/tests/Test/TestLisp.cs index f657286..7a10953 100644 --- a/tests/Test/TestLisp.cs +++ b/tests/Test/TestLisp.cs @@ -1,122 +1,121 @@ -namespace Test +namespace Test; + +public class TestLisp { - public class TestLisp + //定义lisp函数 + [LispFunction("LispTest_RunLisp")] + public static object LispTest_RunLisp(ResultBuffer rb) { - //定义lisp函数 - [LispFunction("LispTest_RunLisp")] - public static object LispTest_RunLisp(ResultBuffer rb) - { - CmdTest_RunLisp(); - return null!; - } + CmdTest_RunLisp(); + return null!; + } - //模态命令,只有当CAD发出命令提示或当前没有其他的命令或程序活动的时候才可以被触发 - [CommandMethod("CmdTest_RunLisp1", CommandFlags.Modal)] - //透明命令,可以在一个命令提示输入的时候触发例如正交切换,zoom等 - [CommandMethod("CmdTest_RunLisp2", CommandFlags.Transparent)] - //选择图元之后执行命令将可以从 获取图元 - [CommandMethod("CmdTest_RunLisp3", CommandFlags.UsePickSet)] - //命令执行前已选中部分实体.在命令执行过程中这些标记不会被清除 - [CommandMethod("CmdTest_RunLisp4", CommandFlags.Redraw)] - //命令不能在透视图中使用 - [CommandMethod("CmdTest_RunLisp5", CommandFlags.NoPerspective)] - //命令不能通过 MULTIPLE命令 重复触发 - [CommandMethod("CmdTest_RunLisp6", CommandFlags.NoMultiple)] - //不允许在模型空间使用命令 - [CommandMethod("CmdTest_RunLisp7", CommandFlags.NoTileMode)] - //不允许在布局空间使用命令 - [CommandMethod("CmdTest_RunLisp8", CommandFlags.NoPaperSpace)] - //命令不能在OEM产品中使用 - [CommandMethod("CmdTest_RunLisp9", CommandFlags.NoOem)] - //不能直接使用命令名调用,必须使用 组名.全局名 调用 - [CommandMethod("CmdTest_RunLisp10", CommandFlags.Undefined)] - //定义lisp方法.已废弃 请使用lispfunction - [CommandMethod("CmdTest_RunLisp11", CommandFlags.Defun)] - //命令不会被存储在新的命令堆上 - [CommandMethod("CmdTest_RunLisp12", CommandFlags.NoNewStack)] - //命令不能被内部锁定(命令锁) - [CommandMethod("CmdTest_RunLisp13", CommandFlags.NoInternalLock)] - //调用命令的文档将会被锁定为只读 - [CommandMethod("CmdTest_RunLisp14", CommandFlags.DocReadLock)] - //调用命令的文档将会被锁定,类似document.lockdocument - [CommandMethod("CmdTest_RunLisp15", CommandFlags.DocExclusiveLock)] - //命令在CAD运行期间都能使用,而不只是在当前文档 - [CommandMethod("CmdTest_RunLisp16", CommandFlags.Session)] - //获取用户输入时,可以与属性面板之类的交互 - [CommandMethod("CmdTest_RunLisp17", CommandFlags.Interruptible)] - //命令不会被记录在命令历史记录 - [CommandMethod("CmdTest_RunLisp18", CommandFlags.NoHistory)] - //命令不会被 UNDO取消 - [CommandMethod("CmdTest_RunLisp19", CommandFlags.NoUndoMarker)] - //不能在参照块中使用命令 - [CommandMethod("CmdTest_RunLisp20", CommandFlags.NoBlockEditor)] + //模态命令,只有当CAD发出命令提示或当前没有其他的命令或程序活动的时候才可以被触发 + [CommandMethod("CmdTest_RunLisp1", CommandFlags.Modal)] + //透明命令,可以在一个命令提示输入的时候触发例如正交切换,zoom等 + [CommandMethod("CmdTest_RunLisp2", CommandFlags.Transparent)] + //选择图元之后执行命令将可以从 获取图元 + [CommandMethod("CmdTest_RunLisp3", CommandFlags.UsePickSet)] + //命令执行前已选中部分实体.在命令执行过程中这些标记不会被清除 + [CommandMethod("CmdTest_RunLisp4", CommandFlags.Redraw)] + //命令不能在透视图中使用 + [CommandMethod("CmdTest_RunLisp5", CommandFlags.NoPerspective)] + //命令不能通过 MULTIPLE命令 重复触发 + [CommandMethod("CmdTest_RunLisp6", CommandFlags.NoMultiple)] + //不允许在模型空间使用命令 + [CommandMethod("CmdTest_RunLisp7", CommandFlags.NoTileMode)] + //不允许在布局空间使用命令 + [CommandMethod("CmdTest_RunLisp8", CommandFlags.NoPaperSpace)] + //命令不能在OEM产品中使用 + [CommandMethod("CmdTest_RunLisp9", CommandFlags.NoOem)] + //不能直接使用命令名调用,必须使用 组名.全局名 调用 + [CommandMethod("CmdTest_RunLisp10", CommandFlags.Undefined)] + //定义lisp方法.已废弃 请使用lispfunction + [CommandMethod("CmdTest_RunLisp11", CommandFlags.Defun)] + //命令不会被存储在新的命令堆上 + [CommandMethod("CmdTest_RunLisp12", CommandFlags.NoNewStack)] + //命令不能被内部锁定(命令锁) + [CommandMethod("CmdTest_RunLisp13", CommandFlags.NoInternalLock)] + //调用命令的文档将会被锁定为只读 + [CommandMethod("CmdTest_RunLisp14", CommandFlags.DocReadLock)] + //调用命令的文档将会被锁定,类似document.lockdocument + [CommandMethod("CmdTest_RunLisp15", CommandFlags.DocExclusiveLock)] + //命令在CAD运行期间都能使用,而不只是在当前文档 + [CommandMethod("CmdTest_RunLisp16", CommandFlags.Session)] + //获取用户输入时,可以与属性面板之类的交互 + [CommandMethod("CmdTest_RunLisp17", CommandFlags.Interruptible)] + //命令不会被记录在命令历史记录 + [CommandMethod("CmdTest_RunLisp18", CommandFlags.NoHistory)] + //命令不会被 UNDO取消 + [CommandMethod("CmdTest_RunLisp19", CommandFlags.NoUndoMarker)] + //不能在参照块中使用命令 + [CommandMethod("CmdTest_RunLisp20", CommandFlags.NoBlockEditor)] #if ac2009 - //acad09增,不会被动作录制器 捕捉到 - [CommandMethod("CmdTest_RunLisp21", CommandFlags.NoActionRecording)] - //acad09增,会被动作录制器捕捉 - [CommandMethod("CmdTest_RunLisp22", CommandFlags.ActionMacro)] + //acad09增,不会被动作录制器 捕捉到 + [CommandMethod("CmdTest_RunLisp21", CommandFlags.NoActionRecording)] + //acad09增,会被动作录制器捕捉 + [CommandMethod("CmdTest_RunLisp22", CommandFlags.ActionMacro)] #endif #if !NET35 - //推断约束时不能使用命令 - [CommandMethod("CmdTest_RunLisp23", CommandFlags.NoInferConstraint)] - //命令允许在选择图元时临时显示动态尺寸 - [CommandMethod("CmdTest_RunLisp24", CommandFlags.TempShowDynDimension)] + //推断约束时不能使用命令 + [CommandMethod("CmdTest_RunLisp23", CommandFlags.NoInferConstraint)] + //命令允许在选择图元时临时显示动态尺寸 + [CommandMethod("CmdTest_RunLisp24", CommandFlags.TempShowDynDimension)] #endif - public static void CmdTest_RunLisp() - { - // 测试方法1: (command "CmdTest_RunLisp1") - // 测试方式2: (LispTest_RunLisp) - var dm = Application.DocumentManager; - var doc = dm.MdiActiveDocument; - var ed = doc.Editor; + public static void CmdTest_RunLisp() + { + // 测试方法1: (command "CmdTest_RunLisp1") + // 测试方式2: (LispTest_RunLisp) + var dm = Application.DocumentManager; + var doc = dm.MdiActiveDocument; + var ed = doc.Editor; - var sb = new StringBuilder(); - foreach (var item in Enum.GetValues(typeof(EditorEx.RunLispFlag))) - { - sb.Append((byte)item); - sb.Append(','); - } - sb.Remove(sb.Length - 1, 1); - var option = new PromptIntegerOptions($"\n输入RunLispFlag枚举值:[{sb}]"); - var ppr = ed.GetInteger(option); + var sb = new StringBuilder(); + foreach (var item in Enum.GetValues(typeof(EditorEx.RunLispFlag))) + { + sb.Append((byte)item); + sb.Append(','); + } + sb.Remove(sb.Length - 1, 1); + var option = new PromptIntegerOptions($"\n输入RunLispFlag枚举值:[{sb}]"); + var ppr = ed.GetInteger(option); - if (ppr.Status != PromptStatus.OK) - return; - var flag = (EditorEx.RunLispFlag)ppr.Value; + if (ppr.Status != PromptStatus.OK) + return; + var flag = (EditorEx.RunLispFlag)ppr.Value; - if (flag == EditorEx.RunLispFlag.AdsQueueexpr) - { - // 同步 - Env.Editor.RunLisp("(setq a 10)(princ)", - EditorEx.RunLispFlag.AdsQueueexpr); - Env.Editor.RunLisp("(princ a)", - EditorEx.RunLispFlag.AdsQueueexpr);//成功输出 - } - else if (flag == EditorEx.RunLispFlag.AcedEvaluateLisp) - { - // 使用(command "CmdTest_RunLisp1")发送,然后 !b 查看变量,acad08是有值的,高版本是null - var strlisp0 = "(setq b 20)"; - var res0 = Env.Editor.RunLisp(strlisp0, - EditorEx.RunLispFlag.AcedEvaluateLisp); //有lisp的返回值 + if (flag == EditorEx.RunLispFlag.AdsQueueexpr) + { + // 同步 + Env.Editor.RunLisp("(setq a 10)(princ)", + EditorEx.RunLispFlag.AdsQueueexpr); + Env.Editor.RunLisp("(princ a)", + EditorEx.RunLispFlag.AdsQueueexpr);//成功输出 + } + else if (flag == EditorEx.RunLispFlag.AcedEvaluateLisp) + { + // 使用(command "CmdTest_RunLisp1")发送,然后 !b 查看变量,acad08是有值的,高版本是null + var strlisp0 = "(setq b 20)"; + var res0 = Env.Editor.RunLisp(strlisp0, + EditorEx.RunLispFlag.AcedEvaluateLisp); //有lisp的返回值 - var strlisp1 = "(defun f1( / )(princ \"aa\"))"; - var res1 = Env.Editor.RunLisp(strlisp1, - EditorEx.RunLispFlag.AcedEvaluateLisp); //有lisp的返回值 + var strlisp1 = "(defun f1( / )(princ \"aa\"))"; + var res1 = Env.Editor.RunLisp(strlisp1, + EditorEx.RunLispFlag.AcedEvaluateLisp); //有lisp的返回值 - var strlisp2 = "(defun f2( / )(command \"line\"))"; - var res2 = Env.Editor.RunLisp(strlisp2, - EditorEx.RunLispFlag.AcedEvaluateLisp); //有lisp的返回值 - } - else if (flag == EditorEx.RunLispFlag.SendStringToExecute) - { - //测试异步 - //(command "CmdTest_RunLisp1")和(LispTest_RunLisp)4都是异步 - var str = "(setq c 40)(princ)"; - Env.Editor.RunLisp(str, - EditorEx.RunLispFlag.SendStringToExecute); //异步,后发送 - Env.Editor.RunLisp("(princ c)", - EditorEx.RunLispFlag.AdsQueueexpr); //同步,先发送了,输出是null - } + var strlisp2 = "(defun f2( / )(command \"line\"))"; + var res2 = Env.Editor.RunLisp(strlisp2, + EditorEx.RunLispFlag.AcedEvaluateLisp); //有lisp的返回值 + } + else if (flag == EditorEx.RunLispFlag.SendStringToExecute) + { + //测试异步 + //(command "CmdTest_RunLisp1")和(LispTest_RunLisp)4都是异步 + var str = "(setq c 40)(princ)"; + Env.Editor.RunLisp(str, + EditorEx.RunLispFlag.SendStringToExecute); //异步,后发送 + Env.Editor.RunLisp("(princ c)", + EditorEx.RunLispFlag.AdsQueueexpr); //同步,先发送了,输出是null } } } -- Gitee From 1be41d4812aa569b8eff4731abdc40ea1689f73e Mon Sep 17 00:00:00 2001 From: liuqihong <540762622@qq.com> Date: Tue, 16 Aug 2022 23:57:33 +0800 Subject: [PATCH 377/675] =?UTF-8?q?=E4=BF=AE=E6=94=B9RemoveXData=E7=9A=84b?= =?UTF-8?q?ug?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../ExtensionMethod/DBObjectEx.cs | 47 +++++++------------ 1 file changed, 18 insertions(+), 29 deletions(-) diff --git a/src/IFoxCAD.Cad.Shared/ExtensionMethod/DBObjectEx.cs b/src/IFoxCAD.Cad.Shared/ExtensionMethod/DBObjectEx.cs index 0142fe8..8ebee46 100644 --- a/src/IFoxCAD.Cad.Shared/ExtensionMethod/DBObjectEx.cs +++ b/src/IFoxCAD.Cad.Shared/ExtensionMethod/DBObjectEx.cs @@ -17,28 +17,21 @@ public static void RemoveXData(this DBObject obj, string appName, DxfCode dxfCod if (obj.XData == null) return; XDataList data = obj.XData; - var indexlst = new List(); - bool flag = false; - - // TODO 惊惊:山人来解决 1逻辑错误ifB是ifA的取反 2flag无用 3考虑用forr倒序删除片段 + bool appNameIdentical = false; for (int i = 0; i < data.Count; i++) { - if (data[i].TypeCode == (int)DxfCode.ExtendedDataRegAppName && - data[i].Value.ToString() == appName) - flag = true; - - if (flag) + if (data[i].TypeCode == (int)DxfCode.ExtendedDataRegAppName) { - if (data[i].TypeCode == (int)DxfCode.ExtendedDataRegAppName && - data[i].Value.ToString() != appName) - break; - if (data[i].TypeCode == (int)dxfCode) - indexlst.Add(i); + appNameIdentical = data[i].Value.ToString() == appName; + break; } } - foreach (var item in indexlst) - data.RemoveAt(item); + if (!appNameIdentical) + return; + for (int i = data.Count - 1; i >= 0; i--) + if (data[i].TypeCode == (int)dxfCode) + data.RemoveAt(i); using (obj.ForWrite()) obj.XData = data; } @@ -54,25 +47,21 @@ public static void ChangeXData(this DBObject obj, string appName, DxfCode dxfCod if (obj.XData == null) return; XDataList data = obj.XData; - bool flag = false; - - // TODO 惊惊:山人来解决 1逻辑错误ifB是ifA的取反 2flag无用 + bool appNameIdentical = false; for (int i = 0; i < data.Count; i++) { - if (data[i].TypeCode == (int)DxfCode.ExtendedDataRegAppName && - data[i].Value.ToString() == appName) - flag = true; - - if (flag) + if (data[i].TypeCode == (int)DxfCode.ExtendedDataRegAppName) { - if (data[i].TypeCode == (int)DxfCode.ExtendedDataRegAppName && - data[i].Value.ToString() != appName) - break; - if (data[i].TypeCode == (int)dxfCode) - data[i] = new TypedValue((int)dxfCode, newvalue); + appNameIdentical = data[i].Value.ToString() == appName; + break; } } + if (!appNameIdentical) + return; + for (int i = data.Count - 1; i >= 0; i--) + if (data[i].TypeCode == (int)dxfCode) + data[i] = new TypedValue((int)dxfCode, newvalue); using (obj.ForWrite()) obj.XData = data; } -- Gitee From ffdec6bd8f2c368e96b4fc591309244bb8f711c0 Mon Sep 17 00:00:00 2001 From: liuqihong <540762622@qq.com> Date: Wed, 17 Aug 2022 00:48:11 +0800 Subject: [PATCH 378/675] =?UTF-8?q?=E4=BF=AE=E6=94=B9=E9=83=A8=E5=88=86?= =?UTF-8?q?=E6=A0=BC=E5=BC=8F=E5=B9=B6=E4=B8=94=E7=94=B1=E4=BA=8E=E5=85=B6?= =?UTF-8?q?=E4=BD=99cad=E6=97=A0=E4=BA=BA=E7=BB=B4=E6=8A=A4=E5=85=88?= =?UTF-8?q?=E4=B8=8B=E6=9E=B6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/IFoxCAD.Basal/CLS/RuntimeHelpers.cs | 5 +- .../ExtensionMethod/SymbolTableEx.cs | 164 ++++++++---------- src/IFoxCAD.Cad.Shared/Runtime/DBTrans.cs | 5 - src/IFoxCAD.Cad.Shared/Runtime/SymbolTable.cs | 36 ++-- .../IFoxCAD.Aacad08/GlobalUsings.cs | 3 +- .../IFoxCAD.Aacad09plus/GlobalUsings.cs | 3 +- src/IFoxCAD.Cad/IFoxCAD.Gcad/GlobalUsings.cs | 41 ----- .../IFoxCAD.Gcad/IFoxCAD.Gcad.csproj | 83 ++++++++- src/IFoxCAD.Cad/IFoxCAD.ZWcad/GlobalUsings.cs | 41 ----- .../IFoxCAD.ZWcad/IFoxCAD.ZWcad.csproj | 1 - .../IFoxCAD.Zcad/IFoxCAD.Zcad.csproj | 82 +++++++++ tests/Test/Test.cs | 6 +- tests/Test/testblock.cs | 9 - 13 files changed, 268 insertions(+), 211 deletions(-) delete mode 100644 src/IFoxCAD.Cad/IFoxCAD.Gcad/GlobalUsings.cs delete mode 100644 src/IFoxCAD.Cad/IFoxCAD.ZWcad/GlobalUsings.cs delete mode 100644 src/IFoxCAD.Cad/IFoxCAD.ZWcad/IFoxCAD.ZWcad.csproj create mode 100644 src/IFoxCAD.Cad/IFoxCAD.Zcad/IFoxCAD.Zcad.csproj diff --git a/src/IFoxCAD.Basal/CLS/RuntimeHelpers.cs b/src/IFoxCAD.Basal/CLS/RuntimeHelpers.cs index a446c59..29c2c3c 100644 --- a/src/IFoxCAD.Basal/CLS/RuntimeHelpers.cs +++ b/src/IFoxCAD.Basal/CLS/RuntimeHelpers.cs @@ -5,7 +5,10 @@ namespace System.Runtime.CompilerServices; +//编译提示多个程序集中定义,屏蔽不了,但是不影响编译 +//#pragma warning disable CS1685 // 类型与导入类型冲突 public static class RuntimeHelpers +//#pragma warning restore CS1685 // 类型与导入类型冲突 { /// /// Slices the specified array using the specified range. @@ -38,7 +41,5 @@ public static T[] GetSubArray(T[] array, Range range) return dest; } } - - } //#endif \ No newline at end of file diff --git a/src/IFoxCAD.Cad.Shared/ExtensionMethod/SymbolTableEx.cs b/src/IFoxCAD.Cad.Shared/ExtensionMethod/SymbolTableEx.cs index 3c0ace3..a390436 100644 --- a/src/IFoxCAD.Cad.Shared/ExtensionMethod/SymbolTableEx.cs +++ b/src/IFoxCAD.Cad.Shared/ExtensionMethod/SymbolTableEx.cs @@ -38,19 +38,13 @@ public static ObjectId Add(this SymbolTable table, /// 新图层名 public static ObjectId Rename(this SymbolTable table, string Oldname, string NewName) { - if (table.Has(Oldname)) - { - table.Change(Oldname, ly => - { - ly.Name = NewName; - } - ); - return table[NewName]; - } - else - { + if (!table.Has(Oldname)) return ObjectId.Null; - } + + table.Change(Oldname, layer => { + layer.Name = NewName; + }); + return table[NewName]; } /// /// 删除图层 @@ -61,24 +55,18 @@ public static ObjectId Rename(this SymbolTable tab public static bool Delete(this SymbolTable table, string name) { if (name == "0" || name == "Defpoints" || !table.Has(name) || table[name] == DBTrans.Top.Database.Clayer) - { return false; - } + table.CurrentSymbolTable.GenerateUsageData(); var ltr = table.GetRecord(name); - if (ltr is not null) - { - if (ltr.IsUsed) - { - return false; - } - using (ltr.ForWrite()) - { - ltr.Erase(); - } - return true; - } - return false; + if (ltr is null) + return false; + + if (ltr.IsUsed) + return false; + using (ltr.ForWrite()) + ltr.Erase(); + return true; } #endregion @@ -93,22 +81,22 @@ public static bool Delete(this SymbolTable table, /// 添加属性定义的委托 /// 块定义id /// TODO: 需要测试匿名块等特殊的块是否能定义 - public static ObjectId Add(this SymbolTable table, string name, Action? action = null, Func>? ents = null, Func>? attdef = null) + public static ObjectId Add(this SymbolTable table, + string name, + Action? action = null, + Func>? ents = null, + Func>? attdef = null) { - return table.Add(name, btr => - { + return table.Add(name, btr => { action?.Invoke(btr); + var entsres = ents?.Invoke(); if (entsres is not null) - { btr.AddEntity(entsres); - } + var adddefres = attdef?.Invoke(); if (adddefres is not null) - { btr.AddEntity(adddefres); - } - }); } /// @@ -119,18 +107,16 @@ public static ObjectId Add(this SymbolTable table, /// 图元 /// 属性定义 /// - public static ObjectId Add(this SymbolTable table, string name, IEnumerable? ents = null, IEnumerable? attdef = null) + public static ObjectId Add(this SymbolTable table, + string name, + IEnumerable? ents = null, + IEnumerable? attdef = null) { - return table.Add(name, btr => - { + return table.Add(name, btr => { if (ents is not null) - { btr.AddEntity(ents); - } if (attdef is not null) - { btr.AddEntity(attdef); - } }); } @@ -152,20 +138,19 @@ public static ObjectId Add(this SymbolTable table, /// 块表 /// 块定义id /// 属性列表 - public static void AddAttsToBlocks(this SymbolTable table, ObjectId id, List atts) + public static void AddAttsToBlocks(this SymbolTable table, + ObjectId id, + List atts) { - table.Change(id, btr => - { - var attTags = new List(); + List attTags = new(); + table.Change(id, btr => { + btr.GetEntities() .ForEach(def => attTags.Add(def.Tag.ToUpper())); - foreach (AttributeDefinition att in atts) - { - if (!attTags.Contains(att.Tag.ToUpper())) - { - btr.AddEntity(att); - } - } + + for (int i = 0; i < atts.Count; i++) + if (!attTags.Contains(atts[i].Tag.ToUpper())) + btr.AddEntity(atts[i]); }); } /// @@ -174,20 +159,19 @@ public static void AddAttsToBlocks(this SymbolTable块表 /// 块定义名字 /// 属性列表 - public static void AddAttsToBlocks(this SymbolTable table, string name, List atts) + public static void AddAttsToBlocks(this SymbolTable table, + string name, + List atts) { - table.Change(name, btr => - { - var attTags = new List(); + List attTags = new(); + table.Change(name, btr => { + btr.GetEntities() .ForEach(def => attTags.Add(def.Tag.ToUpper())); - foreach (AttributeDefinition att in atts) - { - if (!attTags.Contains(att.Tag.ToUpper())) - { - btr.AddEntity(att); - } - } + + for (int i = 0; i < atts.Count; i++) + if (!attTags.Contains(atts[i].Tag.ToUpper())) + btr.AddEntity(atts[i]); }); } @@ -203,11 +187,10 @@ public static ObjectId GetBlockFrom(this SymbolTable块定义名 /// 是否覆盖 /// 块定义Id - public static ObjectId GetBlockFrom(this SymbolTable table, string fileName, string blockName, bool over) + public static ObjectId GetBlockFrom(this SymbolTable table, + string fileName, + string blockName, + bool over) { - return - table.GetRecordFrom( - t => t.BlockTable, - fileName, - blockName, - over); + return table.GetRecordFrom(t => t.BlockTable, fileName, blockName, over); } #endregion @@ -258,20 +239,19 @@ public static ObjectId Add(this SymbolTable { return table.Add( name, - ltt => - { + ltt => { ltt.AsciiDescription = description; ltt.PatternLength = length; //线型的总长度 - ltt.NumDashes = dash.Length; //组成线型的笔画数目 - for (int i = 0; i < dash.Length; i++) + ltt.NumDashes = dash.Length; //组成线型的笔画数目 + for (int i = 0; i < dash.Length; i++) { ltt.SetDashLengthAt(i, dash[i]); } - //ltt.SetDashLengthAt(0, 0.5); //0.5个单位的划线 - //ltt.SetDashLengthAt(1, -0.25); //0.25个单位的空格 - //ltt.SetDashLengthAt(2, 0); // 一个点 - //ltt.SetDashLengthAt(3, -0.25); //0.25个单位的空格 - } + //ltt.SetDashLengthAt(0, 0.5); //0.5个单位的划线 + //ltt.SetDashLengthAt(1, -0.25); //0.25个单位的空格 + //ltt.SetDashLengthAt(2, 0); // 一个点 + //ltt.SetDashLengthAt(3, -0.25); //0.25个单位的空格 + } ); } #endregion @@ -293,8 +273,7 @@ public static ObjectId Add(this SymbolTable - { + tstr => { tstr.Name = textStyleName; tstr.FileName = font; tstr.XScale = xscale; @@ -308,7 +287,10 @@ public static ObjectId Add(this SymbolTable字体名枚举 /// 宽度比例 /// 文字样式Id - public static ObjectId Add(this SymbolTable table, string textStyleName, FontTTF fontTTF, double xscale = 1.0) + public static ObjectId Add(this SymbolTable table, + string textStyleName, + FontTTF fontTTF, + double xscale = 1.0) { return table.Add(textStyleName, fontTTF.GetDesc(), xscale); } @@ -335,20 +317,16 @@ public static ObjectId AddWithChange(this SymbolTable - { + table.Change(textStyleName, ttr => { ttr.FileName = smallFont; ttr.XScale = xScale; ttr.TextSize = height; - if (bigFont != "") - { + if (bigFont != string.Empty) ttr.BigFontFileName = bigFont; - } }); return table[textStyleName]; } - return table.Add(textStyleName, ttr => - { + return table.Add(textStyleName, ttr => { ttr.FileName = smallFont; ttr.XScale = xScale; ttr.TextSize = height; diff --git a/src/IFoxCAD.Cad.Shared/Runtime/DBTrans.cs b/src/IFoxCAD.Cad.Shared/Runtime/DBTrans.cs index d207edf..b97f733 100644 --- a/src/IFoxCAD.Cad.Shared/Runtime/DBTrans.cs +++ b/src/IFoxCAD.Cad.Shared/Runtime/DBTrans.cs @@ -122,9 +122,7 @@ public DBTrans(Database database, bool commit = true) /// 密码 public DBTrans(string fileName, bool commit = true, -#pragma warning disable CS0436 // 类型与导入类型冲突 FileOpenMode openMode = FileOpenMode.OpenForReadAndWriteNoShare, -#pragma warning restore CS0436 // 类型与导入类型冲突 string? password = null) { if (string.IsNullOrEmpty(fileName?.Trim())) @@ -337,9 +335,6 @@ public ObjectId GetObjectId(string handleString) //return Database.GetObjectId(false, hanle, 0); return Helper.TryGetObjectId(Database, hanle); } - - - #endregion #region 保存文件 diff --git a/src/IFoxCAD.Cad.Shared/Runtime/SymbolTable.cs b/src/IFoxCAD.Cad.Shared/Runtime/SymbolTable.cs index 434625e..7312d93 100644 --- a/src/IFoxCAD.Cad.Shared/Runtime/SymbolTable.cs +++ b/src/IFoxCAD.Cad.Shared/Runtime/SymbolTable.cs @@ -1,5 +1,20 @@ namespace IFoxCAD.Cad; +/// +/// 委托执行状态 +/// +public enum DelegateState +{ + /// + /// 继续执行 + /// + Go, + /// + /// 中断执行 + /// + Break +} + public class SymbolTable : IEnumerable where TTable : SymbolTable where TRecord : SymbolTableRecord, new() @@ -158,12 +173,11 @@ public void Remove(ObjectId id) private static void Change(TRecord record, Action action) { using (record.ForWrite()) - { action.Invoke(record); - } // 调用regen()函数可能会导致卡顿 //Env.Editor.Regen(); } + /// /// 修改符号表 /// @@ -173,10 +187,9 @@ public void Change(string name, Action action) { var record = GetRecord(name); if (record is not null) - { Change(record, action); - } } + /// /// 修改符号表 /// @@ -186,9 +199,7 @@ public void Change(ObjectId id, Action action) { var record = GetRecord(id); if (record is not null) - { Change(record, action); - } } #endregion @@ -219,9 +230,7 @@ public IEnumerable GetRecords() { var record = GetRecord(item); if (record is not null) - { yield return record; - } } } @@ -241,9 +250,7 @@ public IEnumerable GetRecordNames(Func filter) { var record = GetRecord(item); if (record is not null && filter.Invoke(record)) - { yield return record.Name; - } } } @@ -257,9 +264,7 @@ public IEnumerable GetRecordNames(Func filter) public ObjectId GetRecordFrom(SymbolTable table, string name, bool over) { if (table is null) - { throw new ArgumentNullException(nameof(table), "对象为null"); - } ObjectId rid = this[name]; bool has = rid != ObjectId.Null; @@ -269,7 +274,12 @@ public ObjectId GetRecordFrom(SymbolTable table, string name, b using IdMapping idm = new(); using (ObjectIdCollection ids = new() { id }) { - table.Database.WblockCloneObjects(ids, CurrentSymbolTable.Id, idm, DuplicateRecordCloning.Replace, false); + table.Database. + WblockCloneObjects(ids, + CurrentSymbolTable.Id, + idm, + DuplicateRecordCloning.Replace, + false); } rid = idm[id].Value; } diff --git a/src/IFoxCAD.Cad/IFoxCAD.Aacad08/GlobalUsings.cs b/src/IFoxCAD.Cad/IFoxCAD.Aacad08/GlobalUsings.cs index a125299..2c84a3c 100644 --- a/src/IFoxCAD.Cad/IFoxCAD.Aacad08/GlobalUsings.cs +++ b/src/IFoxCAD.Cad/IFoxCAD.Aacad08/GlobalUsings.cs @@ -25,8 +25,7 @@ global using Autodesk.AutoCAD.DatabaseServices.Filters; global using Autodesk.AutoCAD; -//jig此命名空间容易引起Polyline等等重义,因此不放入全局空间 -//using Autodesk.AutoCAD.GraphicsInterface; +//jig命名空间会引起Viewport/Polyline等等重义,最好逐个引入 using Autodesk.AutoCAD.GraphicsInterface global using WorldDraw = Autodesk.AutoCAD.GraphicsInterface.WorldDraw; global using Manager = Autodesk.AutoCAD.GraphicsSystem.Manager; diff --git a/src/IFoxCAD.Cad/IFoxCAD.Aacad09plus/GlobalUsings.cs b/src/IFoxCAD.Cad/IFoxCAD.Aacad09plus/GlobalUsings.cs index a125299..2c84a3c 100644 --- a/src/IFoxCAD.Cad/IFoxCAD.Aacad09plus/GlobalUsings.cs +++ b/src/IFoxCAD.Cad/IFoxCAD.Aacad09plus/GlobalUsings.cs @@ -25,8 +25,7 @@ global using Autodesk.AutoCAD.DatabaseServices.Filters; global using Autodesk.AutoCAD; -//jig此命名空间容易引起Polyline等等重义,因此不放入全局空间 -//using Autodesk.AutoCAD.GraphicsInterface; +//jig命名空间会引起Viewport/Polyline等等重义,最好逐个引入 using Autodesk.AutoCAD.GraphicsInterface global using WorldDraw = Autodesk.AutoCAD.GraphicsInterface.WorldDraw; global using Manager = Autodesk.AutoCAD.GraphicsSystem.Manager; diff --git a/src/IFoxCAD.Cad/IFoxCAD.Gcad/GlobalUsings.cs b/src/IFoxCAD.Cad/IFoxCAD.Gcad/GlobalUsings.cs deleted file mode 100644 index a125299..0000000 --- a/src/IFoxCAD.Cad/IFoxCAD.Gcad/GlobalUsings.cs +++ /dev/null @@ -1,41 +0,0 @@ -/// 系统引用 -global using System; -global using System.Collections; -global using System.Collections.Generic; -global using System.IO; -global using System.Linq; -global using System.Text; -global using System.Reflection; -global using System.Text.RegularExpressions; -global using Microsoft.Win32; -global using System.ComponentModel; -global using System.Runtime.InteropServices; - -global using Exception = System.Exception; - -/// autocad 引用 -global using Autodesk.AutoCAD.ApplicationServices; -global using Autodesk.AutoCAD.EditorInput; -global using Autodesk.AutoCAD.Colors; -global using Autodesk.AutoCAD.DatabaseServices; -global using Autodesk.AutoCAD.Geometry; -global using Autodesk.AutoCAD.Runtime; -global using Acap = Autodesk.AutoCAD.ApplicationServices.Application; - -global using Autodesk.AutoCAD.DatabaseServices.Filters; -global using Autodesk.AutoCAD; - -//jig此命名空间容易引起Polyline等等重义,因此不放入全局空间 -//using Autodesk.AutoCAD.GraphicsInterface; -global using WorldDraw = Autodesk.AutoCAD.GraphicsInterface.WorldDraw; -global using Manager = Autodesk.AutoCAD.GraphicsSystem.Manager; - -global using Group = Autodesk.AutoCAD.DatabaseServices.Group; -global using Viewport = Autodesk.AutoCAD.DatabaseServices.Viewport; - -global using System.Collections.Specialized; -global using Registry = Microsoft.Win32.Registry; -global using RegistryKey = Microsoft.Win32.RegistryKey; - -/// ifoxcad.basal 引用 -global using IFoxCAD.Basal; \ No newline at end of file diff --git a/src/IFoxCAD.Cad/IFoxCAD.Gcad/IFoxCAD.Gcad.csproj b/src/IFoxCAD.Cad/IFoxCAD.Gcad/IFoxCAD.Gcad.csproj index 5f28270..7852e32 100644 --- a/src/IFoxCAD.Cad/IFoxCAD.Gcad/IFoxCAD.Gcad.csproj +++ b/src/IFoxCAD.Cad/IFoxCAD.Gcad/IFoxCAD.Gcad.csproj @@ -1 +1,82 @@ - \ No newline at end of file + + + + net35;net40;net45 + true + enable + true + true + 0.3.7 + InspireFunction + xsfhlzh;vicwjb;liuqihong + 基于.NET的Cad二次开发类库 + InspireFunction + https://gitee.com/inspirefunction/ifoxcad + https://gitee.com/inspirefunction/ifoxcad.git + git + IFoxCAD;CAD;AutoCad;C#;NET + 增加很多东西. + true + true + preview + true + LICENSE + true + x64 + + + + + runtime + + + + + + runtime + + + + + + runtime + + + 4.3.0 + + + + + + + + DEBUG + + + $(Configuration);ac2009 + + + $(Configuration);ac2013 + + + $(Configuration);ac2015 + + + + True + + + + + + + + + + + + diff --git a/src/IFoxCAD.Cad/IFoxCAD.ZWcad/GlobalUsings.cs b/src/IFoxCAD.Cad/IFoxCAD.ZWcad/GlobalUsings.cs deleted file mode 100644 index a125299..0000000 --- a/src/IFoxCAD.Cad/IFoxCAD.ZWcad/GlobalUsings.cs +++ /dev/null @@ -1,41 +0,0 @@ -/// 系统引用 -global using System; -global using System.Collections; -global using System.Collections.Generic; -global using System.IO; -global using System.Linq; -global using System.Text; -global using System.Reflection; -global using System.Text.RegularExpressions; -global using Microsoft.Win32; -global using System.ComponentModel; -global using System.Runtime.InteropServices; - -global using Exception = System.Exception; - -/// autocad 引用 -global using Autodesk.AutoCAD.ApplicationServices; -global using Autodesk.AutoCAD.EditorInput; -global using Autodesk.AutoCAD.Colors; -global using Autodesk.AutoCAD.DatabaseServices; -global using Autodesk.AutoCAD.Geometry; -global using Autodesk.AutoCAD.Runtime; -global using Acap = Autodesk.AutoCAD.ApplicationServices.Application; - -global using Autodesk.AutoCAD.DatabaseServices.Filters; -global using Autodesk.AutoCAD; - -//jig此命名空间容易引起Polyline等等重义,因此不放入全局空间 -//using Autodesk.AutoCAD.GraphicsInterface; -global using WorldDraw = Autodesk.AutoCAD.GraphicsInterface.WorldDraw; -global using Manager = Autodesk.AutoCAD.GraphicsSystem.Manager; - -global using Group = Autodesk.AutoCAD.DatabaseServices.Group; -global using Viewport = Autodesk.AutoCAD.DatabaseServices.Viewport; - -global using System.Collections.Specialized; -global using Registry = Microsoft.Win32.Registry; -global using RegistryKey = Microsoft.Win32.RegistryKey; - -/// ifoxcad.basal 引用 -global using IFoxCAD.Basal; \ No newline at end of file diff --git a/src/IFoxCAD.Cad/IFoxCAD.ZWcad/IFoxCAD.ZWcad.csproj b/src/IFoxCAD.Cad/IFoxCAD.ZWcad/IFoxCAD.ZWcad.csproj deleted file mode 100644 index 5f28270..0000000 --- a/src/IFoxCAD.Cad/IFoxCAD.ZWcad/IFoxCAD.ZWcad.csproj +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/src/IFoxCAD.Cad/IFoxCAD.Zcad/IFoxCAD.Zcad.csproj b/src/IFoxCAD.Cad/IFoxCAD.Zcad/IFoxCAD.Zcad.csproj new file mode 100644 index 0000000..7852e32 --- /dev/null +++ b/src/IFoxCAD.Cad/IFoxCAD.Zcad/IFoxCAD.Zcad.csproj @@ -0,0 +1,82 @@ + + + + net35;net40;net45 + true + enable + true + true + 0.3.7 + InspireFunction + xsfhlzh;vicwjb;liuqihong + 基于.NET的Cad二次开发类库 + InspireFunction + https://gitee.com/inspirefunction/ifoxcad + https://gitee.com/inspirefunction/ifoxcad.git + git + IFoxCAD;CAD;AutoCad;C#;NET + 增加很多东西. + true + true + preview + true + LICENSE + true + x64 + + + + + runtime + + + + + + runtime + + + + + + runtime + + + 4.3.0 + + + + + + + + DEBUG + + + $(Configuration);ac2009 + + + $(Configuration);ac2013 + + + $(Configuration);ac2015 + + + + True + + + + + + + + + + + + diff --git a/tests/Test/Test.cs b/tests/Test/Test.cs index c1d70eb..80c7f29 100644 --- a/tests/Test/Test.cs +++ b/tests/Test/Test.cs @@ -279,7 +279,11 @@ public void PrintLayerName() { tr.Editor.WriteMessage(layerRecord.Name); } - + foreach (var layerRecord in tr.LayerTable.GetRecords()) + { + tr.Editor.WriteMessage(layerRecord.Name); + break; + } } diff --git a/tests/Test/testblock.cs b/tests/Test/testblock.cs index b80924e..b1c6af5 100644 --- a/tests/Test/testblock.cs +++ b/tests/Test/testblock.cs @@ -55,8 +55,6 @@ public void BlockDefChange() //}); - - tr.BlockTable.Change("test", btr => { foreach (var id in btr) { @@ -68,16 +66,9 @@ public void BlockDefChange() dBText.DimensionText = "234"; dBText.RecomputeDimensionBlock(true); } - if (ent is Hatch hatch) - { hatch.ColorIndex = 0; - - } - - } - } }); tr.Editor.Regen(); -- Gitee From e993781b4d90726af51041cf373dc756f565c1cc Mon Sep 17 00:00:00 2001 From: liuqihong <540762622@qq.com> Date: Wed, 17 Aug 2022 01:30:44 +0800 Subject: [PATCH 379/675] =?UTF-8?q?=E7=BB=9F=E4=B8=80=E4=B8=A4=E4=B8=AAcsp?= =?UTF-8?q?roj=E6=A0=BC=E5=BC=8F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- IFoxCAD.sln | 16 ------------- .../IFoxCAD.Aacad08/IFoxCAD.Acad08.csproj | 3 +-- .../IFoxCAD.Acad09plus.csproj | 24 ++++--------------- 3 files changed, 6 insertions(+), 37 deletions(-) diff --git a/IFoxCAD.sln b/IFoxCAD.sln index 43c5de7..53407b3 100644 --- a/IFoxCAD.sln +++ b/IFoxCAD.sln @@ -15,10 +15,6 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Cad", "Cad", "{465C4E39-FBA EndProject Project("{D954291E-2A0B-460D-934E-DC6B0785DB48}") = "IFoxCAD.Cad.Shared", "src\IFoxCAD.Cad.Shared\IFoxCAD.Cad.Shared.shproj", "{82FB8450-B971-4E30-859F-4B2DDB81F590}" EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "IFoxCAD.Gcad", "src\IFoxCAD.Cad\IFoxCAD.Gcad\IFoxCAD.Gcad.csproj", "{D7756AF6-601D-40C2-97E9-940E5AFC2E08}" -EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "IFoxCAD.ZWcad", "src\IFoxCAD.Cad\IFoxCAD.ZWcad\IFoxCAD.ZWcad.csproj", "{C6FC723B-B4CD-475A-BBA7-4FE5CE82AC5A}" -EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "IFoxCAD.Acad08", "src\IFoxCAD.Cad\IFoxCAD.Aacad08\IFoxCAD.Acad08.csproj", "{36F87D06-88B3-45E3-A2A8-0FC737B25428}" EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "IFoxCAD.Acad09plus", "src\IFoxCAD.Cad\IFoxCAD.Aacad09plus\IFoxCAD.Acad09plus.csproj", "{CC46E4E2-90DF-48F5-B3D7-83FAFED58F2A}" @@ -45,14 +41,6 @@ Global {E2873F0B-CAD2-45E8-8FF0-C05C0C6AD955}.Debug|Any CPU.Build.0 = Debug|Any CPU {E2873F0B-CAD2-45E8-8FF0-C05C0C6AD955}.Release|Any CPU.ActiveCfg = Release|Any CPU {E2873F0B-CAD2-45E8-8FF0-C05C0C6AD955}.Release|Any CPU.Build.0 = Release|Any CPU - {D7756AF6-601D-40C2-97E9-940E5AFC2E08}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {D7756AF6-601D-40C2-97E9-940E5AFC2E08}.Debug|Any CPU.Build.0 = Debug|Any CPU - {D7756AF6-601D-40C2-97E9-940E5AFC2E08}.Release|Any CPU.ActiveCfg = Release|Any CPU - {D7756AF6-601D-40C2-97E9-940E5AFC2E08}.Release|Any CPU.Build.0 = Release|Any CPU - {C6FC723B-B4CD-475A-BBA7-4FE5CE82AC5A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {C6FC723B-B4CD-475A-BBA7-4FE5CE82AC5A}.Debug|Any CPU.Build.0 = Debug|Any CPU - {C6FC723B-B4CD-475A-BBA7-4FE5CE82AC5A}.Release|Any CPU.ActiveCfg = Release|Any CPU - {C6FC723B-B4CD-475A-BBA7-4FE5CE82AC5A}.Release|Any CPU.Build.0 = Release|Any CPU {36F87D06-88B3-45E3-A2A8-0FC737B25428}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {36F87D06-88B3-45E3-A2A8-0FC737B25428}.Debug|Any CPU.Build.0 = Debug|Any CPU {36F87D06-88B3-45E3-A2A8-0FC737B25428}.Release|Any CPU.ActiveCfg = Release|Any CPU @@ -67,8 +55,6 @@ Global EndGlobalSection GlobalSection(NestedProjects) = preSolution {82FB8450-B971-4E30-859F-4B2DDB81F590} = {465C4E39-FBA2-417A-AB31-BDC01601F420} - {D7756AF6-601D-40C2-97E9-940E5AFC2E08} = {465C4E39-FBA2-417A-AB31-BDC01601F420} - {C6FC723B-B4CD-475A-BBA7-4FE5CE82AC5A} = {465C4E39-FBA2-417A-AB31-BDC01601F420} {36F87D06-88B3-45E3-A2A8-0FC737B25428} = {465C4E39-FBA2-417A-AB31-BDC01601F420} {CC46E4E2-90DF-48F5-B3D7-83FAFED58F2A} = {465C4E39-FBA2-417A-AB31-BDC01601F420} EndGlobalSection @@ -78,8 +64,6 @@ Global GlobalSection(SharedMSBuildProjectFiles) = preSolution src\IFoxCAD.Cad.Shared\IFoxCAD.Cad.Shared.projitems*{36f87d06-88b3-45e3-a2a8-0fc737b25428}*SharedItemsImports = 5 src\IFoxCAD.Cad.Shared\IFoxCAD.Cad.Shared.projitems*{82fb8450-b971-4e30-859f-4b2ddb81f590}*SharedItemsImports = 13 - src\IFoxCAD.Cad.Shared\IFoxCAD.Cad.Shared.projitems*{c6fc723b-b4cd-475a-bba7-4fe5ce82ac5a}*SharedItemsImports = 5 src\IFoxCAD.Cad.Shared\IFoxCAD.Cad.Shared.projitems*{cc46e4e2-90df-48f5-b3d7-83fafed58f2a}*SharedItemsImports = 5 - src\IFoxCAD.Cad.Shared\IFoxCAD.Cad.Shared.projitems*{d7756af6-601d-40c2-97e9-940e5afc2e08}*SharedItemsImports = 5 EndGlobalSection EndGlobal diff --git a/src/IFoxCAD.Cad/IFoxCAD.Aacad08/IFoxCAD.Acad08.csproj b/src/IFoxCAD.Cad/IFoxCAD.Aacad08/IFoxCAD.Acad08.csproj index cf9665d..c3d35c1 100644 --- a/src/IFoxCAD.Cad/IFoxCAD.Aacad08/IFoxCAD.Acad08.csproj +++ b/src/IFoxCAD.Cad/IFoxCAD.Aacad08/IFoxCAD.Acad08.csproj @@ -25,7 +25,6 @@ - @@ -56,8 +55,8 @@ - True + diff --git a/src/IFoxCAD.Cad/IFoxCAD.Aacad09plus/IFoxCAD.Acad09plus.csproj b/src/IFoxCAD.Cad/IFoxCAD.Aacad09plus/IFoxCAD.Acad09plus.csproj index 7852e32..c55d4f6 100644 --- a/src/IFoxCAD.Cad/IFoxCAD.Aacad09plus/IFoxCAD.Acad09plus.csproj +++ b/src/IFoxCAD.Cad/IFoxCAD.Aacad09plus/IFoxCAD.Acad09plus.csproj @@ -26,31 +26,17 @@ - - runtime - - + + - - runtime - + - - runtime - - - 4.3.0 - + + - - - - DEBUG -- Gitee From 1a96f891cef69f655fe056a3e89e4224857c29d7 Mon Sep 17 00:00:00 2001 From: liuqihong <540762622@qq.com> Date: Wed, 17 Aug 2022 01:53:14 +0800 Subject: [PATCH 380/675] =?UTF-8?q?=E7=94=B1=E4=BA=8E=E4=BF=AE=E6=94=B9?= =?UTF-8?q?=E4=BA=86=E9=87=8D=E8=A6=81=E7=9A=84bug=E6=89=80=E4=BB=A5?= =?UTF-8?q?=E5=90=8C=E6=AD=A5=E4=B8=A4=E4=B8=AA=E5=88=86=E6=94=AF=E4=BB=A3?= =?UTF-8?q?=E7=A0=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- IFoxCAD.sln | 12 +- src/IFoxCAD.Basal/ArrayEx.cs | 81 +++--- src/IFoxCAD.Basal/CLS/RuntimeHelpers.cs | 7 +- src/IFoxCAD.Basal/DictEx.cs | 30 +- src/IFoxCAD.Basal/ListEx.cs | 10 +- src/IFoxCAD.Cad/ExtensionMethod/CurveEx.cs | 20 +- src/IFoxCAD.Cad/ExtensionMethod/DBObjectEx.cs | 65 ++--- src/IFoxCAD.Cad/ExtensionMethod/EditorEx.cs | 12 +- src/IFoxCAD.Cad/ExtensionMethod/ObjEx.cs | 2 +- src/IFoxCAD.Cad/ExtensionMethod/ObjectIdEx.cs | 2 +- .../ExtensionMethod/SymbolTableEx.cs | 164 +++++------ src/IFoxCAD.Cad/IFoxCAD.Cad.csproj | 82 +++--- src/IFoxCAD.Cad/Runtime/DBTrans.cs | 9 +- src/IFoxCAD.Cad/Runtime/Env.cs | 2 +- src/IFoxCAD.Cad/Runtime/SymbolTable.cs | 41 ++- src/IFoxCAD.Cad/SelectionFilter/OpFilter.cs | 29 +- tests/Test/CmdINI.cs | 22 +- tests/Test/Test.cs | 31 +-- tests/Test/TestAOP.cs | 6 +- tests/Test/TestCurve.cs | 239 ++++++++-------- tests/Test/TestDBTrans.cs | 4 +- tests/Test/TestJig.cs | 1 + tests/Test/TestLisp.cs | 213 +++++++------- tests/Test/TestLoop.cs | 50 ++-- tests/Test/TestMirrorFile.cs | 6 +- tests/Test/TestPoint.cs | 260 +++++++++--------- tests/Test/TestQuadTree.cs | 4 +- tests/Test/testConvexHull.cs | 144 +++++----- tests/Test/testFileDatabase.cs | 42 +-- tests/Test/testblock.cs | 18 +- tests/Test/testeditor.cs | 2 +- tests/Test/testid.cs | 119 ++++---- tests/Test/testselectfilter.cs | 53 ++-- 33 files changed, 851 insertions(+), 931 deletions(-) diff --git a/IFoxCAD.sln b/IFoxCAD.sln index a9e0664..e9e57c9 100644 --- a/IFoxCAD.sln +++ b/IFoxCAD.sln @@ -3,8 +3,6 @@ Microsoft Visual Studio Solution File, Format Version 12.00 # Visual Studio Version 17 VisualStudioVersion = 17.1.32113.165 MinimumVisualStudioVersion = 10.0.40219.1 -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "IFoxCAD.Cad", "src\IFoxCAD.Cad\IFoxCAD.Cad.csproj", "{D5FAED7C-A99B-4BEE-A745-45442DC44971}" -EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Test", "tests\Test\Test.csproj", "{B1602568-F023-46C7-B635-3CB281F1EC00}" EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "IFoxCAD.WPF", "src\IFoxCAD.WPF\IFoxCAD.WPF.csproj", "{D820D629-1AB3-4BE3-A772-CB753D98CCB8}" @@ -13,16 +11,14 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "IFoxCAD.Basal", "src\IFoxCA EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "TestConsole", "tests\TestConsole\TestConsole.csproj", "{E2873F0B-CAD2-45E8-8FF0-C05C0C6AD955}" EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "IFoxCAD.Cad", "src\IFoxCAD.Cad\IFoxCAD.Cad.csproj", "{D5FAED7C-A99B-4BEE-A745-45442DC44971}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU Release|Any CPU = Release|Any CPU EndGlobalSection GlobalSection(ProjectConfigurationPlatforms) = postSolution - {D5FAED7C-A99B-4BEE-A745-45442DC44971}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {D5FAED7C-A99B-4BEE-A745-45442DC44971}.Debug|Any CPU.Build.0 = Debug|Any CPU - {D5FAED7C-A99B-4BEE-A745-45442DC44971}.Release|Any CPU.ActiveCfg = Release|Any CPU - {D5FAED7C-A99B-4BEE-A745-45442DC44971}.Release|Any CPU.Build.0 = Release|Any CPU {B1602568-F023-46C7-B635-3CB281F1EC00}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {B1602568-F023-46C7-B635-3CB281F1EC00}.Debug|Any CPU.Build.0 = Debug|Any CPU {B1602568-F023-46C7-B635-3CB281F1EC00}.Release|Any CPU.ActiveCfg = Release|Any CPU @@ -39,6 +35,10 @@ Global {E2873F0B-CAD2-45E8-8FF0-C05C0C6AD955}.Debug|Any CPU.Build.0 = Debug|Any CPU {E2873F0B-CAD2-45E8-8FF0-C05C0C6AD955}.Release|Any CPU.ActiveCfg = Release|Any CPU {E2873F0B-CAD2-45E8-8FF0-C05C0C6AD955}.Release|Any CPU.Build.0 = Release|Any CPU + {D5FAED7C-A99B-4BEE-A745-45442DC44971}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {D5FAED7C-A99B-4BEE-A745-45442DC44971}.Debug|Any CPU.Build.0 = Debug|Any CPU + {D5FAED7C-A99B-4BEE-A745-45442DC44971}.Release|Any CPU.ActiveCfg = Release|Any CPU + {D5FAED7C-A99B-4BEE-A745-45442DC44971}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE diff --git a/src/IFoxCAD.Basal/ArrayEx.cs b/src/IFoxCAD.Basal/ArrayEx.cs index 34fb4b7..2ac37fa 100644 --- a/src/IFoxCAD.Basal/ArrayEx.cs +++ b/src/IFoxCAD.Basal/ArrayEx.cs @@ -1,50 +1,49 @@ -namespace IFoxCAD.Basal +namespace IFoxCAD.Basal; + +/* + * 由于linq的函数大部分带有状态机,而cad是一个单机程序, + * 使用状态机会变得缓慢,因此我们设计的时候着重于时间优化, + * 本工具类在着重于数组遍历时候替代linq + */ +public static class ArrayEx { - /* - * 由于linq的函数大部分带有状态机,而cad是一个单机程序, - * 使用状态机会变得缓慢,因此我们设计的时候着重于时间优化, - * 本工具类在着重于数组遍历时候替代linq - */ - public static class ArrayEx + /// + /// 合并数组 + /// + /// + /// + public static T[] Combine2(this T[] a, T[] b) { - /// - /// 合并数组 - /// - /// - /// - public static T[] Combine2(this T[] a, T[] b) - { - var c = new T[a.Length + b.Length]; - Array.Copy(a, 0, c, 0, a.Length); - Array.Copy(b, 0, c, a.Length, b.Length); - return c; - } + var c = new T[a.Length + b.Length]; + Array.Copy(a, 0, c, 0, a.Length); + Array.Copy(b, 0, c, a.Length, b.Length); + return c; + } - /// - /// 一维数组消重,此函数建议更改为: - /// set = new(); - /// foreach (var item in listInOut) - /// set.Add(item); - /// ]]> - /// - /// - /// 传入有重复成员的数组,传出没有重复的 - /// 传出参数1:数组开头;传出参数2:数组结尾;返回值比较结尾为就移除 - [Obsolete] - public static void Deduplication(List listInOut, Func func) + /// + /// 一维数组消重,此函数建议更改为: + /// set = new(); + /// foreach (var item in listInOut) + /// set.Add(item); + /// ]]> + /// + /// + /// 传入有重复成员的数组,传出没有重复的 + /// 传出参数1:数组开头;传出参数2:数组结尾;返回值比较结尾为就移除 + [Obsolete] + public static void Deduplication(List listInOut, Func func) + { + for (int i = 0; i < listInOut.Count; i++) { - for (int i = 0; i < listInOut.Count; i++) + var first = listInOut[i]; + for (int j = listInOut.Count - 1; j > i; j--) { - var first = listInOut[i]; - for (int j = listInOut.Count - 1; j > i; j--) - { - var last = listInOut[j]; - if (func(first, last)) - listInOut.RemoveAt(j); - } + var last = listInOut[j]; + if (func(first, last)) + listInOut.RemoveAt(j); } } - } + } diff --git a/src/IFoxCAD.Basal/CLS/RuntimeHelpers.cs b/src/IFoxCAD.Basal/CLS/RuntimeHelpers.cs index a446c59..1db16f1 100644 --- a/src/IFoxCAD.Basal/CLS/RuntimeHelpers.cs +++ b/src/IFoxCAD.Basal/CLS/RuntimeHelpers.cs @@ -5,7 +5,10 @@ namespace System.Runtime.CompilerServices; +//编译提示多个程序集中定义,屏蔽不了,但是不影响编译 +//#pragma warning disable CS1685 // 类型与导入类型冲突 public static class RuntimeHelpers +//#pragma warning restore CS1685 // 类型与导入类型冲突 { /// /// Slices the specified array using the specified range. @@ -38,7 +41,5 @@ public static T[] GetSubArray(T[] array, Range range) return dest; } } - - } -//#endif \ No newline at end of file +//#endif diff --git a/src/IFoxCAD.Basal/DictEx.cs b/src/IFoxCAD.Basal/DictEx.cs index 6fbccc4..7ff153f 100644 --- a/src/IFoxCAD.Basal/DictEx.cs +++ b/src/IFoxCAD.Basal/DictEx.cs @@ -1,21 +1,15 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; +namespace IFoxCAD.Basal; -namespace IFoxCAD.Basal +public static class DictEx { - public static class DictEx - { - //public static TKey? GetKey(this IDictionary dict!!, TKey key!!) - //{ - // if (dict.ContainsKey(key)) - // { - // foreach (var item in dict.Keys) - // if (key.Equals(item)) - // return item; - // } - // return default; - //} - } + //public static TKey? GetKey(this IDictionary dict!!, TKey key!!) + //{ + // if (dict.ContainsKey(key)) + // { + // foreach (var item in dict.Keys) + // if (key.Equals(item)) + // return item; + // } + // return default; + //} } diff --git a/src/IFoxCAD.Basal/ListEx.cs b/src/IFoxCAD.Basal/ListEx.cs index 4ead97b..528b6ad 100644 --- a/src/IFoxCAD.Basal/ListEx.cs +++ b/src/IFoxCAD.Basal/ListEx.cs @@ -1,5 +1,5 @@ - -namespace IFoxCAD.Basal; +namespace IFoxCAD.Basal; + public static class ListEx { @@ -12,9 +12,9 @@ public static bool EqualsAll(this IList a, IList b) public static bool EqualsAll(this IList a, IList b, IEqualityComparer? comparer) { - if (a == null) - return b == null; - else if (b == null) + if (a is null) + return b is null; + else if (b is null) return false; if (a.Count != b.Count) diff --git a/src/IFoxCAD.Cad/ExtensionMethod/CurveEx.cs b/src/IFoxCAD.Cad/ExtensionMethod/CurveEx.cs index a57c153..4119020 100644 --- a/src/IFoxCAD.Cad/ExtensionMethod/CurveEx.cs +++ b/src/IFoxCAD.Cad/ExtensionMethod/CurveEx.cs @@ -27,11 +27,13 @@ public static double GetLength(this Curve curve) /// /// /// 打断后曲线的集合 - public static IEnumerable GetSplitCurves(this Curve curve, - IEnumerable pars, - Func, - IEnumerable>? func = null) + public static IEnumerable GetSplitCurves(this Curve curve, + IEnumerable pars, + Func, IEnumerable>? func = null) { + if (pars == null) + throw new ArgumentNullException(nameof(pars)); + if (func != null) pars = func.Invoke(pars); return @@ -39,7 +41,7 @@ public static IEnumerable GetSplitCurves(this Curve curve, .GetSplitCurves(new DoubleCollection(pars.ToArray())) .Cast(); } - + /// /// 获取分割曲线集合 /// @@ -59,7 +61,7 @@ public static IEnumerable GetSplitCurves(this Curve curve, IEnumerable x).ToArray() : pars.ToArray())) .Cast(); } - + /// /// 获取分割曲线集合 /// @@ -73,7 +75,7 @@ public static IEnumerable GetSplitCurves(this Curve curve, IEnumerable(); } - + /// /// 获取分割曲线集合 /// @@ -150,7 +152,7 @@ public static List BreakCurve(this List curves) paramss.Add(new List()); } } - + //var oldCurves = new List(); var newCurves = new List(); var cci3d = new CurveCurveIntersector3d(); @@ -715,4 +717,4 @@ public static void ChamferAt(this Polyline polyline, int index, double radius, b #endregion Polyline #endregion Curve -} \ No newline at end of file +} diff --git a/src/IFoxCAD.Cad/ExtensionMethod/DBObjectEx.cs b/src/IFoxCAD.Cad/ExtensionMethod/DBObjectEx.cs index 07bba27..8ebee46 100644 --- a/src/IFoxCAD.Cad/ExtensionMethod/DBObjectEx.cs +++ b/src/IFoxCAD.Cad/ExtensionMethod/DBObjectEx.cs @@ -14,34 +14,26 @@ public static class DBObjectEx /// 要删除数据的组码 public static void RemoveXData(this DBObject obj, string appName, DxfCode dxfCode) { + if (obj.XData == null) + return; XDataList data = obj.XData; - var indexlst = new List(); - bool flag = false; + bool appNameIdentical = false; for (int i = 0; i < data.Count; i++) { - if (data[i].TypeCode == (int)DxfCode.ExtendedDataRegAppName && data[i].Value.ToString() == appName) + if (data[i].TypeCode == (int)DxfCode.ExtendedDataRegAppName) { - flag = true; - } - if (flag) - { - if (data[i].TypeCode == (int)DxfCode.ExtendedDataRegAppName && data[i].Value.ToString() != appName) - break; - if (data[i].TypeCode == (int)dxfCode) - { - indexlst.Add(i); - } + appNameIdentical = data[i].Value.ToString() == appName; + break; } } - foreach (var item in indexlst) - { - data.RemoveAt(item); - } + if (!appNameIdentical) + return; + for (int i = data.Count - 1; i >= 0; i--) + if (data[i].TypeCode == (int)dxfCode) + data.RemoveAt(i); using (obj.ForWrite()) - { obj.XData = data; - } } /// /// 修改扩展数据 @@ -52,29 +44,26 @@ public static void RemoveXData(this DBObject obj, string appName, DxfCode dxfCod /// 新的数据 public static void ChangeXData(this DBObject obj, string appName, DxfCode dxfCode, object newvalue) { + if (obj.XData == null) + return; XDataList data = obj.XData; - bool flag = false; + bool appNameIdentical = false; for (int i = 0; i < data.Count; i++) { - if (data[i].TypeCode == (int)DxfCode.ExtendedDataRegAppName && data[i].Value.ToString() == appName) - { - flag = true; - } - if (flag) + if (data[i].TypeCode == (int)DxfCode.ExtendedDataRegAppName) { - if (data[i].TypeCode == (int)DxfCode.ExtendedDataRegAppName && data[i].Value.ToString() != appName) - break; - if (data[i].TypeCode == (int)dxfCode) - { - data[i] = new TypedValue((int)dxfCode, newvalue); - } + appNameIdentical = data[i].Value.ToString() == appName; + break; } } + if (!appNameIdentical) + return; + for (int i = data.Count - 1; i >= 0; i--) + if (data[i].TypeCode == (int)dxfCode) + data[i] = new TypedValue((int)dxfCode, newvalue); using (obj.ForWrite()) - { obj.XData = data; - } } #endregion @@ -91,22 +80,16 @@ public static void ForWrite(this T obj, Action action) where T : DBObject var _isNotifyEnabled = obj.IsNotifyEnabled; var _isWriteEnabled = obj.IsWriteEnabled; if (_isNotifyEnabled) - { obj.UpgradeFromNotify(); - } else if (!_isWriteEnabled) - { obj.UpgradeOpen(); - } + action?.Invoke(obj); + if (_isNotifyEnabled) - { obj.DowngradeToNotify(_isWriteEnabled); - } else if (!_isWriteEnabled) - { obj.DowngradeOpen(); - } } /// diff --git a/src/IFoxCAD.Cad/ExtensionMethod/EditorEx.cs b/src/IFoxCAD.Cad/ExtensionMethod/EditorEx.cs index 47f06f0..9528cec 100644 --- a/src/IFoxCAD.Cad/ExtensionMethod/EditorEx.cs +++ b/src/IFoxCAD.Cad/ExtensionMethod/EditorEx.cs @@ -443,7 +443,7 @@ public static void WriteMessage(string message) try { if (Acceptable()) - Application.DocumentManager.MdiActiveDocument.Editor.WriteMessage("\n" + message); + Acap.DocumentManager.MdiActiveDocument.Editor.WriteMessage("\n" + message); else return; } @@ -469,9 +469,9 @@ public static void WriteMessage(string format, params object[] args) /// 有,没有 public static bool HasEditor() { - return Application.DocumentManager.MdiActiveDocument is not null - && Application.DocumentManager.Count != 0 - && Application.DocumentManager.MdiActiveDocument.Editor is not null; + return Acap.DocumentManager.MdiActiveDocument is not null + && Acap.DocumentManager.Count != 0 + && Acap.DocumentManager.MdiActiveDocument.Editor is not null; } /// @@ -481,7 +481,7 @@ public static bool HasEditor() public static bool Acceptable() { return HasEditor() - && !Application.DocumentManager.MdiActiveDocument.Editor.IsDragging; + && !Acap.DocumentManager.MdiActiveDocument.Editor.IsDragging; } #endregion Info @@ -1081,7 +1081,7 @@ public enum RunLispFlag : byte } if ((flag & RunLispFlag.SendStringToExecute) == RunLispFlag.SendStringToExecute) { - var dm = Application.DocumentManager; + var dm = Acap.DocumentManager; var doc = dm.MdiActiveDocument; doc.SendStringToExecute(lispCode + "\n", false, false, false); } diff --git a/src/IFoxCAD.Cad/ExtensionMethod/ObjEx.cs b/src/IFoxCAD.Cad/ExtensionMethod/ObjEx.cs index c831959..1c90814 100644 --- a/src/IFoxCAD.Cad/ExtensionMethod/ObjEx.cs +++ b/src/IFoxCAD.Cad/ExtensionMethod/ObjEx.cs @@ -8,7 +8,7 @@ public static class ObjEx /// public static void Print(this object obj) { - Application.DocumentManager.MdiActiveDocument.Editor.WriteMessage($"{obj}\n"); + Acap.DocumentManager.MdiActiveDocument.Editor.WriteMessage($"{obj}\n"); } /// /// 系统的打印 diff --git a/src/IFoxCAD.Cad/ExtensionMethod/ObjectIdEx.cs b/src/IFoxCAD.Cad/ExtensionMethod/ObjectIdEx.cs index 52c9261..e147d6c 100644 --- a/src/IFoxCAD.Cad/ExtensionMethod/ObjectIdEx.cs +++ b/src/IFoxCAD.Cad/ExtensionMethod/ObjectIdEx.cs @@ -49,7 +49,7 @@ public static IEnumerable OfType(this IEnumerable ids) wh return ids.Where(id => id.ObjectClass().DxfName == dxfName); } #endregion GetObject - + //Acad08缺少 id.ObjectClass 如何补偿? public static RXClass ObjectClass(this ObjectId id) { diff --git a/src/IFoxCAD.Cad/ExtensionMethod/SymbolTableEx.cs b/src/IFoxCAD.Cad/ExtensionMethod/SymbolTableEx.cs index 3c0ace3..a390436 100644 --- a/src/IFoxCAD.Cad/ExtensionMethod/SymbolTableEx.cs +++ b/src/IFoxCAD.Cad/ExtensionMethod/SymbolTableEx.cs @@ -38,19 +38,13 @@ public static ObjectId Add(this SymbolTable table, /// 新图层名 public static ObjectId Rename(this SymbolTable table, string Oldname, string NewName) { - if (table.Has(Oldname)) - { - table.Change(Oldname, ly => - { - ly.Name = NewName; - } - ); - return table[NewName]; - } - else - { + if (!table.Has(Oldname)) return ObjectId.Null; - } + + table.Change(Oldname, layer => { + layer.Name = NewName; + }); + return table[NewName]; } /// /// 删除图层 @@ -61,24 +55,18 @@ public static ObjectId Rename(this SymbolTable tab public static bool Delete(this SymbolTable table, string name) { if (name == "0" || name == "Defpoints" || !table.Has(name) || table[name] == DBTrans.Top.Database.Clayer) - { return false; - } + table.CurrentSymbolTable.GenerateUsageData(); var ltr = table.GetRecord(name); - if (ltr is not null) - { - if (ltr.IsUsed) - { - return false; - } - using (ltr.ForWrite()) - { - ltr.Erase(); - } - return true; - } - return false; + if (ltr is null) + return false; + + if (ltr.IsUsed) + return false; + using (ltr.ForWrite()) + ltr.Erase(); + return true; } #endregion @@ -93,22 +81,22 @@ public static bool Delete(this SymbolTable table, /// 添加属性定义的委托 /// 块定义id /// TODO: 需要测试匿名块等特殊的块是否能定义 - public static ObjectId Add(this SymbolTable table, string name, Action? action = null, Func>? ents = null, Func>? attdef = null) + public static ObjectId Add(this SymbolTable table, + string name, + Action? action = null, + Func>? ents = null, + Func>? attdef = null) { - return table.Add(name, btr => - { + return table.Add(name, btr => { action?.Invoke(btr); + var entsres = ents?.Invoke(); if (entsres is not null) - { btr.AddEntity(entsres); - } + var adddefres = attdef?.Invoke(); if (adddefres is not null) - { btr.AddEntity(adddefres); - } - }); } /// @@ -119,18 +107,16 @@ public static ObjectId Add(this SymbolTable table, /// 图元 /// 属性定义 /// - public static ObjectId Add(this SymbolTable table, string name, IEnumerable? ents = null, IEnumerable? attdef = null) + public static ObjectId Add(this SymbolTable table, + string name, + IEnumerable? ents = null, + IEnumerable? attdef = null) { - return table.Add(name, btr => - { + return table.Add(name, btr => { if (ents is not null) - { btr.AddEntity(ents); - } if (attdef is not null) - { btr.AddEntity(attdef); - } }); } @@ -152,20 +138,19 @@ public static ObjectId Add(this SymbolTable table, /// 块表 /// 块定义id /// 属性列表 - public static void AddAttsToBlocks(this SymbolTable table, ObjectId id, List atts) + public static void AddAttsToBlocks(this SymbolTable table, + ObjectId id, + List atts) { - table.Change(id, btr => - { - var attTags = new List(); + List attTags = new(); + table.Change(id, btr => { + btr.GetEntities() .ForEach(def => attTags.Add(def.Tag.ToUpper())); - foreach (AttributeDefinition att in atts) - { - if (!attTags.Contains(att.Tag.ToUpper())) - { - btr.AddEntity(att); - } - } + + for (int i = 0; i < atts.Count; i++) + if (!attTags.Contains(atts[i].Tag.ToUpper())) + btr.AddEntity(atts[i]); }); } /// @@ -174,20 +159,19 @@ public static void AddAttsToBlocks(this SymbolTable块表 /// 块定义名字 /// 属性列表 - public static void AddAttsToBlocks(this SymbolTable table, string name, List atts) + public static void AddAttsToBlocks(this SymbolTable table, + string name, + List atts) { - table.Change(name, btr => - { - var attTags = new List(); + List attTags = new(); + table.Change(name, btr => { + btr.GetEntities() .ForEach(def => attTags.Add(def.Tag.ToUpper())); - foreach (AttributeDefinition att in atts) - { - if (!attTags.Contains(att.Tag.ToUpper())) - { - btr.AddEntity(att); - } - } + + for (int i = 0; i < atts.Count; i++) + if (!attTags.Contains(atts[i].Tag.ToUpper())) + btr.AddEntity(atts[i]); }); } @@ -203,11 +187,10 @@ public static ObjectId GetBlockFrom(this SymbolTable块定义名 /// 是否覆盖 /// 块定义Id - public static ObjectId GetBlockFrom(this SymbolTable table, string fileName, string blockName, bool over) + public static ObjectId GetBlockFrom(this SymbolTable table, + string fileName, + string blockName, + bool over) { - return - table.GetRecordFrom( - t => t.BlockTable, - fileName, - blockName, - over); + return table.GetRecordFrom(t => t.BlockTable, fileName, blockName, over); } #endregion @@ -258,20 +239,19 @@ public static ObjectId Add(this SymbolTable { return table.Add( name, - ltt => - { + ltt => { ltt.AsciiDescription = description; ltt.PatternLength = length; //线型的总长度 - ltt.NumDashes = dash.Length; //组成线型的笔画数目 - for (int i = 0; i < dash.Length; i++) + ltt.NumDashes = dash.Length; //组成线型的笔画数目 + for (int i = 0; i < dash.Length; i++) { ltt.SetDashLengthAt(i, dash[i]); } - //ltt.SetDashLengthAt(0, 0.5); //0.5个单位的划线 - //ltt.SetDashLengthAt(1, -0.25); //0.25个单位的空格 - //ltt.SetDashLengthAt(2, 0); // 一个点 - //ltt.SetDashLengthAt(3, -0.25); //0.25个单位的空格 - } + //ltt.SetDashLengthAt(0, 0.5); //0.5个单位的划线 + //ltt.SetDashLengthAt(1, -0.25); //0.25个单位的空格 + //ltt.SetDashLengthAt(2, 0); // 一个点 + //ltt.SetDashLengthAt(3, -0.25); //0.25个单位的空格 + } ); } #endregion @@ -293,8 +273,7 @@ public static ObjectId Add(this SymbolTable - { + tstr => { tstr.Name = textStyleName; tstr.FileName = font; tstr.XScale = xscale; @@ -308,7 +287,10 @@ public static ObjectId Add(this SymbolTable字体名枚举 /// 宽度比例 /// 文字样式Id - public static ObjectId Add(this SymbolTable table, string textStyleName, FontTTF fontTTF, double xscale = 1.0) + public static ObjectId Add(this SymbolTable table, + string textStyleName, + FontTTF fontTTF, + double xscale = 1.0) { return table.Add(textStyleName, fontTTF.GetDesc(), xscale); } @@ -335,20 +317,16 @@ public static ObjectId AddWithChange(this SymbolTable - { + table.Change(textStyleName, ttr => { ttr.FileName = smallFont; ttr.XScale = xScale; ttr.TextSize = height; - if (bigFont != "") - { + if (bigFont != string.Empty) ttr.BigFontFileName = bigFont; - } }); return table[textStyleName]; } - return table.Add(textStyleName, ttr => - { + return table.Add(textStyleName, ttr => { ttr.FileName = smallFont; ttr.XScale = xScale; ttr.TextSize = height; diff --git a/src/IFoxCAD.Cad/IFoxCAD.Cad.csproj b/src/IFoxCAD.Cad/IFoxCAD.Cad.csproj index 07fad40..9207184 100644 --- a/src/IFoxCAD.Cad/IFoxCAD.Cad.csproj +++ b/src/IFoxCAD.Cad/IFoxCAD.Cad.csproj @@ -1,14 +1,13 @@ - net35;net40;net45 true - enable - true - true + enable + true + true 0.3.7 InspireFunction - xsfhlzh;vicwjb + xsfhlzh;vicwjb;liuqihong 基于.NET的Cad二次开发类库 InspireFunction https://gitee.com/inspirefunction/ifoxcad @@ -24,56 +23,43 @@ true x64 - - - - runtime - - - - - runtime - - + + + - - - runtime - - - - - + + + - - DEBUG - - - $(Configuration);ac2009 - - - $(Configuration);ac2013 - - - $(Configuration);ac2015 - - + + + + + + DEBUG + + + $(Configuration);ac2009 + + + $(Configuration);ac2013 + + + $(Configuration);ac2015 + - - True - - + + True + + - + - + - + - - + \ No newline at end of file diff --git a/src/IFoxCAD.Cad/Runtime/DBTrans.cs b/src/IFoxCAD.Cad/Runtime/DBTrans.cs index d207edf..3785034 100644 --- a/src/IFoxCAD.Cad/Runtime/DBTrans.cs +++ b/src/IFoxCAD.Cad/Runtime/DBTrans.cs @@ -87,7 +87,7 @@ public static DBTrans Top /// 是否锁文档 public DBTrans(Document? doc = null, bool commit = true, bool doclock = false) { - Document = doc ?? Application.DocumentManager.MdiActiveDocument; + Document = doc ?? Acap.DocumentManager.MdiActiveDocument; Database = Document.Database; Editor = Document.Editor; Transaction = Database.TransactionManager.StartTransaction(); @@ -122,9 +122,7 @@ public DBTrans(Database database, bool commit = true) /// 密码 public DBTrans(string fileName, bool commit = true, -#pragma warning disable CS0436 // 类型与导入类型冲突 FileOpenMode openMode = FileOpenMode.OpenForReadAndWriteNoShare, -#pragma warning restore CS0436 // 类型与导入类型冲突 string? password = null) { if (string.IsNullOrEmpty(fileName?.Trim())) @@ -337,9 +335,6 @@ public ObjectId GetObjectId(string handleString) //return Database.GetObjectId(false, hanle, 0); return Helper.TryGetObjectId(Database, hanle); } - - - #endregion #region 保存文件 @@ -350,7 +345,7 @@ public ObjectId GetObjectId(string handleString) public void SaveDwgFile(DwgVersion version = DwgVersion.AC1800) { bool flag = true; - foreach (Document doc in Application.DocumentManager) + foreach (Document doc in Acap.DocumentManager) { // 前台开图,使用命令保存 if (doc.Database.Filename == this.Database.Filename) diff --git a/src/IFoxCAD.Cad/Runtime/Env.cs b/src/IFoxCAD.Cad/Runtime/Env.cs index 379504b..12a9738 100644 --- a/src/IFoxCAD.Cad/Runtime/Env.cs +++ b/src/IFoxCAD.Cad/Runtime/Env.cs @@ -18,7 +18,7 @@ public static class Env /// /// 当前文档 /// - public static Document Document => Application.DocumentManager.MdiActiveDocument; + public static Document Document => Acap.DocumentManager.MdiActiveDocument; /// /// 编辑器对象 diff --git a/src/IFoxCAD.Cad/Runtime/SymbolTable.cs b/src/IFoxCAD.Cad/Runtime/SymbolTable.cs index 434625e..11e1013 100644 --- a/src/IFoxCAD.Cad/Runtime/SymbolTable.cs +++ b/src/IFoxCAD.Cad/Runtime/SymbolTable.cs @@ -1,5 +1,20 @@ namespace IFoxCAD.Cad; +/// +/// 委托执行状态 +/// +public enum DelegateState +{ + /// + /// 继续执行 + /// + Go, + /// + /// 中断执行 + /// + Break +} + public class SymbolTable : IEnumerable where TTable : SymbolTable where TRecord : SymbolTableRecord, new() @@ -158,12 +173,11 @@ public void Remove(ObjectId id) private static void Change(TRecord record, Action action) { using (record.ForWrite()) - { action.Invoke(record); - } // 调用regen()函数可能会导致卡顿 //Env.Editor.Regen(); } + /// /// 修改符号表 /// @@ -173,10 +187,9 @@ public void Change(string name, Action action) { var record = GetRecord(name); if (record is not null) - { Change(record, action); - } } + /// /// 修改符号表 /// @@ -186,9 +199,7 @@ public void Change(ObjectId id, Action action) { var record = GetRecord(id); if (record is not null) - { Change(record, action); - } } #endregion @@ -219,9 +230,7 @@ public IEnumerable GetRecords() { var record = GetRecord(item); if (record is not null) - { yield return record; - } } } @@ -241,9 +250,7 @@ public IEnumerable GetRecordNames(Func filter) { var record = GetRecord(item); if (record is not null && filter.Invoke(record)) - { yield return record.Name; - } } } @@ -257,9 +264,7 @@ public IEnumerable GetRecordNames(Func filter) public ObjectId GetRecordFrom(SymbolTable table, string name, bool over) { if (table is null) - { throw new ArgumentNullException(nameof(table), "对象为null"); - } ObjectId rid = this[name]; bool has = rid != ObjectId.Null; @@ -269,7 +274,12 @@ public ObjectId GetRecordFrom(SymbolTable table, string name, b using IdMapping idm = new(); using (ObjectIdCollection ids = new() { id }) { - table.Database.WblockCloneObjects(ids, CurrentSymbolTable.Id, idm, DuplicateRecordCloning.Replace, false); + table.Database. + WblockCloneObjects(ids, + CurrentSymbolTable.Id, + idm, + DuplicateRecordCloning.Replace, + false); } rid = idm[id].Value; } @@ -284,7 +294,10 @@ public ObjectId GetRecordFrom(SymbolTable table, string name, b /// 符号表记录名 /// 是否覆盖, 为覆盖, 为不覆盖 /// 对象id - internal ObjectId GetRecordFrom(Func> tableSelector, string fileName, string name, bool over) + internal ObjectId GetRecordFrom(Func> tableSelector, + string fileName, + string name, + bool over) { using var tr = new DBTrans(fileName); return GetRecordFrom(tableSelector(tr), name, over); diff --git a/src/IFoxCAD.Cad/SelectionFilter/OpFilter.cs b/src/IFoxCAD.Cad/SelectionFilter/OpFilter.cs index a698aeb..48843f4 100644 --- a/src/IFoxCAD.Cad/SelectionFilter/OpFilter.cs +++ b/src/IFoxCAD.Cad/SelectionFilter/OpFilter.cs @@ -10,6 +10,11 @@ public abstract class OpFilter /// public abstract string Name { get; } + /// + /// 只读属性,表示这个过滤器取反 + /// + public OpFilter Not => new OpNot(this); + /// /// 获取TypedValue类型的值的迭代器的抽象方法,子类必须重写 /// @@ -26,14 +31,6 @@ public abstract class OpFilter return item.Not; } - /// - /// 只读属性,表示这个过滤器取反 - /// - public OpFilter Not - { - get { return new OpNot(this); } - } - /// /// 过滤器值转换为 TypedValue 类型数组 /// @@ -61,10 +58,10 @@ public static implicit operator SelectionFilter(OpFilter item) /// 字符串 public override string ToString() { - string s = ""; + var sb = new StringBuilder(); foreach (var value in GetValues()) - s += value.ToString(); - return s; + sb.Append(value); + return sb.ToString(); } /// @@ -76,12 +73,12 @@ public override string ToString() /// !(e.Dxf(0) == "line" & e.Dxf(8) == "0") /// | e.Dxf(0) != "circle" & e.Dxf(8) == "2" & e.Dxf(10) >= p); /// /// 例子2: - /// var f2 = OpFilter.Bulid( + /// var f2 = OpFilter.Build( /// e => e.Or( /// !e.And(e.Dxf(0) == "line", e.Dxf(8) == "0"), /// e.And(e.Dxf(0) != "circle", e.Dxf(8) == "2", @@ -90,7 +87,7 @@ public override string ToString() /// /// 构建过滤器的函数委托 /// 过滤器 - public static OpFilter Bulid(Func func) + public static OpFilter Build(Func func) { return func(new Op()).Filter!; } @@ -107,9 +104,7 @@ public class Op /// internal OpFilter? Filter { get; private set; } - internal Op() - { - } + internal Op() { } private Op(OpFilter filter) { diff --git a/tests/Test/CmdINI.cs b/tests/Test/CmdINI.cs index 2e37c1d..7a882ec 100644 --- a/tests/Test/CmdINI.cs +++ b/tests/Test/CmdINI.cs @@ -1,4 +1,6 @@ -/// +namespace Test; + +/// /// 注册中心(自动执行接口): /// 用于启动cad后写入启动注册表及反射调用以下特性和接口 /// netload的工程必须继承虚函数后才能使用特性和接口 @@ -12,7 +14,7 @@ public class CmdINI : AutoRegAssem { public CmdINI() : base(AutoRegConfig.All) { - var dm = Application.DocumentManager; + var dm = Acap.DocumentManager; var doc = dm.MdiActiveDocument; var ed = doc.Editor; ed.WriteMessage($"\n {nameof(CmdINI)}构造函数,开始自动执行\r\n"); @@ -26,7 +28,7 @@ public void IFoxAddReg() { base.RegApp(); - var dm = Application.DocumentManager; + var dm = Acap.DocumentManager; var doc = dm.MdiActiveDocument; var ed = doc.Editor; ed.WriteMessage($"\n 加入注册表"); @@ -41,7 +43,7 @@ public void IFoxRemoveReg() //执行命令的时候会再次执行构造函数(导致初始化两次),但是再次执行就不会了 base.UnRegApp(); - var dm = Application.DocumentManager; + var dm = Acap.DocumentManager; var doc = dm.MdiActiveDocument; var ed = doc.Editor; ed.WriteMessage($"\n 卸载注册表"); @@ -57,7 +59,7 @@ public class Cmd_IFoxInitialize [IFoxInitialize] public void NameCasual() { - var dm = Application.DocumentManager; + var dm = Acap.DocumentManager; var doc = dm.MdiActiveDocument; var ed = doc.Editor; ed.WriteMessage("\n 开始自动执行 可以分开多个类和多个函数 \r\n"); @@ -66,7 +68,7 @@ public void NameCasual() [IFoxInitialize] public void NameCasualtest() { - var dm = Application.DocumentManager; + var dm = Acap.DocumentManager; var doc = dm.MdiActiveDocument; var ed = doc.Editor; ed.WriteMessage("\n 开始自动执行 又一次测试 \r\n"); @@ -75,7 +77,7 @@ public void NameCasualtest() [IFoxInitialize] public void Initialize() { - var dm = Application.DocumentManager; + var dm = Acap.DocumentManager; var doc = dm.MdiActiveDocument; var ed = doc.Editor; ed.WriteMessage("\n 开始自动执行 Initialize \r\n"); @@ -86,7 +88,7 @@ public void Terminate() { //try //{ - // var dm = Application.DocumentManager; + // var dm = Acap.DocumentManager; // var doc = dm.MdiActiveDocument; // var ed = doc.Editor; //注意此时编辑器已经回收,所以此句没用,并引发错误 // ed.WriteMessage("\n 结束自动执行 Terminate \r\n"); @@ -100,12 +102,12 @@ public void Terminate() //public void Initialize() //{ // //文档管理器将比此接口前创建,因此此句会执行 - // Application.DocumentManager.MdiActiveDocument.Editor.WriteMessage("\nload...."); + // Acap.DocumentManager.MdiActiveDocument.Editor.WriteMessage("\nload...."); //} //[IFoxInitialize(Sequence.First, false)] //public void Terminate() //{ // //文档管理器将比此接口前死亡,因此此句不会执行 - // Application.DocumentManager.MdiActiveDocument?.Editor.WriteMessage("\nunload...."); + // Acap.DocumentManager.MdiActiveDocument?.Editor.WriteMessage("\nunload...."); //} } \ No newline at end of file diff --git a/tests/Test/Test.cs b/tests/Test/Test.cs index bc45757..8cb7f78 100644 --- a/tests/Test/Test.cs +++ b/tests/Test/Test.cs @@ -1,5 +1,5 @@ -#pragma warning disable CS0219 // 变量已被赋值,但从未使用过它的值 -namespace Test; +namespace Test; +#pragma warning disable CS0219 // 变量已被赋值,但从未使用过它的值 public class Test { @@ -219,7 +219,7 @@ public void AddXdata() [CommandMethod("getxdata")] public void GetXdata() { - var doc = Application.DocumentManager.MdiActiveDocument; + var doc = Acap.DocumentManager.MdiActiveDocument; var ed = doc.Editor; using var tr = new DBTrans(); tr.RegAppTable.ForEach(id => @@ -241,7 +241,7 @@ public void GetXdata() [CommandMethod("changexdata")] public void Changexdata() { - var doc = Application.DocumentManager.MdiActiveDocument; + var doc = Acap.DocumentManager.MdiActiveDocument; var ed = doc.Editor; var appname = "myapp"; var res = ed.GetEntity("\n select the entity:"); @@ -257,7 +257,7 @@ public void Changexdata() [CommandMethod("removexdata")] public void Removexdata() { - var doc = Application.DocumentManager.MdiActiveDocument; + var doc = Acap.DocumentManager.MdiActiveDocument; var ed = doc.Editor; var appname = "myapp"; var res = ed.GetEntity("\n select the entity:"); @@ -279,7 +279,11 @@ public void PrintLayerName() { tr.Editor.WriteMessage(layerRecord.Name); } - + foreach (var layerRecord in tr.LayerTable.GetRecords()) + { + tr.Editor.WriteMessage(layerRecord.Name); + break; + } } @@ -332,29 +336,18 @@ public void TestRec() } }); - - } - - - - - public Database Getdb() { - var db = Application.DocumentManager.MdiActiveDocument.Database; + var db = Acap.DocumentManager.MdiActiveDocument.Database; return db; } - public Document Getdoc() { - var doc = Application.DocumentManager.MdiActiveDocument; + var doc = Acap.DocumentManager.MdiActiveDocument; return doc; } } - - - #pragma warning restore CS0219 // 变量已被赋值,但从未使用过它的值 diff --git a/tests/Test/TestAOP.cs b/tests/Test/TestAOP.cs index b5b5c92..4723f69 100644 --- a/tests/Test/TestAOP.cs +++ b/tests/Test/TestAOP.cs @@ -1,4 +1,6 @@ -//被注入的函数将不能使用断点, +namespace Test; + +//被注入的函数将不能使用断点, //因此用户要充分了解才能使用 #if false /* @@ -63,4 +65,4 @@ public void InjectionTransaction2() } } } -#endif \ No newline at end of file +#endif diff --git a/tests/Test/TestCurve.cs b/tests/Test/TestCurve.cs index 4d5cd96..2606a83 100644 --- a/tests/Test/TestCurve.cs +++ b/tests/Test/TestCurve.cs @@ -1,158 +1,155 @@ -using System.Security.Policy; +namespace Test; -namespace Test +public class TestGraph { - public class TestGraph + [CommandMethod("testpointindict")] + public void TestPointInDict() { - [CommandMethod("testpointindict")] - public void TestPointInDict() + var pt1 = new Point3d(0.0255, 0.452, 0); + var pt2 = new Point3d(0.0255001, 0.452003, 0); + var pt3 = new Point3d(0.0255002, 0.4520001, 0); + var pt4 = new Point3d(0.0255450, 0.45287893, 0); + var pt5 = new Point3d(0.02554935, 0.452092375, 0); + var dict = new Dictionary { - var pt1 = new Point3d(0.0255, 0.452, 0); - var pt2 = new Point3d(0.0255001, 0.452003, 0); - var pt3 = new Point3d(0.0255002, 0.4520001, 0); - var pt4 = new Point3d(0.0255450, 0.45287893, 0); - var pt5 = new Point3d(0.02554935, 0.452092375, 0); - var dict = new Dictionary - { - { pt1, 1 }, - { pt2, 2 }, - { pt3, 3 }, - { pt4, 4 }, - { pt5, 5 } - }; - Env.Print(dict[pt1]); - } + { pt1, 1 }, + { pt2, 2 }, + { pt3, 3 }, + { pt4, 4 }, + { pt5, 5 } + }; + Env.Print(dict[pt1]); + } - [CommandMethod("testgraph")] - public void TestGraph1() - { - using var tr = new DBTrans(); - var ents = Env.Editor.SSGet()?.Value?.GetEntities(); - if (ents == null) - return; - Tools.TestTimes2(1, "new", () => { - - - var res = ents.GetAllCycle(); - - //res.ForEach((i, t) => t.ForWrite(e => e.ColorIndex = i + 1)); - Env.Print(res.Count()); - tr.CurrentSpace.AddEntity(res); - }); + [CommandMethod("testgraph")] + public void TestGraph1() + { + using var tr = new DBTrans(); + var ents = Env.Editor.SSGet()?.Value?.GetEntities(); + if (ents == null) + return; + Tools.TestTimes2(1, "new", () => { + + + var res = ents.GetAllCycle(); - } + //res.ForEach((i, t) => t.ForWrite(e => e.ColorIndex = i + 1)); + Env.Print(res.Count()); + tr.CurrentSpace.AddEntity(res); + }); - [CommandMethod("testgraphspeed")] - public void TestGraphspeed() - { - using var tr = new DBTrans(); - var ents = Env.Editor.SSGet()?.Value?.GetEntities(); - if (ents == null) - return; + } + [CommandMethod("testgraphspeed")] + public void TestGraphspeed() + { + using var tr = new DBTrans(); + var ents = Env.Editor.SSGet()?.Value?.GetEntities(); + if (ents == null) + return; - var graph = new IFoxCAD.Cad.Graph(); // 为了调试先把图的访问改为internal - foreach (var curve in ents) - { + var graph = new IFoxCAD.Cad.Graph(); // 为了调试先把图的访问改为internal - graph.AddEdge(curve.GetGeCurve()); + foreach (var curve in ents) + { + graph.AddEdge(curve.GetGeCurve()); - } - //新建 dfs - var dfs = new DepthFirst(); + + } + //新建 dfs + var dfs = new DepthFirst(); #if true - Tools.TestTimes2(100, "new", () => { - // 查询全部的 闭合环 - dfs.FindAll(graph); - }); - Tools.TestTimes2(1000, "new", () => { - // 查询全部的 闭合环 - dfs.FindAll(graph); - }); + Tools.TestTimes2(100, "new", () => { + // 查询全部的 闭合环 + dfs.FindAll(graph); + }); + Tools.TestTimes2(1000, "new", () => { + // 查询全部的 闭合环 + dfs.FindAll(graph); + }); #else - Tools.TestTimes2(100, "old", () => { - // 查询全部的 闭合环 - dfs.FindAll(graph); - }); - Tools.TestTimes2(1000, "old", () => { - // 查询全部的 闭合环 - dfs.FindAll(graph); - }); + Tools.TestTimes2(100, "old", () => { + // 查询全部的 闭合环 + dfs.FindAll(graph); + }); + Tools.TestTimes2(1000, "old", () => { + // 查询全部的 闭合环 + dfs.FindAll(graph); + }); #endif - //res.ForEach((i, t) => t.ForWrite(e => e.ColorIndex = i + 1)); + //res.ForEach((i, t) => t.ForWrite(e => e.ColorIndex = i + 1)); - //tr.CurrentSpace.AddEntity(res); + //tr.CurrentSpace.AddEntity(res); - } } +} - public class TestCurve +public class TestCurve +{ + [CommandMethod("testbreakcurve")] + public void TestBreakCurve() { - [CommandMethod("testbreakcurve")] - public void TestBreakCurve() - { - using var tr = new DBTrans(); - var ents = Env.Editor.SSGet().Value.GetEntities(); - var tt = CurveEx.BreakCurve(ents.ToList()); - tt.ForEach(t => t.ForWrite(e => e.ColorIndex = 1)); - tr.CurrentSpace.AddEntity(tt); - } + using var tr = new DBTrans(); + var ents = Env.Editor.SSGet().Value.GetEntities(); + var tt = CurveEx.BreakCurve(ents.ToList()); + tt.ForEach(t => t.ForWrite(e => e.ColorIndex = 1)); + tr.CurrentSpace.AddEntity(tt); + } - [CommandMethod("testCurveCurveIntersector3d")] - public void TestCurveCurveIntersector3d() - { - using var tr = new DBTrans(); - var ents = Env.Editor.SSGet().Value.GetEntities() - .Select(e => e.ToCompositeCurve3d()).ToList(); + [CommandMethod("testCurveCurveIntersector3d")] + public void TestCurveCurveIntersector3d() + { + using var tr = new DBTrans(); + var ents = Env.Editor.SSGet().Value.GetEntities() + .Select(e => e.ToCompositeCurve3d()).ToList(); - var cci3d = new CurveCurveIntersector3d(); + var cci3d = new CurveCurveIntersector3d(); - for (int i = 0; i < ents.Count; i++) + for (int i = 0; i < ents.Count; i++) + { + var gc1 = ents[i]; + var int1 = gc1.GetInterval(); + //var pars1 = paramss[i]; + for (int j = i; j < ents.Count; j++) { - var gc1 = ents[i]; - var int1 = gc1.GetInterval(); - //var pars1 = paramss[i]; - for (int j = i; j < ents.Count; j++) + var gc2 = ents[j]; + //var pars2 = paramss[j]; + var int2 = gc2.GetInterval(); + cci3d.Set(gc1, gc2, int1, int2, Vector3d.ZAxis); + var d = cci3d.OverlapCount(); + var a = cci3d.GetIntersectionRanges(); + Env.Print($"{a[0].LowerBound}-{a[0].UpperBound} and {a[1].LowerBound}-{a[1].UpperBound}"); + for (int m = 0; m < d; m++) { - var gc2 = ents[j]; - //var pars2 = paramss[j]; - var int2 = gc2.GetInterval(); - cci3d.Set(gc1, gc2, int1, int2, Vector3d.ZAxis); - var d = cci3d.OverlapCount(); - var a = cci3d.GetIntersectionRanges(); - Env.Print($"{a[0].LowerBound}-{a[0].UpperBound} and {a[1].LowerBound}-{a[1].UpperBound}"); - for (int m = 0; m < d; m++) - { - var b = cci3d.GetOverlapRanges(m); - Env.Print($"{b[0].LowerBound}-{b[0].UpperBound} and {b[1].LowerBound}-{b[1].UpperBound}"); - } - - for (int k = 0; k < cci3d.NumberOfIntersectionPoints; k++) - { - //var a = cci3d.GetOverlapRanges(k); - //var b = cci3d.IsTangential(k); - //var c = cci3d.IsTransversal(k); - //var d = cci3d.OverlapCount(); - //var e = cci3d.OverlapDirection(); - var pt = cci3d.GetIntersectionParameters(k); - var pts = cci3d.GetIntersectionPoint(k); - Env.Print(pts); - } + var b = cci3d.GetOverlapRanges(m); + Env.Print($"{b[0].LowerBound}-{b[0].UpperBound} and {b[1].LowerBound}-{b[1].UpperBound}"); + } + for (int k = 0; k < cci3d.NumberOfIntersectionPoints; k++) + { + //var a = cci3d.GetOverlapRanges(k); + //var b = cci3d.IsTangential(k); + //var c = cci3d.IsTransversal(k); + //var d = cci3d.OverlapCount(); + //var e = cci3d.OverlapDirection(); + var pt = cci3d.GetIntersectionParameters(k); + var pts = cci3d.GetIntersectionPoint(k); + Env.Print(pts); + } - } } - // var tt = CurveEx.Topo(ents.ToList()); - //tt.ForEach(t => t.ForWrite(e => e.ColorIndex = 1)); - //tr.CurrentSpace.AddEntity(tt); + } + // var tt = CurveEx.Topo(ents.ToList()); + //tt.ForEach(t => t.ForWrite(e => e.ColorIndex = 1)); + //tr.CurrentSpace.AddEntity(tt); } -} \ No newline at end of file +} diff --git a/tests/Test/TestDBTrans.cs b/tests/Test/TestDBTrans.cs index 230882e..f989fd5 100644 --- a/tests/Test/TestDBTrans.cs +++ b/tests/Test/TestDBTrans.cs @@ -52,12 +52,12 @@ public void TestPt() var tr1 = HostApplicationServices.WorkingDatabase.TransactionManager.TopTransaction; using var tr2 = new DBTrans(); var tr3 = HostApplicationServices.WorkingDatabase.TransactionManager.TopTransaction; - var tr6 = Application.DocumentManager.MdiActiveDocument.TransactionManager.TopTransaction; + var tr6 = Acap.DocumentManager.MdiActiveDocument.TransactionManager.TopTransaction; Env.Print(tr2.Transaction == tr3); Env.Print(tr3 == tr6); using var tr4 = new DBTrans(); var tr5 = HostApplicationServices.WorkingDatabase.TransactionManager.TopTransaction; - var tr7 = Application.DocumentManager.MdiActiveDocument.TransactionManager.TopTransaction; + var tr7 = Acap.DocumentManager.MdiActiveDocument.TransactionManager.TopTransaction; Env.Print(tr4.Transaction == tr5); Env.Print(tr5 == tr7); var trm = HostApplicationServices.WorkingDatabase.TransactionManager; diff --git a/tests/Test/TestJig.cs b/tests/Test/TestJig.cs index 0fb1310..918795c 100644 --- a/tests/Test/TestJig.cs +++ b/tests/Test/TestJig.cs @@ -1,4 +1,5 @@ namespace Test; + using System.Windows.Forms; public class Commands_Jig diff --git a/tests/Test/TestLisp.cs b/tests/Test/TestLisp.cs index f657286..7a10953 100644 --- a/tests/Test/TestLisp.cs +++ b/tests/Test/TestLisp.cs @@ -1,122 +1,121 @@ -namespace Test +namespace Test; + +public class TestLisp { - public class TestLisp + //定义lisp函数 + [LispFunction("LispTest_RunLisp")] + public static object LispTest_RunLisp(ResultBuffer rb) { - //定义lisp函数 - [LispFunction("LispTest_RunLisp")] - public static object LispTest_RunLisp(ResultBuffer rb) - { - CmdTest_RunLisp(); - return null!; - } + CmdTest_RunLisp(); + return null!; + } - //模态命令,只有当CAD发出命令提示或当前没有其他的命令或程序活动的时候才可以被触发 - [CommandMethod("CmdTest_RunLisp1", CommandFlags.Modal)] - //透明命令,可以在一个命令提示输入的时候触发例如正交切换,zoom等 - [CommandMethod("CmdTest_RunLisp2", CommandFlags.Transparent)] - //选择图元之后执行命令将可以从 获取图元 - [CommandMethod("CmdTest_RunLisp3", CommandFlags.UsePickSet)] - //命令执行前已选中部分实体.在命令执行过程中这些标记不会被清除 - [CommandMethod("CmdTest_RunLisp4", CommandFlags.Redraw)] - //命令不能在透视图中使用 - [CommandMethod("CmdTest_RunLisp5", CommandFlags.NoPerspective)] - //命令不能通过 MULTIPLE命令 重复触发 - [CommandMethod("CmdTest_RunLisp6", CommandFlags.NoMultiple)] - //不允许在模型空间使用命令 - [CommandMethod("CmdTest_RunLisp7", CommandFlags.NoTileMode)] - //不允许在布局空间使用命令 - [CommandMethod("CmdTest_RunLisp8", CommandFlags.NoPaperSpace)] - //命令不能在OEM产品中使用 - [CommandMethod("CmdTest_RunLisp9", CommandFlags.NoOem)] - //不能直接使用命令名调用,必须使用 组名.全局名 调用 - [CommandMethod("CmdTest_RunLisp10", CommandFlags.Undefined)] - //定义lisp方法.已废弃 请使用lispfunction - [CommandMethod("CmdTest_RunLisp11", CommandFlags.Defun)] - //命令不会被存储在新的命令堆上 - [CommandMethod("CmdTest_RunLisp12", CommandFlags.NoNewStack)] - //命令不能被内部锁定(命令锁) - [CommandMethod("CmdTest_RunLisp13", CommandFlags.NoInternalLock)] - //调用命令的文档将会被锁定为只读 - [CommandMethod("CmdTest_RunLisp14", CommandFlags.DocReadLock)] - //调用命令的文档将会被锁定,类似document.lockdocument - [CommandMethod("CmdTest_RunLisp15", CommandFlags.DocExclusiveLock)] - //命令在CAD运行期间都能使用,而不只是在当前文档 - [CommandMethod("CmdTest_RunLisp16", CommandFlags.Session)] - //获取用户输入时,可以与属性面板之类的交互 - [CommandMethod("CmdTest_RunLisp17", CommandFlags.Interruptible)] - //命令不会被记录在命令历史记录 - [CommandMethod("CmdTest_RunLisp18", CommandFlags.NoHistory)] - //命令不会被 UNDO取消 - [CommandMethod("CmdTest_RunLisp19", CommandFlags.NoUndoMarker)] - //不能在参照块中使用命令 - [CommandMethod("CmdTest_RunLisp20", CommandFlags.NoBlockEditor)] + //模态命令,只有当CAD发出命令提示或当前没有其他的命令或程序活动的时候才可以被触发 + [CommandMethod("CmdTest_RunLisp1", CommandFlags.Modal)] + //透明命令,可以在一个命令提示输入的时候触发例如正交切换,zoom等 + [CommandMethod("CmdTest_RunLisp2", CommandFlags.Transparent)] + //选择图元之后执行命令将可以从 获取图元 + [CommandMethod("CmdTest_RunLisp3", CommandFlags.UsePickSet)] + //命令执行前已选中部分实体.在命令执行过程中这些标记不会被清除 + [CommandMethod("CmdTest_RunLisp4", CommandFlags.Redraw)] + //命令不能在透视图中使用 + [CommandMethod("CmdTest_RunLisp5", CommandFlags.NoPerspective)] + //命令不能通过 MULTIPLE命令 重复触发 + [CommandMethod("CmdTest_RunLisp6", CommandFlags.NoMultiple)] + //不允许在模型空间使用命令 + [CommandMethod("CmdTest_RunLisp7", CommandFlags.NoTileMode)] + //不允许在布局空间使用命令 + [CommandMethod("CmdTest_RunLisp8", CommandFlags.NoPaperSpace)] + //命令不能在OEM产品中使用 + [CommandMethod("CmdTest_RunLisp9", CommandFlags.NoOem)] + //不能直接使用命令名调用,必须使用 组名.全局名 调用 + [CommandMethod("CmdTest_RunLisp10", CommandFlags.Undefined)] + //定义lisp方法.已废弃 请使用lispfunction + [CommandMethod("CmdTest_RunLisp11", CommandFlags.Defun)] + //命令不会被存储在新的命令堆上 + [CommandMethod("CmdTest_RunLisp12", CommandFlags.NoNewStack)] + //命令不能被内部锁定(命令锁) + [CommandMethod("CmdTest_RunLisp13", CommandFlags.NoInternalLock)] + //调用命令的文档将会被锁定为只读 + [CommandMethod("CmdTest_RunLisp14", CommandFlags.DocReadLock)] + //调用命令的文档将会被锁定,类似document.lockdocument + [CommandMethod("CmdTest_RunLisp15", CommandFlags.DocExclusiveLock)] + //命令在CAD运行期间都能使用,而不只是在当前文档 + [CommandMethod("CmdTest_RunLisp16", CommandFlags.Session)] + //获取用户输入时,可以与属性面板之类的交互 + [CommandMethod("CmdTest_RunLisp17", CommandFlags.Interruptible)] + //命令不会被记录在命令历史记录 + [CommandMethod("CmdTest_RunLisp18", CommandFlags.NoHistory)] + //命令不会被 UNDO取消 + [CommandMethod("CmdTest_RunLisp19", CommandFlags.NoUndoMarker)] + //不能在参照块中使用命令 + [CommandMethod("CmdTest_RunLisp20", CommandFlags.NoBlockEditor)] #if ac2009 - //acad09增,不会被动作录制器 捕捉到 - [CommandMethod("CmdTest_RunLisp21", CommandFlags.NoActionRecording)] - //acad09增,会被动作录制器捕捉 - [CommandMethod("CmdTest_RunLisp22", CommandFlags.ActionMacro)] + //acad09增,不会被动作录制器 捕捉到 + [CommandMethod("CmdTest_RunLisp21", CommandFlags.NoActionRecording)] + //acad09增,会被动作录制器捕捉 + [CommandMethod("CmdTest_RunLisp22", CommandFlags.ActionMacro)] #endif #if !NET35 - //推断约束时不能使用命令 - [CommandMethod("CmdTest_RunLisp23", CommandFlags.NoInferConstraint)] - //命令允许在选择图元时临时显示动态尺寸 - [CommandMethod("CmdTest_RunLisp24", CommandFlags.TempShowDynDimension)] + //推断约束时不能使用命令 + [CommandMethod("CmdTest_RunLisp23", CommandFlags.NoInferConstraint)] + //命令允许在选择图元时临时显示动态尺寸 + [CommandMethod("CmdTest_RunLisp24", CommandFlags.TempShowDynDimension)] #endif - public static void CmdTest_RunLisp() - { - // 测试方法1: (command "CmdTest_RunLisp1") - // 测试方式2: (LispTest_RunLisp) - var dm = Application.DocumentManager; - var doc = dm.MdiActiveDocument; - var ed = doc.Editor; + public static void CmdTest_RunLisp() + { + // 测试方法1: (command "CmdTest_RunLisp1") + // 测试方式2: (LispTest_RunLisp) + var dm = Application.DocumentManager; + var doc = dm.MdiActiveDocument; + var ed = doc.Editor; - var sb = new StringBuilder(); - foreach (var item in Enum.GetValues(typeof(EditorEx.RunLispFlag))) - { - sb.Append((byte)item); - sb.Append(','); - } - sb.Remove(sb.Length - 1, 1); - var option = new PromptIntegerOptions($"\n输入RunLispFlag枚举值:[{sb}]"); - var ppr = ed.GetInteger(option); + var sb = new StringBuilder(); + foreach (var item in Enum.GetValues(typeof(EditorEx.RunLispFlag))) + { + sb.Append((byte)item); + sb.Append(','); + } + sb.Remove(sb.Length - 1, 1); + var option = new PromptIntegerOptions($"\n输入RunLispFlag枚举值:[{sb}]"); + var ppr = ed.GetInteger(option); - if (ppr.Status != PromptStatus.OK) - return; - var flag = (EditorEx.RunLispFlag)ppr.Value; + if (ppr.Status != PromptStatus.OK) + return; + var flag = (EditorEx.RunLispFlag)ppr.Value; - if (flag == EditorEx.RunLispFlag.AdsQueueexpr) - { - // 同步 - Env.Editor.RunLisp("(setq a 10)(princ)", - EditorEx.RunLispFlag.AdsQueueexpr); - Env.Editor.RunLisp("(princ a)", - EditorEx.RunLispFlag.AdsQueueexpr);//成功输出 - } - else if (flag == EditorEx.RunLispFlag.AcedEvaluateLisp) - { - // 使用(command "CmdTest_RunLisp1")发送,然后 !b 查看变量,acad08是有值的,高版本是null - var strlisp0 = "(setq b 20)"; - var res0 = Env.Editor.RunLisp(strlisp0, - EditorEx.RunLispFlag.AcedEvaluateLisp); //有lisp的返回值 + if (flag == EditorEx.RunLispFlag.AdsQueueexpr) + { + // 同步 + Env.Editor.RunLisp("(setq a 10)(princ)", + EditorEx.RunLispFlag.AdsQueueexpr); + Env.Editor.RunLisp("(princ a)", + EditorEx.RunLispFlag.AdsQueueexpr);//成功输出 + } + else if (flag == EditorEx.RunLispFlag.AcedEvaluateLisp) + { + // 使用(command "CmdTest_RunLisp1")发送,然后 !b 查看变量,acad08是有值的,高版本是null + var strlisp0 = "(setq b 20)"; + var res0 = Env.Editor.RunLisp(strlisp0, + EditorEx.RunLispFlag.AcedEvaluateLisp); //有lisp的返回值 - var strlisp1 = "(defun f1( / )(princ \"aa\"))"; - var res1 = Env.Editor.RunLisp(strlisp1, - EditorEx.RunLispFlag.AcedEvaluateLisp); //有lisp的返回值 + var strlisp1 = "(defun f1( / )(princ \"aa\"))"; + var res1 = Env.Editor.RunLisp(strlisp1, + EditorEx.RunLispFlag.AcedEvaluateLisp); //有lisp的返回值 - var strlisp2 = "(defun f2( / )(command \"line\"))"; - var res2 = Env.Editor.RunLisp(strlisp2, - EditorEx.RunLispFlag.AcedEvaluateLisp); //有lisp的返回值 - } - else if (flag == EditorEx.RunLispFlag.SendStringToExecute) - { - //测试异步 - //(command "CmdTest_RunLisp1")和(LispTest_RunLisp)4都是异步 - var str = "(setq c 40)(princ)"; - Env.Editor.RunLisp(str, - EditorEx.RunLispFlag.SendStringToExecute); //异步,后发送 - Env.Editor.RunLisp("(princ c)", - EditorEx.RunLispFlag.AdsQueueexpr); //同步,先发送了,输出是null - } + var strlisp2 = "(defun f2( / )(command \"line\"))"; + var res2 = Env.Editor.RunLisp(strlisp2, + EditorEx.RunLispFlag.AcedEvaluateLisp); //有lisp的返回值 + } + else if (flag == EditorEx.RunLispFlag.SendStringToExecute) + { + //测试异步 + //(command "CmdTest_RunLisp1")和(LispTest_RunLisp)4都是异步 + var str = "(setq c 40)(princ)"; + Env.Editor.RunLisp(str, + EditorEx.RunLispFlag.SendStringToExecute); //异步,后发送 + Env.Editor.RunLisp("(princ c)", + EditorEx.RunLispFlag.AdsQueueexpr); //同步,先发送了,输出是null } } } diff --git a/tests/Test/TestLoop.cs b/tests/Test/TestLoop.cs index 8992df9..d60dc66 100644 --- a/tests/Test/TestLoop.cs +++ b/tests/Test/TestLoop.cs @@ -1,36 +1,30 @@ -using IFoxCAD.Basal; +namespace Test; +using IFoxCAD.Basal; using System.Diagnostics.CodeAnalysis; -namespace Test + +public class TestLoop { - public class TestLoop + [CommandMethod("testloop")] + public void Testloop() { - [CommandMethod("testloop")] - public void Testloop() + var loop = new LoopList { - var loop = new LoopList - { - 0, - 1, - 2, - 3, - 4, - 5 - }; - - - - - Env.Print(loop); - - loop.SetFirst(loop.Last); - Env.Print(loop); - Env.Print(loop.Min()); - loop.SetFirst(new LoopListNode (loop.Min() ,loop)); - Env.Print(loop); - - + 0, + 1, + 2, + 3, + 4, + 5 + }; + + Env.Print(loop); + + loop.SetFirst(loop.Last); + Env.Print(loop); + Env.Print(loop.Min()); + loop.SetFirst(new LoopListNode (loop.Min() ,loop)); + Env.Print(loop); - } } } diff --git a/tests/Test/TestMirrorFile.cs b/tests/Test/TestMirrorFile.cs index b5f212f..9fdd8e0 100644 --- a/tests/Test/TestMirrorFile.cs +++ b/tests/Test/TestMirrorFile.cs @@ -1,4 +1,6 @@ -public class MirrorFile +namespace Test; + +public class MirrorFile { const string file = "D:/JX.dwg"; const string fileSave = "D:/JX222.dwg"; @@ -70,4 +72,4 @@ public static void CmdTest_MirrorFile2() }); tr.Database.SaveAs(fileSave, DwgVersion.AC1021/*AC1021 AutoCAD 2007/2008/2009.*/); } -} \ No newline at end of file +} diff --git a/tests/Test/TestPoint.cs b/tests/Test/TestPoint.cs index 46efe2e..38ecb97 100644 --- a/tests/Test/TestPoint.cs +++ b/tests/Test/TestPoint.cs @@ -1,160 +1,154 @@ -using System.Diagnostics; +namespace Test; -namespace Test + +using System.Diagnostics; + + +public class TestPoint { - public class TestPoint + /// + /// 红黑树排序点集 + /// + [CommandMethod("TestptSortedSet")] + public void TestptSortedSet() { - /// - /// 红黑树排序点集 - /// - [CommandMethod("TestptSortedSet")] - public void TestptSortedSet() + var ss1 = new SortedSet(); + ss1.Add(new Point2d(1, 1)); + ss1.Add(new Point2d(4.6, 2)); + ss1.Add(new Point2d(8, 3)); + ss1.Add(new Point2d(4, 3)); + ss1.Add(new Point2d(5, 40)); + ss1.Add(new Point2d(6, 5)); + ss1.Add(new Point2d(1, 6)); + ss1.Add(new Point2d(7, 6)); + ss1.Add(new Point2d(9, 6)); + + /*判断区间,超过就中断*/ + foreach (var item in ss1) { - var ss1 = new SortedSet(); - ss1.Add(new Point2d(1, 1)); - ss1.Add(new Point2d(4.6, 2)); - ss1.Add(new Point2d(8, 3)); - ss1.Add(new Point2d(4, 3)); - ss1.Add(new Point2d(5, 40)); - ss1.Add(new Point2d(6, 5)); - ss1.Add(new Point2d(1, 6)); - ss1.Add(new Point2d(7, 6)); - ss1.Add(new Point2d(9, 6)); - - /*判断区间,超过就中断*/ - foreach (var item in ss1) + if (item.X > 3 && item.X < 7) { - if (item.X > 3 && item.X < 7) - { - Debug.WriteLine(item); - } - else if (item.X >= 7) - { - break; - } + Debug.WriteLine(item); + } + else if (item.X >= 7) + { + break; } } + } - [CommandMethod("TestptGethash")] - public void TestptGethash() - { - // test - var pt = Env.Editor.GetPoint("pick pt").Value; - //Tools.TestTimes2(1_000_000, "新语法", () => { - // pt.GetHashString2(); - //}); - Tools.TestTimes2(1_000_000, "旧语法", () => { - pt.GetHashString(); - }); - } + [CommandMethod("TestptGethash")] + public void TestptGethash() + { + // test + var pt = Env.Editor.GetPoint("pick pt").Value; + //Tools.TestTimes2(1_000_000, "新语法", () => { + // pt.GetHashString2(); + //}); + Tools.TestTimes2(1_000_000, "旧语法", () => { + pt.GetHashString(); + }); + } - [CommandMethod("Testpoint3d")] - public void TestPoint3d() - { - Env.Print($"4位小数的hash:{new Point3d(0.0_001, 0.0_002, 0.0).GetHashCode()}"); - Env.Print($"5位小数的hash:{new Point3d(0.00_001, 0.00_002, 0.0).GetHashCode()}"); - Env.Print($"6位小数的hash:{new Point3d(0.000_001, 0.000_002, 0.0).GetHashCode()}"); - Env.Print($"7位小数的hash:{new Point3d(0.000_0_001, 0.000_0_002, 0.0).GetHashCode()}"); - Env.Print($"8位小数的hash:{new Point3d(0.000_00_001, 0.000_00_002, 0.0).GetHashCode()}"); - Env.Print($"9位小数的hash:{new Point3d(0.000_000_001, 0.000_000_002, 0.0).GetHashCode()}"); - Env.Print($"10位小数的hash:{new Point3d(0.000_000_0001, 0.000_000_0002, 0.0).GetHashCode()}"); - Env.Print($"10位小数的hash:{new Point3d(0.000_000_0001, 0.000_000_0001, 0.0).GetHashCode()}"); + [CommandMethod("Testpoint3d")] + public void TestPoint3d() + { + Env.Print($"4位小数的hash:{new Point3d(0.0_001, 0.0_002, 0.0).GetHashCode()}"); + Env.Print($"5位小数的hash:{new Point3d(0.00_001, 0.00_002, 0.0).GetHashCode()}"); + Env.Print($"6位小数的hash:{new Point3d(0.000_001, 0.000_002, 0.0).GetHashCode()}"); + Env.Print($"7位小数的hash:{new Point3d(0.000_0_001, 0.000_0_002, 0.0).GetHashCode()}"); + Env.Print($"8位小数的hash:{new Point3d(0.000_00_001, 0.000_00_002, 0.0).GetHashCode()}"); + Env.Print($"9位小数的hash:{new Point3d(0.000_000_001, 0.000_000_002, 0.0).GetHashCode()}"); + Env.Print($"10位小数的hash:{new Point3d(0.000_000_0001, 0.000_000_0002, 0.0).GetHashCode()}"); + Env.Print($"10位小数的hash:{new Point3d(0.000_000_0001, 0.000_000_0001, 0.0).GetHashCode()}"); - Env.Print($"11位小数的hash:{new Point3d(0.000_000_000_01, 0.000_000_000_02, 0.0).GetHashCode()}"); - Env.Print($"11位小数的hash:{new Point3d(0.000_000_000_01, 0.000_000_000_01, 0.0).GetHashCode()}"); + Env.Print($"11位小数的hash:{new Point3d(0.000_000_000_01, 0.000_000_000_02, 0.0).GetHashCode()}"); + Env.Print($"11位小数的hash:{new Point3d(0.000_000_000_01, 0.000_000_000_01, 0.0).GetHashCode()}"); - Env.Print($"12位小数的hash:{new Point3d(0.000_000_000_001, 0.000_000_000_002, 0.0).GetHashCode()}"); - Env.Print($"12位小数的hash:{new Point3d(0.000_000_000_001, 0.000_000_000_001, 0.0).GetHashCode()}"); + Env.Print($"12位小数的hash:{new Point3d(0.000_000_000_001, 0.000_000_000_002, 0.0).GetHashCode()}"); + Env.Print($"12位小数的hash:{new Point3d(0.000_000_000_001, 0.000_000_000_001, 0.0).GetHashCode()}"); - Env.Print($"13位小数的hash:{new Point3d(0.000_000_000_0001, 0.000_000_000_0002, 0.0).GetHashCode()}"); - Env.Print($"13位小数的hash:{new Point3d(0.000_000_000_0001, 0.000_000_000_0001, 0.0).GetHashCode()}"); + Env.Print($"13位小数的hash:{new Point3d(0.000_000_000_0001, 0.000_000_000_0002, 0.0).GetHashCode()}"); + Env.Print($"13位小数的hash:{new Point3d(0.000_000_000_0001, 0.000_000_000_0001, 0.0).GetHashCode()}"); - Env.Print($"14位小数的hash:{new Point3d(0.000_000_000_000_01, 0.000_000_000_000_02, 0.0).GetHashCode()}"); - Env.Print($"14位小数的hash:{new Point3d(0.000_000_000_000_01, 0.000_000_000_000_01, 0.0).GetHashCode()}"); + Env.Print($"14位小数的hash:{new Point3d(0.000_000_000_000_01, 0.000_000_000_000_02, 0.0).GetHashCode()}"); + Env.Print($"14位小数的hash:{new Point3d(0.000_000_000_000_01, 0.000_000_000_000_01, 0.0).GetHashCode()}"); - Env.Print($"15位小数的hash:{new Point3d(0.000_000_000_000_001, 0.000_000_000_000_002, 0.0).GetHashCode()}"); - Env.Print($"15位小数的hash:{new Point3d(0.000_000_000_000_001, 0.000_000_000_000_001, 0.0).GetHashCode()}"); + Env.Print($"15位小数的hash:{new Point3d(0.000_000_000_000_001, 0.000_000_000_000_002, 0.0).GetHashCode()}"); + Env.Print($"15位小数的hash:{new Point3d(0.000_000_000_000_001, 0.000_000_000_000_001, 0.0).GetHashCode()}"); - Env.Print($"16位小数的hash:{new Point3d(0.000_000_000_000_000_1, 0.000_000_000_000_000_2, 0.0).GetHashCode()}"); - Env.Print($"16位小数的hash:{new Point3d(0.000_000_000_000_000_1, 0.000_000_000_000_000_1, 0.0).GetHashCode()}"); + Env.Print($"16位小数的hash:{new Point3d(0.000_000_000_000_000_1, 0.000_000_000_000_000_2, 0.0).GetHashCode()}"); + Env.Print($"16位小数的hash:{new Point3d(0.000_000_000_000_000_1, 0.000_000_000_000_000_1, 0.0).GetHashCode()}"); - Env.Print($"17位小数的hash:{new Point3d(0.000_000_000_000_000_01, 0.000_000_000_000_000_02, 0.0).GetHashCode()}"); - Env.Print($"17位小数的hash:{new Point3d(0.000_000_000_000_000_01, 0.000_000_000_000_000_01, 0.0).GetHashCode()}"); + Env.Print($"17位小数的hash:{new Point3d(0.000_000_000_000_000_01, 0.000_000_000_000_000_02, 0.0).GetHashCode()}"); + Env.Print($"17位小数的hash:{new Point3d(0.000_000_000_000_000_01, 0.000_000_000_000_000_01, 0.0).GetHashCode()}"); - Env.Print($"18位小数的hash:{new Point3d(0.000_000_000_000_000_001, 0.000_000_000_000_000_002, 0.0).GetHashCode()}"); - Env.Print($"18位小数的hash:{new Point3d(0.000_000_000_000_000_001, 0.000_000_000_000_000_001, 0.0).GetHashCode()}"); + Env.Print($"18位小数的hash:{new Point3d(0.000_000_000_000_000_001, 0.000_000_000_000_000_002, 0.0).GetHashCode()}"); + Env.Print($"18位小数的hash:{new Point3d(0.000_000_000_000_000_001, 0.000_000_000_000_000_001, 0.0).GetHashCode()}"); - Env.Print($"19位小数的hash:{new Point3d(0.000_000_000_000_000_000_1, 0.000_000_000_000_000_000_2, 0.0).GetHashCode()}"); - Env.Print($"19位小数的hash:{new Point3d(0.000_000_000_000_000_000_1, 0.000_000_000_000_000_000_1, 0.0).GetHashCode()}"); + Env.Print($"19位小数的hash:{new Point3d(0.000_000_000_000_000_000_1, 0.000_000_000_000_000_000_2, 0.0).GetHashCode()}"); + Env.Print($"19位小数的hash:{new Point3d(0.000_000_000_000_000_000_1, 0.000_000_000_000_000_000_1, 0.0).GetHashCode()}"); - Env.Print($"20位小数的hash:{new Point3d(0.000_000_000_000_000_000_01, 0.000_000_000_000_000_000_02, 0.0).GetHashCode()}"); - Env.Print($"20位小数的hash:{new Point3d(0.000_000_000_000_000_000_01, 0.000_000_000_000_000_000_01, 0.0).GetHashCode()}"); - } + Env.Print($"20位小数的hash:{new Point3d(0.000_000_000_000_000_000_01, 0.000_000_000_000_000_000_02, 0.0).GetHashCode()}"); + Env.Print($"20位小数的hash:{new Point3d(0.000_000_000_000_000_000_01, 0.000_000_000_000_000_000_01, 0.0).GetHashCode()}"); + } - [CommandMethod("Testlistequalspeed")] - public void Testlistequalspeed() - { - var lst1 = new List { 1, 2, 3, 4 }; - var lst2 = new List { 1, 2, 3, 4}; - lst1.EqualsAll(null); - Tools.TestTimes2(1000000, "eqaulspeed:", () => { - lst1.EqualsAll(lst2); - }); - + [CommandMethod("Testlistequalspeed")] + public void Testlistequalspeed() + { + var lst1 = new List { 1, 2, 3, 4 }; + var lst2 = new List { 1, 2, 3, 4}; + lst1.EqualsAll(null); + Tools.TestTimes2(1000000, "eqaulspeed:", () => { + lst1.EqualsAll(lst2); + }); + - } + } - [CommandMethod("Testcontains")] - public void Testcontains() + [CommandMethod("Testcontains")] + public void Testcontains() + { + // test list and dict contains speed + var lst = new List { 1, 2, 3, 4 , 5,6,7,8,9,10, 11,12,13,14,15,16,17,18,19,20}; + var hashset = new HashSet { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20 }; + var dict = new Dictionary { - // test list and dict contains speed - var lst = new List { 1, 2, 3, 4 , 5,6,7,8,9,10, 11,12,13,14,15,16,17,18,19,20}; - var hashset = new HashSet { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20 }; - var dict = new Dictionary - { - { 1, 0 }, - { 2, 1 }, - { 3, 2 }, - { 4, 3 }, - { 5, 4 }, - { 6, 5 }, - { 7, 6 }, - { 8, 7 }, - { 9, 8 }, - { 10, 9 }, - { 11, 11 }, - { 12, 12 }, - { 13, 13 }, - { 14, 14 }, - { 15, 15 }, - { 16, 16 }, - { 17, 17 }, - { 18, 18 }, - { 19, 19 }, - { 20, 20 }, - }; - - Tools.TestTimes2(100_0000, "list:", () => { - lst.Contains(20); - }); - - Tools.TestTimes2(100_0000, "hashset:", () => { - hashset.Contains(20); - }); - - Tools.TestTimes2(100_0000, "dict:", () => { - dict.ContainsKey(20); - }); - - } - - - - + { 1, 0 }, + { 2, 1 }, + { 3, 2 }, + { 4, 3 }, + { 5, 4 }, + { 6, 5 }, + { 7, 6 }, + { 8, 7 }, + { 9, 8 }, + { 10, 9 }, + { 11, 11 }, + { 12, 12 }, + { 13, 13 }, + { 14, 14 }, + { 15, 15 }, + { 16, 16 }, + { 17, 17 }, + { 18, 18 }, + { 19, 19 }, + { 20, 20 }, + }; + + Tools.TestTimes2(100_0000, "list:", () => { + lst.Contains(20); + }); + + Tools.TestTimes2(100_0000, "hashset:", () => { + hashset.Contains(20); + }); + + Tools.TestTimes2(100_0000, "dict:", () => { + dict.ContainsKey(20); + }); } - - -} \ No newline at end of file +} diff --git a/tests/Test/TestQuadTree.cs b/tests/Test/TestQuadTree.cs index 7670eae..fef70c4 100644 --- a/tests/Test/TestQuadTree.cs +++ b/tests/Test/TestQuadTree.cs @@ -413,7 +413,7 @@ void Ssget(QuadTreeSelectMode mode) // //创建四叉树,默认参数无所谓 // var TreeRoot = new QuadTree(new Rect(0, 0, 32525, 32525)); -// var fil = OpFilter.Bulid(e => e.Dxf(0) == "LINE"); +// var fil = OpFilter.Build(e => e.Dxf(0) == "LINE"); // var psr = ed.SSGet("\n 选择需要连接的直线", fil); // if (psr.Status != PromptStatus.OK) return; // var LineEnts = new List(psr.Value.GetEntities(OpenMode.ForWrite)!); @@ -458,4 +458,4 @@ void Ssget(QuadTreeSelectMode mode) // } // }); // } -//} \ No newline at end of file +//} diff --git a/tests/Test/testConvexHull.cs b/tests/Test/testConvexHull.cs index c4b9496..92a6917 100644 --- a/tests/Test/testConvexHull.cs +++ b/tests/Test/testConvexHull.cs @@ -1,76 +1,76 @@ -namespace Test +namespace Test; + + +public class TestConvexHull { - public class TestConvexHull + [CommandMethod("testch")] + public void Testch() { - [CommandMethod("testch")] - public void Testch() - { - //using var tr = new DBTrans(); - //var pts = new List(); - //var flag = true; - //while (flag) - //{ - // var pt = tr.Editor.GetPoint("qudian"); - // if (pt.Status == PromptStatus.OK) - // { - // pts.Add(pt.Value); - // tr.CurrentSpace.AddEntity(new DBPoint(pt.Value)); - // } - // else - // { - // flag = false; - // } - - //} - - //var ptt = ConvexHull.GetConvexHull(pts); - - //Polyline pl = new Polyline(); - //for (int i = 0; i < ptt.Count; i++) - //{ - // pl.AddVertexAt(i, ptt[i].Point2d(), 0, 0, 0); - //} - ////pl.AddVertexAt(0, new Point2d(0, 0), 0, 0, 0); - ////pl.AddVertexAt(1, new Point2d(10, 10), 0, 0, 0); - ////pl.AddVertexAt(2, new Point2d(20, 20), 0, 0, 0); - ////pl.AddVertexAt(3, new Point2d(30, 30), 0, 0, 0); - ////pl.AddVertexAt(4, new Point2d(40, 40), 0, 0, 0); - //pl.Closed = true; - //pl.Color = Color.FromColorIndex(ColorMethod.ByColor, 6); - //tr.CurrentSpace.AddEntity(pl); - - //var a1 = GeometryEx.GetArea(new Point2d(0, 0), new Point2d(1, 0), new Point2d(1, 1)); - //var a2 = ConvexHull.cross(new Point3d(0, 0, 0), new Point3d(1, 0, 0), new Point3d(1, 1, 0)); - //tr.Editor.WriteMessage(a1.ToString()); - //tr.Editor.WriteMessage(a2.ToString()); - - - //var vec1 = new Vector2d(1, 1); - //var vec2 = new Vector2d(-1, 1); - - //var vec3 = vec1.GetPerpendicularVector(); - //var vec4 = vec2.GetPerpendicularVector(); - - //var area1 = vec2.DotProduct(vec1.GetPerpendicularVector()); - //var area2 = vec1.DotProduct(vec2.GetPerpendicularVector()); - - //var area3 = vec2.DotProduct(vec1); - //var area4 = vec1.DotProduct(vec2); - - var area5 = GeometryEx.GetArea(new List { new Point2d(0, 0), new Point2d(1, 1), new Point2d(-1, 1) }); - - var area6 = GeometryEx.GetArea(new List { new Point2d(0, 0), new Point2d(-1, 1), new Point2d(1, 1) }); - //Env.Editor.WriteMessage($"vec1 的法向量= {vec3} \n"); - //Env.Editor.WriteMessage($"vec2 的法向量= {vec4} \n"); - - //Env.Editor.WriteMessage($"vec2 点乘 vec1的法向量= {area1} \n"); - //Env.Editor.WriteMessage($"vec1 点乘 vec2的法向量= {area2} \n"); - - //Env.Editor.WriteMessage($"vec2 点乘 vec1= {area3} \n"); - //Env.Editor.WriteMessage($"vec1 点乘 vec2= {area4} \n"); - - Env.Editor.WriteMessage($"点集的有向面积:{area5} \n"); - Env.Editor.WriteMessage($"点集的有向面积:{area6} \n"); - } + //using var tr = new DBTrans(); + //var pts = new List(); + //var flag = true; + //while (flag) + //{ + // var pt = tr.Editor.GetPoint("qudian"); + // if (pt.Status == PromptStatus.OK) + // { + // pts.Add(pt.Value); + // tr.CurrentSpace.AddEntity(new DBPoint(pt.Value)); + // } + // else + // { + // flag = false; + // } + + //} + + //var ptt = ConvexHull.GetConvexHull(pts); + + //Polyline pl = new Polyline(); + //for (int i = 0; i < ptt.Count; i++) + //{ + // pl.AddVertexAt(i, ptt[i].Point2d(), 0, 0, 0); + //} + ////pl.AddVertexAt(0, new Point2d(0, 0), 0, 0, 0); + ////pl.AddVertexAt(1, new Point2d(10, 10), 0, 0, 0); + ////pl.AddVertexAt(2, new Point2d(20, 20), 0, 0, 0); + ////pl.AddVertexAt(3, new Point2d(30, 30), 0, 0, 0); + ////pl.AddVertexAt(4, new Point2d(40, 40), 0, 0, 0); + //pl.Closed = true; + //pl.Color = Color.FromColorIndex(ColorMethod.ByColor, 6); + //tr.CurrentSpace.AddEntity(pl); + + //var a1 = GeometryEx.GetArea(new Point2d(0, 0), new Point2d(1, 0), new Point2d(1, 1)); + //var a2 = ConvexHull.cross(new Point3d(0, 0, 0), new Point3d(1, 0, 0), new Point3d(1, 1, 0)); + //tr.Editor.WriteMessage(a1.ToString()); + //tr.Editor.WriteMessage(a2.ToString()); + + + //var vec1 = new Vector2d(1, 1); + //var vec2 = new Vector2d(-1, 1); + + //var vec3 = vec1.GetPerpendicularVector(); + //var vec4 = vec2.GetPerpendicularVector(); + + //var area1 = vec2.DotProduct(vec1.GetPerpendicularVector()); + //var area2 = vec1.DotProduct(vec2.GetPerpendicularVector()); + + //var area3 = vec2.DotProduct(vec1); + //var area4 = vec1.DotProduct(vec2); + + var area5 = GeometryEx.GetArea(new List { new Point2d(0, 0), new Point2d(1, 1), new Point2d(-1, 1) }); + + var area6 = GeometryEx.GetArea(new List { new Point2d(0, 0), new Point2d(-1, 1), new Point2d(1, 1) }); + //Env.Editor.WriteMessage($"vec1 的法向量= {vec3} \n"); + //Env.Editor.WriteMessage($"vec2 的法向量= {vec4} \n"); + + //Env.Editor.WriteMessage($"vec2 点乘 vec1的法向量= {area1} \n"); + //Env.Editor.WriteMessage($"vec1 点乘 vec2的法向量= {area2} \n"); + + //Env.Editor.WriteMessage($"vec2 点乘 vec1= {area3} \n"); + //Env.Editor.WriteMessage($"vec1 点乘 vec2= {area4} \n"); + + Env.Editor.WriteMessage($"点集的有向面积:{area5} \n"); + Env.Editor.WriteMessage($"点集的有向面积:{area6} \n"); } } diff --git a/tests/Test/testFileDatabase.cs b/tests/Test/testFileDatabase.cs index 7493f30..c95c97c 100644 --- a/tests/Test/testFileDatabase.cs +++ b/tests/Test/testFileDatabase.cs @@ -1,29 +1,29 @@ -/************************************************************** +namespace Test; + +/************************************************************** *作者:Leon *创建时间:2022/2/11 9:55:32 **************************************************************/ -namespace Test + +public class TestFileDatabase { - public class TestFileDatabase + [CommandMethod("Test_FileDatabaseInit")] + public void TestDatabase() { - [CommandMethod("Test_FileDatabaseInit")] - public void TestDatabase() + try { - try - { - var fileName = @"C:\Users\Administrator\Desktop\合并详图测试BUG.dwg"; - using DBTrans trans = new(fileName); - trans.ModelSpace.AddEntity(new Line(new(0, 0, 0), new(1000, 1000, 0))); - if (trans.Document is not null && trans.Document.IsActive) - trans.Document.SendStringToExecute("_qsave\n", false, true, true); - else - trans.Database.SaveAs(fileName, DwgVersion.AC1021); - } - catch (System.Exception e) - { - System.Windows.MessageBox.Show(e.Message); - } - + var fileName = @"C:\Users\Administrator\Desktop\合并详图测试BUG.dwg"; + using DBTrans trans = new(fileName); + trans.ModelSpace.AddEntity(new Line(new(0, 0, 0), new(1000, 1000, 0))); + if (trans.Document is not null && trans.Document.IsActive) + trans.Document.SendStringToExecute("_qsave\n", false, true, true); + else + trans.Database.SaveAs(fileName, DwgVersion.AC1021); } + catch (System.Exception e) + { + System.Windows.MessageBox.Show(e.Message); + } + } -} \ No newline at end of file +} diff --git a/tests/Test/testblock.cs b/tests/Test/testblock.cs index ef615be..23d6995 100644 --- a/tests/Test/testblock.cs +++ b/tests/Test/testblock.cs @@ -1,4 +1,5 @@ namespace Test; + public class TestBlock { //块定义 @@ -54,8 +55,6 @@ public void BlockDefChange() //}); - - tr.BlockTable.Change("test", btr => { foreach (var id in btr) { @@ -67,16 +66,9 @@ public void BlockDefChange() dBText.DimensionText = "234"; dBText.RecomputeDimensionBlock(true); } - if (ent is Hatch hatch) - { hatch.ColorIndex = 0; - - } - - } - } }); tr.Editor.Regen(); @@ -300,7 +292,7 @@ public void EJ() public void QuickBlockDef() { //Database db = HostApplicationServices.WorkingDatabase; - Editor ed = Application.DocumentManager.MdiActiveDocument.Editor; + Editor ed = Acap.DocumentManager.MdiActiveDocument.Editor; PromptSelectionOptions promptOpt = new() { MessageForAdding = "请选择需要快速制作块的对象" @@ -369,7 +361,7 @@ public void QuickBlockDef() ents.ForEach(ent => ent.ForWrite(e => e.Erase(true))); var bId = tr.CurrentSpace.InsertBlock(pt, blockName); //tr.GetObject(bId, OpenMode.ForWrite).Move(Point3d.Origin, Point3d.Origin); - //var ed = Application.DocumentManager.MdiActiveDocument.Editor; + //var ed = Acap.DocumentManager.MdiActiveDocument.Editor; //ed.Regen(); //tr.Editor.Regen(); // 调用regen() 卡死 @@ -387,7 +379,7 @@ public void QuickBlockDef() public void TestQuickBlockDef() { Database db = HostApplicationServices.WorkingDatabase; - Editor ed = Application.DocumentManager.MdiActiveDocument.Editor; + Editor ed = Acap.DocumentManager.MdiActiveDocument.Editor; PromptSelectionOptions promptOpt = new() { MessageForAdding = "请选择需要快速制作块的对象" @@ -507,7 +499,7 @@ public void Cbll() public void CombineBlocksIntoLibrary() { Document doc = - Application.DocumentManager.MdiActiveDocument; + Acap.DocumentManager.MdiActiveDocument; Editor ed = doc.Editor; Database destDb = doc.Database; diff --git a/tests/Test/testeditor.cs b/tests/Test/testeditor.cs index 7d610cf..bf4fd5f 100644 --- a/tests/Test/testeditor.cs +++ b/tests/Test/testeditor.cs @@ -16,7 +16,7 @@ public void Tested() var res1 = EditorEx.GetLines(pts, true); var res2 = pts.Select(pt => new TypedValue((int)LispDataType.Point2d, pt)).ToList(); - Editor ed = Application.DocumentManager.MdiActiveDocument.Editor; + Editor ed = Acap.DocumentManager.MdiActiveDocument.Editor; var pt = ed.GetPoint("qudiam", new Point3d(0, 0, 0)); var d = ed.GetDouble("qudoule"); var i = ed.GetInteger("quint"); diff --git a/tests/Test/testid.cs b/tests/Test/testid.cs index d93b823..e61553b 100644 --- a/tests/Test/testid.cs +++ b/tests/Test/testid.cs @@ -1,74 +1,73 @@ -namespace Test +namespace Test; + +public class Testid { - public class Testid + [CommandMethod("testid")] + public void TestId() { - [CommandMethod("testid")] - public void TestId() - { - using var tr = new DBTrans(); - Line line = new(new Point3d(0, 0, 0), new Point3d(1, 1, 0)); - tr.CurrentSpace.AddEntity(line); - tr.Dispose(); - - var res = Env.Editor.GetEntity("\npick ent:"); - if (res.Status == PromptStatus.OK) - { - res.ObjectId.Erase(); - } - //using (var tr = new DBTrans()) - //{ - // var res = Env.Editor.GetEntity("\npick ent:"); - // if(res.Status == PromptStatus.OK) - // { - // res.ObjectId.Erase(); - // } + using var tr = new DBTrans(); + Line line = new(new Point3d(0, 0, 0), new Point3d(1, 1, 0)); + tr.CurrentSpace.AddEntity(line); + tr.Dispose(); - //} - } - - [CommandMethod("testmycommand")] - public void TestMyCommand() + var res = Env.Editor.GetEntity("\npick ent:"); + if (res.Status == PromptStatus.OK) { - using var dbtrans = new DBTrans(Env.Document, true, false); - using var trans = Env.Database.TransactionManager.StartTransaction(); - - var l1 = new Line(new Point3d(0, 0, 0), new Point3d(100, 100, 0)); - var blkred = trans.GetObject(Env.Database.CurrentSpaceId, OpenMode.ForWrite) as BlockTableRecord; - blkred.AppendEntity(l1); - trans.AddNewlyCreatedDBObject(l1, true); - trans.Commit(); - //dbtrans.Dispose(); + res.ObjectId.Erase(); } - [CommandMethod("testtextstyle")] - public void TestTextStyle() - { - using var tr = new DBTrans(); - tr.TextStyleTable.Add("宋体", "宋体.ttf", 0.8); + //using (var tr = new DBTrans()) + //{ + // var res = Env.Editor.GetEntity("\npick ent:"); + // if(res.Status == PromptStatus.OK) + // { + // res.ObjectId.Erase(); + // } + + //} + } + + [CommandMethod("testmycommand")] + public void TestMyCommand() + { + using var dbtrans = new DBTrans(Env.Document, true, false); + using var trans = Env.Database.TransactionManager.StartTransaction(); - tr.TextStyleTable.Add("宋体1", FontTTF.宋体, 0.8); - tr.TextStyleTable.Add("仿宋体", FontTTF.仿宋, 0.8); - tr.TextStyleTable.Add("fsgb2312", FontTTF.仿宋GB2312, 0.8); - tr.TextStyleTable.Add("arial", FontTTF.Arial, 0.8); - tr.TextStyleTable.Add("romas", FontTTF.Romans, 0.8); + var l1 = new Line(new Point3d(0, 0, 0), new Point3d(100, 100, 0)); + var blkred = trans.GetObject(Env.Database.CurrentSpaceId, OpenMode.ForWrite) as BlockTableRecord; + blkred.AppendEntity(l1); + trans.AddNewlyCreatedDBObject(l1, true); + trans.Commit(); + //dbtrans.Dispose(); + } + [CommandMethod("testtextstyle")] + public void TestTextStyle() + { + using var tr = new DBTrans(); + tr.TextStyleTable.Add("宋体", "宋体.ttf", 0.8); + tr.TextStyleTable.Add("宋体1", FontTTF.宋体, 0.8); + tr.TextStyleTable.Add("仿宋体", FontTTF.仿宋, 0.8); + tr.TextStyleTable.Add("fsgb2312", FontTTF.仿宋GB2312, 0.8); + tr.TextStyleTable.Add("arial", FontTTF.Arial, 0.8); + tr.TextStyleTable.Add("romas", FontTTF.Romans, 0.8); - tr.TextStyleTable.Add("daziti", ttr => - { - ttr.FileName = "ascii.shx"; - ttr.BigFontFileName = "gbcbig.shx"; - }); - } - [CommandMethod("testtextstylechange")] - public void TestTextStyleChange() + tr.TextStyleTable.Add("daziti", ttr => { - using var tr = new DBTrans(); - + ttr.FileName = "ascii.shx"; + ttr.BigFontFileName = "gbcbig.shx"; + }); + } - tr.TextStyleTable.AddWithChange("宋体1", "simfang.ttf", height: 5); - tr.TextStyleTable.AddWithChange("仿宋体", "宋体.ttf"); - tr.TextStyleTable.AddWithChange("fsgb2312", "Romans", "gbcbig"); - } + [CommandMethod("testtextstylechange")] + public void TestTextStyleChange() + { + using var tr = new DBTrans(); + + + tr.TextStyleTable.AddWithChange("宋体1", "simfang.ttf", height: 5); + tr.TextStyleTable.AddWithChange("仿宋体", "宋体.ttf"); + tr.TextStyleTable.AddWithChange("fsgb2312", "Romans", "gbcbig"); } } diff --git a/tests/Test/testselectfilter.cs b/tests/Test/testselectfilter.cs index d4056c9..d5f57fb 100644 --- a/tests/Test/testselectfilter.cs +++ b/tests/Test/testselectfilter.cs @@ -1,34 +1,33 @@ -namespace Test +namespace Test; + +public class Testselectfilter { - public class Testselectfilter + [CommandMethod("testfilter")] + public void Testfilter() { - [CommandMethod("testfilter")] - public void Testfilter() - { - - var p = new Point3d(10, 10, 0); - var f = OpFilter.Bulid( - e =>!(e.Dxf(0) == "line" & e.Dxf(8) == "0") - | e.Dxf(0) != "circle" & e.Dxf(8) == "2" & e.Dxf(10) >= p); - - - var f2 = OpFilter.Bulid( - e => e.Or( - !e.And(e.Dxf(0) == "line", e.Dxf(8) == "0"), - e.And(e.Dxf(0) != "circle", e.Dxf(8) == "2", - e.Dxf(10) <= new Point3d(10, 10, 0)))); + + var p = new Point3d(10, 10, 0); + var f = OpFilter.Build( + e =>!(e.Dxf(0) == "line" & e.Dxf(8) == "0") + | e.Dxf(0) != "circle" & e.Dxf(8) == "2" & e.Dxf(10) >= p); + + + var f2 = OpFilter.Build( + e => e.Or( + !e.And(e.Dxf(0) == "line", e.Dxf(8) == "0"), + e.And(e.Dxf(0) != "circle", e.Dxf(8) == "2", + e.Dxf(10) <= new Point3d(10, 10, 0)))); - SelectionFilter f3 = f; - SelectionFilter f4 = f2; + SelectionFilter f3 = f; + SelectionFilter f4 = f2; - Env.Editor.WriteMessage(""); - } + Env.Editor.WriteMessage(""); + } - [CommandMethod("testselectanpoint")] - public void Testselectanpoint() - { - var sel2 = Env.Editor.SelectAtPoint(new Point3d(0, 0, 0)); - Env.Editor.WriteMessage(""); - } + [CommandMethod("testselectanpoint")] + public void Testselectanpoint() + { + var sel2 = Env.Editor.SelectAtPoint(new Point3d(0, 0, 0)); + Env.Editor.WriteMessage(""); } } -- Gitee From f10c411ca4ac0fba1be3d3c6ca480955ff6a6683 Mon Sep 17 00:00:00 2001 From: liuqihong <540762622@qq.com> Date: Wed, 17 Aug 2022 01:53:37 +0800 Subject: [PATCH 381/675] =?UTF-8?q?=E7=94=B1=E4=BA=8E=E4=BF=AE=E6=94=B9?= =?UTF-8?q?=E4=BA=86=E9=87=8D=E8=A6=81=E7=9A=84bug=E6=89=80=E4=BB=A5?= =?UTF-8?q?=E5=90=8C=E6=AD=A5=E4=B8=A4=E4=B8=AA=E5=88=86=E6=94=AF=E4=BB=A3?= =?UTF-8?q?=E7=A0=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- IFoxCAD.sln | 10 +- .../CLS/TupleElementNamesAttribute.cs | 7 +- src/IFoxCAD.Basal/IFoxCAD.Basal.csproj | 4 +- src/IFoxCAD.Basal/Sortedset/Sortedset.cs | 139 +++++++++--------- .../ExtensionMethod/EditorEx.cs | 14 +- .../ExtensionMethod/ObjEx.cs | 2 +- .../ExtensionMethod/ObjectIdEx.cs | 2 +- src/IFoxCAD.Cad.Shared/Runtime/DBTrans.cs | 4 +- src/IFoxCAD.Cad.Shared/Runtime/Env.cs | 2 +- src/IFoxCAD.Cad.Shared/Runtime/SymbolTable.cs | 5 +- tests/Test/CmdINI.cs | 18 +-- tests/Test/Test.cs | 21 +-- tests/Test/TestDBTrans.cs | 4 +- tests/Test/TestLoop.cs | 7 - tests/Test/TestPoint.cs | 5 - tests/Test/testblock.cs | 8 +- tests/Test/testeditor.cs | 2 +- 17 files changed, 114 insertions(+), 140 deletions(-) diff --git a/IFoxCAD.sln b/IFoxCAD.sln index 53407b3..8e8cbd4 100644 --- a/IFoxCAD.sln +++ b/IFoxCAD.sln @@ -11,12 +11,12 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "IFoxCAD.Basal", "src\IFoxCA EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "TestConsole", "tests\TestConsole\TestConsole.csproj", "{E2873F0B-CAD2-45E8-8FF0-C05C0C6AD955}" EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "IFoxCAD.Acad08", "src\IFoxCAD.Cad\IFoxCAD.Aacad08\IFoxCAD.Acad08.csproj", "{36F87D06-88B3-45E3-A2A8-0FC737B25428}" +EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Cad", "Cad", "{465C4E39-FBA2-417A-AB31-BDC01601F420}" EndProject Project("{D954291E-2A0B-460D-934E-DC6B0785DB48}") = "IFoxCAD.Cad.Shared", "src\IFoxCAD.Cad.Shared\IFoxCAD.Cad.Shared.shproj", "{82FB8450-B971-4E30-859F-4B2DDB81F590}" EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "IFoxCAD.Acad08", "src\IFoxCAD.Cad\IFoxCAD.Aacad08\IFoxCAD.Acad08.csproj", "{36F87D06-88B3-45E3-A2A8-0FC737B25428}" -EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "IFoxCAD.Acad09plus", "src\IFoxCAD.Cad\IFoxCAD.Aacad09plus\IFoxCAD.Acad09plus.csproj", "{CC46E4E2-90DF-48F5-B3D7-83FAFED58F2A}" EndProject Global @@ -53,14 +53,14 @@ Global GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {31D6C754-CF6B-4AB0-9861-6923DD312350} + EndGlobalSection GlobalSection(NestedProjects) = preSolution {82FB8450-B971-4E30-859F-4B2DDB81F590} = {465C4E39-FBA2-417A-AB31-BDC01601F420} {36F87D06-88B3-45E3-A2A8-0FC737B25428} = {465C4E39-FBA2-417A-AB31-BDC01601F420} {CC46E4E2-90DF-48F5-B3D7-83FAFED58F2A} = {465C4E39-FBA2-417A-AB31-BDC01601F420} EndGlobalSection - GlobalSection(ExtensibilityGlobals) = postSolution - SolutionGuid = {31D6C754-CF6B-4AB0-9861-6923DD312350} - EndGlobalSection GlobalSection(SharedMSBuildProjectFiles) = preSolution src\IFoxCAD.Cad.Shared\IFoxCAD.Cad.Shared.projitems*{36f87d06-88b3-45e3-a2a8-0fc737b25428}*SharedItemsImports = 5 src\IFoxCAD.Cad.Shared\IFoxCAD.Cad.Shared.projitems*{82fb8450-b971-4e30-859f-4b2ddb81f590}*SharedItemsImports = 13 diff --git a/src/IFoxCAD.Basal/CLS/TupleElementNamesAttribute.cs b/src/IFoxCAD.Basal/CLS/TupleElementNamesAttribute.cs index 69670b4..f3f7515 100644 --- a/src/IFoxCAD.Basal/CLS/TupleElementNamesAttribute.cs +++ b/src/IFoxCAD.Basal/CLS/TupleElementNamesAttribute.cs @@ -38,10 +38,7 @@ public sealed class TupleElementNamesAttribute : Attribute /// public TupleElementNamesAttribute(string[] transformNames) { - if (transformNames == null) - throw new ArgumentNullException(nameof(transformNames)); - - _transformNames = transformNames; + _transformNames = transformNames ?? throw new ArgumentNullException(nameof(transformNames)); } /// @@ -50,4 +47,4 @@ public TupleElementNamesAttribute(string[] transformNames) /// meant to carry element names. /// public IList TransformNames => _transformNames; -} \ No newline at end of file +} diff --git a/src/IFoxCAD.Basal/IFoxCAD.Basal.csproj b/src/IFoxCAD.Basal/IFoxCAD.Basal.csproj index 73d940d..41ed7c1 100644 --- a/src/IFoxCAD.Basal/IFoxCAD.Basal.csproj +++ b/src/IFoxCAD.Basal/IFoxCAD.Basal.csproj @@ -6,7 +6,7 @@ net35;net40;net45 true - 0.3.5.1 + 0.3.6 InspireFunction xsfhlzh;vicwjb 基于.NET的二次开发基本类库 @@ -15,7 +15,7 @@ https://gitee.com/inspirefunction/ifoxcad.git git IFoxCAD;C#;NET;Common;Basal - 直接集成元组和索引切片. + 直接sortedset. true true LICENSE diff --git a/src/IFoxCAD.Basal/Sortedset/Sortedset.cs b/src/IFoxCAD.Basal/Sortedset/Sortedset.cs index 9405fc1..058373e 100644 --- a/src/IFoxCAD.Basal/Sortedset/Sortedset.cs +++ b/src/IFoxCAD.Basal/Sortedset/Sortedset.cs @@ -126,7 +126,6 @@ public SortedSet(IEnumerable collection) : this(collection, Comparer.Defau public SortedSet(IEnumerable collection, IComparer comparer) : this(comparer) { - if (collection == null) { throw new ArgumentNullException("collection"); @@ -134,9 +133,8 @@ public SortedSet(IEnumerable collection, IComparer comparer) // these are explicit type checks in the mould of HashSet. It would have worked better // with something like an ISorted (we could make this work for SortedList.Keys etc) - SortedSet baseSortedSet = collection as SortedSet; SortedSet baseTreeSubSet = collection as TreeSubSet; - if (baseSortedSet != null && baseTreeSubSet == null && AreComparersEqual(this, baseSortedSet)) + if (collection is SortedSet baseSortedSet && baseTreeSubSet == null && AreComparersEqual(this, baseSortedSet)) { //breadth first traversal to recreate nodes if (baseSortedSet.Count == 0) @@ -149,8 +147,8 @@ public SortedSet(IEnumerable collection, IComparer comparer) //pre order way to replicate nodes - Stack theirStack = new Stack.Node>(2 * log2(baseSortedSet.Count) + 2); - Stack myStack = new Stack.Node>(2 * log2(baseSortedSet.Count) + 2); + Stack theirStack = new(2 * Log2(baseSortedSet.Count) + 2); + Stack myStack = new(2 * Log2(baseSortedSet.Count) + 2); Node theirCurrent = baseSortedSet.root; Node myCurrent = (theirCurrent != null ? new SortedSet.Node(theirCurrent.Item, theirCurrent.IsRed) : null); root = myCurrent; @@ -189,7 +187,7 @@ public SortedSet(IEnumerable collection, IComparer comparer) else { //As it stands, you're doing an NlogN sort of the collection - List els = new List(collection); + List els = new(collection); els.Sort(this.comparer); for (int i = 1; i < els.Count; i++) { @@ -273,7 +271,7 @@ internal virtual bool InOrderTreeWalk(TreeWalkPredicate action, bool reverse) // See page 264 of "Introduction to algorithms" by Thomas H. Cormen // note: this should be logbase2, but since the stack grows itself, we // don't want the extra cost - Stack stack = new Stack(2 * (int)(SortedSet.log2(Count + 1))); + Stack stack = new(2 * (int)(SortedSet.Log2(Count + 1))); Node current = root; while (current != null) { @@ -313,8 +311,10 @@ internal virtual bool BreadthFirstTreeWalk(TreeWalkPredicate action) return true; } - List processQueue = new List(); - processQueue.Add(root); + List processQueue = new() + { + root + }; Node current; while (processQueue.Count != 0) @@ -474,7 +474,7 @@ internal virtual bool AddIfNotPresent(T item) Debug.Assert(parent != null, "Parent node cannot be null here!"); // ready to insert the new node - Node node = new Node(item); + Node node = new(item); if (order > 0) { parent.Right = node; @@ -752,8 +752,7 @@ void ICollection.CopyTo(Array array, int index) ThrowHelper.ThrowArgumentException(ExceptionResource.Arg_ArrayPlusOffTooSmall); } - T[] tarray = array as T[]; - if (tarray != null) + if (array is T[] tarray) { CopyTo(tarray, index); } @@ -1194,7 +1193,7 @@ public void UnionWith(IEnumerable other) if (s != null && t == null && this.count == 0) { - SortedSet dummy = new SortedSet(s, this.comparer); + SortedSet dummy = new(s, this.comparer); this.root = dummy.root; this.count = dummy.count; this.version++; @@ -1299,8 +1298,10 @@ private static Node ConstructRootFromSortedArray(T[] arr, int startIndex, int en } else if (size == 2) { - root = new Node(arr[startIndex], false); - root.Right = new Node(arr[endIndex], false); + root = new Node(arr[startIndex], false) + { + Right = new Node(arr[endIndex], false) + }; root.Right.IsRed = true; if (redNode != null) { @@ -1309,9 +1310,11 @@ private static Node ConstructRootFromSortedArray(T[] arr, int startIndex, int en } else if (size == 3) { - root = new Node(arr[startIndex + 1], false); - root.Left = new Node(arr[startIndex], false); - root.Right = new Node(arr[endIndex], false); + root = new Node(arr[startIndex + 1], false) + { + Left = new Node(arr[startIndex], false), + Right = new Node(arr[endIndex], false) + }; if (redNode != null) { root.Left.Left = redNode; @@ -1321,8 +1324,10 @@ private static Node ConstructRootFromSortedArray(T[] arr, int startIndex, int en else { int midpt = ((startIndex + endIndex) / 2); - root = new Node(arr[midpt], false); - root.Left = ConstructRootFromSortedArray(arr, startIndex, midpt - 1, redNode); + root = new Node(arr[midpt], false) + { + Left = ConstructRootFromSortedArray(arr, startIndex, midpt - 1, redNode) + }; if (size % 2 == 0) { root.Right = ConstructRootFromSortedArray(arr, midpt + 2, endIndex, new Node(arr[midpt + 1], true)); @@ -1357,12 +1362,11 @@ public virtual void IntersectWith(IEnumerable other) //HashSet optimizations can't be done until equality comparers and comparers are related //Technically, this would work as well with an ISorted - SortedSet s = other as SortedSet; TreeSubSet t = this as TreeSubSet; if (t != null) VersionCheck(); //only let this happen if i am also a SortedSet, not a SubSet - if (s != null && t == null && AreComparersEqual(this, s)) + if (other is SortedSet s && t == null && AreComparersEqual(this, s)) { @@ -1412,7 +1416,7 @@ public virtual void IntersectWith(IEnumerable other) internal virtual void IntersectWithEnumerable(IEnumerable other) { // - List toSave = new List(this.Count); + List toSave = new(this.Count); foreach (T item in other) { if (this.Contains(item)) @@ -1451,9 +1455,8 @@ public void ExceptWith(IEnumerable other) return; } - SortedSet asSorted = other as SortedSet; - if (asSorted != null && AreComparersEqual(this, asSorted)) + if (other is SortedSet asSorted && AreComparersEqual(this, asSorted)) { //outside range, no point doing anything if (!(comparer.Compare(asSorted.Max, this.Min) < 0 || comparer.Compare(asSorted.Min, this.Max) > 0)) @@ -1504,12 +1507,11 @@ public void SymmetricExceptWith(IEnumerable other) } - SortedSet asSorted = other as SortedSet; #if USING_HASH_SET HashSet asHash = other as HashSet; #endif - if (asSorted != null && AreComparersEqual(this, asSorted)) + if (other is SortedSet asSorted && AreComparersEqual(this, asSorted)) { SymmetricExceptWithSameEC(asSorted); } @@ -1590,8 +1592,7 @@ public bool IsSubsetOf(IEnumerable other) return true; - SortedSet asSorted = other as SortedSet; - if (asSorted != null && AreComparersEqual(this, asSorted)) + if (other is SortedSet asSorted && AreComparersEqual(this, asSorted)) { if (this.Count > asSorted.Count) return false; @@ -1648,8 +1649,7 @@ public bool IsProperSubsetOf(IEnumerable other) } #endif //another for sorted sets with the same comparer - SortedSet asSorted = other as SortedSet; - if (asSorted != null && AreComparersEqual(this, asSorted)) + if (other is SortedSet asSorted && AreComparersEqual(this, asSorted)) { if (this.Count >= asSorted.Count) return false; @@ -1687,8 +1687,7 @@ public bool IsSupersetOf(IEnumerable other) } #endif //another for sorted sets with the same comparer - SortedSet asSorted = other as SortedSet; - if (asSorted != null && AreComparersEqual(this, asSorted)) + if (other is SortedSet asSorted && AreComparersEqual(this, asSorted)) { if (this.Count < asSorted.Count) return false; @@ -1732,8 +1731,7 @@ public bool IsProperSupersetOf(IEnumerable other) } #endif //another way for sorted sets - SortedSet asSorted = other as SortedSet; - if (asSorted != null && AreComparersEqual(asSorted, this)) + if (other is SortedSet asSorted && AreComparersEqual(asSorted, this)) { if (asSorted.Count >= this.Count) return false; @@ -1776,8 +1774,7 @@ public bool SetEquals(IEnumerable other) return asHash.SetEquals(this); } #endif - SortedSet asSorted = other as SortedSet; - if (asSorted != null && AreComparersEqual(this, asSorted)) + if (other is SortedSet asSorted && AreComparersEqual(this, asSorted)) { IEnumerator mine = this.GetEnumerator(); IEnumerator theirs = asSorted.GetEnumerator(); @@ -1821,8 +1818,7 @@ public bool Overlaps(IEnumerable other) if ((other as ICollection != null) && (other as ICollection).Count == 0) return false; - SortedSet asSorted = other as SortedSet; - if (asSorted != null && AreComparersEqual(this, asSorted) && (comparer.Compare(Min, asSorted.Max) > 0 || comparer.Compare(Max, asSorted.Min) < 0)) + if (other is SortedSet asSorted && AreComparersEqual(this, asSorted) && (comparer.Compare(Min, asSorted.Max) > 0 || comparer.Compare(Max, asSorted.Min) < 0)) { return false; } @@ -1948,7 +1944,7 @@ public int RemoveWhere(Predicate match) { throw new ArgumentNullException("match"); } - List matches = new List(this.Count); + List matches = new(this.Count); BreadthFirstTreeWalk(delegate (Node n) { @@ -1982,7 +1978,7 @@ public T Min { get { - T ret = default(T); + T ret = default; InOrderTreeWalk(delegate (SortedSet.Node n) { ret = n.Item; return false; }); return ret; } @@ -1992,7 +1988,7 @@ public T Max { get { - T ret = default(T); + T ret = default; InOrderTreeWalk(delegate (SortedSet.Node n) { ret = n.Item; return false; }, true); return ret; } @@ -2000,7 +1996,7 @@ public T Max public IEnumerable Reverse() { - Enumerator e = new Enumerator(this, true); + Enumerator e = new(this, true); while (e.MoveNext()) { yield return e.Current; @@ -2029,7 +2025,7 @@ public virtual SortedSet GetViewBetween(T lowerValue, T upperValue) /// debug status to be checked whenever any operation is called /// /// - internal virtual bool versionUpToDate() + internal virtual bool VersionUpToDate() { return true; } @@ -2059,7 +2055,7 @@ internal sealed class TreeSubSet : SortedSet { #if DEBUG - internal override bool versionUpToDate() + internal override bool VersionUpToDate() { return (this.version == underlying.version); } @@ -2112,7 +2108,7 @@ internal override bool AddIfNotPresent(T item) bool ret = underlying.AddIfNotPresent(item); VersionCheck(); #if DEBUG - Debug.Assert(this.versionUpToDate() && this.root == this.underlying.FindRange(min, max)); + Debug.Assert(this.VersionUpToDate() && this.root == this.underlying.FindRange(min, max)); #endif return ret; @@ -2123,7 +2119,7 @@ public override bool Contains(T item) { VersionCheck(); #if DEBUG - Debug.Assert(this.versionUpToDate() && this.root == this.underlying.FindRange(min, max)); + Debug.Assert(this.VersionUpToDate() && this.root == this.underlying.FindRange(min, max)); #endif return base.Contains(item); } @@ -2139,7 +2135,7 @@ internal override bool DoRemove(T item) bool ret = underlying.Remove(item); VersionCheck(); #if DEBUG - Debug.Assert(this.versionUpToDate() && this.root == this.underlying.FindRange(min, max)); + Debug.Assert(this.VersionUpToDate() && this.root == this.underlying.FindRange(min, max)); #endif return ret; } @@ -2153,11 +2149,11 @@ public override void Clear() return; } - List toRemove = new List(); + List toRemove = new(); BreadthFirstTreeWalk(delegate (Node n) { toRemove.Add(n.Item); return true; }); while (toRemove.Count != 0) { - underlying.Remove(toRemove[toRemove.Count - 1]); + underlying.Remove(toRemove[^1]); toRemove.RemoveAt(toRemove.Count - 1); } root = null; @@ -2193,7 +2189,7 @@ internal override bool InOrderTreeWalk(TreeWalkPredicate action, Boolean reve // The maximum height of a red-black tree is 2*lg(n+1). // See page 264 of "Introduction to algorithms" by Thomas H. Cormen - Stack stack = new Stack(2 * (int)SortedSet.log2(count + 1)); //this is not exactly right if count is out of date, but the stack can grow + Stack stack = new(2 * (int)SortedSet.Log2(count + 1)); //this is not exactly right if count is out of date, but the stack can grow Node current = root; while (current != null) { @@ -2250,8 +2246,10 @@ internal override bool BreadthFirstTreeWalk(TreeWalkPredicate action) return true; } - List processQueue = new List(); - processQueue.Add(root); + List processQueue = new() + { + root + }; Node current; while (processQueue.Count != 0) @@ -2284,7 +2282,7 @@ internal override SortedSet.Node FindNode(T item) } VersionCheck(); #if DEBUG - Debug.Assert(this.versionUpToDate() && this.root == this.underlying.FindRange(min, max)); + Debug.Assert(this.VersionUpToDate() && this.root == this.underlying.FindRange(min, max)); #endif return base.FindNode(item); } @@ -2301,7 +2299,7 @@ internal override int InternalIndexOf(T item) return count; } #if DEBUG - Debug.Assert(this.versionUpToDate() && this.root == this.underlying.FindRange(min, max)); + Debug.Assert(this.VersionUpToDate() && this.root == this.underlying.FindRange(min, max)); #endif return -1; } @@ -2350,7 +2348,7 @@ public override SortedSet GetViewBetween(T lowerValue, T upperValue) internal override void IntersectWithEnumerable(IEnumerable other) { - List toSave = new List(this.Count); + List toSave = new(this.Count); foreach (T item in other) { if (this.Contains(item)) @@ -2362,7 +2360,7 @@ internal override void IntersectWithEnumerable(IEnumerable other) this.Clear(); this.AddAllElements(toSave); #if DEBUG - Debug.Assert(this.versionUpToDate() && this.root == this.underlying.FindRange(min, max)); + Debug.Assert(this.VersionUpToDate() && this.root == this.underlying.FindRange(min, max)); #endif } @@ -2558,12 +2556,12 @@ public struct Enumerator : IEnumerator, IEnumerator { private Stack.Node> stack; private SortedSet.Node current; - static SortedSet.Node dummyNode = new SortedSet.Node(default(T)); + static readonly SortedSet.Node dummyNode = new(default); private bool reverse; #if !FEATURE_NETCORE - private SerializationInfo siInfo; + private readonly SerializationInfo siInfo; #endif internal Enumerator(SortedSet set) { @@ -2575,7 +2573,7 @@ internal Enumerator(SortedSet set) version = tree.version; // 2lg(n + 1) is the maximum height - stack = new Stack.Node>(2 * (int)SortedSet.log2(set.Count + 1)); + stack = new Stack.Node>(2 * (int)SortedSet.Log2(set.Count + 1)); current = null; reverse = false; #if !FEATURE_NETCORE @@ -2593,7 +2591,7 @@ internal Enumerator(SortedSet set, bool reverse) version = tree.version; // 2lg(n + 1) is the maximum height - stack = new Stack.Node>(2 * (int)SortedSet.log2(set.Count + 1)); + stack = new Stack.Node>(2 * (int)SortedSet.Log2(set.Count + 1)); current = null; this.reverse = reverse; #if !FEATURE_NETCORE @@ -2649,7 +2647,7 @@ private void OnDeserialization(Object sender) version = siInfo.GetInt32(EnumVersionName); reverse = siInfo.GetBoolean(ReverseName); bool EnumStarted = siInfo.GetBoolean(EnumStartName); - stack = new Stack.Node>(2 * (int)SortedSet.log2(tree.Count + 1)); + stack = new Stack.Node>(2 * (int)SortedSet.Log2(tree.Count + 1)); current = null; if (EnumStarted) { @@ -2750,7 +2748,7 @@ public T Current { return current.Item; } - return default(T); + return default; } } @@ -2827,12 +2825,12 @@ public bool TryGetValue(T equalValue, out T actualValue) actualValue = node.Item; return true; } - actualValue = default(T); + actualValue = default; return false; } // used for set checking operations (using enumerables) that rely on counting - private static int log2(int value) + private static int Log2(int value) { //Contract.Requires(value>0) int c = 0; @@ -2855,8 +2853,8 @@ private static int log2(int value) /// internal class SortedSetEqualityComparer : IEqualityComparer> { - private IComparer comparer; - private IEqualityComparer e_comparer; + private readonly IComparer comparer; + private readonly IEqualityComparer e_comparer; public SortedSetEqualityComparer() : this(null, null) { } @@ -2895,7 +2893,7 @@ public int GetHashCode(SortedSet obj) { foreach (T t in obj) { - hashCode = hashCode ^ (e_comparer.GetHashCode(t) & 0x7FFFFFFF); + hashCode ^= (e_comparer.GetHashCode(t) & 0x7FFFFFFF); } } // else returns hashcode of 0 for null HashSets return hashCode; @@ -2904,8 +2902,7 @@ public int GetHashCode(SortedSet obj) // Equals method for the comparer itself. public override bool Equals(Object obj) { - SortedSetEqualityComparer comparer = obj as SortedSetEqualityComparer; - if (comparer == null) + if (obj is not SortedSetEqualityComparer comparer) { return false; } @@ -2932,4 +2929,4 @@ public override int GetHashCode() #pragma warning restore CS8618 // 在退出构造函数时,不可为 null 的字段必须包含非 null 值。请考虑声明为可以为 null。 #pragma warning restore CS8601 // 引用类型赋值可能为 null。 #pragma warning restore CS8603 // 可能返回 null 引用。 -#endif \ No newline at end of file +#endif diff --git a/src/IFoxCAD.Cad.Shared/ExtensionMethod/EditorEx.cs b/src/IFoxCAD.Cad.Shared/ExtensionMethod/EditorEx.cs index 47f06f0..eac6110 100644 --- a/src/IFoxCAD.Cad.Shared/ExtensionMethod/EditorEx.cs +++ b/src/IFoxCAD.Cad.Shared/ExtensionMethod/EditorEx.cs @@ -443,7 +443,7 @@ public static void WriteMessage(string message) try { if (Acceptable()) - Application.DocumentManager.MdiActiveDocument.Editor.WriteMessage("\n" + message); + Acap.DocumentManager.MdiActiveDocument.Editor.WriteMessage("\n" + message); else return; } @@ -469,9 +469,9 @@ public static void WriteMessage(string format, params object[] args) /// 有,没有 public static bool HasEditor() { - return Application.DocumentManager.MdiActiveDocument is not null - && Application.DocumentManager.Count != 0 - && Application.DocumentManager.MdiActiveDocument.Editor is not null; + return Acap.DocumentManager.MdiActiveDocument is not null + && Acap.DocumentManager.Count != 0 + && Acap.DocumentManager.MdiActiveDocument.Editor is not null; } /// @@ -481,7 +481,7 @@ public static bool HasEditor() public static bool Acceptable() { return HasEditor() - && !Application.DocumentManager.MdiActiveDocument.Editor.IsDragging; + && !Acap.DocumentManager.MdiActiveDocument.Editor.IsDragging; } #endregion Info @@ -1081,11 +1081,11 @@ public enum RunLispFlag : byte } if ((flag & RunLispFlag.SendStringToExecute) == RunLispFlag.SendStringToExecute) { - var dm = Application.DocumentManager; + var dm = Acap.DocumentManager; var doc = dm.MdiActiveDocument; doc.SendStringToExecute(lispCode + "\n", false, false, false); } return null; } #endregion -} \ No newline at end of file +} diff --git a/src/IFoxCAD.Cad.Shared/ExtensionMethod/ObjEx.cs b/src/IFoxCAD.Cad.Shared/ExtensionMethod/ObjEx.cs index c831959..1c90814 100644 --- a/src/IFoxCAD.Cad.Shared/ExtensionMethod/ObjEx.cs +++ b/src/IFoxCAD.Cad.Shared/ExtensionMethod/ObjEx.cs @@ -8,7 +8,7 @@ public static class ObjEx /// public static void Print(this object obj) { - Application.DocumentManager.MdiActiveDocument.Editor.WriteMessage($"{obj}\n"); + Acap.DocumentManager.MdiActiveDocument.Editor.WriteMessage($"{obj}\n"); } /// /// 系统的打印 diff --git a/src/IFoxCAD.Cad.Shared/ExtensionMethod/ObjectIdEx.cs b/src/IFoxCAD.Cad.Shared/ExtensionMethod/ObjectIdEx.cs index 52c9261..e147d6c 100644 --- a/src/IFoxCAD.Cad.Shared/ExtensionMethod/ObjectIdEx.cs +++ b/src/IFoxCAD.Cad.Shared/ExtensionMethod/ObjectIdEx.cs @@ -49,7 +49,7 @@ public static IEnumerable OfType(this IEnumerable ids) wh return ids.Where(id => id.ObjectClass().DxfName == dxfName); } #endregion GetObject - + //Acad08缺少 id.ObjectClass 如何补偿? public static RXClass ObjectClass(this ObjectId id) { diff --git a/src/IFoxCAD.Cad.Shared/Runtime/DBTrans.cs b/src/IFoxCAD.Cad.Shared/Runtime/DBTrans.cs index b97f733..3785034 100644 --- a/src/IFoxCAD.Cad.Shared/Runtime/DBTrans.cs +++ b/src/IFoxCAD.Cad.Shared/Runtime/DBTrans.cs @@ -87,7 +87,7 @@ public static DBTrans Top /// 是否锁文档 public DBTrans(Document? doc = null, bool commit = true, bool doclock = false) { - Document = doc ?? Application.DocumentManager.MdiActiveDocument; + Document = doc ?? Acap.DocumentManager.MdiActiveDocument; Database = Document.Database; Editor = Document.Editor; Transaction = Database.TransactionManager.StartTransaction(); @@ -345,7 +345,7 @@ public ObjectId GetObjectId(string handleString) public void SaveDwgFile(DwgVersion version = DwgVersion.AC1800) { bool flag = true; - foreach (Document doc in Application.DocumentManager) + foreach (Document doc in Acap.DocumentManager) { // 前台开图,使用命令保存 if (doc.Database.Filename == this.Database.Filename) diff --git a/src/IFoxCAD.Cad.Shared/Runtime/Env.cs b/src/IFoxCAD.Cad.Shared/Runtime/Env.cs index 379504b..12a9738 100644 --- a/src/IFoxCAD.Cad.Shared/Runtime/Env.cs +++ b/src/IFoxCAD.Cad.Shared/Runtime/Env.cs @@ -18,7 +18,7 @@ public static class Env /// /// 当前文档 /// - public static Document Document => Application.DocumentManager.MdiActiveDocument; + public static Document Document => Acap.DocumentManager.MdiActiveDocument; /// /// 编辑器对象 diff --git a/src/IFoxCAD.Cad.Shared/Runtime/SymbolTable.cs b/src/IFoxCAD.Cad.Shared/Runtime/SymbolTable.cs index 7312d93..11e1013 100644 --- a/src/IFoxCAD.Cad.Shared/Runtime/SymbolTable.cs +++ b/src/IFoxCAD.Cad.Shared/Runtime/SymbolTable.cs @@ -294,7 +294,10 @@ public ObjectId GetRecordFrom(SymbolTable table, string name, b /// 符号表记录名 /// 是否覆盖, 为覆盖, 为不覆盖 /// 对象id - internal ObjectId GetRecordFrom(Func> tableSelector, string fileName, string name, bool over) + internal ObjectId GetRecordFrom(Func> tableSelector, + string fileName, + string name, + bool over) { using var tr = new DBTrans(fileName); return GetRecordFrom(tableSelector(tr), name, over); diff --git a/tests/Test/CmdINI.cs b/tests/Test/CmdINI.cs index 74536a2..7a882ec 100644 --- a/tests/Test/CmdINI.cs +++ b/tests/Test/CmdINI.cs @@ -14,7 +14,7 @@ public class CmdINI : AutoRegAssem { public CmdINI() : base(AutoRegConfig.All) { - var dm = Application.DocumentManager; + var dm = Acap.DocumentManager; var doc = dm.MdiActiveDocument; var ed = doc.Editor; ed.WriteMessage($"\n {nameof(CmdINI)}构造函数,开始自动执行\r\n"); @@ -28,7 +28,7 @@ public void IFoxAddReg() { base.RegApp(); - var dm = Application.DocumentManager; + var dm = Acap.DocumentManager; var doc = dm.MdiActiveDocument; var ed = doc.Editor; ed.WriteMessage($"\n 加入注册表"); @@ -43,7 +43,7 @@ public void IFoxRemoveReg() //执行命令的时候会再次执行构造函数(导致初始化两次),但是再次执行就不会了 base.UnRegApp(); - var dm = Application.DocumentManager; + var dm = Acap.DocumentManager; var doc = dm.MdiActiveDocument; var ed = doc.Editor; ed.WriteMessage($"\n 卸载注册表"); @@ -59,7 +59,7 @@ public class Cmd_IFoxInitialize [IFoxInitialize] public void NameCasual() { - var dm = Application.DocumentManager; + var dm = Acap.DocumentManager; var doc = dm.MdiActiveDocument; var ed = doc.Editor; ed.WriteMessage("\n 开始自动执行 可以分开多个类和多个函数 \r\n"); @@ -68,7 +68,7 @@ public void NameCasual() [IFoxInitialize] public void NameCasualtest() { - var dm = Application.DocumentManager; + var dm = Acap.DocumentManager; var doc = dm.MdiActiveDocument; var ed = doc.Editor; ed.WriteMessage("\n 开始自动执行 又一次测试 \r\n"); @@ -77,7 +77,7 @@ public void NameCasualtest() [IFoxInitialize] public void Initialize() { - var dm = Application.DocumentManager; + var dm = Acap.DocumentManager; var doc = dm.MdiActiveDocument; var ed = doc.Editor; ed.WriteMessage("\n 开始自动执行 Initialize \r\n"); @@ -88,7 +88,7 @@ public void Terminate() { //try //{ - // var dm = Application.DocumentManager; + // var dm = Acap.DocumentManager; // var doc = dm.MdiActiveDocument; // var ed = doc.Editor; //注意此时编辑器已经回收,所以此句没用,并引发错误 // ed.WriteMessage("\n 结束自动执行 Terminate \r\n"); @@ -102,12 +102,12 @@ public void Terminate() //public void Initialize() //{ // //文档管理器将比此接口前创建,因此此句会执行 - // Application.DocumentManager.MdiActiveDocument.Editor.WriteMessage("\nload...."); + // Acap.DocumentManager.MdiActiveDocument.Editor.WriteMessage("\nload...."); //} //[IFoxInitialize(Sequence.First, false)] //public void Terminate() //{ // //文档管理器将比此接口前死亡,因此此句不会执行 - // Application.DocumentManager.MdiActiveDocument?.Editor.WriteMessage("\nunload...."); + // Acap.DocumentManager.MdiActiveDocument?.Editor.WriteMessage("\nunload...."); //} } \ No newline at end of file diff --git a/tests/Test/Test.cs b/tests/Test/Test.cs index 80c7f29..8cb7f78 100644 --- a/tests/Test/Test.cs +++ b/tests/Test/Test.cs @@ -219,7 +219,7 @@ public void AddXdata() [CommandMethod("getxdata")] public void GetXdata() { - var doc = Application.DocumentManager.MdiActiveDocument; + var doc = Acap.DocumentManager.MdiActiveDocument; var ed = doc.Editor; using var tr = new DBTrans(); tr.RegAppTable.ForEach(id => @@ -241,7 +241,7 @@ public void GetXdata() [CommandMethod("changexdata")] public void Changexdata() { - var doc = Application.DocumentManager.MdiActiveDocument; + var doc = Acap.DocumentManager.MdiActiveDocument; var ed = doc.Editor; var appname = "myapp"; var res = ed.GetEntity("\n select the entity:"); @@ -257,7 +257,7 @@ public void Changexdata() [CommandMethod("removexdata")] public void Removexdata() { - var doc = Application.DocumentManager.MdiActiveDocument; + var doc = Acap.DocumentManager.MdiActiveDocument; var ed = doc.Editor; var appname = "myapp"; var res = ed.GetEntity("\n select the entity:"); @@ -336,29 +336,18 @@ public void TestRec() } }); - - } - - - - - public Database Getdb() { - var db = Application.DocumentManager.MdiActiveDocument.Database; + var db = Acap.DocumentManager.MdiActiveDocument.Database; return db; } - public Document Getdoc() { - var doc = Application.DocumentManager.MdiActiveDocument; + var doc = Acap.DocumentManager.MdiActiveDocument; return doc; } } - - - #pragma warning restore CS0219 // 变量已被赋值,但从未使用过它的值 diff --git a/tests/Test/TestDBTrans.cs b/tests/Test/TestDBTrans.cs index 230882e..f989fd5 100644 --- a/tests/Test/TestDBTrans.cs +++ b/tests/Test/TestDBTrans.cs @@ -52,12 +52,12 @@ public void TestPt() var tr1 = HostApplicationServices.WorkingDatabase.TransactionManager.TopTransaction; using var tr2 = new DBTrans(); var tr3 = HostApplicationServices.WorkingDatabase.TransactionManager.TopTransaction; - var tr6 = Application.DocumentManager.MdiActiveDocument.TransactionManager.TopTransaction; + var tr6 = Acap.DocumentManager.MdiActiveDocument.TransactionManager.TopTransaction; Env.Print(tr2.Transaction == tr3); Env.Print(tr3 == tr6); using var tr4 = new DBTrans(); var tr5 = HostApplicationServices.WorkingDatabase.TransactionManager.TopTransaction; - var tr7 = Application.DocumentManager.MdiActiveDocument.TransactionManager.TopTransaction; + var tr7 = Acap.DocumentManager.MdiActiveDocument.TransactionManager.TopTransaction; Env.Print(tr4.Transaction == tr5); Env.Print(tr5 == tr7); var trm = HostApplicationServices.WorkingDatabase.TransactionManager; diff --git a/tests/Test/TestLoop.cs b/tests/Test/TestLoop.cs index 5978803..d60dc66 100644 --- a/tests/Test/TestLoop.cs +++ b/tests/Test/TestLoop.cs @@ -1,8 +1,6 @@ namespace Test; - using IFoxCAD.Basal; - using System.Diagnostics.CodeAnalysis; public class TestLoop @@ -20,9 +18,6 @@ public void Testloop() 5 }; - - - Env.Print(loop); loop.SetFirst(loop.Last); @@ -31,7 +26,5 @@ public void Testloop() loop.SetFirst(new LoopListNode (loop.Min() ,loop)); Env.Print(loop); - - } } diff --git a/tests/Test/TestPoint.cs b/tests/Test/TestPoint.cs index ab94c41..10630cd 100644 --- a/tests/Test/TestPoint.cs +++ b/tests/Test/TestPoint.cs @@ -151,9 +151,4 @@ public void Testcontains() }); } - - - - - } \ No newline at end of file diff --git a/tests/Test/testblock.cs b/tests/Test/testblock.cs index b1c6af5..23d6995 100644 --- a/tests/Test/testblock.cs +++ b/tests/Test/testblock.cs @@ -292,7 +292,7 @@ public void EJ() public void QuickBlockDef() { //Database db = HostApplicationServices.WorkingDatabase; - Editor ed = Application.DocumentManager.MdiActiveDocument.Editor; + Editor ed = Acap.DocumentManager.MdiActiveDocument.Editor; PromptSelectionOptions promptOpt = new() { MessageForAdding = "请选择需要快速制作块的对象" @@ -361,7 +361,7 @@ public void QuickBlockDef() ents.ForEach(ent => ent.ForWrite(e => e.Erase(true))); var bId = tr.CurrentSpace.InsertBlock(pt, blockName); //tr.GetObject(bId, OpenMode.ForWrite).Move(Point3d.Origin, Point3d.Origin); - //var ed = Application.DocumentManager.MdiActiveDocument.Editor; + //var ed = Acap.DocumentManager.MdiActiveDocument.Editor; //ed.Regen(); //tr.Editor.Regen(); // 调用regen() 卡死 @@ -379,7 +379,7 @@ public void QuickBlockDef() public void TestQuickBlockDef() { Database db = HostApplicationServices.WorkingDatabase; - Editor ed = Application.DocumentManager.MdiActiveDocument.Editor; + Editor ed = Acap.DocumentManager.MdiActiveDocument.Editor; PromptSelectionOptions promptOpt = new() { MessageForAdding = "请选择需要快速制作块的对象" @@ -499,7 +499,7 @@ public void Cbll() public void CombineBlocksIntoLibrary() { Document doc = - Application.DocumentManager.MdiActiveDocument; + Acap.DocumentManager.MdiActiveDocument; Editor ed = doc.Editor; Database destDb = doc.Database; diff --git a/tests/Test/testeditor.cs b/tests/Test/testeditor.cs index 7d610cf..bf4fd5f 100644 --- a/tests/Test/testeditor.cs +++ b/tests/Test/testeditor.cs @@ -16,7 +16,7 @@ public void Tested() var res1 = EditorEx.GetLines(pts, true); var res2 = pts.Select(pt => new TypedValue((int)LispDataType.Point2d, pt)).ToList(); - Editor ed = Application.DocumentManager.MdiActiveDocument.Editor; + Editor ed = Acap.DocumentManager.MdiActiveDocument.Editor; var pt = ed.GetPoint("qudiam", new Point3d(0, 0, 0)); var d = ed.GetDouble("qudoule"); var i = ed.GetInteger("quint"); -- Gitee From 25fef408f5d1395b8aed5a1412dfe624e525a097 Mon Sep 17 00:00:00 2001 From: liuqihong <540762622@qq.com> Date: Wed, 17 Aug 2022 02:07:29 +0800 Subject: [PATCH 382/675] =?UTF-8?q?=E5=AD=97=E6=AE=B5=E4=BF=AE=E6=94=B9?= =?UTF-8?q?=E9=98=B2=E6=AD=A2=E9=87=8D=E5=A4=8D=E8=8E=B7=E5=8F=96?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/IFoxCAD.Cad.Shared/Runtime/DBTrans.cs | 31 +++++++++++++---------- src/IFoxCAD.Cad.Shared/Runtime/Utils.cs | 4 +-- 2 files changed, 20 insertions(+), 15 deletions(-) diff --git a/src/IFoxCAD.Cad.Shared/Runtime/DBTrans.cs b/src/IFoxCAD.Cad.Shared/Runtime/DBTrans.cs index 3785034..4d2f8de 100644 --- a/src/IFoxCAD.Cad.Shared/Runtime/DBTrans.cs +++ b/src/IFoxCAD.Cad.Shared/Runtime/DBTrans.cs @@ -192,7 +192,8 @@ public static implicit operator Transaction(DBTrans tr) /// /// 块表 /// - public SymbolTable BlockTable => new(this, Database.BlockTableId); + public SymbolTable BlockTable => _BlockTable ??= new(this, Database.BlockTableId); + SymbolTable? _BlockTable; /// /// 当前绘图空间 /// @@ -208,41 +209,45 @@ public static implicit operator Transaction(DBTrans tr) /// /// 层表 /// - public SymbolTable LayerTable => new(this, Database.LayerTableId); + public SymbolTable LayerTable => _LayerTable ??= new(this, Database.LayerTableId); + SymbolTable? _LayerTable; /// /// 文字样式表 /// - public SymbolTable TextStyleTable => new(this, Database.TextStyleTableId); - + public SymbolTable TextStyleTable => _TextStyleTable ??= new(this, Database.TextStyleTableId); + SymbolTable? _TextStyleTable; /// /// 注册应用程序表 /// - public SymbolTable RegAppTable => new(this, Database.RegAppTableId); + public SymbolTable RegAppTable => _RegAppTable ??= new(this, Database.RegAppTableId); + SymbolTable? _RegAppTable; /// /// 标注样式表 /// - public SymbolTable DimStyleTable => new(this, Database.DimStyleTableId); + public SymbolTable DimStyleTable => _DimStyleTable ??= new(this, Database.DimStyleTableId); + SymbolTable? _DimStyleTable; /// /// 线型表 /// - public SymbolTable LinetypeTable => new(this, Database.LinetypeTableId); - + public SymbolTable LinetypeTable => _LinetypeTable ??= new(this, Database.LinetypeTableId); + SymbolTable? _LinetypeTable; /// /// 用户坐标系表 /// - public SymbolTable UcsTable => new(this, Database.UcsTableId); - + public SymbolTable UcsTable => _UcsTable ??= new(this, Database.UcsTableId); + SymbolTable? _UcsTable; /// /// 视图表 /// - public SymbolTable ViewTable => new(this, Database.ViewTableId); - + public SymbolTable ViewTable => _ViewTable ??= new(this, Database.ViewTableId); + SymbolTable? _ViewTable; /// /// 视口表 /// - public SymbolTable ViewportTable => new(this, Database.ViewportTableId); + public SymbolTable ViewportTable => _ViewportTable ??= new(this, Database.ViewportTableId); + SymbolTable? _ViewportTable; #endregion #region 字典 diff --git a/src/IFoxCAD.Cad.Shared/Runtime/Utils.cs b/src/IFoxCAD.Cad.Shared/Runtime/Utils.cs index eb372ab..450aaad 100644 --- a/src/IFoxCAD.Cad.Shared/Runtime/Utils.cs +++ b/src/IFoxCAD.Cad.Shared/Runtime/Utils.cs @@ -1,6 +1,6 @@ -using System; +namespace IFoxCAD.Cad; -namespace IFoxCAD.Cad; +using System; public class Helper { -- Gitee From baa6627da739be2f78adc733e99d3b362e1cdef7 Mon Sep 17 00:00:00 2001 From: liuqihong <540762622@qq.com> Date: Wed, 17 Aug 2022 02:14:38 +0800 Subject: [PATCH 383/675] =?UTF-8?q?=E5=B0=86winform.Application=E5=92=8Cac?= =?UTF-8?q?ad=E7=9A=84=E5=8C=BA=E5=88=86?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/IFoxCAD.Cad.Shared/Runtime/DBTrans.cs | 2 -- src/IFoxCAD.Cad.Shared/Runtime/Env.cs | 26 +++++++++++------------ src/IFoxCAD.Cad.Shared/Runtime/Utils.cs | 2 +- tests/Test/TestLisp.cs | 2 +- tests/Test/testblock.cs | 2 +- tests/Test/testenv.cs | 2 +- tests/Test/wpf/Class1.cs | 15 ++++++------- 7 files changed, 24 insertions(+), 27 deletions(-) diff --git a/src/IFoxCAD.Cad.Shared/Runtime/DBTrans.cs b/src/IFoxCAD.Cad.Shared/Runtime/DBTrans.cs index 4d2f8de..bba7c2f 100644 --- a/src/IFoxCAD.Cad.Shared/Runtime/DBTrans.cs +++ b/src/IFoxCAD.Cad.Shared/Runtime/DBTrans.cs @@ -150,7 +150,6 @@ public DBTrans(string fileName, { #if ac2008 //此处没有一一对应的关系 -#pragma warning disable CS0436 // 类型与导入类型冲突 var sf = openMode switch { FileOpenMode.OpenTryForReadShare => FileShare.Read, @@ -159,7 +158,6 @@ public DBTrans(string fileName, FileOpenMode.OpenForReadAndReadShare => FileShare.ReadWrite, _ => FileShare.ReadWrite, }; -#pragma warning restore CS0436 // 类型与导入类型冲突 Database.ReadDwgFile(fileName, sf, true/*控制读入一个与系统编码不相同的文件时的转换操作*/, password); #else Database.ReadDwgFile(fileName, openMode, true/*控制读入一个与系统编码不相同的文件时的转换操作*/, password); diff --git a/src/IFoxCAD.Cad.Shared/Runtime/Env.cs b/src/IFoxCAD.Cad.Shared/Runtime/Env.cs index 12a9738..3d7e54d 100644 --- a/src/IFoxCAD.Cad.Shared/Runtime/Env.cs +++ b/src/IFoxCAD.Cad.Shared/Runtime/Env.cs @@ -42,7 +42,7 @@ public static class Env /// 对象 public static object GetCurrentProfileProperty(string subSectionName, string propertyName) { - UserConfigurationManager ucm = Application.UserConfigurationManager; + UserConfigurationManager ucm = Acap.UserConfigurationManager; IConfigurationSection cpf = ucm.OpenCurrentProfile(); IConfigurationSection ss = cpf.OpenSubsection(subSectionName); return ss.ReadProperty(propertyName, ""); @@ -55,7 +55,7 @@ public static object GetCurrentProfileProperty(string subSectionName, string pro /// 配置项 public static IConfigurationSection GetDialogSection(object dialog) { - UserConfigurationManager ucm = Application.UserConfigurationManager; + UserConfigurationManager ucm = Acap.UserConfigurationManager; IConfigurationSection ds = ucm.OpenDialogSection(dialog); return ds; } @@ -67,7 +67,7 @@ public static IConfigurationSection GetDialogSection(object dialog) /// 配置项 public static IConfigurationSection GetGlobalSection(string propertyName) { - UserConfigurationManager ucm = Application.UserConfigurationManager; + UserConfigurationManager ucm = Acap.UserConfigurationManager; IConfigurationSection gs = ucm.OpenGlobalSection(); IConfigurationSection ss = gs.OpenSubsection(propertyName); return ss; @@ -82,8 +82,8 @@ public static IConfigurationSection GetGlobalSection(string propertyName) /// public static bool CmdEcho { - get => Convert.ToInt16(Application.GetSystemVariable("cmdecho")) == 1; - set => Application.SetSystemVariable("cmdecho", Convert.ToInt16(value)); + get => Convert.ToInt16(Acap.GetSystemVariable("cmdecho")) == 1; + set => Acap.SetSystemVariable("cmdecho", Convert.ToInt16(value)); } /// @@ -91,8 +91,8 @@ public static bool CmdEcho /// public static bool OrthoMode { - get => Convert.ToInt16(Application.GetSystemVariable("ORTHOMODE")) == 1; - set => Application.SetSystemVariable("ORTHOMODE", Convert.ToInt16(value)); + get => Convert.ToInt16(Acap.GetSystemVariable("ORTHOMODE")) == 1; + set => Acap.SetSystemVariable("ORTHOMODE", Convert.ToInt16(value)); } #region Dimblk @@ -257,7 +257,7 @@ public static DimblkType Dimblk { get { - string s = ((string)Application.GetSystemVariable("dimblk")).ToUpper(); + string s = ((string)Acap.GetSystemVariable("dimblk")).ToUpper(); //if (string.IsNullOrEmpty(s)) //{ // return DimblkType.Defult; @@ -276,7 +276,7 @@ public static DimblkType Dimblk set { string s = GetDimblkName(value); - Application.SetSystemVariable("dimblk", s); + Acap.SetSystemVariable("dimblk", s); } } @@ -401,11 +401,11 @@ public static OSModeType OSMode { get { - return (OSModeType)Convert.ToInt16(Application.GetSystemVariable("osmode")); + return (OSModeType)Convert.ToInt16(Acap.GetSystemVariable("osmode")); } set { - Application.SetSystemVariable("osmode", (int)value); + Acap.SetSystemVariable("osmode", (int)value); } } /// @@ -436,7 +436,7 @@ private static string GetName(this T value) /// 变量值 public static object GetVar(string varName) { - return Application.GetSystemVariable(varName); + return Acap.GetSystemVariable(varName); } /// /// 设置cad变量 @@ -447,7 +447,7 @@ public static void SetVar(string varName, object value) { try { - Application.SetSystemVariable(varName, value); + Acap.SetSystemVariable(varName, value); } catch (System.Exception) { diff --git a/src/IFoxCAD.Cad.Shared/Runtime/Utils.cs b/src/IFoxCAD.Cad.Shared/Runtime/Utils.cs index 450aaad..e152e7a 100644 --- a/src/IFoxCAD.Cad.Shared/Runtime/Utils.cs +++ b/src/IFoxCAD.Cad.Shared/Runtime/Utils.cs @@ -45,7 +45,7 @@ public class Helper static int GetAcDbObjectId(IntPtr db, Handle handle, out ObjectId id, bool createIfNotFound = false, uint reserved = 0) { id = ObjectId.Null; - switch (Application.Version.Major) + switch (Acap.Version.Major) { case 17: { diff --git a/tests/Test/TestLisp.cs b/tests/Test/TestLisp.cs index 7a10953..a5336c1 100644 --- a/tests/Test/TestLisp.cs +++ b/tests/Test/TestLisp.cs @@ -66,7 +66,7 @@ public static void CmdTest_RunLisp() { // 测试方法1: (command "CmdTest_RunLisp1") // 测试方式2: (LispTest_RunLisp) - var dm = Application.DocumentManager; + var dm = Acap.DocumentManager; var doc = dm.MdiActiveDocument; var ed = doc.Editor; diff --git a/tests/Test/testblock.cs b/tests/Test/testblock.cs index 23d6995..f400692 100644 --- a/tests/Test/testblock.cs +++ b/tests/Test/testblock.cs @@ -343,7 +343,7 @@ public void QuickBlockDef() //} //if (tr.BlockTable.Has(blockName)) //{ - // Application.ShowAlertDialog(Environment.NewLine + "块名重复,程序退出!"); + // Acap.ShowAlertDialog(Environment.NewLine + "块名重复,程序退出!"); // return; //} ents.ForEach(ent => diff --git a/tests/Test/testenv.cs b/tests/Test/testenv.cs index 05ffc5c..b482781 100644 --- a/tests/Test/testenv.cs +++ b/tests/Test/testenv.cs @@ -62,7 +62,7 @@ public void Testcadver() { //Env.Print(AcadVersion.Versions); AcadVersion.Versions.ForEach(v => Env.Print(v)); - AcadVersion.FromApp(Application.AcadApplication).Print(); + AcadVersion.FromApp(Acap.AcadApplication).Print(); 1.Print(); "1".Print(); diff --git a/tests/Test/wpf/Class1.cs b/tests/Test/wpf/Class1.cs index 4358668..9fcbbcd 100644 --- a/tests/Test/wpf/Class1.cs +++ b/tests/Test/wpf/Class1.cs @@ -1,13 +1,12 @@ -namespace Test.wpf +namespace Test.wpf; + +public class Class1 { - public class Class1 + [CommandMethod("testwpf")] + public void TestWPf() { - [CommandMethod("testwpf")] - public void TestWPf() - { - var test = new TestView(); - Application.ShowModalWindow(test); - } + var test = new TestView(); + Acap.ShowModalWindow(test); } } -- Gitee From f33272f25bacc6f6a3e02564b0642c52fee7ebd0 Mon Sep 17 00:00:00 2001 From: liuqihong <540762622@qq.com> Date: Wed, 17 Aug 2022 03:42:43 +0800 Subject: [PATCH 384/675] =?UTF-8?q?=E4=BF=AE=E6=94=B9=E4=BB=A3=E7=A0=81?= =?UTF-8?q?=E6=A0=BC=E5=BC=8F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Algorithms/QuadTree/QuadTreeNode.cs | 6 +- .../ExtensionMethod/EditorEx.cs | 2 + .../ExtensionMethod/Tools.cs | 2 +- tests/Test/TestJig.cs | 121 ++++++++---------- tests/Test/TestPoint.cs | 26 ++-- tests/Test/testblock.cs | 6 +- tests/Test/wpf/TestViewModel.cs | 47 ++----- 7 files changed, 83 insertions(+), 127 deletions(-) diff --git a/src/IFoxCAD.Cad.Shared/Algorithms/QuadTree/QuadTreeNode.cs b/src/IFoxCAD.Cad.Shared/Algorithms/QuadTree/QuadTreeNode.cs index 86cb5b4..f349a0a 100644 --- a/src/IFoxCAD.Cad.Shared/Algorithms/QuadTree/QuadTreeNode.cs +++ b/src/IFoxCAD.Cad.Shared/Algorithms/QuadTree/QuadTreeNode.cs @@ -257,7 +257,7 @@ void CreateChildren() /// /// /// - Rect[] RectSplit(Rect box) + static Rect[] RectSplit(Rect box) { var halfWidth = box.Width / 2.0; var halfHeight = box.Height / 2.0; @@ -602,7 +602,9 @@ void GetCurrentContents(Rect queryArea, List results) /// 查找方向 /// 查找节点 /// - TEntity? GetNearestNeighbor(QuadTreeNode queryNode, QuadTreeFindMode findMode, Rect queryArea) + static TEntity? GetNearestNeighbor(QuadTreeNode queryNode, + QuadTreeFindMode findMode, + Rect queryArea) { TEntity? results = default; diff --git a/src/IFoxCAD.Cad.Shared/ExtensionMethod/EditorEx.cs b/src/IFoxCAD.Cad.Shared/ExtensionMethod/EditorEx.cs index eac6110..bd84323 100644 --- a/src/IFoxCAD.Cad.Shared/ExtensionMethod/EditorEx.cs +++ b/src/IFoxCAD.Cad.Shared/ExtensionMethod/EditorEx.cs @@ -1020,6 +1020,7 @@ public static PromptResult GetString(this Editor ed, string Message, string Defa [System.Security.SuppressUnmanagedCodeSecurity]//初始化默认值 static extern int AcedEvaluateLisp(string lispLine, out IntPtr result); +#pragma warning disable CA2101 // 指定对 P/Invoke 字符串参数进行封送处理 #if NET35 [DllImport("acad.exe", CharSet = CharSet.Auto, CallingConvention = CallingConvention.Cdecl, EntryPoint = "ads_queueexpr")] @@ -1027,6 +1028,7 @@ public static PromptResult GetString(this Editor ed, string Message, string Defa [DllImport("accore.dll", CharSet = CharSet.Auto, CallingConvention = CallingConvention.Cdecl, EntryPoint = "ads_queueexpr")] #endif +#pragma warning restore CA2101 // 指定对 P/Invoke 字符串参数进行封送处理 static extern int Ads_queueexpr(string strExpr); public enum RunLispFlag : byte diff --git a/src/IFoxCAD.Cad.Shared/ExtensionMethod/Tools.cs b/src/IFoxCAD.Cad.Shared/ExtensionMethod/Tools.cs index 0e875c5..d454930 100644 --- a/src/IFoxCAD.Cad.Shared/ExtensionMethod/Tools.cs +++ b/src/IFoxCAD.Cad.Shared/ExtensionMethod/Tools.cs @@ -104,7 +104,7 @@ public enum TimeEnum static extern bool QueryPerformanceFrequency(out long lpFrequency); long _startTime, _stopTime; - long _freq; + readonly long _freq; public Timer() { diff --git a/tests/Test/TestJig.cs b/tests/Test/TestJig.cs index 918795c..0f8c6b1 100644 --- a/tests/Test/TestJig.cs +++ b/tests/Test/TestJig.cs @@ -122,8 +122,9 @@ public void TestCmd_Jig44() [CommandMethod("TestCmd_loop")] public void TestCmd_loop() { - DocumentCollection dm = Acap.DocumentManager; - Editor ed = dm.MdiActiveDocument.Editor; + var dm = Acap.DocumentManager; + var ed = dm.MdiActiveDocument.Editor; + // Create and add our message filter MyMessageFilter filter = new(); System.Windows.Forms.Application.AddMessageFilter(filter); @@ -156,9 +157,7 @@ public bool PreFilterMessage(ref Message m) // Check for the Escape keypress Keys kc = (Keys)(int)m.WParam & Keys.KeyCode; if (m.Msg == WM_KEYDOWN && kc == Keys.Escape) - { bCanceled = true; - } Key = kc; // Return true to filter all keypresses return true; @@ -185,72 +184,63 @@ static public void TestCmd_QuickText() if (pr.Status != PromptStatus.OK) return; - var tr = doc.TransactionManager.StartTransaction(); - using (tr) + using var tr = doc.TransactionManager.StartTransaction(); + var btr = (BlockTableRecord)tr.GetObject(db.CurrentSpaceId, OpenMode.ForWrite); + // Create the text object, set its normal and contents + + var acText = new TextInfo(pr.StringResult, + Point3d.Origin, + AttachmentPoint.BaseLeft, textHeight: 200) + .AddDBTextToEntity(); + + acText.Normal = ed.CurrentUserCoordinateSystem.CoordinateSystem3d.Zaxis; + btr.AppendEntity(acText); + tr.AddNewlyCreatedDBObject(acText, true); + + // Create our jig + var pj = new TextPlacementJig(tr, db, acText); + // Loop as we run our jig, as we may have keywords + var state = PromptStatus.Keyword; + while (state == PromptStatus.Keyword) { - BlockTableRecord btr = - (BlockTableRecord)tr.GetObject( - db.CurrentSpaceId, OpenMode.ForWrite - ); - // Create the text object, set its normal and contents - - var acText = new TextInfo(pr.StringResult, - Point3d.Origin, - AttachmentPoint.BaseLeft, textHeight: 200) - .AddDBTextToEntity(); - - acText.Normal = ed.CurrentUserCoordinateSystem.CoordinateSystem3d.Zaxis; - btr.AppendEntity(acText); - tr.AddNewlyCreatedDBObject(acText, true); - - // Create our jig - var pj = new TextPlacementJig(tr, db, acText); - // Loop as we run our jig, as we may have keywords - PromptStatus stat = PromptStatus.Keyword; - while (stat == PromptStatus.Keyword) - { - PromptResult res = ed.Drag(pj); - stat = res.Status; - if ( - stat != PromptStatus.OK && - stat != PromptStatus.Keyword - ) - return; - } - tr.Commit(); + var res = ed.Drag(pj); + state = res.Status; + if (state != PromptStatus.OK && state != PromptStatus.Keyword) + return; } + tr.Commit(); } + +#if true class TextPlacementJig : EntityJig { // Declare some internal state readonly Database _db; readonly Transaction _tr; + Point3d _position; double _angle, _txtSize; + // Constructor - public TextPlacementJig( - Transaction tr, Database db, Entity ent - ) : base(ent) + public TextPlacementJig(Transaction tr, Database db, Entity ent) : base(ent) { _db = db; _tr = tr; _angle = 0; _txtSize = 1; } - protected override SamplerStatus Sampler( - JigPrompts jp - ) + + protected override SamplerStatus Sampler(JigPrompts jp) { // We acquire a point but with keywords - JigPromptPointOptions po = - new( - "\nPosition of text" - ); - po.UserInputControls = - (UserInputControls.Accept3dCoordinates | + JigPromptPointOptions po = new("\nPosition of text") + { + UserInputControls = + UserInputControls.Accept3dCoordinates | UserInputControls.NullResponseAccepted | UserInputControls.NoNegativeResponseAccepted | - UserInputControls.GovernedByOrthoMode); + UserInputControls.GovernedByOrthoMode + }; po.SetMessageAndKeywords( "\nSpecify position of text or " + "[Bold/Italic/LArger/Smaller/" + @@ -258,19 +248,16 @@ JigPrompts jp "Bold Italic LArger Smaller " + "ROtate90 LEft Middle RIght" ); + PromptPointResult ppr = jp.AcquirePoint(po); if (ppr.Status == PromptStatus.Keyword) { switch (ppr.StringResult) { case "Bold": - { - break; - } + break; case "Italic": - { - break; - } + break; case "LArger": { // Multiple the text size by two @@ -295,33 +282,26 @@ JigPrompts jp break; } case "LEft": - { - break; - } + break; case "RIght": - { - break; - } + break; case "Middle": - { - break; - } + break; } return SamplerStatus.OK; } else if (ppr.Status == PromptStatus.OK) { // Check if it has changed or not (reduces flicker) - if ( - _position.DistanceTo(ppr.Value) < - Tolerance.Global.EqualPoint - ) + if (_position.DistanceTo(ppr.Value) < Tolerance.Global.EqualPoint) return SamplerStatus.NoChange; + _position = ppr.Value; return SamplerStatus.OK; } return SamplerStatus.Cancel; } + protected override bool Update() { // Set properties on our text object @@ -331,5 +311,6 @@ protected override bool Update() txt.Rotation = _angle; return true; } - } -} + } +#endif +} \ No newline at end of file diff --git a/tests/Test/TestPoint.cs b/tests/Test/TestPoint.cs index 10630cd..8941ddb 100644 --- a/tests/Test/TestPoint.cs +++ b/tests/Test/TestPoint.cs @@ -12,28 +12,26 @@ public class TestPoint [CommandMethod("TestptSortedSet")] public void TestptSortedSet() { - var ss1 = new SortedSet(); - ss1.Add(new Point2d(1, 1)); - ss1.Add(new Point2d(4.6, 2)); - ss1.Add(new Point2d(8, 3)); - ss1.Add(new Point2d(4, 3)); - ss1.Add(new Point2d(5, 40)); - ss1.Add(new Point2d(6, 5)); - ss1.Add(new Point2d(1, 6)); - ss1.Add(new Point2d(7, 6)); - ss1.Add(new Point2d(9, 6)); + var ss1 = new SortedSet + { + new Point2d(1, 1), + new Point2d(4.6, 2), + new Point2d(8, 3), + new Point2d(4, 3), + new Point2d(5, 40), + new Point2d(6, 5), + new Point2d(1, 6), + new Point2d(7, 6), + new Point2d(9, 6) + }; /*判断区间,超过就中断*/ foreach (var item in ss1) { if (item.X > 3 && item.X < 7) - { Debug.WriteLine(item); - } else if (item.X >= 7) - { break; - } } } diff --git a/tests/Test/testblock.cs b/tests/Test/testblock.cs index f400692..874b5d7 100644 --- a/tests/Test/testblock.cs +++ b/tests/Test/testblock.cs @@ -433,8 +433,10 @@ public void TestQuickBlockDef() public void TestWblock() { var curdb = HostApplicationServices.WorkingDatabase; - PromptSelectionOptions opts = new(); - opts.MessageForAdding = "选择对象"; + PromptSelectionOptions opts = new() + { + MessageForAdding = "选择对象" + }; var ss = Env.Editor.GetSelection(opts).Value; var ids = new ObjectIdCollection(ss.GetObjectIds()); var db = curdb.Wblock(ids, Point3d.Origin); diff --git a/tests/Test/wpf/TestViewModel.cs b/tests/Test/wpf/TestViewModel.cs index 4cb7563..3e01578 100644 --- a/tests/Test/wpf/TestViewModel.cs +++ b/tests/Test/wpf/TestViewModel.cs @@ -1,13 +1,12 @@ - +namespace Test.wpf; + using System.Windows; using System.Windows.Input; -namespace Test.wpf; - class TestViewModel : ViewModelBase { - + private string name; public string Name @@ -22,18 +21,14 @@ public RelayCommand ClickCommand { get { - if (clickCommand is null) - { - clickCommand = new( - execute => Name = "hello " + Name, - can => !string.IsNullOrEmpty(Name)); - } + clickCommand ??= + new(execute => Name = "hello " + Name, + can => !string.IsNullOrEmpty(Name)); return clickCommand; } } private bool receiveMouseMove; - public bool ReceiveMouseMove { get { return receiveMouseMove; } @@ -41,7 +36,6 @@ public bool ReceiveMouseMove } private string tipText; - public string TipText { get { return tipText; } @@ -53,61 +47,38 @@ public RelayCommand LoadCommand { get { - if (loadedCommand is null) - { - loadedCommand = new( - execute => MessageBox.Show("程序加载完毕")); - } + loadedCommand ??= new(execute => MessageBox.Show("程序加载完毕")); return loadedCommand; } } private RelayCommand mouseMoveCommand; - public RelayCommand MouseMoveCommand { get { - if (mouseMoveCommand is null) - { - mouseMoveCommand = new( - execute => - { + mouseMoveCommand ??= new( + execute => { var pt = execute.GetPosition(execute.Device.Target); var left = "左键放开"; var mid = "中键放开"; var right = "右键放开"; if (execute.LeftButton == MouseButtonState.Pressed) - { left = "左键放下"; - } if (execute.MiddleButton == MouseButtonState.Pressed) - { mid = "中键放下"; - } if (execute.RightButton == MouseButtonState.Pressed) - { right = "右键放下"; - } TipText = $"当前鼠标位置:X={pt.X},Y={pt.Y}。当前鼠标状态:{left}、{mid}、{right}"; }, can => ReceiveMouseMove); - } return mouseMoveCommand; } } - - - - - public TestViewModel() { Name = "world"; } - - - } -- Gitee From 57e6f5b93f89034449622530ab4104cfb0d1ada4 Mon Sep 17 00:00:00 2001 From: liuqihong <540762622@qq.com> Date: Wed, 17 Aug 2022 05:36:54 +0800 Subject: [PATCH 385/675] =?UTF-8?q?=E5=A2=9E=E5=8A=A0=E4=B8=80=E4=B8=AA?= =?UTF-8?q?=E8=8E=B7=E5=8F=96=E5=8C=85=E5=9B=B4=E7=9B=92=E7=9A=84=E5=8A=9F?= =?UTF-8?q?=E8=83=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../ExtensionMethod/EntityEx.cs | 114 ++++++++++++++++++ src/IFoxCAD.Cad.Shared/Runtime/AcadVersion.cs | 2 - src/IFoxCAD.Cad.Shared/Runtime/Log.cs | 108 +++++++++++------ tests/Test/testblock.cs | 55 +++++---- 4 files changed, 217 insertions(+), 62 deletions(-) diff --git a/src/IFoxCAD.Cad.Shared/ExtensionMethod/EntityEx.cs b/src/IFoxCAD.Cad.Shared/ExtensionMethod/EntityEx.cs index 9f9fc55..9a1cae7 100644 --- a/src/IFoxCAD.Cad.Shared/ExtensionMethod/EntityEx.cs +++ b/src/IFoxCAD.Cad.Shared/ExtensionMethod/EntityEx.cs @@ -393,6 +393,7 @@ public static void ClipBlockRef(this BlockReference bref, Point3d pt1, Point3d p } #endregion + #region 属性 /// /// 更新动态块属性值 /// @@ -412,4 +413,117 @@ public static void ChangeBlockProperty(this BlockReference blockReference, } } #endregion + + #region 包围盒 + /// + /// 切割符号 + /// + static string? _GetBoundingBoxLogAddress; + public static string GetBoundingBoxLogAddress + { + get + { + if (_GetBoundingBoxLogAddress == null) + { + var sb = new StringBuilder(); + sb.Append(Environment.CurrentDirectory); + sb.Append("\\ErrorLog\\" + nameof(GetBoundingBoxEx)); + //新建文件夹 + var path = sb.ToString(); + if (!Directory.Exists(path)) + { + Directory.CreateDirectory(path) + .Attributes = FileAttributes.Normal; //设置文件夹属性为普通 + } + sb.Append('\\'); + sb.Append(nameof(GetBoundingBoxEx)); + sb.Append(".config"); + _GetBoundingBoxLogAddress = sb.ToString(); + } + return _GetBoundingBoxLogAddress; + } + } + + static readonly HashSet _typeNames; + static EntityEx() + { + _typeNames = new(); + + if (!File.Exists(GetBoundingBoxLogAddress)) + return; + + var old = LogHelper.LogAddress; + LogHelper.OptionFile(GetBoundingBoxLogAddress); + var LogTxts = new FileLogger().ReadLog(); + LogHelper.LogAddress = old; + + foreach (var line in LogTxts) + { + if (line.Contains("备注信息")) + { + var s = line.IndexOf(":") + 1; + var msg = line.Substring(s, line.Length - s).Replace("\"", string.Empty).Trim(); + _typeNames.Add(msg); + } + } + } + + /// + /// 获取图元包围盒 + /// + /// + /// 返回包围盒点,若相同,则表示类型不存在包围盒 + /// 异常: + /// 会将包围盒类型记录到所属路径中,以此查询 + public static (Point3d min, Point3d max) GetBoundingBoxEx(this Entity ent) + { + //提前处理错误类型 + if (ent is AttributeDefinition) //属性定义没有包围盒 + return (Point3d.Origin, Point3d.Origin); + + if (ent is Xline xline)//参照线 + return (xline.BasePoint, xline.BasePoint); + if (ent is Ray ray)//射线 + return (ray.BasePoint, ray.BasePoint); + + try + { + if (ent is BlockReference brf) + { + try + { + var box = brf.GeometryExtentsBestFit(); + return (box.MinPoint, box.MaxPoint); + } + catch + { + //如果是两条参照线的块,将导致无法获取包围盒就会报错 + //是否需要进入块内,然后拿到每个图元的BasePoint再计算中点??感觉过于复杂 + //这个时候拿基点走就算了 + return (brf.Position, brf.Position); + } + } + + //var type = ent.GetType(); + //if (_typeNames.Contains(type)) + // return null; + return (ent.GeometricExtents.MinPoint, ent.GeometricExtents.MaxPoint); + } + catch (Exception e) + { + //无法处理的错误类型将被记录 + var old = LogHelper.LogAddress; + var old2 = LogHelper.FlagOutFile; + + LogHelper.FlagOutFile = true; + LogHelper.OptionFile(GetBoundingBoxLogAddress); + e.WriteLog(ent.GetType().Name, LogTarget.FileNotException); + + LogHelper.LogAddress = old; + LogHelper.FlagOutFile = old2; + } + return (Point3d.Origin, Point3d.Origin); + } + #endregion + #endregion } diff --git a/src/IFoxCAD.Cad.Shared/Runtime/AcadVersion.cs b/src/IFoxCAD.Cad.Shared/Runtime/AcadVersion.cs index f8f5273..9f776fc 100644 --- a/src/IFoxCAD.Cad.Shared/Runtime/AcadVersion.cs +++ b/src/IFoxCAD.Cad.Shared/Runtime/AcadVersion.cs @@ -63,10 +63,8 @@ public static List Versions int major = int.Parse(gs[1].Value); int minor = int.Parse(gs[2].Value); foreach (var ver in Versions) - { if (ver.Major == major && ver.Minor == minor) return ver; - } return null; } } diff --git a/src/IFoxCAD.Cad.Shared/Runtime/Log.cs b/src/IFoxCAD.Cad.Shared/Runtime/Log.cs index 02293e2..fd27f6c 100644 --- a/src/IFoxCAD.Cad.Shared/Runtime/Log.cs +++ b/src/IFoxCAD.Cad.Shared/Runtime/Log.cs @@ -3,14 +3,15 @@ using System; using System.Diagnostics; using System.Threading; +using Exception = Exception; #region 写入日志到不同的环境中 //https://zhuanlan.zhihu.com/p/338492989 public abstract class LogBase { - public abstract void ReadLog(string message); + public abstract void DeleteLog(); + public abstract string[] ReadLog(); public abstract void WriteLog(string message); - public abstract void DeleteLog(string message); } /// @@ -19,17 +20,21 @@ public abstract class LogBase public enum LogTarget { /// - /// 文件 + /// 文件(包含错误和备注) /// File = 1, /// + /// 文件(不包含错误,也就是只写备注信息) + /// + FileNotException = 2, + /// /// 数据库 /// - Database = 2, + Database = 4, /// /// windows日志 /// - EventLog = 4, + EventLog = 8, } /// @@ -37,16 +42,21 @@ public enum LogTarget /// public class FileLogger : LogBase { - public override void DeleteLog(string message) + public override void DeleteLog() { - throw new NotImplementedException(); + File.Delete(LogHelper.LogAddress); } - - public override void ReadLog(string message) + public override string[] ReadLog() { - throw new NotImplementedException(); + List lines = new(); + using (var sr = new StreamReader(LogHelper.LogAddress, true/*自动识别文件头*/)) + { + string line; + while ((line = sr.ReadLine()) != null) + lines.Add(line); + } + return lines.ToArray(); } - public override void WriteLog(string? message) { //把异常信息输出到文件 @@ -63,16 +73,14 @@ public override void WriteLog(string? message) /// public class DBLogger : LogBase { - public override void DeleteLog(string message) + public override void DeleteLog() { throw new NotImplementedException(); } - - public override void ReadLog(string message) + public override string[] ReadLog() { throw new NotImplementedException(); } - public override void WriteLog(string? message) { throw new NotImplementedException(); @@ -84,31 +92,39 @@ public override void WriteLog(string? message) /// public class EventLogger : LogBase { - // https://docs.microsoft.com/en-us/answers/questions/526018/windows-event-log-with-net-5.html - // net50要加 // 需要win权限 + // https://blog.csdn.net/weixin_38208401/article/details/77870909 + // net50要加 + // https://docs.microsoft.com/en-us/answers/questions/526018/windows-event-log-with-net-5.html public string LogName = "IFoxCadLog"; - public override void DeleteLog(string message) + public override void DeleteLog() { #if !NET5_0 && !NET6_0 if (EventLog.Exists(LogName)) EventLog.Delete(LogName); #endif } - - public override void ReadLog(string message) + public override string[] ReadLog() { + List lines = new(); #if !NET5_0 && !NET6_0 - EventLog eventLog = new(); - eventLog.Log = LogName; - foreach (EventLogEntry entry in eventLog.Entries) + try + { + EventLog eventLog = new() + { + Log = LogName + }; + foreach (EventLogEntry entry in eventLog.Entries) + lines.Add(entry.Message); + } + catch (System.Security.SecurityException e) { - //Write your custom code here + throw new Exception("您没有权限读取win日志::" + e.Message); } #endif + return lines.ToArray(); } - public override void WriteLog(string? message) { #if !NET5_0 && !NET6_0 @@ -122,7 +138,7 @@ public override void WriteLog(string? message) } catch (System.Security.SecurityException e) { - throw new Exception("您没有权限写入win日志中" + e.Message); + throw new Exception("您没有权限写入win日志::" + e.Message); } #endif } @@ -146,7 +162,6 @@ public static class LogHelper /// 输出错误信息到vs输出窗口的开关 /// public static bool FlagOutVsOutput = true; - #pragma warning restore CA2211 // 非常量字段应当不可见 /// @@ -219,14 +234,26 @@ public static string WriteLog(this Exception? exception, string? message, } - - static string LogAction(Exception? ex, string? message, LogTarget target) + /// 错误 + /// 备注信息 + /// 记录方式 + static string LogAction(Exception? ex, + string? message, + LogTarget target) { if (ex == null && message == null) return string.Empty; - if (target == LogTarget.File && LogAddress == null) - OptionFile(); + if (LogAddress == null) + { + if (target == LogTarget.File || + target == LogTarget.FileNotException) + OptionFile(); + } + + //不写入错误 + if (target == LogTarget.FileNotException) + ex = null; try { @@ -247,6 +274,10 @@ static string LogAction(Exception? ex, string? message, LogTarget target) logger = new FileLogger(); logger.WriteLog(logtxtJson); break; + case LogTarget.FileNotException: + logger = new FileLogger(); + logger.WriteLog(logtxtJson); + break; case LogTarget.Database: logger = new DBLogger(); logger.WriteLog(logtxtJson); @@ -277,26 +308,25 @@ static string LogAction(Exception? ex, string? message, LogTarget target) [Serializable] public class LogTxt { - public string 当前时间; + public string? 当前时间; public string? 备注信息; public string? 异常信息; public string? 异常对象; public string? 触发方法; public string? 调用堆栈; - LogTxt() + public LogTxt() { } + + public LogTxt(Exception? ex, string? message) : this() { + if (ex == null && message == null) + throw new ArgumentNullException(nameof(ex)); + // 以不同语言显示日期 // DateTime.Now.ToString("f", new System.Globalization.CultureInfo("es-ES")) // DateTime.Now.ToString("f", new System.Globalization.CultureInfo("zh-cn")) // 为了最小信息熵,所以用这样的格式,并且我喜欢补0 当前时间 = DateTime.Now.ToString("yy-MM-dd hh:mm:ss"); - } - - public LogTxt(Exception? ex, string? message) : this() - { - if (ex == null && message == null) - throw new ArgumentNullException(nameof(ex)); if (ex != null) { diff --git a/tests/Test/testblock.cs b/tests/Test/testblock.cs index 874b5d7..a7bd058 100644 --- a/tests/Test/testblock.cs +++ b/tests/Test/testblock.cs @@ -2,9 +2,23 @@ public class TestBlock { + [CommandMethod("Test_GetBoundingBoxEx")] + public void Test_GetBoundingBoxEx() + { + using var tr = new DBTrans(); + var ents = Env.Editor.SSGet()?.Value?.GetEntities(); + if (ents == null) + return; + foreach (var item in ents) + { + var box = item.GetBoundingBoxEx(); + Env.Print("min:" + box.min + ";max" + box.max); + } + } + //块定义 - [CommandMethod("blockdef")] - public void BlockDef() + [CommandMethod("Test_blockdef")] + public void Test_BlockDef() { using var tr = new DBTrans(); //var line = new Line(new Point3d(0, 0, 0), new Point3d(1, 1, 0)); @@ -39,8 +53,8 @@ public void BlockDef() }); } //修改块定义 - [CommandMethod("blockdefchange")] - public void BlockDefChange() + [CommandMethod("Test_blockdefchange")] + public void Test_BlockDefChange() { using var tr = new DBTrans(); //var line = new Line(new Point3d(0, 0, 0), new Point3d(1, 1, 0)); @@ -74,8 +88,8 @@ public void BlockDefChange() tr.Editor.Regen(); } - [CommandMethod("insertblockdef")] - public void InsertBlockDef() + [CommandMethod("Test_insertblockdef")] + public void Test_InsertBlockDef() { using var tr = new DBTrans(); var line1 = new Line(new Point3d(0, 0, 0), new Point3d(1, 1, 0)); @@ -119,8 +133,8 @@ public void InsertBlockDef() tr.CurrentSpace.InsertBlock(new Point3d(-10, 0, 0), "test44"); } - [CommandMethod("addattsdef")] - public void AddAttsDef() + [CommandMethod("Test_addattsdef")] + public void Test_AddAttsDef() { using var tr = new DBTrans(); var blockid = Env.Editor.GetEntity("pick block:").ObjectId; @@ -132,8 +146,8 @@ public void AddAttsDef() tr.BlockTable.AddAttsToBlocks(blockref, new List { att1, att2 }); } - [CommandMethod("testblocknullbug")] - public void TestBlockNullBug() + [CommandMethod("test_blocknullbug")] + public void Test_BlockNullBug() { using var tr = new DBTrans(); @@ -147,7 +161,7 @@ public void TestBlockNullBug() } [CommandMethod("test_block_file")] - public void TestBlockFile() + public void Test_BlockFile() { var tr = new DBTrans(); var id = tr.BlockTable.GetBlockFrom(@"C:\Users\vic\Desktop\test.dwg", false); @@ -155,8 +169,8 @@ public void TestBlockFile() } - [CommandMethod("testclip")] - public void TestClipBlock() + [CommandMethod("test_clip")] + public void Test_ClipBlock() { using var tr = new DBTrans(); tr.BlockTable.Add("test1", @@ -183,7 +197,7 @@ public void TestClipBlock() /// 给用户的测试程序,不知道对错 /// [CommandMethod("test_block_ej")] - public void EJ() + public void test_block_ej() { using (var tr = new DBTrans()) { @@ -375,8 +389,8 @@ public void QuickBlockDef() //} } - [CommandMethod("testquickblockdef")] - public void TestQuickBlockDef() + [CommandMethod("test_quickblockdef")] + public void Test_QuickBlockDef() { Database db = HostApplicationServices.WorkingDatabase; Editor ed = Acap.DocumentManager.MdiActiveDocument.Editor; @@ -484,9 +498,8 @@ public void TestBack() public class BlockImportClass { - - [CommandMethod("CBLL")] - public void Cbll() + [CommandMethod("Test_CBLL")] + public void Test_Cbll() { string filename = @"C:\Users\vic\Desktop\Drawing1.dwg"; using var tr = new DBTrans(); @@ -497,8 +510,8 @@ public void Cbll() } - [CommandMethod("CBL")] - public void CombineBlocksIntoLibrary() + [CommandMethod("Test_CombineBlocksIntoLibrary")] + public void Test_CombineBlocksIntoLibrary() { Document doc = Acap.DocumentManager.MdiActiveDocument; -- Gitee From 38657364c0c1fa70de07b31e6078dc5c425929b5 Mon Sep 17 00:00:00 2001 From: liuqihong <540762622@qq.com> Date: Wed, 17 Aug 2022 12:53:27 +0800 Subject: [PATCH 386/675] =?UTF-8?q?=E5=88=87=E5=89=B2=E5=AD=97=E7=AC=A6?= =?UTF-8?q?=E4=B8=B2=E7=9A=84=E6=96=B9=E5=BC=8F=E6=9B=B4=E7=AE=80=E5=8D=95?= =?UTF-8?q?=E4=BA=86?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../ExtensionMethod/EntityEx.cs | 22 +++++++++---------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/src/IFoxCAD.Cad.Shared/ExtensionMethod/EntityEx.cs b/src/IFoxCAD.Cad.Shared/ExtensionMethod/EntityEx.cs index 9a1cae7..8814413 100644 --- a/src/IFoxCAD.Cad.Shared/ExtensionMethod/EntityEx.cs +++ b/src/IFoxCAD.Cad.Shared/ExtensionMethod/EntityEx.cs @@ -457,12 +457,15 @@ static EntityEx() var LogTxts = new FileLogger().ReadLog(); LogHelper.LogAddress = old; - foreach (var line in LogTxts) + for (int i = 0; i < LogTxts.Length; i++) { + var line = LogTxts[i]; if (line.Contains("备注信息")) { - var s = line.IndexOf(":") + 1; - var msg = line.Substring(s, line.Length - s).Replace("\"", string.Empty).Trim(); + int index = line.IndexOf(":"); + index = line.IndexOf("\"", index) + 1;//1是"\"" + int index2 = line.IndexOf("\"", index); + var msg = line.Substring(index, index2 - index); _typeNames.Add(msg); } } @@ -478,12 +481,11 @@ static EntityEx() public static (Point3d min, Point3d max) GetBoundingBoxEx(this Entity ent) { //提前处理错误类型 - if (ent is AttributeDefinition) //属性定义没有包围盒 + if (ent is AttributeDefinition) //属性定义 return (Point3d.Origin, Point3d.Origin); - - if (ent is Xline xline)//参照线 + else if (ent is Xline xline)//参照线 return (xline.BasePoint, xline.BasePoint); - if (ent is Ray ray)//射线 + else if (ent is Ray ray)//射线 return (ray.BasePoint, ray.BasePoint); try @@ -504,10 +506,8 @@ public static (Point3d min, Point3d max) GetBoundingBoxEx(this Entity ent) } } - //var type = ent.GetType(); - //if (_typeNames.Contains(type)) - // return null; - return (ent.GeometricExtents.MinPoint, ent.GeometricExtents.MaxPoint); + if (!_typeNames.Contains(ent.GetType().Name)) + return (ent.GeometricExtents.MinPoint, ent.GeometricExtents.MaxPoint); } catch (Exception e) { -- Gitee From 6ce1a45a6c9131a006ae6dacb0e45564685a8c7b Mon Sep 17 00:00:00 2001 From: liuqihong <540762622@qq.com> Date: Wed, 17 Aug 2022 13:07:32 +0800 Subject: [PATCH 387/675] =?UTF-8?q?=E4=BF=AE=E6=94=B9=E9=BB=98=E8=AE=A4?= =?UTF-8?q?=E8=B7=AF=E5=BE=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../ExtensionMethod/EntityEx.cs | 61 ++++++++----------- src/IFoxCAD.Cad.Shared/Runtime/Log.cs | 55 ++++++++++------- 2 files changed, 59 insertions(+), 57 deletions(-) diff --git a/src/IFoxCAD.Cad.Shared/ExtensionMethod/EntityEx.cs b/src/IFoxCAD.Cad.Shared/ExtensionMethod/EntityEx.cs index 8814413..bba273f 100644 --- a/src/IFoxCAD.Cad.Shared/ExtensionMethod/EntityEx.cs +++ b/src/IFoxCAD.Cad.Shared/ExtensionMethod/EntityEx.cs @@ -415,33 +415,18 @@ public static void ChangeBlockProperty(this BlockReference blockReference, #endregion #region 包围盒 + static string? _BoxLogAddress; /// - /// 切割符号 + /// 包围盒错误文件路径 /// - static string? _GetBoundingBoxLogAddress; - public static string GetBoundingBoxLogAddress + public static string BoxLogAddress { get { - if (_GetBoundingBoxLogAddress == null) - { - var sb = new StringBuilder(); - sb.Append(Environment.CurrentDirectory); - sb.Append("\\ErrorLog\\" + nameof(GetBoundingBoxEx)); - //新建文件夹 - var path = sb.ToString(); - if (!Directory.Exists(path)) - { - Directory.CreateDirectory(path) - .Attributes = FileAttributes.Normal; //设置文件夹属性为普通 - } - sb.Append('\\'); - sb.Append(nameof(GetBoundingBoxEx)); - sb.Append(".config"); - _GetBoundingBoxLogAddress = sb.ToString(); - } - return _GetBoundingBoxLogAddress; + _BoxLogAddress ??= LogHelper.GetDefaultOption(nameof(GetBoundingBoxEx) + ".config"); + return _BoxLogAddress; } + set { _BoxLogAddress = value; } } static readonly HashSet _typeNames; @@ -449,26 +434,32 @@ static EntityEx() { _typeNames = new(); - if (!File.Exists(GetBoundingBoxLogAddress)) + if (!File.Exists(BoxLogAddress)) return; var old = LogHelper.LogAddress; - LogHelper.OptionFile(GetBoundingBoxLogAddress); - var LogTxts = new FileLogger().ReadLog(); - LogHelper.LogAddress = old; - - for (int i = 0; i < LogTxts.Length; i++) + try { - var line = LogTxts[i]; - if (line.Contains("备注信息")) + LogHelper.OptionFile(BoxLogAddress); + var LogTxts = new FileLogger().ReadLog(); + + for (int i = 0; i < LogTxts.Length; i++) { - int index = line.IndexOf(":"); - index = line.IndexOf("\"", index) + 1;//1是"\"" - int index2 = line.IndexOf("\"", index); - var msg = line.Substring(index, index2 - index); - _typeNames.Add(msg); + var line = LogTxts[i]; + if (line.Contains("备注信息")) + { + int index = line.IndexOf(":"); + index = line.IndexOf("\"", index) + 1;//1是"\"" + int index2 = line.IndexOf("\"", index); + var msg = line.Substring(index, index2 - index); + _typeNames.Add(msg); + } } } + finally + { + LogHelper.LogAddress = old; + } } /// @@ -516,7 +507,7 @@ public static (Point3d min, Point3d max) GetBoundingBoxEx(this Entity ent) var old2 = LogHelper.FlagOutFile; LogHelper.FlagOutFile = true; - LogHelper.OptionFile(GetBoundingBoxLogAddress); + LogHelper.OptionFile(BoxLogAddress); e.WriteLog(ent.GetType().Name, LogTarget.FileNotException); LogHelper.LogAddress = old; diff --git a/src/IFoxCAD.Cad.Shared/Runtime/Log.cs b/src/IFoxCAD.Cad.Shared/Runtime/Log.cs index fd27f6c..3f50915 100644 --- a/src/IFoxCAD.Cad.Shared/Runtime/Log.cs +++ b/src/IFoxCAD.Cad.Shared/Runtime/Log.cs @@ -173,7 +173,7 @@ public static class LogHelper /// /// 提供给外部设置log文件保存路径 /// - /// 空的话就为运行的dll旁边的一个文件夹上 + /// null就生成默认配置 public static void OptionFile(string? newlogAddress = null) { _logWriteLock.EnterWriteLock();// 写模式锁定 读写锁 @@ -181,27 +181,7 @@ public static void OptionFile(string? newlogAddress = null) { LogAddress = newlogAddress; if (string.IsNullOrEmpty(LogAddress)) - { - //微软回复:静态构造函数只会被调用一次, - //并且在它执行完成之前,任何其它线程都不能创建这个类的实例或使用这个类的静态成员 - //https://blog.csdn.net/weixin_34204722/article/details/90095812 - var sb = new StringBuilder(); - sb.Append(Environment.CurrentDirectory); - sb.Append("\\ErrorLog"); - - //新建文件夹 - var path = sb.ToString(); - if (!Directory.Exists(path)) - { - Directory.CreateDirectory(path) - .Attributes = FileAttributes.Normal; //设置文件夹属性为普通 - } - - sb.Append('\\'); - sb.Append(DateTime.Now.ToString("yy-MM-dd")); - sb.Append(".log"); - LogAddress = sb.ToString(); - } + LogAddress = GetDefaultOption(DateTime.Now.ToString("yy-MM-dd") + ".log"); } finally { @@ -209,6 +189,37 @@ public static void OptionFile(string? newlogAddress = null) } } + /// + /// 输入文件名,获取保存路径的完整路径 + /// + /// 文件名,null获取默认路径 + /// 创建路径 + /// 完整路径 + public static string GetDefaultOption(string fileName, bool createDirectory = true) + { + //微软回复:静态构造函数只会被调用一次, + //并且在它执行完成之前,任何其它线程都不能创建这个类的实例或使用这个类的静态成员 + //https://blog.csdn.net/weixin_34204722/article/details/90095812 + var sb = new StringBuilder(); + sb.Append(Environment.CurrentDirectory); + sb.Append("\\ErrorLog"); + + //新建文件夹 + if (createDirectory) + { + var path = sb.ToString(); + if (!Directory.Exists(path)) + { + //设置文件夹属性为普通 + Directory.CreateDirectory(path) + .Attributes = FileAttributes.Normal; + } + } + sb.Append('\\'); + sb.Append(fileName); + return sb.ToString(); + } + public static string WriteLog(this string? message, LogTarget target = LogTarget.File) { -- Gitee From 4946c85c19a10baa61493a6cf0b4035b79bff249 Mon Sep 17 00:00:00 2001 From: liuqihong <540762622@qq.com> Date: Wed, 17 Aug 2022 13:37:30 +0800 Subject: [PATCH 388/675] =?UTF-8?q?=E4=BF=AE=E6=94=B9=E6=B3=A8=E9=87=8A?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/IFoxCAD.Cad.Shared/ExtensionMethod/CurveEx.cs | 10 +++++----- .../ExtensionMethod/DatabaseEx.cs | 8 +++++--- .../ExtensionMethod/EntityEx.cs | 6 ++++-- src/IFoxCAD.Cad.Shared/ExtensionMethod/Jig.cs | 8 +++++--- .../ExtensionMethod/SymbolTableRecordEx.cs | 12 ++++++++---- src/IFoxCAD.Cad.Shared/Runtime/AutoRegAssem.cs | 12 +++++++----- src/IFoxCAD.Cad.Shared/Runtime/Env.cs | 8 +++++--- src/IFoxCAD.Cad.Shared/Runtime/IAutoGo.cs | 14 +++++++++----- tests/Test/CmdINI.cs | 15 ++++++++------- 9 files changed, 56 insertions(+), 37 deletions(-) diff --git a/src/IFoxCAD.Cad.Shared/ExtensionMethod/CurveEx.cs b/src/IFoxCAD.Cad.Shared/ExtensionMethod/CurveEx.cs index 3e9ead4..5eaab1b 100644 --- a/src/IFoxCAD.Cad.Shared/ExtensionMethod/CurveEx.cs +++ b/src/IFoxCAD.Cad.Shared/ExtensionMethod/CurveEx.cs @@ -22,7 +22,7 @@ public static double GetLength(this Curve curve) /// 打断参数表 /// 参数表排序委托 /// - /// 默认: 按所提供的参数表进行分割打断
+ /// 默认:按所提供的参数表进行分割打断
/// 否则:按委托排序后的参数表进行分割打断 ///
/// @@ -49,8 +49,8 @@ public static IEnumerable GetSplitCurves(this Curve curve, /// 打断参数表 /// 对参数表是否进行排序 /// - /// :按参数值升序排序;
- /// :不排序,默认值 + /// 按参数值升序排序
+ /// 不排序,默认值 ///
/// /// 打断后曲线的集合 @@ -83,8 +83,8 @@ public static IEnumerable GetSplitCurves(this Curve curve, IEnumerable打断点表 /// 对点表是否进行排序 /// - /// :按参数值升序排序;
- /// :不排序,默认值 + /// 按参数值升序排序
+ /// 不排序,默认值 ///
/// /// 打断后曲线的集合 diff --git a/src/IFoxCAD.Cad.Shared/ExtensionMethod/DatabaseEx.cs b/src/IFoxCAD.Cad.Shared/ExtensionMethod/DatabaseEx.cs index dff7a9e..624931c 100644 --- a/src/IFoxCAD.Cad.Shared/ExtensionMethod/DatabaseEx.cs +++ b/src/IFoxCAD.Cad.Shared/ExtensionMethod/DatabaseEx.cs @@ -4,9 +4,11 @@ public static class DatabaseEx { /// /// 后台开图文字偏移处理 - /// 0x01 此方案利用前台数据库进行处理 - /// 0x02 当关闭所有前台文档时,会出现无时,不能使用(惊惊没有测试过此状态) - /// 0x03 当关闭所有前台文档时,如何发送命令呢?那就是利用跨进程通讯 + /// + /// 0x01 此方案利用前台数据库进行处理
+ /// 0x02 当关闭所有前台文档时,会出现无时,不能使用(惊惊没有测试过此状态)
+ /// 0x03 当关闭所有前台文档时,如何发送命令呢?那就是利用跨进程通讯 + ///
///
/// 后台打开的数据库 /// 处理后台的任务 diff --git a/src/IFoxCAD.Cad.Shared/ExtensionMethod/EntityEx.cs b/src/IFoxCAD.Cad.Shared/ExtensionMethod/EntityEx.cs index bba273f..7a0b26f 100644 --- a/src/IFoxCAD.Cad.Shared/ExtensionMethod/EntityEx.cs +++ b/src/IFoxCAD.Cad.Shared/ExtensionMethod/EntityEx.cs @@ -197,8 +197,10 @@ public static void ValidateMirror(this DBText txt) /// 多行文字 /// 存储对象变量 /// 回调函数,用于处理炸散之后的对象 - /// 多行文字炸散后的对象 - /// 回调函数处理的结果 + /// + /// 多行文字炸散后的对象
+ /// 回调函数处理的结果 + ///
/// public static void ExplodeFragments(this MText mt, T obj, Func mTextFragmentCallback) { diff --git a/src/IFoxCAD.Cad.Shared/ExtensionMethod/Jig.cs b/src/IFoxCAD.Cad.Shared/ExtensionMethod/Jig.cs index b9182ae..21875eb 100644 --- a/src/IFoxCAD.Cad.Shared/ExtensionMethod/Jig.cs +++ b/src/IFoxCAD.Cad.Shared/ExtensionMethod/Jig.cs @@ -237,9 +237,11 @@ protected override SamplerStatus Sampler(JigPrompts prompts) /// /// 重绘已在数据库的图元 - /// 0x01 此处不加入newEntity的,它们在构造函数的参数回调处加入,它们会进行频繁new和Dispose从而避免遗忘释放 - /// 0x02 此处用于重绘已经在数据的图元 - /// 0x03 此处用于图元亮显暗显,因为会被重绘冲刷掉所以独立出来不重绘,它们也往往已经存在数据库的 + /// + /// 0x01 此处不加入newEntity的,它们在构造函数的参数回调处加入,它们会进行频繁new和Dispose从而避免遗忘释放
+ /// 0x02 此处用于重绘已经在数据的图元
+ /// 0x03 此处用于图元亮显暗显,因为会被重绘冲刷掉所以独立出来不重绘,它们也往往已经存在数据库的 + ///
///
/// /// newEntity只会存在一个图元队列中,而数据库图元可以分多个集合 diff --git a/src/IFoxCAD.Cad.Shared/ExtensionMethod/SymbolTableRecordEx.cs b/src/IFoxCAD.Cad.Shared/ExtensionMethod/SymbolTableRecordEx.cs index bceeac0..91b4d3a 100644 --- a/src/IFoxCAD.Cad.Shared/ExtensionMethod/SymbolTableRecordEx.cs +++ b/src/IFoxCAD.Cad.Shared/ExtensionMethod/SymbolTableRecordEx.cs @@ -10,12 +10,16 @@ public static class SymbolTableRecordEx #region 克隆实体id /// /// 深度克隆id到块表记录 - /// 0x01 此方法不允许是未添加数据库的图元,因此它是id - /// 0x02 若为未添加数据库图元,则利用entity.Clone();同时不需要考虑动态块属性,可以使用entity.GetTransformedCopy + /// + /// 0x01 此方法不允许是未添加数据库的图元,因此它是id
+ /// 0x02 若为未添加数据库图元,则利用entity.Clone();同时不需要考虑动态块属性,可以使用entity.GetTransformedCopy + ///
///
/// - /// 克隆到当前块表记录,相当于原地克隆 - /// 克隆到目标块表记录内,相当于制作新块 + /// + /// 克隆到当前块表记录,相当于原地克隆
+ /// 克隆到目标块表记录内,相当于制作新块 + ///
/// /// 图元id集合,注意所有成员都要在同一个空间中 /// 克隆后的id词典 diff --git a/src/IFoxCAD.Cad.Shared/Runtime/AutoRegAssem.cs b/src/IFoxCAD.Cad.Shared/Runtime/AutoRegAssem.cs index 540762c..969f8a4 100644 --- a/src/IFoxCAD.Cad.Shared/Runtime/AutoRegAssem.cs +++ b/src/IFoxCAD.Cad.Shared/Runtime/AutoRegAssem.cs @@ -3,11 +3,13 @@ /// /// 注册中心 -/// 初始化程序集信息写入注册表并反射特性和接口 -/// 启动cad后的执行顺序为: -/// 1:程序集配置中心构造函数 -/// 2:特性..(多个) -/// 3:接口..(多个) +/// +/// 初始化程序集信息写入注册表并反射特性和接口
+/// 启动cad后的执行顺序为:
+/// 1:程序集配置中心构造函数
+/// 2:特性..(多个)
+/// 3:接口..(多个)
+///
///
public abstract class AutoRegAssem : IExtensionApplication { diff --git a/src/IFoxCAD.Cad.Shared/Runtime/Env.cs b/src/IFoxCAD.Cad.Shared/Runtime/Env.cs index 3d7e54d..c2348da 100644 --- a/src/IFoxCAD.Cad.Shared/Runtime/Env.cs +++ b/src/IFoxCAD.Cad.Shared/Runtime/Env.cs @@ -2,9 +2,11 @@ namespace IFoxCAD.Cad; /// /// 系统管理类 -/// 封装了一些系统 osmode、cmdecho、dimblk 系统变量 -/// 封装了常用的 文档 编辑器 数据库等对象为静态变量 -/// 封装了配置页面的注册表信息获取函数 +/// +/// 封装了一些系统 osmode;cmdecho;dimblk 系统变量
+/// 封装了常用的 文档 编辑器 数据库等对象为静态变量
+/// 封装了配置页面的注册表信息获取函数 +///
///
public static class Env { diff --git a/src/IFoxCAD.Cad.Shared/Runtime/IAutoGo.cs b/src/IFoxCAD.Cad.Shared/Runtime/IAutoGo.cs index 27ce550..52ee39c 100644 --- a/src/IFoxCAD.Cad.Shared/Runtime/IAutoGo.cs +++ b/src/IFoxCAD.Cad.Shared/Runtime/IAutoGo.cs @@ -75,9 +75,11 @@ public void Run() /// /// 此类作为加载后cad自动运行接口的一部分,用于反射特性和接口 -/// 启动cad后的执行顺序为: -/// 1:特性..(多个) -/// 2:接口..(多个) +/// +/// 启动cad后的执行顺序为:
+/// 1:特性..(多个)
+/// 2:接口..(多个) +///
///
public class AutoReflection { @@ -89,8 +91,10 @@ public class AutoReflection /// /// 反射执行 - /// 1.特性: - /// 2.接口: + /// + /// 1.特性:
+ /// 2.接口: + ///
///
/// 约束在此dll进行加速 public AutoReflection(string dllName, AutoRegConfig configInfo) diff --git a/tests/Test/CmdINI.cs b/tests/Test/CmdINI.cs index 7a882ec..68956a0 100644 --- a/tests/Test/CmdINI.cs +++ b/tests/Test/CmdINI.cs @@ -2,13 +2,14 @@ /// /// 注册中心(自动执行接口): -/// 用于启动cad后写入启动注册表及反射调用以下特性和接口 -/// netload的工程必须继承虚函数后才能使用特性和接口 -/// 启动cad后的执行顺序为: -/// 1:构造函数 -/// 2:特性..多个 -/// 3:接口..多个 -/// 4:本类的构造函数 +/// 用于启动cad后写入启动注册表及反射调用以下特性和接口
+/// netload的工程必须继承虚函数后才能使用特性和接口
+/// 启动cad后的执行顺序为:
+/// 1:构造函数
+/// 2:特性..多个
+/// 3:接口..多个
+/// 4:本类的构造函数 +///
///
public class CmdINI : AutoRegAssem { -- Gitee From 74f9702135658d343ca7b969902fc0a0c558d41a Mon Sep 17 00:00:00 2001 From: liuqihong <540762622@qq.com> Date: Wed, 17 Aug 2022 13:58:49 +0800 Subject: [PATCH 389/675] =?UTF-8?q?=E5=8F=96=E6=B6=88=E5=A4=9A=E4=BD=99?= =?UTF-8?q?=E5=A7=94=E6=89=98=E5=B9=B6=E8=BF=9B=E8=A1=8C=E5=8F=82=E6=95=B0?= =?UTF-8?q?=E7=A9=BA=E6=A3=80=E6=9F=A5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../ExtensionMethod/CurveEx.cs | 41 ++++++++++--------- 1 file changed, 22 insertions(+), 19 deletions(-) diff --git a/src/IFoxCAD.Cad.Shared/ExtensionMethod/CurveEx.cs b/src/IFoxCAD.Cad.Shared/ExtensionMethod/CurveEx.cs index 5eaab1b..03fab5f 100644 --- a/src/IFoxCAD.Cad.Shared/ExtensionMethod/CurveEx.cs +++ b/src/IFoxCAD.Cad.Shared/ExtensionMethod/CurveEx.cs @@ -20,22 +20,12 @@ public static double GetLength(this Curve curve) ///
/// 曲线 /// 打断参数表 - /// 参数表排序委托 - /// - /// 默认:按所提供的参数表进行分割打断
- /// 否则:按委托排序后的参数表进行分割打断 - ///
- /// /// 打断后曲线的集合 - public static IEnumerable GetSplitCurves(this Curve curve, - IEnumerable pars, - Func, IEnumerable>? func = null) + public static IEnumerable GetSplitCurves(this Curve curve, IEnumerable pars) { - if (pars == null) + if (pars is null) throw new ArgumentNullException(nameof(pars)); - if (func != null) - pars = func.Invoke(pars); return curve .GetSplitCurves(new DoubleCollection(pars.ToArray())) @@ -56,9 +46,14 @@ public static IEnumerable GetSplitCurves(this Curve curve, /// 打断后曲线的集合 public static IEnumerable GetSplitCurves(this Curve curve, IEnumerable pars, bool isOrder = false) { + if (pars is null) + throw new ArgumentNullException(nameof(pars)); + if (isOrder) + pars = pars.OrderBy(x => x); + return curve - .GetSplitCurves(new DoubleCollection(isOrder ? pars.OrderBy(x => x).ToArray() : pars.ToArray())) + .GetSplitCurves(new DoubleCollection(pars.ToArray())) .Cast(); } @@ -70,6 +65,8 @@ public static IEnumerable GetSplitCurves(this Curve curve, IEnumerable打断后曲线的集合 public static IEnumerable GetSplitCurves(this Curve curve, IEnumerable points) { + if (points is null) + throw new ArgumentNullException(nameof(points)); return curve .GetSplitCurves(new Point3dCollection(points.ToArray())) @@ -88,13 +85,13 @@ public static IEnumerable GetSplitCurves(this Curve curve, IEnumerable /// /// 打断后曲线的集合 - public static IEnumerable GetSplitCurves(this Curve curve, - IEnumerable points, - bool isOrder = false) + public static IEnumerable GetSplitCurves(this Curve curve, IEnumerable points, bool isOrder = false) { + if (points is null) + throw new ArgumentNullException(nameof(points)); if (isOrder) points = points.OrderBy(point => curve.GetParameterAtPoint( - curve.GetClosestPointTo(point, false))); + curve.GetClosestPointTo(point, false))); return curve .GetSplitCurves(new Point3dCollection(points.ToArray())) @@ -108,6 +105,9 @@ public static IEnumerable GetSplitCurves(this Curve curve, /// 所有的闭合环的曲线集合 public static IEnumerable GetAllCycle(this IEnumerable curves) { + if (curves is null) + throw new ArgumentNullException(nameof(curves)); + // 新建图 var graph = new Graph(); foreach (var curve in curves) @@ -140,6 +140,9 @@ public static IEnumerable GetAllCycle(this IEnumerable curves) /// 打断后的曲线列表 public static List BreakCurve(this List curves) { + if (curves is null) + throw new ArgumentNullException(nameof(curves)); + var geCurves = new List(); // 存储曲线转换后的复合曲线 var paramss = new List>(); // 存储每个曲线的交点参数值 @@ -707,7 +710,7 @@ public static void ChamferAt(this Polyline polyline, int index, double radius, b //将结果Ge曲线转为Db曲线,并将相关的数值反映到原曲线 var plTemp = c3ds[0].ToCurve() as Polyline; - if (plTemp == null) + if (plTemp is null) return; polyline.RemoveVertexAt(index); polyline.AddVertexAt(index, plTemp.GetPoint2dAt(1), plTemp.GetBulgeAt(1), 0, 0); @@ -716,5 +719,5 @@ public static void ChamferAt(this Polyline polyline, int index, double radius, b #endregion Polyline - #endregion Curve + #endregion } \ No newline at end of file -- Gitee From c701d62fc57bd098fe011afde1e304fb63beab32 Mon Sep 17 00:00:00 2001 From: zixuan203344 Date: Wed, 17 Aug 2022 09:34:36 +0000 Subject: [PATCH 390/675] update README.md. Signed-off-by: zixuan203344 --- README.md | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/README.md b/README.md index 1796e2a..1d37fda 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,10 @@ # IFoxCAD +#### 释义 +IFox中: +IF:为Inspire Function(中文名:跃动方程)的首字母缩写 +Fox:起初`雪山飞狐`(又狐哥)在明经论坛发布了[开源库](http://bbs.mjtd.com/thread-75701-1-1.html),后`落魄山人`(又小山山)基于狐哥的开源库创建了NFox开源项目,后经多重原因NFox被废弃,小山山又开创新开源项目,取名为`IF+Fox=IFox`,一语双关,简洁而不简单。 + #### 介绍 基于.NET的Cad二次开发类库。 -- Gitee From 7b17f56aa10cccf5297b09be8c37bcb53f9ee0b0 Mon Sep 17 00:00:00 2001 From: zixuan203344 Date: Wed, 17 Aug 2022 10:38:58 +0000 Subject: [PATCH 391/675] update README.md. Signed-off-by: zixuan203344 --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 1d37fda..e4ec848 100644 --- a/README.md +++ b/README.md @@ -2,8 +2,8 @@ #### 释义 IFox中: -IF:为Inspire Function(中文名:跃动方程)的首字母缩写 -Fox:起初`雪山飞狐`(又狐哥)在明经论坛发布了[开源库](http://bbs.mjtd.com/thread-75701-1-1.html),后`落魄山人`(又小山山)基于狐哥的开源库创建了NFox开源项目,后经多重原因NFox被废弃,小山山又开创新开源项目,取名为`IF+Fox=IFox`,一语双关,简洁而不简单。 +- IF:为Inspire Function(中文名:跃动方程)的首字母缩写 +- Fox:起初`雪山飞狐`(又狐哥)在明经论坛发布了[开源库](http://bbs.mjtd.com/thread-75701-1-1.html),后`落魄山人`(又小山山)基于狐哥的开源库创建了NFox开源项目,后经多重原因NFox被废弃,小山山又开创新开源项目,取名为`IF+Fox=IFox`,一语双关,简洁而不简单。 #### 介绍 -- Gitee From 4cdcf9ad0f37e62e90747e1a78284b149e2036af Mon Sep 17 00:00:00 2001 From: liuqihong <540762622@qq.com> Date: Wed, 17 Aug 2022 23:46:40 +0800 Subject: [PATCH 392/675] =?UTF-8?q?=E4=BF=AE=E6=94=B9=E5=91=BD=E5=90=8D?= =?UTF-8?q?=E7=A9=BA=E9=97=B4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/IFoxCAD.Cad.Shared/ExtensionMethod/BulgeVertexWidth.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/IFoxCAD.Cad.Shared/ExtensionMethod/BulgeVertexWidth.cs b/src/IFoxCAD.Cad.Shared/ExtensionMethod/BulgeVertexWidth.cs index dc7abd3..b09ee3f 100644 --- a/src/IFoxCAD.Cad.Shared/ExtensionMethod/BulgeVertexWidth.cs +++ b/src/IFoxCAD.Cad.Shared/ExtensionMethod/BulgeVertexWidth.cs @@ -1,4 +1,4 @@ -namespace IFoxCAD; +namespace IFoxCAD.Cad; /// /// 多段线的顶点,凸度,头宽,尾宽 -- Gitee From 451c88a87ffbf2a0a82518a593be2633fdfdcdac Mon Sep 17 00:00:00 2001 From: liuqihong <540762622@qq.com> Date: Thu, 18 Aug 2022 04:32:37 +0800 Subject: [PATCH 393/675] =?UTF-8?q?=E5=A2=9E=E5=8A=A0=E5=8A=A8=E6=80=81?= =?UTF-8?q?=E5=8A=A0=E8=BD=BD=E5=8A=9F=E8=83=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- IFoxCAD.sln | 14 +- .../ExtensionMethod/EntityEx.cs | 195 ++++++++--- .../IFoxCAD.Aacad08/IFoxCAD.Acad08.csproj | 2 +- .../IFoxCAD.Acad09plus.csproj | 2 +- src/IFoxCAD.LoadForm/AssemblyDependent.cs | 329 ++++++++++++++++++ src/IFoxCAD.LoadForm/CSharpUtils.cs | 69 ++++ .../DefaultAssemblyResolve.cs | 43 +++ src/IFoxCAD.LoadForm/GlobalUsings.cs | 16 + src/IFoxCAD.LoadForm/IFoxCAD.LoadForm.csproj | 41 +++ src/IFoxCAD.LoadForm/LoaderForm.Designer.cs | 135 +++++++ src/IFoxCAD.LoadForm/LoaderForm.cs | 82 +++++ src/IFoxCAD.LoadForm/LoaderForm.resx | 126 +++++++ tests/Test/Properties/Resources.Designer.cs | 63 ++++ tests/Test/Properties/Resources.resx | 101 ++++++ tests/Test/Test.csproj | 11 +- tests/Test/TestNetLoad.cs | 11 + 16 files changed, 1172 insertions(+), 68 deletions(-) create mode 100644 src/IFoxCAD.LoadForm/AssemblyDependent.cs create mode 100644 src/IFoxCAD.LoadForm/CSharpUtils.cs create mode 100644 src/IFoxCAD.LoadForm/DefaultAssemblyResolve.cs create mode 100644 src/IFoxCAD.LoadForm/GlobalUsings.cs create mode 100644 src/IFoxCAD.LoadForm/IFoxCAD.LoadForm.csproj create mode 100644 src/IFoxCAD.LoadForm/LoaderForm.Designer.cs create mode 100644 src/IFoxCAD.LoadForm/LoaderForm.cs create mode 100644 src/IFoxCAD.LoadForm/LoaderForm.resx create mode 100644 tests/Test/Properties/Resources.Designer.cs create mode 100644 tests/Test/Properties/Resources.resx create mode 100644 tests/Test/TestNetLoad.cs diff --git a/IFoxCAD.sln b/IFoxCAD.sln index 8e8cbd4..d147c14 100644 --- a/IFoxCAD.sln +++ b/IFoxCAD.sln @@ -19,6 +19,8 @@ Project("{D954291E-2A0B-460D-934E-DC6B0785DB48}") = "IFoxCAD.Cad.Shared", "src\I EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "IFoxCAD.Acad09plus", "src\IFoxCAD.Cad\IFoxCAD.Aacad09plus\IFoxCAD.Acad09plus.csproj", "{CC46E4E2-90DF-48F5-B3D7-83FAFED58F2A}" EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "IFoxCAD.LoadForm", "src\IFoxCAD.LoadForm\IFoxCAD.LoadForm.csproj", "{05FE8BF6-0C54-4114-9626-A2F76483BE20}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -49,18 +51,22 @@ Global {CC46E4E2-90DF-48F5-B3D7-83FAFED58F2A}.Debug|Any CPU.Build.0 = Debug|Any CPU {CC46E4E2-90DF-48F5-B3D7-83FAFED58F2A}.Release|Any CPU.ActiveCfg = Release|Any CPU {CC46E4E2-90DF-48F5-B3D7-83FAFED58F2A}.Release|Any CPU.Build.0 = Release|Any CPU + {05FE8BF6-0C54-4114-9626-A2F76483BE20}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {05FE8BF6-0C54-4114-9626-A2F76483BE20}.Debug|Any CPU.Build.0 = Debug|Any CPU + {05FE8BF6-0C54-4114-9626-A2F76483BE20}.Release|Any CPU.ActiveCfg = Release|Any CPU + {05FE8BF6-0C54-4114-9626-A2F76483BE20}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE EndGlobalSection - GlobalSection(ExtensibilityGlobals) = postSolution - SolutionGuid = {31D6C754-CF6B-4AB0-9861-6923DD312350} - EndGlobalSection GlobalSection(NestedProjects) = preSolution - {82FB8450-B971-4E30-859F-4B2DDB81F590} = {465C4E39-FBA2-417A-AB31-BDC01601F420} {36F87D06-88B3-45E3-A2A8-0FC737B25428} = {465C4E39-FBA2-417A-AB31-BDC01601F420} + {82FB8450-B971-4E30-859F-4B2DDB81F590} = {465C4E39-FBA2-417A-AB31-BDC01601F420} {CC46E4E2-90DF-48F5-B3D7-83FAFED58F2A} = {465C4E39-FBA2-417A-AB31-BDC01601F420} EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {31D6C754-CF6B-4AB0-9861-6923DD312350} + EndGlobalSection GlobalSection(SharedMSBuildProjectFiles) = preSolution src\IFoxCAD.Cad.Shared\IFoxCAD.Cad.Shared.projitems*{36f87d06-88b3-45e3-a2a8-0fc737b25428}*SharedItemsImports = 5 src\IFoxCAD.Cad.Shared\IFoxCAD.Cad.Shared.projitems*{82fb8450-b971-4e30-859f-4b2ddb81f590}*SharedItemsImports = 13 diff --git a/src/IFoxCAD.Cad.Shared/ExtensionMethod/EntityEx.cs b/src/IFoxCAD.Cad.Shared/ExtensionMethod/EntityEx.cs index 7a0b26f..92f619e 100644 --- a/src/IFoxCAD.Cad.Shared/ExtensionMethod/EntityEx.cs +++ b/src/IFoxCAD.Cad.Shared/ExtensionMethod/EntityEx.cs @@ -417,6 +417,9 @@ public static void ChangeBlockProperty(this BlockReference blockReference, #endregion #region 包围盒 + /// + /// 错误信息保存路径 + /// static string? _BoxLogAddress; /// /// 包围盒错误文件路径 @@ -432,23 +435,76 @@ public static string BoxLogAddress } static readonly HashSet _typeNames; + /// + /// + /// 为了保证更好的性能, + /// 只是在第一次调用此功能的时候进行读取, + /// 免得高频调用时候高频触发磁盘 + /// static EntityEx() { _typeNames = new(); - if (!File.Exists(BoxLogAddress)) return; + ExceptionToHashSet(); + } + + /// + /// 获取图元包围盒 + /// + /// + /// (左下角,右上角,是否有效) + /// 异常: + /// 会将包围盒类型记录到所属路径中,以此查询 + public static (Point3d min, Point3d max, bool IsEffective) GetBoundingBoxEx(this Entity ent) + { + //错误类型处理 + // if (ent is AttributeDefinition) //属性定义 + // return (Point3d.Origin, Point3d.Origin, false); + // else if (ent is Xline xline)//参照线 + // return (xline.BasePoint, xline.BasePoint, true); + // else if (ent is Ray ray)//射线 + // return (ray.BasePoint, ray.BasePoint, true); + + //指定类型处理 + if (ent is BlockReference brf) + try + { + var box = brf.GeometryExtentsBestFit(); + return (box.MinPoint, box.MaxPoint, true); + } + catch + { + //如果是一条参照线的组块,将导致获取包围盒时报错 + //0x01 是否需要进入块内,然后拿到每个图元的BasePoint再计算中点?感觉过于复杂. + //0x02 这个时候拿基点走就算了 + return (brf.Position, brf.Position, true); + } + else if (ent is MText mText) + { + //return GetMTextExtents(mText); + } + else if (!_typeNames.Contains(ent.GetType().Name)) //屏蔽天正等等缺失包围盒的类型 + try + { + return (ent.GeometricExtents.MinPoint, ent.GeometricExtents.MaxPoint, true); + } + catch (Exception e) { ExceptionToLog(e, ent); } + return (Point3d.Origin, Point3d.Origin, false); + } - var old = LogHelper.LogAddress; + + static void ExceptionToHashSet() + { + var old_LogAddress = LogHelper.LogAddress; try { LogHelper.OptionFile(BoxLogAddress); - var LogTxts = new FileLogger().ReadLog(); - - for (int i = 0; i < LogTxts.Length; i++) + var logTxts = new FileLogger().ReadLog(); + for (int i = 0; i < logTxts.Length; i++) { - var line = LogTxts[i]; - if (line.Contains("备注信息")) + var line = logTxts[i]; + if (line.Contains(nameof(LogTxt.备注信息))) { int index = line.IndexOf(":"); index = line.IndexOf("\"", index) + 1;//1是"\"" @@ -460,63 +516,98 @@ static EntityEx() } finally { - LogHelper.LogAddress = old; + LogHelper.LogAddress = old_LogAddress; } } - /// - /// 获取图元包围盒 - /// - /// - /// 返回包围盒点,若相同,则表示类型不存在包围盒 - /// 异常: - /// 会将包围盒类型记录到所属路径中,以此查询 - public static (Point3d min, Point3d max) GetBoundingBoxEx(this Entity ent) + static void ExceptionToLog(Exception e, Entity ent) { - //提前处理错误类型 - if (ent is AttributeDefinition) //属性定义 - return (Point3d.Origin, Point3d.Origin); - else if (ent is Xline xline)//参照线 - return (xline.BasePoint, xline.BasePoint); - else if (ent is Ray ray)//射线 - return (ray.BasePoint, ray.BasePoint); - + //无法处理的错误类型将被记录 + //如果错误无法try,而是cad直接致命错误,那么此处也不会被写入, + //这种情况无法避免程序安全性,总不能写了日志再去删除日志词条,这样会造成频繁IO的 + //遇到一个不认识的类型再去写入?然后记录它是否可以写入? + var old_LogAddress = LogHelper.LogAddress; + var old_FlagOutFile = LogHelper.FlagOutFile; try { - if (ent is BlockReference brf) - { - try - { - var box = brf.GeometryExtentsBestFit(); - return (box.MinPoint, box.MaxPoint); - } - catch - { - //如果是两条参照线的块,将导致无法获取包围盒就会报错 - //是否需要进入块内,然后拿到每个图元的BasePoint再计算中点??感觉过于复杂 - //这个时候拿基点走就算了 - return (brf.Position, brf.Position); - } - } - - if (!_typeNames.Contains(ent.GetType().Name)) - return (ent.GeometricExtents.MinPoint, ent.GeometricExtents.MaxPoint); - } - catch (Exception e) - { - //无法处理的错误类型将被记录 - var old = LogHelper.LogAddress; - var old2 = LogHelper.FlagOutFile; - LogHelper.FlagOutFile = true; LogHelper.OptionFile(BoxLogAddress); e.WriteLog(ent.GetType().Name, LogTarget.FileNotException); + } + finally + { + LogHelper.LogAddress = old_LogAddress; + LogHelper.FlagOutFile = old_FlagOutFile; + } + } + + /// + /// 获取图元包围框 + /// + /// 多行文字对象 + static Extents3d GetMTextExtents(MText mtxt) + { + double width = mtxt.ActualWidth; + double height = mtxt.ActualHeight; + double wl = 0.0; + double hb = 0.0; - LogHelper.LogAddress = old; - LogHelper.FlagOutFile = old2; + //对齐方式 + switch (mtxt.Attachment) + { + case AttachmentPoint.TopCenter: + case AttachmentPoint.MiddleCenter: + case AttachmentPoint.BottomCenter: + wl = width * -0.5; + break; + case AttachmentPoint.TopRight: + case AttachmentPoint.MiddleRight: + case AttachmentPoint.BottomRight: + wl = -width; + break; + } + switch (mtxt.Attachment) + { + case AttachmentPoint.TopLeft: + case AttachmentPoint.TopCenter: + case AttachmentPoint.TopRight: + hb = -height;//下边线到插入点的距离 + break; + case AttachmentPoint.MiddleLeft: + case AttachmentPoint.MiddleCenter: + case AttachmentPoint.MiddleRight: + hb = height * -0.5; + break; } - return (Point3d.Origin, Point3d.Origin); + + double wr = width + wl; + double ht = height + hb; + double degree = mtxt.Rotation; + Point3d center = mtxt.Location; + Point2d ptlb = new(center.X + wl, center.Y + hb); + Point2d ptrt = new(center.X + wr, center.Y + ht); + + //声明多段线 + Polyline pline = new(); + pline.SetDatabaseDefaults(); + //计算矩形的四个顶点 + Point2d p1 = new(Math.Min(ptlb.X, ptrt.X), Math.Min(ptlb.Y, ptrt.Y)); + Point2d p2 = new(Math.Max(ptlb.X, ptrt.X), Math.Min(ptlb.Y, ptrt.Y)); + Point2d p3 = new(Math.Max(ptlb.X, ptrt.X), Math.Max(ptlb.Y, ptrt.Y)); + Point2d p4 = new(Math.Min(ptlb.X, ptrt.X), Math.Max(ptlb.Y, ptrt.Y)); + //添加多段线的顶点 + pline.AddVertexAt(0, p1, 0, 0, 0); + pline.AddVertexAt(1, p2, 0, 0, 0); + pline.AddVertexAt(2, p3, 0, 0, 0); + pline.AddVertexAt(3, p4, 0, 0, 0); + //闭合多段线 + pline.Closed = true; + pline.TransformBy(Matrix3d.Rotation(degree, Vector3d.ZAxis, center)); + return pline.GeometricExtents; } + + #endregion + #endregion } diff --git a/src/IFoxCAD.Cad/IFoxCAD.Aacad08/IFoxCAD.Acad08.csproj b/src/IFoxCAD.Cad/IFoxCAD.Aacad08/IFoxCAD.Acad08.csproj index c3d35c1..91a82ab 100644 --- a/src/IFoxCAD.Cad/IFoxCAD.Aacad08/IFoxCAD.Acad08.csproj +++ b/src/IFoxCAD.Cad/IFoxCAD.Aacad08/IFoxCAD.Acad08.csproj @@ -1,4 +1,4 @@ - + net35;net40;net45 diff --git a/src/IFoxCAD.Cad/IFoxCAD.Aacad09plus/IFoxCAD.Acad09plus.csproj b/src/IFoxCAD.Cad/IFoxCAD.Aacad09plus/IFoxCAD.Acad09plus.csproj index c55d4f6..956e46a 100644 --- a/src/IFoxCAD.Cad/IFoxCAD.Aacad09plus/IFoxCAD.Acad09plus.csproj +++ b/src/IFoxCAD.Cad/IFoxCAD.Aacad09plus/IFoxCAD.Acad09plus.csproj @@ -1,4 +1,4 @@ - + net35;net40;net45 diff --git a/src/IFoxCAD.LoadForm/AssemblyDependent.cs b/src/IFoxCAD.LoadForm/AssemblyDependent.cs new file mode 100644 index 0000000..f4586ce --- /dev/null +++ b/src/IFoxCAD.LoadForm/AssemblyDependent.cs @@ -0,0 +1,329 @@ +namespace LoadEx; + +/// +/// 加载程序集和加载状态 +/// +public struct LoadState +{ + public Assembly? Assembly; + public string DllFullName; + public bool State; + public LoadState(string dllFullName, bool state, Assembly? assembly = null) + { + DllFullName = dllFullName; + State = state; + Assembly = assembly; + } +} + +[HarmonyPatch("Autodesk.AutoCAD.ApplicationServices.ExtensionLoader", "OnAssemblyLoad")] +public class AssemblyDependent : IDisposable +{ + #region 公共 + /// + /// 当前域加载事件,运行时出错的话,就靠这个事件来解决 + /// + public event ResolveEventHandler CurrentDomainAssemblyResolveEvent + { + add { AppDomain.CurrentDomain.AssemblyResolve += value; } + remove { AppDomain.CurrentDomain.AssemblyResolve -= value; } + } + + + #endregion + + #region 字段 + + + /// + /// cad程序域依赖_内存区(不可以卸载) + /// + readonly Assembly[] _cadAssembly; + /// + /// cad程序域依赖_映射区(不可以卸载) + /// + readonly Assembly[] _cadAssemblyRef; + #endregion + + #region 构造 + /// + /// 链式加载dll依赖 + /// + public AssemblyDependent() + { + //初始化一次,反复load,所以这里只放公共元素 + _cadAssembly = AppDomain.CurrentDomain.GetAssemblies(); + _cadAssemblyRef = AppDomain.CurrentDomain.ReflectionOnlyGetAssemblies(); + CurrentDomainAssemblyResolveEvent += AssemblyHelper.DefaultAssemblyResolve; + } + #endregion + + #region 获取加载链 + + /// + /// 加载程序集 + /// + /// dll的文件位置 + /// 返回加载链 + /// true字节加载,false文件加载 + /// 参数 加载成功标识 + /// 链条后面的不再理会,因为相同的dll引用辨识无意义 + /// + public bool Load(string dllFullName, List loadStates, bool byteLoad = true) + { + //var accAsb = typeof(Document).Assembly; + //CSharpUtils.ReplaceMethod( + // accAsb.GetType("Autodesk.AutoCAD.ApplicationServices.ExtensionLoader"), + // "OnAssemblyLoad", typeof(AssemblyDependent), "OnAssemblyLoad", + // BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Static | BindingFlags.Instance); + + if (dllFullName == null) + throw new ArgumentNullException(nameof(dllFullName)); + + dllFullName = Path.GetFullPath(dllFullName);//相对路径要先转换 + if (!File.Exists(dllFullName)) + throw new ArgumentException("路径不存在"); + + List allRefs = new(); + GetAllRefPaths(dllFullName, allRefs); + + bool dllFullNameLoad = false; + + //查询加载链逆向加载,确保前面不丢失 + //这里有问题,从尾巴开始的,就一定是没有任何引用吗? + for (int i = allRefs.Count - 1; i >= 0; i--) + { + var path = allRefs[i]; + + //路径转程序集名 + var an = AssemblyName.GetAssemblyName(path).FullName; + var assembly = _cadAssembly.FirstOrDefault(a => a.FullName == an); + if (assembly != null) + { + loadStates.Add(new LoadState(path, false));//版本号没变不加载 + continue; + } + + //有一次true,就是true + if (path == dllFullName) + dllFullNameLoad = true; + + try + { + var ass = GetPdbAssembly(path, byteLoad); + if (ass == null) + { + if (byteLoad) + ass = Assembly.Load(File.ReadAllBytes(path)); + else + ass = Assembly.LoadFile(path); + } + loadStates.Add(new LoadState(path, true, ass));/*加载成功*/ + } + catch { loadStates.Add(new LoadState(path, false));/*错误造成*/ } + } + return dllFullNameLoad; + } + + /// + /// 在debug模式的时候才获取PBD调试信息 + /// + /// + /// + /// + Assembly? GetPdbAssembly(string path, bool byteLoad) + { +#if DEBUG + //为了实现Debug时候出现断点,见链接,加依赖 + // https://www.cnblogs.com/DasonKwok/p/10510218.html + // https://www.cnblogs.com/DasonKwok/p/10523279.html + + var dir = Path.GetDirectoryName(path); + var pdbName = Path.GetFileNameWithoutExtension(path) + ".pdb"; + var pdbFullName = Path.Combine(dir, pdbName); + if (File.Exists(pdbFullName) && byteLoad) + return Assembly.Load(File.ReadAllBytes(path), File.ReadAllBytes(pdbFullName)); + return null; +#endif + } + + /// + /// 递归获取加载链 + /// + /// + /// 返回的集合 + /// + void GetAllRefPaths(string dllFullName, List dllFullNamesOut) + { + if (dllFullNamesOut.Contains(dllFullName) || !File.Exists(dllFullName)) + return; + dllFullNamesOut.Add(dllFullName); + + //路径转程序集名 + var assName = AssemblyName.GetAssemblyName(dllFullName).FullName; + + //在当前程序域的 assemblyAs内存区 和 assemblyAsRef映射区 找这个程序集名 + var assemblyAs = _cadAssembly.FirstOrDefault(ass => ass.FullName == assName); + Assembly assemblyAsRef; + + //内存区有表示加载过 + //映射区有表示查找过,但没有加载(一般来说不存在.只是debug会注释掉 Assembly.Load 的时候用来测试) + if (assemblyAs != null) + { + assemblyAsRef = assemblyAs; + } + else + { + assemblyAsRef = _cadAssemblyRef.FirstOrDefault(ass => ass.FullName == assName); + + //内存区和映射区都没有的话就把dll加载到映射区,用来找依赖表 + if (assemblyAsRef == null) + { + var byteRef = File.ReadAllBytes(dllFullName); +#if true2 + //1548253108:这里会报错,但是我测试不出来它报错.....他提供了解决方案 + //var accAsb = typeof(Document).Assembly; + const string ext = "Autodesk.AutoCAD.ApplicationServices.ExtensionLoader"; + Harmony hm = new(ext); + hm.PatchAll(); + assemblyAsRef = Assembly.ReflectionOnlyLoad(byteRef); + hm.UnpatchAll(ext); + //CSharpUtils.RestoreMethod(); +#else + // assemblyAsRef = Assembly.ReflectionOnlyLoad(dllFullName); //没有依赖会直接报错 + assemblyAsRef = Assembly.ReflectionOnlyLoad(byteRef); +#endif + } + } + + var sb = new StringBuilder(); + //dll拖拉加载路径-搜索路径(可以增加到这个dll下面的所有文件夹?) + sb.Append(Path.GetDirectoryName(dllFullName)); + sb.Append("\\"); + + //遍历依赖,如果存在dll拖拉加载目录就加入dlls集合 + var asse = assemblyAsRef.GetReferencedAssemblies(); + for (int i = 0; i < asse.Length; i++) + { + var path = sb.ToString() + asse[i].Name; + var paths = new string[] + { + path + ".dll", + path + ".exe" + }; + for (int j = 0; j < paths.Length; j++) + GetAllRefPaths(paths[j], dllFullNamesOut); + } + } + + /// + /// 加载信息 + /// + public static string? PrintMessage(List loadStates) + { + if (loadStates == null) + return null; + + var sb = new StringBuilder(); + bool flag = true; + foreach (var item in loadStates) + { + if (!item.State) + { + sb.Append(Environment.NewLine); + sb.Append("** "); + sb.Append(item.DllFullName); + sb.Append(Environment.NewLine); + sb.Append("** 此文件已加载,同时重复名称和版本号,跳过!"); + sb.Append(Environment.NewLine); + flag = false; + } + } + if (flag) + { + sb.Append(Environment.NewLine); + sb.Append("** 链式加载成功!"); + sb.Append(Environment.NewLine); + } + return sb.ToString(); + } + #endregion + + #region 删除文件 + /// + /// 递归删除文件夹目录及文件 + /// + /// + /// + static void DeleteFolder(string dir) + { + if (!Directory.Exists(dir)) //如果存在这个文件夹删除之 + return; + foreach (string d in Directory.GetFileSystemEntries(dir)) + { + if (File.Exists(d)) + File.Delete(d); //直接删除其中的文件 + else + DeleteFolder(d); //递归删除子文件夹 + } + Directory.Delete(dir, true); //删除已空文件夹 + } + + /// + /// Debug的时候删除obj目录,防止占用 + /// + /// dll文件位置 + public void DebugDelObjFiles(string dllFullName) + { + var filename = Path.GetFileNameWithoutExtension(dllFullName); + var path = Path.GetDirectoryName(dllFullName); + + var pdb = path + "\\" + filename + ".pdb"; + if (File.Exists(pdb)) + File.Delete(pdb); + + var list = path.Split('\\'); + if (list[list.Length - 1] == "Debug" && list[list.Length - 2] == "bin") + { + var projobj = path.Substring(0, path.LastIndexOf("bin")) + "obj"; + DeleteFolder(projobj); + } + } + #endregion + + #region Dispose + public bool Disposed = false; + + /// + /// 显式调用Dispose方法,继承IDisposable + /// + public void Dispose() + { + //由手动释放 + Dispose(true); + //通知垃圾回收机制不再调用终结器(析构器)_跑了这里就不会跑析构函数了 + GC.SuppressFinalize(this); + } + + /// + /// 析构函数,以备忘记了显式调用Dispose方法 + /// + ~AssemblyDependent() + { + Dispose(false); //由系统释放 + } + + /// + /// 释放 + /// + /// + protected virtual void Dispose(bool ing) + { + if (Disposed) return; //不重复释放 + Disposed = true;//让类型知道自己已经被释放 + + CurrentDomainAssemblyResolveEvent -= AssemblyHelper.DefaultAssemblyResolve; + GC.Collect(); + } + #endregion +} diff --git a/src/IFoxCAD.LoadForm/CSharpUtils.cs b/src/IFoxCAD.LoadForm/CSharpUtils.cs new file mode 100644 index 0000000..bae995c --- /dev/null +++ b/src/IFoxCAD.LoadForm/CSharpUtils.cs @@ -0,0 +1,69 @@ +namespace LoadEx; + +internal class CSharpUtils +{ + private static unsafe long* addrOfOrgMethodAddr; + private static unsafe long* orgMethodAddr; + + public static bool ReplaceMethod(Type targetType, string targetMethod, + Type injectType, string injectMethod, + BindingFlags bindingAttr = BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.Public, + Binder? binder = null, + CallingConventions callConvention = CallingConventions.Any, + Type[]? types = null, + ParameterModifier[]? modifiers = null) + { + if (types == null) + types = Type.EmptyTypes; + + MethodInfo tarMethod = targetType.GetMethod(targetMethod, bindingAttr, binder, callConvention, types, modifiers); + MethodInfo injMethod = injectType.GetMethod(injectMethod, bindingAttr, binder, callConvention, types, modifiers); + + var tarMethods = injectType.GetMethods( + BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.Static); + var injMethods = injectType.GetMethods( + BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.Static); + + foreach (var item in tarMethods) + if (item.Name.Equals(targetMethod)) + tarMethod = item; + foreach (var item2 in injMethods) + if (item2.Name.Equals(injectMethod)) + injMethod = item2; + + if (tarMethod == null || injMethod == null) + return false; + RuntimeHelpers.PrepareMethod(tarMethod.MethodHandle); + RuntimeHelpers.PrepareMethod(injMethod.MethodHandle); + unsafe + { + if (IntPtr.Size == 4) + { + addrOfOrgMethodAddr = (long*)((int*)tarMethod.MethodHandle.Value.ToPointer() + 2); + orgMethodAddr = (long*)*addrOfOrgMethodAddr; + int* tar = (int*)tarMethod.MethodHandle.Value.ToPointer() + 2; + int* inj = (int*)injMethod.MethodHandle.Value.ToPointer() + 2; + *tar = *inj; + } + else + { + addrOfOrgMethodAddr = (long*)tarMethod.MethodHandle.Value.ToPointer() + 1; + orgMethodAddr = (long*)*addrOfOrgMethodAddr; + long* tar = (long*)tarMethod.MethodHandle.Value.ToPointer() + 1; + long* inj = (long*)injMethod.MethodHandle.Value.ToPointer() + 1; + *tar = *inj; + } + } + return true; + } + public unsafe static void RestoreMethod() + { + if ((long)orgMethodAddr != 0 && (long)addrOfOrgMethodAddr != 0) + { + if (IntPtr.Size == 4) + *(int*)addrOfOrgMethodAddr = (int)orgMethodAddr; + else + *addrOfOrgMethodAddr = (long)orgMethodAddr; + } + } +} diff --git a/src/IFoxCAD.LoadForm/DefaultAssemblyResolve.cs b/src/IFoxCAD.LoadForm/DefaultAssemblyResolve.cs new file mode 100644 index 0000000..59eed24 --- /dev/null +++ b/src/IFoxCAD.LoadForm/DefaultAssemblyResolve.cs @@ -0,0 +1,43 @@ +namespace LoadEx; + +public class AssemblyHelper +{ + #region 程序域运行事件 + /// + /// 程序域运行事件 + /// + /// 动态加载要注意所有的引用外的dll的加载顺序 + /// Acad2008若没有这个事件,会使动态命令执行时候无法引用当前的程序集函数 + /// 跨程序集反射 + /// 动态加载时,dll的地址会在系统的动态目录里,而它所处的程序集(运行域)是在动态目录里. + /// netload会把所处的运行域给改到cad自己的,而动态加载不通过netload,所以要自己去改. + /// 这相当于是dll注入的意思,只是动态加载的这个"dll"不存在实体,只是一段内存. + /// + /// + public static Assembly? DefaultAssemblyResolve(object sender, ResolveEventArgs args) + { + var cadAss = AppDomain.CurrentDomain.GetAssemblies(); + + //名称和版本号都一致的,调用它 + var load = cadAss.FirstOrDefault(ass => ass.GetName().FullName == args.Name); + if (load != null) + return load; + + //获取名称一致,但是版本号不同的,调用最后的可用版本 + var ag = args.Name.Split(',')[0]; + + //获取最后一个符合条件的, + //否则a.dll引用b.dll函数的时候,b.dll修改重生成之后,加载进去会调用第一个版本的b.dll + foreach (var item in cadAss) + { + if (item.GetName().FullName.Split(',')[0] != ag) + continue; + + //为什么加载的程序版本号最后要是* + //因为vs会帮你迭代这个版本号,所以最后的可用就是循环到最后的. + load = item; + } + return load; + } + #endregion +} \ No newline at end of file diff --git a/src/IFoxCAD.LoadForm/GlobalUsings.cs b/src/IFoxCAD.LoadForm/GlobalUsings.cs new file mode 100644 index 0000000..c81107c --- /dev/null +++ b/src/IFoxCAD.LoadForm/GlobalUsings.cs @@ -0,0 +1,16 @@ +/// 系统引用 +global using System; +global using System.Collections.Generic; +global using System.IO; +global using System.Linq; +global using System.Text; +global using System.Reflection; +global using System.Runtime.CompilerServices; + +/// autocad 引用 +global using Autodesk.AutoCAD.ApplicationServices; +global using Autodesk.AutoCAD.Runtime; +global using Acap = Autodesk.AutoCAD.ApplicationServices.Application; +global using Autodesk.AutoCAD.EditorInput; + +global using HarmonyLib; diff --git a/src/IFoxCAD.LoadForm/IFoxCAD.LoadForm.csproj b/src/IFoxCAD.LoadForm/IFoxCAD.LoadForm.csproj new file mode 100644 index 0000000..381ed8e --- /dev/null +++ b/src/IFoxCAD.LoadForm/IFoxCAD.LoadForm.csproj @@ -0,0 +1,41 @@ + + + + preview + enable + + NET35;NET40;NET45 + true + true + x64 + True + + + + + + + + + + + + + + + + + + + + + $(Configuration);ac2009 + + + $(Configuration);ac2013 + + + $(Configuration);ac2015 + + + diff --git a/src/IFoxCAD.LoadForm/LoaderForm.Designer.cs b/src/IFoxCAD.LoadForm/LoaderForm.Designer.cs new file mode 100644 index 0000000..e218578 --- /dev/null +++ b/src/IFoxCAD.LoadForm/LoaderForm.Designer.cs @@ -0,0 +1,135 @@ +namespace LoadEx; + +partial class LoaderForm +{ + /// + /// Required designer variable. + /// + private System.ComponentModel.IContainer components = null; + + /// + /// Clean up any resources being used. + /// + /// true if managed resources should be disposed; otherwise, false. + protected override void Dispose(bool disposing) + { + if (disposing && (components != null)) + { + components.Dispose(); + } + base.Dispose(disposing); + } + + #region Windows Form Designer generated code + + /// + /// Required method for Designer support - do not modify + /// the contents of this method with the code editor. + /// + private void InitializeComponent() + { + this.components = new System.ComponentModel.Container(); + this.loadDlldialog = new System.Windows.Forms.OpenFileDialog(); + this.textBox1 = new System.Windows.Forms.TextBox(); + this.button1 = new System.Windows.Forms.Button(); + this.label1 = new System.Windows.Forms.Label(); + this.button2 = new System.Windows.Forms.Button(); + this.label2 = new System.Windows.Forms.Label(); + this.toolTip1 = new System.Windows.Forms.ToolTip(this.components); + this.SuspendLayout(); + // + // textBox1 + // + this.textBox1.Location = new System.Drawing.Point(88, 35); + this.textBox1.Name = "textBox1"; + this.textBox1.ReadOnly = true; + this.textBox1.Size = new System.Drawing.Size(634, 34); + this.textBox1.TabIndex = 0; + this.textBox1.TextChanged += new System.EventHandler(this.TextBox1_TextChanged); + // + // button1 + // + this.button1.AutoSize = true; + this.button1.Location = new System.Drawing.Point(743, 30); + this.button1.Name = "button1"; + this.button1.Size = new System.Drawing.Size(177, 42); + this.button1.TabIndex = 1; + this.button1.Text = "手动选择DLL文件"; + this.button1.UseVisualStyleBackColor = true; + this.button1.Click += new System.EventHandler(this.Button1_Click); + // + // label1 + // + this.label1.AutoSize = true; + this.label1.Font = new System.Drawing.Font("微软雅黑", 12F, System.Drawing.FontStyle.Bold, System.Drawing.GraphicsUnit.Point, ((byte)(134))); + this.label1.ForeColor = System.Drawing.Color.Red; + this.label1.Location = new System.Drawing.Point(25, 97); + this.label1.Name = "label1"; + this.label1.Size = new System.Drawing.Size(428, 27); + this.label1.TabIndex = 2; + this.label1.Text = "直接拖拽DLL文件到本窗体可自动加载DLL文件"; + // + // button2 + // + this.button2.AutoSize = true; + this.button2.Location = new System.Drawing.Point(743, 89); + this.button2.Name = "button2"; + this.button2.Size = new System.Drawing.Size(177, 42); + this.button2.TabIndex = 1; + this.button2.Text = "加载"; + this.button2.UseVisualStyleBackColor = true; + this.button2.Click += new System.EventHandler(this.Button2_Click); + // + // label2 + // + this.label2.AutoSize = true; + this.label2.Location = new System.Drawing.Point(25, 38); + this.label2.Name = "label2"; + this.label2.Size = new System.Drawing.Size(57, 27); + this.label2.TabIndex = 3; + this.label2.Text = "路径:"; + // + // toolTip1 + // + this.toolTip1.AutomaticDelay = 200; + this.toolTip1.AutoPopDelay = 3000; + this.toolTip1.InitialDelay = 200; + this.toolTip1.ReshowDelay = 40; + this.toolTip1.ToolTipIcon = System.Windows.Forms.ToolTipIcon.Info; + // + // LoaderForm + // + this.AllowDrop = true; + this.AutoScaleDimensions = new System.Drawing.SizeF(12F, 27F); + this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; + this.ClientSize = new System.Drawing.Size(944, 160); + this.Controls.Add(this.label2); + this.Controls.Add(this.label1); + this.Controls.Add(this.button2); + this.Controls.Add(this.button1); + this.Controls.Add(this.textBox1); + this.Font = new System.Drawing.Font("微软雅黑", 12F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(134))); + this.FormBorderStyle = System.Windows.Forms.FormBorderStyle.FixedSingle; + this.Margin = new System.Windows.Forms.Padding(4, 5, 4, 5); + this.MaximizeBox = false; + this.MinimizeBox = false; + this.Name = "LoaderForm"; + this.Text = ".net dll 加载器"; + this.Load += new System.EventHandler(this.LoaderForm_Load); + this.DragDrop += new System.Windows.Forms.DragEventHandler(this.LoaderForm_DragDrop); + this.DragEnter += new System.Windows.Forms.DragEventHandler(this.LoaderForm_DragEnter); + this.ResumeLayout(false); + this.PerformLayout(); + + } + + #endregion + + private System.Windows.Forms.OpenFileDialog loadDlldialog; + private System.Windows.Forms.TextBox textBox1; + private System.Windows.Forms.Button button1; + private System.Windows.Forms.Label label1; + private System.Windows.Forms.Button button2; + private System.Windows.Forms.Label label2; + private System.Windows.Forms.ToolTip toolTip1; +} \ No newline at end of file diff --git a/src/IFoxCAD.LoadForm/LoaderForm.cs b/src/IFoxCAD.LoadForm/LoaderForm.cs new file mode 100644 index 0000000..74fb05f --- /dev/null +++ b/src/IFoxCAD.LoadForm/LoaderForm.cs @@ -0,0 +1,82 @@ +namespace LoadEx; + +using System.Windows.Forms; + +public partial class LoaderForm : Form +{ + string? _dllPath; + public LoaderForm() + { + InitializeComponent(); + } + + static void LoadDll(string path) + { + var ed = Acap.DocumentManager.MdiActiveDocument.Editor; + var ad = new AssemblyDependent(); + var ls = new List(); + ad.Load(path, ls); + ed.WriteMessage(AssemblyDependent.PrintMessage(ls)); + } + + void TextBox1_TextChanged(object sender, EventArgs e) + { + _dllPath = textBox1.Text; + toolTip1.SetToolTip(textBox1, Path.GetFullPath(textBox1.Text)); + } + + void LoaderForm_Load(object sender, EventArgs e) + { + if (_dllPath != null) + textBox1.Text = _dllPath; + } + + void Button1_Click(object sender, EventArgs e) + { + loadDlldialog.Filter = "dll文件(*.dll)|*.dll"; + loadDlldialog.CheckFileExists = true; + + var dr = loadDlldialog.ShowDialog(); + if (dr == DialogResult.OK) + textBox1.Text = Path.GetFullPath(loadDlldialog.FileName); + } + + void Button2_Click(object sender, EventArgs e) + { + if (!File.Exists(textBox1.Text)) + { + MessageBox.Show($"文件 {textBox1.Text} 不存在!", "提示", MessageBoxButtons.OK); + return; + } + + LoadDll(textBox1.Text); + MessageBox.Show("加载完毕!"); + } + + void LoaderForm_DragDrop(object sender, DragEventArgs e) + { + int i = 0; + var dllPaths = (string[])e.Data.GetData(DataFormats.FileDrop); + foreach (var item in dllPaths) + if (item.EndsWith(".dll")) + { + LoadDll(item); + i++; + if (i == 1) + { + textBox1.Text = item; + _dllPath = item; + } + } + MessageBox.Show("加载完毕!"); + } + + void LoaderForm_DragEnter(object sender, DragEventArgs e) + { + if (e.Data.GetDataPresent(DataFormats.FileDrop)) + e.Effect = DragDropEffects.All; + else + e.Effect = DragDropEffects.None; + } +} + diff --git a/src/IFoxCAD.LoadForm/LoaderForm.resx b/src/IFoxCAD.LoadForm/LoaderForm.resx new file mode 100644 index 0000000..7278b0e --- /dev/null +++ b/src/IFoxCAD.LoadForm/LoaderForm.resx @@ -0,0 +1,126 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + 17, 17 + + + 172, 17 + + \ No newline at end of file diff --git a/tests/Test/Properties/Resources.Designer.cs b/tests/Test/Properties/Resources.Designer.cs new file mode 100644 index 0000000..1617020 --- /dev/null +++ b/tests/Test/Properties/Resources.Designer.cs @@ -0,0 +1,63 @@ +//------------------------------------------------------------------------------ +// +// 此代码由工具生成。 +// 运行时版本:4.0.30319.42000 +// +// 对此文件的更改可能会导致不正确的行为,并且如果 +// 重新生成代码,这些更改将会丢失。 +// +//------------------------------------------------------------------------------ + +namespace Test.Properties { + using System; + + + /// + /// 一个强类型的资源类,用于查找本地化的字符串等。 + /// + // 此类是由 StronglyTypedResourceBuilder + // 类通过类似于 ResGen 或 Visual Studio 的工具自动生成的。 + // 若要添加或移除成员,请编辑 .ResX 文件,然后重新运行 ResGen + // (以 /str 作为命令选项),或重新生成 VS 项目。 + [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "17.0.0.0")] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] + internal class Resources { + + private static global::System.Resources.ResourceManager resourceMan; + + private static global::System.Globalization.CultureInfo resourceCulture; + + [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")] + internal Resources() { + } + + /// + /// 返回此类使用的缓存的 ResourceManager 实例。 + /// + [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] + internal static global::System.Resources.ResourceManager ResourceManager { + get { + if (object.ReferenceEquals(resourceMan, null)) { + global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("Test.Properties.Resources", typeof(Resources).Assembly); + resourceMan = temp; + } + return resourceMan; + } + } + + /// + /// 重写当前线程的 CurrentUICulture 属性,对 + /// 使用此强类型资源类的所有资源查找执行重写。 + /// + [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] + internal static global::System.Globalization.CultureInfo Culture { + get { + return resourceCulture; + } + set { + resourceCulture = value; + } + } + } +} diff --git a/tests/Test/Properties/Resources.resx b/tests/Test/Properties/Resources.resx new file mode 100644 index 0000000..4fdb1b6 --- /dev/null +++ b/tests/Test/Properties/Resources.resx @@ -0,0 +1,101 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 1.3 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.3500.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.3500.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + \ No newline at end of file diff --git a/tests/Test/Test.csproj b/tests/Test/Test.csproj index 33cb53e..739836a 100644 --- a/tests/Test/Test.csproj +++ b/tests/Test/Test.csproj @@ -1,5 +1,4 @@  - preview @@ -10,17 +9,9 @@ x64 - - 1701;1702;CS1685 - - - - 1701;1702;CS1685 - - + - diff --git a/tests/Test/TestNetLoad.cs b/tests/Test/TestNetLoad.cs new file mode 100644 index 0000000..5fe8d53 --- /dev/null +++ b/tests/Test/TestNetLoad.cs @@ -0,0 +1,11 @@ +namespace Test; + +public class NetLoad +{ + [CommandMethod("Test_NetLoad")] + public static void Test_NetLoad() + { + LoadEx.LoaderForm form = new(); + Acap.ShowModalDialog(form); + } +} -- Gitee From 0f49b793fc7a44086f2ae2f90f16f1e414e9f1ae Mon Sep 17 00:00:00 2001 From: liuqihong <540762622@qq.com> Date: Thu, 18 Aug 2022 04:34:49 +0800 Subject: [PATCH 394/675] =?UTF-8?q?=E4=BF=AE=E6=94=B9=E5=91=BD=E5=90=8D?= =?UTF-8?q?=E7=A9=BA=E9=97=B4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- IFoxCAD.sln | 2 +- src/IFoxCAD.LoadForm/AssemblyDependent.cs | 2 +- src/IFoxCAD.LoadForm/CSharpUtils.cs | 2 +- src/IFoxCAD.LoadForm/DefaultAssemblyResolve.cs | 2 +- .../{IFoxCAD.LoadForm.csproj => IFoxCAD.LoadEx.csproj} | 0 src/IFoxCAD.LoadForm/LoaderForm.Designer.cs | 2 +- src/IFoxCAD.LoadForm/LoaderForm.cs | 2 +- tests/Test/Test.csproj | 2 +- tests/Test/TestNetLoad.cs | 2 +- 9 files changed, 8 insertions(+), 8 deletions(-) rename src/IFoxCAD.LoadForm/{IFoxCAD.LoadForm.csproj => IFoxCAD.LoadEx.csproj} (100%) diff --git a/IFoxCAD.sln b/IFoxCAD.sln index d147c14..bba427d 100644 --- a/IFoxCAD.sln +++ b/IFoxCAD.sln @@ -19,7 +19,7 @@ Project("{D954291E-2A0B-460D-934E-DC6B0785DB48}") = "IFoxCAD.Cad.Shared", "src\I EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "IFoxCAD.Acad09plus", "src\IFoxCAD.Cad\IFoxCAD.Aacad09plus\IFoxCAD.Acad09plus.csproj", "{CC46E4E2-90DF-48F5-B3D7-83FAFED58F2A}" EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "IFoxCAD.LoadForm", "src\IFoxCAD.LoadForm\IFoxCAD.LoadForm.csproj", "{05FE8BF6-0C54-4114-9626-A2F76483BE20}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "IFoxCAD.LoadEx", "src\IFoxCAD.LoadForm\IFoxCAD.LoadEx.csproj", "{05FE8BF6-0C54-4114-9626-A2F76483BE20}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution diff --git a/src/IFoxCAD.LoadForm/AssemblyDependent.cs b/src/IFoxCAD.LoadForm/AssemblyDependent.cs index f4586ce..51ba6aa 100644 --- a/src/IFoxCAD.LoadForm/AssemblyDependent.cs +++ b/src/IFoxCAD.LoadForm/AssemblyDependent.cs @@ -1,4 +1,4 @@ -namespace LoadEx; +namespace IFoxCAD.LoadEx; /// /// 加载程序集和加载状态 diff --git a/src/IFoxCAD.LoadForm/CSharpUtils.cs b/src/IFoxCAD.LoadForm/CSharpUtils.cs index bae995c..346ed5e 100644 --- a/src/IFoxCAD.LoadForm/CSharpUtils.cs +++ b/src/IFoxCAD.LoadForm/CSharpUtils.cs @@ -1,4 +1,4 @@ -namespace LoadEx; +namespace IFoxCAD.LoadEx; internal class CSharpUtils { diff --git a/src/IFoxCAD.LoadForm/DefaultAssemblyResolve.cs b/src/IFoxCAD.LoadForm/DefaultAssemblyResolve.cs index 59eed24..e99bb5c 100644 --- a/src/IFoxCAD.LoadForm/DefaultAssemblyResolve.cs +++ b/src/IFoxCAD.LoadForm/DefaultAssemblyResolve.cs @@ -1,4 +1,4 @@ -namespace LoadEx; +namespace IFoxCAD.LoadEx; public class AssemblyHelper { diff --git a/src/IFoxCAD.LoadForm/IFoxCAD.LoadForm.csproj b/src/IFoxCAD.LoadForm/IFoxCAD.LoadEx.csproj similarity index 100% rename from src/IFoxCAD.LoadForm/IFoxCAD.LoadForm.csproj rename to src/IFoxCAD.LoadForm/IFoxCAD.LoadEx.csproj diff --git a/src/IFoxCAD.LoadForm/LoaderForm.Designer.cs b/src/IFoxCAD.LoadForm/LoaderForm.Designer.cs index e218578..c26ef83 100644 --- a/src/IFoxCAD.LoadForm/LoaderForm.Designer.cs +++ b/src/IFoxCAD.LoadForm/LoaderForm.Designer.cs @@ -1,4 +1,4 @@ -namespace LoadEx; +namespace IFoxCAD.LoadEx; partial class LoaderForm { diff --git a/src/IFoxCAD.LoadForm/LoaderForm.cs b/src/IFoxCAD.LoadForm/LoaderForm.cs index 74fb05f..30297f9 100644 --- a/src/IFoxCAD.LoadForm/LoaderForm.cs +++ b/src/IFoxCAD.LoadForm/LoaderForm.cs @@ -1,4 +1,4 @@ -namespace LoadEx; +namespace IFoxCAD.LoadEx; using System.Windows.Forms; diff --git a/tests/Test/Test.csproj b/tests/Test/Test.csproj index 739836a..53570cf 100644 --- a/tests/Test/Test.csproj +++ b/tests/Test/Test.csproj @@ -11,7 +11,7 @@ - + diff --git a/tests/Test/TestNetLoad.cs b/tests/Test/TestNetLoad.cs index 5fe8d53..a9fc708 100644 --- a/tests/Test/TestNetLoad.cs +++ b/tests/Test/TestNetLoad.cs @@ -5,7 +5,7 @@ public class NetLoad [CommandMethod("Test_NetLoad")] public static void Test_NetLoad() { - LoadEx.LoaderForm form = new(); + IFoxCAD.LoadEx.LoaderForm form = new(); Acap.ShowModalDialog(form); } } -- Gitee From ad413cfd0f9350c4e5da5e202acec0550005016b Mon Sep 17 00:00:00 2001 From: liuqihong <540762622@qq.com> Date: Thu, 18 Aug 2022 11:49:19 +0800 Subject: [PATCH 395/675] =?UTF-8?q?=E5=85=A8=E9=83=A8=E4=BD=BF=E7=94=A8?= =?UTF-8?q?=E5=8F=AF=E7=A9=BA=E7=B1=BB=E5=9E=8B=E6=A3=80=E6=9F=A5=E5=B9=B6?= =?UTF-8?q?=E4=B8=94=E4=BF=AE=E6=94=B9=E5=B7=A5=E7=A8=8B=E7=BB=93=E6=9E=84?= =?UTF-8?q?=E6=96=B9=E4=BE=BF=E5=A4=A7=E5=AE=B6=E4=BB=8E08=E5=BC=80?= =?UTF-8?q?=E5=A7=8B=E8=B0=83=E8=AF=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- IFoxCAD.sln | 40 +++++-- src/IFoxCAD.LoadForm/AssemblyDependent.cs | 10 +- .../DefaultAssemblyResolve.cs | 20 ++-- src/IFoxCAD.LoadForm/GlobalUsings.cs | 8 +- src/IFoxCAD.LoadForm/IFoxCAD.LoadEx.csproj | 33 +++--- src/IFoxCAD.LoadForm/LoaderForm.cs | 7 +- tests/TestAcad08/GlobalUsings.cs | 29 +++++ .../Properties/Resources.Designer.cs | 0 .../Properties/Resources.resx | 0 .../TestAcad08/Properties/launchSettings.json | 10 ++ tests/TestAcad08/TestAcad08.csproj | 18 ++++ .../{Test => TestAcad09plus}/GlobalUsings.cs | 0 .../Properties/Resources.Designer.cs | 63 +++++++++++ .../TestAcad09plus/Properties/Resources.resx | 101 ++++++++++++++++++ .../Properties/launchSettings.json | 0 .../TestAcad09plus.csproj} | 16 ++- tests/{Test => TestAcad09plus}/wpf/Class1.cs | 1 - .../wpf/TestView.xaml | 0 .../wpf/TestView.xaml.cs | 0 .../wpf/TestViewModel.cs | 22 ++-- tests/{Test => TestShared}/CmdINI.cs | 0 tests/{Test => TestShared}/Test.cs | 27 ++--- tests/{Test => TestShared}/TestAOP.cs | 0 tests/{Test => TestShared}/TestCurve.cs | 45 ++++---- tests/{Test => TestShared}/TestDBTrans.cs | 0 tests/{Test => TestShared}/TestEnt.cs | 4 +- .../{Test => TestShared}/TestFileDatabase.cs | 2 +- tests/{Test => TestShared}/TestJig.cs | 24 ++--- tests/{Test => TestShared}/TestLisp.cs | 0 tests/{Test => TestShared}/TestLoop.cs | 2 +- tests/{Test => TestShared}/TestMirrorFile.cs | 5 +- tests/{Test => TestShared}/TestNetLoad.cs | 0 tests/{Test => TestShared}/TestPoint.cs | 4 +- tests/{Test => TestShared}/TestQuadTree.cs | 18 ++-- tests/TestShared/TestShared.projitems | 33 ++++++ tests/TestShared/TestShared.shproj | 13 +++ tests/{Test => TestShared}/Testid.cs | 2 +- tests/{Test => TestShared}/testConvexHull.cs | 0 tests/{Test => TestShared}/testblock.cs | 73 ++++++------- tests/{Test => TestShared}/testeditor.cs | 14 +-- tests/{Test => TestShared}/testenv.cs | 3 +- .../{Test => TestShared}/testselectfilter.cs | 0 42 files changed, 453 insertions(+), 194 deletions(-) create mode 100644 tests/TestAcad08/GlobalUsings.cs rename tests/{Test => TestAcad08}/Properties/Resources.Designer.cs (100%) rename tests/{Test => TestAcad08}/Properties/Resources.resx (100%) create mode 100644 tests/TestAcad08/Properties/launchSettings.json create mode 100644 tests/TestAcad08/TestAcad08.csproj rename tests/{Test => TestAcad09plus}/GlobalUsings.cs (100%) create mode 100644 tests/TestAcad09plus/Properties/Resources.Designer.cs create mode 100644 tests/TestAcad09plus/Properties/Resources.resx rename tests/{Test => TestAcad09plus}/Properties/launchSettings.json (100%) rename tests/{Test/Test.csproj => TestAcad09plus/TestAcad09plus.csproj} (62%) rename tests/{Test => TestAcad09plus}/wpf/Class1.cs (99%) rename tests/{Test => TestAcad09plus}/wpf/TestView.xaml (100%) rename tests/{Test => TestAcad09plus}/wpf/TestView.xaml.cs (100%) rename tests/{Test => TestAcad09plus}/wpf/TestViewModel.cs (83%) rename tests/{Test => TestShared}/CmdINI.cs (100%) rename tests/{Test => TestShared}/Test.cs (92%) rename tests/{Test => TestShared}/TestAOP.cs (100%) rename tests/{Test => TestShared}/TestCurve.cs (84%) rename tests/{Test => TestShared}/TestDBTrans.cs (100%) rename tests/{Test => TestShared}/TestEnt.cs (95%) rename tests/{Test => TestShared}/TestFileDatabase.cs (92%) rename tests/{Test => TestShared}/TestJig.cs (93%) rename tests/{Test => TestShared}/TestLisp.cs (100%) rename tests/{Test => TestShared}/TestLoop.cs (93%) rename tests/{Test => TestShared}/TestMirrorFile.cs (93%) rename tests/{Test => TestShared}/TestNetLoad.cs (100%) rename tests/{Test => TestShared}/TestPoint.cs (99%) rename tests/{Test => TestShared}/TestQuadTree.cs (97%) create mode 100644 tests/TestShared/TestShared.projitems create mode 100644 tests/TestShared/TestShared.shproj rename tests/{Test => TestShared}/Testid.cs (98%) rename tests/{Test => TestShared}/testConvexHull.cs (100%) rename tests/{Test => TestShared}/testblock.cs (94%) rename tests/{Test => TestShared}/testeditor.cs (86%) rename tests/{Test => TestShared}/testenv.cs (97%) rename tests/{Test => TestShared}/testselectfilter.cs (100%) diff --git a/IFoxCAD.sln b/IFoxCAD.sln index bba427d..a90c13d 100644 --- a/IFoxCAD.sln +++ b/IFoxCAD.sln @@ -3,8 +3,6 @@ Microsoft Visual Studio Solution File, Format Version 12.00 # Visual Studio Version 17 VisualStudioVersion = 17.1.32113.165 MinimumVisualStudioVersion = 10.0.40219.1 -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Test", "tests\Test\Test.csproj", "{B1602568-F023-46C7-B635-3CB281F1EC00}" -EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "IFoxCAD.WPF", "src\IFoxCAD.WPF\IFoxCAD.WPF.csproj", "{D820D629-1AB3-4BE3-A772-CB753D98CCB8}" EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "IFoxCAD.Basal", "src\IFoxCAD.Basal\IFoxCAD.Basal.csproj", "{40BF07C4-100A-4810-A27B-4587CFB38E6E}" @@ -19,7 +17,15 @@ Project("{D954291E-2A0B-460D-934E-DC6B0785DB48}") = "IFoxCAD.Cad.Shared", "src\I EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "IFoxCAD.Acad09plus", "src\IFoxCAD.Cad\IFoxCAD.Aacad09plus\IFoxCAD.Acad09plus.csproj", "{CC46E4E2-90DF-48F5-B3D7-83FAFED58F2A}" EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "IFoxCAD.LoadEx", "src\IFoxCAD.LoadForm\IFoxCAD.LoadEx.csproj", "{05FE8BF6-0C54-4114-9626-A2F76483BE20}" +Project("{D954291E-2A0B-460D-934E-DC6B0785DB48}") = "TestShared", "tests\TestShared\TestShared.shproj", "{CED63D2D-0AF6-4831-806D-5E8E9B0D0A07}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "TestAcad08", "tests\TestAcad08\TestAcad08.csproj", "{8C573180-523D-427D-8030-CAA7D6B5EB6B}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "TestAcad09plus", "tests\TestAcad09plus\TestAcad09plus.csproj", "{5F478F68-19BC-4A3A-AF39-8728F8D396DD}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Test", "Test", "{C0BEFC15-6DF7-4636-BC76-4A0B88231470}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "IFoxCAD.LoadEx", "src\IFoxCAD.LoadForm\IFoxCAD.LoadEx.csproj", "{BFA1C130-8D87-45D0-8E79-301C1841A7A1}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution @@ -27,10 +33,6 @@ Global Release|Any CPU = Release|Any CPU EndGlobalSection GlobalSection(ProjectConfigurationPlatforms) = postSolution - {B1602568-F023-46C7-B635-3CB281F1EC00}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {B1602568-F023-46C7-B635-3CB281F1EC00}.Debug|Any CPU.Build.0 = Debug|Any CPU - {B1602568-F023-46C7-B635-3CB281F1EC00}.Release|Any CPU.ActiveCfg = Release|Any CPU - {B1602568-F023-46C7-B635-3CB281F1EC00}.Release|Any CPU.Build.0 = Release|Any CPU {D820D629-1AB3-4BE3-A772-CB753D98CCB8}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {D820D629-1AB3-4BE3-A772-CB753D98CCB8}.Debug|Any CPU.Build.0 = Debug|Any CPU {D820D629-1AB3-4BE3-A772-CB753D98CCB8}.Release|Any CPU.ActiveCfg = Release|Any CPU @@ -51,25 +53,41 @@ Global {CC46E4E2-90DF-48F5-B3D7-83FAFED58F2A}.Debug|Any CPU.Build.0 = Debug|Any CPU {CC46E4E2-90DF-48F5-B3D7-83FAFED58F2A}.Release|Any CPU.ActiveCfg = Release|Any CPU {CC46E4E2-90DF-48F5-B3D7-83FAFED58F2A}.Release|Any CPU.Build.0 = Release|Any CPU - {05FE8BF6-0C54-4114-9626-A2F76483BE20}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {05FE8BF6-0C54-4114-9626-A2F76483BE20}.Debug|Any CPU.Build.0 = Debug|Any CPU - {05FE8BF6-0C54-4114-9626-A2F76483BE20}.Release|Any CPU.ActiveCfg = Release|Any CPU - {05FE8BF6-0C54-4114-9626-A2F76483BE20}.Release|Any CPU.Build.0 = Release|Any CPU + {8C573180-523D-427D-8030-CAA7D6B5EB6B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {8C573180-523D-427D-8030-CAA7D6B5EB6B}.Debug|Any CPU.Build.0 = Debug|Any CPU + {8C573180-523D-427D-8030-CAA7D6B5EB6B}.Release|Any CPU.ActiveCfg = Release|Any CPU + {8C573180-523D-427D-8030-CAA7D6B5EB6B}.Release|Any CPU.Build.0 = Release|Any CPU + {5F478F68-19BC-4A3A-AF39-8728F8D396DD}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {5F478F68-19BC-4A3A-AF39-8728F8D396DD}.Debug|Any CPU.Build.0 = Debug|Any CPU + {5F478F68-19BC-4A3A-AF39-8728F8D396DD}.Release|Any CPU.ActiveCfg = Release|Any CPU + {5F478F68-19BC-4A3A-AF39-8728F8D396DD}.Release|Any CPU.Build.0 = Release|Any CPU + {BFA1C130-8D87-45D0-8E79-301C1841A7A1}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {BFA1C130-8D87-45D0-8E79-301C1841A7A1}.Debug|Any CPU.Build.0 = Debug|Any CPU + {BFA1C130-8D87-45D0-8E79-301C1841A7A1}.Release|Any CPU.ActiveCfg = Release|Any CPU + {BFA1C130-8D87-45D0-8E79-301C1841A7A1}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE EndGlobalSection GlobalSection(NestedProjects) = preSolution + {E2873F0B-CAD2-45E8-8FF0-C05C0C6AD955} = {C0BEFC15-6DF7-4636-BC76-4A0B88231470} {36F87D06-88B3-45E3-A2A8-0FC737B25428} = {465C4E39-FBA2-417A-AB31-BDC01601F420} {82FB8450-B971-4E30-859F-4B2DDB81F590} = {465C4E39-FBA2-417A-AB31-BDC01601F420} {CC46E4E2-90DF-48F5-B3D7-83FAFED58F2A} = {465C4E39-FBA2-417A-AB31-BDC01601F420} + {CED63D2D-0AF6-4831-806D-5E8E9B0D0A07} = {C0BEFC15-6DF7-4636-BC76-4A0B88231470} + {8C573180-523D-427D-8030-CAA7D6B5EB6B} = {C0BEFC15-6DF7-4636-BC76-4A0B88231470} + {5F478F68-19BC-4A3A-AF39-8728F8D396DD} = {C0BEFC15-6DF7-4636-BC76-4A0B88231470} + {BFA1C130-8D87-45D0-8E79-301C1841A7A1} = {C0BEFC15-6DF7-4636-BC76-4A0B88231470} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {31D6C754-CF6B-4AB0-9861-6923DD312350} EndGlobalSection GlobalSection(SharedMSBuildProjectFiles) = preSolution src\IFoxCAD.Cad.Shared\IFoxCAD.Cad.Shared.projitems*{36f87d06-88b3-45e3-a2a8-0fc737b25428}*SharedItemsImports = 5 + tests\TestShared\TestShared.projitems*{5f478f68-19bc-4a3a-af39-8728f8d396dd}*SharedItemsImports = 5 src\IFoxCAD.Cad.Shared\IFoxCAD.Cad.Shared.projitems*{82fb8450-b971-4e30-859f-4b2ddb81f590}*SharedItemsImports = 13 + tests\TestShared\TestShared.projitems*{8c573180-523d-427d-8030-caa7d6b5eb6b}*SharedItemsImports = 5 src\IFoxCAD.Cad.Shared\IFoxCAD.Cad.Shared.projitems*{cc46e4e2-90df-48f5-b3d7-83fafed58f2a}*SharedItemsImports = 5 + tests\TestShared\TestShared.projitems*{ced63d2d-0af6-4831-806d-5e8e9b0d0a07}*SharedItemsImports = 13 EndGlobalSection EndGlobal diff --git a/src/IFoxCAD.LoadForm/AssemblyDependent.cs b/src/IFoxCAD.LoadForm/AssemblyDependent.cs index 51ba6aa..1032a05 100644 --- a/src/IFoxCAD.LoadForm/AssemblyDependent.cs +++ b/src/IFoxCAD.LoadForm/AssemblyDependent.cs @@ -19,7 +19,7 @@ public LoadState(string dllFullName, bool state, Assembly? assembly = null) [HarmonyPatch("Autodesk.AutoCAD.ApplicationServices.ExtensionLoader", "OnAssemblyLoad")] public class AssemblyDependent : IDisposable { - #region 公共 + #region 字段和事件 /// /// 当前域加载事件,运行时出错的话,就靠这个事件来解决 /// @@ -28,13 +28,7 @@ public event ResolveEventHandler CurrentDomainAssemblyResolveEvent add { AppDomain.CurrentDomain.AssemblyResolve += value; } remove { AppDomain.CurrentDomain.AssemblyResolve -= value; } } - - - #endregion - - #region 字段 - - + /// /// cad程序域依赖_内存区(不可以卸载) /// diff --git a/src/IFoxCAD.LoadForm/DefaultAssemblyResolve.cs b/src/IFoxCAD.LoadForm/DefaultAssemblyResolve.cs index e99bb5c..2e63ba0 100644 --- a/src/IFoxCAD.LoadForm/DefaultAssemblyResolve.cs +++ b/src/IFoxCAD.LoadForm/DefaultAssemblyResolve.cs @@ -2,18 +2,20 @@ namespace IFoxCAD.LoadEx; public class AssemblyHelper { - #region 程序域运行事件 /// - /// 程序域运行事件 /// - /// 动态加载要注意所有的引用外的dll的加载顺序 - /// Acad2008若没有这个事件,会使动态命令执行时候无法引用当前的程序集函数 - /// 跨程序集反射 - /// 动态加载时,dll的地址会在系统的动态目录里,而它所处的程序集(运行域)是在动态目录里. - /// netload会把所处的运行域给改到cad自己的,而动态加载不通过netload,所以要自己去改. + /// 程序域运行事件 /// 这相当于是dll注入的意思,只是动态加载的这个"dll"不存在实体,只是一段内存. + /// 它总是被 事件使用 + /// 0x01 动态加载要注意所有的引用外的dll的加载顺序 + /// 0x02 指定版本: Acad2008若没有这个事件,会使动态命令执行时候无法引用当前的程序集函数 + /// 0x03 目录构成: 动态加载时,dll的地址会在系统的动态目录里,而它所处的程序集(运行域)是在动态目录里. + /// 0x04 命令构成: cad自带的netload会把所处的运行域给改到cad自己的,而动态加载不通过netload,所以要自己去改. /// /// + /// + /// + /// 程序集如果为空就不会调用 public static Assembly? DefaultAssemblyResolve(object sender, ResolveEventArgs args) { var cadAss = AppDomain.CurrentDomain.GetAssemblies(); @@ -24,7 +26,8 @@ public class AssemblyHelper return load; //获取名称一致,但是版本号不同的,调用最后的可用版本 - var ag = args.Name.Split(',')[0]; + var ag = args.Name.Substring(args.Name.IndexOf(',')); + //var ag = args.Name.Split(',')[0]; //获取最后一个符合条件的, //否则a.dll引用b.dll函数的时候,b.dll修改重生成之后,加载进去会调用第一个版本的b.dll @@ -39,5 +42,4 @@ public class AssemblyHelper } return load; } - #endregion } \ No newline at end of file diff --git a/src/IFoxCAD.LoadForm/GlobalUsings.cs b/src/IFoxCAD.LoadForm/GlobalUsings.cs index c81107c..b2d69fe 100644 --- a/src/IFoxCAD.LoadForm/GlobalUsings.cs +++ b/src/IFoxCAD.LoadForm/GlobalUsings.cs @@ -8,9 +8,9 @@ global using System.Runtime.CompilerServices; /// autocad 引用 -global using Autodesk.AutoCAD.ApplicationServices; -global using Autodesk.AutoCAD.Runtime; -global using Acap = Autodesk.AutoCAD.ApplicationServices.Application; -global using Autodesk.AutoCAD.EditorInput; +//global using Autodesk.AutoCAD.ApplicationServices; +//global using Autodesk.AutoCAD.Runtime; +//global using Acap = Autodesk.AutoCAD.ApplicationServices.Application; +//global using Autodesk.AutoCAD.EditorInput; global using HarmonyLib; diff --git a/src/IFoxCAD.LoadForm/IFoxCAD.LoadEx.csproj b/src/IFoxCAD.LoadForm/IFoxCAD.LoadEx.csproj index 381ed8e..6a0b07e 100644 --- a/src/IFoxCAD.LoadForm/IFoxCAD.LoadEx.csproj +++ b/src/IFoxCAD.LoadForm/IFoxCAD.LoadEx.csproj @@ -7,35 +7,26 @@ NET35;NET40;NET45 true true - x64 True - - - - - - + + - - - + + + Form + + + LoaderForm.cs + - + + LoaderForm.cs + - - $(Configuration);ac2009 - - - $(Configuration);ac2013 - - - $(Configuration);ac2015 - - diff --git a/src/IFoxCAD.LoadForm/LoaderForm.cs b/src/IFoxCAD.LoadForm/LoaderForm.cs index 30297f9..18169d7 100644 --- a/src/IFoxCAD.LoadForm/LoaderForm.cs +++ b/src/IFoxCAD.LoadForm/LoaderForm.cs @@ -10,13 +10,14 @@ public LoaderForm() InitializeComponent(); } - static void LoadDll(string path) + static string? LoadDll(string path) { - var ed = Acap.DocumentManager.MdiActiveDocument.Editor; + //var ed = Acap.DocumentManager.MdiActiveDocument.Editor; var ad = new AssemblyDependent(); var ls = new List(); ad.Load(path, ls); - ed.WriteMessage(AssemblyDependent.PrintMessage(ls)); + //ed.WriteMessage(AssemblyDependent.PrintMessage(ls)); + return AssemblyDependent.PrintMessage(ls); } void TextBox1_TextChanged(object sender, EventArgs e) diff --git a/tests/TestAcad08/GlobalUsings.cs b/tests/TestAcad08/GlobalUsings.cs new file mode 100644 index 0000000..92d6400 --- /dev/null +++ b/tests/TestAcad08/GlobalUsings.cs @@ -0,0 +1,29 @@ +/// 系统引用 +global using System; +global using System.Collections; +global using System.Collections.Generic; +global using System.IO; +global using System.Linq; +global using System.Text; +global using System.Reflection; +global using System.Text.RegularExpressions; +global using Microsoft.Win32; +global using System.ComponentModel; +global using System.Runtime.CompilerServices; + +/// autocad 引用 +global using Autodesk.AutoCAD.ApplicationServices; +global using Autodesk.AutoCAD.EditorInput; +global using Autodesk.AutoCAD.Colors; +global using Autodesk.AutoCAD.DatabaseServices; +global using Autodesk.AutoCAD.Geometry; +global using Autodesk.AutoCAD.Runtime; +global using Acgi = Autodesk.AutoCAD.GraphicsInterface; +global using Acap = Autodesk.AutoCAD.ApplicationServices.Application; + +global using Registry = Microsoft.Win32.Registry; +global using RegistryKey = Microsoft.Win32.RegistryKey; + +/// ifoxcad +global using IFoxCAD.Cad; +global using IFoxCAD.Basal; \ No newline at end of file diff --git a/tests/Test/Properties/Resources.Designer.cs b/tests/TestAcad08/Properties/Resources.Designer.cs similarity index 100% rename from tests/Test/Properties/Resources.Designer.cs rename to tests/TestAcad08/Properties/Resources.Designer.cs diff --git a/tests/Test/Properties/Resources.resx b/tests/TestAcad08/Properties/Resources.resx similarity index 100% rename from tests/Test/Properties/Resources.resx rename to tests/TestAcad08/Properties/Resources.resx diff --git a/tests/TestAcad08/Properties/launchSettings.json b/tests/TestAcad08/Properties/launchSettings.json new file mode 100644 index 0000000..3aa279d --- /dev/null +++ b/tests/TestAcad08/Properties/launchSettings.json @@ -0,0 +1,10 @@ +{ + "profiles": { + "Test": { + "commandName": "Executable", + "executablePath": "C:\\Program Files (x86)\\AutoCAD 2008\\acad.exe", + "commandLineArgs": "/nologo", + "nativeDebugging": false + } + } +} \ No newline at end of file diff --git a/tests/TestAcad08/TestAcad08.csproj b/tests/TestAcad08/TestAcad08.csproj new file mode 100644 index 0000000..604472f --- /dev/null +++ b/tests/TestAcad08/TestAcad08.csproj @@ -0,0 +1,18 @@ + + + preview + enable + + net35 + true + true + x86 + + + + + + + + + diff --git a/tests/Test/GlobalUsings.cs b/tests/TestAcad09plus/GlobalUsings.cs similarity index 100% rename from tests/Test/GlobalUsings.cs rename to tests/TestAcad09plus/GlobalUsings.cs diff --git a/tests/TestAcad09plus/Properties/Resources.Designer.cs b/tests/TestAcad09plus/Properties/Resources.Designer.cs new file mode 100644 index 0000000..1617020 --- /dev/null +++ b/tests/TestAcad09plus/Properties/Resources.Designer.cs @@ -0,0 +1,63 @@ +//------------------------------------------------------------------------------ +// +// 此代码由工具生成。 +// 运行时版本:4.0.30319.42000 +// +// 对此文件的更改可能会导致不正确的行为,并且如果 +// 重新生成代码,这些更改将会丢失。 +// +//------------------------------------------------------------------------------ + +namespace Test.Properties { + using System; + + + /// + /// 一个强类型的资源类,用于查找本地化的字符串等。 + /// + // 此类是由 StronglyTypedResourceBuilder + // 类通过类似于 ResGen 或 Visual Studio 的工具自动生成的。 + // 若要添加或移除成员,请编辑 .ResX 文件,然后重新运行 ResGen + // (以 /str 作为命令选项),或重新生成 VS 项目。 + [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "17.0.0.0")] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] + internal class Resources { + + private static global::System.Resources.ResourceManager resourceMan; + + private static global::System.Globalization.CultureInfo resourceCulture; + + [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")] + internal Resources() { + } + + /// + /// 返回此类使用的缓存的 ResourceManager 实例。 + /// + [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] + internal static global::System.Resources.ResourceManager ResourceManager { + get { + if (object.ReferenceEquals(resourceMan, null)) { + global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("Test.Properties.Resources", typeof(Resources).Assembly); + resourceMan = temp; + } + return resourceMan; + } + } + + /// + /// 重写当前线程的 CurrentUICulture 属性,对 + /// 使用此强类型资源类的所有资源查找执行重写。 + /// + [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] + internal static global::System.Globalization.CultureInfo Culture { + get { + return resourceCulture; + } + set { + resourceCulture = value; + } + } + } +} diff --git a/tests/TestAcad09plus/Properties/Resources.resx b/tests/TestAcad09plus/Properties/Resources.resx new file mode 100644 index 0000000..4fdb1b6 --- /dev/null +++ b/tests/TestAcad09plus/Properties/Resources.resx @@ -0,0 +1,101 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 1.3 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.3500.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.3500.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + \ No newline at end of file diff --git a/tests/Test/Properties/launchSettings.json b/tests/TestAcad09plus/Properties/launchSettings.json similarity index 100% rename from tests/Test/Properties/launchSettings.json rename to tests/TestAcad09plus/Properties/launchSettings.json diff --git a/tests/Test/Test.csproj b/tests/TestAcad09plus/TestAcad09plus.csproj similarity index 62% rename from tests/Test/Test.csproj rename to tests/TestAcad09plus/TestAcad09plus.csproj index 53570cf..2d3b44f 100644 --- a/tests/Test/Test.csproj +++ b/tests/TestAcad09plus/TestAcad09plus.csproj @@ -1,7 +1,7 @@  preview - + enable net45 true @@ -14,4 +14,18 @@ + + + + TestView.xaml + + + + + + Designer + + + + diff --git a/tests/Test/wpf/Class1.cs b/tests/TestAcad09plus/wpf/Class1.cs similarity index 99% rename from tests/Test/wpf/Class1.cs rename to tests/TestAcad09plus/wpf/Class1.cs index 9fcbbcd..b711b1e 100644 --- a/tests/Test/wpf/Class1.cs +++ b/tests/TestAcad09plus/wpf/Class1.cs @@ -5,7 +5,6 @@ public class Class1 [CommandMethod("testwpf")] public void TestWPf() { - var test = new TestView(); Acap.ShowModalWindow(test); } diff --git a/tests/Test/wpf/TestView.xaml b/tests/TestAcad09plus/wpf/TestView.xaml similarity index 100% rename from tests/Test/wpf/TestView.xaml rename to tests/TestAcad09plus/wpf/TestView.xaml diff --git a/tests/Test/wpf/TestView.xaml.cs b/tests/TestAcad09plus/wpf/TestView.xaml.cs similarity index 100% rename from tests/Test/wpf/TestView.xaml.cs rename to tests/TestAcad09plus/wpf/TestView.xaml.cs diff --git a/tests/Test/wpf/TestViewModel.cs b/tests/TestAcad09plus/wpf/TestViewModel.cs similarity index 83% rename from tests/Test/wpf/TestViewModel.cs rename to tests/TestAcad09plus/wpf/TestViewModel.cs index 3e01578..e726029 100644 --- a/tests/Test/wpf/TestViewModel.cs +++ b/tests/TestAcad09plus/wpf/TestViewModel.cs @@ -6,18 +6,16 @@ class TestViewModel : ViewModelBase { - - private string name; - - public string Name + string? name; + public string? Name { get { return name; } set { Set(ref name, value); } } - private RelayCommand clickCommand; + private RelayCommand? clickCommand; - public RelayCommand ClickCommand + public RelayCommand? ClickCommand { get { @@ -35,15 +33,15 @@ public bool ReceiveMouseMove set { Set(ref receiveMouseMove, value); } } - private string tipText; - public string TipText + private string? tipText; + public string? TipText { get { return tipText; } set { Set(ref tipText, value); } } - private RelayCommand loadedCommand; - public RelayCommand LoadCommand + private RelayCommand? loadedCommand; + public RelayCommand? LoadCommand { get { @@ -52,8 +50,8 @@ public RelayCommand LoadCommand } } - private RelayCommand mouseMoveCommand; - public RelayCommand MouseMoveCommand + private RelayCommand? mouseMoveCommand; + public RelayCommand? MouseMoveCommand { get { diff --git a/tests/Test/CmdINI.cs b/tests/TestShared/CmdINI.cs similarity index 100% rename from tests/Test/CmdINI.cs rename to tests/TestShared/CmdINI.cs diff --git a/tests/Test/Test.cs b/tests/TestShared/Test.cs similarity index 92% rename from tests/Test/Test.cs rename to tests/TestShared/Test.cs index 8cb7f78..2cf3ac2 100644 --- a/tests/Test/Test.cs +++ b/tests/TestShared/Test.cs @@ -7,6 +7,8 @@ public class Test public void Dbtest() { using var tr = new DBTrans(); + if (tr.Editor is null) + return; tr.Editor.WriteMessage("\n测试 Editor 属性是否工作!"); tr.Editor.WriteMessage("\n----------开始测试--------------"); tr.Editor.WriteMessage("\n测试document属性是否工作"); @@ -60,19 +62,19 @@ public void Drawarc() public void DraCircle() { using var tr = new DBTrans(); - Circle circle1 = EntityEx.CreateCircle(new Point3d(0, 0, 0), new Point3d(1, 0, 0)); //起点,终点 - Circle circle2 = EntityEx.CreateCircle(new Point3d(-2, 0, 0), new Point3d(2, 0, 0), new Point3d(0, 2, 0));//三点画圆,成功 - Circle circle3 = EntityEx.CreateCircle(new Point3d(-2, 0, 0), new Point3d(0, 0, 0), new Point3d(2, 0, 0));//起点,圆心,终点,失败 - tr.CurrentSpace.AddEntity(circle1, circle2); + var circle1 = EntityEx.CreateCircle(new Point3d(0, 0, 0), new Point3d(1, 0, 0)); //起点,终点 + var circle2 = EntityEx.CreateCircle(new Point3d(-2, 0, 0), new Point3d(2, 0, 0), new Point3d(0, 2, 0));//三点画圆,成功 + var circle3 = EntityEx.CreateCircle(new Point3d(-2, 0, 0), new Point3d(0, 0, 0), new Point3d(2, 0, 0));//起点,圆心,终点,失败 + tr.CurrentSpace.AddEntity(circle1, circle2!); if (circle3 is not null) { tr.CurrentSpace.AddEntity(circle3); } else { - tr.Editor.WriteMessage("三点画圆失败"); + tr.Editor?.WriteMessage("三点画圆失败"); } - tr.CurrentSpace.AddEntity(circle3); + tr.CurrentSpace.AddEntity(circle3!); tr.CurrentSpace.AddCircle(new Point3d(0, 0, 0), new Point3d(1, 1, 0), new Point3d(2, 0, 0));//三点画圆,成功 tr.CurrentSpace.AddCircle(new Point3d(0, 0, 0), new Point3d(1, 1, 0), new Point3d(2, 2, 0));//起点,圆上一点,终点(共线) } @@ -223,7 +225,7 @@ public void GetXdata() var ed = doc.Editor; using var tr = new DBTrans(); tr.RegAppTable.ForEach(id => - id.GetObject().Name.Print()); + id.GetObject()?.Name.Print()); tr.RegAppTable.GetRecords().ForEach(rec => rec.Name.Print()); tr.RegAppTable.GetRecordNames().ForEach(name => name.Print()); tr.RegAppTable.ForEach(re => re.Name.Print()); @@ -248,7 +250,7 @@ public void Changexdata() if (res.Status == PromptStatus.OK) { using var tr = new DBTrans(); - var data = tr.GetObject(res.ObjectId); + var data = tr.GetObject(res.ObjectId)!; data.ChangeXData(appname, DxfCode.ExtendedDataAsciiString, "change"); ed.WriteMessage(data.XData.ToString()); @@ -265,9 +267,8 @@ public void Removexdata() { using var tr = new DBTrans(); var data = tr.GetObject(res.ObjectId); - data.RemoveXData(appname, DxfCode.ExtendedDataAsciiString); - - ed.WriteMessage(data.XData.ToString()); + data?.RemoveXData(appname, DxfCode.ExtendedDataAsciiString); + ed.WriteMessage(data?.XData.ToString()); } } @@ -277,11 +278,11 @@ public void PrintLayerName() using var tr = new DBTrans(); foreach (var layerRecord in tr.LayerTable.GetRecords()) { - tr.Editor.WriteMessage(layerRecord.Name); + tr.Editor?.WriteMessage(layerRecord.Name); } foreach (var layerRecord in tr.LayerTable.GetRecords()) { - tr.Editor.WriteMessage(layerRecord.Name); + tr.Editor?.WriteMessage(layerRecord.Name); break; } } diff --git a/tests/Test/TestAOP.cs b/tests/TestShared/TestAOP.cs similarity index 100% rename from tests/Test/TestAOP.cs rename to tests/TestShared/TestAOP.cs diff --git a/tests/Test/TestCurve.cs b/tests/TestShared/TestCurve.cs similarity index 84% rename from tests/Test/TestCurve.cs rename to tests/TestShared/TestCurve.cs index a8572d6..61080b6 100644 --- a/tests/Test/TestCurve.cs +++ b/tests/TestShared/TestCurve.cs @@ -1,4 +1,6 @@ -namespace Test; +using Autodesk.AutoCAD.DatabaseServices; + +namespace Test; public class TestGraph { @@ -29,13 +31,11 @@ public void TestGraph1() if (ents == null) return; Tools.TestTimes2(1, "new", () => { - - - var res = ents.GetAllCycle(); + var res = ents!.GetAllCycle(); //res.ForEach((i, t) => t.ForWrite(e => e.ColorIndex = i + 1)); Env.Print(res.Count()); - tr.CurrentSpace.AddEntity(res); + tr.CurrentSpace.AddEntity(res); }); } @@ -48,16 +48,16 @@ public void TestGraphspeed() if (ents == null) return; - var graph = new IFoxCAD.Cad.Graph(); // 为了调试先把图的访问改为internal - foreach (var curve in ents) { - - graph.AddEdge(curve.GetGeCurve()); - - +#if NET35 + graph.AddEdge(curve!.ToCurve3d()!); +#else + graph.AddEdge(curve!.GetGeCurve()); +#endif } + //新建 dfs var dfs = new DepthFirst(); #if true @@ -95,8 +95,10 @@ public class TestCurve public void TestBreakCurve() { using var tr = new DBTrans(); - var ents = Env.Editor.SSGet().Value.GetEntities(); - var tt = CurveEx.BreakCurve(ents.ToList()); + var ents = Env.Editor.SSGet()?.Value.GetEntities(); + if (ents is null) + return; + var tt = CurveEx.BreakCurve(ents.ToList()!); tt.ForEach(t => t.ForWrite(e => e.ColorIndex = 1)); tr.CurrentSpace.AddEntity(tt); } @@ -105,22 +107,23 @@ public void TestBreakCurve() public void TestCurveCurveIntersector3d() { using var tr = new DBTrans(); - var ents = Env.Editor.SSGet().Value.GetEntities() - .Select(e => e.ToCompositeCurve3d()).ToList(); + var ents = Env.Editor.SSGet()? + .Value.GetEntities() + .Select(e => e?.ToCompositeCurve3d()).ToList(); + if (ents == null) + return; var cci3d = new CurveCurveIntersector3d(); - - for (int i = 0; i < ents.Count; i++) { var gc1 = ents[i]; - var int1 = gc1.GetInterval(); + var int1 = gc1?.GetInterval(); //var pars1 = paramss[i]; for (int j = i; j < ents.Count; j++) { var gc2 = ents[j]; //var pars2 = paramss[j]; - var int2 = gc2.GetInterval(); + var int2 = gc2?.GetInterval(); cci3d.Set(gc1, gc2, int1, int2, Vector3d.ZAxis); var d = cci3d.OverlapCount(); var a = cci3d.GetIntersectionRanges(); @@ -142,11 +145,7 @@ public void TestCurveCurveIntersector3d() var pts = cci3d.GetIntersectionPoint(k); Env.Print(pts); } - - - } - } // var tt = CurveEx.Topo(ents.ToList()); //tt.ForEach(t => t.ForWrite(e => e.ColorIndex = 1)); diff --git a/tests/Test/TestDBTrans.cs b/tests/TestShared/TestDBTrans.cs similarity index 100% rename from tests/Test/TestDBTrans.cs rename to tests/TestShared/TestDBTrans.cs diff --git a/tests/Test/TestEnt.cs b/tests/TestShared/TestEnt.cs similarity index 95% rename from tests/Test/TestEnt.cs rename to tests/TestShared/TestEnt.cs index e3cccf7..1824a0f 100644 --- a/tests/Test/TestEnt.cs +++ b/tests/TestShared/TestEnt.cs @@ -9,11 +9,9 @@ public void TestEntRoration() using var tr = new DBTrans(); tr.CurrentSpace.AddEntity(line); - var line2 = line.Clone() as Line; + var line2 = (Line)line.Clone(); tr.CurrentSpace.AddEntity(line2); line2.Rotation(new(100, 0, 0), Math.PI / 2); - - } diff --git a/tests/Test/TestFileDatabase.cs b/tests/TestShared/TestFileDatabase.cs similarity index 92% rename from tests/Test/TestFileDatabase.cs rename to tests/TestShared/TestFileDatabase.cs index 6759e83..cd3af21 100644 --- a/tests/Test/TestFileDatabase.cs +++ b/tests/TestShared/TestFileDatabase.cs @@ -18,7 +18,7 @@ public void TestDatabase() if (trans.Document is not null && trans.Document.IsActive) trans.Document.SendStringToExecute("_qsave\n", false, true, true); else - trans.Database.SaveAs(fileName, DwgVersion.AC1021); + trans.Database.SaveAs(fileName, (DwgVersion)27); } catch (System.Exception e) { diff --git a/tests/Test/TestJig.cs b/tests/TestShared/TestJig.cs similarity index 93% rename from tests/Test/TestJig.cs rename to tests/TestShared/TestJig.cs index 0f8c6b1..98e3c2f 100644 --- a/tests/Test/TestJig.cs +++ b/tests/TestShared/TestJig.cs @@ -8,19 +8,17 @@ public class Commands_Jig [CommandMethod("TestCmd_jig33")] public static void TestCmd_jig33() { - Circle cir; using var tr = new DBTrans(); - var per = tr.Editor.GetEntity("\n点选圆形:"); - if (per.Status != PromptStatus.OK) + var per = tr.Editor?.GetEntity("\n点选圆形:"); + if (per?.Status != PromptStatus.OK) return; - cir = tr.GetObject(per.ObjectId, OpenMode.ForWrite); - + var cir = tr.GetObject(per.ObjectId, OpenMode.ForWrite); if (cir == null) return; var oldSp = cir.StartPoint; - JigEx moveJig = null; + JigEx? moveJig = null; moveJig = new JigEx((mousePoint, drawEntitys) => { - moveJig.SetOptions(oldSp);//回调过程中也可以修改基点 + moveJig!.SetOptions(oldSp);//回调过程中也可以修改基点 //cir.UpgradeOpen();//已经提权了,所以这里不需要提权 cir.Move(cir.StartPoint, mousePoint); //cir.DowngradeOpen(); @@ -62,14 +60,14 @@ public void TestCmd_Jig44() * 原因: 多段线与鼠标垂直点作为 BasePoint,jig鼠标点为确定点 * 所以需要先声明再传入指针,但是我发现null也可以. */ - JigEx jig = null; - JigPromptPointOptions options = null; + JigEx? jig = null; + JigPromptPointOptions? options = null; jig = new JigEx((mousePoint, drawEntitys) => { var closestPt = pl.GetClosestPointTo(mousePoint, false); //回调过程中SetOptions会覆盖配置,所以如果想增加关键字或者修改基点, //不要这样做: jig.SetOptions(closestPt) 而是使用底层暴露 - options.BasePoint = closestPt; + options!.BasePoint = closestPt; //需要避免重复加入同一个关键字 if (!options.Keywords.Contains("A")) @@ -99,18 +97,18 @@ public void TestCmd_Jig44() switch (pr.StringResult) { case "A": - tr.Editor.WriteMessage($"\n 您触发了关键字{pr.StringResult}"); + tr.Editor?.WriteMessage($"\n 您触发了关键字{pr.StringResult}"); flag = false; break; case " ": - tr.Editor.WriteMessage("\n 触发关键字空格"); + tr.Editor?.WriteMessage("\n 触发关键字空格"); flag = false; break; } } else if (pr.Status != PromptStatus.OK)//PromptStatus.None == 右键,空格,回车,都在这里结束 { - tr.Editor.WriteMessage(Environment.NewLine + pr.Status.ToString()); + tr.Editor?.WriteMessage(Environment.NewLine + pr.Status.ToString()); return; } else diff --git a/tests/Test/TestLisp.cs b/tests/TestShared/TestLisp.cs similarity index 100% rename from tests/Test/TestLisp.cs rename to tests/TestShared/TestLisp.cs diff --git a/tests/Test/TestLoop.cs b/tests/TestShared/TestLoop.cs similarity index 93% rename from tests/Test/TestLoop.cs rename to tests/TestShared/TestLoop.cs index d60dc66..f69e676 100644 --- a/tests/Test/TestLoop.cs +++ b/tests/TestShared/TestLoop.cs @@ -20,7 +20,7 @@ public void Testloop() Env.Print(loop); - loop.SetFirst(loop.Last); + loop.SetFirst(loop.Last!); Env.Print(loop); Env.Print(loop.Min()); loop.SetFirst(new LoopListNode (loop.Min() ,loop)); diff --git a/tests/Test/TestMirrorFile.cs b/tests/TestShared/TestMirrorFile.cs similarity index 93% rename from tests/Test/TestMirrorFile.cs rename to tests/TestShared/TestMirrorFile.cs index 388b0d1..4d13fcc 100644 --- a/tests/Test/TestMirrorFile.cs +++ b/tests/TestShared/TestMirrorFile.cs @@ -30,7 +30,8 @@ public static void CmdTest_MirrorFile() dbText.DowngradeOpen(); } }); - tr.Database.SaveAs(fileSave, DwgVersion.AC1021/*AC1021 AutoCAD 2007/2008/2009.*/); + var ver = (DwgVersion)27;/*AC1021 AutoCAD 2007/2008/2009.*/ + tr.Database.SaveAs(fileSave, ver); } /// @@ -70,6 +71,6 @@ public static void CmdTest_MirrorFile2() } }); }); - tr.Database.SaveAs(fileSave, DwgVersion.AC1021/*AC1021 AutoCAD 2007/2008/2009.*/); + tr.Database.SaveAs(fileSave, (DwgVersion)27 /*AC1021 AutoCAD 2007/2008/2009.*/); } } \ No newline at end of file diff --git a/tests/Test/TestNetLoad.cs b/tests/TestShared/TestNetLoad.cs similarity index 100% rename from tests/Test/TestNetLoad.cs rename to tests/TestShared/TestNetLoad.cs diff --git a/tests/Test/TestPoint.cs b/tests/TestShared/TestPoint.cs similarity index 99% rename from tests/Test/TestPoint.cs rename to tests/TestShared/TestPoint.cs index 8941ddb..51ab60f 100644 --- a/tests/Test/TestPoint.cs +++ b/tests/TestShared/TestPoint.cs @@ -98,12 +98,10 @@ public void Testlistequalspeed() { var lst1 = new List { 1, 2, 3, 4 }; var lst2 = new List { 1, 2, 3, 4}; - lst1.EqualsAll(null); + lst1.EqualsAll(null!); Tools.TestTimes2(1000000, "eqaulspeed:", () => { lst1.EqualsAll(lst2); }); - - } [CommandMethod("Testcontains")] diff --git a/tests/Test/TestQuadTree.cs b/tests/TestShared/TestQuadTree.cs similarity index 97% rename from tests/Test/TestQuadTree.cs rename to tests/TestShared/TestQuadTree.cs index 6c5d8b4..02aaf14 100644 --- a/tests/Test/TestQuadTree.cs +++ b/tests/TestShared/TestQuadTree.cs @@ -36,7 +36,7 @@ public override int GetHashCode() public partial class TestQuadTree { - QuadTree _quadTreeRoot; + QuadTree? _quadTreeRoot; #region 四叉树创建并加入 [CommandMethod("Test_QuadTree")] public void Test_QuadTree() @@ -93,7 +93,7 @@ public void Test_QuadTree() foreach (var ent in grc) { //初始化图元颜色 - ent.ColorIndex = 1; //Color.FromRgb(0, 0, 0);//黑色 + ent!.ColorIndex = 1; //Color.FromRgb(0, 0, 0);//黑色 var edge = ent.GeometricExtents; //四叉树数据 var entRect = new Rect(edge.MinPoint.X, edge.MinPoint.Y, edge.MaxPoint.X, edge.MaxPoint.Y); @@ -115,12 +115,10 @@ public void Test_QuadTree() //测试只加入四叉树的时间 Timer.RunTime(() => { for (int i = 0; i < ces.Count; i++) - { _quadTreeRoot.Insert(ces[i]); - } }, Timer.TimeEnum.Millisecond, "插入四叉树时间:");//30万图元±0.7秒.cad2021 - tr.Editor.WriteMessage($"\n加入图元数量:{maximumItems}"); + tr.Editor?.WriteMessage($"\n加入图元数量:{maximumItems}"); } /// @@ -128,7 +126,7 @@ public void Test_QuadTree() /// /// 创建数量 /// 数据库边界 - static IEnumerable GenerateRandomCircle(int createNumber, Rect dbExt) + static IEnumerable GenerateRandomCircle(int createNumber, Rect dbExt) { var x1 = (int)dbExt.X; var x2 = (int)(dbExt.X + dbExt.Width); @@ -354,10 +352,12 @@ public void CmdTest_QuadTree4() /// void Ssget(QuadTreeSelectMode mode) { - using var tr = new DBTrans(); - if (_quadTreeRoot is null) return; + + using var tr = new DBTrans(); + if (tr.Editor is null) + return; var rect = GetCorner(tr.Editor); if (rect is null) return; @@ -368,7 +368,7 @@ void Ssget(QuadTreeSelectMode mode) var ces = _quadTreeRoot.Query(rect, mode); ces.ForEach(item => { var ent = tr.GetObject(item.ObjectId, OpenMode.ForWrite); - ent.Color = Color.FromColor(item.Color); + ent!.Color = Color.FromColor(item.Color); ent.DowngradeOpen(); ent.Dispose(); }); diff --git a/tests/TestShared/TestShared.projitems b/tests/TestShared/TestShared.projitems new file mode 100644 index 0000000..7c433a6 --- /dev/null +++ b/tests/TestShared/TestShared.projitems @@ -0,0 +1,33 @@ + + + + $(MSBuildAllProjects);$(MSBuildThisFileFullPath) + true + ced63d2d-0af6-4831-806d-5e8e9b0d0a07 + + + TestShared + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/tests/TestShared/TestShared.shproj b/tests/TestShared/TestShared.shproj new file mode 100644 index 0000000..31ea779 --- /dev/null +++ b/tests/TestShared/TestShared.shproj @@ -0,0 +1,13 @@ + + + + ced63d2d-0af6-4831-806d-5e8e9b0d0a07 + 14.0 + + + + + + + + diff --git a/tests/Test/Testid.cs b/tests/TestShared/Testid.cs similarity index 98% rename from tests/Test/Testid.cs rename to tests/TestShared/Testid.cs index e61553b..1cbf74d 100644 --- a/tests/Test/Testid.cs +++ b/tests/TestShared/Testid.cs @@ -34,7 +34,7 @@ public void TestMyCommand() var l1 = new Line(new Point3d(0, 0, 0), new Point3d(100, 100, 0)); var blkred = trans.GetObject(Env.Database.CurrentSpaceId, OpenMode.ForWrite) as BlockTableRecord; - blkred.AppendEntity(l1); + blkred?.AppendEntity(l1); trans.AddNewlyCreatedDBObject(l1, true); trans.Commit(); //dbtrans.Dispose(); diff --git a/tests/Test/testConvexHull.cs b/tests/TestShared/testConvexHull.cs similarity index 100% rename from tests/Test/testConvexHull.cs rename to tests/TestShared/testConvexHull.cs diff --git a/tests/Test/testblock.cs b/tests/TestShared/testblock.cs similarity index 94% rename from tests/Test/testblock.cs rename to tests/TestShared/testblock.cs index a7bd058..61da1ca 100644 --- a/tests/Test/testblock.cs +++ b/tests/TestShared/testblock.cs @@ -11,6 +11,8 @@ public void Test_GetBoundingBoxEx() return; foreach (var item in ents) { + if(item is null) + continue; var box = item.GetBoundingBoxEx(); Env.Print("min:" + box.min + ";max" + box.max); } @@ -73,7 +75,7 @@ public void Test_BlockDefChange() foreach (var id in btr) { var ent = tr.GetObject(id); - using (ent.ForWrite()) + using (ent!.ForWrite()) { if (ent is Dimension dBText) { @@ -85,7 +87,7 @@ public void Test_BlockDefChange() } } }); - tr.Editor.Regen(); + tr.Editor?.Regen(); } [CommandMethod("Test_insertblockdef")] @@ -138,7 +140,7 @@ public void Test_AddAttsDef() { using var tr = new DBTrans(); var blockid = Env.Editor.GetEntity("pick block:").ObjectId; - var blockref = tr.GetObject(blockid).BlockTableRecord; + var blockref = tr.GetObject(blockid)!.BlockTableRecord; var att1 = new AttributeDefinition() { Position = new Point3d(20, 20, 0), Tag = "addtagTest1", Height = 1, TextString = "valueTest1" }; var att2 = new AttributeDefinition() { Position = new Point3d(10, 12, 0), Tag = "tagTest2", Height = 1, TextString = "valueTest2" }; @@ -183,14 +185,13 @@ public void Test_ClipBlock() ); //tr.BlockTable.Add("hah"); var id = tr.CurrentSpace.InsertBlock(new Point3d(0, 0, 0), "test1"); - var bref = tr.GetObject(id); + var bref = tr.GetObject(id)!; var pts = new List { new Point3d(3, 3, 0), new Point3d(7, 3, 0), new Point3d(7, 7, 0), new Point3d(3, 7, 0) }; bref.ClipBlockRef(pts); var id1 = tr.CurrentSpace.InsertBlock(new Point3d(20, 20, 0), "test1"); var bref1 = tr.GetObject(id); - - bref1.ClipBlockRef(new Point3d(13, 13, 0), new Point3d(17, 17, 0)); + bref1?.ClipBlockRef(new Point3d(13, 13, 0), new Point3d(17, 17, 0)); } /// @@ -224,15 +225,9 @@ public void test_block_ej() ltr.Color = Color.FromColorIndex(ColorMethod.ByAci, 2); //ColorMethod.ByAci可以让我们使用AutoCAD ACI颜色索引……这里为2(表示黄色) }); - ObjectId id = tr.ModelSpace.InsertBlock(Point3d.Origin, blockdef);//插入块参照 - - - - - + var id = tr.ModelSpace.InsertBlock(Point3d.Origin, blockdef);//插入块参照 var entTest = tr.GetObject(id); - entTest.Draw(); - + entTest?.Draw(); } using var tr2 = new DBTrans(); @@ -246,7 +241,7 @@ public void test_block_ej() return; } - var Bref = tr2.GetObject(PER.ObjectId); + var Bref = tr2.GetObject(PER.ObjectId)!; //var BTR = tr.GetObject(Bref.BlockTableRecord, OpenMode.ForWrite); ////如果知道块名字BTRName //BlockTableRecord BTR = tr.GetObject(tr.BlockTable[blockName], OpenMode.ForWrite); @@ -257,10 +252,10 @@ public void test_block_ej() foreach (ObjectId OID in ltr) { - var Ent = tr2.GetObject(OID); - using (Ent.ForWrite()) + var ent = tr2.GetObject(OID); + using (ent!.ForWrite()) { - if (Ent is MText mText) + if (ent is MText mText) { switch (mText.Text) { @@ -274,10 +269,9 @@ public void test_block_ej() ; break; } - - }; - if (Ent is DBText dBText) { dBText.TextString = "haha"; }; - if (Ent is Dimension dimension) + } + else if (ent is DBText dBText) { dBText.TextString = "haha"; } + else if (ent is Dimension dimension) { switch (dimension.DimensionText) { @@ -288,18 +282,12 @@ public void test_block_ej() default: break; } - }; + } } - - } - }); - - tr2.Editor.Regen(); - - + tr2.Editor?.Regen(); } [CommandMethod("W_KSZK")] @@ -344,8 +332,7 @@ public void QuickBlockDef() //ents = ents.OrderBy(x => x.Value).ToList(); var ents = rss.Value.GetEntities(); //ents.ForEach(ent => extents.AddExtents(ent.GeometricExtents)); - var extents = ents.GetExtents(); - + var extents = ents!.GetExtents(); Point3d pt = extents.MinPoint; Matrix3d matrix = Matrix3d.Displacement(Point3d.Origin - pt); //var newEnts = new List(); @@ -361,18 +348,18 @@ public void QuickBlockDef() // return; //} ents.ForEach(ent => - ent.ForWrite(e => e.TransformBy(matrix))); + ent?.ForWrite(e => e?.TransformBy(matrix))); //var newents = ents.Select(ent => //{ // var maping = new IdMapping(); // return ent.DeepClone(ent, maping, true) as Entity; //}); - var newents = ents.Select(ent => ent.Clone() as Entity); + var newents = ents.Select(ent => ent?.Clone() as Entity); //ents.ForEach(ent => ent.ForWrite(e => e.Erase(true))); // 删除实体就会卡死,比较奇怪,估计是Clone()函数的问题 // 经过测试不是删除的问题 - var btrId = tr.BlockTable.Add(blockName, newents); - ents.ForEach(ent => ent.ForWrite(e => e.Erase(true))); + var btrId = tr.BlockTable.Add(blockName, newents!); + ents.ForEach(ent => ent?.ForWrite(e => e?.Erase(true))); var bId = tr.CurrentSpace.InsertBlock(pt, blockName); //tr.GetObject(bId, OpenMode.ForWrite).Move(Point3d.Origin, Point3d.Origin); //var ed = Acap.DocumentManager.MdiActiveDocument.Editor; @@ -416,11 +403,10 @@ public void Test_QuickBlockDef() foreach (var item in ids) { var ent = tr.GetObject(item, OpenMode.ForRead) as Entity; - - btr.AppendEntity(ent.Clone() as Entity); + btr.AppendEntity(ent!.Clone() as Entity); ent.ForWrite(e => e.Erase(true)); } - bt.UpgradeOpen(); + bt!.UpgradeOpen(); bt.Add(btr); tr.AddNewlyCreatedDBObject(btr, true); bt.DowngradeOpen(); @@ -435,7 +421,7 @@ public void Test_QuickBlockDef() { ScaleFactors = default }; - btr1.AppendEntity(br); + btr1!.AppendEntity(br); tr.AddNewlyCreatedDBObject(br, true); btr1.DowngradeOpen(); ed.Regen(); @@ -465,7 +451,7 @@ public void TestChangeDynameicBlock() }; var blockid = Env.Editor.GetEntity("选择个块").ObjectId; using var tr = new DBTrans(); - var blockref = tr.GetObject(blockid); + var blockref = tr.GetObject(blockid)!; blockref.ChangeBlockProperty(pro); // 这是第一个函数的用法 } @@ -509,7 +495,7 @@ public void Test_Cbll() tr.Database.Insert(blkdefname, tr1.Database, false); //插入了块定义,未插入块参照 } - +#if !NET35 [CommandMethod("Test_CombineBlocksIntoLibrary")] public void Test_CombineBlocksIntoLibrary() { @@ -634,5 +620,6 @@ public void Test_CombineBlocksIntoLibrary() failed > 0 ? " (" + failed + " failed)" : "", pathName ); - } + } +#endif } \ No newline at end of file diff --git a/tests/Test/testeditor.cs b/tests/TestShared/testeditor.cs similarity index 86% rename from tests/Test/testeditor.cs rename to tests/TestShared/testeditor.cs index bf4fd5f..7633899 100644 --- a/tests/Test/testeditor.cs +++ b/tests/TestShared/testeditor.cs @@ -29,11 +29,7 @@ public void Testzoom() using var tr = new DBTrans(); var res = Env.Editor.GetEntity("\npick ent:"); if (res.Status == PromptStatus.OK) - { - Env.Editor.ZoomObject(res.ObjectId.GetObject()); - } - - + Env.Editor.ZoomObject(res.ObjectId.GetObject()!); } [CommandMethod("testzoomextent")] public void Testzoomextent() @@ -60,10 +56,8 @@ public void Testssget() { "B", action_b } }; - var ss = Env.Editor.SSGet( ":S", - messages: new string[2] { "get", "del" }, - keywords: keyword); - - Env.Print(ss); + var ss = Env.Editor.SSGet( ":S", messages: new string[2] { "get", "del" }, + keywords: keyword); + Env.Print(ss!); } } diff --git a/tests/Test/testenv.cs b/tests/TestShared/testenv.cs similarity index 97% rename from tests/Test/testenv.cs rename to tests/TestShared/testenv.cs index b482781..a5c190c 100644 --- a/tests/Test/testenv.cs +++ b/tests/TestShared/testenv.cs @@ -62,10 +62,9 @@ public void Testcadver() { //Env.Print(AcadVersion.Versions); AcadVersion.Versions.ForEach(v => Env.Print(v)); - AcadVersion.FromApp(Acap.AcadApplication).Print(); + AcadVersion.FromApp(Acap.AcadApplication)?.Print(); 1.Print(); "1".Print(); - } [CommandMethod("TestGetVar")] diff --git a/tests/Test/testselectfilter.cs b/tests/TestShared/testselectfilter.cs similarity index 100% rename from tests/Test/testselectfilter.cs rename to tests/TestShared/testselectfilter.cs -- Gitee From 5742f4892da730fbf3d70fa94b28f0d6a1eae3cc Mon Sep 17 00:00:00 2001 From: liuqihong <540762622@qq.com> Date: Thu, 18 Aug 2022 11:54:12 +0800 Subject: [PATCH 396/675] =?UTF-8?q?=E4=BF=AE=E6=94=B9=E6=96=87=E4=BB=B6?= =?UTF-8?q?=E5=A4=B9=E5=90=8D=E7=A7=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- IFoxCAD.sln | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/IFoxCAD.sln b/IFoxCAD.sln index a90c13d..8dc6df1 100644 --- a/IFoxCAD.sln +++ b/IFoxCAD.sln @@ -11,7 +11,7 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "TestConsole", "tests\TestCo EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "IFoxCAD.Acad08", "src\IFoxCAD.Cad\IFoxCAD.Aacad08\IFoxCAD.Acad08.csproj", "{36F87D06-88B3-45E3-A2A8-0FC737B25428}" EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Cad", "Cad", "{465C4E39-FBA2-417A-AB31-BDC01601F420}" +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "IFox.Cad.Library", "IFox.Cad.Library", "{465C4E39-FBA2-417A-AB31-BDC01601F420}" EndProject Project("{D954291E-2A0B-460D-934E-DC6B0785DB48}") = "IFoxCAD.Cad.Shared", "src\IFoxCAD.Cad.Shared\IFoxCAD.Cad.Shared.shproj", "{82FB8450-B971-4E30-859F-4B2DDB81F590}" EndProject @@ -77,7 +77,6 @@ Global {CED63D2D-0AF6-4831-806D-5E8E9B0D0A07} = {C0BEFC15-6DF7-4636-BC76-4A0B88231470} {8C573180-523D-427D-8030-CAA7D6B5EB6B} = {C0BEFC15-6DF7-4636-BC76-4A0B88231470} {5F478F68-19BC-4A3A-AF39-8728F8D396DD} = {C0BEFC15-6DF7-4636-BC76-4A0B88231470} - {BFA1C130-8D87-45D0-8E79-301C1841A7A1} = {C0BEFC15-6DF7-4636-BC76-4A0B88231470} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {31D6C754-CF6B-4AB0-9861-6923DD312350} -- Gitee From 0503b718aacf92dcfccb8bb33f964e2c32f847a0 Mon Sep 17 00:00:00 2001 From: liuqihong <540762622@qq.com> Date: Thu, 18 Aug 2022 13:22:37 +0800 Subject: [PATCH 397/675] =?UTF-8?q?=E4=BC=98=E5=8C=96=E4=BB=A3=E7=A0=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Runtime/AutoRegAssem.cs | 1 + src/IFoxCAD.LoadForm/AssemblyDependent.cs | 347 ++++++++++++++---- .../DefaultAssemblyResolve.cs | 30 +- ...50\350\276\276\345\274\217\346\240\221.cs" | 243 ++++++------ tests/TestShared/TestNetLoad.cs | 2 +- 5 files changed, 409 insertions(+), 214 deletions(-) diff --git a/src/IFoxCAD.Cad.Shared/Runtime/AutoRegAssem.cs b/src/IFoxCAD.Cad.Shared/Runtime/AutoRegAssem.cs index 969f8a4..c208ab5 100644 --- a/src/IFoxCAD.Cad.Shared/Runtime/AutoRegAssem.cs +++ b/src/IFoxCAD.Cad.Shared/Runtime/AutoRegAssem.cs @@ -131,6 +131,7 @@ public bool UnRegApp() /// bool SearchForReg() { + //在使用netloadx的时候,此处注册表是失效的,具体原因要进行netloadx测试 var appkey = GetAcAppKey(); if (appkey.SubKeyCount == 0) return false; diff --git a/src/IFoxCAD.LoadForm/AssemblyDependent.cs b/src/IFoxCAD.LoadForm/AssemblyDependent.cs index 1032a05..de823cb 100644 --- a/src/IFoxCAD.LoadForm/AssemblyDependent.cs +++ b/src/IFoxCAD.LoadForm/AssemblyDependent.cs @@ -1,20 +1,10 @@ namespace IFoxCAD.LoadEx; -/// -/// 加载程序集和加载状态 -/// -public struct LoadState -{ - public Assembly? Assembly; - public string DllFullName; - public bool State; - public LoadState(string dllFullName, bool state, Assembly? assembly = null) - { - DllFullName = dllFullName; - State = state; - Assembly = assembly; - } -} +/* + * 因为此处引用了 HarmonyPatch + * 所以单独分一个工程出来作为cad工程的引用 + * 免得污染了cad工程的纯洁 + */ [HarmonyPatch("Autodesk.AutoCAD.ApplicationServices.ExtensionLoader", "OnAssemblyLoad")] public class AssemblyDependent : IDisposable @@ -28,7 +18,6 @@ public event ResolveEventHandler CurrentDomainAssemblyResolveEvent add { AppDomain.CurrentDomain.AssemblyResolve += value; } remove { AppDomain.CurrentDomain.AssemblyResolve -= value; } } - /// /// cad程序域依赖_内存区(不可以卸载) /// @@ -37,6 +26,10 @@ public event ResolveEventHandler CurrentDomainAssemblyResolveEvent /// cad程序域依赖_映射区(不可以卸载) /// readonly Assembly[] _cadAssemblyRef; + /// + /// 拦截cad的Loader异常:默认是 + /// + public bool PatchExtensionLoader = false; #endregion #region 构造 @@ -81,7 +74,7 @@ public bool Load(string dllFullName, List loadStates, bool byteLoad = List allRefs = new(); GetAllRefPaths(dllFullName, allRefs); - bool dllFullNameLoad = false; + bool dllFullNameLoadOk = false; //查询加载链逆向加载,确保前面不丢失 //这里有问题,从尾巴开始的,就一定是没有任何引用吗? @@ -100,7 +93,7 @@ public bool Load(string dllFullName, List loadStates, bool byteLoad = //有一次true,就是true if (path == dllFullName) - dllFullNameLoad = true; + dllFullNameLoadOk = true; try { @@ -116,7 +109,7 @@ public bool Load(string dllFullName, List loadStates, bool byteLoad = } catch { loadStates.Add(new LoadState(path, false));/*错误造成*/ } } - return dllFullNameLoad; + return dllFullNameLoadOk; } /// @@ -137,14 +130,14 @@ public bool Load(string dllFullName, List loadStates, bool byteLoad = var pdbFullName = Path.Combine(dir, pdbName); if (File.Exists(pdbFullName) && byteLoad) return Assembly.Load(File.ReadAllBytes(path), File.ReadAllBytes(pdbFullName)); - return null; #endif + return null; } /// /// 递归获取加载链 /// - /// + /// dll文件位置 /// 返回的集合 /// void GetAllRefPaths(string dllFullName, List dllFullNamesOut) @@ -153,42 +146,9 @@ void GetAllRefPaths(string dllFullName, List dllFullNamesOut) return; dllFullNamesOut.Add(dllFullName); - //路径转程序集名 - var assName = AssemblyName.GetAssemblyName(dllFullName).FullName; - - //在当前程序域的 assemblyAs内存区 和 assemblyAsRef映射区 找这个程序集名 - var assemblyAs = _cadAssembly.FirstOrDefault(ass => ass.FullName == assName); - Assembly assemblyAsRef; - - //内存区有表示加载过 - //映射区有表示查找过,但没有加载(一般来说不存在.只是debug会注释掉 Assembly.Load 的时候用来测试) - if (assemblyAs != null) - { - assemblyAsRef = assemblyAs; - } - else - { - assemblyAsRef = _cadAssemblyRef.FirstOrDefault(ass => ass.FullName == assName); - - //内存区和映射区都没有的话就把dll加载到映射区,用来找依赖表 - if (assemblyAsRef == null) - { - var byteRef = File.ReadAllBytes(dllFullName); -#if true2 - //1548253108:这里会报错,但是我测试不出来它报错.....他提供了解决方案 - //var accAsb = typeof(Document).Assembly; - const string ext = "Autodesk.AutoCAD.ApplicationServices.ExtensionLoader"; - Harmony hm = new(ext); - hm.PatchAll(); - assemblyAsRef = Assembly.ReflectionOnlyLoad(byteRef); - hm.UnpatchAll(ext); - //CSharpUtils.RestoreMethod(); -#else - // assemblyAsRef = Assembly.ReflectionOnlyLoad(dllFullName); //没有依赖会直接报错 - assemblyAsRef = Assembly.ReflectionOnlyLoad(byteRef); -#endif - } - } + var assemblyAsRef = GetAssembly(dllFullName); + if (assemblyAsRef == null) + return; var sb = new StringBuilder(); //dll拖拉加载路径-搜索路径(可以增加到这个dll下面的所有文件夹?) @@ -210,6 +170,52 @@ void GetAllRefPaths(string dllFullName, List dllFullNamesOut) } } + /// + /// 在内存区和映射区找已经加载的程序集 + /// + /// + /// + /// + Assembly? GetAssembly(string dllFullName) + { + //路径转程序集名 + var assName = AssemblyName.GetAssemblyName(dllFullName).FullName; + //在当前程序域的 assemblyAs内存区 和 assemblyAsRef映射区 找这个程序集名 + var assemblyAs = _cadAssembly.FirstOrDefault(ass => ass.FullName == assName); + + //内存区有表示加载过 + //映射区有表示查找过,但没有加载(一般来说不存在.只是debug会注释掉 Assembly.Load 的时候用来测试) + if (assemblyAs != null) + return assemblyAs; + + //映射区 + var assemblyAsRef = _cadAssemblyRef.FirstOrDefault(ass => ass.FullName == assName); + + //内存区和映射区都没有的话就把dll加载到映射区,用来找依赖表 + if (assemblyAsRef != null) + return assemblyAsRef; + + var byteRef = File.ReadAllBytes(dllFullName); + if (PatchExtensionLoader) + { + //QQ1548253108:这里会报错,但是我测试不出来它报错. + //他提供了解决方案如下: + //var accAsb = typeof(Document).Assembly; + const string ext = "Autodesk.AutoCAD.ApplicationServices.ExtensionLoader"; + Harmony hm = new(ext); + hm.PatchAll(); + assemblyAsRef = Assembly.ReflectionOnlyLoad(byteRef); + hm.UnpatchAll(ext); + //CSharpUtils.RestoreMethod(); + } + else + { + // assemblyAsRef = Assembly.ReflectionOnlyLoad(dllFullName); //没有依赖会直接报错 + assemblyAsRef = Assembly.ReflectionOnlyLoad(byteRef); + } + return assemblyAsRef; + } + /// /// 加载信息 /// @@ -243,26 +249,7 @@ void GetAllRefPaths(string dllFullName, List dllFullNamesOut) } #endregion - #region 删除文件 - /// - /// 递归删除文件夹目录及文件 - /// - /// - /// - static void DeleteFolder(string dir) - { - if (!Directory.Exists(dir)) //如果存在这个文件夹删除之 - return; - foreach (string d in Directory.GetFileSystemEntries(dir)) - { - if (File.Exists(d)) - File.Delete(d); //直接删除其中的文件 - else - DeleteFolder(d); //递归删除子文件夹 - } - Directory.Delete(dir, true); //删除已空文件夹 - } - + #region 删除文件 /// /// Debug的时候删除obj目录,防止占用 /// @@ -280,7 +267,60 @@ public void DebugDelObjFiles(string dllFullName) if (list[list.Length - 1] == "Debug" && list[list.Length - 2] == "bin") { var projobj = path.Substring(0, path.LastIndexOf("bin")) + "obj"; - DeleteFolder(projobj); + FileEx.DeleteFolder(projobj); + } + } + #endregion + + #region 移动文件 + /// + /// 临时文件夹_pdb的,无论是否创建这里都应该进行删除 + /// + string? Temp_Pdb_dest; + string? Temp_Pdb_source; + string? Temp_Obj_dest; + string? Temp_Obj_source; + const string Temp = "Temp"; + + /// + /// Debug的时候移动obj目录,防止占用 + /// + public void DebugMoveObjFiles(string dllFullName, Action action) + { + var filename = Path.GetFileNameWithoutExtension(dllFullName); + var path = Path.GetDirectoryName(dllFullName); + + try + { + //新建文件夹_临时目录 + Temp_Pdb_dest = path + $"\\{Temp}\\"; + //移动文件进去 + Temp_Pdb_source = path + "\\" + filename + ".pdb"; + FileEx.MoveFolder(Temp_Pdb_source, Temp_Pdb_dest); + + //检查是否存在obj文件夹,有就递归移动 + var list = path.Split('\\'); + if (list[list.Length - 1] == "Debug" && list[list.Length - 2] == "bin") + { + var bin = path.LastIndexOf("bin"); + var proj = path.Substring(0, bin); + Temp_Obj_source = proj + "obj"; + Temp_Obj_dest = proj + $"{Temp}"; + FileEx.MoveFolder(Temp_Obj_source, Temp_Obj_dest); + } + } + catch { } + + try { action.Invoke(); } + finally + { + /// 还原移动 + FileEx.MoveFolder(Temp_Pdb_dest, Temp_Pdb_source); + FileEx.DeleteFolder(Temp_Pdb_dest); + + FileEx.MoveFolder(Temp_Obj_dest, Temp_Obj_source); + FileEx.DeleteFolder(Temp_Obj_dest); + } } #endregion @@ -321,3 +361,154 @@ protected virtual void Dispose(bool ing) } #endregion } + + +/// +/// 加载程序集和加载状态 +/// +public struct LoadState +{ + public Assembly? Assembly; + public string DllFullName; + public bool State; + public LoadState(string dllFullName, bool state, Assembly? assembly = null) + { + DllFullName = dllFullName; + State = state; + Assembly = assembly; + } +} + +public class FileEx +{ + /// + /// 判断含有文件名和后缀 + /// + /// 路径或者完整文件路径 + static bool ContainFileName(string pathOrFile) + { + // 判断输入的是单文件,它可能不存在 + var a = Path.GetDirectoryName(pathOrFile); + var b = Path.GetFileName(pathOrFile); + var c = Path.GetExtension(pathOrFile); + // 是文件 + return a.Length > 0 && b.Length > 0 && c.Length > 0; + } + + /// + /// 移动文件夹中的所有文件夹与文件到另一个文件夹 + /// + /// 源文件夹 + /// 目标文件夹 + + public static void MoveFolder(string? sourcePathOrFile, string? destPath) + { + if (sourcePathOrFile is null) + throw new ArgumentException(nameof(sourcePathOrFile)); + if (destPath is null) + throw new ArgumentException(nameof(destPath)); + + if (ContainFileName(destPath)) + destPath = Path.GetDirectoryName(destPath); + + //目标目录不存在则创建 + if (!Directory.Exists(destPath)) + { + try + { + Directory.CreateDirectory(destPath); + } + catch (Exception ex) + { + throw new Exception("创建目标目录失败:" + ex.Message); + } + } + + if (ContainFileName(sourcePathOrFile)) + { + // 如果是单个文件,就移动到目录就好了 + if (File.Exists(sourcePathOrFile)) + { + destPath += "\\" + Path.GetFileName(sourcePathOrFile); + File.Move(sourcePathOrFile, destPath); + return; + } + return; + } + + // 如果是文件就改为路径 + if (!Directory.Exists(sourcePathOrFile)) + { + sourcePathOrFile = Path.GetDirectoryName(sourcePathOrFile); + if (!Directory.Exists(sourcePathOrFile)) + throw new DirectoryNotFoundException("源目录不存在!"); + } + + MoveFolder2(sourcePathOrFile, destPath); + } + + /// + /// 移动文件夹中的所有文件夹与文件到另一个文件夹 + /// + /// 源文件夹 + /// 目标文件夹 + static void MoveFolder2(string sourcePath, string destPath) + { + if (!Directory.Exists(destPath)) + { + //目标目录不存在则创建 + try + { + Directory.CreateDirectory(destPath); + } + catch (Exception ex) + { + throw new Exception("创建目标目录失败:" + ex.Message); + } + } + + //获得源文件下所有文件 + var files = new List(Directory.GetFiles(sourcePath)); + files.ForEach(c => { + string destFile = Path.Combine(destPath, Path.GetFileName(c)); + //覆盖模式 + if (File.Exists(destFile)) + File.Delete(destFile); + File.Move(c, destFile); + }); + //获得源文件下所有目录文件 + List folders = new(Directory.GetDirectories(sourcePath)); + + folders.ForEach(c => { + string destDir = Path.Combine(destPath, Path.GetFileName(c)); + //Directory.Move必须要在同一个根目录下移动才有效,不能在不同卷中移动。 + //Directory.Move(c, destDir); + + //采用递归的方法实现 + MoveFolder2(c, destDir); + }); + } + + /// + /// 递归删除文件夹目录及文件 + /// + /// + /// + public static void DeleteFolder(string? dir) + { + if (dir is null) + throw new ArgumentException(nameof(dir)); + + if (!Directory.Exists(dir)) //如果存在这个文件夹删除之 + return; + + foreach (string d in Directory.GetFileSystemEntries(dir)) + { + if (File.Exists(d)) + File.Delete(d); //直接删除其中的文件 + else + DeleteFolder(d); //递归删除子文件夹 + } + Directory.Delete(dir, true); //删除已空文件夹 + } +} \ No newline at end of file diff --git a/src/IFoxCAD.LoadForm/DefaultAssemblyResolve.cs b/src/IFoxCAD.LoadForm/DefaultAssemblyResolve.cs index 2e63ba0..6630579 100644 --- a/src/IFoxCAD.LoadForm/DefaultAssemblyResolve.cs +++ b/src/IFoxCAD.LoadForm/DefaultAssemblyResolve.cs @@ -18,28 +18,32 @@ public class AssemblyHelper /// 程序集如果为空就不会调用 public static Assembly? DefaultAssemblyResolve(object sender, ResolveEventArgs args) { + Assembly? result = null; var cadAss = AppDomain.CurrentDomain.GetAssemblies(); //名称和版本号都一致的,调用它 - var load = cadAss.FirstOrDefault(ass => ass.GetName().FullName == args.Name); - if (load != null) - return load; + result = cadAss.FirstOrDefault(ass => ass.GetName().FullName == args.Name); + if (result != null) + return result; //获取名称一致,但是版本号不同的,调用最后的可用版本 - var ag = args.Name.Substring(args.Name.IndexOf(',')); - //var ag = args.Name.Split(',')[0]; + var ag = GetAssemblyName(args.Name); //获取最后一个符合条件的, - //否则a.dll引用b.dll函数的时候,b.dll修改重生成之后,加载进去会调用第一个版本的b.dll - foreach (var item in cadAss) + //否则a.dll引用b.dll函数的时候,b.dll修改重生成之后, + //加载进去会调用第一个版本的b.dll, + //vs会迭代程序版本号的*,所以最后的可用就是循环到最后的. + for (int i = 0; i < cadAss.Length; i++) { - if (item.GetName().FullName.Split(',')[0] != ag) + if (GetAssemblyName(cadAss[i].GetName().FullName) != ag) continue; - - //为什么加载的程序版本号最后要是* - //因为vs会帮你迭代这个版本号,所以最后的可用就是循环到最后的. - load = item; + result = cadAss[i]; } - return load; + return result; + } + + static string GetAssemblyName(string argString) + { + return argString.Substring(argString.IndexOf(',')); } } \ No newline at end of file diff --git "a/tests/TestConsole/\350\241\250\350\276\276\345\274\217\346\240\221.cs" "b/tests/TestConsole/\350\241\250\350\276\276\345\274\217\346\240\221.cs" index f5cc477..b53fc48 100644 --- "a/tests/TestConsole/\350\241\250\350\276\276\345\274\217\346\240\221.cs" +++ "b/tests/TestConsole/\350\241\250\350\276\276\345\274\217\346\240\221.cs" @@ -1,143 +1,142 @@ -using System; +namespace TestConsole; + +using System; using System.Collections.Generic; using System.Diagnostics; using System.Linq; using System.Linq.Expressions; -namespace TestConsole +/// +/// 表达式树 +/// MSDN链接 +/// +public class Test_Expression { - /// - /// 表达式树 - /// MSDN链接 - /// - public class Test_Expression + public static void Demo1() { - public static void Demo1() - { - // 官方例子:表达式体内只有一个式子 - // 创建表达式树 - Expression> exprTree = num => num < 5; + // 官方例子:表达式体内只有一个式子 + // 创建表达式树 + Expression> exprTree = num => num < 5; + + // 分解表达式树 + ParameterExpression param = exprTree.Parameters[0];//num + BinaryExpression operation = (BinaryExpression)exprTree.Body;//函数体 {(num < 5)} + ParameterExpression left = (ParameterExpression)operation.Left;//左节点 num + ConstantExpression right = (ConstantExpression)operation.Right;//右表达式 5 + + Console.WriteLine("表达式树例子: {0} => {1} {2} {3}", + param.Name, left.Name, operation.NodeType, right.Value); + Console.Read(); + } - // 分解表达式树 - ParameterExpression param = exprTree.Parameters[0];//num - BinaryExpression operation = (BinaryExpression)exprTree.Body;//函数体 {(num < 5)} - ParameterExpression left = (ParameterExpression)operation.Left;//左节点 num - ConstantExpression right = (ConstantExpression)operation.Right;//右表达式 5 - Console.WriteLine("表达式树例子: {0} => {1} {2} {3}", - param.Name, left.Name, operation.NodeType, right.Value); - Console.Read(); - } + public static void Demo2() + { + // 这里是会报错的!! 原因就是体内有多个例子需要分解!! + // Expression> exprTree = x => x > 5 && x < 50; + // + // // 分解表达式树 + // ParameterExpression param = exprTree.Parameters[0];// x + // BinaryExpression operation = (BinaryExpression)exprTree.Body;//函数体 {((x > 5) AndAlso (x < 50))} + // + // ParameterExpression left = (ParameterExpression)operation.Left;//左节点.......这里报错 + // ConstantExpression right = (ConstantExpression)operation.Right;//右表达式.....这里报错 + // + // Console.WriteLine("表达式树例子: {0} => {1} {2} {3}", + // param.Name, left.Name, operation.NodeType, right.Value); + } - public static void Demo2() - { - // 这里是会报错的!! 原因就是体内有多个例子需要分解!! - // Expression> exprTree = x => x > 5 && x < 50; - // - // // 分解表达式树 - // ParameterExpression param = exprTree.Parameters[0];// x - // BinaryExpression operation = (BinaryExpression)exprTree.Body;//函数体 {((x > 5) AndAlso (x < 50))} - // - // ParameterExpression left = (ParameterExpression)operation.Left;//左节点.......这里报错 - // ConstantExpression right = (ConstantExpression)operation.Right;//右表达式.....这里报错 - // - // Console.WriteLine("表达式树例子: {0} => {1} {2} {3}", - // param.Name, left.Name, operation.NodeType, right.Value); - } - - - //博客园例子,表达式体内有多个式子 - public static void Demo3() - { - List names = new() { "Cai", "Edward", "Beauty" }; - - Console.WriteLine("******************一个表达式"); - Expression> lambda2 = name => name.Length > 2 && name.Length < 4; - var method2 = ReBuildExpression(lambda2); - var query2 = names.Where(method2); - foreach (string n in query2) - Console.WriteLine(n); - - Console.WriteLine("******************二个表达式"); - Expression> lambda0 = item => item.Length > 2; - Expression> lambda1 = item => item.Length < 4; - var method = ReBuildExpression(lambda0, lambda1); - var query = names.Where(method); - foreach (string n in query) - Console.WriteLine(n); - Console.WriteLine("******************表达式结束"); - Console.Read(); - } - - - static Func ReBuildExpression(Expression> lambda) - { - MyExpressionVisitor my = new() - { - Parameter = Expression.Parameter(typeof(string), "name") - }; - - Expression left = my.Modify(lambda.Body); - //构造一个新的表达式 - var newLambda = Expression.Lambda>(left, my.Parameter); - return newLambda.Compile(); - } - - - - /// - /// 重构表达式_合并 - /// - /// 匿名函数表达式1 - /// 匿名函数表达式2 - /// - static Func ReBuildExpression(Expression> lambda0, - Expression> lambda1) + //博客园例子,表达式体内有多个式子 + public static void Demo3() + { + List names = new() { "Cai", "Edward", "Beauty" }; + + Console.WriteLine("******************一个表达式"); + Expression> lambda2 = name => name.Length > 2 && name.Length < 4; + var method2 = ReBuildExpression(lambda2); + var query2 = names.Where(method2); + foreach (string n in query2) + Console.WriteLine(n); + + Console.WriteLine("******************二个表达式"); + Expression> lambda0 = item => item.Length > 2; + Expression> lambda1 = item => item.Length < 4; + var method = ReBuildExpression(lambda0, lambda1); + var query = names.Where(method); + foreach (string n in query) + Console.WriteLine(n); + Console.WriteLine("******************表达式结束"); + Console.Read(); + } + + + static Func ReBuildExpression(Expression> lambda) + { + MyExpressionVisitor my = new() { - MyExpressionVisitor my = new() - { - Parameter = Expression.Parameter(typeof(string), "name") - }; - - Expression left = my.Modify(lambda0.Body); - Expression right = my.Modify(lambda1.Body); - var expression = Expression.AndAlso(left, right);//就是 && 合并两个匿名函数 - - //构造一个新的表达式 - var newLambda = Expression.Lambda>(expression, my.Parameter); - return newLambda.Compile(); - } + Parameter = Expression.Parameter(typeof(string), "name") + }; + + Expression left = my.Modify(lambda.Body); + //构造一个新的表达式 + var newLambda = Expression.Lambda>(left, my.Parameter); + return newLambda.Compile(); } + /// - /// 表达式参数分解 - /// 博客园链接 + /// 重构表达式_合并 /// - public class MyExpressionVisitor : ExpressionVisitor + /// 匿名函数表达式1 + /// 匿名函数表达式2 + /// + static Func ReBuildExpression(Expression> lambda0, + Expression> lambda1) { - /// - /// 公共参数 - /// - public ParameterExpression? Parameter; - /// - /// 返回替换后的参数表达式 - /// - /// - /// - public Expression Modify(Expression exp) + MyExpressionVisitor my = new() { - return Visit(exp); - } - /// - /// 重写参数 - /// - /// - /// - protected override Expression? VisitParameter(ParameterExpression p) - { - return Parameter; - } + Parameter = Expression.Parameter(typeof(string), "name") + }; + + Expression left = my.Modify(lambda0.Body); + Expression right = my.Modify(lambda1.Body); + var expression = Expression.AndAlso(left, right);//就是 && 合并两个匿名函数 + + //构造一个新的表达式 + var newLambda = Expression.Lambda>(expression, my.Parameter); + return newLambda.Compile(); + } +} + + +/// +/// 表达式参数分解 +/// 博客园链接 +/// +public class MyExpressionVisitor : ExpressionVisitor +{ + /// + /// 公共参数 + /// + public ParameterExpression? Parameter; + /// + /// 返回替换后的参数表达式 + /// + /// + /// + public Expression Modify(Expression exp) + { + return Visit(exp); + } + /// + /// 重写参数 + /// + /// + /// + protected override Expression? VisitParameter(ParameterExpression p) + { + return Parameter; } } diff --git a/tests/TestShared/TestNetLoad.cs b/tests/TestShared/TestNetLoad.cs index a9fc708..6d913cb 100644 --- a/tests/TestShared/TestNetLoad.cs +++ b/tests/TestShared/TestNetLoad.cs @@ -2,7 +2,7 @@ public class NetLoad { - [CommandMethod("Test_NetLoad")] + [CommandMethod("netloadx")] public static void Test_NetLoad() { IFoxCAD.LoadEx.LoaderForm form = new(); -- Gitee From c6f30d2a446f1ccc86017e054106b44c2a94f702 Mon Sep 17 00:00:00 2001 From: liuqihong <540762622@qq.com> Date: Thu, 18 Aug 2022 13:41:32 +0800 Subject: [PATCH 398/675] =?UTF-8?q?=E4=BF=AE=E6=94=B9net=E4=B8=BA=E5=A4=A7?= =?UTF-8?q?=E5=86=99?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/IFoxCAD.Basal/IFoxCAD.Basal.csproj | 2 +- src/IFoxCAD.Cad.Shared/Runtime/Log.cs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/IFoxCAD.Basal/IFoxCAD.Basal.csproj b/src/IFoxCAD.Basal/IFoxCAD.Basal.csproj index 41ed7c1..dd80bb3 100644 --- a/src/IFoxCAD.Basal/IFoxCAD.Basal.csproj +++ b/src/IFoxCAD.Basal/IFoxCAD.Basal.csproj @@ -4,7 +4,7 @@ preview enable - net35;net40;net45 + net35;NET40;NET45 true 0.3.6 InspireFunction diff --git a/src/IFoxCAD.Cad.Shared/Runtime/Log.cs b/src/IFoxCAD.Cad.Shared/Runtime/Log.cs index 3f50915..df2fe0d 100644 --- a/src/IFoxCAD.Cad.Shared/Runtime/Log.cs +++ b/src/IFoxCAD.Cad.Shared/Runtime/Log.cs @@ -94,7 +94,7 @@ public class EventLogger : LogBase { // 需要win权限 // https://blog.csdn.net/weixin_38208401/article/details/77870909 - // net50要加 + // NET50要加 // https://docs.microsoft.com/en-us/answers/questions/526018/windows-event-log-with-net-5.html public string LogName = "IFoxCadLog"; -- Gitee From fdf39c2d66f88aeb06318ec4160d50093965f91a Mon Sep 17 00:00:00 2001 From: liuqihong <540762622@qq.com> Date: Thu, 18 Aug 2022 18:52:40 +0800 Subject: [PATCH 399/675] =?UTF-8?q?=E5=8F=96=E6=B6=88=E6=B3=A8=E5=85=A5?= =?UTF-8?q?=E7=89=B9=E6=80=A7=E4=B8=B0=E5=AF=8C=E4=BE=8B=E5=AD=90?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/IFoxCAD.LoadForm/AssemblyDependent.cs | 25 ++- src/IFoxCAD.LoadForm/CSharpUtils.cs | 69 -------- src/IFoxCAD.LoadForm/LoaderForm.Designer.cs | 184 ++++++++++---------- src/IFoxCAD.LoadForm/LoaderForm.cs | 8 +- src/IFoxCAD.LoadForm/LoaderForm.resx | 8 +- tests/TestShared/TestNetLoad.cs | 15 +- 6 files changed, 132 insertions(+), 177 deletions(-) delete mode 100644 src/IFoxCAD.LoadForm/CSharpUtils.cs diff --git a/src/IFoxCAD.LoadForm/AssemblyDependent.cs b/src/IFoxCAD.LoadForm/AssemblyDependent.cs index de823cb..6cce1ff 100644 --- a/src/IFoxCAD.LoadForm/AssemblyDependent.cs +++ b/src/IFoxCAD.LoadForm/AssemblyDependent.cs @@ -1,4 +1,6 @@ -namespace IFoxCAD.LoadEx; +using Mono.Cecil.Cil; + +namespace IFoxCAD.LoadEx; /* * 因为此处引用了 HarmonyPatch @@ -6,7 +8,6 @@ * 免得污染了cad工程的纯洁 */ -[HarmonyPatch("Autodesk.AutoCAD.ApplicationServices.ExtensionLoader", "OnAssemblyLoad")] public class AssemblyDependent : IDisposable { #region 字段和事件 @@ -198,15 +199,25 @@ void GetAllRefPaths(string dllFullName, List dllFullNamesOut) var byteRef = File.ReadAllBytes(dllFullName); if (PatchExtensionLoader) { - //QQ1548253108:这里会报错,但是我测试不出来它报错. - //他提供了解决方案如下: - //var accAsb = typeof(Document).Assembly; + //QQ1548253108:这里会报错,但是我测试不出来它报错.他提供了解决方案. + /* 方案一: + * 在类上面加 [HarmonyPatch("Autodesk.AutoCAD.ApplicationServices.ExtensionLoader", "OnAssemblyLoad")] + * const string ext = "Autodesk.AutoCAD.ApplicationServices.ExtensionLoader"; + * Harmony hm = new(ext); + * hm.PatchAll(); + * assemblyAsRef = Assembly.ReflectionOnlyLoad(byteRef); + * hm.UnpatchAll(ext); + */ + + //方案二: const string ext = "Autodesk.AutoCAD.ApplicationServices.ExtensionLoader"; Harmony hm = new(ext); - hm.PatchAll(); + hm.Patch(typeof(Document).Assembly + .GetType(ext) + .GetMethod("OnAssemblyLoad"), + new HarmonyMethod(GetType(), "Dummy")); assemblyAsRef = Assembly.ReflectionOnlyLoad(byteRef); hm.UnpatchAll(ext); - //CSharpUtils.RestoreMethod(); } else { diff --git a/src/IFoxCAD.LoadForm/CSharpUtils.cs b/src/IFoxCAD.LoadForm/CSharpUtils.cs deleted file mode 100644 index 346ed5e..0000000 --- a/src/IFoxCAD.LoadForm/CSharpUtils.cs +++ /dev/null @@ -1,69 +0,0 @@ -namespace IFoxCAD.LoadEx; - -internal class CSharpUtils -{ - private static unsafe long* addrOfOrgMethodAddr; - private static unsafe long* orgMethodAddr; - - public static bool ReplaceMethod(Type targetType, string targetMethod, - Type injectType, string injectMethod, - BindingFlags bindingAttr = BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.Public, - Binder? binder = null, - CallingConventions callConvention = CallingConventions.Any, - Type[]? types = null, - ParameterModifier[]? modifiers = null) - { - if (types == null) - types = Type.EmptyTypes; - - MethodInfo tarMethod = targetType.GetMethod(targetMethod, bindingAttr, binder, callConvention, types, modifiers); - MethodInfo injMethod = injectType.GetMethod(injectMethod, bindingAttr, binder, callConvention, types, modifiers); - - var tarMethods = injectType.GetMethods( - BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.Static); - var injMethods = injectType.GetMethods( - BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.Static); - - foreach (var item in tarMethods) - if (item.Name.Equals(targetMethod)) - tarMethod = item; - foreach (var item2 in injMethods) - if (item2.Name.Equals(injectMethod)) - injMethod = item2; - - if (tarMethod == null || injMethod == null) - return false; - RuntimeHelpers.PrepareMethod(tarMethod.MethodHandle); - RuntimeHelpers.PrepareMethod(injMethod.MethodHandle); - unsafe - { - if (IntPtr.Size == 4) - { - addrOfOrgMethodAddr = (long*)((int*)tarMethod.MethodHandle.Value.ToPointer() + 2); - orgMethodAddr = (long*)*addrOfOrgMethodAddr; - int* tar = (int*)tarMethod.MethodHandle.Value.ToPointer() + 2; - int* inj = (int*)injMethod.MethodHandle.Value.ToPointer() + 2; - *tar = *inj; - } - else - { - addrOfOrgMethodAddr = (long*)tarMethod.MethodHandle.Value.ToPointer() + 1; - orgMethodAddr = (long*)*addrOfOrgMethodAddr; - long* tar = (long*)tarMethod.MethodHandle.Value.ToPointer() + 1; - long* inj = (long*)injMethod.MethodHandle.Value.ToPointer() + 1; - *tar = *inj; - } - } - return true; - } - public unsafe static void RestoreMethod() - { - if ((long)orgMethodAddr != 0 && (long)addrOfOrgMethodAddr != 0) - { - if (IntPtr.Size == 4) - *(int*)addrOfOrgMethodAddr = (int)orgMethodAddr; - else - *addrOfOrgMethodAddr = (long)orgMethodAddr; - } - } -} diff --git a/src/IFoxCAD.LoadForm/LoaderForm.Designer.cs b/src/IFoxCAD.LoadForm/LoaderForm.Designer.cs index c26ef83..fccd531 100644 --- a/src/IFoxCAD.LoadForm/LoaderForm.Designer.cs +++ b/src/IFoxCAD.LoadForm/LoaderForm.Designer.cs @@ -28,98 +28,98 @@ protected override void Dispose(bool disposing) /// private void InitializeComponent() { - this.components = new System.ComponentModel.Container(); - this.loadDlldialog = new System.Windows.Forms.OpenFileDialog(); - this.textBox1 = new System.Windows.Forms.TextBox(); - this.button1 = new System.Windows.Forms.Button(); - this.label1 = new System.Windows.Forms.Label(); - this.button2 = new System.Windows.Forms.Button(); - this.label2 = new System.Windows.Forms.Label(); - this.toolTip1 = new System.Windows.Forms.ToolTip(this.components); - this.SuspendLayout(); - // - // textBox1 - // - this.textBox1.Location = new System.Drawing.Point(88, 35); - this.textBox1.Name = "textBox1"; - this.textBox1.ReadOnly = true; - this.textBox1.Size = new System.Drawing.Size(634, 34); - this.textBox1.TabIndex = 0; - this.textBox1.TextChanged += new System.EventHandler(this.TextBox1_TextChanged); - // - // button1 - // - this.button1.AutoSize = true; - this.button1.Location = new System.Drawing.Point(743, 30); - this.button1.Name = "button1"; - this.button1.Size = new System.Drawing.Size(177, 42); - this.button1.TabIndex = 1; - this.button1.Text = "手动选择DLL文件"; - this.button1.UseVisualStyleBackColor = true; - this.button1.Click += new System.EventHandler(this.Button1_Click); - // - // label1 - // - this.label1.AutoSize = true; - this.label1.Font = new System.Drawing.Font("微软雅黑", 12F, System.Drawing.FontStyle.Bold, System.Drawing.GraphicsUnit.Point, ((byte)(134))); - this.label1.ForeColor = System.Drawing.Color.Red; - this.label1.Location = new System.Drawing.Point(25, 97); - this.label1.Name = "label1"; - this.label1.Size = new System.Drawing.Size(428, 27); - this.label1.TabIndex = 2; - this.label1.Text = "直接拖拽DLL文件到本窗体可自动加载DLL文件"; - // - // button2 - // - this.button2.AutoSize = true; - this.button2.Location = new System.Drawing.Point(743, 89); - this.button2.Name = "button2"; - this.button2.Size = new System.Drawing.Size(177, 42); - this.button2.TabIndex = 1; - this.button2.Text = "加载"; - this.button2.UseVisualStyleBackColor = true; - this.button2.Click += new System.EventHandler(this.Button2_Click); - // - // label2 - // - this.label2.AutoSize = true; - this.label2.Location = new System.Drawing.Point(25, 38); - this.label2.Name = "label2"; - this.label2.Size = new System.Drawing.Size(57, 27); - this.label2.TabIndex = 3; - this.label2.Text = "路径:"; - // - // toolTip1 - // - this.toolTip1.AutomaticDelay = 200; - this.toolTip1.AutoPopDelay = 3000; - this.toolTip1.InitialDelay = 200; - this.toolTip1.ReshowDelay = 40; - this.toolTip1.ToolTipIcon = System.Windows.Forms.ToolTipIcon.Info; - // - // LoaderForm - // - this.AllowDrop = true; - this.AutoScaleDimensions = new System.Drawing.SizeF(12F, 27F); - this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; - this.ClientSize = new System.Drawing.Size(944, 160); - this.Controls.Add(this.label2); - this.Controls.Add(this.label1); - this.Controls.Add(this.button2); - this.Controls.Add(this.button1); - this.Controls.Add(this.textBox1); - this.Font = new System.Drawing.Font("微软雅黑", 12F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(134))); - this.FormBorderStyle = System.Windows.Forms.FormBorderStyle.FixedSingle; - this.Margin = new System.Windows.Forms.Padding(4, 5, 4, 5); - this.MaximizeBox = false; - this.MinimizeBox = false; - this.Name = "LoaderForm"; - this.Text = ".net dll 加载器"; - this.Load += new System.EventHandler(this.LoaderForm_Load); - this.DragDrop += new System.Windows.Forms.DragEventHandler(this.LoaderForm_DragDrop); - this.DragEnter += new System.Windows.Forms.DragEventHandler(this.LoaderForm_DragEnter); - this.ResumeLayout(false); - this.PerformLayout(); + this.components = new System.ComponentModel.Container(); + this.loadDlldialog = new System.Windows.Forms.OpenFileDialog(); + this.textBox1 = new System.Windows.Forms.TextBox(); + this.button1 = new System.Windows.Forms.Button(); + this.label1 = new System.Windows.Forms.Label(); + this.button2 = new System.Windows.Forms.Button(); + this.label2 = new System.Windows.Forms.Label(); + this.toolTip1 = new System.Windows.Forms.ToolTip(this.components); + this.SuspendLayout(); + // + // textBox1 + // + this.textBox1.Location = new System.Drawing.Point(80, 35); + this.textBox1.Name = "textBox1"; + this.textBox1.Size = new System.Drawing.Size(655, 29); + this.textBox1.TabIndex = 0; + this.textBox1.TextChanged += new System.EventHandler(this.TextBox1_TextChanged); + // + // button1 + // + this.button1.AutoSize = true; + this.button1.Location = new System.Drawing.Point(375, 86); + this.button1.Name = "button1"; + this.button1.Size = new System.Drawing.Size(177, 42); + this.button1.TabIndex = 1; + this.button1.Text = "手动选择DLL文件"; + this.button1.UseVisualStyleBackColor = true; + this.button1.Click += new System.EventHandler(this.Button1_Click); + // + // label1 + // + this.label1.AutoSize = true; + this.label1.Font = new System.Drawing.Font("微软雅黑", 12F, System.Drawing.FontStyle.Bold, System.Drawing.GraphicsUnit.Point, ((byte)(134))); + this.label1.ForeColor = System.Drawing.Color.Red; + this.label1.Location = new System.Drawing.Point(25, 97); + this.label1.Name = "label1"; + this.label1.Size = new System.Drawing.Size(344, 22); + this.label1.TabIndex = 2; + this.label1.Text = "直接拖拽DLL文件到本窗体可自动加载DLL文件"; + // + // button2 + // + this.button2.AutoSize = true; + this.button2.Location = new System.Drawing.Point(558, 86); + this.button2.Name = "button2"; + this.button2.Size = new System.Drawing.Size(177, 42); + this.button2.TabIndex = 1; + this.button2.Text = "加载"; + this.button2.UseVisualStyleBackColor = true; + this.button2.Click += new System.EventHandler(this.Button2_Click); + this.button2.KeyDown += new System.Windows.Forms.KeyEventHandler(this.Button2_KeyDown); + // + // label2 + // + this.label2.AutoSize = true; + this.label2.Location = new System.Drawing.Point(25, 38); + this.label2.Name = "label2"; + this.label2.Size = new System.Drawing.Size(46, 21); + this.label2.TabIndex = 3; + this.label2.Text = "路径:"; + // + // toolTip1 + // + this.toolTip1.AutomaticDelay = 200; + this.toolTip1.AutoPopDelay = 3000; + this.toolTip1.InitialDelay = 200; + this.toolTip1.ReshowDelay = 40; + this.toolTip1.ToolTipIcon = System.Windows.Forms.ToolTipIcon.Info; + // + // LoaderForm + // + this.AllowDrop = true; + this.AutoScaleDimensions = new System.Drawing.SizeF(10F, 21F); + this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; + this.ClientSize = new System.Drawing.Size(752, 140); + this.Controls.Add(this.label2); + this.Controls.Add(this.label1); + this.Controls.Add(this.button2); + this.Controls.Add(this.button1); + this.Controls.Add(this.textBox1); + this.Font = new System.Drawing.Font("微软雅黑", 12F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(134))); + this.FormBorderStyle = System.Windows.Forms.FormBorderStyle.FixedSingle; + this.Margin = new System.Windows.Forms.Padding(4, 5, 4, 5); + this.MaximizeBox = false; + this.MinimizeBox = false; + this.Name = "LoaderForm"; + this.Text = ".net dll 加载器"; + this.Load += new System.EventHandler(this.LoaderForm_Load); + this.DragDrop += new System.Windows.Forms.DragEventHandler(this.LoaderForm_DragDrop); + this.DragEnter += new System.Windows.Forms.DragEventHandler(this.LoaderForm_DragEnter); + this.ResumeLayout(false); + this.PerformLayout(); } diff --git a/src/IFoxCAD.LoadForm/LoaderForm.cs b/src/IFoxCAD.LoadForm/LoaderForm.cs index 18169d7..7e4d933 100644 --- a/src/IFoxCAD.LoadForm/LoaderForm.cs +++ b/src/IFoxCAD.LoadForm/LoaderForm.cs @@ -12,11 +12,9 @@ public LoaderForm() static string? LoadDll(string path) { - //var ed = Acap.DocumentManager.MdiActiveDocument.Editor; var ad = new AssemblyDependent(); var ls = new List(); ad.Load(path, ls); - //ed.WriteMessage(AssemblyDependent.PrintMessage(ls)); return AssemblyDependent.PrintMessage(ls); } @@ -79,5 +77,11 @@ void LoaderForm_DragEnter(object sender, DragEventArgs e) else e.Effect = DragDropEffects.None; } + + private void Button2_KeyDown(object sender, KeyEventArgs e) + { + if (e.KeyCode == Keys.Enter) + Button2_Click(sender, e); + } } diff --git a/src/IFoxCAD.LoadForm/LoaderForm.resx b/src/IFoxCAD.LoadForm/LoaderForm.resx index 7278b0e..9361c31 100644 --- a/src/IFoxCAD.LoadForm/LoaderForm.resx +++ b/src/IFoxCAD.LoadForm/LoaderForm.resx @@ -112,15 +112,15 @@ 2.0 - System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - + 17, 17 - + 172, 17 \ No newline at end of file diff --git a/tests/TestShared/TestNetLoad.cs b/tests/TestShared/TestNetLoad.cs index 6d913cb..3a1f0f3 100644 --- a/tests/TestShared/TestNetLoad.cs +++ b/tests/TestShared/TestNetLoad.cs @@ -1,11 +1,20 @@ -namespace Test; +using System.Windows.Forms; + +namespace Test; public class NetLoad { + static IFoxCAD.LoadEx.LoaderForm? _form; [CommandMethod("netloadx")] public static void Test_NetLoad() { - IFoxCAD.LoadEx.LoaderForm form = new(); - Acap.ShowModalDialog(form); + _form ??= new(); + + if (!_form.Visible) + _form.Visible = true; + + _form.Show(); + //Acap.ShowModalDialog(_form); + _form.Focus(); } } -- Gitee From 65d51ba312148ee3feaf88bfc1274196f55bc6ef Mon Sep 17 00:00:00 2001 From: liuqihong <540762622@qq.com> Date: Thu, 18 Aug 2022 20:09:03 +0800 Subject: [PATCH 400/675] =?UTF-8?q?=E5=B0=86net=E7=89=88=E6=9C=AC=E6=94=B9?= =?UTF-8?q?=E4=B8=BA=E5=A4=A7=E5=86=99?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/IFoxCAD.Basal/CLS/ValueTuple.cs | 2 +- src/IFoxCAD.Basal/IFoxCAD.Basal.csproj | 2 +- .../IFoxCAD.Aacad08/IFoxCAD.Acad08.csproj | 4 ++-- .../IFoxCAD.Acad09plus.csproj | 2 +- src/IFoxCAD.WPF/IFoxCAD.WPF.csproj | 4 ++-- tests/TestAcad08/GlobalUsings.cs | 11 ++++++++++- tests/TestAcad08/TestAcad08.csproj | 9 +++++++-- tests/TestAcad09plus/TestAcad09plus.csproj | 18 +++++++++--------- tests/TestConsole/Program.cs | 2 +- tests/TestConsole/TestConsole.csproj | 9 ++------- 10 files changed, 36 insertions(+), 27 deletions(-) diff --git a/src/IFoxCAD.Basal/CLS/ValueTuple.cs b/src/IFoxCAD.Basal/CLS/ValueTuple.cs index bece1ed..d1db08b 100644 --- a/src/IFoxCAD.Basal/CLS/ValueTuple.cs +++ b/src/IFoxCAD.Basal/CLS/ValueTuple.cs @@ -8,7 +8,7 @@ using System.Numerics.Hashing; /* * 惊惊: - * 首先是因为有人想要编译的时候只形成一个dll,然后把元组塞入IFox,同时也补充了net35没有元组的遗憾. + * 首先是因为有人想要编译的时候只形成一个dll,然后把元组塞入IFox,同时也补充了NET35没有元组的遗憾. * 而利用nuget元组包必然会形成依赖地狱. * * 如果你的工程使用了nuget元组包,就造成了必须要剔除IFox. diff --git a/src/IFoxCAD.Basal/IFoxCAD.Basal.csproj b/src/IFoxCAD.Basal/IFoxCAD.Basal.csproj index dd80bb3..3c441a3 100644 --- a/src/IFoxCAD.Basal/IFoxCAD.Basal.csproj +++ b/src/IFoxCAD.Basal/IFoxCAD.Basal.csproj @@ -4,7 +4,7 @@ preview enable - net35;NET40;NET45 + NET35;NET40;NET45 true 0.3.6 InspireFunction diff --git a/src/IFoxCAD.Cad/IFoxCAD.Aacad08/IFoxCAD.Acad08.csproj b/src/IFoxCAD.Cad/IFoxCAD.Aacad08/IFoxCAD.Acad08.csproj index 91a82ab..4ccd972 100644 --- a/src/IFoxCAD.Cad/IFoxCAD.Aacad08/IFoxCAD.Acad08.csproj +++ b/src/IFoxCAD.Cad/IFoxCAD.Aacad08/IFoxCAD.Acad08.csproj @@ -1,7 +1,7 @@  - net35;net40;net45 + NET35;NET40;NET45 true enable true @@ -35,7 +35,7 @@ - + diff --git a/src/IFoxCAD.Cad/IFoxCAD.Aacad09plus/IFoxCAD.Acad09plus.csproj b/src/IFoxCAD.Cad/IFoxCAD.Aacad09plus/IFoxCAD.Acad09plus.csproj index 956e46a..474ceee 100644 --- a/src/IFoxCAD.Cad/IFoxCAD.Aacad09plus/IFoxCAD.Acad09plus.csproj +++ b/src/IFoxCAD.Cad/IFoxCAD.Aacad09plus/IFoxCAD.Acad09plus.csproj @@ -1,7 +1,7 @@  - net35;net40;net45 + NET35;NET40;NET45 true enable true diff --git a/src/IFoxCAD.WPF/IFoxCAD.WPF.csproj b/src/IFoxCAD.WPF/IFoxCAD.WPF.csproj index 9fa74ff..8020198 100644 --- a/src/IFoxCAD.WPF/IFoxCAD.WPF.csproj +++ b/src/IFoxCAD.WPF/IFoxCAD.WPF.csproj @@ -4,7 +4,7 @@ preview enable - net45 + NET45 true true true @@ -22,7 +22,7 @@ 开启可空类型. - + DEBUG;TRACE diff --git a/tests/TestAcad08/GlobalUsings.cs b/tests/TestAcad08/GlobalUsings.cs index 92d6400..917387b 100644 --- a/tests/TestAcad08/GlobalUsings.cs +++ b/tests/TestAcad08/GlobalUsings.cs @@ -11,6 +11,15 @@ global using System.ComponentModel; global using System.Runtime.CompilerServices; + +// +// +//#if !NET472 +//global using System.Web.Script.Serialization;/*序列化的类 程序集:System.Web.Extensions.dll*/ +//#else +//global using System.Text.Json; +//#endif + /// autocad 引用 global using Autodesk.AutoCAD.ApplicationServices; global using Autodesk.AutoCAD.EditorInput; @@ -18,7 +27,7 @@ global using Autodesk.AutoCAD.DatabaseServices; global using Autodesk.AutoCAD.Geometry; global using Autodesk.AutoCAD.Runtime; -global using Acgi = Autodesk.AutoCAD.GraphicsInterface; +global using Acgi = Autodesk.AutoCAD.GraphicsInterface; global using Acap = Autodesk.AutoCAD.ApplicationServices.Application; global using Registry = Microsoft.Win32.Registry; diff --git a/tests/TestAcad08/TestAcad08.csproj b/tests/TestAcad08/TestAcad08.csproj index 604472f..1388890 100644 --- a/tests/TestAcad08/TestAcad08.csproj +++ b/tests/TestAcad08/TestAcad08.csproj @@ -3,15 +3,20 @@ preview enable - net35 + NET35 true true x86 - + + + + + + diff --git a/tests/TestAcad09plus/TestAcad09plus.csproj b/tests/TestAcad09plus/TestAcad09plus.csproj index 2d3b44f..a5ae899 100644 --- a/tests/TestAcad09plus/TestAcad09plus.csproj +++ b/tests/TestAcad09plus/TestAcad09plus.csproj @@ -3,28 +3,28 @@ preview enable - net45 + NET45 true true x64 - + - + - - TestView.xaml - + + TestView.xaml + - - Designer - + + Designer + diff --git a/tests/TestConsole/Program.cs b/tests/TestConsole/Program.cs index aa31926..99a5e5f 100644 --- a/tests/TestConsole/Program.cs +++ b/tests/TestConsole/Program.cs @@ -32,7 +32,7 @@ // Console.WriteLine((byte)enumItem); var sb = new StringBuilder(); -/*因为 net framework 没写好的原因,导致直接使用迭代器反而更慢,到了net60就迭代器比foreach更快*/ +/*因为 net framework 没写好的原因,导致直接使用迭代器反而更慢,到了NET60就迭代器比foreach更快*/ var enums = Enum.GetValues(typeof(Season)).GetEnumerator(); while (enums.MoveNext()) { diff --git a/tests/TestConsole/TestConsole.csproj b/tests/TestConsole/TestConsole.csproj index 293a6aa..53e4165 100644 --- a/tests/TestConsole/TestConsole.csproj +++ b/tests/TestConsole/TestConsole.csproj @@ -5,7 +5,7 @@ enable Exe - net45 + NET45 enable preview @@ -13,10 +13,5 @@ - - - - - - + -- Gitee From 139a8cce47884a42a4fda4dc8863d653d715e26c Mon Sep 17 00:00:00 2001 From: liuqihong <540762622@qq.com> Date: Thu, 18 Aug 2022 20:09:19 +0800 Subject: [PATCH 401/675] =?UTF-8?q?=E6=94=B9=E4=B8=BA=E5=8D=95=E4=BE=8B?= =?UTF-8?q?=E6=A8=A1=E5=BC=8F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- tests/TestShared/TestNetLoad.cs | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/tests/TestShared/TestNetLoad.cs b/tests/TestShared/TestNetLoad.cs index 3a1f0f3..75e2783 100644 --- a/tests/TestShared/TestNetLoad.cs +++ b/tests/TestShared/TestNetLoad.cs @@ -1,6 +1,4 @@ -using System.Windows.Forms; - -namespace Test; +namespace Test; public class NetLoad { @@ -13,6 +11,7 @@ public static void Test_NetLoad() if (!_form.Visible) _form.Visible = true; + _form.Show(); //Acap.ShowModalDialog(_form); _form.Focus(); -- Gitee From 82c4a20187bce603f771ebe5580c0bb681287c10 Mon Sep 17 00:00:00 2001 From: liuqihong <540762622@qq.com> Date: Thu, 18 Aug 2022 20:13:19 +0800 Subject: [PATCH 402/675] =?UTF-8?q?=E4=BF=9D=E6=8C=81=E5=92=8Cdev=E5=88=86?= =?UTF-8?q?=E6=94=AF=E4=B8=80=E6=A0=B7=E7=9A=84readme?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/README.md b/README.md index 1796e2a..e4ec848 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,10 @@ # IFoxCAD +#### 释义 +IFox中: +- IF:为Inspire Function(中文名:跃动方程)的首字母缩写 +- Fox:起初`雪山飞狐`(又狐哥)在明经论坛发布了[开源库](http://bbs.mjtd.com/thread-75701-1-1.html),后`落魄山人`(又小山山)基于狐哥的开源库创建了NFox开源项目,后经多重原因NFox被废弃,小山山又开创新开源项目,取名为`IF+Fox=IFox`,一语双关,简洁而不简单。 + #### 介绍 基于.NET的Cad二次开发类库。 -- Gitee From c4d586c474887f714fc32b7c00a828a80f277027 Mon Sep 17 00:00:00 2001 From: liuqihong <540762622@qq.com> Date: Fri, 19 Aug 2022 03:16:16 +0800 Subject: [PATCH 403/675] st --- .../IFoxCAD - Backup.Acad08.csproj | 56 ++++++ src/IFoxCAD.LoadForm/AssemblyDependent.cs | 161 ++++++++++-------- src/IFoxCAD.LoadForm/IFoxCAD.LoadEx.csproj | 4 +- src/IFoxCAD.LoadForm/LoaderForm.Designer.cs | 3 +- src/IFoxCAD.LoadForm/LoaderForm.cs | 37 ++-- tests/TestShared/TestNetLoad.cs | 7 +- 6 files changed, 183 insertions(+), 85 deletions(-) create mode 100644 src/IFoxCAD.Cad/IFoxCAD.Aacad08/IFoxCAD - Backup.Acad08.csproj diff --git a/src/IFoxCAD.Cad/IFoxCAD.Aacad08/IFoxCAD - Backup.Acad08.csproj b/src/IFoxCAD.Cad/IFoxCAD.Aacad08/IFoxCAD - Backup.Acad08.csproj new file mode 100644 index 0000000..ced3eed --- /dev/null +++ b/src/IFoxCAD.Cad/IFoxCAD.Aacad08/IFoxCAD - Backup.Acad08.csproj @@ -0,0 +1,56 @@ + + + + NET35 + true + enable + true + true + 0.3.7 + InspireFunction + xsfhlzh;vicwjb;liuqihong + 基于.NET的Cad二次开发类库 + InspireFunction + https://gitee.com/inspirefunction/ifoxcad + https://gitee.com/inspirefunction/ifoxcad.git + git + IFoxCAD;CAD;AutoCad;C#;NET + 增加很多东西. + true + true + preview + true + LICENSE + true + + + + + + + + + + DEBUG + + + $(Configuration);ac2008;ac2009 + + + + + True + + + + + + + + + + + + diff --git a/src/IFoxCAD.LoadForm/AssemblyDependent.cs b/src/IFoxCAD.LoadForm/AssemblyDependent.cs index 6cce1ff..65e452b 100644 --- a/src/IFoxCAD.LoadForm/AssemblyDependent.cs +++ b/src/IFoxCAD.LoadForm/AssemblyDependent.cs @@ -1,4 +1,5 @@ -using Mono.Cecil.Cil; +#define HarmonyPatch +#define HarmonyPatch_1 namespace IFoxCAD.LoadEx; @@ -8,8 +9,17 @@ namespace IFoxCAD.LoadEx; * 免得污染了cad工程的纯洁 */ +#if HarmonyPatch_1 +[HarmonyPatch("Autodesk.AutoCAD.ApplicationServices.ExtensionLoader", "OnAssemblyLoad")] +#endif public class AssemblyDependent : IDisposable { +#if HarmonyPatch + //这个是不能删除的,否则就不执行了 + //HarmonyPatch hook method 返回 false 表示拦截原函数 + public static bool Prefix() { return false; } +#endif + #region 字段和事件 /// /// 当前域加载事件,运行时出错的话,就靠这个事件来解决 @@ -19,14 +29,7 @@ public event ResolveEventHandler CurrentDomainAssemblyResolveEvent add { AppDomain.CurrentDomain.AssemblyResolve += value; } remove { AppDomain.CurrentDomain.AssemblyResolve -= value; } } - /// - /// cad程序域依赖_内存区(不可以卸载) - /// - readonly Assembly[] _cadAssembly; - /// - /// cad程序域依赖_映射区(不可以卸载) - /// - readonly Assembly[] _cadAssemblyRef; + /// /// 拦截cad的Loader异常:默认是 /// @@ -39,9 +42,7 @@ public event ResolveEventHandler CurrentDomainAssemblyResolveEvent /// public AssemblyDependent() { - //初始化一次,反复load,所以这里只放公共元素 - _cadAssembly = AppDomain.CurrentDomain.GetAssemblies(); - _cadAssemblyRef = AppDomain.CurrentDomain.ReflectionOnlyGetAssemblies(); + //初始化一次,反复load CurrentDomainAssemblyResolveEvent += AssemblyHelper.DefaultAssemblyResolve; } #endregion @@ -59,12 +60,6 @@ public AssemblyDependent() /// public bool Load(string dllFullName, List loadStates, bool byteLoad = true) { - //var accAsb = typeof(Document).Assembly; - //CSharpUtils.ReplaceMethod( - // accAsb.GetType("Autodesk.AutoCAD.ApplicationServices.ExtensionLoader"), - // "OnAssemblyLoad", typeof(AssemblyDependent), "OnAssemblyLoad", - // BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Static | BindingFlags.Instance); - if (dllFullName == null) throw new ArgumentNullException(nameof(dllFullName)); @@ -72,8 +67,13 @@ public bool Load(string dllFullName, List loadStates, bool byteLoad = if (!File.Exists(dllFullName)) throw new ArgumentException("路径不存在"); + //程序集数组要动态获取(每次Load的时候), + //否则会变成一个固定数组,造成加载了之后也不会出现成员 + var cadAssembly = AppDomain.CurrentDomain.GetAssemblies(); + var cadAssemblyRef = AppDomain.CurrentDomain.ReflectionOnlyGetAssemblies(); + List allRefs = new(); - GetAllRefPaths(dllFullName, allRefs); + GetAllRefPaths(cadAssembly, cadAssemblyRef, dllFullName, allRefs); bool dllFullNameLoadOk = false; @@ -81,34 +81,32 @@ public bool Load(string dllFullName, List loadStates, bool byteLoad = //这里有问题,从尾巴开始的,就一定是没有任何引用吗? for (int i = allRefs.Count - 1; i >= 0; i--) { - var path = allRefs[i]; + var allRef = allRefs[i]; //路径转程序集名 - var an = AssemblyName.GetAssemblyName(path).FullName; - var assembly = _cadAssembly.FirstOrDefault(a => a.FullName == an); + var an = AssemblyName.GetAssemblyName(allRef).FullName; + var assembly = cadAssembly.FirstOrDefault(a => a.FullName == an); if (assembly != null) { - loadStates.Add(new LoadState(path, false));//版本号没变不加载 + loadStates.Add(new LoadState(allRef, false));//版本号没变不加载 continue; } //有一次true,就是true - if (path == dllFullName) + if (allRef == dllFullName) dllFullNameLoadOk = true; try { - var ass = GetPdbAssembly(path, byteLoad); + var ass = GetPdbAssembly(allRef); if (ass == null) - { if (byteLoad) - ass = Assembly.Load(File.ReadAllBytes(path)); + ass = Assembly.Load(File.ReadAllBytes(allRef)); else - ass = Assembly.LoadFile(path); - } - loadStates.Add(new LoadState(path, true, ass));/*加载成功*/ + ass = Assembly.LoadFile(allRef); + loadStates.Add(new LoadState(allRef, true, ass));/*加载成功*/ } - catch { loadStates.Add(new LoadState(path, false));/*错误造成*/ } + catch { loadStates.Add(new LoadState(allRef, false));/*错误造成*/ } } return dllFullNameLoadOk; } @@ -119,7 +117,7 @@ public bool Load(string dllFullName, List loadStates, bool byteLoad = /// /// /// - Assembly? GetPdbAssembly(string path, bool byteLoad) + Assembly? GetPdbAssembly(string path) { #if DEBUG //为了实现Debug时候出现断点,见链接,加依赖 @@ -129,7 +127,7 @@ public bool Load(string dllFullName, List loadStates, bool byteLoad = var dir = Path.GetDirectoryName(path); var pdbName = Path.GetFileNameWithoutExtension(path) + ".pdb"; var pdbFullName = Path.Combine(dir, pdbName); - if (File.Exists(pdbFullName) && byteLoad) + if (File.Exists(pdbFullName)) return Assembly.Load(File.ReadAllBytes(path), File.ReadAllBytes(pdbFullName)); #endif return null; @@ -138,16 +136,21 @@ public bool Load(string dllFullName, List loadStates, bool byteLoad = /// /// 递归获取加载链 /// + /// 程序集_内存区 + /// 程序集_映射区 /// dll文件位置 /// 返回的集合 /// - void GetAllRefPaths(string dllFullName, List dllFullNamesOut) + void GetAllRefPaths(Assembly[] cadAssembly, + Assembly[] cadAssemblyRef, + string dllFullName, + List dllFullNamesOut) { if (dllFullNamesOut.Contains(dllFullName) || !File.Exists(dllFullName)) return; dllFullNamesOut.Add(dllFullName); - var assemblyAsRef = GetAssembly(dllFullName); + var assemblyAsRef = GetAssembly(cadAssembly, cadAssemblyRef, dllFullName); if (assemblyAsRef == null) return; @@ -167,22 +170,25 @@ void GetAllRefPaths(string dllFullName, List dllFullNamesOut) path + ".exe" }; for (int j = 0; j < paths.Length; j++) - GetAllRefPaths(paths[j], dllFullNamesOut); + GetAllRefPaths(cadAssembly, cadAssemblyRef, paths[j], dllFullNamesOut); } } /// /// 在内存区和映射区找已经加载的程序集 /// - /// - /// + /// 程序集_内存区 + /// 程序集_映射区 + /// dll文件位置 /// - Assembly? GetAssembly(string dllFullName) + Assembly? GetAssembly(Assembly[] cadAssembly, + Assembly[] cadAssemblyRef, + string dllFullName) { //路径转程序集名 var assName = AssemblyName.GetAssemblyName(dllFullName).FullName; //在当前程序域的 assemblyAs内存区 和 assemblyAsRef映射区 找这个程序集名 - var assemblyAs = _cadAssembly.FirstOrDefault(ass => ass.FullName == assName); + var assemblyAs = cadAssembly.FirstOrDefault(ass => ass.FullName == assName); //内存区有表示加载过 //映射区有表示查找过,但没有加载(一般来说不存在.只是debug会注释掉 Assembly.Load 的时候用来测试) @@ -190,7 +196,7 @@ void GetAllRefPaths(string dllFullName, List dllFullNamesOut) return assemblyAs; //映射区 - var assemblyAsRef = _cadAssemblyRef.FirstOrDefault(ass => ass.FullName == assName); + var assemblyAsRef = cadAssemblyRef.FirstOrDefault(ass => ass.FullName == assName); //内存区和映射区都没有的话就把dll加载到映射区,用来找依赖表 if (assemblyAsRef != null) @@ -199,30 +205,44 @@ void GetAllRefPaths(string dllFullName, List dllFullNamesOut) var byteRef = File.ReadAllBytes(dllFullName); if (PatchExtensionLoader) { - //QQ1548253108:这里会报错,但是我测试不出来它报错.他提供了解决方案. - /* 方案一: +#if HarmonyPatch_1 + /* QQ1548253108:这里会报错,他提供了解决方案. + * 方案一: * 在类上面加 [HarmonyPatch("Autodesk.AutoCAD.ApplicationServices.ExtensionLoader", "OnAssemblyLoad")] - * const string ext = "Autodesk.AutoCAD.ApplicationServices.ExtensionLoader"; - * Harmony hm = new(ext); - * hm.PatchAll(); - * assemblyAsRef = Assembly.ReflectionOnlyLoad(byteRef); - * hm.UnpatchAll(ext); */ - - //方案二: const string ext = "Autodesk.AutoCAD.ApplicationServices.ExtensionLoader"; Harmony hm = new(ext); - hm.Patch(typeof(Document).Assembly - .GetType(ext) - .GetMethod("OnAssemblyLoad"), - new HarmonyMethod(GetType(), "Dummy")); + hm.PatchAll(); + assemblyAsRef = Assembly.ReflectionOnlyLoad(byteRef); + hm.UnpatchAll(ext); +#endif +#if HarmonyPatch_2 + //方案二:跟cad耦合了 + const string ext = "Autodesk.AutoCAD.ApplicationServices.ExtensionLoader"; + var docAss = typeof(Autodesk.AutoCAD.ApplicationServices.Document).Assembly; + var a = docAss.GetType(ext);//这里会空 + var b = a?.GetMethod("OnAssemblyLoad"); + Harmony hm = new(ext); + hm.Patch(b, new HarmonyMethod(GetType(), "Dummy")); assemblyAsRef = Assembly.ReflectionOnlyLoad(byteRef); hm.UnpatchAll(ext); +#endif } else { - // assemblyAsRef = Assembly.ReflectionOnlyLoad(dllFullName); //没有依赖会直接报错 - assemblyAsRef = Assembly.ReflectionOnlyLoad(byteRef); + /* + * 0x01 没有依赖会直接报错 assemblyAsRef = Assembly.ReflectionOnlyLoad(dllFullName); + * 0x02 重复加载无修改的同一个dll,会出现如下异常: + * System.IO.FileLoadException: + * “API 限制: 程序集“”已从其他位置加载。无法从同一个 Appdomain 中的另一新位置加载该程序集。” + * catch 兜不住的,仍然会在cad上面打印,原因是程序集数组要动态获取 + */ + try + { + assemblyAsRef = Assembly.ReflectionOnlyLoad(byteRef); + } + catch (System.IO.FileLoadException) + { } } return assemblyAsRef; } @@ -236,26 +256,31 @@ void GetAllRefPaths(string dllFullName, List dllFullNamesOut) return null; var sb = new StringBuilder(); - bool flag = true; - foreach (var item in loadStates) + var ok = loadStates.FindAll(a => a.State); + var no = loadStates.FindAll(a => !a.State); + + if (ok.Count != 0) { - if (!item.State) + sb.Append("** 这些文件加载成功!"); + foreach (var item in ok) { sb.Append(Environment.NewLine); - sb.Append("** "); + sb.Append("++ "); sb.Append(item.DllFullName); - sb.Append(Environment.NewLine); - sb.Append("** 此文件已加载,同时重复名称和版本号,跳过!"); - sb.Append(Environment.NewLine); - flag = false; } - } - if (flag) - { sb.Append(Environment.NewLine); - sb.Append("** 链式加载成功!"); sb.Append(Environment.NewLine); } + if (no.Count != 0) + { + sb.Append("** 这些文件已被加载过,同时重复名称和版本号,跳过!"); + foreach (var item in no) + { + sb.Append(Environment.NewLine); + sb.Append("-- "); + sb.Append(item.DllFullName); + } + } return sb.ToString(); } #endregion diff --git a/src/IFoxCAD.LoadForm/IFoxCAD.LoadEx.csproj b/src/IFoxCAD.LoadForm/IFoxCAD.LoadEx.csproj index 6a0b07e..176ef27 100644 --- a/src/IFoxCAD.LoadForm/IFoxCAD.LoadEx.csproj +++ b/src/IFoxCAD.LoadForm/IFoxCAD.LoadEx.csproj @@ -15,9 +15,7 @@ - - Form - + LoaderForm.cs diff --git a/src/IFoxCAD.LoadForm/LoaderForm.Designer.cs b/src/IFoxCAD.LoadForm/LoaderForm.Designer.cs index fccd531..9bde05e 100644 --- a/src/IFoxCAD.LoadForm/LoaderForm.Designer.cs +++ b/src/IFoxCAD.LoadForm/LoaderForm.Designer.cs @@ -114,7 +114,8 @@ private void InitializeComponent() this.MaximizeBox = false; this.MinimizeBox = false; this.Name = "LoaderForm"; - this.Text = ".net dll 加载器"; + this.StartPosition = System.Windows.Forms.FormStartPosition.CenterScreen; + this.Text = "netloadx .net dll 加载器"; this.Load += new System.EventHandler(this.LoaderForm_Load); this.DragDrop += new System.Windows.Forms.DragEventHandler(this.LoaderForm_DragDrop); this.DragEnter += new System.Windows.Forms.DragEventHandler(this.LoaderForm_DragEnter); diff --git a/src/IFoxCAD.LoadForm/LoaderForm.cs b/src/IFoxCAD.LoadForm/LoaderForm.cs index 7e4d933..5aeee74 100644 --- a/src/IFoxCAD.LoadForm/LoaderForm.cs +++ b/src/IFoxCAD.LoadForm/LoaderForm.cs @@ -5,30 +5,44 @@ public partial class LoaderForm : Form { string? _dllPath; + AssemblyDependent _ad; public LoaderForm() { + //Owner = form; + //MdiParent = form; + StartPosition = FormStartPosition.CenterScreen;//在当前屏幕中央 InitializeComponent(); + _ad = new AssemblyDependent(); } - static string? LoadDll(string path) + void LoaderForm_Load(object sender, EventArgs e) + { + // if (_dllPath != null) + // textBox1.Text = _dllPath; +#if NET35 + textBox1.Text = "G:\\K01.惊惊连盒\\net35\\JoinBoxAcad.dll"; +#else + textBox1.Text = "G:\\K01.惊惊连盒\\net48\\JoinBoxAcad.dll"; +#endif + + } + + string? LoadDll(string path) { - var ad = new AssemblyDependent(); var ls = new List(); - ad.Load(path, ls); + _ad.Load(path, ls); return AssemblyDependent.PrintMessage(ls); } void TextBox1_TextChanged(object sender, EventArgs e) { _dllPath = textBox1.Text; + if (string.IsNullOrEmpty(_dllPath?.Trim())) + return; toolTip1.SetToolTip(textBox1, Path.GetFullPath(textBox1.Text)); } - void LoaderForm_Load(object sender, EventArgs e) - { - if (_dllPath != null) - textBox1.Text = _dllPath; - } + void Button1_Click(object sender, EventArgs e) { @@ -48,8 +62,11 @@ void Button2_Click(object sender, EventArgs e) return; } - LoadDll(textBox1.Text); - MessageBox.Show("加载完毕!"); + var msg = LoadDll(textBox1.Text); + if (msg != null) + MessageBox.Show(msg, "加载完毕!"); + else + MessageBox.Show("无任何信息", "加载出现问题!"); } void LoaderForm_DragDrop(object sender, DragEventArgs e) diff --git a/tests/TestShared/TestNetLoad.cs b/tests/TestShared/TestNetLoad.cs index 75e2783..e6428fe 100644 --- a/tests/TestShared/TestNetLoad.cs +++ b/tests/TestShared/TestNetLoad.cs @@ -6,14 +6,15 @@ public class NetLoad [CommandMethod("netloadx")] public static void Test_NetLoad() { - _form ??= new(); + if (_form == null || _form.IsDisposed) + _form = new(); if (!_form.Visible) _form.Visible = true; - _form.Show(); - //Acap.ShowModalDialog(_form); _form.Focus(); + + //Acap.ShowModalDialog(_form); } } -- Gitee From ba5bf610f05d43c534e30e5fa9e7d9aa77f082ea Mon Sep 17 00:00:00 2001 From: liuqihong <540762622@qq.com> Date: Fri, 19 Aug 2022 03:28:07 +0800 Subject: [PATCH 404/675] =?UTF-8?q?=E4=BF=AE=E6=94=B9=E4=BA=86=E7=89=88?= =?UTF-8?q?=E6=9C=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../IFoxCAD.Aacad08/IFoxCAD.Acad08.csproj | 30 +++++-------------- 1 file changed, 7 insertions(+), 23 deletions(-) diff --git a/src/IFoxCAD.Cad/IFoxCAD.Aacad08/IFoxCAD.Acad08.csproj b/src/IFoxCAD.Cad/IFoxCAD.Aacad08/IFoxCAD.Acad08.csproj index 4ccd972..6603b76 100644 --- a/src/IFoxCAD.Cad/IFoxCAD.Aacad08/IFoxCAD.Acad08.csproj +++ b/src/IFoxCAD.Cad/IFoxCAD.Aacad08/IFoxCAD.Acad08.csproj @@ -1,7 +1,7 @@  - + - NET35;NET40;NET45 + NET35 true enable true @@ -30,34 +30,18 @@ - - - - - - - - - - DEBUG $(Configuration);ac2008;ac2009 - - $(Configuration);ac2013 - - - $(Configuration);ac2015 - - + - - True - - + + True + + - - - runtime - - - - - - runtime - - - - - - runtime - - - 4.3.0 - - + + DEBUG + + + $(Configuration);GCAD + - + + + runtime + + - - DEBUG - - - $(Configuration);ac2009 - - - $(Configuration);ac2013 - - - $(Configuration);ac2015 - - + True diff --git a/src/IFoxCAD.Cad/IFoxCAD.Zcad/GlobalUsings.cs b/src/IFoxCAD.Cad/IFoxCAD.Zcad/GlobalUsings.cs new file mode 100644 index 0000000..36eeee5 --- /dev/null +++ b/src/IFoxCAD.Cad/IFoxCAD.Zcad/GlobalUsings.cs @@ -0,0 +1,40 @@ +/// 系统引用 +global using System; +global using System.Collections; +global using System.Collections.Generic; +global using System.IO; +global using System.Linq; +global using System.Text; +global using System.Reflection; +global using System.Text.RegularExpressions; +global using Microsoft.Win32; +global using System.ComponentModel; +global using System.Runtime.InteropServices; + +global using Exception = System.Exception; + +/// autocad 引用 +global using ZwSoft.ZwCAD.ApplicationServices; +global using ZwSoft.ZwCAD.EditorInput; +global using ZwSoft.ZwCAD.Colors; +global using ZwSoft.ZwCAD.DatabaseServices; +global using ZwSoft.ZwCAD.Geometry; +global using ZwSoft.ZwCAD.Runtime; +global using Acap = ZwSoft.ZwCAD.ApplicationServices.Application; + +global using ZwSoft.ZwCAD.DatabaseServices.Filters; +global using ZwSoft.ZwCAD; + +//jig命名空间会引起Viewport/Polyline等等重义,最好逐个引入 using ZwSoft.ZwCAD.GraphicsInterface +global using WorldDraw = ZwSoft.ZwCAD.GraphicsInterface.WorldDraw; +global using Manager = ZwSoft.ZwCAD.GraphicsSystem.Manager; + +global using Group = ZwSoft.ZwCAD.DatabaseServices.Group; +global using Viewport = ZwSoft.ZwCAD.DatabaseServices.Viewport; + +global using System.Collections.Specialized; +global using Registry = Microsoft.Win32.Registry; +global using RegistryKey = Microsoft.Win32.RegistryKey; + +/// ifoxcad.basal 引用 +global using IFoxCAD.Basal; \ No newline at end of file diff --git a/src/IFoxCAD.Cad/IFoxCAD.Zcad/IFoxCAD.Zcad.csproj b/src/IFoxCAD.Cad/IFoxCAD.Zcad/IFoxCAD.Zcad.csproj index 7852e32..6b6305e 100644 --- a/src/IFoxCAD.Cad/IFoxCAD.Zcad/IFoxCAD.Zcad.csproj +++ b/src/IFoxCAD.Cad/IFoxCAD.Zcad/IFoxCAD.Zcad.csproj @@ -1,7 +1,7 @@  - net35;net40;net45 + NET45;NET48 true enable true @@ -22,47 +22,33 @@ true LICENSE true - x64 + - - - runtime - - - - - - runtime - - - - - - runtime - - - 4.3.0 - - - - - - DEBUG - - $(Configuration);ac2009 - - - $(Configuration);ac2013 - + - $(Configuration);ac2015 + $(Configuration);ZCAD + + + $(Configuration);ZCAD + + + + + runtime + + + + + + runtime + + + True -- Gitee From e7a04797fe4e5803f6f8a912447ab43edba1434e Mon Sep 17 00:00:00 2001 From: liuqihong <540762622@qq.com> Date: Sat, 20 Aug 2022 00:27:00 +0800 Subject: [PATCH 410/675] =?UTF-8?q?=E5=88=A0=E9=99=A4=E4=B8=80=E4=B8=AA?= =?UTF-8?q?=E6=9E=84=E9=80=A0=E5=87=BD=E6=95=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../ExtensionMethod/EntityBoundingInfo.cs | 41 ++++++++++--------- 1 file changed, 21 insertions(+), 20 deletions(-) diff --git a/src/IFoxCAD.Cad.Shared/ExtensionMethod/EntityBoundingInfo.cs b/src/IFoxCAD.Cad.Shared/ExtensionMethod/EntityBoundingInfo.cs index 97ca65a..b50698c 100644 --- a/src/IFoxCAD.Cad.Shared/ExtensionMethod/EntityBoundingInfo.cs +++ b/src/IFoxCAD.Cad.Shared/ExtensionMethod/EntityBoundingInfo.cs @@ -1,5 +1,7 @@ #define Debug_Cause_Error +using System.Windows; + namespace IFoxCAD.Cad; /// @@ -27,8 +29,8 @@ public struct BoundingInfo public Extents2d Extents2d => new(MinX, MinY, MaxX, MaxY); public BoundingInfo(double minX, double minY, double minZ, - double maxX, double maxY, double maxZ, - bool isEffective, double angle = 0) + double maxX, double maxY, double maxZ, + bool isEffective, double angle = 0) { MinX = minX; MinY = minY; @@ -46,24 +48,22 @@ public BoundingInfo(Point3d min, Point3d max, bool isEffective, double angle = 0 isEffective, angle) { } - public BoundingInfo(Rect rect, double angle = 0) - { - MinX = rect.X; - MinY = rect.Y; - MaxX = rect.Right; - MaxY = rect.Top; - Angle = angle; - } + //public BoundingInfo(Rect rect, double angle = 0) + //{ + // MinX = rect.X; + // MinY = rect.Y; + // MinZ = 0; + // MaxX = rect.Right; + // MaxY = rect.Top; + // MaxZ = 0; + // Angle = angle; + //} } public class EntityBoundingInfo { #region 保存异常类型的日志 /// - /// 错误信息保存路径 - /// - static string? _BoxLogAddress; - /// /// 包围盒错误文件路径 /// public static string BoxLogAddress @@ -75,9 +75,9 @@ public static string BoxLogAddress } set { _BoxLogAddress = value; } } + static string? _BoxLogAddress; static readonly HashSet _typeNames; /// - /// /// 为了保证更好的性能, /// 只是在第一次调用此功能的时候进行读取, /// 免得高频调用时候高频触发磁盘 @@ -185,7 +185,8 @@ static BoundingInfo GetBoxInfoInBlockReference(BlockReference brf) { try { - var fit = brf.GeometryExtentsBestFit();//这个获取是原点附近,需要平移到块基点 + //这个获取是原点附近,需要平移到块基点 + var fit = brf.GeometryExtentsBestFit(); var minX = fit.MinPoint.X + brf.Position.X; var minY = fit.MinPoint.Y + brf.Position.Y; var minZ = fit.MinPoint.Z + brf.Position.Z; @@ -258,9 +259,9 @@ static BoundingInfo GetBoxInfoInMText(MText mtxt) double ht = height + hb; Point3d center = mtxt.Location; - Point3d ptMin = new(center.X + wl, center.Y + hb, 0); - Point3d ptMax = new(center.X + wr, center.Y + ht, 0); - - return new(ptMin, ptMax, true, mtxt.Rotation); + return new(center.X + wl, center.Y + hb, 0, + center.X + wr, center.Y + ht, 0, + true, + mtxt.Rotation); } } \ No newline at end of file -- Gitee From 680849a94d9e10cce2faf8f87d03fec3a5680ff6 Mon Sep 17 00:00:00 2001 From: liuqihong <540762622@qq.com> Date: Sat, 20 Aug 2022 00:57:05 +0800 Subject: [PATCH 411/675] =?UTF-8?q?=E7=94=B1=E4=BA=8Egcad=E5=92=8Czcad?= =?UTF-8?q?=E7=BC=BA=E5=B0=91=E5=86=85=E5=AE=B9=E5=9B=A0=E6=AD=A4=E4=B8=8D?= =?UTF-8?q?=E4=BD=9C=E4=B8=BA=E4=B8=BB=E8=A6=81=E7=BB=B4=E6=8A=A4=E5=AF=B9?= =?UTF-8?q?=E8=B1=A1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- IFoxCAD.sln | 16 ---------------- .../ExtensionMethod/EntityBoundingInfo.cs | 3 --- 2 files changed, 19 deletions(-) diff --git a/IFoxCAD.sln b/IFoxCAD.sln index 0cfb30a..8dc6df1 100644 --- a/IFoxCAD.sln +++ b/IFoxCAD.sln @@ -27,10 +27,6 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Test", "Test", "{C0BEFC15-6 EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "IFoxCAD.LoadEx", "src\IFoxCAD.LoadForm\IFoxCAD.LoadEx.csproj", "{BFA1C130-8D87-45D0-8E79-301C1841A7A1}" EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "IFoxCAD.Gcad", "src\IFoxCAD.Cad\IFoxCAD.Gcad\IFoxCAD.Gcad.csproj", "{A5128914-23D9-409A-B40E-D436973E89D4}" -EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "IFoxCAD.Zcad", "src\IFoxCAD.Cad\IFoxCAD.Zcad\IFoxCAD.Zcad.csproj", "{3746383C-76A9-4E14-8FBD-038E51C9DC97}" -EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -69,14 +65,6 @@ Global {BFA1C130-8D87-45D0-8E79-301C1841A7A1}.Debug|Any CPU.Build.0 = Debug|Any CPU {BFA1C130-8D87-45D0-8E79-301C1841A7A1}.Release|Any CPU.ActiveCfg = Release|Any CPU {BFA1C130-8D87-45D0-8E79-301C1841A7A1}.Release|Any CPU.Build.0 = Release|Any CPU - {A5128914-23D9-409A-B40E-D436973E89D4}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {A5128914-23D9-409A-B40E-D436973E89D4}.Debug|Any CPU.Build.0 = Debug|Any CPU - {A5128914-23D9-409A-B40E-D436973E89D4}.Release|Any CPU.ActiveCfg = Release|Any CPU - {A5128914-23D9-409A-B40E-D436973E89D4}.Release|Any CPU.Build.0 = Release|Any CPU - {3746383C-76A9-4E14-8FBD-038E51C9DC97}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {3746383C-76A9-4E14-8FBD-038E51C9DC97}.Debug|Any CPU.Build.0 = Debug|Any CPU - {3746383C-76A9-4E14-8FBD-038E51C9DC97}.Release|Any CPU.ActiveCfg = Release|Any CPU - {3746383C-76A9-4E14-8FBD-038E51C9DC97}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -89,19 +77,15 @@ Global {CED63D2D-0AF6-4831-806D-5E8E9B0D0A07} = {C0BEFC15-6DF7-4636-BC76-4A0B88231470} {8C573180-523D-427D-8030-CAA7D6B5EB6B} = {C0BEFC15-6DF7-4636-BC76-4A0B88231470} {5F478F68-19BC-4A3A-AF39-8728F8D396DD} = {C0BEFC15-6DF7-4636-BC76-4A0B88231470} - {A5128914-23D9-409A-B40E-D436973E89D4} = {465C4E39-FBA2-417A-AB31-BDC01601F420} - {3746383C-76A9-4E14-8FBD-038E51C9DC97} = {465C4E39-FBA2-417A-AB31-BDC01601F420} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {31D6C754-CF6B-4AB0-9861-6923DD312350} EndGlobalSection GlobalSection(SharedMSBuildProjectFiles) = preSolution src\IFoxCAD.Cad.Shared\IFoxCAD.Cad.Shared.projitems*{36f87d06-88b3-45e3-a2a8-0fc737b25428}*SharedItemsImports = 5 - src\IFoxCAD.Cad.Shared\IFoxCAD.Cad.Shared.projitems*{3746383c-76a9-4e14-8fbd-038e51c9dc97}*SharedItemsImports = 5 tests\TestShared\TestShared.projitems*{5f478f68-19bc-4a3a-af39-8728f8d396dd}*SharedItemsImports = 5 src\IFoxCAD.Cad.Shared\IFoxCAD.Cad.Shared.projitems*{82fb8450-b971-4e30-859f-4b2ddb81f590}*SharedItemsImports = 13 tests\TestShared\TestShared.projitems*{8c573180-523d-427d-8030-caa7d6b5eb6b}*SharedItemsImports = 5 - src\IFoxCAD.Cad.Shared\IFoxCAD.Cad.Shared.projitems*{a5128914-23d9-409a-b40e-d436973e89d4}*SharedItemsImports = 5 src\IFoxCAD.Cad.Shared\IFoxCAD.Cad.Shared.projitems*{cc46e4e2-90df-48f5-b3d7-83fafed58f2a}*SharedItemsImports = 5 tests\TestShared\TestShared.projitems*{ced63d2d-0af6-4831-806d-5e8e9b0d0a07}*SharedItemsImports = 13 EndGlobalSection diff --git a/src/IFoxCAD.Cad.Shared/ExtensionMethod/EntityBoundingInfo.cs b/src/IFoxCAD.Cad.Shared/ExtensionMethod/EntityBoundingInfo.cs index b50698c..8511b59 100644 --- a/src/IFoxCAD.Cad.Shared/ExtensionMethod/EntityBoundingInfo.cs +++ b/src/IFoxCAD.Cad.Shared/ExtensionMethod/EntityBoundingInfo.cs @@ -1,7 +1,4 @@ #define Debug_Cause_Error - -using System.Windows; - namespace IFoxCAD.Cad; /// -- Gitee From 9f936ea1a05565d97946e9766884b83fc1d226e8 Mon Sep 17 00:00:00 2001 From: liuqihong <540762622@qq.com> Date: Sat, 20 Aug 2022 12:45:21 +0800 Subject: [PATCH 412/675] =?UTF-8?q?=E5=A2=9E=E5=8A=A0=E9=BB=98=E8=AE=A4?= =?UTF-8?q?=E6=89=93=E5=BC=80=E5=9B=A0=E6=AD=A4=E5=9B=BE=E5=B1=82=E7=9A=84?= =?UTF-8?q?=E8=A1=8C=E4=B8=BA?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/IFoxCAD.Cad.Shared/Runtime/DBTrans.cs | 5 +- src/IFoxCAD.Cad.Shared/Runtime/SymbolTable.cs | 56 ++++++++++++++----- 2 files changed, 44 insertions(+), 17 deletions(-) diff --git a/src/IFoxCAD.Cad.Shared/Runtime/DBTrans.cs b/src/IFoxCAD.Cad.Shared/Runtime/DBTrans.cs index bba7c2f..6747c7c 100644 --- a/src/IFoxCAD.Cad.Shared/Runtime/DBTrans.cs +++ b/src/IFoxCAD.Cad.Shared/Runtime/DBTrans.cs @@ -1,7 +1,8 @@ namespace IFoxCAD.Cad; /// -/// 事务栈,隐匿事务在数据库其中担任的角色 +/// 事务栈 +/// 隐匿事务在数据库其中担任的角色 /// public class DBTrans : IDisposable { @@ -219,13 +220,11 @@ public static implicit operator Transaction(DBTrans tr) /// public SymbolTable RegAppTable => _RegAppTable ??= new(this, Database.RegAppTableId); SymbolTable? _RegAppTable; - /// /// 标注样式表 /// public SymbolTable DimStyleTable => _DimStyleTable ??= new(this, Database.DimStyleTableId); SymbolTable? _DimStyleTable; - /// /// 线型表 /// diff --git a/src/IFoxCAD.Cad.Shared/Runtime/SymbolTable.cs b/src/IFoxCAD.Cad.Shared/Runtime/SymbolTable.cs index 11e1013..04b5e7a 100644 --- a/src/IFoxCAD.Cad.Shared/Runtime/SymbolTable.cs +++ b/src/IFoxCAD.Cad.Shared/Runtime/SymbolTable.cs @@ -1,5 +1,8 @@ namespace IFoxCAD.Cad; +using Autodesk.AutoCAD.DatabaseServices; +using System.Windows.Documents; + /// /// 委托执行状态 /// @@ -44,11 +47,23 @@ public class SymbolTable : IEnumerable /// /// 事务管理器 /// 符号表id - internal SymbolTable(DBTrans tr, ObjectId tableId) + /// 默认行为:例如打开隐藏图层 + internal SymbolTable(DBTrans tr, ObjectId tableId, bool defaultBehavior = true) { DTrans = tr; Database = tr.Database; CurrentSymbolTable = DTrans.GetObject(tableId)!; + + if (defaultBehavior) + { + if (CurrentSymbolTable is LayerTable layer) + { + //层表包含隐藏的,全部显示出来 + layer = layer.IncludingHidden; + if (layer is TTable tt) + CurrentSymbolTable = tt; + } + } } #endregion @@ -239,6 +254,7 @@ public IEnumerable GetRecords() /// /// 记录的名字集合 public IEnumerable GetRecordNames() => GetRecords().Select(record => record.Name); + /// /// 获取符合过滤条件的符号表记录名字集合 /// @@ -275,10 +291,10 @@ public ObjectId GetRecordFrom(SymbolTable table, string name, b using (ObjectIdCollection ids = new() { id }) { table.Database. - WblockCloneObjects(ids, + WblockCloneObjects(ids, CurrentSymbolTable.Id, idm, - DuplicateRecordCloning.Replace, + DuplicateRecordCloning.Replace, false); } rid = idm[id].Value; @@ -295,8 +311,8 @@ public ObjectId GetRecordFrom(SymbolTable table, string name, b /// 是否覆盖, 为覆盖, 为不覆盖 /// 对象id internal ObjectId GetRecordFrom(Func> tableSelector, - string fileName, - string name, + string fileName, + string name, bool over) { using var tr = new DBTrans(fileName); @@ -308,18 +324,30 @@ internal ObjectId GetRecordFrom(Func> tabl /// /// 遍历集合的迭代器,执行action委托 /// - /// 集合值的类型 - /// 集合 /// 要运行的委托 - public void ForEach(Action action, OpenMode openMode = OpenMode.ForRead) + /// 打开模式,默认为只读 + /// 检查id是否删除,默认false + public void ForEach(Action action, OpenMode openMode = OpenMode.ForRead, bool checkIdOk = false) { - //GetRecords().ForEach(re => action(re)); - - foreach (var item in this) + if (checkIdOk) { - var record = GetRecord(item, openMode); - if (record is not null) - action(record); + foreach (var id in this) + { + if (!id.IsOk()) + continue; + var record = GetRecord(id, openMode); + if (record is not null) + action(record); + } + } + else + { + foreach (var id in this) + { + var record = GetRecord(id, openMode); + if (record is not null) + action(record); + } } } #endregion -- Gitee From d8b2ebe99261d05b0e9e30b561023d263c022a3c Mon Sep 17 00:00:00 2001 From: liuqihong <540762622@qq.com> Date: Sat, 20 Aug 2022 21:03:00 +0800 Subject: [PATCH 413/675] =?UTF-8?q?=E5=A2=9E=E5=8A=A0=E4=BB=A3=E7=A0=81?= =?UTF-8?q?=E8=A7=84=E8=8C=83?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- ...43\347\240\201\350\247\204\350\214\203.md" | 112 ++++++++++++++++++ .../ExtensionMethod/DatabaseEx.cs | 5 +- .../IFoxCAD.Cad.Shared.projitems | 1 + src/IFoxCAD.Cad.Shared/Runtime/DBTrans.cs | 17 ++- src/IFoxCAD.Cad.Shared/Runtime/SymbolTable.cs | 52 +++++--- 5 files changed, 161 insertions(+), 26 deletions(-) create mode 100644 "docs/0x01 \344\273\243\347\240\201\350\247\204\350\214\203.md" diff --git "a/docs/0x01 \344\273\243\347\240\201\350\247\204\350\214\203.md" "b/docs/0x01 \344\273\243\347\240\201\350\247\204\350\214\203.md" new file mode 100644 index 0000000..89f0879 --- /dev/null +++ "b/docs/0x01 \344\273\243\347\240\201\350\247\204\350\214\203.md" @@ -0,0 +1,112 @@ +# IFox工程规范 + +## 代码规范 + +### 0x01 分离逻辑代码和业务代码 + +Good: + +```c# +foreach (xx in yyy) + if (xx == "a") + { + 业务(); + break; + } +``` + +Bad: + +```c# +bool flag = false; +foreach (xx in yyy) + if (xx == "a") + { + 业务(); + flag = true; + break; + } +if(!flag) + 其他业务(); +``` +Good: + +```c# +bool flag = false; +foreach (xx in yyy) + if (xx == "a") + { + flag = true; + break; + } +if(!flag) + 其他业务(); +else + 业务(); +``` + +主要原因是统一业务在判断分支上,能够更清晰分离逻辑代码和业务代码. + + +### 0x02 分离逻辑代码和业务代码 + +![img](%E4%BB%A3%E7%A0%81%E8%A7%84%E8%8C%83.assets/2HJE@WH1%60PPUBOH2ZFL$BT.png) + +写代码的时候总是要把业务代码独立成一个逻辑处理.这样主要是为了防止业务耦合: + 例如: for {业务1,业务2,业务1,业务2....} +如果有这样的逻辑,那么我们看代码的时候总是认为业务2某种条件必须要跟着业务1 +优化代码的人一看:这代码就不能动了!! +相信我,若干年后的你就是这个优化代码的人. + + + +### 0x03 .editorconfig 配置要求 + +c#的代码风格是两个大括号隔行 + +```c# +if() +{ + ... +} +``` + +但是,由于vs没有制作好的原因,导致`委托箭头`代码格式化总是会出现格式化错误. +所以我们推荐用 .editorconfig 文件约束这个`委托箭头` + +```mermaid +graph LR +vs --> 选项 --> 文本编辑器 --> c# -->代码样式,展开它 --> 格式设置 --> 新行 --> 右页,大括号的新行选项 --> 将lambda表达式的左括号置于新行,取消掉勾勾 +``` + +保存为 .editorconfig 文件: + +```mermaid +graph LR +vs --> 选项 --> 文本编辑器 --> c# -->代码样式 --> 右页,基于设置生成.editorconfig文件 --> 保存到工程中 +``` + +这样每次打开工程vs会自动识别这个 .editorconfig 文件,而不会用你电脑默认设置的. + + + +## 文件管理规范 + +### 0x01 (软性) + +.git文件夹通常和.sln放一起, +主要原因是我们习惯同时在vs控制台和git bash使用git命令 +.vs控制台的默认所在路径会是sln, +为了避免输入不同的相对路径时候发生错误,因而规定 + + + +### 0x02 + +git子模块往往写入到一个统一的文件夹: assets +主要是明白哪些是人家的,哪些是我的. + + + + + diff --git a/src/IFoxCAD.Cad.Shared/ExtensionMethod/DatabaseEx.cs b/src/IFoxCAD.Cad.Shared/ExtensionMethod/DatabaseEx.cs index 624931c..c4261e8 100644 --- a/src/IFoxCAD.Cad.Shared/ExtensionMethod/DatabaseEx.cs +++ b/src/IFoxCAD.Cad.Shared/ExtensionMethod/DatabaseEx.cs @@ -6,8 +6,9 @@ public static class DatabaseEx /// 后台开图文字偏移处理 /// /// 0x01 此方案利用前台数据库进行处理
- /// 0x02 当关闭所有前台文档时,会出现无时,不能使用(惊惊没有测试过此状态)
- /// 0x03 当关闭所有前台文档时,如何发送命令呢?那就是利用跨进程通讯 + /// 0x02 当关闭所有前台文档时,会出现无时,应该不能使用(惊惊没有测试过此状态)
+ /// 测试条件是:当关闭所有前台文档时,那么如何发送命令呢?那就是利用跨进程通讯
+ /// 0x03 此问题主要出现是这个线性引擎上面,在参照/深度克隆的底层共用此技术,导致单行文字偏移
///
///
/// 后台打开的数据库 diff --git a/src/IFoxCAD.Cad.Shared/IFoxCAD.Cad.Shared.projitems b/src/IFoxCAD.Cad.Shared/IFoxCAD.Cad.Shared.projitems index f937e1e..32b1b8d 100644 --- a/src/IFoxCAD.Cad.Shared/IFoxCAD.Cad.Shared.projitems +++ b/src/IFoxCAD.Cad.Shared/IFoxCAD.Cad.Shared.projitems @@ -18,6 +18,7 @@ + diff --git a/src/IFoxCAD.Cad.Shared/Runtime/DBTrans.cs b/src/IFoxCAD.Cad.Shared/Runtime/DBTrans.cs index 6747c7c..db1394f 100644 --- a/src/IFoxCAD.Cad.Shared/Runtime/DBTrans.cs +++ b/src/IFoxCAD.Cad.Shared/Runtime/DBTrans.cs @@ -1,4 +1,6 @@ -namespace IFoxCAD.Cad; +using System.Windows.Controls; + +namespace IFoxCAD.Cad; /// /// 事务栈 @@ -346,22 +348,25 @@ public ObjectId GetObjectId(string handleString) /// dwg版本,默认为2004 public void SaveDwgFile(DwgVersion version = DwgVersion.AC1800) { - bool flag = true; + Document? doca = null; foreach (Document doc in Acap.DocumentManager) { - // 前台开图,使用命令保存 if (doc.Database.Filename == this.Database.Filename) { - doc.SendStringToExecute("_qsave\n", false, true, true); //不需要切换文档 - flag = false; + doca = doc; break; } } - if (flag) + if (doca == null) { // 后台开图,用数据库保存 Database.SaveAs(Database.Filename, version); } + else + { + // 前台开图,使用命令保存;不需要切换文档 + doca.SendStringToExecute("_qsave\n", false, true, true); + } } #endregion diff --git a/src/IFoxCAD.Cad.Shared/Runtime/SymbolTable.cs b/src/IFoxCAD.Cad.Shared/Runtime/SymbolTable.cs index 04b5e7a..fc81d58 100644 --- a/src/IFoxCAD.Cad.Shared/Runtime/SymbolTable.cs +++ b/src/IFoxCAD.Cad.Shared/Runtime/SymbolTable.cs @@ -322,34 +322,50 @@ internal ObjectId GetRecordFrom(Func> tabl #region 遍历 /// - /// 遍历集合的迭代器,执行action委托 + /// 遍历集合的迭代器,执行action委托 /// /// 要运行的委托 /// 打开模式,默认为只读 /// 检查id是否删除,默认false - public void ForEach(Action action, OpenMode openMode = OpenMode.ForRead, bool checkIdOk = false) + public void ForEach(Action action, + OpenMode openMode = OpenMode.ForRead, + bool checkIdOk = false) { - if (checkIdOk) + foreach (var id in this) { - foreach (var id in this) - { - if (!id.IsOk()) - continue; - var record = GetRecord(id, openMode); - if (record is not null) - action(record); - } + if (checkIdOk && !id.IsOk()) + continue; + var record = GetRecord(id, openMode); + if (record is not null) + action(record); } - else + } + + + /// + /// 遍历集合的迭代器,执行action委托 + /// + /// 要执行的委托 + /// 打开模式,默认为只读 + /// 检查id是否删除,默认false + public void ForEach(Action action, + OpenMode openMode = OpenMode.ForRead, + bool checkIdOk = false) + { + DelegateState state = DelegateState.Go;/*这种方式比Action改Func更友好*/ + foreach (var id in this) { - foreach (var id in this) - { - var record = GetRecord(id, openMode); - if (record is not null) - action(record); - } + if (checkIdOk && !id.IsOk()) + continue; + var record = GetRecord(id, openMode); + if (record is not null) + action(record, state); + if (state == DelegateState.Break) + break; } } + + #endregion #region IEnumerable 成员 -- Gitee From 3a30ea1c97a607500c674c9cbfac6ea9a5e59cbf Mon Sep 17 00:00:00 2001 From: liuqihong <540762622@qq.com> Date: Sat, 20 Aug 2022 21:27:28 +0800 Subject: [PATCH 414/675] =?UTF-8?q?=E8=A1=A5=E5=85=85=E7=BC=BA=E5=A4=B1?= =?UTF-8?q?=E5=9B=BE=E7=89=87?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../2HJE@WH1`PPUBOH2ZFL$BT.png" | Bin 0 -> 65425 bytes ...43\347\240\201\350\247\204\350\214\203.md" | 2 +- src/IFoxCAD.Cad.Shared/XrefEx.cs | 362 ++++++++++++++++++ 3 files changed, 363 insertions(+), 1 deletion(-) create mode 100644 "docs/0x01\344\273\243\347\240\201\350\247\204\350\214\203.assets/2HJE@WH1`PPUBOH2ZFL$BT.png" rename "docs/0x01 \344\273\243\347\240\201\350\247\204\350\214\203.md" => "docs/0x01\344\273\243\347\240\201\350\247\204\350\214\203.md" (96%) create mode 100644 src/IFoxCAD.Cad.Shared/XrefEx.cs diff --git "a/docs/0x01\344\273\243\347\240\201\350\247\204\350\214\203.assets/2HJE@WH1`PPUBOH2ZFL$BT.png" "b/docs/0x01\344\273\243\347\240\201\350\247\204\350\214\203.assets/2HJE@WH1`PPUBOH2ZFL$BT.png" new file mode 100644 index 0000000000000000000000000000000000000000..459930b8d54d075d94b4d06e960fbbc7edbdd17b GIT binary patch literal 65425 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%0D(%0zSv4n9Z9Cz1byS!p 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
  • <@J2maNRvDBImzCe;=5^G|HpMi#5^ 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

    npTOhE zMs)Ncq38P(QKunnvUY)boY!pC8w2{k2y~Y~@^YKKS|1F0fj@tC&VV|U@!>j{8sFcT zyxwB+T7w#a{8K8u6R1C>Jg|YyA^oXiDSXg>(pfI=nBIZ^+kvDqzQdXtt0eKb0&f~n z<}vS;J%TzOHdE(q99b`_=>K*AdOz4?P$_=vAO6V0!I0@`-b$H!44nPj0j5(rUm;pI W(pFr0_ZPrGKXDNm;R-?BAO8;*`RE`3 literal 0 HcmV?d00001 diff --git "a/docs/0x01 \344\273\243\347\240\201\350\247\204\350\214\203.md" "b/docs/0x01\344\273\243\347\240\201\350\247\204\350\214\203.md" similarity index 96% rename from "docs/0x01 \344\273\243\347\240\201\350\247\204\350\214\203.md" rename to "docs/0x01\344\273\243\347\240\201\350\247\204\350\214\203.md" index 89f0879..7393d64 100644 --- "a/docs/0x01 \344\273\243\347\240\201\350\247\204\350\214\203.md" +++ "b/docs/0x01\344\273\243\347\240\201\350\247\204\350\214\203.md" @@ -50,7 +50,7 @@ else ### 0x02 分离逻辑代码和业务代码 -![img](%E4%BB%A3%E7%A0%81%E8%A7%84%E8%8C%83.assets/2HJE@WH1%60PPUBOH2ZFL$BT.png) +![img](0x01%E4%BB%A3%E7%A0%81%E8%A7%84%E8%8C%83.assets/2HJE@WH1%60PPUBOH2ZFL$BT.png) 写代码的时候总是要把业务代码独立成一个逻辑处理.这样主要是为了防止业务耦合: 例如: for {业务1,业务2,业务1,业务2....} diff --git a/src/IFoxCAD.Cad.Shared/XrefEx.cs b/src/IFoxCAD.Cad.Shared/XrefEx.cs new file mode 100644 index 0000000..9ac31a0 --- /dev/null +++ b/src/IFoxCAD.Cad.Shared/XrefEx.cs @@ -0,0 +1,362 @@ +using System.Xml.Linq; + +namespace IFoxCAD.Cad; + + +//测试代码(图纸仅绑定,不做其他处理,如清理,关图层,删除嵌套被卸载的参照等) +public class TestCmd_BindXrefs +{ + //后台绑定 + [CommandMethod("MyXBindXrefs")] + public static void MyXBindXrefs() + { + string newfile = @"D:\桌面\测试文件\三梅中学-教学楼-结构施工图 - 副本.dwg"; + using var tr = new DBTrans(newfile, openMode: FileOpenMode.OpenForReadAndAllShare/*后台绑定需要特别注意*/); + //用于后期删除嵌套被卸载的参照 + Dictionary nested = new(); + XrefEx.XBindXrefs(tr, nested, SymTypes.LayerTable); + tr.SaveDwgFile(); + } + +#if true2 + //后台绑定 + [CommandMethod("MyXBindXrefs")] + public static void MyXBindXrefs() + { + using var tr = new DBTrans(filePath, openMode: FileOpenMode.OpenForReadAndAllShare/*后台绑定注意*/); + //用于后期删除嵌套被卸载的参照 + Dictionary nested = new(); + tr.XBindXrefs(nested); + tr.SaveDwgFile(); + } +#endif + + //前台绑定 + [CommandMethod("MyXBindXrefs1")] + public static void MyXBindXrefs1() + { + using var tr = new DBTrans(); + //用于后期删除嵌套被卸载的参照 + Dictionary nested = new(); + tr.XBindXrefs(nested); + tr.SaveDwgFile(); + } +} + +public enum SymTypes +{ + ///

    + /// 图层表 + /// + LayerTable = 1, + /// + /// 文字样式表 + /// + TextStyleTable = 2, + /// + /// 标注样式表 + /// + DimStyleTable = 4, + /// + /// 线型表 + /// + LinetypeTable = 8, + /// + /// 注册应用程序表 + /// + RegAppTable = 16, + + All = LayerTable | TextStyleTable | DimStyleTable | LinetypeTable | RegAppTable +} + +public static class XrefEx +{ + ///
    + /// + /// 双重绑定参照 + /// 该方法仅用于"图层绑定"项,其他项有异常eWasOpenForNotify + /// + /// 参考链接 + /// + /// + /// 嵌套参照名称,id + /// 需要绑定的符号表 + /// 是否拆离参照 + /// 是否删除被卸载的嵌套参照 + public static void XBindXrefs(this DBTrans tr, + Dictionary nested, + SymTypes xNum = SymTypes.LayerTable, + bool detachXref = true, + bool easeNested = true) + { + DoubleBind(tr, nested, detachXref, (xbindXrefsIds) => { + + // 起初测试是将九大符号表记录均加入的,但经实测不行...(为什么?存疑) + if ((xNum & SymTypes.LayerTable) == SymTypes.LayerTable) + tr.LayerTable.ForEach(tabRec => { + AddedxbindXrefsIds(tabRec, xbindXrefsIds); + }, checkIdOk: true); + + if ((xNum & SymTypes.TextStyleTable) == SymTypes.TextStyleTable) + tr.TextStyleTable.ForEach(tabRec => { + AddedxbindXrefsIds(tabRec, xbindXrefsIds); + }, checkIdOk: true); + + if ((xNum & SymTypes.LinetypeTable) == SymTypes.LinetypeTable) + tr.LinetypeTable.ForEach(tabRec => { + AddedxbindXrefsIds(tabRec, xbindXrefsIds); + }, checkIdOk: true); + + if ((xNum & SymTypes.DimStyleTable) == SymTypes.DimStyleTable) + tr.DimStyleTable.ForEach(tabRec => { + AddedxbindXrefsIds(tabRec, xbindXrefsIds); + }, checkIdOk: true); + + if ((xNum & SymTypes.RegAppTable) == SymTypes.RegAppTable) + tr.RegAppTable.ForEach(tabRec => { + AddedxbindXrefsIds(tabRec, xbindXrefsIds); + }, checkIdOk: true); + }); + + // 内部删除嵌套参照的块操作 + if (easeNested) + { + foreach (var item in nested) + { + var name = item.Value; + if (tr.BlockTable.Has(name)) + tr.GetObject(tr.BlockTable[name], OpenMode.ForWrite)? + .Erase(); + } + } + } + + /// + /// 符号表记录加入xbind + /// + /// + /// + /// + static void AddedxbindXrefsIds(TTableRecord tabRec, ObjectIdCollection xbindXrefsIds) + where TTableRecord : SymbolTableRecord + { + if (tabRec.IsResolved) + xbindXrefsIds.Add(tabRec.ObjectId); + } + + static void DoubleBind(this DBTrans tr, + Dictionary nested, + bool detachXref, + Action xbindAction) + { + DatabaseEx.DBTextDeviation(tr.Database, () => { + + //解析外部参照:此功能不能锁定文档 + //useThreadEngine==true,线性引擎,会在cad命令历史打印一些AEC信息,并导致绑定慢一点...具体作用不详 + //doNewOnly==true,仅处理 Unresolved_未融入(未解析)的参照 + tr.Database.ResolveXrefs(useThreadEngine: false, doNewOnly: false); + + //有这个 eWrongObjectType 异常,说明还是 xbindXrefsIds 的集合id有问题 + //xbind才是绑其他符号表,/xbind绑块表也会有异常 + var xbindXrefsIds = new ObjectIdCollection(); + //bind只绑块表 + var bindXrefsIds = new ObjectIdCollection(); + + tr.BlockTable.ForEach(btr => { + if (btr.IsFromExternalReference && btr.IsResolved) + bindXrefsIds.Add(btr.ObjectId); + }, checkIdOk: true); + + // 直接拆离的id + List detachXrefIds = new(); + + // 补充是否被嵌套卸载的处理 + var xg = tr.Database.GetHostDwgXrefGraph(true);//参数:包含僵尸参照 + for (int j = 0; j < xg.NumNodes; j++) + { + var xrNode = xg.GetXrefNode(j); + switch (xrNode.XrefStatus) + { + case XrefStatus.Unresolved://未融入_ResolveXrefs参数2 + break; + case XrefStatus.FileNotFound://未融入(未解析)_未找到文件 + break; + case XrefStatus.Unreferenced://未参照 + detachXrefIds.Add(xrNode.BlockTableRecordId); + break; + case XrefStatus.Unloaded://已卸载 + { + var xrId = xrNode.BlockTableRecordId; + if (!xrId.IsOk()) + break; + var btr = tr.GetObject(xrId); + if (btr != null && btr.IsFromExternalReference) + { + if (!xrNode.IsNested) + detachXrefIds.Add(xrId); + else if (!nested.ContainsKey(xrId)) + nested.Add(xrId, xrNode.Name);//嵌套参照 + } + } + break; + case XrefStatus.Resolved://已融入_就是可以绑定的 + break; + case XrefStatus.NotAnXref://不是外部参照 + break; + default: + break; + } + } + + //拆离未参照的文件 + if (detachXref) + { + for (int i = 0; i < detachXrefIds.Count; i++) + tr.Database.DetachXref(detachXrefIds[i]); + } + + xbindAction?.Invoke(xbindXrefsIds); + + //若有嵌套参照被卸载,重载 + var keys = nested.Keys.ToArray(); + if (keys.Length > 0) + tr.Database.ReloadXrefs(new ObjectIdCollection(keys)); + + // 切勿交换,若交换秩序,则会绑定无效 + if (xbindXrefsIds.Count > 0) + tr.Database.XBindXrefs(xbindXrefsIds, true); + if (bindXrefsIds.Count > 0) + tr.Database.BindXrefs(bindXrefsIds, true); + }); + } + + +#if true2 + /// + /// 清理符号7表(块表,标注样式表,图层表,线型表,文字样式表,视口样式表,注册应用表) + ///注:视图样式表和坐标系表不能这样清理,有异常 + /// + /// 数据库 + /// 无返回值 + public static void Purge(Database db) + { + ObjectIdCollection ids; + ObjectIdCollection ids1; + ObjectIdCollection ids2; + ObjectIdCollection ids3; + ObjectIdCollection ids4; + ObjectIdCollection ids5; + //ObjectIdCollection ids6; + //ObjectIdCollection ids7; + ObjectIdCollection ids8; + using (Transaction tr = db.TransactionManager.StartTransaction()) + { + //块表 + BlockTable blockt = tr.GetObject(db.BlockTableId, OpenMode.ForRead) as BlockTable; + //标注样式表1 + DimStyleTable dimt = tr.GetObject(db.DimStyleTableId, OpenMode.ForRead) as DimStyleTable; + //图层表2 + LayerTable layert = tr.GetObject(db.LayerTableId, OpenMode.ForRead) as LayerTable; + //线型表3 + LinetypeTable linetypet = tr.GetObject(db.LinetypeTableId, OpenMode.ForRead) as LinetypeTable; + //文字样式4 + TextStyleTable textstylet = tr.GetObject(db.TextStyleTableId, OpenMode.ForRead) as TextStyleTable; + //视口样式表5 + ViewportTable viewport = tr.GetObject(db.ViewportTableId, OpenMode.ForRead) as ViewportTable; + //注册应用表8 + RegAppTable regappt = tr.GetObject(db.RegAppTableId, OpenMode.ForRead) as RegAppTable; + + #region 开始清理 + do + { + ids = new ObjectIdCollection(blockt.Cast().ToArray()); + db.Purge(ids); + if (ids != null) + { + foreach (ObjectId id in ids) + { + tr.GetObject(id, OpenMode.ForWrite).Erase(); + } + } + ids1 = new ObjectIdCollection(dimt.Cast().ToArray()); + db.Purge(ids1); + if (ids1 != null) + { + foreach (ObjectId id in ids1) + { + tr.GetObject(id, OpenMode.ForWrite).Erase(); + } + } + ids2 = new ObjectIdCollection(layert.Cast().ToArray()); + db.Purge(ids2); + if (ids2 != null) + { + foreach (ObjectId id in ids2) + { + tr.GetObject(id, OpenMode.ForWrite).Erase(); + } + } + ids3 = new ObjectIdCollection(linetypet.Cast().ToArray()); + db.Purge(ids3); + if (ids3 != null) + { + foreach (ObjectId id in ids3) + { + tr.GetObject(id, OpenMode.ForWrite).Erase(); + } + } + ids4 = new ObjectIdCollection(textstylet.Cast().ToArray()); + db.Purge(ids4); + if (ids4 != null) + { + foreach (ObjectId id in ids4) + { + tr.GetObject(id, OpenMode.ForWrite).Erase(); + } + } + ids5 = new ObjectIdCollection(viewport.Cast().ToArray()); + db.Purge(ids5); + if (ids5 != null) + { + foreach (ObjectId id in ids5) + { + tr.GetObject(id, OpenMode.ForWrite).Erase(); + } + } + #region 经测试视图样式表和坐标系表不能这样清理,不然有异常 + ////视图样式表6 + //ViewTable viewt = tr.GetObject(db.ViewTableId, OpenMode.ForRead) as ViewTable; + //ids6 = new ObjectIdCollection(viewt.Cast().ToArray()); + //db.Purge(ids6); + //foreach (ObjectId id in ids6) + //{ + // ViewTableRecord btr6 = (ViewTableRecord)tr.GetObject(id, OpenMode.ForWrite); + // btr6.Erase(); + //} + ////坐标系表7 + //UcsTable ucst = tr.GetObject(db.UcsTableId, OpenMode.ForRead) as UcsTable; + //ids7 = new ObjectIdCollection(ucst.Cast().ToArray()); + //db.Purge(ids7); + //foreach (ObjectId id in ids7) + //{ + // UcsTableRecord btr7 = (UcsTableRecord)tr.GetObject(id, OpenMode.ForWrite); + // btr7.Erase(); + //} + #endregion + ids8 = new ObjectIdCollection(regappt.Cast().ToArray()); + db.Purge(ids8); + if (ids8 != null) + { + foreach (ObjectId id in ids8) + { + tr.GetObject(id, OpenMode.ForWrite).Erase(); + } + } + } while (ids.Count > 0 || ids1.Count > 0 || ids2.Count > 0 || ids3.Count > 0 || ids4.Count > 0 || ids5.Count > 0 || ids8.Count > 0); + #endregion + + tr.Commit();//提交事务 + } + } +#endif +} \ No newline at end of file -- Gitee From d52a855786ace7cd974a286787b98883c260363e Mon Sep 17 00:00:00 2001 From: liuqihong <540762622@qq.com> Date: Sun, 21 Aug 2022 01:03:32 +0800 Subject: [PATCH 415/675] =?UTF-8?q?=E4=BC=98=E5=8C=96=E5=A5=BD=E4=BA=86Xre?= =?UTF-8?q?fEx?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../ExtensionMethod/ObjectIdEx.cs | 5 +- .../ExtensionMethod/XrefEx.cs | 726 ++++++++++++++++++ src/IFoxCAD.Cad.Shared/Runtime/DBTrans.cs | 10 +- src/IFoxCAD.Cad.Shared/XrefEx.cs | 362 --------- 4 files changed, 732 insertions(+), 371 deletions(-) create mode 100644 src/IFoxCAD.Cad.Shared/ExtensionMethod/XrefEx.cs delete mode 100644 src/IFoxCAD.Cad.Shared/XrefEx.cs diff --git a/src/IFoxCAD.Cad.Shared/ExtensionMethod/ObjectIdEx.cs b/src/IFoxCAD.Cad.Shared/ExtensionMethod/ObjectIdEx.cs index e147d6c..1b51a4b 100644 --- a/src/IFoxCAD.Cad.Shared/ExtensionMethod/ObjectIdEx.cs +++ b/src/IFoxCAD.Cad.Shared/ExtensionMethod/ObjectIdEx.cs @@ -32,7 +32,10 @@ public static class ObjectIdEx /// 打开模式 /// 打开删除对象 /// 指定类型对象集合 - public static IEnumerable GetObject(this IEnumerable ids, OpenMode mode = OpenMode.ForRead, bool openErased = false, Transaction? tr = default) where T : DBObject + public static IEnumerable GetObject(this IEnumerable ids, + OpenMode mode = OpenMode.ForRead, + bool openErased = false, + Transaction? tr = default) where T : DBObject { return ids.Select(id => id.GetObject(mode, openErased, tr)); } diff --git a/src/IFoxCAD.Cad.Shared/ExtensionMethod/XrefEx.cs b/src/IFoxCAD.Cad.Shared/ExtensionMethod/XrefEx.cs new file mode 100644 index 0000000..b61588e --- /dev/null +++ b/src/IFoxCAD.Cad.Shared/ExtensionMethod/XrefEx.cs @@ -0,0 +1,726 @@ +//#define error_demo +//#define test_demo + +namespace IFoxCAD.Cad; + +using Autodesk.AutoCAD.DatabaseServices; +using System.Security.Cryptography; +using System.Xml.Linq; + +#if test_demo +//测试代码(图纸仅绑定,不做其他处理,如清理,关图层,删除嵌套被卸载的参照等) +public class TestCmd_BindXrefs +{ + //后台绑定 + [CommandMethod("MyXBindXrefs")] + public static void MyXBindXrefs() + { + string newfile = @"D:\桌面\测试文件\三梅中学-教学楼-结构施工图 - 副本.dwg"; + using var tr = new DBTrans(newfile, openMode: FileOpenMode.OpenForReadAndAllShare/*后台绑定特别注意*/); + tr.SetXref(XrefBindingModes.Bind); + tr.SaveDwgFile(); + } + + //前台绑定 + [CommandMethod("MyXBindXrefs1")] + public static void MyXBindXrefs1() + { + using var tr = new DBTrans(); + tr.SetXref(XrefBindingModes.Bind); + tr.SaveDwgFile(); + } +} + +#endif + +#region 参照绑定类 +public enum XrefBindingModes : byte +{ + /// + /// 卸载 + /// + Unload, + /// + /// 重载 + /// + Reload, + /// + /// 拆离 + /// + Detach, + /// + /// 绑定 + /// + Bind, +} + +public enum SymModes : ushort +{ + /// + /// 块表 + /// + BlockTable = 1, + + /// + /// 图层表 + /// + LayerTable = 2, + /// + /// 文字样式表 + /// + TextStyleTable = 4, + /// + /// 注册应用程序表 + /// + RegAppTable = 8, + /// + /// 标注样式表 + /// + DimStyleTable = 16, + /// + /// 线型表 + /// + LinetypeTable = 32, + Option1 = LayerTable | TextStyleTable | DimStyleTable | LinetypeTable | RegAppTable, + + /// + /// 用户坐标系表 + /// + UcsTable = 64, + /// + /// 视图表 + /// + ViewTable = 128, + /// + /// 视口表 + /// + ViewportTable = 256, + Option2 = UcsTable | ViewTable | ViewportTable, + + //全部 + All = BlockTable | Option1 | Option2, + + //清理 + Purge = + BlockTable | + DimStyleTable | + LayerTable | + LinetypeTable | + TextStyleTable | + ViewportTable | + RegAppTable, +} + + +public static class XrefEx +{ + /// + /// 修改外部参照 + /// + /// + /// 处理参照的枚举 + /// 要处理的参照名称,就处理所有 + /// + /// + /// 参数==才触发
    + /// 需要绑定的符号表:请保持默认
    + /// 目前仅推荐用于
    + /// 其他项有异常: eWasOpenForNotify
    + ///
    + /// + public static void SetXref(this DBTrans tr, + XrefBindingModes xrefModes, + HashSet? names = null, + SymModes xrefModesBind = SymModes.LayerTable) + { + DatabaseEx.DBTextDeviation(tr.Database, () => { + switch (xrefModes) + { + case XrefBindingModes.Bind: + { + //此功能有绑定出错的问题 + //db.BindXrefs(xrefIds, true); + + //绑定后会自动拆离 + //此功能修补了上面缺失 + Dictionary nested = new(); + DoubleBindx(tr, nested, xrefModesBind); + } + break; + case XrefBindingModes.Detach: + { + var xrefIds = GetXrefNode(tr.Database, names); + foreach (ObjectId id in xrefIds) + tr.Database.DetachXref(id); + } + break; + case XrefBindingModes.Unload: + { + var xrefIds = GetXrefNode(tr.Database, names); + if (xrefIds.Count > 0) + tr.Database.UnloadXrefs(xrefIds); + } + break; + case XrefBindingModes.Reload: + { + var xrefIds = GetXrefNode(tr.Database, names); + if (xrefIds.Count > 0) + tr.Database.ReloadXrefs(xrefIds); + } + break; + default: + break; + } + }); + } + + /// + /// 获取参照 + /// + /// + /// 过滤名称 + /// + static ObjectIdCollection GetXrefNode(Database db, HashSet? names) + { + //储存要处理的参照id + var xrefIds = new ObjectIdCollection(); + XrefNodeForEach(db, (xNodeName, xNodeId, xNodeStatus, xNodeIsNested) => { + if (names is null) + xrefIds.Add(xNodeId); //每个都加入 + else if (names.Contains(xNodeName)) + xrefIds.Add(xNodeId); //只加入名称相同的 + }); + return xrefIds; + } + + /// + /// 遍历参照 + /// + /// + /// (参照名,参照块表记录id,参照状态,是否嵌入) + static void XrefNodeForEach(Database db, Action action) + { + // btRec.IsFromOverlayReference 是覆盖 + // btRec.GetXrefDatabase(true) 外部参照数据库 + + //解析外部参照:此功能不能锁定文档 + //useThreadEngine==true,线性引擎,会在cad命令历史打印一些AEC信息,并导致绑定慢一点...具体作用不详 + //doNewOnly==true,仅处理 Unresolved_未融入(未解析)的参照 + db.ResolveXrefs(useThreadEngine: false, doNewOnly: false); + + var xg = db.GetHostDwgXrefGraph(true);//参数:包含僵尸参照 + for (int i = 0; i < xg.NumNodes; i++) + { + var xNode = xg.GetXrefNode(i); + if (!xNode.BlockTableRecordId.IsOk()) + continue; + + action.Invoke(xNode.Name, + xNode.BlockTableRecordId, + xNode.XrefStatus, + xNode.IsNested); + } + } + + + /// + /// 双重绑定参照 + /// 参考链接 + /// + /// + /// 嵌套参照(块表记录id,名称) + /// + /// 是否拆离参照,默认true:学官方的绑定后自动拆离 + /// 是否删除被卸载的嵌套参照,默认true + static void DoubleBindx(DBTrans tr, + Dictionary nested, + SymModes symType = SymModes.LayerTable, + bool detachXref = true, + bool eraseNested = true) + { + DoubleBindxAction(tr, nested, detachXref, (xbindXrefsIds) => { + + // 起初测试是将九大符号表记录均加入的,但经实测不行...(为什么?存疑) + #region Option1 + if ((symType & SymModes.LayerTable) == SymModes.LayerTable) + AddedxbindIds(xbindXrefsIds, tr.LayerTable); + + if ((symType & SymModes.TextStyleTable) == SymModes.TextStyleTable) + AddedxbindIds(xbindXrefsIds, tr.TextStyleTable); + + if ((symType & SymModes.RegAppTable) == SymModes.RegAppTable) + AddedxbindIds(xbindXrefsIds, tr.RegAppTable); + + if ((symType & SymModes.DimStyleTable) == SymModes.DimStyleTable) + AddedxbindIds(xbindXrefsIds, tr.DimStyleTable); + + if ((symType & SymModes.LinetypeTable) == SymModes.LinetypeTable) + AddedxbindIds(xbindXrefsIds, tr.LinetypeTable); + #endregion + + #region Option2 + if ((symType & SymModes.UcsTable) == SymModes.UcsTable) + AddedxbindIds(xbindXrefsIds, tr.UcsTable); + + if ((symType & SymModes.ViewTable) == SymModes.ViewTable) + AddedxbindIds(xbindXrefsIds, tr.ViewTable); + + if ((symType & SymModes.ViewportTable) == SymModes.ViewportTable) + AddedxbindIds(xbindXrefsIds, tr.ViewportTable); + #endregion + }); + + // 内部删除嵌套参照的块操作 + if (eraseNested) + { +#if ac2008 + //因为Acad08索引器存在会暴露isErase的(桌子底层的原因), + //也就是可能获取两个名称一样的,只能用遍历的方式进行 + HashSet names = new(); + foreach (var item in nested) + names.Add(item.Value); + + //遍历全图,找到参照名称一样的删除 + tr.BlockTable.ForEach(btr => { + if (btr.IsLayout) + return; + if (names.Contains(btr.Name)) + { + btr.UpgradeOpen(); + btr.Erase(); + btr.DowngradeOpen(); + btr.Dispose(); + } + }, checkIdOk: true); +#else + foreach (var item in nested) + { + var name = item.Value; + if (tr.BlockTable.Has(name)) + tr.GetObject(tr.BlockTable[name], OpenMode.ForWrite)? + .Erase(); + } +#endif + } + } + + /// + /// 符号表记录加入xbind + /// + static void AddedxbindIds(ObjectIdCollection xbindXrefsIds, + SymbolTable symbolTable) + where TTable : SymbolTable + where TRecord : SymbolTableRecord, new() + { + symbolTable.ForEach(tabRec => { + if (tabRec.IsResolved) + xbindXrefsIds.Add(tabRec.ObjectId); + }, checkIdOk: true); + } + + static void DoubleBindxAction(this DBTrans tr, + Dictionary nested, + bool detachXref, + Action xbindAction) + { + + //有这个 eWrongObjectType 异常,说明还是 xbindXrefsIds 的集合id有问题 + //xbind才是绑其他符号表,/xbind绑块表也会有异常 + var xbindXrefsIds = new ObjectIdCollection(); + //bind只绑块表 + var bindXrefsIds = new ObjectIdCollection(); + + tr.BlockTable.ForEach(btr => { + if (btr.IsFromExternalReference && btr.IsResolved) + bindXrefsIds.Add(btr.ObjectId); + }, checkIdOk: true); + + // 直接拆离的id + List detachXrefIds = new(); + + // 补充是否被嵌套卸载的处理 + XrefNodeForEach(tr.Database, (xNodeName, xNodeId, xNodeStatus, xNodeIsNested) => { + switch (xNodeStatus) + { + case XrefStatus.Unresolved://未融入_ResolveXrefs参数2 + break; + case XrefStatus.FileNotFound://未融入(未解析)_未找到文件 + break; + case XrefStatus.Unreferenced://未参照 + detachXrefIds.Add(xNodeId); + break; + case XrefStatus.Unloaded://已卸载 + { + var btr = tr.GetObject(xNodeId); + if (btr != null && btr.IsFromExternalReference) + { + if (!xNodeIsNested) + detachXrefIds.Add(xNodeId); + else if (!nested.ContainsKey(xNodeId)) + nested.Add(xNodeId, xNodeName);//嵌套参照 + } + } + break; + case XrefStatus.Resolved://已融入_就是可以绑定的 + break; + case XrefStatus.NotAnXref://不是外部参照 + break; + default: + break; + } + }); + + + //拆离未参照的文件 + if (detachXref) + { + for (int i = 0; i < detachXrefIds.Count; i++) + tr.Database.DetachXref(detachXrefIds[i]); + } + + xbindAction?.Invoke(xbindXrefsIds); + + //若有嵌套参照被卸载,重载 + var keys = nested.Keys.ToArray(); + if (keys.Length > 0) + tr.Database.ReloadXrefs(new ObjectIdCollection(keys)); + + // 切勿交换,若交换秩序,则会绑定无效 + if (xbindXrefsIds.Count > 0) + tr.Database.XBindXrefs(xbindXrefsIds, true); + if (bindXrefsIds.Count > 0) + tr.Database.BindXrefs(bindXrefsIds, true); + } +} + +#endregion + +#region 参照路径工具类 +/// +/// 参照路径转换模式 +/// +public enum PathConverterModes : byte +{ + /// + /// 相对路径 + /// + Relative, + /// + /// 绝对路径 + /// + Complete +} + +/* + * https://blog.csdn.net/my98800/article/details/51450696 + * https://blog.csdn.net/lishuangquan1987/article/details/53678215 + * https://www.cnblogs.com/hont/p/5412340.html + */ +/// +/// 获取外部参照的路径 +/// +public class XrefPath +{ + + #region 属性 + /// + /// 外部参照保存的路径 + /// + /// 可能是
    + /// 0x01 相对路径
    + /// 0x02 绝对路径
    + /// 0x03 共目录优先找到的路径(文件夹整体移动会发生此类情况) + ///
    + ///
    + public string? PathSave { get; private set; } + /// + /// 找到的路径(参照面板的名称) + /// 路径不存在时,返回是外部参照dwg文件路径 + /// + public string? PathDescribe { get; private set; } + + string? _PathComplete; + /// + /// 绝对路径 + /// + public string? PathComplete + { + get + { + return _PathComplete ??= + PathConverter(_directory, PathDescribe, PathConverterModes.Complete); + } + } + + string? _PathRelative; + /// + /// 相对路径 + /// + public string? PathRelative + { + get + { + return _PathRelative ??= + PathConverter(_directory, PathComplete, PathConverterModes.Relative); + } + } + + /// + /// 是否外部参照 + /// + public bool IsFromExternalReference { get; private set; } + + /// + /// 基础路径 + /// + readonly string? _directory; + #endregion + + #region 构造 + /// + /// 获取外部参照的路径 + /// + /// 外部参照图元 + /// 事务 + /// 是否外部参照 + public XrefPath(BlockReference brf, DBTrans tr) + { + if (brf == null) + throw new ArgumentNullException(nameof(brf)); + + var btRec = tr.GetObject(brf.BlockTableRecord);//块表记录 + if (btRec == null) + return; + + _directory = Path.GetDirectoryName(tr.Database.Filename); + + IsFromExternalReference = btRec.IsFromExternalReference; + if (!IsFromExternalReference) + return; + + //相对路径==".\\AA.dwg";无路径=="AA.dwg"; + PathSave = btRec.PathName; + + //相对路径||绝对路径: + if ((!string.IsNullOrEmpty(PathSave) && PathSave[0] == '.') || File.Exists(PathSave)) + PathDescribe = PathSave; + else + { + //无路径: + var db = btRec.GetXrefDatabase(true); + PathDescribe = db.Filename; + } + } + #endregion + + #region 静态函数 + /// + /// 获取相对路径或者绝对路径 + /// + /// 基础目录(末尾无斜杠) + /// 相对路径或者绝对路径 + /// 依照枚举返回对应的字符串 + /// + public static string? PathConverter(string? directory, + string? fileRelations, + PathConverterModes converterModes) + { + if (directory == null) + throw new ArgumentNullException(nameof(directory)); + if (fileRelations == null) + throw new ArgumentNullException(nameof(fileRelations)); + + string? result = null; + switch (converterModes) + { + case PathConverterModes.Relative: + result = GetRelativePath(directory, fileRelations); + break; + case PathConverterModes.Complete: + result = GetCompletePath(directory, fileRelations); + break; + default: + break; + } + return result; + } + +#if error_demo + /// + /// 绝对路径->相对路径 + /// + /// 绝对路径 + /// 相对关系 + /// + /// StringHelper.GetRelativePath("G:\\A1.项目\\20190920金山谷黄宅\\01.饰施图\\03.平面图", "G:\\A1.项目\\20190920金山谷黄宅\\01.饰施图\\01.辅助文件\\图框\\A3图框.dwg"); + public static string GetRelativePath(string strDbPath, string strXrefPath) + { + Uri uri1 = new(strXrefPath); + Uri uri2 = new(strDbPath); + Uri relativeUri = uri2.MakeRelativeUri(uri1);//测试例子变成 01.%E8%BE%85%E5%8A%A9%E6%96%87%E4%BB%B6/%E5%9B%BE%E6%A1%86/A3%E5%9B%BE%E6%A1%86.dwg + string str = relativeUri.ToString(); + + //因为这里不会实现".\A.dwg"而是"A.dwg",所以加入这个操作,满足同目录文件 + var strs = str.Split('\\'); + if (strs.Length == 1) + str = ".\\" + str; + return str; + } +#else + /// + /// 绝对路径->相对路径 + /// + /// 相对关系:文件夹路径 + /// 完整路径:文件路径 + /// 相对路径 + /// "..\\01.辅助文件\\图框\\A3图框.dwg" + /// ]]> + static string GetRelativePath(string directory, string file) + { + string[] directorys = directory.Split('\\'); + string[] files = file.Split('\\'); + //获取两条路径中的最短路径 + int getMinLength = directorys.Length < files.Length ? directorys.Length : files.Length; + + //用于确定我们退出的循环中的位置。 + int lastCommonRoot = -1; + int index; + //找到共根 + for (index = 0; index < getMinLength; index++) + { + if (directorys[index] != files[index]) + break; + lastCommonRoot = index; + } + //如果我们没有找到一个共同的前缀,那么抛出 + if (lastCommonRoot == -1) + throw new ArgumentException("路径没有公共相同路径部分"); + + //建立相对路径 + var result = new StringBuilder(); + for (index = lastCommonRoot + 1; index < directorys.Length; index++) + if (directorys[index].Length > 0) + result.Append("..\\");//上级目录加入 + + //添加文件夹 + for (index = lastCommonRoot + 1; index < files.Length - 1; index++) + result.Append(files[index] + "\\"); + + //本级目录 + if (result.Length == 0) + result.Append(".\\"); + //result.Append(strXrefPaths[^1]);//下级目录加入 + result.Append(files[files.Length - 1]);//下级目录加入 + return result.ToString(); + } +#endif + + /// + /// 相对路径->绝对路径 + /// + /// 文件夹路径 + /// 相对关系:有..的 + /// 完整路径 + /// "G:\\A1.项目\\20190920金山谷黄宅\\01.饰施图\\01.辅助文件\\图框\\A3图框.dwg" + /// ]]> + static string? GetCompletePath(string directory, string relativePath) + { + if (relativePath is null || relativePath.Trim() == string.Empty) + return null; + + var relativeName = Path.GetDirectoryName(relativePath); + if (relativeName is null) + return null; + + if (relativePath[0] != '.') + return relativePath; + + const char slash = '\\'; + + //判断向上删除几个 + var path_xiangduis = relativeName.Split(slash); + int index = 0; + for (int i = 0; i < path_xiangduis.Length; i++) + { + if (path_xiangduis[i] != "..") + break; + index++; + } + + var result = new StringBuilder(); + //前段 + var path_dwgs = directory.Split(slash); + path_dwgs = path_dwgs.Where(s => !string.IsNullOrEmpty(s)).ToArray();//清理空数组 + for (int i = 0; i < path_dwgs.Length - index; i++) + { + result.Append(path_dwgs[i]); + result.Append(slash); + } + //后段 + for (int i = 0; i < path_xiangduis.Length; i++) + { + var item = path_xiangduis[i]; + if (item != "." && item != "..") + { + result.Append(item); + result.Append(slash); + } + } + result.Append(Path.GetFileName(relativePath)); + return result.ToString(); + } + #endregion +} +#endregion + + +public static class DBTransEx +{ + /// + /// 清理符号表 + /// + /// + /// + public static void Purge(this DBTrans tr, SymModes sym = SymModes.Purge) + { + var db = tr.Database; + if ((sym & SymModes.BlockTable) == SymModes.BlockTable) + DatabasePurge(db, tr.BlockTable); + if ((sym & SymModes.DimStyleTable) == SymModes.DimStyleTable) + DatabasePurge(db, tr.DimStyleTable); + if ((sym & SymModes.LayerTable) == SymModes.LayerTable) + DatabasePurge(db, tr.LayerTable); + if ((sym & SymModes.LinetypeTable) == SymModes.LinetypeTable) + DatabasePurge(db, tr.LinetypeTable); + if ((sym & SymModes.TextStyleTable) == SymModes.TextStyleTable) + DatabasePurge(db, tr.TextStyleTable); + if ((sym & SymModes.ViewportTable) == SymModes.ViewportTable) + DatabasePurge(db, tr.ViewportTable); + if ((sym & SymModes.RegAppTable) == SymModes.RegAppTable) + DatabasePurge(db, tr.RegAppTable); + + //以下不能这样清理,不然有异常 + if ((sym & SymModes.ViewTable) == SymModes.ViewTable) + DatabasePurge(db, tr.ViewTable); + if ((sym & SymModes.UcsTable) == SymModes.UcsTable) + DatabasePurge(db, tr.UcsTable); + } + + static void DatabasePurge(Database db, + SymbolTable symbolTable) + where TTable : SymbolTable + where TRecord : SymbolTableRecord, new() + { + var idArray = symbolTable.Select(id => id)?.ToArray(); + if (idArray != null && idArray.Length > 0) + { + var ids = new ObjectIdCollection(idArray); + while (ids.Count > 0) + db.Purge(ids); + } + } +} \ No newline at end of file diff --git a/src/IFoxCAD.Cad.Shared/Runtime/DBTrans.cs b/src/IFoxCAD.Cad.Shared/Runtime/DBTrans.cs index db1394f..8599bb4 100644 --- a/src/IFoxCAD.Cad.Shared/Runtime/DBTrans.cs +++ b/src/IFoxCAD.Cad.Shared/Runtime/DBTrans.cs @@ -357,16 +357,10 @@ public void SaveDwgFile(DwgVersion version = DwgVersion.AC1800) break; } } - if (doca == null) - { - // 后台开图,用数据库保存 + if (doca == null) // 后台开图,用数据库保存 Database.SaveAs(Database.Filename, version); - } - else - { - // 前台开图,使用命令保存;不需要切换文档 + else // 前台开图,使用命令保存;不需要切换文档 doca.SendStringToExecute("_qsave\n", false, true, true); - } } #endregion diff --git a/src/IFoxCAD.Cad.Shared/XrefEx.cs b/src/IFoxCAD.Cad.Shared/XrefEx.cs deleted file mode 100644 index 9ac31a0..0000000 --- a/src/IFoxCAD.Cad.Shared/XrefEx.cs +++ /dev/null @@ -1,362 +0,0 @@ -using System.Xml.Linq; - -namespace IFoxCAD.Cad; - - -//测试代码(图纸仅绑定,不做其他处理,如清理,关图层,删除嵌套被卸载的参照等) -public class TestCmd_BindXrefs -{ - //后台绑定 - [CommandMethod("MyXBindXrefs")] - public static void MyXBindXrefs() - { - string newfile = @"D:\桌面\测试文件\三梅中学-教学楼-结构施工图 - 副本.dwg"; - using var tr = new DBTrans(newfile, openMode: FileOpenMode.OpenForReadAndAllShare/*后台绑定需要特别注意*/); - //用于后期删除嵌套被卸载的参照 - Dictionary nested = new(); - XrefEx.XBindXrefs(tr, nested, SymTypes.LayerTable); - tr.SaveDwgFile(); - } - -#if true2 - //后台绑定 - [CommandMethod("MyXBindXrefs")] - public static void MyXBindXrefs() - { - using var tr = new DBTrans(filePath, openMode: FileOpenMode.OpenForReadAndAllShare/*后台绑定注意*/); - //用于后期删除嵌套被卸载的参照 - Dictionary nested = new(); - tr.XBindXrefs(nested); - tr.SaveDwgFile(); - } -#endif - - //前台绑定 - [CommandMethod("MyXBindXrefs1")] - public static void MyXBindXrefs1() - { - using var tr = new DBTrans(); - //用于后期删除嵌套被卸载的参照 - Dictionary nested = new(); - tr.XBindXrefs(nested); - tr.SaveDwgFile(); - } -} - -public enum SymTypes -{ - /// - /// 图层表 - /// - LayerTable = 1, - /// - /// 文字样式表 - /// - TextStyleTable = 2, - /// - /// 标注样式表 - /// - DimStyleTable = 4, - /// - /// 线型表 - /// - LinetypeTable = 8, - /// - /// 注册应用程序表 - /// - RegAppTable = 16, - - All = LayerTable | TextStyleTable | DimStyleTable | LinetypeTable | RegAppTable -} - -public static class XrefEx -{ - /// - /// - /// 双重绑定参照 - /// 该方法仅用于"图层绑定"项,其他项有异常eWasOpenForNotify - /// - /// 参考链接 - /// - /// - /// 嵌套参照名称,id - /// 需要绑定的符号表 - /// 是否拆离参照 - /// 是否删除被卸载的嵌套参照 - public static void XBindXrefs(this DBTrans tr, - Dictionary nested, - SymTypes xNum = SymTypes.LayerTable, - bool detachXref = true, - bool easeNested = true) - { - DoubleBind(tr, nested, detachXref, (xbindXrefsIds) => { - - // 起初测试是将九大符号表记录均加入的,但经实测不行...(为什么?存疑) - if ((xNum & SymTypes.LayerTable) == SymTypes.LayerTable) - tr.LayerTable.ForEach(tabRec => { - AddedxbindXrefsIds(tabRec, xbindXrefsIds); - }, checkIdOk: true); - - if ((xNum & SymTypes.TextStyleTable) == SymTypes.TextStyleTable) - tr.TextStyleTable.ForEach(tabRec => { - AddedxbindXrefsIds(tabRec, xbindXrefsIds); - }, checkIdOk: true); - - if ((xNum & SymTypes.LinetypeTable) == SymTypes.LinetypeTable) - tr.LinetypeTable.ForEach(tabRec => { - AddedxbindXrefsIds(tabRec, xbindXrefsIds); - }, checkIdOk: true); - - if ((xNum & SymTypes.DimStyleTable) == SymTypes.DimStyleTable) - tr.DimStyleTable.ForEach(tabRec => { - AddedxbindXrefsIds(tabRec, xbindXrefsIds); - }, checkIdOk: true); - - if ((xNum & SymTypes.RegAppTable) == SymTypes.RegAppTable) - tr.RegAppTable.ForEach(tabRec => { - AddedxbindXrefsIds(tabRec, xbindXrefsIds); - }, checkIdOk: true); - }); - - // 内部删除嵌套参照的块操作 - if (easeNested) - { - foreach (var item in nested) - { - var name = item.Value; - if (tr.BlockTable.Has(name)) - tr.GetObject(tr.BlockTable[name], OpenMode.ForWrite)? - .Erase(); - } - } - } - - /// - /// 符号表记录加入xbind - /// - /// - /// - /// - static void AddedxbindXrefsIds(TTableRecord tabRec, ObjectIdCollection xbindXrefsIds) - where TTableRecord : SymbolTableRecord - { - if (tabRec.IsResolved) - xbindXrefsIds.Add(tabRec.ObjectId); - } - - static void DoubleBind(this DBTrans tr, - Dictionary nested, - bool detachXref, - Action xbindAction) - { - DatabaseEx.DBTextDeviation(tr.Database, () => { - - //解析外部参照:此功能不能锁定文档 - //useThreadEngine==true,线性引擎,会在cad命令历史打印一些AEC信息,并导致绑定慢一点...具体作用不详 - //doNewOnly==true,仅处理 Unresolved_未融入(未解析)的参照 - tr.Database.ResolveXrefs(useThreadEngine: false, doNewOnly: false); - - //有这个 eWrongObjectType 异常,说明还是 xbindXrefsIds 的集合id有问题 - //xbind才是绑其他符号表,/xbind绑块表也会有异常 - var xbindXrefsIds = new ObjectIdCollection(); - //bind只绑块表 - var bindXrefsIds = new ObjectIdCollection(); - - tr.BlockTable.ForEach(btr => { - if (btr.IsFromExternalReference && btr.IsResolved) - bindXrefsIds.Add(btr.ObjectId); - }, checkIdOk: true); - - // 直接拆离的id - List detachXrefIds = new(); - - // 补充是否被嵌套卸载的处理 - var xg = tr.Database.GetHostDwgXrefGraph(true);//参数:包含僵尸参照 - for (int j = 0; j < xg.NumNodes; j++) - { - var xrNode = xg.GetXrefNode(j); - switch (xrNode.XrefStatus) - { - case XrefStatus.Unresolved://未融入_ResolveXrefs参数2 - break; - case XrefStatus.FileNotFound://未融入(未解析)_未找到文件 - break; - case XrefStatus.Unreferenced://未参照 - detachXrefIds.Add(xrNode.BlockTableRecordId); - break; - case XrefStatus.Unloaded://已卸载 - { - var xrId = xrNode.BlockTableRecordId; - if (!xrId.IsOk()) - break; - var btr = tr.GetObject(xrId); - if (btr != null && btr.IsFromExternalReference) - { - if (!xrNode.IsNested) - detachXrefIds.Add(xrId); - else if (!nested.ContainsKey(xrId)) - nested.Add(xrId, xrNode.Name);//嵌套参照 - } - } - break; - case XrefStatus.Resolved://已融入_就是可以绑定的 - break; - case XrefStatus.NotAnXref://不是外部参照 - break; - default: - break; - } - } - - //拆离未参照的文件 - if (detachXref) - { - for (int i = 0; i < detachXrefIds.Count; i++) - tr.Database.DetachXref(detachXrefIds[i]); - } - - xbindAction?.Invoke(xbindXrefsIds); - - //若有嵌套参照被卸载,重载 - var keys = nested.Keys.ToArray(); - if (keys.Length > 0) - tr.Database.ReloadXrefs(new ObjectIdCollection(keys)); - - // 切勿交换,若交换秩序,则会绑定无效 - if (xbindXrefsIds.Count > 0) - tr.Database.XBindXrefs(xbindXrefsIds, true); - if (bindXrefsIds.Count > 0) - tr.Database.BindXrefs(bindXrefsIds, true); - }); - } - - -#if true2 - /// - /// 清理符号7表(块表,标注样式表,图层表,线型表,文字样式表,视口样式表,注册应用表) - ///注:视图样式表和坐标系表不能这样清理,有异常 - /// - /// 数据库 - /// 无返回值 - public static void Purge(Database db) - { - ObjectIdCollection ids; - ObjectIdCollection ids1; - ObjectIdCollection ids2; - ObjectIdCollection ids3; - ObjectIdCollection ids4; - ObjectIdCollection ids5; - //ObjectIdCollection ids6; - //ObjectIdCollection ids7; - ObjectIdCollection ids8; - using (Transaction tr = db.TransactionManager.StartTransaction()) - { - //块表 - BlockTable blockt = tr.GetObject(db.BlockTableId, OpenMode.ForRead) as BlockTable; - //标注样式表1 - DimStyleTable dimt = tr.GetObject(db.DimStyleTableId, OpenMode.ForRead) as DimStyleTable; - //图层表2 - LayerTable layert = tr.GetObject(db.LayerTableId, OpenMode.ForRead) as LayerTable; - //线型表3 - LinetypeTable linetypet = tr.GetObject(db.LinetypeTableId, OpenMode.ForRead) as LinetypeTable; - //文字样式4 - TextStyleTable textstylet = tr.GetObject(db.TextStyleTableId, OpenMode.ForRead) as TextStyleTable; - //视口样式表5 - ViewportTable viewport = tr.GetObject(db.ViewportTableId, OpenMode.ForRead) as ViewportTable; - //注册应用表8 - RegAppTable regappt = tr.GetObject(db.RegAppTableId, OpenMode.ForRead) as RegAppTable; - - #region 开始清理 - do - { - ids = new ObjectIdCollection(blockt.Cast().ToArray()); - db.Purge(ids); - if (ids != null) - { - foreach (ObjectId id in ids) - { - tr.GetObject(id, OpenMode.ForWrite).Erase(); - } - } - ids1 = new ObjectIdCollection(dimt.Cast().ToArray()); - db.Purge(ids1); - if (ids1 != null) - { - foreach (ObjectId id in ids1) - { - tr.GetObject(id, OpenMode.ForWrite).Erase(); - } - } - ids2 = new ObjectIdCollection(layert.Cast().ToArray()); - db.Purge(ids2); - if (ids2 != null) - { - foreach (ObjectId id in ids2) - { - tr.GetObject(id, OpenMode.ForWrite).Erase(); - } - } - ids3 = new ObjectIdCollection(linetypet.Cast().ToArray()); - db.Purge(ids3); - if (ids3 != null) - { - foreach (ObjectId id in ids3) - { - tr.GetObject(id, OpenMode.ForWrite).Erase(); - } - } - ids4 = new ObjectIdCollection(textstylet.Cast().ToArray()); - db.Purge(ids4); - if (ids4 != null) - { - foreach (ObjectId id in ids4) - { - tr.GetObject(id, OpenMode.ForWrite).Erase(); - } - } - ids5 = new ObjectIdCollection(viewport.Cast().ToArray()); - db.Purge(ids5); - if (ids5 != null) - { - foreach (ObjectId id in ids5) - { - tr.GetObject(id, OpenMode.ForWrite).Erase(); - } - } - #region 经测试视图样式表和坐标系表不能这样清理,不然有异常 - ////视图样式表6 - //ViewTable viewt = tr.GetObject(db.ViewTableId, OpenMode.ForRead) as ViewTable; - //ids6 = new ObjectIdCollection(viewt.Cast().ToArray()); - //db.Purge(ids6); - //foreach (ObjectId id in ids6) - //{ - // ViewTableRecord btr6 = (ViewTableRecord)tr.GetObject(id, OpenMode.ForWrite); - // btr6.Erase(); - //} - ////坐标系表7 - //UcsTable ucst = tr.GetObject(db.UcsTableId, OpenMode.ForRead) as UcsTable; - //ids7 = new ObjectIdCollection(ucst.Cast().ToArray()); - //db.Purge(ids7); - //foreach (ObjectId id in ids7) - //{ - // UcsTableRecord btr7 = (UcsTableRecord)tr.GetObject(id, OpenMode.ForWrite); - // btr7.Erase(); - //} - #endregion - ids8 = new ObjectIdCollection(regappt.Cast().ToArray()); - db.Purge(ids8); - if (ids8 != null) - { - foreach (ObjectId id in ids8) - { - tr.GetObject(id, OpenMode.ForWrite).Erase(); - } - } - } while (ids.Count > 0 || ids1.Count > 0 || ids2.Count > 0 || ids3.Count > 0 || ids4.Count > 0 || ids5.Count > 0 || ids8.Count > 0); - #endregion - - tr.Commit();//提交事务 - } - } -#endif -} \ No newline at end of file -- Gitee From cb342f127736e678d77385f6f39c61c26110df21 Mon Sep 17 00:00:00 2001 From: liuqihong <540762622@qq.com> Date: Sun, 21 Aug 2022 04:13:58 +0800 Subject: [PATCH 416/675] =?UTF-8?q?=E4=BF=AE=E6=94=B9switch=E5=AF=B9?= =?UTF-8?q?=E9=BD=90=E6=96=B9=E5=BC=8F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .editorconfig | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/.editorconfig b/.editorconfig index f0012cd..f76aad0 100644 --- a/.editorconfig +++ b/.editorconfig @@ -53,6 +53,7 @@ dotnet_style_prefer_auto_properties = true dotnet_style_prefer_compound_assignment = true dotnet_style_prefer_conditional_expression_over_assignment = true dotnet_style_prefer_conditional_expression_over_return = true +dotnet_style_prefer_foreach_explicit_cast_in_source = when_strongly_typed dotnet_style_prefer_inferred_anonymous_type_member_names = true dotnet_style_prefer_inferred_tuple_names = true dotnet_style_prefer_is_null_check_over_reference_equality_method = true:error @@ -66,7 +67,7 @@ dotnet_style_readonly_field = true dotnet_code_quality_unused_parameters = all # 禁止显示首选项 -dotnet_remove_unnecessary_suppression_exclusions = none +dotnet_remove_unnecessary_suppression_exclusions = 0 # 新行首选项 dotnet_style_allow_multiple_blank_lines_experimental = true @@ -92,6 +93,7 @@ csharp_style_expression_bodied_properties = true # 模式匹配首选项 csharp_style_pattern_matching_over_as_with_null_check = true csharp_style_pattern_matching_over_is_with_cast_check = true +csharp_style_prefer_extended_property_pattern = true csharp_style_prefer_not_pattern = true csharp_style_prefer_pattern_matching = true csharp_style_prefer_switch_expression = true @@ -106,15 +108,21 @@ csharp_preferred_modifier_order = public,private,protected,internal,static,exter # 代码块首选项 csharp_prefer_braces = true csharp_prefer_simple_using_statement = true +csharp_style_namespace_declarations = block_scoped +csharp_style_prefer_method_group_conversion = true +csharp_style_prefer_top_level_statements = true # 表达式级首选项 csharp_prefer_simple_default_expression = true csharp_style_deconstructed_variable_declaration = true csharp_style_implicit_object_creation_when_type_is_apparent = true csharp_style_inlined_variable_declaration = true -csharp_style_pattern_local_over_anonymous_function = true csharp_style_prefer_index_operator = true +csharp_style_prefer_local_over_anonymous_function = true +csharp_style_prefer_null_check_over_type_check = true csharp_style_prefer_range_operator = true +csharp_style_prefer_tuple_swap = true +csharp_style_prefer_utf8_string_literals = true csharp_style_throw_expression = true csharp_style_unused_value_assignment_preference = discard_variable csharp_style_unused_value_expression_statement_preference = discard_variable @@ -141,8 +149,8 @@ csharp_new_line_between_query_expression_clauses = true # 缩进首选项 csharp_indent_block_contents = true csharp_indent_braces = false -csharp_indent_case_contents = true -csharp_indent_case_contents_when_block = true +csharp_indent_case_contents = false +csharp_indent_case_contents_when_block = false csharp_indent_labels = one_less_than_current csharp_indent_switch_labels = true -- Gitee From fc3fa7558633279e36bd5b941649837e37f52875 Mon Sep 17 00:00:00 2001 From: liuqihong <540762622@qq.com> Date: Sun, 21 Aug 2022 04:14:21 +0800 Subject: [PATCH 417/675] =?UTF-8?q?=E4=BF=AE=E6=94=B9=E5=8F=82=E7=85=A7?= =?UTF-8?q?=E5=B7=A5=E5=8E=82?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../ExtensionMethod/Enums.cs | 116 +++++- .../ExtensionMethod/XrefEx.cs | 382 +++++++----------- .../IFoxCAD.Cad.Shared.projitems | 2 +- src/IFoxCAD.Cad.Shared/Runtime/AssemInfo.cs | 2 + src/IFoxCAD.Cad.Shared/Runtime/SymbolTable.cs | 24 +- 5 files changed, 266 insertions(+), 260 deletions(-) diff --git a/src/IFoxCAD.Cad.Shared/ExtensionMethod/Enums.cs b/src/IFoxCAD.Cad.Shared/ExtensionMethod/Enums.cs index fc0fbaf..e3c0651 100644 --- a/src/IFoxCAD.Cad.Shared/ExtensionMethod/Enums.cs +++ b/src/IFoxCAD.Cad.Shared/ExtensionMethod/Enums.cs @@ -1,5 +1,116 @@ namespace IFoxCAD.Cad; +/// +/// 循环状态 +/// +public enum LoopState +{ + /// + /// 继续执行 + /// + Go, + /// + /// 中断执行 + /// + Break +} + +/// +/// 参照路径转换 +/// +public enum PathConverterModes : byte +{ + /// + /// 相对路径 + /// + Relative, + /// + /// 绝对路径 + /// + Complete +} + +/// +/// 参照绑定 +/// +public enum XrefModes : byte +{ + /// + /// 卸载 + /// + Unload, + /// + /// 重载 + /// + Reload, + /// + /// 拆离 + /// + Detach, + /// + /// 绑定 + /// + Bind, +} + +public enum SymModes : ushort +{ + /// + /// 块表 + /// + BlockTable = 1, + + /// + /// 图层表 + /// + LayerTable = 2, + /// + /// 文字样式表 + /// + TextStyleTable = 4, + /// + /// 注册应用程序表 + /// + RegAppTable = 8, + /// + /// 标注样式表 + /// + DimStyleTable = 16, + /// + /// 线型表 + /// + LinetypeTable = 32, + Option1 = LayerTable | TextStyleTable | DimStyleTable | LinetypeTable | RegAppTable, + + /// + /// 用户坐标系表 + /// + UcsTable = 64, + /// + /// 视图表 + /// + ViewTable = 128, + /// + /// 视口表 + /// + ViewportTable = 256, + Option2 = UcsTable | ViewTable | ViewportTable, + + //全部 + All = BlockTable | Option1 | Option2, + + //清理 + Purge = + BlockTable | + DimStyleTable | + LayerTable | + LinetypeTable | + TextStyleTable | + ViewportTable | + RegAppTable, +} + + /// /// 坐标系类型枚举 /// @@ -96,11 +207,10 @@ public static string GetDesc(this Enum val) var type = val.GetType(); var memberInfo = type.GetMember(val.ToString()); var attributes = memberInfo[0].GetCustomAttributes(typeof(DescriptionAttribute), false); - //如果没有定义描述,就把当前枚举值的对应名称返回 + //如果没有定义描述,就把当前枚举值的对应名称返回 if (attributes is null || attributes.Length != 1) - { return val.ToString(); - } + return ((DescriptionAttribute)attributes.Single()).Description; } } diff --git a/src/IFoxCAD.Cad.Shared/ExtensionMethod/XrefEx.cs b/src/IFoxCAD.Cad.Shared/ExtensionMethod/XrefEx.cs index b61588e..60afc1f 100644 --- a/src/IFoxCAD.Cad.Shared/ExtensionMethod/XrefEx.cs +++ b/src/IFoxCAD.Cad.Shared/ExtensionMethod/XrefEx.cs @@ -3,21 +3,17 @@ namespace IFoxCAD.Cad; -using Autodesk.AutoCAD.DatabaseServices; -using System.Security.Cryptography; -using System.Xml.Linq; - #if test_demo -//测试代码(图纸仅绑定,不做其他处理,如清理,关图层,删除嵌套被卸载的参照等) public class TestCmd_BindXrefs { //后台绑定 [CommandMethod("MyXBindXrefs")] public static void MyXBindXrefs() { - string newfile = @"D:\桌面\测试文件\三梅中学-教学楼-结构施工图 - 副本.dwg"; - using var tr = new DBTrans(newfile, openMode: FileOpenMode.OpenForReadAndAllShare/*后台绑定特别注意*/); - tr.SetXref(XrefBindingModes.Bind); + string newfile = @"D:\桌面\xxx\xxx.dwg"; + using var tr = new DBTrans(newfile, + openMode: FileOpenMode.OpenForReadAndAllShare/*后台绑定特别注意*/); + tr.XrefFactory(XrefModes.Bind); tr.SaveDwgFile(); } @@ -26,150 +22,71 @@ public static void MyXBindXrefs() public static void MyXBindXrefs1() { using var tr = new DBTrans(); - tr.SetXref(XrefBindingModes.Bind); + tr.XrefFactory(XrefModes.Bind); tr.SaveDwgFile(); } } - #endif #region 参照绑定类 -public enum XrefBindingModes : byte -{ - /// - /// 卸载 - /// - Unload, - /// - /// 重载 - /// - Reload, - /// - /// 拆离 - /// - Detach, - /// - /// 绑定 - /// - Bind, -} - -public enum SymModes : ushort -{ - /// - /// 块表 - /// - BlockTable = 1, - - /// - /// 图层表 - /// - LayerTable = 2, - /// - /// 文字样式表 - /// - TextStyleTable = 4, - /// - /// 注册应用程序表 - /// - RegAppTable = 8, - /// - /// 标注样式表 - /// - DimStyleTable = 16, - /// - /// 线型表 - /// - LinetypeTable = 32, - Option1 = LayerTable | TextStyleTable | DimStyleTable | LinetypeTable | RegAppTable, - - /// - /// 用户坐标系表 - /// - UcsTable = 64, - /// - /// 视图表 - /// - ViewTable = 128, - /// - /// 视口表 - /// - ViewportTable = 256, - Option2 = UcsTable | ViewTable | ViewportTable, - - //全部 - All = BlockTable | Option1 | Option2, - - //清理 - Purge = - BlockTable | - DimStyleTable | - LayerTable | - LinetypeTable | - TextStyleTable | - ViewportTable | - RegAppTable, -} - - public static class XrefEx { /// - /// 修改外部参照 + /// 外部参照 /// /// /// 处理参照的枚举 - /// 要处理的参照名称,就处理所有 + /// 要处理的参照名称,就处理所有 /// /// - /// 参数==才触发
    + /// 参数==才触发
    /// 需要绑定的符号表:请保持默认
    /// 目前仅推荐用于
    /// 其他项有异常: eWasOpenForNotify
    ///
    /// - public static void SetXref(this DBTrans tr, - XrefBindingModes xrefModes, - HashSet? names = null, - SymModes xrefModesBind = SymModes.LayerTable) + public static void XrefFactory(this DBTrans tr, + XrefModes xrefModes, + HashSet? xrefNames = null, + SymModes xrefModesBind = SymModes.LayerTable) { DatabaseEx.DBTextDeviation(tr.Database, () => { + switch (xrefModes) { - case XrefBindingModes.Bind: - { - //此功能有绑定出错的问题 - //db.BindXrefs(xrefIds, true); + case XrefModes.Unload: + { + var xrefIds = GetXrefNode(tr.Database, xrefNames); + if (xrefIds.Count > 0) + tr.Database.UnloadXrefs(xrefIds); + } + break; + case XrefModes.Reload: + { + var xrefIds = GetXrefNode(tr.Database, xrefNames); + if (xrefIds.Count > 0) + tr.Database.ReloadXrefs(xrefIds); + } + break; + case XrefModes.Detach: + { + var xrefIds = GetXrefNode(tr.Database, xrefNames); + foreach (ObjectId id in xrefIds) + tr.Database.DetachXref(id); + } + break; + case XrefModes.Bind: + { + //此功能有绑定出错的问题 + //db.BindXrefs(xrefIds, true); - //绑定后会自动拆离 - //此功能修补了上面缺失 - Dictionary nested = new(); - DoubleBindx(tr, nested, xrefModesBind); - } - break; - case XrefBindingModes.Detach: - { - var xrefIds = GetXrefNode(tr.Database, names); - foreach (ObjectId id in xrefIds) - tr.Database.DetachXref(id); - } - break; - case XrefBindingModes.Unload: - { - var xrefIds = GetXrefNode(tr.Database, names); - if (xrefIds.Count > 0) - tr.Database.UnloadXrefs(xrefIds); - } - break; - case XrefBindingModes.Reload: - { - var xrefIds = GetXrefNode(tr.Database, names); - if (xrefIds.Count > 0) - tr.Database.ReloadXrefs(xrefIds); - } - break; + //绑定后会自动拆离 + //此功能修补了上面缺失 + BindXrefsEx(tr, xrefNames, xrefModesBind); + } + break; default: - break; + break; } }); } @@ -185,10 +102,9 @@ static ObjectIdCollection GetXrefNode(Database db, HashSet? names) //储存要处理的参照id var xrefIds = new ObjectIdCollection(); XrefNodeForEach(db, (xNodeName, xNodeId, xNodeStatus, xNodeIsNested) => { - if (names is null) - xrefIds.Add(xNodeId); //每个都加入 - else if (names.Contains(xNodeName)) - xrefIds.Add(xNodeId); //只加入名称相同的 + //为空的时候全部加入 || 有内容时候含有目标 + if (names is null || names.Contains(xNodeName)) + xrefIds.Add(xNodeId); }); return xrefIds; } @@ -225,50 +141,61 @@ static void XrefNodeForEach(Database db, Action /// 双重绑定参照 - /// 参考链接 + /// 参考链接 ///
  • /// - /// 嵌套参照(块表记录id,名称) - /// + /// 要处理的参照名称,就处理所有 + /// + /// + /// 参数==才触发
    + /// 需要绑定的符号表:请保持默认
    + /// 目前仅推荐用于
    + /// 其他项有异常: eWasOpenForNotify
    + ///
    + /// /// 是否拆离参照,默认true:学官方的绑定后自动拆离 /// 是否删除被卸载的嵌套参照,默认true - static void DoubleBindx(DBTrans tr, - Dictionary nested, - SymModes symType = SymModes.LayerTable, + static void BindXrefsEx(DBTrans tr, + HashSet? xrefNames = null, + SymModes xrefModesBind = SymModes.LayerTable, bool detachXref = true, bool eraseNested = true) { - DoubleBindxAction(tr, nested, detachXref, (xbindXrefsIds) => { + //嵌套参照(块表记录id,名称) + Dictionary nested = new(); + DoubleBind(tr, nested, (xbindXrefsIds) => { // 起初测试是将九大符号表记录均加入的,但经实测不行...(为什么?存疑) #region Option1 - if ((symType & SymModes.LayerTable) == SymModes.LayerTable) + if ((xrefModesBind & SymModes.LayerTable) == SymModes.LayerTable) AddedxbindIds(xbindXrefsIds, tr.LayerTable); - if ((symType & SymModes.TextStyleTable) == SymModes.TextStyleTable) + if ((xrefModesBind & SymModes.TextStyleTable) == SymModes.TextStyleTable) AddedxbindIds(xbindXrefsIds, tr.TextStyleTable); - if ((symType & SymModes.RegAppTable) == SymModes.RegAppTable) + if ((xrefModesBind & SymModes.RegAppTable) == SymModes.RegAppTable) AddedxbindIds(xbindXrefsIds, tr.RegAppTable); - if ((symType & SymModes.DimStyleTable) == SymModes.DimStyleTable) + if ((xrefModesBind & SymModes.DimStyleTable) == SymModes.DimStyleTable) AddedxbindIds(xbindXrefsIds, tr.DimStyleTable); - if ((symType & SymModes.LinetypeTable) == SymModes.LinetypeTable) + if ((xrefModesBind & SymModes.LinetypeTable) == SymModes.LinetypeTable) AddedxbindIds(xbindXrefsIds, tr.LinetypeTable); #endregion #region Option2 - if ((symType & SymModes.UcsTable) == SymModes.UcsTable) + if ((xrefModesBind & SymModes.UcsTable) == SymModes.UcsTable) AddedxbindIds(xbindXrefsIds, tr.UcsTable); - if ((symType & SymModes.ViewTable) == SymModes.ViewTable) + if ((xrefModesBind & SymModes.ViewTable) == SymModes.ViewTable) AddedxbindIds(xbindXrefsIds, tr.ViewTable); - if ((symType & SymModes.ViewportTable) == SymModes.ViewportTable) + if ((xrefModesBind & SymModes.ViewportTable) == SymModes.ViewportTable) AddedxbindIds(xbindXrefsIds, tr.ViewportTable); #endregion - }); + + }, xrefNames, detachXref); + // 内部删除嵌套参照的块操作 if (eraseNested) @@ -276,15 +203,15 @@ static void DoubleBindx(DBTrans tr, #if ac2008 //因为Acad08索引器存在会暴露isErase的(桌子底层的原因), //也就是可能获取两个名称一样的,只能用遍历的方式进行 - HashSet names = new(); + HashSet namess = new(); foreach (var item in nested) - names.Add(item.Value); + namess.Add(item.Value); //遍历全图,找到参照名称一样的删除 tr.BlockTable.ForEach(btr => { if (btr.IsLayout) return; - if (names.Contains(btr.Name)) + if (namess.Contains(btr.Name)) { btr.UpgradeOpen(); btr.Erase(); @@ -305,12 +232,12 @@ static void DoubleBindx(DBTrans tr, } /// - /// 符号表记录加入xbind + /// 符号表记录加入容器 /// static void AddedxbindIds(ObjectIdCollection xbindXrefsIds, - SymbolTable symbolTable) - where TTable : SymbolTable - where TRecord : SymbolTableRecord, new() + SymbolTable symbolTable) + where TTable : SymbolTable + where TRecord : SymbolTableRecord, new() { symbolTable.ForEach(tabRec => { if (tabRec.IsResolved) @@ -318,19 +245,24 @@ static void AddedxbindIds(ObjectIdCollection xbindXrefsIds, }, checkIdOk: true); } - static void DoubleBindxAction(this DBTrans tr, - Dictionary nested, - bool detachXref, - Action xbindAction) + /// + /// 双重绑定 + /// + static void DoubleBind(DBTrans tr, + Dictionary nested, + Action xbindAction, + HashSet? xrefNames = null, + bool detachXref = true) { - - //有这个 eWrongObjectType 异常,说明还是 xbindXrefsIds 的集合id有问题 - //xbind才是绑其他符号表,/xbind绑块表也会有异常 + //xbind + //0x01 它是用来绑其他符号表,绑块表会有异常 + //0x02 集合若有问题,就会出现eWrongObjectType var xbindXrefsIds = new ObjectIdCollection(); - //bind只绑块表 + //bind 只绑块表 var bindXrefsIds = new ObjectIdCollection(); tr.BlockTable.ForEach(btr => { + //外部参照 && 已融入 if (btr.IsFromExternalReference && btr.IsResolved) bindXrefsIds.Add(btr.ObjectId); }, checkIdOk: true); @@ -338,18 +270,25 @@ static void DoubleBindxAction(this DBTrans tr, // 直接拆离的id List detachXrefIds = new(); - // 补充是否被嵌套卸载的处理 + //收集要处理的id XrefNodeForEach(tr.Database, (xNodeName, xNodeId, xNodeStatus, xNodeIsNested) => { switch (xNodeStatus) { case XrefStatus.Unresolved://未融入_ResolveXrefs参数2 - break; + break; case XrefStatus.FileNotFound://未融入(未解析)_未找到文件 - break; + break; case XrefStatus.Unreferenced://未参照 - detachXrefIds.Add(xNodeId); - break; + { + //为空的时候全部加入 || 有内容时候含有目标 + if (xrefNames == null || xrefNames.Contains(xNodeName)) + detachXrefIds.Add(xNodeId); + } + break; case XrefStatus.Unloaded://已卸载 + { + //为空的时候全部加入 || 有内容时候含有目标 + if (xrefNames == null || xrefNames.Contains(xNodeName)) { var btr = tr.GetObject(xNodeId); if (btr != null && btr.IsFromExternalReference) @@ -360,17 +299,17 @@ static void DoubleBindxAction(this DBTrans tr, nested.Add(xNodeId, xNodeName);//嵌套参照 } } - break; + } + break; case XrefStatus.Resolved://已融入_就是可以绑定的 - break; + break; case XrefStatus.NotAnXref://不是外部参照 - break; + break; default: - break; + break; } }); - //拆离未参照的文件 if (detachXref) { @@ -380,12 +319,12 @@ static void DoubleBindxAction(this DBTrans tr, xbindAction?.Invoke(xbindXrefsIds); - //若有嵌套参照被卸载,重载 + //嵌套参照被卸载则进行重载,才能进行绑定 var keys = nested.Keys.ToArray(); if (keys.Length > 0) tr.Database.ReloadXrefs(new ObjectIdCollection(keys)); - // 切勿交换,若交换秩序,则会绑定无效 + //切勿交换,若交换秩序,则会绑定无效 if (xbindXrefsIds.Count > 0) tr.Database.XBindXrefs(xbindXrefsIds, true); if (bindXrefsIds.Count > 0) @@ -397,36 +336,23 @@ static void DoubleBindxAction(this DBTrans tr, #region 参照路径工具类 /// -/// 参照路径转换模式 +/// 获取外部参照的路径 /// -public enum PathConverterModes : byte +public class XrefPath { + #region 属性 /// - /// 相对路径 + /// 基础路径 /// - Relative, + readonly string? _directory; /// - /// 绝对路径 + /// 是否外部参照 /// - Complete -} - -/* - * https://blog.csdn.net/my98800/article/details/51450696 - * https://blog.csdn.net/lishuangquan1987/article/details/53678215 - * https://www.cnblogs.com/hont/p/5412340.html - */ -/// -/// 获取外部参照的路径 -/// -public class XrefPath -{ - - #region 属性 + public bool IsFromExternalReference { get; private set; } /// /// 外部参照保存的路径 /// - /// 可能是
    + /// 它们会是以下任一路径:
    /// 0x01 相对路径
    /// 0x02 绝对路径
    /// 0x03 共目录优先找到的路径(文件夹整体移动会发生此类情况) @@ -443,37 +369,15 @@ public class XrefPath /// /// 绝对路径 /// - public string? PathComplete - { - get - { - return _PathComplete ??= - PathConverter(_directory, PathDescribe, PathConverterModes.Complete); - } - } + public string? PathComplete => _PathComplete ??= + PathConverter(_directory, PathDescribe, PathConverterModes.Complete); string? _PathRelative; /// /// 相对路径 /// - public string? PathRelative - { - get - { - return _PathRelative ??= - PathConverter(_directory, PathComplete, PathConverterModes.Relative); - } - } - - /// - /// 是否外部参照 - /// - public bool IsFromExternalReference { get; private set; } - - /// - /// 基础路径 - /// - readonly string? _directory; + public string? PathRelative => _PathRelative ??= + PathConverter(_directory, PathComplete, PathConverterModes.Relative); #endregion #region 构造 @@ -498,15 +402,18 @@ public XrefPath(BlockReference brf, DBTrans tr) if (!IsFromExternalReference) return; - //相对路径==".\\AA.dwg";无路径=="AA.dwg"; + //相对路径==".\\AA.dwg" + //无路径=="AA.dwg" PathSave = btRec.PathName; - - //相对路径||绝对路径: + if ((!string.IsNullOrEmpty(PathSave) && PathSave[0] == '.') || File.Exists(PathSave)) + { + //相对路径||绝对路径 PathDescribe = PathSave; - else + } + else { - //无路径: + //无路径 var db = btRec.GetXrefDatabase(true); PathDescribe = db.Filename; } @@ -516,6 +423,7 @@ public XrefPath(BlockReference brf, DBTrans tr) #region 静态函数 /// /// 获取相对路径或者绝对路径 + /// 参考链接 /// /// 基础目录(末尾无斜杠) /// 相对路径或者绝对路径 @@ -534,13 +442,13 @@ public XrefPath(BlockReference brf, DBTrans tr) switch (converterModes) { case PathConverterModes.Relative: - result = GetRelativePath(directory, fileRelations); - break; + result = GetRelativePath(directory, fileRelations); + break; case PathConverterModes.Complete: - result = GetCompletePath(directory, fileRelations); - break; + result = GetCompletePath(directory, fileRelations); + break; default: - break; + break; } return result; } @@ -552,12 +460,14 @@ public XrefPath(BlockReference brf, DBTrans tr) /// 绝对路径 /// 相对关系 /// - /// StringHelper.GetRelativePath("G:\\A1.项目\\20190920金山谷黄宅\\01.饰施图\\03.平面图", "G:\\A1.项目\\20190920金山谷黄宅\\01.饰施图\\01.辅助文件\\图框\\A3图框.dwg"); + /// StringHelper.GetRelativePath("G:\\A1.项目\\20190920金山谷黄宅\\01.饰施图\\03.平面图", + /// "G:\\A1.项目\\20190920金山谷黄宅\\01.饰施图\\01.辅助文件\\图框\\A3图框.dwg"); public static string GetRelativePath(string strDbPath, string strXrefPath) { Uri uri1 = new(strXrefPath); Uri uri2 = new(strDbPath); - Uri relativeUri = uri2.MakeRelativeUri(uri1);//测试例子变成 01.%E8%BE%85%E5%8A%A9%E6%96%87%E4%BB%B6/%E5%9B%BE%E6%A1%86/A3%E5%9B%BE%E6%A1%86.dwg + Uri relativeUri = uri2.MakeRelativeUri(uri1); + //测试例子变成 01.%E8%BE%85%E5%8A%A9%E6%96%87%E4%BB%B6/%E5%9B%BE%E6%A1%86/A3%E5%9B%BE%E6%A1%86.dwg string str = relativeUri.ToString(); //因为这里不会实现".\A.dwg"而是"A.dwg",所以加入这个操作,满足同目录文件 @@ -574,7 +484,8 @@ public static string GetRelativePath(string strDbPath, string strXrefPath) /// 完整路径:文件路径 /// 相对路径 /// "..\\01.辅助文件\\图框\\A3图框.dwg" /// ]]> static string GetRelativePath(string directory, string file) @@ -624,7 +535,8 @@ static string GetRelativePath(string directory, string file) /// 相对关系:有..的 /// 完整路径 /// "G:\\A1.项目\\20190920金山谷黄宅\\01.饰施图\\01.辅助文件\\图框\\A3图框.dwg" /// ]]> static string? GetCompletePath(string directory, string relativePath) diff --git a/src/IFoxCAD.Cad.Shared/IFoxCAD.Cad.Shared.projitems b/src/IFoxCAD.Cad.Shared/IFoxCAD.Cad.Shared.projitems index 32b1b8d..98110ab 100644 --- a/src/IFoxCAD.Cad.Shared/IFoxCAD.Cad.Shared.projitems +++ b/src/IFoxCAD.Cad.Shared/IFoxCAD.Cad.Shared.projitems @@ -18,7 +18,7 @@ - + diff --git a/src/IFoxCAD.Cad.Shared/Runtime/AssemInfo.cs b/src/IFoxCAD.Cad.Shared/Runtime/AssemInfo.cs index c13cc26..6d916ff 100644 --- a/src/IFoxCAD.Cad.Shared/Runtime/AssemInfo.cs +++ b/src/IFoxCAD.Cad.Shared/Runtime/AssemInfo.cs @@ -75,3 +75,5 @@ public enum AutoRegConfig All = Regedit | ReflectionAttribute | ReflectionInterface, } + + diff --git a/src/IFoxCAD.Cad.Shared/Runtime/SymbolTable.cs b/src/IFoxCAD.Cad.Shared/Runtime/SymbolTable.cs index fc81d58..8d5c973 100644 --- a/src/IFoxCAD.Cad.Shared/Runtime/SymbolTable.cs +++ b/src/IFoxCAD.Cad.Shared/Runtime/SymbolTable.cs @@ -1,23 +1,5 @@ namespace IFoxCAD.Cad; -using Autodesk.AutoCAD.DatabaseServices; -using System.Windows.Documents; - -/// -/// 委托执行状态 -/// -public enum DelegateState -{ - /// - /// 继续执行 - /// - Go, - /// - /// 中断执行 - /// - Break -} - public class SymbolTable : IEnumerable where TTable : SymbolTable where TRecord : SymbolTableRecord, new() @@ -348,11 +330,11 @@ public void ForEach(Action action, /// 要执行的委托 /// 打开模式,默认为只读 /// 检查id是否删除,默认false - public void ForEach(Action action, + public void ForEach(Action action, OpenMode openMode = OpenMode.ForRead, bool checkIdOk = false) { - DelegateState state = DelegateState.Go;/*这种方式比Action改Func更友好*/ + LoopState state = LoopState.Go;/*这种方式比Action改Func更友好*/ foreach (var id in this) { if (checkIdOk && !id.IsOk()) @@ -360,7 +342,7 @@ public void ForEach(Action action, var record = GetRecord(id, openMode); if (record is not null) action(record, state); - if (state == DelegateState.Break) + if (state == LoopState.Break) break; } } -- Gitee From 15a176fafb57abd06c63bc4c0d822cdf86e8d4de Mon Sep 17 00:00:00 2001 From: liuqihong <540762622@qq.com> Date: Sun, 21 Aug 2022 18:07:01 +0800 Subject: [PATCH 418/675] =?UTF-8?q?=E4=BC=98=E5=8C=96=E9=80=BB=E8=BE=91?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../ExtensionMethod/XrefEx.cs | 23 +++++++++---------- 1 file changed, 11 insertions(+), 12 deletions(-) diff --git a/src/IFoxCAD.Cad.Shared/ExtensionMethod/XrefEx.cs b/src/IFoxCAD.Cad.Shared/ExtensionMethod/XrefEx.cs index 60afc1f..e9a9d94 100644 --- a/src/IFoxCAD.Cad.Shared/ExtensionMethod/XrefEx.cs +++ b/src/IFoxCAD.Cad.Shared/ExtensionMethod/XrefEx.cs @@ -320,9 +320,9 @@ static void DoubleBind(DBTrans tr, xbindAction?.Invoke(xbindXrefsIds); //嵌套参照被卸载则进行重载,才能进行绑定 - var keys = nested.Keys.ToArray(); - if (keys.Length > 0) - tr.Database.ReloadXrefs(new ObjectIdCollection(keys)); + var keys = nested.Keys; + if (keys.Count > 0) + tr.Database.ReloadXrefs(new ObjectIdCollection(keys.ToArray())); //切勿交换,若交换秩序,则会绑定无效 if (xbindXrefsIds.Count > 0) @@ -405,13 +405,13 @@ public XrefPath(BlockReference brf, DBTrans tr) //相对路径==".\\AA.dwg" //无路径=="AA.dwg" PathSave = btRec.PathName; - + if ((!string.IsNullOrEmpty(PathSave) && PathSave[0] == '.') || File.Exists(PathSave)) { //相对路径||绝对路径 PathDescribe = PathSave; } - else + else { //无路径 var db = btRec.GetXrefDatabase(true); @@ -627,12 +627,11 @@ static void DatabasePurge(Database db, where TTable : SymbolTable where TRecord : SymbolTableRecord, new() { - var idArray = symbolTable.Select(id => id)?.ToArray(); - if (idArray != null && idArray.Length > 0) - { - var ids = new ObjectIdCollection(idArray); - while (ids.Count > 0) - db.Purge(ids); - } + var idArray = symbolTable.Select(id => id); + if (!idArray.Any()) + return; + var ids = new ObjectIdCollection(idArray.ToArray()); + while (ids.Count > 0) + db.Purge(ids); } } \ No newline at end of file -- Gitee From ae0b3f5c944199e3c4a884a36a2185db4459f543 Mon Sep 17 00:00:00 2001 From: liuqihong <540762622@qq.com> Date: Sun, 21 Aug 2022 18:49:37 +0800 Subject: [PATCH 419/675] =?UTF-8?q?=E6=9A=B4=E9=9C=B2=E5=85=AC=E5=85=B1?= =?UTF-8?q?=E8=B7=AF=E5=BE=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/IFoxCAD.Cad.Shared/ExtensionMethod/XrefEx.cs | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/IFoxCAD.Cad.Shared/ExtensionMethod/XrefEx.cs b/src/IFoxCAD.Cad.Shared/ExtensionMethod/XrefEx.cs index e9a9d94..14e9f2c 100644 --- a/src/IFoxCAD.Cad.Shared/ExtensionMethod/XrefEx.cs +++ b/src/IFoxCAD.Cad.Shared/ExtensionMethod/XrefEx.cs @@ -344,7 +344,7 @@ public class XrefPath /// /// 基础路径 /// - readonly string? _directory; + public string CurrentDatabasePath; /// /// 是否外部参照 /// @@ -370,14 +370,14 @@ public class XrefPath /// 绝对路径 ///
    public string? PathComplete => _PathComplete ??= - PathConverter(_directory, PathDescribe, PathConverterModes.Complete); + PathConverter(CurrentDatabasePath, PathDescribe, PathConverterModes.Complete); string? _PathRelative; /// /// 相对路径 /// public string? PathRelative => _PathRelative ??= - PathConverter(_directory, PathComplete, PathConverterModes.Relative); + PathConverter(CurrentDatabasePath, PathComplete, PathConverterModes.Relative); #endregion #region 构造 @@ -392,12 +392,12 @@ public XrefPath(BlockReference brf, DBTrans tr) if (brf == null) throw new ArgumentNullException(nameof(brf)); + CurrentDatabasePath = Path.GetDirectoryName(tr.Database.Filename); + var btRec = tr.GetObject(brf.BlockTableRecord);//块表记录 if (btRec == null) return; - _directory = Path.GetDirectoryName(tr.Database.Filename); - IsFromExternalReference = btRec.IsFromExternalReference; if (!IsFromExternalReference) return; -- Gitee From 111210109ca8530e924acbf18bdbd04561c80fa3 Mon Sep 17 00:00:00 2001 From: liuqihong <540762622@qq.com> Date: Mon, 22 Aug 2022 10:37:21 +0800 Subject: [PATCH 420/675] =?UTF-8?q?=E5=B0=86=E5=86=85=E9=83=A8=E5=8F=98?= =?UTF-8?q?=E9=87=8F=E6=9A=B4=E9=9C=B2=E6=88=90=E4=B8=BA=E5=AD=97=E6=AE=B5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../ExtensionMethod/XrefEx.cs | 446 +++++++++++------- 1 file changed, 266 insertions(+), 180 deletions(-) diff --git a/src/IFoxCAD.Cad.Shared/ExtensionMethod/XrefEx.cs b/src/IFoxCAD.Cad.Shared/ExtensionMethod/XrefEx.cs index 14e9f2c..5eed317 100644 --- a/src/IFoxCAD.Cad.Shared/ExtensionMethod/XrefEx.cs +++ b/src/IFoxCAD.Cad.Shared/ExtensionMethod/XrefEx.cs @@ -1,5 +1,5 @@ //#define error_demo -//#define test_demo +#define test_demo namespace IFoxCAD.Cad; @@ -28,82 +28,130 @@ public static void MyXBindXrefs1() } #endif -#region 参照绑定类 -public static class XrefEx +#region 参照工厂 +public interface XrefBindModes { /// - /// 外部参照 + /// 卸载 /// - /// - /// 处理参照的枚举 + public void Unload(); + /// + /// 重载 + /// + public void Reload(); + /// + /// 拆离 + /// + public void Detach(); + /// + /// 绑定 + /// + public void Bind(); +} + +public class XrefFactory : XrefBindModes +{ + #region 私有字段 + readonly DBTrans tr; + /// /// 要处理的参照名称,就处理所有 - /// - /// - /// 参数==才触发
    - /// 需要绑定的符号表:请保持默认
    + ///
    + readonly HashSet? xrefNames; + #endregion + + #region 公开字段 + /// + /// 解析外部参照: + /// 线性引擎,会在cad命令历史打印一些AEC信息,并导致绑定慢一点...具体作用不详 + /// + public bool useThreadEngine = false; + + /// + /// 解析外部参照:仅处理 Unresolved_未融入(未解析)的参照 + /// + public bool doNewOnly = false; + + /// + /// 解析外部参照:包含僵尸参照 + /// + public bool includeGhosts = true; + + + + /// + /// bind时候是否拆离参照,默认true:学官方的绑定后自动拆离 + /// + public bool detachXref = true; + + /// + /// bind时候是否删除被卸载的嵌套参照,默认true + /// + public bool eraseNested = true; + + /// + /// bind时候控制绑定的符号表:请保持默认
    /// 目前仅推荐用于
    /// 其他项有异常: eWasOpenForNotify
    - /// - /// - public static void XrefFactory(this DBTrans tr, - XrefModes xrefModes, - HashSet? xrefNames = null, - SymModes xrefModesBind = SymModes.LayerTable) + ///
    + public SymModes xrefModesBind = SymModes.LayerTable; + #endregion + + /// + /// 参照工厂 + /// + /// + /// 要处理的参照名称,就处理所有 + public XrefFactory(DBTrans tr, HashSet? xrefNames = null) { - DatabaseEx.DBTextDeviation(tr.Database, () => { + this.tr = tr; + this.xrefNames = xrefNames; + } - switch (xrefModes) - { - case XrefModes.Unload: - { - var xrefIds = GetXrefNode(tr.Database, xrefNames); - if (xrefIds.Count > 0) - tr.Database.UnloadXrefs(xrefIds); - } - break; - case XrefModes.Reload: - { - var xrefIds = GetXrefNode(tr.Database, xrefNames); - if (xrefIds.Count > 0) - tr.Database.ReloadXrefs(xrefIds); - } - break; - case XrefModes.Detach: - { - var xrefIds = GetXrefNode(tr.Database, xrefNames); - foreach (ObjectId id in xrefIds) - tr.Database.DetachXref(id); - } - break; - case XrefModes.Bind: - { - //此功能有绑定出错的问题 - //db.BindXrefs(xrefIds, true); + public void Bind() + { + //此功能有绑定出错的问题 + //db.BindXrefs(xrefIds, true); - //绑定后会自动拆离 - //此功能修补了上面缺失 - BindXrefsEx(tr, xrefNames, xrefModesBind); - } - break; - default: - break; - } - }); + //绑定后会自动拆离 + //此功能修补了上面缺失 + DoubleBind(); } + public void Detach() + { + var xrefIds = GetXrefNode(); + foreach (ObjectId id in xrefIds) + tr.Database.DetachXref(id); + } + + public void Reload() + { + var xrefIds = GetXrefNode(); + if (xrefIds.Count > 0) + tr.Database.ReloadXrefs(xrefIds); + } + + public void Unload() + { + var xrefIds = GetXrefNode(); + if (xrefIds.Count > 0) + tr.Database.UnloadXrefs(xrefIds); + } + + /// /// 获取参照 /// /// /// 过滤名称 /// - static ObjectIdCollection GetXrefNode(Database db, HashSet? names) + ObjectIdCollection GetXrefNode() { //储存要处理的参照id var xrefIds = new ObjectIdCollection(); - XrefNodeForEach(db, (xNodeName, xNodeId, xNodeStatus, xNodeIsNested) => { + XrefNodeForEach((xNodeName, xNodeId, xNodeStatus, xNodeIsNested) => { //为空的时候全部加入 || 有内容时候含有目标 - if (names is null || names.Contains(xNodeName)) + if (xrefNames is null || xrefNames.Contains(xNodeName)) xrefIds.Add(xNodeId); }); return xrefIds; @@ -112,19 +160,19 @@ static ObjectIdCollection GetXrefNode(Database db, HashSet? names) /// /// 遍历参照 /// - /// /// (参照名,参照块表记录id,参照状态,是否嵌入) - static void XrefNodeForEach(Database db, Action action) + void XrefNodeForEach(Action action) { // btRec.IsFromOverlayReference 是覆盖 // btRec.GetXrefDatabase(true) 外部参照数据库 + if (action == null) + return; + //解析外部参照:此功能不能锁定文档 - //useThreadEngine==true,线性引擎,会在cad命令历史打印一些AEC信息,并导致绑定慢一点...具体作用不详 - //doNewOnly==true,仅处理 Unresolved_未融入(未解析)的参照 - db.ResolveXrefs(useThreadEngine: false, doNewOnly: false); + tr.Database.ResolveXrefs(useThreadEngine, doNewOnly); - var xg = db.GetHostDwgXrefGraph(true);//参数:包含僵尸参照 + var xg = tr.Database.GetHostDwgXrefGraph(includeGhosts); for (int i = 0; i < xg.NumNodes; i++) { var xNode = xg.GetXrefNode(i); @@ -138,99 +186,7 @@ static void XrefNodeForEach(Database db, Action - /// 双重绑定参照 - /// 参考链接 - ///
    - /// - /// 要处理的参照名称,就处理所有 - /// - /// - /// 参数==才触发
    - /// 需要绑定的符号表:请保持默认
    - /// 目前仅推荐用于
    - /// 其他项有异常: eWasOpenForNotify
    - ///
    - /// - /// 是否拆离参照,默认true:学官方的绑定后自动拆离 - /// 是否删除被卸载的嵌套参照,默认true - static void BindXrefsEx(DBTrans tr, - HashSet? xrefNames = null, - SymModes xrefModesBind = SymModes.LayerTable, - bool detachXref = true, - bool eraseNested = true) - { - //嵌套参照(块表记录id,名称) - Dictionary nested = new(); - DoubleBind(tr, nested, (xbindXrefsIds) => { - - // 起初测试是将九大符号表记录均加入的,但经实测不行...(为什么?存疑) - #region Option1 - if ((xrefModesBind & SymModes.LayerTable) == SymModes.LayerTable) - AddedxbindIds(xbindXrefsIds, tr.LayerTable); - - if ((xrefModesBind & SymModes.TextStyleTable) == SymModes.TextStyleTable) - AddedxbindIds(xbindXrefsIds, tr.TextStyleTable); - - if ((xrefModesBind & SymModes.RegAppTable) == SymModes.RegAppTable) - AddedxbindIds(xbindXrefsIds, tr.RegAppTable); - - if ((xrefModesBind & SymModes.DimStyleTable) == SymModes.DimStyleTable) - AddedxbindIds(xbindXrefsIds, tr.DimStyleTable); - - if ((xrefModesBind & SymModes.LinetypeTable) == SymModes.LinetypeTable) - AddedxbindIds(xbindXrefsIds, tr.LinetypeTable); - #endregion - - #region Option2 - if ((xrefModesBind & SymModes.UcsTable) == SymModes.UcsTable) - AddedxbindIds(xbindXrefsIds, tr.UcsTable); - - if ((xrefModesBind & SymModes.ViewTable) == SymModes.ViewTable) - AddedxbindIds(xbindXrefsIds, tr.ViewTable); - - if ((xrefModesBind & SymModes.ViewportTable) == SymModes.ViewportTable) - AddedxbindIds(xbindXrefsIds, tr.ViewportTable); - #endregion - - }, xrefNames, detachXref); - - - // 内部删除嵌套参照的块操作 - if (eraseNested) - { -#if ac2008 - //因为Acad08索引器存在会暴露isErase的(桌子底层的原因), - //也就是可能获取两个名称一样的,只能用遍历的方式进行 - HashSet namess = new(); - foreach (var item in nested) - namess.Add(item.Value); - - //遍历全图,找到参照名称一样的删除 - tr.BlockTable.ForEach(btr => { - if (btr.IsLayout) - return; - if (namess.Contains(btr.Name)) - { - btr.UpgradeOpen(); - btr.Erase(); - btr.DowngradeOpen(); - btr.Dispose(); - } - }, checkIdOk: true); -#else - foreach (var item in nested) - { - var name = item.Value; - if (tr.BlockTable.Has(name)) - tr.GetObject(tr.BlockTable[name], OpenMode.ForWrite)? - .Erase(); - } -#endif - } - } - + /// /// 符号表记录加入容器 /// @@ -245,19 +201,8 @@ static void AddedxbindIds(ObjectIdCollection xbindXrefsIds, }, checkIdOk: true); } - /// - /// 双重绑定 - /// - static void DoubleBind(DBTrans tr, - Dictionary nested, - Action xbindAction, - HashSet? xrefNames = null, - bool detachXref = true) + ObjectIdCollection GetBindIds() { - //xbind - //0x01 它是用来绑其他符号表,绑块表会有异常 - //0x02 集合若有问题,就会出现eWrongObjectType - var xbindXrefsIds = new ObjectIdCollection(); //bind 只绑块表 var bindXrefsIds = new ObjectIdCollection(); @@ -267,11 +212,60 @@ static void DoubleBind(DBTrans tr, bindXrefsIds.Add(btr.ObjectId); }, checkIdOk: true); + return bindXrefsIds; + } + + ObjectIdCollection GetXBindIds() + { + //xbind + //0x01 它是用来绑其他符号表,绑块表会有异常 + //0x02 集合若有问题,就会出现eWrongObjectType + var xbindXrefsIds = new ObjectIdCollection(); + + // 起初测试是将九大符号表记录均加入的,但经实测不行...(为什么?存疑) + #region Option1 + if ((xrefModesBind & SymModes.LayerTable) == SymModes.LayerTable) + AddedxbindIds(xbindXrefsIds, tr.LayerTable); + + if ((xrefModesBind & SymModes.TextStyleTable) == SymModes.TextStyleTable) + AddedxbindIds(xbindXrefsIds, tr.TextStyleTable); + + if ((xrefModesBind & SymModes.RegAppTable) == SymModes.RegAppTable) + AddedxbindIds(xbindXrefsIds, tr.RegAppTable); + + if ((xrefModesBind & SymModes.DimStyleTable) == SymModes.DimStyleTable) + AddedxbindIds(xbindXrefsIds, tr.DimStyleTable); + + if ((xrefModesBind & SymModes.LinetypeTable) == SymModes.LinetypeTable) + AddedxbindIds(xbindXrefsIds, tr.LinetypeTable); + #endregion + + #region Option2 + if ((xrefModesBind & SymModes.UcsTable) == SymModes.UcsTable) + AddedxbindIds(xbindXrefsIds, tr.UcsTable); + + if ((xrefModesBind & SymModes.ViewTable) == SymModes.ViewTable) + AddedxbindIds(xbindXrefsIds, tr.ViewTable); + + if ((xrefModesBind & SymModes.ViewportTable) == SymModes.ViewportTable) + AddedxbindIds(xbindXrefsIds, tr.ViewportTable); + #endregion + + return xbindXrefsIds; + } + + /// + /// 获取可以拆离的ids + /// + /// 返回已卸载中含有嵌套的参照,要重载之后才能绑定 + /// 返回未参照中嵌套的参照,直接拆离 + List GetDetachIds(Dictionary nested) + { // 直接拆离的id List detachXrefIds = new(); - + //收集要处理的id - XrefNodeForEach(tr.Database, (xNodeName, xNodeId, xNodeStatus, xNodeIsNested) => { + XrefNodeForEach((xNodeName, xNodeId, xNodeStatus, xNodeIsNested) => { switch (xNodeStatus) { case XrefStatus.Unresolved://未融入_ResolveXrefs参数2 @@ -309,26 +303,120 @@ static void DoubleBind(DBTrans tr, break; } }); + return detachXrefIds; + } + /// + /// 双重绑定参照 + /// 参考链接 + /// + void DoubleBind() + { + Dictionary nested = new(); + var detachXrefIds = GetDetachIds(nested); //拆离未参照的文件 if (detachXref) { for (int i = 0; i < detachXrefIds.Count; i++) tr.Database.DetachXref(detachXrefIds[i]); } - - xbindAction?.Invoke(xbindXrefsIds); - //嵌套参照被卸载则进行重载,才能进行绑定 var keys = nested.Keys; if (keys.Count > 0) tr.Database.ReloadXrefs(new ObjectIdCollection(keys.ToArray())); - //切勿交换,若交换秩序,则会绑定无效 - if (xbindXrefsIds.Count > 0) - tr.Database.XBindXrefs(xbindXrefsIds, true); - if (bindXrefsIds.Count > 0) - tr.Database.BindXrefs(bindXrefsIds, true); + var bindIds = GetBindIds(); + var xbindIds = GetXBindIds(); + //切勿交换,否则会绑定无效 + if (xbindIds.Count > 0) + tr.Database.XBindXrefs(xbindIds, true); + if (bindIds.Count > 0) + tr.Database.BindXrefs(bindIds, true); + + + // 内部删除嵌套参照的块操作 + if (eraseNested) + { +#if ac2008 + //因为Acad08索引器存在会暴露isErase的(桌子底层的原因), + //也就是可能获取两个名称一样的,只能用遍历的方式进行 + HashSet namess = new(); + foreach (var item in nested) + namess.Add(item.Value); + + //遍历全图,找到参照名称一样的删除 + tr.BlockTable.ForEach(btr => { + if (btr.IsLayout) + return; + if (namess.Contains(btr.Name)) + { + btr.UpgradeOpen(); + btr.Erase(); + btr.DowngradeOpen(); + btr.Dispose(); + } + }, checkIdOk: true); +#else + foreach (var item in nested) + { + var name = item.Value; + if (tr.BlockTable.Has(name)) + tr.GetObject(tr.BlockTable[name], OpenMode.ForWrite)? + .Erase(); + } +#endif + } + } +} + + + +public static class XrefEx +{ + /// + /// 外部参照工厂 + /// + /// + /// 处理参照的枚举 + /// 要处理的参照名称,就处理所有 + /// 处理单行文字偏移 + public static void XrefFactory(this DBTrans tr, + XrefModes xrefModes, + HashSet? xrefNames = null, + bool isDBTextDeviation = true) + { + var xf = new XrefFactory(tr, xrefNames); + if (isDBTextDeviation) + { + DatabaseEx.DBTextDeviation(tr.Database, () => { + SetXrefBind(xrefModes, xf); + }); + } + else + { + SetXrefBind(xrefModes, xf); + } + } + + static void SetXrefBind(XrefModes xrefModes, XrefFactory xf) + { + switch (xrefModes) + { + case XrefModes.Unload: + xf.Unload(); + break; + case XrefModes.Reload: + xf.Reload(); + break; + case XrefModes.Detach: + xf.Detach(); + break; + case XrefModes.Bind: + xf.Bind(); + break; + default: + break; + } } } @@ -429,9 +517,7 @@ public XrefPath(BlockReference brf, DBTrans tr) /// 相对路径或者绝对路径 /// 依照枚举返回对应的字符串 /// - public static string? PathConverter(string? directory, - string? fileRelations, - PathConverterModes converterModes) + public static string? PathConverter(string? directory, string? fileRelations, PathConverterModes converterModes) { if (directory == null) throw new ArgumentNullException(nameof(directory)); -- Gitee From 53bf10077f01b742b67e7721d3cb8be594c0c941 Mon Sep 17 00:00:00 2001 From: liuqihong <540762622@qq.com> Date: Mon, 22 Aug 2022 10:40:31 +0800 Subject: [PATCH 421/675] =?UTF-8?q?=E4=BC=98=E5=8C=96=E6=B3=A8=E9=87=8A?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/IFoxCAD.Cad.Shared/ExtensionMethod/XrefEx.cs | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/src/IFoxCAD.Cad.Shared/ExtensionMethod/XrefEx.cs b/src/IFoxCAD.Cad.Shared/ExtensionMethod/XrefEx.cs index 5eed317..b94831b 100644 --- a/src/IFoxCAD.Cad.Shared/ExtensionMethod/XrefEx.cs +++ b/src/IFoxCAD.Cad.Shared/ExtensionMethod/XrefEx.cs @@ -308,26 +308,27 @@ List GetDetachIds(Dictionary nested) /// /// 双重绑定参照 - /// 参考链接 + /// 参考链接 /// void DoubleBind() { Dictionary nested = new(); var detachXrefIds = GetDetachIds(nested); - //拆离未参照的文件 + + //拆离:未参照的文件 if (detachXref) { for (int i = 0; i < detachXrefIds.Count; i++) tr.Database.DetachXref(detachXrefIds[i]); } - //嵌套参照被卸载则进行重载,才能进行绑定 + //重载:嵌套参照已卸载了,需要重载之后才能进行绑定 var keys = nested.Keys; if (keys.Count > 0) tr.Database.ReloadXrefs(new ObjectIdCollection(keys.ToArray())); + //绑定:切勿交换,否则会绑定无效 var bindIds = GetBindIds(); var xbindIds = GetXBindIds(); - //切勿交换,否则会绑定无效 if (xbindIds.Count > 0) tr.Database.XBindXrefs(xbindIds, true); if (bindIds.Count > 0) -- Gitee From 5882ea327efa49960a77d20bbf97fcf58e7a2a0e Mon Sep 17 00:00:00 2001 From: liuqihong <540762622@qq.com> Date: Tue, 23 Aug 2022 02:47:15 +0800 Subject: [PATCH 422/675] =?UTF-8?q?=E4=BF=AE=E6=94=B9=E6=89=93=E5=BC=80?= =?UTF-8?q?=E6=96=87=E4=BB=B6=E6=96=B9=E5=BC=8F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/IFoxCAD.Cad.Shared/Runtime/DBTrans.cs | 45 +++++++++++++++++------ 1 file changed, 34 insertions(+), 11 deletions(-) diff --git a/src/IFoxCAD.Cad.Shared/Runtime/DBTrans.cs b/src/IFoxCAD.Cad.Shared/Runtime/DBTrans.cs index 8599bb4..47fd4fb 100644 --- a/src/IFoxCAD.Cad.Shared/Runtime/DBTrans.cs +++ b/src/IFoxCAD.Cad.Shared/Runtime/DBTrans.cs @@ -1,6 +1,6 @@ -using System.Windows.Controls; +namespace IFoxCAD.Cad; -namespace IFoxCAD.Cad; +using System.Diagnostics; /// /// 事务栈 @@ -152,16 +152,31 @@ public DBTrans(string fileName, else { #if ac2008 - //此处没有一一对应的关系 - var sf = openMode switch + FileAccess fileAccess = FileAccess.Read; + FileShare fileShare = FileShare.Read; + switch (openMode) { - FileOpenMode.OpenTryForReadShare => FileShare.Read, - FileOpenMode.OpenForReadAndAllShare => FileShare.ReadWrite, - FileOpenMode.OpenForReadAndWriteNoShare => FileShare.None, - FileOpenMode.OpenForReadAndReadShare => FileShare.ReadWrite, - _ => FileShare.ReadWrite, - }; - Database.ReadDwgFile(fileName, sf, true/*控制读入一个与系统编码不相同的文件时的转换操作*/, password); + case FileOpenMode.OpenTryForReadShare://这个是什么状态?? + fileAccess = FileAccess.ReadWrite; + fileShare = FileShare.ReadWrite; + break; + case FileOpenMode.OpenForReadAndAllShare://完美匹配 + fileAccess = FileAccess.ReadWrite; + fileShare = FileShare.ReadWrite; + break; + case FileOpenMode.OpenForReadAndWriteNoShare://完美匹配 + fileAccess = FileAccess.ReadWrite; + fileShare = FileShare.None; + break; + case FileOpenMode.OpenForReadAndReadShare://完美匹配 + fileAccess = FileAccess.Read; + fileShare = FileShare.Read; + break; + default: + break; + } + using FileStream fileStream = new(fileName, FileMode.Open, fileAccess, fileShare); + Database.ReadDwgFile(fileStream.SafeFileHandle.DangerousGetHandle(), true/*控制读入一个与系统编码不相同的文件时的转换操作*/, password); #else Database.ReadDwgFile(fileName, openMode, true/*控制读入一个与系统编码不相同的文件时的转换操作*/, password); #endif @@ -358,7 +373,15 @@ public void SaveDwgFile(DwgVersion version = DwgVersion.AC1800) } } if (doca == null) // 后台开图,用数据库保存 + { + //用了不存在的文件进行后台打开,并且设置保存,这个时候就软处理 + if (string.IsNullOrEmpty(Database.Filename)) + { + Debug.WriteLine("**** 此数据没有保存路径,无法保存!"); + return; + } Database.SaveAs(Database.Filename, version); + } else // 前台开图,使用命令保存;不需要切换文档 doca.SendStringToExecute("_qsave\n", false, true, true); } -- Gitee From b48748ed090d898d315ac7213abc7af538eaf41b Mon Sep 17 00:00:00 2001 From: liuqihong <540762622@qq.com> Date: Tue, 23 Aug 2022 02:48:03 +0800 Subject: [PATCH 423/675] =?UTF-8?q?=E5=8F=91=E7=8E=B0=E7=BB=91=E5=AE=9A?= =?UTF-8?q?=E9=94=99=E8=AF=AF=E7=9A=84=E4=B8=80=E7=A7=8D?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../ExtensionMethod/XrefEx.cs | 201 +++++++++++------- 1 file changed, 119 insertions(+), 82 deletions(-) diff --git a/src/IFoxCAD.Cad.Shared/ExtensionMethod/XrefEx.cs b/src/IFoxCAD.Cad.Shared/ExtensionMethod/XrefEx.cs index b94831b..995b00b 100644 --- a/src/IFoxCAD.Cad.Shared/ExtensionMethod/XrefEx.cs +++ b/src/IFoxCAD.Cad.Shared/ExtensionMethod/XrefEx.cs @@ -1,16 +1,18 @@ //#define error_demo #define test_demo +using System.Diagnostics; + namespace IFoxCAD.Cad; #if test_demo public class TestCmd_BindXrefs { //后台绑定 - [CommandMethod("MyXBindXrefs")] - public static void MyXBindXrefs() + [CommandMethod("Test_Bind1")] + public static void Test_Bind1() { - string newfile = @"D:\桌面\xxx\xxx.dwg"; + string newfile = @"D:\Test.dwg"; using var tr = new DBTrans(newfile, openMode: FileOpenMode.OpenForReadAndAllShare/*后台绑定特别注意*/); tr.XrefFactory(XrefModes.Bind); @@ -18,18 +20,42 @@ public static void MyXBindXrefs() } //前台绑定 - [CommandMethod("MyXBindXrefs1")] - public static void MyXBindXrefs1() + [CommandMethod("Test_Bind2")] + public static void Test_Bind2() { using var tr = new DBTrans(); tr.XrefFactory(XrefModes.Bind); tr.SaveDwgFile(); } + + [CommandMethod("Test_Bind3")] + public static void Test_Bind3() + { + using var tr = new DBTrans(); + var xf = new XrefFactory(tr) + { + UseThreadEngine = false + }; + DatabaseEx.DBTextDeviation(tr.Database, xf.Bind); + tr.SaveDwgFile(); + } + + [CommandMethod("Test_Bind4")] + public static void Test_Bind4() + { + using var tr = new DBTrans(); + var xf = new XrefFactory(tr) + { + UseThreadEngine = true + }; + DatabaseEx.DBTextDeviation(tr.Database, xf.Bind); + tr.SaveDwgFile(); + } } #endif #region 参照工厂 -public interface XrefBindModes +public interface IXrefBindModes { /// /// 卸载 @@ -49,51 +75,55 @@ public interface XrefBindModes public void Bind(); } -public class XrefFactory : XrefBindModes +public class XrefFactory : IXrefBindModes { #region 私有字段 - readonly DBTrans tr; + readonly DBTrans _tr; /// /// 要处理的参照名称,就处理所有 /// - readonly HashSet? xrefNames; + readonly HashSet? _xrefNames; #endregion #region 公开字段 /// - /// 解析外部参照: - /// 线性引擎,会在cad命令历史打印一些AEC信息,并导致绑定慢一点...具体作用不详 + /// 解析外部参照:线性引擎
    + /// 默认为true:会在cad命令历史打印一些AEC信息,并导致绑定慢一点...具体作用不详
    + /// 为false会出现bind出现异常:
    ///
    - public bool useThreadEngine = false; - + public bool UseThreadEngine = true; /// /// 解析外部参照:仅处理 Unresolved_未融入(未解析)的参照 /// - public bool doNewOnly = false; - + public bool DoNewOnly = false; /// /// 解析外部参照:包含僵尸参照 /// - public bool includeGhosts = true; - + public bool IncludeGhosts = true; /// - /// bind时候是否拆离参照,默认true:学官方的绑定后自动拆离 + /// bind时候是否为插入模式
    + /// 时候不出现双美元符号 ///
    - public bool detachXref = true; - + public bool InsertBind = true; /// - /// bind时候是否删除被卸载的嵌套参照,默认true + /// bind时候是否拆离参照
    + /// 默认:学官方的绑定后自动拆离 ///
    - public bool eraseNested = true; - + public bool DetachFlag = true; + /// + /// bind时候是否删除被卸载的嵌套参照
    + /// 默认 + ///
    + public bool EraseNested = true; /// /// bind时候控制绑定的符号表:请保持默认
    /// 目前仅推荐用于
    - /// 其他项有异常: eWasOpenForNotify
    + /// 其他项有异常:
    ///
    - public SymModes xrefModesBind = SymModes.LayerTable; + public SymModes SymModesBind = SymModes.LayerTable; + #endregion /// @@ -103,8 +133,8 @@ public class XrefFactory : XrefBindModes /// 要处理的参照名称,就处理所有 public XrefFactory(DBTrans tr, HashSet? xrefNames = null) { - this.tr = tr; - this.xrefNames = xrefNames; + this._tr = tr; + this._xrefNames = xrefNames; } public void Bind() @@ -119,39 +149,37 @@ public void Bind() public void Detach() { - var xrefIds = GetXrefNode(); + var xrefIds = GetAllXrefNode(); foreach (ObjectId id in xrefIds) - tr.Database.DetachXref(id); + _tr.Database.DetachXref(id); } public void Reload() { - var xrefIds = GetXrefNode(); + var xrefIds = GetAllXrefNode(); if (xrefIds.Count > 0) - tr.Database.ReloadXrefs(xrefIds); + _tr.Database.ReloadXrefs(xrefIds); } public void Unload() { - var xrefIds = GetXrefNode(); + var xrefIds = GetAllXrefNode(); if (xrefIds.Count > 0) - tr.Database.UnloadXrefs(xrefIds); + _tr.Database.UnloadXrefs(xrefIds); } /// /// 获取参照 /// - /// - /// 过滤名称 - /// - ObjectIdCollection GetXrefNode() + /// 全部参照id + ObjectIdCollection GetAllXrefNode() { //储存要处理的参照id var xrefIds = new ObjectIdCollection(); XrefNodeForEach((xNodeName, xNodeId, xNodeStatus, xNodeIsNested) => { //为空的时候全部加入 || 有内容时候含有目标 - if (xrefNames is null || xrefNames.Contains(xNodeName)) + if (_xrefNames is null || _xrefNames.Contains(xNodeName)) xrefIds.Add(xNodeId); }); return xrefIds; @@ -170,9 +198,9 @@ void XrefNodeForEach(Action action) return; //解析外部参照:此功能不能锁定文档 - tr.Database.ResolveXrefs(useThreadEngine, doNewOnly); + _tr.Database.ResolveXrefs(UseThreadEngine, DoNewOnly); - var xg = tr.Database.GetHostDwgXrefGraph(includeGhosts); + var xg = _tr.Database.GetHostDwgXrefGraph(IncludeGhosts); for (int i = 0; i < xg.NumNodes; i++) { var xNode = xg.GetXrefNode(i); @@ -186,7 +214,7 @@ void XrefNodeForEach(Action action) } } - + /// /// 符号表记录加入容器 /// @@ -204,15 +232,23 @@ static void AddedxbindIds(ObjectIdCollection xbindXrefsIds, ObjectIdCollection GetBindIds() { //bind 只绑块表 - var bindXrefsIds = new ObjectIdCollection(); + var bindIds = new ObjectIdCollection(); + + _tr.BlockTable.ForEach(btr => { + if (btr.IsLayout) + return; + +#if DEBUG + if (btr.IsFromExternalReference) + Debug.WriteLine(btr.ObjectId); +#endif - tr.BlockTable.ForEach(btr => { //外部参照 && 已融入 if (btr.IsFromExternalReference && btr.IsResolved) - bindXrefsIds.Add(btr.ObjectId); + bindIds.Add(btr.ObjectId); }, checkIdOk: true); - return bindXrefsIds; + return bindIds; } ObjectIdCollection GetXBindIds() @@ -220,38 +256,38 @@ ObjectIdCollection GetXBindIds() //xbind //0x01 它是用来绑其他符号表,绑块表会有异常 //0x02 集合若有问题,就会出现eWrongObjectType - var xbindXrefsIds = new ObjectIdCollection(); + var xbindIds = new ObjectIdCollection(); // 起初测试是将九大符号表记录均加入的,但经实测不行...(为什么?存疑) #region Option1 - if ((xrefModesBind & SymModes.LayerTable) == SymModes.LayerTable) - AddedxbindIds(xbindXrefsIds, tr.LayerTable); + if ((SymModesBind & SymModes.LayerTable) == SymModes.LayerTable) + AddedxbindIds(xbindIds, _tr.LayerTable); - if ((xrefModesBind & SymModes.TextStyleTable) == SymModes.TextStyleTable) - AddedxbindIds(xbindXrefsIds, tr.TextStyleTable); + if ((SymModesBind & SymModes.TextStyleTable) == SymModes.TextStyleTable) + AddedxbindIds(xbindIds, _tr.TextStyleTable); - if ((xrefModesBind & SymModes.RegAppTable) == SymModes.RegAppTable) - AddedxbindIds(xbindXrefsIds, tr.RegAppTable); + if ((SymModesBind & SymModes.RegAppTable) == SymModes.RegAppTable) + AddedxbindIds(xbindIds, _tr.RegAppTable); - if ((xrefModesBind & SymModes.DimStyleTable) == SymModes.DimStyleTable) - AddedxbindIds(xbindXrefsIds, tr.DimStyleTable); + if ((SymModesBind & SymModes.DimStyleTable) == SymModes.DimStyleTable) + AddedxbindIds(xbindIds, _tr.DimStyleTable); - if ((xrefModesBind & SymModes.LinetypeTable) == SymModes.LinetypeTable) - AddedxbindIds(xbindXrefsIds, tr.LinetypeTable); + if ((SymModesBind & SymModes.LinetypeTable) == SymModes.LinetypeTable) + AddedxbindIds(xbindIds, _tr.LinetypeTable); #endregion #region Option2 - if ((xrefModesBind & SymModes.UcsTable) == SymModes.UcsTable) - AddedxbindIds(xbindXrefsIds, tr.UcsTable); + if ((SymModesBind & SymModes.UcsTable) == SymModes.UcsTable) + AddedxbindIds(xbindIds, _tr.UcsTable); - if ((xrefModesBind & SymModes.ViewTable) == SymModes.ViewTable) - AddedxbindIds(xbindXrefsIds, tr.ViewTable); + if ((SymModesBind & SymModes.ViewTable) == SymModes.ViewTable) + AddedxbindIds(xbindIds, _tr.ViewTable); - if ((xrefModesBind & SymModes.ViewportTable) == SymModes.ViewportTable) - AddedxbindIds(xbindXrefsIds, tr.ViewportTable); + if ((SymModesBind & SymModes.ViewportTable) == SymModes.ViewportTable) + AddedxbindIds(xbindIds, _tr.ViewportTable); #endregion - return xbindXrefsIds; + return xbindIds; } /// @@ -262,8 +298,8 @@ ObjectIdCollection GetXBindIds() List GetDetachIds(Dictionary nested) { // 直接拆离的id - List detachXrefIds = new(); - + List detachIds = new(); + //收集要处理的id XrefNodeForEach((xNodeName, xNodeId, xNodeStatus, xNodeIsNested) => { switch (xNodeStatus) @@ -275,20 +311,20 @@ List GetDetachIds(Dictionary nested) case XrefStatus.Unreferenced://未参照 { //为空的时候全部加入 || 有内容时候含有目标 - if (xrefNames == null || xrefNames.Contains(xNodeName)) - detachXrefIds.Add(xNodeId); + if (_xrefNames == null || _xrefNames.Contains(xNodeName)) + detachIds.Add(xNodeId); } break; case XrefStatus.Unloaded://已卸载 { //为空的时候全部加入 || 有内容时候含有目标 - if (xrefNames == null || xrefNames.Contains(xNodeName)) + if (_xrefNames == null || _xrefNames.Contains(xNodeName)) { - var btr = tr.GetObject(xNodeId); + var btr = _tr.GetObject(xNodeId); if (btr != null && btr.IsFromExternalReference) { if (!xNodeIsNested) - detachXrefIds.Add(xNodeId); + detachIds.Add(xNodeId); else if (!nested.ContainsKey(xNodeId)) nested.Add(xNodeId, xNodeName);//嵌套参照 } @@ -303,9 +339,10 @@ List GetDetachIds(Dictionary nested) break; } }); - return detachXrefIds; + return detachIds; } + /// /// 双重绑定参照 /// 参考链接 @@ -313,30 +350,30 @@ List GetDetachIds(Dictionary nested) void DoubleBind() { Dictionary nested = new(); - var detachXrefIds = GetDetachIds(nested); + var detachIds = GetDetachIds(nested); //拆离:未参照的文件 - if (detachXref) + if (DetachFlag) { - for (int i = 0; i < detachXrefIds.Count; i++) - tr.Database.DetachXref(detachXrefIds[i]); + for (int i = 0; i < detachIds.Count; i++) + _tr.Database.DetachXref(detachIds[i]); } //重载:嵌套参照已卸载了,需要重载之后才能进行绑定 var keys = nested.Keys; if (keys.Count > 0) - tr.Database.ReloadXrefs(new ObjectIdCollection(keys.ToArray())); + _tr.Database.ReloadXrefs(new ObjectIdCollection(keys.ToArray())); //绑定:切勿交换,否则会绑定无效 var bindIds = GetBindIds(); var xbindIds = GetXBindIds(); if (xbindIds.Count > 0) - tr.Database.XBindXrefs(xbindIds, true); + _tr.Database.XBindXrefs(xbindIds, InsertBind); if (bindIds.Count > 0) - tr.Database.BindXrefs(bindIds, true); + _tr.Database.BindXrefs(bindIds, InsertBind); // 内部删除嵌套参照的块操作 - if (eraseNested) + if (EraseNested) { #if ac2008 //因为Acad08索引器存在会暴露isErase的(桌子底层的原因), @@ -346,7 +383,7 @@ void DoubleBind() namess.Add(item.Value); //遍历全图,找到参照名称一样的删除 - tr.BlockTable.ForEach(btr => { + _tr.BlockTable.ForEach(btr => { if (btr.IsLayout) return; if (namess.Contains(btr.Name)) @@ -361,8 +398,8 @@ void DoubleBind() foreach (var item in nested) { var name = item.Value; - if (tr.BlockTable.Has(name)) - tr.GetObject(tr.BlockTable[name], OpenMode.ForWrite)? + if (_tr.BlockTable.Has(name)) + _tr.GetObject(_tr.BlockTable[name], OpenMode.ForWrite)? .Erase(); } #endif -- Gitee From fb395738cca80eb546e0085cd241c7a1d7353e75 Mon Sep 17 00:00:00 2001 From: liuqihong <540762622@qq.com> Date: Tue, 23 Aug 2022 02:48:46 +0800 Subject: [PATCH 424/675] =?UTF-8?q?=E5=A2=9E=E5=8A=A0=E4=B8=80=E4=B8=AA?= =?UTF-8?q?=E6=9E=9A=E4=B8=BE=E6=B3=A8=E9=87=8A=E7=B1=BB=E7=9A=84=E8=AF=BB?= =?UTF-8?q?=E5=86=99=E6=96=B9=E6=B3=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/IFoxCAD.Basal/EnumEx.cs | 55 +++++++++++++++++++++++++++++++++++++ 1 file changed, 55 insertions(+) create mode 100644 src/IFoxCAD.Basal/EnumEx.cs diff --git a/src/IFoxCAD.Basal/EnumEx.cs b/src/IFoxCAD.Basal/EnumEx.cs new file mode 100644 index 0000000..7827ac8 --- /dev/null +++ b/src/IFoxCAD.Basal/EnumEx.cs @@ -0,0 +1,55 @@ +namespace IFoxCAD.Basal; + +using System.ComponentModel; + +public static class EnumEx +{ + //缓存 + static readonly Dictionary _enumDict = new(); + + /// + /// 打印枚举的特性注释内容 + /// + /// 枚举 + /// 注释内容 + public static string? PrintDescription(this Enum e) + { + if (_enumDict.ContainsKey(e)) + return _enumDict[e]; + + var eType = e.GetType(); + var fieldInfo = eType.GetField(Enum.GetName(eType, e)); + if (fieldInfo == null) + return null; + + var attribute = Attribute.GetCustomAttribute(fieldInfo, + typeof(DescriptionAttribute)) as DescriptionAttribute; + if (attribute != null) + { + _enumDict.Add(e, attribute.Description); + return attribute.Description; + } + + //如果就是空的,就尝试去遍历所有的字段进行组合 + List enums = new(); + foreach (Enum item in Enum.GetValues(eType)) //遍历这个枚举类型 + { + if ((e.GetHashCode() & item.GetHashCode()) == item.GetHashCode() && + e.GetHashCode() != item.GetHashCode()) + enums.Add(item); + } + + string? result = null; + if (enums.Count > 0) + { + List eNames = new(); + for (int i = 0; i < enums.Count; i++) + if (_enumDict.ContainsKey(enums[i])) + eNames.Add(_enumDict[enums[i]]!); + + result = string.Join("|", eNames.ToArray()); + } + _enumDict.Add(e, result); + return _enumDict[e]; + } +} \ No newline at end of file -- Gitee From affa900a3565dd9e15ce1bbcaa8e2a4e3192667f Mon Sep 17 00:00:00 2001 From: liuqihong <540762622@qq.com> Date: Tue, 23 Aug 2022 03:27:59 +0800 Subject: [PATCH 425/675] =?UTF-8?q?=E6=89=93=E5=8D=B0=E6=9E=9A=E4=B8=BE?= =?UTF-8?q?=E6=B3=A8=E9=87=8A=E5=A2=9E=E5=8A=A0=E6=B5=8B=E8=AF=95=E4=BE=8B?= =?UTF-8?q?=E5=AD=90?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/IFoxCAD.Basal/EnumEx.cs | 71 +++++++++++++++++++++++-------- tests/TestConsole/GlobalUsings.cs | 14 ++++++ tests/TestConsole/Info.cs | 19 +++++++++ tests/TestConsole/Program.cs | 10 +++++ 4 files changed, 96 insertions(+), 18 deletions(-) create mode 100644 tests/TestConsole/GlobalUsings.cs create mode 100644 tests/TestConsole/Info.cs diff --git a/src/IFoxCAD.Basal/EnumEx.cs b/src/IFoxCAD.Basal/EnumEx.cs index 7827ac8..4a829e3 100644 --- a/src/IFoxCAD.Basal/EnumEx.cs +++ b/src/IFoxCAD.Basal/EnumEx.cs @@ -1,33 +1,48 @@ namespace IFoxCAD.Basal; using System.ComponentModel; +using System.Linq; public static class EnumEx { + /// + /// 打印枚举的特性注释内容 + /// + public static string? PrintNote(this Enum e, bool noDescrToString = true) + { + var hash = GetAttribute(e, noDescrToString); + if (hash != null) + return string.Join("|", hash.ToArray()); + return null; + } + //缓存 - static readonly Dictionary _enumDict = new(); + static readonly Dictionary> _enumDict = new(); /// /// 打印枚举的特性注释内容 /// /// 枚举 /// 注释内容 - public static string? PrintDescription(this Enum e) + public static HashSet? GetAttribute(this Enum e, bool noDescrToString = true) + where T : DescriptionAttribute { - if (_enumDict.ContainsKey(e)) - return _enumDict[e]; - var eType = e.GetType(); + string eFullName = GetDisplayName(e); + + if (_enumDict.ContainsKey(eFullName)) + return _enumDict[eFullName]; + var fieldInfo = eType.GetField(Enum.GetName(eType, e)); if (fieldInfo == null) - return null; + return null!; - var attribute = Attribute.GetCustomAttribute(fieldInfo, - typeof(DescriptionAttribute)) as DescriptionAttribute; + var attribute = Attribute.GetCustomAttribute(fieldInfo, typeof(T)) as T; if (attribute != null) { - _enumDict.Add(e, attribute.Description); - return attribute.Description; + var res = new HashSet() { attribute.Description }; + _enumDict.Add(eFullName, res); + return res; } //如果就是空的,就尝试去遍历所有的字段进行组合 @@ -39,17 +54,37 @@ public static class EnumEx enums.Add(item); } - string? result = null; + HashSet eNames = new(); if (enums.Count > 0) { - List eNames = new(); for (int i = 0; i < enums.Count; i++) - if (_enumDict.ContainsKey(enums[i])) - eNames.Add(_enumDict[enums[i]]!); - - result = string.Join("|", eNames.ToArray()); + { + var key = enums[i]; + var tname = GetDisplayName(key); + if (_enumDict.ContainsKey(tname)) + { + foreach (var item in _enumDict[tname]) + eNames.Add(item); + } + else + { + var get = GetAttribute(key, noDescrToString); + if (get != null) + foreach (var item in get) //没有缓存就递归 + eNames.Add(item); + } + } } - _enumDict.Add(e, result); - return _enumDict[e]; + + if (eNames.Count == 0 && noDescrToString) + eNames.Add(e.ToString()); + + _enumDict.Add(eFullName, eNames); + return eNames; + } + + static string GetDisplayName(Enum e) + { + return e.GetType().FullName + "." + e.ToString(); } } \ No newline at end of file diff --git a/tests/TestConsole/GlobalUsings.cs b/tests/TestConsole/GlobalUsings.cs new file mode 100644 index 0000000..6328a67 --- /dev/null +++ b/tests/TestConsole/GlobalUsings.cs @@ -0,0 +1,14 @@ +/// 系统引用 +global using System; +global using System.Collections; +global using System.Collections.Generic; +global using System.IO; +global using System.Linq; +global using System.Text; +global using System.Reflection; +global using System.Text.RegularExpressions; +global using Microsoft.Win32; +global using System.ComponentModel; +global using System.Runtime.CompilerServices; + +global using IFoxCAD.Basal; \ No newline at end of file diff --git a/tests/TestConsole/Info.cs b/tests/TestConsole/Info.cs new file mode 100644 index 0000000..60bbe5b --- /dev/null +++ b/tests/TestConsole/Info.cs @@ -0,0 +1,19 @@ +namespace TestConsole; + +[Flags] +public enum PlugIn +{ + [Description("惊惊")] + JoinBox = 1, + [Description("源泉")] + YuanQuan = 2, + [Description("迷你")] + IMinCad = 4, + + Lisp = JoinBox | YuanQuan | IMinCad, + + DOCBAR = 8, + DUOTAB = 16, + + All = Lisp | DOCBAR | DUOTAB +} diff --git a/tests/TestConsole/Program.cs b/tests/TestConsole/Program.cs index 99a5e5f..b690334 100644 --- a/tests/TestConsole/Program.cs +++ b/tests/TestConsole/Program.cs @@ -1,7 +1,17 @@ // See https://aka.ms/new-console-template for more information using System; using System.Text; +using TestConsole; +Console.WriteLine(PlugIn.JoinBox.PrintNote()); +Console.WriteLine(PlugIn.Lisp.PrintNote());//这里先交换顺序来试试能不能成功 +Console.WriteLine(PlugIn.IMinCad.PrintNote()); +Console.WriteLine(PlugIn.YuanQuan.PrintNote()); +Console.WriteLine(PlugIn.All.PrintNote()); +Console.WriteLine(PlugIn.DOCBAR.PrintNote()); +Console.WriteLine(PlugIn.DUOTAB.PrintNote()); + +Console.WriteLine("测试工程"); //表达式树例子 TestConsole.Test_Expression.Demo3(); -- Gitee From 40df286bf07b087b7496b7964ed7de8f5b9fc894 Mon Sep 17 00:00:00 2001 From: liuqihong <540762622@qq.com> Date: Tue, 23 Aug 2022 04:03:02 +0800 Subject: [PATCH 426/675] =?UTF-8?q?=E4=B8=A4=E4=B8=AA=E7=9B=B8=E5=90=8C?= =?UTF-8?q?=E5=8A=9F=E8=83=BD=E6=94=BE=E4=B8=80=E8=B5=B7?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/IFoxCAD.Basal/EnumEx.cs | 17 +++++++++++++++- .../ExtensionMethod/Enums.cs | 20 +------------------ 2 files changed, 17 insertions(+), 20 deletions(-) diff --git a/src/IFoxCAD.Basal/EnumEx.cs b/src/IFoxCAD.Basal/EnumEx.cs index 4a829e3..c216606 100644 --- a/src/IFoxCAD.Basal/EnumEx.cs +++ b/src/IFoxCAD.Basal/EnumEx.cs @@ -87,4 +87,19 @@ static string GetDisplayName(Enum e) { return e.GetType().FullName + "." + e.ToString(); } -} \ No newline at end of file + + + //这个完全被上面替代了 + public static string GetDesc(this Enum val) + { + var type = val.GetType(); + var memberInfo = type.GetMember(val.ToString()); + var attributes = memberInfo[0].GetCustomAttributes(typeof(DescriptionAttribute), false); + //如果没有定义描述,就把当前枚举值的对应名称返回 + if (attributes is null || attributes.Length != 1) + return val.ToString(); + + return ((DescriptionAttribute)attributes.Single()).Description; + } +} + diff --git a/src/IFoxCAD.Cad.Shared/ExtensionMethod/Enums.cs b/src/IFoxCAD.Cad.Shared/ExtensionMethod/Enums.cs index e3c0651..3401c1c 100644 --- a/src/IFoxCAD.Cad.Shared/ExtensionMethod/Enums.cs +++ b/src/IFoxCAD.Cad.Shared/ExtensionMethod/Enums.cs @@ -196,22 +196,4 @@ public enum FontTTF Arial, [Description("Romans")] Romans -} - - - -public static class EnumHelper -{ - public static string GetDesc(this Enum val) - { - var type = val.GetType(); - var memberInfo = type.GetMember(val.ToString()); - var attributes = memberInfo[0].GetCustomAttributes(typeof(DescriptionAttribute), false); - //如果没有定义描述,就把当前枚举值的对应名称返回 - if (attributes is null || attributes.Length != 1) - return val.ToString(); - - return ((DescriptionAttribute)attributes.Single()).Description; - } -} - +} \ No newline at end of file -- Gitee From 703635fc28647ca82b4ef750018af19cf4d2dae9 Mon Sep 17 00:00:00 2001 From: liuqihong <540762622@qq.com> Date: Tue, 23 Aug 2022 04:36:21 +0800 Subject: [PATCH 427/675] =?UTF-8?q?=E4=BC=98=E5=8C=96=E4=BB=A3=E7=A0=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/IFoxCAD.Basal/EnumEx.cs | 93 +++++++++++++++++-------------------- 1 file changed, 42 insertions(+), 51 deletions(-) diff --git a/src/IFoxCAD.Basal/EnumEx.cs b/src/IFoxCAD.Basal/EnumEx.cs index c216606..4c8bebf 100644 --- a/src/IFoxCAD.Basal/EnumEx.cs +++ b/src/IFoxCAD.Basal/EnumEx.cs @@ -6,18 +6,15 @@ public static class EnumEx { /// - /// 打印枚举的特性注释内容 + /// 清理缓存 /// - public static string? PrintNote(this Enum e, bool noDescrToString = true) + public static void CleanCache() { - var hash = GetAttribute(e, noDescrToString); - if (hash != null) - return string.Join("|", hash.ToArray()); - return null; + _cache.Clear(); } - - //缓存 - static readonly Dictionary> _enumDict = new(); + + //(类型完整名,描述组合) + static readonly Dictionary> _cache = new(); /// /// 打印枚举的特性注释内容 @@ -28,68 +25,63 @@ public static class EnumEx where T : DescriptionAttribute { var eType = e.GetType(); - string eFullName = GetDisplayName(e); + string eFullName = eType.FullName + "." + e.ToString(); - if (_enumDict.ContainsKey(eFullName)) - return _enumDict[eFullName]; + if (_cache.ContainsKey(eFullName)) + return _cache[eFullName]; var fieldInfo = eType.GetField(Enum.GetName(eType, e)); if (fieldInfo == null) return null!; - var attribute = Attribute.GetCustomAttribute(fieldInfo, typeof(T)) as T; - if (attribute != null) + if (Attribute.GetCustomAttribute(fieldInfo, typeof(T)) is T attribute) { var res = new HashSet() { attribute.Description }; - _enumDict.Add(eFullName, res); + _cache.Add(eFullName, res); return res; } - //如果就是空的,就尝试去遍历所有的字段进行组合 - List enums = new(); - foreach (Enum item in Enum.GetValues(eType)) //遍历这个枚举类型 + //注释存放的容器 + HashSet nodes = new(); + + //通常到这里的就是 ALL = A | B | C + //遍历所有的枚举,组合每个注释 + List enumHas = new(); + foreach (Enum enumItem in Enum.GetValues(eType)) //遍历这个枚举类型 { - if ((e.GetHashCode() & item.GetHashCode()) == item.GetHashCode() && - e.GetHashCode() != item.GetHashCode()) - enums.Add(item); + if ((e.GetHashCode() & enumItem.GetHashCode()) == enumItem.GetHashCode() && + e.GetHashCode() != enumItem.GetHashCode()) + enumHas.Add(enumItem); } - - HashSet eNames = new(); - if (enums.Count > 0) + for (int i = 0; i < enumHas.Count; i++) { - for (int i = 0; i < enums.Count; i++) - { - var key = enums[i]; - var tname = GetDisplayName(key); - if (_enumDict.ContainsKey(tname)) - { - foreach (var item in _enumDict[tname]) - eNames.Add(item); - } - else - { - var get = GetAttribute(key, noDescrToString); - if (get != null) - foreach (var item in get) //没有缓存就递归 - eNames.Add(item); - } - } + var atts = GetAttribute(enumHas[i], noDescrToString);//递归 + if (atts == null) + continue; + foreach (var item in atts) + nodes.Add(item);//递归时候可能存在重复的元素 } - if (eNames.Count == 0 && noDescrToString) - eNames.Add(e.ToString()); + if (nodes.Count == 0 && noDescrToString) + nodes.Add(e.ToString()); - _enumDict.Add(eFullName, eNames); - return eNames; + _cache.Add(eFullName, nodes); + return nodes; } - - static string GetDisplayName(Enum e) + + /// + /// 打印枚举的特性注释内容 + /// + public static string? PrintNote(this Enum e, bool noDescToString = true) { - return e.GetType().FullName + "." + e.ToString(); + var hash = GetAttribute(e, noDescToString); + if (hash != null) + return string.Join("|", hash.ToArray()); + return null; } - //这个完全被上面替代了 + //TODO 山人审核代码之后可以删除,这个完全被上面替代了 public static string GetDesc(this Enum val) { var type = val.GetType(); @@ -101,5 +93,4 @@ public static string GetDesc(this Enum val) return ((DescriptionAttribute)attributes.Single()).Description; } -} - +} \ No newline at end of file -- Gitee From be820366ee0718709e616e9bc99fb28f648cea0f Mon Sep 17 00:00:00 2001 From: liuqihong <540762622@qq.com> Date: Tue, 23 Aug 2022 06:25:24 +0800 Subject: [PATCH 428/675] =?UTF-8?q?=E4=BF=AE=E6=94=B9=E4=BA=86=E4=B8=80?= =?UTF-8?q?=E4=B8=AA=E5=91=88=E7=8E=B0=E6=96=B9=E5=BC=8F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/IFoxCAD.Basal/ArrayEx.cs | 26 ++++++++++++++++---------- src/IFoxCAD.Basal/EnumEx.cs | 36 ++++++++++++++++++++++-------------- tests/TestConsole/Info.cs | 22 ++++++++++++++++++++++ tests/TestConsole/Program.cs | 11 ++++++++++- 4 files changed, 70 insertions(+), 25 deletions(-) diff --git a/src/IFoxCAD.Basal/ArrayEx.cs b/src/IFoxCAD.Basal/ArrayEx.cs index 2ac37fa..0699c33 100644 --- a/src/IFoxCAD.Basal/ArrayEx.cs +++ b/src/IFoxCAD.Basal/ArrayEx.cs @@ -21,7 +21,9 @@ public static T[] Combine2(this T[] a, T[] b) } /// - /// 一维数组消重,此函数建议更改为: + /// 一维数组按规则消除
    + /// 本例适用于数值类型比较,特定规则比较
    + /// 如果是哈希比较,建议更改为: /// set = new(); /// foreach (var item in listInOut) @@ -29,19 +31,23 @@ public static T[] Combine2(this T[] a, T[] b) /// ]]> ///
    /// - /// 传入有重复成员的数组,传出没有重复的 - /// 传出参数1:数组开头;传出参数2:数组结尾;返回值比较结尾为就移除 - [Obsolete] - public static void Deduplication(List listInOut, Func func) + /// 传入有重复成员的数组,原数组修改 + /// + /// 传出参数1:数组开头
    + /// 传出参数2:数组结尾
    + /// 返回值比较结尾为就移除
    + /// + public static void Deduplication(List lst, Func func) { - for (int i = 0; i < listInOut.Count; i++) + //头和尾比较,满足条件移除尾巴 + for (int i = 0; i < lst.Count; i++) { - var first = listInOut[i]; - for (int j = listInOut.Count - 1; j > i; j--) + var first = lst[i]; + for (int j = lst.Count - 1; j > i/*符号是 >= 而且是i*/; j--) { - var last = listInOut[j]; + var last = lst[j]; if (func(first, last)) - listInOut.RemoveAt(j); + lst.RemoveAt(j); } } } diff --git a/src/IFoxCAD.Basal/EnumEx.cs b/src/IFoxCAD.Basal/EnumEx.cs index 4c8bebf..8d0f71f 100644 --- a/src/IFoxCAD.Basal/EnumEx.cs +++ b/src/IFoxCAD.Basal/EnumEx.cs @@ -12,7 +12,7 @@ public static void CleanCache() { _cache.Clear(); } - + //(类型完整名,描述组合) static readonly Dictionary> _cache = new(); @@ -34,32 +34,40 @@ public static void CleanCache() if (fieldInfo == null) return null!; + //注释存放的容器 + HashSet nodes = new(); if (Attribute.GetCustomAttribute(fieldInfo, typeof(T)) is T attribute) { - var res = new HashSet() { attribute.Description }; - _cache.Add(eFullName, res); - return res; + nodes.Add(attribute.Description); + _cache.Add(eFullName, nodes); + return nodes; } - //注释存放的容器 - HashSet nodes = new(); - //通常到这里的就是 ALL = A | B | C //遍历所有的枚举,组合每个注释 List enumHas = new(); - foreach (Enum enumItem in Enum.GetValues(eType)) //遍历这个枚举类型 + foreach (Enum em in Enum.GetValues(eType)) //遍历这个枚举类型 { - if ((e.GetHashCode() & enumItem.GetHashCode()) == enumItem.GetHashCode() && - e.GetHashCode() != enumItem.GetHashCode()) - enumHas.Add(enumItem); + if ((e.GetHashCode() & em.GetHashCode()) == em.GetHashCode() && + e.GetHashCode() != em.GetHashCode()) + enumHas.Add(em); } - for (int i = 0; i < enumHas.Count; i++) + + //采取的行为是:注释的行为是特殊的,就按照注释的,否则,遍历子元素提取注释 + //大的在前面才能判断是否包含后面的,后面的就是要移除的 + enumHas = enumHas.OrderByDescending(a => a.GetHashCode()).ToList(); + ArrayEx.Deduplication(enumHas, (a, b) => { + return (a.GetHashCode() & b.GetHashCode()) == b.GetHashCode(); + }); + + //逆序仅仅为排序了逆向,不一定和书写顺序一样,尤其是递归可能存在重复的元素 + for (int i = enumHas.Count - 1; i >= 0; i--) { var atts = GetAttribute(enumHas[i], noDescrToString);//递归 if (atts == null) continue; foreach (var item in atts) - nodes.Add(item);//递归时候可能存在重复的元素 + nodes.Add(item); } if (nodes.Count == 0 && noDescrToString) @@ -68,7 +76,7 @@ public static void CleanCache() _cache.Add(eFullName, nodes); return nodes; } - + /// /// 打印枚举的特性注释内容 /// diff --git a/tests/TestConsole/Info.cs b/tests/TestConsole/Info.cs index 60bbe5b..a33fddc 100644 --- a/tests/TestConsole/Info.cs +++ b/tests/TestConsole/Info.cs @@ -17,3 +17,25 @@ public enum PlugIn All = Lisp | DOCBAR | DUOTAB } + + +[Flags] +public enum PlugIn2 +{ + [Description("惊惊")] + JoinBox = 1, + [Description("源泉")] + YuanQuan = 2, + [Description("迷你")] + IMinCad = 4, + + [Description("*Lisp*")] + Lisp = JoinBox | YuanQuan | IMinCad, + + DOCBAR = 8, + DUOTAB = 16, + + //all == *Lisp*|DOCBAR|DUOTAB + //采取的行为是:注释的行为是特殊的,就按照注释的,否则,遍历子元素提取注释 + All = Lisp | DOCBAR | DUOTAB +} \ No newline at end of file diff --git a/tests/TestConsole/Program.cs b/tests/TestConsole/Program.cs index b690334..ec47dd5 100644 --- a/tests/TestConsole/Program.cs +++ b/tests/TestConsole/Program.cs @@ -3,6 +3,7 @@ using System.Text; using TestConsole; +//乱序 Console.WriteLine(PlugIn.JoinBox.PrintNote()); Console.WriteLine(PlugIn.Lisp.PrintNote());//这里先交换顺序来试试能不能成功 Console.WriteLine(PlugIn.IMinCad.PrintNote()); @@ -10,8 +11,16 @@ Console.WriteLine(PlugIn.All.PrintNote()); Console.WriteLine(PlugIn.DOCBAR.PrintNote()); Console.WriteLine(PlugIn.DUOTAB.PrintNote()); +Console.WriteLine("***************************************************"); +//乱序2 +Console.WriteLine(PlugIn2.JoinBox.PrintNote()); +Console.WriteLine(PlugIn2.Lisp.PrintNote());//这里先交换顺序来试试能不能成功 +Console.WriteLine(PlugIn2.IMinCad.PrintNote()); +Console.WriteLine(PlugIn2.YuanQuan.PrintNote()); +Console.WriteLine(PlugIn2.All.PrintNote()); +Console.WriteLine(PlugIn2.DOCBAR.PrintNote()); +Console.WriteLine(PlugIn2.DUOTAB.PrintNote()); -Console.WriteLine("测试工程"); //表达式树例子 TestConsole.Test_Expression.Demo3(); -- Gitee From eda3e1ff943939f8c13b210213adb3baf9814965 Mon Sep 17 00:00:00 2001 From: liuqihong <540762622@qq.com> Date: Tue, 23 Aug 2022 06:34:40 +0800 Subject: [PATCH 429/675] =?UTF-8?q?=E4=BC=98=E5=8C=96=E6=B3=A8=E9=87=8A?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/IFoxCAD.Basal/EnumEx.cs | 11 ++++++----- tests/TestConsole/Program.cs | 1 + 2 files changed, 7 insertions(+), 5 deletions(-) diff --git a/src/IFoxCAD.Basal/EnumEx.cs b/src/IFoxCAD.Basal/EnumEx.cs index 8d0f71f..7165481 100644 --- a/src/IFoxCAD.Basal/EnumEx.cs +++ b/src/IFoxCAD.Basal/EnumEx.cs @@ -46,21 +46,22 @@ public static void CleanCache() //通常到这里的就是 ALL = A | B | C //遍历所有的枚举,组合每个注释 List enumHas = new(); - foreach (Enum em in Enum.GetValues(eType)) //遍历这个枚举类型 - { + + //遍历这个枚举类型,获取枚举按位包含的成员 + foreach (Enum em in Enum.GetValues(eType)) if ((e.GetHashCode() & em.GetHashCode()) == em.GetHashCode() && e.GetHashCode() != em.GetHashCode()) enumHas.Add(em); - } + //采取的行为是:注释的行为是特殊的,就按照注释的,否则,遍历子元素提取注释 - //大的在前面才能判断是否包含后面的,后面的就是要移除的 + //大的在前面才能判断是否按位包含后面的,后面的就是要移除的 enumHas = enumHas.OrderByDescending(a => a.GetHashCode()).ToList(); ArrayEx.Deduplication(enumHas, (a, b) => { return (a.GetHashCode() & b.GetHashCode()) == b.GetHashCode(); }); - //逆序仅仅为排序了逆向,不一定和书写顺序一样,尤其是递归可能存在重复的元素 + //逆序仅仅为排序后处理,不一定和书写顺序一样,尤其是递归可能存在重复的元素 for (int i = enumHas.Count - 1; i >= 0; i--) { var atts = GetAttribute(enumHas[i], noDescrToString);//递归 diff --git a/tests/TestConsole/Program.cs b/tests/TestConsole/Program.cs index ec47dd5..60325f7 100644 --- a/tests/TestConsole/Program.cs +++ b/tests/TestConsole/Program.cs @@ -21,6 +21,7 @@ Console.WriteLine(PlugIn2.DOCBAR.PrintNote()); Console.WriteLine(PlugIn2.DUOTAB.PrintNote()); +EnumEx.CleanCache(); //表达式树例子 TestConsole.Test_Expression.Demo3(); -- Gitee From 288d8f67e90da764046347b9905029c20ec187a3 Mon Sep 17 00:00:00 2001 From: liuqihong <540762622@qq.com> Date: Tue, 23 Aug 2022 11:45:43 +0800 Subject: [PATCH 430/675] =?UTF-8?q?=E4=BF=AE=E5=A5=BD=E4=BA=86=E7=BB=91?= =?UTF-8?q?=E5=AE=9A=E5=8F=82=E7=85=A7=E7=9A=84=E5=8F=82=E6=95=B0=E4=BA=86?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../ExtensionMethod/DatabaseEx.cs | 1 + .../ExtensionMethod/XrefEx.cs | 155 ++++++------------ .../IFoxCAD.Cad.Shared.projitems | 1 + src/IFoxCAD.Cad.Shared/Runtime/DBTrans.cs | 25 ++- tests/TestShared/XrefExTest.cs | 24 +++ 5 files changed, 97 insertions(+), 109 deletions(-) create mode 100644 tests/TestShared/XrefExTest.cs diff --git a/src/IFoxCAD.Cad.Shared/ExtensionMethod/DatabaseEx.cs b/src/IFoxCAD.Cad.Shared/ExtensionMethod/DatabaseEx.cs index c4261e8..427a2aa 100644 --- a/src/IFoxCAD.Cad.Shared/ExtensionMethod/DatabaseEx.cs +++ b/src/IFoxCAD.Cad.Shared/ExtensionMethod/DatabaseEx.cs @@ -9,6 +9,7 @@ public static class DatabaseEx /// 0x02 当关闭所有前台文档时,会出现无时,应该不能使用(惊惊没有测试过此状态)
    /// 测试条件是:当关闭所有前台文档时,那么如何发送命令呢?那就是利用跨进程通讯
    /// 0x03 此问题主要出现是这个线性引擎上面,在参照/深度克隆的底层共用此技术,导致单行文字偏移
    + /// 0x04 异常: 前台绑定的时候不能用它,否则出现:
    /// ///
    /// 后台打开的数据库 diff --git a/src/IFoxCAD.Cad.Shared/ExtensionMethod/XrefEx.cs b/src/IFoxCAD.Cad.Shared/ExtensionMethod/XrefEx.cs index 995b00b..c6b4cfc 100644 --- a/src/IFoxCAD.Cad.Shared/ExtensionMethod/XrefEx.cs +++ b/src/IFoxCAD.Cad.Shared/ExtensionMethod/XrefEx.cs @@ -1,59 +1,8 @@ //#define error_demo -#define test_demo - -using System.Diagnostics; +#define lack_test namespace IFoxCAD.Cad; -#if test_demo -public class TestCmd_BindXrefs -{ - //后台绑定 - [CommandMethod("Test_Bind1")] - public static void Test_Bind1() - { - string newfile = @"D:\Test.dwg"; - using var tr = new DBTrans(newfile, - openMode: FileOpenMode.OpenForReadAndAllShare/*后台绑定特别注意*/); - tr.XrefFactory(XrefModes.Bind); - tr.SaveDwgFile(); - } - - //前台绑定 - [CommandMethod("Test_Bind2")] - public static void Test_Bind2() - { - using var tr = new DBTrans(); - tr.XrefFactory(XrefModes.Bind); - tr.SaveDwgFile(); - } - - [CommandMethod("Test_Bind3")] - public static void Test_Bind3() - { - using var tr = new DBTrans(); - var xf = new XrefFactory(tr) - { - UseThreadEngine = false - }; - DatabaseEx.DBTextDeviation(tr.Database, xf.Bind); - tr.SaveDwgFile(); - } - - [CommandMethod("Test_Bind4")] - public static void Test_Bind4() - { - using var tr = new DBTrans(); - var xf = new XrefFactory(tr) - { - UseThreadEngine = true - }; - DatabaseEx.DBTextDeviation(tr.Database, xf.Bind); - tr.SaveDwgFile(); - } -} -#endif - #region 参照工厂 public interface IXrefBindModes { @@ -88,14 +37,15 @@ public class XrefFactory : IXrefBindModes #region 公开字段 /// /// 解析外部参照:线性引擎
    - /// 默认为true:会在cad命令历史打印一些AEC信息,并导致绑定慢一点...具体作用不详
    - /// 为false会出现bind出现异常:
    + /// 默认
    + /// 时会在cad命令历史打印一些AEC信息,并导致绑定慢一点...具体作用不详
    ///
    - public bool UseThreadEngine = true; + public bool UseThreadEngine = false; /// - /// 解析外部参照:仅处理 Unresolved_未融入(未解析)的参照 + /// 解析外部参照:仅处理 Unresolved_未融入(未解析)的参照
    + /// 默认 ///
    - public bool DoNewOnly = false; + public bool DoNewOnly = true; /// /// 解析外部参照:包含僵尸参照 /// @@ -104,14 +54,14 @@ public class XrefFactory : IXrefBindModes /// /// bind时候是否为插入模式
    - /// 时候不出现双美元符号 + /// 默认可能和双美元符号有关 ///
    public bool InsertBind = true; /// /// bind时候是否拆离参照
    /// 默认:学官方的绑定后自动拆离 ///
    - public bool DetachFlag = true; + public bool AutoDetach = true; /// /// bind时候是否删除被卸载的嵌套参照
    /// 默认 @@ -126,6 +76,7 @@ public class XrefFactory : IXrefBindModes #endregion + #region 构造 /// /// 参照工厂 /// @@ -133,10 +84,12 @@ public class XrefFactory : IXrefBindModes /// 要处理的参照名称,就处理所有 public XrefFactory(DBTrans tr, HashSet? xrefNames = null) { - this._tr = tr; - this._xrefNames = xrefNames; + _tr = tr; + _xrefNames = xrefNames; } + #endregion + #region 重写 public void Bind() { //此功能有绑定出错的问题 @@ -167,8 +120,9 @@ public void Unload() if (xrefIds.Count > 0) _tr.Database.UnloadXrefs(xrefIds); } + #endregion - + #region 双重绑定 /// /// 获取参照 /// @@ -178,13 +132,18 @@ ObjectIdCollection GetAllXrefNode() //储存要处理的参照id var xrefIds = new ObjectIdCollection(); XrefNodeForEach((xNodeName, xNodeId, xNodeStatus, xNodeIsNested) => { - //为空的时候全部加入 || 有内容时候含有目标 - if (_xrefNames is null || _xrefNames.Contains(xNodeName)) + if (XrefNamesContains(xNodeName)) xrefIds.Add(xNodeId); }); return xrefIds; } + bool XrefNamesContains(string xNodeName) + { + //为空的时候全部加入 || 有内容时候含有目标 + return _xrefNames is null || _xrefNames.Contains(xNodeName); + } + /// /// 遍历参照 /// @@ -214,7 +173,6 @@ void XrefNodeForEach(Action action) } } - /// /// 符号表记录加入容器 /// @@ -229,28 +187,6 @@ static void AddedxbindIds(ObjectIdCollection xbindXrefsIds, }, checkIdOk: true); } - ObjectIdCollection GetBindIds() - { - //bind 只绑块表 - var bindIds = new ObjectIdCollection(); - - _tr.BlockTable.ForEach(btr => { - if (btr.IsLayout) - return; - -#if DEBUG - if (btr.IsFromExternalReference) - Debug.WriteLine(btr.ObjectId); -#endif - - //外部参照 && 已融入 - if (btr.IsFromExternalReference && btr.IsResolved) - bindIds.Add(btr.ObjectId); - }, checkIdOk: true); - - return bindIds; - } - ObjectIdCollection GetXBindIds() { //xbind @@ -290,6 +226,23 @@ ObjectIdCollection GetXBindIds() return xbindIds; } + ObjectIdCollection GetBindIds() + { + //bind 只绑块表 + var bindIds = new ObjectIdCollection(); + + _tr.BlockTable.ForEach(btr => { + if (btr.IsLayout) + return; + + //外部参照 && 已融入 + if (btr.IsFromExternalReference && btr.IsResolved) + bindIds.Add(btr.ObjectId); + }, checkIdOk: true); + + return bindIds; + } + /// /// 获取可以拆离的ids /// @@ -311,14 +264,14 @@ List GetDetachIds(Dictionary nested) case XrefStatus.Unreferenced://未参照 { //为空的时候全部加入 || 有内容时候含有目标 - if (_xrefNames == null || _xrefNames.Contains(xNodeName)) + if (XrefNamesContains(xNodeName)) detachIds.Add(xNodeId); } break; case XrefStatus.Unloaded://已卸载 { //为空的时候全部加入 || 有内容时候含有目标 - if (_xrefNames == null || _xrefNames.Contains(xNodeName)) + if (XrefNamesContains(xNodeName)) { var btr = _tr.GetObject(xNodeId); if (btr != null && btr.IsFromExternalReference) @@ -342,7 +295,6 @@ List GetDetachIds(Dictionary nested) return detachIds; } - /// /// 双重绑定参照 /// 参考链接 @@ -353,7 +305,7 @@ void DoubleBind() var detachIds = GetDetachIds(nested); //拆离:未参照的文件 - if (DetachFlag) + if (AutoDetach) { for (int i = 0; i < detachIds.Count; i++) _tr.Database.DetachXref(detachIds[i]); @@ -378,15 +330,15 @@ void DoubleBind() #if ac2008 //因为Acad08索引器存在会暴露isErase的(桌子底层的原因), //也就是可能获取两个名称一样的,只能用遍历的方式进行 - HashSet namess = new(); + HashSet nestedHash = new(); foreach (var item in nested) - namess.Add(item.Value); + nestedHash.Add(item.Value); //遍历全图,找到参照名称一样的删除 _tr.BlockTable.ForEach(btr => { if (btr.IsLayout) return; - if (namess.Contains(btr.Name)) + if (nestedHash.Contains(btr.Name)) { btr.UpgradeOpen(); btr.Erase(); @@ -404,7 +356,8 @@ void DoubleBind() } #endif } - } + } + #endregion } @@ -417,20 +370,18 @@ public static class XrefEx /// /// 处理参照的枚举 /// 要处理的参照名称,就处理所有 - /// 处理单行文字偏移 public static void XrefFactory(this DBTrans tr, XrefModes xrefModes, - HashSet? xrefNames = null, - bool isDBTextDeviation = true) + HashSet? xrefNames = null) { var xf = new XrefFactory(tr, xrefNames); - if (isDBTextDeviation) + if (tr.Document == null)//判断是后台 { DatabaseEx.DBTextDeviation(tr.Database, () => { SetXrefBind(xrefModes, xf); }); } - else + else //前台 { SetXrefBind(xrefModes, xf); } @@ -714,6 +665,7 @@ static string GetRelativePath(string directory, string file) #endregion +#if lack_test public static class DBTransEx { /// @@ -758,4 +710,5 @@ static void DatabasePurge(Database db, while (ids.Count > 0) db.Purge(ids); } -} \ No newline at end of file +} +#endif \ No newline at end of file diff --git a/src/IFoxCAD.Cad.Shared/IFoxCAD.Cad.Shared.projitems b/src/IFoxCAD.Cad.Shared/IFoxCAD.Cad.Shared.projitems index 98110ab..3208026 100644 --- a/src/IFoxCAD.Cad.Shared/IFoxCAD.Cad.Shared.projitems +++ b/src/IFoxCAD.Cad.Shared/IFoxCAD.Cad.Shared.projitems @@ -18,6 +18,7 @@ + diff --git a/src/IFoxCAD.Cad.Shared/Runtime/DBTrans.cs b/src/IFoxCAD.Cad.Shared/Runtime/DBTrans.cs index 47fd4fb..d4ca4c6 100644 --- a/src/IFoxCAD.Cad.Shared/Runtime/DBTrans.cs +++ b/src/IFoxCAD.Cad.Shared/Runtime/DBTrans.cs @@ -152,33 +152,42 @@ public DBTrans(string fileName, else { #if ac2008 - FileAccess fileAccess = FileAccess.Read; + //FileAccess fileAccess = FileAccess.Read; FileShare fileShare = FileShare.Read; switch (openMode) { case FileOpenMode.OpenTryForReadShare://这个是什么状态?? - fileAccess = FileAccess.ReadWrite; + //fileAccess = FileAccess.ReadWrite; fileShare = FileShare.ReadWrite; break; case FileOpenMode.OpenForReadAndAllShare://完美匹配 - fileAccess = FileAccess.ReadWrite; + //fileAccess = FileAccess.ReadWrite; fileShare = FileShare.ReadWrite; break; case FileOpenMode.OpenForReadAndWriteNoShare://完美匹配 - fileAccess = FileAccess.ReadWrite; + //fileAccess = FileAccess.ReadWrite; fileShare = FileShare.None; break; case FileOpenMode.OpenForReadAndReadShare://完美匹配 - fileAccess = FileAccess.Read; + //fileAccess = FileAccess.Read; fileShare = FileShare.Read; break; default: break; } - using FileStream fileStream = new(fileName, FileMode.Open, fileAccess, fileShare); - Database.ReadDwgFile(fileStream.SafeFileHandle.DangerousGetHandle(), true/*控制读入一个与系统编码不相同的文件时的转换操作*/, password); + + //这个会致命错误 + //using FileStream fileStream = new(fileName, FileMode.Open, fileAccess, fileShare); + //Database.ReadDwgFile(fileStream.SafeFileHandle.DangerousGetHandle(), + // true/*控制读入一个与系统编码不相同的文件时的转换操作*/,password); + + + Database.ReadDwgFile(fileName, fileShare, + true/*控制读入一个与系统编码不相同的文件时的转换操作*/, password); + #else - Database.ReadDwgFile(fileName, openMode, true/*控制读入一个与系统编码不相同的文件时的转换操作*/, password); + Database.ReadDwgFile(fileName, openMode, + true/*控制读入一个与系统编码不相同的文件时的转换操作*/, password); #endif } Database.CloseInput(true); diff --git a/tests/TestShared/XrefExTest.cs b/tests/TestShared/XrefExTest.cs new file mode 100644 index 0000000..b1d2545 --- /dev/null +++ b/tests/TestShared/XrefExTest.cs @@ -0,0 +1,24 @@ +namespace Test; + +public class TestCmd_BindXrefs +{ + //后台绑定 + [CommandMethod("Test_Bind1")] + public static void Test_Bind1() + { + string fileName = @"D:\Test.dwg"; + using var tr = new DBTrans(fileName, + openMode: FileOpenMode.OpenForReadAndAllShare/*后台绑定特别注意*/); + tr.XrefFactory(XrefModes.Bind); + tr.SaveDwgFile(); + } + + //前台绑定 + [CommandMethod("Test_Bind2")] + public static void Test_Bind2() + { + using var tr = new DBTrans(); + tr.XrefFactory(XrefModes.Bind); + tr.SaveDwgFile(); + } +} -- Gitee From 7a6f55d2ff7f09543197f60c272aea91984aa0f6 Mon Sep 17 00:00:00 2001 From: liuqihong <540762622@qq.com> Date: Tue, 23 Aug 2022 12:23:08 +0800 Subject: [PATCH 431/675] =?UTF-8?q?=E5=BF=98=E8=AE=B0=E5=88=A0=E9=99=A4?= =?UTF-8?q?=E4=B8=80=E4=B8=AA=E5=A4=9A=E4=BD=99=E6=96=87=E4=BB=B6=E4=BA=86?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/IFoxCAD.Cad.Shared/IFoxCAD.Cad.Shared.projitems | 1 - 1 file changed, 1 deletion(-) diff --git a/src/IFoxCAD.Cad.Shared/IFoxCAD.Cad.Shared.projitems b/src/IFoxCAD.Cad.Shared/IFoxCAD.Cad.Shared.projitems index 3208026..98110ab 100644 --- a/src/IFoxCAD.Cad.Shared/IFoxCAD.Cad.Shared.projitems +++ b/src/IFoxCAD.Cad.Shared/IFoxCAD.Cad.Shared.projitems @@ -18,7 +18,6 @@ - -- Gitee From 277a1c64fa9e756e365aab9514d3dfdc22679f59 Mon Sep 17 00:00:00 2001 From: liuqihong <540762622@qq.com> Date: Tue, 23 Aug 2022 20:59:40 +0800 Subject: [PATCH 432/675] =?UTF-8?q?=E5=9C=A8=E4=BA=8B=E5=8A=A1=E6=A0=88?= =?UTF-8?q?=E4=B8=AD=E6=8F=90=E4=BE=9B=E4=B8=80=E4=B8=AATask=E6=96=B9?= =?UTF-8?q?=E6=B3=95=E6=96=B9=E4=BE=BF=E5=A4=84=E7=90=86=E5=89=8D=E5=90=8E?= =?UTF-8?q?=E5=8F=B0=E4=BB=BB=E5=8A=A1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../ExtensionMethod/DatabaseEx.cs | 27 ---------- .../ExtensionMethod/XrefEx.cs | 50 +++++++------------ .../IFoxCAD.Cad.Shared.projitems | 1 - src/IFoxCAD.Cad.Shared/Runtime/DBTrans.cs | 48 ++++++++++++++++-- tests/TestShared/TestMirrorFile.cs | 5 +- 5 files changed, 65 insertions(+), 66 deletions(-) delete mode 100644 src/IFoxCAD.Cad.Shared/ExtensionMethod/DatabaseEx.cs diff --git a/src/IFoxCAD.Cad.Shared/ExtensionMethod/DatabaseEx.cs b/src/IFoxCAD.Cad.Shared/ExtensionMethod/DatabaseEx.cs deleted file mode 100644 index 427a2aa..0000000 --- a/src/IFoxCAD.Cad.Shared/ExtensionMethod/DatabaseEx.cs +++ /dev/null @@ -1,27 +0,0 @@ -namespace IFoxCAD.Cad; - -public static class DatabaseEx -{ - /// - /// 后台开图文字偏移处理 - /// - /// 0x01 此方案利用前台数据库进行处理
    - /// 0x02 当关闭所有前台文档时,会出现无时,应该不能使用(惊惊没有测试过此状态)
    - /// 测试条件是:当关闭所有前台文档时,那么如何发送命令呢?那就是利用跨进程通讯
    - /// 0x03 此问题主要出现是这个线性引擎上面,在参照/深度克隆的底层共用此技术,导致单行文字偏移
    - /// 0x04 异常: 前台绑定的时候不能用它,否则出现:
    - ///
    - ///
    - /// 后台打开的数据库 - /// 处理后台的任务 - public static void DBTextDeviation(this Database backstageOpenDwg, Action action) - { - var wdb = HostApplicationServices.WorkingDatabase; - if (wdb != null) - { - HostApplicationServices.WorkingDatabase = backstageOpenDwg; - action?.Invoke(); - HostApplicationServices.WorkingDatabase = wdb; - } - } -} \ No newline at end of file diff --git a/src/IFoxCAD.Cad.Shared/ExtensionMethod/XrefEx.cs b/src/IFoxCAD.Cad.Shared/ExtensionMethod/XrefEx.cs index c6b4cfc..4324439 100644 --- a/src/IFoxCAD.Cad.Shared/ExtensionMethod/XrefEx.cs +++ b/src/IFoxCAD.Cad.Shared/ExtensionMethod/XrefEx.cs @@ -375,37 +375,25 @@ public static void XrefFactory(this DBTrans tr, HashSet? xrefNames = null) { var xf = new XrefFactory(tr, xrefNames); - if (tr.Document == null)//判断是后台 - { - DatabaseEx.DBTextDeviation(tr.Database, () => { - SetXrefBind(xrefModes, xf); - }); - } - else //前台 - { - SetXrefBind(xrefModes, xf); - } - } - - static void SetXrefBind(XrefModes xrefModes, XrefFactory xf) - { - switch (xrefModes) - { - case XrefModes.Unload: - xf.Unload(); - break; - case XrefModes.Reload: - xf.Reload(); - break; - case XrefModes.Detach: - xf.Detach(); - break; - case XrefModes.Bind: - xf.Bind(); - break; - default: - break; - } + tr.Task(() => { + switch (xrefModes) + { + case XrefModes.Unload: + xf.Unload(); + break; + case XrefModes.Reload: + xf.Reload(); + break; + case XrefModes.Detach: + xf.Detach(); + break; + case XrefModes.Bind: + xf.Bind(); + break; + default: + break; + } + }); } } diff --git a/src/IFoxCAD.Cad.Shared/IFoxCAD.Cad.Shared.projitems b/src/IFoxCAD.Cad.Shared/IFoxCAD.Cad.Shared.projitems index 98110ab..06e287f 100644 --- a/src/IFoxCAD.Cad.Shared/IFoxCAD.Cad.Shared.projitems +++ b/src/IFoxCAD.Cad.Shared/IFoxCAD.Cad.Shared.projitems @@ -24,7 +24,6 @@ - diff --git a/src/IFoxCAD.Cad.Shared/Runtime/DBTrans.cs b/src/IFoxCAD.Cad.Shared/Runtime/DBTrans.cs index d4ca4c6..fc80105 100644 --- a/src/IFoxCAD.Cad.Shared/Runtime/DBTrans.cs +++ b/src/IFoxCAD.Cad.Shared/Runtime/DBTrans.cs @@ -128,9 +128,11 @@ public DBTrans(string fileName, FileOpenMode openMode = FileOpenMode.OpenForReadAndWriteNoShare, string? password = null) { - if (string.IsNullOrEmpty(fileName?.Trim())) + if (fileName == null || string.IsNullOrEmpty(fileName.Trim())) throw new ArgumentNullException(nameof(fileName)); + fileName = fileName.Replace("/", "\\");////doc.Name总是"D:\\JX.dwg" + if (!File.Exists(fileName)) Database = new Database(true, false); else @@ -181,10 +183,8 @@ public DBTrans(string fileName, //Database.ReadDwgFile(fileStream.SafeFileHandle.DangerousGetHandle(), // true/*控制读入一个与系统编码不相同的文件时的转换操作*/,password); - Database.ReadDwgFile(fileName, fileShare, true/*控制读入一个与系统编码不相同的文件时的转换操作*/, password); - #else Database.ReadDwgFile(fileName, openMode, true/*控制读入一个与系统编码不相同的文件时的转换操作*/, password); @@ -396,7 +396,47 @@ public void SaveDwgFile(DwgVersion version = DwgVersion.AC1800) } #endregion - #region idispose接口相关函数 + + /// + /// 自动进行前后台分开处理的任务 + /// + /// + /// 备注:
    + /// 0x01 此方案利用前台数据库进行处理
    + /// 0x02 此问题主要出现是这个线性引擎上面,在参照/深度克隆的底层共用此技术,导致单行文字偏移
    + /// 0x03 异常: 前台绑定的时候不能用它,否则出现:
    + ///
    + /// 委托 + /// 开启单行文字偏移处理 + public void Task(Action action, bool handlingDBTextDeviation = true) + { + if (action == null) + throw new ArgumentNullException(nameof(action)); + + //前台开图 || 后台直接处理 + if (Document != null || !handlingDBTextDeviation) + { + action.Invoke(); + return; + } + + //后台 + //这种情况发生在关闭了所有文档之后,进行跨进程通讯 + var dbBak = HostApplicationServices.WorkingDatabase; + if (dbBak == null) + { + action.Invoke(); + return; + } + + //处理单行文字偏移 + HostApplicationServices.WorkingDatabase = Database; + action.Invoke(); + HostApplicationServices.WorkingDatabase = dbBak; + } + + + #region IDisposable接口相关函数 /// /// 取消事务 /// diff --git a/tests/TestShared/TestMirrorFile.cs b/tests/TestShared/TestMirrorFile.cs index 4d13fcc..5c52b67 100644 --- a/tests/TestShared/TestMirrorFile.cs +++ b/tests/TestShared/TestMirrorFile.cs @@ -41,10 +41,9 @@ public static void CmdTest_MirrorFile() [CommandMethod("CmdTest_MirrorFile2")] public static void CmdTest_MirrorFile2() { - using var tr = new DBTrans(file, openMode: FileOpenMode.OpenForReadAndReadShare); - - tr.Database.DBTextDeviation(() => { + using var tr = new DBTrans(file); + tr.Task(() => { var yaxis = new Point3d(0, 1, 0); tr.BlockTable.Change(tr.ModelSpace.ObjectId, modelSpace => { foreach (ObjectId entId in modelSpace) -- Gitee From 6c212dbdb3b3367c04b5e39fec7083d231b71eb2 Mon Sep 17 00:00:00 2001 From: liuqihong <540762622@qq.com> Date: Wed, 24 Aug 2022 15:06:56 +0800 Subject: [PATCH 433/675] =?UTF-8?q?=E4=BF=AE=E6=94=B9=E4=BA=86bug?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../ExtensionMethod/DBObjectEx.cs | 37 ++++++++++++++----- tests/TestShared/Test.cs | 30 ++++++++++----- 2 files changed, 49 insertions(+), 18 deletions(-) diff --git a/src/IFoxCAD.Cad.Shared/ExtensionMethod/DBObjectEx.cs b/src/IFoxCAD.Cad.Shared/ExtensionMethod/DBObjectEx.cs index 8ebee46..acd9822 100644 --- a/src/IFoxCAD.Cad.Shared/ExtensionMethod/DBObjectEx.cs +++ b/src/IFoxCAD.Cad.Shared/ExtensionMethod/DBObjectEx.cs @@ -1,4 +1,6 @@ -namespace IFoxCAD.Cad; +using System.Linq; + +namespace IFoxCAD.Cad; /// /// 实体对象扩展类 @@ -16,22 +18,39 @@ public static void RemoveXData(this DBObject obj, string appName, DxfCode dxfCod { if (obj.XData == null) return; - XDataList data = obj.XData; - bool appNameIdentical = false; + XDataList data = obj.XData;//测试命令 addxdata removexdata + + int appNameIndex = -1; + int appNameIndexNext = -1; + + //先找到属于它的名字索引,然后再找到下一个不属于它名字的索引,移除中间部分 for (int i = 0; i < data.Count; i++) { if (data[i].TypeCode == (int)DxfCode.ExtendedDataRegAppName) { - appNameIdentical = data[i].Value.ToString() == appName; - break; + if (data[i].Value.ToString() == appName) + { + appNameIndex = i; + continue; + } + if (appNameIndex != -1)//表示已经记录,开始它后面的appName + { + appNameIndexNext = i; + break; + } } } - if (!appNameIdentical) + if (appNameIndex == -1) return; - for (int i = data.Count - 1; i >= 0; i--) - if (data[i].TypeCode == (int)dxfCode) - data.RemoveAt(i); + if (appNameIndexNext == -1) + appNameIndexNext = data.Count; + + //移除指定App的扩展 + for (int i = appNameIndexNext - 1; i >= appNameIndex; i--) + if (data[i].TypeCode == ((short)dxfCode)) + data.RemoveAt(i); + using (obj.ForWrite()) obj.XData = data; } diff --git a/tests/TestShared/Test.cs b/tests/TestShared/Test.cs index 2cf3ac2..e2aa9d8 100644 --- a/tests/TestShared/Test.cs +++ b/tests/TestShared/Test.cs @@ -197,20 +197,28 @@ public void Addpl2() public void AddXdata() { using var tr = new DBTrans(); - var appname = "myapp"; + var appname = "myapp2"; + tr.RegAppTable.Add("myapp1"); tr.RegAppTable.Add(appname); // add函数会默认的在存在这个名字的时候返回这个名字的regapp的id,不存在就新建 - tr.RegAppTable.Add("myapp2"); + tr.RegAppTable.Add("myapp3"); + tr.RegAppTable.Add("myapp4"); var line = new Line(new Point3d(0, 0, 0), new Point3d(1, 1, 0)) { XData = new XDataList() { - { DxfCode.ExtendedDataRegAppName, appname }, //可以用dxfcode和int表示组码 - { DxfCode.ExtendedDataAsciiString, "hahhahah" }, + { DxfCode.ExtendedDataRegAppName, "myapp1" }, //可以用dxfcode和int表示组码 + { DxfCode.ExtendedDataAsciiString, "xxxxxxx" }, {1070, 12 }, - { DxfCode.ExtendedDataRegAppName, "myapp2" }, //可以用dxfcode和int表示组码 + { DxfCode.ExtendedDataRegAppName, appname }, //可以用dxfcode和int表示组码,移除中间的测试 { DxfCode.ExtendedDataAsciiString, "hahhahah" }, + {1070, 12 }, + { DxfCode.ExtendedDataRegAppName, "myapp3" }, //可以用dxfcode和int表示组码 + { DxfCode.ExtendedDataAsciiString, "aaaaaaaaa" }, + {1070, 12 }, + { DxfCode.ExtendedDataRegAppName, "myapp4" }, //可以用dxfcode和int表示组码 + { DxfCode.ExtendedDataAsciiString, "bbbbbbbbb" }, {1070, 12 } } }; @@ -256,19 +264,23 @@ public void Changexdata() ed.WriteMessage(data.XData.ToString()); } } + + [CommandMethod("removexdata")] public void Removexdata() { var doc = Acap.DocumentManager.MdiActiveDocument; var ed = doc.Editor; - var appname = "myapp"; + var appname = "myapp2"; var res = ed.GetEntity("\n select the entity:"); if (res.Status == PromptStatus.OK) { using var tr = new DBTrans(); - var data = tr.GetObject(res.ObjectId); - data?.RemoveXData(appname, DxfCode.ExtendedDataAsciiString); - ed.WriteMessage(data?.XData.ToString()); + var ent = tr.GetObject(res.ObjectId); + ed.WriteMessage("\n移除前:" + ent?.XData.ToString()); + + ent?.RemoveXData(appname, DxfCode.ExtendedDataAsciiString); + ed.WriteMessage("\n移除后:" + ent?.XData.ToString()); } } -- Gitee From be2697060d2cd579c984f216fa40a6a94c0b0356 Mon Sep 17 00:00:00 2001 From: liuqihong <540762622@qq.com> Date: Wed, 24 Aug 2022 15:28:26 +0800 Subject: [PATCH 434/675] =?UTF-8?q?=E4=BF=AE=E6=94=B9bug?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../ExtensionMethod/DBObjectEx.cs | 58 ++++++++++--------- 1 file changed, 32 insertions(+), 26 deletions(-) diff --git a/src/IFoxCAD.Cad.Shared/ExtensionMethod/DBObjectEx.cs b/src/IFoxCAD.Cad.Shared/ExtensionMethod/DBObjectEx.cs index acd9822..ef88878 100644 --- a/src/IFoxCAD.Cad.Shared/ExtensionMethod/DBObjectEx.cs +++ b/src/IFoxCAD.Cad.Shared/ExtensionMethod/DBObjectEx.cs @@ -9,17 +9,13 @@ public static class DBObjectEx { #region Xdata扩展 /// - /// 删除扩展数据 + /// 获取appName的索引区间 /// - /// 对象实例 - /// 应用程序名称 - /// 要删除数据的组码 - public static void RemoveXData(this DBObject obj, string appName, DxfCode dxfCode) + /// + /// + /// + public static void GetAppIndex(XDataList data, string appName, Action action) { - if (obj.XData == null) - return; - XDataList data = obj.XData;//测试命令 addxdata removexdata - int appNameIndex = -1; int appNameIndexNext = -1; @@ -46,10 +42,28 @@ public static void RemoveXData(this DBObject obj, string appName, DxfCode dxfCod if (appNameIndexNext == -1) appNameIndexNext = data.Count; + action?.Invoke(appNameIndex, appNameIndexNext); + } + + /// + /// 删除扩展数据 + /// + /// 对象实例 + /// 应用程序名称 + /// 要删除数据的组码 + public static void RemoveXData(this DBObject obj, string appName, DxfCode dxfCode) + { + if (obj.XData == null) + return; + XDataList data = obj.XData; + + //测试命令 addxdata removexdata //移除指定App的扩展 - for (int i = appNameIndexNext - 1; i >= appNameIndex; i--) - if (data[i].TypeCode == ((short)dxfCode)) - data.RemoveAt(i); + GetAppIndex(data, appName, (appNameIndex, appNameIndexNext) => { + for (int i = appNameIndexNext - 1; i >= appNameIndex; i--) + if (data[i].TypeCode == ((short)dxfCode)) + data.RemoveAt(i); + }); using (obj.ForWrite()) obj.XData = data; @@ -66,21 +80,13 @@ public static void ChangeXData(this DBObject obj, string appName, DxfCode dxfCod if (obj.XData == null) return; XDataList data = obj.XData; - bool appNameIdentical = false; - for (int i = 0; i < data.Count; i++) - { - if (data[i].TypeCode == (int)DxfCode.ExtendedDataRegAppName) - { - appNameIdentical = data[i].Value.ToString() == appName; - break; - } - } - if (!appNameIdentical) - return; - for (int i = data.Count - 1; i >= 0; i--) - if (data[i].TypeCode == (int)dxfCode) - data[i] = new TypedValue((int)dxfCode, newvalue); + GetAppIndex(data, appName, (appNameIndex, appNameIndexNext) => { + for (int i = appNameIndexNext - 1; i >= appNameIndex; i--) + if (data[i].TypeCode == (short)dxfCode) + data[i] = new TypedValue((short)dxfCode, newvalue); + }); + using (obj.ForWrite()) obj.XData = data; } -- Gitee From 734a483f6260512b4c99b10d65a08a3a313b51ca Mon Sep 17 00:00:00 2001 From: liuqihong <540762622@qq.com> Date: Wed, 24 Aug 2022 15:37:06 +0800 Subject: [PATCH 435/675] =?UTF-8?q?=E6=89=94=E5=88=B0=E5=A7=94=E6=89=98?= =?UTF-8?q?=E5=86=85=E4=B8=8D=E9=9C=80=E8=A6=81=E6=AF=8F=E6=AC=A1=E5=BC=80?= =?UTF-8?q?=E5=9B=BE=E5=85=83?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/IFoxCAD.Cad.Shared/ExtensionMethod/DBObjectEx.cs | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/IFoxCAD.Cad.Shared/ExtensionMethod/DBObjectEx.cs b/src/IFoxCAD.Cad.Shared/ExtensionMethod/DBObjectEx.cs index ef88878..d6899b6 100644 --- a/src/IFoxCAD.Cad.Shared/ExtensionMethod/DBObjectEx.cs +++ b/src/IFoxCAD.Cad.Shared/ExtensionMethod/DBObjectEx.cs @@ -63,10 +63,10 @@ public static void RemoveXData(this DBObject obj, string appName, DxfCode dxfCod for (int i = appNameIndexNext - 1; i >= appNameIndex; i--) if (data[i].TypeCode == ((short)dxfCode)) data.RemoveAt(i); - }); - using (obj.ForWrite()) - obj.XData = data; + using (obj.ForWrite()) + obj.XData = data; + }); } /// /// 修改扩展数据 @@ -85,10 +85,10 @@ public static void ChangeXData(this DBObject obj, string appName, DxfCode dxfCod for (int i = appNameIndexNext - 1; i >= appNameIndex; i--) if (data[i].TypeCode == (short)dxfCode) data[i] = new TypedValue((short)dxfCode, newvalue); - }); - using (obj.ForWrite()) - obj.XData = data; + using (obj.ForWrite()) + obj.XData = data; + }); } #endregion -- Gitee From ac07214567f39b583ddaeeda4bc25476e24c5e30 Mon Sep 17 00:00:00 2001 From: liuqihong <540762622@qq.com> Date: Wed, 24 Aug 2022 16:12:23 +0800 Subject: [PATCH 436/675] =?UTF-8?q?=E5=88=A9=E7=94=A8=E7=AC=AC=E4=B8=80?= =?UTF-8?q?=E6=AC=A1=E5=BE=AA=E7=8E=AF=E5=B0=B1=E6=89=BE=E5=88=B0=E7=A7=BB?= =?UTF-8?q?=E9=99=A4=E7=9A=84=E7=B4=A2=E5=BC=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../ExtensionMethod/DBObjectEx.cs | 68 +++++++++++-------- tests/TestShared/Test.cs | 2 +- 2 files changed, 41 insertions(+), 29 deletions(-) diff --git a/src/IFoxCAD.Cad.Shared/ExtensionMethod/DBObjectEx.cs b/src/IFoxCAD.Cad.Shared/ExtensionMethod/DBObjectEx.cs index d6899b6..4a3237a 100644 --- a/src/IFoxCAD.Cad.Shared/ExtensionMethod/DBObjectEx.cs +++ b/src/IFoxCAD.Cad.Shared/ExtensionMethod/DBObjectEx.cs @@ -8,41 +8,51 @@ namespace IFoxCAD.Cad; public static class DBObjectEx { #region Xdata扩展 + /// /// 获取appName的索引区间 /// - /// - /// - /// - public static void GetAppIndex(XDataList data, string appName, Action action) + /// xdata + /// 注册名称 + /// 任务组码对象 + /// 返回任务组码的索引 + static List GetXdataAppIndex(XDataList data, string appName, DxfCode[] dxfCodes) { + List acIndex = new(); int appNameIndex = -1; - int appNameIndexNext = -1; + //int appNameIndexNext = -1; //先找到属于它的名字索引,然后再找到下一个不属于它名字的索引,移除中间部分 for (int i = 0; i < data.Count; i++) { - if (data[i].TypeCode == (int)DxfCode.ExtendedDataRegAppName) + if (data[i].TypeCode == (short)DxfCode.ExtendedDataRegAppName) { if (data[i].Value.ToString() == appName) { appNameIndex = i; continue; } - if (appNameIndex != -1)//表示已经记录,开始它后面的appName + if (appNameIndex != -1) { - appNameIndexNext = i; + //找到了后面的appName + //appNameIndexNext = i; break; } } + if (appNameIndex != -1 && //找next的时候,获取任务(移除)的对象 + dxfCodes.Contains((DxfCode)data[i].TypeCode)) + acIndex.Add(i); } - if (appNameIndex == -1) - return; - if (appNameIndexNext == -1) - appNameIndexNext = data.Count; + //当前app索引 + //if (appNameIndex == -1) + //return; - action?.Invoke(appNameIndex, appNameIndexNext); + //下一个app索引,如果是空,就为末尾 + //if (appNameIndexNext == -1) + // appNameIndexNext = data.Count; + + return acIndex; } /// @@ -59,14 +69,15 @@ public static void RemoveXData(this DBObject obj, string appName, DxfCode dxfCod //测试命令 addxdata removexdata //移除指定App的扩展 - GetAppIndex(data, appName, (appNameIndex, appNameIndexNext) => { - for (int i = appNameIndexNext - 1; i >= appNameIndex; i--) - if (data[i].TypeCode == ((short)dxfCode)) - data.RemoveAt(i); - - using (obj.ForWrite()) - obj.XData = data; - }); + var indexs = GetXdataAppIndex(data, appName, new DxfCode[] { dxfCode }); + if (indexs.Count == 0) + return; + + for (int i = indexs.Count - 1; i >= 0; i--) + data.RemoveAt(indexs[i]); + + using (obj.ForWrite()) + obj.XData = data; } /// /// 修改扩展数据 @@ -81,14 +92,15 @@ public static void ChangeXData(this DBObject obj, string appName, DxfCode dxfCod return; XDataList data = obj.XData; - GetAppIndex(data, appName, (appNameIndex, appNameIndexNext) => { - for (int i = appNameIndexNext - 1; i >= appNameIndex; i--) - if (data[i].TypeCode == (short)dxfCode) - data[i] = new TypedValue((short)dxfCode, newvalue); + var indexs = GetXdataAppIndex(data, appName, new DxfCode[] { dxfCode }); + if (indexs.Count == 0) + return; + + for (int i = indexs.Count - 1; i >= 0; i--) + data[i] = new TypedValue((short)dxfCode, newvalue); - using (obj.ForWrite()) - obj.XData = data; - }); + using (obj.ForWrite()) + obj.XData = data; } #endregion diff --git a/tests/TestShared/Test.cs b/tests/TestShared/Test.cs index e2aa9d8..5ead798 100644 --- a/tests/TestShared/Test.cs +++ b/tests/TestShared/Test.cs @@ -212,7 +212,7 @@ public void AddXdata() { DxfCode.ExtendedDataAsciiString, "xxxxxxx" }, {1070, 12 }, { DxfCode.ExtendedDataRegAppName, appname }, //可以用dxfcode和int表示组码,移除中间的测试 - { DxfCode.ExtendedDataAsciiString, "hahhahah" }, + { DxfCode.ExtendedDataAsciiString, "要移除的我" }, {1070, 12 }, { DxfCode.ExtendedDataRegAppName, "myapp3" }, //可以用dxfcode和int表示组码 { DxfCode.ExtendedDataAsciiString, "aaaaaaaaa" }, -- Gitee From e356b911340ae1d0672e02944fbb85e2b8a5b605 Mon Sep 17 00:00:00 2001 From: liuqihong <540762622@qq.com> Date: Thu, 25 Aug 2022 15:05:57 +0800 Subject: [PATCH 437/675] =?UTF-8?q?=E4=BF=AE=E6=94=B9=E6=B8=85=E7=90=86?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/IFoxCAD.Cad.Shared/ExtensionMethod/XrefEx.cs | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/src/IFoxCAD.Cad.Shared/ExtensionMethod/XrefEx.cs b/src/IFoxCAD.Cad.Shared/ExtensionMethod/XrefEx.cs index 4324439..25657be 100644 --- a/src/IFoxCAD.Cad.Shared/ExtensionMethod/XrefEx.cs +++ b/src/IFoxCAD.Cad.Shared/ExtensionMethod/XrefEx.cs @@ -356,7 +356,7 @@ void DoubleBind() } #endif } - } + } #endregion } @@ -691,12 +691,17 @@ static void DatabasePurge(Database db, where TTable : SymbolTable where TRecord : SymbolTableRecord, new() { - var idArray = symbolTable.Select(id => id); - if (!idArray.Any()) + var ids = new ObjectIdCollection(); + symbolTable.ForEach(id => ids.Add(id)); + if (ids.Count == 0) return; - var ids = new ObjectIdCollection(idArray.ToArray()); - while (ids.Count > 0) + do + { + //Purge是过滤出能够清理的对象 db.Purge(ids); + foreach (ObjectId id in ids) + id.Erase(); + } while (ids.Count > 0); } } #endif \ No newline at end of file -- Gitee From 6f26359efc2b0556913a29fda29acfb8c5ea5bed Mon Sep 17 00:00:00 2001 From: liuqihong <540762622@qq.com> Date: Thu, 25 Aug 2022 15:20:02 +0800 Subject: [PATCH 438/675] =?UTF-8?q?=E5=A2=9E=E5=8A=A0=E6=B5=8B=E8=AF=95?= =?UTF-8?q?=E5=A4=84=E7=90=86?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/IFoxCAD.LoadForm/AssemblyDependent.cs | 65 +++++++++++++++-------- 1 file changed, 43 insertions(+), 22 deletions(-) diff --git a/src/IFoxCAD.LoadForm/AssemblyDependent.cs b/src/IFoxCAD.LoadForm/AssemblyDependent.cs index 452700f..e996272 100644 --- a/src/IFoxCAD.LoadForm/AssemblyDependent.cs +++ b/src/IFoxCAD.LoadForm/AssemblyDependent.cs @@ -3,12 +3,6 @@ namespace IFoxCAD.LoadEx; -/* - * 因为此处引用了 nuget的 Lib.Harmony - * 所以单独分一个工程出来作为cad工程的引用 - * 免得污染了cad工程的纯洁 - */ - #if HarmonyPatch_1 [HarmonyPatch("Autodesk.AutoCAD.ApplicationServices.ExtensionLoader", "OnAssemblyLoad")] #endif @@ -55,8 +49,8 @@ public AssemblyDependent() /// dll的文件位置 /// 返回加载链 /// true字节加载,false文件加载 - /// 参数 加载成功标识 - /// 链条后面的不再理会,因为相同的dll引用辨识无意义 + /// 参数 是否加载成功 + /// 链条后面的不再理会,因为相同的dll引用辨识无意义 /// public bool Load(string? dllFullName, List loadStates, bool byteLoad = true) { @@ -99,6 +93,18 @@ public bool Load(string? dllFullName, List loadStates, bool byteLoad try { var ass = GetPdbAssembly(allRef); + +#if Debug_WriteLine_null + if (ass == null) + System.Diagnostics.Debug + .WriteLine($"****{nameof(Load)}:此文件无加载了pdb对象:" + allRef); +#endif + +#if Debug_WriteLine_notnull + if (ass != null) + System.Diagnostics.Debug + .WriteLine($"****{nameof(Load)}:此文件加载了pdb对象:" + allRef); +#endif if (ass == null) if (byteLoad) ass = Assembly.Load(File.ReadAllBytes(allRef)); @@ -251,10 +257,12 @@ void GetAllRefPaths(Assembly[] cadAssembly, return assemblyAsRef; } + /// /// 加载信息 /// - public static string? PrintMessage(List loadStates) + public static string? PrintMessage(List loadStates, + PrintModes modes = PrintModes.All) { if (loadStates == null) return null; @@ -263,26 +271,33 @@ void GetAllRefPaths(Assembly[] cadAssembly, var ok = loadStates.FindAll(a => a.State); var no = loadStates.FindAll(a => !a.State); - if (ok.Count != 0) + if ((modes & PrintModes.Yes) == PrintModes.Yes) { - sb.Append("** 这些文件加载成功!"); - foreach (var item in ok) + if (ok.Count != 0) { + sb.Append("** 这些文件加载成功!"); + foreach (var item in ok) + { + sb.Append(Environment.NewLine); + sb.Append("++ "); + sb.Append(item.DllFullName); + } + sb.Append(Environment.NewLine); sb.Append(Environment.NewLine); - sb.Append("++ "); - sb.Append(item.DllFullName); } - sb.Append(Environment.NewLine); - sb.Append(Environment.NewLine); } - if (no.Count != 0) + + if ((modes & PrintModes.No) == PrintModes.No) { - sb.Append("** 这些文件已被加载过,同时重复名称和版本号,跳过!"); - foreach (var item in no) + if (no.Count != 0) { - sb.Append(Environment.NewLine); - sb.Append("-- "); - sb.Append(item.DllFullName); + sb.Append("** 这些文件已被加载过,同时重复名称和版本号,跳过!"); + foreach (var item in no) + { + sb.Append(Environment.NewLine); + sb.Append("-- "); + sb.Append(item.DllFullName); + } } } return sb.ToString(); @@ -395,6 +410,12 @@ protected virtual void Dispose(bool ing) #endregion } +public enum PrintModes +{ + Yes = 1, + No = 2, + All = Yes | No, +} /// /// 加载程序集和加载状态 -- Gitee From e50b954d8149bbdc4aa4a4719c1c647293f35346 Mon Sep 17 00:00:00 2001 From: liuqihong <540762622@qq.com> Date: Thu, 25 Aug 2022 15:48:57 +0800 Subject: [PATCH 439/675] =?UTF-8?q?=E6=B5=8B=E8=AF=95=E5=88=A0=E9=99=A4?= =?UTF-8?q?=E6=88=90=E5=8A=9F=5FDIE?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../ExtensionMethod/Enums.cs | 12 +------ .../ExtensionMethod/XrefEx.cs | 36 ++++++++----------- 2 files changed, 16 insertions(+), 32 deletions(-) diff --git a/src/IFoxCAD.Cad.Shared/ExtensionMethod/Enums.cs b/src/IFoxCAD.Cad.Shared/ExtensionMethod/Enums.cs index 3401c1c..9f9f7c3 100644 --- a/src/IFoxCAD.Cad.Shared/ExtensionMethod/Enums.cs +++ b/src/IFoxCAD.Cad.Shared/ExtensionMethod/Enums.cs @@ -97,17 +97,7 @@ public enum SymModes : ushort Option2 = UcsTable | ViewTable | ViewportTable, //全部 - All = BlockTable | Option1 | Option2, - - //清理 - Purge = - BlockTable | - DimStyleTable | - LayerTable | - LinetypeTable | - TextStyleTable | - ViewportTable | - RegAppTable, + All = BlockTable | Option1 | Option2 } diff --git a/src/IFoxCAD.Cad.Shared/ExtensionMethod/XrefEx.cs b/src/IFoxCAD.Cad.Shared/ExtensionMethod/XrefEx.cs index 25657be..919faa6 100644 --- a/src/IFoxCAD.Cad.Shared/ExtensionMethod/XrefEx.cs +++ b/src/IFoxCAD.Cad.Shared/ExtensionMethod/XrefEx.cs @@ -661,32 +661,29 @@ public static class DBTransEx /// /// /// - public static void Purge(this DBTrans tr, SymModes sym = SymModes.Purge) + public static void Purge(this DBTrans tr, SymModes sym = SymModes.All) { - var db = tr.Database; if ((sym & SymModes.BlockTable) == SymModes.BlockTable) - DatabasePurge(db, tr.BlockTable); + DatabasePurge(tr, tr.BlockTable); if ((sym & SymModes.DimStyleTable) == SymModes.DimStyleTable) - DatabasePurge(db, tr.DimStyleTable); + DatabasePurge(tr, tr.DimStyleTable); if ((sym & SymModes.LayerTable) == SymModes.LayerTable) - DatabasePurge(db, tr.LayerTable); + DatabasePurge(tr, tr.LayerTable); if ((sym & SymModes.LinetypeTable) == SymModes.LinetypeTable) - DatabasePurge(db, tr.LinetypeTable); + DatabasePurge(tr, tr.LinetypeTable); if ((sym & SymModes.TextStyleTable) == SymModes.TextStyleTable) - DatabasePurge(db, tr.TextStyleTable); + DatabasePurge(tr, tr.TextStyleTable); if ((sym & SymModes.ViewportTable) == SymModes.ViewportTable) - DatabasePurge(db, tr.ViewportTable); + DatabasePurge(tr, tr.ViewportTable); if ((sym & SymModes.RegAppTable) == SymModes.RegAppTable) - DatabasePurge(db, tr.RegAppTable); - - //以下不能这样清理,不然有异常 + DatabasePurge(tr, tr.RegAppTable); if ((sym & SymModes.ViewTable) == SymModes.ViewTable) - DatabasePurge(db, tr.ViewTable); + DatabasePurge(tr, tr.ViewTable); if ((sym & SymModes.UcsTable) == SymModes.UcsTable) - DatabasePurge(db, tr.UcsTable); + DatabasePurge(tr, tr.UcsTable); } - static void DatabasePurge(Database db, + static void DatabasePurge(DBTrans tr, SymbolTable symbolTable) where TTable : SymbolTable where TRecord : SymbolTableRecord, new() @@ -695,13 +692,10 @@ static void DatabasePurge(Database db, symbolTable.ForEach(id => ids.Add(id)); if (ids.Count == 0) return; - do - { - //Purge是过滤出能够清理的对象 - db.Purge(ids); - foreach (ObjectId id in ids) - id.Erase(); - } while (ids.Count > 0); + //Purge是查询能够清理的对象 + tr.Database.Purge(ids); + foreach (ObjectId id in ids) + id.Erase(); } } #endif \ No newline at end of file -- Gitee From fc099dfaa230b91dab0898ea9d11e528d0f6f956 Mon Sep 17 00:00:00 2001 From: liuqihong <540762622@qq.com> Date: Thu, 25 Aug 2022 16:03:50 +0800 Subject: [PATCH 440/675] =?UTF-8?q?=E4=BF=AE=E6=94=B9=E4=B8=BA=E5=BE=AA?= =?UTF-8?q?=E7=8E=AF=E6=B8=85=E7=90=86?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/IFoxCAD.Cad.Shared/ExtensionMethod/XrefEx.cs | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/src/IFoxCAD.Cad.Shared/ExtensionMethod/XrefEx.cs b/src/IFoxCAD.Cad.Shared/ExtensionMethod/XrefEx.cs index 919faa6..eedbeda 100644 --- a/src/IFoxCAD.Cad.Shared/ExtensionMethod/XrefEx.cs +++ b/src/IFoxCAD.Cad.Shared/ExtensionMethod/XrefEx.cs @@ -690,12 +690,13 @@ static void DatabasePurge(DBTrans tr, { var ids = new ObjectIdCollection(); symbolTable.ForEach(id => ids.Add(id)); - if (ids.Count == 0) - return; - //Purge是查询能够清理的对象 - tr.Database.Purge(ids); - foreach (ObjectId id in ids) - id.Erase(); + while (ids.Count > 0) + { + //Purge是查询能够清理的对象 + tr.Database.Purge(ids); + foreach (ObjectId id in ids) + id.Erase(); + } } } #endif \ No newline at end of file -- Gitee From fdf2537d30f9f8431df42c4ce286aaed8ccc639d Mon Sep 17 00:00:00 2001 From: liuqihong <540762622@qq.com> Date: Thu, 25 Aug 2022 18:48:40 +0800 Subject: [PATCH 441/675] =?UTF-8?q?=E6=B8=85=E7=90=86=E7=9A=84=E4=BF=AE?= =?UTF-8?q?=E6=94=B9=5F=E5=B0=9A=E6=9C=AA=E6=B5=8B=E8=AF=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../ExtensionMethod/XrefEx.cs | 70 +++++++++++-------- 1 file changed, 41 insertions(+), 29 deletions(-) diff --git a/src/IFoxCAD.Cad.Shared/ExtensionMethod/XrefEx.cs b/src/IFoxCAD.Cad.Shared/ExtensionMethod/XrefEx.cs index eedbeda..5bea91c 100644 --- a/src/IFoxCAD.Cad.Shared/ExtensionMethod/XrefEx.cs +++ b/src/IFoxCAD.Cad.Shared/ExtensionMethod/XrefEx.cs @@ -662,41 +662,53 @@ public static class DBTransEx /// /// public static void Purge(this DBTrans tr, SymModes sym = SymModes.All) - { - if ((sym & SymModes.BlockTable) == SymModes.BlockTable) - DatabasePurge(tr, tr.BlockTable); - if ((sym & SymModes.DimStyleTable) == SymModes.DimStyleTable) - DatabasePurge(tr, tr.DimStyleTable); - if ((sym & SymModes.LayerTable) == SymModes.LayerTable) - DatabasePurge(tr, tr.LayerTable); - if ((sym & SymModes.LinetypeTable) == SymModes.LinetypeTable) - DatabasePurge(tr, tr.LinetypeTable); - if ((sym & SymModes.TextStyleTable) == SymModes.TextStyleTable) - DatabasePurge(tr, tr.TextStyleTable); - if ((sym & SymModes.ViewportTable) == SymModes.ViewportTable) - DatabasePurge(tr, tr.ViewportTable); - if ((sym & SymModes.RegAppTable) == SymModes.RegAppTable) - DatabasePurge(tr, tr.RegAppTable); - if ((sym & SymModes.ViewTable) == SymModes.ViewTable) - DatabasePurge(tr, tr.ViewTable); - if ((sym & SymModes.UcsTable) == SymModes.UcsTable) - DatabasePurge(tr, tr.UcsTable); - } - - static void DatabasePurge(DBTrans tr, - SymbolTable symbolTable) - where TTable : SymbolTable - where TRecord : SymbolTableRecord, new() { var ids = new ObjectIdCollection(); - symbolTable.ForEach(id => ids.Add(id)); - while (ids.Count > 0) + var db = tr.Database; + + //0x01 + //db.Purge(ids)是获取未硬引用(无引用?)的对象,也就可以删除的. + //0x02 + //如果一个图元引用一个图层, + //假设这个图元是可以删除的(实际上它可能来自于词典记录的id) => 那么它被 db.Purge(ids) 识别, + //但是这个图层因为有硬引用,所以不被 db.Purge(ids) 识别, + //只能删除图元之后,循环第二次再通过 db.Purge(ids) 获取图层id. + + do { + if ((sym & SymModes.BlockTable) == SymModes.BlockTable) + GetIds(ids, tr.BlockTable); + if ((sym & SymModes.DimStyleTable) == SymModes.DimStyleTable) + GetIds(ids, tr.DimStyleTable); + if ((sym & SymModes.LayerTable) == SymModes.LayerTable) + GetIds(ids, tr.LayerTable); + if ((sym & SymModes.LinetypeTable) == SymModes.LinetypeTable) + GetIds(ids, tr.LinetypeTable); + if ((sym & SymModes.TextStyleTable) == SymModes.TextStyleTable) + GetIds(ids, tr.TextStyleTable); + if ((sym & SymModes.ViewportTable) == SymModes.ViewportTable) + GetIds(ids, tr.ViewportTable); + if ((sym & SymModes.RegAppTable) == SymModes.RegAppTable) + GetIds(ids, tr.RegAppTable); + if ((sym & SymModes.ViewTable) == SymModes.ViewTable) + GetIds(ids, tr.ViewTable); + if ((sym & SymModes.UcsTable) == SymModes.UcsTable) + GetIds(ids, tr.UcsTable); + //Purge是查询能够清理的对象 - tr.Database.Purge(ids); + db.Purge(ids); foreach (ObjectId id in ids) id.Erase(); - } + + } while (ids.Count > 0); + } + + static void GetIds(ObjectIdCollection ids, + SymbolTable symbolTable) + where TTable : SymbolTable + where TRecord : SymbolTableRecord, new() + { + symbolTable.ForEach(id => ids.Add(id)); } } #endif \ No newline at end of file -- Gitee From 089340c31d7adef42be9791ecb3fd2252262e2b1 Mon Sep 17 00:00:00 2001 From: liuqihong <540762622@qq.com> Date: Thu, 25 Aug 2022 18:51:20 +0800 Subject: [PATCH 442/675] =?UTF-8?q?=E5=A2=9E=E5=8A=A0=E6=B3=A8=E9=87=8A=5F?= =?UTF-8?q?=E5=B0=9A=E6=9C=AA=E6=B5=8B=E8=AF=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/IFoxCAD.Cad.Shared/ExtensionMethod/XrefEx.cs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/IFoxCAD.Cad.Shared/ExtensionMethod/XrefEx.cs b/src/IFoxCAD.Cad.Shared/ExtensionMethod/XrefEx.cs index 5bea91c..8fe49c3 100644 --- a/src/IFoxCAD.Cad.Shared/ExtensionMethod/XrefEx.cs +++ b/src/IFoxCAD.Cad.Shared/ExtensionMethod/XrefEx.cs @@ -673,6 +673,8 @@ public static void Purge(this DBTrans tr, SymModes sym = SymModes.All) //假设这个图元是可以删除的(实际上它可能来自于词典记录的id) => 那么它被 db.Purge(ids) 识别, //但是这个图层因为有硬引用,所以不被 db.Purge(ids) 识别, //只能删除图元之后,循环第二次再通过 db.Purge(ids) 获取图层id. + //0x03 + //因为删除之后,符号表内的引用可能被修改,因此需要重复遍历符号表. do { @@ -690,6 +692,7 @@ public static void Purge(this DBTrans tr, SymModes sym = SymModes.All) GetIds(ids, tr.ViewportTable); if ((sym & SymModes.RegAppTable) == SymModes.RegAppTable) GetIds(ids, tr.RegAppTable); + //SHUN007 说这两个表可能有错误 if ((sym & SymModes.ViewTable) == SymModes.ViewTable) GetIds(ids, tr.ViewTable); if ((sym & SymModes.UcsTable) == SymModes.UcsTable) -- Gitee From 11fa75100b89c61f9118c477b7eb68457e29d463 Mon Sep 17 00:00:00 2001 From: liuqihong <540762622@qq.com> Date: Thu, 25 Aug 2022 19:05:53 +0800 Subject: [PATCH 443/675] =?UTF-8?q?=E4=BF=AE=E6=94=B9=E4=BA=86=E6=B3=A8?= =?UTF-8?q?=E9=87=8A?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/IFoxCAD.Cad.Shared/Runtime/DBTrans.cs | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/src/IFoxCAD.Cad.Shared/Runtime/DBTrans.cs b/src/IFoxCAD.Cad.Shared/Runtime/DBTrans.cs index fc80105..16530e1 100644 --- a/src/IFoxCAD.Cad.Shared/Runtime/DBTrans.cs +++ b/src/IFoxCAD.Cad.Shared/Runtime/DBTrans.cs @@ -398,13 +398,16 @@ public void SaveDwgFile(DwgVersion version = DwgVersion.AC1800) /// - /// 自动进行前后台分开处理的任务 + /// 前台后台任务分别处理 /// /// /// 备注:
    - /// 0x01 此方案利用前台数据库进行处理
    - /// 0x02 此问题主要出现是这个线性引擎上面,在参照/深度克隆的底层共用此技术,导致单行文字偏移
    - /// 0x03 异常: 前台绑定的时候不能用它,否则出现:
    + /// + /// 0x01 文字偏移问题主要出现是这个线性引擎上面, + /// 在 参照绑定/深度克隆 的底层共用此技术导致问题发生 + /// 0x02 后台是利用前台当前数据库进行处理的 + /// 0x03 跨进程通讯暂无测试(可能存在bug) + /// ///
    /// 委托 /// 开启单行文字偏移处理 @@ -430,6 +433,8 @@ public void Task(Action action, bool handlingDBTextDeviation = true) } //处理单行文字偏移 + //前台绑定参照的时候不能用它,否则出现:
    + //所以本函数自动识别前后台做处理 HostApplicationServices.WorkingDatabase = Database; action.Invoke(); HostApplicationServices.WorkingDatabase = dbBak; -- Gitee From aee5a512088640b086900ceb9a71a47f376ece4f Mon Sep 17 00:00:00 2001 From: liuqihong <540762622@qq.com> Date: Fri, 26 Aug 2022 11:09:22 +0800 Subject: [PATCH 444/675] =?UTF-8?q?=E6=B8=85=E7=90=86=E4=BC=9A=E5=BC=95?= =?UTF-8?q?=E8=B5=B7=E6=AD=BB=E5=BE=AA=E7=8E=AF=E6=89=80=E4=BB=A5=E5=88=A0?= =?UTF-8?q?=E9=99=A4=E5=AE=83=E5=8F=AA=E7=95=99=E4=B8=8B=E4=B8=80=E6=AC=A1?= =?UTF-8?q?=E6=B8=85=E7=90=86?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../ExtensionMethod/XrefEx.cs | 56 +++++++++---------- 1 file changed, 27 insertions(+), 29 deletions(-) diff --git a/src/IFoxCAD.Cad.Shared/ExtensionMethod/XrefEx.cs b/src/IFoxCAD.Cad.Shared/ExtensionMethod/XrefEx.cs index 8fe49c3..2d40750 100644 --- a/src/IFoxCAD.Cad.Shared/ExtensionMethod/XrefEx.cs +++ b/src/IFoxCAD.Cad.Shared/ExtensionMethod/XrefEx.cs @@ -675,35 +675,33 @@ public static void Purge(this DBTrans tr, SymModes sym = SymModes.All) //只能删除图元之后,循环第二次再通过 db.Purge(ids) 获取图层id. //0x03 //因为删除之后,符号表内的引用可能被修改,因此需要重复遍历符号表. - - do - { - if ((sym & SymModes.BlockTable) == SymModes.BlockTable) - GetIds(ids, tr.BlockTable); - if ((sym & SymModes.DimStyleTable) == SymModes.DimStyleTable) - GetIds(ids, tr.DimStyleTable); - if ((sym & SymModes.LayerTable) == SymModes.LayerTable) - GetIds(ids, tr.LayerTable); - if ((sym & SymModes.LinetypeTable) == SymModes.LinetypeTable) - GetIds(ids, tr.LinetypeTable); - if ((sym & SymModes.TextStyleTable) == SymModes.TextStyleTable) - GetIds(ids, tr.TextStyleTable); - if ((sym & SymModes.ViewportTable) == SymModes.ViewportTable) - GetIds(ids, tr.ViewportTable); - if ((sym & SymModes.RegAppTable) == SymModes.RegAppTable) - GetIds(ids, tr.RegAppTable); - //SHUN007 说这两个表可能有错误 - if ((sym & SymModes.ViewTable) == SymModes.ViewTable) - GetIds(ids, tr.ViewTable); - if ((sym & SymModes.UcsTable) == SymModes.UcsTable) - GetIds(ids, tr.UcsTable); - - //Purge是查询能够清理的对象 - db.Purge(ids); - foreach (ObjectId id in ids) - id.Erase(); - - } while (ids.Count > 0); + //0x04 + //试试循环事务 + + if ((sym & SymModes.BlockTable) == SymModes.BlockTable) + GetIds(ids, tr.BlockTable); + if ((sym & SymModes.DimStyleTable) == SymModes.DimStyleTable) + GetIds(ids, tr.DimStyleTable); + if ((sym & SymModes.LayerTable) == SymModes.LayerTable) + GetIds(ids, tr.LayerTable); + if ((sym & SymModes.LinetypeTable) == SymModes.LinetypeTable) + GetIds(ids, tr.LinetypeTable); + if ((sym & SymModes.TextStyleTable) == SymModes.TextStyleTable) + GetIds(ids, tr.TextStyleTable); + if ((sym & SymModes.ViewportTable) == SymModes.ViewportTable) + GetIds(ids, tr.ViewportTable); + if ((sym & SymModes.RegAppTable) == SymModes.RegAppTable) + GetIds(ids, tr.RegAppTable); + //SHUN007 说这两个表可能有错误 + if ((sym & SymModes.ViewTable) == SymModes.ViewTable) + GetIds(ids, tr.ViewTable); + if ((sym & SymModes.UcsTable) == SymModes.UcsTable) + GetIds(ids, tr.UcsTable); + + //Purge是查询能够清理的对象 + db.Purge(ids); + foreach (ObjectId id in ids) + id.Erase(); } static void GetIds(ObjectIdCollection ids, -- Gitee From 4a88df4c5b5d0ad966c482ff06e4c203ec8dff28 Mon Sep 17 00:00:00 2001 From: liuqihong <540762622@qq.com> Date: Fri, 26 Aug 2022 23:56:08 +0800 Subject: [PATCH 445/675] =?UTF-8?q?=E6=9B=B4=E6=94=B9=E8=A7=84=E8=8C=83?= =?UTF-8?q?=E5=A4=84=E7=90=86?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- ...344\273\243\347\240\201\350\247\204\350\214\203.md" | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git "a/docs/0x01\344\273\243\347\240\201\350\247\204\350\214\203.md" "b/docs/0x01\344\273\243\347\240\201\350\247\204\350\214\203.md" index 7393d64..b19302b 100644 --- "a/docs/0x01\344\273\243\347\240\201\350\247\204\350\214\203.md" +++ "b/docs/0x01\344\273\243\347\240\201\350\247\204\350\214\203.md" @@ -52,13 +52,13 @@ else ![img](0x01%E4%BB%A3%E7%A0%81%E8%A7%84%E8%8C%83.assets/2HJE@WH1%60PPUBOH2ZFL$BT.png) -写代码的时候总是要把业务代码独立成一个逻辑处理.这样主要是为了防止业务耦合: - 例如: for {业务1,业务2,业务1,业务2....} -如果有这样的逻辑,那么我们看代码的时候总是认为业务2某种条件必须要跟着业务1 -优化代码的人一看:这代码就不能动了!! -相信我,若干年后的你就是这个优化代码的人. +上述代码中出现了这种情形: for {业务1,业务2,业务1,业务2....} +如果有这样的逻辑,那么我们看代码的时候总是认为业务2某种条件必须要跟着业务1. +优化代码的人一看:这代码就不能动了!! 相信我,若干年后的你就是这个优化代码的人. + +所以这样的情况下,我们采用的是用个`List`收集拆离(业务)的id,然后在最后进行循环拆离(业务). ### 0x03 .editorconfig 配置要求 -- Gitee From 5dd0389e09b5feb533691d003ec70412d214acf8 Mon Sep 17 00:00:00 2001 From: liuqihong <540762622@qq.com> Date: Sat, 27 Aug 2022 02:30:12 +0800 Subject: [PATCH 446/675] =?UTF-8?q?=E5=88=A0=E9=99=A4=E5=A4=9A=E4=BD=99?= =?UTF-8?q?=E6=96=87=E4=BB=B6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../IFoxCAD - Backup.Acad08.csproj | 56 ------------------- 1 file changed, 56 deletions(-) delete mode 100644 src/IFoxCAD.Cad/IFoxCAD.Aacad08/IFoxCAD - Backup.Acad08.csproj diff --git a/src/IFoxCAD.Cad/IFoxCAD.Aacad08/IFoxCAD - Backup.Acad08.csproj b/src/IFoxCAD.Cad/IFoxCAD.Aacad08/IFoxCAD - Backup.Acad08.csproj deleted file mode 100644 index ced3eed..0000000 --- a/src/IFoxCAD.Cad/IFoxCAD.Aacad08/IFoxCAD - Backup.Acad08.csproj +++ /dev/null @@ -1,56 +0,0 @@ - - - - NET35 - true - enable - true - true - 0.3.7 - InspireFunction - xsfhlzh;vicwjb;liuqihong - 基于.NET的Cad二次开发类库 - InspireFunction - https://gitee.com/inspirefunction/ifoxcad - https://gitee.com/inspirefunction/ifoxcad.git - git - IFoxCAD;CAD;AutoCad;C#;NET - 增加很多东西. - true - true - preview - true - LICENSE - true - - - - - - - - - - DEBUG - - - $(Configuration);ac2008;ac2009 - - - - - True - - - - - - - - - - - - -- Gitee From 3ae9127fcf18c9f57ebf545ffcab378eeafbd430 Mon Sep 17 00:00:00 2001 From: liuqihong <540762622@qq.com> Date: Sat, 27 Aug 2022 15:25:45 +0800 Subject: [PATCH 447/675] =?UTF-8?q?=E4=BF=AE=E6=94=B9=E8=87=AA=E5=8A=A8?= =?UTF-8?q?=E6=89=A7=E8=A1=8C=E6=8E=A5=E5=8F=A3=E7=9A=84=E8=8C=83=E4=BE=8B?= =?UTF-8?q?=E9=94=99=E8=AF=AF?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- tests/TestShared/CmdINI.cs | 36 +++++++++++++++++++++++++----------- 1 file changed, 25 insertions(+), 11 deletions(-) diff --git a/tests/TestShared/CmdINI.cs b/tests/TestShared/CmdINI.cs index 68956a0..3e94042 100644 --- a/tests/TestShared/CmdINI.cs +++ b/tests/TestShared/CmdINI.cs @@ -8,18 +8,32 @@ /// 1:构造函数
    /// 2:特性..多个
    /// 3:接口..多个
    -/// 4:本类的构造函数 -/// +/// 4:本类的构造函数
    +/// +/// **** 警告 **** +/// 如果不写一个 储存这个对象, +/// 而是直接写卸载命令在此, +/// 第一次加载的时候会初始化完成,然后这个类生命就结束了, +/// 第二次通过命令进入,会引发构造函数再次执行,留意构造函数的打印信息即可发现 +/// +/// ///
    -public class CmdINI : AutoRegAssem +public class CmdInit : AutoRegAssem { - public CmdINI() : base(AutoRegConfig.All) + public CmdInit() : base(AutoRegConfig.All) { var dm = Acap.DocumentManager; var doc = dm.MdiActiveDocument; var ed = doc.Editor; - ed.WriteMessage($"\n {nameof(CmdINI)}构造函数,开始自动执行\r\n"); + ed.WriteMessage($"\n {nameof(CmdInit)}构造函数,开始自动执行\r\n"); + + CmdInitEx.CmdInit = this; } +} + +public class CmdInitEx +{ + public static CmdInit? CmdInit; ///如果netload之后用 删除注册表, ///由于不是也不能卸载dll,再netload是无法执行自动接口的, @@ -27,12 +41,12 @@ public CmdINI() : base(AutoRegConfig.All) [CommandMethod("IFoxAddReg")] public void IFoxAddReg() { - base.RegApp(); - var dm = Acap.DocumentManager; var doc = dm.MdiActiveDocument; var ed = doc.Editor; ed.WriteMessage($"\n 加入注册表"); + + CmdInit?.RegApp(); } /// @@ -40,14 +54,14 @@ public void IFoxAddReg() /// [CommandMethod("IFoxRemoveReg")] public void IFoxRemoveReg() - { - //执行命令的时候会再次执行构造函数(导致初始化两次),但是再次执行就不会了 - base.UnRegApp(); - + { var dm = Acap.DocumentManager; var doc = dm.MdiActiveDocument; var ed = doc.Editor; ed.WriteMessage($"\n 卸载注册表"); + + //执行命令的时候会再次执行构造函数(导致初始化两次),但是再次执行就不会了 + CmdInit?.UnRegApp(); } } -- Gitee From 1ef3e0a3918f24f3754e1bdc986bb6322f731e62 Mon Sep 17 00:00:00 2001 From: liuqihong <540762622@qq.com> Date: Sat, 27 Aug 2022 15:33:22 +0800 Subject: [PATCH 448/675] =?UTF-8?q?=E4=BF=AE=E6=94=B9=E6=B3=A8=E9=87=8A?= =?UTF-8?q?=E5=92=8C=E7=B1=BB=E5=90=8D?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- tests/TestShared/CmdINI.cs | 24 +++++++++++++----------- 1 file changed, 13 insertions(+), 11 deletions(-) diff --git a/tests/TestShared/CmdINI.cs b/tests/TestShared/CmdINI.cs index 3e94042..02bac75 100644 --- a/tests/TestShared/CmdINI.cs +++ b/tests/TestShared/CmdINI.cs @@ -2,8 +2,10 @@ /// /// 注册中心(自动执行接口): -/// 用于启动cad后写入启动注册表及反射调用以下特性和接口
    -/// netload的工程必须继承虚函数后才能使用特性和接口
    +/// +/// 继承虚函数后才能使用
    +/// 0x01 netload加载之后自动执行,写入启动注册表,下次就不需要netload了
    +/// 0x02 反射调用特性和接口
    /// 启动cad后的执行顺序为:
    /// 1:构造函数
    /// 2:特性..多个
    @@ -11,29 +13,29 @@ /// 4:本类的构造函数
    /// /// **** 警告 **** -/// 如果不写一个 储存这个对象, +/// 如果不写一个 储存这个对象, /// 而是直接写卸载命令在此, /// 第一次加载的时候会初始化完成,然后这个类生命就结束了, /// 第二次通过命令进入,会引发构造函数再次执行,留意构造函数的打印信息即可发现 /// ///
    ///
    -public class CmdInit : AutoRegAssem +public class AutoRegAssemEx : AutoRegAssem { - public CmdInit() : base(AutoRegConfig.All) + public AutoRegAssemEx() : base(AutoRegConfig.All) { var dm = Acap.DocumentManager; var doc = dm.MdiActiveDocument; var ed = doc.Editor; - ed.WriteMessage($"\n {nameof(CmdInit)}构造函数,开始自动执行\r\n"); + ed.WriteMessage($"\n {nameof(AutoRegAssemEx)}构造函数,开始自动执行\r\n"); - CmdInitEx.CmdInit = this; + CmdInit.AutoRegAssemEx = this; } } -public class CmdInitEx +public class CmdInit { - public static CmdInit? CmdInit; + public static AutoRegAssemEx? AutoRegAssemEx; ///如果netload之后用 删除注册表, ///由于不是也不能卸载dll,再netload是无法执行自动接口的, @@ -46,7 +48,7 @@ public void IFoxAddReg() var ed = doc.Editor; ed.WriteMessage($"\n 加入注册表"); - CmdInit?.RegApp(); + AutoRegAssemEx?.RegApp(); } /// @@ -61,7 +63,7 @@ public void IFoxRemoveReg() ed.WriteMessage($"\n 卸载注册表"); //执行命令的时候会再次执行构造函数(导致初始化两次),但是再次执行就不会了 - CmdInit?.UnRegApp(); + AutoRegAssemEx?.UnRegApp(); } } -- Gitee From 10b7ae625d0565a48e0f1509fb4a055cc057713d Mon Sep 17 00:00:00 2001 From: liuqihong <540762622@qq.com> Date: Sat, 27 Aug 2022 21:29:20 +0800 Subject: [PATCH 449/675] =?UTF-8?q?sln=E4=B8=AD=E6=98=BE=E7=A4=BAreadme?= =?UTF-8?q?=E7=AD=89=E6=96=87=E4=BB=B6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- IFoxCAD.sln | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/IFoxCAD.sln b/IFoxCAD.sln index 8dc6df1..c64fd36 100644 --- a/IFoxCAD.sln +++ b/IFoxCAD.sln @@ -3,6 +3,15 @@ Microsoft Visual Studio Solution File, Format Version 12.00 # Visual Studio Version 17 VisualStudioVersion = 17.1.32113.165 MinimumVisualStudioVersion = 10.0.40219.1 +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{786E7347-B116-4F26-9AEF-33EB0AB88D58}" + ProjectSection(SolutionItems) = preProject + .editorconfig = .editorconfig + .gitattributes = .gitattributes + .gitignore = .gitignore + LICENSE = LICENSE + README.md = README.md + EndProjectSection +EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "IFoxCAD.WPF", "src\IFoxCAD.WPF\IFoxCAD.WPF.csproj", "{D820D629-1AB3-4BE3-A772-CB753D98CCB8}" EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "IFoxCAD.Basal", "src\IFoxCAD.Basal\IFoxCAD.Basal.csproj", "{40BF07C4-100A-4810-A27B-4587CFB38E6E}" -- Gitee From d9d070aa3c248aa8e5ca4940c64218ff6997366a Mon Sep 17 00:00:00 2001 From: liuqihong <540762622@qq.com> Date: Sun, 28 Aug 2022 05:46:47 +0800 Subject: [PATCH 450/675] =?UTF-8?q?=E7=BB=9F=E4=B8=80=E6=89=80=E6=9C=89?= =?UTF-8?q?=E7=9A=84dispose=E6=B3=A8=E9=87=8A?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/IFoxCAD.Cad.Shared/Runtime/DBTrans.cs | 55 ++++++++++--------- .../Runtime/MethodInfoHelper.cs | 2 +- src/IFoxCAD.LoadForm/AssemblyDependent.cs | 23 +++----- 3 files changed, 39 insertions(+), 41 deletions(-) diff --git a/src/IFoxCAD.Cad.Shared/Runtime/DBTrans.cs b/src/IFoxCAD.Cad.Shared/Runtime/DBTrans.cs index 16530e1..bcbf650 100644 --- a/src/IFoxCAD.Cad.Shared/Runtime/DBTrans.cs +++ b/src/IFoxCAD.Cad.Shared/Runtime/DBTrans.cs @@ -16,7 +16,7 @@ public class DBTrans : IDisposable /// /// 是否释放资源 /// - private bool disposedValue; + private bool IsDisposed; /// /// 是否提交事务 /// @@ -449,6 +449,7 @@ public void Abort() { Dispose(false); } + /// /// 提交事务 /// @@ -457,57 +458,59 @@ public void Commit() Dispose(true); } + /// + /// 手动调用释放 + /// + public void Dispose() + { + Dispose(disposing: true); + GC.SuppressFinalize(this); + } + + /// + /// 析构函数调用释放 + /// + ~DBTrans() + { + Dispose(disposing: false); + } + protected virtual void Dispose(bool disposing) { /* 事务dispose流程: * 1. 根据传入的参数确定是否提交,true为提交,false为不提交 - * 2. 根据disposedValue的值确定是否重复dispose,false为首次dispose + * 2. 根据IsDisposed的值确定是否重复dispose,false为首次dispose * 3. 如果锁文档就将文档锁dispose * 4. 不管是否提交,既然进入dispose,就要将事务栈的当前事务弹出 * 注意这里的事务栈不是cad的事务管理器,而是dbtrans的事务 * 5. 清理非托管的字段 */ - if (disposedValue) - return; + //不重复释放,并设置已经释放 + if (IsDisposed) return; + IsDisposed = true; - // 释放未托管的资源(未托管的对象)并替代终结器 - // 将大型字段设置为 null - disposedValue = true; if (disposing) { - // 调用cad的事务进行提交,释放托管状态(托管对象) + //调用cad的事务进行提交,释放托管状态(托管对象) Transaction.Commit(); } else { - // 否则取消所有的修改 + //否则取消所有的修改 Transaction.Abort(); } - // 调用 cad事务的dispose进行销毁 + + //调用cad事务的dispose进行销毁 if (!Transaction.IsDisposed) Transaction.Dispose(); - // 调用文档锁dispose + //调用文档锁dispose documentLock?.Dispose(); - // 将事务栈的当前dbtrans弹栈 + //将事务栈的当前dbtrans弹栈 dBTrans.Pop(); } - - // 仅当“Dispose(bool disposing)”拥有用于释放未托管资源的代码时才替代终结器 - ~DBTrans() - { - // 不要更改此代码。请将清理代码放入“Dispose(bool disposing)”方法中 - Dispose(disposing: false); - } - - public void Dispose() - { - // 不要更改此代码。请将清理代码放入“Dispose(bool disposing)”方法中 - Dispose(disposing: true); - GC.SuppressFinalize(this); - } #endregion } diff --git a/src/IFoxCAD.Cad.Shared/Runtime/MethodInfoHelper.cs b/src/IFoxCAD.Cad.Shared/Runtime/MethodInfoHelper.cs index c64a5f8..fac263c 100644 --- a/src/IFoxCAD.Cad.Shared/Runtime/MethodInfoHelper.cs +++ b/src/IFoxCAD.Cad.Shared/Runtime/MethodInfoHelper.cs @@ -20,8 +20,8 @@ internal static class MethodInfoHelper //新函数指针进入此处 //参数数量一定要匹配,为null则参数个数不同导致报错, //参数为stirng[],则可以传入object[]代替,其他参数是否还可以实现默认构造? - var paramInfos = methodInfo.GetParameters(); var args = new List { }; + var paramInfos = methodInfo.GetParameters(); for (int i = 0; i < paramInfos.Length; i++) args.Add(null!); result = methodInfo.Invoke(null, args.ToArray());//静态调用 diff --git a/src/IFoxCAD.LoadForm/AssemblyDependent.cs b/src/IFoxCAD.LoadForm/AssemblyDependent.cs index e996272..1c512ea 100644 --- a/src/IFoxCAD.LoadForm/AssemblyDependent.cs +++ b/src/IFoxCAD.LoadForm/AssemblyDependent.cs @@ -375,35 +375,30 @@ public void DebugMoveObjFiles(string? dllFullName, Action action) #endregion #region Dispose - public bool Disposed = false; + public bool IsDisposed = false; /// - /// 显式调用Dispose方法,继承IDisposable + /// 手动调用释放 /// public void Dispose() { - //由手动释放 Dispose(true); - //通知垃圾回收机制不再调用终结器(析构器)_跑了这里就不会跑析构函数了 GC.SuppressFinalize(this); } /// - /// 析构函数,以备忘记了显式调用Dispose方法 + /// 析构函数调用释放 /// ~AssemblyDependent() { - Dispose(false); //由系统释放 + Dispose(false); } - - /// - /// 释放 - /// - /// - protected virtual void Dispose(bool ing) + + protected virtual void Dispose(bool disposing) { - if (Disposed) return; //不重复释放 - Disposed = true;//让类型知道自己已经被释放 + //不重复释放,并设置已经释放 + if (IsDisposed) return; + IsDisposed = true; CurrentDomainAssemblyResolveEvent -= AssemblyHelper.DefaultAssemblyResolve; } -- Gitee From 7e600b07ba767080d4609f2a511c4840938c76f1 Mon Sep 17 00:00:00 2001 From: liuqihong <540762622@qq.com> Date: Sun, 28 Aug 2022 23:20:40 +0000 Subject: [PATCH 451/675] =?UTF-8?q?=E9=98=B2=E6=AD=A2=E6=B3=A8=E5=86=8C?= =?UTF-8?q?=E8=A1=A8=E8=A2=AB=E5=8D=B8=E8=BD=BD=E4=B8=A4=E6=AC=A1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: liuqihong <540762622@qq.com> --- tests/TestShared/CmdINI.cs | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/tests/TestShared/CmdINI.cs b/tests/TestShared/CmdINI.cs index 02bac75..d9479ce 100644 --- a/tests/TestShared/CmdINI.cs +++ b/tests/TestShared/CmdINI.cs @@ -1,4 +1,4 @@ -namespace Test; +namespace Test; /// /// 注册中心(自动执行接口): @@ -48,7 +48,9 @@ public void IFoxAddReg() var ed = doc.Editor; ed.WriteMessage($"\n 加入注册表"); - AutoRegAssemEx?.RegApp(); + if (AutoRegAssemEx is null) + AutoRegAssemEx = new(); + AutoRegAssemEx.RegApp(); } /// @@ -62,8 +64,9 @@ public void IFoxRemoveReg() var ed = doc.Editor; ed.WriteMessage($"\n 卸载注册表"); - //执行命令的时候会再次执行构造函数(导致初始化两次),但是再次执行就不会了 + //防止卸载两次,不然会报错的 AutoRegAssemEx?.UnRegApp(); + AutoRegAssemEx = null; } } -- Gitee From b3e633cb3c3ee8324f2ac2ea3933c3fd05e57303 Mon Sep 17 00:00:00 2001 From: liuqihong <540762622@qq.com> Date: Mon, 29 Aug 2022 19:34:09 +0800 Subject: [PATCH 452/675] =?UTF-8?q?=E5=91=BD=E5=90=8D=E7=A9=BA=E9=97=B4?= =?UTF-8?q?=E6=94=B9=E4=B8=BA=E6=9C=89=E6=84=8F=E4=B9=89=E7=9A=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/IFoxCAD.Cad.Shared/Runtime/DBTrans.cs | 2 +- src/IFoxCAD.Cad.Shared/Runtime/IAutoGo.cs | 2 +- src/IFoxCAD.Cad.Shared/Runtime/Utils.cs | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/IFoxCAD.Cad.Shared/Runtime/DBTrans.cs b/src/IFoxCAD.Cad.Shared/Runtime/DBTrans.cs index bcbf650..43ca19c 100644 --- a/src/IFoxCAD.Cad.Shared/Runtime/DBTrans.cs +++ b/src/IFoxCAD.Cad.Shared/Runtime/DBTrans.cs @@ -361,7 +361,7 @@ public ObjectId GetObjectId(string handleString) { var hanle = new Handle(Convert.ToInt64(handleString, 16)); //return Database.GetObjectId(false, hanle, 0); - return Helper.TryGetObjectId(Database, hanle); + return DBTransHelper.TryGetObjectId(Database, hanle); } #endregion diff --git a/src/IFoxCAD.Cad.Shared/Runtime/IAutoGo.cs b/src/IFoxCAD.Cad.Shared/Runtime/IAutoGo.cs index 52ee39c..f3ddcf2 100644 --- a/src/IFoxCAD.Cad.Shared/Runtime/IAutoGo.cs +++ b/src/IFoxCAD.Cad.Shared/Runtime/IAutoGo.cs @@ -143,7 +143,7 @@ public void Terminate() RunFunctions(_TerminateList); } } - catch (System.Exception) + catch (System.Exception e) { Debugger.Break(); } diff --git a/src/IFoxCAD.Cad.Shared/Runtime/Utils.cs b/src/IFoxCAD.Cad.Shared/Runtime/Utils.cs index e152e7a..ff32aad 100644 --- a/src/IFoxCAD.Cad.Shared/Runtime/Utils.cs +++ b/src/IFoxCAD.Cad.Shared/Runtime/Utils.cs @@ -2,7 +2,7 @@ using System; -public class Helper +public class DBTransHelper { /* * id = db.GetObjectId(false, handle, 0); -- Gitee From c3180934439ae52e9f33cdac7aec45e86a58776b Mon Sep 17 00:00:00 2001 From: liuqihong <540762622@qq.com> Date: Mon, 29 Aug 2022 21:09:24 +0800 Subject: [PATCH 453/675] =?UTF-8?q?=E5=9C=A8=E6=B3=A8=E9=87=8A=E5=90=8E?= =?UTF-8?q?=E9=9D=A2=E5=8A=A0=E7=A9=BA=E6=A0=BC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 18 +- src/IFoxCAD.Basal/ArrayEx.cs | 2 +- src/IFoxCAD.Basal/CLS/Range.cs | 2 +- src/IFoxCAD.Basal/CLS/RuntimeHelpers.cs | 12 +- src/IFoxCAD.Basal/CLS/ValueTuple.cs | 8 +- src/IFoxCAD.Basal/DictEx.cs | 6 +- src/IFoxCAD.Basal/EnumEx.cs | 22 +- src/IFoxCAD.Basal/LinkedHashSet.cs | 2 +- src/IFoxCAD.Basal/LoopList.cs | 16 +- src/IFoxCAD.Basal/Sortedset/ISet.cs | 22 +- src/IFoxCAD.Basal/Sortedset/SR.cs | 8 +- src/IFoxCAD.Basal/Sortedset/Sortedset.cs | 220 ++++++++-------- .../Algorithms/Graph/Graph.cs | 10 +- .../Algorithms/QuadTree/QuadTree.cs | 34 +-- .../Algorithms/QuadTree/QuadTreeNode.cs | 200 +++++++------- .../Algorithms/QuadTree/QuadTreeSelectMode.cs | 12 +- .../Algorithms/QuadTree/Rect.cs | 84 +++--- .../Algorithms/QuadTree/Utility.cs | 6 +- .../ExtensionMethod/BulgeVertexWidth.cs | 2 +- .../ExtensionMethod/Curve2dEx.cs | 2 +- .../ExtensionMethod/Curve3dEx.cs | 22 +- .../ExtensionMethod/CurveEx.cs | 32 +-- .../ExtensionMethod/DBDictionaryEx.cs | 6 +- .../ExtensionMethod/DBObjectEx.cs | 24 +- .../ExtensionMethod/EditorEx.cs | 243 +++++++++--------- .../ExtensionMethod/EntityBoundingInfo.cs | 44 ++-- .../ExtensionMethod/EntityEx.cs | 24 +- .../ExtensionMethod/Enums.cs | 2 +- .../ExtensionMethod/GeometryEx.cs | 52 ++-- src/IFoxCAD.Cad.Shared/ExtensionMethod/Jig.cs | 84 +++--- .../ExtensionMethod/ObjectIdEx.cs | 6 +- .../ExtensionMethod/PointEx.cs | 20 +- .../ExtensionMethod/SymbolTableEx.cs | 22 +- .../ExtensionMethod/SymbolTableRecordEx.cs | 18 +- .../ExtensionMethod/Tools.cs | 12 +- .../ExtensionMethod/XrefEx.cs | 126 ++++----- .../HatchConverter.cs" | 70 ++--- .../HatchInfo.cs" | 198 +++++++------- .../AttachmentPointHelper.cs" | 20 +- .../TextInfo.cs" | 28 +- src/IFoxCAD.Cad.Shared/Runtime/AOP.cs | 182 ++++++------- .../Runtime/AutoRegAssem.cs | 24 +- src/IFoxCAD.Cad.Shared/Runtime/CadVersion.cs | 6 +- src/IFoxCAD.Cad.Shared/Runtime/DBTrans.cs | 54 ++-- src/IFoxCAD.Cad.Shared/Runtime/Env.cs | 18 +- .../Runtime/FileOpenMode.cs | 2 +- src/IFoxCAD.Cad.Shared/Runtime/IAutoGo.cs | 32 +-- src/IFoxCAD.Cad.Shared/Runtime/Log.cs | 38 +-- .../Runtime/MethodInfoHelper.cs | 20 +- src/IFoxCAD.Cad.Shared/Runtime/SymbolTable.cs | 4 +- src/IFoxCAD.Cad.Shared/Runtime/Utils.cs | 12 +- .../IFoxCAD.Aacad08/GlobalUsings.cs | 2 +- .../IFoxCAD.Aacad09plus/GlobalUsings.cs | 2 +- src/IFoxCAD.LoadForm/AssemblyDependent.cs | 74 +++--- .../DefaultAssemblyResolve.cs | 12 +- src/IFoxCAD.LoadForm/GlobalUsings.cs | 8 +- src/IFoxCAD.LoadForm/LoaderForm.cs | 10 +- src/IFoxCAD.WPF/EventBindingExtension.cs | 8 +- tests/TestAcad08/GlobalUsings.cs | 14 +- tests/TestConsole/Info.cs | 4 +- tests/TestConsole/Program.cs | 22 +- tests/TestConsole/RuntimeHelpers.cs | 2 +- ...50\350\276\276\345\274\217\346\240\221.cs" | 22 +- tests/TestShared/CmdINI.cs | 42 +-- tests/TestShared/Test.cs | 78 +++--- tests/TestShared/TestAOP.cs | 20 +- tests/TestShared/TestCurve.cs | 26 +- tests/TestShared/TestDBTrans.cs | 44 ++-- tests/TestShared/TestEnt.cs | 4 +- tests/TestShared/TestJig.cs | 30 +-- tests/TestShared/TestLisp.cs | 66 ++--- tests/TestShared/TestMirrorFile.cs | 12 +- tests/TestShared/TestNetLoad.cs | 2 +- tests/TestShared/TestPoint.cs | 4 +- tests/TestShared/TestQuadTree.cs | 132 +++++----- tests/TestShared/Testid.cs | 8 +- tests/TestShared/testConvexHull.cs | 74 +++--- tests/TestShared/testblock.cs | 146 +++++------ tests/TestShared/testeditor.cs | 10 +- tests/TestShared/testenv.cs | 13 +- 80 files changed, 1510 insertions(+), 1514 deletions(-) diff --git a/README.md b/README.md index e4ec848..5e026a2 100644 --- a/README.md +++ b/README.md @@ -143,31 +143,31 @@ IFox的[jing分支](https://gitee.com/inspirefunction/ifoxcad/tree/jing/)是一 [IFoxInitialize] public void InitOne() { - //TODO 您想在加载dll之后自动执行的函数 + // TODO 您想在加载dll之后自动执行的函数 // 可以随便在哪里类里 可以多次实现 IFoxInitialize 特性 } } - //其他的类中的函数: - //实现自动接口之后,在任意一个函数上面使用此特性,减少每次改动 CmdINI 类 + // 其他的类中的函数: + // 实现自动接口之后,在任意一个函数上面使用此特性,减少每次改动 CmdINI 类 public class AutoTest { [IFoxInitialize] public void Initialize() { - //TODO 您想在加载dll之后自动执行的函数 + // TODO 您想在加载dll之后自动执行的函数 } [IFoxInitialize] public void InitTwo() { - //TODO 您想在加载dll之后自动执行的函数 + // TODO 您想在加载dll之后自动执行的函数 // 可以随便在哪里类里 可以多次实现 IFoxInitialize 特性 } [IFoxInitialize(isInitialize: false)] // 特性的参数为false的时候就表示卸载时执行的函数 public void Terminate() { - //TODO 您想在关闭cad时自动执行的函数 + // TODO 您想在关闭cad时自动执行的函数 } } ``` @@ -179,10 +179,10 @@ IFox的[jing分支](https://gitee.com/inspirefunction/ifoxcad/tree/jing/)是一 由于cad的对象是有打开模式,是否可写等等,为了安全起见,在处理对象时,一般是用读模式打开,然后需要写数据的时候在提权为写模式,然后在降级到读模式,但是这个过程中,很容易漏掉某些步骤,然后cad崩溃。为了处理这些情况,内裤提供了提权类来保证读写模式的有序转换。 ```c# - using(line.ForWrite()) //开启对象写模式提权事务 + using(line.ForWrite()) // 开启对象写模式提权事务 { - //处理代码 - } //关闭事务自动处理读写模式 + // 处理代码 + } // 关闭事务自动处理读写模式 ``` 8. 未完待续。。。。 diff --git a/src/IFoxCAD.Basal/ArrayEx.cs b/src/IFoxCAD.Basal/ArrayEx.cs index 0699c33..21947f6 100644 --- a/src/IFoxCAD.Basal/ArrayEx.cs +++ b/src/IFoxCAD.Basal/ArrayEx.cs @@ -39,7 +39,7 @@ public static T[] Combine2(this T[] a, T[] b) /// public static void Deduplication(List lst, Func func) { - //头和尾比较,满足条件移除尾巴 + // 头和尾比较,满足条件移除尾巴 for (int i = 0; i < lst.Count; i++) { var first = lst[i]; diff --git a/src/IFoxCAD.Basal/CLS/Range.cs b/src/IFoxCAD.Basal/CLS/Range.cs index 221167b..35fb4cc 100644 --- a/src/IFoxCAD.Basal/CLS/Range.cs +++ b/src/IFoxCAD.Basal/CLS/Range.cs @@ -75,7 +75,7 @@ public override string ToString() #if NET45 [MethodImpl(MethodImplOptions.AggressiveInlining)] #endif - //[CLSCompliant(false)] + // [CLSCompliant(false)] public (int Offset, int Length) GetOffsetAndLength(int length) { int start; diff --git a/src/IFoxCAD.Basal/CLS/RuntimeHelpers.cs b/src/IFoxCAD.Basal/CLS/RuntimeHelpers.cs index 29c2c3c..1008b09 100644 --- a/src/IFoxCAD.Basal/CLS/RuntimeHelpers.cs +++ b/src/IFoxCAD.Basal/CLS/RuntimeHelpers.cs @@ -1,14 +1,14 @@ -//#if NET35 +// #if NET35 // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. namespace System.Runtime.CompilerServices; -//编译提示多个程序集中定义,屏蔽不了,但是不影响编译 -//#pragma warning disable CS1685 // 类型与导入类型冲突 +// 编译提示多个程序集中定义,屏蔽不了,但是不影响编译 +// #pragma warning disable CS1685 // 类型与导入类型冲突 public static class RuntimeHelpers -//#pragma warning restore CS1685 // 类型与导入类型冲突 +// #pragma warning restore CS1685 // 类型与导入类型冲突 { /// /// Slices the specified array using the specified range. @@ -25,7 +25,7 @@ public static T[] GetSubArray(T[] array, Range range) // We know the type of the array to be exactly T[]. if (length == 0) { - //return Array.Empty(); + // return Array.Empty(); return new T[0]; } @@ -42,4 +42,4 @@ public static T[] GetSubArray(T[] array, Range range) } } } -//#endif \ No newline at end of file +// #endif \ No newline at end of file diff --git a/src/IFoxCAD.Basal/CLS/ValueTuple.cs b/src/IFoxCAD.Basal/CLS/ValueTuple.cs index d1db08b..e7edddf 100644 --- a/src/IFoxCAD.Basal/CLS/ValueTuple.cs +++ b/src/IFoxCAD.Basal/CLS/ValueTuple.cs @@ -1,7 +1,7 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. -//#pragma warning disable SA1141 // explicitly not using tuple syntax in tuple implementation +// #pragma warning disable SA1141 // explicitly not using tuple syntax in tuple implementation using System.Diagnostics; @@ -61,7 +61,7 @@ public static int Combine(int h1, int h2) namespace System { - //internal static class SR + // internal static class SR internal sealed partial class SR { // public const string ArgumentException_ValueTupleIncorrectType = "The parameter should be a ValueTuple type of appropriate arity."; @@ -2131,8 +2131,8 @@ int ITupleInternal.Size { get { - //ITupleInternal? rest = Rest as ITupleInternal; - //return rest == null ? 8 : 7 + rest.Size; + // ITupleInternal? rest = Rest as ITupleInternal; + // return rest == null ? 8 : 7 + rest.Size; return Rest is not ITupleInternal rest ? 8 : 7 + rest.Size; } } diff --git a/src/IFoxCAD.Basal/DictEx.cs b/src/IFoxCAD.Basal/DictEx.cs index 7ff153f..296fb16 100644 --- a/src/IFoxCAD.Basal/DictEx.cs +++ b/src/IFoxCAD.Basal/DictEx.cs @@ -2,8 +2,8 @@ public static class DictEx { - //public static TKey? GetKey(this IDictionary dict!!, TKey key!!) - //{ + // public static TKey? GetKey(this IDictionary dict!!, TKey key!!) + // { // if (dict.ContainsKey(key)) // { // foreach (var item in dict.Keys) @@ -11,5 +11,5 @@ public static class DictEx // return item; // } // return default; - //} + // } } diff --git a/src/IFoxCAD.Basal/EnumEx.cs b/src/IFoxCAD.Basal/EnumEx.cs index 7165481..46aa077 100644 --- a/src/IFoxCAD.Basal/EnumEx.cs +++ b/src/IFoxCAD.Basal/EnumEx.cs @@ -13,7 +13,7 @@ public static void CleanCache() _cache.Clear(); } - //(类型完整名,描述组合) + // (类型完整名,描述组合) static readonly Dictionary> _cache = new(); /// @@ -34,7 +34,7 @@ public static void CleanCache() if (fieldInfo == null) return null!; - //注释存放的容器 + // 注释存放的容器 HashSet nodes = new(); if (Attribute.GetCustomAttribute(fieldInfo, typeof(T)) is T attribute) { @@ -43,28 +43,28 @@ public static void CleanCache() return nodes; } - //通常到这里的就是 ALL = A | B | C - //遍历所有的枚举,组合每个注释 + // 通常到这里的就是 ALL = A | B | C + // 遍历所有的枚举,组合每个注释 List enumHas = new(); - //遍历这个枚举类型,获取枚举按位包含的成员 + // 遍历这个枚举类型,获取枚举按位包含的成员 foreach (Enum em in Enum.GetValues(eType)) if ((e.GetHashCode() & em.GetHashCode()) == em.GetHashCode() && e.GetHashCode() != em.GetHashCode()) enumHas.Add(em); - //采取的行为是:注释的行为是特殊的,就按照注释的,否则,遍历子元素提取注释 - //大的在前面才能判断是否按位包含后面的,后面的就是要移除的 + // 采取的行为是:注释的行为是特殊的,就按照注释的,否则,遍历子元素提取注释 + // 大的在前面才能判断是否按位包含后面的,后面的就是要移除的 enumHas = enumHas.OrderByDescending(a => a.GetHashCode()).ToList(); ArrayEx.Deduplication(enumHas, (a, b) => { return (a.GetHashCode() & b.GetHashCode()) == b.GetHashCode(); }); - //逆序仅仅为排序后处理,不一定和书写顺序一样,尤其是递归可能存在重复的元素 + // 逆序仅仅为排序后处理,不一定和书写顺序一样,尤其是递归可能存在重复的元素 for (int i = enumHas.Count - 1; i >= 0; i--) { - var atts = GetAttribute(enumHas[i], noDescrToString);//递归 + var atts = GetAttribute(enumHas[i], noDescrToString);// 递归 if (atts == null) continue; foreach (var item in atts) @@ -90,13 +90,13 @@ public static void CleanCache() } - //TODO 山人审核代码之后可以删除,这个完全被上面替代了 + // TODO 山人审核代码之后可以删除,这个完全被上面替代了 public static string GetDesc(this Enum val) { var type = val.GetType(); var memberInfo = type.GetMember(val.ToString()); var attributes = memberInfo[0].GetCustomAttributes(typeof(DescriptionAttribute), false); - //如果没有定义描述,就把当前枚举值的对应名称返回 + // 如果没有定义描述,就把当前枚举值的对应名称返回 if (attributes is null || attributes.Length != 1) return val.ToString(); diff --git a/src/IFoxCAD.Basal/LinkedHashSet.cs b/src/IFoxCAD.Basal/LinkedHashSet.cs index 8659c24..6295795 100644 --- a/src/IFoxCAD.Basal/LinkedHashSet.cs +++ b/src/IFoxCAD.Basal/LinkedHashSet.cs @@ -135,7 +135,7 @@ public bool Contains(T item) public void CopyTo(T[] array, int arrayIndex) { - //m_LinkedList.CopyTo(array, arrayIndex); + // m_LinkedList.CopyTo(array, arrayIndex); return; } diff --git a/src/IFoxCAD.Basal/LoopList.cs b/src/IFoxCAD.Basal/LoopList.cs index c0e021c..9bd986b 100644 --- a/src/IFoxCAD.Basal/LoopList.cs +++ b/src/IFoxCAD.Basal/LoopList.cs @@ -1,6 +1,6 @@ namespace IFoxCAD.Basal; -#line hidden //调试的时候跳过它 +#line hidden // 调试的时候跳过它 /// /// 环链表节点 @@ -161,7 +161,7 @@ public void Reverse() /// public void Clear() { - //移除头部,表示链表再也无法遍历得到 + // 移除头部,表示链表再也无法遍历得到 First = null; Count = 0; } @@ -238,17 +238,17 @@ public bool Contains(T value) /// public LoopListNode? Find(T value) { - //LoopListNode result = null; - //ForEach(node => - //{ + // LoopListNode result = null; + // ForEach(node => + // { // if (node.Value.Equals(t2)) // { // result = node; // return true; // } // return false; - //}); - //return result; + // }); + // return result; LoopListNode? node = First; var c = EqualityComparer.Default; @@ -536,7 +536,7 @@ void InternalRemove(LoopListNode node) { if (Count == 1 || node == First) { - RemoveFirst();//此处会减数字 + RemoveFirst();// 此处会减数字 } else { diff --git a/src/IFoxCAD.Basal/Sortedset/ISet.cs b/src/IFoxCAD.Basal/Sortedset/ISet.cs index 549e9f0..d991205 100644 --- a/src/IFoxCAD.Basal/Sortedset/ISet.cs +++ b/src/IFoxCAD.Basal/Sortedset/ISet.cs @@ -30,37 +30,37 @@ namespace System.Collections.Generic public interface ISet : ICollection { - //Add ITEM to the set, return true if added, false if duplicate + // Add ITEM to the set, return true if added, false if duplicate new bool Add(T item); - //Transform this set into its union with the IEnumerable other + // Transform this set into its union with the IEnumerable other void UnionWith(IEnumerable other); - //Transform this set into its intersection with the IEnumberable other + // Transform this set into its intersection with the IEnumberable other void IntersectWith(IEnumerable other); - //Transform this set so it contains no elements that are also in other + // Transform this set so it contains no elements that are also in other void ExceptWith(IEnumerable other); - //Transform this set so it contains elements initially in this or in other, but not both + // Transform this set so it contains elements initially in this or in other, but not both void SymmetricExceptWith(IEnumerable other); - //Check if this set is a subset of other + // Check if this set is a subset of other bool IsSubsetOf(IEnumerable other); - //Check if this set is a superset of other + // Check if this set is a superset of other bool IsSupersetOf(IEnumerable other); - //Check if this set is a subset of other, but not the same as it + // Check if this set is a subset of other, but not the same as it bool IsProperSupersetOf(IEnumerable other); - //Check if this set is a superset of other, but not the same as it + // Check if this set is a superset of other, but not the same as it bool IsProperSubsetOf(IEnumerable other); - //Check if this set has any elements in common with other + // Check if this set has any elements in common with other bool Overlaps(IEnumerable other); - //Check if this set contains the same and only the same elements as other + // Check if this set contains the same and only the same elements as other bool SetEquals(IEnumerable other); diff --git a/src/IFoxCAD.Basal/Sortedset/SR.cs b/src/IFoxCAD.Basal/Sortedset/SR.cs index 093de14..59e10a7 100644 --- a/src/IFoxCAD.Basal/Sortedset/SR.cs +++ b/src/IFoxCAD.Basal/Sortedset/SR.cs @@ -565,7 +565,7 @@ internal static string GetString(CultureInfo culture, string name) internal const string Error_InvalidRadiusValue = "Error_InvalidRadiusValue"; internal const string ToolTipString = "ToolTipString"; - //Collection Editor Resources + // Collection Editor Resources internal const string CollectionEditorCaption = "CollectionEditorCaption"; internal const string CollectionEditorProperties = "CollectionEditorProperties"; internal const string CollectionEditorPropertiesMultiSelect = "CollectionEditorPropertiesMultiSelect"; @@ -604,7 +604,7 @@ internal static string GetString(CultureInfo culture, string name) internal const string Error_InsideAtomicScope = "Error_InsideAtomicScope"; internal const string SuspendReason_WorkflowChange = "SuspendReason_WorkflowChange"; - //type filtering + // type filtering internal const string FilterDescription_ParameterDeclaration = "FilterDescription_ParameterDeclaration"; internal const string FilterDescription_GenericArgument = "FilterDescription_GenericArgument"; @@ -718,7 +718,7 @@ internal static string GetString(CultureInfo culture, string name) internal const string Standard = "Standard"; internal const string Base = "Base"; - //CustomActivityDesigner + // CustomActivityDesigner internal const string ValidatorCompanionClassDesc = "ValidatorCompanionClassDesc"; internal const string ExecutorCompanionClassDesc = "ExecutorCompanionClassDesc"; internal const string DesignerCompanionClassDesc = "DesignerCompanionClassDesc"; @@ -873,7 +873,7 @@ internal static string GetString(CultureInfo culture, string name) internal const string NamespaceAndDeclaringTypeCannotBeNull = "NamespaceAndDeclaringTypeCannotBeNull"; internal const string NotElementType = "NotElementType"; - //Layout persistence errors + // Layout persistence errors internal const string Error_LayoutSerializationActivityNotFound = "Error_LayoutSerializationActivityNotFound"; internal const string Error_LayoutSerializationAssociatedActivityNotFound = "Error_LayoutSerializationAssociatedActivityNotFound"; internal const string Error_LayoutSerializationPersistenceSupport = "Error_LayoutSerializationPersistenceSupport"; diff --git a/src/IFoxCAD.Basal/Sortedset/Sortedset.cs b/src/IFoxCAD.Basal/Sortedset/Sortedset.cs index 058373e..f9c7060 100644 --- a/src/IFoxCAD.Basal/Sortedset/Sortedset.cs +++ b/src/IFoxCAD.Basal/Sortedset/Sortedset.cs @@ -65,11 +65,11 @@ internal enum TreeRotation [DebuggerDisplay("Count = {Count}")] #if !FEATURE_NETCORE [Serializable] - public class SortedSet : ISet, ICollection, ICollection, ISerializable, IDeserializationCallback//, IReadOnlyCollection + public class SortedSet : ISet, ICollection, ICollection, ISerializable, IDeserializationCallback// , IReadOnlyCollection { #else public class SortedSet : ISet, ICollection, ICollection, IReadOnlyCollection { -#endif //!FEATURE_NETCORE +#endif // !FEATURE_NETCORE #region local variables/constants Node root; IComparer comparer; @@ -81,7 +81,7 @@ public class SortedSet : ISet, ICollection, ICollection, IReadOnlyColle private const String CountName = "Count"; private const String ItemsName = "Items"; private const String VersionName = "Version"; - //needed for enumerator + // needed for enumerator private const String TreeName = "Tree"; private const String NodeValueName = "Item"; private const String EnumStartName = "EnumStarted"; @@ -89,13 +89,13 @@ public class SortedSet : ISet, ICollection, ICollection, IReadOnlyColle private const String EnumVersionName = "EnumVersion"; #if !FEATURE_NETCORE - //needed for TreeSubset + // needed for TreeSubset private const String minName = "Min"; private const String maxName = "Max"; private const String lBoundActiveName = "lBoundActive"; private const String uBoundActiveName = "uBoundActive"; - private SerializationInfo siInfo; //A temporary variable which we need during deserialization. + private SerializationInfo siInfo; // A temporary variable which we need during deserialization. #endif internal const int StackAllocThreshold = 100; @@ -136,7 +136,7 @@ public SortedSet(IEnumerable collection, IComparer comparer) SortedSet baseTreeSubSet = collection as TreeSubSet; if (collection is SortedSet baseSortedSet && baseTreeSubSet == null && AreComparersEqual(this, baseSortedSet)) { - //breadth first traversal to recreate nodes + // breadth first traversal to recreate nodes if (baseSortedSet.Count == 0) { count = 0; @@ -146,7 +146,7 @@ public SortedSet(IEnumerable collection, IComparer comparer) } - //pre order way to replicate nodes + // pre order way to replicate nodes Stack theirStack = new(2 * Log2(baseSortedSet.Count) + 2); Stack myStack = new(2 * Log2(baseSortedSet.Count) + 2); Node theirCurrent = baseSortedSet.root; @@ -185,7 +185,7 @@ public SortedSet(IEnumerable collection, IComparer comparer) version = 0; } else - { //As it stands, you're doing an NlogN sort of the collection + { // As it stands, you're doing an NlogN sort of the collection List els = new(collection); els.Sort(this.comparer); @@ -387,11 +387,11 @@ object ICollection.SyncRoot #region Subclass helpers - //virtual function for subclass that needs to update count + // virtual function for subclass that needs to update count internal virtual void VersionCheck() { } - //virtual function for subclass that needs to do range checks + // virtual function for subclass that needs to do range checks internal virtual bool IsWithinRange(T item) { return true; @@ -439,8 +439,8 @@ internal virtual bool AddIfNotPresent(T item) Node grandParent = null; Node greatGrandParent = null; - //even if we don't actually add to the set, we may be altering its structure (by doing rotations - //and such). so update version to disable any enumerators/subsets working on it + // even if we don't actually add to the set, we may be altering its structure (by doing rotations + // and such). so update version to disable any enumerators/subsets working on it version++; @@ -523,8 +523,8 @@ internal virtual bool DoRemove(T item) // In top-down implementation, it is important to make sure the node to be deleted is not a 2-node. // Following code will make sure the node on the path is not a 2 Node. - //even if we don't actually remove from the set, we may be altering its structure (by doing rotations - //and such). so update version to disable any enumerators/subsets working on it + // even if we don't actually remove from the set, we may be altering its structure (by doing rotations + // and such). so update version to disable any enumerators/subsets working on it version++; Node current = root; @@ -588,29 +588,29 @@ internal virtual bool DoRemove(T item) switch (rotation) { case TreeRotation.RightRotation: - Debug.Assert(parent.Left == sibling, "sibling must be left child of parent!"); - Debug.Assert(sibling.Left.IsRed, "Left child of sibling must be red!"); - sibling.Left.IsRed = false; - newGrandParent = RotateRight(parent); - break; + Debug.Assert(parent.Left == sibling, "sibling must be left child of parent!"); + Debug.Assert(sibling.Left.IsRed, "Left child of sibling must be red!"); + sibling.Left.IsRed = false; + newGrandParent = RotateRight(parent); + break; case TreeRotation.LeftRotation: - Debug.Assert(parent.Right == sibling, "sibling must be left child of parent!"); - Debug.Assert(sibling.Right.IsRed, "Right child of sibling must be red!"); - sibling.Right.IsRed = false; - newGrandParent = RotateLeft(parent); - break; + Debug.Assert(parent.Right == sibling, "sibling must be left child of parent!"); + Debug.Assert(sibling.Right.IsRed, "Right child of sibling must be red!"); + sibling.Right.IsRed = false; + newGrandParent = RotateLeft(parent); + break; case TreeRotation.RightLeftRotation: - Debug.Assert(parent.Right == sibling, "sibling must be left child of parent!"); - Debug.Assert(sibling.Left.IsRed, "Left child of sibling must be red!"); - newGrandParent = RotateRightLeft(parent); - break; + Debug.Assert(parent.Right == sibling, "sibling must be left child of parent!"); + Debug.Assert(sibling.Left.IsRed, "Left child of sibling must be red!"); + newGrandParent = RotateRightLeft(parent); + break; case TreeRotation.LeftRightRotation: - Debug.Assert(parent.Left == sibling, "sibling must be left child of parent!"); - Debug.Assert(sibling.Right.IsRed, "Right child of sibling must be red!"); - newGrandParent = RotateLeftRight(parent); - break; + Debug.Assert(parent.Left == sibling, "sibling must be left child of parent!"); + Debug.Assert(sibling.Right.IsRed, "Right child of sibling must be red!"); + newGrandParent = RotateLeftRight(parent); + break; } newGrandParent.IsRed = parent.IsRed; @@ -708,7 +708,7 @@ public void CopyTo(T[] array, int index, int count) { throw new ArgumentException(SR.GetString("SR.Arg_ArrayPlusOffTooSmall")); } - //upper bound + // upper bound count += index; InOrderTreeWalk(delegate (Node node) @@ -948,9 +948,9 @@ internal virtual Node FindNode(T item) return null; } - //used for bithelpers. Note that this implementation is completely different - //from the Subset's. The two should not be mixed. This indexes as if the tree were an array. - //http://en.wikipedia.org/wiki/Binary_Tree#Methods_for_storing_binary_trees + // used for bithelpers. Note that this implementation is completely different + // from the Subset's. The two should not be mixed. This indexes as if the tree were an array. + // http://en.wikipedia.org/wiki/Binary_Tree#Methods_for_storing_binary_trees internal virtual int InternalIndexOf(T item) { Node current = root; @@ -1140,7 +1140,7 @@ internal static bool SortedSetEquals(SortedSet set1, SortedSet set2, IComp } - //This is a little frustrating because we can't support more sorted structures + // This is a little frustrating because we can't support more sorted structures private static bool AreComparersEqual(SortedSet set1, SortedSet set2) { return set1.Comparer.Equals(set2.Comparer); @@ -1171,11 +1171,11 @@ internal T[] ToArray() #region ISet Members /// - /// Transform this set into its union with the IEnumerable OTHER - ///Attempts to insert each element and rejects it if it exists. - /// NOTE: The caller object is important as UnionWith uses the Comparator - ///associated with THIS to check equality - /// Throws ArgumentNullException if OTHER is null + /// Transform this set into its union with the IEnumerable OTHER + /// Attempts to insert each element and rejects it if it exists. + /// NOTE: The caller object is important as UnionWith uses the Comparator + /// associated with THIS to check equality + /// Throws ArgumentNullException if OTHER is null /// /// public void UnionWith(IEnumerable other) @@ -1202,8 +1202,8 @@ public void UnionWith(IEnumerable other) if (s != null && t == null && AreComparersEqual(this, s) && (s.Count > this.Count / 2)) - { //this actually hurts if N is much greater than M the /2 is arbitrary - //first do a merge sort to an array. + { // this actually hurts if N is much greater than M the /2 is arbitrary + // first do a merge sort to an array. T[] merged = new T[s.Count + this.Count]; int c = 0; Enumerator mine = this.GetEnumerator(); @@ -1239,9 +1239,9 @@ public void UnionWith(IEnumerable other) } while (remaining.MoveNext()); } - //now merged has all c elements + // now merged has all c elements - //safe to gc the root, we have all the elements + // safe to gc the root, we have all the elements root = null; @@ -1261,9 +1261,9 @@ private static Node ConstructRootFromSortedArray(T[] arr, int startIndex, int en - //what does this do? - //you're given a sorted array... say 1 2 3 4 5 6 - //2 cases: + // what does this do? + // you're given a sorted array... say 1 2 3 4 5 6 + // 2 cases: // If there are odd # of elements, pick the middle element (in this case 4), and compute // its left and right branches // If there are even # of elements, pick the left middle element, save the right middle element @@ -1278,10 +1278,10 @@ private static Node ConstructRootFromSortedArray(T[] arr, int startIndex, int en // down to the bottom - //the iterative way to do this ends up wasting more space than it saves in stack frames (at - //least in what i tried) - //so we're doing this recursively - //base cases are described below + // the iterative way to do this ends up wasting more space than it saves in stack frames (at + // least in what i tried) + // so we're doing this recursively + // base cases are described below int size = endIndex - startIndex + 1; if (size == 0) { @@ -1359,18 +1359,18 @@ public virtual void IntersectWith(IEnumerable other) if (Count == 0) return; - //HashSet optimizations can't be done until equality comparers and comparers are related + // HashSet optimizations can't be done until equality comparers and comparers are related - //Technically, this would work as well with an ISorted + // Technically, this would work as well with an ISorted TreeSubSet t = this as TreeSubSet; if (t != null) VersionCheck(); - //only let this happen if i am also a SortedSet, not a SubSet + // only let this happen if i am also a SortedSet, not a SubSet if (other is SortedSet s && t == null && AreComparersEqual(this, s)) { - //first do a merge sort to an array. + // first do a merge sort to an array. T[] merged = new T[this.Count]; int c = 0; Enumerator mine = this.GetEnumerator(); @@ -1398,9 +1398,9 @@ public virtual void IntersectWith(IEnumerable other) } } - //now merged has all c elements + // now merged has all c elements - //safe to gc the root, we have all the elements + // safe to gc the root, we have all the elements root = null; root = SortedSet.ConstructRootFromSortedArray(merged, 0, c - 1, null); @@ -1458,7 +1458,7 @@ public void ExceptWith(IEnumerable other) if (other is SortedSet asSorted && AreComparersEqual(this, asSorted)) { - //outside range, no point doing anything + // outside range, no point doing anything if (!(comparer.Compare(asSorted.Max, this.Min) < 0 || comparer.Compare(asSorted.Min, this.Max) > 0)) { T min = this.Min; @@ -1522,21 +1522,21 @@ public void SymmetricExceptWith(IEnumerable other) #endif else { - //need perf improvement on this + // need perf improvement on this T[] elements = (new List(other)).ToArray(); Array.Sort(elements, this.Comparer); SymmetricExceptWithSameEC(elements); } } - //OTHER must be a set + // OTHER must be a set internal void SymmetricExceptWithSameEC(ISet other) { foreach (T item in other) { - //yes, it is classier to say - //if (!this.Remove(item))this.Add(item); - //but this ends up saving on rotations + // yes, it is classier to say + // if (!this.Remove(item))this.Add(item); + // but this ends up saving on rotations if (this.Contains(item)) { this.Remove(item); @@ -1548,7 +1548,7 @@ internal void SymmetricExceptWithSameEC(ISet other) } } - //OTHER must be a sorted array + // OTHER must be a sorted array internal void SymmetricExceptWithSameEC(T[] other) { if (other.Length == 0) @@ -1600,8 +1600,8 @@ public bool IsSubsetOf(IEnumerable other) } else { - //worst case: mark every element in my set and see if i've counted all - //O(MlogN) + // worst case: mark every element in my set and see if i've counted all + // O(MlogN) ElementCount result = CheckUniqueAndUnfoundElements(other, false); return (result.uniqueCount == Count && result.unfoundCount >= 0); @@ -1642,13 +1642,13 @@ public bool IsProperSubsetOf(IEnumerable other) #if USING_HASH_SET - //do it one way for HashSets + // do it one way for HashSets HashSet asHash = other as HashSet; if (asHash != null && comparer.Equals(Comparer.Default) && asHash.Comparer.Equals(EqualityComparer.Default)) { return asHash.IsProperSupersetOf(this); } #endif - //another for sorted sets with the same comparer + // another for sorted sets with the same comparer if (other is SortedSet asSorted && AreComparersEqual(this, asSorted)) { if (this.Count >= asSorted.Count) @@ -1657,8 +1657,8 @@ public bool IsProperSubsetOf(IEnumerable other) } - //worst case: mark every element in my set and see if i've counted all - //O(MlogN). + // worst case: mark every element in my set and see if i've counted all + // O(MlogN). ElementCount result = CheckUniqueAndUnfoundElements(other, false); return (result.uniqueCount == Count && result.unfoundCount > 0); } @@ -1679,14 +1679,14 @@ public bool IsSupersetOf(IEnumerable other) if ((other as ICollection) != null && (other as ICollection).Count == 0) return true; - //do it one way for HashSets + // do it one way for HashSets #if USING_HASH_SET HashSet asHash = other as HashSet; if (asHash != null && comparer.Equals(Comparer.Default) && asHash.Comparer.Equals(EqualityComparer.Default)) { return asHash.IsSubsetOf(this); } #endif - //another for sorted sets with the same comparer + // another for sorted sets with the same comparer if (other is SortedSet asSorted && AreComparersEqual(this, asSorted)) { if (this.Count < asSorted.Count) @@ -1699,7 +1699,7 @@ public bool IsSupersetOf(IEnumerable other) } return true; } - //and a third for everything else + // and a third for everything else return ContainsAllElements(other); } @@ -1723,14 +1723,14 @@ public bool IsProperSupersetOf(IEnumerable other) return true; #if USING_HASH_SET - //do it one way for HashSets + // do it one way for HashSets HashSet asHash = other as HashSet; if (asHash != null && comparer.Equals(Comparer.Default) && asHash.Comparer.Equals(EqualityComparer.Default)) { return asHash.IsProperSubsetOf(this); } #endif - //another way for sorted sets + // another way for sorted sets if (other is SortedSet asSorted && AreComparersEqual(asSorted, this)) { if (asSorted.Count >= this.Count) @@ -1745,10 +1745,10 @@ public bool IsProperSupersetOf(IEnumerable other) } - //worst case: mark every element in my set and see if i've counted all - //O(MlogN) - //slight optimization, put it into a HashSet and then check can do it in O(N+M) - //but slower in better cases + wastes space + // worst case: mark every element in my set and see if i've counted all + // O(MlogN) + // slight optimization, put it into a HashSet and then check can do it in O(N+M) + // but slower in better cases + wastes space ElementCount result = CheckUniqueAndUnfoundElements(other, true); return (result.uniqueCount < Count && result.unfoundCount == 0); } @@ -1792,8 +1792,8 @@ public bool SetEquals(IEnumerable other) return mineEnded && theirsEnded; } - //worst case: mark every element in my set and see if i've counted all - //O(N) by size of other + // worst case: mark every element in my set and see if i've counted all + // O(N) by size of other ElementCount result = CheckUniqueAndUnfoundElements(other, true); return (result.uniqueCount == Count && result.unfoundCount == 0); } @@ -2046,12 +2046,12 @@ internal sealed class TreeSubSet : SortedSet { #endif SortedSet underlying; T min, max; - //these exist for unbounded collections - //for instance, you could allow this subset to be defined for i>10. The set will throw if - //anything <=10 is added, but there is no upperbound. These features Head(), Tail(), were punted - //in the spec, and are not available, but the framework is there to make them available at some point. + // these exist for unbounded collections + // for instance, you could allow this subset to be defined for i>10. The set will throw if + // anything <=10 is added, but there is no upperbound. These features Head(), Tail(), were punted + // in the spec, and are not available, but the framework is there to make them available at some point. bool lBoundActive, uBoundActive; - //used to see if the count is out of date + // used to see if the count is out of date #if DEBUG @@ -2189,7 +2189,7 @@ internal override bool InOrderTreeWalk(TreeWalkPredicate action, Boolean reve // The maximum height of a red-black tree is 2*lg(n+1). // See page 264 of "Introduction to algorithms" by Thomas H. Cormen - Stack stack = new(2 * (int)SortedSet.Log2(count + 1)); //this is not exactly right if count is out of date, but the stack can grow + Stack stack = new(2 * (int)SortedSet.Log2(count + 1)); // this is not exactly right if count is out of date, but the stack can grow Node current = root; while (current != null) { @@ -2287,8 +2287,8 @@ internal override SortedSet.Node FindNode(T item) return base.FindNode(item); } - //this does indexing in an inefficient way compared to the actual sortedset, but it saves a - //lot of space + // this does indexing in an inefficient way compared to the actual sortedset, but it saves a + // lot of space internal override int InternalIndexOf(T item) { int count = -1; @@ -2325,20 +2325,20 @@ private void VersionCheckImpl() - //This passes functionality down to the underlying tree, clipping edges if necessary - //There's nothing gained by having a nested subset. May as well draw it from the base - //Cannot increase the bounds of the subset, can only decrease it + // This passes functionality down to the underlying tree, clipping edges if necessary + // There's nothing gained by having a nested subset. May as well draw it from the base + // Cannot increase the bounds of the subset, can only decrease it public override SortedSet GetViewBetween(T lowerValue, T upperValue) { if (lBoundActive && Comparer.Compare(min, lowerValue) > 0) { - //lBound = min; + // lBound = min; throw new ArgumentOutOfRangeException("lowerValue"); } if (uBoundActive && Comparer.Compare(max, upperValue) < 0) { - //uBound = max; + // uBound = max; throw new ArgumentOutOfRangeException("upperValue"); } TreeSubSet ret = (TreeSubSet)underlying.GetViewBetween(lowerValue, upperValue); @@ -2385,8 +2385,8 @@ protected override void GetObjectData(SerializationInfo info, StreamingContext c void IDeserializationCallback.OnDeserialization(Object sender) { - //don't do anything here as its already been done by the constructor - //OnDeserialization(sender); + // don't do anything here as its already been done by the constructor + // OnDeserialization(sender); } @@ -2427,7 +2427,7 @@ private void OnDeserializationImpl(Object sender) underlying.version = siInfo.GetInt32(VersionName); count = underlying.count; version = underlying.version - 1; - VersionCheck(); //this should update the count to be right and update root to be right + VersionCheck(); // this should update the count to be right and update root to be right if (count != savedCount) { @@ -2462,7 +2462,7 @@ protected virtual void GetObjectData(SerializationInfo info, StreamingContext co ThrowHelper.ThrowArgumentNullException(ExceptionArgument.info); } - info.AddValue(CountName, count); //This is the length of the bucket array. + info.AddValue(CountName, count); // This is the length of the bucket array. info.AddValue(ComparerName, comparer, typeof(IComparer)); info.AddValue(VersionName, version); @@ -2483,7 +2483,7 @@ protected virtual void OnDeserialization(Object sender) { if (comparer != null) { - return; //Somebody had a dependency on this class and fixed us up before the ObjectManager got to it. + return; // Somebody had a dependency on this class and fixed us up before the ObjectManager got to it. } if (siInfo == null) @@ -2516,7 +2516,7 @@ protected virtual void OnDeserialization(Object sender) } siInfo = null; } -#endif //!FEATURE_NETCORE +#endif // !FEATURE_NETCORE #endregion #region Helper Classes @@ -2566,7 +2566,7 @@ public struct Enumerator : IEnumerator, IEnumerator { internal Enumerator(SortedSet set) { tree = set; - //this is a hack to make sure that the underlying subset has not been changed since + // this is a hack to make sure that the underlying subset has not been changed since // tree.VersionCheck(); @@ -2585,7 +2585,7 @@ internal Enumerator(SortedSet set) internal Enumerator(SortedSet set, bool reverse) { tree = set; - //this is a hack to make sure that the underlying subset has not been changed since + // this is a hack to make sure that the underlying subset has not been changed since // tree.VersionCheck(); version = tree.version; @@ -2653,7 +2653,7 @@ private void OnDeserialization(Object sender) { T item = (T)siInfo.GetValue(NodeValueName, typeof(T)); Intialize(); - //go until it reaches the value we want + // go until it reaches the value we want while (this.MoveNext()) { if (tree.Comparer.Compare(this.Current, item) == 0) @@ -2663,7 +2663,7 @@ private void OnDeserialization(Object sender) } -#endif //!FEATURE_NETCORE +#endif // !FEATURE_NETCORE private void Intialize() @@ -2696,7 +2696,7 @@ private void Intialize() public bool MoveNext() { - //this is a hack to make sure that the underlying subset has not been changed since + // this is a hack to make sure that the underlying subset has not been changed since // tree.VersionCheck(); @@ -2832,7 +2832,7 @@ public bool TryGetValue(T equalValue, out T actualValue) // used for set checking operations (using enumerables) that rely on counting private static int Log2(int value) { - //Contract.Requires(value>0) + // Contract.Requires(value>0) int c = 0; while (value > 0) { @@ -2884,8 +2884,8 @@ public bool Equals(SortedSet x, SortedSet y) { return SortedSet.SortedSetEquals(x, y, comparer); } - //IMPORTANT: this part uses the fact that GetHashCode() is consistent with the notion of equality in - //the set + // IMPORTANT: this part uses the fact that GetHashCode() is consistent with the notion of equality in + // the set public int GetHashCode(SortedSet obj) { int hashCode = 0; diff --git a/src/IFoxCAD.Cad.Shared/Algorithms/Graph/Graph.cs b/src/IFoxCAD.Cad.Shared/Algorithms/Graph/Graph.cs index c1d606f..64e551b 100644 --- a/src/IFoxCAD.Cad.Shared/Algorithms/Graph/Graph.cs +++ b/src/IFoxCAD.Cad.Shared/Algorithms/Graph/Graph.cs @@ -130,11 +130,11 @@ public void RemoveVertex(Point3d pt) foreach (var item in edges.Values) { item.RemoveWhere(x => vertex.Equals(x.TargetVertex)); - //foreach (var edge in item) - //{ + // foreach (var edge in item) + // { // if (vertex.Equals(edge.TargetVertex)) // item.Remove(edge); - //} + // } } vertexs.Remove(str); } @@ -497,7 +497,7 @@ public sealed class DepthFirst public void FindAll(IGraph graph) { var total = new HashSet(); - //var graphtmp = graph.Clone(); + // var graphtmp = graph.Clone(); foreach (var item in graph.VerticesAsEnumberable) { Dfs(graph, new LinkedHashSet { item }, total); @@ -577,7 +577,7 @@ void Dfs(IGraph graph, List visited) var inv = Invert(cur,cur[0]); // O(n) var curstr = Gethashstring(cur,inv); - //Env.Print(curstr); + // Env.Print(curstr); if (Isnew(curstr)) { Curve3ds.Add(cur); diff --git a/src/IFoxCAD.Cad.Shared/Algorithms/QuadTree/QuadTree.cs b/src/IFoxCAD.Cad.Shared/Algorithms/QuadTree/QuadTree.cs index 0eb1bf8..7032b10 100644 --- a/src/IFoxCAD.Cad.Shared/Algorithms/QuadTree/QuadTree.cs +++ b/src/IFoxCAD.Cad.Shared/Algorithms/QuadTree/QuadTree.cs @@ -50,7 +50,7 @@ public class QuadTree where TEntity : QuadEntity /// 四叉树矩形范围 public QuadTree(Rect rect) { - _rootNode = new QuadTreeNode(rect, null, 0);//初始化根节点 + _rootNode = new QuadTreeNode(rect, null, 0);// 初始化根节点 _points = new(); } #endregion @@ -85,42 +85,42 @@ public void Insert(TEntity ent) var sq_Botton = _rootNode._Y; var sq_Right = _rootNode._Right; var sq_Top = _rootNode._Top; - if (ent._Y >= _rootNode._Y)//上↑增殖 + if (ent._Y >= _rootNode._Y)// 上↑增殖 { if (ent._X >= _rootNode._X) { - //右上↗增殖 + // 右上↗增殖 sq_Right += _rootNode.Width; sq_Top += _rootNode.Height; } else { - //左上↖增殖 + // 左上↖增殖 sq_Left -= _rootNode.Width; sq_Top += _rootNode.Height; } } - else//在下↓ + else// 在下↓ { if (ent._X >= _rootNode._X) { - //右下↘增殖 + // 右下↘增殖 sq_Right += _rootNode.Width; sq_Botton -= _rootNode.Height; } else { - //左下↙增殖 + // 左下↙增殖 sq_Left -= _rootNode.Width; sq_Botton -= _rootNode.Height; } } - //扩大2次方 + // 扩大2次方 var rectSquare = new Rect(sq_Left, sq_Botton, sq_Right, sq_Top); - //四叉树的旧根要作为四分之一插入 - //新根中计算原根 - //把 旧根节点 连接到 新根节点 上面,然后新根成为根 + // 四叉树的旧根要作为四分之一插入 + // 新根中计算原根 + // 把 旧根节点 连接到 新根节点 上面,然后新根成为根 var newRoot = new QuadTreeNode(rectSquare, null, 0); var insert = newRoot.Insert(_rootNode); if (insert is null) @@ -144,8 +144,8 @@ public void Insert(TEntity ent) else throw new("四叉树:新节点不对,无法连接"); - //其后的子节点层数全部增加层数, - //要加多少层取决于当前根边界属于新根边界的所在层 + // 其后的子节点层数全部增加层数, + // 要加多少层取决于当前根边界属于新根边界的所在层 var depth = insert.Depth; if (depth == 0) throw new("四叉树:插入节点是0,造成错误"); @@ -154,7 +154,7 @@ public void Insert(TEntity ent) return false; }); - //交换根控制 + // 交换根控制 _rootNode = newRoot; } @@ -172,9 +172,9 @@ public List Query(Rect rect, QuadTreeSelectMode selectMode = QuadTreeSe QuadTreeEvn.SelectMode = selectMode; var results = new List(); - //选择图元 + // 选择图元 _rootNode.Query(rect, results); - //选择点 + // 选择点 var ptge = _points.GetEnumerator(); switch (selectMode) { @@ -195,7 +195,7 @@ public List Query(Rect rect, QuadTreeSelectMode selectMode = QuadTreeSe results.Add(ptEnt); } else if (ptEnt._X > rect._Right) - break;//超过后面范围就break,因为红黑树已经排序 + break;// 超过后面范围就break,因为红黑树已经排序 } break; default: diff --git a/src/IFoxCAD.Cad.Shared/Algorithms/QuadTree/QuadTreeNode.cs b/src/IFoxCAD.Cad.Shared/Algorithms/QuadTree/QuadTreeNode.cs index f349a0a..017eace 100644 --- a/src/IFoxCAD.Cad.Shared/Algorithms/QuadTree/QuadTreeNode.cs +++ b/src/IFoxCAD.Cad.Shared/Algorithms/QuadTree/QuadTreeNode.cs @@ -129,13 +129,13 @@ public QuadTreeNode(Rect box, QuadTreeNode? parent, int depth) if (!Contains(rect)) return null; - //四叉树分裂,将当前节点分为四个子节点 + // 四叉树分裂,将当前节点分为四个子节点 if (NodesIsEmpty) CreateChildren(); - //当前节点边界 包含 图元包围盒 就插入 - //退出递归:4个节点都不完全包含 - //4个节点的上层 + // 当前节点边界 包含 图元包围盒 就插入 + // 退出递归:4个节点都不完全包含 + // 4个节点的上层 var nodes = Nodes; for (int i = 0; i < nodes.Length; i++) { @@ -160,31 +160,31 @@ public QuadTreeNode(Rect box, QuadTreeNode? parent, int depth) { if (!Contains(ent)) { - //Debug.WriteLine("不在四叉树边界范围"); - //Trace.WriteLine("不在四叉树边界范围"); + // Debug.WriteLine("不在四叉树边界范围"); + // Trace.WriteLine("不在四叉树边界范围"); return null; } // if (ent.IsPoint) // { - // //找到最后一层包含它的节点,然后加入它 - // //因此是跳过分裂矩形的,以免造成无限递归 + // // 找到最后一层包含它的节点,然后加入它 + // // 因此是跳过分裂矩形的,以免造成无限递归 // var minNode = GetMinNode(ent); // minNode.Contents.Add(ent); // return minNode; // } #if true2 - //方案二: - //内容数超过才分裂,防止树深度过高,但是多选过滤时候慢一点 + // 方案二: + // 内容数超过才分裂,防止树深度过高,但是多选过滤时候慢一点 if (Contents.Count > QuadTreeEvn.QuadTreeContentsCountSplit) { - //分裂出四个子节点 + // 分裂出四个子节点 if (_nodesIsEmpty) { CreateChildren(); - //分裂之后将当前层的内容扔到四个子节点, - //如果被压着,那么就不会扔到下面 + // 分裂之后将当前层的内容扔到四个子节点, + // 如果被压着,那么就不会扔到下面 for (int i = Contents.Count - 1; i >= 0; i--) { var minNode = GetMinNode(Contents[i].Box); @@ -194,8 +194,8 @@ public QuadTreeNode(Rect box, QuadTreeNode? parent, int depth) } else { - //没有分裂的话,就递归 - //退出递归:4个节点都不完全包含,内容就是他们的父亲 + // 没有分裂的话,就递归 + // 退出递归:4个节点都不完全包含,内容就是他们的父亲 var nodes = _Nodes; for (int i = 0; i < nodes.Length; i++) { @@ -203,35 +203,35 @@ public QuadTreeNode(Rect box, QuadTreeNode? parent, int depth) if (node is null) continue; - //这里需要中断.(匿名方法ForEach无法中断,会造成父节点加入内容) + // 这里需要中断.(匿名方法ForEach无法中断,会造成父节点加入内容) if (node.Contains(ent)) return node.Insert(ent); } } } #else - //方案一:分裂到最细节点 + // 方案一:分裂到最细节点 - //分裂出四个子节点 + // 分裂出四个子节点 if (NodesIsEmpty) CreateChildren(); - //4个子节点开始递归 - //退出递归:4个节点都不完全包含,内容就是他们的父亲 + // 4个子节点开始递归 + // 退出递归:4个节点都不完全包含,内容就是他们的父亲 var nodes = Nodes; for (int i = 0; i < nodes.Length; i++) { var node = nodes[i]; if (node is null) continue; - //这里需要中断.(匿名方法ForEach无法中断,会造成父节点加入内容) + // 这里需要中断.(匿名方法ForEach无法中断,会造成父节点加入内容) if (node.Contains(ent)) return node.Insert(ent); } #endif - //为什么要用容器? - //相同包围盒或者四叉树分割线压着多个. + // 为什么要用容器? + // 相同包围盒或者四叉树分割线压着多个. this.Contents.Add(ent); return this; } @@ -264,10 +264,10 @@ static Rect[] RectSplit(Rect box) var upperRight = new Rect(box._X + halfWidth, box._Y + halfHeight, box._Right, box._Top); var upperLeft = new Rect(box._X, box._Y + halfHeight, box._Right - halfWidth, box._Top); - var lowerleft = new Rect(box._X, box._Y, box._Right - halfWidth, box._Top - halfHeight);//基础 + var lowerleft = new Rect(box._X, box._Y, box._Right - halfWidth, box._Top - halfHeight);// 基础 var lowerRight = new Rect(box._X + halfWidth, box._Y, box._Right, box._Top - halfHeight); - //依照象限顺序输出 + // 依照象限顺序输出 return new Rect[] { upperRight, upperLeft, lowerleft, lowerRight }; } #endregion @@ -279,13 +279,13 @@ static Rect[] RectSplit(Rect box) /// 根据图元删除 public bool Remove(TEntity easeEnt) { - //通过图元id删除无疑是非常低效的, - //1.相当于在所有的容器查找它,但是移除只会移除一次, + // 通过图元id删除无疑是非常低效的, + // 1.相当于在所有的容器查找它,但是移除只会移除一次, // 因此必须要求图元只会加入一次,才能中断检索剩余分支. - //2.这个代价还是太高,因此我们还是要默认条件,图元载入一次之后,不再改动. - //3.不再改动也不太合理,因为cad图元还是可以修改的 + // 2.这个代价还是太高,因此我们还是要默认条件,图元载入一次之后,不再改动. + // 3.不再改动也不太合理,因为cad图元还是可以修改的 - //1.处理内容 + // 1.处理内容 if (Contents.Remove(easeEnt)) { if (CountSubTree == 0) @@ -293,15 +293,15 @@ public bool Remove(TEntity easeEnt) return true; } - //2.递归子节点移除 + // 2.递归子节点移除 var nodes = Nodes; for (int i = 0; i < nodes.Length; i++) { var node = nodes[i]; if (node is null) continue; - if (node.Remove(easeEnt)) //递归进入子节点删除内容 - return true; //删除成功就中断其他节点的搜索 + if (node.Remove(easeEnt)) // 递归进入子节点删除内容 + return true; // 删除成功就中断其他节点的搜索 } return false; } @@ -317,13 +317,13 @@ void Clear(QuadTreeNode node) nodes[i]?.Clear(nodes[i]); node.Contents.Clear(); - //node.Contents = null;//重复加入时候会出错 + // node.Contents = null;// 重复加入时候会出错 node.RightTopTree = null; node.LeftTopTree = null; node.LeftBottomTree = null; node.RightBottomTree = null; node.Parent = null; - //node.Box = zoreRect; + // node.Box = zoreRect; } /// @@ -332,23 +332,23 @@ void Clear(QuadTreeNode node) /// 根据范围删除 public void Remove(Rect queryArea) { - //本节点内容移除 - if (Contents is not null && Contents.Count > 0)//从最上层的根节点开始进入 + // 本节点内容移除 + if (Contents is not null && Contents.Count > 0)// 从最上层的根节点开始进入 { for (int i = Contents.Count - 1; i >= 0; i--) { var ent = Contents[i]; - //移除之后,如果容器是0,那么这里不能直接 Contents=null, - //因为此节点下面可能还有节点, - //需要判断了其后数量0才可以清理. - //否则其后还有内容,那么此节点就是仍然可以用的. + // 移除之后,如果容器是0,那么这里不能直接 Contents=null, + // 因为此节点下面可能还有节点, + // 需要判断了其后数量0才可以清理. + // 否则其后还有内容,那么此节点就是仍然可以用的. if (queryArea.Contains(ent)) Contents.Remove(ent); } } - //同插入一样 - //跳到指定节点再搜索这个节点下面的图元 + // 同插入一样 + // 跳到指定节点再搜索这个节点下面的图元 var nodes = Nodes; for (int i = 0; i < nodes.Length; i++) { @@ -358,27 +358,27 @@ public void Remove(Rect queryArea) if (node.NodesIsEmpty) continue; - //此节点边界 完全包含 查询区域,则转到该节点,并跳过其余节点(打断此循环) + // 此节点边界 完全包含 查询区域,则转到该节点,并跳过其余节点(打断此循环) if (node.Contains(queryArea)) { node.Remove(queryArea); break; } - //查询区域 完全包含 此节点边界,提取此节点全部内容 - //跳过分析碰撞,并继续循环搜索其他节点 + // 查询区域 完全包含 此节点边界,提取此节点全部内容 + // 跳过分析碰撞,并继续循环搜索其他节点 if (queryArea.Contains(node)) { node.Clear(node); continue; } - //查询区域 与 此节点四边形边线碰撞 查询该四边形中,并继续循环搜索其他节点 - //1,角点碰撞 2,边界碰撞 + // 查询区域 与 此节点四边形边线碰撞 查询该四边形中,并继续循环搜索其他节点 + // 1,角点碰撞 2,边界碰撞 if (node.IntersectsWith(queryArea)) node.Remove(queryArea); } - //本节点内容移除之后,旗下还有内容的话, - //会跳过此处,再进入子节点进行递归,直到最后一个节点 + // 本节点内容移除之后,旗下还有内容的话, + // 会跳过此处,再进入子节点进行递归,直到最后一个节点 if (CountSubTree == 0) Clear(this); } @@ -394,33 +394,33 @@ public void Query(Rect queryArea, List results) { GetCurrentContents(queryArea, results); - //遍历子节点 + // 遍历子节点 var nodes = Nodes; for (int i = 0; i < nodes.Length; i++) { var node = nodes[i]; if (node is null) continue; - //子节点的4个子节点都是空的, - //那么表示元素会在子节点这一层啊... + // 子节点的4个子节点都是空的, + // 那么表示元素会在子节点这一层啊... if (node.NodesIsEmpty) continue; - //此节点边界 完全包含 查询区域,则转到该节点,并跳过其余节点(打断此循环) + // 此节点边界 完全包含 查询区域,则转到该节点,并跳过其余节点(打断此循环) if (node.Contains(queryArea)) { node.Query(queryArea, results); break; } - //查询区域 完全包含 此节点边界,提取此节点全部内容 - //跳过分析碰撞,并继续循环搜索其他节点 + // 查询区域 完全包含 此节点边界,提取此节点全部内容 + // 跳过分析碰撞,并继续循环搜索其他节点 if (queryArea.Contains(node)) { node.ContentsSubTree(results); continue; } - //查询区域 与 此节点四边形边线碰撞 查询该四边形中,并继续循环搜索其他节点 - //1,角点碰撞 2,边界碰撞 + // 查询区域 与 此节点四边形边线碰撞 查询该四边形中,并继续循环搜索其他节点 + // 1,角点碰撞 2,边界碰撞 if (node.IntersectsWith(queryArea)) node.Query(queryArea, results); } @@ -433,7 +433,7 @@ public void Query(Rect queryArea, List results) /// void GetCurrentContents(Rect queryArea, List results) { - //遍历当前节点内容,加入方式取决于碰撞模式 + // 遍历当前节点内容,加入方式取决于碰撞模式 if (QuadTreeEvn.SelectMode == QuadTreeSelectMode.IntersectsWith) { for (int i = 0; i < Contents.Count; i++) @@ -462,11 +462,11 @@ void GetCurrentContents(Rect queryArea, List results) public TEntity? FindNearEntity(Rect queryArea) { TEntity? resultEntity = default; - //1.找到 查找矩形 所在的节点,利用此节点的矩形. + // 1.找到 查找矩形 所在的节点,利用此节点的矩形. var queryNode = GetMinNode(queryArea); var queryAreaCenter = queryArea.CenterPoint; - //2.从根开始搜索 + // 2.从根开始搜索 // 如果搜索父亲的父亲的...内容群,它不是距离最近的,只是节点(亲属关系)最近 // 储存找过的<图元,距离> var entDic = new Dictionary(); @@ -475,10 +475,10 @@ void GetCurrentContents(Rect queryArea, List results) QuadTreeEvn.SelectMode = QuadTreeSelectMode.IntersectsWith; while (true) { - //循环找父节点大小 + // 循环找父节点大小 var hw = queryNode.Width / 2.0; var hh = queryNode.Height / 2.0; - //3.利用选区中心扩展一个节点边界大小的矩形.从而选择图元 + // 3.利用选区中心扩展一个节点边界大小的矩形.从而选择图元 // 再判断图元的与目标的距离,找到最小距离,即为最近 var minPt = new Point2d(queryAreaCenter.X - hw, queryAreaCenter.Y - hh); var maxPt = new Point2d(queryAreaCenter.X + hw, queryAreaCenter.Y + hh); @@ -490,7 +490,7 @@ void GetCurrentContents(Rect queryArea, List results) if (entDic.ContainsKey(ent)) continue; var dis = ent.CenterPoint.GetDistanceTo(queryAreaCenter); - if (dis > 1e-6)//剔除本身 + if (dis > 1e-6)// 剔除本身 entDic.Add(ent, dis); } if (entDic.Count > 0) @@ -498,9 +498,9 @@ void GetCurrentContents(Rect queryArea, List results) resultEntity = entDic.OrderBy(a => a.Value).First().Key; break; } - if (queryNode.Parent is null)//最顶层就退出 + if (queryNode.Parent is null)// 最顶层就退出 break; - queryNode = queryNode.Parent;//利用父节点矩形进行变大选区 + queryNode = queryNode.Parent;// 利用父节点矩形进行变大选区 } QuadTreeEvn.SelectMode = old; return resultEntity; @@ -516,15 +516,15 @@ void GetCurrentContents(Rect queryArea, List results) public TEntity? FindNeibor(Rect queryArea, QuadTreeFindMode findMode) { TEntity? resultEntity = default; - //1.找到 查找矩形 所在的节点,利用此节点的矩形. - //2.利用节点矩形是分裂的特点,边和边必然贴合. - //3.找到方向 findMode 拥有的节点,然后查找节点的内容 + // 1.找到 查找矩形 所在的节点,利用此节点的矩形. + // 2.利用节点矩形是分裂的特点,边和边必然贴合. + // 3.找到方向 findMode 拥有的节点,然后查找节点的内容 var queryNode = GetMinNode(queryArea); bool whileFlag = true; - //同一个节点可能包含邻居,因为四叉树的加入是图元压线, - //那么就在这里搜就得了,用中心点决定空间位置 - //但是本空间的图元可能都比它矮,无法满足条件 + // 同一个节点可能包含邻居,因为四叉树的加入是图元压线, + // 那么就在这里搜就得了,用中心点决定空间位置 + // 但是本空间的图元可能都比它矮,无法满足条件 if (queryNode.CountSubTree > 1) { resultEntity = GetNearestNeighbor(queryNode, findMode, queryArea); @@ -536,11 +536,11 @@ void GetCurrentContents(Rect queryArea, List results) while (whileFlag) { - //同一个父节点是临近的,优先获取 兄弟节点 的内容. - //循环了第二次是北方兄弟的节点, - //但是这不是一个找到临近图元的方法, - //因为临近的可能是父亲的父亲的父亲...另一个函数 FindNearEntity 写 - //本方案也仅仅作为找北方节点 + // 同一个父节点是临近的,优先获取 兄弟节点 的内容. + // 循环了第二次是北方兄弟的节点, + // 但是这不是一个找到临近图元的方法, + // 因为临近的可能是父亲的父亲的父亲...另一个函数 FindNearEntity 写 + // 本方案也仅仅作为找北方节点 var parent = queryNode.Parent; if (parent is not null) { @@ -548,7 +548,7 @@ void GetCurrentContents(Rect queryArea, List results) { case QuadTreeFindMode.Top: { - //下格才获取上格,否则导致做了无用功,上格就直接获取邻居了 + // 下格才获取上格,否则导致做了无用功,上格就直接获取邻居了 if (parent.LeftBottomTree == queryNode || parent.RightBottomTree == queryNode) resultEntity = GetNearestNeighbor(parent, findMode, queryArea); } @@ -576,9 +576,9 @@ void GetCurrentContents(Rect queryArea, List results) if (resultEntity is not null) break; - //通过 所在节点 找 邻居节点, - //拿到 邻居节点 下面的所有内容(图元) - //内容可能是空的,再从邻居那往北找...如果找到了四叉树最外层,仍然没有内容,退出循环 + // 通过 所在节点 找 邻居节点, + // 拿到 邻居节点 下面的所有内容(图元) + // 内容可能是空的,再从邻居那往北找...如果找到了四叉树最外层,仍然没有内容,退出循环 var neiborNode = FindNeiborNode(queryNode, findMode); if (neiborNode is null) continue; @@ -587,7 +587,7 @@ void GetCurrentContents(Rect queryArea, List results) resultEntity = GetNearestNeighbor(neiborNode, findMode, queryArea); break; } - if (neiborNode.Parent is null)//如果找到了四叉树最外层,仍然没有内容,退出循环 + if (neiborNode.Parent is null)// 如果找到了四叉树最外层,仍然没有内容,退出循环 break; queryNode = neiborNode; } @@ -615,7 +615,7 @@ void GetCurrentContents(Rect queryArea, List results) { case QuadTreeFindMode.Top: { - //取出Y比queryArea的还大的一个,是最近的那个 + // 取出Y比queryArea的还大的一个,是最近的那个 var qy = qcent.Y; queryNode.ContentsSubTree(lst); lst.ForEach(ent => @@ -665,7 +665,7 @@ void GetCurrentContents(Rect queryArea, List results) } if (lst.Count > 0) - return lst[0];//可能就是本体重叠 + return lst[0];// 可能就是本体重叠 return results; } @@ -683,10 +683,10 @@ QuadTreeNode GetMinNode(Rect queryArea) if (node is null) continue; - //边界包含查询面积,那么再递归查询, - //直到最后四个都不包含,那么上一个就是图元所在节点 + // 边界包含查询面积,那么再递归查询, + // 直到最后四个都不包含,那么上一个就是图元所在节点 if (node.Contains(queryArea)) - return node.GetMinNode(queryArea);//中断后面的范围,才可以返回正确的 + return node.GetMinNode(queryArea);// 中断后面的范围,才可以返回正确的 } return this; } @@ -706,19 +706,19 @@ QuadTreeNode GetMinNode(Rect queryArea) { case QuadTreeFindMode.Top: { - //判断当前节点在父节点的位置,如果是在 下格 就取对应的 上格 + // 判断当前节点在父节点的位置,如果是在 下格 就取对应的 上格 if (tar == parent.LeftBottomTree) return parent.LeftTopTree; if (tar == parent.RightBottomTree) return parent.RightTopTree; - //否则就是上格 - //找父节点的北方邻居..也就是在爷节点上面找 - //递归,此时必然是 下格,就必然返回 上格,然后退出递归 + // 否则就是上格 + // 找父节点的北方邻居..也就是在爷节点上面找 + // 递归,此时必然是 下格,就必然返回 上格,然后退出递归 var parentNeibor = FindNeiborNode(parent, QuadTreeFindMode.Top); - //父节点的北方邻居 无 子节点 + // 父节点的北方邻居 无 子节点 if (parentNeibor is null || parentNeibor.RightTopTree is null) - return parentNeibor;//返回父节点的北方邻居,比较大 - //父节点的北方邻居 有 子节点,剩下条件就只有这两 + return parentNeibor;// 返回父节点的北方邻居,比较大 + // 父节点的北方邻居 有 子节点,剩下条件就只有这两 // 如果直接返回,那么是(相同或更大), // 而找邻近图元需要的是这个(相同或更大)下面的图元,在外面对这个格子内图元用坐标分析就好了 @@ -770,8 +770,8 @@ QuadTreeNode GetMinNode(Rect queryArea) /// /// 所有的点归类到最小包围它的空间 /// - //public void PointsToMinNode() - //{ + // public void PointsToMinNode() + // { // ForEach(node => // { // for (int i = 0; i < node.Contents.Count; i++) @@ -779,7 +779,7 @@ QuadTreeNode GetMinNode(Rect queryArea) // var ent = node.Contents[i]; // if (ent.IsPoint) // { - // //如果最小包含!=当前,就是没有放在最适合的位置 + // // 如果最小包含!=当前,就是没有放在最适合的位置 // var queryNode = GetMinNode(ent); // if (queryNode != node) // { @@ -790,7 +790,7 @@ QuadTreeNode GetMinNode(Rect queryArea) // } // return false; // }); - //} + // } #endregion #region 方法 @@ -800,11 +800,11 @@ QuadTreeNode GetMinNode(Rect queryArea) /// QTAction public bool ForEach(QuadTree.QTAction action) { - //执行本节点 + // 执行本节点 if (action(this)) return true; - //递归执行本节点的子节点 + // 递归执行本节点的子节点 var nodes = Nodes; for (int i = 0; i < nodes.Length; i++) { diff --git a/src/IFoxCAD.Cad.Shared/Algorithms/QuadTree/QuadTreeSelectMode.cs b/src/IFoxCAD.Cad.Shared/Algorithms/QuadTree/QuadTreeSelectMode.cs index d180236..2cc2e37 100644 --- a/src/IFoxCAD.Cad.Shared/Algorithms/QuadTree/QuadTreeSelectMode.cs +++ b/src/IFoxCAD.Cad.Shared/Algorithms/QuadTree/QuadTreeSelectMode.cs @@ -5,8 +5,8 @@ /// public enum QuadTreeSelectMode { - IntersectsWith, //碰撞到就选中 - Contains, //全包含才选中 + IntersectsWith, // 碰撞到就选中 + Contains, // 全包含才选中 } /// @@ -14,8 +14,8 @@ public enum QuadTreeSelectMode /// public enum QuadTreeFindMode { - Top = 1, //上 - Bottom = 2, //下 - Left = 4, //左 - Right = 8, //右 + Top = 1, // 上 + Bottom = 2, // 下 + Left = 4, // 左 + Right = 8, // 右 } \ No newline at end of file diff --git a/src/IFoxCAD.Cad.Shared/Algorithms/QuadTree/Rect.cs b/src/IFoxCAD.Cad.Shared/Algorithms/QuadTree/Rect.cs index c533ddb..7639104 100644 --- a/src/IFoxCAD.Cad.Shared/Algorithms/QuadTree/Rect.cs +++ b/src/IFoxCAD.Cad.Shared/Algorithms/QuadTree/Rect.cs @@ -13,7 +13,7 @@ public TolerancePoint2d(double tolerance = 1e-6) _tolerance = tolerance; } - public bool Equals(Point2d a, Point2d b)//Point3d是struct不会为null + public bool Equals(Point2d a, Point2d b)// Point3d是struct不会为null { /*默认规则是==是0容差,Eq是有容差*/ // 方形限定 @@ -30,9 +30,9 @@ public bool Equals(Point2d a, Point2d b)//Point3d是struct不会为null public int GetHashCode(Point2d obj) { - //结构体直接返回 obj.GetHashCode(); Point3d ToleranceDistinct3d - //因为结构体是用可值叠加来判断?或者因为结构体兼备了一些享元模式的状态? - //而类是构造的指针,所以取哈希值要改成x+y+z..s给Equals判断用,+是会溢出,所以用^ + // 结构体直接返回 obj.GetHashCode(); Point3d ToleranceDistinct3d + // 因为结构体是用可值叠加来判断?或者因为结构体兼备了一些享元模式的状态? + // 而类是构造的指针,所以取哈希值要改成x+y+z..s给Equals判断用,+是会溢出,所以用^ return (int)obj.X ^ (int)obj.Y;// ^ (int)obj.Z; } } @@ -53,9 +53,9 @@ public class Rect : IEquatable, IComparable #pragma warning restore CA2211 // 非常量字段应当不可见 #region 字段 - //这里的成员不要用{get}封装成属性,否则会导致跳转了一次函数, - //10w图元将会从187毫秒变成400毫秒 - //不用 protected 否则子类传入Rect对象进来无法用 + // 这里的成员不要用{get}封装成属性,否则会导致跳转了一次函数, + // 10w图元将会从187毫秒变成400毫秒 + // 不用 protected 否则子类传入Rect对象进来无法用 internal double _X; internal double _Y; internal double _Right; @@ -195,12 +195,12 @@ public bool Equals(Rect? b) } public static bool operator ==(Rect? a, Rect? b) { - //此处地方不允许使用==null,因为此处是定义 + // 此处地方不允许使用==null,因为此处是定义 if (b is null) return a is null; else if (a is null) return false; - if (ReferenceEquals(a, b))//同一对象 + if (ReferenceEquals(a, b))// 同一对象 return true; return a.Equals(b, 0); @@ -213,7 +213,7 @@ public bool Equals(Rect? b, double tolerance = 1e-6) { if (b is null) return false; - if (ReferenceEquals(this, b)) //同一对象 + if (ReferenceEquals(this, b)) // 同一对象 return true; return Math.Abs(_X - b._X) < tolerance && @@ -274,18 +274,18 @@ public Point2d[] GetCommonPoint(Rect other) public Point2d[] ToPoints() { - Point2d a = MinPoint;//min + Point2d a = MinPoint;// min Point2d b = new(_Right, _Y); - Point2d c = MaxPoint;//max + Point2d c = MaxPoint;// max Point2d d = new(_X, _Top); return new Point2d[] { a, b, c, d }; } public (Point2d boxMin, Point2d boxRigthDown, Point2d boxMax, Point2d boxLeftUp) ToPoints4() { - Point2d a = MinPoint;//min + Point2d a = MinPoint;// min Point2d b = new(_Right, _Y); - Point2d c = MaxPoint;//max + Point2d c = MaxPoint;// max Point2d d = new(_X, _Top); return (a, b, c, d); } @@ -311,21 +311,21 @@ public static bool IsRectAngle(List? ptList, double tolerance = 1e-8) var pts = ptList.ToList(); /* 消重,不这里设置,否则这不是一个正确的单元测试 - * //var ptList = pts.Distinct().ToList(); + * // var ptList = pts.Distinct().ToList(); * var ptList = pts.DistinctExBy((a, b) => a.DistanceTo(b) < 1e-6).ToList(); */ if (ptList.Count == 5) { - //首尾点相同移除最后 + // 首尾点相同移除最后 if (pts[0].IsEqualTo(pts[^1], CadTolerance)) pts.RemoveAt(pts.Count - 1); } if (pts.Count != 4) return false; - //最快的方案 - //点乘求值法:(为了处理 正梯形/平行四边形 需要三次) - //这里的容差要在1e-8内,因为点乘的三次浮点数乘法会令精度变低 + // 最快的方案 + // 点乘求值法:(为了处理 正梯形/平行四边形 需要三次) + // 这里的容差要在1e-8内,因为点乘的三次浮点数乘法会令精度变低 var dot = DotProductValue(pts[0], pts[1], pts[3]); if (Math.Abs(dot) < tolerance) { @@ -366,7 +366,7 @@ public static bool IsRect(List? ptList, double tolerance = 1e-10) var pts = ptList.ToList(); if (ptList.Count == 5) { - //首尾点相同移除最后 + // 首尾点相同移除最后 if (pts[0].IsEqualTo(pts[^1], CadTolerance)) pts.RemoveAt(pts.Count - 1); } @@ -389,16 +389,16 @@ public static (Point2d boxMin, Point2d boxMax) GetMinMax(IEnumerable pt var xMax = double.MinValue; var yMin = double.MaxValue; var yMax = double.MinValue; - //var zMin = double.MaxValue; - //var zMax = double.MinValue; + // var zMin = double.MaxValue; + // var zMax = double.MinValue; pts.ForEach(p => { xMin = Math.Min(p.X, xMin); xMax = Math.Max(p.X, xMax); yMin = Math.Min(p.Y, yMin); yMax = Math.Max(p.Y, yMax); - //zMin = Math.Min(p.Z, zMin); - //zMax = Math.Max(p.Z, zMax); + // zMin = Math.Min(p.Z, zMin); + // zMax = Math.Max(p.Z, zMax); }); return (new Point2d(xMin, yMin), new Point2d(xMax, yMax)); } @@ -416,7 +416,7 @@ public static bool RectAnglePointOrder(List? pts) if (!Rect.IsRectAngle(pts)) return false; - //获取min和max点(非包围盒) + // 获取min和max点(非包围盒) pts = pts.OrderBy(a => a.X).ThenBy(a => a.Y).ToList(); var minPt = pts.First(); var maxPt = pts.Last(); @@ -424,14 +424,14 @@ public static bool RectAnglePointOrder(List? pts) link.AddRange(pts); pts.Clear(); - //排序这四个点,左下/右下/右上/左上 + // 排序这四个点,左下/右下/右上/左上 var node = link.Find(minPt); for (int i = 0; i < 4; i++) { pts.Add(node!.Value); node = node.Next; } - //保证是逆时针 + // 保证是逆时针 var isAcw = CrossAclockwise(pts[0], pts[1], pts[2]); if (!isAcw) (pts[3], pts[1]) = (pts[1], pts[3]); @@ -470,7 +470,7 @@ static double Cross(Point2d o, Point2d a, Point2d b) /// b点在oa的逆时针 static bool CrossAclockwise(Point2d o, Point2d a, Point2d b) { - return Cross(o, a, b) > -1e-6;//浮点数容差考虑 + return Cross(o, a, b) > -1e-6;// 浮点数容差考虑 } #if !WinForm @@ -501,11 +501,11 @@ public static void XCollision(List box, Func collisionProcessing, Action lastProcessing) where T : Rect { - //先排序X:不需要Y排序,因为Y的上下浮动不共X .ThenBy(a => a.Box.Y) - //因为先排序就可以有序遍历x区间,超过就break,达到更快 + // 先排序X:不需要Y排序,因为Y的上下浮动不共X .ThenBy(a => a.Box.Y) + // 因为先排序就可以有序遍历x区间,超过就break,达到更快 box = box.OrderBy(a => a._X).ToList(); - //遍历所有图元 + // 遍历所有图元 for (int i = 0; i < box.Count; i++) { var oneRect = box[i]; @@ -514,14 +514,14 @@ public static void XCollision(List box, bool actionlast = true; - //搜索范围要在 one 的头尾中间的部分 + // 搜索范围要在 one 的头尾中间的部分 for (int j = i + 1; j < box.Count; j++) { var twoRect = box[j]; - //x碰撞:矩形2的Left 在 矩形1[Left-Right]闭区间;穿过的话,也必然有自己的Left因此不需要处理 + // x碰撞:矩形2的Left 在 矩形1[Left-Right]闭区间;穿过的话,也必然有自己的Left因此不需要处理 if (oneRect._X <= twoRect._X && twoRect._X <= oneRect._Right) { - //y碰撞,那就是真的碰撞了 + // y碰撞,那就是真的碰撞了 if ((oneRect._Top >= twoRect._Top && twoRect._Top >= oneRect._Y) /*包容上边*/ || (oneRect._Top >= twoRect._Y && twoRect._Y >= oneRect._Y) /*包容下边*/ || (twoRect._Top >= oneRect._Top && oneRect._Y >= twoRect._Y)) /*穿过*/ @@ -529,13 +529,13 @@ public static void XCollision(List box, if (collisionProcessing(oneRect, twoRect)) actionlast = false; } - //这里想中断y高过它的无意义比较, - //但是必须排序Y,而排序Y必须同X,而这里不是同X(而是同X区间),所以不能中断 - //而做到X区间排序,就必须创造一个集合,再排序这个集合, - //会导致每个图元都拥有一次X区间集合,开销更巨大(因此放弃). + // 这里想中断y高过它的无意义比较, + // 但是必须排序Y,而排序Y必须同X,而这里不是同X(而是同X区间),所以不能中断 + // 而做到X区间排序,就必须创造一个集合,再排序这个集合, + // 会导致每个图元都拥有一次X区间集合,开销更巨大(因此放弃). } else - break;//因为已经排序了,后续的必然超过 x碰撞区间 + break;// 因为已经排序了,后续的必然超过 x碰撞区间 } if (actionlast) @@ -548,10 +548,10 @@ public static void XCollision(List box, #region 转换类型 #if !WinForm // 隐式转换(相当于是重载赋值运算符) - //public static implicit operator Rect(System.Windows.Rect rect) - //{ + // public static implicit operator Rect(System.Windows.Rect rect) + // { // return new Rect(rect.Left, rect.Bottom, rect.Right, rect.Top); - //} + // } public static implicit operator Rect(System.Drawing.RectangleF rect) { return new Rect(rect.Left, rect.Bottom, rect.Right, rect.Top); diff --git a/src/IFoxCAD.Cad.Shared/Algorithms/QuadTree/Utility.cs b/src/IFoxCAD.Cad.Shared/Algorithms/QuadTree/Utility.cs index 2a0be8e..4c0d297 100644 --- a/src/IFoxCAD.Cad.Shared/Algorithms/QuadTree/Utility.cs +++ b/src/IFoxCAD.Cad.Shared/Algorithms/QuadTree/Utility.cs @@ -28,10 +28,10 @@ public static Random GetRandom() * 解释代码: * 0x01: * (int)(long.MaxValue & 0xffffffffL) | (int)(long.MaxValue >> 32); - * Convert.ToString(long.MaxValue & 0xffffffffL, 2)//去掉高位:"11111111111111111111111111111111" 32个,再强转int + * Convert.ToString(long.MaxValue & 0xffffffffL, 2)// 去掉高位:"11111111111111111111111111111111" 32个,再强转int * 按位与&是保证符号位肯定是1,其他尽可能为0,高位被去掉只是MaxValue&0的原因,强转才是去掉高位..."尽可能"一词带来第一次随机性 * 0x02: - * Convert.ToString((long.MaxValue >> 32), 2) //去掉低位: "1111111111111111111111111111111" 31个,再强转int + * Convert.ToString((long.MaxValue >> 32), 2) // 去掉低位: "1111111111111111111111111111111" 31个,再强转int * 按位或|是尽可能为1..."尽可能"一词带来第二次随机性 * */ @@ -52,7 +52,7 @@ public static System.Drawing.Color RandomColor int R = ran.Next(255); int G = ran.Next(255); int B = ran.Next(255); - B = (R + G > 400) ? R + G - 400 : B;//0 : 380 - R - G; + B = (R + G > 400) ? R + G - 400 : B;// 0 : 380 - R - G; B = (B > 255) ? 255 : B; return System.Drawing.Color.FromArgb(R, G, B); } diff --git a/src/IFoxCAD.Cad.Shared/ExtensionMethod/BulgeVertexWidth.cs b/src/IFoxCAD.Cad.Shared/ExtensionMethod/BulgeVertexWidth.cs index b09ee3f..1f9e08d 100644 --- a/src/IFoxCAD.Cad.Shared/ExtensionMethod/BulgeVertexWidth.cs +++ b/src/IFoxCAD.Cad.Shared/ExtensionMethod/BulgeVertexWidth.cs @@ -70,7 +70,7 @@ public BulgeVertexWidth(BulgeVertex bv) /// 子段编号 public BulgeVertexWidth(Polyline pl, int index) { - var pt = pl.GetPoint2dAt(index);//这里可以3d + var pt = pl.GetPoint2dAt(index);// 这里可以3d X = pt.X; Y = pt.Y; Bulge = pl.GetBulgeAt(index); diff --git a/src/IFoxCAD.Cad.Shared/ExtensionMethod/Curve2dEx.cs b/src/IFoxCAD.Cad.Shared/ExtensionMethod/Curve2dEx.cs index 51ff4e2..c1c06c6 100644 --- a/src/IFoxCAD.Cad.Shared/ExtensionMethod/Curve2dEx.cs +++ b/src/IFoxCAD.Cad.Shared/ExtensionMethod/Curve2dEx.cs @@ -121,7 +121,7 @@ public static Arc ToArc(this CircularArc2d a2d) #region EllipticalArc2d - //椭圆弧 + // 椭圆弧 /// /// 将二维解析类椭圆弧转换为实体椭圆弧,然后进行矩阵变换 /// diff --git a/src/IFoxCAD.Cad.Shared/ExtensionMethod/Curve3dEx.cs b/src/IFoxCAD.Cad.Shared/ExtensionMethod/Curve3dEx.cs index 57ca8fd..f5ebdab 100644 --- a/src/IFoxCAD.Cad.Shared/ExtensionMethod/Curve3dEx.cs +++ b/src/IFoxCAD.Cad.Shared/ExtensionMethod/Curve3dEx.cs @@ -148,7 +148,7 @@ public static bool IsCircular(this Curve3d curve) /// 三维复合曲线列表 public static List GetSplitCurves(this CompositeCurve3d c3d, List pars) { - //曲线参数剔除重复的 + // 曲线参数剔除重复的 pars.Sort(); for (int i = pars.Count - 1; i > 0; i--) { @@ -159,20 +159,20 @@ public static List GetSplitCurves(this CompositeCurve3d c3d, L if (pars.Count == 0) return new List(); - //这个是曲线参数类 + // 这个是曲线参数类 var inter = c3d.GetInterval(); - //曲线们 + // 曲线们 var c3ds = c3d.GetCurves(); if (c3ds.Length == 1 && c3ds[0].IsClosed()) { - //闭合曲线不允许打断于一点 + // 闭合曲线不允许打断于一点 if (pars.Count > 1) { - //如果包含起点 + // 如果包含起点 if (Tolerance.Global.IsEqualPoint(pars[0], inter.LowerBound)) { pars[0] = inter.LowerBound; - //又包含终点,去除终点 + // 又包含终点,去除终点 if (Tolerance.Global.IsEqualPoint(pars[^1], inter.UpperBound)) { pars.RemoveAt(pars.Count - 1); @@ -184,7 +184,7 @@ public static List GetSplitCurves(this CompositeCurve3d c3d, L { pars[^1] = inter.UpperBound; } - //加入第一点以支持反向打断 + // 加入第一点以支持反向打断 pars.Add(pars[0]); } else @@ -194,7 +194,7 @@ public static List GetSplitCurves(this CompositeCurve3d c3d, L } else { - //非闭合曲线加入起点和终点 + // 非闭合曲线加入起点和终点 if (Tolerance.Global.IsEqualPoint(pars[0], inter.LowerBound)) pars[0] = inter.LowerBound; else @@ -209,7 +209,7 @@ public static List GetSplitCurves(this CompositeCurve3d c3d, L for (int i = 0; i < pars.Count - 1; i++) { List cc3ds = new(); - //复合曲线参数转换到包含曲线参数 + // 复合曲线参数转换到包含曲线参数 var cp1 = c3d.GlobalToLocalParameter(pars[i]); var cp2 = c3d.GlobalToLocalParameter(pars[i + 1]); if (cp1.SegmentIndex == cp2.SegmentIndex) @@ -404,7 +404,7 @@ public static Circle ToCircle(this CircularArc3d ca3d) => /// 实体圆弧 public static Arc ToArc(this CircularArc3d ca3d) { - //必须新建,而不能直接使用GetPlane()获取 + // 必须新建,而不能直接使用GetPlane()获取 double angle = ca3d.ReferenceVector.AngleOnPlane(new Plane(ca3d.Center, ca3d.Normal)); return new Arc(ca3d.Center, ca3d.Normal, ca3d.Radius, ca3d.StartAngle + angle, ca3d.EndAngle + angle); } @@ -462,7 +462,7 @@ public static Ellipse ToCurve(this EllipticalArc3d ea3d) ea3d.MinorRadius / ea3d.MajorRadius, 0, Math.PI * 2); - //Ge椭圆角度就是Db椭圆的参数 + // Ge椭圆角度就是Db椭圆的参数 if (!ea3d.IsClosed()) { ell.StartAngle = ell.GetAngleAtParameter(ea3d.StartAngle); diff --git a/src/IFoxCAD.Cad.Shared/ExtensionMethod/CurveEx.cs b/src/IFoxCAD.Cad.Shared/ExtensionMethod/CurveEx.cs index 03fab5f..41aad25 100644 --- a/src/IFoxCAD.Cad.Shared/ExtensionMethod/CurveEx.cs +++ b/src/IFoxCAD.Cad.Shared/ExtensionMethod/CurveEx.cs @@ -118,7 +118,7 @@ public static IEnumerable GetAllCycle(this IEnumerable curves) graph.AddEdge(curve.GetGeCurve()); #endif } - //新建 dfs + // 新建 dfs var dfs = new DepthFirst(); // 查询全部的 闭合环 dfs.FindAll(graph); @@ -156,14 +156,14 @@ public static List BreakCurve(this List curves) } } - //var oldCurves = new List(); + // var oldCurves = new List(); var newCurves = new List(); var cci3d = new CurveCurveIntersector3d(); for (int i = 0; i < curves.Count; i++) { var gc1 = geCurves[i]; - var pars1 = paramss[i]; //引用 + var pars1 = paramss[i]; // 引用 for (int j = i; j < curves.Count; j++) { var gc2 = geCurves[j]; @@ -193,7 +193,7 @@ public static List BreakCurve(this List curves) newCurves.Add(c3dCur); } } - //oldCurves.Add(curves[i]); + // oldCurves.Add(curves[i]); } } } @@ -201,7 +201,7 @@ public static List BreakCurve(this List curves) return newCurves; } - //转换DBCurve为GeCurved + // 转换DBCurve为GeCurved #region Curve @@ -475,9 +475,9 @@ public static NurbCurve3d ToCurve3d(this Spline spl) return ToNurbCurve3d(pl2d); } - //Polyline pl = new Polyline(); - //pl.ConvertFrom(pl2d, false); - //return ToCurve3d(pl); + // Polyline pl = new Polyline(); + // pl.ConvertFrom(pl2d, false); + // return ToCurve3d(pl); } /// @@ -644,7 +644,7 @@ public static void ChamferAt(this Polyline polyline, int index, double radius, b SegmentType.Line != polyline.GetSegmentType(index)) throw new System.Exception("非直线段不能倒角"); - //获取当前索引号的前后两段直线,并组合为Ge复合曲线 + // 获取当前索引号的前后两段直线,并组合为Ge复合曲线 Curve3d[] c3ds = new Curve3d[] { @@ -653,11 +653,11 @@ public static void ChamferAt(this Polyline polyline, int index, double radius, b }; CompositeCurve3d cc3d = new(c3ds); - //试倒直角 - //子曲线的个数有三种情况: - //1、=3时倒角方向正确 - //2、=2时倒角方向相反 - //3、=0或为直线时失败 + // 试倒直角 + // 子曲线的个数有三种情况: + // 1、=3时倒角方向正确 + // 2、=2时倒角方向相反 + // 3、=0或为直线时失败 c3ds = cc3d.GetTrimmedOffset ( @@ -691,7 +691,7 @@ public static void ChamferAt(this Polyline polyline, int index, double radius, b throw new System.Exception("倒角半径过大"); } - //GetTrimmedOffset会生成倒角+偏移,故先反方向倒角,再倒回 + // GetTrimmedOffset会生成倒角+偏移,故先反方向倒角,再倒回 c3ds = cc3d.GetTrimmedOffset ( -radius, @@ -708,7 +708,7 @@ public static void ChamferAt(this Polyline polyline, int index, double radius, b type ); - //将结果Ge曲线转为Db曲线,并将相关的数值反映到原曲线 + // 将结果Ge曲线转为Db曲线,并将相关的数值反映到原曲线 var plTemp = c3ds[0].ToCurve() as Polyline; if (plTemp is null) return; diff --git a/src/IFoxCAD.Cad.Shared/ExtensionMethod/DBDictionaryEx.cs b/src/IFoxCAD.Cad.Shared/ExtensionMethod/DBDictionaryEx.cs index 89dc2d5..719d7df 100644 --- a/src/IFoxCAD.Cad.Shared/ExtensionMethod/DBDictionaryEx.cs +++ b/src/IFoxCAD.Cad.Shared/ExtensionMethod/DBDictionaryEx.cs @@ -258,10 +258,10 @@ public static void SetValue(this DataCell cell, CellType type, object value) ///// 是否创建子字典 ///// 键值列表 ///// 字典 - //public static DBDictionary GetSubDictionary(this DBObject obj, bool createSubDictionary, params string[] dictNames) - //{ + // public static DBDictionary GetSubDictionary(this DBObject obj, bool createSubDictionary, params string[] dictNames) + // { // return obj.GetXDictionary().GetSubDictionary(createSubDictionary, dictNames); - //} + // } #endregion diff --git a/src/IFoxCAD.Cad.Shared/ExtensionMethod/DBObjectEx.cs b/src/IFoxCAD.Cad.Shared/ExtensionMethod/DBObjectEx.cs index 4a3237a..a8590f5 100644 --- a/src/IFoxCAD.Cad.Shared/ExtensionMethod/DBObjectEx.cs +++ b/src/IFoxCAD.Cad.Shared/ExtensionMethod/DBObjectEx.cs @@ -20,9 +20,9 @@ static List GetXdataAppIndex(XDataList data, string appName, DxfCode[] dxfC { List acIndex = new(); int appNameIndex = -1; - //int appNameIndexNext = -1; + // int appNameIndexNext = -1; - //先找到属于它的名字索引,然后再找到下一个不属于它名字的索引,移除中间部分 + // 先找到属于它的名字索引,然后再找到下一个不属于它名字的索引,移除中间部分 for (int i = 0; i < data.Count; i++) { if (data[i].TypeCode == (short)DxfCode.ExtendedDataRegAppName) @@ -34,22 +34,22 @@ static List GetXdataAppIndex(XDataList data, string appName, DxfCode[] dxfC } if (appNameIndex != -1) { - //找到了后面的appName - //appNameIndexNext = i; + // 找到了后面的appName + // appNameIndexNext = i; break; } } - if (appNameIndex != -1 && //找next的时候,获取任务(移除)的对象 + if (appNameIndex != -1 && // 找next的时候,获取任务(移除)的对象 dxfCodes.Contains((DxfCode)data[i].TypeCode)) acIndex.Add(i); } - //当前app索引 - //if (appNameIndex == -1) - //return; + // 当前app索引 + // if (appNameIndex == -1) + // return; - //下一个app索引,如果是空,就为末尾 - //if (appNameIndexNext == -1) + // 下一个app索引,如果是空,就为末尾 + // if (appNameIndexNext == -1) // appNameIndexNext = data.Count; return acIndex; @@ -67,8 +67,8 @@ public static void RemoveXData(this DBObject obj, string appName, DxfCode dxfCod return; XDataList data = obj.XData; - //测试命令 addxdata removexdata - //移除指定App的扩展 + // 测试命令 addxdata removexdata + // 移除指定App的扩展 var indexs = GetXdataAppIndex(data, appName, new DxfCode[] { dxfCode }); if (indexs.Count == 0) return; diff --git a/src/IFoxCAD.Cad.Shared/ExtensionMethod/EditorEx.cs b/src/IFoxCAD.Cad.Shared/ExtensionMethod/EditorEx.cs index bd84323..8e40ebf 100644 --- a/src/IFoxCAD.Cad.Shared/ExtensionMethod/EditorEx.cs +++ b/src/IFoxCAD.Cad.Shared/ExtensionMethod/EditorEx.cs @@ -107,13 +107,13 @@ public static SelectionSet SelectByLineWeight(this Editor editor, LineWeight lin /* - * //定义选择集选项 + * // 定义选择集选项 * var pso = new PromptSelectionOptions * { - * AllowDuplicates = false, //重复选择 + * AllowDuplicates = false, // 重复选择 * }; * - * //getai遍历全图选择块有用到 + * // getai遍历全图选择块有用到 * var dic = new Dictionary() { * { "Z,全部同名", ()=> { * getai = BlockHelper.EnumAttIdentical.AllBlockName; @@ -125,14 +125,14 @@ public static SelectionSet SelectByLineWeight(this Editor editor, LineWeight lin * { "V,属性值-默认", ()=> { * getai = BlockHelper.EnumAttIdentical.DisplayAndTagText; * }}, - * //允许以下操作,相同的会加入前面的 - * //{ "V,属性值-默认|X,啊啊啊啊", ()=> { + * // 允许以下操作,相同的会加入前面的 + * // { "V,属性值-默认|X,啊啊啊啊", ()=> { * - * //}}, + * // }}, * }; * pso.SsgetAddKeys(dic); * - * //创建选择集过滤器,只选择块对象 + * // 创建选择集过滤器,只选择块对象 * var filList = new TypedValue[] { new TypedValue((int)DxfCode.Start, "INSERT") }; * var filter = new SelectionFilter(filList); * ssPsr = ed.GetSelection(pso, filter); @@ -176,7 +176,7 @@ public static void SsgetAddKeys(this PromptSelectionOptions pso, foreach (var item in tmp) dicActions.Add(item.Key, item.Value); - //去除关键字重复的,把重复的执行动作移动到前面 + // 去除关键字重复的,把重复的执行动作移动到前面 for (int i = 0; i < dicActions.Count; i++) { var pair1 = dicActions.ElementAt(i); @@ -204,18 +204,18 @@ public static void SsgetAddKeys(this PromptSelectionOptions pso, keySplitS[i + 1] + "(" + keySplitS[i] + ")"); } - //回调的时候我想用Dict的O(1)索引, - //但是此函数内进行new Dictionary() 在函数栈释放的时候,它被释放掉了. - //因此 dicActions 参数的生命周期 + // 回调的时候我想用Dict的O(1)索引, + // 但是此函数内进行new Dictionary() 在函数栈释放的时候,它被释放掉了. + // 因此 dicActions 参数的生命周期 tmp = new(dicActions); dicActions.Clear(); foreach (var item in tmp) dicActions.Add(item.Key.Split(',')[0], item.Value); var keyWords = pso.Keywords; - //从选择集命令中显示关键字 + // 从选择集命令中显示关键字 pso.MessageForAdding = keyWords.GetDisplayString(true); - //关键字回调事件 ssget关键字 + // 关键字回调事件 ssget关键字 pso.KeywordInput += (sender, e) => { dicActions[e.Input].Invoke(); }; @@ -226,111 +226,108 @@ public static void SsgetAddKeys(this PromptSelectionOptions pso, - //#region 即时选择样板 - - ///// - ///// 即时选择,框选更新关键字 - ///// - //public static void SelectTest() - //{ - // Env.Editor.WriteMessage("\n[白嫖工具]--测试"); - // //激活选中事件 - // Env.Editor.SelectionAdded += SelectTest_SelectionAdded; - // //初始化坐标系 - // Env.Editor.CurrentUserCoordinateSystem = Matrix3d.Identity; - - // //创建过滤器 - // var sf = new OpEqual(0, "arc"); - // var pso = new PromptSelectionOptions - // { - // MessageForAdding = "\n请选择对象:" - // }; - - // pso.Keywords.Add("Z"); - // pso.Keywords.Add("X"); - // pso.Keywords.Add("Q"); - // //注册关键字 - // pso.KeywordInput += SelectTest_KeywordInput; - // try - // { - // //用户选择 - // var psr = Env.Editor.GetSelection(pso, sf); - // //处理代码 - - - // } - // catch (Exception ex)//捕获关键字 - // { - // if (ex.Message == "XuError") - // { - // //关闭关键字事件 - // pso.KeywordInput -= SelectTest_KeywordInput; - // //关闭选中事件 - // Env.Editor.SelectionAdded -= SelectTest_SelectionAdded; - // //重新调用自身 - // ZengLiangYuanJiao(); - // } - // } - // //关闭关键字事件 - // pso.KeywordInput -= SelectTest_KeywordInput; - // //关闭选中事件 - // Env.Editor.SelectionAdded -= SelectTest_SelectionAdded; - //} - - ///// - ///// 即时选择 - ///// - ///// - ///// - //private static void SelectTest_SelectionAdded(object sender, SelectionAddedEventArgs e) - //{ - // //关闭选中事件 - // Env.Editor.SelectionAdded -= SelectTest_SelectionAdded; - // using (var tr = new DBTrans()) - // { - // //处理代码 - // for (int i = 0; i < e.AddedObjects.Count; i++) - // { - - - // //处理完移除已处理的对象 - // e.Remove(i); - // } - // } - // //激活选中事件 - // Env.Editor.SelectionAdded += SelectTest_SelectionAdded; - //} - - ///// - ///// 关键字响应 - ///// - ///// - ///// - //private static void SelectTest_KeywordInput(object sender, SelectionTextInputEventArgs e) - //{ - // //获取关键字 - // switch (e.Input) - // { - // case "Z": - // { - // break; - // } - // case "X": - // { - // break; - // } - - // case "Q": - // { - // break; - // } - // } - // //抛出异常,用于更新提示信息 - // throw new ArgumentException("XuError"); - //} - - - //#endregion + // #region 即时选择样板 + // /// + // /// 即时选择,框选更新关键字 + // /// + // public static void SelectTest() + // { + // Env.Editor.WriteMessage("\n[白嫖工具]--测试"); + // // 激活选中事件 + // Env.Editor.SelectionAdded += SelectTest_SelectionAdded; + // // 初始化坐标系 + // Env.Editor.CurrentUserCoordinateSystem = Matrix3d.Identity; + + // // 创建过滤器 + // var sf = new OpEqual(0, "arc"); + // var pso = new PromptSelectionOptions + // { + // MessageForAdding = "\n请选择对象:" + // }; + + // pso.Keywords.Add("Z"); + // pso.Keywords.Add("X"); + // pso.Keywords.Add("Q"); + // // 注册关键字 + // pso.KeywordInput += SelectTest_KeywordInput; + // try + // { + // // 用户选择 + // var psr = Env.Editor.GetSelection(pso, sf); + // // 处理代码 + + + // } + // catch (Exception ex)// 捕获关键字 + // { + // if (ex.Message == "XuError") + // { + // // 关闭关键字事件 + // pso.KeywordInput -= SelectTest_KeywordInput; + // // 关闭选中事件 + // Env.Editor.SelectionAdded -= SelectTest_SelectionAdded; + // // 重新调用自身 + // ZengLiangYuanJiao(); + // } + // } + // // 关闭关键字事件 + // pso.KeywordInput -= SelectTest_KeywordInput; + // // 关闭选中事件 + // Env.Editor.SelectionAdded -= SelectTest_SelectionAdded; + // } + + // /// + // /// 即时选择 + // /// + // /// + // /// + // private static void SelectTest_SelectionAdded(object sender, SelectionAddedEventArgs e) + // { + // // 关闭选中事件 + // Env.Editor.SelectionAdded -= SelectTest_SelectionAdded; + // using (var tr = new DBTrans()) + // { + // // 处理代码 + // for (int i = 0; i < e.AddedObjects.Count; i++) + // { + + + // // 处理完移除已处理的对象 + // e.Remove(i); + // } + // } + // // 激活选中事件 + // Env.Editor.SelectionAdded += SelectTest_SelectionAdded; + // } + + // /// + // /// 关键字响应 + // /// + // /// + // /// + // private static void SelectTest_KeywordInput(object sender, SelectionTextInputEventArgs e) + // { + // // 获取关键字 + // switch (e.Input) + // { + // case "Z": + // { + // break; + // } + // case "X": + // { + // break; + // } + + // case "Q": + // { + // break; + // } + // } + // // 抛出异常,用于更新提示信息 + // throw new ArgumentException("XuError"); + // } + // #endregion #endregion #region Info @@ -857,11 +854,11 @@ public static void Zoom(this Editor ed, Point3d CenPt, double width, double heig view.Width = width; view.Height = height; view.CenterPoint = new Point2d(CenPt.X, CenPt.Y); - ed.SetCurrentView(view);//更新当前视图 + ed.SetCurrentView(view);// 更新当前视图 } /// - ///缩放窗口范围 + /// 缩放窗口范围 /// /// 命令行对象 /// 第一点 @@ -887,7 +884,7 @@ public static void ZoomWindow(this Editor ed, Point3d lpt, Point3d rpt, double o /// public static Extents3d? GetValidExtents3d(this Database db, double extention = 1e-6) { - db.UpdateExt(true);//更新当前模型空间的范围 + db.UpdateExt(true);// 更新当前模型空间的范围 var ve = new Vector3d(extention, extention, extention); // 数据库没有图元的时候,min是大,max是小,导致新建出错 // 数据如下: @@ -909,7 +906,7 @@ public static void ZoomWindow(this Editor ed, Point3d lpt, Point3d rpt, double o public static void ZoomExtents(this Editor ed, double offsetDist = 0.00) { Database db = ed.Document.Database; - // db.UpdateExt(true); //GetValidExtents3d内提供了 + // db.UpdateExt(true); // GetValidExtents3d内提供了 var dbExtent = db.GetValidExtents3d(); if (dbExtent == null) ed.ZoomWindow(Point3d.Origin, new Point3d(1, 1, 0), offsetDist); @@ -1017,7 +1014,7 @@ public static PromptResult GetString(this Editor ed, string Message, string Defa [DllImport("accore.dll", CharSet = CharSet.Unicode, CallingConvention = CallingConvention.Cdecl, EntryPoint = "?acedEvaluateLisp@@YAHPEB_WAEAPEAUresbuf@@@Z")] #endif - [System.Security.SuppressUnmanagedCodeSecurity]//初始化默认值 + [System.Security.SuppressUnmanagedCodeSecurity]// 初始化默认值 static extern int AcedEvaluateLisp(string lispLine, out IntPtr result); #pragma warning disable CA2101 // 指定对 P/Invoke 字符串参数进行封送处理 diff --git a/src/IFoxCAD.Cad.Shared/ExtensionMethod/EntityBoundingInfo.cs b/src/IFoxCAD.Cad.Shared/ExtensionMethod/EntityBoundingInfo.cs index 8511b59..43010d6 100644 --- a/src/IFoxCAD.Cad.Shared/ExtensionMethod/EntityBoundingInfo.cs +++ b/src/IFoxCAD.Cad.Shared/ExtensionMethod/EntityBoundingInfo.cs @@ -45,8 +45,8 @@ public BoundingInfo(Point3d min, Point3d max, bool isEffective, double angle = 0 isEffective, angle) { } - //public BoundingInfo(Rect rect, double angle = 0) - //{ + // public BoundingInfo(Rect rect, double angle = 0) + // { // MinX = rect.X; // MinY = rect.Y; // MinZ = 0; @@ -54,7 +54,7 @@ public BoundingInfo(Point3d min, Point3d max, bool isEffective, double angle = 0 // MaxY = rect.Top; // MaxZ = 0; // Angle = angle; - //} + // } } public class EntityBoundingInfo @@ -103,7 +103,7 @@ static void ExceptionToHashSet() if (line.Contains(nameof(LogTxt.备注信息))) { int index = line.IndexOf(":"); - index = line.IndexOf("\"", index) + 1;//1是"\"" + index = line.IndexOf("\"", index) + 1;// 1是"\"" int index2 = line.IndexOf("\"", index); var msg = line.Substring(index, index2 - index); _typeNames.Add(msg); @@ -123,10 +123,10 @@ static void ExceptionToHashSet() /// static void ExceptionToLog(Exception e, Entity ent) { - //无法处理的错误类型将被记录 - //如果错误无法try,而是cad直接致命错误,那么此处也不会被写入, - //这种情况无法避免程序安全性,总不能写了日志再去删除日志词条,这样会造成频繁IO的 - //遇到一个不认识的类型再去写入?然后记录它是否可以写入? + // 无法处理的错误类型将被记录 + // 如果错误无法try,而是cad直接致命错误,那么此处也不会被写入, + // 这种情况无法避免程序安全性,总不能写了日志再去删除日志词条,这样会造成频繁IO的 + // 遇到一个不认识的类型再去写入?然后记录它是否可以写入? var old_LogAddress = LogHelper.LogAddress; var old_FlagOutFile = LogHelper.FlagOutFile; try @@ -153,20 +153,20 @@ static void ExceptionToLog(Exception e, Entity ent) public static BoundingInfo GetBoundingInfo(Entity ent) { #if Debug_Cause_Error - //错误类型处理 - if (ent is AttributeDefinition) //属性定义 + // 错误类型处理 + if (ent is AttributeDefinition) // 属性定义 return new(Point3d.Origin, Point3d.Origin, false); - else if (ent is Xline xline)//参照线 + else if (ent is Xline xline)// 参照线 return new(xline.BasePoint, xline.BasePoint, true); - else if (ent is Ray ray)//射线 + else if (ent is Ray ray)// 射线 return new(ray.BasePoint, ray.BasePoint, true); #endif - //指定类型处理 + // 指定类型处理 if (ent is BlockReference brf) return GetBoxInfoInBlockReference(brf); else if (ent is MText mText) return GetBoxInfoInMText(mText); - else if (!_typeNames.Contains(ent.GetType().Name)) //屏蔽天正等等缺失包围盒的类型 + else if (!_typeNames.Contains(ent.GetType().Name)) // 屏蔽天正等等缺失包围盒的类型 try { return new(ent.GeometricExtents.MinPoint, ent.GeometricExtents.MaxPoint, true); @@ -182,7 +182,7 @@ static BoundingInfo GetBoxInfoInBlockReference(BlockReference brf) { try { - //这个获取是原点附近,需要平移到块基点 + // 这个获取是原点附近,需要平移到块基点 var fit = brf.GeometryExtentsBestFit(); var minX = fit.MinPoint.X + brf.Position.X; var minY = fit.MinPoint.Y + brf.Position.Y; @@ -194,9 +194,9 @@ static BoundingInfo GetBoxInfoInBlockReference(BlockReference brf) } catch { - //如果是一条参照线的组块,将导致获取包围盒时报错 - //0x01 是否需要进入块内,然后拿到每个图元的BasePoint再计算中点?感觉过于复杂. - //0x02 这个时候拿基点走就算了 + // 如果是一条参照线的组块,将导致获取包围盒时报错 + // 0x01 是否需要进入块内,然后拿到每个图元的BasePoint再计算中点?感觉过于复杂. + // 0x02 这个时候拿基点走就算了 return new(brf.Position, brf.Position, true); } } @@ -219,12 +219,12 @@ static BoundingInfo GetBoxInfoInMText(MText mtxt) * ------------------------------------ */ - double width = mtxt.ActualWidth;//实际宽度 - double height = mtxt.ActualHeight;//实际高度 + double width = mtxt.ActualWidth;// 实际宽度 + double height = mtxt.ActualHeight;// 实际高度 double wl = 0.0; double hb = 0.0; - //对齐方式 + // 对齐方式 switch (mtxt.Attachment) { case AttachmentPoint.TopCenter: @@ -243,7 +243,7 @@ static BoundingInfo GetBoxInfoInMText(MText mtxt) case AttachmentPoint.TopLeft: case AttachmentPoint.TopCenter: case AttachmentPoint.TopRight: - hb = -height;//下边线到插入点的距离 + hb = -height;// 下边线到插入点的距离 break; case AttachmentPoint.MiddleLeft: case AttachmentPoint.MiddleCenter: diff --git a/src/IFoxCAD.Cad.Shared/ExtensionMethod/EntityEx.cs b/src/IFoxCAD.Cad.Shared/ExtensionMethod/EntityEx.cs index fa0dc9f..42de3f4 100644 --- a/src/IFoxCAD.Cad.Shared/ExtensionMethod/EntityEx.cs +++ b/src/IFoxCAD.Cad.Shared/ExtensionMethod/EntityEx.cs @@ -13,10 +13,10 @@ public static class EntityEx /// 实体对象 public static void Flush(this Entity entity, DBTrans? trans = null) { - //if (entity is null) - //{ + // if (entity is null) + // { // throw new ArgumentNullException(nameof(entity)); - //} + // } trans ??= DBTrans.Top; entity.RecordGraphicsModified(true); trans.Transaction.TransactionManager.QueueForGraphicsFlush(); @@ -242,7 +242,7 @@ public static Arc CreateArcSCE(Point3d startPoint, Point3d centerPoint, Point3d arc.Radius = centerPoint.DistanceTo(startPoint); Vector2d startVector = new(startPoint.X - centerPoint.X, startPoint.Y - centerPoint.Y); Vector2d endVector = new(endPoint.X - centerPoint.X, endPoint.Y - centerPoint.Y); - //计算起始和终止角度 + // 计算起始和终止角度 arc.StartAngle = startVector.Angle; arc.EndAngle = endVector.Angle; return arc; @@ -257,9 +257,9 @@ public static Arc CreateArcSCE(Point3d startPoint, Point3d centerPoint, Point3d /// 圆弧 public static Arc CreateArc(Point3d startPoint, Point3d pointOnArc, Point3d endPoint) { - //创建一个几何类的圆弧对象 + // 创建一个几何类的圆弧对象 CircularArc3d geArc = new(startPoint, pointOnArc, endPoint); - //将几何类圆弧对象的圆心和半径赋值给圆弧 + // 将几何类圆弧对象的圆心和半径赋值给圆弧 #if NET35 return (Arc)geArc.ToCurve(); #else @@ -315,14 +315,14 @@ public static Circle CreateCircle(Point3d startPoint, Point3d endPoint) /// public static Circle? CreateCircle(Point3d pt1, Point3d pt2, Point3d pt3) { - //先判断三点是否共线,得到pt1点指向pt2、pt2点的矢量 + // 先判断三点是否共线,得到pt1点指向pt2、pt2点的矢量 Vector3d va = pt1.GetVectorTo(pt2); Vector3d vb = pt1.GetVectorTo(pt3); - //如两矢量夹角为0或180度(π弧度),则三点共线. + // 如两矢量夹角为0或180度(π弧度),则三点共线. if (va.GetAngleTo(vb) == 0 | va.GetAngleTo(vb) == Math.PI) return null; - //创建一个几何类的圆弧对象 + // 创建一个几何类的圆弧对象 CircularArc3d geArc = new(pt1, pt2, pt3); geArc.ToCircle(); return geArc.ToCircle(); @@ -336,7 +336,7 @@ public static Circle CreateCircle(Point3d startPoint, Point3d endPoint) /// 图形的ObjectId public static Circle? CreateCircle(Point3d center, double radius, double vex = 0, double vey = 0, double vez = 1) { - return new Circle(center, new Vector3d(vex, vey, vez), radius);//平面法向量XY方向 + return new Circle(center, new Vector3d(vex, vey, vez), radius);// 平面法向量XY方向 } #endregion @@ -366,7 +366,7 @@ public static void ClipBlockRef(this BlockReference bref, IEnumerable p using SpatialFilter sf = new() { Definition = sfd }; var dict = bref.GetXDictionary()!.GetSubDictionary(true, new string[] { filterDictName })!; dict.SetAt(spatialName, sf); - //SetToDictionary(dict, spatialName, sf); + // SetToDictionary(dict, spatialName, sf); } /// @@ -391,7 +391,7 @@ public static void ClipBlockRef(this BlockReference bref, Point3d pt1, Point3d p using SpatialFilter sf = new() { Definition = sfd }; var dict = bref.GetXDictionary()!.GetSubDictionary(true, new string[] { filterDictName })!; dict.SetAt(spatialName, sf); - //SetToDictionary(dict, spatialName, sf); + // SetToDictionary(dict, spatialName, sf); } #endregion diff --git a/src/IFoxCAD.Cad.Shared/ExtensionMethod/Enums.cs b/src/IFoxCAD.Cad.Shared/ExtensionMethod/Enums.cs index 9f9f7c3..6049adb 100644 --- a/src/IFoxCAD.Cad.Shared/ExtensionMethod/Enums.cs +++ b/src/IFoxCAD.Cad.Shared/ExtensionMethod/Enums.cs @@ -96,7 +96,7 @@ public enum SymModes : ushort ViewportTable = 256, Option2 = UcsTable | ViewTable | ViewportTable, - //全部 + // 全部 All = BlockTable | Option1 | Option2 } diff --git a/src/IFoxCAD.Cad.Shared/ExtensionMethod/GeometryEx.cs b/src/IFoxCAD.Cad.Shared/ExtensionMethod/GeometryEx.cs index 4edff55..58749b2 100644 --- a/src/IFoxCAD.Cad.Shared/ExtensionMethod/GeometryEx.cs +++ b/src/IFoxCAD.Cad.Shared/ExtensionMethod/GeometryEx.cs @@ -18,7 +18,7 @@ public static class GeometryEx /// 点与多边形的关系 public static PointOnRegionType PointOnRegion(this IEnumerable pts, Point2d pt) { - //遍历点集并生成首尾连接的多边形 + // 遍历点集并生成首尾连接的多边形 var ptlst = new LoopList(pts); if (ptlst.Count < 3) return PointOnRegionType.Error; @@ -30,11 +30,11 @@ public static PointOnRegionType PointOnRegion(this IEnumerable pts, Poi } var cc2d = new CompositeCurve2d(ls2ds.ToArray()); - //在多边形上? + // 在多边形上? if (cc2d.IsOn(pt)) return PointOnRegionType.On; - //在最小包围矩形外? + // 在最小包围矩形外? var bb2d = cc2d.BoundBlock; if (!bb2d.Contains(pt)) return PointOnRegionType.Outside; @@ -68,7 +68,7 @@ public static PointOnRegionType PointOnRegion(this IEnumerable pts, Poi /// 点与多边形的关系 public static PointOnRegionType PointOnRegion(this IEnumerable pts, Point3d pt) { - //遍历点集并生成首尾连接的多边形 + // 遍历点集并生成首尾连接的多边形 var ptlst = new LoopList(pts); if (ptlst.First!.Value == ptlst.Last!.Value) ptlst.RemoveLast(); @@ -80,11 +80,11 @@ public static PointOnRegionType PointOnRegion(this IEnumerable pts, Poi ls3ds.Add(new LineSegment3d(node.Value, node.Next!.Value)); var cc3d = new CompositeCurve3d(ls3ds.ToArray()); - //在多边形上? + // 在多边形上? if (cc3d.IsOn(pt)) return PointOnRegionType.On; - //在最小包围矩形外? + // 在最小包围矩形外? var bb2d = cc3d.BoundBlock; if (!bb2d.Contains(pt)) return PointOnRegionType.Outside; @@ -140,7 +140,7 @@ public static CircularArc2d GetMinCircle(Point2d pt1, Point2d pt2, Point2d pt3, { ptlst = new LoopList { pt1, pt2, pt3 }; - //遍历各点与下一点的向量长度,找到距离最大的两个点 + // 遍历各点与下一点的向量长度,找到距离最大的两个点 LoopListNode maxNode = ptlst.GetNodes().FindByMax ( @@ -148,19 +148,19 @@ public static CircularArc2d GetMinCircle(Point2d pt1, Point2d pt2, Point2d pt3, node => node.Value.GetDistanceTo(node.Next!.Value) ); - //以两点做最小包围圆 + // 以两点做最小包围圆 CircularArc2d ca2d = GetMinCircle(maxNode.Value, maxNode.Next!.Value, out LoopList tptlst); - //如果另一点属于该圆 + // 如果另一点属于该圆 if (ca2d.IsIn(maxNode.Previous!.Value)) { - //返回 + // 返回 ptlst = tptlst; return ca2d; } - //否则按三点做圆 - //ptlst.SetFirst(maxNode); + // 否则按三点做圆 + // ptlst.SetFirst(maxNode); ptlst = new LoopList { maxNode.Value, maxNode.Next.Value, maxNode.Previous.Value }; ca2d = new CircularArc2d(pt1, pt2, pt3); ca2d.SetAngles(0, Math.PI * 2); @@ -182,15 +182,15 @@ public static CircularArc2d GetMinCircle(Point2d pt1, Point2d pt2, Point2d pt3, ptlst = null; CircularArc2d? ca2d = null; - //遍历C43的组合,环链表的优势在这里 + // 遍历C43的组合,环链表的优势在这里 foreach (LoopListNode firstNode in iniptlst.GetNodes()) { - //获取各组合下三点的最小包围圆 + // 获取各组合下三点的最小包围圆 var secondNode = firstNode.Next; var thirdNode = secondNode!.Next; var tca2d = GetMinCircle(firstNode.Value, secondNode.Value, thirdNode!.Value, out LoopList tptlst); - //如果另一点属于该圆,并且半径小于当前值就把它做为候选解 + // 如果另一点属于该圆,并且半径小于当前值就把它做为候选解 if (!tca2d.IsIn(firstNode.Previous!.Value)) continue; if (ca2d is null || tca2d.Radius < ca2d.Radius) @@ -200,7 +200,7 @@ public static CircularArc2d GetMinCircle(Point2d pt1, Point2d pt2, Point2d pt3, } } - //返回直径最小的圆 + // 返回直径最小的圆 return ca2d; } @@ -341,7 +341,7 @@ public static OrientationType IsClockWise(this IEnumerable pnts) /// 解析类圆对象 public static CircularArc2d? GetMinCircle(this List pnts, out LoopList? ptlst) { - //点数较小时直接返回 + // 点数较小时直接返回 switch (pnts.Count) { case 0: @@ -362,29 +362,29 @@ public static OrientationType IsClockWise(this IEnumerable pnts) return GetMinCircle(pnts[0], pnts[1], pnts[2], pnts[3], out ptlst); } - //按前三点计算最小包围圆 + // 按前三点计算最小包围圆 var tpnts = new Point2d[4]; pnts.CopyTo(0, tpnts, 0, 3); var ca2d = GetMinCircle(tpnts[0], tpnts[1], tpnts[2], out ptlst); - //找到点集中距离圆心的最远点为第四点 + // 找到点集中距离圆心的最远点为第四点 tpnts[3] = pnts.FindByMax(pnt => ca2d.Center.GetDistanceTo(pnt)); - //如果最远点属于圆结束 + // 如果最远点属于圆结束 while (!ca2d.IsIn(tpnts[3])) { - //如果最远点不属于圆,按此四点计算最小包围圆 + // 如果最远点不属于圆,按此四点计算最小包围圆 ca2d = GetMinCircle(tpnts[0], tpnts[1], tpnts[2], tpnts[3], out ptlst); - //将结果作为新的前三点 + // 将结果作为新的前三点 if (ptlst!.Count == 3) { tpnts[2] = ptlst.Last!.Value; } else { - //第三点取另两点中距离圆心较远的点 - //按算法中描述的任选其中一点的话,还是无法收敛...... + // 第三点取另两点中距离圆心较远的点 + // 按算法中描述的任选其中一点的话,还是无法收敛...... tpnts[2] = tpnts.Except(ptlst) .FindByMax(pnt => ca2d!.Center.GetDistanceTo(pnt)); @@ -392,10 +392,10 @@ public static OrientationType IsClockWise(this IEnumerable pnts) tpnts[0] = ptlst.First!.Value; tpnts[1] = ptlst.First.Next!.Value; - //按此三点计算最小包围圆 + // 按此三点计算最小包围圆 ca2d = GetMinCircle(tpnts[0], tpnts[1], tpnts[2], out ptlst); - //找到点集中圆心的最远点为第四点 + // 找到点集中圆心的最远点为第四点 tpnts[3] = pnts.FindByMax(pnt => ca2d.Center.GetDistanceTo(pnt)); } diff --git a/src/IFoxCAD.Cad.Shared/ExtensionMethod/Jig.cs b/src/IFoxCAD.Cad.Shared/ExtensionMethod/Jig.cs index 21875eb..5b5d3d5 100644 --- a/src/IFoxCAD.Cad.Shared/ExtensionMethod/Jig.cs +++ b/src/IFoxCAD.Cad.Shared/ExtensionMethod/Jig.cs @@ -30,10 +30,10 @@ public class JigEx : DrawJig readonly Action>? _mouseAction; - readonly Tolerance _tolerance;//容差 + readonly Tolerance _tolerance;// 容差 - readonly Queue _drawEntitys;//重复生成的图元,放在这里刷新 - JigPromptPointOptions? _options;//jig鼠标配置 + readonly Queue _drawEntitys;// 重复生成的图元,放在这里刷新 + JigPromptPointOptions? _options;// jig鼠标配置 bool _worldDrawFlag = false; // 20220503 bool _systemVariables_Orthomode = false; @@ -42,7 +42,7 @@ bool SystemVariables_Orthomode // 正交修改还原 get => _systemVariables_Orthomode; set { - //1正交,0非正交 //setvar: https://www.cnblogs.com/JJBox/p/10209541.html + // 1正交,0非正交 // setvar: https://www.cnblogs.com/JJBox/p/10209541.html if (Env.OrthoMode != value) Env.OrthoMode = _systemVariables_Orthomode = value; } @@ -92,9 +92,9 @@ public JigPromptPointOptions SetOptions(Point3d basePoint, _options = JigPointOptions(); _options.Message = Environment.NewLine + msg; - _options.Cursor = cursorType; //光标绑定 - _options.UseBasePoint = true; //基点打开 - _options.BasePoint = basePoint; //基点设定 + _options.Cursor = cursorType; // 光标绑定 + _options.UseBasePoint = true; // 基点打开 + _options.BasePoint = basePoint; // 基点设定 return _options; } @@ -115,7 +115,7 @@ public JigPromptPointOptions SetOptions(string msg, _options = JigPointOptions(); _options.Message = Environment.NewLine + msg; - //加入关键字,加入时候将空格内容放到最后 + // 加入关键字,加入时候将空格内容放到最后 string spaceValue = string.Empty; const string spaceKey = " "; @@ -126,9 +126,9 @@ public JigPromptPointOptions SetOptions(string msg, else _options.Keywords.Add(item.Key, item.Key, item.Value); - ///因为默认配置函数导致此处空格触发是无效的, - ///但是用户如果想触发,就需要在外部减去默认UserInputControls配置 - ///要放最后,才能优先触发其他关键字 + /// 因为默认配置函数导致此处空格触发是无效的, + /// 但是用户如果想触发,就需要在外部减去默认UserInputControls配置 + /// 要放最后,才能优先触发其他关键字 if (spaceValue != string.Empty) _options.Keywords.Add(spaceKey, spaceKey, spaceValue); else @@ -137,8 +137,8 @@ public JigPromptPointOptions SetOptions(string msg, // 外部设置减去配置 // _options.UserInputControls = // _options.UserInputControls - // ^ UserInputControls.NullResponseAccepted //输入了鼠标右键,结束jig - // ^ UserInputControls.AnyBlankTerminatesInput; //空格或回车,结束jig; + // ^ UserInputControls.NullResponseAccepted // 输入了鼠标右键,结束jig + // ^ UserInputControls.AnyBlankTerminatesInput; // 空格或回车,结束jig; return _options; } @@ -162,7 +162,7 @@ public void SetOptions(Action action, bool orthomode = fa /// public PromptResult Drag() { - //jig功能必然是当前前台文档,所以封装内部更好调用 + // jig功能必然是当前前台文档,所以封装内部更好调用 var dm = Acap.DocumentManager; var doc = dm.MdiActiveDocument; var ed = doc.Editor; @@ -182,13 +182,13 @@ public PromptResult Drag() public IEnumerable? AddEntityToMsPs(BlockTableRecord btrOfAddEntitySpace, IEnumerable? removeEntity = null) { - //内部用 _drawEntitys 外部用 Entitys,减少一层转换 + // 内部用 _drawEntitys 外部用 Entitys,减少一层转换 if (_drawEntitys.Count == 0) return null; IEnumerable es = _drawEntitys; if (removeEntity != null) - es = es.Except(removeEntity);//差集 + es = es.Except(removeEntity);// 差集 return btrOfAddEntitySpace.AddEntity(es); } @@ -203,7 +203,7 @@ public PromptResult Drag() protected override SamplerStatus Sampler(JigPrompts prompts) { if (_worldDrawFlag) - return SamplerStatus.NoChange;//OK的时候拖动鼠标与否都不出现图元 + return SamplerStatus.NoChange;// OK的时候拖动鼠标与否都不出现图元 if (_options is null) throw new NullReferenceException(nameof(_options)); @@ -214,21 +214,21 @@ protected override SamplerStatus Sampler(JigPrompts prompts) else if (pro.Status != PromptStatus.OK) return SamplerStatus.Cancel; - //上次鼠标点不同(一定要这句,不然图元刷新太快会看到奇怪的边线) + // 上次鼠标点不同(一定要这句,不然图元刷新太快会看到奇怪的边线) var mousePointWcs = pro.Value; - //== 是比较类字段,但是最好转为哈希比较. - //IsEqualTo 是方形判断(仅加法),但是cad是距离. - //Distance 是圆形判断(会求平方根,使用了牛顿迭代), - //大量数据(十万以上/频繁刷新)面前会显得非常慢. + // == 是比较类字段,但是最好转为哈希比较. + // IsEqualTo 是方形判断(仅加法),但是cad是距离. + // Distance 是圆形判断(会求平方根,使用了牛顿迭代), + // 大量数据(十万以上/频繁刷新)面前会显得非常慢. if (mousePointWcs.IsEqualTo(MousePointWcsLast, _tolerance)) return SamplerStatus.NoChange; - //上次循环的缓冲区图元清理,否则将会在vs输出遗忘 Dispose + // 上次循环的缓冲区图元清理,否则将会在vs输出遗忘 Dispose while (_drawEntitys.Count > 0) _drawEntitys.Dequeue().Dispose(); - //委托把容器扔出去接收新创建的图元,然后给重绘更新 + // 委托把容器扔出去接收新创建的图元,然后给重绘更新 _mouseAction?.Invoke(mousePointWcs, _drawEntitys); MousePointWcsLast = mousePointWcs; @@ -302,10 +302,10 @@ static JigPromptPointOptions JigPointOptions() return new JigPromptPointOptions() { UserInputControls = - UserInputControls.GovernedByUCSDetect //由UCS探测用 - | UserInputControls.Accept3dCoordinates //接受三维坐标 - | UserInputControls.NullResponseAccepted //输入了鼠标右键,结束jig - | UserInputControls.AnyBlankTerminatesInput //空格或回车,结束jig; + UserInputControls.GovernedByUCSDetect // 由UCS探测用 + | UserInputControls.Accept3dCoordinates // 接受三维坐标 + | UserInputControls.NullResponseAccepted // 输入了鼠标右键,结束jig + | UserInputControls.AnyBlankTerminatesInput // 空格或回车,结束jig; }; } @@ -320,23 +320,23 @@ public void SetSpaceIsKeyword() throw new ArgumentNullException(nameof(_options)); if ((opt.UserInputControls & UserInputControls.NullResponseAccepted) == UserInputControls.NullResponseAccepted) - opt.UserInputControls ^= UserInputControls.NullResponseAccepted; //输入了鼠标右键,结束jig + opt.UserInputControls ^= UserInputControls.NullResponseAccepted; // 输入了鼠标右键,结束jig if ((opt.UserInputControls & UserInputControls.AnyBlankTerminatesInput) == UserInputControls.AnyBlankTerminatesInput) - opt.UserInputControls ^= UserInputControls.AnyBlankTerminatesInput; //空格或回车,结束jig + opt.UserInputControls ^= UserInputControls.AnyBlankTerminatesInput; // 空格或回车,结束jig } } #if false -| UserInputControls.DoNotEchoCancelForCtrlC //不要取消CtrlC的回音 -| UserInputControls.DoNotUpdateLastPoint //不要更新最后一点 -| UserInputControls.NoDwgLimitsChecking //没有Dwg限制检查 -| UserInputControls.NoZeroResponseAccepted //接受非零响应 -| UserInputControls.NoNegativeResponseAccepted //不否定回复已被接受 -| UserInputControls.Accept3dCoordinates //返回点的三维坐标,是转换坐标系了? -| UserInputControls.AcceptMouseUpAsPoint //接受释放按键时的点而不是按下时 - -| UserInputControls.InitialBlankTerminatesInput //初始 空格或回车,结束jig -| UserInputControls.AcceptOtherInputString //接受其他输入字符串 -| UserInputControls.NoZDirectionOrtho //无方向正射,直接输入数字时以基点到当前点作为方向 -| UserInputControls.UseBasePointElevation //使用基点高程,基点的Z高度探测 +| UserInputControls.DoNotEchoCancelForCtrlC // 不要取消CtrlC的回音 +| UserInputControls.DoNotUpdateLastPoint // 不要更新最后一点 +| UserInputControls.NoDwgLimitsChecking // 没有Dwg限制检查 +| UserInputControls.NoZeroResponseAccepted // 接受非零响应 +| UserInputControls.NoNegativeResponseAccepted // 不否定回复已被接受 +| UserInputControls.Accept3dCoordinates // 返回点的三维坐标,是转换坐标系了? +| UserInputControls.AcceptMouseUpAsPoint // 接受释放按键时的点而不是按下时 + +| UserInputControls.InitialBlankTerminatesInput // 初始 空格或回车,结束jig +| UserInputControls.AcceptOtherInputString // 接受其他输入字符串 +| UserInputControls.NoZDirectionOrtho // 无方向正射,直接输入数字时以基点到当前点作为方向 +| UserInputControls.UseBasePointElevation // 使用基点高程,基点的Z高度探测 #endif \ No newline at end of file diff --git a/src/IFoxCAD.Cad.Shared/ExtensionMethod/ObjectIdEx.cs b/src/IFoxCAD.Cad.Shared/ExtensionMethod/ObjectIdEx.cs index 1b51a4b..92e8b0f 100644 --- a/src/IFoxCAD.Cad.Shared/ExtensionMethod/ObjectIdEx.cs +++ b/src/IFoxCAD.Cad.Shared/ExtensionMethod/ObjectIdEx.cs @@ -19,7 +19,7 @@ public static class ObjectIdEx public static T? GetObject(this ObjectId id, OpenMode mode = OpenMode.ForRead, bool openErased = false, Transaction? tr = default) where T : DBObject { tr ??= DBTrans.Top.Transaction; - //tr = Env.GetTrans(tr); + // tr = Env.GetTrans(tr); return tr.GetObject(id, mode, openErased) as T; } @@ -53,7 +53,7 @@ public static IEnumerable OfType(this IEnumerable ids) wh } #endregion GetObject - //Acad08缺少 id.ObjectClass 如何补偿? + // Acad08缺少 id.ObjectClass 如何补偿? public static RXClass ObjectClass(this ObjectId id) { #if NET35 @@ -86,7 +86,7 @@ public static void Erase(this ObjectId id) { ent.Erase(); }// 第一种读写权限自动转换写法 - //Env.Editor.Regen(); + // Env.Editor.Regen(); } } } diff --git a/src/IFoxCAD.Cad.Shared/ExtensionMethod/PointEx.cs b/src/IFoxCAD.Cad.Shared/ExtensionMethod/PointEx.cs index 4898d50..f65c24e 100644 --- a/src/IFoxCAD.Cad.Shared/ExtensionMethod/PointEx.cs +++ b/src/IFoxCAD.Cad.Shared/ExtensionMethod/PointEx.cs @@ -21,7 +21,7 @@ public static string GetHashString(this Point3d pt, int xyz = 3, int decimalReta }; } - //为了频繁触发所以弄个全局变量 + // 为了频繁触发所以弄个全局变量 static Plane? _Plane; /// /// 两点计算弧度范围0到2Pi @@ -57,7 +57,7 @@ public static double GetAngle(this Point2d startPoint, Point2d endPoint) /// public static Point2d GetCenter(this Point2d a, Point2d b) { - // (p1 + p2) / 2; //溢出风险 + // (p1 + p2) / 2; // 溢出风险 return new Point2d(a.X * 0.5 + b.X * 0.5, a.Y * 0.5 + b.Y * 0.5); } @@ -74,13 +74,13 @@ public static double GetArcBulge(this Point2d arc1, Point2d arc2, Point2d arc3, { double dStartAngle = arc2.GetAngle(arc1); double dEndAngle = arc2.GetAngle(arc3); - //求的P1P2与P1P3夹角 + // 求的P1P2与P1P3夹角 var talAngle = (Math.PI - dStartAngle + dEndAngle) / 2; - //凸度==拱高/半弦长==拱高比值/半弦长比值 - //有了比值就不需要拿到拱高值和半弦长值了,因为接下来是相除得凸度 + // 凸度==拱高/半弦长==拱高比值/半弦长比值 + // 有了比值就不需要拿到拱高值和半弦长值了,因为接下来是相除得凸度 double bulge = Math.Sin(talAngle) / Math.Cos(talAngle); - //处理精度 + // 处理精度 if (bulge > 0.9999 && bulge < 1.0001) bulge = 1; else if (bulge < -0.9999 && bulge > -1.0001) @@ -100,10 +100,10 @@ public static Point2dCollection End2End(this Point2dCollection ptcol) if (ptcol == null) throw new ArgumentNullException(nameof(ptcol)); - if (ptcol.Count == 0 || ptcol[0].Equals(ptcol[^1]))//首尾相同直接返回 + if (ptcol.Count == 0 || ptcol[0].Equals(ptcol[^1]))// 首尾相同直接返回 return ptcol; - //首尾不同,去加一个到最后 + // 首尾不同,去加一个到最后 var lst = new Point2d[ptcol.Count + 1]; for (int i = 0; i < lst.Length; i++) lst[i] = ptcol[i]; @@ -119,10 +119,10 @@ public static Point3dCollection End2End(this Point3dCollection ptcol) if (ptcol == null) throw new ArgumentNullException(nameof(ptcol)); - if (ptcol.Count == 0 || ptcol[0].Equals(ptcol[^1]))//首尾相同直接返回 + if (ptcol.Count == 0 || ptcol[0].Equals(ptcol[^1]))// 首尾相同直接返回 return ptcol; - //首尾不同,去加一个到最后 + // 首尾不同,去加一个到最后 var lst = new Point3d[ptcol.Count + 1]; for (int i = 0; i < lst.Length; i++) lst[i] = ptcol[i]; diff --git a/src/IFoxCAD.Cad.Shared/ExtensionMethod/SymbolTableEx.cs b/src/IFoxCAD.Cad.Shared/ExtensionMethod/SymbolTableEx.cs index a390436..4703c78 100644 --- a/src/IFoxCAD.Cad.Shared/ExtensionMethod/SymbolTableEx.cs +++ b/src/IFoxCAD.Cad.Shared/ExtensionMethod/SymbolTableEx.cs @@ -26,8 +26,8 @@ public static ObjectId Add(this SymbolTable table, /// 图层id public static ObjectId Add(this SymbolTable table, string name, int colorIndex) { - colorIndex %= 256;//防止输入的颜色超出256 - colorIndex = Math.Abs(colorIndex);//防止负数 + colorIndex %= 256;// 防止输入的颜色超出256 + colorIndex = Math.Abs(colorIndex);// 防止负数 return table.Add(name, lt => lt.Color = Color.FromColorIndex(ColorMethod.ByColor, (short)colorIndex)); } /// @@ -184,9 +184,9 @@ public static void AddAttsToBlocks(this SymbolTable块定义Id public static ObjectId GetBlockFrom(this SymbolTable table, string fileName, bool over) { - //FileInfo fi = new(fileName); - //string blkdefname = fi.Name; - //if (blkdefname.Contains(".")) + // FileInfo fi = new(fileName); + // string blkdefname = fi.Name; + // if (blkdefname.Contains(".")) // blkdefname = blkdefname.Substring(0, blkdefname.LastIndexOf('.')); string blkdefname = SymbolUtilityServices.RepairSymbolName( @@ -241,16 +241,16 @@ public static ObjectId Add(this SymbolTable name, ltt => { ltt.AsciiDescription = description; - ltt.PatternLength = length; //线型的总长度 - ltt.NumDashes = dash.Length; //组成线型的笔画数目 + ltt.PatternLength = length; // 线型的总长度 + ltt.NumDashes = dash.Length; // 组成线型的笔画数目 for (int i = 0; i < dash.Length; i++) { ltt.SetDashLengthAt(i, dash[i]); } - //ltt.SetDashLengthAt(0, 0.5); //0.5个单位的划线 - //ltt.SetDashLengthAt(1, -0.25); //0.25个单位的空格 - //ltt.SetDashLengthAt(2, 0); // 一个点 - //ltt.SetDashLengthAt(3, -0.25); //0.25个单位的空格 + // ltt.SetDashLengthAt(0, 0.5); // 0.5个单位的划线 + // ltt.SetDashLengthAt(1, -0.25); // 0.25个单位的空格 + // ltt.SetDashLengthAt(2, 0); // 一个点 + // ltt.SetDashLengthAt(3, -0.25); // 0.25个单位的空格 } ); } diff --git a/src/IFoxCAD.Cad.Shared/ExtensionMethod/SymbolTableRecordEx.cs b/src/IFoxCAD.Cad.Shared/ExtensionMethod/SymbolTableRecordEx.cs index 91b4d3a..5a6f4cc 100644 --- a/src/IFoxCAD.Cad.Shared/ExtensionMethod/SymbolTableRecordEx.cs +++ b/src/IFoxCAD.Cad.Shared/ExtensionMethod/SymbolTableRecordEx.cs @@ -56,12 +56,12 @@ public static IdMapping DeepClone(this BlockTableRecord btr, ObjectIdCollection /// /// 图元 /// 矩阵 - //public static void EntityTransformedCopy(this Entity ent, Matrix3d matrix) - //{ + // public static void EntityTransformedCopy(this Entity ent, Matrix3d matrix) + // { // var entNew = ent.GetTransformedCopy(matrix); // if (ent is BlockReference blockReference) // entNew.SetPropertiesFrom(blockReference); - //} + // } #endregion @@ -76,7 +76,7 @@ public static IdMapping DeepClone(this BlockTableRecord btr, ObjectIdCollection public static ObjectId AddEntity(this BlockTableRecord btr, Entity entity, Transaction? trans = null) { - //if (entity is null) + // if (entity is null) // throw new ArgumentNullException(nameof(entity), "对象为 null"); ObjectId id; @@ -100,7 +100,7 @@ public static ObjectId AddEntity(this BlockTableRecord btr, Entity entity, public static IEnumerable AddEntity(this BlockTableRecord btr, IEnumerable ents, Transaction? trans = null) where T : Entity { - //if (ents.Any(ent => ent is null)) + // if (ents.Any(ent => ent is null)) // throw new ArgumentNullException(nameof(ents), "实体集合内存在 null 对象"); trans ??= DBTrans.Top.Transaction; @@ -141,7 +141,7 @@ public static IEnumerable AddEntity(this BlockTableRecord btr, params /// 图元id private static ObjectId AddEnt(this BlockTableRecord btr, T ent, Action? action, Transaction? trans) where T : Entity { - //trans ??= DBTrans.Top.Transaction; + // trans ??= DBTrans.Top.Transaction; action?.Invoke(ent); return btr.AddEntity(ent, trans); } @@ -154,7 +154,7 @@ private static ObjectId AddEnt(this BlockTableRecord btr, T ent, Action? a /// 图元id,如果委托返回 null,则为 ObjectId.Null public static ObjectId AddEnt(this BlockTableRecord btr, Func action, Transaction? transaction) { - //transaction ??= DBTrans.Top.Transaction; + // transaction ??= DBTrans.Top.Transaction; var ent = action.Invoke(); if (ent is null) return ObjectId.Null; @@ -207,7 +207,7 @@ public static ObjectId AddCircle(this BlockTableRecord btr, Point3d p0, Point3d Action? action = default, Transaction? trans = default) { var circle = EntityEx.CreateCircle(p0, p1, p2); - //return circle is not null ? btr.AddEnt(circle, action, trans) : throw new ArgumentNullException(nameof(circle), "对象为 null"); + // return circle is not null ? btr.AddEnt(circle, action, trans) : throw new ArgumentNullException(nameof(circle), "对象为 null"); if (circle is null) throw new ArgumentNullException(nameof(circle), "对象为 null"); @@ -242,7 +242,7 @@ public static ObjectId AddPline(this BlockTableRecord btr, for (int i = 0; i < bvws.Count; i++) pl.AddVertexAt(i, bvws[i].Vertex, bvws[i].Bulge, bvws[i].StartWidth, bvws[i].EndWidth); } - pl.Closed = isClosed;//闭合 + pl.Closed = isClosed;// 闭合 return btr.AddEnt(pl, action, trans); } /// diff --git a/src/IFoxCAD.Cad.Shared/ExtensionMethod/Tools.cs b/src/IFoxCAD.Cad.Shared/ExtensionMethod/Tools.cs index d454930..b11d81d 100644 --- a/src/IFoxCAD.Cad.Shared/ExtensionMethod/Tools.cs +++ b/src/IFoxCAD.Cad.Shared/ExtensionMethod/Tools.cs @@ -5,11 +5,11 @@ public static class Tools public static void TestTimes2(int count, string message, Action action) { System.Diagnostics.Stopwatch watch = new(); - watch.Start(); //开始监视代码运行时间 + watch.Start(); // 开始监视代码运行时间 for (int i = 0; i < count; i++) - action.Invoke();//需要测试的代码 - watch.Stop(); //停止监视 - TimeSpan timespan = watch.Elapsed; //获取当前实例测量得出的总时间 + action.Invoke();// 需要测试的代码 + watch.Stop(); // 停止监视 + TimeSpan timespan = watch.Elapsed; // 获取当前实例测量得出的总时间 double time = timespan.TotalMilliseconds; string name = "毫秒"; if (timespan.TotalMilliseconds > 1000) @@ -17,7 +17,7 @@ public static void TestTimes2(int count, string message, Action action) time = timespan.TotalSeconds; name = "秒"; } - Env.Print($"{message} 代码执行 {count} 次的时间:{time} ({name})"); //总毫秒数 + Env.Print($"{message} 代码执行 {count} 次的时间:{time} ({name})"); // 总毫秒数 } /// @@ -53,7 +53,7 @@ public static void TestTimes(int count, string message, Action action, } /* -//测试例子,同时验证两个计时器 +// 测试例子,同时验证两个计时器 var stopwatch = new Stopwatch(); Timer.RunTime(() => { diff --git a/src/IFoxCAD.Cad.Shared/ExtensionMethod/XrefEx.cs b/src/IFoxCAD.Cad.Shared/ExtensionMethod/XrefEx.cs index 2d40750..d602c9d 100644 --- a/src/IFoxCAD.Cad.Shared/ExtensionMethod/XrefEx.cs +++ b/src/IFoxCAD.Cad.Shared/ExtensionMethod/XrefEx.cs @@ -1,4 +1,4 @@ -//#define error_demo +// #define error_demo #define lack_test namespace IFoxCAD.Cad; @@ -92,11 +92,11 @@ public XrefFactory(DBTrans tr, HashSet? xrefNames = null) #region 重写 public void Bind() { - //此功能有绑定出错的问题 - //db.BindXrefs(xrefIds, true); + // 此功能有绑定出错的问题 + // db.BindXrefs(xrefIds, true); - //绑定后会自动拆离 - //此功能修补了上面缺失 + // 绑定后会自动拆离 + // 此功能修补了上面缺失 DoubleBind(); } @@ -129,7 +129,7 @@ public void Unload() /// 全部参照id ObjectIdCollection GetAllXrefNode() { - //储存要处理的参照id + // 储存要处理的参照id var xrefIds = new ObjectIdCollection(); XrefNodeForEach((xNodeName, xNodeId, xNodeStatus, xNodeIsNested) => { if (XrefNamesContains(xNodeName)) @@ -140,7 +140,7 @@ ObjectIdCollection GetAllXrefNode() bool XrefNamesContains(string xNodeName) { - //为空的时候全部加入 || 有内容时候含有目标 + // 为空的时候全部加入 || 有内容时候含有目标 return _xrefNames is null || _xrefNames.Contains(xNodeName); } @@ -156,7 +156,7 @@ void XrefNodeForEach(Action action) if (action == null) return; - //解析外部参照:此功能不能锁定文档 + // 解析外部参照:此功能不能锁定文档 _tr.Database.ResolveXrefs(UseThreadEngine, DoNewOnly); var xg = _tr.Database.GetHostDwgXrefGraph(IncludeGhosts); @@ -189,9 +189,9 @@ static void AddedxbindIds(ObjectIdCollection xbindXrefsIds, ObjectIdCollection GetXBindIds() { - //xbind - //0x01 它是用来绑其他符号表,绑块表会有异常 - //0x02 集合若有问题,就会出现eWrongObjectType + // xbind + // 0x01 它是用来绑其他符号表,绑块表会有异常 + // 0x02 集合若有问题,就会出现eWrongObjectType var xbindIds = new ObjectIdCollection(); // 起初测试是将九大符号表记录均加入的,但经实测不行...(为什么?存疑) @@ -228,14 +228,14 @@ ObjectIdCollection GetXBindIds() ObjectIdCollection GetBindIds() { - //bind 只绑块表 + // bind 只绑块表 var bindIds = new ObjectIdCollection(); _tr.BlockTable.ForEach(btr => { if (btr.IsLayout) return; - //外部参照 && 已融入 + // 外部参照 && 已融入 if (btr.IsFromExternalReference && btr.IsResolved) bindIds.Add(btr.ObjectId); }, checkIdOk: true); @@ -253,24 +253,24 @@ List GetDetachIds(Dictionary nested) // 直接拆离的id List detachIds = new(); - //收集要处理的id + // 收集要处理的id XrefNodeForEach((xNodeName, xNodeId, xNodeStatus, xNodeIsNested) => { switch (xNodeStatus) { - case XrefStatus.Unresolved://未融入_ResolveXrefs参数2 + case XrefStatus.Unresolved:// 未融入_ResolveXrefs参数2 break; - case XrefStatus.FileNotFound://未融入(未解析)_未找到文件 + case XrefStatus.FileNotFound:// 未融入(未解析)_未找到文件 break; - case XrefStatus.Unreferenced://未参照 + case XrefStatus.Unreferenced:// 未参照 { - //为空的时候全部加入 || 有内容时候含有目标 + // 为空的时候全部加入 || 有内容时候含有目标 if (XrefNamesContains(xNodeName)) detachIds.Add(xNodeId); } break; - case XrefStatus.Unloaded://已卸载 + case XrefStatus.Unloaded:// 已卸载 { - //为空的时候全部加入 || 有内容时候含有目标 + // 为空的时候全部加入 || 有内容时候含有目标 if (XrefNamesContains(xNodeName)) { var btr = _tr.GetObject(xNodeId); @@ -279,14 +279,14 @@ List GetDetachIds(Dictionary nested) if (!xNodeIsNested) detachIds.Add(xNodeId); else if (!nested.ContainsKey(xNodeId)) - nested.Add(xNodeId, xNodeName);//嵌套参照 + nested.Add(xNodeId, xNodeName);// 嵌套参照 } } } break; - case XrefStatus.Resolved://已融入_就是可以绑定的 + case XrefStatus.Resolved:// 已融入_就是可以绑定的 break; - case XrefStatus.NotAnXref://不是外部参照 + case XrefStatus.NotAnXref:// 不是外部参照 break; default: break; @@ -304,18 +304,18 @@ void DoubleBind() Dictionary nested = new(); var detachIds = GetDetachIds(nested); - //拆离:未参照的文件 + // 拆离:未参照的文件 if (AutoDetach) { for (int i = 0; i < detachIds.Count; i++) _tr.Database.DetachXref(detachIds[i]); } - //重载:嵌套参照已卸载了,需要重载之后才能进行绑定 + // 重载:嵌套参照已卸载了,需要重载之后才能进行绑定 var keys = nested.Keys; if (keys.Count > 0) _tr.Database.ReloadXrefs(new ObjectIdCollection(keys.ToArray())); - //绑定:切勿交换,否则会绑定无效 + // 绑定:切勿交换,否则会绑定无效 var bindIds = GetBindIds(); var xbindIds = GetXBindIds(); if (xbindIds.Count > 0) @@ -328,13 +328,13 @@ void DoubleBind() if (EraseNested) { #if ac2008 - //因为Acad08索引器存在会暴露isErase的(桌子底层的原因), - //也就是可能获取两个名称一样的,只能用遍历的方式进行 + // 因为Acad08索引器存在会暴露isErase的(桌子底层的原因), + // 也就是可能获取两个名称一样的,只能用遍历的方式进行 HashSet nestedHash = new(); foreach (var item in nested) nestedHash.Add(item.Value); - //遍历全图,找到参照名称一样的删除 + // 遍历全图,找到参照名称一样的删除 _tr.BlockTable.ForEach(btr => { if (btr.IsLayout) return; @@ -459,7 +459,7 @@ public XrefPath(BlockReference brf, DBTrans tr) CurrentDatabasePath = Path.GetDirectoryName(tr.Database.Filename); - var btRec = tr.GetObject(brf.BlockTableRecord);//块表记录 + var btRec = tr.GetObject(brf.BlockTableRecord);// 块表记录 if (btRec == null) return; @@ -467,18 +467,18 @@ public XrefPath(BlockReference brf, DBTrans tr) if (!IsFromExternalReference) return; - //相对路径==".\\AA.dwg" - //无路径=="AA.dwg" + // 相对路径==".\\AA.dwg" + // 无路径=="AA.dwg" PathSave = btRec.PathName; if ((!string.IsNullOrEmpty(PathSave) && PathSave[0] == '.') || File.Exists(PathSave)) { - //相对路径||绝对路径 + // 相对路径||绝对路径 PathDescribe = PathSave; } else { - //无路径 + // 无路径 var db = btRec.GetXrefDatabase(true); PathDescribe = db.Filename; } @@ -530,10 +530,10 @@ public static string GetRelativePath(string strDbPath, string strXrefPath) Uri uri1 = new(strXrefPath); Uri uri2 = new(strDbPath); Uri relativeUri = uri2.MakeRelativeUri(uri1); - //测试例子变成 01.%E8%BE%85%E5%8A%A9%E6%96%87%E4%BB%B6/%E5%9B%BE%E6%A1%86/A3%E5%9B%BE%E6%A1%86.dwg + // 测试例子变成 01.%E8%BE%85%E5%8A%A9%E6%96%87%E4%BB%B6/%E5%9B%BE%E6%A1%86/A3%E5%9B%BE%E6%A1%86.dwg string str = relativeUri.ToString(); - //因为这里不会实现".\A.dwg"而是"A.dwg",所以加入这个操作,满足同目录文件 + // 因为这里不会实现".\A.dwg"而是"A.dwg",所以加入这个操作,满足同目录文件 var strs = str.Split('\\'); if (strs.Length == 1) str = ".\\" + str; @@ -555,38 +555,38 @@ static string GetRelativePath(string directory, string file) { string[] directorys = directory.Split('\\'); string[] files = file.Split('\\'); - //获取两条路径中的最短路径 + // 获取两条路径中的最短路径 int getMinLength = directorys.Length < files.Length ? directorys.Length : files.Length; - //用于确定我们退出的循环中的位置。 + // 用于确定我们退出的循环中的位置。 int lastCommonRoot = -1; int index; - //找到共根 + // 找到共根 for (index = 0; index < getMinLength; index++) { if (directorys[index] != files[index]) break; lastCommonRoot = index; } - //如果我们没有找到一个共同的前缀,那么抛出 + // 如果我们没有找到一个共同的前缀,那么抛出 if (lastCommonRoot == -1) throw new ArgumentException("路径没有公共相同路径部分"); - //建立相对路径 + // 建立相对路径 var result = new StringBuilder(); for (index = lastCommonRoot + 1; index < directorys.Length; index++) if (directorys[index].Length > 0) - result.Append("..\\");//上级目录加入 + result.Append("..\\");// 上级目录加入 - //添加文件夹 + // 添加文件夹 for (index = lastCommonRoot + 1; index < files.Length - 1; index++) result.Append(files[index] + "\\"); - //本级目录 + // 本级目录 if (result.Length == 0) result.Append(".\\"); - //result.Append(strXrefPaths[^1]);//下级目录加入 - result.Append(files[files.Length - 1]);//下级目录加入 + // result.Append(strXrefPaths[^1]);// 下级目录加入 + result.Append(files[files.Length - 1]);// 下级目录加入 return result.ToString(); } #endif @@ -616,7 +616,7 @@ static string GetRelativePath(string directory, string file) const char slash = '\\'; - //判断向上删除几个 + // 判断向上删除几个 var path_xiangduis = relativeName.Split(slash); int index = 0; for (int i = 0; i < path_xiangduis.Length; i++) @@ -627,15 +627,15 @@ static string GetRelativePath(string directory, string file) } var result = new StringBuilder(); - //前段 + // 前段 var path_dwgs = directory.Split(slash); - path_dwgs = path_dwgs.Where(s => !string.IsNullOrEmpty(s)).ToArray();//清理空数组 + path_dwgs = path_dwgs.Where(s => !string.IsNullOrEmpty(s)).ToArray();// 清理空数组 for (int i = 0; i < path_dwgs.Length - index; i++) { result.Append(path_dwgs[i]); result.Append(slash); } - //后段 + // 后段 for (int i = 0; i < path_xiangduis.Length; i++) { var item = path_xiangduis[i]; @@ -666,17 +666,17 @@ public static void Purge(this DBTrans tr, SymModes sym = SymModes.All) var ids = new ObjectIdCollection(); var db = tr.Database; - //0x01 - //db.Purge(ids)是获取未硬引用(无引用?)的对象,也就可以删除的. - //0x02 - //如果一个图元引用一个图层, - //假设这个图元是可以删除的(实际上它可能来自于词典记录的id) => 那么它被 db.Purge(ids) 识别, - //但是这个图层因为有硬引用,所以不被 db.Purge(ids) 识别, - //只能删除图元之后,循环第二次再通过 db.Purge(ids) 获取图层id. - //0x03 - //因为删除之后,符号表内的引用可能被修改,因此需要重复遍历符号表. - //0x04 - //试试循环事务 + // 0x01 + // db.Purge(ids)是获取未硬引用(无引用?)的对象,也就可以删除的. + // 0x02 + // 如果一个图元引用一个图层, + // 假设这个图元是可以删除的(实际上它可能来自于词典记录的id) => 那么它被 db.Purge(ids) 识别, + // 但是这个图层因为有硬引用,所以不被 db.Purge(ids) 识别, + // 只能删除图元之后,循环第二次再通过 db.Purge(ids) 获取图层id. + // 0x03 + // 因为删除之后,符号表内的引用可能被修改,因此需要重复遍历符号表. + // 0x04 + // 试试循环事务 if ((sym & SymModes.BlockTable) == SymModes.BlockTable) GetIds(ids, tr.BlockTable); @@ -692,13 +692,13 @@ public static void Purge(this DBTrans tr, SymModes sym = SymModes.All) GetIds(ids, tr.ViewportTable); if ((sym & SymModes.RegAppTable) == SymModes.RegAppTable) GetIds(ids, tr.RegAppTable); - //SHUN007 说这两个表可能有错误 + // SHUN007 说这两个表可能有错误 if ((sym & SymModes.ViewTable) == SymModes.ViewTable) GetIds(ids, tr.ViewTable); if ((sym & SymModes.UcsTable) == SymModes.UcsTable) GetIds(ids, tr.UcsTable); - //Purge是查询能够清理的对象 + // Purge是查询能够清理的对象 db.Purge(ids); foreach (ObjectId id in ids) id.Erase(); diff --git "a/src/IFoxCAD.Cad.Shared/ExtensionMethod/\346\226\260\345\273\272\345\241\253\345\205\205/HatchConverter.cs" "b/src/IFoxCAD.Cad.Shared/ExtensionMethod/\346\226\260\345\273\272\345\241\253\345\205\205/HatchConverter.cs" index 7868bee..09af1d6 100644 --- "a/src/IFoxCAD.Cad.Shared/ExtensionMethod/\346\226\260\345\273\272\345\241\253\345\205\205/HatchConverter.cs" +++ "b/src/IFoxCAD.Cad.Shared/ExtensionMethod/\346\226\260\345\273\272\345\241\253\345\205\205/HatchConverter.cs" @@ -88,23 +88,23 @@ public HatchConverter(Hatch hatch) : this() { _oldHatch = hatch; - //不能在提取信息的时候进行新建cad图元, - //否则cad将会提示遗忘释放 + // 不能在提取信息的时候进行新建cad图元, + // 否则cad将会提示遗忘释放 hatch.ForEach(loop => { var hcData = new HatchConverterData(); bool isCurve2d = true; if (loop.IsPolyline) { - //边界是多段线 + // 边界是多段线 HatchLoopIsPolyline(loop, hcData); isCurve2d = false; } else { - if (loop.Curves.Count == 2)//1是不可能的,大于2的是曲线 + if (loop.Curves.Count == 2)// 1是不可能的,大于2的是曲线 { - //边界是曲线,过滤可能是圆形的情况 + // 边界是曲线,过滤可能是圆形的情况 var cir = TwoArcFormOneCircle(loop); if (cir is not null) { @@ -114,7 +114,7 @@ public HatchConverter(Hatch hatch) : this() } } - //边界是曲线 + // 边界是曲线 if (isCurve2d) HatchLoopIsCurve2d(loop, hcData); @@ -137,9 +137,9 @@ static void HatchLoopIsPolyline(HatchLoop loop, HatchConverterData hcData) if (hcData is null) throw new ArgumentNullException(nameof(hcData)); - //判断为圆形: - //上下两个圆弧,然后填充,就会生成此种填充 - //顶点数是3,凸度是半圆,两个半圆就是一个圆形 + // 判断为圆形: + // 上下两个圆弧,然后填充,就会生成此种填充 + // 顶点数是3,凸度是半圆,两个半圆就是一个圆形 if (loop.Polyline.Count == 3 && loop.Polyline[0].Bulge == 1 && loop.Polyline[1].Bulge == 1 || loop.Polyline.Count == 3 && loop.Polyline[0].Bulge == -1 && loop.Polyline[1].Bulge == -1) { @@ -147,7 +147,7 @@ static void HatchLoopIsPolyline(HatchLoop loop, HatchConverterData hcData) } else { - //遍历多段线信息 + // 遍历多段线信息 var bvc = loop.Polyline; for (int i = 0; i < bvc.Count; i++) hcData.PolyLineData.Add(new BulgeVertexWidth(bvc[i])); @@ -170,23 +170,23 @@ static void HatchLoopIsPolyline(HatchLoop loop, HatchConverterData hcData) CircleData? circular = null; - //判断为圆形: - //用一条(不是两条)多段线画出两条圆弧为正圆,就会生成此种填充 - //边界为曲线,数量为2,可能是两个半圆曲线,如果是,就加入圆形数据中 + // 判断为圆形: + // 用一条(不是两条)多段线画出两条圆弧为正圆,就会生成此种填充 + // 边界为曲线,数量为2,可能是两个半圆曲线,如果是,就加入圆形数据中 - //第一段 - var getCurves1Pts = loop.Curves[0].GetSamplePoints(3); //曲线取样点分两份(3点) - var mid1Pt = getCurves1Pts[1]; //腰点 + // 第一段 + var getCurves1Pts = loop.Curves[0].GetSamplePoints(3); // 曲线取样点分两份(3点) + var mid1Pt = getCurves1Pts[1]; // 腰点 double bulge1 = loop.Curves[0].StartPoint.GetArcBulge(mid1Pt, loop.Curves[0].EndPoint); - //第二段 + // 第二段 var getCurves2Pts = loop.Curves[1].GetSamplePoints(3); var mid2Pt = getCurves2Pts[1]; double bulge2 = loop.Curves[1].StartPoint.GetArcBulge(mid2Pt, loop.Curves[1].EndPoint); - //第一段上弧&&第二段反弧 || 第一段反弧&&第二段上弧 + // 第一段上弧&&第二段反弧 || 第一段反弧&&第二段上弧 if (bulge1 == -1 && bulge2 == -1 || bulge1 == 1 && bulge2 == 1) - circular = new CircleData(loop.Curves[0].StartPoint, loop.Curves[1].StartPoint); //两个起点就是对称点 + circular = new CircleData(loop.Curves[0].StartPoint, loop.Curves[1].StartPoint); // 两个起点就是对称点 return circular; } @@ -198,36 +198,36 @@ static void HatchLoopIsPolyline(HatchLoop loop, HatchConverterData hcData) /// 收集图元信息 static void HatchLoopIsCurve2d(HatchLoop loop, HatchConverterData hcData) { - //取每一段曲线,曲线可能是直线来的,但是圆弧会按照顶点来分段 + // 取每一段曲线,曲线可能是直线来的,但是圆弧会按照顶点来分段 int curveIsClosed = 0; - //遍历边界的多个子段 + // 遍历边界的多个子段 foreach (Curve2d curve in loop.Curves) { - //计数用于实现闭合 + // 计数用于实现闭合 curveIsClosed++; if (curve is NurbCurve2d spl) { - //判断为样条曲线: + // 判断为样条曲线: hcData.SplineData.Add(spl); continue; } var pts = curve.GetSamplePoints(3); var midPt = pts[1]; - if (curve.StartPoint.IsEqualTo(curve.EndPoint, new Tolerance(1e-6, 1e-6)))//首尾相同,就是圆形 + if (curve.StartPoint.IsEqualTo(curve.EndPoint, new Tolerance(1e-6, 1e-6)))// 首尾相同,就是圆形 { - //判断为圆形: - //获取起点,然后采样三点,中间就是对称点(直径点) + // 判断为圆形: + // 获取起点,然后采样三点,中间就是对称点(直径点) hcData.CircleData.Add(new CircleData(curve.StartPoint, midPt)); continue; } - //判断为多段线,圆弧: + // 判断为多段线,圆弧: double bulge = curve.StartPoint.GetArcBulge(midPt, curve.EndPoint); hcData.PolyLineData.Add(new BulgeVertexWidth(curve.StartPoint, bulge)); - //末尾点,不闭合的情况下就要获取这个 + // 末尾点,不闭合的情况下就要获取这个 if (curveIsClosed == loop.Curves.Count) hcData.PolyLineData.Add(new BulgeVertexWidth(curve.EndPoint, 0)); } @@ -243,7 +243,7 @@ public void CreateBoundaryEntitys(List outEnts) { var data = _hcDatas[i]; - //生成边界:多段线 + // 生成边界:多段线 if (data.PolyLineData.Count > 0) { Polyline pl = new(); @@ -259,12 +259,12 @@ public void CreateBoundaryEntitys(List outEnts) outEnts.Add(pl); } - //生成边界:圆 + // 生成边界:圆 data.CircleData.ForEach(item => { outEnts.Add(new Circle(item.Center.Point3d(), Vector3d.ZAxis, item.Radius)); }); - //生成边界:样条曲线 + // 生成边界:样条曲线 data.SplineData.ForEach(item => { outEnts.Add(item.ToCurve()); }); @@ -293,7 +293,7 @@ public ObjectId CreateBoundarysAndHatchToMsPs(BlockTableRecord btrOfAddEntitySpa bool createHatchFlag = true, Transaction? trans = null) { - //重设边界之前肯定是有边界才可以 + // 重设边界之前肯定是有边界才可以 if (BoundaryIds.Count == 0) { List boundaryEntitys = new(); @@ -334,7 +334,7 @@ public ObjectId CreateBoundarysAndHatchToMsPs(BlockTableRecord btrOfAddEntitySpa void ResetBoundary(Hatch hatch, bool boundaryAssociative = true) { - //删除原有边界 + // 删除原有边界 while (hatch.NumberOfLoops != 0) hatch.RemoveLoopAt(0); @@ -345,13 +345,13 @@ void ResetBoundary(Hatch hatch, { obIds.Clear(); obIds.Add(BoundaryIds[i]); - //要先添加最外面的边界 + // 要先添加最外面的边界 if (i == 0) hatch.AppendLoop(HatchLoopTypes.Outermost, obIds); else hatch.AppendLoop(HatchLoopTypes.Default, obIds); } - //计算填充并显示 + // 计算填充并显示 hatch.EvaluateHatch(true); } #endregion diff --git "a/src/IFoxCAD.Cad.Shared/ExtensionMethod/\346\226\260\345\273\272\345\241\253\345\205\205/HatchInfo.cs" "b/src/IFoxCAD.Cad.Shared/ExtensionMethod/\346\226\260\345\273\272\345\241\253\345\205\205/HatchInfo.cs" index 1226479..d753460 100644 --- "a/src/IFoxCAD.Cad.Shared/ExtensionMethod/\346\226\260\345\273\272\345\241\253\345\205\205/HatchInfo.cs" +++ "b/src/IFoxCAD.Cad.Shared/ExtensionMethod/\346\226\260\345\273\272\345\241\253\345\205\205/HatchInfo.cs" @@ -1,56 +1,56 @@ -namespace IFoxCAD.Cad; +namespace IFoxCAD.Cad; /* - * ӵĵһ߽߽,ڶͼı߽硣 - * Ҫⲿ߽,ʹӻΪ HatchLoopTypes.Outermost AppendLoop , - * һ߽类,ͿԼı߽硣 - * ڲ߽ʹô HatchLoopTypes.Default AppendLoop + * 添加的第一个边界必须是外边界,即用于定义图案填充最外面的边界。 + * 要添加外部边界,请使用添加环的类型为 HatchLoopTypes.Outermost 常量的 AppendLoop 方法, + * 一旦外边界被定义,就可以继续添加另外的边界。 + * 添加内部边界请使用带 HatchLoopTypes.Default 常量的 AppendLoop 方法。 * - * ߽ʱ,ӵ(߽,߽,߽,߽ͨ....) - * ߽ʱ,ӵ(߽,߽ͨ.....߽,߽ͨ....) + * 多个外边界的时候,添加的是(外边界,外边界,外边界,普通边界....) + * 多个外边界的时候,添加的是(外边界,普通边界.....外边界,普通边界....) */ /// -/// ͼ +/// 图案填充 /// public class HatchInfo { - #region Ա + #region 成员 /// - /// ߽id(ŵһ) + /// 边界id(最外面放第一) /// readonly List _boundaryIds; /// - /// ͼԪ + /// 填充图元 /// readonly Hatch _hatch; /// - /// ߽(˴ֱ=>Ա,Ϊ뷴Ӧ) + /// 边界关联(此处不能直接=>给填充成员,因为它会加入反应器) /// readonly bool _boundaryAssociative; /// - /// :û(̶)//ݶļ + /// 填充的名称:用户定义(固定名称)/渐变/填充依据定义文件 /// string? _hatchName; /// - /// ģʽ(Ԥ/û/Զ) + /// 填充模式类型(预定义/用户定义/自定义) /// HatchPatternType _patternTypeHatch; /// - /// ģʽ + /// 渐变模式类型 /// GradientPatternType _patternTypeGradient; /// - /// / + /// 比例/间距 /// double Scale => _hatch.PatternScale; /// - /// Ƕ + /// 角度 /// double Angle => _hatch.PatternAngle; #endregion - #region + #region 构造 HatchInfo() { _hatch = new Hatch(); @@ -59,36 +59,36 @@ public class HatchInfo } /// - /// ͼ + /// 图案填充 /// - /// ߽ - /// ԭ - /// - /// Ƕ + /// 关联边界 + /// 填充原点 + /// 比例 + /// 角度 public HatchInfo(bool boundaryAssociative = true, Point2d? hatchOrigin = null, double hatchScale = 1, double hatchAngle = 0) : this() { if (hatchScale <= 0) - throw new ArgumentException("Сڵ0"); + throw new ArgumentException("填充比例不允许小于等于0"); - _hatch.PatternScale = hatchScale;// - _hatch.PatternAngle = hatchAngle;//Ƕ + _hatch.PatternScale = hatchScale;// 填充比例 + _hatch.PatternAngle = hatchAngle;// 填充角度 _boundaryAssociative = boundaryAssociative; hatchOrigin ??= Point2d.Origin; - _hatch.Origin = hatchOrigin.Value; //ԭ + _hatch.Origin = hatchOrigin.Value; // 填充原点 } /// - /// ͼ + /// 图案填充 /// - /// ߽ - /// ߽ - /// ԭ - /// - /// Ƕ + /// 边界 + /// 关联边界 + /// 填充原点 + /// 比例 + /// 角度 public HatchInfo(IEnumerable boundaryIds, bool boundaryAssociative = true, Point2d? hatchOrigin = null, @@ -101,111 +101,111 @@ public HatchInfo(IEnumerable boundaryIds, #endregion - #region + #region 方法 /// - /// ģʽ1:Ԥ + /// 模式1:预定义 /// public HatchInfo Mode1PreDefined(string name) { _hatchName = name; - _hatch.HatchObjectType = HatchObjectType.HatchObject; //(/) + _hatch.HatchObjectType = HatchObjectType.HatchObject; // 对象类型(填充/渐变) _patternTypeHatch = HatchPatternType.PreDefined; return this; } /// - /// ģʽ2:û + /// 模式2:用户定义 /// - /// Ƿ˫ + /// 是否双向 public HatchInfo Mode2UserDefined(bool patternDouble = true) { _hatchName = "_USER"; - _hatch.HatchObjectType = HatchObjectType.HatchObject; //(/) + _hatch.HatchObjectType = HatchObjectType.HatchObject; // 对象类型(填充/渐变) _patternTypeHatch = HatchPatternType.UserDefined; - _hatch.PatternDouble = patternDouble; //Ƿ˫򣨱д SetHatchPattern ֮ǰ - _hatch.PatternSpace = Scale; //ࣨд SetHatchPattern ֮ǰ + _hatch.PatternDouble = patternDouble; // 是否双向(必须写在 SetHatchPattern 之前) + _hatch.PatternSpace = Scale; // 间距(必须写在 SetHatchPattern 之前) return this; } /// - /// ģʽ3:Զ + /// 模式3:自定义 /// /// public HatchInfo Mode3UserDefined(string name) { _hatchName = name; - _hatch.HatchObjectType = HatchObjectType.HatchObject; //(/) + _hatch.HatchObjectType = HatchObjectType.HatchObject; // 对象类型(填充/渐变) _patternTypeHatch = HatchPatternType.CustomDefined; return this; } /// - /// ģʽ4: + /// 模式4:渐变填充 /// - /// - /// ɫʼɫ - /// ɫɫ - /// ƶ - /// ɫֵ - /// ɫ˫ɫ + /// 渐变填充名称 + /// 渐变色起始颜色 + /// 渐变色结束颜色 + /// 渐变移动 + /// 色调值 + /// 单色双色 public HatchInfo Mode4Gradient(GradientName name, Color colorStart, Color colorEnd, float gradientShift = 0, float shadeTintValue = 0, bool gradientOneColorMode = false) { - //entgetֱȻ"SOLID",Ϊ"","" + // entget渐变的名字必然是"SOLID",但是这里作为"渐变"名,而不是"填充"名 _hatchName = name.ToString(); - _hatch.HatchObjectType = HatchObjectType.GradientObject; //(/) - _patternTypeGradient = GradientPatternType.PreDefinedGradient;//ģʽ4: - //_patternTypeGradient = GradientPatternType.UserDefinedGradient;//ģʽ5:..ģʽɶ + _hatch.HatchObjectType = HatchObjectType.GradientObject; // 对象类型(填充/渐变) + _patternTypeGradient = GradientPatternType.PreDefinedGradient;// 模式4:渐变 + // _patternTypeGradient = GradientPatternType.UserDefinedGradient;// 模式5:渐变..这种模式干啥用呢 - //ýɫʼͽɫ + // 设置渐变色填充的起始和结束颜色 var gColor1 = new GradientColor(colorStart, 0); var gColor2 = new GradientColor(colorEnd, 1); _hatch.SetGradientColors(new GradientColor[] { gColor1, gColor2 }); - _hatch.GradientShift = gradientShift; //ݶλ - _hatch.ShadeTintValue = shadeTintValue; //Ӱɫֵ - _hatch.GradientOneColorMode = gradientOneColorMode;//䵥ɫ/˫ɫ - _hatch.GradientAngle = Angle; //Ƕ + _hatch.GradientShift = gradientShift; // 梯度位移 + _hatch.ShadeTintValue = shadeTintValue; // 阴影色值 + _hatch.GradientOneColorMode = gradientOneColorMode;// 渐变单色/双色 + _hatch.GradientAngle = Angle; // 渐变角度 return this; } /// - /// + /// 构建 /// - /// ˿ռ + /// 将填充加入此空间 public ObjectId Build(BlockTableRecord btrOfAddEntitySpace) { - //ݿ + // 加入数据库 var hatchId = btrOfAddEntitySpace.AddEntity(_hatch); - //ģʽ:/ + // 设置模式:渐变/填充 if (_hatch.HatchObjectType == HatchObjectType.GradientObject) _hatch.SetGradient(_patternTypeGradient, _hatchName); else _hatch.SetHatchPattern(_patternTypeHatch, _hatchName); - //߽,ݿռھͻ - //Ϊ true 뷴Ӧ,˱Ƚ(ά뽫ʮɺ),. + // 关联边界,如果不先添加数据库空间内就会出错 + // 为 true 会加入反应器,因此比较慢(二维码将会十几秒才生成好),视需求而定. _hatch.Associative = _boundaryAssociative; - // AppendLoop ؼ,Ͳ + // 利用 AppendLoop 重载加入,这里就不处理 if (_boundaryIds.Count > 0) AppendLoop(_boundaryIds, HatchLoopTypes.Default); - //䲢ʾ(߽,쳣) + // 计算填充并显示(若边界出错,这句会异常) _hatch.EvaluateHatch(true); return hatchId; } /// - /// ִͼԪ޸ + /// 执行图元的属性修改 /// - /// ӳʵ + /// 扔出填充实体 public HatchInfo Action(Action action) { action(_hatch); @@ -213,7 +213,7 @@ public HatchInfo Action(Action action) } /// - /// ձ߽缯 + /// 清空边界集合 /// public HatchInfo ClearBoundary() { @@ -222,7 +222,7 @@ public HatchInfo ClearBoundary() } /// - /// ɾ߽ͼԪ + /// 删除边界图元 /// public HatchInfo EraseBoundary() { @@ -232,16 +232,16 @@ public HatchInfo EraseBoundary() } /// - /// ߽ + /// 加入边界 /// - /// ߽id - /// 뷽ʽ + /// 边界id + /// 加入方式 void AppendLoop(IEnumerable boundaryIds, HatchLoopTypes hatchLoopTypes = HatchLoopTypes.Default) { var obIds = new ObjectIdCollection(); - //߽DZպϵ,Ѿݿ - //պϻ. + // 边界是闭合的,而且已经加入数据库 + // 填充闭合环类型.最外面 foreach (var border in boundaryIds) { obIds.Clear(); @@ -252,12 +252,12 @@ void AppendLoop(IEnumerable boundaryIds, } /// - /// ߽(¸߰汾亯) + /// 加入边界(仿高版本的填充函数) /// - /// 㼯 - /// ͹ȼ - /// ˿ռ - /// 뷽ʽ + /// 点集 + /// 凸度集 + /// 加入此空间 + /// 加入方式 /// public HatchInfo AppendLoop(Point2dCollection pts, DoubleCollection bluges, @@ -271,8 +271,8 @@ public HatchInfo AppendLoop(Point2dCollection pts, #if NET35 _boundaryIds.Add(CreateAddBoundary(ptsEnd2End, bluges, btrOfAddEntitySpace)); #else - //2011API,ԲͼԪ¼߽, - //ͨĻ,߽ _boundaryIds ǿյ,ô Build() ʱҪ˿յ + // 2011新增API,可以不生成图元的情况下加入边界, + // 通过这里进入的话,边界 _boundaryIds 是空的,那么 Build() 时候就需要过滤空的 _hatch.AppendLoop(hatchLoopTypes, ptsEnd2End, bluges); #endif return this; @@ -280,12 +280,12 @@ public HatchInfo AppendLoop(Point2dCollection pts, #if NET35 /// - /// ͨ㼯͹ɱ߽Ķ + /// 通过点集和凸度生成边界的多段线 /// - /// 㼯 - /// ͹ȼ - /// ˿ռ - /// id + /// 点集 + /// 凸度集 + /// 加入此空间 + /// 多段线id static ObjectId CreateAddBoundary(Point2dCollection? pts, DoubleCollection? bluges, BlockTableRecord btrOfAddEntitySpace) @@ -307,46 +307,46 @@ static ObjectId CreateAddBoundary(Point2dCollection? pts, #endif #endregion - #region ö + #region 枚举 /// - /// ɫͼ + /// 渐变色填充的图案名称 /// public enum GradientName { /// - /// ״ + /// 线状渐变 /// Linear, /// - /// Բ״ + /// 圆柱状渐变 /// Cylinder, /// - /// Բ״ + /// 反圆柱状渐变 /// Invcylinder, /// - /// ״ + /// 球状渐变 /// Spherical, /// - /// ״ + /// 反球状渐变 /// Invspherical, /// - /// ״ + /// 半球状渐变 /// Hemisperical, /// - /// ״ + /// 反半球状渐变 /// InvHemisperical, /// - /// ״ + /// 抛物面状渐变 /// Curved, /// - /// ״ + /// 反抛物面状渐变 /// Incurved } diff --git "a/src/IFoxCAD.Cad.Shared/ExtensionMethod/\346\226\260\345\273\272\346\226\207\345\255\227/AttachmentPointHelper.cs" "b/src/IFoxCAD.Cad.Shared/ExtensionMethod/\346\226\260\345\273\272\346\226\207\345\255\227/AttachmentPointHelper.cs" index 989721c..e630a51 100644 --- "a/src/IFoxCAD.Cad.Shared/ExtensionMethod/\346\226\260\345\273\272\346\226\207\345\255\227/AttachmentPointHelper.cs" +++ "b/src/IFoxCAD.Cad.Shared/ExtensionMethod/\346\226\260\345\273\272\346\226\207\345\255\227/AttachmentPointHelper.cs" @@ -5,29 +5,29 @@ public static class AttachmentPointHelper static readonly Dictionary _alignment = new() { { "左上", AttachmentPoint.TopLeft }, - { "中上", AttachmentPoint.TopCenter },//单行的对齐 + { "中上", AttachmentPoint.TopCenter },// 单行的对齐 { "右上", AttachmentPoint.TopRight }, { "左中", AttachmentPoint.MiddleLeft }, - { "正中", AttachmentPoint.MiddleCenter },//多行的正中 + { "正中", AttachmentPoint.MiddleCenter },// 多行的正中 { "右中", AttachmentPoint.MiddleRight }, - { "左对齐", AttachmentPoint.BaseLeft },//※优先(放在前面优先获取) + { "左对齐", AttachmentPoint.BaseLeft },// ※优先(放在前面优先获取) { "左", AttachmentPoint.BaseLeft }, { "中间", AttachmentPoint.BaseMid }, - { "右对齐", AttachmentPoint.BaseRight },//※优先(放在前面优先获取) + { "右对齐", AttachmentPoint.BaseRight },// ※优先(放在前面优先获取) { "右", AttachmentPoint.BaseRight }, { "左下", AttachmentPoint.BottomLeft }, { "中下", AttachmentPoint.BottomCenter }, { "右下", AttachmentPoint.BottomRight }, - { "对齐", AttachmentPoint.BaseAlign },//※优先(放在前面优先获取) + { "对齐", AttachmentPoint.BaseAlign },// ※优先(放在前面优先获取) { "调整", AttachmentPoint.BaseAlign }, - { "居中", AttachmentPoint.BaseCenter },//单行的中 + { "居中", AttachmentPoint.BaseCenter },// 单行的中 { "铺满", AttachmentPoint.BaseFit }, }; @@ -53,16 +53,16 @@ public static string Get(AttachmentPoint value) } #if false -//反射描述 -//这些东西cad没有用到啊...所以不纳入了 +// 反射描述 +// 这些东西cad没有用到啊...所以不纳入了 public enum AttachmentPoint2 { [Description("下对齐")] BottomAlign = 14, [Description("中对齐")] - MiddleAlign = 15,//0xF + MiddleAlign = 15,// 0xF [Description("上对齐")] - TopAlign = 16,//0x10 + TopAlign = 16,// 0x10 [Description("下铺满")] BottomFit = 18, [Description("中铺满")] diff --git "a/src/IFoxCAD.Cad.Shared/ExtensionMethod/\346\226\260\345\273\272\346\226\207\345\255\227/TextInfo.cs" "b/src/IFoxCAD.Cad.Shared/ExtensionMethod/\346\226\260\345\273\272\346\226\207\345\255\227/TextInfo.cs" index 573963b..d8bfd7a 100644 --- "a/src/IFoxCAD.Cad.Shared/ExtensionMethod/\346\226\260\345\273\272\346\226\207\345\255\227/TextInfo.cs" +++ "b/src/IFoxCAD.Cad.Shared/ExtensionMethod/\346\226\260\345\273\272\346\226\207\345\255\227/TextInfo.cs" @@ -59,16 +59,16 @@ public DBText AddDBTextToEntity() acText.SetDatabaseDefaults(); if (Database is not null) - acText.SetDatabaseDefaults(Database);//我的默认值是填满的,所以可以不需要 + acText.SetDatabaseDefaults(Database);// 我的默认值是填满的,所以可以不需要 if (TextStyleId is not null) acText.SetTextStyleId(TextStyleId.Value); - acText.Height = TextHeight; //高度 - acText.TextString = Contents; //内容 - acText.Position = Position; //插入点(一定要先设置) - acText.Justify = TextJustify; //使他们对齐 - //acText.HorizontalMode + acText.Height = TextHeight; // 高度 + acText.TextString = Contents; // 内容 + acText.Position = Position; // 插入点(一定要先设置) + acText.Justify = TextJustify; // 使他们对齐 + // acText.HorizontalMode if (AlignmentPoint is not null) acText.AlignmentPoint = AlignmentPoint.Value; @@ -98,18 +98,18 @@ public MText AddMTextToEntity() if (TextStyleId is not null) mText.SetTextStyleId(TextStyleId.Value); - mText.TextHeight = TextHeight; //高度 - mText.Contents = Contents; //内容 - mText.Location = Position; //插入点(一定要先设置) + mText.TextHeight = TextHeight; // 高度 + mText.Contents = Contents; // 内容 + mText.Location = Position; // 插入点(一定要先设置) - //mText.SetAttachmentMovingLocation(TextJustify); - mText.Attachment = TextJustify;//使他们对齐 + // mText.SetAttachmentMovingLocation(TextJustify); + mText.Attachment = TextJustify;// 使他们对齐 return mText; } } -//反射设定对象的文字样式id +// 反射设定对象的文字样式id public static partial class TextInfoHelper { /// @@ -169,9 +169,9 @@ static PropertyInfo GetTextStyleIdType(Entity acText) { var entType = acText.GetType(); var prs = entType.GetProperties(); - _textStyleId = prs.FirstOrDefault(a => a.Name == "TextStyle");//反射获取属性 + _textStyleId = prs.FirstOrDefault(a => a.Name == "TextStyle");// 反射获取属性 if (_textStyleId == null) - _textStyleId = prs.FirstOrDefault(a => a.Name == "TextStyleId");//反射获取属性 + _textStyleId = prs.FirstOrDefault(a => a.Name == "TextStyleId");// 反射获取属性 } return _textStyleId; } diff --git a/src/IFoxCAD.Cad.Shared/Runtime/AOP.cs b/src/IFoxCAD.Cad.Shared/Runtime/AOP.cs index ac8270b..4bb5cb3 100644 --- a/src/IFoxCAD.Cad.Shared/Runtime/AOP.cs +++ b/src/IFoxCAD.Cad.Shared/Runtime/AOP.cs @@ -1,99 +1,99 @@ -//namespace IFoxCAD.Cad; -//using HarmonyLib; +// namespace IFoxCAD.Cad; +// using HarmonyLib; -//public class IFoxRefuseInjectionTransaction : Attribute -//{ -// /// -// /// 拒绝注入事务 -// /// -// public IFoxRefuseInjectionTransaction() -// { -// } -//} - -//public class AOP -//{ -// /// -// /// 在此命名空间下的命令末尾注入清空事务栈函数 -// /// -// public static void Run(string nameSpace) -// { -// Dictionary cmdDic = new(); -// AutoClass.AppDomainGetTypes(type => { -// if (type.Namespace != nameSpace) -// return; -// //类上面特性 -// if (type.IsClass) -// { -// var attr = type.GetCustomAttributes(true); -// if (RefuseInjectionTransaction(attr)) -// return; -// } +// public class IFoxRefuseInjectionTransaction : Attribute +// { +// /// +// /// 拒绝注入事务 +// /// +// public IFoxRefuseInjectionTransaction() +// { +// } +// } -// //函数上面特性 -// var mets = type.GetMethods();//获得它的成员函数 -// for (int ii = 0; ii < mets.Length; ii++) -// { -// var method = mets[ii]; -// //找到特性,特性下面的方法要是Public,否则就被编译器优化掉了. -// var attr = method.GetCustomAttributes(true); -// for (int jj = 0; jj < attr.Length; jj++) -// if (attr[jj] is CommandMethodAttribute cmdAtt) -// { -// if (!RefuseInjectionTransaction(attr)) -// cmdDic.Add(cmdAtt.GlobalName, (cmdAtt, type, method)); -// } -// } -// }); +// public class AOP +// { +// /// +// /// 在此命名空间下的命令末尾注入清空事务栈函数 +// /// +// public static void Run(string nameSpace) +// { +// Dictionary cmdDic = new(); +// AutoClass.AppDomainGetTypes(type => { +// if (type.Namespace != nameSpace) +// return; +// // 类上面特性 +// if (type.IsClass) +// { +// var attr = type.GetCustomAttributes(true); +// if (RefuseInjectionTransaction(attr)) +// return; +// } -// //运行的命令写在了Test.dll,当然不是ifox.cad类库内了.... -// if (cmdDic.Count == 0) -// return; +// // 函数上面特性 +// var mets = type.GetMethods();// 获得它的成员函数 +// for (int ii = 0; ii < mets.Length; ii++) +// { +// var method = mets[ii]; +// // 找到特性,特性下面的方法要是Public,否则就被编译器优化掉了. +// var attr = method.GetCustomAttributes(true); +// for (int jj = 0; jj < attr.Length; jj++) +// if (attr[jj] is CommandMethodAttribute cmdAtt) +// { +// if (!RefuseInjectionTransaction(attr)) +// cmdDic.Add(cmdAtt.GlobalName, (cmdAtt, type, method)); +// } +// } +// }); -// var harmony = new Harmony(nameSpace); -// var mPrefix = SymbolExtensions.GetMethodInfo(() => IFoxCmdAddFirst());//进入函数前 -// var mPostfix = SymbolExtensions.GetMethodInfo(() => IFoxCmdAddLast());//进入函数后 -// var mp1 = new HarmonyMethod(mPrefix); -// var mp2 = new HarmonyMethod(mPostfix); +// // 运行的命令写在了Test.dll,当然不是ifox.cad类库内了.... +// if (cmdDic.Count == 0) +// return; -// foreach (var item in cmdDic) -// { -// //原函数执行(空间type,函数名) -// var mOriginal = AccessTools.Method(item.Value.MetType, item.Value.MetInfo.Name); -// //mOriginal.Invoke(); -// //新函数执行:创造两个函数加入里面 -// var newMet = harmony.Patch(mOriginal, mp1, mp2); -// //newMet.Invoke(); -// } -// } +// var harmony = new Harmony(nameSpace); +// var mPrefix = SymbolExtensions.GetMethodInfo(() => IFoxCmdAddFirst());// 进入函数前 +// var mPostfix = SymbolExtensions.GetMethodInfo(() => IFoxCmdAddLast());// 进入函数后 +// var mp1 = new HarmonyMethod(mPrefix); +// var mp2 = new HarmonyMethod(mPostfix); -// /// -// /// 拒绝注入事务 -// /// -// /// 属性 -// /// -// private static bool RefuseInjectionTransaction(object[] attr) -// { -// bool refuseInjectionTransaction = false; -// for (int kk = 0; kk < attr.Length; kk++) -// { -// if (attr[kk] is IFoxRefuseInjectionTransaction) -// { -// refuseInjectionTransaction = true; -// break; -// } -// } -// return refuseInjectionTransaction; -// } +// foreach (var item in cmdDic) +// { +// // 原函数执行(空间type,函数名) +// var mOriginal = AccessTools.Method(item.Value.MetType, item.Value.MetInfo.Name); +// // mOriginal.Invoke(); +// // 新函数执行:创造两个函数加入里面 +// var newMet = harmony.Patch(mOriginal, mp1, mp2); +// // newMet.Invoke(); +// } +// } -// public static void IFoxCmdAddFirst() -// { -// //此生命周期会在静态事务栈上面,被无限延长 -// var _ = DBTrans.Top; -// } +// /// +// /// 拒绝注入事务 +// /// +// /// 属性 +// /// +// private static bool RefuseInjectionTransaction(object[] attr) +// { +// bool refuseInjectionTransaction = false; +// for (int kk = 0; kk < attr.Length; kk++) +// { +// if (attr[kk] is IFoxRefuseInjectionTransaction) +// { +// refuseInjectionTransaction = true; +// break; +// } +// } +// return refuseInjectionTransaction; +// } -// public static void IFoxCmdAddLast() -// { -// DBTrans.FinishDatabase(); -// } -//} \ No newline at end of file +// public static void IFoxCmdAddFirst() +// { +// // 此生命周期会在静态事务栈上面,被无限延长 +// var _ = DBTrans.Top; +// } + +// public static void IFoxCmdAddLast() +// { +// DBTrans.FinishDatabase(); +// } +// } \ No newline at end of file diff --git a/src/IFoxCAD.Cad.Shared/Runtime/AutoRegAssem.cs b/src/IFoxCAD.Cad.Shared/Runtime/AutoRegAssem.cs index c208ab5..c6f861f 100644 --- a/src/IFoxCAD.Cad.Shared/Runtime/AutoRegAssem.cs +++ b/src/IFoxCAD.Cad.Shared/Runtime/AutoRegAssem.cs @@ -65,9 +65,9 @@ public AutoRegAssem(AutoRegConfig autoRegConfig) RegApp(); } - //实例化了 AutoClass 之后会自动执行 IFoxAutoGo 接口下面的类, - //以及自动执行特性 [IFoxInitialize] - //类库用户不在此处进行其他代码,而是实现特性 + // 实例化了 AutoClass 之后会自动执行 IFoxAutoGo 接口下面的类, + // 以及自动执行特性 [IFoxInitialize] + // 类库用户不在此处进行其他代码,而是实现特性 _autoRef = new AutoReflection(_info.Name, autoRegConfig); _autoRef.Initialize(); } @@ -85,7 +85,7 @@ public static RegistryKey GetAcAppKey(bool writable = true) RegistryKey? ackey = null; #if NET35 - string key = HostApplicationServices.Current.RegistryProductRootKey; //这里浩辰读出来是"" + string key = HostApplicationServices.Current.RegistryProductRootKey; // 这里浩辰读出来是"" #elif !HC2020 string key = HostApplicationServices.Current.UserRegistryProductRootKey; #endif @@ -93,14 +93,14 @@ public static RegistryKey GetAcAppKey(bool writable = true) #if !HC2020 ackey = Registry.CurrentUser.OpenSubKey(key, writable); #else - //浩辰 - var s = GrxCAD.DatabaseServices.HostApplicationServices.Current.RegistryProductRootKey;//浩辰奇怪的空值 + // 浩辰 + var s = GrxCAD.DatabaseServices.HostApplicationServices.Current.RegistryProductRootKey;// 浩辰奇怪的空值 string str = CadSystem.Getvar("ACADVER"); str = Regex.Replace(str, @"[^\d.\d]", ""); double.TryParse(str, out double a); - // string regedit = @"Software\Gstarsoft\GstarCAD\R" + a.ToString() + @"\zh-CN"; //2019 - // string regedit = @"Software\Gstarsoft\GstarCAD\B" + a.ToString() + @"\zh-CN";//2020 这里是 - string regedit = @"Software\Gstarsoft\GstarCAD\B20\zh-CN";//2020 这里是 + // string regedit = @"Software\Gstarsoft\GstarCAD\R" + a.ToString() + @"\zh-CN"; // 2019 + // string regedit = @"Software\Gstarsoft\GstarCAD\B" + a.ToString() + @"\zh-CN";// 2020 这里是 + string regedit = @"Software\Gstarsoft\GstarCAD\B20\zh-CN";// 2020 这里是 ackey = Registry.CurrentUser.OpenSubKey(regedit, writable); #endif @@ -131,7 +131,7 @@ public bool UnRegApp() /// bool SearchForReg() { - //在使用netloadx的时候,此处注册表是失效的,具体原因要进行netloadx测试 + // 在使用netloadx的时候,此处注册表是失效的,具体原因要进行netloadx测试 var appkey = GetAcAppKey(); if (appkey.SubKeyCount == 0) return false; @@ -139,7 +139,7 @@ bool SearchForReg() var regApps = appkey.GetSubKeyNames(); if (regApps.Contains(_info.Name)) { - //20220409 bug:文件名相同,路径不同,需要判断路径 + // 20220409 bug:文件名相同,路径不同,需要判断路径 var info = appkey.OpenSubKey(_info.Name); return info.GetValue("LOADER")?.ToString().ToLower() == _info.Loader.ToLower(); } @@ -160,7 +160,7 @@ public void RegApp() appkey.Close(); } - //这里的是不会自动执行的 + // 这里的是不会自动执行的 public void Initialize() { } public void Terminate() { } diff --git a/src/IFoxCAD.Cad.Shared/Runtime/CadVersion.cs b/src/IFoxCAD.Cad.Shared/Runtime/CadVersion.cs index 2e32c74..d9614bf 100644 --- a/src/IFoxCAD.Cad.Shared/Runtime/CadVersion.cs +++ b/src/IFoxCAD.Cad.Shared/Runtime/CadVersion.cs @@ -47,10 +47,10 @@ public override string ToString() // return base.GetHashCode(); // } - // //public override string ToString() - // //{ + // // public override string ToString() + // // { // // return base.ToString(); - // //} + // // } } #else public record CadVersion diff --git a/src/IFoxCAD.Cad.Shared/Runtime/DBTrans.cs b/src/IFoxCAD.Cad.Shared/Runtime/DBTrans.cs index 43ca19c..1109832 100644 --- a/src/IFoxCAD.Cad.Shared/Runtime/DBTrans.cs +++ b/src/IFoxCAD.Cad.Shared/Runtime/DBTrans.cs @@ -131,7 +131,7 @@ public DBTrans(string fileName, if (fileName == null || string.IsNullOrEmpty(fileName.Trim())) throw new ArgumentNullException(nameof(fileName)); - fileName = fileName.Replace("/", "\\");////doc.Name总是"D:\\JX.dwg" + fileName = fileName.Replace("/", "\\");//// doc.Name总是"D:\\JX.dwg" if (!File.Exists(fileName)) Database = new Database(true, false); @@ -154,33 +154,33 @@ public DBTrans(string fileName, else { #if ac2008 - //FileAccess fileAccess = FileAccess.Read; + // FileAccess fileAccess = FileAccess.Read; FileShare fileShare = FileShare.Read; switch (openMode) { - case FileOpenMode.OpenTryForReadShare://这个是什么状态?? - //fileAccess = FileAccess.ReadWrite; + case FileOpenMode.OpenTryForReadShare:// 这个是什么状态?? + // fileAccess = FileAccess.ReadWrite; fileShare = FileShare.ReadWrite; break; - case FileOpenMode.OpenForReadAndAllShare://完美匹配 - //fileAccess = FileAccess.ReadWrite; + case FileOpenMode.OpenForReadAndAllShare:// 完美匹配 + // fileAccess = FileAccess.ReadWrite; fileShare = FileShare.ReadWrite; break; - case FileOpenMode.OpenForReadAndWriteNoShare://完美匹配 - //fileAccess = FileAccess.ReadWrite; + case FileOpenMode.OpenForReadAndWriteNoShare:// 完美匹配 + // fileAccess = FileAccess.ReadWrite; fileShare = FileShare.None; break; - case FileOpenMode.OpenForReadAndReadShare://完美匹配 - //fileAccess = FileAccess.Read; + case FileOpenMode.OpenForReadAndReadShare:// 完美匹配 + // fileAccess = FileAccess.Read; fileShare = FileShare.Read; break; default: break; } - //这个会致命错误 - //using FileStream fileStream = new(fileName, FileMode.Open, fileAccess, fileShare); - //Database.ReadDwgFile(fileStream.SafeFileHandle.DangerousGetHandle(), + // 这个会致命错误 + // using FileStream fileStream = new(fileName, FileMode.Open, fileAccess, fileShare); + // Database.ReadDwgFile(fileStream.SafeFileHandle.DangerousGetHandle(), // true/*控制读入一个与系统编码不相同的文件时的转换操作*/,password); Database.ReadDwgFile(fileName, fileShare, @@ -360,7 +360,7 @@ public static implicit operator Transaction(DBTrans tr) public ObjectId GetObjectId(string handleString) { var hanle = new Handle(Convert.ToInt64(handleString, 16)); - //return Database.GetObjectId(false, hanle, 0); + // return Database.GetObjectId(false, hanle, 0); return DBTransHelper.TryGetObjectId(Database, hanle); } #endregion @@ -383,7 +383,7 @@ public void SaveDwgFile(DwgVersion version = DwgVersion.AC1800) } if (doca == null) // 后台开图,用数据库保存 { - //用了不存在的文件进行后台打开,并且设置保存,这个时候就软处理 + // 用了不存在的文件进行后台打开,并且设置保存,这个时候就软处理 if (string.IsNullOrEmpty(Database.Filename)) { Debug.WriteLine("**** 此数据没有保存路径,无法保存!"); @@ -416,15 +416,15 @@ public void Task(Action action, bool handlingDBTextDeviation = true) if (action == null) throw new ArgumentNullException(nameof(action)); - //前台开图 || 后台直接处理 + // 前台开图 || 后台直接处理 if (Document != null || !handlingDBTextDeviation) { action.Invoke(); return; } - //后台 - //这种情况发生在关闭了所有文档之后,进行跨进程通讯 + // 后台 + // 这种情况发生在关闭了所有文档之后,进行跨进程通讯 var dbBak = HostApplicationServices.WorkingDatabase; if (dbBak == null) { @@ -432,9 +432,9 @@ public void Task(Action action, bool handlingDBTextDeviation = true) return; } - //处理单行文字偏移 - //前台绑定参照的时候不能用它,否则出现:
    - //所以本函数自动识别前后台做处理 + // 处理单行文字偏移 + // 前台绑定参照的时候不能用它,否则出现:
    + // 所以本函数自动识别前后台做处理 HostApplicationServices.WorkingDatabase = Database; action.Invoke(); HostApplicationServices.WorkingDatabase = dbBak; @@ -486,30 +486,30 @@ protected virtual void Dispose(bool disposing) * 5. 清理非托管的字段 */ - //不重复释放,并设置已经释放 + // 不重复释放,并设置已经释放 if (IsDisposed) return; IsDisposed = true; if (disposing) { - //调用cad的事务进行提交,释放托管状态(托管对象) + // 调用cad的事务进行提交,释放托管状态(托管对象) Transaction.Commit(); } else { - //否则取消所有的修改 + // 否则取消所有的修改 Transaction.Abort(); } - //调用cad事务的dispose进行销毁 + // 调用cad事务的dispose进行销毁 if (!Transaction.IsDisposed) Transaction.Dispose(); - //调用文档锁dispose + // 调用文档锁dispose documentLock?.Dispose(); - //将事务栈的当前dbtrans弹栈 + // 将事务栈的当前dbtrans弹栈 dBTrans.Pop(); } #endregion diff --git a/src/IFoxCAD.Cad.Shared/Runtime/Env.cs b/src/IFoxCAD.Cad.Shared/Runtime/Env.cs index c2348da..6053962 100644 --- a/src/IFoxCAD.Cad.Shared/Runtime/Env.cs +++ b/src/IFoxCAD.Cad.Shared/Runtime/Env.cs @@ -260,19 +260,19 @@ public static DimblkType Dimblk get { string s = ((string)Acap.GetSystemVariable("dimblk")).ToUpper(); - //if (string.IsNullOrEmpty(s)) - //{ + // if (string.IsNullOrEmpty(s)) + // { // return DimblkType.Defult; - //} - //else - //{ + // } + // else + // { // if (dimdescdict.TryGetValue(s, out DimblkType value)) // { // return value; // } // return s.ToEnum(); - // //return s.FromDescName(); - //} + // // return s.FromDescName(); + // } return dimdescdict[s]; } set @@ -463,7 +463,7 @@ public static void SetVar(string varName, object value) /// 指定的环境变量的值;或者如果找不到环境变量,则返回 null public static string? GetEnv(string var) { - //从当前进程或者从当前用户或本地计算机的 Windows 操作系统注册表项检索环境变量的值 + // 从当前进程或者从当前用户或本地计算机的 Windows 操作系统注册表项检索环境变量的值 return Environment.GetEnvironmentVariable(var); } /// @@ -473,7 +473,7 @@ public static void SetVar(string varName, object value) /// 变量值 public static void SetEnv(string var, string? value) { - //创建、修改或删除当前进程中或者为当前用户或本地计算机保留的 Windows 操作系统注册表项中存储的环境变量 + // 创建、修改或删除当前进程中或者为当前用户或本地计算机保留的 Windows 操作系统注册表项中存储的环境变量 Environment.SetEnvironmentVariable(var, value); } #endregion diff --git a/src/IFoxCAD.Cad.Shared/Runtime/FileOpenMode.cs b/src/IFoxCAD.Cad.Shared/Runtime/FileOpenMode.cs index 5108dd5..912b531 100644 --- a/src/IFoxCAD.Cad.Shared/Runtime/FileOpenMode.cs +++ b/src/IFoxCAD.Cad.Shared/Runtime/FileOpenMode.cs @@ -1,4 +1,4 @@ -#if ac2008 //NET35 +#if ac2008 // NET35 namespace Autodesk.AutoCAD.DatabaseServices { [Wrapper("AcDbDatabase::OpenMode")] diff --git a/src/IFoxCAD.Cad.Shared/Runtime/IAutoGo.cs b/src/IFoxCAD.Cad.Shared/Runtime/IAutoGo.cs index f3ddcf2..62bdbb0 100644 --- a/src/IFoxCAD.Cad.Shared/Runtime/IAutoGo.cs +++ b/src/IFoxCAD.Cad.Shared/Runtime/IAutoGo.cs @@ -51,8 +51,8 @@ public IFoxInitialize(Sequence sequence = Sequence.Last, bool isInitialize = tru } } -//为了解决IExtensionApplication在一个dll内无法多次实现接口的关系 -//所以在这里反射加载所有的 IAutoGo ,以达到能分开写"启动运行"函数的目的 +// 为了解决IExtensionApplication在一个dll内无法多次实现接口的关系 +// 所以在这里反射加载所有的 IAutoGo ,以达到能分开写"启动运行"函数的目的 class RunClass { public Sequence Sequence { get; } @@ -83,8 +83,8 @@ public void Run() /// public class AutoReflection { - static List _InitializeList = new(); //储存方法用于初始化 - static List _TerminateList = new(); //储存方法用于结束释放 + static List _InitializeList = new(); // 储存方法用于初始化 + static List _TerminateList = new(); // 储存方法用于结束释放 readonly string _dllName; readonly AutoRegConfig _autoRegConfig; @@ -103,12 +103,12 @@ public AutoReflection(string dllName, AutoRegConfig configInfo) _autoRegConfig = configInfo; } - //启动cad的时候会自动执行 + // 启动cad的时候会自动执行 public void Initialize() { try { - //收集特性,包括启动时和关闭时 + // 收集特性,包括启动时和关闭时 if ((_autoRegConfig & AutoRegConfig.ReflectionAttribute) == AutoRegConfig.ReflectionAttribute) GetAttributeFunctions(_InitializeList, _TerminateList); @@ -117,7 +117,7 @@ public void Initialize() if (_InitializeList.Count > 0) { - //按照 SequenceId 排序_升序 + // 按照 SequenceId 排序_升序 _InitializeList = _InitializeList.OrderBy(runClass => runClass.Sequence).ToList(); RunFunctions(_InitializeList); } @@ -128,7 +128,7 @@ public void Initialize() } } - //关闭cad的时候会自动执行 + // 关闭cad的时候会自动执行 public void Terminate() { try @@ -138,7 +138,7 @@ public void Terminate() if (_TerminateList.Count > 0) { - //按照 SequenceId 排序_降序 + // 按照 SequenceId 排序_降序 _TerminateList = _TerminateList.OrderByDescending(runClass => runClass.Sequence).ToList(); RunFunctions(_TerminateList); } @@ -163,22 +163,22 @@ public static void AppDomainGetTypes(Action action, string? dllNameWithout { var assemblies = AppDomain.CurrentDomain.GetAssemblies(); #if !NET35 - //cad2021出现如下报错 - //System.NotSupportedException:动态程序集中不支持已调用的成员 - //assemblies = assemblies.Where(p => !p.IsDynamic).ToArray();//这个要容器类型转换 + // cad2021出现如下报错 + // System.NotSupportedException:动态程序集中不支持已调用的成员 + // assemblies = assemblies.Where(p => !p.IsDynamic).ToArray();// 这个要容器类型转换 assemblies = Array.FindAll(assemblies, p => !p.IsDynamic); #endif - //主程序域 + // 主程序域 for (int ii = 0; ii < assemblies.Length; ii++) { var assembly = assemblies[ii]; - //获取类型集合,反射时候还依赖其他的dll就会这个错误 - //此通讯库要跳过,否则会报错. + // 获取类型集合,反射时候还依赖其他的dll就会这个错误 + // 此通讯库要跳过,否则会报错. var location = Path.GetFileNameWithoutExtension(assembly.Location); if (dllNameWithoutExtension != null && location != dllNameWithoutExtension) continue; - if (location == "AcInfoCenterConn")//通讯库 + if (location == "AcInfoCenterConn")// 通讯库 continue; Type[]? types = null; diff --git a/src/IFoxCAD.Cad.Shared/Runtime/Log.cs b/src/IFoxCAD.Cad.Shared/Runtime/Log.cs index df2fe0d..fe05b5a 100644 --- a/src/IFoxCAD.Cad.Shared/Runtime/Log.cs +++ b/src/IFoxCAD.Cad.Shared/Runtime/Log.cs @@ -6,7 +6,7 @@ using Exception = Exception; #region 写入日志到不同的环境中 -//https://zhuanlan.zhihu.com/p/338492989 +// https://zhuanlan.zhihu.com/p/338492989 public abstract class LogBase { public abstract void DeleteLog(); @@ -59,7 +59,7 @@ public override string[] ReadLog() } public override void WriteLog(string? message) { - //把异常信息输出到文件 + // 把异常信息输出到文件 var sw = new StreamWriter(LogHelper.LogAddress, true/*当天日志文件存在就追加,否则就创建*/); sw.Write(message); sw.Flush(); @@ -197,20 +197,20 @@ public static void OptionFile(string? newlogAddress = null) /// 完整路径 public static string GetDefaultOption(string fileName, bool createDirectory = true) { - //微软回复:静态构造函数只会被调用一次, - //并且在它执行完成之前,任何其它线程都不能创建这个类的实例或使用这个类的静态成员 - //https://blog.csdn.net/weixin_34204722/article/details/90095812 + // 微软回复:静态构造函数只会被调用一次, + // 并且在它执行完成之前,任何其它线程都不能创建这个类的实例或使用这个类的静态成员 + // https://blog.csdn.net/weixin_34204722/article/details/90095812 var sb = new StringBuilder(); sb.Append(Environment.CurrentDirectory); sb.Append("\\ErrorLog"); - //新建文件夹 + // 新建文件夹 if (createDirectory) { var path = sb.ToString(); if (!Directory.Exists(path)) { - //设置文件夹属性为普通 + // 设置文件夹属性为普通 Directory.CreateDirectory(path) .Attributes = FileAttributes.Normal; } @@ -262,7 +262,7 @@ static string LogAction(Exception? ex, OptionFile(); } - //不写入错误 + // 不写入错误 if (target == LogTarget.FileNotException) ex = null; @@ -271,7 +271,7 @@ static string LogAction(Exception? ex, _logWriteLock.EnterWriteLock();// 写模式锁定 读写锁 var logtxt = new LogTxt(ex, message); - //var logtxtJson = Newtonsoft.Json.JsonConvert.SerializeObject(logtxt, Formatting.Indented); + // var logtxtJson = Newtonsoft.Json.JsonConvert.SerializeObject(logtxt, Formatting.Indented); var logtxtJson = logtxt?.ToString(); if (logtxtJson == null) return string.Empty; @@ -369,7 +369,7 @@ public LogTxt(Exception? ex, string? message) : this() #endregion -#if false //最简单的实现 +#if false // 最简单的实现 public static class Log { /// @@ -385,19 +385,19 @@ public static class Log static Log() { - //微软回复:静态构造函数只会被调用一次, - //并且在它执行完成之前,任何其它线程都不能创建这个类的实例或使用这个类的静态成员 - //https://blog.csdn.net/weixin_34204722/article/details/90095812 + // 微软回复:静态构造函数只会被调用一次, + // 并且在它执行完成之前,任何其它线程都不能创建这个类的实例或使用这个类的静态成员 + // https://blog.csdn.net/weixin_34204722/article/details/90095812 var sb = new StringBuilder(); sb.Append(Environment.CurrentDirectory); sb.Append("\\ErrorLog"); - //新建文件夹 + // 新建文件夹 var path = sb.ToString(); if (!Directory.Exists(path)) { Directory.CreateDirectory(path) - .Attributes = FileAttributes.Normal; //设置文件夹属性为普通 + .Attributes = FileAttributes.Normal; // 设置文件夹属性为普通 } sb.Append('\\'); @@ -422,13 +422,13 @@ static Log() _logWriteLock.EnterWriteLock();// 写模式锁定 读写锁 var logtxt = new LogTxt(ex, remarks); - //var logtxtJson = Newtonsoft.Json.JsonConvert.SerializeObject(logtxt, Formatting.Indented); + // var logtxtJson = Newtonsoft.Json.JsonConvert.SerializeObject(logtxt, Formatting.Indented); var logtxtJson = logtxt.ToString(); if (logtxtJson == null) return string.Empty; - //把异常信息输出到文件 + // 把异常信息输出到文件 var sw = new StreamWriter(_logAddress, true/*当天日志文件存在就追加,否则就创建*/); sw.Write(logtxtJson); sw.Flush(); @@ -439,8 +439,8 @@ static Log() { Debug.WriteLine("错误日志: " + _logAddress); Debug.Write(logtxtJson); - //Debugger.Break(); - //Debug.Assert(false, "终止进程"); + // Debugger.Break(); + // Debug.Assert(false, "终止进程"); } return logtxtJson; } diff --git a/src/IFoxCAD.Cad.Shared/Runtime/MethodInfoHelper.cs b/src/IFoxCAD.Cad.Shared/Runtime/MethodInfoHelper.cs index fac263c..5f8e2bc 100644 --- a/src/IFoxCAD.Cad.Shared/Runtime/MethodInfoHelper.cs +++ b/src/IFoxCAD.Cad.Shared/Runtime/MethodInfoHelper.cs @@ -17,19 +17,19 @@ internal static class MethodInfoHelper object? result = null; if (methodInfo.IsStatic) { - //新函数指针进入此处 - //参数数量一定要匹配,为null则参数个数不同导致报错, - //参数为stirng[],则可以传入object[]代替,其他参数是否还可以实现默认构造? + // 新函数指针进入此处 + // 参数数量一定要匹配,为null则参数个数不同导致报错, + // 参数为stirng[],则可以传入object[]代替,其他参数是否还可以实现默认构造? var args = new List { }; var paramInfos = methodInfo.GetParameters(); for (int i = 0; i < paramInfos.Length; i++) args.Add(null!); - result = methodInfo.Invoke(null, args.ToArray());//静态调用 + result = methodInfo.Invoke(null, args.ToArray());// 静态调用 } else { - //原命令的函数指针进入此处 - //object instance; + // 原命令的函数指针进入此处 + // object instance; if (methodDic.ContainsKey(methodInfo)) instance = methodDic[methodInfo]; @@ -37,16 +37,16 @@ internal static class MethodInfoHelper { var reftype = methodInfo.ReflectedType; if (reftype == null) return null; - var fullName = reftype.FullName; //命名空间+类 + var fullName = reftype.FullName; // 命名空间+类 if (fullName == null) return null; - var type = reftype.Assembly.GetType(fullName);//通过程序集反射创建类+ + var type = reftype.Assembly.GetType(fullName);// 通过程序集反射创建类+ if (type == null) return null; instance = Activator.CreateInstance(type); - if (!type.IsAbstract)//无法创建抽象类成员 + if (!type.IsAbstract)// 无法创建抽象类成员 methodDic.Add(methodInfo, instance); } if (instance != null) - result = methodInfo.Invoke(instance, null); //非静态,调用实例化方法 + result = methodInfo.Invoke(instance, null); // 非静态,调用实例化方法 } return result; } diff --git a/src/IFoxCAD.Cad.Shared/Runtime/SymbolTable.cs b/src/IFoxCAD.Cad.Shared/Runtime/SymbolTable.cs index 8d5c973..a619836 100644 --- a/src/IFoxCAD.Cad.Shared/Runtime/SymbolTable.cs +++ b/src/IFoxCAD.Cad.Shared/Runtime/SymbolTable.cs @@ -40,7 +40,7 @@ internal SymbolTable(DBTrans tr, ObjectId tableId, bool defaultBehavior = true) { if (CurrentSymbolTable is LayerTable layer) { - //层表包含隐藏的,全部显示出来 + // 层表包含隐藏的,全部显示出来 layer = layer.IncludingHidden; if (layer is TTable tt) CurrentSymbolTable = tt; @@ -172,7 +172,7 @@ private static void Change(TRecord record, Action action) using (record.ForWrite()) action.Invoke(record); // 调用regen()函数可能会导致卡顿 - //Env.Editor.Regen(); + // Env.Editor.Regen(); } /// diff --git a/src/IFoxCAD.Cad.Shared/Runtime/Utils.cs b/src/IFoxCAD.Cad.Shared/Runtime/Utils.cs index ff32aad..6c668da 100644 --- a/src/IFoxCAD.Cad.Shared/Runtime/Utils.cs +++ b/src/IFoxCAD.Cad.Shared/Runtime/Utils.cs @@ -74,18 +74,18 @@ static int GetAcDbObjectId(IntPtr db, Handle handle, out ObjectId id, bool creat public static ObjectId TryGetObjectId(Database db, Handle handle) { #if !NET35 - //高版本直接利用 + // 高版本直接利用 var es = db.TryGetObjectId(handle, out ObjectId id); - //if (!es) + // if (!es) #else var es = GetAcDbObjectId(db.UnmanagedObject, handle, out ObjectId id); - //if (ErrorStatus.OK != (ErrorStatus)es) + // if (ErrorStatus.OK != (ErrorStatus)es) #endif return id; } - //public static int GetCadFileVersion(string filename) - //{ + // public static int GetCadFileVersion(string filename) + // { // var bytes = File.ReadAllBytes(filename); // var headstr = Encoding.Default.GetString(bytes)[0..6]; // if (!headstr.StartsWith("AC")) return 0; @@ -94,5 +94,5 @@ public static ObjectId TryGetObjectId(Database db, Handle handle) // Enum.TryParse() // return vernum + 986; - //} + // } } \ No newline at end of file diff --git a/src/IFoxCAD.Cad/IFoxCAD.Aacad08/GlobalUsings.cs b/src/IFoxCAD.Cad/IFoxCAD.Aacad08/GlobalUsings.cs index 2c84a3c..9ea1ec2 100644 --- a/src/IFoxCAD.Cad/IFoxCAD.Aacad08/GlobalUsings.cs +++ b/src/IFoxCAD.Cad/IFoxCAD.Aacad08/GlobalUsings.cs @@ -25,7 +25,7 @@ global using Autodesk.AutoCAD.DatabaseServices.Filters; global using Autodesk.AutoCAD; -//jig命名空间会引起Viewport/Polyline等等重义,最好逐个引入 using Autodesk.AutoCAD.GraphicsInterface +// jig命名空间会引起Viewport/Polyline等等重义,最好逐个引入 using Autodesk.AutoCAD.GraphicsInterface global using WorldDraw = Autodesk.AutoCAD.GraphicsInterface.WorldDraw; global using Manager = Autodesk.AutoCAD.GraphicsSystem.Manager; diff --git a/src/IFoxCAD.Cad/IFoxCAD.Aacad09plus/GlobalUsings.cs b/src/IFoxCAD.Cad/IFoxCAD.Aacad09plus/GlobalUsings.cs index 2c84a3c..9ea1ec2 100644 --- a/src/IFoxCAD.Cad/IFoxCAD.Aacad09plus/GlobalUsings.cs +++ b/src/IFoxCAD.Cad/IFoxCAD.Aacad09plus/GlobalUsings.cs @@ -25,7 +25,7 @@ global using Autodesk.AutoCAD.DatabaseServices.Filters; global using Autodesk.AutoCAD; -//jig命名空间会引起Viewport/Polyline等等重义,最好逐个引入 using Autodesk.AutoCAD.GraphicsInterface +// jig命名空间会引起Viewport/Polyline等等重义,最好逐个引入 using Autodesk.AutoCAD.GraphicsInterface global using WorldDraw = Autodesk.AutoCAD.GraphicsInterface.WorldDraw; global using Manager = Autodesk.AutoCAD.GraphicsSystem.Manager; diff --git a/src/IFoxCAD.LoadForm/AssemblyDependent.cs b/src/IFoxCAD.LoadForm/AssemblyDependent.cs index 1c512ea..067a2b9 100644 --- a/src/IFoxCAD.LoadForm/AssemblyDependent.cs +++ b/src/IFoxCAD.LoadForm/AssemblyDependent.cs @@ -9,8 +9,8 @@ namespace IFoxCAD.LoadEx; public class AssemblyDependent : IDisposable { #if HarmonyPatch - //这个是不能删除的,否则就不执行了 - //HarmonyPatch hook method 返回 false 表示拦截原函数 + // 这个是不能删除的,否则就不执行了 + // HarmonyPatch hook method 返回 false 表示拦截原函数 public static bool Prefix() { return false; } #endif @@ -36,7 +36,7 @@ public event ResolveEventHandler CurrentDomainAssemblyResolveEvent /// public AssemblyDependent() { - //初始化一次,反复load + // 初始化一次,反复load CurrentDomainAssemblyResolveEvent += AssemblyHelper.DefaultAssemblyResolve; } #endregion @@ -57,12 +57,12 @@ public bool Load(string? dllFullName, List loadStates, bool byteLoad if (dllFullName == null) throw new ArgumentNullException(nameof(dllFullName)); - dllFullName = Path.GetFullPath(dllFullName);//相对路径要先转换 + dllFullName = Path.GetFullPath(dllFullName);// 相对路径要先转换 if (!File.Exists(dllFullName)) throw new ArgumentException("路径不存在"); - //程序集数组要动态获取(每次Load的时候), - //否则会变成一个固定数组,造成加载了之后也不会出现成员 + // 程序集数组要动态获取(每次Load的时候), + // 否则会变成一个固定数组,造成加载了之后也不会出现成员 var cadAssembly = AppDomain.CurrentDomain.GetAssemblies(); var cadAssemblyRef = AppDomain.CurrentDomain.ReflectionOnlyGetAssemblies(); @@ -71,22 +71,22 @@ public bool Load(string? dllFullName, List loadStates, bool byteLoad bool dllFullNameLoadOk = false; - //查询加载链逆向加载,确保前面不丢失 - //这里有问题,从尾巴开始的,就一定是没有任何引用吗? + // 查询加载链逆向加载,确保前面不丢失 + // 这里有问题,从尾巴开始的,就一定是没有任何引用吗? for (int i = allRefs.Count - 1; i >= 0; i--) { var allRef = allRefs[i]; - //路径转程序集名 + // 路径转程序集名 var an = AssemblyName.GetAssemblyName(allRef).FullName; var assembly = cadAssembly.FirstOrDefault(a => a.FullName == an); if (assembly != null) { - loadStates.Add(new LoadState(allRef, false));//版本号没变不加载 + loadStates.Add(new LoadState(allRef, false));// 版本号没变不加载 continue; } - //有一次true,就是true + // 有一次true,就是true if (allRef == dllFullName) dllFullNameLoadOk = true; @@ -126,7 +126,7 @@ public bool Load(string? dllFullName, List loadStates, bool byteLoad Assembly? GetPdbAssembly(string? path) { #if DEBUG - //为了实现Debug时候出现断点,见链接,加依赖 + // 为了实现Debug时候出现断点,见链接,加依赖 // https://www.cnblogs.com/DasonKwok/p/10510218.html // https://www.cnblogs.com/DasonKwok/p/10523279.html @@ -164,11 +164,11 @@ void GetAllRefPaths(Assembly[] cadAssembly, return; var sb = new StringBuilder(); - //dll拖拉加载路径-搜索路径(可以增加到这个dll下面的所有文件夹?) + // dll拖拉加载路径-搜索路径(可以增加到这个dll下面的所有文件夹?) sb.Append(Path.GetDirectoryName(dllFullName)); sb.Append("\\"); - //遍历依赖,如果存在dll拖拉加载目录就加入dlls集合 + // 遍历依赖,如果存在dll拖拉加载目录就加入dlls集合 var asse = assemblyAsRef.GetReferencedAssemblies(); for (int i = 0; i < asse.Length; i++) { @@ -194,20 +194,20 @@ void GetAllRefPaths(Assembly[] cadAssembly, Assembly[] cadAssemblyRef, string? dllFullName) { - //路径转程序集名 + // 路径转程序集名 var assName = AssemblyName.GetAssemblyName(dllFullName).FullName; - //在当前程序域的 assemblyAs内存区 和 assemblyAsRef映射区 找这个程序集名 + // 在当前程序域的 assemblyAs内存区 和 assemblyAsRef映射区 找这个程序集名 var assemblyAs = cadAssembly.FirstOrDefault(ass => ass.FullName == assName); - //内存区有表示加载过 - //映射区有表示查找过,但没有加载(一般来说不存在.只是debug会注释掉 Assembly.Load 的时候用来测试) + // 内存区有表示加载过 + // 映射区有表示查找过,但没有加载(一般来说不存在.只是debug会注释掉 Assembly.Load 的时候用来测试) if (assemblyAs != null) return assemblyAs; - //映射区 + // 映射区 var assemblyAsRef = cadAssemblyRef.FirstOrDefault(ass => ass.FullName == assName); - //内存区和映射区都没有的话就把dll加载到映射区,用来找依赖表 + // 内存区和映射区都没有的话就把dll加载到映射区,用来找依赖表 if (assemblyAsRef != null) return assemblyAsRef; @@ -226,7 +226,7 @@ void GetAllRefPaths(Assembly[] cadAssembly, hm.UnpatchAll(ext); #endif #if HarmonyPatch_2 - //方案二:跟cad耦合了 + // 方案二:跟cad耦合了 const string ext = "Autodesk.AutoCAD.ApplicationServices.ExtensionLoader"; var docAss = typeof(Autodesk.AutoCAD.ApplicationServices.Document).Assembly; var a = docAss.GetType(ext); @@ -345,13 +345,13 @@ public void DebugMoveObjFiles(string? dllFullName, Action action) var filename = Path.GetFileNameWithoutExtension(dllFullName); var path = Path.GetDirectoryName(dllFullName); - //新建文件夹_临时目录 + // 新建文件夹_临时目录 temp_Pdb_dest = path + $"\\{Temp}\\"; - //移动文件进去 + // 移动文件进去 temp_Pdb_source = path + "\\" + filename + ".pdb"; FileEx.MoveFolder(temp_Pdb_source, temp_Pdb_dest); - //检查是否存在obj文件夹,有就递归移动 + // 检查是否存在obj文件夹,有就递归移动 var list = path.Split('\\'); if (list[list.Length - 1] == "Debug" && list[list.Length - 2] == "bin") { @@ -396,7 +396,7 @@ public void Dispose() protected virtual void Dispose(bool disposing) { - //不重复释放,并设置已经释放 + // 不重复释放,并设置已经释放 if (IsDisposed) return; IsDisposed = true; @@ -459,7 +459,7 @@ public static void MoveFolder(string? sourcePathOrFile, string? destPath) if (ContainFileName(destPath)) destPath = Path.GetDirectoryName(destPath); - //目标目录不存在则创建 + // 目标目录不存在则创建 if (!Directory.Exists(destPath)) Directory.CreateDirectory(destPath); @@ -492,28 +492,28 @@ public static void MoveFolder(string? sourcePathOrFile, string? destPath) /// 目标文件夹 static void MoveFolder2(string sourcePath, string destPath) { - //目标目录不存在则创建 + // 目标目录不存在则创建 if (!Directory.Exists(destPath)) Directory.CreateDirectory(destPath); - //获得源文件下所有文件 + // 获得源文件下所有文件 var files = new List(Directory.GetFiles(sourcePath)); files.ForEach(c => { string destFile = Path.Combine(destPath, Path.GetFileName(c)); - //覆盖模式 + // 覆盖模式 if (File.Exists(destFile)) File.Delete(destFile); File.Move(c, destFile); }); - //获得源文件下所有目录文件 + // 获得源文件下所有目录文件 List folders = new(Directory.GetDirectories(sourcePath)); folders.ForEach(c => { string destDir = Path.Combine(destPath, Path.GetFileName(c)); - //Directory.Move必须要在同一个根目录下移动才有效,不能在不同卷中移动。 - //Directory.Move(c, destDir); + // Directory.Move必须要在同一个根目录下移动才有效,不能在不同卷中移动。 + // Directory.Move(c, destDir); - //采用递归的方法实现 + // 采用递归的方法实现 MoveFolder2(c, destDir); }); } @@ -527,15 +527,15 @@ public static void DeleteFolder(string? dir) { if (dir is null) throw new ArgumentException(nameof(dir)); - if (!Directory.Exists(dir)) //如果存在这个文件夹删除之 + if (!Directory.Exists(dir)) // 如果存在这个文件夹删除之 return; foreach (string d in Directory.GetFileSystemEntries(dir)) { if (File.Exists(d)) - File.Delete(d); //直接删除其中的文件 + File.Delete(d); // 直接删除其中的文件 else - DeleteFolder(d); //递归删除子文件夹 + DeleteFolder(d); // 递归删除子文件夹 } - Directory.Delete(dir, true); //删除已空文件夹 + Directory.Delete(dir, true); // 删除已空文件夹 } } \ No newline at end of file diff --git a/src/IFoxCAD.LoadForm/DefaultAssemblyResolve.cs b/src/IFoxCAD.LoadForm/DefaultAssemblyResolve.cs index 6630579..8aabc1d 100644 --- a/src/IFoxCAD.LoadForm/DefaultAssemblyResolve.cs +++ b/src/IFoxCAD.LoadForm/DefaultAssemblyResolve.cs @@ -21,18 +21,18 @@ public class AssemblyHelper Assembly? result = null; var cadAss = AppDomain.CurrentDomain.GetAssemblies(); - //名称和版本号都一致的,调用它 + // 名称和版本号都一致的,调用它 result = cadAss.FirstOrDefault(ass => ass.GetName().FullName == args.Name); if (result != null) return result; - //获取名称一致,但是版本号不同的,调用最后的可用版本 + // 获取名称一致,但是版本号不同的,调用最后的可用版本 var ag = GetAssemblyName(args.Name); - //获取最后一个符合条件的, - //否则a.dll引用b.dll函数的时候,b.dll修改重生成之后, - //加载进去会调用第一个版本的b.dll, - //vs会迭代程序版本号的*,所以最后的可用就是循环到最后的. + // 获取最后一个符合条件的, + // 否则a.dll引用b.dll函数的时候,b.dll修改重生成之后, + // 加载进去会调用第一个版本的b.dll, + // vs会迭代程序版本号的*,所以最后的可用就是循环到最后的. for (int i = 0; i < cadAss.Length; i++) { if (GetAssemblyName(cadAss[i].GetName().FullName) != ag) diff --git a/src/IFoxCAD.LoadForm/GlobalUsings.cs b/src/IFoxCAD.LoadForm/GlobalUsings.cs index b2d69fe..5f10b33 100644 --- a/src/IFoxCAD.LoadForm/GlobalUsings.cs +++ b/src/IFoxCAD.LoadForm/GlobalUsings.cs @@ -8,9 +8,9 @@ global using System.Runtime.CompilerServices; /// autocad 引用 -//global using Autodesk.AutoCAD.ApplicationServices; -//global using Autodesk.AutoCAD.Runtime; -//global using Acap = Autodesk.AutoCAD.ApplicationServices.Application; -//global using Autodesk.AutoCAD.EditorInput; +// global using Autodesk.AutoCAD.ApplicationServices; +// global using Autodesk.AutoCAD.Runtime; +// global using Acap = Autodesk.AutoCAD.ApplicationServices.Application; +// global using Autodesk.AutoCAD.EditorInput; global using HarmonyLib; diff --git a/src/IFoxCAD.LoadForm/LoaderForm.cs b/src/IFoxCAD.LoadForm/LoaderForm.cs index 151af1c..57eca28 100644 --- a/src/IFoxCAD.LoadForm/LoaderForm.cs +++ b/src/IFoxCAD.LoadForm/LoaderForm.cs @@ -9,15 +9,15 @@ public partial class LoaderForm : Form public LoaderForm() { - //Owner = form; - //MdiParent = form; + // Owner = form; + // MdiParent = form; InitializeComponent(); _ad = new(); } void LoaderForm_Load(object sender, EventArgs e) { - StartPosition = FormStartPosition.CenterScreen;//在当前屏幕中央 + StartPosition = FormStartPosition.CenterScreen;// 在当前屏幕中央 if (DllPath != null) textBox1.Text = DllPath; } @@ -50,7 +50,7 @@ void Button2_Click(object sender, EventArgs e) LoadDlls(new() { textBox1.Text }); } - //鼠标拖拽文件到窗口 + // 鼠标拖拽文件到窗口 void LoaderForm_DragDrop(object sender, DragEventArgs e) { var paths = (string[])e.Data.GetData(DataFormats.FileDrop); @@ -89,7 +89,7 @@ void LoadDlls(HashSet paths) MessageBox.Show("无任何信息", "加载出现问题!"); } - //鼠标样式修改 + // 鼠标样式修改 void LoaderForm_DragEnter(object sender, DragEventArgs e) { if (e.Data.GetDataPresent(DataFormats.FileDrop)) diff --git a/src/IFoxCAD.WPF/EventBindingExtension.cs b/src/IFoxCAD.WPF/EventBindingExtension.cs index eb58c75..7352143 100644 --- a/src/IFoxCAD.WPF/EventBindingExtension.cs +++ b/src/IFoxCAD.WPF/EventBindingExtension.cs @@ -65,14 +65,14 @@ public class EventBindingExtension : MarkupExtension Type? eventHandlerType = null; if (memberInfo is EventInfo eventInfo) { - //var info = memberInfo as EventInfo; - //var eventInfo = info; + // var info = memberInfo as EventInfo; + // var eventInfo = info; eventHandlerType = eventInfo.EventHandlerType; } else if (memberInfo is MethodInfo methodInfo) { - //var info = memberInfo as MethodInfo; - //var methodInfo = info; + // var info = memberInfo as MethodInfo; + // var methodInfo = info; ParameterInfo[] pars = methodInfo.GetParameters(); eventHandlerType = pars[1].ParameterType; } diff --git a/tests/TestAcad08/GlobalUsings.cs b/tests/TestAcad08/GlobalUsings.cs index 917387b..1431096 100644 --- a/tests/TestAcad08/GlobalUsings.cs +++ b/tests/TestAcad08/GlobalUsings.cs @@ -12,13 +12,13 @@ global using System.Runtime.CompilerServices; -// -// -//#if !NET472 -//global using System.Web.Script.Serialization;/*序列化的类 程序集:System.Web.Extensions.dll*/ -//#else -//global using System.Text.Json; -//#endif +// +// +// #if !NET472 +// global using System.Web.Script.Serialization;/*序列化的类 程序集:System.Web.Extensions.dll*/ +// #else +// global using System.Text.Json; +// #endif /// autocad 引用 global using Autodesk.AutoCAD.ApplicationServices; diff --git a/tests/TestConsole/Info.cs b/tests/TestConsole/Info.cs index a33fddc..b6ad87f 100644 --- a/tests/TestConsole/Info.cs +++ b/tests/TestConsole/Info.cs @@ -35,7 +35,7 @@ public enum PlugIn2 DOCBAR = 8, DUOTAB = 16, - //all == *Lisp*|DOCBAR|DUOTAB - //采取的行为是:注释的行为是特殊的,就按照注释的,否则,遍历子元素提取注释 + // all == *Lisp*|DOCBAR|DUOTAB + // 采取的行为是:注释的行为是特殊的,就按照注释的,否则,遍历子元素提取注释 All = Lisp | DOCBAR | DUOTAB } \ No newline at end of file diff --git a/tests/TestConsole/Program.cs b/tests/TestConsole/Program.cs index 60325f7..7d37d3b 100644 --- a/tests/TestConsole/Program.cs +++ b/tests/TestConsole/Program.cs @@ -3,18 +3,18 @@ using System.Text; using TestConsole; -//乱序 +// 乱序 Console.WriteLine(PlugIn.JoinBox.PrintNote()); -Console.WriteLine(PlugIn.Lisp.PrintNote());//这里先交换顺序来试试能不能成功 +Console.WriteLine(PlugIn.Lisp.PrintNote());// 这里先交换顺序来试试能不能成功 Console.WriteLine(PlugIn.IMinCad.PrintNote()); Console.WriteLine(PlugIn.YuanQuan.PrintNote()); Console.WriteLine(PlugIn.All.PrintNote()); Console.WriteLine(PlugIn.DOCBAR.PrintNote()); Console.WriteLine(PlugIn.DUOTAB.PrintNote()); Console.WriteLine("***************************************************"); -//乱序2 +// 乱序2 Console.WriteLine(PlugIn2.JoinBox.PrintNote()); -Console.WriteLine(PlugIn2.Lisp.PrintNote());//这里先交换顺序来试试能不能成功 +Console.WriteLine(PlugIn2.Lisp.PrintNote());// 这里先交换顺序来试试能不能成功 Console.WriteLine(PlugIn2.IMinCad.PrintNote()); Console.WriteLine(PlugIn2.YuanQuan.PrintNote()); Console.WriteLine(PlugIn2.All.PrintNote()); @@ -23,9 +23,9 @@ EnumEx.CleanCache(); -//表达式树例子 +// 表达式树例子 TestConsole.Test_Expression.Demo3(); -//TestConsole.Test_Expression.Demo1(); +// TestConsole.Test_Expression.Demo1(); #region 元组测试 var valuetuple = (1, 2); @@ -46,9 +46,9 @@ #region 测试遍历枚举 -//Season a = Season.Autumn; -//Console.WriteLine($"Integral value of {a} is {(int)a}"); // output: Integral value of Autumn is 2 -//foreach (var enumItem in Enum.GetValues(typeof(Season))) +// Season a = Season.Autumn; +// Console.WriteLine($"Integral value of {a} is {(int)a}"); // output: Integral value of Autumn is 2 +// foreach (var enumItem in Enum.GetValues(typeof(Season))) // Console.WriteLine((byte)enumItem); var sb = new StringBuilder(); @@ -61,8 +61,8 @@ } Console.WriteLine(sb); -sb.Remove(sb.Length - 1, 1);//剔除末尾, -//因为有返回值所以容易理解成 sb = sb.Remove(sb.Length - 1, 1); +sb.Remove(sb.Length - 1, 1);// 剔除末尾, +// 因为有返回值所以容易理解成 sb = sb.Remove(sb.Length - 1, 1); Console.WriteLine(sb); public enum Season : byte diff --git a/tests/TestConsole/RuntimeHelpers.cs b/tests/TestConsole/RuntimeHelpers.cs index 9e09b29..310242d 100644 --- a/tests/TestConsole/RuntimeHelpers.cs +++ b/tests/TestConsole/RuntimeHelpers.cs @@ -26,7 +26,7 @@ public static T[] GetSubArray(T[] array, Range range) if (length == 0) { - //return Array.Empty(); + // return Array.Empty(); return new T[0]; diff --git "a/tests/TestConsole/\350\241\250\350\276\276\345\274\217\346\240\221.cs" "b/tests/TestConsole/\350\241\250\350\276\276\345\274\217\346\240\221.cs" index b53fc48..4c706dd 100644 --- "a/tests/TestConsole/\350\241\250\350\276\276\345\274\217\346\240\221.cs" +++ "b/tests/TestConsole/\350\241\250\350\276\276\345\274\217\346\240\221.cs" @@ -19,10 +19,10 @@ public static void Demo1() Expression> exprTree = num => num < 5; // 分解表达式树 - ParameterExpression param = exprTree.Parameters[0];//num - BinaryExpression operation = (BinaryExpression)exprTree.Body;//函数体 {(num < 5)} - ParameterExpression left = (ParameterExpression)operation.Left;//左节点 num - ConstantExpression right = (ConstantExpression)operation.Right;//右表达式 5 + ParameterExpression param = exprTree.Parameters[0];// num + BinaryExpression operation = (BinaryExpression)exprTree.Body;// 函数体 {(num < 5)} + ParameterExpression left = (ParameterExpression)operation.Left;// 左节点 num + ConstantExpression right = (ConstantExpression)operation.Right;// 右表达式 5 Console.WriteLine("表达式树例子: {0} => {1} {2} {3}", param.Name, left.Name, operation.NodeType, right.Value); @@ -37,17 +37,17 @@ public static void Demo2() // // // 分解表达式树 // ParameterExpression param = exprTree.Parameters[0];// x - // BinaryExpression operation = (BinaryExpression)exprTree.Body;//函数体 {((x > 5) AndAlso (x < 50))} + // BinaryExpression operation = (BinaryExpression)exprTree.Body;// 函数体 {((x > 5) AndAlso (x < 50))} // - // ParameterExpression left = (ParameterExpression)operation.Left;//左节点.......这里报错 - // ConstantExpression right = (ConstantExpression)operation.Right;//右表达式.....这里报错 + // ParameterExpression left = (ParameterExpression)operation.Left;// 左节点.......这里报错 + // ConstantExpression right = (ConstantExpression)operation.Right;// 右表达式.....这里报错 // // Console.WriteLine("表达式树例子: {0} => {1} {2} {3}", // param.Name, left.Name, operation.NodeType, right.Value); } - //博客园例子,表达式体内有多个式子 + // 博客园例子,表达式体内有多个式子 public static void Demo3() { List names = new() { "Cai", "Edward", "Beauty" }; @@ -79,7 +79,7 @@ static Func ReBuildExpression(Expression> lambd }; Expression left = my.Modify(lambda.Body); - //构造一个新的表达式 + // 构造一个新的表达式 var newLambda = Expression.Lambda>(left, my.Parameter); return newLambda.Compile(); } @@ -102,9 +102,9 @@ static Func ReBuildExpression(Expression> lambd Expression left = my.Modify(lambda0.Body); Expression right = my.Modify(lambda1.Body); - var expression = Expression.AndAlso(left, right);//就是 && 合并两个匿名函数 + var expression = Expression.AndAlso(left, right);// 就是 && 合并两个匿名函数 - //构造一个新的表达式 + // 构造一个新的表达式 var newLambda = Expression.Lambda>(expression, my.Parameter); return newLambda.Compile(); } diff --git a/tests/TestShared/CmdINI.cs b/tests/TestShared/CmdINI.cs index d9479ce..361cb65 100644 --- a/tests/TestShared/CmdINI.cs +++ b/tests/TestShared/CmdINI.cs @@ -37,9 +37,9 @@ public class CmdInit { public static AutoRegAssemEx? AutoRegAssemEx; - ///如果netload之后用 删除注册表, - ///由于不是也不能卸载dll,再netload是无法执行自动接口的, - ///所以此时会产生无法再注册的问题...因此需要暴露此注册函数(硬来) + /// 如果netload之后用 删除注册表, + /// 由于不是也不能卸载dll,再netload是无法执行自动接口的, + /// 所以此时会产生无法再注册的问题...因此需要暴露此注册函数(硬来) [CommandMethod("IFoxAddReg")] public void IFoxAddReg() { @@ -64,7 +64,7 @@ public void IFoxRemoveReg() var ed = doc.Editor; ed.WriteMessage($"\n 卸载注册表"); - //防止卸载两次,不然会报错的 + // 防止卸载两次,不然会报错的 AutoRegAssemEx?.UnRegApp(); AutoRegAssemEx = null; } @@ -106,28 +106,28 @@ public void Initialize() [IFoxInitialize(isInitialize: false)] public void Terminate() { - //try - //{ + // try + // { // var dm = Acap.DocumentManager; // var doc = dm.MdiActiveDocument; - // var ed = doc.Editor; //注意此时编辑器已经回收,所以此句没用,并引发错误 + // var ed = doc.Editor; // 注意此时编辑器已经回收,所以此句没用,并引发错误 // ed.WriteMessage("\n 结束自动执行 Terminate \r\n"); - //} - //catch (System.Exception) - //{ - //} + // } + // catch (System.Exception) + // { + // } } - //[IFoxInitialize] - //public void Initialize() - //{ - // //文档管理器将比此接口前创建,因此此句会执行 + // [IFoxInitialize] + // public void Initialize() + // { + // // 文档管理器将比此接口前创建,因此此句会执行 // Acap.DocumentManager.MdiActiveDocument.Editor.WriteMessage("\nload...."); - //} - //[IFoxInitialize(Sequence.First, false)] - //public void Terminate() - //{ - // //文档管理器将比此接口前死亡,因此此句不会执行 + // } + // [IFoxInitialize(Sequence.First, false)] + // public void Terminate() + // { + // // 文档管理器将比此接口前死亡,因此此句不会执行 // Acap.DocumentManager.MdiActiveDocument?.Editor.WriteMessage("\nunload...."); - //} + // } } \ No newline at end of file diff --git a/tests/TestShared/Test.cs b/tests/TestShared/Test.cs index 5ead798..309b773 100644 --- a/tests/TestShared/Test.cs +++ b/tests/TestShared/Test.cs @@ -24,17 +24,17 @@ public void Dbtest() Line line = new(new Point3d(0, 0, 0), new Point3d(1, 1, 0)); Circle circle = new(new Point3d(0, 0, 0), Vector3d.ZAxis, 2); - //var lienid = tr.AddEntity(line); - //var cirid = tr.AddEntity(circle); - //var linent = tr.GetObject(lienid); - //var lineent = tr.GetObject(cirid); - //var linee = tr.GetObject(cirid); //经测试,类型不匹配,返回null - //var dd = tr.GetObject(lienid); - //List ds = new() { linee, dd }; - //tr.CurrentSpace.AddEntity(line,tr); + // var lienid = tr.AddEntity(line); + // var cirid = tr.AddEntity(circle); + // var linent = tr.GetObject(lienid); + // var lineent = tr.GetObject(cirid); + // var linee = tr.GetObject(cirid); // 经测试,类型不匹配,返回null + // var dd = tr.GetObject(lienid); + // List ds = new() { linee, dd }; + // tr.CurrentSpace.AddEntity(line,tr); } - //add entity test + // add entity test [CommandMethod("addent")] public void Addent() { @@ -51,20 +51,20 @@ public void Addent() public void Drawarc() { using var tr = new DBTrans(); - Arc arc1 = EntityEx.CreateArcSCE(new Point3d(2, 0, 0), new Point3d(0, 0, 0), new Point3d(0, 2, 0));//起点,圆心,终点 - Arc arc2 = EntityEx.CreateArc(new Point3d(4, 0, 0), new Point3d(0, 0, 0), Math.PI / 2); //起点,圆心,弧度 - Arc arc3 = EntityEx.CreateArc(new Point3d(1, 0, 0), new Point3d(0, 0, 0), new Point3d(0, 1, 0)); //起点,圆上一点,终点 + Arc arc1 = EntityEx.CreateArcSCE(new Point3d(2, 0, 0), new Point3d(0, 0, 0), new Point3d(0, 2, 0));// 起点,圆心,终点 + Arc arc2 = EntityEx.CreateArc(new Point3d(4, 0, 0), new Point3d(0, 0, 0), Math.PI / 2); // 起点,圆心,弧度 + Arc arc3 = EntityEx.CreateArc(new Point3d(1, 0, 0), new Point3d(0, 0, 0), new Point3d(0, 1, 0)); // 起点,圆上一点,终点 tr.CurrentSpace.AddEntity(arc1, arc2, arc3); - tr.CurrentSpace.AddArc(new Point3d(0, 0, 0), new Point3d(1, 1, 0), new Point3d(2, 0, 0));//起点,圆上一点,终点 + tr.CurrentSpace.AddArc(new Point3d(0, 0, 0), new Point3d(1, 1, 0), new Point3d(2, 0, 0));// 起点,圆上一点,终点 } [CommandMethod("drawcircle")] public void DraCircle() { using var tr = new DBTrans(); - var circle1 = EntityEx.CreateCircle(new Point3d(0, 0, 0), new Point3d(1, 0, 0)); //起点,终点 - var circle2 = EntityEx.CreateCircle(new Point3d(-2, 0, 0), new Point3d(2, 0, 0), new Point3d(0, 2, 0));//三点画圆,成功 - var circle3 = EntityEx.CreateCircle(new Point3d(-2, 0, 0), new Point3d(0, 0, 0), new Point3d(2, 0, 0));//起点,圆心,终点,失败 + var circle1 = EntityEx.CreateCircle(new Point3d(0, 0, 0), new Point3d(1, 0, 0)); // 起点,终点 + var circle2 = EntityEx.CreateCircle(new Point3d(-2, 0, 0), new Point3d(2, 0, 0), new Point3d(0, 2, 0));// 三点画圆,成功 + var circle3 = EntityEx.CreateCircle(new Point3d(-2, 0, 0), new Point3d(0, 0, 0), new Point3d(2, 0, 0));// 起点,圆心,终点,失败 tr.CurrentSpace.AddEntity(circle1, circle2!); if (circle3 is not null) { @@ -75,8 +75,8 @@ public void DraCircle() tr.Editor?.WriteMessage("三点画圆失败"); } tr.CurrentSpace.AddEntity(circle3!); - tr.CurrentSpace.AddCircle(new Point3d(0, 0, 0), new Point3d(1, 1, 0), new Point3d(2, 0, 0));//三点画圆,成功 - tr.CurrentSpace.AddCircle(new Point3d(0, 0, 0), new Point3d(1, 1, 0), new Point3d(2, 2, 0));//起点,圆上一点,终点(共线) + tr.CurrentSpace.AddCircle(new Point3d(0, 0, 0), new Point3d(1, 1, 0), new Point3d(2, 0, 0));// 三点画圆,成功 + tr.CurrentSpace.AddCircle(new Point3d(0, 0, 0), new Point3d(1, 1, 0), new Point3d(2, 2, 0));// 起点,圆上一点,终点(共线) } [CommandMethod("layertest")] @@ -97,7 +97,7 @@ public void Layertest() } - //添加图层 + // 添加图层 [CommandMethod("layerAdd1")] public void Layertest1() { @@ -105,29 +105,29 @@ public void Layertest1() tr.LayerTable.Add("test1", Color.FromColorIndex(ColorMethod.ByColor, 1)); } - //添加图层 + // 添加图层 [CommandMethod("layerAdd2")] public void Layertest2() { using var tr = new DBTrans(); tr.LayerTable.Add("test2", 2); - //tr.LayerTable["3"] = new LayerTableRecord(); + // tr.LayerTable["3"] = new LayerTableRecord(); } - //删除图层 + // 删除图层 [CommandMethod("layerdel")] public void LayerDel() { using var tr = new DBTrans(); - Env.Editor.WriteMessage(tr.LayerTable.Delete("0").ToString()); //删除图层 0 - Env.Editor.WriteMessage(tr.LayerTable.Delete("Defpoints").ToString());//删除图层 Defpoints - Env.Editor.WriteMessage(tr.LayerTable.Delete("1").ToString()); //删除不存在的图层 1 - Env.Editor.WriteMessage(tr.LayerTable.Delete("2").ToString()); //删除有图元的图层 2 - Env.Editor.WriteMessage(tr.LayerTable.Delete("3").ToString()); //删除图层 3 + Env.Editor.WriteMessage(tr.LayerTable.Delete("0").ToString()); // 删除图层 0 + Env.Editor.WriteMessage(tr.LayerTable.Delete("Defpoints").ToString());// 删除图层 Defpoints + Env.Editor.WriteMessage(tr.LayerTable.Delete("1").ToString()); // 删除不存在的图层 1 + Env.Editor.WriteMessage(tr.LayerTable.Delete("2").ToString()); // 删除有图元的图层 2 + Env.Editor.WriteMessage(tr.LayerTable.Delete("3").ToString()); // 删除图层 3 - tr.LayerTable.Remove("2"); //测试是否能强制删除 + tr.LayerTable.Remove("2"); // 测试是否能强制删除 } - //添加直线 + // 添加直线 [CommandMethod("linedemo1")] public void AddLine1() { @@ -154,7 +154,7 @@ public void AddLine1() tr.CurrentSpace.AddEntity(line2, line3, circle); } - //增加多段线1 + // 增加多段线1 [CommandMethod("Pldemo1")] public void AddPolyline1() { @@ -171,7 +171,7 @@ public void AddPolyline1() tr.CurrentSpace.AddEntity(pl); } - //增加多段线2 + // 增加多段线2 [CommandMethod("pldemo2")] public void Addpl2() { @@ -208,16 +208,16 @@ public void AddXdata() { XData = new XDataList() { - { DxfCode.ExtendedDataRegAppName, "myapp1" }, //可以用dxfcode和int表示组码 + { DxfCode.ExtendedDataRegAppName, "myapp1" }, // 可以用dxfcode和int表示组码 { DxfCode.ExtendedDataAsciiString, "xxxxxxx" }, {1070, 12 }, - { DxfCode.ExtendedDataRegAppName, appname }, //可以用dxfcode和int表示组码,移除中间的测试 + { DxfCode.ExtendedDataRegAppName, appname }, // 可以用dxfcode和int表示组码,移除中间的测试 { DxfCode.ExtendedDataAsciiString, "要移除的我" }, {1070, 12 }, - { DxfCode.ExtendedDataRegAppName, "myapp3" }, //可以用dxfcode和int表示组码 + { DxfCode.ExtendedDataRegAppName, "myapp3" }, // 可以用dxfcode和int表示组码 { DxfCode.ExtendedDataAsciiString, "aaaaaaaaa" }, {1070, 12 }, - { DxfCode.ExtendedDataRegAppName, "myapp4" }, //可以用dxfcode和int表示组码 + { DxfCode.ExtendedDataRegAppName, "myapp4" }, // 可以用dxfcode和int表示组码 { DxfCode.ExtendedDataAsciiString, "bbbbbbbbb" }, {1070, 12 } } @@ -238,14 +238,14 @@ public void GetXdata() tr.RegAppTable.GetRecordNames().ForEach(name => name.Print()); tr.RegAppTable.ForEach(re => re.Name.Print()); - //var res = ed.GetEntity("\n select the entity:"); - //if (res.Status == PromptStatus.OK) - //{ + // var res = ed.GetEntity("\n select the entity:"); + // if (res.Status == PromptStatus.OK) + // { // using var tr = new DBTrans(); // tr.RegAppTable.ForEach(id => id.GetObject().Print()); // var data = tr.GetObject(res.ObjectId).XData; // ed.WriteMessage(data.ToString()); - //} + // } } [CommandMethod("changexdata")] diff --git a/tests/TestShared/TestAOP.cs b/tests/TestShared/TestAOP.cs index b1b807a..2b9169f 100644 --- a/tests/TestShared/TestAOP.cs +++ b/tests/TestShared/TestAOP.cs @@ -1,7 +1,7 @@ namespace Test; -//被注入的函数将不能使用断点, -//因此用户要充分了解才能使用 +// 被注入的函数将不能使用断点, +// 因此用户要充分了解才能使用 #if false /* * 类库用户想侵入的命名空间是用户的, @@ -27,35 +27,35 @@ namespace Test */ public class AopTestClass { - //类不拒绝,这里拒绝 + // 类不拒绝,这里拒绝 [IFoxRefuseInjectionTransaction] [CommandMethod("IFoxRefuseInjectionTransaction")] public void IFoxRefuseInjectionTransaction() { } - //不拒绝 + // 不拒绝 [CommandMethod("InjectionTransaction")] public void InjectionTransaction() { - //怎么用事务呢? - //直接用 DBTrans.Top + // 怎么用事务呢? + // 直接用 DBTrans.Top var dBTrans = new DBTrans(); dBTrans.Commit(); } } - //拒绝注入事务,写类上,则方法全都拒绝 + // 拒绝注入事务,写类上,则方法全都拒绝 [IFoxRefuseInjectionTransaction] public class AopTestClassRefuseInjection { - //此时这个也是拒绝的..这里加特性只是无所谓 + // 此时这个也是拒绝的..这里加特性只是无所谓 [IFoxRefuseInjectionTransaction] [CommandMethod("IFoxRefuseInjectionTransaction2")] public void IFoxRefuseInjectionTransaction2() { - //拒绝注入就要自己开事务,通常用在循环提交事务上面. - //另见 报错0x02 https://www.cnblogs.com/JJBox/p/10798940.html + // 拒绝注入就要自己开事务,通常用在循环提交事务上面. + // 另见 报错0x02 https://www.cnblogs.com/JJBox/p/10798940.html using var tr = new DBTrans(); } diff --git a/tests/TestShared/TestCurve.cs b/tests/TestShared/TestCurve.cs index 61080b6..b01280b 100644 --- a/tests/TestShared/TestCurve.cs +++ b/tests/TestShared/TestCurve.cs @@ -33,7 +33,7 @@ public void TestGraph1() Tools.TestTimes2(1, "new", () => { var res = ents!.GetAllCycle(); - //res.ForEach((i, t) => t.ForWrite(e => e.ColorIndex = i + 1)); + // res.ForEach((i, t) => t.ForWrite(e => e.ColorIndex = i + 1)); Env.Print(res.Count()); tr.CurrentSpace.AddEntity(res); }); @@ -58,7 +58,7 @@ public void TestGraphspeed() #endif } - //新建 dfs + // 新建 dfs var dfs = new DepthFirst(); #if true Tools.TestTimes2(100, "new", () => { @@ -79,9 +79,9 @@ public void TestGraphspeed() dfs.FindAll(graph); }); #endif - //res.ForEach((i, t) => t.ForWrite(e => e.ColorIndex = i + 1)); + // res.ForEach((i, t) => t.ForWrite(e => e.ColorIndex = i + 1)); - //tr.CurrentSpace.AddEntity(res); + // tr.CurrentSpace.AddEntity(res); } } @@ -118,11 +118,11 @@ public void TestCurveCurveIntersector3d() { var gc1 = ents[i]; var int1 = gc1?.GetInterval(); - //var pars1 = paramss[i]; + // var pars1 = paramss[i]; for (int j = i; j < ents.Count; j++) { var gc2 = ents[j]; - //var pars2 = paramss[j]; + // var pars2 = paramss[j]; var int2 = gc2?.GetInterval(); cci3d.Set(gc1, gc2, int1, int2, Vector3d.ZAxis); var d = cci3d.OverlapCount(); @@ -136,11 +136,11 @@ public void TestCurveCurveIntersector3d() for (int k = 0; k < cci3d.NumberOfIntersectionPoints; k++) { - //var a = cci3d.GetOverlapRanges(k); - //var b = cci3d.IsTangential(k); - //var c = cci3d.IsTransversal(k); - //var d = cci3d.OverlapCount(); - //var e = cci3d.OverlapDirection(); + // var a = cci3d.GetOverlapRanges(k); + // var b = cci3d.IsTangential(k); + // var c = cci3d.IsTransversal(k); + // var d = cci3d.OverlapCount(); + // var e = cci3d.OverlapDirection(); var pt = cci3d.GetIntersectionParameters(k); var pts = cci3d.GetIntersectionPoint(k); Env.Print(pts); @@ -148,7 +148,7 @@ public void TestCurveCurveIntersector3d() } } // var tt = CurveEx.Topo(ents.ToList()); - //tt.ForEach(t => t.ForWrite(e => e.ColorIndex = 1)); - //tr.CurrentSpace.AddEntity(tt); + // tt.ForEach(t => t.ForWrite(e => e.ColorIndex = 1)); + // tr.CurrentSpace.AddEntity(tt); } } \ No newline at end of file diff --git a/tests/TestShared/TestDBTrans.cs b/tests/TestShared/TestDBTrans.cs index f989fd5..cd9e670 100644 --- a/tests/TestShared/TestDBTrans.cs +++ b/tests/TestShared/TestDBTrans.cs @@ -8,7 +8,7 @@ public void Testtr() string filename = @"C:\Users\vic\Desktop\test.dwg"; using var tr = new DBTrans(filename); tr.ModelSpace.AddCircle(new Point3d(10, 10, 0), 20); - //tr.Database.SaveAs(filename,DwgVersion.Current); + // tr.Database.SaveAs(filename,DwgVersion.Current); tr.SaveDwgFile(); } [CommandMethod("testifoxcommit")] @@ -18,36 +18,36 @@ public void Testifoxcommit() using var tr = new DBTrans(); tr.ModelSpace.AddCircle(new Point3d(0, 0, 0), 20); tr.Abort(); - //tr.Commit(); + // tr.Commit(); } // AOP 应用 预计示例: // 1. 无参数 - //[AOP] - //[CommandMethod("TESTAOP")] - //public void testaop() - //{ + // [AOP] + // [CommandMethod("TESTAOP")] + // public void testaop() + // { // // 不用 using var tr = new DBTrans(); // var tr = DBTrans.Top; // tr.ModelSpace.AddCircle(new Point3d(0, 0, 0), 20); - //} + // } // 2. 有参数 - //[AOP("file")] - //[CommandMethod("TESTAOP")] - //public void testaop() - //{ + // [AOP("file")] + // [CommandMethod("TESTAOP")] + // public void testaop() + // { // // 不用 using var tr = new DBTrans(file); // var tr = DBTrans.Top; // tr.ModelSpace.AddCircle(new Point3d(0, 0, 0), 20); - //} + // } [CommandMethod("testpt")] public void TestPt() { - //var pt = Env.Editor.GetPoint("pick pt:").Value; - //var pl = Env.Editor.GetEntity("pick pl").ObjectId; + // var pt = Env.Editor.GetPoint("pick pt:").Value; + // var pl = Env.Editor.GetEntity("pick pl").ObjectId; var tr1 = HostApplicationServices.WorkingDatabase.TransactionManager.TopTransaction; using var tr2 = new DBTrans(); @@ -62,14 +62,14 @@ public void TestPt() Env.Print(tr5 == tr7); var trm = HostApplicationServices.WorkingDatabase.TransactionManager; - //var ptt = tr.GetObject(pl).GetClosestPointTo(pt,false); - //var pt1 = new Point3d(0, 0.00000000000001, 0); - //var pt2 = new Point3d(0, 0.00001, 0); - //Env.Print(Tolerance.Global.EqualPoint); - //Env.Print(pt1.IsEqualTo(pt2).ToString()); - //Env.Print(pt1.IsEqualTo(pt2,new Tolerance(0.0,1e-6)).ToString()); - //Env.Print((pt1 == pt2).ToString()); - //Env.Print((pt1 != pt2).ToString()); + // var ptt = tr.GetObject(pl).GetClosestPointTo(pt,false); + // var pt1 = new Point3d(0, 0.00000000000001, 0); + // var pt2 = new Point3d(0, 0.00001, 0); + // Env.Print(Tolerance.Global.EqualPoint); + // Env.Print(pt1.IsEqualTo(pt2).ToString()); + // Env.Print(pt1.IsEqualTo(pt2,new Tolerance(0.0,1e-6)).ToString()); + // Env.Print((pt1 == pt2).ToString()); + // Env.Print((pt1 != pt2).ToString()); diff --git a/tests/TestShared/TestEnt.cs b/tests/TestShared/TestEnt.cs index 1824a0f..010303a 100644 --- a/tests/TestShared/TestEnt.cs +++ b/tests/TestShared/TestEnt.cs @@ -26,12 +26,12 @@ public void TestTypeSpeed() }); Tools.TestTimes(100000, "name 匹配:", () => { - //var t = line.GetType().Name; + // var t = line.GetType().Name; var tt = line1.GetType().Name == nameof(Line); }); Tools.TestTimes(100000, "dxfname 匹配:", () => { - //var t = line.GetType().Name; + // var t = line.GetType().Name; var tt = line1.GetRXClass().DxfName == nameof(Line); }); } diff --git a/tests/TestShared/TestJig.cs b/tests/TestShared/TestJig.cs index 98e3c2f..f8a9c5a 100644 --- a/tests/TestShared/TestJig.cs +++ b/tests/TestShared/TestJig.cs @@ -4,7 +4,7 @@ public class Commands_Jig { - //已在数据库的图元如何进入jig + // 已在数据库的图元如何进入jig [CommandMethod("TestCmd_jig33")] public static void TestCmd_jig33() { @@ -18,18 +18,18 @@ public static void TestCmd_jig33() var oldSp = cir.StartPoint; JigEx? moveJig = null; moveJig = new JigEx((mousePoint, drawEntitys) => { - moveJig!.SetOptions(oldSp);//回调过程中也可以修改基点 - //cir.UpgradeOpen();//已经提权了,所以这里不需要提权 + moveJig!.SetOptions(oldSp);// 回调过程中也可以修改基点 + // cir.UpgradeOpen();// 已经提权了,所以这里不需要提权 cir.Move(cir.StartPoint, mousePoint); - //cir.DowngradeOpen(); + // cir.DowngradeOpen(); - //此处会Dispose图元, - //所以此处不加入已经在数据库的图元,而是加入new Entity的. - //drawEntitys.Enqueue(cir); + // 此处会Dispose图元, + // 所以此处不加入已经在数据库的图元,而是加入new Entity的. + // drawEntitys.Enqueue(cir); }); moveJig.SetOptions(cir.GeometricExtents.MinPoint, orthomode: true); - //此处详见方法注释 + // 此处详见方法注释 moveJig.DatabaseEntityDraw(draw => { draw.RawGeometry.Draw(cir); }); @@ -43,7 +43,7 @@ public static void TestCmd_jig33() } - //不在数据库的图元如何进入jig + // 不在数据库的图元如何进入jig [CommandMethod("TestCmd_Jig44")] public void TestCmd_Jig44() { @@ -65,20 +65,20 @@ public void TestCmd_Jig44() jig = new JigEx((mousePoint, drawEntitys) => { var closestPt = pl.GetClosestPointTo(mousePoint, false); - //回调过程中SetOptions会覆盖配置,所以如果想增加关键字或者修改基点, - //不要这样做: jig.SetOptions(closestPt) 而是使用底层暴露 + // 回调过程中SetOptions会覆盖配置,所以如果想增加关键字或者修改基点, + // 不要这样做: jig.SetOptions(closestPt) 而是使用底层暴露 options!.BasePoint = closestPt; - //需要避免重复加入同一个关键字 + // 需要避免重复加入同一个关键字 if (!options.Keywords.Contains("A")) options.Keywords.Add("A"); - //生成文字 + // 生成文字 var dictString = (pl.GetDistAtPoint(closestPt) * 0.001).ToString("0.00"); var acText = new TextInfo(dictString, closestPt, AttachmentPoint.BaseLeft, textHeight: 200) .AddDBTextToEntity(); - //加入刷新队列 + // 加入刷新队列 drawEntitys.Enqueue(acText); }); @@ -106,7 +106,7 @@ public void TestCmd_Jig44() break; } } - else if (pr.Status != PromptStatus.OK)//PromptStatus.None == 右键,空格,回车,都在这里结束 + else if (pr.Status != PromptStatus.OK)// PromptStatus.None == 右键,空格,回车,都在这里结束 { tr.Editor?.WriteMessage(Environment.NewLine + pr.Status.ToString()); return; diff --git a/tests/TestShared/TestLisp.cs b/tests/TestShared/TestLisp.cs index a5336c1..725a18d 100644 --- a/tests/TestShared/TestLisp.cs +++ b/tests/TestShared/TestLisp.cs @@ -2,7 +2,7 @@ public class TestLisp { - //定义lisp函数 + // 定义lisp函数 [LispFunction("LispTest_RunLisp")] public static object LispTest_RunLisp(ResultBuffer rb) { @@ -10,56 +10,56 @@ public static object LispTest_RunLisp(ResultBuffer rb) return null!; } - //模态命令,只有当CAD发出命令提示或当前没有其他的命令或程序活动的时候才可以被触发 + // 模态命令,只有当CAD发出命令提示或当前没有其他的命令或程序活动的时候才可以被触发 [CommandMethod("CmdTest_RunLisp1", CommandFlags.Modal)] - //透明命令,可以在一个命令提示输入的时候触发例如正交切换,zoom等 + // 透明命令,可以在一个命令提示输入的时候触发例如正交切换,zoom等 [CommandMethod("CmdTest_RunLisp2", CommandFlags.Transparent)] - //选择图元之后执行命令将可以从 获取图元 + // 选择图元之后执行命令将可以从 获取图元 [CommandMethod("CmdTest_RunLisp3", CommandFlags.UsePickSet)] - //命令执行前已选中部分实体.在命令执行过程中这些标记不会被清除 + // 命令执行前已选中部分实体.在命令执行过程中这些标记不会被清除 [CommandMethod("CmdTest_RunLisp4", CommandFlags.Redraw)] - //命令不能在透视图中使用 + // 命令不能在透视图中使用 [CommandMethod("CmdTest_RunLisp5", CommandFlags.NoPerspective)] - //命令不能通过 MULTIPLE命令 重复触发 + // 命令不能通过 MULTIPLE命令 重复触发 [CommandMethod("CmdTest_RunLisp6", CommandFlags.NoMultiple)] - //不允许在模型空间使用命令 + // 不允许在模型空间使用命令 [CommandMethod("CmdTest_RunLisp7", CommandFlags.NoTileMode)] - //不允许在布局空间使用命令 + // 不允许在布局空间使用命令 [CommandMethod("CmdTest_RunLisp8", CommandFlags.NoPaperSpace)] - //命令不能在OEM产品中使用 + // 命令不能在OEM产品中使用 [CommandMethod("CmdTest_RunLisp9", CommandFlags.NoOem)] - //不能直接使用命令名调用,必须使用 组名.全局名 调用 + // 不能直接使用命令名调用,必须使用 组名.全局名 调用 [CommandMethod("CmdTest_RunLisp10", CommandFlags.Undefined)] - //定义lisp方法.已废弃 请使用lispfunction + // 定义lisp方法.已废弃 请使用lispfunction [CommandMethod("CmdTest_RunLisp11", CommandFlags.Defun)] - //命令不会被存储在新的命令堆上 + // 命令不会被存储在新的命令堆上 [CommandMethod("CmdTest_RunLisp12", CommandFlags.NoNewStack)] - //命令不能被内部锁定(命令锁) + // 命令不能被内部锁定(命令锁) [CommandMethod("CmdTest_RunLisp13", CommandFlags.NoInternalLock)] - //调用命令的文档将会被锁定为只读 + // 调用命令的文档将会被锁定为只读 [CommandMethod("CmdTest_RunLisp14", CommandFlags.DocReadLock)] - //调用命令的文档将会被锁定,类似document.lockdocument + // 调用命令的文档将会被锁定,类似document.lockdocument [CommandMethod("CmdTest_RunLisp15", CommandFlags.DocExclusiveLock)] - //命令在CAD运行期间都能使用,而不只是在当前文档 + // 命令在CAD运行期间都能使用,而不只是在当前文档 [CommandMethod("CmdTest_RunLisp16", CommandFlags.Session)] - //获取用户输入时,可以与属性面板之类的交互 + // 获取用户输入时,可以与属性面板之类的交互 [CommandMethod("CmdTest_RunLisp17", CommandFlags.Interruptible)] - //命令不会被记录在命令历史记录 + // 命令不会被记录在命令历史记录 [CommandMethod("CmdTest_RunLisp18", CommandFlags.NoHistory)] - //命令不会被 UNDO取消 + // 命令不会被 UNDO取消 [CommandMethod("CmdTest_RunLisp19", CommandFlags.NoUndoMarker)] - //不能在参照块中使用命令 + // 不能在参照块中使用命令 [CommandMethod("CmdTest_RunLisp20", CommandFlags.NoBlockEditor)] #if ac2009 - //acad09增,不会被动作录制器 捕捉到 + // acad09增,不会被动作录制器 捕捉到 [CommandMethod("CmdTest_RunLisp21", CommandFlags.NoActionRecording)] - //acad09增,会被动作录制器捕捉 + // acad09增,会被动作录制器捕捉 [CommandMethod("CmdTest_RunLisp22", CommandFlags.ActionMacro)] #endif #if !NET35 - //推断约束时不能使用命令 + // 推断约束时不能使用命令 [CommandMethod("CmdTest_RunLisp23", CommandFlags.NoInferConstraint)] - //命令允许在选择图元时临时显示动态尺寸 + // 命令允许在选择图元时临时显示动态尺寸 [CommandMethod("CmdTest_RunLisp24", CommandFlags.TempShowDynDimension)] #endif public static void CmdTest_RunLisp() @@ -90,32 +90,32 @@ public static void CmdTest_RunLisp() Env.Editor.RunLisp("(setq a 10)(princ)", EditorEx.RunLispFlag.AdsQueueexpr); Env.Editor.RunLisp("(princ a)", - EditorEx.RunLispFlag.AdsQueueexpr);//成功输出 + EditorEx.RunLispFlag.AdsQueueexpr);// 成功输出 } else if (flag == EditorEx.RunLispFlag.AcedEvaluateLisp) { // 使用(command "CmdTest_RunLisp1")发送,然后 !b 查看变量,acad08是有值的,高版本是null var strlisp0 = "(setq b 20)"; var res0 = Env.Editor.RunLisp(strlisp0, - EditorEx.RunLispFlag.AcedEvaluateLisp); //有lisp的返回值 + EditorEx.RunLispFlag.AcedEvaluateLisp); // 有lisp的返回值 var strlisp1 = "(defun f1( / )(princ \"aa\"))"; var res1 = Env.Editor.RunLisp(strlisp1, - EditorEx.RunLispFlag.AcedEvaluateLisp); //有lisp的返回值 + EditorEx.RunLispFlag.AcedEvaluateLisp); // 有lisp的返回值 var strlisp2 = "(defun f2( / )(command \"line\"))"; var res2 = Env.Editor.RunLisp(strlisp2, - EditorEx.RunLispFlag.AcedEvaluateLisp); //有lisp的返回值 + EditorEx.RunLispFlag.AcedEvaluateLisp); // 有lisp的返回值 } else if (flag == EditorEx.RunLispFlag.SendStringToExecute) { - //测试异步 - //(command "CmdTest_RunLisp1")和(LispTest_RunLisp)4都是异步 + // 测试异步 + // (command "CmdTest_RunLisp1")和(LispTest_RunLisp)4都是异步 var str = "(setq c 40)(princ)"; Env.Editor.RunLisp(str, - EditorEx.RunLispFlag.SendStringToExecute); //异步,后发送 + EditorEx.RunLispFlag.SendStringToExecute); // 异步,后发送 Env.Editor.RunLisp("(princ c)", - EditorEx.RunLispFlag.AdsQueueexpr); //同步,先发送了,输出是null + EditorEx.RunLispFlag.AdsQueueexpr); // 同步,先发送了,输出是null } } } diff --git a/tests/TestShared/TestMirrorFile.cs b/tests/TestShared/TestMirrorFile.cs index 5c52b67..34bc6d6 100644 --- a/tests/TestShared/TestMirrorFile.cs +++ b/tests/TestShared/TestMirrorFile.cs @@ -23,10 +23,10 @@ public static void CmdTest_MirrorFile() dbText.UpgradeOpen(); var pos = dbText.Position; - //text.Move(pos, Point3d.Origin); - //Y轴 + // text.Move(pos, Point3d.Origin); + // Y轴 dbText.Mirror(Point3d.Origin, new Point3d(0, 1, 0)); - //text.Move(Point3d.Origin, pos); + // text.Move(Point3d.Origin, pos); dbText.DowngradeOpen(); } }); @@ -52,13 +52,13 @@ public static void CmdTest_MirrorFile2() if (entity is DBText dbText) { dbText.Mirror(Point3d.Origin, yaxis); - dbText.IsMirroredInX = true; //这句将导致文字偏移 + dbText.IsMirroredInX = true; // 这句将导致文字偏移 - //指定文字的垂直对齐方式 + // 指定文字的垂直对齐方式 if (dbText.VerticalMode == TextVerticalMode.TextBase) dbText.VerticalMode = TextVerticalMode.TextBottom; - //指定文字的水平对齐方式 + // 指定文字的水平对齐方式 dbText.HorizontalMode = dbText.HorizontalMode switch { TextHorizontalMode.TextLeft => TextHorizontalMode.TextRight, diff --git a/tests/TestShared/TestNetLoad.cs b/tests/TestShared/TestNetLoad.cs index 1378370..f1de378 100644 --- a/tests/TestShared/TestNetLoad.cs +++ b/tests/TestShared/TestNetLoad.cs @@ -21,6 +21,6 @@ public static void Test_NetLoadx() _form.Show(); _form.Focus(); - //Acap.ShowModalDialog(_form); + // Acap.ShowModalDialog(_form); } } diff --git a/tests/TestShared/TestPoint.cs b/tests/TestShared/TestPoint.cs index 51ab60f..688f09f 100644 --- a/tests/TestShared/TestPoint.cs +++ b/tests/TestShared/TestPoint.cs @@ -42,9 +42,9 @@ public void TestptGethash() { // test var pt = Env.Editor.GetPoint("pick pt").Value; - //Tools.TestTimes2(1_000_000, "新语法", () => { + // Tools.TestTimes2(1_000_000, "新语法", () => { // pt.GetHashString2(); - //}); + // }); Tools.TestTimes2(1_000_000, "旧语法", () => { pt.GetHashString(); }); diff --git a/tests/TestShared/TestQuadTree.cs b/tests/TestShared/TestQuadTree.cs index 02aaf14..1d72ad0 100644 --- a/tests/TestShared/TestQuadTree.cs +++ b/tests/TestShared/TestQuadTree.cs @@ -9,8 +9,8 @@ namespace Test; public class CadEntity : QuadEntity { public ObjectId ObjectId; - //这里加入其他字段 - public List? Link;//碰撞链 + // 这里加入其他字段 + public List? Link;// 碰撞链 public System.Drawing.Color Color; public double Angle; public CadEntity(ObjectId objectId, Rect box) : base(box) @@ -44,20 +44,20 @@ public void Test_QuadTree() using var tr = new DBTrans(); Rect dbExt; - //使用数据库边界来进行 + // 使用数据库边界来进行 var dbExtent = tr.Database.GetValidExtents3d(); if (dbExtent == null) { - //throw new ArgumentException("画一个矩形"); - - //这个初始值的矩形是很有意义, - //主要是四叉树分裂过程中产生多个Rect,Rect内有很多重复的double值,是否可以内存复用,以此减少内存大小? - //接着想了一下,Rect可以是int,long,这样可以利用位运算它扩展和缩小, - //最小就是1,并且可以控制四叉树深度,不至于无限递归. - //而且指针长度跟值是一样的,所以就不需要复用了,毕竟跳转一个函数地址挺麻烦的. - //但是因为啊惊懒的原因,并没有单独制作这样的矩形, - //而且非常糟糕的是,c#不支持模板约束运算符,使得值类型之间需要通过一层接口来委婉处理,拉低了效率..引用类型倒是无所谓.. - //要么忍着,要么换c++去搞四叉树吧 + // throw new ArgumentException("画一个矩形"); + + // 这个初始值的矩形是很有意义, + // 主要是四叉树分裂过程中产生多个Rect,Rect内有很多重复的double值,是否可以内存复用,以此减少内存大小? + // 接着想了一下,Rect可以是int,long,这样可以利用位运算它扩展和缩小, + // 最小就是1,并且可以控制四叉树深度,不至于无限递归. + // 而且指针长度跟值是一样的,所以就不需要复用了,毕竟跳转一个函数地址挺麻烦的. + // 但是因为啊惊懒的原因,并没有单独制作这样的矩形, + // 而且非常糟糕的是,c#不支持模板约束运算符,使得值类型之间需要通过一层接口来委婉处理,拉低了效率..引用类型倒是无所谓.. + // 要么忍着,要么换c++去搞四叉树吧 dbExt = new Rect(0, 0, 1 << 10, 1 << 10); } else @@ -67,10 +67,10 @@ public void Test_QuadTree() dbExt = new Rect(a, b); } - //创建四叉树 + // 创建四叉树 _quadTreeRoot = new QuadTree(dbExt); - //数据库边界 + // 数据库边界 var pl = dbExt.ToPoints(); var databaseBoundary = new List<(Point3d, double, double, double)> { @@ -81,21 +81,21 @@ public void Test_QuadTree() }; tr.CurrentSpace.AddPline(databaseBoundary); - //生成多少个图元,导致cad会令undo出错(八叉树深度过大 treemax) - //int maximumItems = 30_0000; + // 生成多少个图元,导致cad会令undo出错(八叉树深度过大 treemax) + // int maximumItems = 30_0000; int maximumItems = 1000; - //随机图元生成 - List ces = new(); //用于随机获取图元 + // 随机图元生成 + List ces = new(); // 用于随机获取图元 Timer.RunTime(() => { - //生成外边界和随机圆形 + // 生成外边界和随机圆形 var grc = GenerateRandomCircle(maximumItems, dbExt); foreach (var ent in grc) { - //初始化图元颜色 - ent!.ColorIndex = 1; //Color.FromRgb(0, 0, 0);//黑色 + // 初始化图元颜色 + ent!.ColorIndex = 1; // Color.FromRgb(0, 0, 0);// 黑色 var edge = ent.GeometricExtents; - //四叉树数据 + // 四叉树数据 var entRect = new Rect(edge.MinPoint.X, edge.MinPoint.Y, edge.MaxPoint.X, edge.MaxPoint.Y); var entId = tr.CurrentSpace.AddEntity(ent); var ce = new CadEntity(entId, entRect) @@ -110,13 +110,13 @@ public void Test_QuadTree() var dbPointCe = new CadEntity(entId, entRect); ces.Add(dbPointCe); } - }, Timer.TimeEnum.Millisecond, "画圆消耗时间:");//30万图元±3秒.cad2021 + }, Timer.TimeEnum.Millisecond, "画圆消耗时间:");// 30万图元±3秒.cad2021 - //测试只加入四叉树的时间 + // 测试只加入四叉树的时间 Timer.RunTime(() => { for (int i = 0; i < ces.Count; i++) _quadTreeRoot.Insert(ces[i]); - }, Timer.TimeEnum.Millisecond, "插入四叉树时间:");//30万图元±0.7秒.cad2021 + }, Timer.TimeEnum.Millisecond, "插入四叉树时间:");// 30万图元±0.7秒.cad2021 tr.Editor?.WriteMessage($"\n加入图元数量:{maximumItems}"); } @@ -138,14 +138,14 @@ public void Test_QuadTree() { var x = rand.Next(x1, x2) + rand.NextDouble(); var y = rand.Next(y1, y2) + rand.NextDouble(); - yield return EntityEx.CreateCircle(new Point3d(x, y, 0), rand.Next(1, 100)); //起点,终点 + yield return EntityEx.CreateCircle(new Point3d(x, y, 0), rand.Next(1, 100)); // 起点,终点 } } /*TODO 啊惊: 有点懒不想改了*/ #if true2 - //选择加入到四叉树 + // 选择加入到四叉树 [CommandMethod("CmdTest_QuadTree21")] public void CmdTest_QuadTree21() { @@ -162,7 +162,7 @@ public void CmdTest_QuadTree21() AddQuadTreeRoot(db, ed, ss); } - //自动加入全图到四叉树 + // 自动加入全图到四叉树 [CommandMethod("CmdTest_QuadTree20")] public void CmdTest_QuadTree20() { @@ -177,14 +177,14 @@ public void CmdTest_QuadTree20() var time1 = Timer.RunTime(() => { db.Action(tr => { db.TraverseBlockTable(tr, btRec => { - if (!btRec.IsLayout)//布局跳过 + if (!btRec.IsLayout)// 布局跳过 return false; foreach (var item in btRec) { - //var ent = item.ToEntity(tr); + // var ent = item.ToEntity(tr); ss.Add(item); - ++entnum;//图元数量:100000, 遍历全图时间:0.216秒 CmdTest_QuadTree2 + ++entnum;// 图元数量:100000, 遍历全图时间:0.216秒 CmdTest_QuadTree2 } return false; }); @@ -192,7 +192,7 @@ public void CmdTest_QuadTree20() }); ed.WriteMessage($"\n图元数量:{entnum}, 遍历全图时间:{time1 / 1000.0}秒"); - //清空原有的 + // 清空原有的 _quadTreeRoot = null; AddQuadTreeRoot(db, ed, ss); } @@ -204,13 +204,13 @@ void AddQuadTreeRoot(Database db, Editor ed, List ss) ed.WriteMessage("\n四叉树是空的,重新初始化"); Rect dbExt; - //使用数据库边界来进行 + // 使用数据库边界来进行 var dbExtent = db.GetValidExtents3d(); if (dbExtent == null) { - //throw new ArgumentException("画一个矩形"); + // throw new ArgumentException("画一个矩形"); - //测试时候画个矩形,在矩形内画随机坐标的圆形 + // 测试时候画个矩形,在矩形内画随机坐标的圆形 dbExt = new Rect(0, 0, 32525, 32525); } else @@ -236,7 +236,7 @@ void AddQuadTreeRoot(Database db, Editor ed, List ss) if (ent is null) return; var edge = new EdgeEntity(ent); - //四叉树数据 + // 四叉树数据 var ce = new CadEntity(entId, edge.Edge) { Color = Utility.RandomColor @@ -260,13 +260,13 @@ void AddQuadTreeRoot(Database db, Editor ed, List ss) #if true2 #region 节点边界显示 - //四叉树减去节点 + // 四叉树减去节点 [CommandMethod("CmdTest_QuadTree0")] public void CmdTest_QuadTree0() { var dm = Acap.DocumentManager; var doc = dm.MdiActiveDocument; - //var db = doc.Database; + // var db = doc.Database; var ed = doc.Editor; ed.WriteMessage("\n四叉树减区"); @@ -281,7 +281,7 @@ public void CmdTest_QuadTree0() _quadTreeRoot.Remove(rect); } - //创建节点边界 + // 创建节点边界 [CommandMethod("CmdTest_QuadTree00")] public void CmdTest_CreateNodesRect() { @@ -297,17 +297,17 @@ public void CmdTest_CreateNodesRect() return; } - //此处发现了一个事务处理的bug,提交数量过多的时候,会导致 ctrl+z 无法回滚, - //需要把事务放在循环体内部 - //报错: 0x6B00500A (msvcr80.dll)处(位于 acad.exe 中)引发的异常: 0xC0000005: 写入位置 0xFFE00000 时发生访问冲突。 - //画出所有的四叉树节点边界,因为事务放在外面引起 + // 此处发现了一个事务处理的bug,提交数量过多的时候,会导致 ctrl+z 无法回滚, + // 需要把事务放在循环体内部 + // 报错: 0x6B00500A (msvcr80.dll)处(位于 acad.exe 中)引发的异常: 0xC0000005: 写入位置 0xFFE00000 时发生访问冲突。 + // 画出所有的四叉树节点边界,因为事务放在外面引起 var nodeRects = new List(); _quadTreeRoot.ForEach(node => { nodeRects.Add(node); return false; }); var rectIds = new List(); - foreach (var item in nodeRects)//Count = 97341 当数量接近这个量级 + foreach (var item in nodeRects)// Count = 97341 当数量接近这个量级 { db.Action(tr => { var pts = item.ToPoints(); @@ -320,7 +320,7 @@ public void CmdTest_CreateNodesRect() db.CoverGroup(tr, rectIds); }); - //获取四叉树深度 + // 获取四叉树深度 int dep = 0; _quadTreeRoot.ForEach(node => { dep = dep > node.Depth ? dep : node.Depth; @@ -333,7 +333,7 @@ public void CmdTest_CreateNodesRect() #endif #region 四叉树查询节点 - //选择范围改图元颜色 + // 选择范围改图元颜色 [CommandMethod("CmdTest_QuadTree3")] public void CmdTest_QuadTree3() { @@ -364,7 +364,7 @@ void Ssget(QuadTreeSelectMode mode) tr.Editor.WriteMessage("选择模式:" + mode); - //仿选择集 + // 仿选择集 var ces = _quadTreeRoot.Query(rect, mode); ces.ForEach(item => { var ent = tr.GetObject(item.ObjectId, OpenMode.ForWrite); @@ -388,8 +388,8 @@ void Ssget(QuadTreeSelectMode mode) return null; var optionsB = new PromptCornerOptions(Environment.NewLine + "输入矩形角点2:", pprA.Value) { - UseDashedLine = true,//使用虚线 - AllowNone = true,//回车 + UseDashedLine = true,// 使用虚线 + AllowNone = true,// 回车 }; var pprB = ed.GetCorner(optionsB); if (pprB.Status != PromptStatus.OK) @@ -404,27 +404,27 @@ void Ssget(QuadTreeSelectMode mode) #endregion } -//public partial class TestQuadTree -//{ +// public partial class TestQuadTree +// { // public void Cmd_tt6() // { // using var tr = new DBTrans(); // var ed = tr.Editor; -// //创建四叉树,默认参数无所谓 +// // 创建四叉树,默认参数无所谓 // var TreeRoot = new QuadTree(new Rect(0, 0, 32525, 32525)); // var fil = OpFilter.Build(e => e.Dxf(0) == "LINE"); // var psr = ed.SSGet("\n 选择需要连接的直线", fil); // if (psr.Status != PromptStatus.OK) return; // var LineEnts = new List(psr.Value.GetEntities(OpenMode.ForWrite)!); -// //将实体插入到四岔树 +// // 将实体插入到四岔树 // foreach (var line in LineEnts) // { // var edge = line.GeometricExtents; // var entRect = new Rect(edge.MinPoint.X, edge.MinPoint.Y, edge.MaxPoint.X, edge.MaxPoint.Y); // var ce = new CadEntity(line.Id, entRect) // { -// //四叉树数据 +// // 四叉树数据 // Angle = line.Angle // }; // TreeRoot.Insert(ce); @@ -432,24 +432,24 @@ void Ssget(QuadTreeSelectMode mode) // var ppo = new PromptPointOptions(Environment.NewLine + "\n指定标注点:<空格退出>") // { -// AllowArbitraryInput = true,//任意输入 -// AllowNone = true //允许回车 +// AllowArbitraryInput = true,// 任意输入 +// AllowNone = true // 允许回车 // }; -// var ppr = ed.GetPoint(ppo);//用户点选 +// var ppr = ed.GetPoint(ppo);// 用户点选 // if (ppr.Status != PromptStatus.OK) // return; // var rect = new Rect(ppr.Value.Point2d(), 100, 100); -// tr.CurrentSpace.AddEntity(rect.ToPolyLine());//显示选择靶标范围 +// tr.CurrentSpace.AddEntity(rect.ToPolyLine());// 显示选择靶标范围 -// var nent = TreeRoot.FindNearEntity(rect);//查询最近实体,按逆时针 -// var ent = tr.GetObject(nent.ObjectId, OpenMode.ForWrite);//打开实体 -// ent.ColorIndex = Utility.GetRandom().Next(1, 256);//1~256随机色 -// ent.DowngradeOpen();//实体降级 +// var nent = TreeRoot.FindNearEntity(rect);// 查询最近实体,按逆时针 +// var ent = tr.GetObject(nent.ObjectId, OpenMode.ForWrite);// 打开实体 +// ent.ColorIndex = Utility.GetRandom().Next(1, 256);// 1~256随机色 +// ent.DowngradeOpen();// 实体降级 // ent.Dispose(); -// var res = TreeRoot.Query(rect, QuadTreeSelectMode.IntersectsWith);//查询选择靶标范围相碰的ID +// var res = TreeRoot.Query(rect, QuadTreeSelectMode.IntersectsWith);// 查询选择靶标范围相碰的ID // res.ForEach(item => { -// if (item.Angle == 0 || item.Angle == Math.PI) //过滤直线角度为0或180的直线 +// if (item.Angle == 0 || item.Angle == Math.PI) // 过滤直线角度为0或180的直线 // { // var ent = tr.GetObject(item.ObjectId, OpenMode.ForWrite); // ent.ColorIndex = Utility.GetRandom().Next(1, 7); @@ -458,4 +458,4 @@ void Ssget(QuadTreeSelectMode mode) // } // }); // } -//} \ No newline at end of file +// } \ No newline at end of file diff --git a/tests/TestShared/Testid.cs b/tests/TestShared/Testid.cs index 1cbf74d..10a4b35 100644 --- a/tests/TestShared/Testid.cs +++ b/tests/TestShared/Testid.cs @@ -15,15 +15,15 @@ public void TestId() { res.ObjectId.Erase(); } - //using (var tr = new DBTrans()) - //{ + // using (var tr = new DBTrans()) + // { // var res = Env.Editor.GetEntity("\npick ent:"); // if(res.Status == PromptStatus.OK) // { // res.ObjectId.Erase(); // } - //} + // } } [CommandMethod("testmycommand")] @@ -37,7 +37,7 @@ public void TestMyCommand() blkred?.AppendEntity(l1); trans.AddNewlyCreatedDBObject(l1, true); trans.Commit(); - //dbtrans.Dispose(); + // dbtrans.Dispose(); } [CommandMethod("testtextstyle")] public void TestTextStyle() diff --git a/tests/TestShared/testConvexHull.cs b/tests/TestShared/testConvexHull.cs index 405ce2d..f96b302 100644 --- a/tests/TestShared/testConvexHull.cs +++ b/tests/TestShared/testConvexHull.cs @@ -6,11 +6,11 @@ public class TestConvexHull [CommandMethod("testch")] public void Testch() { - //using var tr = new DBTrans(); - //var pts = new List(); - //var flag = true; - //while (flag) - //{ + // using var tr = new DBTrans(); + // var pts = new List(); + // var flag = true; + // while (flag) + // { // var pt = tr.Editor.GetPoint("qudian"); // if (pt.Status == PromptStatus.OK) // { @@ -22,53 +22,53 @@ public void Testch() // flag = false; // } - //} + // } - //var ptt = ConvexHull.GetConvexHull(pts); + // var ptt = ConvexHull.GetConvexHull(pts); - //Polyline pl = new Polyline(); - //for (int i = 0; i < ptt.Count; i++) - //{ + // Polyline pl = new Polyline(); + // for (int i = 0; i < ptt.Count; i++) + // { // pl.AddVertexAt(i, ptt[i].Point2d(), 0, 0, 0); - //} - ////pl.AddVertexAt(0, new Point2d(0, 0), 0, 0, 0); - ////pl.AddVertexAt(1, new Point2d(10, 10), 0, 0, 0); - ////pl.AddVertexAt(2, new Point2d(20, 20), 0, 0, 0); - ////pl.AddVertexAt(3, new Point2d(30, 30), 0, 0, 0); - ////pl.AddVertexAt(4, new Point2d(40, 40), 0, 0, 0); - //pl.Closed = true; - //pl.Color = Color.FromColorIndex(ColorMethod.ByColor, 6); - //tr.CurrentSpace.AddEntity(pl); + // } + //// pl.AddVertexAt(0, new Point2d(0, 0), 0, 0, 0); + //// pl.AddVertexAt(1, new Point2d(10, 10), 0, 0, 0); + //// pl.AddVertexAt(2, new Point2d(20, 20), 0, 0, 0); + //// pl.AddVertexAt(3, new Point2d(30, 30), 0, 0, 0); + //// pl.AddVertexAt(4, new Point2d(40, 40), 0, 0, 0); + // pl.Closed = true; + // pl.Color = Color.FromColorIndex(ColorMethod.ByColor, 6); + // tr.CurrentSpace.AddEntity(pl); - //var a1 = GeometryEx.GetArea(new Point2d(0, 0), new Point2d(1, 0), new Point2d(1, 1)); - //var a2 = ConvexHull.cross(new Point3d(0, 0, 0), new Point3d(1, 0, 0), new Point3d(1, 1, 0)); - //tr.Editor.WriteMessage(a1.ToString()); - //tr.Editor.WriteMessage(a2.ToString()); + // var a1 = GeometryEx.GetArea(new Point2d(0, 0), new Point2d(1, 0), new Point2d(1, 1)); + // var a2 = ConvexHull.cross(new Point3d(0, 0, 0), new Point3d(1, 0, 0), new Point3d(1, 1, 0)); + // tr.Editor.WriteMessage(a1.ToString()); + // tr.Editor.WriteMessage(a2.ToString()); - //var vec1 = new Vector2d(1, 1); - //var vec2 = new Vector2d(-1, 1); + // var vec1 = new Vector2d(1, 1); + // var vec2 = new Vector2d(-1, 1); - //var vec3 = vec1.GetPerpendicularVector(); - //var vec4 = vec2.GetPerpendicularVector(); + // var vec3 = vec1.GetPerpendicularVector(); + // var vec4 = vec2.GetPerpendicularVector(); - //var area1 = vec2.DotProduct(vec1.GetPerpendicularVector()); - //var area2 = vec1.DotProduct(vec2.GetPerpendicularVector()); + // var area1 = vec2.DotProduct(vec1.GetPerpendicularVector()); + // var area2 = vec1.DotProduct(vec2.GetPerpendicularVector()); - //var area3 = vec2.DotProduct(vec1); - //var area4 = vec1.DotProduct(vec2); + // var area3 = vec2.DotProduct(vec1); + // var area4 = vec1.DotProduct(vec2); var area5 = GeometryEx.GetArea(new List { new Point2d(0, 0), new Point2d(1, 1), new Point2d(-1, 1) }); var area6 = GeometryEx.GetArea(new List { new Point2d(0, 0), new Point2d(-1, 1), new Point2d(1, 1) }); - //Env.Editor.WriteMessage($"vec1 的法向量= {vec3} \n"); - //Env.Editor.WriteMessage($"vec2 的法向量= {vec4} \n"); + // Env.Editor.WriteMessage($"vec1 的法向量= {vec3} \n"); + // Env.Editor.WriteMessage($"vec2 的法向量= {vec4} \n"); - //Env.Editor.WriteMessage($"vec2 点乘 vec1的法向量= {area1} \n"); - //Env.Editor.WriteMessage($"vec1 点乘 vec2的法向量= {area2} \n"); + // Env.Editor.WriteMessage($"vec2 点乘 vec1的法向量= {area1} \n"); + // Env.Editor.WriteMessage($"vec1 点乘 vec2的法向量= {area2} \n"); - //Env.Editor.WriteMessage($"vec2 点乘 vec1= {area3} \n"); - //Env.Editor.WriteMessage($"vec1 点乘 vec2= {area4} \n"); + // Env.Editor.WriteMessage($"vec2 点乘 vec1= {area3} \n"); + // Env.Editor.WriteMessage($"vec1 点乘 vec2= {area4} \n"); Env.Editor.WriteMessage($"点集的有向面积:{area5} \n"); Env.Editor.WriteMessage($"点集的有向面积:{area6} \n"); diff --git a/tests/TestShared/testblock.cs b/tests/TestShared/testblock.cs index 429b77e..1199e9e 100644 --- a/tests/TestShared/testblock.cs +++ b/tests/TestShared/testblock.cs @@ -18,29 +18,29 @@ public void Test_GetBoundingBoxEx() } } - //块定义 + // 块定义 [CommandMethod("Test_blockdef")] public void Test_BlockDef() { using var tr = new DBTrans(); - //var line = new Line(new Point3d(0, 0, 0), new Point3d(1, 1, 0)); + // var line = new Line(new Point3d(0, 0, 0), new Point3d(1, 1, 0)); tr.BlockTable.Add("test", btr => { btr.Origin = new Point3d(0, 0, 0); }, - () => //图元 + () => // 图元 { return new List { new Line(new Point3d(0, 0, 0), new Point3d(1, 1, 0)) }; }, - () => //属性定义 + () => // 属性定义 { var id1 = new AttributeDefinition() { Position = new Point3d(0, 0, 0), Tag = "start", Height = 0.2 }; var id2 = new AttributeDefinition() { Position = new Point3d(1, 1, 0), Tag = "end", Height = 0.2 }; return new List { id1, id2 }; } ); - //ObjectId objectId = tr.BlockTable.Add("a");//新建块 - //objectId.GetObject().AddEntity();//测试添加空实体 + // ObjectId objectId = tr.BlockTable.Add("a");// 新建块 + // objectId.GetObject().AddEntity();// 测试添加空实体 tr.BlockTable.Add("test1", btr => { btr.Origin = new Point3d(0, 0, 0); @@ -54,21 +54,21 @@ public void Test_BlockDef() return new List { line, acText }; }); } - //修改块定义 + // 修改块定义 [CommandMethod("Test_blockdefchange")] public void Test_BlockDefChange() { using var tr = new DBTrans(); - //var line = new Line(new Point3d(0, 0, 0), new Point3d(1, 1, 0)); - //tr.BlockTable.Change("test", btr => - //{ + // var line = new Line(new Point3d(0, 0, 0), new Point3d(1, 1, 0)); + // tr.BlockTable.Change("test", btr => + // { // btr.Origin = new Point3d(5, 5, 0); // btr.AddEntity(new Circle(new Point3d(0, 0, 0), Vector3d.ZAxis, 2)); // btr.GetEntities() // .ToList() - // .ForEach(e => e.Flush()); //刷新块显示 + // .ForEach(e => e.Flush()); // 刷新块显示 - //}); + // }); tr.BlockTable.Change("test", btr => { @@ -114,11 +114,11 @@ public void Test_InsertBlockDef() var att3 = new AttributeDefinition() { Position = new Point3d(10, 14, 0), Tag = "tagTest3", Height = 1, TextString = "valueTest3" }; var att4 = new AttributeDefinition() { Position = new Point3d(10, 16, 0), Tag = "tagTest4", Height = 1, TextString = "valueTest4" }; tr.BlockTable.Add("test2", new List { line3, line4 }, new List { att3, att4 }); - //tr.CurrentSpace.InsertBlock(new Point3d(4, 4, 0), "test1"); // 测试默认 - //tr.CurrentSpace.InsertBlock(new Point3d(4, 4, 0), "test2"); - //tr.CurrentSpace.InsertBlock(new Point3d(4, 4, 0), "test3"); //测试插入不存在的块定义 - //tr.CurrentSpace.InsertBlock(new Point3d(0, 0, 0), "test1", new Scale3d(2)); // 测试放大2倍 - //tr.CurrentSpace.InsertBlock(new Point3d(4, 4, 0), "test1", new Scale3d(2), Math.PI / 4); // 测试放大2倍,旋转45度 + // tr.CurrentSpace.InsertBlock(new Point3d(4, 4, 0), "test1"); // 测试默认 + // tr.CurrentSpace.InsertBlock(new Point3d(4, 4, 0), "test2"); + // tr.CurrentSpace.InsertBlock(new Point3d(4, 4, 0), "test3"); // 测试插入不存在的块定义 + // tr.CurrentSpace.InsertBlock(new Point3d(0, 0, 0), "test1", new Scale3d(2)); // 测试放大2倍 + // tr.CurrentSpace.InsertBlock(new Point3d(4, 4, 0), "test1", new Scale3d(2), Math.PI / 4); // 测试放大2倍,旋转45度 var def1 = new Dictionary { @@ -183,7 +183,7 @@ public void Test_ClipBlock() ); } ); - //tr.BlockTable.Add("hah"); + // tr.BlockTable.Add("hah"); var id = tr.CurrentSpace.InsertBlock(new Point3d(0, 0, 0), "test1"); var bref = tr.GetObject(id)!; var pts = new List { new Point3d(3, 3, 0), new Point3d(7, 3, 0), new Point3d(7, 7, 0), new Point3d(3, 7, 0) }; @@ -203,29 +203,29 @@ public void test_block_ej() using (var tr = new DBTrans()) { - //Point3d.Origin.AddBellowToModelSpace(100, 100, 5, 3, 30);//画波纹管 + // Point3d.Origin.AddBellowToModelSpace(100, 100, 5, 3, 30);// 画波纹管 - //Database db2 = new Database(false, true); - //string fullFileName = @".\MyBlockDwgFile\001.dwg"; - //db2.ReadDwgFile(fullFileName, System.IO.FileShare.Read, true, null); - //db2.CloseInput(true); - //string blockName = "test"; - //if (!tr.BlockTable.Has(blockName)) - //{ - // //tr.Database.Insert(blockName, db2, false);//插入块 + // Database db2 = new Database(false, true); + // string fullFileName = @".\MyBlockDwgFile\001.dwg"; + // db2.ReadDwgFile(fullFileName, System.IO.FileShare.Read, true, null); + // db2.CloseInput(true); + // string blockName = "test"; + // if (!tr.BlockTable.Has(blockName)) + // { + // // tr.Database.Insert(blockName, db2, false);// 插入块 // db.Insert(blockName, db2, false); - //} + // } string fullFileName = @"C:\Users\vic\Desktop\001.dwg"; var blockdef = tr.BlockTable.GetBlockFrom(fullFileName, false); - tr.Database.Clayer = tr.LayerTable["0"];//当前图层切换为0图层 + tr.Database.Clayer = tr.LayerTable["0"];// 当前图层切换为0图层 tr.LayerTable.Change(tr.Database.Clayer, ltr => { - ltr.Color = Color.FromColorIndex(ColorMethod.ByAci, 2); //ColorMethod.ByAci可以让我们使用AutoCAD ACI颜色索引……这里为2(表示黄色) + ltr.Color = Color.FromColorIndex(ColorMethod.ByAci, 2); // ColorMethod.ByAci可以让我们使用AutoCAD ACI颜色索引……这里为2(表示黄色) }); - var id = tr.ModelSpace.InsertBlock(Point3d.Origin, blockdef);//插入块参照 + var id = tr.ModelSpace.InsertBlock(Point3d.Origin, blockdef);// 插入块参照 var entTest = tr.GetObject(id); entTest?.Draw(); } @@ -242,9 +242,9 @@ public void test_block_ej() } var Bref = tr2.GetObject(PER.ObjectId)!; - //var BTR = tr.GetObject(Bref.BlockTableRecord, OpenMode.ForWrite); - ////如果知道块名字BTRName - //BlockTableRecord BTR = tr.GetObject(tr.BlockTable[blockName], OpenMode.ForWrite); + // var BTR = tr.GetObject(Bref.BlockTableRecord, OpenMode.ForWrite); + //// 如果知道块名字BTRName + // BlockTableRecord BTR = tr.GetObject(tr.BlockTable[blockName], OpenMode.ForWrite); var btr = tr2.BlockTable[Bref.Name]; @@ -293,24 +293,24 @@ public void test_block_ej() [CommandMethod("W_KSZK")] public void QuickBlockDef() { - //Database db = HostApplicationServices.WorkingDatabase; + // Database db = HostApplicationServices.WorkingDatabase; Editor ed = Acap.DocumentManager.MdiActiveDocument.Editor; PromptSelectionOptions promptOpt = new() { MessageForAdding = "请选择需要快速制作块的对象" }; string blockName = "W_BLOCK_" + DateTime.Now.ToString("yyyyMMdd_HHmmss"); - //var rss = ed.GetSelection(promptOpt); + // var rss = ed.GetSelection(promptOpt); var rss = Env.Editor.GetSelection(promptOpt); using var tr = new DBTrans(); if (rss.Status == PromptStatus.OK) { - //SelectionSet ss = rss.Value; - //ObjectId[] ids = ss.GetObjectIds(); - //var ents = new List>(); - //var extents = new Extents3d(); - //foreach (var id in ids) - //{ + // SelectionSet ss = rss.Value; + // ObjectId[] ids = ss.GetObjectIds(); + // var ents = new List>(); + // var extents = new Extents3d(); + // foreach (var id in ids) + // { // Entity ent = tr.GetObject(id); // if (ent is null) // continue; @@ -328,52 +328,52 @@ public void QuickBlockDef() // { // ed.WriteMessage(exc.Message); // } - //} - //ents = ents.OrderBy(x => x.Value).ToList(); + // } + // ents = ents.OrderBy(x => x.Value).ToList(); var ents = rss.Value.GetEntities(); - //ents.ForEach(ent => extents.AddExtents(ent.GeometricExtents)); + // ents.ForEach(ent => extents.AddExtents(ent.GeometricExtents)); var extents = ents!.GetExtents(); Point3d pt = extents.MinPoint; Matrix3d matrix = Matrix3d.Displacement(Point3d.Origin - pt); - //var newEnts = new List(); - //foreach (var ent in ents) - //{ + // var newEnts = new List(); + // foreach (var ent in ents) + // { // var newEnt = ent.Key; // newEnt.TransformBy(matrix); // newEnts.Add(newEnt); - //} - //if (tr.BlockTable.Has(blockName)) - //{ + // } + // if (tr.BlockTable.Has(blockName)) + // { // Acap.ShowAlertDialog(Environment.NewLine + "块名重复,程序退出!"); // return; - //} + // } ents.ForEach(ent => ent?.ForWrite(e => e?.TransformBy(matrix))); - //var newents = ents.Select(ent => - //{ + // var newents = ents.Select(ent => + // { // var maping = new IdMapping(); // return ent.DeepClone(ent, maping, true) as Entity; - //}); + // }); var newents = ents.Select(ent => ent?.Clone() as Entity); - //ents.ForEach(ent => ent.ForWrite(e => e.Erase(true))); // 删除实体就会卡死,比较奇怪,估计是Clone()函数的问题 + // ents.ForEach(ent => ent.ForWrite(e => e.Erase(true))); // 删除实体就会卡死,比较奇怪,估计是Clone()函数的问题 // 经过测试不是删除的问题 var btrId = tr.BlockTable.Add(blockName, newents!); ents.ForEach(ent => ent?.ForWrite(e => e?.Erase(true))); var bId = tr.CurrentSpace.InsertBlock(pt, blockName); - //tr.GetObject(bId, OpenMode.ForWrite).Move(Point3d.Origin, Point3d.Origin); - //var ed = Acap.DocumentManager.MdiActiveDocument.Editor; - //ed.Regen(); - //tr.Editor.Regen(); + // tr.GetObject(bId, OpenMode.ForWrite).Move(Point3d.Origin, Point3d.Origin); + // var ed = Acap.DocumentManager.MdiActiveDocument.Editor; + // ed.Regen(); + // tr.Editor.Regen(); // 调用regen() 卡死 } - //tr.Editor.Regen(); - //ed.Regen(); - //using (var tr = new DBTrans()) - //{ + // tr.Editor.Regen(); + // ed.Regen(); + // using (var tr = new DBTrans()) + // { // tr.CurrentSpace.InsertBlock(Point3d.Origin, blockName); // tr.Editor.Regen(); - //} + // } } [CommandMethod("test_quickblockdef")] @@ -387,7 +387,7 @@ public void Test_QuickBlockDef() }; string blockName = "W_BLOCK_" + DateTime.Now.ToString("yyyyMMdd_HHmmss"); var rss = ed.GetSelection(promptOpt); - //var rss = Env.Editor.GetSelection(promptOpt); + // var rss = Env.Editor.GetSelection(promptOpt); if (rss.Status != PromptStatus.OK) { return; @@ -411,11 +411,11 @@ public void Test_QuickBlockDef() tr.AddNewlyCreatedDBObject(btr, true); bt.DowngradeOpen(); // tr.Commit(); - //} + // } - //using (var tr1 = db.TransactionManager.StartTransaction()) - //{ - //var bt = tr1.GetObject(db.BlockTableId, OpenMode.ForRead) as BlockTable; + // using (var tr1 = db.TransactionManager.StartTransaction()) + // { + // var bt = tr1.GetObject(db.BlockTableId, OpenMode.ForRead) as BlockTable; var btr1 = tr.GetObject(db.CurrentSpaceId, OpenMode.ForWrite) as BlockTableRecord; var br = new BlockReference(Point3d.Origin, bt[blockName]) { @@ -426,7 +426,7 @@ public void Test_QuickBlockDef() btr1.DowngradeOpen(); ed.Regen(); tr.Commit(); - //ed.Regen(); + // ed.Regen(); } @@ -490,9 +490,9 @@ public void Test_Cbll() string filename = @"C:\Users\vic\Desktop\Drawing1.dwg"; using var tr = new DBTrans(); using var tr1 = new DBTrans(filename); - //tr.BlockTable.GetBlockFrom(filename, true); + // tr.BlockTable.GetBlockFrom(filename, true); string blkdefname = SymbolUtilityServices.RepairSymbolName(SymbolUtilityServices.GetSymbolNameFromPathName(filename, "dwg"), false); - tr.Database.Insert(blkdefname, tr1.Database, false); //插入了块定义,未插入块参照 + tr.Database.Insert(blkdefname, tr1.Database, false); // 插入了块定义,未插入块参照 } #if !NET35 diff --git a/tests/TestShared/testeditor.cs b/tests/TestShared/testeditor.cs index 7633899..60d742f 100644 --- a/tests/TestShared/testeditor.cs +++ b/tests/TestShared/testeditor.cs @@ -34,12 +34,12 @@ public void Testzoom() [CommandMethod("testzoomextent")] public void Testzoomextent() { - //using var tr = new DBTrans(); - //var res = Env.Editor.GetEntity("\npick ent:"); - //if (res.Status == PromptStatus.OK) - //{ + // using var tr = new DBTrans(); + // var res = Env.Editor.GetEntity("\npick ent:"); + // if (res.Status == PromptStatus.OK) + // { // Env.Editor.ZoomObject(res.ObjectId.GetObject()); - //} + // } Env.Editor.ZoomExtents(); } diff --git a/tests/TestShared/testenv.cs b/tests/TestShared/testenv.cs index a5c190c..e86343e 100644 --- a/tests/TestShared/testenv.cs +++ b/tests/TestShared/testenv.cs @@ -43,7 +43,7 @@ public void Testosmode() Env.OSMode = (Env.OSModeType)5179; // 追加模式 Env.OSMode |= Env.OSModeType.Center; - //检查是否有某个模式 + // 检查是否有某个模式 var os = Env.OSMode.Include(Env.OSModeType.Center); // 取消某个模式 Env.OSMode ^= Env.OSModeType.Center; @@ -60,7 +60,7 @@ public void Testosmode1() [CommandMethod("testcadver")] public void Testcadver() { - //Env.Print(AcadVersion.Versions); + // Env.Print(AcadVersion.Versions); AcadVersion.Versions.ForEach(v => Env.Print(v)); AcadVersion.FromApp(Acap.AcadApplication)?.Print(); 1.Print(); @@ -79,10 +79,9 @@ public void TestGetVar() [CommandMethod("TestDwgVersion")] public void TestDwgVersion() { - // - //string filename = @"C:\Users\vic\Desktop\test.dwg"; - //var a = Helper.GetCadFileVersion(filename); - //a.Print(); - //((DwgVersion)a).Print(); + // string filename = @"C:\Users\vic\Desktop\test.dwg"; + // var a = Helper.GetCadFileVersion(filename); + // a.Print(); + // ((DwgVersion)a).Print(); } } -- Gitee From 87e2207cd7aaebdcba6234aad807fb66bec26761 Mon Sep 17 00:00:00 2001 From: liuqihong <540762622@qq.com> Date: Mon, 29 Aug 2022 21:18:22 +0800 Subject: [PATCH 454/675] =?UTF-8?q?=E5=A2=9E=E5=8A=A0=E4=BB=A3=E7=A0=81?= =?UTF-8?q?=E8=A7=84=E8=8C=83?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- ...43\347\240\201\350\247\204\350\214\203.md" | 23 +++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git "a/docs/0x01\344\273\243\347\240\201\350\247\204\350\214\203.md" "b/docs/0x01\344\273\243\347\240\201\350\247\204\350\214\203.md" index b19302b..4e0c680 100644 --- "a/docs/0x01\344\273\243\347\240\201\350\247\204\350\214\203.md" +++ "b/docs/0x01\344\273\243\347\240\201\350\247\204\350\214\203.md" @@ -90,6 +90,29 @@ vs --> 选项 --> 文本编辑器 --> c# -->代码样式 --> 右页,基于设置 +### 0x04 所有的注释符号//后面加空格 + +利用此正则替换: + +``` +(? Date: Tue, 30 Aug 2022 00:37:13 +0800 Subject: [PATCH 455/675] =?UTF-8?q?=E5=88=87=E5=89=B2=E5=AD=97=E7=AC=A6?= =?UTF-8?q?=E4=B8=B2=E9=94=99=E8=AF=AF?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/IFoxCAD.LoadForm/DefaultAssemblyResolve.cs | 18 +++++++++++++++++- 1 file changed, 17 insertions(+), 1 deletion(-) diff --git a/src/IFoxCAD.LoadForm/DefaultAssemblyResolve.cs b/src/IFoxCAD.LoadForm/DefaultAssemblyResolve.cs index 8aabc1d..fc69703 100644 --- a/src/IFoxCAD.LoadForm/DefaultAssemblyResolve.cs +++ b/src/IFoxCAD.LoadForm/DefaultAssemblyResolve.cs @@ -1,5 +1,7 @@ namespace IFoxCAD.LoadEx; +using System.Diagnostics; + public class AssemblyHelper { /// @@ -33,17 +35,31 @@ public class AssemblyHelper // 否则a.dll引用b.dll函数的时候,b.dll修改重生成之后, // 加载进去会调用第一个版本的b.dll, // vs会迭代程序版本号的*,所以最后的可用就是循环到最后的. + + for (int i = 0; i < cadAss.Length; i++) { if (GetAssemblyName(cadAss[i].GetName().FullName) != ag) continue; result = cadAss[i]; } + + if (result == null) + { + var sb = new StringBuilder(); + sb.AppendLine(nameof(DefaultAssemblyResolve) + "出错,没有在本程序集中找到它"); + sb.AppendLine("++参数名:: " + GetAssemblyName(args.Name)); + sb.AppendLine("++参数完整信息:: " + args.Name); + for (int i = 0; i < cadAss.Length; i++) + sb.AppendLine("-------匹配对象:: " + GetAssemblyName(cadAss[i].GetName().FullName)); + Debug.WriteLine(sb.ToString()); + Debugger.Break(); + } return result; } static string GetAssemblyName(string argString) { - return argString.Substring(argString.IndexOf(',')); + return argString.Substring(0, argString.IndexOf(',')); } } \ No newline at end of file -- Gitee From 14336fd481112a9876b111c428b5a065c0eb90db Mon Sep 17 00:00:00 2001 From: yxrwendao Date: Wed, 31 Aug 2022 02:25:44 +0800 Subject: [PATCH 456/675] =?UTF-8?q?20220831=20=E6=B7=BB=E5=8A=A0=E4=BA=86R?= =?UTF-8?q?ondom=E7=9A=84=E6=89=A9=E5=B1=95=E7=B1=BBRandomEx.cs?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/IFoxCAD.Basal/RandomEx.cs | 227 ++++++++++++++++++++++++++++++++++ 1 file changed, 227 insertions(+) create mode 100644 src/IFoxCAD.Basal/RandomEx.cs diff --git a/src/IFoxCAD.Basal/RandomEx.cs b/src/IFoxCAD.Basal/RandomEx.cs new file mode 100644 index 0000000..b29eeb4 --- /dev/null +++ b/src/IFoxCAD.Basal/RandomEx.cs @@ -0,0 +1,227 @@ +/* +*┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━模块信息━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┓ +*┃ 作 者:YxrWendao +*┃ 创建时间:2022/8/30 22:49:30 +*┃ 模块描述:随机数生成器 +*┃ 使用范围:通用 +*┃ 说 明:本模块中除GetRandom与NextColor方法是IFoxCAD原有的以外,其他方法均通过网络收集整理而来。 +*┃ 代码版本:1.0 +*┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛ +*/ +namespace IFoxCAD.Basal +{ + /* GetRandom与NextColor方法没有在IFoxCAD找到,所以全部整理到此模块,如有重复,请知情者自行删除。 + */ + /// + /// 随机值扩展类 + /// + public static class RandomEx + { + /// + /// 生成一个指定范围的浮点数值 + /// + /// 一个随机值产生器 + /// 范围最小浮点数值 + /// 范围最大浮点数值 + /// + public static double NextDouble(Random ran, double minValue, double maxValue) + { + //https://www.cnblogs.com/qingheshiguang/p/15806915.html + return ran.NextDouble() * (maxValue - minValue) + minValue; + } + /// + /// 生成一个指定范围的浮点数值 + /// + /// 范围最小浮点数值 + /// 范围最大浮点数值 + /// + public static double NextDouble(double minValue, double maxValue) + { + //https://www.cnblogs.com/qingheshiguang/p/15806915.html + return NextDouble(GetRandom(), minValue, maxValue); + } + /// + /// 生成一个布尔随机数 + /// + /// + public static bool NextBool() + { + //https://www.cnblogs.com/qingheshiguang/p/15806915.html + return NextBool(GetRandom()); + } + /// + /// 生成一个布尔随机数 + /// + /// + public static bool NextBool(Random ran) + { + //https://www.cnblogs.com/qingheshiguang/p/15806915.html + bool[] arr = { true, false }; + return arr[ran.Next(2)]; + } + /// + /// 生成一个不连续或指定值的随机值 + /// + /// 一个字符串数组 + /// + public static string NextString(string[] arr) + { + //https://www.cnblogs.com/qingheshiguang/p/15806915.html + return NextString(GetRandom(), arr); + } + /// + /// 生成一个不连续或指定值的随机值 + /// + /// 一个随机值产生器 + /// 一个字符串数组 + /// + public static string NextString(Random ran, string[] arr) + { + //https://www.cnblogs.com/qingheshiguang/p/15806915.html + if (ran is null) + { + ran = GetRandom(); + } + int n = ran.Next(arr.Length - 1); + return arr[n]; + } + /// + /// 生成一个不连续或指定值的随机值 + /// + /// 一个双精度值数组 + /// + public static double NextDouble(double[] arr) + { + //https://www.cnblogs.com/qingheshiguang/p/15806915.html + return NextDouble(GetRandom(), arr); + } + /// + /// 生成不连续或指定值的随机值 + /// + /// 一个随机值产生器 + /// 一个双精度值数组 + /// + public static double NextDouble(Random ran, double[] arr) + { + //https://www.cnblogs.com/qingheshiguang/p/15806915.html + if (ran is null) + { + ran = GetRandom(); + } + int n = ran.Next(arr.Length - 1); + return arr[n]; + } + /// + /// 生成指定范围内的整数 + /// + /// 范围最大整数值 + /// + public static int NextInt(int max) + { + return NextInt(GetRandom(), max); + } + /// + /// 生成指定范围内的整数 + /// + /// 一个随机值产生器 + /// 范围最大整数值 + /// + public static int NextInt(Random ran, int max) + { + if (ran is null) + { + ran = GetRandom(); + } + return ran.Next(max); + } + /// + /// 生成指定范围内的整数 + /// + /// 范围的最小整数 + /// 范围的最大整数 + /// 返回一个介于之间的整数 + public static int NextInt(int min, int max) + { + return NextInt(GetRandom(), min, max); + } + /// + /// 生成指定范围内的整数 + /// + /// 一个随机值产生器 + /// 范围的最小整数 + /// 范围的最大整数 + /// 返回一个介于之间的整数 + public static int NextInt(Random ran, int min, int max) + { + if (ran is null) + { + ran = GetRandom(); + } + return ran.Next(min, max); + } + + /// + /// 生成一个随机颜色 + /// + /// 返回一个System.Drawing.Color + public static System.Drawing.Color NextColor() + { + //来源于:IFoxCAD.Utility类 + return NextColor(GetRandom()); + } + /// + /// 生成一个随机颜色 + /// + /// + public static System.Drawing.Color NextColor(Random ran) + { + //来源于:IFoxCAD.Utility类 + if (ran is null) + { + ran = GetRandom(); + } + int R = ran.Next(255); + int G = ran.Next(255); + int B = ran.Next(255); + B = (R + G > 400) ? R + G - 400 : B;//0 : 380 - R - G; + B = (B > 255) ? 255 : B; + return System.Drawing.Color.FromArgb(R, G, B); + } + + /// + /// 带有随机种子的随机数 + /// 为什么这样写随机种子呢 + /// + /// + public static Random GetRandom() + { + //来源于:IFoxCAD.Utility类 + var tick = DateTime.Now.Ticks; + /* + * 知识准备: + * | 高位64位 | 低位32位 | + * Convert.ToString(int.MaxValue, 2)输出二进制 "1111111111111111111111111111111" 31个;最高位是符号位,所以少1位 + * Convert.ToString(long.MaxValue,2)输出二进制,刚好长一倍 "11111111111111111111111111111111 1111111111111111111111111111111" 63个;最高位是符号位,所以少1位 + * Convert.ToString(0xffffffffL, 2)int.MaxValue再按位多1 "1 1111111111111111111111111111111" 32个;前面的0不会打印出来 + * + * Convert.ToString(long.MaxValue>>32, 2)相当于平移高位的到低位范围,也就是上面少打印的二进制 + * 验证右移是不是高位保留,答案是 + * var a = Convert.ToInt64("101111111111111111111111111111111111111111111111111111111111111", 2); + * Convert.ToString(a >> 32,2); + * + * 解释代码: + * 0x01: + * (int)(long.MaxValue & 0xffffffffL) | (int)(long.MaxValue >> 32); + * Convert.ToString(long.MaxValue & 0xffffffffL, 2)//去掉高位:"11111111111111111111111111111111" 32个,再强转int + * 按位与&是保证符号位肯定是1,其他尽可能为0,高位被去掉只是MaxValue&0的原因,强转才是去掉高位..."尽可能"一词带来第一次随机性 + * 0x02: + * Convert.ToString((long.MaxValue >> 32), 2) //去掉低位: "1111111111111111111111111111111" 31个,再强转int + * 按位或|是尽可能为1..."尽可能"一词带来第二次随机性 + * + */ + + var tickSeeds = (int)(tick & 0xffffffffL) | (int)(tick >> 32); + return new Random(tickSeeds); + } + } +} -- Gitee From 66dd4cbbb07b2bb5ad35026098a285a17b4c8bc4 Mon Sep 17 00:00:00 2001 From: liuqihong <540762622@qq.com> Date: Wed, 31 Aug 2022 12:53:40 +0800 Subject: [PATCH 457/675] =?UTF-8?q?=E6=A0=B9=E6=8D=AE=E9=9A=8F=E6=9C=BA?= =?UTF-8?q?=E6=95=B0=E7=B1=BB=E6=95=B4=E7=90=86?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/IFoxCAD.Basal/RandomEx.cs | 402 +++++++++--------- .../Algorithms/QuadTree/Utility.cs | 60 --- .../IFoxCAD.Cad.Shared.projitems | 1 - tests/TestShared/TestQuadTree.cs | 5 +- 4 files changed, 202 insertions(+), 266 deletions(-) delete mode 100644 src/IFoxCAD.Cad.Shared/Algorithms/QuadTree/Utility.cs diff --git a/src/IFoxCAD.Basal/RandomEx.cs b/src/IFoxCAD.Basal/RandomEx.cs index b29eeb4..e07b974 100644 --- a/src/IFoxCAD.Basal/RandomEx.cs +++ b/src/IFoxCAD.Basal/RandomEx.cs @@ -8,220 +8,216 @@ *┃ 代码版本:1.0 *┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛ */ -namespace IFoxCAD.Basal + +namespace IFoxCAD.Basal; + +/// +/// 随机值扩展类 +/// +public static class RandomEx { - /* GetRandom与NextColor方法没有在IFoxCAD找到,所以全部整理到此模块,如有重复,请知情者自行删除。 - */ /// - /// 随机值扩展类 + /// 生成一个指定范围的浮点数值
    + /// 相关链接 ///
    - public static class RandomEx + /// 一个随机值产生器 + /// 范围最小浮点数值 + /// 范围最大浮点数值 + /// + public static double NextDouble(Random ran, double minValue, double maxValue) { - /// - /// 生成一个指定范围的浮点数值 - /// - /// 一个随机值产生器 - /// 范围最小浮点数值 - /// 范围最大浮点数值 - /// - public static double NextDouble(Random ran, double minValue, double maxValue) - { - //https://www.cnblogs.com/qingheshiguang/p/15806915.html - return ran.NextDouble() * (maxValue - minValue) + minValue; - } - /// - /// 生成一个指定范围的浮点数值 - /// - /// 范围最小浮点数值 - /// 范围最大浮点数值 - /// - public static double NextDouble(double minValue, double maxValue) - { - //https://www.cnblogs.com/qingheshiguang/p/15806915.html - return NextDouble(GetRandom(), minValue, maxValue); - } - /// - /// 生成一个布尔随机数 - /// - /// - public static bool NextBool() - { - //https://www.cnblogs.com/qingheshiguang/p/15806915.html - return NextBool(GetRandom()); - } - /// - /// 生成一个布尔随机数 - /// - /// - public static bool NextBool(Random ran) - { - //https://www.cnblogs.com/qingheshiguang/p/15806915.html - bool[] arr = { true, false }; - return arr[ran.Next(2)]; - } - /// - /// 生成一个不连续或指定值的随机值 - /// - /// 一个字符串数组 - /// - public static string NextString(string[] arr) - { - //https://www.cnblogs.com/qingheshiguang/p/15806915.html - return NextString(GetRandom(), arr); - } - /// - /// 生成一个不连续或指定值的随机值 - /// - /// 一个随机值产生器 - /// 一个字符串数组 - /// - public static string NextString(Random ran, string[] arr) - { - //https://www.cnblogs.com/qingheshiguang/p/15806915.html - if (ran is null) - { - ran = GetRandom(); - } - int n = ran.Next(arr.Length - 1); - return arr[n]; - } - /// - /// 生成一个不连续或指定值的随机值 - /// - /// 一个双精度值数组 - /// - public static double NextDouble(double[] arr) - { - //https://www.cnblogs.com/qingheshiguang/p/15806915.html - return NextDouble(GetRandom(), arr); - } - /// - /// 生成不连续或指定值的随机值 - /// - /// 一个随机值产生器 - /// 一个双精度值数组 - /// - public static double NextDouble(Random ran, double[] arr) - { - //https://www.cnblogs.com/qingheshiguang/p/15806915.html - if (ran is null) - { - ran = GetRandom(); - } - int n = ran.Next(arr.Length - 1); - return arr[n]; - } - /// - /// 生成指定范围内的整数 - /// - /// 范围最大整数值 - /// - public static int NextInt(int max) - { - return NextInt(GetRandom(), max); - } - /// - /// 生成指定范围内的整数 - /// - /// 一个随机值产生器 - /// 范围最大整数值 - /// - public static int NextInt(Random ran, int max) - { - if (ran is null) - { - ran = GetRandom(); - } - return ran.Next(max); - } - /// - /// 生成指定范围内的整数 - /// - /// 范围的最小整数 - /// 范围的最大整数 - /// 返回一个介于之间的整数 - public static int NextInt(int min, int max) - { - return NextInt(GetRandom(), min, max); - } - /// - /// 生成指定范围内的整数 - /// - /// 一个随机值产生器 - /// 范围的最小整数 - /// 范围的最大整数 - /// 返回一个介于之间的整数 - public static int NextInt(Random ran, int min, int max) - { - if (ran is null) - { - ran = GetRandom(); - } - return ran.Next(min, max); - } + return ran.NextDouble() * (maxValue - minValue) + minValue; + } + /// + /// 生成一个指定范围的浮点数值 + /// + /// 范围最小浮点数值 + /// 范围最大浮点数值 + /// + public static double NextDouble(double minValue, double maxValue) + { + return NextDouble(GetRandom(), minValue, maxValue); + } + /// + /// 生成一个布尔随机数 + /// + /// + public static bool NextBool() + { + return NextBool(GetRandom()); + } + /// + /// 生成一个布尔随机数
    + ///
    + /// + public static bool NextBool(Random ran) + { + bool[] arr = { true, false }; + return arr[ran.Next(2)]; + } + /// + /// 生成一个不连续或指定值的随机值 + /// + /// 一个字符串数组 + /// + public static string NextString(string[] arr) + { + return NextString(GetRandom(), arr); + } + /// + /// 生成一个不连续或指定值的随机值 + /// + /// 一个随机值产生器 + /// 一个字符串数组 + /// + public static string NextString(Random ran, string[] arr) + { + ran ??= GetRandom(); + int n = ran.Next(arr.Length - 1); + return arr[n]; + } + /// + /// 生成一个不连续或指定值的随机值 + /// + /// 一个双精度值数组 + /// + public static double NextDouble(double[] arr) + { + return NextDouble(GetRandom(), arr); + } + /// + /// 生成不连续或指定值的随机值 + /// + /// 一个随机值产生器 + /// 一个双精度值数组 + /// + public static double NextDouble(Random ran, double[] arr) + { + ran ??= GetRandom(); + int n = ran.Next(arr.Length - 1); + return arr[n]; + } + /// + /// 生成指定范围内的整数 + /// + /// 范围最大整数值 + /// + public static int NextInt(int max) + { + return NextInt(GetRandom(), max); + } + /// + /// 生成指定范围内的整数 + /// + /// 一个随机值产生器 + /// 范围最大整数值 + /// + public static int NextInt(Random ran, int max) + { + ran ??= GetRandom(); + return ran.Next(max); + } + /// + /// 生成指定范围内的整数 + /// + /// 范围的最小整数 + /// 范围的最大整数 + /// 返回一个介于之间的整数 + public static int NextInt(int min, int max) + { + return NextInt(GetRandom(), min, max); + } + /// + /// 生成指定范围内的整数 + /// + /// 一个随机值产生器 + /// 范围的最小整数 + /// 范围的最大整数 + /// 返回一个介于之间的整数 + public static int NextInt(Random ran, int min, int max) + { + ran ??= GetRandom(); + return ran.Next(min, max); + } - /// - /// 生成一个随机颜色 - /// - /// 返回一个System.Drawing.Color - public static System.Drawing.Color NextColor() - { - //来源于:IFoxCAD.Utility类 - return NextColor(GetRandom()); - } - /// - /// 生成一个随机颜色 - /// - /// - public static System.Drawing.Color NextColor(Random ran) + /// + /// 生成一个随机颜色 + /// + /// 返回一个System.Drawing.Color + public static System.Drawing.Color NextColor() + { + return NextColor(GetRandom()); + } + /// + /// 生成一个随机颜色 + /// + /// + public static System.Drawing.Color NextColor(Random ran) + { + ran ??= GetRandom(); + int R = ran.Next(255); + int G = ran.Next(255); + int B = ran.Next(255); + B = (R + G > 400) ? R + G - 400 : B;//0 : 380 - R - G; + B = (B > 255) ? 255 : B; + return System.Drawing.Color.FromArgb(R, G, B); + } + + + + + + + /* + * 知识准备: + * | 高位64位 | 低位32位 | + * Convert.ToString(int.MaxValue, 2)输出二进制 "1111111111111111111111111111111" 31个;最高位是符号位,所以少1位 + * Convert.ToString(long.MaxValue,2)输出二进制,刚好长一倍 "11111111111111111111111111111111 1111111111111111111111111111111" 63个;最高位是符号位,所以少1位 + * Convert.ToString(0xffffffffL, 2)int.MaxValue再按位多1 "1 1111111111111111111111111111111" 32个;前面的0不会打印出来 + * + * Convert.ToString(long.MaxValue>>32, 2)相当于平移高位的到低位范围,也就是上面少打印的二进制 + * 验证右移是不是高位保留,答案是 + * var a = Convert.ToInt64("101111111111111111111111111111111111111111111111111111111111111", 2); + * Convert.ToString(a >> 32,2); + * + * 解释代码: + * 0x01: + * (int)(long.MaxValue & 0xffffffffL) | (int)(long.MaxValue >> 32); + * Convert.ToString(long.MaxValue & 0xffffffffL, 2)//去掉高位:"11111111111111111111111111111111" 32个,再强转int + * 按位与&是保证符号位肯定是1,其他尽可能为0,高位被去掉只是MaxValue&0的原因,强转才是去掉高位..."尽可能"一词带来第一次随机性 + * 0x02: + * Convert.ToString((long.MaxValue >> 32), 2) //去掉低位: "1111111111111111111111111111111" 31个,再强转int + * 按位或|是尽可能为1..."尽可能"一词带来第二次随机性 + * + */ + + /// + /// 带有随机种子的随机数
    + /// 为什么这样写随机种子呢 + ///
    + /// + public static Random GetRandom() + { + var tick = DateTime.Now.Ticks; + var tickSeeds = (int)(tick & 0xffffffffL) | (int)(tick >> 32); + return new Random(tickSeeds); + } + + /// + /// 随机颜色 + /// + /// + public static System.Drawing.Color RandomColor + { + get { - //来源于:IFoxCAD.Utility类 - if (ran is null) - { - ran = GetRandom(); - } + var ran = GetRandom(); int R = ran.Next(255); int G = ran.Next(255); int B = ran.Next(255); - B = (R + G > 400) ? R + G - 400 : B;//0 : 380 - R - G; + B = (R + G > 400) ? R + G - 400 : B;// 0 : 380 - R - G; B = (B > 255) ? 255 : B; return System.Drawing.Color.FromArgb(R, G, B); } - - /// - /// 带有随机种子的随机数 - /// 为什么这样写随机种子呢 - /// - /// - public static Random GetRandom() - { - //来源于:IFoxCAD.Utility类 - var tick = DateTime.Now.Ticks; - /* - * 知识准备: - * | 高位64位 | 低位32位 | - * Convert.ToString(int.MaxValue, 2)输出二进制 "1111111111111111111111111111111" 31个;最高位是符号位,所以少1位 - * Convert.ToString(long.MaxValue,2)输出二进制,刚好长一倍 "11111111111111111111111111111111 1111111111111111111111111111111" 63个;最高位是符号位,所以少1位 - * Convert.ToString(0xffffffffL, 2)int.MaxValue再按位多1 "1 1111111111111111111111111111111" 32个;前面的0不会打印出来 - * - * Convert.ToString(long.MaxValue>>32, 2)相当于平移高位的到低位范围,也就是上面少打印的二进制 - * 验证右移是不是高位保留,答案是 - * var a = Convert.ToInt64("101111111111111111111111111111111111111111111111111111111111111", 2); - * Convert.ToString(a >> 32,2); - * - * 解释代码: - * 0x01: - * (int)(long.MaxValue & 0xffffffffL) | (int)(long.MaxValue >> 32); - * Convert.ToString(long.MaxValue & 0xffffffffL, 2)//去掉高位:"11111111111111111111111111111111" 32个,再强转int - * 按位与&是保证符号位肯定是1,其他尽可能为0,高位被去掉只是MaxValue&0的原因,强转才是去掉高位..."尽可能"一词带来第一次随机性 - * 0x02: - * Convert.ToString((long.MaxValue >> 32), 2) //去掉低位: "1111111111111111111111111111111" 31个,再强转int - * 按位或|是尽可能为1..."尽可能"一词带来第二次随机性 - * - */ - - var tickSeeds = (int)(tick & 0xffffffffL) | (int)(tick >> 32); - return new Random(tickSeeds); - } } } diff --git a/src/IFoxCAD.Cad.Shared/Algorithms/QuadTree/Utility.cs b/src/IFoxCAD.Cad.Shared/Algorithms/QuadTree/Utility.cs deleted file mode 100644 index 4c0d297..0000000 --- a/src/IFoxCAD.Cad.Shared/Algorithms/QuadTree/Utility.cs +++ /dev/null @@ -1,60 +0,0 @@ -using System; - -namespace IFoxCAD.Cad; - -public static class Utility -{ - /// - /// 带有随机种子的随机数 - /// 为什么这样写随机种子呢 - /// - /// - public static Random GetRandom() - { - var tick = DateTime.Now.Ticks; - - /* - * 知识准备: - * | 高位64位 | 低位32位 | - * Convert.ToString(int.MaxValue, 2)输出二进制 "1111111111111111111111111111111" 31个;最高位是符号位,所以少1位 - * Convert.ToString(long.MaxValue,2)输出二进制,刚好长一倍 "11111111111111111111111111111111 1111111111111111111111111111111" 63个;最高位是符号位,所以少1位 - * Convert.ToString(0xffffffffL, 2)int.MaxValue再按位多1 "1 1111111111111111111111111111111" 32个;前面的0不会打印出来 - * - * Convert.ToString(long.MaxValue>>32, 2)相当于平移高位的到低位范围,也就是上面少打印的二进制 - * 验证右移是不是高位保留,答案是 - * var a = Convert.ToInt64("101111111111111111111111111111111111111111111111111111111111111", 2); - * Convert.ToString(a >> 32,2); - * - * 解释代码: - * 0x01: - * (int)(long.MaxValue & 0xffffffffL) | (int)(long.MaxValue >> 32); - * Convert.ToString(long.MaxValue & 0xffffffffL, 2)// 去掉高位:"11111111111111111111111111111111" 32个,再强转int - * 按位与&是保证符号位肯定是1,其他尽可能为0,高位被去掉只是MaxValue&0的原因,强转才是去掉高位..."尽可能"一词带来第一次随机性 - * 0x02: - * Convert.ToString((long.MaxValue >> 32), 2) // 去掉低位: "1111111111111111111111111111111" 31个,再强转int - * 按位或|是尽可能为1..."尽可能"一词带来第二次随机性 - * - */ - - var tickSeeds = (int)(tick & 0xffffffffL) | (int)(tick >> 32); - return new Random(tickSeeds); - } - - /// - /// 随机颜色 - /// - /// - public static System.Drawing.Color RandomColor - { - get - { - var ran = GetRandom(); - int R = ran.Next(255); - int G = ran.Next(255); - int B = ran.Next(255); - B = (R + G > 400) ? R + G - 400 : B;// 0 : 380 - R - G; - B = (B > 255) ? 255 : B; - return System.Drawing.Color.FromArgb(R, G, B); - } - } -} \ No newline at end of file diff --git a/src/IFoxCAD.Cad.Shared/IFoxCAD.Cad.Shared.projitems b/src/IFoxCAD.Cad.Shared/IFoxCAD.Cad.Shared.projitems index 06e287f..f1cfeb8 100644 --- a/src/IFoxCAD.Cad.Shared/IFoxCAD.Cad.Shared.projitems +++ b/src/IFoxCAD.Cad.Shared/IFoxCAD.Cad.Shared.projitems @@ -17,7 +17,6 @@ - diff --git a/tests/TestShared/TestQuadTree.cs b/tests/TestShared/TestQuadTree.cs index 1d72ad0..3de6bdd 100644 --- a/tests/TestShared/TestQuadTree.cs +++ b/tests/TestShared/TestQuadTree.cs @@ -98,9 +98,10 @@ public void Test_QuadTree() // 四叉树数据 var entRect = new Rect(edge.MinPoint.X, edge.MinPoint.Y, edge.MaxPoint.X, edge.MaxPoint.Y); var entId = tr.CurrentSpace.AddEntity(ent); + var ce = new CadEntity(entId, entRect) { - Color = Utility.RandomColor + Color = RandomEx.RandomColor }; ces.Add(ce); /*加入随机点*/ @@ -133,7 +134,7 @@ public void Test_QuadTree() var y1 = (int)dbExt.Y; var y2 = (int)(dbExt.Y + dbExt.Height); - var rand = Utility.GetRandom(); + var rand = RandomEx.GetRandom(); for (int i = 0; i < createNumber; i++) { var x = rand.Next(x1, x2) + rand.NextDouble(); -- Gitee From b26fbc68de6e3d84439f072dea648ff4005a2f59 Mon Sep 17 00:00:00 2001 From: liuqihong <540762622@qq.com> Date: Wed, 31 Aug 2022 12:56:01 +0800 Subject: [PATCH 458/675] =?UTF-8?q?=E5=88=A0=E9=99=A4=E4=B8=80=E4=B8=AA?= =?UTF-8?q?=E9=A2=9C=E8=89=B2=E5=87=BD=E6=95=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/IFoxCAD.Basal/RandomEx.cs | 23 +---------------------- tests/TestShared/TestQuadTree.cs | 2 +- 2 files changed, 2 insertions(+), 23 deletions(-) diff --git a/src/IFoxCAD.Basal/RandomEx.cs b/src/IFoxCAD.Basal/RandomEx.cs index e07b974..a9682f7 100644 --- a/src/IFoxCAD.Basal/RandomEx.cs +++ b/src/IFoxCAD.Basal/RandomEx.cs @@ -143,7 +143,7 @@ public static int NextInt(Random ran, int min, int max) /// /// 生成一个随机颜色 /// - /// 返回一个System.Drawing.Color + /// 返回 public static System.Drawing.Color NextColor() { return NextColor(GetRandom()); @@ -164,10 +164,6 @@ public static System.Drawing.Color NextColor(Random ran) } - - - - /* * 知识准备: * | 高位64位 | 低位32位 | @@ -203,21 +199,4 @@ public static Random GetRandom() return new Random(tickSeeds); } - /// - /// 随机颜色 - /// - /// - public static System.Drawing.Color RandomColor - { - get - { - var ran = GetRandom(); - int R = ran.Next(255); - int G = ran.Next(255); - int B = ran.Next(255); - B = (R + G > 400) ? R + G - 400 : B;// 0 : 380 - R - G; - B = (B > 255) ? 255 : B; - return System.Drawing.Color.FromArgb(R, G, B); - } - } } diff --git a/tests/TestShared/TestQuadTree.cs b/tests/TestShared/TestQuadTree.cs index 3de6bdd..2047a29 100644 --- a/tests/TestShared/TestQuadTree.cs +++ b/tests/TestShared/TestQuadTree.cs @@ -101,7 +101,7 @@ public void Test_QuadTree() var ce = new CadEntity(entId, entRect) { - Color = RandomEx.RandomColor + Color = RandomEx.NextColor() }; ces.Add(ce); /*加入随机点*/ -- Gitee From 4a5442debe5f88e8217ed6f15120c634d5de1ef3 Mon Sep 17 00:00:00 2001 From: liuqihong <540762622@qq.com> Date: Wed, 31 Aug 2022 20:26:41 +0800 Subject: [PATCH 459/675] =?UTF-8?q?=E4=BF=AE=E6=94=B9=E6=96=87=E4=BB=B6?= =?UTF-8?q?=E5=90=8D=E5=B9=B6=E4=B8=94=E4=BC=98=E5=8C=96=E6=B3=A8=E9=87=8A?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../ExtensionMethod/{Jig.cs => JigEx.cs} | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) rename src/IFoxCAD.Cad.Shared/ExtensionMethod/{Jig.cs => JigEx.cs} (98%) diff --git a/src/IFoxCAD.Cad.Shared/ExtensionMethod/Jig.cs b/src/IFoxCAD.Cad.Shared/ExtensionMethod/JigEx.cs similarity index 98% rename from src/IFoxCAD.Cad.Shared/ExtensionMethod/Jig.cs rename to src/IFoxCAD.Cad.Shared/ExtensionMethod/JigEx.cs index 5b5d3d5..e47d74e 100644 --- a/src/IFoxCAD.Cad.Shared/ExtensionMethod/Jig.cs +++ b/src/IFoxCAD.Cad.Shared/ExtensionMethod/JigEx.cs @@ -62,9 +62,10 @@ private JigEx() /// 在界面绘制图元 ///
    /// - /// 用来频繁执行的回调: - /// 鼠标点; - /// 加入新建的图元,鼠标采样期间会Dispose图元的;所以已经在数据库图元利用事件加入,不要在此加入; + /// 用来频繁执行的回调:
    + /// 鼠标点;
    + /// 加入新建的图元,鼠标采样期间会Dispose图元的;
    + /// 所以已经在数据库图元利用事件加入,不要在此加入;
    /// /// 鼠标移动的容差 public JigEx(Action>? action = null, double tolerance = 1e-6) : this() -- Gitee From 436387a564cc53853dcb4c5e64ddaba95787fee4 Mon Sep 17 00:00:00 2001 From: liuqihong <540762622@qq.com> Date: Wed, 31 Aug 2022 20:35:19 +0800 Subject: [PATCH 460/675] =?UTF-8?q?=E4=BF=AE=E6=94=B9=E6=B8=85=E7=90=86?= =?UTF-8?q?=E5=8A=9F=E8=83=BD=5F=E5=BE=85=E6=B5=8B=E8=AF=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../ExtensionMethod/DBTransEx.cs | 95 +++++++++++++++++++ .../ExtensionMethod/XrefEx.cs | 65 +------------ .../IFoxCAD.Aacad09plus/GlobalUsings.cs | 3 + 3 files changed, 99 insertions(+), 64 deletions(-) create mode 100644 src/IFoxCAD.Cad.Shared/ExtensionMethod/DBTransEx.cs diff --git a/src/IFoxCAD.Cad.Shared/ExtensionMethod/DBTransEx.cs b/src/IFoxCAD.Cad.Shared/ExtensionMethod/DBTransEx.cs new file mode 100644 index 0000000..c37db66 --- /dev/null +++ b/src/IFoxCAD.Cad.Shared/ExtensionMethod/DBTransEx.cs @@ -0,0 +1,95 @@ +#define lack_test + +namespace IFoxCAD.Cad; + +#if lack_test +public static class DBTransEx +{ + + /* + * 0x01 + * db.Purge(ids)是获取未硬引用(无引用?)的对象,也就可以删除的. + * 0x02 + * 如果一个图元引用一个图层, + * 假设这个图元是可以删除的(实际上它可能来自于词典记录的id) => 那么它被 db.Purge(ids) 识别, + * 但是这个图层因为有硬引用,所以不被 db.Purge(ids) 识别, + * 只能删除图元之后,循环第二次再通过 db.Purge(ids) 获取图层id. + * 0x03 + * 因为删除之后,符号表内的引用可能被修改,因此需要重复遍历符号表. + * 0x04 + * 试试循环事务 + * 0x05 + * 无法过滤外部参照图层,使得全部图层打开了 + */ + + /// + /// 清理符号表 + /// + /// + /// + /// 排除外部参照 + public static void Purge(this DBTrans tr, + SymModes sym = SymModes.All, + bool excludeXref = true) + { + var ids = new ObjectIdCollection(); + var db = tr.Database; + + if ((sym & SymModes.BlockTable) == SymModes.BlockTable) + { + if (!excludeXref) + GetAllIds(tr, tr.BlockTable, ids, excludeXref); + else + // TODO: 此处是否需要?块表需要排除参照id? + tr.BlockTable.ForEach(tabRec => { + if (!tabRec.IsFromExternalReference) + ids.Add(tabRec.Id); + }); + } + if ((sym & SymModes.DimStyleTable) == SymModes.DimStyleTable) + GetAllIds(tr, tr.DimStyleTable, ids, excludeXref); + if ((sym & SymModes.LayerTable) == SymModes.LayerTable) + GetAllIds(tr, tr.LayerTable, ids, excludeXref); + if ((sym & SymModes.LinetypeTable) == SymModes.LinetypeTable) + GetAllIds(tr, tr.LinetypeTable, ids, excludeXref); + if ((sym & SymModes.TextStyleTable) == SymModes.TextStyleTable) + GetAllIds(tr, tr.TextStyleTable, ids, excludeXref); + if ((sym & SymModes.ViewportTable) == SymModes.ViewportTable) + GetAllIds(tr, tr.ViewportTable, ids, excludeXref); + if ((sym & SymModes.RegAppTable) == SymModes.RegAppTable) + GetAllIds(tr, tr.RegAppTable, ids, excludeXref); + + // SHUN007 说这两个表可能有错误 + if ((sym & SymModes.ViewTable) == SymModes.ViewTable) + GetAllIds(tr, tr.ViewTable, ids, excludeXref); + if ((sym & SymModes.UcsTable) == SymModes.UcsTable) + GetAllIds(tr, tr.UcsTable, ids, excludeXref); + + // Purge是查询能够清理的对象 + db.Purge(ids); + foreach (ObjectId id in ids) + id.Erase(); + } + + static void GetAllIds(DBTrans tr, + SymbolTable symbolTable, + ObjectIdCollection ids, + bool excludeXref = true) + where TTable : SymbolTable + where TRecord : SymbolTableRecord, new() + { + if (!excludeXref) + symbolTable.ForEach(id => ids.Add(id)); + else + { + symbolTable.ForEach(id => { + var tabRec = tr.GetObject(id); + if (tabRec == null) + return; + if (!tabRec.Name.Contains("|")) + ids.Add(tabRec.Id); + }); + } + } +} +#endif diff --git a/src/IFoxCAD.Cad.Shared/ExtensionMethod/XrefEx.cs b/src/IFoxCAD.Cad.Shared/ExtensionMethod/XrefEx.cs index d602c9d..b8e4f03 100644 --- a/src/IFoxCAD.Cad.Shared/ExtensionMethod/XrefEx.cs +++ b/src/IFoxCAD.Cad.Shared/ExtensionMethod/XrefEx.cs @@ -1,5 +1,4 @@ // #define error_demo -#define lack_test namespace IFoxCAD.Cad; @@ -650,66 +649,4 @@ static string GetRelativePath(string directory, string file) } #endregion } -#endregion - - -#if lack_test -public static class DBTransEx -{ - /// - /// 清理符号表 - /// - /// - /// - public static void Purge(this DBTrans tr, SymModes sym = SymModes.All) - { - var ids = new ObjectIdCollection(); - var db = tr.Database; - - // 0x01 - // db.Purge(ids)是获取未硬引用(无引用?)的对象,也就可以删除的. - // 0x02 - // 如果一个图元引用一个图层, - // 假设这个图元是可以删除的(实际上它可能来自于词典记录的id) => 那么它被 db.Purge(ids) 识别, - // 但是这个图层因为有硬引用,所以不被 db.Purge(ids) 识别, - // 只能删除图元之后,循环第二次再通过 db.Purge(ids) 获取图层id. - // 0x03 - // 因为删除之后,符号表内的引用可能被修改,因此需要重复遍历符号表. - // 0x04 - // 试试循环事务 - - if ((sym & SymModes.BlockTable) == SymModes.BlockTable) - GetIds(ids, tr.BlockTable); - if ((sym & SymModes.DimStyleTable) == SymModes.DimStyleTable) - GetIds(ids, tr.DimStyleTable); - if ((sym & SymModes.LayerTable) == SymModes.LayerTable) - GetIds(ids, tr.LayerTable); - if ((sym & SymModes.LinetypeTable) == SymModes.LinetypeTable) - GetIds(ids, tr.LinetypeTable); - if ((sym & SymModes.TextStyleTable) == SymModes.TextStyleTable) - GetIds(ids, tr.TextStyleTable); - if ((sym & SymModes.ViewportTable) == SymModes.ViewportTable) - GetIds(ids, tr.ViewportTable); - if ((sym & SymModes.RegAppTable) == SymModes.RegAppTable) - GetIds(ids, tr.RegAppTable); - // SHUN007 说这两个表可能有错误 - if ((sym & SymModes.ViewTable) == SymModes.ViewTable) - GetIds(ids, tr.ViewTable); - if ((sym & SymModes.UcsTable) == SymModes.UcsTable) - GetIds(ids, tr.UcsTable); - - // Purge是查询能够清理的对象 - db.Purge(ids); - foreach (ObjectId id in ids) - id.Erase(); - } - - static void GetIds(ObjectIdCollection ids, - SymbolTable symbolTable) - where TTable : SymbolTable - where TRecord : SymbolTableRecord, new() - { - symbolTable.ForEach(id => ids.Add(id)); - } -} -#endif \ No newline at end of file +#endregion \ No newline at end of file diff --git a/src/IFoxCAD.Cad/IFoxCAD.Aacad09plus/GlobalUsings.cs b/src/IFoxCAD.Cad/IFoxCAD.Aacad09plus/GlobalUsings.cs index 9ea1ec2..60ba3eb 100644 --- a/src/IFoxCAD.Cad/IFoxCAD.Aacad09plus/GlobalUsings.cs +++ b/src/IFoxCAD.Cad/IFoxCAD.Aacad09plus/GlobalUsings.cs @@ -36,5 +36,8 @@ global using Registry = Microsoft.Win32.Registry; global using RegistryKey = Microsoft.Win32.RegistryKey; +global using Autodesk.AutoCAD.GraphicsInterface; +global using Polyline = Autodesk.AutoCAD.DatabaseServices.Polyline; + /// ifoxcad.basal 引用 global using IFoxCAD.Basal; \ No newline at end of file -- Gitee From 7baeb9cbb1678345bae16dd54501a20d45ecf689 Mon Sep 17 00:00:00 2001 From: liuqihong <540762622@qq.com> Date: Wed, 31 Aug 2022 21:47:48 +0800 Subject: [PATCH 461/675] =?UTF-8?q?=E4=BC=98=E5=8C=96=E5=90=8E=E5=8F=B0?= =?UTF-8?q?=E4=BF=9D=E5=AD=98=E8=B7=AF=E5=BE=84=E4=B8=8D=E5=AD=98=E5=9C=A8?= =?UTF-8?q?=E6=97=B6=E5=80=99=E4=BF=9D=E5=AD=98=E5=88=B0=E6=A1=8C=E9=9D=A2?= =?UTF-8?q?=E5=B9=B6=E6=8F=90=E4=BE=9B=E6=B5=8B=E8=AF=95=E5=91=BD=E4=BB=A4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../IFoxCAD.Cad.Shared.projitems | 3 ++- src/IFoxCAD.Cad.Shared/Runtime/DBTrans.cs | 24 +++++++++++++------ tests/TestShared/TestDBTrans.cs | 12 +++++++++- 3 files changed, 30 insertions(+), 9 deletions(-) diff --git a/src/IFoxCAD.Cad.Shared/IFoxCAD.Cad.Shared.projitems b/src/IFoxCAD.Cad.Shared/IFoxCAD.Cad.Shared.projitems index f1cfeb8..b883721 100644 --- a/src/IFoxCAD.Cad.Shared/IFoxCAD.Cad.Shared.projitems +++ b/src/IFoxCAD.Cad.Shared/IFoxCAD.Cad.Shared.projitems @@ -17,6 +17,7 @@ + @@ -30,7 +31,7 @@ - + diff --git a/src/IFoxCAD.Cad.Shared/Runtime/DBTrans.cs b/src/IFoxCAD.Cad.Shared/Runtime/DBTrans.cs index 1109832..4809dc7 100644 --- a/src/IFoxCAD.Cad.Shared/Runtime/DBTrans.cs +++ b/src/IFoxCAD.Cad.Shared/Runtime/DBTrans.cs @@ -1,6 +1,7 @@ namespace IFoxCAD.Cad; using System.Diagnostics; +using System.IO; /// /// 事务栈 @@ -381,22 +382,31 @@ public void SaveDwgFile(DwgVersion version = DwgVersion.AC1800) break; } } - if (doca == null) // 后台开图,用数据库保存 + if (doca == null) { - // 用了不存在的文件进行后台打开,并且设置保存,这个时候就软处理 - if (string.IsNullOrEmpty(Database.Filename)) + // 后台开图,用数据库保存 + if (!string.IsNullOrEmpty(Database.Filename)) { - Debug.WriteLine("**** 此数据没有保存路径,无法保存!"); + Database.SaveAs(Database.Filename, version); return; } - Database.SaveAs(Database.Filename, version); + + /// 构造函数(fileName)用了不存在的路径进行后台打开,就会出现此问题 + /// 测试命令 FileNotExist + Debug.WriteLine("**** 此文件路径不存在,无法保存!将自动保存到桌面中."); + string dir = Environment.GetFolderPath( + Environment.SpecialFolder.DesktopDirectory) + "\\路径不存在进行临时保存\\"; + if (!Directory.Exists(dir)) + Directory.CreateDirectory(dir); + string file = DateTime.Now.ToString("yyyy-MM-dd--h-mm-ss-ffff"); + Database.SaveAs(dir + file + ".dwg", version); } else // 前台开图,使用命令保存;不需要切换文档 doca.SendStringToExecute("_qsave\n", false, true, true); } #endregion - + #region 前台后台任务 /// /// 前台后台任务分别处理 /// @@ -439,7 +449,7 @@ public void Task(Action action, bool handlingDBTextDeviation = true) action.Invoke(); HostApplicationServices.WorkingDatabase = dbBak; } - + #endregion #region IDisposable接口相关函数 /// diff --git a/tests/TestShared/TestDBTrans.cs b/tests/TestShared/TestDBTrans.cs index cd9e670..6aa1c77 100644 --- a/tests/TestShared/TestDBTrans.cs +++ b/tests/TestShared/TestDBTrans.cs @@ -1,7 +1,17 @@ -namespace Test; +using System.Security.Cryptography; + +namespace Test; public class TestTrans { + [CommandMethod(nameof(FileNotExist))] + public void FileNotExist() + { + using var tr = new DBTrans("test.dwg"); + tr.SaveDwgFile();//不存在的dwg会在桌面进行临时保存 + } + + [CommandMethod("testtr")] public void Testtr() { -- Gitee From b9ae3e63a70cc6935176aab338155fd4a0bec8b5 Mon Sep 17 00:00:00 2001 From: liuqihong <540762622@qq.com> Date: Wed, 31 Aug 2022 23:41:11 +0800 Subject: [PATCH 462/675] =?UTF-8?q?=E5=A2=9E=E5=8A=A0=E8=AF=BB=E5=8F=96cad?= =?UTF-8?q?=E7=8E=AF=E5=A2=83=E5=8F=98=E9=87=8F=E5=92=8C=E8=AE=BE=E7=BD=AE?= =?UTF-8?q?=E9=BB=98=E8=AE=A4=E4=BF=9D=E5=AD=98=E6=96=B9=E5=BC=8F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/IFoxCAD.Cad.Shared/Runtime/DBTrans.cs | 31 +++++++++++++- src/IFoxCAD.Cad.Shared/Runtime/Env.cs | 50 +++++++++++++++++++++++ tests/TestShared/TestDBTrans.cs | 4 +- 3 files changed, 80 insertions(+), 5 deletions(-) diff --git a/src/IFoxCAD.Cad.Shared/Runtime/DBTrans.cs b/src/IFoxCAD.Cad.Shared/Runtime/DBTrans.cs index 4809dc7..4f3caeb 100644 --- a/src/IFoxCAD.Cad.Shared/Runtime/DBTrans.cs +++ b/src/IFoxCAD.Cad.Shared/Runtime/DBTrans.cs @@ -371,8 +371,34 @@ public ObjectId GetObjectId(string handleString) /// 保存当前数据库的dwg文件,如果前台打开则按dwg默认版本保存,否则按version参数的版本保存 /// /// dwg版本,默认为2004 - public void SaveDwgFile(DwgVersion version = DwgVersion.AC1800) + /// 为true时候无效,将读取当前cad环境配置的版本 + public void SaveDwgFile(DwgVersion version = DwgVersion.AC1800, + bool IsDefaultFormatForSave = true) { + if (IsDefaultFormatForSave) + { + var ffs = Env.AcedGetEnv("DefaultFormatForSave"); + version = ffs switch + { + "1" => DwgVersion.AC1009,// r12/LT12 dxf + "8" => DwgVersion.AC1014,// r14/LT98/LT97 dwg + "12" => DwgVersion.AC1015,// 2000 dwg + "13" => DwgVersion.AC1800a,// 2000 dxf + "24" => DwgVersion.AC1800,// 2004 dwg + "25" => (DwgVersion)26,// 2004 dxf + "36" => (DwgVersion)27,// 2007 dwg DwgVersion.AC1021 + "37" => (DwgVersion)28,// 2007 dxf + //"38" => (DwgVersion),// dwt 样板文件...啊惊没找到这个是什么 + "48" => (DwgVersion)29,// 2010 dwg DwgVersion.AC1024 + "49" => (DwgVersion)30,// 2010 dxf + "60" => (DwgVersion)31,// 2013 dwg DwgVersion.AC1027 + "61" => (DwgVersion)32,// 2013 dxf + "64" => (DwgVersion)33,// 2018 dwg DwgVersion.AC1032 + "65" => (DwgVersion)34,// 2018 dxf + _ => (DwgVersion)25, + }; + } + Document? doca = null; foreach (Document doc in Acap.DocumentManager) { @@ -382,7 +408,7 @@ public void SaveDwgFile(DwgVersion version = DwgVersion.AC1800) break; } } - if (doca == null) + if (doca == null) { // 后台开图,用数据库保存 if (!string.IsNullOrEmpty(Database.Filename)) @@ -400,6 +426,7 @@ public void SaveDwgFile(DwgVersion version = DwgVersion.AC1800) Directory.CreateDirectory(dir); string file = DateTime.Now.ToString("yyyy-MM-dd--h-mm-ss-ffff"); Database.SaveAs(dir + file + ".dwg", version); + } else // 前台开图,使用命令保存;不需要切换文档 doca.SendStringToExecute("_qsave\n", false, true, true); diff --git a/src/IFoxCAD.Cad.Shared/Runtime/Env.cs b/src/IFoxCAD.Cad.Shared/Runtime/Env.cs index 6053962..957269d 100644 --- a/src/IFoxCAD.Cad.Shared/Runtime/Env.cs +++ b/src/IFoxCAD.Cad.Shared/Runtime/Env.cs @@ -456,6 +456,56 @@ public static void SetVar(string varName, object value) Env.Print($"{varName} 是不存在的变量!"); } } + +#if NET35 + [System.Security.SuppressUnmanagedCodeSecurity] + [DllImport("acad.exe", CharSet = CharSet.Auto, CallingConvention = CallingConvention.Cdecl, EntryPoint = "acedGetEnv")] + static extern int AcedGetEnv(string envName, StringBuilder ReturnValue); + + [System.Security.SuppressUnmanagedCodeSecurity] + [DllImport("acad.exe", CharSet = CharSet.Auto, CallingConvention = CallingConvention.Cdecl, EntryPoint = "acedSetEnv")] + static extern int AcedSetEnv(string envName, StringBuilder NewValue); +#elif !HC2020 + [System.Security.SuppressUnmanagedCodeSecurity] + [DllImport("accore.dll", CharSet = CharSet.Auto, CallingConvention = CallingConvention.Cdecl, EntryPoint = "acedGetEnv")] + static extern int AcedGetEnv(string envName, StringBuilder ReturnValue); + + [System.Security.SuppressUnmanagedCodeSecurity] + [DllImport("accore.dll", CharSet = CharSet.Auto, CallingConvention = CallingConvention.Cdecl, EntryPoint = "acedSetEnv")] + static extern int AcedSetEnv(string envName, StringBuilder NewValue); +#endif + +#if HC2020 + [System.Security.SuppressUnmanagedCodeSecurity] + [DllImport("gced.dll", CharSet = CharSet.Auto, CallingConvention = CallingConvention.Cdecl, EntryPoint = "gcedGetEnv")] + static extern int AcedGetEnv(string envName, StringBuilder ReturnValue); + + [System.Security.SuppressUnmanagedCodeSecurity] + [DllImport("gced.dll", CharSet = CharSet.Auto, CallingConvention = CallingConvention.Cdecl, EntryPoint = "gcedSetEnv")] + static extern int AcedSetEnv(string envName, StringBuilder NewValue); +#endif + + /// + /// 设置环境变量 + /// + public static string AcedGetEnv(string name) + { + var sbRes = new StringBuilder(1024); + _ = AcedGetEnv(name, sbRes); + return sbRes.ToString(); + } + + /// + /// 设置环境变量 + /// + /// lisp的名称 + /// 要设置的值 + /// 成功标识 + public static int AcedSetEnv(string name, string var) + { + return AcedSetEnv(name, new StringBuilder(var)); + } + /// /// 获取系统环境变量 /// diff --git a/tests/TestShared/TestDBTrans.cs b/tests/TestShared/TestDBTrans.cs index 6aa1c77..5a42582 100644 --- a/tests/TestShared/TestDBTrans.cs +++ b/tests/TestShared/TestDBTrans.cs @@ -1,6 +1,4 @@ -using System.Security.Cryptography; - -namespace Test; +namespace Test; public class TestTrans { -- Gitee From 43495afa6637b252dc8d2e89462ef498e3817807 Mon Sep 17 00:00:00 2001 From: liuqihong <540762622@qq.com> Date: Thu, 1 Sep 2022 00:18:31 +0800 Subject: [PATCH 463/675] =?UTF-8?q?=E9=98=B2=E6=AD=A2=E6=96=87=E4=BB=B6?= =?UTF-8?q?=E5=AD=98=E5=9C=A8=E5=86=B2=E7=AA=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/IFoxCAD.Cad.Shared/Runtime/DBTrans.cs | 68 +++++++++++------------ src/IFoxCAD.Cad.Shared/Runtime/Env.cs | 30 ++++++++++ 2 files changed, 62 insertions(+), 36 deletions(-) diff --git a/src/IFoxCAD.Cad.Shared/Runtime/DBTrans.cs b/src/IFoxCAD.Cad.Shared/Runtime/DBTrans.cs index 4f3caeb..dd7a8f2 100644 --- a/src/IFoxCAD.Cad.Shared/Runtime/DBTrans.cs +++ b/src/IFoxCAD.Cad.Shared/Runtime/DBTrans.cs @@ -2,6 +2,7 @@ using System.Diagnostics; using System.IO; +using System.Threading; /// /// 事务栈 @@ -368,36 +369,17 @@ public ObjectId GetObjectId(string handleString) #region 保存文件 /// - /// 保存当前数据库的dwg文件,如果前台打开则按dwg默认版本保存,否则按version参数的版本保存 + /// 保存当前数据库的dwg文件
    + /// 前台按dwg默认版本保存(环境变量),
    + /// 后台按参数保存,同时受影响 ///
    - /// dwg版本,默认为2004 - /// 为true时候无效,将读取当前cad环境配置的版本 + /// 默认2004dwg: + /// 为true时候无效,将自动识别环境变量 public void SaveDwgFile(DwgVersion version = DwgVersion.AC1800, - bool IsDefaultFormatForSave = true) + bool automatic = true) { - if (IsDefaultFormatForSave) - { - var ffs = Env.AcedGetEnv("DefaultFormatForSave"); - version = ffs switch - { - "1" => DwgVersion.AC1009,// r12/LT12 dxf - "8" => DwgVersion.AC1014,// r14/LT98/LT97 dwg - "12" => DwgVersion.AC1015,// 2000 dwg - "13" => DwgVersion.AC1800a,// 2000 dxf - "24" => DwgVersion.AC1800,// 2004 dwg - "25" => (DwgVersion)26,// 2004 dxf - "36" => (DwgVersion)27,// 2007 dwg DwgVersion.AC1021 - "37" => (DwgVersion)28,// 2007 dxf - //"38" => (DwgVersion),// dwt 样板文件...啊惊没找到这个是什么 - "48" => (DwgVersion)29,// 2010 dwg DwgVersion.AC1024 - "49" => (DwgVersion)30,// 2010 dxf - "60" => (DwgVersion)31,// 2013 dwg DwgVersion.AC1027 - "61" => (DwgVersion)32,// 2013 dxf - "64" => (DwgVersion)33,// 2018 dwg DwgVersion.AC1032 - "65" => (DwgVersion)34,// 2018 dxf - _ => (DwgVersion)25, - }; - } + if (automatic) + version = Env.GetDefaultFormatForSaveToDwgVersion(); Document? doca = null; foreach (Document doc in Acap.DocumentManager) @@ -417,19 +399,33 @@ public void SaveDwgFile(DwgVersion version = DwgVersion.AC1800, return; } - /// 构造函数(fileName)用了不存在的路径进行后台打开,就会出现此问题 - /// 测试命令 FileNotExist - Debug.WriteLine("**** 此文件路径不存在,无法保存!将自动保存到桌面中."); - string dir = Environment.GetFolderPath( - Environment.SpecialFolder.DesktopDirectory) + "\\路径不存在进行临时保存\\"; + // 构造函数(fileName)用了不存在的路径进行后台打开,就会出现此问题 + // 测试命令 FileNotExist + var dir = Environment.GetFolderPath( + Environment.SpecialFolder.DesktopDirectory) + "\\路径不存在进行临时保存\\"; if (!Directory.Exists(dir)) Directory.CreateDirectory(dir); - string file = DateTime.Now.ToString("yyyy-MM-dd--h-mm-ss-ffff"); - Database.SaveAs(dir + file + ".dwg", version); - + string file; + do + { + var time = DateTime.Now.ToString("yyyy-MM-dd--h-mm-ss-ffff"); + file = dir + time + ".dwg"; + Thread.Sleep(100); + } while (File.Exists(file)); + Database.SaveAs(file, version); + + // 防止前台关闭了所有文档导致没有Editor,所以使用 MessageBox 发送警告 + System.Windows.Forms.MessageBox.Show( + $"**** 后台保存图纸出错," + + $"此文件路径不存在,无法保存!" + + $"将自动保存到桌面中." + + $"\n\n路径为{file}", nameof(DBTrans)); } - else // 前台开图,使用命令保存;不需要切换文档 + else + { + // 前台开图,使用命令保存;不需要切换文档 doca.SendStringToExecute("_qsave\n", false, true, true); + } } #endregion diff --git a/src/IFoxCAD.Cad.Shared/Runtime/Env.cs b/src/IFoxCAD.Cad.Shared/Runtime/Env.cs index 957269d..77092fe 100644 --- a/src/IFoxCAD.Cad.Shared/Runtime/Env.cs +++ b/src/IFoxCAD.Cad.Shared/Runtime/Env.cs @@ -539,4 +539,34 @@ public static void SetEnv(string var, string? value) ///
    /// Bool public static bool IsUcs() => (short)GetVar("WORLDUCS") == 0; + + /// + /// 获取当前配置文件的保存版本 + /// + /// + public static DwgVersion GetDefaultFormatForSaveToDwgVersion() + { + DwgVersion version; + var ffs = Env.AcedGetEnv("DefaultFormatForSave"); + version = ffs switch + { + "1" => DwgVersion.AC1009,// r12/LT12 dxf + "8" => DwgVersion.AC1014,// r14/LT98/LT97 dwg + "12" => DwgVersion.AC1015,// 2000 dwg + "13" => DwgVersion.AC1800a,// 2000 dxf + "24" => DwgVersion.AC1800,// 2004 dwg + "25" => (DwgVersion)26,// 2004 dxf + "36" => (DwgVersion)27,// 2007 dwg DwgVersion.AC1021 + "37" => (DwgVersion)28,// 2007 dxf + //"38" => (DwgVersion),// dwt 样板文件...啊惊没找到这个是什么 + "48" => (DwgVersion)29,// 2010 dwg DwgVersion.AC1024 + "49" => (DwgVersion)30,// 2010 dxf + "60" => (DwgVersion)31,// 2013 dwg DwgVersion.AC1027 + "61" => (DwgVersion)32,// 2013 dxf + "64" => (DwgVersion)33,// 2018 dwg DwgVersion.AC1032 + "65" => (DwgVersion)34,// 2018 dxf + _ => (DwgVersion)25, + }; + return version; + } } -- Gitee From 6d83906a8635fef3b882abe6e047d9a001e0e31f Mon Sep 17 00:00:00 2001 From: liuqihong <540762622@qq.com> Date: Thu, 1 Sep 2022 02:16:44 +0800 Subject: [PATCH 464/675] =?UTF-8?q?=E5=AE=8C=E5=96=84=E4=BF=9D=E5=AD=98?= =?UTF-8?q?=E5=87=BD=E6=95=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../ExtensionMethod/DBTransEx.cs | 7 +- src/IFoxCAD.Cad.Shared/Runtime/DBTrans.cs | 76 ++++++++++++------- src/IFoxCAD.Cad.Shared/Runtime/Env.cs | 21 +++++ tests/TestShared/TestDBTrans.cs | 8 +- 4 files changed, 78 insertions(+), 34 deletions(-) diff --git a/src/IFoxCAD.Cad.Shared/ExtensionMethod/DBTransEx.cs b/src/IFoxCAD.Cad.Shared/ExtensionMethod/DBTransEx.cs index c37db66..d238252 100644 --- a/src/IFoxCAD.Cad.Shared/ExtensionMethod/DBTransEx.cs +++ b/src/IFoxCAD.Cad.Shared/ExtensionMethod/DBTransEx.cs @@ -27,10 +27,8 @@ public static class DBTransEx ///
    /// /// - /// 排除外部参照 - public static void Purge(this DBTrans tr, - SymModes sym = SymModes.All, - bool excludeXref = true) + /// 排除外部参照:默认true,为false时候会令图层全部显示再清理,包括冻结 + public static void Purge(this DBTrans tr, SymModes sym = SymModes.All, bool excludeXref = true) { var ids = new ObjectIdCollection(); var db = tr.Database; @@ -40,7 +38,6 @@ public static void Purge(this DBTrans tr, if (!excludeXref) GetAllIds(tr, tr.BlockTable, ids, excludeXref); else - // TODO: 此处是否需要?块表需要排除参照id? tr.BlockTable.ForEach(tabRec => { if (!tabRec.IsFromExternalReference) ids.Add(tabRec.Id); diff --git a/src/IFoxCAD.Cad.Shared/Runtime/DBTrans.cs b/src/IFoxCAD.Cad.Shared/Runtime/DBTrans.cs index dd7a8f2..f36262b 100644 --- a/src/IFoxCAD.Cad.Shared/Runtime/DBTrans.cs +++ b/src/IFoxCAD.Cad.Shared/Runtime/DBTrans.cs @@ -27,6 +27,10 @@ public class DBTrans : IDisposable /// 事务栈 /// private static readonly Stack dBTrans = new(); + /// + /// 文件名 + /// + private readonly string? _fileFullName; #endregion #region 公开属性 @@ -133,15 +137,15 @@ public DBTrans(string fileName, if (fileName == null || string.IsNullOrEmpty(fileName.Trim())) throw new ArgumentNullException(nameof(fileName)); - fileName = fileName.Replace("/", "\\");//// doc.Name总是"D:\\JX.dwg" + _fileFullName = fileName.Replace("/", "\\");//// doc.Name总是"D:\\JX.dwg" - if (!File.Exists(fileName)) + if (!File.Exists(_fileFullName)) Database = new Database(true, false); else { var doc = Acap.DocumentManager .Cast() - .FirstOrDefault(doc => doc.Name == fileName); + .FirstOrDefault(doc => doc.Name == _fileFullName); if (doc is not null) { Database = doc.Database; @@ -151,8 +155,8 @@ public DBTrans(string fileName, else { Database = new Database(false, true); - if (Path.GetExtension(fileName).ToLower().Contains("dxf")) - Database.DxfIn(fileName, null); + if (Path.GetExtension(_fileFullName).ToLower().Contains("dxf")) + Database.DxfIn(_fileFullName, null); else { #if ac2008 @@ -181,14 +185,14 @@ public DBTrans(string fileName, } // 这个会致命错误 - // using FileStream fileStream = new(fileName, FileMode.Open, fileAccess, fileShare); + // using FileStream fileStream = new(_fileFullName, FileMode.Open, fileAccess, fileShare); // Database.ReadDwgFile(fileStream.SafeFileHandle.DangerousGetHandle(), // true/*控制读入一个与系统编码不相同的文件时的转换操作*/,password); - Database.ReadDwgFile(fileName, fileShare, + Database.ReadDwgFile(_fileFullName, fileShare, true/*控制读入一个与系统编码不相同的文件时的转换操作*/, password); #else - Database.ReadDwgFile(fileName, openMode, + Database.ReadDwgFile(_fileFullName, openMode, true/*控制读入一个与系统编码不相同的文件时的转换操作*/, password); #endif } @@ -378,9 +382,6 @@ public ObjectId GetObjectId(string handleString) public void SaveDwgFile(DwgVersion version = DwgVersion.AC1800, bool automatic = true) { - if (automatic) - version = Env.GetDefaultFormatForSaveToDwgVersion(); - Document? doca = null; foreach (Document doc in Acap.DocumentManager) { @@ -390,29 +391,41 @@ public void SaveDwgFile(DwgVersion version = DwgVersion.AC1800, break; } } - if (doca == null) + if (doca != null) { - // 后台开图,用数据库保存 - if (!string.IsNullOrEmpty(Database.Filename)) - { - Database.SaveAs(Database.Filename, version); - return; - } + // 前台开图,使用命令保存;不需要切换文档 + doca.SendStringToExecute("_qsave\n", false, true, true); + return; + } + + // 后台开图,用数据库保存 + string file = Database.Filename; + if (automatic) + version = Env.GetDefaultFormatForSaveToDwgVersion(); + + var isDxf = false; + if (string.IsNullOrEmpty(file)) + { + var fileName = Path.GetFileNameWithoutExtension(_fileFullName); + var fileExt = Path.GetExtension(_fileFullName); + isDxf = fileExt.ToLower().Contains("dxf"); // 构造函数(fileName)用了不存在的路径进行后台打开,就会出现此问题 // 测试命令 FileNotExist var dir = Environment.GetFolderPath( - Environment.SpecialFolder.DesktopDirectory) + "\\路径不存在进行临时保存\\"; + Environment.SpecialFolder.DesktopDirectory) + + "\\后台保存出错进行临时保存\\"; + if (!Directory.Exists(dir)) Directory.CreateDirectory(dir); - string file; - do + + file = dir + fileName + fileExt; + while (File.Exists(file)) { - var time = DateTime.Now.ToString("yyyy-MM-dd--h-mm-ss-ffff"); - file = dir + time + ".dwg"; + var time = DateTime.Now.ToString("--yyMMdd--hhmmssffff"); + file = dir + fileName + time + fileExt; Thread.Sleep(100); - } while (File.Exists(file)); - Database.SaveAs(file, version); + } // 防止前台关闭了所有文档导致没有Editor,所以使用 MessageBox 发送警告 System.Windows.Forms.MessageBox.Show( @@ -421,10 +434,19 @@ public void SaveDwgFile(DwgVersion version = DwgVersion.AC1800, $"将自动保存到桌面中." + $"\n\n路径为{file}", nameof(DBTrans)); } + + if (isDxf) + { + // dxf用任何版本号都会报错 + Database.DxfOut(file, 7, true); + } else { - // 前台开图,使用命令保存;不需要切换文档 - doca.SendStringToExecute("_qsave\n", false, true, true); + // dwg需要版本号,而dxf不用,dwg用dxf版本号会报错 + // 若扩展名和版本号冲突,按照扩展名为准 + if (version.IsDxfVersion()) + version = DwgVersion.Current; + Database.SaveAs(file, version); } } #endregion diff --git a/src/IFoxCAD.Cad.Shared/Runtime/Env.cs b/src/IFoxCAD.Cad.Shared/Runtime/Env.cs index 77092fe..db74805 100644 --- a/src/IFoxCAD.Cad.Shared/Runtime/Env.cs +++ b/src/IFoxCAD.Cad.Shared/Runtime/Env.cs @@ -569,4 +569,25 @@ public static DwgVersion GetDefaultFormatForSaveToDwgVersion() }; return version; } + + /// + /// 是否为dxf版本号 + /// + /// + /// + public static bool IsDxfVersion(this DwgVersion dwgVersion) + { + var result = (int)dwgVersion switch + { + 16 => true,// r12/LT12 dxf + 24 => true,// 2000 dxf + 26 => true,// 2004 dxf + 28 => true,// 2007 dxf + 30 => true,// 2010 dxf + 32 => true,// 2013 dxf + 34 => true,// 2018 dxf + _ => false, + }; + return result; + } } diff --git a/tests/TestShared/TestDBTrans.cs b/tests/TestShared/TestDBTrans.cs index 5a42582..b144788 100644 --- a/tests/TestShared/TestDBTrans.cs +++ b/tests/TestShared/TestDBTrans.cs @@ -5,8 +5,12 @@ public class TestTrans [CommandMethod(nameof(FileNotExist))] public void FileNotExist() { + //不存在的dwg会在桌面进行临时保存 using var tr = new DBTrans("test.dwg"); - tr.SaveDwgFile();//不存在的dwg会在桌面进行临时保存 + //tr.SaveDwgFile(); + + //测试保存为dxf 2007 dxf + tr.SaveDwgFile((DwgVersion)24, false); } @@ -22,7 +26,7 @@ public void Testtr() [CommandMethod("testifoxcommit")] public void Testifoxcommit() { - + using var tr = new DBTrans(); tr.ModelSpace.AddCircle(new Point3d(0, 0, 0), 20); tr.Abort(); -- Gitee From 5fa4913047e628973ef881c046b1c990cc4326f9 Mon Sep 17 00:00:00 2001 From: liuqihong <540762622@qq.com> Date: Thu, 1 Sep 2022 04:16:58 +0800 Subject: [PATCH 465/675] =?UTF-8?q?=E5=A2=9E=E5=8A=A0=E6=B5=8B=E8=AF=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/IFoxCAD.Cad.Shared/Runtime/DBTrans.cs | 121 +++++++++++++++------- tests/TestShared/TestDBTrans.cs | 24 ++++- 2 files changed, 105 insertions(+), 40 deletions(-) diff --git a/src/IFoxCAD.Cad.Shared/Runtime/DBTrans.cs b/src/IFoxCAD.Cad.Shared/Runtime/DBTrans.cs index f36262b..aeba572 100644 --- a/src/IFoxCAD.Cad.Shared/Runtime/DBTrans.cs +++ b/src/IFoxCAD.Cad.Shared/Runtime/DBTrans.cs @@ -3,6 +3,7 @@ using System.Diagnostics; using System.IO; using System.Threading; +using System.Windows.Forms; /// /// 事务栈 @@ -379,8 +380,12 @@ public ObjectId GetObjectId(string handleString) /// /// 默认2004dwg: /// 为true时候无效,将自动识别环境变量 + /// 另存为路径,前台将调用另存为面板,此时它将无效 + /// 保存失败的提示 public void SaveDwgFile(DwgVersion version = DwgVersion.AC1800, - bool automatic = true) + bool automatic = true, + string? saveAsName = null, + bool echoes = true) { Document? doca = null; foreach (Document doc in Acap.DocumentManager) @@ -391,64 +396,108 @@ public void SaveDwgFile(DwgVersion version = DwgVersion.AC1800, break; } } + + // 前台开图,使用命令保存;不需要切换文档 if (doca != null) { - // 前台开图,使用命令保存;不需要切换文档 - doca.SendStringToExecute("_qsave\n", false, true, true); + if (saveAsName == null) + doca.SendStringToExecute("_qsave\n", false, true, true); + else + /// 无法把 给这个面板 + doca.SendStringToExecute($"_Saveas\n", false, true, true); return; } + // 后台开图,用数据库保存 - string file = Database.Filename; - if (automatic) - version = Env.GetDefaultFormatForSaveToDwgVersion(); - - var isDxf = false; - if (string.IsNullOrEmpty(file)) + string? fileMsg = _fileFullName; + bool flag = false; + if (saveAsName == null) { - var fileName = Path.GetFileNameWithoutExtension(_fileFullName); - var fileExt = Path.GetExtension(_fileFullName); - isDxf = fileExt.ToLower().Contains("dxf"); - - // 构造函数(fileName)用了不存在的路径进行后台打开,就会出现此问题 - // 测试命令 FileNotExist - var dir = Environment.GetFolderPath( - Environment.SpecialFolder.DesktopDirectory) - + "\\后台保存出错进行临时保存\\"; - - if (!Directory.Exists(dir)) - Directory.CreateDirectory(dir); - - file = dir + fileName + fileExt; - while (File.Exists(file)) + flag = true; + } + else + { + // 路径失败也保存到桌面 + // 没有文件名呢?那就报错,保存到系统目录也会报错,只能尽人事 + var path = Path.GetDirectoryName(saveAsName); + if (string.IsNullOrEmpty(path)) + flag = true; + else { - var time = DateTime.Now.ToString("--yyMMdd--hhmmssffff"); - file = dir + fileName + time + fileExt; - Thread.Sleep(100); + try + { + if (!Directory.Exists(path)) + Directory.CreateDirectory(path); + } + catch { flag = true; } } + } + if (string.IsNullOrEmpty(Path.GetFileNameWithoutExtension(saveAsName))) + flag = true; - // 防止前台关闭了所有文档导致没有Editor,所以使用 MessageBox 发送警告 - System.Windows.Forms.MessageBox.Show( - $"**** 后台保存图纸出错," + - $"此文件路径不存在,无法保存!" + - $"将自动保存到桌面中." + - $"\n\n路径为{file}", nameof(DBTrans)); + if (flag) + { + var (error, file2) = GetSaveAsName(); + if (echoes && error) + MessageBox.Show($"错误参数:\n{fileMsg}\n\n它将保存:\n{file2}", "错误的文件路径"); + saveAsName = file2; } - if (isDxf) + if (Path.GetExtension(saveAsName).ToLower().Contains("dxf")) { // dxf用任何版本号都会报错 - Database.DxfOut(file, 7, true); + Database.DxfOut(saveAsName, 7, true); } else { + if (automatic) + version = Env.GetDefaultFormatForSaveToDwgVersion(); + // dwg需要版本号,而dxf不用,dwg用dxf版本号会报错 // 若扩展名和版本号冲突,按照扩展名为准 if (version.IsDxfVersion()) version = DwgVersion.Current; - Database.SaveAs(file, version); + Database.SaveAs(saveAsName, version); + } + } + + (bool error, string path) GetSaveAsName() + { + var file = Database.Filename; + if (!string.IsNullOrEmpty(file)) + return (false, file); + + // 因为用户的任务可能占用时间非常长,所以这里提示保存到桌面, + // 而不是弹出警告就结束 + // 防止前台关闭了所有文档导致没有Editor,所以使用 MessageBox 发送警告 + var fileName = Path.GetFileNameWithoutExtension(_fileFullName); + var fileExt = Path.GetExtension(_fileFullName); + + if (fileName == string.Empty) + fileName = DateTime.Now.ToString("--yyMMdd--hhmmssffff"); + if (fileExt == string.Empty) + fileExt = ".dwg"; + + // 构造函数(fileName)用了不存在的路径进行后台打开,就会出现此问题 + // 测试命令 FileNotExist + var dir = Environment.GetFolderPath( + Environment.SpecialFolder.DesktopDirectory) + + "\\后台保存出错的文件\\"; + + if (!Directory.Exists(dir)) + Directory.CreateDirectory(dir); + + file = dir + fileName + fileExt; + while (File.Exists(file)) + { + var time = DateTime.Now.ToString("--yyMMdd--hhmmssffff"); + file = dir + fileName + time + fileExt; + Thread.Sleep(100); } + return (true, file); } + #endregion #region 前台后台任务 diff --git a/tests/TestShared/TestDBTrans.cs b/tests/TestShared/TestDBTrans.cs index b144788..8882dad 100644 --- a/tests/TestShared/TestDBTrans.cs +++ b/tests/TestShared/TestDBTrans.cs @@ -2,17 +2,33 @@ public class TestTrans { + // 后台:不存在路径的dwg会在桌面进行临时保存 [CommandMethod(nameof(FileNotExist))] public void FileNotExist() { - //不存在的dwg会在桌面进行临时保存 using var tr = new DBTrans("test.dwg"); - //tr.SaveDwgFile(); - - //测试保存为dxf 2007 dxf tr.SaveDwgFile((DwgVersion)24, false); } + // 前台:由于是弹出面板,此时路径不会起任何作用 + [CommandMethod(nameof(FileNotExist2))] + public void FileNotExist2() + { + using var tr = new DBTrans(); + tr.SaveDwgFile(saveAsName: "D:\\"); + } + + // 后台:只有路径,没有文件名 + [CommandMethod(nameof(FileNotExist3))] + public void FileNotExist3() + { + using var tr = new DBTrans("D:\\"); + tr.SaveDwgFile(); + + using var tr2 = new DBTrans("D:\\"); + tr2.SaveDwgFile(saveAsName: "D:\\"); + } + [CommandMethod("testtr")] public void Testtr() -- Gitee From 5271d88a942edc554b81a63e2e022bf117a2e2b9 Mon Sep 17 00:00:00 2001 From: liuqihong <540762622@qq.com> Date: Wed, 31 Aug 2022 20:32:57 +0000 Subject: [PATCH 466/675] =?UTF-8?q?=E4=BF=AE=E6=94=B9=E6=8F=90=E7=A4=BA?= =?UTF-8?q?=E9=94=99=E8=AF=AF?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: liuqihong <540762622@qq.com> --- src/IFoxCAD.Cad.Shared/Runtime/DBTrans.cs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/IFoxCAD.Cad.Shared/Runtime/DBTrans.cs b/src/IFoxCAD.Cad.Shared/Runtime/DBTrans.cs index aeba572..e5e6ffe 100644 --- a/src/IFoxCAD.Cad.Shared/Runtime/DBTrans.cs +++ b/src/IFoxCAD.Cad.Shared/Runtime/DBTrans.cs @@ -1,4 +1,4 @@ -namespace IFoxCAD.Cad; +namespace IFoxCAD.Cad; using System.Diagnostics; using System.IO; @@ -418,6 +418,7 @@ public void SaveDwgFile(DwgVersion version = DwgVersion.AC1800, } else { + fileMsg = saveAsName; // 路径失败也保存到桌面 // 没有文件名呢?那就报错,保存到系统目录也会报错,只能尽人事 var path = Path.GetDirectoryName(saveAsName); -- Gitee From d3690ed17e932a2c0effe3f53037cc5382b98276 Mon Sep 17 00:00:00 2001 From: liuqihong <540762622@qq.com> Date: Wed, 31 Aug 2022 20:36:09 +0000 Subject: [PATCH 467/675] =?UTF-8?q?=E7=A9=BA=E6=A0=BC=E6=9D=A1=E4=BB=B6?= =?UTF-8?q?=E8=A1=A5=E5=85=85?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: liuqihong <540762622@qq.com> --- src/IFoxCAD.Cad.Shared/Runtime/DBTrans.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/IFoxCAD.Cad.Shared/Runtime/DBTrans.cs b/src/IFoxCAD.Cad.Shared/Runtime/DBTrans.cs index e5e6ffe..02bb2ea 100644 --- a/src/IFoxCAD.Cad.Shared/Runtime/DBTrans.cs +++ b/src/IFoxCAD.Cad.Shared/Runtime/DBTrans.cs @@ -475,9 +475,9 @@ public void SaveDwgFile(DwgVersion version = DwgVersion.AC1800, var fileName = Path.GetFileNameWithoutExtension(_fileFullName); var fileExt = Path.GetExtension(_fileFullName); - if (fileName == string.Empty) + if (fileName.Trim() == string.Empty) fileName = DateTime.Now.ToString("--yyMMdd--hhmmssffff"); - if (fileExt == string.Empty) + if (fileExt.Trim() == string.Empty) fileExt = ".dwg"; // 构造函数(fileName)用了不存在的路径进行后台打开,就会出现此问题 -- Gitee From 5ed93227cfc30773e93d1c426c79a9b0307c7184 Mon Sep 17 00:00:00 2001 From: liuqihong <540762622@qq.com> Date: Thu, 1 Sep 2022 06:39:25 +0800 Subject: [PATCH 468/675] =?UTF-8?q?=E4=BC=98=E5=8C=96=E4=BB=A3=E7=A0=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/IFoxCAD.Cad.Shared/Runtime/DBTrans.cs | 59 ++++++++++++----------- src/IFoxCAD.Cad.Shared/Runtime/Env.cs | 13 ++--- tests/TestShared/TestDBTrans.cs | 4 +- 3 files changed, 41 insertions(+), 35 deletions(-) diff --git a/src/IFoxCAD.Cad.Shared/Runtime/DBTrans.cs b/src/IFoxCAD.Cad.Shared/Runtime/DBTrans.cs index 02bb2ea..b54a9b2 100644 --- a/src/IFoxCAD.Cad.Shared/Runtime/DBTrans.cs +++ b/src/IFoxCAD.Cad.Shared/Runtime/DBTrans.cs @@ -380,11 +380,11 @@ public ObjectId GetObjectId(string handleString) /// /// 默认2004dwg: /// 为true时候无效,将自动识别环境变量 - /// 另存为路径,前台将调用另存为面板,此时它将无效 + /// 另存为路径,前台将调用另存为面板,此时它将无效 /// 保存失败的提示 public void SaveDwgFile(DwgVersion version = DwgVersion.AC1800, bool automatic = true, - string? saveAsName = null, + string? saveAsFile = null, bool echoes = true) { Document? doca = null; @@ -400,70 +400,75 @@ public void SaveDwgFile(DwgVersion version = DwgVersion.AC1800, // 前台开图,使用命令保存;不需要切换文档 if (doca != null) { - if (saveAsName == null) + if (saveAsFile == null) doca.SendStringToExecute("_qsave\n", false, true, true); else - /// 无法把 给这个面板 + /// 无法把 给这个面板 doca.SendStringToExecute($"_Saveas\n", false, true, true); return; } - // 后台开图,用数据库保存 - string? fileMsg = _fileFullName; + string? fileMsg; bool flag = false; - if (saveAsName == null) + if (saveAsFile == null) { + fileMsg = _fileFullName; flag = true; } else { - fileMsg = saveAsName; + fileMsg = saveAsFile; // 路径失败也保存到桌面 // 没有文件名呢?那就报错,保存到系统目录也会报错,只能尽人事 - var path = Path.GetDirectoryName(saveAsName); + var path = Path.GetDirectoryName(saveAsFile); if (string.IsNullOrEmpty(path)) + { flag = true; - else + } + else if (!Directory.Exists(path)) { - try - { - if (!Directory.Exists(path)) - Directory.CreateDirectory(path); - } + try { Directory.CreateDirectory(path); } catch { flag = true; } } } - if (string.IsNullOrEmpty(Path.GetFileNameWithoutExtension(saveAsName))) + var fileNameWith = Path.GetFileNameWithoutExtension(saveAsFile).Trim(); + if (string.IsNullOrEmpty(fileNameWith)) flag = true; if (flag) { - var (error, file2) = GetSaveAsName(); + var (error, file) = GetSaveAsFile(); if (echoes && error) - MessageBox.Show($"错误参数:\n{fileMsg}\n\n它将保存:\n{file2}", "错误的文件路径"); - saveAsName = file2; + MessageBox.Show($"错误参数:\n{fileMsg}\n\n它将保存:\n{file}", "错误的文件路径"); + saveAsFile = file; } - if (Path.GetExtension(saveAsName).ToLower().Contains("dxf")) + + if (Path.GetExtension(saveAsFile).ToLower().Contains("dxf")) { // dxf用任何版本号都会报错 - Database.DxfOut(saveAsName, 7, true); + Database.DxfOut(saveAsFile, 7, true); } else { if (automatic) - version = Env.GetDefaultFormatForSaveToDwgVersion(); + version = Env.GetDefaultDwgVersion(); // dwg需要版本号,而dxf不用,dwg用dxf版本号会报错 // 若扩展名和版本号冲突,按照扩展名为准 if (version.IsDxfVersion()) version = DwgVersion.Current; - Database.SaveAs(saveAsName, version); + Database.SaveAs(saveAsFile, version); } } - (bool error, string path) GetSaveAsName() + + /// + /// 获取文件名,无效的话就制造 + /// + /// + (bool error, string path) GetSaveAsFile() { var file = Database.Filename; if (!string.IsNullOrEmpty(file)) @@ -472,12 +477,12 @@ public void SaveDwgFile(DwgVersion version = DwgVersion.AC1800, // 因为用户的任务可能占用时间非常长,所以这里提示保存到桌面, // 而不是弹出警告就结束 // 防止前台关闭了所有文档导致没有Editor,所以使用 MessageBox 发送警告 - var fileName = Path.GetFileNameWithoutExtension(_fileFullName); + var fileName = Path.GetFileNameWithoutExtension(_fileFullName).Trim(); var fileExt = Path.GetExtension(_fileFullName); - if (fileName.Trim() == string.Empty) + if (string.IsNullOrEmpty(fileName)) fileName = DateTime.Now.ToString("--yyMMdd--hhmmssffff"); - if (fileExt.Trim() == string.Empty) + if (string.IsNullOrEmpty(fileExt)) fileExt = ".dwg"; // 构造函数(fileName)用了不存在的路径进行后台打开,就会出现此问题 diff --git a/src/IFoxCAD.Cad.Shared/Runtime/Env.cs b/src/IFoxCAD.Cad.Shared/Runtime/Env.cs index db74805..56206ac 100644 --- a/src/IFoxCAD.Cad.Shared/Runtime/Env.cs +++ b/src/IFoxCAD.Cad.Shared/Runtime/Env.cs @@ -544,28 +544,29 @@ public static void SetEnv(string var, string? value) /// 获取当前配置文件的保存版本 /// /// - public static DwgVersion GetDefaultFormatForSaveToDwgVersion() + public static DwgVersion GetDefaultDwgVersion() { DwgVersion version; var ffs = Env.AcedGetEnv("DefaultFormatForSave"); version = ffs switch { - "1" => DwgVersion.AC1009,// r12/LT12 dxf - "8" => DwgVersion.AC1014,// r14/LT98/LT97 dwg + "1" => DwgVersion.AC1009,// R12/LT12 dxf + "8" => DwgVersion.AC1014,// R14/LT98/LT97 dwg "12" => DwgVersion.AC1015,// 2000 dwg "13" => DwgVersion.AC1800a,// 2000 dxf "24" => DwgVersion.AC1800,// 2004 dwg "25" => (DwgVersion)26,// 2004 dxf "36" => (DwgVersion)27,// 2007 dwg DwgVersion.AC1021 "37" => (DwgVersion)28,// 2007 dxf - //"38" => (DwgVersion),// dwt 样板文件...啊惊没找到这个是什么 + + //"38" => (DwgVersion),// dwt 样板文件...啊惊没找到这个是什么 "48" => (DwgVersion)29,// 2010 dwg DwgVersion.AC1024 "49" => (DwgVersion)30,// 2010 dxf "60" => (DwgVersion)31,// 2013 dwg DwgVersion.AC1027 "61" => (DwgVersion)32,// 2013 dxf "64" => (DwgVersion)33,// 2018 dwg DwgVersion.AC1032 "65" => (DwgVersion)34,// 2018 dxf - _ => (DwgVersion)25, + _ => DwgVersion.AC1800, }; return version; } @@ -579,7 +580,7 @@ public static bool IsDxfVersion(this DwgVersion dwgVersion) { var result = (int)dwgVersion switch { - 16 => true,// r12/LT12 dxf + 16 => true,// R12/LT12 dxf 24 => true,// 2000 dxf 26 => true,// 2004 dxf 28 => true,// 2007 dxf diff --git a/tests/TestShared/TestDBTrans.cs b/tests/TestShared/TestDBTrans.cs index 8882dad..ad17c71 100644 --- a/tests/TestShared/TestDBTrans.cs +++ b/tests/TestShared/TestDBTrans.cs @@ -15,7 +15,7 @@ public void FileNotExist() public void FileNotExist2() { using var tr = new DBTrans(); - tr.SaveDwgFile(saveAsName: "D:\\"); + tr.SaveDwgFile(saveAsFile: "D:\\"); } // 后台:只有路径,没有文件名 @@ -26,7 +26,7 @@ public void FileNotExist3() tr.SaveDwgFile(); using var tr2 = new DBTrans("D:\\"); - tr2.SaveDwgFile(saveAsName: "D:\\"); + tr2.SaveDwgFile(saveAsFile: "D:\\"); } -- Gitee From c9ba7dca2a07b5dcc264a5b5bbfa90d7edecb715 Mon Sep 17 00:00:00 2001 From: liuqihong <540762622@qq.com> Date: Fri, 2 Sep 2022 00:42:43 +0800 Subject: [PATCH 469/675] =?UTF-8?q?=E6=8F=90=E4=BE=9Bcad=E5=B9=B4=E4=BB=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/IFoxCAD.Cad.Shared/Runtime/Env.cs | 160 ++++++++++++++++++++++++-- 1 file changed, 148 insertions(+), 12 deletions(-) diff --git a/src/IFoxCAD.Cad.Shared/Runtime/Env.cs b/src/IFoxCAD.Cad.Shared/Runtime/Env.cs index 56206ac..3defcd6 100644 --- a/src/IFoxCAD.Cad.Shared/Runtime/Env.cs +++ b/src/IFoxCAD.Cad.Shared/Runtime/Env.cs @@ -436,7 +436,7 @@ private static string GetName(this T value) /// /// 变量名 /// 变量值 - public static object GetVar(string varName) + public static object GetVar(string? varName) { return Acap.GetSystemVariable(varName); } @@ -445,7 +445,7 @@ public static object GetVar(string varName) /// /// 变量名 /// 变量值 - public static void SetVar(string varName, object value) + public static void SetVar(string? varName, object? value) { try { @@ -460,35 +460,35 @@ public static void SetVar(string varName, object value) #if NET35 [System.Security.SuppressUnmanagedCodeSecurity] [DllImport("acad.exe", CharSet = CharSet.Auto, CallingConvention = CallingConvention.Cdecl, EntryPoint = "acedGetEnv")] - static extern int AcedGetEnv(string envName, StringBuilder ReturnValue); + static extern int AcedGetEnv(string? envName, StringBuilder ReturnValue); [System.Security.SuppressUnmanagedCodeSecurity] [DllImport("acad.exe", CharSet = CharSet.Auto, CallingConvention = CallingConvention.Cdecl, EntryPoint = "acedSetEnv")] - static extern int AcedSetEnv(string envName, StringBuilder NewValue); + static extern int AcedSetEnv(string? envName, StringBuilder NewValue); #elif !HC2020 [System.Security.SuppressUnmanagedCodeSecurity] [DllImport("accore.dll", CharSet = CharSet.Auto, CallingConvention = CallingConvention.Cdecl, EntryPoint = "acedGetEnv")] - static extern int AcedGetEnv(string envName, StringBuilder ReturnValue); + static extern int AcedGetEnv(string? envName, StringBuilder ReturnValue); [System.Security.SuppressUnmanagedCodeSecurity] [DllImport("accore.dll", CharSet = CharSet.Auto, CallingConvention = CallingConvention.Cdecl, EntryPoint = "acedSetEnv")] - static extern int AcedSetEnv(string envName, StringBuilder NewValue); + static extern int AcedSetEnv(string? envName, StringBuilder NewValue); #endif #if HC2020 [System.Security.SuppressUnmanagedCodeSecurity] [DllImport("gced.dll", CharSet = CharSet.Auto, CallingConvention = CallingConvention.Cdecl, EntryPoint = "gcedGetEnv")] - static extern int AcedGetEnv(string envName, StringBuilder ReturnValue); + static extern int AcedGetEnv(string? envName, StringBuilder ReturnValue); [System.Security.SuppressUnmanagedCodeSecurity] [DllImport("gced.dll", CharSet = CharSet.Auto, CallingConvention = CallingConvention.Cdecl, EntryPoint = "gcedSetEnv")] - static extern int AcedSetEnv(string envName, StringBuilder NewValue); + static extern int AcedSetEnv(string? envName, StringBuilder NewValue); #endif /// /// 设置环境变量 /// - public static string AcedGetEnv(string name) + public static string AcedGetEnv(string? name) { var sbRes = new StringBuilder(1024); _ = AcedGetEnv(name, sbRes); @@ -501,7 +501,7 @@ public static string AcedGetEnv(string name) /// lisp的名称 /// 要设置的值 /// 成功标识 - public static int AcedSetEnv(string name, string var) + public static int AcedSetEnv(string? name, string? var) { return AcedSetEnv(name, new StringBuilder(var)); } @@ -511,7 +511,7 @@ public static int AcedSetEnv(string name, string var) /// /// 变量名 /// 指定的环境变量的值;或者如果找不到环境变量,则返回 null - public static string? GetEnv(string var) + public static string? GetEnv(string? var) { // 从当前进程或者从当前用户或本地计算机的 Windows 操作系统注册表项检索环境变量的值 return Environment.GetEnvironmentVariable(var); @@ -521,7 +521,7 @@ public static int AcedSetEnv(string name, string var) /// /// 变量名 /// 变量值 - public static void SetEnv(string var, string? value) + public static void SetEnv(string? var, string? value) { // 创建、修改或删除当前进程中或者为当前用户或本地计算机保留的 Windows 操作系统注册表项中存储的环境变量 Environment.SetEnvironmentVariable(var, value); @@ -591,4 +591,140 @@ public static bool IsDxfVersion(this DwgVersion dwgVersion) }; return result; } + + /// + /// 获取cad年份 + /// + /// 超出年份就报错 + public static int GetAcadVersion() + { + var ver = Acap.Version.Major + "." + Acap.Version.Minor; + int acarVarNum = ver switch + { + "16.2" => 2006, + "17.0" => 2007, + "17.1" => 2008, + "17.2" => 2009, + "18.0" => 2010, + "18.1" => 2011, + "18.2" => 2012, + "19.0" => 2013, + "19.1" => 2014, + "20.0" => 2015, + "20.1" => 2016, + "21.0" => 2017, + "22.0" => 2018, + "23.0" => 2019, + "23.1" => 2020, + "24.0" => 2021, + "24.1" => 2022, + _ => throw new NotImplementedException(), + }; + return acarVarNum; + } + + +#if !NET35 && !NET40 + [CommandMethod(nameof(Test_GetvarAll))] + public static void Test_GetvarAll() + { + GetvarAll(); + } + + public static Dictionary GetvarAll() + { + var dict = new Dictionary(); + var en = new SystemVariableEnumerator(); + while (en.MoveNext()) + { + Console.WriteLine(en.Current.Name + "-----" + en.Current.Value); + dict.Add(en.Current.Name, en.Current.Value); + } + return dict; + } +#endif + + + /// + /// 返回现有系统变量并设置新系统变量 + /// + /// 设置的变量词典 + /// 返回现有变量词典 + public static Dictionary SaveNowVar(Dictionary pairs) + { + if (pairs is null) + throw new ArgumentNullException(nameof(pairs)); + + // 系统变量保存 + var dict = new Dictionary(); + // 系统变量设置 + foreach (var item in pairs) + { + var bak = Env.AcedGetEnv(item.Key); + Env.AcedSetEnv(item.Key, item.Value); + if (bak is not null) + dict.Add(item.Key, bak); + } + return dict; + } + +#if true2 + /// + /// 设置系统或环境变量 + /// + /// 变量名 + /// 变量值 + /// 成功设置返回值,失败null + public static string? Setvar(string? name, string? parameter) + { + if (name is null) + throw new ArgumentException(null, nameof(name)); + if (parameter is null) + throw new ArgumentException(null, nameof(parameter)); + + string? valueTypeName = null; + string? valueOld; + try + { + // 改系统变量 + var value = Acap.GetSystemVariable(name); + if (value is null) + return null; + valueOld = value.ToString(); + valueTypeName = value.GetType().Name; + // 如果出现了clayer无法设置,是没有锁文档导致的 + switch (valueTypeName) + { + case "String": + Acap.SetSystemVariable(name, parameter.Replace("\"", ""));// 去掉引号 + break; + case "Double": + Acap.SetSystemVariable(name, double.Parse(parameter)); + break; + case "Int16": + Acap.SetSystemVariable(name, short.Parse(parameter)); + break; + case "Int32": + Acap.SetSystemVariable(name, int.Parse(parameter)); + break; + } + } + catch (Exception err1) + { + try + { + valueOld = Env.AcedGetEnv(name); + Env.AcedSetEnv(name, parameter); + } + catch (Exception err2) + { + // 当系统变量没有,环境变量也没有才抛出错误 + var error = $"\n**** cad系统变量和环境都没有:" + + $"{name}\n出错:{parameter}\n来自:{valueTypeName}\n{err1.Message}\n{err2.Message}"; + throw new Exception(error); + } + } + return valueOld; + } +#endif } -- Gitee From ef3b3039e53e0aa6e81fe0ff6fb57a94efc820ab Mon Sep 17 00:00:00 2001 From: liuqihong <540762622@qq.com> Date: Fri, 2 Sep 2022 10:38:48 +0800 Subject: [PATCH 470/675] =?UTF-8?q?=E6=8F=90=E4=BE=9B=E7=B3=BB=E7=BB=9F?= =?UTF-8?q?=E5=8F=98=E9=87=8F=E5=92=8C=E7=8E=AF=E5=A2=83=E7=9A=84=E5=A4=84?= =?UTF-8?q?=E7=90=86=E6=89=8B=E6=B3=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/IFoxCAD.Cad.Shared/Runtime/Env.cs | 190 +++++++++++++------------- tests/TestShared/CmdINI.cs | 27 +++- tests/TestShared/Test.cs | 2 +- 3 files changed, 120 insertions(+), 99 deletions(-) diff --git a/src/IFoxCAD.Cad.Shared/Runtime/Env.cs b/src/IFoxCAD.Cad.Shared/Runtime/Env.cs index 3defcd6..e74ffe0 100644 --- a/src/IFoxCAD.Cad.Shared/Runtime/Env.cs +++ b/src/IFoxCAD.Cad.Shared/Runtime/Env.cs @@ -430,9 +430,9 @@ private static string GetName(this T value) #endregion Enum - #region 环境变量 + #region acad系统变量 /// - /// 获取cad变量 + /// 获取cad系统变量 /// /// 变量名 /// 变量值 @@ -441,11 +441,15 @@ public static object GetVar(string? varName) return Acap.GetSystemVariable(varName); } /// - /// 设置cad变量 + /// 设置cad系统变量
    + /// 0x01 建议先获取现有变量值和设置的是否相同,否则直接设置会发生异常
    + /// 0x02 建议锁文档,否则 Psltscale 设置发生异常
    + /// 发生异常的时候vs输出窗口会打印一下,但是如果不介意也没啥问题 ///
    /// 变量名 /// 变量值 - public static void SetVar(string? varName, object? value) + /// 输出异常,默认true;此设置仅为打印到命令栏,无法控制vs输出 + public static void SetVar(string? varName, object? value, bool echo = true) { try { @@ -453,10 +457,13 @@ public static void SetVar(string? varName, object? value) } catch (System.Exception) { - Env.Print($"{varName} 是不存在的变量!"); + if (echo) + Env.Print($"{varName} 是不存在的变量!"); } - } + } + #endregion + #region acad环境变量 #if NET35 [System.Security.SuppressUnmanagedCodeSecurity] [DllImport("acad.exe", CharSet = CharSet.Auto, CallingConvention = CallingConvention.Cdecl, EntryPoint = "acedGetEnv")] @@ -474,7 +481,6 @@ public static void SetVar(string? varName, object? value) [DllImport("accore.dll", CharSet = CharSet.Auto, CallingConvention = CallingConvention.Cdecl, EntryPoint = "acedSetEnv")] static extern int AcedSetEnv(string? envName, StringBuilder NewValue); #endif - #if HC2020 [System.Security.SuppressUnmanagedCodeSecurity] [DllImport("gced.dll", CharSet = CharSet.Auto, CallingConvention = CallingConvention.Cdecl, EntryPoint = "gcedGetEnv")] @@ -486,8 +492,10 @@ public static void SetVar(string? varName, object? value) #endif /// - /// 设置环境变量 + /// 读取acad环境变量 /// + /// + /// 返回值从不为null,需判断 public static string AcedGetEnv(string? name) { var sbRes = new StringBuilder(1024); @@ -496,16 +504,22 @@ public static string AcedGetEnv(string? name) } /// - /// 设置环境变量 + /// 设置acad环境变量
    + /// 它是不会报错的,但是直接设置会写入注册表的,
    + /// 如果是设置高低版本cad不同的变量,建议先读取判断再设置
    ///
    - /// lisp的名称 - /// 要设置的值 - /// 成功标识 + /// + /// + /// public static int AcedSetEnv(string? name, string? var) { + // acad2008注册表路径: + // 计算机\HKEY_CURRENT_USER\SOFTWARE\Autodesk\AutoCAD\R17.1\ACAD - 6001:804\FixedProfile\General return AcedSetEnv(name, new StringBuilder(var)); } + #endregion + #region win环境变量 /// /// 获取系统环境变量 /// @@ -525,7 +539,7 @@ public static void SetEnv(string? var, string? value) { // 创建、修改或删除当前进程中或者为当前用户或本地计算机保留的 Windows 操作系统注册表项中存储的环境变量 Environment.SetEnvironmentVariable(var, value); - } + } #endregion @@ -534,12 +548,15 @@ public static void SetEnv(string? var, string? value) /// /// 要打印的对象 public static void Print(object message) => Editor.WriteMessage($"{message}\n"); + /// /// 判断当前是否在UCS坐标下 /// /// Bool public static bool IsUcs() => (short)GetVar("WORLDUCS") == 0; + + #region dwg版本号/cad版本号/年份 /// /// 获取当前配置文件的保存版本 /// @@ -622,109 +639,88 @@ public static int GetAcadVersion() }; return acarVarNum; } + #endregion -#if !NET35 && !NET40 - [CommandMethod(nameof(Test_GetvarAll))] - public static void Test_GetvarAll() - { - GetvarAll(); - } - - public static Dictionary GetvarAll() - { - var dict = new Dictionary(); - var en = new SystemVariableEnumerator(); - while (en.MoveNext()) - { - Console.WriteLine(en.Current.Name + "-----" + en.Current.Value); - dict.Add(en.Current.Name, en.Current.Value); - } - return dict; - } -#endif - - + #region cad变量功能延伸 /// - /// 返回现有系统变量并设置新系统变量 + /// 设置cad系统变量
    + /// 提供一个反序列化后,无cad异常输出的功能
    + /// 注意,您需要再此执行时候设置文档锁 + /// + /// 否则也将导致修改数据库异常
    ///
    - /// 设置的变量词典 - /// 返回现有变量词典 - public static Dictionary SaveNowVar(Dictionary pairs) + /// + /// + /// 成功返回当前值,失败null + /// + public static object? SetVarEx(string? key, string? value) { - if (pairs is null) - throw new ArgumentNullException(nameof(pairs)); + if (key == null) + throw new ArgumentNullException(nameof(key)); + if (value == null) + throw new ArgumentNullException(nameof(value)); - // 系统变量保存 - var dict = new Dictionary(); - // 系统变量设置 - foreach (var item in pairs) + var currentVar = Env.GetVar(key); + if (currentVar == null) + return null; + + object? valueType = null; + switch (currentVar.GetType().Name) { - var bak = Env.AcedGetEnv(item.Key); - Env.AcedSetEnv(item.Key, item.Value); - if (bak is not null) - dict.Add(item.Key, bak); + case "String": + valueType = value.Replace("\"", string.Empty); + break; + case "Double": + valueType = double.Parse(value); + break; + case "Int16": + valueType = short.Parse(value); + break; + case "Int32": + valueType = int.Parse(value); + break; } - return dict; + + // 相同的参数进行设置会发生一次异常 + if (currentVar.ToString().ToUpper() != valueType!.ToString().ToUpper()) + Env.SetVar(key, valueType); + + return currentVar; } -#if true2 /// - /// 设置系统或环境变量 + /// 设置新系统变量,返回现有系统变量 /// - /// 变量名 - /// 变量值 - /// 成功设置返回值,失败null - public static string? Setvar(string? name, string? parameter) + /// 设置的变量词典 + /// 返回现有变量词典,然后下次就可以利用它进行设置回来了 + public static Dictionary SaveCadVar(Dictionary args) { - if (name is null) - throw new ArgumentException(null, nameof(name)); - if (parameter is null) - throw new ArgumentException(null, nameof(parameter)); + if (args is null) + throw new ArgumentNullException(nameof(args)); - string? valueTypeName = null; - string? valueOld; - try - { - // 改系统变量 - var value = Acap.GetSystemVariable(name); - if (value is null) - return null; - valueOld = value.ToString(); - valueTypeName = value.GetType().Name; - // 如果出现了clayer无法设置,是没有锁文档导致的 - switch (valueTypeName) - { - case "String": - Acap.SetSystemVariable(name, parameter.Replace("\"", ""));// 去掉引号 - break; - case "Double": - Acap.SetSystemVariable(name, double.Parse(parameter)); - break; - case "Int16": - Acap.SetSystemVariable(name, short.Parse(parameter)); - break; - case "Int32": - Acap.SetSystemVariable(name, int.Parse(parameter)); - break; - } - } - catch (Exception err1) + var dict = new Dictionary(); + foreach (var item in args) { - try + // 判断是否为系统变量 + var ok = SetVarEx(item.Key, item.Value); + if (ok != null) { - valueOld = Env.AcedGetEnv(name); - Env.AcedSetEnv(name, parameter); + dict.Add(item.Key, ok.ToString()); + continue; } - catch (Exception err2) + + // 判断是否为系统变量 + var envstr = Env.AcedGetEnv(item.Key); + if (!string.IsNullOrEmpty(envstr)) { - // 当系统变量没有,环境变量也没有才抛出错误 - var error = $"\n**** cad系统变量和环境都没有:" + - $"{name}\n出错:{parameter}\n来自:{valueTypeName}\n{err1.Message}\n{err2.Message}"; - throw new Exception(error); + Env.AcedSetEnv(item.Key, item.Value); + dict.Add(item.Key, envstr); } } - return valueOld; + return dict; } -#endif + + + #endregion } diff --git a/tests/TestShared/CmdINI.cs b/tests/TestShared/CmdINI.cs index 361cb65..ad50fc3 100644 --- a/tests/TestShared/CmdINI.cs +++ b/tests/TestShared/CmdINI.cs @@ -58,7 +58,7 @@ public void IFoxAddReg() /// [CommandMethod("IFoxRemoveReg")] public void IFoxRemoveReg() - { + { var dm = Acap.DocumentManager; var doc = dm.MdiActiveDocument; var ed = doc.Editor; @@ -130,4 +130,29 @@ public void Terminate() // // 文档管理器将比此接口前死亡,因此此句不会执行 // Acap.DocumentManager.MdiActiveDocument?.Editor.WriteMessage("\nunload...."); // } +} + +public partial class Test +{ +#if !NET35 && !NET40 + + // 通过此功能获取全部变量,尚不清楚此处如何设置,没有通过测试 + [CommandMethod(nameof(Test_GetvarAll))] + public static void Test_GetvarAll() + { + GetvarAll(); + } + + public static Dictionary GetvarAll() + { + var dict = new Dictionary(); + var en = new SystemVariableEnumerator(); + while (en.MoveNext()) + { + Console.WriteLine(en.Current.Name + "-----" + en.Current.Value);//Value会出现异常 + dict.Add(en.Current.Name, en.Current.Value); + } + return dict; + } +#endif } \ No newline at end of file diff --git a/tests/TestShared/Test.cs b/tests/TestShared/Test.cs index 309b773..ceea8de 100644 --- a/tests/TestShared/Test.cs +++ b/tests/TestShared/Test.cs @@ -1,7 +1,7 @@ namespace Test; #pragma warning disable CS0219 // 变量已被赋值,但从未使用过它的值 -public class Test +public partial class Test { [CommandMethod("dbtest")] public void Dbtest() -- Gitee From 98f17e7ddaffe59f8dd4cda374ab8cfe7f74b02a Mon Sep 17 00:00:00 2001 From: liuqihong <540762622@qq.com> Date: Fri, 2 Sep 2022 11:06:15 +0800 Subject: [PATCH 471/675] =?UTF-8?q?=E6=8F=90=E4=BE=9Bacad=E7=8E=AF?= =?UTF-8?q?=E5=A2=83=E5=8F=98=E9=87=8F=E6=B5=8B=E8=AF=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/IFoxCAD.Cad.Shared/Runtime/Env.cs | 1 + tests/TestShared/CmdINI.cs | 7 +++++++ 2 files changed, 8 insertions(+) diff --git a/src/IFoxCAD.Cad.Shared/Runtime/Env.cs b/src/IFoxCAD.Cad.Shared/Runtime/Env.cs index e74ffe0..6db0fc6 100644 --- a/src/IFoxCAD.Cad.Shared/Runtime/Env.cs +++ b/src/IFoxCAD.Cad.Shared/Runtime/Env.cs @@ -548,6 +548,7 @@ public static void SetEnv(string? var, string? value) /// /// 要打印的对象 public static void Print(object message) => Editor.WriteMessage($"{message}\n"); + public static void Printl(object message) => Editor.WriteMessage($"{Environment.NewLine}{message}\n"); /// /// 判断当前是否在UCS坐标下 diff --git a/tests/TestShared/CmdINI.cs b/tests/TestShared/CmdINI.cs index ad50fc3..c3c58b4 100644 --- a/tests/TestShared/CmdINI.cs +++ b/tests/TestShared/CmdINI.cs @@ -134,6 +134,13 @@ public void Terminate() public partial class Test { + [CommandMethod(nameof(Test_GetEnv))] + public static void Test_GetEnv() + { + var dir = Env.AcedGetEnv("PrinterConfigDir"); + Env.Printl("pc3打印机位置:" + dir); + } + #if !NET35 && !NET40 // 通过此功能获取全部变量,尚不清楚此处如何设置,没有通过测试 -- Gitee From e976b907752653918459a708daf1e2effce6e776 Mon Sep 17 00:00:00 2001 From: liuqihong <540762622@qq.com> Date: Fri, 2 Sep 2022 11:12:28 +0800 Subject: [PATCH 472/675] =?UTF-8?q?=E4=BC=98=E5=8C=96=E4=BB=A3=E7=A0=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/IFoxCAD.Cad.Shared/Runtime/Env.cs | 32 ++++++++++----------------- 1 file changed, 12 insertions(+), 20 deletions(-) diff --git a/src/IFoxCAD.Cad.Shared/Runtime/Env.cs b/src/IFoxCAD.Cad.Shared/Runtime/Env.cs index 6db0fc6..956dc82 100644 --- a/src/IFoxCAD.Cad.Shared/Runtime/Env.cs +++ b/src/IFoxCAD.Cad.Shared/Runtime/Env.cs @@ -460,7 +460,7 @@ public static void SetVar(string? varName, object? value, bool echo = true) if (echo) Env.Print($"{varName} 是不存在的变量!"); } - } + } #endregion #region acad环境变量 @@ -539,7 +539,7 @@ public static void SetEnv(string? var, string? value) { // 创建、修改或删除当前进程中或者为当前用户或本地计算机保留的 Windows 操作系统注册表项中存储的环境变量 Environment.SetEnvironmentVariable(var, value); - } + } #endregion @@ -647,8 +647,8 @@ public static int GetAcadVersion() /// /// 设置cad系统变量
    /// 提供一个反序列化后,无cad异常输出的功能
    - /// 注意,您需要再此执行时候设置文档锁 - /// + /// 注意,您需要再此执行时候设置文档锁
    + ///
    /// 否则也将导致修改数据库异常
    ///
    /// @@ -666,22 +666,14 @@ public static int GetAcadVersion() if (currentVar == null) return null; - object? valueType = null; - switch (currentVar.GetType().Name) + object? valueType = currentVar.GetType().Name switch { - case "String": - valueType = value.Replace("\"", string.Empty); - break; - case "Double": - valueType = double.Parse(value); - break; - case "Int16": - valueType = short.Parse(value); - break; - case "Int32": - valueType = int.Parse(value); - break; - } + "String" => value.Replace("\"", string.Empty), + "Double" => double.Parse(value), + "Int16" => short.Parse(value), + "Int32" => int.Parse(value), + _ => throw new NotImplementedException(), + }; // 相同的参数进行设置会发生一次异常 if (currentVar.ToString().ToUpper() != valueType!.ToString().ToUpper()) @@ -720,7 +712,7 @@ public static Dictionary SaveCadVar(Dictionary a } } return dict; - } + } #endregion -- Gitee From edfe83c4fdffeac1873c37b3ed8c6f901cb5e2a6 Mon Sep 17 00:00:00 2001 From: liuqihong <540762622@qq.com> Date: Fri, 2 Sep 2022 12:13:20 +0800 Subject: [PATCH 473/675] =?UTF-8?q?=E6=8B=86=E7=A6=BB=E4=BF=9D=E5=AD=98?= =?UTF-8?q?=E5=87=BD=E6=95=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/IFoxCAD.Cad.Shared/Runtime/DBTrans.cs | 124 ++++++++++++---------- src/IFoxCAD.Cad.Shared/Runtime/Env.cs | 4 +- tests/TestShared/TestDBTrans.cs | 6 +- 3 files changed, 74 insertions(+), 60 deletions(-) diff --git a/src/IFoxCAD.Cad.Shared/Runtime/DBTrans.cs b/src/IFoxCAD.Cad.Shared/Runtime/DBTrans.cs index b54a9b2..77bc62d 100644 --- a/src/IFoxCAD.Cad.Shared/Runtime/DBTrans.cs +++ b/src/IFoxCAD.Cad.Shared/Runtime/DBTrans.cs @@ -31,7 +31,7 @@ public class DBTrans : IDisposable /// /// 文件名 /// - private readonly string? _fileFullName; + private readonly string? _fileName; #endregion #region 公开属性 @@ -138,15 +138,19 @@ public DBTrans(string fileName, if (fileName == null || string.IsNullOrEmpty(fileName.Trim())) throw new ArgumentNullException(nameof(fileName)); - _fileFullName = fileName.Replace("/", "\\");//// doc.Name总是"D:\\JX.dwg" + _fileName = fileName.Replace("/", "\\");//// doc.Name总是"D:\\JX.dwg" - if (!File.Exists(_fileFullName)) + if (!File.Exists(_fileName)) + { + // 此处若为失败的文件名,那么保存的时候就会丢失名称, + // 因此用 _fileName 储存 Database = new Database(true, false); + } else { var doc = Acap.DocumentManager .Cast() - .FirstOrDefault(doc => doc.Name == _fileFullName); + .FirstOrDefault(doc => doc.Name == _fileName); if (doc is not null) { Database = doc.Database; @@ -156,8 +160,10 @@ public DBTrans(string fileName, else { Database = new Database(false, true); - if (Path.GetExtension(_fileFullName).ToLower().Contains("dxf")) - Database.DxfIn(_fileFullName, null); + if (Path.GetExtension(_fileName).ToLower().Contains("dxf")) + { + Database.DxfIn(_fileName, null); + } else { #if ac2008 @@ -186,15 +192,12 @@ public DBTrans(string fileName, } // 这个会致命错误 - // using FileStream fileStream = new(_fileFullName, FileMode.Open, fileAccess, fileShare); - // Database.ReadDwgFile(fileStream.SafeFileHandle.DangerousGetHandle(), - // true/*控制读入一个与系统编码不相同的文件时的转换操作*/,password); + // using FileStream fileStream = new(_fileName, FileMode.Open, fileAccess, fileShare); + // Database.ReadDwgFile(fileStream.SafeFileHandle.DangerousGetHandle(), true, password); - Database.ReadDwgFile(_fileFullName, fileShare, - true/*控制读入一个与系统编码不相同的文件时的转换操作*/, password); + Database.ReadDwgFile(_fileName, fileShare, true, password); #else - Database.ReadDwgFile(_fileFullName, openMode, - true/*控制读入一个与系统编码不相同的文件时的转换操作*/, password); + Database.ReadDwgFile(_fileName, openMode,true, password); #endif } Database.CloseInput(true); @@ -374,77 +377,89 @@ public ObjectId GetObjectId(string handleString) #region 保存文件 /// - /// 保存当前数据库的dwg文件
    - /// 前台按dwg默认版本保存(环境变量),
    - /// 后台按参数保存,同时受影响 - ///
    - /// 默认2004dwg: - /// 为true时候无效,将自动识别环境变量 - /// 另存为路径,前台将调用另存为面板,此时它将无效 - /// 保存失败的提示 - public void SaveDwgFile(DwgVersion version = DwgVersion.AC1800, - bool automatic = true, - string? saveAsFile = null, - bool echoes = true) + /// 保存文件 + ///
    + /// + public void SaveDwgFile(DwgVersion version = DwgVersion.AC1800) + { + SaveFile(version); + } + + /// + /// 保存文件
    + ///
    + /// 默认2004dwg;若保存dxf则需要在路径输入扩展名 + /// 为true时候无效,将变为自动识别环境变量 + /// 另存为文件,前台将调用时它将无效,将变为弹出面板 + /// 保存路径失败的提示 + public void SaveFile(DwgVersion version = DwgVersion.AC1800, + bool automatic = true, + string? saveAsFile = null, + bool echoes = true) { - Document? doca = null; - foreach (Document doc in Acap.DocumentManager) + // 遍历当前所有文档,文档必然是前台的 + Document? doc = null; + foreach (Document docItem in Acap.DocumentManager) { - if (doc.Database.Filename == this.Database.Filename) + if (docItem.Database.Filename == this.Database.Filename) { - doca = doc; + doc = docItem; break; } } - // 前台开图,使用命令保存;不需要切换文档 - if (doca != null) + if (doc != null) { if (saveAsFile == null) - doca.SendStringToExecute("_qsave\n", false, true, true); + doc.SendStringToExecute("_qsave\n", false, true, true); else /// 无法把 给这个面板 - doca.SendStringToExecute($"_Saveas\n", false, true, true); + doc.SendStringToExecute($"_Saveas\n", false, true, true); return; } // 后台开图,用数据库保存 string? fileMsg; - bool flag = false; - if (saveAsFile == null) + bool creatFlag = false; + saveAsFile = saveAsFile?.Trim(); + if (string.IsNullOrEmpty(saveAsFile)) { - fileMsg = _fileFullName; - flag = true; + fileMsg = _fileName; + creatFlag = true; } else { fileMsg = saveAsFile; + // 路径失败也保存到桌面 - // 没有文件名呢?那就报错,保存到系统目录也会报错,只能尽人事 var path = Path.GetDirectoryName(saveAsFile); if (string.IsNullOrEmpty(path)) { - flag = true; + creatFlag = true; } else if (!Directory.Exists(path)) { try { Directory.CreateDirectory(path); } - catch { flag = true; } + catch { creatFlag = true; } } + + // 文件名缺失时 + if (!creatFlag && + string.IsNullOrEmpty(Path.GetFileName(saveAsFile).Trim())) + creatFlag = true; } var fileNameWith = Path.GetFileNameWithoutExtension(saveAsFile).Trim(); if (string.IsNullOrEmpty(fileNameWith)) - flag = true; + creatFlag = true; - if (flag) + if (creatFlag) { - var (error, file) = GetSaveAsFile(); + var (error, file) = GetOrCreateSaveAsFile(); if (echoes && error) MessageBox.Show($"错误参数:\n{fileMsg}\n\n它将保存:\n{file}", "错误的文件路径"); saveAsFile = file; } - if (Path.GetExtension(saveAsFile).ToLower().Contains("dxf")) { // dxf用任何版本号都会报错 @@ -468,17 +483,18 @@ public void SaveDwgFile(DwgVersion version = DwgVersion.AC1800, /// 获取文件名,无效的话就制造 /// /// - (bool error, string path) GetSaveAsFile() + (bool error, string path) GetOrCreateSaveAsFile() { var file = Database.Filename; if (!string.IsNullOrEmpty(file)) return (false, file); - // 因为用户的任务可能占用时间非常长,所以这里提示保存到桌面, + // 为了防止用户输入了错误的路径造成无法保存, + // 所以此处将进行保存到桌面, // 而不是弹出警告就结束 // 防止前台关闭了所有文档导致没有Editor,所以使用 MessageBox 发送警告 - var fileName = Path.GetFileNameWithoutExtension(_fileFullName).Trim(); - var fileExt = Path.GetExtension(_fileFullName); + var fileName = Path.GetFileNameWithoutExtension(_fileName).Trim(); + var fileExt = Path.GetExtension(_fileName); if (string.IsNullOrEmpty(fileName)) fileName = DateTime.Now.ToString("--yyMMdd--hhmmssffff"); @@ -512,12 +528,12 @@ public void SaveDwgFile(DwgVersion version = DwgVersion.AC1800, /// /// /// 备注:
    - /// - /// 0x01 文字偏移问题主要出现是这个线性引擎上面, - /// 在 参照绑定/深度克隆 的底层共用此技术导致问题发生 - /// 0x02 后台是利用前台当前数据库进行处理的 - /// 0x03 跨进程通讯暂无测试(可能存在bug) - /// + /// + /// 0x01 文字偏移问题主要出现线性引擎函数上面,
    + /// 在 参照绑定/深度克隆 的底层共用此函数导致
    + /// 0x02 后台是利用前台当前数据库进行处理的
    + /// 0x03 跨进程通讯暂无测试(可能存在bug)
    + ///
    ///
    /// 委托 /// 开启单行文字偏移处理 diff --git a/src/IFoxCAD.Cad.Shared/Runtime/Env.cs b/src/IFoxCAD.Cad.Shared/Runtime/Env.cs index 956dc82..13dc43a 100644 --- a/src/IFoxCAD.Cad.Shared/Runtime/Env.cs +++ b/src/IFoxCAD.Cad.Shared/Runtime/Env.cs @@ -584,7 +584,7 @@ public static DwgVersion GetDefaultDwgVersion() "61" => (DwgVersion)32,// 2013 dxf "64" => (DwgVersion)33,// 2018 dwg DwgVersion.AC1032 "65" => (DwgVersion)34,// 2018 dxf - _ => DwgVersion.AC1800, + _ => throw new NotImplementedException(),//提醒维护 }; return version; } @@ -713,7 +713,5 @@ public static Dictionary SaveCadVar(Dictionary a } return dict; } - - #endregion } diff --git a/tests/TestShared/TestDBTrans.cs b/tests/TestShared/TestDBTrans.cs index ad17c71..5ffefa8 100644 --- a/tests/TestShared/TestDBTrans.cs +++ b/tests/TestShared/TestDBTrans.cs @@ -7,7 +7,7 @@ public class TestTrans public void FileNotExist() { using var tr = new DBTrans("test.dwg"); - tr.SaveDwgFile((DwgVersion)24, false); + tr.SaveFile((DwgVersion)24, false); } // 前台:由于是弹出面板,此时路径不会起任何作用 @@ -15,7 +15,7 @@ public void FileNotExist() public void FileNotExist2() { using var tr = new DBTrans(); - tr.SaveDwgFile(saveAsFile: "D:\\"); + tr.SaveFile(saveAsFile: "D:\\"); } // 后台:只有路径,没有文件名 @@ -26,7 +26,7 @@ public void FileNotExist3() tr.SaveDwgFile(); using var tr2 = new DBTrans("D:\\"); - tr2.SaveDwgFile(saveAsFile: "D:\\"); + tr2.SaveFile(saveAsFile: "D:\\"); } -- Gitee From 4d0e85287e35b393e9ad3c29432358d8c9365d8b Mon Sep 17 00:00:00 2001 From: liuqihong <540762622@qq.com> Date: Sun, 4 Sep 2022 19:09:22 +0800 Subject: [PATCH 474/675] =?UTF-8?q?=E5=A2=9E=E5=8A=A0=E6=B3=A8=E9=87=8A?= =?UTF-8?q?=E5=AF=B9=E8=B1=A1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../ExtensionMethod/JigEx.cs | 23 ++++++++++++++++++- 1 file changed, 22 insertions(+), 1 deletion(-) diff --git a/src/IFoxCAD.Cad.Shared/ExtensionMethod/JigEx.cs b/src/IFoxCAD.Cad.Shared/ExtensionMethod/JigEx.cs index e47d74e..9341771 100644 --- a/src/IFoxCAD.Cad.Shared/ExtensionMethod/JigEx.cs +++ b/src/IFoxCAD.Cad.Shared/ExtensionMethod/JigEx.cs @@ -53,9 +53,10 @@ bool SystemVariables_Orthomode // 正交修改还原 /// /// 在界面绘制图元 /// - private JigEx() + JigEx() { _drawEntitys = new(); + DimensionEntitys = new(); } /// @@ -293,6 +294,7 @@ protected override bool WorldDraw(WorldDraw draw) } #endregion + /// /// 用户输入控制默认配置 /// 令jig.Drag().Status == @@ -325,6 +327,25 @@ public void SetSpaceIsKeyword() if ((opt.UserInputControls & UserInputControls.AnyBlankTerminatesInput) == UserInputControls.AnyBlankTerminatesInput) opt.UserInputControls ^= UserInputControls.AnyBlankTerminatesInput; // 空格或回车,结束jig } + + + #region 注释数据 + /// + /// 注释数据,可以在缩放的时候不受影响 + /// + public DynamicDimensionDataCollection DimensionEntitys; + + /// + /// 重写注释数据 + /// + /// + /// + protected override DynamicDimensionDataCollection GetDynamicDimensionData(double dimScale) + { + base.GetDynamicDimensionData(dimScale); + return DimensionEntitys; + } + #endregion } #if false -- Gitee From d13bf30e96bc34b8b9fdbcb586751cd29ff85de8 Mon Sep 17 00:00:00 2001 From: liuqihong <540762622@qq.com> Date: Mon, 5 Sep 2022 16:15:43 +0800 Subject: [PATCH 475/675] =?UTF-8?q?=E4=BF=AE=E6=94=B9dispose=E7=9A=84?= =?UTF-8?q?=E6=9A=B4=E9=9C=B2=E9=97=AE=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../ExtensionMethod/JigEx.cs | 5 ++++ src/IFoxCAD.Cad.Shared/Runtime/DBTrans.cs | 27 +++++++++---------- src/IFoxCAD.Cad.Shared/Runtime/IAutoGo.cs | 1 + src/IFoxCAD.LoadForm/AssemblyDependent.cs | 4 +-- 4 files changed, 20 insertions(+), 17 deletions(-) diff --git a/src/IFoxCAD.Cad.Shared/ExtensionMethod/JigEx.cs b/src/IFoxCAD.Cad.Shared/ExtensionMethod/JigEx.cs index 9341771..07d5813 100644 --- a/src/IFoxCAD.Cad.Shared/ExtensionMethod/JigEx.cs +++ b/src/IFoxCAD.Cad.Shared/ExtensionMethod/JigEx.cs @@ -346,6 +346,11 @@ protected override DynamicDimensionDataCollection GetDynamicDimensionData(double return DimensionEntitys; } #endregion + + protected override void ViewportDraw(ViewportDraw draw) + { + base.ViewportDraw(draw); + } } #if false diff --git a/src/IFoxCAD.Cad.Shared/Runtime/DBTrans.cs b/src/IFoxCAD.Cad.Shared/Runtime/DBTrans.cs index 77bc62d..950c542 100644 --- a/src/IFoxCAD.Cad.Shared/Runtime/DBTrans.cs +++ b/src/IFoxCAD.Cad.Shared/Runtime/DBTrans.cs @@ -17,10 +17,6 @@ public class DBTrans : IDisposable /// private readonly DocumentLock? documentLock; /// - /// 是否释放资源 - /// - private bool IsDisposed; - /// /// 是否提交事务 /// private readonly bool _commit; @@ -197,7 +193,7 @@ public DBTrans(string fileName, Database.ReadDwgFile(_fileName, fileShare, true, password); #else - Database.ReadDwgFile(_fileName, openMode,true, password); + Database.ReadDwgFile(_fileName, openMode, true, password); #endif } Database.CloseInput(true); @@ -444,7 +440,7 @@ public void SaveFile(DwgVersion version = DwgVersion.AC1800, } // 文件名缺失时 - if (!creatFlag && + if (!creatFlag && string.IsNullOrEmpty(Path.GetFileName(saveAsFile).Trim())) creatFlag = true; } @@ -584,12 +580,14 @@ public void Commit() Dispose(true); } + public bool IsDisposed { get; private set; } = false; + /// /// 手动调用释放 /// public void Dispose() { - Dispose(disposing: true); + Dispose(true); GC.SuppressFinalize(this); } @@ -598,18 +596,17 @@ public void Dispose() /// ~DBTrans() { - Dispose(disposing: false); + Dispose(false); } protected virtual void Dispose(bool disposing) { /* 事务dispose流程: * 1. 根据传入的参数确定是否提交,true为提交,false为不提交 - * 2. 根据IsDisposed的值确定是否重复dispose,false为首次dispose - * 3. 如果锁文档就将文档锁dispose - * 4. 不管是否提交,既然进入dispose,就要将事务栈的当前事务弹出 + * 2. 如果锁文档就将文档锁dispose + * 3. 不管是否提交,既然进入dispose,就要将事务栈的当前事务弹出 * 注意这里的事务栈不是cad的事务管理器,而是dbtrans的事务 - * 5. 清理非托管的字段 + * 4. 清理非托管的字段 */ // 不重复释放,并设置已经释放 @@ -628,14 +625,14 @@ protected virtual void Dispose(bool disposing) Transaction.Abort(); } - // 调用cad事务的dispose进行销毁 + // 将cad事务进行销毁 if (!Transaction.IsDisposed) Transaction.Dispose(); - // 调用文档锁dispose + // 将文档锁销毁 documentLock?.Dispose(); - // 将事务栈的当前dbtrans弹栈 + // 将当前事务栈弹栈 dBTrans.Pop(); } #endregion diff --git a/src/IFoxCAD.Cad.Shared/Runtime/IAutoGo.cs b/src/IFoxCAD.Cad.Shared/Runtime/IAutoGo.cs index 62bdbb0..49193ff 100644 --- a/src/IFoxCAD.Cad.Shared/Runtime/IAutoGo.cs +++ b/src/IFoxCAD.Cad.Shared/Runtime/IAutoGo.cs @@ -145,6 +145,7 @@ public void Terminate() } catch (System.Exception e) { + Env.Printl(e.Message); Debugger.Break(); } } diff --git a/src/IFoxCAD.LoadForm/AssemblyDependent.cs b/src/IFoxCAD.LoadForm/AssemblyDependent.cs index 067a2b9..c85b0a7 100644 --- a/src/IFoxCAD.LoadForm/AssemblyDependent.cs +++ b/src/IFoxCAD.LoadForm/AssemblyDependent.cs @@ -374,8 +374,8 @@ public void DebugMoveObjFiles(string? dllFullName, Action action) } #endregion - #region Dispose - public bool IsDisposed = false; + #region IDisposable接口相关函数 + public bool IsDisposed { get; private set; } = false; /// /// 手动调用释放 -- Gitee From 2ac2bd1d5748dcf1c6d3283235cbf6197ce2ff69 Mon Sep 17 00:00:00 2001 From: liuqihong <540762622@qq.com> Date: Tue, 6 Sep 2022 15:39:29 +0800 Subject: [PATCH 476/675] =?UTF-8?q?=E5=B1=8F=E8=94=BD=E6=97=A0=E6=B5=8B?= =?UTF-8?q?=E8=AF=95=E7=9A=84=E4=B8=9C=E8=A5=BF?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/IFoxCAD.Cad.Shared/ExtensionMethod/JigEx.cs | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/src/IFoxCAD.Cad.Shared/ExtensionMethod/JigEx.cs b/src/IFoxCAD.Cad.Shared/ExtensionMethod/JigEx.cs index 07d5813..a7e6dc1 100644 --- a/src/IFoxCAD.Cad.Shared/ExtensionMethod/JigEx.cs +++ b/src/IFoxCAD.Cad.Shared/ExtensionMethod/JigEx.cs @@ -238,8 +238,8 @@ protected override SamplerStatus Sampler(JigPrompts prompts) } /// - /// 重绘已在数据库的图元 - /// + /// 重绘已在数据库的图元 + /// /// 0x01 此处不加入newEntity的,它们在构造函数的参数回调处加入,它们会进行频繁new和Dispose从而避免遗忘释放
    /// 0x02 此处用于重绘已经在数据的图元
    /// 0x03 此处用于图元亮显暗显,因为会被重绘冲刷掉所以独立出来不重绘,它们也往往已经存在数据库的 @@ -347,10 +347,11 @@ protected override DynamicDimensionDataCollection GetDynamicDimensionData(double } #endregion - protected override void ViewportDraw(ViewportDraw draw) - { - base.ViewportDraw(draw); - } + // 此处无测试 + //protected override void ViewportDraw(ViewportDraw draw) + //{ + // base.ViewportDraw(draw); + //} } #if false -- Gitee From 16fc013ac8e7e5d1616c958ddf095770a453196d Mon Sep 17 00:00:00 2001 From: liuqihong <540762622@qq.com> Date: Tue, 6 Sep 2022 16:32:45 +0800 Subject: [PATCH 477/675] =?UTF-8?q?=E5=B0=86=E7=8E=AF=E5=A2=83=E5=8F=98?= =?UTF-8?q?=E9=87=8F=E7=BB=9F=E4=B8=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/IFoxCAD.Cad.Shared/Runtime/Env.cs | 77 +++++++++++++++------------ tests/TestShared/CmdINI.cs | 11 +++- 2 files changed, 53 insertions(+), 35 deletions(-) diff --git a/src/IFoxCAD.Cad.Shared/Runtime/Env.cs b/src/IFoxCAD.Cad.Shared/Runtime/Env.cs index 13dc43a..acf8cae 100644 --- a/src/IFoxCAD.Cad.Shared/Runtime/Env.cs +++ b/src/IFoxCAD.Cad.Shared/Runtime/Env.cs @@ -492,13 +492,22 @@ public static void SetVar(string? varName, object? value, bool echo = true) #endif /// - /// 读取acad环境变量 + /// 读取acad环境变量
    + /// 也能获取win环境变量 ///
    /// /// 返回值从不为null,需判断 - public static string AcedGetEnv(string? name) + public static string GetEnv(string? name) { - var sbRes = new StringBuilder(1024); + // acad2008注册表路径: + // 计算机\HKEY_CURRENT_USER\SOFTWARE\Autodesk\AutoCAD\R17.1\ACAD - 6001:804\FixedProfile\General + // 用户: 计算机\HKEY_CURRENT_USER\Environment + // 系统: 计算机\HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\Session Manager\Environment + + //Path的长度非常长/可用内存 (最新格式) 1 MB (标准格式) + //https://docs.microsoft.com/zh-cn/windows/win32/sysinfo/registry-element-size-limits + + var sbRes = new StringBuilder(1024 * 1024); _ = AcedGetEnv(name, sbRes); return sbRes.ToString(); } @@ -511,35 +520,35 @@ public static string AcedGetEnv(string? name) /// /// /// - public static int AcedSetEnv(string? name, string? var) + public static int SetEnv(string? name, string? var) { - // acad2008注册表路径: - // 计算机\HKEY_CURRENT_USER\SOFTWARE\Autodesk\AutoCAD\R17.1\ACAD - 6001:804\FixedProfile\General return AcedSetEnv(name, new StringBuilder(var)); } #endregion - #region win环境变量 - /// - /// 获取系统环境变量 - /// - /// 变量名 - /// 指定的环境变量的值;或者如果找不到环境变量,则返回 null - public static string? GetEnv(string? var) - { - // 从当前进程或者从当前用户或本地计算机的 Windows 操作系统注册表项检索环境变量的值 - return Environment.GetEnvironmentVariable(var); - } - /// - /// 设置系统环境变量 - /// - /// 变量名 - /// 变量值 - public static void SetEnv(string? var, string? value) - { - // 创建、修改或删除当前进程中或者为当前用户或本地计算机保留的 Windows 操作系统注册表项中存储的环境变量 - Environment.SetEnvironmentVariable(var, value); - } + #region win环境变量/由于 Aced的 能够同时获取此变量与cad内的,所以废弃 + ///// + ///// 获取系统环境变量 + ///// + ///// 变量名 + ///// 指定的环境变量的值;或者如果找不到环境变量,则返回 null + //public static string? GetEnv(string? var) + //{ + // // 从当前进程或者从当前用户或本地计算机的 Windows 操作系统注册表项检索环境变量的值 + // // 用户: 计算机\HKEY_CURRENT_USER\Environment + // // 系统: 计算机\HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\Session Manager\Environment + // return Environment.GetEnvironmentVariable(var); + //} + ///// + ///// 设置系统环境变量 + ///// + ///// 变量名 + ///// 变量值 + //public static void SetEnv(string? var, string? value) + //{ + // // 创建、修改或删除当前进程中或者为当前用户或本地计算机保留的 Windows 操作系统注册表项中存储的环境变量 + // Environment.SetEnvironmentVariable(var, value); + //} #endregion @@ -565,11 +574,11 @@ public static void SetEnv(string? var, string? value) public static DwgVersion GetDefaultDwgVersion() { DwgVersion version; - var ffs = Env.AcedGetEnv("DefaultFormatForSave"); + var ffs = Env.GetEnv("DefaultFormatForSave"); version = ffs switch { "1" => DwgVersion.AC1009,// R12/LT12 dxf - "8" => DwgVersion.AC1014,// R14/LT98/LT97 dwg + "8" => DwgVersion.AC1014,// R14/LT98/LT97 dwg "12" => DwgVersion.AC1015,// 2000 dwg "13" => DwgVersion.AC1800a,// 2000 dxf "24" => DwgVersion.AC1800,// 2004 dwg @@ -583,7 +592,7 @@ public static DwgVersion GetDefaultDwgVersion() "60" => (DwgVersion)31,// 2013 dwg DwgVersion.AC1027 "61" => (DwgVersion)32,// 2013 dxf "64" => (DwgVersion)33,// 2018 dwg DwgVersion.AC1032 - "65" => (DwgVersion)34,// 2018 dxf + "65" => (DwgVersion)34,// 2018 dxf _ => throw new NotImplementedException(),//提醒维护 }; return version; @@ -604,7 +613,7 @@ public static bool IsDxfVersion(this DwgVersion dwgVersion) 28 => true,// 2007 dxf 30 => true,// 2010 dxf 32 => true,// 2013 dxf - 34 => true,// 2018 dxf + 34 => true,// 2018 dxf _ => false, }; return result; @@ -704,14 +713,14 @@ public static Dictionary SaveCadVar(Dictionary a } // 判断是否为系统变量 - var envstr = Env.AcedGetEnv(item.Key); + var envstr = Env.GetEnv(item.Key); if (!string.IsNullOrEmpty(envstr)) { - Env.AcedSetEnv(item.Key, item.Value); + Env.SetEnv(item.Key, item.Value); dict.Add(item.Key, envstr); } } return dict; } #endregion -} +} \ No newline at end of file diff --git a/tests/TestShared/CmdINI.cs b/tests/TestShared/CmdINI.cs index c3c58b4..1370267 100644 --- a/tests/TestShared/CmdINI.cs +++ b/tests/TestShared/CmdINI.cs @@ -137,10 +137,19 @@ public partial class Test [CommandMethod(nameof(Test_GetEnv))] public static void Test_GetEnv() { - var dir = Env.AcedGetEnv("PrinterConfigDir"); + var dir = Env.GetEnv("PrinterConfigDir"); Env.Printl("pc3打印机位置:" + dir); + + Env.SetEnv("abc", "656"); + + var obj = Env.GetEnv("abc"); + Env.Printl("GetEnv:" + obj); + + Env.Printl("GetEnv:" + Env.GetEnv("abc")); + Env.Printl("GetEnv PATH:" + Env.GetEnv("PATH")); } + #if !NET35 && !NET40 // 通过此功能获取全部变量,尚不清楚此处如何设置,没有通过测试 -- Gitee From 1344fe4216ca82bc2242963c5eedc5e786a4ce2b Mon Sep 17 00:00:00 2001 From: liuqihong <540762622@qq.com> Date: Tue, 6 Sep 2022 16:36:09 +0800 Subject: [PATCH 478/675] =?UTF-8?q?=E4=BC=98=E5=8C=96=E6=B3=A8=E9=87=8A?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/IFoxCAD.Cad.Shared/Runtime/Env.cs | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/src/IFoxCAD.Cad.Shared/Runtime/Env.cs b/src/IFoxCAD.Cad.Shared/Runtime/Env.cs index acf8cae..b3cf00d 100644 --- a/src/IFoxCAD.Cad.Shared/Runtime/Env.cs +++ b/src/IFoxCAD.Cad.Shared/Runtime/Env.cs @@ -495,17 +495,18 @@ public static void SetVar(string? varName, object? value, bool echo = true) /// 读取acad环境变量
    /// 也能获取win环境变量 ///
    - /// + /// 变量名 /// 返回值从不为null,需判断 public static string GetEnv(string? name) { - // acad2008注册表路径: - // 计算机\HKEY_CURRENT_USER\SOFTWARE\Autodesk\AutoCAD\R17.1\ACAD - 6001:804\FixedProfile\General + // 它将混合查询以下路径: + // acad2008注册表路径: 计算机\HKEY_CURRENT_USER\SOFTWARE\Autodesk\AutoCAD\R17.1\ACAD - 6001:804\FixedProfile\General // 用户: 计算机\HKEY_CURRENT_USER\Environment // 系统: 计算机\HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\Session Manager\Environment - //Path的长度非常长/可用内存 (最新格式) 1 MB (标准格式) - //https://docs.microsoft.com/zh-cn/windows/win32/sysinfo/registry-element-size-limits + // GetEnv("Path")长度很长: + // 可用内存 (最新格式) 1 MB (标准格式) + // https://docs.microsoft.com/zh-cn/windows/win32/sysinfo/registry-element-size-limits var sbRes = new StringBuilder(1024 * 1024); _ = AcedGetEnv(name, sbRes); @@ -517,8 +518,8 @@ public static string GetEnv(string? name) /// 它是不会报错的,但是直接设置会写入注册表的,
    /// 如果是设置高低版本cad不同的变量,建议先读取判断再设置
    ///
    - /// - /// + /// 变量名 + /// 变量值 /// public static int SetEnv(string? name, string? var) { -- Gitee From a6f691b5fc8c7ad381c6dfcb3d51a66fc3da28bf Mon Sep 17 00:00:00 2001 From: liuqihong <540762622@qq.com> Date: Tue, 6 Sep 2022 20:19:58 +0800 Subject: [PATCH 479/675] =?UTF-8?q?=E9=BB=98=E8=AE=A4=E7=BB=91=E5=AE=9A?= =?UTF-8?q?=E6=96=B9=E5=BC=8F=E4=B8=8Ecad=E4=BF=9D=E6=8C=81=E4=B8=80?= =?UTF-8?q?=E8=87=B4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../ExtensionMethod/XrefEx.cs | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/src/IFoxCAD.Cad.Shared/ExtensionMethod/XrefEx.cs b/src/IFoxCAD.Cad.Shared/ExtensionMethod/XrefEx.cs index b8e4f03..cb57d30 100644 --- a/src/IFoxCAD.Cad.Shared/ExtensionMethod/XrefEx.cs +++ b/src/IFoxCAD.Cad.Shared/ExtensionMethod/XrefEx.cs @@ -52,10 +52,11 @@ public class XrefFactory : IXrefBindModes /// - /// bind时候是否为插入模式
    - /// 默认可能和双美元符号有关 + /// 绑定模式和双美元符号相关(与cad保持相同的默认)
    + /// 为绑定模式,产生双美元; + /// 为插入模式,块重名会以本图覆盖; ///
    - public bool InsertBind = true; + public bool BindOrInsert = false; /// /// bind时候是否拆离参照
    /// 默认:学官方的绑定后自动拆离 @@ -72,7 +73,6 @@ public class XrefFactory : IXrefBindModes /// 其他项有异常:
    ///
    public SymModes SymModesBind = SymModes.LayerTable; - #endregion #region 构造 @@ -318,9 +318,9 @@ void DoubleBind() var bindIds = GetBindIds(); var xbindIds = GetXBindIds(); if (xbindIds.Count > 0) - _tr.Database.XBindXrefs(xbindIds, InsertBind); + _tr.Database.XBindXrefs(xbindIds, BindOrInsert); if (bindIds.Count > 0) - _tr.Database.BindXrefs(bindIds, InsertBind); + _tr.Database.BindXrefs(bindIds, BindOrInsert); // 内部删除嵌套参照的块操作 @@ -484,7 +484,7 @@ public XrefPath(BlockReference brf, DBTrans tr) } #endregion - #region 静态函数 + #region 静态函数 /// /// 获取相对路径或者绝对路径 /// 参考链接 @@ -522,7 +522,7 @@ public XrefPath(BlockReference brf, DBTrans tr) /// 绝对路径 /// 相对关系 /// - /// StringHelper.GetRelativePath("G:\\A1.项目\\20190920金山谷黄宅\\01.饰施图\\03.平面图", + /// StringHelper.GetRelativePath("G:\\A1.项目\\20190920金山谷黄宅\\01.饰施图\\03.平面图", /// "G:\\A1.项目\\20190920金山谷黄宅\\01.饰施图\\01.辅助文件\\图框\\A3图框.dwg"); public static string GetRelativePath(string strDbPath, string strXrefPath) { @@ -594,7 +594,7 @@ static string GetRelativePath(string directory, string file) /// 相对路径->绝对路径 /// /// 文件夹路径 - /// 相对关系:有..的 + /// 相对关系:有..的 /// 完整路径 /// Date: Thu, 8 Sep 2022 15:54:06 +0800 Subject: [PATCH 480/675] =?UTF-8?q?=E9=85=8D=E7=BD=AE=E4=B8=AD=E5=BF=83?= =?UTF-8?q?=E6=B7=BB=E5=8A=A0=E5=8E=BB=E6=95=99=E8=82=B2=E7=89=88=E5=8A=9F?= =?UTF-8?q?=E8=83=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../IFoxCAD.Cad.Shared.projitems | 1 + src/IFoxCAD.Cad.Shared/Runtime/AssemInfo.cs | 9 +- .../Runtime/AutoRegAssem.cs | 5 +- src/IFoxCAD.Cad.Shared/Runtime/EMR.cs | 135 ++++++++++++++++++ src/IFoxCAD.Cad.Shared/Runtime/Env.cs | 5 + src/IFoxCAD.Cad.Shared/Runtime/IAutoGo.cs | 1 - 6 files changed, 151 insertions(+), 5 deletions(-) create mode 100644 src/IFoxCAD.Cad.Shared/Runtime/EMR.cs diff --git a/src/IFoxCAD.Cad.Shared/IFoxCAD.Cad.Shared.projitems b/src/IFoxCAD.Cad.Shared/IFoxCAD.Cad.Shared.projitems index b883721..d717aa8 100644 --- a/src/IFoxCAD.Cad.Shared/IFoxCAD.Cad.Shared.projitems +++ b/src/IFoxCAD.Cad.Shared/IFoxCAD.Cad.Shared.projitems @@ -61,6 +61,7 @@ + diff --git a/src/IFoxCAD.Cad.Shared/Runtime/AssemInfo.cs b/src/IFoxCAD.Cad.Shared/Runtime/AssemInfo.cs index 6d916ff..337ac0d 100644 --- a/src/IFoxCAD.Cad.Shared/Runtime/AssemInfo.cs +++ b/src/IFoxCAD.Cad.Shared/Runtime/AssemInfo.cs @@ -73,7 +73,10 @@ public enum AutoRegConfig /// ReflectionInterface = 4, - All = Regedit | ReflectionAttribute | ReflectionInterface, -} - + /// + /// 移除教育版 + /// + RemoveEMR = 8, + All = Regedit | ReflectionAttribute | ReflectionInterface | RemoveEMR, +} \ No newline at end of file diff --git a/src/IFoxCAD.Cad.Shared/Runtime/AutoRegAssem.cs b/src/IFoxCAD.Cad.Shared/Runtime/AutoRegAssem.cs index c6f861f..ece5123 100644 --- a/src/IFoxCAD.Cad.Shared/Runtime/AutoRegAssem.cs +++ b/src/IFoxCAD.Cad.Shared/Runtime/AutoRegAssem.cs @@ -65,6 +65,9 @@ public AutoRegAssem(AutoRegConfig autoRegConfig) RegApp(); } + if ((autoRegConfig & AutoRegConfig.RemoveEMR) == AutoRegConfig.RemoveEMR) + EMR.Remove(); + // 实例化了 AutoClass 之后会自动执行 IFoxAutoGo 接口下面的类, // 以及自动执行特性 [IFoxInitialize] // 类库用户不在此处进行其他代码,而是实现特性 @@ -169,4 +172,4 @@ public void Terminate() { } _autoRef.Terminate(); } #endregion RegApp -} +} \ No newline at end of file diff --git a/src/IFoxCAD.Cad.Shared/Runtime/EMR.cs b/src/IFoxCAD.Cad.Shared/Runtime/EMR.cs new file mode 100644 index 0000000..8c3db8e --- /dev/null +++ b/src/IFoxCAD.Cad.Shared/Runtime/EMR.cs @@ -0,0 +1,135 @@ +#if true +namespace IFoxCAD.Cad; + +using System.Diagnostics; + +// 作者: [VB.net]福萝卜 莱昂纳多·胖子 +// Email:oneeshine@163.com +// QQ: 461884072 +// 测试 2006-2019+ + +/// +/// 去教育版 +/// +/// +internal class EMR +{ + /// + /// 释放库 + /// + /// 句柄 + /// + [DllImport("kernel32.dll", CharSet = CharSet.Auto)] + static extern IntPtr FreeLibrary(IntPtr loadLibraryIntPtr); + + /// + /// 获取一个应用程序或dll的模块句柄,要求已经载入 + /// + /// + /// + [DllImport("kernel32.dll", CharSet = CharSet.Auto, SetLastError = true)] + static extern IntPtr GetModuleHandle(string name); + + /// + /// 获取要引入的函数,将符号名或标识号转换为DLL内部地址 + /// + /// exe/dll句柄 + /// 接口名 + /// + [DllImport("kernel32.dll", SetLastError = true, CharSet = CharSet.Ansi, ExactSpelling = true)] + static extern IntPtr GetProcAddress(IntPtr hModule, string procName); + + /// + /// 虚拟保护 + /// + /// + /// + /// + /// + /// + [DllImport("kernel32.dll", CharSet = CharSet.Auto, SetLastError = true)] + static extern bool VirtualProtect(IntPtr lpAddress, IntPtr dwSize, uint flNewProtect, ref uint lpflOldProtect); + + /// + /// 去教育版 + /// + /// + public static string Remove(bool echoe = false) + { + var dllName = Env.GetAcapVersionDll(); + IntPtr moduleHandle = GetModuleHandle(dllName); + if (moduleHandle == IntPtr.Zero) + return typeof(EMR).FullName + "." + nameof(Remove) + "找不到模块:" + dllName; + + string funcname = System.Text.Encoding.Unicode.GetString(new byte[] { 63 }); + if (IntPtr.Size == 4) + funcname += "isEMR@AcDbDatabase@@QBE_NXZ"; + else + funcname += "isEMR@AcDbDatabase@@QEBA_NXZ"; + + IntPtr funcAdress = GetProcAddress(moduleHandle, funcname); + if (funcAdress == IntPtr.Zero) + return "无法找指定函数:" + funcname; + + IntPtr ptr; + if (IntPtr.Size == 4) + ptr = new IntPtr(funcAdress.ToInt32() + 3); + else + ptr = new IntPtr(funcAdress.ToInt64() + 4); + + if (!CheckFunc(ref ptr, 51, 2) && echoe)//08 通过此处 + Debug.WriteLine("无法验证函数体:0x33"); + IntPtr destPtr = ptr; + + if (!CheckFunc(ref ptr, 57, 6) && echoe)//08 无法通过此处,所以只是打印提示 + Debug.WriteLine("无法验证函数体:0x39"); + if (!CheckFunc(ref ptr, 15, 2) && echoe)//08 无法通过此处,所以只是打印提示 + Debug.WriteLine("无法验证函数体:0x0F"); + + uint flag = default; + uint tccc = default; + + IntPtr ip100 = new(100); + if (!VirtualProtect(destPtr, ip100, 64, ref flag))// 修改内存权限 + return "内存模式修改失败!"; + + Marshal.WriteByte(destPtr, 137); + VirtualProtect(destPtr, ip100, flag, ref tccc);// 恢复内存权限 + + return string.Empty; + } + + /// + /// 验证函数体 + /// + /// + /// + /// + /// + static bool CheckFunc(ref IntPtr adress, byte val, int len) + { + if (Marshal.ReadByte(adress) == 233) + { + if (IntPtr.Size == 4) + { + var pass = Marshal.ReadInt32(new IntPtr(adress.ToInt32() + 1)); + adress = new IntPtr(adress.ToInt32() + pass + 5); + } + else + { + var pass = Marshal.ReadInt64(new IntPtr(adress.ToInt64() + 1)); + adress = new IntPtr(adress.ToInt64() + pass + 5); + } + } + if (Marshal.ReadByte(adress) == val) + { + if (IntPtr.Size == 4) + adress = new IntPtr(adress.ToInt32() + len); + else + adress = new IntPtr(adress.ToInt64() + len); + return true; + } + return false; + } +} +#endif \ No newline at end of file diff --git a/src/IFoxCAD.Cad.Shared/Runtime/Env.cs b/src/IFoxCAD.Cad.Shared/Runtime/Env.cs index b3cf00d..572e3e8 100644 --- a/src/IFoxCAD.Cad.Shared/Runtime/Env.cs +++ b/src/IFoxCAD.Cad.Shared/Runtime/Env.cs @@ -650,6 +650,11 @@ public static int GetAcadVersion() }; return acarVarNum; } + + public static string GetAcapVersionDll(string str = "acdb") + { + return str + Acap.Version.Major + ".dll"; + } #endregion diff --git a/src/IFoxCAD.Cad.Shared/Runtime/IAutoGo.cs b/src/IFoxCAD.Cad.Shared/Runtime/IAutoGo.cs index 49193ff..c52e7c9 100644 --- a/src/IFoxCAD.Cad.Shared/Runtime/IAutoGo.cs +++ b/src/IFoxCAD.Cad.Shared/Runtime/IAutoGo.cs @@ -267,7 +267,6 @@ void GetInterfaceFunctions(List runClassList, string methodName) break; } }, _dllName); - } /// -- Gitee From 50b2e417118ece74a515f31d93e35c7248b0c65a Mon Sep 17 00:00:00 2001 From: liuqihong <540762622@qq.com> Date: Fri, 9 Sep 2022 07:15:52 +0800 Subject: [PATCH 481/675] =?UTF-8?q?=E4=BF=AE=E6=94=B9=E7=BB=88=E6=AD=A2?= =?UTF-8?q?=E5=BE=AA=E7=8E=AF=E5=87=BA=E9=94=99?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../ExtensionMethod/Enums.cs | 38 ++++++++++------- src/IFoxCAD.Cad.Shared/Runtime/SymbolTable.cs | 6 +-- tests/TestConsole/Helper.cs | 42 +++++++++++++++++++ tests/TestConsole/Program.cs | 15 +++++++ 4 files changed, 82 insertions(+), 19 deletions(-) create mode 100644 tests/TestConsole/Helper.cs diff --git a/src/IFoxCAD.Cad.Shared/ExtensionMethod/Enums.cs b/src/IFoxCAD.Cad.Shared/ExtensionMethod/Enums.cs index 6049adb..2dd94b5 100644 --- a/src/IFoxCAD.Cad.Shared/ExtensionMethod/Enums.cs +++ b/src/IFoxCAD.Cad.Shared/ExtensionMethod/Enums.cs @@ -1,20 +1,5 @@ namespace IFoxCAD.Cad; -/// -/// 循环状态 -/// -public enum LoopState -{ - /// - /// 继续执行 - /// - Go, - /// - /// 中断执行 - /// - Break -} - /// /// 参照路径转换 /// @@ -186,4 +171,25 @@ public enum FontTTF Arial, [Description("Romans")] Romans -} \ No newline at end of file +} + + +public class LoopState +{ + const int PLS_NONE = 0; + const int PLS_EXCEPTIONAL = 1; + const int PLS_BROKEN = 2; + const int PLS_STOPPED = 4; + const int PLS_CANCELED = 8; + + private volatile int _LoopStateFlags = PLS_NONE; + + public bool IsRun => _LoopStateFlags == PLS_NONE; + public bool IsCancel => _LoopStateFlags == PLS_CANCELED; + public bool IsExceptional => _LoopStateFlags == PLS_EXCEPTIONAL; + + public bool IsBreak => (_LoopStateFlags & PLS_BROKEN) == PLS_BROKEN; + public bool IsStop => (_LoopStateFlags & PLS_STOPPED) == PLS_STOPPED; + public void Stop() => _LoopStateFlags = PLS_STOPPED; + public void Break() => _LoopStateFlags = PLS_BROKEN; +} \ No newline at end of file diff --git a/src/IFoxCAD.Cad.Shared/Runtime/SymbolTable.cs b/src/IFoxCAD.Cad.Shared/Runtime/SymbolTable.cs index a619836..8f96bb9 100644 --- a/src/IFoxCAD.Cad.Shared/Runtime/SymbolTable.cs +++ b/src/IFoxCAD.Cad.Shared/Runtime/SymbolTable.cs @@ -334,7 +334,7 @@ public void ForEach(Action action, OpenMode openMode = OpenMode.ForRead, bool checkIdOk = false) { - LoopState state = LoopState.Go;/*这种方式比Action改Func更友好*/ + LoopState state = new();/*这种方式比Action改Func更友好*/ foreach (var id in this) { if (checkIdOk && !id.IsOk()) @@ -342,7 +342,7 @@ public void ForEach(Action action, var record = GetRecord(id, openMode); if (record is not null) action(record, state); - if (state == LoopState.Break) + if (state.IsBreak) break; } } @@ -363,4 +363,4 @@ IEnumerator IEnumerable.GetEnumerator() return GetEnumerator(); } #endregion -} +} \ No newline at end of file diff --git a/tests/TestConsole/Helper.cs b/tests/TestConsole/Helper.cs new file mode 100644 index 0000000..763970a --- /dev/null +++ b/tests/TestConsole/Helper.cs @@ -0,0 +1,42 @@ +namespace TestConsole; + +public class LoopState +{ + const int PLS_NONE = 0; + const int PLS_EXCEPTIONAL = 1; + const int PLS_BROKEN = 2; + const int PLS_STOPPED = 4; + const int PLS_CANCELED = 8; + + private volatile int _LoopStateFlags = PLS_NONE; + + public bool IsRun => _LoopStateFlags == PLS_NONE; + public bool IsCancel => _LoopStateFlags == PLS_CANCELED; + public bool IsExceptional => _LoopStateFlags == PLS_EXCEPTIONAL; + + public bool IsBreak => (_LoopStateFlags & PLS_BROKEN) == PLS_BROKEN; + public bool IsStop => (_LoopStateFlags & PLS_STOPPED) == PLS_STOPPED; + public void Stop() => _LoopStateFlags = PLS_STOPPED; + public void Break() => _LoopStateFlags = PLS_BROKEN; +} + +public static class Helper +{ + public static void ForEach(this IEnumerable ints, Action action) + { + LoopState state = new(); + foreach (var item in ints) + { + action(item, state); + if (state.IsBreak) + break; + } + + //int forNum = 5; + //var result = Parallel.For(0, forNum, (int i, ParallelLoopState pls) => { + // if (i > 2) + // pls.Break(); + // Task.Delay(10).Wait(); + //}); + } +} \ No newline at end of file diff --git a/tests/TestConsole/Program.cs b/tests/TestConsole/Program.cs index 7d37d3b..9db5e76 100644 --- a/tests/TestConsole/Program.cs +++ b/tests/TestConsole/Program.cs @@ -3,6 +3,21 @@ using System.Text; using TestConsole; + +List list = new List(); +list.Add(1); +list.Add(2); +list.Add(3); +list.Add(4); +list.Add(5); +list.ForEach((x, loop) => { + if (x == 3) + loop.Break(); + Console.WriteLine(x); +}); + + + // 乱序 Console.WriteLine(PlugIn.JoinBox.PrintNote()); Console.WriteLine(PlugIn.Lisp.PrintNote());// 这里先交换顺序来试试能不能成功 -- Gitee From 9a13d6885a1bab9a405a32d2b194f415d0e5ddbd Mon Sep 17 00:00:00 2001 From: liuqihong <540762622@qq.com> Date: Fri, 9 Sep 2022 18:45:57 +0800 Subject: [PATCH 482/675] =?UTF-8?q?=E4=BC=98=E5=8C=96=E7=A7=BB=E9=99=A4?= =?UTF-8?q?=E6=95=99=E8=82=B2=E7=89=88?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Runtime/{EMR.cs => AcadEMR.cs} | 44 ++++++++++++------- .../Runtime/AutoRegAssem.cs | 2 +- 2 files changed, 30 insertions(+), 16 deletions(-) rename src/IFoxCAD.Cad.Shared/Runtime/{EMR.cs => AcadEMR.cs} (77%) diff --git a/src/IFoxCAD.Cad.Shared/Runtime/EMR.cs b/src/IFoxCAD.Cad.Shared/Runtime/AcadEMR.cs similarity index 77% rename from src/IFoxCAD.Cad.Shared/Runtime/EMR.cs rename to src/IFoxCAD.Cad.Shared/Runtime/AcadEMR.cs index 8c3db8e..96bb71d 100644 --- a/src/IFoxCAD.Cad.Shared/Runtime/EMR.cs +++ b/src/IFoxCAD.Cad.Shared/Runtime/AcadEMR.cs @@ -12,7 +12,7 @@ namespace IFoxCAD.Cad; /// 去教育版 /// /// -internal class EMR +internal class AcadEMR { /// /// 释放库 @@ -50,16 +50,21 @@ internal class EMR [DllImport("kernel32.dll", CharSet = CharSet.Auto, SetLastError = true)] static extern bool VirtualProtect(IntPtr lpAddress, IntPtr dwSize, uint flNewProtect, ref uint lpflOldProtect); + /// - /// 去教育版 + /// 移除教育版 /// - /// - public static string Remove(bool echoe = false) + /// 打印出错信息 + public static void Remove(bool echoes = false) { var dllName = Env.GetAcapVersionDll(); IntPtr moduleHandle = GetModuleHandle(dllName); if (moduleHandle == IntPtr.Zero) - return typeof(EMR).FullName + "." + nameof(Remove) + "找不到模块:" + dllName; + { + if (echoes) + Env.Printl(typeof(AcadEMR).FullName + "." + nameof(Remove) + "找不到模块:" + dllName); + return; + } string funcname = System.Text.Encoding.Unicode.GetString(new byte[] { 63 }); if (IntPtr.Size == 4) @@ -69,7 +74,11 @@ public static string Remove(bool echoe = false) IntPtr funcAdress = GetProcAddress(moduleHandle, funcname); if (funcAdress == IntPtr.Zero) - return "无法找指定函数:" + funcname; + { + if (echoes) + Env.Printl("无法找指定函数:" + funcname); + return; + } IntPtr ptr; if (IntPtr.Size == 4) @@ -77,26 +86,31 @@ public static string Remove(bool echoe = false) else ptr = new IntPtr(funcAdress.ToInt64() + 4); - if (!CheckFunc(ref ptr, 51, 2) && echoe)//08 通过此处 - Debug.WriteLine("无法验证函数体:0x33"); + if (!CheckFunc(ref ptr, 51, 2))//08 通过此处 + if (echoes) + Env.Printl("无法验证函数体:0x33"); IntPtr destPtr = ptr; - if (!CheckFunc(ref ptr, 57, 6) && echoe)//08 无法通过此处,所以只是打印提示 - Debug.WriteLine("无法验证函数体:0x39"); - if (!CheckFunc(ref ptr, 15, 2) && echoe)//08 无法通过此处,所以只是打印提示 - Debug.WriteLine("无法验证函数体:0x0F"); + if (!CheckFunc(ref ptr, 57, 6))//08 无法通过此处,所以只是打印提示 + if (echoes) + Env.Printl("无法验证函数体:0x39"); + if (!CheckFunc(ref ptr, 15, 2))//08 无法通过此处,所以只是打印提示 + if (echoes) + Env.Printl("无法验证函数体:0x0F"); uint flag = default; uint tccc = default; IntPtr ip100 = new(100); if (!VirtualProtect(destPtr, ip100, 64, ref flag))// 修改内存权限 - return "内存模式修改失败!"; + { + if (echoes) + Env.Printl("内存模式修改失败!"); + return; + } Marshal.WriteByte(destPtr, 137); VirtualProtect(destPtr, ip100, flag, ref tccc);// 恢复内存权限 - - return string.Empty; } /// diff --git a/src/IFoxCAD.Cad.Shared/Runtime/AutoRegAssem.cs b/src/IFoxCAD.Cad.Shared/Runtime/AutoRegAssem.cs index ece5123..fd3bacc 100644 --- a/src/IFoxCAD.Cad.Shared/Runtime/AutoRegAssem.cs +++ b/src/IFoxCAD.Cad.Shared/Runtime/AutoRegAssem.cs @@ -66,7 +66,7 @@ public AutoRegAssem(AutoRegConfig autoRegConfig) } if ((autoRegConfig & AutoRegConfig.RemoveEMR) == AutoRegConfig.RemoveEMR) - EMR.Remove(); + AcadEMR.Remove(); // 实例化了 AutoClass 之后会自动执行 IFoxAutoGo 接口下面的类, // 以及自动执行特性 [IFoxInitialize] -- Gitee From b0a6fb5b91ff20761dd502492911b7695c4f8eb9 Mon Sep 17 00:00:00 2001 From: liuqihong <540762622@qq.com> Date: Fri, 9 Sep 2022 18:51:29 +0800 Subject: [PATCH 483/675] =?UTF-8?q?=E4=BF=AE=E6=94=B9=E4=BA=8B=E5=8A=A1?= =?UTF-8?q?=E4=B8=AD=E7=9A=84task=E5=87=BD=E6=95=B0=E4=B8=8D=E8=83=BD?= =?UTF-8?q?=E7=9B=B4=E6=8E=A5=E8=8E=B7=E5=8F=96=E6=BF=80=E6=B4=BB=E7=9A=84?= =?UTF-8?q?=E6=95=B0=E6=8D=AE=E5=BA=93?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/IFoxCAD.Cad.Shared/Runtime/DBTrans.cs | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/src/IFoxCAD.Cad.Shared/Runtime/DBTrans.cs b/src/IFoxCAD.Cad.Shared/Runtime/DBTrans.cs index 950c542..270886f 100644 --- a/src/IFoxCAD.Cad.Shared/Runtime/DBTrans.cs +++ b/src/IFoxCAD.Cad.Shared/Runtime/DBTrans.cs @@ -43,7 +43,7 @@ public static DBTrans Top * 事务栈上面有事务,这个事务属于当前文档, * 那么直接提交原本事务然后再开一个(一直把栈前面的同数据库提交清空) * 那不就发生跨事务读取图元了吗?....否决 - * + * * 0x02 * 跨文档事务出错 Autodesk.AutoCAD.Runtime.Exception:“eNotFromThisDocument” * Curves.GetEntities()会从Top获取事务(Top会new一个),此时会是当前文档; @@ -52,7 +52,7 @@ public static DBTrans Top * 然后我新建了一个文档,再进行命令=>又进入Top,Top返回了前一个文档的事务 * 因此所以无法清理栈,所以Dispose不触发,导致无法刷新图元和Ctrl+Z出错 * 所以用AOP方式修复 - * + * * 0x03 * 经过艰苦卓绝的测试,aop模式由于不能断点调试,所以暂时放弃。 */ @@ -524,12 +524,10 @@ public void SaveFile(DwgVersion version = DwgVersion.AC1800, /// /// /// 备注:
    - /// /// 0x01 文字偏移问题主要出现线性引擎函数上面,
    /// 在 参照绑定/深度克隆 的底层共用此函数导致
    /// 0x02 后台是利用前台当前数据库进行处理的
    /// 0x03 跨进程通讯暂无测试(可能存在bug)
    - ///
    ///
    /// 委托 /// 开启单行文字偏移处理 @@ -547,16 +545,18 @@ public void Task(Action action, bool handlingDBTextDeviation = true) // 后台 // 这种情况发生在关闭了所有文档之后,进行跨进程通讯 - var dbBak = HostApplicationServices.WorkingDatabase; - if (dbBak == null) + // 此处要先获取激活的文档,不能直接获取当前数据库否则异常 + var dm = Acap.DocumentManager; + var doc = dm.MdiActiveDocument; + if (doc == null) { action.Invoke(); return; } - // 处理单行文字偏移 - // 前台绑定参照的时候不能用它,否则出现:
    + // 前台绑定参照的时候不能用它,否则抛出异常:eWasErased // 所以本函数自动识别前后台做处理 + var dbBak = doc.Database; HostApplicationServices.WorkingDatabase = Database; action.Invoke(); HostApplicationServices.WorkingDatabase = dbBak; @@ -636,4 +636,4 @@ protected virtual void Dispose(bool disposing) dBTrans.Pop(); } #endregion -} +} \ No newline at end of file -- Gitee From 7f7524753055eb364233cc91be19395d3d21e1fa Mon Sep 17 00:00:00 2001 From: liuqihong <540762622@qq.com> Date: Fri, 9 Sep 2022 18:53:51 +0800 Subject: [PATCH 484/675] =?UTF-8?q?=E5=8E=BB=E6=95=99=E8=82=B2=E7=89=88?= =?UTF-8?q?=E6=96=87=E4=BB=B6=E6=8C=87=E5=AE=9A=E4=B8=BAacad=E7=9A=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/IFoxCAD.Cad.Shared/IFoxCAD.Cad.Shared.projitems | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/IFoxCAD.Cad.Shared/IFoxCAD.Cad.Shared.projitems b/src/IFoxCAD.Cad.Shared/IFoxCAD.Cad.Shared.projitems index d717aa8..a890148 100644 --- a/src/IFoxCAD.Cad.Shared/IFoxCAD.Cad.Shared.projitems +++ b/src/IFoxCAD.Cad.Shared/IFoxCAD.Cad.Shared.projitems @@ -61,7 +61,7 @@ - + -- Gitee From 424b0a67591b9085c834d3b3a286c8ee1882bd14 Mon Sep 17 00:00:00 2001 From: DYH <1742647821@qq.com> Date: Sat, 10 Sep 2022 21:58:28 +0800 Subject: [PATCH 485/675] =?UTF-8?q?=E6=B7=BB=E5=8A=A0=E7=9E=AC=E6=80=81?= =?UTF-8?q?=E7=B1=BB?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../ExtensionMethod/JigExTransient.cs | 149 ++++++++++++++++++ 1 file changed, 149 insertions(+) create mode 100644 src/IFoxCAD.Cad/ExtensionMethod/JigExTransient.cs diff --git a/src/IFoxCAD.Cad/ExtensionMethod/JigExTransient.cs b/src/IFoxCAD.Cad/ExtensionMethod/JigExTransient.cs new file mode 100644 index 0000000..770739a --- /dev/null +++ b/src/IFoxCAD.Cad/ExtensionMethod/JigExTransient.cs @@ -0,0 +1,149 @@ +#if !ac2008 +namespace IFoxCAD.Cad; + +/// +/// 瞬态容器 +/// +public class JigExTransient : IDisposable +{ + #region 私有字段 + // 整数集,暂时不知道有什么意义 + IntegerCollection _integerCollection; + // 维护集合 + HashSet _entities; + #endregion + + #region 公开属性 + /// + /// 对象集合 + /// + public Entity[] Entities => _entities.ToArray(); + /// + /// 数量 + /// + public int Count => _entities.Count; + #endregion + + #region 构造函数 + /// + /// 瞬态容器 + /// + public JigExTransient() + { + _integerCollection = new(); + _entities = new(); + } + #endregion + + #region 方法 + /// + /// 判断瞬态容器里是否含有对象 + /// + /// 对象 + /// 含有返回true + public bool Contains(Entity ent) + { + return _entities.Contains(ent); + } + + /// + /// 向瞬态容器中添加对象 + /// + /// 图元 + /// 绘图模式 + public void Add(Entity ent, TransientDrawingMode tdm = TransientDrawingMode.Main) + { + if (_entities.Add(ent)) + { + TransientManager + .CurrentTransientManager + .AddTransient(ent, tdm, 128, _integerCollection); + } + } + + + /// + /// 从瞬态容器中移除图元 + /// + /// 已经加入瞬态容器的图元 + public void Remove(Entity ent) + { + if (!Contains(ent)) + return; + TransientManager + .CurrentTransientManager + .EraseTransient(ent, _integerCollection); + _entities.Remove(ent); + } + + /// + /// 清空瞬态容器并移除图元显示 + /// + public void Clear() + { + foreach (var ent in _entities) + { + TransientManager + .CurrentTransientManager + .EraseTransient(ent, _integerCollection); + } + _entities.Clear(); + } + + + /// + /// 更新单个显示 + /// + /// 已经加入瞬态容器的图元 + public void Update(Entity ent) + { + if (!Contains(ent)) + return; + TransientManager + .CurrentTransientManager + .UpdateTransient(ent, _integerCollection); + } + + /// + /// 更新全部显示 + /// + public void UpdateAll() + { + foreach (var ent in _entities) + Update(ent); + } + #endregion + + #region IDisposable接口相关函数 + public bool IsDisposed { get; private set; } = false; + + /// + /// 手动释放 + /// + public void Dispose() + { + Dispose(true); + GC.SuppressFinalize(this); + } + + /// + /// 析构函数调用释放 + /// + ~JigExTransient() + { + Dispose(false); + } + + /// + /// 销毁瞬态容器 + /// + protected virtual void Dispose(bool disposing) + { + if (IsDisposed) return; + IsDisposed = true; + + Clear();// 清空瞬态容器并移除对象在图纸上的显示 + } + #endregion +} +#endif \ No newline at end of file -- Gitee From 846c761638041715df3c38853b70d9e495a58621 Mon Sep 17 00:00:00 2001 From: liuqihong <540762622@qq.com> Date: Sat, 10 Sep 2022 22:10:54 +0800 Subject: [PATCH 486/675] =?UTF-8?q?=E7=9E=AC=E6=80=81=E7=B1=BB=E5=90=88?= =?UTF-8?q?=E5=B9=B6=E6=9C=AC=E5=88=86=E6=94=AF?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../ExtensionMethod/JigExTransient.cs | 149 ++++++++++++++++++ 1 file changed, 149 insertions(+) create mode 100644 src/IFoxCAD.Cad.Shared/ExtensionMethod/JigExTransient.cs diff --git a/src/IFoxCAD.Cad.Shared/ExtensionMethod/JigExTransient.cs b/src/IFoxCAD.Cad.Shared/ExtensionMethod/JigExTransient.cs new file mode 100644 index 0000000..e161928 --- /dev/null +++ b/src/IFoxCAD.Cad.Shared/ExtensionMethod/JigExTransient.cs @@ -0,0 +1,149 @@ +#if !ac2008 +namespace IFoxCAD.Cad; + +/// +/// 瞬态容器 +/// +public class JigExTransient : IDisposable +{ + #region 私有字段 + // 整数集,暂时不知道有什么意义 + IntegerCollection _integerCollection; + // 维护集合 + HashSet _entities; + #endregion + + #region 公开属性 + /// + /// 对象集合 + /// + public Entity[] Entities => _entities.ToArray(); + /// + /// 数量 + /// + public int Count => _entities.Count; + #endregion + + #region 构造函数 + /// + /// 瞬态容器 + /// + public JigExTransient() + { + _integerCollection = new(); + _entities = new(); + } + #endregion + + #region 方法 + /// + /// 判断瞬态容器里是否含有对象 + /// + /// 对象 + /// 含有返回true + public bool Contains(Entity ent) + { + return _entities.Contains(ent); + } + + /// + /// 向瞬态容器中添加对象 + /// + /// 图元 + /// 绘图模式 + public void Add(Entity ent, TransientDrawingMode tdm = TransientDrawingMode.Main) + { + if (_entities.Add(ent)) + { + TransientManager + .CurrentTransientManager + .AddTransient(ent, tdm, 128, _integerCollection); + } + } + + + /// + /// 从瞬态容器中移除图元 + /// + /// 已经加入瞬态容器的图元 + public void Remove(Entity ent) + { + if (!Contains(ent)) + return; + TransientManager + .CurrentTransientManager + .EraseTransient(ent, _integerCollection); + _entities.Remove(ent); + } + + /// + /// 清空瞬态容器并移除图元显示 + /// + public void Clear() + { + foreach (var ent in _entities) + { + TransientManager + .CurrentTransientManager + .EraseTransient(ent, _integerCollection); + } + _entities.Clear(); + } + + + /// + /// 更新单个显示 + /// + /// 已经加入瞬态容器的图元 + public void Update(Entity ent) + { + if (!Contains(ent)) + return; + TransientManager + .CurrentTransientManager + .UpdateTransient(ent, _integerCollection); + } + + /// + /// 更新全部显示 + /// + public void UpdateAll() + { + foreach (var ent in _entities) + Update(ent); + } + #endregion + + #region IDisposable接口相关函数 + public bool IsDisposed { get; private set; } = false; + + /// + /// 手动释放 + /// + public void Dispose() + { + Dispose(true); + GC.SuppressFinalize(this); + } + + /// + /// 析构函数调用释放 + /// + ~JigExTransient() + { + Dispose(false); + } + + /// + /// 销毁瞬态容器 + /// + protected virtual void Dispose(bool disposing) + { + if (IsDisposed) return; + IsDisposed = true; + + Clear();// 清空瞬态容器并移除对象在图纸上的显示 + } + #endregion +} +#endif \ No newline at end of file -- Gitee From 6b33d9301b69fd80262845c149df8ed2dd04d7b4 Mon Sep 17 00:00:00 2001 From: liuqihong <540762622@qq.com> Date: Sat, 10 Sep 2022 22:17:31 +0800 Subject: [PATCH 487/675] update README.md. --- README.md | 30 ++++++++++++++++++++---------- 1 file changed, 20 insertions(+), 10 deletions(-) diff --git a/README.md b/README.md index 5e026a2..36ca94d 100644 --- a/README.md +++ b/README.md @@ -3,7 +3,17 @@ #### 释义 IFox中: - IF:为Inspire Function(中文名:跃动方程)的首字母缩写 -- Fox:起初`雪山飞狐`(又狐哥)在明经论坛发布了[开源库](http://bbs.mjtd.com/thread-75701-1-1.html),后`落魄山人`(又小山山)基于狐哥的开源库创建了NFox开源项目,后经多重原因NFox被废弃,小山山又开创新开源项目,取名为`IF+Fox=IFox`,一语双关,简洁而不简单。 +- Fox:起初 **雪山飞狐(又狐哥)** 在明经论坛发布了[开源库](http://bbs.mjtd.com/thread-75701-1-1.html),后来狐哥自己的项目进行了极大的丰富后形成NFox类库。 + + 在 **落魄山人(又小山山)** 开始学习c#开发后,由于这个人过于懒,不想自己写基础的函数了,所以厚颜无耻的跟狐哥要来了 **NFox** 的源码。 + + 在学习了一阵源码后,发现狐哥的代码惊天地,泣鬼神,惊为天人,直接跪服,从此成为狐哥的脑残粉。 + + 为不使明珠蒙尘,特向狐哥申请是否可以开源,经原作者狐哥同意,正式发布 **NFox** 开源类库。 + + 后来本人多次二逼操作后, **NFox** 莫名无法运行,找不到原因,以及原代码嵌套引用多,逻辑复杂,痛定思痛,遂放弃 **NFox** 的开发,基于 **NFox** 重构为 **IFox** ,重新发布开源项目,取名为`IF+Fox=IFox`,一语双关,简洁而不简单。当然这些都是掩饰,其实就是 **I(爱)Fox(狐哥)** 。 + + **Inspire Function(中文名:跃动方程)及 IFoxCad 的名字均为小轩轩起名** #### 介绍 @@ -143,31 +153,31 @@ IFox的[jing分支](https://gitee.com/inspirefunction/ifoxcad/tree/jing/)是一 [IFoxInitialize] public void InitOne() { - // TODO 您想在加载dll之后自动执行的函数 + //TODO 您想在加载dll之后自动执行的函数 // 可以随便在哪里类里 可以多次实现 IFoxInitialize 特性 } } - // 其他的类中的函数: - // 实现自动接口之后,在任意一个函数上面使用此特性,减少每次改动 CmdINI 类 + //其他的类中的函数: + //实现自动接口之后,在任意一个函数上面使用此特性,减少每次改动 CmdINI 类 public class AutoTest { [IFoxInitialize] public void Initialize() { - // TODO 您想在加载dll之后自动执行的函数 + //TODO 您想在加载dll之后自动执行的函数 } [IFoxInitialize] public void InitTwo() { - // TODO 您想在加载dll之后自动执行的函数 + //TODO 您想在加载dll之后自动执行的函数 // 可以随便在哪里类里 可以多次实现 IFoxInitialize 特性 } [IFoxInitialize(isInitialize: false)] // 特性的参数为false的时候就表示卸载时执行的函数 public void Terminate() { - // TODO 您想在关闭cad时自动执行的函数 + //TODO 您想在关闭cad时自动执行的函数 } } ``` @@ -179,10 +189,10 @@ IFox的[jing分支](https://gitee.com/inspirefunction/ifoxcad/tree/jing/)是一 由于cad的对象是有打开模式,是否可写等等,为了安全起见,在处理对象时,一般是用读模式打开,然后需要写数据的时候在提权为写模式,然后在降级到读模式,但是这个过程中,很容易漏掉某些步骤,然后cad崩溃。为了处理这些情况,内裤提供了提权类来保证读写模式的有序转换。 ```c# - using(line.ForWrite()) // 开启对象写模式提权事务 + using(line.ForWrite()) //开启对象写模式提权事务 { - // 处理代码 - } // 关闭事务自动处理读写模式 + //处理代码 + } //关闭事务自动处理读写模式 ``` 8. 未完待续。。。。 -- Gitee From 1abb130ff49bae6031f624b2b11f00b50738bb39 Mon Sep 17 00:00:00 2001 From: DYH <1742647821@qq.com> Date: Wed, 14 Sep 2022 20:06:16 +0800 Subject: [PATCH 488/675] =?UTF-8?q?=E6=B7=BB=E5=8A=A0JigExTransient?= =?UTF-8?q?=E7=B1=BB=E7=9A=84=E6=B5=8B=E8=AF=95=E5=87=BD=E6=95=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- tests/TestShared/TestJigExTransient.cs | 52 ++++++++++++++++++++++++++ tests/TestShared/TestShared.projitems | 1 + 2 files changed, 53 insertions(+) create mode 100644 tests/TestShared/TestJigExTransient.cs diff --git a/tests/TestShared/TestJigExTransient.cs b/tests/TestShared/TestJigExTransient.cs new file mode 100644 index 0000000..28d05f8 --- /dev/null +++ b/tests/TestShared/TestJigExTransient.cs @@ -0,0 +1,52 @@ +namespace Test; + +public partial class Test +{ + [CommandMethod(nameof(TestJigExTransient))] + + public void TestJigExTransient() + { + // 先取1点,建2个圆 + var r1 = Env.Editor.GetPoint("\n选择点"); + if (r1.Status != PromptStatus.OK) + return; + var pt1 = r1.Value.Ucs2Wcs(); + + var c1 = new Circle(pt1, Vector3d.ZAxis, 100); + var c2 = new Circle(pt1.Polar(0, 300), Vector3d.ZAxis, 100); + + // 建瞬态容器 + using var jet = new JigExTransient(); + + // 将c1以默认模式、c2以亮显模式加到瞬态容器,即在图纸上显示 + jet.Add(c1); + jet.Add(c2, TransientDrawingMode.Highlight); + + // 再取一点,再建一个圆c3 + var r2 = Env.Editor.GetPoint("\n选择下一点"); + if (r2.Status != PromptStatus.OK) + return; + var pt2 = r2.Value.Ucs2Wcs(); + + var c3 = new Circle(pt2, Vector3d.ZAxis, 150); + + // 将c1从瞬态容器中移除,将c2修改颜色,c3加入瞬态容器 + jet.Remove(c1); + c2.ColorIndex = 1; + jet.Add(c3); + + // 由于c2进行了修改,所以需要更新,可以单个更新或更新整个瞬态容器 + jet.Update(c2); + //jet.UpdateAll(); + + var r4 = Env.Editor.GetPoint("\n此拾取无意义,仅为了暂停查看"); + + // 加到图纸中,为测试瞬态容器可以自行dispose消失,所以未全部加入 + using var tr = new DBTrans(); + tr.CurrentSpace.AddEntity(c3); + + // 若想将容器中所有图元全部加入提供了Entities属性 + // tr.CurrentSpace.AddEntity(jet.Entities); + + } +} diff --git a/tests/TestShared/TestShared.projitems b/tests/TestShared/TestShared.projitems index 7c433a6..ea9afeb 100644 --- a/tests/TestShared/TestShared.projitems +++ b/tests/TestShared/TestShared.projitems @@ -22,6 +22,7 @@ + -- Gitee From 66a3bb39f97615f29e673af42e1ab88fddd7a5cd Mon Sep 17 00:00:00 2001 From: liuqihong <540762622@qq.com> Date: Wed, 14 Sep 2022 22:11:41 +0800 Subject: [PATCH 489/675] =?UTF-8?q?=E7=9E=AC=E6=80=81=E5=85=B3=E8=81=94?= =?UTF-8?q?=E6=96=87=E4=BB=B6=E4=BF=AE=E6=94=B9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../IFoxCAD.Cad.Shared.projitems | 1 + tests/TestAcad08/TestAcad08.csproj | 4 +++ tests/TestShared/TestJigExTransient.cs | 35 ++++++++++--------- tests/TestShared/TestLisp.cs | 4 +-- 4 files changed, 25 insertions(+), 19 deletions(-) diff --git a/src/IFoxCAD.Cad.Shared/IFoxCAD.Cad.Shared.projitems b/src/IFoxCAD.Cad.Shared/IFoxCAD.Cad.Shared.projitems index a890148..0ab6fe2 100644 --- a/src/IFoxCAD.Cad.Shared/IFoxCAD.Cad.Shared.projitems +++ b/src/IFoxCAD.Cad.Shared/IFoxCAD.Cad.Shared.projitems @@ -18,6 +18,7 @@ + diff --git a/tests/TestAcad08/TestAcad08.csproj b/tests/TestAcad08/TestAcad08.csproj index 1388890..7840206 100644 --- a/tests/TestAcad08/TestAcad08.csproj +++ b/tests/TestAcad08/TestAcad08.csproj @@ -8,6 +8,10 @@ true x86 + + + $(Configuration);ac2008;ac2009 + diff --git a/tests/TestShared/TestJigExTransient.cs b/tests/TestShared/TestJigExTransient.cs index 28d05f8..15b8434 100644 --- a/tests/TestShared/TestJigExTransient.cs +++ b/tests/TestShared/TestJigExTransient.cs @@ -1,28 +1,28 @@ -namespace Test; +#if !ac2008 +namespace Test; public partial class Test { [CommandMethod(nameof(TestJigExTransient))] - public void TestJigExTransient() { - // 先取1点,建2个圆 - var r1 = Env.Editor.GetPoint("\n选择点"); - if (r1.Status != PromptStatus.OK) + // 先取1点,建2个圆 + var getpt = Env.Editor.GetPoint("\n选择点"); + if (getpt.Status != PromptStatus.OK) return; - var pt1 = r1.Value.Ucs2Wcs(); + var pt = getpt.Value.Ucs2Wcs(); - var c1 = new Circle(pt1, Vector3d.ZAxis, 100); - var c2 = new Circle(pt1.Polar(0, 300), Vector3d.ZAxis, 100); + var c1 = new Circle(pt, Vector3d.ZAxis, 100); + var c2 = new Circle(pt.Polar(0, 300), Vector3d.ZAxis, 100); - // 建瞬态容器 + // 创建瞬态容器 using var jet = new JigExTransient(); - // 将c1以默认模式、c2以亮显模式加到瞬态容器,即在图纸上显示 + // 将c1以默认模式、c2以亮显模式加到瞬态容器,即在图纸上显示 jet.Add(c1); - jet.Add(c2, TransientDrawingMode.Highlight); + jet.Add(c2, Acgi.TransientDrawingMode.Highlight); - // 再取一点,再建一个圆c3 + // 再取一点,再建一个圆c3 var r2 = Env.Editor.GetPoint("\n选择下一点"); if (r2.Status != PromptStatus.OK) return; @@ -30,23 +30,24 @@ public void TestJigExTransient() var c3 = new Circle(pt2, Vector3d.ZAxis, 150); - // 将c1从瞬态容器中移除,将c2修改颜色,c3加入瞬态容器 + // 将c1从瞬态容器中移除,将c2修改颜色,c3加入瞬态容器 jet.Remove(c1); c2.ColorIndex = 1; jet.Add(c3); - // 由于c2进行了修改,所以需要更新,可以单个更新或更新整个瞬态容器 + // 由于c2进行了修改,所以需要更新, + // 可以单个更新或更新整个瞬态容器 jet.Update(c2); //jet.UpdateAll(); - var r4 = Env.Editor.GetPoint("\n此拾取无意义,仅为了暂停查看"); + var r4 = Env.Editor.GetPoint("\n此拾取无意义,仅为了暂停查看"); - // 加到图纸中,为测试瞬态容器可以自行dispose消失,所以未全部加入 + // 加到图纸中,为测试瞬态容器可以自行dispose消失,所以未全部加入 using var tr = new DBTrans(); tr.CurrentSpace.AddEntity(c3); // 若想将容器中所有图元全部加入提供了Entities属性 // tr.CurrentSpace.AddEntity(jet.Entities); - } } +#endif \ No newline at end of file diff --git a/tests/TestShared/TestLisp.cs b/tests/TestShared/TestLisp.cs index 725a18d..1a17bf0 100644 --- a/tests/TestShared/TestLisp.cs +++ b/tests/TestShared/TestLisp.cs @@ -50,7 +50,7 @@ public static object LispTest_RunLisp(ResultBuffer rb) [CommandMethod("CmdTest_RunLisp19", CommandFlags.NoUndoMarker)] // 不能在参照块中使用命令 [CommandMethod("CmdTest_RunLisp20", CommandFlags.NoBlockEditor)] -#if ac2009 +#if !ac2008 // acad09增,不会被动作录制器 捕捉到 [CommandMethod("CmdTest_RunLisp21", CommandFlags.NoActionRecording)] // acad09增,会被动作录制器捕捉 @@ -118,4 +118,4 @@ public static void CmdTest_RunLisp() EditorEx.RunLispFlag.AdsQueueexpr); // 同步,先发送了,输出是null } } -} +} \ No newline at end of file -- Gitee From 9d732fb2eb1c2f3344972d1aeb049aa4621d827d Mon Sep 17 00:00:00 2001 From: liuqihong <540762622@qq.com> Date: Wed, 14 Sep 2022 22:30:41 +0800 Subject: [PATCH 490/675] =?UTF-8?q?=E8=BF=9B=E5=85=A50.4=E7=89=88=E6=9C=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/IFoxCAD.Basal/ArrayEx.cs | 26 +- src/IFoxCAD.Basal/CLS/Index.cs | 3 +- src/IFoxCAD.Basal/CLS/Range.cs | 5 +- src/IFoxCAD.Basal/CLS/RuntimeHelpers.cs | 12 +- .../CLS/TupleElementNamesAttribute.cs | 2 +- src/IFoxCAD.Basal/CLS/ValueTuple.cs | 138 ++-- src/IFoxCAD.Basal/DictEx.cs | 6 +- src/IFoxCAD.Basal/EnumEx.cs | 105 +++ src/IFoxCAD.Basal/IFoxCAD.Basal.csproj | 4 +- src/IFoxCAD.Basal/LinkedHashSet.cs | 2 +- src/IFoxCAD.Basal/LoopList.cs | 16 +- src/IFoxCAD.Basal/RandomEx.cs | 202 ++++++ src/IFoxCAD.Basal/Sortedset/ISet.cs | 40 +- src/IFoxCAD.Basal/Sortedset/SR.cs | 8 +- src/IFoxCAD.Basal/Sortedset/Sortedset.cs | 430 ++++++------ src/IFoxCAD.Basal/Sortedset/ThrowHelper.cs | 199 +++--- src/IFoxCAD.Basal/Sortedset/bithelper.cs | 33 +- src/IFoxCAD.Cad/Algorithms/Graph/Graph.cs | 10 +- .../Algorithms/QuadTree/QuadTree.cs | 34 +- .../Algorithms/QuadTree/QuadTreeNode.cs | 206 +++--- .../Algorithms/QuadTree/QuadTreeSelectMode.cs | 12 +- src/IFoxCAD.Cad/Algorithms/QuadTree/Rect.cs | 84 +-- .../Algorithms/QuadTree/Utility.cs | 60 -- .../ExtensionMethod/BulgeVertexWidth.cs | 4 +- src/IFoxCAD.Cad/ExtensionMethod/Curve2dEx.cs | 2 +- src/IFoxCAD.Cad/ExtensionMethod/Curve3dEx.cs | 22 +- src/IFoxCAD.Cad/ExtensionMethod/CurveEx.cs | 125 ++-- .../ExtensionMethod/DBDictionaryEx.cs | 6 +- src/IFoxCAD.Cad/ExtensionMethod/DBObjectEx.cs | 91 ++- src/IFoxCAD.Cad/ExtensionMethod/DBTransEx.cs | 92 +++ src/IFoxCAD.Cad/ExtensionMethod/DatabaseEx.cs | 23 - src/IFoxCAD.Cad/ExtensionMethod/EditorEx.cs | 330 +++++---- .../ExtensionMethod/EntityBoundingInfo.cs | 264 +++++++ src/IFoxCAD.Cad/ExtensionMethod/EntityEx.cs | 46 +- src/IFoxCAD.Cad/ExtensionMethod/Enums.cs | 120 +++- src/IFoxCAD.Cad/ExtensionMethod/GeometryEx.cs | 83 ++- .../ExtensionMethod/{Jig.cs => JigEx.cs} | 130 ++-- .../ExtensionMethod/JigExTransient.cs | 2 +- src/IFoxCAD.Cad/ExtensionMethod/ObjectIdEx.cs | 13 +- src/IFoxCAD.Cad/ExtensionMethod/PointEx.cs | 20 +- .../ExtensionMethod/SymbolTableEx.cs | 22 +- .../ExtensionMethod/SymbolTableRecordEx.cs | 30 +- src/IFoxCAD.Cad/ExtensionMethod/Tools.cs | 14 +- src/IFoxCAD.Cad/ExtensionMethod/XrefEx.cs | 652 ++++++++++++++++++ .../HatchConverter.cs" | 70 +- .../HatchInfo.cs" | 198 +++--- .../AttachmentPointHelper.cs" | 20 +- .../TextInfo.cs" | 28 +- src/IFoxCAD.Cad/GlobalUsings.cs | 6 +- src/IFoxCAD.Cad/IFoxCAD.Cad.csproj | 4 +- src/IFoxCAD.Cad/Runtime/AOP.cs | 182 ++--- src/IFoxCAD.Cad/Runtime/AcadEMR.cs | 149 ++++ src/IFoxCAD.Cad/Runtime/AcadVersion.cs | 2 - src/IFoxCAD.Cad/Runtime/AssemInfo.cs | 9 +- src/IFoxCAD.Cad/Runtime/AutoRegAssem.cs | 44 +- src/IFoxCAD.Cad/Runtime/CadVersion.cs | 6 +- src/IFoxCAD.Cad/Runtime/DBTrans.cs | 366 +++++++--- src/IFoxCAD.Cad/Runtime/Env.cs | 326 +++++++-- src/IFoxCAD.Cad/Runtime/FileOpenMode.cs | 2 +- src/IFoxCAD.Cad/Runtime/IAutoGo.cs | 50 +- src/IFoxCAD.Cad/Runtime/Log.cs | 189 +++-- src/IFoxCAD.Cad/Runtime/MethodInfoHelper.cs | 22 +- src/IFoxCAD.Cad/Runtime/SymbolTable.cs | 86 ++- src/IFoxCAD.Cad/Runtime/Utils.cs | 46 +- src/IFoxCAD.WPF/EventBindingExtension.cs | 8 +- src/IFoxCAD.WPF/IFoxCAD.WPF.csproj | 6 +- tests/Test/CmdINI.cs | 133 +++- tests/Test/Test.cs | 119 ++-- tests/Test/Test.csproj | 8 +- tests/Test/TestAOP.cs | 22 +- tests/Test/TestCurve.cs | 77 +-- tests/Test/TestDBTrans.cs | 74 +- tests/Test/TestEnt.cs | 8 +- tests/Test/TestJig.cs | 169 ++--- tests/Test/TestJigExTransient.cs | 53 ++ tests/Test/TestLisp.cs | 72 +- tests/Test/TestLoop.cs | 2 +- tests/Test/TestMirrorFile.cs | 24 +- tests/Test/TestPoint.cs | 36 +- tests/Test/TestQuadTree.cs | 153 ++-- tests/Test/XrefExTest.cs | 24 + tests/Test/testConvexHull.cs | 76 +- tests/Test/testFileDatabase.cs | 4 +- tests/Test/testblock.cs | 306 ++++---- tests/Test/testeditor.cs | 24 +- tests/Test/testenv.cs | 16 +- tests/Test/testid.cs | 10 +- tests/Test/testselectfilter.cs | 2 +- tests/Test/wpf/Class1.cs | 16 +- tests/Test/wpf/TestView.xaml.cs | 22 +- tests/Test/wpf/TestViewModel.cs | 67 +- tests/TestConsole/GlobalUsings.cs | 14 + tests/TestConsole/Helper.cs | 42 ++ tests/TestConsole/Info.cs | 41 ++ tests/TestConsole/Program.cs | 51 +- tests/TestConsole/RuntimeHelpers.cs | 2 +- tests/TestConsole/TestConsole.csproj | 9 +- ...50\350\276\276\345\274\217\346\240\221.cs" | 243 ++++--- 98 files changed, 4867 insertions(+), 2599 deletions(-) create mode 100644 src/IFoxCAD.Basal/EnumEx.cs create mode 100644 src/IFoxCAD.Basal/RandomEx.cs delete mode 100644 src/IFoxCAD.Cad/Algorithms/QuadTree/Utility.cs create mode 100644 src/IFoxCAD.Cad/ExtensionMethod/DBTransEx.cs delete mode 100644 src/IFoxCAD.Cad/ExtensionMethod/DatabaseEx.cs create mode 100644 src/IFoxCAD.Cad/ExtensionMethod/EntityBoundingInfo.cs rename src/IFoxCAD.Cad/ExtensionMethod/{Jig.cs => JigEx.cs} (69%) create mode 100644 src/IFoxCAD.Cad/ExtensionMethod/XrefEx.cs create mode 100644 src/IFoxCAD.Cad/Runtime/AcadEMR.cs create mode 100644 tests/Test/TestJigExTransient.cs create mode 100644 tests/Test/XrefExTest.cs create mode 100644 tests/TestConsole/GlobalUsings.cs create mode 100644 tests/TestConsole/Helper.cs create mode 100644 tests/TestConsole/Info.cs diff --git a/src/IFoxCAD.Basal/ArrayEx.cs b/src/IFoxCAD.Basal/ArrayEx.cs index 2ac37fa..21947f6 100644 --- a/src/IFoxCAD.Basal/ArrayEx.cs +++ b/src/IFoxCAD.Basal/ArrayEx.cs @@ -21,7 +21,9 @@ public static T[] Combine2(this T[] a, T[] b) } /// - /// 一维数组消重,此函数建议更改为: + /// 一维数组按规则消除
    + /// 本例适用于数值类型比较,特定规则比较
    + /// 如果是哈希比较,建议更改为: /// set = new(); /// foreach (var item in listInOut) @@ -29,19 +31,23 @@ public static T[] Combine2(this T[] a, T[] b) /// ]]> ///
    /// - /// 传入有重复成员的数组,传出没有重复的 - /// 传出参数1:数组开头;传出参数2:数组结尾;返回值比较结尾为就移除 - [Obsolete] - public static void Deduplication(List listInOut, Func func) + /// 传入有重复成员的数组,原数组修改 + /// + /// 传出参数1:数组开头
    + /// 传出参数2:数组结尾
    + /// 返回值比较结尾为就移除
    + /// + public static void Deduplication(List lst, Func func) { - for (int i = 0; i < listInOut.Count; i++) + // 头和尾比较,满足条件移除尾巴 + for (int i = 0; i < lst.Count; i++) { - var first = listInOut[i]; - for (int j = listInOut.Count - 1; j > i; j--) + var first = lst[i]; + for (int j = lst.Count - 1; j > i/*符号是 >= 而且是i*/; j--) { - var last = listInOut[j]; + var last = lst[j]; if (func(first, last)) - listInOut.RemoveAt(j); + lst.RemoveAt(j); } } } diff --git a/src/IFoxCAD.Basal/CLS/Index.cs b/src/IFoxCAD.Basal/CLS/Index.cs index 97b51e5..a588dc6 100644 --- a/src/IFoxCAD.Basal/CLS/Index.cs +++ b/src/IFoxCAD.Basal/CLS/Index.cs @@ -144,5 +144,4 @@ public override string ToString() return ((uint)Value).ToString(); } -} - +} \ No newline at end of file diff --git a/src/IFoxCAD.Basal/CLS/Range.cs b/src/IFoxCAD.Basal/CLS/Range.cs index 221167b..43dce54 100644 --- a/src/IFoxCAD.Basal/CLS/Range.cs +++ b/src/IFoxCAD.Basal/CLS/Range.cs @@ -75,7 +75,7 @@ public override string ToString() #if NET45 [MethodImpl(MethodImplOptions.AggressiveInlining)] #endif - //[CLSCompliant(false)] + // [CLSCompliant(false)] public (int Offset, int Length) GetOffsetAndLength(int length) { int start; @@ -99,5 +99,4 @@ public override string ToString() return (start, end - start); } -} - +} \ No newline at end of file diff --git a/src/IFoxCAD.Basal/CLS/RuntimeHelpers.cs b/src/IFoxCAD.Basal/CLS/RuntimeHelpers.cs index 1db16f1..1008b09 100644 --- a/src/IFoxCAD.Basal/CLS/RuntimeHelpers.cs +++ b/src/IFoxCAD.Basal/CLS/RuntimeHelpers.cs @@ -1,14 +1,14 @@ -//#if NET35 +// #if NET35 // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. namespace System.Runtime.CompilerServices; -//编译提示多个程序集中定义,屏蔽不了,但是不影响编译 -//#pragma warning disable CS1685 // 类型与导入类型冲突 +// 编译提示多个程序集中定义,屏蔽不了,但是不影响编译 +// #pragma warning disable CS1685 // 类型与导入类型冲突 public static class RuntimeHelpers -//#pragma warning restore CS1685 // 类型与导入类型冲突 +// #pragma warning restore CS1685 // 类型与导入类型冲突 { /// /// Slices the specified array using the specified range. @@ -25,7 +25,7 @@ public static T[] GetSubArray(T[] array, Range range) // We know the type of the array to be exactly T[]. if (length == 0) { - //return Array.Empty(); + // return Array.Empty(); return new T[0]; } @@ -42,4 +42,4 @@ public static T[] GetSubArray(T[] array, Range range) } } } -//#endif +// #endif \ No newline at end of file diff --git a/src/IFoxCAD.Basal/CLS/TupleElementNamesAttribute.cs b/src/IFoxCAD.Basal/CLS/TupleElementNamesAttribute.cs index 9c3afb3..f3f7515 100644 --- a/src/IFoxCAD.Basal/CLS/TupleElementNamesAttribute.cs +++ b/src/IFoxCAD.Basal/CLS/TupleElementNamesAttribute.cs @@ -47,4 +47,4 @@ public TupleElementNamesAttribute(string[] transformNames) /// meant to carry element names. /// public IList TransformNames => _transformNames; -} \ No newline at end of file +} diff --git a/src/IFoxCAD.Basal/CLS/ValueTuple.cs b/src/IFoxCAD.Basal/CLS/ValueTuple.cs index bece1ed..568ddb9 100644 --- a/src/IFoxCAD.Basal/CLS/ValueTuple.cs +++ b/src/IFoxCAD.Basal/CLS/ValueTuple.cs @@ -1,18 +1,18 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. -//#pragma warning disable SA1141 // explicitly not using tuple syntax in tuple implementation +// #pragma warning disable SA1141 // explicitly not using tuple syntax in tuple implementation using System.Diagnostics; using System.Numerics.Hashing; /* * 惊惊: - * 首先是因为有人想要编译的时候只形成一个dll,然后把元组塞入IFox,同时也补充了net35没有元组的遗憾. + * 首先是因为有人想要编译的时候只形成一个dll,然后把元组塞入IFox,同时也补充了NET35没有元组的遗憾. * 而利用nuget元组包必然会形成依赖地狱. - * + * * 如果你的工程使用了nuget元组包,就造成了必须要剔除IFox. - * + * * 改IFox的元组命名空间倒是可以分离两者,但是 vs编译器 无法识别带其他命名空间的元组. * 所以元组本身就是冲突的,需要把其他元组卸载掉,由IFox提供. */ @@ -61,11 +61,11 @@ public static int Combine(int h1, int h2) namespace System { - //internal static class SR + // internal static class SR internal sealed partial class SR { // public const string ArgumentException_ValueTupleIncorrectType = "The parameter should be a ValueTuple type of appropriate arity."; - // public const string ArgumentException_ValueTupleLastArgumentNotAValueTuple = "The TRest type argument of ValueTuple`8 must be a ValueTuple."; + // public const string ArgumentException_ValueTupleLastArgumentNotAValueTuple = "The TRest type argument of ValueTuple`8 must be a ValueTuple."; public const string ArgumentException_ValueTupleIncorrectType = "该参数应该是适当数量的 ValueTuple 类型."; public const string ArgumentException_ValueTupleLastArgumentNotAValueTuple = "ValueTuple`8 的 TREST 类型参数必须是 ValueTuple."; } @@ -139,7 +139,7 @@ int IComparable.CompareTo(object other) /// /// A signed number indicating the relative values of this instance and . /// Returns less than zero if this instance is less than , zero if this - /// instance is equal to , and greater than zero if this instance is greater + /// instance is equal to , and greater than zero if this instance is greater /// than . /// public int CompareTo(ValueTuple other) @@ -425,7 +425,7 @@ int IComparable.CompareTo(object other) /// /// A signed number indicating the relative values of this instance and . /// Returns less than zero if this instance is less than , zero if this - /// instance is equal to , and greater than zero if this instance is greater + /// instance is equal to , and greater than zero if this instance is greater /// than . /// public int CompareTo(ValueTuple other) @@ -596,7 +596,7 @@ int IComparable.CompareTo(object other) /// /// A signed number indicating the relative values of this instance and . /// Returns less than zero if this instance is less than , zero if this - /// instance is equal to , and greater than zero if this instance is greater + /// instance is equal to , and greater than zero if this instance is greater /// than . /// public int CompareTo(ValueTuple other) @@ -771,7 +771,7 @@ int IComparable.CompareTo(object other) /// /// A signed number indicating the relative values of this instance and . /// Returns less than zero if this instance is less than , zero if this - /// instance is equal to , and greater than zero if this instance is greater + /// instance is equal to , and greater than zero if this instance is greater /// than . /// public int CompareTo(ValueTuple other) @@ -961,7 +961,7 @@ int IComparable.CompareTo(object other) /// /// A signed number indicating the relative values of this instance and . /// Returns less than zero if this instance is less than , zero if this - /// instance is equal to , and greater than zero if this instance is greater + /// instance is equal to , and greater than zero if this instance is greater /// than . /// public int CompareTo(ValueTuple other) @@ -1168,7 +1168,7 @@ int IComparable.CompareTo(object other) /// /// A signed number indicating the relative values of this instance and . /// Returns less than zero if this instance is less than , zero if this - /// instance is equal to , and greater than zero if this instance is greater + /// instance is equal to , and greater than zero if this instance is greater /// than . /// public int CompareTo(ValueTuple other) @@ -1392,7 +1392,7 @@ int IComparable.CompareTo(object other) /// /// A signed number indicating the relative values of this instance and . /// Returns less than zero if this instance is less than , zero if this - /// instance is equal to , and greater than zero if this instance is greater + /// instance is equal to , and greater than zero if this instance is greater /// than . /// public int CompareTo(ValueTuple other) @@ -1633,7 +1633,7 @@ int IComparable.CompareTo(object other) /// /// A signed number indicating the relative values of this instance and . /// Returns less than zero if this instance is less than , zero if this - /// instance is equal to , and greater than zero if this instance is greater + /// instance is equal to , and greater than zero if this instance is greater /// than . /// public int CompareTo(ValueTuple other) @@ -1897,7 +1897,7 @@ int IComparable.CompareTo(object other) /// /// A signed number indicating the relative values of this instance and . /// Returns less than zero if this instance is less than , zero if this - /// instance is equal to , and greater than zero if this instance is greater + /// instance is equal to , and greater than zero if this instance is greater /// than . /// public int CompareTo(ValueTuple other) @@ -1987,48 +1987,48 @@ public override int GetHashCode() switch (k) { case 1: - return ValueTuple.CombineHashCodes(EqualityComparer.Default.GetHashCode(Item7), - rest.GetHashCode()); + return ValueTuple.CombineHashCodes(EqualityComparer.Default.GetHashCode(Item7), + rest.GetHashCode()); case 2: - return ValueTuple.CombineHashCodes(EqualityComparer.Default.GetHashCode(Item6), - EqualityComparer.Default.GetHashCode(Item7), - rest.GetHashCode()); + return ValueTuple.CombineHashCodes(EqualityComparer.Default.GetHashCode(Item6), + EqualityComparer.Default.GetHashCode(Item7), + rest.GetHashCode()); case 3: - return ValueTuple.CombineHashCodes(EqualityComparer.Default.GetHashCode(Item5), - EqualityComparer.Default.GetHashCode(Item6), - EqualityComparer.Default.GetHashCode(Item7), - rest.GetHashCode()); + return ValueTuple.CombineHashCodes(EqualityComparer.Default.GetHashCode(Item5), + EqualityComparer.Default.GetHashCode(Item6), + EqualityComparer.Default.GetHashCode(Item7), + rest.GetHashCode()); case 4: - return ValueTuple.CombineHashCodes(EqualityComparer.Default.GetHashCode(Item4), - EqualityComparer.Default.GetHashCode(Item5), - EqualityComparer.Default.GetHashCode(Item6), - EqualityComparer.Default.GetHashCode(Item7), - rest.GetHashCode()); + return ValueTuple.CombineHashCodes(EqualityComparer.Default.GetHashCode(Item4), + EqualityComparer.Default.GetHashCode(Item5), + EqualityComparer.Default.GetHashCode(Item6), + EqualityComparer.Default.GetHashCode(Item7), + rest.GetHashCode()); case 5: - return ValueTuple.CombineHashCodes(EqualityComparer.Default.GetHashCode(Item3), - EqualityComparer.Default.GetHashCode(Item4), - EqualityComparer.Default.GetHashCode(Item5), - EqualityComparer.Default.GetHashCode(Item6), - EqualityComparer.Default.GetHashCode(Item7), - rest.GetHashCode()); + return ValueTuple.CombineHashCodes(EqualityComparer.Default.GetHashCode(Item3), + EqualityComparer.Default.GetHashCode(Item4), + EqualityComparer.Default.GetHashCode(Item5), + EqualityComparer.Default.GetHashCode(Item6), + EqualityComparer.Default.GetHashCode(Item7), + rest.GetHashCode()); case 6: - return ValueTuple.CombineHashCodes(EqualityComparer.Default.GetHashCode(Item2), - EqualityComparer.Default.GetHashCode(Item3), - EqualityComparer.Default.GetHashCode(Item4), - EqualityComparer.Default.GetHashCode(Item5), - EqualityComparer.Default.GetHashCode(Item6), - EqualityComparer.Default.GetHashCode(Item7), - rest.GetHashCode()); + return ValueTuple.CombineHashCodes(EqualityComparer.Default.GetHashCode(Item2), + EqualityComparer.Default.GetHashCode(Item3), + EqualityComparer.Default.GetHashCode(Item4), + EqualityComparer.Default.GetHashCode(Item5), + EqualityComparer.Default.GetHashCode(Item6), + EqualityComparer.Default.GetHashCode(Item7), + rest.GetHashCode()); case 7: case 8: - return ValueTuple.CombineHashCodes(EqualityComparer.Default.GetHashCode(Item1), - EqualityComparer.Default.GetHashCode(Item2), - EqualityComparer.Default.GetHashCode(Item3), - EqualityComparer.Default.GetHashCode(Item4), - EqualityComparer.Default.GetHashCode(Item5), - EqualityComparer.Default.GetHashCode(Item6), - EqualityComparer.Default.GetHashCode(Item7), - rest.GetHashCode()); + return ValueTuple.CombineHashCodes(EqualityComparer.Default.GetHashCode(Item1), + EqualityComparer.Default.GetHashCode(Item2), + EqualityComparer.Default.GetHashCode(Item3), + EqualityComparer.Default.GetHashCode(Item4), + EqualityComparer.Default.GetHashCode(Item5), + EqualityComparer.Default.GetHashCode(Item6), + EqualityComparer.Default.GetHashCode(Item7), + rest.GetHashCode()); } Debug.Assert(false, "Missed all cases for computing ValueTuple hash code"); @@ -2063,27 +2063,27 @@ private int GetHashCodeCore(IEqualityComparer comparer) switch (k) { case 1: - return ValueTuple.CombineHashCodes(comparer.GetHashCode(Item7), rest.GetHashCode(comparer)); + return ValueTuple.CombineHashCodes(comparer.GetHashCode(Item7), rest.GetHashCode(comparer)); case 2: - return ValueTuple.CombineHashCodes(comparer.GetHashCode(Item6), comparer.GetHashCode(Item7), rest.GetHashCode(comparer)); + return ValueTuple.CombineHashCodes(comparer.GetHashCode(Item6), comparer.GetHashCode(Item7), rest.GetHashCode(comparer)); case 3: - return ValueTuple.CombineHashCodes(comparer.GetHashCode(Item5), comparer.GetHashCode(Item6), comparer.GetHashCode(Item7), - rest.GetHashCode(comparer)); + return ValueTuple.CombineHashCodes(comparer.GetHashCode(Item5), comparer.GetHashCode(Item6), comparer.GetHashCode(Item7), + rest.GetHashCode(comparer)); case 4: - return ValueTuple.CombineHashCodes(comparer.GetHashCode(Item4), comparer.GetHashCode(Item5), comparer.GetHashCode(Item6), - comparer.GetHashCode(Item7), rest.GetHashCode(comparer)); + return ValueTuple.CombineHashCodes(comparer.GetHashCode(Item4), comparer.GetHashCode(Item5), comparer.GetHashCode(Item6), + comparer.GetHashCode(Item7), rest.GetHashCode(comparer)); case 5: - return ValueTuple.CombineHashCodes(comparer.GetHashCode(Item3), comparer.GetHashCode(Item4), comparer.GetHashCode(Item5), - comparer.GetHashCode(Item6), comparer.GetHashCode(Item7), rest.GetHashCode(comparer)); + return ValueTuple.CombineHashCodes(comparer.GetHashCode(Item3), comparer.GetHashCode(Item4), comparer.GetHashCode(Item5), + comparer.GetHashCode(Item6), comparer.GetHashCode(Item7), rest.GetHashCode(comparer)); case 6: - return ValueTuple.CombineHashCodes(comparer.GetHashCode(Item2), comparer.GetHashCode(Item3), comparer.GetHashCode(Item4), - comparer.GetHashCode(Item5), comparer.GetHashCode(Item6), comparer.GetHashCode(Item7), - rest.GetHashCode(comparer)); + return ValueTuple.CombineHashCodes(comparer.GetHashCode(Item2), comparer.GetHashCode(Item3), comparer.GetHashCode(Item4), + comparer.GetHashCode(Item5), comparer.GetHashCode(Item6), comparer.GetHashCode(Item7), + rest.GetHashCode(comparer)); case 7: case 8: - return ValueTuple.CombineHashCodes(comparer.GetHashCode(Item1), comparer.GetHashCode(Item2), comparer.GetHashCode(Item3), - comparer.GetHashCode(Item4), comparer.GetHashCode(Item5), comparer.GetHashCode(Item6), - comparer.GetHashCode(Item7), rest.GetHashCode(comparer)); + return ValueTuple.CombineHashCodes(comparer.GetHashCode(Item1), comparer.GetHashCode(Item2), comparer.GetHashCode(Item3), + comparer.GetHashCode(Item4), comparer.GetHashCode(Item5), comparer.GetHashCode(Item6), + comparer.GetHashCode(Item7), rest.GetHashCode(comparer)); } Debug.Assert(false, "Missed all cases for computing ValueTuple hash code"); @@ -2131,14 +2131,10 @@ int ITupleInternal.Size { get { - //ITupleInternal? rest = Rest as ITupleInternal; - //return rest == null ? 8 : 7 + rest.Size; + // ITupleInternal? rest = Rest as ITupleInternal; + // return rest == null ? 8 : 7 + rest.Size; return Rest is not ITupleInternal rest ? 8 : 7 + rest.Size; } } } -} - - - - +} \ No newline at end of file diff --git a/src/IFoxCAD.Basal/DictEx.cs b/src/IFoxCAD.Basal/DictEx.cs index 7ff153f..296fb16 100644 --- a/src/IFoxCAD.Basal/DictEx.cs +++ b/src/IFoxCAD.Basal/DictEx.cs @@ -2,8 +2,8 @@ public static class DictEx { - //public static TKey? GetKey(this IDictionary dict!!, TKey key!!) - //{ + // public static TKey? GetKey(this IDictionary dict!!, TKey key!!) + // { // if (dict.ContainsKey(key)) // { // foreach (var item in dict.Keys) @@ -11,5 +11,5 @@ public static class DictEx // return item; // } // return default; - //} + // } } diff --git a/src/IFoxCAD.Basal/EnumEx.cs b/src/IFoxCAD.Basal/EnumEx.cs new file mode 100644 index 0000000..46aa077 --- /dev/null +++ b/src/IFoxCAD.Basal/EnumEx.cs @@ -0,0 +1,105 @@ +namespace IFoxCAD.Basal; + +using System.ComponentModel; +using System.Linq; + +public static class EnumEx +{ + /// + /// 清理缓存 + /// + public static void CleanCache() + { + _cache.Clear(); + } + + // (类型完整名,描述组合) + static readonly Dictionary> _cache = new(); + + /// + /// 打印枚举的特性注释内容 + /// + /// 枚举 + /// 注释内容 + public static HashSet? GetAttribute(this Enum e, bool noDescrToString = true) + where T : DescriptionAttribute + { + var eType = e.GetType(); + string eFullName = eType.FullName + "." + e.ToString(); + + if (_cache.ContainsKey(eFullName)) + return _cache[eFullName]; + + var fieldInfo = eType.GetField(Enum.GetName(eType, e)); + if (fieldInfo == null) + return null!; + + // 注释存放的容器 + HashSet nodes = new(); + if (Attribute.GetCustomAttribute(fieldInfo, typeof(T)) is T attribute) + { + nodes.Add(attribute.Description); + _cache.Add(eFullName, nodes); + return nodes; + } + + // 通常到这里的就是 ALL = A | B | C + // 遍历所有的枚举,组合每个注释 + List enumHas = new(); + + // 遍历这个枚举类型,获取枚举按位包含的成员 + foreach (Enum em in Enum.GetValues(eType)) + if ((e.GetHashCode() & em.GetHashCode()) == em.GetHashCode() && + e.GetHashCode() != em.GetHashCode()) + enumHas.Add(em); + + + // 采取的行为是:注释的行为是特殊的,就按照注释的,否则,遍历子元素提取注释 + // 大的在前面才能判断是否按位包含后面的,后面的就是要移除的 + enumHas = enumHas.OrderByDescending(a => a.GetHashCode()).ToList(); + ArrayEx.Deduplication(enumHas, (a, b) => { + return (a.GetHashCode() & b.GetHashCode()) == b.GetHashCode(); + }); + + // 逆序仅仅为排序后处理,不一定和书写顺序一样,尤其是递归可能存在重复的元素 + for (int i = enumHas.Count - 1; i >= 0; i--) + { + var atts = GetAttribute(enumHas[i], noDescrToString);// 递归 + if (atts == null) + continue; + foreach (var item in atts) + nodes.Add(item); + } + + if (nodes.Count == 0 && noDescrToString) + nodes.Add(e.ToString()); + + _cache.Add(eFullName, nodes); + return nodes; + } + + /// + /// 打印枚举的特性注释内容 + /// + public static string? PrintNote(this Enum e, bool noDescToString = true) + { + var hash = GetAttribute(e, noDescToString); + if (hash != null) + return string.Join("|", hash.ToArray()); + return null; + } + + + // TODO 山人审核代码之后可以删除,这个完全被上面替代了 + public static string GetDesc(this Enum val) + { + var type = val.GetType(); + var memberInfo = type.GetMember(val.ToString()); + var attributes = memberInfo[0].GetCustomAttributes(typeof(DescriptionAttribute), false); + // 如果没有定义描述,就把当前枚举值的对应名称返回 + if (attributes is null || attributes.Length != 1) + return val.ToString(); + + return ((DescriptionAttribute)attributes.Single()).Description; + } +} \ No newline at end of file diff --git a/src/IFoxCAD.Basal/IFoxCAD.Basal.csproj b/src/IFoxCAD.Basal/IFoxCAD.Basal.csproj index 41ed7c1..e72ed9a 100644 --- a/src/IFoxCAD.Basal/IFoxCAD.Basal.csproj +++ b/src/IFoxCAD.Basal/IFoxCAD.Basal.csproj @@ -4,9 +4,9 @@ preview enable - net35;net40;net45 + NET35;NET40;NET45 true - 0.3.6 + 0.4 InspireFunction xsfhlzh;vicwjb 基于.NET的二次开发基本类库 diff --git a/src/IFoxCAD.Basal/LinkedHashSet.cs b/src/IFoxCAD.Basal/LinkedHashSet.cs index 8659c24..6295795 100644 --- a/src/IFoxCAD.Basal/LinkedHashSet.cs +++ b/src/IFoxCAD.Basal/LinkedHashSet.cs @@ -135,7 +135,7 @@ public bool Contains(T item) public void CopyTo(T[] array, int arrayIndex) { - //m_LinkedList.CopyTo(array, arrayIndex); + // m_LinkedList.CopyTo(array, arrayIndex); return; } diff --git a/src/IFoxCAD.Basal/LoopList.cs b/src/IFoxCAD.Basal/LoopList.cs index c0e021c..9bd986b 100644 --- a/src/IFoxCAD.Basal/LoopList.cs +++ b/src/IFoxCAD.Basal/LoopList.cs @@ -1,6 +1,6 @@ namespace IFoxCAD.Basal; -#line hidden //调试的时候跳过它 +#line hidden // 调试的时候跳过它 /// /// 环链表节点 @@ -161,7 +161,7 @@ public void Reverse() /// public void Clear() { - //移除头部,表示链表再也无法遍历得到 + // 移除头部,表示链表再也无法遍历得到 First = null; Count = 0; } @@ -238,17 +238,17 @@ public bool Contains(T value) /// public LoopListNode? Find(T value) { - //LoopListNode result = null; - //ForEach(node => - //{ + // LoopListNode result = null; + // ForEach(node => + // { // if (node.Value.Equals(t2)) // { // result = node; // return true; // } // return false; - //}); - //return result; + // }); + // return result; LoopListNode? node = First; var c = EqualityComparer.Default; @@ -536,7 +536,7 @@ void InternalRemove(LoopListNode node) { if (Count == 1 || node == First) { - RemoveFirst();//此处会减数字 + RemoveFirst();// 此处会减数字 } else { diff --git a/src/IFoxCAD.Basal/RandomEx.cs b/src/IFoxCAD.Basal/RandomEx.cs new file mode 100644 index 0000000..a9682f7 --- /dev/null +++ b/src/IFoxCAD.Basal/RandomEx.cs @@ -0,0 +1,202 @@ +/* +*┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━模块信息━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┓ +*┃ 作 者:YxrWendao +*┃ 创建时间:2022/8/30 22:49:30 +*┃ 模块描述:随机数生成器 +*┃ 使用范围:通用 +*┃ 说 明:本模块中除GetRandom与NextColor方法是IFoxCAD原有的以外,其他方法均通过网络收集整理而来。 +*┃ 代码版本:1.0 +*┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛ +*/ + +namespace IFoxCAD.Basal; + +/// +/// 随机值扩展类 +/// +public static class RandomEx +{ + /// + /// 生成一个指定范围的浮点数值
    + /// 相关链接 + ///
    + /// 一个随机值产生器 + /// 范围最小浮点数值 + /// 范围最大浮点数值 + /// + public static double NextDouble(Random ran, double minValue, double maxValue) + { + return ran.NextDouble() * (maxValue - minValue) + minValue; + } + /// + /// 生成一个指定范围的浮点数值 + /// + /// 范围最小浮点数值 + /// 范围最大浮点数值 + /// + public static double NextDouble(double minValue, double maxValue) + { + return NextDouble(GetRandom(), minValue, maxValue); + } + /// + /// 生成一个布尔随机数 + /// + /// + public static bool NextBool() + { + return NextBool(GetRandom()); + } + /// + /// 生成一个布尔随机数
    + ///
    + /// + public static bool NextBool(Random ran) + { + bool[] arr = { true, false }; + return arr[ran.Next(2)]; + } + /// + /// 生成一个不连续或指定值的随机值 + /// + /// 一个字符串数组 + /// + public static string NextString(string[] arr) + { + return NextString(GetRandom(), arr); + } + /// + /// 生成一个不连续或指定值的随机值 + /// + /// 一个随机值产生器 + /// 一个字符串数组 + /// + public static string NextString(Random ran, string[] arr) + { + ran ??= GetRandom(); + int n = ran.Next(arr.Length - 1); + return arr[n]; + } + /// + /// 生成一个不连续或指定值的随机值 + /// + /// 一个双精度值数组 + /// + public static double NextDouble(double[] arr) + { + return NextDouble(GetRandom(), arr); + } + /// + /// 生成不连续或指定值的随机值 + /// + /// 一个随机值产生器 + /// 一个双精度值数组 + /// + public static double NextDouble(Random ran, double[] arr) + { + ran ??= GetRandom(); + int n = ran.Next(arr.Length - 1); + return arr[n]; + } + /// + /// 生成指定范围内的整数 + /// + /// 范围最大整数值 + /// + public static int NextInt(int max) + { + return NextInt(GetRandom(), max); + } + /// + /// 生成指定范围内的整数 + /// + /// 一个随机值产生器 + /// 范围最大整数值 + /// + public static int NextInt(Random ran, int max) + { + ran ??= GetRandom(); + return ran.Next(max); + } + /// + /// 生成指定范围内的整数 + /// + /// 范围的最小整数 + /// 范围的最大整数 + /// 返回一个介于之间的整数 + public static int NextInt(int min, int max) + { + return NextInt(GetRandom(), min, max); + } + /// + /// 生成指定范围内的整数 + /// + /// 一个随机值产生器 + /// 范围的最小整数 + /// 范围的最大整数 + /// 返回一个介于之间的整数 + public static int NextInt(Random ran, int min, int max) + { + ran ??= GetRandom(); + return ran.Next(min, max); + } + + /// + /// 生成一个随机颜色 + /// + /// 返回 + public static System.Drawing.Color NextColor() + { + return NextColor(GetRandom()); + } + /// + /// 生成一个随机颜色 + /// + /// + public static System.Drawing.Color NextColor(Random ran) + { + ran ??= GetRandom(); + int R = ran.Next(255); + int G = ran.Next(255); + int B = ran.Next(255); + B = (R + G > 400) ? R + G - 400 : B;//0 : 380 - R - G; + B = (B > 255) ? 255 : B; + return System.Drawing.Color.FromArgb(R, G, B); + } + + + /* + * 知识准备: + * | 高位64位 | 低位32位 | + * Convert.ToString(int.MaxValue, 2)输出二进制 "1111111111111111111111111111111" 31个;最高位是符号位,所以少1位 + * Convert.ToString(long.MaxValue,2)输出二进制,刚好长一倍 "11111111111111111111111111111111 1111111111111111111111111111111" 63个;最高位是符号位,所以少1位 + * Convert.ToString(0xffffffffL, 2)int.MaxValue再按位多1 "1 1111111111111111111111111111111" 32个;前面的0不会打印出来 + * + * Convert.ToString(long.MaxValue>>32, 2)相当于平移高位的到低位范围,也就是上面少打印的二进制 + * 验证右移是不是高位保留,答案是 + * var a = Convert.ToInt64("101111111111111111111111111111111111111111111111111111111111111", 2); + * Convert.ToString(a >> 32,2); + * + * 解释代码: + * 0x01: + * (int)(long.MaxValue & 0xffffffffL) | (int)(long.MaxValue >> 32); + * Convert.ToString(long.MaxValue & 0xffffffffL, 2)//去掉高位:"11111111111111111111111111111111" 32个,再强转int + * 按位与&是保证符号位肯定是1,其他尽可能为0,高位被去掉只是MaxValue&0的原因,强转才是去掉高位..."尽可能"一词带来第一次随机性 + * 0x02: + * Convert.ToString((long.MaxValue >> 32), 2) //去掉低位: "1111111111111111111111111111111" 31个,再强转int + * 按位或|是尽可能为1..."尽可能"一词带来第二次随机性 + * + */ + + /// + /// 带有随机种子的随机数
    + /// 为什么这样写随机种子呢 + ///
    + /// + public static Random GetRandom() + { + var tick = DateTime.Now.Ticks; + var tickSeeds = (int)(tick & 0xffffffffL) | (int)(tick >> 32); + return new Random(tickSeeds); + } + +} diff --git a/src/IFoxCAD.Basal/Sortedset/ISet.cs b/src/IFoxCAD.Basal/Sortedset/ISet.cs index 549e9f0..22b9509 100644 --- a/src/IFoxCAD.Basal/Sortedset/ISet.cs +++ b/src/IFoxCAD.Basal/Sortedset/ISet.cs @@ -1,71 +1,65 @@ #if NET35 // ==++== -// +// // Copyright (c) Microsoft Corporation. All rights reserved. -// +// // ==--== /*============================================================ ** ** Interface: ISet -** +** ** kimhamil ** ** ** Purpose: Base interface for all generic sets. ** -** +** ===========================================================*/ namespace System.Collections.Generic { - using System; using System.Runtime.CompilerServices; /// /// Generic collection that guarantees the uniqueness of its elements, as defined - /// by some comparer. It also supports basic set operations such as Union, Intersection, + /// by some comparer. It also supports basic set operations such as Union, Intersection, /// Complement and Exclusive Complement. /// public interface ISet : ICollection { - - //Add ITEM to the set, return true if added, false if duplicate + // Add ITEM to the set, return true if added, false if duplicate new bool Add(T item); - //Transform this set into its union with the IEnumerable other + // Transform this set into its union with the IEnumerable other void UnionWith(IEnumerable other); - //Transform this set into its intersection with the IEnumberable other + // Transform this set into its intersection with the IEnumberable other void IntersectWith(IEnumerable other); - //Transform this set so it contains no elements that are also in other + // Transform this set so it contains no elements that are also in other void ExceptWith(IEnumerable other); - //Transform this set so it contains elements initially in this or in other, but not both + // Transform this set so it contains elements initially in this or in other, but not both void SymmetricExceptWith(IEnumerable other); - //Check if this set is a subset of other + // Check if this set is a subset of other bool IsSubsetOf(IEnumerable other); - //Check if this set is a superset of other + // Check if this set is a superset of other bool IsSupersetOf(IEnumerable other); - //Check if this set is a subset of other, but not the same as it + // Check if this set is a subset of other, but not the same as it bool IsProperSupersetOf(IEnumerable other); - //Check if this set is a superset of other, but not the same as it + // Check if this set is a superset of other, but not the same as it bool IsProperSubsetOf(IEnumerable other); - //Check if this set has any elements in common with other + // Check if this set has any elements in common with other bool Overlaps(IEnumerable other); - //Check if this set contains the same and only the same elements as other + // Check if this set contains the same and only the same elements as other bool SetEquals(IEnumerable other); - - - } - -} +} #endif \ No newline at end of file diff --git a/src/IFoxCAD.Basal/Sortedset/SR.cs b/src/IFoxCAD.Basal/Sortedset/SR.cs index 093de14..59e10a7 100644 --- a/src/IFoxCAD.Basal/Sortedset/SR.cs +++ b/src/IFoxCAD.Basal/Sortedset/SR.cs @@ -565,7 +565,7 @@ internal static string GetString(CultureInfo culture, string name) internal const string Error_InvalidRadiusValue = "Error_InvalidRadiusValue"; internal const string ToolTipString = "ToolTipString"; - //Collection Editor Resources + // Collection Editor Resources internal const string CollectionEditorCaption = "CollectionEditorCaption"; internal const string CollectionEditorProperties = "CollectionEditorProperties"; internal const string CollectionEditorPropertiesMultiSelect = "CollectionEditorPropertiesMultiSelect"; @@ -604,7 +604,7 @@ internal static string GetString(CultureInfo culture, string name) internal const string Error_InsideAtomicScope = "Error_InsideAtomicScope"; internal const string SuspendReason_WorkflowChange = "SuspendReason_WorkflowChange"; - //type filtering + // type filtering internal const string FilterDescription_ParameterDeclaration = "FilterDescription_ParameterDeclaration"; internal const string FilterDescription_GenericArgument = "FilterDescription_GenericArgument"; @@ -718,7 +718,7 @@ internal static string GetString(CultureInfo culture, string name) internal const string Standard = "Standard"; internal const string Base = "Base"; - //CustomActivityDesigner + // CustomActivityDesigner internal const string ValidatorCompanionClassDesc = "ValidatorCompanionClassDesc"; internal const string ExecutorCompanionClassDesc = "ExecutorCompanionClassDesc"; internal const string DesignerCompanionClassDesc = "DesignerCompanionClassDesc"; @@ -873,7 +873,7 @@ internal static string GetString(CultureInfo culture, string name) internal const string NamespaceAndDeclaringTypeCannotBeNull = "NamespaceAndDeclaringTypeCannotBeNull"; internal const string NotElementType = "NotElementType"; - //Layout persistence errors + // Layout persistence errors internal const string Error_LayoutSerializationActivityNotFound = "Error_LayoutSerializationActivityNotFound"; internal const string Error_LayoutSerializationAssociatedActivityNotFound = "Error_LayoutSerializationAssociatedActivityNotFound"; internal const string Error_LayoutSerializationPersistenceSupport = "Error_LayoutSerializationPersistenceSupport"; diff --git a/src/IFoxCAD.Basal/Sortedset/Sortedset.cs b/src/IFoxCAD.Basal/Sortedset/Sortedset.cs index fc55659..4088582 100644 --- a/src/IFoxCAD.Basal/Sortedset/Sortedset.cs +++ b/src/IFoxCAD.Basal/Sortedset/Sortedset.cs @@ -9,9 +9,9 @@ #pragma warning disable CS8604 // 引用类型参数可能为 null。 // #define USING_HASH_SET // ==++== -// +// // Copyright (c) Microsoft Corporation. All rights reserved. -// +// // ==--== /*============================================================ ** @@ -20,7 +20,7 @@ ** Purpose: A generic sorted set. ** ** Date: August 15, 2008 -** +** ===========================================================*/ @@ -39,14 +39,14 @@ namespace System.Collections.Generic // 2. Every leaf (nil node) is black // 3. If a node is red, then both its children are black // 4. Every simple path from a node to a descendant leaf contains the same number of black nodes - // - // The basic idea of red-black tree is to represent 2-3-4 trees as standard BSTs but to add one extra bit of information - // per node to encode 3-nodes and 4-nodes. + // + // The basic idea of red-black tree is to represent 2-3-4 trees as standard BSTs but to add one extra bit of information + // per node to encode 3-nodes and 4-nodes. // 4-nodes will be represented as: B // R R - // 3 -node will be represented as: B or B + // 3 -node will be represented as: B or B // R B B R - // + // // For a detailed description of the algorithm, take a look at "Algorithms" by Robert Sedgewick. // @@ -65,11 +65,11 @@ internal enum TreeRotation [DebuggerDisplay("Count = {Count}")] #if !FEATURE_NETCORE [Serializable] - public class SortedSet : ISet, ICollection, ICollection, ISerializable, IDeserializationCallback//, IReadOnlyCollection + public class SortedSet : ISet, ICollection, ICollection, ISerializable, IDeserializationCallback// , IReadOnlyCollection { #else public class SortedSet : ISet, ICollection, ICollection, IReadOnlyCollection { -#endif //!FEATURE_NETCORE +#endif // !FEATURE_NETCORE #region local variables/constants Node root; IComparer comparer; @@ -81,7 +81,7 @@ public class SortedSet : ISet, ICollection, ICollection, IReadOnlyColle private const String CountName = "Count"; private const String ItemsName = "Items"; private const String VersionName = "Version"; - //needed for enumerator + // needed for enumerator private const String TreeName = "Tree"; private const String NodeValueName = "Item"; private const String EnumStartName = "EnumStarted"; @@ -89,13 +89,13 @@ public class SortedSet : ISet, ICollection, ICollection, IReadOnlyColle private const String EnumVersionName = "EnumVersion"; #if !FEATURE_NETCORE - //needed for TreeSubset + // needed for TreeSubset private const String minName = "Min"; private const String maxName = "Max"; private const String lBoundActiveName = "lBoundActive"; private const String uBoundActiveName = "uBoundActive"; - private SerializationInfo siInfo; //A temporary variable which we need during deserialization. + private SerializationInfo siInfo; // A temporary variable which we need during deserialization. #endif internal const int StackAllocThreshold = 100; @@ -105,7 +105,6 @@ public class SortedSet : ISet, ICollection, ICollection, IReadOnlyColle public SortedSet() { this.comparer = Comparer.Default; - } public SortedSet(IComparer comparer) @@ -121,7 +120,9 @@ public SortedSet(IComparer comparer) } - public SortedSet(IEnumerable collection) : this(collection, Comparer.Default) { } + public SortedSet(IEnumerable collection) : this(collection, Comparer.Default) + { + } public SortedSet(IEnumerable collection, IComparer comparer) : this(comparer) @@ -136,7 +137,7 @@ public SortedSet(IEnumerable collection, IComparer comparer) SortedSet baseTreeSubSet = collection as TreeSubSet; if (collection is SortedSet baseSortedSet && baseTreeSubSet == null && AreComparersEqual(this, baseSortedSet)) { - //breadth first traversal to recreate nodes + // breadth first traversal to recreate nodes if (baseSortedSet.Count == 0) { count = 0; @@ -146,7 +147,7 @@ public SortedSet(IEnumerable collection, IComparer comparer) } - //pre order way to replicate nodes + // pre order way to replicate nodes Stack theirStack = new(2 * Log2(baseSortedSet.Count) + 2); Stack myStack = new(2 * Log2(baseSortedSet.Count) + 2); Node theirCurrent = baseSortedSet.root; @@ -185,8 +186,7 @@ public SortedSet(IEnumerable collection, IComparer comparer) version = 0; } else - { //As it stands, you're doing an NlogN sort of the collection - + { // As it stands, you're doing an NlogN sort of the collection List els = new(collection); els.Sort(this.comparer); for (int i = 1; i < els.Count; i++) @@ -216,7 +216,6 @@ protected SortedSet(SerializationInfo info, StreamingContext context) #region Bulk Operation Helpers private void AddAllElements(IEnumerable collection) { - foreach (T item in collection) { if (!this.Contains(item)) @@ -250,8 +249,8 @@ private bool ContainsAllElements(IEnumerable collection) // // Do a in order walk on tree and calls the delegate for each node. // If the action delegate returns false, stop the walk. - // - // Return true if the entire tree has been walked. + // + // Return true if the entire tree has been walked. // Otherwise returns false. // internal bool InOrderTreeWalk(TreeWalkPredicate action) @@ -259,7 +258,7 @@ internal bool InOrderTreeWalk(TreeWalkPredicate action) return InOrderTreeWalk(action, false); } - // Allows for the change in traversal direction. Reverse visits nodes in descending order + // Allows for the change in traversal direction. Reverse visits nodes in descending order internal virtual bool InOrderTreeWalk(TreeWalkPredicate action, bool reverse) { if (root == null) @@ -269,7 +268,7 @@ internal virtual bool InOrderTreeWalk(TreeWalkPredicate action, bool reverse) // The maximum height of a red-black tree is 2*lg(n+1). // See page 264 of "Introduction to algorithms" by Thomas H. Cormen - // note: this should be logbase2, but since the stack grows itself, we + // note: this should be logbase2, but since the stack grows itself, we // don't want the extra cost Stack stack = new(2 * (int)(SortedSet.Log2(Count + 1))); Node current = root; @@ -297,11 +296,11 @@ internal virtual bool InOrderTreeWalk(TreeWalkPredicate action, bool reverse) } // - // Do a left to right breadth first walk on tree and + // Do a left to right breadth first walk on tree and // calls the delegate for each node. // If the action delegate returns false, stop the walk. - // - // Return true if the entire tree has been walked. + // + // Return true if the entire tree has been walked. // Otherwise returns false. // internal virtual bool BreadthFirstTreeWalk(TreeWalkPredicate action) @@ -387,23 +386,22 @@ object ICollection.SyncRoot #region Subclass helpers - //virtual function for subclass that needs to update count + // virtual function for subclass that needs to update count internal virtual void VersionCheck() { } - //virtual function for subclass that needs to do range checks + // virtual function for subclass that needs to do range checks internal virtual bool IsWithinRange(T item) { return true; - } #endregion #region ICollection Members /// - /// Add the value ITEM to the tree, returns true if added, false if duplicate + /// Add the value ITEM to the tree, returns true if added, false if duplicate /// - /// item to be added + /// item to be added public bool Add(T item) { return AddIfNotPresent(item); @@ -416,9 +414,9 @@ void ICollection.Add(T item) /// - /// Adds ITEM to the tree if not already present. Returns TRUE if value was successfully added + /// Adds ITEM to the tree if not already present. Returns TRUE if value was successfully added /// or FALSE if it is a duplicate - /// + ///
    internal virtual bool AddIfNotPresent(T item) { if (root == null) @@ -430,17 +428,17 @@ internal virtual bool AddIfNotPresent(T item) } // - // Search for a node at bottom to insert the new node. + // Search for a node at bottom to insert the new node. // If we can guanratee the node we found is not a 4-node, it would be easy to do insertion. // We split 4-nodes along the search path. - // + // Node current = root; Node parent = null; Node grandParent = null; Node greatGrandParent = null; - //even if we don't actually add to the set, we may be altering its structure (by doing rotations - //and such). so update version to disable any enumerators/subsets working on it + // even if we don't actually add to the set, we may be altering its structure (by doing rotations + // and such). so update version to disable any enumerators/subsets working on it version++; @@ -456,7 +454,7 @@ internal virtual bool AddIfNotPresent(T item) return false; } - // split a 4-node into two 2-nodes + // split a 4-node into two 2-nodes if (Is4Node(current)) { Split4Node(current); @@ -508,23 +506,22 @@ public bool Remove(T item) internal virtual bool DoRemove(T item) { - if (root == null) { return false; } - // Search for a node and then find its succesor. - // Then copy the item from the succesor to the matching node and delete the successor. - // If a node doesn't have a successor, we can replace it with its left child (if not empty.) + // Search for a node and then find its succesor. + // Then copy the item from the succesor to the matching node and delete the successor. + // If a node doesn't have a successor, we can replace it with its left child (if not empty.) // or delete the matching node. - // + // // In top-down implementation, it is important to make sure the node to be deleted is not a 2-node. - // Following code will make sure the node on the path is not a 2 Node. + // Following code will make sure the node on the path is not a 2 Node. - //even if we don't actually remove from the set, we may be altering its structure (by doing rotations - //and such). so update version to disable any enumerators/subsets working on it + // even if we don't actually remove from the set, we may be altering its structure (by doing rotations + // and such). so update version to disable any enumerators/subsets working on it version++; Node current = root; @@ -546,8 +543,8 @@ internal virtual bool DoRemove(T item) Node sibling = GetSibling(current, parent); if (sibling.IsRed) { - // If parent is a 3-node, flip the orientation of the red link. - // We can acheive this by a single rotation + // If parent is a 3-node, flip the orientation of the red link. + // We can acheive this by a single rotation // This case is converted to one of other cased below. Debug.Assert(!parent.IsRed, "parent must be a black node!"); if (parent.Right == sibling) @@ -563,7 +560,7 @@ internal virtual bool DoRemove(T item) sibling.IsRed = false; // parent's color // sibling becomes child of grandParent or root after rotation. Update link from grandParent or root ReplaceChildOfNodeOrRoot(grandParent, parent, sibling); - // sibling will become grandParent of current node + // sibling will become grandParent of current node grandParent = sibling; if (parent == match) { @@ -588,29 +585,29 @@ internal virtual bool DoRemove(T item) switch (rotation) { case TreeRotation.RightRotation: - Debug.Assert(parent.Left == sibling, "sibling must be left child of parent!"); - Debug.Assert(sibling.Left.IsRed, "Left child of sibling must be red!"); - sibling.Left.IsRed = false; - newGrandParent = RotateRight(parent); - break; + Debug.Assert(parent.Left == sibling, "sibling must be left child of parent!"); + Debug.Assert(sibling.Left.IsRed, "Left child of sibling must be red!"); + sibling.Left.IsRed = false; + newGrandParent = RotateRight(parent); + break; case TreeRotation.LeftRotation: - Debug.Assert(parent.Right == sibling, "sibling must be left child of parent!"); - Debug.Assert(sibling.Right.IsRed, "Right child of sibling must be red!"); - sibling.Right.IsRed = false; - newGrandParent = RotateLeft(parent); - break; + Debug.Assert(parent.Right == sibling, "sibling must be left child of parent!"); + Debug.Assert(sibling.Right.IsRed, "Right child of sibling must be red!"); + sibling.Right.IsRed = false; + newGrandParent = RotateLeft(parent); + break; case TreeRotation.RightLeftRotation: - Debug.Assert(parent.Right == sibling, "sibling must be left child of parent!"); - Debug.Assert(sibling.Left.IsRed, "Left child of sibling must be red!"); - newGrandParent = RotateRightLeft(parent); - break; + Debug.Assert(parent.Right == sibling, "sibling must be left child of parent!"); + Debug.Assert(sibling.Left.IsRed, "Left child of sibling must be red!"); + newGrandParent = RotateRightLeft(parent); + break; case TreeRotation.LeftRightRotation: - Debug.Assert(parent.Left == sibling, "sibling must be left child of parent!"); - Debug.Assert(sibling.Right.IsRed, "Right child of sibling must be red!"); - newGrandParent = RotateLeftRight(parent); - break; + Debug.Assert(parent.Left == sibling, "sibling must be left child of parent!"); + Debug.Assert(sibling.Right.IsRed, "Right child of sibling must be red!"); + newGrandParent = RotateLeftRight(parent); + break; } newGrandParent.IsRed = parent.IsRed; @@ -673,16 +670,21 @@ public virtual void Clear() public virtual bool Contains(T item) { - return FindNode(item) != null; } - public void CopyTo(T[] array) { CopyTo(array, 0, Count); } + public void CopyTo(T[] array) + { + CopyTo(array, 0, Count); + } - public void CopyTo(T[] array, int index) { CopyTo(array, index, Count); } + public void CopyTo(T[] array, int index) + { + CopyTo(array, index, Count); + } public void CopyTo(T[] array, int index, int count) { @@ -708,7 +710,7 @@ public void CopyTo(T[] array, int index, int count) { throw new ArgumentException(SR.GetString("SR.Arg_ArrayPlusOffTooSmall")); } - //upper bound + // upper bound count += index; InOrderTreeWalk(delegate (Node node) @@ -809,10 +811,10 @@ private static Node GetSibling(Node node, Node parent) } // After calling InsertionBalance, we need to make sure current and parent up-to-date. - // It doesn't matter if we keep grandParent and greatGrantParent up-to-date + // It doesn't matter if we keep grandParent and greatGrantParent up-to-date // because we won't need to split again in the next node. // By the time we need to split again, everything will be correctly set. - // + // private void InsertionBalance(Node current, ref Node parent, Node grandParent, Node greatGrandParent) { Debug.Assert(grandParent != null, "Grand parent cannot be null here!"); @@ -827,7 +829,7 @@ private void InsertionBalance(Node current, ref Node parent, Node grandParent, N else { // different orientaton, double rotation newChildOfGreatGrandParent = currentIsOnRight ? RotateLeftRight(grandParent) : RotateRightLeft(grandParent); - // current node now becomes the child of greatgrandparent + // current node now becomes the child of greatgrandparent parent = greatGrandParent; } // grand parent will become a child of either parent of current. @@ -872,8 +874,8 @@ private static void Merge2Nodes(Node parent, Node child1, Node child2) child2.IsRed = true; } - // Replace the child of a parent node. - // If the parent node is null, replace the root. + // Replace the child of a parent node. + // If the parent node is null, replace the root. private void ReplaceChildOfNodeOrRoot(Node parent, Node child, Node newChild) { if (parent != null) @@ -926,7 +928,6 @@ private void ReplaceNode(Node match, Node parentOfMatch, Node succesor, Node par } ReplaceChildOfNodeOrRoot(parentOfMatch, match, succesor); - } internal virtual Node FindNode(T item) @@ -948,9 +949,9 @@ internal virtual Node FindNode(T item) return null; } - //used for bithelpers. Note that this implementation is completely different - //from the Subset's. The two should not be mixed. This indexes as if the tree were an array. - //http://en.wikipedia.org/wiki/Binary_Tree#Methods_for_storing_binary_trees + // used for bithelpers. Note that this implementation is completely different + // from the Subset's. The two should not be mixed. This indexes as if the tree were an array. + // http://en.wikipedia.org/wiki/Binary_Tree#Methods_for_storing_binary_trees internal virtual int InternalIndexOf(T item) { Node current = root; @@ -1086,7 +1087,7 @@ public static IEqualityComparer> CreateSetComparer() /// Create a new set comparer for this set, where this set's members' equality is defined by the /// memberEqualityComparer. Note that this equality comparer's definition of equality must be the /// same as this set's Comparer's definition of equality - /// + /// public static IEqualityComparer> CreateSetComparer(IEqualityComparer memberEqualityComparer) { return new SortedSetEqualityComparer(memberEqualityComparer); @@ -1096,7 +1097,7 @@ public static IEqualityComparer> CreateSetComparer(IEqualityCompare /// /// Decides whether these sets are the same, given the comparer. If the EC's are the same, we can /// just use SetEquals, but if they aren't then we have to manually check with the given comparer - /// + /// internal static bool SortedSetEquals(SortedSet set1, SortedSet set2, IComparer comparer) { // handle null cases first @@ -1136,11 +1137,10 @@ internal static bool SortedSetEquals(SortedSet set1, SortedSet set2, IComp } return true; } - } - //This is a little frustrating because we can't support more sorted structures + // This is a little frustrating because we can't support more sorted structures private static bool AreComparersEqual(SortedSet set1, SortedSet set2) { return set1.Comparer.Equals(set2.Comparer); @@ -1171,11 +1171,11 @@ internal T[] ToArray() #region ISet Members /// - /// Transform this set into its union with the IEnumerable OTHER - ///Attempts to insert each element and rejects it if it exists. - /// NOTE: The caller object is important as UnionWith uses the Comparator - ///associated with THIS to check equality - /// Throws ArgumentNullException if OTHER is null + /// Transform this set into its union with the IEnumerable OTHER + /// Attempts to insert each element and rejects it if it exists. + /// NOTE: The caller object is important as UnionWith uses the Comparator + /// associated with THIS to check equality + /// Throws ArgumentNullException if OTHER is null /// /// public void UnionWith(IEnumerable other) @@ -1202,8 +1202,8 @@ public void UnionWith(IEnumerable other) if (s != null && t == null && AreComparersEqual(this, s) && (s.Count > this.Count / 2)) - { //this actually hurts if N is much greater than M the /2 is arbitrary - //first do a merge sort to an array. + { // this actually hurts if N is much greater than M the /2 is arbitrary + // first do a merge sort to an array. T[] merged = new T[s.Count + this.Count]; int c = 0; Enumerator mine = this.GetEnumerator(); @@ -1239,9 +1239,9 @@ public void UnionWith(IEnumerable other) } while (remaining.MoveNext()); } - //now merged has all c elements + // now merged has all c elements - //safe to gc the root, we have all the elements + // safe to gc the root, we have all the elements root = null; @@ -1258,12 +1258,9 @@ public void UnionWith(IEnumerable other) private static Node ConstructRootFromSortedArray(T[] arr, int startIndex, int endIndex, Node redNode) { - - - - //what does this do? - //you're given a sorted array... say 1 2 3 4 5 6 - //2 cases: + // what does this do? + // you're given a sorted array... say 1 2 3 4 5 6 + // 2 cases: // If there are odd # of elements, pick the middle element (in this case 4), and compute // its left and right branches // If there are even # of elements, pick the left middle element, save the right middle element @@ -1272,16 +1269,16 @@ private static Node ConstructRootFromSortedArray(T[] arr, int startIndex, int en // now add 4 as a red node to the lowest element on the right branch // 3 3 // 1 5 -> 1 5 - // 2 6 2 4 6 + // 2 6 2 4 6 // As we're adding to the leftmost of the right branch, nesting will not hurt the red-black properties // Leaf nodes are red if they have no sibling (if there are 2 nodes or if a node trickles // down to the bottom - //the iterative way to do this ends up wasting more space than it saves in stack frames (at - //least in what i tried) - //so we're doing this recursively - //base cases are described below + // the iterative way to do this ends up wasting more space than it saves in stack frames (at + // least in what i tried) + // so we're doing this recursively + // base cases are described below int size = endIndex - startIndex + 1; if (size == 0) { @@ -1318,7 +1315,6 @@ private static Node ConstructRootFromSortedArray(T[] arr, int startIndex, int en if (redNode != null) { root.Left.Left = redNode; - } } else @@ -1338,17 +1334,16 @@ private static Node ConstructRootFromSortedArray(T[] arr, int startIndex, int en } } return root; - } /// - /// Transform this set into its intersection with the IEnumerable OTHER - /// NOTE: The caller object is important as IntersectionWith uses the - /// comparator associated with THIS to check equality - /// Throws ArgumentNullException if OTHER is null + /// Transform this set into its intersection with the IEnumerable OTHER + /// NOTE: The caller object is important as IntersectionWith uses the + /// comparator associated with THIS to check equality + /// Throws ArgumentNullException if OTHER is null /// - /// + /// public virtual void IntersectWith(IEnumerable other) { if (other == null) @@ -1359,18 +1354,16 @@ public virtual void IntersectWith(IEnumerable other) if (Count == 0) return; - //HashSet optimizations can't be done until equality comparers and comparers are related + // HashSet optimizations can't be done until equality comparers and comparers are related - //Technically, this would work as well with an ISorted + // Technically, this would work as well with an ISorted TreeSubSet t = this as TreeSubSet; if (t != null) VersionCheck(); - //only let this happen if i am also a SortedSet, not a SubSet + // only let this happen if i am also a SortedSet, not a SubSet if (other is SortedSet s && t == null && AreComparersEqual(this, s)) { - - - //first do a merge sort to an array. + // first do a merge sort to an array. T[] merged = new T[this.Count]; int c = 0; Enumerator mine = this.GetEnumerator(); @@ -1398,9 +1391,9 @@ public virtual void IntersectWith(IEnumerable other) } } - //now merged has all c elements + // now merged has all c elements - //safe to gc the root, we have all the elements + // safe to gc the root, we have all the elements root = null; root = SortedSet.ConstructRootFromSortedArray(merged, 0, c - 1, null); @@ -1427,16 +1420,15 @@ internal virtual void IntersectWithEnumerable(IEnumerable other) } this.Clear(); AddAllElements(toSave); - } /// - /// Transform this set into its complement with the IEnumerable OTHER - /// NOTE: The caller object is important as ExceptWith uses the - /// comparator associated with THIS to check equality - /// Throws ArgumentNullException if OTHER is null + /// Transform this set into its complement with the IEnumerable OTHER + /// NOTE: The caller object is important as ExceptWith uses the + /// comparator associated with THIS to check equality + /// Throws ArgumentNullException if OTHER is null /// /// public void ExceptWith(IEnumerable other) @@ -1458,7 +1450,7 @@ public void ExceptWith(IEnumerable other) if (other is SortedSet asSorted && AreComparersEqual(this, asSorted)) { - //outside range, no point doing anything + // outside range, no point doing anything if (!(comparer.Compare(asSorted.Max, this.Min) < 0 || comparer.Compare(asSorted.Min, this.Max) > 0)) { T min = this.Min; @@ -1472,7 +1464,6 @@ public void ExceptWith(IEnumerable other) Remove(item); } } - } else { @@ -1482,9 +1473,9 @@ public void ExceptWith(IEnumerable other) /// /// Transform this set so it contains elements in THIS or OTHER but not both - /// NOTE: The caller object is important as SymmetricExceptWith uses the - /// comparator associated with THIS to check equality - /// Throws ArgumentNullException if OTHER is null + /// NOTE: The caller object is important as SymmetricExceptWith uses the + /// comparator associated with THIS to check equality + /// Throws ArgumentNullException if OTHER is null /// /// public void SymmetricExceptWith(IEnumerable other) @@ -1522,21 +1513,21 @@ public void SymmetricExceptWith(IEnumerable other) #endif else { - //need perf improvement on this + // need perf improvement on this T[] elements = (new List(other)).ToArray(); Array.Sort(elements, this.Comparer); SymmetricExceptWithSameEC(elements); } } - //OTHER must be a set + // OTHER must be a set internal void SymmetricExceptWithSameEC(ISet other) { foreach (T item in other) { - //yes, it is classier to say - //if (!this.Remove(item))this.Add(item); - //but this ends up saving on rotations + // yes, it is classier to say + // if (!this.Remove(item))this.Add(item); + // but this ends up saving on rotations if (this.Contains(item)) { this.Remove(item); @@ -1548,7 +1539,7 @@ internal void SymmetricExceptWithSameEC(ISet other) } } - //OTHER must be a sorted array + // OTHER must be a sorted array internal void SymmetricExceptWithSameEC(T[] other) { if (other.Length == 0) @@ -1600,8 +1591,8 @@ public bool IsSubsetOf(IEnumerable other) } else { - //worst case: mark every element in my set and see if i've counted all - //O(MlogN) + // worst case: mark every element in my set and see if i've counted all + // O(MlogN) ElementCount result = CheckUniqueAndUnfoundElements(other, false); return (result.uniqueCount == Count && result.unfoundCount >= 0); @@ -1617,7 +1608,6 @@ private bool IsSubsetOfSortedSetWithSameEC(SortedSet asSorted) return false; } return true; - } @@ -1642,13 +1632,13 @@ public bool IsProperSubsetOf(IEnumerable other) #if USING_HASH_SET - //do it one way for HashSets + // do it one way for HashSets HashSet asHash = other as HashSet; if (asHash != null && comparer.Equals(Comparer.Default) && asHash.Comparer.Equals(EqualityComparer.Default)) { return asHash.IsProperSupersetOf(this); } #endif - //another for sorted sets with the same comparer + // another for sorted sets with the same comparer if (other is SortedSet asSorted && AreComparersEqual(this, asSorted)) { if (this.Count >= asSorted.Count) @@ -1657,8 +1647,8 @@ public bool IsProperSubsetOf(IEnumerable other) } - //worst case: mark every element in my set and see if i've counted all - //O(MlogN). + // worst case: mark every element in my set and see if i've counted all + // O(MlogN). ElementCount result = CheckUniqueAndUnfoundElements(other, false); return (result.uniqueCount == Count && result.unfoundCount > 0); } @@ -1679,14 +1669,14 @@ public bool IsSupersetOf(IEnumerable other) if ((other as ICollection) != null && (other as ICollection).Count == 0) return true; - //do it one way for HashSets + // do it one way for HashSets #if USING_HASH_SET HashSet asHash = other as HashSet; if (asHash != null && comparer.Equals(Comparer.Default) && asHash.Comparer.Equals(EqualityComparer.Default)) { return asHash.IsSubsetOf(this); } #endif - //another for sorted sets with the same comparer + // another for sorted sets with the same comparer if (other is SortedSet asSorted && AreComparersEqual(this, asSorted)) { if (this.Count < asSorted.Count) @@ -1699,7 +1689,7 @@ public bool IsSupersetOf(IEnumerable other) } return true; } - //and a third for everything else + // and a third for everything else return ContainsAllElements(other); } @@ -1723,14 +1713,14 @@ public bool IsProperSupersetOf(IEnumerable other) return true; #if USING_HASH_SET - //do it one way for HashSets - + // do it one way for HashSets + HashSet asHash = other as HashSet; if (asHash != null && comparer.Equals(Comparer.Default) && asHash.Comparer.Equals(EqualityComparer.Default)) { return asHash.IsProperSubsetOf(this); } #endif - //another way for sorted sets + // another way for sorted sets if (other is SortedSet asSorted && AreComparersEqual(asSorted, this)) { if (asSorted.Count >= this.Count) @@ -1745,10 +1735,10 @@ public bool IsProperSupersetOf(IEnumerable other) } - //worst case: mark every element in my set and see if i've counted all - //O(MlogN) - //slight optimization, put it into a HashSet and then check can do it in O(N+M) - //but slower in better cases + wastes space + // worst case: mark every element in my set and see if i've counted all + // O(MlogN) + // slight optimization, put it into a HashSet and then check can do it in O(N+M) + // but slower in better cases + wastes space ElementCount result = CheckUniqueAndUnfoundElements(other, true); return (result.uniqueCount < Count && result.unfoundCount == 0); } @@ -1792,8 +1782,8 @@ public bool SetEquals(IEnumerable other) return mineEnded && theirsEnded; } - //worst case: mark every element in my set and see if i've counted all - //O(N) by size of other + // worst case: mark every element in my set and see if i've counted all + // O(N) by size of other ElementCount result = CheckUniqueAndUnfoundElements(other, true); return (result.uniqueCount == Count && result.unfoundCount == 0); } @@ -1841,14 +1831,14 @@ public bool Overlaps(IEnumerable other) /// /// This works similar to HashSet's CheckUniqueAndUnfound (description below), except that the bit /// array maps differently than in the HashSet. We can only use this for the bulk boolean checks. - /// + /// /// Determines counts that can be used to determine equality, subset, and superset. This /// is only used when other is an IEnumerable and not a HashSet. If other is a HashSet - /// these properties can be checked faster without use of marking because we can assume + /// these properties can be checked faster without use of marking because we can assume /// other has no duplicates. - /// + /// /// The following count checks are performed by callers: - /// 1. Equals: checks if unfoundCount = 0 and uniqueFoundCount = Count; i.e. everything + /// 1. Equals: checks if unfoundCount = 0 and uniqueFoundCount = Count; i.e. everything /// in other is in this and everything in this is in other /// 2. Subset: checks if unfoundCount >= 0 and uniqueFoundCount = Count; i.e. other may /// have elements not in this and everything in this is in other @@ -1857,7 +1847,7 @@ public bool Overlaps(IEnumerable other) /// 4. Proper superset: checks if unfound count = 0 and uniqueFoundCount strictly less /// than Count; i.e. everything in other was in this and this had at least one element /// not contained in other. - /// + /// /// An earlier implementation used delegates to perform these checks rather than returning /// an ElementCount struct; however this was changed due to the perf overhead of delegates. /// @@ -1876,7 +1866,7 @@ private unsafe ElementCount CheckUniqueAndUnfoundElements(IEnumerable other, { ElementCount result; - // need special case in case this has no elements. + // need special case in case this has no elements. if (Count == 0) { int numElementsInOther = 0; @@ -1965,7 +1955,6 @@ public int RemoveWhere(Predicate match) } return actuallyRemoved; - } @@ -2009,7 +1998,7 @@ public IEnumerable Reverse() /// Any changes made to the subset reflect in the actual tree /// /// Lowest Value allowed in the subset - /// Highest Value allowed in the subset + /// Highest Value allowed in the subset public virtual SortedSet GetViewBetween(T lowerValue, T upperValue) { if (Comparer.Compare(lowerValue, upperValue) > 0) @@ -2036,7 +2025,7 @@ internal virtual bool VersionUpToDate() /// This class represents a subset view into the tree. Any changes to this view /// are reflected in the actual tree. Uses the Comparator of the underlying tree. /// - /// + /// #if !FEATURE_NETCORE [Serializable] internal sealed class TreeSubSet : SortedSet, ISerializable, IDeserializationCallback @@ -2046,12 +2035,12 @@ internal sealed class TreeSubSet : SortedSet { #endif SortedSet underlying; T min, max; - //these exist for unbounded collections - //for instance, you could allow this subset to be defined for i>10. The set will throw if - //anything <=10 is added, but there is no upperbound. These features Head(), Tail(), were punted - //in the spec, and are not available, but the framework is there to make them available at some point. + // these exist for unbounded collections + // for instance, you could allow this subset to be defined for i>10. The set will throw if + // anything <=10 is added, but there is no upperbound. These features Head(), Tail(), were punted + // in the spec, and are not available, but the framework is there to make them available at some point. bool lBoundActive, uBoundActive; - //used to see if the count is out of date + // used to see if the count is out of date #if DEBUG @@ -2069,7 +2058,7 @@ public TreeSubSet(SortedSet Underlying, T Min, T Max, bool lowerBoundActive, max = Max; lBoundActive = lowerBoundActive; uBoundActive = upperBoundActive; - root = underlying.FindRange(min, max, lBoundActive, uBoundActive); // root is first element within range + root = underlying.FindRange(min, max, lBoundActive, uBoundActive); // root is first element within range count = 0; version = -1; VersionCheckImpl(); @@ -2095,11 +2084,10 @@ private TreeSubSet(SerializationInfo info, StreamingContext context) /// /// Additions to this tree need to be added to the underlying tree as well - /// + /// internal override bool AddIfNotPresent(T item) { - if (!IsWithinRange(item)) { ThrowHelper.ThrowArgumentOutOfRangeException(ExceptionArgument.collection); @@ -2126,7 +2114,6 @@ public override bool Contains(T item) internal override bool DoRemove(T item) { // todo: uppercase this and others - if (!IsWithinRange(item)) { return false; @@ -2142,8 +2129,6 @@ internal override bool DoRemove(T item) public override void Clear() { - - if (count == 0) { return; @@ -2164,7 +2149,6 @@ public override void Clear() internal override bool IsWithinRange(T item) { - int comp = (lBoundActive ? Comparer.Compare(min, item) : -1); if (comp > 0) { @@ -2189,7 +2173,7 @@ internal override bool InOrderTreeWalk(TreeWalkPredicate action, Boolean reve // The maximum height of a red-black tree is 2*lg(n+1). // See page 264 of "Introduction to algorithms" by Thomas H. Cormen - Stack stack = new(2 * (int)SortedSet.Log2(count + 1)); //this is not exactly right if count is out of date, but the stack can grow + Stack stack = new(2 * (int)SortedSet.Log2(count + 1)); // this is not exactly right if count is out of date, but the stack can grow Node current = root; while (current != null) { @@ -2268,14 +2252,12 @@ internal override bool BreadthFirstTreeWalk(TreeWalkPredicate action) { processQueue.Add(current.Right); } - } return true; } internal override SortedSet.Node FindNode(T item) { - if (!IsWithinRange(item)) { return null; @@ -2287,8 +2269,8 @@ internal override SortedSet.Node FindNode(T item) return base.FindNode(item); } - //this does indexing in an inefficient way compared to the actual sortedset, but it saves a - //lot of space + // this does indexing in an inefficient way compared to the actual sortedset, but it saves a + // lot of space internal override int InternalIndexOf(T item) { int count = -1; @@ -2325,20 +2307,19 @@ private void VersionCheckImpl() - //This passes functionality down to the underlying tree, clipping edges if necessary - //There's nothing gained by having a nested subset. May as well draw it from the base - //Cannot increase the bounds of the subset, can only decrease it + // This passes functionality down to the underlying tree, clipping edges if necessary + // There's nothing gained by having a nested subset. May as well draw it from the base + // Cannot increase the bounds of the subset, can only decrease it public override SortedSet GetViewBetween(T lowerValue, T upperValue) { - if (lBoundActive && Comparer.Compare(min, lowerValue) > 0) { - //lBound = min; + // lBound = min; throw new ArgumentOutOfRangeException("lowerValue"); } if (uBoundActive && Comparer.Compare(max, upperValue) < 0) { - //uBound = max; + // uBound = max; throw new ArgumentOutOfRangeException("upperValue"); } TreeSubSet ret = (TreeSubSet)underlying.GetViewBetween(lowerValue, upperValue); @@ -2347,7 +2328,6 @@ public override SortedSet GetViewBetween(T lowerValue, T upperValue) internal override void IntersectWithEnumerable(IEnumerable other) { - List toSave = new(this.Count); foreach (T item in other) { @@ -2385,9 +2365,8 @@ protected override void GetObjectData(SerializationInfo info, StreamingContext c void IDeserializationCallback.OnDeserialization(Object sender) { - //don't do anything here as its already been done by the constructor - //OnDeserialization(sender); - + // don't do anything here as its already been done by the constructor + // OnDeserialization(sender); } protected override void OnDeserialization(Object sender) @@ -2427,20 +2406,15 @@ private void OnDeserializationImpl(Object sender) underlying.version = siInfo.GetInt32(VersionName); count = underlying.count; version = underlying.version - 1; - VersionCheck(); //this should update the count to be right and update root to be right + VersionCheck(); // this should update the count to be right and update root to be right if (count != savedCount) { ThrowHelper.ThrowSerializationException(ExceptionResource.Serialization_MismatchedCount); } siInfo = null; - } #endif // !FEATURE_NETCORE - - - - } @@ -2462,7 +2436,7 @@ protected virtual void GetObjectData(SerializationInfo info, StreamingContext co ThrowHelper.ThrowArgumentNullException(ExceptionArgument.info); } - info.AddValue(CountName, count); //This is the length of the bucket array. + info.AddValue(CountName, count); // This is the length of the bucket array. info.AddValue(ComparerName, comparer, typeof(IComparer)); info.AddValue(VersionName, version); @@ -2483,7 +2457,7 @@ protected virtual void OnDeserialization(Object sender) { if (comparer != null) { - return; //Somebody had a dependency on this class and fixed us up before the ObjectManager got to it. + return; // Somebody had a dependency on this class and fixed us up before the ObjectManager got to it. } if (siInfo == null) @@ -2516,7 +2490,7 @@ protected virtual void OnDeserialization(Object sender) } siInfo = null; } -#endif //!FEATURE_NETCORE +#endif // !FEATURE_NETCORE #endregion #region Helper Classes @@ -2529,14 +2503,14 @@ internal class Node public Node(T item) { - // The default color will be red, we never need to create a black node directly. + // The default color will be red, we never need to create a black node directly. this.Item = item; IsRed = true; } public Node(T item, bool isRed) { - // The default color will be red, we never need to create a black node directly. + // The default color will be red, we never need to create a black node directly. this.Item = item; this.IsRed = isRed; } @@ -2566,7 +2540,7 @@ public struct Enumerator : IEnumerator, IEnumerator { internal Enumerator(SortedSet set) { tree = set; - //this is a hack to make sure that the underlying subset has not been changed since + // this is a hack to make sure that the underlying subset has not been changed since // tree.VersionCheck(); @@ -2585,7 +2559,7 @@ internal Enumerator(SortedSet set) internal Enumerator(SortedSet set, bool reverse) { tree = set; - //this is a hack to make sure that the underlying subset has not been changed since + // this is a hack to make sure that the underlying subset has not been changed since // tree.VersionCheck(); version = tree.version; @@ -2598,7 +2572,6 @@ internal Enumerator(SortedSet set, bool reverse) siInfo = null; #endif Intialize(); - } #if !FEATURE_NETCORE @@ -2622,7 +2595,6 @@ private void GetObjectData(SerializationInfo info, StreamingContext context) if (info == null) { ThrowHelper.ThrowArgumentNullException(ExceptionArgument.info); - } info.AddValue(TreeName, tree, typeof(SortedSet)); info.AddValue(EnumVersionName, version); @@ -2653,23 +2625,19 @@ private void OnDeserialization(Object sender) { T item = (T)siInfo.GetValue(NodeValueName, typeof(T)); Intialize(); - //go until it reaches the value we want + // go until it reaches the value we want while (this.MoveNext()) { if (tree.Comparer.Compare(this.Current, item) == 0) break; } } - - } -#endif //!FEATURE_NETCORE +#endif // !FEATURE_NETCORE private void Intialize() { - - current = null; SortedSet.Node node = tree.root; Node next = null, other = null; @@ -2695,8 +2663,7 @@ private void Intialize() public bool MoveNext() { - - //this is a hack to make sure that the underlying subset has not been changed since + // this is a hack to make sure that the underlying subset has not been changed since // tree.VersionCheck(); @@ -2788,10 +2755,6 @@ void IEnumerator.Reset() { Reset(); } - - - - } @@ -2812,7 +2775,7 @@ internal struct ElementCount /// The value from the set that the search found, or the default value of when the search yielded no match. /// A value indicating whether the search was successful. /// - /// This can be useful when you want to reuse a previously stored reference instead of + /// This can be useful when you want to reuse a previously stored reference instead of /// a newly constructed one (so that more sharing of references can occur) or to look up /// a value that has more complete data than the value you currently have, although their /// comparer functions indicate they are equal. @@ -2832,7 +2795,7 @@ public bool TryGetValue(T equalValue, out T actualValue) // used for set checking operations (using enumerables) that rely on counting private static int Log2(int value) { - //Contract.Requires(value>0) + // Contract.Requires(value>0) int c = 0; while (value > 0) { @@ -2842,30 +2805,34 @@ private static int Log2(int value) return c; } #endregion - - } /// /// A class that generates an IEqualityComparer for this SortedSet. Requires that the definition of /// equality defined by the IComparer for this SortedSet be consistent with the default IEqualityComparer /// for the type T. If not, such an IEqualityComparer should be provided through the constructor. - /// + /// internal class SortedSetEqualityComparer : IEqualityComparer> { private readonly IComparer comparer; private readonly IEqualityComparer e_comparer; - public SortedSetEqualityComparer() : this(null, null) { } + public SortedSetEqualityComparer() : this(null, null) + { + } - public SortedSetEqualityComparer(IComparer comparer) : this(comparer, null) { } + public SortedSetEqualityComparer(IComparer comparer) : this(comparer, null) + { + } - public SortedSetEqualityComparer(IEqualityComparer memberEqualityComparer) : this(null, memberEqualityComparer) { } + public SortedSetEqualityComparer(IEqualityComparer memberEqualityComparer) : this(null, memberEqualityComparer) + { + } /// /// Create a new SetEqualityComparer, given a comparer for member order and another for member equality (these /// must be consistent in their definition of equality) - /// + /// public SortedSetEqualityComparer(IComparer comparer, IEqualityComparer memberEqualityComparer) { if (comparer == null) @@ -2884,8 +2851,8 @@ public bool Equals(SortedSet x, SortedSet y) { return SortedSet.SortedSetEquals(x, y, comparer); } - //IMPORTANT: this part uses the fact that GetHashCode() is consistent with the notion of equality in - //the set + // IMPORTANT: this part uses the fact that GetHashCode() is consistent with the notion of equality in + // the set public int GetHashCode(SortedSet obj) { int hashCode = 0; @@ -2899,7 +2866,7 @@ public int GetHashCode(SortedSet obj) return hashCode; } - // Equals method for the comparer itself. + // Equals method for the comparer itself. public override bool Equals(Object obj) { if (obj is not SortedSetEqualityComparer comparer) @@ -2913,10 +2880,7 @@ public override int GetHashCode() { return comparer.GetHashCode() ^ e_comparer.GetHashCode(); } - - } - } diff --git a/src/IFoxCAD.Basal/Sortedset/ThrowHelper.cs b/src/IFoxCAD.Basal/Sortedset/ThrowHelper.cs index 205dd1c..ebb2f41 100644 --- a/src/IFoxCAD.Basal/Sortedset/ThrowHelper.cs +++ b/src/IFoxCAD.Basal/Sortedset/ThrowHelper.cs @@ -5,8 +5,8 @@ namespace System { // This file defines an internal class used to throw exceptions in BCL code. - // The main purpose is to reduce code size. - // + // The main purpose is to reduce code size. + // // The old way to throw an exception generates quite a lot IL code and assembly code. // Following is an example: // C# source @@ -18,10 +18,10 @@ namespace System // IL_0012: newobj instance void System.ArgumentNullException::.ctor(string,string) // IL_0017: throw // which is 21bytes in IL. - // + // // So we want to get rid of the ldstr and call to Environment.GetResource in IL. // In order to do that, I created two enums: ExceptionResource, ExceptionArgument to represent the - // argument name and resource name in a small integer. The source code will be changed to + // argument name and resource name in a small integer. The source code will be changed to // ThrowHelper.ThrowArgumentNullException(ExceptionArgument.key, ExceptionResource.ArgumentNull_Key); // // The IL code will be 7 bytes. @@ -30,11 +30,11 @@ namespace System // IL_000a: call void System.ThrowHelper::ThrowArgumentNullException(valuetype System.ExceptionArgument) // IL_000f: ldarg.0 // - // This will also reduce the Jitted code size a lot. + // This will also reduce the Jitted code size a lot. + // + // It is very important we do this for generic classes because we can easily generate the same code + // multiple times for different instantiation. // - // It is very important we do this for generic classes because we can easily generate the same code - // multiple times for different instantiation. - // // < @@ -108,7 +108,7 @@ internal static void ThrowNotSupportedException(ExceptionResource resource) // Allow nulls for reference types and Nullable, but not for value types. internal static void IfNullAndNullsAreIllegalThenThrow(object value, ExceptionArgument argName) { - // Note that default(T) is not equal to null for value types except when T is Nullable. + // Note that default(T) is not equal to null for value types except when T is Nullable. if (value == null && !(default(T) == null)) ThrowHelper.ThrowArgumentNullException(argName); } @@ -123,76 +123,76 @@ internal static string GetArgumentName(ExceptionArgument argument) switch (argument) { case ExceptionArgument.array: - argumentName = "array"; - break; + argumentName = "array"; + break; case ExceptionArgument.arrayIndex: - argumentName = "arrayIndex"; - break; + argumentName = "arrayIndex"; + break; case ExceptionArgument.capacity: - argumentName = "capacity"; - break; + argumentName = "capacity"; + break; case ExceptionArgument.collection: - argumentName = "collection"; - break; + argumentName = "collection"; + break; case ExceptionArgument.converter: - argumentName = "converter"; - break; + argumentName = "converter"; + break; case ExceptionArgument.count: - argumentName = "count"; - break; + argumentName = "count"; + break; case ExceptionArgument.dictionary: - argumentName = "dictionary"; - break; + argumentName = "dictionary"; + break; case ExceptionArgument.index: - argumentName = "index"; - break; + argumentName = "index"; + break; case ExceptionArgument.info: - argumentName = "info"; - break; + argumentName = "info"; + break; case ExceptionArgument.key: - argumentName = "key"; - break; + argumentName = "key"; + break; case ExceptionArgument.match: - argumentName = "match"; - break; + argumentName = "match"; + break; case ExceptionArgument.obj: - argumentName = "obj"; - break; + argumentName = "obj"; + break; case ExceptionArgument.queue: - argumentName = "queue"; - break; + argumentName = "queue"; + break; case ExceptionArgument.stack: - argumentName = "stack"; - break; + argumentName = "stack"; + break; case ExceptionArgument.startIndex: - argumentName = "startIndex"; - break; + argumentName = "startIndex"; + break; case ExceptionArgument.value: - argumentName = "value"; - break; + argumentName = "value"; + break; case ExceptionArgument.item: - argumentName = "item"; - break; + argumentName = "item"; + break; default: - Debug.Assert(false, "The enum value is not defined, please checked ExceptionArgumentName Enum."); - return string.Empty; + Debug.Assert(false, "The enum value is not defined, please checked ExceptionArgumentName Enum."); + return string.Empty; } return argumentName; @@ -208,120 +208,119 @@ internal static string GetResourceName(ExceptionResource resource) switch (resource) { case ExceptionResource.Argument_ImplementIComparable: - resourceName = "SR.Argument_ImplementIComparable"; - break; + resourceName = "SR.Argument_ImplementIComparable"; + break; case ExceptionResource.Argument_AddingDuplicate: - resourceName = "SR.Argument_AddingDuplicate"; - break; + resourceName = "SR.Argument_AddingDuplicate"; + break; case ExceptionResource.ArgumentOutOfRange_Index: - resourceName = "SR.ArgumentOutOfRange_Index"; - break; + resourceName = "SR.ArgumentOutOfRange_Index"; + break; case ExceptionResource.ArgumentOutOfRange_NeedNonNegNum: - resourceName = "SR.ArgumentOutOfRange_NeedNonNegNum"; - break; + resourceName = "SR.ArgumentOutOfRange_NeedNonNegNum"; + break; case ExceptionResource.ArgumentOutOfRange_NeedNonNegNumRequired: - resourceName = "SR.ArgumentOutOfRange_NeedNonNegNumRequired"; - break; + resourceName = "SR.ArgumentOutOfRange_NeedNonNegNumRequired"; + break; case ExceptionResource.ArgumentOutOfRange_SmallCapacity: - resourceName = "SR.ArgumentOutOfRange_SmallCapacity"; - break; + resourceName = "SR.ArgumentOutOfRange_SmallCapacity"; + break; case ExceptionResource.Arg_ArrayPlusOffTooSmall: - resourceName = "SR.Arg_ArrayPlusOffTooSmall"; - break; + resourceName = "SR.Arg_ArrayPlusOffTooSmall"; + break; case ExceptionResource.Arg_RankMultiDimNotSupported: - resourceName = "SR.Arg_MultiRank"; - break; + resourceName = "SR.Arg_MultiRank"; + break; case ExceptionResource.Arg_NonZeroLowerBound: - resourceName = "SR.Arg_NonZeroLowerBound"; - break; + resourceName = "SR.Arg_NonZeroLowerBound"; + break; case ExceptionResource.Argument_InvalidArrayType: - resourceName = "SR.Invalid_Array_Type"; - break; + resourceName = "SR.Invalid_Array_Type"; + break; case ExceptionResource.Argument_InvalidOffLen: - resourceName = "SR.Argument_InvalidOffLen"; - break; + resourceName = "SR.Argument_InvalidOffLen"; + break; case ExceptionResource.InvalidOperation_CannotRemoveFromStackOrQueue: - resourceName = "SR.InvalidOperation_CannotRemoveFromStackOrQueue"; - break; + resourceName = "SR.InvalidOperation_CannotRemoveFromStackOrQueue"; + break; case ExceptionResource.InvalidOperation_EmptyCollection: - resourceName = "SR.InvalidOperation_EmptyCollection"; - break; + resourceName = "SR.InvalidOperation_EmptyCollection"; + break; case ExceptionResource.InvalidOperation_EmptyQueue: - resourceName = "SR.InvalidOperation_EmptyQueue"; - break; + resourceName = "SR.InvalidOperation_EmptyQueue"; + break; case ExceptionResource.InvalidOperation_EnumOpCantHappen: - resourceName = "SR.InvalidOperation_EnumOpCantHappen"; - break; + resourceName = "SR.InvalidOperation_EnumOpCantHappen"; + break; case ExceptionResource.InvalidOperation_EnumFailedVersion: - resourceName = "SR.InvalidOperation_EnumFailedVersion"; - break; + resourceName = "SR.InvalidOperation_EnumFailedVersion"; + break; case ExceptionResource.InvalidOperation_EmptyStack: - resourceName = "SR.InvalidOperation_EmptyStack"; - break; + resourceName = "SR.InvalidOperation_EmptyStack"; + break; case ExceptionResource.InvalidOperation_EnumNotStarted: - resourceName = "SR.InvalidOperation_EnumNotStarted"; - break; + resourceName = "SR.InvalidOperation_EnumNotStarted"; + break; case ExceptionResource.InvalidOperation_EnumEnded: - resourceName = "SR.InvalidOperation_EnumEnded"; - break; + resourceName = "SR.InvalidOperation_EnumEnded"; + break; case ExceptionResource.NotSupported_KeyCollectionSet: - resourceName = "SR.NotSupported_KeyCollectionSet"; - break; + resourceName = "SR.NotSupported_KeyCollectionSet"; + break; case ExceptionResource.NotSupported_SortedListNestedWrite: - resourceName = "SR.NotSupported_SortedListNestedWrite"; - break; + resourceName = "SR.NotSupported_SortedListNestedWrite"; + break; #if !SILVERLIGHT case ExceptionResource.Serialization_InvalidOnDeser: - resourceName = "SR.Serialization_InvalidOnDeser"; - break; + resourceName = "SR.Serialization_InvalidOnDeser"; + break; case ExceptionResource.Serialization_MissingValues: - resourceName = "SR.Serialization_MissingValues"; - break; + resourceName = "SR.Serialization_MissingValues"; + break; case ExceptionResource.Serialization_MismatchedCount: - resourceName = "SR.Serialization_MismatchedCount"; - break; + resourceName = "SR.Serialization_MismatchedCount"; + break; #endif case ExceptionResource.NotSupported_ValueCollectionSet: - resourceName = "SR.NotSupported_ValueCollectionSet"; - break; + resourceName = "SR.NotSupported_ValueCollectionSet"; + break; default: - Debug.Assert(false, "The enum value is not defined, please checked ExceptionArgumentName Enum."); - return string.Empty; + Debug.Assert(false, "The enum value is not defined, please checked ExceptionArgumentName Enum."); + return string.Empty; } return resourceName; } - } // // The convention for this enum is using the argument name as the enum name - // + // internal enum ExceptionArgument { obj, @@ -345,7 +344,7 @@ internal enum ExceptionArgument // // The convention for this enum is using the resource name as the enum name - // + // internal enum ExceptionResource { Argument_ImplementIComparable, diff --git a/src/IFoxCAD.Basal/Sortedset/bithelper.cs b/src/IFoxCAD.Basal/Sortedset/bithelper.cs index d68c4d8..665b435 100644 --- a/src/IFoxCAD.Basal/Sortedset/bithelper.cs +++ b/src/IFoxCAD.Basal/Sortedset/bithelper.cs @@ -9,15 +9,14 @@ namespace System.Collections.Generic { - /// /// ABOUT: - /// Helps with operations that rely on bit marking to indicate whether an item in the - /// collection should be added, removed, visited already, etc. - /// - /// BitHelper doesn't allocate the array; you must pass in an array or ints allocated on the - /// stack or heap. ToIntArrayLength() tells you the int array size you must allocate. - /// + /// Helps with operations that rely on bit marking to indicate whether an item in the + /// collection should be added, removed, visited already, etc. + /// + /// BitHelper doesn't allocate the array; you must pass in an array or ints allocated on the + /// stack or heap. ToIntArrayLength() tells you the int array size you must allocate. + /// /// USAGE: /// Suppose you need to represent a bit array of length (i.e. logical bit array length) /// BIT_ARRAY_LENGTH. Then this is the suggested way to instantiate BitHelper: @@ -31,25 +30,24 @@ namespace System.Collections.Generic /// int[] m_arrayPtr = new int[intArrayLength]; /// bitHelper = new BitHelper(m_arrayPtr, intArrayLength); /// *************************************************************************** - /// + /// /// IMPORTANT: /// The second ctor args, length, should be specified as the length of the int array, not /// the logical bit array. Because length is used for bounds checking into the int array, - /// it's especially important to get this correct for the stackalloc version. See the code - /// samples above; this is the value gotten from ToIntArrayLength(). - /// - /// The length ctor argument is the only exception; for other methods -- MarkBit and + /// it's especially important to get this correct for the stackalloc version. See the code + /// samples above; this is the value gotten from ToIntArrayLength(). + /// + /// The length ctor argument is the only exception; for other methods -- MarkBit and /// IsMarked -- pass in values as indices into the logical bit array, and it will be mapped /// to the position within the array of ints. - /// - /// + /// + /// unsafe internal class BitHelper { // should not be serialized - private const byte MarkedBitFlag = 1; private const byte IntSize = 32; @@ -63,7 +61,7 @@ unsafe internal class BitHelper // array of ints private int[] m_array; - // whether to operate on stack alloc'd or heap alloc'd array + // whether to operate on stack alloc'd or heap alloc'd array private bool useStackAlloc; /// @@ -154,7 +152,7 @@ internal unsafe bool IsMarked(int bitPosition) } /// - /// How many ints must be allocated to represent n bits. Returns (n+31)/32, but + /// How many ints must be allocated to represent n bits. Returns (n+31)/32, but /// avoids overflow /// /// @@ -163,7 +161,6 @@ internal static int ToIntArrayLength(int n) { return n > 0 ? ((n - 1) / IntSize + 1) : 0; } - } } diff --git a/src/IFoxCAD.Cad/Algorithms/Graph/Graph.cs b/src/IFoxCAD.Cad/Algorithms/Graph/Graph.cs index c1d606f..64e551b 100644 --- a/src/IFoxCAD.Cad/Algorithms/Graph/Graph.cs +++ b/src/IFoxCAD.Cad/Algorithms/Graph/Graph.cs @@ -130,11 +130,11 @@ public void RemoveVertex(Point3d pt) foreach (var item in edges.Values) { item.RemoveWhere(x => vertex.Equals(x.TargetVertex)); - //foreach (var edge in item) - //{ + // foreach (var edge in item) + // { // if (vertex.Equals(edge.TargetVertex)) // item.Remove(edge); - //} + // } } vertexs.Remove(str); } @@ -497,7 +497,7 @@ public sealed class DepthFirst public void FindAll(IGraph graph) { var total = new HashSet(); - //var graphtmp = graph.Clone(); + // var graphtmp = graph.Clone(); foreach (var item in graph.VerticesAsEnumberable) { Dfs(graph, new LinkedHashSet { item }, total); @@ -577,7 +577,7 @@ void Dfs(IGraph graph, List visited) var inv = Invert(cur,cur[0]); // O(n) var curstr = Gethashstring(cur,inv); - //Env.Print(curstr); + // Env.Print(curstr); if (Isnew(curstr)) { Curve3ds.Add(cur); diff --git a/src/IFoxCAD.Cad/Algorithms/QuadTree/QuadTree.cs b/src/IFoxCAD.Cad/Algorithms/QuadTree/QuadTree.cs index 0eb1bf8..7032b10 100644 --- a/src/IFoxCAD.Cad/Algorithms/QuadTree/QuadTree.cs +++ b/src/IFoxCAD.Cad/Algorithms/QuadTree/QuadTree.cs @@ -50,7 +50,7 @@ public class QuadTree where TEntity : QuadEntity /// 四叉树矩形范围 public QuadTree(Rect rect) { - _rootNode = new QuadTreeNode(rect, null, 0);//初始化根节点 + _rootNode = new QuadTreeNode(rect, null, 0);// 初始化根节点 _points = new(); } #endregion @@ -85,42 +85,42 @@ public void Insert(TEntity ent) var sq_Botton = _rootNode._Y; var sq_Right = _rootNode._Right; var sq_Top = _rootNode._Top; - if (ent._Y >= _rootNode._Y)//上↑增殖 + if (ent._Y >= _rootNode._Y)// 上↑增殖 { if (ent._X >= _rootNode._X) { - //右上↗增殖 + // 右上↗增殖 sq_Right += _rootNode.Width; sq_Top += _rootNode.Height; } else { - //左上↖增殖 + // 左上↖增殖 sq_Left -= _rootNode.Width; sq_Top += _rootNode.Height; } } - else//在下↓ + else// 在下↓ { if (ent._X >= _rootNode._X) { - //右下↘增殖 + // 右下↘增殖 sq_Right += _rootNode.Width; sq_Botton -= _rootNode.Height; } else { - //左下↙增殖 + // 左下↙增殖 sq_Left -= _rootNode.Width; sq_Botton -= _rootNode.Height; } } - //扩大2次方 + // 扩大2次方 var rectSquare = new Rect(sq_Left, sq_Botton, sq_Right, sq_Top); - //四叉树的旧根要作为四分之一插入 - //新根中计算原根 - //把 旧根节点 连接到 新根节点 上面,然后新根成为根 + // 四叉树的旧根要作为四分之一插入 + // 新根中计算原根 + // 把 旧根节点 连接到 新根节点 上面,然后新根成为根 var newRoot = new QuadTreeNode(rectSquare, null, 0); var insert = newRoot.Insert(_rootNode); if (insert is null) @@ -144,8 +144,8 @@ public void Insert(TEntity ent) else throw new("四叉树:新节点不对,无法连接"); - //其后的子节点层数全部增加层数, - //要加多少层取决于当前根边界属于新根边界的所在层 + // 其后的子节点层数全部增加层数, + // 要加多少层取决于当前根边界属于新根边界的所在层 var depth = insert.Depth; if (depth == 0) throw new("四叉树:插入节点是0,造成错误"); @@ -154,7 +154,7 @@ public void Insert(TEntity ent) return false; }); - //交换根控制 + // 交换根控制 _rootNode = newRoot; } @@ -172,9 +172,9 @@ public List Query(Rect rect, QuadTreeSelectMode selectMode = QuadTreeSe QuadTreeEvn.SelectMode = selectMode; var results = new List(); - //选择图元 + // 选择图元 _rootNode.Query(rect, results); - //选择点 + // 选择点 var ptge = _points.GetEnumerator(); switch (selectMode) { @@ -195,7 +195,7 @@ public List Query(Rect rect, QuadTreeSelectMode selectMode = QuadTreeSe results.Add(ptEnt); } else if (ptEnt._X > rect._Right) - break;//超过后面范围就break,因为红黑树已经排序 + break;// 超过后面范围就break,因为红黑树已经排序 } break; default: diff --git a/src/IFoxCAD.Cad/Algorithms/QuadTree/QuadTreeNode.cs b/src/IFoxCAD.Cad/Algorithms/QuadTree/QuadTreeNode.cs index 86cb5b4..017eace 100644 --- a/src/IFoxCAD.Cad/Algorithms/QuadTree/QuadTreeNode.cs +++ b/src/IFoxCAD.Cad/Algorithms/QuadTree/QuadTreeNode.cs @@ -129,13 +129,13 @@ public QuadTreeNode(Rect box, QuadTreeNode? parent, int depth) if (!Contains(rect)) return null; - //四叉树分裂,将当前节点分为四个子节点 + // 四叉树分裂,将当前节点分为四个子节点 if (NodesIsEmpty) CreateChildren(); - //当前节点边界 包含 图元包围盒 就插入 - //退出递归:4个节点都不完全包含 - //4个节点的上层 + // 当前节点边界 包含 图元包围盒 就插入 + // 退出递归:4个节点都不完全包含 + // 4个节点的上层 var nodes = Nodes; for (int i = 0; i < nodes.Length; i++) { @@ -160,31 +160,31 @@ public QuadTreeNode(Rect box, QuadTreeNode? parent, int depth) { if (!Contains(ent)) { - //Debug.WriteLine("不在四叉树边界范围"); - //Trace.WriteLine("不在四叉树边界范围"); + // Debug.WriteLine("不在四叉树边界范围"); + // Trace.WriteLine("不在四叉树边界范围"); return null; } // if (ent.IsPoint) // { - // //找到最后一层包含它的节点,然后加入它 - // //因此是跳过分裂矩形的,以免造成无限递归 + // // 找到最后一层包含它的节点,然后加入它 + // // 因此是跳过分裂矩形的,以免造成无限递归 // var minNode = GetMinNode(ent); // minNode.Contents.Add(ent); // return minNode; // } #if true2 - //方案二: - //内容数超过才分裂,防止树深度过高,但是多选过滤时候慢一点 + // 方案二: + // 内容数超过才分裂,防止树深度过高,但是多选过滤时候慢一点 if (Contents.Count > QuadTreeEvn.QuadTreeContentsCountSplit) { - //分裂出四个子节点 + // 分裂出四个子节点 if (_nodesIsEmpty) { CreateChildren(); - //分裂之后将当前层的内容扔到四个子节点, - //如果被压着,那么就不会扔到下面 + // 分裂之后将当前层的内容扔到四个子节点, + // 如果被压着,那么就不会扔到下面 for (int i = Contents.Count - 1; i >= 0; i--) { var minNode = GetMinNode(Contents[i].Box); @@ -194,8 +194,8 @@ public QuadTreeNode(Rect box, QuadTreeNode? parent, int depth) } else { - //没有分裂的话,就递归 - //退出递归:4个节点都不完全包含,内容就是他们的父亲 + // 没有分裂的话,就递归 + // 退出递归:4个节点都不完全包含,内容就是他们的父亲 var nodes = _Nodes; for (int i = 0; i < nodes.Length; i++) { @@ -203,35 +203,35 @@ public QuadTreeNode(Rect box, QuadTreeNode? parent, int depth) if (node is null) continue; - //这里需要中断.(匿名方法ForEach无法中断,会造成父节点加入内容) + // 这里需要中断.(匿名方法ForEach无法中断,会造成父节点加入内容) if (node.Contains(ent)) return node.Insert(ent); } } } #else - //方案一:分裂到最细节点 + // 方案一:分裂到最细节点 - //分裂出四个子节点 + // 分裂出四个子节点 if (NodesIsEmpty) CreateChildren(); - //4个子节点开始递归 - //退出递归:4个节点都不完全包含,内容就是他们的父亲 + // 4个子节点开始递归 + // 退出递归:4个节点都不完全包含,内容就是他们的父亲 var nodes = Nodes; for (int i = 0; i < nodes.Length; i++) { var node = nodes[i]; if (node is null) continue; - //这里需要中断.(匿名方法ForEach无法中断,会造成父节点加入内容) + // 这里需要中断.(匿名方法ForEach无法中断,会造成父节点加入内容) if (node.Contains(ent)) return node.Insert(ent); } #endif - //为什么要用容器? - //相同包围盒或者四叉树分割线压着多个. + // 为什么要用容器? + // 相同包围盒或者四叉树分割线压着多个. this.Contents.Add(ent); return this; } @@ -257,17 +257,17 @@ void CreateChildren() /// /// /// - Rect[] RectSplit(Rect box) + static Rect[] RectSplit(Rect box) { var halfWidth = box.Width / 2.0; var halfHeight = box.Height / 2.0; var upperRight = new Rect(box._X + halfWidth, box._Y + halfHeight, box._Right, box._Top); var upperLeft = new Rect(box._X, box._Y + halfHeight, box._Right - halfWidth, box._Top); - var lowerleft = new Rect(box._X, box._Y, box._Right - halfWidth, box._Top - halfHeight);//基础 + var lowerleft = new Rect(box._X, box._Y, box._Right - halfWidth, box._Top - halfHeight);// 基础 var lowerRight = new Rect(box._X + halfWidth, box._Y, box._Right, box._Top - halfHeight); - //依照象限顺序输出 + // 依照象限顺序输出 return new Rect[] { upperRight, upperLeft, lowerleft, lowerRight }; } #endregion @@ -279,13 +279,13 @@ Rect[] RectSplit(Rect box) /// 根据图元删除 public bool Remove(TEntity easeEnt) { - //通过图元id删除无疑是非常低效的, - //1.相当于在所有的容器查找它,但是移除只会移除一次, + // 通过图元id删除无疑是非常低效的, + // 1.相当于在所有的容器查找它,但是移除只会移除一次, // 因此必须要求图元只会加入一次,才能中断检索剩余分支. - //2.这个代价还是太高,因此我们还是要默认条件,图元载入一次之后,不再改动. - //3.不再改动也不太合理,因为cad图元还是可以修改的 + // 2.这个代价还是太高,因此我们还是要默认条件,图元载入一次之后,不再改动. + // 3.不再改动也不太合理,因为cad图元还是可以修改的 - //1.处理内容 + // 1.处理内容 if (Contents.Remove(easeEnt)) { if (CountSubTree == 0) @@ -293,15 +293,15 @@ public bool Remove(TEntity easeEnt) return true; } - //2.递归子节点移除 + // 2.递归子节点移除 var nodes = Nodes; for (int i = 0; i < nodes.Length; i++) { var node = nodes[i]; if (node is null) continue; - if (node.Remove(easeEnt)) //递归进入子节点删除内容 - return true; //删除成功就中断其他节点的搜索 + if (node.Remove(easeEnt)) // 递归进入子节点删除内容 + return true; // 删除成功就中断其他节点的搜索 } return false; } @@ -317,13 +317,13 @@ void Clear(QuadTreeNode node) nodes[i]?.Clear(nodes[i]); node.Contents.Clear(); - //node.Contents = null;//重复加入时候会出错 + // node.Contents = null;// 重复加入时候会出错 node.RightTopTree = null; node.LeftTopTree = null; node.LeftBottomTree = null; node.RightBottomTree = null; node.Parent = null; - //node.Box = zoreRect; + // node.Box = zoreRect; } /// @@ -332,23 +332,23 @@ void Clear(QuadTreeNode node) /// 根据范围删除 public void Remove(Rect queryArea) { - //本节点内容移除 - if (Contents is not null && Contents.Count > 0)//从最上层的根节点开始进入 + // 本节点内容移除 + if (Contents is not null && Contents.Count > 0)// 从最上层的根节点开始进入 { for (int i = Contents.Count - 1; i >= 0; i--) { var ent = Contents[i]; - //移除之后,如果容器是0,那么这里不能直接 Contents=null, - //因为此节点下面可能还有节点, - //需要判断了其后数量0才可以清理. - //否则其后还有内容,那么此节点就是仍然可以用的. + // 移除之后,如果容器是0,那么这里不能直接 Contents=null, + // 因为此节点下面可能还有节点, + // 需要判断了其后数量0才可以清理. + // 否则其后还有内容,那么此节点就是仍然可以用的. if (queryArea.Contains(ent)) Contents.Remove(ent); } } - //同插入一样 - //跳到指定节点再搜索这个节点下面的图元 + // 同插入一样 + // 跳到指定节点再搜索这个节点下面的图元 var nodes = Nodes; for (int i = 0; i < nodes.Length; i++) { @@ -358,27 +358,27 @@ public void Remove(Rect queryArea) if (node.NodesIsEmpty) continue; - //此节点边界 完全包含 查询区域,则转到该节点,并跳过其余节点(打断此循环) + // 此节点边界 完全包含 查询区域,则转到该节点,并跳过其余节点(打断此循环) if (node.Contains(queryArea)) { node.Remove(queryArea); break; } - //查询区域 完全包含 此节点边界,提取此节点全部内容 - //跳过分析碰撞,并继续循环搜索其他节点 + // 查询区域 完全包含 此节点边界,提取此节点全部内容 + // 跳过分析碰撞,并继续循环搜索其他节点 if (queryArea.Contains(node)) { node.Clear(node); continue; } - //查询区域 与 此节点四边形边线碰撞 查询该四边形中,并继续循环搜索其他节点 - //1,角点碰撞 2,边界碰撞 + // 查询区域 与 此节点四边形边线碰撞 查询该四边形中,并继续循环搜索其他节点 + // 1,角点碰撞 2,边界碰撞 if (node.IntersectsWith(queryArea)) node.Remove(queryArea); } - //本节点内容移除之后,旗下还有内容的话, - //会跳过此处,再进入子节点进行递归,直到最后一个节点 + // 本节点内容移除之后,旗下还有内容的话, + // 会跳过此处,再进入子节点进行递归,直到最后一个节点 if (CountSubTree == 0) Clear(this); } @@ -394,33 +394,33 @@ public void Query(Rect queryArea, List results) { GetCurrentContents(queryArea, results); - //遍历子节点 + // 遍历子节点 var nodes = Nodes; for (int i = 0; i < nodes.Length; i++) { var node = nodes[i]; if (node is null) continue; - //子节点的4个子节点都是空的, - //那么表示元素会在子节点这一层啊... + // 子节点的4个子节点都是空的, + // 那么表示元素会在子节点这一层啊... if (node.NodesIsEmpty) continue; - //此节点边界 完全包含 查询区域,则转到该节点,并跳过其余节点(打断此循环) + // 此节点边界 完全包含 查询区域,则转到该节点,并跳过其余节点(打断此循环) if (node.Contains(queryArea)) { node.Query(queryArea, results); break; } - //查询区域 完全包含 此节点边界,提取此节点全部内容 - //跳过分析碰撞,并继续循环搜索其他节点 + // 查询区域 完全包含 此节点边界,提取此节点全部内容 + // 跳过分析碰撞,并继续循环搜索其他节点 if (queryArea.Contains(node)) { node.ContentsSubTree(results); continue; } - //查询区域 与 此节点四边形边线碰撞 查询该四边形中,并继续循环搜索其他节点 - //1,角点碰撞 2,边界碰撞 + // 查询区域 与 此节点四边形边线碰撞 查询该四边形中,并继续循环搜索其他节点 + // 1,角点碰撞 2,边界碰撞 if (node.IntersectsWith(queryArea)) node.Query(queryArea, results); } @@ -433,7 +433,7 @@ public void Query(Rect queryArea, List results) /// void GetCurrentContents(Rect queryArea, List results) { - //遍历当前节点内容,加入方式取决于碰撞模式 + // 遍历当前节点内容,加入方式取决于碰撞模式 if (QuadTreeEvn.SelectMode == QuadTreeSelectMode.IntersectsWith) { for (int i = 0; i < Contents.Count; i++) @@ -462,11 +462,11 @@ void GetCurrentContents(Rect queryArea, List results) public TEntity? FindNearEntity(Rect queryArea) { TEntity? resultEntity = default; - //1.找到 查找矩形 所在的节点,利用此节点的矩形. + // 1.找到 查找矩形 所在的节点,利用此节点的矩形. var queryNode = GetMinNode(queryArea); var queryAreaCenter = queryArea.CenterPoint; - //2.从根开始搜索 + // 2.从根开始搜索 // 如果搜索父亲的父亲的...内容群,它不是距离最近的,只是节点(亲属关系)最近 // 储存找过的<图元,距离> var entDic = new Dictionary(); @@ -475,10 +475,10 @@ void GetCurrentContents(Rect queryArea, List results) QuadTreeEvn.SelectMode = QuadTreeSelectMode.IntersectsWith; while (true) { - //循环找父节点大小 + // 循环找父节点大小 var hw = queryNode.Width / 2.0; var hh = queryNode.Height / 2.0; - //3.利用选区中心扩展一个节点边界大小的矩形.从而选择图元 + // 3.利用选区中心扩展一个节点边界大小的矩形.从而选择图元 // 再判断图元的与目标的距离,找到最小距离,即为最近 var minPt = new Point2d(queryAreaCenter.X - hw, queryAreaCenter.Y - hh); var maxPt = new Point2d(queryAreaCenter.X + hw, queryAreaCenter.Y + hh); @@ -490,7 +490,7 @@ void GetCurrentContents(Rect queryArea, List results) if (entDic.ContainsKey(ent)) continue; var dis = ent.CenterPoint.GetDistanceTo(queryAreaCenter); - if (dis > 1e-6)//剔除本身 + if (dis > 1e-6)// 剔除本身 entDic.Add(ent, dis); } if (entDic.Count > 0) @@ -498,9 +498,9 @@ void GetCurrentContents(Rect queryArea, List results) resultEntity = entDic.OrderBy(a => a.Value).First().Key; break; } - if (queryNode.Parent is null)//最顶层就退出 + if (queryNode.Parent is null)// 最顶层就退出 break; - queryNode = queryNode.Parent;//利用父节点矩形进行变大选区 + queryNode = queryNode.Parent;// 利用父节点矩形进行变大选区 } QuadTreeEvn.SelectMode = old; return resultEntity; @@ -516,15 +516,15 @@ void GetCurrentContents(Rect queryArea, List results) public TEntity? FindNeibor(Rect queryArea, QuadTreeFindMode findMode) { TEntity? resultEntity = default; - //1.找到 查找矩形 所在的节点,利用此节点的矩形. - //2.利用节点矩形是分裂的特点,边和边必然贴合. - //3.找到方向 findMode 拥有的节点,然后查找节点的内容 + // 1.找到 查找矩形 所在的节点,利用此节点的矩形. + // 2.利用节点矩形是分裂的特点,边和边必然贴合. + // 3.找到方向 findMode 拥有的节点,然后查找节点的内容 var queryNode = GetMinNode(queryArea); bool whileFlag = true; - //同一个节点可能包含邻居,因为四叉树的加入是图元压线, - //那么就在这里搜就得了,用中心点决定空间位置 - //但是本空间的图元可能都比它矮,无法满足条件 + // 同一个节点可能包含邻居,因为四叉树的加入是图元压线, + // 那么就在这里搜就得了,用中心点决定空间位置 + // 但是本空间的图元可能都比它矮,无法满足条件 if (queryNode.CountSubTree > 1) { resultEntity = GetNearestNeighbor(queryNode, findMode, queryArea); @@ -536,11 +536,11 @@ void GetCurrentContents(Rect queryArea, List results) while (whileFlag) { - //同一个父节点是临近的,优先获取 兄弟节点 的内容. - //循环了第二次是北方兄弟的节点, - //但是这不是一个找到临近图元的方法, - //因为临近的可能是父亲的父亲的父亲...另一个函数 FindNearEntity 写 - //本方案也仅仅作为找北方节点 + // 同一个父节点是临近的,优先获取 兄弟节点 的内容. + // 循环了第二次是北方兄弟的节点, + // 但是这不是一个找到临近图元的方法, + // 因为临近的可能是父亲的父亲的父亲...另一个函数 FindNearEntity 写 + // 本方案也仅仅作为找北方节点 var parent = queryNode.Parent; if (parent is not null) { @@ -548,7 +548,7 @@ void GetCurrentContents(Rect queryArea, List results) { case QuadTreeFindMode.Top: { - //下格才获取上格,否则导致做了无用功,上格就直接获取邻居了 + // 下格才获取上格,否则导致做了无用功,上格就直接获取邻居了 if (parent.LeftBottomTree == queryNode || parent.RightBottomTree == queryNode) resultEntity = GetNearestNeighbor(parent, findMode, queryArea); } @@ -576,9 +576,9 @@ void GetCurrentContents(Rect queryArea, List results) if (resultEntity is not null) break; - //通过 所在节点 找 邻居节点, - //拿到 邻居节点 下面的所有内容(图元) - //内容可能是空的,再从邻居那往北找...如果找到了四叉树最外层,仍然没有内容,退出循环 + // 通过 所在节点 找 邻居节点, + // 拿到 邻居节点 下面的所有内容(图元) + // 内容可能是空的,再从邻居那往北找...如果找到了四叉树最外层,仍然没有内容,退出循环 var neiborNode = FindNeiborNode(queryNode, findMode); if (neiborNode is null) continue; @@ -587,7 +587,7 @@ void GetCurrentContents(Rect queryArea, List results) resultEntity = GetNearestNeighbor(neiborNode, findMode, queryArea); break; } - if (neiborNode.Parent is null)//如果找到了四叉树最外层,仍然没有内容,退出循环 + if (neiborNode.Parent is null)// 如果找到了四叉树最外层,仍然没有内容,退出循环 break; queryNode = neiborNode; } @@ -602,7 +602,9 @@ void GetCurrentContents(Rect queryArea, List results) /// 查找方向 /// 查找节点 /// - TEntity? GetNearestNeighbor(QuadTreeNode queryNode, QuadTreeFindMode findMode, Rect queryArea) + static TEntity? GetNearestNeighbor(QuadTreeNode queryNode, + QuadTreeFindMode findMode, + Rect queryArea) { TEntity? results = default; @@ -613,7 +615,7 @@ void GetCurrentContents(Rect queryArea, List results) { case QuadTreeFindMode.Top: { - //取出Y比queryArea的还大的一个,是最近的那个 + // 取出Y比queryArea的还大的一个,是最近的那个 var qy = qcent.Y; queryNode.ContentsSubTree(lst); lst.ForEach(ent => @@ -663,7 +665,7 @@ void GetCurrentContents(Rect queryArea, List results) } if (lst.Count > 0) - return lst[0];//可能就是本体重叠 + return lst[0];// 可能就是本体重叠 return results; } @@ -681,10 +683,10 @@ QuadTreeNode GetMinNode(Rect queryArea) if (node is null) continue; - //边界包含查询面积,那么再递归查询, - //直到最后四个都不包含,那么上一个就是图元所在节点 + // 边界包含查询面积,那么再递归查询, + // 直到最后四个都不包含,那么上一个就是图元所在节点 if (node.Contains(queryArea)) - return node.GetMinNode(queryArea);//中断后面的范围,才可以返回正确的 + return node.GetMinNode(queryArea);// 中断后面的范围,才可以返回正确的 } return this; } @@ -704,19 +706,19 @@ QuadTreeNode GetMinNode(Rect queryArea) { case QuadTreeFindMode.Top: { - //判断当前节点在父节点的位置,如果是在 下格 就取对应的 上格 + // 判断当前节点在父节点的位置,如果是在 下格 就取对应的 上格 if (tar == parent.LeftBottomTree) return parent.LeftTopTree; if (tar == parent.RightBottomTree) return parent.RightTopTree; - //否则就是上格 - //找父节点的北方邻居..也就是在爷节点上面找 - //递归,此时必然是 下格,就必然返回 上格,然后退出递归 + // 否则就是上格 + // 找父节点的北方邻居..也就是在爷节点上面找 + // 递归,此时必然是 下格,就必然返回 上格,然后退出递归 var parentNeibor = FindNeiborNode(parent, QuadTreeFindMode.Top); - //父节点的北方邻居 无 子节点 + // 父节点的北方邻居 无 子节点 if (parentNeibor is null || parentNeibor.RightTopTree is null) - return parentNeibor;//返回父节点的北方邻居,比较大 - //父节点的北方邻居 有 子节点,剩下条件就只有这两 + return parentNeibor;// 返回父节点的北方邻居,比较大 + // 父节点的北方邻居 有 子节点,剩下条件就只有这两 // 如果直接返回,那么是(相同或更大), // 而找邻近图元需要的是这个(相同或更大)下面的图元,在外面对这个格子内图元用坐标分析就好了 @@ -768,8 +770,8 @@ QuadTreeNode GetMinNode(Rect queryArea) /// /// 所有的点归类到最小包围它的空间 /// - //public void PointsToMinNode() - //{ + // public void PointsToMinNode() + // { // ForEach(node => // { // for (int i = 0; i < node.Contents.Count; i++) @@ -777,7 +779,7 @@ QuadTreeNode GetMinNode(Rect queryArea) // var ent = node.Contents[i]; // if (ent.IsPoint) // { - // //如果最小包含!=当前,就是没有放在最适合的位置 + // // 如果最小包含!=当前,就是没有放在最适合的位置 // var queryNode = GetMinNode(ent); // if (queryNode != node) // { @@ -788,7 +790,7 @@ QuadTreeNode GetMinNode(Rect queryArea) // } // return false; // }); - //} + // } #endregion #region 方法 @@ -798,11 +800,11 @@ QuadTreeNode GetMinNode(Rect queryArea) /// QTAction public bool ForEach(QuadTree.QTAction action) { - //执行本节点 + // 执行本节点 if (action(this)) return true; - //递归执行本节点的子节点 + // 递归执行本节点的子节点 var nodes = Nodes; for (int i = 0; i < nodes.Length; i++) { diff --git a/src/IFoxCAD.Cad/Algorithms/QuadTree/QuadTreeSelectMode.cs b/src/IFoxCAD.Cad/Algorithms/QuadTree/QuadTreeSelectMode.cs index d180236..2cc2e37 100644 --- a/src/IFoxCAD.Cad/Algorithms/QuadTree/QuadTreeSelectMode.cs +++ b/src/IFoxCAD.Cad/Algorithms/QuadTree/QuadTreeSelectMode.cs @@ -5,8 +5,8 @@ /// public enum QuadTreeSelectMode { - IntersectsWith, //碰撞到就选中 - Contains, //全包含才选中 + IntersectsWith, // 碰撞到就选中 + Contains, // 全包含才选中 } /// @@ -14,8 +14,8 @@ public enum QuadTreeSelectMode /// public enum QuadTreeFindMode { - Top = 1, //上 - Bottom = 2, //下 - Left = 4, //左 - Right = 8, //右 + Top = 1, // 上 + Bottom = 2, // 下 + Left = 4, // 左 + Right = 8, // 右 } \ No newline at end of file diff --git a/src/IFoxCAD.Cad/Algorithms/QuadTree/Rect.cs b/src/IFoxCAD.Cad/Algorithms/QuadTree/Rect.cs index c533ddb..7639104 100644 --- a/src/IFoxCAD.Cad/Algorithms/QuadTree/Rect.cs +++ b/src/IFoxCAD.Cad/Algorithms/QuadTree/Rect.cs @@ -13,7 +13,7 @@ public TolerancePoint2d(double tolerance = 1e-6) _tolerance = tolerance; } - public bool Equals(Point2d a, Point2d b)//Point3d是struct不会为null + public bool Equals(Point2d a, Point2d b)// Point3d是struct不会为null { /*默认规则是==是0容差,Eq是有容差*/ // 方形限定 @@ -30,9 +30,9 @@ public bool Equals(Point2d a, Point2d b)//Point3d是struct不会为null public int GetHashCode(Point2d obj) { - //结构体直接返回 obj.GetHashCode(); Point3d ToleranceDistinct3d - //因为结构体是用可值叠加来判断?或者因为结构体兼备了一些享元模式的状态? - //而类是构造的指针,所以取哈希值要改成x+y+z..s给Equals判断用,+是会溢出,所以用^ + // 结构体直接返回 obj.GetHashCode(); Point3d ToleranceDistinct3d + // 因为结构体是用可值叠加来判断?或者因为结构体兼备了一些享元模式的状态? + // 而类是构造的指针,所以取哈希值要改成x+y+z..s给Equals判断用,+是会溢出,所以用^ return (int)obj.X ^ (int)obj.Y;// ^ (int)obj.Z; } } @@ -53,9 +53,9 @@ public class Rect : IEquatable, IComparable #pragma warning restore CA2211 // 非常量字段应当不可见 #region 字段 - //这里的成员不要用{get}封装成属性,否则会导致跳转了一次函数, - //10w图元将会从187毫秒变成400毫秒 - //不用 protected 否则子类传入Rect对象进来无法用 + // 这里的成员不要用{get}封装成属性,否则会导致跳转了一次函数, + // 10w图元将会从187毫秒变成400毫秒 + // 不用 protected 否则子类传入Rect对象进来无法用 internal double _X; internal double _Y; internal double _Right; @@ -195,12 +195,12 @@ public bool Equals(Rect? b) } public static bool operator ==(Rect? a, Rect? b) { - //此处地方不允许使用==null,因为此处是定义 + // 此处地方不允许使用==null,因为此处是定义 if (b is null) return a is null; else if (a is null) return false; - if (ReferenceEquals(a, b))//同一对象 + if (ReferenceEquals(a, b))// 同一对象 return true; return a.Equals(b, 0); @@ -213,7 +213,7 @@ public bool Equals(Rect? b, double tolerance = 1e-6) { if (b is null) return false; - if (ReferenceEquals(this, b)) //同一对象 + if (ReferenceEquals(this, b)) // 同一对象 return true; return Math.Abs(_X - b._X) < tolerance && @@ -274,18 +274,18 @@ public Point2d[] GetCommonPoint(Rect other) public Point2d[] ToPoints() { - Point2d a = MinPoint;//min + Point2d a = MinPoint;// min Point2d b = new(_Right, _Y); - Point2d c = MaxPoint;//max + Point2d c = MaxPoint;// max Point2d d = new(_X, _Top); return new Point2d[] { a, b, c, d }; } public (Point2d boxMin, Point2d boxRigthDown, Point2d boxMax, Point2d boxLeftUp) ToPoints4() { - Point2d a = MinPoint;//min + Point2d a = MinPoint;// min Point2d b = new(_Right, _Y); - Point2d c = MaxPoint;//max + Point2d c = MaxPoint;// max Point2d d = new(_X, _Top); return (a, b, c, d); } @@ -311,21 +311,21 @@ public static bool IsRectAngle(List? ptList, double tolerance = 1e-8) var pts = ptList.ToList(); /* 消重,不这里设置,否则这不是一个正确的单元测试 - * //var ptList = pts.Distinct().ToList(); + * // var ptList = pts.Distinct().ToList(); * var ptList = pts.DistinctExBy((a, b) => a.DistanceTo(b) < 1e-6).ToList(); */ if (ptList.Count == 5) { - //首尾点相同移除最后 + // 首尾点相同移除最后 if (pts[0].IsEqualTo(pts[^1], CadTolerance)) pts.RemoveAt(pts.Count - 1); } if (pts.Count != 4) return false; - //最快的方案 - //点乘求值法:(为了处理 正梯形/平行四边形 需要三次) - //这里的容差要在1e-8内,因为点乘的三次浮点数乘法会令精度变低 + // 最快的方案 + // 点乘求值法:(为了处理 正梯形/平行四边形 需要三次) + // 这里的容差要在1e-8内,因为点乘的三次浮点数乘法会令精度变低 var dot = DotProductValue(pts[0], pts[1], pts[3]); if (Math.Abs(dot) < tolerance) { @@ -366,7 +366,7 @@ public static bool IsRect(List? ptList, double tolerance = 1e-10) var pts = ptList.ToList(); if (ptList.Count == 5) { - //首尾点相同移除最后 + // 首尾点相同移除最后 if (pts[0].IsEqualTo(pts[^1], CadTolerance)) pts.RemoveAt(pts.Count - 1); } @@ -389,16 +389,16 @@ public static (Point2d boxMin, Point2d boxMax) GetMinMax(IEnumerable pt var xMax = double.MinValue; var yMin = double.MaxValue; var yMax = double.MinValue; - //var zMin = double.MaxValue; - //var zMax = double.MinValue; + // var zMin = double.MaxValue; + // var zMax = double.MinValue; pts.ForEach(p => { xMin = Math.Min(p.X, xMin); xMax = Math.Max(p.X, xMax); yMin = Math.Min(p.Y, yMin); yMax = Math.Max(p.Y, yMax); - //zMin = Math.Min(p.Z, zMin); - //zMax = Math.Max(p.Z, zMax); + // zMin = Math.Min(p.Z, zMin); + // zMax = Math.Max(p.Z, zMax); }); return (new Point2d(xMin, yMin), new Point2d(xMax, yMax)); } @@ -416,7 +416,7 @@ public static bool RectAnglePointOrder(List? pts) if (!Rect.IsRectAngle(pts)) return false; - //获取min和max点(非包围盒) + // 获取min和max点(非包围盒) pts = pts.OrderBy(a => a.X).ThenBy(a => a.Y).ToList(); var minPt = pts.First(); var maxPt = pts.Last(); @@ -424,14 +424,14 @@ public static bool RectAnglePointOrder(List? pts) link.AddRange(pts); pts.Clear(); - //排序这四个点,左下/右下/右上/左上 + // 排序这四个点,左下/右下/右上/左上 var node = link.Find(minPt); for (int i = 0; i < 4; i++) { pts.Add(node!.Value); node = node.Next; } - //保证是逆时针 + // 保证是逆时针 var isAcw = CrossAclockwise(pts[0], pts[1], pts[2]); if (!isAcw) (pts[3], pts[1]) = (pts[1], pts[3]); @@ -470,7 +470,7 @@ static double Cross(Point2d o, Point2d a, Point2d b) /// b点在oa的逆时针 static bool CrossAclockwise(Point2d o, Point2d a, Point2d b) { - return Cross(o, a, b) > -1e-6;//浮点数容差考虑 + return Cross(o, a, b) > -1e-6;// 浮点数容差考虑 } #if !WinForm @@ -501,11 +501,11 @@ public static void XCollision(List box, Func collisionProcessing, Action lastProcessing) where T : Rect { - //先排序X:不需要Y排序,因为Y的上下浮动不共X .ThenBy(a => a.Box.Y) - //因为先排序就可以有序遍历x区间,超过就break,达到更快 + // 先排序X:不需要Y排序,因为Y的上下浮动不共X .ThenBy(a => a.Box.Y) + // 因为先排序就可以有序遍历x区间,超过就break,达到更快 box = box.OrderBy(a => a._X).ToList(); - //遍历所有图元 + // 遍历所有图元 for (int i = 0; i < box.Count; i++) { var oneRect = box[i]; @@ -514,14 +514,14 @@ public static void XCollision(List box, bool actionlast = true; - //搜索范围要在 one 的头尾中间的部分 + // 搜索范围要在 one 的头尾中间的部分 for (int j = i + 1; j < box.Count; j++) { var twoRect = box[j]; - //x碰撞:矩形2的Left 在 矩形1[Left-Right]闭区间;穿过的话,也必然有自己的Left因此不需要处理 + // x碰撞:矩形2的Left 在 矩形1[Left-Right]闭区间;穿过的话,也必然有自己的Left因此不需要处理 if (oneRect._X <= twoRect._X && twoRect._X <= oneRect._Right) { - //y碰撞,那就是真的碰撞了 + // y碰撞,那就是真的碰撞了 if ((oneRect._Top >= twoRect._Top && twoRect._Top >= oneRect._Y) /*包容上边*/ || (oneRect._Top >= twoRect._Y && twoRect._Y >= oneRect._Y) /*包容下边*/ || (twoRect._Top >= oneRect._Top && oneRect._Y >= twoRect._Y)) /*穿过*/ @@ -529,13 +529,13 @@ public static void XCollision(List box, if (collisionProcessing(oneRect, twoRect)) actionlast = false; } - //这里想中断y高过它的无意义比较, - //但是必须排序Y,而排序Y必须同X,而这里不是同X(而是同X区间),所以不能中断 - //而做到X区间排序,就必须创造一个集合,再排序这个集合, - //会导致每个图元都拥有一次X区间集合,开销更巨大(因此放弃). + // 这里想中断y高过它的无意义比较, + // 但是必须排序Y,而排序Y必须同X,而这里不是同X(而是同X区间),所以不能中断 + // 而做到X区间排序,就必须创造一个集合,再排序这个集合, + // 会导致每个图元都拥有一次X区间集合,开销更巨大(因此放弃). } else - break;//因为已经排序了,后续的必然超过 x碰撞区间 + break;// 因为已经排序了,后续的必然超过 x碰撞区间 } if (actionlast) @@ -548,10 +548,10 @@ public static void XCollision(List box, #region 转换类型 #if !WinForm // 隐式转换(相当于是重载赋值运算符) - //public static implicit operator Rect(System.Windows.Rect rect) - //{ + // public static implicit operator Rect(System.Windows.Rect rect) + // { // return new Rect(rect.Left, rect.Bottom, rect.Right, rect.Top); - //} + // } public static implicit operator Rect(System.Drawing.RectangleF rect) { return new Rect(rect.Left, rect.Bottom, rect.Right, rect.Top); diff --git a/src/IFoxCAD.Cad/Algorithms/QuadTree/Utility.cs b/src/IFoxCAD.Cad/Algorithms/QuadTree/Utility.cs deleted file mode 100644 index 2a0be8e..0000000 --- a/src/IFoxCAD.Cad/Algorithms/QuadTree/Utility.cs +++ /dev/null @@ -1,60 +0,0 @@ -using System; - -namespace IFoxCAD.Cad; - -public static class Utility -{ - /// - /// 带有随机种子的随机数 - /// 为什么这样写随机种子呢 - /// - /// - public static Random GetRandom() - { - var tick = DateTime.Now.Ticks; - - /* - * 知识准备: - * | 高位64位 | 低位32位 | - * Convert.ToString(int.MaxValue, 2)输出二进制 "1111111111111111111111111111111" 31个;最高位是符号位,所以少1位 - * Convert.ToString(long.MaxValue,2)输出二进制,刚好长一倍 "11111111111111111111111111111111 1111111111111111111111111111111" 63个;最高位是符号位,所以少1位 - * Convert.ToString(0xffffffffL, 2)int.MaxValue再按位多1 "1 1111111111111111111111111111111" 32个;前面的0不会打印出来 - * - * Convert.ToString(long.MaxValue>>32, 2)相当于平移高位的到低位范围,也就是上面少打印的二进制 - * 验证右移是不是高位保留,答案是 - * var a = Convert.ToInt64("101111111111111111111111111111111111111111111111111111111111111", 2); - * Convert.ToString(a >> 32,2); - * - * 解释代码: - * 0x01: - * (int)(long.MaxValue & 0xffffffffL) | (int)(long.MaxValue >> 32); - * Convert.ToString(long.MaxValue & 0xffffffffL, 2)//去掉高位:"11111111111111111111111111111111" 32个,再强转int - * 按位与&是保证符号位肯定是1,其他尽可能为0,高位被去掉只是MaxValue&0的原因,强转才是去掉高位..."尽可能"一词带来第一次随机性 - * 0x02: - * Convert.ToString((long.MaxValue >> 32), 2) //去掉低位: "1111111111111111111111111111111" 31个,再强转int - * 按位或|是尽可能为1..."尽可能"一词带来第二次随机性 - * - */ - - var tickSeeds = (int)(tick & 0xffffffffL) | (int)(tick >> 32); - return new Random(tickSeeds); - } - - /// - /// 随机颜色 - /// - /// - public static System.Drawing.Color RandomColor - { - get - { - var ran = GetRandom(); - int R = ran.Next(255); - int G = ran.Next(255); - int B = ran.Next(255); - B = (R + G > 400) ? R + G - 400 : B;//0 : 380 - R - G; - B = (B > 255) ? 255 : B; - return System.Drawing.Color.FromArgb(R, G, B); - } - } -} \ No newline at end of file diff --git a/src/IFoxCAD.Cad/ExtensionMethod/BulgeVertexWidth.cs b/src/IFoxCAD.Cad/ExtensionMethod/BulgeVertexWidth.cs index dc7abd3..1f9e08d 100644 --- a/src/IFoxCAD.Cad/ExtensionMethod/BulgeVertexWidth.cs +++ b/src/IFoxCAD.Cad/ExtensionMethod/BulgeVertexWidth.cs @@ -1,4 +1,4 @@ -namespace IFoxCAD; +namespace IFoxCAD.Cad; /// /// 多段线的顶点,凸度,头宽,尾宽 @@ -70,7 +70,7 @@ public BulgeVertexWidth(BulgeVertex bv) /// 子段编号 public BulgeVertexWidth(Polyline pl, int index) { - var pt = pl.GetPoint2dAt(index);//这里可以3d + var pt = pl.GetPoint2dAt(index);// 这里可以3d X = pt.X; Y = pt.Y; Bulge = pl.GetBulgeAt(index); diff --git a/src/IFoxCAD.Cad/ExtensionMethod/Curve2dEx.cs b/src/IFoxCAD.Cad/ExtensionMethod/Curve2dEx.cs index 51ff4e2..c1c06c6 100644 --- a/src/IFoxCAD.Cad/ExtensionMethod/Curve2dEx.cs +++ b/src/IFoxCAD.Cad/ExtensionMethod/Curve2dEx.cs @@ -121,7 +121,7 @@ public static Arc ToArc(this CircularArc2d a2d) #region EllipticalArc2d - //椭圆弧 + // 椭圆弧 /// /// 将二维解析类椭圆弧转换为实体椭圆弧,然后进行矩阵变换 /// diff --git a/src/IFoxCAD.Cad/ExtensionMethod/Curve3dEx.cs b/src/IFoxCAD.Cad/ExtensionMethod/Curve3dEx.cs index 57ca8fd..f5ebdab 100644 --- a/src/IFoxCAD.Cad/ExtensionMethod/Curve3dEx.cs +++ b/src/IFoxCAD.Cad/ExtensionMethod/Curve3dEx.cs @@ -148,7 +148,7 @@ public static bool IsCircular(this Curve3d curve) /// 三维复合曲线列表 public static List GetSplitCurves(this CompositeCurve3d c3d, List pars) { - //曲线参数剔除重复的 + // 曲线参数剔除重复的 pars.Sort(); for (int i = pars.Count - 1; i > 0; i--) { @@ -159,20 +159,20 @@ public static List GetSplitCurves(this CompositeCurve3d c3d, L if (pars.Count == 0) return new List(); - //这个是曲线参数类 + // 这个是曲线参数类 var inter = c3d.GetInterval(); - //曲线们 + // 曲线们 var c3ds = c3d.GetCurves(); if (c3ds.Length == 1 && c3ds[0].IsClosed()) { - //闭合曲线不允许打断于一点 + // 闭合曲线不允许打断于一点 if (pars.Count > 1) { - //如果包含起点 + // 如果包含起点 if (Tolerance.Global.IsEqualPoint(pars[0], inter.LowerBound)) { pars[0] = inter.LowerBound; - //又包含终点,去除终点 + // 又包含终点,去除终点 if (Tolerance.Global.IsEqualPoint(pars[^1], inter.UpperBound)) { pars.RemoveAt(pars.Count - 1); @@ -184,7 +184,7 @@ public static List GetSplitCurves(this CompositeCurve3d c3d, L { pars[^1] = inter.UpperBound; } - //加入第一点以支持反向打断 + // 加入第一点以支持反向打断 pars.Add(pars[0]); } else @@ -194,7 +194,7 @@ public static List GetSplitCurves(this CompositeCurve3d c3d, L } else { - //非闭合曲线加入起点和终点 + // 非闭合曲线加入起点和终点 if (Tolerance.Global.IsEqualPoint(pars[0], inter.LowerBound)) pars[0] = inter.LowerBound; else @@ -209,7 +209,7 @@ public static List GetSplitCurves(this CompositeCurve3d c3d, L for (int i = 0; i < pars.Count - 1; i++) { List cc3ds = new(); - //复合曲线参数转换到包含曲线参数 + // 复合曲线参数转换到包含曲线参数 var cp1 = c3d.GlobalToLocalParameter(pars[i]); var cp2 = c3d.GlobalToLocalParameter(pars[i + 1]); if (cp1.SegmentIndex == cp2.SegmentIndex) @@ -404,7 +404,7 @@ public static Circle ToCircle(this CircularArc3d ca3d) => /// 实体圆弧 public static Arc ToArc(this CircularArc3d ca3d) { - //必须新建,而不能直接使用GetPlane()获取 + // 必须新建,而不能直接使用GetPlane()获取 double angle = ca3d.ReferenceVector.AngleOnPlane(new Plane(ca3d.Center, ca3d.Normal)); return new Arc(ca3d.Center, ca3d.Normal, ca3d.Radius, ca3d.StartAngle + angle, ca3d.EndAngle + angle); } @@ -462,7 +462,7 @@ public static Ellipse ToCurve(this EllipticalArc3d ea3d) ea3d.MinorRadius / ea3d.MajorRadius, 0, Math.PI * 2); - //Ge椭圆角度就是Db椭圆的参数 + // Ge椭圆角度就是Db椭圆的参数 if (!ea3d.IsClosed()) { ell.StartAngle = ell.GetAngleAtParameter(ea3d.StartAngle); diff --git a/src/IFoxCAD.Cad/ExtensionMethod/CurveEx.cs b/src/IFoxCAD.Cad/ExtensionMethod/CurveEx.cs index 4119020..be70f0a 100644 --- a/src/IFoxCAD.Cad/ExtensionMethod/CurveEx.cs +++ b/src/IFoxCAD.Cad/ExtensionMethod/CurveEx.cs @@ -20,22 +20,12 @@ public static double GetLength(this Curve curve) /// /// 曲线 /// 打断参数表 - /// 参数表排序委托 - /// - /// 默认: 按所提供的参数表进行分割打断
    - /// 否则:按委托排序后的参数表进行分割打断 - ///
    - /// /// 打断后曲线的集合 - public static IEnumerable GetSplitCurves(this Curve curve, - IEnumerable pars, - Func, IEnumerable>? func = null) + public static IEnumerable GetSplitCurves(this Curve curve, IEnumerable pars) { - if (pars == null) + if (pars is null) throw new ArgumentNullException(nameof(pars)); - if (func != null) - pars = func.Invoke(pars); return curve .GetSplitCurves(new DoubleCollection(pars.ToArray())) @@ -49,16 +39,21 @@ public static IEnumerable GetSplitCurves(this Curve curve, /// 打断参数表 /// 对参数表是否进行排序 /// - /// :按参数值升序排序;
    - /// :不排序,默认值 + /// 按参数值升序排序
    + /// 不排序,默认值 ///
    /// /// 打断后曲线的集合 public static IEnumerable GetSplitCurves(this Curve curve, IEnumerable pars, bool isOrder = false) { + if (pars is null) + throw new ArgumentNullException(nameof(pars)); + if (isOrder) + pars = pars.OrderBy(x => x); + return curve - .GetSplitCurves(new DoubleCollection(isOrder ? pars.OrderBy(x => x).ToArray() : pars.ToArray())) + .GetSplitCurves(new DoubleCollection(pars.ToArray())) .Cast(); } @@ -70,6 +65,8 @@ public static IEnumerable GetSplitCurves(this Curve curve, IEnumerable打断后曲线的集合 public static IEnumerable GetSplitCurves(this Curve curve, IEnumerable points) { + if (points is null) + throw new ArgumentNullException(nameof(points)); return curve .GetSplitCurves(new Point3dCollection(points.ToArray())) @@ -83,18 +80,18 @@ public static IEnumerable GetSplitCurves(this Curve curve, IEnumerable打断点表 /// 对点表是否进行排序 /// - /// :按参数值升序排序;
    - /// :不排序,默认值 + /// 按参数值升序排序
    + /// 不排序,默认值 ///
    /// /// 打断后曲线的集合 - public static IEnumerable GetSplitCurves(this Curve curve, - IEnumerable points, - bool isOrder = false) + public static IEnumerable GetSplitCurves(this Curve curve, IEnumerable points, bool isOrder = false) { + if (points is null) + throw new ArgumentNullException(nameof(points)); if (isOrder) points = points.OrderBy(point => curve.GetParameterAtPoint( - curve.GetClosestPointTo(point, false))); + curve.GetClosestPointTo(point, false))); return curve .GetSplitCurves(new Point3dCollection(points.ToArray())) @@ -108,17 +105,20 @@ public static IEnumerable GetSplitCurves(this Curve curve, /// 所有的闭合环的曲线集合 public static IEnumerable GetAllCycle(this IEnumerable curves) { + if (curves is null) + throw new ArgumentNullException(nameof(curves)); + // 新建图 var graph = new Graph(); foreach (var curve in curves) { #if NET35 graph.AddEdge(curve.ToCurve3d()!); -#else +#else graph.AddEdge(curve.GetGeCurve()); #endif } - //新建 dfs + // 新建 dfs var dfs = new DepthFirst(); // 查询全部的 闭合环 dfs.FindAll(graph); @@ -140,6 +140,9 @@ public static IEnumerable GetAllCycle(this IEnumerable curves) /// 打断后的曲线列表 public static List BreakCurve(this List curves) { + if (curves is null) + throw new ArgumentNullException(nameof(curves)); + var geCurves = new List(); // 存储曲线转换后的复合曲线 var paramss = new List>(); // 存储每个曲线的交点参数值 @@ -153,14 +156,14 @@ public static List BreakCurve(this List curves) } } - //var oldCurves = new List(); + // var oldCurves = new List(); var newCurves = new List(); var cci3d = new CurveCurveIntersector3d(); for (int i = 0; i < curves.Count; i++) { var gc1 = geCurves[i]; - var pars1 = paramss[i]; //引用 + var pars1 = paramss[i]; // 引用 for (int j = i; j < curves.Count; j++) { var gc2 = geCurves[j]; @@ -190,7 +193,7 @@ public static List BreakCurve(this List curves) newCurves.Add(c3dCur); } } - //oldCurves.Add(curves[i]); + // oldCurves.Add(curves[i]); } } } @@ -198,7 +201,7 @@ public static List BreakCurve(this List curves) return newCurves; } - //转换DBCurve为GeCurved + // 转换DBCurve为GeCurved #region Curve @@ -464,17 +467,17 @@ public static NurbCurve3d ToCurve3d(this Spline spl) { case Poly2dType.SimplePoly: case Poly2dType.FitCurvePoly: - Polyline pl = new(); - pl.SetDatabaseDefaults(); - pl.ConvertFrom(pl2d, false); - return ToCurve3d(pl); + Polyline pl = new(); + pl.SetDatabaseDefaults(); + pl.ConvertFrom(pl2d, false); + return ToCurve3d(pl); default: - return ToNurbCurve3d(pl2d); + return ToNurbCurve3d(pl2d); } - //Polyline pl = new Polyline(); - //pl.ConvertFrom(pl2d, false); - //return ToCurve3d(pl); + // Polyline pl = new Polyline(); + // pl.ConvertFrom(pl2d, false); + // return ToCurve3d(pl); } /// @@ -488,13 +491,13 @@ public static NurbCurve3d ToCurve3d(this Spline spl) { case Poly2dType.SimplePoly: case Poly2dType.FitCurvePoly: - Polyline pl = new(); - pl.SetDatabaseDefaults(); - pl.ConvertFrom(pl2d, false); - return ToNurbCurve3d(pl); + Polyline pl = new(); + pl.SetDatabaseDefaults(); + pl.ConvertFrom(pl2d, false); + return ToNurbCurve3d(pl); default: - return ToCurve3d(pl2d.Spline); + return ToCurve3d(pl2d.Spline); } } @@ -575,15 +578,15 @@ public static CompositeCurve3d ToCurve3d(this Polyline pl) switch (pl.GetSegmentType(i)) { case SegmentType.Line: - c3ds.Add(pl.GetLineSegmentAt(i)); - break; + c3ds.Add(pl.GetLineSegmentAt(i)); + break; case SegmentType.Arc: - c3ds.Add(pl.GetArcSegmentAt(i)); - break; + c3ds.Add(pl.GetArcSegmentAt(i)); + break; default: - break; + break; } } return new CompositeCurve3d(c3ds.ToArray()); @@ -603,15 +606,15 @@ public static CompositeCurve3d ToCurve3d(this Polyline pl) switch (pl.GetSegmentType(i)) { case SegmentType.Line: - nc3dtemp = new NurbCurve3d(pl.GetLineSegmentAt(i)); - break; + nc3dtemp = new NurbCurve3d(pl.GetLineSegmentAt(i)); + break; case SegmentType.Arc: - nc3dtemp = pl.GetArcSegmentAt(i).ToNurbCurve3d(); - break; + nc3dtemp = pl.GetArcSegmentAt(i).ToNurbCurve3d(); + break; default: - break; + break; } if (nc3d is null) { @@ -641,7 +644,7 @@ public static void ChamferAt(this Polyline polyline, int index, double radius, b SegmentType.Line != polyline.GetSegmentType(index)) throw new System.Exception("非直线段不能倒角"); - //获取当前索引号的前后两段直线,并组合为Ge复合曲线 + // 获取当前索引号的前后两段直线,并组合为Ge复合曲线 Curve3d[] c3ds = new Curve3d[] { @@ -650,11 +653,11 @@ public static void ChamferAt(this Polyline polyline, int index, double radius, b }; CompositeCurve3d cc3d = new(c3ds); - //试倒直角 - //子曲线的个数有三种情况: - //1、=3时倒角方向正确 - //2、=2时倒角方向相反 - //3、=0或为直线时失败 + // 试倒直角 + // 子曲线的个数有三种情况: + // 1、=3时倒角方向正确 + // 2、=2时倒角方向相反 + // 3、=0或为直线时失败 c3ds = cc3d.GetTrimmedOffset ( @@ -688,7 +691,7 @@ public static void ChamferAt(this Polyline polyline, int index, double radius, b throw new System.Exception("倒角半径过大"); } - //GetTrimmedOffset会生成倒角+偏移,故先反方向倒角,再倒回 + // GetTrimmedOffset会生成倒角+偏移,故先反方向倒角,再倒回 c3ds = cc3d.GetTrimmedOffset ( -radius, @@ -705,9 +708,9 @@ public static void ChamferAt(this Polyline polyline, int index, double radius, b type ); - //将结果Ge曲线转为Db曲线,并将相关的数值反映到原曲线 + // 将结果Ge曲线转为Db曲线,并将相关的数值反映到原曲线 var plTemp = c3ds[0].ToCurve() as Polyline; - if (plTemp == null) + if (plTemp is null) return; polyline.RemoveVertexAt(index); polyline.AddVertexAt(index, plTemp.GetPoint2dAt(1), plTemp.GetBulgeAt(1), 0, 0); @@ -716,5 +719,5 @@ public static void ChamferAt(this Polyline polyline, int index, double radius, b #endregion Polyline - #endregion Curve -} + #endregion +} \ No newline at end of file diff --git a/src/IFoxCAD.Cad/ExtensionMethod/DBDictionaryEx.cs b/src/IFoxCAD.Cad/ExtensionMethod/DBDictionaryEx.cs index 89dc2d5..719d7df 100644 --- a/src/IFoxCAD.Cad/ExtensionMethod/DBDictionaryEx.cs +++ b/src/IFoxCAD.Cad/ExtensionMethod/DBDictionaryEx.cs @@ -258,10 +258,10 @@ public static void SetValue(this DataCell cell, CellType type, object value) ///// 是否创建子字典 ///// 键值列表 ///// 字典 - //public static DBDictionary GetSubDictionary(this DBObject obj, bool createSubDictionary, params string[] dictNames) - //{ + // public static DBDictionary GetSubDictionary(this DBObject obj, bool createSubDictionary, params string[] dictNames) + // { // return obj.GetXDictionary().GetSubDictionary(createSubDictionary, dictNames); - //} + // } #endregion diff --git a/src/IFoxCAD.Cad/ExtensionMethod/DBObjectEx.cs b/src/IFoxCAD.Cad/ExtensionMethod/DBObjectEx.cs index 8ebee46..a8590f5 100644 --- a/src/IFoxCAD.Cad/ExtensionMethod/DBObjectEx.cs +++ b/src/IFoxCAD.Cad/ExtensionMethod/DBObjectEx.cs @@ -1,4 +1,6 @@ -namespace IFoxCAD.Cad; +using System.Linq; + +namespace IFoxCAD.Cad; /// /// 实体对象扩展类 @@ -6,6 +8,53 @@ public static class DBObjectEx { #region Xdata扩展 + + /// + /// 获取appName的索引区间 + /// + /// xdata + /// 注册名称 + /// 任务组码对象 + /// 返回任务组码的索引 + static List GetXdataAppIndex(XDataList data, string appName, DxfCode[] dxfCodes) + { + List acIndex = new(); + int appNameIndex = -1; + // int appNameIndexNext = -1; + + // 先找到属于它的名字索引,然后再找到下一个不属于它名字的索引,移除中间部分 + for (int i = 0; i < data.Count; i++) + { + if (data[i].TypeCode == (short)DxfCode.ExtendedDataRegAppName) + { + if (data[i].Value.ToString() == appName) + { + appNameIndex = i; + continue; + } + if (appNameIndex != -1) + { + // 找到了后面的appName + // appNameIndexNext = i; + break; + } + } + if (appNameIndex != -1 && // 找next的时候,获取任务(移除)的对象 + dxfCodes.Contains((DxfCode)data[i].TypeCode)) + acIndex.Add(i); + } + + // 当前app索引 + // if (appNameIndex == -1) + // return; + + // 下一个app索引,如果是空,就为末尾 + // if (appNameIndexNext == -1) + // appNameIndexNext = data.Count; + + return acIndex; + } + /// /// 删除扩展数据 /// @@ -17,21 +66,16 @@ public static void RemoveXData(this DBObject obj, string appName, DxfCode dxfCod if (obj.XData == null) return; XDataList data = obj.XData; - bool appNameIdentical = false; - for (int i = 0; i < data.Count; i++) - { - if (data[i].TypeCode == (int)DxfCode.ExtendedDataRegAppName) - { - appNameIdentical = data[i].Value.ToString() == appName; - break; - } - } - if (!appNameIdentical) + + // 测试命令 addxdata removexdata + // 移除指定App的扩展 + var indexs = GetXdataAppIndex(data, appName, new DxfCode[] { dxfCode }); + if (indexs.Count == 0) return; - for (int i = data.Count - 1; i >= 0; i--) - if (data[i].TypeCode == (int)dxfCode) - data.RemoveAt(i); + for (int i = indexs.Count - 1; i >= 0; i--) + data.RemoveAt(indexs[i]); + using (obj.ForWrite()) obj.XData = data; } @@ -47,21 +91,14 @@ public static void ChangeXData(this DBObject obj, string appName, DxfCode dxfCod if (obj.XData == null) return; XDataList data = obj.XData; - bool appNameIdentical = false; - for (int i = 0; i < data.Count; i++) - { - if (data[i].TypeCode == (int)DxfCode.ExtendedDataRegAppName) - { - appNameIdentical = data[i].Value.ToString() == appName; - break; - } - } - if (!appNameIdentical) + + var indexs = GetXdataAppIndex(data, appName, new DxfCode[] { dxfCode }); + if (indexs.Count == 0) return; - for (int i = data.Count - 1; i >= 0; i--) - if (data[i].TypeCode == (int)dxfCode) - data[i] = new TypedValue((int)dxfCode, newvalue); + for (int i = indexs.Count - 1; i >= 0; i--) + data[i] = new TypedValue((short)dxfCode, newvalue); + using (obj.ForWrite()) obj.XData = data; } diff --git a/src/IFoxCAD.Cad/ExtensionMethod/DBTransEx.cs b/src/IFoxCAD.Cad/ExtensionMethod/DBTransEx.cs new file mode 100644 index 0000000..d238252 --- /dev/null +++ b/src/IFoxCAD.Cad/ExtensionMethod/DBTransEx.cs @@ -0,0 +1,92 @@ +#define lack_test + +namespace IFoxCAD.Cad; + +#if lack_test +public static class DBTransEx +{ + + /* + * 0x01 + * db.Purge(ids)是获取未硬引用(无引用?)的对象,也就可以删除的. + * 0x02 + * 如果一个图元引用一个图层, + * 假设这个图元是可以删除的(实际上它可能来自于词典记录的id) => 那么它被 db.Purge(ids) 识别, + * 但是这个图层因为有硬引用,所以不被 db.Purge(ids) 识别, + * 只能删除图元之后,循环第二次再通过 db.Purge(ids) 获取图层id. + * 0x03 + * 因为删除之后,符号表内的引用可能被修改,因此需要重复遍历符号表. + * 0x04 + * 试试循环事务 + * 0x05 + * 无法过滤外部参照图层,使得全部图层打开了 + */ + + /// + /// 清理符号表 + /// + /// + /// + /// 排除外部参照:默认true,为false时候会令图层全部显示再清理,包括冻结 + public static void Purge(this DBTrans tr, SymModes sym = SymModes.All, bool excludeXref = true) + { + var ids = new ObjectIdCollection(); + var db = tr.Database; + + if ((sym & SymModes.BlockTable) == SymModes.BlockTable) + { + if (!excludeXref) + GetAllIds(tr, tr.BlockTable, ids, excludeXref); + else + tr.BlockTable.ForEach(tabRec => { + if (!tabRec.IsFromExternalReference) + ids.Add(tabRec.Id); + }); + } + if ((sym & SymModes.DimStyleTable) == SymModes.DimStyleTable) + GetAllIds(tr, tr.DimStyleTable, ids, excludeXref); + if ((sym & SymModes.LayerTable) == SymModes.LayerTable) + GetAllIds(tr, tr.LayerTable, ids, excludeXref); + if ((sym & SymModes.LinetypeTable) == SymModes.LinetypeTable) + GetAllIds(tr, tr.LinetypeTable, ids, excludeXref); + if ((sym & SymModes.TextStyleTable) == SymModes.TextStyleTable) + GetAllIds(tr, tr.TextStyleTable, ids, excludeXref); + if ((sym & SymModes.ViewportTable) == SymModes.ViewportTable) + GetAllIds(tr, tr.ViewportTable, ids, excludeXref); + if ((sym & SymModes.RegAppTable) == SymModes.RegAppTable) + GetAllIds(tr, tr.RegAppTable, ids, excludeXref); + + // SHUN007 说这两个表可能有错误 + if ((sym & SymModes.ViewTable) == SymModes.ViewTable) + GetAllIds(tr, tr.ViewTable, ids, excludeXref); + if ((sym & SymModes.UcsTable) == SymModes.UcsTable) + GetAllIds(tr, tr.UcsTable, ids, excludeXref); + + // Purge是查询能够清理的对象 + db.Purge(ids); + foreach (ObjectId id in ids) + id.Erase(); + } + + static void GetAllIds(DBTrans tr, + SymbolTable symbolTable, + ObjectIdCollection ids, + bool excludeXref = true) + where TTable : SymbolTable + where TRecord : SymbolTableRecord, new() + { + if (!excludeXref) + symbolTable.ForEach(id => ids.Add(id)); + else + { + symbolTable.ForEach(id => { + var tabRec = tr.GetObject(id); + if (tabRec == null) + return; + if (!tabRec.Name.Contains("|")) + ids.Add(tabRec.Id); + }); + } + } +} +#endif diff --git a/src/IFoxCAD.Cad/ExtensionMethod/DatabaseEx.cs b/src/IFoxCAD.Cad/ExtensionMethod/DatabaseEx.cs deleted file mode 100644 index dff7a9e..0000000 --- a/src/IFoxCAD.Cad/ExtensionMethod/DatabaseEx.cs +++ /dev/null @@ -1,23 +0,0 @@ -namespace IFoxCAD.Cad; - -public static class DatabaseEx -{ - /// - /// 后台开图文字偏移处理 - /// 0x01 此方案利用前台数据库进行处理 - /// 0x02 当关闭所有前台文档时,会出现无时,不能使用(惊惊没有测试过此状态) - /// 0x03 当关闭所有前台文档时,如何发送命令呢?那就是利用跨进程通讯 - /// - /// 后台打开的数据库 - /// 处理后台的任务 - public static void DBTextDeviation(this Database backstageOpenDwg, Action action) - { - var wdb = HostApplicationServices.WorkingDatabase; - if (wdb != null) - { - HostApplicationServices.WorkingDatabase = backstageOpenDwg; - action?.Invoke(); - HostApplicationServices.WorkingDatabase = wdb; - } - } -} \ No newline at end of file diff --git a/src/IFoxCAD.Cad/ExtensionMethod/EditorEx.cs b/src/IFoxCAD.Cad/ExtensionMethod/EditorEx.cs index 9528cec..eaca60b 100644 --- a/src/IFoxCAD.Cad/ExtensionMethod/EditorEx.cs +++ b/src/IFoxCAD.Cad/ExtensionMethod/EditorEx.cs @@ -70,7 +70,6 @@ public static SelectionSet SelectByLineWeight(this Editor editor, LineWeight lin pso.RejectPaperspaceViewport = mode.Contains(":V"); pso.AllowSubSelections = mode.Contains("-A"); pso.ForceSubSelections = mode.Contains("-F"); - } if (messages is not null) { @@ -89,7 +88,6 @@ public static SelectionSet SelectByLineWeight(this Editor editor, LineWeight lin if (keywords.ContainsKey(e.Input)) keywords[e.Input].Invoke(); }; - } try { @@ -99,7 +97,7 @@ public static SelectionSet SelectByLineWeight(this Editor editor, LineWeight lin ss = editor.GetSelection(pso); } catch (Exception e) - { + { editor.WriteMessage($"\nKey is {e.Message}"); } return ss; @@ -107,13 +105,13 @@ public static SelectionSet SelectByLineWeight(this Editor editor, LineWeight lin /* - * //定义选择集选项 + * // 定义选择集选项 * var pso = new PromptSelectionOptions * { - * AllowDuplicates = false, //重复选择 + * AllowDuplicates = false, // 重复选择 * }; * - * //getai遍历全图选择块有用到 + * // getai遍历全图选择块有用到 * var dic = new Dictionary() { * { "Z,全部同名", ()=> { * getai = BlockHelper.EnumAttIdentical.AllBlockName; @@ -125,14 +123,14 @@ public static SelectionSet SelectByLineWeight(this Editor editor, LineWeight lin * { "V,属性值-默认", ()=> { * getai = BlockHelper.EnumAttIdentical.DisplayAndTagText; * }}, - * //允许以下操作,相同的会加入前面的 - * //{ "V,属性值-默认|X,啊啊啊啊", ()=> { + * // 允许以下操作,相同的会加入前面的 + * // { "V,属性值-默认|X,啊啊啊啊", ()=> { * - * //}}, + * // }}, * }; * pso.SsgetAddKeys(dic); * - * //创建选择集过滤器,只选择块对象 + * // 创建选择集过滤器,只选择块对象 * var filList = new TypedValue[] { new TypedValue((int)DxfCode.Start, "INSERT") }; * var filter = new SelectionFilter(filList); * ssPsr = ed.GetSelection(pso, filter); @@ -176,7 +174,7 @@ public static void SsgetAddKeys(this PromptSelectionOptions pso, foreach (var item in tmp) dicActions.Add(item.Key, item.Value); - //去除关键字重复的,把重复的执行动作移动到前面 + // 去除关键字重复的,把重复的执行动作移动到前面 for (int i = 0; i < dicActions.Count; i++) { var pair1 = dicActions.ElementAt(i); @@ -204,18 +202,18 @@ public static void SsgetAddKeys(this PromptSelectionOptions pso, keySplitS[i + 1] + "(" + keySplitS[i] + ")"); } - //回调的时候我想用Dict的O(1)索引, - //但是此函数内进行new Dictionary() 在函数栈释放的时候,它被释放掉了. - //因此 dicActions 参数的生命周期 + // 回调的时候我想用Dict的O(1)索引, + // 但是此函数内进行new Dictionary() 在函数栈释放的时候,它被释放掉了. + // 因此 dicActions 参数的生命周期 tmp = new(dicActions); dicActions.Clear(); foreach (var item in tmp) dicActions.Add(item.Key.Split(',')[0], item.Value); var keyWords = pso.Keywords; - //从选择集命令中显示关键字 + // 从选择集命令中显示关键字 pso.MessageForAdding = keyWords.GetDisplayString(true); - //关键字回调事件 ssget关键字 + // 关键字回调事件 ssget关键字 pso.KeywordInput += (sender, e) => { dicActions[e.Input].Invoke(); }; @@ -226,111 +224,106 @@ public static void SsgetAddKeys(this PromptSelectionOptions pso, - //#region 即时选择样板 - - ///// - ///// 即时选择,框选更新关键字 - ///// - //public static void SelectTest() - //{ - // Env.Editor.WriteMessage("\n[白嫖工具]--测试"); - // //激活选中事件 - // Env.Editor.SelectionAdded += SelectTest_SelectionAdded; - // //初始化坐标系 - // Env.Editor.CurrentUserCoordinateSystem = Matrix3d.Identity; - - // //创建过滤器 - // var sf = new OpEqual(0, "arc"); - // var pso = new PromptSelectionOptions - // { - // MessageForAdding = "\n请选择对象:" - // }; - - // pso.Keywords.Add("Z"); - // pso.Keywords.Add("X"); - // pso.Keywords.Add("Q"); - // //注册关键字 - // pso.KeywordInput += SelectTest_KeywordInput; - // try - // { - // //用户选择 - // var psr = Env.Editor.GetSelection(pso, sf); - // //处理代码 - - - // } - // catch (Exception ex)//捕获关键字 - // { - // if (ex.Message == "XuError") - // { - // //关闭关键字事件 - // pso.KeywordInput -= SelectTest_KeywordInput; - // //关闭选中事件 - // Env.Editor.SelectionAdded -= SelectTest_SelectionAdded; - // //重新调用自身 - // ZengLiangYuanJiao(); - // } - // } - // //关闭关键字事件 - // pso.KeywordInput -= SelectTest_KeywordInput; - // //关闭选中事件 - // Env.Editor.SelectionAdded -= SelectTest_SelectionAdded; - //} - - ///// - ///// 即时选择 - ///// - ///// - ///// - //private static void SelectTest_SelectionAdded(object sender, SelectionAddedEventArgs e) - //{ - // //关闭选中事件 - // Env.Editor.SelectionAdded -= SelectTest_SelectionAdded; - // using (var tr = new DBTrans()) - // { - // //处理代码 - // for (int i = 0; i < e.AddedObjects.Count; i++) - // { - - - // //处理完移除已处理的对象 - // e.Remove(i); - // } - // } - // //激活选中事件 - // Env.Editor.SelectionAdded += SelectTest_SelectionAdded; - //} - - ///// - ///// 关键字响应 - ///// - ///// - ///// - //private static void SelectTest_KeywordInput(object sender, SelectionTextInputEventArgs e) - //{ - // //获取关键字 - // switch (e.Input) - // { - // case "Z": - // { - // break; - // } - // case "X": - // { - // break; - // } - - // case "Q": - // { - // break; - // } - // } - // //抛出异常,用于更新提示信息 - // throw new ArgumentException("XuError"); - //} - - - //#endregion + // #region 即时选择样板 + // /// + // /// 即时选择,框选更新关键字 + // /// + // public static void SelectTest() + // { + // Env.Editor.WriteMessage("\n[白嫖工具]--测试"); + // // 激活选中事件 + // Env.Editor.SelectionAdded += SelectTest_SelectionAdded; + // // 初始化坐标系 + // Env.Editor.CurrentUserCoordinateSystem = Matrix3d.Identity; + + // // 创建过滤器 + // var sf = new OpEqual(0, "arc"); + // var pso = new PromptSelectionOptions + // { + // MessageForAdding = "\n请选择对象:" + // }; + + // pso.Keywords.Add("Z"); + // pso.Keywords.Add("X"); + // pso.Keywords.Add("Q"); + // // 注册关键字 + // pso.KeywordInput += SelectTest_KeywordInput; + // try + // { + // // 用户选择 + // var psr = Env.Editor.GetSelection(pso, sf); + // // 处理代码 + + + // } + // catch (Exception ex)// 捕获关键字 + // { + // if (ex.Message == "XuError") + // { + // // 关闭关键字事件 + // pso.KeywordInput -= SelectTest_KeywordInput; + // // 关闭选中事件 + // Env.Editor.SelectionAdded -= SelectTest_SelectionAdded; + // // 重新调用自身 + // ZengLiangYuanJiao(); + // } + // } + // // 关闭关键字事件 + // pso.KeywordInput -= SelectTest_KeywordInput; + // // 关闭选中事件 + // Env.Editor.SelectionAdded -= SelectTest_SelectionAdded; + // } + + // /// + // /// 即时选择 + // /// + // /// + // /// + // private static void SelectTest_SelectionAdded(object sender, SelectionAddedEventArgs e) + // { + // // 关闭选中事件 + // Env.Editor.SelectionAdded -= SelectTest_SelectionAdded; + // using (var tr = new DBTrans()) + // { + // // 处理代码 + // for (int i = 0; i < e.AddedObjects.Count; i++) + // { + // // 处理完移除已处理的对象 + // e.Remove(i); + // } + // } + // // 激活选中事件 + // Env.Editor.SelectionAdded += SelectTest_SelectionAdded; + // } + + // /// + // /// 关键字响应 + // /// + // /// + // /// + // private static void SelectTest_KeywordInput(object sender, SelectionTextInputEventArgs e) + // { + // // 获取关键字 + // switch (e.Input) + // { + // case "Z": + // { + // break; + // } + // case "X": + // { + // break; + // } + + // case "Q": + // { + // break; + // } + // } + // // 抛出异常,用于更新提示信息 + // throw new ArgumentException("XuError"); + // } + // #endregion #endregion #region Info @@ -496,7 +489,6 @@ public static bool Acceptable() /// public static List GetLines(IEnumerable pnts, bool isClosed) { - var itor = pnts.GetEnumerator(); if (!itor.MoveNext()) return new List(); @@ -715,55 +707,55 @@ public static Matrix3d GetMatrix(this Editor editor, CoordinateSystemCode from, switch (from) { case CoordinateSystemCode.Wcs: - switch (to) - { - case CoordinateSystemCode.Ucs: - return editor.GetMatrixFromWcsToUcs(); + switch (to) + { + case CoordinateSystemCode.Ucs: + return editor.GetMatrixFromWcsToUcs(); - case CoordinateSystemCode.MDcs: - return editor.GetMatrixFromMDcsToWcs(); + case CoordinateSystemCode.MDcs: + return editor.GetMatrixFromMDcsToWcs(); - case CoordinateSystemCode.PDcs: - throw new Exception("To be used only with DCS...ErrorStatus.InvalidInput"); - } - break; + case CoordinateSystemCode.PDcs: + throw new Exception("To be used only with DCS...ErrorStatus.InvalidInput"); + } + break; case CoordinateSystemCode.Ucs: - switch (to) - { - case CoordinateSystemCode.Wcs: - return editor.GetMatrixFromUcsToWcs(); + switch (to) + { + case CoordinateSystemCode.Wcs: + return editor.GetMatrixFromUcsToWcs(); - case CoordinateSystemCode.MDcs: - return editor.GetMatrixFromUcsToWcs() * editor.GetMatrixFromWcsToMDcs(); + case CoordinateSystemCode.MDcs: + return editor.GetMatrixFromUcsToWcs() * editor.GetMatrixFromWcsToMDcs(); - case CoordinateSystemCode.PDcs: - throw new Exception("To be used only with DCS... ErrorStatus.InvalidInput"); - } - break; + case CoordinateSystemCode.PDcs: + throw new Exception("To be used only with DCS... ErrorStatus.InvalidInput"); + } + break; case CoordinateSystemCode.MDcs: - switch (to) - { - case CoordinateSystemCode.Wcs: - return editor.GetMatrixFromMDcsToWcs(); + switch (to) + { + case CoordinateSystemCode.Wcs: + return editor.GetMatrixFromMDcsToWcs(); - case CoordinateSystemCode.Ucs: - return editor.GetMatrixFromMDcsToWcs() * editor.GetMatrixFromWcsToUcs(); + case CoordinateSystemCode.Ucs: + return editor.GetMatrixFromMDcsToWcs() * editor.GetMatrixFromWcsToUcs(); - case CoordinateSystemCode.PDcs: - return editor.GetMatrixFromMDcsToPDcs(); - } - break; + case CoordinateSystemCode.PDcs: + return editor.GetMatrixFromMDcsToPDcs(); + } + break; case CoordinateSystemCode.PDcs: - switch (to) - { - case CoordinateSystemCode.Wcs: - throw new Exception("To be used only with DCS... ErrorStatus.InvalidInput"); - case CoordinateSystemCode.Ucs: - throw new Exception("To be used only with DCS... ErrorStatus.InvalidInput"); - case CoordinateSystemCode.MDcs: - return editor.GetMatrixFromPDcsToMDcs(); - } - break; + switch (to) + { + case CoordinateSystemCode.Wcs: + throw new Exception("To be used only with DCS... ErrorStatus.InvalidInput"); + case CoordinateSystemCode.Ucs: + throw new Exception("To be used only with DCS... ErrorStatus.InvalidInput"); + case CoordinateSystemCode.MDcs: + return editor.GetMatrixFromPDcsToMDcs(); + } + break; } return Matrix3d.Identity; #else @@ -857,11 +849,11 @@ public static void Zoom(this Editor ed, Point3d CenPt, double width, double heig view.Width = width; view.Height = height; view.CenterPoint = new Point2d(CenPt.X, CenPt.Y); - ed.SetCurrentView(view);//更新当前视图 + ed.SetCurrentView(view);// 更新当前视图 } /// - ///缩放窗口范围 + /// 缩放窗口范围 /// /// 命令行对象 /// 第一点 @@ -887,7 +879,7 @@ public static void ZoomWindow(this Editor ed, Point3d lpt, Point3d rpt, double o /// public static Extents3d? GetValidExtents3d(this Database db, double extention = 1e-6) { - db.UpdateExt(true);//更新当前模型空间的范围 + db.UpdateExt(true);// 更新当前模型空间的范围 var ve = new Vector3d(extention, extention, extention); // 数据库没有图元的时候,min是大,max是小,导致新建出错 // 数据如下: @@ -909,7 +901,7 @@ public static void ZoomWindow(this Editor ed, Point3d lpt, Point3d rpt, double o public static void ZoomExtents(this Editor ed, double offsetDist = 0.00) { Database db = ed.Document.Database; - // db.UpdateExt(true); //GetValidExtents3d内提供了 + // db.UpdateExt(true); // GetValidExtents3d内提供了 var dbExtent = db.GetValidExtents3d(); if (dbExtent == null) ed.ZoomWindow(Point3d.Origin, new Point3d(1, 1, 0), offsetDist); @@ -1017,9 +1009,10 @@ public static PromptResult GetString(this Editor ed, string Message, string Defa [DllImport("accore.dll", CharSet = CharSet.Unicode, CallingConvention = CallingConvention.Cdecl, EntryPoint = "?acedEvaluateLisp@@YAHPEB_WAEAPEAUresbuf@@@Z")] #endif - [System.Security.SuppressUnmanagedCodeSecurity]//初始化默认值 + [System.Security.SuppressUnmanagedCodeSecurity]// 初始化默认值 static extern int AcedEvaluateLisp(string lispLine, out IntPtr result); +#pragma warning disable CA2101 // 指定对 P/Invoke 字符串参数进行封送处理 #if NET35 [DllImport("acad.exe", CharSet = CharSet.Auto, CallingConvention = CallingConvention.Cdecl, EntryPoint = "ads_queueexpr")] @@ -1027,6 +1020,7 @@ public static PromptResult GetString(this Editor ed, string Message, string Defa [DllImport("accore.dll", CharSet = CharSet.Auto, CallingConvention = CallingConvention.Cdecl, EntryPoint = "ads_queueexpr")] #endif +#pragma warning restore CA2101 // 指定对 P/Invoke 字符串参数进行封送处理 static extern int Ads_queueexpr(string strExpr); public enum RunLispFlag : byte @@ -1058,7 +1052,7 @@ public enum RunLispFlag : byte * } * 调用方式: * (command "CmdTest_RunLisp1") - * bug说明: + * bug说明: * AcedEvaluateLisp接口在高版本调用时候没有运行成功,使得 !abc 没有值 * 经过测试,cad08调用成功,此bug与CommandFlags无关 * 解决方案: diff --git a/src/IFoxCAD.Cad/ExtensionMethod/EntityBoundingInfo.cs b/src/IFoxCAD.Cad/ExtensionMethod/EntityBoundingInfo.cs new file mode 100644 index 0000000..43010d6 --- /dev/null +++ b/src/IFoxCAD.Cad/ExtensionMethod/EntityBoundingInfo.cs @@ -0,0 +1,264 @@ +#define Debug_Cause_Error +namespace IFoxCAD.Cad; + +/// +/// AABB和OBB信息 +/// +public struct BoundingInfo +{ + public double MinX; + public double MinY; + public double MinZ; + + public double MaxX; + public double MaxY; + public double MaxZ; + + /// + /// AABB这里永远是0 + /// + public double Angle; + public bool IsEffective; + + public Point3d Min => new(MinX, MinY, MinZ); + public Point3d Max => new(MaxX, MaxY, MaxZ); + public Extents3d Extents3d => new(Min, Max); + public Extents2d Extents2d => new(MinX, MinY, MaxX, MaxY); + + public BoundingInfo(double minX, double minY, double minZ, + double maxX, double maxY, double maxZ, + bool isEffective, double angle = 0) + { + MinX = minX; + MinY = minY; + MinZ = minZ; + MaxX = maxX; + MaxY = maxY; + MaxZ = maxZ; + IsEffective = isEffective; + Angle = angle; + } + + public BoundingInfo(Point3d min, Point3d max, bool isEffective, double angle = 0) + : this(min.X, min.Y, min.Z, + max.X, max.Y, max.Z, + isEffective, angle) + { } + + // public BoundingInfo(Rect rect, double angle = 0) + // { + // MinX = rect.X; + // MinY = rect.Y; + // MinZ = 0; + // MaxX = rect.Right; + // MaxY = rect.Top; + // MaxZ = 0; + // Angle = angle; + // } +} + +public class EntityBoundingInfo +{ + #region 保存异常类型的日志 + /// + /// 包围盒错误文件路径 + /// + public static string BoxLogAddress + { + get + { + _BoxLogAddress ??= LogHelper.GetDefaultOption(nameof(GetBoundingInfo) + ".config"); + return _BoxLogAddress; + } + set { _BoxLogAddress = value; } + } + static string? _BoxLogAddress; + static readonly HashSet _typeNames; + /// + /// 为了保证更好的性能, + /// 只是在第一次调用此功能的时候进行读取, + /// 免得高频调用时候高频触发磁盘 + /// + static EntityBoundingInfo() + { + _typeNames = new(); + if (!File.Exists(BoxLogAddress)) + return; + ExceptionToHashSet(); + } + + /// + /// 读取日志的异常到容器 + /// + static void ExceptionToHashSet() + { + var old_LogAddress = LogHelper.LogAddress; + try + { + LogHelper.OptionFile(BoxLogAddress); + var logTxts = new FileLogger().ReadLog(); + for (int i = 0; i < logTxts.Length; i++) + { + var line = logTxts[i]; + if (line.Contains(nameof(LogTxt.备注信息))) + { + int index = line.IndexOf(":"); + index = line.IndexOf("\"", index) + 1;// 1是"\"" + int index2 = line.IndexOf("\"", index); + var msg = line.Substring(index, index2 - index); + _typeNames.Add(msg); + } + } + } + finally + { + LogHelper.LogAddress = old_LogAddress; + } + } + + /// + /// 写入容器类型到异常日志 + /// + /// + /// + static void ExceptionToLog(Exception e, Entity ent) + { + // 无法处理的错误类型将被记录 + // 如果错误无法try,而是cad直接致命错误,那么此处也不会被写入, + // 这种情况无法避免程序安全性,总不能写了日志再去删除日志词条,这样会造成频繁IO的 + // 遇到一个不认识的类型再去写入?然后记录它是否可以写入? + var old_LogAddress = LogHelper.LogAddress; + var old_FlagOutFile = LogHelper.FlagOutFile; + try + { + LogHelper.FlagOutFile = true; + LogHelper.OptionFile(BoxLogAddress); + e.WriteLog(ent.GetType().Name, LogTarget.FileNotException); + } + finally + { + LogHelper.LogAddress = old_LogAddress; + LogHelper.FlagOutFile = old_FlagOutFile; + } + } + #endregion + + /// + /// 获取图元包围盒 + /// + /// + /// (左下角,右上角,是否有效) + /// 异常: + /// 会将包围盒类型记录到所属路径中,以此查询 + public static BoundingInfo GetBoundingInfo(Entity ent) + { +#if Debug_Cause_Error + // 错误类型处理 + if (ent is AttributeDefinition) // 属性定义 + return new(Point3d.Origin, Point3d.Origin, false); + else if (ent is Xline xline)// 参照线 + return new(xline.BasePoint, xline.BasePoint, true); + else if (ent is Ray ray)// 射线 + return new(ray.BasePoint, ray.BasePoint, true); +#endif + // 指定类型处理 + if (ent is BlockReference brf) + return GetBoxInfoInBlockReference(brf); + else if (ent is MText mText) + return GetBoxInfoInMText(mText); + else if (!_typeNames.Contains(ent.GetType().Name)) // 屏蔽天正等等缺失包围盒的类型 + try + { + return new(ent.GeometricExtents.MinPoint, ent.GeometricExtents.MaxPoint, true); + } + catch (Exception e) { ExceptionToLog(e, ent); } + return new(Point3d.Origin, Point3d.Origin, false); + } + + /// + /// 处理块参照 + /// + static BoundingInfo GetBoxInfoInBlockReference(BlockReference brf) + { + try + { + // 这个获取是原点附近,需要平移到块基点 + var fit = brf.GeometryExtentsBestFit(); + var minX = fit.MinPoint.X + brf.Position.X; + var minY = fit.MinPoint.Y + brf.Position.Y; + var minZ = fit.MinPoint.Z + brf.Position.Z; + var maxX = fit.MaxPoint.X + brf.Position.X; + var maxY = fit.MaxPoint.Y + brf.Position.Y; + var maxZ = fit.MaxPoint.Z + brf.Position.Z; + return new(minX, minY, minZ, maxX, maxY, maxZ, true); + } + catch + { + // 如果是一条参照线的组块,将导致获取包围盒时报错 + // 0x01 是否需要进入块内,然后拿到每个图元的BasePoint再计算中点?感觉过于复杂. + // 0x02 这个时候拿基点走就算了 + return new(brf.Position, brf.Position, true); + } + } + + /// + /// 处理多行文字 + /// + static BoundingInfo GetBoxInfoInMText(MText mtxt) + { + /* + * MText Aussehen + * ------------------------------------ + * | | | + * | | |ht + * | | | + * |-----wl-------插入点------wr-------| + * | | | + * | | |hb + * | | | + * ------------------------------------ + */ + + double width = mtxt.ActualWidth;// 实际宽度 + double height = mtxt.ActualHeight;// 实际高度 + double wl = 0.0; + double hb = 0.0; + + // 对齐方式 + switch (mtxt.Attachment) + { + case AttachmentPoint.TopCenter: + case AttachmentPoint.MiddleCenter: + case AttachmentPoint.BottomCenter: + wl = width * -0.5; + break; + case AttachmentPoint.TopRight: + case AttachmentPoint.MiddleRight: + case AttachmentPoint.BottomRight: + wl = -width; + break; + } + switch (mtxt.Attachment) + { + case AttachmentPoint.TopLeft: + case AttachmentPoint.TopCenter: + case AttachmentPoint.TopRight: + hb = -height;// 下边线到插入点的距离 + break; + case AttachmentPoint.MiddleLeft: + case AttachmentPoint.MiddleCenter: + case AttachmentPoint.MiddleRight: + hb = height * -0.5; + break; + } + + double wr = width + wl; + double ht = height + hb; + + Point3d center = mtxt.Location; + return new(center.X + wl, center.Y + hb, 0, + center.X + wr, center.Y + ht, 0, + true, + mtxt.Rotation); + } +} \ No newline at end of file diff --git a/src/IFoxCAD.Cad/ExtensionMethod/EntityEx.cs b/src/IFoxCAD.Cad/ExtensionMethod/EntityEx.cs index 9f9fc55..23ac2e4 100644 --- a/src/IFoxCAD.Cad/ExtensionMethod/EntityEx.cs +++ b/src/IFoxCAD.Cad/ExtensionMethod/EntityEx.cs @@ -13,10 +13,10 @@ public static class EntityEx /// 实体对象 public static void Flush(this Entity entity, DBTrans? trans = null) { - //if (entity is null) - //{ + // if (entity is null) + // { // throw new ArgumentNullException(nameof(entity)); - //} + // } trans ??= DBTrans.Top; entity.RecordGraphicsModified(true); trans.Transaction.TransactionManager.QueueForGraphicsFlush(); @@ -197,8 +197,10 @@ public static void ValidateMirror(this DBText txt) /// 多行文字 /// 存储对象变量 /// 回调函数,用于处理炸散之后的对象 - /// 多行文字炸散后的对象 - /// 回调函数处理的结果 + /// + /// 多行文字炸散后的对象
    + /// 回调函数处理的结果 + ///
    /// public static void ExplodeFragments(this MText mt, T obj, Func mTextFragmentCallback) { @@ -240,7 +242,7 @@ public static Arc CreateArcSCE(Point3d startPoint, Point3d centerPoint, Point3d arc.Radius = centerPoint.DistanceTo(startPoint); Vector2d startVector = new(startPoint.X - centerPoint.X, startPoint.Y - centerPoint.Y); Vector2d endVector = new(endPoint.X - centerPoint.X, endPoint.Y - centerPoint.Y); - //计算起始和终止角度 + // 计算起始和终止角度 arc.StartAngle = startVector.Angle; arc.EndAngle = endVector.Angle; return arc; @@ -255,9 +257,9 @@ public static Arc CreateArcSCE(Point3d startPoint, Point3d centerPoint, Point3d /// 圆弧 public static Arc CreateArc(Point3d startPoint, Point3d pointOnArc, Point3d endPoint) { - //创建一个几何类的圆弧对象 + // 创建一个几何类的圆弧对象 CircularArc3d geArc = new(startPoint, pointOnArc, endPoint); - //将几何类圆弧对象的圆心和半径赋值给圆弧 + // 将几何类圆弧对象的圆心和半径赋值给圆弧 #if NET35 return (Arc)geArc.ToCurve(); #else @@ -313,14 +315,14 @@ public static Circle CreateCircle(Point3d startPoint, Point3d endPoint) /// public static Circle? CreateCircle(Point3d pt1, Point3d pt2, Point3d pt3) { - //先判断三点是否共线,得到pt1点指向pt2、pt2点的矢量 + // 先判断三点是否共线,得到pt1点指向pt2、pt2点的矢量 Vector3d va = pt1.GetVectorTo(pt2); Vector3d vb = pt1.GetVectorTo(pt3); - //如两矢量夹角为0或180度(π弧度),则三点共线. + // 如两矢量夹角为0或180度(π弧度),则三点共线. if (va.GetAngleTo(vb) == 0 | va.GetAngleTo(vb) == Math.PI) return null; - //创建一个几何类的圆弧对象 + // 创建一个几何类的圆弧对象 CircularArc3d geArc = new(pt1, pt2, pt3); geArc.ToCircle(); return geArc.ToCircle(); @@ -334,7 +336,7 @@ public static Circle CreateCircle(Point3d startPoint, Point3d endPoint) /// 图形的ObjectId public static Circle? CreateCircle(Point3d center, double radius, double vex = 0, double vey = 0, double vez = 1) { - return new Circle(center, new Vector3d(vex, vey, vez), radius);//平面法向量XY方向 + return new Circle(center, new Vector3d(vex, vey, vez), radius);// 平面法向量XY方向 } #endregion @@ -364,7 +366,7 @@ public static void ClipBlockRef(this BlockReference bref, IEnumerable p using SpatialFilter sf = new() { Definition = sfd }; var dict = bref.GetXDictionary()!.GetSubDictionary(true, new string[] { filterDictName })!; dict.SetAt(spatialName, sf); - //SetToDictionary(dict, spatialName, sf); + // SetToDictionary(dict, spatialName, sf); } /// @@ -389,10 +391,11 @@ public static void ClipBlockRef(this BlockReference bref, Point3d pt1, Point3d p using SpatialFilter sf = new() { Definition = sfd }; var dict = bref.GetXDictionary()!.GetSubDictionary(true, new string[] { filterDictName })!; dict.SetAt(spatialName, sf); - //SetToDictionary(dict, spatialName, sf); + // SetToDictionary(dict, spatialName, sf); } #endregion + #region 属性 /// /// 更新动态块属性值 /// @@ -412,4 +415,17 @@ public static void ChangeBlockProperty(this BlockReference blockReference, } } #endregion -} + + /// + /// 获取图元包围盒 + /// + /// + /// 包围盒信息 + /// 异常: + /// 会将包围盒类型记录到所属路径中,以此查询 + public static BoundingInfo GetBoundingBoxEx(this Entity ent) + { + return EntityBoundingInfo.GetBoundingInfo(ent); + } + #endregion +} \ No newline at end of file diff --git a/src/IFoxCAD.Cad/ExtensionMethod/Enums.cs b/src/IFoxCAD.Cad/ExtensionMethod/Enums.cs index fc0fbaf..2dd94b5 100644 --- a/src/IFoxCAD.Cad/ExtensionMethod/Enums.cs +++ b/src/IFoxCAD.Cad/ExtensionMethod/Enums.cs @@ -1,5 +1,91 @@ namespace IFoxCAD.Cad; +/// +/// 参照路径转换 +/// +public enum PathConverterModes : byte +{ + /// + /// 相对路径 + /// + Relative, + /// + /// 绝对路径 + /// + Complete +} + +/// +/// 参照绑定 +/// +public enum XrefModes : byte +{ + /// + /// 卸载 + /// + Unload, + /// + /// 重载 + /// + Reload, + /// + /// 拆离 + /// + Detach, + /// + /// 绑定 + /// + Bind, +} + +public enum SymModes : ushort +{ + /// + /// 块表 + /// + BlockTable = 1, + + /// + /// 图层表 + /// + LayerTable = 2, + /// + /// 文字样式表 + /// + TextStyleTable = 4, + /// + /// 注册应用程序表 + /// + RegAppTable = 8, + /// + /// 标注样式表 + /// + DimStyleTable = 16, + /// + /// 线型表 + /// + LinetypeTable = 32, + Option1 = LayerTable | TextStyleTable | DimStyleTable | LinetypeTable | RegAppTable, + + /// + /// 用户坐标系表 + /// + UcsTable = 64, + /// + /// 视图表 + /// + ViewTable = 128, + /// + /// 视口表 + /// + ViewportTable = 256, + Option2 = UcsTable | ViewTable | ViewportTable, + + // 全部 + All = BlockTable | Option1 | Option2 +} + + /// /// 坐标系类型枚举 /// @@ -88,20 +174,22 @@ public enum FontTTF } - -public static class EnumHelper +public class LoopState { - public static string GetDesc(this Enum val) - { - var type = val.GetType(); - var memberInfo = type.GetMember(val.ToString()); - var attributes = memberInfo[0].GetCustomAttributes(typeof(DescriptionAttribute), false); - //如果没有定义描述,就把当前枚举值的对应名称返回 - if (attributes is null || attributes.Length != 1) - { - return val.ToString(); - } - return ((DescriptionAttribute)attributes.Single()).Description; - } -} - + const int PLS_NONE = 0; + const int PLS_EXCEPTIONAL = 1; + const int PLS_BROKEN = 2; + const int PLS_STOPPED = 4; + const int PLS_CANCELED = 8; + + private volatile int _LoopStateFlags = PLS_NONE; + + public bool IsRun => _LoopStateFlags == PLS_NONE; + public bool IsCancel => _LoopStateFlags == PLS_CANCELED; + public bool IsExceptional => _LoopStateFlags == PLS_EXCEPTIONAL; + + public bool IsBreak => (_LoopStateFlags & PLS_BROKEN) == PLS_BROKEN; + public bool IsStop => (_LoopStateFlags & PLS_STOPPED) == PLS_STOPPED; + public void Stop() => _LoopStateFlags = PLS_STOPPED; + public void Break() => _LoopStateFlags = PLS_BROKEN; +} \ No newline at end of file diff --git a/src/IFoxCAD.Cad/ExtensionMethod/GeometryEx.cs b/src/IFoxCAD.Cad/ExtensionMethod/GeometryEx.cs index eca3a15..58749b2 100644 --- a/src/IFoxCAD.Cad/ExtensionMethod/GeometryEx.cs +++ b/src/IFoxCAD.Cad/ExtensionMethod/GeometryEx.cs @@ -18,7 +18,7 @@ public static class GeometryEx /// 点与多边形的关系 public static PointOnRegionType PointOnRegion(this IEnumerable pts, Point2d pt) { - //遍历点集并生成首尾连接的多边形 + // 遍历点集并生成首尾连接的多边形 var ptlst = new LoopList(pts); if (ptlst.Count < 3) return PointOnRegionType.Error; @@ -30,11 +30,11 @@ public static PointOnRegionType PointOnRegion(this IEnumerable pts, Poi } var cc2d = new CompositeCurve2d(ls2ds.ToArray()); - //在多边形上? + // 在多边形上? if (cc2d.IsOn(pt)) return PointOnRegionType.On; - //在最小包围矩形外? + // 在最小包围矩形外? var bb2d = cc2d.BoundBlock; if (!bb2d.Contains(pt)) return PointOnRegionType.Outside; @@ -68,7 +68,7 @@ public static PointOnRegionType PointOnRegion(this IEnumerable pts, Poi /// 点与多边形的关系 public static PointOnRegionType PointOnRegion(this IEnumerable pts, Point3d pt) { - //遍历点集并生成首尾连接的多边形 + // 遍历点集并生成首尾连接的多边形 var ptlst = new LoopList(pts); if (ptlst.First!.Value == ptlst.Last!.Value) ptlst.RemoveLast(); @@ -77,16 +77,14 @@ public static PointOnRegionType PointOnRegion(this IEnumerable pts, Poi var ls3ds = new List(); foreach (var node in ptlst.GetNodes()) - { ls3ds.Add(new LineSegment3d(node.Value, node.Next!.Value)); - } var cc3d = new CompositeCurve3d(ls3ds.ToArray()); - //在多边形上? + // 在多边形上? if (cc3d.IsOn(pt)) return PointOnRegionType.On; - //在最小包围矩形外? + // 在最小包围矩形外? var bb2d = cc3d.BoundBlock; if (!bb2d.Contains(pt)) return PointOnRegionType.Outside; @@ -142,7 +140,7 @@ public static CircularArc2d GetMinCircle(Point2d pt1, Point2d pt2, Point2d pt3, { ptlst = new LoopList { pt1, pt2, pt3 }; - //遍历各点与下一点的向量长度,找到距离最大的两个点 + // 遍历各点与下一点的向量长度,找到距离最大的两个点 LoopListNode maxNode = ptlst.GetNodes().FindByMax ( @@ -150,19 +148,19 @@ public static CircularArc2d GetMinCircle(Point2d pt1, Point2d pt2, Point2d pt3, node => node.Value.GetDistanceTo(node.Next!.Value) ); - //以两点做最小包围圆 + // 以两点做最小包围圆 CircularArc2d ca2d = GetMinCircle(maxNode.Value, maxNode.Next!.Value, out LoopList tptlst); - //如果另一点属于该圆 + // 如果另一点属于该圆 if (ca2d.IsIn(maxNode.Previous!.Value)) { - //返回 + // 返回 ptlst = tptlst; return ca2d; } - //否则按三点做圆 - //ptlst.SetFirst(maxNode); + // 否则按三点做圆 + // ptlst.SetFirst(maxNode); ptlst = new LoopList { maxNode.Value, maxNode.Next.Value, maxNode.Previous.Value }; ca2d = new CircularArc2d(pt1, pt2, pt3); ca2d.SetAngles(0, Math.PI * 2); @@ -184,26 +182,25 @@ public static CircularArc2d GetMinCircle(Point2d pt1, Point2d pt2, Point2d pt3, ptlst = null; CircularArc2d? ca2d = null; - //遍历C43的组合,环链表的优势在这里 + // 遍历C43的组合,环链表的优势在这里 foreach (LoopListNode firstNode in iniptlst.GetNodes()) { - //获取各组合下三点的最小包围圆 + // 获取各组合下三点的最小包围圆 var secondNode = firstNode.Next; var thirdNode = secondNode!.Next; - CircularArc2d tca2d = GetMinCircle(firstNode.Value, secondNode.Value, thirdNode!.Value, out LoopList tptlst); + var tca2d = GetMinCircle(firstNode.Value, secondNode.Value, thirdNode!.Value, out LoopList tptlst); - //如果另一点属于该圆,并且半径小于当前值就把它做为候选解 - if (tca2d.IsIn(firstNode.Previous!.Value)) + // 如果另一点属于该圆,并且半径小于当前值就把它做为候选解 + if (!tca2d.IsIn(firstNode.Previous!.Value)) + continue; + if (ca2d is null || tca2d.Radius < ca2d.Radius) { - if (ca2d is null || tca2d.Radius < ca2d.Radius) - { - ca2d = tca2d; - ptlst = tptlst; - } + ca2d = tca2d; + ptlst = tptlst; } } - //返回直径最小的圆 + // 返回直径最小的圆 return ca2d; } @@ -216,7 +213,7 @@ public static CircularArc2d GetMinCircle(Point2d pt1, Point2d pt2, Point2d pt3, /// 三点围成的三角形的有向面积 private static double CalArea(Point2d ptBase, Point2d pt1, Point2d pt2) { - return (pt2 - ptBase).DotProduct((pt1 - ptBase).GetPerpendicularVector()) / 2; + return (pt2 - ptBase).DotProduct((pt1 - ptBase).GetPerpendicularVector()) * 0.5; } /// /// 计算三点围成的三角形的真实面积 @@ -239,10 +236,8 @@ public static double GetArea(this Point2d ptBase, Point2d pt1, Point2d pt2) /// OrientationType 类型值 public static OrientationType IsClockWise(this Point2d ptBase, Point2d pt1, Point2d pt2) { - return CalArea(ptBase, pt1, pt2) switch { - > 0 => OrientationType.CounterClockWise, < 0 => OrientationType.ClockWise, _ => OrientationType.Parallel @@ -296,10 +291,10 @@ public static OrientationType IsClockWise(Vector2d vecBase, Vector2d vec) /// 有向面积 private static double CalArea(IEnumerable pnts) { - IEnumerator itor = pnts.GetEnumerator(); + var itor = pnts.GetEnumerator(); if (!itor.MoveNext()) throw new ArgumentNullException(nameof(pnts)); - Point2d start = itor.Current; + var start = itor.Current; Point2d p1, p2 = start; double area = 0; @@ -346,7 +341,7 @@ public static OrientationType IsClockWise(this IEnumerable pnts) /// 解析类圆对象 public static CircularArc2d? GetMinCircle(this List pnts, out LoopList? ptlst) { - //点数较小时直接返回 + // 点数较小时直接返回 switch (pnts.Count) { case 0: @@ -367,29 +362,29 @@ public static OrientationType IsClockWise(this IEnumerable pnts) return GetMinCircle(pnts[0], pnts[1], pnts[2], pnts[3], out ptlst); } - //按前三点计算最小包围圆 - Point2d[] tpnts = new Point2d[4]; + // 按前三点计算最小包围圆 + var tpnts = new Point2d[4]; pnts.CopyTo(0, tpnts, 0, 3); - CircularArc2d? ca2d = GetMinCircle(tpnts[0], tpnts[1], tpnts[2], out ptlst); + var ca2d = GetMinCircle(tpnts[0], tpnts[1], tpnts[2], out ptlst); - //找到点集中距离圆心的最远点为第四点 + // 找到点集中距离圆心的最远点为第四点 tpnts[3] = pnts.FindByMax(pnt => ca2d.Center.GetDistanceTo(pnt)); - //如果最远点属于圆结束 + // 如果最远点属于圆结束 while (!ca2d.IsIn(tpnts[3])) { - //如果最远点不属于圆,按此四点计算最小包围圆 + // 如果最远点不属于圆,按此四点计算最小包围圆 ca2d = GetMinCircle(tpnts[0], tpnts[1], tpnts[2], tpnts[3], out ptlst); - //将结果作为新的前三点 + // 将结果作为新的前三点 if (ptlst!.Count == 3) { tpnts[2] = ptlst.Last!.Value; } else { - //第三点取另两点中距离圆心较远的点 - //按算法中描述的任选其中一点的话,还是无法收敛...... + // 第三点取另两点中距离圆心较远的点 + // 按算法中描述的任选其中一点的话,还是无法收敛...... tpnts[2] = tpnts.Except(ptlst) .FindByMax(pnt => ca2d!.Center.GetDistanceTo(pnt)); @@ -397,10 +392,10 @@ public static OrientationType IsClockWise(this IEnumerable pnts) tpnts[0] = ptlst.First!.Value; tpnts[1] = ptlst.First.Next!.Value; - //按此三点计算最小包围圆 + // 按此三点计算最小包围圆 ca2d = GetMinCircle(tpnts[0], tpnts[1], tpnts[2], out ptlst); - //找到点集中圆心的最远点为第四点 + // 找到点集中圆心的最远点为第四点 tpnts[3] = pnts.FindByMax(pnt => ca2d.Center.GetDistanceTo(pnt)); } @@ -614,7 +609,7 @@ public static Point3d Point3d(this Point2d pt) /// 二维点 /// Z值 /// 三维点 - public static Point3d Point3d(this Point2d pt,double z) + public static Point3d Point3d(this Point2d pt, double z) { return new Point3d(pt.X, pt.Y, z); } @@ -629,7 +624,7 @@ public static Point3d GetMidPointTo(this Point3d pt1, Point3d pt2) { return new Point3d((pt1.X + pt2.X) * 0.5, (pt1.Y + pt2.Y) * 0.5, (pt1.Z + pt2.Z) * 0.5); } - + /// /// 获取两个点之间的中点 /// diff --git a/src/IFoxCAD.Cad/ExtensionMethod/Jig.cs b/src/IFoxCAD.Cad/ExtensionMethod/JigEx.cs similarity index 69% rename from src/IFoxCAD.Cad/ExtensionMethod/Jig.cs rename to src/IFoxCAD.Cad/ExtensionMethod/JigEx.cs index b9182ae..a7e6dc1 100644 --- a/src/IFoxCAD.Cad/ExtensionMethod/Jig.cs +++ b/src/IFoxCAD.Cad/ExtensionMethod/JigEx.cs @@ -30,10 +30,10 @@ public class JigEx : DrawJig readonly Action>? _mouseAction; - readonly Tolerance _tolerance;//容差 + readonly Tolerance _tolerance;// 容差 - readonly Queue _drawEntitys;//重复生成的图元,放在这里刷新 - JigPromptPointOptions? _options;//jig鼠标配置 + readonly Queue _drawEntitys;// 重复生成的图元,放在这里刷新 + JigPromptPointOptions? _options;// jig鼠标配置 bool _worldDrawFlag = false; // 20220503 bool _systemVariables_Orthomode = false; @@ -42,7 +42,7 @@ bool SystemVariables_Orthomode // 正交修改还原 get => _systemVariables_Orthomode; set { - //1正交,0非正交 //setvar: https://www.cnblogs.com/JJBox/p/10209541.html + // 1正交,0非正交 // setvar: https://www.cnblogs.com/JJBox/p/10209541.html if (Env.OrthoMode != value) Env.OrthoMode = _systemVariables_Orthomode = value; } @@ -53,18 +53,20 @@ bool SystemVariables_Orthomode // 正交修改还原 /// /// 在界面绘制图元 /// - private JigEx() + JigEx() { _drawEntitys = new(); + DimensionEntitys = new(); } /// /// 在界面绘制图元 /// /// - /// 用来频繁执行的回调: - /// 鼠标点; - /// 加入新建的图元,鼠标采样期间会Dispose图元的;所以已经在数据库图元利用事件加入,不要在此加入; + /// 用来频繁执行的回调:
    + /// 鼠标点;
    + /// 加入新建的图元,鼠标采样期间会Dispose图元的;
    + /// 所以已经在数据库图元利用事件加入,不要在此加入;
    /// /// 鼠标移动的容差 public JigEx(Action>? action = null, double tolerance = 1e-6) : this() @@ -92,9 +94,9 @@ public JigPromptPointOptions SetOptions(Point3d basePoint, _options = JigPointOptions(); _options.Message = Environment.NewLine + msg; - _options.Cursor = cursorType; //光标绑定 - _options.UseBasePoint = true; //基点打开 - _options.BasePoint = basePoint; //基点设定 + _options.Cursor = cursorType; // 光标绑定 + _options.UseBasePoint = true; // 基点打开 + _options.BasePoint = basePoint; // 基点设定 return _options; } @@ -115,7 +117,7 @@ public JigPromptPointOptions SetOptions(string msg, _options = JigPointOptions(); _options.Message = Environment.NewLine + msg; - //加入关键字,加入时候将空格内容放到最后 + // 加入关键字,加入时候将空格内容放到最后 string spaceValue = string.Empty; const string spaceKey = " "; @@ -126,9 +128,9 @@ public JigPromptPointOptions SetOptions(string msg, else _options.Keywords.Add(item.Key, item.Key, item.Value); - ///因为默认配置函数导致此处空格触发是无效的, - ///但是用户如果想触发,就需要在外部减去默认UserInputControls配置 - ///要放最后,才能优先触发其他关键字 + /// 因为默认配置函数导致此处空格触发是无效的, + /// 但是用户如果想触发,就需要在外部减去默认UserInputControls配置 + /// 要放最后,才能优先触发其他关键字 if (spaceValue != string.Empty) _options.Keywords.Add(spaceKey, spaceKey, spaceValue); else @@ -137,8 +139,8 @@ public JigPromptPointOptions SetOptions(string msg, // 外部设置减去配置 // _options.UserInputControls = // _options.UserInputControls - // ^ UserInputControls.NullResponseAccepted //输入了鼠标右键,结束jig - // ^ UserInputControls.AnyBlankTerminatesInput; //空格或回车,结束jig; + // ^ UserInputControls.NullResponseAccepted // 输入了鼠标右键,结束jig + // ^ UserInputControls.AnyBlankTerminatesInput; // 空格或回车,结束jig; return _options; } @@ -162,7 +164,7 @@ public void SetOptions(Action action, bool orthomode = fa /// public PromptResult Drag() { - //jig功能必然是当前前台文档,所以封装内部更好调用 + // jig功能必然是当前前台文档,所以封装内部更好调用 var dm = Acap.DocumentManager; var doc = dm.MdiActiveDocument; var ed = doc.Editor; @@ -182,13 +184,13 @@ public PromptResult Drag() public IEnumerable? AddEntityToMsPs(BlockTableRecord btrOfAddEntitySpace, IEnumerable? removeEntity = null) { - //内部用 _drawEntitys 外部用 Entitys,减少一层转换 + // 内部用 _drawEntitys 外部用 Entitys,减少一层转换 if (_drawEntitys.Count == 0) return null; IEnumerable es = _drawEntitys; if (removeEntity != null) - es = es.Except(removeEntity);//差集 + es = es.Except(removeEntity);// 差集 return btrOfAddEntitySpace.AddEntity(es); } @@ -203,7 +205,7 @@ public PromptResult Drag() protected override SamplerStatus Sampler(JigPrompts prompts) { if (_worldDrawFlag) - return SamplerStatus.NoChange;//OK的时候拖动鼠标与否都不出现图元 + return SamplerStatus.NoChange;// OK的时候拖动鼠标与否都不出现图元 if (_options is null) throw new NullReferenceException(nameof(_options)); @@ -214,21 +216,21 @@ protected override SamplerStatus Sampler(JigPrompts prompts) else if (pro.Status != PromptStatus.OK) return SamplerStatus.Cancel; - //上次鼠标点不同(一定要这句,不然图元刷新太快会看到奇怪的边线) + // 上次鼠标点不同(一定要这句,不然图元刷新太快会看到奇怪的边线) var mousePointWcs = pro.Value; - //== 是比较类字段,但是最好转为哈希比较. - //IsEqualTo 是方形判断(仅加法),但是cad是距离. - //Distance 是圆形判断(会求平方根,使用了牛顿迭代), - //大量数据(十万以上/频繁刷新)面前会显得非常慢. + // == 是比较类字段,但是最好转为哈希比较. + // IsEqualTo 是方形判断(仅加法),但是cad是距离. + // Distance 是圆形判断(会求平方根,使用了牛顿迭代), + // 大量数据(十万以上/频繁刷新)面前会显得非常慢. if (mousePointWcs.IsEqualTo(MousePointWcsLast, _tolerance)) return SamplerStatus.NoChange; - //上次循环的缓冲区图元清理,否则将会在vs输出遗忘 Dispose + // 上次循环的缓冲区图元清理,否则将会在vs输出遗忘 Dispose while (_drawEntitys.Count > 0) _drawEntitys.Dequeue().Dispose(); - //委托把容器扔出去接收新创建的图元,然后给重绘更新 + // 委托把容器扔出去接收新创建的图元,然后给重绘更新 _mouseAction?.Invoke(mousePointWcs, _drawEntitys); MousePointWcsLast = mousePointWcs; @@ -236,10 +238,12 @@ protected override SamplerStatus Sampler(JigPrompts prompts) } /// - /// 重绘已在数据库的图元 - /// 0x01 此处不加入newEntity的,它们在构造函数的参数回调处加入,它们会进行频繁new和Dispose从而避免遗忘释放 - /// 0x02 此处用于重绘已经在数据的图元 - /// 0x03 此处用于图元亮显暗显,因为会被重绘冲刷掉所以独立出来不重绘,它们也往往已经存在数据库的 + /// 重绘已在数据库的图元 + /// + /// 0x01 此处不加入newEntity的,它们在构造函数的参数回调处加入,它们会进行频繁new和Dispose从而避免遗忘释放
    + /// 0x02 此处用于重绘已经在数据的图元
    + /// 0x03 此处用于图元亮显暗显,因为会被重绘冲刷掉所以独立出来不重绘,它们也往往已经存在数据库的 + ///
    ///
    /// /// newEntity只会存在一个图元队列中,而数据库图元可以分多个集合 @@ -290,6 +294,7 @@ protected override bool WorldDraw(WorldDraw draw) } #endregion + /// /// 用户输入控制默认配置 /// 令jig.Drag().Status == @@ -300,10 +305,10 @@ static JigPromptPointOptions JigPointOptions() return new JigPromptPointOptions() { UserInputControls = - UserInputControls.GovernedByUCSDetect //由UCS探测用 - | UserInputControls.Accept3dCoordinates //接受三维坐标 - | UserInputControls.NullResponseAccepted //输入了鼠标右键,结束jig - | UserInputControls.AnyBlankTerminatesInput //空格或回车,结束jig; + UserInputControls.GovernedByUCSDetect // 由UCS探测用 + | UserInputControls.Accept3dCoordinates // 接受三维坐标 + | UserInputControls.NullResponseAccepted // 输入了鼠标右键,结束jig + | UserInputControls.AnyBlankTerminatesInput // 空格或回车,结束jig; }; } @@ -318,23 +323,48 @@ public void SetSpaceIsKeyword() throw new ArgumentNullException(nameof(_options)); if ((opt.UserInputControls & UserInputControls.NullResponseAccepted) == UserInputControls.NullResponseAccepted) - opt.UserInputControls ^= UserInputControls.NullResponseAccepted; //输入了鼠标右键,结束jig + opt.UserInputControls ^= UserInputControls.NullResponseAccepted; // 输入了鼠标右键,结束jig if ((opt.UserInputControls & UserInputControls.AnyBlankTerminatesInput) == UserInputControls.AnyBlankTerminatesInput) - opt.UserInputControls ^= UserInputControls.AnyBlankTerminatesInput; //空格或回车,结束jig + opt.UserInputControls ^= UserInputControls.AnyBlankTerminatesInput; // 空格或回车,结束jig } + + + #region 注释数据 + /// + /// 注释数据,可以在缩放的时候不受影响 + /// + public DynamicDimensionDataCollection DimensionEntitys; + + /// + /// 重写注释数据 + /// + /// + /// + protected override DynamicDimensionDataCollection GetDynamicDimensionData(double dimScale) + { + base.GetDynamicDimensionData(dimScale); + return DimensionEntitys; + } + #endregion + + // 此处无测试 + //protected override void ViewportDraw(ViewportDraw draw) + //{ + // base.ViewportDraw(draw); + //} } #if false -| UserInputControls.DoNotEchoCancelForCtrlC //不要取消CtrlC的回音 -| UserInputControls.DoNotUpdateLastPoint //不要更新最后一点 -| UserInputControls.NoDwgLimitsChecking //没有Dwg限制检查 -| UserInputControls.NoZeroResponseAccepted //接受非零响应 -| UserInputControls.NoNegativeResponseAccepted //不否定回复已被接受 -| UserInputControls.Accept3dCoordinates //返回点的三维坐标,是转换坐标系了? -| UserInputControls.AcceptMouseUpAsPoint //接受释放按键时的点而不是按下时 - -| UserInputControls.InitialBlankTerminatesInput //初始 空格或回车,结束jig -| UserInputControls.AcceptOtherInputString //接受其他输入字符串 -| UserInputControls.NoZDirectionOrtho //无方向正射,直接输入数字时以基点到当前点作为方向 -| UserInputControls.UseBasePointElevation //使用基点高程,基点的Z高度探测 +| UserInputControls.DoNotEchoCancelForCtrlC // 不要取消CtrlC的回音 +| UserInputControls.DoNotUpdateLastPoint // 不要更新最后一点 +| UserInputControls.NoDwgLimitsChecking // 没有Dwg限制检查 +| UserInputControls.NoZeroResponseAccepted // 接受非零响应 +| UserInputControls.NoNegativeResponseAccepted // 不否定回复已被接受 +| UserInputControls.Accept3dCoordinates // 返回点的三维坐标,是转换坐标系了? +| UserInputControls.AcceptMouseUpAsPoint // 接受释放按键时的点而不是按下时 + +| UserInputControls.InitialBlankTerminatesInput // 初始 空格或回车,结束jig +| UserInputControls.AcceptOtherInputString // 接受其他输入字符串 +| UserInputControls.NoZDirectionOrtho // 无方向正射,直接输入数字时以基点到当前点作为方向 +| UserInputControls.UseBasePointElevation // 使用基点高程,基点的Z高度探测 #endif \ No newline at end of file diff --git a/src/IFoxCAD.Cad/ExtensionMethod/JigExTransient.cs b/src/IFoxCAD.Cad/ExtensionMethod/JigExTransient.cs index 770739a..e161928 100644 --- a/src/IFoxCAD.Cad/ExtensionMethod/JigExTransient.cs +++ b/src/IFoxCAD.Cad/ExtensionMethod/JigExTransient.cs @@ -1,4 +1,4 @@ -#if !ac2008 +#if !ac2008 namespace IFoxCAD.Cad; /// diff --git a/src/IFoxCAD.Cad/ExtensionMethod/ObjectIdEx.cs b/src/IFoxCAD.Cad/ExtensionMethod/ObjectIdEx.cs index e147d6c..d0ebf72 100644 --- a/src/IFoxCAD.Cad/ExtensionMethod/ObjectIdEx.cs +++ b/src/IFoxCAD.Cad/ExtensionMethod/ObjectIdEx.cs @@ -19,7 +19,7 @@ public static class ObjectIdEx public static T? GetObject(this ObjectId id, OpenMode mode = OpenMode.ForRead, bool openErased = false, Transaction? tr = default) where T : DBObject { tr ??= DBTrans.Top.Transaction; - //tr = Env.GetTrans(tr); + // tr = Env.GetTrans(tr); return tr.GetObject(id, mode, openErased) as T; } @@ -32,7 +32,10 @@ public static class ObjectIdEx /// 打开模式 /// 打开删除对象 /// 指定类型对象集合 - public static IEnumerable GetObject(this IEnumerable ids, OpenMode mode = OpenMode.ForRead, bool openErased = false, Transaction? tr = default) where T : DBObject + public static IEnumerable GetObject(this IEnumerable ids, + OpenMode mode = OpenMode.ForRead, + bool openErased = false, + Transaction? tr = default) where T : DBObject { return ids.Select(id => id.GetObject(mode, openErased, tr)); } @@ -50,7 +53,7 @@ public static IEnumerable OfType(this IEnumerable ids) wh } #endregion GetObject - //Acad08缺少 id.ObjectClass 如何补偿? + // Acad08缺少 id.ObjectClass 如何补偿? public static RXClass ObjectClass(this ObjectId id) { #if NET35 @@ -83,7 +86,7 @@ public static void Erase(this ObjectId id) { ent.Erase(); }// 第一种读写权限自动转换写法 - //Env.Editor.Regen(); + // Env.Editor.Regen(); } } -} +} \ No newline at end of file diff --git a/src/IFoxCAD.Cad/ExtensionMethod/PointEx.cs b/src/IFoxCAD.Cad/ExtensionMethod/PointEx.cs index 4898d50..f65c24e 100644 --- a/src/IFoxCAD.Cad/ExtensionMethod/PointEx.cs +++ b/src/IFoxCAD.Cad/ExtensionMethod/PointEx.cs @@ -21,7 +21,7 @@ public static string GetHashString(this Point3d pt, int xyz = 3, int decimalReta }; } - //为了频繁触发所以弄个全局变量 + // 为了频繁触发所以弄个全局变量 static Plane? _Plane; /// /// 两点计算弧度范围0到2Pi @@ -57,7 +57,7 @@ public static double GetAngle(this Point2d startPoint, Point2d endPoint) /// public static Point2d GetCenter(this Point2d a, Point2d b) { - // (p1 + p2) / 2; //溢出风险 + // (p1 + p2) / 2; // 溢出风险 return new Point2d(a.X * 0.5 + b.X * 0.5, a.Y * 0.5 + b.Y * 0.5); } @@ -74,13 +74,13 @@ public static double GetArcBulge(this Point2d arc1, Point2d arc2, Point2d arc3, { double dStartAngle = arc2.GetAngle(arc1); double dEndAngle = arc2.GetAngle(arc3); - //求的P1P2与P1P3夹角 + // 求的P1P2与P1P3夹角 var talAngle = (Math.PI - dStartAngle + dEndAngle) / 2; - //凸度==拱高/半弦长==拱高比值/半弦长比值 - //有了比值就不需要拿到拱高值和半弦长值了,因为接下来是相除得凸度 + // 凸度==拱高/半弦长==拱高比值/半弦长比值 + // 有了比值就不需要拿到拱高值和半弦长值了,因为接下来是相除得凸度 double bulge = Math.Sin(talAngle) / Math.Cos(talAngle); - //处理精度 + // 处理精度 if (bulge > 0.9999 && bulge < 1.0001) bulge = 1; else if (bulge < -0.9999 && bulge > -1.0001) @@ -100,10 +100,10 @@ public static Point2dCollection End2End(this Point2dCollection ptcol) if (ptcol == null) throw new ArgumentNullException(nameof(ptcol)); - if (ptcol.Count == 0 || ptcol[0].Equals(ptcol[^1]))//首尾相同直接返回 + if (ptcol.Count == 0 || ptcol[0].Equals(ptcol[^1]))// 首尾相同直接返回 return ptcol; - //首尾不同,去加一个到最后 + // 首尾不同,去加一个到最后 var lst = new Point2d[ptcol.Count + 1]; for (int i = 0; i < lst.Length; i++) lst[i] = ptcol[i]; @@ -119,10 +119,10 @@ public static Point3dCollection End2End(this Point3dCollection ptcol) if (ptcol == null) throw new ArgumentNullException(nameof(ptcol)); - if (ptcol.Count == 0 || ptcol[0].Equals(ptcol[^1]))//首尾相同直接返回 + if (ptcol.Count == 0 || ptcol[0].Equals(ptcol[^1]))// 首尾相同直接返回 return ptcol; - //首尾不同,去加一个到最后 + // 首尾不同,去加一个到最后 var lst = new Point3d[ptcol.Count + 1]; for (int i = 0; i < lst.Length; i++) lst[i] = ptcol[i]; diff --git a/src/IFoxCAD.Cad/ExtensionMethod/SymbolTableEx.cs b/src/IFoxCAD.Cad/ExtensionMethod/SymbolTableEx.cs index a390436..4703c78 100644 --- a/src/IFoxCAD.Cad/ExtensionMethod/SymbolTableEx.cs +++ b/src/IFoxCAD.Cad/ExtensionMethod/SymbolTableEx.cs @@ -26,8 +26,8 @@ public static ObjectId Add(this SymbolTable table, /// 图层id public static ObjectId Add(this SymbolTable table, string name, int colorIndex) { - colorIndex %= 256;//防止输入的颜色超出256 - colorIndex = Math.Abs(colorIndex);//防止负数 + colorIndex %= 256;// 防止输入的颜色超出256 + colorIndex = Math.Abs(colorIndex);// 防止负数 return table.Add(name, lt => lt.Color = Color.FromColorIndex(ColorMethod.ByColor, (short)colorIndex)); } /// @@ -184,9 +184,9 @@ public static void AddAttsToBlocks(this SymbolTable块定义Id public static ObjectId GetBlockFrom(this SymbolTable table, string fileName, bool over) { - //FileInfo fi = new(fileName); - //string blkdefname = fi.Name; - //if (blkdefname.Contains(".")) + // FileInfo fi = new(fileName); + // string blkdefname = fi.Name; + // if (blkdefname.Contains(".")) // blkdefname = blkdefname.Substring(0, blkdefname.LastIndexOf('.')); string blkdefname = SymbolUtilityServices.RepairSymbolName( @@ -241,16 +241,16 @@ public static ObjectId Add(this SymbolTable name, ltt => { ltt.AsciiDescription = description; - ltt.PatternLength = length; //线型的总长度 - ltt.NumDashes = dash.Length; //组成线型的笔画数目 + ltt.PatternLength = length; // 线型的总长度 + ltt.NumDashes = dash.Length; // 组成线型的笔画数目 for (int i = 0; i < dash.Length; i++) { ltt.SetDashLengthAt(i, dash[i]); } - //ltt.SetDashLengthAt(0, 0.5); //0.5个单位的划线 - //ltt.SetDashLengthAt(1, -0.25); //0.25个单位的空格 - //ltt.SetDashLengthAt(2, 0); // 一个点 - //ltt.SetDashLengthAt(3, -0.25); //0.25个单位的空格 + // ltt.SetDashLengthAt(0, 0.5); // 0.5个单位的划线 + // ltt.SetDashLengthAt(1, -0.25); // 0.25个单位的空格 + // ltt.SetDashLengthAt(2, 0); // 一个点 + // ltt.SetDashLengthAt(3, -0.25); // 0.25个单位的空格 } ); } diff --git a/src/IFoxCAD.Cad/ExtensionMethod/SymbolTableRecordEx.cs b/src/IFoxCAD.Cad/ExtensionMethod/SymbolTableRecordEx.cs index bceeac0..5a6f4cc 100644 --- a/src/IFoxCAD.Cad/ExtensionMethod/SymbolTableRecordEx.cs +++ b/src/IFoxCAD.Cad/ExtensionMethod/SymbolTableRecordEx.cs @@ -10,12 +10,16 @@ public static class SymbolTableRecordEx #region 克隆实体id /// /// 深度克隆id到块表记录 - /// 0x01 此方法不允许是未添加数据库的图元,因此它是id - /// 0x02 若为未添加数据库图元,则利用entity.Clone();同时不需要考虑动态块属性,可以使用entity.GetTransformedCopy + /// + /// 0x01 此方法不允许是未添加数据库的图元,因此它是id
    + /// 0x02 若为未添加数据库图元,则利用entity.Clone();同时不需要考虑动态块属性,可以使用entity.GetTransformedCopy + ///
    ///
    /// - /// 克隆到当前块表记录,相当于原地克隆 - /// 克隆到目标块表记录内,相当于制作新块 + /// + /// 克隆到当前块表记录,相当于原地克隆
    + /// 克隆到目标块表记录内,相当于制作新块 + ///
    /// /// 图元id集合,注意所有成员都要在同一个空间中 /// 克隆后的id词典 @@ -52,12 +56,12 @@ public static IdMapping DeepClone(this BlockTableRecord btr, ObjectIdCollection ///
    /// 图元 /// 矩阵 - //public static void EntityTransformedCopy(this Entity ent, Matrix3d matrix) - //{ + // public static void EntityTransformedCopy(this Entity ent, Matrix3d matrix) + // { // var entNew = ent.GetTransformedCopy(matrix); // if (ent is BlockReference blockReference) // entNew.SetPropertiesFrom(blockReference); - //} + // } #endregion @@ -72,7 +76,7 @@ public static IdMapping DeepClone(this BlockTableRecord btr, ObjectIdCollection public static ObjectId AddEntity(this BlockTableRecord btr, Entity entity, Transaction? trans = null) { - //if (entity is null) + // if (entity is null) // throw new ArgumentNullException(nameof(entity), "对象为 null"); ObjectId id; @@ -96,7 +100,7 @@ public static ObjectId AddEntity(this BlockTableRecord btr, Entity entity, public static IEnumerable AddEntity(this BlockTableRecord btr, IEnumerable ents, Transaction? trans = null) where T : Entity { - //if (ents.Any(ent => ent is null)) + // if (ents.Any(ent => ent is null)) // throw new ArgumentNullException(nameof(ents), "实体集合内存在 null 对象"); trans ??= DBTrans.Top.Transaction; @@ -137,7 +141,7 @@ public static IEnumerable AddEntity(this BlockTableRecord btr, params /// 图元id private static ObjectId AddEnt(this BlockTableRecord btr, T ent, Action? action, Transaction? trans) where T : Entity { - //trans ??= DBTrans.Top.Transaction; + // trans ??= DBTrans.Top.Transaction; action?.Invoke(ent); return btr.AddEntity(ent, trans); } @@ -150,7 +154,7 @@ private static ObjectId AddEnt(this BlockTableRecord btr, T ent, Action? a /// 图元id,如果委托返回 null,则为 ObjectId.Null public static ObjectId AddEnt(this BlockTableRecord btr, Func action, Transaction? transaction) { - //transaction ??= DBTrans.Top.Transaction; + // transaction ??= DBTrans.Top.Transaction; var ent = action.Invoke(); if (ent is null) return ObjectId.Null; @@ -203,7 +207,7 @@ public static ObjectId AddCircle(this BlockTableRecord btr, Point3d p0, Point3d Action? action = default, Transaction? trans = default) { var circle = EntityEx.CreateCircle(p0, p1, p2); - //return circle is not null ? btr.AddEnt(circle, action, trans) : throw new ArgumentNullException(nameof(circle), "对象为 null"); + // return circle is not null ? btr.AddEnt(circle, action, trans) : throw new ArgumentNullException(nameof(circle), "对象为 null"); if (circle is null) throw new ArgumentNullException(nameof(circle), "对象为 null"); @@ -238,7 +242,7 @@ public static ObjectId AddPline(this BlockTableRecord btr, for (int i = 0; i < bvws.Count; i++) pl.AddVertexAt(i, bvws[i].Vertex, bvws[i].Bulge, bvws[i].StartWidth, bvws[i].EndWidth); } - pl.Closed = isClosed;//闭合 + pl.Closed = isClosed;// 闭合 return btr.AddEnt(pl, action, trans); } /// diff --git a/src/IFoxCAD.Cad/ExtensionMethod/Tools.cs b/src/IFoxCAD.Cad/ExtensionMethod/Tools.cs index 0e875c5..b11d81d 100644 --- a/src/IFoxCAD.Cad/ExtensionMethod/Tools.cs +++ b/src/IFoxCAD.Cad/ExtensionMethod/Tools.cs @@ -5,11 +5,11 @@ public static class Tools public static void TestTimes2(int count, string message, Action action) { System.Diagnostics.Stopwatch watch = new(); - watch.Start(); //开始监视代码运行时间 + watch.Start(); // 开始监视代码运行时间 for (int i = 0; i < count; i++) - action.Invoke();//需要测试的代码 - watch.Stop(); //停止监视 - TimeSpan timespan = watch.Elapsed; //获取当前实例测量得出的总时间 + action.Invoke();// 需要测试的代码 + watch.Stop(); // 停止监视 + TimeSpan timespan = watch.Elapsed; // 获取当前实例测量得出的总时间 double time = timespan.TotalMilliseconds; string name = "毫秒"; if (timespan.TotalMilliseconds > 1000) @@ -17,7 +17,7 @@ public static void TestTimes2(int count, string message, Action action) time = timespan.TotalSeconds; name = "秒"; } - Env.Print($"{message} 代码执行 {count} 次的时间:{time} ({name})"); //总毫秒数 + Env.Print($"{message} 代码执行 {count} 次的时间:{time} ({name})"); // 总毫秒数 } /// @@ -53,7 +53,7 @@ public static void TestTimes(int count, string message, Action action, } /* -//测试例子,同时验证两个计时器 +// 测试例子,同时验证两个计时器 var stopwatch = new Stopwatch(); Timer.RunTime(() => { @@ -104,7 +104,7 @@ public enum TimeEnum static extern bool QueryPerformanceFrequency(out long lpFrequency); long _startTime, _stopTime; - long _freq; + readonly long _freq; public Timer() { diff --git a/src/IFoxCAD.Cad/ExtensionMethod/XrefEx.cs b/src/IFoxCAD.Cad/ExtensionMethod/XrefEx.cs new file mode 100644 index 0000000..cb57d30 --- /dev/null +++ b/src/IFoxCAD.Cad/ExtensionMethod/XrefEx.cs @@ -0,0 +1,652 @@ +// #define error_demo + +namespace IFoxCAD.Cad; + +#region 参照工厂 +public interface IXrefBindModes +{ + /// + /// 卸载 + /// + public void Unload(); + /// + /// 重载 + /// + public void Reload(); + /// + /// 拆离 + /// + public void Detach(); + /// + /// 绑定 + /// + public void Bind(); +} + +public class XrefFactory : IXrefBindModes +{ + #region 私有字段 + readonly DBTrans _tr; + /// + /// 要处理的参照名称,就处理所有 + /// + readonly HashSet? _xrefNames; + #endregion + + #region 公开字段 + /// + /// 解析外部参照:线性引擎
    + /// 默认
    + /// 时会在cad命令历史打印一些AEC信息,并导致绑定慢一点...具体作用不详
    + ///
    + public bool UseThreadEngine = false; + /// + /// 解析外部参照:仅处理 Unresolved_未融入(未解析)的参照
    + /// 默认 + ///
    + public bool DoNewOnly = true; + /// + /// 解析外部参照:包含僵尸参照 + /// + public bool IncludeGhosts = true; + + + /// + /// 绑定模式和双美元符号相关(与cad保持相同的默认)
    + /// 为绑定模式,产生双美元; + /// 为插入模式,块重名会以本图覆盖; + ///
    + public bool BindOrInsert = false; + /// + /// bind时候是否拆离参照
    + /// 默认:学官方的绑定后自动拆离 + ///
    + public bool AutoDetach = true; + /// + /// bind时候是否删除被卸载的嵌套参照
    + /// 默认 + ///
    + public bool EraseNested = true; + /// + /// bind时候控制绑定的符号表:请保持默认
    + /// 目前仅推荐用于
    + /// 其他项有异常:
    + ///
    + public SymModes SymModesBind = SymModes.LayerTable; + #endregion + + #region 构造 + /// + /// 参照工厂 + /// + /// + /// 要处理的参照名称,就处理所有 + public XrefFactory(DBTrans tr, HashSet? xrefNames = null) + { + _tr = tr; + _xrefNames = xrefNames; + } + #endregion + + #region 重写 + public void Bind() + { + // 此功能有绑定出错的问题 + // db.BindXrefs(xrefIds, true); + + // 绑定后会自动拆离 + // 此功能修补了上面缺失 + DoubleBind(); + } + + public void Detach() + { + var xrefIds = GetAllXrefNode(); + foreach (ObjectId id in xrefIds) + _tr.Database.DetachXref(id); + } + + public void Reload() + { + var xrefIds = GetAllXrefNode(); + if (xrefIds.Count > 0) + _tr.Database.ReloadXrefs(xrefIds); + } + + public void Unload() + { + var xrefIds = GetAllXrefNode(); + if (xrefIds.Count > 0) + _tr.Database.UnloadXrefs(xrefIds); + } + #endregion + + #region 双重绑定 + /// + /// 获取参照 + /// + /// 全部参照id + ObjectIdCollection GetAllXrefNode() + { + // 储存要处理的参照id + var xrefIds = new ObjectIdCollection(); + XrefNodeForEach((xNodeName, xNodeId, xNodeStatus, xNodeIsNested) => { + if (XrefNamesContains(xNodeName)) + xrefIds.Add(xNodeId); + }); + return xrefIds; + } + + bool XrefNamesContains(string xNodeName) + { + // 为空的时候全部加入 || 有内容时候含有目标 + return _xrefNames is null || _xrefNames.Contains(xNodeName); + } + + /// + /// 遍历参照 + /// + /// (参照名,参照块表记录id,参照状态,是否嵌入) + void XrefNodeForEach(Action action) + { + // btRec.IsFromOverlayReference 是覆盖 + // btRec.GetXrefDatabase(true) 外部参照数据库 + + if (action == null) + return; + + // 解析外部参照:此功能不能锁定文档 + _tr.Database.ResolveXrefs(UseThreadEngine, DoNewOnly); + + var xg = _tr.Database.GetHostDwgXrefGraph(IncludeGhosts); + for (int i = 0; i < xg.NumNodes; i++) + { + var xNode = xg.GetXrefNode(i); + if (!xNode.BlockTableRecordId.IsOk()) + continue; + + action.Invoke(xNode.Name, + xNode.BlockTableRecordId, + xNode.XrefStatus, + xNode.IsNested); + } + } + + /// + /// 符号表记录加入容器 + /// + static void AddedxbindIds(ObjectIdCollection xbindXrefsIds, + SymbolTable symbolTable) + where TTable : SymbolTable + where TRecord : SymbolTableRecord, new() + { + symbolTable.ForEach(tabRec => { + if (tabRec.IsResolved) + xbindXrefsIds.Add(tabRec.ObjectId); + }, checkIdOk: true); + } + + ObjectIdCollection GetXBindIds() + { + // xbind + // 0x01 它是用来绑其他符号表,绑块表会有异常 + // 0x02 集合若有问题,就会出现eWrongObjectType + var xbindIds = new ObjectIdCollection(); + + // 起初测试是将九大符号表记录均加入的,但经实测不行...(为什么?存疑) + #region Option1 + if ((SymModesBind & SymModes.LayerTable) == SymModes.LayerTable) + AddedxbindIds(xbindIds, _tr.LayerTable); + + if ((SymModesBind & SymModes.TextStyleTable) == SymModes.TextStyleTable) + AddedxbindIds(xbindIds, _tr.TextStyleTable); + + if ((SymModesBind & SymModes.RegAppTable) == SymModes.RegAppTable) + AddedxbindIds(xbindIds, _tr.RegAppTable); + + if ((SymModesBind & SymModes.DimStyleTable) == SymModes.DimStyleTable) + AddedxbindIds(xbindIds, _tr.DimStyleTable); + + if ((SymModesBind & SymModes.LinetypeTable) == SymModes.LinetypeTable) + AddedxbindIds(xbindIds, _tr.LinetypeTable); + #endregion + + #region Option2 + if ((SymModesBind & SymModes.UcsTable) == SymModes.UcsTable) + AddedxbindIds(xbindIds, _tr.UcsTable); + + if ((SymModesBind & SymModes.ViewTable) == SymModes.ViewTable) + AddedxbindIds(xbindIds, _tr.ViewTable); + + if ((SymModesBind & SymModes.ViewportTable) == SymModes.ViewportTable) + AddedxbindIds(xbindIds, _tr.ViewportTable); + #endregion + + return xbindIds; + } + + ObjectIdCollection GetBindIds() + { + // bind 只绑块表 + var bindIds = new ObjectIdCollection(); + + _tr.BlockTable.ForEach(btr => { + if (btr.IsLayout) + return; + + // 外部参照 && 已融入 + if (btr.IsFromExternalReference && btr.IsResolved) + bindIds.Add(btr.ObjectId); + }, checkIdOk: true); + + return bindIds; + } + + /// + /// 获取可以拆离的ids + /// + /// 返回已卸载中含有嵌套的参照,要重载之后才能绑定 + /// 返回未参照中嵌套的参照,直接拆离 + List GetDetachIds(Dictionary nested) + { + // 直接拆离的id + List detachIds = new(); + + // 收集要处理的id + XrefNodeForEach((xNodeName, xNodeId, xNodeStatus, xNodeIsNested) => { + switch (xNodeStatus) + { + case XrefStatus.Unresolved:// 未融入_ResolveXrefs参数2 + break; + case XrefStatus.FileNotFound:// 未融入(未解析)_未找到文件 + break; + case XrefStatus.Unreferenced:// 未参照 + { + // 为空的时候全部加入 || 有内容时候含有目标 + if (XrefNamesContains(xNodeName)) + detachIds.Add(xNodeId); + } + break; + case XrefStatus.Unloaded:// 已卸载 + { + // 为空的时候全部加入 || 有内容时候含有目标 + if (XrefNamesContains(xNodeName)) + { + var btr = _tr.GetObject(xNodeId); + if (btr != null && btr.IsFromExternalReference) + { + if (!xNodeIsNested) + detachIds.Add(xNodeId); + else if (!nested.ContainsKey(xNodeId)) + nested.Add(xNodeId, xNodeName);// 嵌套参照 + } + } + } + break; + case XrefStatus.Resolved:// 已融入_就是可以绑定的 + break; + case XrefStatus.NotAnXref:// 不是外部参照 + break; + default: + break; + } + }); + return detachIds; + } + + /// + /// 双重绑定参照 + /// 参考链接 + /// + void DoubleBind() + { + Dictionary nested = new(); + var detachIds = GetDetachIds(nested); + + // 拆离:未参照的文件 + if (AutoDetach) + { + for (int i = 0; i < detachIds.Count; i++) + _tr.Database.DetachXref(detachIds[i]); + } + // 重载:嵌套参照已卸载了,需要重载之后才能进行绑定 + var keys = nested.Keys; + if (keys.Count > 0) + _tr.Database.ReloadXrefs(new ObjectIdCollection(keys.ToArray())); + + // 绑定:切勿交换,否则会绑定无效 + var bindIds = GetBindIds(); + var xbindIds = GetXBindIds(); + if (xbindIds.Count > 0) + _tr.Database.XBindXrefs(xbindIds, BindOrInsert); + if (bindIds.Count > 0) + _tr.Database.BindXrefs(bindIds, BindOrInsert); + + + // 内部删除嵌套参照的块操作 + if (EraseNested) + { +#if ac2008 + // 因为Acad08索引器存在会暴露isErase的(桌子底层的原因), + // 也就是可能获取两个名称一样的,只能用遍历的方式进行 + HashSet nestedHash = new(); + foreach (var item in nested) + nestedHash.Add(item.Value); + + // 遍历全图,找到参照名称一样的删除 + _tr.BlockTable.ForEach(btr => { + if (btr.IsLayout) + return; + if (nestedHash.Contains(btr.Name)) + { + btr.UpgradeOpen(); + btr.Erase(); + btr.DowngradeOpen(); + btr.Dispose(); + } + }, checkIdOk: true); +#else + foreach (var item in nested) + { + var name = item.Value; + if (_tr.BlockTable.Has(name)) + _tr.GetObject(_tr.BlockTable[name], OpenMode.ForWrite)? + .Erase(); + } +#endif + } + } + #endregion +} + + + +public static class XrefEx +{ + /// + /// 外部参照工厂 + /// + /// + /// 处理参照的枚举 + /// 要处理的参照名称,就处理所有 + public static void XrefFactory(this DBTrans tr, + XrefModes xrefModes, + HashSet? xrefNames = null) + { + var xf = new XrefFactory(tr, xrefNames); + tr.Task(() => { + switch (xrefModes) + { + case XrefModes.Unload: + xf.Unload(); + break; + case XrefModes.Reload: + xf.Reload(); + break; + case XrefModes.Detach: + xf.Detach(); + break; + case XrefModes.Bind: + xf.Bind(); + break; + default: + break; + } + }); + } +} + +#endregion + +#region 参照路径工具类 +/// +/// 获取外部参照的路径 +/// +public class XrefPath +{ + #region 属性 + /// + /// 基础路径 + /// + public string CurrentDatabasePath; + /// + /// 是否外部参照 + /// + public bool IsFromExternalReference { get; private set; } + /// + /// 外部参照保存的路径 + /// + /// 它们会是以下任一路径:
    + /// 0x01 相对路径
    + /// 0x02 绝对路径
    + /// 0x03 共目录优先找到的路径(文件夹整体移动会发生此类情况) + ///
    + ///
    + public string? PathSave { get; private set; } + /// + /// 找到的路径(参照面板的名称) + /// 路径不存在时,返回是外部参照dwg文件路径 + /// + public string? PathDescribe { get; private set; } + + string? _PathComplete; + /// + /// 绝对路径 + /// + public string? PathComplete => _PathComplete ??= + PathConverter(CurrentDatabasePath, PathDescribe, PathConverterModes.Complete); + + string? _PathRelative; + /// + /// 相对路径 + /// + public string? PathRelative => _PathRelative ??= + PathConverter(CurrentDatabasePath, PathComplete, PathConverterModes.Relative); + #endregion + + #region 构造 + /// + /// 获取外部参照的路径 + /// + /// 外部参照图元 + /// 事务 + /// 是否外部参照 + public XrefPath(BlockReference brf, DBTrans tr) + { + if (brf == null) + throw new ArgumentNullException(nameof(brf)); + + CurrentDatabasePath = Path.GetDirectoryName(tr.Database.Filename); + + var btRec = tr.GetObject(brf.BlockTableRecord);// 块表记录 + if (btRec == null) + return; + + IsFromExternalReference = btRec.IsFromExternalReference; + if (!IsFromExternalReference) + return; + + // 相对路径==".\\AA.dwg" + // 无路径=="AA.dwg" + PathSave = btRec.PathName; + + if ((!string.IsNullOrEmpty(PathSave) && PathSave[0] == '.') || File.Exists(PathSave)) + { + // 相对路径||绝对路径 + PathDescribe = PathSave; + } + else + { + // 无路径 + var db = btRec.GetXrefDatabase(true); + PathDescribe = db.Filename; + } + } + #endregion + + #region 静态函数 + /// + /// 获取相对路径或者绝对路径 + /// 参考链接 + /// + /// 基础目录(末尾无斜杠) + /// 相对路径或者绝对路径 + /// 依照枚举返回对应的字符串 + /// + public static string? PathConverter(string? directory, string? fileRelations, PathConverterModes converterModes) + { + if (directory == null) + throw new ArgumentNullException(nameof(directory)); + if (fileRelations == null) + throw new ArgumentNullException(nameof(fileRelations)); + + string? result = null; + switch (converterModes) + { + case PathConverterModes.Relative: + result = GetRelativePath(directory, fileRelations); + break; + case PathConverterModes.Complete: + result = GetCompletePath(directory, fileRelations); + break; + default: + break; + } + return result; + } + +#if error_demo + /// + /// 绝对路径->相对路径 + /// + /// 绝对路径 + /// 相对关系 + /// + /// StringHelper.GetRelativePath("G:\\A1.项目\\20190920金山谷黄宅\\01.饰施图\\03.平面图", + /// "G:\\A1.项目\\20190920金山谷黄宅\\01.饰施图\\01.辅助文件\\图框\\A3图框.dwg"); + public static string GetRelativePath(string strDbPath, string strXrefPath) + { + Uri uri1 = new(strXrefPath); + Uri uri2 = new(strDbPath); + Uri relativeUri = uri2.MakeRelativeUri(uri1); + // 测试例子变成 01.%E8%BE%85%E5%8A%A9%E6%96%87%E4%BB%B6/%E5%9B%BE%E6%A1%86/A3%E5%9B%BE%E6%A1%86.dwg + string str = relativeUri.ToString(); + + // 因为这里不会实现".\A.dwg"而是"A.dwg",所以加入这个操作,满足同目录文件 + var strs = str.Split('\\'); + if (strs.Length == 1) + str = ".\\" + str; + return str; + } +#else + /// + /// 绝对路径->相对路径 + /// + /// 相对关系:文件夹路径 + /// 完整路径:文件路径 + /// 相对路径 + /// "..\\01.辅助文件\\图框\\A3图框.dwg" + /// ]]> + static string GetRelativePath(string directory, string file) + { + string[] directorys = directory.Split('\\'); + string[] files = file.Split('\\'); + // 获取两条路径中的最短路径 + int getMinLength = directorys.Length < files.Length ? directorys.Length : files.Length; + + // 用于确定我们退出的循环中的位置。 + int lastCommonRoot = -1; + int index; + // 找到共根 + for (index = 0; index < getMinLength; index++) + { + if (directorys[index] != files[index]) + break; + lastCommonRoot = index; + } + // 如果我们没有找到一个共同的前缀,那么抛出 + if (lastCommonRoot == -1) + throw new ArgumentException("路径没有公共相同路径部分"); + + // 建立相对路径 + var result = new StringBuilder(); + for (index = lastCommonRoot + 1; index < directorys.Length; index++) + if (directorys[index].Length > 0) + result.Append("..\\");// 上级目录加入 + + // 添加文件夹 + for (index = lastCommonRoot + 1; index < files.Length - 1; index++) + result.Append(files[index] + "\\"); + + // 本级目录 + if (result.Length == 0) + result.Append(".\\"); + // result.Append(strXrefPaths[^1]);// 下级目录加入 + result.Append(files[files.Length - 1]);// 下级目录加入 + return result.ToString(); + } +#endif + + /// + /// 相对路径->绝对路径 + /// + /// 文件夹路径 + /// 相对关系:有..的 + /// 完整路径 + /// "G:\\A1.项目\\20190920金山谷黄宅\\01.饰施图\\01.辅助文件\\图框\\A3图框.dwg" + /// ]]> + static string? GetCompletePath(string directory, string relativePath) + { + if (relativePath is null || relativePath.Trim() == string.Empty) + return null; + + var relativeName = Path.GetDirectoryName(relativePath); + if (relativeName is null) + return null; + + if (relativePath[0] != '.') + return relativePath; + + const char slash = '\\'; + + // 判断向上删除几个 + var path_xiangduis = relativeName.Split(slash); + int index = 0; + for (int i = 0; i < path_xiangduis.Length; i++) + { + if (path_xiangduis[i] != "..") + break; + index++; + } + + var result = new StringBuilder(); + // 前段 + var path_dwgs = directory.Split(slash); + path_dwgs = path_dwgs.Where(s => !string.IsNullOrEmpty(s)).ToArray();// 清理空数组 + for (int i = 0; i < path_dwgs.Length - index; i++) + { + result.Append(path_dwgs[i]); + result.Append(slash); + } + // 后段 + for (int i = 0; i < path_xiangduis.Length; i++) + { + var item = path_xiangduis[i]; + if (item != "." && item != "..") + { + result.Append(item); + result.Append(slash); + } + } + result.Append(Path.GetFileName(relativePath)); + return result.ToString(); + } + #endregion +} +#endregion \ No newline at end of file diff --git "a/src/IFoxCAD.Cad/ExtensionMethod/\346\226\260\345\273\272\345\241\253\345\205\205/HatchConverter.cs" "b/src/IFoxCAD.Cad/ExtensionMethod/\346\226\260\345\273\272\345\241\253\345\205\205/HatchConverter.cs" index 7868bee..09af1d6 100644 --- "a/src/IFoxCAD.Cad/ExtensionMethod/\346\226\260\345\273\272\345\241\253\345\205\205/HatchConverter.cs" +++ "b/src/IFoxCAD.Cad/ExtensionMethod/\346\226\260\345\273\272\345\241\253\345\205\205/HatchConverter.cs" @@ -88,23 +88,23 @@ public HatchConverter(Hatch hatch) : this() { _oldHatch = hatch; - //不能在提取信息的时候进行新建cad图元, - //否则cad将会提示遗忘释放 + // 不能在提取信息的时候进行新建cad图元, + // 否则cad将会提示遗忘释放 hatch.ForEach(loop => { var hcData = new HatchConverterData(); bool isCurve2d = true; if (loop.IsPolyline) { - //边界是多段线 + // 边界是多段线 HatchLoopIsPolyline(loop, hcData); isCurve2d = false; } else { - if (loop.Curves.Count == 2)//1是不可能的,大于2的是曲线 + if (loop.Curves.Count == 2)// 1是不可能的,大于2的是曲线 { - //边界是曲线,过滤可能是圆形的情况 + // 边界是曲线,过滤可能是圆形的情况 var cir = TwoArcFormOneCircle(loop); if (cir is not null) { @@ -114,7 +114,7 @@ public HatchConverter(Hatch hatch) : this() } } - //边界是曲线 + // 边界是曲线 if (isCurve2d) HatchLoopIsCurve2d(loop, hcData); @@ -137,9 +137,9 @@ static void HatchLoopIsPolyline(HatchLoop loop, HatchConverterData hcData) if (hcData is null) throw new ArgumentNullException(nameof(hcData)); - //判断为圆形: - //上下两个圆弧,然后填充,就会生成此种填充 - //顶点数是3,凸度是半圆,两个半圆就是一个圆形 + // 判断为圆形: + // 上下两个圆弧,然后填充,就会生成此种填充 + // 顶点数是3,凸度是半圆,两个半圆就是一个圆形 if (loop.Polyline.Count == 3 && loop.Polyline[0].Bulge == 1 && loop.Polyline[1].Bulge == 1 || loop.Polyline.Count == 3 && loop.Polyline[0].Bulge == -1 && loop.Polyline[1].Bulge == -1) { @@ -147,7 +147,7 @@ static void HatchLoopIsPolyline(HatchLoop loop, HatchConverterData hcData) } else { - //遍历多段线信息 + // 遍历多段线信息 var bvc = loop.Polyline; for (int i = 0; i < bvc.Count; i++) hcData.PolyLineData.Add(new BulgeVertexWidth(bvc[i])); @@ -170,23 +170,23 @@ static void HatchLoopIsPolyline(HatchLoop loop, HatchConverterData hcData) CircleData? circular = null; - //判断为圆形: - //用一条(不是两条)多段线画出两条圆弧为正圆,就会生成此种填充 - //边界为曲线,数量为2,可能是两个半圆曲线,如果是,就加入圆形数据中 + // 判断为圆形: + // 用一条(不是两条)多段线画出两条圆弧为正圆,就会生成此种填充 + // 边界为曲线,数量为2,可能是两个半圆曲线,如果是,就加入圆形数据中 - //第一段 - var getCurves1Pts = loop.Curves[0].GetSamplePoints(3); //曲线取样点分两份(3点) - var mid1Pt = getCurves1Pts[1]; //腰点 + // 第一段 + var getCurves1Pts = loop.Curves[0].GetSamplePoints(3); // 曲线取样点分两份(3点) + var mid1Pt = getCurves1Pts[1]; // 腰点 double bulge1 = loop.Curves[0].StartPoint.GetArcBulge(mid1Pt, loop.Curves[0].EndPoint); - //第二段 + // 第二段 var getCurves2Pts = loop.Curves[1].GetSamplePoints(3); var mid2Pt = getCurves2Pts[1]; double bulge2 = loop.Curves[1].StartPoint.GetArcBulge(mid2Pt, loop.Curves[1].EndPoint); - //第一段上弧&&第二段反弧 || 第一段反弧&&第二段上弧 + // 第一段上弧&&第二段反弧 || 第一段反弧&&第二段上弧 if (bulge1 == -1 && bulge2 == -1 || bulge1 == 1 && bulge2 == 1) - circular = new CircleData(loop.Curves[0].StartPoint, loop.Curves[1].StartPoint); //两个起点就是对称点 + circular = new CircleData(loop.Curves[0].StartPoint, loop.Curves[1].StartPoint); // 两个起点就是对称点 return circular; } @@ -198,36 +198,36 @@ static void HatchLoopIsPolyline(HatchLoop loop, HatchConverterData hcData) /// 收集图元信息 static void HatchLoopIsCurve2d(HatchLoop loop, HatchConverterData hcData) { - //取每一段曲线,曲线可能是直线来的,但是圆弧会按照顶点来分段 + // 取每一段曲线,曲线可能是直线来的,但是圆弧会按照顶点来分段 int curveIsClosed = 0; - //遍历边界的多个子段 + // 遍历边界的多个子段 foreach (Curve2d curve in loop.Curves) { - //计数用于实现闭合 + // 计数用于实现闭合 curveIsClosed++; if (curve is NurbCurve2d spl) { - //判断为样条曲线: + // 判断为样条曲线: hcData.SplineData.Add(spl); continue; } var pts = curve.GetSamplePoints(3); var midPt = pts[1]; - if (curve.StartPoint.IsEqualTo(curve.EndPoint, new Tolerance(1e-6, 1e-6)))//首尾相同,就是圆形 + if (curve.StartPoint.IsEqualTo(curve.EndPoint, new Tolerance(1e-6, 1e-6)))// 首尾相同,就是圆形 { - //判断为圆形: - //获取起点,然后采样三点,中间就是对称点(直径点) + // 判断为圆形: + // 获取起点,然后采样三点,中间就是对称点(直径点) hcData.CircleData.Add(new CircleData(curve.StartPoint, midPt)); continue; } - //判断为多段线,圆弧: + // 判断为多段线,圆弧: double bulge = curve.StartPoint.GetArcBulge(midPt, curve.EndPoint); hcData.PolyLineData.Add(new BulgeVertexWidth(curve.StartPoint, bulge)); - //末尾点,不闭合的情况下就要获取这个 + // 末尾点,不闭合的情况下就要获取这个 if (curveIsClosed == loop.Curves.Count) hcData.PolyLineData.Add(new BulgeVertexWidth(curve.EndPoint, 0)); } @@ -243,7 +243,7 @@ public void CreateBoundaryEntitys(List outEnts) { var data = _hcDatas[i]; - //生成边界:多段线 + // 生成边界:多段线 if (data.PolyLineData.Count > 0) { Polyline pl = new(); @@ -259,12 +259,12 @@ public void CreateBoundaryEntitys(List outEnts) outEnts.Add(pl); } - //生成边界:圆 + // 生成边界:圆 data.CircleData.ForEach(item => { outEnts.Add(new Circle(item.Center.Point3d(), Vector3d.ZAxis, item.Radius)); }); - //生成边界:样条曲线 + // 生成边界:样条曲线 data.SplineData.ForEach(item => { outEnts.Add(item.ToCurve()); }); @@ -293,7 +293,7 @@ public ObjectId CreateBoundarysAndHatchToMsPs(BlockTableRecord btrOfAddEntitySpa bool createHatchFlag = true, Transaction? trans = null) { - //重设边界之前肯定是有边界才可以 + // 重设边界之前肯定是有边界才可以 if (BoundaryIds.Count == 0) { List boundaryEntitys = new(); @@ -334,7 +334,7 @@ public ObjectId CreateBoundarysAndHatchToMsPs(BlockTableRecord btrOfAddEntitySpa void ResetBoundary(Hatch hatch, bool boundaryAssociative = true) { - //删除原有边界 + // 删除原有边界 while (hatch.NumberOfLoops != 0) hatch.RemoveLoopAt(0); @@ -345,13 +345,13 @@ void ResetBoundary(Hatch hatch, { obIds.Clear(); obIds.Add(BoundaryIds[i]); - //要先添加最外面的边界 + // 要先添加最外面的边界 if (i == 0) hatch.AppendLoop(HatchLoopTypes.Outermost, obIds); else hatch.AppendLoop(HatchLoopTypes.Default, obIds); } - //计算填充并显示 + // 计算填充并显示 hatch.EvaluateHatch(true); } #endregion diff --git "a/src/IFoxCAD.Cad/ExtensionMethod/\346\226\260\345\273\272\345\241\253\345\205\205/HatchInfo.cs" "b/src/IFoxCAD.Cad/ExtensionMethod/\346\226\260\345\273\272\345\241\253\345\205\205/HatchInfo.cs" index 1226479..d753460 100644 --- "a/src/IFoxCAD.Cad/ExtensionMethod/\346\226\260\345\273\272\345\241\253\345\205\205/HatchInfo.cs" +++ "b/src/IFoxCAD.Cad/ExtensionMethod/\346\226\260\345\273\272\345\241\253\345\205\205/HatchInfo.cs" @@ -1,56 +1,56 @@ -namespace IFoxCAD.Cad; +namespace IFoxCAD.Cad; /* - * ӵĵһ߽߽,ڶͼı߽硣 - * Ҫⲿ߽,ʹӻΪ HatchLoopTypes.Outermost AppendLoop , - * һ߽类,ͿԼı߽硣 - * ڲ߽ʹô HatchLoopTypes.Default AppendLoop + * 添加的第一个边界必须是外边界,即用于定义图案填充最外面的边界。 + * 要添加外部边界,请使用添加环的类型为 HatchLoopTypes.Outermost 常量的 AppendLoop 方法, + * 一旦外边界被定义,就可以继续添加另外的边界。 + * 添加内部边界请使用带 HatchLoopTypes.Default 常量的 AppendLoop 方法。 * - * ߽ʱ,ӵ(߽,߽,߽,߽ͨ....) - * ߽ʱ,ӵ(߽,߽ͨ.....߽,߽ͨ....) + * 多个外边界的时候,添加的是(外边界,外边界,外边界,普通边界....) + * 多个外边界的时候,添加的是(外边界,普通边界.....外边界,普通边界....) */ /// -/// ͼ +/// 图案填充 /// public class HatchInfo { - #region Ա + #region 成员 /// - /// ߽id(ŵһ) + /// 边界id(最外面放第一) /// readonly List _boundaryIds; /// - /// ͼԪ + /// 填充图元 /// readonly Hatch _hatch; /// - /// ߽(˴ֱ=>Ա,Ϊ뷴Ӧ) + /// 边界关联(此处不能直接=>给填充成员,因为它会加入反应器) /// readonly bool _boundaryAssociative; /// - /// :û(̶)//ݶļ + /// 填充的名称:用户定义(固定名称)/渐变/填充依据定义文件 /// string? _hatchName; /// - /// ģʽ(Ԥ/û/Զ) + /// 填充模式类型(预定义/用户定义/自定义) /// HatchPatternType _patternTypeHatch; /// - /// ģʽ + /// 渐变模式类型 /// GradientPatternType _patternTypeGradient; /// - /// / + /// 比例/间距 /// double Scale => _hatch.PatternScale; /// - /// Ƕ + /// 角度 /// double Angle => _hatch.PatternAngle; #endregion - #region + #region 构造 HatchInfo() { _hatch = new Hatch(); @@ -59,36 +59,36 @@ public class HatchInfo } /// - /// ͼ + /// 图案填充 /// - /// ߽ - /// ԭ - /// - /// Ƕ + /// 关联边界 + /// 填充原点 + /// 比例 + /// 角度 public HatchInfo(bool boundaryAssociative = true, Point2d? hatchOrigin = null, double hatchScale = 1, double hatchAngle = 0) : this() { if (hatchScale <= 0) - throw new ArgumentException("Сڵ0"); + throw new ArgumentException("填充比例不允许小于等于0"); - _hatch.PatternScale = hatchScale;// - _hatch.PatternAngle = hatchAngle;//Ƕ + _hatch.PatternScale = hatchScale;// 填充比例 + _hatch.PatternAngle = hatchAngle;// 填充角度 _boundaryAssociative = boundaryAssociative; hatchOrigin ??= Point2d.Origin; - _hatch.Origin = hatchOrigin.Value; //ԭ + _hatch.Origin = hatchOrigin.Value; // 填充原点 } /// - /// ͼ + /// 图案填充 /// - /// ߽ - /// ߽ - /// ԭ - /// - /// Ƕ + /// 边界 + /// 关联边界 + /// 填充原点 + /// 比例 + /// 角度 public HatchInfo(IEnumerable boundaryIds, bool boundaryAssociative = true, Point2d? hatchOrigin = null, @@ -101,111 +101,111 @@ public HatchInfo(IEnumerable boundaryIds, #endregion - #region + #region 方法 /// - /// ģʽ1:Ԥ + /// 模式1:预定义 /// public HatchInfo Mode1PreDefined(string name) { _hatchName = name; - _hatch.HatchObjectType = HatchObjectType.HatchObject; //(/) + _hatch.HatchObjectType = HatchObjectType.HatchObject; // 对象类型(填充/渐变) _patternTypeHatch = HatchPatternType.PreDefined; return this; } /// - /// ģʽ2:û + /// 模式2:用户定义 /// - /// Ƿ˫ + /// 是否双向 public HatchInfo Mode2UserDefined(bool patternDouble = true) { _hatchName = "_USER"; - _hatch.HatchObjectType = HatchObjectType.HatchObject; //(/) + _hatch.HatchObjectType = HatchObjectType.HatchObject; // 对象类型(填充/渐变) _patternTypeHatch = HatchPatternType.UserDefined; - _hatch.PatternDouble = patternDouble; //Ƿ˫򣨱д SetHatchPattern ֮ǰ - _hatch.PatternSpace = Scale; //ࣨд SetHatchPattern ֮ǰ + _hatch.PatternDouble = patternDouble; // 是否双向(必须写在 SetHatchPattern 之前) + _hatch.PatternSpace = Scale; // 间距(必须写在 SetHatchPattern 之前) return this; } /// - /// ģʽ3:Զ + /// 模式3:自定义 /// /// public HatchInfo Mode3UserDefined(string name) { _hatchName = name; - _hatch.HatchObjectType = HatchObjectType.HatchObject; //(/) + _hatch.HatchObjectType = HatchObjectType.HatchObject; // 对象类型(填充/渐变) _patternTypeHatch = HatchPatternType.CustomDefined; return this; } /// - /// ģʽ4: + /// 模式4:渐变填充 /// - /// - /// ɫʼɫ - /// ɫɫ - /// ƶ - /// ɫֵ - /// ɫ˫ɫ + /// 渐变填充名称 + /// 渐变色起始颜色 + /// 渐变色结束颜色 + /// 渐变移动 + /// 色调值 + /// 单色双色 public HatchInfo Mode4Gradient(GradientName name, Color colorStart, Color colorEnd, float gradientShift = 0, float shadeTintValue = 0, bool gradientOneColorMode = false) { - //entgetֱȻ"SOLID",Ϊ"","" + // entget渐变的名字必然是"SOLID",但是这里作为"渐变"名,而不是"填充"名 _hatchName = name.ToString(); - _hatch.HatchObjectType = HatchObjectType.GradientObject; //(/) - _patternTypeGradient = GradientPatternType.PreDefinedGradient;//ģʽ4: - //_patternTypeGradient = GradientPatternType.UserDefinedGradient;//ģʽ5:..ģʽɶ + _hatch.HatchObjectType = HatchObjectType.GradientObject; // 对象类型(填充/渐变) + _patternTypeGradient = GradientPatternType.PreDefinedGradient;// 模式4:渐变 + // _patternTypeGradient = GradientPatternType.UserDefinedGradient;// 模式5:渐变..这种模式干啥用呢 - //ýɫʼͽɫ + // 设置渐变色填充的起始和结束颜色 var gColor1 = new GradientColor(colorStart, 0); var gColor2 = new GradientColor(colorEnd, 1); _hatch.SetGradientColors(new GradientColor[] { gColor1, gColor2 }); - _hatch.GradientShift = gradientShift; //ݶλ - _hatch.ShadeTintValue = shadeTintValue; //Ӱɫֵ - _hatch.GradientOneColorMode = gradientOneColorMode;//䵥ɫ/˫ɫ - _hatch.GradientAngle = Angle; //Ƕ + _hatch.GradientShift = gradientShift; // 梯度位移 + _hatch.ShadeTintValue = shadeTintValue; // 阴影色值 + _hatch.GradientOneColorMode = gradientOneColorMode;// 渐变单色/双色 + _hatch.GradientAngle = Angle; // 渐变角度 return this; } /// - /// + /// 构建 /// - /// ˿ռ + /// 将填充加入此空间 public ObjectId Build(BlockTableRecord btrOfAddEntitySpace) { - //ݿ + // 加入数据库 var hatchId = btrOfAddEntitySpace.AddEntity(_hatch); - //ģʽ:/ + // 设置模式:渐变/填充 if (_hatch.HatchObjectType == HatchObjectType.GradientObject) _hatch.SetGradient(_patternTypeGradient, _hatchName); else _hatch.SetHatchPattern(_patternTypeHatch, _hatchName); - //߽,ݿռھͻ - //Ϊ true 뷴Ӧ,˱Ƚ(ά뽫ʮɺ),. + // 关联边界,如果不先添加数据库空间内就会出错 + // 为 true 会加入反应器,因此比较慢(二维码将会十几秒才生成好),视需求而定. _hatch.Associative = _boundaryAssociative; - // AppendLoop ؼ,Ͳ + // 利用 AppendLoop 重载加入,这里就不处理 if (_boundaryIds.Count > 0) AppendLoop(_boundaryIds, HatchLoopTypes.Default); - //䲢ʾ(߽,쳣) + // 计算填充并显示(若边界出错,这句会异常) _hatch.EvaluateHatch(true); return hatchId; } /// - /// ִͼԪ޸ + /// 执行图元的属性修改 /// - /// ӳʵ + /// 扔出填充实体 public HatchInfo Action(Action action) { action(_hatch); @@ -213,7 +213,7 @@ public HatchInfo Action(Action action) } /// - /// ձ߽缯 + /// 清空边界集合 /// public HatchInfo ClearBoundary() { @@ -222,7 +222,7 @@ public HatchInfo ClearBoundary() } /// - /// ɾ߽ͼԪ + /// 删除边界图元 /// public HatchInfo EraseBoundary() { @@ -232,16 +232,16 @@ public HatchInfo EraseBoundary() } /// - /// ߽ + /// 加入边界 /// - /// ߽id - /// 뷽ʽ + /// 边界id + /// 加入方式 void AppendLoop(IEnumerable boundaryIds, HatchLoopTypes hatchLoopTypes = HatchLoopTypes.Default) { var obIds = new ObjectIdCollection(); - //߽DZպϵ,Ѿݿ - //պϻ. + // 边界是闭合的,而且已经加入数据库 + // 填充闭合环类型.最外面 foreach (var border in boundaryIds) { obIds.Clear(); @@ -252,12 +252,12 @@ void AppendLoop(IEnumerable boundaryIds, } /// - /// ߽(¸߰汾亯) + /// 加入边界(仿高版本的填充函数) /// - /// 㼯 - /// ͹ȼ - /// ˿ռ - /// 뷽ʽ + /// 点集 + /// 凸度集 + /// 加入此空间 + /// 加入方式 /// public HatchInfo AppendLoop(Point2dCollection pts, DoubleCollection bluges, @@ -271,8 +271,8 @@ public HatchInfo AppendLoop(Point2dCollection pts, #if NET35 _boundaryIds.Add(CreateAddBoundary(ptsEnd2End, bluges, btrOfAddEntitySpace)); #else - //2011API,ԲͼԪ¼߽, - //ͨĻ,߽ _boundaryIds ǿյ,ô Build() ʱҪ˿յ + // 2011新增API,可以不生成图元的情况下加入边界, + // 通过这里进入的话,边界 _boundaryIds 是空的,那么 Build() 时候就需要过滤空的 _hatch.AppendLoop(hatchLoopTypes, ptsEnd2End, bluges); #endif return this; @@ -280,12 +280,12 @@ public HatchInfo AppendLoop(Point2dCollection pts, #if NET35 /// - /// ͨ㼯͹ɱ߽Ķ + /// 通过点集和凸度生成边界的多段线 /// - /// 㼯 - /// ͹ȼ - /// ˿ռ - /// id + /// 点集 + /// 凸度集 + /// 加入此空间 + /// 多段线id static ObjectId CreateAddBoundary(Point2dCollection? pts, DoubleCollection? bluges, BlockTableRecord btrOfAddEntitySpace) @@ -307,46 +307,46 @@ static ObjectId CreateAddBoundary(Point2dCollection? pts, #endif #endregion - #region ö + #region 枚举 /// - /// ɫͼ + /// 渐变色填充的图案名称 /// public enum GradientName { /// - /// ״ + /// 线状渐变 /// Linear, /// - /// Բ״ + /// 圆柱状渐变 /// Cylinder, /// - /// Բ״ + /// 反圆柱状渐变 /// Invcylinder, /// - /// ״ + /// 球状渐变 /// Spherical, /// - /// ״ + /// 反球状渐变 /// Invspherical, /// - /// ״ + /// 半球状渐变 /// Hemisperical, /// - /// ״ + /// 反半球状渐变 /// InvHemisperical, /// - /// ״ + /// 抛物面状渐变 /// Curved, /// - /// ״ + /// 反抛物面状渐变 /// Incurved } diff --git "a/src/IFoxCAD.Cad/ExtensionMethod/\346\226\260\345\273\272\346\226\207\345\255\227/AttachmentPointHelper.cs" "b/src/IFoxCAD.Cad/ExtensionMethod/\346\226\260\345\273\272\346\226\207\345\255\227/AttachmentPointHelper.cs" index 989721c..e630a51 100644 --- "a/src/IFoxCAD.Cad/ExtensionMethod/\346\226\260\345\273\272\346\226\207\345\255\227/AttachmentPointHelper.cs" +++ "b/src/IFoxCAD.Cad/ExtensionMethod/\346\226\260\345\273\272\346\226\207\345\255\227/AttachmentPointHelper.cs" @@ -5,29 +5,29 @@ public static class AttachmentPointHelper static readonly Dictionary _alignment = new() { { "左上", AttachmentPoint.TopLeft }, - { "中上", AttachmentPoint.TopCenter },//单行的对齐 + { "中上", AttachmentPoint.TopCenter },// 单行的对齐 { "右上", AttachmentPoint.TopRight }, { "左中", AttachmentPoint.MiddleLeft }, - { "正中", AttachmentPoint.MiddleCenter },//多行的正中 + { "正中", AttachmentPoint.MiddleCenter },// 多行的正中 { "右中", AttachmentPoint.MiddleRight }, - { "左对齐", AttachmentPoint.BaseLeft },//※优先(放在前面优先获取) + { "左对齐", AttachmentPoint.BaseLeft },// ※优先(放在前面优先获取) { "左", AttachmentPoint.BaseLeft }, { "中间", AttachmentPoint.BaseMid }, - { "右对齐", AttachmentPoint.BaseRight },//※优先(放在前面优先获取) + { "右对齐", AttachmentPoint.BaseRight },// ※优先(放在前面优先获取) { "右", AttachmentPoint.BaseRight }, { "左下", AttachmentPoint.BottomLeft }, { "中下", AttachmentPoint.BottomCenter }, { "右下", AttachmentPoint.BottomRight }, - { "对齐", AttachmentPoint.BaseAlign },//※优先(放在前面优先获取) + { "对齐", AttachmentPoint.BaseAlign },// ※优先(放在前面优先获取) { "调整", AttachmentPoint.BaseAlign }, - { "居中", AttachmentPoint.BaseCenter },//单行的中 + { "居中", AttachmentPoint.BaseCenter },// 单行的中 { "铺满", AttachmentPoint.BaseFit }, }; @@ -53,16 +53,16 @@ public static string Get(AttachmentPoint value) } #if false -//反射描述 -//这些东西cad没有用到啊...所以不纳入了 +// 反射描述 +// 这些东西cad没有用到啊...所以不纳入了 public enum AttachmentPoint2 { [Description("下对齐")] BottomAlign = 14, [Description("中对齐")] - MiddleAlign = 15,//0xF + MiddleAlign = 15,// 0xF [Description("上对齐")] - TopAlign = 16,//0x10 + TopAlign = 16,// 0x10 [Description("下铺满")] BottomFit = 18, [Description("中铺满")] diff --git "a/src/IFoxCAD.Cad/ExtensionMethod/\346\226\260\345\273\272\346\226\207\345\255\227/TextInfo.cs" "b/src/IFoxCAD.Cad/ExtensionMethod/\346\226\260\345\273\272\346\226\207\345\255\227/TextInfo.cs" index 573963b..d8bfd7a 100644 --- "a/src/IFoxCAD.Cad/ExtensionMethod/\346\226\260\345\273\272\346\226\207\345\255\227/TextInfo.cs" +++ "b/src/IFoxCAD.Cad/ExtensionMethod/\346\226\260\345\273\272\346\226\207\345\255\227/TextInfo.cs" @@ -59,16 +59,16 @@ public DBText AddDBTextToEntity() acText.SetDatabaseDefaults(); if (Database is not null) - acText.SetDatabaseDefaults(Database);//我的默认值是填满的,所以可以不需要 + acText.SetDatabaseDefaults(Database);// 我的默认值是填满的,所以可以不需要 if (TextStyleId is not null) acText.SetTextStyleId(TextStyleId.Value); - acText.Height = TextHeight; //高度 - acText.TextString = Contents; //内容 - acText.Position = Position; //插入点(一定要先设置) - acText.Justify = TextJustify; //使他们对齐 - //acText.HorizontalMode + acText.Height = TextHeight; // 高度 + acText.TextString = Contents; // 内容 + acText.Position = Position; // 插入点(一定要先设置) + acText.Justify = TextJustify; // 使他们对齐 + // acText.HorizontalMode if (AlignmentPoint is not null) acText.AlignmentPoint = AlignmentPoint.Value; @@ -98,18 +98,18 @@ public MText AddMTextToEntity() if (TextStyleId is not null) mText.SetTextStyleId(TextStyleId.Value); - mText.TextHeight = TextHeight; //高度 - mText.Contents = Contents; //内容 - mText.Location = Position; //插入点(一定要先设置) + mText.TextHeight = TextHeight; // 高度 + mText.Contents = Contents; // 内容 + mText.Location = Position; // 插入点(一定要先设置) - //mText.SetAttachmentMovingLocation(TextJustify); - mText.Attachment = TextJustify;//使他们对齐 + // mText.SetAttachmentMovingLocation(TextJustify); + mText.Attachment = TextJustify;// 使他们对齐 return mText; } } -//反射设定对象的文字样式id +// 反射设定对象的文字样式id public static partial class TextInfoHelper { /// @@ -169,9 +169,9 @@ static PropertyInfo GetTextStyleIdType(Entity acText) { var entType = acText.GetType(); var prs = entType.GetProperties(); - _textStyleId = prs.FirstOrDefault(a => a.Name == "TextStyle");//反射获取属性 + _textStyleId = prs.FirstOrDefault(a => a.Name == "TextStyle");// 反射获取属性 if (_textStyleId == null) - _textStyleId = prs.FirstOrDefault(a => a.Name == "TextStyleId");//反射获取属性 + _textStyleId = prs.FirstOrDefault(a => a.Name == "TextStyleId");// 反射获取属性 } return _textStyleId; } diff --git a/src/IFoxCAD.Cad/GlobalUsings.cs b/src/IFoxCAD.Cad/GlobalUsings.cs index a125299..60ba3eb 100644 --- a/src/IFoxCAD.Cad/GlobalUsings.cs +++ b/src/IFoxCAD.Cad/GlobalUsings.cs @@ -25,8 +25,7 @@ global using Autodesk.AutoCAD.DatabaseServices.Filters; global using Autodesk.AutoCAD; -//jig此命名空间容易引起Polyline等等重义,因此不放入全局空间 -//using Autodesk.AutoCAD.GraphicsInterface; +// jig命名空间会引起Viewport/Polyline等等重义,最好逐个引入 using Autodesk.AutoCAD.GraphicsInterface global using WorldDraw = Autodesk.AutoCAD.GraphicsInterface.WorldDraw; global using Manager = Autodesk.AutoCAD.GraphicsSystem.Manager; @@ -37,5 +36,8 @@ global using Registry = Microsoft.Win32.Registry; global using RegistryKey = Microsoft.Win32.RegistryKey; +global using Autodesk.AutoCAD.GraphicsInterface; +global using Polyline = Autodesk.AutoCAD.DatabaseServices.Polyline; + /// ifoxcad.basal 引用 global using IFoxCAD.Basal; \ No newline at end of file diff --git a/src/IFoxCAD.Cad/IFoxCAD.Cad.csproj b/src/IFoxCAD.Cad/IFoxCAD.Cad.csproj index 9207184..99b5567 100644 --- a/src/IFoxCAD.Cad/IFoxCAD.Cad.csproj +++ b/src/IFoxCAD.Cad/IFoxCAD.Cad.csproj @@ -1,11 +1,11 @@ - net35;net40;net45 + NET35;NET40;NET45 true enable true true - 0.3.7 + 0.4 InspireFunction xsfhlzh;vicwjb;liuqihong 基于.NET的Cad二次开发类库 diff --git a/src/IFoxCAD.Cad/Runtime/AOP.cs b/src/IFoxCAD.Cad/Runtime/AOP.cs index ac8270b..4bb5cb3 100644 --- a/src/IFoxCAD.Cad/Runtime/AOP.cs +++ b/src/IFoxCAD.Cad/Runtime/AOP.cs @@ -1,99 +1,99 @@ -//namespace IFoxCAD.Cad; -//using HarmonyLib; +// namespace IFoxCAD.Cad; +// using HarmonyLib; -//public class IFoxRefuseInjectionTransaction : Attribute -//{ -// /// -// /// 拒绝注入事务 -// /// -// public IFoxRefuseInjectionTransaction() -// { -// } -//} - -//public class AOP -//{ -// /// -// /// 在此命名空间下的命令末尾注入清空事务栈函数 -// /// -// public static void Run(string nameSpace) -// { -// Dictionary cmdDic = new(); -// AutoClass.AppDomainGetTypes(type => { -// if (type.Namespace != nameSpace) -// return; -// //类上面特性 -// if (type.IsClass) -// { -// var attr = type.GetCustomAttributes(true); -// if (RefuseInjectionTransaction(attr)) -// return; -// } +// public class IFoxRefuseInjectionTransaction : Attribute +// { +// /// +// /// 拒绝注入事务 +// /// +// public IFoxRefuseInjectionTransaction() +// { +// } +// } -// //函数上面特性 -// var mets = type.GetMethods();//获得它的成员函数 -// for (int ii = 0; ii < mets.Length; ii++) -// { -// var method = mets[ii]; -// //找到特性,特性下面的方法要是Public,否则就被编译器优化掉了. -// var attr = method.GetCustomAttributes(true); -// for (int jj = 0; jj < attr.Length; jj++) -// if (attr[jj] is CommandMethodAttribute cmdAtt) -// { -// if (!RefuseInjectionTransaction(attr)) -// cmdDic.Add(cmdAtt.GlobalName, (cmdAtt, type, method)); -// } -// } -// }); +// public class AOP +// { +// /// +// /// 在此命名空间下的命令末尾注入清空事务栈函数 +// /// +// public static void Run(string nameSpace) +// { +// Dictionary cmdDic = new(); +// AutoClass.AppDomainGetTypes(type => { +// if (type.Namespace != nameSpace) +// return; +// // 类上面特性 +// if (type.IsClass) +// { +// var attr = type.GetCustomAttributes(true); +// if (RefuseInjectionTransaction(attr)) +// return; +// } -// //运行的命令写在了Test.dll,当然不是ifox.cad类库内了.... -// if (cmdDic.Count == 0) -// return; +// // 函数上面特性 +// var mets = type.GetMethods();// 获得它的成员函数 +// for (int ii = 0; ii < mets.Length; ii++) +// { +// var method = mets[ii]; +// // 找到特性,特性下面的方法要是Public,否则就被编译器优化掉了. +// var attr = method.GetCustomAttributes(true); +// for (int jj = 0; jj < attr.Length; jj++) +// if (attr[jj] is CommandMethodAttribute cmdAtt) +// { +// if (!RefuseInjectionTransaction(attr)) +// cmdDic.Add(cmdAtt.GlobalName, (cmdAtt, type, method)); +// } +// } +// }); -// var harmony = new Harmony(nameSpace); -// var mPrefix = SymbolExtensions.GetMethodInfo(() => IFoxCmdAddFirst());//进入函数前 -// var mPostfix = SymbolExtensions.GetMethodInfo(() => IFoxCmdAddLast());//进入函数后 -// var mp1 = new HarmonyMethod(mPrefix); -// var mp2 = new HarmonyMethod(mPostfix); +// // 运行的命令写在了Test.dll,当然不是ifox.cad类库内了.... +// if (cmdDic.Count == 0) +// return; -// foreach (var item in cmdDic) -// { -// //原函数执行(空间type,函数名) -// var mOriginal = AccessTools.Method(item.Value.MetType, item.Value.MetInfo.Name); -// //mOriginal.Invoke(); -// //新函数执行:创造两个函数加入里面 -// var newMet = harmony.Patch(mOriginal, mp1, mp2); -// //newMet.Invoke(); -// } -// } +// var harmony = new Harmony(nameSpace); +// var mPrefix = SymbolExtensions.GetMethodInfo(() => IFoxCmdAddFirst());// 进入函数前 +// var mPostfix = SymbolExtensions.GetMethodInfo(() => IFoxCmdAddLast());// 进入函数后 +// var mp1 = new HarmonyMethod(mPrefix); +// var mp2 = new HarmonyMethod(mPostfix); -// /// -// /// 拒绝注入事务 -// /// -// /// 属性 -// /// -// private static bool RefuseInjectionTransaction(object[] attr) -// { -// bool refuseInjectionTransaction = false; -// for (int kk = 0; kk < attr.Length; kk++) -// { -// if (attr[kk] is IFoxRefuseInjectionTransaction) -// { -// refuseInjectionTransaction = true; -// break; -// } -// } -// return refuseInjectionTransaction; -// } +// foreach (var item in cmdDic) +// { +// // 原函数执行(空间type,函数名) +// var mOriginal = AccessTools.Method(item.Value.MetType, item.Value.MetInfo.Name); +// // mOriginal.Invoke(); +// // 新函数执行:创造两个函数加入里面 +// var newMet = harmony.Patch(mOriginal, mp1, mp2); +// // newMet.Invoke(); +// } +// } -// public static void IFoxCmdAddFirst() -// { -// //此生命周期会在静态事务栈上面,被无限延长 -// var _ = DBTrans.Top; -// } +// /// +// /// 拒绝注入事务 +// /// +// /// 属性 +// /// +// private static bool RefuseInjectionTransaction(object[] attr) +// { +// bool refuseInjectionTransaction = false; +// for (int kk = 0; kk < attr.Length; kk++) +// { +// if (attr[kk] is IFoxRefuseInjectionTransaction) +// { +// refuseInjectionTransaction = true; +// break; +// } +// } +// return refuseInjectionTransaction; +// } -// public static void IFoxCmdAddLast() -// { -// DBTrans.FinishDatabase(); -// } -//} \ No newline at end of file +// public static void IFoxCmdAddFirst() +// { +// // 此生命周期会在静态事务栈上面,被无限延长 +// var _ = DBTrans.Top; +// } + +// public static void IFoxCmdAddLast() +// { +// DBTrans.FinishDatabase(); +// } +// } \ No newline at end of file diff --git a/src/IFoxCAD.Cad/Runtime/AcadEMR.cs b/src/IFoxCAD.Cad/Runtime/AcadEMR.cs new file mode 100644 index 0000000..96bb71d --- /dev/null +++ b/src/IFoxCAD.Cad/Runtime/AcadEMR.cs @@ -0,0 +1,149 @@ +#if true +namespace IFoxCAD.Cad; + +using System.Diagnostics; + +// 作者: [VB.net]福萝卜 莱昂纳多·胖子 +// Email:oneeshine@163.com +// QQ: 461884072 +// 测试 2006-2019+ + +/// +/// 去教育版 +/// +/// +internal class AcadEMR +{ + /// + /// 释放库 + /// + /// 句柄 + /// + [DllImport("kernel32.dll", CharSet = CharSet.Auto)] + static extern IntPtr FreeLibrary(IntPtr loadLibraryIntPtr); + + /// + /// 获取一个应用程序或dll的模块句柄,要求已经载入 + /// + /// + /// + [DllImport("kernel32.dll", CharSet = CharSet.Auto, SetLastError = true)] + static extern IntPtr GetModuleHandle(string name); + + /// + /// 获取要引入的函数,将符号名或标识号转换为DLL内部地址 + /// + /// exe/dll句柄 + /// 接口名 + /// + [DllImport("kernel32.dll", SetLastError = true, CharSet = CharSet.Ansi, ExactSpelling = true)] + static extern IntPtr GetProcAddress(IntPtr hModule, string procName); + + /// + /// 虚拟保护 + /// + /// + /// + /// + /// + /// + [DllImport("kernel32.dll", CharSet = CharSet.Auto, SetLastError = true)] + static extern bool VirtualProtect(IntPtr lpAddress, IntPtr dwSize, uint flNewProtect, ref uint lpflOldProtect); + + + /// + /// 移除教育版 + /// + /// 打印出错信息 + public static void Remove(bool echoes = false) + { + var dllName = Env.GetAcapVersionDll(); + IntPtr moduleHandle = GetModuleHandle(dllName); + if (moduleHandle == IntPtr.Zero) + { + if (echoes) + Env.Printl(typeof(AcadEMR).FullName + "." + nameof(Remove) + "找不到模块:" + dllName); + return; + } + + string funcname = System.Text.Encoding.Unicode.GetString(new byte[] { 63 }); + if (IntPtr.Size == 4) + funcname += "isEMR@AcDbDatabase@@QBE_NXZ"; + else + funcname += "isEMR@AcDbDatabase@@QEBA_NXZ"; + + IntPtr funcAdress = GetProcAddress(moduleHandle, funcname); + if (funcAdress == IntPtr.Zero) + { + if (echoes) + Env.Printl("无法找指定函数:" + funcname); + return; + } + + IntPtr ptr; + if (IntPtr.Size == 4) + ptr = new IntPtr(funcAdress.ToInt32() + 3); + else + ptr = new IntPtr(funcAdress.ToInt64() + 4); + + if (!CheckFunc(ref ptr, 51, 2))//08 通过此处 + if (echoes) + Env.Printl("无法验证函数体:0x33"); + IntPtr destPtr = ptr; + + if (!CheckFunc(ref ptr, 57, 6))//08 无法通过此处,所以只是打印提示 + if (echoes) + Env.Printl("无法验证函数体:0x39"); + if (!CheckFunc(ref ptr, 15, 2))//08 无法通过此处,所以只是打印提示 + if (echoes) + Env.Printl("无法验证函数体:0x0F"); + + uint flag = default; + uint tccc = default; + + IntPtr ip100 = new(100); + if (!VirtualProtect(destPtr, ip100, 64, ref flag))// 修改内存权限 + { + if (echoes) + Env.Printl("内存模式修改失败!"); + return; + } + + Marshal.WriteByte(destPtr, 137); + VirtualProtect(destPtr, ip100, flag, ref tccc);// 恢复内存权限 + } + + /// + /// 验证函数体 + /// + /// + /// + /// + /// + static bool CheckFunc(ref IntPtr adress, byte val, int len) + { + if (Marshal.ReadByte(adress) == 233) + { + if (IntPtr.Size == 4) + { + var pass = Marshal.ReadInt32(new IntPtr(adress.ToInt32() + 1)); + adress = new IntPtr(adress.ToInt32() + pass + 5); + } + else + { + var pass = Marshal.ReadInt64(new IntPtr(adress.ToInt64() + 1)); + adress = new IntPtr(adress.ToInt64() + pass + 5); + } + } + if (Marshal.ReadByte(adress) == val) + { + if (IntPtr.Size == 4) + adress = new IntPtr(adress.ToInt32() + len); + else + adress = new IntPtr(adress.ToInt64() + len); + return true; + } + return false; + } +} +#endif \ No newline at end of file diff --git a/src/IFoxCAD.Cad/Runtime/AcadVersion.cs b/src/IFoxCAD.Cad/Runtime/AcadVersion.cs index f8f5273..9f776fc 100644 --- a/src/IFoxCAD.Cad/Runtime/AcadVersion.cs +++ b/src/IFoxCAD.Cad/Runtime/AcadVersion.cs @@ -63,10 +63,8 @@ public static List Versions int major = int.Parse(gs[1].Value); int minor = int.Parse(gs[2].Value); foreach (var ver in Versions) - { if (ver.Major == major && ver.Minor == minor) return ver; - } return null; } } diff --git a/src/IFoxCAD.Cad/Runtime/AssemInfo.cs b/src/IFoxCAD.Cad/Runtime/AssemInfo.cs index c13cc26..337ac0d 100644 --- a/src/IFoxCAD.Cad/Runtime/AssemInfo.cs +++ b/src/IFoxCAD.Cad/Runtime/AssemInfo.cs @@ -73,5 +73,10 @@ public enum AutoRegConfig /// ReflectionInterface = 4, - All = Regedit | ReflectionAttribute | ReflectionInterface, -} + /// + /// 移除教育版 + /// + RemoveEMR = 8, + + All = Regedit | ReflectionAttribute | ReflectionInterface | RemoveEMR, +} \ No newline at end of file diff --git a/src/IFoxCAD.Cad/Runtime/AutoRegAssem.cs b/src/IFoxCAD.Cad/Runtime/AutoRegAssem.cs index 540762c..cfc3d17 100644 --- a/src/IFoxCAD.Cad/Runtime/AutoRegAssem.cs +++ b/src/IFoxCAD.Cad/Runtime/AutoRegAssem.cs @@ -3,11 +3,13 @@ /// /// 注册中心 -/// 初始化程序集信息写入注册表并反射特性和接口 -/// 启动cad后的执行顺序为: -/// 1:程序集配置中心构造函数 -/// 2:特性..(多个) -/// 3:接口..(多个) +/// +/// 初始化程序集信息写入注册表并反射特性和接口
    +/// 启动cad后的执行顺序为:
    +/// 1:程序集配置中心构造函数
    +/// 2:特性..(多个)
    +/// 3:接口..(多个)
    +///
    ///
    public abstract class AutoRegAssem : IExtensionApplication { @@ -63,9 +65,12 @@ public AutoRegAssem(AutoRegConfig autoRegConfig) RegApp(); } - //实例化了 AutoClass 之后会自动执行 IFoxAutoGo 接口下面的类, - //以及自动执行特性 [IFoxInitialize] - //类库用户不在此处进行其他代码,而是实现特性 + if ((autoRegConfig & AutoRegConfig.RemoveEMR) == AutoRegConfig.RemoveEMR) + AcadEMR.Remove(); + + // 实例化了 AutoClass 之后会自动执行 IFoxAutoGo 接口下面的类, + // 以及自动执行特性 [IFoxInitialize] + // 类库用户不在此处进行其他代码,而是实现特性 _autoRef = new AutoReflection(_info.Name, autoRegConfig); _autoRef.Initialize(); } @@ -83,7 +88,7 @@ public static RegistryKey GetAcAppKey(bool writable = true) RegistryKey? ackey = null; #if NET35 - string key = HostApplicationServices.Current.RegistryProductRootKey; //这里浩辰读出来是"" + string key = HostApplicationServices.Current.RegistryProductRootKey; // 这里浩辰读出来是"" #elif !HC2020 string key = HostApplicationServices.Current.UserRegistryProductRootKey; #endif @@ -91,14 +96,14 @@ public static RegistryKey GetAcAppKey(bool writable = true) #if !HC2020 ackey = Registry.CurrentUser.OpenSubKey(key, writable); #else - //浩辰 - var s = GrxCAD.DatabaseServices.HostApplicationServices.Current.RegistryProductRootKey;//浩辰奇怪的空值 + // 浩辰 + var s = GrxCAD.DatabaseServices.HostApplicationServices.Current.RegistryProductRootKey;// 浩辰奇怪的空值 string str = CadSystem.Getvar("ACADVER"); str = Regex.Replace(str, @"[^\d.\d]", ""); double.TryParse(str, out double a); - // string regedit = @"Software\Gstarsoft\GstarCAD\R" + a.ToString() + @"\zh-CN"; //2019 - // string regedit = @"Software\Gstarsoft\GstarCAD\B" + a.ToString() + @"\zh-CN";//2020 这里是 - string regedit = @"Software\Gstarsoft\GstarCAD\B20\zh-CN";//2020 这里是 + // string regedit = @"Software\Gstarsoft\GstarCAD\R" + a.ToString() + @"\zh-CN"; // 2019 + // string regedit = @"Software\Gstarsoft\GstarCAD\B" + a.ToString() + @"\zh-CN";// 2020 这里是 + string regedit = @"Software\Gstarsoft\GstarCAD\B20\zh-CN";// 2020 这里是 ackey = Registry.CurrentUser.OpenSubKey(regedit, writable); #endif @@ -129,6 +134,7 @@ public bool UnRegApp() /// bool SearchForReg() { + // 在使用netloadx的时候,此处注册表是失效的,具体原因要进行netloadx测试 var appkey = GetAcAppKey(); if (appkey.SubKeyCount == 0) return false; @@ -136,7 +142,7 @@ bool SearchForReg() var regApps = appkey.GetSubKeyNames(); if (regApps.Contains(_info.Name)) { - //20220409 bug:文件名相同,路径不同,需要判断路径 + // 20220409 bug:文件名相同,路径不同,需要判断路径 var info = appkey.OpenSubKey(_info.Name); return info.GetValue("LOADER")?.ToString().ToLower() == _info.Loader.ToLower(); } @@ -157,13 +163,15 @@ public void RegApp() appkey.Close(); } - //这里的是不会自动执行的 + // 这里的是不会自动执行的 public void Initialize() { } - public void Terminate() { } + public void Terminate() + { + } ~AutoRegAssem() { _autoRef.Terminate(); } #endregion RegApp -} +} \ No newline at end of file diff --git a/src/IFoxCAD.Cad/Runtime/CadVersion.cs b/src/IFoxCAD.Cad/Runtime/CadVersion.cs index 2e32c74..d9614bf 100644 --- a/src/IFoxCAD.Cad/Runtime/CadVersion.cs +++ b/src/IFoxCAD.Cad/Runtime/CadVersion.cs @@ -47,10 +47,10 @@ public override string ToString() // return base.GetHashCode(); // } - // //public override string ToString() - // //{ + // // public override string ToString() + // // { // // return base.ToString(); - // //} + // // } } #else public record CadVersion diff --git a/src/IFoxCAD.Cad/Runtime/DBTrans.cs b/src/IFoxCAD.Cad/Runtime/DBTrans.cs index 3785034..270886f 100644 --- a/src/IFoxCAD.Cad/Runtime/DBTrans.cs +++ b/src/IFoxCAD.Cad/Runtime/DBTrans.cs @@ -1,7 +1,13 @@ -namespace IFoxCAD.Cad; +namespace IFoxCAD.Cad; + +using System.Diagnostics; +using System.IO; +using System.Threading; +using System.Windows.Forms; /// -/// 事务栈,隐匿事务在数据库其中担任的角色 +/// 事务栈 +/// 隐匿事务在数据库其中担任的角色 /// public class DBTrans : IDisposable { @@ -11,10 +17,6 @@ public class DBTrans : IDisposable ///
    private readonly DocumentLock? documentLock; /// - /// 是否释放资源 - /// - private bool disposedValue; - /// /// 是否提交事务 /// private readonly bool _commit; @@ -22,6 +24,10 @@ public class DBTrans : IDisposable /// 事务栈 ///
    private static readonly Stack dBTrans = new(); + /// + /// 文件名 + /// + private readonly string? _fileName; #endregion #region 公开属性 @@ -37,7 +43,7 @@ public static DBTrans Top * 事务栈上面有事务,这个事务属于当前文档, * 那么直接提交原本事务然后再开一个(一直把栈前面的同数据库提交清空) * 那不就发生跨事务读取图元了吗?....否决 - * + * * 0x02 * 跨文档事务出错 Autodesk.AutoCAD.Runtime.Exception:“eNotFromThisDocument” * Curves.GetEntities()会从Top获取事务(Top会new一个),此时会是当前文档; @@ -46,7 +52,7 @@ public static DBTrans Top * 然后我新建了一个文档,再进行命令=>又进入Top,Top返回了前一个文档的事务 * 因此所以无法清理栈,所以Dispose不触发,导致无法刷新图元和Ctrl+Z出错 * 所以用AOP方式修复 - * + * * 0x03 * 经过艰苦卓绝的测试,aop模式由于不能断点调试,所以暂时放弃。 */ @@ -125,16 +131,22 @@ public DBTrans(string fileName, FileOpenMode openMode = FileOpenMode.OpenForReadAndWriteNoShare, string? password = null) { - if (string.IsNullOrEmpty(fileName?.Trim())) + if (fileName == null || string.IsNullOrEmpty(fileName.Trim())) throw new ArgumentNullException(nameof(fileName)); - if (!File.Exists(fileName)) + _fileName = fileName.Replace("/", "\\");//// doc.Name总是"D:\\JX.dwg" + + if (!File.Exists(_fileName)) + { + // 此处若为失败的文件名,那么保存的时候就会丢失名称, + // 因此用 _fileName 储存 Database = new Database(true, false); + } else { var doc = Acap.DocumentManager .Cast() - .FirstOrDefault(doc => doc.Name == fileName); + .FirstOrDefault(doc => doc.Name == _fileName); if (doc is not null) { Database = doc.Database; @@ -144,25 +156,44 @@ public DBTrans(string fileName, else { Database = new Database(false, true); - if (Path.GetExtension(fileName).ToLower().Contains("dxf")) - Database.DxfIn(fileName, null); + if (Path.GetExtension(_fileName).ToLower().Contains("dxf")) + { + Database.DxfIn(_fileName, null); + } else { #if ac2008 - //此处没有一一对应的关系 -#pragma warning disable CS0436 // 类型与导入类型冲突 - var sf = openMode switch + // FileAccess fileAccess = FileAccess.Read; + FileShare fileShare = FileShare.Read; + switch (openMode) { - FileOpenMode.OpenTryForReadShare => FileShare.Read, - FileOpenMode.OpenForReadAndAllShare => FileShare.ReadWrite, - FileOpenMode.OpenForReadAndWriteNoShare => FileShare.None, - FileOpenMode.OpenForReadAndReadShare => FileShare.ReadWrite, - _ => FileShare.ReadWrite, - }; -#pragma warning restore CS0436 // 类型与导入类型冲突 - Database.ReadDwgFile(fileName, sf, true/*控制读入一个与系统编码不相同的文件时的转换操作*/, password); + case FileOpenMode.OpenTryForReadShare:// 这个是什么状态?? + // fileAccess = FileAccess.ReadWrite; + fileShare = FileShare.ReadWrite; + break; + case FileOpenMode.OpenForReadAndAllShare:// 完美匹配 + // fileAccess = FileAccess.ReadWrite; + fileShare = FileShare.ReadWrite; + break; + case FileOpenMode.OpenForReadAndWriteNoShare:// 完美匹配 + // fileAccess = FileAccess.ReadWrite; + fileShare = FileShare.None; + break; + case FileOpenMode.OpenForReadAndReadShare:// 完美匹配 + // fileAccess = FileAccess.Read; + fileShare = FileShare.Read; + break; + default: + break; + } + + // 这个会致命错误 + // using FileStream fileStream = new(_fileName, FileMode.Open, fileAccess, fileShare); + // Database.ReadDwgFile(fileStream.SafeFileHandle.DangerousGetHandle(), true, password); + + Database.ReadDwgFile(_fileName, fileShare, true, password); #else - Database.ReadDwgFile(fileName, openMode, true/*控制读入一个与系统编码不相同的文件时的转换操作*/, password); + Database.ReadDwgFile(_fileName, openMode, true, password); #endif } Database.CloseInput(true); @@ -192,7 +223,8 @@ public static implicit operator Transaction(DBTrans tr) /// /// 块表 /// - public SymbolTable BlockTable => new(this, Database.BlockTableId); + public SymbolTable BlockTable => _BlockTable ??= new(this, Database.BlockTableId); + SymbolTable? _BlockTable; /// /// 当前绘图空间 /// @@ -208,41 +240,43 @@ public static implicit operator Transaction(DBTrans tr) /// /// 层表 /// - public SymbolTable LayerTable => new(this, Database.LayerTableId); + public SymbolTable LayerTable => _LayerTable ??= new(this, Database.LayerTableId); + SymbolTable? _LayerTable; /// /// 文字样式表 /// - public SymbolTable TextStyleTable => new(this, Database.TextStyleTableId); - + public SymbolTable TextStyleTable => _TextStyleTable ??= new(this, Database.TextStyleTableId); + SymbolTable? _TextStyleTable; /// /// 注册应用程序表 /// - public SymbolTable RegAppTable => new(this, Database.RegAppTableId); - + public SymbolTable RegAppTable => _RegAppTable ??= new(this, Database.RegAppTableId); + SymbolTable? _RegAppTable; /// /// 标注样式表 /// - public SymbolTable DimStyleTable => new(this, Database.DimStyleTableId); - + public SymbolTable DimStyleTable => _DimStyleTable ??= new(this, Database.DimStyleTableId); + SymbolTable? _DimStyleTable; /// /// 线型表 /// - public SymbolTable LinetypeTable => new(this, Database.LinetypeTableId); - + public SymbolTable LinetypeTable => _LinetypeTable ??= new(this, Database.LinetypeTableId); + SymbolTable? _LinetypeTable; /// /// 用户坐标系表 /// - public SymbolTable UcsTable => new(this, Database.UcsTableId); - + public SymbolTable UcsTable => _UcsTable ??= new(this, Database.UcsTableId); + SymbolTable? _UcsTable; /// /// 视图表 /// - public SymbolTable ViewTable => new(this, Database.ViewTableId); - + public SymbolTable ViewTable => _ViewTable ??= new(this, Database.ViewTableId); + SymbolTable? _ViewTable; /// /// 视口表 /// - public SymbolTable ViewportTable => new(this, Database.ViewportTableId); + public SymbolTable ViewportTable => _ViewportTable ??= new(this, Database.ViewportTableId); + SymbolTable? _ViewportTable; #endregion #region 字典 @@ -332,38 +366,204 @@ public static implicit operator Transaction(DBTrans tr) public ObjectId GetObjectId(string handleString) { var hanle = new Handle(Convert.ToInt64(handleString, 16)); - //return Database.GetObjectId(false, hanle, 0); - return Helper.TryGetObjectId(Database, hanle); + // return Database.GetObjectId(false, hanle, 0); + return DBTransHelper.TryGetObjectId(Database, hanle); } #endregion #region 保存文件 /// - /// 保存当前数据库的dwg文件,如果前台打开则按dwg默认版本保存,否则按version参数的版本保存 + /// 保存文件 /// - /// dwg版本,默认为2004 + /// public void SaveDwgFile(DwgVersion version = DwgVersion.AC1800) { - bool flag = true; - foreach (Document doc in Acap.DocumentManager) + SaveFile(version); + } + + /// + /// 保存文件
    + ///
    + /// 默认2004dwg;若保存dxf则需要在路径输入扩展名 + /// 为true时候无效,将变为自动识别环境变量 + /// 另存为文件,前台将调用时它将无效,将变为弹出面板 + /// 保存路径失败的提示 + public void SaveFile(DwgVersion version = DwgVersion.AC1800, + bool automatic = true, + string? saveAsFile = null, + bool echoes = true) + { + // 遍历当前所有文档,文档必然是前台的 + Document? doc = null; + foreach (Document docItem in Acap.DocumentManager) { - // 前台开图,使用命令保存 - if (doc.Database.Filename == this.Database.Filename) + if (docItem.Database.Filename == this.Database.Filename) { - doc.SendStringToExecute("_qsave\n", false, true, true); //不需要切换文档 - flag = false; + doc = docItem; break; } } - if (flag) + // 前台开图,使用命令保存;不需要切换文档 + if (doc != null) + { + if (saveAsFile == null) + doc.SendStringToExecute("_qsave\n", false, true, true); + else + /// 无法把 给这个面板 + doc.SendStringToExecute($"_Saveas\n", false, true, true); + return; + } + + // 后台开图,用数据库保存 + string? fileMsg; + bool creatFlag = false; + saveAsFile = saveAsFile?.Trim(); + if (string.IsNullOrEmpty(saveAsFile)) + { + fileMsg = _fileName; + creatFlag = true; + } + else + { + fileMsg = saveAsFile; + + // 路径失败也保存到桌面 + var path = Path.GetDirectoryName(saveAsFile); + if (string.IsNullOrEmpty(path)) + { + creatFlag = true; + } + else if (!Directory.Exists(path)) + { + try { Directory.CreateDirectory(path); } + catch { creatFlag = true; } + } + + // 文件名缺失时 + if (!creatFlag && + string.IsNullOrEmpty(Path.GetFileName(saveAsFile).Trim())) + creatFlag = true; + } + var fileNameWith = Path.GetFileNameWithoutExtension(saveAsFile).Trim(); + if (string.IsNullOrEmpty(fileNameWith)) + creatFlag = true; + + if (creatFlag) + { + var (error, file) = GetOrCreateSaveAsFile(); + if (echoes && error) + MessageBox.Show($"错误参数:\n{fileMsg}\n\n它将保存:\n{file}", "错误的文件路径"); + saveAsFile = file; + } + + if (Path.GetExtension(saveAsFile).ToLower().Contains("dxf")) + { + // dxf用任何版本号都会报错 + Database.DxfOut(saveAsFile, 7, true); + } + else { - // 后台开图,用数据库保存 - Database.SaveAs(Database.Filename, version); + if (automatic) + version = Env.GetDefaultDwgVersion(); + + // dwg需要版本号,而dxf不用,dwg用dxf版本号会报错 + // 若扩展名和版本号冲突,按照扩展名为准 + if (version.IsDxfVersion()) + version = DwgVersion.Current; + Database.SaveAs(saveAsFile, version); } } + + + /// + /// 获取文件名,无效的话就制造 + /// + /// + (bool error, string path) GetOrCreateSaveAsFile() + { + var file = Database.Filename; + if (!string.IsNullOrEmpty(file)) + return (false, file); + + // 为了防止用户输入了错误的路径造成无法保存, + // 所以此处将进行保存到桌面, + // 而不是弹出警告就结束 + // 防止前台关闭了所有文档导致没有Editor,所以使用 MessageBox 发送警告 + var fileName = Path.GetFileNameWithoutExtension(_fileName).Trim(); + var fileExt = Path.GetExtension(_fileName); + + if (string.IsNullOrEmpty(fileName)) + fileName = DateTime.Now.ToString("--yyMMdd--hhmmssffff"); + if (string.IsNullOrEmpty(fileExt)) + fileExt = ".dwg"; + + // 构造函数(fileName)用了不存在的路径进行后台打开,就会出现此问题 + // 测试命令 FileNotExist + var dir = Environment.GetFolderPath( + Environment.SpecialFolder.DesktopDirectory) + + "\\后台保存出错的文件\\"; + + if (!Directory.Exists(dir)) + Directory.CreateDirectory(dir); + + file = dir + fileName + fileExt; + while (File.Exists(file)) + { + var time = DateTime.Now.ToString("--yyMMdd--hhmmssffff"); + file = dir + fileName + time + fileExt; + Thread.Sleep(100); + } + return (true, file); + } + + #endregion + + #region 前台后台任务 + /// + /// 前台后台任务分别处理 + /// + /// + /// 备注:
    + /// 0x01 文字偏移问题主要出现线性引擎函数上面,
    + /// 在 参照绑定/深度克隆 的底层共用此函数导致
    + /// 0x02 后台是利用前台当前数据库进行处理的
    + /// 0x03 跨进程通讯暂无测试(可能存在bug)
    + ///
    + /// 委托 + /// 开启单行文字偏移处理 + public void Task(Action action, bool handlingDBTextDeviation = true) + { + if (action == null) + throw new ArgumentNullException(nameof(action)); + + // 前台开图 || 后台直接处理 + if (Document != null || !handlingDBTextDeviation) + { + action.Invoke(); + return; + } + + // 后台 + // 这种情况发生在关闭了所有文档之后,进行跨进程通讯 + // 此处要先获取激活的文档,不能直接获取当前数据库否则异常 + var dm = Acap.DocumentManager; + var doc = dm.MdiActiveDocument; + if (doc == null) + { + action.Invoke(); + return; + } + // 处理单行文字偏移 + // 前台绑定参照的时候不能用它,否则抛出异常:eWasErased + // 所以本函数自动识别前后台做处理 + var dbBak = doc.Database; + HostApplicationServices.WorkingDatabase = Database; + action.Invoke(); + HostApplicationServices.WorkingDatabase = dbBak; + } #endregion - #region idispose接口相关函数 + #region IDisposable接口相关函数 /// /// 取消事务 /// @@ -371,6 +571,7 @@ public void Abort() { Dispose(false); } + /// /// 提交事务 /// @@ -379,23 +580,39 @@ public void Commit() Dispose(true); } + public bool IsDisposed { get; private set; } = false; + + /// + /// 手动调用释放 + /// + public void Dispose() + { + Dispose(true); + GC.SuppressFinalize(this); + } + + /// + /// 析构函数调用释放 + /// + ~DBTrans() + { + Dispose(false); + } + protected virtual void Dispose(bool disposing) { /* 事务dispose流程: * 1. 根据传入的参数确定是否提交,true为提交,false为不提交 - * 2. 根据disposedValue的值确定是否重复dispose,false为首次dispose - * 3. 如果锁文档就将文档锁dispose - * 4. 不管是否提交,既然进入dispose,就要将事务栈的当前事务弹出 + * 2. 如果锁文档就将文档锁dispose + * 3. 不管是否提交,既然进入dispose,就要将事务栈的当前事务弹出 * 注意这里的事务栈不是cad的事务管理器,而是dbtrans的事务 - * 5. 清理非托管的字段 + * 4. 清理非托管的字段 */ - if (disposedValue) - return; + // 不重复释放,并设置已经释放 + if (IsDisposed) return; + IsDisposed = true; - // 释放未托管的资源(未托管的对象)并替代终结器 - // 将大型字段设置为 null - disposedValue = true; if (disposing) { @@ -407,29 +624,16 @@ protected virtual void Dispose(bool disposing) // 否则取消所有的修改 Transaction.Abort(); } - // 调用 cad事务的dispose进行销毁 + + // 将cad事务进行销毁 if (!Transaction.IsDisposed) Transaction.Dispose(); - // 调用文档锁dispose + // 将文档锁销毁 documentLock?.Dispose(); - // 将事务栈的当前dbtrans弹栈 + // 将当前事务栈弹栈 dBTrans.Pop(); } - - // 仅当“Dispose(bool disposing)”拥有用于释放未托管资源的代码时才替代终结器 - ~DBTrans() - { - // 不要更改此代码。请将清理代码放入“Dispose(bool disposing)”方法中 - Dispose(disposing: false); - } - - public void Dispose() - { - // 不要更改此代码。请将清理代码放入“Dispose(bool disposing)”方法中 - Dispose(disposing: true); - GC.SuppressFinalize(this); - } #endregion -} +} \ No newline at end of file diff --git a/src/IFoxCAD.Cad/Runtime/Env.cs b/src/IFoxCAD.Cad/Runtime/Env.cs index 12a9738..572e3e8 100644 --- a/src/IFoxCAD.Cad/Runtime/Env.cs +++ b/src/IFoxCAD.Cad/Runtime/Env.cs @@ -2,9 +2,11 @@ namespace IFoxCAD.Cad; /// /// 系统管理类 -/// 封装了一些系统 osmode、cmdecho、dimblk 系统变量 -/// 封装了常用的 文档 编辑器 数据库等对象为静态变量 -/// 封装了配置页面的注册表信息获取函数 +/// +/// 封装了一些系统 osmode;cmdecho;dimblk 系统变量
    +/// 封装了常用的 文档 编辑器 数据库等对象为静态变量
    +/// 封装了配置页面的注册表信息获取函数 +///
    ///
    public static class Env { @@ -42,7 +44,7 @@ public static class Env /// 对象 public static object GetCurrentProfileProperty(string subSectionName, string propertyName) { - UserConfigurationManager ucm = Application.UserConfigurationManager; + UserConfigurationManager ucm = Acap.UserConfigurationManager; IConfigurationSection cpf = ucm.OpenCurrentProfile(); IConfigurationSection ss = cpf.OpenSubsection(subSectionName); return ss.ReadProperty(propertyName, ""); @@ -55,7 +57,7 @@ public static object GetCurrentProfileProperty(string subSectionName, string pro /// 配置项 public static IConfigurationSection GetDialogSection(object dialog) { - UserConfigurationManager ucm = Application.UserConfigurationManager; + UserConfigurationManager ucm = Acap.UserConfigurationManager; IConfigurationSection ds = ucm.OpenDialogSection(dialog); return ds; } @@ -67,7 +69,7 @@ public static IConfigurationSection GetDialogSection(object dialog) /// 配置项 public static IConfigurationSection GetGlobalSection(string propertyName) { - UserConfigurationManager ucm = Application.UserConfigurationManager; + UserConfigurationManager ucm = Acap.UserConfigurationManager; IConfigurationSection gs = ucm.OpenGlobalSection(); IConfigurationSection ss = gs.OpenSubsection(propertyName); return ss; @@ -82,8 +84,8 @@ public static IConfigurationSection GetGlobalSection(string propertyName) ///
    public static bool CmdEcho { - get => Convert.ToInt16(Application.GetSystemVariable("cmdecho")) == 1; - set => Application.SetSystemVariable("cmdecho", Convert.ToInt16(value)); + get => Convert.ToInt16(Acap.GetSystemVariable("cmdecho")) == 1; + set => Acap.SetSystemVariable("cmdecho", Convert.ToInt16(value)); } /// @@ -91,8 +93,8 @@ public static bool CmdEcho /// public static bool OrthoMode { - get => Convert.ToInt16(Application.GetSystemVariable("ORTHOMODE")) == 1; - set => Application.SetSystemVariable("ORTHOMODE", Convert.ToInt16(value)); + get => Convert.ToInt16(Acap.GetSystemVariable("ORTHOMODE")) == 1; + set => Acap.SetSystemVariable("ORTHOMODE", Convert.ToInt16(value)); } #region Dimblk @@ -257,26 +259,26 @@ public static DimblkType Dimblk { get { - string s = ((string)Application.GetSystemVariable("dimblk")).ToUpper(); - //if (string.IsNullOrEmpty(s)) - //{ + string s = ((string)Acap.GetSystemVariable("dimblk")).ToUpper(); + // if (string.IsNullOrEmpty(s)) + // { // return DimblkType.Defult; - //} - //else - //{ + // } + // else + // { // if (dimdescdict.TryGetValue(s, out DimblkType value)) // { // return value; // } // return s.ToEnum(); - // //return s.FromDescName(); - //} + // // return s.FromDescName(); + // } return dimdescdict[s]; } set { string s = GetDimblkName(value); - Application.SetSystemVariable("dimblk", s); + Acap.SetSystemVariable("dimblk", s); } } @@ -401,11 +403,11 @@ public static OSModeType OSMode { get { - return (OSModeType)Convert.ToInt16(Application.GetSystemVariable("osmode")); + return (OSModeType)Convert.ToInt16(Acap.GetSystemVariable("osmode")); } set { - Application.SetSystemVariable("osmode", (int)value); + Acap.SetSystemVariable("osmode", (int)value); } } /// @@ -428,63 +430,303 @@ private static string GetName(this T value) #endregion Enum - #region 环境变量 + #region acad系统变量 /// - /// 获取cad变量 + /// 获取cad系统变量 /// /// 变量名 /// 变量值 - public static object GetVar(string varName) + public static object GetVar(string? varName) { - return Application.GetSystemVariable(varName); + return Acap.GetSystemVariable(varName); } /// - /// 设置cad变量 + /// 设置cad系统变量
    + /// 0x01 建议先获取现有变量值和设置的是否相同,否则直接设置会发生异常
    + /// 0x02 建议锁文档,否则 Psltscale 设置发生异常
    + /// 发生异常的时候vs输出窗口会打印一下,但是如果不介意也没啥问题 ///
    /// 变量名 /// 变量值 - public static void SetVar(string varName, object value) + /// 输出异常,默认true;此设置仅为打印到命令栏,无法控制vs输出 + public static void SetVar(string? varName, object? value, bool echo = true) { try { - Application.SetSystemVariable(varName, value); + Acap.SetSystemVariable(varName, value); } catch (System.Exception) { - Env.Print($"{varName} 是不存在的变量!"); + if (echo) + Env.Print($"{varName} 是不存在的变量!"); } } + #endregion + + #region acad环境变量 +#if NET35 + [System.Security.SuppressUnmanagedCodeSecurity] + [DllImport("acad.exe", CharSet = CharSet.Auto, CallingConvention = CallingConvention.Cdecl, EntryPoint = "acedGetEnv")] + static extern int AcedGetEnv(string? envName, StringBuilder ReturnValue); + + [System.Security.SuppressUnmanagedCodeSecurity] + [DllImport("acad.exe", CharSet = CharSet.Auto, CallingConvention = CallingConvention.Cdecl, EntryPoint = "acedSetEnv")] + static extern int AcedSetEnv(string? envName, StringBuilder NewValue); +#elif !HC2020 + [System.Security.SuppressUnmanagedCodeSecurity] + [DllImport("accore.dll", CharSet = CharSet.Auto, CallingConvention = CallingConvention.Cdecl, EntryPoint = "acedGetEnv")] + static extern int AcedGetEnv(string? envName, StringBuilder ReturnValue); + + [System.Security.SuppressUnmanagedCodeSecurity] + [DllImport("accore.dll", CharSet = CharSet.Auto, CallingConvention = CallingConvention.Cdecl, EntryPoint = "acedSetEnv")] + static extern int AcedSetEnv(string? envName, StringBuilder NewValue); +#endif +#if HC2020 + [System.Security.SuppressUnmanagedCodeSecurity] + [DllImport("gced.dll", CharSet = CharSet.Auto, CallingConvention = CallingConvention.Cdecl, EntryPoint = "gcedGetEnv")] + static extern int AcedGetEnv(string? envName, StringBuilder ReturnValue); + + [System.Security.SuppressUnmanagedCodeSecurity] + [DllImport("gced.dll", CharSet = CharSet.Auto, CallingConvention = CallingConvention.Cdecl, EntryPoint = "gcedSetEnv")] + static extern int AcedSetEnv(string? envName, StringBuilder NewValue); +#endif + /// - /// 获取系统环境变量 + /// 读取acad环境变量
    + /// 也能获取win环境变量 ///
    - /// 变量名 - /// 指定的环境变量的值;或者如果找不到环境变量,则返回 null - public static string? GetEnv(string var) + /// 变量名 + /// 返回值从不为null,需判断 + public static string GetEnv(string? name) { - //从当前进程或者从当前用户或本地计算机的 Windows 操作系统注册表项检索环境变量的值 - return Environment.GetEnvironmentVariable(var); + // 它将混合查询以下路径: + // acad2008注册表路径: 计算机\HKEY_CURRENT_USER\SOFTWARE\Autodesk\AutoCAD\R17.1\ACAD - 6001:804\FixedProfile\General + // 用户: 计算机\HKEY_CURRENT_USER\Environment + // 系统: 计算机\HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\Session Manager\Environment + + // GetEnv("Path")长度很长: + // 可用内存 (最新格式) 1 MB (标准格式) + // https://docs.microsoft.com/zh-cn/windows/win32/sysinfo/registry-element-size-limits + + var sbRes = new StringBuilder(1024 * 1024); + _ = AcedGetEnv(name, sbRes); + return sbRes.ToString(); } + /// - /// 设置系统环境变量 + /// 设置acad环境变量
    + /// 它是不会报错的,但是直接设置会写入注册表的,
    + /// 如果是设置高低版本cad不同的变量,建议先读取判断再设置
    ///
    - /// 变量名 - /// 变量值 - public static void SetEnv(string var, string? value) + /// 变量名 + /// 变量值 + /// + public static int SetEnv(string? name, string? var) { - //创建、修改或删除当前进程中或者为当前用户或本地计算机保留的 Windows 操作系统注册表项中存储的环境变量 - Environment.SetEnvironmentVariable(var, value); + return AcedSetEnv(name, new StringBuilder(var)); } #endregion + #region win环境变量/由于 Aced的 能够同时获取此变量与cad内的,所以废弃 + ///// + ///// 获取系统环境变量 + ///// + ///// 变量名 + ///// 指定的环境变量的值;或者如果找不到环境变量,则返回 null + //public static string? GetEnv(string? var) + //{ + // // 从当前进程或者从当前用户或本地计算机的 Windows 操作系统注册表项检索环境变量的值 + // // 用户: 计算机\HKEY_CURRENT_USER\Environment + // // 系统: 计算机\HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\Session Manager\Environment + // return Environment.GetEnvironmentVariable(var); + //} + ///// + ///// 设置系统环境变量 + ///// + ///// 变量名 + ///// 变量值 + //public static void SetEnv(string? var, string? value) + //{ + // // 创建、修改或删除当前进程中或者为当前用户或本地计算机保留的 Windows 操作系统注册表项中存储的环境变量 + // Environment.SetEnvironmentVariable(var, value); + //} + #endregion + /// /// 命令行打印,会自动调用对象的toString函数 /// /// 要打印的对象 public static void Print(object message) => Editor.WriteMessage($"{message}\n"); + public static void Printl(object message) => Editor.WriteMessage($"{Environment.NewLine}{message}\n"); + /// /// 判断当前是否在UCS坐标下 /// /// Bool public static bool IsUcs() => (short)GetVar("WORLDUCS") == 0; -} + + + #region dwg版本号/cad版本号/年份 + /// + /// 获取当前配置文件的保存版本 + /// + /// + public static DwgVersion GetDefaultDwgVersion() + { + DwgVersion version; + var ffs = Env.GetEnv("DefaultFormatForSave"); + version = ffs switch + { + "1" => DwgVersion.AC1009,// R12/LT12 dxf + "8" => DwgVersion.AC1014,// R14/LT98/LT97 dwg + "12" => DwgVersion.AC1015,// 2000 dwg + "13" => DwgVersion.AC1800a,// 2000 dxf + "24" => DwgVersion.AC1800,// 2004 dwg + "25" => (DwgVersion)26,// 2004 dxf + "36" => (DwgVersion)27,// 2007 dwg DwgVersion.AC1021 + "37" => (DwgVersion)28,// 2007 dxf + + //"38" => (DwgVersion),// dwt 样板文件...啊惊没找到这个是什么 + "48" => (DwgVersion)29,// 2010 dwg DwgVersion.AC1024 + "49" => (DwgVersion)30,// 2010 dxf + "60" => (DwgVersion)31,// 2013 dwg DwgVersion.AC1027 + "61" => (DwgVersion)32,// 2013 dxf + "64" => (DwgVersion)33,// 2018 dwg DwgVersion.AC1032 + "65" => (DwgVersion)34,// 2018 dxf + _ => throw new NotImplementedException(),//提醒维护 + }; + return version; + } + + /// + /// 是否为dxf版本号 + /// + /// + /// + public static bool IsDxfVersion(this DwgVersion dwgVersion) + { + var result = (int)dwgVersion switch + { + 16 => true,// R12/LT12 dxf + 24 => true,// 2000 dxf + 26 => true,// 2004 dxf + 28 => true,// 2007 dxf + 30 => true,// 2010 dxf + 32 => true,// 2013 dxf + 34 => true,// 2018 dxf + _ => false, + }; + return result; + } + + /// + /// 获取cad年份 + /// + /// 超出年份就报错 + public static int GetAcadVersion() + { + var ver = Acap.Version.Major + "." + Acap.Version.Minor; + int acarVarNum = ver switch + { + "16.2" => 2006, + "17.0" => 2007, + "17.1" => 2008, + "17.2" => 2009, + "18.0" => 2010, + "18.1" => 2011, + "18.2" => 2012, + "19.0" => 2013, + "19.1" => 2014, + "20.0" => 2015, + "20.1" => 2016, + "21.0" => 2017, + "22.0" => 2018, + "23.0" => 2019, + "23.1" => 2020, + "24.0" => 2021, + "24.1" => 2022, + _ => throw new NotImplementedException(), + }; + return acarVarNum; + } + + public static string GetAcapVersionDll(string str = "acdb") + { + return str + Acap.Version.Major + ".dll"; + } + #endregion + + + #region cad变量功能延伸 + /// + /// 设置cad系统变量
    + /// 提供一个反序列化后,无cad异常输出的功能
    + /// 注意,您需要再此执行时候设置文档锁
    + ///
    + /// 否则也将导致修改数据库异常
    + ///
    + /// + /// + /// 成功返回当前值,失败null + /// + public static object? SetVarEx(string? key, string? value) + { + if (key == null) + throw new ArgumentNullException(nameof(key)); + if (value == null) + throw new ArgumentNullException(nameof(value)); + + var currentVar = Env.GetVar(key); + if (currentVar == null) + return null; + + object? valueType = currentVar.GetType().Name switch + { + "String" => value.Replace("\"", string.Empty), + "Double" => double.Parse(value), + "Int16" => short.Parse(value), + "Int32" => int.Parse(value), + _ => throw new NotImplementedException(), + }; + + // 相同的参数进行设置会发生一次异常 + if (currentVar.ToString().ToUpper() != valueType!.ToString().ToUpper()) + Env.SetVar(key, valueType); + + return currentVar; + } + + /// + /// 设置新系统变量,返回现有系统变量 + /// + /// 设置的变量词典 + /// 返回现有变量词典,然后下次就可以利用它进行设置回来了 + public static Dictionary SaveCadVar(Dictionary args) + { + if (args is null) + throw new ArgumentNullException(nameof(args)); + + var dict = new Dictionary(); + foreach (var item in args) + { + // 判断是否为系统变量 + var ok = SetVarEx(item.Key, item.Value); + if (ok != null) + { + dict.Add(item.Key, ok.ToString()); + continue; + } + + // 判断是否为系统变量 + var envstr = Env.GetEnv(item.Key); + if (!string.IsNullOrEmpty(envstr)) + { + Env.SetEnv(item.Key, item.Value); + dict.Add(item.Key, envstr); + } + } + return dict; + } + #endregion +} \ No newline at end of file diff --git a/src/IFoxCAD.Cad/Runtime/FileOpenMode.cs b/src/IFoxCAD.Cad/Runtime/FileOpenMode.cs index 5108dd5..912b531 100644 --- a/src/IFoxCAD.Cad/Runtime/FileOpenMode.cs +++ b/src/IFoxCAD.Cad/Runtime/FileOpenMode.cs @@ -1,4 +1,4 @@ -#if ac2008 //NET35 +#if ac2008 // NET35 namespace Autodesk.AutoCAD.DatabaseServices { [Wrapper("AcDbDatabase::OpenMode")] diff --git a/src/IFoxCAD.Cad/Runtime/IAutoGo.cs b/src/IFoxCAD.Cad/Runtime/IAutoGo.cs index 27ce550..c52e7c9 100644 --- a/src/IFoxCAD.Cad/Runtime/IAutoGo.cs +++ b/src/IFoxCAD.Cad/Runtime/IAutoGo.cs @@ -51,8 +51,8 @@ public IFoxInitialize(Sequence sequence = Sequence.Last, bool isInitialize = tru } } -//为了解决IExtensionApplication在一个dll内无法多次实现接口的关系 -//所以在这里反射加载所有的 IAutoGo ,以达到能分开写"启动运行"函数的目的 +// 为了解决IExtensionApplication在一个dll内无法多次实现接口的关系 +// 所以在这里反射加载所有的 IAutoGo ,以达到能分开写"启动运行"函数的目的 class RunClass { public Sequence Sequence { get; } @@ -75,22 +75,26 @@ public void Run() /// /// 此类作为加载后cad自动运行接口的一部分,用于反射特性和接口 -/// 启动cad后的执行顺序为: -/// 1:特性..(多个) -/// 2:接口..(多个) +/// +/// 启动cad后的执行顺序为:
    +/// 1:特性..(多个)
    +/// 2:接口..(多个) +///
    ///
    public class AutoReflection { - static List _InitializeList = new(); //储存方法用于初始化 - static List _TerminateList = new(); //储存方法用于结束释放 + static List _InitializeList = new(); // 储存方法用于初始化 + static List _TerminateList = new(); // 储存方法用于结束释放 readonly string _dllName; readonly AutoRegConfig _autoRegConfig; /// /// 反射执行 - /// 1.特性: - /// 2.接口: + /// + /// 1.特性:
    + /// 2.接口: + ///
    ///
    /// 约束在此dll进行加速 public AutoReflection(string dllName, AutoRegConfig configInfo) @@ -99,12 +103,12 @@ public AutoReflection(string dllName, AutoRegConfig configInfo) _autoRegConfig = configInfo; } - //启动cad的时候会自动执行 + // 启动cad的时候会自动执行 public void Initialize() { try { - //收集特性,包括启动时和关闭时 + // 收集特性,包括启动时和关闭时 if ((_autoRegConfig & AutoRegConfig.ReflectionAttribute) == AutoRegConfig.ReflectionAttribute) GetAttributeFunctions(_InitializeList, _TerminateList); @@ -113,7 +117,7 @@ public void Initialize() if (_InitializeList.Count > 0) { - //按照 SequenceId 排序_升序 + // 按照 SequenceId 排序_升序 _InitializeList = _InitializeList.OrderBy(runClass => runClass.Sequence).ToList(); RunFunctions(_InitializeList); } @@ -124,7 +128,7 @@ public void Initialize() } } - //关闭cad的时候会自动执行 + // 关闭cad的时候会自动执行 public void Terminate() { try @@ -134,13 +138,14 @@ public void Terminate() if (_TerminateList.Count > 0) { - //按照 SequenceId 排序_降序 + // 按照 SequenceId 排序_降序 _TerminateList = _TerminateList.OrderByDescending(runClass => runClass.Sequence).ToList(); RunFunctions(_TerminateList); } } - catch (System.Exception) + catch (System.Exception e) { + Env.Printl(e.Message); Debugger.Break(); } } @@ -159,22 +164,22 @@ public static void AppDomainGetTypes(Action action, string? dllNameWithout { var assemblies = AppDomain.CurrentDomain.GetAssemblies(); #if !NET35 - //cad2021出现如下报错 - //System.NotSupportedException:动态程序集中不支持已调用的成员 - //assemblies = assemblies.Where(p => !p.IsDynamic).ToArray();//这个要容器类型转换 + // cad2021出现如下报错 + // System.NotSupportedException:动态程序集中不支持已调用的成员 + // assemblies = assemblies.Where(p => !p.IsDynamic).ToArray();// 这个要容器类型转换 assemblies = Array.FindAll(assemblies, p => !p.IsDynamic); #endif - //主程序域 + // 主程序域 for (int ii = 0; ii < assemblies.Length; ii++) { var assembly = assemblies[ii]; - //获取类型集合,反射时候还依赖其他的dll就会这个错误 - //此通讯库要跳过,否则会报错. + // 获取类型集合,反射时候还依赖其他的dll就会这个错误 + // 此通讯库要跳过,否则会报错. var location = Path.GetFileNameWithoutExtension(assembly.Location); if (dllNameWithoutExtension != null && location != dllNameWithoutExtension) continue; - if (location == "AcInfoCenterConn")//通讯库 + if (location == "AcInfoCenterConn")// 通讯库 continue; Type[]? types = null; @@ -262,7 +267,6 @@ void GetInterfaceFunctions(List runClassList, string methodName) break; } }, _dllName); - } /// diff --git a/src/IFoxCAD.Cad/Runtime/Log.cs b/src/IFoxCAD.Cad/Runtime/Log.cs index 02293e2..fe05b5a 100644 --- a/src/IFoxCAD.Cad/Runtime/Log.cs +++ b/src/IFoxCAD.Cad/Runtime/Log.cs @@ -3,14 +3,15 @@ using System; using System.Diagnostics; using System.Threading; +using Exception = Exception; #region 写入日志到不同的环境中 -//https://zhuanlan.zhihu.com/p/338492989 +// https://zhuanlan.zhihu.com/p/338492989 public abstract class LogBase { - public abstract void ReadLog(string message); + public abstract void DeleteLog(); + public abstract string[] ReadLog(); public abstract void WriteLog(string message); - public abstract void DeleteLog(string message); } /// @@ -19,17 +20,21 @@ public abstract class LogBase public enum LogTarget { /// - /// 文件 + /// 文件(包含错误和备注) /// File = 1, /// + /// 文件(不包含错误,也就是只写备注信息) + /// + FileNotException = 2, + /// /// 数据库 /// - Database = 2, + Database = 4, /// /// windows日志 /// - EventLog = 4, + EventLog = 8, } /// @@ -37,19 +42,24 @@ public enum LogTarget /// public class FileLogger : LogBase { - public override void DeleteLog(string message) + public override void DeleteLog() { - throw new NotImplementedException(); + File.Delete(LogHelper.LogAddress); } - - public override void ReadLog(string message) + public override string[] ReadLog() { - throw new NotImplementedException(); + List lines = new(); + using (var sr = new StreamReader(LogHelper.LogAddress, true/*自动识别文件头*/)) + { + string line; + while ((line = sr.ReadLine()) != null) + lines.Add(line); + } + return lines.ToArray(); } - public override void WriteLog(string? message) { - //把异常信息输出到文件 + // 把异常信息输出到文件 var sw = new StreamWriter(LogHelper.LogAddress, true/*当天日志文件存在就追加,否则就创建*/); sw.Write(message); sw.Flush(); @@ -63,16 +73,14 @@ public override void WriteLog(string? message) /// public class DBLogger : LogBase { - public override void DeleteLog(string message) + public override void DeleteLog() { throw new NotImplementedException(); } - - public override void ReadLog(string message) + public override string[] ReadLog() { throw new NotImplementedException(); } - public override void WriteLog(string? message) { throw new NotImplementedException(); @@ -84,31 +92,39 @@ public override void WriteLog(string? message) /// public class EventLogger : LogBase { - // https://docs.microsoft.com/en-us/answers/questions/526018/windows-event-log-with-net-5.html - // net50要加 // 需要win权限 + // https://blog.csdn.net/weixin_38208401/article/details/77870909 + // NET50要加 + // https://docs.microsoft.com/en-us/answers/questions/526018/windows-event-log-with-net-5.html public string LogName = "IFoxCadLog"; - public override void DeleteLog(string message) + public override void DeleteLog() { #if !NET5_0 && !NET6_0 if (EventLog.Exists(LogName)) EventLog.Delete(LogName); #endif } - - public override void ReadLog(string message) + public override string[] ReadLog() { + List lines = new(); #if !NET5_0 && !NET6_0 - EventLog eventLog = new(); - eventLog.Log = LogName; - foreach (EventLogEntry entry in eventLog.Entries) + try + { + EventLog eventLog = new() + { + Log = LogName + }; + foreach (EventLogEntry entry in eventLog.Entries) + lines.Add(entry.Message); + } + catch (System.Security.SecurityException e) { - //Write your custom code here + throw new Exception("您没有权限读取win日志::" + e.Message); } #endif + return lines.ToArray(); } - public override void WriteLog(string? message) { #if !NET5_0 && !NET6_0 @@ -122,7 +138,7 @@ public override void WriteLog(string? message) } catch (System.Security.SecurityException e) { - throw new Exception("您没有权限写入win日志中" + e.Message); + throw new Exception("您没有权限写入win日志::" + e.Message); } #endif } @@ -146,7 +162,6 @@ public static class LogHelper /// 输出错误信息到vs输出窗口的开关 ///
    public static bool FlagOutVsOutput = true; - #pragma warning restore CA2211 // 非常量字段应当不可见 /// @@ -158,7 +173,7 @@ public static class LogHelper /// /// 提供给外部设置log文件保存路径 /// - /// 空的话就为运行的dll旁边的一个文件夹上 + /// null就生成默认配置 public static void OptionFile(string? newlogAddress = null) { _logWriteLock.EnterWriteLock();// 写模式锁定 读写锁 @@ -166,27 +181,7 @@ public static void OptionFile(string? newlogAddress = null) { LogAddress = newlogAddress; if (string.IsNullOrEmpty(LogAddress)) - { - //微软回复:静态构造函数只会被调用一次, - //并且在它执行完成之前,任何其它线程都不能创建这个类的实例或使用这个类的静态成员 - //https://blog.csdn.net/weixin_34204722/article/details/90095812 - var sb = new StringBuilder(); - sb.Append(Environment.CurrentDirectory); - sb.Append("\\ErrorLog"); - - //新建文件夹 - var path = sb.ToString(); - if (!Directory.Exists(path)) - { - Directory.CreateDirectory(path) - .Attributes = FileAttributes.Normal; //设置文件夹属性为普通 - } - - sb.Append('\\'); - sb.Append(DateTime.Now.ToString("yy-MM-dd")); - sb.Append(".log"); - LogAddress = sb.ToString(); - } + LogAddress = GetDefaultOption(DateTime.Now.ToString("yy-MM-dd") + ".log"); } finally { @@ -194,6 +189,37 @@ public static void OptionFile(string? newlogAddress = null) } } + /// + /// 输入文件名,获取保存路径的完整路径 + /// + /// 文件名,null获取默认路径 + /// 创建路径 + /// 完整路径 + public static string GetDefaultOption(string fileName, bool createDirectory = true) + { + // 微软回复:静态构造函数只会被调用一次, + // 并且在它执行完成之前,任何其它线程都不能创建这个类的实例或使用这个类的静态成员 + // https://blog.csdn.net/weixin_34204722/article/details/90095812 + var sb = new StringBuilder(); + sb.Append(Environment.CurrentDirectory); + sb.Append("\\ErrorLog"); + + // 新建文件夹 + if (createDirectory) + { + var path = sb.ToString(); + if (!Directory.Exists(path)) + { + // 设置文件夹属性为普通 + Directory.CreateDirectory(path) + .Attributes = FileAttributes.Normal; + } + } + sb.Append('\\'); + sb.Append(fileName); + return sb.ToString(); + } + public static string WriteLog(this string? message, LogTarget target = LogTarget.File) { @@ -219,21 +245,33 @@ public static string WriteLog(this Exception? exception, string? message, } - - static string LogAction(Exception? ex, string? message, LogTarget target) + /// 错误 + /// 备注信息 + /// 记录方式 + static string LogAction(Exception? ex, + string? message, + LogTarget target) { if (ex == null && message == null) return string.Empty; - if (target == LogTarget.File && LogAddress == null) - OptionFile(); + if (LogAddress == null) + { + if (target == LogTarget.File || + target == LogTarget.FileNotException) + OptionFile(); + } + + // 不写入错误 + if (target == LogTarget.FileNotException) + ex = null; try { _logWriteLock.EnterWriteLock();// 写模式锁定 读写锁 var logtxt = new LogTxt(ex, message); - //var logtxtJson = Newtonsoft.Json.JsonConvert.SerializeObject(logtxt, Formatting.Indented); + // var logtxtJson = Newtonsoft.Json.JsonConvert.SerializeObject(logtxt, Formatting.Indented); var logtxtJson = logtxt?.ToString(); if (logtxtJson == null) return string.Empty; @@ -247,6 +285,10 @@ static string LogAction(Exception? ex, string? message, LogTarget target) logger = new FileLogger(); logger.WriteLog(logtxtJson); break; + case LogTarget.FileNotException: + logger = new FileLogger(); + logger.WriteLog(logtxtJson); + break; case LogTarget.Database: logger = new DBLogger(); logger.WriteLog(logtxtJson); @@ -277,26 +319,25 @@ static string LogAction(Exception? ex, string? message, LogTarget target) [Serializable] public class LogTxt { - public string 当前时间; + public string? 当前时间; public string? 备注信息; public string? 异常信息; public string? 异常对象; public string? 触发方法; public string? 调用堆栈; - LogTxt() + public LogTxt() { } + + public LogTxt(Exception? ex, string? message) : this() { + if (ex == null && message == null) + throw new ArgumentNullException(nameof(ex)); + // 以不同语言显示日期 // DateTime.Now.ToString("f", new System.Globalization.CultureInfo("es-ES")) // DateTime.Now.ToString("f", new System.Globalization.CultureInfo("zh-cn")) // 为了最小信息熵,所以用这样的格式,并且我喜欢补0 当前时间 = DateTime.Now.ToString("yy-MM-dd hh:mm:ss"); - } - - public LogTxt(Exception? ex, string? message) : this() - { - if (ex == null && message == null) - throw new ArgumentNullException(nameof(ex)); if (ex != null) { @@ -328,7 +369,7 @@ public LogTxt(Exception? ex, string? message) : this() #endregion -#if false //最简单的实现 +#if false // 最简单的实现 public static class Log { /// @@ -344,19 +385,19 @@ public static class Log static Log() { - //微软回复:静态构造函数只会被调用一次, - //并且在它执行完成之前,任何其它线程都不能创建这个类的实例或使用这个类的静态成员 - //https://blog.csdn.net/weixin_34204722/article/details/90095812 + // 微软回复:静态构造函数只会被调用一次, + // 并且在它执行完成之前,任何其它线程都不能创建这个类的实例或使用这个类的静态成员 + // https://blog.csdn.net/weixin_34204722/article/details/90095812 var sb = new StringBuilder(); sb.Append(Environment.CurrentDirectory); sb.Append("\\ErrorLog"); - //新建文件夹 + // 新建文件夹 var path = sb.ToString(); if (!Directory.Exists(path)) { Directory.CreateDirectory(path) - .Attributes = FileAttributes.Normal; //设置文件夹属性为普通 + .Attributes = FileAttributes.Normal; // 设置文件夹属性为普通 } sb.Append('\\'); @@ -381,13 +422,13 @@ static Log() _logWriteLock.EnterWriteLock();// 写模式锁定 读写锁 var logtxt = new LogTxt(ex, remarks); - //var logtxtJson = Newtonsoft.Json.JsonConvert.SerializeObject(logtxt, Formatting.Indented); + // var logtxtJson = Newtonsoft.Json.JsonConvert.SerializeObject(logtxt, Formatting.Indented); var logtxtJson = logtxt.ToString(); if (logtxtJson == null) return string.Empty; - //把异常信息输出到文件 + // 把异常信息输出到文件 var sw = new StreamWriter(_logAddress, true/*当天日志文件存在就追加,否则就创建*/); sw.Write(logtxtJson); sw.Flush(); @@ -398,8 +439,8 @@ static Log() { Debug.WriteLine("错误日志: " + _logAddress); Debug.Write(logtxtJson); - //Debugger.Break(); - //Debug.Assert(false, "终止进程"); + // Debugger.Break(); + // Debug.Assert(false, "终止进程"); } return logtxtJson; } diff --git a/src/IFoxCAD.Cad/Runtime/MethodInfoHelper.cs b/src/IFoxCAD.Cad/Runtime/MethodInfoHelper.cs index c64a5f8..5f8e2bc 100644 --- a/src/IFoxCAD.Cad/Runtime/MethodInfoHelper.cs +++ b/src/IFoxCAD.Cad/Runtime/MethodInfoHelper.cs @@ -17,19 +17,19 @@ internal static class MethodInfoHelper object? result = null; if (methodInfo.IsStatic) { - //新函数指针进入此处 - //参数数量一定要匹配,为null则参数个数不同导致报错, - //参数为stirng[],则可以传入object[]代替,其他参数是否还可以实现默认构造? - var paramInfos = methodInfo.GetParameters(); + // 新函数指针进入此处 + // 参数数量一定要匹配,为null则参数个数不同导致报错, + // 参数为stirng[],则可以传入object[]代替,其他参数是否还可以实现默认构造? var args = new List { }; + var paramInfos = methodInfo.GetParameters(); for (int i = 0; i < paramInfos.Length; i++) args.Add(null!); - result = methodInfo.Invoke(null, args.ToArray());//静态调用 + result = methodInfo.Invoke(null, args.ToArray());// 静态调用 } else { - //原命令的函数指针进入此处 - //object instance; + // 原命令的函数指针进入此处 + // object instance; if (methodDic.ContainsKey(methodInfo)) instance = methodDic[methodInfo]; @@ -37,16 +37,16 @@ internal static class MethodInfoHelper { var reftype = methodInfo.ReflectedType; if (reftype == null) return null; - var fullName = reftype.FullName; //命名空间+类 + var fullName = reftype.FullName; // 命名空间+类 if (fullName == null) return null; - var type = reftype.Assembly.GetType(fullName);//通过程序集反射创建类+ + var type = reftype.Assembly.GetType(fullName);// 通过程序集反射创建类+ if (type == null) return null; instance = Activator.CreateInstance(type); - if (!type.IsAbstract)//无法创建抽象类成员 + if (!type.IsAbstract)// 无法创建抽象类成员 methodDic.Add(methodInfo, instance); } if (instance != null) - result = methodInfo.Invoke(instance, null); //非静态,调用实例化方法 + result = methodInfo.Invoke(instance, null); // 非静态,调用实例化方法 } return result; } diff --git a/src/IFoxCAD.Cad/Runtime/SymbolTable.cs b/src/IFoxCAD.Cad/Runtime/SymbolTable.cs index 11e1013..8f96bb9 100644 --- a/src/IFoxCAD.Cad/Runtime/SymbolTable.cs +++ b/src/IFoxCAD.Cad/Runtime/SymbolTable.cs @@ -1,20 +1,5 @@ namespace IFoxCAD.Cad; -/// -/// 委托执行状态 -/// -public enum DelegateState -{ - /// - /// 继续执行 - /// - Go, - /// - /// 中断执行 - /// - Break -} - public class SymbolTable : IEnumerable where TTable : SymbolTable where TRecord : SymbolTableRecord, new() @@ -44,11 +29,23 @@ public class SymbolTable : IEnumerable /// /// 事务管理器 /// 符号表id - internal SymbolTable(DBTrans tr, ObjectId tableId) + /// 默认行为:例如打开隐藏图层 + internal SymbolTable(DBTrans tr, ObjectId tableId, bool defaultBehavior = true) { DTrans = tr; Database = tr.Database; CurrentSymbolTable = DTrans.GetObject(tableId)!; + + if (defaultBehavior) + { + if (CurrentSymbolTable is LayerTable layer) + { + // 层表包含隐藏的,全部显示出来 + layer = layer.IncludingHidden; + if (layer is TTable tt) + CurrentSymbolTable = tt; + } + } } #endregion @@ -175,7 +172,7 @@ private static void Change(TRecord record, Action action) using (record.ForWrite()) action.Invoke(record); // 调用regen()函数可能会导致卡顿 - //Env.Editor.Regen(); + // Env.Editor.Regen(); } /// @@ -239,6 +236,7 @@ public IEnumerable GetRecords() /// /// 记录的名字集合 public IEnumerable GetRecordNames() => GetRecords().Select(record => record.Name); + /// /// 获取符合过滤条件的符号表记录名字集合 /// @@ -275,10 +273,10 @@ public ObjectId GetRecordFrom(SymbolTable table, string name, b using (ObjectIdCollection ids = new() { id }) { table.Database. - WblockCloneObjects(ids, + WblockCloneObjects(ids, CurrentSymbolTable.Id, idm, - DuplicateRecordCloning.Replace, + DuplicateRecordCloning.Replace, false); } rid = idm[id].Value; @@ -295,8 +293,8 @@ public ObjectId GetRecordFrom(SymbolTable table, string name, b /// 是否覆盖, 为覆盖, 为不覆盖 /// 对象id internal ObjectId GetRecordFrom(Func> tableSelector, - string fileName, - string name, + string fileName, + string name, bool over) { using var tr = new DBTrans(fileName); @@ -306,22 +304,50 @@ internal ObjectId GetRecordFrom(Func> tabl #region 遍历 /// - /// 遍历集合的迭代器,执行action委托 + /// 遍历集合的迭代器,执行action委托 /// - /// 集合值的类型 - /// 集合 /// 要运行的委托 - public void ForEach(Action action, OpenMode openMode = OpenMode.ForRead) + /// 打开模式,默认为只读 + /// 检查id是否删除,默认false + public void ForEach(Action action, + OpenMode openMode = OpenMode.ForRead, + bool checkIdOk = false) { - //GetRecords().ForEach(re => action(re)); - - foreach (var item in this) + foreach (var id in this) { - var record = GetRecord(item, openMode); + if (checkIdOk && !id.IsOk()) + continue; + var record = GetRecord(id, openMode); if (record is not null) action(record); } } + + + /// + /// 遍历集合的迭代器,执行action委托 + /// + /// 要执行的委托 + /// 打开模式,默认为只读 + /// 检查id是否删除,默认false + public void ForEach(Action action, + OpenMode openMode = OpenMode.ForRead, + bool checkIdOk = false) + { + LoopState state = new();/*这种方式比Action改Func更友好*/ + foreach (var id in this) + { + if (checkIdOk && !id.IsOk()) + continue; + var record = GetRecord(id, openMode); + if (record is not null) + action(record, state); + if (state.IsBreak) + break; + } + } + + #endregion #region IEnumerable 成员 @@ -337,4 +363,4 @@ IEnumerator IEnumerable.GetEnumerator() return GetEnumerator(); } #endregion -} +} \ No newline at end of file diff --git a/src/IFoxCAD.Cad/Runtime/Utils.cs b/src/IFoxCAD.Cad/Runtime/Utils.cs index eb372ab..28c47c2 100644 --- a/src/IFoxCAD.Cad/Runtime/Utils.cs +++ b/src/IFoxCAD.Cad/Runtime/Utils.cs @@ -1,8 +1,8 @@ -using System; +namespace IFoxCAD.Cad; -namespace IFoxCAD.Cad; +using System; -public class Helper +public class DBTransHelper { /* * id = db.GetObjectId(false, handle, 0); @@ -45,22 +45,22 @@ public class Helper static int GetAcDbObjectId(IntPtr db, Handle handle, out ObjectId id, bool createIfNotFound = false, uint reserved = 0) { id = ObjectId.Null; - switch (Application.Version.Major) + switch (Acap.Version.Major) { case 17: - { - if (IntPtr.Size == 4) - return getAcDbObjectId17x32(db, out id, createIfNotFound, ref handle, reserved); - else - return getAcDbObjectId17x64(db, out id, createIfNotFound, ref handle, reserved); - } + { + if (IntPtr.Size == 4) + return getAcDbObjectId17x32(db, out id, createIfNotFound, ref handle, reserved); + else + return getAcDbObjectId17x64(db, out id, createIfNotFound, ref handle, reserved); + } case 18: - { - if (IntPtr.Size == 4) - return getAcDbObjectId18x32(db, out id, createIfNotFound, ref handle, reserved); - else - return getAcDbObjectId18x64(db, out id, createIfNotFound, ref handle, reserved); - } + { + if (IntPtr.Size == 4) + return getAcDbObjectId18x32(db, out id, createIfNotFound, ref handle, reserved); + else + return getAcDbObjectId18x64(db, out id, createIfNotFound, ref handle, reserved); + } } return -1; } @@ -74,18 +74,18 @@ static int GetAcDbObjectId(IntPtr db, Handle handle, out ObjectId id, bool creat public static ObjectId TryGetObjectId(Database db, Handle handle) { #if !NET35 - //高版本直接利用 + // 高版本直接利用 var es = db.TryGetObjectId(handle, out ObjectId id); - //if (!es) + // if (!es) #else var es = GetAcDbObjectId(db.UnmanagedObject, handle, out ObjectId id); - //if (ErrorStatus.OK != (ErrorStatus)es) + // if (ErrorStatus.OK != (ErrorStatus)es) #endif return id; } - //public static int GetCadFileVersion(string filename) - //{ + // public static int GetCadFileVersion(string filename) + // { // var bytes = File.ReadAllBytes(filename); // var headstr = Encoding.Default.GetString(bytes)[0..6]; // if (!headstr.StartsWith("AC")) return 0; @@ -93,6 +93,6 @@ public static ObjectId TryGetObjectId(Database db, Handle handle) // var a = Enum.Parse(typeof(DwgVersion), "AC1800"); // Enum.TryParse() // return vernum + 986; - - //} + + // } } \ No newline at end of file diff --git a/src/IFoxCAD.WPF/EventBindingExtension.cs b/src/IFoxCAD.WPF/EventBindingExtension.cs index eb58c75..7352143 100644 --- a/src/IFoxCAD.WPF/EventBindingExtension.cs +++ b/src/IFoxCAD.WPF/EventBindingExtension.cs @@ -65,14 +65,14 @@ public class EventBindingExtension : MarkupExtension Type? eventHandlerType = null; if (memberInfo is EventInfo eventInfo) { - //var info = memberInfo as EventInfo; - //var eventInfo = info; + // var info = memberInfo as EventInfo; + // var eventInfo = info; eventHandlerType = eventInfo.EventHandlerType; } else if (memberInfo is MethodInfo methodInfo) { - //var info = memberInfo as MethodInfo; - //var methodInfo = info; + // var info = memberInfo as MethodInfo; + // var methodInfo = info; ParameterInfo[] pars = methodInfo.GetParameters(); eventHandlerType = pars[1].ParameterType; } diff --git a/src/IFoxCAD.WPF/IFoxCAD.WPF.csproj b/src/IFoxCAD.WPF/IFoxCAD.WPF.csproj index 9fa74ff..4028640 100644 --- a/src/IFoxCAD.WPF/IFoxCAD.WPF.csproj +++ b/src/IFoxCAD.WPF/IFoxCAD.WPF.csproj @@ -4,12 +4,12 @@ preview enable - net45 + NET45 true true true true - 0.3.0 + 0.4 xsfhlzh;vicwjb InspireFunction WPF的简单MVVM模式开发类库 @@ -22,7 +22,7 @@ 开启可空类型. - + DEBUG;TRACE diff --git a/tests/Test/CmdINI.cs b/tests/Test/CmdINI.cs index 7a882ec..1370267 100644 --- a/tests/Test/CmdINI.cs +++ b/tests/Test/CmdINI.cs @@ -1,37 +1,56 @@ -namespace Test; +namespace Test; /// /// 注册中心(自动执行接口): -/// 用于启动cad后写入启动注册表及反射调用以下特性和接口 -/// netload的工程必须继承虚函数后才能使用特性和接口 -/// 启动cad后的执行顺序为: -/// 1:构造函数 -/// 2:特性..多个 -/// 3:接口..多个 -/// 4:本类的构造函数 +/// +/// 继承虚函数后才能使用
    +/// 0x01 netload加载之后自动执行,写入启动注册表,下次就不需要netload了
    +/// 0x02 反射调用特性和接口
    +/// 启动cad后的执行顺序为:
    +/// 1:构造函数
    +/// 2:特性..多个
    +/// 3:接口..多个
    +/// 4:本类的构造函数
    +/// +/// **** 警告 **** +/// 如果不写一个 储存这个对象, +/// 而是直接写卸载命令在此, +/// 第一次加载的时候会初始化完成,然后这个类生命就结束了, +/// 第二次通过命令进入,会引发构造函数再次执行,留意构造函数的打印信息即可发现 +/// +///
    ///
    -public class CmdINI : AutoRegAssem +public class AutoRegAssemEx : AutoRegAssem { - public CmdINI() : base(AutoRegConfig.All) + public AutoRegAssemEx() : base(AutoRegConfig.All) { var dm = Acap.DocumentManager; var doc = dm.MdiActiveDocument; var ed = doc.Editor; - ed.WriteMessage($"\n {nameof(CmdINI)}构造函数,开始自动执行\r\n"); + ed.WriteMessage($"\n {nameof(AutoRegAssemEx)}构造函数,开始自动执行\r\n"); + + CmdInit.AutoRegAssemEx = this; } +} + +public class CmdInit +{ + public static AutoRegAssemEx? AutoRegAssemEx; - ///如果netload之后用 删除注册表, - ///由于不是也不能卸载dll,再netload是无法执行自动接口的, - ///所以此时会产生无法再注册的问题...因此需要暴露此注册函数(硬来) + /// 如果netload之后用 删除注册表, + /// 由于不是也不能卸载dll,再netload是无法执行自动接口的, + /// 所以此时会产生无法再注册的问题...因此需要暴露此注册函数(硬来) [CommandMethod("IFoxAddReg")] public void IFoxAddReg() { - base.RegApp(); - var dm = Acap.DocumentManager; var doc = dm.MdiActiveDocument; var ed = doc.Editor; ed.WriteMessage($"\n 加入注册表"); + + if (AutoRegAssemEx is null) + AutoRegAssemEx = new(); + AutoRegAssemEx.RegApp(); } /// @@ -40,13 +59,14 @@ public void IFoxAddReg() [CommandMethod("IFoxRemoveReg")] public void IFoxRemoveReg() { - //执行命令的时候会再次执行构造函数(导致初始化两次),但是再次执行就不会了 - base.UnRegApp(); - var dm = Acap.DocumentManager; var doc = dm.MdiActiveDocument; var ed = doc.Editor; ed.WriteMessage($"\n 卸载注册表"); + + // 防止卸载两次,不然会报错的 + AutoRegAssemEx?.UnRegApp(); + AutoRegAssemEx = null; } } @@ -86,28 +106,69 @@ public void Initialize() [IFoxInitialize(isInitialize: false)] public void Terminate() { - //try - //{ + // try + // { // var dm = Acap.DocumentManager; // var doc = dm.MdiActiveDocument; - // var ed = doc.Editor; //注意此时编辑器已经回收,所以此句没用,并引发错误 + // var ed = doc.Editor; // 注意此时编辑器已经回收,所以此句没用,并引发错误 // ed.WriteMessage("\n 结束自动执行 Terminate \r\n"); - //} - //catch (System.Exception) - //{ - //} + // } + // catch (System.Exception) + // { + // } } - //[IFoxInitialize] - //public void Initialize() - //{ - // //文档管理器将比此接口前创建,因此此句会执行 + // [IFoxInitialize] + // public void Initialize() + // { + // // 文档管理器将比此接口前创建,因此此句会执行 // Acap.DocumentManager.MdiActiveDocument.Editor.WriteMessage("\nload...."); - //} - //[IFoxInitialize(Sequence.First, false)] - //public void Terminate() - //{ - // //文档管理器将比此接口前死亡,因此此句不会执行 + // } + // [IFoxInitialize(Sequence.First, false)] + // public void Terminate() + // { + // // 文档管理器将比此接口前死亡,因此此句不会执行 // Acap.DocumentManager.MdiActiveDocument?.Editor.WriteMessage("\nunload...."); - //} + // } +} + +public partial class Test +{ + [CommandMethod(nameof(Test_GetEnv))] + public static void Test_GetEnv() + { + var dir = Env.GetEnv("PrinterConfigDir"); + Env.Printl("pc3打印机位置:" + dir); + + Env.SetEnv("abc", "656"); + + var obj = Env.GetEnv("abc"); + Env.Printl("GetEnv:" + obj); + + Env.Printl("GetEnv:" + Env.GetEnv("abc")); + Env.Printl("GetEnv PATH:" + Env.GetEnv("PATH")); + } + + +#if !NET35 && !NET40 + + // 通过此功能获取全部变量,尚不清楚此处如何设置,没有通过测试 + [CommandMethod(nameof(Test_GetvarAll))] + public static void Test_GetvarAll() + { + GetvarAll(); + } + + public static Dictionary GetvarAll() + { + var dict = new Dictionary(); + var en = new SystemVariableEnumerator(); + while (en.MoveNext()) + { + Console.WriteLine(en.Current.Name + "-----" + en.Current.Value);//Value会出现异常 + dict.Add(en.Current.Name, en.Current.Value); + } + return dict; + } +#endif } \ No newline at end of file diff --git a/tests/Test/Test.cs b/tests/Test/Test.cs index 8cb7f78..ceea8de 100644 --- a/tests/Test/Test.cs +++ b/tests/Test/Test.cs @@ -1,12 +1,14 @@ namespace Test; #pragma warning disable CS0219 // 变量已被赋值,但从未使用过它的值 -public class Test +public partial class Test { [CommandMethod("dbtest")] public void Dbtest() { using var tr = new DBTrans(); + if (tr.Editor is null) + return; tr.Editor.WriteMessage("\n测试 Editor 属性是否工作!"); tr.Editor.WriteMessage("\n----------开始测试--------------"); tr.Editor.WriteMessage("\n测试document属性是否工作"); @@ -22,17 +24,17 @@ public void Dbtest() Line line = new(new Point3d(0, 0, 0), new Point3d(1, 1, 0)); Circle circle = new(new Point3d(0, 0, 0), Vector3d.ZAxis, 2); - //var lienid = tr.AddEntity(line); - //var cirid = tr.AddEntity(circle); - //var linent = tr.GetObject(lienid); - //var lineent = tr.GetObject(cirid); - //var linee = tr.GetObject(cirid); //经测试,类型不匹配,返回null - //var dd = tr.GetObject(lienid); - //List ds = new() { linee, dd }; - //tr.CurrentSpace.AddEntity(line,tr); + // var lienid = tr.AddEntity(line); + // var cirid = tr.AddEntity(circle); + // var linent = tr.GetObject(lienid); + // var lineent = tr.GetObject(cirid); + // var linee = tr.GetObject(cirid); // 经测试,类型不匹配,返回null + // var dd = tr.GetObject(lienid); + // List ds = new() { linee, dd }; + // tr.CurrentSpace.AddEntity(line,tr); } - //add entity test + // add entity test [CommandMethod("addent")] public void Addent() { @@ -49,32 +51,32 @@ public void Addent() public void Drawarc() { using var tr = new DBTrans(); - Arc arc1 = EntityEx.CreateArcSCE(new Point3d(2, 0, 0), new Point3d(0, 0, 0), new Point3d(0, 2, 0));//起点,圆心,终点 - Arc arc2 = EntityEx.CreateArc(new Point3d(4, 0, 0), new Point3d(0, 0, 0), Math.PI / 2); //起点,圆心,弧度 - Arc arc3 = EntityEx.CreateArc(new Point3d(1, 0, 0), new Point3d(0, 0, 0), new Point3d(0, 1, 0)); //起点,圆上一点,终点 + Arc arc1 = EntityEx.CreateArcSCE(new Point3d(2, 0, 0), new Point3d(0, 0, 0), new Point3d(0, 2, 0));// 起点,圆心,终点 + Arc arc2 = EntityEx.CreateArc(new Point3d(4, 0, 0), new Point3d(0, 0, 0), Math.PI / 2); // 起点,圆心,弧度 + Arc arc3 = EntityEx.CreateArc(new Point3d(1, 0, 0), new Point3d(0, 0, 0), new Point3d(0, 1, 0)); // 起点,圆上一点,终点 tr.CurrentSpace.AddEntity(arc1, arc2, arc3); - tr.CurrentSpace.AddArc(new Point3d(0, 0, 0), new Point3d(1, 1, 0), new Point3d(2, 0, 0));//起点,圆上一点,终点 + tr.CurrentSpace.AddArc(new Point3d(0, 0, 0), new Point3d(1, 1, 0), new Point3d(2, 0, 0));// 起点,圆上一点,终点 } [CommandMethod("drawcircle")] public void DraCircle() { using var tr = new DBTrans(); - Circle circle1 = EntityEx.CreateCircle(new Point3d(0, 0, 0), new Point3d(1, 0, 0)); //起点,终点 - Circle circle2 = EntityEx.CreateCircle(new Point3d(-2, 0, 0), new Point3d(2, 0, 0), new Point3d(0, 2, 0));//三点画圆,成功 - Circle circle3 = EntityEx.CreateCircle(new Point3d(-2, 0, 0), new Point3d(0, 0, 0), new Point3d(2, 0, 0));//起点,圆心,终点,失败 - tr.CurrentSpace.AddEntity(circle1, circle2); + var circle1 = EntityEx.CreateCircle(new Point3d(0, 0, 0), new Point3d(1, 0, 0)); // 起点,终点 + var circle2 = EntityEx.CreateCircle(new Point3d(-2, 0, 0), new Point3d(2, 0, 0), new Point3d(0, 2, 0));// 三点画圆,成功 + var circle3 = EntityEx.CreateCircle(new Point3d(-2, 0, 0), new Point3d(0, 0, 0), new Point3d(2, 0, 0));// 起点,圆心,终点,失败 + tr.CurrentSpace.AddEntity(circle1, circle2!); if (circle3 is not null) { tr.CurrentSpace.AddEntity(circle3); } else { - tr.Editor.WriteMessage("三点画圆失败"); + tr.Editor?.WriteMessage("三点画圆失败"); } - tr.CurrentSpace.AddEntity(circle3); - tr.CurrentSpace.AddCircle(new Point3d(0, 0, 0), new Point3d(1, 1, 0), new Point3d(2, 0, 0));//三点画圆,成功 - tr.CurrentSpace.AddCircle(new Point3d(0, 0, 0), new Point3d(1, 1, 0), new Point3d(2, 2, 0));//起点,圆上一点,终点(共线) + tr.CurrentSpace.AddEntity(circle3!); + tr.CurrentSpace.AddCircle(new Point3d(0, 0, 0), new Point3d(1, 1, 0), new Point3d(2, 0, 0));// 三点画圆,成功 + tr.CurrentSpace.AddCircle(new Point3d(0, 0, 0), new Point3d(1, 1, 0), new Point3d(2, 2, 0));// 起点,圆上一点,终点(共线) } [CommandMethod("layertest")] @@ -95,7 +97,7 @@ public void Layertest() } - //添加图层 + // 添加图层 [CommandMethod("layerAdd1")] public void Layertest1() { @@ -103,29 +105,29 @@ public void Layertest1() tr.LayerTable.Add("test1", Color.FromColorIndex(ColorMethod.ByColor, 1)); } - //添加图层 + // 添加图层 [CommandMethod("layerAdd2")] public void Layertest2() { using var tr = new DBTrans(); tr.LayerTable.Add("test2", 2); - //tr.LayerTable["3"] = new LayerTableRecord(); + // tr.LayerTable["3"] = new LayerTableRecord(); } - //删除图层 + // 删除图层 [CommandMethod("layerdel")] public void LayerDel() { using var tr = new DBTrans(); - Env.Editor.WriteMessage(tr.LayerTable.Delete("0").ToString()); //删除图层 0 - Env.Editor.WriteMessage(tr.LayerTable.Delete("Defpoints").ToString());//删除图层 Defpoints - Env.Editor.WriteMessage(tr.LayerTable.Delete("1").ToString()); //删除不存在的图层 1 - Env.Editor.WriteMessage(tr.LayerTable.Delete("2").ToString()); //删除有图元的图层 2 - Env.Editor.WriteMessage(tr.LayerTable.Delete("3").ToString()); //删除图层 3 + Env.Editor.WriteMessage(tr.LayerTable.Delete("0").ToString()); // 删除图层 0 + Env.Editor.WriteMessage(tr.LayerTable.Delete("Defpoints").ToString());// 删除图层 Defpoints + Env.Editor.WriteMessage(tr.LayerTable.Delete("1").ToString()); // 删除不存在的图层 1 + Env.Editor.WriteMessage(tr.LayerTable.Delete("2").ToString()); // 删除有图元的图层 2 + Env.Editor.WriteMessage(tr.LayerTable.Delete("3").ToString()); // 删除图层 3 - tr.LayerTable.Remove("2"); //测试是否能强制删除 + tr.LayerTable.Remove("2"); // 测试是否能强制删除 } - //添加直线 + // 添加直线 [CommandMethod("linedemo1")] public void AddLine1() { @@ -152,7 +154,7 @@ public void AddLine1() tr.CurrentSpace.AddEntity(line2, line3, circle); } - //增加多段线1 + // 增加多段线1 [CommandMethod("Pldemo1")] public void AddPolyline1() { @@ -169,7 +171,7 @@ public void AddPolyline1() tr.CurrentSpace.AddEntity(pl); } - //增加多段线2 + // 增加多段线2 [CommandMethod("pldemo2")] public void Addpl2() { @@ -195,20 +197,28 @@ public void Addpl2() public void AddXdata() { using var tr = new DBTrans(); - var appname = "myapp"; + var appname = "myapp2"; + tr.RegAppTable.Add("myapp1"); tr.RegAppTable.Add(appname); // add函数会默认的在存在这个名字的时候返回这个名字的regapp的id,不存在就新建 - tr.RegAppTable.Add("myapp2"); + tr.RegAppTable.Add("myapp3"); + tr.RegAppTable.Add("myapp4"); var line = new Line(new Point3d(0, 0, 0), new Point3d(1, 1, 0)) { XData = new XDataList() { - { DxfCode.ExtendedDataRegAppName, appname }, //可以用dxfcode和int表示组码 - { DxfCode.ExtendedDataAsciiString, "hahhahah" }, + { DxfCode.ExtendedDataRegAppName, "myapp1" }, // 可以用dxfcode和int表示组码 + { DxfCode.ExtendedDataAsciiString, "xxxxxxx" }, + {1070, 12 }, + { DxfCode.ExtendedDataRegAppName, appname }, // 可以用dxfcode和int表示组码,移除中间的测试 + { DxfCode.ExtendedDataAsciiString, "要移除的我" }, {1070, 12 }, - { DxfCode.ExtendedDataRegAppName, "myapp2" }, //可以用dxfcode和int表示组码 - { DxfCode.ExtendedDataAsciiString, "hahhahah" }, + { DxfCode.ExtendedDataRegAppName, "myapp3" }, // 可以用dxfcode和int表示组码 + { DxfCode.ExtendedDataAsciiString, "aaaaaaaaa" }, + {1070, 12 }, + { DxfCode.ExtendedDataRegAppName, "myapp4" }, // 可以用dxfcode和int表示组码 + { DxfCode.ExtendedDataAsciiString, "bbbbbbbbb" }, {1070, 12 } } }; @@ -223,19 +233,19 @@ public void GetXdata() var ed = doc.Editor; using var tr = new DBTrans(); tr.RegAppTable.ForEach(id => - id.GetObject().Name.Print()); + id.GetObject()?.Name.Print()); tr.RegAppTable.GetRecords().ForEach(rec => rec.Name.Print()); tr.RegAppTable.GetRecordNames().ForEach(name => name.Print()); tr.RegAppTable.ForEach(re => re.Name.Print()); - //var res = ed.GetEntity("\n select the entity:"); - //if (res.Status == PromptStatus.OK) - //{ + // var res = ed.GetEntity("\n select the entity:"); + // if (res.Status == PromptStatus.OK) + // { // using var tr = new DBTrans(); // tr.RegAppTable.ForEach(id => id.GetObject().Print()); // var data = tr.GetObject(res.ObjectId).XData; // ed.WriteMessage(data.ToString()); - //} + // } } [CommandMethod("changexdata")] @@ -248,26 +258,29 @@ public void Changexdata() if (res.Status == PromptStatus.OK) { using var tr = new DBTrans(); - var data = tr.GetObject(res.ObjectId); + var data = tr.GetObject(res.ObjectId)!; data.ChangeXData(appname, DxfCode.ExtendedDataAsciiString, "change"); ed.WriteMessage(data.XData.ToString()); } } + + [CommandMethod("removexdata")] public void Removexdata() { var doc = Acap.DocumentManager.MdiActiveDocument; var ed = doc.Editor; - var appname = "myapp"; + var appname = "myapp2"; var res = ed.GetEntity("\n select the entity:"); if (res.Status == PromptStatus.OK) { using var tr = new DBTrans(); - var data = tr.GetObject(res.ObjectId); - data.RemoveXData(appname, DxfCode.ExtendedDataAsciiString); + var ent = tr.GetObject(res.ObjectId); + ed.WriteMessage("\n移除前:" + ent?.XData.ToString()); - ed.WriteMessage(data.XData.ToString()); + ent?.RemoveXData(appname, DxfCode.ExtendedDataAsciiString); + ed.WriteMessage("\n移除后:" + ent?.XData.ToString()); } } @@ -277,11 +290,11 @@ public void PrintLayerName() using var tr = new DBTrans(); foreach (var layerRecord in tr.LayerTable.GetRecords()) { - tr.Editor.WriteMessage(layerRecord.Name); + tr.Editor?.WriteMessage(layerRecord.Name); } foreach (var layerRecord in tr.LayerTable.GetRecords()) { - tr.Editor.WriteMessage(layerRecord.Name); + tr.Editor?.WriteMessage(layerRecord.Name); break; } } diff --git a/tests/Test/Test.csproj b/tests/Test/Test.csproj index 8e3469a..3768cfb 100644 --- a/tests/Test/Test.csproj +++ b/tests/Test/Test.csproj @@ -2,19 +2,19 @@ preview - + enable - net45 + NET45 true true x64 - + 1701;1702;CS1685 - + 1701;1702;CS1685 diff --git a/tests/Test/TestAOP.cs b/tests/Test/TestAOP.cs index 4723f69..2b9169f 100644 --- a/tests/Test/TestAOP.cs +++ b/tests/Test/TestAOP.cs @@ -1,7 +1,7 @@ namespace Test; -//被注入的函数将不能使用断点, -//因此用户要充分了解才能使用 +// 被注入的函数将不能使用断点, +// 因此用户要充分了解才能使用 #if false /* * 类库用户想侵入的命名空间是用户的, @@ -27,35 +27,35 @@ namespace Test */ public class AopTestClass { - //类不拒绝,这里拒绝 + // 类不拒绝,这里拒绝 [IFoxRefuseInjectionTransaction] [CommandMethod("IFoxRefuseInjectionTransaction")] public void IFoxRefuseInjectionTransaction() { } - //不拒绝 + // 不拒绝 [CommandMethod("InjectionTransaction")] public void InjectionTransaction() { - //怎么用事务呢? - //直接用 DBTrans.Top + // 怎么用事务呢? + // 直接用 DBTrans.Top var dBTrans = new DBTrans(); dBTrans.Commit(); } } - //拒绝注入事务,写类上,则方法全都拒绝 + // 拒绝注入事务,写类上,则方法全都拒绝 [IFoxRefuseInjectionTransaction] public class AopTestClassRefuseInjection { - //此时这个也是拒绝的..这里加特性只是无所谓 + // 此时这个也是拒绝的..这里加特性只是无所谓 [IFoxRefuseInjectionTransaction] [CommandMethod("IFoxRefuseInjectionTransaction2")] public void IFoxRefuseInjectionTransaction2() { - //拒绝注入就要自己开事务,通常用在循环提交事务上面. - //另见 报错0x02 https://www.cnblogs.com/JJBox/p/10798940.html + // 拒绝注入就要自己开事务,通常用在循环提交事务上面. + // 另见 报错0x02 https://www.cnblogs.com/JJBox/p/10798940.html using var tr = new DBTrans(); } @@ -65,4 +65,4 @@ public void InjectionTransaction2() } } } -#endif +#endif \ No newline at end of file diff --git a/tests/Test/TestCurve.cs b/tests/Test/TestCurve.cs index 2606a83..4a80837 100644 --- a/tests/Test/TestCurve.cs +++ b/tests/Test/TestCurve.cs @@ -1,4 +1,6 @@ -namespace Test; +using Autodesk.AutoCAD.DatabaseServices; + +namespace Test; public class TestGraph { @@ -29,15 +31,12 @@ public void TestGraph1() if (ents == null) return; Tools.TestTimes2(1, "new", () => { - - - var res = ents.GetAllCycle(); + var res = ents!.GetAllCycle(); - //res.ForEach((i, t) => t.ForWrite(e => e.ColorIndex = i + 1)); + // res.ForEach((i, t) => t.ForWrite(e => e.ColorIndex = i + 1)); Env.Print(res.Count()); - tr.CurrentSpace.AddEntity(res); + tr.CurrentSpace.AddEntity(res); }); - } [CommandMethod("testgraphspeed")] @@ -48,17 +47,17 @@ public void TestGraphspeed() if (ents == null) return; - var graph = new IFoxCAD.Cad.Graph(); // 为了调试先把图的访问改为internal - foreach (var curve in ents) { - - graph.AddEdge(curve.GetGeCurve()); - - +#if NET35 + graph.AddEdge(curve!.ToCurve3d()!); +#else + graph.AddEdge(curve!.GetGeCurve()); +#endif } - //新建 dfs + + // 新建 dfs var dfs = new DepthFirst(); #if true Tools.TestTimes2(100, "new", () => { @@ -78,11 +77,10 @@ public void TestGraphspeed() // 查询全部的 闭合环 dfs.FindAll(graph); }); -#endif - //res.ForEach((i, t) => t.ForWrite(e => e.ColorIndex = i + 1)); - - //tr.CurrentSpace.AddEntity(res); +#endif + // res.ForEach((i, t) => t.ForWrite(e => e.ColorIndex = i + 1)); + // tr.CurrentSpace.AddEntity(res); } } @@ -95,8 +93,10 @@ public class TestCurve public void TestBreakCurve() { using var tr = new DBTrans(); - var ents = Env.Editor.SSGet().Value.GetEntities(); - var tt = CurveEx.BreakCurve(ents.ToList()); + var ents = Env.Editor.SSGet()?.Value.GetEntities(); + if (ents is null) + return; + var tt = CurveEx.BreakCurve(ents.ToList()!); tt.ForEach(t => t.ForWrite(e => e.ColorIndex = 1)); tr.CurrentSpace.AddEntity(tt); } @@ -105,22 +105,23 @@ public void TestBreakCurve() public void TestCurveCurveIntersector3d() { using var tr = new DBTrans(); - var ents = Env.Editor.SSGet().Value.GetEntities() - .Select(e => e.ToCompositeCurve3d()).ToList(); + var ents = Env.Editor.SSGet()? + .Value.GetEntities() + .Select(e => e?.ToCompositeCurve3d()).ToList(); + if (ents == null) + return; var cci3d = new CurveCurveIntersector3d(); - - for (int i = 0; i < ents.Count; i++) { var gc1 = ents[i]; - var int1 = gc1.GetInterval(); - //var pars1 = paramss[i]; + var int1 = gc1?.GetInterval(); + // var pars1 = paramss[i]; for (int j = i; j < ents.Count; j++) { var gc2 = ents[j]; - //var pars2 = paramss[j]; - var int2 = gc2.GetInterval(); + // var pars2 = paramss[j]; + var int2 = gc2?.GetInterval(); cci3d.Set(gc1, gc2, int1, int2, Vector3d.ZAxis); var d = cci3d.OverlapCount(); var a = cci3d.GetIntersectionRanges(); @@ -133,23 +134,19 @@ public void TestCurveCurveIntersector3d() for (int k = 0; k < cci3d.NumberOfIntersectionPoints; k++) { - //var a = cci3d.GetOverlapRanges(k); - //var b = cci3d.IsTangential(k); - //var c = cci3d.IsTransversal(k); - //var d = cci3d.OverlapCount(); - //var e = cci3d.OverlapDirection(); + // var a = cci3d.GetOverlapRanges(k); + // var b = cci3d.IsTangential(k); + // var c = cci3d.IsTransversal(k); + // var d = cci3d.OverlapCount(); + // var e = cci3d.OverlapDirection(); var pt = cci3d.GetIntersectionParameters(k); var pts = cci3d.GetIntersectionPoint(k); Env.Print(pts); } - - - } - } // var tt = CurveEx.Topo(ents.ToList()); - //tt.ForEach(t => t.ForWrite(e => e.ColorIndex = 1)); - //tr.CurrentSpace.AddEntity(tt); + // tt.ForEach(t => t.ForWrite(e => e.ColorIndex = 1)); + // tr.CurrentSpace.AddEntity(tt); } -} +} \ No newline at end of file diff --git a/tests/Test/TestDBTrans.cs b/tests/Test/TestDBTrans.cs index f989fd5..5ffefa8 100644 --- a/tests/Test/TestDBTrans.cs +++ b/tests/Test/TestDBTrans.cs @@ -2,52 +2,80 @@ public class TestTrans { + // 后台:不存在路径的dwg会在桌面进行临时保存 + [CommandMethod(nameof(FileNotExist))] + public void FileNotExist() + { + using var tr = new DBTrans("test.dwg"); + tr.SaveFile((DwgVersion)24, false); + } + + // 前台:由于是弹出面板,此时路径不会起任何作用 + [CommandMethod(nameof(FileNotExist2))] + public void FileNotExist2() + { + using var tr = new DBTrans(); + tr.SaveFile(saveAsFile: "D:\\"); + } + + // 后台:只有路径,没有文件名 + [CommandMethod(nameof(FileNotExist3))] + public void FileNotExist3() + { + using var tr = new DBTrans("D:\\"); + tr.SaveDwgFile(); + + using var tr2 = new DBTrans("D:\\"); + tr2.SaveFile(saveAsFile: "D:\\"); + } + + [CommandMethod("testtr")] public void Testtr() { string filename = @"C:\Users\vic\Desktop\test.dwg"; using var tr = new DBTrans(filename); tr.ModelSpace.AddCircle(new Point3d(10, 10, 0), 20); - //tr.Database.SaveAs(filename,DwgVersion.Current); + // tr.Database.SaveAs(filename,DwgVersion.Current); tr.SaveDwgFile(); } [CommandMethod("testifoxcommit")] public void Testifoxcommit() { - + using var tr = new DBTrans(); tr.ModelSpace.AddCircle(new Point3d(0, 0, 0), 20); tr.Abort(); - //tr.Commit(); + // tr.Commit(); } // AOP 应用 预计示例: // 1. 无参数 - //[AOP] - //[CommandMethod("TESTAOP")] - //public void testaop() - //{ + // [AOP] + // [CommandMethod("TESTAOP")] + // public void testaop() + // { // // 不用 using var tr = new DBTrans(); // var tr = DBTrans.Top; // tr.ModelSpace.AddCircle(new Point3d(0, 0, 0), 20); - //} + // } // 2. 有参数 - //[AOP("file")] - //[CommandMethod("TESTAOP")] - //public void testaop() - //{ + // [AOP("file")] + // [CommandMethod("TESTAOP")] + // public void testaop() + // { // // 不用 using var tr = new DBTrans(file); // var tr = DBTrans.Top; // tr.ModelSpace.AddCircle(new Point3d(0, 0, 0), 20); - //} + // } [CommandMethod("testpt")] public void TestPt() { - //var pt = Env.Editor.GetPoint("pick pt:").Value; - //var pl = Env.Editor.GetEntity("pick pl").ObjectId; + // var pt = Env.Editor.GetPoint("pick pt:").Value; + // var pl = Env.Editor.GetEntity("pick pl").ObjectId; var tr1 = HostApplicationServices.WorkingDatabase.TransactionManager.TopTransaction; using var tr2 = new DBTrans(); @@ -62,14 +90,14 @@ public void TestPt() Env.Print(tr5 == tr7); var trm = HostApplicationServices.WorkingDatabase.TransactionManager; - //var ptt = tr.GetObject(pl).GetClosestPointTo(pt,false); - //var pt1 = new Point3d(0, 0.00000000000001, 0); - //var pt2 = new Point3d(0, 0.00001, 0); - //Env.Print(Tolerance.Global.EqualPoint); - //Env.Print(pt1.IsEqualTo(pt2).ToString()); - //Env.Print(pt1.IsEqualTo(pt2,new Tolerance(0.0,1e-6)).ToString()); - //Env.Print((pt1 == pt2).ToString()); - //Env.Print((pt1 != pt2).ToString()); + // var ptt = tr.GetObject(pl).GetClosestPointTo(pt,false); + // var pt1 = new Point3d(0, 0.00000000000001, 0); + // var pt2 = new Point3d(0, 0.00001, 0); + // Env.Print(Tolerance.Global.EqualPoint); + // Env.Print(pt1.IsEqualTo(pt2).ToString()); + // Env.Print(pt1.IsEqualTo(pt2,new Tolerance(0.0,1e-6)).ToString()); + // Env.Print((pt1 == pt2).ToString()); + // Env.Print((pt1 != pt2).ToString()); diff --git a/tests/Test/TestEnt.cs b/tests/Test/TestEnt.cs index e3cccf7..010303a 100644 --- a/tests/Test/TestEnt.cs +++ b/tests/Test/TestEnt.cs @@ -9,11 +9,9 @@ public void TestEntRoration() using var tr = new DBTrans(); tr.CurrentSpace.AddEntity(line); - var line2 = line.Clone() as Line; + var line2 = (Line)line.Clone(); tr.CurrentSpace.AddEntity(line2); line2.Rotation(new(100, 0, 0), Math.PI / 2); - - } @@ -28,12 +26,12 @@ public void TestTypeSpeed() }); Tools.TestTimes(100000, "name 匹配:", () => { - //var t = line.GetType().Name; + // var t = line.GetType().Name; var tt = line1.GetType().Name == nameof(Line); }); Tools.TestTimes(100000, "dxfname 匹配:", () => { - //var t = line.GetType().Name; + // var t = line.GetType().Name; var tt = line1.GetRXClass().DxfName == nameof(Line); }); } diff --git a/tests/Test/TestJig.cs b/tests/Test/TestJig.cs index 918795c..f8a9c5a 100644 --- a/tests/Test/TestJig.cs +++ b/tests/Test/TestJig.cs @@ -4,34 +4,32 @@ public class Commands_Jig { - //已在数据库的图元如何进入jig + // 已在数据库的图元如何进入jig [CommandMethod("TestCmd_jig33")] public static void TestCmd_jig33() { - Circle cir; using var tr = new DBTrans(); - var per = tr.Editor.GetEntity("\n点选圆形:"); - if (per.Status != PromptStatus.OK) + var per = tr.Editor?.GetEntity("\n点选圆形:"); + if (per?.Status != PromptStatus.OK) return; - cir = tr.GetObject(per.ObjectId, OpenMode.ForWrite); - + var cir = tr.GetObject(per.ObjectId, OpenMode.ForWrite); if (cir == null) return; var oldSp = cir.StartPoint; - JigEx moveJig = null; + JigEx? moveJig = null; moveJig = new JigEx((mousePoint, drawEntitys) => { - moveJig.SetOptions(oldSp);//回调过程中也可以修改基点 - //cir.UpgradeOpen();//已经提权了,所以这里不需要提权 + moveJig!.SetOptions(oldSp);// 回调过程中也可以修改基点 + // cir.UpgradeOpen();// 已经提权了,所以这里不需要提权 cir.Move(cir.StartPoint, mousePoint); - //cir.DowngradeOpen(); + // cir.DowngradeOpen(); - //此处会Dispose图元, - //所以此处不加入已经在数据库的图元,而是加入new Entity的. - //drawEntitys.Enqueue(cir); + // 此处会Dispose图元, + // 所以此处不加入已经在数据库的图元,而是加入new Entity的. + // drawEntitys.Enqueue(cir); }); moveJig.SetOptions(cir.GeometricExtents.MinPoint, orthomode: true); - //此处详见方法注释 + // 此处详见方法注释 moveJig.DatabaseEntityDraw(draw => { draw.RawGeometry.Draw(cir); }); @@ -45,7 +43,7 @@ public static void TestCmd_jig33() } - //不在数据库的图元如何进入jig + // 不在数据库的图元如何进入jig [CommandMethod("TestCmd_Jig44")] public void TestCmd_Jig44() { @@ -62,25 +60,25 @@ public void TestCmd_Jig44() * 原因: 多段线与鼠标垂直点作为 BasePoint,jig鼠标点为确定点 * 所以需要先声明再传入指针,但是我发现null也可以. */ - JigEx jig = null; - JigPromptPointOptions options = null; + JigEx? jig = null; + JigPromptPointOptions? options = null; jig = new JigEx((mousePoint, drawEntitys) => { var closestPt = pl.GetClosestPointTo(mousePoint, false); - //回调过程中SetOptions会覆盖配置,所以如果想增加关键字或者修改基点, - //不要这样做: jig.SetOptions(closestPt) 而是使用底层暴露 - options.BasePoint = closestPt; + // 回调过程中SetOptions会覆盖配置,所以如果想增加关键字或者修改基点, + // 不要这样做: jig.SetOptions(closestPt) 而是使用底层暴露 + options!.BasePoint = closestPt; - //需要避免重复加入同一个关键字 + // 需要避免重复加入同一个关键字 if (!options.Keywords.Contains("A")) options.Keywords.Add("A"); - //生成文字 + // 生成文字 var dictString = (pl.GetDistAtPoint(closestPt) * 0.001).ToString("0.00"); var acText = new TextInfo(dictString, closestPt, AttachmentPoint.BaseLeft, textHeight: 200) .AddDBTextToEntity(); - //加入刷新队列 + // 加入刷新队列 drawEntitys.Enqueue(acText); }); @@ -99,18 +97,18 @@ public void TestCmd_Jig44() switch (pr.StringResult) { case "A": - tr.Editor.WriteMessage($"\n 您触发了关键字{pr.StringResult}"); + tr.Editor?.WriteMessage($"\n 您触发了关键字{pr.StringResult}"); flag = false; break; case " ": - tr.Editor.WriteMessage("\n 触发关键字空格"); + tr.Editor?.WriteMessage("\n 触发关键字空格"); flag = false; break; } } - else if (pr.Status != PromptStatus.OK)//PromptStatus.None == 右键,空格,回车,都在这里结束 + else if (pr.Status != PromptStatus.OK)// PromptStatus.None == 右键,空格,回车,都在这里结束 { - tr.Editor.WriteMessage(Environment.NewLine + pr.Status.ToString()); + tr.Editor?.WriteMessage(Environment.NewLine + pr.Status.ToString()); return; } else @@ -122,8 +120,9 @@ public void TestCmd_Jig44() [CommandMethod("TestCmd_loop")] public void TestCmd_loop() { - DocumentCollection dm = Acap.DocumentManager; - Editor ed = dm.MdiActiveDocument.Editor; + var dm = Acap.DocumentManager; + var ed = dm.MdiActiveDocument.Editor; + // Create and add our message filter MyMessageFilter filter = new(); System.Windows.Forms.Application.AddMessageFilter(filter); @@ -156,9 +155,7 @@ public bool PreFilterMessage(ref Message m) // Check for the Escape keypress Keys kc = (Keys)(int)m.WParam & Keys.KeyCode; if (m.Msg == WM_KEYDOWN && kc == Keys.Escape) - { bCanceled = true; - } Key = kc; // Return true to filter all keypresses return true; @@ -185,72 +182,63 @@ static public void TestCmd_QuickText() if (pr.Status != PromptStatus.OK) return; - var tr = doc.TransactionManager.StartTransaction(); - using (tr) - { - BlockTableRecord btr = - (BlockTableRecord)tr.GetObject( - db.CurrentSpaceId, OpenMode.ForWrite - ); - // Create the text object, set its normal and contents + using var tr = doc.TransactionManager.StartTransaction(); + var btr = (BlockTableRecord)tr.GetObject(db.CurrentSpaceId, OpenMode.ForWrite); + // Create the text object, set its normal and contents - var acText = new TextInfo(pr.StringResult, - Point3d.Origin, - AttachmentPoint.BaseLeft, textHeight: 200) - .AddDBTextToEntity(); + var acText = new TextInfo(pr.StringResult, + Point3d.Origin, + AttachmentPoint.BaseLeft, textHeight: 200) + .AddDBTextToEntity(); - acText.Normal = ed.CurrentUserCoordinateSystem.CoordinateSystem3d.Zaxis; - btr.AppendEntity(acText); - tr.AddNewlyCreatedDBObject(acText, true); + acText.Normal = ed.CurrentUserCoordinateSystem.CoordinateSystem3d.Zaxis; + btr.AppendEntity(acText); + tr.AddNewlyCreatedDBObject(acText, true); - // Create our jig - var pj = new TextPlacementJig(tr, db, acText); - // Loop as we run our jig, as we may have keywords - PromptStatus stat = PromptStatus.Keyword; - while (stat == PromptStatus.Keyword) - { - PromptResult res = ed.Drag(pj); - stat = res.Status; - if ( - stat != PromptStatus.OK && - stat != PromptStatus.Keyword - ) - return; - } - tr.Commit(); + // Create our jig + var pj = new TextPlacementJig(tr, db, acText); + // Loop as we run our jig, as we may have keywords + var state = PromptStatus.Keyword; + while (state == PromptStatus.Keyword) + { + var res = ed.Drag(pj); + state = res.Status; + if (state != PromptStatus.OK && state != PromptStatus.Keyword) + return; } + tr.Commit(); } + +#if true class TextPlacementJig : EntityJig { // Declare some internal state readonly Database _db; readonly Transaction _tr; + Point3d _position; double _angle, _txtSize; + // Constructor - public TextPlacementJig( - Transaction tr, Database db, Entity ent - ) : base(ent) + public TextPlacementJig(Transaction tr, Database db, Entity ent) : base(ent) { _db = db; _tr = tr; _angle = 0; _txtSize = 1; } - protected override SamplerStatus Sampler( - JigPrompts jp - ) + + protected override SamplerStatus Sampler(JigPrompts jp) { // We acquire a point but with keywords - JigPromptPointOptions po = - new( - "\nPosition of text" - ); - po.UserInputControls = - (UserInputControls.Accept3dCoordinates | + JigPromptPointOptions po = new("\nPosition of text") + { + UserInputControls = + UserInputControls.Accept3dCoordinates | UserInputControls.NullResponseAccepted | UserInputControls.NoNegativeResponseAccepted | - UserInputControls.GovernedByOrthoMode); + UserInputControls.GovernedByOrthoMode + }; po.SetMessageAndKeywords( "\nSpecify position of text or " + "[Bold/Italic/LArger/Smaller/" + @@ -258,19 +246,16 @@ JigPrompts jp "Bold Italic LArger Smaller " + "ROtate90 LEft Middle RIght" ); + PromptPointResult ppr = jp.AcquirePoint(po); if (ppr.Status == PromptStatus.Keyword) { switch (ppr.StringResult) { case "Bold": - { - break; - } + break; case "Italic": - { - break; - } + break; case "LArger": { // Multiple the text size by two @@ -295,33 +280,26 @@ JigPrompts jp break; } case "LEft": - { - break; - } + break; case "RIght": - { - break; - } + break; case "Middle": - { - break; - } + break; } return SamplerStatus.OK; } else if (ppr.Status == PromptStatus.OK) { // Check if it has changed or not (reduces flicker) - if ( - _position.DistanceTo(ppr.Value) < - Tolerance.Global.EqualPoint - ) + if (_position.DistanceTo(ppr.Value) < Tolerance.Global.EqualPoint) return SamplerStatus.NoChange; + _position = ppr.Value; return SamplerStatus.OK; } return SamplerStatus.Cancel; } + protected override bool Update() { // Set properties on our text object @@ -331,5 +309,6 @@ protected override bool Update() txt.Rotation = _angle; return true; } - } -} + } +#endif +} \ No newline at end of file diff --git a/tests/Test/TestJigExTransient.cs b/tests/Test/TestJigExTransient.cs new file mode 100644 index 0000000..15b8434 --- /dev/null +++ b/tests/Test/TestJigExTransient.cs @@ -0,0 +1,53 @@ +#if !ac2008 +namespace Test; + +public partial class Test +{ + [CommandMethod(nameof(TestJigExTransient))] + public void TestJigExTransient() + { + // 先取1点,建2个圆 + var getpt = Env.Editor.GetPoint("\n选择点"); + if (getpt.Status != PromptStatus.OK) + return; + var pt = getpt.Value.Ucs2Wcs(); + + var c1 = new Circle(pt, Vector3d.ZAxis, 100); + var c2 = new Circle(pt.Polar(0, 300), Vector3d.ZAxis, 100); + + // 创建瞬态容器 + using var jet = new JigExTransient(); + + // 将c1以默认模式、c2以亮显模式加到瞬态容器,即在图纸上显示 + jet.Add(c1); + jet.Add(c2, Acgi.TransientDrawingMode.Highlight); + + // 再取一点,再建一个圆c3 + var r2 = Env.Editor.GetPoint("\n选择下一点"); + if (r2.Status != PromptStatus.OK) + return; + var pt2 = r2.Value.Ucs2Wcs(); + + var c3 = new Circle(pt2, Vector3d.ZAxis, 150); + + // 将c1从瞬态容器中移除,将c2修改颜色,c3加入瞬态容器 + jet.Remove(c1); + c2.ColorIndex = 1; + jet.Add(c3); + + // 由于c2进行了修改,所以需要更新, + // 可以单个更新或更新整个瞬态容器 + jet.Update(c2); + //jet.UpdateAll(); + + var r4 = Env.Editor.GetPoint("\n此拾取无意义,仅为了暂停查看"); + + // 加到图纸中,为测试瞬态容器可以自行dispose消失,所以未全部加入 + using var tr = new DBTrans(); + tr.CurrentSpace.AddEntity(c3); + + // 若想将容器中所有图元全部加入提供了Entities属性 + // tr.CurrentSpace.AddEntity(jet.Entities); + } +} +#endif \ No newline at end of file diff --git a/tests/Test/TestLisp.cs b/tests/Test/TestLisp.cs index 7a10953..1a17bf0 100644 --- a/tests/Test/TestLisp.cs +++ b/tests/Test/TestLisp.cs @@ -2,7 +2,7 @@ public class TestLisp { - //定义lisp函数 + // 定义lisp函数 [LispFunction("LispTest_RunLisp")] public static object LispTest_RunLisp(ResultBuffer rb) { @@ -10,63 +10,63 @@ public static object LispTest_RunLisp(ResultBuffer rb) return null!; } - //模态命令,只有当CAD发出命令提示或当前没有其他的命令或程序活动的时候才可以被触发 + // 模态命令,只有当CAD发出命令提示或当前没有其他的命令或程序活动的时候才可以被触发 [CommandMethod("CmdTest_RunLisp1", CommandFlags.Modal)] - //透明命令,可以在一个命令提示输入的时候触发例如正交切换,zoom等 + // 透明命令,可以在一个命令提示输入的时候触发例如正交切换,zoom等 [CommandMethod("CmdTest_RunLisp2", CommandFlags.Transparent)] - //选择图元之后执行命令将可以从 获取图元 + // 选择图元之后执行命令将可以从 获取图元 [CommandMethod("CmdTest_RunLisp3", CommandFlags.UsePickSet)] - //命令执行前已选中部分实体.在命令执行过程中这些标记不会被清除 + // 命令执行前已选中部分实体.在命令执行过程中这些标记不会被清除 [CommandMethod("CmdTest_RunLisp4", CommandFlags.Redraw)] - //命令不能在透视图中使用 + // 命令不能在透视图中使用 [CommandMethod("CmdTest_RunLisp5", CommandFlags.NoPerspective)] - //命令不能通过 MULTIPLE命令 重复触发 + // 命令不能通过 MULTIPLE命令 重复触发 [CommandMethod("CmdTest_RunLisp6", CommandFlags.NoMultiple)] - //不允许在模型空间使用命令 + // 不允许在模型空间使用命令 [CommandMethod("CmdTest_RunLisp7", CommandFlags.NoTileMode)] - //不允许在布局空间使用命令 + // 不允许在布局空间使用命令 [CommandMethod("CmdTest_RunLisp8", CommandFlags.NoPaperSpace)] - //命令不能在OEM产品中使用 + // 命令不能在OEM产品中使用 [CommandMethod("CmdTest_RunLisp9", CommandFlags.NoOem)] - //不能直接使用命令名调用,必须使用 组名.全局名 调用 + // 不能直接使用命令名调用,必须使用 组名.全局名 调用 [CommandMethod("CmdTest_RunLisp10", CommandFlags.Undefined)] - //定义lisp方法.已废弃 请使用lispfunction + // 定义lisp方法.已废弃 请使用lispfunction [CommandMethod("CmdTest_RunLisp11", CommandFlags.Defun)] - //命令不会被存储在新的命令堆上 + // 命令不会被存储在新的命令堆上 [CommandMethod("CmdTest_RunLisp12", CommandFlags.NoNewStack)] - //命令不能被内部锁定(命令锁) + // 命令不能被内部锁定(命令锁) [CommandMethod("CmdTest_RunLisp13", CommandFlags.NoInternalLock)] - //调用命令的文档将会被锁定为只读 + // 调用命令的文档将会被锁定为只读 [CommandMethod("CmdTest_RunLisp14", CommandFlags.DocReadLock)] - //调用命令的文档将会被锁定,类似document.lockdocument + // 调用命令的文档将会被锁定,类似document.lockdocument [CommandMethod("CmdTest_RunLisp15", CommandFlags.DocExclusiveLock)] - //命令在CAD运行期间都能使用,而不只是在当前文档 + // 命令在CAD运行期间都能使用,而不只是在当前文档 [CommandMethod("CmdTest_RunLisp16", CommandFlags.Session)] - //获取用户输入时,可以与属性面板之类的交互 + // 获取用户输入时,可以与属性面板之类的交互 [CommandMethod("CmdTest_RunLisp17", CommandFlags.Interruptible)] - //命令不会被记录在命令历史记录 + // 命令不会被记录在命令历史记录 [CommandMethod("CmdTest_RunLisp18", CommandFlags.NoHistory)] - //命令不会被 UNDO取消 + // 命令不会被 UNDO取消 [CommandMethod("CmdTest_RunLisp19", CommandFlags.NoUndoMarker)] - //不能在参照块中使用命令 + // 不能在参照块中使用命令 [CommandMethod("CmdTest_RunLisp20", CommandFlags.NoBlockEditor)] -#if ac2009 - //acad09增,不会被动作录制器 捕捉到 +#if !ac2008 + // acad09增,不会被动作录制器 捕捉到 [CommandMethod("CmdTest_RunLisp21", CommandFlags.NoActionRecording)] - //acad09增,会被动作录制器捕捉 + // acad09增,会被动作录制器捕捉 [CommandMethod("CmdTest_RunLisp22", CommandFlags.ActionMacro)] #endif #if !NET35 - //推断约束时不能使用命令 + // 推断约束时不能使用命令 [CommandMethod("CmdTest_RunLisp23", CommandFlags.NoInferConstraint)] - //命令允许在选择图元时临时显示动态尺寸 + // 命令允许在选择图元时临时显示动态尺寸 [CommandMethod("CmdTest_RunLisp24", CommandFlags.TempShowDynDimension)] #endif public static void CmdTest_RunLisp() { // 测试方法1: (command "CmdTest_RunLisp1") // 测试方式2: (LispTest_RunLisp) - var dm = Application.DocumentManager; + var dm = Acap.DocumentManager; var doc = dm.MdiActiveDocument; var ed = doc.Editor; @@ -90,32 +90,32 @@ public static void CmdTest_RunLisp() Env.Editor.RunLisp("(setq a 10)(princ)", EditorEx.RunLispFlag.AdsQueueexpr); Env.Editor.RunLisp("(princ a)", - EditorEx.RunLispFlag.AdsQueueexpr);//成功输出 + EditorEx.RunLispFlag.AdsQueueexpr);// 成功输出 } else if (flag == EditorEx.RunLispFlag.AcedEvaluateLisp) { // 使用(command "CmdTest_RunLisp1")发送,然后 !b 查看变量,acad08是有值的,高版本是null var strlisp0 = "(setq b 20)"; var res0 = Env.Editor.RunLisp(strlisp0, - EditorEx.RunLispFlag.AcedEvaluateLisp); //有lisp的返回值 + EditorEx.RunLispFlag.AcedEvaluateLisp); // 有lisp的返回值 var strlisp1 = "(defun f1( / )(princ \"aa\"))"; var res1 = Env.Editor.RunLisp(strlisp1, - EditorEx.RunLispFlag.AcedEvaluateLisp); //有lisp的返回值 + EditorEx.RunLispFlag.AcedEvaluateLisp); // 有lisp的返回值 var strlisp2 = "(defun f2( / )(command \"line\"))"; var res2 = Env.Editor.RunLisp(strlisp2, - EditorEx.RunLispFlag.AcedEvaluateLisp); //有lisp的返回值 + EditorEx.RunLispFlag.AcedEvaluateLisp); // 有lisp的返回值 } else if (flag == EditorEx.RunLispFlag.SendStringToExecute) { - //测试异步 - //(command "CmdTest_RunLisp1")和(LispTest_RunLisp)4都是异步 + // 测试异步 + // (command "CmdTest_RunLisp1")和(LispTest_RunLisp)4都是异步 var str = "(setq c 40)(princ)"; Env.Editor.RunLisp(str, - EditorEx.RunLispFlag.SendStringToExecute); //异步,后发送 + EditorEx.RunLispFlag.SendStringToExecute); // 异步,后发送 Env.Editor.RunLisp("(princ c)", - EditorEx.RunLispFlag.AdsQueueexpr); //同步,先发送了,输出是null + EditorEx.RunLispFlag.AdsQueueexpr); // 同步,先发送了,输出是null } } -} +} \ No newline at end of file diff --git a/tests/Test/TestLoop.cs b/tests/Test/TestLoop.cs index d60dc66..f69e676 100644 --- a/tests/Test/TestLoop.cs +++ b/tests/Test/TestLoop.cs @@ -20,7 +20,7 @@ public void Testloop() Env.Print(loop); - loop.SetFirst(loop.Last); + loop.SetFirst(loop.Last!); Env.Print(loop); Env.Print(loop.Min()); loop.SetFirst(new LoopListNode (loop.Min() ,loop)); diff --git a/tests/Test/TestMirrorFile.cs b/tests/Test/TestMirrorFile.cs index 9fdd8e0..34bc6d6 100644 --- a/tests/Test/TestMirrorFile.cs +++ b/tests/Test/TestMirrorFile.cs @@ -23,14 +23,15 @@ public static void CmdTest_MirrorFile() dbText.UpgradeOpen(); var pos = dbText.Position; - //text.Move(pos, Point3d.Origin); - //Y轴 + // text.Move(pos, Point3d.Origin); + // Y轴 dbText.Mirror(Point3d.Origin, new Point3d(0, 1, 0)); - //text.Move(Point3d.Origin, pos); + // text.Move(Point3d.Origin, pos); dbText.DowngradeOpen(); } }); - tr.Database.SaveAs(fileSave, DwgVersion.AC1021/*AC1021 AutoCAD 2007/2008/2009.*/); + var ver = (DwgVersion)27;/*AC1021 AutoCAD 2007/2008/2009.*/ + tr.Database.SaveAs(fileSave, ver); } /// @@ -40,10 +41,9 @@ public static void CmdTest_MirrorFile() [CommandMethod("CmdTest_MirrorFile2")] public static void CmdTest_MirrorFile2() { - using var tr = new DBTrans(file, openMode: FileOpenMode.OpenForReadAndReadShare); - - tr.Database.DBTextDeviation(() => { + using var tr = new DBTrans(file); + tr.Task(() => { var yaxis = new Point3d(0, 1, 0); tr.BlockTable.Change(tr.ModelSpace.ObjectId, modelSpace => { foreach (ObjectId entId in modelSpace) @@ -52,13 +52,13 @@ public static void CmdTest_MirrorFile2() if (entity is DBText dbText) { dbText.Mirror(Point3d.Origin, yaxis); - dbText.IsMirroredInX = true; //这句将导致文字偏移 + dbText.IsMirroredInX = true; // 这句将导致文字偏移 - //指定文字的垂直对齐方式 + // 指定文字的垂直对齐方式 if (dbText.VerticalMode == TextVerticalMode.TextBase) dbText.VerticalMode = TextVerticalMode.TextBottom; - //指定文字的水平对齐方式 + // 指定文字的水平对齐方式 dbText.HorizontalMode = dbText.HorizontalMode switch { TextHorizontalMode.TextLeft => TextHorizontalMode.TextRight, @@ -70,6 +70,6 @@ public static void CmdTest_MirrorFile2() } }); }); - tr.Database.SaveAs(fileSave, DwgVersion.AC1021/*AC1021 AutoCAD 2007/2008/2009.*/); + tr.Database.SaveAs(fileSave, (DwgVersion)27 /*AC1021 AutoCAD 2007/2008/2009.*/); } -} +} \ No newline at end of file diff --git a/tests/Test/TestPoint.cs b/tests/Test/TestPoint.cs index 38ecb97..688f09f 100644 --- a/tests/Test/TestPoint.cs +++ b/tests/Test/TestPoint.cs @@ -12,28 +12,26 @@ public class TestPoint [CommandMethod("TestptSortedSet")] public void TestptSortedSet() { - var ss1 = new SortedSet(); - ss1.Add(new Point2d(1, 1)); - ss1.Add(new Point2d(4.6, 2)); - ss1.Add(new Point2d(8, 3)); - ss1.Add(new Point2d(4, 3)); - ss1.Add(new Point2d(5, 40)); - ss1.Add(new Point2d(6, 5)); - ss1.Add(new Point2d(1, 6)); - ss1.Add(new Point2d(7, 6)); - ss1.Add(new Point2d(9, 6)); + var ss1 = new SortedSet + { + new Point2d(1, 1), + new Point2d(4.6, 2), + new Point2d(8, 3), + new Point2d(4, 3), + new Point2d(5, 40), + new Point2d(6, 5), + new Point2d(1, 6), + new Point2d(7, 6), + new Point2d(9, 6) + }; /*判断区间,超过就中断*/ foreach (var item in ss1) { if (item.X > 3 && item.X < 7) - { Debug.WriteLine(item); - } else if (item.X >= 7) - { break; - } } } @@ -44,9 +42,9 @@ public void TestptGethash() { // test var pt = Env.Editor.GetPoint("pick pt").Value; - //Tools.TestTimes2(1_000_000, "新语法", () => { + // Tools.TestTimes2(1_000_000, "新语法", () => { // pt.GetHashString2(); - //}); + // }); Tools.TestTimes2(1_000_000, "旧语法", () => { pt.GetHashString(); }); @@ -100,12 +98,10 @@ public void Testlistequalspeed() { var lst1 = new List { 1, 2, 3, 4 }; var lst2 = new List { 1, 2, 3, 4}; - lst1.EqualsAll(null); + lst1.EqualsAll(null!); Tools.TestTimes2(1000000, "eqaulspeed:", () => { lst1.EqualsAll(lst2); }); - - } [CommandMethod("Testcontains")] @@ -151,4 +147,4 @@ public void Testcontains() }); } -} +} \ No newline at end of file diff --git a/tests/Test/TestQuadTree.cs b/tests/Test/TestQuadTree.cs index fef70c4..2047a29 100644 --- a/tests/Test/TestQuadTree.cs +++ b/tests/Test/TestQuadTree.cs @@ -9,8 +9,8 @@ namespace Test; public class CadEntity : QuadEntity { public ObjectId ObjectId; - //这里加入其他字段 - public List? Link;//碰撞链 + // 这里加入其他字段 + public List? Link;// 碰撞链 public System.Drawing.Color Color; public double Angle; public CadEntity(ObjectId objectId, Rect box) : base(box) @@ -36,7 +36,7 @@ public override int GetHashCode() public partial class TestQuadTree { - QuadTree _quadTreeRoot; + QuadTree? _quadTreeRoot; #region 四叉树创建并加入 [CommandMethod("Test_QuadTree")] public void Test_QuadTree() @@ -44,20 +44,20 @@ public void Test_QuadTree() using var tr = new DBTrans(); Rect dbExt; - //使用数据库边界来进行 + // 使用数据库边界来进行 var dbExtent = tr.Database.GetValidExtents3d(); if (dbExtent == null) { - //throw new ArgumentException("画一个矩形"); - - //这个初始值的矩形是很有意义, - //主要是四叉树分裂过程中产生多个Rect,Rect内有很多重复的double值,是否可以内存复用,以此减少内存大小? - //接着想了一下,Rect可以是int,long,这样可以利用位运算它扩展和缩小, - //最小就是1,并且可以控制四叉树深度,不至于无限递归. - //而且指针长度跟值是一样的,所以就不需要复用了,毕竟跳转一个函数地址挺麻烦的. - //但是因为啊惊懒的原因,并没有单独制作这样的矩形, - //而且非常糟糕的是,c#不支持模板约束运算符,使得值类型之间需要通过一层接口来委婉处理,拉低了效率..引用类型倒是无所谓.. - //要么忍着,要么换c++去搞四叉树吧 + // throw new ArgumentException("画一个矩形"); + + // 这个初始值的矩形是很有意义, + // 主要是四叉树分裂过程中产生多个Rect,Rect内有很多重复的double值,是否可以内存复用,以此减少内存大小? + // 接着想了一下,Rect可以是int,long,这样可以利用位运算它扩展和缩小, + // 最小就是1,并且可以控制四叉树深度,不至于无限递归. + // 而且指针长度跟值是一样的,所以就不需要复用了,毕竟跳转一个函数地址挺麻烦的. + // 但是因为啊惊懒的原因,并没有单独制作这样的矩形, + // 而且非常糟糕的是,c#不支持模板约束运算符,使得值类型之间需要通过一层接口来委婉处理,拉低了效率..引用类型倒是无所谓.. + // 要么忍着,要么换c++去搞四叉树吧 dbExt = new Rect(0, 0, 1 << 10, 1 << 10); } else @@ -67,10 +67,10 @@ public void Test_QuadTree() dbExt = new Rect(a, b); } - //创建四叉树 + // 创建四叉树 _quadTreeRoot = new QuadTree(dbExt); - //数据库边界 + // 数据库边界 var pl = dbExt.ToPoints(); var databaseBoundary = new List<(Point3d, double, double, double)> { @@ -81,26 +81,27 @@ public void Test_QuadTree() }; tr.CurrentSpace.AddPline(databaseBoundary); - //生成多少个图元,导致cad会令undo出错(八叉树深度过大 treemax) - //int maximumItems = 30_0000; + // 生成多少个图元,导致cad会令undo出错(八叉树深度过大 treemax) + // int maximumItems = 30_0000; int maximumItems = 1000; - //随机图元生成 - List ces = new(); //用于随机获取图元 + // 随机图元生成 + List ces = new(); // 用于随机获取图元 Timer.RunTime(() => { - //生成外边界和随机圆形 + // 生成外边界和随机圆形 var grc = GenerateRandomCircle(maximumItems, dbExt); foreach (var ent in grc) { - //初始化图元颜色 - ent.ColorIndex = 1; //Color.FromRgb(0, 0, 0);//黑色 + // 初始化图元颜色 + ent!.ColorIndex = 1; // Color.FromRgb(0, 0, 0);// 黑色 var edge = ent.GeometricExtents; - //四叉树数据 + // 四叉树数据 var entRect = new Rect(edge.MinPoint.X, edge.MinPoint.Y, edge.MaxPoint.X, edge.MaxPoint.Y); var entId = tr.CurrentSpace.AddEntity(ent); + var ce = new CadEntity(entId, entRect) { - Color = Utility.RandomColor + Color = RandomEx.NextColor() }; ces.Add(ce); /*加入随机点*/ @@ -110,17 +111,15 @@ public void Test_QuadTree() var dbPointCe = new CadEntity(entId, entRect); ces.Add(dbPointCe); } - }, Timer.TimeEnum.Millisecond, "画圆消耗时间:");//30万图元±3秒.cad2021 + }, Timer.TimeEnum.Millisecond, "画圆消耗时间:");// 30万图元±3秒.cad2021 - //测试只加入四叉树的时间 + // 测试只加入四叉树的时间 Timer.RunTime(() => { for (int i = 0; i < ces.Count; i++) - { _quadTreeRoot.Insert(ces[i]); - } - }, Timer.TimeEnum.Millisecond, "插入四叉树时间:");//30万图元±0.7秒.cad2021 + }, Timer.TimeEnum.Millisecond, "插入四叉树时间:");// 30万图元±0.7秒.cad2021 - tr.Editor.WriteMessage($"\n加入图元数量:{maximumItems}"); + tr.Editor?.WriteMessage($"\n加入图元数量:{maximumItems}"); } /// @@ -128,26 +127,26 @@ public void Test_QuadTree() /// /// 创建数量 /// 数据库边界 - static IEnumerable GenerateRandomCircle(int createNumber, Rect dbExt) + static IEnumerable GenerateRandomCircle(int createNumber, Rect dbExt) { var x1 = (int)dbExt.X; var x2 = (int)(dbExt.X + dbExt.Width); var y1 = (int)dbExt.Y; var y2 = (int)(dbExt.Y + dbExt.Height); - var rand = Utility.GetRandom(); + var rand = RandomEx.GetRandom(); for (int i = 0; i < createNumber; i++) { var x = rand.Next(x1, x2) + rand.NextDouble(); var y = rand.Next(y1, y2) + rand.NextDouble(); - yield return EntityEx.CreateCircle(new Point3d(x, y, 0), rand.Next(1, 100)); //起点,终点 + yield return EntityEx.CreateCircle(new Point3d(x, y, 0), rand.Next(1, 100)); // 起点,终点 } } /*TODO 啊惊: 有点懒不想改了*/ #if true2 - //选择加入到四叉树 + // 选择加入到四叉树 [CommandMethod("CmdTest_QuadTree21")] public void CmdTest_QuadTree21() { @@ -164,7 +163,7 @@ public void CmdTest_QuadTree21() AddQuadTreeRoot(db, ed, ss); } - //自动加入全图到四叉树 + // 自动加入全图到四叉树 [CommandMethod("CmdTest_QuadTree20")] public void CmdTest_QuadTree20() { @@ -179,14 +178,14 @@ public void CmdTest_QuadTree20() var time1 = Timer.RunTime(() => { db.Action(tr => { db.TraverseBlockTable(tr, btRec => { - if (!btRec.IsLayout)//布局跳过 + if (!btRec.IsLayout)// 布局跳过 return false; foreach (var item in btRec) { - //var ent = item.ToEntity(tr); + // var ent = item.ToEntity(tr); ss.Add(item); - ++entnum;//图元数量:100000, 遍历全图时间:0.216秒 CmdTest_QuadTree2 + ++entnum;// 图元数量:100000, 遍历全图时间:0.216秒 CmdTest_QuadTree2 } return false; }); @@ -194,7 +193,7 @@ public void CmdTest_QuadTree20() }); ed.WriteMessage($"\n图元数量:{entnum}, 遍历全图时间:{time1 / 1000.0}秒"); - //清空原有的 + // 清空原有的 _quadTreeRoot = null; AddQuadTreeRoot(db, ed, ss); } @@ -206,13 +205,13 @@ void AddQuadTreeRoot(Database db, Editor ed, List ss) ed.WriteMessage("\n四叉树是空的,重新初始化"); Rect dbExt; - //使用数据库边界来进行 + // 使用数据库边界来进行 var dbExtent = db.GetValidExtents3d(); if (dbExtent == null) { - //throw new ArgumentException("画一个矩形"); + // throw new ArgumentException("画一个矩形"); - //测试时候画个矩形,在矩形内画随机坐标的圆形 + // 测试时候画个矩形,在矩形内画随机坐标的圆形 dbExt = new Rect(0, 0, 32525, 32525); } else @@ -238,7 +237,7 @@ void AddQuadTreeRoot(Database db, Editor ed, List ss) if (ent is null) return; var edge = new EdgeEntity(ent); - //四叉树数据 + // 四叉树数据 var ce = new CadEntity(entId, edge.Edge) { Color = Utility.RandomColor @@ -262,13 +261,13 @@ void AddQuadTreeRoot(Database db, Editor ed, List ss) #if true2 #region 节点边界显示 - //四叉树减去节点 + // 四叉树减去节点 [CommandMethod("CmdTest_QuadTree0")] public void CmdTest_QuadTree0() { var dm = Acap.DocumentManager; var doc = dm.MdiActiveDocument; - //var db = doc.Database; + // var db = doc.Database; var ed = doc.Editor; ed.WriteMessage("\n四叉树减区"); @@ -283,7 +282,7 @@ public void CmdTest_QuadTree0() _quadTreeRoot.Remove(rect); } - //创建节点边界 + // 创建节点边界 [CommandMethod("CmdTest_QuadTree00")] public void CmdTest_CreateNodesRect() { @@ -299,17 +298,17 @@ public void CmdTest_CreateNodesRect() return; } - //此处发现了一个事务处理的bug,提交数量过多的时候,会导致 ctrl+z 无法回滚, - //需要把事务放在循环体内部 - //报错: 0x6B00500A (msvcr80.dll)处(位于 acad.exe 中)引发的异常: 0xC0000005: 写入位置 0xFFE00000 时发生访问冲突。 - //画出所有的四叉树节点边界,因为事务放在外面引起 + // 此处发现了一个事务处理的bug,提交数量过多的时候,会导致 ctrl+z 无法回滚, + // 需要把事务放在循环体内部 + // 报错: 0x6B00500A (msvcr80.dll)处(位于 acad.exe 中)引发的异常: 0xC0000005: 写入位置 0xFFE00000 时发生访问冲突。 + // 画出所有的四叉树节点边界,因为事务放在外面引起 var nodeRects = new List(); _quadTreeRoot.ForEach(node => { nodeRects.Add(node); return false; }); var rectIds = new List(); - foreach (var item in nodeRects)//Count = 97341 当数量接近这个量级 + foreach (var item in nodeRects)// Count = 97341 当数量接近这个量级 { db.Action(tr => { var pts = item.ToPoints(); @@ -322,7 +321,7 @@ public void CmdTest_CreateNodesRect() db.CoverGroup(tr, rectIds); }); - //获取四叉树深度 + // 获取四叉树深度 int dep = 0; _quadTreeRoot.ForEach(node => { dep = dep > node.Depth ? dep : node.Depth; @@ -335,7 +334,7 @@ public void CmdTest_CreateNodesRect() #endif #region 四叉树查询节点 - //选择范围改图元颜色 + // 选择范围改图元颜色 [CommandMethod("CmdTest_QuadTree3")] public void CmdTest_QuadTree3() { @@ -354,21 +353,23 @@ public void CmdTest_QuadTree4() /// void Ssget(QuadTreeSelectMode mode) { - using var tr = new DBTrans(); - if (_quadTreeRoot is null) return; + + using var tr = new DBTrans(); + if (tr.Editor is null) + return; var rect = GetCorner(tr.Editor); if (rect is null) return; tr.Editor.WriteMessage("选择模式:" + mode); - //仿选择集 + // 仿选择集 var ces = _quadTreeRoot.Query(rect, mode); ces.ForEach(item => { var ent = tr.GetObject(item.ObjectId, OpenMode.ForWrite); - ent.Color = Color.FromColor(item.Color); + ent!.Color = Color.FromColor(item.Color); ent.DowngradeOpen(); ent.Dispose(); }); @@ -388,8 +389,8 @@ void Ssget(QuadTreeSelectMode mode) return null; var optionsB = new PromptCornerOptions(Environment.NewLine + "输入矩形角点2:", pprA.Value) { - UseDashedLine = true,//使用虚线 - AllowNone = true,//回车 + UseDashedLine = true,// 使用虚线 + AllowNone = true,// 回车 }; var pprB = ed.GetCorner(optionsB); if (pprB.Status != PromptStatus.OK) @@ -404,27 +405,27 @@ void Ssget(QuadTreeSelectMode mode) #endregion } -//public partial class TestQuadTree -//{ +// public partial class TestQuadTree +// { // public void Cmd_tt6() // { // using var tr = new DBTrans(); // var ed = tr.Editor; -// //创建四叉树,默认参数无所谓 +// // 创建四叉树,默认参数无所谓 // var TreeRoot = new QuadTree(new Rect(0, 0, 32525, 32525)); // var fil = OpFilter.Build(e => e.Dxf(0) == "LINE"); // var psr = ed.SSGet("\n 选择需要连接的直线", fil); // if (psr.Status != PromptStatus.OK) return; // var LineEnts = new List(psr.Value.GetEntities(OpenMode.ForWrite)!); -// //将实体插入到四岔树 +// // 将实体插入到四岔树 // foreach (var line in LineEnts) // { // var edge = line.GeometricExtents; // var entRect = new Rect(edge.MinPoint.X, edge.MinPoint.Y, edge.MaxPoint.X, edge.MaxPoint.Y); // var ce = new CadEntity(line.Id, entRect) // { -// //四叉树数据 +// // 四叉树数据 // Angle = line.Angle // }; // TreeRoot.Insert(ce); @@ -432,24 +433,24 @@ void Ssget(QuadTreeSelectMode mode) // var ppo = new PromptPointOptions(Environment.NewLine + "\n指定标注点:<空格退出>") // { -// AllowArbitraryInput = true,//任意输入 -// AllowNone = true //允许回车 +// AllowArbitraryInput = true,// 任意输入 +// AllowNone = true // 允许回车 // }; -// var ppr = ed.GetPoint(ppo);//用户点选 +// var ppr = ed.GetPoint(ppo);// 用户点选 // if (ppr.Status != PromptStatus.OK) // return; // var rect = new Rect(ppr.Value.Point2d(), 100, 100); -// tr.CurrentSpace.AddEntity(rect.ToPolyLine());//显示选择靶标范围 +// tr.CurrentSpace.AddEntity(rect.ToPolyLine());// 显示选择靶标范围 -// var nent = TreeRoot.FindNearEntity(rect);//查询最近实体,按逆时针 -// var ent = tr.GetObject(nent.ObjectId, OpenMode.ForWrite);//打开实体 -// ent.ColorIndex = Utility.GetRandom().Next(1, 256);//1~256随机色 -// ent.DowngradeOpen();//实体降级 +// var nent = TreeRoot.FindNearEntity(rect);// 查询最近实体,按逆时针 +// var ent = tr.GetObject(nent.ObjectId, OpenMode.ForWrite);// 打开实体 +// ent.ColorIndex = Utility.GetRandom().Next(1, 256);// 1~256随机色 +// ent.DowngradeOpen();// 实体降级 // ent.Dispose(); -// var res = TreeRoot.Query(rect, QuadTreeSelectMode.IntersectsWith);//查询选择靶标范围相碰的ID +// var res = TreeRoot.Query(rect, QuadTreeSelectMode.IntersectsWith);// 查询选择靶标范围相碰的ID // res.ForEach(item => { -// if (item.Angle == 0 || item.Angle == Math.PI) //过滤直线角度为0或180的直线 +// if (item.Angle == 0 || item.Angle == Math.PI) // 过滤直线角度为0或180的直线 // { // var ent = tr.GetObject(item.ObjectId, OpenMode.ForWrite); // ent.ColorIndex = Utility.GetRandom().Next(1, 7); @@ -458,4 +459,4 @@ void Ssget(QuadTreeSelectMode mode) // } // }); // } -//} +// } \ No newline at end of file diff --git a/tests/Test/XrefExTest.cs b/tests/Test/XrefExTest.cs new file mode 100644 index 0000000..b1d2545 --- /dev/null +++ b/tests/Test/XrefExTest.cs @@ -0,0 +1,24 @@ +namespace Test; + +public class TestCmd_BindXrefs +{ + //后台绑定 + [CommandMethod("Test_Bind1")] + public static void Test_Bind1() + { + string fileName = @"D:\Test.dwg"; + using var tr = new DBTrans(fileName, + openMode: FileOpenMode.OpenForReadAndAllShare/*后台绑定特别注意*/); + tr.XrefFactory(XrefModes.Bind); + tr.SaveDwgFile(); + } + + //前台绑定 + [CommandMethod("Test_Bind2")] + public static void Test_Bind2() + { + using var tr = new DBTrans(); + tr.XrefFactory(XrefModes.Bind); + tr.SaveDwgFile(); + } +} diff --git a/tests/Test/testConvexHull.cs b/tests/Test/testConvexHull.cs index 92a6917..f96b302 100644 --- a/tests/Test/testConvexHull.cs +++ b/tests/Test/testConvexHull.cs @@ -1,4 +1,4 @@ -namespace Test; +namespace Test; public class TestConvexHull @@ -6,11 +6,11 @@ public class TestConvexHull [CommandMethod("testch")] public void Testch() { - //using var tr = new DBTrans(); - //var pts = new List(); - //var flag = true; - //while (flag) - //{ + // using var tr = new DBTrans(); + // var pts = new List(); + // var flag = true; + // while (flag) + // { // var pt = tr.Editor.GetPoint("qudian"); // if (pt.Status == PromptStatus.OK) // { @@ -22,53 +22,53 @@ public void Testch() // flag = false; // } - //} + // } - //var ptt = ConvexHull.GetConvexHull(pts); + // var ptt = ConvexHull.GetConvexHull(pts); - //Polyline pl = new Polyline(); - //for (int i = 0; i < ptt.Count; i++) - //{ + // Polyline pl = new Polyline(); + // for (int i = 0; i < ptt.Count; i++) + // { // pl.AddVertexAt(i, ptt[i].Point2d(), 0, 0, 0); - //} - ////pl.AddVertexAt(0, new Point2d(0, 0), 0, 0, 0); - ////pl.AddVertexAt(1, new Point2d(10, 10), 0, 0, 0); - ////pl.AddVertexAt(2, new Point2d(20, 20), 0, 0, 0); - ////pl.AddVertexAt(3, new Point2d(30, 30), 0, 0, 0); - ////pl.AddVertexAt(4, new Point2d(40, 40), 0, 0, 0); - //pl.Closed = true; - //pl.Color = Color.FromColorIndex(ColorMethod.ByColor, 6); - //tr.CurrentSpace.AddEntity(pl); + // } + //// pl.AddVertexAt(0, new Point2d(0, 0), 0, 0, 0); + //// pl.AddVertexAt(1, new Point2d(10, 10), 0, 0, 0); + //// pl.AddVertexAt(2, new Point2d(20, 20), 0, 0, 0); + //// pl.AddVertexAt(3, new Point2d(30, 30), 0, 0, 0); + //// pl.AddVertexAt(4, new Point2d(40, 40), 0, 0, 0); + // pl.Closed = true; + // pl.Color = Color.FromColorIndex(ColorMethod.ByColor, 6); + // tr.CurrentSpace.AddEntity(pl); - //var a1 = GeometryEx.GetArea(new Point2d(0, 0), new Point2d(1, 0), new Point2d(1, 1)); - //var a2 = ConvexHull.cross(new Point3d(0, 0, 0), new Point3d(1, 0, 0), new Point3d(1, 1, 0)); - //tr.Editor.WriteMessage(a1.ToString()); - //tr.Editor.WriteMessage(a2.ToString()); + // var a1 = GeometryEx.GetArea(new Point2d(0, 0), new Point2d(1, 0), new Point2d(1, 1)); + // var a2 = ConvexHull.cross(new Point3d(0, 0, 0), new Point3d(1, 0, 0), new Point3d(1, 1, 0)); + // tr.Editor.WriteMessage(a1.ToString()); + // tr.Editor.WriteMessage(a2.ToString()); - //var vec1 = new Vector2d(1, 1); - //var vec2 = new Vector2d(-1, 1); + // var vec1 = new Vector2d(1, 1); + // var vec2 = new Vector2d(-1, 1); - //var vec3 = vec1.GetPerpendicularVector(); - //var vec4 = vec2.GetPerpendicularVector(); + // var vec3 = vec1.GetPerpendicularVector(); + // var vec4 = vec2.GetPerpendicularVector(); - //var area1 = vec2.DotProduct(vec1.GetPerpendicularVector()); - //var area2 = vec1.DotProduct(vec2.GetPerpendicularVector()); + // var area1 = vec2.DotProduct(vec1.GetPerpendicularVector()); + // var area2 = vec1.DotProduct(vec2.GetPerpendicularVector()); - //var area3 = vec2.DotProduct(vec1); - //var area4 = vec1.DotProduct(vec2); + // var area3 = vec2.DotProduct(vec1); + // var area4 = vec1.DotProduct(vec2); var area5 = GeometryEx.GetArea(new List { new Point2d(0, 0), new Point2d(1, 1), new Point2d(-1, 1) }); var area6 = GeometryEx.GetArea(new List { new Point2d(0, 0), new Point2d(-1, 1), new Point2d(1, 1) }); - //Env.Editor.WriteMessage($"vec1 的法向量= {vec3} \n"); - //Env.Editor.WriteMessage($"vec2 的法向量= {vec4} \n"); + // Env.Editor.WriteMessage($"vec1 的法向量= {vec3} \n"); + // Env.Editor.WriteMessage($"vec2 的法向量= {vec4} \n"); - //Env.Editor.WriteMessage($"vec2 点乘 vec1的法向量= {area1} \n"); - //Env.Editor.WriteMessage($"vec1 点乘 vec2的法向量= {area2} \n"); + // Env.Editor.WriteMessage($"vec2 点乘 vec1的法向量= {area1} \n"); + // Env.Editor.WriteMessage($"vec1 点乘 vec2的法向量= {area2} \n"); - //Env.Editor.WriteMessage($"vec2 点乘 vec1= {area3} \n"); - //Env.Editor.WriteMessage($"vec1 点乘 vec2= {area4} \n"); + // Env.Editor.WriteMessage($"vec2 点乘 vec1= {area3} \n"); + // Env.Editor.WriteMessage($"vec1 点乘 vec2= {area4} \n"); Env.Editor.WriteMessage($"点集的有向面积:{area5} \n"); Env.Editor.WriteMessage($"点集的有向面积:{area6} \n"); diff --git a/tests/Test/testFileDatabase.cs b/tests/Test/testFileDatabase.cs index c95c97c..cd3af21 100644 --- a/tests/Test/testFileDatabase.cs +++ b/tests/Test/testFileDatabase.cs @@ -18,7 +18,7 @@ public void TestDatabase() if (trans.Document is not null && trans.Document.IsActive) trans.Document.SendStringToExecute("_qsave\n", false, true, true); else - trans.Database.SaveAs(fileName, DwgVersion.AC1021); + trans.Database.SaveAs(fileName, (DwgVersion)27); } catch (System.Exception e) { @@ -26,4 +26,4 @@ public void TestDatabase() } } -} +} \ No newline at end of file diff --git a/tests/Test/testblock.cs b/tests/Test/testblock.cs index 23d6995..3fd0c81 100644 --- a/tests/Test/testblock.cs +++ b/tests/Test/testblock.cs @@ -2,33 +2,48 @@ public class TestBlock { - //块定义 - [CommandMethod("blockdef")] - public void BlockDef() + [CommandMethod("Test_GetBoundingBoxEx")] + public void Test_GetBoundingBoxEx() { using var tr = new DBTrans(); - //var line = new Line(new Point3d(0, 0, 0), new Point3d(1, 1, 0)); + var ents = Env.Editor.SSGet()?.Value?.GetEntities(); + if (ents == null) + return; + foreach (var item in ents) + { + if (item is null) + continue; + var box = item.GetBoundingBoxEx(); + Env.Print("min:" + box.Min + ";max" + box.Max); + } + } + + // 块定义 + [CommandMethod("Test_blockdef")] + public void Test_BlockDef() + { + using var tr = new DBTrans(); + // var line = new Line(new Point3d(0, 0, 0), new Point3d(1, 1, 0)); tr.BlockTable.Add("test", btr => { btr.Origin = new Point3d(0, 0, 0); }, - () => //图元 + () => // 图元 { return new List { new Line(new Point3d(0, 0, 0), new Point3d(1, 1, 0)) }; }, - () => //属性定义 + () => // 属性定义 { var id1 = new AttributeDefinition() { Position = new Point3d(0, 0, 0), Tag = "start", Height = 0.2 }; var id2 = new AttributeDefinition() { Position = new Point3d(1, 1, 0), Tag = "end", Height = 0.2 }; return new List { id1, id2 }; } ); - //ObjectId objectId = tr.BlockTable.Add("a");//新建块 - //objectId.GetObject().AddEntity();//测试添加空实体 + // ObjectId objectId = tr.BlockTable.Add("a");// 新建块 + // objectId.GetObject().AddEntity();// 测试添加空实体 tr.BlockTable.Add("test1", btr => { btr.Origin = new Point3d(0, 0, 0); - }, () => { var line = new Line(new Point3d(0, 0, 0), new Point3d(1, 1, 0)); @@ -38,28 +53,28 @@ public void BlockDef() return new List { line, acText }; }); } - //修改块定义 - [CommandMethod("blockdefchange")] - public void BlockDefChange() + // 修改块定义 + [CommandMethod("Test_blockdefchange")] + public void Test_BlockDefChange() { using var tr = new DBTrans(); - //var line = new Line(new Point3d(0, 0, 0), new Point3d(1, 1, 0)); - //tr.BlockTable.Change("test", btr => - //{ + // var line = new Line(new Point3d(0, 0, 0), new Point3d(1, 1, 0)); + // tr.BlockTable.Change("test", btr => + // { // btr.Origin = new Point3d(5, 5, 0); // btr.AddEntity(new Circle(new Point3d(0, 0, 0), Vector3d.ZAxis, 2)); // btr.GetEntities() // .ToList() - // .ForEach(e => e.Flush()); //刷新块显示 + // .ForEach(e => e.Flush()); // 刷新块显示 - //}); + // }); tr.BlockTable.Change("test", btr => { foreach (var id in btr) { var ent = tr.GetObject(id); - using (ent.ForWrite()) + using (ent!.ForWrite()) { if (ent is Dimension dBText) { @@ -71,11 +86,11 @@ public void BlockDefChange() } } }); - tr.Editor.Regen(); + tr.Editor?.Regen(); } - [CommandMethod("insertblockdef")] - public void InsertBlockDef() + [CommandMethod("Test_insertblockdef")] + public void Test_InsertBlockDef() { using var tr = new DBTrans(); var line1 = new Line(new Point3d(0, 0, 0), new Point3d(1, 1, 0)); @@ -98,11 +113,11 @@ public void InsertBlockDef() var att3 = new AttributeDefinition() { Position = new Point3d(10, 14, 0), Tag = "tagTest3", Height = 1, TextString = "valueTest3" }; var att4 = new AttributeDefinition() { Position = new Point3d(10, 16, 0), Tag = "tagTest4", Height = 1, TextString = "valueTest4" }; tr.BlockTable.Add("test2", new List { line3, line4 }, new List { att3, att4 }); - //tr.CurrentSpace.InsertBlock(new Point3d(4, 4, 0), "test1"); // 测试默认 - //tr.CurrentSpace.InsertBlock(new Point3d(4, 4, 0), "test2"); - //tr.CurrentSpace.InsertBlock(new Point3d(4, 4, 0), "test3"); //测试插入不存在的块定义 - //tr.CurrentSpace.InsertBlock(new Point3d(0, 0, 0), "test1", new Scale3d(2)); // 测试放大2倍 - //tr.CurrentSpace.InsertBlock(new Point3d(4, 4, 0), "test1", new Scale3d(2), Math.PI / 4); // 测试放大2倍,旋转45度 + // tr.CurrentSpace.InsertBlock(new Point3d(4, 4, 0), "test1"); // 测试默认 + // tr.CurrentSpace.InsertBlock(new Point3d(4, 4, 0), "test2"); + // tr.CurrentSpace.InsertBlock(new Point3d(4, 4, 0), "test3"); // 测试插入不存在的块定义 + // tr.CurrentSpace.InsertBlock(new Point3d(0, 0, 0), "test1", new Scale3d(2)); // 测试放大2倍 + // tr.CurrentSpace.InsertBlock(new Point3d(4, 4, 0), "test1", new Scale3d(2), Math.PI / 4); // 测试放大2倍,旋转45度 var def1 = new Dictionary { @@ -119,12 +134,12 @@ public void InsertBlockDef() tr.CurrentSpace.InsertBlock(new Point3d(-10, 0, 0), "test44"); } - [CommandMethod("addattsdef")] - public void AddAttsDef() + [CommandMethod("Test_addattsdef")] + public void Test_AddAttsDef() { using var tr = new DBTrans(); var blockid = Env.Editor.GetEntity("pick block:").ObjectId; - var blockref = tr.GetObject(blockid).BlockTableRecord; + var blockref = tr.GetObject(blockid)!.BlockTableRecord; var att1 = new AttributeDefinition() { Position = new Point3d(20, 20, 0), Tag = "addtagTest1", Height = 1, TextString = "valueTest1" }; var att2 = new AttributeDefinition() { Position = new Point3d(10, 12, 0), Tag = "tagTest2", Height = 1, TextString = "valueTest2" }; @@ -132,8 +147,8 @@ public void AddAttsDef() tr.BlockTable.AddAttsToBlocks(blockref, new List { att1, att2 }); } - [CommandMethod("testblocknullbug")] - public void TestBlockNullBug() + [CommandMethod("test_blocknullbug")] + public void Test_BlockNullBug() { using var tr = new DBTrans(); @@ -147,7 +162,7 @@ public void TestBlockNullBug() } [CommandMethod("test_block_file")] - public void TestBlockFile() + public void Test_BlockFile() { var tr = new DBTrans(); var id = tr.BlockTable.GetBlockFrom(@"C:\Users\vic\Desktop\test.dwg", false); @@ -155,8 +170,8 @@ public void TestBlockFile() } - [CommandMethod("testclip")] - public void TestClipBlock() + [CommandMethod("test_clip")] + public void Test_ClipBlock() { using var tr = new DBTrans(); tr.BlockTable.Add("test1", @@ -167,58 +182,50 @@ public void TestClipBlock() ); } ); - //tr.BlockTable.Add("hah"); + // tr.BlockTable.Add("hah"); var id = tr.CurrentSpace.InsertBlock(new Point3d(0, 0, 0), "test1"); - var bref = tr.GetObject(id); + var bref = tr.GetObject(id)!; var pts = new List { new Point3d(3, 3, 0), new Point3d(7, 3, 0), new Point3d(7, 7, 0), new Point3d(3, 7, 0) }; bref.ClipBlockRef(pts); var id1 = tr.CurrentSpace.InsertBlock(new Point3d(20, 20, 0), "test1"); var bref1 = tr.GetObject(id); - - bref1.ClipBlockRef(new Point3d(13, 13, 0), new Point3d(17, 17, 0)); + bref1?.ClipBlockRef(new Point3d(13, 13, 0), new Point3d(17, 17, 0)); } /// /// 给用户的测试程序,不知道对错 /// [CommandMethod("test_block_ej")] - public void EJ() + public void test_block_ej() { using (var tr = new DBTrans()) { - - //Point3d.Origin.AddBellowToModelSpace(100, 100, 5, 3, 30);//画波纹管 - - //Database db2 = new Database(false, true); - //string fullFileName = @".\MyBlockDwgFile\001.dwg"; - //db2.ReadDwgFile(fullFileName, System.IO.FileShare.Read, true, null); - //db2.CloseInput(true); - //string blockName = "test"; - //if (!tr.BlockTable.Has(blockName)) - //{ - // //tr.Database.Insert(blockName, db2, false);//插入块 + // Point3d.Origin.AddBellowToModelSpace(100, 100, 5, 3, 30);// 画波纹管 + + // Database db2 = new Database(false, true); + // string fullFileName = @".\MyBlockDwgFile\001.dwg"; + // db2.ReadDwgFile(fullFileName, System.IO.FileShare.Read, true, null); + // db2.CloseInput(true); + // string blockName = "test"; + // if (!tr.BlockTable.Has(blockName)) + // { + // // tr.Database.Insert(blockName, db2, false);// 插入块 // db.Insert(blockName, db2, false); - //} + // } string fullFileName = @"C:\Users\vic\Desktop\001.dwg"; var blockdef = tr.BlockTable.GetBlockFrom(fullFileName, false); - tr.Database.Clayer = tr.LayerTable["0"];//当前图层切换为0图层 + tr.Database.Clayer = tr.LayerTable["0"];// 当前图层切换为0图层 tr.LayerTable.Change(tr.Database.Clayer, ltr => { - ltr.Color = Color.FromColorIndex(ColorMethod.ByAci, 2); //ColorMethod.ByAci可以让我们使用AutoCAD ACI颜色索引……这里为2(表示黄色) + ltr.Color = Color.FromColorIndex(ColorMethod.ByAci, 2); // ColorMethod.ByAci可以让我们使用AutoCAD ACI颜色索引……这里为2(表示黄色) }); - ObjectId id = tr.ModelSpace.InsertBlock(Point3d.Origin, blockdef);//插入块参照 - - - - - + var id = tr.ModelSpace.InsertBlock(Point3d.Origin, blockdef);// 插入块参照 var entTest = tr.GetObject(id); - entTest.Draw(); - + entTest?.Draw(); } using var tr2 = new DBTrans(); @@ -232,83 +239,75 @@ public void EJ() return; } - var Bref = tr2.GetObject(PER.ObjectId); - //var BTR = tr.GetObject(Bref.BlockTableRecord, OpenMode.ForWrite); - ////如果知道块名字BTRName - //BlockTableRecord BTR = tr.GetObject(tr.BlockTable[blockName], OpenMode.ForWrite); + var Bref = tr2.GetObject(PER.ObjectId)!; + // var BTR = tr.GetObject(Bref.BlockTableRecord, OpenMode.ForWrite); + //// 如果知道块名字BTRName + // BlockTableRecord BTR = tr.GetObject(tr.BlockTable[blockName], OpenMode.ForWrite); var btr = tr2.BlockTable[Bref.Name]; tr2.BlockTable.Change(btr, ltr => { - foreach (ObjectId OID in ltr) { - var Ent = tr2.GetObject(OID); - using (Ent.ForWrite()) + var ent = tr2.GetObject(OID); + using (ent!.ForWrite()) { - if (Ent is MText mText) + if (ent is MText mText) { switch (mText.Text) { case "$$A": - mText.Contents = "hahaha"; - break; + mText.Contents = "hahaha"; + break; case "$$B": - ; - break; + ; + break; default: - ; - break; + ; + break; } - - }; - if (Ent is DBText dBText) { dBText.TextString = "haha"; }; - if (Ent is Dimension dimension) + } + else if (ent is DBText dBText) { dBText.TextString = "haha"; } + else if (ent is Dimension dimension) { switch (dimension.DimensionText) { case "$$pipeLen": - dimension.DimensionText = "350"; - dimension.RecomputeDimensionBlock(true); - break; + dimension.DimensionText = "350"; + dimension.RecomputeDimensionBlock(true); + break; default: - break; + break; } - }; + } } - - } - }); - - tr2.Editor.Regen(); - - + tr2.Editor?.Regen(); } [CommandMethod("W_KSZK")] public void QuickBlockDef() { - //Database db = HostApplicationServices.WorkingDatabase; + // Database db = HostApplicationServices.WorkingDatabase; Editor ed = Acap.DocumentManager.MdiActiveDocument.Editor; PromptSelectionOptions promptOpt = new() { MessageForAdding = "请选择需要快速制作块的对象" }; string blockName = "W_BLOCK_" + DateTime.Now.ToString("yyyyMMdd_HHmmss"); - //var rss = ed.GetSelection(promptOpt); + // var rss = ed.GetSelection(promptOpt); var rss = Env.Editor.GetSelection(promptOpt); using var tr = new DBTrans(); if (rss.Status == PromptStatus.OK) { - //SelectionSet ss = rss.Value; - //ObjectId[] ids = ss.GetObjectIds(); - //var ents = new List>(); - //var extents = new Extents3d(); - //foreach (var id in ids) - //{ + // SelectionSet ss = rss.Value; + // ObjectId[] ids = ss.GetObjectIds(); + // var ents = new List>(); + // var extents = new Extents3d(); + // foreach (var id in ids) + // { // Entity ent = tr.GetObject(id); // if (ent is null) // continue; @@ -326,57 +325,56 @@ public void QuickBlockDef() // { // ed.WriteMessage(exc.Message); // } - //} - //ents = ents.OrderBy(x => x.Value).ToList(); + // } + // ents = ents.OrderBy(x => x.Value).ToList(); var ents = rss.Value.GetEntities(); - //ents.ForEach(ent => extents.AddExtents(ent.GeometricExtents)); - var extents = ents.GetExtents(); - + // ents.ForEach(ent => extents.AddExtents(ent.GeometricExtents)); + var extents = ents!.GetExtents(); Point3d pt = extents.MinPoint; Matrix3d matrix = Matrix3d.Displacement(Point3d.Origin - pt); - //var newEnts = new List(); - //foreach (var ent in ents) - //{ + // var newEnts = new List(); + // foreach (var ent in ents) + // { // var newEnt = ent.Key; // newEnt.TransformBy(matrix); // newEnts.Add(newEnt); - //} - //if (tr.BlockTable.Has(blockName)) - //{ - // Application.ShowAlertDialog(Environment.NewLine + "块名重复,程序退出!"); + // } + // if (tr.BlockTable.Has(blockName)) + // { + // Acap.ShowAlertDialog(Environment.NewLine + "块名重复,程序退出!"); // return; - //} + // } ents.ForEach(ent => - ent.ForWrite(e => e.TransformBy(matrix))); - //var newents = ents.Select(ent => - //{ + ent?.ForWrite(e => e?.TransformBy(matrix))); + // var newents = ents.Select(ent => + // { // var maping = new IdMapping(); // return ent.DeepClone(ent, maping, true) as Entity; - //}); - var newents = ents.Select(ent => ent.Clone() as Entity); + // }); + var newents = ents.Select(ent => ent?.Clone() as Entity); - //ents.ForEach(ent => ent.ForWrite(e => e.Erase(true))); // 删除实体就会卡死,比较奇怪,估计是Clone()函数的问题 + // ents.ForEach(ent => ent.ForWrite(e => e.Erase(true))); // 删除实体就会卡死,比较奇怪,估计是Clone()函数的问题 // 经过测试不是删除的问题 - var btrId = tr.BlockTable.Add(blockName, newents); - ents.ForEach(ent => ent.ForWrite(e => e.Erase(true))); + var btrId = tr.BlockTable.Add(blockName, newents!); + ents.ForEach(ent => ent?.ForWrite(e => e?.Erase(true))); var bId = tr.CurrentSpace.InsertBlock(pt, blockName); - //tr.GetObject(bId, OpenMode.ForWrite).Move(Point3d.Origin, Point3d.Origin); - //var ed = Acap.DocumentManager.MdiActiveDocument.Editor; - //ed.Regen(); - //tr.Editor.Regen(); + // tr.GetObject(bId, OpenMode.ForWrite).Move(Point3d.Origin, Point3d.Origin); + // var ed = Acap.DocumentManager.MdiActiveDocument.Editor; + // ed.Regen(); + // tr.Editor.Regen(); // 调用regen() 卡死 } - //tr.Editor.Regen(); - //ed.Regen(); - //using (var tr = new DBTrans()) - //{ + // tr.Editor.Regen(); + // ed.Regen(); + // using (var tr = new DBTrans()) + // { // tr.CurrentSpace.InsertBlock(Point3d.Origin, blockName); // tr.Editor.Regen(); - //} + // } } - [CommandMethod("testquickblockdef")] - public void TestQuickBlockDef() + [CommandMethod("test_quickblockdef")] + public void Test_QuickBlockDef() { Database db = HostApplicationServices.WorkingDatabase; Editor ed = Acap.DocumentManager.MdiActiveDocument.Editor; @@ -386,7 +384,7 @@ public void TestQuickBlockDef() }; string blockName = "W_BLOCK_" + DateTime.Now.ToString("yyyyMMdd_HHmmss"); var rss = ed.GetSelection(promptOpt); - //var rss = Env.Editor.GetSelection(promptOpt); + // var rss = Env.Editor.GetSelection(promptOpt); if (rss.Status != PromptStatus.OK) { return; @@ -402,39 +400,39 @@ public void TestQuickBlockDef() foreach (var item in ids) { var ent = tr.GetObject(item, OpenMode.ForRead) as Entity; - - btr.AppendEntity(ent.Clone() as Entity); + btr.AppendEntity(ent!.Clone() as Entity); ent.ForWrite(e => e.Erase(true)); } - bt.UpgradeOpen(); + bt!.UpgradeOpen(); bt.Add(btr); tr.AddNewlyCreatedDBObject(btr, true); bt.DowngradeOpen(); // tr.Commit(); - //} + // } - //using (var tr1 = db.TransactionManager.StartTransaction()) - //{ - //var bt = tr1.GetObject(db.BlockTableId, OpenMode.ForRead) as BlockTable; + // using (var tr1 = db.TransactionManager.StartTransaction()) + // { + // var bt = tr1.GetObject(db.BlockTableId, OpenMode.ForRead) as BlockTable; var btr1 = tr.GetObject(db.CurrentSpaceId, OpenMode.ForWrite) as BlockTableRecord; var br = new BlockReference(Point3d.Origin, bt[blockName]) { ScaleFactors = default }; - btr1.AppendEntity(br); + btr1!.AppendEntity(br); tr.AddNewlyCreatedDBObject(br, true); btr1.DowngradeOpen(); ed.Regen(); tr.Commit(); - //ed.Regen(); - + // ed.Regen(); } public void TestWblock() { var curdb = HostApplicationServices.WorkingDatabase; - PromptSelectionOptions opts = new(); - opts.MessageForAdding = "选择对象"; + PromptSelectionOptions opts = new() + { + MessageForAdding = "选择对象" + }; var ss = Env.Editor.GetSelection(opts).Value; var ids = new ObjectIdCollection(ss.GetObjectIds()); var db = curdb.Wblock(ids, Point3d.Origin); @@ -449,7 +447,7 @@ public void TestChangeDynameicBlock() }; var blockid = Env.Editor.GetEntity("选择个块").ObjectId; using var tr = new DBTrans(); - var blockref = tr.GetObject(blockid); + var blockref = tr.GetObject(blockid)!; blockref.ChangeBlockProperty(pro); // 这是第一个函数的用法 } @@ -475,28 +473,25 @@ public void TestBack() ent.ForWrite(e => e.ColorIndex = 4); }); tr.Database.SaveAs(dwg, DwgVersion.Current); - } - } public class BlockImportClass { - - [CommandMethod("CBLL")] - public void Cbll() + [CommandMethod("Test_CBLL")] + public void Test_Cbll() { string filename = @"C:\Users\vic\Desktop\Drawing1.dwg"; using var tr = new DBTrans(); using var tr1 = new DBTrans(filename); - //tr.BlockTable.GetBlockFrom(filename, true); + // tr.BlockTable.GetBlockFrom(filename, true); string blkdefname = SymbolUtilityServices.RepairSymbolName(SymbolUtilityServices.GetSymbolNameFromPathName(filename, "dwg"), false); - tr.Database.Insert(blkdefname, tr1.Database, false); //插入了块定义,未插入块参照 + tr.Database.Insert(blkdefname, tr1.Database, false); // 插入了块定义,未插入块参照 } - - [CommandMethod("CBL")] - public void CombineBlocksIntoLibrary() +#if !NET35 + [CommandMethod("Test_CombineBlocksIntoLibrary")] + public void Test_CombineBlocksIntoLibrary() { Document doc = Acap.DocumentManager.MdiActiveDocument; @@ -620,4 +615,5 @@ public void CombineBlocksIntoLibrary() pathName ); } +#endif } \ No newline at end of file diff --git a/tests/Test/testeditor.cs b/tests/Test/testeditor.cs index bf4fd5f..60d742f 100644 --- a/tests/Test/testeditor.cs +++ b/tests/Test/testeditor.cs @@ -29,21 +29,17 @@ public void Testzoom() using var tr = new DBTrans(); var res = Env.Editor.GetEntity("\npick ent:"); if (res.Status == PromptStatus.OK) - { - Env.Editor.ZoomObject(res.ObjectId.GetObject()); - } - - + Env.Editor.ZoomObject(res.ObjectId.GetObject()!); } [CommandMethod("testzoomextent")] public void Testzoomextent() { - //using var tr = new DBTrans(); - //var res = Env.Editor.GetEntity("\npick ent:"); - //if (res.Status == PromptStatus.OK) - //{ + // using var tr = new DBTrans(); + // var res = Env.Editor.GetEntity("\npick ent:"); + // if (res.Status == PromptStatus.OK) + // { // Env.Editor.ZoomObject(res.ObjectId.GetObject()); - //} + // } Env.Editor.ZoomExtents(); } @@ -60,10 +56,8 @@ public void Testssget() { "B", action_b } }; - var ss = Env.Editor.SSGet( ":S", - messages: new string[2] { "get", "del" }, - keywords: keyword); - - Env.Print(ss); + var ss = Env.Editor.SSGet( ":S", messages: new string[2] { "get", "del" }, + keywords: keyword); + Env.Print(ss!); } } diff --git a/tests/Test/testenv.cs b/tests/Test/testenv.cs index 05ffc5c..e86343e 100644 --- a/tests/Test/testenv.cs +++ b/tests/Test/testenv.cs @@ -43,7 +43,7 @@ public void Testosmode() Env.OSMode = (Env.OSModeType)5179; // 追加模式 Env.OSMode |= Env.OSModeType.Center; - //检查是否有某个模式 + // 检查是否有某个模式 var os = Env.OSMode.Include(Env.OSModeType.Center); // 取消某个模式 Env.OSMode ^= Env.OSModeType.Center; @@ -60,12 +60,11 @@ public void Testosmode1() [CommandMethod("testcadver")] public void Testcadver() { - //Env.Print(AcadVersion.Versions); + // Env.Print(AcadVersion.Versions); AcadVersion.Versions.ForEach(v => Env.Print(v)); - AcadVersion.FromApp(Application.AcadApplication).Print(); + AcadVersion.FromApp(Acap.AcadApplication)?.Print(); 1.Print(); "1".Print(); - } [CommandMethod("TestGetVar")] @@ -80,10 +79,9 @@ public void TestGetVar() [CommandMethod("TestDwgVersion")] public void TestDwgVersion() { - // - //string filename = @"C:\Users\vic\Desktop\test.dwg"; - //var a = Helper.GetCadFileVersion(filename); - //a.Print(); - //((DwgVersion)a).Print(); + // string filename = @"C:\Users\vic\Desktop\test.dwg"; + // var a = Helper.GetCadFileVersion(filename); + // a.Print(); + // ((DwgVersion)a).Print(); } } diff --git a/tests/Test/testid.cs b/tests/Test/testid.cs index e61553b..10a4b35 100644 --- a/tests/Test/testid.cs +++ b/tests/Test/testid.cs @@ -15,15 +15,15 @@ public void TestId() { res.ObjectId.Erase(); } - //using (var tr = new DBTrans()) - //{ + // using (var tr = new DBTrans()) + // { // var res = Env.Editor.GetEntity("\npick ent:"); // if(res.Status == PromptStatus.OK) // { // res.ObjectId.Erase(); // } - //} + // } } [CommandMethod("testmycommand")] @@ -34,10 +34,10 @@ public void TestMyCommand() var l1 = new Line(new Point3d(0, 0, 0), new Point3d(100, 100, 0)); var blkred = trans.GetObject(Env.Database.CurrentSpaceId, OpenMode.ForWrite) as BlockTableRecord; - blkred.AppendEntity(l1); + blkred?.AppendEntity(l1); trans.AddNewlyCreatedDBObject(l1, true); trans.Commit(); - //dbtrans.Dispose(); + // dbtrans.Dispose(); } [CommandMethod("testtextstyle")] public void TestTextStyle() diff --git a/tests/Test/testselectfilter.cs b/tests/Test/testselectfilter.cs index d5f57fb..dab975c 100644 --- a/tests/Test/testselectfilter.cs +++ b/tests/Test/testselectfilter.cs @@ -1,4 +1,4 @@ -namespace Test; +namespace Test; public class Testselectfilter { diff --git a/tests/Test/wpf/Class1.cs b/tests/Test/wpf/Class1.cs index 4358668..b711b1e 100644 --- a/tests/Test/wpf/Class1.cs +++ b/tests/Test/wpf/Class1.cs @@ -1,13 +1,11 @@ -namespace Test.wpf +namespace Test.wpf; + +public class Class1 { - public class Class1 + [CommandMethod("testwpf")] + public void TestWPf() { - [CommandMethod("testwpf")] - public void TestWPf() - { - - var test = new TestView(); - Application.ShowModalWindow(test); - } + var test = new TestView(); + Acap.ShowModalWindow(test); } } diff --git a/tests/Test/wpf/TestView.xaml.cs b/tests/Test/wpf/TestView.xaml.cs index a86b16d..4eb001e 100644 --- a/tests/Test/wpf/TestView.xaml.cs +++ b/tests/Test/wpf/TestView.xaml.cs @@ -1,4 +1,6 @@ -using System; +namespace Test.wpf; + +using System; using System.Collections.Generic; using System.Linq; using System.Text; @@ -12,17 +14,15 @@ using System.Windows.Media.Imaging; using System.Windows.Shapes; -namespace Test.wpf + +/// +/// TestView.xaml 的交互逻辑 +/// +public partial class TestView : Window { - /// - /// TestView.xaml 的交互逻辑 - /// - public partial class TestView : Window + public TestView() { - public TestView() - { - InitializeComponent(); - DataContext = new TestViewModel(); - } + InitializeComponent(); + DataContext = new TestViewModel(); } } diff --git a/tests/Test/wpf/TestViewModel.cs b/tests/Test/wpf/TestViewModel.cs index 4cb7563..e726029 100644 --- a/tests/Test/wpf/TestViewModel.cs +++ b/tests/Test/wpf/TestViewModel.cs @@ -1,113 +1,82 @@ - +namespace Test.wpf; + using System.Windows; using System.Windows.Input; -namespace Test.wpf; - class TestViewModel : ViewModelBase { - - private string name; - - public string Name + string? name; + public string? Name { get { return name; } set { Set(ref name, value); } } - private RelayCommand clickCommand; + private RelayCommand? clickCommand; - public RelayCommand ClickCommand + public RelayCommand? ClickCommand { get { - if (clickCommand is null) - { - clickCommand = new( - execute => Name = "hello " + Name, - can => !string.IsNullOrEmpty(Name)); - } + clickCommand ??= + new(execute => Name = "hello " + Name, + can => !string.IsNullOrEmpty(Name)); return clickCommand; } } private bool receiveMouseMove; - public bool ReceiveMouseMove { get { return receiveMouseMove; } set { Set(ref receiveMouseMove, value); } } - private string tipText; - - public string TipText + private string? tipText; + public string? TipText { get { return tipText; } set { Set(ref tipText, value); } } - private RelayCommand loadedCommand; - public RelayCommand LoadCommand + private RelayCommand? loadedCommand; + public RelayCommand? LoadCommand { get { - if (loadedCommand is null) - { - loadedCommand = new( - execute => MessageBox.Show("程序加载完毕")); - } + loadedCommand ??= new(execute => MessageBox.Show("程序加载完毕")); return loadedCommand; } } - private RelayCommand mouseMoveCommand; - - public RelayCommand MouseMoveCommand + private RelayCommand? mouseMoveCommand; + public RelayCommand? MouseMoveCommand { get { - if (mouseMoveCommand is null) - { - mouseMoveCommand = new( - execute => - { + mouseMoveCommand ??= new( + execute => { var pt = execute.GetPosition(execute.Device.Target); var left = "左键放开"; var mid = "中键放开"; var right = "右键放开"; if (execute.LeftButton == MouseButtonState.Pressed) - { left = "左键放下"; - } if (execute.MiddleButton == MouseButtonState.Pressed) - { mid = "中键放下"; - } if (execute.RightButton == MouseButtonState.Pressed) - { right = "右键放下"; - } TipText = $"当前鼠标位置:X={pt.X},Y={pt.Y}。当前鼠标状态:{left}、{mid}、{right}"; }, can => ReceiveMouseMove); - } return mouseMoveCommand; } } - - - - - public TestViewModel() { Name = "world"; } - - - } diff --git a/tests/TestConsole/GlobalUsings.cs b/tests/TestConsole/GlobalUsings.cs new file mode 100644 index 0000000..6328a67 --- /dev/null +++ b/tests/TestConsole/GlobalUsings.cs @@ -0,0 +1,14 @@ +/// 系统引用 +global using System; +global using System.Collections; +global using System.Collections.Generic; +global using System.IO; +global using System.Linq; +global using System.Text; +global using System.Reflection; +global using System.Text.RegularExpressions; +global using Microsoft.Win32; +global using System.ComponentModel; +global using System.Runtime.CompilerServices; + +global using IFoxCAD.Basal; \ No newline at end of file diff --git a/tests/TestConsole/Helper.cs b/tests/TestConsole/Helper.cs new file mode 100644 index 0000000..763970a --- /dev/null +++ b/tests/TestConsole/Helper.cs @@ -0,0 +1,42 @@ +namespace TestConsole; + +public class LoopState +{ + const int PLS_NONE = 0; + const int PLS_EXCEPTIONAL = 1; + const int PLS_BROKEN = 2; + const int PLS_STOPPED = 4; + const int PLS_CANCELED = 8; + + private volatile int _LoopStateFlags = PLS_NONE; + + public bool IsRun => _LoopStateFlags == PLS_NONE; + public bool IsCancel => _LoopStateFlags == PLS_CANCELED; + public bool IsExceptional => _LoopStateFlags == PLS_EXCEPTIONAL; + + public bool IsBreak => (_LoopStateFlags & PLS_BROKEN) == PLS_BROKEN; + public bool IsStop => (_LoopStateFlags & PLS_STOPPED) == PLS_STOPPED; + public void Stop() => _LoopStateFlags = PLS_STOPPED; + public void Break() => _LoopStateFlags = PLS_BROKEN; +} + +public static class Helper +{ + public static void ForEach(this IEnumerable ints, Action action) + { + LoopState state = new(); + foreach (var item in ints) + { + action(item, state); + if (state.IsBreak) + break; + } + + //int forNum = 5; + //var result = Parallel.For(0, forNum, (int i, ParallelLoopState pls) => { + // if (i > 2) + // pls.Break(); + // Task.Delay(10).Wait(); + //}); + } +} \ No newline at end of file diff --git a/tests/TestConsole/Info.cs b/tests/TestConsole/Info.cs new file mode 100644 index 0000000..b6ad87f --- /dev/null +++ b/tests/TestConsole/Info.cs @@ -0,0 +1,41 @@ +namespace TestConsole; + +[Flags] +public enum PlugIn +{ + [Description("惊惊")] + JoinBox = 1, + [Description("源泉")] + YuanQuan = 2, + [Description("迷你")] + IMinCad = 4, + + Lisp = JoinBox | YuanQuan | IMinCad, + + DOCBAR = 8, + DUOTAB = 16, + + All = Lisp | DOCBAR | DUOTAB +} + + +[Flags] +public enum PlugIn2 +{ + [Description("惊惊")] + JoinBox = 1, + [Description("源泉")] + YuanQuan = 2, + [Description("迷你")] + IMinCad = 4, + + [Description("*Lisp*")] + Lisp = JoinBox | YuanQuan | IMinCad, + + DOCBAR = 8, + DUOTAB = 16, + + // all == *Lisp*|DOCBAR|DUOTAB + // 采取的行为是:注释的行为是特殊的,就按照注释的,否则,遍历子元素提取注释 + All = Lisp | DOCBAR | DUOTAB +} \ No newline at end of file diff --git a/tests/TestConsole/Program.cs b/tests/TestConsole/Program.cs index aa31926..9db5e76 100644 --- a/tests/TestConsole/Program.cs +++ b/tests/TestConsole/Program.cs @@ -1,11 +1,46 @@ // See https://aka.ms/new-console-template for more information using System; using System.Text; +using TestConsole; -//表达式树例子 +List list = new List(); +list.Add(1); +list.Add(2); +list.Add(3); +list.Add(4); +list.Add(5); +list.ForEach((x, loop) => { + if (x == 3) + loop.Break(); + Console.WriteLine(x); +}); + + + +// 乱序 +Console.WriteLine(PlugIn.JoinBox.PrintNote()); +Console.WriteLine(PlugIn.Lisp.PrintNote());// 这里先交换顺序来试试能不能成功 +Console.WriteLine(PlugIn.IMinCad.PrintNote()); +Console.WriteLine(PlugIn.YuanQuan.PrintNote()); +Console.WriteLine(PlugIn.All.PrintNote()); +Console.WriteLine(PlugIn.DOCBAR.PrintNote()); +Console.WriteLine(PlugIn.DUOTAB.PrintNote()); +Console.WriteLine("***************************************************"); +// 乱序2 +Console.WriteLine(PlugIn2.JoinBox.PrintNote()); +Console.WriteLine(PlugIn2.Lisp.PrintNote());// 这里先交换顺序来试试能不能成功 +Console.WriteLine(PlugIn2.IMinCad.PrintNote()); +Console.WriteLine(PlugIn2.YuanQuan.PrintNote()); +Console.WriteLine(PlugIn2.All.PrintNote()); +Console.WriteLine(PlugIn2.DOCBAR.PrintNote()); +Console.WriteLine(PlugIn2.DUOTAB.PrintNote()); + +EnumEx.CleanCache(); + +// 表达式树例子 TestConsole.Test_Expression.Demo3(); -//TestConsole.Test_Expression.Demo1(); +// TestConsole.Test_Expression.Demo1(); #region 元组测试 var valuetuple = (1, 2); @@ -26,13 +61,13 @@ #region 测试遍历枚举 -//Season a = Season.Autumn; -//Console.WriteLine($"Integral value of {a} is {(int)a}"); // output: Integral value of Autumn is 2 -//foreach (var enumItem in Enum.GetValues(typeof(Season))) +// Season a = Season.Autumn; +// Console.WriteLine($"Integral value of {a} is {(int)a}"); // output: Integral value of Autumn is 2 +// foreach (var enumItem in Enum.GetValues(typeof(Season))) // Console.WriteLine((byte)enumItem); var sb = new StringBuilder(); -/*因为 net framework 没写好的原因,导致直接使用迭代器反而更慢,到了net60就迭代器比foreach更快*/ +/*因为 net framework 没写好的原因,导致直接使用迭代器反而更慢,到了NET60就迭代器比foreach更快*/ var enums = Enum.GetValues(typeof(Season)).GetEnumerator(); while (enums.MoveNext()) { @@ -41,8 +76,8 @@ } Console.WriteLine(sb); -sb.Remove(sb.Length - 1, 1);//剔除末尾, -//因为有返回值所以容易理解成 sb = sb.Remove(sb.Length - 1, 1); +sb.Remove(sb.Length - 1, 1);// 剔除末尾, +// 因为有返回值所以容易理解成 sb = sb.Remove(sb.Length - 1, 1); Console.WriteLine(sb); public enum Season : byte diff --git a/tests/TestConsole/RuntimeHelpers.cs b/tests/TestConsole/RuntimeHelpers.cs index 9e09b29..310242d 100644 --- a/tests/TestConsole/RuntimeHelpers.cs +++ b/tests/TestConsole/RuntimeHelpers.cs @@ -26,7 +26,7 @@ public static T[] GetSubArray(T[] array, Range range) if (length == 0) { - //return Array.Empty(); + // return Array.Empty(); return new T[0]; diff --git a/tests/TestConsole/TestConsole.csproj b/tests/TestConsole/TestConsole.csproj index 293a6aa..53e4165 100644 --- a/tests/TestConsole/TestConsole.csproj +++ b/tests/TestConsole/TestConsole.csproj @@ -5,7 +5,7 @@ enable Exe - net45 + NET45 enable preview @@ -13,10 +13,5 @@ - - - - - - + diff --git "a/tests/TestConsole/\350\241\250\350\276\276\345\274\217\346\240\221.cs" "b/tests/TestConsole/\350\241\250\350\276\276\345\274\217\346\240\221.cs" index f5cc477..4c706dd 100644 --- "a/tests/TestConsole/\350\241\250\350\276\276\345\274\217\346\240\221.cs" +++ "b/tests/TestConsole/\350\241\250\350\276\276\345\274\217\346\240\221.cs" @@ -1,143 +1,142 @@ -using System; +namespace TestConsole; + +using System; using System.Collections.Generic; using System.Diagnostics; using System.Linq; using System.Linq.Expressions; -namespace TestConsole +/// +/// 表达式树 +/// MSDN链接 +/// +public class Test_Expression { - /// - /// 表达式树 - /// MSDN链接 - /// - public class Test_Expression + public static void Demo1() { - public static void Demo1() - { - // 官方例子:表达式体内只有一个式子 - // 创建表达式树 - Expression> exprTree = num => num < 5; + // 官方例子:表达式体内只有一个式子 + // 创建表达式树 + Expression> exprTree = num => num < 5; + + // 分解表达式树 + ParameterExpression param = exprTree.Parameters[0];// num + BinaryExpression operation = (BinaryExpression)exprTree.Body;// 函数体 {(num < 5)} + ParameterExpression left = (ParameterExpression)operation.Left;// 左节点 num + ConstantExpression right = (ConstantExpression)operation.Right;// 右表达式 5 + + Console.WriteLine("表达式树例子: {0} => {1} {2} {3}", + param.Name, left.Name, operation.NodeType, right.Value); + Console.Read(); + } - // 分解表达式树 - ParameterExpression param = exprTree.Parameters[0];//num - BinaryExpression operation = (BinaryExpression)exprTree.Body;//函数体 {(num < 5)} - ParameterExpression left = (ParameterExpression)operation.Left;//左节点 num - ConstantExpression right = (ConstantExpression)operation.Right;//右表达式 5 - Console.WriteLine("表达式树例子: {0} => {1} {2} {3}", - param.Name, left.Name, operation.NodeType, right.Value); - Console.Read(); - } + public static void Demo2() + { + // 这里是会报错的!! 原因就是体内有多个例子需要分解!! + // Expression> exprTree = x => x > 5 && x < 50; + // + // // 分解表达式树 + // ParameterExpression param = exprTree.Parameters[0];// x + // BinaryExpression operation = (BinaryExpression)exprTree.Body;// 函数体 {((x > 5) AndAlso (x < 50))} + // + // ParameterExpression left = (ParameterExpression)operation.Left;// 左节点.......这里报错 + // ConstantExpression right = (ConstantExpression)operation.Right;// 右表达式.....这里报错 + // + // Console.WriteLine("表达式树例子: {0} => {1} {2} {3}", + // param.Name, left.Name, operation.NodeType, right.Value); + } - public static void Demo2() - { - // 这里是会报错的!! 原因就是体内有多个例子需要分解!! - // Expression> exprTree = x => x > 5 && x < 50; - // - // // 分解表达式树 - // ParameterExpression param = exprTree.Parameters[0];// x - // BinaryExpression operation = (BinaryExpression)exprTree.Body;//函数体 {((x > 5) AndAlso (x < 50))} - // - // ParameterExpression left = (ParameterExpression)operation.Left;//左节点.......这里报错 - // ConstantExpression right = (ConstantExpression)operation.Right;//右表达式.....这里报错 - // - // Console.WriteLine("表达式树例子: {0} => {1} {2} {3}", - // param.Name, left.Name, operation.NodeType, right.Value); - } - - - //博客园例子,表达式体内有多个式子 - public static void Demo3() - { - List names = new() { "Cai", "Edward", "Beauty" }; - - Console.WriteLine("******************一个表达式"); - Expression> lambda2 = name => name.Length > 2 && name.Length < 4; - var method2 = ReBuildExpression(lambda2); - var query2 = names.Where(method2); - foreach (string n in query2) - Console.WriteLine(n); - - Console.WriteLine("******************二个表达式"); - Expression> lambda0 = item => item.Length > 2; - Expression> lambda1 = item => item.Length < 4; - var method = ReBuildExpression(lambda0, lambda1); - var query = names.Where(method); - foreach (string n in query) - Console.WriteLine(n); - Console.WriteLine("******************表达式结束"); - Console.Read(); - } - - - static Func ReBuildExpression(Expression> lambda) - { - MyExpressionVisitor my = new() - { - Parameter = Expression.Parameter(typeof(string), "name") - }; - - Expression left = my.Modify(lambda.Body); - //构造一个新的表达式 - var newLambda = Expression.Lambda>(left, my.Parameter); - return newLambda.Compile(); - } - - - - /// - /// 重构表达式_合并 - /// - /// 匿名函数表达式1 - /// 匿名函数表达式2 - /// - static Func ReBuildExpression(Expression> lambda0, - Expression> lambda1) + // 博客园例子,表达式体内有多个式子 + public static void Demo3() + { + List names = new() { "Cai", "Edward", "Beauty" }; + + Console.WriteLine("******************一个表达式"); + Expression> lambda2 = name => name.Length > 2 && name.Length < 4; + var method2 = ReBuildExpression(lambda2); + var query2 = names.Where(method2); + foreach (string n in query2) + Console.WriteLine(n); + + Console.WriteLine("******************二个表达式"); + Expression> lambda0 = item => item.Length > 2; + Expression> lambda1 = item => item.Length < 4; + var method = ReBuildExpression(lambda0, lambda1); + var query = names.Where(method); + foreach (string n in query) + Console.WriteLine(n); + Console.WriteLine("******************表达式结束"); + Console.Read(); + } + + + static Func ReBuildExpression(Expression> lambda) + { + MyExpressionVisitor my = new() { - MyExpressionVisitor my = new() - { - Parameter = Expression.Parameter(typeof(string), "name") - }; - - Expression left = my.Modify(lambda0.Body); - Expression right = my.Modify(lambda1.Body); - var expression = Expression.AndAlso(left, right);//就是 && 合并两个匿名函数 - - //构造一个新的表达式 - var newLambda = Expression.Lambda>(expression, my.Parameter); - return newLambda.Compile(); - } + Parameter = Expression.Parameter(typeof(string), "name") + }; + + Expression left = my.Modify(lambda.Body); + // 构造一个新的表达式 + var newLambda = Expression.Lambda>(left, my.Parameter); + return newLambda.Compile(); } + /// - /// 表达式参数分解 - /// 博客园链接 + /// 重构表达式_合并 /// - public class MyExpressionVisitor : ExpressionVisitor + /// 匿名函数表达式1 + /// 匿名函数表达式2 + /// + static Func ReBuildExpression(Expression> lambda0, + Expression> lambda1) { - /// - /// 公共参数 - /// - public ParameterExpression? Parameter; - /// - /// 返回替换后的参数表达式 - /// - /// - /// - public Expression Modify(Expression exp) + MyExpressionVisitor my = new() { - return Visit(exp); - } - /// - /// 重写参数 - /// - /// - /// - protected override Expression? VisitParameter(ParameterExpression p) - { - return Parameter; - } + Parameter = Expression.Parameter(typeof(string), "name") + }; + + Expression left = my.Modify(lambda0.Body); + Expression right = my.Modify(lambda1.Body); + var expression = Expression.AndAlso(left, right);// 就是 && 合并两个匿名函数 + + // 构造一个新的表达式 + var newLambda = Expression.Lambda>(expression, my.Parameter); + return newLambda.Compile(); + } +} + + +/// +/// 表达式参数分解 +/// 博客园链接 +/// +public class MyExpressionVisitor : ExpressionVisitor +{ + /// + /// 公共参数 + /// + public ParameterExpression? Parameter; + /// + /// 返回替换后的参数表达式 + /// + /// + /// + public Expression Modify(Expression exp) + { + return Visit(exp); + } + /// + /// 重写参数 + /// + /// + /// + protected override Expression? VisitParameter(ParameterExpression p) + { + return Parameter; } } -- Gitee From 133466ce07961861ccdfe42d6265ab8d6e7fd71b Mon Sep 17 00:00:00 2001 From: liuqihong <540762622@qq.com> Date: Wed, 14 Sep 2022 22:34:21 +0800 Subject: [PATCH 491/675] =?UTF-8?q?=E8=BF=9B=E5=85=A50.4=E7=89=88=E6=9C=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/IFoxCAD.Basal/IFoxCAD.Basal.csproj | 4 ++-- src/IFoxCAD.Cad/IFoxCAD.Aacad08/IFoxCAD.Acad08.csproj | 2 +- src/IFoxCAD.Cad/IFoxCAD.Aacad09plus/IFoxCAD.Acad09plus.csproj | 2 +- {tests/TestShared => src/IFoxCAD.LoadForm}/TestNetLoad.cs | 4 +++- src/IFoxCAD.WPF/IFoxCAD.WPF.csproj | 4 ++-- tests/TestShared/TestShared.projitems | 1 - 6 files changed, 9 insertions(+), 8 deletions(-) rename {tests/TestShared => src/IFoxCAD.LoadForm}/TestNetLoad.cs (94%) diff --git a/src/IFoxCAD.Basal/IFoxCAD.Basal.csproj b/src/IFoxCAD.Basal/IFoxCAD.Basal.csproj index 3c441a3..dbc9743 100644 --- a/src/IFoxCAD.Basal/IFoxCAD.Basal.csproj +++ b/src/IFoxCAD.Basal/IFoxCAD.Basal.csproj @@ -6,9 +6,9 @@ NET35;NET40;NET45 true - 0.3.6 + 0.4 InspireFunction - xsfhlzh;vicwjb + xsfhlzh;vicwjb;liuqihong 基于.NET的二次开发基本类库 InspireFunction https://gitee.com/inspirefunction/ifoxcad diff --git a/src/IFoxCAD.Cad/IFoxCAD.Aacad08/IFoxCAD.Acad08.csproj b/src/IFoxCAD.Cad/IFoxCAD.Aacad08/IFoxCAD.Acad08.csproj index d0e62e6..6ca24d1 100644 --- a/src/IFoxCAD.Cad/IFoxCAD.Aacad08/IFoxCAD.Acad08.csproj +++ b/src/IFoxCAD.Cad/IFoxCAD.Aacad08/IFoxCAD.Acad08.csproj @@ -6,7 +6,7 @@ enable true true - 0.3.7 + 0.4 InspireFunction xsfhlzh;vicwjb;liuqihong 基于.NET的Cad二次开发类库 diff --git a/src/IFoxCAD.Cad/IFoxCAD.Aacad09plus/IFoxCAD.Acad09plus.csproj b/src/IFoxCAD.Cad/IFoxCAD.Aacad09plus/IFoxCAD.Acad09plus.csproj index 474ceee..f4ebfac 100644 --- a/src/IFoxCAD.Cad/IFoxCAD.Aacad09plus/IFoxCAD.Acad09plus.csproj +++ b/src/IFoxCAD.Cad/IFoxCAD.Aacad09plus/IFoxCAD.Acad09plus.csproj @@ -6,7 +6,7 @@ enable true true - 0.3.7 + 0.4 InspireFunction xsfhlzh;vicwjb;liuqihong 基于.NET的Cad二次开发类库 diff --git a/tests/TestShared/TestNetLoad.cs b/src/IFoxCAD.LoadForm/TestNetLoad.cs similarity index 94% rename from tests/TestShared/TestNetLoad.cs rename to src/IFoxCAD.LoadForm/TestNetLoad.cs index f1de378..1c15706 100644 --- a/tests/TestShared/TestNetLoad.cs +++ b/src/IFoxCAD.LoadForm/TestNetLoad.cs @@ -1,4 +1,5 @@ -namespace Test; +#if test +namespace Test; public class NetLoad { @@ -24,3 +25,4 @@ public static void Test_NetLoadx() // Acap.ShowModalDialog(_form); } } +#endif \ No newline at end of file diff --git a/src/IFoxCAD.WPF/IFoxCAD.WPF.csproj b/src/IFoxCAD.WPF/IFoxCAD.WPF.csproj index 8020198..59cd3aa 100644 --- a/src/IFoxCAD.WPF/IFoxCAD.WPF.csproj +++ b/src/IFoxCAD.WPF/IFoxCAD.WPF.csproj @@ -9,8 +9,8 @@ true true true - 0.3.0 - xsfhlzh;vicwjb + 0.4 + xsfhlzh;vicwjb;liuqihong InspireFunction WPF的简单MVVM模式开发类库 InspireFunction diff --git a/tests/TestShared/TestShared.projitems b/tests/TestShared/TestShared.projitems index ea9afeb..78d3e20 100644 --- a/tests/TestShared/TestShared.projitems +++ b/tests/TestShared/TestShared.projitems @@ -26,7 +26,6 @@ - -- Gitee From 92652f7b5cad5bd5a6a44606aabbd6d5fbc1d448 Mon Sep 17 00:00:00 2001 From: liuqihong <540762622@qq.com> Date: Wed, 14 Sep 2022 22:47:36 +0800 Subject: [PATCH 492/675] =?UTF-8?q?LoopState=E6=96=87=E4=BB=B6=E7=BB=9F?= =?UTF-8?q?=E4=B8=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/IFoxCAD.Basal/LoopState.cs | 24 +++++++++++++++++++ .../ExtensionMethod/Enums.cs | 21 ---------------- tests/TestConsole/Helper.cs | 20 ---------------- 3 files changed, 24 insertions(+), 41 deletions(-) create mode 100644 src/IFoxCAD.Basal/LoopState.cs diff --git a/src/IFoxCAD.Basal/LoopState.cs b/src/IFoxCAD.Basal/LoopState.cs new file mode 100644 index 0000000..44b25a6 --- /dev/null +++ b/src/IFoxCAD.Basal/LoopState.cs @@ -0,0 +1,24 @@ +namespace IFoxCAD.Basal; + +/// +/// 控制循环结束 +/// +public class LoopState +{ + const int PLS_NONE = 0; + const int PLS_EXCEPTIONAL = 1; + const int PLS_BROKEN = 2; + const int PLS_STOPPED = 4; + const int PLS_CANCELED = 8; + + private volatile int _LoopStateFlags = PLS_NONE; + + public bool IsRun => _LoopStateFlags == PLS_NONE; + public bool IsCancel => _LoopStateFlags == PLS_CANCELED; + public bool IsExceptional => _LoopStateFlags == PLS_EXCEPTIONAL; + + public bool IsBreak => (_LoopStateFlags & PLS_BROKEN) == PLS_BROKEN; + public bool IsStop => (_LoopStateFlags & PLS_STOPPED) == PLS_STOPPED; + public void Stop() => _LoopStateFlags = PLS_STOPPED; + public void Break() => _LoopStateFlags = PLS_BROKEN; +} \ No newline at end of file diff --git a/src/IFoxCAD.Cad.Shared/ExtensionMethod/Enums.cs b/src/IFoxCAD.Cad.Shared/ExtensionMethod/Enums.cs index 2dd94b5..d100ded 100644 --- a/src/IFoxCAD.Cad.Shared/ExtensionMethod/Enums.cs +++ b/src/IFoxCAD.Cad.Shared/ExtensionMethod/Enums.cs @@ -171,25 +171,4 @@ public enum FontTTF Arial, [Description("Romans")] Romans -} - - -public class LoopState -{ - const int PLS_NONE = 0; - const int PLS_EXCEPTIONAL = 1; - const int PLS_BROKEN = 2; - const int PLS_STOPPED = 4; - const int PLS_CANCELED = 8; - - private volatile int _LoopStateFlags = PLS_NONE; - - public bool IsRun => _LoopStateFlags == PLS_NONE; - public bool IsCancel => _LoopStateFlags == PLS_CANCELED; - public bool IsExceptional => _LoopStateFlags == PLS_EXCEPTIONAL; - - public bool IsBreak => (_LoopStateFlags & PLS_BROKEN) == PLS_BROKEN; - public bool IsStop => (_LoopStateFlags & PLS_STOPPED) == PLS_STOPPED; - public void Stop() => _LoopStateFlags = PLS_STOPPED; - public void Break() => _LoopStateFlags = PLS_BROKEN; } \ No newline at end of file diff --git a/tests/TestConsole/Helper.cs b/tests/TestConsole/Helper.cs index 763970a..763424a 100644 --- a/tests/TestConsole/Helper.cs +++ b/tests/TestConsole/Helper.cs @@ -1,25 +1,5 @@ namespace TestConsole; -public class LoopState -{ - const int PLS_NONE = 0; - const int PLS_EXCEPTIONAL = 1; - const int PLS_BROKEN = 2; - const int PLS_STOPPED = 4; - const int PLS_CANCELED = 8; - - private volatile int _LoopStateFlags = PLS_NONE; - - public bool IsRun => _LoopStateFlags == PLS_NONE; - public bool IsCancel => _LoopStateFlags == PLS_CANCELED; - public bool IsExceptional => _LoopStateFlags == PLS_EXCEPTIONAL; - - public bool IsBreak => (_LoopStateFlags & PLS_BROKEN) == PLS_BROKEN; - public bool IsStop => (_LoopStateFlags & PLS_STOPPED) == PLS_STOPPED; - public void Stop() => _LoopStateFlags = PLS_STOPPED; - public void Break() => _LoopStateFlags = PLS_BROKEN; -} - public static class Helper { public static void ForEach(this IEnumerable ints, Action action) -- Gitee From b67f984e5faf6d42ebde943fc32edde9a17b2ff7 Mon Sep 17 00:00:00 2001 From: liuqihong <540762622@qq.com> Date: Wed, 14 Sep 2022 22:49:39 +0800 Subject: [PATCH 493/675] =?UTF-8?q?LoopState=E7=BB=9F=E4=B8=80=E6=96=87?= =?UTF-8?q?=E4=BB=B6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/IFoxCAD.Basal/LoopState.cs | 24 ++++++++++++++++++++++++ src/IFoxCAD.Cad/ExtensionMethod/Enums.cs | 21 --------------------- tests/TestConsole/Helper.cs | 20 -------------------- 3 files changed, 24 insertions(+), 41 deletions(-) create mode 100644 src/IFoxCAD.Basal/LoopState.cs diff --git a/src/IFoxCAD.Basal/LoopState.cs b/src/IFoxCAD.Basal/LoopState.cs new file mode 100644 index 0000000..44b25a6 --- /dev/null +++ b/src/IFoxCAD.Basal/LoopState.cs @@ -0,0 +1,24 @@ +namespace IFoxCAD.Basal; + +/// +/// 控制循环结束 +/// +public class LoopState +{ + const int PLS_NONE = 0; + const int PLS_EXCEPTIONAL = 1; + const int PLS_BROKEN = 2; + const int PLS_STOPPED = 4; + const int PLS_CANCELED = 8; + + private volatile int _LoopStateFlags = PLS_NONE; + + public bool IsRun => _LoopStateFlags == PLS_NONE; + public bool IsCancel => _LoopStateFlags == PLS_CANCELED; + public bool IsExceptional => _LoopStateFlags == PLS_EXCEPTIONAL; + + public bool IsBreak => (_LoopStateFlags & PLS_BROKEN) == PLS_BROKEN; + public bool IsStop => (_LoopStateFlags & PLS_STOPPED) == PLS_STOPPED; + public void Stop() => _LoopStateFlags = PLS_STOPPED; + public void Break() => _LoopStateFlags = PLS_BROKEN; +} \ No newline at end of file diff --git a/src/IFoxCAD.Cad/ExtensionMethod/Enums.cs b/src/IFoxCAD.Cad/ExtensionMethod/Enums.cs index 2dd94b5..d100ded 100644 --- a/src/IFoxCAD.Cad/ExtensionMethod/Enums.cs +++ b/src/IFoxCAD.Cad/ExtensionMethod/Enums.cs @@ -171,25 +171,4 @@ public enum FontTTF Arial, [Description("Romans")] Romans -} - - -public class LoopState -{ - const int PLS_NONE = 0; - const int PLS_EXCEPTIONAL = 1; - const int PLS_BROKEN = 2; - const int PLS_STOPPED = 4; - const int PLS_CANCELED = 8; - - private volatile int _LoopStateFlags = PLS_NONE; - - public bool IsRun => _LoopStateFlags == PLS_NONE; - public bool IsCancel => _LoopStateFlags == PLS_CANCELED; - public bool IsExceptional => _LoopStateFlags == PLS_EXCEPTIONAL; - - public bool IsBreak => (_LoopStateFlags & PLS_BROKEN) == PLS_BROKEN; - public bool IsStop => (_LoopStateFlags & PLS_STOPPED) == PLS_STOPPED; - public void Stop() => _LoopStateFlags = PLS_STOPPED; - public void Break() => _LoopStateFlags = PLS_BROKEN; } \ No newline at end of file diff --git a/tests/TestConsole/Helper.cs b/tests/TestConsole/Helper.cs index 763970a..763424a 100644 --- a/tests/TestConsole/Helper.cs +++ b/tests/TestConsole/Helper.cs @@ -1,25 +1,5 @@ namespace TestConsole; -public class LoopState -{ - const int PLS_NONE = 0; - const int PLS_EXCEPTIONAL = 1; - const int PLS_BROKEN = 2; - const int PLS_STOPPED = 4; - const int PLS_CANCELED = 8; - - private volatile int _LoopStateFlags = PLS_NONE; - - public bool IsRun => _LoopStateFlags == PLS_NONE; - public bool IsCancel => _LoopStateFlags == PLS_CANCELED; - public bool IsExceptional => _LoopStateFlags == PLS_EXCEPTIONAL; - - public bool IsBreak => (_LoopStateFlags & PLS_BROKEN) == PLS_BROKEN; - public bool IsStop => (_LoopStateFlags & PLS_STOPPED) == PLS_STOPPED; - public void Stop() => _LoopStateFlags = PLS_STOPPED; - public void Break() => _LoopStateFlags = PLS_BROKEN; -} - public static class Helper { public static void ForEach(this IEnumerable ints, Action action) -- Gitee From 15bb2c8847866a5dd3df558f3b54e593f13c0326 Mon Sep 17 00:00:00 2001 From: liuqihong <540762622@qq.com> Date: Thu, 15 Sep 2022 02:45:54 +0800 Subject: [PATCH 494/675] =?UTF-8?q?=E4=BF=AE=E6=94=B9=E5=BE=AA=E7=8E=AF?= =?UTF-8?q?=E7=BB=88=E6=AD=A2=E6=96=B9=E5=BC=8F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../ExtensionMethod/CollectionEx.cs | 92 ++++++++++++------- src/IFoxCAD.Cad.Shared/Runtime/SymbolTable.cs | 4 +- 2 files changed, 60 insertions(+), 36 deletions(-) diff --git a/src/IFoxCAD.Cad.Shared/ExtensionMethod/CollectionEx.cs b/src/IFoxCAD.Cad.Shared/ExtensionMethod/CollectionEx.cs index bfbed62..2c4e8ce 100644 --- a/src/IFoxCAD.Cad.Shared/ExtensionMethod/CollectionEx.cs +++ b/src/IFoxCAD.Cad.Shared/ExtensionMethod/CollectionEx.cs @@ -71,33 +71,59 @@ public static List ToList(this ObjectIdCollection ids) /// - /// 遍历集合的迭代器,执行action委托 + /// 遍历集合,执行委托 /// /// 集合值的类型 /// 集合 - /// 要运行的委托 + /// 委托 public static void ForEach(this IEnumerable source, Action action) { + if (action == null) + throw new ArgumentNullException(nameof(action)); + foreach (var element in source) - { - action?.Invoke(element); - } + action.Invoke(element); } /// - /// 同时遍历集合索引和值的迭代器,执行action委托 + /// 遍历集合,执行委托
    + /// 输出索引值 ///
    /// 集合值的类型 /// 集合 - /// 要运行的委托 + /// 委托 public static void ForEach(this IEnumerable source, Action action) { + if (action == null) + throw new ArgumentNullException(nameof(action)); + int i = 0; - foreach (var item in source) + foreach (var element in source) { - action?.Invoke(i, item); + action.Invoke(i, element); i++; } + } + /// + /// 遍历集合,执行委托
    + /// 输出索引值,允许循环中断 + ///
    + /// 集合值的类型 + /// 集合 + /// 委托 + public static void ForEach(this IEnumerable source, Action action) + { + if (action == null) + throw new ArgumentNullException(nameof(action)); + LoopState state = new();/*这种方式比Action改Func更友好*/ + int i = 0; + foreach (var element in source) + { + action.Invoke(i, element, state); + if (!state.IsRun) + break; + i++; + } } @@ -123,31 +149,31 @@ public static bool Contains(this KeywordCollection collection, string name, switch (keywordName) { case KeywordName.GlobalName: - for (int i = 0; i < collection.Count; i++) - if (collection[i].GlobalName == name) - { - contains = true; - break; - } - break; + for (int i = 0; i < collection.Count; i++) + if (collection[i].GlobalName == name) + { + contains = true; + break; + } + break; case KeywordName.LocalName: - for (int i = 0; i < collection.Count; i++) - if (collection[i].LocalName == name) - { - contains = true; - break; - } - break; + for (int i = 0; i < collection.Count; i++) + if (collection[i].LocalName == name) + { + contains = true; + break; + } + break; case KeywordName.DisplayName: - for (int i = 0; i < collection.Count; i++) - if (collection[i].DisplayName == name) - { - contains = true; - break; - } - break; + for (int i = 0; i < collection.Count; i++) + if (collection[i].DisplayName == name) + { + contains = true; + break; + } + break; default: - break; + break; } return contains; } @@ -193,6 +219,6 @@ public static List GetValues(this IdMapping idmap) foreach (IdPair item in idmap) ids.Add(item.Value); return ids; - } + } #endregion -} +} \ No newline at end of file diff --git a/src/IFoxCAD.Cad.Shared/Runtime/SymbolTable.cs b/src/IFoxCAD.Cad.Shared/Runtime/SymbolTable.cs index 8f96bb9..8e0efba 100644 --- a/src/IFoxCAD.Cad.Shared/Runtime/SymbolTable.cs +++ b/src/IFoxCAD.Cad.Shared/Runtime/SymbolTable.cs @@ -342,12 +342,10 @@ public void ForEach(Action action, var record = GetRecord(id, openMode); if (record is not null) action(record, state); - if (state.IsBreak) + if (!state.IsRun) break; } } - - #endregion #region IEnumerable 成员 -- Gitee From 4061eaab764ffc7bbc8d3f667dff6bbe9b49b7e3 Mon Sep 17 00:00:00 2001 From: liuqihong <540762622@qq.com> Date: Thu, 15 Sep 2022 02:48:00 +0800 Subject: [PATCH 495/675] =?UTF-8?q?=E4=BF=AE=E6=94=B9=E6=B7=B1=E5=BA=A6?= =?UTF-8?q?=E5=85=8B=E9=9A=86=E6=96=B9=E6=B3=95=E5=90=8D=E9=81=BF=E5=85=8D?= =?UTF-8?q?=E5=92=8C=E5=8E=9F=E7=94=9F=E5=86=B2=E7=AA=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../ExtensionMethod/SymbolTableRecordEx.cs | 9 ++++----- .../HatchConverter.cs" | 2 +- 2 files changed, 5 insertions(+), 6 deletions(-) diff --git a/src/IFoxCAD.Cad.Shared/ExtensionMethod/SymbolTableRecordEx.cs b/src/IFoxCAD.Cad.Shared/ExtensionMethod/SymbolTableRecordEx.cs index 5a6f4cc..4a5737e 100644 --- a/src/IFoxCAD.Cad.Shared/ExtensionMethod/SymbolTableRecordEx.cs +++ b/src/IFoxCAD.Cad.Shared/ExtensionMethod/SymbolTableRecordEx.cs @@ -23,11 +23,11 @@ public static class SymbolTableRecordEx /// /// 图元id集合,注意所有成员都要在同一个空间中 /// 克隆后的id词典 - public static IdMapping DeepClone(this BlockTableRecord btr, ObjectIdCollection objIds) + public static IdMapping DeepCloneEx(this BlockTableRecord btr, ObjectIdCollection objIds) { if (objIds is null || objIds.Count == 0) throw new ArgumentNullException(nameof(objIds)); - + var db = objIds[0].Database; IdMapping mapping = new(); using (btr.ForWrite()) @@ -39,7 +39,7 @@ public static IdMapping DeepClone(this BlockTableRecord btr, ObjectIdCollection // 不在此提取,为了此函数被高频调用 // 获取克隆键值对(旧块名,新块名) // foreach (ObjectId item in blockIds) - // result.Add(mapping[item].Value); + // result.Add(mapping[item].Value); } catch (System.Exception e) { @@ -289,7 +289,6 @@ public static ObjectId AddPline(this BlockTableRecord btr, Action? action = default, Transaction? trans = default) { - Polyline pl = new(); pl.SetDatabaseDefaults(); pts.ForEach((i, vertex) => { @@ -461,4 +460,4 @@ public static ObjectId InsertBlock(this BlockTableRecord blockTableRecord, Point #endregion #endregion -} +} \ No newline at end of file diff --git "a/src/IFoxCAD.Cad.Shared/ExtensionMethod/\346\226\260\345\273\272\345\241\253\345\205\205/HatchConverter.cs" "b/src/IFoxCAD.Cad.Shared/ExtensionMethod/\346\226\260\345\273\272\345\241\253\345\205\205/HatchConverter.cs" index 09af1d6..27d4bfe 100644 --- "a/src/IFoxCAD.Cad.Shared/ExtensionMethod/\346\226\260\345\273\272\345\241\253\345\205\205/HatchConverter.cs" +++ "b/src/IFoxCAD.Cad.Shared/ExtensionMethod/\346\226\260\345\273\272\345\241\253\345\205\205/HatchConverter.cs" @@ -314,7 +314,7 @@ public ObjectId CreateBoundarysAndHatchToMsPs(BlockTableRecord btrOfAddEntitySpa * 那么它的平移后的基点在哪里呢? */ - var newHatchId = btrOfAddEntitySpace.DeepClone(new ObjectIdCollection(new ObjectId[] { OldHatchId })).GetValues()[0]; + var newHatchId = btrOfAddEntitySpace.DeepCloneEx(new ObjectIdCollection(new ObjectId[] { OldHatchId })).GetValues()[0]; trans ??= DBTrans.Top.Transaction; var hatchEnt = trans.GetObject(newHatchId, OpenMode.ForWrite) as Hatch; if (hatchEnt != null) -- Gitee From 92708bbe86bfda49d553c75c0a0d9d7a0f5912b5 Mon Sep 17 00:00:00 2001 From: liuqihong <540762622@qq.com> Date: Thu, 15 Sep 2022 02:49:37 +0800 Subject: [PATCH 496/675] =?UTF-8?q?=E5=A2=9E=E5=8A=A0=E5=BA=8F=E5=88=97?= =?UTF-8?q?=E5=8C=96=E5=9B=BE=E5=85=83?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../ExtensionMethod/Filer/DwgFiler.cs | 538 ++++++++++++++++++ .../ExtensionMethod/Filer/DwgFilerEx.cs | 83 +++ .../ExtensionMethod/Filer/DxfFiler.cs | 219 +++++++ .../IFoxCAD.Cad.Shared.projitems | 3 + .../IFoxCAD.Aacad08/GlobalUsings.cs | 40 -- .../IFoxCAD.Aacad08/IFoxCAD.Acad08.csproj | 8 +- .../IFoxCAD.Aacad09plus/GlobalUsings.cs | 5 + tests/TestAcad08/GlobalUsings.cs | 38 -- tests/TestAcad08/TestAcad08.csproj | 6 +- tests/TestAcad09plus/GlobalUsings.cs | 9 +- tests/TestShared/TestDwgFilerEx.cs | 156 +++++ tests/TestShared/TestShared.projitems | 1 + tests/TestShared/testblock.cs | 28 +- 13 files changed, 1033 insertions(+), 101 deletions(-) create mode 100644 src/IFoxCAD.Cad.Shared/ExtensionMethod/Filer/DwgFiler.cs create mode 100644 src/IFoxCAD.Cad.Shared/ExtensionMethod/Filer/DwgFilerEx.cs create mode 100644 src/IFoxCAD.Cad.Shared/ExtensionMethod/Filer/DxfFiler.cs delete mode 100644 src/IFoxCAD.Cad/IFoxCAD.Aacad08/GlobalUsings.cs delete mode 100644 tests/TestAcad08/GlobalUsings.cs create mode 100644 tests/TestShared/TestDwgFilerEx.cs diff --git a/src/IFoxCAD.Cad.Shared/ExtensionMethod/Filer/DwgFiler.cs b/src/IFoxCAD.Cad.Shared/ExtensionMethod/Filer/DwgFiler.cs new file mode 100644 index 0000000..a412edd --- /dev/null +++ b/src/IFoxCAD.Cad.Shared/ExtensionMethod/Filer/DwgFiler.cs @@ -0,0 +1,538 @@ +namespace IFoxCAD.Cad; +/* + Arx自定义实体类,加 读函数(assertReadEnabled)和写函数(assertWriteEnabled) + + 所有属性位置都不要改动位置,因为涉及序列化 + [Serializable] 设置类 可以序列化 + [Newtonsoft.Json.JsonIgnore] 设置成员 不可序列化 +*/ + + +[Serializable] +public class DwgFiler : Acad_DwgFiler +{ +#if NET35 + public int m_Position; +#else + public long m_Position; +#endif + public FilerType m_FilerType; + public Acad_ErrorStatus m_FilerStatus; + public List AddressList; + public int AddressListPt = 0; + public List BinaryChunkList; + public int BinaryChunkListPt = 0; + public List BooleanList; + public int BooleanListPt = 0; + public List ByteList; + public int ByteListPt = 0; + public List BytesList; + public int BytesListPt = 0; + public List DoubleList; + public int DoubleListPt = 0; + public List HandleList; + public int HandleListPt = 0; + [NonSerialized] + public List HardOwnershipIdList; + public int HardOwnershipIdListPt = 0; + [NonSerialized] + public List HardPointerIdList; + public int HardPointerIdListPt = 0; + public List Int16List; + public int Int16ListPt = 0; + public List Int32List; + public int Int32ListPt = 0; +#if !NET35 + public List Int64List; + public int Int64ListPt = 0; +#endif + public List Point2dList; + public int Point2dListPt = 0; + public List Point3dList; + public int Point3dListPt = 0; + public List Scale3dList; + public int Scale3dListPt = 0; + [NonSerialized] + public List SoftOwnershipIdList; + public int SoftOwnershipIdListPt = 0; + [NonSerialized] + public List SoftPointerIdList; + public int SoftPointerIdListPt = 0; + public List StringList; + public int StringListPt = 0; + public List Uint16List; + public int uint16ListPt = 0; + public List Uint32List; + public int uint32ListPt = 0; +#if !NET35 + public List Uint64List; + public int uint64ListPt = 0; +#endif + public List Vector2dList; + public int Vector2dListPt = 0; + public List Vector3dList; + public int Vector3dListPt = 0; + + public DwgFiler() + { + m_Position = 0; + m_FilerType = FilerType.CopyFiler; + m_FilerStatus = Acad_ErrorStatus.OK; + AddressList = new List(); + BinaryChunkList = new List(); + BooleanList = new List(); + ByteList = new List(); + BytesList = new List(); + DoubleList = new List(); + HandleList = new List(); + HardOwnershipIdList = new List(); + HardPointerIdList = new List(); + Int16List = new List(); + Int32List = new List(); +#if !NET35 + Int64List = new List(); +#endif + Point2dList = new List(); + Point3dList = new List(); + Scale3dList = new List(); + SoftOwnershipIdList = new List(); + SoftPointerIdList = new List(); + StringList = new List(); + Uint16List = new List(); + Uint32List = new List(); +#if !NET35 + Uint64List = new List(); +#endif + Vector2dList = new List(); + Vector3dList = new List(); + } + +#if NET35 + public override int Position => m_Position; +#else + public override long Position => m_Position; +#endif + public override FilerType FilerType + { + get { return this.m_FilerType; } + } + + public override Acad_ErrorStatus FilerStatus + { + get { return m_FilerStatus; } + set { m_FilerStatus = value; } + } + + public override IntPtr ReadAddress() + { + if (AddressList.Count == 0) + return new IntPtr(); + return AddressList[AddressListPt++]; + } + + public override byte[]? ReadBinaryChunk() + { + if (BinaryChunkList.Count == 0) + return null; + return BinaryChunkList[BinaryChunkListPt++]; + } + + public override bool ReadBoolean() + { + if (BooleanList.Count == 0) + return false; + return BooleanList[BooleanListPt++]; + } + + public override byte ReadByte() + { + if (ByteList.Count == 0) + return 0; + return ByteList[ByteListPt++]; + } + + public override void ReadBytes(byte[] value) + { + if (ByteList.Count == 0) + return; + value = new byte[BytesList[BytesListPt].Length]; + BytesList[BytesListPt++].CopyTo(value, 0); + } + + public override double ReadDouble() + { + if (DoubleList.Count == 0) + return 0; + return DoubleList[DoubleListPt++]; + } + + public override Handle ReadHandle() + { + if (HandleList.Count == 0) + return new Handle(); + return HandleList[HandleListPt++]; + } + + public override ObjectId ReadHardOwnershipId() + { + if (HardOwnershipIdList.Count == 0) + return new ObjectId(); + return HardOwnershipIdList[HardOwnershipIdListPt++]; + } + + public override ObjectId ReadHardPointerId() + { + if (HardPointerIdList.Count == 0) + return new ObjectId(); + return HardPointerIdList[HardPointerIdListPt++]; + } + + public override short ReadInt16() + { + if (Int16List.Count == 0) + return 0; + return Int16List[Int16ListPt++]; + } + + public override int ReadInt32() + { + if (Int32List.Count == 0) + return 0; + return Int32List[Int32ListPt++]; + } + +#if !NET35 + public override long ReadInt64() + { + if (Int64List.Count == 0) + return 0; + return Int64List[Int64ListPt++]; + } +#endif + + public override Point2d ReadPoint2d() + { + if (Point2dList.Count == 0) + return new Point2d(); + return Point2dList[Point2dListPt++]; + } + + public override Point3d ReadPoint3d() + { + if (Point3dList.Count == 0) + return new Point3d(); + return Point3dList[Point3dListPt++]; + } + + public override Scale3d ReadScale3d() + { + if (Scale3dList.Count == 0) + return new Scale3d(); + return Scale3dList[Scale3dListPt++]; + } + + public override ObjectId ReadSoftOwnershipId() + { + if (SoftOwnershipIdList.Count == 0) + return new ObjectId(); + return SoftOwnershipIdList[SoftOwnershipIdListPt++]; + } + + public override ObjectId ReadSoftPointerId() + { + if (SoftPointerIdList.Count == 0) + return new ObjectId(); + return SoftPointerIdList[SoftPointerIdListPt++]; + } + + public override string? ReadString() + { + if (StringList.Count == 0) + return null; + return StringList[StringListPt++]; + } + + public override ushort ReadUInt16() + { + if (Uint16List.Count == 0) + return 0; + return Uint16List[uint16ListPt++]; + } + + public override uint ReadUInt32() + { + if (Uint32List.Count == 0) + return 0; + return Uint32List[uint32ListPt++]; + } + +#if !NET35 + public override ulong ReadUInt64() + { + if (Uint64List.Count == 0) + return 0; + return Uint64List[uint64ListPt++]; + } +#endif + + public override Vector2d ReadVector2d() + { + if (Vector2dList.Count == 0) + return new Vector2d(); + return Vector2dList[Vector2dListPt++]; + } + + public override Vector3d ReadVector3d() + { + if (Vector3dList.Count == 0) + return new Vector3d(); + return Vector3dList[Vector3dListPt++]; + } + + public override void ResetFilerStatus() + { + AddressList.Clear(); + AddressListPt = 0; + BinaryChunkList.Clear(); + BinaryChunkListPt = 0; + BooleanList.Clear(); + BooleanListPt = 0; + ByteList.Clear(); + ByteListPt = 0; + BytesList.Clear(); + BytesListPt = 0; + DoubleList.Clear(); + DoubleListPt = 0; + HandleList.Clear(); + HandleListPt = 0; + HardOwnershipIdList.Clear(); + HardOwnershipIdListPt = 0; + HardPointerIdList.Clear(); + HardPointerIdListPt = 0; + Int16List.Clear(); + Int16ListPt = 0; + Int32List.Clear(); + Int32ListPt = 0; +#if !NET35 + Int64List.Clear(); + Int64ListPt = 0; +#endif + Point2dList.Clear(); + Point2dListPt = 0; + Point3dList.Clear(); + Point3dListPt = 0; + Scale3dList.Clear(); + Scale3dListPt = 0; + SoftOwnershipIdList.Clear(); + SoftOwnershipIdListPt = 0; + SoftPointerIdList.Clear(); + SoftPointerIdListPt = 0; + StringList.Clear(); + StringListPt = 0; + Uint16List.Clear(); + uint16ListPt = 0; + Uint32List.Clear(); + uint32ListPt = 0; +#if !NET35 + Uint64List.Clear(); + uint64ListPt = 0; +#endif + Vector2dList.Clear(); + Vector2dListPt = 0; + Vector3dList.Clear(); + Vector3dListPt = 0; + + m_FilerType = FilerType.CopyFiler; + } + + public override void Seek( +#if ac2008 || ac2009 + int +#else + long +#endif + offset, int method) + { + Editor ed = Acap.DocumentManager.MdiActiveDocument.Editor; + ed.WriteMessage(MethodInfo.GetCurrentMethod().Name + " = " + " \n "); + } + + public override void WriteAddress(IntPtr value) + { + AddressList.Add(value); + } + + public override void WriteBinaryChunk(byte[] chunk) + { + BinaryChunkList.Add(chunk); + } + + public override void WriteBoolean(bool value) + { + BooleanList.Add(value); + } + + public override void WriteByte(byte value) + { + ByteList.Add(value); + } + + public override void WriteBytes(byte[] value) + { + BytesList.Add(value); + } + + public override void WriteDouble(double value) + { + DoubleList.Add(value); + } + + public override void WriteHandle(Handle handle) + { + HandleList.Add(handle); + } + + public override void WriteHardOwnershipId(ObjectId value) + { + HardOwnershipIdList.Add(value); + } + + public override void WriteHardPointerId(ObjectId value) + { + HardPointerIdList.Add(value); + } + + public override void WriteInt16(short value) + { + Int16List.Add(value); + } + + public override void WriteInt32(int value) + { + Int32List.Add(value); + } + +#if !NET35 + public override void WriteInt64(long value) + { + Int64List.Add(value); + } +#endif + public override void WritePoint2d(Point2d value) + { + Point2dList.Add(value); + } + + public override void WritePoint3d(Point3d value) + { + Point3dList.Add(value); + } + + public override void WriteScale3d(Scale3d value) + { + Scale3dList.Add(value); + } + + public override void WriteSoftOwnershipId(ObjectId value) + { + SoftOwnershipIdList.Add(value); + } + + public override void WriteSoftPointerId(ObjectId value) + { + SoftPointerIdList.Add(value); + } + + public override void WriteString(string value) + { + StringList.Add(value); + } + + public override void WriteUInt16(ushort value) + { + Uint16List.Add(value); + } + + public override void WriteUInt32(uint value) + { + Uint32List.Add(value); + } + +#if !NET35 + public override void WriteUInt64(ulong value) + { + Uint64List.Add(value); + } +#endif + + public override void WriteVector2d(Vector2d value) + { + Vector2dList.Add(value); + } + + public override void WriteVector3d(Vector3d value) + { + Vector3dList.Add(value); + } + + public override string ToString() + { + int ptCount = AddressListPt + + BinaryChunkListPt + + BooleanListPt + + ByteListPt + + BytesListPt + + DoubleListPt + + HandleListPt + + HardOwnershipIdListPt + + HardPointerIdListPt + + Int16ListPt + + Int32ListPt + +#if !NET35 + Int64ListPt + +#endif + Point2dListPt + + Point3dListPt + + Scale3dListPt + + SoftOwnershipIdListPt + + SoftPointerIdListPt + + StringListPt + + uint16ListPt + + uint32ListPt + +#if !NET35 + uint64ListPt + +#endif + Vector2dListPt + + Vector3dListPt; + + int ltCount = AddressList.Count + + BinaryChunkList.Count + + BooleanList.Count + + ByteList.Count + + BytesList.Count + + DoubleList.Count + + HandleList.Count + + HardOwnershipIdList.Count + + HardPointerIdList.Count + + Int16List.Count + + Int32List.Count + +#if !NET35 + Int64List.Count + +#endif + Point2dList.Count + + Point3dList.Count + + Scale3dList.Count + + SoftOwnershipIdList.Count + + SoftPointerIdList.Count + + StringList.Count + + Uint16List.Count + + Uint32List.Count + +#if !NET35 + Uint64List.Count + +#endif + Vector2dList.Count + + Vector3dList.Count; + + return "\nDataIn::" + ptCount + "\nDataOut::" + ltCount; + } +} \ No newline at end of file diff --git a/src/IFoxCAD.Cad.Shared/ExtensionMethod/Filer/DwgFilerEx.cs b/src/IFoxCAD.Cad.Shared/ExtensionMethod/Filer/DwgFilerEx.cs new file mode 100644 index 0000000..b7a4d0e --- /dev/null +++ b/src/IFoxCAD.Cad.Shared/ExtensionMethod/Filer/DwgFilerEx.cs @@ -0,0 +1,83 @@ +namespace IFoxCAD.Cad; + +/// +/// Dwg序列化 +/// +public class DwgFilerEx +{ + #region 成员 + DBObject? _entity; + public DwgFiler DwgFiler { get; private set; } + #endregion + + #region 构造 + /// + /// Dwg序列化 + /// + public DwgFilerEx(DwgFiler? acad_DwgFiler = null) + { + if (acad_DwgFiler == null) + acad_DwgFiler = new(); + DwgFiler = acad_DwgFiler; + } + + /// + /// Dwg序列化 + /// + public DwgFilerEx(DBObject entity) : this() + { + DwgOut(entity); + } + + #endregion + + #region 方法 + public void DwgOut(DBObject entity) + { + _entity = entity; + _entity.DwgOut(DwgFiler); + } + + public void DwgIn() + { + _entity?.DwgIn(DwgFiler); + } + + /// + /// 反序列化 + /// + /// + /// + public DwgFilerEx? DeserializeObject(string json) + { + throw new ArgumentException(); + //return JsonConvert.DeserializeObject(json);// 反序列化*字符串转类 + } + + /// + /// 序列化 + /// + /// + public string SerializeObject() + { + throw new ArgumentException(); + //return JsonConvert.SerializeObject(DwgFiler, Formatting.Indented); // 序列化*类转字符串 + } + + public override string ToString() + { + // 替换中括号以外的字符串,替换逗号为换行符 https://bbs.csdn.net/topics/370134253 + //var str = SerializeObject(); + //str = str.Substring(1, str.Length - 2); + //str = Regex.Replace(str, @"(?:,)(?![^\[]*?\])", "\r\n"); + //return str; + + return DwgFiler.ToString(); + } + + public static implicit operator Acad_DwgFiler(DwgFilerEx df) + { + return df.DwgFiler; + } + #endregion +} \ No newline at end of file diff --git a/src/IFoxCAD.Cad.Shared/ExtensionMethod/Filer/DxfFiler.cs b/src/IFoxCAD.Cad.Shared/ExtensionMethod/Filer/DxfFiler.cs new file mode 100644 index 0000000..ef9c42d --- /dev/null +++ b/src/IFoxCAD.Cad.Shared/ExtensionMethod/Filer/DxfFiler.cs @@ -0,0 +1,219 @@ +namespace IFoxCAD.Cad; + +/* 此处暂未完成,无任何测试,尚且不知道怎么用 */ +using System.Runtime.Remoting; + +public class DxfFiler : Acad_DxfFiler +{ + public DxfFiler(IntPtr unmanagedPointer, [MarshalAs(UnmanagedType.U1)] bool autoDelete) : base(unmanagedPointer, autoDelete) + { + } + + public override bool IsModifyingExistingObject => base.IsModifyingExistingObject; + + public override double Thickness => base.Thickness; + + public override double Elevation => base.Elevation; + + public override bool AtEmbeddedObjectStart => base.AtEmbeddedObjectStart; + + public override bool AtEndOfObject => base.AtEndOfObject; + + public override bool AtExtendedData => base.AtExtendedData; + + public override bool AtEndOfFile => base.AtEndOfFile; + + public override int Precision { get => base.Precision; set => base.Precision = value; } + + public override string ErrorMessage => base.ErrorMessage; + + public override bool AtSubclassData(string value) + { + return base.AtSubclassData(value); + } + + public override object Clone() + { + return base.Clone(); + } + + public override void CopyFrom(RXObject source) + { + base.CopyFrom(source); + } + + public override ObjRef CreateObjRef(Type requestedType) + { + return base.CreateObjRef(requestedType); + } + + public override bool Equals(object obj) + { + return base.Equals(obj); + } + + public override void FilerStatus() + { + base.FilerStatus(); + } + + public override int GetHashCode() + { + return base.GetHashCode(); + } + + public override void HaltAtClassBoundaries(bool value) + { + base.HaltAtClassBoundaries(value); + } + + public override object InitializeLifetimeService() + { + return base.InitializeLifetimeService(); + } + + public override void PushBackItem() + { + base.PushBackItem(); + } + + public override ResultBuffer ReadResultBuffer() + { + return base.ReadResultBuffer(); + } + + public override void ResetFilerStatus() + { + base.ResetFilerStatus(); + } + + public override int RewindFiler() + { + return base.RewindFiler(); + } + + public override void SetError(string format, params string[] values) + { + base.SetError(format, values); + } + + public override void SetError(Acad_ErrorStatus value, string format, params string[] values) + { + base.SetError(value, format, values); + } + + public override string ToString() + { + return base.ToString(); + } + + public override void WriteBool(DxfCode opCode, bool value) + { + base.WriteBool(opCode, value); + } + + public override void WriteBoolean(DxfCode opCode, bool value) + { + base.WriteBoolean(opCode, value); + } + + public override void WriteByte(DxfCode opCode, byte value) + { + base.WriteByte(opCode, value); + } + + public override void WriteBytes(DxfCode opCode, byte[] chunk) + { + base.WriteBytes(opCode, chunk); + } + + public override void WriteDouble(DxfCode opCode, double value, int precision) + { + base.WriteDouble(opCode, value, precision); + } + + public override void WriteEmbeddedObjectStart() + { + base.WriteEmbeddedObjectStart(); + } + + public override void WriteHandle(DxfCode opCode, Handle value) + { + base.WriteHandle(opCode, value); + } + + public override void WriteInt16(DxfCode opCode, short value) + { + base.WriteInt16(opCode, value); + } + + public override void WriteInt32(DxfCode opCode, int value) + { + base.WriteInt32(opCode, value); + } + + public override void WriteObjectId(DxfCode opCode, ObjectId value) + { + base.WriteObjectId(opCode, value); + } + + public override void WritePoint2d(DxfCode opCode, Point2d value, int precision) + { + base.WritePoint2d(opCode, value, precision); + } + + public override void WritePoint3d(DxfCode opCode, Point3d value, int precision) + { + base.WritePoint3d(opCode, value, precision); + } + + public override void WriteResultBuffer(ResultBuffer buffer) + { + base.WriteResultBuffer(buffer); + } + + public override void WriteScale3d(DxfCode opCode, Scale3d value, int precision) + { + base.WriteScale3d(opCode, value, precision); + } + + public override void WriteString(DxfCode opCode, string value) + { + base.WriteString(opCode, value); + } + + public override void WriteUInt16(DxfCode opCode, ushort value) + { + base.WriteUInt16(opCode, value); + } + + public override void WriteUInt32(DxfCode opCode, uint value) + { + base.WriteUInt32(opCode, value); + } + + public override void WriteVector2d(DxfCode opCode, Vector2d value, int precision) + { + base.WriteVector2d(opCode, value, precision); + } + + public override void WriteVector3d(DxfCode opCode, Vector3d value, int precision) + { + base.WriteVector3d(opCode, value, precision); + } + + public override void WriteXDataStart() + { + base.WriteXDataStart(); + } + + protected override void DeleteUnmanagedObject() + { + base.DeleteUnmanagedObject(); + } + + protected override void Dispose(bool disposing) + { + base.Dispose(disposing); + } +} \ No newline at end of file diff --git a/src/IFoxCAD.Cad.Shared/IFoxCAD.Cad.Shared.projitems b/src/IFoxCAD.Cad.Shared/IFoxCAD.Cad.Shared.projitems index 0ab6fe2..568ba2d 100644 --- a/src/IFoxCAD.Cad.Shared/IFoxCAD.Cad.Shared.projitems +++ b/src/IFoxCAD.Cad.Shared/IFoxCAD.Cad.Shared.projitems @@ -40,6 +40,9 @@ + + + diff --git a/src/IFoxCAD.Cad/IFoxCAD.Aacad08/GlobalUsings.cs b/src/IFoxCAD.Cad/IFoxCAD.Aacad08/GlobalUsings.cs deleted file mode 100644 index 9ea1ec2..0000000 --- a/src/IFoxCAD.Cad/IFoxCAD.Aacad08/GlobalUsings.cs +++ /dev/null @@ -1,40 +0,0 @@ -/// 系统引用 -global using System; -global using System.Collections; -global using System.Collections.Generic; -global using System.IO; -global using System.Linq; -global using System.Text; -global using System.Reflection; -global using System.Text.RegularExpressions; -global using Microsoft.Win32; -global using System.ComponentModel; -global using System.Runtime.InteropServices; - -global using Exception = System.Exception; - -/// autocad 引用 -global using Autodesk.AutoCAD.ApplicationServices; -global using Autodesk.AutoCAD.EditorInput; -global using Autodesk.AutoCAD.Colors; -global using Autodesk.AutoCAD.DatabaseServices; -global using Autodesk.AutoCAD.Geometry; -global using Autodesk.AutoCAD.Runtime; -global using Acap = Autodesk.AutoCAD.ApplicationServices.Application; - -global using Autodesk.AutoCAD.DatabaseServices.Filters; -global using Autodesk.AutoCAD; - -// jig命名空间会引起Viewport/Polyline等等重义,最好逐个引入 using Autodesk.AutoCAD.GraphicsInterface -global using WorldDraw = Autodesk.AutoCAD.GraphicsInterface.WorldDraw; -global using Manager = Autodesk.AutoCAD.GraphicsSystem.Manager; - -global using Group = Autodesk.AutoCAD.DatabaseServices.Group; -global using Viewport = Autodesk.AutoCAD.DatabaseServices.Viewport; - -global using System.Collections.Specialized; -global using Registry = Microsoft.Win32.Registry; -global using RegistryKey = Microsoft.Win32.RegistryKey; - -/// ifoxcad.basal 引用 -global using IFoxCAD.Basal; \ No newline at end of file diff --git a/src/IFoxCAD.Cad/IFoxCAD.Aacad08/IFoxCAD.Acad08.csproj b/src/IFoxCAD.Cad/IFoxCAD.Aacad08/IFoxCAD.Acad08.csproj index 6ca24d1..a5506b6 100644 --- a/src/IFoxCAD.Cad/IFoxCAD.Aacad08/IFoxCAD.Acad08.csproj +++ b/src/IFoxCAD.Cad/IFoxCAD.Aacad08/IFoxCAD.Acad08.csproj @@ -1,5 +1,5 @@  - + NET35 true @@ -33,7 +33,7 @@ DEBUG - + $(Configuration);ac2008;ac2009 @@ -54,4 +54,8 @@ + + + + diff --git a/src/IFoxCAD.Cad/IFoxCAD.Aacad09plus/GlobalUsings.cs b/src/IFoxCAD.Cad/IFoxCAD.Aacad09plus/GlobalUsings.cs index 60ba3eb..385a8f6 100644 --- a/src/IFoxCAD.Cad/IFoxCAD.Aacad09plus/GlobalUsings.cs +++ b/src/IFoxCAD.Cad/IFoxCAD.Aacad09plus/GlobalUsings.cs @@ -39,5 +39,10 @@ global using Autodesk.AutoCAD.GraphicsInterface; global using Polyline = Autodesk.AutoCAD.DatabaseServices.Polyline; +global using Acad_DwgFiler = Autodesk.AutoCAD.DatabaseServices.DwgFiler; +global using Acad_DxfFiler = Autodesk.AutoCAD.DatabaseServices.DxfFiler; +global using Acad_ErrorStatus = Autodesk.AutoCAD.Runtime.ErrorStatus; + + /// ifoxcad.basal 引用 global using IFoxCAD.Basal; \ No newline at end of file diff --git a/tests/TestAcad08/GlobalUsings.cs b/tests/TestAcad08/GlobalUsings.cs deleted file mode 100644 index 1431096..0000000 --- a/tests/TestAcad08/GlobalUsings.cs +++ /dev/null @@ -1,38 +0,0 @@ -/// 系统引用 -global using System; -global using System.Collections; -global using System.Collections.Generic; -global using System.IO; -global using System.Linq; -global using System.Text; -global using System.Reflection; -global using System.Text.RegularExpressions; -global using Microsoft.Win32; -global using System.ComponentModel; -global using System.Runtime.CompilerServices; - - -// -// -// #if !NET472 -// global using System.Web.Script.Serialization;/*序列化的类 程序集:System.Web.Extensions.dll*/ -// #else -// global using System.Text.Json; -// #endif - -/// autocad 引用 -global using Autodesk.AutoCAD.ApplicationServices; -global using Autodesk.AutoCAD.EditorInput; -global using Autodesk.AutoCAD.Colors; -global using Autodesk.AutoCAD.DatabaseServices; -global using Autodesk.AutoCAD.Geometry; -global using Autodesk.AutoCAD.Runtime; -global using Acgi = Autodesk.AutoCAD.GraphicsInterface; -global using Acap = Autodesk.AutoCAD.ApplicationServices.Application; - -global using Registry = Microsoft.Win32.Registry; -global using RegistryKey = Microsoft.Win32.RegistryKey; - -/// ifoxcad -global using IFoxCAD.Cad; -global using IFoxCAD.Basal; \ No newline at end of file diff --git a/tests/TestAcad08/TestAcad08.csproj b/tests/TestAcad08/TestAcad08.csproj index 7840206..a8ea524 100644 --- a/tests/TestAcad08/TestAcad08.csproj +++ b/tests/TestAcad08/TestAcad08.csproj @@ -8,7 +8,7 @@ true x86 - + $(Configuration);ac2008;ac2009 @@ -24,4 +24,8 @@ + + + + diff --git a/tests/TestAcad09plus/GlobalUsings.cs b/tests/TestAcad09plus/GlobalUsings.cs index 2896cbf..d3d5799 100644 --- a/tests/TestAcad09plus/GlobalUsings.cs +++ b/tests/TestAcad09plus/GlobalUsings.cs @@ -18,13 +18,18 @@ global using Autodesk.AutoCAD.DatabaseServices; global using Autodesk.AutoCAD.Geometry; global using Autodesk.AutoCAD.Runtime; -global using Acgi = Autodesk.AutoCAD.GraphicsInterface; +global using Acgi = Autodesk.AutoCAD.GraphicsInterface; global using Acap = Autodesk.AutoCAD.ApplicationServices.Application; global using Registry = Microsoft.Win32.Registry; global using RegistryKey = Microsoft.Win32.RegistryKey; +global using Acad_DwgFiler = Autodesk.AutoCAD.DatabaseServices.DwgFiler; +global using Acad_DxfFiler = Autodesk.AutoCAD.DatabaseServices.DxfFiler; + /// ifoxcad global using IFoxCAD.Cad; +global using IFoxCAD.Basal; +#if !ac2008 global using IFoxCAD.WPF; -global using IFoxCAD.Basal; \ No newline at end of file +#endif \ No newline at end of file diff --git a/tests/TestShared/TestDwgFilerEx.cs b/tests/TestShared/TestDwgFilerEx.cs new file mode 100644 index 0000000..c2e4118 --- /dev/null +++ b/tests/TestShared/TestDwgFilerEx.cs @@ -0,0 +1,156 @@ +namespace Test; + +using DxfFiler = IFoxCAD.Cad.DxfFiler; + +public class CmdTestDwgFilerEx +{ + [CommandMethod(nameof(CmdTest_DwgFilerEx))] + public static void CmdTest_DwgFilerEx() + { + var dm = Acap.DocumentManager; + var doc = dm.MdiActiveDocument; + var db = doc.Database; + var ed = doc.Editor; + ed.WriteMessage("\n****测试,序列化图元"); + + var ssPsr = ed.SelectImplied();// 预选 + if (ssPsr.Status != PromptStatus.OK) + { + ssPsr = ed.GetSelection();// 手选 这里输入al会变成all,无法删除ssget的all关键字 + if (ssPsr.Status != PromptStatus.OK) + return; + } + + using var tr = new DBTrans(); + var ids = ssPsr.Value.GetObjectIds(); + foreach (var id in ids) + { + if (!id.IsOk()) + continue; + var ent = tr.GetObject(id, OpenMode.ForRead); + if (ent is null) + continue; + var dwgFilerEx = new DwgFilerEx(ent); + ed.WriteMessage(Environment.NewLine + dwgFilerEx.ToString()); + } + } + + [CommandMethod(nameof(CmdTest_EntDxfout))] + public static void CmdTest_EntDxfout() + { + var dm = Acap.DocumentManager; + var doc = dm.MdiActiveDocument; + var db = doc.Database; + var ed = doc.Editor; + + // 定义选择集选项 + var pso = new PromptSelectionOptions + { + RejectObjectsOnLockedLayers = true, // 不选择锁定图层对象 + AllowDuplicates = true, // 不允许重复选择 + }; + var ssPsr = ed.GetSelection(pso);// 手选 这里输入al会变成all,无法删除ssget的all关键字 + if (ssPsr.Status != PromptStatus.OK) + return; + + using var tr = new DBTrans(); + var ids = ssPsr.Value.GetObjectIds(); + foreach (var id in ids) + { + if (!id.IsOk()) + continue; + var ent = tr.GetObject(id, OpenMode.ForRead); + if (ent is null) + continue; + // ResultBuffer rbDxf = new(); + var filer = new DxfFiler(ent.UnmanagedObject, true);/// 这里有问题 + ent.DxfOut(filer); + } + } + + + [CommandMethod(nameof(CmdTest_TextOut))] + public static void CmdTest_TextOut() + { + var dm = Acap.DocumentManager; + var doc = dm.MdiActiveDocument; + var db = doc.Database; + var ed = doc.Editor; + +#if true + var peo1 = new PromptEntityOptions(Environment.NewLine + "点选源TCH_WIREDIM2:") + { + AllowObjectOnLockedLayer = false, + AllowNone = false + }; + var gt1 = ed.GetEntity(peo1); + if (gt1.Status != PromptStatus.OK) + return; +#else + var peo2 = new PromptEntityOptions(Environment.NewLine + "点选目标TCH_WIREDIM2:") + { + AllowObjectOnLockedLayer = false, + AllowNone = false + }; + var gt2 = ed.GetEntity(peo2); + if (gt2.Status != PromptStatus.OK) + return; +#endif + + using var tr = new DBTrans(); + var dwgFilerEx = new DwgFilerEx(); + var bText = tr.GetObject(gt1.ObjectId, OpenMode.ForRead); + if (bText is null) + return; + + // DwgFilerEx.StringList[0] = "1@2@3@4@5@6@7@"; + // 复制 TCH_WIREDIM2 不行,TEXT 也不行,直接崩溃。line等线就没事 + bText.DwgOut(dwgFilerEx.DwgFiler); + + int testNum = 1 | 2 | 4 | 8; + + if ((testNum & 1) == 1) + { + // 错误,原地克隆也是不行的,它会生成在了模型中. + var sIds = new List + { + bText.ObjectId + }; + // 克隆到目标块表内 + var newObjs = tr.CurrentSpace + .DeepCloneEx(new ObjectIdCollection(sIds.ToArray())); + var newTexts = newObjs.GetValues().GetObject(); + newTexts.ForEach(nText => { + if (nText == null) + return; + // 通过上面的克隆就已经在块表上面了.所以下面的设置也跟设置到已有图元上一样报错. + nText.UpgradeOpen(); + nText.DwgIn(dwgFilerEx); + tr.CurrentSpace.AddEntity(nText); + nText.DowngradeOpen(); + }); + } + if ((testNum & 2) == 2) + { + // 出错 + // 直接设置 + bText.DwgIn(dwgFilerEx); + } + if ((testNum & 4) == 4) + { + // 出错 + // 此时是内存中对象.... + var nText = (DBText)bText.Clone(); + nText.DwgIn(dwgFilerEx); + tr.CurrentSpace.AddEntity(nText); + } + if ((testNum & 8) == 8) + { + // 新对象相当于克隆,是ok的 + DBText nText = new(); + nText.SetDatabaseDefaults(); + nText.DwgIn(dwgFilerEx); + tr.CurrentSpace.AddEntity(nText); + } + } +} \ No newline at end of file diff --git a/tests/TestShared/TestShared.projitems b/tests/TestShared/TestShared.projitems index 78d3e20..0dc6599 100644 --- a/tests/TestShared/TestShared.projitems +++ b/tests/TestShared/TestShared.projitems @@ -10,6 +10,7 @@ + diff --git a/tests/TestShared/testblock.cs b/tests/TestShared/testblock.cs index 1199e9e..8581eba 100644 --- a/tests/TestShared/testblock.cs +++ b/tests/TestShared/testblock.cs @@ -11,7 +11,7 @@ public void Test_GetBoundingBoxEx() return; foreach (var item in ents) { - if(item is null) + if (item is null) continue; var box = item.GetBoundingBoxEx(); Env.Print("min:" + box.Min + ";max" + box.Max); @@ -44,7 +44,6 @@ public void Test_BlockDef() tr.BlockTable.Add("test1", btr => { btr.Origin = new Point3d(0, 0, 0); - }, () => { var line = new Line(new Point3d(0, 0, 0), new Point3d(1, 1, 0)); @@ -202,7 +201,6 @@ public void test_block_ej() { using (var tr = new DBTrans()) { - // Point3d.Origin.AddBellowToModelSpace(100, 100, 5, 3, 30);// 画波纹管 // Database db2 = new Database(false, true); @@ -249,7 +247,6 @@ public void test_block_ej() var btr = tr2.BlockTable[Bref.Name]; tr2.BlockTable.Change(btr, ltr => { - foreach (ObjectId OID in ltr) { var ent = tr2.GetObject(OID); @@ -260,14 +257,12 @@ public void test_block_ej() switch (mText.Text) { case "$$A": - mText.Contents = "hahaha"; - break; + mText.Contents = "hahaha"; + break; case "$$B": - ; - break; + break; default: - ; - break; + break; } } else if (ent is DBText dBText) { dBText.TextString = "haha"; } @@ -276,11 +271,11 @@ public void test_block_ej() switch (dimension.DimensionText) { case "$$pipeLen": - dimension.DimensionText = "350"; - dimension.RecomputeDimensionBlock(true); - break; + dimension.DimensionText = "350"; + dimension.RecomputeDimensionBlock(true); + break; default: - break; + break; } } } @@ -427,7 +422,6 @@ public void Test_QuickBlockDef() ed.Regen(); tr.Commit(); // ed.Regen(); - } public void TestWblock() @@ -477,9 +471,7 @@ public void TestBack() ent.ForWrite(e => e.ColorIndex = 4); }); tr.Database.SaveAs(dwg, DwgVersion.Current); - } - } public class BlockImportClass @@ -620,6 +612,6 @@ public void Test_CombineBlocksIntoLibrary() failed > 0 ? " (" + failed + " failed)" : "", pathName ); - } + } #endif } \ No newline at end of file -- Gitee From e3539575a89e1bc5fd3d6ff3f6d61367369307fc Mon Sep 17 00:00:00 2001 From: liuqihong <540762622@qq.com> Date: Thu, 15 Sep 2022 03:02:22 +0800 Subject: [PATCH 497/675] =?UTF-8?q?=E4=BC=98=E5=8C=96=E6=A0=BC=E5=BC=8F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/IFoxCAD.Basal/CLS/Index.cs | 3 +- src/IFoxCAD.Basal/CLS/Range.cs | 3 +- .../CLS/TupleElementNamesAttribute.cs | 2 +- src/IFoxCAD.Basal/CLS/ValueTuple.cs | 130 +++++------ src/IFoxCAD.Basal/Sortedset/ISet.cs | 18 +- src/IFoxCAD.Basal/Sortedset/Sortedset.cs | 216 ++++++++---------- src/IFoxCAD.Basal/Sortedset/ThrowHelper.cs | 199 ++++++++-------- src/IFoxCAD.Basal/Sortedset/bithelper.cs | 33 ++- 8 files changed, 276 insertions(+), 328 deletions(-) diff --git a/src/IFoxCAD.Basal/CLS/Index.cs b/src/IFoxCAD.Basal/CLS/Index.cs index 97b51e5..a588dc6 100644 --- a/src/IFoxCAD.Basal/CLS/Index.cs +++ b/src/IFoxCAD.Basal/CLS/Index.cs @@ -144,5 +144,4 @@ public override string ToString() return ((uint)Value).ToString(); } -} - +} \ No newline at end of file diff --git a/src/IFoxCAD.Basal/CLS/Range.cs b/src/IFoxCAD.Basal/CLS/Range.cs index 35fb4cc..43dce54 100644 --- a/src/IFoxCAD.Basal/CLS/Range.cs +++ b/src/IFoxCAD.Basal/CLS/Range.cs @@ -99,5 +99,4 @@ public override string ToString() return (start, end - start); } -} - +} \ No newline at end of file diff --git a/src/IFoxCAD.Basal/CLS/TupleElementNamesAttribute.cs b/src/IFoxCAD.Basal/CLS/TupleElementNamesAttribute.cs index f3f7515..9c3afb3 100644 --- a/src/IFoxCAD.Basal/CLS/TupleElementNamesAttribute.cs +++ b/src/IFoxCAD.Basal/CLS/TupleElementNamesAttribute.cs @@ -47,4 +47,4 @@ public TupleElementNamesAttribute(string[] transformNames) /// meant to carry element names. ///
    public IList TransformNames => _transformNames; -} +} \ No newline at end of file diff --git a/src/IFoxCAD.Basal/CLS/ValueTuple.cs b/src/IFoxCAD.Basal/CLS/ValueTuple.cs index e7edddf..568ddb9 100644 --- a/src/IFoxCAD.Basal/CLS/ValueTuple.cs +++ b/src/IFoxCAD.Basal/CLS/ValueTuple.cs @@ -10,9 +10,9 @@ * 惊惊: * 首先是因为有人想要编译的时候只形成一个dll,然后把元组塞入IFox,同时也补充了NET35没有元组的遗憾. * 而利用nuget元组包必然会形成依赖地狱. - * + * * 如果你的工程使用了nuget元组包,就造成了必须要剔除IFox. - * + * * 改IFox的元组命名空间倒是可以分离两者,但是 vs编译器 无法识别带其他命名空间的元组. * 所以元组本身就是冲突的,需要把其他元组卸载掉,由IFox提供. */ @@ -61,11 +61,11 @@ public static int Combine(int h1, int h2) namespace System { - // internal static class SR + // internal static class SR internal sealed partial class SR { // public const string ArgumentException_ValueTupleIncorrectType = "The parameter should be a ValueTuple type of appropriate arity."; - // public const string ArgumentException_ValueTupleLastArgumentNotAValueTuple = "The TRest type argument of ValueTuple`8 must be a ValueTuple."; + // public const string ArgumentException_ValueTupleLastArgumentNotAValueTuple = "The TRest type argument of ValueTuple`8 must be a ValueTuple."; public const string ArgumentException_ValueTupleIncorrectType = "该参数应该是适当数量的 ValueTuple 类型."; public const string ArgumentException_ValueTupleLastArgumentNotAValueTuple = "ValueTuple`8 的 TREST 类型参数必须是 ValueTuple."; } @@ -139,7 +139,7 @@ int IComparable.CompareTo(object other) /// /// A signed number indicating the relative values of this instance and . /// Returns less than zero if this instance is less than , zero if this - /// instance is equal to , and greater than zero if this instance is greater + /// instance is equal to , and greater than zero if this instance is greater /// than . /// public int CompareTo(ValueTuple other) @@ -425,7 +425,7 @@ int IComparable.CompareTo(object other) /// /// A signed number indicating the relative values of this instance and . /// Returns less than zero if this instance is less than , zero if this - /// instance is equal to , and greater than zero if this instance is greater + /// instance is equal to , and greater than zero if this instance is greater /// than . /// public int CompareTo(ValueTuple other) @@ -596,7 +596,7 @@ int IComparable.CompareTo(object other) /// /// A signed number indicating the relative values of this instance and . /// Returns less than zero if this instance is less than , zero if this - /// instance is equal to , and greater than zero if this instance is greater + /// instance is equal to , and greater than zero if this instance is greater /// than . /// public int CompareTo(ValueTuple other) @@ -771,7 +771,7 @@ int IComparable.CompareTo(object other) /// /// A signed number indicating the relative values of this instance and . /// Returns less than zero if this instance is less than , zero if this - /// instance is equal to , and greater than zero if this instance is greater + /// instance is equal to , and greater than zero if this instance is greater /// than . /// public int CompareTo(ValueTuple other) @@ -961,7 +961,7 @@ int IComparable.CompareTo(object other) /// /// A signed number indicating the relative values of this instance and . /// Returns less than zero if this instance is less than , zero if this - /// instance is equal to , and greater than zero if this instance is greater + /// instance is equal to , and greater than zero if this instance is greater /// than . /// public int CompareTo(ValueTuple other) @@ -1168,7 +1168,7 @@ int IComparable.CompareTo(object other) /// /// A signed number indicating the relative values of this instance and . /// Returns less than zero if this instance is less than , zero if this - /// instance is equal to , and greater than zero if this instance is greater + /// instance is equal to , and greater than zero if this instance is greater /// than . /// public int CompareTo(ValueTuple other) @@ -1392,7 +1392,7 @@ int IComparable.CompareTo(object other) /// /// A signed number indicating the relative values of this instance and . /// Returns less than zero if this instance is less than , zero if this - /// instance is equal to , and greater than zero if this instance is greater + /// instance is equal to , and greater than zero if this instance is greater /// than . /// public int CompareTo(ValueTuple other) @@ -1633,7 +1633,7 @@ int IComparable.CompareTo(object other) /// /// A signed number indicating the relative values of this instance and . /// Returns less than zero if this instance is less than , zero if this - /// instance is equal to , and greater than zero if this instance is greater + /// instance is equal to , and greater than zero if this instance is greater /// than . /// public int CompareTo(ValueTuple other) @@ -1897,7 +1897,7 @@ int IComparable.CompareTo(object other) /// /// A signed number indicating the relative values of this instance and . /// Returns less than zero if this instance is less than , zero if this - /// instance is equal to , and greater than zero if this instance is greater + /// instance is equal to , and greater than zero if this instance is greater /// than . /// public int CompareTo(ValueTuple other) @@ -1987,48 +1987,48 @@ public override int GetHashCode() switch (k) { case 1: - return ValueTuple.CombineHashCodes(EqualityComparer.Default.GetHashCode(Item7), - rest.GetHashCode()); + return ValueTuple.CombineHashCodes(EqualityComparer.Default.GetHashCode(Item7), + rest.GetHashCode()); case 2: - return ValueTuple.CombineHashCodes(EqualityComparer.Default.GetHashCode(Item6), - EqualityComparer.Default.GetHashCode(Item7), - rest.GetHashCode()); + return ValueTuple.CombineHashCodes(EqualityComparer.Default.GetHashCode(Item6), + EqualityComparer.Default.GetHashCode(Item7), + rest.GetHashCode()); case 3: - return ValueTuple.CombineHashCodes(EqualityComparer.Default.GetHashCode(Item5), - EqualityComparer.Default.GetHashCode(Item6), - EqualityComparer.Default.GetHashCode(Item7), - rest.GetHashCode()); + return ValueTuple.CombineHashCodes(EqualityComparer.Default.GetHashCode(Item5), + EqualityComparer.Default.GetHashCode(Item6), + EqualityComparer.Default.GetHashCode(Item7), + rest.GetHashCode()); case 4: - return ValueTuple.CombineHashCodes(EqualityComparer.Default.GetHashCode(Item4), - EqualityComparer.Default.GetHashCode(Item5), - EqualityComparer.Default.GetHashCode(Item6), - EqualityComparer.Default.GetHashCode(Item7), - rest.GetHashCode()); + return ValueTuple.CombineHashCodes(EqualityComparer.Default.GetHashCode(Item4), + EqualityComparer.Default.GetHashCode(Item5), + EqualityComparer.Default.GetHashCode(Item6), + EqualityComparer.Default.GetHashCode(Item7), + rest.GetHashCode()); case 5: - return ValueTuple.CombineHashCodes(EqualityComparer.Default.GetHashCode(Item3), - EqualityComparer.Default.GetHashCode(Item4), - EqualityComparer.Default.GetHashCode(Item5), - EqualityComparer.Default.GetHashCode(Item6), - EqualityComparer.Default.GetHashCode(Item7), - rest.GetHashCode()); + return ValueTuple.CombineHashCodes(EqualityComparer.Default.GetHashCode(Item3), + EqualityComparer.Default.GetHashCode(Item4), + EqualityComparer.Default.GetHashCode(Item5), + EqualityComparer.Default.GetHashCode(Item6), + EqualityComparer.Default.GetHashCode(Item7), + rest.GetHashCode()); case 6: - return ValueTuple.CombineHashCodes(EqualityComparer.Default.GetHashCode(Item2), - EqualityComparer.Default.GetHashCode(Item3), - EqualityComparer.Default.GetHashCode(Item4), - EqualityComparer.Default.GetHashCode(Item5), - EqualityComparer.Default.GetHashCode(Item6), - EqualityComparer.Default.GetHashCode(Item7), - rest.GetHashCode()); + return ValueTuple.CombineHashCodes(EqualityComparer.Default.GetHashCode(Item2), + EqualityComparer.Default.GetHashCode(Item3), + EqualityComparer.Default.GetHashCode(Item4), + EqualityComparer.Default.GetHashCode(Item5), + EqualityComparer.Default.GetHashCode(Item6), + EqualityComparer.Default.GetHashCode(Item7), + rest.GetHashCode()); case 7: case 8: - return ValueTuple.CombineHashCodes(EqualityComparer.Default.GetHashCode(Item1), - EqualityComparer.Default.GetHashCode(Item2), - EqualityComparer.Default.GetHashCode(Item3), - EqualityComparer.Default.GetHashCode(Item4), - EqualityComparer.Default.GetHashCode(Item5), - EqualityComparer.Default.GetHashCode(Item6), - EqualityComparer.Default.GetHashCode(Item7), - rest.GetHashCode()); + return ValueTuple.CombineHashCodes(EqualityComparer.Default.GetHashCode(Item1), + EqualityComparer.Default.GetHashCode(Item2), + EqualityComparer.Default.GetHashCode(Item3), + EqualityComparer.Default.GetHashCode(Item4), + EqualityComparer.Default.GetHashCode(Item5), + EqualityComparer.Default.GetHashCode(Item6), + EqualityComparer.Default.GetHashCode(Item7), + rest.GetHashCode()); } Debug.Assert(false, "Missed all cases for computing ValueTuple hash code"); @@ -2063,27 +2063,27 @@ private int GetHashCodeCore(IEqualityComparer comparer) switch (k) { case 1: - return ValueTuple.CombineHashCodes(comparer.GetHashCode(Item7), rest.GetHashCode(comparer)); + return ValueTuple.CombineHashCodes(comparer.GetHashCode(Item7), rest.GetHashCode(comparer)); case 2: - return ValueTuple.CombineHashCodes(comparer.GetHashCode(Item6), comparer.GetHashCode(Item7), rest.GetHashCode(comparer)); + return ValueTuple.CombineHashCodes(comparer.GetHashCode(Item6), comparer.GetHashCode(Item7), rest.GetHashCode(comparer)); case 3: - return ValueTuple.CombineHashCodes(comparer.GetHashCode(Item5), comparer.GetHashCode(Item6), comparer.GetHashCode(Item7), - rest.GetHashCode(comparer)); + return ValueTuple.CombineHashCodes(comparer.GetHashCode(Item5), comparer.GetHashCode(Item6), comparer.GetHashCode(Item7), + rest.GetHashCode(comparer)); case 4: - return ValueTuple.CombineHashCodes(comparer.GetHashCode(Item4), comparer.GetHashCode(Item5), comparer.GetHashCode(Item6), - comparer.GetHashCode(Item7), rest.GetHashCode(comparer)); + return ValueTuple.CombineHashCodes(comparer.GetHashCode(Item4), comparer.GetHashCode(Item5), comparer.GetHashCode(Item6), + comparer.GetHashCode(Item7), rest.GetHashCode(comparer)); case 5: - return ValueTuple.CombineHashCodes(comparer.GetHashCode(Item3), comparer.GetHashCode(Item4), comparer.GetHashCode(Item5), - comparer.GetHashCode(Item6), comparer.GetHashCode(Item7), rest.GetHashCode(comparer)); + return ValueTuple.CombineHashCodes(comparer.GetHashCode(Item3), comparer.GetHashCode(Item4), comparer.GetHashCode(Item5), + comparer.GetHashCode(Item6), comparer.GetHashCode(Item7), rest.GetHashCode(comparer)); case 6: - return ValueTuple.CombineHashCodes(comparer.GetHashCode(Item2), comparer.GetHashCode(Item3), comparer.GetHashCode(Item4), - comparer.GetHashCode(Item5), comparer.GetHashCode(Item6), comparer.GetHashCode(Item7), - rest.GetHashCode(comparer)); + return ValueTuple.CombineHashCodes(comparer.GetHashCode(Item2), comparer.GetHashCode(Item3), comparer.GetHashCode(Item4), + comparer.GetHashCode(Item5), comparer.GetHashCode(Item6), comparer.GetHashCode(Item7), + rest.GetHashCode(comparer)); case 7: case 8: - return ValueTuple.CombineHashCodes(comparer.GetHashCode(Item1), comparer.GetHashCode(Item2), comparer.GetHashCode(Item3), - comparer.GetHashCode(Item4), comparer.GetHashCode(Item5), comparer.GetHashCode(Item6), - comparer.GetHashCode(Item7), rest.GetHashCode(comparer)); + return ValueTuple.CombineHashCodes(comparer.GetHashCode(Item1), comparer.GetHashCode(Item2), comparer.GetHashCode(Item3), + comparer.GetHashCode(Item4), comparer.GetHashCode(Item5), comparer.GetHashCode(Item6), + comparer.GetHashCode(Item7), rest.GetHashCode(comparer)); } Debug.Assert(false, "Missed all cases for computing ValueTuple hash code"); @@ -2137,8 +2137,4 @@ int ITupleInternal.Size } } } -} - - - - +} \ No newline at end of file diff --git a/src/IFoxCAD.Basal/Sortedset/ISet.cs b/src/IFoxCAD.Basal/Sortedset/ISet.cs index d991205..22b9509 100644 --- a/src/IFoxCAD.Basal/Sortedset/ISet.cs +++ b/src/IFoxCAD.Basal/Sortedset/ISet.cs @@ -1,35 +1,33 @@ #if NET35 // ==++== -// +// // Copyright (c) Microsoft Corporation. All rights reserved. -// +// // ==--== /*============================================================ ** ** Interface: ISet -** +** ** kimhamil ** ** ** Purpose: Base interface for all generic sets. ** -** +** ===========================================================*/ namespace System.Collections.Generic { - using System; using System.Runtime.CompilerServices; /// /// Generic collection that guarantees the uniqueness of its elements, as defined - /// by some comparer. It also supports basic set operations such as Union, Intersection, + /// by some comparer. It also supports basic set operations such as Union, Intersection, /// Complement and Exclusive Complement. /// public interface ISet : ICollection { - // Add ITEM to the set, return true if added, false if duplicate new bool Add(T item); @@ -62,10 +60,6 @@ public interface ISet : ICollection // Check if this set contains the same and only the same elements as other bool SetEquals(IEnumerable other); - - - } - -} +} #endif \ No newline at end of file diff --git a/src/IFoxCAD.Basal/Sortedset/Sortedset.cs b/src/IFoxCAD.Basal/Sortedset/Sortedset.cs index f9c7060..4088582 100644 --- a/src/IFoxCAD.Basal/Sortedset/Sortedset.cs +++ b/src/IFoxCAD.Basal/Sortedset/Sortedset.cs @@ -9,9 +9,9 @@ #pragma warning disable CS8604 // 引用类型参数可能为 null。 // #define USING_HASH_SET // ==++== -// +// // Copyright (c) Microsoft Corporation. All rights reserved. -// +// // ==--== /*============================================================ ** @@ -20,7 +20,7 @@ ** Purpose: A generic sorted set. ** ** Date: August 15, 2008 -** +** ===========================================================*/ @@ -39,14 +39,14 @@ namespace System.Collections.Generic // 2. Every leaf (nil node) is black // 3. If a node is red, then both its children are black // 4. Every simple path from a node to a descendant leaf contains the same number of black nodes - // - // The basic idea of red-black tree is to represent 2-3-4 trees as standard BSTs but to add one extra bit of information - // per node to encode 3-nodes and 4-nodes. + // + // The basic idea of red-black tree is to represent 2-3-4 trees as standard BSTs but to add one extra bit of information + // per node to encode 3-nodes and 4-nodes. // 4-nodes will be represented as: B // R R - // 3 -node will be represented as: B or B + // 3 -node will be represented as: B or B // R B B R - // + // // For a detailed description of the algorithm, take a look at "Algorithms" by Robert Sedgewick. // @@ -95,7 +95,7 @@ public class SortedSet : ISet, ICollection, ICollection, IReadOnlyColle private const String lBoundActiveName = "lBoundActive"; private const String uBoundActiveName = "uBoundActive"; - private SerializationInfo siInfo; // A temporary variable which we need during deserialization. + private SerializationInfo siInfo; // A temporary variable which we need during deserialization. #endif internal const int StackAllocThreshold = 100; @@ -105,7 +105,6 @@ public class SortedSet : ISet, ICollection, ICollection, IReadOnlyColle public SortedSet() { this.comparer = Comparer.Default; - } public SortedSet(IComparer comparer) @@ -121,7 +120,9 @@ public SortedSet(IComparer comparer) } - public SortedSet(IEnumerable collection) : this(collection, Comparer.Default) { } + public SortedSet(IEnumerable collection) : this(collection, Comparer.Default) + { + } public SortedSet(IEnumerable collection, IComparer comparer) : this(comparer) @@ -186,7 +187,6 @@ public SortedSet(IEnumerable collection, IComparer comparer) } else { // As it stands, you're doing an NlogN sort of the collection - List els = new(collection); els.Sort(this.comparer); for (int i = 1; i < els.Count; i++) @@ -216,7 +216,6 @@ protected SortedSet(SerializationInfo info, StreamingContext context) #region Bulk Operation Helpers private void AddAllElements(IEnumerable collection) { - foreach (T item in collection) { if (!this.Contains(item)) @@ -250,8 +249,8 @@ private bool ContainsAllElements(IEnumerable collection) // // Do a in order walk on tree and calls the delegate for each node. // If the action delegate returns false, stop the walk. - // - // Return true if the entire tree has been walked. + // + // Return true if the entire tree has been walked. // Otherwise returns false. // internal bool InOrderTreeWalk(TreeWalkPredicate action) @@ -259,7 +258,7 @@ internal bool InOrderTreeWalk(TreeWalkPredicate action) return InOrderTreeWalk(action, false); } - // Allows for the change in traversal direction. Reverse visits nodes in descending order + // Allows for the change in traversal direction. Reverse visits nodes in descending order internal virtual bool InOrderTreeWalk(TreeWalkPredicate action, bool reverse) { if (root == null) @@ -269,7 +268,7 @@ internal virtual bool InOrderTreeWalk(TreeWalkPredicate action, bool reverse) // The maximum height of a red-black tree is 2*lg(n+1). // See page 264 of "Introduction to algorithms" by Thomas H. Cormen - // note: this should be logbase2, but since the stack grows itself, we + // note: this should be logbase2, but since the stack grows itself, we // don't want the extra cost Stack stack = new(2 * (int)(SortedSet.Log2(Count + 1))); Node current = root; @@ -297,11 +296,11 @@ internal virtual bool InOrderTreeWalk(TreeWalkPredicate action, bool reverse) } // - // Do a left to right breadth first walk on tree and + // Do a left to right breadth first walk on tree and // calls the delegate for each node. // If the action delegate returns false, stop the walk. - // - // Return true if the entire tree has been walked. + // + // Return true if the entire tree has been walked. // Otherwise returns false. // internal virtual bool BreadthFirstTreeWalk(TreeWalkPredicate action) @@ -395,15 +394,14 @@ internal virtual void VersionCheck() { } internal virtual bool IsWithinRange(T item) { return true; - } #endregion #region ICollection Members /// - /// Add the value ITEM to the tree, returns true if added, false if duplicate + /// Add the value ITEM to the tree, returns true if added, false if duplicate /// - /// item to be added + /// item to be added public bool Add(T item) { return AddIfNotPresent(item); @@ -416,9 +414,9 @@ void ICollection.Add(T item) /// - /// Adds ITEM to the tree if not already present. Returns TRUE if value was successfully added + /// Adds ITEM to the tree if not already present. Returns TRUE if value was successfully added /// or FALSE if it is a duplicate - /// + ///
    internal virtual bool AddIfNotPresent(T item) { if (root == null) @@ -430,10 +428,10 @@ internal virtual bool AddIfNotPresent(T item) } // - // Search for a node at bottom to insert the new node. + // Search for a node at bottom to insert the new node. // If we can guanratee the node we found is not a 4-node, it would be easy to do insertion. // We split 4-nodes along the search path. - // + // Node current = root; Node parent = null; Node grandParent = null; @@ -456,7 +454,7 @@ internal virtual bool AddIfNotPresent(T item) return false; } - // split a 4-node into two 2-nodes + // split a 4-node into two 2-nodes if (Is4Node(current)) { Split4Node(current); @@ -508,20 +506,19 @@ public bool Remove(T item) internal virtual bool DoRemove(T item) { - if (root == null) { return false; } - // Search for a node and then find its succesor. - // Then copy the item from the succesor to the matching node and delete the successor. - // If a node doesn't have a successor, we can replace it with its left child (if not empty.) + // Search for a node and then find its succesor. + // Then copy the item from the succesor to the matching node and delete the successor. + // If a node doesn't have a successor, we can replace it with its left child (if not empty.) // or delete the matching node. - // + // // In top-down implementation, it is important to make sure the node to be deleted is not a 2-node. - // Following code will make sure the node on the path is not a 2 Node. + // Following code will make sure the node on the path is not a 2 Node. // even if we don't actually remove from the set, we may be altering its structure (by doing rotations // and such). so update version to disable any enumerators/subsets working on it @@ -546,8 +543,8 @@ internal virtual bool DoRemove(T item) Node sibling = GetSibling(current, parent); if (sibling.IsRed) { - // If parent is a 3-node, flip the orientation of the red link. - // We can acheive this by a single rotation + // If parent is a 3-node, flip the orientation of the red link. + // We can acheive this by a single rotation // This case is converted to one of other cased below. Debug.Assert(!parent.IsRed, "parent must be a black node!"); if (parent.Right == sibling) @@ -563,7 +560,7 @@ internal virtual bool DoRemove(T item) sibling.IsRed = false; // parent's color // sibling becomes child of grandParent or root after rotation. Update link from grandParent or root ReplaceChildOfNodeOrRoot(grandParent, parent, sibling); - // sibling will become grandParent of current node + // sibling will become grandParent of current node grandParent = sibling; if (parent == match) { @@ -673,16 +670,21 @@ public virtual void Clear() public virtual bool Contains(T item) { - return FindNode(item) != null; } - public void CopyTo(T[] array) { CopyTo(array, 0, Count); } + public void CopyTo(T[] array) + { + CopyTo(array, 0, Count); + } - public void CopyTo(T[] array, int index) { CopyTo(array, index, Count); } + public void CopyTo(T[] array, int index) + { + CopyTo(array, index, Count); + } public void CopyTo(T[] array, int index, int count) { @@ -809,10 +811,10 @@ private static Node GetSibling(Node node, Node parent) } // After calling InsertionBalance, we need to make sure current and parent up-to-date. - // It doesn't matter if we keep grandParent and greatGrantParent up-to-date + // It doesn't matter if we keep grandParent and greatGrantParent up-to-date // because we won't need to split again in the next node. // By the time we need to split again, everything will be correctly set. - // + // private void InsertionBalance(Node current, ref Node parent, Node grandParent, Node greatGrandParent) { Debug.Assert(grandParent != null, "Grand parent cannot be null here!"); @@ -827,7 +829,7 @@ private void InsertionBalance(Node current, ref Node parent, Node grandParent, N else { // different orientaton, double rotation newChildOfGreatGrandParent = currentIsOnRight ? RotateLeftRight(grandParent) : RotateRightLeft(grandParent); - // current node now becomes the child of greatgrandparent + // current node now becomes the child of greatgrandparent parent = greatGrandParent; } // grand parent will become a child of either parent of current. @@ -872,8 +874,8 @@ private static void Merge2Nodes(Node parent, Node child1, Node child2) child2.IsRed = true; } - // Replace the child of a parent node. - // If the parent node is null, replace the root. + // Replace the child of a parent node. + // If the parent node is null, replace the root. private void ReplaceChildOfNodeOrRoot(Node parent, Node child, Node newChild) { if (parent != null) @@ -926,7 +928,6 @@ private void ReplaceNode(Node match, Node parentOfMatch, Node succesor, Node par } ReplaceChildOfNodeOrRoot(parentOfMatch, match, succesor); - } internal virtual Node FindNode(T item) @@ -948,7 +949,7 @@ internal virtual Node FindNode(T item) return null; } - // used for bithelpers. Note that this implementation is completely different + // used for bithelpers. Note that this implementation is completely different // from the Subset's. The two should not be mixed. This indexes as if the tree were an array. // http://en.wikipedia.org/wiki/Binary_Tree#Methods_for_storing_binary_trees internal virtual int InternalIndexOf(T item) @@ -1086,7 +1087,7 @@ public static IEqualityComparer> CreateSetComparer() /// Create a new set comparer for this set, where this set's members' equality is defined by the /// memberEqualityComparer. Note that this equality comparer's definition of equality must be the /// same as this set's Comparer's definition of equality - /// + /// public static IEqualityComparer> CreateSetComparer(IEqualityComparer memberEqualityComparer) { return new SortedSetEqualityComparer(memberEqualityComparer); @@ -1096,7 +1097,7 @@ public static IEqualityComparer> CreateSetComparer(IEqualityCompare /// /// Decides whether these sets are the same, given the comparer. If the EC's are the same, we can /// just use SetEquals, but if they aren't then we have to manually check with the given comparer - /// + /// internal static bool SortedSetEquals(SortedSet set1, SortedSet set2, IComparer comparer) { // handle null cases first @@ -1136,7 +1137,6 @@ internal static bool SortedSetEquals(SortedSet set1, SortedSet set2, IComp } return true; } - } @@ -1258,9 +1258,6 @@ public void UnionWith(IEnumerable other) private static Node ConstructRootFromSortedArray(T[] arr, int startIndex, int endIndex, Node redNode) { - - - // what does this do? // you're given a sorted array... say 1 2 3 4 5 6 // 2 cases: @@ -1272,7 +1269,7 @@ private static Node ConstructRootFromSortedArray(T[] arr, int startIndex, int en // now add 4 as a red node to the lowest element on the right branch // 3 3 // 1 5 -> 1 5 - // 2 6 2 4 6 + // 2 6 2 4 6 // As we're adding to the leftmost of the right branch, nesting will not hurt the red-black properties // Leaf nodes are red if they have no sibling (if there are 2 nodes or if a node trickles // down to the bottom @@ -1318,7 +1315,6 @@ private static Node ConstructRootFromSortedArray(T[] arr, int startIndex, int en if (redNode != null) { root.Left.Left = redNode; - } } else @@ -1338,17 +1334,16 @@ private static Node ConstructRootFromSortedArray(T[] arr, int startIndex, int en } } return root; - } /// - /// Transform this set into its intersection with the IEnumerable OTHER - /// NOTE: The caller object is important as IntersectionWith uses the - /// comparator associated with THIS to check equality - /// Throws ArgumentNullException if OTHER is null + /// Transform this set into its intersection with the IEnumerable OTHER + /// NOTE: The caller object is important as IntersectionWith uses the + /// comparator associated with THIS to check equality + /// Throws ArgumentNullException if OTHER is null /// - /// + /// public virtual void IntersectWith(IEnumerable other) { if (other == null) @@ -1368,8 +1363,6 @@ public virtual void IntersectWith(IEnumerable other) // only let this happen if i am also a SortedSet, not a SubSet if (other is SortedSet s && t == null && AreComparersEqual(this, s)) { - - // first do a merge sort to an array. T[] merged = new T[this.Count]; int c = 0; @@ -1427,16 +1420,15 @@ internal virtual void IntersectWithEnumerable(IEnumerable other) } this.Clear(); AddAllElements(toSave); - } /// - /// Transform this set into its complement with the IEnumerable OTHER - /// NOTE: The caller object is important as ExceptWith uses the - /// comparator associated with THIS to check equality - /// Throws ArgumentNullException if OTHER is null + /// Transform this set into its complement with the IEnumerable OTHER + /// NOTE: The caller object is important as ExceptWith uses the + /// comparator associated with THIS to check equality + /// Throws ArgumentNullException if OTHER is null /// /// public void ExceptWith(IEnumerable other) @@ -1472,7 +1464,6 @@ public void ExceptWith(IEnumerable other) Remove(item); } } - } else { @@ -1482,9 +1473,9 @@ public void ExceptWith(IEnumerable other) /// /// Transform this set so it contains elements in THIS or OTHER but not both - /// NOTE: The caller object is important as SymmetricExceptWith uses the - /// comparator associated with THIS to check equality - /// Throws ArgumentNullException if OTHER is null + /// NOTE: The caller object is important as SymmetricExceptWith uses the + /// comparator associated with THIS to check equality + /// Throws ArgumentNullException if OTHER is null /// /// public void SymmetricExceptWith(IEnumerable other) @@ -1617,7 +1608,6 @@ private bool IsSubsetOfSortedSetWithSameEC(SortedSet asSorted) return false; } return true; - } @@ -1724,7 +1714,7 @@ public bool IsProperSupersetOf(IEnumerable other) #if USING_HASH_SET // do it one way for HashSets - + HashSet asHash = other as HashSet; if (asHash != null && comparer.Equals(Comparer.Default) && asHash.Comparer.Equals(EqualityComparer.Default)) { return asHash.IsProperSubsetOf(this); @@ -1841,14 +1831,14 @@ public bool Overlaps(IEnumerable other) /// /// This works similar to HashSet's CheckUniqueAndUnfound (description below), except that the bit /// array maps differently than in the HashSet. We can only use this for the bulk boolean checks. - /// + /// /// Determines counts that can be used to determine equality, subset, and superset. This /// is only used when other is an IEnumerable and not a HashSet. If other is a HashSet - /// these properties can be checked faster without use of marking because we can assume + /// these properties can be checked faster without use of marking because we can assume /// other has no duplicates. - /// + /// /// The following count checks are performed by callers: - /// 1. Equals: checks if unfoundCount = 0 and uniqueFoundCount = Count; i.e. everything + /// 1. Equals: checks if unfoundCount = 0 and uniqueFoundCount = Count; i.e. everything /// in other is in this and everything in this is in other /// 2. Subset: checks if unfoundCount >= 0 and uniqueFoundCount = Count; i.e. other may /// have elements not in this and everything in this is in other @@ -1857,7 +1847,7 @@ public bool Overlaps(IEnumerable other) /// 4. Proper superset: checks if unfound count = 0 and uniqueFoundCount strictly less /// than Count; i.e. everything in other was in this and this had at least one element /// not contained in other. - /// + /// /// An earlier implementation used delegates to perform these checks rather than returning /// an ElementCount struct; however this was changed due to the perf overhead of delegates. /// @@ -1876,7 +1866,7 @@ private unsafe ElementCount CheckUniqueAndUnfoundElements(IEnumerable other, { ElementCount result; - // need special case in case this has no elements. + // need special case in case this has no elements. if (Count == 0) { int numElementsInOther = 0; @@ -1965,7 +1955,6 @@ public int RemoveWhere(Predicate match) } return actuallyRemoved; - } @@ -2009,7 +1998,7 @@ public IEnumerable Reverse() /// Any changes made to the subset reflect in the actual tree /// /// Lowest Value allowed in the subset - /// Highest Value allowed in the subset + /// Highest Value allowed in the subset public virtual SortedSet GetViewBetween(T lowerValue, T upperValue) { if (Comparer.Compare(lowerValue, upperValue) > 0) @@ -2036,7 +2025,7 @@ internal virtual bool VersionUpToDate() /// This class represents a subset view into the tree. Any changes to this view /// are reflected in the actual tree. Uses the Comparator of the underlying tree. /// - /// + /// #if !FEATURE_NETCORE [Serializable] internal sealed class TreeSubSet : SortedSet, ISerializable, IDeserializationCallback @@ -2069,7 +2058,7 @@ public TreeSubSet(SortedSet Underlying, T Min, T Max, bool lowerBoundActive, max = Max; lBoundActive = lowerBoundActive; uBoundActive = upperBoundActive; - root = underlying.FindRange(min, max, lBoundActive, uBoundActive); // root is first element within range + root = underlying.FindRange(min, max, lBoundActive, uBoundActive); // root is first element within range count = 0; version = -1; VersionCheckImpl(); @@ -2095,11 +2084,10 @@ private TreeSubSet(SerializationInfo info, StreamingContext context) /// /// Additions to this tree need to be added to the underlying tree as well - /// + /// internal override bool AddIfNotPresent(T item) { - if (!IsWithinRange(item)) { ThrowHelper.ThrowArgumentOutOfRangeException(ExceptionArgument.collection); @@ -2126,7 +2114,6 @@ public override bool Contains(T item) internal override bool DoRemove(T item) { // todo: uppercase this and others - if (!IsWithinRange(item)) { return false; @@ -2142,8 +2129,6 @@ internal override bool DoRemove(T item) public override void Clear() { - - if (count == 0) { return; @@ -2164,7 +2149,6 @@ public override void Clear() internal override bool IsWithinRange(T item) { - int comp = (lBoundActive ? Comparer.Compare(min, item) : -1); if (comp > 0) { @@ -2268,14 +2252,12 @@ internal override bool BreadthFirstTreeWalk(TreeWalkPredicate action) { processQueue.Add(current.Right); } - } return true; } internal override SortedSet.Node FindNode(T item) { - if (!IsWithinRange(item)) { return null; @@ -2330,7 +2312,6 @@ private void VersionCheckImpl() // Cannot increase the bounds of the subset, can only decrease it public override SortedSet GetViewBetween(T lowerValue, T upperValue) { - if (lBoundActive && Comparer.Compare(min, lowerValue) > 0) { // lBound = min; @@ -2347,7 +2328,6 @@ public override SortedSet GetViewBetween(T lowerValue, T upperValue) internal override void IntersectWithEnumerable(IEnumerable other) { - List toSave = new(this.Count); foreach (T item in other) { @@ -2387,7 +2367,6 @@ void IDeserializationCallback.OnDeserialization(Object sender) { // don't do anything here as its already been done by the constructor // OnDeserialization(sender); - } protected override void OnDeserialization(Object sender) @@ -2434,13 +2413,8 @@ private void OnDeserializationImpl(Object sender) ThrowHelper.ThrowSerializationException(ExceptionResource.Serialization_MismatchedCount); } siInfo = null; - } #endif // !FEATURE_NETCORE - - - - } @@ -2529,14 +2503,14 @@ internal class Node public Node(T item) { - // The default color will be red, we never need to create a black node directly. + // The default color will be red, we never need to create a black node directly. this.Item = item; IsRed = true; } public Node(T item, bool isRed) { - // The default color will be red, we never need to create a black node directly. + // The default color will be red, we never need to create a black node directly. this.Item = item; this.IsRed = isRed; } @@ -2598,7 +2572,6 @@ internal Enumerator(SortedSet set, bool reverse) siInfo = null; #endif Intialize(); - } #if !FEATURE_NETCORE @@ -2622,7 +2595,6 @@ private void GetObjectData(SerializationInfo info, StreamingContext context) if (info == null) { ThrowHelper.ThrowArgumentNullException(ExceptionArgument.info); - } info.AddValue(TreeName, tree, typeof(SortedSet)); info.AddValue(EnumVersionName, version); @@ -2660,16 +2632,12 @@ private void OnDeserialization(Object sender) break; } } - - } #endif // !FEATURE_NETCORE private void Intialize() { - - current = null; SortedSet.Node node = tree.root; Node next = null, other = null; @@ -2695,7 +2663,6 @@ private void Intialize() public bool MoveNext() { - // this is a hack to make sure that the underlying subset has not been changed since // tree.VersionCheck(); @@ -2788,10 +2755,6 @@ void IEnumerator.Reset() { Reset(); } - - - - } @@ -2812,7 +2775,7 @@ internal struct ElementCount /// The value from the set that the search found, or the default value of when the search yielded no match. /// A value indicating whether the search was successful. /// - /// This can be useful when you want to reuse a previously stored reference instead of + /// This can be useful when you want to reuse a previously stored reference instead of /// a newly constructed one (so that more sharing of references can occur) or to look up /// a value that has more complete data than the value you currently have, although their /// comparer functions indicate they are equal. @@ -2842,30 +2805,34 @@ private static int Log2(int value) return c; } #endregion - - } /// /// A class that generates an IEqualityComparer for this SortedSet. Requires that the definition of /// equality defined by the IComparer for this SortedSet be consistent with the default IEqualityComparer /// for the type T. If not, such an IEqualityComparer should be provided through the constructor. - /// + /// internal class SortedSetEqualityComparer : IEqualityComparer> { private readonly IComparer comparer; private readonly IEqualityComparer e_comparer; - public SortedSetEqualityComparer() : this(null, null) { } + public SortedSetEqualityComparer() : this(null, null) + { + } - public SortedSetEqualityComparer(IComparer comparer) : this(comparer, null) { } + public SortedSetEqualityComparer(IComparer comparer) : this(comparer, null) + { + } - public SortedSetEqualityComparer(IEqualityComparer memberEqualityComparer) : this(null, memberEqualityComparer) { } + public SortedSetEqualityComparer(IEqualityComparer memberEqualityComparer) : this(null, memberEqualityComparer) + { + } /// /// Create a new SetEqualityComparer, given a comparer for member order and another for member equality (these /// must be consistent in their definition of equality) - /// + /// public SortedSetEqualityComparer(IComparer comparer, IEqualityComparer memberEqualityComparer) { if (comparer == null) @@ -2899,7 +2866,7 @@ public int GetHashCode(SortedSet obj) return hashCode; } - // Equals method for the comparer itself. + // Equals method for the comparer itself. public override bool Equals(Object obj) { if (obj is not SortedSetEqualityComparer comparer) @@ -2913,10 +2880,7 @@ public override int GetHashCode() { return comparer.GetHashCode() ^ e_comparer.GetHashCode(); } - - } - } @@ -2929,4 +2893,4 @@ public override int GetHashCode() #pragma warning restore CS8618 // 在退出构造函数时,不可为 null 的字段必须包含非 null 值。请考虑声明为可以为 null。 #pragma warning restore CS8601 // 引用类型赋值可能为 null。 #pragma warning restore CS8603 // 可能返回 null 引用。 -#endif +#endif \ No newline at end of file diff --git a/src/IFoxCAD.Basal/Sortedset/ThrowHelper.cs b/src/IFoxCAD.Basal/Sortedset/ThrowHelper.cs index 205dd1c..ebb2f41 100644 --- a/src/IFoxCAD.Basal/Sortedset/ThrowHelper.cs +++ b/src/IFoxCAD.Basal/Sortedset/ThrowHelper.cs @@ -5,8 +5,8 @@ namespace System { // This file defines an internal class used to throw exceptions in BCL code. - // The main purpose is to reduce code size. - // + // The main purpose is to reduce code size. + // // The old way to throw an exception generates quite a lot IL code and assembly code. // Following is an example: // C# source @@ -18,10 +18,10 @@ namespace System // IL_0012: newobj instance void System.ArgumentNullException::.ctor(string,string) // IL_0017: throw // which is 21bytes in IL. - // + // // So we want to get rid of the ldstr and call to Environment.GetResource in IL. // In order to do that, I created two enums: ExceptionResource, ExceptionArgument to represent the - // argument name and resource name in a small integer. The source code will be changed to + // argument name and resource name in a small integer. The source code will be changed to // ThrowHelper.ThrowArgumentNullException(ExceptionArgument.key, ExceptionResource.ArgumentNull_Key); // // The IL code will be 7 bytes. @@ -30,11 +30,11 @@ namespace System // IL_000a: call void System.ThrowHelper::ThrowArgumentNullException(valuetype System.ExceptionArgument) // IL_000f: ldarg.0 // - // This will also reduce the Jitted code size a lot. + // This will also reduce the Jitted code size a lot. + // + // It is very important we do this for generic classes because we can easily generate the same code + // multiple times for different instantiation. // - // It is very important we do this for generic classes because we can easily generate the same code - // multiple times for different instantiation. - // // < @@ -108,7 +108,7 @@ internal static void ThrowNotSupportedException(ExceptionResource resource) // Allow nulls for reference types and Nullable, but not for value types. internal static void IfNullAndNullsAreIllegalThenThrow(object value, ExceptionArgument argName) { - // Note that default(T) is not equal to null for value types except when T is Nullable. + // Note that default(T) is not equal to null for value types except when T is Nullable. if (value == null && !(default(T) == null)) ThrowHelper.ThrowArgumentNullException(argName); } @@ -123,76 +123,76 @@ internal static string GetArgumentName(ExceptionArgument argument) switch (argument) { case ExceptionArgument.array: - argumentName = "array"; - break; + argumentName = "array"; + break; case ExceptionArgument.arrayIndex: - argumentName = "arrayIndex"; - break; + argumentName = "arrayIndex"; + break; case ExceptionArgument.capacity: - argumentName = "capacity"; - break; + argumentName = "capacity"; + break; case ExceptionArgument.collection: - argumentName = "collection"; - break; + argumentName = "collection"; + break; case ExceptionArgument.converter: - argumentName = "converter"; - break; + argumentName = "converter"; + break; case ExceptionArgument.count: - argumentName = "count"; - break; + argumentName = "count"; + break; case ExceptionArgument.dictionary: - argumentName = "dictionary"; - break; + argumentName = "dictionary"; + break; case ExceptionArgument.index: - argumentName = "index"; - break; + argumentName = "index"; + break; case ExceptionArgument.info: - argumentName = "info"; - break; + argumentName = "info"; + break; case ExceptionArgument.key: - argumentName = "key"; - break; + argumentName = "key"; + break; case ExceptionArgument.match: - argumentName = "match"; - break; + argumentName = "match"; + break; case ExceptionArgument.obj: - argumentName = "obj"; - break; + argumentName = "obj"; + break; case ExceptionArgument.queue: - argumentName = "queue"; - break; + argumentName = "queue"; + break; case ExceptionArgument.stack: - argumentName = "stack"; - break; + argumentName = "stack"; + break; case ExceptionArgument.startIndex: - argumentName = "startIndex"; - break; + argumentName = "startIndex"; + break; case ExceptionArgument.value: - argumentName = "value"; - break; + argumentName = "value"; + break; case ExceptionArgument.item: - argumentName = "item"; - break; + argumentName = "item"; + break; default: - Debug.Assert(false, "The enum value is not defined, please checked ExceptionArgumentName Enum."); - return string.Empty; + Debug.Assert(false, "The enum value is not defined, please checked ExceptionArgumentName Enum."); + return string.Empty; } return argumentName; @@ -208,120 +208,119 @@ internal static string GetResourceName(ExceptionResource resource) switch (resource) { case ExceptionResource.Argument_ImplementIComparable: - resourceName = "SR.Argument_ImplementIComparable"; - break; + resourceName = "SR.Argument_ImplementIComparable"; + break; case ExceptionResource.Argument_AddingDuplicate: - resourceName = "SR.Argument_AddingDuplicate"; - break; + resourceName = "SR.Argument_AddingDuplicate"; + break; case ExceptionResource.ArgumentOutOfRange_Index: - resourceName = "SR.ArgumentOutOfRange_Index"; - break; + resourceName = "SR.ArgumentOutOfRange_Index"; + break; case ExceptionResource.ArgumentOutOfRange_NeedNonNegNum: - resourceName = "SR.ArgumentOutOfRange_NeedNonNegNum"; - break; + resourceName = "SR.ArgumentOutOfRange_NeedNonNegNum"; + break; case ExceptionResource.ArgumentOutOfRange_NeedNonNegNumRequired: - resourceName = "SR.ArgumentOutOfRange_NeedNonNegNumRequired"; - break; + resourceName = "SR.ArgumentOutOfRange_NeedNonNegNumRequired"; + break; case ExceptionResource.ArgumentOutOfRange_SmallCapacity: - resourceName = "SR.ArgumentOutOfRange_SmallCapacity"; - break; + resourceName = "SR.ArgumentOutOfRange_SmallCapacity"; + break; case ExceptionResource.Arg_ArrayPlusOffTooSmall: - resourceName = "SR.Arg_ArrayPlusOffTooSmall"; - break; + resourceName = "SR.Arg_ArrayPlusOffTooSmall"; + break; case ExceptionResource.Arg_RankMultiDimNotSupported: - resourceName = "SR.Arg_MultiRank"; - break; + resourceName = "SR.Arg_MultiRank"; + break; case ExceptionResource.Arg_NonZeroLowerBound: - resourceName = "SR.Arg_NonZeroLowerBound"; - break; + resourceName = "SR.Arg_NonZeroLowerBound"; + break; case ExceptionResource.Argument_InvalidArrayType: - resourceName = "SR.Invalid_Array_Type"; - break; + resourceName = "SR.Invalid_Array_Type"; + break; case ExceptionResource.Argument_InvalidOffLen: - resourceName = "SR.Argument_InvalidOffLen"; - break; + resourceName = "SR.Argument_InvalidOffLen"; + break; case ExceptionResource.InvalidOperation_CannotRemoveFromStackOrQueue: - resourceName = "SR.InvalidOperation_CannotRemoveFromStackOrQueue"; - break; + resourceName = "SR.InvalidOperation_CannotRemoveFromStackOrQueue"; + break; case ExceptionResource.InvalidOperation_EmptyCollection: - resourceName = "SR.InvalidOperation_EmptyCollection"; - break; + resourceName = "SR.InvalidOperation_EmptyCollection"; + break; case ExceptionResource.InvalidOperation_EmptyQueue: - resourceName = "SR.InvalidOperation_EmptyQueue"; - break; + resourceName = "SR.InvalidOperation_EmptyQueue"; + break; case ExceptionResource.InvalidOperation_EnumOpCantHappen: - resourceName = "SR.InvalidOperation_EnumOpCantHappen"; - break; + resourceName = "SR.InvalidOperation_EnumOpCantHappen"; + break; case ExceptionResource.InvalidOperation_EnumFailedVersion: - resourceName = "SR.InvalidOperation_EnumFailedVersion"; - break; + resourceName = "SR.InvalidOperation_EnumFailedVersion"; + break; case ExceptionResource.InvalidOperation_EmptyStack: - resourceName = "SR.InvalidOperation_EmptyStack"; - break; + resourceName = "SR.InvalidOperation_EmptyStack"; + break; case ExceptionResource.InvalidOperation_EnumNotStarted: - resourceName = "SR.InvalidOperation_EnumNotStarted"; - break; + resourceName = "SR.InvalidOperation_EnumNotStarted"; + break; case ExceptionResource.InvalidOperation_EnumEnded: - resourceName = "SR.InvalidOperation_EnumEnded"; - break; + resourceName = "SR.InvalidOperation_EnumEnded"; + break; case ExceptionResource.NotSupported_KeyCollectionSet: - resourceName = "SR.NotSupported_KeyCollectionSet"; - break; + resourceName = "SR.NotSupported_KeyCollectionSet"; + break; case ExceptionResource.NotSupported_SortedListNestedWrite: - resourceName = "SR.NotSupported_SortedListNestedWrite"; - break; + resourceName = "SR.NotSupported_SortedListNestedWrite"; + break; #if !SILVERLIGHT case ExceptionResource.Serialization_InvalidOnDeser: - resourceName = "SR.Serialization_InvalidOnDeser"; - break; + resourceName = "SR.Serialization_InvalidOnDeser"; + break; case ExceptionResource.Serialization_MissingValues: - resourceName = "SR.Serialization_MissingValues"; - break; + resourceName = "SR.Serialization_MissingValues"; + break; case ExceptionResource.Serialization_MismatchedCount: - resourceName = "SR.Serialization_MismatchedCount"; - break; + resourceName = "SR.Serialization_MismatchedCount"; + break; #endif case ExceptionResource.NotSupported_ValueCollectionSet: - resourceName = "SR.NotSupported_ValueCollectionSet"; - break; + resourceName = "SR.NotSupported_ValueCollectionSet"; + break; default: - Debug.Assert(false, "The enum value is not defined, please checked ExceptionArgumentName Enum."); - return string.Empty; + Debug.Assert(false, "The enum value is not defined, please checked ExceptionArgumentName Enum."); + return string.Empty; } return resourceName; } - } // // The convention for this enum is using the argument name as the enum name - // + // internal enum ExceptionArgument { obj, @@ -345,7 +344,7 @@ internal enum ExceptionArgument // // The convention for this enum is using the resource name as the enum name - // + // internal enum ExceptionResource { Argument_ImplementIComparable, diff --git a/src/IFoxCAD.Basal/Sortedset/bithelper.cs b/src/IFoxCAD.Basal/Sortedset/bithelper.cs index d68c4d8..665b435 100644 --- a/src/IFoxCAD.Basal/Sortedset/bithelper.cs +++ b/src/IFoxCAD.Basal/Sortedset/bithelper.cs @@ -9,15 +9,14 @@ namespace System.Collections.Generic { - /// /// ABOUT: - /// Helps with operations that rely on bit marking to indicate whether an item in the - /// collection should be added, removed, visited already, etc. - /// - /// BitHelper doesn't allocate the array; you must pass in an array or ints allocated on the - /// stack or heap. ToIntArrayLength() tells you the int array size you must allocate. - /// + /// Helps with operations that rely on bit marking to indicate whether an item in the + /// collection should be added, removed, visited already, etc. + /// + /// BitHelper doesn't allocate the array; you must pass in an array or ints allocated on the + /// stack or heap. ToIntArrayLength() tells you the int array size you must allocate. + /// /// USAGE: /// Suppose you need to represent a bit array of length (i.e. logical bit array length) /// BIT_ARRAY_LENGTH. Then this is the suggested way to instantiate BitHelper: @@ -31,25 +30,24 @@ namespace System.Collections.Generic /// int[] m_arrayPtr = new int[intArrayLength]; /// bitHelper = new BitHelper(m_arrayPtr, intArrayLength); /// *************************************************************************** - /// + /// /// IMPORTANT: /// The second ctor args, length, should be specified as the length of the int array, not /// the logical bit array. Because length is used for bounds checking into the int array, - /// it's especially important to get this correct for the stackalloc version. See the code - /// samples above; this is the value gotten from ToIntArrayLength(). - /// - /// The length ctor argument is the only exception; for other methods -- MarkBit and + /// it's especially important to get this correct for the stackalloc version. See the code + /// samples above; this is the value gotten from ToIntArrayLength(). + /// + /// The length ctor argument is the only exception; for other methods -- MarkBit and /// IsMarked -- pass in values as indices into the logical bit array, and it will be mapped /// to the position within the array of ints. - /// - /// + /// + /// unsafe internal class BitHelper { // should not be serialized - private const byte MarkedBitFlag = 1; private const byte IntSize = 32; @@ -63,7 +61,7 @@ unsafe internal class BitHelper // array of ints private int[] m_array; - // whether to operate on stack alloc'd or heap alloc'd array + // whether to operate on stack alloc'd or heap alloc'd array private bool useStackAlloc; /// @@ -154,7 +152,7 @@ internal unsafe bool IsMarked(int bitPosition) } /// - /// How many ints must be allocated to represent n bits. Returns (n+31)/32, but + /// How many ints must be allocated to represent n bits. Returns (n+31)/32, but /// avoids overflow /// /// @@ -163,7 +161,6 @@ internal static int ToIntArrayLength(int n) { return n > 0 ? ((n - 1) / IntSize + 1) : 0; } - } } -- Gitee From 62fba5f62212078c887ec0a842183a3eeee1fe00 Mon Sep 17 00:00:00 2001 From: liuqihong <540762622@qq.com> Date: Thu, 15 Sep 2022 03:09:17 +0800 Subject: [PATCH 498/675] =?UTF-8?q?=E7=BB=9F=E4=B8=80=E5=BC=95=E7=94=A8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../IFoxCAD.Aacad09plus/GlobalUsings.cs | 13 ++++------- tests/TestAcad09plus/GlobalUsings.cs | 23 +++++++++++++++---- 2 files changed, 24 insertions(+), 12 deletions(-) diff --git a/src/IFoxCAD.Cad/IFoxCAD.Aacad09plus/GlobalUsings.cs b/src/IFoxCAD.Cad/IFoxCAD.Aacad09plus/GlobalUsings.cs index 385a8f6..2430170 100644 --- a/src/IFoxCAD.Cad/IFoxCAD.Aacad09plus/GlobalUsings.cs +++ b/src/IFoxCAD.Cad/IFoxCAD.Aacad09plus/GlobalUsings.cs @@ -10,9 +10,13 @@ global using Microsoft.Win32; global using System.ComponentModel; global using System.Runtime.InteropServices; +global using System.Collections.Specialized; global using Exception = System.Exception; +global using Registry = Microsoft.Win32.Registry; +global using RegistryKey = Microsoft.Win32.RegistryKey; + /// autocad 引用 global using Autodesk.AutoCAD.ApplicationServices; global using Autodesk.AutoCAD.EditorInput; @@ -21,6 +25,7 @@ global using Autodesk.AutoCAD.Geometry; global using Autodesk.AutoCAD.Runtime; global using Acap = Autodesk.AutoCAD.ApplicationServices.Application; +global using Acgi = Autodesk.AutoCAD.GraphicsInterface; global using Autodesk.AutoCAD.DatabaseServices.Filters; global using Autodesk.AutoCAD; @@ -28,21 +33,13 @@ // jig命名空间会引起Viewport/Polyline等等重义,最好逐个引入 using Autodesk.AutoCAD.GraphicsInterface global using WorldDraw = Autodesk.AutoCAD.GraphicsInterface.WorldDraw; global using Manager = Autodesk.AutoCAD.GraphicsSystem.Manager; - global using Group = Autodesk.AutoCAD.DatabaseServices.Group; global using Viewport = Autodesk.AutoCAD.DatabaseServices.Viewport; - -global using System.Collections.Specialized; -global using Registry = Microsoft.Win32.Registry; -global using RegistryKey = Microsoft.Win32.RegistryKey; - global using Autodesk.AutoCAD.GraphicsInterface; global using Polyline = Autodesk.AutoCAD.DatabaseServices.Polyline; - global using Acad_DwgFiler = Autodesk.AutoCAD.DatabaseServices.DwgFiler; global using Acad_DxfFiler = Autodesk.AutoCAD.DatabaseServices.DxfFiler; global using Acad_ErrorStatus = Autodesk.AutoCAD.Runtime.ErrorStatus; - /// ifoxcad.basal 引用 global using IFoxCAD.Basal; \ No newline at end of file diff --git a/tests/TestAcad09plus/GlobalUsings.cs b/tests/TestAcad09plus/GlobalUsings.cs index d3d5799..1e1bc8d 100644 --- a/tests/TestAcad09plus/GlobalUsings.cs +++ b/tests/TestAcad09plus/GlobalUsings.cs @@ -9,7 +9,13 @@ global using System.Text.RegularExpressions; global using Microsoft.Win32; global using System.ComponentModel; -global using System.Runtime.CompilerServices; +global using System.Runtime.InteropServices; +global using System.Collections.Specialized; + +global using Exception = System.Exception; + +global using Registry = Microsoft.Win32.Registry; +global using RegistryKey = Microsoft.Win32.RegistryKey; /// autocad 引用 global using Autodesk.AutoCAD.ApplicationServices; @@ -18,14 +24,23 @@ global using Autodesk.AutoCAD.DatabaseServices; global using Autodesk.AutoCAD.Geometry; global using Autodesk.AutoCAD.Runtime; -global using Acgi = Autodesk.AutoCAD.GraphicsInterface; global using Acap = Autodesk.AutoCAD.ApplicationServices.Application; +global using Acgi = Autodesk.AutoCAD.GraphicsInterface; -global using Registry = Microsoft.Win32.Registry; -global using RegistryKey = Microsoft.Win32.RegistryKey; +global using Autodesk.AutoCAD.DatabaseServices.Filters; +global using Autodesk.AutoCAD; +// jig命名空间会引起Viewport/Polyline等等重义,最好逐个引入 using Autodesk.AutoCAD.GraphicsInterface +global using WorldDraw = Autodesk.AutoCAD.GraphicsInterface.WorldDraw; +global using Manager = Autodesk.AutoCAD.GraphicsSystem.Manager; +global using Group = Autodesk.AutoCAD.DatabaseServices.Group; +global using Viewport = Autodesk.AutoCAD.DatabaseServices.Viewport; +global using Autodesk.AutoCAD.GraphicsInterface; +global using Polyline = Autodesk.AutoCAD.DatabaseServices.Polyline; global using Acad_DwgFiler = Autodesk.AutoCAD.DatabaseServices.DwgFiler; global using Acad_DxfFiler = Autodesk.AutoCAD.DatabaseServices.DxfFiler; +global using Acad_ErrorStatus = Autodesk.AutoCAD.Runtime.ErrorStatus; + /// ifoxcad global using IFoxCAD.Cad; -- Gitee From 60039b7078a845991706091a0fc9db551104f6c9 Mon Sep 17 00:00:00 2001 From: liuqihong <540762622@qq.com> Date: Thu, 15 Sep 2022 03:09:38 +0800 Subject: [PATCH 499/675] =?UTF-8?q?=E5=BC=95=E5=85=A5jing=E5=88=86?= =?UTF-8?q?=E6=94=AF=E5=86=85=E5=AE=B9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../CLS/TupleElementNamesAttribute.cs | 2 +- src/IFoxCAD.Basal/IFoxCAD.Basal.csproj | 2 +- .../ExtensionMethod/CollectionEx.cs | 92 +-- src/IFoxCAD.Cad/ExtensionMethod/CurveEx.cs | 42 +- src/IFoxCAD.Cad/ExtensionMethod/EditorEx.cs | 107 ++-- src/IFoxCAD.Cad/ExtensionMethod/EntityEx.cs | 2 +- .../ExtensionMethod/Filer/DwgFiler.cs | 538 ++++++++++++++++++ .../ExtensionMethod/Filer/DwgFilerEx.cs | 83 +++ .../ExtensionMethod/Filer/DxfFiler.cs | 219 +++++++ src/IFoxCAD.Cad/ExtensionMethod/ObjectIdEx.cs | 6 +- .../ExtensionMethod/SymbolTableRecordEx.cs | 9 +- .../HatchConverter.cs" | 2 +- src/IFoxCAD.Cad/GlobalUsings.cs | 14 +- src/IFoxCAD.Cad/Runtime/AutoRegAssem.cs | 4 +- src/IFoxCAD.Cad/Runtime/SymbolTable.cs | 4 +- src/IFoxCAD.Cad/Runtime/Utils.cs | 26 +- tests/Test/GlobalUsings.cs | 30 +- tests/Test/TestCurve.cs | 4 +- tests/Test/TestDwgFilerEx.cs | 156 +++++ tests/Test/testblock.cs | 2 - 20 files changed, 1192 insertions(+), 152 deletions(-) create mode 100644 src/IFoxCAD.Cad/ExtensionMethod/Filer/DwgFiler.cs create mode 100644 src/IFoxCAD.Cad/ExtensionMethod/Filer/DwgFilerEx.cs create mode 100644 src/IFoxCAD.Cad/ExtensionMethod/Filer/DxfFiler.cs create mode 100644 tests/Test/TestDwgFilerEx.cs diff --git a/src/IFoxCAD.Basal/CLS/TupleElementNamesAttribute.cs b/src/IFoxCAD.Basal/CLS/TupleElementNamesAttribute.cs index f3f7515..9c3afb3 100644 --- a/src/IFoxCAD.Basal/CLS/TupleElementNamesAttribute.cs +++ b/src/IFoxCAD.Basal/CLS/TupleElementNamesAttribute.cs @@ -47,4 +47,4 @@ public TupleElementNamesAttribute(string[] transformNames) /// meant to carry element names. /// public IList TransformNames => _transformNames; -} +} \ No newline at end of file diff --git a/src/IFoxCAD.Basal/IFoxCAD.Basal.csproj b/src/IFoxCAD.Basal/IFoxCAD.Basal.csproj index e72ed9a..dbc9743 100644 --- a/src/IFoxCAD.Basal/IFoxCAD.Basal.csproj +++ b/src/IFoxCAD.Basal/IFoxCAD.Basal.csproj @@ -8,7 +8,7 @@ true 0.4 InspireFunction - xsfhlzh;vicwjb + xsfhlzh;vicwjb;liuqihong 基于.NET的二次开发基本类库 InspireFunction https://gitee.com/inspirefunction/ifoxcad diff --git a/src/IFoxCAD.Cad/ExtensionMethod/CollectionEx.cs b/src/IFoxCAD.Cad/ExtensionMethod/CollectionEx.cs index bfbed62..2c4e8ce 100644 --- a/src/IFoxCAD.Cad/ExtensionMethod/CollectionEx.cs +++ b/src/IFoxCAD.Cad/ExtensionMethod/CollectionEx.cs @@ -71,33 +71,59 @@ public static List ToList(this ObjectIdCollection ids) /// - /// 遍历集合的迭代器,执行action委托 + /// 遍历集合,执行委托 /// /// 集合值的类型 /// 集合 - /// 要运行的委托 + /// 委托 public static void ForEach(this IEnumerable source, Action action) { + if (action == null) + throw new ArgumentNullException(nameof(action)); + foreach (var element in source) - { - action?.Invoke(element); - } + action.Invoke(element); } /// - /// 同时遍历集合索引和值的迭代器,执行action委托 + /// 遍历集合,执行委托
    + /// 输出索引值 ///
    /// 集合值的类型 /// 集合 - /// 要运行的委托 + /// 委托 public static void ForEach(this IEnumerable source, Action action) { + if (action == null) + throw new ArgumentNullException(nameof(action)); + int i = 0; - foreach (var item in source) + foreach (var element in source) { - action?.Invoke(i, item); + action.Invoke(i, element); i++; } + } + /// + /// 遍历集合,执行委托
    + /// 输出索引值,允许循环中断 + ///
    + /// 集合值的类型 + /// 集合 + /// 委托 + public static void ForEach(this IEnumerable source, Action action) + { + if (action == null) + throw new ArgumentNullException(nameof(action)); + LoopState state = new();/*这种方式比Action改Func更友好*/ + int i = 0; + foreach (var element in source) + { + action.Invoke(i, element, state); + if (!state.IsRun) + break; + i++; + } } @@ -123,31 +149,31 @@ public static bool Contains(this KeywordCollection collection, string name, switch (keywordName) { case KeywordName.GlobalName: - for (int i = 0; i < collection.Count; i++) - if (collection[i].GlobalName == name) - { - contains = true; - break; - } - break; + for (int i = 0; i < collection.Count; i++) + if (collection[i].GlobalName == name) + { + contains = true; + break; + } + break; case KeywordName.LocalName: - for (int i = 0; i < collection.Count; i++) - if (collection[i].LocalName == name) - { - contains = true; - break; - } - break; + for (int i = 0; i < collection.Count; i++) + if (collection[i].LocalName == name) + { + contains = true; + break; + } + break; case KeywordName.DisplayName: - for (int i = 0; i < collection.Count; i++) - if (collection[i].DisplayName == name) - { - contains = true; - break; - } - break; + for (int i = 0; i < collection.Count; i++) + if (collection[i].DisplayName == name) + { + contains = true; + break; + } + break; default: - break; + break; } return contains; } @@ -193,6 +219,6 @@ public static List GetValues(this IdMapping idmap) foreach (IdPair item in idmap) ids.Add(item.Value); return ids; - } + } #endregion -} +} \ No newline at end of file diff --git a/src/IFoxCAD.Cad/ExtensionMethod/CurveEx.cs b/src/IFoxCAD.Cad/ExtensionMethod/CurveEx.cs index be70f0a..41aad25 100644 --- a/src/IFoxCAD.Cad/ExtensionMethod/CurveEx.cs +++ b/src/IFoxCAD.Cad/ExtensionMethod/CurveEx.cs @@ -114,7 +114,7 @@ public static IEnumerable GetAllCycle(this IEnumerable curves) { #if NET35 graph.AddEdge(curve.ToCurve3d()!); -#else +#else graph.AddEdge(curve.GetGeCurve()); #endif } @@ -467,12 +467,12 @@ public static NurbCurve3d ToCurve3d(this Spline spl) { case Poly2dType.SimplePoly: case Poly2dType.FitCurvePoly: - Polyline pl = new(); - pl.SetDatabaseDefaults(); - pl.ConvertFrom(pl2d, false); - return ToCurve3d(pl); + Polyline pl = new(); + pl.SetDatabaseDefaults(); + pl.ConvertFrom(pl2d, false); + return ToCurve3d(pl); default: - return ToNurbCurve3d(pl2d); + return ToNurbCurve3d(pl2d); } // Polyline pl = new Polyline(); @@ -491,13 +491,13 @@ public static NurbCurve3d ToCurve3d(this Spline spl) { case Poly2dType.SimplePoly: case Poly2dType.FitCurvePoly: - Polyline pl = new(); - pl.SetDatabaseDefaults(); - pl.ConvertFrom(pl2d, false); - return ToNurbCurve3d(pl); + Polyline pl = new(); + pl.SetDatabaseDefaults(); + pl.ConvertFrom(pl2d, false); + return ToNurbCurve3d(pl); default: - return ToCurve3d(pl2d.Spline); + return ToCurve3d(pl2d.Spline); } } @@ -578,15 +578,15 @@ public static CompositeCurve3d ToCurve3d(this Polyline pl) switch (pl.GetSegmentType(i)) { case SegmentType.Line: - c3ds.Add(pl.GetLineSegmentAt(i)); - break; + c3ds.Add(pl.GetLineSegmentAt(i)); + break; case SegmentType.Arc: - c3ds.Add(pl.GetArcSegmentAt(i)); - break; + c3ds.Add(pl.GetArcSegmentAt(i)); + break; default: - break; + break; } } return new CompositeCurve3d(c3ds.ToArray()); @@ -606,15 +606,15 @@ public static CompositeCurve3d ToCurve3d(this Polyline pl) switch (pl.GetSegmentType(i)) { case SegmentType.Line: - nc3dtemp = new NurbCurve3d(pl.GetLineSegmentAt(i)); - break; + nc3dtemp = new NurbCurve3d(pl.GetLineSegmentAt(i)); + break; case SegmentType.Arc: - nc3dtemp = pl.GetArcSegmentAt(i).ToNurbCurve3d(); - break; + nc3dtemp = pl.GetArcSegmentAt(i).ToNurbCurve3d(); + break; default: - break; + break; } if (nc3d is null) { diff --git a/src/IFoxCAD.Cad/ExtensionMethod/EditorEx.cs b/src/IFoxCAD.Cad/ExtensionMethod/EditorEx.cs index eaca60b..8e40ebf 100644 --- a/src/IFoxCAD.Cad/ExtensionMethod/EditorEx.cs +++ b/src/IFoxCAD.Cad/ExtensionMethod/EditorEx.cs @@ -70,6 +70,7 @@ public static SelectionSet SelectByLineWeight(this Editor editor, LineWeight lin pso.RejectPaperspaceViewport = mode.Contains(":V"); pso.AllowSubSelections = mode.Contains("-A"); pso.ForceSubSelections = mode.Contains("-F"); + } if (messages is not null) { @@ -88,6 +89,7 @@ public static SelectionSet SelectByLineWeight(this Editor editor, LineWeight lin if (keywords.ContainsKey(e.Input)) keywords[e.Input].Invoke(); }; + } try { @@ -97,7 +99,7 @@ public static SelectionSet SelectByLineWeight(this Editor editor, LineWeight lin ss = editor.GetSelection(pso); } catch (Exception e) - { + { editor.WriteMessage($"\nKey is {e.Message}"); } return ss; @@ -235,14 +237,14 @@ public static void SsgetAddKeys(this PromptSelectionOptions pso, // Env.Editor.SelectionAdded += SelectTest_SelectionAdded; // // 初始化坐标系 // Env.Editor.CurrentUserCoordinateSystem = Matrix3d.Identity; - + // // 创建过滤器 // var sf = new OpEqual(0, "arc"); // var pso = new PromptSelectionOptions // { // MessageForAdding = "\n请选择对象:" // }; - + // pso.Keywords.Add("Z"); // pso.Keywords.Add("X"); // pso.Keywords.Add("Q"); @@ -253,8 +255,8 @@ public static void SsgetAddKeys(this PromptSelectionOptions pso, // // 用户选择 // var psr = Env.Editor.GetSelection(pso, sf); // // 处理代码 - - + + // } // catch (Exception ex)// 捕获关键字 // { @@ -273,7 +275,7 @@ public static void SsgetAddKeys(this PromptSelectionOptions pso, // // 关闭选中事件 // Env.Editor.SelectionAdded -= SelectTest_SelectionAdded; // } - + // /// // /// 即时选择 // /// @@ -288,6 +290,8 @@ public static void SsgetAddKeys(this PromptSelectionOptions pso, // // 处理代码 // for (int i = 0; i < e.AddedObjects.Count; i++) // { + + // // 处理完移除已处理的对象 // e.Remove(i); // } @@ -295,7 +299,7 @@ public static void SsgetAddKeys(this PromptSelectionOptions pso, // // 激活选中事件 // Env.Editor.SelectionAdded += SelectTest_SelectionAdded; // } - + // /// // /// 关键字响应 // /// @@ -314,7 +318,7 @@ public static void SsgetAddKeys(this PromptSelectionOptions pso, // { // break; // } - + // case "Q": // { // break; @@ -322,7 +326,7 @@ public static void SsgetAddKeys(this PromptSelectionOptions pso, // } // // 抛出异常,用于更新提示信息 // throw new ArgumentException("XuError"); - // } + // } // #endregion #endregion @@ -489,6 +493,7 @@ public static bool Acceptable() /// public static List GetLines(IEnumerable pnts, bool isClosed) { + var itor = pnts.GetEnumerator(); if (!itor.MoveNext()) return new List(); @@ -707,55 +712,55 @@ public static Matrix3d GetMatrix(this Editor editor, CoordinateSystemCode from, switch (from) { case CoordinateSystemCode.Wcs: - switch (to) - { - case CoordinateSystemCode.Ucs: - return editor.GetMatrixFromWcsToUcs(); + switch (to) + { + case CoordinateSystemCode.Ucs: + return editor.GetMatrixFromWcsToUcs(); - case CoordinateSystemCode.MDcs: - return editor.GetMatrixFromMDcsToWcs(); + case CoordinateSystemCode.MDcs: + return editor.GetMatrixFromMDcsToWcs(); - case CoordinateSystemCode.PDcs: - throw new Exception("To be used only with DCS...ErrorStatus.InvalidInput"); - } - break; + case CoordinateSystemCode.PDcs: + throw new Exception("To be used only with DCS...ErrorStatus.InvalidInput"); + } + break; case CoordinateSystemCode.Ucs: - switch (to) - { - case CoordinateSystemCode.Wcs: - return editor.GetMatrixFromUcsToWcs(); + switch (to) + { + case CoordinateSystemCode.Wcs: + return editor.GetMatrixFromUcsToWcs(); - case CoordinateSystemCode.MDcs: - return editor.GetMatrixFromUcsToWcs() * editor.GetMatrixFromWcsToMDcs(); + case CoordinateSystemCode.MDcs: + return editor.GetMatrixFromUcsToWcs() * editor.GetMatrixFromWcsToMDcs(); - case CoordinateSystemCode.PDcs: - throw new Exception("To be used only with DCS... ErrorStatus.InvalidInput"); - } - break; + case CoordinateSystemCode.PDcs: + throw new Exception("To be used only with DCS... ErrorStatus.InvalidInput"); + } + break; case CoordinateSystemCode.MDcs: - switch (to) - { - case CoordinateSystemCode.Wcs: - return editor.GetMatrixFromMDcsToWcs(); + switch (to) + { + case CoordinateSystemCode.Wcs: + return editor.GetMatrixFromMDcsToWcs(); - case CoordinateSystemCode.Ucs: - return editor.GetMatrixFromMDcsToWcs() * editor.GetMatrixFromWcsToUcs(); + case CoordinateSystemCode.Ucs: + return editor.GetMatrixFromMDcsToWcs() * editor.GetMatrixFromWcsToUcs(); - case CoordinateSystemCode.PDcs: - return editor.GetMatrixFromMDcsToPDcs(); - } - break; + case CoordinateSystemCode.PDcs: + return editor.GetMatrixFromMDcsToPDcs(); + } + break; case CoordinateSystemCode.PDcs: - switch (to) - { - case CoordinateSystemCode.Wcs: - throw new Exception("To be used only with DCS... ErrorStatus.InvalidInput"); - case CoordinateSystemCode.Ucs: - throw new Exception("To be used only with DCS... ErrorStatus.InvalidInput"); - case CoordinateSystemCode.MDcs: - return editor.GetMatrixFromPDcsToMDcs(); - } - break; + switch (to) + { + case CoordinateSystemCode.Wcs: + throw new Exception("To be used only with DCS... ErrorStatus.InvalidInput"); + case CoordinateSystemCode.Ucs: + throw new Exception("To be used only with DCS... ErrorStatus.InvalidInput"); + case CoordinateSystemCode.MDcs: + return editor.GetMatrixFromPDcsToMDcs(); + } + break; } return Matrix3d.Identity; #else @@ -1052,7 +1057,7 @@ public enum RunLispFlag : byte * } * 调用方式: * (command "CmdTest_RunLisp1") - * bug说明: + * bug说明: * AcedEvaluateLisp接口在高版本调用时候没有运行成功,使得 !abc 没有值 * 经过测试,cad08调用成功,此bug与CommandFlags无关 * 解决方案: @@ -1082,4 +1087,4 @@ public enum RunLispFlag : byte return null; } #endregion -} \ No newline at end of file +} diff --git a/src/IFoxCAD.Cad/ExtensionMethod/EntityEx.cs b/src/IFoxCAD.Cad/ExtensionMethod/EntityEx.cs index 23ac2e4..42de3f4 100644 --- a/src/IFoxCAD.Cad/ExtensionMethod/EntityEx.cs +++ b/src/IFoxCAD.Cad/ExtensionMethod/EntityEx.cs @@ -428,4 +428,4 @@ public static BoundingInfo GetBoundingBoxEx(this Entity ent) return EntityBoundingInfo.GetBoundingInfo(ent); } #endregion -} \ No newline at end of file +} diff --git a/src/IFoxCAD.Cad/ExtensionMethod/Filer/DwgFiler.cs b/src/IFoxCAD.Cad/ExtensionMethod/Filer/DwgFiler.cs new file mode 100644 index 0000000..a412edd --- /dev/null +++ b/src/IFoxCAD.Cad/ExtensionMethod/Filer/DwgFiler.cs @@ -0,0 +1,538 @@ +namespace IFoxCAD.Cad; +/* + Arx自定义实体类,加 读函数(assertReadEnabled)和写函数(assertWriteEnabled) + + 所有属性位置都不要改动位置,因为涉及序列化 + [Serializable] 设置类 可以序列化 + [Newtonsoft.Json.JsonIgnore] 设置成员 不可序列化 +*/ + + +[Serializable] +public class DwgFiler : Acad_DwgFiler +{ +#if NET35 + public int m_Position; +#else + public long m_Position; +#endif + public FilerType m_FilerType; + public Acad_ErrorStatus m_FilerStatus; + public List AddressList; + public int AddressListPt = 0; + public List BinaryChunkList; + public int BinaryChunkListPt = 0; + public List BooleanList; + public int BooleanListPt = 0; + public List ByteList; + public int ByteListPt = 0; + public List BytesList; + public int BytesListPt = 0; + public List DoubleList; + public int DoubleListPt = 0; + public List HandleList; + public int HandleListPt = 0; + [NonSerialized] + public List HardOwnershipIdList; + public int HardOwnershipIdListPt = 0; + [NonSerialized] + public List HardPointerIdList; + public int HardPointerIdListPt = 0; + public List Int16List; + public int Int16ListPt = 0; + public List Int32List; + public int Int32ListPt = 0; +#if !NET35 + public List Int64List; + public int Int64ListPt = 0; +#endif + public List Point2dList; + public int Point2dListPt = 0; + public List Point3dList; + public int Point3dListPt = 0; + public List Scale3dList; + public int Scale3dListPt = 0; + [NonSerialized] + public List SoftOwnershipIdList; + public int SoftOwnershipIdListPt = 0; + [NonSerialized] + public List SoftPointerIdList; + public int SoftPointerIdListPt = 0; + public List StringList; + public int StringListPt = 0; + public List Uint16List; + public int uint16ListPt = 0; + public List Uint32List; + public int uint32ListPt = 0; +#if !NET35 + public List Uint64List; + public int uint64ListPt = 0; +#endif + public List Vector2dList; + public int Vector2dListPt = 0; + public List Vector3dList; + public int Vector3dListPt = 0; + + public DwgFiler() + { + m_Position = 0; + m_FilerType = FilerType.CopyFiler; + m_FilerStatus = Acad_ErrorStatus.OK; + AddressList = new List(); + BinaryChunkList = new List(); + BooleanList = new List(); + ByteList = new List(); + BytesList = new List(); + DoubleList = new List(); + HandleList = new List(); + HardOwnershipIdList = new List(); + HardPointerIdList = new List(); + Int16List = new List(); + Int32List = new List(); +#if !NET35 + Int64List = new List(); +#endif + Point2dList = new List(); + Point3dList = new List(); + Scale3dList = new List(); + SoftOwnershipIdList = new List(); + SoftPointerIdList = new List(); + StringList = new List(); + Uint16List = new List(); + Uint32List = new List(); +#if !NET35 + Uint64List = new List(); +#endif + Vector2dList = new List(); + Vector3dList = new List(); + } + +#if NET35 + public override int Position => m_Position; +#else + public override long Position => m_Position; +#endif + public override FilerType FilerType + { + get { return this.m_FilerType; } + } + + public override Acad_ErrorStatus FilerStatus + { + get { return m_FilerStatus; } + set { m_FilerStatus = value; } + } + + public override IntPtr ReadAddress() + { + if (AddressList.Count == 0) + return new IntPtr(); + return AddressList[AddressListPt++]; + } + + public override byte[]? ReadBinaryChunk() + { + if (BinaryChunkList.Count == 0) + return null; + return BinaryChunkList[BinaryChunkListPt++]; + } + + public override bool ReadBoolean() + { + if (BooleanList.Count == 0) + return false; + return BooleanList[BooleanListPt++]; + } + + public override byte ReadByte() + { + if (ByteList.Count == 0) + return 0; + return ByteList[ByteListPt++]; + } + + public override void ReadBytes(byte[] value) + { + if (ByteList.Count == 0) + return; + value = new byte[BytesList[BytesListPt].Length]; + BytesList[BytesListPt++].CopyTo(value, 0); + } + + public override double ReadDouble() + { + if (DoubleList.Count == 0) + return 0; + return DoubleList[DoubleListPt++]; + } + + public override Handle ReadHandle() + { + if (HandleList.Count == 0) + return new Handle(); + return HandleList[HandleListPt++]; + } + + public override ObjectId ReadHardOwnershipId() + { + if (HardOwnershipIdList.Count == 0) + return new ObjectId(); + return HardOwnershipIdList[HardOwnershipIdListPt++]; + } + + public override ObjectId ReadHardPointerId() + { + if (HardPointerIdList.Count == 0) + return new ObjectId(); + return HardPointerIdList[HardPointerIdListPt++]; + } + + public override short ReadInt16() + { + if (Int16List.Count == 0) + return 0; + return Int16List[Int16ListPt++]; + } + + public override int ReadInt32() + { + if (Int32List.Count == 0) + return 0; + return Int32List[Int32ListPt++]; + } + +#if !NET35 + public override long ReadInt64() + { + if (Int64List.Count == 0) + return 0; + return Int64List[Int64ListPt++]; + } +#endif + + public override Point2d ReadPoint2d() + { + if (Point2dList.Count == 0) + return new Point2d(); + return Point2dList[Point2dListPt++]; + } + + public override Point3d ReadPoint3d() + { + if (Point3dList.Count == 0) + return new Point3d(); + return Point3dList[Point3dListPt++]; + } + + public override Scale3d ReadScale3d() + { + if (Scale3dList.Count == 0) + return new Scale3d(); + return Scale3dList[Scale3dListPt++]; + } + + public override ObjectId ReadSoftOwnershipId() + { + if (SoftOwnershipIdList.Count == 0) + return new ObjectId(); + return SoftOwnershipIdList[SoftOwnershipIdListPt++]; + } + + public override ObjectId ReadSoftPointerId() + { + if (SoftPointerIdList.Count == 0) + return new ObjectId(); + return SoftPointerIdList[SoftPointerIdListPt++]; + } + + public override string? ReadString() + { + if (StringList.Count == 0) + return null; + return StringList[StringListPt++]; + } + + public override ushort ReadUInt16() + { + if (Uint16List.Count == 0) + return 0; + return Uint16List[uint16ListPt++]; + } + + public override uint ReadUInt32() + { + if (Uint32List.Count == 0) + return 0; + return Uint32List[uint32ListPt++]; + } + +#if !NET35 + public override ulong ReadUInt64() + { + if (Uint64List.Count == 0) + return 0; + return Uint64List[uint64ListPt++]; + } +#endif + + public override Vector2d ReadVector2d() + { + if (Vector2dList.Count == 0) + return new Vector2d(); + return Vector2dList[Vector2dListPt++]; + } + + public override Vector3d ReadVector3d() + { + if (Vector3dList.Count == 0) + return new Vector3d(); + return Vector3dList[Vector3dListPt++]; + } + + public override void ResetFilerStatus() + { + AddressList.Clear(); + AddressListPt = 0; + BinaryChunkList.Clear(); + BinaryChunkListPt = 0; + BooleanList.Clear(); + BooleanListPt = 0; + ByteList.Clear(); + ByteListPt = 0; + BytesList.Clear(); + BytesListPt = 0; + DoubleList.Clear(); + DoubleListPt = 0; + HandleList.Clear(); + HandleListPt = 0; + HardOwnershipIdList.Clear(); + HardOwnershipIdListPt = 0; + HardPointerIdList.Clear(); + HardPointerIdListPt = 0; + Int16List.Clear(); + Int16ListPt = 0; + Int32List.Clear(); + Int32ListPt = 0; +#if !NET35 + Int64List.Clear(); + Int64ListPt = 0; +#endif + Point2dList.Clear(); + Point2dListPt = 0; + Point3dList.Clear(); + Point3dListPt = 0; + Scale3dList.Clear(); + Scale3dListPt = 0; + SoftOwnershipIdList.Clear(); + SoftOwnershipIdListPt = 0; + SoftPointerIdList.Clear(); + SoftPointerIdListPt = 0; + StringList.Clear(); + StringListPt = 0; + Uint16List.Clear(); + uint16ListPt = 0; + Uint32List.Clear(); + uint32ListPt = 0; +#if !NET35 + Uint64List.Clear(); + uint64ListPt = 0; +#endif + Vector2dList.Clear(); + Vector2dListPt = 0; + Vector3dList.Clear(); + Vector3dListPt = 0; + + m_FilerType = FilerType.CopyFiler; + } + + public override void Seek( +#if ac2008 || ac2009 + int +#else + long +#endif + offset, int method) + { + Editor ed = Acap.DocumentManager.MdiActiveDocument.Editor; + ed.WriteMessage(MethodInfo.GetCurrentMethod().Name + " = " + " \n "); + } + + public override void WriteAddress(IntPtr value) + { + AddressList.Add(value); + } + + public override void WriteBinaryChunk(byte[] chunk) + { + BinaryChunkList.Add(chunk); + } + + public override void WriteBoolean(bool value) + { + BooleanList.Add(value); + } + + public override void WriteByte(byte value) + { + ByteList.Add(value); + } + + public override void WriteBytes(byte[] value) + { + BytesList.Add(value); + } + + public override void WriteDouble(double value) + { + DoubleList.Add(value); + } + + public override void WriteHandle(Handle handle) + { + HandleList.Add(handle); + } + + public override void WriteHardOwnershipId(ObjectId value) + { + HardOwnershipIdList.Add(value); + } + + public override void WriteHardPointerId(ObjectId value) + { + HardPointerIdList.Add(value); + } + + public override void WriteInt16(short value) + { + Int16List.Add(value); + } + + public override void WriteInt32(int value) + { + Int32List.Add(value); + } + +#if !NET35 + public override void WriteInt64(long value) + { + Int64List.Add(value); + } +#endif + public override void WritePoint2d(Point2d value) + { + Point2dList.Add(value); + } + + public override void WritePoint3d(Point3d value) + { + Point3dList.Add(value); + } + + public override void WriteScale3d(Scale3d value) + { + Scale3dList.Add(value); + } + + public override void WriteSoftOwnershipId(ObjectId value) + { + SoftOwnershipIdList.Add(value); + } + + public override void WriteSoftPointerId(ObjectId value) + { + SoftPointerIdList.Add(value); + } + + public override void WriteString(string value) + { + StringList.Add(value); + } + + public override void WriteUInt16(ushort value) + { + Uint16List.Add(value); + } + + public override void WriteUInt32(uint value) + { + Uint32List.Add(value); + } + +#if !NET35 + public override void WriteUInt64(ulong value) + { + Uint64List.Add(value); + } +#endif + + public override void WriteVector2d(Vector2d value) + { + Vector2dList.Add(value); + } + + public override void WriteVector3d(Vector3d value) + { + Vector3dList.Add(value); + } + + public override string ToString() + { + int ptCount = AddressListPt + + BinaryChunkListPt + + BooleanListPt + + ByteListPt + + BytesListPt + + DoubleListPt + + HandleListPt + + HardOwnershipIdListPt + + HardPointerIdListPt + + Int16ListPt + + Int32ListPt + +#if !NET35 + Int64ListPt + +#endif + Point2dListPt + + Point3dListPt + + Scale3dListPt + + SoftOwnershipIdListPt + + SoftPointerIdListPt + + StringListPt + + uint16ListPt + + uint32ListPt + +#if !NET35 + uint64ListPt + +#endif + Vector2dListPt + + Vector3dListPt; + + int ltCount = AddressList.Count + + BinaryChunkList.Count + + BooleanList.Count + + ByteList.Count + + BytesList.Count + + DoubleList.Count + + HandleList.Count + + HardOwnershipIdList.Count + + HardPointerIdList.Count + + Int16List.Count + + Int32List.Count + +#if !NET35 + Int64List.Count + +#endif + Point2dList.Count + + Point3dList.Count + + Scale3dList.Count + + SoftOwnershipIdList.Count + + SoftPointerIdList.Count + + StringList.Count + + Uint16List.Count + + Uint32List.Count + +#if !NET35 + Uint64List.Count + +#endif + Vector2dList.Count + + Vector3dList.Count; + + return "\nDataIn::" + ptCount + "\nDataOut::" + ltCount; + } +} \ No newline at end of file diff --git a/src/IFoxCAD.Cad/ExtensionMethod/Filer/DwgFilerEx.cs b/src/IFoxCAD.Cad/ExtensionMethod/Filer/DwgFilerEx.cs new file mode 100644 index 0000000..b7a4d0e --- /dev/null +++ b/src/IFoxCAD.Cad/ExtensionMethod/Filer/DwgFilerEx.cs @@ -0,0 +1,83 @@ +namespace IFoxCAD.Cad; + +/// +/// Dwg序列化 +/// +public class DwgFilerEx +{ + #region 成员 + DBObject? _entity; + public DwgFiler DwgFiler { get; private set; } + #endregion + + #region 构造 + /// + /// Dwg序列化 + /// + public DwgFilerEx(DwgFiler? acad_DwgFiler = null) + { + if (acad_DwgFiler == null) + acad_DwgFiler = new(); + DwgFiler = acad_DwgFiler; + } + + /// + /// Dwg序列化 + /// + public DwgFilerEx(DBObject entity) : this() + { + DwgOut(entity); + } + + #endregion + + #region 方法 + public void DwgOut(DBObject entity) + { + _entity = entity; + _entity.DwgOut(DwgFiler); + } + + public void DwgIn() + { + _entity?.DwgIn(DwgFiler); + } + + /// + /// 反序列化 + /// + /// + /// + public DwgFilerEx? DeserializeObject(string json) + { + throw new ArgumentException(); + //return JsonConvert.DeserializeObject(json);// 反序列化*字符串转类 + } + + /// + /// 序列化 + /// + /// + public string SerializeObject() + { + throw new ArgumentException(); + //return JsonConvert.SerializeObject(DwgFiler, Formatting.Indented); // 序列化*类转字符串 + } + + public override string ToString() + { + // 替换中括号以外的字符串,替换逗号为换行符 https://bbs.csdn.net/topics/370134253 + //var str = SerializeObject(); + //str = str.Substring(1, str.Length - 2); + //str = Regex.Replace(str, @"(?:,)(?![^\[]*?\])", "\r\n"); + //return str; + + return DwgFiler.ToString(); + } + + public static implicit operator Acad_DwgFiler(DwgFilerEx df) + { + return df.DwgFiler; + } + #endregion +} \ No newline at end of file diff --git a/src/IFoxCAD.Cad/ExtensionMethod/Filer/DxfFiler.cs b/src/IFoxCAD.Cad/ExtensionMethod/Filer/DxfFiler.cs new file mode 100644 index 0000000..ef9c42d --- /dev/null +++ b/src/IFoxCAD.Cad/ExtensionMethod/Filer/DxfFiler.cs @@ -0,0 +1,219 @@ +namespace IFoxCAD.Cad; + +/* 此处暂未完成,无任何测试,尚且不知道怎么用 */ +using System.Runtime.Remoting; + +public class DxfFiler : Acad_DxfFiler +{ + public DxfFiler(IntPtr unmanagedPointer, [MarshalAs(UnmanagedType.U1)] bool autoDelete) : base(unmanagedPointer, autoDelete) + { + } + + public override bool IsModifyingExistingObject => base.IsModifyingExistingObject; + + public override double Thickness => base.Thickness; + + public override double Elevation => base.Elevation; + + public override bool AtEmbeddedObjectStart => base.AtEmbeddedObjectStart; + + public override bool AtEndOfObject => base.AtEndOfObject; + + public override bool AtExtendedData => base.AtExtendedData; + + public override bool AtEndOfFile => base.AtEndOfFile; + + public override int Precision { get => base.Precision; set => base.Precision = value; } + + public override string ErrorMessage => base.ErrorMessage; + + public override bool AtSubclassData(string value) + { + return base.AtSubclassData(value); + } + + public override object Clone() + { + return base.Clone(); + } + + public override void CopyFrom(RXObject source) + { + base.CopyFrom(source); + } + + public override ObjRef CreateObjRef(Type requestedType) + { + return base.CreateObjRef(requestedType); + } + + public override bool Equals(object obj) + { + return base.Equals(obj); + } + + public override void FilerStatus() + { + base.FilerStatus(); + } + + public override int GetHashCode() + { + return base.GetHashCode(); + } + + public override void HaltAtClassBoundaries(bool value) + { + base.HaltAtClassBoundaries(value); + } + + public override object InitializeLifetimeService() + { + return base.InitializeLifetimeService(); + } + + public override void PushBackItem() + { + base.PushBackItem(); + } + + public override ResultBuffer ReadResultBuffer() + { + return base.ReadResultBuffer(); + } + + public override void ResetFilerStatus() + { + base.ResetFilerStatus(); + } + + public override int RewindFiler() + { + return base.RewindFiler(); + } + + public override void SetError(string format, params string[] values) + { + base.SetError(format, values); + } + + public override void SetError(Acad_ErrorStatus value, string format, params string[] values) + { + base.SetError(value, format, values); + } + + public override string ToString() + { + return base.ToString(); + } + + public override void WriteBool(DxfCode opCode, bool value) + { + base.WriteBool(opCode, value); + } + + public override void WriteBoolean(DxfCode opCode, bool value) + { + base.WriteBoolean(opCode, value); + } + + public override void WriteByte(DxfCode opCode, byte value) + { + base.WriteByte(opCode, value); + } + + public override void WriteBytes(DxfCode opCode, byte[] chunk) + { + base.WriteBytes(opCode, chunk); + } + + public override void WriteDouble(DxfCode opCode, double value, int precision) + { + base.WriteDouble(opCode, value, precision); + } + + public override void WriteEmbeddedObjectStart() + { + base.WriteEmbeddedObjectStart(); + } + + public override void WriteHandle(DxfCode opCode, Handle value) + { + base.WriteHandle(opCode, value); + } + + public override void WriteInt16(DxfCode opCode, short value) + { + base.WriteInt16(opCode, value); + } + + public override void WriteInt32(DxfCode opCode, int value) + { + base.WriteInt32(opCode, value); + } + + public override void WriteObjectId(DxfCode opCode, ObjectId value) + { + base.WriteObjectId(opCode, value); + } + + public override void WritePoint2d(DxfCode opCode, Point2d value, int precision) + { + base.WritePoint2d(opCode, value, precision); + } + + public override void WritePoint3d(DxfCode opCode, Point3d value, int precision) + { + base.WritePoint3d(opCode, value, precision); + } + + public override void WriteResultBuffer(ResultBuffer buffer) + { + base.WriteResultBuffer(buffer); + } + + public override void WriteScale3d(DxfCode opCode, Scale3d value, int precision) + { + base.WriteScale3d(opCode, value, precision); + } + + public override void WriteString(DxfCode opCode, string value) + { + base.WriteString(opCode, value); + } + + public override void WriteUInt16(DxfCode opCode, ushort value) + { + base.WriteUInt16(opCode, value); + } + + public override void WriteUInt32(DxfCode opCode, uint value) + { + base.WriteUInt32(opCode, value); + } + + public override void WriteVector2d(DxfCode opCode, Vector2d value, int precision) + { + base.WriteVector2d(opCode, value, precision); + } + + public override void WriteVector3d(DxfCode opCode, Vector3d value, int precision) + { + base.WriteVector3d(opCode, value, precision); + } + + public override void WriteXDataStart() + { + base.WriteXDataStart(); + } + + protected override void DeleteUnmanagedObject() + { + base.DeleteUnmanagedObject(); + } + + protected override void Dispose(bool disposing) + { + base.Dispose(disposing); + } +} \ No newline at end of file diff --git a/src/IFoxCAD.Cad/ExtensionMethod/ObjectIdEx.cs b/src/IFoxCAD.Cad/ExtensionMethod/ObjectIdEx.cs index d0ebf72..92e8b0f 100644 --- a/src/IFoxCAD.Cad/ExtensionMethod/ObjectIdEx.cs +++ b/src/IFoxCAD.Cad/ExtensionMethod/ObjectIdEx.cs @@ -32,9 +32,9 @@ public static class ObjectIdEx /// 打开模式 /// 打开删除对象 /// 指定类型对象集合 - public static IEnumerable GetObject(this IEnumerable ids, + public static IEnumerable GetObject(this IEnumerable ids, OpenMode mode = OpenMode.ForRead, - bool openErased = false, + bool openErased = false, Transaction? tr = default) where T : DBObject { return ids.Select(id => id.GetObject(mode, openErased, tr)); @@ -89,4 +89,4 @@ public static void Erase(this ObjectId id) // Env.Editor.Regen(); } } -} \ No newline at end of file +} diff --git a/src/IFoxCAD.Cad/ExtensionMethod/SymbolTableRecordEx.cs b/src/IFoxCAD.Cad/ExtensionMethod/SymbolTableRecordEx.cs index 5a6f4cc..4a5737e 100644 --- a/src/IFoxCAD.Cad/ExtensionMethod/SymbolTableRecordEx.cs +++ b/src/IFoxCAD.Cad/ExtensionMethod/SymbolTableRecordEx.cs @@ -23,11 +23,11 @@ public static class SymbolTableRecordEx /// /// 图元id集合,注意所有成员都要在同一个空间中 /// 克隆后的id词典 - public static IdMapping DeepClone(this BlockTableRecord btr, ObjectIdCollection objIds) + public static IdMapping DeepCloneEx(this BlockTableRecord btr, ObjectIdCollection objIds) { if (objIds is null || objIds.Count == 0) throw new ArgumentNullException(nameof(objIds)); - + var db = objIds[0].Database; IdMapping mapping = new(); using (btr.ForWrite()) @@ -39,7 +39,7 @@ public static IdMapping DeepClone(this BlockTableRecord btr, ObjectIdCollection // 不在此提取,为了此函数被高频调用 // 获取克隆键值对(旧块名,新块名) // foreach (ObjectId item in blockIds) - // result.Add(mapping[item].Value); + // result.Add(mapping[item].Value); } catch (System.Exception e) { @@ -289,7 +289,6 @@ public static ObjectId AddPline(this BlockTableRecord btr, Action? action = default, Transaction? trans = default) { - Polyline pl = new(); pl.SetDatabaseDefaults(); pts.ForEach((i, vertex) => { @@ -461,4 +460,4 @@ public static ObjectId InsertBlock(this BlockTableRecord blockTableRecord, Point #endregion #endregion -} +} \ No newline at end of file diff --git "a/src/IFoxCAD.Cad/ExtensionMethod/\346\226\260\345\273\272\345\241\253\345\205\205/HatchConverter.cs" "b/src/IFoxCAD.Cad/ExtensionMethod/\346\226\260\345\273\272\345\241\253\345\205\205/HatchConverter.cs" index 09af1d6..27d4bfe 100644 --- "a/src/IFoxCAD.Cad/ExtensionMethod/\346\226\260\345\273\272\345\241\253\345\205\205/HatchConverter.cs" +++ "b/src/IFoxCAD.Cad/ExtensionMethod/\346\226\260\345\273\272\345\241\253\345\205\205/HatchConverter.cs" @@ -314,7 +314,7 @@ public ObjectId CreateBoundarysAndHatchToMsPs(BlockTableRecord btrOfAddEntitySpa * 那么它的平移后的基点在哪里呢? */ - var newHatchId = btrOfAddEntitySpace.DeepClone(new ObjectIdCollection(new ObjectId[] { OldHatchId })).GetValues()[0]; + var newHatchId = btrOfAddEntitySpace.DeepCloneEx(new ObjectIdCollection(new ObjectId[] { OldHatchId })).GetValues()[0]; trans ??= DBTrans.Top.Transaction; var hatchEnt = trans.GetObject(newHatchId, OpenMode.ForWrite) as Hatch; if (hatchEnt != null) diff --git a/src/IFoxCAD.Cad/GlobalUsings.cs b/src/IFoxCAD.Cad/GlobalUsings.cs index 60ba3eb..2430170 100644 --- a/src/IFoxCAD.Cad/GlobalUsings.cs +++ b/src/IFoxCAD.Cad/GlobalUsings.cs @@ -10,9 +10,13 @@ global using Microsoft.Win32; global using System.ComponentModel; global using System.Runtime.InteropServices; +global using System.Collections.Specialized; global using Exception = System.Exception; +global using Registry = Microsoft.Win32.Registry; +global using RegistryKey = Microsoft.Win32.RegistryKey; + /// autocad 引用 global using Autodesk.AutoCAD.ApplicationServices; global using Autodesk.AutoCAD.EditorInput; @@ -21,6 +25,7 @@ global using Autodesk.AutoCAD.Geometry; global using Autodesk.AutoCAD.Runtime; global using Acap = Autodesk.AutoCAD.ApplicationServices.Application; +global using Acgi = Autodesk.AutoCAD.GraphicsInterface; global using Autodesk.AutoCAD.DatabaseServices.Filters; global using Autodesk.AutoCAD; @@ -28,16 +33,13 @@ // jig命名空间会引起Viewport/Polyline等等重义,最好逐个引入 using Autodesk.AutoCAD.GraphicsInterface global using WorldDraw = Autodesk.AutoCAD.GraphicsInterface.WorldDraw; global using Manager = Autodesk.AutoCAD.GraphicsSystem.Manager; - global using Group = Autodesk.AutoCAD.DatabaseServices.Group; global using Viewport = Autodesk.AutoCAD.DatabaseServices.Viewport; - -global using System.Collections.Specialized; -global using Registry = Microsoft.Win32.Registry; -global using RegistryKey = Microsoft.Win32.RegistryKey; - global using Autodesk.AutoCAD.GraphicsInterface; global using Polyline = Autodesk.AutoCAD.DatabaseServices.Polyline; +global using Acad_DwgFiler = Autodesk.AutoCAD.DatabaseServices.DwgFiler; +global using Acad_DxfFiler = Autodesk.AutoCAD.DatabaseServices.DxfFiler; +global using Acad_ErrorStatus = Autodesk.AutoCAD.Runtime.ErrorStatus; /// ifoxcad.basal 引用 global using IFoxCAD.Basal; \ No newline at end of file diff --git a/src/IFoxCAD.Cad/Runtime/AutoRegAssem.cs b/src/IFoxCAD.Cad/Runtime/AutoRegAssem.cs index cfc3d17..fd3bacc 100644 --- a/src/IFoxCAD.Cad/Runtime/AutoRegAssem.cs +++ b/src/IFoxCAD.Cad/Runtime/AutoRegAssem.cs @@ -165,9 +165,7 @@ public void RegApp() // 这里的是不会自动执行的 public void Initialize() { } - public void Terminate() - { - } + public void Terminate() { } ~AutoRegAssem() { diff --git a/src/IFoxCAD.Cad/Runtime/SymbolTable.cs b/src/IFoxCAD.Cad/Runtime/SymbolTable.cs index 8f96bb9..8e0efba 100644 --- a/src/IFoxCAD.Cad/Runtime/SymbolTable.cs +++ b/src/IFoxCAD.Cad/Runtime/SymbolTable.cs @@ -342,12 +342,10 @@ public void ForEach(Action action, var record = GetRecord(id, openMode); if (record is not null) action(record, state); - if (state.IsBreak) + if (!state.IsRun) break; } } - - #endregion #region IEnumerable 成员 diff --git a/src/IFoxCAD.Cad/Runtime/Utils.cs b/src/IFoxCAD.Cad/Runtime/Utils.cs index 28c47c2..6c668da 100644 --- a/src/IFoxCAD.Cad/Runtime/Utils.cs +++ b/src/IFoxCAD.Cad/Runtime/Utils.cs @@ -48,19 +48,19 @@ static int GetAcDbObjectId(IntPtr db, Handle handle, out ObjectId id, bool creat switch (Acap.Version.Major) { case 17: - { - if (IntPtr.Size == 4) - return getAcDbObjectId17x32(db, out id, createIfNotFound, ref handle, reserved); - else - return getAcDbObjectId17x64(db, out id, createIfNotFound, ref handle, reserved); - } + { + if (IntPtr.Size == 4) + return getAcDbObjectId17x32(db, out id, createIfNotFound, ref handle, reserved); + else + return getAcDbObjectId17x64(db, out id, createIfNotFound, ref handle, reserved); + } case 18: - { - if (IntPtr.Size == 4) - return getAcDbObjectId18x32(db, out id, createIfNotFound, ref handle, reserved); - else - return getAcDbObjectId18x64(db, out id, createIfNotFound, ref handle, reserved); - } + { + if (IntPtr.Size == 4) + return getAcDbObjectId18x32(db, out id, createIfNotFound, ref handle, reserved); + else + return getAcDbObjectId18x64(db, out id, createIfNotFound, ref handle, reserved); + } } return -1; } @@ -93,6 +93,6 @@ public static ObjectId TryGetObjectId(Database db, Handle handle) // var a = Enum.Parse(typeof(DwgVersion), "AC1800"); // Enum.TryParse() // return vernum + 986; - + // } } \ No newline at end of file diff --git a/tests/Test/GlobalUsings.cs b/tests/Test/GlobalUsings.cs index 2896cbf..1e1bc8d 100644 --- a/tests/Test/GlobalUsings.cs +++ b/tests/Test/GlobalUsings.cs @@ -9,7 +9,13 @@ global using System.Text.RegularExpressions; global using Microsoft.Win32; global using System.ComponentModel; -global using System.Runtime.CompilerServices; +global using System.Runtime.InteropServices; +global using System.Collections.Specialized; + +global using Exception = System.Exception; + +global using Registry = Microsoft.Win32.Registry; +global using RegistryKey = Microsoft.Win32.RegistryKey; /// autocad 引用 global using Autodesk.AutoCAD.ApplicationServices; @@ -18,13 +24,27 @@ global using Autodesk.AutoCAD.DatabaseServices; global using Autodesk.AutoCAD.Geometry; global using Autodesk.AutoCAD.Runtime; -global using Acgi = Autodesk.AutoCAD.GraphicsInterface; global using Acap = Autodesk.AutoCAD.ApplicationServices.Application; +global using Acgi = Autodesk.AutoCAD.GraphicsInterface; + +global using Autodesk.AutoCAD.DatabaseServices.Filters; +global using Autodesk.AutoCAD; + +// jig命名空间会引起Viewport/Polyline等等重义,最好逐个引入 using Autodesk.AutoCAD.GraphicsInterface +global using WorldDraw = Autodesk.AutoCAD.GraphicsInterface.WorldDraw; +global using Manager = Autodesk.AutoCAD.GraphicsSystem.Manager; +global using Group = Autodesk.AutoCAD.DatabaseServices.Group; +global using Viewport = Autodesk.AutoCAD.DatabaseServices.Viewport; +global using Autodesk.AutoCAD.GraphicsInterface; +global using Polyline = Autodesk.AutoCAD.DatabaseServices.Polyline; +global using Acad_DwgFiler = Autodesk.AutoCAD.DatabaseServices.DwgFiler; +global using Acad_DxfFiler = Autodesk.AutoCAD.DatabaseServices.DxfFiler; +global using Acad_ErrorStatus = Autodesk.AutoCAD.Runtime.ErrorStatus; -global using Registry = Microsoft.Win32.Registry; -global using RegistryKey = Microsoft.Win32.RegistryKey; /// ifoxcad global using IFoxCAD.Cad; +global using IFoxCAD.Basal; +#if !ac2008 global using IFoxCAD.WPF; -global using IFoxCAD.Basal; \ No newline at end of file +#endif \ No newline at end of file diff --git a/tests/Test/TestCurve.cs b/tests/Test/TestCurve.cs index 4a80837..541029c 100644 --- a/tests/Test/TestCurve.cs +++ b/tests/Test/TestCurve.cs @@ -1,6 +1,4 @@ -using Autodesk.AutoCAD.DatabaseServices; - -namespace Test; +namespace Test; public class TestGraph { diff --git a/tests/Test/TestDwgFilerEx.cs b/tests/Test/TestDwgFilerEx.cs new file mode 100644 index 0000000..c2e4118 --- /dev/null +++ b/tests/Test/TestDwgFilerEx.cs @@ -0,0 +1,156 @@ +namespace Test; + +using DxfFiler = IFoxCAD.Cad.DxfFiler; + +public class CmdTestDwgFilerEx +{ + [CommandMethod(nameof(CmdTest_DwgFilerEx))] + public static void CmdTest_DwgFilerEx() + { + var dm = Acap.DocumentManager; + var doc = dm.MdiActiveDocument; + var db = doc.Database; + var ed = doc.Editor; + ed.WriteMessage("\n****测试,序列化图元"); + + var ssPsr = ed.SelectImplied();// 预选 + if (ssPsr.Status != PromptStatus.OK) + { + ssPsr = ed.GetSelection();// 手选 这里输入al会变成all,无法删除ssget的all关键字 + if (ssPsr.Status != PromptStatus.OK) + return; + } + + using var tr = new DBTrans(); + var ids = ssPsr.Value.GetObjectIds(); + foreach (var id in ids) + { + if (!id.IsOk()) + continue; + var ent = tr.GetObject(id, OpenMode.ForRead); + if (ent is null) + continue; + var dwgFilerEx = new DwgFilerEx(ent); + ed.WriteMessage(Environment.NewLine + dwgFilerEx.ToString()); + } + } + + [CommandMethod(nameof(CmdTest_EntDxfout))] + public static void CmdTest_EntDxfout() + { + var dm = Acap.DocumentManager; + var doc = dm.MdiActiveDocument; + var db = doc.Database; + var ed = doc.Editor; + + // 定义选择集选项 + var pso = new PromptSelectionOptions + { + RejectObjectsOnLockedLayers = true, // 不选择锁定图层对象 + AllowDuplicates = true, // 不允许重复选择 + }; + var ssPsr = ed.GetSelection(pso);// 手选 这里输入al会变成all,无法删除ssget的all关键字 + if (ssPsr.Status != PromptStatus.OK) + return; + + using var tr = new DBTrans(); + var ids = ssPsr.Value.GetObjectIds(); + foreach (var id in ids) + { + if (!id.IsOk()) + continue; + var ent = tr.GetObject(id, OpenMode.ForRead); + if (ent is null) + continue; + // ResultBuffer rbDxf = new(); + var filer = new DxfFiler(ent.UnmanagedObject, true);/// 这里有问题 + ent.DxfOut(filer); + } + } + + + [CommandMethod(nameof(CmdTest_TextOut))] + public static void CmdTest_TextOut() + { + var dm = Acap.DocumentManager; + var doc = dm.MdiActiveDocument; + var db = doc.Database; + var ed = doc.Editor; + +#if true + var peo1 = new PromptEntityOptions(Environment.NewLine + "点选源TCH_WIREDIM2:") + { + AllowObjectOnLockedLayer = false, + AllowNone = false + }; + var gt1 = ed.GetEntity(peo1); + if (gt1.Status != PromptStatus.OK) + return; +#else + var peo2 = new PromptEntityOptions(Environment.NewLine + "点选目标TCH_WIREDIM2:") + { + AllowObjectOnLockedLayer = false, + AllowNone = false + }; + var gt2 = ed.GetEntity(peo2); + if (gt2.Status != PromptStatus.OK) + return; +#endif + + using var tr = new DBTrans(); + var dwgFilerEx = new DwgFilerEx(); + var bText = tr.GetObject(gt1.ObjectId, OpenMode.ForRead); + if (bText is null) + return; + + // DwgFilerEx.StringList[0] = "1@2@3@4@5@6@7@"; + // 复制 TCH_WIREDIM2 不行,TEXT 也不行,直接崩溃。line等线就没事 + bText.DwgOut(dwgFilerEx.DwgFiler); + + int testNum = 1 | 2 | 4 | 8; + + if ((testNum & 1) == 1) + { + // 错误,原地克隆也是不行的,它会生成在了模型中. + var sIds = new List + { + bText.ObjectId + }; + // 克隆到目标块表内 + var newObjs = tr.CurrentSpace + .DeepCloneEx(new ObjectIdCollection(sIds.ToArray())); + var newTexts = newObjs.GetValues().GetObject(); + newTexts.ForEach(nText => { + if (nText == null) + return; + // 通过上面的克隆就已经在块表上面了.所以下面的设置也跟设置到已有图元上一样报错. + nText.UpgradeOpen(); + nText.DwgIn(dwgFilerEx); + tr.CurrentSpace.AddEntity(nText); + nText.DowngradeOpen(); + }); + } + if ((testNum & 2) == 2) + { + // 出错 + // 直接设置 + bText.DwgIn(dwgFilerEx); + } + if ((testNum & 4) == 4) + { + // 出错 + // 此时是内存中对象.... + var nText = (DBText)bText.Clone(); + nText.DwgIn(dwgFilerEx); + tr.CurrentSpace.AddEntity(nText); + } + if ((testNum & 8) == 8) + { + // 新对象相当于克隆,是ok的 + DBText nText = new(); + nText.SetDatabaseDefaults(); + nText.DwgIn(dwgFilerEx); + tr.CurrentSpace.AddEntity(nText); + } + } +} \ No newline at end of file diff --git a/tests/Test/testblock.cs b/tests/Test/testblock.cs index 3fd0c81..8581eba 100644 --- a/tests/Test/testblock.cs +++ b/tests/Test/testblock.cs @@ -260,10 +260,8 @@ public void test_block_ej() mText.Contents = "hahaha"; break; case "$$B": - ; break; default: - ; break; } } -- Gitee From 98225a0901f0d4ae72959cd9889f02827b5e5b4c Mon Sep 17 00:00:00 2001 From: liuqihong <540762622@qq.com> Date: Fri, 16 Sep 2022 00:08:14 +0800 Subject: [PATCH 500/675] =?UTF-8?q?=E6=94=AF=E6=8C=81=E6=B5=A9=E8=BE=B0?= =?UTF-8?q?=E5=92=8C=E4=B8=AD=E6=9C=9B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- IFoxCAD.sln | 16 +++++ .../ExtensionMethod/CollectionEx.cs | 43 ++++++++++-- .../ExtensionMethod/CurveEx.cs | 46 +++++++------ .../ExtensionMethod/DBDictionaryEx.cs | 68 +++++++------------ .../ExtensionMethod/EntityEx.cs | 6 +- .../ExtensionMethod/Filer/DwgFiler.cs | 29 ++++++-- .../ExtensionMethod/Filer/DwgFilerEx.cs | 10 +-- .../ExtensionMethod/Filer/DxfFiler.cs | 10 +-- .../ExtensionMethod/JigExTransient.cs | 2 +- src/IFoxCAD.Cad.Shared/Runtime/DBTrans.cs | 12 +++- src/IFoxCAD.Cad.Shared/Runtime/Env.cs | 4 +- .../IFoxCAD.Aacad08/IFoxCAD.Acad08.csproj | 2 +- .../IFoxCAD.Aacad09plus/GlobalUsings.cs | 10 +-- .../IFoxCAD.Acad09plus.csproj | 6 +- src/IFoxCAD.Cad/IFoxCAD.Gcad/GlobalUsings.cs | 17 +++-- .../IFoxCAD.Gcad/IFoxCAD.Gcad.csproj | 2 +- src/IFoxCAD.Cad/IFoxCAD.Zcad/GlobalUsings.cs | 16 +++-- .../IFoxCAD.Zcad/IFoxCAD.Zcad.csproj | 6 +- tests/TestAcad08/TestAcad08.csproj | 2 +- tests/TestAcad09plus/GlobalUsings.cs | 6 +- tests/TestAcad09plus/TestAcad09plus.csproj | 4 ++ 21 files changed, 194 insertions(+), 123 deletions(-) diff --git a/IFoxCAD.sln b/IFoxCAD.sln index c64fd36..2a9f817 100644 --- a/IFoxCAD.sln +++ b/IFoxCAD.sln @@ -36,6 +36,10 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Test", "Test", "{C0BEFC15-6 EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "IFoxCAD.LoadEx", "src\IFoxCAD.LoadForm\IFoxCAD.LoadEx.csproj", "{BFA1C130-8D87-45D0-8E79-301C1841A7A1}" EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "IFoxCAD.Gcad", "src\IFoxCAD.Cad\IFoxCAD.Gcad\IFoxCAD.Gcad.csproj", "{D0CE63A9-2DBA-4263-9749-E8CD3EA0DB57}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "IFoxCAD.Zcad", "src\IFoxCAD.Cad\IFoxCAD.Zcad\IFoxCAD.Zcad.csproj", "{751D15FC-B664-4B71-BB99-529E22EE1C4F}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -74,6 +78,14 @@ Global {BFA1C130-8D87-45D0-8E79-301C1841A7A1}.Debug|Any CPU.Build.0 = Debug|Any CPU {BFA1C130-8D87-45D0-8E79-301C1841A7A1}.Release|Any CPU.ActiveCfg = Release|Any CPU {BFA1C130-8D87-45D0-8E79-301C1841A7A1}.Release|Any CPU.Build.0 = Release|Any CPU + {D0CE63A9-2DBA-4263-9749-E8CD3EA0DB57}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {D0CE63A9-2DBA-4263-9749-E8CD3EA0DB57}.Debug|Any CPU.Build.0 = Debug|Any CPU + {D0CE63A9-2DBA-4263-9749-E8CD3EA0DB57}.Release|Any CPU.ActiveCfg = Release|Any CPU + {D0CE63A9-2DBA-4263-9749-E8CD3EA0DB57}.Release|Any CPU.Build.0 = Release|Any CPU + {751D15FC-B664-4B71-BB99-529E22EE1C4F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {751D15FC-B664-4B71-BB99-529E22EE1C4F}.Debug|Any CPU.Build.0 = Debug|Any CPU + {751D15FC-B664-4B71-BB99-529E22EE1C4F}.Release|Any CPU.ActiveCfg = Release|Any CPU + {751D15FC-B664-4B71-BB99-529E22EE1C4F}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -86,6 +98,8 @@ Global {CED63D2D-0AF6-4831-806D-5E8E9B0D0A07} = {C0BEFC15-6DF7-4636-BC76-4A0B88231470} {8C573180-523D-427D-8030-CAA7D6B5EB6B} = {C0BEFC15-6DF7-4636-BC76-4A0B88231470} {5F478F68-19BC-4A3A-AF39-8728F8D396DD} = {C0BEFC15-6DF7-4636-BC76-4A0B88231470} + {D0CE63A9-2DBA-4263-9749-E8CD3EA0DB57} = {465C4E39-FBA2-417A-AB31-BDC01601F420} + {751D15FC-B664-4B71-BB99-529E22EE1C4F} = {465C4E39-FBA2-417A-AB31-BDC01601F420} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {31D6C754-CF6B-4AB0-9861-6923DD312350} @@ -93,9 +107,11 @@ Global GlobalSection(SharedMSBuildProjectFiles) = preSolution src\IFoxCAD.Cad.Shared\IFoxCAD.Cad.Shared.projitems*{36f87d06-88b3-45e3-a2a8-0fc737b25428}*SharedItemsImports = 5 tests\TestShared\TestShared.projitems*{5f478f68-19bc-4a3a-af39-8728f8d396dd}*SharedItemsImports = 5 + src\IFoxCAD.Cad.Shared\IFoxCAD.Cad.Shared.projitems*{751d15fc-b664-4b71-bb99-529e22ee1c4f}*SharedItemsImports = 5 src\IFoxCAD.Cad.Shared\IFoxCAD.Cad.Shared.projitems*{82fb8450-b971-4e30-859f-4b2ddb81f590}*SharedItemsImports = 13 tests\TestShared\TestShared.projitems*{8c573180-523d-427d-8030-caa7d6b5eb6b}*SharedItemsImports = 5 src\IFoxCAD.Cad.Shared\IFoxCAD.Cad.Shared.projitems*{cc46e4e2-90df-48f5-b3d7-83fafed58f2a}*SharedItemsImports = 5 tests\TestShared\TestShared.projitems*{ced63d2d-0af6-4831-806d-5e8e9b0d0a07}*SharedItemsImports = 13 + src\IFoxCAD.Cad.Shared\IFoxCAD.Cad.Shared.projitems*{d0ce63a9-2dba-4263-9749-e8cd3ea0db57}*SharedItemsImports = 5 EndGlobalSection EndGlobal diff --git a/src/IFoxCAD.Cad.Shared/ExtensionMethod/CollectionEx.cs b/src/IFoxCAD.Cad.Shared/ExtensionMethod/CollectionEx.cs index 2c4e8ce..a7f1654 100644 --- a/src/IFoxCAD.Cad.Shared/ExtensionMethod/CollectionEx.cs +++ b/src/IFoxCAD.Cad.Shared/ExtensionMethod/CollectionEx.cs @@ -1,4 +1,7 @@ -namespace IFoxCAD.Cad; +using System.ComponentModel; +using System.Xml.Linq; + +namespace IFoxCAD.Cad; /// /// 集合扩展类 @@ -143,34 +146,55 @@ public enum KeywordName /// 关键字容器字段名 /// true含有 public static bool Contains(this KeywordCollection collection, string name, - KeywordName keywordName = KeywordName.GlobalName) + KeywordName keywordName = KeywordName.GlobalName) { bool contains = false; switch (keywordName) { case KeywordName.GlobalName: for (int i = 0; i < collection.Count; i++) - if (collection[i].GlobalName == name) + { +#if gcad + var item = collection.get_Item(i); +#else + var item = collection[i]; +#endif + if (item.GlobalName == name) { contains = true; break; } + } break; case KeywordName.LocalName: for (int i = 0; i < collection.Count; i++) - if (collection[i].LocalName == name) + { +#if gcad + var item = collection.get_Item(i); +#else + var item = collection[i]; +#endif + if (item.LocalName == name) { contains = true; break; } + } break; case KeywordName.DisplayName: for (int i = 0; i < collection.Count; i++) - if (collection[i].DisplayName == name) + { +#if gcad + var item = collection.get_Item(i); +#else + var item = collection[i]; +#endif + if (item.DisplayName == name) { contains = true; break; } + } break; default: break; @@ -188,7 +212,14 @@ public static Dictionary GetDict(this KeywordCollection collecti { Dictionary map = new(); for (int i = 0; i < collection.Count; i++) - map.Add(collection[i].GlobalName, collection[i].DisplayName); + { +#if gcad + var item = collection.get_Item(i); +#else + var item = collection[i]; +#endif + map.Add(item.GlobalName, item.DisplayName); + } return map; } #endregion diff --git a/src/IFoxCAD.Cad.Shared/ExtensionMethod/CurveEx.cs b/src/IFoxCAD.Cad.Shared/ExtensionMethod/CurveEx.cs index 41aad25..9b7ae1b 100644 --- a/src/IFoxCAD.Cad.Shared/ExtensionMethod/CurveEx.cs +++ b/src/IFoxCAD.Cad.Shared/ExtensionMethod/CurveEx.cs @@ -112,9 +112,11 @@ public static IEnumerable GetAllCycle(this IEnumerable curves) var graph = new Graph(); foreach (var curve in curves) { -#if NET35 +#if !acad graph.AddEdge(curve.ToCurve3d()!); -#else +#elif NET35 + graph.AddEdge(curve.ToCurve3d()!); +#else graph.AddEdge(curve.GetGeCurve()); #endif } @@ -467,12 +469,12 @@ public static NurbCurve3d ToCurve3d(this Spline spl) { case Poly2dType.SimplePoly: case Poly2dType.FitCurvePoly: - Polyline pl = new(); - pl.SetDatabaseDefaults(); - pl.ConvertFrom(pl2d, false); - return ToCurve3d(pl); + Polyline pl = new(); + pl.SetDatabaseDefaults(); + pl.ConvertFrom(pl2d, false); + return ToCurve3d(pl); default: - return ToNurbCurve3d(pl2d); + return ToNurbCurve3d(pl2d); } // Polyline pl = new Polyline(); @@ -491,13 +493,13 @@ public static NurbCurve3d ToCurve3d(this Spline spl) { case Poly2dType.SimplePoly: case Poly2dType.FitCurvePoly: - Polyline pl = new(); - pl.SetDatabaseDefaults(); - pl.ConvertFrom(pl2d, false); - return ToNurbCurve3d(pl); + Polyline pl = new(); + pl.SetDatabaseDefaults(); + pl.ConvertFrom(pl2d, false); + return ToNurbCurve3d(pl); default: - return ToCurve3d(pl2d.Spline); + return ToCurve3d(pl2d.Spline); } } @@ -578,15 +580,15 @@ public static CompositeCurve3d ToCurve3d(this Polyline pl) switch (pl.GetSegmentType(i)) { case SegmentType.Line: - c3ds.Add(pl.GetLineSegmentAt(i)); - break; + c3ds.Add(pl.GetLineSegmentAt(i)); + break; case SegmentType.Arc: - c3ds.Add(pl.GetArcSegmentAt(i)); - break; + c3ds.Add(pl.GetArcSegmentAt(i)); + break; default: - break; + break; } } return new CompositeCurve3d(c3ds.ToArray()); @@ -606,15 +608,15 @@ public static CompositeCurve3d ToCurve3d(this Polyline pl) switch (pl.GetSegmentType(i)) { case SegmentType.Line: - nc3dtemp = new NurbCurve3d(pl.GetLineSegmentAt(i)); - break; + nc3dtemp = new NurbCurve3d(pl.GetLineSegmentAt(i)); + break; case SegmentType.Arc: - nc3dtemp = pl.GetArcSegmentAt(i).ToNurbCurve3d(); - break; + nc3dtemp = pl.GetArcSegmentAt(i).ToNurbCurve3d(); + break; default: - break; + break; } if (nc3d is null) { diff --git a/src/IFoxCAD.Cad.Shared/ExtensionMethod/DBDictionaryEx.cs b/src/IFoxCAD.Cad.Shared/ExtensionMethod/DBDictionaryEx.cs index 719d7df..f6e6c65 100644 --- a/src/IFoxCAD.Cad.Shared/ExtensionMethod/DBDictionaryEx.cs +++ b/src/IFoxCAD.Cad.Shared/ExtensionMethod/DBDictionaryEx.cs @@ -151,53 +151,51 @@ public static DataTable CreateDataTable(Dictionary colTypes, o /// 数据 public static void SetValue(this DataCell cell, CellType type, object value) { - - switch (type) { case CellType.Bool: - cell.SetBool((bool)value); - break; + cell.SetBool((bool)value); + break; case CellType.CharPtr: - cell.SetString((string)value); - break; + cell.SetString((string)value); + break; case CellType.Integer: - cell.SetInteger((int)value); - break; + cell.SetInteger((int)value); + break; case CellType.Double: - cell.SetDouble((double)value); - break; + cell.SetDouble((double)value); + break; case CellType.ObjectId: - cell.SetObjectId((ObjectId)value); - break; + cell.SetObjectId((ObjectId)value); + break; case CellType.Point: - cell.SetPoint((Point3d)value); - break; + cell.SetPoint((Point3d)value); + break; case CellType.Vector: - cell.SetVector((Vector3d)value); - break; + cell.SetVector((Vector3d)value); + break; case CellType.HardOwnerId: - cell.SetHardOwnershipId((ObjectId)value); - break; + cell.SetHardOwnershipId((ObjectId)value); + break; case CellType.HardPtrId: - cell.SetHardPointerId((ObjectId)value); - break; + cell.SetHardPointerId((ObjectId)value); + break; case CellType.SoftOwnerId: - cell.SetSoftOwnershipId((ObjectId)value); - break; + cell.SetSoftOwnershipId((ObjectId)value); + break; case CellType.SoftPtrId: - cell.SetSoftPointerId((ObjectId)value); - break; + cell.SetSoftPointerId((ObjectId)value); + break; } } #endregion @@ -366,24 +364,4 @@ public static List RemoveNullGroup(this DBDictionary dict, Func AddressList; public int AddressListPt = 0; public List BinaryChunkList; @@ -77,7 +77,7 @@ public DwgFiler() { m_Position = 0; m_FilerType = FilerType.CopyFiler; - m_FilerStatus = Acad_ErrorStatus.OK; + m_FilerStatus = Cad_ErrorStatus.OK; AddressList = new List(); BinaryChunkList = new List(); BooleanList = new List(); @@ -117,7 +117,7 @@ public override FilerType FilerType get { return this.m_FilerType; } } - public override Acad_ErrorStatus FilerStatus + public override Cad_ErrorStatus FilerStatus { get { return m_FilerStatus; } set { m_FilerStatus = value; } @@ -345,17 +345,32 @@ public override void ResetFilerStatus() m_FilerType = FilerType.CopyFiler; } +#if zcad //中望这里弄错了 + public override void Seek(int offset, int method) + { + var ed = Acap.DocumentManager.MdiActiveDocument.Editor; + ed.WriteMessage(MethodInfo.GetCurrentMethod().Name + " = " + " \n "); + } + public override void Seek(long offset, int method) + { + var ed = Acap.DocumentManager.MdiActiveDocument.Editor; + ed.WriteMessage(MethodInfo.GetCurrentMethod().Name + " = " + " \n "); + } +#endif + +#if acad || gcad public override void Seek( -#if ac2008 || ac2009 +#if NET35 int #else long #endif - offset, int method) + offset, int method) { - Editor ed = Acap.DocumentManager.MdiActiveDocument.Editor; + var ed = Acap.DocumentManager.MdiActiveDocument.Editor; ed.WriteMessage(MethodInfo.GetCurrentMethod().Name + " = " + " \n "); } +#endif public override void WriteAddress(IntPtr value) { diff --git a/src/IFoxCAD.Cad.Shared/ExtensionMethod/Filer/DwgFilerEx.cs b/src/IFoxCAD.Cad.Shared/ExtensionMethod/Filer/DwgFilerEx.cs index b7a4d0e..8f6a3e2 100644 --- a/src/IFoxCAD.Cad.Shared/ExtensionMethod/Filer/DwgFilerEx.cs +++ b/src/IFoxCAD.Cad.Shared/ExtensionMethod/Filer/DwgFilerEx.cs @@ -14,11 +14,11 @@ public class DwgFilerEx /// /// Dwg序列化 /// - public DwgFilerEx(DwgFiler? acad_DwgFiler = null) + public DwgFilerEx(DwgFiler? Cad_DwgFiler = null) { - if (acad_DwgFiler == null) - acad_DwgFiler = new(); - DwgFiler = acad_DwgFiler; + if (Cad_DwgFiler == null) + Cad_DwgFiler = new(); + DwgFiler = Cad_DwgFiler; } /// @@ -75,7 +75,7 @@ public override string ToString() return DwgFiler.ToString(); } - public static implicit operator Acad_DwgFiler(DwgFilerEx df) + public static implicit operator Cad_DwgFiler(DwgFilerEx df) { return df.DwgFiler; } diff --git a/src/IFoxCAD.Cad.Shared/ExtensionMethod/Filer/DxfFiler.cs b/src/IFoxCAD.Cad.Shared/ExtensionMethod/Filer/DxfFiler.cs index ef9c42d..975c505 100644 --- a/src/IFoxCAD.Cad.Shared/ExtensionMethod/Filer/DxfFiler.cs +++ b/src/IFoxCAD.Cad.Shared/ExtensionMethod/Filer/DxfFiler.cs @@ -1,9 +1,10 @@ -namespace IFoxCAD.Cad; +#if acad +namespace IFoxCAD.Cad; /* 此处暂未完成,无任何测试,尚且不知道怎么用 */ using System.Runtime.Remoting; -public class DxfFiler : Acad_DxfFiler +public class DxfFiler : Cad_DxfFiler { public DxfFiler(IntPtr unmanagedPointer, [MarshalAs(UnmanagedType.U1)] bool autoDelete) : base(unmanagedPointer, autoDelete) { @@ -97,7 +98,7 @@ public override void SetError(string format, params string[] values) base.SetError(format, values); } - public override void SetError(Acad_ErrorStatus value, string format, params string[] values) + public override void SetError(Cad_ErrorStatus value, string format, params string[] values) { base.SetError(value, format, values); } @@ -216,4 +217,5 @@ protected override void Dispose(bool disposing) { base.Dispose(disposing); } -} \ No newline at end of file +} +#endif \ No newline at end of file diff --git a/src/IFoxCAD.Cad.Shared/ExtensionMethod/JigExTransient.cs b/src/IFoxCAD.Cad.Shared/ExtensionMethod/JigExTransient.cs index e161928..c7532b9 100644 --- a/src/IFoxCAD.Cad.Shared/ExtensionMethod/JigExTransient.cs +++ b/src/IFoxCAD.Cad.Shared/ExtensionMethod/JigExTransient.cs @@ -1,4 +1,4 @@ -#if !ac2008 +#if acad && !ac2008 namespace IFoxCAD.Cad; /// diff --git a/src/IFoxCAD.Cad.Shared/Runtime/DBTrans.cs b/src/IFoxCAD.Cad.Shared/Runtime/DBTrans.cs index 270886f..8fce7ef 100644 --- a/src/IFoxCAD.Cad.Shared/Runtime/DBTrans.cs +++ b/src/IFoxCAD.Cad.Shared/Runtime/DBTrans.cs @@ -324,11 +324,15 @@ public static implicit operator Transaction(DBTrans tr) /// 布局字典 /// public DBDictionary LayoutDict => GetObject(Database.LayoutDictionaryId)!; + +#if !zcad /// /// 数据链接字典 /// public DBDictionary DataLinkDict => GetObject(Database.DataLinkDictionaryId)!; -#if !ac2009 +#endif + +#if !ac2009 && !zcad /// /// 详细视图样式字典 /// @@ -459,7 +463,13 @@ public void SaveFile(DwgVersion version = DwgVersion.AC1800, if (Path.GetExtension(saveAsFile).ToLower().Contains("dxf")) { // dxf用任何版本号都会报错 +#if acad || gcad Database.DxfOut(saveAsFile, 7, true); +#endif + +#if zcad //中望这里没有测试 + Database.DxfOut(saveAsFile, 7, version, true); +#endif } else { diff --git a/src/IFoxCAD.Cad.Shared/Runtime/Env.cs b/src/IFoxCAD.Cad.Shared/Runtime/Env.cs index 572e3e8..c067e68 100644 --- a/src/IFoxCAD.Cad.Shared/Runtime/Env.cs +++ b/src/IFoxCAD.Cad.Shared/Runtime/Env.cs @@ -36,6 +36,7 @@ public static class Env #region Preferences +#if !zcad //中望这里弄错了 /// /// 获取当前配置的数据 /// @@ -50,6 +51,7 @@ public static object GetCurrentProfileProperty(string subSectionName, string pro return ss.ReadProperty(propertyName, ""); } + /// /// 获取对话框配置的数据 /// @@ -74,7 +76,7 @@ public static IConfigurationSection GetGlobalSection(string propertyName) IConfigurationSection ss = gs.OpenSubsection(propertyName); return ss; } - +#endif #endregion Preferences #region Enum diff --git a/src/IFoxCAD.Cad/IFoxCAD.Aacad08/IFoxCAD.Acad08.csproj b/src/IFoxCAD.Cad/IFoxCAD.Aacad08/IFoxCAD.Acad08.csproj index a5506b6..b11ec4b 100644 --- a/src/IFoxCAD.Cad/IFoxCAD.Aacad08/IFoxCAD.Acad08.csproj +++ b/src/IFoxCAD.Cad/IFoxCAD.Aacad08/IFoxCAD.Acad08.csproj @@ -35,7 +35,7 @@ - $(Configuration);ac2008;ac2009 + $(Configuration);acad;ac2008;ac2009 diff --git a/src/IFoxCAD.Cad/IFoxCAD.Aacad09plus/GlobalUsings.cs b/src/IFoxCAD.Cad/IFoxCAD.Aacad09plus/GlobalUsings.cs index 2430170..52c60de 100644 --- a/src/IFoxCAD.Cad/IFoxCAD.Aacad09plus/GlobalUsings.cs +++ b/src/IFoxCAD.Cad/IFoxCAD.Aacad09plus/GlobalUsings.cs @@ -17,7 +17,7 @@ global using Registry = Microsoft.Win32.Registry; global using RegistryKey = Microsoft.Win32.RegistryKey; -/// autocad 引用 +/// cad 引用 global using Autodesk.AutoCAD.ApplicationServices; global using Autodesk.AutoCAD.EditorInput; global using Autodesk.AutoCAD.Colors; @@ -31,15 +31,15 @@ global using Autodesk.AutoCAD; // jig命名空间会引起Viewport/Polyline等等重义,最好逐个引入 using Autodesk.AutoCAD.GraphicsInterface +global using Autodesk.AutoCAD.GraphicsInterface; global using WorldDraw = Autodesk.AutoCAD.GraphicsInterface.WorldDraw; global using Manager = Autodesk.AutoCAD.GraphicsSystem.Manager; global using Group = Autodesk.AutoCAD.DatabaseServices.Group; global using Viewport = Autodesk.AutoCAD.DatabaseServices.Viewport; -global using Autodesk.AutoCAD.GraphicsInterface; global using Polyline = Autodesk.AutoCAD.DatabaseServices.Polyline; -global using Acad_DwgFiler = Autodesk.AutoCAD.DatabaseServices.DwgFiler; -global using Acad_DxfFiler = Autodesk.AutoCAD.DatabaseServices.DxfFiler; -global using Acad_ErrorStatus = Autodesk.AutoCAD.Runtime.ErrorStatus; +global using Cad_DwgFiler = Autodesk.AutoCAD.DatabaseServices.DwgFiler; +global using Cad_DxfFiler = Autodesk.AutoCAD.DatabaseServices.DxfFiler; +global using Cad_ErrorStatus = Autodesk.AutoCAD.Runtime.ErrorStatus; /// ifoxcad.basal 引用 global using IFoxCAD.Basal; \ No newline at end of file diff --git a/src/IFoxCAD.Cad/IFoxCAD.Aacad09plus/IFoxCAD.Acad09plus.csproj b/src/IFoxCAD.Cad/IFoxCAD.Aacad09plus/IFoxCAD.Acad09plus.csproj index f4ebfac..5df8558 100644 --- a/src/IFoxCAD.Cad/IFoxCAD.Aacad09plus/IFoxCAD.Acad09plus.csproj +++ b/src/IFoxCAD.Cad/IFoxCAD.Aacad09plus/IFoxCAD.Acad09plus.csproj @@ -41,13 +41,13 @@ DEBUG - $(Configuration);ac2009 + $(Configuration);acad;ac2009 - $(Configuration);ac2013 + $(Configuration);acad;ac2013 - $(Configuration);ac2015 + $(Configuration);acad;ac2015 diff --git a/src/IFoxCAD.Cad/IFoxCAD.Gcad/GlobalUsings.cs b/src/IFoxCAD.Cad/IFoxCAD.Gcad/GlobalUsings.cs index 3f82278..0af0b20 100644 --- a/src/IFoxCAD.Cad/IFoxCAD.Gcad/GlobalUsings.cs +++ b/src/IFoxCAD.Cad/IFoxCAD.Gcad/GlobalUsings.cs @@ -10,10 +10,14 @@ global using Microsoft.Win32; global using System.ComponentModel; global using System.Runtime.InteropServices; +global using System.Collections.Specialized; global using Exception = System.Exception; -/// autocad 引用 +global using Registry = Microsoft.Win32.Registry; +global using RegistryKey = Microsoft.Win32.RegistryKey; + +/// cad 引用 global using GrxCAD.ApplicationServices; global using GrxCAD.EditorInput; global using GrxCAD.Colors; @@ -21,20 +25,21 @@ global using GrxCAD.Geometry; global using GrxCAD.Runtime; global using Acap = GrxCAD.ApplicationServices.Application; +global using Acgi = GrxCAD.GraphicsInterface; global using GrxCAD.DatabaseServices.Filters; global using GrxCAD; //jig命名空间会引起Viewport/Polyline等等重义,最好逐个引入 using Autodesk.AutoCAD.GraphicsInterface +global using GrxCAD.GraphicsInterface; global using WorldDraw = GrxCAD.GraphicsInterface.WorldDraw; global using Manager = GrxCAD.GraphicsSystem.Manager; - global using Group = GrxCAD.DatabaseServices.Group; global using Viewport = GrxCAD.DatabaseServices.Viewport; - -global using System.Collections.Specialized; -global using Registry = Microsoft.Win32.Registry; -global using RegistryKey = Microsoft.Win32.RegistryKey; +global using Polyline = GrxCAD.DatabaseServices.Polyline; +global using Cad_DwgFiler = GrxCAD.DatabaseServices.DwgFiler; +global using Cad_DxfFiler = GrxCAD.DatabaseServices.DxfFiler; +global using Cad_ErrorStatus = GrxCAD.Runtime.ErrorStatus; /// ifoxcad.basal 引用 global using IFoxCAD.Basal; \ No newline at end of file diff --git a/src/IFoxCAD.Cad/IFoxCAD.Gcad/IFoxCAD.Gcad.csproj b/src/IFoxCAD.Cad/IFoxCAD.Gcad/IFoxCAD.Gcad.csproj index d1a8911..a55e3a6 100644 --- a/src/IFoxCAD.Cad/IFoxCAD.Gcad/IFoxCAD.Gcad.csproj +++ b/src/IFoxCAD.Cad/IFoxCAD.Gcad/IFoxCAD.Gcad.csproj @@ -30,7 +30,7 @@ - $(Configuration);GCAD + $(Configuration);gcad diff --git a/src/IFoxCAD.Cad/IFoxCAD.Zcad/GlobalUsings.cs b/src/IFoxCAD.Cad/IFoxCAD.Zcad/GlobalUsings.cs index 36eeee5..71111c5 100644 --- a/src/IFoxCAD.Cad/IFoxCAD.Zcad/GlobalUsings.cs +++ b/src/IFoxCAD.Cad/IFoxCAD.Zcad/GlobalUsings.cs @@ -10,10 +10,14 @@ global using Microsoft.Win32; global using System.ComponentModel; global using System.Runtime.InteropServices; +global using System.Collections.Specialized; global using Exception = System.Exception; -/// autocad 引用 +global using Registry = Microsoft.Win32.Registry; +global using RegistryKey = Microsoft.Win32.RegistryKey; + +/// cad 引用 global using ZwSoft.ZwCAD.ApplicationServices; global using ZwSoft.ZwCAD.EditorInput; global using ZwSoft.ZwCAD.Colors; @@ -26,15 +30,15 @@ global using ZwSoft.ZwCAD; //jig命名空间会引起Viewport/Polyline等等重义,最好逐个引入 using ZwSoft.ZwCAD.GraphicsInterface +global using ZwSoft.ZwCAD.GraphicsInterface; global using WorldDraw = ZwSoft.ZwCAD.GraphicsInterface.WorldDraw; global using Manager = ZwSoft.ZwCAD.GraphicsSystem.Manager; - global using Group = ZwSoft.ZwCAD.DatabaseServices.Group; global using Viewport = ZwSoft.ZwCAD.DatabaseServices.Viewport; - -global using System.Collections.Specialized; -global using Registry = Microsoft.Win32.Registry; -global using RegistryKey = Microsoft.Win32.RegistryKey; +global using Polyline = ZwSoft.ZwCAD.DatabaseServices.Polyline; +global using Cad_DwgFiler = ZwSoft.ZwCAD.DatabaseServices.DwgFiler; +global using Cad_DxfFiler = ZwSoft.ZwCAD.DatabaseServices.DxfFiler; +global using Cad_ErrorStatus = ZwSoft.ZwCAD.Runtime.ErrorStatus; /// ifoxcad.basal 引用 global using IFoxCAD.Basal; \ No newline at end of file diff --git a/src/IFoxCAD.Cad/IFoxCAD.Zcad/IFoxCAD.Zcad.csproj b/src/IFoxCAD.Cad/IFoxCAD.Zcad/IFoxCAD.Zcad.csproj index 6b6305e..c97b9f6 100644 --- a/src/IFoxCAD.Cad/IFoxCAD.Zcad/IFoxCAD.Zcad.csproj +++ b/src/IFoxCAD.Cad/IFoxCAD.Zcad/IFoxCAD.Zcad.csproj @@ -1,7 +1,7 @@  - NET45;NET48 + NET48 true enable true @@ -30,11 +30,11 @@ - $(Configuration);ZCAD + $(Configuration);zcad - $(Configuration);ZCAD + $(Configuration);zcad diff --git a/tests/TestAcad08/TestAcad08.csproj b/tests/TestAcad08/TestAcad08.csproj index a8ea524..a0a397f 100644 --- a/tests/TestAcad08/TestAcad08.csproj +++ b/tests/TestAcad08/TestAcad08.csproj @@ -10,7 +10,7 @@ - $(Configuration);ac2008;ac2009 + $(Configuration);acad;ac2008;ac2009 diff --git a/tests/TestAcad09plus/GlobalUsings.cs b/tests/TestAcad09plus/GlobalUsings.cs index 1e1bc8d..56ba5f9 100644 --- a/tests/TestAcad09plus/GlobalUsings.cs +++ b/tests/TestAcad09plus/GlobalUsings.cs @@ -37,9 +37,9 @@ global using Viewport = Autodesk.AutoCAD.DatabaseServices.Viewport; global using Autodesk.AutoCAD.GraphicsInterface; global using Polyline = Autodesk.AutoCAD.DatabaseServices.Polyline; -global using Acad_DwgFiler = Autodesk.AutoCAD.DatabaseServices.DwgFiler; -global using Acad_DxfFiler = Autodesk.AutoCAD.DatabaseServices.DxfFiler; -global using Acad_ErrorStatus = Autodesk.AutoCAD.Runtime.ErrorStatus; +global using Cad_DwgFiler = Autodesk.AutoCAD.DatabaseServices.DwgFiler; +global using Cad_DxfFiler = Autodesk.AutoCAD.DatabaseServices.DxfFiler; +global using Cad_ErrorStatus = Autodesk.AutoCAD.Runtime.ErrorStatus; /// ifoxcad diff --git a/tests/TestAcad09plus/TestAcad09plus.csproj b/tests/TestAcad09plus/TestAcad09plus.csproj index a5ae899..0f956c6 100644 --- a/tests/TestAcad09plus/TestAcad09plus.csproj +++ b/tests/TestAcad09plus/TestAcad09plus.csproj @@ -9,6 +9,10 @@ x64 + + $(Configuration);acad + + -- Gitee From ad1ebc1e47be56ba4e65870922b26266a656e26d Mon Sep 17 00:00:00 2001 From: liuqihong <540762622@qq.com> Date: Fri, 16 Sep 2022 00:11:27 +0800 Subject: [PATCH 501/675] =?UTF-8?q?=E4=B8=AD=E6=9C=9B=E5=AE=98=E6=96=B9?= =?UTF-8?q?=E7=9A=84=E9=97=AE=E9=A2=98=E6=90=9C=E7=B4=A2=E4=B8=AD=E6=9C=9B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/IFoxCAD.Cad.Shared/ExtensionMethod/Filer/DwgFiler.cs | 2 +- src/IFoxCAD.Cad.Shared/Runtime/Env.cs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/IFoxCAD.Cad.Shared/ExtensionMethod/Filer/DwgFiler.cs b/src/IFoxCAD.Cad.Shared/ExtensionMethod/Filer/DwgFiler.cs index 3db0402..67f5b82 100644 --- a/src/IFoxCAD.Cad.Shared/ExtensionMethod/Filer/DwgFiler.cs +++ b/src/IFoxCAD.Cad.Shared/ExtensionMethod/Filer/DwgFiler.cs @@ -345,7 +345,7 @@ public override void ResetFilerStatus() m_FilerType = FilerType.CopyFiler; } -#if zcad //中望这里弄错了 +#if zcad //中望官方的问题 public override void Seek(int offset, int method) { var ed = Acap.DocumentManager.MdiActiveDocument.Editor; diff --git a/src/IFoxCAD.Cad.Shared/Runtime/Env.cs b/src/IFoxCAD.Cad.Shared/Runtime/Env.cs index c067e68..0699d44 100644 --- a/src/IFoxCAD.Cad.Shared/Runtime/Env.cs +++ b/src/IFoxCAD.Cad.Shared/Runtime/Env.cs @@ -36,7 +36,7 @@ public static class Env #region Preferences -#if !zcad //中望这里弄错了 +#if !zcad //中望官方的问题 /// /// 获取当前配置的数据 /// -- Gitee From 54a01b08d90c1fad9f2107b92390ce7985630530 Mon Sep 17 00:00:00 2001 From: liuqihong <540762622@qq.com> Date: Fri, 16 Sep 2022 00:13:02 +0800 Subject: [PATCH 502/675] =?UTF-8?q?=E5=A2=9E=E5=8A=A0=E4=B8=AD=E6=9C=9B?= =?UTF-8?q?=E9=97=AE=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/IFoxCAD.Cad.Shared/Runtime/DBTrans.cs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/IFoxCAD.Cad.Shared/Runtime/DBTrans.cs b/src/IFoxCAD.Cad.Shared/Runtime/DBTrans.cs index 8fce7ef..35bbc85 100644 --- a/src/IFoxCAD.Cad.Shared/Runtime/DBTrans.cs +++ b/src/IFoxCAD.Cad.Shared/Runtime/DBTrans.cs @@ -325,14 +325,13 @@ public static implicit operator Transaction(DBTrans tr) /// public DBDictionary LayoutDict => GetObject(Database.LayoutDictionaryId)!; -#if !zcad +#if !zcad //中望官方的问题 /// /// 数据链接字典 /// public DBDictionary DataLinkDict => GetObject(Database.DataLinkDictionaryId)!; -#endif -#if !ac2009 && !zcad +#if !ac2009 /// /// 详细视图样式字典 /// @@ -341,6 +340,7 @@ public static implicit operator Transaction(DBTrans tr) /// 剖面视图样式字典 /// public DBDictionary SectionViewStyleDict => GetObject(Database.SectionViewStyleDictionaryId)!; +#endif #endif #endregion -- Gitee From 7790a0069daf8b7d9fc8461780f3f827bc6339e2 Mon Sep 17 00:00:00 2001 From: liuqihong <540762622@qq.com> Date: Fri, 16 Sep 2022 00:43:06 +0800 Subject: [PATCH 503/675] =?UTF-8?q?=E4=B8=AD=E6=9C=9B=E9=97=AE=E9=A2=98?= =?UTF-8?q?=E5=A2=9E=E5=8A=A0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 18 +-- src/IFoxCAD.Basal/CLS/RuntimeHelpers.cs | 2 +- src/IFoxCAD.Basal/EnumEx.cs | 2 +- src/IFoxCAD.Basal/RandomEx.cs | 27 ++-- src/IFoxCAD.Basal/Sortedset/Sortedset.cs | 4 +- .../ExtensionMethod/EditorEx.cs | 120 +++++++++--------- .../ExtensionMethod/Filer/DwgFiler.cs | 2 +- .../ExtensionMethod/Filer/DwgFilerEx.cs | 12 +- .../ExtensionMethod/JigEx.cs | 8 +- .../ExtensionMethod/SymbolTableRecordEx.cs | 2 +- src/IFoxCAD.Cad.Shared/Runtime/AcadEMR.cs | 6 +- .../Runtime/AutoRegAssem.cs | 34 ++--- src/IFoxCAD.Cad.Shared/Runtime/DBTrans.cs | 4 +- src/IFoxCAD.Cad.Shared/Runtime/Env.cs | 72 ++++++----- src/IFoxCAD.Cad/IFoxCAD.Gcad/GlobalUsings.cs | 2 +- src/IFoxCAD.Cad/IFoxCAD.Zcad/GlobalUsings.cs | 2 +- tests/TestConsole/Helper.cs | 14 +- tests/TestConsole/RuntimeHelpers.cs | 5 +- tests/TestShared/CmdINI.cs | 2 +- tests/TestShared/TestJigExTransient.cs | 2 +- tests/TestShared/TestQuadTree.cs | 12 +- 21 files changed, 178 insertions(+), 174 deletions(-) diff --git a/README.md b/README.md index 36ca94d..6c94336 100644 --- a/README.md +++ b/README.md @@ -153,31 +153,31 @@ IFox的[jing分支](https://gitee.com/inspirefunction/ifoxcad/tree/jing/)是一 [IFoxInitialize] public void InitOne() { - //TODO 您想在加载dll之后自动执行的函数 + // TODO 您想在加载dll之后自动执行的函数 // 可以随便在哪里类里 可以多次实现 IFoxInitialize 特性 } } - //其他的类中的函数: - //实现自动接口之后,在任意一个函数上面使用此特性,减少每次改动 CmdINI 类 + // 其他的类中的函数: + // 实现自动接口之后,在任意一个函数上面使用此特性,减少每次改动 CmdINI 类 public class AutoTest { [IFoxInitialize] public void Initialize() { - //TODO 您想在加载dll之后自动执行的函数 + // TODO 您想在加载dll之后自动执行的函数 } [IFoxInitialize] public void InitTwo() { - //TODO 您想在加载dll之后自动执行的函数 + // TODO 您想在加载dll之后自动执行的函数 // 可以随便在哪里类里 可以多次实现 IFoxInitialize 特性 } [IFoxInitialize(isInitialize: false)] // 特性的参数为false的时候就表示卸载时执行的函数 public void Terminate() { - //TODO 您想在关闭cad时自动执行的函数 + // TODO 您想在关闭cad时自动执行的函数 } } ``` @@ -189,10 +189,10 @@ IFox的[jing分支](https://gitee.com/inspirefunction/ifoxcad/tree/jing/)是一 由于cad的对象是有打开模式,是否可写等等,为了安全起见,在处理对象时,一般是用读模式打开,然后需要写数据的时候在提权为写模式,然后在降级到读模式,但是这个过程中,很容易漏掉某些步骤,然后cad崩溃。为了处理这些情况,内裤提供了提权类来保证读写模式的有序转换。 ```c# - using(line.ForWrite()) //开启对象写模式提权事务 + using(line.ForWrite()) // 开启对象写模式提权事务 { - //处理代码 - } //关闭事务自动处理读写模式 + // 处理代码 + } // 关闭事务自动处理读写模式 ``` 8. 未完待续。。。。 diff --git a/src/IFoxCAD.Basal/CLS/RuntimeHelpers.cs b/src/IFoxCAD.Basal/CLS/RuntimeHelpers.cs index 1008b09..7c2a4a9 100644 --- a/src/IFoxCAD.Basal/CLS/RuntimeHelpers.cs +++ b/src/IFoxCAD.Basal/CLS/RuntimeHelpers.cs @@ -20,7 +20,7 @@ public static T[] GetSubArray(T[] array, Range range) (int offset, int length) = range.GetOffsetAndLength(array.Length); - if (default(T)! != null || typeof(T[]) == array.GetType()) // TODO-NULLABLE: default(T) == null warning (https://github.com/dotnet/roslyn/issues/34757) + if (default(T)! != null || typeof(T[]) == array.GetType()) // NULLABLE: default(T) == null warning (https://github.com/dotnet/roslyn/issues/34757) { // We know the type of the array to be exactly T[]. if (length == 0) diff --git a/src/IFoxCAD.Basal/EnumEx.cs b/src/IFoxCAD.Basal/EnumEx.cs index 46aa077..79f01bc 100644 --- a/src/IFoxCAD.Basal/EnumEx.cs +++ b/src/IFoxCAD.Basal/EnumEx.cs @@ -90,7 +90,7 @@ public static void CleanCache() } - // TODO 山人审核代码之后可以删除,这个完全被上面替代了 + // TODO: 山人审核代码之后可以删除,这个完全被上面替代了 public static string GetDesc(this Enum val) { var type = val.GetType(); diff --git a/src/IFoxCAD.Basal/RandomEx.cs b/src/IFoxCAD.Basal/RandomEx.cs index a9682f7..4d74324 100644 --- a/src/IFoxCAD.Basal/RandomEx.cs +++ b/src/IFoxCAD.Basal/RandomEx.cs @@ -30,7 +30,7 @@ public static double NextDouble(Random ran, double minValue, double maxValue) } /// /// 生成一个指定范围的浮点数值 - /// + ///
    /// 范围最小浮点数值 /// 范围最大浮点数值 /// @@ -57,7 +57,7 @@ public static bool NextBool(Random ran) } /// /// 生成一个不连续或指定值的随机值 - /// + /// /// 一个字符串数组 /// public static string NextString(string[] arr) @@ -78,7 +78,7 @@ public static string NextString(Random ran, string[] arr) } /// /// 生成一个不连续或指定值的随机值 - /// + /// /// 一个双精度值数组 /// public static double NextDouble(double[] arr) @@ -99,7 +99,7 @@ public static double NextDouble(Random ran, double[] arr) } /// /// 生成指定范围内的整数 - /// + /// /// 范围最大整数值 /// public static int NextInt(int max) @@ -119,7 +119,7 @@ public static int NextInt(Random ran, int max) } /// /// 生成指定范围内的整数 - /// + /// /// 范围的最小整数 /// 范围的最大整数 /// 返回一个介于之间的整数 @@ -158,13 +158,13 @@ public static System.Drawing.Color NextColor(Random ran) int R = ran.Next(255); int G = ran.Next(255); int B = ran.Next(255); - B = (R + G > 400) ? R + G - 400 : B;//0 : 380 - R - G; + B = (R + G > 400) ? R + G - 400 : B;// 0 : 380 - R - G; B = (B > 255) ? 255 : B; return System.Drawing.Color.FromArgb(R, G, B); } - /* + /* * 知识准备: * | 高位64位 | 低位32位 | * Convert.ToString(int.MaxValue, 2)输出二进制 "1111111111111111111111111111111" 31个;最高位是符号位,所以少1位 @@ -179,12 +179,12 @@ public static System.Drawing.Color NextColor(Random ran) * 解释代码: * 0x01: * (int)(long.MaxValue & 0xffffffffL) | (int)(long.MaxValue >> 32); - * Convert.ToString(long.MaxValue & 0xffffffffL, 2)//去掉高位:"11111111111111111111111111111111" 32个,再强转int + * Convert.ToString(long.MaxValue & 0xffffffffL, 2)// 去掉高位:"11111111111111111111111111111111" 32个,再强转int * 按位与&是保证符号位肯定是1,其他尽可能为0,高位被去掉只是MaxValue&0的原因,强转才是去掉高位..."尽可能"一词带来第一次随机性 * 0x02: - * Convert.ToString((long.MaxValue >> 32), 2) //去掉低位: "1111111111111111111111111111111" 31个,再强转int + * Convert.ToString((long.MaxValue >> 32), 2) // 去掉低位: "1111111111111111111111111111111" 31个,再强转int * 按位或|是尽可能为1..."尽可能"一词带来第二次随机性 - * + * */ /// @@ -193,10 +193,9 @@ public static System.Drawing.Color NextColor(Random ran) /// /// public static Random GetRandom() - { - var tick = DateTime.Now.Ticks; + { + var tick = DateTime.Now.Ticks; var tickSeeds = (int)(tick & 0xffffffffL) | (int)(tick >> 32); return new Random(tickSeeds); } - -} +} \ No newline at end of file diff --git a/src/IFoxCAD.Basal/Sortedset/Sortedset.cs b/src/IFoxCAD.Basal/Sortedset/Sortedset.cs index 4088582..58e3c65 100644 --- a/src/IFoxCAD.Basal/Sortedset/Sortedset.cs +++ b/src/IFoxCAD.Basal/Sortedset/Sortedset.cs @@ -501,7 +501,7 @@ internal virtual bool AddIfNotPresent(T item) /// public bool Remove(T item) { - return this.DoRemove(item); // hack so it can be made non-virtual + return this.DoRemove(item); // so it can be made non-virtual } internal virtual bool DoRemove(T item) @@ -2113,7 +2113,7 @@ public override bool Contains(T item) } internal override bool DoRemove(T item) - { // todo: uppercase this and others + { if (!IsWithinRange(item)) { return false; diff --git a/src/IFoxCAD.Cad.Shared/ExtensionMethod/EditorEx.cs b/src/IFoxCAD.Cad.Shared/ExtensionMethod/EditorEx.cs index 8e40ebf..0fcc643 100644 --- a/src/IFoxCAD.Cad.Shared/ExtensionMethod/EditorEx.cs +++ b/src/IFoxCAD.Cad.Shared/ExtensionMethod/EditorEx.cs @@ -70,7 +70,6 @@ public static SelectionSet SelectByLineWeight(this Editor editor, LineWeight lin pso.RejectPaperspaceViewport = mode.Contains(":V"); pso.AllowSubSelections = mode.Contains("-A"); pso.ForceSubSelections = mode.Contains("-F"); - } if (messages is not null) { @@ -89,7 +88,6 @@ public static SelectionSet SelectByLineWeight(this Editor editor, LineWeight lin if (keywords.ContainsKey(e.Input)) keywords[e.Input].Invoke(); }; - } try { @@ -99,7 +97,7 @@ public static SelectionSet SelectByLineWeight(this Editor editor, LineWeight lin ss = editor.GetSelection(pso); } catch (Exception e) - { + { editor.WriteMessage($"\nKey is {e.Message}"); } return ss; @@ -237,14 +235,14 @@ public static void SsgetAddKeys(this PromptSelectionOptions pso, // Env.Editor.SelectionAdded += SelectTest_SelectionAdded; // // 初始化坐标系 // Env.Editor.CurrentUserCoordinateSystem = Matrix3d.Identity; - + // // 创建过滤器 // var sf = new OpEqual(0, "arc"); // var pso = new PromptSelectionOptions // { // MessageForAdding = "\n请选择对象:" // }; - + // pso.Keywords.Add("Z"); // pso.Keywords.Add("X"); // pso.Keywords.Add("Q"); @@ -255,8 +253,8 @@ public static void SsgetAddKeys(this PromptSelectionOptions pso, // // 用户选择 // var psr = Env.Editor.GetSelection(pso, sf); // // 处理代码 - - + + // } // catch (Exception ex)// 捕获关键字 // { @@ -275,7 +273,7 @@ public static void SsgetAddKeys(this PromptSelectionOptions pso, // // 关闭选中事件 // Env.Editor.SelectionAdded -= SelectTest_SelectionAdded; // } - + // /// // /// 即时选择 // /// @@ -290,8 +288,6 @@ public static void SsgetAddKeys(this PromptSelectionOptions pso, // // 处理代码 // for (int i = 0; i < e.AddedObjects.Count; i++) // { - - // // 处理完移除已处理的对象 // e.Remove(i); // } @@ -299,7 +295,7 @@ public static void SsgetAddKeys(this PromptSelectionOptions pso, // // 激活选中事件 // Env.Editor.SelectionAdded += SelectTest_SelectionAdded; // } - + // /// // /// 关键字响应 // /// @@ -318,7 +314,7 @@ public static void SsgetAddKeys(this PromptSelectionOptions pso, // { // break; // } - + // case "Q": // { // break; @@ -326,7 +322,7 @@ public static void SsgetAddKeys(this PromptSelectionOptions pso, // } // // 抛出异常,用于更新提示信息 // throw new ArgumentException("XuError"); - // } + // } // #endregion #endregion @@ -493,7 +489,6 @@ public static bool Acceptable() /// public static List GetLines(IEnumerable pnts, bool isClosed) { - var itor = pnts.GetEnumerator(); if (!itor.MoveNext()) return new List(); @@ -712,55 +707,55 @@ public static Matrix3d GetMatrix(this Editor editor, CoordinateSystemCode from, switch (from) { case CoordinateSystemCode.Wcs: - switch (to) - { - case CoordinateSystemCode.Ucs: - return editor.GetMatrixFromWcsToUcs(); + switch (to) + { + case CoordinateSystemCode.Ucs: + return editor.GetMatrixFromWcsToUcs(); - case CoordinateSystemCode.MDcs: - return editor.GetMatrixFromMDcsToWcs(); + case CoordinateSystemCode.MDcs: + return editor.GetMatrixFromMDcsToWcs(); - case CoordinateSystemCode.PDcs: - throw new Exception("To be used only with DCS...ErrorStatus.InvalidInput"); - } - break; + case CoordinateSystemCode.PDcs: + throw new Exception("To be used only with DCS...ErrorStatus.InvalidInput"); + } + break; case CoordinateSystemCode.Ucs: - switch (to) - { - case CoordinateSystemCode.Wcs: - return editor.GetMatrixFromUcsToWcs(); + switch (to) + { + case CoordinateSystemCode.Wcs: + return editor.GetMatrixFromUcsToWcs(); - case CoordinateSystemCode.MDcs: - return editor.GetMatrixFromUcsToWcs() * editor.GetMatrixFromWcsToMDcs(); + case CoordinateSystemCode.MDcs: + return editor.GetMatrixFromUcsToWcs() * editor.GetMatrixFromWcsToMDcs(); - case CoordinateSystemCode.PDcs: - throw new Exception("To be used only with DCS... ErrorStatus.InvalidInput"); - } - break; + case CoordinateSystemCode.PDcs: + throw new Exception("To be used only with DCS... ErrorStatus.InvalidInput"); + } + break; case CoordinateSystemCode.MDcs: - switch (to) - { - case CoordinateSystemCode.Wcs: - return editor.GetMatrixFromMDcsToWcs(); + switch (to) + { + case CoordinateSystemCode.Wcs: + return editor.GetMatrixFromMDcsToWcs(); - case CoordinateSystemCode.Ucs: - return editor.GetMatrixFromMDcsToWcs() * editor.GetMatrixFromWcsToUcs(); + case CoordinateSystemCode.Ucs: + return editor.GetMatrixFromMDcsToWcs() * editor.GetMatrixFromWcsToUcs(); - case CoordinateSystemCode.PDcs: - return editor.GetMatrixFromMDcsToPDcs(); - } - break; + case CoordinateSystemCode.PDcs: + return editor.GetMatrixFromMDcsToPDcs(); + } + break; case CoordinateSystemCode.PDcs: - switch (to) - { - case CoordinateSystemCode.Wcs: - throw new Exception("To be used only with DCS... ErrorStatus.InvalidInput"); - case CoordinateSystemCode.Ucs: - throw new Exception("To be used only with DCS... ErrorStatus.InvalidInput"); - case CoordinateSystemCode.MDcs: - return editor.GetMatrixFromPDcsToMDcs(); - } - break; + switch (to) + { + case CoordinateSystemCode.Wcs: + throw new Exception("To be used only with DCS... ErrorStatus.InvalidInput"); + case CoordinateSystemCode.Ucs: + throw new Exception("To be used only with DCS... ErrorStatus.InvalidInput"); + case CoordinateSystemCode.MDcs: + return editor.GetMatrixFromPDcsToMDcs(); + } + break; } return Matrix3d.Identity; #else @@ -999,13 +994,13 @@ public static PromptResult GetString(this Editor ed, string Message, string Defa #region 执行lisp #if NET35 - [DllImport("acad.exe", CallingConvention = CallingConvention.Cdecl, EntryPoint = "acedInvoke")] + [DllImport("acad.exe", #else - [DllImport("accore.dll", CallingConvention = CallingConvention.Cdecl, EntryPoint = "acedInvoke")] + [DllImport("accore.dll", #endif + CallingConvention = CallingConvention.Cdecl, EntryPoint = "acedInvoke")] static extern int AcedInvoke(IntPtr args, out IntPtr result); - #if NET35 [DllImport("acad.exe", CharSet = CharSet.Unicode, CallingConvention = CallingConvention.Cdecl, EntryPoint = "?acedEvaluateLisp@@YAHPB_WAAPAUresbuf@@@Z")] @@ -1019,12 +1014,11 @@ public static PromptResult GetString(this Editor ed, string Message, string Defa #pragma warning disable CA2101 // 指定对 P/Invoke 字符串参数进行封送处理 #if NET35 - [DllImport("acad.exe", CharSet = CharSet.Auto, CallingConvention = CallingConvention.Cdecl, - EntryPoint = "ads_queueexpr")] + [DllImport("acad.exe", #else - [DllImport("accore.dll", CharSet = CharSet.Auto, CallingConvention = CallingConvention.Cdecl, - EntryPoint = "ads_queueexpr")] + [DllImport("accore.dll", #endif + CharSet = CharSet.Auto, CallingConvention = CallingConvention.Cdecl, EntryPoint = "ads_queueexpr")] #pragma warning restore CA2101 // 指定对 P/Invoke 字符串参数进行封送处理 static extern int Ads_queueexpr(string strExpr); @@ -1057,7 +1051,7 @@ public enum RunLispFlag : byte * } * 调用方式: * (command "CmdTest_RunLisp1") - * bug说明: + * bug说明: * AcedEvaluateLisp接口在高版本调用时候没有运行成功,使得 !abc 没有值 * 经过测试,cad08调用成功,此bug与CommandFlags无关 * 解决方案: @@ -1087,4 +1081,4 @@ public enum RunLispFlag : byte return null; } #endregion -} +} \ No newline at end of file diff --git a/src/IFoxCAD.Cad.Shared/ExtensionMethod/Filer/DwgFiler.cs b/src/IFoxCAD.Cad.Shared/ExtensionMethod/Filer/DwgFiler.cs index 67f5b82..9cc99f6 100644 --- a/src/IFoxCAD.Cad.Shared/ExtensionMethod/Filer/DwgFiler.cs +++ b/src/IFoxCAD.Cad.Shared/ExtensionMethod/Filer/DwgFiler.cs @@ -345,7 +345,7 @@ public override void ResetFilerStatus() m_FilerType = FilerType.CopyFiler; } -#if zcad //中望官方的问题 +#if zcad // 中望官方的问题 public override void Seek(int offset, int method) { var ed = Acap.DocumentManager.MdiActiveDocument.Editor; diff --git a/src/IFoxCAD.Cad.Shared/ExtensionMethod/Filer/DwgFilerEx.cs b/src/IFoxCAD.Cad.Shared/ExtensionMethod/Filer/DwgFilerEx.cs index 8f6a3e2..2f8e0c9 100644 --- a/src/IFoxCAD.Cad.Shared/ExtensionMethod/Filer/DwgFilerEx.cs +++ b/src/IFoxCAD.Cad.Shared/ExtensionMethod/Filer/DwgFilerEx.cs @@ -51,7 +51,7 @@ public void DwgIn() public DwgFilerEx? DeserializeObject(string json) { throw new ArgumentException(); - //return JsonConvert.DeserializeObject(json);// 反序列化*字符串转类 + // return JsonConvert.DeserializeObject(json);// 反序列化*字符串转类 } /// @@ -61,16 +61,16 @@ public void DwgIn() public string SerializeObject() { throw new ArgumentException(); - //return JsonConvert.SerializeObject(DwgFiler, Formatting.Indented); // 序列化*类转字符串 + // return JsonConvert.SerializeObject(DwgFiler, Formatting.Indented); // 序列化*类转字符串 } public override string ToString() { // 替换中括号以外的字符串,替换逗号为换行符 https://bbs.csdn.net/topics/370134253 - //var str = SerializeObject(); - //str = str.Substring(1, str.Length - 2); - //str = Regex.Replace(str, @"(?:,)(?![^\[]*?\])", "\r\n"); - //return str; + // var str = SerializeObject(); + // str = str.Substring(1, str.Length - 2); + // str = Regex.Replace(str, @"(?:,)(?![^\[]*?\])", "\r\n"); + // return str; return DwgFiler.ToString(); } diff --git a/src/IFoxCAD.Cad.Shared/ExtensionMethod/JigEx.cs b/src/IFoxCAD.Cad.Shared/ExtensionMethod/JigEx.cs index a7e6dc1..c406304 100644 --- a/src/IFoxCAD.Cad.Shared/ExtensionMethod/JigEx.cs +++ b/src/IFoxCAD.Cad.Shared/ExtensionMethod/JigEx.cs @@ -348,10 +348,10 @@ protected override DynamicDimensionDataCollection GetDynamicDimensionData(double #endregion // 此处无测试 - //protected override void ViewportDraw(ViewportDraw draw) - //{ - // base.ViewportDraw(draw); - //} + // protected override void ViewportDraw(ViewportDraw draw) + // { + // base.ViewportDraw(draw); + // } } #if false diff --git a/src/IFoxCAD.Cad.Shared/ExtensionMethod/SymbolTableRecordEx.cs b/src/IFoxCAD.Cad.Shared/ExtensionMethod/SymbolTableRecordEx.cs index 4a5737e..c27f341 100644 --- a/src/IFoxCAD.Cad.Shared/ExtensionMethod/SymbolTableRecordEx.cs +++ b/src/IFoxCAD.Cad.Shared/ExtensionMethod/SymbolTableRecordEx.cs @@ -316,7 +316,7 @@ public static ObjectId AddArc(this BlockTableRecord btr, return btr.AddEnt(arc, action, trans); } - // todo: 所有涉及默认无参构造的实体类型,都需要调用SetDatabaseDefaults(); + // TODO: 所有涉及默认无参构造的实体类型,都需要调用SetDatabaseDefaults(); #endregion #region 获取实体/实体id diff --git a/src/IFoxCAD.Cad.Shared/Runtime/AcadEMR.cs b/src/IFoxCAD.Cad.Shared/Runtime/AcadEMR.cs index 96bb71d..4b8b106 100644 --- a/src/IFoxCAD.Cad.Shared/Runtime/AcadEMR.cs +++ b/src/IFoxCAD.Cad.Shared/Runtime/AcadEMR.cs @@ -86,15 +86,15 @@ public static void Remove(bool echoes = false) else ptr = new IntPtr(funcAdress.ToInt64() + 4); - if (!CheckFunc(ref ptr, 51, 2))//08 通过此处 + if (!CheckFunc(ref ptr, 51, 2))// 08 通过此处 if (echoes) Env.Printl("无法验证函数体:0x33"); IntPtr destPtr = ptr; - if (!CheckFunc(ref ptr, 57, 6))//08 无法通过此处,所以只是打印提示 + if (!CheckFunc(ref ptr, 57, 6))// 08 无法通过此处,所以只是打印提示 if (echoes) Env.Printl("无法验证函数体:0x39"); - if (!CheckFunc(ref ptr, 15, 2))//08 无法通过此处,所以只是打印提示 + if (!CheckFunc(ref ptr, 15, 2))// 08 无法通过此处,所以只是打印提示 if (echoes) Env.Printl("无法验证函数体:0x0F"); diff --git a/src/IFoxCAD.Cad.Shared/Runtime/AutoRegAssem.cs b/src/IFoxCAD.Cad.Shared/Runtime/AutoRegAssem.cs index fd3bacc..91ab6d1 100644 --- a/src/IFoxCAD.Cad.Shared/Runtime/AutoRegAssem.cs +++ b/src/IFoxCAD.Cad.Shared/Runtime/AutoRegAssem.cs @@ -86,27 +86,27 @@ public AutoRegAssem(AutoRegConfig autoRegConfig) public static RegistryKey GetAcAppKey(bool writable = true) { RegistryKey? ackey = null; - + var hc = HostApplicationServices.Current; +#if acad || zcad // 中望此处缺乏测试 #if NET35 - string key = HostApplicationServices.Current.RegistryProductRootKey; // 这里浩辰读出来是"" -#elif !HC2020 - string key = HostApplicationServices.Current.UserRegistryProductRootKey; + string key = hc.RegistryProductRootKey; +#else + string key = hc.UserRegistryProductRootKey; #endif - -#if !HC2020 ackey = Registry.CurrentUser.OpenSubKey(key, writable); -#else - // 浩辰 - var s = GrxCAD.DatabaseServices.HostApplicationServices.Current.RegistryProductRootKey;// 浩辰奇怪的空值 - string str = CadSystem.Getvar("ACADVER"); - str = Regex.Replace(str, @"[^\d.\d]", ""); - double.TryParse(str, out double a); - // string regedit = @"Software\Gstarsoft\GstarCAD\R" + a.ToString() + @"\zh-CN"; // 2019 - // string regedit = @"Software\Gstarsoft\GstarCAD\B" + a.ToString() + @"\zh-CN";// 2020 这里是 - string regedit = @"Software\Gstarsoft\GstarCAD\B20\zh-CN";// 2020 这里是 +#endif +#if gcad + // gcad 此处是否仍然有bug? 等待其他人测试反馈 + var key = hc.RegistryProductRootKey; // 浩辰2020读出来是"" + string regedit = ""; + if (Env.GetAcadVersion() == 2019) + regedit = @"Software\Gstarsoft\GstarCAD\R" + 19 + @"\zh-CN"; + if (Env.GetAcadVersion() == 2020) + regedit = @"Software\Gstarsoft\GstarCAD\B" + 20 + @"\zh-CN"; ackey = Registry.CurrentUser.OpenSubKey(regedit, writable); #endif + return ackey.CreateSubKey("Applications"); } @@ -165,7 +165,9 @@ public void RegApp() // 这里的是不会自动执行的 public void Initialize() { } - public void Terminate() { } + public void Terminate() + { + } ~AutoRegAssem() { diff --git a/src/IFoxCAD.Cad.Shared/Runtime/DBTrans.cs b/src/IFoxCAD.Cad.Shared/Runtime/DBTrans.cs index 35bbc85..153b539 100644 --- a/src/IFoxCAD.Cad.Shared/Runtime/DBTrans.cs +++ b/src/IFoxCAD.Cad.Shared/Runtime/DBTrans.cs @@ -325,7 +325,7 @@ public static implicit operator Transaction(DBTrans tr) /// public DBDictionary LayoutDict => GetObject(Database.LayoutDictionaryId)!; -#if !zcad //中望官方的问题 +#if !zcad // 中望官方的问题 /// /// 数据链接字典 /// @@ -467,7 +467,7 @@ public void SaveFile(DwgVersion version = DwgVersion.AC1800, Database.DxfOut(saveAsFile, 7, true); #endif -#if zcad //中望这里没有测试 +#if zcad // 中望这里没有测试 Database.DxfOut(saveAsFile, 7, version, true); #endif } diff --git a/src/IFoxCAD.Cad.Shared/Runtime/Env.cs b/src/IFoxCAD.Cad.Shared/Runtime/Env.cs index 0699d44..7c6b840 100644 --- a/src/IFoxCAD.Cad.Shared/Runtime/Env.cs +++ b/src/IFoxCAD.Cad.Shared/Runtime/Env.cs @@ -36,7 +36,7 @@ public static class Env #region Preferences -#if !zcad //中望官方的问题 +#if !zcad // 中望官方的问题 /// /// 获取当前配置的数据 /// @@ -80,7 +80,6 @@ public static IConfigurationSection GetGlobalSection(string propertyName) #endregion Preferences #region Enum - /// /// 控制在AutoLISP的command函数运行时AutoCAD是否回显提示和输入, 为显示, 为不显示 /// @@ -432,7 +431,7 @@ private static string GetName(this T value) #endregion Enum - #region acad系统变量 + #region 系统变量 /// /// 获取cad系统变量 /// @@ -465,7 +464,8 @@ public static void SetVar(string? varName, object? value, bool echo = true) } #endregion - #region acad环境变量 + #region 环境变量 +#if acad #if NET35 [System.Security.SuppressUnmanagedCodeSecurity] [DllImport("acad.exe", CharSet = CharSet.Auto, CallingConvention = CallingConvention.Cdecl, EntryPoint = "acedGetEnv")] @@ -474,7 +474,7 @@ public static void SetVar(string? varName, object? value, bool echo = true) [System.Security.SuppressUnmanagedCodeSecurity] [DllImport("acad.exe", CharSet = CharSet.Auto, CallingConvention = CallingConvention.Cdecl, EntryPoint = "acedSetEnv")] static extern int AcedSetEnv(string? envName, StringBuilder NewValue); -#elif !HC2020 +#else [System.Security.SuppressUnmanagedCodeSecurity] [DllImport("accore.dll", CharSet = CharSet.Auto, CallingConvention = CallingConvention.Cdecl, EntryPoint = "acedGetEnv")] static extern int AcedGetEnv(string? envName, StringBuilder ReturnValue); @@ -483,7 +483,9 @@ public static void SetVar(string? varName, object? value, bool echo = true) [DllImport("accore.dll", CharSet = CharSet.Auto, CallingConvention = CallingConvention.Cdecl, EntryPoint = "acedSetEnv")] static extern int AcedSetEnv(string? envName, StringBuilder NewValue); #endif -#if HC2020 +#endif + +#if gcad [System.Security.SuppressUnmanagedCodeSecurity] [DllImport("gced.dll", CharSet = CharSet.Auto, CallingConvention = CallingConvention.Cdecl, EntryPoint = "gcedGetEnv")] static extern int AcedGetEnv(string? envName, StringBuilder ReturnValue); @@ -493,6 +495,16 @@ public static void SetVar(string? varName, object? value, bool echo = true) static extern int AcedSetEnv(string? envName, StringBuilder NewValue); #endif +#if zcad // TODO: 中望没有测试,此处仅为不报错;本工程所有含有"中望"均存在问题 + [System.Security.SuppressUnmanagedCodeSecurity] + [DllImport("zced.dll", CharSet = CharSet.Auto, CallingConvention = CallingConvention.Cdecl, EntryPoint = "zcedGetEnv")] + static extern int AcedGetEnv(string? envName, StringBuilder ReturnValue); + + [System.Security.SuppressUnmanagedCodeSecurity] + [DllImport("zced.dll", CharSet = CharSet.Auto, CallingConvention = CallingConvention.Cdecl, EntryPoint = "zcedSetEnv")] + static extern int AcedSetEnv(string? envName, StringBuilder NewValue); +#endif + /// /// 读取acad环境变量
    /// 也能获取win环境变量 @@ -530,28 +542,28 @@ public static int SetEnv(string? name, string? var) #endregion #region win环境变量/由于 Aced的 能够同时获取此变量与cad内的,所以废弃 - ///// - ///// 获取系统环境变量 - ///// - ///// 变量名 - ///// 指定的环境变量的值;或者如果找不到环境变量,则返回 null - //public static string? GetEnv(string? var) - //{ - // // 从当前进程或者从当前用户或本地计算机的 Windows 操作系统注册表项检索环境变量的值 - // // 用户: 计算机\HKEY_CURRENT_USER\Environment - // // 系统: 计算机\HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\Session Manager\Environment - // return Environment.GetEnvironmentVariable(var); - //} - ///// - ///// 设置系统环境变量 - ///// - ///// 变量名 - ///// 变量值 - //public static void SetEnv(string? var, string? value) - //{ - // // 创建、修改或删除当前进程中或者为当前用户或本地计算机保留的 Windows 操作系统注册表项中存储的环境变量 - // Environment.SetEnvironmentVariable(var, value); - //} + // /// + // /// 获取系统环境变量 + // /// + // /// 变量名 + // /// 指定的环境变量的值;或者如果找不到环境变量,则返回 null + // public static string? GetEnv(string? var) + // { + // // 从当前进程或者从当前用户或本地计算机的 Windows 操作系统注册表项检索环境变量的值 + // // 用户: 计算机\HKEY_CURRENT_USER\Environment + // // 系统: 计算机\HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\Session Manager\Environment + // return Environment.GetEnvironmentVariable(var); + // } + // /// + // /// 设置系统环境变量 + // /// + // /// 变量名 + // /// 变量值 + // public static void SetEnv(string? var, string? value) + // { + // // 创建、修改或删除当前进程中或者为当前用户或本地计算机保留的 Windows 操作系统注册表项中存储的环境变量 + // Environment.SetEnvironmentVariable(var, value); + // } #endregion @@ -589,14 +601,14 @@ public static DwgVersion GetDefaultDwgVersion() "36" => (DwgVersion)27,// 2007 dwg DwgVersion.AC1021 "37" => (DwgVersion)28,// 2007 dxf - //"38" => (DwgVersion),// dwt 样板文件...啊惊没找到这个是什么 + // "38" => (DwgVersion),// dwt 样板文件...啊惊没找到这个是什么 "48" => (DwgVersion)29,// 2010 dwg DwgVersion.AC1024 "49" => (DwgVersion)30,// 2010 dxf "60" => (DwgVersion)31,// 2013 dwg DwgVersion.AC1027 "61" => (DwgVersion)32,// 2013 dxf "64" => (DwgVersion)33,// 2018 dwg DwgVersion.AC1032 "65" => (DwgVersion)34,// 2018 dxf - _ => throw new NotImplementedException(),//提醒维护 + _ => throw new NotImplementedException(),// 提醒维护 }; return version; } diff --git a/src/IFoxCAD.Cad/IFoxCAD.Gcad/GlobalUsings.cs b/src/IFoxCAD.Cad/IFoxCAD.Gcad/GlobalUsings.cs index 0af0b20..c8d64c4 100644 --- a/src/IFoxCAD.Cad/IFoxCAD.Gcad/GlobalUsings.cs +++ b/src/IFoxCAD.Cad/IFoxCAD.Gcad/GlobalUsings.cs @@ -30,7 +30,7 @@ global using GrxCAD.DatabaseServices.Filters; global using GrxCAD; -//jig命名空间会引起Viewport/Polyline等等重义,最好逐个引入 using Autodesk.AutoCAD.GraphicsInterface +// jig命名空间会引起Viewport/Polyline等等重义,最好逐个引入 using Autodesk.AutoCAD.GraphicsInterface global using GrxCAD.GraphicsInterface; global using WorldDraw = GrxCAD.GraphicsInterface.WorldDraw; global using Manager = GrxCAD.GraphicsSystem.Manager; diff --git a/src/IFoxCAD.Cad/IFoxCAD.Zcad/GlobalUsings.cs b/src/IFoxCAD.Cad/IFoxCAD.Zcad/GlobalUsings.cs index 71111c5..721b5cb 100644 --- a/src/IFoxCAD.Cad/IFoxCAD.Zcad/GlobalUsings.cs +++ b/src/IFoxCAD.Cad/IFoxCAD.Zcad/GlobalUsings.cs @@ -29,7 +29,7 @@ global using ZwSoft.ZwCAD.DatabaseServices.Filters; global using ZwSoft.ZwCAD; -//jig命名空间会引起Viewport/Polyline等等重义,最好逐个引入 using ZwSoft.ZwCAD.GraphicsInterface +// jig命名空间会引起Viewport/Polyline等等重义,最好逐个引入 using ZwSoft.ZwCAD.GraphicsInterface global using ZwSoft.ZwCAD.GraphicsInterface; global using WorldDraw = ZwSoft.ZwCAD.GraphicsInterface.WorldDraw; global using Manager = ZwSoft.ZwCAD.GraphicsSystem.Manager; diff --git a/tests/TestConsole/Helper.cs b/tests/TestConsole/Helper.cs index 763424a..6fba766 100644 --- a/tests/TestConsole/Helper.cs +++ b/tests/TestConsole/Helper.cs @@ -8,15 +8,15 @@ public static void ForEach(this IEnumerable ints, Action act foreach (var item in ints) { action(item, state); - if (state.IsBreak) + if (!state.IsRun) break; } - //int forNum = 5; - //var result = Parallel.For(0, forNum, (int i, ParallelLoopState pls) => { - // if (i > 2) - // pls.Break(); - // Task.Delay(10).Wait(); - //}); + // int forNum = 5; + // var result = Parallel.For(0, forNum, (int i, ParallelLoopState pls) => { + // if (i > 2) + // pls.Break(); + // Task.Delay(10).Wait(); + // }); } } \ No newline at end of file diff --git a/tests/TestConsole/RuntimeHelpers.cs b/tests/TestConsole/RuntimeHelpers.cs index 310242d..fb91a15 100644 --- a/tests/TestConsole/RuntimeHelpers.cs +++ b/tests/TestConsole/RuntimeHelpers.cs @@ -20,7 +20,7 @@ public static T[] GetSubArray(T[] array, Range range) (int offset, int length) = range.GetOffsetAndLength(array.Length); - if (default(T)! != null || typeof(T[]) == array.GetType()) // TODO-NULLABLE: default(T) == null warning (https://github.com/dotnet/roslyn/issues/34757) + if (default(T)! != null || typeof(T[]) == array.GetType()) // NULLABLE: default(T) == null warning (https://github.com/dotnet/roslyn/issues/34757) { // We know the type of the array to be exactly T[]. @@ -28,8 +28,6 @@ public static T[] GetSubArray(T[] array, Range range) { // return Array.Empty(); return new T[0]; - - } var dest = new T[length]; @@ -46,5 +44,4 @@ public static T[] GetSubArray(T[] array, Range range) } } } - #endif \ No newline at end of file diff --git a/tests/TestShared/CmdINI.cs b/tests/TestShared/CmdINI.cs index 1370267..8faf2b8 100644 --- a/tests/TestShared/CmdINI.cs +++ b/tests/TestShared/CmdINI.cs @@ -165,7 +165,7 @@ public static Dictionary GetvarAll() var en = new SystemVariableEnumerator(); while (en.MoveNext()) { - Console.WriteLine(en.Current.Name + "-----" + en.Current.Value);//Value会出现异常 + Console.WriteLine(en.Current.Name + "-----" + en.Current.Value);// Value会出现异常 dict.Add(en.Current.Name, en.Current.Value); } return dict; diff --git a/tests/TestShared/TestJigExTransient.cs b/tests/TestShared/TestJigExTransient.cs index 15b8434..f4d341e 100644 --- a/tests/TestShared/TestJigExTransient.cs +++ b/tests/TestShared/TestJigExTransient.cs @@ -38,7 +38,7 @@ public void TestJigExTransient() // 由于c2进行了修改,所以需要更新, // 可以单个更新或更新整个瞬态容器 jet.Update(c2); - //jet.UpdateAll(); + // jet.UpdateAll(); var r4 = Env.Editor.GetPoint("\n此拾取无意义,仅为了暂停查看"); diff --git a/tests/TestShared/TestQuadTree.cs b/tests/TestShared/TestQuadTree.cs index 2047a29..e10c240 100644 --- a/tests/TestShared/TestQuadTree.cs +++ b/tests/TestShared/TestQuadTree.cs @@ -88,7 +88,7 @@ public void Test_QuadTree() // 随机图元生成 List ces = new(); // 用于随机获取图元 Timer.RunTime(() => { - // 生成外边界和随机圆形 + // 生成外边界和随机圆形 var grc = GenerateRandomCircle(maximumItems, dbExt); foreach (var ent in grc) { @@ -98,7 +98,7 @@ public void Test_QuadTree() // 四叉树数据 var entRect = new Rect(edge.MinPoint.X, edge.MinPoint.Y, edge.MaxPoint.X, edge.MaxPoint.Y); var entId = tr.CurrentSpace.AddEntity(ent); - + var ce = new CadEntity(entId, entRect) { Color = RandomEx.NextColor() @@ -143,8 +143,8 @@ public void Test_QuadTree() } } - /*TODO 啊惊: 有点懒不想改了*/ -#if true2 + /* 啊惊: 有点懒不想改了*/ +#if true2 // 选择加入到四叉树 [CommandMethod("CmdTest_QuadTree21")] @@ -196,7 +196,7 @@ public void CmdTest_QuadTree20() // 清空原有的 _quadTreeRoot = null; AddQuadTreeRoot(db, ed, ss); - } + } void AddQuadTreeRoot(Database db, Editor ed, List ss) { @@ -257,7 +257,7 @@ void AddQuadTreeRoot(Database db, Editor ed, List ss) #endregion - /*TODO 啊惊: 有点懒不想改了*/ + /* 啊惊: 有点懒不想改了*/ #if true2 #region 节点边界显示 -- Gitee From 2805c1740fd8de67a6b9db819aa4f07ef46a4728 Mon Sep 17 00:00:00 2001 From: Music_Die Date: Fri, 16 Sep 2022 10:15:43 +0800 Subject: [PATCH 504/675] =?UTF-8?q?=E4=BF=AE=E5=A4=8Dgetentitys=20?= =?UTF-8?q?=E6=97=A0=E6=B3=95=E6=89=93=E5=BC=80=E9=94=81=E5=AE=9A=E5=9B=BE?= =?UTF-8?q?=E5=B1=82=E4=B8=8A=E7=9A=84=E5=9B=BE=E5=85=83?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/IFoxCAD.Cad.Shared/ExtensionMethod/SymbolTableRecordEx.cs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/IFoxCAD.Cad.Shared/ExtensionMethod/SymbolTableRecordEx.cs b/src/IFoxCAD.Cad.Shared/ExtensionMethod/SymbolTableRecordEx.cs index c27f341..a85221f 100644 --- a/src/IFoxCAD.Cad.Shared/ExtensionMethod/SymbolTableRecordEx.cs +++ b/src/IFoxCAD.Cad.Shared/ExtensionMethod/SymbolTableRecordEx.cs @@ -330,13 +330,15 @@ public static ObjectId AddArc(this BlockTableRecord btr, /// 实体集合 public static IEnumerable GetEntities(this BlockTableRecord btr, OpenMode mode = OpenMode.ForRead, + bool openErased = false, + bool forceOpenOnLockedLayer = false, Transaction? trans = default) where T : Entity { trans ??= DBTrans.Top.Transaction; return btr .Cast() - .Select(id => trans.GetObject(id, mode)) + .Select(id => trans.GetObject(id, mode, openErased, forceOpenOnLockedLayer)) .OfType(); } -- Gitee From 0e6282a7bad22021844d8d56fd889f795bd2fb48 Mon Sep 17 00:00:00 2001 From: liuqihong <540762622@qq.com> Date: Fri, 16 Sep 2022 20:56:57 +0800 Subject: [PATCH 505/675] =?UTF-8?q?=E5=AE=B9=E9=87=8F=E4=BF=AE=E6=94=B9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/IFoxCAD.Cad.Shared/Runtime/Env.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/IFoxCAD.Cad.Shared/Runtime/Env.cs b/src/IFoxCAD.Cad.Shared/Runtime/Env.cs index 7c6b840..6de5354 100644 --- a/src/IFoxCAD.Cad.Shared/Runtime/Env.cs +++ b/src/IFoxCAD.Cad.Shared/Runtime/Env.cs @@ -522,7 +522,7 @@ public static string GetEnv(string? name) // 可用内存 (最新格式) 1 MB (标准格式) // https://docs.microsoft.com/zh-cn/windows/win32/sysinfo/registry-element-size-limits - var sbRes = new StringBuilder(1024 * 1024); + var sbRes = new StringBuilder(1 << 23); _ = AcedGetEnv(name, sbRes); return sbRes.ToString(); } -- Gitee From 2d29a8b97ec4bb205225937d0a684b8980bd2446 Mon Sep 17 00:00:00 2001 From: liuqihong <540762622@qq.com> Date: Fri, 16 Sep 2022 21:07:36 +0800 Subject: [PATCH 506/675] =?UTF-8?q?=E4=BF=AE=E6=94=B9=E9=A2=84=E5=A4=84?= =?UTF-8?q?=E7=90=86=E7=AC=A6=E5=8F=B7?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/IFoxCAD.Cad.Shared/ExtensionMethod/CurveEx.cs | 8 +++++--- src/IFoxCAD.Cad.Shared/ExtensionMethod/EntityEx.cs | 8 +++++--- src/IFoxCAD.Cad.Shared/ExtensionMethod/Filer/DwgFiler.cs | 3 +-- 3 files changed, 11 insertions(+), 8 deletions(-) diff --git a/src/IFoxCAD.Cad.Shared/ExtensionMethod/CurveEx.cs b/src/IFoxCAD.Cad.Shared/ExtensionMethod/CurveEx.cs index 9b7ae1b..9304ecc 100644 --- a/src/IFoxCAD.Cad.Shared/ExtensionMethod/CurveEx.cs +++ b/src/IFoxCAD.Cad.Shared/ExtensionMethod/CurveEx.cs @@ -112,12 +112,14 @@ public static IEnumerable GetAllCycle(this IEnumerable curves) var graph = new Graph(); foreach (var curve in curves) { -#if !acad - graph.AddEdge(curve.ToCurve3d()!); -#elif NET35 +#if !gcad +#if NET35 graph.AddEdge(curve.ToCurve3d()!); #else graph.AddEdge(curve.GetGeCurve()); +#endif +#else + graph.AddEdge(curve.ToCurve3d()!); #endif } // 新建 dfs diff --git a/src/IFoxCAD.Cad.Shared/ExtensionMethod/EntityEx.cs b/src/IFoxCAD.Cad.Shared/ExtensionMethod/EntityEx.cs index 8d9f04c..374f376 100644 --- a/src/IFoxCAD.Cad.Shared/ExtensionMethod/EntityEx.cs +++ b/src/IFoxCAD.Cad.Shared/ExtensionMethod/EntityEx.cs @@ -260,12 +260,14 @@ public static Arc CreateArc(Point3d startPoint, Point3d pointOnArc, Point3d endP // 创建一个几何类的圆弧对象 CircularArc3d geArc = new(startPoint, pointOnArc, endPoint); // 将几何类圆弧对象的圆心和半径赋值给圆弧 -#if !acad - return (Arc)geArc.ToCurve(); -#elif NET35 +#if !gcad +#if NET35 return (Arc)geArc.ToCurve(); #else return (Arc)Curve.CreateFromGeCurve(geArc); +#endif +#else + return (Arc)geArc.ToCurve(); #endif } diff --git a/src/IFoxCAD.Cad.Shared/ExtensionMethod/Filer/DwgFiler.cs b/src/IFoxCAD.Cad.Shared/ExtensionMethod/Filer/DwgFiler.cs index 9cc99f6..871cbdd 100644 --- a/src/IFoxCAD.Cad.Shared/ExtensionMethod/Filer/DwgFiler.cs +++ b/src/IFoxCAD.Cad.Shared/ExtensionMethod/Filer/DwgFiler.cs @@ -353,8 +353,7 @@ public override void Seek(int offset, int method) } public override void Seek(long offset, int method) { - var ed = Acap.DocumentManager.MdiActiveDocument.Editor; - ed.WriteMessage(MethodInfo.GetCurrentMethod().Name + " = " + " \n "); + Seek((int)offset, method); } #endif -- Gitee From ba773c42f5b63154efd55852902d6968294b7a9e Mon Sep 17 00:00:00 2001 From: liuqihong <540762622@qq.com> Date: Fri, 16 Sep 2022 21:54:07 +0800 Subject: [PATCH 507/675] =?UTF-8?q?=E5=B0=86=E6=89=80=E6=9C=89=E7=9A=84Get?= =?UTF-8?q?Object=E6=9A=B4=E9=9C=B2=E5=85=A8=E9=83=A8=E5=8F=82=E6=95=B0=20?= =?UTF-8?q?DIE=E7=9A=84=E4=BF=AE=E6=94=B9=E6=9C=89=E7=A0=B4=E5=9D=8F?= =?UTF-8?q?=E6=80=A7=E6=9B=B4=E6=96=B0,=E6=9C=AC=E6=AC=A1=E5=B0=86?= =?UTF-8?q?=E5=8F=82=E6=95=B0=E8=B0=83=E6=95=B4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../ExtensionMethod/CurveEx.cs | 5 +- .../ExtensionMethod/EditorEx.cs | 10 ++- .../ExtensionMethod/ObjectIdEx.cs | 30 +++++---- .../ExtensionMethod/SelectionSetEx.cs | 66 +++++++++++++++---- .../ExtensionMethod/SymbolTableRecordEx.cs | 35 ++++++---- .../HatchConverter.cs" | 9 ++- src/IFoxCAD.Cad.Shared/Runtime/DBTrans.cs | 10 +-- src/IFoxCAD.Cad.Shared/Runtime/SymbolTable.cs | 16 ++--- 8 files changed, 123 insertions(+), 58 deletions(-) diff --git a/src/IFoxCAD.Cad.Shared/ExtensionMethod/CurveEx.cs b/src/IFoxCAD.Cad.Shared/ExtensionMethod/CurveEx.cs index 9304ecc..c55b289 100644 --- a/src/IFoxCAD.Cad.Shared/ExtensionMethod/CurveEx.cs +++ b/src/IFoxCAD.Cad.Shared/ExtensionMethod/CurveEx.cs @@ -558,8 +558,9 @@ public static PolylineCurve3d ToPolylineCurve3d(this Polyline3d pl) Point3dCollection pnts = new(); foreach (ObjectId id in pl) { - PolylineVertex3d ver = (PolylineVertex3d)id.GetObject(OpenMode.ForRead); - pnts.Add(ver.Position); + var ver = id.GetObject(OpenMode.ForRead); + if (ver != null) + pnts.Add(ver.Position); } return new PolylineCurve3d(pnts); } diff --git a/src/IFoxCAD.Cad.Shared/ExtensionMethod/EditorEx.cs b/src/IFoxCAD.Cad.Shared/ExtensionMethod/EditorEx.cs index 0fcc643..535749d 100644 --- a/src/IFoxCAD.Cad.Shared/ExtensionMethod/EditorEx.cs +++ b/src/IFoxCAD.Cad.Shared/ExtensionMethod/EditorEx.cs @@ -659,15 +659,19 @@ public static Matrix3d GetMatrixFromMDcsToPDcs(this Editor editor) Database db = editor.Document.Database; Matrix3d mat; - using (Transaction tr = db.TransactionManager.StartTransaction()) + using (var tr = db.TransactionManager.StartTransaction()) { - var vp = tr.GetObject(editor.CurrentViewportObjectId, OpenMode.ForRead) as Viewport; + var openErased = false; + var openLockedLayer = false; + var vp = tr.GetObject(editor.CurrentViewportObjectId, OpenMode.ForRead, + openErased, openLockedLayer) as Viewport; if (vp?.Number == 1) { try { editor.SwitchToModelSpace(); - vp = tr.GetObject(editor.CurrentViewportObjectId, OpenMode.ForRead) as Viewport; + vp = tr.GetObject(editor.CurrentViewportObjectId, OpenMode.ForRead, + openErased, openLockedLayer) as Viewport; editor.SwitchToPaperSpace(); } catch diff --git a/src/IFoxCAD.Cad.Shared/ExtensionMethod/ObjectIdEx.cs b/src/IFoxCAD.Cad.Shared/ExtensionMethod/ObjectIdEx.cs index 92e8b0f..d4efe95 100644 --- a/src/IFoxCAD.Cad.Shared/ExtensionMethod/ObjectIdEx.cs +++ b/src/IFoxCAD.Cad.Shared/ExtensionMethod/ObjectIdEx.cs @@ -12,15 +12,19 @@ public static class ObjectIdEx ///
    /// 指定的泛型 /// 对象id - /// 事务 /// 打开模式 - /// 打开删除对象 + /// 事务 + /// 是否打开已删除对象,默认为不打开 + /// 是否打开锁定图层对象,默认为不打开 /// 指定类型对象 - public static T? GetObject(this ObjectId id, OpenMode mode = OpenMode.ForRead, bool openErased = false, Transaction? tr = default) where T : DBObject + public static T? GetObject(this ObjectId id, + OpenMode mode = OpenMode.ForRead, + Transaction? tr = default, + bool openErased = false, + bool openLockedLayer = false) where T : DBObject { tr ??= DBTrans.Top.Transaction; - // tr = Env.GetTrans(tr); - return tr.GetObject(id, mode, openErased) as T; + return tr.GetObject(id, mode, openErased, openLockedLayer) as T; } /// @@ -28,16 +32,18 @@ public static class ObjectIdEx /// /// 指定的泛型 /// 对象id集合 - /// 事务 /// 打开模式 - /// 打开删除对象 + /// 事务 + /// 是否打开已删除对象,默认为不打开 + /// 是否打开锁定图层对象,默认为不打开 /// 指定类型对象集合 - public static IEnumerable GetObject(this IEnumerable ids, + public static IEnumerable GetObject(this IEnumerable ids, OpenMode mode = OpenMode.ForRead, - bool openErased = false, - Transaction? tr = default) where T : DBObject + Transaction? tr = default, + bool openErased = false, + bool openLockedLayer = false) where T : DBObject { - return ids.Select(id => id.GetObject(mode, openErased, tr)); + return ids.Select(id => id.GetObject(mode, tr, openErased, openLockedLayer)); } /// @@ -89,4 +95,4 @@ public static void Erase(this ObjectId id) // Env.Editor.Regen(); } } -} +} \ No newline at end of file diff --git a/src/IFoxCAD.Cad.Shared/ExtensionMethod/SelectionSetEx.cs b/src/IFoxCAD.Cad.Shared/ExtensionMethod/SelectionSetEx.cs index dbde0ac..d63d5bd 100644 --- a/src/IFoxCAD.Cad.Shared/ExtensionMethod/SelectionSetEx.cs +++ b/src/IFoxCAD.Cad.Shared/ExtensionMethod/SelectionSetEx.cs @@ -63,22 +63,25 @@ public static IEnumerable> GetObjectIdGroup(this Sel /// /// 指定类型 /// 选择集 - /// 事务 /// 打开模式 + /// 事务 + /// 是否打开已删除对象,默认为不打开 + /// 是否打开锁定图层对象,默认为不打开 /// 图元集合 - public static IEnumerable GetEntities(this SelectionSet ss, OpenMode openMode = OpenMode.ForRead, DBTrans? tr = default) where T : Entity + public static IEnumerable GetEntities(this SelectionSet ss, + OpenMode openMode = OpenMode.ForRead, + DBTrans? tr = default, + bool openErased = false, + bool openLockedLayer = false) where T : Entity { if (ss is null) throw new ArgumentNullException(nameof(ss)); tr ??= DBTrans.Top; - return - ss - .GetObjectIds() - .Select(id => tr.GetObject(id, openMode)) - .Where(ent => ent != null); + return ss.GetObjectIds() + .Select(id => tr.GetObject(id, openMode, openErased, openLockedLayer)) + .Where(ent => ent != null); } - #endregion #region ForEach @@ -87,15 +90,52 @@ public static IEnumerable> GetObjectIdGroup(this Sel /// /// 指定图元类型 /// 选择集 + /// 处理函数;(图元) + /// 打开模式 /// 事务 + /// 是否打开已删除对象,默认为不打开 + /// 是否打开锁定图层对象,默认为不打开 + public static void ForEach(this SelectionSet ss, + Action action, + OpenMode openMode = OpenMode.ForRead, + DBTrans? tr = default, + bool openErased = false, + bool openLockedLayer = false) where T : Entity + { + ForEach(ss, (ent, state) => { + action.Invoke(ent); + }, openMode, tr, openErased, openLockedLayer); + } + + /// + /// 遍历选择集 + /// + /// 指定图元类型 + /// 选择集 + /// 处理函数;(图元,终止方式) /// 打开模式 - /// 处理函数 - public static void ForEach(this SelectionSet ss, Action action, OpenMode openMode = OpenMode.ForRead, DBTrans? tr = default) where T : Entity + /// 事务 + /// 是否打开已删除对象,默认为不打开 + /// 是否打开锁定图层对象,默认为不打开 + /// + public static void ForEach(this SelectionSet ss, + Action action, + OpenMode openMode = OpenMode.ForRead, + DBTrans? tr = default, + bool openErased = false, + bool openLockedLayer = false) where T : Entity { - foreach (T? ent in ss.GetEntities(openMode, tr)) + if (action == null) + throw new ArgumentNullException(nameof(action)); + + LoopState state = new(); + var ents = ss.GetEntities(openMode, tr, openErased, openLockedLayer); + foreach (var ent in ents) { - action?.Invoke(ent); + action.Invoke(ent, state); + if (!state.IsRun) + break; } } #endregion -} +} \ No newline at end of file diff --git a/src/IFoxCAD.Cad.Shared/ExtensionMethod/SymbolTableRecordEx.cs b/src/IFoxCAD.Cad.Shared/ExtensionMethod/SymbolTableRecordEx.cs index a85221f..1648583 100644 --- a/src/IFoxCAD.Cad.Shared/ExtensionMethod/SymbolTableRecordEx.cs +++ b/src/IFoxCAD.Cad.Shared/ExtensionMethod/SymbolTableRecordEx.cs @@ -327,18 +327,20 @@ public static ObjectId AddArc(this BlockTableRecord btr, /// 块表记录 /// 打开模式 /// 事务 + /// 是否打开已删除对象,默认为不打开 + /// 是否打开锁定图层对象,默认为不打开 /// 实体集合 public static IEnumerable GetEntities(this BlockTableRecord btr, OpenMode mode = OpenMode.ForRead, + Transaction? trans = default, bool openErased = false, - bool forceOpenOnLockedLayer = false, - Transaction? trans = default) where T : Entity + bool openLockedLayer = false) where T : Entity { trans ??= DBTrans.Top.Transaction; return btr .Cast() - .Select(id => trans.GetObject(id, mode, openErased, forceOpenOnLockedLayer)) + .Select(id => trans.GetObject(id, mode, openErased, openLockedLayer)) .OfType(); } @@ -352,7 +354,7 @@ public static IEnumerable GetObjectIds(this BlockTableRecord btr) w { string dxfName = RXClass.GetClass(typeof(T)).DxfName; return btr.Cast() - .Where(id => id.ObjectClass().DxfName == dxfName); + .Where(id => id.ObjectClass().DxfName == dxfName); } /// @@ -362,23 +364,27 @@ public static IEnumerable GetObjectIds(this BlockTableRecord btr) w /// 实体Id分组 public static IEnumerable> GetObjectIds(this BlockTableRecord btr) { - return - btr - .Cast() - .GroupBy(id => id.ObjectClass().DxfName); + return btr.Cast() + .GroupBy(id => id.ObjectClass().DxfName); } + /// /// 获取绘制顺序表 /// /// 块表 /// 事务 + /// 是否打开已删除对象,默认为不打开 + /// 是否打开锁定图层对象,默认为不打开 /// 绘制顺序表 public static DrawOrderTable? GetDrawOrderTable(this BlockTableRecord btr, - Transaction? trans = default) + Transaction? trans = default, + bool openErased = false, + bool openLockedLayer = false) { trans ??= DBTrans.Top.Transaction; - return trans.GetObject(btr.DrawOrderTableId, OpenMode.ForRead) as DrawOrderTable; + return trans.GetObject(btr.DrawOrderTableId, OpenMode.ForRead, + openErased, openLockedLayer) as DrawOrderTable; } #endregion @@ -398,7 +404,8 @@ public static ObjectId InsertBlock(this BlockTableRecord blockTableRecord, Point string blockName, Scale3d scale = default, double rotation = default, - Dictionary? atts = default, Transaction? trans = null) + Dictionary? atts = default, + Transaction? trans = null) { trans ??= DBTrans.Top.Transaction; if (!DBTrans.Top.BlockTable.Has(blockName)) @@ -417,11 +424,13 @@ public static ObjectId InsertBlock(this BlockTableRecord blockTableRecord, Point /// 块插入旋转角(弧度),默认为0 /// 属性字典{Tag,Value},默认为null /// 块参照对象id - public static ObjectId InsertBlock(this BlockTableRecord blockTableRecord, Point3d position, + public static ObjectId InsertBlock(this BlockTableRecord blockTableRecord, + Point3d position, ObjectId blockId, Scale3d scale = default, double rotation = default, - Dictionary? atts = default, Transaction? trans = null) + Dictionary? atts = default, + Transaction? trans = null) { trans ??= DBTrans.Top.Transaction; if (!DBTrans.Top.BlockTable.Has(blockId)) diff --git "a/src/IFoxCAD.Cad.Shared/ExtensionMethod/\346\226\260\345\273\272\345\241\253\345\205\205/HatchConverter.cs" "b/src/IFoxCAD.Cad.Shared/ExtensionMethod/\346\226\260\345\273\272\345\241\253\345\205\205/HatchConverter.cs" index 27d4bfe..4d2ba7c 100644 --- "a/src/IFoxCAD.Cad.Shared/ExtensionMethod/\346\226\260\345\273\272\345\241\253\345\205\205/HatchConverter.cs" +++ "b/src/IFoxCAD.Cad.Shared/ExtensionMethod/\346\226\260\345\273\272\345\241\253\345\205\205/HatchConverter.cs" @@ -314,9 +314,14 @@ public ObjectId CreateBoundarysAndHatchToMsPs(BlockTableRecord btrOfAddEntitySpa * 那么它的平移后的基点在哪里呢? */ - var newHatchId = btrOfAddEntitySpace.DeepCloneEx(new ObjectIdCollection(new ObjectId[] { OldHatchId })).GetValues()[0]; + var newHatchId = btrOfAddEntitySpace.DeepCloneEx( + new ObjectIdCollection(new ObjectId[] { OldHatchId })).GetValues()[0]; trans ??= DBTrans.Top.Transaction; - var hatchEnt = trans.GetObject(newHatchId, OpenMode.ForWrite) as Hatch; + + bool openErased = false; + bool openLockedLayer = false; + var hatchEnt = trans.GetObject(newHatchId, OpenMode.ForWrite, + openErased, openLockedLayer) as Hatch; if (hatchEnt != null) { ResetBoundary(hatchEnt, boundaryAssociative); diff --git a/src/IFoxCAD.Cad.Shared/Runtime/DBTrans.cs b/src/IFoxCAD.Cad.Shared/Runtime/DBTrans.cs index 153b539..7be12c5 100644 --- a/src/IFoxCAD.Cad.Shared/Runtime/DBTrans.cs +++ b/src/IFoxCAD.Cad.Shared/Runtime/DBTrans.cs @@ -352,14 +352,14 @@ public static implicit operator Transaction(DBTrans tr) /// 对象id /// 打开模式,默认为只读 /// 是否打开已删除对象,默认为不打开 - /// 是否打开锁定图层对象,默认为不打开 + /// 是否打开锁定图层对象,默认为不打开 /// 图元对象,类型不匹配时返回 public T? GetObject(ObjectId id, - OpenMode mode = OpenMode.ForRead, - bool openErased = false, - bool forceOpenOnLockedLayer = false) where T : DBObject + OpenMode mode = OpenMode.ForRead, + bool openErased = false, + bool openLockedLayer = false) where T : DBObject { - return Transaction.GetObject(id, mode, openErased, forceOpenOnLockedLayer) as T; + return Transaction.GetObject(id, mode, openErased, openLockedLayer) as T; } /// diff --git a/src/IFoxCAD.Cad.Shared/Runtime/SymbolTable.cs b/src/IFoxCAD.Cad.Shared/Runtime/SymbolTable.cs index 8e0efba..a33c167 100644 --- a/src/IFoxCAD.Cad.Shared/Runtime/SymbolTable.cs +++ b/src/IFoxCAD.Cad.Shared/Runtime/SymbolTable.cs @@ -36,15 +36,15 @@ internal SymbolTable(DBTrans tr, ObjectId tableId, bool defaultBehavior = true) Database = tr.Database; CurrentSymbolTable = DTrans.GetObject(tableId)!; - if (defaultBehavior) + if (!defaultBehavior) + return; + + if (CurrentSymbolTable is LayerTable layer) { - if (CurrentSymbolTable is LayerTable layer) - { - // 层表包含隐藏的,全部显示出来 - layer = layer.IncludingHidden; - if (layer is TTable tt) - CurrentSymbolTable = tt; - } + // 层表包含隐藏的,全部显示出来 + layer = layer.IncludingHidden; + if (layer is TTable tt) + CurrentSymbolTable = tt; } } -- Gitee From 520e5f81dcdeb2ccbde9fdad2c6376cce4a24109 Mon Sep 17 00:00:00 2001 From: liuqihong <540762622@qq.com> Date: Sun, 18 Sep 2022 23:42:08 +0800 Subject: [PATCH 508/675] =?UTF-8?q?=E4=BF=AE=E5=A4=8Dvs=E5=87=BA=E7=8E=B0?= =?UTF-8?q?=E7=9A=84=E6=8F=90=E7=A4=BA:=20=E5=BD=93=E5=89=8D=E4=B8=8A?= =?UTF-8?q?=E4=B8=8B=E6=96=87=E4=B8=AD=E4=B8=8D=E5=AD=98=E5=9C=A8=E5=90=8D?= =?UTF-8?q?=E7=A7=B0=E2=80=9CInitializeComponent=E2=80=9D=20=E8=BF=99?= =?UTF-8?q?=E6=98=AF=E5=9B=A0=E4=B8=BA=E7=BC=BA=E5=B0=91=E4=BA=86Designer.?= =?UTF-8?q?cs=E6=96=87=E4=BB=B6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Properties/Resources.Designer.cs | 63 +++++++++++++++++++ 1 file changed, 63 insertions(+) create mode 100644 src/IFoxCAD.WPF/Properties/Resources.Designer.cs diff --git a/src/IFoxCAD.WPF/Properties/Resources.Designer.cs b/src/IFoxCAD.WPF/Properties/Resources.Designer.cs new file mode 100644 index 0000000..93cd9be --- /dev/null +++ b/src/IFoxCAD.WPF/Properties/Resources.Designer.cs @@ -0,0 +1,63 @@ +//------------------------------------------------------------------------------ +// +// 此代码由工具生成。 +// 运行时版本:4.0.30319.42000 +// +// 对此文件的更改可能会导致不正确的行为,并且如果 +// 重新生成代码,这些更改将会丢失。 +// +//------------------------------------------------------------------------------ + +namespace IFoxCAD.WPF.Properties { + using System; + + + /// + /// 一个强类型的资源类,用于查找本地化的字符串等。 + /// + // 此类是由 StronglyTypedResourceBuilder + // 类通过类似于 ResGen 或 Visual Studio 的工具自动生成的。 + // 若要添加或移除成员,请编辑 .ResX 文件,然后重新运行 ResGen + // (以 /str 作为命令选项),或重新生成 VS 项目。 + [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "16.0.0.0")] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] + internal class Resources { + + private static global::System.Resources.ResourceManager resourceMan; + + private static global::System.Globalization.CultureInfo resourceCulture; + + [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")] + internal Resources() { + } + + /// + /// 返回此类使用的缓存的 ResourceManager 实例。 + /// + [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] + internal static global::System.Resources.ResourceManager ResourceManager { + get { + if (object.ReferenceEquals(resourceMan, null)) { + global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("IFoxCAD.WPF.Properties.Resources", typeof(Resources).Assembly); + resourceMan = temp; + } + return resourceMan; + } + } + + /// + /// 重写当前线程的 CurrentUICulture 属性,对 + /// 使用此强类型资源类的所有资源查找执行重写。 + /// + [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] + internal static global::System.Globalization.CultureInfo Culture { + get { + return resourceCulture; + } + set { + resourceCulture = value; + } + } + } +} -- Gitee From 8af93f35fa4c9c7041714bfc37bc5b9ad2909f5b Mon Sep 17 00:00:00 2001 From: liuqihong <540762622@qq.com> Date: Sun, 18 Sep 2022 23:46:28 +0800 Subject: [PATCH 509/675] =?UTF-8?q?=E6=92=A4=E5=9B=9E=E9=94=99=E8=AF=AF?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Properties/Resources.Designer.cs | 63 ------------------- 1 file changed, 63 deletions(-) delete mode 100644 src/IFoxCAD.WPF/Properties/Resources.Designer.cs diff --git a/src/IFoxCAD.WPF/Properties/Resources.Designer.cs b/src/IFoxCAD.WPF/Properties/Resources.Designer.cs deleted file mode 100644 index 93cd9be..0000000 --- a/src/IFoxCAD.WPF/Properties/Resources.Designer.cs +++ /dev/null @@ -1,63 +0,0 @@ -//------------------------------------------------------------------------------ -// -// 此代码由工具生成。 -// 运行时版本:4.0.30319.42000 -// -// 对此文件的更改可能会导致不正确的行为,并且如果 -// 重新生成代码,这些更改将会丢失。 -// -//------------------------------------------------------------------------------ - -namespace IFoxCAD.WPF.Properties { - using System; - - - /// - /// 一个强类型的资源类,用于查找本地化的字符串等。 - /// - // 此类是由 StronglyTypedResourceBuilder - // 类通过类似于 ResGen 或 Visual Studio 的工具自动生成的。 - // 若要添加或移除成员,请编辑 .ResX 文件,然后重新运行 ResGen - // (以 /str 作为命令选项),或重新生成 VS 项目。 - [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "16.0.0.0")] - [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] - [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] - internal class Resources { - - private static global::System.Resources.ResourceManager resourceMan; - - private static global::System.Globalization.CultureInfo resourceCulture; - - [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")] - internal Resources() { - } - - /// - /// 返回此类使用的缓存的 ResourceManager 实例。 - /// - [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] - internal static global::System.Resources.ResourceManager ResourceManager { - get { - if (object.ReferenceEquals(resourceMan, null)) { - global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("IFoxCAD.WPF.Properties.Resources", typeof(Resources).Assembly); - resourceMan = temp; - } - return resourceMan; - } - } - - /// - /// 重写当前线程的 CurrentUICulture 属性,对 - /// 使用此强类型资源类的所有资源查找执行重写。 - /// - [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] - internal static global::System.Globalization.CultureInfo Culture { - get { - return resourceCulture; - } - set { - resourceCulture = value; - } - } - } -} -- Gitee From ee093d8764774f045e5e8c5d365a5b8ce08bc546 Mon Sep 17 00:00:00 2001 From: liuqihong <540762622@qq.com> Date: Mon, 19 Sep 2022 02:15:29 +0800 Subject: [PATCH 510/675] =?UTF-8?q?=E6=9B=B4=E6=96=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- ...4\273\243\347\240\201\350\247\204\350\214\203.md" | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git "a/docs/0x01\344\273\243\347\240\201\350\247\204\350\214\203.md" "b/docs/0x01\344\273\243\347\240\201\350\247\204\350\214\203.md" index 4e0c680..e00e396 100644 --- "a/docs/0x01\344\273\243\347\240\201\350\247\204\350\214\203.md" +++ "b/docs/0x01\344\273\243\347\240\201\350\247\204\350\214\203.md" @@ -74,19 +74,27 @@ if() 但是,由于vs没有制作好的原因,导致`委托箭头`代码格式化总是会出现格式化错误. 所以我们推荐用 .editorconfig 文件约束这个`委托箭头` +在.edirorconfig文件上面增加此句: + +``` +csharp_style_var_elsewhere = false +``` + +没有这个文件的话,请使用如下步骤: + ```mermaid graph LR vs --> 选项 --> 文本编辑器 --> c# -->代码样式,展开它 --> 格式设置 --> 新行 --> 右页,大括号的新行选项 --> 将lambda表达式的左括号置于新行,取消掉勾勾 ``` -保存为 .editorconfig 文件: +保存为 .editorconfig 文件,并放在.sln旁边,加入git管理: ```mermaid graph LR vs --> 选项 --> 文本编辑器 --> c# -->代码样式 --> 右页,基于设置生成.editorconfig文件 --> 保存到工程中 ``` -这样每次打开工程vs会自动识别这个 .editorconfig 文件,而不会用你电脑默认设置的. +以后每次打开工程vs会自动识别这个 .editorconfig 文件,而不会用你电脑默认设置的. -- Gitee From ff01bf21ea3ea90bb157e32ad9d08c495795f463 Mon Sep 17 00:00:00 2001 From: liuqihong <540762622@qq.com> Date: Mon, 19 Sep 2022 18:54:06 +0800 Subject: [PATCH 511/675] =?UTF-8?q?=E5=A2=9E=E5=8A=A0cad=E5=89=AA=E8=B4=B4?= =?UTF-8?q?=E6=9D=BF?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../ExtensionMethod/DBObjectEx.cs | 2 +- .../HatchConverter.cs" | 3 +- src/IFoxCAD.Cad.Shared/Runtime/DBTrans.cs | 36 +++-- ...ad\345\211\252\350\264\264\346\235\277.cs" | 139 ++++++++++++++++++ 4 files changed, 163 insertions(+), 17 deletions(-) create mode 100644 "tests/TestShared/cad\345\211\252\350\264\264\346\235\277.cs" diff --git a/src/IFoxCAD.Cad.Shared/ExtensionMethod/DBObjectEx.cs b/src/IFoxCAD.Cad.Shared/ExtensionMethod/DBObjectEx.cs index a8590f5..14d9c9e 100644 --- a/src/IFoxCAD.Cad.Shared/ExtensionMethod/DBObjectEx.cs +++ b/src/IFoxCAD.Cad.Shared/ExtensionMethod/DBObjectEx.cs @@ -176,4 +176,4 @@ public void Dispose() #endregion IDisposable 成员 } #endregion -} +} \ No newline at end of file diff --git "a/src/IFoxCAD.Cad.Shared/ExtensionMethod/\346\226\260\345\273\272\345\241\253\345\205\205/HatchConverter.cs" "b/src/IFoxCAD.Cad.Shared/ExtensionMethod/\346\226\260\345\273\272\345\241\253\345\205\205/HatchConverter.cs" index 4d2ba7c..62389db 100644 --- "a/src/IFoxCAD.Cad.Shared/ExtensionMethod/\346\226\260\345\273\272\345\241\253\345\205\205/HatchConverter.cs" +++ "b/src/IFoxCAD.Cad.Shared/ExtensionMethod/\346\226\260\345\273\272\345\241\253\345\205\205/HatchConverter.cs" @@ -283,8 +283,7 @@ public void CreateBoundaryEntitys(List outEnts) /// /// 创建边界图元和新填充到当前空间 /// - /// 事务 - /// 数据库 + /// /// 边界关联 /// 是否创建填充,false则只创建边界 /// 新填充id,边界在获取 diff --git a/src/IFoxCAD.Cad.Shared/Runtime/DBTrans.cs b/src/IFoxCAD.Cad.Shared/Runtime/DBTrans.cs index 7be12c5..22bc33b 100644 --- a/src/IFoxCAD.Cad.Shared/Runtime/DBTrans.cs +++ b/src/IFoxCAD.Cad.Shared/Runtime/DBTrans.cs @@ -425,7 +425,8 @@ public void SaveFile(DwgVersion version = DwgVersion.AC1800, if (string.IsNullOrEmpty(saveAsFile)) { fileMsg = _fileName; - creatFlag = true; + saveAsFile = _fileName; + //creatFlag = true; } else { @@ -448,9 +449,16 @@ public void SaveFile(DwgVersion version = DwgVersion.AC1800, string.IsNullOrEmpty(Path.GetFileName(saveAsFile).Trim())) creatFlag = true; } - var fileNameWith = Path.GetFileNameWithoutExtension(saveAsFile).Trim(); - if (string.IsNullOrEmpty(fileNameWith)) + if (saveAsFile != null) + { + var fileNameWith = Path.GetFileNameWithoutExtension(saveAsFile).Trim(); + if (string.IsNullOrEmpty(fileNameWith)) + creatFlag = true; + } + else + { creatFlag = true; + } if (creatFlag) { @@ -470,18 +478,18 @@ public void SaveFile(DwgVersion version = DwgVersion.AC1800, #if zcad // 中望这里没有测试 Database.DxfOut(saveAsFile, 7, version, true); #endif + return; } - else - { - if (automatic) - version = Env.GetDefaultDwgVersion(); - - // dwg需要版本号,而dxf不用,dwg用dxf版本号会报错 - // 若扩展名和版本号冲突,按照扩展名为准 - if (version.IsDxfVersion()) - version = DwgVersion.Current; - Database.SaveAs(saveAsFile, version); - } + + if (automatic) + version = Env.GetDefaultDwgVersion(); + + // dwg需要版本号,而dxf不用,dwg用dxf版本号会报错 + // 若扩展名和版本号冲突,按照扩展名为准 + if (version.IsDxfVersion()) + version = DwgVersion.Current; + + Database.SaveAs(saveAsFile, version); } diff --git "a/tests/TestShared/cad\345\211\252\350\264\264\346\235\277.cs" "b/tests/TestShared/cad\345\211\252\350\264\264\346\235\277.cs" new file mode 100644 index 0000000..ad190c1 --- /dev/null +++ "b/tests/TestShared/cad\345\211\252\350\264\264\346\235\277.cs" @@ -0,0 +1,139 @@ +using Clipboard = System.Windows.Forms.Clipboard; + +namespace JoinBoxAcad; +// https://forums.autodesk.com/t5/net/paste-list-of-objects-from-clipboard-on-dwg-file-using-c-net/td-p/6797606 +public class cad剪贴板 +{ + //[IFoxInitialize] + public void Init() + { + var doc = Acap.DocumentManager.MdiActiveDocument; + doc.CommandWillStart += Doc_CommandWillStart; + } + + void Doc_CommandWillStart(object sender, CommandEventArgs e) + { + // 粘贴命令 + if (e.GlobalCommandName != "PASTECLIP") + return; + + // 获取剪贴板上面的保存路径 + var format = Clipboard.GetDataObject() + .GetFormats() + .FirstOrDefault(f => f.StartsWith("AutoCAD")); + + if (string.IsNullOrEmpty(format)) + return; + + string fileName; + var data = (MemoryStream)Clipboard.GetData(format); + using (var reader = new StreamReader(data)) + { + fileName = reader.ReadToEnd(); + fileName = fileName.Replace("\0", string.Empty); + fileName = fileName.Substring(0, fileName.IndexOf(".DWG") + 4); + } + if (string.IsNullOrEmpty(Path.GetFileName(fileName))) + return; + + using var tr = new DBTrans(fileName, true, FileOpenMode.OpenForReadAndAllShare); + foreach (var id in tr.ModelSpace) + { + if (!id.IsOk()) + continue; + var ent = tr.GetObject(id, OpenMode.ForWrite); + ent!.ColorIndex = 30; + } + tr.SaveFile((DwgVersion)27); + } + + [CommandMethod(nameof(Ccopy), CommandFlags.UsePickSet)] + public void Ccopy() + { + var dm = Acap.DocumentManager; + var doc = dm.MdiActiveDocument; + var sdb = doc.Database; + var ed = doc.Editor; + + List result = new(); + var psr = ed.SelectImplied();// 预选 + if (psr.Status != PromptStatus.OK) + { + psr = ed.GetSelection(); + if (psr.Status != PromptStatus.OK) + return; + } + result.AddRange(psr.Value.GetObjectIds()); + + // 获取临时路径 + // var path = Environment.GetFolderPath(Environment.SpecialFolder.DesktopDirectory) + "\\" + var path = Path.GetTempPath() + + DateTime.Now.ToString("yyyyMMddHHmmssfff") + ".DWG"; + + // 写入剪贴板 + var data = new MemoryStream(); + var bytes = Encoding.Unicode.GetBytes(path); + data.Write(bytes, 0, bytes.Length); + Clipboard.SetData($"AutoCAD.r{Acap.Version.Major}", data); + + // 克隆到目标块表内 + using var tr2 = new DBTrans(path); + tr2.Task(() => { + var map = new IdMapping(); + sdb.WblockCloneObjects( + new ObjectIdCollection(result.ToArray()), + tr2.ModelSpace.ObjectId, + map, + DuplicateRecordCloning.Replace, + false); + }); + tr2.SaveFile(); + } + + [CommandMethod(nameof(NewPaste), CommandFlags.Transparent)] + public void NewPaste() + { + var doc = Acap.DocumentManager.MdiActiveDocument; + var obj = (System.Windows.Forms.DataObject)Clipboard.GetDataObject(); + if (obj == null) + return; + + // Find out whether the clipboard contains AutoCAD data + var formats = obj.GetFormats(); + string formatFound = ""; + bool foundDwg = false; + foreach (var name in formats) + { + if (name.Contains("AutoCAD.r")) + { + foundDwg = true; + formatFound = name; + break; + } + } + if (foundDwg) + { + // If Found, discover where is the Database + var MStr = obj.GetData(formatFound) as MemoryStream; + if (MStr == null) + return; + + MStr.Position = 0; + var sr = new StreamReader(MStr, Encoding.UTF8); + var myStr = sr.ReadToEnd(); + + //删除不必要的字符 + var sda = myStr.Replace("\0", ""); + int index = sda.IndexOf(".DWG"); + if (index > 0) + sda = sda.Substring(0, index + 4); + + doc.Editor.WriteMessage(sda); + } + else + { + // If not, pasteclip normally + doc.SendStringToExecute("\x1B\x1B_.PASTECLIP ", true, false, true); + } + } +} \ No newline at end of file -- Gitee From d83ae5275bc09f9a33c4aea2bec89e594effc43a Mon Sep 17 00:00:00 2001 From: liuqihong <540762622@qq.com> Date: Tue, 20 Sep 2022 03:39:16 +0800 Subject: [PATCH 512/675] =?UTF-8?q?=E4=BF=AE=E5=A4=8D=E4=BA=86=E4=B8=80?= =?UTF-8?q?=E4=BA=9Bbug?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/IFoxCAD.Cad.Shared/Runtime/DBTrans.cs | 14 +- .../TestShared/Copyclip.cs | 199 +++++++++++------- tests/TestShared/TestJig.cs | 60 +++--- tests/TestShared/TestShared.projitems | 1 + 4 files changed, 164 insertions(+), 110 deletions(-) rename "tests/TestShared/cad\345\211\252\350\264\264\346\235\277.cs" => tests/TestShared/Copyclip.cs (36%) diff --git a/src/IFoxCAD.Cad.Shared/Runtime/DBTrans.cs b/src/IFoxCAD.Cad.Shared/Runtime/DBTrans.cs index 22bc33b..38f9144 100644 --- a/src/IFoxCAD.Cad.Shared/Runtime/DBTrans.cs +++ b/src/IFoxCAD.Cad.Shared/Runtime/DBTrans.cs @@ -134,13 +134,15 @@ public DBTrans(string fileName, if (fileName == null || string.IsNullOrEmpty(fileName.Trim())) throw new ArgumentNullException(nameof(fileName)); - _fileName = fileName.Replace("/", "\\");//// doc.Name总是"D:\\JX.dwg" + _fileName = fileName.Replace("/", "\\");// doc.Name总是"D:\\JX.dwg" + // 此处若为失败的文件名,那么保存的时候就会丢失名称, + // 因此用 _fileName 储存 if (!File.Exists(_fileName)) { - // 此处若为失败的文件名,那么保存的时候就会丢失名称, - // 因此用 _fileName 储存 - Database = new Database(true, false); + // 第二个参数如果使用了false,那么将导致关闭cad的时候出现致命错误;cad08测试 + // Unhandled Access Violation Reading Ox113697a0 Exception at 4b4154h + Database = new Database(true, true); } else { @@ -425,7 +427,7 @@ public void SaveFile(DwgVersion version = DwgVersion.AC1800, if (string.IsNullOrEmpty(saveAsFile)) { fileMsg = _fileName; - saveAsFile = _fileName; + saveAsFile = fileMsg; //creatFlag = true; } else @@ -632,7 +634,7 @@ protected virtual void Dispose(bool disposing) IsDisposed = true; - if (disposing) + if (_commit)// disposing { // 调用cad的事务进行提交,释放托管状态(托管对象) Transaction.Commit(); diff --git "a/tests/TestShared/cad\345\211\252\350\264\264\346\235\277.cs" b/tests/TestShared/Copyclip.cs similarity index 36% rename from "tests/TestShared/cad\345\211\252\350\264\264\346\235\277.cs" rename to tests/TestShared/Copyclip.cs index ad190c1..b907463 100644 --- "a/tests/TestShared/cad\345\211\252\350\264\264\346\235\277.cs" +++ b/tests/TestShared/Copyclip.cs @@ -1,20 +1,53 @@ -using Clipboard = System.Windows.Forms.Clipboard; +#define test + +namespace Test; + +using IFoxCAD.Cad; +using System.Threading; +using Clipboard = System.Windows.Forms.Clipboard; -namespace JoinBoxAcad; // https://forums.autodesk.com/t5/net/paste-list-of-objects-from-clipboard-on-dwg-file-using-c-net/td-p/6797606 -public class cad剪贴板 +public class Copyclip { - //[IFoxInitialize] +#if test + [IFoxInitialize] +#endif public void Init() { - var doc = Acap.DocumentManager.MdiActiveDocument; - doc.CommandWillStart += Doc_CommandWillStart; + Acap.DocumentManager.DocumentLockModeChanged += DocumentManager_DocumentLockModeChanged; + } + + /// + /// 反应器->命令否决触发命令前(不可锁文档) + /// + /// + /// + void DocumentManager_DocumentLockModeChanged(object sender, DocumentLockModeChangedEventArgs e) + { + var up = e.GlobalCommandName.ToUpper(); + if (up == "COPYCLIP") + { + e.Veto(); + IFoxCopyclip(); + } + else if (up == "PASTECLIP") + { + // 由于不知道校验码是怎么构造的,所以只能否决命令,并且自己实现粘贴 + e.Veto(); + IFoxPasteclip(); + } } - void Doc_CommandWillStart(object sender, CommandEventArgs e) + /// + /// 粘贴命令 + /// +#if test + [CommandMethod(nameof(IFoxPasteclip))] +#endif + public void IFoxPasteclip() { - // 粘贴命令 - if (e.GlobalCommandName != "PASTECLIP") + var dm = Acap.DocumentManager; + if (dm.Count == 0) return; // 获取剪贴板上面的保存路径 @@ -36,21 +69,61 @@ void Doc_CommandWillStart(object sender, CommandEventArgs e) if (string.IsNullOrEmpty(Path.GetFileName(fileName))) return; - using var tr = new DBTrans(fileName, true, FileOpenMode.OpenForReadAndAllShare); - foreach (var id in tr.ModelSpace) - { - if (!id.IsOk()) - continue; - var ent = tr.GetObject(id, OpenMode.ForWrite); - ent!.ColorIndex = 30; - } - tr.SaveFile((DwgVersion)27); + Env.Printl("粘贴来源: " + fileName); + + // 获取临时文件的图元id + var result = new List(); + using var fileTr = new DBTrans(fileName, false, FileOpenMode.OpenForReadAndAllShare); + foreach (var id in fileTr.ModelSpace) + result.Add(id); + + // 加入当前图纸的块表 + using var tr = new DBTrans(); + var map = new IdMapping(); + tr.Task(() => { + tr.Database.WblockCloneObjects( + new ObjectIdCollection(result.ToArray()), + tr.Database.BlockTableId, // 粘贴目标 + map, + DuplicateRecordCloning.Replace, + false); + }); + + // 在jig预览 + List ents = new(); + map.GetValues().ForEach(a => { + var ent = tr.GetObject(a); + if (ent != null) + ents.Add(ent); + }); + + var moveJig = new JigEx((mousePoint, drawEntitys) => { + ents.ForEach(ent => { + var entClone = (Entity)ent.Clone(); + entClone.Move(Point3d.Origin, mousePoint); + drawEntitys.Enqueue(entClone); + }); + }); + moveJig.SetOptions(Point3d.Origin, orthomode: false); + moveJig.Drag(); + + // 加入当前空间 + tr.CurrentSpace.AddEntity(moveJig.Entitys); } - [CommandMethod(nameof(Ccopy), CommandFlags.UsePickSet)] - public void Ccopy() + + /// + /// 复制命令 + /// +#if test + [CommandMethod(nameof(IFoxCopyclip), CommandFlags.UsePickSet)] +#endif + public void IFoxCopyclip() { var dm = Acap.DocumentManager; + if (dm.Count == 0) + return; + var doc = dm.MdiActiveDocument; var sdb = doc.Database; var ed = doc.Editor; @@ -66,74 +139,52 @@ public void Ccopy() result.AddRange(psr.Value.GetObjectIds()); // 获取临时路径 - // var path = Environment.GetFolderPath(Environment.SpecialFolder.DesktopDirectory) + "\\" - var path = Path.GetTempPath() + string file; + do + { + file = Path.GetTempPath() + DateTime.Now.ToString("yyyyMMddHHmmssfff") + ".DWG"; + Thread.Sleep(1); + } while (File.Exists(file)); // 写入剪贴板 + var sb = new StringBuilder(); +#if true2 + //每个字符后面插入\0 + for (int i = 0; i < file.Length; i++) + { + sb.Append(file[i]); + sb.Append("\0"); + } + sb.Append("\0"); + var r15 = "R15"; + for (int i = 0; i < r15.Length; i++) + { + sb.Append(r15[i]); + sb.Append("\0"); + } + // 后面一段是还是啥?? 校验码?? + // 因为不知道,所以只能自己粘贴的时候处理了 +#else + sb.Append(file); +#endif var data = new MemoryStream(); - var bytes = Encoding.Unicode.GetBytes(path); + var bytes = Encoding.Unicode.GetBytes(sb.ToString()); data.Write(bytes, 0, bytes.Length); + Clipboard.SetData($"AutoCAD.r{Acap.Version.Major}", data); // 克隆到目标块表内 - using var tr2 = new DBTrans(path); - tr2.Task(() => { + using var fileTr = new DBTrans(file); + fileTr.Task(() => { var map = new IdMapping(); sdb.WblockCloneObjects( new ObjectIdCollection(result.ToArray()), - tr2.ModelSpace.ObjectId, + fileTr.ModelSpace.ObjectId, map, DuplicateRecordCloning.Replace, false); }); - tr2.SaveFile(); - } - - [CommandMethod(nameof(NewPaste), CommandFlags.Transparent)] - public void NewPaste() - { - var doc = Acap.DocumentManager.MdiActiveDocument; - var obj = (System.Windows.Forms.DataObject)Clipboard.GetDataObject(); - if (obj == null) - return; - - // Find out whether the clipboard contains AutoCAD data - var formats = obj.GetFormats(); - string formatFound = ""; - bool foundDwg = false; - foreach (var name in formats) - { - if (name.Contains("AutoCAD.r")) - { - foundDwg = true; - formatFound = name; - break; - } - } - if (foundDwg) - { - // If Found, discover where is the Database - var MStr = obj.GetData(formatFound) as MemoryStream; - if (MStr == null) - return; - - MStr.Position = 0; - var sr = new StreamReader(MStr, Encoding.UTF8); - var myStr = sr.ReadToEnd(); - - //删除不必要的字符 - var sda = myStr.Replace("\0", ""); - int index = sda.IndexOf(".DWG"); - if (index > 0) - sda = sda.Substring(0, index + 4); - - doc.Editor.WriteMessage(sda); - } - else - { - // If not, pasteclip normally - doc.SendStringToExecute("\x1B\x1B_.PASTECLIP ", true, false, true); - } + fileTr.SaveFile(); } } \ No newline at end of file diff --git a/tests/TestShared/TestJig.cs b/tests/TestShared/TestJig.cs index f8a9c5a..3f725ae 100644 --- a/tests/TestShared/TestJig.cs +++ b/tests/TestShared/TestJig.cs @@ -97,13 +97,13 @@ public void TestCmd_Jig44() switch (pr.StringResult) { case "A": - tr.Editor?.WriteMessage($"\n 您触发了关键字{pr.StringResult}"); - flag = false; - break; + tr.Editor?.WriteMessage($"\n 您触发了关键字{pr.StringResult}"); + flag = false; + break; case " ": - tr.Editor?.WriteMessage("\n 触发关键字空格"); - flag = false; - break; + tr.Editor?.WriteMessage("\n 触发关键字空格"); + flag = false; + break; } } else if (pr.Status != PromptStatus.OK)// PromptStatus.None == 右键,空格,回车,都在这里结束 @@ -253,38 +253,38 @@ protected override SamplerStatus Sampler(JigPrompts jp) switch (ppr.StringResult) { case "Bold": - break; + break; case "Italic": - break; + break; case "LArger": - { - // Multiple the text size by two - _txtSize *= 2; - break; - } + { + // Multiple the text size by two + _txtSize *= 2; + break; + } case "Smaller": - { - // Divide the text size by two - _txtSize /= 2; - break; - } + { + // Divide the text size by two + _txtSize /= 2; + break; + } case "ROtate90": + { + // To rotate clockwise we subtract 90 degrees and + // then normalise the angle between 0 and 360 + _angle -= Math.PI / 2; + while (_angle < Math.PI * 2) { - // To rotate clockwise we subtract 90 degrees and - // then normalise the angle between 0 and 360 - _angle -= Math.PI / 2; - while (_angle < Math.PI * 2) - { - _angle += Math.PI * 2; - } - break; + _angle += Math.PI * 2; } - case "LEft": break; + } + case "LEft": + break; case "RIght": - break; + break; case "Middle": - break; + break; } return SamplerStatus.OK; } @@ -309,6 +309,6 @@ protected override bool Update() txt.Rotation = _angle; return true; } - } + } #endif } \ No newline at end of file diff --git a/tests/TestShared/TestShared.projitems b/tests/TestShared/TestShared.projitems index 0dc6599..0f6076c 100644 --- a/tests/TestShared/TestShared.projitems +++ b/tests/TestShared/TestShared.projitems @@ -9,6 +9,7 @@ TestShared + -- Gitee From f38c1e7bb2b6a1497bb7a17a084e81712bc0815e Mon Sep 17 00:00:00 2001 From: liuqihong <540762622@qq.com> Date: Tue, 20 Sep 2022 16:35:26 +0800 Subject: [PATCH 513/675] =?UTF-8?q?=E5=89=AA=E8=B4=B4=E6=9D=BF=E4=BF=AE?= =?UTF-8?q?=E6=94=B9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- tests/TestShared/Copyclip.cs | 31 +++++++++++++++++++++++++++---- 1 file changed, 27 insertions(+), 4 deletions(-) diff --git a/tests/TestShared/Copyclip.cs b/tests/TestShared/Copyclip.cs index b907463..6e36c4b 100644 --- a/tests/TestShared/Copyclip.cs +++ b/tests/TestShared/Copyclip.cs @@ -36,6 +36,10 @@ void DocumentManager_DocumentLockModeChanged(object sender, DocumentLockModeChan e.Veto(); IFoxPasteclip(); } + else if (up == "PASTEBLOCK") //ctrl+shift+v 粘贴为块也要自己造 + { + + } } /// @@ -97,20 +101,32 @@ public void IFoxPasteclip() ents.Add(ent); }); + // 求全部图元的左下角作为基点 + double minx = double.MaxValue; + double miny = double.MaxValue; + ents.ForEach(ent => { + var info = ent.GetBoundingBoxEx(); + minx = minx > info.MinX ? info.MinX : minx; + miny = miny > info.MinY ? info.MinY : miny; + }); + var bs = new Point3d(minx, miny, 0); + var moveJig = new JigEx((mousePoint, drawEntitys) => { ents.ForEach(ent => { var entClone = (Entity)ent.Clone(); - entClone.Move(Point3d.Origin, mousePoint); + entClone.Move(bs, mousePoint); drawEntitys.Enqueue(entClone); }); }); - moveJig.SetOptions(Point3d.Origin, orthomode: false); + moveJig.SetOptions(bs, orthomode: false); moveJig.Drag(); // 加入当前空间 tr.CurrentSpace.AddEntity(moveJig.Entitys); } + // 有了这个的话,还需要读取剪贴板吗?? + static string? _path; /// /// 复制命令 @@ -120,6 +136,10 @@ public void IFoxPasteclip() #endif public void IFoxCopyclip() { + // 此处要先去删除tmp文件夹的上次剪贴板产生的dwg文件 + if (_path != null) + File.Delete(_path); + var dm = Acap.DocumentManager; if (dm.Count == 0) return; @@ -142,8 +162,10 @@ public void IFoxCopyclip() string file; do { - file = Path.GetTempPath() - + DateTime.Now.ToString("yyyyMMddHHmmssfff") + ".DWG"; + var t1 = DateTime.Now.ToString("yyyyMMddHHmmssfff"); + t1 = Convert.ToInt32(t1.GetHashCode()).ToString("X"); + var t2 = Convert.ToInt32(t1.GetHashCode()).ToString("X");// 这里是为了满足长度而做的 + file = Path.GetTempPath() + "A$" + t1 + t2[0] + ".DWG"; Thread.Sleep(1); } while (File.Exists(file)); @@ -186,5 +208,6 @@ public void IFoxCopyclip() false); }); fileTr.SaveFile(); + _path = file; } } \ No newline at end of file -- Gitee From 8c70e38eae7f5cba04fad53cef9457cb90891442 Mon Sep 17 00:00:00 2001 From: liuqihong <540762622@qq.com> Date: Tue, 20 Sep 2022 16:42:36 +0800 Subject: [PATCH 514/675] =?UTF-8?q?=E4=BF=AE=E6=94=B9=E5=89=AA=E8=B4=B4?= =?UTF-8?q?=E6=9D=BF?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- tests/TestShared/Copyclip.cs | 52 +++++++++++++++++++----------------- 1 file changed, 28 insertions(+), 24 deletions(-) diff --git a/tests/TestShared/Copyclip.cs b/tests/TestShared/Copyclip.cs index 6e36c4b..629b68f 100644 --- a/tests/TestShared/Copyclip.cs +++ b/tests/TestShared/Copyclip.cs @@ -33,12 +33,32 @@ void DocumentManager_DocumentLockModeChanged(object sender, DocumentLockModeChan else if (up == "PASTECLIP") { // 由于不知道校验码是怎么构造的,所以只能否决命令,并且自己实现粘贴 + // 获取剪贴板上面的保存路径 + var format = Clipboard.GetDataObject() + .GetFormats() + .FirstOrDefault(f => f.StartsWith("AutoCAD")); + + if (string.IsNullOrEmpty(format)) + return; + + string fileName; + var data = (MemoryStream)Clipboard.GetData(format); + using (var reader = new StreamReader(data)) + { + fileName = reader.ReadToEnd(); + fileName = fileName.Replace("\0", string.Empty); + fileName = fileName.Substring(0, fileName.IndexOf(".DWG") + 4); + } + if (string.IsNullOrEmpty(Path.GetFileName(fileName))) + return; + + // 如果剪贴板存在这个路径,才去否决,粘贴文字之类的由原本的命令执行 + _fileName = fileName; e.Veto(); IFoxPasteclip(); } else if (up == "PASTEBLOCK") //ctrl+shift+v 粘贴为块也要自己造 { - } } @@ -54,30 +74,14 @@ public void IFoxPasteclip() if (dm.Count == 0) return; - // 获取剪贴板上面的保存路径 - var format = Clipboard.GetDataObject() - .GetFormats() - .FirstOrDefault(f => f.StartsWith("AutoCAD")); - - if (string.IsNullOrEmpty(format)) - return; - - string fileName; - var data = (MemoryStream)Clipboard.GetData(format); - using (var reader = new StreamReader(data)) - { - fileName = reader.ReadToEnd(); - fileName = fileName.Replace("\0", string.Empty); - fileName = fileName.Substring(0, fileName.IndexOf(".DWG") + 4); - } - if (string.IsNullOrEmpty(Path.GetFileName(fileName))) + if (_fileName == null) return; - Env.Printl("粘贴来源: " + fileName); + Env.Printl("粘贴来源: " + _fileName); // 获取临时文件的图元id var result = new List(); - using var fileTr = new DBTrans(fileName, false, FileOpenMode.OpenForReadAndAllShare); + using var fileTr = new DBTrans(_fileName, false, FileOpenMode.OpenForReadAndAllShare); foreach (var id in fileTr.ModelSpace) result.Add(id); @@ -126,7 +130,7 @@ public void IFoxPasteclip() } // 有了这个的话,还需要读取剪贴板吗?? - static string? _path; + static string? _fileName; /// /// 复制命令 @@ -137,8 +141,8 @@ public void IFoxPasteclip() public void IFoxCopyclip() { // 此处要先去删除tmp文件夹的上次剪贴板产生的dwg文件 - if (_path != null) - File.Delete(_path); + if (_fileName != null) + File.Delete(_fileName); var dm = Acap.DocumentManager; if (dm.Count == 0) @@ -208,6 +212,6 @@ public void IFoxCopyclip() false); }); fileTr.SaveFile(); - _path = file; + _fileName = file; } } \ No newline at end of file -- Gitee From dd64455a2ed223ad67049bd936945d783ffc5d94 Mon Sep 17 00:00:00 2001 From: liuqihong <540762622@qq.com> Date: Tue, 20 Sep 2022 23:12:03 +0800 Subject: [PATCH 515/675] =?UTF-8?q?=E5=89=A9=E4=B8=8B=E5=8A=A8=E6=80=81?= =?UTF-8?q?=E5=9D=97?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../ExtensionMethod/CollectionEx.cs | 13 + src/IFoxCAD.Cad.Shared/Runtime/DBTrans.cs | 10 +- tests/TestShared/Copyclip.cs | 311 ++++++++++++------ 3 files changed, 237 insertions(+), 97 deletions(-) diff --git a/src/IFoxCAD.Cad.Shared/ExtensionMethod/CollectionEx.cs b/src/IFoxCAD.Cad.Shared/ExtensionMethod/CollectionEx.cs index a7f1654..d70e4b4 100644 --- a/src/IFoxCAD.Cad.Shared/ExtensionMethod/CollectionEx.cs +++ b/src/IFoxCAD.Cad.Shared/ExtensionMethod/CollectionEx.cs @@ -251,5 +251,18 @@ public static List GetValues(this IdMapping idmap) ids.Add(item.Value); return ids; } + + /// + /// 转换为词典 + /// + /// + /// + public static Dictionary ToDictionary(this IdMapping mapping) + { + var keyValuePairs = new Dictionary(); + foreach (IdPair item in mapping) + keyValuePairs.Add(item.Key, item.Value); + return keyValuePairs; + } #endregion } \ No newline at end of file diff --git a/src/IFoxCAD.Cad.Shared/Runtime/DBTrans.cs b/src/IFoxCAD.Cad.Shared/Runtime/DBTrans.cs index 38f9144..64369b1 100644 --- a/src/IFoxCAD.Cad.Shared/Runtime/DBTrans.cs +++ b/src/IFoxCAD.Cad.Shared/Runtime/DBTrans.cs @@ -9,8 +9,13 @@ namespace IFoxCAD.Cad; /// 事务栈 /// 隐匿事务在数据库其中担任的角色 /// +[DebuggerDisplay("{DebuggerDisplay,nq}")] +[DebuggerTypeProxy(typeof(DBTrans))] public class DBTrans : IDisposable { + [DebuggerBrowsable(DebuggerBrowsableState.Never)] + private string DebuggerDisplay => "StackCount:" + dBTrans.Count; + #region 私有字段 /// /// 文档锁 @@ -519,9 +524,8 @@ public void SaveFile(DwgVersion version = DwgVersion.AC1800, // 构造函数(fileName)用了不存在的路径进行后台打开,就会出现此问题 // 测试命令 FileNotExist - var dir = Environment.GetFolderPath( - Environment.SpecialFolder.DesktopDirectory) - + "\\后台保存出错的文件\\"; + var dir = Environment.GetFolderPath(Environment.SpecialFolder.DesktopDirectory) + + "\\后台保存出错的文件\\"; if (!Directory.Exists(dir)) Directory.CreateDirectory(dir); diff --git a/tests/TestShared/Copyclip.cs b/tests/TestShared/Copyclip.cs index 629b68f..d01e7d7 100644 --- a/tests/TestShared/Copyclip.cs +++ b/tests/TestShared/Copyclip.cs @@ -3,11 +3,12 @@ namespace Test; using IFoxCAD.Cad; +using System; using System.Threading; using Clipboard = System.Windows.Forms.Clipboard; // https://forums.autodesk.com/t5/net/paste-list-of-objects-from-clipboard-on-dwg-file-using-c-net/td-p/6797606 -public class Copyclip +public class InterceptCopyclip { #if test [IFoxInitialize] @@ -33,23 +34,8 @@ void DocumentManager_DocumentLockModeChanged(object sender, DocumentLockModeChan else if (up == "PASTECLIP") { // 由于不知道校验码是怎么构造的,所以只能否决命令,并且自己实现粘贴 - // 获取剪贴板上面的保存路径 - var format = Clipboard.GetDataObject() - .GetFormats() - .FirstOrDefault(f => f.StartsWith("AutoCAD")); - - if (string.IsNullOrEmpty(format)) - return; - - string fileName; - var data = (MemoryStream)Clipboard.GetData(format); - using (var reader = new StreamReader(data)) - { - fileName = reader.ReadToEnd(); - fileName = fileName.Replace("\0", string.Empty); - fileName = fileName.Substring(0, fileName.IndexOf(".DWG") + 4); - } - if (string.IsNullOrEmpty(Path.GetFileName(fileName))) + var fileName = GetCadPasteclip("AutoCAD"); + if (fileName == null) return; // 如果剪贴板存在这个路径,才去否决,粘贴文字之类的由原本的命令执行 @@ -57,91 +43,72 @@ void DocumentManager_DocumentLockModeChanged(object sender, DocumentLockModeChan e.Veto(); IFoxPasteclip(); } - else if (up == "PASTEBLOCK") //ctrl+shift+v 粘贴为块也要自己造 + else if (up == "COPYBASE") //ctrl+shift+c 带基点复制 + { + e.Veto(); + IFoxCopyBase(); + } + else if (up == "PASTEBLOCK") //ctrl+shift+v 粘贴为块 { + e.Veto(); + IFoxPasteBlock(); } } + // 有了这个的话,还需要读取剪贴板吗? + // 需要的,剪贴板可能存在不是dwg路径,而是文字内容等 + static string? _fileName; + /// - /// 粘贴命令 + /// 获取剪贴板路径 /// -#if test - [CommandMethod(nameof(IFoxPasteclip))] -#endif - public void IFoxPasteclip() + /// 控制不同的cad + /// 获取是否成功 + string? GetCadPasteclip(string pasteclipStr = "AutoCAD") { - var dm = Acap.DocumentManager; - if (dm.Count == 0) - return; - - if (_fileName == null) - return; - - Env.Printl("粘贴来源: " + _fileName); - - // 获取临时文件的图元id - var result = new List(); - using var fileTr = new DBTrans(_fileName, false, FileOpenMode.OpenForReadAndAllShare); - foreach (var id in fileTr.ModelSpace) - result.Add(id); - - // 加入当前图纸的块表 - using var tr = new DBTrans(); - var map = new IdMapping(); - tr.Task(() => { - tr.Database.WblockCloneObjects( - new ObjectIdCollection(result.ToArray()), - tr.Database.BlockTableId, // 粘贴目标 - map, - DuplicateRecordCloning.Replace, - false); - }); - - // 在jig预览 - List ents = new(); - map.GetValues().ForEach(a => { - var ent = tr.GetObject(a); - if (ent != null) - ents.Add(ent); - }); + // 获取剪贴板上面的保存路径 + var format = Clipboard.GetDataObject() + .GetFormats() + .FirstOrDefault(f => f.StartsWith(pasteclipStr)); - // 求全部图元的左下角作为基点 - double minx = double.MaxValue; - double miny = double.MaxValue; - ents.ForEach(ent => { - var info = ent.GetBoundingBoxEx(); - minx = minx > info.MinX ? info.MinX : minx; - miny = miny > info.MinY ? info.MinY : miny; - }); - var bs = new Point3d(minx, miny, 0); + if (string.IsNullOrEmpty(format)) + return null; - var moveJig = new JigEx((mousePoint, drawEntitys) => { - ents.ForEach(ent => { - var entClone = (Entity)ent.Clone(); - entClone.Move(bs, mousePoint); - drawEntitys.Enqueue(entClone); - }); - }); - moveJig.SetOptions(bs, orthomode: false); - moveJig.Drag(); - - // 加入当前空间 - tr.CurrentSpace.AddEntity(moveJig.Entitys); + string fileName; + var data = (MemoryStream)Clipboard.GetData(format); + using (var reader = new StreamReader(data)) + { + fileName = reader.ReadToEnd(); + fileName = fileName.Replace("\0", string.Empty); + fileName = fileName.Substring(0, fileName.IndexOf(".DWG") + 4); + } + if (string.IsNullOrEmpty(Path.GetFileName(fileName))) + return null; + return fileName; } - // 有了这个的话,还需要读取剪贴板吗?? - static string? _fileName; /// /// 复制命令 /// -#if test [CommandMethod(nameof(IFoxCopyclip), CommandFlags.UsePickSet)] -#endif public void IFoxCopyclip() + { + Copy(false); + } + /// + /// 带基点复制 + /// + [CommandMethod(nameof(IFoxCopyBase))] + public void IFoxCopyBase() + { + Copy(true); + } + + public void Copy(bool getPoint) { // 此处要先去删除tmp文件夹的上次剪贴板产生的dwg文件 - if (_fileName != null) + if (_fileName != null && File.Exists(_fileName)) File.Delete(_fileName); var dm = Acap.DocumentManager; @@ -162,16 +129,21 @@ public void IFoxCopyclip() } result.AddRange(psr.Value.GetObjectIds()); + Point3d basePoint = Point3d.Origin; + if (getPoint) + { + var pr = ed.GetPoint("\n选择基点"); + if (pr.Status != PromptStatus.OK) + return; + basePoint = pr.Value; + } + // 获取临时路径 - string file; do { - var t1 = DateTime.Now.ToString("yyyyMMddHHmmssfff"); - t1 = Convert.ToInt32(t1.GetHashCode()).ToString("X"); - var t2 = Convert.ToInt32(t1.GetHashCode()).ToString("X");// 这里是为了满足长度而做的 - file = Path.GetTempPath() + "A$" + t1 + t2[0] + ".DWG"; + _fileName = TimeName(); Thread.Sleep(1); - } while (File.Exists(file)); + } while (File.Exists(_fileName)); // 写入剪贴板 var sb = new StringBuilder(); @@ -192,7 +164,7 @@ public void IFoxCopyclip() // 后面一段是还是啥?? 校验码?? // 因为不知道,所以只能自己粘贴的时候处理了 #else - sb.Append(file); + sb.Append(_fileName); #endif var data = new MemoryStream(); var bytes = Encoding.Unicode.GetBytes(sb.ToString()); @@ -201,7 +173,7 @@ public void IFoxCopyclip() Clipboard.SetData($"AutoCAD.r{Acap.Version.Major}", data); // 克隆到目标块表内 - using var fileTr = new DBTrans(file); + using var fileTr = new DBTrans(_fileName); fileTr.Task(() => { var map = new IdMapping(); sdb.WblockCloneObjects( @@ -211,7 +183,158 @@ public void IFoxCopyclip() DuplicateRecordCloning.Replace, false); }); - fileTr.SaveFile(); - _fileName = file; + + // 移动到原点 + if (!basePoint.IsEqualTo(Point3d.Origin)) + { + var ents = fileTr.ModelSpace.GetEntities(); + foreach (var ent in ents) + using (ent.ForWrite()) + ent.Move(basePoint, Point3d.Origin); + } + + // 大于dwg07格式,保存为07,以实现高低版本通用剪贴板 + if ((int)DwgVersion.Current >= 27) + { + fileTr.SaveFile((DwgVersion)27, false); + } + else + { +#pragma warning disable CS0162 // 检测到无法访问的代码 + fileTr.SaveFile(); // 低于dwg07格式的,本工程没有支持cad06dll,所以这里只是展示 +#pragma warning restore CS0162 // 检测到无法访问的代码 + } + } + + string TimeName() + { + var t1 = DateTime.Now.ToString("yyyyMMddHHmmssfff"); + t1 = Convert.ToInt32(t1.GetHashCode()).ToString("X"); + var t2 = Convert.ToInt32(t1.GetHashCode()).ToString("X");// 这里是为了满足长度而做的 + return Path.GetTempPath() + "A$" + t1 + t2[0] + ".DWG"; + } + + + /// + /// 粘贴命令 + /// + [CommandMethod(nameof(IFoxPasteclip))] + public void IFoxPasteclip() + { + Paste(false); + } + /// + /// 粘贴为块 + /// + [CommandMethod(nameof(IFoxPasteBlock), CommandFlags.UsePickSet)] + public void IFoxPasteBlock() + { + Paste(true); + } + + public void Paste(bool isBlock) + { + var dm = Acap.DocumentManager; + if (dm.Count == 0) + return; + + if (_fileName == null) + return; + + Env.Printl("粘贴来源: " + _fileName); + + // 获取临时文件的图元id + var fileEntityIds = new List(); + using (var fileTr = new DBTrans(_fileName, false, FileOpenMode.OpenForReadAndAllShare)) + { + foreach (var id in fileTr.ModelSpace) + fileEntityIds.Add(id); + } + + using var tr = new DBTrans(); + // 加入当前图纸的块表 + var map = new IdMapping(); + tr.Task(() => { + tr.Database.WblockCloneObjects( + new ObjectIdCollection(fileEntityIds.ToArray()), + tr.Database.BlockTableId, // 粘贴目标 + map, + DuplicateRecordCloning.Replace, + false); + }); + + + // 在jig预览 + List ents = new(); + var dict = map.ToDictionary(); + foreach (var fent in fileEntityIds) + { + var id = dict[fent]; // 拷贝动态块的时候,这里全是不ok + if (!id.IsOk()) + continue; + + var ent = tr.GetObject(id); + if (ent != null) + ents.Add(ent); + } + + if (ents.Count == 0) + return; + + Point3d basePoint = Point3d.Origin; + if (!isBlock) + { + // 求全部图元的左下角作为基点 + double minx = double.MaxValue; + double miny = double.MaxValue; + ents.ForEach(ent => { + var info = ent.GetBoundingBoxEx(); + minx = minx > info.MinX ? info.MinX : minx; + miny = miny > info.MinY ? info.MinY : miny; + }); + basePoint = new Point3d(minx, miny, 0); + } + + var moveJig = new JigEx((mousePoint, drawEntitys) => { + ents.ForEach(ent => { + var entClone = (Entity)ent.Clone(); + entClone.Move(basePoint, mousePoint); + drawEntitys.Enqueue(entClone); + }); + }); + moveJig.SetOptions(basePoint, orthomode: false); + var dr = moveJig.Drag(); + if (dr.Status != PromptStatus.OK) + return; + + // 直接粘贴 + if (!isBlock) + { + tr.CurrentSpace.AddEntity(moveJig.Entitys); + return; + } + + // 粘贴为块 + // 预览的时候移动了,这里要反向移动 + moveJig.Entitys.ForEach(ent => { + using (ent.ForWrite()) + ent.Move(moveJig.MousePointWcsLast, Point3d.Origin); + }); + + // 获取块名 + var blockName = Path.GetFileNameWithoutExtension(_fileName); + while (tr.BlockTable.Has(blockName)) + { + blockName = Path.GetFileNameWithoutExtension(TimeName()); + Thread.Sleep(1); + } + + var btrId = tr.BlockTable.Add(blockName, moveJig.Entitys); + var blockId = tr.CurrentSpace.InsertBlock(Point3d.Origin, btrId); + var blockEnt = tr.GetObject(blockId); + if (blockEnt == null) + return; + using (blockEnt.ForWrite()) + blockEnt.Move(Point3d.Origin, moveJig.MousePointWcsLast); } } \ No newline at end of file -- Gitee From 402cf426b959110cfc0864188a7e54536afd64b2 Mon Sep 17 00:00:00 2001 From: liuqihong <540762622@qq.com> Date: Wed, 21 Sep 2022 02:27:37 +0800 Subject: [PATCH 516/675] =?UTF-8?q?=E4=BC=98=E5=8C=96=E5=89=AA=E8=B4=B4?= =?UTF-8?q?=E6=9D=BF?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../ExtensionMethod/EntityBoundingInfo.cs | 32 ++- .../ExtensionMethod/JigEx.cs | 239 ++++++++++-------- .../ExtensionMethod/SymbolTableRecordEx.cs | 4 +- tests/TestShared/Copyclip.cs | 235 ++++++++++------- tests/TestShared/TestJig.cs | 4 +- 5 files changed, 315 insertions(+), 199 deletions(-) diff --git a/src/IFoxCAD.Cad.Shared/ExtensionMethod/EntityBoundingInfo.cs b/src/IFoxCAD.Cad.Shared/ExtensionMethod/EntityBoundingInfo.cs index 43010d6..99b9708 100644 --- a/src/IFoxCAD.Cad.Shared/ExtensionMethod/EntityBoundingInfo.cs +++ b/src/IFoxCAD.Cad.Shared/ExtensionMethod/EntityBoundingInfo.cs @@ -55,6 +55,22 @@ public BoundingInfo(Point3d min, Point3d max, bool isEffective, double angle = 0 // MaxZ = 0; // Angle = angle; // } + + public override string ToString() + { + return Extents3d.ToString(); + } + + public void Move(Point3d pt1, Point3d pt2) + { + var ve = pt1 - pt2; + MinX -= ve.X; + MinY -= ve.Y; + MinZ -= ve.Z; + MaxX -= ve.X; + MaxY -= ve.Y; + MaxZ -= ve.Z; + } } public class EntityBoundingInfo @@ -230,26 +246,26 @@ static BoundingInfo GetBoxInfoInMText(MText mtxt) case AttachmentPoint.TopCenter: case AttachmentPoint.MiddleCenter: case AttachmentPoint.BottomCenter: - wl = width * -0.5; - break; + wl = width * -0.5; + break; case AttachmentPoint.TopRight: case AttachmentPoint.MiddleRight: case AttachmentPoint.BottomRight: - wl = -width; - break; + wl = -width; + break; } switch (mtxt.Attachment) { case AttachmentPoint.TopLeft: case AttachmentPoint.TopCenter: case AttachmentPoint.TopRight: - hb = -height;// 下边线到插入点的距离 - break; + hb = -height;// 下边线到插入点的距离 + break; case AttachmentPoint.MiddleLeft: case AttachmentPoint.MiddleCenter: case AttachmentPoint.MiddleRight: - hb = height * -0.5; - break; + hb = height * -0.5; + break; } double wr = width + wl; diff --git a/src/IFoxCAD.Cad.Shared/ExtensionMethod/JigEx.cs b/src/IFoxCAD.Cad.Shared/ExtensionMethod/JigEx.cs index c406304..5b6d69c 100644 --- a/src/IFoxCAD.Cad.Shared/ExtensionMethod/JigEx.cs +++ b/src/IFoxCAD.Cad.Shared/ExtensionMethod/JigEx.cs @@ -12,7 +12,7 @@ */ public delegate void WorldDrawEvent(WorldDraw draw); -public class JigEx : DrawJig +public class JigEx : DrawJig, IDisposable { #region 成员 /// @@ -76,6 +76,104 @@ public JigEx(Action>? action = null, double tolerance = 1 } #endregion + #region 重写 + /// + /// 鼠标采样器 + /// + /// + /// 返回状态:令频繁刷新结束 + protected override SamplerStatus Sampler(JigPrompts prompts) + { + if (_worldDrawFlag) + return SamplerStatus.NoChange;// OK的时候拖动鼠标与否都不出现图元 + + if (_options is null) + throw new NullReferenceException(nameof(_options)); + + var pro = prompts.AcquirePoint(_options); + if (pro.Status == PromptStatus.Keyword) + return SamplerStatus.OK; + else if (pro.Status != PromptStatus.OK) + return SamplerStatus.Cancel; + + // 上次鼠标点不同(一定要这句,不然图元刷新太快会看到奇怪的边线) + var mousePointWcs = pro.Value; + + // == 是比较类字段,但是最好转为哈希比较. + // IsEqualTo 是方形判断(仅加法),但是cad是距离. + // Distance 是圆形判断(会求平方根,使用了牛顿迭代), + // 大量数据(十万以上/频繁刷新)面前会显得非常慢. + if (mousePointWcs.IsEqualTo(MousePointWcsLast, _tolerance)) + return SamplerStatus.NoChange; + + // 上次循环的缓冲区图元清理,否则将会在vs输出遗忘 Dispose + while (_drawEntitys.Count > 0) + _drawEntitys.Dequeue().Dispose(); + + // 委托把容器扔出去接收新创建的图元,然后给重绘更新 + _mouseAction?.Invoke(mousePointWcs, _drawEntitys); + MousePointWcsLast = mousePointWcs; + + return SamplerStatus.OK; + } + + /// + /// 重绘已在数据库的图元 + /// + /// 0x01 此处不加入newEntity的,它们在构造函数的参数回调处加入,它们会进行频繁new和Dispose从而避免遗忘释放
    + /// 0x02 此处用于重绘已经在数据的图元
    + /// 0x03 此处用于图元亮显暗显,因为会被重绘冲刷掉所以独立出来不重绘,它们也往往已经存在数据库的 + ///
    + ///
    + /// + /// newEntity只会存在一个图元队列中,而数据库图元可以分多个集合 + /// 例如: 集合A亮显时 集合B暗显/集合B亮显时 集合A暗显,所以我没有设计多个"数据库图元集合"存放,而是由用户在构造函数外自行创建 + /// + /// + public void DatabaseEntityDraw(WorldDrawEvent action) + { + WorldDrawEvent = action; + } + + /* WorldDraw 封装外的操作说明: + * 0x01 + * 我有一个业务是一次性生成四个方向的箭头,因为cad08缺少瞬时图元, + * 那么可以先提交一次事务,再开一个事务,把Entity传给jig,最后选择删除部分. + * 虽然这个是可行的方案,但是Entity穿越事务本身来说是非必要不使用的. + * 0x02 + * 四个箭头最近鼠标的亮显,其余淡显, + * 在jig使用淡显ent.Unhighlight和亮显ent.Highlight() + * 需要绕过重绘,否则重绘将导致图元频闪,令这两个操作失效, + * 此时需要自定义一个集合 EntityList (不使用本函数的_drawEntitys) + * 再将 EntityList 传给 WorldDrawEvent 事件,事件内实现亮显和淡显(事件已经利用 DatabaseEntityDraw函数进行提供). + * 0x03 + * draw.Geometry.Draw(_drawEntitys[i]); + * 此函数有问题,acad08克隆一份数组也可以用来刷新, + * 而arx上面的jig只能一次改一个,所以可以用此函数. + * 起因是此函数属于异步刷新, + * 同步上下文的刷新是 RawGeometry + * 0x04 + * cad22测试出现,08不会, + * draw.RawGeometry.Draw(ent);会跳到 Sampler(),所以设置 _worldDrawFlag + * 但是禁止重绘重入的话(令图元不频繁重绘),那么鼠标停着的时候就看不见图元, + * 所以只能重绘结束的时候才允许鼠标采集,采集过程的时候不会触发重绘, + * 这样才可以保证容器在重绘中不被更改. + */ + /// + /// 重绘图形 + /// + protected override bool WorldDraw(WorldDraw draw) + { + _worldDrawFlag = true; + WorldDrawEvent?.Invoke(draw); + _drawEntitys.ForEach(ent => { + draw.RawGeometry.Draw(ent); + }); + _worldDrawFlag = false; + return true; + } + #endregion + #region 方法 /// /// 鼠标配置:基点 @@ -194,107 +292,9 @@ public PromptResult Drag() return btrOfAddEntitySpace.AddEntity(es); } - #endregion - - #region 重写 - /// - /// 鼠标采样器 - /// - /// - /// 返回状态:令频繁刷新结束 - protected override SamplerStatus Sampler(JigPrompts prompts) - { - if (_worldDrawFlag) - return SamplerStatus.NoChange;// OK的时候拖动鼠标与否都不出现图元 - - if (_options is null) - throw new NullReferenceException(nameof(_options)); - - var pro = prompts.AcquirePoint(_options); - if (pro.Status == PromptStatus.Keyword) - return SamplerStatus.OK; - else if (pro.Status != PromptStatus.OK) - return SamplerStatus.Cancel; - - // 上次鼠标点不同(一定要这句,不然图元刷新太快会看到奇怪的边线) - var mousePointWcs = pro.Value; - - // == 是比较类字段,但是最好转为哈希比较. - // IsEqualTo 是方形判断(仅加法),但是cad是距离. - // Distance 是圆形判断(会求平方根,使用了牛顿迭代), - // 大量数据(十万以上/频繁刷新)面前会显得非常慢. - if (mousePointWcs.IsEqualTo(MousePointWcsLast, _tolerance)) - return SamplerStatus.NoChange; - - // 上次循环的缓冲区图元清理,否则将会在vs输出遗忘 Dispose - while (_drawEntitys.Count > 0) - _drawEntitys.Dequeue().Dispose(); - - // 委托把容器扔出去接收新创建的图元,然后给重绘更新 - _mouseAction?.Invoke(mousePointWcs, _drawEntitys); - MousePointWcsLast = mousePointWcs; - - return SamplerStatus.OK; - } - - /// - /// 重绘已在数据库的图元 - /// - /// 0x01 此处不加入newEntity的,它们在构造函数的参数回调处加入,它们会进行频繁new和Dispose从而避免遗忘释放
    - /// 0x02 此处用于重绘已经在数据的图元
    - /// 0x03 此处用于图元亮显暗显,因为会被重绘冲刷掉所以独立出来不重绘,它们也往往已经存在数据库的 - ///
    - ///
    - /// - /// newEntity只会存在一个图元队列中,而数据库图元可以分多个集合 - /// 例如: 集合A亮显时 集合B暗显/集合B亮显时 集合A暗显,所以我没有设计多个"数据库图元集合"存放,而是由用户在构造函数外自行创建 - /// - /// - public void DatabaseEntityDraw(WorldDrawEvent action) - { - WorldDrawEvent = action; - } - - /* WorldDraw 封装外的操作说明: - * 0x01 - * 我有一个业务是一次性生成四个方向的箭头,因为cad08缺少瞬时图元, - * 那么可以先提交一次事务,再开一个事务,把Entity传给jig,最后选择删除部分. - * 虽然这个是可行的方案,但是Entity穿越事务本身来说是非必要不使用的. - * 0x02 - * 四个箭头最近鼠标的亮显,其余淡显, - * 在jig使用淡显ent.Unhighlight和亮显ent.Highlight() - * 需要绕过重绘,否则重绘将导致图元频闪,令这两个操作失效, - * 此时需要自定义一个集合 EntityList (不使用本函数的_drawEntitys) - * 再将 EntityList 传给 WorldDrawEvent 事件,事件内实现亮显和淡显(事件已经利用 DatabaseEntityDraw函数进行提供). - * 0x03 - * draw.Geometry.Draw(_drawEntitys[i]); - * 此函数有问题,acad08克隆一份数组也可以用来刷新, - * 而arx上面的jig只能一次改一个,所以可以用此函数. - * 起因是此函数属于异步刷新, - * 同步上下文的刷新是 RawGeometry - * 0x04 - * cad22测试出现,08不会, - * draw.RawGeometry.Draw(ent);会跳到 Sampler(),所以设置 _worldDrawFlag - * 但是禁止重绘重入的话(令图元不频繁重绘),那么鼠标停着的时候就看不见图元, - * 所以只能重绘结束的时候才允许鼠标采集,采集过程的时候不会触发重绘, - * 这样才可以保证容器在重绘中不被更改. - */ - /// - /// 重绘图形 - /// - protected override bool WorldDraw(WorldDraw draw) - { - _worldDrawFlag = true; - WorldDrawEvent?.Invoke(draw); - _drawEntitys.ForEach(ent => { - draw.RawGeometry.Draw(ent); - }); - _worldDrawFlag = false; - return true; - } - #endregion + #region 配置 /// /// 用户输入控制默认配置 /// 令jig.Drag().Status == @@ -327,7 +327,7 @@ public void SetSpaceIsKeyword() if ((opt.UserInputControls & UserInputControls.AnyBlankTerminatesInput) == UserInputControls.AnyBlankTerminatesInput) opt.UserInputControls ^= UserInputControls.AnyBlankTerminatesInput; // 空格或回车,结束jig } - + #endregion #region 注释数据 /// @@ -345,6 +345,43 @@ protected override DynamicDimensionDataCollection GetDynamicDimensionData(double base.GetDynamicDimensionData(dimScale); return DimensionEntitys; } + + #endregion + #endregion + + #region IDisposable接口相关函数 + public bool IsDisposed { get; private set; } = false; + + /// + /// 手动调用释放 + /// + public void Dispose() + { + Dispose(true); + GC.SuppressFinalize(this); + } + + /// + /// 析构函数调用释放 + /// + ~JigEx() + { + Dispose(false); + } + + protected virtual void Dispose(bool disposing) + { + // 不重复释放,并设置已经释放 + if (IsDisposed) return; + IsDisposed = true; + + // 最后一次的图元如果没有加入数据库,就在此销毁,所以JigEx调用的时候加using + _drawEntitys?.ForEach(ent => { + if (ent.Database == null && !ent.IsDisposed) + ent.Dispose(); + }); + _drawEntitys?.Clear(); + } #endregion // 此处无测试 diff --git a/src/IFoxCAD.Cad.Shared/ExtensionMethod/SymbolTableRecordEx.cs b/src/IFoxCAD.Cad.Shared/ExtensionMethod/SymbolTableRecordEx.cs index 1648583..a0c215a 100644 --- a/src/IFoxCAD.Cad.Shared/ExtensionMethod/SymbolTableRecordEx.cs +++ b/src/IFoxCAD.Cad.Shared/ExtensionMethod/SymbolTableRecordEx.cs @@ -345,7 +345,7 @@ public static IEnumerable GetEntities(this BlockTableRecord btr, } /// - /// 按类型获取实体Id,AutoCad2010以上版本支持 + /// 按类型获取实体Id /// /// 实体类型 /// 块表记录 @@ -354,7 +354,7 @@ public static IEnumerable GetObjectIds(this BlockTableRecord btr) w { string dxfName = RXClass.GetClass(typeof(T)).DxfName; return btr.Cast() - .Where(id => id.ObjectClass().DxfName == dxfName); + .Where(id => id.ObjectClass()?.DxfName == dxfName); } /// diff --git a/tests/TestShared/Copyclip.cs b/tests/TestShared/Copyclip.cs index d01e7d7..c6fe98e 100644 --- a/tests/TestShared/Copyclip.cs +++ b/tests/TestShared/Copyclip.cs @@ -4,7 +4,9 @@ namespace Test; using IFoxCAD.Cad; using System; +using System.Linq; using System.Threading; +using System.Windows.Media.Imaging; using Clipboard = System.Windows.Forms.Clipboard; // https://forums.autodesk.com/t5/net/paste-list-of-objects-from-clipboard-on-dwg-file-using-c-net/td-p/6797606 @@ -18,6 +20,30 @@ public void Init() Acap.DocumentManager.DocumentLockModeChanged += DocumentManager_DocumentLockModeChanged; } +#if test + [IFoxInitialize(isInitialize: false)] +#endif + public void Terminate() + { + // 此处要先去删除tmp文件夹的上次剪贴板产生的dwg文件 + for (int i = 0; i < _delFile.Count; i++) + { + if (!File.Exists(_delFile[i])) + { + _delFile.RemoveAt(i); + continue; + } + + try + { + File.Delete(_delFile[i]); + _delFile.RemoveAt(i); + } + catch // 占用的时候无法删除 + { } + } + } + /// /// 反应器->命令否决触发命令前(不可锁文档) /// @@ -55,9 +81,47 @@ void DocumentManager_DocumentLockModeChanged(object sender, DocumentLockModeChan } } + /// + /// 复制命令 + /// + [CommandMethod(nameof(IFoxCopyclip), CommandFlags.UsePickSet)] + public void IFoxCopyclip() + { + Copy(false); + } + /// + /// 带基点复制 + /// + [CommandMethod(nameof(IFoxCopyBase))] + public void IFoxCopyBase() + { + Copy(true); + } + /// + /// 粘贴命令 + /// + [CommandMethod(nameof(IFoxPasteclip))] + public void IFoxPasteclip() + { + Paste(false); + } + /// + /// 粘贴为块 + /// + [CommandMethod(nameof(IFoxPasteBlock), CommandFlags.UsePickSet)] + public void IFoxPasteBlock() + { + Paste(true); + } + + + + + // 有了这个的话,还需要读取剪贴板吗? // 需要的,剪贴板可能存在不是dwg路径,而是文字内容等 static string? _fileName; + List _delFile = new(); /// /// 获取剪贴板路径 @@ -87,29 +151,13 @@ void DocumentManager_DocumentLockModeChanged(object sender, DocumentLockModeChan return fileName; } - /// - /// 复制命令 + /// 复制 /// - [CommandMethod(nameof(IFoxCopyclip), CommandFlags.UsePickSet)] - public void IFoxCopyclip() - { - Copy(false); - } - /// - /// 带基点复制 - /// - [CommandMethod(nameof(IFoxCopyBase))] - public void IFoxCopyBase() - { - Copy(true); - } - + /// public void Copy(bool getPoint) { - // 此处要先去删除tmp文件夹的上次剪贴板产生的dwg文件 - if (_fileName != null && File.Exists(_fileName)) - File.Delete(_fileName); + Terminate(); var dm = Acap.DocumentManager; if (dm.Count == 0) @@ -123,7 +171,7 @@ public void Copy(bool getPoint) var psr = ed.SelectImplied();// 预选 if (psr.Status != PromptStatus.OK) { - psr = ed.GetSelection(); + psr = ed.GetSelection();// 手选 if (psr.Status != PromptStatus.OK) return; } @@ -204,8 +252,15 @@ public void Copy(bool getPoint) fileTr.SaveFile(); // 低于dwg07格式的,本工程没有支持cad06dll,所以这里只是展示 #pragma warning restore CS0162 // 检测到无法访问的代码 } + + if (!_delFile.Contains(_fileName)) + _delFile.Add(_fileName); } + /// + /// 获取时间名 + /// + /// string TimeName() { var t1 = DateTime.Now.ToString("yyyyMMddHHmmssfff"); @@ -214,24 +269,10 @@ string TimeName() return Path.GetTempPath() + "A$" + t1 + t2[0] + ".DWG"; } - - /// - /// 粘贴命令 - /// - [CommandMethod(nameof(IFoxPasteclip))] - public void IFoxPasteclip() - { - Paste(false); - } /// - /// 粘贴为块 + /// 粘贴 /// - [CommandMethod(nameof(IFoxPasteBlock), CommandFlags.UsePickSet)] - public void IFoxPasteBlock() - { - Paste(true); - } - + /// public void Paste(bool isBlock) { var dm = Acap.DocumentManager; @@ -252,89 +293,111 @@ public void Paste(bool isBlock) } using var tr = new DBTrans(); - // 加入当前图纸的块表 + tr.Editor?.SetImpliedSelection(new ObjectId[0]); // 清空选择集 + + // 获取块名 + var blockNameNew = Path.GetFileNameWithoutExtension(_fileName); + while (tr.BlockTable.Has(blockNameNew)) + { + blockNameNew = Path.GetFileNameWithoutExtension(TimeName()); + Thread.Sleep(1); + } + // 新建块表记录 + var btrIdNew = tr.BlockTable.Add(blockNameNew); + + // 加入新建的块表记录 + /// 动态块粘贴,然后用:ctrl+z会导致动态块特性无法恢复, + /// 是因为它 var map = new IdMapping(); tr.Task(() => { tr.Database.WblockCloneObjects( new ObjectIdCollection(fileEntityIds.ToArray()), - tr.Database.BlockTableId, // 粘贴目标 + btrIdNew, // tr.Database.BlockTableId, // 粘贴目标 map, - DuplicateRecordCloning.Replace, + DuplicateRecordCloning.Ignore, false); }); - - // 在jig预览 - List ents = new(); - var dict = map.ToDictionary(); - foreach (var fent in fileEntityIds) - { - var id = dict[fent]; // 拷贝动态块的时候,这里全是不ok - if (!id.IsOk()) - continue; - - var ent = tr.GetObject(id); - if (ent != null) - ents.Add(ent); - } - - if (ents.Count == 0) - return; - Point3d basePoint = Point3d.Origin; if (!isBlock) { - // 求全部图元的左下角作为基点 + // 遍历块内 + // 获取左下角点作为基点 double minx = double.MaxValue; double miny = double.MaxValue; - ents.ForEach(ent => { + var btrNew = tr.GetObject(btrIdNew); + foreach (var id in btrNew!) + { + var ent = tr.GetObject(id); + if (ent == null) + continue; var info = ent.GetBoundingBoxEx(); + if (ent is BlockReference brf) + info.Move(brf.Position, Point3d.Origin); minx = minx > info.MinX ? info.MinX : minx; miny = miny > info.MinY ? info.MinY : miny; - }); + } basePoint = new Point3d(minx, miny, 0); } - var moveJig = new JigEx((mousePoint, drawEntitys) => { - ents.ForEach(ent => { - var entClone = (Entity)ent.Clone(); - entClone.Move(basePoint, mousePoint); - drawEntitys.Enqueue(entClone); - }); + // 预览并获取交互点 + using var moveJig = new JigEx((mousePoint, drawEntitys) => { + var blockref = new BlockReference(Point3d.Origin, btrIdNew); + blockref.Move(basePoint, mousePoint); + drawEntitys.Enqueue(blockref); }); - moveJig.SetOptions(basePoint, orthomode: false); + + moveJig.SetOptions(basePoint); var dr = moveJig.Drag(); + if (dr.Status == PromptStatus.None) // 空格为原点拷贝? + return; if (dr.Status != PromptStatus.OK) return; - // 直接粘贴 - if (!isBlock) + // 粘贴为块,创建图元 + if (isBlock) { tr.CurrentSpace.AddEntity(moveJig.Entitys); return; } - // 粘贴为块 - // 预览的时候移动了,这里要反向移动 - moveJig.Entitys.ForEach(ent => { + // 直接粘贴 + using ObjectIdCollection ids = new(); + ((BlockReference)moveJig.Entitys[0]).ForEach(a => ids.Add(a)); + + map = tr.CurrentSpace.DeepCloneEx(ids); + map.GetValues().ForEach(id => { + if (!id.IsOk()) + return; + var ent = tr.GetObject(id); + if (ent == null) + return; using (ent.ForWrite()) - ent.Move(moveJig.MousePointWcsLast, Point3d.Origin); + ent.Move(basePoint, moveJig.MousePointWcsLast); }); + } +} - // 获取块名 - var blockName = Path.GetFileNameWithoutExtension(_fileName); - while (tr.BlockTable.Has(blockName)) - { - blockName = Path.GetFileNameWithoutExtension(TimeName()); - Thread.Sleep(1); - } +public static class BlockReferenceHelper +{ + /// + /// 遍历块内 + /// + /// + /// + /// + /// + public static void ForEach(this BlockReference brf, Action action, DBTrans? tr = null) + { + if (action == null) + throw new ArgumentNullException(nameof(action)); + + tr ??= DBTrans.Top; - var btrId = tr.BlockTable.Add(blockName, moveJig.Entitys); - var blockId = tr.CurrentSpace.InsertBlock(Point3d.Origin, btrId); - var blockEnt = tr.GetObject(blockId); - if (blockEnt == null) + var btr = tr.GetObject(brf.BlockTableRecord); + if (btr == null) return; - using (blockEnt.ForWrite()) - blockEnt.Move(Point3d.Origin, moveJig.MousePointWcsLast); + foreach (var id in btr) + action.Invoke(id); } } \ No newline at end of file diff --git a/tests/TestShared/TestJig.cs b/tests/TestShared/TestJig.cs index 3f725ae..9342ac9 100644 --- a/tests/TestShared/TestJig.cs +++ b/tests/TestShared/TestJig.cs @@ -40,6 +40,7 @@ public static void TestCmd_jig33() if (prDrag.Status == PromptStatus.OK) break; } + moveJig.Dispose(); } @@ -60,9 +61,8 @@ public void TestCmd_Jig44() * 原因: 多段线与鼠标垂直点作为 BasePoint,jig鼠标点为确定点 * 所以需要先声明再传入指针,但是我发现null也可以. */ - JigEx? jig = null; JigPromptPointOptions? options = null; - jig = new JigEx((mousePoint, drawEntitys) => { + using var jig = new JigEx((mousePoint, drawEntitys) => { var closestPt = pl.GetClosestPointTo(mousePoint, false); // 回调过程中SetOptions会覆盖配置,所以如果想增加关键字或者修改基点, -- Gitee From 2b20f116e61c054a9d3a35a76051654c0277c8d5 Mon Sep 17 00:00:00 2001 From: liuqihong <540762622@qq.com> Date: Wed, 21 Sep 2022 04:20:06 +0800 Subject: [PATCH 517/675] =?UTF-8?q?=E4=BC=98=E5=8C=96=E4=BB=A3=E7=A0=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- tests/TestShared/Copyclip.cs | 229 ++++++++++++++++++++--------------- 1 file changed, 131 insertions(+), 98 deletions(-) diff --git a/tests/TestShared/Copyclip.cs b/tests/TestShared/Copyclip.cs index c6fe98e..65dafcc 100644 --- a/tests/TestShared/Copyclip.cs +++ b/tests/TestShared/Copyclip.cs @@ -6,7 +6,6 @@ namespace Test; using System; using System.Linq; using System.Threading; -using System.Windows.Media.Imaging; using Clipboard = System.Windows.Forms.Clipboard; // https://forums.autodesk.com/t5/net/paste-list-of-objects-from-clipboard-on-dwg-file-using-c-net/td-p/6797606 @@ -20,7 +19,11 @@ public void Init() Acap.DocumentManager.DocumentLockModeChanged += DocumentManager_DocumentLockModeChanged; } -#if test +#if false + // 想要重启cad之后还可以继续用剪贴板,那么就不要这个 + // 会出现永远存在临时文件夹的情况: + // 0x01 复制的时候,无法删除占用中的, + // 0x02 调试期间直接退出acad.exe [IFoxInitialize(isInitialize: false)] #endif public void Terminate() @@ -44,6 +47,7 @@ public void Terminate() } } + /// /// 反应器->命令否决触发命令前(不可锁文档) /// @@ -52,33 +56,51 @@ public void Terminate() void DocumentManager_DocumentLockModeChanged(object sender, DocumentLockModeChangedEventArgs e) { var up = e.GlobalCommandName.ToUpper(); - if (up == "COPYCLIP") - { - e.Veto(); - IFoxCopyclip(); - } - else if (up == "PASTECLIP") + + if (up == "PASTEBLOCK" || up == "PASTECLIP") { - // 由于不知道校验码是怎么构造的,所以只能否决命令,并且自己实现粘贴 - var fileName = GetCadPasteclip("AutoCAD"); - if (fileName == null) + // 桌子是如何做到 粘贴为块的时候读取到基点的? 而直接粘贴则是左下角 + // 是通过粘贴的字符串结构(乱码)提供的 + var ok = GetCadPasteclip("AutoCAD"); + if (!ok) return; // 如果剪贴板存在这个路径,才去否决,粘贴文字之类的由原本的命令执行 - _fileName = fileName; + Env.Print("粘贴来源: " + _fileName); + } + + + if (up == "COPYCLIP")// 复制 + { e.Veto(); - IFoxPasteclip(); + IFoxCopyclip(); } else if (up == "COPYBASE") //ctrl+shift+c 带基点复制 { e.Veto(); IFoxCopyBase(); } + else if (up == "PASTECLIP")// 粘贴 + { + e.Veto(); + IFoxPasteclip(); + } else if (up == "PASTEBLOCK") //ctrl+shift+v 粘贴为块 { e.Veto(); IFoxPasteBlock(); } + else if (up == "CUTCLIP") // 剪切 + { + e.Veto(); + List ids = new(); + Copy(false, ids); + + using var tr = new DBTrans(); + ids.ForEach(id => { + id.Erase(); + }); + } } /// @@ -119,16 +141,21 @@ public void IFoxPasteBlock() // 有了这个的话,还需要读取剪贴板吗? - // 需要的,剪贴板可能存在不是dwg路径,而是文字内容等 - static string? _fileName; - List _delFile = new(); + // 需要的,当前剪贴板中,有可能不是dwg路径,而是文字内容等 + string? _fileName; + // 基点 + Point3d? _basePoint; + // 基点字符串 + const string _BasePointStr = "BasePoint:"; + // 删除的文件,如果删除出错(占用),将一直在这个集合中,直到cad关闭 + readonly List _delFile = new(); /// /// 获取剪贴板路径 /// /// 控制不同的cad /// 获取是否成功 - string? GetCadPasteclip(string pasteclipStr = "AutoCAD") + bool GetCadPasteclip(string pasteclipStr) { // 获取剪贴板上面的保存路径 var format = Clipboard.GetDataObject() @@ -136,26 +163,41 @@ public void IFoxPasteBlock() .FirstOrDefault(f => f.StartsWith(pasteclipStr)); if (string.IsNullOrEmpty(format)) - return null; + return false; - string fileName; var data = (MemoryStream)Clipboard.GetData(format); using (var reader = new StreamReader(data)) { - fileName = reader.ReadToEnd(); - fileName = fileName.Replace("\0", string.Empty); - fileName = fileName.Substring(0, fileName.IndexOf(".DWG") + 4); + var str = reader.ReadToEnd(); + str = str.Replace("\0", string.Empty); + _fileName = str.Substring(0, str.IndexOf(".DWG") + 4); + + var bpstr = str.IndexOf(_BasePointStr); + if (bpstr > -1) + { + int start = bpstr + _BasePointStr.Length + 1;//1是(括号 + int end = str.IndexOf(")") - start; + var ptstr = str.Substring(start, end); + var ptArr = ptstr.Split(','); + if (ptArr != null) + { + _basePoint = new Point3d( + double.Parse(ptArr[0]), + double.Parse(ptArr[1]), + double.Parse(ptArr[2])); + } + } } - if (string.IsNullOrEmpty(Path.GetFileName(fileName))) - return null; - return fileName; + if (string.IsNullOrEmpty(Path.GetFileName(_fileName))) + return false; + return true; } /// /// 复制 /// /// - public void Copy(bool getPoint) + public void Copy(bool getPoint, List? objectIds = null) { Terminate(); @@ -167,7 +209,7 @@ public void Copy(bool getPoint) var sdb = doc.Database; var ed = doc.Editor; - List result = new(); + objectIds ??= new(); var psr = ed.SelectImplied();// 预选 if (psr.Status != PromptStatus.OK) { @@ -175,7 +217,7 @@ public void Copy(bool getPoint) if (psr.Status != PromptStatus.OK) return; } - result.AddRange(psr.Value.GetObjectIds()); + objectIds.AddRange(psr.Value.GetObjectIds()); Point3d basePoint = Point3d.Origin; if (getPoint) @@ -185,6 +227,28 @@ public void Copy(bool getPoint) return; basePoint = pr.Value; } + else + { + // 遍历块内 + // 获取左下角点作为基点 + double minx = double.MaxValue; + double miny = double.MaxValue; + double minz = double.MaxValue; + using var tr = new DBTrans(); + foreach (var id in objectIds) + { + var ent = tr.GetObject(id); + if (ent == null) + continue; + var info = ent.GetBoundingBoxEx(); + if (ent is BlockReference brf) + info.Move(brf.Position, Point3d.Origin); + minx = minx > info.MinX ? info.MinX : minx; + miny = miny > info.MinY ? info.MinY : miny; + minz = minz > info.MinZ ? info.MinZ : minz; + } + basePoint = new Point3d(minx, miny, minz); + } // 获取临时路径 do @@ -193,31 +257,14 @@ public void Copy(bool getPoint) Thread.Sleep(1); } while (File.Exists(_fileName)); - // 写入剪贴板 + // 写入剪贴板,这里arx有个结构体 var sb = new StringBuilder(); -#if true2 - //每个字符后面插入\0 - for (int i = 0; i < file.Length; i++) - { - sb.Append(file[i]); - sb.Append("\0"); - } - sb.Append("\0"); - var r15 = "R15"; - for (int i = 0; i < r15.Length; i++) - { - sb.Append(r15[i]); - sb.Append("\0"); - } - // 后面一段是还是啥?? 校验码?? - // 因为不知道,所以只能自己粘贴的时候处理了 -#else sb.Append(_fileName); -#endif + sb.Append(_BasePointStr + basePoint.ToString()); + var data = new MemoryStream(); var bytes = Encoding.Unicode.GetBytes(sb.ToString()); data.Write(bytes, 0, bytes.Length); - Clipboard.SetData($"AutoCAD.r{Acap.Version.Major}", data); // 克隆到目标块表内 @@ -225,22 +272,13 @@ public void Copy(bool getPoint) fileTr.Task(() => { var map = new IdMapping(); sdb.WblockCloneObjects( - new ObjectIdCollection(result.ToArray()), + new ObjectIdCollection(objectIds.ToArray()), fileTr.ModelSpace.ObjectId, map, DuplicateRecordCloning.Replace, false); }); - // 移动到原点 - if (!basePoint.IsEqualTo(Point3d.Origin)) - { - var ents = fileTr.ModelSpace.GetEntities(); - foreach (var ent in ents) - using (ent.ForWrite()) - ent.Move(basePoint, Point3d.Origin); - } - // 大于dwg07格式,保存为07,以实现高低版本通用剪贴板 if ((int)DwgVersion.Current >= 27) { @@ -257,6 +295,8 @@ public void Copy(bool getPoint) _delFile.Add(_fileName); } + + /// /// 获取时间名 /// @@ -282,8 +322,6 @@ public void Paste(bool isBlock) if (_fileName == null) return; - Env.Printl("粘贴来源: " + _fileName); - // 获取临时文件的图元id var fileEntityIds = new List(); using (var fileTr = new DBTrans(_fileName, false, FileOpenMode.OpenForReadAndAllShare)) @@ -306,7 +344,7 @@ public void Paste(bool isBlock) var btrIdNew = tr.BlockTable.Add(blockNameNew); // 加入新建的块表记录 - /// 动态块粘贴,然后用:ctrl+z会导致动态块特性无法恢复, + // 动态块粘贴,然后用:ctrl+z会导致动态块特性无法恢复, /// 是因为它 var map = new IdMapping(); tr.Task(() => { @@ -318,32 +356,25 @@ public void Paste(bool isBlock) false); }); - Point3d basePoint = Point3d.Origin; - if (!isBlock) + Point3d basePoint = _basePoint!.Value; + + // 移动块内的带基点 + var btr = tr.GetObject(btrIdNew); + if (btr == null) + return; + foreach (var id in btr) { - // 遍历块内 - // 获取左下角点作为基点 - double minx = double.MaxValue; - double miny = double.MaxValue; - var btrNew = tr.GetObject(btrIdNew); - foreach (var id in btrNew!) - { - var ent = tr.GetObject(id); - if (ent == null) - continue; - var info = ent.GetBoundingBoxEx(); - if (ent is BlockReference brf) - info.Move(brf.Position, Point3d.Origin); - minx = minx > info.MinX ? info.MinX : minx; - miny = miny > info.MinY ? info.MinY : miny; - } - basePoint = new Point3d(minx, miny, 0); + var ent = tr.GetObject(id); + if (ent == null) + return; + using (ent.ForWrite()) + ent.Move(basePoint, Point3d.Origin); } // 预览并获取交互点 using var moveJig = new JigEx((mousePoint, drawEntitys) => { var blockref = new BlockReference(Point3d.Origin, btrIdNew); - blockref.Move(basePoint, mousePoint); + blockref.Move(Point3d.Origin, mousePoint); drawEntitys.Enqueue(blockref); }); @@ -354,27 +385,29 @@ public void Paste(bool isBlock) if (dr.Status != PromptStatus.OK) return; - // 粘贴为块,创建图元 if (isBlock) { + // 粘贴为块,创建图元 tr.CurrentSpace.AddEntity(moveJig.Entitys); - return; } - - // 直接粘贴 - using ObjectIdCollection ids = new(); - ((BlockReference)moveJig.Entitys[0]).ForEach(a => ids.Add(a)); - - map = tr.CurrentSpace.DeepCloneEx(ids); - map.GetValues().ForEach(id => { - if (!id.IsOk()) - return; - var ent = tr.GetObject(id); - if (ent == null) - return; - using (ent.ForWrite()) - ent.Move(basePoint, moveJig.MousePointWcsLast); - }); + else + { + // 直接粘贴 + using ObjectIdCollection ids = new(); + var brf = (BlockReference)moveJig.Entitys[0]; + brf.ForEach(id => ids.Add(id)); + + map = tr.CurrentSpace.DeepCloneEx(ids); + map.GetValues().ForEach(id => { + if (!id.IsOk()) + return; + var ent = tr.GetObject(id); + if (ent == null) + return; + using (ent.ForWrite()) + ent.Move(Point3d.Origin, moveJig.MousePointWcsLast); + }); + } } } -- Gitee From f6e21e8835172b35a8d80c38cc8b8ea50151b20b Mon Sep 17 00:00:00 2001 From: liuqihong <540762622@qq.com> Date: Wed, 21 Sep 2022 02:12:59 +0000 Subject: [PATCH 518/675] update tests/TestShared/Copyclip.cs. Signed-off-by: liuqihong <540762622@qq.com> --- tests/TestShared/Copyclip.cs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/tests/TestShared/Copyclip.cs b/tests/TestShared/Copyclip.cs index 65dafcc..d99bd3b 100644 --- a/tests/TestShared/Copyclip.cs +++ b/tests/TestShared/Copyclip.cs @@ -1,4 +1,4 @@ -#define test +#define test namespace Test; @@ -114,7 +114,7 @@ public void IFoxCopyclip() /// /// 带基点复制 /// - [CommandMethod(nameof(IFoxCopyBase))] + [CommandMethod(nameof(IFoxCopyBase), CommandFlags.UsePickSet)] public void IFoxCopyBase() { Copy(true); @@ -130,7 +130,7 @@ public void IFoxPasteclip() /// /// 粘贴为块 /// - [CommandMethod(nameof(IFoxPasteBlock), CommandFlags.UsePickSet)] + [CommandMethod(nameof(IFoxPasteBlock))] public void IFoxPasteBlock() { Paste(true); -- Gitee From da85ce6e64471e80064a58d9d852cc30b5209549 Mon Sep 17 00:00:00 2001 From: liuqihong <540762622@qq.com> Date: Wed, 21 Sep 2022 21:59:09 +0800 Subject: [PATCH 519/675] =?UTF-8?q?=E4=BC=98=E5=8C=96=E4=BB=A3=E7=A0=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- tests/TestShared/Copyclip.cs | 239 +++++++++++++++++++---------------- 1 file changed, 132 insertions(+), 107 deletions(-) diff --git a/tests/TestShared/Copyclip.cs b/tests/TestShared/Copyclip.cs index d99bd3b..b215c86 100644 --- a/tests/TestShared/Copyclip.cs +++ b/tests/TestShared/Copyclip.cs @@ -42,8 +42,10 @@ public void Terminate() File.Delete(_delFile[i]); _delFile.RemoveAt(i); } - catch // 占用的时候无法删除 - { } + catch + { + Env.Printl("无法删除(是否占用):" + _delFile[i]); + } } } @@ -66,14 +68,14 @@ void DocumentManager_DocumentLockModeChanged(object sender, DocumentLockModeChan return; // 如果剪贴板存在这个路径,才去否决,粘贴文字之类的由原本的命令执行 - Env.Print("粘贴来源: " + _fileName); + Env.Print("粘贴来源: " + _tmpFileName); } if (up == "COPYCLIP")// 复制 { e.Veto(); - IFoxCopyclip(); + IFoxCopyClip(); } else if (up == "COPYBASE") //ctrl+shift+c 带基点复制 { @@ -83,7 +85,7 @@ void DocumentManager_DocumentLockModeChanged(object sender, DocumentLockModeChan else if (up == "PASTECLIP")// 粘贴 { e.Veto(); - IFoxPasteclip(); + IFoxPasteClip(); } else if (up == "PASTEBLOCK") //ctrl+shift+v 粘贴为块 { @@ -93,21 +95,15 @@ void DocumentManager_DocumentLockModeChanged(object sender, DocumentLockModeChan else if (up == "CUTCLIP") // 剪切 { e.Veto(); - List ids = new(); - Copy(false, ids); - - using var tr = new DBTrans(); - ids.ForEach(id => { - id.Erase(); - }); + Copy(false, true); } } /// - /// 复制命令 + /// 复制 /// - [CommandMethod(nameof(IFoxCopyclip), CommandFlags.UsePickSet)] - public void IFoxCopyclip() + [CommandMethod(nameof(IFoxCopyClip), CommandFlags.UsePickSet)] + public void IFoxCopyClip() { Copy(false); } @@ -120,10 +116,10 @@ public void IFoxCopyBase() Copy(true); } /// - /// 粘贴命令 + /// 粘贴 /// - [CommandMethod(nameof(IFoxPasteclip))] - public void IFoxPasteclip() + [CommandMethod(nameof(IFoxPasteClip))] + public void IFoxPasteClip() { Paste(false); } @@ -142,7 +138,7 @@ public void IFoxPasteBlock() // 有了这个的话,还需要读取剪贴板吗? // 需要的,当前剪贴板中,有可能不是dwg路径,而是文字内容等 - string? _fileName; + string? _tmpFileName; // 基点 Point3d? _basePoint; // 基点字符串 @@ -170,13 +166,13 @@ bool GetCadPasteclip(string pasteclipStr) { var str = reader.ReadToEnd(); str = str.Replace("\0", string.Empty); - _fileName = str.Substring(0, str.IndexOf(".DWG") + 4); + _tmpFileName = str.Substring(0, str.IndexOf(".DWG") + 4); var bpstr = str.IndexOf(_BasePointStr); if (bpstr > -1) { int start = bpstr + _BasePointStr.Length + 1;//1是(括号 - int end = str.IndexOf(")") - start; + int end = str.IndexOf(")", start) - start; var ptstr = str.Substring(start, end); var ptArr = ptstr.Split(','); if (ptArr != null) @@ -188,7 +184,7 @@ bool GetCadPasteclip(string pasteclipStr) } } } - if (string.IsNullOrEmpty(Path.GetFileName(_fileName))) + if (string.IsNullOrEmpty(Path.GetFileName(_tmpFileName))) return false; return true; } @@ -197,7 +193,7 @@ bool GetCadPasteclip(string pasteclipStr) /// 复制 /// /// - public void Copy(bool getPoint, List? objectIds = null) + public void Copy(bool getPoint, bool isEraseSsget = false) { Terminate(); @@ -205,11 +201,10 @@ public void Copy(bool getPoint, List? objectIds = null) if (dm.Count == 0) return; - var doc = dm.MdiActiveDocument; - var sdb = doc.Database; - var ed = doc.Editor; + using var tr = new DBTrans(); + var ed = tr.Editor!; - objectIds ??= new(); + var objectIds = new List(); var psr = ed.SelectImplied();// 预选 if (psr.Status != PromptStatus.OK) { @@ -219,13 +214,12 @@ public void Copy(bool getPoint, List? objectIds = null) } objectIds.AddRange(psr.Value.GetObjectIds()); - Point3d basePoint = Point3d.Origin; if (getPoint) { var pr = ed.GetPoint("\n选择基点"); if (pr.Status != PromptStatus.OK) return; - basePoint = pr.Value; + _basePoint = pr.Value; } else { @@ -234,7 +228,6 @@ public void Copy(bool getPoint, List? objectIds = null) double minx = double.MaxValue; double miny = double.MaxValue; double minz = double.MaxValue; - using var tr = new DBTrans(); foreach (var id in objectIds) { var ent = tr.GetObject(id); @@ -247,20 +240,20 @@ public void Copy(bool getPoint, List? objectIds = null) miny = miny > info.MinY ? info.MinY : miny; minz = minz > info.MinZ ? info.MinZ : minz; } - basePoint = new Point3d(minx, miny, minz); + _basePoint = new Point3d(minx, miny, minz); } // 获取临时路径 do { - _fileName = TimeName(); + _tmpFileName = TimeName(); Thread.Sleep(1); - } while (File.Exists(_fileName)); + } while (File.Exists(_tmpFileName)); // 写入剪贴板,这里arx有个结构体 var sb = new StringBuilder(); - sb.Append(_fileName); - sb.Append(_BasePointStr + basePoint.ToString()); + sb.Append(_tmpFileName); + sb.Append(_BasePointStr + _basePoint.ToString()); var data = new MemoryStream(); var bytes = Encoding.Unicode.GetBytes(sb.ToString()); @@ -268,31 +261,41 @@ public void Copy(bool getPoint, List? objectIds = null) Clipboard.SetData($"AutoCAD.r{Acap.Version.Major}", data); // 克隆到目标块表内 - using var fileTr = new DBTrans(_fileName); - fileTr.Task(() => { - var map = new IdMapping(); - sdb.WblockCloneObjects( - new ObjectIdCollection(objectIds.ToArray()), - fileTr.ModelSpace.ObjectId, - map, - DuplicateRecordCloning.Replace, - false); - }); - - // 大于dwg07格式,保存为07,以实现高低版本通用剪贴板 - if ((int)DwgVersion.Current >= 27) - { - fileTr.SaveFile((DwgVersion)27, false); - } - else + using (var fileTr = new DBTrans(_tmpFileName)) { + fileTr.Task(() => { + var map = new IdMapping(); + tr.Database.WblockCloneObjects( + new ObjectIdCollection(objectIds.ToArray()), + fileTr.ModelSpace.ObjectId, + map, + DuplicateRecordCloning.Replace, + false); + }); + + // 大于dwg07格式,保存为07,以实现高低版本通用剪贴板 + if ((int)DwgVersion.Current >= 27) + { + fileTr.SaveFile((DwgVersion)27, false); + } + else + { #pragma warning disable CS0162 // 检测到无法访问的代码 - fileTr.SaveFile(); // 低于dwg07格式的,本工程没有支持cad06dll,所以这里只是展示 + fileTr.SaveFile(); // 低于dwg07格式的,本工程没有支持cad06dll,所以这里只是展示 #pragma warning restore CS0162 // 检测到无法访问的代码 + } } - if (!_delFile.Contains(_fileName)) - _delFile.Add(_fileName); + if (!_delFile.Contains(_tmpFileName)) + _delFile.Add(_tmpFileName); + + // 剪切时候删除 + if (isEraseSsget) + { + objectIds.ForEach(id => { + id.Erase(); + }); + } } @@ -319,12 +322,12 @@ public void Paste(bool isBlock) if (dm.Count == 0) return; - if (_fileName == null) + if (_tmpFileName == null) return; // 获取临时文件的图元id var fileEntityIds = new List(); - using (var fileTr = new DBTrans(_fileName, false, FileOpenMode.OpenForReadAndAllShare)) + using (var fileTr = new DBTrans(_tmpFileName, false, FileOpenMode.OpenForReadAndAllShare)) { foreach (var id in fileTr.ModelSpace) fileEntityIds.Add(id); @@ -333,15 +336,10 @@ public void Paste(bool isBlock) using var tr = new DBTrans(); tr.Editor?.SetImpliedSelection(new ObjectId[0]); // 清空选择集 - // 获取块名 - var blockNameNew = Path.GetFileNameWithoutExtension(_fileName); - while (tr.BlockTable.Has(blockNameNew)) - { - blockNameNew = Path.GetFileNameWithoutExtension(TimeName()); - Thread.Sleep(1); - } // 新建块表记录 - var btrIdNew = tr.BlockTable.Add(blockNameNew); + var btr = CreateBlockTableRecord(tr); + if (btr == null) + return; // 加入新建的块表记录 // 动态块粘贴,然后用:ctrl+z会导致动态块特性无法恢复, @@ -350,7 +348,7 @@ public void Paste(bool isBlock) tr.Task(() => { tr.Database.WblockCloneObjects( new ObjectIdCollection(fileEntityIds.ToArray()), - btrIdNew, // tr.Database.BlockTableId, // 粘贴目标 + btr.ObjectId, // tr.Database.BlockTableId, // 粘贴目标 map, DuplicateRecordCloning.Ignore, false); @@ -358,12 +356,14 @@ public void Paste(bool isBlock) Point3d basePoint = _basePoint!.Value; - // 移动块内的带基点 - var btr = tr.GetObject(btrIdNew); - if (btr == null) - return; + // 移动块内,从基点到原点 foreach (var id in btr) { + if (!id.IsOk()) + { + Env.Printl("jig预览块内有克隆失败的东西"); + continue; + } var ent = tr.GetObject(id); if (ent == null) return; @@ -372,12 +372,12 @@ public void Paste(bool isBlock) } // 预览并获取交互点 + // 天正此处可能存在失败 using var moveJig = new JigEx((mousePoint, drawEntitys) => { - var blockref = new BlockReference(Point3d.Origin, btrIdNew); + var blockref = new BlockReference(Point3d.Origin, btr.ObjectId); blockref.Move(Point3d.Origin, mousePoint); drawEntitys.Enqueue(blockref); }); - moveJig.SetOptions(basePoint); var dr = moveJig.Drag(); if (dr.Status == PromptStatus.None) // 空格为原点拷贝? @@ -389,48 +389,73 @@ public void Paste(bool isBlock) { // 粘贴为块,创建图元 tr.CurrentSpace.AddEntity(moveJig.Entitys); + return; } - else + + // 直接粘贴 + using ObjectIdCollection ids = new(); + foreach (var id in btr) { - // 直接粘贴 - using ObjectIdCollection ids = new(); - var brf = (BlockReference)moveJig.Entitys[0]; - brf.ForEach(id => ids.Add(id)); - - map = tr.CurrentSpace.DeepCloneEx(ids); - map.GetValues().ForEach(id => { - if (!id.IsOk()) - return; - var ent = tr.GetObject(id); - if (ent == null) - return; - using (ent.ForWrite()) - ent.Move(Point3d.Origin, moveJig.MousePointWcsLast); - }); + if (!id.IsOk()) + continue; + ids.Add(id); } + + // 深度克隆,然后平移到当前鼠标点位置 + map = tr.CurrentSpace.DeepCloneEx(ids); + map.GetValues().ForEach(id => { + if (!id.IsOk()) + return; + var ent = tr.GetObject(id); + if (ent == null) + return; + using (ent.ForWrite()) + ent.Move(Point3d.Origin, moveJig.MousePointWcsLast); + }); + + // 删除jig预览的块表记录 + using (btr.ForWrite()) + btr.Erase(); } -} -public static class BlockReferenceHelper -{ /// - /// 遍历块内 + /// 创建块表记录 /// - /// - /// /// - /// - public static void ForEach(this BlockReference brf, Action action, DBTrans? tr = null) + /// + BlockTableRecord? CreateBlockTableRecord(DBTrans tr) { - if (action == null) - throw new ArgumentNullException(nameof(action)); - - tr ??= DBTrans.Top; - - var btr = tr.GetObject(brf.BlockTableRecord); - if (btr == null) - return; - foreach (var id in btr) - action.Invoke(id); + var blockNameNew = Path.GetFileNameWithoutExtension(_tmpFileName); + while (tr.BlockTable.Has(blockNameNew)) + { + blockNameNew = Path.GetFileNameWithoutExtension(TimeName()); + Thread.Sleep(1); + } + var btrIdNew = tr.BlockTable.Add(blockNameNew); + return tr.GetObject(btrIdNew); } -} \ No newline at end of file +} + +//public static class BlockReferenceHelper +//{ +// /// +// /// 遍历块内 +// /// +// /// +// /// +// /// +// /// +// public static void ForEach(this BlockReference brf, Action action, DBTrans? tr = null) +// { +// if (action == null) +// throw new ArgumentNullException(nameof(action)); + +// tr ??= DBTrans.Top; + +// var btr = tr.GetObject(brf.BlockTableRecord); +// if (btr == null) +// return; +// foreach (var id in btr) +// action.Invoke(id); +// } +//} \ No newline at end of file -- Gitee From ce5b4052ecf49dca2164e4c8128ddc40531bba89 Mon Sep 17 00:00:00 2001 From: liuqihong <540762622@qq.com> Date: Wed, 21 Sep 2022 23:06:09 +0800 Subject: [PATCH 520/675] =?UTF-8?q?=E5=A2=9E=E5=8A=A0=E5=8A=9F=E8=83=BD?= =?UTF-8?q?=E8=AF=B4=E6=98=8E,=E5=88=A9=E7=94=A8=E7=BB=93=E6=9E=84?= =?UTF-8?q?=E5=82=A8=E5=AD=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- tests/TestShared/Copyclip.cs | 217 +++++++++++++++++++++++------------ 1 file changed, 141 insertions(+), 76 deletions(-) diff --git a/tests/TestShared/Copyclip.cs b/tests/TestShared/Copyclip.cs index b215c86..55c0b37 100644 --- a/tests/TestShared/Copyclip.cs +++ b/tests/TestShared/Copyclip.cs @@ -1,23 +1,58 @@ #define test namespace Test; - using IFoxCAD.Cad; using System; using System.Linq; using System.Threading; using Clipboard = System.Windows.Forms.Clipboard; -// https://forums.autodesk.com/t5/net/paste-list-of-objects-from-clipboard-on-dwg-file-using-c-net/td-p/6797606 +/* + * 0x01 (已完成) + * 跨cad复制,由于高版本会保存为当前dwg格式,所以我们将所有都保存为07格式(有动态块), + * 就可以多个版本cad相互复制粘贴了 + * + * 0x02 + * 设置一个粘贴板栈,用tmp.config储存(路径和粘贴基点),ctrl+shfit+v v v 就是三次前的剪贴板内容 + * 这样就更好地跨cad复制了 + * + * 0x03 + * 天正图元的复制粘贴出错原因 + * + * 引用技术贴: + * https://forums.autodesk.com/t5/net/paste-list-of-objects-from-clipboard-on-dwg-file-using-c-net/td-p/6797606 + */ public class InterceptCopyclip { + /// + /// 剪贴板结构
    + /// 此处没有按照ARX的tagClipboardInfo结构复刻(别问,问就是不会) + ///
    + struct TagClipboardInfo + { + /// + /// dwg储存路径
    + /// 有了这个的话,还需要读取剪贴板吗?
    + /// 需要的,当前剪贴板中,有可能不是dwg路径,而是文字内容等
    + ///
    + public string File; + /// + /// 粘贴基点 + /// + public Point3d Point; + /// + /// 粘贴基点字符串 + /// + public const string PointStr = "BasePoint:"; + } + #if test [IFoxInitialize] -#endif public void Init() { Acap.DocumentManager.DocumentLockModeChanged += DocumentManager_DocumentLockModeChanged; } +#endif #if false // 想要重启cad之后还可以继续用剪贴板,那么就不要这个 @@ -49,7 +84,6 @@ public void Terminate() } } - /// /// 反应器->命令否决触发命令前(不可锁文档) /// @@ -62,16 +96,15 @@ void DocumentManager_DocumentLockModeChanged(object sender, DocumentLockModeChan if (up == "PASTEBLOCK" || up == "PASTECLIP") { // 桌子是如何做到 粘贴为块的时候读取到基点的? 而直接粘贴则是左下角 - // 是通过粘贴的字符串结构(乱码)提供的 - var ok = GetCadPasteclip("AutoCAD"); - if (!ok) + // 是通过粘贴的字符串结构(乱码部分)提供的 + // 剪贴板不存在这个路径,粘贴文字之类的,由原本的命令执行 + var tag = GetCadPasteclip("AutoCAD"); + if (tag == null) return; - - // 如果剪贴板存在这个路径,才去否决,粘贴文字之类的由原本的命令执行 - Env.Print("粘贴来源: " + _tmpFileName); + Env.Print("粘贴来源: " + tag.Value.File); + _clipboardInfo = tag; } - if (up == "COPYCLIP")// 复制 { e.Veto(); @@ -134,15 +167,7 @@ public void IFoxPasteBlock() - - - // 有了这个的话,还需要读取剪贴板吗? - // 需要的,当前剪贴板中,有可能不是dwg路径,而是文字内容等 - string? _tmpFileName; - // 基点 - Point3d? _basePoint; - // 基点字符串 - const string _BasePointStr = "BasePoint:"; + TagClipboardInfo? _clipboardInfo; // 删除的文件,如果删除出错(占用),将一直在这个集合中,直到cad关闭 readonly List _delFile = new(); @@ -151,7 +176,7 @@ public void IFoxPasteBlock() ///
    /// 控制不同的cad /// 获取是否成功 - bool GetCadPasteclip(string pasteclipStr) + TagClipboardInfo? GetCadPasteclip(string pasteclipStr) { // 获取剪贴板上面的保存路径 var format = Clipboard.GetDataObject() @@ -159,34 +184,35 @@ bool GetCadPasteclip(string pasteclipStr) .FirstOrDefault(f => f.StartsWith(pasteclipStr)); if (string.IsNullOrEmpty(format)) - return false; + return null; + var tag = new TagClipboardInfo(); var data = (MemoryStream)Clipboard.GetData(format); using (var reader = new StreamReader(data)) { var str = reader.ReadToEnd(); str = str.Replace("\0", string.Empty); - _tmpFileName = str.Substring(0, str.IndexOf(".DWG") + 4); + tag.File = str.Substring(0, str.IndexOf(".DWG") + 4); - var bpstr = str.IndexOf(_BasePointStr); + var bpstr = str.IndexOf(TagClipboardInfo.PointStr); if (bpstr > -1) { - int start = bpstr + _BasePointStr.Length + 1;//1是(括号 + int start = bpstr + TagClipboardInfo.PointStr.Length + 1;//1是(括号 int end = str.IndexOf(")", start) - start; var ptstr = str.Substring(start, end); var ptArr = ptstr.Split(','); if (ptArr != null) { - _basePoint = new Point3d( + tag.Point = new Point3d( double.Parse(ptArr[0]), double.Parse(ptArr[1]), double.Parse(ptArr[2])); } } } - if (string.IsNullOrEmpty(Path.GetFileName(_tmpFileName))) - return false; - return true; + if (string.IsNullOrEmpty(Path.GetFileName(tag.File))) + return null; + return tag; } /// @@ -207,19 +233,19 @@ public void Copy(bool getPoint, bool isEraseSsget = false) var objectIds = new List(); var psr = ed.SelectImplied();// 预选 if (psr.Status != PromptStatus.OK) - { psr = ed.GetSelection();// 手选 - if (psr.Status != PromptStatus.OK) - return; - } + if (psr.Status != PromptStatus.OK) + return; + objectIds.AddRange(psr.Value.GetObjectIds()); + var clipboardInfo = new TagClipboardInfo(); if (getPoint) { var pr = ed.GetPoint("\n选择基点"); if (pr.Status != PromptStatus.OK) return; - _basePoint = pr.Value; + clipboardInfo.Point = pr.Value; } else { @@ -240,20 +266,21 @@ public void Copy(bool getPoint, bool isEraseSsget = false) miny = miny > info.MinY ? info.MinY : miny; minz = minz > info.MinZ ? info.MinZ : minz; } - _basePoint = new Point3d(minx, miny, minz); + clipboardInfo.Point = new Point3d(minx, miny, minz); } // 获取临时路径 do { - _tmpFileName = TimeName(); + clipboardInfo.File = TimeName(); Thread.Sleep(1); - } while (File.Exists(_tmpFileName)); + } while (File.Exists(clipboardInfo.File)); + // 手动序列化 // 写入剪贴板,这里arx有个结构体 var sb = new StringBuilder(); - sb.Append(_tmpFileName); - sb.Append(_BasePointStr + _basePoint.ToString()); + sb.Append(clipboardInfo.File); + sb.Append(TagClipboardInfo.PointStr + clipboardInfo.Point.ToString()); var data = new MemoryStream(); var bytes = Encoding.Unicode.GetBytes(sb.ToString()); @@ -261,7 +288,7 @@ public void Copy(bool getPoint, bool isEraseSsget = false) Clipboard.SetData($"AutoCAD.r{Acap.Version.Major}", data); // 克隆到目标块表内 - using (var fileTr = new DBTrans(_tmpFileName)) + using (var fileTr = new DBTrans(clipboardInfo.File)) { fileTr.Task(() => { var map = new IdMapping(); @@ -286,9 +313,6 @@ public void Copy(bool getPoint, bool isEraseSsget = false) } } - if (!_delFile.Contains(_tmpFileName)) - _delFile.Add(_tmpFileName); - // 剪切时候删除 if (isEraseSsget) { @@ -296,20 +320,9 @@ public void Copy(bool getPoint, bool isEraseSsget = false) id.Erase(); }); } - } - - - /// - /// 获取时间名 - /// - /// - string TimeName() - { - var t1 = DateTime.Now.ToString("yyyyMMddHHmmssfff"); - t1 = Convert.ToInt32(t1.GetHashCode()).ToString("X"); - var t2 = Convert.ToInt32(t1.GetHashCode()).ToString("X");// 这里是为了满足长度而做的 - return Path.GetTempPath() + "A$" + t1 + t2[0] + ".DWG"; + if (!_delFile.Contains(clipboardInfo.File)) + _delFile.Add(clipboardInfo.File); } /// @@ -322,22 +335,32 @@ public void Paste(bool isBlock) if (dm.Count == 0) return; - if (_tmpFileName == null) + if (_clipboardInfo == null) return; + var clipboardInfo = _clipboardInfo.Value; // 获取临时文件的图元id var fileEntityIds = new List(); - using (var fileTr = new DBTrans(_tmpFileName, false, FileOpenMode.OpenForReadAndAllShare)) + using (var fileTr = new DBTrans(clipboardInfo.File, false, FileOpenMode.OpenForReadAndAllShare)) { foreach (var id in fileTr.ModelSpace) - fileEntityIds.Add(id); + if (id.IsOk()) + fileEntityIds.Add(id); } using var tr = new DBTrans(); +#if test + // 给辰的测试 + //var pr = tr.Editor?.GetPoint("aaa"); + //if (pr != null && pr.Status == PromptStatus.OK) + //{ + // tr.Editor?.WriteMessage("获取了点:" + pr.Value); + //} +#endif tr.Editor?.SetImpliedSelection(new ObjectId[0]); // 清空选择集 // 新建块表记录 - var btr = CreateBlockTableRecord(tr); + var btr = CreateBlockTableRecord(tr, clipboardInfo.File); if (btr == null) return; @@ -354,7 +377,6 @@ public void Paste(bool isBlock) false); }); - Point3d basePoint = _basePoint!.Value; // 移动块内,从基点到原点 foreach (var id in btr) @@ -368,31 +390,61 @@ public void Paste(bool isBlock) if (ent == null) return; using (ent.ForWrite()) - ent.Move(basePoint, Point3d.Origin); + ent.Move(clipboardInfo.Point, Point3d.Origin); } // 预览并获取交互点 - // 天正此处可能存在失败 + // 天正此处可能存在失败:天正图元不给你jig接口调用之类的 using var moveJig = new JigEx((mousePoint, drawEntitys) => { var blockref = new BlockReference(Point3d.Origin, btr.ObjectId); blockref.Move(Point3d.Origin, mousePoint); drawEntitys.Enqueue(blockref); }); - moveJig.SetOptions(basePoint); + moveJig.SetOptions(clipboardInfo.Point) + .Keywords.Add("A", "A", "引线点粘贴(A)"); + var dr = moveJig.Drag(); - if (dr.Status == PromptStatus.None) // 空格为原点拷贝? - return; - if (dr.Status != PromptStatus.OK) + Point3d moveTo = Point3d.Origin; + if (dr.Status == PromptStatus.Keyword) + moveTo = clipboardInfo.Point; + else if (dr.Status == PromptStatus.OK) + moveTo = moveJig.MousePointWcsLast; + else return; if (isBlock) + PasteIsBlock(tr, moveJig.Entitys, moveJig.MousePointWcsLast, moveTo); + else + PasteNotBlock(tr, btr, Point3d.Origin, moveTo); + } + + /// + /// 粘贴为块 + /// + /// + /// + /// + /// + static void PasteIsBlock(DBTrans tr, Entity[] entitys, Point3d move, Point3d moveTo) + { + if (!move.IsEqualTo(moveTo, new Tolerance(1e-6, 1e-6))) { - // 粘贴为块,创建图元 - tr.CurrentSpace.AddEntity(moveJig.Entitys); - return; + entitys.ForEach(ent => { + ent.Move(move, moveTo); + }); } + tr.CurrentSpace.AddEntity(entitys); + } - // 直接粘贴 + /// + /// 直接粘贴(不为块参照) + /// + /// + /// + /// 它总是为 + /// 目标点 + static void PasteNotBlock(DBTrans tr, BlockTableRecord btr, Point3d move, Point3d moveTo) + { using ObjectIdCollection ids = new(); foreach (var id in btr) { @@ -401,8 +453,8 @@ public void Paste(bool isBlock) ids.Add(id); } - // 深度克隆,然后平移到当前鼠标点位置 - map = tr.CurrentSpace.DeepCloneEx(ids); + // 深度克隆,然后平移到当前目标点位置 + var map = tr.CurrentSpace.DeepCloneEx(ids); map.GetValues().ForEach(id => { if (!id.IsOk()) return; @@ -410,7 +462,7 @@ public void Paste(bool isBlock) if (ent == null) return; using (ent.ForWrite()) - ent.Move(Point3d.Origin, moveJig.MousePointWcsLast); + ent.Move(move, moveTo); }); // 删除jig预览的块表记录 @@ -418,14 +470,27 @@ public void Paste(bool isBlock) btr.Erase(); } + + /// + /// 获取时间名 + /// + /// + string TimeName() + { + var t1 = DateTime.Now.ToString("yyyyMMddHHmmssfff"); + t1 = Convert.ToInt32(t1.GetHashCode()).ToString("X"); + var t2 = Convert.ToInt32(t1.GetHashCode()).ToString("X");// 这里是为了满足长度而做的 + return Path.GetTempPath() + "A$" + t1 + t2[0] + ".DWG"; + } + /// /// 创建块表记录 /// /// /// - BlockTableRecord? CreateBlockTableRecord(DBTrans tr) + BlockTableRecord? CreateBlockTableRecord(DBTrans tr, string tmpFile) { - var blockNameNew = Path.GetFileNameWithoutExtension(_tmpFileName); + var blockNameNew = Path.GetFileNameWithoutExtension(tmpFile); while (tr.BlockTable.Has(blockNameNew)) { blockNameNew = Path.GetFileNameWithoutExtension(TimeName()); -- Gitee From 110bd8ccc352a060fe5c3308c6357b4cc2b570e6 Mon Sep 17 00:00:00 2001 From: liuqihong <540762622@qq.com> Date: Thu, 22 Sep 2022 04:20:12 +0800 Subject: [PATCH 521/675] =?UTF-8?q?=E5=89=AA=E8=B4=B4=E6=9D=BF=E5=B0=81?= =?UTF-8?q?=E8=A3=85=E5=87=BD=E6=95=B0=E5=AE=9E=E7=8E=B0=E5=86=85=E8=81=9A?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- tests/TestShared/Copyclip.cs | 218 ++++++++++++++++++++--------------- 1 file changed, 124 insertions(+), 94 deletions(-) diff --git a/tests/TestShared/Copyclip.cs b/tests/TestShared/Copyclip.cs index 55c0b37..4014ee6 100644 --- a/tests/TestShared/Copyclip.cs +++ b/tests/TestShared/Copyclip.cs @@ -43,7 +43,91 @@ struct TagClipboardInfo /// /// 粘贴基点字符串 /// - public const string PointStr = "BasePoint:"; + const string PointStr = "BasePoint:"; + + public override string ToString() + { + var sb = new StringBuilder(); + sb.Append(File); + sb.Append(PointStr); + sb.Append(Point); + return sb.ToString(); + } + + /// + /// 写入剪贴板 + /// + public void SetClipboard() + { + using var data = new MemoryStream(); + var bytes = Encoding.Unicode.GetBytes(ToString()); + data.Write(bytes, 0, bytes.Length); + Clipboard.SetData($"AutoCAD.r{Acap.Version.Major}", data); + } + + /// + /// 获取剪贴板 + /// + /// 控制不同的cad + /// 获取是否成功 + public static TagClipboardInfo? GetClipboard(string pasteclipStr) + { + // 获取剪贴板上面的保存路径 + var format = Clipboard.GetDataObject() + .GetFormats() + .FirstOrDefault(f => f.StartsWith(pasteclipStr)); + if (string.IsNullOrEmpty(format)) + return null; + + using var data = (MemoryStream)Clipboard.GetData(format); + using var reader = new StreamReader(data); + + // 获取剪贴板设置的:临时文件夹中的dwg文件路径 + var str = reader.ReadToEnd(); + str = str.Replace("\0", string.Empty); + var file = str.Substring(0, str.IndexOf(".DWG") + 4); + if (string.IsNullOrEmpty(Path.GetFileName(file))) + return null; + + // 获取剪贴板设置的:dwg基点 + var bpstr = str.IndexOf(PointStr); + if (bpstr == -1) + return null; + + int start = bpstr + PointStr.Length + 1;//1是(括号 + int end = str.IndexOf(")", start) - start; + var ptstr = str.Substring(start, end); + var ptArr = ptstr.Split(','); + + double x = 0; + double y = 0; + double z = 0; + if (ptArr.Length > 0) + x = double.Parse(ptArr[0]); + if (ptArr.Length > 1) + y = double.Parse(ptArr[1]); + if (ptArr.Length > 2) + z = double.Parse(ptArr[2]); + + return new TagClipboardInfo + { + File = file, + Point = new(x, y, z) + }; + } + + /// + /// 创建临时路径的时间文件名 + /// + /// 格式,X是16进制 + /// + public static string CreateTempFileName(string format = "X") + { + var t1 = DateTime.Now.ToString("yyyyMMddHHmmssfffffff"); + t1 = Convert.ToInt32(t1.GetHashCode()).ToString(format); + var t2 = Convert.ToInt32(t1.GetHashCode()).ToString(format);// 这里是为了满足长度而做的 + return Path.GetTempPath() + "A$" + t1 + t2[0] + ".DWG"; + } } #if test @@ -98,7 +182,7 @@ void DocumentManager_DocumentLockModeChanged(object sender, DocumentLockModeChan // 桌子是如何做到 粘贴为块的时候读取到基点的? 而直接粘贴则是左下角 // 是通过粘贴的字符串结构(乱码部分)提供的 // 剪贴板不存在这个路径,粘贴文字之类的,由原本的命令执行 - var tag = GetCadPasteclip("AutoCAD"); + var tag = TagClipboardInfo.GetClipboard("AutoCAD"); if (tag == null) return; Env.Print("粘贴来源: " + tag.Value.File); @@ -171,49 +255,6 @@ public void IFoxPasteBlock() // 删除的文件,如果删除出错(占用),将一直在这个集合中,直到cad关闭 readonly List _delFile = new(); - /// - /// 获取剪贴板路径 - /// - /// 控制不同的cad - /// 获取是否成功 - TagClipboardInfo? GetCadPasteclip(string pasteclipStr) - { - // 获取剪贴板上面的保存路径 - var format = Clipboard.GetDataObject() - .GetFormats() - .FirstOrDefault(f => f.StartsWith(pasteclipStr)); - - if (string.IsNullOrEmpty(format)) - return null; - - var tag = new TagClipboardInfo(); - var data = (MemoryStream)Clipboard.GetData(format); - using (var reader = new StreamReader(data)) - { - var str = reader.ReadToEnd(); - str = str.Replace("\0", string.Empty); - tag.File = str.Substring(0, str.IndexOf(".DWG") + 4); - - var bpstr = str.IndexOf(TagClipboardInfo.PointStr); - if (bpstr > -1) - { - int start = bpstr + TagClipboardInfo.PointStr.Length + 1;//1是(括号 - int end = str.IndexOf(")", start) - start; - var ptstr = str.Substring(start, end); - var ptArr = ptstr.Split(','); - if (ptArr != null) - { - tag.Point = new Point3d( - double.Parse(ptArr[0]), - double.Parse(ptArr[1]), - double.Parse(ptArr[2])); - } - } - } - if (string.IsNullOrEmpty(Path.GetFileName(tag.File))) - return null; - return tag; - } /// /// 复制 @@ -228,21 +269,20 @@ public void Copy(bool getPoint, bool isEraseSsget = false) return; using var tr = new DBTrans(); - var ed = tr.Editor!; - - var objectIds = new List(); - var psr = ed.SelectImplied();// 预选 + if (tr.Editor == null) + return; + var psr = tr.Editor.SelectImplied();// 预选 if (psr.Status != PromptStatus.OK) - psr = ed.GetSelection();// 手选 + psr = tr.Editor.GetSelection();// 手选 if (psr.Status != PromptStatus.OK) return; - objectIds.AddRange(psr.Value.GetObjectIds()); - + // 设置基点 var clipboardInfo = new TagClipboardInfo(); + var idArray = psr.Value.GetObjectIds(); if (getPoint) { - var pr = ed.GetPoint("\n选择基点"); + var pr = tr.Editor.GetPoint("\n选择基点"); if (pr.Status != PromptStatus.OK) return; clipboardInfo.Point = pr.Value; @@ -254,7 +294,7 @@ public void Copy(bool getPoint, bool isEraseSsget = false) double minx = double.MaxValue; double miny = double.MaxValue; double minz = double.MaxValue; - foreach (var id in objectIds) + foreach (var id in idArray) { var ent = tr.GetObject(id); if (ent == null) @@ -269,38 +309,31 @@ public void Copy(bool getPoint, bool isEraseSsget = false) clipboardInfo.Point = new Point3d(minx, miny, minz); } - // 获取临时路径 - do + // 写入剪贴板 + clipboardInfo.File = TagClipboardInfo.CreateTempFileName(); + while (File.Exists(clipboardInfo.File)) { - clipboardInfo.File = TimeName(); + clipboardInfo.File = TagClipboardInfo.CreateTempFileName(); Thread.Sleep(1); - } while (File.Exists(clipboardInfo.File)); - - // 手动序列化 - // 写入剪贴板,这里arx有个结构体 - var sb = new StringBuilder(); - sb.Append(clipboardInfo.File); - sb.Append(TagClipboardInfo.PointStr + clipboardInfo.Point.ToString()); - - var data = new MemoryStream(); - var bytes = Encoding.Unicode.GetBytes(sb.ToString()); - data.Write(bytes, 0, bytes.Length); - Clipboard.SetData($"AutoCAD.r{Acap.Version.Major}", data); + } + clipboardInfo.SetClipboard(); // 克隆到目标块表内 using (var fileTr = new DBTrans(clipboardInfo.File)) { fileTr.Task(() => { var map = new IdMapping(); + using var ids = new ObjectIdCollection(idArray); tr.Database.WblockCloneObjects( - new ObjectIdCollection(objectIds.ToArray()), + ids, fileTr.ModelSpace.ObjectId, map, DuplicateRecordCloning.Replace, false); }); - // 大于dwg07格式,保存为07,以实现高低版本通用剪贴板 + // 大于dwg07格式的,保存为07,以实现高低版本通用剪贴板 + // 小于dwg07格式的,本工程没有支持cad06dll,所以这里仅为展示 if ((int)DwgVersion.Current >= 27) { fileTr.SaveFile((DwgVersion)27, false); @@ -308,7 +341,7 @@ public void Copy(bool getPoint, bool isEraseSsget = false) else { #pragma warning disable CS0162 // 检测到无法访问的代码 - fileTr.SaveFile(); // 低于dwg07格式的,本工程没有支持cad06dll,所以这里只是展示 + fileTr.SaveFile(); #pragma warning restore CS0162 // 检测到无法访问的代码 } } @@ -316,7 +349,7 @@ public void Copy(bool getPoint, bool isEraseSsget = false) // 剪切时候删除 if (isEraseSsget) { - objectIds.ForEach(id => { + idArray.ForEach(id => { id.Erase(); }); } @@ -383,7 +416,7 @@ public void Paste(bool isBlock) { if (!id.IsOk()) { - Env.Printl("jig预览块内有克隆失败的东西"); + Env.Printl("jig预览块内有克隆失败的东西,是否天正克隆期间导致?"); continue; } var ent = tr.GetObject(id); @@ -410,12 +443,24 @@ public void Paste(bool isBlock) else if (dr.Status == PromptStatus.OK) moveTo = moveJig.MousePointWcsLast; else + { + // 删除jig预览的块表记录 + using (btr.ForWrite()) + btr.Erase(); return; + } if (isBlock) + { PasteIsBlock(tr, moveJig.Entitys, moveJig.MousePointWcsLast, moveTo); + } else + { PasteNotBlock(tr, btr, Point3d.Origin, moveTo); + // 删除jig预览的块表记录 + using (btr.ForWrite()) + btr.Erase(); + } } /// @@ -464,36 +509,21 @@ static void PasteNotBlock(DBTrans tr, BlockTableRecord btr, Point3d move, Point3 using (ent.ForWrite()) ent.Move(move, moveTo); }); - - // 删除jig预览的块表记录 - using (btr.ForWrite()) - btr.Erase(); - } - - - /// - /// 获取时间名 - /// - /// - string TimeName() - { - var t1 = DateTime.Now.ToString("yyyyMMddHHmmssfff"); - t1 = Convert.ToInt32(t1.GetHashCode()).ToString("X"); - var t2 = Convert.ToInt32(t1.GetHashCode()).ToString("X");// 这里是为了满足长度而做的 - return Path.GetTempPath() + "A$" + t1 + t2[0] + ".DWG"; } /// /// 创建块表记录 /// /// + /// 此名称若已在块表存在,就会自动用时间名称代替 /// - BlockTableRecord? CreateBlockTableRecord(DBTrans tr, string tmpFile) + BlockTableRecord? CreateBlockTableRecord(DBTrans tr, string tempFile) { - var blockNameNew = Path.GetFileNameWithoutExtension(tmpFile); + var blockNameNew = Path.GetFileNameWithoutExtension(tempFile); while (tr.BlockTable.Has(blockNameNew)) { - blockNameNew = Path.GetFileNameWithoutExtension(TimeName()); + tempFile = TagClipboardInfo.CreateTempFileName(); + blockNameNew = Path.GetFileNameWithoutExtension(tempFile); Thread.Sleep(1); } var btrIdNew = tr.BlockTable.Add(blockNameNew); -- Gitee From 19e45daa524b622176af522b814d80276f65a43a Mon Sep 17 00:00:00 2001 From: liuqihong <540762622@qq.com> Date: Thu, 22 Sep 2022 04:22:12 +0800 Subject: [PATCH 522/675] =?UTF-8?q?=E9=80=BB=E8=BE=91=E5=87=BA=E9=94=99?= =?UTF-8?q?=E4=BA=86?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- tests/TestShared/Copyclip.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/TestShared/Copyclip.cs b/tests/TestShared/Copyclip.cs index 4014ee6..d977438 100644 --- a/tests/TestShared/Copyclip.cs +++ b/tests/TestShared/Copyclip.cs @@ -69,7 +69,7 @@ public void SetClipboard() /// 获取剪贴板 /// /// 控制不同的cad - /// 获取是否成功 + /// 转为结构输出 public static TagClipboardInfo? GetClipboard(string pasteclipStr) { // 获取剪贴板上面的保存路径 @@ -421,7 +421,7 @@ public void Paste(bool isBlock) } var ent = tr.GetObject(id); if (ent == null) - return; + continue; using (ent.ForWrite()) ent.Move(clipboardInfo.Point, Point3d.Origin); } -- Gitee From 3a393b93278335fe9eb71c0113f75bdb29f0a367 Mon Sep 17 00:00:00 2001 From: liuqihong <540762622@qq.com> Date: Fri, 23 Sep 2022 11:58:45 +0800 Subject: [PATCH 523/675] =?UTF-8?q?=E5=8A=A0=E5=85=A5arx=E5=89=AA=E8=B4=B4?= =?UTF-8?q?=E6=9D=BF=E7=BB=93=E6=9E=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../IFoxCAD.Aacad08/IFoxCAD.Acad08.csproj | 4 + .../IFoxCAD.Acad09plus.csproj | 4 + tests/TestAcad08/TestAcad08 - Backup.csproj | 31 ++ tests/TestAcad08/TestAcad08.csproj | 9 +- tests/TestAcad09plus/TestAcad09plus.csproj | 4 + tests/TestConsole/Program.cs | 4 + tests/TestConsole/TestSerialize.cs | 48 +++ tests/TestShared/Copyclip.cs | 398 ++++++++++++++---- tests/TestShared/TestJson.cs | 24 ++ 9 files changed, 450 insertions(+), 76 deletions(-) create mode 100644 tests/TestAcad08/TestAcad08 - Backup.csproj create mode 100644 tests/TestConsole/TestSerialize.cs create mode 100644 tests/TestShared/TestJson.cs diff --git a/src/IFoxCAD.Cad/IFoxCAD.Aacad08/IFoxCAD.Acad08.csproj b/src/IFoxCAD.Cad/IFoxCAD.Aacad08/IFoxCAD.Acad08.csproj index b11ec4b..1382cbc 100644 --- a/src/IFoxCAD.Cad/IFoxCAD.Aacad08/IFoxCAD.Acad08.csproj +++ b/src/IFoxCAD.Cad/IFoxCAD.Aacad08/IFoxCAD.Acad08.csproj @@ -58,4 +58,8 @@ + diff --git a/src/IFoxCAD.Cad/IFoxCAD.Aacad09plus/IFoxCAD.Acad09plus.csproj b/src/IFoxCAD.Cad/IFoxCAD.Aacad09plus/IFoxCAD.Acad09plus.csproj index 5df8558..d49ff11 100644 --- a/src/IFoxCAD.Cad/IFoxCAD.Aacad09plus/IFoxCAD.Acad09plus.csproj +++ b/src/IFoxCAD.Cad/IFoxCAD.Aacad09plus/IFoxCAD.Acad09plus.csproj @@ -65,4 +65,8 @@ + diff --git a/tests/TestAcad08/TestAcad08 - Backup.csproj b/tests/TestAcad08/TestAcad08 - Backup.csproj new file mode 100644 index 0000000..b1ac45e --- /dev/null +++ b/tests/TestAcad08/TestAcad08 - Backup.csproj @@ -0,0 +1,31 @@ + + + preview + enable + + NET35 + true + true + x86 + + + + $(Configuration);acad;ac2008;ac2009 + + + + + + + + + + + + + + + + + + diff --git a/tests/TestAcad08/TestAcad08.csproj b/tests/TestAcad08/TestAcad08.csproj index a0a397f..6e84144 100644 --- a/tests/TestAcad08/TestAcad08.csproj +++ b/tests/TestAcad08/TestAcad08.csproj @@ -18,14 +18,13 @@ - - - - - + diff --git a/tests/TestAcad09plus/TestAcad09plus.csproj b/tests/TestAcad09plus/TestAcad09plus.csproj index 0f956c6..f116e65 100644 --- a/tests/TestAcad09plus/TestAcad09plus.csproj +++ b/tests/TestAcad09plus/TestAcad09plus.csproj @@ -32,4 +32,8 @@ + diff --git a/tests/TestConsole/Program.cs b/tests/TestConsole/Program.cs index 9db5e76..708ceba 100644 --- a/tests/TestConsole/Program.cs +++ b/tests/TestConsole/Program.cs @@ -1,9 +1,13 @@ // See https://aka.ms/new-console-template for more information using System; +using System.Runtime.Serialization.Formatters.Binary; using System.Text; using TestConsole; +Console.WriteLine("***************************************************"); + + List list = new List(); list.Add(1); list.Add(2); diff --git a/tests/TestConsole/TestSerialize.cs b/tests/TestConsole/TestSerialize.cs new file mode 100644 index 0000000..413764d --- /dev/null +++ b/tests/TestConsole/TestSerialize.cs @@ -0,0 +1,48 @@ +using System.Runtime.Serialization; + +namespace TestConsole; + +[Serializable] +public struct StructDemo +{ + public char[] Chars1; + public char[] Chars2; +} + +[Serializable] +public struct StructDemo2 : ISerializable +{ + public char[] Chars1; + public char[] Chars2; + + public void GetObjectData(SerializationInfo info, StreamingContext context) + { + info.AddValue("i", Chars1); + info.AddValue("j", Chars2); + } +} + +// 二进制序列化,存在元数据 +//{ +// StructDemo structDemo = new(); +// structDemo.Chars1 = "aaa".ToCharArray(); +// structDemo.Chars2 = "bbb".ToCharArray(); + +// using MemoryStream stream = new(); +// BinaryFormatter formatter = new(); +// formatter.Serialize(stream, structDemo); +// var str = Encoding.ASCII.GetString(stream.ToArray()); +// Console.WriteLine(str); +//} + +//{ +// StructDemo2 structDemo = new(); +// structDemo.Chars1 = "aaa".ToCharArray(); +// structDemo.Chars2 = "bbb".ToCharArray(); + +// using MemoryStream stream = new(); +// BinaryFormatter formatter = new(); +// formatter.Serialize(stream, structDemo); +// var str = Encoding.ASCII.GetString(stream.ToArray()); +// Console.WriteLine(str); +//} \ No newline at end of file diff --git a/tests/TestShared/Copyclip.cs b/tests/TestShared/Copyclip.cs index d977438..3093413 100644 --- a/tests/TestShared/Copyclip.cs +++ b/tests/TestShared/Copyclip.cs @@ -1,11 +1,12 @@ #define test namespace Test; + +using Autodesk.AutoCAD.DatabaseServices; using IFoxCAD.Cad; using System; -using System.Linq; +using System.Text; using System.Threading; -using Clipboard = System.Windows.Forms.Clipboard; /* * 0x01 (已完成) @@ -13,8 +14,8 @@ namespace Test; * 就可以多个版本cad相互复制粘贴了 * * 0x02 - * 设置一个粘贴板栈,用tmp.config储存(路径和粘贴基点),ctrl+shfit+v v v 就是三次前的剪贴板内容 - * 这样就更好地跨cad复制了 + * 设置一个粘贴板栈,用tmp.config储存(路径和粘贴基点), + * ctrl+shfit+v v v 就是三次前的剪贴板内容;也可以制作一个剪贴板窗口更好给用户交互 * * 0x03 * 天正图元的复制粘贴出错原因 @@ -24,43 +25,221 @@ namespace Test; */ public class InterceptCopyclip { + enum eClipInfoFlags + { + kbDragGeometry = 0x01, + }; + + enum eXrefType + { + kXrefTypeAttach = 1, + kXrefTypeOverlay = 2 + }; + + enum eExpandedClipDataTypes + { + kDcPlotStyles = 1, + kDcXrefs = 2, + kDcLayouts = 3, + kDcBlocks = 4, + kDcLayers = 5, + kDcDrawings = 6, + kDcLinetypes = 7, + kDcTextStyles = 8, + kDcDimStyles = 9, + kDcBlocksWithAttdef = 10, + //#ifdef ADCHATCH + kDcHatches = 11, + //#endif + kTpXrefs = 12, + kTpImages = 13, + kTpTable = 14, + kDcTableStyles = 15, + kDcMultileaderStyles = 16, + kDcVisualStyles = 17, + kDcSectionViewStyles = 18, + kDcDetailViewStyles = 19, + }; + + [Serializable] + [StructLayout(LayoutKind.Sequential)] + public struct Rect + { + public int Left { get; } + public int Top { get; } + public int Right { get; } + public int Bottom { get; } + public Rect(int left, int top, int right, int bottom) + { + Left = left; + Top = top; + Right = right; + Bottom = bottom; + } + } + + [Serializable] + [StructLayout(LayoutKind.Sequential, Pack = 8)] + public struct Point3D + { + public double X { get; } + public double Y { get; } + public double Z { get; } + public Point3D(double x, double y, double z) + { + X = x; + Y = y; + Z = z; + } + public static implicit operator Point3D(Point3d pt) + { + return new Point3D(pt.X, pt.Y, pt.Z); + } + public static implicit operator Point3d(Point3D pt) + { + return new Point3d(pt.X, pt.Y, pt.Z); + } + } /// - /// 剪贴板结构
    - /// 此处没有按照ARX的tagClipboardInfo结构复刻(别问,问就是不会) + /// ARX剪贴板结构 ///
    + [Serializable] + [StructLayout(LayoutKind.Sequential, Pack = 8, CharSet = CharSet.Unicode)] struct TagClipboardInfo { - /// - /// dwg储存路径
    - /// 有了这个的话,还需要读取剪贴板吗?
    - /// 需要的,当前剪贴板中,有可能不是dwg路径,而是文字内容等
    - ///
    - public string File; - /// - /// 粘贴基点 - /// - public Point3d Point; - /// - /// 粘贴基点字符串 - /// - const string PointStr = "BasePoint:"; + #region 字段,对应arx结构的,不要改动,本结构也不允许再加字段 + [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 260)] + public string szTempFile; // 临时文件夹的dwg文件 + [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 260)] + public string szSourceFile; // 文件名从中做出选择..是不是指定块表记录? + [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 4)] + public string szSignature; + public int nFlags; // kbDragGeometry: 从AutoCAD拖动几何图形 + public Point3D dptInsert; // 插入点的原始世界坐标' + public Rect rectGDI; // GDI coord 选择集的边界矩形 + public IntPtr mpView; // void* 用于验证这个对象是在这个视图中创建的 (HWND*) + public int dwThreadId; // AutoCAD thread 创建数据对象 + public int nLen; // 下一段的长度的数据,如果有的话,从chData + public int nType; // 类型的数据,如果有(eExpandedClipDataTypes) + [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 1)] + public string chData; // 数据的开始,如果有 + #endregion + + public string File => szTempFile; + public Point3d Point => dptInsert; + + public TagClipboardInfo(string tmpPath, Point3d insert) + { + szTempFile = tmpPath; + dptInsert = insert; + szSignature = "R15"; + nFlags = 0x01; + + szSourceFile = string.Empty; + chData = string.Empty; + } public override string ToString() { var sb = new StringBuilder(); - sb.Append(File); - sb.Append(PointStr); - sb.Append(Point); + sb.AppendLine($"szTempFile:{szTempFile}"); + sb.AppendLine($"szSourceFile:{szSourceFile}"); + sb.AppendLine($"szSignature:{szSignature}"); + sb.AppendLine($"nFlags:{nFlags}"); + sb.AppendLine($"dptInsert:{dptInsert}"); + sb.AppendLine($"rectGDI:{rectGDI}"); + sb.AppendLine($"mpView:{mpView}"); + sb.AppendLine($"dwThreadId:{dwThreadId}"); + sb.AppendLine($"nLen:{nLen}"); + sb.AppendLine($"nType:{nType}"); + sb.AppendLine($"chData:{chData}"); return sb.ToString(); } + static uint GetRegisterClipboardFormat(string cadStr) + { + // cf = NativeMethods.RegisterClipboardFormat($"{cadStr}.r{Acap.Version.Major}"); + // r17写死,代表每个版本都去找它,否则将作为剪贴板版本隔离 + uint cf = NativeMethods.RegisterClipboardFormat($"{cadStr}.r17"); + return cf; + } + + /// + /// 获取剪贴板 + /// + /// 控制不同的cad:"AutoCAD" + public static TagClipboardInfo? GetClipboard(string cadStr = "AutoCAD") + { + TagClipboardInfo? tag = null; + IntPtr data = IntPtr.Zero; + try + { + uint cf = GetRegisterClipboardFormat(cadStr); + if (!NativeMethods.OpenClipboard(IntPtr.Zero)) + return null; + // 剪贴板的数据拷贝进去结构体中,会依照数据长度进行拷贝 + data = NativeMethods.GetClipboardData(cf); + if (data == IntPtr.Zero) + return null; + IntPtr ptr = NativeMethods.GlobalLock(data); + if (ptr != IntPtr.Zero) + { + // 非托管内存块->托管对象 + tag = (TagClipboardInfo)Marshal.PtrToStructure(ptr, typeof(TagClipboardInfo)); + } + } + finally + { + if (data != IntPtr.Zero) + NativeMethods.GlobalUnlock(data); + NativeMethods.CloseClipboard(); + } + return tag; + } + + /// + /// 设置剪贴板 + /// + /// + /// true成功拷贝;false可能重复粘贴对象导致 + public bool SetClipboard(string cadStr = "AutoCAD") + { + uint cf = GetRegisterClipboardFormat(cadStr); + int size = Marshal.SizeOf(typeof(TagClipboardInfo)); + IntPtr ptr = IntPtr.Zero; + const uint GMEM_MOVEABLE = 0x0002; + IntPtr hglobal = NativeMethods.GlobalAlloc(GMEM_MOVEABLE, size); + try + { + if (NativeMethods.OpenClipboard(IntPtr.Zero)) + { + ptr = NativeMethods.GlobalLock(hglobal); + if (ptr != IntPtr.Zero)// 重复复制同一个图元时 + { + NativeMethods.EmptyClipboard(); + Marshal.StructureToPtr(this, ptr, true); + NativeMethods.GlobalUnlock(hglobal); + NativeMethods.SetClipboardData(cf, hglobal); + NativeMethods.CloseClipboard(); + } + } + } + finally + { + NativeMethods.GlobalFree(hglobal); + } + return ptr != IntPtr.Zero; + } + +#if true2 + // 这种写入方式是失败的 /// /// 写入剪贴板 /// public void SetClipboard() { using var data = new MemoryStream(); - var bytes = Encoding.Unicode.GetBytes(ToString()); + var bytes = Encoding.Unicode.GetBytes(ArxTagClipboardInfo.ToString()); data.Write(bytes, 0, bytes.Length); Clipboard.SetData($"AutoCAD.r{Acap.Version.Major}", data); } @@ -68,10 +247,13 @@ public void SetClipboard() /// /// 获取剪贴板 /// - /// 控制不同的cad + /// 控制不同的cad:"AutoCAD" /// 转为结构输出 - public static TagClipboardInfo? GetClipboard(string pasteclipStr) + public static TagClipboardInfo? GetClipboard(string pasteclipStr = "AutoCAD") { + /// 粘贴基点字符串 + const string PointStr = "BasePoint:"; + // 获取剪贴板上面的保存路径 var format = Clipboard.GetDataObject() .GetFormats() @@ -109,27 +291,95 @@ public void SetClipboard() if (ptArr.Length > 2) z = double.Parse(ptArr[2]); - return new TagClipboardInfo - { - File = file, - Point = new(x, y, z) - }; + return new(file, new(x, y, z)); } - +#endif +#if true2 + void GetSize() + { + var v_1 = Marshal.OffsetOf(typeof(TagClipboardInfo), nameof(TagClipboardInfo.szTempFile)).ToInt32(); + var v_2 = Marshal.OffsetOf(typeof(TagClipboardInfo), nameof(TagClipboardInfo.szSourceFile)).ToInt32(); + var v_3 = Marshal.OffsetOf(typeof(TagClipboardInfo), nameof(TagClipboardInfo.szSignature)).ToInt32(); + var v_4 = Marshal.OffsetOf(typeof(TagClipboardInfo), nameof(TagClipboardInfo.nFlags)).ToInt32(); + var v_5 = Marshal.OffsetOf(typeof(TagClipboardInfo), nameof(TagClipboardInfo.dptInsert)).ToInt32(); + var v_6 = Marshal.OffsetOf(typeof(TagClipboardInfo), nameof(TagClipboardInfo.rectGDI)).ToInt32(); + var v_7 = Marshal.OffsetOf(typeof(TagClipboardInfo), nameof(TagClipboardInfo.mpView)).ToInt32(); + var v_8 = Marshal.OffsetOf(typeof(TagClipboardInfo), nameof(TagClipboardInfo.dwThreadId)).ToInt32(); + var v_9 = Marshal.OffsetOf(typeof(TagClipboardInfo), nameof(TagClipboardInfo.nLen)).ToInt32(); + var v_10 = Marshal.OffsetOf(typeof(TagClipboardInfo), nameof(TagClipboardInfo.nType)).ToInt32(); + var v_11 = Marshal.OffsetOf(typeof(TagClipboardInfo), nameof(TagClipboardInfo.chData)).ToInt32(); + var v_12 = Marshal.SizeOf(typeof(TagClipboardInfo)); //1120 + + var v_a = Marshal.SizeOf(typeof(Point3D));//24 + var v_b = Marshal.SizeOf(typeof(Rect));//16 + } +#endif + } + class NativeMethods + { /// - /// 创建临时路径的时间文件名 + /// 锁定内存 /// - /// 格式,X是16进制 + /// /// - public static string CreateTempFileName(string format = "X") - { - var t1 = DateTime.Now.ToString("yyyyMMddHHmmssfffffff"); - t1 = Convert.ToInt32(t1.GetHashCode()).ToString(format); - var t2 = Convert.ToInt32(t1.GetHashCode()).ToString(format);// 这里是为了满足长度而做的 - return Path.GetTempPath() + "A$" + t1 + t2[0] + ".DWG"; - } + [DllImport("kernel32.dll")] + public static extern IntPtr GlobalLock(IntPtr hMem); + /// + /// 解锁内存 + /// + /// + /// + [DllImport("kernel32.dll")] + public static extern bool GlobalUnlock(IntPtr hMem); + /// + /// 开启剪贴板 + /// + /// + /// + [DllImport("user32.dll")] + public static extern bool OpenClipboard(IntPtr hWndNewOwner); + /// + /// 关闭剪贴板 + /// + /// + [DllImport("user32.dll")] + public static extern bool CloseClipboard(); + /// + /// 根据数据格式获取剪贴板 + /// + /// 数据格式名称 + /// + [DllImport("user32.dll")] + public static extern uint RegisterClipboardFormat(string lpszFormat); + /// + /// 获取剪贴板 + /// + /// + /// + [DllImport("user32.dll")] + public static extern IntPtr GetClipboardData(uint uFormat); + /// + /// 设置剪贴板 + /// + /// + /// + /// + [DllImport("user32.dll")] + public static extern IntPtr SetClipboardData(uint uFormat, IntPtr hMem); + + [DllImport("user32.dll")] + public static extern bool EmptyClipboard(); + [DllImport("kernel32.dll")] + public static extern IntPtr GlobalAlloc(uint uFlags, int dwBytes); + [DllImport("kernel32.dll")] + public static extern IntPtr GlobalFree(IntPtr hMem); } + + + + + #if test [IFoxInitialize] public void Init() @@ -176,19 +426,6 @@ public void Terminate() void DocumentManager_DocumentLockModeChanged(object sender, DocumentLockModeChangedEventArgs e) { var up = e.GlobalCommandName.ToUpper(); - - if (up == "PASTEBLOCK" || up == "PASTECLIP") - { - // 桌子是如何做到 粘贴为块的时候读取到基点的? 而直接粘贴则是左下角 - // 是通过粘贴的字符串结构(乱码部分)提供的 - // 剪贴板不存在这个路径,粘贴文字之类的,由原本的命令执行 - var tag = TagClipboardInfo.GetClipboard("AutoCAD"); - if (tag == null) - return; - Env.Print("粘贴来源: " + tag.Value.File); - _clipboardInfo = tag; - } - if (up == "COPYCLIP")// 复制 { e.Veto(); @@ -250,20 +487,18 @@ public void IFoxPasteBlock() } - - TagClipboardInfo? _clipboardInfo; - // 删除的文件,如果删除出错(占用),将一直在这个集合中,直到cad关闭 + /// + /// 储存准备删除的文件 + /// 如果删除出错(占用),将一直在这个集合中,直到cad关闭 + /// readonly List _delFile = new(); - /// /// 复制 /// /// public void Copy(bool getPoint, bool isEraseSsget = false) { - Terminate(); - var dm = Acap.DocumentManager; if (dm.Count == 0) return; @@ -278,14 +513,14 @@ public void Copy(bool getPoint, bool isEraseSsget = false) return; // 设置基点 - var clipboardInfo = new TagClipboardInfo(); + Point3d pt = Point3d.Origin; var idArray = psr.Value.GetObjectIds(); if (getPoint) { var pr = tr.Editor.GetPoint("\n选择基点"); if (pr.Status != PromptStatus.OK) return; - clipboardInfo.Point = pr.Value; + pt = pr.Value; } else { @@ -306,17 +541,21 @@ public void Copy(bool getPoint, bool isEraseSsget = false) miny = miny > info.MinY ? info.MinY : miny; minz = minz > info.MinZ ? info.MinZ : minz; } - clipboardInfo.Point = new Point3d(minx, miny, minz); + pt = new(minx, miny, minz); } - // 写入剪贴板 - clipboardInfo.File = TagClipboardInfo.CreateTempFileName(); - while (File.Exists(clipboardInfo.File)) + var tempFile = CreateTempFileName(); + while (File.Exists(tempFile)) { - clipboardInfo.File = TagClipboardInfo.CreateTempFileName(); + tempFile = CreateTempFileName(); Thread.Sleep(1); } - clipboardInfo.SetClipboard(); + + // 写入剪贴板 + // 如果成功拷贝,删除上一次的临时文件 + var clipboardInfo = new TagClipboardInfo(tempFile, pt); + if (clipboardInfo.SetClipboard()) + Terminate(); // 克隆到目标块表内 using (var fileTr = new DBTrans(clipboardInfo.File)) @@ -368,9 +607,11 @@ public void Paste(bool isBlock) if (dm.Count == 0) return; - if (_clipboardInfo == null) + var tag = TagClipboardInfo.GetClipboard(); + if (tag == null) return; - var clipboardInfo = _clipboardInfo.Value; + var clipboardInfo = tag.Value; + Env.Print("粘贴来源: " + clipboardInfo.File); // 获取临时文件的图元id var fileEntityIds = new List(); @@ -380,6 +621,8 @@ public void Paste(bool isBlock) if (id.IsOk()) fileEntityIds.Add(id); } + if (fileEntityIds.Count == 0) + return; using var tr = new DBTrans(); #if test @@ -522,13 +765,26 @@ static void PasteNotBlock(DBTrans tr, BlockTableRecord btr, Point3d move, Point3 var blockNameNew = Path.GetFileNameWithoutExtension(tempFile); while (tr.BlockTable.Has(blockNameNew)) { - tempFile = TagClipboardInfo.CreateTempFileName(); + tempFile = CreateTempFileName(); blockNameNew = Path.GetFileNameWithoutExtension(tempFile); Thread.Sleep(1); } var btrIdNew = tr.BlockTable.Add(blockNameNew); return tr.GetObject(btrIdNew); } + + /// + /// 创建临时路径的时间文件名 + /// + /// 格式,X是16进制 + /// + public static string CreateTempFileName(string format = "X") + { + var t1 = DateTime.Now.ToString("yyyyMMddHHmmssfffffff"); + t1 = Convert.ToInt32(t1.GetHashCode()).ToString(format); + var t2 = Convert.ToInt32(t1.GetHashCode()).ToString(format);// 这里是为了满足长度而做的 + return Path.GetTempPath() + "A$" + t1 + t2[0] + ".DWG"; + } } //public static class BlockReferenceHelper diff --git a/tests/TestShared/TestJson.cs b/tests/TestShared/TestJson.cs new file mode 100644 index 0000000..f6bfaf6 --- /dev/null +++ b/tests/TestShared/TestJson.cs @@ -0,0 +1,24 @@ +namespace TestShared; + +#if true2 +public class TestJson +{ + /* + + + + */ + protected void Page_Load(object sender, EventArgs e) + { + var RegisteredUsers = new List(); + RegisteredUsers.Add(0); + RegisteredUsers.Add(1); + RegisteredUsers.Add(2); + RegisteredUsers.Add(3); + + var serializer = new System.Web.Script.Serialization.JavaScriptSerializer(); + var serializedResult = serializer.Serialize(RegisteredUsers); + var deserializedResult = serializer.Deserialize>(serializedResult); + } +} +#endif \ No newline at end of file -- Gitee From 7bbe398709f5e31964287ef1d3b8c9dbb1c902ac Mon Sep 17 00:00:00 2001 From: liuqihong <540762622@qq.com> Date: Sat, 24 Sep 2022 05:32:59 +0800 Subject: [PATCH 524/675] =?UTF-8?q?=E6=8B=86=E5=88=86=E5=89=AA=E8=B4=B4?= =?UTF-8?q?=E6=9D=BF=E6=96=87=E4=BB=B6=E5=88=B0=E5=86=85=E5=BA=93?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../IFoxCAD.Cad.Shared.projitems | 1 + .../Runtime/TagClipboardInfo.cs | 457 +++++++++++++++++ tests/TestShared/Copyclip.cs | 460 +++--------------- tests/TestShared/TestShared.projitems | 1 + 4 files changed, 519 insertions(+), 400 deletions(-) create mode 100644 src/IFoxCAD.Cad.Shared/Runtime/TagClipboardInfo.cs diff --git a/src/IFoxCAD.Cad.Shared/IFoxCAD.Cad.Shared.projitems b/src/IFoxCAD.Cad.Shared/IFoxCAD.Cad.Shared.projitems index 568ba2d..74ab2a5 100644 --- a/src/IFoxCAD.Cad.Shared/IFoxCAD.Cad.Shared.projitems +++ b/src/IFoxCAD.Cad.Shared/IFoxCAD.Cad.Shared.projitems @@ -59,6 +59,7 @@ + diff --git a/src/IFoxCAD.Cad.Shared/Runtime/TagClipboardInfo.cs b/src/IFoxCAD.Cad.Shared/Runtime/TagClipboardInfo.cs new file mode 100644 index 0000000..a27cd95 --- /dev/null +++ b/src/IFoxCAD.Cad.Shared/Runtime/TagClipboardInfo.cs @@ -0,0 +1,457 @@ +using System; +using System.Collections.Generic; +using System.Text; +using System.Threading; + +namespace IFoxCAD.Cad; + +#if true2 +enum eClipInfoFlags +{ + kbDragGeometry = 0x01, +}; + +enum eXrefType +{ + kXrefTypeAttach = 1, + kXrefTypeOverlay = 2 +}; + +enum eExpandedClipDataTypes +{ + kDcPlotStyles = 1, + kDcXrefs = 2, + kDcLayouts = 3, + kDcBlocks = 4, + kDcLayers = 5, + kDcDrawings = 6, + kDcLinetypes = 7, + kDcTextStyles = 8, + kDcDimStyles = 9, + kDcBlocksWithAttdef = 10, + //#ifdef ADCHATCH + kDcHatches = 11, + //#endif + kTpXrefs = 12, + kTpImages = 13, + kTpTable = 14, + kDcTableStyles = 15, + kDcMultileaderStyles = 16, + kDcVisualStyles = 17, + kDcSectionViewStyles = 18, + kDcDetailViewStyles = 19, +}; +#endif + +public class ClipboardEnv +{ + // 此句将导致剪贴板的key隔离,从而导致cad版本隔离 + // public static string CadVer = $"AutoCAD.r{Acap.Version.Major}" + // 将r17写死,代表每个cad版本都去找它,实现不隔离cad版本 + public static string CadVer = "AutoCAD.r17"; +} + +[Serializable] +[StructLayout(LayoutKind.Sequential)] +public struct IntRect +{ + public int Left { get; } + public int Top { get; } + public int Right { get; } + public int Bottom { get; } + public IntRect(int left, int top, int right, int bottom) + { + Left = left; + Top = top; + Right = right; + Bottom = bottom; + } + + public static IntRect Zero => new(0, 0, 0, 0); + + public override string ToString() + { + return $"Left:{Left},Top:{Top},Right:{Right},Bottom:{Bottom}"; + } +} + +[Serializable] +[StructLayout(LayoutKind.Sequential, Pack = 8)] +public struct Point3D +{ + public double X { get; } + public double Y { get; } + public double Z { get; } + public Point3D(double x, double y, double z) + { + X = x; + Y = y; + Z = z; + } + public static implicit operator Point3D(Point3d pt) + { + return new Point3D(pt.X, pt.Y, pt.Z); + } + public static implicit operator Point3d(Point3D pt) + { + return new Point3d(pt.X, pt.Y, pt.Z); + } + + public override string ToString() + { + return $"X:{X},Y:{Y},Z:{Z}"; + } +} +/// +/// ARX剪贴板结构 +/// +[Serializable] +[StructLayout(LayoutKind.Sequential, Pack = 8, CharSet = CharSet.Unicode/*此参数将导致260*2*/)] +public struct TagClipboardInfo +{ + #region 字段,对应arx结构的,不要改动,本结构也不允许再加字段 + [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 260)] + public string szTempFile; // 临时文件夹的dwg文件 + [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 260)] + public string szSourceFile; // 文件名从中做出选择..是不是指定块表记录? + [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 4)] + public string szSignature; + public int nFlags; // kbDragGeometry: 从AutoCAD拖动几何图形 + public Point3D dptInsert; // 插入点的原始世界坐标' + public IntRect rectGDI; // GDI coord 选择集的边界矩形 + public IntPtr mpView; // void* 用于验证这个对象是在这个视图中创建的 (HWND*) + public int dwThreadId; // AutoCAD thread 创建数据对象 + public int nLen; // 下一段的长度的数据,如果有的话,从chData + public int nType; // 类型的数据,如果有(eExpandedClipDataTypes) + [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 1)] + public string chData; // 数据的开始,如果有 + #endregion + + public string File => szTempFile; + public Point3d Point => dptInsert; + + /// + /// cad剪贴板 + /// + /// 临时dwg的保存路径 + /// 粘贴点 + public TagClipboardInfo(string tmpFile, Point3d insert) + { + szTempFile = tmpFile; + szSourceFile = string.Empty; + szSignature = "R15"; //恒定是这个 + nFlags = 0; + dptInsert = insert; + rectGDI = IntRect.Zero; + nLen = 0; + nType = 0; + chData = string.Empty; + + mpView = AcedGetAcadDwgview(); + dwThreadId = (int)NativeMethods.GetWindowThreadProcessId( + Acap.MainWindow.Handle, out uint processId); + } + + /// + /// 获取视口指针 + /// +#if NET35 + [DllImport("acad.exe", EntryPoint = "?acedGetAcadDwgView@@YAPAVCView@@XZ")] //acad08 +#else + [DllImport("acad.exe", EntryPoint = "?acedGetAcadDwgView@@YAPEAVCView@@XZ")]//acad21 +#endif + static extern IntPtr AcedGetAcadDwgview(); + + + public override string ToString() + { + var sb = new StringBuilder(); + sb.AppendLine($"szTempFile:{szTempFile}"); + sb.AppendLine($"szSourceFile:{szSourceFile}"); + sb.AppendLine($"szSignature:{szSignature}"); + sb.AppendLine($"nFlags:{nFlags}"); + sb.AppendLine($"dptInsert:{dptInsert}"); + sb.AppendLine($"rectGDI:{rectGDI}"); + sb.AppendLine($"mpView:{mpView}"); + sb.AppendLine($"dwThreadId:{dwThreadId}"); + sb.AppendLine($"nLen:{nLen}"); + sb.AppendLine($"nType:{nType}"); + sb.AppendLine($"chData:{chData}"); + return sb.ToString(); + } + + + + /// + /// 获取剪贴板 + /// + /// 控制不同的cad + public static TagClipboardInfo? GetClipboard() + { + TagClipboardInfo? tag = null; + bool openFlag = false; + try + { + openFlag = NativeMethods.OpenClipboard(IntPtr.Zero); + if (!openFlag) + return null; + + // 读取剪贴板的指定 key 的数据 + var key = NativeMethods.RegisterClipboardFormat(ClipboardEnv.CadVer); + var data = NativeMethods.GetClipboardData(key); + + // 剪贴板的数据拷贝进去结构体中,会依照数据长度进行拷贝 + bool lockFlag = NativeMethods.GlobalLockTask(data, ptr => { + // 非托管内存块->托管对象 + tag = (TagClipboardInfo)Marshal.PtrToStructure(ptr, typeof(TagClipboardInfo)); + }); + if (!lockFlag) + return null; + } + finally + { + if (openFlag) + NativeMethods.CloseClipboard(); + } + return tag; + } + + + /// + /// 设置剪贴板 + /// + /// true成功拷贝;false可能重复粘贴对象导致 + public static bool SetClipboard(TagClipboardInfo tag) + { + IntPtr data = IntPtr.Zero; + bool openFlag = false; + try + { + openFlag = NativeMethods.OpenClipboard(IntPtr.Zero); + if (!openFlag) + return false; + const uint GMEM_MOVEABLE = 0x0002; + int size = Marshal.SizeOf(typeof(TagClipboardInfo)); + data = NativeMethods.GlobalAlloc(GMEM_MOVEABLE, size); + + bool lockFlag = NativeMethods.GlobalLockTask(data, ptr => { + NativeMethods.EmptyClipboard(); + // 托管对象->非托管内存块 + Marshal.StructureToPtr(tag, ptr, true); + }); + if (!lockFlag) + return false; + + var key = NativeMethods.RegisterClipboardFormat(ClipboardEnv.CadVer); + NativeMethods.SetClipboardData(key, data); + } + finally + { + if (data != IntPtr.Zero) + NativeMethods.GlobalFree(data); + if (openFlag) + NativeMethods.CloseClipboard(); + } + return true; + } + +#if true2 + // 这种写入方式是失败的 + /// + /// 写入剪贴板 + /// + public void SetClipboard() + { + using var data = new MemoryStream(); + var bytes = Encoding.Unicode.GetBytes(ArxTagClipboardInfo.ToString()); + data.Write(bytes, 0, bytes.Length); + Clipboard.SetData($"AutoCAD.r{Acap.Version.Major}", data); + } + + /// + /// 获取剪贴板 + /// + /// 控制不同的cad:"AutoCAD" + /// 转为结构输出 + public static TagClipboardInfo? GetClipboard(string pasteclipStr = "AutoCAD") + { + /// 粘贴基点字符串 + const string PointStr = "BasePoint:"; + + // 获取剪贴板上面的保存路径 + var format = Clipboard.GetDataObject() + .GetFormats() + .FirstOrDefault(f => f.StartsWith(pasteclipStr)); + if (string.IsNullOrEmpty(format)) + return null; + + using var data = (MemoryStream)Clipboard.GetData(format); + using var reader = new StreamReader(data); + + // 获取剪贴板设置的:临时文件夹中的dwg文件路径 + var str = reader.ReadToEnd(); + str = str.Replace("\0", string.Empty); + var file = str.Substring(0, str.IndexOf(".DWG") + 4); + if (string.IsNullOrEmpty(Path.GetFileName(file))) + return null; + + // 获取剪贴板设置的:dwg基点 + var bpstr = str.IndexOf(PointStr); + if (bpstr == -1) + return null; + + int start = bpstr + PointStr.Length + 1;//1是(括号 + int end = str.IndexOf(")", start) - start; + var ptstr = str.Substring(start, end); + var ptArr = ptstr.Split(','); + + double x = 0; + double y = 0; + double z = 0; + if (ptArr.Length > 0) + x = double.Parse(ptArr[0]); + if (ptArr.Length > 1) + y = double.Parse(ptArr[1]); + if (ptArr.Length > 2) + z = double.Parse(ptArr[2]); + + return new(file, new(x, y, z)); + } +#endif + void GetSize() + { + var v_1 = Marshal.OffsetOf(typeof(TagClipboardInfo), nameof(TagClipboardInfo.szTempFile)).ToInt32(); + var v_2 = Marshal.OffsetOf(typeof(TagClipboardInfo), nameof(TagClipboardInfo.szSourceFile)).ToInt32(); + var v_3 = Marshal.OffsetOf(typeof(TagClipboardInfo), nameof(TagClipboardInfo.szSignature)).ToInt32(); + var v_4 = Marshal.OffsetOf(typeof(TagClipboardInfo), nameof(TagClipboardInfo.nFlags)).ToInt32(); + var v_5 = Marshal.OffsetOf(typeof(TagClipboardInfo), nameof(TagClipboardInfo.dptInsert)).ToInt32(); + var v_6 = Marshal.OffsetOf(typeof(TagClipboardInfo), nameof(TagClipboardInfo.rectGDI)).ToInt32(); + var v_7 = Marshal.OffsetOf(typeof(TagClipboardInfo), nameof(TagClipboardInfo.mpView)).ToInt32(); + var v_8 = Marshal.OffsetOf(typeof(TagClipboardInfo), nameof(TagClipboardInfo.dwThreadId)).ToInt32(); + var v_9 = Marshal.OffsetOf(typeof(TagClipboardInfo), nameof(TagClipboardInfo.nLen)).ToInt32(); + var v_10 = Marshal.OffsetOf(typeof(TagClipboardInfo), nameof(TagClipboardInfo.nType)).ToInt32(); + var v_11 = Marshal.OffsetOf(typeof(TagClipboardInfo), nameof(TagClipboardInfo.chData)).ToInt32(); + var v_12 = Marshal.SizeOf(typeof(TagClipboardInfo)); //1120 + + var v_a = Marshal.SizeOf(typeof(Point3D));//24 + var v_b = Marshal.SizeOf(typeof(IntRect));//16 + } +} +class NativeMethods +{ + /// + /// 锁定内存 + /// + /// + /// + [DllImport("kernel32.dll")] + static extern IntPtr GlobalLock(IntPtr hMem); + /// + /// 解锁内存 + /// + /// + /// + [DllImport("kernel32.dll")] + static extern bool GlobalUnlock(IntPtr hMem); + /// + /// 开启剪贴板 + /// + /// + /// + [DllImport("user32.dll")] + public static extern bool OpenClipboard(IntPtr hWndNewOwner); + /// + /// 关闭剪贴板 + /// + /// + [DllImport("user32.dll")] + public static extern bool CloseClipboard(); + /// + /// 根据数据格式获取剪贴板 + /// + /// 数据格式名称 + /// + [DllImport("user32.dll")] + public static extern uint RegisterClipboardFormat(string lpszFormat); + /// + /// 获取剪贴板 + /// + /// + /// + [DllImport("user32.dll")] + public static extern IntPtr GetClipboardData(uint uFormat); + /// + /// 设置剪贴板 + /// + /// + /// + /// + [DllImport("user32.dll")] + public static extern IntPtr SetClipboardData(uint uFormat, IntPtr hMem); + /// + /// 清空剪切板并释放剪切板内数据的句柄 + /// + /// + [DllImport("user32.dll")] + public static extern bool EmptyClipboard(); + /// + /// 从堆中分配一定数目的字节数 + /// + /// 分配方式 + /// 分配的字节数 + /// + [DllImport("kernel32.dll")] + public static extern IntPtr GlobalAlloc(uint uFlags, int dwBytes); + /// + /// 释放堆内存 + /// + /// 产生的句柄 + /// + [DllImport("kernel32.dll")] + public static extern IntPtr GlobalFree(IntPtr hMem); + + /// + /// 查找主线程
    + /// 代替
    + /// 托管线程和他们不一样: + ///
    + /// 主窗口 + /// 进程ID + /// 线程ID + [DllImport("user32.dll", SetLastError = true)] + public static extern uint GetWindowThreadProcessId(IntPtr hWnd, out uint lpdwProcessId); + + /// + /// 锁定和释放内存 + /// + /// 锁定数据对象指针 + /// 返回锁定的内存片段指针,锁定期间执行任务 + /// 是否锁定成功 + /// + public static bool GlobalLockTask(IntPtr data, Action task) + { + if (task == null) + throw new ArgumentNullException(nameof(task)); + + if (data == IntPtr.Zero) + return false; + + try + { + var ptr = GlobalLock(data); + + // 有几率导致无效锁定: + // 重复复制同一个图元时,第二次是 IntPtr.Zero, + // 第三次就又可以复制了 + if (ptr == IntPtr.Zero) + return false; + + task.Invoke(ptr); + } + finally + { + GlobalUnlock(data); + } + return true; + } +} \ No newline at end of file diff --git a/tests/TestShared/Copyclip.cs b/tests/TestShared/Copyclip.cs index 3093413..f2704df 100644 --- a/tests/TestShared/Copyclip.cs +++ b/tests/TestShared/Copyclip.cs @@ -1,11 +1,11 @@ #define test +#define PASTECLIP +#define COPYCLIP namespace Test; -using Autodesk.AutoCAD.DatabaseServices; using IFoxCAD.Cad; using System; -using System.Text; using System.Threading; /* @@ -23,399 +23,15 @@ namespace Test; * 引用技术贴: * https://forums.autodesk.com/t5/net/paste-list-of-objects-from-clipboard-on-dwg-file-using-c-net/td-p/6797606 */ -public class InterceptCopyclip +public class Copyclip { - enum eClipInfoFlags - { - kbDragGeometry = 0x01, - }; - - enum eXrefType - { - kXrefTypeAttach = 1, - kXrefTypeOverlay = 2 - }; - - enum eExpandedClipDataTypes - { - kDcPlotStyles = 1, - kDcXrefs = 2, - kDcLayouts = 3, - kDcBlocks = 4, - kDcLayers = 5, - kDcDrawings = 6, - kDcLinetypes = 7, - kDcTextStyles = 8, - kDcDimStyles = 9, - kDcBlocksWithAttdef = 10, - //#ifdef ADCHATCH - kDcHatches = 11, - //#endif - kTpXrefs = 12, - kTpImages = 13, - kTpTable = 14, - kDcTableStyles = 15, - kDcMultileaderStyles = 16, - kDcVisualStyles = 17, - kDcSectionViewStyles = 18, - kDcDetailViewStyles = 19, - }; - - [Serializable] - [StructLayout(LayoutKind.Sequential)] - public struct Rect - { - public int Left { get; } - public int Top { get; } - public int Right { get; } - public int Bottom { get; } - public Rect(int left, int top, int right, int bottom) - { - Left = left; - Top = top; - Right = right; - Bottom = bottom; - } - } - - [Serializable] - [StructLayout(LayoutKind.Sequential, Pack = 8)] - public struct Point3D - { - public double X { get; } - public double Y { get; } - public double Z { get; } - public Point3D(double x, double y, double z) - { - X = x; - Y = y; - Z = z; - } - public static implicit operator Point3D(Point3d pt) - { - return new Point3D(pt.X, pt.Y, pt.Z); - } - public static implicit operator Point3d(Point3D pt) - { - return new Point3d(pt.X, pt.Y, pt.Z); - } - } - /// - /// ARX剪贴板结构 - /// - [Serializable] - [StructLayout(LayoutKind.Sequential, Pack = 8, CharSet = CharSet.Unicode)] - struct TagClipboardInfo - { - #region 字段,对应arx结构的,不要改动,本结构也不允许再加字段 - [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 260)] - public string szTempFile; // 临时文件夹的dwg文件 - [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 260)] - public string szSourceFile; // 文件名从中做出选择..是不是指定块表记录? - [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 4)] - public string szSignature; - public int nFlags; // kbDragGeometry: 从AutoCAD拖动几何图形 - public Point3D dptInsert; // 插入点的原始世界坐标' - public Rect rectGDI; // GDI coord 选择集的边界矩形 - public IntPtr mpView; // void* 用于验证这个对象是在这个视图中创建的 (HWND*) - public int dwThreadId; // AutoCAD thread 创建数据对象 - public int nLen; // 下一段的长度的数据,如果有的话,从chData - public int nType; // 类型的数据,如果有(eExpandedClipDataTypes) - [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 1)] - public string chData; // 数据的开始,如果有 - #endregion - - public string File => szTempFile; - public Point3d Point => dptInsert; - - public TagClipboardInfo(string tmpPath, Point3d insert) - { - szTempFile = tmpPath; - dptInsert = insert; - szSignature = "R15"; - nFlags = 0x01; - - szSourceFile = string.Empty; - chData = string.Empty; - } - - public override string ToString() - { - var sb = new StringBuilder(); - sb.AppendLine($"szTempFile:{szTempFile}"); - sb.AppendLine($"szSourceFile:{szSourceFile}"); - sb.AppendLine($"szSignature:{szSignature}"); - sb.AppendLine($"nFlags:{nFlags}"); - sb.AppendLine($"dptInsert:{dptInsert}"); - sb.AppendLine($"rectGDI:{rectGDI}"); - sb.AppendLine($"mpView:{mpView}"); - sb.AppendLine($"dwThreadId:{dwThreadId}"); - sb.AppendLine($"nLen:{nLen}"); - sb.AppendLine($"nType:{nType}"); - sb.AppendLine($"chData:{chData}"); - return sb.ToString(); - } - - static uint GetRegisterClipboardFormat(string cadStr) - { - // cf = NativeMethods.RegisterClipboardFormat($"{cadStr}.r{Acap.Version.Major}"); - // r17写死,代表每个版本都去找它,否则将作为剪贴板版本隔离 - uint cf = NativeMethods.RegisterClipboardFormat($"{cadStr}.r17"); - return cf; - } - - /// - /// 获取剪贴板 - /// - /// 控制不同的cad:"AutoCAD" - public static TagClipboardInfo? GetClipboard(string cadStr = "AutoCAD") - { - TagClipboardInfo? tag = null; - IntPtr data = IntPtr.Zero; - try - { - uint cf = GetRegisterClipboardFormat(cadStr); - if (!NativeMethods.OpenClipboard(IntPtr.Zero)) - return null; - // 剪贴板的数据拷贝进去结构体中,会依照数据长度进行拷贝 - data = NativeMethods.GetClipboardData(cf); - if (data == IntPtr.Zero) - return null; - IntPtr ptr = NativeMethods.GlobalLock(data); - if (ptr != IntPtr.Zero) - { - // 非托管内存块->托管对象 - tag = (TagClipboardInfo)Marshal.PtrToStructure(ptr, typeof(TagClipboardInfo)); - } - } - finally - { - if (data != IntPtr.Zero) - NativeMethods.GlobalUnlock(data); - NativeMethods.CloseClipboard(); - } - return tag; - } - - /// - /// 设置剪贴板 - /// - /// - /// true成功拷贝;false可能重复粘贴对象导致 - public bool SetClipboard(string cadStr = "AutoCAD") - { - uint cf = GetRegisterClipboardFormat(cadStr); - int size = Marshal.SizeOf(typeof(TagClipboardInfo)); - IntPtr ptr = IntPtr.Zero; - const uint GMEM_MOVEABLE = 0x0002; - IntPtr hglobal = NativeMethods.GlobalAlloc(GMEM_MOVEABLE, size); - try - { - if (NativeMethods.OpenClipboard(IntPtr.Zero)) - { - ptr = NativeMethods.GlobalLock(hglobal); - if (ptr != IntPtr.Zero)// 重复复制同一个图元时 - { - NativeMethods.EmptyClipboard(); - Marshal.StructureToPtr(this, ptr, true); - NativeMethods.GlobalUnlock(hglobal); - NativeMethods.SetClipboardData(cf, hglobal); - NativeMethods.CloseClipboard(); - } - } - } - finally - { - NativeMethods.GlobalFree(hglobal); - } - return ptr != IntPtr.Zero; - } - -#if true2 - // 这种写入方式是失败的 - /// - /// 写入剪贴板 - /// - public void SetClipboard() - { - using var data = new MemoryStream(); - var bytes = Encoding.Unicode.GetBytes(ArxTagClipboardInfo.ToString()); - data.Write(bytes, 0, bytes.Length); - Clipboard.SetData($"AutoCAD.r{Acap.Version.Major}", data); - } - - /// - /// 获取剪贴板 - /// - /// 控制不同的cad:"AutoCAD" - /// 转为结构输出 - public static TagClipboardInfo? GetClipboard(string pasteclipStr = "AutoCAD") - { - /// 粘贴基点字符串 - const string PointStr = "BasePoint:"; - - // 获取剪贴板上面的保存路径 - var format = Clipboard.GetDataObject() - .GetFormats() - .FirstOrDefault(f => f.StartsWith(pasteclipStr)); - if (string.IsNullOrEmpty(format)) - return null; - - using var data = (MemoryStream)Clipboard.GetData(format); - using var reader = new StreamReader(data); - - // 获取剪贴板设置的:临时文件夹中的dwg文件路径 - var str = reader.ReadToEnd(); - str = str.Replace("\0", string.Empty); - var file = str.Substring(0, str.IndexOf(".DWG") + 4); - if (string.IsNullOrEmpty(Path.GetFileName(file))) - return null; - - // 获取剪贴板设置的:dwg基点 - var bpstr = str.IndexOf(PointStr); - if (bpstr == -1) - return null; - - int start = bpstr + PointStr.Length + 1;//1是(括号 - int end = str.IndexOf(")", start) - start; - var ptstr = str.Substring(start, end); - var ptArr = ptstr.Split(','); - - double x = 0; - double y = 0; - double z = 0; - if (ptArr.Length > 0) - x = double.Parse(ptArr[0]); - if (ptArr.Length > 1) - y = double.Parse(ptArr[1]); - if (ptArr.Length > 2) - z = double.Parse(ptArr[2]); - - return new(file, new(x, y, z)); - } -#endif -#if true2 - void GetSize() - { - var v_1 = Marshal.OffsetOf(typeof(TagClipboardInfo), nameof(TagClipboardInfo.szTempFile)).ToInt32(); - var v_2 = Marshal.OffsetOf(typeof(TagClipboardInfo), nameof(TagClipboardInfo.szSourceFile)).ToInt32(); - var v_3 = Marshal.OffsetOf(typeof(TagClipboardInfo), nameof(TagClipboardInfo.szSignature)).ToInt32(); - var v_4 = Marshal.OffsetOf(typeof(TagClipboardInfo), nameof(TagClipboardInfo.nFlags)).ToInt32(); - var v_5 = Marshal.OffsetOf(typeof(TagClipboardInfo), nameof(TagClipboardInfo.dptInsert)).ToInt32(); - var v_6 = Marshal.OffsetOf(typeof(TagClipboardInfo), nameof(TagClipboardInfo.rectGDI)).ToInt32(); - var v_7 = Marshal.OffsetOf(typeof(TagClipboardInfo), nameof(TagClipboardInfo.mpView)).ToInt32(); - var v_8 = Marshal.OffsetOf(typeof(TagClipboardInfo), nameof(TagClipboardInfo.dwThreadId)).ToInt32(); - var v_9 = Marshal.OffsetOf(typeof(TagClipboardInfo), nameof(TagClipboardInfo.nLen)).ToInt32(); - var v_10 = Marshal.OffsetOf(typeof(TagClipboardInfo), nameof(TagClipboardInfo.nType)).ToInt32(); - var v_11 = Marshal.OffsetOf(typeof(TagClipboardInfo), nameof(TagClipboardInfo.chData)).ToInt32(); - var v_12 = Marshal.SizeOf(typeof(TagClipboardInfo)); //1120 - - var v_a = Marshal.SizeOf(typeof(Point3D));//24 - var v_b = Marshal.SizeOf(typeof(Rect));//16 - } -#endif - } - class NativeMethods - { - /// - /// 锁定内存 - /// - /// - /// - [DllImport("kernel32.dll")] - public static extern IntPtr GlobalLock(IntPtr hMem); - /// - /// 解锁内存 - /// - /// - /// - [DllImport("kernel32.dll")] - public static extern bool GlobalUnlock(IntPtr hMem); - /// - /// 开启剪贴板 - /// - /// - /// - [DllImport("user32.dll")] - public static extern bool OpenClipboard(IntPtr hWndNewOwner); - /// - /// 关闭剪贴板 - /// - /// - [DllImport("user32.dll")] - public static extern bool CloseClipboard(); - /// - /// 根据数据格式获取剪贴板 - /// - /// 数据格式名称 - /// - [DllImport("user32.dll")] - public static extern uint RegisterClipboardFormat(string lpszFormat); - /// - /// 获取剪贴板 - /// - /// - /// - [DllImport("user32.dll")] - public static extern IntPtr GetClipboardData(uint uFormat); - /// - /// 设置剪贴板 - /// - /// - /// - /// - [DllImport("user32.dll")] - public static extern IntPtr SetClipboardData(uint uFormat, IntPtr hMem); - - [DllImport("user32.dll")] - public static extern bool EmptyClipboard(); - [DllImport("kernel32.dll")] - public static extern IntPtr GlobalAlloc(uint uFlags, int dwBytes); - [DllImport("kernel32.dll")] - public static extern IntPtr GlobalFree(IntPtr hMem); - } - - - - - - + #region 命令 #if test [IFoxInitialize] public void Init() { - Acap.DocumentManager.DocumentLockModeChanged += DocumentManager_DocumentLockModeChanged; - } -#endif - -#if false - // 想要重启cad之后还可以继续用剪贴板,那么就不要这个 - // 会出现永远存在临时文件夹的情况: - // 0x01 复制的时候,无法删除占用中的, - // 0x02 调试期间直接退出acad.exe - [IFoxInitialize(isInitialize: false)] -#endif - public void Terminate() - { - // 此处要先去删除tmp文件夹的上次剪贴板产生的dwg文件 - for (int i = 0; i < _delFile.Count; i++) - { - if (!File.Exists(_delFile[i])) - { - _delFile.RemoveAt(i); - continue; - } - - try - { - File.Delete(_delFile[i]); - _delFile.RemoveAt(i); - } - catch - { - Env.Printl("无法删除(是否占用):" + _delFile[i]); - } - } + Acap.DocumentManager.DocumentLockModeChanged + += DocumentManager_DocumentLockModeChanged; } /// @@ -426,6 +42,7 @@ public void Terminate() void DocumentManager_DocumentLockModeChanged(object sender, DocumentLockModeChangedEventArgs e) { var up = e.GlobalCommandName.ToUpper(); +#if COPYCLIP if (up == "COPYCLIP")// 复制 { e.Veto(); @@ -436,21 +53,25 @@ void DocumentManager_DocumentLockModeChanged(object sender, DocumentLockModeChan e.Veto(); IFoxCopyBase(); } - else if (up == "PASTECLIP")// 粘贴 + else if (up == "CUTCLIP") // 剪切 { e.Veto(); - IFoxPasteClip(); + Copy(false, true); } - else if (up == "PASTEBLOCK") //ctrl+shift+v 粘贴为块 +#endif +#if PASTECLIP + // TODO 此处直接粘贴到命令栏也会触发,所以需要控制焦点 + if (up == "PASTECLIP")// 粘贴 { e.Veto(); - IFoxPasteBlock(); + IFoxPasteClip(); } - else if (up == "CUTCLIP") // 剪切 + else if (up == "PASTEBLOCK") //ctrl+shift+v 粘贴为块 { e.Veto(); - Copy(false, true); + IFoxPasteBlock(); } +#endif } /// @@ -485,6 +106,39 @@ public void IFoxPasteBlock() { Paste(true); } +#endif + #endregion + + +#if false + // 想要重启cad之后还可以继续用剪贴板,那么就不要这个 + // 会出现永远存在临时文件夹的情况: + // 0x01 复制的时候,无法删除占用中的, + // 0x02 调试期间直接退出acad.exe + [IFoxInitialize(isInitialize: false)] +#endif + public void Terminate() + { + // 此处要先去删除tmp文件夹的上次剪贴板产生的dwg文件 + for (int i = 0; i < _delFile.Count; i++) + { + if (!File.Exists(_delFile[i])) + { + _delFile.RemoveAt(i); + continue; + } + + try + { + File.Delete(_delFile[i]); + _delFile.RemoveAt(i); + } + catch + { + Env.Printl("无法删除(是否占用):" + _delFile[i]); + } + } + } /// @@ -497,7 +151,7 @@ public void IFoxPasteBlock() /// 复制 /// /// - public void Copy(bool getPoint, bool isEraseSsget = false) + void Copy(bool getPoint, bool isEraseSsget = false) { var dm = Acap.DocumentManager; if (dm.Count == 0) @@ -554,7 +208,7 @@ public void Copy(bool getPoint, bool isEraseSsget = false) // 写入剪贴板 // 如果成功拷贝,删除上一次的临时文件 var clipboardInfo = new TagClipboardInfo(tempFile, pt); - if (clipboardInfo.SetClipboard()) + if (TagClipboardInfo.SetClipboard(clipboardInfo)) Terminate(); // 克隆到目标块表内 @@ -601,7 +255,7 @@ public void Copy(bool getPoint, bool isEraseSsget = false) /// 粘贴 /// /// - public void Paste(bool isBlock) + void Paste(bool isBlock) { var dm = Acap.DocumentManager; if (dm.Count == 0) @@ -613,6 +267,12 @@ public void Paste(bool isBlock) var clipboardInfo = tag.Value; Env.Print("粘贴来源: " + clipboardInfo.File); + if (!File.Exists(clipboardInfo.File)) + { + Env.Print("文件不存在"); + return; + } + // 获取临时文件的图元id var fileEntityIds = new List(); using (var fileTr = new DBTrans(clipboardInfo.File, false, FileOpenMode.OpenForReadAndAllShare)) @@ -778,7 +438,7 @@ static void PasteNotBlock(DBTrans tr, BlockTableRecord btr, Point3d move, Point3 /// /// 格式,X是16进制 /// - public static string CreateTempFileName(string format = "X") + static string CreateTempFileName(string format = "X") { var t1 = DateTime.Now.ToString("yyyyMMddHHmmssfffffff"); t1 = Convert.ToInt32(t1.GetHashCode()).ToString(format); diff --git a/tests/TestShared/TestShared.projitems b/tests/TestShared/TestShared.projitems index 0f6076c..031ecc5 100644 --- a/tests/TestShared/TestShared.projitems +++ b/tests/TestShared/TestShared.projitems @@ -25,6 +25,7 @@ + -- Gitee From f2d76a81ff09c6cda820f42ed45cc7dc3db468df Mon Sep 17 00:00:00 2001 From: liuqihong <540762622@qq.com> Date: Mon, 26 Sep 2022 07:17:29 +0800 Subject: [PATCH 525/675] =?UTF-8?q?=E5=88=A0=E9=99=A4=E5=A4=9A=E4=BD=99?= =?UTF-8?q?=E6=96=87=E4=BB=B6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- tests/TestAcad08/TestAcad08 - Backup.csproj | 31 --------------------- 1 file changed, 31 deletions(-) delete mode 100644 tests/TestAcad08/TestAcad08 - Backup.csproj diff --git a/tests/TestAcad08/TestAcad08 - Backup.csproj b/tests/TestAcad08/TestAcad08 - Backup.csproj deleted file mode 100644 index b1ac45e..0000000 --- a/tests/TestAcad08/TestAcad08 - Backup.csproj +++ /dev/null @@ -1,31 +0,0 @@ - - - preview - enable - - NET35 - true - true - x86 - - - - $(Configuration);acad;ac2008;ac2009 - - - - - - - - - - - - - - - - - - -- Gitee From b78446a7bb5bcd4615e7a2c1126896ad5d70271c Mon Sep 17 00:00:00 2001 From: liuqihong <540762622@qq.com> Date: Mon, 26 Sep 2022 07:45:30 +0800 Subject: [PATCH 526/675] =?UTF-8?q?=E4=BC=98=E5=8C=96=E4=BB=A3=E7=A0=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../CLS/TupleElementNamesAttribute.cs | 2 -- src/IFoxCAD.LoadForm/AssemblyDependent.cs | 8 +++---- src/IFoxCAD.WPF/EventBindingExtension.cs | 21 +------------------ tests/TestShared/CmdINI.cs | 4 ++-- 4 files changed, 7 insertions(+), 28 deletions(-) diff --git a/src/IFoxCAD.Basal/CLS/TupleElementNamesAttribute.cs b/src/IFoxCAD.Basal/CLS/TupleElementNamesAttribute.cs index 9c3afb3..93e6193 100644 --- a/src/IFoxCAD.Basal/CLS/TupleElementNamesAttribute.cs +++ b/src/IFoxCAD.Basal/CLS/TupleElementNamesAttribute.cs @@ -2,8 +2,6 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. -using IFoxCAD.Basal; - namespace System.Runtime.CompilerServices; /// diff --git a/src/IFoxCAD.LoadForm/AssemblyDependent.cs b/src/IFoxCAD.LoadForm/AssemblyDependent.cs index c85b0a7..bd9b2f4 100644 --- a/src/IFoxCAD.LoadForm/AssemblyDependent.cs +++ b/src/IFoxCAD.LoadForm/AssemblyDependent.cs @@ -97,7 +97,7 @@ public bool Load(string? dllFullName, List loadStates, bool byteLoad #if Debug_WriteLine_null if (ass == null) System.Diagnostics.Debug - .WriteLine($"****{nameof(Load)}:此文件无加载了pdb对象:" + allRef); + .WriteLine($"****{nameof(Load)}:此文件无加载了pdb对象:" + allRef); #endif #if Debug_WriteLine_notnull @@ -240,7 +240,7 @@ void GetAllRefPaths(Assembly[] cadAssembly, else { /* - * 0x01 此句没有依赖会直接报错 + * 0x01 此句没有依赖会直接报错 * assemblyAsRef = Assembly.ReflectionOnlyLoad(dllFullName); * 0x02 重复加载无修改的同一个dll,会出现如下异常: * System.IO.FileLoadException: @@ -304,7 +304,7 @@ void GetAllRefPaths(Assembly[] cadAssembly, } #endregion - #region 删除文件 + #region 删除文件 /// /// Debug的时候删除obj目录,防止占用 /// @@ -393,7 +393,7 @@ public void Dispose() { Dispose(false); } - + protected virtual void Dispose(bool disposing) { // 不重复释放,并设置已经释放 diff --git a/src/IFoxCAD.WPF/EventBindingExtension.cs b/src/IFoxCAD.WPF/EventBindingExtension.cs index 7352143..76f5b5e 100644 --- a/src/IFoxCAD.WPF/EventBindingExtension.cs +++ b/src/IFoxCAD.WPF/EventBindingExtension.cs @@ -26,35 +26,23 @@ public class EventBindingExtension : MarkupExtension public override object? ProvideValue(IServiceProvider serviceProvider) { if (serviceProvider is null) - { throw new ArgumentNullException(nameof(serviceProvider)); - } if (serviceProvider.GetService(typeof(IProvideValueTarget)) is not IProvideValueTarget targetProvider) - { throw new InvalidOperationException(); - } if (targetProvider.TargetObject is not FrameworkElement targetObject) - { throw new InvalidOperationException(); - } if (targetProvider.TargetProperty is not MemberInfo memberInfo) - { throw new InvalidOperationException(); - } if (string.IsNullOrWhiteSpace(Command)) { Command = memberInfo.Name.Replace("Add", ""); if (Command.Contains("Handler")) - { Command = Command.Replace("Handler", "Command"); - } else - { Command += "Command"; - } } return CreateHandler(memberInfo, Command!, targetObject.GetType()); @@ -136,13 +124,9 @@ public static void HandlerIntern(object sender, object args, string cmdName, str var cmd = GetCommand(fe, cmdName); object? commandParam = null; if (!string.IsNullOrWhiteSpace(commandParameter)) - { commandParam = GetCommandParameter(fe, args, commandParameter!); - } if ((cmd is not null) && cmd.CanExecute(commandParam)) - { cmd.Execute(commandParam); - } } } @@ -154,14 +138,11 @@ public static void HandlerIntern(object sender, object args, string cmdName, str var vmType = vm.GetType(); var cmdProp = vmType.GetProperty(cmdName); if (cmdProp is not null) - { return cmdProp.GetValue(vm) as ICommand; - } #if DEBUG throw new Exception("EventBinding path error: '" + cmdName + "' property not found on '" + vmType + "' 'DelegateCommand'"); #endif - #pragma warning disable CS0162 // 检测到无法访问的代码 return null; #pragma warning restore CS0162 // 检测到无法访问的代码 @@ -207,4 +188,4 @@ internal static object FollowPropertyPath(object target, string path, Type? valu } return target; } -} +} \ No newline at end of file diff --git a/tests/TestShared/CmdINI.cs b/tests/TestShared/CmdINI.cs index 8faf2b8..ea7b21c 100644 --- a/tests/TestShared/CmdINI.cs +++ b/tests/TestShared/CmdINI.cs @@ -40,7 +40,7 @@ public class CmdInit /// 如果netload之后用 删除注册表, /// 由于不是也不能卸载dll,再netload是无法执行自动接口的, /// 所以此时会产生无法再注册的问题...因此需要暴露此注册函数(硬来) - [CommandMethod("IFoxAddReg")] + [CommandMethod(nameof(IFoxAddReg))] public void IFoxAddReg() { var dm = Acap.DocumentManager; @@ -56,7 +56,7 @@ public void IFoxAddReg() /// /// 卸载注册表信息 /// - [CommandMethod("IFoxRemoveReg")] + [CommandMethod(nameof(IFoxRemoveReg))] public void IFoxRemoveReg() { var dm = Acap.DocumentManager; -- Gitee From 0f966815f3ceccef8b2aa969c77d26ced8f17da2 Mon Sep 17 00:00:00 2001 From: liuqihong <540762622@qq.com> Date: Mon, 26 Sep 2022 08:08:59 +0800 Subject: [PATCH 527/675] =?UTF-8?q?=E5=89=AA=E8=B4=B4=E6=9D=BF=E5=A2=9E?= =?UTF-8?q?=E5=8A=A0=E6=88=AA=E5=9B=BEBMP?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/IFoxCAD.Cad.Shared/Copyclip/BitmapTool.cs | 159 ++++ src/IFoxCAD.Cad.Shared/Copyclip/EmfTool.cs | 113 +++ .../{Runtime => Copyclip}/TagClipboardInfo.cs | 776 +++++++++++------- src/IFoxCAD.Cad.Shared/Copyclip/WindowsAPI.cs | 126 +++ .../ExtensionMethod/EditorEx.cs | 63 +- .../IFoxCAD.Cad.Shared.projitems | 6 +- src/IFoxCAD.Cad.Shared/Runtime/LateBinding.cs | 44 + tests/TestShared/Copyclip.cs | 210 +++-- tests/TestShared/testeditor.cs | 22 +- 9 files changed, 1156 insertions(+), 363 deletions(-) create mode 100644 src/IFoxCAD.Cad.Shared/Copyclip/BitmapTool.cs create mode 100644 src/IFoxCAD.Cad.Shared/Copyclip/EmfTool.cs rename src/IFoxCAD.Cad.Shared/{Runtime => Copyclip}/TagClipboardInfo.cs (35%) create mode 100644 src/IFoxCAD.Cad.Shared/Copyclip/WindowsAPI.cs create mode 100644 src/IFoxCAD.Cad.Shared/Runtime/LateBinding.cs diff --git a/src/IFoxCAD.Cad.Shared/Copyclip/BitmapTool.cs b/src/IFoxCAD.Cad.Shared/Copyclip/BitmapTool.cs new file mode 100644 index 0000000..1978b2a --- /dev/null +++ b/src/IFoxCAD.Cad.Shared/Copyclip/BitmapTool.cs @@ -0,0 +1,159 @@ +namespace IFoxCAD.Cad; + +using System; + +public class BitmapTool +{ + // https://blog.csdn.net/shellching/article/details/18405185 + /// Windows不允许程序员直接访问硬件, + /// 它对屏幕的操作是通过环境设备,也就是DC来完成的 + /// 屏幕上的每一个窗口都对应一个DC,可以把DC想象成一个视频缓冲区, + /// 对这这个缓冲区的操作,会表现在这个缓冲区对应的屏幕窗口上. + /// 在窗口的DC之外,可以建立自己的DC,就是说它不对应窗口, + /// 这个方法就是 CreateCompatibleDC,这个DC就是一个内存缓冲区, + /// 通过这个DC你可以把和它兼容的窗口DC保存到这个DC中, + /// 就是说你可以通过它在不同的DC之间拷贝数据. + /// 例如:你先在这个DC中建立好数据,然后在拷贝到窗口的DC就是完成了这个窗口的刷新 + + /// + /// 检索指定窗口的工作区的显示设备上下文(DC)的句柄
    + /// 显示设备上下文可以在随后的图形显示界面(GDI)函数中使用,
    + /// 以在窗口的工作区中绘制
    + ///
    + /// + /// + [DllImport("user32.dll", SetLastError = true)] + public static extern IntPtr GetDC(IntPtr hWnd); + + /// + /// 创建DC + /// + /// + /// + [DllImport("gdi32.dll", EntryPoint = "CreateCompatibleDC", SetLastError = true)] + public static extern IntPtr CreateCompatibleDC([In] IntPtr hdc); + + /// + /// 获取窗口客户区的大小,客户区为窗口中除标题栏,菜单栏之外的地方 + /// + /// + /// + /// + [DllImport("user32.dll", CharSet = CharSet.Auto, EntryPoint = "GetClientRect")] + public static extern bool GetClientRect(IntPtr hwnd, out IntRect lpRect); + + /// + /// Creates a bitmap compatible with the device that is associated with the specified device context. + /// + /// A handle to a device context. + /// The bitmap width, in pixels. + /// The bitmap height, in pixels. + /// If the function succeeds, the return value is a handle to the compatible bitmap (DDB). If the function fails, the return value is . + [DllImport("gdi32.dll", EntryPoint = "CreateCompatibleBitmap")] + public static extern IntPtr CreateCompatibleBitmap([In] IntPtr hdc, int nWidth, int nHeight); + + /// Selects an object into the specified device context (DC). The new object replaces the previous object of the same type. + /// A handle to the DC. + /// A handle to the object to be selected. + /// + /// If the selected object is not a region and the function succeeds, the return value is a handle to the object being replaced. If the selected object is a region and the function succeeds, the return value is one of the following values. + /// SIMPLEREGION - Region consists of a single rectangle. + /// COMPLEXREGION - Region consists of more than one rectangle. + /// NULLREGION - Region is empty. + /// If an error occurs and the selected object is not a region, the return value is NULL. Otherwise, it is HGDI_ERROR. + /// + /// + /// This function returns the previously selected object of the specified type. An application should always replace a new object with the original, default object after it has finished drawing with the new object. + /// An application cannot select a single bitmap into more than one DC at a time. + /// ICM: If the object being selected is a brush or a pen, color management is performed. + /// + [DllImport("gdi32.dll", EntryPoint = "SelectObject")] + public static extern IntPtr SelectObject([In] IntPtr hdc, [In] IntPtr hgdiobj); + + /// Deletes a logical pen, brush, font, bitmap, region, or palette, freeing all system resources associated with the object. After the object is deleted, the specified handle is no longer valid. + /// A handle to a logical pen, brush, font, bitmap, region, or palette. + /// + /// If the function succeeds, the return value is nonzero. + /// If the specified handle is not valid or is currently selected into a DC, the return value is zero. + /// + /// + /// Do not delete a drawing object (pen or brush) while it is still selected into a DC. + /// When a pattern brush is deleted, the bitmap associated with the brush is not deleted. The bitmap must be deleted independently. + /// + [DllImport("gdi32.dll", EntryPoint = "DeleteObject")] + [return: MarshalAs(UnmanagedType.Bool)] + public static extern bool DeleteObject([In] IntPtr hObject); + + /// + /// 指定的源设备环境区域中的像素进行位块转换,以传送到目标设备环境 + /// + /// Handle to the destination device context. + /// The leftmost x-coordinate of the destination rectangle (in pixels). + /// The topmost y-coordinate of the destination rectangle (in pixels). + /// The width of the source and destination rectangles (in pixels). + /// The height of the source and the destination rectangles (in pixels). + /// Handle to the source device context. + /// The leftmost x-coordinate of the source rectangle (in pixels). + /// The topmost y-coordinate of the source rectangle (in pixels). + /// A raster-operation code. + /// + /// true if the operation succeedes, false otherwise. To get extended error information, call . + /// + [DllImport("gdi32.dll", EntryPoint = "BitBlt", SetLastError = true)] + [return: MarshalAs(UnmanagedType.Bool)] + public static extern bool BitBlt([In] IntPtr hdc, int nXDest, int nYDest, int nWidth, int nHeight, [In] IntPtr hdcSrc, int nXSrc, int nYSrc, TernaryRasterOperations dwRop); + + public enum TernaryRasterOperations : uint + { + SRCCOPY = 0x00CC0020, + SRCPAINT = 0x00EE0086, + SRCAND = 0x008800C6, + SRCINVERT = 0x00660046, + SRCERASE = 0x00440328, + NOTSRCCOPY = 0x00330008, + NOTSRCERASE = 0x001100A6, + MERGECOPY = 0x00C000CA, + MERGEPAINT = 0x00BB0226, + PATCOPY = 0x00F00021, + PATPAINT = 0x00FB0A09, + PATINVERT = 0x005A0049, + DSTINVERT = 0x00550009, + BLACKNESS = 0x00000042, + WHITENESS = 0x00FF0062, + CAPTUREBLT = 0x40000000 //only if WinVer >= 5.0.0 (see wingdi.h) + } + + /// + /// 截图成为BMP + /// + /// 截图的窗口 + /// 扔出BMP执行任务 + /// + public static void CaptureWndImage(IntPtr hWnd, Action action) + { + if (action == null) + throw new ArgumentNullException(nameof(action)); + + var hDC = GetDC(hWnd); + var hMemDC = CreateCompatibleDC(hDC); + if (hMemDC == IntPtr.Zero) + return; + + GetClientRect(hWnd, out IntRect rcClient); + int width = rcClient.Right - rcClient.Left; + int height = rcClient.Bottom - rcClient.Top; + + var hBitmap = CreateCompatibleBitmap(hDC, width, height); + if (hBitmap != IntPtr.Zero) + { + SelectObject(hMemDC, hBitmap); + if (BitBlt(hMemDC, 0, 0, width, height, + hDC, 0, 0, TernaryRasterOperations.SRCCOPY)) + { + action.Invoke(hBitmap); + } + DeleteObject(hBitmap); + } + DeleteObject(hMemDC); + } +} \ No newline at end of file diff --git a/src/IFoxCAD.Cad.Shared/Copyclip/EmfTool.cs b/src/IFoxCAD.Cad.Shared/Copyclip/EmfTool.cs new file mode 100644 index 0000000..b635936 --- /dev/null +++ b/src/IFoxCAD.Cad.Shared/Copyclip/EmfTool.cs @@ -0,0 +1,113 @@ +namespace IFoxCAD.Cad; + +using System; +using System.Drawing.Imaging; + +public class EmfTool +{ + /// + /// 获取矢量图的byte + /// + /// + /// + /// + /// + [DllImport("gdi32.dll")] + public static extern uint GetEnhMetaFileBits(IntPtr hemf, uint cbBuffer, byte[] lpbBuffer); + /// + /// byte转换矢量图 + /// + /// + /// + /// + [DllImport("gdi32.dll")] + public static extern IntPtr SetEnhMetaFileBits(uint cbBuffer, byte[] lpBuffer); + /// + /// 删除矢量图 + /// + /// + /// + [DllImport("gdi32.dll")] + public static extern bool DeleteEnhMetaFile(IntPtr hemf); + + /// + /// 创建矢量图 + /// https://www.cnblogs.com/5iedu/p/4706327.html + /// + /// 参考设备环境,NULL时表示以屏幕为参考 + /// 指定文件名时,创建磁盘文件(.EMF)。为NULL时创建内存图元文件 + /// 用于描述图元文件的大小和位置(以0.01mm为单位),可用它精确定义图元文件的物理尺寸 + /// 对图元文件的一段说明。包括创建应用程序的名字、一个NULL字符、对图元文件的一段说明以及两个NULL字符。 + /// 增强型图元文件DC(注意不是图元文件的句柄,要获得实际的图元文件句柄,得调用 CloseEnhMetaFile 函数) + [DllImport("gdi32.dll")] + public static extern IntPtr CreateEnhMetaFile(IntPtr hdcRef, string szFilename, IntRect lpRect, string lpDescription); + + /// + /// Copy EMF to file + /// + /// Handle to EMF + /// File + /// + [DllImport("gdi32.dll")] + static extern IntPtr CopyEnhMetaFile(IntPtr hemfSrc, string lpszFile); + + /// + /// 矢量图保存 + /// + /// + /// + public static void SaveMetaFile(Metafile file, string emfName) + { + //MetafileHeader metafileHeader = file.GetMetafileHeader(); //这句话可要可不要 + IntPtr iptrMetafileHandle = file.GetHenhmetafile(); + CopyEnhMetaFile(iptrMetafileHandle, emfName); + DeleteEnhMetaFile(iptrMetafileHandle); + } + + /// + /// 矢量图 转换 byte[] + /// + /// + /// + public byte[]? ConvertMetaFileToByteArray(System.Drawing.Image image) + { + return ToByteArray((Metafile)image); + } + + /// + /// 矢量图 转换 byte[] + /// + /// + /// + public byte[]? ToByteArray(Metafile mf) + { + byte[]? dataArray = null; + IntPtr handle = mf.GetHenhmetafile(); + + uint bufferSize = GetEnhMetaFileBits(handle, 0, null!); + if (handle != IntPtr.Zero) + { + dataArray = new byte[bufferSize]; + GetEnhMetaFileBits(handle, bufferSize, dataArray); + } + DeleteEnhMetaFile(handle); + return dataArray; + } + + /// + /// byte[] 转换 矢量图 + /// + /// + /// 返回值true删除句柄 + /// + public static void ToMetafile(byte[] data, Func task) + { + if (task == null) + throw new ArgumentNullException(nameof(task)); + + IntPtr hemf = SetEnhMetaFileBits((uint)data.Length, data); + using var mf = new Metafile(hemf, true); + if (task.Invoke(mf)) // 对图像进行操作,就不能进行删除句柄 + DeleteEnhMetaFile(hemf); + } +} \ No newline at end of file diff --git a/src/IFoxCAD.Cad.Shared/Runtime/TagClipboardInfo.cs b/src/IFoxCAD.Cad.Shared/Copyclip/TagClipboardInfo.cs similarity index 35% rename from src/IFoxCAD.Cad.Shared/Runtime/TagClipboardInfo.cs rename to src/IFoxCAD.Cad.Shared/Copyclip/TagClipboardInfo.cs index a27cd95..aebf194 100644 --- a/src/IFoxCAD.Cad.Shared/Runtime/TagClipboardInfo.cs +++ b/src/IFoxCAD.Cad.Shared/Copyclip/TagClipboardInfo.cs @@ -1,47 +1,11 @@ -using System; -using System.Collections.Generic; +namespace IFoxCAD.Cad; + +using System; +using System.Diagnostics; +using System.Drawing.Imaging; using System.Text; using System.Threading; - -namespace IFoxCAD.Cad; - -#if true2 -enum eClipInfoFlags -{ - kbDragGeometry = 0x01, -}; - -enum eXrefType -{ - kXrefTypeAttach = 1, - kXrefTypeOverlay = 2 -}; - -enum eExpandedClipDataTypes -{ - kDcPlotStyles = 1, - kDcXrefs = 2, - kDcLayouts = 3, - kDcBlocks = 4, - kDcLayers = 5, - kDcDrawings = 6, - kDcLinetypes = 7, - kDcTextStyles = 8, - kDcDimStyles = 9, - kDcBlocksWithAttdef = 10, - //#ifdef ADCHATCH - kDcHatches = 11, - //#endif - kTpXrefs = 12, - kTpImages = 13, - kTpTable = 14, - kDcTableStyles = 15, - kDcMultileaderStyles = 16, - kDcVisualStyles = 17, - kDcSectionViewStyles = 18, - kDcDetailViewStyles = 19, -}; -#endif +using System.Windows; public class ClipboardEnv { @@ -51,57 +15,6 @@ public class ClipboardEnv public static string CadVer = "AutoCAD.r17"; } -[Serializable] -[StructLayout(LayoutKind.Sequential)] -public struct IntRect -{ - public int Left { get; } - public int Top { get; } - public int Right { get; } - public int Bottom { get; } - public IntRect(int left, int top, int right, int bottom) - { - Left = left; - Top = top; - Right = right; - Bottom = bottom; - } - - public static IntRect Zero => new(0, 0, 0, 0); - - public override string ToString() - { - return $"Left:{Left},Top:{Top},Right:{Right},Bottom:{Bottom}"; - } -} - -[Serializable] -[StructLayout(LayoutKind.Sequential, Pack = 8)] -public struct Point3D -{ - public double X { get; } - public double Y { get; } - public double Z { get; } - public Point3D(double x, double y, double z) - { - X = x; - Y = y; - Z = z; - } - public static implicit operator Point3D(Point3d pt) - { - return new Point3D(pt.X, pt.Y, pt.Z); - } - public static implicit operator Point3d(Point3D pt) - { - return new Point3d(pt.X, pt.Y, pt.Z); - } - - public override string ToString() - { - return $"X:{X},Y:{Y},Z:{Z}"; - } -} /// /// ARX剪贴板结构 /// @@ -119,7 +32,7 @@ public struct TagClipboardInfo public int nFlags; // kbDragGeometry: 从AutoCAD拖动几何图形 public Point3D dptInsert; // 插入点的原始世界坐标' public IntRect rectGDI; // GDI coord 选择集的边界矩形 - public IntPtr mpView; // void* 用于验证这个对象是在这个视图中创建的 (HWND*) + public IntPtr mpView; // 用于验证这个对象是在这个视图中创建的 (HWND*) public int dwThreadId; // AutoCAD thread 创建数据对象 public int nLen; // 下一段的长度的数据,如果有的话,从chData public int nType; // 类型的数据,如果有(eExpandedClipDataTypes) @@ -127,9 +40,21 @@ public struct TagClipboardInfo public string chData; // 数据的开始,如果有 #endregion + #region 属性,可以改动 public string File => szTempFile; public Point3d Point => dptInsert; +#pragma warning disable CA2211 // 非常量字段应当不可见 + public static IntPtr AcadDwgview + = IntPtr.Zero; + //= AcedGetAcadDwgview(); // c#需要收集这个函数,我先不写,免得中间版本挂了 + + public static int MainWindowThreadId = + (int)WindowsAPI.GetWindowThreadProcessId(Acap.MainWindow.Handle, out uint processId); +#pragma warning restore CA2211 // 非常量字段应当不可见 + #endregion + + #region 构造 /// /// cad剪贴板 /// @@ -147,22 +72,13 @@ public TagClipboardInfo(string tmpFile, Point3d insert) nType = 0; chData = string.Empty; - mpView = AcedGetAcadDwgview(); - dwThreadId = (int)NativeMethods.GetWindowThreadProcessId( - Acap.MainWindow.Handle, out uint processId); + // mpView threadid 可能是用来删除的,用于剪贴板回调清理资源时候判断信息 + mpView = AcadDwgview; + dwThreadId = MainWindowThreadId; } + #endregion - /// - /// 获取视口指针 - /// -#if NET35 - [DllImport("acad.exe", EntryPoint = "?acedGetAcadDwgView@@YAPAVCView@@XZ")] //acad08 -#else - [DllImport("acad.exe", EntryPoint = "?acedGetAcadDwgView@@YAPEAVCView@@XZ")]//acad21 -#endif - static extern IntPtr AcedGetAcadDwgview(); - - + #region 方法 public override string ToString() { var sb = new StringBuilder(); @@ -179,145 +95,9 @@ public override string ToString() sb.AppendLine($"chData:{chData}"); return sb.ToString(); } + #endregion - - - /// - /// 获取剪贴板 - /// - /// 控制不同的cad - public static TagClipboardInfo? GetClipboard() - { - TagClipboardInfo? tag = null; - bool openFlag = false; - try - { - openFlag = NativeMethods.OpenClipboard(IntPtr.Zero); - if (!openFlag) - return null; - - // 读取剪贴板的指定 key 的数据 - var key = NativeMethods.RegisterClipboardFormat(ClipboardEnv.CadVer); - var data = NativeMethods.GetClipboardData(key); - - // 剪贴板的数据拷贝进去结构体中,会依照数据长度进行拷贝 - bool lockFlag = NativeMethods.GlobalLockTask(data, ptr => { - // 非托管内存块->托管对象 - tag = (TagClipboardInfo)Marshal.PtrToStructure(ptr, typeof(TagClipboardInfo)); - }); - if (!lockFlag) - return null; - } - finally - { - if (openFlag) - NativeMethods.CloseClipboard(); - } - return tag; - } - - - /// - /// 设置剪贴板 - /// - /// true成功拷贝;false可能重复粘贴对象导致 - public static bool SetClipboard(TagClipboardInfo tag) - { - IntPtr data = IntPtr.Zero; - bool openFlag = false; - try - { - openFlag = NativeMethods.OpenClipboard(IntPtr.Zero); - if (!openFlag) - return false; - const uint GMEM_MOVEABLE = 0x0002; - int size = Marshal.SizeOf(typeof(TagClipboardInfo)); - data = NativeMethods.GlobalAlloc(GMEM_MOVEABLE, size); - - bool lockFlag = NativeMethods.GlobalLockTask(data, ptr => { - NativeMethods.EmptyClipboard(); - // 托管对象->非托管内存块 - Marshal.StructureToPtr(tag, ptr, true); - }); - if (!lockFlag) - return false; - - var key = NativeMethods.RegisterClipboardFormat(ClipboardEnv.CadVer); - NativeMethods.SetClipboardData(key, data); - } - finally - { - if (data != IntPtr.Zero) - NativeMethods.GlobalFree(data); - if (openFlag) - NativeMethods.CloseClipboard(); - } - return true; - } - -#if true2 - // 这种写入方式是失败的 - /// - /// 写入剪贴板 - /// - public void SetClipboard() - { - using var data = new MemoryStream(); - var bytes = Encoding.Unicode.GetBytes(ArxTagClipboardInfo.ToString()); - data.Write(bytes, 0, bytes.Length); - Clipboard.SetData($"AutoCAD.r{Acap.Version.Major}", data); - } - - /// - /// 获取剪贴板 - /// - /// 控制不同的cad:"AutoCAD" - /// 转为结构输出 - public static TagClipboardInfo? GetClipboard(string pasteclipStr = "AutoCAD") - { - /// 粘贴基点字符串 - const string PointStr = "BasePoint:"; - - // 获取剪贴板上面的保存路径 - var format = Clipboard.GetDataObject() - .GetFormats() - .FirstOrDefault(f => f.StartsWith(pasteclipStr)); - if (string.IsNullOrEmpty(format)) - return null; - - using var data = (MemoryStream)Clipboard.GetData(format); - using var reader = new StreamReader(data); - - // 获取剪贴板设置的:临时文件夹中的dwg文件路径 - var str = reader.ReadToEnd(); - str = str.Replace("\0", string.Empty); - var file = str.Substring(0, str.IndexOf(".DWG") + 4); - if (string.IsNullOrEmpty(Path.GetFileName(file))) - return null; - - // 获取剪贴板设置的:dwg基点 - var bpstr = str.IndexOf(PointStr); - if (bpstr == -1) - return null; - - int start = bpstr + PointStr.Length + 1;//1是(括号 - int end = str.IndexOf(")", start) - start; - var ptstr = str.Substring(start, end); - var ptArr = ptstr.Split(','); - - double x = 0; - double y = 0; - double z = 0; - if (ptArr.Length > 0) - x = double.Parse(ptArr[0]); - if (ptArr.Length > 1) - y = double.Parse(ptArr[1]); - if (ptArr.Length > 2) - z = double.Parse(ptArr[2]); - - return new(file, new(x, y, z)); - } -#endif + #region 测试大小 void GetSize() { var v_1 = Marshal.OffsetOf(typeof(TagClipboardInfo), nameof(TagClipboardInfo.szTempFile)).ToInt32(); @@ -336,122 +116,502 @@ void GetSize() var v_a = Marshal.SizeOf(typeof(Point3D));//24 var v_b = Marshal.SizeOf(typeof(IntRect));//16 } + #endregion + + #region 视口指针 + //[CommandMethod(nameof(testAcedGetAcadDwgview))] + //public void testAcedGetAcadDwgview() + //{ + // var dm = Acap.DocumentManager; + // var doc = dm.MdiActiveDocument; + // var ed = doc.Editor; + + // var a = AcedGetAcadDwgview().ToString(); //自动执行的时候就存在了 + // var b = ed.CurrentViewportObjectId.ToString(); + // Debug.WriteLine("a == b:" + a == b);//不对 + + // var tab = ed.GetCurrentView(); + // var c = tab.ObjectId.ToString(); + // Debug.WriteLine("a == c:" + a == c);//不对 + //} + + /// + /// 获取视口指针 + /// +#if NET35 + [DllImport("acad.exe", EntryPoint = "?acedGetAcadDwgView@@YAPAVCView@@XZ")] //acad08 +#else + [DllImport("acad.exe", EntryPoint = "?acedGetAcadDwgView@@YAPEAVCView@@XZ")]//acad21 +#endif + static extern IntPtr AcedGetAcadDwgview(); + #endregion +} + + +[Serializable] +[StructLayout(LayoutKind.Sequential)] +[DebuggerDisplay("{DebuggerDisplay,nq}")] +[DebuggerTypeProxy(typeof(IntRect))] +public struct IntRect +{ + [DebuggerBrowsable(DebuggerBrowsableState.Never)] + private string DebuggerDisplay => $"(Left:{Left},Top:{Top},Right:{Right},Bottom:{Bottom})"; + + public int Left { get; } + public int Top { get; } + public int Right { get; } + public int Bottom { get; } + public IntRect(int left, int top, int right, int bottom) + { + Left = left; + Top = top; + Right = right; + Bottom = bottom; + } + + static readonly IntRect _Zero = new(0, 0, 0, 0); + public static IntRect Zero => _Zero; + + public override string ToString() + { + return $"({Left},{Top},{Right},{Bottom})"; + } +} + + +[Serializable] +[StructLayout(LayoutKind.Sequential, Pack = 8)] +[DebuggerDisplay("{DebuggerDisplay,nq}")] +[DebuggerTypeProxy(typeof(Point3D))] +public struct Point3D +{ + [DebuggerBrowsable(DebuggerBrowsableState.Never)] + private string DebuggerDisplay => $"(X:{X},Y:{Y},Z:{Z})"; + + public double X { get; } + public double Y { get; } + public double Z { get; } + public Point3D(double x, double y, double z) + { + X = x; + Y = y; + Z = z; + } + public static implicit operator Point3D(Point3d pt) + { + return new Point3D(pt.X, pt.Y, pt.Z); + } + public static implicit operator Point3d(Point3D pt) + { + return new Point3d(pt.X, pt.Y, pt.Z); + } + + public override string ToString() + { + return $"({X},{Y},{Z})"; + } } -class NativeMethods + +public class ClipTool { /// - /// 锁定内存 + /// 侦听剪贴板 /// - /// + /// /// - [DllImport("kernel32.dll")] - static extern IntPtr GlobalLock(IntPtr hMem); + [DllImport("user32.dll", SetLastError = true)] + public static extern bool AddClipboardFormatListener(IntPtr hWnd); /// - /// 解锁内存 + /// 移除侦听剪贴板 /// - /// + /// /// - [DllImport("kernel32.dll")] - static extern bool GlobalUnlock(IntPtr hMem); + [DllImport("user32.dll", SetLastError = true)] + public static extern bool RemoveClipboardFormatListener(IntPtr hWnd); /// - /// 开启剪贴板 + /// 将CWnd加入一个窗口链 + /// 每当剪贴板的内容发生变化时,就会通知这些窗口 + /// + /// 句柄 + /// 返回剪贴板观察器链中下一个窗口的句柄 + [DllImport("User32.dll")] + public static extern int SetClipboardViewer(IntPtr hWndNewViewer); + /// + /// 从剪贴板链中移出的窗口句柄 + /// + /// 从剪贴板链中移出的窗口句柄 + /// hWndRemove的下一个在剪贴板链中的窗口句柄 + /// 如果成功,非零;否则为0。 + [DllImport("User32.dll", CharSet = CharSet.Auto)] + public static extern bool ChangeClipboardChain(IntPtr hWndRemove, IntPtr hWndNewNext); + + + /// + /// 开启剪贴板
    + /// 如果另一个窗口已经打开剪贴板,函数会失败.每次成功调用后都应有调用. ///
    /// /// - [DllImport("user32.dll")] - public static extern bool OpenClipboard(IntPtr hWndNewOwner); + [DllImport("user32.dll", SetLastError = true)] + static extern bool OpenClipboard(IntPtr hWndNewOwner); /// /// 关闭剪贴板 /// /// - [DllImport("user32.dll")] + [DllImport("user32.dll", SetLastError = true)] public static extern bool CloseClipboard(); /// /// 根据数据格式获取剪贴板 /// /// 数据格式名称 /// - [DllImport("user32.dll")] + [DllImport("user32.dll", SetLastError = true)] public static extern uint RegisterClipboardFormat(string lpszFormat); /// /// 获取剪贴板 /// - /// + /// 通常为但是cad有自己的位码 /// - [DllImport("user32.dll")] + [DllImport("user32.dll", SetLastError = true)] public static extern IntPtr GetClipboardData(uint uFormat); /// /// 设置剪贴板 /// - /// - /// + /// 通常为但是cad有自己的位码 + /// 指定具有指定格式的数据的句柄,
    + /// 该参数为空则为延迟提交:
    + /// 有其他程序对剪切板中的数据进行请求时,该程序才会将指定格式的数据写入到剪切板中. + /// /// - [DllImport("user32.dll")] + [DllImport("user32.dll", SetLastError = true)] public static extern IntPtr SetClipboardData(uint uFormat, IntPtr hMem); /// /// 清空剪切板并释放剪切板内数据的句柄 /// /// - [DllImport("user32.dll")] + [DllImport("user32.dll", SetLastError = true)] public static extern bool EmptyClipboard(); /// - /// 从堆中分配一定数目的字节数 - /// - /// 分配方式 - /// 分配的字节数 - /// - [DllImport("kernel32.dll")] - public static extern IntPtr GlobalAlloc(uint uFlags, int dwBytes); - /// - /// 释放堆内存 + /// 枚举剪贴板内数据类型 /// - /// 产生的句柄 + /// /// - [DllImport("kernel32.dll")] - public static extern IntPtr GlobalFree(IntPtr hMem); - - /// - /// 查找主线程
    - /// 代替
    - /// 托管线程和他们不一样: - ///
    - /// 主窗口 - /// 进程ID - /// 线程ID [DllImport("user32.dll", SetLastError = true)] - public static extern uint GetWindowThreadProcessId(IntPtr hWnd, out uint lpdwProcessId); + public static extern uint EnumClipboardFormats(uint format); + + + /* 写入时候注意: + * 0x01 c#自带的是com剪贴板 + * 最好不要使用,它不能在已经打开的剪贴板中使用, + * 也无法写入多个cf对象,也就是复制bitmap的时候会覆盖cad图元 + * Clipboard.SetImage(bitmap); + * 0x02 + * 剪贴板写入各种类型 https://blog.csdn.net/glt3953/article/details/8808262 + */ /// - /// 锁定和释放内存 + /// 打开剪贴板
    + /// 写入之前必须清空,
    + /// 否则将导致发送 WM_DESTROYCLIPBOARD 消息到上一次剪贴板拥有者释放资源
    + /// 所以写入的时候必须一次性写入多个cf
    ///
    - /// 锁定数据对象指针 - /// 返回锁定的内存片段指针,锁定期间执行任务 - /// 是否锁定成功 + /// 接收返回的栈空间指针用于释放 + /// true写入,false读取 + /// /// - public static bool GlobalLockTask(IntPtr data, Action task) + public static bool OpenClipboardTask(Action> action, bool isSetClipboard) { - if (task == null) - throw new ArgumentNullException(nameof(task)); - - if (data == IntPtr.Zero) - return false; + if (action == null) + throw new ArgumentNullException(nameof(action)); + bool openFlag = false; + List freeDatas = new(); try { - var ptr = GlobalLock(data); - - // 有几率导致无效锁定: - // 重复复制同一个图元时,第二次是 IntPtr.Zero, - // 第三次就又可以复制了 - if (ptr == IntPtr.Zero) + openFlag = OpenClipboard(IntPtr.Zero); + if (!openFlag) return false; - - task.Invoke(ptr); + if (isSetClipboard) + EmptyClipboard(); + action.Invoke(freeDatas); } finally { - GlobalUnlock(data); + for (int i = 0; i < freeDatas.Count; i++) + WindowsAPI.GlobalFree(freeDatas[i]); + if (openFlag) + CloseClipboard(); } + return openFlag; + } + + /// + /// 获取剪贴板 + /// + /// 剪贴板的索引名 + public static bool GetClipboard(string clipKey, out T? tag) + { + bool flag = false; + T? result = default; + + OpenClipboardTask(lst => { + // 读取剪贴板的指定数据 + var clipKeyFormat = RegisterClipboardFormat(clipKey);//ClipboardEnv.CadVer + var clipTypeData = GetClipboardData(clipKeyFormat); + + // 剪贴板的数据拷贝进去结构体中,会依照数据长度进行拷贝 + flag = WindowsAPI.GlobalLockTask(clipTypeData, ptr => { + // 非托管内存块->托管对象 + result = (T)Marshal.PtrToStructure(ptr, typeof(T)); + }); + }, false); + + tag = result; + return flag; + } + + // 申请的内存必须为可移动 + public const uint GMEM_MOVEABLE = 0x0002; + + /// + /// 获取剪贴板对象信息
    + /// 微软链接 + ///
    + /// 剪贴板的索引名 + /// 封送类型 + /// 返回:索引值 + /// 返回:申请封送类型栈空间的指针 + /// true成功拷贝;false可能重复粘贴对象导致 + public static bool GetClipboardFormat(string clipKey, T clipType, + out uint clipKeyFormat, out IntPtr clipTypeData) + { + /* + * // 这种写入方式是失败的,ToString转二进制序列化的时候存在元数据 + * using var data = new MemoryStream(); + * var bytes = Encoding.Unicode.GetBytes(TagClipboardInfo.ToString()); + * data.Write(bytes, 0, bytes.Length); + * Clipboard.SetData($"AutoCAD.r{Acap.Version.Major}", data); + */ + + clipKeyFormat = RegisterClipboardFormat(clipKey); + clipTypeData = WindowsAPI.GlobalAlloc(GMEM_MOVEABLE, Marshal.SizeOf(typeof(T))); + bool lockFlag = WindowsAPI.GlobalLockTask(clipTypeData, ptr => { + // 托管对象->非托管内存块 + Marshal.StructureToPtr(clipType, ptr, true); + }); + if (!lockFlag || clipTypeData == IntPtr.Zero) + return false; return true; } -} \ No newline at end of file +} + +/// +/// 剪贴板的CF,也就是它的key +/// +public enum ClipboardFormat : uint +{ + /// + /// Text format. Each line ends with a carriage return/linefeed (CR-LF) combination. A null character signals + /// the end of the data. Use this format for ANSI text. + /// + CF_TEXT = 1, + + /// + /// A handle to a bitmap (HBITMAP). + /// + CF_BITMAP = 2, + + /// + /// Handle to a metafile picture format as defined by the METAFILEPICT structure. When passing a + /// CF_METAFILEPICT handle by means of DDE, the application responsible for deleting hMem should + /// also free the metafile referred to by the CF_METAFILEPICT handle. + /// + CF_METAFILEPICT = 3, + + /// + /// Microsoft Symbolic Link (SYLK) format. + /// + CF_SYLK = 4, + + /// + /// Software Arts' Data Interchange Format. + /// + CF_DIF = 5, + + /// + /// Tagged-image file format. + /// + CF_TIFF = 6, + + /// + /// Text format containing characters in the OEM character set. Each line ends with a carriage return/linefeed + /// (CR-LF) combination. A null character signals the end of the data. + /// + CF_OEMTEXT = 7, + + /// + /// A memory object containing a BITMAPINFO structure followed by the bitmap bits. + /// + CF_DIB = 8, + + /// + /// Handle to a color palette. Whenever an application places data in the clipboard that depends on or assumes + /// a color palette, it should place the palette on the clipboard as well. If the clipboard contains data in + /// the (logical color palette) format, the application should use the + /// SelectPalette and RealizePalette functions to realize (compare) any other data in the + /// clipboard against that logical palette. When displaying clipboard data, the clipboard always uses as its + /// current palette any object on the clipboard that is in the CF_PALETTE format. + /// + CF_PALETTE = 9, + + /// + /// Data for the pen extensions to the Microsoft Windows for Pen Computing. + /// + CF_PENDATA = 10, + + /// + /// Represents audio data more complex than can be represented in a CF_WAVE standard wave format. + /// + CF_RIFF = 11, + + /// + /// Represents audio data in one of the standard wave formats, such as 11 kHz or 22 kHz PCM. + /// + CF_WAVE = 12, + + /// + /// Unicode text format. Each line ends with a carriage return/linefeed (CR-LF) combination. A null character + /// signals the end of the data. + /// + CF_UNICODETEXT = 13, + + /// + /// A handle to an enhanced metafile (HENHMETAFILE). + /// + CF_ENHMETAFILE = 14, + + /// + /// A handle to type HDROP that identifies a list of files. An application can retrieve information + /// about the files by passing the handle to the DragQueryFile function. + /// + CF_HDROP = 15, + + /// + /// The data is a handle to the locale identifier associated with text in the clipboard. When you close the + /// clipboard, if it contains CF_TEXT data but no CF_LOCALE data, the system automatically sets + /// the CF_LOCALE format to the current input language. You can use the CF_LOCALE format to + /// associate a different locale with the clipboard text. + /// An application that pastes text from the clipboard can retrieve this format to determine which character + /// set was used to generate the text. + /// Note that the clipboard does not support plain text in multiple character sets. To achieve this, use a + /// formatted text data type such as RTF instead. + /// The system uses the code page associated with CF_LOCALE to implicitly convert from + /// to . Therefore, the correct code page table is used for + /// the conversion. + /// + CF_LOCALE = 16, + + /// + /// A memory object containing a BITMAPV5HEADER structure followed by the bitmap color space + /// information and the bitmap bits. + /// + CF_DIBV5 = 17, + + /// + /// Owner-display format. The clipboard owner must display and update the clipboard viewer window, and receive + /// the , , + /// , , and + /// messages. The hMem parameter must be null. + /// + CF_OWNERDISPLAY = 0x0080, + + /// + /// Text display format associated with a private format. The hMem parameter must be a handle to data + /// that can be displayed in text format in lieu of the privately formatted data. + /// + CF_DSPTEXT = 0x0081, + + /// + /// Bitmap display format associated with a private format. The hMem parameter must be a handle to + /// data that can be displayed in bitmap format in lieu of the privately formatted data. + /// + CF_DSPBITMAP = 0x0082, + + /// + /// Metafile-picture display format associated with a private format. The hMem parameter must be a + /// handle to data that can be displayed in metafile-picture format in lieu of the privately formatted data. + /// + CF_DSPMETAFILEPICT = 0x0083, + + /// + /// Enhanced metafile display format associated with a private format. The hMem parameter must be a + /// handle to data that can be displayed in enhanced metafile format in lieu of the privately formatted data. + /// + CF_DSPENHMETAFILE = 0x008E, + + /// + /// Start of a range of integer values for application-defined GDI object clipboard formats. The end of the + /// range is . Handles associated with clipboard formats in this range are not + /// automatically deleted using the GlobalFree function when the clipboard is emptied. Also, when using + /// values in this range, the hMem parameter is not a handle to a GDI object, but is a handle allocated + /// by the GlobalAlloc function with the GMEM_MOVEABLE flag. + /// + CF_GDIOBJFIRST = 0x0300, + + /// + /// See . + /// + CF_GDIOBJLAST = 0x03FF, + + /// + /// Start of a range of integer values for private clipboard formats. The range ends with + /// . Handles associated with private clipboard formats are not freed + /// automatically, the clipboard owner must free such handles, typically in response to the + /// message. + /// + CF_PRIVATEFIRST = 0x0200, + + /// + /// See . + /// + CF_PRIVATELAST = 0x02FF, +} + +#if true2 +// arx剪贴板头文件的枚举 +enum eClipInfoFlags +{ + kbDragGeometry = 0x01, +}; + +enum eXrefType +{ + kXrefTypeAttach = 1, + kXrefTypeOverlay = 2 +}; + +enum eExpandedClipDataTypes +{ + kDcPlotStyles = 1, + kDcXrefs = 2, + kDcLayouts = 3, + kDcBlocks = 4, + kDcLayers = 5, + kDcDrawings = 6, + kDcLinetypes = 7, + kDcTextStyles = 8, + kDcDimStyles = 9, + kDcBlocksWithAttdef = 10, + //#ifdef ADCHATCH + kDcHatches = 11, + //#endif + kTpXrefs = 12, + kTpImages = 13, + kTpTable = 14, + kDcTableStyles = 15, + kDcMultileaderStyles = 16, + kDcVisualStyles = 17, + kDcSectionViewStyles = 18, + kDcDetailViewStyles = 19, +}; +#endif \ No newline at end of file diff --git a/src/IFoxCAD.Cad.Shared/Copyclip/WindowsAPI.cs b/src/IFoxCAD.Cad.Shared/Copyclip/WindowsAPI.cs new file mode 100644 index 0000000..8f44f56 --- /dev/null +++ b/src/IFoxCAD.Cad.Shared/Copyclip/WindowsAPI.cs @@ -0,0 +1,126 @@ +namespace IFoxCAD.Cad; + +using System; +using System.Threading; + +public class WindowsAPI +{ + /// + /// 锁定内存 + /// + /// + /// + [DllImport("kernel32.dll", SetLastError = true)] + static extern IntPtr GlobalLock(IntPtr hMem); + /// + /// 解锁内存 + /// + /// + /// + [DllImport("kernel32.dll", SetLastError = true)] + static extern bool GlobalUnlock(IntPtr hMem); + /// + /// 从堆中分配内存 + /// + /// 分配方式 + /// 分配的字节数 + /// + [DllImport("kernel32.dll", SetLastError = true)] + public static extern IntPtr GlobalAlloc(uint uFlags, int dwBytes); + /// + /// 释放堆内存 + /// + /// 产生的句柄 + /// + [DllImport("kernel32.dll", SetLastError = true)] + public static extern IntPtr GlobalFree(IntPtr hMem); + /// + /// 获取内存块大小 + /// + /// + /// + [DllImport("kernel32.dll", SetLastError = true)] + public static extern uint GlobalSize(IntPtr hMem); + + /// + /// 查找主线程
    + /// 代替
    + /// 托管线程和他们不一样: + ///
    + /// 主窗口 + /// 进程ID + /// 线程ID + [DllImport("user32.dll", SetLastError = true)] + public static extern uint GetWindowThreadProcessId(IntPtr hWnd, out uint lpdwProcessId); + + /// + /// 锁定和释放内存 + /// + /// 锁定数据对象指针 + /// 返回锁定的内存片段指针,锁定期间执行任务 + /// 是否锁定成功 + /// + public static bool GlobalLockTask(IntPtr data, Action task) + { + if (task == null) + throw new ArgumentNullException(nameof(task)); + if (data == IntPtr.Zero) + return false; + + try + { + var ptr = GlobalLock(data); + // 有几率导致无效锁定: + // 重复复制同一个图元时,第二次是 IntPtr.Zero, + // 第三次就又可以复制了 + if (ptr == IntPtr.Zero) + return false; + task.Invoke(ptr); + } + finally { GlobalUnlock(data); } + return true; + } +} + + + + + +#if true2 +// arx剪贴板头文件的枚举 +enum eClipInfoFlags +{ + kbDragGeometry = 0x01, +}; + +enum eXrefType +{ + kXrefTypeAttach = 1, + kXrefTypeOverlay = 2 +}; + +enum eExpandedClipDataTypes +{ + kDcPlotStyles = 1, + kDcXrefs = 2, + kDcLayouts = 3, + kDcBlocks = 4, + kDcLayers = 5, + kDcDrawings = 6, + kDcLinetypes = 7, + kDcTextStyles = 8, + kDcDimStyles = 9, + kDcBlocksWithAttdef = 10, + //#ifdef ADCHATCH + kDcHatches = 11, + //#endif + kTpXrefs = 12, + kTpImages = 13, + kTpTable = 14, + kDcTableStyles = 15, + kDcMultileaderStyles = 16, + kDcVisualStyles = 17, + kDcSectionViewStyles = 18, + kDcDetailViewStyles = 19, +}; +#endif \ No newline at end of file diff --git a/src/IFoxCAD.Cad.Shared/ExtensionMethod/EditorEx.cs b/src/IFoxCAD.Cad.Shared/ExtensionMethod/EditorEx.cs index 535749d..28184f4 100644 --- a/src/IFoxCAD.Cad.Shared/ExtensionMethod/EditorEx.cs +++ b/src/IFoxCAD.Cad.Shared/ExtensionMethod/EditorEx.cs @@ -1051,13 +1051,14 @@ public enum RunLispFlag : byte * [CommandMethod("CmdTest_RunLisp")] * public static void CmdTest_RunLisp() * { - * var res = SendLisp.RunLisp("(setq abc 10)"); + * var res = RunLisp("(setq abc 10)"); * } * 调用方式: * (command "CmdTest_RunLisp1") * bug说明: - * AcedEvaluateLisp接口在高版本调用时候没有运行成功,使得 !abc 没有值 - * 经过测试,cad08调用成功,此bug与CommandFlags无关 + * AcedEvaluateLisp 接口 + * 在高版本调用时候没有运行成功,使得 !abc 没有值 + * 在cad08成功,此bug与CommandFlags无关 * 解决方案: * 0x01 用异步接口,但是这样是显式调用了: * (setq thisdrawing (vla-get-activedocument (vlax-get-acad-object)))(vla-SendCommand thisdrawing "CmdTest_RunLisp1 ") @@ -1085,4 +1086,60 @@ public enum RunLispFlag : byte return null; } #endregion + + #region Export + /// + /// 输出WMF
    + ///
    + /// 命令行对象 + /// 保存文件 + /// 选择集的对象,为null时候手选 + /// 是否清空选择集 + /// + public static void ExportWMF(this Editor editor, string saveFile, + ObjectId[]? ids = null, bool wmfSetDel = false) + { + if (string.IsNullOrEmpty(saveFile)) + throw new ArgumentNullException(nameof(saveFile)); + if (File.Exists(saveFile)) + throw new FileFormatException("文件重复:" + saveFile); + + var dm = Acap.DocumentManager; + if (dm.Count == 0) + return; + + // 剔除后缀 + int dot = saveFile.LastIndexOf('.'); + if (dot != -1) + { + // 因为文件名可以有.所以后缀点必须是最后\的后面 + int s = saveFile.LastIndexOf('\\'); + if (s < dot) + saveFile = saveFile.Substring(0, dot); + } + + // ActiveSelectionSet: + // 第一次执行会触发选择, + // 再次重复命令执行的时候,它会无法再选择 + // 因此此处选择一次,此时必然有当前选择集,它就直接获取当前选择集 + if (ids == null || ids.Length == 0) + { + var psr = editor.SelectImplied();// 预选 + if (psr.Status != PromptStatus.OK) + psr = editor.GetSelection();// 手选 + if (psr.Status != PromptStatus.OK) + return; + } +#if zcad + var com = Acap.ZcadApplication; +#else + var com = Acap.AcadApplication; +#endif + var doc = com.GetProperty("ActiveDocument"); + var wmfSet = doc.GetProperty("ActiveSelectionSet"); + doc.Invoke("Export", saveFile, "wmf", wmfSet); // JPGOUT,PNGOUT + if (wmfSetDel) + wmfSet.Invoke("Delete"); + } + #endregion } \ No newline at end of file diff --git a/src/IFoxCAD.Cad.Shared/IFoxCAD.Cad.Shared.projitems b/src/IFoxCAD.Cad.Shared/IFoxCAD.Cad.Shared.projitems index 74ab2a5..8ef3b71 100644 --- a/src/IFoxCAD.Cad.Shared/IFoxCAD.Cad.Shared.projitems +++ b/src/IFoxCAD.Cad.Shared/IFoxCAD.Cad.Shared.projitems @@ -49,6 +49,7 @@ + @@ -59,7 +60,9 @@ - + + + @@ -74,5 +77,6 @@ + \ No newline at end of file diff --git a/src/IFoxCAD.Cad.Shared/Runtime/LateBinding.cs b/src/IFoxCAD.Cad.Shared/Runtime/LateBinding.cs new file mode 100644 index 0000000..21df19c --- /dev/null +++ b/src/IFoxCAD.Cad.Shared/Runtime/LateBinding.cs @@ -0,0 +1,44 @@ +namespace IFoxCAD.Cad; + +/// +/// 后绑代码工具 +/// +public static class LateBinding +{ + public static object GetInstance(string appName) + { + return Marshal.GetActiveObject(appName); + } + public static object CreateInstance(string appName) + { + return Activator.CreateInstance(Type.GetTypeFromProgID(appName)); + } + public static object GetOrCreateInstance(string appName) + { + try { return GetInstance(appName); } + catch { return CreateInstance(appName); } + } + public static void ReleaseInstance(this object obj) + { + Marshal.ReleaseComObject(obj); + } + + public static object GetProperty(this object obj, string propName, params object[] parameter) + { + return obj.GetType().InvokeMember(propName, + BindingFlags.GetProperty, + null, obj, parameter); + } + public static void SetProperty(this object obj, string propName, params object[] parameter) + { + obj.GetType().InvokeMember(propName, + BindingFlags.SetProperty, + null, obj, parameter); + } + public static object Invoke(this object obj, string memberName, params object[] parameter) + { + return obj.GetType().InvokeMember(memberName, + BindingFlags.Public | BindingFlags.InvokeMethod, + null, obj, parameter); + } +} \ No newline at end of file diff --git a/tests/TestShared/Copyclip.cs b/tests/TestShared/Copyclip.cs index f2704df..153e995 100644 --- a/tests/TestShared/Copyclip.cs +++ b/tests/TestShared/Copyclip.cs @@ -1,11 +1,9 @@ #define test -#define PASTECLIP #define COPYCLIP +#define PASTECLIP namespace Test; - -using IFoxCAD.Cad; -using System; +using Autodesk.AutoCAD.DatabaseServices; using System.Threading; /* @@ -42,42 +40,64 @@ public void Init() void DocumentManager_DocumentLockModeChanged(object sender, DocumentLockModeChangedEventArgs e) { var up = e.GlobalCommandName.ToUpper(); + string? cmd = null; #if COPYCLIP if (up == "COPYCLIP")// 复制 { e.Veto(); - IFoxCopyClip(); + cmd = nameof(IFoxCopyClip); } else if (up == "COPYBASE") //ctrl+shift+c 带基点复制 { e.Veto(); - IFoxCopyBase(); + cmd = nameof(IFoxCopyBase); } else if (up == "CUTCLIP") // 剪切 { e.Veto(); - Copy(false, true); + cmd = nameof(IFoxCutclip); } #endif #if PASTECLIP - // TODO 此处直接粘贴到命令栏也会触发,所以需要控制焦点 if (up == "PASTECLIP")// 粘贴 { + //TODO === 完成之后此处将会移除 + var getClip = ClipTool.GetClipboard(ClipboardEnv.CadVer, out TagClipboardInfo tag); + if (!getClip) + return; + //=== 完成之后此处将会移除 + e.Veto(); - IFoxPasteClip(); + cmd = nameof(IFoxPasteClip); } else if (up == "PASTEBLOCK") //ctrl+shift+v 粘贴为块 { + //TODO === 完成之后此处将会移除 + var getClip = ClipTool.GetClipboard(ClipboardEnv.CadVer, out TagClipboardInfo tag); + if (!getClip) + return; + //=== 完成之后此处将会移除 + e.Veto(); - IFoxPasteBlock(); + cmd = nameof(IFoxPasteBlock); } #endif + if (cmd != null) + { + var dm = Acap.DocumentManager; + if (dm.Count == 0) + return; + var doc = dm.MdiActiveDocument; + // 发送命令是因为导出WMF函数的com需要命令形式,否则将报错 + // 但是发送命令会导致选择集被取消了,那么就需要设置 CommandFlags.Redraw + doc.SendStringToExecute(cmd + "\n", true, false, false); + } } /// /// 复制 /// - [CommandMethod(nameof(IFoxCopyClip), CommandFlags.UsePickSet)] + [CommandMethod(nameof(IFoxCopyClip), CommandFlags.UsePickSet | CommandFlags.Redraw)] public void IFoxCopyClip() { Copy(false); @@ -85,11 +105,21 @@ public void IFoxCopyClip() /// /// 带基点复制 /// - [CommandMethod(nameof(IFoxCopyBase), CommandFlags.UsePickSet)] + [CommandMethod(nameof(IFoxCopyBase), CommandFlags.UsePickSet | CommandFlags.Redraw)] public void IFoxCopyBase() { Copy(true); } + /// + /// 剪切 + /// + [CommandMethod(nameof(IFoxCutclip), CommandFlags.UsePickSet | CommandFlags.Redraw)] + public void IFoxCutclip() + { + Copy(false, true); + } + + /// /// 粘贴 /// @@ -109,14 +139,11 @@ public void IFoxPasteBlock() #endif #endregion - -#if false // 想要重启cad之后还可以继续用剪贴板,那么就不要这个 // 会出现永远存在临时文件夹的情况: // 0x01 复制的时候,无法删除占用中的, // 0x02 调试期间直接退出acad.exe - [IFoxInitialize(isInitialize: false)] -#endif + // [IFoxInitialize(isInitialize: false)] public void Terminate() { // 此处要先去删除tmp文件夹的上次剪贴板产生的dwg文件 @@ -133,16 +160,14 @@ public void Terminate() File.Delete(_delFile[i]); _delFile.RemoveAt(i); } - catch - { - Env.Printl("无法删除(是否占用):" + _delFile[i]); - } + catch { Env.Printl("无法删除(是否占用):" + _delFile[i]); } } } /// /// 储存准备删除的文件 + /// 也可以用txt代替 /// 如果删除出错(占用),将一直在这个集合中,直到cad关闭 /// readonly List _delFile = new(); @@ -156,22 +181,34 @@ void Copy(bool getPoint, bool isEraseSsget = false) var dm = Acap.DocumentManager; if (dm.Count == 0) return; + var doc = dm.MdiActiveDocument; - using var tr = new DBTrans(); - if (tr.Editor == null) + if (doc.Editor == null) return; - var psr = tr.Editor.SelectImplied();// 预选 + var psr = doc.Editor.SelectImplied();// 预选 if (psr.Status != PromptStatus.OK) - psr = tr.Editor.GetSelection();// 手选 + psr = doc.Editor.GetSelection();// 手选 if (psr.Status != PromptStatus.OK) return; // 设置基点 Point3d pt = Point3d.Origin; var idArray = psr.Value.GetObjectIds(); + + var tempFile = CreateTempFileName(); + while (File.Exists(tempFile) || + File.Exists(Path.ChangeExtension(tempFile, "wmf"))) + { + tempFile = CreateTempFileName(); + Thread.Sleep(1); + } + + using var tr = new DBTrans(); + + #region 写入 AutoCAD.R17 数据 if (getPoint) { - var pr = tr.Editor.GetPoint("\n选择基点"); + var pr = doc.Editor.GetPoint("\n选择基点"); if (pr.Status != PromptStatus.OK) return; pt = pr.Value; @@ -198,21 +235,10 @@ void Copy(bool getPoint, bool isEraseSsget = false) pt = new(minx, miny, minz); } - var tempFile = CreateTempFileName(); - while (File.Exists(tempFile)) - { - tempFile = CreateTempFileName(); - Thread.Sleep(1); - } - - // 写入剪贴板 - // 如果成功拷贝,删除上一次的临时文件 - var clipboardInfo = new TagClipboardInfo(tempFile, pt); - if (TagClipboardInfo.SetClipboard(clipboardInfo)) - Terminate(); + var cadClipType = new TagClipboardInfo(tempFile, pt); // 克隆到目标块表内 - using (var fileTr = new DBTrans(clipboardInfo.File)) + using (var fileTr = new DBTrans(cadClipType.File)) { fileTr.Task(() => { var map = new IdMapping(); @@ -246,11 +272,98 @@ void Copy(bool getPoint, bool isEraseSsget = false) id.Erase(); }); } + #endregion + + #region 写入 WMF 数据 + var wmf = Path.ChangeExtension(cadClipType.File, "wmf"); + Env.Editor.ExportWMF(wmf, idArray); + + //using var mf = new Metafile(wmf); + //IntPtr emfHandle = mf.GetHenhmetafile(); + #endregion + + /* + * 剪贴板说明 + * https://blog.csdn.net/chinabinlang/article/details/9815495 + * + * 看了这ole剪贴板,感觉如果桌子真的是这样做,那么粘贴链接可能还真没法做成. + * 1,不知道桌子如何发送wmf文件,是结构体传送,还是文件路径传送. + * 2,不知道桌子如何接收剪贴板数据,是延迟接收还是一次性写入全局变量或者文件. + * https://blog.csdn.net/chinabinlang/article/details/9815495 + */ + /// 必须一次性写入剪贴板,详见说明 + bool getFlag = false; + ClipTool.OpenClipboardTask((freeDatas) => { + getFlag = ClipTool.GetClipboardFormat( + ClipboardEnv.CadVer, cadClipType, + out uint cadClipFormat, out IntPtr cadClipData); + freeDatas.Add(cadClipData); + + // 写入cad图元 + ClipTool.SetClipboardData(cadClipFormat, cadClipData); + + // 写入BMP位图...这是截图,不是WMF转BMP,不对 + BitmapTool.CaptureWndImage(doc.Window.Handle, bitmapHandle => { + ClipTool.SetClipboardData((uint)ClipboardFormat.CF_BITMAP, bitmapHandle); + }); + - if (!_delFile.Contains(clipboardInfo.File)) - _delFile.Add(clipboardInfo.File); + // 写入WMF + // MFC类 CMetaFileDC 不知道怎么做... + // https://blog.csdn.net/glt3953/article/details/8808262 + // ClipTool.SetClipboardData((uint)ClipboardFormat.CF_ENHMETAFILE, wmfHandle); + + // c# WMF转换文件 + // https://blog.csdn.net/u013419838/article/details/100154891 + //using MemoryStream ms = new(File.ReadAllBytes(wmf)); + //using GZipStream gzipStream = new(ms, CompressionMode.Decompress); + //using MemoryStream outStream = new(); + //int readCount; + //byte[] data = new byte[2048]; + //do + //{ + // readCount = gzipStream.Read(data, 0, data.Length); + // outStream.Write(data, 0, readCount); + //} while (readCount == data.Length); + // outStream.GetBuffer() + + //ClipTool.GetClipboardFormat( + // System.Windows.Forms.DataFormats.EnhancedMetafile, emfClipType, + // out uint emfClipFormat, out IntPtr emfClipData); + //freeDatas.Add(emfClipData); + + // 没效果 + // using var mf = new Metafile(wmf); + // IntPtr emfHandle = mf.GetHenhmetafile(); + //if (emfHandle != IntPtr.Zero) + //{ + // ClipTool.SetClipboardData( + // ClipTool.RegisterClipboardFormat(System.Windows.Forms.DataFormats.EnhancedMetafile), + // emfHandle); + //} + + /* + * 文件结构 + * https://www.vuln.cn/6358#:~:text=%20%E4%B8%8B%E9%9D%A2%E7%AE%80%E8%A6%81%E4%BB%8B%E7%BB%8D%E4%B8%80%E4%B8%8BEMF%E6%96%87%E4%BB%B6%E7%9A%84%E7%BB%93%E6%9E%84%EF%BC%8CEMF%E6%96%87%E4%BB%B6%E7%94%B1%E5%8F%AF%E5%8F%98%E5%A4%A7%E5%B0%8F%E7%9A%84%E5%85%83%E6%96%87%E4%BB%B6%E5%9D%97%E7%BB%84%E6%88%90%E3%80%82,%E6%AF%8F%E4%B8%AA%E5%85%83%E6%96%87%E4%BB%B6%E5%9D%97%E9%83%BD%E6%98%AF%E4%B8%80%E4%B8%AA%E5%8F%AF%E5%8F%98%E9%95%BF%E5%BA%A6%E7%9A%84ENHMETARECORD%E7%BB%93%E6%9E%84%EF%BC%8C%E7%BB%93%E6%9E%84%E5%A6%82%E4%B8%8B%E3%80%82%20SDK%E4%B8%AD%E5%AE%9A%E4%B9%89%E4%BA%86%E4%B8%8D%E5%90%8C%E7%9A%84iType%E7%B1%BB%E5%9E%8B%EF%BC%8C%E5%A6%82%E4%B8%8B%E6%89%80%E7%A4%BA%E3%80%82%20%E6%A0%B9%E6%8D%AEiType%E7%B1%BB%E5%9E%8B%E7%9A%84%E4%B8%8D%E5%90%8C%EF%BC%8CdParm%E6%98%AF%E4%B8%8D%E5%90%8C%E7%9A%84%E7%BB%93%E6%9E%84%EF%BC%8CEMR_SETDIBITSTODEVICE%E5%AF%B9%E5%BA%94%E7%9A%84%E7%BB%93%E6%9E%84%E6%98%AFEMRSETDIBITSTODEVICE%E3%80%82 + * https://blog.csdn.net/juma/article/details/2200023 有代码抄 + * https://www.cnblogs.com/5iedu/p/4706327.html + * https://vimsky.com/examples/detail/csharp-ex-System.Runtime.InteropServices.ComTypes-STGMEDIUM---class.html + * https://blog.csdn.net/qq_45533841/article/details/106011204 + */ + }, true); + + // 成功拷贝就删除上一次的临时文件 + if (getFlag) + Terminate(); + + // 加入删除队列,下次删除 + if (!_delFile.Contains(cadClipType.File)) + _delFile.Add(cadClipType.File); + if (!_delFile.Contains(wmf)) + _delFile.Add(wmf); } + /// /// 粘贴 /// @@ -261,10 +374,10 @@ void Paste(bool isBlock) if (dm.Count == 0) return; - var tag = TagClipboardInfo.GetClipboard(); - if (tag == null) + var getClip = ClipTool.GetClipboard(ClipboardEnv.CadVer, out TagClipboardInfo tag); + if (!getClip) return; - var clipboardInfo = tag.Value; + var clipboardInfo = tag; Env.Print("粘贴来源: " + clipboardInfo.File); if (!File.Exists(clipboardInfo.File)) @@ -275,7 +388,9 @@ void Paste(bool isBlock) // 获取临时文件的图元id var fileEntityIds = new List(); - using (var fileTr = new DBTrans(clipboardInfo.File, false, FileOpenMode.OpenForReadAndAllShare)) + using (var fileTr = new DBTrans(clipboardInfo.File, + commit: false, + openMode: FileOpenMode.OpenForReadAndAllShare)) { foreach (var id in fileTr.ModelSpace) if (id.IsOk()) @@ -285,14 +400,13 @@ void Paste(bool isBlock) return; using var tr = new DBTrans(); -#if test + // 给辰的测试 //var pr = tr.Editor?.GetPoint("aaa"); //if (pr != null && pr.Status == PromptStatus.OK) - //{ // tr.Editor?.WriteMessage("获取了点:" + pr.Value); - //} -#endif + + tr.Editor?.SetImpliedSelection(new ObjectId[0]); // 清空选择集 // 新建块表记录 diff --git a/tests/TestShared/testeditor.cs b/tests/TestShared/testeditor.cs index 60d742f..b2a6c76 100644 --- a/tests/TestShared/testeditor.cs +++ b/tests/TestShared/testeditor.cs @@ -1,4 +1,7 @@ -namespace Test; +using Autodesk.AutoCAD.EditorInput; +using IFoxCAD.Cad; + +namespace Test; public class Testeditor { @@ -56,8 +59,21 @@ public void Testssget() { "B", action_b } }; - var ss = Env.Editor.SSGet( ":S", messages: new string[2] { "get", "del" }, + var ss = Env.Editor.SSGet(":S", messages: new string[2] { "get", "del" }, keywords: keyword); Env.Print(ss!); } -} + + [CommandMethod(nameof(Test_ExportWMF), CommandFlags.Modal | CommandFlags.UsePickSet)] + public void Test_ExportWMF() + { + var psr = Env.Editor.SelectImplied();// 预选,如果没有设置 CommandFlags.UsePickSet 将导致PromptStatus.Error + if (psr.Status != PromptStatus.OK) + psr = Env.Editor.GetSelection();// 手选 + if (psr.Status != PromptStatus.OK) + return; + + var ids = psr.Value.GetObjectIds(); + Env.Editor.ExportWMF("D:\\桌面\\aaa.dwg", ids); + } +} \ No newline at end of file -- Gitee From e28ca93b33eb9e3b3062091ad1262ea97e6299d1 Mon Sep 17 00:00:00 2001 From: liuqihong <540762622@qq.com> Date: Mon, 26 Sep 2022 22:18:12 +0800 Subject: [PATCH 528/675] =?UTF-8?q?=E5=80=92=E5=BA=8F=E5=88=A0=E9=99=A4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- tests/TestShared/Copyclip.cs | 34 ++++++++++++++++------------------ 1 file changed, 16 insertions(+), 18 deletions(-) diff --git a/tests/TestShared/Copyclip.cs b/tests/TestShared/Copyclip.cs index 153e995..aaaf49a 100644 --- a/tests/TestShared/Copyclip.cs +++ b/tests/TestShared/Copyclip.cs @@ -139,25 +139,20 @@ public void IFoxPasteBlock() #endif #endregion - // 想要重启cad之后还可以继续用剪贴板,那么就不要这个 + // 想要重启cad之后还可以继续用剪贴板,那么就不要这个: + // [IFoxInitialize(isInitialize: false)] // 会出现永远存在临时文件夹的情况: // 0x01 复制的时候,无法删除占用中的, - // 0x02 调试期间直接退出acad.exe - // [IFoxInitialize(isInitialize: false)] + // 0x02 调试期间直接退出 acad.exe public void Terminate() { // 此处要先去删除tmp文件夹的上次剪贴板产生的dwg文件 - for (int i = 0; i < _delFile.Count; i++) + for (int i = _delFile.Count - 1; i >= 0; i--) { - if (!File.Exists(_delFile[i])) - { - _delFile.RemoveAt(i); - continue; - } - try { - File.Delete(_delFile[i]); + if (File.Exists(_delFile[i])) + File.Delete(_delFile[i]); _delFile.RemoveAt(i); } catch { Env.Printl("无法删除(是否占用):" + _delFile[i]); } @@ -182,7 +177,6 @@ void Copy(bool getPoint, bool isEraseSsget = false) if (dm.Count == 0) return; var doc = dm.MdiActiveDocument; - if (doc.Editor == null) return; var psr = doc.Editor.SelectImplied();// 预选 @@ -241,8 +235,8 @@ void Copy(bool getPoint, bool isEraseSsget = false) using (var fileTr = new DBTrans(cadClipType.File)) { fileTr.Task(() => { - var map = new IdMapping(); - using var ids = new ObjectIdCollection(idArray); + using IdMapping map = new(); + using ObjectIdCollection ids = new(idArray); tr.Database.WblockCloneObjects( ids, fileTr.ModelSpace.ObjectId, @@ -349,6 +343,8 @@ void Copy(bool getPoint, bool isEraseSsget = false) * https://www.cnblogs.com/5iedu/p/4706327.html * https://vimsky.com/examples/detail/csharp-ex-System.Runtime.InteropServices.ComTypes-STGMEDIUM---class.html * https://blog.csdn.net/qq_45533841/article/details/106011204 + * + * 搞不定... */ }, true); @@ -417,17 +413,17 @@ void Paste(bool isBlock) // 加入新建的块表记录 // 动态块粘贴,然后用:ctrl+z会导致动态块特性无法恢复, /// 是因为它 - var map = new IdMapping(); + using IdMapping map = new(); + using ObjectIdCollection idc = new(fileEntityIds.ToArray()); tr.Task(() => { tr.Database.WblockCloneObjects( - new ObjectIdCollection(fileEntityIds.ToArray()), + idc, btr.ObjectId, // tr.Database.BlockTableId, // 粘贴目标 map, DuplicateRecordCloning.Ignore, false); }); - // 移动块内,从基点到原点 foreach (var id in btr) { @@ -516,7 +512,9 @@ static void PasteNotBlock(DBTrans tr, BlockTableRecord btr, Point3d move, Point3 } // 深度克隆,然后平移到当前目标点位置 - var map = tr.CurrentSpace.DeepCloneEx(ids); + using IdMapping map = new(); + tr.CurrentSpace.DeepCloneEx(ids, map); + map.GetValues().ForEach(id => { if (!id.IsOk()) return; -- Gitee From 886cb84bfb31996b874aab5e9be94f98ef2fa1be Mon Sep 17 00:00:00 2001 From: liuqihong <540762622@qq.com> Date: Mon, 26 Sep 2022 22:19:08 +0800 Subject: [PATCH 529/675] =?UTF-8?q?using=E6=89=80=E6=9C=89=E7=9A=84cad?= =?UTF-8?q?=E5=AE=B9=E5=99=A8,=E9=81=BF=E5=85=8D=E9=81=97=E5=BF=98?= =?UTF-8?q?=E9=87=8A=E6=94=BE,=E9=83=A8=E5=88=86=E6=9C=AA=E7=BB=8F?= =?UTF-8?q?=E6=B5=8B=E8=AF=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../ExtensionMethod/CollectionEx.cs | 10 ++--- .../ExtensionMethod/Curve2dEx.cs | 11 +---- .../ExtensionMethod/Curve3dEx.cs | 6 +-- .../ExtensionMethod/CurveEx.cs | 27 ++++++------- .../ExtensionMethod/DBDictionaryEx.cs | 3 +- .../ExtensionMethod/DBTransEx.cs | 5 +-- .../ExtensionMethod/EntityEx.cs | 12 ++++-- .../ExtensionMethod/PointEx.cs | 16 ++++---- .../ExtensionMethod/SymbolTableRecordEx.cs | 8 ++-- .../ExtensionMethod/XrefEx.cs | 40 ++++++++++--------- .../HatchConverter.cs" | 10 +++-- .../HatchInfo.cs" | 9 ++--- src/IFoxCAD.Cad.Shared/Runtime/SymbolTable.cs | 20 +++++----- tests/TestShared/TestDwgFilerEx.cs | 8 ++-- tests/TestShared/testblock.cs | 2 +- 15 files changed, 95 insertions(+), 92 deletions(-) diff --git a/src/IFoxCAD.Cad.Shared/ExtensionMethod/CollectionEx.cs b/src/IFoxCAD.Cad.Shared/ExtensionMethod/CollectionEx.cs index d70e4b4..bfd49f8 100644 --- a/src/IFoxCAD.Cad.Shared/ExtensionMethod/CollectionEx.cs +++ b/src/IFoxCAD.Cad.Shared/ExtensionMethod/CollectionEx.cs @@ -12,7 +12,7 @@ public static class CollectionEx /// 对象id迭代器转换为集合 ///
    /// 对象id的迭代器 - /// 对象id集合 + /// 对象id集合,记得释放 public static ObjectIdCollection ToCollection(this IEnumerable ids) { return new ObjectIdCollection(ids.ToArray()); @@ -23,7 +23,7 @@ public static ObjectIdCollection ToCollection(this IEnumerable ids) ///
    /// 对象类型 /// 实体对象的迭代器 - /// 实体集合 + /// 实体集合,记得释放 public static DBObjectCollection ToCollection(this IEnumerable objs) where T : DBObject { DBObjectCollection objCol = new(); @@ -36,7 +36,7 @@ public static DBObjectCollection ToCollection(this IEnumerable objs) where /// double 数值迭代器转换为 double 数值集合 ///
    /// double 数值迭代器 - /// double 数值集合 + /// 数值集合,它没有Dispose public static DoubleCollection ToCollection(this IEnumerable doubles) { return new DoubleCollection(doubles.ToArray()); @@ -46,7 +46,7 @@ public static DoubleCollection ToCollection(this IEnumerable doubles) /// 二维点迭代器转换为二维点集合 ///
    /// 二维点迭代器 - /// 二维点集合 + /// 二维点集合,!acad记得释放 public static Point2dCollection ToCollection(this IEnumerable pts) { return new Point2dCollection(pts.ToArray()); @@ -56,7 +56,7 @@ public static Point2dCollection ToCollection(this IEnumerable pts) /// 三维点迭代器转换为三维点集合 ///
    /// 三维点迭代器 - /// 三维点集合 + /// 三维点集合,记得释放 public static Point3dCollection ToCollection(this IEnumerable pts) { return new Point3dCollection(pts.ToArray()); diff --git a/src/IFoxCAD.Cad.Shared/ExtensionMethod/Curve2dEx.cs b/src/IFoxCAD.Cad.Shared/ExtensionMethod/Curve2dEx.cs index c1c06c6..765bf4d 100644 --- a/src/IFoxCAD.Cad.Shared/ExtensionMethod/Curve2dEx.cs +++ b/src/IFoxCAD.Cad.Shared/ExtensionMethod/Curve2dEx.cs @@ -246,7 +246,6 @@ public static Line ToCurve(this LineSegment2d ls2d) new Line( new Point3d(plane, ls2d.StartPoint), new Point3d(plane, ls2d.EndPoint)); - } #endregion LineSegment2d @@ -275,23 +274,17 @@ public static Spline ToCurve(this NurbCurve2d nc2d) { int i; Plane plane = new(); - Point3dCollection ctlpnts = new(); + using Point3dCollection ctlpnts = new(); for (i = 0; i < nc2d.NumControlPoints; i++) - { ctlpnts.Add(new Point3d(plane, nc2d.GetControlPointAt(i))); - } DoubleCollection knots = new(); foreach (double knot in nc2d.Knots) - { knots.Add(knot); - } DoubleCollection weights = new(); for (i = 0; i < nc2d.NumWeights; i++) - { weights.Add(nc2d.GetWeightAt(i)); - } NurbCurve2dData ncdata = nc2d.DefinitionData; @@ -309,4 +302,4 @@ public static Spline ToCurve(this NurbCurve2d nc2d) } #endregion NurbCurve2d -} +} \ No newline at end of file diff --git a/src/IFoxCAD.Cad.Shared/ExtensionMethod/Curve3dEx.cs b/src/IFoxCAD.Cad.Shared/ExtensionMethod/Curve3dEx.cs index f5ebdab..2ed17cf 100644 --- a/src/IFoxCAD.Cad.Shared/ExtensionMethod/Curve3dEx.cs +++ b/src/IFoxCAD.Cad.Shared/ExtensionMethod/Curve3dEx.cs @@ -536,12 +536,10 @@ public static Spline ToCurve(this NurbCurve3d nc3d) /// 实体类三维多段线 public static Polyline3d ToCurve(this PolylineCurve3d pl3d) { - Point3dCollection pnts = new(); + using Point3dCollection pnts = new(); for (int i = 0; i < pl3d.NumberOfControlPoints; i++) - { pnts.Add(pl3d.ControlPointAt(i)); - } bool closed = false; int n = pnts.Count - 1; @@ -554,4 +552,4 @@ public static Polyline3d ToCurve(this PolylineCurve3d pl3d) } #endregion PolylineCurve3d -} +} \ No newline at end of file diff --git a/src/IFoxCAD.Cad.Shared/ExtensionMethod/CurveEx.cs b/src/IFoxCAD.Cad.Shared/ExtensionMethod/CurveEx.cs index c55b289..cc21fd2 100644 --- a/src/IFoxCAD.Cad.Shared/ExtensionMethod/CurveEx.cs +++ b/src/IFoxCAD.Cad.Shared/ExtensionMethod/CurveEx.cs @@ -67,10 +67,9 @@ public static IEnumerable GetSplitCurves(this Curve curve, IEnumerable(); + + using var pts = new Point3dCollection(points.ToArray()); + return curve.GetSplitCurves(pts).Cast(); } /// @@ -89,13 +88,15 @@ public static IEnumerable GetSplitCurves(this Curve curve, IEnumerable curve.GetParameterAtPoint( - curve.GetClosestPointTo(point, false))); - return - curve - .GetSplitCurves(new Point3dCollection(points.ToArray())) - .Cast(); + points = points.OrderBy(point => { + var pt = curve.GetClosestPointTo(point, false); + return curve.GetParameterAtPoint(pt); + }); + + using Point3dCollection pts = new(points.ToArray()); + return curve.GetSplitCurves(pts).Cast(); } /// @@ -512,11 +513,9 @@ public static NurbCurve3d ToCurve3d(this Spline spl) /// 三维ge多段线 public static PolylineCurve3d ToPolylineCurve3d(this Polyline2d pl) { - Point3dCollection pnts = new(); + using Point3dCollection pnts = new(); foreach (Vertex2d ver in pl) - { pnts.Add(ver.Position); - } return new PolylineCurve3d(pnts); } @@ -555,7 +554,7 @@ public static NurbCurve3d ToNurbCurve3d(this Polyline3d pl3d) /// 三维ge多段线 public static PolylineCurve3d ToPolylineCurve3d(this Polyline3d pl) { - Point3dCollection pnts = new(); + using Point3dCollection pnts = new(); foreach (ObjectId id in pl) { var ver = id.GetObject(OpenMode.ForRead); diff --git a/src/IFoxCAD.Cad.Shared/ExtensionMethod/DBDictionaryEx.cs b/src/IFoxCAD.Cad.Shared/ExtensionMethod/DBDictionaryEx.cs index f6e6c65..31043b2 100644 --- a/src/IFoxCAD.Cad.Shared/ExtensionMethod/DBDictionaryEx.cs +++ b/src/IFoxCAD.Cad.Shared/ExtensionMethod/DBDictionaryEx.cs @@ -296,7 +296,8 @@ public static ObjectId AddGroup(this DBDictionary dict, string name, IEnumerable if (dict.Contains(name)) return ObjectId.Null; - return dict.AddGroup(name, new ObjectIdCollection(ids.ToArray())); + using ObjectIdCollection idc = new(ids.ToArray());//TODO 需要using吗? 这里是堆交给cad管理了?还是它将被cad克隆? 等待测试 + return dict.AddGroup(name, idc); } diff --git a/src/IFoxCAD.Cad.Shared/ExtensionMethod/DBTransEx.cs b/src/IFoxCAD.Cad.Shared/ExtensionMethod/DBTransEx.cs index d238252..9af63bb 100644 --- a/src/IFoxCAD.Cad.Shared/ExtensionMethod/DBTransEx.cs +++ b/src/IFoxCAD.Cad.Shared/ExtensionMethod/DBTransEx.cs @@ -5,7 +5,6 @@ namespace IFoxCAD.Cad; #if lack_test public static class DBTransEx { - /* * 0x01 * db.Purge(ids)是获取未硬引用(无引用?)的对象,也就可以删除的. @@ -30,7 +29,7 @@ public static class DBTransEx /// 排除外部参照:默认true,为false时候会令图层全部显示再清理,包括冻结 public static void Purge(this DBTrans tr, SymModes sym = SymModes.All, bool excludeXref = true) { - var ids = new ObjectIdCollection(); + using ObjectIdCollection ids = new(); var db = tr.Database; if ((sym & SymModes.BlockTable) == SymModes.BlockTable) @@ -89,4 +88,4 @@ static void GetAllIds(DBTrans tr, } } } -#endif +#endif \ No newline at end of file diff --git a/src/IFoxCAD.Cad.Shared/ExtensionMethod/EntityEx.cs b/src/IFoxCAD.Cad.Shared/ExtensionMethod/EntityEx.cs index 374f376..eaee7f0 100644 --- a/src/IFoxCAD.Cad.Shared/ExtensionMethod/EntityEx.cs +++ b/src/IFoxCAD.Cad.Shared/ExtensionMethod/EntityEx.cs @@ -391,11 +391,17 @@ public static void ClipBlockRef(this BlockReference bref, Point3d pt1, Point3d p new Point2d(Math.Max(pt1.X, pt2.X), Math.Max(pt1.Y, pt2.Y)) }; - SpatialFilterDefinition sfd = new(pts, Vector3d.ZAxis, 0.0, 0.0, 0.0, true); - using SpatialFilter sf = new() { Definition = sfd }; - var dict = bref.GetXDictionary()!.GetSubDictionary(true, new string[] { filterDictName })!; + using SpatialFilter sf = new() + { + Definition = new(pts, Vector3d.ZAxis, 0.0, 0.0, 0.0, true) + }; + var dict = bref.GetXDictionary()! + .GetSubDictionary(true, new string[] { filterDictName })!; dict.SetAt(spatialName, sf); // SetToDictionary(dict, spatialName, sf); +#if !acad + pts.Dispose(); +#endif } #endregion diff --git a/src/IFoxCAD.Cad.Shared/ExtensionMethod/PointEx.cs b/src/IFoxCAD.Cad.Shared/ExtensionMethod/PointEx.cs index f65c24e..6f02e77 100644 --- a/src/IFoxCAD.Cad.Shared/ExtensionMethod/PointEx.cs +++ b/src/IFoxCAD.Cad.Shared/ExtensionMethod/PointEx.cs @@ -2,7 +2,6 @@ namespace IFoxCAD.Cad; public static class PointEx { - /// /// 获取点的hash字符串,同时可以作为pt的字符串表示 /// @@ -95,13 +94,13 @@ public static double GetArcBulge(this Point2d arc1, Point2d arc2, Point2d arc3, /// /// 首尾相连 /// - public static Point2dCollection End2End(this Point2dCollection ptcol) + public static void End2End(this Point2dCollection ptcol) { if (ptcol == null) throw new ArgumentNullException(nameof(ptcol)); if (ptcol.Count == 0 || ptcol[0].Equals(ptcol[^1]))// 首尾相同直接返回 - return ptcol; + return; // 首尾不同,去加一个到最后 var lst = new Point2d[ptcol.Count + 1]; @@ -109,18 +108,19 @@ public static Point2dCollection End2End(this Point2dCollection ptcol) lst[i] = ptcol[i]; lst[^1] = lst[0]; - return new(lst); + ptcol.Clear(); + ptcol.AddRange(lst); } /// /// 首尾相连 /// - public static Point3dCollection End2End(this Point3dCollection ptcol) + public static void End2End(this Point3dCollection ptcol) { if (ptcol == null) throw new ArgumentNullException(nameof(ptcol)); if (ptcol.Count == 0 || ptcol[0].Equals(ptcol[^1]))// 首尾相同直接返回 - return ptcol; + return; // 首尾不同,去加一个到最后 var lst = new Point3d[ptcol.Count + 1]; @@ -128,7 +128,9 @@ public static Point3dCollection End2End(this Point3dCollection ptcol) lst[i] = ptcol[i]; lst[^1] = lst[0]; - return new(lst); + ptcol.Clear(); + for (int i = 0; i < lst.Length; i++) + ptcol.Add(lst[i]); } #endregion } \ No newline at end of file diff --git a/src/IFoxCAD.Cad.Shared/ExtensionMethod/SymbolTableRecordEx.cs b/src/IFoxCAD.Cad.Shared/ExtensionMethod/SymbolTableRecordEx.cs index a0c215a..48f464b 100644 --- a/src/IFoxCAD.Cad.Shared/ExtensionMethod/SymbolTableRecordEx.cs +++ b/src/IFoxCAD.Cad.Shared/ExtensionMethod/SymbolTableRecordEx.cs @@ -22,19 +22,18 @@ public static class SymbolTableRecordEx /// /// /// 图元id集合,注意所有成员都要在同一个空间中 - /// 克隆后的id词典 - public static IdMapping DeepCloneEx(this BlockTableRecord btr, ObjectIdCollection objIds) + /// 返回克隆后的id词典 + public static void DeepCloneEx(this BlockTableRecord btr, ObjectIdCollection objIds, IdMapping maoOut) { if (objIds is null || objIds.Count == 0) throw new ArgumentNullException(nameof(objIds)); var db = objIds[0].Database; - IdMapping mapping = new(); using (btr.ForWrite()) { try { - db.DeepCloneObjects(objIds, btr.ObjectId, mapping, false); + db.DeepCloneObjects(objIds, btr.ObjectId, maoOut, false); // 不在此提取,为了此函数被高频调用 // 获取克隆键值对(旧块名,新块名) @@ -47,7 +46,6 @@ public static IdMapping DeepCloneEx(this BlockTableRecord btr, ObjectIdCollectio e.WriteLog("深度克隆出错了"); } } - return mapping; } /// diff --git a/src/IFoxCAD.Cad.Shared/ExtensionMethod/XrefEx.cs b/src/IFoxCAD.Cad.Shared/ExtensionMethod/XrefEx.cs index cb57d30..9ecdaa8 100644 --- a/src/IFoxCAD.Cad.Shared/ExtensionMethod/XrefEx.cs +++ b/src/IFoxCAD.Cad.Shared/ExtensionMethod/XrefEx.cs @@ -101,21 +101,24 @@ public void Bind() public void Detach() { - var xrefIds = GetAllXrefNode(); + using ObjectIdCollection xrefIds = new(); + GetAllXrefNode(xrefIds); foreach (ObjectId id in xrefIds) _tr.Database.DetachXref(id); } public void Reload() { - var xrefIds = GetAllXrefNode(); + using ObjectIdCollection xrefIds = new(); + GetAllXrefNode(xrefIds); if (xrefIds.Count > 0) _tr.Database.ReloadXrefs(xrefIds); } public void Unload() { - var xrefIds = GetAllXrefNode(); + using ObjectIdCollection xrefIds = new(); + GetAllXrefNode(xrefIds); if (xrefIds.Count > 0) _tr.Database.UnloadXrefs(xrefIds); } @@ -125,16 +128,15 @@ public void Unload() /// /// 获取参照 /// - /// 全部参照id - ObjectIdCollection GetAllXrefNode() + /// 返回全部参照id + void GetAllXrefNode(ObjectIdCollection xrefIds) { // 储存要处理的参照id - var xrefIds = new ObjectIdCollection(); + //var xrefIds = new ObjectIdCollection(); XrefNodeForEach((xNodeName, xNodeId, xNodeStatus, xNodeIsNested) => { if (XrefNamesContains(xNodeName)) xrefIds.Add(xNodeId); }); - return xrefIds; } bool XrefNamesContains(string xNodeName) @@ -186,12 +188,13 @@ static void AddedxbindIds(ObjectIdCollection xbindXrefsIds, }, checkIdOk: true); } - ObjectIdCollection GetXBindIds() + + void GetXBindIds(ObjectIdCollection xbindIds) { // xbind // 0x01 它是用来绑其他符号表,绑块表会有异常 // 0x02 集合若有问题,就会出现eWrongObjectType - var xbindIds = new ObjectIdCollection(); + //var xbindIds = new ObjectIdCollection(); // 起初测试是将九大符号表记录均加入的,但经实测不行...(为什么?存疑) #region Option1 @@ -221,14 +224,12 @@ ObjectIdCollection GetXBindIds() if ((SymModesBind & SymModes.ViewportTable) == SymModes.ViewportTable) AddedxbindIds(xbindIds, _tr.ViewportTable); #endregion - - return xbindIds; } - ObjectIdCollection GetBindIds() + void GetBindIds(ObjectIdCollection bindIds) { // bind 只绑块表 - var bindIds = new ObjectIdCollection(); + //var bindIds = new ObjectIdCollection(); _tr.BlockTable.ForEach(btr => { if (btr.IsLayout) @@ -238,8 +239,6 @@ ObjectIdCollection GetBindIds() if (btr.IsFromExternalReference && btr.IsResolved) bindIds.Add(btr.ObjectId); }, checkIdOk: true); - - return bindIds; } /// @@ -312,11 +311,16 @@ void DoubleBind() // 重载:嵌套参照已卸载了,需要重载之后才能进行绑定 var keys = nested.Keys; if (keys.Count > 0) - _tr.Database.ReloadXrefs(new ObjectIdCollection(keys.ToArray())); + { + using ObjectIdCollection idc = new(keys.ToArray()); + _tr.Database.ReloadXrefs(idc); + } // 绑定:切勿交换,否则会绑定无效 - var bindIds = GetBindIds(); - var xbindIds = GetXBindIds(); + using ObjectIdCollection bindIds = new(); + using ObjectIdCollection xbindIds = new(); + GetBindIds(bindIds); + GetXBindIds(xbindIds); if (xbindIds.Count > 0) _tr.Database.XBindXrefs(xbindIds, BindOrInsert); if (bindIds.Count > 0) diff --git "a/src/IFoxCAD.Cad.Shared/ExtensionMethod/\346\226\260\345\273\272\345\241\253\345\205\205/HatchConverter.cs" "b/src/IFoxCAD.Cad.Shared/ExtensionMethod/\346\226\260\345\273\272\345\241\253\345\205\205/HatchConverter.cs" index 62389db..e38b8e0 100644 --- "a/src/IFoxCAD.Cad.Shared/ExtensionMethod/\346\226\260\345\273\272\345\241\253\345\205\205/HatchConverter.cs" +++ "b/src/IFoxCAD.Cad.Shared/ExtensionMethod/\346\226\260\345\273\272\345\241\253\345\205\205/HatchConverter.cs" @@ -1,4 +1,6 @@ namespace IFoxCAD.Cad; + +using System.Data; using PointV = Point2d; /// @@ -313,8 +315,10 @@ public ObjectId CreateBoundarysAndHatchToMsPs(BlockTableRecord btrOfAddEntitySpa * 那么它的平移后的基点在哪里呢? */ - var newHatchId = btrOfAddEntitySpace.DeepCloneEx( - new ObjectIdCollection(new ObjectId[] { OldHatchId })).GetValues()[0]; + using ObjectIdCollection idc = new(new ObjectId[] { OldHatchId }); + using IdMapping map = new(); + btrOfAddEntitySpace.DeepCloneEx(idc, map); + var newHatchId = map.GetValues()[0]; trans ??= DBTrans.Top.Transaction; bool openErased = false; @@ -344,7 +348,7 @@ void ResetBoundary(Hatch hatch, hatch.Associative = boundaryAssociative; - var obIds = new ObjectIdCollection(); + using ObjectIdCollection obIds = new(); for (int i = 0; i < BoundaryIds.Count; i++) { obIds.Clear(); diff --git "a/src/IFoxCAD.Cad.Shared/ExtensionMethod/\346\226\260\345\273\272\345\241\253\345\205\205/HatchInfo.cs" "b/src/IFoxCAD.Cad.Shared/ExtensionMethod/\346\226\260\345\273\272\345\241\253\345\205\205/HatchInfo.cs" index d753460..9f4e90b 100644 --- "a/src/IFoxCAD.Cad.Shared/ExtensionMethod/\346\226\260\345\273\272\345\241\253\345\205\205/HatchInfo.cs" +++ "b/src/IFoxCAD.Cad.Shared/ExtensionMethod/\346\226\260\345\273\272\345\241\253\345\205\205/HatchInfo.cs" @@ -239,7 +239,7 @@ public HatchInfo EraseBoundary() void AppendLoop(IEnumerable boundaryIds, HatchLoopTypes hatchLoopTypes = HatchLoopTypes.Default) { - var obIds = new ObjectIdCollection(); + using ObjectIdCollection obIds = new(); // 边界是闭合的,而且已经加入数据库 // 填充闭合环类型.最外面 foreach (var border in boundaryIds) @@ -248,7 +248,6 @@ void AppendLoop(IEnumerable boundaryIds, obIds.Add(border); _hatch.AppendLoop(hatchLoopTypes, obIds); } - obIds.Dispose(); } /// @@ -267,13 +266,13 @@ public HatchInfo AppendLoop(Point2dCollection pts, if (pts == null) throw new ArgumentNullException(nameof(pts)); - var ptsEnd2End = pts.End2End(); + pts.End2End(); #if NET35 - _boundaryIds.Add(CreateAddBoundary(ptsEnd2End, bluges, btrOfAddEntitySpace)); + _boundaryIds.Add(CreateAddBoundary(pts, bluges, btrOfAddEntitySpace)); #else // 2011新增API,可以不生成图元的情况下加入边界, // 通过这里进入的话,边界 _boundaryIds 是空的,那么 Build() 时候就需要过滤空的 - _hatch.AppendLoop(hatchLoopTypes, ptsEnd2End, bluges); + _hatch.AppendLoop(hatchLoopTypes, pts, bluges); #endif return this; } diff --git a/src/IFoxCAD.Cad.Shared/Runtime/SymbolTable.cs b/src/IFoxCAD.Cad.Shared/Runtime/SymbolTable.cs index a33c167..7374ae4 100644 --- a/src/IFoxCAD.Cad.Shared/Runtime/SymbolTable.cs +++ b/src/IFoxCAD.Cad.Shared/Runtime/SymbolTable.cs @@ -269,17 +269,15 @@ public ObjectId GetRecordFrom(SymbolTable table, string name, b if ((has && over) || !has) { ObjectId id = table[name]; - using IdMapping idm = new(); - using (ObjectIdCollection ids = new() { id }) - { - table.Database. - WblockCloneObjects(ids, - CurrentSymbolTable.Id, - idm, - DuplicateRecordCloning.Replace, - false); - } - rid = idm[id].Value; + using IdMapping map = new(); + using ObjectIdCollection ids = new() { id }; + table.Database.WblockCloneObjects( + ids, + CurrentSymbolTable.Id, + map, + DuplicateRecordCloning.Replace, + false); + rid = map[id].Value; } return rid; } diff --git a/tests/TestShared/TestDwgFilerEx.cs b/tests/TestShared/TestDwgFilerEx.cs index c2e4118..3bd033d 100644 --- a/tests/TestShared/TestDwgFilerEx.cs +++ b/tests/TestShared/TestDwgFilerEx.cs @@ -117,9 +117,11 @@ public static void CmdTest_TextOut() bText.ObjectId }; // 克隆到目标块表内 - var newObjs = tr.CurrentSpace - .DeepCloneEx(new ObjectIdCollection(sIds.ToArray())); - var newTexts = newObjs.GetValues().GetObject(); + using ObjectIdCollection bindIds = new(sIds.ToArray()); + using IdMapping map = new(); + + tr.CurrentSpace.DeepCloneEx(bindIds, map); + var newTexts = map.GetValues().GetObject(); newTexts.ForEach(nText => { if (nText == null) return; diff --git a/tests/TestShared/testblock.cs b/tests/TestShared/testblock.cs index 8581eba..2378f69 100644 --- a/tests/TestShared/testblock.cs +++ b/tests/TestShared/testblock.cs @@ -432,7 +432,7 @@ public void TestWblock() MessageForAdding = "选择对象" }; var ss = Env.Editor.GetSelection(opts).Value; - var ids = new ObjectIdCollection(ss.GetObjectIds()); + using ObjectIdCollection ids = new(ss.GetObjectIds()); var db = curdb.Wblock(ids, Point3d.Origin); db.SaveAs(@"c:\test.dwg", DwgVersion.Current); } -- Gitee From 4c9b42bac68cd933aaa9aa17f28e4bf742f40210 Mon Sep 17 00:00:00 2001 From: liuqihong <540762622@qq.com> Date: Tue, 27 Sep 2022 20:07:46 +0800 Subject: [PATCH 530/675] =?UTF-8?q?=E6=8F=90=E4=BE=9B=E5=B1=8F=E8=94=BD?= =?UTF-8?q?=E5=85=83=E7=BB=84=E7=9A=84=E6=96=B9=E5=BC=8F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 31 +++++++++++++++++++ src/IFoxCAD.Basal/{ => General}/ArrayEx.cs | 0 src/IFoxCAD.Basal/{ => General}/DictEx.cs | 0 src/IFoxCAD.Basal/{ => General}/EnumEx.cs | 0 .../{ => General}/GlobalUsings.cs | 0 .../{ => General}/LinkedHashMap.cs | 0 .../{ => General}/LinkedHashSet.cs | 0 src/IFoxCAD.Basal/{ => General}/LinqEx.cs | 0 src/IFoxCAD.Basal/{ => General}/ListEx.cs | 0 src/IFoxCAD.Basal/{ => General}/LoopList.cs | 0 src/IFoxCAD.Basal/{ => General}/LoopState.cs | 0 src/IFoxCAD.Basal/{ => General}/RandomEx.cs | 0 12 files changed, 31 insertions(+) rename src/IFoxCAD.Basal/{ => General}/ArrayEx.cs (100%) rename src/IFoxCAD.Basal/{ => General}/DictEx.cs (100%) rename src/IFoxCAD.Basal/{ => General}/EnumEx.cs (100%) rename src/IFoxCAD.Basal/{ => General}/GlobalUsings.cs (100%) rename src/IFoxCAD.Basal/{ => General}/LinkedHashMap.cs (100%) rename src/IFoxCAD.Basal/{ => General}/LinkedHashSet.cs (100%) rename src/IFoxCAD.Basal/{ => General}/LinqEx.cs (100%) rename src/IFoxCAD.Basal/{ => General}/ListEx.cs (100%) rename src/IFoxCAD.Basal/{ => General}/LoopList.cs (100%) rename src/IFoxCAD.Basal/{ => General}/LoopState.cs (100%) rename src/IFoxCAD.Basal/{ => General}/RandomEx.cs (100%) diff --git a/README.md b/README.md index 6c94336..b597a4d 100644 --- a/README.md +++ b/README.md @@ -197,3 +197,34 @@ IFox的[jing分支](https://gitee.com/inspirefunction/ifoxcad/tree/jing/)是一 8. 未完待续。。。。 + + + + +#### 屏蔽IFox.Basal的元组功能 + +由于c#在每个版本提供的元组功能不一样,所以IFox内置了元组功能,但是内置元组又会引起某些用户的工程冲突. + +因此您需要制作一个影子工程: + +1. 您需要具备使用git子模块的能力,引用jing分支中的源码. + +2. 子模块是为了保证可以不破坏IFox项目的实现更新. + +3. 新建 XX.Basal 文件夹 + +4. 复制 IFox.Basal 的 .csproj 到上一步的文件夹,将其改为你的 XX.Basal + +5. 修改上一步的. csproj 引入nuget: `System.ValueTuple` + +6. 利用引用链接的方式进行引用 IFox.Basal 的文件,具体引用的依照自己喜欢,不引用CLS就等于屏蔽了元组 + + ```xml + + + + + ``` + +这个方法可以把 XX.Basal 独立在IFox子模块外,令git pull仍然有效,并且 IFox.Basal 不做大更改的时候影子工程更新幅度非常少. + diff --git a/src/IFoxCAD.Basal/ArrayEx.cs b/src/IFoxCAD.Basal/General/ArrayEx.cs similarity index 100% rename from src/IFoxCAD.Basal/ArrayEx.cs rename to src/IFoxCAD.Basal/General/ArrayEx.cs diff --git a/src/IFoxCAD.Basal/DictEx.cs b/src/IFoxCAD.Basal/General/DictEx.cs similarity index 100% rename from src/IFoxCAD.Basal/DictEx.cs rename to src/IFoxCAD.Basal/General/DictEx.cs diff --git a/src/IFoxCAD.Basal/EnumEx.cs b/src/IFoxCAD.Basal/General/EnumEx.cs similarity index 100% rename from src/IFoxCAD.Basal/EnumEx.cs rename to src/IFoxCAD.Basal/General/EnumEx.cs diff --git a/src/IFoxCAD.Basal/GlobalUsings.cs b/src/IFoxCAD.Basal/General/GlobalUsings.cs similarity index 100% rename from src/IFoxCAD.Basal/GlobalUsings.cs rename to src/IFoxCAD.Basal/General/GlobalUsings.cs diff --git a/src/IFoxCAD.Basal/LinkedHashMap.cs b/src/IFoxCAD.Basal/General/LinkedHashMap.cs similarity index 100% rename from src/IFoxCAD.Basal/LinkedHashMap.cs rename to src/IFoxCAD.Basal/General/LinkedHashMap.cs diff --git a/src/IFoxCAD.Basal/LinkedHashSet.cs b/src/IFoxCAD.Basal/General/LinkedHashSet.cs similarity index 100% rename from src/IFoxCAD.Basal/LinkedHashSet.cs rename to src/IFoxCAD.Basal/General/LinkedHashSet.cs diff --git a/src/IFoxCAD.Basal/LinqEx.cs b/src/IFoxCAD.Basal/General/LinqEx.cs similarity index 100% rename from src/IFoxCAD.Basal/LinqEx.cs rename to src/IFoxCAD.Basal/General/LinqEx.cs diff --git a/src/IFoxCAD.Basal/ListEx.cs b/src/IFoxCAD.Basal/General/ListEx.cs similarity index 100% rename from src/IFoxCAD.Basal/ListEx.cs rename to src/IFoxCAD.Basal/General/ListEx.cs diff --git a/src/IFoxCAD.Basal/LoopList.cs b/src/IFoxCAD.Basal/General/LoopList.cs similarity index 100% rename from src/IFoxCAD.Basal/LoopList.cs rename to src/IFoxCAD.Basal/General/LoopList.cs diff --git a/src/IFoxCAD.Basal/LoopState.cs b/src/IFoxCAD.Basal/General/LoopState.cs similarity index 100% rename from src/IFoxCAD.Basal/LoopState.cs rename to src/IFoxCAD.Basal/General/LoopState.cs diff --git a/src/IFoxCAD.Basal/RandomEx.cs b/src/IFoxCAD.Basal/General/RandomEx.cs similarity index 100% rename from src/IFoxCAD.Basal/RandomEx.cs rename to src/IFoxCAD.Basal/General/RandomEx.cs -- Gitee From c37f343004a3dc2a9971afe3bdcdc3b0115486a5 Mon Sep 17 00:00:00 2001 From: liuqihong <540762622@qq.com> Date: Tue, 27 Sep 2022 14:59:21 +0000 Subject: [PATCH 531/675] update README.md. Signed-off-by: liuqihong <540762622@qq.com> --- README.md | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/README.md b/README.md index b597a4d..2c589c9 100644 --- a/README.md +++ b/README.md @@ -208,16 +208,15 @@ IFox的[jing分支](https://gitee.com/inspirefunction/ifoxcad/tree/jing/)是一 因此您需要制作一个影子工程: 1. 您需要具备使用git子模块的能力,引用jing分支中的源码. + 子模块是为了保证您不修改IFox项目,因为你需要定期`git pull`更新组织提供的内容. -2. 子模块是为了保证可以不破坏IFox项目的实现更新. +2. 新建 XX.Basal 文件夹 -3. 新建 XX.Basal 文件夹 +3. 复制 IFox.Basal 的 .csproj 到上一步的文件夹,将其改为你的 XX.Basal -4. 复制 IFox.Basal 的 .csproj 到上一步的文件夹,将其改为你的 XX.Basal +4. 修改上一步的 .csproj 引入nuget: `System.ValueTuple` -5. 修改上一步的. csproj 引入nuget: `System.ValueTuple` - -6. 利用引用链接的方式进行引用 IFox.Basal 的文件,具体引用的依照自己喜欢,不引用CLS就等于屏蔽了元组 +5. 利用引用链接的方式进行引用 IFox.Basal 的文件,具体引用的依照自己喜欢,不引用CLS就等于屏蔽了元组 ```xml @@ -226,5 +225,5 @@ IFox的[jing分支](https://gitee.com/inspirefunction/ifoxcad/tree/jing/)是一 ``` -这个方法可以把 XX.Basal 独立在IFox子模块外,令git pull仍然有效,并且 IFox.Basal 不做大更改的时候影子工程更新幅度非常少. +这个方法便可以把 XX.Basal 独立在IFox项目外,令`git pull`仍然有效,并且 IFox.Basal 不做大更改的时候,影子工程更新幅度非常少. -- Gitee From 449d188cc221599d27c8edca9d6467640b972177 Mon Sep 17 00:00:00 2001 From: liuqihong <540762622@qq.com> Date: Tue, 27 Sep 2022 15:27:34 +0000 Subject: [PATCH 532/675] update README.md. Signed-off-by: liuqihong <540762622@qq.com> --- README.md | 21 +++++++++++++++------ 1 file changed, 15 insertions(+), 6 deletions(-) diff --git a/README.md b/README.md index 2c589c9..501f74d 100644 --- a/README.md +++ b/README.md @@ -203,20 +203,23 @@ IFox的[jing分支](https://gitee.com/inspirefunction/ifoxcad/tree/jing/)是一 #### 屏蔽IFox.Basal的元组功能 -由于c#在每个版本提供的元组功能不一样,所以IFox内置了元组功能,但是内置元组又会引起某些用户的工程冲突. +由于c#在每个版本提供的元组功能不一样(有的中间版本缺少),所以IFox内置了元组功能,但是内置元组又会引起某些用户的工程冲突. 因此您需要制作一个影子工程: 1. 您需要具备使用git子模块的能力,引用jing分支中的源码. + 子模块是为了保证您不修改IFox项目,因为你需要定期`git pull`更新组织提供的内容. -2. 新建 XX.Basal 文件夹 + 熟悉子模块之后,你需要把IFox项目一个个加入你的解决方案, + + 除了本次主角IFox.Basal. -3. 复制 IFox.Basal 的 .csproj 到上一步的文件夹,将其改为你的 XX.Basal +2. 在子模块文件夹外新建 IFox.Basal(影) 文件夹 -4. 修改上一步的 .csproj 引入nuget: `System.ValueTuple` +3. 复制 IFox.Basal(本) 的 .csproj 到上一步的文件夹. -5. 利用引用链接的方式进行引用 IFox.Basal 的文件,具体引用的依照自己喜欢,不引用CLS就等于屏蔽了元组 +4. 修改 .csproj(影) 利用引用链接的方式进行引用 IFox.Basal(本) 的文件,不引用CLS就等于屏蔽了元组 ```xml @@ -225,5 +228,11 @@ IFox的[jing分支](https://gitee.com/inspirefunction/ifoxcad/tree/jing/)是一 ``` -这个方法便可以把 XX.Basal 独立在IFox项目外,令`git pull`仍然有效,并且 IFox.Basal 不做大更改的时候,影子工程更新幅度非常少. +5. 修改 .csproj(影) 引入微软提供的元组 nuget: `System.ValueTuple` (或者你喜欢的) + +6. 解决方案加入 .csproj(影) 之后被解决方案的其他成员进行引用. + +这个方法便可以把 影子工程 独立在IFox项目外,令`git pull`仍然有效, + +并且 本体工程 不做大更改的时候,影子工程更新幅度非常少,也多亏csproj改版了,不然也没有这个骚操作. -- Gitee From dbea064ec1932bbce55c76822357b7d9faf823fd Mon Sep 17 00:00:00 2001 From: liuqihong <540762622@qq.com> Date: Tue, 27 Sep 2022 15:30:23 +0000 Subject: [PATCH 533/675] update README.md. Signed-off-by: liuqihong <540762622@qq.com> --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 501f74d..b2ff7d4 100644 --- a/README.md +++ b/README.md @@ -230,7 +230,7 @@ IFox的[jing分支](https://gitee.com/inspirefunction/ifoxcad/tree/jing/)是一 5. 修改 .csproj(影) 引入微软提供的元组 nuget: `System.ValueTuple` (或者你喜欢的) -6. 解决方案加入 .csproj(影) 之后被解决方案的其他成员进行引用. +6. 解决方案加入 .csproj(影) 之后被内部其他项目引用. 这个方法便可以把 影子工程 独立在IFox项目外,令`git pull`仍然有效, -- Gitee From c49004ace0424f0235b3d4145b43591c3563b5ca Mon Sep 17 00:00:00 2001 From: liuqihong <540762622@qq.com> Date: Tue, 27 Sep 2022 20:08:45 +0800 Subject: [PATCH 534/675] =?UTF-8?q?=E5=85=B3=E4=BA=8Ewmf=E8=AF=BB=E5=86=99?= =?UTF-8?q?=E7=9A=84=E6=B5=8B=E8=AF=95=200x01=20=E5=89=AA=E8=B4=B4?= =?UTF-8?q?=E6=9D=BF=E5=86=99=E5=85=A5cad=E7=9A=84wmf=E5=A4=B1=E8=B4=A5=20?= =?UTF-8?q?0x02=20=E5=89=AA=E8=B4=B4=E6=9D=BF=E5=86=99=E5=85=A5csharp?= =?UTF-8?q?=E7=94=9F=E6=88=90=E7=9A=84=E6=88=90=E5=8A=9F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/IFoxCAD.Cad.Shared/Copyclip/BitmapTool.cs | 11 +- src/IFoxCAD.Cad.Shared/Copyclip/EmfTool.cs | 211 +++++++++++++- .../Copyclip/TagClipboardInfo.cs | 104 ++++++- src/IFoxCAD.Cad.Shared/Copyclip/WindowsAPI.cs | 105 ++++++- .../ExtensionMethod/EditorEx.cs | 6 +- tests/TestConsole/Info.cs | 6 +- tests/TestConsole/Program.cs | 2 + tests/TestShared/Copyclip.cs | 260 ++++++++++++------ 8 files changed, 579 insertions(+), 126 deletions(-) diff --git a/src/IFoxCAD.Cad.Shared/Copyclip/BitmapTool.cs b/src/IFoxCAD.Cad.Shared/Copyclip/BitmapTool.cs index 1978b2a..cf3f0a8 100644 --- a/src/IFoxCAD.Cad.Shared/Copyclip/BitmapTool.cs +++ b/src/IFoxCAD.Cad.Shared/Copyclip/BitmapTool.cs @@ -33,15 +33,6 @@ public class BitmapTool [DllImport("gdi32.dll", EntryPoint = "CreateCompatibleDC", SetLastError = true)] public static extern IntPtr CreateCompatibleDC([In] IntPtr hdc); - /// - /// 获取窗口客户区的大小,客户区为窗口中除标题栏,菜单栏之外的地方 - /// - /// - /// - /// - [DllImport("user32.dll", CharSet = CharSet.Auto, EntryPoint = "GetClientRect")] - public static extern bool GetClientRect(IntPtr hwnd, out IntRect lpRect); - /// /// Creates a bitmap compatible with the device that is associated with the specified device context. /// @@ -139,7 +130,7 @@ public static void CaptureWndImage(IntPtr hWnd, Action action) if (hMemDC == IntPtr.Zero) return; - GetClientRect(hWnd, out IntRect rcClient); + WindowsAPI.GetClientRect(hWnd, out IntRect rcClient); int width = rcClient.Right - rcClient.Left; int height = rcClient.Bottom - rcClient.Top; diff --git a/src/IFoxCAD.Cad.Shared/Copyclip/EmfTool.cs b/src/IFoxCAD.Cad.Shared/Copyclip/EmfTool.cs index b635936..67ae270 100644 --- a/src/IFoxCAD.Cad.Shared/Copyclip/EmfTool.cs +++ b/src/IFoxCAD.Cad.Shared/Copyclip/EmfTool.cs @@ -1,9 +1,12 @@ namespace IFoxCAD.Cad; using System; +using System.Drawing.Drawing2D; +using System.Drawing; using System.Drawing.Imaging; +using System.Diagnostics; -public class EmfTool +public static class EmfTool { /// /// 获取矢量图的byte @@ -43,25 +46,25 @@ public class EmfTool public static extern IntPtr CreateEnhMetaFile(IntPtr hdcRef, string szFilename, IntRect lpRect, string lpDescription); /// - /// Copy EMF to file + /// EMF保存到文件或者路径 /// - /// Handle to EMF - /// File + /// EMF要复制的增强型图元文件的句柄 + /// 指向目标文件名称的指针,为NULL则将源图元文件复制到内存中 /// [DllImport("gdi32.dll")] - static extern IntPtr CopyEnhMetaFile(IntPtr hemfSrc, string lpszFile); + public static extern IntPtr CopyEnhMetaFile(IntPtr hemfSrc, string? lpszFile); /// /// 矢量图保存 /// /// /// - public static void SaveMetaFile(Metafile file, string emfName) + public static void SaveMetaFile(this Metafile file, string emfName) { //MetafileHeader metafileHeader = file.GetMetafileHeader(); //这句话可要可不要 - IntPtr iptrMetafileHandle = file.GetHenhmetafile(); - CopyEnhMetaFile(iptrMetafileHandle, emfName); - DeleteEnhMetaFile(iptrMetafileHandle); + IntPtr h = file.GetHenhmetafile(); + CopyEnhMetaFile(h, emfName); + DeleteEnhMetaFile(h); } /// @@ -69,7 +72,7 @@ public static void SaveMetaFile(Metafile file, string emfName) /// /// /// - public byte[]? ConvertMetaFileToByteArray(System.Drawing.Image image) + public static byte[]? ToByteArray(this System.Drawing.Image image) { return ToByteArray((Metafile)image); } @@ -79,16 +82,16 @@ public static void SaveMetaFile(Metafile file, string emfName) /// /// /// - public byte[]? ToByteArray(Metafile mf) + public static byte[]? ToByteArray(this Metafile mf) { byte[]? dataArray = null; IntPtr handle = mf.GetHenhmetafile(); - uint bufferSize = GetEnhMetaFileBits(handle, 0, null!); + uint size = GetEnhMetaFileBits(handle, 0, null!); if (handle != IntPtr.Zero) { - dataArray = new byte[bufferSize]; - GetEnhMetaFileBits(handle, bufferSize, dataArray); + dataArray = new byte[size]; + GetEnhMetaFileBits(handle, size, dataArray); } DeleteEnhMetaFile(handle); return dataArray; @@ -110,4 +113,184 @@ public static void ToMetafile(byte[] data, Func task if (task.Invoke(mf)) // 对图像进行操作,就不能进行删除句柄 DeleteEnhMetaFile(hemf); } + + + + /// + /// 导出为 Emf 或 Wmf 文件 + /// 相关链接 + /// + /// 文件路径 + /// 窗口宽度 + /// 窗口高度 + /// 是否成功 + public static bool Export(string filePath, int width, int height) + { + try + { + using Bitmap bmp = new(width, height); + using Graphics gs = Graphics.FromImage(bmp); + using Metafile mf = new(filePath, gs.GetHdc()); + using Graphics g = Graphics.FromImage(mf); + Draw(g); + g.Save(); + return true; + } + catch { return false; } + } + /// + /// 绘制图形 + /// + /// 用于绘图的Graphics对象 + static void Draw(Graphics g) + { + HatchBrush hb = new(HatchStyle.LightUpwardDiagonal, Color.Black, Color.White); + + g.FillEllipse(Brushes.Gray, 10f, 10f, 200, 200); + g.DrawEllipse(new Pen(Color.Black, 1f), 10f, 10f, 200, 200); + + g.FillEllipse(hb, 30f, 95f, 30, 30); + g.DrawEllipse(new Pen(Color.Black, 1f), 30f, 95f, 30, 30); + + g.FillEllipse(hb, 160f, 95f, 30, 30); + g.DrawEllipse(new Pen(Color.Black, 1f), 160f, 95f, 30, 30); + + g.FillEllipse(hb, 95f, 30f, 30, 30); + g.DrawEllipse(new Pen(Color.Black, 1f), 95f, 30f, 30, 30); + + g.FillEllipse(hb, 95f, 160f, 30, 30); + g.DrawEllipse(new Pen(Color.Black, 1f), 95f, 160f, 30, 30); + + g.FillEllipse(Brushes.Blue, 60f, 60f, 100, 100); + g.DrawEllipse(new Pen(Color.Black, 1f), 60f, 60f, 100, 100); + + g.FillEllipse(Brushes.BlanchedAlmond, 95f, 95f, 30, 30); + g.DrawEllipse(new Pen(Color.Black, 1f), 95f, 95f, 30, 30); + + g.DrawRectangle(new Pen(Brushes.Blue, 0.1f), 6, 6, 208, 208); + + g.DrawLine(new Pen(Color.Black, 0.1f), 110f, 110f, 220f, 25f); + g.DrawString("剖面图", new Font("宋体", 9f), Brushes.Green, 220f, 20f); + } +} + +// DWORD == uint +// WORD == ushort +// LONG == int + +/* + * Console.WriteLine(Marshal.SizeOf(typeof(PlaceableMetaHeader))); + * Console.WriteLine(Marshal.SizeOf(typeof(WindowsMetaHeader))); + * Console.WriteLine(Marshal.SizeOf(typeof(StandardMetaRecord))); + */ + +[Serializable] +[StructLayout(LayoutKind.Sequential, Pack = 2)] +public struct WmfStr +{ + public PlaceableMetaHeader Placeable; + public WindowsMetaHeader Wmfhead; + public StandardMetaRecord Wmfrecord; +} + +//WMF 文件格式: +//https://blog.51cto.com/chenyanxi/803247 +//文件缩放信息:22字节 +[Serializable] +[StructLayout(LayoutKind.Sequential, Pack = 2)] +public struct PlaceableMetaHeader +{ + public uint Key; /* 固定大小以相反顺序出现 9AC6CDD7h */ + public ushort Handle; /* Metafile HANDLE number (always 0) */ + public short Left; /* Left coordinate in metafile units */ + public short Top; /* Top coordinate in metafile units */ + public short Right; /* Right coordinate in metafile units */ + public short Bottom; /* Bottom coordinate in metafile units */ + public ushort Inch; /* Number of metafile units per inch */ + public uint Reserved; /* Reserved (always 0) */ + public ushort Checksum;      /* Checksum value for previous 10 WORDs */ +} + +//紧接文件缩放信息的是 WMFHEAD, 18字节 +[Serializable] +[StructLayout(LayoutKind.Sequential, Pack = 2)] +public struct WindowsMetaHeader +{ + public ushort FileType; /* Type of metafile (0=memory, 1=disk) */ + public ushort HeaderSize; /* Size of header in WORDS (always 9) */ + public ushort Version; /* Version of Microsoft Windows used */ + public uint FileSize; /* Total size of the metafile in WORDs */ + public ushort NumOfObjects; /* Number of objects in the file */ + public uint MaxRecordSize; /* The size of largest record in WORDs */ + public ushort NumOfParams;    /* Not Used (always 0) */ +} + +//紧接 WMFHEAD 的是 WMFRECORD, 14字节 +[Serializable] +[StructLayout(LayoutKind.Sequential, Pack = 2)] +public struct StandardMetaRecord +{ + public uint Size; /* Total size of the record in WORDs */ + public ushort Function; /* Function number (defined in WINDOWS.H) */ + public ushort[] Parameters;  /* Parameter values passed to function */ +} + + +public class WMF_java +{ + /// + /// 判断此文件是否为活动式wmf文件 + /// https://blog.51cto.com/chenyanxi/803247 + /// + /// 文件路径 + /// + /// + public static byte[] OpinionHead(string wmf) + { + using FileStream wmfFile = new(wmf, FileMode.Open, FileAccess.Read, FileShare.ReadWrite); // FileShare才能进c盘 + if (wmfFile.Length == 0) + throw new IOException("文件字节0长:" + wmfFile); + + var fileByte = new byte[wmfFile.Length]; + wmfFile.Read(fileByte, 0, fileByte.Length); + wmfFile.Close(); + + // 对头22个字节进行判断 + Debug.WriteLine((fileByte[0] & 0xff) + + "\t" + (fileByte[1] & 0xff) + + "\t" + (fileByte[2] & 0xff) + + "\t" + (fileByte[3] & 0xff)); + + // 微软的wmf文件分为两种一种是标准的图元文件, + // 一种是活动式图元文件,活动式图元文件 与 标准的图元文件 的主要区别是, + // 活动式图元文件包含了图像的原始大小和缩放信息. + if (fileByte.Length < 5) + throw new IOException("无法校验文件签名:" + wmfFile); + + // 对开始的四个字节进行校验 d7 cd c6 9a (WMF活动式图元文件签名) + if (((fileByte[0] & 0xff) != 215) || + ((fileByte[1] & 0xff) != 205) || + ((fileByte[2] & 0xff) != 198) || + ((fileByte[3] & 0xff) != 154)) + { + fileByte = null; + throw new IOException("此文件不为活动式wmf文件:" + wmfFile); + } + + // 验证文件校验位 + int index = 0; + for (int i = 0; i < 20; i += 2) + { + if (i == 0) + index = ((fileByte[i + 1] & 0xff) << 8) | (fileByte[i] & 0xff); + else if (i < 19) + index ^= ((fileByte[i + 1] & 0xff) << 8) | (fileByte[i] & 0xff); + } + if (index != (((fileByte[21] & 0xff) << 8) | (fileByte[20] & 0xff))) + { + fileByte = null; + throw new IOException("此文件已损坏!" + wmfFile); + } + return fileByte; + } } \ No newline at end of file diff --git a/src/IFoxCAD.Cad.Shared/Copyclip/TagClipboardInfo.cs b/src/IFoxCAD.Cad.Shared/Copyclip/TagClipboardInfo.cs index aebf194..df3f92d 100644 --- a/src/IFoxCAD.Cad.Shared/Copyclip/TagClipboardInfo.cs +++ b/src/IFoxCAD.Cad.Shared/Copyclip/TagClipboardInfo.cs @@ -212,7 +212,16 @@ public override string ToString() } } -public class ClipTool +/* + * OLE 剪贴板说明 + * https://blog.csdn.net/chinabinlang/article/details/9815495 + * + * 感觉如果桌子真的是这样做,那么粘贴链接可能还真没法做成. + * 1,不知道桌子如何发送wmf文件,是结构体传送,还是文件路径传送. + * 2,不知道桌子如何接收剪贴板数据,是延迟接收还是一次性写入全局变量或者文件. + */ + +public partial class ClipTool { /// /// 侦听剪贴板 @@ -338,7 +347,10 @@ public static bool OpenClipboardTask(Action> action, bool isSetClip finally { for (int i = 0; i < freeDatas.Count; i++) - WindowsAPI.GlobalFree(freeDatas[i]); + { + //WindowsAPI.GlobalFree(freeDatas[i]); + Marshal.FreeHGlobal(freeDatas[i]); + } if (openFlag) CloseClipboard(); } @@ -370,9 +382,6 @@ public static bool GetClipboard(string clipKey, out T? tag) return flag; } - // 申请的内存必须为可移动 - public const uint GMEM_MOVEABLE = 0x0002; - /// /// 获取剪贴板对象信息
    /// 微软链接 @@ -394,7 +403,11 @@ public static bool GetClipboardFormat(string clipKey, T clipType, */ clipKeyFormat = RegisterClipboardFormat(clipKey); - clipTypeData = WindowsAPI.GlobalAlloc(GMEM_MOVEABLE, Marshal.SizeOf(typeof(T))); + + // const uint GMEM_MOVEABLE = 0x0002;//剪贴板需要的类型 + // clipTypeData = WindowsAPI.GlobalAlloc(GMEM_MOVEABLE, Marshal.SizeOf(typeof(T))); + clipTypeData = Marshal.AllocHGlobal(Marshal.SizeOf(typeof(T))); + bool lockFlag = WindowsAPI.GlobalLockTask(clipTypeData, ptr => { // 托管对象->非托管内存块 Marshal.StructureToPtr(clipType, ptr, true); @@ -405,6 +418,85 @@ public static bool GetClipboardFormat(string clipKey, T clipType, } } +public static class ClipEx +{ + // https://blog.csdn.net/vencon_s/article/details/46345083 + + /// + /// 剪贴板数据保存目标数据列表 + /// + static readonly List _bytes = new(); + /// + /// 剪贴板数据类型列表 + /// + static readonly List _formats = new(); + + /// + /// 遍历剪贴板保存内容 + /// + /// true成功,false失败 + public static bool SaveClip() + { + bool result = ClipTool.OpenClipboardTask(free => { + _bytes.Clear(); + _formats.Clear(); + + uint cf = 0; + while (true) + { + cf = ClipTool.EnumClipboardFormats(cf);// 枚举剪贴板所有数据类型 + if (cf == 0) + break; + + _formats.Add(cf); + IntPtr hMem = ClipTool.GetClipboardData(cf); + WindowsAPI.GlobalLockTask(hMem, prt => { + uint size = WindowsAPI.GlobalSize(hMem); + if (size > 0) + { + var buffer = new byte[size]; + Marshal.Copy(prt, buffer, 0, buffer.Length);// 将剪贴板数据保存到自定义字节数组 + _bytes.Add(buffer); + } + }); + } + }, false); + if (result) + result = _formats.Count > 0; + return result; + } + + /// + /// 恢复保存的数据 + /// + /// true成功,false失败 + public static bool RestoreClip() + { + if (_formats.Count <= 0) + return false; + + bool result = ClipTool.OpenClipboardTask(free => { + for (int i = 0; i < _formats.Count; i++) + { + int size = _bytes[i].Length; + IntPtr si = Marshal.AllocHGlobal(size); + if (size > 0) + { + Marshal.Copy(_bytes[i], 0, si, size); + ClipTool.SetClipboardData(_formats[i], si); + } + } + }, true); + + if (result) + result = _formats.Count > 0; + return result; + } +} + + + + /// /// 剪贴板的CF,也就是它的key /// diff --git a/src/IFoxCAD.Cad.Shared/Copyclip/WindowsAPI.cs b/src/IFoxCAD.Cad.Shared/Copyclip/WindowsAPI.cs index 8f44f56..c90e55a 100644 --- a/src/IFoxCAD.Cad.Shared/Copyclip/WindowsAPI.cs +++ b/src/IFoxCAD.Cad.Shared/Copyclip/WindowsAPI.cs @@ -5,6 +5,47 @@ public class WindowsAPI { + // https://blog.csdn.net/haelang/article/details/45147121 + [DllImport("kernel32.dll")] + public extern static uint GetLastError(); + + /// + /// 获取窗口客户区的大小,客户区为窗口中除标题栏,菜单栏之外的地方 + /// + /// + /// + /// + [DllImport("user32.dll", CharSet = CharSet.Auto, EntryPoint = "GetClientRect")] + public static extern bool GetClientRect(IntPtr hwnd, out IntRect lpRect); + + /// + /// 查找主线程
    + /// 代替
    + /// 托管线程和他们不一样: + ///
    + /// 主窗口 + /// 进程ID + /// 线程ID + [DllImport("user32.dll", SetLastError = true)] + public static extern uint GetWindowThreadProcessId(IntPtr hWnd, out uint lpdwProcessId); + + + + [DllImport("kernel32.dll", CharSet = CharSet.Auto, SetLastError = true)] + public static extern IntPtr CreateFile( + [MarshalAs(UnmanagedType.LPTStr)] string filename, + [MarshalAs(UnmanagedType.U4)] FileAccess access, + [MarshalAs(UnmanagedType.U4)] FileShare share, + IntPtr securityAttributes, // optional SECURITY_ATTRIBUTES struct or IntPtr.Zero + [MarshalAs(UnmanagedType.U4)] FileMode creationDisposition, + [MarshalAs(UnmanagedType.U4)] FileAttributes flagsAndAttributes, + IntPtr templateFile); + + [DllImport("kernel32", SetLastError = true)] + public static extern bool CloseHandle(IntPtr hObject);/* 对象句柄 CreateFile*/ + + + /// /// 锁定内存 /// @@ -19,8 +60,10 @@ public class WindowsAPI /// [DllImport("kernel32.dll", SetLastError = true)] static extern bool GlobalUnlock(IntPtr hMem); +#if true2 /// /// 从堆中分配内存 + /// 被代替: Marshal.AllocHGlobal /// /// 分配方式 /// 分配的字节数 @@ -29,11 +72,13 @@ public class WindowsAPI public static extern IntPtr GlobalAlloc(uint uFlags, int dwBytes); /// /// 释放堆内存 + /// 被代替: Marshal.FreeHGlobal /// /// 产生的句柄 /// [DllImport("kernel32.dll", SetLastError = true)] public static extern IntPtr GlobalFree(IntPtr hMem); +#endif /// /// 获取内存块大小 /// @@ -42,16 +87,7 @@ public class WindowsAPI [DllImport("kernel32.dll", SetLastError = true)] public static extern uint GlobalSize(IntPtr hMem); - /// - /// 查找主线程
    - /// 代替
    - /// 托管线程和他们不一样: - ///
    - /// 主窗口 - /// 进程ID - /// 线程ID - [DllImport("user32.dll", SetLastError = true)] - public static extern uint GetWindowThreadProcessId(IntPtr hWnd, out uint lpdwProcessId); + /// /// 锁定和释放内存 @@ -80,10 +116,57 @@ public static bool GlobalLockTask(IntPtr data, Action task) finally { GlobalUnlock(data); } return true; } -} + /// + /// byte数组转结构体 + /// + /// byte数组 + /// 返回的结构体 + /// 转换后的结构体 + public static bool BytesToStuct(byte[] bytes, out T? obj) + { + obj = default; + var tt = typeof(T); + // 得到结构体的大小 + int size = Marshal.SizeOf(tt); + if (size > bytes.Length) + return false; + + // 分配结构体大小的内存空间 + IntPtr structPtr = Marshal.AllocHGlobal(size); + // 将byte数组拷到分配好的内存空间 + Marshal.Copy(bytes, 0, structPtr, size); + // 将内存空间转换为目标结构体 + obj = (T)Marshal.PtrToStructure(structPtr, tt); + // 释放内存空间 + Marshal.FreeHGlobal(structPtr); + return true; + } + /// + /// 结构体转byte数组 + /// + /// 要转换的结构体 + /// 转换后的byte数组 + public static byte[] StructToBytes(object structObj) + { + // 得到结构体的大小 + int size = Marshal.SizeOf(structObj); + // 创建byte数组 + byte[] bytes = new byte[size]; + // 分配结构体大小的内存空间 + IntPtr structPtr = Marshal.AllocHGlobal(size); + // 将结构体拷到分配好的内存空间 + Marshal.StructureToPtr(structObj, structPtr, false); + //从 内存空间拷到byte数组 + Marshal.Copy(structPtr, bytes, 0, size); + // 释放内存空间 + Marshal.FreeHGlobal(structPtr); + // 返回byte数组 + return bytes; + } +} #if true2 diff --git a/src/IFoxCAD.Cad.Shared/ExtensionMethod/EditorEx.cs b/src/IFoxCAD.Cad.Shared/ExtensionMethod/EditorEx.cs index 28184f4..26b3f87 100644 --- a/src/IFoxCAD.Cad.Shared/ExtensionMethod/EditorEx.cs +++ b/src/IFoxCAD.Cad.Shared/ExtensionMethod/EditorEx.cs @@ -1090,6 +1090,7 @@ public enum RunLispFlag : byte #region Export /// /// 输出WMF
    + /// 此函数不适用于后台 ///
    /// 命令行对象 /// 保存文件 @@ -1119,9 +1120,8 @@ public static void ExportWMF(this Editor editor, string saveFile, } // ActiveSelectionSet: - // 第一次执行会触发选择, - // 再次重复命令执行的时候,它会无法再选择 - // 因此此处选择一次,此时必然有当前选择集,它就直接获取当前选择集 + // 第一次执行会触发选择,再次重复命令执行的时候,它会无法再选择(即使清空选择集). + // 因此此处netAPI进行选择,它就能读取当前选择集缓冲区的对象 if (ids == null || ids.Length == 0) { var psr = editor.SelectImplied();// 预选 diff --git a/tests/TestConsole/Info.cs b/tests/TestConsole/Info.cs index b6ad87f..7c41b71 100644 --- a/tests/TestConsole/Info.cs +++ b/tests/TestConsole/Info.cs @@ -1,4 +1,6 @@ -namespace TestConsole; +using System.Runtime.InteropServices; + +namespace TestConsole; [Flags] public enum PlugIn @@ -11,7 +13,7 @@ public enum PlugIn IMinCad = 4, Lisp = JoinBox | YuanQuan | IMinCad, - + DOCBAR = 8, DUOTAB = 16, diff --git a/tests/TestConsole/Program.cs b/tests/TestConsole/Program.cs index 708ceba..f124e0b 100644 --- a/tests/TestConsole/Program.cs +++ b/tests/TestConsole/Program.cs @@ -1,10 +1,12 @@ // See https://aka.ms/new-console-template for more information using System; +using System.Runtime.InteropServices; using System.Runtime.Serialization.Formatters.Binary; using System.Text; using TestConsole; + Console.WriteLine("***************************************************"); diff --git a/tests/TestShared/Copyclip.cs b/tests/TestShared/Copyclip.cs index aaaf49a..ab348d0 100644 --- a/tests/TestShared/Copyclip.cs +++ b/tests/TestShared/Copyclip.cs @@ -4,7 +4,11 @@ namespace Test; using Autodesk.AutoCAD.DatabaseServices; +using System.Diagnostics; +using System.Drawing.Imaging; using System.Threading; +using System.Windows; +using static HarmonyLib.Code; /* * 0x01 (已完成) @@ -246,17 +250,9 @@ void Copy(bool getPoint, bool isEraseSsget = false) }); // 大于dwg07格式的,保存为07,以实现高低版本通用剪贴板 - // 小于dwg07格式的,本工程没有支持cad06dll,所以这里仅为展示 + // 小于dwg07格式的,本工程没有支持cad06dll if ((int)DwgVersion.Current >= 27) - { fileTr.SaveFile((DwgVersion)27, false); - } - else - { -#pragma warning disable CS0162 // 检测到无法访问的代码 - fileTr.SaveFile(); -#pragma warning restore CS0162 // 检测到无法访问的代码 - } } // 剪切时候删除 @@ -270,84 +266,104 @@ void Copy(bool getPoint, bool isEraseSsget = false) #region 写入 WMF 数据 var wmf = Path.ChangeExtension(cadClipType.File, "wmf"); - Env.Editor.ExportWMF(wmf, idArray); - //using var mf = new Metafile(wmf); - //IntPtr emfHandle = mf.GetHenhmetafile(); + int a1 = 1; + if (a1 == 1) + { + // 导出的可能只是wmf其中一个字段 + // cad命令 wmfin:不能导入 c# Graphics类 自绘的 + // https://www.cnblogs.com/5iedu/p/4706327.html + Env.Editor.ExportWMF(wmf, idArray); + } + if (a1 == 2) + { + // 这是c#写入wmf流程 + // c#画的wmf格式是可以的...用这样方式生成的就是可以写剪贴板 + WindowsAPI.GetClientRect(doc.Window.Handle, out IntRect rcClient); + int width = rcClient.Right - rcClient.Left; + int height = rcClient.Bottom - rcClient.Top; + EmfTool.Export(wmf, width, height); + } + + // 这里能够读出 cad wmf 签名 + { + var javaByte = WMF_java.OpinionHead(wmf); + // 此处转失败 + // WmfStr WmfStr = new(); + // bool ja = WindowsAPI.BytesToStuct(javaByte, out WmfStr); + } + + //wmf指针 + IntPtr wmfMeta = IntPtr.Zero; + int a2 = 1; + if (a2 == 1) + { + wmfMeta = GetMetafile(wmf); + } + if (a2 == 2) + { + wmfMeta = WindowsAPI.CreateFile(wmf, + FileAccess.Read, FileShare.ReadWrite, + IntPtr.Zero, FileMode.Open, + FileAttributes.Normal, IntPtr.Zero); + } + if (a2 == 3) + { + // 不成功 + using FileStream wmfFile = new(wmf, FileMode.Open, FileAccess.Read, FileShare.ReadWrite); // FileShare才能进c盘 + var fileByte = new byte[wmfFile.Length]; + wmfFile.Read(fileByte, 0, fileByte.Length); + if (fileByte.Length > 0) + { + wmfMeta = Marshal.AllocHGlobal(fileByte.Length); + Marshal.Copy(fileByte, 0, wmfMeta, fileByte.Length); + } + } + + if (wmfMeta == IntPtr.Zero) + { + var errorNum = WindowsAPI.GetLastError(); + if (errorNum == 6) + Debug.WriteLine("WMF错误:句柄无效!"); + else + Debug.WriteLine("WMF错误:" + errorNum); + } #endregion - /* - * 剪贴板说明 - * https://blog.csdn.net/chinabinlang/article/details/9815495 - * - * 看了这ole剪贴板,感觉如果桌子真的是这样做,那么粘贴链接可能还真没法做成. - * 1,不知道桌子如何发送wmf文件,是结构体传送,还是文件路径传送. - * 2,不知道桌子如何接收剪贴板数据,是延迟接收还是一次性写入全局变量或者文件. - * https://blog.csdn.net/chinabinlang/article/details/9815495 - */ - /// 必须一次性写入剪贴板,详见说明 + /// 必须一次性写入剪贴板,详见 OpenClipboardTask bool getFlag = false; - ClipTool.OpenClipboardTask((freeDatas) => { + ClipTool.OpenClipboardTask(free => { getFlag = ClipTool.GetClipboardFormat( ClipboardEnv.CadVer, cadClipType, out uint cadClipFormat, out IntPtr cadClipData); - freeDatas.Add(cadClipData); // 写入cad图元 ClipTool.SetClipboardData(cadClipFormat, cadClipData); + free.Add(cadClipData); + // c# cad截图 https://www.cnblogs.com/shangdishijiao/p/15166499.html // 写入BMP位图...这是截图,不是WMF转BMP,不对 - BitmapTool.CaptureWndImage(doc.Window.Handle, bitmapHandle => { - ClipTool.SetClipboardData((uint)ClipboardFormat.CF_BITMAP, bitmapHandle); - }); - + //BitmapTool.CaptureWndImage(doc.Window.Handle, bitmapHandle => { + // ClipTool.SetClipboardData((uint)ClipboardFormat.CF_BITMAP, bitmapHandle); + //}); - // 写入WMF - // MFC类 CMetaFileDC 不知道怎么做... - // https://blog.csdn.net/glt3953/article/details/8808262 - // ClipTool.SetClipboardData((uint)ClipboardFormat.CF_ENHMETAFILE, wmfHandle); - - // c# WMF转换文件 - // https://blog.csdn.net/u013419838/article/details/100154891 - //using MemoryStream ms = new(File.ReadAllBytes(wmf)); - //using GZipStream gzipStream = new(ms, CompressionMode.Decompress); - //using MemoryStream outStream = new(); - //int readCount; - //byte[] data = new byte[2048]; - //do - //{ - // readCount = gzipStream.Read(data, 0, data.Length); - // outStream.Write(data, 0, readCount); - //} while (readCount == data.Length); - // outStream.GetBuffer() - - //ClipTool.GetClipboardFormat( - // System.Windows.Forms.DataFormats.EnhancedMetafile, emfClipType, - // out uint emfClipFormat, out IntPtr emfClipData); - //freeDatas.Add(emfClipData); - - // 没效果 - // using var mf = new Metafile(wmf); - // IntPtr emfHandle = mf.GetHenhmetafile(); - //if (emfHandle != IntPtr.Zero) - //{ - // ClipTool.SetClipboardData( - // ClipTool.RegisterClipboardFormat(System.Windows.Forms.DataFormats.EnhancedMetafile), - // emfHandle); - //} - - /* - * 文件结构 - * https://www.vuln.cn/6358#:~:text=%20%E4%B8%8B%E9%9D%A2%E7%AE%80%E8%A6%81%E4%BB%8B%E7%BB%8D%E4%B8%80%E4%B8%8BEMF%E6%96%87%E4%BB%B6%E7%9A%84%E7%BB%93%E6%9E%84%EF%BC%8CEMF%E6%96%87%E4%BB%B6%E7%94%B1%E5%8F%AF%E5%8F%98%E5%A4%A7%E5%B0%8F%E7%9A%84%E5%85%83%E6%96%87%E4%BB%B6%E5%9D%97%E7%BB%84%E6%88%90%E3%80%82,%E6%AF%8F%E4%B8%AA%E5%85%83%E6%96%87%E4%BB%B6%E5%9D%97%E9%83%BD%E6%98%AF%E4%B8%80%E4%B8%AA%E5%8F%AF%E5%8F%98%E9%95%BF%E5%BA%A6%E7%9A%84ENHMETARECORD%E7%BB%93%E6%9E%84%EF%BC%8C%E7%BB%93%E6%9E%84%E5%A6%82%E4%B8%8B%E3%80%82%20SDK%E4%B8%AD%E5%AE%9A%E4%B9%89%E4%BA%86%E4%B8%8D%E5%90%8C%E7%9A%84iType%E7%B1%BB%E5%9E%8B%EF%BC%8C%E5%A6%82%E4%B8%8B%E6%89%80%E7%A4%BA%E3%80%82%20%E6%A0%B9%E6%8D%AEiType%E7%B1%BB%E5%9E%8B%E7%9A%84%E4%B8%8D%E5%90%8C%EF%BC%8CdParm%E6%98%AF%E4%B8%8D%E5%90%8C%E7%9A%84%E7%BB%93%E6%9E%84%EF%BC%8CEMR_SETDIBITSTODEVICE%E5%AF%B9%E5%BA%94%E7%9A%84%E7%BB%93%E6%9E%84%E6%98%AFEMRSETDIBITSTODEVICE%E3%80%82 - * https://blog.csdn.net/juma/article/details/2200023 有代码抄 - * https://www.cnblogs.com/5iedu/p/4706327.html - * https://vimsky.com/examples/detail/csharp-ex-System.Runtime.InteropServices.ComTypes-STGMEDIUM---class.html - * https://blog.csdn.net/qq_45533841/article/details/106011204 - * - * 搞不定... - */ + // TODO 写入wmf + if (wmfMeta != IntPtr.Zero) + { + ClipTool.SetClipboardData((uint)ClipboardFormat.CF_ENHMETAFILE, wmfMeta); + EmfTool.DeleteEnhMetaFile(wmfMeta); + } }, true); + if (a2 == 2) + { + WindowsAPI.CloseHandle(wmfMeta); + } + if (a2 == 3) + { + Marshal.FreeHGlobal(wmfMeta); + } + // 成功拷贝就删除上一次的临时文件 if (getFlag) Terminate(); @@ -359,6 +375,23 @@ void Copy(bool getPoint, bool isEraseSsget = false) _delFile.Add(wmf); } + static IntPtr GetMetafile(string wmf) + { + var hEMF2 = IntPtr.Zero; + + // 这是c#写入wmf流程 + // FileShare才能进c盘 + using FileStream wmfFile = new(wmf, FileMode.Open, FileAccess.Read, FileShare.ReadWrite); + using Metafile mf = new(wmfFile); + var hEMF = mf.GetHenhmetafile(); + if (hEMF != IntPtr.Zero) + { + hEMF2 = EmfTool.CopyEnhMetaFile(hEMF, null);// 这句: 句柄无效..cad的wmf文件不识别 + //EmfTool.DeleteEnhMetaFile(hEMF);//托管类应该是封装好的 + } + return hEMF2; + } + /// /// 粘贴 @@ -396,13 +429,6 @@ void Paste(bool isBlock) return; using var tr = new DBTrans(); - - // 给辰的测试 - //var pr = tr.Editor?.GetPoint("aaa"); - //if (pr != null && pr.Status == PromptStatus.OK) - // tr.Editor?.WriteMessage("获取了点:" + pr.Value); - - tr.Editor?.SetImpliedSelection(new ObjectId[0]); // 清空选择集 // 新建块表记录 @@ -474,6 +500,45 @@ void Paste(bool isBlock) using (btr.ForWrite()) btr.Erase(); } + + #region 读取剪贴板WMF +#if true2 + // win32api 不成功 + ClipTool.OpenClipboardTask((freeDatas) => { + // 剪贴板数据保存目标数据列表 + List _bytes = new(); + var hMem = ClipTool.GetClipboardData((uint)ClipboardFormat.CF_ENHMETAFILE); + WindowsAPI.GlobalLockTask(hMem, prt => { + uint size = WindowsAPI.GlobalSize(hMem); + if (size > 0) + { + var buffer = new byte[size]; + Marshal.Copy(prt, buffer, 0, buffer.Length);// 将剪贴板数据保存到自定义字节数组 + _bytes.Add(buffer); + } + }); + }, false); +#else + try + { + // c# 读取成功 + if (Clipboard.ContainsData(DataFormats.EnhancedMetafile)) + { + var iData = Clipboard.GetDataObject();//从剪切板获取数据 + if (!iData.GetDataPresent(DataFormats.EnhancedMetafile)) + return; + + //定义图形-图元文件 + var metafile = (Metafile)iData.GetData(DataFormats.EnhancedMetafile); + Env.Printl("Metafile:" + metafile.Size.ToString()); + } + } + catch (Exception e) + { + throw; + } +#endif + #endregion } /// @@ -581,4 +646,39 @@ static string CreateTempFileName(string format = "X") // foreach (var id in btr) // action.Invoke(id); // } -//} \ No newline at end of file +//} + + +#if true2 + +public class TestImageFormat +{ + public ImageFormat GetFormat(string filename) + { + string ext = Path.GetExtension(filename).ToLower(); + var imf = ext switch + { + ".bmp" => ImageFormat.Bmp, + ".gif" => ImageFormat.Gif, + ".jpg" => ImageFormat.Jpeg, + ".tif" => ImageFormat.Tiff, + ".wmf" => ImageFormat.Wmf, + ".png" => ImageFormat.Png, + _ => throw new NotImplementedException(), + }; + return imf; + } + + [CommandMethod("CPI")] + public void CreatePreviewImage() + { + // Get the size of the document and capture the preview at that size + var size = Document.Window.DeviceIndependentSize; + using (var bmp = Document.CapturePreviewImage(Convert.ToUInt32(size.Width), Convert.ToUInt32(size.Height))) + { + // Save the file with the format derived from the filename + bmp.Save(outFile, GetFormat(outFile)); + } + } +} +#endif \ No newline at end of file -- Gitee From 98abacdd555f7ec3bebda3f8f0f6703f221d2fe2 Mon Sep 17 00:00:00 2001 From: liuqihong <540762622@qq.com> Date: Sun, 2 Oct 2022 02:27:20 +0800 Subject: [PATCH 535/675] =?UTF-8?q?=E5=89=AA=E8=B4=B4=E6=9D=BF=E8=8E=B7?= =?UTF-8?q?=E5=8F=96wmf=E6=88=90=E5=8A=9F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/IFoxCAD.Cad.Shared/Copyclip/EmfTool.cs | 333 +++++++++++------- src/IFoxCAD.Cad.Shared/Copyclip/WindowsAPI.cs | 13 +- tests/TestShared/Copyclip.cs | 91 +---- 3 files changed, 223 insertions(+), 214 deletions(-) diff --git a/src/IFoxCAD.Cad.Shared/Copyclip/EmfTool.cs b/src/IFoxCAD.Cad.Shared/Copyclip/EmfTool.cs index 67ae270..3bc0b15 100644 --- a/src/IFoxCAD.Cad.Shared/Copyclip/EmfTool.cs +++ b/src/IFoxCAD.Cad.Shared/Copyclip/EmfTool.cs @@ -5,9 +5,131 @@ using System.Drawing; using System.Drawing.Imaging; using System.Diagnostics; +using System.Windows; +using System.Security.Policy; +using System.Security.Cryptography; + + + +// DWORD == uint +// WORD == ushort +// LONG == int + +/* + * Console.WriteLine(Marshal.SizeOf(typeof(PlaceableMetaHeader))); + * Console.WriteLine(Marshal.SizeOf(typeof(WindowsMetaHeader))); + * Console.WriteLine(Marshal.SizeOf(typeof(StandardMetaRecord))); + */ + +[Serializable] +[StructLayout(LayoutKind.Sequential, Pack = 2)] +public struct WmfStr +{ + public PlaceableMetaHeader Placeable; + public WindowsMetaHeader Wmfhead; + public StandardMetaRecord Wmfrecord; +} + +//WMF 文件格式: +//https://blog.51cto.com/chenyanxi/803247 +//文件缩放信息:22字节 +[Serializable] +[StructLayout(LayoutKind.Sequential, Pack = 2)] +public struct PlaceableMetaHeader +{ + public uint Key; /* 固定大小以相反顺序出现 9AC6CDD7h */ + public ushort Handle; /* Metafile HANDLE number (always 0) */ + + public short Left; /* Left coordinate in metafile units */ + public short Top; /* Top coordinate in metafile units */ + public short Right; /* Right coordinate in metafile units */ + public short Bottom; /* Bottom coordinate in metafile units */ + + public ushort Inch; /* Number of metafile units per inch */ + public uint Reserved; /* Reserved (always 0) */ + public ushort Checksum; /* Checksum value for previous 10 WORDs */ + + // 微软的wmf文件分为两种一种是标准的图元文件, + // 一种是活动式图元文件,活动式图元文件 与 标准的图元文件 的主要区别是, + // 活动式图元文件包含了图像的原始大小和缩放信息. + /// + /// 是活动式图元文件 + /// + public bool IsActivity => Key == 0x9AC6CDD7; +} + +//紧接文件缩放信息的是 WMFHEAD, 18字节 +[Serializable] +[StructLayout(LayoutKind.Sequential, Pack = 2)] +public struct WindowsMetaHeader +{ + public ushort FileType; /* Type of metafile (0=memory, 1=disk) */ + public ushort HeaderSize; /* Size of header in WORDS (always 9) */ + public ushort Version; /* Version of Microsoft Windows used */ + public uint FileSize; /* Total size of the metafile in WORDs */ + public ushort NumOfObjects; /* Number of objects in the file */ + public uint MaxRecordSize; /* The size of largest record in WORDs */ + public ushort NumOfParams;    /* Not Used (always 0) */ +} + +//紧接 WMFHEAD 的是 WMFRECORD, 14字节 +[Serializable] +[StructLayout(LayoutKind.Sequential, Pack = 2)] +public struct StandardMetaRecord +{ + public uint Size; /* Total size of the record in WORDs */ + public ushort Function; /* Function number (defined in WINDOWS.H) */ + public ushort[] Parameters;  /* Parameter values passed to function */ +} + public static class EmfTool { + // https://zhidao.baidu.com/question/646739770512964165/answer/1616737219.html?qq-pf-to=pcqq.c2c + //16位的函数 + [DllImport("gdi32.dll")] + public static extern uint GetMetaFile(StringBuilder path); + //32位的函数 + [DllImport("gdi32.dll")] + public static extern uint GetEnhMetaFile(StringBuilder path); + + + /// + /// 将一个标准Windows图元文件转换成增强型图元文件 + /// + /// 数组的长度 + /// + /// 数组包含了标准图元文件数据.
    + /// 常用 GetMetaFileBitsEx 或 GetWinMetaFileBits 函数获得 + /// + /// + /// 用于决定原始格式及图元文件分辨率的一个参考设备场景;
    + /// 采用显示器分辨率为: + /// + /// + /// 定义一个图元文件附加参考信息的结构
    + /// 为null时,会假定使用当前显示器的 MM_ANISOTROPIC 映射模式 + /// + /// + /// 错误: ;
    + /// 成功: 返回一个增强型图元emf文件的指针(位于内存中) + ///
    + [DllImport("gdi32.dll", EntryPoint = "SetWinMetaFileBits")] + public static extern IntPtr SetWinMetaFileBits(uint nSize, IntPtr lpMeta16Data, IntPtr hdcRef, IntPtr lpMFP); + + /* + * [DllImport("gdi32.dll", EntryPoint = "SetWinMetaFileBits")] + * public static extern int SetWinMetaFileBits(uint nSize, ref byte lpbBuffer, IntPtr hdcRef, ref METAFILEPICT lpmfp); + * // https://learn.microsoft.com/en-us/windows/win32/api/wingdi/ns-wingdi-metafilepict + * public struct METAFILEPICT + * { + * public int mm; + * public int xExt; + * public int yExt; + * public HMETAFILE hMF; //内存图元文件的句柄 + * } + */ + /// /// 获取矢量图的byte /// @@ -37,12 +159,12 @@ public static class EmfTool /// 创建矢量图 /// https://www.cnblogs.com/5iedu/p/4706327.html ///
    - /// 参考设备环境,NULL时表示以屏幕为参考 - /// 指定文件名时,创建磁盘文件(.EMF)。为NULL时创建内存图元文件 + /// 参考设备环境,NULL时表示以屏幕为参考 + /// 指定文件名时,创建磁盘文件(.EMF).为NULL时创建内存图元文件 /// 用于描述图元文件的大小和位置(以0.01mm为单位),可用它精确定义图元文件的物理尺寸 - /// 对图元文件的一段说明。包括创建应用程序的名字、一个NULL字符、对图元文件的一段说明以及两个NULL字符。 + /// 对图元文件的一段说明.包括创建应用程序的名字、一个NULL字符、对图元文件的一段说明以及两个NULL字符. /// 增强型图元文件DC(注意不是图元文件的句柄,要获得实际的图元文件句柄,得调用 CloseEnhMetaFile 函数) - [DllImport("gdi32.dll")] + [DllImport("gdi32.dll", SetLastError = true)] public static extern IntPtr CreateEnhMetaFile(IntPtr hdcRef, string szFilename, IntRect lpRect, string lpDescription); /// @@ -72,11 +194,12 @@ public static void SaveMetaFile(this Metafile file, string emfName) /// /// /// - public static byte[]? ToByteArray(this System.Drawing.Image image) + public static byte[]? ToByteArray(this Image image) { return ToByteArray((Metafile)image); } + // https://www.pinvoke.net/default.aspx/gdi32.getenhmetafile /// /// 矢量图 转换 byte[] /// @@ -84,17 +207,19 @@ public static void SaveMetaFile(this Metafile file, string emfName) /// public static byte[]? ToByteArray(this Metafile mf) { - byte[]? dataArray = null; + byte[]? arr = null; IntPtr handle = mf.GetHenhmetafile(); - - uint size = GetEnhMetaFileBits(handle, 0, null!); if (handle != IntPtr.Zero) { - dataArray = new byte[size]; - GetEnhMetaFileBits(handle, size, dataArray); + uint size = GetEnhMetaFileBits(handle, 0, null!); + if (size != 0) + { + arr = new byte[size]; + _ = GetEnhMetaFileBits(handle, size, arr); + } + DeleteEnhMetaFile(handle); } - DeleteEnhMetaFile(handle); - return dataArray; + return arr; } /// @@ -103,7 +228,7 @@ public static void SaveMetaFile(this Metafile file, string emfName) /// /// 返回值true删除句柄 /// - public static void ToMetafile(byte[] data, Func task) + public static void ToMetafile(byte[] data, Func task) { if (task == null) throw new ArgumentNullException(nameof(task)); @@ -115,6 +240,65 @@ public static void ToMetafile(byte[] data, Func task } + /// + /// cad的wmf文件解析,转为emf
    + ///
    + /// 文件路径 + /// emf指针,可以直接写入剪贴板 + /// + public static IntPtr CadGetMetafile(string wmf) + { + using FileStream wmfFile = new(wmf, FileMode.Open, FileAccess.Read, FileShare.ReadWrite); // FileShare才能进c盘 + if (wmfFile.Length == 0) + throw new IOException("文件字节0长:" + wmfFile); + if (wmfFile.Length < 5) + throw new IOException("无法校验文件签名:" + wmfFile); + + var fileByte = new byte[wmfFile.Length]; + wmfFile.Read(fileByte, 0, fileByte.Length); + wmfFile.Close(); + + bool bts = WindowsAPI.BytesToStruct(fileByte, out PlaceableMetaHeader structWMF, out int iOffset); + if (!bts) + throw new IOException("失败:类型转换,路径:" + wmfFile); + + if (structWMF.IsActivity) + iOffset = Marshal.SizeOf(typeof(PlaceableMetaHeader)); + + // byte[] 指针偏移 https://blog.csdn.net/i0048egi/article/details/56063494 + GCHandle hObject1 = GCHandle.Alloc(fileByte.Skip(iOffset).ToArray(), GCHandleType.Pinned); + + //TODO 这里两个0值的设置没有和cad一样有个矩形 + var hEMF = SetWinMetaFileBits((uint)(fileByte.Length - iOffset), + hObject1.AddrOfPinnedObject(), + IntPtr.Zero, IntPtr.Zero); + if (hObject1.IsAllocated) + hObject1.Free(); + + if (hEMF == IntPtr.Zero) + throw new IOException("失败:" + nameof(SetWinMetaFileBits)); + return hEMF; + } + + + /// + /// c#获取wmf方式 + /// + /// + /// + public static IntPtr GetMetafile(string wmf) + { + using FileStream wmfFile = new(wmf, FileMode.Open, FileAccess.Read, FileShare.ReadWrite); // FileShare才能进c盘 + var hEMF2 = IntPtr.Zero; + + using Metafile mf = new(wmfFile); + var hEMF = mf.GetHenhmetafile(); + if (hEMF != IntPtr.Zero) + hEMF2 = CopyEnhMetaFile(hEMF, null);// 这句: 句柄无效..cad的wmf文件不识别 + //EmfTool.DeleteEnhMetaFile(hEMF);//托管类应该是封装好的 + return hEMF2; + } + /// /// 导出为 Emf 或 Wmf 文件 @@ -138,6 +322,8 @@ public static bool Export(string filePath, int width, int height) } catch { return false; } } + + /// /// 绘制图形 /// @@ -172,125 +358,4 @@ static void Draw(Graphics g) g.DrawLine(new Pen(Color.Black, 0.1f), 110f, 110f, 220f, 25f); g.DrawString("剖面图", new Font("宋体", 9f), Brushes.Green, 220f, 20f); } -} - -// DWORD == uint -// WORD == ushort -// LONG == int - -/* - * Console.WriteLine(Marshal.SizeOf(typeof(PlaceableMetaHeader))); - * Console.WriteLine(Marshal.SizeOf(typeof(WindowsMetaHeader))); - * Console.WriteLine(Marshal.SizeOf(typeof(StandardMetaRecord))); - */ - -[Serializable] -[StructLayout(LayoutKind.Sequential, Pack = 2)] -public struct WmfStr -{ - public PlaceableMetaHeader Placeable; - public WindowsMetaHeader Wmfhead; - public StandardMetaRecord Wmfrecord; -} - -//WMF 文件格式: -//https://blog.51cto.com/chenyanxi/803247 -//文件缩放信息:22字节 -[Serializable] -[StructLayout(LayoutKind.Sequential, Pack = 2)] -public struct PlaceableMetaHeader -{ - public uint Key; /* 固定大小以相反顺序出现 9AC6CDD7h */ - public ushort Handle; /* Metafile HANDLE number (always 0) */ - public short Left; /* Left coordinate in metafile units */ - public short Top; /* Top coordinate in metafile units */ - public short Right; /* Right coordinate in metafile units */ - public short Bottom; /* Bottom coordinate in metafile units */ - public ushort Inch; /* Number of metafile units per inch */ - public uint Reserved; /* Reserved (always 0) */ - public ushort Checksum;      /* Checksum value for previous 10 WORDs */ -} - -//紧接文件缩放信息的是 WMFHEAD, 18字节 -[Serializable] -[StructLayout(LayoutKind.Sequential, Pack = 2)] -public struct WindowsMetaHeader -{ - public ushort FileType; /* Type of metafile (0=memory, 1=disk) */ - public ushort HeaderSize; /* Size of header in WORDS (always 9) */ - public ushort Version; /* Version of Microsoft Windows used */ - public uint FileSize; /* Total size of the metafile in WORDs */ - public ushort NumOfObjects; /* Number of objects in the file */ - public uint MaxRecordSize; /* The size of largest record in WORDs */ - public ushort NumOfParams;    /* Not Used (always 0) */ -} - -//紧接 WMFHEAD 的是 WMFRECORD, 14字节 -[Serializable] -[StructLayout(LayoutKind.Sequential, Pack = 2)] -public struct StandardMetaRecord -{ - public uint Size; /* Total size of the record in WORDs */ - public ushort Function; /* Function number (defined in WINDOWS.H) */ - public ushort[] Parameters;  /* Parameter values passed to function */ -} - - -public class WMF_java -{ - /// - /// 判断此文件是否为活动式wmf文件 - /// https://blog.51cto.com/chenyanxi/803247 - /// - /// 文件路径 - /// - /// - public static byte[] OpinionHead(string wmf) - { - using FileStream wmfFile = new(wmf, FileMode.Open, FileAccess.Read, FileShare.ReadWrite); // FileShare才能进c盘 - if (wmfFile.Length == 0) - throw new IOException("文件字节0长:" + wmfFile); - - var fileByte = new byte[wmfFile.Length]; - wmfFile.Read(fileByte, 0, fileByte.Length); - wmfFile.Close(); - - // 对头22个字节进行判断 - Debug.WriteLine((fileByte[0] & 0xff) - + "\t" + (fileByte[1] & 0xff) - + "\t" + (fileByte[2] & 0xff) - + "\t" + (fileByte[3] & 0xff)); - - // 微软的wmf文件分为两种一种是标准的图元文件, - // 一种是活动式图元文件,活动式图元文件 与 标准的图元文件 的主要区别是, - // 活动式图元文件包含了图像的原始大小和缩放信息. - if (fileByte.Length < 5) - throw new IOException("无法校验文件签名:" + wmfFile); - - // 对开始的四个字节进行校验 d7 cd c6 9a (WMF活动式图元文件签名) - if (((fileByte[0] & 0xff) != 215) || - ((fileByte[1] & 0xff) != 205) || - ((fileByte[2] & 0xff) != 198) || - ((fileByte[3] & 0xff) != 154)) - { - fileByte = null; - throw new IOException("此文件不为活动式wmf文件:" + wmfFile); - } - - // 验证文件校验位 - int index = 0; - for (int i = 0; i < 20; i += 2) - { - if (i == 0) - index = ((fileByte[i + 1] & 0xff) << 8) | (fileByte[i] & 0xff); - else if (i < 19) - index ^= ((fileByte[i + 1] & 0xff) << 8) | (fileByte[i] & 0xff); - } - if (index != (((fileByte[21] & 0xff) << 8) | (fileByte[20] & 0xff))) - { - fileByte = null; - throw new IOException("此文件已损坏!" + wmfFile); - } - return fileByte; - } } \ No newline at end of file diff --git a/src/IFoxCAD.Cad.Shared/Copyclip/WindowsAPI.cs b/src/IFoxCAD.Cad.Shared/Copyclip/WindowsAPI.cs index c90e55a..9972818 100644 --- a/src/IFoxCAD.Cad.Shared/Copyclip/WindowsAPI.cs +++ b/src/IFoxCAD.Cad.Shared/Copyclip/WindowsAPI.cs @@ -123,20 +123,21 @@ public static bool GlobalLockTask(IntPtr data, Action task) /// /// byte数组 /// 返回的结构体 + /// 返回的结构大小 /// 转换后的结构体 - public static bool BytesToStuct(byte[] bytes, out T? obj) + public static bool BytesToStruct(byte[] bytes, out T? obj, out int typeSize) { obj = default; var tt = typeof(T); // 得到结构体的大小 - int size = Marshal.SizeOf(tt); - if (size > bytes.Length) + typeSize = Marshal.SizeOf(tt); + if (typeSize > bytes.Length) return false; // 分配结构体大小的内存空间 - IntPtr structPtr = Marshal.AllocHGlobal(size); + IntPtr structPtr = Marshal.AllocHGlobal(typeSize); // 将byte数组拷到分配好的内存空间 - Marshal.Copy(bytes, 0, structPtr, size); + Marshal.Copy(bytes, 0, structPtr, typeSize); // 将内存空间转换为目标结构体 obj = (T)Marshal.PtrToStructure(structPtr, tt); // 释放内存空间 @@ -147,7 +148,7 @@ public static bool BytesToStuct(byte[] bytes, out T? obj) /// /// 结构体转byte数组 /// - /// 要转换的结构体 + /// 要转换的结构体 /// 转换后的byte数组 public static byte[] StructToBytes(object structObj) { diff --git a/tests/TestShared/Copyclip.cs b/tests/TestShared/Copyclip.cs index ab348d0..a01f85b 100644 --- a/tests/TestShared/Copyclip.cs +++ b/tests/TestShared/Copyclip.cs @@ -267,13 +267,14 @@ void Copy(bool getPoint, bool isEraseSsget = false) #region 写入 WMF 数据 var wmf = Path.ChangeExtension(cadClipType.File, "wmf"); + //wmf指针 + IntPtr wmfMeta = IntPtr.Zero; + int a1 = 1; if (a1 == 1) { - // 导出的可能只是wmf其中一个字段 - // cad命令 wmfin:不能导入 c# Graphics类 自绘的 - // https://www.cnblogs.com/5iedu/p/4706327.html Env.Editor.ExportWMF(wmf, idArray); + wmfMeta = EmfTool.CadGetMetafile(wmf); } if (a1 == 2) { @@ -282,51 +283,10 @@ void Copy(bool getPoint, bool isEraseSsget = false) WindowsAPI.GetClientRect(doc.Window.Handle, out IntRect rcClient); int width = rcClient.Right - rcClient.Left; int height = rcClient.Bottom - rcClient.Top; - EmfTool.Export(wmf, width, height); - } - - // 这里能够读出 cad wmf 签名 - { - var javaByte = WMF_java.OpinionHead(wmf); - // 此处转失败 - // WmfStr WmfStr = new(); - // bool ja = WindowsAPI.BytesToStuct(javaByte, out WmfStr); - } - - //wmf指针 - IntPtr wmfMeta = IntPtr.Zero; - int a2 = 1; - if (a2 == 1) - { - wmfMeta = GetMetafile(wmf); - } - if (a2 == 2) - { - wmfMeta = WindowsAPI.CreateFile(wmf, - FileAccess.Read, FileShare.ReadWrite, - IntPtr.Zero, FileMode.Open, - FileAttributes.Normal, IntPtr.Zero); - } - if (a2 == 3) - { - // 不成功 - using FileStream wmfFile = new(wmf, FileMode.Open, FileAccess.Read, FileShare.ReadWrite); // FileShare才能进c盘 - var fileByte = new byte[wmfFile.Length]; - wmfFile.Read(fileByte, 0, fileByte.Length); - if (fileByte.Length > 0) - { - wmfMeta = Marshal.AllocHGlobal(fileByte.Length); - Marshal.Copy(fileByte, 0, wmfMeta, fileByte.Length); - } - } + EmfTool.Export(wmf, width, height);//cad的命令wmfin:不能导入c#自绘的 - if (wmfMeta == IntPtr.Zero) - { - var errorNum = WindowsAPI.GetLastError(); - if (errorNum == 6) - Debug.WriteLine("WMF错误:句柄无效!"); - else - Debug.WriteLine("WMF错误:" + errorNum); + //c#方法,但是它读取不了cad的wmf + wmfMeta = EmfTool.GetMetafile(wmf); } #endregion @@ -347,7 +307,6 @@ void Copy(bool getPoint, bool isEraseSsget = false) // ClipTool.SetClipboardData((uint)ClipboardFormat.CF_BITMAP, bitmapHandle); //}); - // TODO 写入wmf if (wmfMeta != IntPtr.Zero) { ClipTool.SetClipboardData((uint)ClipboardFormat.CF_ENHMETAFILE, wmfMeta); @@ -355,15 +314,6 @@ void Copy(bool getPoint, bool isEraseSsget = false) } }, true); - if (a2 == 2) - { - WindowsAPI.CloseHandle(wmfMeta); - } - if (a2 == 3) - { - Marshal.FreeHGlobal(wmfMeta); - } - // 成功拷贝就删除上一次的临时文件 if (getFlag) Terminate(); @@ -375,23 +325,6 @@ void Copy(bool getPoint, bool isEraseSsget = false) _delFile.Add(wmf); } - static IntPtr GetMetafile(string wmf) - { - var hEMF2 = IntPtr.Zero; - - // 这是c#写入wmf流程 - // FileShare才能进c盘 - using FileStream wmfFile = new(wmf, FileMode.Open, FileAccess.Read, FileShare.ReadWrite); - using Metafile mf = new(wmfFile); - var hEMF = mf.GetHenhmetafile(); - if (hEMF != IntPtr.Zero) - { - hEMF2 = EmfTool.CopyEnhMetaFile(hEMF, null);// 这句: 句柄无效..cad的wmf文件不识别 - //EmfTool.DeleteEnhMetaFile(hEMF);//托管类应该是封装好的 - } - return hEMF2; - } - /// /// 粘贴 @@ -517,6 +450,16 @@ void Paste(bool isBlock) _bytes.Add(buffer); } }); + + //{ + // var size = Marshal.SizeOf(typeof(PlaceableMetaHeader)); + // if (size > 0) + // { + // var buffer = new byte[size]; + // Marshal.Copy(hMem, buffer, 0, buffer.Length);// 将剪贴板数据保存到自定义字节数组 + // _bytes.Add(buffer); + // } + //} }, false); #else try -- Gitee From 51c0221b6cceca220cc2be3f0bfff7adaa3667ca Mon Sep 17 00:00:00 2001 From: liuqihong <540762622@qq.com> Date: Sun, 2 Oct 2022 03:54:40 +0800 Subject: [PATCH 536/675] =?UTF-8?q?=E4=BF=AE=E6=AD=A3=E5=81=8F=E7=A7=BB?= =?UTF-8?q?=E5=80=BC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/IFoxCAD.Cad.Shared/Copyclip/EmfTool.cs | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/IFoxCAD.Cad.Shared/Copyclip/EmfTool.cs b/src/IFoxCAD.Cad.Shared/Copyclip/EmfTool.cs index 3bc0b15..47c270c 100644 --- a/src/IFoxCAD.Cad.Shared/Copyclip/EmfTool.cs +++ b/src/IFoxCAD.Cad.Shared/Copyclip/EmfTool.cs @@ -258,12 +258,13 @@ public static IntPtr CadGetMetafile(string wmf) wmfFile.Read(fileByte, 0, fileByte.Length); wmfFile.Close(); - bool bts = WindowsAPI.BytesToStruct(fileByte, out PlaceableMetaHeader structWMF, out int iOffset); + bool bts = WindowsAPI.BytesToStruct(fileByte, out PlaceableMetaHeader sWMF, out int sWMFsize); if (!bts) throw new IOException("失败:类型转换,路径:" + wmfFile); - if (structWMF.IsActivity) - iOffset = Marshal.SizeOf(typeof(PlaceableMetaHeader)); + int iOffset = 0; + if (sWMF.IsActivity) + iOffset = sWMFsize; // byte[] 指针偏移 https://blog.csdn.net/i0048egi/article/details/56063494 GCHandle hObject1 = GCHandle.Alloc(fileByte.Skip(iOffset).ToArray(), GCHandleType.Pinned); -- Gitee From 2ecbe432c321dfcda92ac162f311ddf1602ffce7 Mon Sep 17 00:00:00 2001 From: liuqihong <540762622@qq.com> Date: Sun, 2 Oct 2022 05:26:53 +0800 Subject: [PATCH 537/675] =?UTF-8?q?=E4=BB=BB=E5=8A=A1221002=E5=8F=82?= =?UTF-8?q?=E6=95=B0=E6=B5=8B=E8=AF=95=E4=B8=8D=E5=AF=B9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/IFoxCAD.Cad.Shared/Copyclip/BitmapTool.cs | 3 + src/IFoxCAD.Cad.Shared/Copyclip/EmfTool.cs | 102 ++++++++++-------- .../Copyclip/TagClipboardInfo.cs | 35 +++--- src/IFoxCAD.Cad.Shared/Copyclip/WindowsAPI.cs | 92 +++++++++++----- tests/TestShared/Copyclip.cs | 13 ++- 5 files changed, 152 insertions(+), 93 deletions(-) diff --git a/src/IFoxCAD.Cad.Shared/Copyclip/BitmapTool.cs b/src/IFoxCAD.Cad.Shared/Copyclip/BitmapTool.cs index cf3f0a8..3036ac2 100644 --- a/src/IFoxCAD.Cad.Shared/Copyclip/BitmapTool.cs +++ b/src/IFoxCAD.Cad.Shared/Copyclip/BitmapTool.cs @@ -25,6 +25,9 @@ public class BitmapTool [DllImport("user32.dll", SetLastError = true)] public static extern IntPtr GetDC(IntPtr hWnd); + [DllImport("user32.dll")] + public static extern bool ReleaseDC(IntPtr hWnd, IntPtr hDC); + /// /// 创建DC /// diff --git a/src/IFoxCAD.Cad.Shared/Copyclip/EmfTool.cs b/src/IFoxCAD.Cad.Shared/Copyclip/EmfTool.cs index 47c270c..a297d02 100644 --- a/src/IFoxCAD.Cad.Shared/Copyclip/EmfTool.cs +++ b/src/IFoxCAD.Cad.Shared/Copyclip/EmfTool.cs @@ -83,6 +83,18 @@ public struct StandardMetaRecord } +// https://learn.microsoft.com/en-us/windows/win32/api/wingdi/ns-wingdi-metafilepict +// http://www.cppblog.com/zwp/archive/2012/02/25/60225.html +[Serializable] +[StructLayout(LayoutKind.Sequential, Pack = 2)] +public struct METAFILEPICT +{ + public int mm; + public int xExt; + public int yExt; + public IntPtr hMF; //内存图元文件的句柄 +} + public static class EmfTool { // https://zhidao.baidu.com/question/646739770512964165/answer/1616737219.html?qq-pf-to=pcqq.c2c @@ -116,19 +128,10 @@ public static class EmfTool /// [DllImport("gdi32.dll", EntryPoint = "SetWinMetaFileBits")] public static extern IntPtr SetWinMetaFileBits(uint nSize, IntPtr lpMeta16Data, IntPtr hdcRef, IntPtr lpMFP); - /* - * [DllImport("gdi32.dll", EntryPoint = "SetWinMetaFileBits")] - * public static extern int SetWinMetaFileBits(uint nSize, ref byte lpbBuffer, IntPtr hdcRef, ref METAFILEPICT lpmfp); - * // https://learn.microsoft.com/en-us/windows/win32/api/wingdi/ns-wingdi-metafilepict - * public struct METAFILEPICT - * { - * public int mm; - * public int xExt; - * public int yExt; - * public HMETAFILE hMF; //内存图元文件的句柄 - * } - */ + [DllImport("gdi32.dll", EntryPoint = "SetWinMetaFileBits")] + public static extern int SetWinMetaFileBits(uint nSize, ref byte lpbBuffer, IntPtr hdcRef, ref METAFILEPICT lpmfp); + */ /// /// 获取矢量图的byte @@ -243,39 +246,54 @@ public static void ToMetafile(byte[] data, Func task) /// /// cad的wmf文件解析,转为emf
    ///
    - /// 文件路径 + /// 文件路径 /// emf指针,可以直接写入剪贴板 /// - public static IntPtr CadGetMetafile(string wmf) + public static IntPtr CadGetMetafile(string cadWmfFile, IntPtr hWnd, IntRect rect) { - using FileStream wmfFile = new(wmf, FileMode.Open, FileAccess.Read, FileShare.ReadWrite); // FileShare才能进c盘 - if (wmfFile.Length == 0) - throw new IOException("文件字节0长:" + wmfFile); - if (wmfFile.Length < 5) - throw new IOException("无法校验文件签名:" + wmfFile); + using FileStream file = new(cadWmfFile, FileMode.Open, FileAccess.Read, FileShare.ReadWrite); // FileShare才能进c盘 + if (file.Length == 0) + throw new IOException("文件字节0长:" + file); + if (file.Length < 5) + throw new IOException("无法校验文件签名:" + file); - var fileByte = new byte[wmfFile.Length]; - wmfFile.Read(fileByte, 0, fileByte.Length); - wmfFile.Close(); + var fileByte = new byte[file.Length]; + file.Read(fileByte, 0, fileByte.Length); + file.Close(); bool bts = WindowsAPI.BytesToStruct(fileByte, out PlaceableMetaHeader sWMF, out int sWMFsize); if (!bts) - throw new IOException("失败:类型转换,路径:" + wmfFile); - - int iOffset = 0; - if (sWMF.IsActivity) - iOffset = sWMFsize; - - // byte[] 指针偏移 https://blog.csdn.net/i0048egi/article/details/56063494 - GCHandle hObject1 = GCHandle.Alloc(fileByte.Skip(iOffset).ToArray(), GCHandleType.Pinned); - - //TODO 这里两个0值的设置没有和cad一样有个矩形 - var hEMF = SetWinMetaFileBits((uint)(fileByte.Length - iOffset), - hObject1.AddrOfPinnedObject(), - IntPtr.Zero, IntPtr.Zero); - if (hObject1.IsAllocated) - hObject1.Free(); + throw new IOException("失败:类型转换,路径:" + file); + var hDC = BitmapTool.GetDC(hWnd); + IntPtr hEMF = IntPtr.Zero; + try + { + var mpType = new METAFILEPICT + { + mm = 0,//MM_ANISOTROPIC 这里是随便填的不知道 MM_ANISOTROPIC 是什么值 + xExt = rect.Left, + yExt = rect.Top, + hMF = IntPtr.Zero + }; + int iOffset = 0; + if (sWMF.IsActivity) + iOffset = sWMFsize; + // byte[] 指针偏移 https://blog.csdn.net/i0048egi/article/details/56063494 + var arr = fileByte.Skip(iOffset).ToArray(); + GCHandle hObject1 = GCHandle.Alloc(arr, GCHandleType.Pinned); + + WindowsAPI.StructToPtr(mpType, mpPtr => { + //TODO 任务221002 这里末尾两个参数不对,没有跟cad一样有个矩形作为边界 + hEMF = SetWinMetaFileBits((uint)arr.Length, hObject1.AddrOfPinnedObject(), hDC, mpPtr); + if (hObject1.IsAllocated) + hObject1.Free(); + }); + } + finally + { + BitmapTool.ReleaseDC(IntPtr.Zero, hDC); + } if (hEMF == IntPtr.Zero) throw new IOException("失败:" + nameof(SetWinMetaFileBits)); return hEMF; @@ -285,18 +303,18 @@ public static IntPtr CadGetMetafile(string wmf) /// /// c#获取wmf方式 /// - /// + /// /// - public static IntPtr GetMetafile(string wmf) + public static IntPtr GetMetafile(string wmfSave) { - using FileStream wmfFile = new(wmf, FileMode.Open, FileAccess.Read, FileShare.ReadWrite); // FileShare才能进c盘 + using FileStream file = new(wmfSave, FileMode.Open, FileAccess.Read, FileShare.ReadWrite); // FileShare才能进c盘 var hEMF2 = IntPtr.Zero; - using Metafile mf = new(wmfFile); + using Metafile mf = new(file); var hEMF = mf.GetHenhmetafile(); if (hEMF != IntPtr.Zero) hEMF2 = CopyEnhMetaFile(hEMF, null);// 这句: 句柄无效..cad的wmf文件不识别 - //EmfTool.DeleteEnhMetaFile(hEMF);//托管类应该是封装好的 + //EmfTool.DeleteEnhMetaFile(hEMF);//托管类应该是封装好的 return hEMF2; } diff --git a/src/IFoxCAD.Cad.Shared/Copyclip/TagClipboardInfo.cs b/src/IFoxCAD.Cad.Shared/Copyclip/TagClipboardInfo.cs index df3f92d..a3a6a71 100644 --- a/src/IFoxCAD.Cad.Shared/Copyclip/TagClipboardInfo.cs +++ b/src/IFoxCAD.Cad.Shared/Copyclip/TagClipboardInfo.cs @@ -344,13 +344,14 @@ public static bool OpenClipboardTask(Action> action, bool isSetClip EmptyClipboard(); action.Invoke(freeDatas); } + catch (Exception e) + { + Debug.WriteLine(e.Message); + } finally { for (int i = 0; i < freeDatas.Count; i++) - { - //WindowsAPI.GlobalFree(freeDatas[i]); Marshal.FreeHGlobal(freeDatas[i]); - } if (openFlag) CloseClipboard(); } @@ -363,7 +364,7 @@ public static bool OpenClipboardTask(Action> action, bool isSetClip /// 剪贴板的索引名 public static bool GetClipboard(string clipKey, out T? tag) { - bool flag = false; + bool locked = false; T? result = default; OpenClipboardTask(lst => { @@ -372,14 +373,14 @@ public static bool GetClipboard(string clipKey, out T? tag) var clipTypeData = GetClipboardData(clipKeyFormat); // 剪贴板的数据拷贝进去结构体中,会依照数据长度进行拷贝 - flag = WindowsAPI.GlobalLockTask(clipTypeData, ptr => { + locked = WindowsAPI.GlobalLockTask(clipTypeData, ptr => { // 非托管内存块->托管对象 result = (T)Marshal.PtrToStructure(ptr, typeof(T)); }); }, false); tag = result; - return flag; + return locked; } /// @@ -403,17 +404,11 @@ public static bool GetClipboardFormat(string clipKey, T clipType, */ clipKeyFormat = RegisterClipboardFormat(clipKey); - - // const uint GMEM_MOVEABLE = 0x0002;//剪贴板需要的类型 - // clipTypeData = WindowsAPI.GlobalAlloc(GMEM_MOVEABLE, Marshal.SizeOf(typeof(T))); - clipTypeData = Marshal.AllocHGlobal(Marshal.SizeOf(typeof(T))); - - bool lockFlag = WindowsAPI.GlobalLockTask(clipTypeData, ptr => { - // 托管对象->非托管内存块 - Marshal.StructureToPtr(clipType, ptr, true); - }); - if (!lockFlag || clipTypeData == IntPtr.Zero) - return false; + IntPtr res = IntPtr.Zero; + WindowsAPI.StructToPtr(clipType, clipPtr => { + res = clipPtr; + }, false); + clipTypeData = res; return true; } } @@ -449,9 +444,9 @@ public static bool SaveClip() break; _formats.Add(cf); - IntPtr hMem = ClipTool.GetClipboardData(cf); - WindowsAPI.GlobalLockTask(hMem, prt => { - uint size = WindowsAPI.GlobalSize(hMem); + IntPtr clipTypeData = ClipTool.GetClipboardData(cf); + var locked = WindowsAPI.GlobalLockTask(clipTypeData, prt => { + uint size = WindowsAPI.GlobalSize(clipTypeData); if (size > 0) { var buffer = new byte[size]; diff --git a/src/IFoxCAD.Cad.Shared/Copyclip/WindowsAPI.cs b/src/IFoxCAD.Cad.Shared/Copyclip/WindowsAPI.cs index 9972818..84bc7a0 100644 --- a/src/IFoxCAD.Cad.Shared/Copyclip/WindowsAPI.cs +++ b/src/IFoxCAD.Cad.Shared/Copyclip/WindowsAPI.cs @@ -1,6 +1,7 @@ namespace IFoxCAD.Cad; using System; +using System.Diagnostics; using System.Threading; public class WindowsAPI @@ -31,20 +32,20 @@ public class WindowsAPI +#if true2 [DllImport("kernel32.dll", CharSet = CharSet.Auto, SetLastError = true)] public static extern IntPtr CreateFile( - [MarshalAs(UnmanagedType.LPTStr)] string filename, - [MarshalAs(UnmanagedType.U4)] FileAccess access, - [MarshalAs(UnmanagedType.U4)] FileShare share, - IntPtr securityAttributes, // optional SECURITY_ATTRIBUTES struct or IntPtr.Zero - [MarshalAs(UnmanagedType.U4)] FileMode creationDisposition, - [MarshalAs(UnmanagedType.U4)] FileAttributes flagsAndAttributes, - IntPtr templateFile); + [MarshalAs(UnmanagedType.LPTStr)] string filename, + [MarshalAs(UnmanagedType.U4)] FileAccess access, + [MarshalAs(UnmanagedType.U4)] FileShare share, + IntPtr securityAttributes, // optional SECURITY_ATTRIBUTES struct or IntPtr.Zero + [MarshalAs(UnmanagedType.U4)] FileMode creationDisposition, + [MarshalAs(UnmanagedType.U4)] FileAttributes flagsAndAttributes, + IntPtr templateFile); [DllImport("kernel32", SetLastError = true)] public static extern bool CloseHandle(IntPtr hObject);/* 对象句柄 CreateFile*/ - - +#endif /// /// 锁定内存 @@ -88,7 +89,6 @@ public static extern IntPtr CreateFile( public static extern uint GlobalSize(IntPtr hMem); - /// /// 锁定和释放内存 /// @@ -128,9 +128,9 @@ public static bool GlobalLockTask(IntPtr data, Action task) public static bool BytesToStruct(byte[] bytes, out T? obj, out int typeSize) { obj = default; - var tt = typeof(T); + var structType = typeof(T); // 得到结构体的大小 - typeSize = Marshal.SizeOf(tt); + typeSize = Marshal.SizeOf(structType); if (typeSize > bytes.Length) return false; @@ -139,7 +139,7 @@ public static bool BytesToStruct(byte[] bytes, out T? obj, out int typeSize) // 将byte数组拷到分配好的内存空间 Marshal.Copy(bytes, 0, structPtr, typeSize); // 将内存空间转换为目标结构体 - obj = (T)Marshal.PtrToStructure(structPtr, tt); + obj = (T)Marshal.PtrToStructure(structPtr, structType); // 释放内存空间 Marshal.FreeHGlobal(structPtr); return true; @@ -149,24 +149,62 @@ public static bool BytesToStruct(byte[] bytes, out T? obj, out int typeSize) /// 结构体转byte数组 /// /// 要转换的结构体 - /// 转换后的byte数组 - public static byte[] StructToBytes(object structObj) + public static byte[] StructToBytes(object? structObj) { // 得到结构体的大小 - int size = Marshal.SizeOf(structObj); - // 创建byte数组 - byte[] bytes = new byte[size]; - // 分配结构体大小的内存空间 - IntPtr structPtr = Marshal.AllocHGlobal(size); - // 将结构体拷到分配好的内存空间 - Marshal.StructureToPtr(structObj, structPtr, false); - //从 内存空间拷到byte数组 - Marshal.Copy(structPtr, bytes, 0, size); - // 释放内存空间 - Marshal.FreeHGlobal(structPtr); - // 返回byte数组 + int typeSize = Marshal.SizeOf(structObj); + // 从内存空间拷到byte数组 + byte[] bytes = new byte[typeSize]; + + StructToPtr(structObj, structPtr => { + Marshal.Copy(structPtr, bytes, 0, typeSize); + }); return bytes; } + + /// + /// 结构体转指针 + /// + /// 要转换的结构体 + /// 输出指针 + /// 释放申请的内存 + /// StructureToPtr的参数 + /// + public static void StructToPtr(object? structObj, + Action? task = null, + bool freeHGlobal = true, + bool fDeleteOld = true) + { + if (structObj == null) + throw new ArgumentNullException(nameof(structObj)); + + // 得到结构体的大小 + int typeSize = Marshal.SizeOf(structObj); + + // 分配结构体大小的内存空间 + // 此处需要锁定吗? + IntPtr structPtr = Marshal.AllocHGlobal(typeSize); + bool locked = false; + try + { + locked = GlobalLockTask(structPtr, ptr => { + // 将结构体拷到分配好的内存空间 + Marshal.StructureToPtr(structObj, ptr, fDeleteOld); + task?.Invoke(ptr); + }); + } + catch (Exception e) + { + Marshal.FreeHGlobal(structPtr); + structPtr = IntPtr.Zero; + Debug.WriteLine(e.Message); + } + finally + { + if (freeHGlobal && structPtr != IntPtr.Zero) + Marshal.FreeHGlobal(structPtr); + } + } } diff --git a/tests/TestShared/Copyclip.cs b/tests/TestShared/Copyclip.cs index a01f85b..c4b70e3 100644 --- a/tests/TestShared/Copyclip.cs +++ b/tests/TestShared/Copyclip.cs @@ -274,7 +274,12 @@ void Copy(bool getPoint, bool isEraseSsget = false) if (a1 == 1) { Env.Editor.ExportWMF(wmf, idArray); - wmfMeta = EmfTool.CadGetMetafile(wmf); + + var hWnd = doc.Window.Handle; + WindowsAPI.GetClientRect(hWnd, out IntRect rcClient); + int width = rcClient.Right - rcClient.Left; + int height = rcClient.Bottom - rcClient.Top; + wmfMeta = EmfTool.CadGetMetafile(wmf, hWnd, rcClient); } if (a1 == 2) { @@ -303,9 +308,9 @@ void Copy(bool getPoint, bool isEraseSsget = false) // c# cad截图 https://www.cnblogs.com/shangdishijiao/p/15166499.html // 写入BMP位图...这是截图,不是WMF转BMP,不对 - //BitmapTool.CaptureWndImage(doc.Window.Handle, bitmapHandle => { - // ClipTool.SetClipboardData((uint)ClipboardFormat.CF_BITMAP, bitmapHandle); - //}); + // BitmapTool.CaptureWndImage(doc.Window.Handle, bitmapHandle => { + // ClipTool.SetClipboardData((uint)ClipboardFormat.CF_BITMAP, bitmapHandle); + // }); if (wmfMeta != IntPtr.Zero) { -- Gitee From b5e1f783f38bb228d64d8b02977b8f785afec2cd Mon Sep 17 00:00:00 2001 From: liuqihong <540762622@qq.com> Date: Sun, 2 Oct 2022 17:12:49 +0800 Subject: [PATCH 538/675] =?UTF-8?q?=E8=AF=BB=E5=8F=96emf=E5=A4=B1=E8=B4=A5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/IFoxCAD.Cad.Shared/Copyclip/EmfTool.cs | 202 ++++++++++++------ .../Copyclip/TagClipboardInfo.cs | 13 ++ src/IFoxCAD.Cad.Shared/Copyclip/WindowsAPI.cs | 18 -- tests/TestShared/Copyclip.cs | 94 ++++---- 4 files changed, 187 insertions(+), 140 deletions(-) diff --git a/src/IFoxCAD.Cad.Shared/Copyclip/EmfTool.cs b/src/IFoxCAD.Cad.Shared/Copyclip/EmfTool.cs index a297d02..7d3729b 100644 --- a/src/IFoxCAD.Cad.Shared/Copyclip/EmfTool.cs +++ b/src/IFoxCAD.Cad.Shared/Copyclip/EmfTool.cs @@ -10,7 +10,6 @@ using System.Security.Cryptography; - // DWORD == uint // WORD == ushort // LONG == int @@ -21,14 +20,14 @@ * Console.WriteLine(Marshal.SizeOf(typeof(StandardMetaRecord))); */ -[Serializable] -[StructLayout(LayoutKind.Sequential, Pack = 2)] -public struct WmfStr -{ - public PlaceableMetaHeader Placeable; - public WindowsMetaHeader Wmfhead; - public StandardMetaRecord Wmfrecord; -} +//[Serializable] +//[StructLayout(LayoutKind.Sequential, Pack = 2)] +//public struct WmfStr +//{ +// public PlaceableMetaHeader Placeable; +// public WindowsMetaHeader Wmfhead; +// public StandardMetaRecord Wmfrecord; +//} //WMF 文件格式: //https://blog.51cto.com/chenyanxi/803247 @@ -56,6 +55,56 @@ public struct PlaceableMetaHeader /// 是活动式图元文件 /// public bool IsActivity => Key == 0x9AC6CDD7; + + /// + /// wmf转为emf
    + ///
    + /// 文件路径 + /// 成功emf指针,可以直接写入剪贴板;失败0 + /// + public static IntPtr Wmf2Emf(string wmfFile) + { + using FileStream file = new(wmfFile, FileMode.Open, FileAccess.Read, FileShare.ReadWrite); // FileShare才能进c盘 + if (file.Length == 0) + throw new IOException("文件字节0长:" + file); + if (file.Length < 5) + throw new IOException("无法校验文件签名:" + file); + + var fileByte = new byte[file.Length]; + file.Read(fileByte, 0, fileByte.Length); + file.Close(); + + bool bts = WindowsAPI.BytesToStruct(fileByte, out PlaceableMetaHeader sWMF, out int sWMFsize); + if (!bts) + throw new IOException("失败:类型转换,路径:" + file); + + var mpType = new MetaFilePict + { + mm = MappingModes.MM_ANISOTROPIC, + xExt = sWMF.Right - sWMF.Left, + yExt = sWMF.Bottom - sWMF.Top, + hMF = IntPtr.Zero + }; + + IntPtr hEMF = IntPtr.Zero; + int iOffset = 0; + if (sWMF.IsActivity) + iOffset = sWMFsize; + + // byte[] 指针偏移 + var arr = fileByte.Skip(iOffset).ToArray(); + GCHandle arrHandle = GCHandle.Alloc(arr, GCHandleType.Pinned); + + WindowsAPI.StructToPtr(mpType, mpPtr => { + hEMF = EmfTool.SetWinMetaFileBits( + (uint)fileByte.Length, arrHandle.AddrOfPinnedObject(), IntPtr.Zero, mpPtr); + }); + + if (arrHandle.IsAllocated) + arrHandle.Free(); + + return hEMF; + } } //紧接文件缩放信息的是 WMFHEAD, 18字节 @@ -87,14 +136,51 @@ public struct StandardMetaRecord // http://www.cppblog.com/zwp/archive/2012/02/25/60225.html [Serializable] [StructLayout(LayoutKind.Sequential, Pack = 2)] -public struct METAFILEPICT +public struct MetaFilePict { - public int mm; + public MappingModes mm; public int xExt; public int yExt; public IntPtr hMF; //内存图元文件的句柄 } +public enum MappingModes +{ + MM_TEXT = 1, + MM_LOMETRIC = 2, + MM_HIMETRIC = 3, + MM_LOENGLISH = 4, + MM_HIENGLISH = 5, + MM_TWIPS = 6, + MM_ISOTROPIC = 7, + MM_ANISOTROPIC = 8, +} + + +[StructLayout(LayoutKind.Sequential)] +public struct ENHMETAHEADER +{ + public uint iType; + public int nSize; + public IntRect rclBounds; + public IntRect rclFrame; + public uint dSignature; + public uint nVersion; + public uint nBytes; + public uint nRecords; + public ushort nHandles; + public ushort sReserved; + public uint nDescription; + public uint offDescription; + public uint nPalEntries; + public IntSize szlDevice; + public IntSize szlMillimeters; + public uint cbPixelFormat; + public uint offPixelFormat; + public uint bOpenGL; + public IntSize szlMicrometers; +} + public static class EmfTool { // https://zhidao.baidu.com/question/646739770512964165/answer/1616737219.html?qq-pf-to=pcqq.c2c @@ -243,71 +329,14 @@ public static void ToMetafile(byte[] data, Func task) } - /// - /// cad的wmf文件解析,转为emf
    - ///
    - /// 文件路径 - /// emf指针,可以直接写入剪贴板 - /// - public static IntPtr CadGetMetafile(string cadWmfFile, IntPtr hWnd, IntRect rect) - { - using FileStream file = new(cadWmfFile, FileMode.Open, FileAccess.Read, FileShare.ReadWrite); // FileShare才能进c盘 - if (file.Length == 0) - throw new IOException("文件字节0长:" + file); - if (file.Length < 5) - throw new IOException("无法校验文件签名:" + file); - - var fileByte = new byte[file.Length]; - file.Read(fileByte, 0, fileByte.Length); - file.Close(); - - bool bts = WindowsAPI.BytesToStruct(fileByte, out PlaceableMetaHeader sWMF, out int sWMFsize); - if (!bts) - throw new IOException("失败:类型转换,路径:" + file); - - var hDC = BitmapTool.GetDC(hWnd); - IntPtr hEMF = IntPtr.Zero; - try - { - var mpType = new METAFILEPICT - { - mm = 0,//MM_ANISOTROPIC 这里是随便填的不知道 MM_ANISOTROPIC 是什么值 - xExt = rect.Left, - yExt = rect.Top, - hMF = IntPtr.Zero - }; - int iOffset = 0; - if (sWMF.IsActivity) - iOffset = sWMFsize; - // byte[] 指针偏移 https://blog.csdn.net/i0048egi/article/details/56063494 - var arr = fileByte.Skip(iOffset).ToArray(); - GCHandle hObject1 = GCHandle.Alloc(arr, GCHandleType.Pinned); - - WindowsAPI.StructToPtr(mpType, mpPtr => { - //TODO 任务221002 这里末尾两个参数不对,没有跟cad一样有个矩形作为边界 - hEMF = SetWinMetaFileBits((uint)arr.Length, hObject1.AddrOfPinnedObject(), hDC, mpPtr); - if (hObject1.IsAllocated) - hObject1.Free(); - }); - } - finally - { - BitmapTool.ReleaseDC(IntPtr.Zero, hDC); - } - if (hEMF == IntPtr.Zero) - throw new IOException("失败:" + nameof(SetWinMetaFileBits)); - return hEMF; - } - - /// /// c#获取wmf方式 /// - /// + /// /// - public static IntPtr GetMetafile(string wmfSave) + public static IntPtr GetMetafile(string wmfFile) { - using FileStream file = new(wmfSave, FileMode.Open, FileAccess.Read, FileShare.ReadWrite); // FileShare才能进c盘 + using FileStream file = new(wmfFile, FileMode.Open, FileAccess.Read, FileShare.ReadWrite); // FileShare才能进c盘 var hEMF2 = IntPtr.Zero; using Metafile mf = new(file); @@ -319,6 +348,18 @@ public static IntPtr GetMetafile(string wmfSave) } + /* + * // 这是c#写入wmf流程 + * // c#画的wmf格式是可以的...用这样方式生成的就是可以写剪贴板 + * WindowsAPI.GetClientRect(doc.Window.Handle, out IntRect rcClient); + * int width = rcClient.Right - rcClient.Left; + * int height = rcClient.Bottom - rcClient.Top; + * EmfTool.Export(wmf, width, height);//cad的命令wmfin:不能导入c#自绘的 + * + * //c#方法,但是它读取不了cad的wmf + * wmfMeta = EmfTool.GetMetafile(wmf); + */ + /// /// 导出为 Emf 或 Wmf 文件 /// 相关链接 @@ -377,4 +418,23 @@ static void Draw(Graphics g) g.DrawLine(new Pen(Color.Black, 0.1f), 110f, 110f, 220f, 25f); g.DrawString("剖面图", new Font("宋体", 9f), Brushes.Green, 220f, 20f); } + + /// + /// 返回对一个增强型图元文件的说明 + /// + /// 目标增强型图元文件的句柄 + /// lpszDescription缓冲区的长度 + /// 指定一个预先初始化好的字串缓冲区,准备随同图元文件说明载入。 + /// 参考CreateEnhMetaFile函数,了解增强型图元文件说明字串的具体格式 + /// + [DllImport("gdi32", EntryPoint = "GetEnhMetaFileDescription")] + public static extern uint GetEnhMetaFileDescription( + uint hemf, + uint cchBuffer, + [MarshalAs(UnmanagedType.LPWStr)] StringBuilder lpszDescription + ); + + + [DllImport("gdi32", EntryPoint = "GetEnhMetaFileHeader")] + public static extern uint GetEnhMetaFileHeader(uint hemf, uint cbBuffer, IntPtr /*ENHMETAHEADER*/ lpemh); } \ No newline at end of file diff --git a/src/IFoxCAD.Cad.Shared/Copyclip/TagClipboardInfo.cs b/src/IFoxCAD.Cad.Shared/Copyclip/TagClipboardInfo.cs index a3a6a71..c83af23 100644 --- a/src/IFoxCAD.Cad.Shared/Copyclip/TagClipboardInfo.cs +++ b/src/IFoxCAD.Cad.Shared/Copyclip/TagClipboardInfo.cs @@ -179,6 +179,19 @@ public override string ToString() } +[StructLayout(LayoutKind.Sequential)] +public struct IntSize +{ + public int cx; + public int cy; + + public IntSize(int cx, int cy) + { + this.cx = cx; + this.cy = cy; + } +} + [Serializable] [StructLayout(LayoutKind.Sequential, Pack = 8)] [DebuggerDisplay("{DebuggerDisplay,nq}")] diff --git a/src/IFoxCAD.Cad.Shared/Copyclip/WindowsAPI.cs b/src/IFoxCAD.Cad.Shared/Copyclip/WindowsAPI.cs index 84bc7a0..f8224e6 100644 --- a/src/IFoxCAD.Cad.Shared/Copyclip/WindowsAPI.cs +++ b/src/IFoxCAD.Cad.Shared/Copyclip/WindowsAPI.cs @@ -30,23 +30,6 @@ public class WindowsAPI [DllImport("user32.dll", SetLastError = true)] public static extern uint GetWindowThreadProcessId(IntPtr hWnd, out uint lpdwProcessId); - - -#if true2 - [DllImport("kernel32.dll", CharSet = CharSet.Auto, SetLastError = true)] - public static extern IntPtr CreateFile( - [MarshalAs(UnmanagedType.LPTStr)] string filename, - [MarshalAs(UnmanagedType.U4)] FileAccess access, - [MarshalAs(UnmanagedType.U4)] FileShare share, - IntPtr securityAttributes, // optional SECURITY_ATTRIBUTES struct or IntPtr.Zero - [MarshalAs(UnmanagedType.U4)] FileMode creationDisposition, - [MarshalAs(UnmanagedType.U4)] FileAttributes flagsAndAttributes, - IntPtr templateFile); - - [DllImport("kernel32", SetLastError = true)] - public static extern bool CloseHandle(IntPtr hObject);/* 对象句柄 CreateFile*/ -#endif - /// /// 锁定内存 /// @@ -88,7 +71,6 @@ public static extern IntPtr CreateFile( [DllImport("kernel32.dll", SetLastError = true)] public static extern uint GlobalSize(IntPtr hMem); - /// /// 锁定和释放内存 /// diff --git a/tests/TestShared/Copyclip.cs b/tests/TestShared/Copyclip.cs index c4b70e3..9491894 100644 --- a/tests/TestShared/Copyclip.cs +++ b/tests/TestShared/Copyclip.cs @@ -4,11 +4,10 @@ namespace Test; using Autodesk.AutoCAD.DatabaseServices; -using System.Diagnostics; +using System; using System.Drawing.Imaging; using System.Threading; using System.Windows; -using static HarmonyLib.Code; /* * 0x01 (已完成) @@ -265,58 +264,35 @@ void Copy(bool getPoint, bool isEraseSsget = false) #endregion #region 写入 WMF 数据 - var wmf = Path.ChangeExtension(cadClipType.File, "wmf"); - - //wmf指针 + // 通过cad com导出wmf,再将wmf转为emf,然后才能写入剪贴板 IntPtr wmfMeta = IntPtr.Zero; - - int a1 = 1; - if (a1 == 1) - { - Env.Editor.ExportWMF(wmf, idArray); - - var hWnd = doc.Window.Handle; - WindowsAPI.GetClientRect(hWnd, out IntRect rcClient); - int width = rcClient.Right - rcClient.Left; - int height = rcClient.Bottom - rcClient.Top; - wmfMeta = EmfTool.CadGetMetafile(wmf, hWnd, rcClient); - } - if (a1 == 2) - { - // 这是c#写入wmf流程 - // c#画的wmf格式是可以的...用这样方式生成的就是可以写剪贴板 - WindowsAPI.GetClientRect(doc.Window.Handle, out IntRect rcClient); - int width = rcClient.Right - rcClient.Left; - int height = rcClient.Bottom - rcClient.Top; - EmfTool.Export(wmf, width, height);//cad的命令wmfin:不能导入c#自绘的 - - //c#方法,但是它读取不了cad的wmf - wmfMeta = EmfTool.GetMetafile(wmf); - } + var wmf = Path.ChangeExtension(cadClipType.File, "wmf"); + Env.Editor.ExportWMF(wmf, idArray); + wmfMeta = PlaceableMetaHeader.Wmf2Emf(wmf); #endregion - /// 必须一次性写入剪贴板,详见 OpenClipboardTask + // 必须一次性写入剪贴板,详见 OpenClipboardTask bool getFlag = false; ClipTool.OpenClipboardTask(free => { - getFlag = ClipTool.GetClipboardFormat( - ClipboardEnv.CadVer, cadClipType, - out uint cadClipFormat, out IntPtr cadClipData); + getFlag = ClipTool.GetClipboardFormat(ClipboardEnv.CadVer, cadClipType, + out uint cadClipFormat, out IntPtr cadClipData); // 写入cad图元 ClipTool.SetClipboardData(cadClipFormat, cadClipData); free.Add(cadClipData); - // c# cad截图 https://www.cnblogs.com/shangdishijiao/p/15166499.html - // 写入BMP位图...这是截图,不是WMF转BMP,不对 - // BitmapTool.CaptureWndImage(doc.Window.Handle, bitmapHandle => { - // ClipTool.SetClipboardData((uint)ClipboardFormat.CF_BITMAP, bitmapHandle); - // }); - + // 写入 wmf 使得在粘贴链接的时候可以用 if (wmfMeta != IntPtr.Zero) { ClipTool.SetClipboardData((uint)ClipboardFormat.CF_ENHMETAFILE, wmfMeta); EmfTool.DeleteEnhMetaFile(wmfMeta); } + + // c# cad截图 https://www.cnblogs.com/shangdishijiao/p/15166499.html + // 写入BMP位图...这是截图,不是WMF转BMP,不对 + // BitmapTool.CaptureWndImage(doc.Window.Handle, bitmapHandle => { + // ClipTool.SetClipboardData((uint)ClipboardFormat.CF_BITMAP, bitmapHandle); + // }); }, true); // 成功拷贝就删除上一次的临时文件 @@ -440,31 +416,47 @@ void Paste(bool isBlock) } #region 读取剪贴板WMF + +#if true3 + var cf_metafile = (uint)ClipboardFormat.CF_ENHMETAFILE; + var len = EmfTool.GetEnhMetaFileDescription(cf_metafile, 0, null!); + var desc = new StringBuilder(); //PTSTR desc = (PTSTR)malloc(sizeof(TCHAR) * (len + 1)); + len = EmfTool.GetEnhMetaFileDescription(cf_metafile, len, desc); + + var m_grid = new StringBuilder(); + m_grid.Append(desc); + + len = EmfTool.GetEnhMetaFileHeader(cf_metafile, 0, IntPtr.Zero); + IntPtr header = Marshal.AllocHGlobal((int)len); + len = EmfTool.GetEnhMetaFileHeader(cf_metafile, len, header); + + // 将内存空间转换为目标结构体 + var obj = (ENHMETAHEADER)Marshal.PtrToStructure(header, typeof(ENHMETAHEADER)); + m_grid.Append(obj.nRecords); + + Marshal.FreeHGlobal(header); +#endif + #if true2 // win32api 不成功 - ClipTool.OpenClipboardTask((freeDatas) => { + ClipTool.OpenClipboardTask(free => { // 剪贴板数据保存目标数据列表 List _bytes = new(); var hMem = ClipTool.GetClipboardData((uint)ClipboardFormat.CF_ENHMETAFILE); + if (hMem == IntPtr.Zero) + { + Debug.WriteLine("失败:GetClipboardData"); + return; + } WindowsAPI.GlobalLockTask(hMem, prt => { uint size = WindowsAPI.GlobalSize(hMem); if (size > 0) { var buffer = new byte[size]; - Marshal.Copy(prt, buffer, 0, buffer.Length);// 将剪贴板数据保存到自定义字节数组 + Marshal.Copy(prt, buffer, 0, buffer.Length); _bytes.Add(buffer); } }); - - //{ - // var size = Marshal.SizeOf(typeof(PlaceableMetaHeader)); - // if (size > 0) - // { - // var buffer = new byte[size]; - // Marshal.Copy(hMem, buffer, 0, buffer.Length);// 将剪贴板数据保存到自定义字节数组 - // _bytes.Add(buffer); - // } - //} }, false); #else try -- Gitee From c50a7bbe255fbba415e9d6f9a74249540b72f368 Mon Sep 17 00:00:00 2001 From: liuqihong <540762622@qq.com> Date: Sun, 2 Oct 2022 17:56:22 +0800 Subject: [PATCH 539/675] =?UTF-8?q?=E7=A7=BB=E9=99=A4=E6=B5=8B=E8=AF=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/IFoxCAD.Cad.Shared/Copyclip/EmfTool.cs | 73 +++++----------- .../ExtensionMethod/DBDictionaryEx.cs | 2 +- tests/TestShared/Copyclip.cs | 84 ++++--------------- 3 files changed, 37 insertions(+), 122 deletions(-) diff --git a/src/IFoxCAD.Cad.Shared/Copyclip/EmfTool.cs b/src/IFoxCAD.Cad.Shared/Copyclip/EmfTool.cs index 7d3729b..299b22d 100644 --- a/src/IFoxCAD.Cad.Shared/Copyclip/EmfTool.cs +++ b/src/IFoxCAD.Cad.Shared/Copyclip/EmfTool.cs @@ -9,7 +9,6 @@ using System.Security.Policy; using System.Security.Cryptography; - // DWORD == uint // WORD == ushort // LONG == int @@ -20,15 +19,6 @@ * Console.WriteLine(Marshal.SizeOf(typeof(StandardMetaRecord))); */ -//[Serializable] -//[StructLayout(LayoutKind.Sequential, Pack = 2)] -//public struct WmfStr -//{ -// public PlaceableMetaHeader Placeable; -// public WindowsMetaHeader Wmfhead; -// public StandardMetaRecord Wmfrecord; -//} - //WMF 文件格式: //https://blog.51cto.com/chenyanxi/803247 //文件缩放信息:22字节 @@ -183,15 +173,6 @@ public struct ENHMETAHEADER public static class EmfTool { - // https://zhidao.baidu.com/question/646739770512964165/answer/1616737219.html?qq-pf-to=pcqq.c2c - //16位的函数 - [DllImport("gdi32.dll")] - public static extern uint GetMetaFile(StringBuilder path); - //32位的函数 - [DllImport("gdi32.dll")] - public static extern uint GetEnhMetaFile(StringBuilder path); - - /// /// 将一个标准Windows图元文件转换成增强型图元文件 /// @@ -214,10 +195,7 @@ public static class EmfTool /// [DllImport("gdi32.dll", EntryPoint = "SetWinMetaFileBits")] public static extern IntPtr SetWinMetaFileBits(uint nSize, IntPtr lpMeta16Data, IntPtr hdcRef, IntPtr lpMFP); - /* - [DllImport("gdi32.dll", EntryPoint = "SetWinMetaFileBits")] - public static extern int SetWinMetaFileBits(uint nSize, ref byte lpbBuffer, IntPtr hdcRef, ref METAFILEPICT lpmfp); - */ + /// /// 获取矢量图的byte @@ -329,6 +307,15 @@ public static void ToMetafile(byte[] data, Func task) } +#if false + // https://zhidao.baidu.com/question/646739770512964165/answer/1616737219.html?qq-pf-to=pcqq.c2c + //16位的函数 + [DllImport("gdi32.dll")] + public static extern uint GetMetaFile(StringBuilder path); + //32位的函数 + [DllImport("gdi32.dll")] + public static extern uint GetEnhMetaFile(StringBuilder path); + /// /// c#获取wmf方式 /// @@ -349,16 +336,16 @@ public static IntPtr GetMetafile(string wmfFile) /* - * // 这是c#写入wmf流程 - * // c#画的wmf格式是可以的...用这样方式生成的就是可以写剪贴板 - * WindowsAPI.GetClientRect(doc.Window.Handle, out IntRect rcClient); - * int width = rcClient.Right - rcClient.Left; - * int height = rcClient.Bottom - rcClient.Top; - * EmfTool.Export(wmf, width, height);//cad的命令wmfin:不能导入c#自绘的 - * - * //c#方法,但是它读取不了cad的wmf - * wmfMeta = EmfTool.GetMetafile(wmf); - */ + * // 这是c#写入wmf流程 + * // c#画的wmf格式是可以的...用这样方式生成的就是可以写剪贴板 + * WindowsAPI.GetClientRect(doc.Window.Handle, out IntRect rcClient); + * int width = rcClient.Right - rcClient.Left; + * int height = rcClient.Bottom - rcClient.Top; + * EmfTool.Export(wmf, width, height);//cad的命令wmfin:不能导入c#自绘的 + * + * //c#方法,但是它读取不了cad的wmf + * wmfMeta = EmfTool.GetMetafile(wmf); + */ /// /// 导出为 Emf 或 Wmf 文件 @@ -418,23 +405,5 @@ static void Draw(Graphics g) g.DrawLine(new Pen(Color.Black, 0.1f), 110f, 110f, 220f, 25f); g.DrawString("剖面图", new Font("宋体", 9f), Brushes.Green, 220f, 20f); } - - /// - /// 返回对一个增强型图元文件的说明 - /// - /// 目标增强型图元文件的句柄 - /// lpszDescription缓冲区的长度 - /// 指定一个预先初始化好的字串缓冲区,准备随同图元文件说明载入。 - /// 参考CreateEnhMetaFile函数,了解增强型图元文件说明字串的具体格式 - /// - [DllImport("gdi32", EntryPoint = "GetEnhMetaFileDescription")] - public static extern uint GetEnhMetaFileDescription( - uint hemf, - uint cchBuffer, - [MarshalAs(UnmanagedType.LPWStr)] StringBuilder lpszDescription - ); - - - [DllImport("gdi32", EntryPoint = "GetEnhMetaFileHeader")] - public static extern uint GetEnhMetaFileHeader(uint hemf, uint cbBuffer, IntPtr /*ENHMETAHEADER*/ lpemh); +#endif } \ No newline at end of file diff --git a/src/IFoxCAD.Cad.Shared/ExtensionMethod/DBDictionaryEx.cs b/src/IFoxCAD.Cad.Shared/ExtensionMethod/DBDictionaryEx.cs index 31043b2..f7cb3e3 100644 --- a/src/IFoxCAD.Cad.Shared/ExtensionMethod/DBDictionaryEx.cs +++ b/src/IFoxCAD.Cad.Shared/ExtensionMethod/DBDictionaryEx.cs @@ -296,7 +296,7 @@ public static ObjectId AddGroup(this DBDictionary dict, string name, IEnumerable if (dict.Contains(name)) return ObjectId.Null; - using ObjectIdCollection idc = new(ids.ToArray());//TODO 需要using吗? 这里是堆交给cad管理了?还是它将被cad克隆? 等待测试 + using ObjectIdCollection idc = new(ids.ToArray());//需要using吗? 暂无测试 return dict.AddGroup(name, idc); } diff --git a/tests/TestShared/Copyclip.cs b/tests/TestShared/Copyclip.cs index 9491894..c490f58 100644 --- a/tests/TestShared/Copyclip.cs +++ b/tests/TestShared/Copyclip.cs @@ -64,7 +64,8 @@ void DocumentManager_DocumentLockModeChanged(object sender, DocumentLockModeChan #if PASTECLIP if (up == "PASTECLIP")// 粘贴 { - //TODO === 完成之后此处将会移除 + // TODO === 完成之后此处将会移除 + // 粘贴文本的生成单行文字/多行文字,这些还需要自己去实现 var getClip = ClipTool.GetClipboard(ClipboardEnv.CadVer, out TagClipboardInfo tag); if (!getClip) return; @@ -75,7 +76,7 @@ void DocumentManager_DocumentLockModeChanged(object sender, DocumentLockModeChan } else if (up == "PASTEBLOCK") //ctrl+shift+v 粘贴为块 { - //TODO === 完成之后此处将会移除 + // TODO === 完成之后此处将会移除 var getClip = ClipTool.GetClipboard(ClipboardEnv.CadVer, out TagClipboardInfo tag); if (!getClip) return; @@ -277,11 +278,11 @@ void Copy(bool getPoint, bool isEraseSsget = false) getFlag = ClipTool.GetClipboardFormat(ClipboardEnv.CadVer, cadClipType, out uint cadClipFormat, out IntPtr cadClipData); - // 写入cad图元 + // 写入剪贴板: cad图元 ClipTool.SetClipboardData(cadClipFormat, cadClipData); free.Add(cadClipData); - // 写入 wmf 使得在粘贴链接的时候可以用 + // 写入剪贴板: wmf,使得在粘贴链接的时候可以用 if (wmfMeta != IntPtr.Zero) { ClipTool.SetClipboardData((uint)ClipboardFormat.CF_ENHMETAFILE, wmfMeta); @@ -289,7 +290,7 @@ void Copy(bool getPoint, bool isEraseSsget = false) } // c# cad截图 https://www.cnblogs.com/shangdishijiao/p/15166499.html - // 写入BMP位图...这是截图,不是WMF转BMP,不对 + // 写入剪贴板: BMP位图,这是截图,不是WMF转BMP,不对 // BitmapTool.CaptureWndImage(doc.Window.Handle, bitmapHandle => { // ClipTool.SetClipboardData((uint)ClipboardFormat.CF_BITMAP, bitmapHandle); // }); @@ -350,9 +351,8 @@ void Paste(bool isBlock) if (btr == null) return; - // 加入新建的块表记录 - // 动态块粘贴,然后用:ctrl+z会导致动态块特性无法恢复, - /// 是因为它 + /// 克隆进块表记录 + /// 动态块粘贴之后,用ctrl+z导致动态块特性无法恢复,是因为它: using IdMapping map = new(); using ObjectIdCollection idc = new(fileEntityIds.ToArray()); tr.Task(() => { @@ -416,68 +416,15 @@ void Paste(bool isBlock) } #region 读取剪贴板WMF - -#if true3 - var cf_metafile = (uint)ClipboardFormat.CF_ENHMETAFILE; - var len = EmfTool.GetEnhMetaFileDescription(cf_metafile, 0, null!); - var desc = new StringBuilder(); //PTSTR desc = (PTSTR)malloc(sizeof(TCHAR) * (len + 1)); - len = EmfTool.GetEnhMetaFileDescription(cf_metafile, len, desc); - - var m_grid = new StringBuilder(); - m_grid.Append(desc); - - len = EmfTool.GetEnhMetaFileHeader(cf_metafile, 0, IntPtr.Zero); - IntPtr header = Marshal.AllocHGlobal((int)len); - len = EmfTool.GetEnhMetaFileHeader(cf_metafile, len, header); - - // 将内存空间转换为目标结构体 - var obj = (ENHMETAHEADER)Marshal.PtrToStructure(header, typeof(ENHMETAHEADER)); - m_grid.Append(obj.nRecords); - - Marshal.FreeHGlobal(header); -#endif - -#if true2 - // win32api 不成功 - ClipTool.OpenClipboardTask(free => { - // 剪贴板数据保存目标数据列表 - List _bytes = new(); - var hMem = ClipTool.GetClipboardData((uint)ClipboardFormat.CF_ENHMETAFILE); - if (hMem == IntPtr.Zero) - { - Debug.WriteLine("失败:GetClipboardData"); - return; - } - WindowsAPI.GlobalLockTask(hMem, prt => { - uint size = WindowsAPI.GlobalSize(hMem); - if (size > 0) - { - var buffer = new byte[size]; - Marshal.Copy(prt, buffer, 0, buffer.Length); - _bytes.Add(buffer); - } - }); - }, false); -#else - try - { - // c# 读取成功 - if (Clipboard.ContainsData(DataFormats.EnhancedMetafile)) - { - var iData = Clipboard.GetDataObject();//从剪切板获取数据 - if (!iData.GetDataPresent(DataFormats.EnhancedMetafile)) - return; - - //定义图形-图元文件 - var metafile = (Metafile)iData.GetData(DataFormats.EnhancedMetafile); - Env.Printl("Metafile:" + metafile.Size.ToString()); - } - } - catch (Exception e) + // c# 读取成功,win32直接读取剪贴板的话是不成功的 + if (Clipboard.ContainsData(DataFormats.EnhancedMetafile)) { - throw; + var iData = Clipboard.GetDataObject();//从剪切板获取数据 + if (!iData.GetDataPresent(DataFormats.EnhancedMetafile)) + return; + var metafile = (Metafile)iData.GetData(DataFormats.EnhancedMetafile); + Env.Printl("Metafile:" + metafile.Size.ToString()); } -#endif #endregion } @@ -590,7 +537,6 @@ static string CreateTempFileName(string format = "X") #if true2 - public class TestImageFormat { public ImageFormat GetFormat(string filename) -- Gitee From 043924f464fd7e93315fb44af37a6d0695ccd525 Mon Sep 17 00:00:00 2001 From: liuqihong <540762622@qq.com> Date: Mon, 3 Oct 2022 12:37:17 +0800 Subject: [PATCH 540/675] =?UTF-8?q?=E4=BC=98=E5=8C=96=E5=86=85=E5=AD=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/IFoxCAD.Cad.Shared/Copyclip/EmfTool.cs | 16 +++++++--------- tests/TestShared/Copyclip.cs | 2 +- 2 files changed, 8 insertions(+), 10 deletions(-) diff --git a/src/IFoxCAD.Cad.Shared/Copyclip/EmfTool.cs b/src/IFoxCAD.Cad.Shared/Copyclip/EmfTool.cs index 299b22d..e4a12f4 100644 --- a/src/IFoxCAD.Cad.Shared/Copyclip/EmfTool.cs +++ b/src/IFoxCAD.Cad.Shared/Copyclip/EmfTool.cs @@ -68,6 +68,10 @@ public static IntPtr Wmf2Emf(string wmfFile) if (!bts) throw new IOException("失败:类型转换,路径:" + file); + // 转为emf的地址 + IntPtr hEMF = IntPtr.Zero; + + // 控制输出的时候跟cad一样带有一个矩形框边界,而不是所有图元的包围盒作为边界 var mpType = new MetaFilePict { mm = MappingModes.MM_ANISOTROPIC, @@ -76,23 +80,17 @@ public static IntPtr Wmf2Emf(string wmfFile) hMF = IntPtr.Zero }; - IntPtr hEMF = IntPtr.Zero; + // byte[] 指针偏移 int iOffset = 0; if (sWMF.IsActivity) iOffset = sWMFsize; - - // byte[] 指针偏移 - var arr = fileByte.Skip(iOffset).ToArray(); - GCHandle arrHandle = GCHandle.Alloc(arr, GCHandleType.Pinned); + IntPtr intPtr = Marshal.UnsafeAddrOfPinnedArrayElement(fileByte, iOffset); WindowsAPI.StructToPtr(mpType, mpPtr => { hEMF = EmfTool.SetWinMetaFileBits( - (uint)fileByte.Length, arrHandle.AddrOfPinnedObject(), IntPtr.Zero, mpPtr); + (uint)fileByte.Length, intPtr, IntPtr.Zero, mpPtr); }); - if (arrHandle.IsAllocated) - arrHandle.Free(); - return hEMF; } } diff --git a/tests/TestShared/Copyclip.cs b/tests/TestShared/Copyclip.cs index c490f58..cf253dc 100644 --- a/tests/TestShared/Copyclip.cs +++ b/tests/TestShared/Copyclip.cs @@ -423,7 +423,7 @@ void Paste(bool isBlock) if (!iData.GetDataPresent(DataFormats.EnhancedMetafile)) return; var metafile = (Metafile)iData.GetData(DataFormats.EnhancedMetafile); - Env.Printl("Metafile:" + metafile.Size.ToString()); + Env.Printl(nameof(Metafile) + metafile.Size.ToString()); } #endregion } -- Gitee From bf12d70cdde003bc8fadb679bc9e6ce7e7135ba2 Mon Sep 17 00:00:00 2001 From: liuqihong <540762622@qq.com> Date: Mon, 3 Oct 2022 20:39:55 +0800 Subject: [PATCH 541/675] =?UTF-8?q?=E4=BF=AE=E5=A4=8D=E5=89=AA=E8=B4=B4?= =?UTF-8?q?=E6=9D=BF=E9=AB=98=E9=A2=91=E5=A4=8D=E5=88=B6=E6=97=B6=E5=80=99?= =?UTF-8?q?=E4=BC=9A=E5=8D=A1=E6=AD=BB=20=E5=8E=9F=E5=9B=A0=E6=98=AF?= =?UTF-8?q?=E9=94=81=E5=AE=9A=E4=BA=86=E5=86=85=E5=AD=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Copyclip/TagClipboardInfo.cs | 217 +++++--- src/IFoxCAD.Cad.Shared/Copyclip/WindowsAPI.cs | 69 ++- tests/TestShared/Copyclip.cs | 472 ++++++++++-------- 3 files changed, 459 insertions(+), 299 deletions(-) diff --git a/src/IFoxCAD.Cad.Shared/Copyclip/TagClipboardInfo.cs b/src/IFoxCAD.Cad.Shared/Copyclip/TagClipboardInfo.cs index c83af23..5fd1bbe 100644 --- a/src/IFoxCAD.Cad.Shared/Copyclip/TagClipboardInfo.cs +++ b/src/IFoxCAD.Cad.Shared/Copyclip/TagClipboardInfo.cs @@ -1,4 +1,6 @@ -namespace IFoxCAD.Cad; +#define Marshal + +namespace IFoxCAD.Cad; using System; using System.Diagnostics; @@ -6,6 +8,8 @@ using System.Text; using System.Threading; using System.Windows; +using System.Windows.Media.Media3D; +using System.Xml.Linq; public class ClipboardEnv { @@ -20,7 +24,7 @@ public class ClipboardEnv /// [Serializable] [StructLayout(LayoutKind.Sequential, Pack = 8, CharSet = CharSet.Unicode/*此参数将导致260*2*/)] -public struct TagClipboardInfo +public struct TagClipboardInfo : IEquatable { #region 字段,对应arx结构的,不要改动,本结构也不允许再加字段 [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 260)] @@ -145,6 +149,51 @@ void GetSize() #endif static extern IntPtr AcedGetAcadDwgview(); #endregion + + #region 重载运算符_比较 + public bool Equals(TagClipboardInfo other) + { + return + szTempFile == other.szTempFile && + szSourceFile == other.szSourceFile && + szSignature == other.szSignature && + nFlags == other.nFlags && + dptInsert == other.dptInsert && + rectGDI == other.rectGDI && + mpView == other.mpView && + dwThreadId == other.dwThreadId && + nLen == other.nLen && + nType == other.nType && + chData == other.chData; + } + public static bool operator !=(TagClipboardInfo a, TagClipboardInfo b) + { + return !(a == b); + } + public static bool operator ==(TagClipboardInfo a, TagClipboardInfo b) + { + return a.Equals(b); + } + public override bool Equals(object obj) + { + return obj is TagClipboardInfo info && Equals(info); + } + public override int GetHashCode() + { + return + szTempFile.GetHashCode() ^ + szSourceFile.GetHashCode() ^ + szSignature.GetHashCode() ^ + nFlags ^ + dptInsert.GetHashCode() ^ + rectGDI.GetHashCode() ^ + mpView.GetHashCode() ^ + dwThreadId ^ + nLen ^ + nType ^ + chData.GetHashCode(); + } + #endregion } @@ -155,18 +204,22 @@ void GetSize() public struct IntRect { [DebuggerBrowsable(DebuggerBrowsableState.Never)] - private string DebuggerDisplay => $"(Left:{Left},Top:{Top},Right:{Right},Bottom:{Bottom})"; - - public int Left { get; } - public int Top { get; } - public int Right { get; } - public int Bottom { get; } + private string DebuggerDisplay => $"(Left:{_Left},Top:{_Top},Right:{_Right},Bottom:{_Bottom})"; + + int _Left; + int _Top; + int _Right; + int _Bottom; + public int Left => _Left; + public int Top => _Top; + public int Right => _Right; + public int Bottom => _Bottom; public IntRect(int left, int top, int right, int bottom) { - Left = left; - Top = top; - Right = right; - Bottom = bottom; + _Left = left; + _Top = top; + _Right = right; + _Bottom = bottom; } static readonly IntRect _Zero = new(0, 0, 0, 0); @@ -174,8 +227,35 @@ public IntRect(int left, int top, int right, int bottom) public override string ToString() { - return $"({Left},{Top},{Right},{Bottom})"; + return $"({_Left},{_Top},{_Right},{_Bottom})"; + } + + #region 重载运算符_比较 + public bool Equals(IntRect other) + { + return + _Left == other._Left && + _Top == other._Top && + _Right == other._Right && + _Bottom == other._Bottom; } + public static bool operator !=(IntRect a, IntRect b) + { + return !(a == b); + } + public static bool operator ==(IntRect a, IntRect b) + { + return a.Equals(b); + } + public override bool Equals(object obj) + { + return obj is IntRect d && Equals(d); + } + public override int GetHashCode() + { + return ((_Left, _Top).GetHashCode(), _Right).GetHashCode() ^ _Bottom.GetHashCode(); + } + #endregion } @@ -196,19 +276,22 @@ public IntSize(int cx, int cy) [StructLayout(LayoutKind.Sequential, Pack = 8)] [DebuggerDisplay("{DebuggerDisplay,nq}")] [DebuggerTypeProxy(typeof(Point3D))] -public struct Point3D +public struct Point3D : IEquatable { [DebuggerBrowsable(DebuggerBrowsableState.Never)] - private string DebuggerDisplay => $"(X:{X},Y:{Y},Z:{Z})"; - - public double X { get; } - public double Y { get; } - public double Z { get; } + private string DebuggerDisplay => $"(X:{_X},Y:{_Y},Z:{_Z})"; + + double _X; + double _Y; + double _Z; + public double X => _X; + public double Y => _Y; + public double Z => _Z; public Point3D(double x, double y, double z) { - X = x; - Y = y; - Z = z; + _X = x; + _Y = y; + _Z = z; } public static implicit operator Point3D(Point3d pt) { @@ -223,6 +306,32 @@ public override string ToString() { return $"({X},{Y},{Z})"; } + + #region 重载运算符_比较 + public bool Equals(Point3D other) + { + return + _X == other._X && + _Y == other._Y && + _Z == other._Z; + } + public static bool operator !=(Point3D a, Point3D b) + { + return !(a == b); + } + public static bool operator ==(Point3D a, Point3D b) + { + return a.Equals(b); + } + public override bool Equals(object obj) + { + return obj is Point3D d && Equals(d); + } + public override int GetHashCode() + { + return (_X, _Y).GetHashCode() ^ _Z.GetHashCode(); + } + #endregion } /* @@ -338,10 +447,10 @@ public partial class ClipTool /// 所以写入的时候必须一次性写入多个cf
    ///
    /// 接收返回的栈空间指针用于释放 - /// true写入,false读取 + /// true写入,false读取 /// /// - public static bool OpenClipboardTask(Action> action, bool isSetClipboard) + public static bool OpenClipboardTask(bool isWrite, Action> action) { if (action == null) throw new ArgumentNullException(nameof(action)); @@ -353,18 +462,25 @@ public static bool OpenClipboardTask(Action> action, bool isSetClip openFlag = OpenClipboard(IntPtr.Zero); if (!openFlag) return false; - if (isSetClipboard) + if (isWrite) EmptyClipboard(); action.Invoke(freeDatas); } catch (Exception e) { + Debugger.Break(); Debug.WriteLine(e.Message); } finally { for (int i = 0; i < freeDatas.Count; i++) + { +#if Marshal Marshal.FreeHGlobal(freeDatas[i]); +#else + WindowsAPI.GlobalFree(freeDatas[i]); +#endif + } if (openFlag) CloseClipboard(); } @@ -380,7 +496,7 @@ public static bool GetClipboard(string clipKey, out T? tag) bool locked = false; T? result = default; - OpenClipboardTask(lst => { + ClipTool.OpenClipboardTask(false, lst => { // 读取剪贴板的指定数据 var clipKeyFormat = RegisterClipboardFormat(clipKey);//ClipboardEnv.CadVer var clipTypeData = GetClipboardData(clipKeyFormat); @@ -390,42 +506,15 @@ public static bool GetClipboard(string clipKey, out T? tag) // 非托管内存块->托管对象 result = (T)Marshal.PtrToStructure(ptr, typeof(T)); }); - }, false); + }); tag = result; return locked; } - - /// - /// 获取剪贴板对象信息
    - /// 微软链接 - ///
    - /// 剪贴板的索引名 - /// 封送类型 - /// 返回:索引值 - /// 返回:申请封送类型栈空间的指针 - /// true成功拷贝;false可能重复粘贴对象导致 - public static bool GetClipboardFormat(string clipKey, T clipType, - out uint clipKeyFormat, out IntPtr clipTypeData) - { - /* - * // 这种写入方式是失败的,ToString转二进制序列化的时候存在元数据 - * using var data = new MemoryStream(); - * var bytes = Encoding.Unicode.GetBytes(TagClipboardInfo.ToString()); - * data.Write(bytes, 0, bytes.Length); - * Clipboard.SetData($"AutoCAD.r{Acap.Version.Major}", data); - */ - - clipKeyFormat = RegisterClipboardFormat(clipKey); - IntPtr res = IntPtr.Zero; - WindowsAPI.StructToPtr(clipType, clipPtr => { - res = clipPtr; - }, false); - clipTypeData = res; - return true; - } } +#if true2 +// 无法备份emf内容 public static class ClipEx { // https://blog.csdn.net/vencon_s/article/details/46345083 @@ -445,7 +534,7 @@ public static class ClipEx /// true成功,false失败 public static bool SaveClip() { - bool result = ClipTool.OpenClipboardTask(free => { + bool result = ClipTool.OpenClipboardTask(false, free => { _bytes.Clear(); _formats.Clear(); @@ -468,7 +557,7 @@ public static bool SaveClip() } }); } - }, false); + }); if (result) result = _formats.Count > 0; return result; @@ -483,25 +572,25 @@ public static bool RestoreClip() if (_formats.Count <= 0) return false; - bool result = ClipTool.OpenClipboardTask(free => { + bool result = ClipTool.OpenClipboardTask(true, free => { for (int i = 0; i < _formats.Count; i++) { int size = _bytes[i].Length; - IntPtr si = Marshal.AllocHGlobal(size); + IntPtr structPtr = Marshal.AllocHGlobal(size); if (size > 0) { - Marshal.Copy(_bytes[i], 0, si, size); - ClipTool.SetClipboardData(_formats[i], si); + Marshal.Copy(_bytes[i], 0, structPtr, size); + ClipTool.SetClipboardData(_formats[i], structPtr); } } - }, true); + }); if (result) result = _formats.Count > 0; return result; } } - +#endif diff --git a/src/IFoxCAD.Cad.Shared/Copyclip/WindowsAPI.cs b/src/IFoxCAD.Cad.Shared/Copyclip/WindowsAPI.cs index f8224e6..04694a6 100644 --- a/src/IFoxCAD.Cad.Shared/Copyclip/WindowsAPI.cs +++ b/src/IFoxCAD.Cad.Shared/Copyclip/WindowsAPI.cs @@ -1,4 +1,5 @@ -namespace IFoxCAD.Cad; +#define Marshal +namespace IFoxCAD.Cad; using System; using System.Diagnostics; @@ -44,7 +45,7 @@ public class WindowsAPI /// [DllImport("kernel32.dll", SetLastError = true)] static extern bool GlobalUnlock(IntPtr hMem); -#if true2 +#if !Marshal /// /// 从堆中分配内存 /// 被代替: Marshal.AllocHGlobal @@ -104,12 +105,12 @@ public static bool GlobalLockTask(IntPtr data, Action task) /// byte数组转结构体 /// /// byte数组 - /// 返回的结构体 + /// 返回的结构体 /// 返回的结构大小 /// 转换后的结构体 - public static bool BytesToStruct(byte[] bytes, out T? obj, out int typeSize) + public static bool BytesToStruct(byte[] bytes, out T? result, out int typeSize) { - obj = default; + result = default; var structType = typeof(T); // 得到结构体的大小 typeSize = Marshal.SizeOf(structType); @@ -117,13 +118,22 @@ public static bool BytesToStruct(byte[] bytes, out T? obj, out int typeSize) return false; // 分配结构体大小的内存空间 +#if Marshal IntPtr structPtr = Marshal.AllocHGlobal(typeSize); +#else + const int GMEM_MOVEABLE = 0x0002; + IntPtr structPtr = WindowsAPI.GlobalAlloc(GMEM_MOVEABLE, typeSize); +#endif // 将byte数组拷到分配好的内存空间 Marshal.Copy(bytes, 0, structPtr, typeSize); // 将内存空间转换为目标结构体 - obj = (T)Marshal.PtrToStructure(structPtr, structType); + result = (T)Marshal.PtrToStructure(structPtr, structType); // 释放内存空间 +#if Marshal Marshal.FreeHGlobal(structPtr); +#else + WindowsAPI.GlobalFree(structPtr); +#endif return true; } @@ -150,41 +160,54 @@ public static byte[] StructToBytes(object? structObj) /// 要转换的结构体 /// 输出指针 /// 释放申请的内存 - /// StructureToPtr的参数 + /// 是否锁定内存 /// public static void StructToPtr(object? structObj, Action? task = null, bool freeHGlobal = true, - bool fDeleteOld = true) + bool lockPrt = true) { if (structObj == null) throw new ArgumentNullException(nameof(structObj)); - - // 得到结构体的大小 - int typeSize = Marshal.SizeOf(structObj); - - // 分配结构体大小的内存空间 - // 此处需要锁定吗? - IntPtr structPtr = Marshal.AllocHGlobal(typeSize); - bool locked = false; +#if Marshal + IntPtr structPtr = Marshal.AllocHGlobal(Marshal.SizeOf(structObj)); +#else + const int GMEM_MOVEABLE = 0x0002; + IntPtr structPtr = WindowsAPI.GlobalAlloc(GMEM_MOVEABLE, Marshal.SizeOf(structObj)); +#endif + if (structPtr == IntPtr.Zero) + return; try { - locked = GlobalLockTask(structPtr, ptr => { - // 将结构体拷到分配好的内存空间 - Marshal.StructureToPtr(structObj, ptr, fDeleteOld); - task?.Invoke(ptr); - }); + if (lockPrt) + GlobalLockTask(structPtr, ptr => { + ToPtr(structObj, task, ptr); + }); + else + ToPtr(structObj, task, structPtr); } catch (Exception e) { - Marshal.FreeHGlobal(structPtr); - structPtr = IntPtr.Zero; + Debugger.Break(); Debug.WriteLine(e.Message); } finally { if (freeHGlobal && structPtr != IntPtr.Zero) + { +#if Marshal Marshal.FreeHGlobal(structPtr); +#else + WindowsAPI.GlobalFree(structPtr); +#endif + } + } + + // 将结构体拷到分配好的内存空间 + static void ToPtr(object? structObj, Action? task, IntPtr structPtr) + { + Marshal.StructureToPtr(structObj, structPtr, true); + task?.Invoke(structPtr); } } } diff --git a/tests/TestShared/Copyclip.cs b/tests/TestShared/Copyclip.cs index cf253dc..cb28bfc 100644 --- a/tests/TestShared/Copyclip.cs +++ b/tests/TestShared/Copyclip.cs @@ -5,6 +5,7 @@ namespace Test; using Autodesk.AutoCAD.DatabaseServices; using System; +using System.Diagnostics; using System.Drawing.Imaging; using System.Threading; using System.Windows; @@ -164,6 +165,13 @@ public void Terminate() } + /// + /// 读写锁,当资源处于写入模式时,
    + /// 其他线程写入需要等待本次写入结束之后才能继续写入 + /// 参考链接 + ///
    + static ReaderWriterLockSlim _rwLock = new(); + /// /// 储存准备删除的文件 /// 也可以用txt代替 @@ -177,134 +185,150 @@ public void Terminate() /// void Copy(bool getPoint, bool isEraseSsget = false) { - var dm = Acap.DocumentManager; - if (dm.Count == 0) - return; - var doc = dm.MdiActiveDocument; - if (doc.Editor == null) - return; - var psr = doc.Editor.SelectImplied();// 预选 - if (psr.Status != PromptStatus.OK) - psr = doc.Editor.GetSelection();// 手选 - if (psr.Status != PromptStatus.OK) - return; - - // 设置基点 - Point3d pt = Point3d.Origin; - var idArray = psr.Value.GetObjectIds(); - - var tempFile = CreateTempFileName(); - while (File.Exists(tempFile) || - File.Exists(Path.ChangeExtension(tempFile, "wmf"))) + try { - tempFile = CreateTempFileName(); - Thread.Sleep(1); - } - - using var tr = new DBTrans(); + if (!_rwLock.IsWriteLockHeld) + _rwLock.EnterWriteLock(); // 进入写入锁 + else + Debug.WriteLine("没有进入读写锁"); - #region 写入 AutoCAD.R17 数据 - if (getPoint) - { - var pr = doc.Editor.GetPoint("\n选择基点"); - if (pr.Status != PromptStatus.OK) + var dm = Acap.DocumentManager; + if (dm.Count == 0) return; - pt = pr.Value; - } - else - { - // 遍历块内 - // 获取左下角点作为基点 - double minx = double.MaxValue; - double miny = double.MaxValue; - double minz = double.MaxValue; - foreach (var id in idArray) + var doc = dm.MdiActiveDocument; + if (doc.Editor == null) + return; + var psr = doc.Editor.SelectImplied();// 预选 + if (psr.Status != PromptStatus.OK) + psr = doc.Editor.GetSelection();// 手选 + if (psr.Status != PromptStatus.OK) + return; + + // 设置基点 + Point3d pt = Point3d.Origin; + var idArray = psr.Value.GetObjectIds(); + + var tempFile = CreateTempFileName(); + while (File.Exists(tempFile) || + File.Exists(Path.ChangeExtension(tempFile, "wmf"))) { - var ent = tr.GetObject(id); - if (ent == null) - continue; - var info = ent.GetBoundingBoxEx(); - if (ent is BlockReference brf) - info.Move(brf.Position, Point3d.Origin); - minx = minx > info.MinX ? info.MinX : minx; - miny = miny > info.MinY ? info.MinY : miny; - minz = minz > info.MinZ ? info.MinZ : minz; + tempFile = CreateTempFileName(); + Thread.Sleep(1); } - pt = new(minx, miny, minz); - } - var cadClipType = new TagClipboardInfo(tempFile, pt); + using var tr = new DBTrans(); - // 克隆到目标块表内 - using (var fileTr = new DBTrans(cadClipType.File)) - { - fileTr.Task(() => { - using IdMapping map = new(); - using ObjectIdCollection ids = new(idArray); - tr.Database.WblockCloneObjects( - ids, - fileTr.ModelSpace.ObjectId, - map, - DuplicateRecordCloning.Replace, - false); - }); + #region 写入 AutoCAD.R17 数据 + if (getPoint) + { + var pr = doc.Editor.GetPoint("\n选择基点"); + if (pr.Status != PromptStatus.OK) + return; + pt = pr.Value; + } + else + { + // 遍历块内 + // 获取左下角点作为基点 + double minx = double.MaxValue; + double miny = double.MaxValue; + double minz = double.MaxValue; + foreach (var id in idArray) + { + var ent = tr.GetObject(id); + if (ent == null) + continue; + var info = ent.GetBoundingBoxEx(); + if (ent is BlockReference brf) + info.Move(brf.Position, Point3d.Origin); + minx = minx > info.MinX ? info.MinX : minx; + miny = miny > info.MinY ? info.MinY : miny; + minz = minz > info.MinZ ? info.MinZ : minz; + } + pt = new(minx, miny, minz); + } - // 大于dwg07格式的,保存为07,以实现高低版本通用剪贴板 - // 小于dwg07格式的,本工程没有支持cad06dll - if ((int)DwgVersion.Current >= 27) - fileTr.SaveFile((DwgVersion)27, false); - } + var cadClipType = new TagClipboardInfo(tempFile, pt); - // 剪切时候删除 - if (isEraseSsget) - { - idArray.ForEach(id => { - id.Erase(); - }); - } - #endregion - - #region 写入 WMF 数据 - // 通过cad com导出wmf,再将wmf转为emf,然后才能写入剪贴板 - IntPtr wmfMeta = IntPtr.Zero; - var wmf = Path.ChangeExtension(cadClipType.File, "wmf"); - Env.Editor.ExportWMF(wmf, idArray); - wmfMeta = PlaceableMetaHeader.Wmf2Emf(wmf); - #endregion - - // 必须一次性写入剪贴板,详见 OpenClipboardTask - bool getFlag = false; - ClipTool.OpenClipboardTask(free => { - getFlag = ClipTool.GetClipboardFormat(ClipboardEnv.CadVer, cadClipType, - out uint cadClipFormat, out IntPtr cadClipData); - - // 写入剪贴板: cad图元 - ClipTool.SetClipboardData(cadClipFormat, cadClipData); - free.Add(cadClipData); - - // 写入剪贴板: wmf,使得在粘贴链接的时候可以用 - if (wmfMeta != IntPtr.Zero) + // 克隆到目标块表内 + using (var fileTr = new DBTrans(cadClipType.File)) { - ClipTool.SetClipboardData((uint)ClipboardFormat.CF_ENHMETAFILE, wmfMeta); - EmfTool.DeleteEnhMetaFile(wmfMeta); + fileTr.Task(() => { + using IdMapping map = new(); + using ObjectIdCollection ids = new(idArray); + tr.Database.WblockCloneObjects( + ids, + fileTr.ModelSpace.ObjectId, + map, + DuplicateRecordCloning.Replace, + false); + }); + + // 大于dwg07格式的,保存为07,以实现高低版本通用剪贴板 + // 小于dwg07格式的,本工程没有支持cad06dll + if ((int)DwgVersion.Current >= 27) + fileTr.SaveFile((DwgVersion)27, false); } + #endregion + + #region 写入 WMF 数据 + // 通过cad com导出wmf,再将wmf转为emf,然后才能写入剪贴板 + IntPtr wmfMeta = IntPtr.Zero; + var wmf = Path.ChangeExtension(cadClipType.File, "wmf"); + Env.Editor.ExportWMF(wmf, idArray); + wmfMeta = PlaceableMetaHeader.Wmf2Emf(wmf); + #endregion + + // 必须一次性写入剪贴板,详见 OpenClipboardTask + var cadClipFormat = ClipTool.RegisterClipboardFormat(ClipboardEnv.CadVer); + bool getFlag = ClipTool.OpenClipboardTask(true, free => { + // 写入剪贴板: cad图元 + WindowsAPI.StructToPtr(cadClipType, cadClipData => { + ClipTool.SetClipboardData(cadClipFormat, cadClipData); + }, false, false); + + // 写入剪贴板: wmf,使得在粘贴链接的时候可以用 + if (wmfMeta != IntPtr.Zero) + { + ClipTool.SetClipboardData((uint)ClipboardFormat.CF_ENHMETAFILE, wmfMeta); + EmfTool.DeleteEnhMetaFile(wmfMeta); + } + + // c# cad截图 https://www.cnblogs.com/shangdishijiao/p/15166499.html + // 写入剪贴板: BMP位图,这是截图,不是WMF转BMP,不对 + // BitmapTool.CaptureWndImage(doc.Window.Handle, bitmapHandle => { + // ClipTool.SetClipboardData((uint)ClipboardFormat.CF_BITMAP, bitmapHandle); + // }); + }); + + // 成功拷贝就删除上一次的临时文件 + if (getFlag) + Terminate(); - // c# cad截图 https://www.cnblogs.com/shangdishijiao/p/15166499.html - // 写入剪贴板: BMP位图,这是截图,不是WMF转BMP,不对 - // BitmapTool.CaptureWndImage(doc.Window.Handle, bitmapHandle => { - // ClipTool.SetClipboardData((uint)ClipboardFormat.CF_BITMAP, bitmapHandle); - // }); - }, true); - - // 成功拷贝就删除上一次的临时文件 - if (getFlag) - Terminate(); - - // 加入删除队列,下次删除 - if (!_delFile.Contains(cadClipType.File)) - _delFile.Add(cadClipType.File); - if (!_delFile.Contains(wmf)) - _delFile.Add(wmf); + // 加入删除队列,下次删除 + if (!_delFile.Contains(cadClipType.File)) + _delFile.Add(cadClipType.File); + if (!_delFile.Contains(wmf)) + _delFile.Add(wmf); + + // 剪切时候删除 + if (isEraseSsget) + { + idArray.ForEach(id => { + id.Erase(); + }); + } + } + catch (Exception e) + { + Debugger.Break(); + throw e; + } + finally + { + if (_rwLock.IsWriteLockHeld) + _rwLock.ExitWriteLock(); // 退出写入锁 + } } @@ -314,118 +338,142 @@ void Copy(bool getPoint, bool isEraseSsget = false) /// void Paste(bool isBlock) { - var dm = Acap.DocumentManager; - if (dm.Count == 0) - return; + try + { + if (!_rwLock.IsWriteLockHeld) + _rwLock.EnterWriteLock(); // 进入写入锁 - var getClip = ClipTool.GetClipboard(ClipboardEnv.CadVer, out TagClipboardInfo tag); - if (!getClip) - return; - var clipboardInfo = tag; - Env.Print("粘贴来源: " + clipboardInfo.File); + var dm = Acap.DocumentManager; + if (dm.Count == 0) + return; - if (!File.Exists(clipboardInfo.File)) - { - Env.Print("文件不存在"); - return; - } + var getClip = ClipTool.GetClipboard(ClipboardEnv.CadVer, out TagClipboardInfo tag); + if (!getClip) + return; + var clipboardInfo = tag; + Env.Print("粘贴来源: " + clipboardInfo.File); - // 获取临时文件的图元id - var fileEntityIds = new List(); - using (var fileTr = new DBTrans(clipboardInfo.File, - commit: false, - openMode: FileOpenMode.OpenForReadAndAllShare)) - { - foreach (var id in fileTr.ModelSpace) - if (id.IsOk()) - fileEntityIds.Add(id); - } - if (fileEntityIds.Count == 0) - return; + if (!File.Exists(clipboardInfo.File)) + { + Env.Print("文件不存在"); + return; + } - using var tr = new DBTrans(); - tr.Editor?.SetImpliedSelection(new ObjectId[0]); // 清空选择集 + // 获取临时文件的图元id + var fileEntityIds = new List(); + using (var fileTr = new DBTrans(clipboardInfo.File, + commit: false, + openMode: FileOpenMode.OpenForReadAndAllShare)) + { + foreach (var id in fileTr.ModelSpace) + if (id.IsOk()) + fileEntityIds.Add(id); + } + if (fileEntityIds.Count == 0) + return; - // 新建块表记录 - var btr = CreateBlockTableRecord(tr, clipboardInfo.File); - if (btr == null) - return; + using var tr = new DBTrans(); + tr.Editor?.SetImpliedSelection(new ObjectId[0]); // 清空选择集 - /// 克隆进块表记录 - /// 动态块粘贴之后,用ctrl+z导致动态块特性无法恢复,是因为它: - using IdMapping map = new(); - using ObjectIdCollection idc = new(fileEntityIds.ToArray()); - tr.Task(() => { - tr.Database.WblockCloneObjects( - idc, - btr.ObjectId, // tr.Database.BlockTableId, // 粘贴目标 - map, - DuplicateRecordCloning.Ignore, - false); - }); + // 新建块表记录 + var btr = CreateBlockTableRecord(tr, clipboardInfo.File); + if (btr == null) + return; - // 移动块内,从基点到原点 - foreach (var id in btr) - { - if (!id.IsOk()) + /// 克隆进块表记录 + /// 动态块粘贴之后,用ctrl+z导致动态块特性无法恢复,是因为它: + using IdMapping map = new(); + using ObjectIdCollection idc = new(fileEntityIds.ToArray()); + tr.Task(() => { + tr.Database.WblockCloneObjects( + idc, + btr.ObjectId, // tr.Database.BlockTableId, // 粘贴目标 + map, + DuplicateRecordCloning.Ignore, + false); + }); + + // 移动块内,从基点到原点 + foreach (var id in btr) { - Env.Printl("jig预览块内有克隆失败的东西,是否天正克隆期间导致?"); - continue; + if (!id.IsOk()) + { + Env.Printl("jig预览块内有克隆失败的东西,是否天正克隆期间导致?"); + continue; + } + var ent = tr.GetObject(id); + if (ent == null) + continue; + using (ent.ForWrite()) + ent.Move(clipboardInfo.Point, Point3d.Origin); } - var ent = tr.GetObject(id); - if (ent == null) - continue; - using (ent.ForWrite()) - ent.Move(clipboardInfo.Point, Point3d.Origin); - } - // 预览并获取交互点 - // 天正此处可能存在失败:天正图元不给你jig接口调用之类的 - using var moveJig = new JigEx((mousePoint, drawEntitys) => { - var blockref = new BlockReference(Point3d.Origin, btr.ObjectId); - blockref.Move(Point3d.Origin, mousePoint); - drawEntitys.Enqueue(blockref); - }); - moveJig.SetOptions(clipboardInfo.Point) - .Keywords.Add("A", "A", "引线点粘贴(A)"); - - var dr = moveJig.Drag(); - Point3d moveTo = Point3d.Origin; - if (dr.Status == PromptStatus.Keyword) - moveTo = clipboardInfo.Point; - else if (dr.Status == PromptStatus.OK) - moveTo = moveJig.MousePointWcsLast; - else - { - // 删除jig预览的块表记录 - using (btr.ForWrite()) - btr.Erase(); - return; - } + // 预览并获取交互点 + // 天正此处可能存在失败:天正图元不给你jig接口调用之类的 + using var moveJig = new JigEx((mousePoint, drawEntitys) => { + var blockref = new BlockReference(Point3d.Origin, btr.ObjectId); + blockref.Move(Point3d.Origin, mousePoint); + drawEntitys.Enqueue(blockref); + }); + moveJig.SetOptions(clipboardInfo.Point) + .Keywords.Add("A", "A", "引线点粘贴(A)"); + + var dr = moveJig.Drag(); + Point3d moveTo = Point3d.Origin; + if (dr.Status == PromptStatus.Keyword) + moveTo = clipboardInfo.Point; + else if (dr.Status == PromptStatus.OK) + moveTo = moveJig.MousePointWcsLast; + else + { + // 删除jig预览的块表记录 + using (btr.ForWrite()) + btr.Erase(); + return; + } - if (isBlock) - { - PasteIsBlock(tr, moveJig.Entitys, moveJig.MousePointWcsLast, moveTo); + if (isBlock) + { + PasteIsBlock(tr, moveJig.Entitys, moveJig.MousePointWcsLast, moveTo); + } + else + { + PasteNotBlock(tr, btr, Point3d.Origin, moveTo); + // 删除jig预览的块表记录 + using (btr.ForWrite()) + btr.Erase(); + } + + try + { + #region 读取剪贴板WMF + // c# 读取成功,win32直接读取剪贴板的话是不成功的 + if (Clipboard.ContainsData(DataFormats.EnhancedMetafile)) + { + var iData = Clipboard.GetDataObject();//从剪切板获取数据 + if (!iData.GetDataPresent(DataFormats.EnhancedMetafile)) + return; + var metafile = (Metafile)iData.GetData(DataFormats.EnhancedMetafile); + Env.Printl(nameof(Metafile) + metafile.Size.ToString()); + } + #endregion + } + catch (Exception e) + { + Debugger.Break(); + Debug.WriteLine(e.ToString()); + } } - else + catch (Exception e)//{"剪贴板上的数据无效 (异常来自 HRESULT:0x800401D3 (CLIPBRD_E_BAD_DATA))"} { - PasteNotBlock(tr, btr, Point3d.Origin, moveTo); - // 删除jig预览的块表记录 - using (btr.ForWrite()) - btr.Erase(); + Debugger.Break(); + Debug.WriteLine(e.ToString()); } - - #region 读取剪贴板WMF - // c# 读取成功,win32直接读取剪贴板的话是不成功的 - if (Clipboard.ContainsData(DataFormats.EnhancedMetafile)) + finally { - var iData = Clipboard.GetDataObject();//从剪切板获取数据 - if (!iData.GetDataPresent(DataFormats.EnhancedMetafile)) - return; - var metafile = (Metafile)iData.GetData(DataFormats.EnhancedMetafile); - Env.Printl(nameof(Metafile) + metafile.Size.ToString()); + if (_rwLock.IsWriteLockHeld) + _rwLock.ExitWriteLock(); // 退出写入锁 } - #endregion } /// -- Gitee From 6f4eb724f08e45e209f3507b8dba68a5c8192c01 Mon Sep 17 00:00:00 2001 From: liuqihong <540762622@qq.com> Date: Mon, 3 Oct 2022 21:06:47 +0800 Subject: [PATCH 542/675] =?UTF-8?q?=E4=BC=98=E5=8C=96=E4=BB=A3=E7=A0=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Copyclip/TagClipboardInfo.cs | 15 +++------------ tests/TestShared/Copyclip.cs | 6 ++---- 2 files changed, 5 insertions(+), 16 deletions(-) diff --git a/src/IFoxCAD.Cad.Shared/Copyclip/TagClipboardInfo.cs b/src/IFoxCAD.Cad.Shared/Copyclip/TagClipboardInfo.cs index 5fd1bbe..dcad2f8 100644 --- a/src/IFoxCAD.Cad.Shared/Copyclip/TagClipboardInfo.cs +++ b/src/IFoxCAD.Cad.Shared/Copyclip/TagClipboardInfo.cs @@ -450,13 +450,12 @@ public partial class ClipTool /// true写入,false读取 /// /// - public static bool OpenClipboardTask(bool isWrite, Action> action) + public static bool OpenClipboardTask(bool isWrite, Action action) { if (action == null) throw new ArgumentNullException(nameof(action)); bool openFlag = false; - List freeDatas = new(); try { openFlag = OpenClipboard(IntPtr.Zero); @@ -464,7 +463,7 @@ public static bool OpenClipboardTask(bool isWrite, Action> action) return false; if (isWrite) EmptyClipboard(); - action.Invoke(freeDatas); + action.Invoke(); } catch (Exception e) { @@ -473,14 +472,6 @@ public static bool OpenClipboardTask(bool isWrite, Action> action) } finally { - for (int i = 0; i < freeDatas.Count; i++) - { -#if Marshal - Marshal.FreeHGlobal(freeDatas[i]); -#else - WindowsAPI.GlobalFree(freeDatas[i]); -#endif - } if (openFlag) CloseClipboard(); } @@ -496,7 +487,7 @@ public static bool GetClipboard(string clipKey, out T? tag) bool locked = false; T? result = default; - ClipTool.OpenClipboardTask(false, lst => { + ClipTool.OpenClipboardTask(false, () => { // 读取剪贴板的指定数据 var clipKeyFormat = RegisterClipboardFormat(clipKey);//ClipboardEnv.CadVer var clipTypeData = GetClipboardData(clipKeyFormat); diff --git a/tests/TestShared/Copyclip.cs b/tests/TestShared/Copyclip.cs index cb28bfc..c7aa4b3 100644 --- a/tests/TestShared/Copyclip.cs +++ b/tests/TestShared/Copyclip.cs @@ -189,8 +189,6 @@ void Copy(bool getPoint, bool isEraseSsget = false) { if (!_rwLock.IsWriteLockHeld) _rwLock.EnterWriteLock(); // 进入写入锁 - else - Debug.WriteLine("没有进入读写锁"); var dm = Acap.DocumentManager; if (dm.Count == 0) @@ -281,11 +279,11 @@ void Copy(bool getPoint, bool isEraseSsget = false) // 必须一次性写入剪贴板,详见 OpenClipboardTask var cadClipFormat = ClipTool.RegisterClipboardFormat(ClipboardEnv.CadVer); - bool getFlag = ClipTool.OpenClipboardTask(true, free => { + bool getFlag = ClipTool.OpenClipboardTask(true, () => { // 写入剪贴板: cad图元 WindowsAPI.StructToPtr(cadClipType, cadClipData => { ClipTool.SetClipboardData(cadClipFormat, cadClipData); - }, false, false); + }, false/*不释放内存*/, false/*不锁定内存(否则高频触发时候卡死)*/); // 写入剪贴板: wmf,使得在粘贴链接的时候可以用 if (wmfMeta != IntPtr.Zero) -- Gitee From fa8c5d3dd10c036822154afe0a9abbad8913850e Mon Sep 17 00:00:00 2001 From: liuqihong <540762622@qq.com> Date: Tue, 4 Oct 2022 00:37:18 +0800 Subject: [PATCH 543/675] =?UTF-8?q?=E7=9E=AC=E6=80=81=E5=9B=BE=E5=85=83?= =?UTF-8?q?=E4=BE=8B=E5=AD=90=E5=A2=9E=E5=8A=A0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- tests/TestShared/TestJigExTransient.cs | 46 +++++++++++++++++++++++--- 1 file changed, 41 insertions(+), 5 deletions(-) diff --git a/tests/TestShared/TestJigExTransient.cs b/tests/TestShared/TestJigExTransient.cs index f4d341e..0aabbb1 100644 --- a/tests/TestShared/TestJigExTransient.cs +++ b/tests/TestShared/TestJigExTransient.cs @@ -16,9 +16,9 @@ public void TestJigExTransient() var c2 = new Circle(pt.Polar(0, 300), Vector3d.ZAxis, 100); // 创建瞬态容器 - using var jet = new JigExTransient(); + using JigExTransient jet = new(); - // 将c1以默认模式、c2以亮显模式加到瞬态容器,即在图纸上显示 + // 将c1以默认模式,c2以亮显模式加到瞬态容器,即在图纸上显示 jet.Add(c1); jet.Add(c2, Acgi.TransientDrawingMode.Highlight); @@ -28,11 +28,11 @@ public void TestJigExTransient() return; var pt2 = r2.Value.Ucs2Wcs(); - var c3 = new Circle(pt2, Vector3d.ZAxis, 150); - // 将c1从瞬态容器中移除,将c2修改颜色,c3加入瞬态容器 jet.Remove(c1); + c2.ColorIndex = 1; + var c3 = new Circle(pt2, Vector3d.ZAxis, 150); jet.Add(c3); // 由于c2进行了修改,所以需要更新, @@ -43,11 +43,47 @@ public void TestJigExTransient() var r4 = Env.Editor.GetPoint("\n此拾取无意义,仅为了暂停查看"); // 加到图纸中,为测试瞬态容器可以自行dispose消失,所以未全部加入 - using var tr = new DBTrans(); + using DBTrans tr = new(); tr.CurrentSpace.AddEntity(c3); // 若想将容器中所有图元全部加入提供了Entities属性 // tr.CurrentSpace.AddEntity(jet.Entities); } + + [CommandMethod(nameof(TestJigExTransentDim))] + public static void TestJigExTransentDim() + { + using DBTrans tr = new(); + Editor ed = tr.Editor!; + PromptPointOptions ppo = new("") + { + AppendKeywordsToMessage = false, + }; + List pts = new(); + for (int i = 0; i < 3; i++) + { + ppo.Message = $"\n选择标注点{i + 1}"; + var ppr = ed.GetPoint(ppo); + if (ppr.Status != PromptStatus.OK) + return; + pts.Add(ppr.Value); + } + + using RotatedDimension dimension = new( + rotation: 0, + line1Point: pts[0], + line2Point: pts[1], + dimensionLinePoint: pts[2], + dimensionText: "<>", + tr.Database.Dimstyle); + + using JigExTransient jet = new(); + jet.Add(dimension); + jet.UpdateAll(); + + var r4 = Env.Editor.GetPoint("\n此拾取无意义,仅为了暂停查看"); + + tr.CurrentSpace.AddEntity(dimension); + } } #endif \ No newline at end of file -- Gitee From d7e708185a3a2356c41532741cfe146df29fb0a3 Mon Sep 17 00:00:00 2001 From: liuqihong <540762622@qq.com> Date: Tue, 4 Oct 2022 01:10:48 +0800 Subject: [PATCH 544/675] =?UTF-8?q?=E4=BC=98=E5=8C=96=E4=BB=A3=E7=A0=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Copyclip/TagClipboardInfo.cs | 1 + tests/TestShared/Copyclip.cs | 57 +++++++++++++------ 2 files changed, 40 insertions(+), 18 deletions(-) diff --git a/src/IFoxCAD.Cad.Shared/Copyclip/TagClipboardInfo.cs b/src/IFoxCAD.Cad.Shared/Copyclip/TagClipboardInfo.cs index dcad2f8..e4d513b 100644 --- a/src/IFoxCAD.Cad.Shared/Copyclip/TagClipboardInfo.cs +++ b/src/IFoxCAD.Cad.Shared/Copyclip/TagClipboardInfo.cs @@ -17,6 +17,7 @@ public class ClipboardEnv // public static string CadVer = $"AutoCAD.r{Acap.Version.Major}" // 将r17写死,代表每个cad版本都去找它,实现不隔离cad版本 public static string CadVer = "AutoCAD.r17"; + public static string CadCurrentVer = $"AutoCAD.r{Acap.Version.Major}"; } /// diff --git a/tests/TestShared/Copyclip.cs b/tests/TestShared/Copyclip.cs index c7aa4b3..8fbf33a 100644 --- a/tests/TestShared/Copyclip.cs +++ b/tests/TestShared/Copyclip.cs @@ -93,7 +93,7 @@ void DocumentManager_DocumentLockModeChanged(object sender, DocumentLockModeChan if (dm.Count == 0) return; var doc = dm.MdiActiveDocument; - // 发送命令是因为导出WMF函数的com需要命令形式,否则将报错 + // 发送命令是因为com导出WMF需要命令形式,否则将报错 // 但是发送命令会导致选择集被取消了,那么就需要设置 CommandFlags.Redraw doc.SendStringToExecute(cmd + "\n", true, false, false); } @@ -347,7 +347,15 @@ void Paste(bool isBlock) var getClip = ClipTool.GetClipboard(ClipboardEnv.CadVer, out TagClipboardInfo tag); if (!getClip) - return; + { + // 在没有安装插件的高版本cad中复制,此时剪贴板是当前版本的, + // 那么在安装了插件的cad中需要识别这个同版本的剪贴板内容 + // 例如天正只在某个启动的cad中加载插件,而不是全部 + getClip = ClipTool.GetClipboard(ClipboardEnv.CadCurrentVer, out tag); + if (!getClip) + return; + } + var clipboardInfo = tag; Env.Print("粘贴来源: " + clipboardInfo.File); @@ -359,9 +367,9 @@ void Paste(bool isBlock) // 获取临时文件的图元id var fileEntityIds = new List(); - using (var fileTr = new DBTrans(clipboardInfo.File, - commit: false, - openMode: FileOpenMode.OpenForReadAndAllShare)) + using (DBTrans fileTr = new(clipboardInfo.File, + commit: false, + openMode: FileOpenMode.OpenForReadAndAllShare)) { foreach (var id in fileTr.ModelSpace) if (id.IsOk()) @@ -370,7 +378,7 @@ void Paste(bool isBlock) if (fileEntityIds.Count == 0) return; - using var tr = new DBTrans(); + using DBTrans tr = new(); tr.Editor?.SetImpliedSelection(new ObjectId[0]); // 清空选择集 // 新建块表记录 @@ -396,7 +404,7 @@ void Paste(bool isBlock) { if (!id.IsOk()) { - Env.Printl("jig预览块内有克隆失败的东西,是否天正克隆期间导致?"); + Env.Printl("jig预览块内有克隆失败的脏东西,是否天正克隆期间导致?"); continue; } var ent = tr.GetObject(id); @@ -413,8 +421,9 @@ void Paste(bool isBlock) blockref.Move(Point3d.Origin, mousePoint); drawEntitys.Enqueue(blockref); }); - moveJig.SetOptions(clipboardInfo.Point) - .Keywords.Add("A", "A", "引线点粘贴(A)"); + var jppo = moveJig.SetOptions(clipboardInfo.Point); + jppo.Keywords.Add(" ", " ", "<空格取消>"); + jppo.Keywords.Add("A", "A", "引线点粘贴(A)"); var dr = moveJig.Drag(); Point3d moveTo = Point3d.Origin; @@ -582,7 +591,7 @@ static string CreateTempFileName(string format = "X") //} -#if true2 +#if !ac2008 public class TestImageFormat { public ImageFormat GetFormat(string filename) @@ -601,16 +610,28 @@ public ImageFormat GetFormat(string filename) return imf; } - [CommandMethod("CPI")] + // 此处相当于截图,可以后台用 + [CommandMethod(nameof(CreatePreviewImage))] public void CreatePreviewImage() { - // Get the size of the document and capture the preview at that size - var size = Document.Window.DeviceIndependentSize; - using (var bmp = Document.CapturePreviewImage(Convert.ToUInt32(size.Width), Convert.ToUInt32(size.Height))) - { - // Save the file with the format derived from the filename - bmp.Save(outFile, GetFormat(outFile)); - } + using DBTrans tr = new(); + if (tr.Document == null) + return; + + var size = tr.Document.Window.DeviceIndependentSize; + using var bmp = tr.Document.CapturePreviewImage( + Convert.ToUInt32(size.Width), + Convert.ToUInt32(size.Height)); + + //保存wmf会变png,看二进制签名 + // var outFile = Path.ChangeExtension(tr.Database.Filename, ".wmf"); + // bmp.Save(outFile, GetFormat(outFile)); + + var outFile = Path.ChangeExtension(tr.Database.Filename, ".bmp"); + bmp.Save(outFile, GetFormat(outFile)); + + Env.Printl($"保存文件:{outFile}"); + Env.Printl($"保存后缀:{GetFormat(outFile)}"); } } #endif \ No newline at end of file -- Gitee From 7d23b00ae6769fc9109ec0664fd6ae4a3379304c Mon Sep 17 00:00:00 2001 From: liuqihong <540762622@qq.com> Date: Tue, 4 Oct 2022 06:33:34 +0800 Subject: [PATCH 545/675] =?UTF-8?q?=E8=AF=BB=E5=8F=96=E5=89=AA=E8=B4=B4?= =?UTF-8?q?=E6=9D=BF=E7=9A=84=E6=96=B9=E5=BC=8F=E5=A2=9E=E5=8A=A0winapi?= =?UTF-8?q?=E7=9A=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/IFoxCAD.Cad.Shared/Copyclip/EmfTool.cs | 65 +++++++- .../Copyclip/TagClipboardInfo.cs | 41 +++-- tests/TestShared/Copyclip.cs | 149 ++++++++++++++---- 3 files changed, 206 insertions(+), 49 deletions(-) diff --git a/src/IFoxCAD.Cad.Shared/Copyclip/EmfTool.cs b/src/IFoxCAD.Cad.Shared/Copyclip/EmfTool.cs index e4a12f4..a70542c 100644 --- a/src/IFoxCAD.Cad.Shared/Copyclip/EmfTool.cs +++ b/src/IFoxCAD.Cad.Shared/Copyclip/EmfTool.cs @@ -145,8 +145,9 @@ public enum MappingModes } -[StructLayout(LayoutKind.Sequential)] -public struct ENHMETAHEADER +[Serializable] +[StructLayout(LayoutKind.Sequential, Pack = 2)] +public struct EnhMetaHeader { public uint iType; public int nSize; @@ -167,10 +168,66 @@ public struct ENHMETAHEADER public uint offPixelFormat; public uint bOpenGL; public IntSize szlMicrometers; + + public override string ToString() + { + return + nameof(iType) + ":" + iType.ToString() + Environment.NewLine + + nameof(nSize) + ":" + nSize.ToString() + Environment.NewLine + + nameof(rclBounds) + ":" + rclBounds.ToString() + Environment.NewLine + + nameof(rclFrame) + ":" + rclFrame.ToString() + Environment.NewLine + + nameof(dSignature) + ":" + dSignature.ToString() + Environment.NewLine + + nameof(nVersion) + ":" + nVersion.ToString() + Environment.NewLine + + nameof(nBytes) + ":" + nBytes.ToString() + Environment.NewLine + + nameof(nRecords) + ":" + nRecords.ToString() + Environment.NewLine + + nameof(nHandles) + ":" + nHandles.ToString() + Environment.NewLine + + nameof(sReserved) + ":" + sReserved.ToString() + Environment.NewLine + + nameof(nDescription) + ":" + nDescription.ToString() + Environment.NewLine + + nameof(offDescription) + ":" + offDescription.ToString() + Environment.NewLine + + nameof(nPalEntries) + ":" + nPalEntries.ToString() + Environment.NewLine + + nameof(szlDevice) + ":" + szlDevice.ToString() + Environment.NewLine + + nameof(szlMillimeters) + ":" + szlMillimeters.ToString() + Environment.NewLine + + nameof(cbPixelFormat) + ":" + cbPixelFormat.ToString() + Environment.NewLine + + nameof(offPixelFormat) + ":" + offPixelFormat.ToString() + Environment.NewLine + + nameof(bOpenGL) + ":" + bOpenGL.ToString() + Environment.NewLine + + nameof(szlMicrometers) + ":" + szlMicrometers; + } } public static class EmfTool { + /// + /// 返回对一个增强型图元文件的说明 + /// + /// 目标增强型图元文件的句柄 + /// lpszDescription缓冲区的长度 + /// 指定一个预先初始化好的字串缓冲区,准备随同图元文件说明载入。 + /// 参考CreateEnhMetaFile函数,了解增强型图元文件说明字串的具体格式 + /// + [DllImport("gdi32.dll")] + public static extern uint GetEnhMetaFileDescription( + IntPtr hemf, + uint cchBuffer, + [MarshalAs(UnmanagedType.LPWStr)] + //[In,Out] + StringBuilder lpDescription + ); + + [System.Diagnostics.DebuggerStepThrough()] + [System.CodeDom.Compiler.GeneratedCode("InteropSignatureToolkit", "0.9 Beta1")] + public static uint GetEnhMetaFileDescriptionString(IntPtr hemf, out string lpDescription) + { + StringBuilder sb = new(1024); + uint methodRetVar = GetEnhMetaFileDescription(hemf, (uint)sb.Capacity, sb); + lpDescription = sb.ToString(); + return methodRetVar; + } + + + [DllImport("gdi32.dll", EntryPoint = "GetEnhMetaFileHeader")] + public static extern uint GetEnhMetaFileHeader(IntPtr hemf, uint cbBuffer, IntPtr /*ENHMETAHEADER*/ lpemh); + + /// /// 将一个标准Windows图元文件转换成增强型图元文件 /// @@ -193,8 +250,6 @@ public static class EmfTool /// [DllImport("gdi32.dll", EntryPoint = "SetWinMetaFileBits")] public static extern IntPtr SetWinMetaFileBits(uint nSize, IntPtr lpMeta16Data, IntPtr hdcRef, IntPtr lpMFP); - - /// /// 获取矢量图的byte /// @@ -276,7 +331,7 @@ public static void SaveMetaFile(this Metafile file, string emfName) IntPtr handle = mf.GetHenhmetafile(); if (handle != IntPtr.Zero) { - uint size = GetEnhMetaFileBits(handle, 0, null!); + var size = GetEnhMetaFileBits(handle, 0, null!); if (size != 0) { arr = new byte[size]; diff --git a/src/IFoxCAD.Cad.Shared/Copyclip/TagClipboardInfo.cs b/src/IFoxCAD.Cad.Shared/Copyclip/TagClipboardInfo.cs index e4d513b..c6dee45 100644 --- a/src/IFoxCAD.Cad.Shared/Copyclip/TagClipboardInfo.cs +++ b/src/IFoxCAD.Cad.Shared/Copyclip/TagClipboardInfo.cs @@ -124,21 +124,23 @@ void GetSize() #endregion #region 视口指针 - //[CommandMethod(nameof(testAcedGetAcadDwgview))] - //public void testAcedGetAcadDwgview() - //{ - // var dm = Acap.DocumentManager; - // var doc = dm.MdiActiveDocument; - // var ed = doc.Editor; + /* + [CommandMethod(nameof(testAcedGetAcadDwgview))] + public void testAcedGetAcadDwgview() + { + var dm = Acap.DocumentManager; + var doc = dm.MdiActiveDocument; + var ed = doc.Editor; - // var a = AcedGetAcadDwgview().ToString(); //自动执行的时候就存在了 - // var b = ed.CurrentViewportObjectId.ToString(); - // Debug.WriteLine("a == b:" + a == b);//不对 + var a = AcedGetAcadDwgview().ToString(); //自动执行的时候就存在了 + var b = ed.CurrentViewportObjectId.ToString(); + Debug.WriteLine("a == b:" + a == b);//不对 - // var tab = ed.GetCurrentView(); - // var c = tab.ObjectId.ToString(); - // Debug.WriteLine("a == c:" + a == c);//不对 - //} + var tab = ed.GetCurrentView(); + var c = tab.ObjectId.ToString(); + Debug.WriteLine("a == c:" + a == c);//不对 + } + */ /// /// 获取视口指针 @@ -263,13 +265,18 @@ public override int GetHashCode() [StructLayout(LayoutKind.Sequential)] public struct IntSize { - public int cx; - public int cy; + public int Hight; + public int Width; public IntSize(int cx, int cy) { - this.cx = cx; - this.cy = cy; + this.Hight = cx; + this.Width = cy; + } + + public override string ToString() + { + return $"({Hight},{Width})"; } } diff --git a/tests/TestShared/Copyclip.cs b/tests/TestShared/Copyclip.cs index 8fbf33a..2d808b3 100644 --- a/tests/TestShared/Copyclip.cs +++ b/tests/TestShared/Copyclip.cs @@ -6,9 +6,12 @@ namespace Test; using Autodesk.AutoCAD.DatabaseServices; using System; using System.Diagnostics; +using System.Drawing; using System.Drawing.Imaging; using System.Threading; using System.Windows; +using System.Windows.Media; +using Image = System.Drawing.Image; /* * 0x01 (已完成) @@ -271,10 +274,10 @@ void Copy(bool getPoint, bool isEraseSsget = false) #region 写入 WMF 数据 // 通过cad com导出wmf,再将wmf转为emf,然后才能写入剪贴板 - IntPtr wmfMeta = IntPtr.Zero; + IntPtr emf = IntPtr.Zero; var wmf = Path.ChangeExtension(cadClipType.File, "wmf"); Env.Editor.ExportWMF(wmf, idArray); - wmfMeta = PlaceableMetaHeader.Wmf2Emf(wmf); + emf = PlaceableMetaHeader.Wmf2Emf(wmf); #endregion // 必须一次性写入剪贴板,详见 OpenClipboardTask @@ -286,18 +289,11 @@ void Copy(bool getPoint, bool isEraseSsget = false) }, false/*不释放内存*/, false/*不锁定内存(否则高频触发时候卡死)*/); // 写入剪贴板: wmf,使得在粘贴链接的时候可以用 - if (wmfMeta != IntPtr.Zero) - { - ClipTool.SetClipboardData((uint)ClipboardFormat.CF_ENHMETAFILE, wmfMeta); - EmfTool.DeleteEnhMetaFile(wmfMeta); - } - - // c# cad截图 https://www.cnblogs.com/shangdishijiao/p/15166499.html - // 写入剪贴板: BMP位图,这是截图,不是WMF转BMP,不对 - // BitmapTool.CaptureWndImage(doc.Window.Handle, bitmapHandle => { - // ClipTool.SetClipboardData((uint)ClipboardFormat.CF_BITMAP, bitmapHandle); - // }); + if (emf != IntPtr.Zero) + ClipTool.SetClipboardData((uint)ClipboardFormat.CF_ENHMETAFILE, emf); }); + if (emf != IntPtr.Zero) + EmfTool.DeleteEnhMetaFile(emf); // 成功拷贝就删除上一次的临时文件 if (getFlag) @@ -454,15 +450,107 @@ void Paste(bool isBlock) try { #region 读取剪贴板WMF - // c# 读取成功,win32直接读取剪贴板的话是不成功的 - if (Clipboard.ContainsData(DataFormats.EnhancedMetafile)) + var msg = new StringBuilder(); + + int a3 = 2 | 4; + if ((a3 & 1) == 1) + { + // win32api 不成功 + ClipTool.OpenClipboardTask(false, () => { + // 剪贴板数据保存目标数据列表 + List _bytes = new(); + var cf = (uint)ClipboardFormat.CF_ENHMETAFILE; + var clipTypeData = ClipTool.GetClipboardData(cf); + if (clipTypeData == IntPtr.Zero) + { + Env.Printl("失败:GetClipboardData"); + return; + } + + // 无法锁定剪贴板emf内存,也无法获取GlobalSize + bool locked = WindowsAPI.GlobalLockTask(clipTypeData, prt => { + uint size = WindowsAPI.GlobalSize(prt); + if (size > 0) + { + var buffer = new byte[size]; + Marshal.Copy(prt, buffer, 0, buffer.Length); + _bytes.Add(buffer); + } + }); + if (!locked) + Env.Printl("锁定内存失败"); + }); + } + if ((a3 & 2) == 2) + { + ClipTool.OpenClipboardTask(false, () => { + // 无法锁定剪贴板emf内存,也无法获取GlobalSize + // 需要直接通过指针跳转到指定emf结构位置 + var cf = (uint)ClipboardFormat.CF_ENHMETAFILE; + var clipTypeData = ClipTool.GetClipboardData(cf); + if (clipTypeData == IntPtr.Zero) + { + Env.Printl("失败:GetClipboardData"); + return; + } + + int a4 = 2 | 4; + if ((a4 & 1) == 1) + { + // 此处无效 + var len = EmfTool.GetEnhMetaFileDescription(clipTypeData, 0, null!); + if (len != 0) + { + //PTSTR desc = (PTSTR)malloc(sizeof(TCHAR) * (len + 1)); + //GetEnhMetaFileDescription(clipTypeData, len, desc); + EmfTool.GetEnhMetaFileDescriptionString(clipTypeData, out string desc); + msg.AppendLine(desc); + } + } + if ((a4 & 2) == 2) + { + // 获取文件信息 + var len = EmfTool.GetEnhMetaFileHeader(clipTypeData, 0, IntPtr.Zero); + if (len != 0) + { + IntPtr header = Marshal.AllocHGlobal((int)len); + len = EmfTool.GetEnhMetaFileHeader(clipTypeData, len, header); + // 将内存空间转换为目标结构体 + var obj = (EnhMetaHeader)Marshal.PtrToStructure(header, typeof(EnhMetaHeader)); + msg.AppendLine(obj.ToString()); + Marshal.FreeHGlobal(header); + } + } + if ((a4 & 4) == 4) + { + // 保存emf文件 + // https://blog.csdn.net/tigertianx/article/details/7098490 + var len = EmfTool.GetEnhMetaFileBits(clipTypeData, 0, null!); + if (len != 0) + { + var bytes = new byte[len]; + _ = EmfTool.GetEnhMetaFileBits(clipTypeData, len, bytes); + + using MemoryStream ms1 = new(bytes); + using var bm = Image.FromStream(ms1);//此方法emf保存成任何版本都会变成png + bm.Save("D:\\桌面\\a.png"); + } + } + }); + } + if ((a3 & 4) == 4) { - var iData = Clipboard.GetDataObject();//从剪切板获取数据 - if (!iData.GetDataPresent(DataFormats.EnhancedMetafile)) - return; - var metafile = (Metafile)iData.GetData(DataFormats.EnhancedMetafile); - Env.Printl(nameof(Metafile) + metafile.Size.ToString()); + // c# 读取成功,win32直接读取剪贴板的话是不成功的 + if (Clipboard.ContainsData(DataFormats.EnhancedMetafile)) + { + var iData = Clipboard.GetDataObject();//从剪切板获取数据 + if (!iData.GetDataPresent(DataFormats.EnhancedMetafile)) + return; + var metafile = (Metafile)iData.GetData(DataFormats.EnhancedMetafile); + msg.AppendLine("c#::" + metafile.Size.ToString()); + } } + Env.Printl($"{nameof(Metafile)}:{msg}"); #endregion } catch (Exception e) @@ -610,7 +698,8 @@ public ImageFormat GetFormat(string filename) return imf; } - // 此处相当于截图,可以后台用 + // 此处相当于截图,后台没有doc不可用 + // https://www.cnblogs.com/shangdishijiao/p/15166499.html [CommandMethod(nameof(CreatePreviewImage))] public void CreatePreviewImage() { @@ -618,20 +707,26 @@ public void CreatePreviewImage() if (tr.Document == null) return; - var size = tr.Document.Window.DeviceIndependentSize; - using var bmp = tr.Document.CapturePreviewImage( + var doc = tr.Document; + + var size = doc.Window.DeviceIndependentSize; + using var bmp = doc.CapturePreviewImage( Convert.ToUInt32(size.Width), Convert.ToUInt32(size.Height)); //保存wmf会变png,看二进制签名 - // var outFile = Path.ChangeExtension(tr.Database.Filename, ".wmf"); - // bmp.Save(outFile, GetFormat(outFile)); - var outFile = Path.ChangeExtension(tr.Database.Filename, ".bmp"); bmp.Save(outFile, GetFormat(outFile)); - Env.Printl($"保存文件:{outFile}"); Env.Printl($"保存后缀:{GetFormat(outFile)}"); + + // 利用winAPI截图 + bool getFlag = ClipTool.OpenClipboardTask(true, () => { + BitmapTool.CaptureWndImage(doc.Window.Handle, bitmapHandle => { + // 写入剪贴板: BMP位图,这是截图,不是WMF转BMP,不对 + ClipTool.SetClipboardData((uint)ClipboardFormat.CF_BITMAP, bitmapHandle); + }); + }); } } #endif \ No newline at end of file -- Gitee From 798ddffede4c00353d8ea8c9835e9bac004aed34 Mon Sep 17 00:00:00 2001 From: liuqihong <540762622@qq.com> Date: Tue, 4 Oct 2022 07:14:26 +0800 Subject: [PATCH 546/675] =?UTF-8?q?=E5=B1=8F=E8=94=BD=E6=B5=8B=E8=AF=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- tests/TestShared/Copyclip.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/TestShared/Copyclip.cs b/tests/TestShared/Copyclip.cs index 2d808b3..9654890 100644 --- a/tests/TestShared/Copyclip.cs +++ b/tests/TestShared/Copyclip.cs @@ -494,7 +494,7 @@ void Paste(bool isBlock) return; } - int a4 = 2 | 4; + int a4 = 0; if ((a4 & 1) == 1) { // 此处无效 -- Gitee From cb8f2b46ee8c6b937ed921680c332d95fe5e8ad6 Mon Sep 17 00:00:00 2001 From: liuqihong <540762622@qq.com> Date: Tue, 4 Oct 2022 07:16:39 +0800 Subject: [PATCH 547/675] =?UTF-8?q?=E5=B1=8F=E8=94=BD=E6=B5=8B=E8=AF=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- tests/TestShared/Copyclip.cs | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/tests/TestShared/Copyclip.cs b/tests/TestShared/Copyclip.cs index 9654890..25835b4 100644 --- a/tests/TestShared/Copyclip.cs +++ b/tests/TestShared/Copyclip.cs @@ -452,7 +452,7 @@ void Paste(bool isBlock) #region 读取剪贴板WMF var msg = new StringBuilder(); - int a3 = 2 | 4; + int a3 = 0; if ((a3 & 1) == 1) { // win32api 不成功 @@ -494,7 +494,7 @@ void Paste(bool isBlock) return; } - int a4 = 0; + int a4 = 2 | 4; if ((a4 & 1) == 1) { // 此处无效 @@ -550,7 +550,9 @@ void Paste(bool isBlock) msg.AppendLine("c#::" + metafile.Size.ToString()); } } - Env.Printl($"{nameof(Metafile)}:{msg}"); + + if (msg.Length != 0) + Env.Printl($"{nameof(Metafile)}:{msg}"); #endregion } catch (Exception e) -- Gitee From 15154fe7ad1afea2ddb404bb63eb47182561a84e Mon Sep 17 00:00:00 2001 From: liuqihong <540762622@qq.com> Date: Tue, 4 Oct 2022 14:32:44 +0800 Subject: [PATCH 548/675] =?UTF-8?q?=E4=BC=98=E5=8C=96=E6=A0=BC=E5=BC=8F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 2 +- src/IFoxCAD.Cad.Shared/Runtime/DBTrans.cs | 2 +- src/IFoxCAD.Cad.Shared/Runtime/SymbolTable.cs | 2 +- tests/TestShared/Copyclip.cs | 4 +- tests/TestShared/Test.cs | 40 +++---- tests/TestShared/TestAOP.cs | 6 +- tests/TestShared/TestCurve.cs | 12 +- tests/TestShared/TestDBTrans.cs | 25 ++--- tests/TestShared/TestDwgFilerEx.cs | 6 +- tests/TestShared/TestEnt.cs | 19 ++-- tests/TestShared/TestJig.cs | 4 +- tests/TestShared/TestJigExTransient.cs | 28 ++--- tests/TestShared/TestMirrorFile.cs | 4 +- tests/TestShared/TestQuadTree.cs | 6 +- tests/TestShared/Testid.cs | 17 ++- tests/TestShared/testConvexHull.cs | 4 +- tests/TestShared/testblock.cs | 104 +++++++++--------- tests/TestShared/testeditor.cs | 4 +- 18 files changed, 135 insertions(+), 154 deletions(-) diff --git a/README.md b/README.md index b2ff7d4..74eae3c 100644 --- a/README.md +++ b/README.md @@ -101,7 +101,7 @@ IFox的[jing分支](https://gitee.com/inspirefunction/ifoxcad/tree/jing/)是一 [CommandMethod("hello")] public void Hello() { - using var tr = new DBTrans(); + using DBTrans tr = new(); var line1 = new Line(new Point3d(0, 0, 0), new Point3d(1, 1, 0)); tr.CurrentSpace.AddEntity(line1); // 如果您没有添加preview到项目文件里的话:按如下旧语法: diff --git a/src/IFoxCAD.Cad.Shared/Runtime/DBTrans.cs b/src/IFoxCAD.Cad.Shared/Runtime/DBTrans.cs index 64369b1..e283777 100644 --- a/src/IFoxCAD.Cad.Shared/Runtime/DBTrans.cs +++ b/src/IFoxCAD.Cad.Shared/Runtime/DBTrans.cs @@ -52,7 +52,7 @@ public static DBTrans Top * 0x02 * 跨文档事务出错 Autodesk.AutoCAD.Runtime.Exception:“eNotFromThisDocument” * Curves.GetEntities()会从Top获取事务(Top会new一个),此时会是当前文档; - * 然后命令文中发生了 using var tr = new DBTrans(); + * 然后命令文中发生了 using DBTrans tr = new(); * 当退出命令此事务释放,但是从来不释放Top, * 然后我新建了一个文档,再进行命令=>又进入Top,Top返回了前一个文档的事务 * 因此所以无法清理栈,所以Dispose不触发,导致无法刷新图元和Ctrl+Z出错 diff --git a/src/IFoxCAD.Cad.Shared/Runtime/SymbolTable.cs b/src/IFoxCAD.Cad.Shared/Runtime/SymbolTable.cs index 7374ae4..2a4327d 100644 --- a/src/IFoxCAD.Cad.Shared/Runtime/SymbolTable.cs +++ b/src/IFoxCAD.Cad.Shared/Runtime/SymbolTable.cs @@ -295,7 +295,7 @@ internal ObjectId GetRecordFrom(Func> tabl string name, bool over) { - using var tr = new DBTrans(fileName); + using DBTrans tr = new(fileName); return GetRecordFrom(tableSelector(tr), name, over); } #endregion diff --git a/tests/TestShared/Copyclip.cs b/tests/TestShared/Copyclip.cs index 25835b4..e6ddadb 100644 --- a/tests/TestShared/Copyclip.cs +++ b/tests/TestShared/Copyclip.cs @@ -217,7 +217,7 @@ void Copy(bool getPoint, bool isEraseSsget = false) Thread.Sleep(1); } - using var tr = new DBTrans(); + using DBTrans tr = new(); #region 写入 AutoCAD.R17 数据 if (getPoint) @@ -252,7 +252,7 @@ void Copy(bool getPoint, bool isEraseSsget = false) var cadClipType = new TagClipboardInfo(tempFile, pt); // 克隆到目标块表内 - using (var fileTr = new DBTrans(cadClipType.File)) + using (DBTrans fileTr = new(cadClipType.File)) { fileTr.Task(() => { using IdMapping map = new(); diff --git a/tests/TestShared/Test.cs b/tests/TestShared/Test.cs index ceea8de..92abfc4 100644 --- a/tests/TestShared/Test.cs +++ b/tests/TestShared/Test.cs @@ -6,7 +6,7 @@ public partial class Test [CommandMethod("dbtest")] public void Dbtest() { - using var tr = new DBTrans(); + using DBTrans tr = new(); if (tr.Editor is null) return; tr.Editor.WriteMessage("\n测试 Editor 属性是否工作!"); @@ -38,7 +38,7 @@ public void Dbtest() [CommandMethod("addent")] public void Addent() { - using var tr = new DBTrans(); + using DBTrans tr = new(); Line line = new(new Point3d(0, 0, 0), new Point3d(1, 1, 0)); tr.CurrentSpace.AddEntity(line); Line line1 = new(new Point3d(10, 10, 0), new Point3d(41, 1, 0)); @@ -50,7 +50,7 @@ public void Addent() [CommandMethod("drawarc")] public void Drawarc() { - using var tr = new DBTrans(); + using DBTrans tr = new(); Arc arc1 = EntityEx.CreateArcSCE(new Point3d(2, 0, 0), new Point3d(0, 0, 0), new Point3d(0, 2, 0));// 起点,圆心,终点 Arc arc2 = EntityEx.CreateArc(new Point3d(4, 0, 0), new Point3d(0, 0, 0), Math.PI / 2); // 起点,圆心,弧度 Arc arc3 = EntityEx.CreateArc(new Point3d(1, 0, 0), new Point3d(0, 0, 0), new Point3d(0, 1, 0)); // 起点,圆上一点,终点 @@ -61,7 +61,7 @@ public void Drawarc() [CommandMethod("drawcircle")] public void DraCircle() { - using var tr = new DBTrans(); + using DBTrans tr = new(); var circle1 = EntityEx.CreateCircle(new Point3d(0, 0, 0), new Point3d(1, 0, 0)); // 起点,终点 var circle2 = EntityEx.CreateCircle(new Point3d(-2, 0, 0), new Point3d(2, 0, 0), new Point3d(0, 2, 0));// 三点画圆,成功 var circle3 = EntityEx.CreateCircle(new Point3d(-2, 0, 0), new Point3d(0, 0, 0), new Point3d(2, 0, 0));// 起点,圆心,终点,失败 @@ -82,12 +82,11 @@ public void DraCircle() [CommandMethod("layertest")] public void Layertest() { - using var tr = new DBTrans(); + using DBTrans tr = new(); tr.LayerTable.Add("1"); tr.LayerTable.Add("2", lt => { lt.Color = Color.FromColorIndex(ColorMethod.ByColor, 1); lt.LineWeight = LineWeight.LineWeight030; - }); tr.LayerTable.Remove("3"); tr.LayerTable.Delete("0"); @@ -101,7 +100,7 @@ public void Layertest() [CommandMethod("layerAdd1")] public void Layertest1() { - using var tr = new DBTrans(); + using DBTrans tr = new(); tr.LayerTable.Add("test1", Color.FromColorIndex(ColorMethod.ByColor, 1)); } @@ -109,7 +108,7 @@ public void Layertest1() [CommandMethod("layerAdd2")] public void Layertest2() { - using var tr = new DBTrans(); + using DBTrans tr = new(); tr.LayerTable.Add("test2", 2); // tr.LayerTable["3"] = new LayerTableRecord(); } @@ -117,7 +116,7 @@ public void Layertest2() [CommandMethod("layerdel")] public void LayerDel() { - using var tr = new DBTrans(); + using DBTrans tr = new(); Env.Editor.WriteMessage(tr.LayerTable.Delete("0").ToString()); // 删除图层 0 Env.Editor.WriteMessage(tr.LayerTable.Delete("Defpoints").ToString());// 删除图层 Defpoints Env.Editor.WriteMessage(tr.LayerTable.Delete("1").ToString()); // 删除不存在的图层 1 @@ -131,7 +130,7 @@ public void LayerDel() [CommandMethod("linedemo1")] public void AddLine1() { - using var tr = new DBTrans(); + using DBTrans tr = new(); // tr.ModelSpace.AddEnt(line); // tr.ModelSpace.AddEnts(line,circle); @@ -158,7 +157,7 @@ public void AddLine1() [CommandMethod("Pldemo1")] public void AddPolyline1() { - using var tr = new DBTrans(); + using DBTrans tr = new(); Polyline pl = new(); pl.SetDatabaseDefaults(); pl.AddVertexAt(0, new Point2d(0, 0), 0, 0, 0); @@ -183,7 +182,7 @@ public void Addpl2() (new Point3d(0,10,0),0,0,0), (new Point3d(5,5,0),0,0,0) }; - using var tr = new DBTrans(); + using DBTrans tr = new(); tr.CurrentSpace.AddPline(pts); } @@ -196,7 +195,7 @@ public void Addpl2() [CommandMethod("addxdata")] public void AddXdata() { - using var tr = new DBTrans(); + using DBTrans tr = new(); var appname = "myapp2"; tr.RegAppTable.Add("myapp1"); @@ -231,7 +230,7 @@ public void GetXdata() { var doc = Acap.DocumentManager.MdiActiveDocument; var ed = doc.Editor; - using var tr = new DBTrans(); + using DBTrans tr = new(); tr.RegAppTable.ForEach(id => id.GetObject()?.Name.Print()); tr.RegAppTable.GetRecords().ForEach(rec => rec.Name.Print()); @@ -241,7 +240,7 @@ public void GetXdata() // var res = ed.GetEntity("\n select the entity:"); // if (res.Status == PromptStatus.OK) // { - // using var tr = new DBTrans(); + // using DBTrans tr = new(); // tr.RegAppTable.ForEach(id => id.GetObject().Print()); // var data = tr.GetObject(res.ObjectId).XData; // ed.WriteMessage(data.ToString()); @@ -257,7 +256,7 @@ public void Changexdata() var res = ed.GetEntity("\n select the entity:"); if (res.Status == PromptStatus.OK) { - using var tr = new DBTrans(); + using DBTrans tr = new(); var data = tr.GetObject(res.ObjectId)!; data.ChangeXData(appname, DxfCode.ExtendedDataAsciiString, "change"); @@ -275,7 +274,7 @@ public void Removexdata() var res = ed.GetEntity("\n select the entity:"); if (res.Status == PromptStatus.OK) { - using var tr = new DBTrans(); + using DBTrans tr = new(); var ent = tr.GetObject(res.ObjectId); ed.WriteMessage("\n移除前:" + ent?.XData.ToString()); @@ -287,7 +286,7 @@ public void Removexdata() [CommandMethod("PrintLayerName")] public void PrintLayerName() { - using var tr = new DBTrans(); + using DBTrans tr = new(); foreach (var layerRecord in tr.LayerTable.GetRecords()) { tr.Editor?.WriteMessage(layerRecord.Name); @@ -324,7 +323,6 @@ public void TestRec() { result = p41.IsParallelTo(p12); } - }); Tools.TestTimes(1000000, "三次点乘", () => { @@ -336,7 +334,6 @@ public void TestRec() { result = true; } - }); Tools.TestTimes(1000000, "三次垂直", () => { @@ -347,7 +344,6 @@ public void TestRec() { result = true; } - }); } @@ -363,4 +359,4 @@ public Document Getdoc() return doc; } } -#pragma warning restore CS0219 // 变量已被赋值,但从未使用过它的值 +#pragma warning restore CS0219 // 变量已被赋值,但从未使用过它的值 \ No newline at end of file diff --git a/tests/TestShared/TestAOP.cs b/tests/TestShared/TestAOP.cs index 2b9169f..8b0e638 100644 --- a/tests/TestShared/TestAOP.cs +++ b/tests/TestShared/TestAOP.cs @@ -21,7 +21,7 @@ public void Initialize() namespace Test { - /* + /* * 天秀的事务注入,让你告别事务处理 * https://www.cnblogs.com/JJBox/p/16157578.html */ @@ -56,7 +56,7 @@ public void IFoxRefuseInjectionTransaction2() { // 拒绝注入就要自己开事务,通常用在循环提交事务上面. // 另见 报错0x02 https://www.cnblogs.com/JJBox/p/10798940.html - using var tr = new DBTrans(); + using DBTrans tr = new(); } [CommandMethod("InjectionTransaction2")] @@ -64,5 +64,5 @@ public void InjectionTransaction2() { } } -} +} #endif \ No newline at end of file diff --git a/tests/TestShared/TestCurve.cs b/tests/TestShared/TestCurve.cs index b01280b..a8ad2d5 100644 --- a/tests/TestShared/TestCurve.cs +++ b/tests/TestShared/TestCurve.cs @@ -26,7 +26,7 @@ public void TestPointInDict() [CommandMethod("testgraph")] public void TestGraph1() { - using var tr = new DBTrans(); + using DBTrans tr = new(); var ents = Env.Editor.SSGet()?.Value?.GetEntities(); if (ents == null) return; @@ -37,13 +37,12 @@ public void TestGraph1() Env.Print(res.Count()); tr.CurrentSpace.AddEntity(res); }); - } [CommandMethod("testgraphspeed")] public void TestGraphspeed() { - using var tr = new DBTrans(); + using DBTrans tr = new(); var ents = Env.Editor.SSGet()?.Value?.GetEntities(); if (ents == null) return; @@ -78,11 +77,10 @@ public void TestGraphspeed() // 查询全部的 闭合环 dfs.FindAll(graph); }); -#endif +#endif // res.ForEach((i, t) => t.ForWrite(e => e.ColorIndex = i + 1)); // tr.CurrentSpace.AddEntity(res); - } } @@ -94,7 +92,7 @@ public class TestCurve [CommandMethod("testbreakcurve")] public void TestBreakCurve() { - using var tr = new DBTrans(); + using DBTrans tr = new(); var ents = Env.Editor.SSGet()?.Value.GetEntities(); if (ents is null) return; @@ -106,7 +104,7 @@ public void TestBreakCurve() [CommandMethod("testCurveCurveIntersector3d")] public void TestCurveCurveIntersector3d() { - using var tr = new DBTrans(); + using DBTrans tr = new(); var ents = Env.Editor.SSGet()? .Value.GetEntities() .Select(e => e?.ToCompositeCurve3d()).ToList(); diff --git a/tests/TestShared/TestDBTrans.cs b/tests/TestShared/TestDBTrans.cs index 5ffefa8..89cdaeb 100644 --- a/tests/TestShared/TestDBTrans.cs +++ b/tests/TestShared/TestDBTrans.cs @@ -6,7 +6,7 @@ public class TestTrans [CommandMethod(nameof(FileNotExist))] public void FileNotExist() { - using var tr = new DBTrans("test.dwg"); + using DBTrans tr = new("test.dwg"); tr.SaveFile((DwgVersion)24, false); } @@ -14,7 +14,7 @@ public void FileNotExist() [CommandMethod(nameof(FileNotExist2))] public void FileNotExist2() { - using var tr = new DBTrans(); + using DBTrans tr = new(); tr.SaveFile(saveAsFile: "D:\\"); } @@ -22,10 +22,10 @@ public void FileNotExist2() [CommandMethod(nameof(FileNotExist3))] public void FileNotExist3() { - using var tr = new DBTrans("D:\\"); + using DBTrans tr = new("D:\\"); tr.SaveDwgFile(); - using var tr2 = new DBTrans("D:\\"); + using DBTrans tr2 = new("D:\\"); tr2.SaveFile(saveAsFile: "D:\\"); } @@ -34,7 +34,7 @@ public void FileNotExist3() public void Testtr() { string filename = @"C:\Users\vic\Desktop\test.dwg"; - using var tr = new DBTrans(filename); + using DBTrans tr = new(filename); tr.ModelSpace.AddCircle(new Point3d(10, 10, 0), 20); // tr.Database.SaveAs(filename,DwgVersion.Current); tr.SaveDwgFile(); @@ -42,8 +42,7 @@ public void Testtr() [CommandMethod("testifoxcommit")] public void Testifoxcommit() { - - using var tr = new DBTrans(); + using DBTrans tr = new(); tr.ModelSpace.AddCircle(new Point3d(0, 0, 0), 20); tr.Abort(); // tr.Commit(); @@ -55,7 +54,7 @@ public void Testifoxcommit() // [CommandMethod("TESTAOP")] // public void testaop() // { - // // 不用 using var tr = new DBTrans(); + // // 不用 using DBTrans tr = new(); // var tr = DBTrans.Top; // tr.ModelSpace.AddCircle(new Point3d(0, 0, 0), 20); // } @@ -78,12 +77,12 @@ public void TestPt() // var pl = Env.Editor.GetEntity("pick pl").ObjectId; var tr1 = HostApplicationServices.WorkingDatabase.TransactionManager.TopTransaction; - using var tr2 = new DBTrans(); + using DBTrans tr2 = new(); var tr3 = HostApplicationServices.WorkingDatabase.TransactionManager.TopTransaction; var tr6 = Acap.DocumentManager.MdiActiveDocument.TransactionManager.TopTransaction; Env.Print(tr2.Transaction == tr3); Env.Print(tr3 == tr6); - using var tr4 = new DBTrans(); + using DBTrans tr4 = new(); var tr5 = HostApplicationServices.WorkingDatabase.TransactionManager.TopTransaction; var tr7 = Acap.DocumentManager.MdiActiveDocument.TransactionManager.TopTransaction; Env.Print(tr4.Transaction == tr5); @@ -98,9 +97,5 @@ public void TestPt() // Env.Print(pt1.IsEqualTo(pt2,new Tolerance(0.0,1e-6)).ToString()); // Env.Print((pt1 == pt2).ToString()); // Env.Print((pt1 != pt2).ToString()); - - - } - -} +} \ No newline at end of file diff --git a/tests/TestShared/TestDwgFilerEx.cs b/tests/TestShared/TestDwgFilerEx.cs index 3bd033d..d2267af 100644 --- a/tests/TestShared/TestDwgFilerEx.cs +++ b/tests/TestShared/TestDwgFilerEx.cs @@ -21,7 +21,7 @@ public static void CmdTest_DwgFilerEx() return; } - using var tr = new DBTrans(); + using DBTrans tr = new(); var ids = ssPsr.Value.GetObjectIds(); foreach (var id in ids) { @@ -53,7 +53,7 @@ public static void CmdTest_EntDxfout() if (ssPsr.Status != PromptStatus.OK) return; - using var tr = new DBTrans(); + using DBTrans tr = new(); var ids = ssPsr.Value.GetObjectIds(); foreach (var id in ids) { @@ -97,7 +97,7 @@ public static void CmdTest_TextOut() return; #endif - using var tr = new DBTrans(); + using DBTrans tr = new(); var dwgFilerEx = new DwgFilerEx(); var bText = tr.GetObject(gt1.ObjectId, OpenMode.ForRead); if (bText is null) diff --git a/tests/TestShared/TestEnt.cs b/tests/TestShared/TestEnt.cs index 010303a..0cfdad3 100644 --- a/tests/TestShared/TestEnt.cs +++ b/tests/TestShared/TestEnt.cs @@ -5,9 +5,9 @@ public class TestEnt [CommandMethod("TestEntRoration")] public void TestEntRoration() { - var line = new Line(new(0,0,0),new(100,0,0)); + var line = new Line(new(0, 0, 0), new(100, 0, 0)); - using var tr = new DBTrans(); + using DBTrans tr = new(); tr.CurrentSpace.AddEntity(line); var line2 = (Line)line.Clone(); tr.CurrentSpace.AddEntity(line2); @@ -20,19 +20,16 @@ public void TestTypeSpeed() { var line = new Line(); var line1 = line as Entity; - Tools.TestTimes(100000, "is 匹配:", () => - { - var t = line1 is Line; - }); - Tools.TestTimes(100000, "name 匹配:", () => - { + Tools.TestTimes(100000, "is 匹配:", () => { + var t = line1 is Line; + }); + Tools.TestTimes(100000, "name 匹配:", () => { // var t = line.GetType().Name; var tt = line1.GetType().Name == nameof(Line); }); - Tools.TestTimes(100000, "dxfname 匹配:", () => - { + Tools.TestTimes(100000, "dxfname 匹配:", () => { // var t = line.GetType().Name; var tt = line1.GetRXClass().DxfName == nameof(Line); }); } -} +} \ No newline at end of file diff --git a/tests/TestShared/TestJig.cs b/tests/TestShared/TestJig.cs index 9342ac9..20e5ed3 100644 --- a/tests/TestShared/TestJig.cs +++ b/tests/TestShared/TestJig.cs @@ -8,7 +8,7 @@ public class Commands_Jig [CommandMethod("TestCmd_jig33")] public static void TestCmd_jig33() { - using var tr = new DBTrans(); + using DBTrans tr = new(); var per = tr.Editor?.GetEntity("\n点选圆形:"); if (per?.Status != PromptStatus.OK) return; @@ -48,7 +48,7 @@ public static void TestCmd_jig33() [CommandMethod("TestCmd_Jig44")] public void TestCmd_Jig44() { - using var tr = new DBTrans(); + using DBTrans tr = new(); var per = Env.Editor.GetEntity("\n请选择一条多段线:"); if (per.Status != PromptStatus.OK) return; diff --git a/tests/TestShared/TestJigExTransient.cs b/tests/TestShared/TestJigExTransient.cs index 0aabbb1..2ee546f 100644 --- a/tests/TestShared/TestJigExTransient.cs +++ b/tests/TestShared/TestJigExTransient.cs @@ -40,7 +40,7 @@ public void TestJigExTransient() jet.Update(c2); // jet.UpdateAll(); - var r4 = Env.Editor.GetPoint("\n此拾取无意义,仅为了暂停查看"); + Env.Editor.GetPoint("\n此拾取无意义,仅为了暂停查看"); // 加到图纸中,为测试瞬态容器可以自行dispose消失,所以未全部加入 using DBTrans tr = new(); @@ -53,8 +53,6 @@ public void TestJigExTransient() [CommandMethod(nameof(TestJigExTransentDim))] public static void TestJigExTransentDim() { - using DBTrans tr = new(); - Editor ed = tr.Editor!; PromptPointOptions ppo = new("") { AppendKeywordsToMessage = false, @@ -63,26 +61,28 @@ public static void TestJigExTransentDim() for (int i = 0; i < 3; i++) { ppo.Message = $"\n选择标注点{i + 1}"; - var ppr = ed.GetPoint(ppo); + var ppr = Env.Editor.GetPoint(ppo); if (ppr.Status != PromptStatus.OK) return; pts.Add(ppr.Value); } - using RotatedDimension dimension = new( - rotation: 0, - line1Point: pts[0], - line2Point: pts[1], - dimensionLinePoint: pts[2], - dimensionText: "<>", - tr.Database.Dimstyle); + using DBTrans tr = new(); + + using RotatedDimension dimension = new(); + dimension.SetDatabaseDefaults();// cad16没有这个不显示 + dimension.Rotation = 0; + dimension.XLine1Point = pts[0]; + dimension.XLine2Point = pts[1]; + dimension.DimLinePoint = pts[2]; + dimension.DimensionText = "<>"; + dimension.DimensionStyle = tr.Database.Dimstyle; using JigExTransient jet = new(); - jet.Add(dimension); + jet.Add(dimension, TransientDrawingMode.Highlight); jet.UpdateAll(); - var r4 = Env.Editor.GetPoint("\n此拾取无意义,仅为了暂停查看"); - + Env.Editor.GetPoint("\n此拾取无意义,仅为了暂停查看"); tr.CurrentSpace.AddEntity(dimension); } } diff --git a/tests/TestShared/TestMirrorFile.cs b/tests/TestShared/TestMirrorFile.cs index 34bc6d6..c53e629 100644 --- a/tests/TestShared/TestMirrorFile.cs +++ b/tests/TestShared/TestMirrorFile.cs @@ -12,7 +12,7 @@ public class MirrorFile [CommandMethod("CmdTest_MirrorFile")] public static void CmdTest_MirrorFile() { - using var tr = new DBTrans(file, openMode: FileOpenMode.OpenForReadAndReadShare); + using DBTrans tr = new(file, openMode: FileOpenMode.OpenForReadAndReadShare); tr.BlockTable.Change(tr.ModelSpace.ObjectId, modelSpace => { foreach (ObjectId entId in modelSpace) @@ -41,7 +41,7 @@ public static void CmdTest_MirrorFile() [CommandMethod("CmdTest_MirrorFile2")] public static void CmdTest_MirrorFile2() { - using var tr = new DBTrans(file); + using DBTrans tr = new(file); tr.Task(() => { var yaxis = new Point3d(0, 1, 0); diff --git a/tests/TestShared/TestQuadTree.cs b/tests/TestShared/TestQuadTree.cs index e10c240..9a310e7 100644 --- a/tests/TestShared/TestQuadTree.cs +++ b/tests/TestShared/TestQuadTree.cs @@ -41,7 +41,7 @@ public partial class TestQuadTree [CommandMethod("Test_QuadTree")] public void Test_QuadTree() { - using var tr = new DBTrans(); + using DBTrans tr = new(); Rect dbExt; // 使用数据库边界来进行 @@ -356,7 +356,7 @@ void Ssget(QuadTreeSelectMode mode) if (_quadTreeRoot is null) return; - using var tr = new DBTrans(); + using DBTrans tr = new(); if (tr.Editor is null) return; var rect = GetCorner(tr.Editor); @@ -409,7 +409,7 @@ void Ssget(QuadTreeSelectMode mode) // { // public void Cmd_tt6() // { -// using var tr = new DBTrans(); +// using DBTrans tr = new(); // var ed = tr.Editor; // // 创建四叉树,默认参数无所谓 // var TreeRoot = new QuadTree(new Rect(0, 0, 32525, 32525)); diff --git a/tests/TestShared/Testid.cs b/tests/TestShared/Testid.cs index 10a4b35..ca28148 100644 --- a/tests/TestShared/Testid.cs +++ b/tests/TestShared/Testid.cs @@ -5,7 +5,7 @@ public class Testid [CommandMethod("testid")] public void TestId() { - using var tr = new DBTrans(); + using DBTrans tr = new(); Line line = new(new Point3d(0, 0, 0), new Point3d(1, 1, 0)); tr.CurrentSpace.AddEntity(line); tr.Dispose(); @@ -25,11 +25,11 @@ public void TestId() // } } - + [CommandMethod("testmycommand")] public void TestMyCommand() { - using var dbtrans = new DBTrans(Env.Document, true, false); + using DBTrans dbtrans = new(Env.Document, true, false); using var trans = Env.Database.TransactionManager.StartTransaction(); var l1 = new Line(new Point3d(0, 0, 0), new Point3d(100, 100, 0)); @@ -42,7 +42,7 @@ public void TestMyCommand() [CommandMethod("testtextstyle")] public void TestTextStyle() { - using var tr = new DBTrans(); + using DBTrans tr = new(); tr.TextStyleTable.Add("宋体", "宋体.ttf", 0.8); tr.TextStyleTable.Add("宋体1", FontTTF.宋体, 0.8); @@ -53,8 +53,7 @@ public void TestTextStyle() - tr.TextStyleTable.Add("daziti", ttr => - { + tr.TextStyleTable.Add("daziti", ttr => { ttr.FileName = "ascii.shx"; ttr.BigFontFileName = "gbcbig.shx"; }); @@ -63,11 +62,11 @@ public void TestTextStyle() [CommandMethod("testtextstylechange")] public void TestTextStyleChange() { - using var tr = new DBTrans(); - + using DBTrans tr = new(); + tr.TextStyleTable.AddWithChange("宋体1", "simfang.ttf", height: 5); tr.TextStyleTable.AddWithChange("仿宋体", "宋体.ttf"); tr.TextStyleTable.AddWithChange("fsgb2312", "Romans", "gbcbig"); } -} +} \ No newline at end of file diff --git a/tests/TestShared/testConvexHull.cs b/tests/TestShared/testConvexHull.cs index f96b302..186c50a 100644 --- a/tests/TestShared/testConvexHull.cs +++ b/tests/TestShared/testConvexHull.cs @@ -6,7 +6,7 @@ public class TestConvexHull [CommandMethod("testch")] public void Testch() { - // using var tr = new DBTrans(); + // using DBTrans tr = new(); // var pts = new List(); // var flag = true; // while (flag) @@ -73,4 +73,4 @@ public void Testch() Env.Editor.WriteMessage($"点集的有向面积:{area5} \n"); Env.Editor.WriteMessage($"点集的有向面积:{area6} \n"); } -} +} \ No newline at end of file diff --git a/tests/TestShared/testblock.cs b/tests/TestShared/testblock.cs index 2378f69..5d54e46 100644 --- a/tests/TestShared/testblock.cs +++ b/tests/TestShared/testblock.cs @@ -5,7 +5,7 @@ public class TestBlock [CommandMethod("Test_GetBoundingBoxEx")] public void Test_GetBoundingBoxEx() { - using var tr = new DBTrans(); + using DBTrans tr = new(); var ents = Env.Editor.SSGet()?.Value?.GetEntities(); if (ents == null) return; @@ -22,7 +22,7 @@ public void Test_GetBoundingBoxEx() [CommandMethod("Test_blockdef")] public void Test_BlockDef() { - using var tr = new DBTrans(); + using DBTrans tr = new(); // var line = new Line(new Point3d(0, 0, 0), new Point3d(1, 1, 0)); tr.BlockTable.Add("test", btr => { @@ -57,7 +57,7 @@ public void Test_BlockDef() [CommandMethod("Test_blockdefchange")] public void Test_BlockDefChange() { - using var tr = new DBTrans(); + using DBTrans tr = new(); // var line = new Line(new Point3d(0, 0, 0), new Point3d(1, 1, 0)); // tr.BlockTable.Change("test", btr => // { @@ -92,7 +92,7 @@ public void Test_BlockDefChange() [CommandMethod("Test_insertblockdef")] public void Test_InsertBlockDef() { - using var tr = new DBTrans(); + using DBTrans tr = new(); var line1 = new Line(new Point3d(0, 0, 0), new Point3d(1, 1, 0)); var line2 = new Line(new Point3d(0, 0, 0), new Point3d(-1, 1, 0)); var att1 = new AttributeDefinition() { Position = new Point3d(10, 10, 0), Tag = "tagTest1", Height = 1, TextString = "valueTest1" }; @@ -120,16 +120,16 @@ public void Test_InsertBlockDef() // tr.CurrentSpace.InsertBlock(new Point3d(4, 4, 0), "test1", new Scale3d(2), Math.PI / 4); // 测试放大2倍,旋转45度 var def1 = new Dictionary - { - { "tagTest1", "1" }, - { "tagTest2", "2" } - }; + { + { "tagTest1", "1" }, + { "tagTest2", "2" } + }; tr.CurrentSpace.InsertBlock(new Point3d(0, 0, 0), "test1", atts: def1); var def2 = new Dictionary - { - { "tagTest3", "1" }, - { "tagTest4", "" } - }; + { + { "tagTest3", "1" }, + { "tagTest4", "" } + }; tr.CurrentSpace.InsertBlock(new Point3d(10, 10, 0), "test2", atts: def2); tr.CurrentSpace.InsertBlock(new Point3d(-10, 0, 0), "test44"); } @@ -137,20 +137,19 @@ public void Test_InsertBlockDef() [CommandMethod("Test_addattsdef")] public void Test_AddAttsDef() { - using var tr = new DBTrans(); + using DBTrans tr = new(); var blockid = Env.Editor.GetEntity("pick block:").ObjectId; var blockref = tr.GetObject(blockid)!.BlockTableRecord; var att1 = new AttributeDefinition() { Position = new Point3d(20, 20, 0), Tag = "addtagTest1", Height = 1, TextString = "valueTest1" }; var att2 = new AttributeDefinition() { Position = new Point3d(10, 12, 0), Tag = "tagTest2", Height = 1, TextString = "valueTest2" }; - - tr.BlockTable.AddAttsToBlocks(blockref, new List { att1, att2 }); + tr.BlockTable.AddAttsToBlocks(blockref, new() { att1, att2 }); } [CommandMethod("test_blocknullbug")] public void Test_BlockNullBug() { - using var tr = new DBTrans(); + using DBTrans tr = new(); var ents = new List(); var line5 = new Line(new Point3d(0, 0, 0), new Point3d(1, 1, 0)); @@ -164,7 +163,7 @@ public void Test_BlockNullBug() [CommandMethod("test_block_file")] public void Test_BlockFile() { - var tr = new DBTrans(); + using DBTrans tr = new(); var id = tr.BlockTable.GetBlockFrom(@"C:\Users\vic\Desktop\test.dwg", false); tr.CurrentSpace.InsertBlock(Point3d.Origin, id); } @@ -173,15 +172,12 @@ public void Test_BlockFile() [CommandMethod("test_clip")] public void Test_ClipBlock() { - using var tr = new DBTrans(); - tr.BlockTable.Add("test1", - btr => { - btr.Origin = new Point3d(0, 0, 0); - btr.AddEntity(new Line(new Point3d(0, 0, 0), new Point3d(10, 10, 0)), - new Line(new Point3d(10, 10, 0), new Point3d(10, 0, 0)) - ); - } - ); + using DBTrans tr = new(); + tr.BlockTable.Add("test1", btr => { + btr.Origin = new Point3d(0, 0, 0); + btr.AddEntity(new Line(new Point3d(0, 0, 0), new Point3d(10, 10, 0)), + new Line(new Point3d(10, 10, 0), new Point3d(10, 0, 0))); + }); // tr.BlockTable.Add("hah"); var id = tr.CurrentSpace.InsertBlock(new Point3d(0, 0, 0), "test1"); var bref = tr.GetObject(id)!; @@ -196,10 +192,10 @@ public void Test_ClipBlock() /// /// 给用户的测试程序,不知道对错 /// - [CommandMethod("test_block_ej")] - public void test_block_ej() + [CommandMethod(nameof(Test_block_ej))] + public void Test_block_ej() { - using (var tr = new DBTrans()) + using (DBTrans tr = new()) { // Point3d.Origin.AddBellowToModelSpace(100, 100, 5, 3, 30);// 画波纹管 @@ -228,18 +224,16 @@ public void test_block_ej() entTest?.Draw(); } - using var tr2 = new DBTrans(); - PromptEntityOptions PEO = new("\n请选择一个块"); - PEO.SetRejectMessage("\n对象必须是块"); - PEO.AddAllowedClass(typeof(BlockReference), true); + using DBTrans tr2 = new(); + PromptEntityOptions peo = new("\n请选择一个块"); + peo.SetRejectMessage("\n对象必须是块"); + peo.AddAllowedClass(typeof(BlockReference), true); - PromptEntityResult PER = Env.Editor.GetEntity(PEO); - if (PER.Status != PromptStatus.OK) - { + var per = Env.Editor.GetEntity(peo); + if (per.Status != PromptStatus.OK) return; - } - var Bref = tr2.GetObject(PER.ObjectId)!; + var Bref = tr2.GetObject(per.ObjectId)!; // var BTR = tr.GetObject(Bref.BlockTableRecord, OpenMode.ForWrite); //// 如果知道块名字BTRName // BlockTableRecord BTR = tr.GetObject(tr.BlockTable[blockName], OpenMode.ForWrite); @@ -247,13 +241,12 @@ public void test_block_ej() var btr = tr2.BlockTable[Bref.Name]; tr2.BlockTable.Change(btr, ltr => { - foreach (ObjectId OID in ltr) + foreach (ObjectId oid in ltr) { - var ent = tr2.GetObject(OID); - using (ent!.ForWrite()) + var ent = tr2.GetObject(oid); + if (ent is MText mText) { - if (ent is MText mText) - { + using (ent.ForWrite()) switch (mText.Text) { case "$$A": @@ -264,10 +257,15 @@ public void test_block_ej() default: break; } - } - else if (ent is DBText dBText) { dBText.TextString = "haha"; } - else if (ent is Dimension dimension) - { + } + else if (ent is DBText dBText) + { + using (ent.ForWrite()) + dBText.TextString = "haha"; + } + else if (ent is Dimension dimension) + { + using (ent.ForWrite()) switch (dimension.DimensionText) { case "$$pipeLen": @@ -277,11 +275,9 @@ public void test_block_ej() default: break; } - } } } }); - tr2.Editor?.Regen(); } @@ -297,7 +293,7 @@ public void QuickBlockDef() string blockName = "W_BLOCK_" + DateTime.Now.ToString("yyyyMMdd_HHmmss"); // var rss = ed.GetSelection(promptOpt); var rss = Env.Editor.GetSelection(promptOpt); - using var tr = new DBTrans(); + using DBTrans tr = new(); if (rss.Status == PromptStatus.OK) { // SelectionSet ss = rss.Value; @@ -444,7 +440,7 @@ public void TestChangeDynameicBlock() { "haha", 1 } }; var blockid = Env.Editor.GetEntity("选择个块").ObjectId; - using var tr = new DBTrans(); + using DBTrans tr = new(); var blockref = tr.GetObject(blockid)!; blockref.ChangeBlockProperty(pro); // 这是第一个函数的用法 @@ -461,7 +457,7 @@ public void TestBack() return; } - using var tr = new DBTrans(dwg); + using DBTrans tr = new(dwg); tr.ModelSpace.GetEntities().ForEach(ent => { ent.ForWrite(e => e.ColorIndex = 3); }); @@ -480,8 +476,8 @@ public class BlockImportClass public void Test_Cbll() { string filename = @"C:\Users\vic\Desktop\Drawing1.dwg"; - using var tr = new DBTrans(); - using var tr1 = new DBTrans(filename); + using DBTrans tr = new(); + using DBTrans tr1 = new(filename); // tr.BlockTable.GetBlockFrom(filename, true); string blkdefname = SymbolUtilityServices.RepairSymbolName(SymbolUtilityServices.GetSymbolNameFromPathName(filename, "dwg"), false); tr.Database.Insert(blkdefname, tr1.Database, false); // 插入了块定义,未插入块参照 diff --git a/tests/TestShared/testeditor.cs b/tests/TestShared/testeditor.cs index b2a6c76..1fb0890 100644 --- a/tests/TestShared/testeditor.cs +++ b/tests/TestShared/testeditor.cs @@ -29,7 +29,7 @@ public void Tested() [CommandMethod("testzoom")] public void Testzoom() { - using var tr = new DBTrans(); + using DBTrans tr = new(); var res = Env.Editor.GetEntity("\npick ent:"); if (res.Status == PromptStatus.OK) Env.Editor.ZoomObject(res.ObjectId.GetObject()!); @@ -37,7 +37,7 @@ public void Testzoom() [CommandMethod("testzoomextent")] public void Testzoomextent() { - // using var tr = new DBTrans(); + // using DBTrans tr = new(); // var res = Env.Editor.GetEntity("\npick ent:"); // if (res.Status == PromptStatus.OK) // { -- Gitee From 7213fa8b79f173ee94dbfaa35029d82ca4b2bbdd Mon Sep 17 00:00:00 2001 From: liuqihong <540762622@qq.com> Date: Wed, 5 Oct 2022 22:41:53 +0800 Subject: [PATCH 549/675] =?UTF-8?q?=E5=89=AA=E8=B4=B4=E6=9D=BF=E9=AB=98?= =?UTF-8?q?=E7=89=88=E6=9C=AC=E5=AD=98=E5=9C=A8=E9=97=AE=E9=A2=98=20?= =?UTF-8?q?=E4=BB=BB=E5=8A=A1=2020221007?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/IFoxCAD.Basal/General/EnumEx.cs | 20 +- src/IFoxCAD.Cad.Shared/Copyclip/EmfTool.cs | 626 +++++++++++++++--- .../Copyclip/TagClipboardInfo.cs | 5 + src/IFoxCAD.Cad.Shared/Copyclip/WindowsAPI.cs | 23 +- .../ExtensionMethod/EditorEx.cs | 12 +- .../IFoxCAD.Aacad08/IFoxCAD.Acad08.csproj | 9 +- .../IFoxCAD.Acad09plus.csproj | 27 +- .../IFoxCAD.Gcad/IFoxCAD.Gcad.csproj | 5 + .../IFoxCAD.Zcad/IFoxCAD.Zcad.csproj | 5 + tests/TestAcad08/TestAcad08.csproj | 9 +- tests/TestAcad09plus/TestAcad09plus.csproj | 21 +- tests/TestShared/Copyclip.cs | 280 ++++++-- tests/TestShared/testeditor.cs | 6 +- 13 files changed, 856 insertions(+), 192 deletions(-) diff --git a/src/IFoxCAD.Basal/General/EnumEx.cs b/src/IFoxCAD.Basal/General/EnumEx.cs index 79f01bc..50ab654 100644 --- a/src/IFoxCAD.Basal/General/EnumEx.cs +++ b/src/IFoxCAD.Basal/General/EnumEx.cs @@ -89,17 +89,25 @@ public static void CleanCache() return null; } + // 不按位运算的情况下,直接获取比较快捷 + public static string GetDesc(this Enum e) + { + return GetDesc(e.GetType(), e.ToString()); + } - // TODO: 山人审核代码之后可以删除,这个完全被上面替代了 - public static string GetDesc(this Enum val) + /// + /// 获取字段的描述内容 + /// + /// + /// + /// + public static string GetDesc(this Type type, string field) { - var type = val.GetType(); - var memberInfo = type.GetMember(val.ToString()); + var memberInfo = type.GetMember(field); var attributes = memberInfo[0].GetCustomAttributes(typeof(DescriptionAttribute), false); // 如果没有定义描述,就把当前枚举值的对应名称返回 if (attributes is null || attributes.Length != 1) - return val.ToString(); - + return field; return ((DescriptionAttribute)attributes.Single()).Description; } } \ No newline at end of file diff --git a/src/IFoxCAD.Cad.Shared/Copyclip/EmfTool.cs b/src/IFoxCAD.Cad.Shared/Copyclip/EmfTool.cs index a70542c..12e2df1 100644 --- a/src/IFoxCAD.Cad.Shared/Copyclip/EmfTool.cs +++ b/src/IFoxCAD.Cad.Shared/Copyclip/EmfTool.cs @@ -8,6 +8,13 @@ using System.Windows; using System.Security.Policy; using System.Security.Cryptography; +using System.Windows.Interop; +using System.Text; +using static System.Windows.Forms.VisualStyles.VisualStyleElement.TextBox; +using System.Xml; +using Point = System.Drawing.Point; +using Size = System.Drawing.Size; + // DWORD == uint // WORD == ushort @@ -19,6 +26,37 @@ * Console.WriteLine(Marshal.SizeOf(typeof(StandardMetaRecord))); */ + +// https://learn.microsoft.com/en-us/windows/win32/api/wingdi/ns-wingdi-metafilepict +// http://www.cppblog.com/zwp/archive/2012/02/25/60225.html +[Serializable] +[StructLayout(LayoutKind.Sequential, Pack = 2)] +public struct MetaFilePict +{ + public MappingModes mm; + public int xExt; + public int yExt; + public IntPtr hMF; //内存图元文件的句柄 +} + +public enum MappingModes : int +{ + MM_TEXT = 1, + MM_LOMETRIC = 2, + MM_HIMETRIC = 3, + MM_LOENGLISH = 4,//逻辑坐标的单位为0.01英寸 + MM_HIENGLISH = 5, + MM_TWIPS = 6, + MM_ISOTROPIC = 7, + MM_ANISOTROPIC = 8, + + //Minimum and Maximum Mapping Mode values + MM_MIN = MM_TEXT, + MM_MAX = MM_ANISOTROPIC, + MM_MAX_FIXEDSCALE = MM_TWIPS, +} + + //WMF 文件格式: //https://blog.51cto.com/chenyanxi/803247 //文件缩放信息:22字节 @@ -50,7 +88,10 @@ public struct PlaceableMetaHeader /// wmf转为emf
    ///
    /// 文件路径 - /// 成功emf指针,可以直接写入剪贴板;失败0 + /// + /// 错误: ;
    + /// 成功: 返回一个增强型图元 emf文件句柄 (位于内存中) + ///
    /// public static IntPtr Wmf2Emf(string wmfFile) { @@ -95,6 +136,29 @@ public static IntPtr Wmf2Emf(string wmfFile) } } +public class Emf +{ + public IntPtr EmfHandle; + + /// + /// 转换wmf到emf + /// + /// + public void Wmf2Emf(string wmfFile) + { + EmfHandle = PlaceableMetaHeader.Wmf2Emf(wmfFile);//emf文件句柄 + } + + /// + /// 获取emf结构 + /// + /// + public EnhMetaHeader? CreateEnhMetaHeader() + { + return EnhMetaHeader.Create(EmfHandle); + } +} + //紧接文件缩放信息的是 WMFHEAD, 18字节 [Serializable] [StructLayout(LayoutKind.Sequential, Pack = 2)] @@ -119,110 +183,460 @@ public struct StandardMetaRecord public ushort[] Parameters;  /* Parameter values passed to function */ } - -// https://learn.microsoft.com/en-us/windows/win32/api/wingdi/ns-wingdi-metafilepict -// http://www.cppblog.com/zwp/archive/2012/02/25/60225.html -[Serializable] -[StructLayout(LayoutKind.Sequential, Pack = 2)] -public struct MetaFilePict -{ - public MappingModes mm; - public int xExt; - public int yExt; - public IntPtr hMF; //内存图元文件的句柄 -} - -public enum MappingModes -{ - MM_TEXT = 1, - MM_LOMETRIC = 2, - MM_HIMETRIC = 3, - MM_LOENGLISH = 4, - MM_HIENGLISH = 5, - MM_TWIPS = 6, - MM_ISOTROPIC = 7, - MM_ANISOTROPIC = 8, -} - - +// 文件结构:头记录(ENHMETAHEADER),各记录(ENHMETARECORD),文件结尾(EMR_EOF) +// https://www.cnblogs.com/5iedu/p/4706327.html [Serializable] [StructLayout(LayoutKind.Sequential, Pack = 2)] public struct EnhMetaHeader { + [Description("记录类型")] public uint iType; - public int nSize; + [Description("结构大小")] + public int nSize; //注意这个大小是含描述字符串的长度,即等于sizeof(ENHMETAHEADER)+nDescription*2 + [Description("外接矩形(单位是像素)")] public IntRect rclBounds; + [Description("图片矩形(单位是 0.1 毫米)")] public IntRect rclFrame; + [Description("文件签名")] public uint dSignature; + [Description("文件版本")] public uint nVersion; + [Description("文件尺寸")] public uint nBytes; + [Description("记录数")] public uint nRecords; + [Description("句柄数")] public ushort nHandles; + [Description("保留")] public ushort sReserved; + [Description("说明文本的长度")] public uint nDescription; + [Description("说明文本的偏移量")] public uint offDescription; + [Description("调色板的元素数")] public uint nPalEntries; + [Description("分辨率(像素)")] public IntSize szlDevice; + [Description("分辨率(毫米)")] public IntSize szlMillimeters; + [Description("像素格式的尺寸")] public uint cbPixelFormat; + [Description("像素格式的起始偏移位置")] public uint offPixelFormat; + [Description("在不含OpenGL记录时,该值为FALSE")] public uint bOpenGL; + [Description("参考设备的尺寸(微米)")] public IntSize szlMicrometers; public override string ToString() { - return - nameof(iType) + ":" + iType.ToString() + Environment.NewLine + - nameof(nSize) + ":" + nSize.ToString() + Environment.NewLine + - nameof(rclBounds) + ":" + rclBounds.ToString() + Environment.NewLine + - nameof(rclFrame) + ":" + rclFrame.ToString() + Environment.NewLine + - nameof(dSignature) + ":" + dSignature.ToString() + Environment.NewLine + - nameof(nVersion) + ":" + nVersion.ToString() + Environment.NewLine + - nameof(nBytes) + ":" + nBytes.ToString() + Environment.NewLine + - nameof(nRecords) + ":" + nRecords.ToString() + Environment.NewLine + - nameof(nHandles) + ":" + nHandles.ToString() + Environment.NewLine + - nameof(sReserved) + ":" + sReserved.ToString() + Environment.NewLine + - nameof(nDescription) + ":" + nDescription.ToString() + Environment.NewLine + - nameof(offDescription) + ":" + offDescription.ToString() + Environment.NewLine + - nameof(nPalEntries) + ":" + nPalEntries.ToString() + Environment.NewLine + - nameof(szlDevice) + ":" + szlDevice.ToString() + Environment.NewLine + - nameof(szlMillimeters) + ":" + szlMillimeters.ToString() + Environment.NewLine + - nameof(cbPixelFormat) + ":" + cbPixelFormat.ToString() + Environment.NewLine + - nameof(offPixelFormat) + ":" + offPixelFormat.ToString() + Environment.NewLine + - nameof(bOpenGL) + ":" + bOpenGL.ToString() + Environment.NewLine + - nameof(szlMicrometers) + ":" + szlMicrometers; + //var tp = GetType(); + //var sb = new StringBuilder(); + //sb.AppendLine(EnumEx.GetDesc(tp, nameof(iType)) + ":" + iType); + + // 输出json + // NET472 System.Text.Json + var serializer = new System.Web.Script.Serialization.JavaScriptSerializer(); + string jsonString = serializer.Serialize(this); + return jsonString; + } + + /// + /// 通过wmf创建 + /// + /// + /// + public static EnhMetaHeader? Create(string wmf) + { + var emf = PlaceableMetaHeader.Wmf2Emf(wmf); + if (emf == IntPtr.Zero) + throw new ArgumentException(null, nameof(wmf)); + return Create(emf); + } + + /// + /// 创建 + /// + /// + /// 参数1的结构体首地址
    + /// 也就是的返回值 + /// + /// + public static EnhMetaHeader? Create(IntPtr clipTypeData) + { + if (clipTypeData == IntPtr.Zero) + return null; + + EnhMetaHeader? result = null; + var len = EmfTool.GetEnhMetaFileHeader(clipTypeData, 0, IntPtr.Zero); + if (len != 0) + { + IntPtr header = Marshal.AllocHGlobal((int)len); + EmfTool.GetEnhMetaFileHeader(clipTypeData, len, header); + result = (EnhMetaHeader)Marshal.PtrToStructure(header, typeof(EnhMetaHeader)); + Marshal.FreeHGlobal(header); + } + return result; } } + + public static class EmfTool { + /// + /// 保存 + /// + /// GetEnhMetaFileBits 参数1的结构体首地址 + /// 保存路径 + /// + public static void Save(IntPtr clipTypeData, string file) + { + // 保存emf文件 + // https://blog.csdn.net/tigertianx/article/details/7098490 + var len = EmfTool.GetEnhMetaFileBits(clipTypeData, 0, null!); + if (len != 0) + { + var bytes = new byte[len]; + _ = EmfTool.GetEnhMetaFileBits(clipTypeData, len, bytes); + + using MemoryStream ms1 = new(bytes); + using var bm = Image.FromStream(ms1);//此方法emf保存成任何版本都会变成png + bm.Save(file); + } + } + /// /// 返回对一个增强型图元文件的说明 /// /// 目标增强型图元文件的句柄 /// lpszDescription缓冲区的长度 - /// 指定一个预先初始化好的字串缓冲区,准备随同图元文件说明载入。 - /// 参考CreateEnhMetaFile函数,了解增强型图元文件说明字串的具体格式 + /// 指定一个预先初始化好的字串缓冲区,准备随同图元文件说明载入; + /// 参考 CreateEnhMetaFile 函数,了解增强型图元文件说明字串的具体格式 /// [DllImport("gdi32.dll")] - public static extern uint GetEnhMetaFileDescription( - IntPtr hemf, - uint cchBuffer, - [MarshalAs(UnmanagedType.LPWStr)] - //[In,Out] - StringBuilder lpDescription - ); + static extern uint GetEnhMetaFileDescription(IntPtr hemf, uint cchBuffer, [MarshalAs(UnmanagedType.LPStr)] StringBuilder lpDescription); + /// + /// 获取emf描述 + /// + /// 文件句柄 + /// 描述的内容 [System.Diagnostics.DebuggerStepThrough()] - [System.CodeDom.Compiler.GeneratedCode("InteropSignatureToolkit", "0.9 Beta1")] - public static uint GetEnhMetaFileDescriptionString(IntPtr hemf, out string lpDescription) + //[System.CodeDom.Compiler.GeneratedCode("InteropSignatureToolkit", "0.9 Beta1")] + public static string? GetEnhMetaFileDescriptionEx(IntPtr clipTypeData) + { + var len = GetEnhMetaFileDescription(clipTypeData, 0, null!); + if (len != 0) + { + StringBuilder desc = new((int)len); + GetEnhMetaFileDescription(clipTypeData, (uint)desc.Capacity, desc); + return desc.ToString(); + } + return null; + } + + public enum DeviceCap : int { - StringBuilder sb = new(1024); - uint methodRetVar = GetEnhMetaFileDescription(hemf, (uint)sb.Capacity, sb); - lpDescription = sb.ToString(); - return methodRetVar; + /// + /// Device driver version + /// + DRIVERVERSION = 0, + /// + /// Device classification + /// + TECHNOLOGY = 2, + /// + /// Horizontal size in millimeters + /// + HORZSIZE = 4, + /// + /// Vertical size in millimeters + /// + VERTSIZE = 6, + /// + /// Horizontal width in pixels + /// + HORZRES = 8, + /// + /// Vertical height in pixels + /// + VERTRES = 10, + /// + /// Number of bits per pixel + /// + BITSPIXEL = 12, + /// + /// Number of planes + /// + PLANES = 14, + /// + /// Number of brushes the device has + /// + NUMBRUSHES = 16, + /// + /// Number of pens the device has + /// + NUMPENS = 18, + /// + /// Number of markers the device has + /// + NUMMARKERS = 20, + /// + /// Number of fonts the device has + /// + NUMFONTS = 22, + /// + /// Number of colors the device supports + /// + NUMCOLORS = 24, + /// + /// Size required for device descriptor + /// + PDEVICESIZE = 26, + /// + /// Curve capabilities + /// + CURVECAPS = 28, + /// + /// Line capabilities + /// + LINECAPS = 30, + /// + /// Polygonal capabilities + /// + POLYGONALCAPS = 32, + /// + /// Text capabilities + /// + TEXTCAPS = 34, + /// + /// Clipping capabilities + /// + CLIPCAPS = 36, + /// + /// Bitblt capabilities + /// + RASTERCAPS = 38, + /// + /// Length of the X leg + /// + ASPECTX = 40, + /// + /// Length of the Y leg + /// + ASPECTY = 42, + /// + /// Length of the hypotenuse + /// + ASPECTXY = 44, + /// + /// Shading and Blending caps + /// + SHADEBLENDCAPS = 45, + + /// + /// Logical pixels inch in X + /// + LOGPIXELSX = 88, + /// + /// Logical pixels inch in Y + /// + LOGPIXELSY = 90, + + /// + /// Number of entries in physical palette + /// + SIZEPALETTE = 104, + /// + /// Number of reserved entries in palette + /// + NUMRESERVED = 106, + /// + /// Actual color resolution + /// + COLORRES = 108, + + // Printing related DeviceCaps. These replace the appropriate Escapes + /// + /// Physical Width in device units + /// + PHYSICALWIDTH = 110, + /// + /// Physical Height in device units + /// + PHYSICALHEIGHT = 111, + /// + /// Physical Printable Area x margin + /// + PHYSICALOFFSETX = 112, + /// + /// Physical Printable Area y margin + /// + PHYSICALOFFSETY = 113, + /// + /// Scaling factor x + /// + SCALINGFACTORX = 114, + /// + /// Scaling factor y + /// + SCALINGFACTORY = 115, + + /// + /// Current vertical refresh rate of the display device (for displays only) in Hz + /// + VREFRESH = 116, + /// + /// Vertical height of entire desktop in pixels + /// + DESKTOPVERTRES = 117, + /// + /// Horizontal width of entire desktop in pixels + /// + DESKTOPHORZRES = 118, + /// + /// Preferred blt alignment + /// + BLTALIGNMENT = 119 } + [DllImport("gdi32.dll")] + static extern int GetDeviceCaps(IntPtr hDC, DeviceCap nIndex); + + [DllImport("gdi32.dll")] + static extern int SetMapMode(IntPtr hDC, MappingModes fnMapMode); + + [DllImport("gdi32.dll")] + static extern bool SetViewportOrgEx(IntPtr hDC, int x, int y, Point[] prevPoint); + + [DllImport("gdi32.dll")] + static extern bool SetWindowOrgEx(IntPtr hDC, int x, int y, Point[] prevPoint); + + [DllImport("gdi32.dll")] + static extern bool SetViewportExtEx(IntPtr hDC, int nExtentX, int nExtentY, Size[] prevSize); + + [DllImport("gdi32.dll")] + static extern bool SetWindowExtEx(IntPtr hDC, int nExtentX, int nExtentY, Size[] prevSize); + + [DllImport("Gdi32.dll")] + public static extern int CreatePen(int nPenStyle, int nWidth, int nColor); + + [DllImport("Gdi32.dll")] + public static extern int GetStockObject(int nStockBrush); + + [DllImport("Gdi32.dll")] + public static extern int SelectObject(IntPtr hDC, int hGdiObject); + + [DllImport("Gdi32.dll")] + public static extern int DeleteObject(int hBitmap); + + [DllImport("Gdi32.dll")] + public static extern int MoveToEx(IntPtr hDC, int x, int y, int nPreviousPoint); + + [DllImport("Gdi32.dll")] + public static extern int LineTo(IntPtr hDC, int x, int y); + + [DllImport("Gdi32.dll")] + public static extern int Rectangle(IntPtr hDC, int nLeft, int nTop, int nRight, int nBottom); + + [DllImport("Gdi32.dll")] + public static extern bool DPtoLP(IntPtr hdc, [In, Out] Point[] lpPoints, int nCount); + + + /// + /// 设置emf描述 + /// + /// emf文件句柄 + /// 设置描述 + /// 新的emf指针 + /// + public static void SetEnhMetaFileDescriptionEx(ref IntPtr hMetaFile, string desc) + { + if (hMetaFile == IntPtr.Zero) + throw new ArgumentNullException(nameof(hMetaFile)); + + var em = EnhMetaHeader.Create(hMetaFile);//emf结构 GetEnhMetaFileHeader + if (em == null) + return; + + var emh = em.Value; + // 创建画布句柄 + IntRect intRect = emh.rclFrame; //new(0, 0, 0, 0); + var hMetaDC = EmfTool.CreateEnhMetaFile(IntPtr.Zero, null!, ref intRect, desc); + if (hMetaDC == IntPtr.Zero) + return; + //SetMapMode(hMetaDC, MappingModes.MM_ANISOTROPIC); // 默认的就是这个模式 + //SetMapMode(hMetaDC, MappingModes.MM_HIMETRIC);//逻辑单位:0.01mm + + // 设置单位 + //var size = new IntSize(0, 0); + //EmfTool.SetWindowExtEx(hMetaDC, 0, 0, ref size); + //EmfTool.SetViewportExtEx(hMetaDC, 0, 0, ref size); + //EmfTool.GetEnhMetaFilePaletteEntries() 统一调色 + //SetViewportOrgEx(hMetaDC, 0, 0, null!);//将视口原点设在左下角 + + // 旧的克隆到新的 + /* + * 第18章 图元文件_18.2 增强型图元文件(emf)(2) + * https://blog.51cto.com/u_15082403/3724715 + * 方案2——利用图像的物理尺寸 + * 通过rclFrame字段(是设备单位:0.01mm)显示出来的刻度尺,这样不管在视频显示器 + * 打印机上,显示出来的刻度尺都较为真实 + */ + //目标设备信息 + int cxMms = GetDeviceCaps(hMetaDC, DeviceCap.HORZSIZE);//宽度(单位:mm) + int cyMms = GetDeviceCaps(hMetaDC, DeviceCap.VERTSIZE);//高度(单位:mm) + var cxArea = cxMms; + var cyArea = cyMms; +#if true2 + int cxPix = GetDeviceCaps(hMetaDC, DeviceCap.HORZRES);//宽度(单位:像素) + int cyPix = GetDeviceCaps(hMetaDC, DeviceCap.VERTRES);//高度(单位:像素) + int cxImage = emh.rclFrame.Right - emh.rclFrame.Left; //单位:0.01mm + int cyImage = emh.rclFrame.Bottom - emh.rclFrame.Top; + + // 设置之后图像就没有拉伸了,但是跑偏了 + //将图元文件大小(0.01mm为单位)转换为像素大小 + cxImage = cxImage * cxPix / cxMms / 100; + cyImage = cyImage * cyPix / cyMms / 100; + + //在指定的矩形区内,水平和垂直居中显示图元文件,同时保证了区域的大小为cxImage和cyImage + int left = (cxArea - cxImage) / 2; + int right = (cxArea + cxImage) / 2; + int top = (cyArea - cyImage) / 2; + int bottom = (cyArea + cyImage) / 2; +#else + cxArea = 0; + cyArea = 0; + SetMapMode(hMetaDC, MappingModes.MM_HIMETRIC);//逻辑单位:0.01mm + SetViewportOrgEx(hMetaDC, 0, cyArea, null!);//将视口原点设在左下角 + var pt = new Point(cxArea, 0); + + int cxImage = emh.rclFrame.Right - emh.rclFrame.Left; //单位:0.01mm + int cyImage = emh.rclFrame.Bottom - emh.rclFrame.Top; + + //在指定的矩形区内,水平和垂直居中显示图元文件,同时保证了区域的大小为cxImage和cyImage + int left = (pt.X - cxImage) / 2; + int right = (pt.X + cxImage) / 2; + int top = (pt.Y + cyImage) / 2; //注意,这里与前面例子不同 + int bottom = (pt.Y - cyImage) / 2; //注意,这里与前面例子不同 +#endif + var rect = new IntRect(left, top, right, bottom); + + // 图像拉伸了 + //bool pef = EmfTool.EnumEnhMetaFile(hMetaDC, hMetaFile, IntPtr.Zero, IntPtr.Zero, ref emhValue.rclFrame);// 这个失败 + bool pef = EmfTool.PlayEnhMetaFile(hMetaDC, hMetaFile, ref rect); + if (!pef) + { + DeleteObject(hMetaDC); + Debugger.Break(); + return; + } + // 删除旧的图元文件句柄,返回新的 + var del = EmfTool.DeleteEnhMetaFile(hMetaFile); + if (del) + hMetaFile = EmfTool.CloseEnhMetaFile(hMetaDC); + } [DllImport("gdi32.dll", EntryPoint = "GetEnhMetaFileHeader")] public static extern uint GetEnhMetaFileHeader(IntPtr hemf, uint cbBuffer, IntPtr /*ENHMETAHEADER*/ lpemh); @@ -276,16 +690,82 @@ public static uint GetEnhMetaFileDescriptionString(IntPtr hemf, out string lpDes public static extern bool DeleteEnhMetaFile(IntPtr hemf); /// - /// 创建矢量图 + /// 创建emf
    /// https://www.cnblogs.com/5iedu/p/4706327.html ///
    - /// 参考设备环境,NULL时表示以屏幕为参考 - /// 指定文件名时,创建磁盘文件(.EMF).为NULL时创建内存图元文件 - /// 用于描述图元文件的大小和位置(以0.01mm为单位),可用它精确定义图元文件的物理尺寸 + /// 参考设备环境,null以整个屏幕为参考 + /// 指定文件名时,创建磁盘文件(.EMF),为null时创建内存图元文件 + /// 用于描述图元文件的大小和位置(以0.01mm为单位),可用它精确定义图元文件的物理尺寸 /// 对图元文件的一段说明.包括创建应用程序的名字、一个NULL字符、对图元文件的一段说明以及两个NULL字符. - /// 增强型图元文件DC(注意不是图元文件的句柄,要获得实际的图元文件句柄,得调用 CloseEnhMetaFile 函数) + /// 返回画布句柄DC(图元文件句柄得调用 CloseEnhMetaFile 函数) + [DllImport("gdi32.dll", SetLastError = true)] + public static extern IntPtr CreateEnhMetaFile(IntPtr hdcRef, string szFilename, ref IntRect lpRect, string lpDescription); + + [DllImport("gdi32.dll", SetLastError = true)] + public static extern bool DeleteObject(IntPtr hdcRef); + + /// + /// 在指定的设备场景中画一个增强型图元文件;
    + /// 与标准图元文件不同,完成回放后,增强型图元文件会恢复设备场景以前的状态 + ///
    + /// 画布句柄 + /// 欲描绘的emf的图元文件句柄 + /// 指定显示区域(逻辑单位)GDI会缩放图像以适应该矩形范围 + /// + [DllImport("gdi32.dll")] + public static extern bool PlayEnhMetaFile(IntPtr hdcRef, IntPtr hemf, ref IntRect lpRect); + + // https://blog.csdn.net/hongke457546235/article/details/17404715 + /// + /// 逻辑单位设置窗口单位 + /// {只能在 MM_ISOTROPIC 或 MM_ANISOTROPIC 模式下使用下面两个函数} + /// + /// 画布句柄 + /// 以逻辑单位表示的新窗口区域的高度 + /// 以逻辑单位表示的新窗口区域的宽度 + /// 保存函数调用前窗口区域尺寸的SIZE结构地址,NULL则表示忽略调用前的尺寸 + [DllImport("gdi32.dll")] + public static extern bool SetWindowExtEx(IntPtr hdcRef, int nHeight, int nWidth, ref IntSize lpSize); + + /// + /// 视口区域的定义 + /// {只能在 MM_ISOTROPIC 或 MM_ANISOTROPIC 模式下使用下面两个函数} + /// + /// + /// + /// + /// + [DllImport("gdi32.dll")] + public static extern bool SetViewportExtEx(IntPtr hdcRef, int nHeight, int nWidth, ref IntSize lpSize); + + /// + /// 旧emf绘制新的hdcEMF中(即回放) + /// + /// 画布句柄 + /// 图元文件句柄 + /// 回调函数 + /// 传给回调函数的额外参数 + /// 在指定的矩形区内显示图元文件 + /// [DllImport("gdi32.dll", SetLastError = true)] - public static extern IntPtr CreateEnhMetaFile(IntPtr hdcRef, string szFilename, IntRect lpRect, string lpDescription); + public static extern bool EnumEnhMetaFile(IntPtr hdcRef, IntPtr hmf, IntPtr proc, IntPtr procParam, ref IntRect lpRect); + + /// + /// 返回图元文件句柄 + /// + /// 画布句柄 + [DllImport("gdi32.dll", SetLastError = true)] + public static extern IntPtr CloseEnhMetaFile(IntPtr hdcRef); + + // https://zhidao.baidu.com/question/646739770512964165/answer/1616737219.html?qq-pf-to=pcqq.c2c + //16位的函数 + [DllImport("gdi32.dll")] + public static extern IntPtr GetMetaFile(string path); + //32位的函数 + [DllImport("gdi32.dll")] + public static extern IntPtr GetEnhMetaFile(string path); + + /// /// EMF保存到文件或者路径 @@ -361,14 +841,6 @@ public static void ToMetafile(byte[] data, Func task) #if false - // https://zhidao.baidu.com/question/646739770512964165/answer/1616737219.html?qq-pf-to=pcqq.c2c - //16位的函数 - [DllImport("gdi32.dll")] - public static extern uint GetMetaFile(StringBuilder path); - //32位的函数 - [DllImport("gdi32.dll")] - public static extern uint GetEnhMetaFile(StringBuilder path); - /// /// c#获取wmf方式 /// diff --git a/src/IFoxCAD.Cad.Shared/Copyclip/TagClipboardInfo.cs b/src/IFoxCAD.Cad.Shared/Copyclip/TagClipboardInfo.cs index c6dee45..6f73ca3 100644 --- a/src/IFoxCAD.Cad.Shared/Copyclip/TagClipboardInfo.cs +++ b/src/IFoxCAD.Cad.Shared/Copyclip/TagClipboardInfo.cs @@ -258,6 +258,11 @@ public override int GetHashCode() { return ((_Left, _Top).GetHashCode(), _Right).GetHashCode() ^ _Bottom.GetHashCode(); } + + public IntRect Clone() + { + return new IntRect(_Left, _Top, _Right, _Bottom); + } #endregion } diff --git a/src/IFoxCAD.Cad.Shared/Copyclip/WindowsAPI.cs b/src/IFoxCAD.Cad.Shared/Copyclip/WindowsAPI.cs index 04694a6..b9826c1 100644 --- a/src/IFoxCAD.Cad.Shared/Copyclip/WindowsAPI.cs +++ b/src/IFoxCAD.Cad.Shared/Copyclip/WindowsAPI.cs @@ -170,21 +170,22 @@ public static void StructToPtr(object? structObj, if (structObj == null) throw new ArgumentNullException(nameof(structObj)); #if Marshal - IntPtr structPtr = Marshal.AllocHGlobal(Marshal.SizeOf(structObj)); + IntPtr newPtr = Marshal.AllocHGlobal(Marshal.SizeOf(structObj)); #else const int GMEM_MOVEABLE = 0x0002; - IntPtr structPtr = WindowsAPI.GlobalAlloc(GMEM_MOVEABLE, Marshal.SizeOf(structObj)); + IntPtr newPtr = WindowsAPI.GlobalAlloc(GMEM_MOVEABLE, Marshal.SizeOf(structObj)); #endif - if (structPtr == IntPtr.Zero) + if (newPtr == IntPtr.Zero) return; try { + // 剪贴板写入的时候不允许锁定内存,否则在频繁触发剪贴板将导致卡死程序 if (lockPrt) - GlobalLockTask(structPtr, ptr => { + GlobalLockTask(newPtr, ptr => { ToPtr(structObj, task, ptr); }); else - ToPtr(structObj, task, structPtr); + ToPtr(structObj, task, newPtr); } catch (Exception e) { @@ -193,21 +194,21 @@ public static void StructToPtr(object? structObj, } finally { - if (freeHGlobal && structPtr != IntPtr.Zero) + if (freeHGlobal && newPtr != IntPtr.Zero) { #if Marshal - Marshal.FreeHGlobal(structPtr); + Marshal.FreeHGlobal(newPtr); #else - WindowsAPI.GlobalFree(structPtr); + WindowsAPI.GlobalFree(newPtr); #endif } } // 将结构体拷到分配好的内存空间 - static void ToPtr(object? structObj, Action? task, IntPtr structPtr) + static void ToPtr(object? structObj, Action? task, IntPtr newPtr) { - Marshal.StructureToPtr(structObj, structPtr, true); - task?.Invoke(structPtr); + Marshal.StructureToPtr(structObj, newPtr, true); + task?.Invoke(newPtr); } } } diff --git a/src/IFoxCAD.Cad.Shared/ExtensionMethod/EditorEx.cs b/src/IFoxCAD.Cad.Shared/ExtensionMethod/EditorEx.cs index 26b3f87..c669610 100644 --- a/src/IFoxCAD.Cad.Shared/ExtensionMethod/EditorEx.cs +++ b/src/IFoxCAD.Cad.Shared/ExtensionMethod/EditorEx.cs @@ -1097,8 +1097,8 @@ public enum RunLispFlag : byte /// 选择集的对象,为null时候手选 /// 是否清空选择集 /// - public static void ExportWMF(this Editor editor, string saveFile, - ObjectId[]? ids = null, bool wmfSetDel = false) + public static void ComExportWMF(this Editor editor, string saveFile, + ObjectId[]? ids = null, bool wmfSetDel = false) { if (string.IsNullOrEmpty(saveFile)) throw new ArgumentNullException(nameof(saveFile)); @@ -1129,7 +1129,10 @@ public static void ExportWMF(this Editor editor, string saveFile, psr = editor.GetSelection();// 手选 if (psr.Status != PromptStatus.OK) return; + ids = psr.Value.GetObjectIds(); } + editor.SetImpliedSelection(ids); + #if zcad var com = Acap.ZcadApplication; #else @@ -1137,7 +1140,10 @@ public static void ExportWMF(this Editor editor, string saveFile, #endif var doc = com.GetProperty("ActiveDocument"); var wmfSet = doc.GetProperty("ActiveSelectionSet"); - doc.Invoke("Export", saveFile, "wmf", wmfSet); // JPGOUT,PNGOUT + + // TODO 20221007 cad21 先net选择,再进行,此处再选择一次? + // cad21 调试期间无法选择性粘贴? + var exp = doc.Invoke("Export", saveFile, "wmf", wmfSet); // JPGOUT,PNGOUT if (wmfSetDel) wmfSet.Invoke("Delete"); } diff --git a/src/IFoxCAD.Cad/IFoxCAD.Aacad08/IFoxCAD.Acad08.csproj b/src/IFoxCAD.Cad/IFoxCAD.Aacad08/IFoxCAD.Acad08.csproj index 1382cbc..5249ea4 100644 --- a/src/IFoxCAD.Cad/IFoxCAD.Aacad08/IFoxCAD.Acad08.csproj +++ b/src/IFoxCAD.Cad/IFoxCAD.Aacad08/IFoxCAD.Acad08.csproj @@ -58,8 +58,9 @@ - + + + + + diff --git a/src/IFoxCAD.Cad/IFoxCAD.Aacad09plus/IFoxCAD.Acad09plus.csproj b/src/IFoxCAD.Cad/IFoxCAD.Aacad09plus/IFoxCAD.Acad09plus.csproj index d49ff11..39ded5e 100644 --- a/src/IFoxCAD.Cad/IFoxCAD.Aacad09plus/IFoxCAD.Acad09plus.csproj +++ b/src/IFoxCAD.Cad/IFoxCAD.Aacad09plus/IFoxCAD.Acad09plus.csproj @@ -26,16 +26,16 @@ - - + + - + - - + + DEBUG @@ -57,16 +57,17 @@ - + - - - + + + + + + + \ No newline at end of file diff --git a/src/IFoxCAD.Cad/IFoxCAD.Gcad/IFoxCAD.Gcad.csproj b/src/IFoxCAD.Cad/IFoxCAD.Gcad/IFoxCAD.Gcad.csproj index a55e3a6..c664c2c 100644 --- a/src/IFoxCAD.Cad/IFoxCAD.Gcad/IFoxCAD.Gcad.csproj +++ b/src/IFoxCAD.Cad/IFoxCAD.Gcad/IFoxCAD.Gcad.csproj @@ -55,4 +55,9 @@ + + + + + diff --git a/src/IFoxCAD.Cad/IFoxCAD.Zcad/IFoxCAD.Zcad.csproj b/src/IFoxCAD.Cad/IFoxCAD.Zcad/IFoxCAD.Zcad.csproj index c97b9f6..14c41a7 100644 --- a/src/IFoxCAD.Cad/IFoxCAD.Zcad/IFoxCAD.Zcad.csproj +++ b/src/IFoxCAD.Cad/IFoxCAD.Zcad/IFoxCAD.Zcad.csproj @@ -65,4 +65,9 @@ + + + + + diff --git a/tests/TestAcad08/TestAcad08.csproj b/tests/TestAcad08/TestAcad08.csproj index 6e84144..aeeeab7 100644 --- a/tests/TestAcad08/TestAcad08.csproj +++ b/tests/TestAcad08/TestAcad08.csproj @@ -23,8 +23,9 @@ - + + + + + diff --git a/tests/TestAcad09plus/TestAcad09plus.csproj b/tests/TestAcad09plus/TestAcad09plus.csproj index f116e65..f623391 100644 --- a/tests/TestAcad09plus/TestAcad09plus.csproj +++ b/tests/TestAcad09plus/TestAcad09plus.csproj @@ -14,11 +14,11 @@ - - - + + + - + TestView.xaml @@ -31,9 +31,10 @@ - - - + + + + + + + \ No newline at end of file diff --git a/tests/TestShared/Copyclip.cs b/tests/TestShared/Copyclip.cs index e6ddadb..270b1cc 100644 --- a/tests/TestShared/Copyclip.cs +++ b/tests/TestShared/Copyclip.cs @@ -8,8 +8,12 @@ namespace Test; using System.Diagnostics; using System.Drawing; using System.Drawing.Imaging; +using System.Runtime.Serialization.Formatters.Binary; using System.Threading; using System.Windows; +using System.Windows.Controls; +using System.Windows.Documents; +using System.Windows.Interop; using System.Windows.Media; using Image = System.Drawing.Image; @@ -199,16 +203,18 @@ void Copy(bool getPoint, bool isEraseSsget = false) var doc = dm.MdiActiveDocument; if (doc.Editor == null) return; + ObjectId[] idArray = null!; +#if true var psr = doc.Editor.SelectImplied();// 预选 if (psr.Status != PromptStatus.OK) psr = doc.Editor.GetSelection();// 手选 if (psr.Status != PromptStatus.OK) return; + idArray = psr.Value.GetObjectIds(); +#endif // 设置基点 Point3d pt = Point3d.Origin; - var idArray = psr.Value.GetObjectIds(); - var tempFile = CreateTempFileName(); while (File.Exists(tempFile) || File.Exists(Path.ChangeExtension(tempFile, "wmf"))) @@ -219,6 +225,40 @@ void Copy(bool getPoint, bool isEraseSsget = false) using DBTrans tr = new(); + + #region 写入 WMF 数据 + /* + * 通过cadCom导出wmf(免得自己转换每种图元), + * 再将wmf转为emf,然后才能写入剪贴板 + * wmf生成(win32api): https://www.cnblogs.com/5iedu/p/4706324.html + */ + IntPtr hMetaFile = IntPtr.Zero; + { + var wmf = Path.ChangeExtension(tempFile, "wmf"); + Env.Editor.ComExportWMF(wmf, idArray); + if (File.Exists(wmf)) + { + hMetaFile = PlaceableMetaHeader.Wmf2Emf(wmf);//emf文件句柄 + try { File.Delete(wmf); } + catch (Exception e) { Env.Printl(e); } + } + else + { + Env.Printl("没有创建wmf,失败"); + } + } + if (hMetaFile != IntPtr.Zero) + { + // 此处尚未完成 + // EmfTool.SetEnhMetaFileDescriptionEx(ref hMetaFile, "这是阿惊的emf"); + } + + // 保存文件 + //var emfsave = Path.ChangeExtension(cadClipType.File, ".emf"); + //EmfTool.Save(emf, emfsave); + + #endregion + #region 写入 AutoCAD.R17 数据 if (getPoint) { @@ -272,14 +312,6 @@ void Copy(bool getPoint, bool isEraseSsget = false) } #endregion - #region 写入 WMF 数据 - // 通过cad com导出wmf,再将wmf转为emf,然后才能写入剪贴板 - IntPtr emf = IntPtr.Zero; - var wmf = Path.ChangeExtension(cadClipType.File, "wmf"); - Env.Editor.ExportWMF(wmf, idArray); - emf = PlaceableMetaHeader.Wmf2Emf(wmf); - #endregion - // 必须一次性写入剪贴板,详见 OpenClipboardTask var cadClipFormat = ClipTool.RegisterClipboardFormat(ClipboardEnv.CadVer); bool getFlag = ClipTool.OpenClipboardTask(true, () => { @@ -289,11 +321,11 @@ void Copy(bool getPoint, bool isEraseSsget = false) }, false/*不释放内存*/, false/*不锁定内存(否则高频触发时候卡死)*/); // 写入剪贴板: wmf,使得在粘贴链接的时候可以用 - if (emf != IntPtr.Zero) - ClipTool.SetClipboardData((uint)ClipboardFormat.CF_ENHMETAFILE, emf); + if (hMetaFile != IntPtr.Zero) + ClipTool.SetClipboardData((uint)ClipboardFormat.CF_ENHMETAFILE, hMetaFile); }); - if (emf != IntPtr.Zero) - EmfTool.DeleteEnhMetaFile(emf); + if (hMetaFile != IntPtr.Zero) + EmfTool.DeleteEnhMetaFile(hMetaFile); // 成功拷贝就删除上一次的临时文件 if (getFlag) @@ -302,8 +334,6 @@ void Copy(bool getPoint, bool isEraseSsget = false) // 加入删除队列,下次删除 if (!_delFile.Contains(cadClipType.File)) _delFile.Add(cadClipType.File); - if (!_delFile.Contains(wmf)) - _delFile.Add(wmf); // 剪切时候删除 if (isEraseSsget) @@ -352,10 +382,10 @@ void Paste(bool isBlock) return; } - var clipboardInfo = tag; - Env.Print("粘贴来源: " + clipboardInfo.File); + var cadClipType = tag; + Env.Print("粘贴来源: " + cadClipType.File); - if (!File.Exists(clipboardInfo.File)) + if (!File.Exists(cadClipType.File)) { Env.Print("文件不存在"); return; @@ -363,7 +393,7 @@ void Paste(bool isBlock) // 获取临时文件的图元id var fileEntityIds = new List(); - using (DBTrans fileTr = new(clipboardInfo.File, + using (DBTrans fileTr = new(cadClipType.File, commit: false, openMode: FileOpenMode.OpenForReadAndAllShare)) { @@ -378,12 +408,13 @@ void Paste(bool isBlock) tr.Editor?.SetImpliedSelection(new ObjectId[0]); // 清空选择集 // 新建块表记录 - var btr = CreateBlockTableRecord(tr, clipboardInfo.File); + var btr = CreateBlockTableRecord(tr, cadClipType.File); if (btr == null) return; /// 克隆进块表记录 - /// 动态块粘贴之后,用ctrl+z导致动态块特性无法恢复,是因为它: + /// 动态块粘贴之后,用ctrl+z导致动态块特性无法恢复, + /// 是因为它: using IdMapping map = new(); using ObjectIdCollection idc = new(fileEntityIds.ToArray()); tr.Task(() => { @@ -407,7 +438,7 @@ void Paste(bool isBlock) if (ent == null) continue; using (ent.ForWrite()) - ent.Move(clipboardInfo.Point, Point3d.Origin); + ent.Move(cadClipType.Point, Point3d.Origin); } // 预览并获取交互点 @@ -417,14 +448,14 @@ void Paste(bool isBlock) blockref.Move(Point3d.Origin, mousePoint); drawEntitys.Enqueue(blockref); }); - var jppo = moveJig.SetOptions(clipboardInfo.Point); + var jppo = moveJig.SetOptions(cadClipType.Point); jppo.Keywords.Add(" ", " ", "<空格取消>"); jppo.Keywords.Add("A", "A", "引线点粘贴(A)"); var dr = moveJig.Drag(); Point3d moveTo = Point3d.Origin; if (dr.Status == PromptStatus.Keyword) - moveTo = clipboardInfo.Point; + moveTo = cadClipType.Point; else if (dr.Status == PromptStatus.OK) moveTo = moveJig.MousePointWcsLast; else @@ -453,6 +484,7 @@ void Paste(bool isBlock) var msg = new StringBuilder(); int a3 = 0; + a3 = 2 | 4; if ((a3 & 1) == 1) { // win32api 不成功 @@ -463,7 +495,7 @@ void Paste(bool isBlock) var clipTypeData = ClipTool.GetClipboardData(cf); if (clipTypeData == IntPtr.Zero) { - Env.Printl("失败:GetClipboardData"); + Env.Printl("失败:粘贴剪贴板emf1"); return; } @@ -490,51 +522,30 @@ void Paste(bool isBlock) var clipTypeData = ClipTool.GetClipboardData(cf); if (clipTypeData == IntPtr.Zero) { - Env.Printl("失败:GetClipboardData"); + Env.Printl("失败:粘贴剪贴板emf2"); return; } - int a4 = 2 | 4; + int a4 = 1 | 2 | 4; if ((a4 & 1) == 1) { - // 此处无效 - var len = EmfTool.GetEnhMetaFileDescription(clipTypeData, 0, null!); - if (len != 0) - { - //PTSTR desc = (PTSTR)malloc(sizeof(TCHAR) * (len + 1)); - //GetEnhMetaFileDescription(clipTypeData, len, desc); - EmfTool.GetEnhMetaFileDescriptionString(clipTypeData, out string desc); - msg.AppendLine(desc); - } + // 获取描述 + var desc = EmfTool.GetEnhMetaFileDescriptionEx(clipTypeData); + if (!string.IsNullOrEmpty(desc)) + msg.AppendLine("DescriptionEx::" + desc); } if ((a4 & 2) == 2) { // 获取文件信息 - var len = EmfTool.GetEnhMetaFileHeader(clipTypeData, 0, IntPtr.Zero); - if (len != 0) - { - IntPtr header = Marshal.AllocHGlobal((int)len); - len = EmfTool.GetEnhMetaFileHeader(clipTypeData, len, header); - // 将内存空间转换为目标结构体 - var obj = (EnhMetaHeader)Marshal.PtrToStructure(header, typeof(EnhMetaHeader)); - msg.AppendLine(obj.ToString()); - Marshal.FreeHGlobal(header); - } + var obj = EnhMetaHeader.Create(clipTypeData); + if (obj != null) + msg.AppendLine("EnhMetaHeader::" + obj.Value.ToString()); } if ((a4 & 4) == 4) { - // 保存emf文件 - // https://blog.csdn.net/tigertianx/article/details/7098490 - var len = EmfTool.GetEnhMetaFileBits(clipTypeData, 0, null!); - if (len != 0) - { - var bytes = new byte[len]; - _ = EmfTool.GetEnhMetaFileBits(clipTypeData, len, bytes); - - using MemoryStream ms1 = new(bytes); - using var bm = Image.FromStream(ms1);//此方法emf保存成任何版本都会变成png - bm.Save("D:\\桌面\\a.png"); - } + // 保存文件 + //var emfsave = Path.ChangeExtension(cadClipType.File, ".emf"); + //EmfTool.Save(clipTypeData, emfsave); } }); } @@ -547,12 +558,21 @@ void Paste(bool isBlock) if (!iData.GetDataPresent(DataFormats.EnhancedMetafile)) return; var metafile = (Metafile)iData.GetData(DataFormats.EnhancedMetafile); - msg.AppendLine("c#::" + metafile.Size.ToString()); + /* + // 为什么序列化失败了呢 + var formatter = new BinaryFormatter(); + //using MemoryStream stream = new(); + Stream stream = new FileStream("MyFile.bin", FileMode.Create, FileAccess.Write, FileShare.None); + formatter.Serialize(stream, metafile); + stream.Close(); + //Metafile obj = (Metafile)formatter.Deserialize(stream); + */ + msg.AppendLine($"c# Metafile::{metafile.Size}"); } } if (msg.Length != 0) - Env.Printl($"{nameof(Metafile)}:{msg}"); + Env.Printl(msg); #endregion } catch (Exception e) @@ -731,4 +751,140 @@ public void CreatePreviewImage() }); } } -#endif \ No newline at end of file +#endif + +public class OleTestClass +{ + // https://adndevblog.typepad.com/autocad/2012/04/update-linked-ole-object-from-net.html + + /// + /// 更新ole链接 + /// 如果 OLE 对象在另一个应用程序中打开,则上面的代码似乎不会更新该对象 + /// 例如,位图在 Microsoft 画图中打开 + /// + /// + /// + [DllImport("mfc90u.dll", CallingConvention = CallingConvention.ThisCall, + EntryPoint = "#6766")] + public static extern int COleClientItem_UpdateLink(IntPtr thisClientItem); + + [DllImport("acdb18.dll", CallingConvention = CallingConvention.ThisCall, + EntryPoint = "?getOleClientItem@AcDbOle2Frame@@QBEPAVCOleClientItem@@XZ")] + public static extern IntPtr AcDbOle2Frame_getOleClientItem(IntPtr thisOle2Frame); + + [CommandMethod(nameof(UpdateOleClient))] + public void UpdateOleClient() + { + var per = Env.Editor.GetEntity("\n选择OLE更新链接"); + if (per.Status != PromptStatus.OK) + return; + + using DBTrans tr = new(); + var ole2frame = tr.GetObject(per.ObjectId); + if (ole2frame == null) + return; + using (ole2frame.ForWrite()) + { + IntPtr ptrClientItem = AcDbOle2Frame_getOleClientItem(ole2frame.UnmanagedObject); + COleClientItem_UpdateLink(ptrClientItem); + } + } + + + // https://adndevblog.typepad.com/autocad/2012/06/iterating-ole-linked-entities.html + // 获取OLE路径 + [CommandMethod(nameof(GetOlePath))] + public void GetOlePath() + { + using DBTrans tr = new(); + foreach (ObjectId id in tr.CurrentSpace) + { + if (!id.IsOk()) + continue; + + var ole2frame = tr.GetObject(id); + if (ole2frame == null) + continue; + switch (ole2frame.Type) + { + case Ole2Frame.ItemType.Static: + Env.Editor.WriteMessage("\n" + "Static"); + break; + case Ole2Frame.ItemType.Embedded: + Env.Editor.WriteMessage("\n" + "Embedded"); + break; + case Ole2Frame.ItemType.Link: + Env.Editor.WriteMessage("\n" + "Link"); + Env.Editor.WriteMessage("\n" + ole2frame.LinkPath); + break; + } + } + } + + [CommandMethod(nameof(SetOle))] + public void SetOle() + { + //var pr = Env.Editor.GetPoint("\n选择基点"); + //if (pr.Status != PromptStatus.OK) + // return; + //var pt = pr.Value; + + using DBTrans tr = new(); + + // https://forums.autodesk.com/t5/net/how-to-create-an-ole-object-with-ole2frame/td-p/3203222 + var oo = new Ole2Frame(); + oo.SetDatabaseDefaults(); + //oo.CopyFrom() + oo.Position2d = new(0, 0, 100, 100); + + // 打印质量? https://learn.microsoft.com/zh-cn/dotnet/api/system.printing.outputquality?view=windowsdesktop-6.0 + //oo.OutputQuality = + //oo.AutoOutputQuality = + oo.Rotation = 0.0; + //oo.WcsWidth = ; + //oo.WcsHeight = ; + //oo.ScaleWidth = ; + //oo.ScaleHeight = ; + + //宽高比锁定 + oo.LockAspect = true; + } + +#if true2 + // https://forums.autodesk.com/t5/net/how-can-i-read-data-from-ole2frame-object-excel-through-api/td-p/7944241 + public void GetOleForOffice() + { + using DBTrans tr = new(); + foreach (ObjectId id in tr.CurrentSpace) + { + if (!id.IsOk()) + continue; + + var ole2frame = tr.GetObject(id); + if (ole2frame == null) + continue; + + var wb = (Microsoft.Office.Interop.Excel.Workbook)ole2frame.OleObject; + var ws = (Microsoft.Office.Interop.Excel.Worksheet)wb.ActiveSheet; + var range = (Microsoft.Office.Interop.Excel.Range)ws.UsedRange; + + tb.SetSize(range.Rows.Count, range.Columns.Count); + tb.SetRowHeight(range.RowHeight); + tb.SetColumnWidth(range.ColumnWidth); + tb.Position = new Point3d(ole2frame.Location.X, ole2frame.Location.Y, ole2frame.Location.Z); + for (int row = 0; row < range.Rows.Count; row++) + { + for (int col = 0; col < range.Columns.Count; col++) + { + tb.Cells[row, col].TextHeight = 1; + var aa = (Microsoft.Office.Interop.Excel.Range)range.Cells[row + 1, col + 1]; + var bb = Convert.ToString(aa.Value2); + tb.SetTextString(row, col, bb ?? ""); + tb.Cells[row, col].Alignment = CellAlignment.MiddleCenter; + } + } + tb.GenerateLayout(); + } + } +#endif +} \ No newline at end of file diff --git a/tests/TestShared/testeditor.cs b/tests/TestShared/testeditor.cs index 1fb0890..e50c2a7 100644 --- a/tests/TestShared/testeditor.cs +++ b/tests/TestShared/testeditor.cs @@ -67,13 +67,15 @@ public void Testssget() [CommandMethod(nameof(Test_ExportWMF), CommandFlags.Modal | CommandFlags.UsePickSet)] public void Test_ExportWMF() { - var psr = Env.Editor.SelectImplied();// 预选,如果没有设置 CommandFlags.UsePickSet 将导致PromptStatus.Error + var psr = Env.Editor.SelectImplied();// 预选 if (psr.Status != PromptStatus.OK) psr = Env.Editor.GetSelection();// 手选 if (psr.Status != PromptStatus.OK) return; var ids = psr.Value.GetObjectIds(); - Env.Editor.ExportWMF("D:\\桌面\\aaa.dwg", ids); + // acad21(acad08没有)先选择再执行..会让你再选择一次 + // 而且只发生在启动cad之后第一次执行. + Env.Editor.ComExportWMF("D:\\桌面\\aaa.dwg", ids); } } \ No newline at end of file -- Gitee From e5350903ae3197ee3360d181585502e7dcf7f51a Mon Sep 17 00:00:00 2001 From: liuqihong <540762622@qq.com> Date: Sat, 8 Oct 2022 21:54:35 +0800 Subject: [PATCH 550/675] =?UTF-8?q?=E8=A1=A5=E5=85=85=E4=BA=8B=E5=8A=A1?= =?UTF-8?q?=E5=88=B7=E6=96=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../ExtensionMethod/EntityEx.cs | 2 - src/IFoxCAD.Cad.Shared/Runtime/DBTrans.cs | 3 + src/IFoxCAD.Cad.Shared/Runtime/RedrawEx.cs | 153 ++++++++++++++++++ 3 files changed, 156 insertions(+), 2 deletions(-) create mode 100644 src/IFoxCAD.Cad.Shared/Runtime/RedrawEx.cs diff --git a/src/IFoxCAD.Cad.Shared/ExtensionMethod/EntityEx.cs b/src/IFoxCAD.Cad.Shared/ExtensionMethod/EntityEx.cs index eaee7f0..9d5cbb3 100644 --- a/src/IFoxCAD.Cad.Shared/ExtensionMethod/EntityEx.cs +++ b/src/IFoxCAD.Cad.Shared/ExtensionMethod/EntityEx.cs @@ -14,9 +14,7 @@ public static class EntityEx public static void Flush(this Entity entity, DBTrans? trans = null) { // if (entity is null) - // { // throw new ArgumentNullException(nameof(entity)); - // } trans ??= DBTrans.Top; entity.RecordGraphicsModified(true); trans.Transaction.TransactionManager.QueueForGraphicsFlush(); diff --git a/src/IFoxCAD.Cad.Shared/Runtime/DBTrans.cs b/src/IFoxCAD.Cad.Shared/Runtime/DBTrans.cs index e283777..b17685f 100644 --- a/src/IFoxCAD.Cad.Shared/Runtime/DBTrans.cs +++ b/src/IFoxCAD.Cad.Shared/Runtime/DBTrans.cs @@ -640,6 +640,9 @@ protected virtual void Dispose(bool disposing) if (_commit)// disposing { + // 刷新队列(后台不刷新) + Editor?.Redraw(); + // 调用cad的事务进行提交,释放托管状态(托管对象) Transaction.Commit(); } diff --git a/src/IFoxCAD.Cad.Shared/Runtime/RedrawEx.cs b/src/IFoxCAD.Cad.Shared/Runtime/RedrawEx.cs new file mode 100644 index 0000000..c9437bf --- /dev/null +++ b/src/IFoxCAD.Cad.Shared/Runtime/RedrawEx.cs @@ -0,0 +1,153 @@ +namespace IFoxCAD.Cad; + +[Flags] +public enum BrightEntity : byte +{ + /// + /// 块更新 + /// + RecordGraphicsModified = 1, + /// + /// 标注更新 + /// + RecomputeDimensionBlock = 2, + /// + /// 重画 + /// + Draw = 4, + /// + /// 亮显 + /// + Highlight = 8, + /// + /// 亮显取消 + /// + Unhighlight = 16, + /// + /// 显示图元 + /// + VisibleTrue = 32, + /// + /// 隐藏图元 + /// + VisibleFalse = 64, +} + +[Flags] +public enum BrightEditor : byte +{ + /// + /// 刷新屏幕,图元不生成(例如块还是旧的显示) + /// + UpdateScreen = 1, + /// + /// 刷新全图 + /// + Regen = 2, + /// + /// 清空选择集 + /// + SelectionClean = 4, + /// + /// 视口外 + /// + ViewportsFrom = 8, + /// + /// 视口内 + /// + ViewportsIn = 16, +} + +public static class RedrawEx +{ + /// + /// 刷新屏幕 + /// + public static void Redraw(this Editor ed, Entity? ent = null) + { + // 刷新图元 + ent?.Redraw(BrightEntity.Draw | + BrightEntity.RecordGraphicsModified | + BrightEntity.RecomputeDimensionBlock); + + // 刷新 + ed.Redraw(BrightEditor.UpdateScreen); + + /* + * 我发现命令加 CommandFlags.Redraw 就不需要以下处理了: + * 数据库事务和文档事务不一样,文档事务有刷新函数. + * var doc = Acap.DocumentManager.MdiActiveDocument; + * var ed = doc.Editor; + * var tm = doc.TransactionManager; + * tm.QueueForGraphicsFlush();// 如果在最外层事务结束之前需要更新图形,此句把目前为止所做的更改放入 刷新队列 + * tm.FlushGraphics(); // 将当前 刷新队列 的图形提交到显示器 + * ed.UpdateScreen(); // 仅刷新屏幕,图元不生成(例如块还是旧的显示) + */ + var dm = Acap.DocumentManager; + var doc = dm.MdiActiveDocument; + var tm = doc.TransactionManager; + tm.QueueForGraphicsFlush(); + tm.FlushGraphics(); + + // acad2014及以上要加,立即处理队列上面的消息 + System.Windows.Forms.Application.DoEvents(); + } + + /// + /// 刷新屏幕 + /// + /// 编辑器 + /// 更新的方式 + public static void Redraw(this Editor ed, BrightEditor bright) + { + if ((bright & BrightEditor.UpdateScreen) == BrightEditor.UpdateScreen) + { + // Acap.UpdateScreen();// 两个函数底层差不多 + // 仅刷新屏幕,图元不生成(例如块还是旧的显示) + ed.UpdateScreen(); + } + + if ((bright & BrightEditor.Regen) == BrightEditor.Regen) + ed.Regen(); + + if ((bright & BrightEditor.SelectionClean) == BrightEditor.SelectionClean) + ed.SetImpliedSelection(new ObjectId[0]); + + if ((bright & BrightEditor.ViewportsFrom) == BrightEditor.ViewportsFrom) + ed.UpdateTiledViewportsFromDatabase(); // 更新视口外 + + if ((bright & BrightEditor.ViewportsIn) == BrightEditor.ViewportsIn) + ed.UpdateTiledViewportsInDatabase(); // 更新视口内 + } + + /// + /// 更改图元显示 + /// + /// 图元 + /// 更新的方式 + public static void Redraw(this Entity ent, BrightEntity bright) + { + // 调用时候必须 ent.UpgradeOpen(),参数true表示关闭图元后进行UpData,实现局部刷新块. + if ((bright & BrightEntity.RecordGraphicsModified) == BrightEntity.RecordGraphicsModified) + ent.RecordGraphicsModified(true); + + if ((bright & BrightEntity.RecomputeDimensionBlock) == BrightEntity.RecomputeDimensionBlock) + if (ent is Dimension dim) + dim.RecomputeDimensionBlock(true); + + if ((bright & BrightEntity.Draw) == BrightEntity.Draw) + ent.Draw(); + + if ((bright & BrightEntity.Highlight) == BrightEntity.Highlight) + ent.Highlight(); + + if ((bright & BrightEntity.Unhighlight) == BrightEntity.Unhighlight) + ent.Unhighlight(); + + if ((bright & BrightEntity.VisibleTrue) == BrightEntity.VisibleTrue) + ent.Visible = true; + + if ((bright & BrightEntity.VisibleFalse) == BrightEntity.VisibleFalse) + ent.Visible = false; + } +} \ No newline at end of file -- Gitee From 171c457857531507568478ef1e4913af4957c011 Mon Sep 17 00:00:00 2001 From: liuqihong <540762622@qq.com> Date: Sat, 8 Oct 2022 21:59:56 +0800 Subject: [PATCH 551/675] =?UTF-8?q?=E5=85=B1=E4=BA=AB=E9=A1=B9=E7=9B=AE?= =?UTF-8?q?=E6=B7=BB=E5=8A=A0=E6=96=87=E4=BB=B6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/IFoxCAD.Cad.Shared/IFoxCAD.Cad.Shared.projitems | 1 + src/IFoxCAD.Cad.Shared/Runtime/RedrawEx.cs | 2 ++ 2 files changed, 3 insertions(+) diff --git a/src/IFoxCAD.Cad.Shared/IFoxCAD.Cad.Shared.projitems b/src/IFoxCAD.Cad.Shared/IFoxCAD.Cad.Shared.projitems index 8ef3b71..9184907 100644 --- a/src/IFoxCAD.Cad.Shared/IFoxCAD.Cad.Shared.projitems +++ b/src/IFoxCAD.Cad.Shared/IFoxCAD.Cad.Shared.projitems @@ -49,6 +49,7 @@ + diff --git a/src/IFoxCAD.Cad.Shared/Runtime/RedrawEx.cs b/src/IFoxCAD.Cad.Shared/Runtime/RedrawEx.cs index c9437bf..72a57be 100644 --- a/src/IFoxCAD.Cad.Shared/Runtime/RedrawEx.cs +++ b/src/IFoxCAD.Cad.Shared/Runtime/RedrawEx.cs @@ -63,6 +63,8 @@ public static class RedrawEx /// /// 刷新屏幕 /// + /// 编辑器 + /// 图元 public static void Redraw(this Editor ed, Entity? ent = null) { // 刷新图元 -- Gitee From a252e68b10dfd192ae571afa1d0cef50a370e3f3 Mon Sep 17 00:00:00 2001 From: liuqihong <540762622@qq.com> Date: Sun, 9 Oct 2022 09:16:09 +0800 Subject: [PATCH 552/675] =?UTF-8?q?=E4=BF=AE=E5=A4=8Djson=E7=9A=84?= =?UTF-8?q?=E7=A8=8B=E5=BA=8F=E9=9B=86=E5=BC=95=E7=94=A8,=E5=A2=9E?= =?UTF-8?q?=E5=8A=A0=E4=B8=8D=E5=AE=89=E5=85=A8=E7=B1=BB=E5=9E=8B=E7=9A=84?= =?UTF-8?q?Point3d=E6=B5=8B=E8=AF=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Copyclip/TagClipboardInfo.cs | 49 ++++++++------- src/IFoxCAD.Cad.Shared/Copyclip/WindowsAPI.cs | 26 ++------ .../IFoxCAD.Aacad08/IFoxCAD.Acad08.csproj | 15 +++-- .../IFoxCAD.Acad09plus.csproj | 28 ++++----- .../IFoxCAD.Gcad/IFoxCAD.Gcad.csproj | 29 +++++---- .../IFoxCAD.Zcad/IFoxCAD.Zcad.csproj | 27 ++++---- tests/TestAcad08/TestAcad08.csproj | 5 +- tests/TestAcad09plus/TestAcad09plus.csproj | 15 ++--- tests/TestConsole/Program.cs | 1 + tests/TestConsole/TestConsole.csproj | 6 +- tests/TestShared/TestJson.cs | 17 +++--- tests/TestShared/TestMarshal.cs | 61 +++++++++++++++++++ tests/TestShared/TestShared.projitems | 1 + 13 files changed, 166 insertions(+), 114 deletions(-) create mode 100644 tests/TestShared/TestMarshal.cs diff --git a/src/IFoxCAD.Cad.Shared/Copyclip/TagClipboardInfo.cs b/src/IFoxCAD.Cad.Shared/Copyclip/TagClipboardInfo.cs index 6f73ca3..a04e695 100644 --- a/src/IFoxCAD.Cad.Shared/Copyclip/TagClipboardInfo.cs +++ b/src/IFoxCAD.Cad.Shared/Copyclip/TagClipboardInfo.cs @@ -1,6 +1,4 @@ -#define Marshal - -namespace IFoxCAD.Cad; +namespace IFoxCAD.Cad; using System; using System.Diagnostics; @@ -13,10 +11,9 @@ namespace IFoxCAD.Cad; public class ClipboardEnv { - // 此句将导致剪贴板的key隔离,从而导致cad版本隔离 - // public static string CadVer = $"AutoCAD.r{Acap.Version.Major}" - // 将r17写死,代表每个cad版本都去找它,实现不隔离cad版本 + // 0x01 将r17写死,代表每个cad版本都去找它,实现不隔离cad版本 public static string CadVer = "AutoCAD.r17"; + // 0x02 当前版本在r17找不到的时候找,避免按需加载插件的时候无法获取剪贴板 public static string CadCurrentVer = $"AutoCAD.r{Acap.Version.Major}"; } @@ -261,7 +258,7 @@ public override int GetHashCode() public IntRect Clone() { - return new IntRect(_Left, _Top, _Right, _Bottom); + return (IntRect)MemberwiseClone(); } #endregion } @@ -300,6 +297,19 @@ public struct Point3D : IEquatable public double X => _X; public double Y => _Y; public double Z => _Z; + public void SetX(double num) + { + _X = num; + } + public void SetY(double num) + { + _Y = num; + } + public void SetZ(double num) + { + _Z = num; + } + public Point3D(double x, double y, double z) { _X = x; @@ -348,12 +358,15 @@ public override int GetHashCode() } /* - * OLE 剪贴板说明 - * https://blog.csdn.net/chinabinlang/article/details/9815495 + * OLE 剪贴板说明 https://blog.csdn.net/chinabinlang/article/details/9815495 + * 写入时候注意: + * 0x01 c#自带的是com剪贴板 + * 最好不要使用,它不能在已经打开的剪贴板中使用, + * 也无法写入多个cf对象,也就是复制bitmap的时候会覆盖cad图元 + * Clipboard.SetImage(bitmap); + * 0x02 + * 剪贴板写入各种类型 https://blog.csdn.net/glt3953/article/details/8808262 * - * 感觉如果桌子真的是这样做,那么粘贴链接可能还真没法做成. - * 1,不知道桌子如何发送wmf文件,是结构体传送,还是文件路径传送. - * 2,不知道桌子如何接收剪贴板数据,是延迟接收还是一次性写入全局变量或者文件. */ public partial class ClipTool @@ -444,15 +457,6 @@ public partial class ClipTool public static extern uint EnumClipboardFormats(uint format); - /* 写入时候注意: - * 0x01 c#自带的是com剪贴板 - * 最好不要使用,它不能在已经打开的剪贴板中使用, - * 也无法写入多个cf对象,也就是复制bitmap的时候会覆盖cad图元 - * Clipboard.SetImage(bitmap); - * 0x02 - * 剪贴板写入各种类型 https://blog.csdn.net/glt3953/article/details/8808262 - */ - /// /// 打开剪贴板
    /// 写入之前必须清空,
    @@ -519,10 +523,9 @@ public static bool GetClipboard(string clipKey, out T? tag) #if true2 // 无法备份emf内容 +// https://blog.csdn.net/vencon_s/article/details/46345083 public static class ClipEx { - // https://blog.csdn.net/vencon_s/article/details/46345083 - /// /// 剪贴板数据保存目标数据列表 /// diff --git a/src/IFoxCAD.Cad.Shared/Copyclip/WindowsAPI.cs b/src/IFoxCAD.Cad.Shared/Copyclip/WindowsAPI.cs index b9826c1..7cdf5ed 100644 --- a/src/IFoxCAD.Cad.Shared/Copyclip/WindowsAPI.cs +++ b/src/IFoxCAD.Cad.Shared/Copyclip/WindowsAPI.cs @@ -46,6 +46,10 @@ public class WindowsAPI [DllImport("kernel32.dll", SetLastError = true)] static extern bool GlobalUnlock(IntPtr hMem); #if !Marshal + /* + const int GMEM_MOVEABLE = 0x0002; + IntPtr newPtr = WindowsAPI.GlobalAlloc(GMEM_MOVEABLE, Marshal.SizeOf(structObj)); + */ /// /// 从堆中分配内存 /// 被代替: Marshal.AllocHGlobal @@ -118,22 +122,13 @@ public static bool BytesToStruct(byte[] bytes, out T? result, out int typeSiz return false; // 分配结构体大小的内存空间 -#if Marshal IntPtr structPtr = Marshal.AllocHGlobal(typeSize); -#else - const int GMEM_MOVEABLE = 0x0002; - IntPtr structPtr = WindowsAPI.GlobalAlloc(GMEM_MOVEABLE, typeSize); -#endif // 将byte数组拷到分配好的内存空间 Marshal.Copy(bytes, 0, structPtr, typeSize); // 将内存空间转换为目标结构体 result = (T)Marshal.PtrToStructure(structPtr, structType); // 释放内存空间 -#if Marshal Marshal.FreeHGlobal(structPtr); -#else - WindowsAPI.GlobalFree(structPtr); -#endif return true; } @@ -169,14 +164,9 @@ public static void StructToPtr(object? structObj, { if (structObj == null) throw new ArgumentNullException(nameof(structObj)); -#if Marshal IntPtr newPtr = Marshal.AllocHGlobal(Marshal.SizeOf(structObj)); -#else - const int GMEM_MOVEABLE = 0x0002; - IntPtr newPtr = WindowsAPI.GlobalAlloc(GMEM_MOVEABLE, Marshal.SizeOf(structObj)); -#endif if (newPtr == IntPtr.Zero) - return; + throw new ArgumentException(nameof(newPtr)); try { // 剪贴板写入的时候不允许锁定内存,否则在频繁触发剪贴板将导致卡死程序 @@ -195,13 +185,7 @@ public static void StructToPtr(object? structObj, finally { if (freeHGlobal && newPtr != IntPtr.Zero) - { -#if Marshal Marshal.FreeHGlobal(newPtr); -#else - WindowsAPI.GlobalFree(newPtr); -#endif - } } // 将结构体拷到分配好的内存空间 diff --git a/src/IFoxCAD.Cad/IFoxCAD.Aacad08/IFoxCAD.Acad08.csproj b/src/IFoxCAD.Cad/IFoxCAD.Aacad08/IFoxCAD.Acad08.csproj index 5249ea4..4df9f3d 100644 --- a/src/IFoxCAD.Cad/IFoxCAD.Aacad08/IFoxCAD.Acad08.csproj +++ b/src/IFoxCAD.Cad/IFoxCAD.Aacad08/IFoxCAD.Acad08.csproj @@ -25,19 +25,18 @@ - - - - - DEBUG - $(Configuration);acad;ac2008;ac2009 + + + + + True @@ -59,8 +58,8 @@ - - + + diff --git a/src/IFoxCAD.Cad/IFoxCAD.Aacad09plus/IFoxCAD.Acad09plus.csproj b/src/IFoxCAD.Cad/IFoxCAD.Aacad09plus/IFoxCAD.Acad09plus.csproj index 39ded5e..99f9a44 100644 --- a/src/IFoxCAD.Cad/IFoxCAD.Aacad09plus/IFoxCAD.Acad09plus.csproj +++ b/src/IFoxCAD.Cad/IFoxCAD.Aacad09plus/IFoxCAD.Acad09plus.csproj @@ -25,18 +25,6 @@ x64 - - - - - - - - - - - - DEBUG @@ -49,6 +37,18 @@ $(Configuration);acad;ac2015 + + + + + + + + + + + + True @@ -66,8 +66,8 @@ - - + + \ No newline at end of file diff --git a/src/IFoxCAD.Cad/IFoxCAD.Gcad/IFoxCAD.Gcad.csproj b/src/IFoxCAD.Cad/IFoxCAD.Gcad/IFoxCAD.Gcad.csproj index c664c2c..58df1d3 100644 --- a/src/IFoxCAD.Cad/IFoxCAD.Gcad/IFoxCAD.Gcad.csproj +++ b/src/IFoxCAD.Cad/IFoxCAD.Gcad/IFoxCAD.Gcad.csproj @@ -25,21 +25,20 @@ - - DEBUG - - - - $(Configuration);gcad - + + DEBUG + + + $(Configuration);gcad + - - - runtime - - + + + runtime + + - + True @@ -56,8 +55,8 @@ - - + + diff --git a/src/IFoxCAD.Cad/IFoxCAD.Zcad/IFoxCAD.Zcad.csproj b/src/IFoxCAD.Cad/IFoxCAD.Zcad/IFoxCAD.Zcad.csproj index 14c41a7..0ae1bdf 100644 --- a/src/IFoxCAD.Cad/IFoxCAD.Zcad/IFoxCAD.Zcad.csproj +++ b/src/IFoxCAD.Cad/IFoxCAD.Zcad/IFoxCAD.Zcad.csproj @@ -28,27 +28,24 @@ DEBUG - $(Configuration);zcad - $(Configuration);zcad - - - runtime - - + + + runtime + + + + + runtime + + - - - runtime - - - True @@ -66,8 +63,8 @@ - - + + diff --git a/tests/TestAcad08/TestAcad08.csproj b/tests/TestAcad08/TestAcad08.csproj index aeeeab7..3395882 100644 --- a/tests/TestAcad08/TestAcad08.csproj +++ b/tests/TestAcad08/TestAcad08.csproj @@ -7,6 +7,7 @@ true true x86 + True @@ -24,8 +25,8 @@ - - + + diff --git a/tests/TestAcad09plus/TestAcad09plus.csproj b/tests/TestAcad09plus/TestAcad09plus.csproj index f623391..e0afab6 100644 --- a/tests/TestAcad09plus/TestAcad09plus.csproj +++ b/tests/TestAcad09plus/TestAcad09plus.csproj @@ -7,16 +7,17 @@ true true x64 + True - $(Configuration);acad + $(Configuration);acad;ac2015 - - - + + + @@ -31,10 +32,10 @@ - + - - + + \ No newline at end of file diff --git a/tests/TestConsole/Program.cs b/tests/TestConsole/Program.cs index f124e0b..268e472 100644 --- a/tests/TestConsole/Program.cs +++ b/tests/TestConsole/Program.cs @@ -7,6 +7,7 @@ + Console.WriteLine("***************************************************"); diff --git a/tests/TestConsole/TestConsole.csproj b/tests/TestConsole/TestConsole.csproj index 53e4165..0e0c1b2 100644 --- a/tests/TestConsole/TestConsole.csproj +++ b/tests/TestConsole/TestConsole.csproj @@ -13,5 +13,9 @@ - + + + + + diff --git a/tests/TestShared/TestJson.cs b/tests/TestShared/TestJson.cs index f6bfaf6..9205679 100644 --- a/tests/TestShared/TestJson.cs +++ b/tests/TestShared/TestJson.cs @@ -1,14 +1,16 @@ namespace TestShared; -#if true2 public class TestJson { /* - - - + * 需要引入: + * + * + * + * */ - protected void Page_Load(object sender, EventArgs e) + [CommandMethod(nameof(JavaScriptSerializer))] + public void JavaScriptSerializer() { var RegisteredUsers = new List(); RegisteredUsers.Add(0); @@ -18,7 +20,6 @@ protected void Page_Load(object sender, EventArgs e) var serializer = new System.Web.Script.Serialization.JavaScriptSerializer(); var serializedResult = serializer.Serialize(RegisteredUsers); - var deserializedResult = serializer.Deserialize>(serializedResult); + var deserializedResult = serializer.Deserialize>(serializedResult); } -} -#endif \ No newline at end of file +} \ No newline at end of file diff --git a/tests/TestShared/TestMarshal.cs b/tests/TestShared/TestMarshal.cs new file mode 100644 index 0000000..95925e0 --- /dev/null +++ b/tests/TestShared/TestMarshal.cs @@ -0,0 +1,61 @@ +using Autodesk.AutoCAD.Runtime; + +namespace TestShared; + +public class TestMarshal +{ + [CommandMethod(nameof(Test_Marshal))] + public void Test_Marshal() + { + // 0x01 申请内存,拷贝进去.那我要怎么直接获取这段内存然后直接修改呢? + Point3d structObj = new(100, 50, 0); + IntPtr structPtr = Marshal.AllocHGlobal(Marshal.SizeOf(structObj)); + Marshal.StructureToPtr(structObj, structPtr, true); + "打印A:".Print(); + structObj.Print(); + + // 0x02 拷贝到数组 + int typeSize = Marshal.SizeOf(structObj); + byte[] bytes = new byte[typeSize]; + Marshal.Copy(structPtr, bytes, 0, typeSize); + + // 0x03 还原指针到结构 + // 将内存空间转换为目标结构体 + var result = (Point3d)Marshal.PtrToStructure(structPtr, typeof(Point3d)); + "打印B:".Print(); + result.Print(); + + // 将Point3d内存转为Point3D,以此避开get保护,实现修改内部值 + unsafe + { + int* p = (int*)&structObj; + var result2 = (Point3D)Marshal.PtrToStructure((IntPtr)p, typeof(Point3D)); + result2.SetX(220); + Marshal.StructureToPtr(result2, (IntPtr)p, true); + } + "打印C:".Print(); + structObj.Print(); + + // 避免在安全类型中转换,直接用结构指针处理 + unsafe + { + var structPt = (Point3D*)&structObj; + structPt->SetY(1569); + } + "打印D:".Print(); + structObj.Print(); + + + //这个是不对的,会获取类型的指针,替换了就错误了 + //RuntimeTypeHandle handle = structObj.GetType().TypeHandle; + //IntPtr ptr = handle.Value; + //var result3 = (Point3D)Marshal.PtrToStructure(ptr, typeof(Point3D)); + //result3.SetX(330); + //Marshal.StructureToPtr(result3, ptr, true); + //"打印D:".Print(); + //structObj.Print(); + + // 释放内存 + Marshal.FreeHGlobal(structPtr); + } +} \ No newline at end of file diff --git a/tests/TestShared/TestShared.projitems b/tests/TestShared/TestShared.projitems index 031ecc5..bb84b7a 100644 --- a/tests/TestShared/TestShared.projitems +++ b/tests/TestShared/TestShared.projitems @@ -28,6 +28,7 @@ + -- Gitee From cf3f9ce7d262f83e70355af221f034aa859b5fb3 Mon Sep 17 00:00:00 2001 From: liuqihong <540762622@qq.com> Date: Mon, 10 Oct 2022 20:03:46 +0800 Subject: [PATCH 553/675] =?UTF-8?q?=E4=BF=AE=E6=94=B9=E4=B8=8D=E5=AE=89?= =?UTF-8?q?=E5=85=A8=E4=BE=8B=E5=AD=90?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Copyclip/TagClipboardInfo.cs | 41 +++++---------- tests/TestShared/TestMarshal.cs | 52 +++++++++---------- 2 files changed, 39 insertions(+), 54 deletions(-) diff --git a/src/IFoxCAD.Cad.Shared/Copyclip/TagClipboardInfo.cs b/src/IFoxCAD.Cad.Shared/Copyclip/TagClipboardInfo.cs index a04e695..3477ef1 100644 --- a/src/IFoxCAD.Cad.Shared/Copyclip/TagClipboardInfo.cs +++ b/src/IFoxCAD.Cad.Shared/Copyclip/TagClipboardInfo.cs @@ -6,6 +6,7 @@ using System.Text; using System.Threading; using System.Windows; +using System.Windows.Documents; using System.Windows.Media.Media3D; using System.Xml.Linq; @@ -289,32 +290,18 @@ public override string ToString() public struct Point3D : IEquatable { [DebuggerBrowsable(DebuggerBrowsableState.Never)] - private string DebuggerDisplay => $"(X:{_X},Y:{_Y},Z:{_Z})"; - - double _X; - double _Y; - double _Z; - public double X => _X; - public double Y => _Y; - public double Z => _Z; - public void SetX(double num) - { - _X = num; - } - public void SetY(double num) - { - _Y = num; - } - public void SetZ(double num) - { - _Z = num; - } + private string DebuggerDisplay => $"(X:{X},Y:{Y},Z:{Z})"; + /* 由于此类是用来优化,从而实现字段修改,因此直接暴露字段减少栈帧 */ + public double X; + public double Y; + public double Z; + public Point3D(double x, double y, double z) { - _X = x; - _Y = y; - _Z = z; + X = x; + Y = y; + Z = z; } public static implicit operator Point3D(Point3d pt) { @@ -334,9 +321,9 @@ public override string ToString() public bool Equals(Point3D other) { return - _X == other._X && - _Y == other._Y && - _Z == other._Z; + X == other.X && + Y == other.Y && + Z == other.Z; } public static bool operator !=(Point3D a, Point3D b) { @@ -352,7 +339,7 @@ public override bool Equals(object obj) } public override int GetHashCode() { - return (_X, _Y).GetHashCode() ^ _Z.GetHashCode(); + return (X, Y).GetHashCode() ^ Z.GetHashCode(); } #endregion } diff --git a/tests/TestShared/TestMarshal.cs b/tests/TestShared/TestMarshal.cs index 95925e0..5003f31 100644 --- a/tests/TestShared/TestMarshal.cs +++ b/tests/TestShared/TestMarshal.cs @@ -1,50 +1,48 @@ -using Autodesk.AutoCAD.Runtime; - -namespace TestShared; +namespace TestShared; public class TestMarshal { [CommandMethod(nameof(Test_Marshal))] public void Test_Marshal() { - // 0x01 申请内存,拷贝进去.那我要怎么直接获取这段内存然后直接修改呢? + // 0x01 如何修改Point3d内容? Point3d structObj = new(100, 50, 0); - IntPtr structPtr = Marshal.AllocHGlobal(Marshal.SizeOf(structObj)); - Marshal.StructureToPtr(structObj, structPtr, true); - "打印A:".Print(); + "\n".Print(); + "原始:".Print(); structObj.Print(); - // 0x02 拷贝到数组 - int typeSize = Marshal.SizeOf(structObj); - byte[] bytes = new byte[typeSize]; - Marshal.Copy(structPtr, bytes, 0, typeSize); - - // 0x03 还原指针到结构 - // 将内存空间转换为目标结构体 - var result = (Point3d)Marshal.PtrToStructure(structPtr, typeof(Point3d)); - "打印B:".Print(); - result.Print(); - + // 0x02 最佳方法: // 将Point3d内存转为Point3D,以此避开get保护,实现修改内部值 + // 为了避免在安全类型中转换,多了栈帧(无法内联),直接用指针处理 unsafe { - int* p = (int*)&structObj; - var result2 = (Point3D)Marshal.PtrToStructure((IntPtr)p, typeof(Point3D)); - result2.SetX(220); - Marshal.StructureToPtr(result2, (IntPtr)p, true); + var structPt = (Point3D*)&structObj;//必须强转成这个指针类型,不然它为(Point3d*) + structPt->X = 1569; } - "打印C:".Print(); + "指针法:".Print(); structObj.Print(); - // 避免在安全类型中转换,直接用结构指针处理 + // 0x03 此方法仍然需要不安全操作,而且多了几个函数调用... unsafe { - var structPt = (Point3D*)&structObj; - structPt->SetY(1569); + var p = (IntPtr)(IntPtr*)&structObj; + var result2 = (Point3D)Marshal.PtrToStructure(p, typeof(Point3D)); + result2.X = 220; + Marshal.StructureToPtr(result2, p, true); } - "打印D:".Print(); + "封送法:".Print(); structObj.Print(); + // 拷贝到数组,还原指针到结构,最后将内存空间转换为目标结构体 + // 这不闹嘛~ + int typeSize = Marshal.SizeOf(structObj); + byte[] bytes = new byte[typeSize]; + IntPtr structPtr = Marshal.AllocHGlobal(Marshal.SizeOf(structObj)); + Marshal.StructureToPtr(structObj, structPtr, true); + Marshal.Copy(structPtr, bytes, 0, typeSize); + var result = (Point3d)Marshal.PtrToStructure(structPtr, typeof(Point3d)); + "内存拷贝:".Print(); + result.Print(); //这个是不对的,会获取类型的指针,替换了就错误了 //RuntimeTypeHandle handle = structObj.GetType().TypeHandle; -- Gitee From 4cc2a9e84a9a59a428f5505936cbc1a2fad6a2b5 Mon Sep 17 00:00:00 2001 From: liuqihong <540762622@qq.com> Date: Wed, 12 Oct 2022 00:03:53 +0800 Subject: [PATCH 554/675] =?UTF-8?q?=E5=B0=86=E6=89=80=E6=9C=89=E7=9A=84?= =?UTF-8?q?=E6=B5=8B=E8=AF=95=E5=91=BD=E4=BB=A4=E6=95=B4=E7=90=86=E5=A4=A7?= =?UTF-8?q?=E5=B0=8F=E5=86=99?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 2 +- .../Copyclip/TagClipboardInfo.cs | 5 +- .../ExtensionMethod/EditorEx.cs | 30 ++++--- tests/TestAcad09plus/wpf/Class1.cs | 6 +- tests/TestShared/CmdINI.cs | 23 ----- tests/TestShared/Test.cs | 72 +++++++-------- tests/TestShared/TestAOP.cs | 10 +-- .../TestShared/{testblock.cs => TestBlock.cs} | 90 +++++++------------ .../{testConvexHull.cs => TestConvexHull.cs} | 4 +- tests/TestShared/TestCurve.cs | 24 +++-- tests/TestShared/TestDBTrans.cs | 20 ++--- .../{testeditor.cs => TestEditor.cs} | 21 ++--- tests/TestShared/TestEnt.cs | 8 +- tests/TestShared/{testenv.cs => TestEnv.cs} | 76 +++++++++------- tests/TestShared/TestFileDatabase.cs | 7 +- tests/TestShared/{Testid.cs => TestId.cs} | 16 ++-- tests/TestShared/TestJig.cs | 16 ++-- tests/TestShared/TestJigExTransient.cs | 8 +- tests/TestShared/TestLisp.cs | 2 +- tests/TestShared/TestLoop.cs | 13 +-- tests/TestShared/TestMarshal.cs | 25 +++--- tests/TestShared/TestMirrorFile.cs | 4 +- tests/TestShared/TestPoint.cs | 25 +++--- tests/TestShared/TestQuadTree.cs | 17 ++-- ...estselectfilter.cs => TestSelectfilter.cs} | 17 ++-- tests/TestShared/TestShared.projitems | 5 +- .../{XrefExTest.cs => TestXrefEx.cs} | 0 27 files changed, 249 insertions(+), 297 deletions(-) rename tests/TestShared/{testblock.cs => TestBlock.cs} (91%) rename tests/TestShared/{testConvexHull.cs => TestConvexHull.cs} (97%) rename tests/TestShared/{testeditor.cs => TestEditor.cs} (87%) rename tests/TestShared/{testenv.cs => TestEnv.cs} (47%) rename tests/TestShared/{Testid.cs => TestId.cs} (86%) rename tests/TestShared/{testselectfilter.cs => TestSelectfilter.cs} (74%) rename tests/TestShared/{XrefExTest.cs => TestXrefEx.cs} (100%) diff --git a/README.md b/README.md index 74eae3c..5d9e254 100644 --- a/README.md +++ b/README.md @@ -98,7 +98,7 @@ IFox的[jing分支](https://gitee.com/inspirefunction/ifoxcad/tree/jing/)是一 - 添加代码 ```c# - [CommandMethod("hello")] + [CommandMethod(nameof(Hello))] public void Hello() { using DBTrans tr = new(); diff --git a/src/IFoxCAD.Cad.Shared/Copyclip/TagClipboardInfo.cs b/src/IFoxCAD.Cad.Shared/Copyclip/TagClipboardInfo.cs index 3477ef1..d4e7400 100644 --- a/src/IFoxCAD.Cad.Shared/Copyclip/TagClipboardInfo.cs +++ b/src/IFoxCAD.Cad.Shared/Copyclip/TagClipboardInfo.cs @@ -123,7 +123,7 @@ void GetSize() #region 视口指针 /* - [CommandMethod(nameof(testAcedGetAcadDwgview))] + [CommandMethod(nameof(Test_AcedGetAcadDwgview))] public void testAcedGetAcadDwgview() { var dm = Acap.DocumentManager; @@ -284,7 +284,6 @@ public override string ToString() } [Serializable] -[StructLayout(LayoutKind.Sequential, Pack = 8)] [DebuggerDisplay("{DebuggerDisplay,nq}")] [DebuggerTypeProxy(typeof(Point3D))] public struct Point3D : IEquatable @@ -296,7 +295,7 @@ public struct Point3D : IEquatable public double X; public double Y; public double Z; - + public Point3D(double x, double y, double z) { X = x; diff --git a/src/IFoxCAD.Cad.Shared/ExtensionMethod/EditorEx.cs b/src/IFoxCAD.Cad.Shared/ExtensionMethod/EditorEx.cs index c669610..a94bda3 100644 --- a/src/IFoxCAD.Cad.Shared/ExtensionMethod/EditorEx.cs +++ b/src/IFoxCAD.Cad.Shared/ExtensionMethod/EditorEx.cs @@ -13,7 +13,8 @@ public static class EditorEx /// 点 /// 过滤器 /// 选择集结果类 - public static PromptSelectionResult SelectAtPoint(this Editor editor, Point3d point, SelectionFilter? filter = default) + public static PromptSelectionResult SelectAtPoint(this Editor editor, Point3d point, + SelectionFilter? filter = default) { return editor.SelectCrossingWindow(point, point, filter); } @@ -49,13 +50,26 @@ public static SelectionSet SelectByLineWeight(this Editor editor, LineWeight lin }; } - PromptSelectionResult res = editor.SelectAll(filter); + var res = editor.SelectAll(filter); return res.Value; } - public static PromptSelectionResult? SSGet(this Editor editor, string? mode = null, SelectionFilter? filter = null, string[]? messages = null, Dictionary? keywords = null) + /// + /// 选择集 + /// + /// 命令行对象 + /// 模式 + /// 过滤器 + /// 消息 + /// 关键字和回调函数 + /// + public static PromptSelectionResult? SSGet(this Editor editor, + string? mode = null, + SelectionFilter? filter = null, + string[]? messages = null, + Dictionary? keywords = null) { - var pso = new PromptSelectionOptions(); + PromptSelectionOptions pso = new(); PromptSelectionResult? ss = null; if (mode is not null) { @@ -307,18 +321,12 @@ public static void SsgetAddKeys(this PromptSelectionOptions pso, // switch (e.Input) // { // case "Z": - // { // break; - // } // case "X": - // { // break; - // } // case "Q": - // { // break; - // } // } // // 抛出异常,用于更新提示信息 // throw new ArgumentException("XuError"); @@ -1048,7 +1056,7 @@ public enum RunLispFlag : byte { /* * 测试命令: - * [CommandMethod("CmdTest_RunLisp")] + * [CommandMethod(nameof(CmdTest_RunLisp))] * public static void CmdTest_RunLisp() * { * var res = RunLisp("(setq abc 10)"); diff --git a/tests/TestAcad09plus/wpf/Class1.cs b/tests/TestAcad09plus/wpf/Class1.cs index b711b1e..cafff3c 100644 --- a/tests/TestAcad09plus/wpf/Class1.cs +++ b/tests/TestAcad09plus/wpf/Class1.cs @@ -2,10 +2,10 @@ public class Class1 { - [CommandMethod("testwpf")] - public void TestWPf() + [CommandMethod(nameof(Test_WPf))] + public void Test_WPf() { var test = new TestView(); Acap.ShowModalWindow(test); } -} +} \ No newline at end of file diff --git a/tests/TestShared/CmdINI.cs b/tests/TestShared/CmdINI.cs index ea7b21c..2a259f5 100644 --- a/tests/TestShared/CmdINI.cs +++ b/tests/TestShared/CmdINI.cs @@ -148,27 +148,4 @@ public static void Test_GetEnv() Env.Printl("GetEnv:" + Env.GetEnv("abc")); Env.Printl("GetEnv PATH:" + Env.GetEnv("PATH")); } - - -#if !NET35 && !NET40 - - // 通过此功能获取全部变量,尚不清楚此处如何设置,没有通过测试 - [CommandMethod(nameof(Test_GetvarAll))] - public static void Test_GetvarAll() - { - GetvarAll(); - } - - public static Dictionary GetvarAll() - { - var dict = new Dictionary(); - var en = new SystemVariableEnumerator(); - while (en.MoveNext()) - { - Console.WriteLine(en.Current.Name + "-----" + en.Current.Value);// Value会出现异常 - dict.Add(en.Current.Name, en.Current.Value); - } - return dict; - } -#endif } \ No newline at end of file diff --git a/tests/TestShared/Test.cs b/tests/TestShared/Test.cs index 92abfc4..b5ab81a 100644 --- a/tests/TestShared/Test.cs +++ b/tests/TestShared/Test.cs @@ -3,8 +3,8 @@ public partial class Test { - [CommandMethod("dbtest")] - public void Dbtest() + [CommandMethod(nameof(Test_DBTrans))] + public void Test_DBTrans() { using DBTrans tr = new(); if (tr.Editor is null) @@ -35,8 +35,8 @@ public void Dbtest() } // add entity test - [CommandMethod("addent")] - public void Addent() + [CommandMethod(nameof(Test_Addent))] + public void Test_Addent() { using DBTrans tr = new(); Line line = new(new Point3d(0, 0, 0), new Point3d(1, 1, 0)); @@ -47,8 +47,8 @@ public void Addent() tr.PaperSpace.AddEntity(line2); } - [CommandMethod("drawarc")] - public void Drawarc() + [CommandMethod(nameof(Test_Drawarc))] + public void Test_Drawarc() { using DBTrans tr = new(); Arc arc1 = EntityEx.CreateArcSCE(new Point3d(2, 0, 0), new Point3d(0, 0, 0), new Point3d(0, 2, 0));// 起点,圆心,终点 @@ -58,8 +58,8 @@ public void Drawarc() tr.CurrentSpace.AddArc(new Point3d(0, 0, 0), new Point3d(1, 1, 0), new Point3d(2, 0, 0));// 起点,圆上一点,终点 } - [CommandMethod("drawcircle")] - public void DraCircle() + [CommandMethod(nameof(Test_DrawCircle))] + public void Test_DrawCircle() { using DBTrans tr = new(); var circle1 = EntityEx.CreateCircle(new Point3d(0, 0, 0), new Point3d(1, 0, 0)); // 起点,终点 @@ -67,20 +67,16 @@ public void DraCircle() var circle3 = EntityEx.CreateCircle(new Point3d(-2, 0, 0), new Point3d(0, 0, 0), new Point3d(2, 0, 0));// 起点,圆心,终点,失败 tr.CurrentSpace.AddEntity(circle1, circle2!); if (circle3 is not null) - { tr.CurrentSpace.AddEntity(circle3); - } else - { tr.Editor?.WriteMessage("三点画圆失败"); - } tr.CurrentSpace.AddEntity(circle3!); tr.CurrentSpace.AddCircle(new Point3d(0, 0, 0), new Point3d(1, 1, 0), new Point3d(2, 0, 0));// 三点画圆,成功 tr.CurrentSpace.AddCircle(new Point3d(0, 0, 0), new Point3d(1, 1, 0), new Point3d(2, 2, 0));// 起点,圆上一点,终点(共线) } - [CommandMethod("layertest")] - public void Layertest() + [CommandMethod(nameof(Test_LayerAdd0))] + public void Test_LayerAdd0() { using DBTrans tr = new(); tr.LayerTable.Add("1"); @@ -97,24 +93,24 @@ public void Layertest() // 添加图层 - [CommandMethod("layerAdd1")] - public void Layertest1() + [CommandMethod(nameof(Test_LayerAdd1))] + public void Test_LayerAdd1() { using DBTrans tr = new(); tr.LayerTable.Add("test1", Color.FromColorIndex(ColorMethod.ByColor, 1)); } // 添加图层 - [CommandMethod("layerAdd2")] - public void Layertest2() + [CommandMethod(nameof(Test_LayerAdd2))] + public void Test_LayerAdd2() { using DBTrans tr = new(); tr.LayerTable.Add("test2", 2); // tr.LayerTable["3"] = new LayerTableRecord(); } // 删除图层 - [CommandMethod("layerdel")] - public void LayerDel() + [CommandMethod(nameof(Test_LayerDel))] + public void Test_LayerDel() { using DBTrans tr = new(); Env.Editor.WriteMessage(tr.LayerTable.Delete("0").ToString()); // 删除图层 0 @@ -127,8 +123,8 @@ public void LayerDel() } // 添加直线 - [CommandMethod("linedemo1")] - public void AddLine1() + [CommandMethod(nameof(Test_AddLine1))] + public void Test_AddLine1() { using DBTrans tr = new(); // tr.ModelSpace.AddEnt(line); @@ -154,8 +150,8 @@ public void AddLine1() } // 增加多段线1 - [CommandMethod("Pldemo1")] - public void AddPolyline1() + [CommandMethod(nameof(Test_AddPolyline1))] + public void Test_AddPolyline1() { using DBTrans tr = new(); Polyline pl = new(); @@ -171,8 +167,8 @@ public void AddPolyline1() } // 增加多段线2 - [CommandMethod("pldemo2")] - public void Addpl2() + [CommandMethod(nameof(Test_AddPolyline2))] + public void Test_AddPolyline2() { var pts = new List<(Point3d, double, double, double)> { @@ -192,8 +188,8 @@ public void Addpl2() // 测试扩展数据 - [CommandMethod("addxdata")] - public void AddXdata() + [CommandMethod(nameof(Test_AddXdata))] + public void Test_AddXdata() { using DBTrans tr = new(); var appname = "myapp2"; @@ -225,8 +221,8 @@ public void AddXdata() tr.CurrentSpace.AddEntity(line); } - [CommandMethod("getxdata")] - public void GetXdata() + [CommandMethod(nameof(Test_GetXdata))] + public void Test_GetXdata() { var doc = Acap.DocumentManager.MdiActiveDocument; var ed = doc.Editor; @@ -247,8 +243,8 @@ public void GetXdata() // } } - [CommandMethod("changexdata")] - public void Changexdata() + [CommandMethod(nameof(Test_Changexdata))] + public void Test_Changexdata() { var doc = Acap.DocumentManager.MdiActiveDocument; var ed = doc.Editor; @@ -265,8 +261,8 @@ public void Changexdata() } - [CommandMethod("removexdata")] - public void Removexdata() + [CommandMethod(nameof(Test_Removexdata))] + public void Test_Removexdata() { var doc = Acap.DocumentManager.MdiActiveDocument; var ed = doc.Editor; @@ -283,8 +279,8 @@ public void Removexdata() } } - [CommandMethod("PrintLayerName")] - public void PrintLayerName() + [CommandMethod(nameof(Test_PrintLayerName))] + public void Test_PrintLayerName() { using DBTrans tr = new(); foreach (var layerRecord in tr.LayerTable.GetRecords()) @@ -299,8 +295,8 @@ public void PrintLayerName() } - [CommandMethod("testrec")] - public void TestRec() + [CommandMethod(nameof(Test_Rec))] + public void Test_Rec() { Point2d p1 = new(10000.2, 100000.5); Point2d p2 = new(15000.9, 100000.5); diff --git a/tests/TestShared/TestAOP.cs b/tests/TestShared/TestAOP.cs index 8b0e638..4edef4d 100644 --- a/tests/TestShared/TestAOP.cs +++ b/tests/TestShared/TestAOP.cs @@ -15,7 +15,7 @@ public class AutoAOP [IFoxInitialize] public void Initialize() { - AOP.Run(nameof(Test)); + AOP.Run(nameof(Test_)); } } @@ -29,13 +29,13 @@ public class AopTestClass { // 类不拒绝,这里拒绝 [IFoxRefuseInjectionTransaction] - [CommandMethod("IFoxRefuseInjectionTransaction")] + [CommandMethod(nameof(IFoxRefuseInjectionTransaction))] public void IFoxRefuseInjectionTransaction() { } // 不拒绝 - [CommandMethod("InjectionTransaction")] + [CommandMethod(nameof(InjectionTransaction))] public void InjectionTransaction() { // 怎么用事务呢? @@ -51,7 +51,7 @@ public class AopTestClassRefuseInjection { // 此时这个也是拒绝的..这里加特性只是无所谓 [IFoxRefuseInjectionTransaction] - [CommandMethod("IFoxRefuseInjectionTransaction2")] + [CommandMethod(nameof(IFoxRefuseInjectionTransaction2))] public void IFoxRefuseInjectionTransaction2() { // 拒绝注入就要自己开事务,通常用在循环提交事务上面. @@ -59,7 +59,7 @@ public void IFoxRefuseInjectionTransaction2() using DBTrans tr = new(); } - [CommandMethod("InjectionTransaction2")] + [CommandMethod(nameof(InjectionTransaction2))] public void InjectionTransaction2() { } diff --git a/tests/TestShared/testblock.cs b/tests/TestShared/TestBlock.cs similarity index 91% rename from tests/TestShared/testblock.cs rename to tests/TestShared/TestBlock.cs index 5d54e46..925875d 100644 --- a/tests/TestShared/testblock.cs +++ b/tests/TestShared/TestBlock.cs @@ -2,7 +2,7 @@ public class TestBlock { - [CommandMethod("Test_GetBoundingBoxEx")] + [CommandMethod(nameof(Test_GetBoundingBoxEx))] public void Test_GetBoundingBoxEx() { using DBTrans tr = new(); @@ -19,7 +19,7 @@ public void Test_GetBoundingBoxEx() } // 块定义 - [CommandMethod("Test_blockdef")] + [CommandMethod(nameof(Test_BlockDef))] public void Test_BlockDef() { using DBTrans tr = new(); @@ -54,7 +54,7 @@ public void Test_BlockDef() }); } // 修改块定义 - [CommandMethod("Test_blockdefchange")] + [CommandMethod(nameof(Test_BlockDefChange))] public void Test_BlockDefChange() { using DBTrans tr = new(); @@ -89,7 +89,7 @@ public void Test_BlockDefChange() tr.Editor?.Regen(); } - [CommandMethod("Test_insertblockdef")] + [CommandMethod(nameof(Test_InsertBlockDef))] public void Test_InsertBlockDef() { using DBTrans tr = new(); @@ -134,7 +134,7 @@ public void Test_InsertBlockDef() tr.CurrentSpace.InsertBlock(new Point3d(-10, 0, 0), "test44"); } - [CommandMethod("Test_addattsdef")] + [CommandMethod(nameof(Test_AddAttsDef))] public void Test_AddAttsDef() { using DBTrans tr = new(); @@ -146,7 +146,7 @@ public void Test_AddAttsDef() tr.BlockTable.AddAttsToBlocks(blockref, new() { att1, att2 }); } - [CommandMethod("test_blocknullbug")] + [CommandMethod(nameof(Test_BlockNullBug))] public void Test_BlockNullBug() { using DBTrans tr = new(); @@ -160,7 +160,7 @@ public void Test_BlockNullBug() tr.CurrentSpace.InsertBlock(new Point3d(0, 0, 0), "test44"); } - [CommandMethod("test_block_file")] + [CommandMethod(nameof(Test_BlockFile))] public void Test_BlockFile() { using DBTrans tr = new(); @@ -169,7 +169,7 @@ public void Test_BlockFile() } - [CommandMethod("test_clip")] + [CommandMethod(nameof(Test_ClipBlock))] public void Test_ClipBlock() { using DBTrans tr = new(); @@ -189,11 +189,9 @@ public void Test_ClipBlock() bref1?.ClipBlockRef(new Point3d(13, 13, 0), new Point3d(17, 17, 0)); } - /// - /// 给用户的测试程序,不知道对错 - /// - [CommandMethod(nameof(Test_block_ej))] - public void Test_block_ej() + // 给用户的测试程序,不知道对错 + [CommandMethod(nameof(Test_Block_ej))] + public void Test_Block_ej() { using (DBTrans tr = new()) { @@ -281,8 +279,8 @@ public void Test_block_ej() tr2.Editor?.Regen(); } - [CommandMethod("W_KSZK")] - public void QuickBlockDef() + [CommandMethod(nameof(Test_QuickBlockDef2))] + public void Test_QuickBlockDef2() { // Database db = HostApplicationServices.WorkingDatabase; Editor ed = Acap.DocumentManager.MdiActiveDocument.Editor; @@ -367,22 +365,22 @@ public void QuickBlockDef() // } } - [CommandMethod("test_quickblockdef")] - public void Test_QuickBlockDef() + [CommandMethod(nameof(Test_QuickBlockDef1))] + public void Test_QuickBlockDef1() { - Database db = HostApplicationServices.WorkingDatabase; - Editor ed = Acap.DocumentManager.MdiActiveDocument.Editor; + var dm = Acap.DocumentManager; + var doc = dm.MdiActiveDocument; + var db = doc.Database; + var ed = doc.Editor; + PromptSelectionOptions promptOpt = new() { MessageForAdding = "请选择需要快速制作块的对象" }; string blockName = "W_BLOCK_" + DateTime.Now.ToString("yyyyMMdd_HHmmss"); - var rss = ed.GetSelection(promptOpt); - // var rss = Env.Editor.GetSelection(promptOpt); + var rss = Env.Editor.GetSelection(promptOpt); if (rss.Status != PromptStatus.OK) - { return; - } using var tr = db.TransactionManager.StartTransaction(); var ids = rss.Value.GetObjectIds(); @@ -420,7 +418,7 @@ public void Test_QuickBlockDef() // ed.Regen(); } - public void TestWblock() + void Wblock() { var curdb = HostApplicationServices.WorkingDatabase; PromptSelectionOptions opts = new() @@ -433,7 +431,7 @@ public void TestWblock() db.SaveAs(@"c:\test.dwg", DwgVersion.Current); } - public void TestChangeDynameicBlock() + void ChangeDynameicBlock() { var pro = new Dictionary { @@ -446,8 +444,8 @@ public void TestChangeDynameicBlock() // 这是第一个函数的用法 } - [CommandMethod("TestBack")] - public void TestBack() + [CommandMethod(nameof(Test_Back))] + public void Test_Back() { string dir = Environment.GetFolderPath(Environment.SpecialFolder.DesktopDirectory); string dwg = dir + "\\test.dwg"; @@ -472,7 +470,7 @@ public void TestBack() public class BlockImportClass { - [CommandMethod("Test_CBLL")] + [CommandMethod(nameof(Test_Cbll))] public void Test_Cbll() { string filename = @"C:\Users\vic\Desktop\Drawing1.dwg"; @@ -484,55 +482,31 @@ public void Test_Cbll() } #if !NET35 - [CommandMethod("Test_CombineBlocksIntoLibrary")] + [CommandMethod(nameof(Test_CombineBlocksIntoLibrary))] public void Test_CombineBlocksIntoLibrary() { - Document doc = - Acap.DocumentManager.MdiActiveDocument; + Document doc = Acap.DocumentManager.MdiActiveDocument; Editor ed = doc.Editor; Database destDb = doc.Database; - // Get name of folder from which to load and import blocks - - PromptResult pr = - ed.GetString("\nEnter the folder of source drawings: "); + PromptResult pr = ed.GetString("\nEnter the folder of source drawings: "); if (pr.Status != PromptStatus.OK) return; string pathName = pr.StringResult; - - // Check the folder exists - if (!Directory.Exists(pathName)) { - ed.WriteMessage( - "\nDirectory does not exist: {0}", pathName - ); + ed.WriteMessage("\nDirectory does not exist: {0}", pathName); return; } - - // Get the names of our DWG files in that folder - string[] fileNames = Directory.GetFiles(pathName, "*.dwg"); - - // A counter for the files we've imported - int imported = 0, failed = 0; - - // For each file in our list - foreach (string fileName in fileNames) { - // Double-check we have a DWG file (probably unnecessary) - - if (fileName.EndsWith( - ".dwg", - StringComparison.InvariantCultureIgnoreCase - ) - ) + if (fileName.EndsWith(".dwg", + StringComparison.InvariantCultureIgnoreCase)) { // Catch exceptions at the file level to allow skipping - try { // Suggestion from Thorsten Meinecke... diff --git a/tests/TestShared/testConvexHull.cs b/tests/TestShared/TestConvexHull.cs similarity index 97% rename from tests/TestShared/testConvexHull.cs rename to tests/TestShared/TestConvexHull.cs index 186c50a..380bc44 100644 --- a/tests/TestShared/testConvexHull.cs +++ b/tests/TestShared/TestConvexHull.cs @@ -3,8 +3,8 @@ namespace Test; public class TestConvexHull { - [CommandMethod("testch")] - public void Testch() + [CommandMethod(nameof(Test_ConvexHull))] + public void Test_ConvexHull() { // using DBTrans tr = new(); // var pts = new List(); diff --git a/tests/TestShared/TestCurve.cs b/tests/TestShared/TestCurve.cs index a8ad2d5..803d760 100644 --- a/tests/TestShared/TestCurve.cs +++ b/tests/TestShared/TestCurve.cs @@ -1,11 +1,9 @@ -using Autodesk.AutoCAD.DatabaseServices; - -namespace Test; +namespace Test; public class TestGraph { - [CommandMethod("testpointindict")] - public void TestPointInDict() + [CommandMethod(nameof(Test_PointInDict))] + public void Test_PointInDict() { var pt1 = new Point3d(0.0255, 0.452, 0); var pt2 = new Point3d(0.0255001, 0.452003, 0); @@ -23,8 +21,8 @@ public void TestPointInDict() Env.Print(dict[pt1]); } - [CommandMethod("testgraph")] - public void TestGraph1() + [CommandMethod(nameof(Test_Graph1))] + public void Test_Graph1() { using DBTrans tr = new(); var ents = Env.Editor.SSGet()?.Value?.GetEntities(); @@ -39,8 +37,8 @@ public void TestGraph1() }); } - [CommandMethod("testgraphspeed")] - public void TestGraphspeed() + [CommandMethod(nameof(Test_Graphspeed))] + public void Test_Graphspeed() { using DBTrans tr = new(); var ents = Env.Editor.SSGet()?.Value?.GetEntities(); @@ -89,8 +87,8 @@ public void TestGraphspeed() public class TestCurve { - [CommandMethod("testbreakcurve")] - public void TestBreakCurve() + [CommandMethod(nameof(Test_BreakCurve))] + public void Test_BreakCurve() { using DBTrans tr = new(); var ents = Env.Editor.SSGet()?.Value.GetEntities(); @@ -101,8 +99,8 @@ public void TestBreakCurve() tr.CurrentSpace.AddEntity(tt); } - [CommandMethod("testCurveCurveIntersector3d")] - public void TestCurveCurveIntersector3d() + [CommandMethod(nameof(Test_CurveCurveIntersector3d))] + public void Test_CurveCurveIntersector3d() { using DBTrans tr = new(); var ents = Env.Editor.SSGet()? diff --git a/tests/TestShared/TestDBTrans.cs b/tests/TestShared/TestDBTrans.cs index 89cdaeb..bc11e99 100644 --- a/tests/TestShared/TestDBTrans.cs +++ b/tests/TestShared/TestDBTrans.cs @@ -30,8 +30,8 @@ public void FileNotExist3() } - [CommandMethod("testtr")] - public void Testtr() + [CommandMethod(nameof(Test_SaveDwgFile))] + public void Test_SaveDwgFile() { string filename = @"C:\Users\vic\Desktop\test.dwg"; using DBTrans tr = new(filename); @@ -39,8 +39,8 @@ public void Testtr() // tr.Database.SaveAs(filename,DwgVersion.Current); tr.SaveDwgFile(); } - [CommandMethod("testifoxcommit")] - public void Testifoxcommit() + [CommandMethod(nameof(Test_DBTransAbort))] + public void Test_DBTransAbort() { using DBTrans tr = new(); tr.ModelSpace.AddCircle(new Point3d(0, 0, 0), 20); @@ -51,8 +51,8 @@ public void Testifoxcommit() // AOP 应用 预计示例: // 1. 无参数 // [AOP] - // [CommandMethod("TESTAOP")] - // public void testaop() + // [CommandMethod(nameof(Test_AOP1))] + // public void TestAOP1() // { // // 不用 using DBTrans tr = new(); // var tr = DBTrans.Top; @@ -61,8 +61,8 @@ public void Testifoxcommit() // 2. 有参数 // [AOP("file")] - // [CommandMethod("TESTAOP")] - // public void testaop() + // [CommandMethod(nameof(Test_AOP2))] + // public void TestAOP2() // { // // 不用 using var tr = new DBTrans(file); // var tr = DBTrans.Top; @@ -70,8 +70,8 @@ public void Testifoxcommit() // } - [CommandMethod("testpt")] - public void TestPt() + [CommandMethod(nameof(Test_Pt))] + public void Test_Pt() { // var pt = Env.Editor.GetPoint("pick pt:").Value; // var pl = Env.Editor.GetEntity("pick pl").ObjectId; diff --git a/tests/TestShared/testeditor.cs b/tests/TestShared/TestEditor.cs similarity index 87% rename from tests/TestShared/testeditor.cs rename to tests/TestShared/TestEditor.cs index e50c2a7..656ddaa 100644 --- a/tests/TestShared/testeditor.cs +++ b/tests/TestShared/TestEditor.cs @@ -1,12 +1,9 @@ -using Autodesk.AutoCAD.EditorInput; -using IFoxCAD.Cad; - -namespace Test; +namespace Test; public class Testeditor { - [CommandMethod("tested")] - public void Tested() + [CommandMethod(nameof(Test_Editor))] + public void Test_Editor() { var pts = new List { @@ -26,16 +23,16 @@ public void Tested() var s = ed.GetString("qustr"); Env.Editor.WriteMessage(""); } - [CommandMethod("testzoom")] - public void Testzoom() + [CommandMethod(nameof(Test_Zoom))] + public void Test_Zoom() { using DBTrans tr = new(); var res = Env.Editor.GetEntity("\npick ent:"); if (res.Status == PromptStatus.OK) Env.Editor.ZoomObject(res.ObjectId.GetObject()!); } - [CommandMethod("testzoomextent")] - public void Testzoomextent() + [CommandMethod(nameof(Test_ZoomExtents))] + public void Test_ZoomExtents() { // using DBTrans tr = new(); // var res = Env.Editor.GetEntity("\npick ent:"); @@ -47,8 +44,8 @@ public void Testzoomextent() Env.Editor.ZoomExtents(); } - [CommandMethod("testssget")] - public void Testssget() + [CommandMethod(nameof(Test_Ssget))] + public void Test_Ssget() { var action_a = () => { Env.Print("this is a"); }; var action_b = () => { Env.Print("this is b"); }; diff --git a/tests/TestShared/TestEnt.cs b/tests/TestShared/TestEnt.cs index 0cfdad3..78c9d8e 100644 --- a/tests/TestShared/TestEnt.cs +++ b/tests/TestShared/TestEnt.cs @@ -2,8 +2,8 @@ public class TestEnt { - [CommandMethod("TestEntRoration")] - public void TestEntRoration() + [CommandMethod(nameof(Test_EntRoration))] + public void Test_EntRoration() { var line = new Line(new(0, 0, 0), new(100, 0, 0)); @@ -15,8 +15,8 @@ public void TestEntRoration() } - [CommandMethod("Testtypespeed")] - public void TestTypeSpeed() + [CommandMethod(nameof(Test_TypeSpeed))] + public void Test_TypeSpeed() { var line = new Line(); var line1 = line as Entity; diff --git a/tests/TestShared/testenv.cs b/tests/TestShared/TestEnv.cs similarity index 47% rename from tests/TestShared/testenv.cs rename to tests/TestShared/TestEnv.cs index e86343e..dc2d1ce 100644 --- a/tests/TestShared/testenv.cs +++ b/tests/TestShared/TestEnv.cs @@ -2,40 +2,33 @@ namespace Test; public class Testenv { - [CommandMethod("testenum")] - public void Testenum() + [CommandMethod(nameof(Test_Enum))] + public void Test_Enum() { - Env.CmdEcho = true; - } - [CommandMethod("testenum1")] - public void Testenum1() + [CommandMethod(nameof(Test_Enum1))] + public void Test_Enum1() { - Env.CmdEcho = false; - } - [CommandMethod("testdimblk")] - public void Testdimblk() + [CommandMethod(nameof(Test_Dimblk))] + public void Test_Dimblk() { - Env.Dimblk = Env.DimblkType.Dot; Env.Dimblk = Env.DimblkType.Defult; Env.Dimblk = Env.DimblkType.Oblique; - } - [CommandMethod("testdimblk1")] - public void Testdimblk1() + [CommandMethod(nameof(Test_Dimblk1))] + public void Test_Dimblk1() { var dim = Env.Dimblk; Env.Editor.WriteMessage(dim.ToString()); - } - [CommandMethod("testosmode")] - public void Testosmode() + [CommandMethod(nameof(Test_Osmode))] + public void Test_Osmode() { // 设置osmode变量,多个值用逻辑或 Env.OSMode = Env.OSModeType.End | Env.OSModeType.Middle; @@ -49,16 +42,15 @@ public void Testosmode() Env.OSMode ^= Env.OSModeType.Center; Env.Editor.WriteMessage(Env.OSMode.ToString()); } - [CommandMethod("testosmode1")] - public void Testosmode1() + [CommandMethod(nameof(Test_Osmode1))] + public void Test_Osmode1() { var dim = Env.OSMode; Env.Editor.WriteMessage(dim.ToString()); - } - [CommandMethod("testcadver")] - public void Testcadver() + [CommandMethod(nameof(Test_Cadver))] + public void Test_Cadver() { // Env.Print(AcadVersion.Versions); AcadVersion.Versions.ForEach(v => Env.Print(v)); @@ -67,8 +59,8 @@ public void Testcadver() "1".Print(); } - [CommandMethod("TestGetVar")] - public void TestGetVar() + [CommandMethod(nameof(Test_GetVar))] + public void Test_GetVar() { // test getvar var a = Env.GetVar("dbmod"); @@ -76,12 +68,34 @@ public void TestGetVar() Env.SetVar("dbmod1", 1); } - [CommandMethod("TestDwgVersion")] - public void TestDwgVersion() + //[CommandMethod(nameof(Test_DwgVersion))] + //public void TestDwgVersion() + //{ + // string filename = @"C:\Users\vic\Desktop\test.dwg"; + // var a = Helper.GetCadFileVersion(filename); + // a.Print(); + // ((DwgVersion)a).Print(); + //} + + +#if !NET35 && !NET40 + // 通过此功能获取全部变量,尚不清楚此处如何设置,没有通过测试 + [CommandMethod(nameof(Test_GetvarAll))] + public static void Test_GetvarAll() + { + GetvarAll(); + } + + public static Dictionary GetvarAll() { - // string filename = @"C:\Users\vic\Desktop\test.dwg"; - // var a = Helper.GetCadFileVersion(filename); - // a.Print(); - // ((DwgVersion)a).Print(); + var dict = new Dictionary(); + var en = new SystemVariableEnumerator(); + while (en.MoveNext()) + { + Console.WriteLine(en.Current.Name + "-----" + en.Current.Value);// Value会出现异常 + dict.Add(en.Current.Name, en.Current.Value); + } + return dict; } -} +#endif +} \ No newline at end of file diff --git a/tests/TestShared/TestFileDatabase.cs b/tests/TestShared/TestFileDatabase.cs index cd3af21..e5c58c3 100644 --- a/tests/TestShared/TestFileDatabase.cs +++ b/tests/TestShared/TestFileDatabase.cs @@ -7,8 +7,8 @@ public class TestFileDatabase { - [CommandMethod("Test_FileDatabaseInit")] - public void TestDatabase() + [CommandMethod(nameof(Test_FileDatabaseInit))] + public void Test_FileDatabaseInit() { try { @@ -22,8 +22,7 @@ public void TestDatabase() } catch (System.Exception e) { - System.Windows.MessageBox.Show(e.Message); + System.Windows.MessageBox.Show(e.Message); } - } } \ No newline at end of file diff --git a/tests/TestShared/Testid.cs b/tests/TestShared/TestId.cs similarity index 86% rename from tests/TestShared/Testid.cs rename to tests/TestShared/TestId.cs index ca28148..57f19cf 100644 --- a/tests/TestShared/Testid.cs +++ b/tests/TestShared/TestId.cs @@ -2,8 +2,8 @@ public class Testid { - [CommandMethod("testid")] - public void TestId() + [CommandMethod(nameof(Test_Id))] + public void Test_Id() { using DBTrans tr = new(); Line line = new(new Point3d(0, 0, 0), new Point3d(1, 1, 0)); @@ -26,8 +26,8 @@ public void TestId() // } } - [CommandMethod("testmycommand")] - public void TestMyCommand() + [CommandMethod(nameof(Test_MyCommand))] + public void Test_MyCommand() { using DBTrans dbtrans = new(Env.Document, true, false); using var trans = Env.Database.TransactionManager.StartTransaction(); @@ -39,8 +39,8 @@ public void TestMyCommand() trans.Commit(); // dbtrans.Dispose(); } - [CommandMethod("testtextstyle")] - public void TestTextStyle() + [CommandMethod(nameof(Test_TextStyle))] + public void Test_TextStyle() { using DBTrans tr = new(); tr.TextStyleTable.Add("宋体", "宋体.ttf", 0.8); @@ -59,8 +59,8 @@ public void TestTextStyle() }); } - [CommandMethod("testtextstylechange")] - public void TestTextStyleChange() + [CommandMethod(nameof(Test_TextStyleChange))] + public void Test_TextStyleChange() { using DBTrans tr = new(); diff --git a/tests/TestShared/TestJig.cs b/tests/TestShared/TestJig.cs index 20e5ed3..291c056 100644 --- a/tests/TestShared/TestJig.cs +++ b/tests/TestShared/TestJig.cs @@ -5,8 +5,8 @@ public class Commands_Jig { // 已在数据库的图元如何进入jig - [CommandMethod("TestCmd_jig33")] - public static void TestCmd_jig33() + [CommandMethod(nameof(Test_Jig33))] + public static void Test_Jig33() { using DBTrans tr = new(); var per = tr.Editor?.GetEntity("\n点选圆形:"); @@ -45,8 +45,8 @@ public static void TestCmd_jig33() // 不在数据库的图元如何进入jig - [CommandMethod("TestCmd_Jig44")] - public void TestCmd_Jig44() + [CommandMethod(nameof(Test_Jig44))] + public void Test_Jig44() { using DBTrans tr = new(); var per = Env.Editor.GetEntity("\n请选择一条多段线:"); @@ -117,8 +117,8 @@ public void TestCmd_Jig44() tr.CurrentSpace.AddEntity(jig.Entitys); } - [CommandMethod("TestCmd_loop")] - public void TestCmd_loop() + [CommandMethod(nameof(Test_Loop))] + public void Test_Loop() { var dm = Acap.DocumentManager; var ed = dm.MdiActiveDocument.Editor; @@ -166,8 +166,8 @@ public bool PreFilterMessage(ref Message m) } - [CommandMethod("TestCmd_QuickText")] - static public void TestCmd_QuickText() + [CommandMethod(nameof(Test_QuickText))] + static public void Test_QuickText() { var dm = Acap.DocumentManager; var doc = dm.MdiActiveDocument; diff --git a/tests/TestShared/TestJigExTransient.cs b/tests/TestShared/TestJigExTransient.cs index 2ee546f..3761171 100644 --- a/tests/TestShared/TestJigExTransient.cs +++ b/tests/TestShared/TestJigExTransient.cs @@ -3,8 +3,8 @@ namespace Test; public partial class Test { - [CommandMethod(nameof(TestJigExTransient))] - public void TestJigExTransient() + [CommandMethod(nameof(Test_JigExTransient))] + public void Test_JigExTransient() { // 先取1点,建2个圆 var getpt = Env.Editor.GetPoint("\n选择点"); @@ -50,8 +50,8 @@ public void TestJigExTransient() // tr.CurrentSpace.AddEntity(jet.Entities); } - [CommandMethod(nameof(TestJigExTransentDim))] - public static void TestJigExTransentDim() + [CommandMethod(nameof(Test_JigExTransentDim))] + public static void Test_JigExTransentDim() { PromptPointOptions ppo = new("") { diff --git a/tests/TestShared/TestLisp.cs b/tests/TestShared/TestLisp.cs index 1a17bf0..91f9079 100644 --- a/tests/TestShared/TestLisp.cs +++ b/tests/TestShared/TestLisp.cs @@ -3,7 +3,7 @@ public class TestLisp { // 定义lisp函数 - [LispFunction("LispTest_RunLisp")] + [LispFunction(nameof(LispTest_RunLisp))] public static object LispTest_RunLisp(ResultBuffer rb) { CmdTest_RunLisp(); diff --git a/tests/TestShared/TestLoop.cs b/tests/TestShared/TestLoop.cs index f69e676..e68915d 100644 --- a/tests/TestShared/TestLoop.cs +++ b/tests/TestShared/TestLoop.cs @@ -1,12 +1,8 @@ namespace Test; - -using IFoxCAD.Basal; -using System.Diagnostics.CodeAnalysis; - public class TestLoop { - [CommandMethod("testloop")] - public void Testloop() + [CommandMethod(nameof(Test_Loop))] + public void Test_Loop() { var loop = new LoopList { @@ -23,8 +19,7 @@ public void Testloop() loop.SetFirst(loop.Last!); Env.Print(loop); Env.Print(loop.Min()); - loop.SetFirst(new LoopListNode (loop.Min() ,loop)); + loop.SetFirst(new LoopListNode(loop.Min(), loop)); Env.Print(loop); - } -} +} \ No newline at end of file diff --git a/tests/TestShared/TestMarshal.cs b/tests/TestShared/TestMarshal.cs index 5003f31..dd4e689 100644 --- a/tests/TestShared/TestMarshal.cs +++ b/tests/TestShared/TestMarshal.cs @@ -5,40 +5,39 @@ public class TestMarshal [CommandMethod(nameof(Test_Marshal))] public void Test_Marshal() { + var dm = Acap.DocumentManager; + var doc = dm.MdiActiveDocument; + var ed = doc.Editor; // 0x01 如何修改Point3d内容? - Point3d structObj = new(100, 50, 0); - "\n".Print(); - "原始:".Print(); - structObj.Print(); + Point3d pt = new(100, 50, 0); + ed.WriteMessage("\n原始:" + pt.ToString()); // 0x02 最佳方法: // 将Point3d内存转为Point3D,以此避开get保护,实现修改内部值 // 为了避免在安全类型中转换,多了栈帧(无法内联),直接用指针处理 unsafe { - var structPt = (Point3D*)&structObj;//必须强转成这个指针类型,不然它为(Point3d*) - structPt->X = 1569; + ((Point3D*)&pt)->X = 12345;//必须强转成这个指针类型,不然它为(Point3d*) } - "指针法:".Print(); - structObj.Print(); + ed.WriteMessage("\n指针法:" + pt.ToString()); // 0x03 此方法仍然需要不安全操作,而且多了几个函数调用... unsafe { - var p = (IntPtr)(IntPtr*)&structObj; + var p = (IntPtr)(IntPtr*)&pt; var result2 = (Point3D)Marshal.PtrToStructure(p, typeof(Point3D)); result2.X = 220; Marshal.StructureToPtr(result2, p, true); } "封送法:".Print(); - structObj.Print(); + pt.Print(); // 拷贝到数组,还原指针到结构,最后将内存空间转换为目标结构体 // 这不闹嘛~ - int typeSize = Marshal.SizeOf(structObj); + int typeSize = Marshal.SizeOf(pt); byte[] bytes = new byte[typeSize]; - IntPtr structPtr = Marshal.AllocHGlobal(Marshal.SizeOf(structObj)); - Marshal.StructureToPtr(structObj, structPtr, true); + IntPtr structPtr = Marshal.AllocHGlobal(Marshal.SizeOf(pt)); + Marshal.StructureToPtr(pt, structPtr, true); Marshal.Copy(structPtr, bytes, 0, typeSize); var result = (Point3d)Marshal.PtrToStructure(structPtr, typeof(Point3d)); "内存拷贝:".Print(); diff --git a/tests/TestShared/TestMirrorFile.cs b/tests/TestShared/TestMirrorFile.cs index c53e629..f27e0c4 100644 --- a/tests/TestShared/TestMirrorFile.cs +++ b/tests/TestShared/TestMirrorFile.cs @@ -9,7 +9,7 @@ public class MirrorFile /// 测试:后台打开图纸,镜像文字是否存在文字偏移 /// 答案:不存在 /// - [CommandMethod("CmdTest_MirrorFile")] + [CommandMethod(nameof(CmdTest_MirrorFile))] public static void CmdTest_MirrorFile() { using DBTrans tr = new(file, openMode: FileOpenMode.OpenForReadAndReadShare); @@ -38,7 +38,7 @@ public static void CmdTest_MirrorFile() /// 测试:后台设置 dbText.IsMirroredInX 属性会令文字偏移 /// 答案:存在,并提出解决方案 ///
    - [CommandMethod("CmdTest_MirrorFile2")] + [CommandMethod(nameof(CmdTest_MirrorFile2))] public static void CmdTest_MirrorFile2() { using DBTrans tr = new(file); diff --git a/tests/TestShared/TestPoint.cs b/tests/TestShared/TestPoint.cs index 688f09f..5a75808 100644 --- a/tests/TestShared/TestPoint.cs +++ b/tests/TestShared/TestPoint.cs @@ -9,8 +9,8 @@ public class TestPoint /// /// 红黑树排序点集 /// - [CommandMethod("TestptSortedSet")] - public void TestptSortedSet() + [CommandMethod(nameof(Test_PtSortedSet))] + public void Test_PtSortedSet() { var ss1 = new SortedSet { @@ -37,8 +37,8 @@ public void TestptSortedSet() - [CommandMethod("TestptGethash")] - public void TestptGethash() + [CommandMethod(nameof(Test_PtGethash))] + public void Test_PtGethash() { // test var pt = Env.Editor.GetPoint("pick pt").Value; @@ -50,8 +50,8 @@ public void TestptGethash() }); } - [CommandMethod("Testpoint3d")] - public void TestPoint3d() + [CommandMethod(nameof(Test_Point3d))] + public void Test_Point3d() { Env.Print($"4位小数的hash:{new Point3d(0.0_001, 0.0_002, 0.0).GetHashCode()}"); Env.Print($"5位小数的hash:{new Point3d(0.00_001, 0.00_002, 0.0).GetHashCode()}"); @@ -93,22 +93,22 @@ public void TestPoint3d() Env.Print($"20位小数的hash:{new Point3d(0.000_000_000_000_000_000_01, 0.000_000_000_000_000_000_01, 0.0).GetHashCode()}"); } - [CommandMethod("Testlistequalspeed")] - public void Testlistequalspeed() + [CommandMethod(nameof(Test_Listequalspeed))] + public void Test_Listequalspeed() { var lst1 = new List { 1, 2, 3, 4 }; - var lst2 = new List { 1, 2, 3, 4}; + var lst2 = new List { 1, 2, 3, 4 }; lst1.EqualsAll(null!); Tools.TestTimes2(1000000, "eqaulspeed:", () => { lst1.EqualsAll(lst2); }); } - [CommandMethod("Testcontains")] - public void Testcontains() + [CommandMethod(nameof(Test_Contains))] + public void Test_Contains() { // test list and dict contains speed - var lst = new List { 1, 2, 3, 4 , 5,6,7,8,9,10, 11,12,13,14,15,16,17,18,19,20}; + var lst = new List { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20 }; var hashset = new HashSet { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20 }; var dict = new Dictionary { @@ -145,6 +145,5 @@ public void Testcontains() Tools.TestTimes2(100_0000, "dict:", () => { dict.ContainsKey(20); }); - } } \ No newline at end of file diff --git a/tests/TestShared/TestQuadTree.cs b/tests/TestShared/TestQuadTree.cs index 9a310e7..4c129ee 100644 --- a/tests/TestShared/TestQuadTree.cs +++ b/tests/TestShared/TestQuadTree.cs @@ -38,7 +38,7 @@ public partial class TestQuadTree { QuadTree? _quadTreeRoot; #region 四叉树创建并加入 - [CommandMethod("Test_QuadTree")] + [CommandMethod(nameof(Test_QuadTree))] public void Test_QuadTree() { using DBTrans tr = new(); @@ -147,7 +147,7 @@ public void Test_QuadTree() #if true2 // 选择加入到四叉树 - [CommandMethod("CmdTest_QuadTree21")] + [CommandMethod(nameof(CmdTest_QuadTree21))] public void CmdTest_QuadTree21() { var dm = Acap.DocumentManager; @@ -164,7 +164,7 @@ public void CmdTest_QuadTree21() } // 自动加入全图到四叉树 - [CommandMethod("CmdTest_QuadTree20")] + [CommandMethod(nameof(CmdTest_QuadTree20))] public void CmdTest_QuadTree20() { var dm = Acap.DocumentManager; @@ -262,7 +262,7 @@ void AddQuadTreeRoot(Database db, Editor ed, List ss) #region 节点边界显示 // 四叉树减去节点 - [CommandMethod("CmdTest_QuadTree0")] + [CommandMethod(nameof(CmdTest_QuadTree0))] public void CmdTest_QuadTree0() { var dm = Acap.DocumentManager; @@ -283,7 +283,7 @@ public void CmdTest_QuadTree0() } // 创建节点边界 - [CommandMethod("CmdTest_QuadTree00")] + [CommandMethod(nameof(CmdTest_CreateNodesRect))] public void CmdTest_CreateNodesRect() { var dm = Acap.DocumentManager; @@ -335,13 +335,13 @@ public void CmdTest_CreateNodesRect() #region 四叉树查询节点 // 选择范围改图元颜色 - [CommandMethod("CmdTest_QuadTree3")] + [CommandMethod(nameof(CmdTest_QuadTree3))] public void CmdTest_QuadTree3() { Ssget(QuadTreeSelectMode.IntersectsWith); } - [CommandMethod("CmdTest_QuadTree4")] + [CommandMethod(nameof(CmdTest_QuadTree4))] public void CmdTest_QuadTree4() { Ssget(QuadTreeSelectMode.Contains); @@ -380,7 +380,6 @@ void Ssget(QuadTreeSelectMode mode) ///
    /// /// -#pragma warning disable CS8632 // 只能在 "#nullable" 注释上下文内的代码中使用可为 null 的引用类型的注释。 public static Rect? GetCorner(Editor ed) { var optionsA = new PromptPointOptions($"{Environment.NewLine}起点位置:"); @@ -400,8 +399,6 @@ void Ssget(QuadTreeSelectMode mode) new Point2d(pprB.Value.X, pprB.Value.Y), true); } -#pragma warning restore CS8632 // 只能在 "#nullable" 注释上下文内的代码中使用可为 null 的引用类型的注释。 - #endregion } diff --git a/tests/TestShared/testselectfilter.cs b/tests/TestShared/TestSelectfilter.cs similarity index 74% rename from tests/TestShared/testselectfilter.cs rename to tests/TestShared/TestSelectfilter.cs index dab975c..ff47d00 100644 --- a/tests/TestShared/testselectfilter.cs +++ b/tests/TestShared/TestSelectfilter.cs @@ -2,16 +2,15 @@ namespace Test; public class Testselectfilter { - [CommandMethod("testfilter")] - public void Testfilter() + [CommandMethod(nameof(Test_Filter))] + public void Test_Filter() { - var p = new Point3d(10, 10, 0); var f = OpFilter.Build( - e =>!(e.Dxf(0) == "line" & e.Dxf(8) == "0") + e => !(e.Dxf(0) == "line" & e.Dxf(8) == "0") | e.Dxf(0) != "circle" & e.Dxf(8) == "2" & e.Dxf(10) >= p); - - + + var f2 = OpFilter.Build( e => e.Or( !e.And(e.Dxf(0) == "line", e.Dxf(8) == "0"), @@ -24,10 +23,10 @@ public void Testfilter() Env.Editor.WriteMessage(""); } - [CommandMethod("testselectanpoint")] - public void Testselectanpoint() + [CommandMethod(nameof(Test_Selectanpoint))] + public void Test_Selectanpoint() { var sel2 = Env.Editor.SelectAtPoint(new Point3d(0, 0, 0)); Env.Editor.WriteMessage(""); } -} +} \ No newline at end of file diff --git a/tests/TestShared/TestShared.projitems b/tests/TestShared/TestShared.projitems index bb84b7a..914b775 100644 --- a/tests/TestShared/TestShared.projitems +++ b/tests/TestShared/TestShared.projitems @@ -9,15 +9,15 @@ TestShared - - + + @@ -33,5 +33,6 @@ + \ No newline at end of file diff --git a/tests/TestShared/XrefExTest.cs b/tests/TestShared/TestXrefEx.cs similarity index 100% rename from tests/TestShared/XrefExTest.cs rename to tests/TestShared/TestXrefEx.cs -- Gitee From 132b474dbe90cedf59d0ddb2d4ad7bb68a3ee253 Mon Sep 17 00:00:00 2001 From: liuqihong <540762622@qq.com> Date: Wed, 12 Oct 2022 00:24:33 +0800 Subject: [PATCH 555/675] =?UTF-8?q?=E5=A4=84=E7=90=86=E9=87=8D=E5=A4=8D?= =?UTF-8?q?=E5=91=BD=E4=BB=A4=E5=BC=95=E8=B5=B7=E7=9A=84=E5=8A=A0=E8=BD=BD?= =?UTF-8?q?=E9=94=99=E8=AF=AF?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/IFoxCAD.Cad.Shared/Runtime/IAutoGo.cs | 31 ++++++++++++++++ tests/TestShared/CmdINI.cs | 8 +++++ .../TestShared/{Test.cs => TestAddEntity.cs} | 35 +++++++++++++++++-- tests/TestShared/TestDBTrans.cs | 4 +-- tests/TestShared/TestEnt.cs | 35 ------------------- tests/TestShared/TestJig.cs | 4 +-- tests/TestShared/TestLoop.cs | 4 +-- tests/TestShared/TestPoint.cs | 8 ++--- tests/TestShared/TestShared.projitems | 3 +- 9 files changed, 82 insertions(+), 50 deletions(-) rename tests/TestShared/{Test.cs => TestAddEntity.cs} (92%) delete mode 100644 tests/TestShared/TestEnt.cs diff --git a/src/IFoxCAD.Cad.Shared/Runtime/IAutoGo.cs b/src/IFoxCAD.Cad.Shared/Runtime/IAutoGo.cs index c52e7c9..b035381 100644 --- a/src/IFoxCAD.Cad.Shared/Runtime/IAutoGo.cs +++ b/src/IFoxCAD.Cad.Shared/Runtime/IAutoGo.cs @@ -308,4 +308,35 @@ static void RunFunctions(List runClassList) runClassList[i].Run(); runClassList.Clear(); } + +#if Debug + /// + /// 检查当前程序域重复出现命令, + /// 当出现重复时候将引起断点 + /// + public static void DebugCheckCmdRecurrence() + { + HashSet keys = new HashSet(); + AutoReflection.AppDomainGetTypes(type => { + if (type.IsAbstract) + return; + + var mets = type.GetMethods(); + for (int ii = 0; ii < mets.Length; ii++) + { + var method = mets[ii]; + var attr = method.GetCustomAttributes(true); + for (int jj = 0; jj < attr.Length; jj++) + { + if (attr[jj] is CommandMethodAttribute att) + { + if (keys.Contains(att.GlobalName)) + Debugger.Break(); + keys.Add(att.GlobalName); + } + } + } + }); + } +#endif } \ No newline at end of file diff --git a/tests/TestShared/CmdINI.cs b/tests/TestShared/CmdINI.cs index 2a259f5..b2ea1b0 100644 --- a/tests/TestShared/CmdINI.cs +++ b/tests/TestShared/CmdINI.cs @@ -1,3 +1,6 @@ +using System.Diagnostics; +using System.Linq; + namespace Test; /// @@ -30,6 +33,11 @@ public AutoRegAssemEx() : base(AutoRegConfig.All) ed.WriteMessage($"\n {nameof(AutoRegAssemEx)}构造函数,开始自动执行\r\n"); CmdInit.AutoRegAssemEx = this; + +#if Debug + // 此处用来反射本程序集,检查是否存在重复命令 + AutoReflection.DebugCheckCmdRecurrence(); +#endif } } diff --git a/tests/TestShared/Test.cs b/tests/TestShared/TestAddEntity.cs similarity index 92% rename from tests/TestShared/Test.cs rename to tests/TestShared/TestAddEntity.cs index b5ab81a..721aea8 100644 --- a/tests/TestShared/Test.cs +++ b/tests/TestShared/TestAddEntity.cs @@ -1,5 +1,4 @@ namespace Test; -#pragma warning disable CS0219 // 变量已被赋值,但从未使用过它的值 public partial class Test { @@ -354,5 +353,35 @@ public Document Getdoc() var doc = Acap.DocumentManager.MdiActiveDocument; return doc; } -} -#pragma warning restore CS0219 // 变量已被赋值,但从未使用过它的值 \ No newline at end of file + + + [CommandMethod(nameof(Test_EntRoration))] + public void Test_EntRoration() + { + var line = new Line(new(0, 0, 0), new(100, 0, 0)); + + using DBTrans tr = new(); + tr.CurrentSpace.AddEntity(line); + var line2 = (Line)line.Clone(); + tr.CurrentSpace.AddEntity(line2); + line2.Rotation(new(100, 0, 0), Math.PI / 2); + } + + [CommandMethod(nameof(Test_TypeSpeed))] + public void Test_TypeSpeed() + { + var line = new Line(); + var line1 = line as Entity; + Tools.TestTimes(100000, "is 匹配:", () => { + var t = line1 is Line; + }); + Tools.TestTimes(100000, "name 匹配:", () => { + // var t = line.GetType().Name; + var tt = line1.GetType().Name == nameof(Line); + }); + Tools.TestTimes(100000, "dxfname 匹配:", () => { + // var t = line.GetType().Name; + var tt = line1.GetRXClass().DxfName == nameof(Line); + }); + } +} \ No newline at end of file diff --git a/tests/TestShared/TestDBTrans.cs b/tests/TestShared/TestDBTrans.cs index bc11e99..005f45f 100644 --- a/tests/TestShared/TestDBTrans.cs +++ b/tests/TestShared/TestDBTrans.cs @@ -70,8 +70,8 @@ public void Test_DBTransAbort() // } - [CommandMethod(nameof(Test_Pt))] - public void Test_Pt() + [CommandMethod(nameof(Test_TopTransaction))] + public void Test_TopTransaction() { // var pt = Env.Editor.GetPoint("pick pt:").Value; // var pl = Env.Editor.GetEntity("pick pl").ObjectId; diff --git a/tests/TestShared/TestEnt.cs b/tests/TestShared/TestEnt.cs deleted file mode 100644 index 78c9d8e..0000000 --- a/tests/TestShared/TestEnt.cs +++ /dev/null @@ -1,35 +0,0 @@ -namespace Test; - -public class TestEnt -{ - [CommandMethod(nameof(Test_EntRoration))] - public void Test_EntRoration() - { - var line = new Line(new(0, 0, 0), new(100, 0, 0)); - - using DBTrans tr = new(); - tr.CurrentSpace.AddEntity(line); - var line2 = (Line)line.Clone(); - tr.CurrentSpace.AddEntity(line2); - line2.Rotation(new(100, 0, 0), Math.PI / 2); - } - - - [CommandMethod(nameof(Test_TypeSpeed))] - public void Test_TypeSpeed() - { - var line = new Line(); - var line1 = line as Entity; - Tools.TestTimes(100000, "is 匹配:", () => { - var t = line1 is Line; - }); - Tools.TestTimes(100000, "name 匹配:", () => { - // var t = line.GetType().Name; - var tt = line1.GetType().Name == nameof(Line); - }); - Tools.TestTimes(100000, "dxfname 匹配:", () => { - // var t = line.GetType().Name; - var tt = line1.GetRXClass().DxfName == nameof(Line); - }); - } -} \ No newline at end of file diff --git a/tests/TestShared/TestJig.cs b/tests/TestShared/TestJig.cs index 291c056..6db1f38 100644 --- a/tests/TestShared/TestJig.cs +++ b/tests/TestShared/TestJig.cs @@ -117,8 +117,8 @@ public void Test_Jig44() tr.CurrentSpace.AddEntity(jig.Entitys); } - [CommandMethod(nameof(Test_Loop))] - public void Test_Loop() + [CommandMethod(nameof(Test_MessageFilter))] + public void Test_MessageFilter() { var dm = Acap.DocumentManager; var ed = dm.MdiActiveDocument.Editor; diff --git a/tests/TestShared/TestLoop.cs b/tests/TestShared/TestLoop.cs index e68915d..438253a 100644 --- a/tests/TestShared/TestLoop.cs +++ b/tests/TestShared/TestLoop.cs @@ -1,8 +1,8 @@ namespace Test; public class TestLoop { - [CommandMethod(nameof(Test_Loop))] - public void Test_Loop() + [CommandMethod(nameof(Test_LoopList))] + public void Test_LoopList() { var loop = new LoopList { diff --git a/tests/TestShared/TestPoint.cs b/tests/TestShared/TestPoint.cs index 5a75808..72c0adb 100644 --- a/tests/TestShared/TestPoint.cs +++ b/tests/TestShared/TestPoint.cs @@ -50,8 +50,8 @@ public void Test_PtGethash() }); } - [CommandMethod(nameof(Test_Point3d))] - public void Test_Point3d() + [CommandMethod(nameof(Test_Point3dHash))] + public void Test_Point3dHash() { Env.Print($"4位小数的hash:{new Point3d(0.0_001, 0.0_002, 0.0).GetHashCode()}"); Env.Print($"5位小数的hash:{new Point3d(0.00_001, 0.00_002, 0.0).GetHashCode()}"); @@ -93,8 +93,8 @@ public void Test_Point3d() Env.Print($"20位小数的hash:{new Point3d(0.000_000_000_000_000_000_01, 0.000_000_000_000_000_000_01, 0.0).GetHashCode()}"); } - [CommandMethod(nameof(Test_Listequalspeed))] - public void Test_Listequalspeed() + [CommandMethod(nameof(Test_ListEqualspeed))] + public void Test_ListEqualspeed() { var lst1 = new List { 1, 2, 3, 4 }; var lst2 = new List { 1, 2, 3, 4 }; diff --git a/tests/TestShared/TestShared.projitems b/tests/TestShared/TestShared.projitems index 914b775..9e3d265 100644 --- a/tests/TestShared/TestShared.projitems +++ b/tests/TestShared/TestShared.projitems @@ -11,7 +11,7 @@ - + @@ -19,7 +19,6 @@ - -- Gitee From 23a316d78345c9ef5bdecc373361beae785bd04f Mon Sep 17 00:00:00 2001 From: liuqihong <540762622@qq.com> Date: Thu, 13 Oct 2022 08:58:31 +0800 Subject: [PATCH 556/675] =?UTF-8?q?=E4=BC=98=E5=8C=96=E4=BB=A3=E7=A0=81=20?= =?UTF-8?q?=E8=AE=BE=E7=BD=AE=E6=96=AD=E7=82=B9=E4=B8=8D=E8=BF=9B=E5=85=A5?= =?UTF-8?q?=E6=9F=90=E4=BA=9B=E5=BE=AA=E7=8E=AF=E5=86=85=E9=83=A8,?= =?UTF-8?q?=E5=A7=94=E6=89=98=E4=BE=9D=E7=84=B6=E5=8F=AF=E4=BB=A5=E8=BF=9B?= =?UTF-8?q?=E5=85=A5=E6=96=AD=E7=82=B9=20=E5=A2=9E=E5=8A=A0=E4=B8=8D?= =?UTF-8?q?=E5=AE=89=E5=85=A8=E7=B1=BB=E5=9E=8B=E6=B5=8B=E8=AF=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/IFoxCAD.Basal/General/ArrayEx.cs | 6 +- src/IFoxCAD.Cad.Shared/Copyclip/EmfTool.cs | 4 +- .../Copyclip/TagClipboardInfo.cs | 20 ++++--- .../ExtensionMethod/CollectionEx.cs | 16 +++++- .../ExtensionMethod/DBDictionaryEx.cs | 3 +- .../ExtensionMethod/EntityEx.cs | 2 + .../ExtensionMethod/GeometryEx.cs | 17 +++--- .../ExtensionMethod/ObjectIdEx.cs | 2 +- .../ExtensionMethod/PointEx.cs | 14 +++-- .../ExtensionMethod/SelectionSetEx.cs | 7 +++ .../ExtensionMethod/Tools.cs | 57 ++++++++++--------- .../SelectionFilter/OpLogi.cs | 3 +- .../IFoxCAD.Aacad08/IFoxCAD.Acad08.csproj | 1 + .../IFoxCAD.Acad09plus.csproj | 1 + .../IFoxCAD.Gcad/IFoxCAD.Gcad.csproj | 1 + .../IFoxCAD.Zcad/IFoxCAD.Zcad.csproj | 1 + src/IFoxCAD.LoadForm/AssemblyDependent.cs | 12 ++-- tests/TestShared/CmdINI.cs | 18 ------ tests/TestShared/TestEnv.cs | 15 +++++ tests/TestShared/TestMarshal.cs | 49 +++++++++++++++- tests/TestShared/TestShared.projitems | 10 ++-- 21 files changed, 167 insertions(+), 92 deletions(-) diff --git a/src/IFoxCAD.Basal/General/ArrayEx.cs b/src/IFoxCAD.Basal/General/ArrayEx.cs index 21947f6..442f453 100644 --- a/src/IFoxCAD.Basal/General/ArrayEx.cs +++ b/src/IFoxCAD.Basal/General/ArrayEx.cs @@ -1,6 +1,6 @@ namespace IFoxCAD.Basal; -/* +/* * 由于linq的函数大部分带有状态机,而cad是一个单机程序, * 使用状态机会变得缓慢,因此我们设计的时候着重于时间优化, * 本工具类在着重于数组遍历时候替代linq @@ -37,6 +37,7 @@ public static T[] Combine2(this T[] a, T[] b) /// 传出参数2:数组结尾
    /// 返回值比较结尾为就移除
    /// + [System.Diagnostics.DebuggerStepThrough] public static void Deduplication(List lst, Func func) { // 头和尾比较,满足条件移除尾巴 @@ -51,5 +52,4 @@ public static void Deduplication(List lst, Func func) } } } - -} +} \ No newline at end of file diff --git a/src/IFoxCAD.Cad.Shared/Copyclip/EmfTool.cs b/src/IFoxCAD.Cad.Shared/Copyclip/EmfTool.cs index 12e2df1..267a1bb 100644 --- a/src/IFoxCAD.Cad.Shared/Copyclip/EmfTool.cs +++ b/src/IFoxCAD.Cad.Shared/Copyclip/EmfTool.cs @@ -322,8 +322,8 @@ public static void Save(IntPtr clipTypeData, string file) ///
    /// 文件句柄 /// 描述的内容 - [System.Diagnostics.DebuggerStepThrough()] - //[System.CodeDom.Compiler.GeneratedCode("InteropSignatureToolkit", "0.9 Beta1")] + [System.Diagnostics.DebuggerStepThrough] + [System.CodeDom.Compiler.GeneratedCode("InteropSignatureToolkit", "0.9 Beta1")]//初始化时指定生成代码的工具的名称和版本 public static string? GetEnhMetaFileDescriptionEx(IntPtr clipTypeData) { var len = GetEnhMetaFileDescription(clipTypeData, 0, null!); diff --git a/src/IFoxCAD.Cad.Shared/Copyclip/TagClipboardInfo.cs b/src/IFoxCAD.Cad.Shared/Copyclip/TagClipboardInfo.cs index d4e7400..10d912e 100644 --- a/src/IFoxCAD.Cad.Shared/Copyclip/TagClipboardInfo.cs +++ b/src/IFoxCAD.Cad.Shared/Copyclip/TagClipboardInfo.cs @@ -2,13 +2,7 @@ using System; using System.Diagnostics; -using System.Drawing.Imaging; using System.Text; -using System.Threading; -using System.Windows; -using System.Windows.Documents; -using System.Windows.Media.Media3D; -using System.Xml.Linq; public class ClipboardEnv { @@ -197,7 +191,7 @@ public override int GetHashCode() #endregion } - +[ComVisible(true)] [Serializable] [StructLayout(LayoutKind.Sequential)] [DebuggerDisplay("{DebuggerDisplay,nq}")] @@ -264,10 +258,16 @@ public IntRect Clone() #endregion } - +[ComVisible(true)] +[Serializable] +[DebuggerDisplay("{DebuggerDisplay,nq}")] +[DebuggerTypeProxy(typeof(IntSize))] [StructLayout(LayoutKind.Sequential)] public struct IntSize { + [DebuggerBrowsable(DebuggerBrowsableState.Never)] + private string DebuggerDisplay => $"(Hight:{Hight},Width:{Width})"; + public int Hight; public int Width; @@ -283,6 +283,7 @@ public override string ToString() } } +[ComVisible(true)] [Serializable] [DebuggerDisplay("{DebuggerDisplay,nq}")] [DebuggerTypeProxy(typeof(Point3D))] @@ -310,7 +311,6 @@ public static implicit operator Point3d(Point3D pt) { return new Point3d(pt.X, pt.Y, pt.Z); } - public override string ToString() { return $"({X},{Y},{Z})"; @@ -453,6 +453,7 @@ public partial class ClipTool /// true写入,false读取 /// /// + [System.Diagnostics.DebuggerStepThrough] public static bool OpenClipboardTask(bool isWrite, Action action) { if (action == null) @@ -485,6 +486,7 @@ public static bool OpenClipboardTask(bool isWrite, Action action) /// 获取剪贴板 ///
    /// 剪贴板的索引名 + /// 返回的结构 public static bool GetClipboard(string clipKey, out T? tag) { bool locked = false; diff --git a/src/IFoxCAD.Cad.Shared/ExtensionMethod/CollectionEx.cs b/src/IFoxCAD.Cad.Shared/ExtensionMethod/CollectionEx.cs index bfd49f8..b10187b 100644 --- a/src/IFoxCAD.Cad.Shared/ExtensionMethod/CollectionEx.cs +++ b/src/IFoxCAD.Cad.Shared/ExtensionMethod/CollectionEx.cs @@ -13,6 +13,7 @@ public static class CollectionEx ///
    /// 对象id的迭代器 /// 对象id集合,记得释放 + [System.Diagnostics.DebuggerStepThrough] public static ObjectIdCollection ToCollection(this IEnumerable ids) { return new ObjectIdCollection(ids.ToArray()); @@ -24,6 +25,7 @@ public static ObjectIdCollection ToCollection(this IEnumerable ids) /// 对象类型 /// 实体对象的迭代器 /// 实体集合,记得释放 + [System.Diagnostics.DebuggerStepThrough] public static DBObjectCollection ToCollection(this IEnumerable objs) where T : DBObject { DBObjectCollection objCol = new(); @@ -37,6 +39,7 @@ public static DBObjectCollection ToCollection(this IEnumerable objs) where ///
    /// double 数值迭代器 /// 数值集合,它没有Dispose + [System.Diagnostics.DebuggerStepThrough] public static DoubleCollection ToCollection(this IEnumerable doubles) { return new DoubleCollection(doubles.ToArray()); @@ -47,6 +50,7 @@ public static DoubleCollection ToCollection(this IEnumerable doubles) ///
    /// 二维点迭代器 /// 二维点集合,!acad记得释放 + [System.Diagnostics.DebuggerStepThrough] public static Point2dCollection ToCollection(this IEnumerable pts) { return new Point2dCollection(pts.ToArray()); @@ -57,6 +61,7 @@ public static Point2dCollection ToCollection(this IEnumerable pts) ///
    /// 三维点迭代器 /// 三维点集合,记得释放 + [System.Diagnostics.DebuggerStepThrough] public static Point3dCollection ToCollection(this IEnumerable pts) { return new Point3dCollection(pts.ToArray()); @@ -67,6 +72,7 @@ public static Point3dCollection ToCollection(this IEnumerable pts) ///
    /// 对象id集合 /// 对象id列表 + [System.Diagnostics.DebuggerStepThrough] public static List ToList(this ObjectIdCollection ids) { return ids.Cast().ToList(); @@ -79,6 +85,7 @@ public static List ToList(this ObjectIdCollection ids) /// 集合值的类型 /// 集合 /// 委托 + [System.Diagnostics.DebuggerStepThrough] public static void ForEach(this IEnumerable source, Action action) { if (action == null) @@ -94,6 +101,7 @@ public static void ForEach(this IEnumerable source, Action action) /// 集合值的类型 /// 集合 /// 委托 + [System.Diagnostics.DebuggerStepThrough] public static void ForEach(this IEnumerable source, Action action) { if (action == null) @@ -113,6 +121,7 @@ public static void ForEach(this IEnumerable source, Action action) /// 集合值的类型 /// 集合 /// 委托 + [System.Diagnostics.DebuggerStepThrough] public static void ForEach(this IEnumerable source, Action action) { if (action == null) @@ -145,6 +154,7 @@ public enum KeywordName /// 关键字 /// 关键字容器字段名 /// true含有 + [System.Diagnostics.DebuggerStepThrough] public static bool Contains(this KeywordCollection collection, string name, KeywordName keywordName = KeywordName.GlobalName) { @@ -196,8 +206,6 @@ public static bool Contains(this KeywordCollection collection, string name, } } break; - default: - break; } return contains; } @@ -208,6 +216,7 @@ public static bool Contains(this KeywordCollection collection, string name, ///
    /// /// + [System.Diagnostics.DebuggerStepThrough] public static Dictionary GetDict(this KeywordCollection collection) { Dictionary map = new(); @@ -231,6 +240,7 @@ public static Dictionary GetDict(this KeywordCollection collecti ///
    /// /// + [System.Diagnostics.DebuggerStepThrough] public static List GetKeys(this IdMapping idmap) { List ids = new(); @@ -244,6 +254,7 @@ public static List GetKeys(this IdMapping idmap) ///
    /// /// + [System.Diagnostics.DebuggerStepThrough] public static List GetValues(this IdMapping idmap) { List ids = new(); @@ -257,6 +268,7 @@ public static List GetValues(this IdMapping idmap) ///
    /// /// + [System.Diagnostics.DebuggerStepThrough] public static Dictionary ToDictionary(this IdMapping mapping) { var keyValuePairs = new Dictionary(); diff --git a/src/IFoxCAD.Cad.Shared/ExtensionMethod/DBDictionaryEx.cs b/src/IFoxCAD.Cad.Shared/ExtensionMethod/DBDictionaryEx.cs index f7cb3e3..1cd3845 100644 --- a/src/IFoxCAD.Cad.Shared/ExtensionMethod/DBDictionaryEx.cs +++ b/src/IFoxCAD.Cad.Shared/ExtensionMethod/DBDictionaryEx.cs @@ -12,6 +12,7 @@ public static class DBDictionaryEx /// 字典 /// 事务 /// 对象迭代器 + [System.Diagnostics.DebuggerStepThrough] public static IEnumerable GetAllObjects(this DBDictionary dict, DBTrans? trans = null) where T : DBObject { trans ??= DBTrans.Top; @@ -103,9 +104,7 @@ public static void SetXRecord(this DBDictionary dict, string key, XRecordDataLis if (id.IsNull) { using (obj.ForWrite()) - { obj.CreateExtensionDictionary(); - } id = obj.ExtensionDictionary; } diff --git a/src/IFoxCAD.Cad.Shared/ExtensionMethod/EntityEx.cs b/src/IFoxCAD.Cad.Shared/ExtensionMethod/EntityEx.cs index 9d5cbb3..092edd3 100644 --- a/src/IFoxCAD.Cad.Shared/ExtensionMethod/EntityEx.cs +++ b/src/IFoxCAD.Cad.Shared/ExtensionMethod/EntityEx.cs @@ -7,6 +7,8 @@ public static class EntityEx { #region 实体刷新 + // 此处已经被RedrawEx代替 + /// /// 刷新实体显示 /// diff --git a/src/IFoxCAD.Cad.Shared/ExtensionMethod/GeometryEx.cs b/src/IFoxCAD.Cad.Shared/ExtensionMethod/GeometryEx.cs index 58749b2..b9750ef 100644 --- a/src/IFoxCAD.Cad.Shared/ExtensionMethod/GeometryEx.cs +++ b/src/IFoxCAD.Cad.Shared/ExtensionMethod/GeometryEx.cs @@ -7,7 +7,6 @@ namespace IFoxCAD.Cad; ///
    public static class GeometryEx { - #region Point&Circle /// @@ -345,21 +344,21 @@ public static OrientationType IsClockWise(this IEnumerable pnts) switch (pnts.Count) { case 0: - ptlst = null; - return null; + ptlst = null; + return null; case 1: - ptlst = new LoopList { pnts[0] }; - return new CircularArc2d(pnts[0], 0); + ptlst = new LoopList { pnts[0] }; + return new CircularArc2d(pnts[0], 0); case 2: - return GetMinCircle(pnts[0], pnts[1], out ptlst); + return GetMinCircle(pnts[0], pnts[1], out ptlst); case 3: - return GetMinCircle(pnts[0], pnts[1], pnts[2], out ptlst); + return GetMinCircle(pnts[0], pnts[1], pnts[2], out ptlst); case 4: - return GetMinCircle(pnts[0], pnts[1], pnts[2], pnts[3], out ptlst); + return GetMinCircle(pnts[0], pnts[1], pnts[2], pnts[3], out ptlst); } // 按前三点计算最小包围圆 @@ -674,4 +673,4 @@ public static Point2d Polar(this Point2d pt, double ang, double len) { return pt + Vector2d.XAxis.RotateBy(ang) * len; } -} +} \ No newline at end of file diff --git a/src/IFoxCAD.Cad.Shared/ExtensionMethod/ObjectIdEx.cs b/src/IFoxCAD.Cad.Shared/ExtensionMethod/ObjectIdEx.cs index d4efe95..a84bd7c 100644 --- a/src/IFoxCAD.Cad.Shared/ExtensionMethod/ObjectIdEx.cs +++ b/src/IFoxCAD.Cad.Shared/ExtensionMethod/ObjectIdEx.cs @@ -37,6 +37,7 @@ public static class ObjectIdEx /// 是否打开已删除对象,默认为不打开 /// 是否打开锁定图层对象,默认为不打开 /// 指定类型对象集合 + [System.Diagnostics.DebuggerStepThrough] public static IEnumerable GetObject(this IEnumerable ids, OpenMode mode = OpenMode.ForRead, Transaction? tr = default, @@ -59,7 +60,6 @@ public static IEnumerable OfType(this IEnumerable ids) wh } #endregion GetObject - // Acad08缺少 id.ObjectClass 如何补偿? public static RXClass ObjectClass(this ObjectId id) { #if NET35 diff --git a/src/IFoxCAD.Cad.Shared/ExtensionMethod/PointEx.cs b/src/IFoxCAD.Cad.Shared/ExtensionMethod/PointEx.cs index 6f02e77..99187e8 100644 --- a/src/IFoxCAD.Cad.Shared/ExtensionMethod/PointEx.cs +++ b/src/IFoxCAD.Cad.Shared/ExtensionMethod/PointEx.cs @@ -20,8 +20,8 @@ public static string GetHashString(this Point3d pt, int xyz = 3, int decimalReta }; } - // 为了频繁触发所以弄个全局变量 - static Plane? _Plane; + // 为了频繁触发所以弄个缓存 + static Plane? _PlaneCache; /// /// 两点计算弧度范围0到2Pi /// @@ -32,10 +32,10 @@ public static string GetHashString(this Point3d pt, int xyz = 3, int decimalReta public static double GetAngle(this Point3d startPoint, Point3d endPoint, Vector3d? direction = null) { if (direction != null) - _Plane = new Plane(Point3d.Origin, direction.Value); - if (_Plane == null) - _Plane = new Plane(Point3d.Origin, Vector3d.ZAxis); - return startPoint.GetVectorTo(endPoint).AngleOnPlane(_Plane); + _PlaneCache = new Plane(Point3d.Origin, direction.Value); + if (_PlaneCache == null) + _PlaneCache = new Plane(Point3d.Origin, Vector3d.ZAxis); + return startPoint.GetVectorTo(endPoint).AngleOnPlane(_PlaneCache); } /// /// 两点计算弧度范围0到2Pi @@ -94,6 +94,7 @@ public static double GetArcBulge(this Point2d arc1, Point2d arc2, Point2d arc3, /// /// 首尾相连 /// + [System.Diagnostics.DebuggerStepThrough] public static void End2End(this Point2dCollection ptcol) { if (ptcol == null) @@ -114,6 +115,7 @@ public static void End2End(this Point2dCollection ptcol) /// /// 首尾相连 /// + [System.Diagnostics.DebuggerStepThrough] public static void End2End(this Point3dCollection ptcol) { if (ptcol == null) diff --git a/src/IFoxCAD.Cad.Shared/ExtensionMethod/SelectionSetEx.cs b/src/IFoxCAD.Cad.Shared/ExtensionMethod/SelectionSetEx.cs index d63d5bd..ac5f503 100644 --- a/src/IFoxCAD.Cad.Shared/ExtensionMethod/SelectionSetEx.cs +++ b/src/IFoxCAD.Cad.Shared/ExtensionMethod/SelectionSetEx.cs @@ -11,6 +11,7 @@ public static class SelectionSetEx /// /// 选择集 /// 已选择的对象集合 + [System.Diagnostics.DebuggerStepThrough] public static IEnumerable GetSelectedObjects(this SelectionSet ss) { return ss.Cast(); @@ -22,6 +23,7 @@ public static IEnumerable GetSelectedObjects(this SelectionSet s /// 已选择的对象泛型 /// 选择集 /// 已选择的对象集合 + [System.Diagnostics.DebuggerStepThrough] public static IEnumerable GetSelectObjects(this SelectionSet ss) where T : SelectedObject { return ss.Cast().OfType(); @@ -33,6 +35,7 @@ public static IEnumerable GetSelectObjects(this SelectionSet ss) where T : /// 图元类型 /// 选择集 /// 已选择的对象id集合 + [System.Diagnostics.DebuggerStepThrough] public static IEnumerable GetObjectIds(this SelectionSet ss) where T : Entity { string dxfName = RXClass.GetClass(typeof(T)).DxfName; @@ -47,6 +50,7 @@ public static IEnumerable GetObjectIds(this SelectionSet ss) where /// /// 选择集 /// 分组后的类型/对象id集合 + [System.Diagnostics.DebuggerStepThrough] public static IEnumerable> GetObjectIdGroup(this SelectionSet ss) { return @@ -68,6 +72,7 @@ public static IEnumerable> GetObjectIdGroup(this Sel /// 是否打开已删除对象,默认为不打开 /// 是否打开锁定图层对象,默认为不打开 /// 图元集合 + [System.Diagnostics.DebuggerStepThrough] public static IEnumerable GetEntities(this SelectionSet ss, OpenMode openMode = OpenMode.ForRead, DBTrans? tr = default, @@ -95,6 +100,7 @@ public static IEnumerable> GetObjectIdGroup(this Sel /// 事务 /// 是否打开已删除对象,默认为不打开 /// 是否打开锁定图层对象,默认为不打开 + [System.Diagnostics.DebuggerStepThrough] public static void ForEach(this SelectionSet ss, Action action, OpenMode openMode = OpenMode.ForRead, @@ -118,6 +124,7 @@ public static void ForEach(this SelectionSet ss, /// 是否打开已删除对象,默认为不打开 /// 是否打开锁定图层对象,默认为不打开 /// + [System.Diagnostics.DebuggerStepThrough] public static void ForEach(this SelectionSet ss, Action action, OpenMode openMode = OpenMode.ForRead, diff --git a/src/IFoxCAD.Cad.Shared/ExtensionMethod/Tools.cs b/src/IFoxCAD.Cad.Shared/ExtensionMethod/Tools.cs index b11d81d..32359c9 100644 --- a/src/IFoxCAD.Cad.Shared/ExtensionMethod/Tools.cs +++ b/src/IFoxCAD.Cad.Shared/ExtensionMethod/Tools.cs @@ -2,6 +2,10 @@ public static class Tools { + /// + /// 计时器 + /// + [System.Diagnostics.DebuggerStepThrough] public static void TestTimes2(int count, string message, Action action) { System.Diagnostics.Stopwatch watch = new(); @@ -23,6 +27,7 @@ public static void TestTimes2(int count, string message, Action action) /// /// 纳秒计时器 /// + [System.Diagnostics.DebuggerStepThrough] public static void TestTimes(int count, string message, Action action, Timer.TimeEnum timeEnum = Timer.TimeEnum.Millisecond) { @@ -35,17 +40,17 @@ public static void TestTimes(int count, string message, Action action, switch (timeEnum) { case Timer.TimeEnum.Second: - timeNameZn = " 秒"; - break; + timeNameZn = " 秒"; + break; case Timer.TimeEnum.Millisecond: - timeNameZn = " 毫秒"; - break; + timeNameZn = " 毫秒"; + break; case Timer.TimeEnum.Microsecond: - timeNameZn = " 微秒"; - break; + timeNameZn = " 微秒"; + break; case Timer.TimeEnum.Nanosecond: - timeNameZn = " 纳秒"; - break; + timeNameZn = " 纳秒"; + break; } Env.Print($"{message} 代码执行 {count} 次的时间:{time} ({timeNameZn})"); @@ -56,12 +61,10 @@ public static void TestTimes(int count, string message, Action action, // 测试例子,同时验证两个计时器 var stopwatch = new Stopwatch(); Timer.RunTime(() => { - stopwatch.Start(); for (int i = 0; i < 10000000; i++) i++; stopwatch.Stop(); - }, Timer.TimeEnum.Millisecond, "运行:"); Console.WriteLine("运行毫秒:" + stopwatch.ElapsedMilliseconds); */ @@ -125,7 +128,7 @@ public void Start() } /// - /// 停止计时器 + /// 停止计时器 /// public void Stop() { @@ -151,17 +154,17 @@ public static double RunTime(Action action, TimeEnum timeEnum = TimeEnum.Millise switch (timeEnum) { case TimeEnum.Second: - time = nanoSecond.Second; - break; + time = nanoSecond.Second; + break; case TimeEnum.Millisecond: - time = nanoSecond.Millisecond; - break; + time = nanoSecond.Millisecond; + break; case TimeEnum.Microsecond: - time = nanoSecond.Microsecond; - break; + time = nanoSecond.Microsecond; + break; case TimeEnum.Nanosecond: - time = nanoSecond.Nanosecond; - break; + time = nanoSecond.Nanosecond; + break; } if (msg != null) { @@ -169,17 +172,17 @@ public static double RunTime(Action action, TimeEnum timeEnum = TimeEnum.Millise switch (timeEnum) { case TimeEnum.Second: - timeNameZn = " 秒"; - break; + timeNameZn = " 秒"; + break; case TimeEnum.Millisecond: - timeNameZn = " 毫秒"; - break; + timeNameZn = " 毫秒"; + break; case TimeEnum.Microsecond: - timeNameZn = " 微秒"; - break; + timeNameZn = " 微秒"; + break; case TimeEnum.Nanosecond: - timeNameZn = " 纳秒"; - break; + timeNameZn = " 纳秒"; + break; } Env.Print(msg + " " + time + timeNameZn); } diff --git a/src/IFoxCAD.Cad.Shared/SelectionFilter/OpLogi.cs b/src/IFoxCAD.Cad.Shared/SelectionFilter/OpLogi.cs index 4227608..7b14b90 100644 --- a/src/IFoxCAD.Cad.Shared/SelectionFilter/OpLogi.cs +++ b/src/IFoxCAD.Cad.Shared/SelectionFilter/OpLogi.cs @@ -25,6 +25,7 @@ public TypedValue Last /// 获取过滤条件 ///
    /// TypedValue迭代器 + //[System.Diagnostics.DebuggerStepThrough] public override IEnumerable GetValues() { yield return First; @@ -125,4 +126,4 @@ public override IEnumerator GetEnumerator() yield return Left; yield return Right; } -} +} \ No newline at end of file diff --git a/src/IFoxCAD.Cad/IFoxCAD.Aacad08/IFoxCAD.Acad08.csproj b/src/IFoxCAD.Cad/IFoxCAD.Aacad08/IFoxCAD.Acad08.csproj index 4df9f3d..e99be5d 100644 --- a/src/IFoxCAD.Cad/IFoxCAD.Aacad08/IFoxCAD.Acad08.csproj +++ b/src/IFoxCAD.Cad/IFoxCAD.Aacad08/IFoxCAD.Acad08.csproj @@ -22,6 +22,7 @@ true LICENSE true + True diff --git a/src/IFoxCAD.Cad/IFoxCAD.Aacad09plus/IFoxCAD.Acad09plus.csproj b/src/IFoxCAD.Cad/IFoxCAD.Aacad09plus/IFoxCAD.Acad09plus.csproj index 99f9a44..4340d95 100644 --- a/src/IFoxCAD.Cad/IFoxCAD.Aacad09plus/IFoxCAD.Acad09plus.csproj +++ b/src/IFoxCAD.Cad/IFoxCAD.Aacad09plus/IFoxCAD.Acad09plus.csproj @@ -22,6 +22,7 @@ true LICENSE true + True x64 diff --git a/src/IFoxCAD.Cad/IFoxCAD.Gcad/IFoxCAD.Gcad.csproj b/src/IFoxCAD.Cad/IFoxCAD.Gcad/IFoxCAD.Gcad.csproj index 58df1d3..314cd87 100644 --- a/src/IFoxCAD.Cad/IFoxCAD.Gcad/IFoxCAD.Gcad.csproj +++ b/src/IFoxCAD.Cad/IFoxCAD.Gcad/IFoxCAD.Gcad.csproj @@ -22,6 +22,7 @@ true LICENSE true + True diff --git a/src/IFoxCAD.Cad/IFoxCAD.Zcad/IFoxCAD.Zcad.csproj b/src/IFoxCAD.Cad/IFoxCAD.Zcad/IFoxCAD.Zcad.csproj index 0ae1bdf..893a212 100644 --- a/src/IFoxCAD.Cad/IFoxCAD.Zcad/IFoxCAD.Zcad.csproj +++ b/src/IFoxCAD.Cad/IFoxCAD.Zcad/IFoxCAD.Zcad.csproj @@ -22,6 +22,7 @@ true LICENSE true + True diff --git a/src/IFoxCAD.LoadForm/AssemblyDependent.cs b/src/IFoxCAD.LoadForm/AssemblyDependent.cs index bd9b2f4..e525265 100644 --- a/src/IFoxCAD.LoadForm/AssemblyDependent.cs +++ b/src/IFoxCAD.LoadForm/AssemblyDependent.cs @@ -527,14 +527,16 @@ public static void DeleteFolder(string? dir) { if (dir is null) throw new ArgumentException(nameof(dir)); - if (!Directory.Exists(dir)) // 如果存在这个文件夹删除之 + if (!Directory.Exists(dir)) // 存在文件夹 return; - foreach (string d in Directory.GetFileSystemEntries(dir)) + var se = Directory.GetFileSystemEntries(dir); + for (int i = 0; i < se.Length; i++) { - if (File.Exists(d)) - File.Delete(d); // 直接删除其中的文件 + if (File.Exists(se[i])) + try { File.Delete(se[i]); /*直接删除其中的文件*/ } + catch { } else - DeleteFolder(d); // 递归删除子文件夹 + DeleteFolder(se[i]); // 递归删除子文件夹 } Directory.Delete(dir, true); // 删除已空文件夹 } diff --git a/tests/TestShared/CmdINI.cs b/tests/TestShared/CmdINI.cs index b2ea1b0..8558a4d 100644 --- a/tests/TestShared/CmdINI.cs +++ b/tests/TestShared/CmdINI.cs @@ -138,22 +138,4 @@ public void Terminate() // // 文档管理器将比此接口前死亡,因此此句不会执行 // Acap.DocumentManager.MdiActiveDocument?.Editor.WriteMessage("\nunload...."); // } -} - -public partial class Test -{ - [CommandMethod(nameof(Test_GetEnv))] - public static void Test_GetEnv() - { - var dir = Env.GetEnv("PrinterConfigDir"); - Env.Printl("pc3打印机位置:" + dir); - - Env.SetEnv("abc", "656"); - - var obj = Env.GetEnv("abc"); - Env.Printl("GetEnv:" + obj); - - Env.Printl("GetEnv:" + Env.GetEnv("abc")); - Env.Printl("GetEnv PATH:" + Env.GetEnv("PATH")); - } } \ No newline at end of file diff --git a/tests/TestShared/TestEnv.cs b/tests/TestShared/TestEnv.cs index dc2d1ce..9ebe091 100644 --- a/tests/TestShared/TestEnv.cs +++ b/tests/TestShared/TestEnv.cs @@ -98,4 +98,19 @@ public static Dictionary GetvarAll() return dict; } #endif + + [CommandMethod(nameof(Test_GetEnv))] + public static void Test_GetEnv() + { + var dir = Env.GetEnv("PrinterConfigDir"); + Env.Printl("pc3打印机位置:" + dir); + + Env.SetEnv("abc", "656"); + + var obj = Env.GetEnv("abc"); + Env.Printl("GetEnv:" + obj); + + Env.Printl("GetEnv:" + Env.GetEnv("abc")); + Env.Printl("GetEnv PATH:" + Env.GetEnv("PATH")); + } } \ No newline at end of file diff --git a/tests/TestShared/TestMarshal.cs b/tests/TestShared/TestMarshal.cs index dd4e689..7d52536 100644 --- a/tests/TestShared/TestMarshal.cs +++ b/tests/TestShared/TestMarshal.cs @@ -1,7 +1,52 @@ -namespace TestShared; +using System.Diagnostics; + +namespace TestShared; public class TestMarshal { + [CommandMethod(nameof(Test_DebuggerStepThrough))] + public void Test_DebuggerStepThrough() + { + DebuggerStepThrough(() => { + for (int i = 0; i < 10; i++)//断点可以进入此处 + { + } + }); + } + + [System.Diagnostics.DebuggerStepThrough] + public void DebuggerStepThrough(Action action) + { + //throw new ArgumentNullException(nameof(action));//可以抛出 + int a = 0;//断点无法进入此处 + int b = 0; + action?.Invoke(); + int c = 0; + int d = 0; + } + + [CommandMethod(nameof(Test_ImplicitPoint3D))] + public void Test_ImplicitPoint3D() + { + // 无法用指针转换类型,所以隐式转换是无法不new的, + // 貌似是因为 + // 如果发生了获取对象的成员引用指针,没有new的话,会发生引用不计数...造成GC释放失效... + // 而微软没有提供一种计数转移的方法...造成我无法实现此操作... + unsafe + { + Point3d pt1 = new(1, 56, 89); + var a1 = (Point3D*)&pt1; + Debug.WriteLine("指针类型转换,获取x::" + a1->X); + + var a = (IntPtr)(&pt1);//explicit 显式转换 == new + var pt2 = (Point3D)Marshal.PtrToStructure(a, typeof(Point3D)); + Debug.WriteLine("pt1转IntPtr地址::" + (int)&a); + Debug.WriteLine("pt1地址::" + (int)&pt1); + Debug.WriteLine("pt2地址::" + (int)&pt2); + Debug.Assert(&pt1 == &pt2);//不相等,是申请了新内存 + } + } + [CommandMethod(nameof(Test_Marshal))] public void Test_Marshal() { @@ -24,7 +69,7 @@ public void Test_Marshal() // 0x03 此方法仍然需要不安全操作,而且多了几个函数调用... unsafe { - var p = (IntPtr)(IntPtr*)&pt; + var p = new IntPtr(&pt); var result2 = (Point3D)Marshal.PtrToStructure(p, typeof(Point3D)); result2.X = 220; Marshal.StructureToPtr(result2, p, true); diff --git a/tests/TestShared/TestShared.projitems b/tests/TestShared/TestShared.projitems index 9e3d265..f041f9c 100644 --- a/tests/TestShared/TestShared.projitems +++ b/tests/TestShared/TestShared.projitems @@ -13,13 +13,13 @@ - - + + - - + + @@ -31,7 +31,7 @@ - + \ No newline at end of file -- Gitee From 32722ae5ec2791f2dfcd9ba1e170d3cce1a465e6 Mon Sep 17 00:00:00 2001 From: liuqihong <540762622@qq.com> Date: Fri, 14 Oct 2022 01:28:24 +0800 Subject: [PATCH 557/675] =?UTF-8?q?=E5=88=B7=E6=96=B0=E6=96=B9=E5=BC=8F?= =?UTF-8?q?=E5=A2=9E=E5=8A=A0:=E5=B9=B3=E7=A7=BB=E6=9B=B4=E6=96=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../ExtensionMethod/EntityEx.cs | 24 --------- src/IFoxCAD.Cad.Shared/Runtime/RedrawEx.cs | 54 +++++++++++++++---- tests/TestShared/TestShared.projitems | 10 ++-- 3 files changed, 50 insertions(+), 38 deletions(-) diff --git a/src/IFoxCAD.Cad.Shared/ExtensionMethod/EntityEx.cs b/src/IFoxCAD.Cad.Shared/ExtensionMethod/EntityEx.cs index 092edd3..381fa1d 100644 --- a/src/IFoxCAD.Cad.Shared/ExtensionMethod/EntityEx.cs +++ b/src/IFoxCAD.Cad.Shared/ExtensionMethod/EntityEx.cs @@ -6,30 +6,6 @@ ///
    public static class EntityEx { - #region 实体刷新 - // 此处已经被RedrawEx代替 - - /// - /// 刷新实体显示 - /// - /// 实体对象 - public static void Flush(this Entity entity, DBTrans? trans = null) - { - // if (entity is null) - // throw new ArgumentNullException(nameof(entity)); - trans ??= DBTrans.Top; - entity.RecordGraphicsModified(true); - trans.Transaction.TransactionManager.QueueForGraphicsFlush(); - trans.Document?.TransactionManager.FlushGraphics(); - } - - /// - /// 刷新实体显示 - /// - /// 实体id - public static void Flush(this ObjectId id) => Flush(DBTrans.Top.GetObject(id)!); - #endregion - #region 多段线端点坐标 /// /// 获取二维多段线的端点坐标 diff --git a/src/IFoxCAD.Cad.Shared/Runtime/RedrawEx.cs b/src/IFoxCAD.Cad.Shared/Runtime/RedrawEx.cs index 72a57be..78822ee 100644 --- a/src/IFoxCAD.Cad.Shared/Runtime/RedrawEx.cs +++ b/src/IFoxCAD.Cad.Shared/Runtime/RedrawEx.cs @@ -1,7 +1,7 @@ namespace IFoxCAD.Cad; [Flags] -public enum BrightEntity : byte +public enum BrightEntity { /// /// 块更新 @@ -31,10 +31,14 @@ public enum BrightEntity : byte /// 隐藏图元 /// VisibleFalse = 64, + /// + /// 平移更新,可以令ctrl+z撤回时候保证刷新 + /// + MoveZero = 128, } [Flags] -public enum BrightEditor : byte +public enum BrightEditor { /// /// 刷新屏幕,图元不生成(例如块还是旧的显示) @@ -64,14 +68,14 @@ public static class RedrawEx /// 刷新屏幕 /// /// 编辑器 - /// 图元 + /// 图元,调用时候图元必须提权 public static void Redraw(this Editor ed, Entity? ent = null) { // 刷新图元 ent?.Redraw(BrightEntity.Draw | BrightEntity.RecordGraphicsModified | - BrightEntity.RecomputeDimensionBlock); - + BrightEntity.RecomputeDimensionBlock | + BrightEntity.MoveZero); // 刷新 ed.Redraw(BrightEditor.UpdateScreen); @@ -104,8 +108,8 @@ public static void Redraw(this Editor ed, BrightEditor bright) { if ((bright & BrightEditor.UpdateScreen) == BrightEditor.UpdateScreen) { - // Acap.UpdateScreen();// 两个函数底层差不多 - // 仅刷新屏幕,图元不生成(例如块还是旧的显示) + // 两个函数底层差不多 + // Acap.UpdateScreen(); ed.UpdateScreen(); } @@ -125,11 +129,11 @@ public static void Redraw(this Editor ed, BrightEditor bright) /// /// 更改图元显示 /// - /// 图元 + /// 图元,调用时候图元必须提权 /// 更新的方式 public static void Redraw(this Entity ent, BrightEntity bright) { - // 调用时候必须 ent.UpgradeOpen(),参数true表示关闭图元后进行UpData,实现局部刷新块. + // 调用时候图元必须提权,参数true表示关闭图元后进行UpData,实现局部刷新块. if ((bright & BrightEntity.RecordGraphicsModified) == BrightEntity.RecordGraphicsModified) ent.RecordGraphicsModified(true); @@ -151,5 +155,37 @@ public static void Redraw(this Entity ent, BrightEntity bright) if ((bright & BrightEntity.VisibleFalse) == BrightEntity.VisibleFalse) ent.Visible = false; + + // 戴耀辉: + // 删除块内图元的时候需要刷新块, + // 用 RecordGraphicsModified 显示是没有问题, + // 但是 ctrl+z 撤销会有显示问题, + // 所以平移0可以在撤回数据库的时候刷新指定图元 + if ((bright & BrightEntity.MoveZero) == BrightEntity.MoveZero) + ent.Move(Point3d.Origin, Point3d.Origin); } + + + #region 实体刷新 + /// + /// 刷新实体显示 + /// + /// 实体对象 + [Obsolete("此处已经被RedrawEx代替")] + public static void Flush(this Entity entity, DBTrans? trans = null) + { + trans ??= DBTrans.Top; + entity.RecordGraphicsModified(true); + trans.Transaction.TransactionManager.QueueForGraphicsFlush(); + trans.Document?.TransactionManager.FlushGraphics(); + } + + /// + /// 刷新实体显示 + /// + /// 实体id + [Obsolete("此处已经被RedrawEx代替")] + public static void Flush(this ObjectId id) + => Flush(DBTrans.Top.GetObject(id)!); + #endregion } \ No newline at end of file diff --git a/tests/TestShared/TestShared.projitems b/tests/TestShared/TestShared.projitems index f041f9c..595b36a 100644 --- a/tests/TestShared/TestShared.projitems +++ b/tests/TestShared/TestShared.projitems @@ -13,15 +13,15 @@ - + - - + + - + @@ -31,7 +31,7 @@ - + \ No newline at end of file -- Gitee From eef65846c26258f31ea184c2cd8b94b30c945373 Mon Sep 17 00:00:00 2001 From: liuqihong <540762622@qq.com> Date: Fri, 14 Oct 2022 19:35:50 +0800 Subject: [PATCH 558/675] =?UTF-8?q?=E4=BC=98=E5=8C=96=E4=BB=A3=E7=A0=81=20?= =?UTF-8?q?=E5=A2=9E=E5=8A=A0forEach=E6=96=AD=E7=82=B9=E8=B7=B3=E8=BF=87?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/IFoxCAD.Basal/General/LinkedHashSet.cs | 15 +++---- src/IFoxCAD.Basal/General/LoopList.cs | 42 +++++++++++-------- src/IFoxCAD.Basal/Sortedset/Sortedset.cs | 6 +-- .../Algorithms/Graph/Graph.cs | 9 ++-- src/IFoxCAD.Cad.Shared/Runtime/SymbolTable.cs | 5 ++- .../SelectionFilter/OpList.cs | 3 +- .../SelectionFilter/OpLogi.cs | 4 ++ 7 files changed, 51 insertions(+), 33 deletions(-) diff --git a/src/IFoxCAD.Basal/General/LinkedHashSet.cs b/src/IFoxCAD.Basal/General/LinkedHashSet.cs index 6295795..6efaa04 100644 --- a/src/IFoxCAD.Basal/General/LinkedHashSet.cs +++ b/src/IFoxCAD.Basal/General/LinkedHashSet.cs @@ -19,7 +19,7 @@ public LinkedHashSet() public bool Add(T item) { - if (m_Dictionary.ContainsKey(item)) + if (m_Dictionary.ContainsKey(item)) return false; var node = m_LinkedList.AddLast(item); m_Dictionary.Add(item, node); @@ -97,16 +97,15 @@ public int Count get { return m_Dictionary.Count; } } - public void For(LoopListNode from, Action action) + public void For(LoopListNode from, Action action) { var first = from; var last = from; - if(first is null) return; + if (first is null) return; for (int i = 0; i < Count; i++) { - - action.Invoke(i,first!.Value, last!.Value); + action.Invoke(i, first!.Value, last!.Value); first = first.Next; last = last.Previous; } @@ -117,11 +116,13 @@ public List ToList() return m_LinkedList.ToList(); } + [System.Diagnostics.DebuggerStepThrough] public IEnumerator GetEnumerator() { return m_LinkedList.GetEnumerator(); } + [System.Diagnostics.DebuggerStepThrough] IEnumerator IEnumerable.GetEnumerator() { return GetEnumerator(); @@ -146,7 +147,7 @@ public bool SetFirst(LoopListNode node) public LinkedHashSet Clone() { - var newset = new LinkedHashSet(); + var newset = new LinkedHashSet(); foreach (var item in this) { newset.Add(item); @@ -218,4 +219,4 @@ private static Exception GetNotSupportedDueToSimplification() { return new NotSupportedException("This method is not supported due to simplification of example code."); } -} +} \ No newline at end of file diff --git a/src/IFoxCAD.Basal/General/LoopList.cs b/src/IFoxCAD.Basal/General/LoopList.cs index 9bd986b..243ced6 100644 --- a/src/IFoxCAD.Basal/General/LoopList.cs +++ b/src/IFoxCAD.Basal/General/LoopList.cs @@ -104,6 +104,7 @@ public LoopList() { } /// 环链表构造函数 /// /// 节点迭代器 + [System.Diagnostics.DebuggerStepThrough] public LoopList(IEnumerable values) { var ge = values.GetEnumerator(); @@ -389,6 +390,7 @@ public LoopListNode Add(T value) ///
    /// /// + [System.Diagnostics.DebuggerStepThrough] public LoopListNode AddLast(T value) { return Add(value); @@ -398,6 +400,7 @@ public LoopListNode AddLast(T value) /// 容器内容全部加入到末尾 ///
    /// + [System.Diagnostics.DebuggerStepThrough] public void AddRange(IEnumerable list) { var ge = list.GetEnumerator(); @@ -455,18 +458,18 @@ public bool RemoveFirst() switch (Count) { case 0: - return false; + return false; case 1: - First = null; - break; + First = null; + break; default: - LoopListNode last = Last!; - First = First!.Next; - First!.Previous = last; - last.Next = First; - break; + LoopListNode last = Last!; + First = First!.Next; + First!.Previous = last; + last.Next = First; + break; } Count--; return true; @@ -481,17 +484,17 @@ public bool RemoveLast() switch (Count) { case 0: - return false; + return false; case 1: - First = null; - break; + First = null; + break; default: - LoopListNode last = Last!.Previous!; - last.Next = First; - First!.Previous = last; - break; + LoopListNode last = Last!.Previous!; + last.Next = First; + First!.Previous = last; + break; } Count--; return true; @@ -630,6 +633,7 @@ public void LinkTo(LoopListNode from, LoopListNode to, int number, bool is ///
    /// /// + [System.Diagnostics.DebuggerStepThrough] public IEnumerable> GetNodes(LoopListNode from) { var node = from; @@ -640,12 +644,11 @@ public IEnumerable> GetNodes(LoopListNode from) } } - - /// /// 获取节点的查询器 /// /// + [System.Diagnostics.DebuggerStepThrough] public IEnumerable> GetNodes() { LoopListNode node = First!; @@ -660,6 +663,7 @@ public IEnumerable> GetNodes() /// 获取节点值的查询器 ///
    /// + [System.Diagnostics.DebuggerStepThrough] public IEnumerator GetEnumerator() { LoopListNode node = First!; @@ -670,10 +674,12 @@ public IEnumerator GetEnumerator() } } + [System.Diagnostics.DebuggerStepThrough] IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); #region IEnumerable 成员 + [System.Diagnostics.DebuggerStepThrough] IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); #endregion IEnumerable 成员 @@ -724,7 +730,7 @@ public string ToString(string? format, IFormatProvider? formatProvider = null) /* 山人说无法分辨ICloneable接口是深浅克隆, * 因此不要在泛型模板实现克隆函数, * 让用户自己来 new(xx)实现浅克隆,所以也不提供Clone()了 - * + * * 因此约定了:CopyTo(T,index)是深克隆;MemberwiseClone()是浅克隆; * public object Clone() * { diff --git a/src/IFoxCAD.Basal/Sortedset/Sortedset.cs b/src/IFoxCAD.Basal/Sortedset/Sortedset.cs index 58e3c65..9560d17 100644 --- a/src/IFoxCAD.Basal/Sortedset/Sortedset.cs +++ b/src/IFoxCAD.Basal/Sortedset/Sortedset.cs @@ -780,19 +780,19 @@ void ICollection.CopyTo(Array array, int index) #endregion #region IEnumerable members + [System.Diagnostics.DebuggerStepThrough] public Enumerator GetEnumerator() { return new Enumerator(this); } - - - + [System.Diagnostics.DebuggerStepThrough] IEnumerator IEnumerable.GetEnumerator() { return new Enumerator(this); } + [System.Diagnostics.DebuggerStepThrough] IEnumerator IEnumerable.GetEnumerator() { return new Enumerator(this); diff --git a/src/IFoxCAD.Cad.Shared/Algorithms/Graph/Graph.cs b/src/IFoxCAD.Cad.Shared/Algorithms/Graph/Graph.cs index 64e551b..255d2b3 100644 --- a/src/IFoxCAD.Cad.Shared/Algorithms/Graph/Graph.cs +++ b/src/IFoxCAD.Cad.Shared/Algorithms/Graph/Graph.cs @@ -254,6 +254,7 @@ public List GetCurves(List graphVertices) /// /// 克隆此图;目测是深克隆 /// + [System.Diagnostics.DebuggerStepThrough] public Graph Clone() { var newGraph = new Graph(); @@ -270,11 +271,13 @@ IGraph IGraph.Clone() return Clone(); } + [System.Diagnostics.DebuggerStepThrough] public IEnumerator GetEnumerator() { return VerticesAsEnumberable.GetEnumerator(); } + [System.Diagnostics.DebuggerStepThrough] IEnumerator? IEnumerable.GetEnumerator() { return GetEnumerator() as IEnumerator; @@ -422,7 +425,7 @@ public sealed class GraphEdge : IEdge, IEquatable #region 构造 /// - /// 无向图中边的定义 + /// 无向图中边的定义 /// /// 下一点 /// 下一点之间的曲线 @@ -575,8 +578,8 @@ void Dfs(IGraph graph, List visited) // 将重复的路径进行过滤,并把新的路径存入结果 var cur = RotateToSmallest(visited); // O(n) var inv = Invert(cur,cur[0]); // O(n) - - var curstr = Gethashstring(cur,inv); + + var curstr = Gethashstring(cur,inv); // Env.Print(curstr); if (Isnew(curstr)) { diff --git a/src/IFoxCAD.Cad.Shared/Runtime/SymbolTable.cs b/src/IFoxCAD.Cad.Shared/Runtime/SymbolTable.cs index 2a4327d..754e35e 100644 --- a/src/IFoxCAD.Cad.Shared/Runtime/SymbolTable.cs +++ b/src/IFoxCAD.Cad.Shared/Runtime/SymbolTable.cs @@ -307,6 +307,7 @@ internal ObjectId GetRecordFrom(Func> tabl /// 要运行的委托 /// 打开模式,默认为只读 /// 检查id是否删除,默认false + [System.Diagnostics.DebuggerStepThrough] public void ForEach(Action action, OpenMode openMode = OpenMode.ForRead, bool checkIdOk = false) @@ -328,6 +329,7 @@ public void ForEach(Action action, /// 要执行的委托 /// 打开模式,默认为只读 /// 检查id是否删除,默认false + [System.Diagnostics.DebuggerStepThrough] public void ForEach(Action action, OpenMode openMode = OpenMode.ForRead, bool checkIdOk = false) @@ -347,13 +349,14 @@ public void ForEach(Action action, #endregion #region IEnumerable 成员 - + [System.Diagnostics.DebuggerStepThrough] public IEnumerator GetEnumerator() { foreach (var id in CurrentSymbolTable) yield return id; } + [System.Diagnostics.DebuggerStepThrough] IEnumerator IEnumerable.GetEnumerator() { return GetEnumerator(); diff --git a/src/IFoxCAD.Cad.Shared/SelectionFilter/OpList.cs b/src/IFoxCAD.Cad.Shared/SelectionFilter/OpList.cs index 3a02513..5870be9 100644 --- a/src/IFoxCAD.Cad.Shared/SelectionFilter/OpList.cs +++ b/src/IFoxCAD.Cad.Shared/SelectionFilter/OpList.cs @@ -96,6 +96,7 @@ public void Add(DxfCode code, object value, string comp) /// 过滤器迭代器 ///
    /// OpFilter迭代器 + [System.Diagnostics.DebuggerStepThrough] public override IEnumerator GetEnumerator() { foreach (var value in _lst) @@ -163,4 +164,4 @@ public override void Add(OpFilter value) _lst.Add(value); } } -} +} \ No newline at end of file diff --git a/src/IFoxCAD.Cad.Shared/SelectionFilter/OpLogi.cs b/src/IFoxCAD.Cad.Shared/SelectionFilter/OpLogi.cs index 7b14b90..bdacb86 100644 --- a/src/IFoxCAD.Cad.Shared/SelectionFilter/OpLogi.cs +++ b/src/IFoxCAD.Cad.Shared/SelectionFilter/OpLogi.cs @@ -41,8 +41,10 @@ public override IEnumerable GetValues() /// 获取迭代器 ///
    /// OpFilter迭代器 + [System.Diagnostics.DebuggerStepThrough] public abstract IEnumerator GetEnumerator(); + [System.Diagnostics.DebuggerStepThrough] IEnumerator IEnumerable.GetEnumerator() { return GetEnumerator(); @@ -77,6 +79,7 @@ public override string Name /// 获取迭代器 ///
    /// OpFilter迭代器 + [System.Diagnostics.DebuggerStepThrough] public override IEnumerator GetEnumerator() { yield return Value; @@ -121,6 +124,7 @@ public override string Name /// 获取迭代器 ///
    /// 选择集过滤器类型迭代器 + [System.Diagnostics.DebuggerStepThrough] public override IEnumerator GetEnumerator() { yield return Left; -- Gitee From 341ef3e41bcf55156a2c07f3c0837693edd9a78f Mon Sep 17 00:00:00 2001 From: liuqihong <540762622@qq.com> Date: Fri, 14 Oct 2022 21:30:12 +0800 Subject: [PATCH 559/675] =?UTF-8?q?=E4=BC=98=E5=8C=96=E4=BB=A3=E7=A0=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/IFoxCAD.Cad.Shared/Runtime/AcadVersion.cs | 18 +++++++++--------- src/IFoxCAD.LoadForm/AssemblyDependent.cs | 3 ++- 2 files changed, 11 insertions(+), 10 deletions(-) diff --git a/src/IFoxCAD.Cad.Shared/Runtime/AcadVersion.cs b/src/IFoxCAD.Cad.Shared/Runtime/AcadVersion.cs index 9f776fc..61aa620 100644 --- a/src/IFoxCAD.Cad.Shared/Runtime/AcadVersion.cs +++ b/src/IFoxCAD.Cad.Shared/Runtime/AcadVersion.cs @@ -19,18 +19,18 @@ public static List Versions .GetValueNames(); var _versions = new List(); - foreach (var rootkey in copys) + for (int i = 0; i < copys.Length; i++) { - if (!Regex.IsMatch(rootkey, _pattern)) + if (!Regex.IsMatch(copys[i], _pattern)) continue; - var gs = Regex.Match(rootkey, _pattern).Groups; + var gs = Regex.Match(copys[i], _pattern).Groups; var ver = new CadVersion { - ProductRootKey = rootkey, + ProductRootKey = copys[i], ProductName = Registry.LocalMachine .OpenSubKey("SOFTWARE") - .OpenSubKey(rootkey) + .OpenSubKey(copys[i]) .GetValue("ProductName") .ToString(), @@ -62,9 +62,9 @@ public static List Versions var gs = Regex.Match(acver, @"(\d+)\.(\d+).*?").Groups; int major = int.Parse(gs[1].Value); int minor = int.Parse(gs[2].Value); - foreach (var ver in Versions) - if (ver.Major == major && ver.Minor == minor) - return ver; + for (int i = 0; i < Versions.Count; i++) + if (Versions[i].Major == major && Versions[i].Minor == minor) + return Versions[i]; return null; } -} +} \ No newline at end of file diff --git a/src/IFoxCAD.LoadForm/AssemblyDependent.cs b/src/IFoxCAD.LoadForm/AssemblyDependent.cs index e525265..f7d3987 100644 --- a/src/IFoxCAD.LoadForm/AssemblyDependent.cs +++ b/src/IFoxCAD.LoadForm/AssemblyDependent.cs @@ -502,7 +502,8 @@ static void MoveFolder2(string sourcePath, string destPath) string destFile = Path.Combine(destPath, Path.GetFileName(c)); // 覆盖模式 if (File.Exists(destFile)) - File.Delete(destFile); + try { File.Delete(destFile); } + catch { } File.Move(c, destFile); }); // 获得源文件下所有目录文件 -- Gitee From 3a9e4437201bfefe7537773351e57c0b3aa26b50 Mon Sep 17 00:00:00 2001 From: liuqihong <540762622@qq.com> Date: Fri, 14 Oct 2022 22:41:29 +0800 Subject: [PATCH 560/675] =?UTF-8?q?=E7=BB=9F=E4=B8=80ForEach=E7=9A=84?= =?UTF-8?q?=E5=8F=82=E6=95=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Algorithms/QuadTree/Rect.cs | 14 +-- .../ExtensionMethod/CollectionEx.cs | 39 +++----- .../ExtensionMethod/ObjectIdEx.cs | 12 +-- .../ExtensionMethod/SymbolTableRecordEx.cs | 11 ++- src/IFoxCAD.Cad.Shared/Runtime/DBTrans.cs | 14 +-- src/IFoxCAD.Cad.Shared/Runtime/SymbolTable.cs | 95 +++++++++++++------ tests/TestShared/Copyclip.cs | 2 +- tests/TestShared/TestDBTrans.cs | 23 +++++ tests/TestShared/TestMirrorFile.cs | 3 +- tests/TestShared/TestXrefEx.cs | 2 +- 10 files changed, 135 insertions(+), 80 deletions(-) diff --git a/src/IFoxCAD.Cad.Shared/Algorithms/QuadTree/Rect.cs b/src/IFoxCAD.Cad.Shared/Algorithms/QuadTree/Rect.cs index 7639104..6ed1681 100644 --- a/src/IFoxCAD.Cad.Shared/Algorithms/QuadTree/Rect.cs +++ b/src/IFoxCAD.Cad.Shared/Algorithms/QuadTree/Rect.cs @@ -138,7 +138,9 @@ public double Area #endregion #region 构造 - public Rect() { } + public Rect() + { + } /// /// 矩形类 @@ -450,7 +452,7 @@ static double Cross(Vector2d a, Vector2d b) } /// - /// 叉积,二维叉乘计算 + /// 叉积,二维叉乘计算 /// /// 原点 /// oa向量 @@ -480,8 +482,8 @@ public Entity ToPolyLine() var pts = ToPoints(); Polyline pl = new(); pl.SetDatabaseDefaults(); - pts.ForEach((i, vertex) => { - pl.AddVertexAt(i, vertex, 0, 0, 0); + pts.ForEach((vertex, state, index) => { + pl.AddVertexAt(index, vertex, 0, 0, 0); }); return pl; } @@ -600,6 +602,4 @@ public int CompareTo(Rect rect) #endregion #endregion - - -} +} \ No newline at end of file diff --git a/src/IFoxCAD.Cad.Shared/ExtensionMethod/CollectionEx.cs b/src/IFoxCAD.Cad.Shared/ExtensionMethod/CollectionEx.cs index b10187b..274b119 100644 --- a/src/IFoxCAD.Cad.Shared/ExtensionMethod/CollectionEx.cs +++ b/src/IFoxCAD.Cad.Shared/ExtensionMethod/CollectionEx.cs @@ -79,6 +79,8 @@ public static List ToList(this ObjectIdCollection ids) } + + /// /// 遍历集合,执行委托 /// @@ -88,50 +90,39 @@ public static List ToList(this ObjectIdCollection ids) [System.Diagnostics.DebuggerStepThrough] public static void ForEach(this IEnumerable source, Action action) { - if (action == null) - throw new ArgumentNullException(nameof(action)); - - foreach (var element in source) - action.Invoke(element); + source.ForEach((a, _, _) => { + action.Invoke(a); + }); } + /// - /// 遍历集合,执行委托
    - /// 输出索引值 + /// 遍历集合,执行委托(允许循环中断) ///
    /// 集合值的类型 /// 集合 /// 委托 [System.Diagnostics.DebuggerStepThrough] - public static void ForEach(this IEnumerable source, Action action) + public static void ForEach(this IEnumerable source, Action action) { - if (action == null) - throw new ArgumentNullException(nameof(action)); - - int i = 0; - foreach (var element in source) - { - action.Invoke(i, element); - i++; - } + source.ForEach((a, b, _) => { + action.Invoke(a, b); + }); } + /// - /// 遍历集合,执行委托
    - /// 输出索引值,允许循环中断 + /// 遍历集合,执行委托(允许循环中断,输出索引值) ///
    /// 集合值的类型 /// 集合 /// 委托 [System.Diagnostics.DebuggerStepThrough] - public static void ForEach(this IEnumerable source, Action action) + public static void ForEach(this IEnumerable source, Action action) { - if (action == null) - throw new ArgumentNullException(nameof(action)); - LoopState state = new();/*这种方式比Action改Func更友好*/ int i = 0; foreach (var element in source) { - action.Invoke(i, element, state); + action.Invoke(element, state, i); if (!state.IsRun) break; i++; diff --git a/src/IFoxCAD.Cad.Shared/ExtensionMethod/ObjectIdEx.cs b/src/IFoxCAD.Cad.Shared/ExtensionMethod/ObjectIdEx.cs index a84bd7c..fa64a72 100644 --- a/src/IFoxCAD.Cad.Shared/ExtensionMethod/ObjectIdEx.cs +++ b/src/IFoxCAD.Cad.Shared/ExtensionMethod/ObjectIdEx.cs @@ -12,19 +12,19 @@ public static class ObjectIdEx ///
    /// 指定的泛型 /// 对象id - /// 打开模式 + /// 打开模式 /// 事务 /// 是否打开已删除对象,默认为不打开 /// 是否打开锁定图层对象,默认为不打开 /// 指定类型对象 public static T? GetObject(this ObjectId id, - OpenMode mode = OpenMode.ForRead, + OpenMode openMode = OpenMode.ForRead, Transaction? tr = default, bool openErased = false, bool openLockedLayer = false) where T : DBObject { tr ??= DBTrans.Top.Transaction; - return tr.GetObject(id, mode, openErased, openLockedLayer) as T; + return tr.GetObject(id, openMode, openErased, openLockedLayer) as T; } /// @@ -32,19 +32,19 @@ public static class ObjectIdEx /// /// 指定的泛型 /// 对象id集合 - /// 打开模式 + /// 打开模式 /// 事务 /// 是否打开已删除对象,默认为不打开 /// 是否打开锁定图层对象,默认为不打开 /// 指定类型对象集合 [System.Diagnostics.DebuggerStepThrough] public static IEnumerable GetObject(this IEnumerable ids, - OpenMode mode = OpenMode.ForRead, + OpenMode openMode = OpenMode.ForRead, Transaction? tr = default, bool openErased = false, bool openLockedLayer = false) where T : DBObject { - return ids.Select(id => id.GetObject(mode, tr, openErased, openLockedLayer)); + return ids.Select(id => id.GetObject(openMode, tr, openErased, openLockedLayer)); } /// diff --git a/src/IFoxCAD.Cad.Shared/ExtensionMethod/SymbolTableRecordEx.cs b/src/IFoxCAD.Cad.Shared/ExtensionMethod/SymbolTableRecordEx.cs index 48f464b..2b85498 100644 --- a/src/IFoxCAD.Cad.Shared/ExtensionMethod/SymbolTableRecordEx.cs +++ b/src/IFoxCAD.Cad.Shared/ExtensionMethod/SymbolTableRecordEx.cs @@ -289,8 +289,9 @@ public static ObjectId AddPline(this BlockTableRecord btr, { Polyline pl = new(); pl.SetDatabaseDefaults(); - pts.ForEach((i, vertex) => { - pl.AddVertexAt(i, vertex.pt.Point2d(), vertex.bulge, vertex.startWidth, vertex.endWidth); + + pts.ForEach((vertex, state, index) => { + pl.AddVertexAt(index, vertex.pt.Point2d(), vertex.bulge, vertex.startWidth, vertex.endWidth); }); return btr.AddEnt(pl, action, trans); @@ -323,13 +324,13 @@ public static ObjectId AddArc(this BlockTableRecord btr, /// /// 实体类型 /// 块表记录 - /// 打开模式 + /// 打开模式 /// 事务 /// 是否打开已删除对象,默认为不打开 /// 是否打开锁定图层对象,默认为不打开 /// 实体集合 public static IEnumerable GetEntities(this BlockTableRecord btr, - OpenMode mode = OpenMode.ForRead, + OpenMode openMode = OpenMode.ForRead, Transaction? trans = default, bool openErased = false, bool openLockedLayer = false) where T : Entity @@ -338,7 +339,7 @@ public static IEnumerable GetEntities(this BlockTableRecord btr, return btr .Cast() - .Select(id => trans.GetObject(id, mode, openErased, openLockedLayer)) + .Select(id => trans.GetObject(id, openMode, openErased, openLockedLayer)) .OfType(); } diff --git a/src/IFoxCAD.Cad.Shared/Runtime/DBTrans.cs b/src/IFoxCAD.Cad.Shared/Runtime/DBTrans.cs index b17685f..bc35ddf 100644 --- a/src/IFoxCAD.Cad.Shared/Runtime/DBTrans.cs +++ b/src/IFoxCAD.Cad.Shared/Runtime/DBTrans.cs @@ -129,11 +129,11 @@ public DBTrans(Database database, bool commit = true) ///
    /// 要打开的文件名 /// 事务是否提交 - /// 开图模式 + /// 开图模式 /// 密码 public DBTrans(string fileName, bool commit = true, - FileOpenMode openMode = FileOpenMode.OpenForReadAndWriteNoShare, + FileOpenMode fileOpenMode = FileOpenMode.OpenForReadAndWriteNoShare, string? password = null) { if (fileName == null || string.IsNullOrEmpty(fileName.Trim())) @@ -172,7 +172,7 @@ public DBTrans(string fileName, #if ac2008 // FileAccess fileAccess = FileAccess.Read; FileShare fileShare = FileShare.Read; - switch (openMode) + switch (fileOpenMode) { case FileOpenMode.OpenTryForReadShare:// 这个是什么状态?? // fileAccess = FileAccess.ReadWrite; @@ -200,7 +200,7 @@ public DBTrans(string fileName, Database.ReadDwgFile(_fileName, fileShare, true, password); #else - Database.ReadDwgFile(_fileName, openMode, true, password); + Database.ReadDwgFile(_fileName, fileOpenMode, true, password); #endif } Database.CloseInput(true); @@ -357,16 +357,16 @@ public static implicit operator Transaction(DBTrans tr) ///
    /// 要获取的图元对象的类型 /// 对象id - /// 打开模式,默认为只读 + /// 打开模式,默认为只读 /// 是否打开已删除对象,默认为不打开 /// 是否打开锁定图层对象,默认为不打开 /// 图元对象,类型不匹配时返回 public T? GetObject(ObjectId id, - OpenMode mode = OpenMode.ForRead, + OpenMode openMode = OpenMode.ForRead, bool openErased = false, bool openLockedLayer = false) where T : DBObject { - return Transaction.GetObject(id, mode, openErased, openLockedLayer) as T; + return Transaction.GetObject(id, openMode, openErased, openLockedLayer) as T; } /// diff --git a/src/IFoxCAD.Cad.Shared/Runtime/SymbolTable.cs b/src/IFoxCAD.Cad.Shared/Runtime/SymbolTable.cs index 754e35e..7423e67 100644 --- a/src/IFoxCAD.Cad.Shared/Runtime/SymbolTable.cs +++ b/src/IFoxCAD.Cad.Shared/Runtime/SymbolTable.cs @@ -1,3 +1,5 @@ +using static System.Windows.Forms.AxHost; + namespace IFoxCAD.Cad; public class SymbolTable : IEnumerable @@ -167,12 +169,10 @@ public void Remove(ObjectId id) /// /// 符号表记录 /// 修改委托 - private static void Change(TRecord record, Action action) + static void Change(TRecord record, Action action) { using (record.ForWrite()) action.Invoke(record); - // 调用regen()函数可能会导致卡顿 - // Env.Editor.Regen(); } /// @@ -205,17 +205,33 @@ public void Change(ObjectId id, Action action) /// 获取符号表记录 /// /// 符号表记录的id - /// 打开模式,默认为只读 + /// 打开模式 + /// 是否打开已删除对象,默认为不打开 + /// 是否打开锁定图层对象,默认为不打开 /// 符号表记录 - public TRecord? GetRecord(ObjectId id, OpenMode openMode = OpenMode.ForRead) => /*id.IsNull ? null : */DTrans.GetObject(id, openMode); + public TRecord? GetRecord(ObjectId id, + OpenMode openMode = OpenMode.ForRead, + bool openErased = false, + bool openLockedLayer = false) + { + return DTrans.GetObject(id, openMode, openErased, openLockedLayer); + } /// /// 获取符号表记录 /// /// 符号表记录名 - /// 打开模式,默认为只读 + /// 打开模式 + /// 是否打开已删除对象,默认为不打开 + /// 是否打开锁定图层对象,默认为不打开 /// 符号表记录 - public TRecord? GetRecord(string name, OpenMode openMode = OpenMode.ForRead) => GetRecord(this[name], openMode); + public TRecord? GetRecord(string name, + OpenMode openMode = OpenMode.ForRead, + bool openErased = false, + bool openLockedLayer = false) + { + return GetRecord(this[name], openMode, openErased, openLockedLayer); + } /// /// 获取符号表记录 @@ -302,48 +318,73 @@ internal ObjectId GetRecordFrom(Func> tabl #region 遍历 /// - /// 遍历集合的迭代器,执行action委托 + /// 遍历集合的迭代器,执行委托 /// - /// 要运行的委托 + /// 要运行的委托 /// 打开模式,默认为只读 /// 检查id是否删除,默认false + /// 是否打开已删除对象,默认为不打开 + /// 是否打开锁定图层对象,默认为不打开 [System.Diagnostics.DebuggerStepThrough] - public void ForEach(Action action, + public void ForEach(Action task, OpenMode openMode = OpenMode.ForRead, - bool checkIdOk = false) + bool checkIdOk = false, + bool openErased = false, + bool openLockedLayer = false) { - foreach (var id in this) - { - if (checkIdOk && !id.IsOk()) - continue; - var record = GetRecord(id, openMode); - if (record is not null) - action(record); - } + ForEach((a, _, _) => { + task.Invoke(a); + }, openMode, checkIdOk, openErased, openLockedLayer); } + /// + /// 遍历集合的迭代器,执行委托(允许循环中断) + /// + /// 要执行的委托 + /// 打开模式,默认为只读 + /// 检查id是否删除,默认false + /// 是否打开已删除对象,默认为不打开 + /// 是否打开锁定图层对象,默认为不打开 + [System.Diagnostics.DebuggerStepThrough] + public void ForEach(Action task, + OpenMode openMode = OpenMode.ForRead, + bool checkIdOk = false, + bool openErased = false, + bool openLockedLayer = false) + { + ForEach((a, b, _) => { + task.Invoke(a, b); + }, openMode, checkIdOk, openErased, openLockedLayer); + } /// - /// 遍历集合的迭代器,执行action委托 + /// 遍历集合的迭代器,执行委托(允许循环中断,输出索引值) /// - /// 要执行的委托 + /// 要执行的委托 /// 打开模式,默认为只读 /// 检查id是否删除,默认false + /// 是否打开已删除对象,默认为不打开 + /// 是否打开锁定图层对象,默认为不打开 [System.Diagnostics.DebuggerStepThrough] - public void ForEach(Action action, + public void ForEach(Action task, OpenMode openMode = OpenMode.ForRead, - bool checkIdOk = false) + bool checkIdOk = false, + bool openErased = false, + bool openLockedLayer = false) { + if (task == null) + throw new ArgumentNullException(nameof(task)); + LoopState state = new();/*这种方式比Action改Func更友好*/ + int i = 0; foreach (var id in this) { if (checkIdOk && !id.IsOk()) continue; - var record = GetRecord(id, openMode); + var record = GetRecord(id, openMode, openErased, openLockedLayer); if (record is not null) - action(record, state); - if (!state.IsRun) - break; + task.Invoke(record, state, i); + i++; } } #endregion diff --git a/tests/TestShared/Copyclip.cs b/tests/TestShared/Copyclip.cs index 270b1cc..431970f 100644 --- a/tests/TestShared/Copyclip.cs +++ b/tests/TestShared/Copyclip.cs @@ -395,7 +395,7 @@ void Paste(bool isBlock) var fileEntityIds = new List(); using (DBTrans fileTr = new(cadClipType.File, commit: false, - openMode: FileOpenMode.OpenForReadAndAllShare)) + fileOpenMode: FileOpenMode.OpenForReadAndAllShare)) { foreach (var id in fileTr.ModelSpace) if (id.IsOk()) diff --git a/tests/TestShared/TestDBTrans.cs b/tests/TestShared/TestDBTrans.cs index 005f45f..12025dd 100644 --- a/tests/TestShared/TestDBTrans.cs +++ b/tests/TestShared/TestDBTrans.cs @@ -2,6 +2,29 @@ public class TestTrans { + [CommandMethod(nameof(CmdTest_ForEachDemo))] + public static void CmdTest_ForEachDemo() + { + using DBTrans tr = new(); + + // 泛型扩展(用变量名来使用它) + tr.BlockTable.ForEach(action: (id) => { }); + tr.BlockTable.ForEach(action: (id, state) => { }); + tr.BlockTable.ForEach(action: (id, state, index) => { }); + + // 符号表扩展(会顶替泛型扩展) + tr.BlockTable.ForEach((btr) => { }, OpenMode.ForRead, true); + tr.BlockTable.ForEach((btr, state) => { }, OpenMode.ForRead, true); + tr.BlockTable.ForEach((btr, state, index) => { }, OpenMode.ForRead, true); + + // 修改:此处有缺陷:cad08会获取已经删除的块表记录,需要检查id.IsOk(),用ForEach代替 + tr.BlockTable.Change("块表记录", btr => { + }); + // 修改:此处无缺陷 + tr.BlockTable.Change(tr.ModelSpace.ObjectId, modelSpace => { + }); + } + // 后台:不存在路径的dwg会在桌面进行临时保存 [CommandMethod(nameof(FileNotExist))] public void FileNotExist() diff --git a/tests/TestShared/TestMirrorFile.cs b/tests/TestShared/TestMirrorFile.cs index f27e0c4..d2dbda9 100644 --- a/tests/TestShared/TestMirrorFile.cs +++ b/tests/TestShared/TestMirrorFile.cs @@ -12,8 +12,7 @@ public class MirrorFile [CommandMethod(nameof(CmdTest_MirrorFile))] public static void CmdTest_MirrorFile() { - using DBTrans tr = new(file, openMode: FileOpenMode.OpenForReadAndReadShare); - + using DBTrans tr = new(file, fileOpenMode: FileOpenMode.OpenForReadAndReadShare); tr.BlockTable.Change(tr.ModelSpace.ObjectId, modelSpace => { foreach (ObjectId entId in modelSpace) { diff --git a/tests/TestShared/TestXrefEx.cs b/tests/TestShared/TestXrefEx.cs index b1d2545..bb9d458 100644 --- a/tests/TestShared/TestXrefEx.cs +++ b/tests/TestShared/TestXrefEx.cs @@ -8,7 +8,7 @@ public static void Test_Bind1() { string fileName = @"D:\Test.dwg"; using var tr = new DBTrans(fileName, - openMode: FileOpenMode.OpenForReadAndAllShare/*后台绑定特别注意*/); + fileOpenMode: FileOpenMode.OpenForReadAndAllShare/*后台绑定特别注意*/); tr.XrefFactory(XrefModes.Bind); tr.SaveDwgFile(); } -- Gitee From 886bf740faaa016e0f491bc48cf2ecd7e1f2ab31 Mon Sep 17 00:00:00 2001 From: liuqihong <540762622@qq.com> Date: Fri, 14 Oct 2022 23:15:08 +0800 Subject: [PATCH 561/675] =?UTF-8?q?=E8=A1=A5=E5=85=85ForEach=E4=B8=AD?= =?UTF-8?q?=E6=96=AD=E6=93=8D=E4=BD=9C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../ExtensionMethod/DBTransEx.cs | 2 +- src/IFoxCAD.Cad.Shared/Runtime/SymbolTable.cs | 14 ++++++++------ tests/TestShared/TestAddEntity.cs | 2 +- tests/TestShared/TestDBTrans.cs | 6 +++--- 4 files changed, 13 insertions(+), 11 deletions(-) diff --git a/src/IFoxCAD.Cad.Shared/ExtensionMethod/DBTransEx.cs b/src/IFoxCAD.Cad.Shared/ExtensionMethod/DBTransEx.cs index 9af63bb..fdd4e91 100644 --- a/src/IFoxCAD.Cad.Shared/ExtensionMethod/DBTransEx.cs +++ b/src/IFoxCAD.Cad.Shared/ExtensionMethod/DBTransEx.cs @@ -40,7 +40,7 @@ public static void Purge(this DBTrans tr, SymModes sym = SymModes.All, bool excl tr.BlockTable.ForEach(tabRec => { if (!tabRec.IsFromExternalReference) ids.Add(tabRec.Id); - }); + }, checkIdOk: true); } if ((sym & SymModes.DimStyleTable) == SymModes.DimStyleTable) GetAllIds(tr, tr.DimStyleTable, ids, excludeXref); diff --git a/src/IFoxCAD.Cad.Shared/Runtime/SymbolTable.cs b/src/IFoxCAD.Cad.Shared/Runtime/SymbolTable.cs index 7423e67..28173d8 100644 --- a/src/IFoxCAD.Cad.Shared/Runtime/SymbolTable.cs +++ b/src/IFoxCAD.Cad.Shared/Runtime/SymbolTable.cs @@ -322,13 +322,13 @@ internal ObjectId GetRecordFrom(Func> tabl /// /// 要运行的委托 /// 打开模式,默认为只读 - /// 检查id是否删除,默认false + /// 检查id是否删除,默认true /// 是否打开已删除对象,默认为不打开 /// 是否打开锁定图层对象,默认为不打开 [System.Diagnostics.DebuggerStepThrough] public void ForEach(Action task, OpenMode openMode = OpenMode.ForRead, - bool checkIdOk = false, + bool checkIdOk = true, bool openErased = false, bool openLockedLayer = false) { @@ -342,13 +342,13 @@ public void ForEach(Action task, ///
    /// 要执行的委托 /// 打开模式,默认为只读 - /// 检查id是否删除,默认false + /// 检查id是否删除,默认true /// 是否打开已删除对象,默认为不打开 /// 是否打开锁定图层对象,默认为不打开 [System.Diagnostics.DebuggerStepThrough] public void ForEach(Action task, OpenMode openMode = OpenMode.ForRead, - bool checkIdOk = false, + bool checkIdOk = true, bool openErased = false, bool openLockedLayer = false) { @@ -362,13 +362,13 @@ public void ForEach(Action task, ///
    /// 要执行的委托 /// 打开模式,默认为只读 - /// 检查id是否删除,默认false + /// 检查id是否删除,默认true /// 是否打开已删除对象,默认为不打开 /// 是否打开锁定图层对象,默认为不打开 [System.Diagnostics.DebuggerStepThrough] public void ForEach(Action task, OpenMode openMode = OpenMode.ForRead, - bool checkIdOk = false, + bool checkIdOk = true, bool openErased = false, bool openLockedLayer = false) { @@ -384,6 +384,8 @@ public void ForEach(Action task, var record = GetRecord(id, openMode, openErased, openLockedLayer); if (record is not null) task.Invoke(record, state, i); + if (!state.IsRun) + break; i++; } } diff --git a/tests/TestShared/TestAddEntity.cs b/tests/TestShared/TestAddEntity.cs index 721aea8..bff17f9 100644 --- a/tests/TestShared/TestAddEntity.cs +++ b/tests/TestShared/TestAddEntity.cs @@ -230,7 +230,7 @@ public void Test_GetXdata() id.GetObject()?.Name.Print()); tr.RegAppTable.GetRecords().ForEach(rec => rec.Name.Print()); tr.RegAppTable.GetRecordNames().ForEach(name => name.Print()); - tr.RegAppTable.ForEach(re => re.Name.Print()); + tr.RegAppTable.ForEach(re => re.Name.Print(), checkIdOk: true); // var res = ed.GetEntity("\n select the entity:"); // if (res.Status == PromptStatus.OK) diff --git a/tests/TestShared/TestDBTrans.cs b/tests/TestShared/TestDBTrans.cs index 12025dd..cad90e6 100644 --- a/tests/TestShared/TestDBTrans.cs +++ b/tests/TestShared/TestDBTrans.cs @@ -13,9 +13,9 @@ public static void CmdTest_ForEachDemo() tr.BlockTable.ForEach(action: (id, state, index) => { }); // 符号表扩展(会顶替泛型扩展) - tr.BlockTable.ForEach((btr) => { }, OpenMode.ForRead, true); - tr.BlockTable.ForEach((btr, state) => { }, OpenMode.ForRead, true); - tr.BlockTable.ForEach((btr, state, index) => { }, OpenMode.ForRead, true); + tr.BlockTable.ForEach((btr) => { }, OpenMode.ForRead, checkIdOk: true); + tr.BlockTable.ForEach((btr, state) => { }, OpenMode.ForRead, checkIdOk: true); + tr.BlockTable.ForEach((btr, state, index) => { }, OpenMode.ForRead, checkIdOk: true); // 修改:此处有缺陷:cad08会获取已经删除的块表记录,需要检查id.IsOk(),用ForEach代替 tr.BlockTable.Change("块表记录", btr => { -- Gitee From 8776ff78de3c866e85aef1adc88eabbff8d86aac Mon Sep 17 00:00:00 2001 From: liuqihong <540762622@qq.com> Date: Sat, 15 Oct 2022 06:34:33 +0800 Subject: [PATCH 562/675] =?UTF-8?q?=E4=BC=98=E5=8C=96=E6=96=AD=E7=82=B9?= =?UTF-8?q?=E8=AE=BE=E7=BD=AE?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/IFoxCAD.Basal/General/LoopState.cs | 4 +- .../ExtensionMethod/CollectionEx.cs | 8 +-- .../ExtensionMethod/DBObjectEx.cs | 2 + .../ExtensionMethod/EntityEx.cs | 18 +++---- .../ExtensionMethod/ObjEx.cs | 2 +- .../ExtensionMethod/SelectionSetEx.cs | 20 ++++---- .../ExtensionMethod/SymbolTableRecordEx.cs | 51 +++++++++++++++++++ src/IFoxCAD.Cad.Shared/Runtime/IAutoGo.cs | 4 +- src/IFoxCAD.Cad.Shared/Runtime/SymbolTable.cs | 38 +++++++------- tests/TestShared/Copyclip.cs | 5 +- tests/TestShared/TestAddEntity.cs | 8 +-- tests/TestShared/TestDBTrans.cs | 25 ++++++--- tests/TestShared/TestMarshal.cs | 21 -------- tests/TestShared/TestMirrorFile.cs | 16 +++--- 14 files changed, 133 insertions(+), 89 deletions(-) diff --git a/src/IFoxCAD.Basal/General/LoopState.cs b/src/IFoxCAD.Basal/General/LoopState.cs index 44b25a6..bc9f5f5 100644 --- a/src/IFoxCAD.Basal/General/LoopState.cs +++ b/src/IFoxCAD.Basal/General/LoopState.cs @@ -1,5 +1,6 @@ namespace IFoxCAD.Basal; +#line hidden // 调试的时候跳过它 /// /// 控制循环结束 /// @@ -21,4 +22,5 @@ public class LoopState public bool IsStop => (_LoopStateFlags & PLS_STOPPED) == PLS_STOPPED; public void Stop() => _LoopStateFlags = PLS_STOPPED; public void Break() => _LoopStateFlags = PLS_BROKEN; -} \ No newline at end of file +} +#line default \ No newline at end of file diff --git a/src/IFoxCAD.Cad.Shared/ExtensionMethod/CollectionEx.cs b/src/IFoxCAD.Cad.Shared/ExtensionMethod/CollectionEx.cs index 274b119..129fb3a 100644 --- a/src/IFoxCAD.Cad.Shared/ExtensionMethod/CollectionEx.cs +++ b/src/IFoxCAD.Cad.Shared/ExtensionMethod/CollectionEx.cs @@ -81,17 +81,17 @@ public static List ToList(this ObjectIdCollection ids) +#line hidden // 调试的时候跳过它 /// /// 遍历集合,执行委托 /// /// 集合值的类型 /// 集合 /// 委托 - [System.Diagnostics.DebuggerStepThrough] public static void ForEach(this IEnumerable source, Action action) { source.ForEach((a, _, _) => { - action.Invoke(a); + action.Invoke(a);//由于此处是委托,所以 DebuggerStepThrough 特性会进入,改用预处理方式避免 }); } @@ -101,7 +101,6 @@ public static void ForEach(this IEnumerable source, Action action) /// 集合值的类型 /// 集合 /// 委托 - [System.Diagnostics.DebuggerStepThrough] public static void ForEach(this IEnumerable source, Action action) { source.ForEach((a, b, _) => { @@ -128,6 +127,9 @@ public static void ForEach(this IEnumerable source, Action
    @@ -175,5 +176,6 @@ public void Dispose() #endregion IDisposable 成员 } +#line default #endregion } \ No newline at end of file diff --git a/src/IFoxCAD.Cad.Shared/ExtensionMethod/EntityEx.cs b/src/IFoxCAD.Cad.Shared/ExtensionMethod/EntityEx.cs index 381fa1d..5225349 100644 --- a/src/IFoxCAD.Cad.Shared/ExtensionMethod/EntityEx.cs +++ b/src/IFoxCAD.Cad.Shared/ExtensionMethod/EntityEx.cs @@ -13,13 +13,11 @@ public static class EntityEx /// 二维多段线 /// 事务 /// 端点坐标集合 - public static IEnumerable GetPoints(this Polyline2d pl2d, DBTrans? tr = null) + public static IEnumerable GetPoints(this Polyline2d pl2d, DBTrans? trans = null) { - tr ??= DBTrans.Top; + trans ??= DBTrans.Top; foreach (ObjectId id in pl2d) - { - yield return tr.GetObject(id)!.Position; - } + yield return trans.GetObject(id)!.Position; } /// @@ -28,13 +26,11 @@ public static IEnumerable GetPoints(this Polyline2d pl2d, DBTrans? tr = /// 三维多段线 /// 事务 /// 端点坐标集合 - public static IEnumerable GetPoints(this Polyline3d pl3d, DBTrans? tr = null) + public static IEnumerable GetPoints(this Polyline3d pl3d, DBTrans? trans = null) { - tr ??= DBTrans.Top; + trans ??= DBTrans.Top; foreach (ObjectId id in pl3d) - { - yield return tr.GetObject(id, OpenMode.ForRead)!.Position; - } + yield return trans.GetObject(id, OpenMode.ForRead)!.Position; } /// @@ -47,7 +43,7 @@ public static IEnumerable GetPoints(this Polyline pl) return Enumerable .Range(0, pl.NumberOfVertices) - .Select(i => pl.GetPoint3dAt(i)); + .Select(pl.GetPoint3dAt); } #endregion diff --git a/src/IFoxCAD.Cad.Shared/ExtensionMethod/ObjEx.cs b/src/IFoxCAD.Cad.Shared/ExtensionMethod/ObjEx.cs index 1c90814..2ccc389 100644 --- a/src/IFoxCAD.Cad.Shared/ExtensionMethod/ObjEx.cs +++ b/src/IFoxCAD.Cad.Shared/ExtensionMethod/ObjEx.cs @@ -18,4 +18,4 @@ public static void PrintLine(this object obj) { Console.WriteLine(obj.ToString()); } -} +} \ No newline at end of file diff --git a/src/IFoxCAD.Cad.Shared/ExtensionMethod/SelectionSetEx.cs b/src/IFoxCAD.Cad.Shared/ExtensionMethod/SelectionSetEx.cs index ac5f503..432ed01 100644 --- a/src/IFoxCAD.Cad.Shared/ExtensionMethod/SelectionSetEx.cs +++ b/src/IFoxCAD.Cad.Shared/ExtensionMethod/SelectionSetEx.cs @@ -74,17 +74,17 @@ public static IEnumerable> GetObjectIdGroup(this Sel /// 图元集合 [System.Diagnostics.DebuggerStepThrough] public static IEnumerable GetEntities(this SelectionSet ss, - OpenMode openMode = OpenMode.ForRead, - DBTrans? tr = default, - bool openErased = false, - bool openLockedLayer = false) where T : Entity + OpenMode openMode = OpenMode.ForRead, + DBTrans? trans = null, + bool openErased = false, + bool openLockedLayer = false) where T : Entity { if (ss is null) throw new ArgumentNullException(nameof(ss)); - tr ??= DBTrans.Top; + trans ??= DBTrans.Top; return ss.GetObjectIds() - .Select(id => tr.GetObject(id, openMode, openErased, openLockedLayer)) + .Select(id => trans.GetObject(id, openMode, openErased, openLockedLayer)) .Where(ent => ent != null); } #endregion @@ -120,7 +120,7 @@ public static void ForEach(this SelectionSet ss, /// 选择集 /// 处理函数;(图元,终止方式) /// 打开模式 - /// 事务 + /// 事务 /// 是否打开已删除对象,默认为不打开 /// 是否打开锁定图层对象,默认为不打开 /// @@ -128,15 +128,17 @@ public static void ForEach(this SelectionSet ss, public static void ForEach(this SelectionSet ss, Action action, OpenMode openMode = OpenMode.ForRead, - DBTrans? tr = default, + DBTrans? trans = null, bool openErased = false, bool openLockedLayer = false) where T : Entity { if (action == null) throw new ArgumentNullException(nameof(action)); + trans ??= DBTrans.Top; + LoopState state = new(); - var ents = ss.GetEntities(openMode, tr, openErased, openLockedLayer); + var ents = ss.GetEntities(openMode, trans, openErased, openLockedLayer); foreach (var ent in ents) { action.Invoke(ent, state); diff --git a/src/IFoxCAD.Cad.Shared/ExtensionMethod/SymbolTableRecordEx.cs b/src/IFoxCAD.Cad.Shared/ExtensionMethod/SymbolTableRecordEx.cs index 2b85498..808df66 100644 --- a/src/IFoxCAD.Cad.Shared/ExtensionMethod/SymbolTableRecordEx.cs +++ b/src/IFoxCAD.Cad.Shared/ExtensionMethod/SymbolTableRecordEx.cs @@ -321,6 +321,7 @@ public static ObjectId AddArc(this BlockTableRecord btr, #region 获取实体/实体id /// /// 获取块表记录内的指定类型的实体 + /// (此处不会检查id.IsOk()) /// /// 实体类型 /// 块表记录 @@ -470,4 +471,54 @@ public static ObjectId InsertBlock(this BlockTableRecord blockTableRecord, #endregion #endregion + + #region 遍历 +#line hidden // 调试的时候跳过它 + /// + /// 遍历符号表记录,执行委托 + /// + /// 要运行的委托 + public static void ForEach(this TRecord record, Action task) + where TRecord : SymbolTableRecord, IEnumerable + { + ForEach(record, (a, _, _) => { + task.Invoke(a);//由于此处是委托,所以 DebuggerStepThrough 特性会进入,改用预处理方式避免 + }); + } + + /// + /// 遍历符号表记录,执行委托(允许循环中断) + /// + /// 要执行的委托 + public static void ForEach(this TRecord record, Action task) + where TRecord : SymbolTableRecord, IEnumerable + { + ForEach(record, (a, b, _) => { + task.Invoke(a, b); + }); + } + + /// + /// 遍历符号表记录,执行委托(允许循环中断,输出索引值) + /// + /// 要执行的委托 + [System.Diagnostics.DebuggerStepThrough] + public static void ForEach(this TRecord record, Action task) + where TRecord : SymbolTableRecord, IEnumerable + { + if (task == null) + throw new ArgumentNullException(nameof(task)); + + LoopState state = new();/*这种方式比Action改Func更友好*/ + int i = 0; + foreach (ObjectId id in record) + { + task.Invoke(id, state, i); + if (!state.IsRun) + break; + i++; + } + } + #endregion +#line default } \ No newline at end of file diff --git a/src/IFoxCAD.Cad.Shared/Runtime/IAutoGo.cs b/src/IFoxCAD.Cad.Shared/Runtime/IAutoGo.cs index b035381..7eed0b2 100644 --- a/src/IFoxCAD.Cad.Shared/Runtime/IAutoGo.cs +++ b/src/IFoxCAD.Cad.Shared/Runtime/IAutoGo.cs @@ -316,7 +316,7 @@ static void RunFunctions(List runClassList) /// public static void DebugCheckCmdRecurrence() { - HashSet keys = new HashSet(); + HashSet keys = new(); AutoReflection.AppDomainGetTypes(type => { if (type.IsAbstract) return; @@ -336,7 +336,7 @@ public static void DebugCheckCmdRecurrence() } } } - }); + }, Assembly.GetCallingAssembly().GetName().Name); } #endif } \ No newline at end of file diff --git a/src/IFoxCAD.Cad.Shared/Runtime/SymbolTable.cs b/src/IFoxCAD.Cad.Shared/Runtime/SymbolTable.cs index 28173d8..1a0c0ef 100644 --- a/src/IFoxCAD.Cad.Shared/Runtime/SymbolTable.cs +++ b/src/IFoxCAD.Cad.Shared/Runtime/SymbolTable.cs @@ -164,27 +164,20 @@ public void Remove(ObjectId id) #endregion #region 修改符号表记录 - /// - /// 修改符号表 - /// - /// 符号表记录 - /// 修改委托 - static void Change(TRecord record, Action action) - { - using (record.ForWrite()) - action.Invoke(record); - } - /// /// 修改符号表 /// /// 符号表记录名 /// 修改委托 + [System.Diagnostics.DebuggerStepThrough] public void Change(string name, Action action) { var record = GetRecord(name); if (record is not null) - Change(record, action); + { + using (record.ForWrite()) + action.Invoke(record); + } } /// @@ -192,11 +185,15 @@ public void Change(string name, Action action) /// /// 符号表记录id /// 修改委托 + [System.Diagnostics.DebuggerStepThrough] public void Change(ObjectId id, Action action) { var record = GetRecord(id); if (record is not null) - Change(record, action); + { + using (record.ForWrite()) + action.Invoke(record); + } } #endregion @@ -209,6 +206,7 @@ public void Change(ObjectId id, Action action) /// 是否打开已删除对象,默认为不打开 /// 是否打开锁定图层对象,默认为不打开 /// 符号表记录 + [System.Diagnostics.DebuggerStepThrough] public TRecord? GetRecord(ObjectId id, OpenMode openMode = OpenMode.ForRead, bool openErased = false, @@ -225,6 +223,7 @@ public void Change(ObjectId id, Action action) /// 是否打开已删除对象,默认为不打开 /// 是否打开锁定图层对象,默认为不打开 /// 符号表记录 + [System.Diagnostics.DebuggerStepThrough] public TRecord? GetRecord(string name, OpenMode openMode = OpenMode.ForRead, bool openErased = false, @@ -317,15 +316,15 @@ internal ObjectId GetRecordFrom(Func> tabl #endregion #region 遍历 +#line hidden // 调试的时候跳过它 /// - /// 遍历集合的迭代器,执行委托 + /// 遍历符号表,执行委托 /// /// 要运行的委托 /// 打开模式,默认为只读 /// 检查id是否删除,默认true /// 是否打开已删除对象,默认为不打开 /// 是否打开锁定图层对象,默认为不打开 - [System.Diagnostics.DebuggerStepThrough] public void ForEach(Action task, OpenMode openMode = OpenMode.ForRead, bool checkIdOk = true, @@ -333,19 +332,18 @@ public void ForEach(Action task, bool openLockedLayer = false) { ForEach((a, _, _) => { - task.Invoke(a); + task.Invoke(a);//由于此处是委托,所以 DebuggerStepThrough 特性会进入,改用预处理方式避免 }, openMode, checkIdOk, openErased, openLockedLayer); } /// - /// 遍历集合的迭代器,执行委托(允许循环中断) + /// 遍历符号表,执行委托(允许循环中断) /// /// 要执行的委托 /// 打开模式,默认为只读 /// 检查id是否删除,默认true /// 是否打开已删除对象,默认为不打开 /// 是否打开锁定图层对象,默认为不打开 - [System.Diagnostics.DebuggerStepThrough] public void ForEach(Action task, OpenMode openMode = OpenMode.ForRead, bool checkIdOk = true, @@ -358,7 +356,7 @@ public void ForEach(Action task, } /// - /// 遍历集合的迭代器,执行委托(允许循环中断,输出索引值) + /// 遍历符号表,执行委托(允许循环中断,输出索引值) /// /// 要执行的委托 /// 打开模式,默认为只读 @@ -389,6 +387,8 @@ public void ForEach(Action task, i++; } } +#line default + #endregion #region IEnumerable 成员 diff --git a/tests/TestShared/Copyclip.cs b/tests/TestShared/Copyclip.cs index 431970f..441afc9 100644 --- a/tests/TestShared/Copyclip.cs +++ b/tests/TestShared/Copyclip.cs @@ -397,9 +397,10 @@ void Paste(bool isBlock) commit: false, fileOpenMode: FileOpenMode.OpenForReadAndAllShare)) { - foreach (var id in fileTr.ModelSpace) + fileTr.ModelSpace.ForEach(id => { if (id.IsOk()) fileEntityIds.Add(id); + }); } if (fileEntityIds.Count == 0) return; @@ -690,7 +691,7 @@ static string CreateTempFileName(string format = "X") // if (action == null) // throw new ArgumentNullException(nameof(action)); -// tr ??= DBTrans.Top; +// trans ??= DBTrans.Top; // var btr = tr.GetObject(brf.BlockTableRecord); // if (btr == null) diff --git a/tests/TestShared/TestAddEntity.cs b/tests/TestShared/TestAddEntity.cs index bff17f9..0b4d37d 100644 --- a/tests/TestShared/TestAddEntity.cs +++ b/tests/TestShared/TestAddEntity.cs @@ -230,7 +230,7 @@ public void Test_GetXdata() id.GetObject()?.Name.Print()); tr.RegAppTable.GetRecords().ForEach(rec => rec.Name.Print()); tr.RegAppTable.GetRecordNames().ForEach(name => name.Print()); - tr.RegAppTable.ForEach(re => re.Name.Print(), checkIdOk: true); + tr.RegAppTable.ForEach(re => re.Name.Print(), checkIdOk: false); // var res = ed.GetEntity("\n select the entity:"); // if (res.Status == PromptStatus.OK) @@ -311,6 +311,7 @@ public void Test_Rec() const double pi90 = Math.PI / 2; + pi90.Print(); Tools.TestTimes(1000000, "对角线", () => { var result = false; @@ -322,13 +323,10 @@ public void Test_Rec() Tools.TestTimes(1000000, "三次点乘", () => { var result = false; - if (Math.Abs(p12.DotProduct(p23)) < 1e8 && Math.Abs(p23.DotProduct(p34)) < 1e8 && Math.Abs(p34.DotProduct(p41)) < 1e8) - { result = true; - } }); Tools.TestTimes(1000000, "三次垂直", () => { @@ -336,9 +334,7 @@ public void Test_Rec() if (p12.IsParallelTo(p23) && p23.IsParallelTo(p34) && p34.IsParallelTo(p41)) - { result = true; - } }); } diff --git a/tests/TestShared/TestDBTrans.cs b/tests/TestShared/TestDBTrans.cs index cad90e6..d73d783 100644 --- a/tests/TestShared/TestDBTrans.cs +++ b/tests/TestShared/TestDBTrans.cs @@ -13,15 +13,28 @@ public static void CmdTest_ForEachDemo() tr.BlockTable.ForEach(action: (id, state, index) => { }); // 符号表扩展(会顶替泛型扩展) - tr.BlockTable.ForEach((btr) => { }, OpenMode.ForRead, checkIdOk: true); - tr.BlockTable.ForEach((btr, state) => { }, OpenMode.ForRead, checkIdOk: true); - tr.BlockTable.ForEach((btr, state, index) => { }, OpenMode.ForRead, checkIdOk: true); + tr.BlockTable.ForEach((btr) => { // 预处理设置不进入ForEach函数体内 + btr.Print();// 此处可以设置断点 + }, OpenMode.ForRead, checkIdOk: true); + tr.BlockTable.ForEach((btr, state) => {// 预处理设置不进入ForEach函数体内 + btr.Print();// 此处可以设置断点 + }, OpenMode.ForRead, checkIdOk: true); + tr.BlockTable.ForEach((btr, state, index) => { // 预处理设置不进入ForEach函数体内 + btr.Print();// 此处可以设置断点 + }, OpenMode.ForRead, checkIdOk: true); // 修改:此处有缺陷:cad08会获取已经删除的块表记录,需要检查id.IsOk(),用ForEach代替 - tr.BlockTable.Change("块表记录", btr => { - }); + // tr.BlockTable.Change("块表记录", btr => { + // }); + // 修改:此处无缺陷 - tr.BlockTable.Change(tr.ModelSpace.ObjectId, modelSpace => { + tr.BlockTable.Change(tr.ModelSpace.ObjectId, modelSpace => { // 特性设置不进入函数体内 + var ents = modelSpace.GetEntities(); // 此处不会检查id.IsOk() + + modelSpace.ForEach(id => { // 利用遍历检查id.IsOk() + if (id.IsOk()) + id.Print(); + }); }); } diff --git a/tests/TestShared/TestMarshal.cs b/tests/TestShared/TestMarshal.cs index 7d52536..07fc3f2 100644 --- a/tests/TestShared/TestMarshal.cs +++ b/tests/TestShared/TestMarshal.cs @@ -4,27 +4,6 @@ namespace TestShared; public class TestMarshal { - [CommandMethod(nameof(Test_DebuggerStepThrough))] - public void Test_DebuggerStepThrough() - { - DebuggerStepThrough(() => { - for (int i = 0; i < 10; i++)//断点可以进入此处 - { - } - }); - } - - [System.Diagnostics.DebuggerStepThrough] - public void DebuggerStepThrough(Action action) - { - //throw new ArgumentNullException(nameof(action));//可以抛出 - int a = 0;//断点无法进入此处 - int b = 0; - action?.Invoke(); - int c = 0; - int d = 0; - } - [CommandMethod(nameof(Test_ImplicitPoint3D))] public void Test_ImplicitPoint3D() { diff --git a/tests/TestShared/TestMirrorFile.cs b/tests/TestShared/TestMirrorFile.cs index d2dbda9..5bdb2bb 100644 --- a/tests/TestShared/TestMirrorFile.cs +++ b/tests/TestShared/TestMirrorFile.cs @@ -1,4 +1,6 @@ -namespace Test; +using Autodesk.AutoCAD.DatabaseServices; + +namespace Test; public class MirrorFile { @@ -14,11 +16,10 @@ public static void CmdTest_MirrorFile() { using DBTrans tr = new(file, fileOpenMode: FileOpenMode.OpenForReadAndReadShare); tr.BlockTable.Change(tr.ModelSpace.ObjectId, modelSpace => { - foreach (ObjectId entId in modelSpace) - { + modelSpace.ForEach(entId => { var dbText = tr.GetObject(entId, OpenMode.ForRead)!; if (dbText is null) - continue; + return; dbText.UpgradeOpen(); var pos = dbText.Position; @@ -27,7 +28,7 @@ public static void CmdTest_MirrorFile() dbText.Mirror(Point3d.Origin, new Point3d(0, 1, 0)); // text.Move(Point3d.Origin, pos); dbText.DowngradeOpen(); - } + }); }); var ver = (DwgVersion)27;/*AC1021 AutoCAD 2007/2008/2009.*/ tr.Database.SaveAs(fileSave, ver); @@ -45,8 +46,7 @@ public static void CmdTest_MirrorFile2() tr.Task(() => { var yaxis = new Point3d(0, 1, 0); tr.BlockTable.Change(tr.ModelSpace.ObjectId, modelSpace => { - foreach (ObjectId entId in modelSpace) - { + modelSpace.ForEach(entId => { var entity = tr.GetObject(entId, OpenMode.ForWrite)!; if (entity is DBText dbText) { @@ -66,7 +66,7 @@ public static void CmdTest_MirrorFile2() }; dbText.AdjustAlignment(tr.Database); } - } + }); }); }); tr.Database.SaveAs(fileSave, (DwgVersion)27 /*AC1021 AutoCAD 2007/2008/2009.*/); -- Gitee From 4f8dd131b307b778dd29672fb86f60a0d643e5ff Mon Sep 17 00:00:00 2001 From: liuqihong <540762622@qq.com> Date: Sun, 16 Oct 2022 21:17:12 +0800 Subject: [PATCH 563/675] =?UTF-8?q?cad=E5=B7=A5=E7=A8=8B=E6=97=A0=E6=B3=95?= =?UTF-8?q?=E6=96=AD=E7=82=B9=E8=BF=9B=E5=85=A5=E5=A7=94=E6=89=98=E8=80=8C?= =?UTF-8?q?=E4=BC=9A=E8=B7=B3=E8=BF=87?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../ExtensionMethod/CollectionEx.cs | 37 +++++++---- tests/TestConsole/Program.cs | 62 ++++++++++++++++++- tests/TestShared/TestDBTrans.cs | 27 ++++++-- 3 files changed, 108 insertions(+), 18 deletions(-) diff --git a/src/IFoxCAD.Cad.Shared/ExtensionMethod/CollectionEx.cs b/src/IFoxCAD.Cad.Shared/ExtensionMethod/CollectionEx.cs index 129fb3a..36ec072 100644 --- a/src/IFoxCAD.Cad.Shared/ExtensionMethod/CollectionEx.cs +++ b/src/IFoxCAD.Cad.Shared/ExtensionMethod/CollectionEx.cs @@ -1,5 +1,7 @@ using System.ComponentModel; +using System.Diagnostics; using System.Xml.Linq; +using static System.Windows.Forms.AxHost; namespace IFoxCAD.Cad; @@ -79,20 +81,22 @@ public static List ToList(this ObjectIdCollection ids) } - - -#line hidden // 调试的时候跳过它 /// /// 遍历集合,执行委托 /// /// 集合值的类型 /// 集合 /// 委托 + //[DebuggerHidden] + [DebuggerStepThrough] public static void ForEach(this IEnumerable source, Action action) { - source.ForEach((a, _, _) => { - action.Invoke(a);//由于此处是委托,所以 DebuggerStepThrough 特性会进入,改用预处理方式避免 - }); + // 这里不要嵌套调用ForEach,因为断点设置的原因 + //source.ForEach((a, _, _) => { + // action.Invoke(a);//由于此处是委托,所以 DebuggerStepThrough 特性会进入,改用预处理方式避免 + //}); + foreach (var element in source) + action.Invoke(element); } /// @@ -101,11 +105,21 @@ public static void ForEach(this IEnumerable source, Action action) /// 集合值的类型 /// 集合 /// 委托 + [DebuggerStepThrough] public static void ForEach(this IEnumerable source, Action action) { - source.ForEach((a, b, _) => { - action.Invoke(a, b); - }); + // 这里不要嵌套调用ForEach,因为断点设置的原因 + //source.ForEach((a, b, _) => { + // action.Invoke(a, b); + //}); + + LoopState state = new();/*这种方式比Action改Func更友好*/ + foreach (var element in source) + { + action.Invoke(element, state); + if (!state.IsRun) + break; + } } /// @@ -114,7 +128,7 @@ public static void ForEach(this IEnumerable source, Action a /// 集合值的类型 /// 集合 /// 委托 - [System.Diagnostics.DebuggerStepThrough] + [DebuggerStepThrough] public static void ForEach(this IEnumerable source, Action action) { LoopState state = new();/*这种方式比Action改Func更友好*/ @@ -127,9 +141,6 @@ public static void ForEach(this IEnumerable source, Action { + Console.WriteLine(a + b); + }); + + var a = new int[] { 1, 2, 3, 4, 5, 6, 78, 9, 92, }; + a.ForEach(a => { + Console.WriteLine(a); + }); + } + + [DebuggerHidden] + private static int AddTwoNumbers(int nNum1, int nNum2) + { + return Add(nNum1, nNum2); + } + private static int Add(int op1, int op2) + { + return op1 + op2; + } + + [DebuggerHidden] + private static void AddTwoNumbers22(Action action) + { + action(10, 20); + } + } + + public static class Fors + { + /// + /// 遍历集合,执行委托 + /// + /// 集合值的类型 + /// 集合 + /// 委托 + [DebuggerHidden] + public static void ForEach(this IEnumerable source, Action action) + { + foreach (var element in source) + { + action.Invoke(element); + } + } + } +} +#if true2 Console.WriteLine("***************************************************"); @@ -94,4 +153,5 @@ public enum Season : byte Autumn, Winter } -#endregion \ No newline at end of file +#endregion +#endif \ No newline at end of file diff --git a/tests/TestShared/TestDBTrans.cs b/tests/TestShared/TestDBTrans.cs index d73d783..5fb6633 100644 --- a/tests/TestShared/TestDBTrans.cs +++ b/tests/TestShared/TestDBTrans.cs @@ -1,4 +1,6 @@ -namespace Test; +using System.Diagnostics; + +namespace Test; public class TestTrans { @@ -8,9 +10,24 @@ public static void CmdTest_ForEachDemo() using DBTrans tr = new(); // 泛型扩展(用变量名来使用它) - tr.BlockTable.ForEach(action: (id) => { }); - tr.BlockTable.ForEach(action: (id, state) => { }); - tr.BlockTable.ForEach(action: (id, state, index) => { }); + tr.BlockTable.ForEach(action: (id) => { + //Debugger.Break();// 为什么cad工程不能断点进入呢? + id.Print(); + Console.WriteLine(id); + }); + + //tr.BlockTable.ForEach(asdad); + //void asdad(object id) + //{ + // id.Print(); + //} + + tr.BlockTable.ForEach(action: (id, state) => { + id.Print(); + }); + tr.BlockTable.ForEach(action: (id, state, index) => { + id.Print(); + }); // 符号表扩展(会顶替泛型扩展) tr.BlockTable.ForEach((btr) => { // 预处理设置不进入ForEach函数体内 @@ -38,6 +55,8 @@ public static void CmdTest_ForEachDemo() }); } + + // 后台:不存在路径的dwg会在桌面进行临时保存 [CommandMethod(nameof(FileNotExist))] public void FileNotExist() -- Gitee From 23bf43faca609998a3eb808fd4659872be5c7b17 Mon Sep 17 00:00:00 2001 From: liuqihong <540762622@qq.com> Date: Mon, 17 Oct 2022 20:46:54 +0800 Subject: [PATCH 564/675] =?UTF-8?q?=E8=87=AA=E5=8A=A8=E6=89=A7=E8=A1=8C?= =?UTF-8?q?=E7=89=B9=E6=80=A7=E5=85=81=E8=AE=B8=E9=9D=99=E6=80=81=E8=B0=83?= =?UTF-8?q?=E7=94=A8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/IFoxCAD.Cad.Shared/Runtime/IAutoGo.cs | 27 ++++-- tests/TestShared/CmdINI.cs | 110 ++++++++++------------ 2 files changed, 69 insertions(+), 68 deletions(-) diff --git a/src/IFoxCAD.Cad.Shared/Runtime/IAutoGo.cs b/src/IFoxCAD.Cad.Shared/Runtime/IAutoGo.cs index 7eed0b2..218a19d 100644 --- a/src/IFoxCAD.Cad.Shared/Runtime/IAutoGo.cs +++ b/src/IFoxCAD.Cad.Shared/Runtime/IAutoGo.cs @@ -1,5 +1,6 @@ namespace IFoxCAD.Cad; +using System.Collections.ObjectModel; using System.Diagnostics; /// @@ -200,7 +201,7 @@ public static void AppDomainGetTypes(Action action, string? dllNameWithout #if DEBUG ++error; #endif - action(type); + action.Invoke(type); } } } @@ -222,17 +223,15 @@ public static void AppDomainGetTypes(Action action, string? dllNameWithout /// void GetInterfaceFunctions(List runClassList, string methodName) { - const string sqid = nameof(Sequence) + "Id"; - AppDomainGetTypes(type => { + // 接口的静态类屏蔽,继承接口无法使用静态类,因此跳过 if (type.IsAbstract) return; var ints = type.GetInterfaces(); for (int sss = 0; sss < ints.Length; sss++) { - var inters = ints[sss]; - if (inters.Name != nameof(IFoxAutoGo)) + if (ints[sss].Name != nameof(IFoxAutoGo)) continue; Sequence? sequence = null; @@ -242,19 +241,20 @@ void GetInterfaceFunctions(List runClassList, string methodName) for (int jj = 0; jj < mets.Length; jj++) { var method = mets[jj]; + + // 接口的静态方法屏蔽,继承的方法也不可能是静态的,因此跳过 if (method.IsAbstract) continue; - if (method.Name == sqid) + if (method.Name == nameof(IFoxAutoGo.SequenceId)) { var obj = method.Invoke(); if (obj is not null) sequence = (Sequence)obj; continue; } - else if (method.Name == methodName) + if (method.Name == methodName) initialize = method; - if (initialize is not null && sequence is not null) break; } @@ -275,13 +275,22 @@ void GetInterfaceFunctions(List runClassList, string methodName) void GetAttributeFunctions(List initialize, List terminate) { AppDomainGetTypes(type => { - if (type.IsAbstract) + if (!type.IsClass) return; + // 特性的静态类不屏蔽 + //if (type.IsAbstract) + // return; + var mets = type.GetMethods(); for (int ii = 0; ii < mets.Length; ii++) { var method = mets[ii]; + + // 特性的静态方法不屏蔽 + //if (method.IsAbstract) + // continue; + var attr = method.GetCustomAttributes(true); for (int jj = 0; jj < attr.Length; jj++) { diff --git a/tests/TestShared/CmdINI.cs b/tests/TestShared/CmdINI.cs index 8558a4d..8dc09c2 100644 --- a/tests/TestShared/CmdINI.cs +++ b/tests/TestShared/CmdINI.cs @@ -1,6 +1,3 @@ -using System.Diagnostics; -using System.Linq; - namespace Test; /// @@ -27,13 +24,8 @@ public class AutoRegAssemEx : AutoRegAssem { public AutoRegAssemEx() : base(AutoRegConfig.All) { - var dm = Acap.DocumentManager; - var doc = dm.MdiActiveDocument; - var ed = doc.Editor; - ed.WriteMessage($"\n {nameof(AutoRegAssemEx)}构造函数,开始自动执行\r\n"); - + Env.Printl($"{nameof(AutoRegAssemEx)}构造函数,开始自动执行\r\n"); CmdInit.AutoRegAssemEx = this; - #if Debug // 此处用来反射本程序集,检查是否存在重复命令 AutoReflection.DebugCheckCmdRecurrence(); @@ -41,6 +33,7 @@ public AutoRegAssemEx() : base(AutoRegConfig.All) } } + public class CmdInit { public static AutoRegAssemEx? AutoRegAssemEx; @@ -51,13 +44,9 @@ public class CmdInit [CommandMethod(nameof(IFoxAddReg))] public void IFoxAddReg() { - var dm = Acap.DocumentManager; - var doc = dm.MdiActiveDocument; - var ed = doc.Editor; - ed.WriteMessage($"\n 加入注册表"); + Env.Printl($"加入注册表"); - if (AutoRegAssemEx is null) - AutoRegAssemEx = new(); + AutoRegAssemEx ??= new(); AutoRegAssemEx.RegApp(); } @@ -67,10 +56,7 @@ public void IFoxAddReg() [CommandMethod(nameof(IFoxRemoveReg))] public void IFoxRemoveReg() { - var dm = Acap.DocumentManager; - var doc = dm.MdiActiveDocument; - var ed = doc.Editor; - ed.WriteMessage($"\n 卸载注册表"); + Env.Printl($"卸载注册表"); // 防止卸载两次,不然会报错的 AutoRegAssemEx?.UnRegApp(); @@ -78,64 +64,70 @@ public void IFoxRemoveReg() } } - /* - * 自动执行特性例子: + * 自动执行:特性 */ public class Cmd_IFoxInitialize { [IFoxInitialize] - public void NameCasual() + public void Initialize() { - var dm = Acap.DocumentManager; - var doc = dm.MdiActiveDocument; - var ed = doc.Editor; - ed.WriteMessage("\n 开始自动执行 可以分开多个类和多个函数 \r\n"); + Env.Printl($"开始自动执行,可以分开多个类和多个函数:{nameof(Cmd_IFoxInitialize)}.{nameof(Initialize)}"); } [IFoxInitialize] - public void NameCasualtest() + public void Initialize2() { - var dm = Acap.DocumentManager; - var doc = dm.MdiActiveDocument; - var ed = doc.Editor; - ed.WriteMessage("\n 开始自动执行 又一次测试 \r\n"); + Env.Printl($"开始自动执行,可以分开多个类和多个函数,又一次测试:{nameof(Cmd_IFoxInitialize)}.{nameof(Initialize2)}"); } + //[IFoxInitialize(isInitialize: false)] + //public void Terminate() + //{ + // try + // { + // // 注意此时编辑器已经回收,所以此句引发错误 + // // 您可以写一些其他的释放动作,例如资源回收之类的 + // Env.Printl($"\n 结束自动执行 Terminate \r\n"); + // } + // catch (System.Exception e) + // { + // System.Windows.Forms.MessageBox.Show(e.Message); + // } + //} + [IFoxInitialize] + public static void StaticInitialize() + { + Env.Printl($"开始自动执行,静态调用:{nameof(Cmd_IFoxInitialize)}.{nameof(StaticInitialize)}"); + } +} + +/* + * 自动执行:接口 + */ +public class Cmd_IFoxInitializeInterface : IFoxAutoGo +{ public void Initialize() { - var dm = Acap.DocumentManager; - var doc = dm.MdiActiveDocument; - var ed = doc.Editor; - ed.WriteMessage("\n 开始自动执行 Initialize \r\n"); + Env.Printl($"开始自动执行,接口调用:{nameof(Cmd_IFoxInitializeInterface)}.{nameof(Initialize)}"); } - [IFoxInitialize(isInitialize: false)] - public void Terminate() + public Sequence SequenceId() { - // try - // { - // var dm = Acap.DocumentManager; - // var doc = dm.MdiActiveDocument; - // var ed = doc.Editor; // 注意此时编辑器已经回收,所以此句没用,并引发错误 - // ed.WriteMessage("\n 结束自动执行 Terminate \r\n"); - // } - // catch (System.Exception) - // { - // } + return Sequence.Last; } - // [IFoxInitialize] - // public void Initialize() - // { - // // 文档管理器将比此接口前创建,因此此句会执行 - // Acap.DocumentManager.MdiActiveDocument.Editor.WriteMessage("\nload...."); - // } - // [IFoxInitialize(Sequence.First, false)] - // public void Terminate() - // { - // // 文档管理器将比此接口前死亡,因此此句不会执行 - // Acap.DocumentManager.MdiActiveDocument?.Editor.WriteMessage("\nunload...."); - // } + public void Terminate() + { + // try + // { + // // 注意此时编辑器已经回收,所以此句没用,并引发错误 + // Env.Printl($"结束自动执行 {nameof(Cmd_IFoxInitializeInterface)}.Terminate \r\n"); + // } + // catch (System.Exception e) + // { + // System.Windows.Forms.MessageBox.Show(e.Message); + // } + } } \ No newline at end of file -- Gitee From 74588004ec96c72392d3f070425ddc118e2197ea Mon Sep 17 00:00:00 2001 From: liuqihong <540762622@qq.com> Date: Mon, 17 Oct 2022 21:31:08 +0800 Subject: [PATCH 565/675] =?UTF-8?q?=E6=A3=80=E6=B5=8B=E5=91=BD=E4=BB=A4?= =?UTF-8?q?=E9=87=8D=E5=A4=8D?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Runtime/AutoRegAssem.cs | 4 +-- src/IFoxCAD.Cad.Shared/Runtime/IAutoGo.cs | 33 ++++++++++++++++--- 2 files changed, 29 insertions(+), 8 deletions(-) diff --git a/src/IFoxCAD.Cad.Shared/Runtime/AutoRegAssem.cs b/src/IFoxCAD.Cad.Shared/Runtime/AutoRegAssem.cs index 91ab6d1..9195d09 100644 --- a/src/IFoxCAD.Cad.Shared/Runtime/AutoRegAssem.cs +++ b/src/IFoxCAD.Cad.Shared/Runtime/AutoRegAssem.cs @@ -165,9 +165,7 @@ public void RegApp() // 这里的是不会自动执行的 public void Initialize() { } - public void Terminate() - { - } + public void Terminate() { } ~AutoRegAssem() { diff --git a/src/IFoxCAD.Cad.Shared/Runtime/IAutoGo.cs b/src/IFoxCAD.Cad.Shared/Runtime/IAutoGo.cs index 218a19d..26f3de5 100644 --- a/src/IFoxCAD.Cad.Shared/Runtime/IAutoGo.cs +++ b/src/IFoxCAD.Cad.Shared/Runtime/IAutoGo.cs @@ -326,26 +326,49 @@ static void RunFunctions(List runClassList) public static void DebugCheckCmdRecurrence() { HashSet keys = new(); - AutoReflection.AppDomainGetTypes(type => { - if (type.IsAbstract) - return; + // 本dll中存在冲突命令,此时cad自动接口可以运行,但是加载命令之后会报错,因此利用断点告诉程序员 + AutoReflection.AppDomainGetTypes(type => { var mets = type.GetMethods(); for (int ii = 0; ii < mets.Length; ii++) { var method = mets[ii]; var attr = method.GetCustomAttributes(true); for (int jj = 0; jj < attr.Length; jj++) - { if (attr[jj] is CommandMethodAttribute att) { if (keys.Contains(att.GlobalName)) Debugger.Break(); keys.Add(att.GlobalName); } - } } }, Assembly.GetCallingAssembly().GetName().Name); + + // 其他dll中存在冲突命令,此时会覆盖命令,友好的提示程序员 + keys.Clear(); + HashSet msgMod = new(); + AutoReflection.AppDomainGetTypes(type => { + var mets = type.GetMethods(); + for (int ii = 0; ii < mets.Length; ii++) + { + var method = mets[ii]; + var attr = method.GetCustomAttributes(true); + for (int jj = 0; jj < attr.Length; jj++) + if (attr[jj] is CommandMethodAttribute att) + { + if (keys.Contains(att.GlobalName)) + msgMod.Add(att.GlobalName); + keys.Add(att.GlobalName); + } + } + }); + var sb = new StringBuilder(); + foreach (string key in msgMod) + sb.AppendLine(key); + Env.Printl("当前cad环境加载的多个DLL中存在重复命令将被覆盖:"); + Env.Printl("{"); + Env.Printl(sb.ToString()); + Env.Printl("}"); } #endif } \ No newline at end of file -- Gitee From 03197d7309c45dd269367267a51caee535fabe96 Mon Sep 17 00:00:00 2001 From: liuqihong <540762622@qq.com> Date: Mon, 17 Oct 2022 23:30:35 +0800 Subject: [PATCH 566/675] =?UTF-8?q?=E4=BC=98=E5=8C=96=E4=BB=A3=E7=A0=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../ExtensionMethod/CurveEx.cs | 4 -- .../ExtensionMethod/DBDictionaryEx.cs | 2 +- .../ExtensionMethod/ObjectIdEx.cs | 16 ++--- .../ExtensionMethod/SymbolTableRecordEx.cs | 68 +++++++++---------- 4 files changed, 41 insertions(+), 49 deletions(-) diff --git a/src/IFoxCAD.Cad.Shared/ExtensionMethod/CurveEx.cs b/src/IFoxCAD.Cad.Shared/ExtensionMethod/CurveEx.cs index cc21fd2..68b286f 100644 --- a/src/IFoxCAD.Cad.Shared/ExtensionMethod/CurveEx.cs +++ b/src/IFoxCAD.Cad.Shared/ExtensionMethod/CurveEx.cs @@ -621,13 +621,9 @@ public static CompositeCurve3d ToCurve3d(this Polyline pl) break; } if (nc3d is null) - { nc3d = nc3dtemp; - } else if (nc3dtemp is not null) - { nc3d.JoinWith(nc3dtemp); - } } return nc3d; } diff --git a/src/IFoxCAD.Cad.Shared/ExtensionMethod/DBDictionaryEx.cs b/src/IFoxCAD.Cad.Shared/ExtensionMethod/DBDictionaryEx.cs index 1cd3845..7fea5c1 100644 --- a/src/IFoxCAD.Cad.Shared/ExtensionMethod/DBDictionaryEx.cs +++ b/src/IFoxCAD.Cad.Shared/ExtensionMethod/DBDictionaryEx.cs @@ -108,7 +108,7 @@ public static void SetXRecord(this DBDictionary dict, string key, XRecordDataLis id = obj.ExtensionDictionary; } - return id.GetObject(tr: trans); + return id.GetObject(trans: trans); } #region 数据表 diff --git a/src/IFoxCAD.Cad.Shared/ExtensionMethod/ObjectIdEx.cs b/src/IFoxCAD.Cad.Shared/ExtensionMethod/ObjectIdEx.cs index fa64a72..a329da5 100644 --- a/src/IFoxCAD.Cad.Shared/ExtensionMethod/ObjectIdEx.cs +++ b/src/IFoxCAD.Cad.Shared/ExtensionMethod/ObjectIdEx.cs @@ -6,25 +6,24 @@ public static class ObjectIdEx { #region GetObject - /// /// 获取指定类型对象 /// /// 指定的泛型 /// 对象id /// 打开模式 - /// 事务 + /// 事务 /// 是否打开已删除对象,默认为不打开 /// 是否打开锁定图层对象,默认为不打开 /// 指定类型对象 public static T? GetObject(this ObjectId id, OpenMode openMode = OpenMode.ForRead, - Transaction? tr = default, + Transaction? trans = null, bool openErased = false, bool openLockedLayer = false) where T : DBObject { - tr ??= DBTrans.Top.Transaction; - return tr.GetObject(id, openMode, openErased, openLockedLayer) as T; + trans ??= DBTrans.Top.Transaction; + return trans.GetObject(id, openMode, openErased, openLockedLayer) as T; } /// @@ -33,18 +32,19 @@ public static class ObjectIdEx /// 指定的泛型 /// 对象id集合 /// 打开模式 - /// 事务 + /// 事务 /// 是否打开已删除对象,默认为不打开 /// 是否打开锁定图层对象,默认为不打开 /// 指定类型对象集合 [System.Diagnostics.DebuggerStepThrough] public static IEnumerable GetObject(this IEnumerable ids, OpenMode openMode = OpenMode.ForRead, - Transaction? tr = default, + Transaction? trans = null, bool openErased = false, bool openLockedLayer = false) where T : DBObject { - return ids.Select(id => id.GetObject(openMode, tr, openErased, openLockedLayer)); + trans ??= DBTrans.Top.Transaction; + return ids.Select(id => id.GetObject(openMode, trans, openErased, openLockedLayer)); } /// diff --git a/src/IFoxCAD.Cad.Shared/ExtensionMethod/SymbolTableRecordEx.cs b/src/IFoxCAD.Cad.Shared/ExtensionMethod/SymbolTableRecordEx.cs index 808df66..c4eb33a 100644 --- a/src/IFoxCAD.Cad.Shared/ExtensionMethod/SymbolTableRecordEx.cs +++ b/src/IFoxCAD.Cad.Shared/ExtensionMethod/SymbolTableRecordEx.cs @@ -104,14 +104,11 @@ public static IEnumerable AddEntity(this BlockTableRecord btr, IEnu trans ??= DBTrans.Top.Transaction; using (btr.ForWrite()) { - return ents - .Select( - ent => { - ObjectId id = btr.AppendEntity(ent); - trans.AddNewlyCreatedDBObject(ent, true); - return id; - }) - .ToList(); + return ents.Select(ent => { + ObjectId id = btr.AppendEntity(ent); + trans.AddNewlyCreatedDBObject(ent, true); + return id; + }).ToList(); } } @@ -137,9 +134,9 @@ public static IEnumerable AddEntity(this BlockTableRecord btr, params /// 图元属性设置委托 /// 事务管理器 /// 图元id - private static ObjectId AddEnt(this BlockTableRecord btr, T ent, Action? action, Transaction? trans) where T : Entity + private static ObjectId AddEnt(this BlockTableRecord btr, T ent, + Action? action, Transaction? trans = null) where T : Entity { - // trans ??= DBTrans.Top.Transaction; action?.Invoke(ent); return btr.AddEntity(ent, trans); } @@ -148,16 +145,15 @@ private static ObjectId AddEnt(this BlockTableRecord btr, T ent, Action? a /// /// 块表 /// 返回图元的委托 - /// 事务 + /// 事务 /// 图元id,如果委托返回 null,则为 ObjectId.Null - public static ObjectId AddEnt(this BlockTableRecord btr, Func action, Transaction? transaction) + public static ObjectId AddEnt(this BlockTableRecord btr, Func action, Transaction? trans = null) { - // transaction ??= DBTrans.Top.Transaction; var ent = action.Invoke(); if (ent is null) return ObjectId.Null; - return btr.AddEntity(ent, transaction); + return btr.AddEntity(ent, trans); } /// @@ -170,7 +166,7 @@ public static ObjectId AddEnt(this BlockTableRecord btr, Func action, Tr /// 直线属性设置委托 /// 直线的id public static ObjectId AddLine(this BlockTableRecord btr, Point3d start, Point3d end, - Action? action = default, Transaction? trans = default) + Action? action = null, Transaction? trans = null) { var line = new Line(start, end); return btr.AddEnt(line, action, trans); @@ -185,7 +181,7 @@ public static ObjectId AddLine(this BlockTableRecord btr, Point3d start, Point3d /// 事务管理器 /// 圆的id public static ObjectId AddCircle(this BlockTableRecord btr, Point3d center, double radius, - Action? action = default, Transaction? trans = default) + Action? action = null, Transaction? trans = null) { var circle = new Circle(center, Vector3d.ZAxis, radius); return btr.AddEnt(circle, action, trans); @@ -202,7 +198,7 @@ public static ObjectId AddCircle(this BlockTableRecord btr, Point3d center, doub /// 事务管理器 /// 三点有外接圆则返回圆的id,否则返回ObjectId.Null public static ObjectId AddCircle(this BlockTableRecord btr, Point3d p0, Point3d p1, Point3d p2, - Action? action = default, Transaction? trans = default) + Action? action = null, Transaction? trans = null) { var circle = EntityEx.CreateCircle(p0, p1, p2); // return circle is not null ? btr.AddEnt(circle, action, trans) : throw new ArgumentNullException(nameof(circle), "对象为 null"); @@ -222,11 +218,10 @@ public static ObjectId AddCircle(this BlockTableRecord btr, Point3d p0, Point3d /// 事务管理器 /// 轻多段线id public static ObjectId AddPline(this BlockTableRecord btr, - List bvws, - double? constantWidth = null, - bool isClosed = true, - Action? action = default, - Transaction? trans = default) + List bvws, + double? constantWidth = null, + bool isClosed = true, + Action? action = null, Transaction? trans = null) { Polyline pl = new(); pl.SetDatabaseDefaults(); @@ -256,11 +251,11 @@ public static ObjectId AddPline(this BlockTableRecord btr, /// 轻多段线id public static ObjectId AddPline(this BlockTableRecord btr, List pts, - List? bulges = default, - List? startWidths = default, - List? endWidths = default, - Action? action = default, - Transaction? trans = default) + List? bulges = null, + List? startWidths = null, + List? endWidths = null, + Action? action = null, + Transaction? trans = null) { bulges ??= new(new double[pts.Count]); startWidths ??= new(new double[pts.Count]); @@ -284,8 +279,8 @@ public static ObjectId AddPline(this BlockTableRecord btr, /// 轻多段线id public static ObjectId AddPline(this BlockTableRecord btr, List<(Point3d pt, double bulge, double startWidth, double endWidth)> pts, - Action? action = default, - Transaction? trans = default) + Action? action = null, + Transaction? trans = null) { Polyline pl = new(); pl.SetDatabaseDefaults(); @@ -309,7 +304,7 @@ public static ObjectId AddPline(this BlockTableRecord btr, /// 圆弧id public static ObjectId AddArc(this BlockTableRecord btr, Point3d startPoint, Point3d pointOnArc, Point3d endPoint, - Action? action = default, Transaction? trans = default) + Action? action = null, Transaction? trans = null) { var arc = EntityEx.CreateArc(startPoint, pointOnArc, endPoint); return btr.AddEnt(arc, action, trans); @@ -332,7 +327,7 @@ public static ObjectId AddArc(this BlockTableRecord btr, /// 实体集合 public static IEnumerable GetEntities(this BlockTableRecord btr, OpenMode openMode = OpenMode.ForRead, - Transaction? trans = default, + Transaction? trans = null, bool openErased = false, bool openLockedLayer = false) where T : Entity { @@ -378,7 +373,7 @@ public static IEnumerable> GetObjectIds(this BlockTa /// 是否打开锁定图层对象,默认为不打开 /// 绘制顺序表 public static DrawOrderTable? GetDrawOrderTable(this BlockTableRecord btr, - Transaction? trans = default, + Transaction? trans = null, bool openErased = false, bool openLockedLayer = false) { @@ -404,7 +399,7 @@ public static ObjectId InsertBlock(this BlockTableRecord blockTableRecord, Point string blockName, Scale3d scale = default, double rotation = default, - Dictionary? atts = default, + Dictionary? atts = null, Transaction? trans = null) { trans ??= DBTrans.Top.Transaction; @@ -429,7 +424,7 @@ public static ObjectId InsertBlock(this BlockTableRecord blockTableRecord, ObjectId blockId, Scale3d scale = default, double rotation = default, - Dictionary? atts = default, + Dictionary? atts = null, Transaction? trans = null) { trans ??= DBTrans.Top.Transaction; @@ -444,7 +439,8 @@ public static ObjectId InsertBlock(this BlockTableRecord blockTableRecord, Rotation = rotation }; var objid = blockTableRecord.AddEntity(blockref); - if (atts != default) + + if (atts != null) { var btr = DBTrans.Top.GetObject(blockref.BlockTableRecord)!; if (btr.HasAttributeDefinitions) @@ -519,6 +515,6 @@ public static void ForEach(this TRecord record, Action Date: Tue, 18 Oct 2022 02:46:37 +0800 Subject: [PATCH 567/675] =?UTF-8?q?=E5=A2=9E=E5=8A=A0=E4=B8=8D=E5=AE=89?= =?UTF-8?q?=E5=85=A8=E6=93=8D=E4=BD=9C=E4=BE=8B=E5=AD=90?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../ExtensionMethod/EditorEx.cs | 17 +++++----- tests/TestShared/TestMarshal.cs | 33 +++++++++++++++++++ 2 files changed, 42 insertions(+), 8 deletions(-) diff --git a/src/IFoxCAD.Cad.Shared/ExtensionMethod/EditorEx.cs b/src/IFoxCAD.Cad.Shared/ExtensionMethod/EditorEx.cs index a94bda3..1182d8b 100644 --- a/src/IFoxCAD.Cad.Shared/ExtensionMethod/EditorEx.cs +++ b/src/IFoxCAD.Cad.Shared/ExtensionMethod/EditorEx.cs @@ -63,14 +63,14 @@ public static SelectionSet SelectByLineWeight(this Editor editor, LineWeight lin /// 消息 /// 关键字和回调函数 /// - public static PromptSelectionResult? SSGet(this Editor editor, - string? mode = null, - SelectionFilter? filter = null, - string[]? messages = null, - Dictionary? keywords = null) + public static PromptSelectionResult SSGet(this Editor editor, + string? mode = null, + SelectionFilter? filter = null, + string[]? messages = null, + Dictionary? keywords = null) { PromptSelectionOptions pso = new(); - PromptSelectionResult? ss = null; + PromptSelectionResult ss; if (mode is not null) { mode = mode.ToUpper(); @@ -110,9 +110,10 @@ public static SelectionSet SelectByLineWeight(this Editor editor, LineWeight lin else ss = editor.GetSelection(pso); } - catch (Exception e) + catch (Exception) { - editor.WriteMessage($"\nKey is {e.Message}"); + //editor.WriteMessage($"\nKey is {e.Message}"); + throw; } return ss; } diff --git a/tests/TestShared/TestMarshal.cs b/tests/TestShared/TestMarshal.cs index 07fc3f2..0bca7fa 100644 --- a/tests/TestShared/TestMarshal.cs +++ b/tests/TestShared/TestMarshal.cs @@ -4,6 +4,39 @@ namespace TestShared; public class TestMarshal { + [CommandMethod(nameof(Test_ChangeLinePoint))] + public void Test_ChangeLinePoint() + { + var prs = Env.Editor.SSGet("\n 选择直线"); + if (prs.Status != PromptStatus.OK) + return; + + using DBTrans tr = new(); + + prs.Value.GetObjectIds().ForEach(id => { + var line = id.GetObject(); + if (line == null) + return; + using (line.ForWrite()) + unsafe + { + // 不允许直接 &这个表达式进行取址(因为它是属性,不是字段) + // 实际上就是默认拷贝一份副本出来 + var p1 = line.StartPoint; + ((Point3D*)&p1)->X = 0; + ((Point3D*)&p1)->Y = 0; + ((Point3D*)&p1)->Z = 0; + line.StartPoint = p1;// 又放回去,节省了一个变量 + + var p2 = line.EndPoint; + ((Point3D*)&p2)->X = 100; + ((Point3D*)&p2)->Y = 100; + ((Point3D*)&p2)->Z = 0; + line.EndPoint = p2; + } + }); + } + [CommandMethod(nameof(Test_ImplicitPoint3D))] public void Test_ImplicitPoint3D() { -- Gitee From b148a1e095b89d71a316004cd1cf61b9247c0e0f Mon Sep 17 00:00:00 2001 From: liuqihong <540762622@qq.com> Date: Tue, 18 Oct 2022 06:35:08 +0800 Subject: [PATCH 568/675] =?UTF-8?q?=E4=BF=AE=E5=A4=8D=E6=B1=82=E4=B8=AD?= =?UTF-8?q?=E7=82=B9=E6=9C=89=E6=BA=A2=E5=87=BA=E9=A3=8E=E9=99=A9=20?= =?UTF-8?q?=E5=A2=9E=E5=8A=A0=E7=BC=93=E5=AD=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../ExtensionMethod/Curve2dEx.cs | 51 +++---- .../ExtensionMethod/EditorEx.cs | 67 ++++----- .../ExtensionMethod/Filer/DwgFiler.cs | 139 ++++++------------ .../ExtensionMethod/GeometryEx.cs | 29 ++-- tests/TestShared/TestMirrorFile.cs | 3 +- 5 files changed, 110 insertions(+), 179 deletions(-) diff --git a/src/IFoxCAD.Cad.Shared/ExtensionMethod/Curve2dEx.cs b/src/IFoxCAD.Cad.Shared/ExtensionMethod/Curve2dEx.cs index 765bf4d..07db654 100644 --- a/src/IFoxCAD.Cad.Shared/ExtensionMethod/Curve2dEx.cs +++ b/src/IFoxCAD.Cad.Shared/ExtensionMethod/Curve2dEx.cs @@ -5,6 +5,8 @@ /// public static class Curve2dEx { + internal static readonly Plane _planeCache = new(); + #region Curve2d /// @@ -64,13 +66,9 @@ public static Curve ToCurve(this CircularArc2d ca2d, Matrix3d mat) public static Curve ToCurve(this CircularArc2d ca2d) { if (ca2d.IsClosed()) - { return ToCircle(ca2d); - } else - { return ToArc(ca2d); - } } /// @@ -82,8 +80,8 @@ public static Circle ToCircle(this CircularArc2d c2d) { return new Circle( - new Point3d(new Plane(), c2d.Center), - new Vector3d(0, 0, 1), + new Point3d(_planeCache, c2d.Center), + Vector3d.ZAxis, c2d.Radius); } @@ -110,7 +108,7 @@ public static Arc ToArc(this CircularArc2d a2d) return new Arc( - new Point3d(new Plane(), a2d.Center), + new Point3d(_planeCache, a2d.Center), Vector3d.ZAxis, a2d.Radius, startangle, @@ -142,12 +140,10 @@ public static Ellipse ToCurve(this EllipticalArc2d ea2d, Matrix3d mat) /// 实体椭圆弧 public static Ellipse ToCurve(this EllipticalArc2d ea2d) { - Plane plane = new(); - Ellipse ell = - new( - new Point3d(plane, ea2d.Center), - new Vector3d(0, 0, 1), - new Vector3d(plane, ea2d.MajorAxis) * ea2d.MajorRadius, + Ellipse ell = new( + new Point3d(_planeCache, ea2d.Center), + Vector3d.ZAxis, + new Vector3d(_planeCache, ea2d.MajorAxis) * ea2d.MajorRadius, ea2d.MinorRadius / ea2d.MajorRadius, 0, Math.PI * 2); @@ -178,13 +174,11 @@ public static Ellipse ToCurve(this EllipticalArc2d ea2d) /// 实体类构造线 public static Xline ToCurve(this Line2d line2d) { - Plane plane = new(); - return - new Xline - { - BasePoint = new Point3d(plane, line2d.PointOnLine), - SecondPoint = new Point3d(plane, line2d.PointOnLine + line2d.Direction) - }; + return new Xline + { + BasePoint = new Point3d(_planeCache, line2d.PointOnLine), + SecondPoint = new Point3d(_planeCache, line2d.PointOnLine + line2d.Direction) + }; } /// @@ -241,11 +235,10 @@ public static Line ToCurve(this LineSegment2d ls2d, Matrix3d mat) /// 实体类直线 public static Line ToCurve(this LineSegment2d ls2d) { - Plane plane = new(); return new Line( - new Point3d(plane, ls2d.StartPoint), - new Point3d(plane, ls2d.EndPoint)); + new Point3d(_planeCache, ls2d.StartPoint), + new Point3d(_planeCache, ls2d.EndPoint)); } #endregion LineSegment2d @@ -272,18 +265,16 @@ public static Spline ToCurve(this NurbCurve2d nc2d, Matrix3d mat) /// 实体类样条曲线 public static Spline ToCurve(this NurbCurve2d nc2d) { - int i; - Plane plane = new(); using Point3dCollection ctlpnts = new(); - for (i = 0; i < nc2d.NumControlPoints; i++) - ctlpnts.Add(new Point3d(plane, nc2d.GetControlPointAt(i))); + for (int i = 0; i < nc2d.NumControlPoints; i++) + ctlpnts.Add(new Point3d(_planeCache, nc2d.GetControlPointAt(i))); DoubleCollection knots = new(); - foreach (double knot in nc2d.Knots) - knots.Add(knot); + for (int i = 0; i < nc2d.Knots.Count; i++) + knots.Add(nc2d.Knots[i]); DoubleCollection weights = new(); - for (i = 0; i < nc2d.NumWeights; i++) + for (int i = 0; i < nc2d.NumWeights; i++) weights.Add(nc2d.GetWeightAt(i)); NurbCurve2dData ncdata = nc2d.DefinitionData; diff --git a/src/IFoxCAD.Cad.Shared/ExtensionMethod/EditorEx.cs b/src/IFoxCAD.Cad.Shared/ExtensionMethod/EditorEx.cs index 1182d8b..62a3f44 100644 --- a/src/IFoxCAD.Cad.Shared/ExtensionMethod/EditorEx.cs +++ b/src/IFoxCAD.Cad.Shared/ExtensionMethod/EditorEx.cs @@ -666,34 +666,32 @@ public static Matrix3d GetMatrixFromMDcsToPDcs(this Editor editor) if ((short)Env.GetVar("TILEMODE") == 1) throw new ArgumentException("TILEMODE == 1..Espace papier uniquement"); - Database db = editor.Document.Database; - Matrix3d mat; - using (var tr = db.TransactionManager.StartTransaction()) + Matrix3d mat = Matrix3d.Identity; + using DBTrans tr = new(); + var vp = tr.GetObject(editor.CurrentViewportObjectId); + if (vp == null) + return mat; + + if (vp.Number == 1) { - var openErased = false; - var openLockedLayer = false; - var vp = tr.GetObject(editor.CurrentViewportObjectId, OpenMode.ForRead, - openErased, openLockedLayer) as Viewport; - if (vp?.Number == 1) + try { - try - { - editor.SwitchToModelSpace(); - vp = tr.GetObject(editor.CurrentViewportObjectId, OpenMode.ForRead, - openErased, openLockedLayer) as Viewport; - editor.SwitchToPaperSpace(); - } - catch - { - throw new Exception("Aucun fenêtre active...ErrorStatus.InvalidInput"); - } + editor.SwitchToModelSpace(); + vp = tr.GetObject(editor.CurrentViewportObjectId); + editor.SwitchToPaperSpace(); + } + catch + { + throw new Exception("Aucun fenêtre active...ErrorStatus.InvalidInput"); } - Point3d vCtr = new(vp!.ViewCenter.X, vp.ViewCenter.Y, 0.0); - mat = Matrix3d.Displacement(vCtr.GetAsVector().Negate()); - mat = Matrix3d.Displacement(vp.CenterPoint.GetAsVector()) * mat; - mat = Matrix3d.Scaling(vp.CustomScale, vp.CenterPoint) * mat; - tr.Commit(); } + if (vp == null) + return mat; + + Point3d vCtr = new(vp.ViewCenter.X, vp.ViewCenter.Y, 0.0); + mat = Matrix3d.Displacement(vCtr.GetAsVector().Negate()); + mat = Matrix3d.Displacement(vp.CenterPoint.GetAsVector()) * mat; + mat = Matrix3d.Scaling(vp.CustomScale, vp.CenterPoint) * mat; return mat; } @@ -801,25 +799,23 @@ public static Matrix3d GetMatrix(this Editor editor, CoordinateSystemCode from, /// 窗口右上点 public static void ZoomWindow(this Editor ed, Point3d minPoint, Point3d maxPoint) { - ViewTableRecord cvtr = ed.GetCurrentView(); ViewTableRecord vtr = new(); - vtr.CopyFrom(cvtr); + vtr.CopyFrom(ed.GetCurrentView()); + + var oldpnts = new Point3d[] { minPoint, maxPoint }; + var pnts = new Point3d[8]; + var dpnts = new Point3d[8]; - Point3d[] oldpnts = new Point3d[] { minPoint, maxPoint }; - Point3d[] pnts = new Point3d[8]; - Point3d[] dpnts = new Point3d[8]; + var mat = ed.GetMatrixFromWcsToMDcs(); for (int i = 0; i < 2; i++) - { for (int j = 0; j < 2; j++) - { for (int k = 0; k < 2; k++) { int n = i * 4 + j * 2 + k; pnts[n] = new Point3d(oldpnts[i][0], oldpnts[j][1], oldpnts[k][2]); - dpnts[n] = pnts[n].TransformBy(ed.GetMatrixFromWcsToMDcs()); + dpnts[n] = pnts[n].TransformBy(mat); } - } - } + double xmin, xmax, ymin, ymax; xmin = xmax = dpnts[0][0]; ymin = ymax = dpnts[0][1]; @@ -833,7 +829,8 @@ public static void ZoomWindow(this Editor ed, Point3d minPoint, Point3d maxPoint vtr.Width = xmax - xmin; vtr.Height = ymax - ymin; - vtr.CenterPoint = (dpnts[0] + (dpnts[7] - dpnts[0]) / 2).Convert2d(new Plane()); + vtr.CenterPoint = (dpnts[0] + (dpnts[7] - dpnts[0]) / 2) + .Convert2d(Curve2dEx._planeCache); ed.SetCurrentView(vtr); ed.Regen(); diff --git a/src/IFoxCAD.Cad.Shared/ExtensionMethod/Filer/DwgFiler.cs b/src/IFoxCAD.Cad.Shared/ExtensionMethod/Filer/DwgFiler.cs index 871cbdd..32e3614 100644 --- a/src/IFoxCAD.Cad.Shared/ExtensionMethod/Filer/DwgFiler.cs +++ b/src/IFoxCAD.Cad.Shared/ExtensionMethod/Filer/DwgFiler.cs @@ -1,4 +1,6 @@ -namespace IFoxCAD.Cad; +using System.Web.Script.Serialization; + +namespace IFoxCAD.Cad; /* Arx自定义实体类,加 读函数(assertReadEnabled)和写函数(assertWriteEnabled) @@ -33,9 +35,11 @@ public class DwgFiler : Cad_DwgFiler public List HandleList; public int HandleListPt = 0; [NonSerialized] + [ScriptIgnore] public List HardOwnershipIdList; public int HardOwnershipIdListPt = 0; [NonSerialized] + [ScriptIgnore] public List HardPointerIdList; public int HardPointerIdListPt = 0; public List Int16List; @@ -53,9 +57,11 @@ public class DwgFiler : Cad_DwgFiler public List Scale3dList; public int Scale3dListPt = 0; [NonSerialized] + [ScriptIgnore] public List SoftOwnershipIdList; public int SoftOwnershipIdListPt = 0; [NonSerialized] + [ScriptIgnore] public List SoftPointerIdList; public int SoftPointerIdListPt = 0; public List StringList; @@ -78,33 +84,33 @@ public DwgFiler() m_Position = 0; m_FilerType = FilerType.CopyFiler; m_FilerStatus = Cad_ErrorStatus.OK; - AddressList = new List(); - BinaryChunkList = new List(); - BooleanList = new List(); - ByteList = new List(); - BytesList = new List(); - DoubleList = new List(); - HandleList = new List(); - HardOwnershipIdList = new List(); - HardPointerIdList = new List(); - Int16List = new List(); - Int32List = new List(); + AddressList = new(); + BinaryChunkList = new(); + BooleanList = new(); + ByteList = new(); + BytesList = new(); + DoubleList = new(); + HandleList = new(); + HardOwnershipIdList = new(); + HardPointerIdList = new(); + Int16List = new(); + Int32List = new(); #if !NET35 - Int64List = new List(); + Int64List = new(); #endif - Point2dList = new List(); - Point3dList = new List(); - Scale3dList = new List(); - SoftOwnershipIdList = new List(); - SoftPointerIdList = new List(); - StringList = new List(); - Uint16List = new List(); - Uint32List = new List(); + Point2dList = new(); + Point3dList = new(); + Scale3dList = new(); + SoftOwnershipIdList = new(); + SoftPointerIdList = new(); + StringList = new(); + Uint16List = new(); + Uint32List = new(); #if !NET35 - Uint64List = new List(); + Uint64List = new(); #endif - Vector2dList = new List(); - Vector3dList = new List(); + Vector2dList = new(); + Vector3dList = new(); } #if NET35 @@ -112,10 +118,7 @@ public DwgFiler() #else public override long Position => m_Position; #endif - public override FilerType FilerType - { - get { return this.m_FilerType; } - } + public override FilerType FilerType => m_FilerType; public override Cad_ErrorStatus FilerStatus { @@ -126,7 +129,7 @@ public override Cad_ErrorStatus FilerStatus public override IntPtr ReadAddress() { if (AddressList.Count == 0) - return new IntPtr(); + return new(); return AddressList[AddressListPt++]; } @@ -169,21 +172,21 @@ public override double ReadDouble() public override Handle ReadHandle() { if (HandleList.Count == 0) - return new Handle(); + return new(); return HandleList[HandleListPt++]; } public override ObjectId ReadHardOwnershipId() { if (HardOwnershipIdList.Count == 0) - return new ObjectId(); + return new(); return HardOwnershipIdList[HardOwnershipIdListPt++]; } public override ObjectId ReadHardPointerId() { if (HardPointerIdList.Count == 0) - return new ObjectId(); + return new(); return HardPointerIdList[HardPointerIdListPt++]; } @@ -213,35 +216,35 @@ public override long ReadInt64() public override Point2d ReadPoint2d() { if (Point2dList.Count == 0) - return new Point2d(); + return new(); return Point2dList[Point2dListPt++]; } public override Point3d ReadPoint3d() { if (Point3dList.Count == 0) - return new Point3d(); + return new(); return Point3dList[Point3dListPt++]; } public override Scale3d ReadScale3d() { if (Scale3dList.Count == 0) - return new Scale3d(); + return new(); return Scale3dList[Scale3dListPt++]; } public override ObjectId ReadSoftOwnershipId() { if (SoftOwnershipIdList.Count == 0) - return new ObjectId(); + return new(); return SoftOwnershipIdList[SoftOwnershipIdListPt++]; } public override ObjectId ReadSoftPointerId() { if (SoftPointerIdList.Count == 0) - return new ObjectId(); + return new(); return SoftPointerIdList[SoftPointerIdListPt++]; } @@ -278,14 +281,14 @@ public override ulong ReadUInt64() public override Vector2d ReadVector2d() { if (Vector2dList.Count == 0) - return new Vector2d(); + return new(); return Vector2dList[Vector2dListPt++]; } public override Vector3d ReadVector3d() { if (Vector3dList.Count == 0) - return new Vector3d(); + return new(); return Vector3dList[Vector3dListPt++]; } @@ -491,62 +494,6 @@ public override void WriteVector3d(Vector3d value) public override string ToString() { - int ptCount = AddressListPt + - BinaryChunkListPt + - BooleanListPt + - ByteListPt + - BytesListPt + - DoubleListPt + - HandleListPt + - HardOwnershipIdListPt + - HardPointerIdListPt + - Int16ListPt + - Int32ListPt + -#if !NET35 - Int64ListPt + -#endif - Point2dListPt + - Point3dListPt + - Scale3dListPt + - SoftOwnershipIdListPt + - SoftPointerIdListPt + - StringListPt + - uint16ListPt + - uint32ListPt + -#if !NET35 - uint64ListPt + -#endif - Vector2dListPt + - Vector3dListPt; - - int ltCount = AddressList.Count + - BinaryChunkList.Count + - BooleanList.Count + - ByteList.Count + - BytesList.Count + - DoubleList.Count + - HandleList.Count + - HardOwnershipIdList.Count + - HardPointerIdList.Count + - Int16List.Count + - Int32List.Count + -#if !NET35 - Int64List.Count + -#endif - Point2dList.Count + - Point3dList.Count + - Scale3dList.Count + - SoftOwnershipIdList.Count + - SoftPointerIdList.Count + - StringList.Count + - Uint16List.Count + - Uint32List.Count + -#if !NET35 - Uint64List.Count + -#endif - Vector2dList.Count + - Vector3dList.Count; - - return "\nDataIn::" + ptCount + "\nDataOut::" + ltCount; + return new JavaScriptSerializer().Serialize(this); } } \ No newline at end of file diff --git a/src/IFoxCAD.Cad.Shared/ExtensionMethod/GeometryEx.cs b/src/IFoxCAD.Cad.Shared/ExtensionMethod/GeometryEx.cs index b9750ef..58e9da3 100644 --- a/src/IFoxCAD.Cad.Shared/ExtensionMethod/GeometryEx.cs +++ b/src/IFoxCAD.Cad.Shared/ExtensionMethod/GeometryEx.cs @@ -182,7 +182,7 @@ public static CircularArc2d GetMinCircle(Point2d pt1, Point2d pt2, Point2d pt3, CircularArc2d? ca2d = null; // 遍历C43的组合,环链表的优势在这里 - foreach (LoopListNode firstNode in iniptlst.GetNodes()) + foreach (var firstNode in iniptlst.GetNodes()) { // 获取各组合下三点的最小包围圆 var secondNode = firstNode.Next; @@ -560,7 +560,7 @@ public static Matrix3d GetScaleMatrix(this Point3d point, double x, double y, do matdata[10] = z; matdata[11] = point.Z * (1 - z); matdata[15] = 1; - return new Matrix3d(matdata); + return new(matdata); } /// @@ -572,7 +572,7 @@ public static Size GetSize(this Extents3d ext) { int width = (int)Math.Floor(ext.MaxPoint.X - ext.MinPoint.X); int height = (int)Math.Ceiling(ext.MaxPoint.Y - ext.MinPoint.Y); - return new Size(width, height); + return new(width, height); } /// @@ -582,7 +582,7 @@ public static Size GetSize(this Extents3d ext) /// 二维点 public static Point2d Point2d(this Point3d pt) { - return new Point2d(pt.X, pt.Y); + return new(pt.X, pt.Y); } /// /// 将三维点集转换为二维点集 @@ -597,20 +597,11 @@ public static IEnumerable Point2d(this IEnumerable pts) /// 将二维点转换为三维点 /// /// 二维点 - /// 三维点 - public static Point3d Point3d(this Point2d pt) - { - return new Point3d(pt.X, pt.Y, 0); - } - /// - /// 将二维点转换为三维点 - /// - /// 二维点 /// Z值 /// 三维点 - public static Point3d Point3d(this Point2d pt, double z) + public static Point3d Point3d(this Point2d pt, double z = 0) { - return new Point3d(pt.X, pt.Y, z); + return new(pt.X, pt.Y, z); } /// @@ -621,7 +612,9 @@ public static Point3d Point3d(this Point2d pt, double z) /// 返回两个点之间的中点 public static Point3d GetMidPointTo(this Point3d pt1, Point3d pt2) { - return new Point3d((pt1.X + pt2.X) * 0.5, (pt1.Y + pt2.Y) * 0.5, (pt1.Z + pt2.Z) * 0.5); + return new(pt1.X * 0.5 + pt2.X * 0.5, + pt1.Y * 0.5 + pt2.Y * 0.5, + pt1.Z * 0.5 + pt2.Z * 0.5); } /// @@ -632,7 +625,9 @@ public static Point3d GetMidPointTo(this Point3d pt1, Point3d pt2) /// 返回两个点之间的中点 public static Point2d GetMidPointTo(this Point2d pt1, Point2d pt2) { - return new Point2d((pt1.X + pt2.X) * 0.5, (pt1.Y + pt2.Y) * 0.5); + // (pt1 + pt2) / 2; // 溢出风险 + return new(pt1.X * 0.5 + pt2.X * 0.5, + pt1.Y * 0.5 + pt2.Y * 0.5); } /// diff --git a/tests/TestShared/TestMirrorFile.cs b/tests/TestShared/TestMirrorFile.cs index 5bdb2bb..20503f7 100644 --- a/tests/TestShared/TestMirrorFile.cs +++ b/tests/TestShared/TestMirrorFile.cs @@ -14,6 +14,7 @@ public class MirrorFile [CommandMethod(nameof(CmdTest_MirrorFile))] public static void CmdTest_MirrorFile() { + var yaxis = new Point3d(0, 1, 0); using DBTrans tr = new(file, fileOpenMode: FileOpenMode.OpenForReadAndReadShare); tr.BlockTable.Change(tr.ModelSpace.ObjectId, modelSpace => { modelSpace.ForEach(entId => { @@ -25,7 +26,7 @@ public static void CmdTest_MirrorFile() var pos = dbText.Position; // text.Move(pos, Point3d.Origin); // Y轴 - dbText.Mirror(Point3d.Origin, new Point3d(0, 1, 0)); + dbText.Mirror(Point3d.Origin, yaxis); // text.Move(Point3d.Origin, pos); dbText.DowngradeOpen(); }); -- Gitee From 15f4d784e6518a1272481030a5659713eb886387 Mon Sep 17 00:00:00 2001 From: liuqihong <540762622@qq.com> Date: Wed, 19 Oct 2022 17:18:28 +0800 Subject: [PATCH 569/675] =?UTF-8?q?WPF=E5=B7=A5=E7=A8=8B=E5=A2=9E=E5=8A=A0?= =?UTF-8?q?=E9=94=99=E8=AF=AF=E4=BE=A6=E5=90=AC=E5=99=A8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/IFoxCAD.WPF/BindingErrorTraceListener.cs | 84 +++++++++++++++++++ src/IFoxCAD.WPF/DependencyObjectExtensions.cs | 18 ++-- src/IFoxCAD.WPF/EnumSelection.cs | 18 ++-- src/IFoxCAD.WPF/EventBindingExtension.cs | 65 +++++++------- src/IFoxCAD.WPF/RelayCommand.cs | 20 +---- src/IFoxCAD.WPF/ViewModelBase.cs | 8 +- 6 files changed, 144 insertions(+), 69 deletions(-) create mode 100644 src/IFoxCAD.WPF/BindingErrorTraceListener.cs diff --git a/src/IFoxCAD.WPF/BindingErrorTraceListener.cs b/src/IFoxCAD.WPF/BindingErrorTraceListener.cs new file mode 100644 index 0000000..9ba3a4b --- /dev/null +++ b/src/IFoxCAD.WPF/BindingErrorTraceListener.cs @@ -0,0 +1,84 @@ +namespace IFoxCAD.WPF; +using System.Text.RegularExpressions; + +/* + xaml 需要绑定失败时候报错(vs默认是不报错的): + https://cloud.tencent.com/developer/article/1342661 + 所有的绑定输出,重写方法就可以转发,构造函数上加入: + public MainWindow() + { + PresentationTraceSources.DataBindingSource.Switch.Level = SourceLevels.Error; + PresentationTraceSources.DataBindingSource.Listeners.Add(new BindingErrorTraceListener()); + App.Current.DispatcherUnhandledException += DispatcherUnhandledException; + // InitializeComponent(); + // DataContext = new ViewModel(); + } + private void DispatcherUnhandledException(object sender, System.Windows.Threading.DispatcherUnhandledExceptionEventArgs e) + { + if (e.Exception is BindingErrorException bindingErrorException) + { + MessageBox.Show($"Binding error. {bindingErrorException.SourceObject}.{bindingErrorException.SourceProperty} {bindingErrorException.TargetElement}.{bindingErrorException.TargetProperty}"); + } + } +*/ + +/// +/// 属性绑定错误异常 +/// +public class BindingErrorException : Exception +{ + /// + /// 来源对象 + /// + public string? SourceObject { get; set; } + /// + /// 来源属性 + /// + public string? SourceProperty { get; set; } + /// + /// 目标元素 + /// + public string? TargetElement { get; set; } + /// + /// 目标属性 + /// + public string? TargetProperty { get; set; } + + public BindingErrorException() : base() + { + } + + public BindingErrorException(string message) : base(message) + { + } +} + +/// +/// 属性绑定错误侦听器 +/// +public class BindingErrorTraceListener : TraceListener +{ + const string BindingErrorPattern = + @"^BindingExpression path error(?:.+)'(.+)' property not found(?:.+)object[\s']+(.+?)'(?:.+)target element is '(.+?)'(?:.+)target property is '(.+?)'(?:.+)$"; + + public override void Write(string message) + { + Trace.WriteLine(string.Format("[Write]{0}", message)); + Debug.WriteLine(string.Format("[Write]{0}", message)); + } + + public override void WriteLine(string message) + { + var match = Regex.Match(message, BindingErrorPattern); + if (match.Success) + { + throw new BindingErrorException(message) + { + SourceObject = match.Groups[2].ToString(), + SourceProperty = match.Groups[1].ToString(), + TargetElement = match.Groups[3].ToString(), + TargetProperty = match.Groups[4].ToString() + }; + } + } +} \ No newline at end of file diff --git a/src/IFoxCAD.WPF/DependencyObjectExtensions.cs b/src/IFoxCAD.WPF/DependencyObjectExtensions.cs index 596961d..0852858 100644 --- a/src/IFoxCAD.WPF/DependencyObjectExtensions.cs +++ b/src/IFoxCAD.WPF/DependencyObjectExtensions.cs @@ -14,21 +14,23 @@ public static class DependencyObjectExtensions { if (child is null) return null; - if (child is ContentElement contentElement) + if (child is ContentElement ce) { - DependencyObject parent = ContentOperations.GetParent(contentElement); - if (parent is not null) return parent; + var parent = ContentOperations.GetParent(ce); + if (parent is not null) + return parent; - FrameworkContentElement? fce = contentElement as FrameworkContentElement; + var fce = ce as FrameworkContentElement; return fce?.Parent; } - if (child is FrameworkElement frameworkElement) + if (child is FrameworkElement fe) { - DependencyObject parent = frameworkElement.Parent; - if (parent is not null) return parent; + var parent = fe.Parent; + if (parent is not null) + return parent; } return VisualTreeHelper.GetParent(child); } -} +} \ No newline at end of file diff --git a/src/IFoxCAD.WPF/EnumSelection.cs b/src/IFoxCAD.WPF/EnumSelection.cs index d964351..96630e4 100644 --- a/src/IFoxCAD.WPF/EnumSelection.cs +++ b/src/IFoxCAD.WPF/EnumSelection.cs @@ -6,12 +6,19 @@ public class EnumSelection : INotifyPropertyChanged where T : struct, ICompar private readonly bool canDeselect; // Can be deselected? (Radio buttons cannot deselect, checkboxes can) private readonly T blankValue; // what is considered the "blank" value if it can be deselected? - public EnumSelection(T value) : this(value, false, default) { } - public EnumSelection(T value, bool canDeselect) : this(value, canDeselect, default) { } - public EnumSelection(T value, T blankValue) : this(value, true, blankValue) { } + public EnumSelection(T value) : this(value, false, default) + { + } + public EnumSelection(T value, bool canDeselect) : this(value, canDeselect, default) + { + } + public EnumSelection(T value, T blankValue) : this(value, true, blankValue) + { + } public EnumSelection(T value, bool canDeselect, T blankValue) { - if (!typeof(T).IsEnum) throw new ArgumentException($"{nameof(T)} must be an enum type"); // I really wish there was a way to constrain generic types to enums... + if (!typeof(T).IsEnum) + throw new ArgumentException($"{nameof(T)} must be an enum type"); // I really wish there was a way to constrain generic types to enums... isFlagged = typeof(T).IsDefined(typeof(FlagsAttribute), false); this.value = value; @@ -46,7 +53,8 @@ public bool this[T key] int iValue = (int)(object)this.value; int iKey = (int)(object)key; - if (((iValue & iKey) == iKey) == value) return; + if ((iValue & iKey) == iKey == value) + return; if (value) Value = (T)(object)(iValue | iKey); diff --git a/src/IFoxCAD.WPF/EventBindingExtension.cs b/src/IFoxCAD.WPF/EventBindingExtension.cs index 76f5b5e..ab46a12 100644 --- a/src/IFoxCAD.WPF/EventBindingExtension.cs +++ b/src/IFoxCAD.WPF/EventBindingExtension.cs @@ -28,13 +28,13 @@ public class EventBindingExtension : MarkupExtension if (serviceProvider is null) throw new ArgumentNullException(nameof(serviceProvider)); if (serviceProvider.GetService(typeof(IProvideValueTarget)) is not IProvideValueTarget targetProvider) - throw new InvalidOperationException(); + throw new InvalidOperationException(message: $"{nameof(ProvideValue)}:{nameof(IProvideValueTarget)}"); if (targetProvider.TargetObject is not FrameworkElement targetObject) - throw new InvalidOperationException(); + throw new InvalidOperationException(message: $"{nameof(ProvideValue)}:{nameof(FrameworkElement)}"); if (targetProvider.TargetProperty is not MemberInfo memberInfo) - throw new InvalidOperationException(); + throw new InvalidOperationException(message: $"{nameof(ProvideValue)}:{nameof(MemberInfo)}"); if (string.IsNullOrWhiteSpace(Command)) { @@ -61,7 +61,7 @@ public class EventBindingExtension : MarkupExtension { // var info = memberInfo as MethodInfo; // var methodInfo = info; - ParameterInfo[] pars = methodInfo.GetParameters(); + var pars = methodInfo.GetParameters(); eventHandlerType = pars[1].ParameterType; } @@ -72,9 +72,9 @@ public class EventBindingExtension : MarkupExtension private object? CreateHandler(MemberInfo memberInfo, string cmdName, Type targetType) #pragma warning restore IDE0060 // 删除未使用的参数 { - Type? eventHandlerType = GetEventHandlerType(memberInfo); - - if (eventHandlerType is null) return null; + var eventHandlerType = GetEventHandlerType(memberInfo); + if (eventHandlerType is null) + return null; var handlerInfo = eventHandlerType.GetMethod("Invoke"); var method = new DynamicMethod("", handlerInfo.ReturnType, @@ -88,21 +88,20 @@ public class EventBindingExtension : MarkupExtension gen.Emit(OpCodes.Ldarg, 0); gen.Emit(OpCodes.Ldarg, 1); gen.Emit(OpCodes.Ldstr, cmdName); + if (CommandParameter is null) - { gen.Emit(OpCodes.Ldnull); - } else - { gen.Emit(OpCodes.Ldstr, CommandParameter); - } + gen.Emit(OpCodes.Call, getMethod); gen.Emit(OpCodes.Ret); return method.CreateDelegate(eventHandlerType); } - static readonly MethodInfo getMethod = typeof(EventBindingExtension).GetMethod("HandlerIntern", new Type[] { typeof(object), typeof(object), typeof(string), typeof(string) }); + static readonly MethodInfo getMethod = typeof(EventBindingExtension) + .GetMethod("HandlerIntern", new Type[] { typeof(object), typeof(object), typeof(string), typeof(string) }); #pragma warning disable IDE0051 // 删除未使用的私有成员 static void Handler(object sender, object args) @@ -133,7 +132,8 @@ public static void HandlerIntern(object sender, object args, string cmdName, str internal static ICommand? GetCommand(FrameworkElement target, string cmdName) { var vm = FindViewModel(target); - if (vm is null) return null; + if (vm is null) + return null; var vmType = vm.GetType(); var cmdProp = vmType.GetProperty(cmdName); @@ -141,11 +141,9 @@ public static void HandlerIntern(object sender, object args, string cmdName, str return cmdProp.GetValue(vm) as ICommand; #if DEBUG throw new Exception("EventBinding path error: '" + cmdName + "' property not found on '" + vmType + "' 'DelegateCommand'"); -#endif - -#pragma warning disable CS0162 // 检测到无法访问的代码 +#else return null; -#pragma warning restore CS0162 // 检测到无法访问的代码 +#endif } internal static object GetCommandParameter(FrameworkElement target, object args, string commandParameter) @@ -162,29 +160,30 @@ internal static object GetCommandParameter(FrameworkElement target, object args, internal static ViewModelBase? FindViewModel(FrameworkElement? target) { - if (target is null) return null; - - if (target.DataContext is ViewModelBase vm) return vm; - - var parent = target.GetParentObject() as FrameworkElement; - - return FindViewModel(parent); + if (target is null) + return null; + if (target.DataContext is ViewModelBase vm) + return vm; + return FindViewModel(target.GetParentObject() as FrameworkElement); } internal static object FollowPropertyPath(object target, string path, Type? valueType = null) { - if (target is null) throw new ArgumentNullException(nameof(target)); - if (path is null) throw new ArgumentNullException(nameof(path)); - - Type currentType = valueType ?? target.GetType(); - - foreach (string propertyName in path.Split('.')) + if (target is null) + throw new ArgumentNullException(nameof(target)); + if (path is null) + throw new ArgumentNullException(nameof(path)); + + valueType ??= target.GetType(); + var spls = path.Split('.'); + for (int i = 0; i < spls.Length; i++) { - PropertyInfo property = currentType.GetProperty(propertyName); - if (property is null) throw new NullReferenceException("property"); + var property = valueType.GetProperty(spls[i]); + if (property is null) + throw new NullReferenceException("property"); target = property.GetValue(target); - currentType = property.PropertyType; + valueType = property.PropertyType; } return target; } diff --git a/src/IFoxCAD.WPF/RelayCommand.cs b/src/IFoxCAD.WPF/RelayCommand.cs index 72fabca..5d35777 100644 --- a/src/IFoxCAD.WPF/RelayCommand.cs +++ b/src/IFoxCAD.WPF/RelayCommand.cs @@ -16,7 +16,6 @@ public class RelayCommand : ICommand /// 执行函数 public RelayCommand(Action execute) : this(execute, null) { - } /// /// 初始化 类. @@ -38,16 +37,12 @@ public event EventHandler CanExecuteChanged add { if (_canExecute is not null) - { CommandManager.RequerySuggested += value; - } } remove { if (_canExecute is not null) - { CommandManager.RequerySuggested -= value; - } } } /// @@ -87,7 +82,6 @@ public class RelayCommand : ICommand /// 执行函数 public RelayCommand(Action execute) : this(execute, (o) => true) { - } /// @@ -109,16 +103,12 @@ public event EventHandler CanExecuteChanged add { if (_canExecute is not null) - { CommandManager.RequerySuggested += value; - } } remove { if (_canExecute is not null) - { CommandManager.RequerySuggested -= value; - } } } /// @@ -131,9 +121,7 @@ public event EventHandler CanExecuteChanged public bool CanExecute(object parameter) { if (_canExecute is null) - { return true; - } return _canExecute((T)parameter); } /// @@ -143,9 +131,7 @@ public bool CanExecute(object parameter) public void Execute(object parameter) { if (_execute is not null && CanExecute(parameter)) - { _execute((T)parameter); - } } } @@ -161,13 +147,9 @@ public class EventCommand : TriggerAction protected override void Invoke(object parameter) { if (CommandParameter is not null) - { parameter = CommandParameter; - } if (Command is not null) - { Command.Execute(parameter); - } } /// /// 事件 @@ -196,4 +178,4 @@ public object CommandParameter /// public static readonly DependencyProperty CommandParameterProperty = DependencyProperty.Register("CommandParameter", typeof(object), typeof(EventCommand), new PropertyMetadata(null)); -} +} \ No newline at end of file diff --git a/src/IFoxCAD.WPF/ViewModelBase.cs b/src/IFoxCAD.WPF/ViewModelBase.cs index c992e81..6d8908a 100644 --- a/src/IFoxCAD.WPF/ViewModelBase.cs +++ b/src/IFoxCAD.WPF/ViewModelBase.cs @@ -16,7 +16,6 @@ public class ViewModelBase : INotifyPropertyChanged /// 属性名 public void OnPropertyChanged([CallerMemberName] string propertyName = "") { - PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName)); } /// @@ -29,7 +28,8 @@ public void OnPropertyChanged([CallerMemberName] string propertyName = "") /// 成功返回 ,反之 protected virtual bool Set(ref T storage, T value, [CallerMemberName] string propertyName = "") { - if (object.Equals(storage, value)) return false; + if (object.Equals(storage, value)) + return false; storage = value; this.OnPropertyChanged(propertyName); @@ -53,6 +53,6 @@ protected RelayCommand CreateCommand(Action executeMethod) /// WPF命令 protected RelayCommand CreateCommand(Action executeMethod, Func canExecuteMethod) { - return new RelayCommand(executeMethod, canExecuteMethod); + return new(executeMethod, canExecuteMethod); } -} +} \ No newline at end of file -- Gitee From 41402d59f3d5ebc04c9a3dd917e9079a2c12654b Mon Sep 17 00:00:00 2001 From: liuqihong <540762622@qq.com> Date: Fri, 21 Oct 2022 08:32:33 +0800 Subject: [PATCH 570/675] =?UTF-8?q?=E4=BF=AE=E6=94=B9xdata=E5=A2=9E?= =?UTF-8?q?=E5=88=A0=E6=94=B9=E6=9F=A5=200x01=20=E8=A1=A5=E5=85=85?= =?UTF-8?q?=E7=A7=BB=E9=99=A4xdata=E7=9A=84appName=E7=9A=84=E6=96=B9?= =?UTF-8?q?=E6=B3=95=200x02=20=E8=A1=A5=E5=85=85=E6=9F=A5=E8=AF=A2xdata?= =?UTF-8?q?=E7=9A=84=E6=88=90=E5=91=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../ExtensionMethod/DBObjectEx.cs | 70 +++++---------- .../ResultData/XdataList.cs | 80 ++++++++++++++++- tests/TestShared/Copyclip.cs | 1 + tests/TestShared/TestAddEntity.cs | 86 ++++++++++++------- 4 files changed, 158 insertions(+), 79 deletions(-) diff --git a/src/IFoxCAD.Cad.Shared/ExtensionMethod/DBObjectEx.cs b/src/IFoxCAD.Cad.Shared/ExtensionMethod/DBObjectEx.cs index 41ace9f..91d9160 100644 --- a/src/IFoxCAD.Cad.Shared/ExtensionMethod/DBObjectEx.cs +++ b/src/IFoxCAD.Cad.Shared/ExtensionMethod/DBObjectEx.cs @@ -8,53 +8,6 @@ namespace IFoxCAD.Cad; public static class DBObjectEx { #region Xdata扩展 - - /// - /// 获取appName的索引区间 - /// - /// xdata - /// 注册名称 - /// 任务组码对象 - /// 返回任务组码的索引 - static List GetXdataAppIndex(XDataList data, string appName, DxfCode[] dxfCodes) - { - List acIndex = new(); - int appNameIndex = -1; - // int appNameIndexNext = -1; - - // 先找到属于它的名字索引,然后再找到下一个不属于它名字的索引,移除中间部分 - for (int i = 0; i < data.Count; i++) - { - if (data[i].TypeCode == (short)DxfCode.ExtendedDataRegAppName) - { - if (data[i].Value.ToString() == appName) - { - appNameIndex = i; - continue; - } - if (appNameIndex != -1) - { - // 找到了后面的appName - // appNameIndexNext = i; - break; - } - } - if (appNameIndex != -1 && // 找next的时候,获取任务(移除)的对象 - dxfCodes.Contains((DxfCode)data[i].TypeCode)) - acIndex.Add(i); - } - - // 当前app索引 - // if (appNameIndex == -1) - // return; - - // 下一个app索引,如果是空,就为末尾 - // if (appNameIndexNext == -1) - // appNameIndexNext = data.Count; - - return acIndex; - } - /// /// 删除扩展数据 /// @@ -69,7 +22,7 @@ public static void RemoveXData(this DBObject obj, string appName, DxfCode dxfCod // 测试命令 addxdata removexdata // 移除指定App的扩展 - var indexs = GetXdataAppIndex(data, appName, new DxfCode[] { dxfCode }); + var indexs = data.GetXdataAppIndex(appName, new DxfCode[] { dxfCode }); if (indexs.Count == 0) return; @@ -79,6 +32,25 @@ public static void RemoveXData(this DBObject obj, string appName, DxfCode dxfCod using (obj.ForWrite()) obj.XData = data; } + /// + /// 删除扩展数据 + /// + /// 对象实例 + /// 应用程序名称 + public static void RemoveXData(this DBObject obj, string appName) + { + if (obj.XData == null) + return; + foreach (var data in obj.XData) + { + // 直接赋值进去等于清空名称 + using var rb = new ResultBuffer(); + rb.Add(new((int)DxfCode.ExtendedDataRegAppName, appName)); + using (obj.ForWrite()) + obj.XData = rb; + } + } + /// /// 修改扩展数据 /// @@ -92,7 +64,7 @@ public static void ChangeXData(this DBObject obj, string appName, DxfCode dxfCod return; XDataList data = obj.XData; - var indexs = GetXdataAppIndex(data, appName, new DxfCode[] { dxfCode }); + var indexs = data.GetXdataAppIndex(appName, new DxfCode[] { dxfCode }); if (indexs.Count == 0) return; diff --git a/src/IFoxCAD.Cad.Shared/ResultData/XdataList.cs b/src/IFoxCAD.Cad.Shared/ResultData/XdataList.cs index a666100..7cf088c 100644 --- a/src/IFoxCAD.Cad.Shared/ResultData/XdataList.cs +++ b/src/IFoxCAD.Cad.Shared/ResultData/XdataList.cs @@ -41,6 +41,84 @@ public void Add(DxfCode code, object obj) Add((int)code, obj); } + /// + /// 是否含有注册名 + /// + /// 注册名 + public bool Contains(string appName) + { + bool result = false; + RangeTask(appName, (tv, state, i) => { + result = true; + state.Break(); + }); + return result; + } + + /// + /// 注册名下含有指定成员 + /// + /// 注册名 + /// 内容 + public bool Contains(string appName, object value) + { + bool result = false; + RangeTask(appName, (tv, state, i) => { + if (tv.Value.Equals(value)) + { + result = true; + state.Break(); + } + }); + return result; + } + + /// + /// 获取appName的索引区间 + /// + /// 注册名称 + /// 任务组码对象 + /// 返回任务组码的索引 + public List GetXdataAppIndex(string appName, DxfCode[] dxfCodes) + { + List indexs = new(); + RangeTask(appName, (tv, state, i) => { + if (dxfCodes.Contains((DxfCode)tv.TypeCode)) + indexs.Add(i); + }); + return indexs; + } + + /// + /// 区间任务 + /// + /// + void RangeTask(string appName, Action action) + { + LoopState state = new(); + // 在名称和名称之间找 + int appNameIndex = -1; + for (int i = 0; i < this.Count; i++) + { + if (this[i].TypeCode == (short)DxfCode.ExtendedDataRegAppName) + { + if (this[i].Value.ToString() == appName) + { + appNameIndex = i; + continue; + } + if (appNameIndex != -1)//找到了下一个名称 + break; + } + if (appNameIndex != -1) // 找下一个的时候,获取任务(移除)的对象 + { + action(this[i], state, i); + if (!state.IsRun) + break; + } + } + } + #endregion #region 转换器 @@ -65,4 +143,4 @@ public void Add(DxfCode code, object obj) /// TypedValue 数组 public static implicit operator XDataList(TypedValue[] values) => new(values); #endregion -} +} \ No newline at end of file diff --git a/tests/TestShared/Copyclip.cs b/tests/TestShared/Copyclip.cs index 441afc9..da3ae61 100644 --- a/tests/TestShared/Copyclip.cs +++ b/tests/TestShared/Copyclip.cs @@ -106,6 +106,7 @@ void DocumentManager_DocumentLockModeChanged(object sender, DocumentLockModeChan } } + /// /// 复制 /// diff --git a/tests/TestShared/TestAddEntity.cs b/tests/TestShared/TestAddEntity.cs index 0b4d37d..002e184 100644 --- a/tests/TestShared/TestAddEntity.cs +++ b/tests/TestShared/TestAddEntity.cs @@ -1,4 +1,7 @@ -namespace Test; +using System.Diagnostics; +using System.Web.UI.WebControls; + +namespace Test; public partial class Test { @@ -184,9 +187,8 @@ public void Test_AddPolyline2() - - // 测试扩展数据 + // 增 [CommandMethod(nameof(Test_AddXdata))] public void Test_AddXdata() { @@ -219,18 +221,38 @@ public void Test_AddXdata() tr.CurrentSpace.AddEntity(line); } + // 删 + [CommandMethod(nameof(Test_RemoveXdata))] + public void Test_RemoveXdata() + { + var appname = "myapp2"; + var res = Env.Editor.GetEntity("\n select the entity:"); + if (res.Status == PromptStatus.OK) + { + using DBTrans tr = new(); + var ent = tr.GetObject(res.ObjectId); + if (ent == null || ent.XData == null) + return; + Env.Printl("\n移除前:" + ent.XData.ToString()); + + ent.RemoveXData(appname, DxfCode.ExtendedDataAsciiString); + Env.Printl("\n移除成员后:" + ent.XData.ToString()); + + ent.RemoveXData(appname); + Env.Printl("\n移除appName后:" + ent.XData.ToString()); + } + } + // 查 [CommandMethod(nameof(Test_GetXdata))] public void Test_GetXdata() { - var doc = Acap.DocumentManager.MdiActiveDocument; - var ed = doc.Editor; using DBTrans tr = new(); tr.RegAppTable.ForEach(id => id.GetObject()?.Name.Print()); tr.RegAppTable.GetRecords().ForEach(rec => rec.Name.Print()); tr.RegAppTable.GetRecordNames().ForEach(name => name.Print()); - tr.RegAppTable.ForEach(re => re.Name.Print(), checkIdOk: false); + tr.RegAppTable.ForEach(reg => reg.Name.Print(), checkIdOk: false); // var res = ed.GetEntity("\n select the entity:"); // if (res.Status == PromptStatus.OK) @@ -240,43 +262,49 @@ public void Test_GetXdata() // var data = tr.GetObject(res.ObjectId).XData; // ed.WriteMessage(data.ToString()); // } - } - [CommandMethod(nameof(Test_Changexdata))] - public void Test_Changexdata() + // 查询appName里面是否含有某个 + var appname = "myapp2"; + var res = Env.Editor.GetEntity("\n select the entity:"); + if (res.Status == PromptStatus.OK) + { + var ent = tr.GetObject(res.ObjectId); + if (ent == null || ent.XData == null) + return; + + XDataList data = ent.XData; + if (data.Contains(appname)) + Env.Printl("含有appName:" + appname); + else + Env.Printl("不含有appName:" + appname); + + var str = "要移除的我"; + if (data.Contains(appname, str)) + Env.Printl("含有内容:" + str); + else + Env.Printl("不含有内容:" + str); + } + } + // 改 + [CommandMethod(nameof(Test_ChangeXdata))] + public void Test_ChangeXdata() { - var doc = Acap.DocumentManager.MdiActiveDocument; - var ed = doc.Editor; var appname = "myapp"; - var res = ed.GetEntity("\n select the entity:"); + var res = Env.Editor.GetEntity("\n select the entity:"); if (res.Status == PromptStatus.OK) { using DBTrans tr = new(); var data = tr.GetObject(res.ObjectId)!; data.ChangeXData(appname, DxfCode.ExtendedDataAsciiString, "change"); - ed.WriteMessage(data.XData.ToString()); + Env.Printl(data.XData.ToString()); } } - [CommandMethod(nameof(Test_Removexdata))] - public void Test_Removexdata() - { - var doc = Acap.DocumentManager.MdiActiveDocument; - var ed = doc.Editor; - var appname = "myapp2"; - var res = ed.GetEntity("\n select the entity:"); - if (res.Status == PromptStatus.OK) - { - using DBTrans tr = new(); - var ent = tr.GetObject(res.ObjectId); - ed.WriteMessage("\n移除前:" + ent?.XData.ToString()); - ent?.RemoveXData(appname, DxfCode.ExtendedDataAsciiString); - ed.WriteMessage("\n移除后:" + ent?.XData.ToString()); - } - } + + [CommandMethod(nameof(Test_PrintLayerName))] public void Test_PrintLayerName() -- Gitee From 952c6d33d8d7afc5139e4a148f093e4b62d63f95 Mon Sep 17 00:00:00 2001 From: liuqihong <540762622@qq.com> Date: Fri, 21 Oct 2022 22:10:32 +0800 Subject: [PATCH 571/675] =?UTF-8?q?=E5=8A=A0=E5=85=A5json=E8=BD=AC?= =?UTF-8?q?=E6=8D=A2=E5=99=A8=E5=A4=84=E7=90=86objectid?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../ExtensionMethod/Filer/DwgFiler.cs | 31 +++-- .../ExtensionMethod/Filer/DwgFilerEx.cs | 26 ++-- .../ExtensionMethod/JsonConverter.cs | 116 ++++++++++++++++++ .../IFoxCAD.Cad.Shared.projitems | 1 + .../IFoxCAD.Aacad09plus/GlobalUsings.cs | 6 +- .../IFoxCAD.Acad09plus.csproj | 12 +- src/IFoxCAD.Cad/IFoxCAD.Gcad/GlobalUsings.cs | 6 +- src/IFoxCAD.Cad/IFoxCAD.Zcad/GlobalUsings.cs | 6 +- tests/TestAcad09plus/GlobalUsings.cs | 4 + 9 files changed, 176 insertions(+), 32 deletions(-) create mode 100644 src/IFoxCAD.Cad.Shared/ExtensionMethod/JsonConverter.cs diff --git a/src/IFoxCAD.Cad.Shared/ExtensionMethod/Filer/DwgFiler.cs b/src/IFoxCAD.Cad.Shared/ExtensionMethod/Filer/DwgFiler.cs index 32e3614..6820f37 100644 --- a/src/IFoxCAD.Cad.Shared/ExtensionMethod/Filer/DwgFiler.cs +++ b/src/IFoxCAD.Cad.Shared/ExtensionMethod/Filer/DwgFiler.cs @@ -1,6 +1,5 @@ -using System.Web.Script.Serialization; +namespace IFoxCAD.Cad; -namespace IFoxCAD.Cad; /* Arx自定义实体类,加 读函数(assertReadEnabled)和写函数(assertWriteEnabled) @@ -9,7 +8,9 @@ [Serializable] 设置类 可以序列化 [Newtonsoft.Json.JsonIgnore] 设置成员 不可序列化 */ - +#if NewtonsoftJson +[JsonConverter(typeof(ObjectIdConverter))] +#endif [Serializable] public class DwgFiler : Cad_DwgFiler { @@ -34,12 +35,12 @@ public class DwgFiler : Cad_DwgFiler public int DoubleListPt = 0; public List HandleList; public int HandleListPt = 0; - [NonSerialized] - [ScriptIgnore] + //[NonSerialized] + //[ScriptIgnore] public List HardOwnershipIdList; public int HardOwnershipIdListPt = 0; - [NonSerialized] - [ScriptIgnore] + //[NonSerialized] + //[ScriptIgnore] public List HardPointerIdList; public int HardPointerIdListPt = 0; public List Int16List; @@ -56,12 +57,12 @@ public class DwgFiler : Cad_DwgFiler public int Point3dListPt = 0; public List Scale3dList; public int Scale3dListPt = 0; - [NonSerialized] - [ScriptIgnore] + //[NonSerialized] + //[ScriptIgnore] public List SoftOwnershipIdList; public int SoftOwnershipIdListPt = 0; - [NonSerialized] - [ScriptIgnore] + //[NonSerialized] + //[ScriptIgnore] public List SoftPointerIdList; public int SoftPointerIdListPt = 0; public List StringList; @@ -494,6 +495,12 @@ public override void WriteVector3d(Vector3d value) public override string ToString() { - return new JavaScriptSerializer().Serialize(this); +#if NewtonsoftJson + return JsonConvert.SerializeObject(this, Formatting.Indented); +#else + JavaScriptSerializer serializer = new(); + serializer.RegisterConverters(new[] { new ObjectIdConverter() }); + return serializer.Serialize(this); +#endif } } \ No newline at end of file diff --git a/src/IFoxCAD.Cad.Shared/ExtensionMethod/Filer/DwgFilerEx.cs b/src/IFoxCAD.Cad.Shared/ExtensionMethod/Filer/DwgFilerEx.cs index 2f8e0c9..f91fb4c 100644 --- a/src/IFoxCAD.Cad.Shared/ExtensionMethod/Filer/DwgFilerEx.cs +++ b/src/IFoxCAD.Cad.Shared/ExtensionMethod/Filer/DwgFilerEx.cs @@ -48,10 +48,15 @@ public void DwgIn() /// /// /// - public DwgFilerEx? DeserializeObject(string json) + public static DwgFilerEx? DeserializeObject(string json) { - throw new ArgumentException(); - // return JsonConvert.DeserializeObject(json);// 反序列化*字符串转类 +#if NewtonsoftJson + return JsonConvert.DeserializeObject(json); +#else + JavaScriptSerializer serializer = new(); + serializer.RegisterConverters(new[] { new ObjectIdConverter() }); + return serializer.Deserialize(json); +#endif } /// @@ -60,18 +65,17 @@ public void DwgIn() /// public string SerializeObject() { - throw new ArgumentException(); - // return JsonConvert.SerializeObject(DwgFiler, Formatting.Indented); // 序列化*类转字符串 +#if NewtonsoftJson + return JsonConvert.SerializeObject(DwgFiler, Formatting.Indented); +#else + JavaScriptSerializer serializer = new(); + serializer.RegisterConverters(new[] { new ObjectIdConverter() }); + return serializer.Serialize(DwgFiler); +#endif } public override string ToString() { - // 替换中括号以外的字符串,替换逗号为换行符 https://bbs.csdn.net/topics/370134253 - // var str = SerializeObject(); - // str = str.Substring(1, str.Length - 2); - // str = Regex.Replace(str, @"(?:,)(?![^\[]*?\])", "\r\n"); - // return str; - return DwgFiler.ToString(); } diff --git a/src/IFoxCAD.Cad.Shared/ExtensionMethod/JsonConverter.cs b/src/IFoxCAD.Cad.Shared/ExtensionMethod/JsonConverter.cs new file mode 100644 index 0000000..62fd55e --- /dev/null +++ b/src/IFoxCAD.Cad.Shared/ExtensionMethod/JsonConverter.cs @@ -0,0 +1,116 @@ +namespace IFoxCAD.Cad; + +#if NewtonsoftJson +/* + * 参考 https://www.cnblogs.com/fps2tao/p/14798710.html + * json类型转换器,使用方法: + * 在类上面增加此特性: [JsonConverter(typeof(ObjectIdConverter))] + */ +/// +/// json转换器 +/// +public class ObjectIdConverter : JsonConverter +{ + /// + /// 约束类型 + /// + public override bool CanConvert(Type objectType) + { + return typeof(ObjectId) == objectType; + } + + /// + /// 反序列化_把字符串生成对象 + /// + public override object ReadJson(JsonReader reader, Type objectType, object? existingValue, JsonSerializer serializer) + { + if (reader.Value == null) + return ObjectId.Null; + try + { + using DBTrans tr = new(); + var id = tr.GetObjectId(reader.Value.ToString()); + return id; + } + catch { return ObjectId.Null; } + } + + /// + /// 序列化_写入json + /// + public override void WriteJson(JsonWriter writer, object? value, JsonSerializer serializer) + { + if (value is ObjectId id) + writer.WriteValue(id == ObjectId.Null ? 0 : id.Handle.Value); + } +} +#else +/* + * 参考 https://developer.aliyun.com/article/51053 + * json类型转换器,使用方法: + * public static string SerializeToJson(object obj) + * { + * JavaScriptSerializer serializer = new(); + * serializer.RegisterConverters(new[] { new ObjectIdConverter() }); + * return serializer.Serialize(obj); + * } + * + * public static T DeserializeJson(string jsonString) + * { + * JavaScriptSerializer serializer = new(); + * serializer.RegisterConverters(new[] { new ObjectIdConverter() }); + * return serializer.Deserialize(jsonString); + * } + */ +/// +/// json转换器 +/// +public class ObjectIdConverter : JavaScriptConverter +{ + const string _id = nameof(ObjectId); + + /// + /// 约束类型 + /// + public override IEnumerable SupportedTypes => new Type[] { typeof(ObjectId) }; + + /// + /// 序列化_写入json + /// + public override IDictionary Serialize(object obj, JavaScriptSerializer serializer) + { + if (obj is not ObjectId id) + return null!; + + Dictionary result = new() + { + { _id, id == ObjectId.Null ? 0 : id.Handle.Value } + }; + return result; + } + + /// + /// 反序列化_把字符串生成对象 + /// + public override object Deserialize(IDictionary dictionary, Type type, JavaScriptSerializer serializer) + { + if (dictionary == null) + throw new ArgumentNullException(nameof(dictionary)); + + if (type != typeof(ObjectId)) + return null!; + + ObjectId id = new(); + try + { + if (dictionary.TryGetValue(_id, out object value)) + { + using DBTrans tr = new(); + id = tr.GetObjectId(value.ToString()); + } + } + catch { return ObjectId.Null; } + return id; + } +} +#endif \ No newline at end of file diff --git a/src/IFoxCAD.Cad.Shared/IFoxCAD.Cad.Shared.projitems b/src/IFoxCAD.Cad.Shared/IFoxCAD.Cad.Shared.projitems index 9184907..79335fe 100644 --- a/src/IFoxCAD.Cad.Shared/IFoxCAD.Cad.Shared.projitems +++ b/src/IFoxCAD.Cad.Shared/IFoxCAD.Cad.Shared.projitems @@ -19,6 +19,7 @@ + diff --git a/src/IFoxCAD.Cad/IFoxCAD.Aacad09plus/GlobalUsings.cs b/src/IFoxCAD.Cad/IFoxCAD.Aacad09plus/GlobalUsings.cs index 52c60de..4227a00 100644 --- a/src/IFoxCAD.Cad/IFoxCAD.Aacad09plus/GlobalUsings.cs +++ b/src/IFoxCAD.Cad/IFoxCAD.Aacad09plus/GlobalUsings.cs @@ -42,4 +42,8 @@ global using Cad_ErrorStatus = Autodesk.AutoCAD.Runtime.ErrorStatus; /// ifoxcad.basal 引用 -global using IFoxCAD.Basal; \ No newline at end of file +global using IFoxCAD.Basal; + +#if !NewtonsoftJson +global using System.Web.Script.Serialization; +#endif \ No newline at end of file diff --git a/src/IFoxCAD.Cad/IFoxCAD.Aacad09plus/IFoxCAD.Acad09plus.csproj b/src/IFoxCAD.Cad/IFoxCAD.Aacad09plus/IFoxCAD.Acad09plus.csproj index 4340d95..071ebc3 100644 --- a/src/IFoxCAD.Cad/IFoxCAD.Aacad09plus/IFoxCAD.Acad09plus.csproj +++ b/src/IFoxCAD.Cad/IFoxCAD.Aacad09plus/IFoxCAD.Acad09plus.csproj @@ -40,14 +40,14 @@ - + - + - - + + @@ -62,10 +62,10 @@ --> - + - + diff --git a/src/IFoxCAD.Cad/IFoxCAD.Gcad/GlobalUsings.cs b/src/IFoxCAD.Cad/IFoxCAD.Gcad/GlobalUsings.cs index c8d64c4..136bc7a 100644 --- a/src/IFoxCAD.Cad/IFoxCAD.Gcad/GlobalUsings.cs +++ b/src/IFoxCAD.Cad/IFoxCAD.Gcad/GlobalUsings.cs @@ -42,4 +42,8 @@ global using Cad_ErrorStatus = GrxCAD.Runtime.ErrorStatus; /// ifoxcad.basal 引用 -global using IFoxCAD.Basal; \ No newline at end of file +global using IFoxCAD.Basal; + +#if !NewtonsoftJson +global using System.Web.Script.Serialization; +#endif \ No newline at end of file diff --git a/src/IFoxCAD.Cad/IFoxCAD.Zcad/GlobalUsings.cs b/src/IFoxCAD.Cad/IFoxCAD.Zcad/GlobalUsings.cs index 721b5cb..add1774 100644 --- a/src/IFoxCAD.Cad/IFoxCAD.Zcad/GlobalUsings.cs +++ b/src/IFoxCAD.Cad/IFoxCAD.Zcad/GlobalUsings.cs @@ -41,4 +41,8 @@ global using Cad_ErrorStatus = ZwSoft.ZwCAD.Runtime.ErrorStatus; /// ifoxcad.basal 引用 -global using IFoxCAD.Basal; \ No newline at end of file +global using IFoxCAD.Basal; + +#if !NewtonsoftJson +global using System.Web.Script.Serialization; +#endif \ No newline at end of file diff --git a/tests/TestAcad09plus/GlobalUsings.cs b/tests/TestAcad09plus/GlobalUsings.cs index 56ba5f9..82e3ea7 100644 --- a/tests/TestAcad09plus/GlobalUsings.cs +++ b/tests/TestAcad09plus/GlobalUsings.cs @@ -47,4 +47,8 @@ global using IFoxCAD.Basal; #if !ac2008 global using IFoxCAD.WPF; +#endif + +#if !NewtonsoftJson +global using System.Web.Script.Serialization; #endif \ No newline at end of file -- Gitee From e07eddf81b2a608b5fffb8b2421ffcfdf802784e Mon Sep 17 00:00:00 2001 From: liuqihong <540762622@qq.com> Date: Sat, 22 Oct 2022 05:21:26 +0800 Subject: [PATCH 572/675] =?UTF-8?q?=E6=B5=8B=E8=AF=95=E6=B7=BB=E5=8A=A0?= =?UTF-8?q?=E5=9D=97=E7=BC=96=E8=BE=91=E5=91=BD=E4=BB=A4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- tests/TestShared/TestBlock.cs | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/tests/TestShared/TestBlock.cs b/tests/TestShared/TestBlock.cs index 925875d..8e24532 100644 --- a/tests/TestShared/TestBlock.cs +++ b/tests/TestShared/TestBlock.cs @@ -2,6 +2,27 @@ public class TestBlock { + // 一个命令就把块编辑搞定,减少用户记忆命令 + [CommandMethod(nameof(Test_Refedit), CommandFlags.Redraw | CommandFlags.Session)] + public void Test_Refedit() + { + Env.Printl($"{nameof(Test_Refedit)}-在位编辑块/在位保存块"); + + // 全部用lisp发送命令是为了空格还是本命令 + // 打开了块编辑器,就关闭掉,保存提示 + if ((short)Env.GetVar("BlockEditor") == 1) + { + Env.Editor.RunLisp("(command \"_.bclose\")"); + return; + } + // 0x01 非在位编辑状态: 先选择块参照,然后在位编辑 + // 0x02 在位编辑状态: 关闭并保存 + if (Env.GetVar("RefEditName").ToString() == "")//显示正在编辑的参照名称 + Env.Editor.RunLisp("(command \"_.refedit\")");//直接点选可以有嵌套层次 + else + Env.Editor.RunLisp("(command \"_.refclose\" \"s\")"); + } + [CommandMethod(nameof(Test_GetBoundingBoxEx))] public void Test_GetBoundingBoxEx() { -- Gitee From 31c2e9bc98b19d679eaad544b3c63babc00504aa Mon Sep 17 00:00:00 2001 From: liuqihong <540762622@qq.com> Date: Tue, 25 Oct 2022 19:03:39 +0800 Subject: [PATCH 573/675] =?UTF-8?q?=E4=BC=98=E5=8C=96=E4=BB=A3=E7=A0=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../ExtensionMethod/JsonConverter.cs | 10 +++++----- src/IFoxCAD.Cad.Shared/Runtime/IAutoGo.cs | 11 +++++++---- 2 files changed, 12 insertions(+), 9 deletions(-) diff --git a/src/IFoxCAD.Cad.Shared/ExtensionMethod/JsonConverter.cs b/src/IFoxCAD.Cad.Shared/ExtensionMethod/JsonConverter.cs index 62fd55e..c1044a9 100644 --- a/src/IFoxCAD.Cad.Shared/ExtensionMethod/JsonConverter.cs +++ b/src/IFoxCAD.Cad.Shared/ExtensionMethod/JsonConverter.cs @@ -72,7 +72,7 @@ public class ObjectIdConverter : JavaScriptConverter /// /// 约束类型 /// - public override IEnumerable SupportedTypes => new Type[] { typeof(ObjectId) }; + public override IEnumerable SupportedTypes => new[] { typeof(ObjectId) }; /// /// 序列化_写入json @@ -100,16 +100,16 @@ public override object Deserialize(IDictionary dictionary, Type if (type != typeof(ObjectId)) return null!; - ObjectId id = new(); + ObjectId id = ObjectId.Null; try { - if (dictionary.TryGetValue(_id, out object value)) + if (dictionary.TryGetValue(_id, out object va)) { using DBTrans tr = new(); - id = tr.GetObjectId(value.ToString()); + id = tr.GetObjectId(va.ToString()); } } - catch { return ObjectId.Null; } + catch { } return id; } } diff --git a/src/IFoxCAD.Cad.Shared/Runtime/IAutoGo.cs b/src/IFoxCAD.Cad.Shared/Runtime/IAutoGo.cs index 26f3de5..b9e5624 100644 --- a/src/IFoxCAD.Cad.Shared/Runtime/IAutoGo.cs +++ b/src/IFoxCAD.Cad.Shared/Runtime/IAutoGo.cs @@ -365,10 +365,13 @@ public static void DebugCheckCmdRecurrence() var sb = new StringBuilder(); foreach (string key in msgMod) sb.AppendLine(key); - Env.Printl("当前cad环境加载的多个DLL中存在重复命令将被覆盖:"); - Env.Printl("{"); - Env.Printl(sb.ToString()); - Env.Printl("}"); + if (sb.Length != 0) + { + Env.Printl("当前cad环境加载的多个DLL中存在重复命令将被覆盖:"); + Env.Printl("{"); + Env.Printl(sb.ToString()); + Env.Printl("}"); + } } #endif } \ No newline at end of file -- Gitee From 3a3c8dde1c9cae02d8429d26b84722dd70771c89 Mon Sep 17 00:00:00 2001 From: liuqihong <540762622@qq.com> Date: Tue, 25 Oct 2022 21:38:46 +0800 Subject: [PATCH 574/675] =?UTF-8?q?=E5=9C=A8=E4=BA=8B=E5=8A=A1=E6=A0=88?= =?UTF-8?q?=E7=B1=BB=E4=B8=AD=E5=A2=9E=E5=8A=A0=E6=89=93=E5=BC=80=E5=9B=BE?= =?UTF-8?q?=E7=BA=B8=E7=9A=84=E5=8F=98=E9=87=8F=E5=B9=B6=E6=8F=90=E4=BE=9B?= =?UTF-8?q?=E6=B5=8B=E8=AF=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/IFoxCAD.Cad.Shared/Runtime/DBTrans.cs | 189 +++++++++++------- .../Runtime/FileOpenMode.cs | 48 ++++- tests/TestShared/TestDBTrans.cs | 10 +- 3 files changed, 172 insertions(+), 75 deletions(-) diff --git a/src/IFoxCAD.Cad.Shared/Runtime/DBTrans.cs b/src/IFoxCAD.Cad.Shared/Runtime/DBTrans.cs index bc35ddf..3f4a87f 100644 --- a/src/IFoxCAD.Cad.Shared/Runtime/DBTrans.cs +++ b/src/IFoxCAD.Cad.Shared/Runtime/DBTrans.cs @@ -14,13 +14,13 @@ namespace IFoxCAD.Cad; public class DBTrans : IDisposable { [DebuggerBrowsable(DebuggerBrowsableState.Never)] - private string DebuggerDisplay => "StackCount:" + dBTrans.Count; + private string DebuggerDisplay => ToString(" | "); #region 私有字段 /// /// 文档锁 /// - private readonly DocumentLock? documentLock; + private readonly DocumentLock? _documentLock; /// /// 是否提交事务 /// @@ -28,7 +28,7 @@ public class DBTrans : IDisposable /// /// 事务栈 /// - private static readonly Stack dBTrans = new(); + private static readonly Stack _dBTrans = new(); /// /// 文件名 /// @@ -63,13 +63,12 @@ public static DBTrans Top */ // 由于大量的函数依赖本属性,强迫用户先开启事务 - if (dBTrans.Count == 0) + if (_dBTrans.Count == 0) throw new ArgumentNullException("事务栈没有任何事务,请在调用前创建:" + nameof(DBTrans)); - var trans = dBTrans.Peek(); + var trans = _dBTrans.Peek(); return trans; } } - /// /// 文档 /// @@ -104,9 +103,9 @@ public DBTrans(Document? doc = null, bool commit = true, bool doclock = false) Transaction = Database.TransactionManager.StartTransaction(); _commit = commit; if (doclock) - documentLock = Document.LockDocument(); + _documentLock = Document.LockDocument(); - dBTrans.Push(this); + _dBTrans.Push(this); } /// @@ -120,7 +119,7 @@ public DBTrans(Database database, bool commit = true) Database = database; Transaction = Database.TransactionManager.StartTransaction(); _commit = commit; - dBTrans.Push(this); + _dBTrans.Push(this); } /// @@ -131,10 +130,12 @@ public DBTrans(Database database, bool commit = true) /// 事务是否提交 /// 开图模式 /// 密码 + /// 后台打开false;前台打开true(必须设置CommandFlags.Session) public DBTrans(string fileName, bool commit = true, FileOpenMode fileOpenMode = FileOpenMode.OpenForReadAndWriteNoShare, - string? password = null) + string? password = null, + bool activeOpen = false) { if (fileName == null || string.IsNullOrEmpty(fileName.Trim())) throw new ArgumentNullException(nameof(fileName)); @@ -145,71 +146,89 @@ public DBTrans(string fileName, // 因此用 _fileName 储存 if (!File.Exists(_fileName)) { - // 第二个参数如果使用了false,那么将导致关闭cad的时候出现致命错误;cad08测试 - // Unhandled Access Violation Reading Ox113697a0 Exception at 4b4154h - Database = new Database(true, true); + if (activeOpen) + { + throw new IOException("错误:事务栈明确为前台开图时,文件不存在"); + } + else + { + // cad08测试: + // 第2个参数使用false,将导致关闭cad的时候出现致命错误: + // Unhandled Access Violation Reading Ox113697a0 Exception at 4b4154h + Database = new Database(true, true); + } } else { var doc = Acap.DocumentManager .Cast() .FirstOrDefault(doc => doc.Name == _fileName); - if (doc is not null) + + if (activeOpen) { + if (doc is null) + { + try + { + // 需要设置命令标记: CommandFlags.Session + // 若没有设置: Open()之后的会进入中断状态(不会执行) + bool forReadOnly = false; + if ((fileOpenMode & FileOpenMode.OpenForReadAndReadShare) == FileOpenMode.OpenForReadAndReadShare) + forReadOnly = true; + doc = Acap.DocumentManager.Open(fileName, forReadOnly, password); + } + catch (Exception e) + { + throw new IOException("错误:此文件打开错误: " + fileName + "\n错误信息:" + e.Message); + } + } + // 需要设置命令标记: CommandFlags.Session + // 若没有设置: 0x01 IsActive 在初始化时会异常, + // 0x02 赋值代码之后不会继续执行 + if (!doc.IsDisposed) + { + if (!doc.IsActive) + Acap.DocumentManager.MdiActiveDocument = doc; + + // 必须要锁图,否则 Editor?.Redraw() 的 tm.QueueForGraphicsFlush() 将报错提示文档锁 + _documentLock = doc.LockDocument(); + } + Database = doc.Database; Document = doc; Editor = doc.Editor; } else { - Database = new Database(false, true); - if (Path.GetExtension(_fileName).ToLower().Contains("dxf")) - { - Database.DxfIn(_fileName, null); - } - else + if (doc is null) { -#if ac2008 - // FileAccess fileAccess = FileAccess.Read; - FileShare fileShare = FileShare.Read; - switch (fileOpenMode) + Database = new Database(false, true); + if (Path.GetExtension(_fileName).ToLower().Contains("dxf")) { - case FileOpenMode.OpenTryForReadShare:// 这个是什么状态?? - // fileAccess = FileAccess.ReadWrite; - fileShare = FileShare.ReadWrite; - break; - case FileOpenMode.OpenForReadAndAllShare:// 完美匹配 - // fileAccess = FileAccess.ReadWrite; - fileShare = FileShare.ReadWrite; - break; - case FileOpenMode.OpenForReadAndWriteNoShare:// 完美匹配 - // fileAccess = FileAccess.ReadWrite; - fileShare = FileShare.None; - break; - case FileOpenMode.OpenForReadAndReadShare:// 完美匹配 - // fileAccess = FileAccess.Read; - fileShare = FileShare.Read; - break; - default: - break; + Database.DxfIn(_fileName, null); } - - // 这个会致命错误 - // using FileStream fileStream = new(_fileName, FileMode.Open, fileAccess, fileShare); - // Database.ReadDwgFile(fileStream.SafeFileHandle.DangerousGetHandle(), true, password); - - Database.ReadDwgFile(_fileName, fileShare, true, password); + else + { +#if ac2008 + Database.ReadDwgFile(_fileName, FileOpenModeHelper.GetFileShare(fileOpenMode), true, password); #else - Database.ReadDwgFile(_fileName, fileOpenMode, true, password); + Database.ReadDwgFile(_fileName, fileOpenMode, true, password); #endif + } + Database.CloseInput(true); + } + else + { + Database = doc.Database; + Document = doc; + Editor = doc.Editor; } - Database.CloseInput(true); } } Transaction = Database.TransactionManager.StartTransaction(); _commit = commit; - dBTrans.Push(this); + _dBTrans.Push(this); } #endregion @@ -637,30 +656,62 @@ protected virtual void Dispose(bool disposing) if (IsDisposed) return; IsDisposed = true; - - if (_commit)// disposing + // 致命错误时候此处是空,直接跳过 + if (Transaction != null) { - // 刷新队列(后台不刷新) - Editor?.Redraw(); + if (_commit)// disposing + { + // 刷新队列(后台不刷新) + Editor?.Redraw(); - // 调用cad的事务进行提交,释放托管状态(托管对象) - Transaction.Commit(); - } - else - { - // 否则取消所有的修改 - Transaction.Abort(); - } + // 调用cad的事务进行提交,释放托管状态(托管对象) + Transaction.Commit(); + } + else + { + // 否则取消所有的修改 + Transaction.Abort(); + } - // 将cad事务进行销毁 - if (!Transaction.IsDisposed) - Transaction.Dispose(); + // 将cad事务进行销毁 + if (!Transaction.IsDisposed) + Transaction.Dispose(); - // 将文档锁销毁 - documentLock?.Dispose(); + // 将文档锁销毁 + _documentLock?.Dispose(); + } // 将当前事务栈弹栈 - dBTrans.Pop(); + _dBTrans.Pop(); + } + #endregion + + #region ToString + public override string ToString() + { + return ToString(null, null); + } + public string ToString(IFormatProvider? provider) + { + return ToString(null, provider); + } + public string ToString(string? format = null, IFormatProvider? formatProvider = null) + { + List lines = new(); + lines.Add($"StackCount = {_dBTrans.Count}"); + lines.Add($"_fileName = \"{_fileName}\""); + lines.Add($"_commit = {_commit}"); + lines.Add($"_documentLock = {_documentLock != null}"); + + lines.Add($"Document = {Document != null}"); + lines.Add($"Editor = {Editor != null}"); + lines.Add($"Transaction = {Transaction != null}"); + lines.Add($"Database = {Database != null}"); + + if (!string.IsNullOrEmpty(format)) + return string.Join(format, lines.ToArray()); + + return string.Join("\n", lines.ToArray()); } #endregion } \ No newline at end of file diff --git a/src/IFoxCAD.Cad.Shared/Runtime/FileOpenMode.cs b/src/IFoxCAD.Cad.Shared/Runtime/FileOpenMode.cs index 912b531..0939073 100644 --- a/src/IFoxCAD.Cad.Shared/Runtime/FileOpenMode.cs +++ b/src/IFoxCAD.Cad.Shared/Runtime/FileOpenMode.cs @@ -4,10 +4,52 @@ namespace Autodesk.AutoCAD.DatabaseServices [Wrapper("AcDbDatabase::OpenMode")] public enum FileOpenMode { - OpenTryForReadShare = 4, - OpenForReadAndAllShare = 3, + /// + /// 只读模式打开 + /// + OpenForReadAndReadShare = 1, OpenForReadAndWriteNoShare = 2, - OpenForReadAndReadShare = 1 + OpenForReadAndAllShare = 3, + OpenTryForReadShare = 4, + } + + public static class FileOpenModeHelper + { + /* + * 这个开图方式会致命错误,不清楚怎么用的文件句柄开图 + * using FileStream fileStream = new(_fileName, FileMode.Open, fileAccess, GetFileShare(fileOpenMode)); + * Database.ReadDwgFile(fileStream.SafeFileHandle.DangerousGetHandle(), true, password); + */ + public static FileShare GetFileShare(FileOpenMode fileOpenMode) + { + // FileAccess fileAccess = FileAccess.Read; + FileShare fileShare = FileShare.Read; + switch (fileOpenMode) + { + // 不完美匹配 + case FileOpenMode.OpenTryForReadShare: + // fileAccess = FileAccess.ReadWrite; + fileShare = FileShare.ReadWrite; + break; + // 完美匹配 + case FileOpenMode.OpenForReadAndAllShare: + // fileAccess = FileAccess.ReadWrite; + fileShare = FileShare.ReadWrite; + break; + // 完美匹配 + case FileOpenMode.OpenForReadAndWriteNoShare: + // fileAccess = FileAccess.ReadWrite; + fileShare = FileShare.None; + break; + // 完美匹配 + case FileOpenMode.OpenForReadAndReadShare: + // fileAccess = FileAccess.Read; + fileShare = FileShare.Read; + break; + } + return fileShare; + } } } + #endif \ No newline at end of file diff --git a/tests/TestShared/TestDBTrans.cs b/tests/TestShared/TestDBTrans.cs index 5fb6633..0dc6050 100644 --- a/tests/TestShared/TestDBTrans.cs +++ b/tests/TestShared/TestDBTrans.cs @@ -1,9 +1,13 @@ -using System.Diagnostics; - -namespace Test; +namespace Test; public class TestTrans { + [CommandMethod(nameof(CmdTest_DBTransActiveOpenDwg), CommandFlags.Session)] + public static void CmdTest_DBTransActiveOpenDwg() + { + using DBTrans tr = new(@"D:\桌面\AA.dwg", activeOpen: true); + } + [CommandMethod(nameof(CmdTest_ForEachDemo))] public static void CmdTest_ForEachDemo() { -- Gitee From 60e311003327dae71bd30da41a5d610318f11a16 Mon Sep 17 00:00:00 2001 From: liuqihong <540762622@qq.com> Date: Tue, 25 Oct 2022 23:05:54 +0800 Subject: [PATCH 575/675] =?UTF-8?q?=E5=8A=A0=E5=85=A5=E8=BE=93=E5=85=A5?= =?UTF-8?q?=E6=B3=95API?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/IFoxCAD.Cad.Shared/Copyclip/WindowsAPI.cs | 160 ++++++++++++++++++ 1 file changed, 160 insertions(+) diff --git a/src/IFoxCAD.Cad.Shared/Copyclip/WindowsAPI.cs b/src/IFoxCAD.Cad.Shared/Copyclip/WindowsAPI.cs index 7cdf5ed..4e9783c 100644 --- a/src/IFoxCAD.Cad.Shared/Copyclip/WindowsAPI.cs +++ b/src/IFoxCAD.Cad.Shared/Copyclip/WindowsAPI.cs @@ -105,6 +105,7 @@ public static bool GlobalLockTask(IntPtr data, Action task) } + /// /// byte数组转结构体 /// @@ -195,6 +196,165 @@ static void ToPtr(object? structObj, Action? task, IntPtr newPtr) task?.Invoke(newPtr); } } + + + #region imm32 + /// + /// 获取输入法的虚拟键码 + /// + /// + /// + [DllImport("imm32.dll")] + public static extern IntPtr ImmGetVirtualKey(IntPtr hWnd); + /// + /// 获取输入法状态 + /// + /// 输入法标识符 + /// 输入模式 + /// 指向函数在其中检索句子模式值的变量的指针 + /// + [DllImport("imm32.dll")] + public static extern bool ImmGetConversionStatus(IntPtr himc, out int lpdw, out int lpdw2); + + /// + /// 获取指定窗口的输入法状态 + /// + /// 窗口句柄 + /// + [DllImport("imm32.dll")] + public static extern IntPtr ImmGetContext(IntPtr hwnd); + /// + /// 设置输入法的当前状态 + /// + /// 窗口句柄 + /// + /// + [DllImport("imm32.dll")] + public static extern bool ImmSetOpenStatus(IntPtr hwnd, bool fOpen); + /// + /// 输入法打开状态 + /// + /// + /// 非0值打开,0值关闭 + [DllImport("imm32.dll")] + public static extern bool ImmGetOpenStatus(IntPtr hwnd); + #endregion + + #region user32 + /// + /// 获取当前窗口 + /// + /// 当前窗口标识符 + [DllImport("user32.dll")] + public static extern IntPtr GetForegroundWindow(); + /// + /// 将一个消息的组成部分合成一个消息并放入对应线程消息队列的方法 + /// + /// 控件句柄 + /// 消息是什么。键盘按键、鼠标点击还是其他 + /// + /// + /// + [DllImport("user32.dll")] + public static extern bool PostMessage(IntPtr hhwnd, int msg, IntPtr wparam, IntPtr lparam); + /// + /// 发送击键 + /// + /// + /// + /// + /// + [DllImport("user32.dll")] + public static extern void keybd_event(byte bVk, byte bScan, int dwFlags, int dwExtraInfo); + /// + /// 获取窗口文字的长度 + /// + /// 窗口标识符 + /// 文字长度 + [DllImport("user32.dll")] + public static extern int GetWindowTextLength(IntPtr hWnd); + /// + /// 获取窗口的标题 + /// + /// 窗口标识符 + /// 窗口文字 + /// 文字长度 + /// + [DllImport("User32.dll", CharSet = CharSet.Auto)] + public static extern int GetWindowText(IntPtr hWnd, StringBuilder text, int nMaxCount); + + /// + /// 获取某个线程的输入法布局 + /// + /// 线程ID + /// 布局码 + [DllImport("user32.dll")] + public static extern int GetKeyboardLayout(int threadid); + /// + /// 获取按键的当前状态 + /// + /// 按键虚拟代码 + /// 按键状态值,高位为1表示按下(<0),0表示弹起(>0) + [DllImport("user32.dll")] + public static extern short GetKeyState(int nVirtKey); + /// + /// 检索指定窗口所属的类的名称。 + /// + /// 窗口标识符 + /// + /// + /// + [DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)] + public static extern int GetClassName(IntPtr hWnd, StringBuilder lpClassName, int nMaxCount); + + [DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)] + public static extern IntPtr GetWindow(IntPtr hWnd, uint uCmd); + + [DllImport("user32.DLL", CharSet = CharSet.Unicode, CallingConvention = CallingConvention.StdCall, SetLastError = true)] + public static extern IntPtr GetTopWindow(IntPtr hWnd); + + + /// + /// 获取线程对应的窗体信息 + /// + /// 线程 + /// + /// + [DllImport("user32.dll")] + [return: MarshalAs(UnmanagedType.Bool)] + static extern bool GetGUIThreadInfo(uint idThread, ref GuiThreadInfo lpgui); + + /// + /// 获取线程对应的窗体信息 + /// + [StructLayout(LayoutKind.Sequential)] + public struct GuiThreadInfo + { + public int cbSize; + public int flags; + public IntPtr hwndActive; + public IntPtr hwndFocus; + public IntPtr hwndCapture; + public IntPtr hwndMenuOwner; + public IntPtr hwndMoveSize; + public IntPtr hwndCaret; + public System.Drawing.Rectangle rcCaret; + + public static GuiThreadInfo Create(uint windowThreadProcessId) + { + if (windowThreadProcessId == 0) + throw new ArgumentNullException(nameof(windowThreadProcessId)); + + GuiThreadInfo gti = new(); + gti.cbSize = Marshal.SizeOf(gti); + GetGUIThreadInfo(windowThreadProcessId, ref gti); + return gti; + } + } + #endregion + + [DllImport("kernel32.dll")] + public static extern int GetCurrentThreadId(); } -- Gitee From f902e6d498f015143230e416ef861b634c186df3 Mon Sep 17 00:00:00 2001 From: liuqihong <540762622@qq.com> Date: Wed, 26 Oct 2022 00:15:03 +0800 Subject: [PATCH 576/675] =?UTF-8?q?=E4=BC=98=E5=8C=96=E4=BB=A3=E7=A0=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/IFoxCAD.Cad.Shared/Runtime/DBTrans.cs | 32 +++++++++-------------- 1 file changed, 13 insertions(+), 19 deletions(-) diff --git a/src/IFoxCAD.Cad.Shared/Runtime/DBTrans.cs b/src/IFoxCAD.Cad.Shared/Runtime/DBTrans.cs index 3f4a87f..16abef4 100644 --- a/src/IFoxCAD.Cad.Shared/Runtime/DBTrans.cs +++ b/src/IFoxCAD.Cad.Shared/Runtime/DBTrans.cs @@ -162,7 +162,7 @@ public DBTrans(string fileName, { var doc = Acap.DocumentManager .Cast() - .FirstOrDefault(doc => doc.Name == _fileName); + .FirstOrDefault(doc => !doc.IsDisposed && doc.Name == _fileName); if (activeOpen) { @@ -170,29 +170,23 @@ public DBTrans(string fileName, { try { - // 需要设置命令标记: CommandFlags.Session - // 若没有设置: Open()之后的会进入中断状态(不会执行) - bool forReadOnly = false; - if ((fileOpenMode & FileOpenMode.OpenForReadAndReadShare) == FileOpenMode.OpenForReadAndReadShare) - forReadOnly = true; - doc = Acap.DocumentManager.Open(fileName, forReadOnly, password); + // 设置命令标记: CommandFlags.Session + // 若没有设置: Open()之后的会进入中断状态(不会执行,直到切换文档ctrl+tab或者关闭文档) + doc = Acap.DocumentManager.Open(fileName, fileOpenMode == FileOpenMode.OpenForReadAndReadShare, password); } catch (Exception e) { - throw new IOException("错误:此文件打开错误: " + fileName + "\n错误信息:" + e.Message); + throw new IOException($"错误:此文件打开错误:{fileName}\n错误信息:{e.Message}"); } } - // 需要设置命令标记: CommandFlags.Session - // 若没有设置: 0x01 IsActive 在初始化时会异常, - // 0x02 赋值代码之后不会继续执行 - if (!doc.IsDisposed) - { - if (!doc.IsActive) - Acap.DocumentManager.MdiActiveDocument = doc; - - // 必须要锁图,否则 Editor?.Redraw() 的 tm.QueueForGraphicsFlush() 将报错提示文档锁 - _documentLock = doc.LockDocument(); - } + // 设置命令标记: CommandFlags.Session + // 若没有设置: doc.IsActive 会异常 + if (!doc.IsActive) + Acap.DocumentManager.MdiActiveDocument = doc; + + // Open()是跨文档,所以必须要锁文档 + // 否则 Editor?.Redraw() 的 tm.QueueForGraphicsFlush() 将报错提示文档锁 + _documentLock = doc.LockDocument(); Database = doc.Database; Document = doc; -- Gitee From bd927cb04aa6ca285cd4b0a57c59ad08aa99f3e7 Mon Sep 17 00:00:00 2001 From: liuqihong <540762622@qq.com> Date: Wed, 26 Oct 2022 14:27:00 +0800 Subject: [PATCH 577/675] =?UTF-8?q?=E6=B3=A8=E5=86=8C=E4=B8=AD=E5=BF=83?= =?UTF-8?q?=E5=A2=9E=E5=8A=A0=E4=B8=8D=E6=93=8D=E4=BD=9C=E6=9E=9A=E4=B8=BE?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/IFoxCAD.Cad.Shared/Runtime/AssemInfo.cs | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/IFoxCAD.Cad.Shared/Runtime/AssemInfo.cs b/src/IFoxCAD.Cad.Shared/Runtime/AssemInfo.cs index 337ac0d..23e5c7d 100644 --- a/src/IFoxCAD.Cad.Shared/Runtime/AssemInfo.cs +++ b/src/IFoxCAD.Cad.Shared/Runtime/AssemInfo.cs @@ -60,6 +60,10 @@ public enum AssemLoadType /// public enum AutoRegConfig { + /// + /// 不进行任何操作 + /// + Undefined = 0, /// /// 注册表 /// @@ -72,7 +76,6 @@ public enum AutoRegConfig /// 反射接口 /// ReflectionInterface = 4, - /// /// 移除教育版 /// -- Gitee From d2dd5a6ae62c2a41de80227ec34948787a6126dc Mon Sep 17 00:00:00 2001 From: liuqihong <540762622@qq.com> Date: Wed, 26 Oct 2022 14:48:36 +0800 Subject: [PATCH 578/675] =?UTF-8?q?=E4=BC=98=E5=8C=96=E4=BB=A3=E7=A0=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/IFoxCAD.Cad.Shared/Runtime/AutoRegAssem.cs | 16 +++++++++++----- 1 file changed, 11 insertions(+), 5 deletions(-) diff --git a/src/IFoxCAD.Cad.Shared/Runtime/AutoRegAssem.cs b/src/IFoxCAD.Cad.Shared/Runtime/AutoRegAssem.cs index 9195d09..e25ccdb 100644 --- a/src/IFoxCAD.Cad.Shared/Runtime/AutoRegAssem.cs +++ b/src/IFoxCAD.Cad.Shared/Runtime/AutoRegAssem.cs @@ -14,7 +14,7 @@ public abstract class AutoRegAssem : IExtensionApplication { #region 字段 - readonly AutoReflection _autoRef; + readonly AutoReflection? _autoRef; readonly AssemInfo _info; #endregion @@ -71,8 +71,12 @@ public AutoRegAssem(AutoRegConfig autoRegConfig) // 实例化了 AutoClass 之后会自动执行 IFoxAutoGo 接口下面的类, // 以及自动执行特性 [IFoxInitialize] // 类库用户不在此处进行其他代码,而是实现特性 - _autoRef = new AutoReflection(_info.Name, autoRegConfig); - _autoRef.Initialize(); + if ((autoRegConfig & AutoRegConfig.ReflectionInterface) == AutoRegConfig.ReflectionInterface || + (autoRegConfig & AutoRegConfig.ReflectionAttribute) == AutoRegConfig.ReflectionAttribute) + { + _autoRef = new AutoReflection(_info.Name, autoRegConfig); + _autoRef.Initialize(); + } } #endregion @@ -165,11 +169,13 @@ public void RegApp() // 这里的是不会自动执行的 public void Initialize() { } - public void Terminate() { } + public void Terminate() + { + } ~AutoRegAssem() { - _autoRef.Terminate(); + _autoRef?.Terminate(); } #endregion RegApp } \ No newline at end of file -- Gitee From 94f5fef3124a8d6c2abc94e5412109ec74743bf8 Mon Sep 17 00:00:00 2001 From: liuqihong <540762622@qq.com> Date: Thu, 27 Oct 2022 09:20:00 +0800 Subject: [PATCH 579/675] =?UTF-8?q?=E4=BF=AE=E5=A4=8Dbug=20IFoxAutoGo?= =?UTF-8?q?=E6=8E=A5=E5=8F=A3=E5=AE=9E=E7=8E=B0=E6=9E=84=E9=80=A0=E5=87=BD?= =?UTF-8?q?=E6=95=B0=E4=BC=9A=E8=A2=AB=E6=89=A7=E8=A1=8C=E4=B8=A4=E6=AC=A1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/IFoxCAD.Basal/CLS/RuntimeHelpers.cs | 4 +- .../ExtensionMethod/EditorEx.cs | 25 ++++++-- .../Runtime/AutoRegAssem.cs | 9 ++- src/IFoxCAD.Cad.Shared/Runtime/Env.cs | 3 +- .../Runtime/FileOpenMode.cs | 36 +++++++++++ src/IFoxCAD.Cad.Shared/Runtime/IAutoGo.cs | 60 +++++++++++++------ .../Runtime/MethodInfoHelper.cs | 25 +++++--- src/IFoxCAD.Cad/IFoxCAD.Gcad/GlobalUsings.cs | 2 + tests/TestShared/CmdINI.cs | 20 ++++++- tests/TestShared/TestAddEntity.cs | 2 + 10 files changed, 147 insertions(+), 39 deletions(-) diff --git a/src/IFoxCAD.Basal/CLS/RuntimeHelpers.cs b/src/IFoxCAD.Basal/CLS/RuntimeHelpers.cs index 7c2a4a9..f0cfa9c 100644 --- a/src/IFoxCAD.Basal/CLS/RuntimeHelpers.cs +++ b/src/IFoxCAD.Basal/CLS/RuntimeHelpers.cs @@ -1,4 +1,4 @@ -// #if NET35 +#if NET35 // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. @@ -42,4 +42,4 @@ public static T[] GetSubArray(T[] array, Range range) } } } -// #endif \ No newline at end of file +#endif \ No newline at end of file diff --git a/src/IFoxCAD.Cad.Shared/ExtensionMethod/EditorEx.cs b/src/IFoxCAD.Cad.Shared/ExtensionMethod/EditorEx.cs index 62a3f44..049c27c 100644 --- a/src/IFoxCAD.Cad.Shared/ExtensionMethod/EditorEx.cs +++ b/src/IFoxCAD.Cad.Shared/ExtensionMethod/EditorEx.cs @@ -601,9 +601,7 @@ public static void DrawCircle(this Editor editor, Point2d pnt, short colorIndex, pnt + vec }; for (int i = 1; i < numEdges; i++) - { pnts.Add(pnt + vec.RotateBy(angle * i)); - } editor.DrawVectors(pnts, colorIndex, true); } @@ -1147,11 +1145,30 @@ public static void ComExportWMF(this Editor editor, string saveFile, var doc = com.GetProperty("ActiveDocument"); var wmfSet = doc.GetProperty("ActiveSelectionSet"); - // TODO 20221007 cad21 先net选择,再进行,此处再选择一次? - // cad21 调试期间无法选择性粘贴? + // TODO 20221007 导出wmf的bug + // cad21 先net选择,再进行,此处再选择一次? + // cad21 调试期间无法选择性粘贴? var exp = doc.Invoke("Export", saveFile, "wmf", wmfSet); // JPGOUT,PNGOUT if (wmfSetDel) wmfSet.Invoke("Delete"); } #endregion + + /// + /// 可以发送透明命令的状态
    + /// 福萝卜:这个应该是修正ribbon里输入丢焦点的问题,低版本可以不要 + ///
    + /// + /// + public static bool IsQuiescentForTransparentCommand(this Editor ed) + { +#if NET35 + //if (ed.IsQuiescent) + //{ + //} + return true; +#else + return ed.IsQuiescentForTransparentCommand; +#endif + } } \ No newline at end of file diff --git a/src/IFoxCAD.Cad.Shared/Runtime/AutoRegAssem.cs b/src/IFoxCAD.Cad.Shared/Runtime/AutoRegAssem.cs index e25ccdb..e8bb720 100644 --- a/src/IFoxCAD.Cad.Shared/Runtime/AutoRegAssem.cs +++ b/src/IFoxCAD.Cad.Shared/Runtime/AutoRegAssem.cs @@ -14,8 +14,8 @@ public abstract class AutoRegAssem : IExtensionApplication { #region 字段 - readonly AutoReflection? _autoRef; readonly AssemInfo _info; + readonly AutoReflection? _autoRef; #endregion #region 静态方法 @@ -23,12 +23,10 @@ public abstract class AutoRegAssem : IExtensionApplication /// 程序集的路径 ///
    public static FileInfo Location => new(Assembly.GetCallingAssembly().Location); - /// /// 程序集的目录 /// public static DirectoryInfo CurrDirectory => Location.Directory; - /// /// 获取程序集的目录 /// @@ -38,7 +36,6 @@ public static DirectoryInfo GetDirectory(Assembly? assem) { if (assem is null) throw new(nameof(assem)); - return new FileInfo(assem.Location).Directory; } #endregion @@ -168,7 +165,9 @@ public void RegApp() } // 这里的是不会自动执行的 - public void Initialize() { } + public void Initialize() + { + } public void Terminate() { } diff --git a/src/IFoxCAD.Cad.Shared/Runtime/Env.cs b/src/IFoxCAD.Cad.Shared/Runtime/Env.cs index 6de5354..0a4697d 100644 --- a/src/IFoxCAD.Cad.Shared/Runtime/Env.cs +++ b/src/IFoxCAD.Cad.Shared/Runtime/Env.cs @@ -495,7 +495,8 @@ public static void SetVar(string? varName, object? value, bool echo = true) static extern int AcedSetEnv(string? envName, StringBuilder NewValue); #endif -#if zcad // TODO: 中望没有测试,此处仅为不报错;本工程所有含有"中望"均存在问题 + // TODO: 中望没有测试,此处仅为不报错;本工程所有含有"中望"均存在问题 +#if zcad [System.Security.SuppressUnmanagedCodeSecurity] [DllImport("zced.dll", CharSet = CharSet.Auto, CallingConvention = CallingConvention.Cdecl, EntryPoint = "zcedGetEnv")] static extern int AcedGetEnv(string? envName, StringBuilder ReturnValue); diff --git a/src/IFoxCAD.Cad.Shared/Runtime/FileOpenMode.cs b/src/IFoxCAD.Cad.Shared/Runtime/FileOpenMode.cs index 0939073..bd5eefb 100644 --- a/src/IFoxCAD.Cad.Shared/Runtime/FileOpenMode.cs +++ b/src/IFoxCAD.Cad.Shared/Runtime/FileOpenMode.cs @@ -1,3 +1,5 @@ +using IFoxCAD.Cad; + #if ac2008 // NET35 namespace Autodesk.AutoCAD.DatabaseServices { @@ -51,5 +53,39 @@ public static FileShare GetFileShare(FileOpenMode fileOpenMode) } } } +#endif + +#if NET35 +namespace Autodesk.AutoCAD.Internal +{ + public class Utils + { + [DllImport("user32.dll")] + static extern IntPtr SetFocus(IntPtr hWnd); + public static void SetFocusToDwgView() + { + // TODO 20221027 等待验证 + IntPtr window; + if (Acap.DocumentManager.Count == 0) + { + window = Acap.MainWindow.Handle; + } + else + { + // 它们是层级关系 + // Main + // -->MDI(大小被 DwgView 局限) + // ---->docW(比MDI大) + // -------->msctls_statusbar32 + // -------->DwgView + var docW = Acap.DocumentManager.MdiActiveDocument.Window.Handle; + var msctls_statusbar32 = WindowsAPI.GetTopWindow(docW); + window = WindowsAPI.GetWindow(msctls_statusbar32, 2U); + } + if (window != IntPtr.Zero) + SetFocus(window); + } + } +} #endif \ No newline at end of file diff --git a/src/IFoxCAD.Cad.Shared/Runtime/IAutoGo.cs b/src/IFoxCAD.Cad.Shared/Runtime/IAutoGo.cs index b9e5624..f9e018d 100644 --- a/src/IFoxCAD.Cad.Shared/Runtime/IAutoGo.cs +++ b/src/IFoxCAD.Cad.Shared/Runtime/IAutoGo.cs @@ -58,11 +58,18 @@ class RunClass { public Sequence Sequence { get; } readonly MethodInfo _methodInfo; - - public RunClass(MethodInfo method, Sequence sequence) + object? _instance; + /// + /// 执行此方法 + /// + /// + /// + /// 已经创建的对象 + public RunClass(MethodInfo method, Sequence sequence, object? instance = null) { _methodInfo = method; Sequence = sequence; + _instance = instance; } /// @@ -70,7 +77,7 @@ public RunClass(MethodInfo method, Sequence sequence) /// public void Run() { - _methodInfo.Invoke(); + _methodInfo.Invoke(ref _instance); } } @@ -114,7 +121,9 @@ public void Initialize() GetAttributeFunctions(_InitializeList, _TerminateList); if ((_autoRegConfig & AutoRegConfig.ReflectionInterface) == AutoRegConfig.ReflectionInterface) - GetInterfaceFunctions(_InitializeList, nameof(Initialize)); + { + GetInterfaceFunctions(_InitializeList, nameof(Initialize), _TerminateList, nameof(Terminate)); + } if (_InitializeList.Count > 0) { @@ -134,8 +143,8 @@ public void Terminate() { try { - if ((_autoRegConfig & AutoRegConfig.ReflectionInterface) == AutoRegConfig.ReflectionInterface) - GetInterfaceFunctions(_TerminateList, nameof(Terminate)); + //if ((_autoRegConfig & AutoRegConfig.ReflectionInterface) == AutoRegConfig.ReflectionInterface) + // GetInterfaceFunctions(_TerminateList, nameof(Terminate)); if (_TerminateList.Count > 0) { @@ -221,7 +230,8 @@ public static void AppDomainGetTypes(Action action, string? dllNameWithout /// 储存要运行的方法 /// 查找方法名 /// - void GetInterfaceFunctions(List runClassList, string methodName) + void GetInterfaceFunctions(List initializes, string initializeName, + List terminates, string terminateName) { AppDomainGetTypes(type => { // 接口的静态类屏蔽,继承接口无法使用静态类,因此跳过 @@ -236,6 +246,8 @@ void GetInterfaceFunctions(List runClassList, string methodName) Sequence? sequence = null; MethodInfo? initialize = null; + MethodInfo? terminate = null; + object? instance = null; var mets = type.GetMethods(); for (int jj = 0; jj < mets.Length; jj++) @@ -248,22 +260,35 @@ void GetInterfaceFunctions(List runClassList, string methodName) if (method.Name == nameof(IFoxAutoGo.SequenceId)) { - var obj = method.Invoke(); + // 避免触发两次构造函数,所以这里需要ref构造的对象出来 + var obj = method.Invoke(ref instance); if (obj is not null) sequence = (Sequence)obj; continue; } - if (method.Name == methodName) + if (method.Name == initializeName) + { initialize = method; - if (initialize is not null && sequence is not null) + continue; + } + if (method.Name == terminateName) + { + terminate = method; + continue; + } + if (sequence is not null && initialize is not null && terminate is not null) break; } - if (initialize is null) - continue; - + // 避免在terminate释放的时候去再次构造,所以需要一次性收集 + // 若是释放的时候去再次构造: + // 0x01 initialize构造的字段拥有的资源就处于系统释放了,这是不合理的 + // 0x02 同时也发生了,为了释放而去构造这个操作 var seq = sequence is null ? Sequence.Last : sequence.Value; - runClassList.Add(new RunClass(initialize, seq)); + if (initialize is not null) + initializes.Add(new(initialize, seq, instance)); + if (terminate is not null) + terminates.Add(new(terminate, seq, instance)); break; } }, _dllName); @@ -272,7 +297,8 @@ void GetInterfaceFunctions(List runClassList, string methodName) /// /// 收集特性下的函数 /// - void GetAttributeFunctions(List initialize, List terminate) + void GetAttributeFunctions(List initializes, + List terminates) { AppDomainGetTypes(type => { if (!type.IsClass) @@ -298,9 +324,9 @@ void GetAttributeFunctions(List initialize, List terminate) { var runc = new RunClass(method, jjAtt.SequenceId); if (jjAtt.IsInitialize) - initialize.Add(runc); + initializes.Add(runc); else - terminate.Add(runc); + terminates.Add(runc); break; } } diff --git a/src/IFoxCAD.Cad.Shared/Runtime/MethodInfoHelper.cs b/src/IFoxCAD.Cad.Shared/Runtime/MethodInfoHelper.cs index 5f8e2bc..1111037 100644 --- a/src/IFoxCAD.Cad.Shared/Runtime/MethodInfoHelper.cs +++ b/src/IFoxCAD.Cad.Shared/Runtime/MethodInfoHelper.cs @@ -2,14 +2,16 @@ internal static class MethodInfoHelper { +#if cache private static readonly Dictionary methodDic = new(); +#endif /// /// 执行函数 /// /// 函数 /// 已经外部创建的对象,为空则此处创建 - public static object? Invoke(this MethodInfo methodInfo, object? instance = null) + public static object? Invoke(this MethodInfo methodInfo, ref object? instance) { if (methodInfo == null) throw new ArgumentNullException(nameof(methodInfo)); @@ -28,22 +30,31 @@ internal static class MethodInfoHelper } else { +#if cache // 原命令的函数指针进入此处 // object instance; if (methodDic.ContainsKey(methodInfo)) instance = methodDic[methodInfo]; - +#endif if (instance == null) { var reftype = methodInfo.ReflectedType; - if (reftype == null) return null; + if (reftype == null) + return null; + var fullName = reftype.FullName; // 命名空间+类 - if (fullName == null) return null; - var type = reftype.Assembly.GetType(fullName);// 通过程序集反射创建类+ - if (type == null) return null; - instance = Activator.CreateInstance(type); + if (fullName == null) + return null; + + var type = reftype.Assembly.GetType(fullName); + if (type == null) + return null; + + instance = Activator.CreateInstance(type);// 构造类 +#if cache if (!type.IsAbstract)// 无法创建抽象类成员 methodDic.Add(methodInfo, instance); +#endif } if (instance != null) result = methodInfo.Invoke(instance, null); // 非静态,调用实例化方法 diff --git a/src/IFoxCAD.Cad/IFoxCAD.Gcad/GlobalUsings.cs b/src/IFoxCAD.Cad/IFoxCAD.Gcad/GlobalUsings.cs index 136bc7a..08de9aa 100644 --- a/src/IFoxCAD.Cad/IFoxCAD.Gcad/GlobalUsings.cs +++ b/src/IFoxCAD.Cad/IFoxCAD.Gcad/GlobalUsings.cs @@ -30,6 +30,8 @@ global using GrxCAD.DatabaseServices.Filters; global using GrxCAD; +global using GrxCAD.Internal; + // jig命名空间会引起Viewport/Polyline等等重义,最好逐个引入 using Autodesk.AutoCAD.GraphicsInterface global using GrxCAD.GraphicsInterface; global using WorldDraw = GrxCAD.GraphicsInterface.WorldDraw; diff --git a/tests/TestShared/CmdINI.cs b/tests/TestShared/CmdINI.cs index 8dc09c2..69cb5ca 100644 --- a/tests/TestShared/CmdINI.cs +++ b/tests/TestShared/CmdINI.cs @@ -1,3 +1,5 @@ +using System.Diagnostics; + namespace Test; /// @@ -69,10 +71,12 @@ public void IFoxRemoveReg() */ public class Cmd_IFoxInitialize { + int TestInt = 0; + [IFoxInitialize] public void Initialize() { - Env.Printl($"开始自动执行,可以分开多个类和多个函数:{nameof(Cmd_IFoxInitialize)}.{nameof(Initialize)}"); + Env.Printl($"开始自动执行,可以分开多个类和多个函数:{nameof(Cmd_IFoxInitialize)}.{nameof(Initialize)}+{TestInt}"); } [IFoxInitialize] @@ -89,6 +93,8 @@ public void Initialize2() // // 注意此时编辑器已经回收,所以此句引发错误 // // 您可以写一些其他的释放动作,例如资源回收之类的 // Env.Printl($"\n 结束自动执行 Terminate \r\n"); + // // 改用 + // Debug.WriteLine($"\n 结束自动执行 Terminate \r\n"); // } // catch (System.Exception e) // { @@ -103,14 +109,16 @@ public static void StaticInitialize() } } + /* * 自动执行:接口 */ public class Cmd_IFoxInitializeInterface : IFoxAutoGo { - public void Initialize() + int TestInt = 0; + public Cmd_IFoxInitializeInterface() { - Env.Printl($"开始自动执行,接口调用:{nameof(Cmd_IFoxInitializeInterface)}.{nameof(Initialize)}"); + Env.Printl($"开始自动执行,{nameof(IFoxAutoGo)}接口调用:{nameof(Cmd_IFoxInitializeInterface)}::{TestInt}"); } public Sequence SequenceId() @@ -118,8 +126,14 @@ public Sequence SequenceId() return Sequence.Last; } + public void Initialize() + { + Env.Printl($"开始自动执行,{nameof(IFoxAutoGo)}接口调用:{nameof(Initialize)}::{TestInt}"); + } + public void Terminate() { + Debug.WriteLine($"开始自动执行,{nameof(IFoxAutoGo)}接口调用:{nameof(Terminate)}::{TestInt}"); // try // { // // 注意此时编辑器已经回收,所以此句没用,并引发错误 diff --git a/tests/TestShared/TestAddEntity.cs b/tests/TestShared/TestAddEntity.cs index 002e184..87884fe 100644 --- a/tests/TestShared/TestAddEntity.cs +++ b/tests/TestShared/TestAddEntity.cs @@ -349,6 +349,7 @@ public void Test_Rec() } }); +#pragma warning disable CS0219 // 变量已被赋值,但从未使用过它的值 Tools.TestTimes(1000000, "三次点乘", () => { var result = false; if (Math.Abs(p12.DotProduct(p23)) < 1e8 && @@ -364,6 +365,7 @@ public void Test_Rec() p34.IsParallelTo(p41)) result = true; }); +#pragma warning restore CS0219 // 变量已被赋值,但从未使用过它的值 } public Database Getdb() -- Gitee From dcda083fd7cd1c9c37ee9eec0f72a8d9573a257e Mon Sep 17 00:00:00 2001 From: liuqihong <540762622@qq.com> Date: Thu, 27 Oct 2022 09:54:06 +0800 Subject: [PATCH 580/675] =?UTF-8?q?=E5=8A=A0=E5=85=A5=E4=B8=A4=E4=B8=AA?= =?UTF-8?q?=E8=BE=93=E5=85=A5=E6=B3=95=E5=88=87=E6=8D=A2=E4=BE=8B=E5=AD=90?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Runtime/FileOpenMode.cs | 1 - tests/TestAcad08/TestAcad08.csproj | 17 + .../InputBinding/Inputting.cs" | 692 ++++++++++++++++++ .../InputBinding/InputtingForm.Designer.cs" | 302 ++++++++ .../InputBinding/InputtingForm.cs" | 178 +++++ .../InputBinding/InputtingForm.resx" | 120 +++ .../Cmd.cs" | 30 + .../DPI.cs" | 8 + .../DocReactor.cs" | 62 ++ .../IMEControl.cs" | 275 +++++++ .../ResourceDictionary/ButtonDictionary.xaml" | 374 ++++++++++ .../ComboboxDictionary.xaml" | 345 +++++++++ .../ComboboxitemDictionary.xaml" | 108 +++ .../ScrollviewDictionary.xaml" | 150 ++++ .../TextboxDictionary.xaml" | 28 + .../Settings.cs" | 148 ++++ .../ShowWPFWindowCentered.cs" | 31 + .../Window/SettingsWindow.xaml" | 52 ++ .../Window/SettingsWindow.xaml.cs" | 40 + .../WindowsAPI.cs" | 96 +++ 20 files changed, 3056 insertions(+), 1 deletion(-) create mode 100644 "tests/TestAcad09plus/\350\276\223\345\205\245\346\263\225\350\207\252\345\212\250\345\210\207\346\215\242/InputBinding/Inputting.cs" create mode 100644 "tests/TestAcad09plus/\350\276\223\345\205\245\346\263\225\350\207\252\345\212\250\345\210\207\346\215\242/InputBinding/InputtingForm.Designer.cs" create mode 100644 "tests/TestAcad09plus/\350\276\223\345\205\245\346\263\225\350\207\252\345\212\250\345\210\207\346\215\242/InputBinding/InputtingForm.cs" create mode 100644 "tests/TestAcad09plus/\350\276\223\345\205\245\346\263\225\350\207\252\345\212\250\345\210\207\346\215\242/InputBinding/InputtingForm.resx" create mode 100644 "tests/TestAcad09plus/\350\276\223\345\205\245\346\263\225\350\207\252\345\212\250\345\210\207\346\215\242/\350\265\253\346\200\235\350\276\223\345\205\245\346\263\225/Cmd.cs" create mode 100644 "tests/TestAcad09plus/\350\276\223\345\205\245\346\263\225\350\207\252\345\212\250\345\210\207\346\215\242/\350\265\253\346\200\235\350\276\223\345\205\245\346\263\225/DPI.cs" create mode 100644 "tests/TestAcad09plus/\350\276\223\345\205\245\346\263\225\350\207\252\345\212\250\345\210\207\346\215\242/\350\265\253\346\200\235\350\276\223\345\205\245\346\263\225/DocReactor.cs" create mode 100644 "tests/TestAcad09plus/\350\276\223\345\205\245\346\263\225\350\207\252\345\212\250\345\210\207\346\215\242/\350\265\253\346\200\235\350\276\223\345\205\245\346\263\225/IMEControl.cs" create mode 100644 "tests/TestAcad09plus/\350\276\223\345\205\245\346\263\225\350\207\252\345\212\250\345\210\207\346\215\242/\350\265\253\346\200\235\350\276\223\345\205\245\346\263\225/ResourceDictionary/ButtonDictionary.xaml" create mode 100644 "tests/TestAcad09plus/\350\276\223\345\205\245\346\263\225\350\207\252\345\212\250\345\210\207\346\215\242/\350\265\253\346\200\235\350\276\223\345\205\245\346\263\225/ResourceDictionary/ComboboxDictionary.xaml" create mode 100644 "tests/TestAcad09plus/\350\276\223\345\205\245\346\263\225\350\207\252\345\212\250\345\210\207\346\215\242/\350\265\253\346\200\235\350\276\223\345\205\245\346\263\225/ResourceDictionary/ComboboxitemDictionary.xaml" create mode 100644 "tests/TestAcad09plus/\350\276\223\345\205\245\346\263\225\350\207\252\345\212\250\345\210\207\346\215\242/\350\265\253\346\200\235\350\276\223\345\205\245\346\263\225/ResourceDictionary/ScrollviewDictionary.xaml" create mode 100644 "tests/TestAcad09plus/\350\276\223\345\205\245\346\263\225\350\207\252\345\212\250\345\210\207\346\215\242/\350\265\253\346\200\235\350\276\223\345\205\245\346\263\225/ResourceDictionary/TextboxDictionary.xaml" create mode 100644 "tests/TestAcad09plus/\350\276\223\345\205\245\346\263\225\350\207\252\345\212\250\345\210\207\346\215\242/\350\265\253\346\200\235\350\276\223\345\205\245\346\263\225/Settings.cs" create mode 100644 "tests/TestAcad09plus/\350\276\223\345\205\245\346\263\225\350\207\252\345\212\250\345\210\207\346\215\242/\350\265\253\346\200\235\350\276\223\345\205\245\346\263\225/ShowWPFWindowCentered.cs" create mode 100644 "tests/TestAcad09plus/\350\276\223\345\205\245\346\263\225\350\207\252\345\212\250\345\210\207\346\215\242/\350\265\253\346\200\235\350\276\223\345\205\245\346\263\225/Window/SettingsWindow.xaml" create mode 100644 "tests/TestAcad09plus/\350\276\223\345\205\245\346\263\225\350\207\252\345\212\250\345\210\207\346\215\242/\350\265\253\346\200\235\350\276\223\345\205\245\346\263\225/Window/SettingsWindow.xaml.cs" create mode 100644 "tests/TestAcad09plus/\350\276\223\345\205\245\346\263\225\350\207\252\345\212\250\345\210\207\346\215\242/\350\265\253\346\200\235\350\276\223\345\205\245\346\263\225/WindowsAPI.cs" diff --git a/src/IFoxCAD.Cad.Shared/Runtime/FileOpenMode.cs b/src/IFoxCAD.Cad.Shared/Runtime/FileOpenMode.cs index bd5eefb..992ba2d 100644 --- a/src/IFoxCAD.Cad.Shared/Runtime/FileOpenMode.cs +++ b/src/IFoxCAD.Cad.Shared/Runtime/FileOpenMode.cs @@ -65,7 +65,6 @@ public class Utils public static void SetFocusToDwgView() { - // TODO 20221027 等待验证 IntPtr window; if (Acap.DocumentManager.Count == 0) { diff --git a/tests/TestAcad08/TestAcad08.csproj b/tests/TestAcad08/TestAcad08.csproj index 3395882..f8150a0 100644 --- a/tests/TestAcad08/TestAcad08.csproj +++ b/tests/TestAcad08/TestAcad08.csproj @@ -29,4 +29,21 @@ + + + + + + + + + MSBuild:Compile + + + + + + MSBuild:Compile + + diff --git "a/tests/TestAcad09plus/\350\276\223\345\205\245\346\263\225\350\207\252\345\212\250\345\210\207\346\215\242/InputBinding/Inputting.cs" "b/tests/TestAcad09plus/\350\276\223\345\205\245\346\263\225\350\207\252\345\212\250\345\210\207\346\215\242/InputBinding/Inputting.cs" new file mode 100644 index 0000000..8301efa --- /dev/null +++ "b/tests/TestAcad09plus/\350\276\223\345\205\245\346\263\225\350\207\252\345\212\250\345\210\207\346\215\242/InputBinding/Inputting.cs" @@ -0,0 +1,692 @@ +#pragma warning disable 0169 + +/* + * 福萝卜说他的是用键盘钩子制作的 + * 此处不是键盘钩子,并且08还需要把文档栏工程的子类化拉取过来才可以使用 + */ + +namespace InputBinding; + +using Autodesk.AutoCAD.Internal; +using System.Threading; +using System.Windows.Forms; +using static WindowsAPI; + +public class Inputting +{ + static AppMessageFilter? _appMsgFilter = null; + static bool _shiftYN1 = false; + static bool _shiftYN2 = false; + static readonly ManualResetEvent _mre1 = new(false); + static readonly ManualResetEvent _mre2 = new(false); + static readonly ManualResetEvent _mre3 = new(false); + static readonly ManualResetEvent _mre4 = new(false); + static readonly ManualResetEvent _mre5 = new(false); + static int _iMode1 = -1; + static int _iMode2 = -1; + static int _iMode3 = -1; + /// + /// 是否卸载输入法切换 + /// + static bool _unloadYN = false; + /// + /// 当前输入法的状态:true中文,false英文 + /// + static bool _status = false; + static long _time; + + static int _hanCount1; + static int _hanCount1Add; + + /// + /// 具有焦点的窗口的句柄 + /// + static IntPtr _hwndFocus; + /// + /// 显示插入记号的窗口的句柄 + /// + static IntPtr _hwndCaret; + /// + /// 捕获鼠标的窗口的句柄 + /// + static IntPtr _hwndCapture; + /// + /// 线程内活动窗口的句柄 + /// + static IntPtr _hwndActive; + + [CommandMethod(nameof(ModalDialog))] + public void ModalDialog() + { + Acap.ShowModalDialog(new InputtingForm()); + } + + #region NewThread1 + [IFoxInitialize] + public void InputBindingInitialize() + { + InputHelper.LoadConfig(); + Acap.PreTranslateMessage += Acap_PreTranslateMessage; + } + + /// + /// 载入和卸载输入法 + /// + [CommandMethod(nameof(IFoxInput))] + public static void IFoxInput() + { + if (!_unloadYN) + { + _unloadYN = true; + Acap.PreTranslateMessage += Acap_PreTranslateMessage; + Env.Printl("已卸载输入法自动切换"); + } + else + { + _unloadYN = false; + Acap.PreTranslateMessage += Acap_PreTranslateMessage; + Env.Printl("已加载输入法自动切换"); + } + } + + public static void Acap_PreTranslateMessage(object sender, PreTranslateMessageEventArgs e) + { + if (!_unloadYN) + { + _appMsgFilter = new(); + Application.AddMessageFilter(_appMsgFilter); + } + else + { + Application.RemoveMessageFilter(_appMsgFilter); + } + Acap.PreTranslateMessage -= Acap_PreTranslateMessage; + } + + public class AppMessageFilter : IMessageFilter + { + bool AcadVersion19() + { + return Acap.Version.Major >= 19 + && InputVar.CmdActive + && IsTrigger() + && _hwndCapture == IntPtr.Zero + && GetKeyboardLayout(GetCurrentThreadId()) < 0; + } + + public bool PreFilterMessage(ref Message m) + { + if (m.Msg == 515) + { + new Thread(new ThreadStart(NewThread1)).Start(); + _mre1.Set(); + return false; + } + + if (m.Msg == 512 && _shiftYN1 && AcadVersion19()) + { + _hanCount1Add = GetWindowTextLength(_hwndCaret); + StringBuilder text = new(2 * _hanCount1Add); + GetWindowText(_hwndFocus, text, text.Capacity); + if (!text.ToString().Contains(InputVar.StatusBar)) + { + Utils.SetFocusToDwgView(); + _shiftYN1 = false; + } + return false; + } + + if (m.Msg != 256) + return false; + + if (m.WParam != (IntPtr)229) + { + if (!_shiftYN1 && AcadVersion19()) + { + _hanCount1Add = GetWindowTextLength(_hwndCaret); + StringBuilder text = new(2 * _hanCount1Add); + GetWindowText(_hwndFocus, text, text.Capacity); + if (!text.ToString().Contains(InputVar.StatusBar)) + { + Utils.SetFocusToDwgView(); + _shiftYN1 = true; + } + } + return false; + } + + if (_shiftYN2) + { + _shiftYN2 = false; + return false; + } + + IntPtr window = IntPtr.Zero; + + var virtualKey = ImmGetVirtualKey(m.HWnd); + var vk = (int)virtualKey; + if (vk != 16 && vk != 17 && vk != 18 && vk != 91 && InputVar.CmdActive) + { + bool containCmdStr = false; + if (_hwndFocus == _hwndCaret) + { + Env.Printl(InputVar.CmdStr); + StringBuilder text = new(2 * GetWindowTextLength(_hwndCaret)); + GetWindowText(_hwndCaret, text, text.Capacity); + if (text.ToString().Contains(InputVar.CmdStr)) + containCmdStr = true; + } + + if (containCmdStr || IsTrigger()) + { + _hanCount1Add = GetWindowTextLength(_hwndCaret); + StringBuilder text = new(2 * _hanCount1Add); + GetWindowText(_hwndFocus, text, text.Capacity); + + if (text.ToString().Contains(InputVar.StatusBar)) + return false; + + ImmGetConversionStatus(ImmGetContext(_hwndFocus), out _iMode3, out _); + if (_hwndCapture == IntPtr.Zero) + Utils.SetFocusToDwgView(); + + PressKey(); + if (_hwndFocus == _hwndCaret) + { + if (containCmdStr) + window = _hwndCaret; + else if (!InputVar.DY) + { + var doc = Acap.DocumentManager.MdiActiveDocument; + window = GetWindow(GetTopWindow(doc.Window.Handle), 2U); + } + } + else if (!InputVar.DY || _hwndCapture != IntPtr.Zero) + window = m.HWnd; + } + } + else if (InputVar.Windows10) + window = m.HWnd; + + if (window != IntPtr.Zero) + { + PostMessage(window, m.Msg, virtualKey, m.LParam); + return true; + } + return false; + } + } + + public static void NewThread1() + { + _mre1.WaitOne(); + Thread.Sleep(200); + if (InputVar.DK) + { + InputVar.DK = false; + return; + } + + if ((!InputVar.CmdActive || IsTrigger()) + && (InputVar.CmdActive || !InputVar.TextEditor || (_hwndCaret == IntPtr.Zero)) + && (InputVar.CmdActive || !InputVar.TextEditor || _hwndActive == _hwndFocus)) + return; + + _hanCount1Add = GetWindowTextLength(_hwndFocus); + StringBuilder text = new(2 * _hanCount1Add); + GetWindowText(_hwndFocus, text, text.Capacity); + + var textStr = text.ToString(); + if (textStr != string.Empty && !textStr.Contains(InputVar.StatusBar)) + { + var context = ImmGetContext(_hwndFocus); + ImmGetConversionStatus(context, out _iMode1, out _); + _status = ImmGetOpenStatus(context); + + _hanCount1 = GetHanNum(textStr); + var length = _hanCount1Add - 2 * _hanCount1; + + if (InputVar.Windows10 || InputVar.CK || InputVar.CS || InputVar.AS) + { + if (!InputVar.CMD) + { + if ((_status && length > _hanCount1) || + (!_status && length <= _hanCount1)) + { + PressKey(); + InputVar.CMD = false; + } + } + else + InputVar.CMD = false; + } + else if (InputVar.SF || InputVar.CT) + { + if (_iMode3 != -1) + { + if ((length <= _hanCount1 && _iMode3 == _iMode1) || + (length > _hanCount1 && _iMode3 > _iMode1)) + InputVar.CMD = false; + } + else if (!InputVar.CMD) + { + PressKey(); + new Thread(new ThreadStart(NewThread100)).Start(); + _mre2.Set(); + } + } + } + } + #endregion + + + /// + /// lisp Input判断文字中中英文的数量决定切换中英文 + /// + /// 文字 + [LispFunction(nameof(InputLispDoubleClick))] + public static ResultBuffer InputLispDoubleClick(ResultBuffer rb) + { + if (rb is null || _unloadYN) + return null!; + + InputVar.DK = true; + string str = rb.AsArray()[0].ToString(); + + var hanCount = GetHanNum(str); + var hanCountAdd = str.Length + hanCount; + var length = hanCountAdd - 2 * hanCount; + + IsTrigger(); + var context = ImmGetContext(_hwndFocus); + ImmGetConversionStatus(context, out _iMode1, out _); + _status = ImmGetOpenStatus(context); + + new Thread(() => { + _mre3.WaitOne(); + Thread.Sleep(50); + if (InputVar.CMD) + { + InputVar.CMD = false; + return; + } + + Thread.Sleep(50); + if (InputVar.Windows10 || InputVar.CK || InputVar.CS || InputVar.AS) + { + if (!(_status && length > hanCount)) + if (_status || length > hanCount) + return; + PressKey(); + } + else + { + if (!InputVar.SF + && !InputVar.CT || length <= hanCount + && _iMode3 == _iMode1 + && _iMode3 != -1 || length > hanCount + && _iMode3 > _iMode1 + && _iMode3 != -1) + return; + + PressKey(); + _hanCount1Add = hanCountAdd; + _hanCount1 = hanCount; + new Thread(new ThreadStart(NewThread100)).Start(); + _mre2.Set(); + } + }).Start(); + + _mre3.Set(); + return null!; + } + + /// + /// lisp Input命令开始 + /// + /// + /// + [LispFunction(nameof(InputLispStart))] + public static ResultBuffer InputLispStart(ResultBuffer rb) + { + if (rb is null || _unloadYN) + return null!; + + string str = rb.AsArray()[0].ToString(); + if (str != "True") + return null!; + + InputVar.CMD = true; + var hanCount = GetHanNum(str); + var hanCountAdd = str.Length + hanCount; + var length = hanCountAdd - 2 * hanCount; + + new Thread(() => { + _mre4.WaitOne(); + Thread.Sleep(100); + + IsTrigger(); + var context = ImmGetContext(_hwndFocus); + ImmGetConversionStatus(context, out _iMode1, out _); + _status = ImmGetOpenStatus(context); + + if (InputVar.Windows10 || InputVar.CK || InputVar.CS || InputVar.AS) + { + if (_status && length > hanCount) + { + Thread.Sleep(100); + PressKey(); + Thread.Sleep(500); + InputVar.CMD = false; + } + else if (!_status && length <= hanCount) + { + Thread.Sleep(100); + PressKey(); + Thread.Sleep(500); + InputVar.CMD = false; + } + else + { + Thread.Sleep(500); + InputVar.CMD = false; + } + } + else + { + if (!InputVar.SF && !InputVar.CT) + return; + if (length <= hanCount && _iMode3 == _iMode1 && _iMode3 != -1) + { + Thread.Sleep(500); + InputVar.CMD = false; + } + else if (length > hanCount && _iMode3 > _iMode1 && _iMode3 != -1) + { + Thread.Sleep(500); + InputVar.CMD = false; + } + else + { + Thread.Sleep(100); + PressKey(); + _hanCount1Add = hanCountAdd; + _hanCount1 = hanCount; + new Thread(new ThreadStart(NewThread100)).Start(); + _mre2.Set(); + } + } + }).Start(); + + _mre4.Set(); + return null!; + } + + /// + /// lisp Input命令结束 + /// + /// + /// + [LispFunction(nameof(InputLispEnd))] + public static ResultBuffer InputLispEnd(ResultBuffer rb) + { + if (rb is null || _unloadYN) + return null!; + + string str = rb.AsArray()[0].ToString(); + if (str != "True") + { + var hanCount = GetHanNum(str); + var hanCountAdd = str.Length + hanCount; + var length = hanCountAdd - 2 * hanCount; + + new Thread(() => { + _mre5.WaitOne(); + Thread.Sleep(100); + + IsTrigger(); + var context = ImmGetContext(_hwndFocus); + ImmGetConversionStatus(context, out _iMode1, out _); + _status = ImmGetOpenStatus(context); + + if (InputVar.Windows10 || InputVar.CK || InputVar.CS || InputVar.AS) + { + if (_status && length > hanCount) + PressKey(); + if (_status || length > hanCount) + return; + PressKey(); + } + else + { + if (!InputVar.SF + && !InputVar.CT || length <= hanCount + && _iMode3 == _iMode1 + && _iMode3 != -1 || length > hanCount + && _iMode3 > _iMode1 + && _iMode3 != -1) + return; + + PressKey(); + _hanCount1Add = hanCountAdd; + _hanCount1 = hanCount; + new Thread(new ThreadStart(NewThread100)).Start(); + _mre2.Set(); + } + }).Start(); + + + _mre5.Set(); + } + return null!; + } + + /// + /// 处理按键事件 + /// + public static void PressKey() + { + if (InputVar.SF) + { + if (_iMode3 == -1) + Thread.Sleep(400); + keybd_event(16, 0, 0, 0); + keybd_event(16, 0, 2, 0); + } + else if (InputVar.CT) + { + if (_iMode3 == -1) + Thread.Sleep(400); + keybd_event(17, 0, 0, 0); + keybd_event(17, 0, 2, 0); + } + else if (InputVar.CK) + { + if (_iMode3 == -1) + Thread.Sleep(400); + keybd_event(17, 0, 0, 0); + keybd_event(32, 0, 0, 0); + keybd_event(32, 0, 2, 0); + keybd_event(17, 0, 2, 0); + if (GetKeyState(20) != 1) + return; + keybd_event(20, 0, 0, 0); + keybd_event(20, 0, 2, 0); + } + else if (InputVar.CS) + { + if (_iMode3 == -1) + Thread.Sleep(400); + keybd_event(16, 0, 0, 0); + keybd_event(17, 0, 0, 0); + keybd_event(17, 0, 2, 0); + keybd_event(16, 0, 2, 0); + if (GetKeyState(20) != 1) + return; + keybd_event(20, 0, 0, 0); + keybd_event(20, 0, 2, 0); + } + else if (InputVar.AS) + { + if (_iMode3 == -1) + Thread.Sleep(400); + keybd_event(91, 0, 0, 0); + keybd_event(32, 0, 0, 0); + keybd_event(32, 0, 2, 0); + keybd_event(91, 0, 2, 0); + if (GetKeyState(20) != 1) + return; + keybd_event(20, 0, 0, 0); + keybd_event(20, 0, 2, 0); + } + else + { + var t1 = DateTime.Now.Ticks / 10000L; + _shiftYN2 = Math.Abs(_time - t1) < 80L; + _time = t1; + } + } + + public static void NewThread100() + { + _mre2.WaitOne(); + Thread.Sleep(100); + + IsTrigger(); + var context = ImmGetContext(_hwndFocus); + ImmGetConversionStatus(context, out _iMode2, out _); + _status = ImmGetOpenStatus(context); + + var length = _hanCount1Add - 2 * _hanCount1; + + if (length <= _hanCount1 + && _iMode1 > _iMode2 + && _iMode1 != _iMode3) + PressKey(); + + if (length > _hanCount1 + && _iMode1 < _iMode2 + && _iMode1 != _iMode3) + PressKey(); + + if (_iMode1 > _iMode2 && _iMode1 > _iMode3) + _iMode3 = _iMode1; + + if (_iMode1 < _iMode2 && _iMode3 < _iMode2) + _iMode3 = _iMode2; + + Thread.Sleep(300); + InputVar.DK = InputVar.CMD = false; + } + + /// + /// 窗体是否激活状态 + /// + /// + public static bool IsTrigger() + { + var pid = GetWindowThreadProcessId(GetForegroundWindow(), out uint lpdwProcessId); + var lpgui = GuiThreadInfo.Create(pid); + _hwndActive = lpgui.hwndActive; + _hwndFocus = lpgui.hwndFocus; + _hwndCaret = lpgui.hwndCaret; + _hwndCapture = lpgui.hwndCapture; + return lpgui.flags == 0; + } + + /// + /// 判断汉字的数量 + /// + /// 文字 + /// + public static int GetHanNum(string str) + { + // TODO 20221025 此处用正则效率低下,应该改用ASCII + int num = 0; + Regex regex = new(@"^[\u4E00-\u9FA5]{0,}$"); //匹配中文字符 + + for (int index = 0; index < str.Length; ++index) + if (regex.IsMatch(str[index].ToString())) + ++num; + + return num; + } +} + +public class InputVar +{ + public static bool DY; + public static bool SF; + public static bool CT; + public static bool CK; + public static bool CS; + public static bool AS; + public static bool SL; + public static bool DK; + public static bool CMD; + + public static bool Windows10 => Environment.OSVersion.Version.CompareTo(new Version("6.2")) >= 0; + public static string StatusBar = "StatusBar"; + public static string CmdStr = "命令:"; + + //[System.Runtime.CompilerServices.MethodImpl(System.Runtime.CompilerServices.MethodImplOptions.AggressiveInlining)] + public static bool CmdActive => (short)Acap.GetSystemVariable("cmdactive") == 0; + public static bool TextEditor => (short)Acap.GetSystemVariable("texteditor") == 0; +} + +public class InputHelper +{ + /// + /// 写入.ini配置 + /// + [DllImport("kernel32.dll")] + public static extern long WritePrivateProfileString(string section, string key, string value, string filepath); + + /// + /// 读取.ini配置 + /// + [DllImport("kernel32.dll")] + public static extern int GetPrivateProfileString(string section, string key, string def, StringBuilder returnvalue, int buffersize, string? filepath); + + /// + /// 返回键值对的值 + /// + /// 配置名称 + /// 键值对的键 + /// + public static string GetValue(string section, string key) + { + StringBuilder result = new(); + GetPrivateProfileString(section, key, "", result, 1024, InputHelper.SectionFile); + return result.ToString(); + } + + public static string SectionFile => $"{Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location)}\\{Section}.ini"; + public const string Section = "输入法配置"; + + /// + /// 加载配置 + /// + public static void LoadConfig() + { + if (!File.Exists(Section)) + { + InputVar.SF = true; + return; + } + var str = GetValue(Section, "Shift切换"); + InputVar.SF = str == "True"; + str = GetValue(Section, "Ctrl切换"); + InputVar.CT = str == "True"; + str = GetValue(Section, "Ctrl+空格"); + InputVar.CK = str == "True"; + str = GetValue(Section, "Ctrl+Shift"); + InputVar.CS = str == "True"; + str = GetValue(Section, "Win+空格"); + InputVar.AS = str == "True"; + str = GetValue(Section, "去多余字母"); + InputVar.DY = str == "True"; + str = GetValue(Section, "增加字母"); + InputVar.SL = str == "True"; + } +} \ No newline at end of file diff --git "a/tests/TestAcad09plus/\350\276\223\345\205\245\346\263\225\350\207\252\345\212\250\345\210\207\346\215\242/InputBinding/InputtingForm.Designer.cs" "b/tests/TestAcad09plus/\350\276\223\345\205\245\346\263\225\350\207\252\345\212\250\345\210\207\346\215\242/InputBinding/InputtingForm.Designer.cs" new file mode 100644 index 0000000..f2fdfa9 --- /dev/null +++ "b/tests/TestAcad09plus/\350\276\223\345\205\245\346\263\225\350\207\252\345\212\250\345\210\207\346\215\242/InputBinding/InputtingForm.Designer.cs" @@ -0,0 +1,302 @@ +namespace InputBinding; + +partial class InputtingForm +{ + + #region Windows Form Designer generated code + + /// + ///Required method for Designer support - do not modify + ///the contents of this method with the code editor. + /// + private void InitializeComponent() + { + this.button1 = new System.Windows.Forms.Button(); + this.button2 = new System.Windows.Forms.Button(); + this.button3 = new System.Windows.Forms.Button(); + this.checkBox1 = new System.Windows.Forms.CheckBox(); + this.checkBox2 = new System.Windows.Forms.CheckBox(); + this.checkBox3 = new System.Windows.Forms.CheckBox(); + this.checkBox4 = new System.Windows.Forms.CheckBox(); + this.checkBox5 = new System.Windows.Forms.CheckBox(); + this.checkBox6 = new System.Windows.Forms.CheckBox(); + this.checkBox7 = new System.Windows.Forms.CheckBox(); + this.label1 = new System.Windows.Forms.Label(); + this.label2 = new System.Windows.Forms.Label(); + this.label3 = new System.Windows.Forms.Label(); + this.label5 = new System.Windows.Forms.Label(); + this.label4 = new System.Windows.Forms.Label(); + this.groupBox1 = new System.Windows.Forms.GroupBox(); + this.groupBox2 = new System.Windows.Forms.GroupBox(); + this.groupBox3 = new System.Windows.Forms.GroupBox(); + this.groupBox1.SuspendLayout(); + this.groupBox2.SuspendLayout(); + this.groupBox3.SuspendLayout(); + this.SuspendLayout(); + // + //button1 + // + this.button1.Font = new System.Drawing.Font("微软雅黑", 10.5F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(134))); + this.button1.Location = new System.Drawing.Point(81, 489); + this.button1.Name = "button1"; + this.button1.Size = new System.Drawing.Size(142, 32); + this.button1.TabIndex = 3; + this.button1.Text = "保存配置并退出"; + this.button1.UseVisualStyleBackColor = true; + this.button1.Click += new System.EventHandler(this.Button1_Click); + // + //button2 + // + this.button2.Font = new System.Drawing.Font("微软雅黑", 9F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(134))); + this.button2.Location = new System.Drawing.Point(15, 155); + this.button2.Name = "button2"; + this.button2.Size = new System.Drawing.Size(116, 32); + this.button2.TabIndex = 9; + this.button2.Text = "临时关闭自动切换"; + this.button2.UseVisualStyleBackColor = true; + this.button2.Click += new System.EventHandler(this.Button2_Click); + // + //button3 + // + this.button3.Font = new System.Drawing.Font("微软雅黑", 9F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(134))); + this.button3.Location = new System.Drawing.Point(137, 155); + this.button3.Name = "button3"; + this.button3.Size = new System.Drawing.Size(132, 32); + this.button3.TabIndex = 10; + this.button3.Text = "打开自动切换"; + this.button3.UseVisualStyleBackColor = true; + this.button3.Click += new System.EventHandler(this.Button3_Click); + // + //checkBox1 + // + this.checkBox1.AutoSize = true; + this.checkBox1.Checked = true; + this.checkBox1.CheckState = System.Windows.Forms.CheckState.Checked; + this.checkBox1.Font = new System.Drawing.Font("微软雅黑", 10.5F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(134))); + this.checkBox1.Location = new System.Drawing.Point(10, 54); + this.checkBox1.Name = "checkBox1"; + this.checkBox1.Size = new System.Drawing.Size(167, 24); + this.checkBox1.TabIndex = 6; + this.checkBox1.Text = "Shift切换中英文(默认)"; + this.checkBox1.UseVisualStyleBackColor = true; + this.checkBox1.CheckedChanged += new System.EventHandler(this.CheckBox1_CheckedChanged); + // + //checkBox2 + // + this.checkBox2.AutoSize = true; + this.checkBox2.Font = new System.Drawing.Font("微软雅黑", 10.5F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(134))); + this.checkBox2.Location = new System.Drawing.Point(10, 84); + this.checkBox2.Name = "checkBox2"; + this.checkBox2.Size = new System.Drawing.Size(121, 24); + this.checkBox2.TabIndex = 7; + this.checkBox2.Text = "Ctrl切换中英文"; + this.checkBox2.UseVisualStyleBackColor = true; + this.checkBox2.CheckedChanged += new System.EventHandler(this.CheckBox2_CheckedChanged); + // + //checkBox3 + // + this.checkBox3.AutoSize = true; + this.checkBox3.Enabled = false; + this.checkBox3.Font = new System.Drawing.Font("微软雅黑", 10.5F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(134))); + this.checkBox3.Location = new System.Drawing.Point(15, 25); + this.checkBox3.Name = "checkBox3"; + this.checkBox3.Size = new System.Drawing.Size(196, 24); + this.checkBox3.TabIndex = 8; + this.checkBox3.Text = "消除切换后命令行多余字母"; + this.checkBox3.UseVisualStyleBackColor = true; + this.checkBox3.CheckedChanged += new System.EventHandler(this.CheckBox3_CheckedChanged); + // + //checkBox4 + // + this.checkBox4.AutoSize = true; + this.checkBox4.Font = new System.Drawing.Font("微软雅黑", 10.5F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(134))); + this.checkBox4.Location = new System.Drawing.Point(10, 54); + this.checkBox4.Name = "checkBox4"; + this.checkBox4.Size = new System.Drawing.Size(89, 24); + this.checkBox4.TabIndex = 11; + this.checkBox4.Text = "Ctrl+空格"; + this.checkBox4.UseVisualStyleBackColor = true; + this.checkBox4.CheckedChanged += new System.EventHandler(this.CheckBox4_CheckedChanged); + // + //checkBox5 + // + this.checkBox5.AutoSize = true; + this.checkBox5.Font = new System.Drawing.Font("微软雅黑", 10.5F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(134))); + this.checkBox5.Location = new System.Drawing.Point(10, 84); + this.checkBox5.Name = "checkBox5"; + this.checkBox5.Size = new System.Drawing.Size(92, 24); + this.checkBox5.TabIndex = 12; + this.checkBox5.Text = "Ctrl+Shift"; + this.checkBox5.UseVisualStyleBackColor = true; + this.checkBox5.CheckedChanged += new System.EventHandler(this.CheckBox5_CheckedChanged); + // + //checkBox6 + // + this.checkBox6.AutoSize = true; + this.checkBox6.Font = new System.Drawing.Font("微软雅黑", 10.5F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(134))); + this.checkBox6.Location = new System.Drawing.Point(10, 114); + this.checkBox6.Name = "checkBox6"; + this.checkBox6.Size = new System.Drawing.Size(93, 24); + this.checkBox6.TabIndex = 13; + this.checkBox6.Text = "Win+空格"; + this.checkBox6.UseVisualStyleBackColor = true; + this.checkBox6.CheckedChanged += new System.EventHandler(this.CheckBox6_CheckedChanged); + // + //checkBox7 + // + this.checkBox7.AutoSize = true; + this.checkBox7.Enabled = false; + this.checkBox7.Font = new System.Drawing.Font("微软雅黑", 10.5F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(134))); + this.checkBox7.Location = new System.Drawing.Point(15, 91); + this.checkBox7.Name = "checkBox7"; + this.checkBox7.Size = new System.Drawing.Size(238, 24); + this.checkBox7.TabIndex = 18; + this.checkBox7.Text = "解决切换后需多按一次命令首字母"; + this.checkBox7.UseVisualStyleBackColor = true; + this.checkBox7.CheckedChanged += new System.EventHandler(this.CheckBox7_CheckedChanged); + // + //label1 + // + this.label1.AutoSize = true; + this.label1.Font = new System.Drawing.Font("微软雅黑", 9F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(134))); + this.label1.Location = new System.Drawing.Point(87, 524); + this.label1.Name = "label1"; + this.label1.Size = new System.Drawing.Size(142, 17); + this.label1.TabIndex = 4; + this.label1.Text = "(.ini配置文件在dll文件夹)"; + // + //label2 + // + this.label2.AutoSize = true; + this.label2.Font = new System.Drawing.Font("微软雅黑", 10.5F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(134))); + this.label2.Location = new System.Drawing.Point(6, 26); + this.label2.Name = "label2"; + this.label2.Size = new System.Drawing.Size(169, 20); + this.label2.TabIndex = 16; + this.label2.Text = "中/英文状态切换快捷键:"; + // + //label3 + // + this.label3.AutoSize = true; + this.label3.Font = new System.Drawing.Font("微软雅黑", 10.5F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(134))); + this.label3.Location = new System.Drawing.Point(6, 26); + this.label3.Name = "label3"; + this.label3.Size = new System.Drawing.Size(214, 20); + this.label3.TabIndex = 17; + this.label3.Text = "中文(CH)/英文(EN)切换快捷键:"; + // + //label5 + // + this.label5.AutoSize = true; + this.label5.Enabled = false; + this.label5.Font = new System.Drawing.Font("微软雅黑", 9F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(134))); + this.label5.Location = new System.Drawing.Point(12, 117); + this.label5.Name = "label5"; + this.label5.Size = new System.Drawing.Size(240, 34); + this.label5.TabIndex = 19; + this.label5.Text = "(部分输入法和CAD版本存在切换后\r\n需多按一次首命令首字母,勾选此项可解决)"; + // + //label4 + // + this.label4.AutoSize = true; + this.label4.Enabled = false; + this.label4.Font = new System.Drawing.Font("微软雅黑", 9F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(134))); + this.label4.Location = new System.Drawing.Point(12, 51); + this.label4.Name = "label4"; + this.label4.Size = new System.Drawing.Size(204, 34); + this.label4.TabIndex = 17; + this.label4.Text = "(部分输入法和CAD版本存在切换后\r\n命令行有多余字母,勾选此项可消除)"; + // + //groupBox1 + // + this.groupBox1.Controls.Add(this.label2); + this.groupBox1.Controls.Add(this.checkBox1); + this.groupBox1.Controls.Add(this.checkBox2); + this.groupBox1.Font = new System.Drawing.Font("微软雅黑", 10.5F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(134))); + this.groupBox1.Location = new System.Drawing.Point(12, 12); + this.groupBox1.Name = "groupBox1"; + this.groupBox1.Size = new System.Drawing.Size(275, 117); + this.groupBox1.TabIndex = 14; + this.groupBox1.TabStop = false; + this.groupBox1.Text = "Shift懒人版"; + // + //groupBox2 + // + this.groupBox2.Controls.Add(this.label3); + this.groupBox2.Controls.Add(this.checkBox4); + this.groupBox2.Controls.Add(this.checkBox6); + this.groupBox2.Controls.Add(this.checkBox5); + this.groupBox2.Font = new System.Drawing.Font("微软雅黑", 10.5F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(134))); + this.groupBox2.Location = new System.Drawing.Point(12, 135); + this.groupBox2.Name = "groupBox2"; + this.groupBox2.Size = new System.Drawing.Size(275, 149); + this.groupBox2.TabIndex = 15; + this.groupBox2.TabStop = false; + this.groupBox2.Text = "进阶版"; + // + //groupBox3 + // + this.groupBox3.Controls.Add(this.button2); + this.groupBox3.Controls.Add(this.button3); + this.groupBox3.Controls.Add(this.label4); + this.groupBox3.Controls.Add(this.label5); + this.groupBox3.Controls.Add(this.checkBox3); + this.groupBox3.Controls.Add(this.checkBox7); + this.groupBox3.Font = new System.Drawing.Font("微软雅黑", 10.5F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(134))); + this.groupBox3.Location = new System.Drawing.Point(12, 290); + this.groupBox3.Name = "groupBox3"; + this.groupBox3.Size = new System.Drawing.Size(275, 193); + this.groupBox3.TabIndex = 16; + this.groupBox3.TabStop = false; + this.groupBox3.Text = "其它"; + // + //InputtingForm + // + this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 12F); + this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; + this.ClientSize = new System.Drawing.Size(296, 550); + this.Controls.Add(this.groupBox3); + this.Controls.Add(this.groupBox2); + this.Controls.Add(this.groupBox1); + this.Controls.Add(this.button1); + this.Controls.Add(this.label1); + this.FormBorderStyle = System.Windows.Forms.FormBorderStyle.FixedDialog; + this.MaximizeBox = false; + this.MinimizeBox = false; + this.Name = "InputtingForm"; + this.StartPosition = System.Windows.Forms.FormStartPosition.CenterScreen; + this.Text = "输入法自动切换"; + this.Load += new System.EventHandler(this.Form1_Load); + this.groupBox1.ResumeLayout(false); + this.groupBox1.PerformLayout(); + this.groupBox2.ResumeLayout(false); + this.groupBox2.PerformLayout(); + this.groupBox3.ResumeLayout(false); + this.groupBox3.PerformLayout(); + this.ResumeLayout(false); + this.PerformLayout(); + + } + + #endregion + private System.Windows.Forms.Button button1; + private System.Windows.Forms.Button button2; + private System.Windows.Forms.Button button3; + + private System.Windows.Forms.CheckBox checkBox1; + private System.Windows.Forms.CheckBox checkBox2; + private System.Windows.Forms.CheckBox checkBox3; + private System.Windows.Forms.CheckBox checkBox4; + private System.Windows.Forms.CheckBox checkBox5; + private System.Windows.Forms.CheckBox checkBox6; + private System.Windows.Forms.CheckBox checkBox7; + private System.Windows.Forms.GroupBox groupBox1; + private System.Windows.Forms.GroupBox groupBox2; + private System.Windows.Forms.GroupBox groupBox3; + + private System.Windows.Forms.Label label1; + private System.Windows.Forms.Label label2; + private System.Windows.Forms.Label label3; + private System.Windows.Forms.Label label4; + private System.Windows.Forms.Label label5; +} \ No newline at end of file diff --git "a/tests/TestAcad09plus/\350\276\223\345\205\245\346\263\225\350\207\252\345\212\250\345\210\207\346\215\242/InputBinding/InputtingForm.cs" "b/tests/TestAcad09plus/\350\276\223\345\205\245\346\263\225\350\207\252\345\212\250\345\210\207\346\215\242/InputBinding/InputtingForm.cs" new file mode 100644 index 0000000..04c27d7 --- /dev/null +++ "b/tests/TestAcad09plus/\350\276\223\345\205\245\346\263\225\350\207\252\345\212\250\345\210\207\346\215\242/InputBinding/InputtingForm.cs" @@ -0,0 +1,178 @@ +namespace InputBinding; +using System.Windows.Forms; + +public partial class InputtingForm : Form +{ + public InputtingForm() + { + InitializeComponent(); + } + + private void Form1_Load(object sender, EventArgs e) + { + if (!File.Exists(InputHelper.SectionFile)) + { + InputVar.SF = true; + return; + } + + var str = InputHelper.GetValue(InputHelper.Section, "Shift切换"); + checkBox1.Checked = InputVar.SF = bool.Parse(str); + str = InputHelper.GetValue(InputHelper.Section, "Ctrl切换"); + checkBox2.Checked = InputVar.CT = bool.Parse(str); + str = InputHelper.GetValue(InputHelper.Section, "Ctrl+空格"); + checkBox4.Checked = InputVar.CK = bool.Parse(str); + str = InputHelper.GetValue(InputHelper.Section, "Ctrl+Shift"); + checkBox5.Checked = InputVar.CS = bool.Parse(str); + str = InputHelper.GetValue(InputHelper.Section, "Win+空格"); + checkBox6.Checked = InputVar.AS = bool.Parse(str); + str = InputHelper.GetValue(InputHelper.Section, "去多余字母"); + checkBox3.Checked = InputVar.DY = bool.Parse(str); + str = InputHelper.GetValue(InputHelper.Section, "增加字母"); + checkBox7.Checked = InputVar.SL = bool.Parse(str); + } + /// + /// 保存配置并退出 + /// + /// + /// + private void Button1_Click(object sender, EventArgs e) + { + try + { + InputVar.SF = checkBox1.Checked; + InputVar.CT = checkBox2.Checked; + InputVar.DY = checkBox3.Checked; + InputVar.CK = checkBox4.Checked; + InputVar.CS = checkBox5.Checked; + InputVar.AS = checkBox6.Checked; + InputVar.SL = checkBox7.Checked; + InputHelper.WritePrivateProfileString(InputHelper.Section, "Shift切换", checkBox1.Checked.ToString(), InputHelper.SectionFile); + InputHelper.WritePrivateProfileString(InputHelper.Section, "Ctrl切换", checkBox2.Checked.ToString(), InputHelper.SectionFile); + InputHelper.WritePrivateProfileString(InputHelper.Section, "Ctrl+空格", checkBox4.Checked.ToString(), InputHelper.SectionFile); + InputHelper.WritePrivateProfileString(InputHelper.Section, "Ctrl+Shift", checkBox5.Checked.ToString(), InputHelper.SectionFile); + InputHelper.WritePrivateProfileString(InputHelper.Section, "Win+空格", checkBox6.Checked.ToString(), InputHelper.SectionFile); + InputHelper.WritePrivateProfileString(InputHelper.Section, "去多余字母", checkBox3.Checked.ToString(), InputHelper.SectionFile); + InputHelper.WritePrivateProfileString(InputHelper.Section, "增加字母", checkBox7.Checked.ToString(), InputHelper.SectionFile); + + if (InputHelper.SectionFile != null) + MessageBox.Show("保存成功"); + } + catch + { + MessageBox.Show("保存失败"); + } + Close(); + } + /// + /// 临时关闭自动切换 + /// + /// + /// + private void Button2_Click(object sender, EventArgs e) + { + Inputting.IFoxInput(); + } + /// + /// 打开自动切换 + /// + /// + /// + private void Button3_Click(object sender, EventArgs e) + { + Inputting.IFoxInput(); + } + + /// + /// Shift切换中英文(默认) + /// + /// + /// + private void CheckBox1_CheckedChanged(object sender, EventArgs e) + { + if (!checkBox1.Checked) + return; + checkBox2.Checked = false; + checkBox4.Checked = false; + checkBox5.Checked = false; + checkBox6.Checked = false; + } + /// + /// Ctrl切换中英文 + /// + /// + /// + private void CheckBox2_CheckedChanged(object sender, EventArgs e) + { + if (!checkBox2.Checked) + return; + checkBox1.Checked = false; + checkBox4.Checked = false; + checkBox5.Checked = false; + checkBox6.Checked = false; + } + /// + /// 消除切换后命令行多余字母 + /// + /// + /// + private void CheckBox3_CheckedChanged(object sender, EventArgs e) + { + if (!checkBox3.Checked) + return; + checkBox7.Checked = false; + } + /// + /// Ctrl+空格 + /// + /// + /// + private void CheckBox4_CheckedChanged(object sender, EventArgs e) + { + if (!checkBox4.Checked) + return; + checkBox1.Checked = + checkBox2.Checked = + checkBox5.Checked = + checkBox6.Checked = false; + } + /// + /// Ctrl+Shift + /// + /// + /// + private void CheckBox5_CheckedChanged(object sender, EventArgs e) + { + if (!checkBox5.Checked) + return; + checkBox1.Checked = + checkBox2.Checked = + checkBox4.Checked = + checkBox6.Checked = false; + } + /// + /// Win+空格 + /// + /// + /// + private void CheckBox6_CheckedChanged(object sender, EventArgs e) + { + if (!checkBox6.Checked) + return; + checkBox1.Checked = + checkBox2.Checked = + checkBox4.Checked = + checkBox5.Checked = false; + } + /// + /// 解决切换后需多按一次命令首字母 + /// + /// + /// + private void CheckBox7_CheckedChanged(object sender, EventArgs e) + { + if (!checkBox7.Checked) + return; + checkBox3.Checked = false; + } +} \ No newline at end of file diff --git "a/tests/TestAcad09plus/\350\276\223\345\205\245\346\263\225\350\207\252\345\212\250\345\210\207\346\215\242/InputBinding/InputtingForm.resx" "b/tests/TestAcad09plus/\350\276\223\345\205\245\346\263\225\350\207\252\345\212\250\345\210\207\346\215\242/InputBinding/InputtingForm.resx" new file mode 100644 index 0000000..d58980a --- /dev/null +++ "b/tests/TestAcad09plus/\350\276\223\345\205\245\346\263\225\350\207\252\345\212\250\345\210\207\346\215\242/InputBinding/InputtingForm.resx" @@ -0,0 +1,120 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + \ No newline at end of file diff --git "a/tests/TestAcad09plus/\350\276\223\345\205\245\346\263\225\350\207\252\345\212\250\345\210\207\346\215\242/\350\265\253\346\200\235\350\276\223\345\205\245\346\263\225/Cmd.cs" "b/tests/TestAcad09plus/\350\276\223\345\205\245\346\263\225\350\207\252\345\212\250\345\210\207\346\215\242/\350\265\253\346\200\235\350\276\223\345\205\245\346\263\225/Cmd.cs" new file mode 100644 index 0000000..8f8e5d5 --- /dev/null +++ "b/tests/TestAcad09plus/\350\276\223\345\205\245\346\263\225\350\207\252\345\212\250\345\210\207\346\215\242/\350\265\253\346\200\235\350\276\223\345\205\245\346\263\225/Cmd.cs" @@ -0,0 +1,30 @@ +namespace Gstar_IMEFilter; + +public class Cmd +{ + [IFoxInitialize] + public void Initialize() + { + Env.Printl($"※输入法控制※\n{nameof(Gstar_IMEFilter)} - 切换开关\n" + nameof(Gstar_IMEFilterSettings) + " - 设置\n"); + DocReactor.IntialReactor(); + Settings.LoadSettings(); + IMEControl.SetIMEHook(); + Acap.QuitWillStart += (s, e) => { + IMEControl.UnIMEHook(); + }; + } + + [CommandMethod(nameof(Gstar_IMEFilter))] + public void Gstar_IMEFilter() + { + Settings.Use = !Settings.Use; + Env.Printl("已经 " + (Settings.Use ? "开启" : "禁用") + " 输入法+"); + } + + [CommandMethod(nameof(Gstar_IMEFilterSettings))] + public void Gstar_IMEFilterSettings() + { + Autodesk.AutoCAD.Internal.Utils.SetFocusToDwgView(); + ShowWPFWindowCentered.Show(new SettingsWindow(), true); + } +} \ No newline at end of file diff --git "a/tests/TestAcad09plus/\350\276\223\345\205\245\346\263\225\350\207\252\345\212\250\345\210\207\346\215\242/\350\265\253\346\200\235\350\276\223\345\205\245\346\263\225/DPI.cs" "b/tests/TestAcad09plus/\350\276\223\345\205\245\346\263\225\350\207\252\345\212\250\345\210\207\346\215\242/\350\265\253\346\200\235\350\276\223\345\205\245\346\263\225/DPI.cs" new file mode 100644 index 0000000..5fb3622 --- /dev/null +++ "b/tests/TestAcad09plus/\350\276\223\345\205\245\346\263\225\350\207\252\345\212\250\345\210\207\346\215\242/\350\265\253\346\200\235\350\276\223\345\205\245\346\263\225/DPI.cs" @@ -0,0 +1,8 @@ +using System.Drawing; + +namespace Gstar_IMEFilter; + +public class DPI +{ + public static double CurrentDPI() => (double)Graphics.FromHwnd(IntPtr.Zero).DpiX / 96.0; +} \ No newline at end of file diff --git "a/tests/TestAcad09plus/\350\276\223\345\205\245\346\263\225\350\207\252\345\212\250\345\210\207\346\215\242/\350\265\253\346\200\235\350\276\223\345\205\245\346\263\225/DocReactor.cs" "b/tests/TestAcad09plus/\350\276\223\345\205\245\346\263\225\350\207\252\345\212\250\345\210\207\346\215\242/\350\265\253\346\200\235\350\276\223\345\205\245\346\263\225/DocReactor.cs" new file mode 100644 index 0000000..13a37c8 --- /dev/null +++ "b/tests/TestAcad09plus/\350\276\223\345\205\245\346\263\225\350\207\252\345\212\250\345\210\207\346\215\242/\350\265\253\346\200\235\350\276\223\345\205\245\346\263\225/DocReactor.cs" @@ -0,0 +1,62 @@ +namespace Gstar_IMEFilter; + +public class DocReactor +{ + internal static void IntialReactor() + { + var dm = Acap.DocumentManager; + var doc = dm.MdiActiveDocument; + var db = doc.Database; + var ed = doc.Editor; + dm.DocumentCreated += DocumentCreated; + + foreach (Document item in dm) + AddReactor(item); + } + + internal static void RemoveReactor() + { + var dm = Acap.DocumentManager; + var doc = dm.MdiActiveDocument; + var db = doc.Database; + var ed = doc.Editor; + foreach (Document item in dm) + RemoveReactor(item); + + dm.DocumentCreated -= DocumentCreated; + } + + static void DocumentCreated(object sender, DocumentCollectionEventArgs e) + { + AddReactor(e.Document); + } + + private static void RemoveReactor(Document doc) + { + if (doc == null) + return; + + doc.CommandWillStart -= CommandWillStart; + } + + private static void AddReactor(Document doc) + { + if (doc == null) + return; + + doc.CommandWillStart += CommandWillStart; + } + + private static void CommandWillStart(object sender, CommandEventArgs e) + { + if (!Settings.Use || ((Document)sender).Editor.IsQuiescentForTransparentCommand()) + return; + + var gName = e.GlobalCommandName; + if (gName == "-HATCHEDIT" || gName == "UNDO") + return; + + //此函数将焦点设置为视图: + Autodesk.AutoCAD.Internal.Utils.SetFocusToDwgView(); + } +} \ No newline at end of file diff --git "a/tests/TestAcad09plus/\350\276\223\345\205\245\346\263\225\350\207\252\345\212\250\345\210\207\346\215\242/\350\265\253\346\200\235\350\276\223\345\205\245\346\263\225/IMEControl.cs" "b/tests/TestAcad09plus/\350\276\223\345\205\245\346\263\225\350\207\252\345\212\250\345\210\207\346\215\242/\350\265\253\346\200\235\350\276\223\345\205\245\346\263\225/IMEControl.cs" new file mode 100644 index 0000000..2c44366 --- /dev/null +++ "b/tests/TestAcad09plus/\350\276\223\345\205\245\346\263\225\350\207\252\345\212\250\345\210\207\346\215\242/\350\265\253\346\200\235\350\276\223\345\205\245\346\263\225/IMEControl.cs" @@ -0,0 +1,275 @@ +using System.Diagnostics; +using System.Windows.Forms; + +namespace Gstar_IMEFilter; + +public class IMEControl +{ + static readonly Regex CMDReg = new("\\(C:.*\\)"); + const int WM_KEYDOWN = 256; + const int WM_KEYUP = 257; + //const int WM_CHAR = 258; + //const int WM_KILLFOCUS = 8; + + internal static string[] DefaultCMDs; + internal static List ExceptCMDs; + internal static int AcadPID; + internal static IntPtr hnexthookproc; + internal static WindowsAPI.CallBackX86? HookProcX86; + internal static WindowsAPI.CallBackX64? HookProcX64; + internal static WindowsAPI.CallBack? HookProc; + + static IMEControl() + { + DefaultCMDs = new string[] { "MTEXT", "DDEDIT", "MTEDIT", "TABLEDIT", "MLEADER", "QLEADER", "MLEADERCONTENTEDIT", "MLEADEREDIT", "TEXTEDIT", "TEXT", "QLEADER" }; + ExceptCMDs = new(); + AcadPID = Process.GetCurrentProcess().Id; + hnexthookproc = IntPtr.Zero; + CheckLowLevelHooksTimeout(); + + string? lines = null; + var ftFile = Path.Combine(Settings.MyDir, "ExceptCMDs.ft"); + if (File.Exists(ftFile)) + lines = File.ReadAllText(ftFile, Encoding.UTF8); + else + Debug.WriteLine("配置文件丢失: " + ftFile); + + if (lines != null) + DefaultCMDs = lines.Split(new string[] { "," }, StringSplitOptions.RemoveEmptyEntries); + } + + public static bool IMEHook(int nCode, int wParam, int lParam) + { + var dm = Acap.DocumentManager; + if (dm.Count == 0) + return false; + var doc = dm.MdiActiveDocument; + if (doc == null) + return false; + if (!WindowsAPI.IsWindowEnabled(Acap.MainWindow.Handle)) + return false; + + string input = doc.CommandInProgress; + Match match = CMDReg.Match(input); + if (match.Success) + input = input.Substring(checked(match.Index + 3), checked(match.Length - 4)); + + if (ExceptCMDs.Contains(input.ToUpper())) + return false; + + if (wParam >= 65 && wParam <= 90 + || (wParam == 32 || wParam == 188) + || (wParam == 190 || wParam == 222 || (wParam == 189 || wParam == 13)) + || (wParam == 187 || wParam == 186 || (wParam == 191 || wParam == 192) || + (wParam == 219 || wParam == 220 || (wParam == 221 || wParam == 222))) + || (wParam == 223 || wParam >= 48 && wParam <= 57 || (wParam == 27 || wParam >= 96 && wParam <= 105) || wParam == 110)) + { + StringBuilder lpClassName1 = new(byte.MaxValue); + IntPtr focus = WindowsAPI.GetFocus(); + WindowsAPI.GetClassName(focus, lpClassName1, checked(lpClassName1.Capacity + 1)); + + string left = lpClassName1.ToString().ToLower(); + if (left.StartsWith("afx")) + { + WindowsAPI.PostMessage(focus, WM_KEYDOWN, new IntPtr(wParam), new IntPtr(65537)); + return true; + } + + if (left.StartsWith("hwndwrapper")) + { + IntPtr parent = WindowsAPI.GetParent(focus); + + StringBuilder lpString = new(byte.MaxValue); + WindowsAPI.GetWindowText(parent, lpString, checked(lpString.Capacity + 1)); + if (lpString.ToString().ToLower() == "CLI Palette".ToLower()) + { + WindowsAPI.PostMessage(focus, WM_KEYUP, new IntPtr(wParam), new IntPtr(65537)); + return true; + } + + StringBuilder lpClassName2 = new(byte.MaxValue); + WindowsAPI.GetClassName(parent, lpClassName2, checked(lpClassName2.Capacity + 1)); + if (lpClassName2.ToString().ToLower().StartsWith("afxmdiframe")) + { + WindowsAPI.PostMessage(focus, WM_KEYUP, new IntPtr(wParam), new IntPtr(65537)); + return true; + } + } + + if (left.StartsWith("edit")) + { + IntPtr parent = WindowsAPI.GetParent(focus); + StringBuilder lpClassName2 = new(byte.MaxValue); + WindowsAPI.GetClassName(parent, lpClassName2, checked(lpClassName2.Capacity + 1)); + if (lpClassName2.ToString().ToLower().StartsWith("afx") && WindowsAPI.GetParent(parent) != doc.Window.Handle) + { + WindowsAPI.PostMessage(focus, WM_KEYDOWN, new IntPtr(wParam), new IntPtr(3735553)); + return true; + } + } + + if (left == "cicerouiwndframe") + return true; + } + return false; + } + + internal static void UnIMEHook() + { + if (!(hnexthookproc != IntPtr.Zero)) + return; + + WindowsAPI.UnhookWindowsHookEx(hnexthookproc); + hnexthookproc = IntPtr.Zero; + } + + internal static void SetIMEHook() + { + UnIMEHook(); + ExceptCMDs.Clear(); + if (hnexthookproc != IntPtr.Zero || !Settings.Use) + return; + + ExceptCMDs.AddRange(DefaultCMDs); + ExceptCMDs.AddRange(Settings.UserFilter.Split(new string[] { "," }, StringSplitOptions.RemoveEmptyEntries)); + + if (Settings.IMEStyle != Settings.IMEStyleS.Global) + { + HookProc = MyKeyboardProc; + hnexthookproc = WindowsAPI.SetWindowsHookEx(WindowsAPI.HookType.WH_KEYBOARD, HookProc, 0, WindowsAPI.GetCurrentThreadId()); + return; + } + + var moduleHandle = (long)WindowsAPI.GetModuleHandle(Process.GetCurrentProcess().MainModule.ModuleName); + if (Marshal.SizeOf(typeof(IntPtr)) == 4) + { + HookProcX86 = (nCode, wParam, lParam) => { + return (IntPtr)MyKeyboardProcX86(nCode, wParam, lParam); + }; + hnexthookproc = WindowsAPI.SetWindowsHookEx(WindowsAPI.HookType.WH_KEYBOARD_LL, HookProcX86, moduleHandle, 0); + } + else + { + HookProcX64 = (nCode, wParam, lParam) => { + return (IntPtr)MyKeyboardProcX64(nCode, wParam, lParam); + }; + hnexthookproc = WindowsAPI.SetWindowsHookEx(WindowsAPI.HookType.WH_KEYBOARD_LL, HookProcX64, moduleHandle, 0); + } + } + + public static IntPtr MyKeyboardProc(int nCode, int wParam, int lParam) + { + if (nCode >= 0) + { + bool flag = !(lParam > 0 & (lParam & -1073741823) == 1) || + (Control.ModifierKeys != Keys.None && Control.ModifierKeys != Keys.Shift) || + !IMEHook(nCode, wParam, lParam); + if (!flag) + return (IntPtr)1; + } + return (IntPtr)WindowsAPI.CallNextHookEx(hnexthookproc, nCode, wParam, lParam); + } + + static int MyKeyboardProcX64(int nCode, int wParam, long lParam) + { + if (!Settings.Use || + WindowsAPI.IsIconic(Acap.MainWindow.Handle.ToInt32()) || + WindowsAPI.GetKeyState(91) < 0 || + WindowsAPI.GetKeyState(92) < 0 || + wParam != WM_KEYDOWN || + nCode < 0) + return WindowsAPI.CallNextHookEx(hnexthookproc, nCode, wParam, lParam); + + IFoxCAD.Cad.WindowsAPI.GetWindowThreadProcessId(WindowsAPI.GetForegroundWindow(), out uint lpdwProcessId); + if (lpdwProcessId != AcadPID) + return WindowsAPI.CallNextHookEx(hnexthookproc, nCode, wParam, lParam); + + var keyb = KeyboardHookStruct.Create(new IntPtr(lParam)); + switch (Control.ModifierKeys) + { + case Keys.None: + { + if (WindowsAPI.GetKeyState(162) < 0 || + WindowsAPI.GetKeyState(163) < 0 || + WindowsAPI.GetKeyState(17) < 0 || + WindowsAPI.GetKeyState(262144) < 0) + return WindowsAPI.CallNextHookEx(hnexthookproc, nCode, wParam, lParam); + + if (IMEHook(nCode, keyb.vkCode, 0)) + return 1; + break; + } + case Keys.Shift: + { + if (IMEHook(nCode, keyb.vkCode, 0)) + return 1; + break; + } + } + return WindowsAPI.CallNextHookEx(hnexthookproc, nCode, wParam, lParam); + } + + static int MyKeyboardProcX86(int nCode, int wParam, int lParam) + { + if (!Settings.Use || + WindowsAPI.IsIconic(Acap.MainWindow.Handle.ToInt32()) || + WindowsAPI.GetKeyState(91) < 0 || + WindowsAPI.GetKeyState(92) < 0 || + wParam != WM_KEYDOWN || + nCode < 0) + return WindowsAPI.CallNextHookEx(hnexthookproc, nCode, wParam, lParam); + + IFoxCAD.Cad.WindowsAPI.GetWindowThreadProcessId(WindowsAPI.GetFocus(), out uint lpdwProcessId); + if (lpdwProcessId != AcadPID) + return WindowsAPI.CallNextHookEx(hnexthookproc, nCode, wParam, lParam); + + var keyb = KeyboardHookStruct.Create(new IntPtr(lParam)); + switch (Control.ModifierKeys) + { + case Keys.None: + { + if (WindowsAPI.GetKeyState(162) < 0 || + WindowsAPI.GetKeyState(163) < 0 || + WindowsAPI.GetKeyState(17) < 0 || + WindowsAPI.GetKeyState(262144) < 0) + return WindowsAPI.CallNextHookEx(hnexthookproc, nCode, wParam, lParam); + + if (IMEHook(nCode, keyb.vkCode, 0)) + return 1; + break; + } + case Keys.Shift: + { + if (IMEHook(nCode, keyb.vkCode, 0)) + return 1; + break; + } + } + return WindowsAPI.CallNextHookEx(hnexthookproc, nCode, wParam, lParam); + } + + static void CheckLowLevelHooksTimeout() + { + const string llh = "LowLevelHooksTimeout"; + + RegistryKey hkml = Registry.CurrentUser; + RegistryKey registryKey = hkml.OpenSubKey("Control Panel\\Desktop", true); + if ((int)registryKey.GetValue(llh, 0) == 0) + registryKey.SetValue(llh, 25000, RegistryValueKind.DWord); + registryKey.Close(); + } + + public struct KeyboardHookStruct + { + public int vkCode; + public int ScanCode; + public int Flags; + public int Time; + public int DwExtraInfo; + + public static KeyboardHookStruct Create(IntPtr lParam) + { + return (KeyboardHookStruct)Marshal.PtrToStructure(lParam, typeof(KeyboardHookStruct)); + } + } +} \ No newline at end of file diff --git "a/tests/TestAcad09plus/\350\276\223\345\205\245\346\263\225\350\207\252\345\212\250\345\210\207\346\215\242/\350\265\253\346\200\235\350\276\223\345\205\245\346\263\225/ResourceDictionary/ButtonDictionary.xaml" "b/tests/TestAcad09plus/\350\276\223\345\205\245\346\263\225\350\207\252\345\212\250\345\210\207\346\215\242/\350\265\253\346\200\235\350\276\223\345\205\245\346\263\225/ResourceDictionary/ButtonDictionary.xaml" new file mode 100644 index 0000000..50e85e1 --- /dev/null +++ "b/tests/TestAcad09plus/\350\276\223\345\205\245\346\263\225\350\207\252\345\212\250\345\210\207\346\215\242/\350\265\253\346\200\235\350\276\223\345\205\245\346\263\225/ResourceDictionary/ButtonDictionary.xaml" @@ -0,0 +1,374 @@ + + + + + + + + + + \ No newline at end of file diff --git "a/tests/TestAcad09plus/\350\276\223\345\205\245\346\263\225\350\207\252\345\212\250\345\210\207\346\215\242/\350\265\253\346\200\235\350\276\223\345\205\245\346\263\225/ResourceDictionary/ComboboxDictionary.xaml" "b/tests/TestAcad09plus/\350\276\223\345\205\245\346\263\225\350\207\252\345\212\250\345\210\207\346\215\242/\350\265\253\346\200\235\350\276\223\345\205\245\346\263\225/ResourceDictionary/ComboboxDictionary.xaml" new file mode 100644 index 0000000..7a7848a --- /dev/null +++ "b/tests/TestAcad09plus/\350\276\223\345\205\245\346\263\225\350\207\252\345\212\250\345\210\207\346\215\242/\350\265\253\346\200\235\350\276\223\345\205\245\346\263\225/ResourceDictionary/ComboboxDictionary.xaml" @@ -0,0 +1,345 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git "a/tests/TestAcad09plus/\350\276\223\345\205\245\346\263\225\350\207\252\345\212\250\345\210\207\346\215\242/\350\265\253\346\200\235\350\276\223\345\205\245\346\263\225/ResourceDictionary/ComboboxitemDictionary.xaml" "b/tests/TestAcad09plus/\350\276\223\345\205\245\346\263\225\350\207\252\345\212\250\345\210\207\346\215\242/\350\265\253\346\200\235\350\276\223\345\205\245\346\263\225/ResourceDictionary/ComboboxitemDictionary.xaml" new file mode 100644 index 0000000..05b89cb --- /dev/null +++ "b/tests/TestAcad09plus/\350\276\223\345\205\245\346\263\225\350\207\252\345\212\250\345\210\207\346\215\242/\350\265\253\346\200\235\350\276\223\345\205\245\346\263\225/ResourceDictionary/ComboboxitemDictionary.xaml" @@ -0,0 +1,108 @@ + + + + + + + + + + + + + + \ No newline at end of file diff --git "a/tests/TestAcad09plus/\350\276\223\345\205\245\346\263\225\350\207\252\345\212\250\345\210\207\346\215\242/\350\265\253\346\200\235\350\276\223\345\205\245\346\263\225/ResourceDictionary/ScrollviewDictionary.xaml" "b/tests/TestAcad09plus/\350\276\223\345\205\245\346\263\225\350\207\252\345\212\250\345\210\207\346\215\242/\350\265\253\346\200\235\350\276\223\345\205\245\346\263\225/ResourceDictionary/ScrollviewDictionary.xaml" new file mode 100644 index 0000000..e19dfd8 --- /dev/null +++ "b/tests/TestAcad09plus/\350\276\223\345\205\245\346\263\225\350\207\252\345\212\250\345\210\207\346\215\242/\350\265\253\346\200\235\350\276\223\345\205\245\346\263\225/ResourceDictionary/ScrollviewDictionary.xaml" @@ -0,0 +1,150 @@ + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git "a/tests/TestAcad09plus/\350\276\223\345\205\245\346\263\225\350\207\252\345\212\250\345\210\207\346\215\242/\350\265\253\346\200\235\350\276\223\345\205\245\346\263\225/ResourceDictionary/TextboxDictionary.xaml" "b/tests/TestAcad09plus/\350\276\223\345\205\245\346\263\225\350\207\252\345\212\250\345\210\207\346\215\242/\350\265\253\346\200\235\350\276\223\345\205\245\346\263\225/ResourceDictionary/TextboxDictionary.xaml" new file mode 100644 index 0000000..76d441c --- /dev/null +++ "b/tests/TestAcad09plus/\350\276\223\345\205\245\346\263\225\350\207\252\345\212\250\345\210\207\346\215\242/\350\265\253\346\200\235\350\276\223\345\205\245\346\263\225/ResourceDictionary/TextboxDictionary.xaml" @@ -0,0 +1,28 @@ + + + \ No newline at end of file diff --git "a/tests/TestAcad09plus/\350\276\223\345\205\245\346\263\225\350\207\252\345\212\250\345\210\207\346\215\242/\350\265\253\346\200\235\350\276\223\345\205\245\346\263\225/Settings.cs" "b/tests/TestAcad09plus/\350\276\223\345\205\245\346\263\225\350\207\252\345\212\250\345\210\207\346\215\242/\350\265\253\346\200\235\350\276\223\345\205\245\346\263\225/Settings.cs" new file mode 100644 index 0000000..506bfdd --- /dev/null +++ "b/tests/TestAcad09plus/\350\276\223\345\205\245\346\263\225\350\207\252\345\212\250\345\210\207\346\215\242/\350\265\253\346\200\235\350\276\223\345\205\245\346\263\225/Settings.cs" @@ -0,0 +1,148 @@ +using System.Xml; + +namespace Gstar_IMEFilter; + +public class Settings +{ + static string _MyDir = ""; + static string _MySettingsPath = ""; + static bool _Use = true; + internal static string _UserFilter = ""; + internal static IMEStyleS _IMEStyle = IMEStyleS.Global; + + internal static string MyDir + { + get + { + if (_MyDir.Length == 0) + _MyDir = Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location); + return _MyDir; + } + } + + public static string MySettingsPath + { + get + { + if (_MySettingsPath.Length == 0) + _MySettingsPath = Path.Combine(MyDir, nameof(Gstar_IMEFilter) + ".xml"); + return _MySettingsPath; + } + } + + public static string UserFilter + { + get => _UserFilter; + set + { + if (_UserFilter.Length == 0) + return; + _UserFilter = value; + SaveSettings(); + } + } + + public static bool Use + { + get => _Use; + set + { + if (_Use == value) + return; + _Use = value; + SaveSettings(); + IMEControl.SetIMEHook(); + } + } + + public static IMEStyleS IMEStyle + { + get => _IMEStyle; + set + { + if (_IMEStyle == value) + return; + _IMEStyle = value; + SaveSettings(); + IMEControl.SetIMEHook(); + } + } + + public static void LoadSettings() + { + if (!File.Exists(MySettingsPath)) + return; + + var xmlReader = XmlReader.Create(MySettingsPath); + while (xmlReader.Read()) + { + if (xmlReader.NodeType != XmlNodeType.Element) + continue; + + string left = xmlReader.Name.ToLower(); + if (left != "use") + { + if (left != "userfilter") + { + if (left == "imestyle") + { + int.TryParse(xmlReader.ReadInnerXml(), out int imes); + _IMEStyle = (IMEStyleS)imes; + } + } + else + { + _UserFilter = xmlReader.ReadInnerXml().ToUpper(); + } + } + else + { + bool.TryParse(xmlReader.ReadInnerXml(), out _Use); + } + } + xmlReader.Close(); + } + + internal static void SaveSettings() + { + try + { + XmlWriterSettings settings = new() + { + Indent = 1 != 0, + NewLineChars = Environment.NewLine + }; + + XmlWriter xmlWriter = XmlWriter.Create(MySettingsPath, settings); + xmlWriter.WriteStartDocument(1 != 0); + xmlWriter.WriteComment("输入法+"); + + xmlWriter.WriteStartElement("settings"); + xmlWriter.WriteStartElement("use"); + xmlWriter.WriteString(Use.ToString()); + xmlWriter.WriteEndElement(); + + xmlWriter.WriteStartElement("userfilter"); + xmlWriter.WriteString(UserFilter); + xmlWriter.WriteEndElement(); + xmlWriter.WriteStartElement("imestyle"); + + xmlWriter.WriteString(((int)IMEStyle).ToString()); + xmlWriter.WriteEndElement(); + xmlWriter.WriteEndElement(); + xmlWriter.WriteEndDocument(); + + xmlWriter.Close(); + } + catch (Exception ex) + { + throw ex; + } + } + + public enum IMEStyleS + { + Global, + Process, + } +} \ No newline at end of file diff --git "a/tests/TestAcad09plus/\350\276\223\345\205\245\346\263\225\350\207\252\345\212\250\345\210\207\346\215\242/\350\265\253\346\200\235\350\276\223\345\205\245\346\263\225/ShowWPFWindowCentered.cs" "b/tests/TestAcad09plus/\350\276\223\345\205\245\346\263\225\350\207\252\345\212\250\345\210\207\346\215\242/\350\265\253\346\200\235\350\276\223\345\205\245\346\263\225/ShowWPFWindowCentered.cs" new file mode 100644 index 0000000..38b6cc8 --- /dev/null +++ "b/tests/TestAcad09plus/\350\276\223\345\205\245\346\263\225\350\207\252\345\212\250\345\210\207\346\215\242/\350\265\253\346\200\235\350\276\223\345\205\245\346\263\225/ShowWPFWindowCentered.cs" @@ -0,0 +1,31 @@ +using System.Windows; +using System.Windows.Interop; + +namespace Gstar_IMEFilter; + +public class ShowWPFWindowCentered +{ + internal static bool Show(Window window, bool modal) + { + new WindowInteropHelper(window).Owner = Acap.MainWindow.Handle; + WindowsAPI.RECT lpRect = new(); + WindowsAPI.GetWindowRect(Acap.MainWindow.Handle, ref lpRect); + + var dpi = DPI.CurrentDPI(); + window.Left = (lpRect.Width / dpi - window.Width) / 2.0 + + lpRect.Left / dpi; + + window.Top = (lpRect.Height / dpi - window.Height) / 2.0 + + lpRect.Top / dpi; + + if (modal) + { + var flag = window.ShowDialog(); + if (flag is null) + return false; + return flag.Value; + } + window.Show(); + return false; + } +} \ No newline at end of file diff --git "a/tests/TestAcad09plus/\350\276\223\345\205\245\346\263\225\350\207\252\345\212\250\345\210\207\346\215\242/\350\265\253\346\200\235\350\276\223\345\205\245\346\263\225/Window/SettingsWindow.xaml" "b/tests/TestAcad09plus/\350\276\223\345\205\245\346\263\225\350\207\252\345\212\250\345\210\207\346\215\242/\350\265\253\346\200\235\350\276\223\345\205\245\346\263\225/Window/SettingsWindow.xaml" new file mode 100644 index 0000000..c0a0da8 --- /dev/null +++ "b/tests/TestAcad09plus/\350\276\223\345\205\245\346\263\225\350\207\252\345\212\250\345\210\207\346\215\242/\350\265\253\346\200\235\350\276\223\345\205\245\346\263\225/Window/SettingsWindow.xaml" @@ -0,0 +1,52 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git "a/tests/TestAcad09plus/\350\276\223\345\205\245\346\263\225\350\207\252\345\212\250\345\210\207\346\215\242/\350\265\253\346\200\235\350\276\223\345\205\245\346\263\225/Window/SettingsWindow.xaml.cs" "b/tests/TestAcad09plus/\350\276\223\345\205\245\346\263\225\350\207\252\345\212\250\345\210\207\346\215\242/\350\265\253\346\200\235\350\276\223\345\205\245\346\263\225/Window/SettingsWindow.xaml.cs" new file mode 100644 index 0000000..42f2e14 --- /dev/null +++ "b/tests/TestAcad09plus/\350\276\223\345\205\245\346\263\225\350\207\252\345\212\250\345\210\207\346\215\242/\350\265\253\346\200\235\350\276\223\345\205\245\346\263\225/Window/SettingsWindow.xaml.cs" @@ -0,0 +1,40 @@ +using System.Windows; +using System.Windows.Input; + +namespace Gstar_IMEFilter; + +/// +/// SettingsWindow.xaml 的交互逻辑 +/// +public partial class SettingsWindow : Window +{ + public SettingsWindow() + { + InitializeComponent(); + Loaded += Window_Loaded; + PreviewKeyDown += Window_PreviewKeyDown; + } + + private void Button_Click(object sender, RoutedEventArgs e) + { + Settings._UserFilter = ExCMD.Text; + Settings._IMEStyle = (Settings.IMEStyleS)CBox.SelectedIndex; + Settings.SaveSettings(); + IMEControl.SetIMEHook(); + DialogResult = true; + } + + private void Window_Loaded(object sender, RoutedEventArgs e) + { + CBox.SelectedIndex = (int)Settings.IMEStyle; + ExCMD.Text = Settings.UserFilter; + DeCMD.Text = string.Join(",", IMEControl.DefaultCMDs); + } + + private void Window_PreviewKeyDown(object sender, KeyEventArgs e) + { + if (e.Key != Key.Escape) + return; + DialogResult = false; + } +} \ No newline at end of file diff --git "a/tests/TestAcad09plus/\350\276\223\345\205\245\346\263\225\350\207\252\345\212\250\345\210\207\346\215\242/\350\265\253\346\200\235\350\276\223\345\205\245\346\263\225/WindowsAPI.cs" "b/tests/TestAcad09plus/\350\276\223\345\205\245\346\263\225\350\207\252\345\212\250\345\210\207\346\215\242/\350\265\253\346\200\235\350\276\223\345\205\245\346\263\225/WindowsAPI.cs" new file mode 100644 index 0000000..842d4b7 --- /dev/null +++ "b/tests/TestAcad09plus/\350\276\223\345\205\245\346\263\225\350\207\252\345\212\250\345\210\207\346\215\242/\350\265\253\346\200\235\350\276\223\345\205\245\346\263\225/WindowsAPI.cs" @@ -0,0 +1,96 @@ +namespace Gstar_IMEFilter; + +public class WindowsAPI +{ + [DllImport("user32.dll", SetLastError = true)] + internal static extern IntPtr GetFocus(); + + [DllImport("user32.dll")] + internal static extern int GetClassName(IntPtr hwnd, StringBuilder lpClassName, int nMaxCount); + + [DllImport("user32.dll ")] + internal static extern IntPtr SendMessage(IntPtr hwnd, int msg, IntPtr wParam, IntPtr lParam); + + [DllImport("user32.dll ")] + internal static extern IntPtr PostMessage(IntPtr hwnd, int msg, IntPtr wParam, IntPtr lParam); + + [DllImport("user32.dll", SetLastError = true)] + internal static extern IntPtr GetParent(IntPtr hWnd); + + [DllImport("kernel32.dll")] + internal static extern long GetHandleInformation(long hObject, ref long lpdwFlags); + + [DllImport("user32.dll", CharSet = CharSet.Unicode, SetLastError = true)] + internal static extern int GetWindowText(IntPtr hWnd, StringBuilder lpString, int nMaxCount); + + [DllImport("user32.dll")] + internal static extern int GetWindowTextLength(IntPtr hWnd); + + [DllImport("kernel32.dll")] + internal static extern int GetCurrentThreadId(); + + [DllImport("user32.dll")] + internal static extern IntPtr SetWindowsHookEx(HookType idHook, CallBackX86 lpfn, long hmod, int dwThreadId); + + [DllImport("user32.dll")] + public static extern IntPtr SetWindowsHookEx(HookType idHook, CallBack lpfn, int hmod, int dwThreadId); + + [DllImport("user32.dll")] + internal static extern IntPtr SetWindowsHookEx(HookType idHook, CallBackX64 lpfn, long hmod, int dwThreadId); + + [DllImport("user32.dll")] + internal static extern int UnhookWindowsHookEx(IntPtr hHook); + + [DllImport("user32.dll")] + internal static extern int CallNextHookEx(IntPtr hHook, int ncode, int wParam, long lParam); + + [DllImport("user32.dll")] + internal static extern int CallNextHookEx(int hHook, int ncode, int wParam, int lParam); + + [DllImport("kernel32.dll")] + internal static extern IntPtr GetModuleHandle(string ModuleName); + + [DllImport("user32.dll")] + internal static extern int ToAscii(int uVirtKey, int uScancode, byte[] lpdKeyState, byte[] lpwTransKey, int fuState); + + [DllImport("user32.dll", SetLastError = true)] + internal static extern IntPtr GetForegroundWindow(); + + [DllImport("user32.dll", SetLastError = true)] + internal static extern IntPtr GetActiveWindow(); + + [DllImport("user32.dll", SetLastError = true)] + internal static extern long GetWindowThreadProcessId(IntPtr hwnd, ref int lpdwProcessId); + + [DllImport("user32.dll", SetLastError = true)] + internal static extern short GetKeyState(int nVirtKey); + + [DllImport("user32.dll", SetLastError = true)] + internal static extern bool IsIconic(int hWnd); + + [DllImport("user32.dll")] + internal static extern bool IsWindowEnabled(IntPtr hWnd); + + [DllImport("user32.dll")] + internal static extern bool GetWindowRect(IntPtr hwnd, ref RECT lpRect); + + public delegate IntPtr CallBackX86(int nCode, int wParam, int lParam); + public delegate IntPtr CallBackX64(int nCode, int wParam, long lParam); + public delegate IntPtr CallBack(int nCode, int wParam, int lParam); + public enum HookType + { + WH_KEYBOARD = 2, + WH_KEYBOARD_LL = 13, + WH_MOUSE_LL = 14, + } + + public struct RECT + { + public int Left; + public int Top; + public int Right; + public int Bottom; + public int Width => checked(Right - Left); + public int Height => checked(Bottom - Top); + } +} \ No newline at end of file -- Gitee From adf18e7b8429228db5425ba608a8882c524d7e99 Mon Sep 17 00:00:00 2001 From: liuqihong <540762622@qq.com> Date: Thu, 27 Oct 2022 23:59:08 +0800 Subject: [PATCH 581/675] =?UTF-8?q?=E6=95=B4=E7=90=86WindowsAPI?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/IFoxCAD.Cad.Shared/Copyclip/BitmapTool.cs | 2 +- src/IFoxCAD.Cad.Shared/Copyclip/EmfTool.cs | 1 + .../Copyclip/TagClipboardInfo.cs | 152 +----------- src/IFoxCAD.Cad.Shared/Copyclip/WindowsAPI.cs | 226 +++++++++++++++++- .../Runtime/FileOpenMode.cs | 5 +- .../TestAcad09plus/wpf/{Class1.cs => Cmd.cs} | 2 +- .../InputBinding/Inputting.cs" | 53 ++-- .../DocReactor.cs" | 42 +--- .../IMEControl.cs" | 130 ++++------ .../Settings.cs" | 30 +-- .../ShowWPFWindowCentered.cs" | 2 +- .../WindowsAPI.cs" | 96 -------- tests/TestShared/TestMarshal.cs | 1 + 13 files changed, 326 insertions(+), 416 deletions(-) rename tests/TestAcad09plus/wpf/{Class1.cs => Cmd.cs} (89%) delete mode 100644 "tests/TestAcad09plus/\350\276\223\345\205\245\346\263\225\350\207\252\345\212\250\345\210\207\346\215\242/\350\265\253\346\200\235\350\276\223\345\205\245\346\263\225/WindowsAPI.cs" diff --git a/src/IFoxCAD.Cad.Shared/Copyclip/BitmapTool.cs b/src/IFoxCAD.Cad.Shared/Copyclip/BitmapTool.cs index 3036ac2..9ce266b 100644 --- a/src/IFoxCAD.Cad.Shared/Copyclip/BitmapTool.cs +++ b/src/IFoxCAD.Cad.Shared/Copyclip/BitmapTool.cs @@ -133,7 +133,7 @@ public static void CaptureWndImage(IntPtr hWnd, Action action) if (hMemDC == IntPtr.Zero) return; - WindowsAPI.GetClientRect(hWnd, out IntRect rcClient); + WindowsAPI.GetClientRect(hWnd, out WindowsAPI.IntRect rcClient); int width = rcClient.Right - rcClient.Left; int height = rcClient.Bottom - rcClient.Top; diff --git a/src/IFoxCAD.Cad.Shared/Copyclip/EmfTool.cs b/src/IFoxCAD.Cad.Shared/Copyclip/EmfTool.cs index 267a1bb..7040654 100644 --- a/src/IFoxCAD.Cad.Shared/Copyclip/EmfTool.cs +++ b/src/IFoxCAD.Cad.Shared/Copyclip/EmfTool.cs @@ -14,6 +14,7 @@ using System.Xml; using Point = System.Drawing.Point; using Size = System.Drawing.Size; +using static IFoxCAD.Cad.WindowsAPI; // DWORD == uint diff --git a/src/IFoxCAD.Cad.Shared/Copyclip/TagClipboardInfo.cs b/src/IFoxCAD.Cad.Shared/Copyclip/TagClipboardInfo.cs index 10d912e..5c2402f 100644 --- a/src/IFoxCAD.Cad.Shared/Copyclip/TagClipboardInfo.cs +++ b/src/IFoxCAD.Cad.Shared/Copyclip/TagClipboardInfo.cs @@ -3,6 +3,7 @@ using System; using System.Diagnostics; using System.Text; +using static IFoxCAD.Cad.WindowsAPI; public class ClipboardEnv { @@ -191,157 +192,6 @@ public override int GetHashCode() #endregion } -[ComVisible(true)] -[Serializable] -[StructLayout(LayoutKind.Sequential)] -[DebuggerDisplay("{DebuggerDisplay,nq}")] -[DebuggerTypeProxy(typeof(IntRect))] -public struct IntRect -{ - [DebuggerBrowsable(DebuggerBrowsableState.Never)] - private string DebuggerDisplay => $"(Left:{_Left},Top:{_Top},Right:{_Right},Bottom:{_Bottom})"; - - int _Left; - int _Top; - int _Right; - int _Bottom; - public int Left => _Left; - public int Top => _Top; - public int Right => _Right; - public int Bottom => _Bottom; - public IntRect(int left, int top, int right, int bottom) - { - _Left = left; - _Top = top; - _Right = right; - _Bottom = bottom; - } - - static readonly IntRect _Zero = new(0, 0, 0, 0); - public static IntRect Zero => _Zero; - - public override string ToString() - { - return $"({_Left},{_Top},{_Right},{_Bottom})"; - } - - #region 重载运算符_比较 - public bool Equals(IntRect other) - { - return - _Left == other._Left && - _Top == other._Top && - _Right == other._Right && - _Bottom == other._Bottom; - } - public static bool operator !=(IntRect a, IntRect b) - { - return !(a == b); - } - public static bool operator ==(IntRect a, IntRect b) - { - return a.Equals(b); - } - public override bool Equals(object obj) - { - return obj is IntRect d && Equals(d); - } - public override int GetHashCode() - { - return ((_Left, _Top).GetHashCode(), _Right).GetHashCode() ^ _Bottom.GetHashCode(); - } - - public IntRect Clone() - { - return (IntRect)MemberwiseClone(); - } - #endregion -} - -[ComVisible(true)] -[Serializable] -[DebuggerDisplay("{DebuggerDisplay,nq}")] -[DebuggerTypeProxy(typeof(IntSize))] -[StructLayout(LayoutKind.Sequential)] -public struct IntSize -{ - [DebuggerBrowsable(DebuggerBrowsableState.Never)] - private string DebuggerDisplay => $"(Hight:{Hight},Width:{Width})"; - - public int Hight; - public int Width; - - public IntSize(int cx, int cy) - { - this.Hight = cx; - this.Width = cy; - } - - public override string ToString() - { - return $"({Hight},{Width})"; - } -} - -[ComVisible(true)] -[Serializable] -[DebuggerDisplay("{DebuggerDisplay,nq}")] -[DebuggerTypeProxy(typeof(Point3D))] -public struct Point3D : IEquatable -{ - [DebuggerBrowsable(DebuggerBrowsableState.Never)] - private string DebuggerDisplay => $"(X:{X},Y:{Y},Z:{Z})"; - - /* 由于此类是用来优化,从而实现字段修改,因此直接暴露字段减少栈帧 */ - public double X; - public double Y; - public double Z; - - public Point3D(double x, double y, double z) - { - X = x; - Y = y; - Z = z; - } - public static implicit operator Point3D(Point3d pt) - { - return new Point3D(pt.X, pt.Y, pt.Z); - } - public static implicit operator Point3d(Point3D pt) - { - return new Point3d(pt.X, pt.Y, pt.Z); - } - public override string ToString() - { - return $"({X},{Y},{Z})"; - } - - #region 重载运算符_比较 - public bool Equals(Point3D other) - { - return - X == other.X && - Y == other.Y && - Z == other.Z; - } - public static bool operator !=(Point3D a, Point3D b) - { - return !(a == b); - } - public static bool operator ==(Point3D a, Point3D b) - { - return a.Equals(b); - } - public override bool Equals(object obj) - { - return obj is Point3D d && Equals(d); - } - public override int GetHashCode() - { - return (X, Y).GetHashCode() ^ Z.GetHashCode(); - } - #endregion -} /* * OLE 剪贴板说明 https://blog.csdn.net/chinabinlang/article/details/9815495 diff --git a/src/IFoxCAD.Cad.Shared/Copyclip/WindowsAPI.cs b/src/IFoxCAD.Cad.Shared/Copyclip/WindowsAPI.cs index 4e9783c..32e7f55 100644 --- a/src/IFoxCAD.Cad.Shared/Copyclip/WindowsAPI.cs +++ b/src/IFoxCAD.Cad.Shared/Copyclip/WindowsAPI.cs @@ -5,7 +5,7 @@ namespace IFoxCAD.Cad; using System.Diagnostics; using System.Threading; -public class WindowsAPI +public partial class WindowsAPI { // https://blog.csdn.net/haelang/article/details/45147121 [DllImport("kernel32.dll")] @@ -264,8 +264,8 @@ static void ToPtr(object? structObj, Action? task, IntPtr newPtr) /// /// /// - [DllImport("user32.dll")] - public static extern void keybd_event(byte bVk, byte bScan, int dwFlags, int dwExtraInfo); + [DllImport("user32.dll", EntryPoint = "keybd_event")] + public static extern void KeybdEvent(byte bVk, byte bScan, int dwFlags, int dwExtraInfo); /// /// 获取窗口文字的长度 /// @@ -355,6 +355,226 @@ public static GuiThreadInfo Create(uint windowThreadProcessId) [DllImport("kernel32.dll")] public static extern int GetCurrentThreadId(); + + [DllImport("user32.dll")] + public static extern IntPtr SetFocus(IntPtr hWnd); +} + + + +public partial class WindowsAPI +{ + // [DllImport("user32.dll", CharSet = CharSet.Unicode, SetLastError = true)] + // internal static extern int GetWindowText(IntPtr hWnd, StringBuilder lpString, int nMaxCount); + + [DllImport("user32.dll", SetLastError = true)] + public static extern IntPtr GetFocus(); + + [DllImport("user32.dll ")] + public static extern IntPtr SendMessage(IntPtr hwnd, int msg, IntPtr wParam, IntPtr lParam); + + [DllImport("user32.dll", SetLastError = true)] + public static extern IntPtr GetParent(IntPtr hWnd); + + [DllImport("kernel32.dll")] + public static extern long GetHandleInformation(long hObject, ref long lpdwFlags); + + [DllImport("user32.dll")] + public static extern IntPtr SetWindowsHookEx(HookType idHook, CallBackX86 lpfn, long hmod, int dwThreadId); + + [DllImport("user32.dll")] + public static extern IntPtr SetWindowsHookEx(HookType idHook, CallBack lpfn, int hmod, int dwThreadId); + + [DllImport("user32.dll")] + public static extern IntPtr SetWindowsHookEx(HookType idHook, CallBackX64 lpfn, long hmod, int dwThreadId); + + [DllImport("user32.dll")] + public static extern int UnhookWindowsHookEx(IntPtr hHook); + + [DllImport("user32.dll")] + public static extern int CallNextHookEx(IntPtr hHook, int ncode, int wParam, long lParam); + + [DllImport("user32.dll")] + public static extern int CallNextHookEx(int hHook, int ncode, int wParam, int lParam); + + [DllImport("kernel32.dll")] + public static extern IntPtr GetModuleHandle(string ModuleName); + + [DllImport("user32.dll")] + public static extern int ToAscii(int uVirtKey, int uScancode, byte[] lpdKeyState, byte[] lpwTransKey, int fuState); + + [DllImport("user32.dll", SetLastError = true)] + public static extern IntPtr GetActiveWindow(); + + [DllImport("user32.dll", SetLastError = true)] + public static extern long GetWindowThreadProcessId(IntPtr hwnd, ref int lpdwProcessId); + + [DllImport("user32.dll", SetLastError = true)] + public static extern bool IsIconic(int hWnd); + + [DllImport("user32.dll")] + public static extern bool IsWindowEnabled(IntPtr hWnd); + + public delegate IntPtr CallBackX86(int nCode, int wParam, int lParam); + public delegate IntPtr CallBackX64(int nCode, int wParam, long lParam); + public delegate IntPtr CallBack(int nCode, int wParam, int lParam); + public enum HookType + { + WH_KEYBOARD = 2, + WH_KEYBOARD_LL = 13, + WH_MOUSE_LL = 14, + } + + [DllImport("user32.dll")] + public static extern bool GetWindowRect(IntPtr hwnd, ref IntRect lpRect); +} + + +public partial class WindowsAPI +{ + [ComVisible(true)] + [Serializable] + [StructLayout(LayoutKind.Sequential)] + [DebuggerDisplay("{DebuggerDisplay,nq}")] + [DebuggerTypeProxy(typeof(IntRect))] + public struct IntRect + { + [DebuggerBrowsable(DebuggerBrowsableState.Never)] + private string DebuggerDisplay => $"(Left:{_Left},Top:{_Top},Right:{_Right},Bottom:{_Bottom})"; + + int _Left; + int _Top; + int _Right; + int _Bottom; + public int Left => _Left; + public int Top => _Top; + public int Right => _Right; + public int Bottom => _Bottom; + public int Width => checked(Right - Left); + public int Height => checked(Bottom - Top); + + public IntRect(int left, int top, int right, int bottom) + { + _Left = left; + _Top = top; + _Right = right; + _Bottom = bottom; + } + + static readonly IntRect _Zero = new(0, 0, 0, 0); + public static IntRect Zero => _Zero; + + public override string ToString() => $"({_Left},{_Top},{_Right},{_Bottom})"; + + #region 重载运算符_比较 + public bool Equals(IntRect other) + { + return + _Left == other._Left && + _Top == other._Top && + _Right == other._Right && + _Bottom == other._Bottom; + } + public static bool operator !=(IntRect a, IntRect b) + { + return !(a == b); + } + public static bool operator ==(IntRect a, IntRect b) + { + return a.Equals(b); + } + public override bool Equals(object obj) + { + return obj is IntRect d && Equals(d); + } + public override int GetHashCode() + { + return ((_Left, _Top).GetHashCode(), _Right).GetHashCode() ^ _Bottom.GetHashCode(); + } + + public IntRect Clone() + { + return (IntRect)MemberwiseClone(); + } + #endregion + } + + [ComVisible(true)] + [Serializable] + [DebuggerDisplay("{DebuggerDisplay,nq}")] + [DebuggerTypeProxy(typeof(IntSize))] + [StructLayout(LayoutKind.Sequential)] + public struct IntSize + { + [DebuggerBrowsable(DebuggerBrowsableState.Never)] + private string DebuggerDisplay => $"(Hight:{Hight},Width:{Width})"; + public int Hight; + public int Width; + + public IntSize(int cx, int cy) + { + Hight = cx; + Width = cy; + } + public override string ToString() => $"({Hight},{Width})"; + } + + [ComVisible(true)] + [Serializable] + [DebuggerDisplay("{DebuggerDisplay,nq}")] + [DebuggerTypeProxy(typeof(Point3D))] + public struct Point3D : IEquatable + { + [DebuggerBrowsable(DebuggerBrowsableState.Never)] + private string DebuggerDisplay => $"(X:{X},Y:{Y},Z:{Z})"; + + /* 由于此类是用来优化,从而实现字段修改,因此直接暴露字段减少栈帧 */ + public double X; + public double Y; + public double Z; + + public Point3D(double x, double y, double z) + { + X = x; + Y = y; + Z = z; + } + public static implicit operator Point3D(Point3d pt) + { + return new Point3D(pt.X, pt.Y, pt.Z); + } + public static implicit operator Point3d(Point3D pt) + { + return new Point3d(pt.X, pt.Y, pt.Z); + } + public override string ToString() => $"({X},{Y},{Z})"; + + #region 重载运算符_比较 + public bool Equals(Point3D other) + { + return + X == other.X && + Y == other.Y && + Z == other.Z; + } + public static bool operator !=(Point3D a, Point3D b) + { + return !(a == b); + } + public static bool operator ==(Point3D a, Point3D b) + { + return a.Equals(b); + } + public override bool Equals(object obj) + { + return obj is Point3D d && Equals(d); + } + public override int GetHashCode() + { + return (X, Y).GetHashCode() ^ Z.GetHashCode(); + } + #endregion + } } diff --git a/src/IFoxCAD.Cad.Shared/Runtime/FileOpenMode.cs b/src/IFoxCAD.Cad.Shared/Runtime/FileOpenMode.cs index 992ba2d..ad825f4 100644 --- a/src/IFoxCAD.Cad.Shared/Runtime/FileOpenMode.cs +++ b/src/IFoxCAD.Cad.Shared/Runtime/FileOpenMode.cs @@ -60,9 +60,6 @@ namespace Autodesk.AutoCAD.Internal { public class Utils { - [DllImport("user32.dll")] - static extern IntPtr SetFocus(IntPtr hWnd); - public static void SetFocusToDwgView() { IntPtr window; @@ -83,7 +80,7 @@ public static void SetFocusToDwgView() window = WindowsAPI.GetWindow(msctls_statusbar32, 2U); } if (window != IntPtr.Zero) - SetFocus(window); + WindowsAPI.SetFocus(window); } } } diff --git a/tests/TestAcad09plus/wpf/Class1.cs b/tests/TestAcad09plus/wpf/Cmd.cs similarity index 89% rename from tests/TestAcad09plus/wpf/Class1.cs rename to tests/TestAcad09plus/wpf/Cmd.cs index cafff3c..9f81401 100644 --- a/tests/TestAcad09plus/wpf/Class1.cs +++ b/tests/TestAcad09plus/wpf/Cmd.cs @@ -1,6 +1,6 @@ namespace Test.wpf; -public class Class1 +public class Cmd { [CommandMethod(nameof(Test_WPf))] public void Test_WPf() diff --git "a/tests/TestAcad09plus/\350\276\223\345\205\245\346\263\225\350\207\252\345\212\250\345\210\207\346\215\242/InputBinding/Inputting.cs" "b/tests/TestAcad09plus/\350\276\223\345\205\245\346\263\225\350\207\252\345\212\250\345\210\207\346\215\242/InputBinding/Inputting.cs" index 8301efa..b9278c8 100644 --- "a/tests/TestAcad09plus/\350\276\223\345\205\245\346\263\225\350\207\252\345\212\250\345\210\207\346\215\242/InputBinding/Inputting.cs" +++ "b/tests/TestAcad09plus/\350\276\223\345\205\245\346\263\225\350\207\252\345\212\250\345\210\207\346\215\242/InputBinding/Inputting.cs" @@ -1,8 +1,11 @@ #pragma warning disable 0169 /* - * 福萝卜说他的是用键盘钩子制作的 - * 此处不是键盘钩子,并且08还需要把文档栏工程的子类化拉取过来才可以使用 + * 0x01 赫思是键盘钩子: + * 可以在08.+使用; + * 0x02 InputBinding是切换输入法: + * 目前仅高版本, + * 08需要把文档栏工程的子类化拉取过来才可以使用 */ namespace InputBinding; @@ -62,7 +65,7 @@ public void ModalDialog() } #region NewThread1 - [IFoxInitialize] + //[IFoxInitialize] 需要自动加载时候启动它,但是它和赫思冲突 public void InputBindingInitialize() { InputHelper.LoadConfig(); @@ -490,54 +493,54 @@ public static void PressKey() { if (_iMode3 == -1) Thread.Sleep(400); - keybd_event(16, 0, 0, 0); - keybd_event(16, 0, 2, 0); + KeybdEvent(16, 0, 0, 0); + KeybdEvent(16, 0, 2, 0); } else if (InputVar.CT) { if (_iMode3 == -1) Thread.Sleep(400); - keybd_event(17, 0, 0, 0); - keybd_event(17, 0, 2, 0); + KeybdEvent(17, 0, 0, 0); + KeybdEvent(17, 0, 2, 0); } else if (InputVar.CK) { if (_iMode3 == -1) Thread.Sleep(400); - keybd_event(17, 0, 0, 0); - keybd_event(32, 0, 0, 0); - keybd_event(32, 0, 2, 0); - keybd_event(17, 0, 2, 0); + KeybdEvent(17, 0, 0, 0); + KeybdEvent(32, 0, 0, 0); + KeybdEvent(32, 0, 2, 0); + KeybdEvent(17, 0, 2, 0); if (GetKeyState(20) != 1) return; - keybd_event(20, 0, 0, 0); - keybd_event(20, 0, 2, 0); + KeybdEvent(20, 0, 0, 0); + KeybdEvent(20, 0, 2, 0); } else if (InputVar.CS) { if (_iMode3 == -1) Thread.Sleep(400); - keybd_event(16, 0, 0, 0); - keybd_event(17, 0, 0, 0); - keybd_event(17, 0, 2, 0); - keybd_event(16, 0, 2, 0); + KeybdEvent(16, 0, 0, 0); + KeybdEvent(17, 0, 0, 0); + KeybdEvent(17, 0, 2, 0); + KeybdEvent(16, 0, 2, 0); if (GetKeyState(20) != 1) return; - keybd_event(20, 0, 0, 0); - keybd_event(20, 0, 2, 0); + KeybdEvent(20, 0, 0, 0); + KeybdEvent(20, 0, 2, 0); } else if (InputVar.AS) { if (_iMode3 == -1) Thread.Sleep(400); - keybd_event(91, 0, 0, 0); - keybd_event(32, 0, 0, 0); - keybd_event(32, 0, 2, 0); - keybd_event(91, 0, 2, 0); + KeybdEvent(91, 0, 0, 0); + KeybdEvent(32, 0, 0, 0); + KeybdEvent(32, 0, 2, 0); + KeybdEvent(91, 0, 2, 0); if (GetKeyState(20) != 1) return; - keybd_event(20, 0, 0, 0); - keybd_event(20, 0, 2, 0); + KeybdEvent(20, 0, 0, 0); + KeybdEvent(20, 0, 2, 0); } else { diff --git "a/tests/TestAcad09plus/\350\276\223\345\205\245\346\263\225\350\207\252\345\212\250\345\210\207\346\215\242/\350\265\253\346\200\235\350\276\223\345\205\245\346\263\225/DocReactor.cs" "b/tests/TestAcad09plus/\350\276\223\345\205\245\346\263\225\350\207\252\345\212\250\345\210\207\346\215\242/\350\265\253\346\200\235\350\276\223\345\205\245\346\263\225/DocReactor.cs" index 13a37c8..8586b85 100644 --- "a/tests/TestAcad09plus/\350\276\223\345\205\245\346\263\225\350\207\252\345\212\250\345\210\207\346\215\242/\350\265\253\346\200\235\350\276\223\345\205\245\346\263\225/DocReactor.cs" +++ "b/tests/TestAcad09plus/\350\276\223\345\205\245\346\263\225\350\207\252\345\212\250\345\210\207\346\215\242/\350\265\253\346\200\235\350\276\223\345\205\245\346\263\225/DocReactor.cs" @@ -5,49 +5,29 @@ public class DocReactor internal static void IntialReactor() { var dm = Acap.DocumentManager; - var doc = dm.MdiActiveDocument; - var db = doc.Database; - var ed = doc.Editor; + // 现有文档 + foreach (Document doc in dm) + doc.CommandWillStart += CommandWillStart; + // 文档创建事件 dm.DocumentCreated += DocumentCreated; - - foreach (Document item in dm) - AddReactor(item); } internal static void RemoveReactor() { var dm = Acap.DocumentManager; - var doc = dm.MdiActiveDocument; - var db = doc.Database; - var ed = doc.Editor; - foreach (Document item in dm) - RemoveReactor(item); - + // 现有文档 + foreach (Document doc in dm) + doc.CommandWillStart -= CommandWillStart; + // 文档创建事件 dm.DocumentCreated -= DocumentCreated; } static void DocumentCreated(object sender, DocumentCollectionEventArgs e) { - AddReactor(e.Document); - } - - private static void RemoveReactor(Document doc) - { - if (doc == null) - return; - - doc.CommandWillStart -= CommandWillStart; - } - - private static void AddReactor(Document doc) - { - if (doc == null) - return; - - doc.CommandWillStart += CommandWillStart; + e.Document.CommandWillStart += CommandWillStart; } - private static void CommandWillStart(object sender, CommandEventArgs e) + static void CommandWillStart(object sender, CommandEventArgs e) { if (!Settings.Use || ((Document)sender).Editor.IsQuiescentForTransparentCommand()) return; @@ -56,7 +36,7 @@ private static void CommandWillStart(object sender, CommandEventArgs e) if (gName == "-HATCHEDIT" || gName == "UNDO") return; - //此函数将焦点设置为视图: + // 此函数将焦点设置为视图: Autodesk.AutoCAD.Internal.Utils.SetFocusToDwgView(); } } \ No newline at end of file diff --git "a/tests/TestAcad09plus/\350\276\223\345\205\245\346\263\225\350\207\252\345\212\250\345\210\207\346\215\242/\350\265\253\346\200\235\350\276\223\345\205\245\346\263\225/IMEControl.cs" "b/tests/TestAcad09plus/\350\276\223\345\205\245\346\263\225\350\207\252\345\212\250\345\210\207\346\215\242/\350\265\253\346\200\235\350\276\223\345\205\245\346\263\225/IMEControl.cs" index 2c44366..e3924f3 100644 --- "a/tests/TestAcad09plus/\350\276\223\345\205\245\346\263\225\350\207\252\345\212\250\345\210\207\346\215\242/\350\265\253\346\200\235\350\276\223\345\205\245\346\263\225/IMEControl.cs" +++ "b/tests/TestAcad09plus/\350\276\223\345\205\245\346\263\225\350\207\252\345\212\250\345\210\207\346\215\242/\350\265\253\346\200\235\350\276\223\345\205\245\346\263\225/IMEControl.cs" @@ -135,7 +135,17 @@ internal static void SetIMEHook() if (Settings.IMEStyle != Settings.IMEStyleS.Global) { - HookProc = MyKeyboardProc; + HookProc = (int nCode, int wParam, int lParam) => { + if (nCode >= 0) + { + bool flag = !(lParam > 0 & (lParam & -1073741823) == 1) || + (Control.ModifierKeys != Keys.None && Control.ModifierKeys != Keys.Shift) || + !IMEHook(nCode, wParam, lParam); + if (!flag) + return (IntPtr)1; + } + return (IntPtr)WindowsAPI.CallNextHookEx(hnexthookproc, nCode, wParam, lParam); + }; hnexthookproc = WindowsAPI.SetWindowsHookEx(WindowsAPI.HookType.WH_KEYBOARD, HookProc, 0, WindowsAPI.GetCurrentThreadId()); return; } @@ -144,119 +154,69 @@ internal static void SetIMEHook() if (Marshal.SizeOf(typeof(IntPtr)) == 4) { HookProcX86 = (nCode, wParam, lParam) => { - return (IntPtr)MyKeyboardProcX86(nCode, wParam, lParam); + if (!MK1(nCode, wParam) && Mk2(nCode, wParam, new IntPtr(lParam))) + return (IntPtr)1; + return (IntPtr)WindowsAPI.CallNextHookEx(hnexthookproc, nCode, wParam, lParam); }; hnexthookproc = WindowsAPI.SetWindowsHookEx(WindowsAPI.HookType.WH_KEYBOARD_LL, HookProcX86, moduleHandle, 0); } else { HookProcX64 = (nCode, wParam, lParam) => { - return (IntPtr)MyKeyboardProcX64(nCode, wParam, lParam); + if (!MK1(nCode, wParam) && Mk2(nCode, wParam, new IntPtr(lParam))) + return (IntPtr)1; + return (IntPtr)WindowsAPI.CallNextHookEx(hnexthookproc, nCode, wParam, lParam); }; hnexthookproc = WindowsAPI.SetWindowsHookEx(WindowsAPI.HookType.WH_KEYBOARD_LL, HookProcX64, moduleHandle, 0); } } - public static IntPtr MyKeyboardProc(int nCode, int wParam, int lParam) + static bool MK1(int nCode, int wParam) { - if (nCode >= 0) - { - bool flag = !(lParam > 0 & (lParam & -1073741823) == 1) || - (Control.ModifierKeys != Keys.None && Control.ModifierKeys != Keys.Shift) || - !IMEHook(nCode, wParam, lParam); - if (!flag) - return (IntPtr)1; - } - return (IntPtr)WindowsAPI.CallNextHookEx(hnexthookproc, nCode, wParam, lParam); + return !Settings.Use || + WindowsAPI.IsIconic(Acap.MainWindow.Handle.ToInt32()) || + WindowsAPI.GetKeyState(91) < 0 || + WindowsAPI.GetKeyState(92) < 0 || + wParam != WM_KEYDOWN || + nCode < 0; } - static int MyKeyboardProcX64(int nCode, int wParam, long lParam) + static bool Mk2(int nCode, int wParam, IntPtr lParam) { - if (!Settings.Use || - WindowsAPI.IsIconic(Acap.MainWindow.Handle.ToInt32()) || - WindowsAPI.GetKeyState(91) < 0 || - WindowsAPI.GetKeyState(92) < 0 || - wParam != WM_KEYDOWN || - nCode < 0) - return WindowsAPI.CallNextHookEx(hnexthookproc, nCode, wParam, lParam); + IntPtr focus; + if (Marshal.SizeOf(typeof(IntPtr)) == 4) + focus = WindowsAPI.GetFocus(); + else + focus = WindowsAPI.GetForegroundWindow(); - IFoxCAD.Cad.WindowsAPI.GetWindowThreadProcessId(WindowsAPI.GetForegroundWindow(), out uint lpdwProcessId); + WindowsAPI.GetWindowThreadProcessId(focus, out uint lpdwProcessId); if (lpdwProcessId != AcadPID) - return WindowsAPI.CallNextHookEx(hnexthookproc, nCode, wParam, lParam); + return false; - var keyb = KeyboardHookStruct.Create(new IntPtr(lParam)); - switch (Control.ModifierKeys) + if (Control.ModifierKeys == Keys.None) { - case Keys.None: - { - if (WindowsAPI.GetKeyState(162) < 0 || - WindowsAPI.GetKeyState(163) < 0 || - WindowsAPI.GetKeyState(17) < 0 || - WindowsAPI.GetKeyState(262144) < 0) - return WindowsAPI.CallNextHookEx(hnexthookproc, nCode, wParam, lParam); - - if (IMEHook(nCode, keyb.vkCode, 0)) - return 1; - break; - } - case Keys.Shift: - { - if (IMEHook(nCode, keyb.vkCode, 0)) - return 1; - break; - } + var hook = KeyboardHookStruct.Create(lParam); + if (WindowsAPI.GetKeyState(162) < 0 || WindowsAPI.GetKeyState(163) < 0 || + WindowsAPI.GetKeyState(17) < 0 || WindowsAPI.GetKeyState(262144) < 0) + return false; + if (IMEHook(nCode, hook.vkCode, 0)) + return true; } - return WindowsAPI.CallNextHookEx(hnexthookproc, nCode, wParam, lParam); - } - - static int MyKeyboardProcX86(int nCode, int wParam, int lParam) - { - if (!Settings.Use || - WindowsAPI.IsIconic(Acap.MainWindow.Handle.ToInt32()) || - WindowsAPI.GetKeyState(91) < 0 || - WindowsAPI.GetKeyState(92) < 0 || - wParam != WM_KEYDOWN || - nCode < 0) - return WindowsAPI.CallNextHookEx(hnexthookproc, nCode, wParam, lParam); - - IFoxCAD.Cad.WindowsAPI.GetWindowThreadProcessId(WindowsAPI.GetFocus(), out uint lpdwProcessId); - if (lpdwProcessId != AcadPID) - return WindowsAPI.CallNextHookEx(hnexthookproc, nCode, wParam, lParam); - - var keyb = KeyboardHookStruct.Create(new IntPtr(lParam)); - switch (Control.ModifierKeys) + if (Control.ModifierKeys == Keys.Shift) { - case Keys.None: - { - if (WindowsAPI.GetKeyState(162) < 0 || - WindowsAPI.GetKeyState(163) < 0 || - WindowsAPI.GetKeyState(17) < 0 || - WindowsAPI.GetKeyState(262144) < 0) - return WindowsAPI.CallNextHookEx(hnexthookproc, nCode, wParam, lParam); - - if (IMEHook(nCode, keyb.vkCode, 0)) - return 1; - break; - } - case Keys.Shift: - { - if (IMEHook(nCode, keyb.vkCode, 0)) - return 1; - break; - } + var hook = KeyboardHookStruct.Create(lParam); + if (IMEHook(nCode, hook.vkCode, 0)) + return true; } - return WindowsAPI.CallNextHookEx(hnexthookproc, nCode, wParam, lParam); + return false; } static void CheckLowLevelHooksTimeout() { const string llh = "LowLevelHooksTimeout"; - - RegistryKey hkml = Registry.CurrentUser; - RegistryKey registryKey = hkml.OpenSubKey("Control Panel\\Desktop", true); + using var registryKey = Registry.CurrentUser.OpenSubKey("Control Panel\\Desktop", true); if ((int)registryKey.GetValue(llh, 0) == 0) registryKey.SetValue(llh, 25000, RegistryValueKind.DWord); - registryKey.Close(); } public struct KeyboardHookStruct diff --git "a/tests/TestAcad09plus/\350\276\223\345\205\245\346\263\225\350\207\252\345\212\250\345\210\207\346\215\242/\350\265\253\346\200\235\350\276\223\345\205\245\346\263\225/Settings.cs" "b/tests/TestAcad09plus/\350\276\223\345\205\245\346\263\225\350\207\252\345\212\250\345\210\207\346\215\242/\350\265\253\346\200\235\350\276\223\345\205\245\346\263\225/Settings.cs" index 506bfdd..3312625 100644 --- "a/tests/TestAcad09plus/\350\276\223\345\205\245\346\263\225\350\207\252\345\212\250\345\210\207\346\215\242/\350\265\253\346\200\235\350\276\223\345\205\245\346\263\225/Settings.cs" +++ "b/tests/TestAcad09plus/\350\276\223\345\205\245\346\263\225\350\207\252\345\212\250\345\210\207\346\215\242/\350\265\253\346\200\235\350\276\223\345\205\245\346\263\225/Settings.cs" @@ -73,34 +73,28 @@ public static void LoadSettings() if (!File.Exists(MySettingsPath)) return; - var xmlReader = XmlReader.Create(MySettingsPath); + using var xmlReader = XmlReader.Create(MySettingsPath); while (xmlReader.Read()) { if (xmlReader.NodeType != XmlNodeType.Element) continue; - string left = xmlReader.Name.ToLower(); - if (left != "use") + switch (left) { - if (left != "userfilter") - { - if (left == "imestyle") - { - int.TryParse(xmlReader.ReadInnerXml(), out int imes); - _IMEStyle = (IMEStyleS)imes; - } - } - else + case "use": + bool.TryParse(xmlReader.ReadInnerXml(), out _Use); + break; + case "userfilter": + _UserFilter = xmlReader.ReadInnerXml().ToUpper(); + break; + case "imestyle": { - _UserFilter = xmlReader.ReadInnerXml().ToUpper(); + int.TryParse(xmlReader.ReadInnerXml(), out int imes); + _IMEStyle = (IMEStyleS)imes; } - } - else - { - bool.TryParse(xmlReader.ReadInnerXml(), out _Use); + break; } } - xmlReader.Close(); } internal static void SaveSettings() diff --git "a/tests/TestAcad09plus/\350\276\223\345\205\245\346\263\225\350\207\252\345\212\250\345\210\207\346\215\242/\350\265\253\346\200\235\350\276\223\345\205\245\346\263\225/ShowWPFWindowCentered.cs" "b/tests/TestAcad09plus/\350\276\223\345\205\245\346\263\225\350\207\252\345\212\250\345\210\207\346\215\242/\350\265\253\346\200\235\350\276\223\345\205\245\346\263\225/ShowWPFWindowCentered.cs" index 38b6cc8..1599036 100644 --- "a/tests/TestAcad09plus/\350\276\223\345\205\245\346\263\225\350\207\252\345\212\250\345\210\207\346\215\242/\350\265\253\346\200\235\350\276\223\345\205\245\346\263\225/ShowWPFWindowCentered.cs" +++ "b/tests/TestAcad09plus/\350\276\223\345\205\245\346\263\225\350\207\252\345\212\250\345\210\207\346\215\242/\350\265\253\346\200\235\350\276\223\345\205\245\346\263\225/ShowWPFWindowCentered.cs" @@ -8,7 +8,7 @@ public class ShowWPFWindowCentered internal static bool Show(Window window, bool modal) { new WindowInteropHelper(window).Owner = Acap.MainWindow.Handle; - WindowsAPI.RECT lpRect = new(); + WindowsAPI.IntRect lpRect = new(); WindowsAPI.GetWindowRect(Acap.MainWindow.Handle, ref lpRect); var dpi = DPI.CurrentDPI(); diff --git "a/tests/TestAcad09plus/\350\276\223\345\205\245\346\263\225\350\207\252\345\212\250\345\210\207\346\215\242/\350\265\253\346\200\235\350\276\223\345\205\245\346\263\225/WindowsAPI.cs" "b/tests/TestAcad09plus/\350\276\223\345\205\245\346\263\225\350\207\252\345\212\250\345\210\207\346\215\242/\350\265\253\346\200\235\350\276\223\345\205\245\346\263\225/WindowsAPI.cs" deleted file mode 100644 index 842d4b7..0000000 --- "a/tests/TestAcad09plus/\350\276\223\345\205\245\346\263\225\350\207\252\345\212\250\345\210\207\346\215\242/\350\265\253\346\200\235\350\276\223\345\205\245\346\263\225/WindowsAPI.cs" +++ /dev/null @@ -1,96 +0,0 @@ -namespace Gstar_IMEFilter; - -public class WindowsAPI -{ - [DllImport("user32.dll", SetLastError = true)] - internal static extern IntPtr GetFocus(); - - [DllImport("user32.dll")] - internal static extern int GetClassName(IntPtr hwnd, StringBuilder lpClassName, int nMaxCount); - - [DllImport("user32.dll ")] - internal static extern IntPtr SendMessage(IntPtr hwnd, int msg, IntPtr wParam, IntPtr lParam); - - [DllImport("user32.dll ")] - internal static extern IntPtr PostMessage(IntPtr hwnd, int msg, IntPtr wParam, IntPtr lParam); - - [DllImport("user32.dll", SetLastError = true)] - internal static extern IntPtr GetParent(IntPtr hWnd); - - [DllImport("kernel32.dll")] - internal static extern long GetHandleInformation(long hObject, ref long lpdwFlags); - - [DllImport("user32.dll", CharSet = CharSet.Unicode, SetLastError = true)] - internal static extern int GetWindowText(IntPtr hWnd, StringBuilder lpString, int nMaxCount); - - [DllImport("user32.dll")] - internal static extern int GetWindowTextLength(IntPtr hWnd); - - [DllImport("kernel32.dll")] - internal static extern int GetCurrentThreadId(); - - [DllImport("user32.dll")] - internal static extern IntPtr SetWindowsHookEx(HookType idHook, CallBackX86 lpfn, long hmod, int dwThreadId); - - [DllImport("user32.dll")] - public static extern IntPtr SetWindowsHookEx(HookType idHook, CallBack lpfn, int hmod, int dwThreadId); - - [DllImport("user32.dll")] - internal static extern IntPtr SetWindowsHookEx(HookType idHook, CallBackX64 lpfn, long hmod, int dwThreadId); - - [DllImport("user32.dll")] - internal static extern int UnhookWindowsHookEx(IntPtr hHook); - - [DllImport("user32.dll")] - internal static extern int CallNextHookEx(IntPtr hHook, int ncode, int wParam, long lParam); - - [DllImport("user32.dll")] - internal static extern int CallNextHookEx(int hHook, int ncode, int wParam, int lParam); - - [DllImport("kernel32.dll")] - internal static extern IntPtr GetModuleHandle(string ModuleName); - - [DllImport("user32.dll")] - internal static extern int ToAscii(int uVirtKey, int uScancode, byte[] lpdKeyState, byte[] lpwTransKey, int fuState); - - [DllImport("user32.dll", SetLastError = true)] - internal static extern IntPtr GetForegroundWindow(); - - [DllImport("user32.dll", SetLastError = true)] - internal static extern IntPtr GetActiveWindow(); - - [DllImport("user32.dll", SetLastError = true)] - internal static extern long GetWindowThreadProcessId(IntPtr hwnd, ref int lpdwProcessId); - - [DllImport("user32.dll", SetLastError = true)] - internal static extern short GetKeyState(int nVirtKey); - - [DllImport("user32.dll", SetLastError = true)] - internal static extern bool IsIconic(int hWnd); - - [DllImport("user32.dll")] - internal static extern bool IsWindowEnabled(IntPtr hWnd); - - [DllImport("user32.dll")] - internal static extern bool GetWindowRect(IntPtr hwnd, ref RECT lpRect); - - public delegate IntPtr CallBackX86(int nCode, int wParam, int lParam); - public delegate IntPtr CallBackX64(int nCode, int wParam, long lParam); - public delegate IntPtr CallBack(int nCode, int wParam, int lParam); - public enum HookType - { - WH_KEYBOARD = 2, - WH_KEYBOARD_LL = 13, - WH_MOUSE_LL = 14, - } - - public struct RECT - { - public int Left; - public int Top; - public int Right; - public int Bottom; - public int Width => checked(Right - Left); - public int Height => checked(Bottom - Top); - } -} \ No newline at end of file diff --git a/tests/TestShared/TestMarshal.cs b/tests/TestShared/TestMarshal.cs index 0bca7fa..963b802 100644 --- a/tests/TestShared/TestMarshal.cs +++ b/tests/TestShared/TestMarshal.cs @@ -1,4 +1,5 @@ using System.Diagnostics; +using static IFoxCAD.Cad.WindowsAPI; namespace TestShared; -- Gitee From cedafcab302b37ec0732e14c972491d40b80b550 Mon Sep 17 00:00:00 2001 From: liuqihong <540762622@qq.com> Date: Fri, 28 Oct 2022 01:04:44 +0800 Subject: [PATCH 582/675] =?UTF-8?q?=E5=A2=9E=E5=8A=A0ForEach=E9=81=8D?= =?UTF-8?q?=E5=8E=86=E5=9D=97=E5=86=85?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../ExtensionMethod/CollectionEx.cs | 33 +++++----- .../ExtensionMethod/EntityEx.cs | 61 +++++++++++++++++++ .../ExtensionMethod/SymbolTableRecordEx.cs | 17 +++--- tests/TestShared/Copyclip.cs | 45 +++----------- 4 files changed, 98 insertions(+), 58 deletions(-) diff --git a/src/IFoxCAD.Cad.Shared/ExtensionMethod/CollectionEx.cs b/src/IFoxCAD.Cad.Shared/ExtensionMethod/CollectionEx.cs index 36ec072..acd120a 100644 --- a/src/IFoxCAD.Cad.Shared/ExtensionMethod/CollectionEx.cs +++ b/src/IFoxCAD.Cad.Shared/ExtensionMethod/CollectionEx.cs @@ -1,5 +1,4 @@ using System.ComponentModel; -using System.Diagnostics; using System.Xml.Linq; using static System.Windows.Forms.AxHost; @@ -87,14 +86,17 @@ public static List ToList(this ObjectIdCollection ids) /// 集合值的类型 /// 集合 /// 委托 - //[DebuggerHidden] - [DebuggerStepThrough] + [System.Diagnostics.DebuggerStepThrough] //[DebuggerHidden] 两个特性差不多 public static void ForEach(this IEnumerable source, Action action) { - // 这里不要嵌套调用ForEach,因为断点设置的原因 - //source.ForEach((a, _, _) => { - // action.Invoke(a);//由于此处是委托,所以 DebuggerStepThrough 特性会进入,改用预处理方式避免 - //}); + // 这里不要嵌套调用ForEach委托, + // 因为这样可以在调用函数上断点直接跑Action内,不会进入此处(除了cad之外); + // 而cad很奇怪,只能用预处理方式避免 + // 嵌套调用ForEach委托: + // source.ForEach((a, _, _) => { + // action.Invoke(a); + // }); + foreach (var element in source) action.Invoke(element); } @@ -105,13 +107,16 @@ public static void ForEach(this IEnumerable source, Action action) /// 集合值的类型 /// 集合 /// 委托 - [DebuggerStepThrough] + [System.Diagnostics.DebuggerStepThrough] public static void ForEach(this IEnumerable source, Action action) { - // 这里不要嵌套调用ForEach,因为断点设置的原因 - //source.ForEach((a, b, _) => { - // action.Invoke(a, b); - //}); + // 这里不要嵌套调用ForEach委托, + // 因为这样可以在调用函数上断点直接跑Action内,不会进入此处(除了cad之外); + // 而cad很奇怪,只能用预处理方式避免 + // 嵌套调用ForEach委托: + // source.ForEach((a, b, _) => { + // action.Invoke(a, b); + // }); LoopState state = new();/*这种方式比Action改Func更友好*/ foreach (var element in source) @@ -128,11 +133,11 @@ public static void ForEach(this IEnumerable source, Action a /// 集合值的类型 /// 集合 /// 委托 - [DebuggerStepThrough] + [System.Diagnostics.DebuggerStepThrough] public static void ForEach(this IEnumerable source, Action action) { - LoopState state = new();/*这种方式比Action改Func更友好*/ int i = 0; + LoopState state = new();/*这种方式比Action改Func更友好*/ foreach (var element in source) { action.Invoke(element, state, i); diff --git a/src/IFoxCAD.Cad.Shared/ExtensionMethod/EntityEx.cs b/src/IFoxCAD.Cad.Shared/ExtensionMethod/EntityEx.cs index 5225349..d839ba8 100644 --- a/src/IFoxCAD.Cad.Shared/ExtensionMethod/EntityEx.cs +++ b/src/IFoxCAD.Cad.Shared/ExtensionMethod/EntityEx.cs @@ -409,5 +409,66 @@ public static BoundingInfo GetBoundingBoxEx(this Entity ent) { return EntityBoundingInfo.GetBoundingInfo(ent); } + +#line hidden + /// + /// 遍历块内 + /// + /// + /// + /// + /// + [System.Diagnostics.DebuggerStepThrough] + public static void ForEach(this BlockReference brf, Action action, DBTrans? tr = null) + { + if (action == null) + throw new ArgumentNullException(nameof(action)); + + tr ??= DBTrans.Top; + var btr = tr.GetObject(brf.BlockTableRecord); + if (btr == null) + return; + btr.ForEach(action); + } + /// + /// 遍历块内 + /// + /// + /// + /// + /// + [System.Diagnostics.DebuggerStepThrough] + public static void ForEach(this BlockReference brf, Action action, DBTrans? tr = null) + { + if (action == null) + throw new ArgumentNullException(nameof(action)); + + tr ??= DBTrans.Top; + var btr = tr.GetObject(brf.BlockTableRecord); + if (btr == null) + return; + btr.ForEach(action); + } + /// + /// 遍历块内 + /// + /// + /// + /// + /// + [System.Diagnostics.DebuggerStepThrough] + public static void ForEach(this BlockReference brf, Action action, DBTrans? tr = null) + { + if (action == null) + throw new ArgumentNullException(nameof(action)); + + tr ??= DBTrans.Top; + var btr = tr.GetObject(brf.BlockTableRecord); + if (btr == null) + return; + btr.ForEach(action); + } +#line default + #endregion } \ No newline at end of file diff --git a/src/IFoxCAD.Cad.Shared/ExtensionMethod/SymbolTableRecordEx.cs b/src/IFoxCAD.Cad.Shared/ExtensionMethod/SymbolTableRecordEx.cs index c4eb33a..d895a40 100644 --- a/src/IFoxCAD.Cad.Shared/ExtensionMethod/SymbolTableRecordEx.cs +++ b/src/IFoxCAD.Cad.Shared/ExtensionMethod/SymbolTableRecordEx.cs @@ -477,9 +477,8 @@ public static ObjectId InsertBlock(this BlockTableRecord blockTableRecord, public static void ForEach(this TRecord record, Action task) where TRecord : SymbolTableRecord, IEnumerable { - ForEach(record, (a, _, _) => { - task.Invoke(a);//由于此处是委托,所以 DebuggerStepThrough 特性会进入,改用预处理方式避免 - }); + foreach (ObjectId id in record) + task.Invoke(id); } /// @@ -489,9 +488,13 @@ public static void ForEach(this TRecord record, Action task) public static void ForEach(this TRecord record, Action task) where TRecord : SymbolTableRecord, IEnumerable { - ForEach(record, (a, b, _) => { - task.Invoke(a, b); - }); + LoopState state = new();/*这种方式比Action改Func更友好*/ + foreach (ObjectId id in record) + { + task.Invoke(id, state); + if (!state.IsRun) + break; + } } /// @@ -505,8 +508,8 @@ public static void ForEach(this TRecord record, Action= 27) fileTr.SaveFile((DwgVersion)27, false); + else + throw new ArgumentException($"版本过低,无法保存,版本号:{DwgVersion.Current}"); } #endregion @@ -394,8 +391,7 @@ void Paste(bool isBlock) // 获取临时文件的图元id var fileEntityIds = new List(); - using (DBTrans fileTr = new(cadClipType.File, - commit: false, + using (DBTrans fileTr = new(cadClipType.File, commit: false, fileOpenMode: FileOpenMode.OpenForReadAndAllShare)) { fileTr.ModelSpace.ForEach(id => { @@ -678,31 +674,6 @@ static string CreateTempFileName(string format = "X") } } -//public static class BlockReferenceHelper -//{ -// /// -// /// 遍历块内 -// /// -// /// -// /// -// /// -// /// -// public static void ForEach(this BlockReference brf, Action action, DBTrans? tr = null) -// { -// if (action == null) -// throw new ArgumentNullException(nameof(action)); - -// trans ??= DBTrans.Top; - -// var btr = tr.GetObject(brf.BlockTableRecord); -// if (btr == null) -// return; -// foreach (var id in btr) -// action.Invoke(id); -// } -//} - - #if !ac2008 public class TestImageFormat { -- Gitee From 6681c9c88203193956c5f007ec26de9829e53032 Mon Sep 17 00:00:00 2001 From: liuqihong <540762622@qq.com> Date: Fri, 28 Oct 2022 01:09:20 +0800 Subject: [PATCH 583/675] =?UTF-8?q?=E4=BC=98=E5=8C=96=E4=BB=A3=E7=A0=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/IFoxCAD.Cad.Shared/Copyclip/WindowsAPI.cs | 167 ++++++++---------- .../ExtensionMethod/EntityBoundingInfo.cs | 9 +- tests/TestShared/Copyclip.cs | 6 +- tests/TestShared/TestBlock.cs | 33 ++-- 4 files changed, 98 insertions(+), 117 deletions(-) diff --git a/src/IFoxCAD.Cad.Shared/Copyclip/WindowsAPI.cs b/src/IFoxCAD.Cad.Shared/Copyclip/WindowsAPI.cs index 32e7f55..301257a 100644 --- a/src/IFoxCAD.Cad.Shared/Copyclip/WindowsAPI.cs +++ b/src/IFoxCAD.Cad.Shared/Copyclip/WindowsAPI.cs @@ -7,29 +7,28 @@ namespace IFoxCAD.Cad; public partial class WindowsAPI { + #region kernel32 // https://blog.csdn.net/haelang/article/details/45147121 [DllImport("kernel32.dll")] public extern static uint GetLastError(); - /// - /// 获取窗口客户区的大小,客户区为窗口中除标题栏,菜单栏之外的地方 - /// - /// - /// - /// - [DllImport("user32.dll", CharSet = CharSet.Auto, EntryPoint = "GetClientRect")] - public static extern bool GetClientRect(IntPtr hwnd, out IntRect lpRect); + [DllImport("kernel32.dll")] + public static extern long GetHandleInformation(long hObject, ref long lpdwFlags); + + [DllImport("kernel32.dll")] + public static extern IntPtr GetModuleHandle(string ModuleName); + + [DllImport("kernel32.dll")] + public static extern int GetCurrentThreadId(); /// - /// 查找主线程
    - /// 代替
    - /// 托管线程和他们不一样: + /// 获取要引入的函数,将符号名或标识号转换为DLL内部地址 ///
    - /// 主窗口 - /// 进程ID - /// 线程ID - [DllImport("user32.dll", SetLastError = true)] - public static extern uint GetWindowThreadProcessId(IntPtr hWnd, out uint lpdwProcessId); + /// exe/dll句柄 + /// 接口名 + /// + [DllImport("kernel32.dll", SetLastError = true, CharSet = CharSet.Ansi, ExactSpelling = true)] + public static extern IntPtr GetProcAddress(IntPtr hModule, string procName); /// /// 锁定内存 @@ -104,8 +103,6 @@ public static bool GlobalLockTask(IntPtr data, Action task) return true; } - - /// /// byte数组转结构体 /// @@ -197,7 +194,11 @@ static void ToPtr(object? structObj, Action? task, IntPtr newPtr) } } + #endregion +} +public partial class WindowsAPI +{ #region imm32 /// /// 获取输入法的虚拟键码 @@ -239,8 +240,40 @@ static void ToPtr(object? structObj, Action? task, IntPtr newPtr) [DllImport("imm32.dll")] public static extern bool ImmGetOpenStatus(IntPtr hwnd); #endregion +} +public partial class WindowsAPI +{ #region user32 + + /// + /// 获取窗口客户区的大小,客户区为窗口中除标题栏,菜单栏之外的地方 + /// + /// + /// + /// + [DllImport("user32.dll", CharSet = CharSet.Auto, EntryPoint = "GetClientRect")] + public static extern bool GetClientRect(IntPtr hwnd, out IntRect lpRect); + + /// + /// 查找主线程
    + /// 代替
    + /// 托管线程和他们不一样: + ///
    + /// 主窗口 + /// 进程ID + /// 线程ID + [DllImport("user32.dll", SetLastError = true)] + public static extern uint GetWindowThreadProcessId(IntPtr hWnd, out uint lpdwProcessId); + + /// + /// 焦点 + /// + /// + /// + [DllImport("user32.dll")] + public static extern IntPtr SetFocus(IntPtr hWnd); + /// /// 获取当前窗口 /// @@ -283,6 +316,10 @@ static void ToPtr(object? structObj, Action? task, IntPtr newPtr) [DllImport("User32.dll", CharSet = CharSet.Auto)] public static extern int GetWindowText(IntPtr hWnd, StringBuilder text, int nMaxCount); + // [DllImport("user32.dll", CharSet = CharSet.Unicode, SetLastError = true)] + // internal static extern int GetWindowText(IntPtr hWnd, StringBuilder lpString, int nMaxCount); + + /// /// 获取某个线程的输入法布局 /// @@ -351,21 +388,6 @@ public static GuiThreadInfo Create(uint windowThreadProcessId) return gti; } } - #endregion - - [DllImport("kernel32.dll")] - public static extern int GetCurrentThreadId(); - - [DllImport("user32.dll")] - public static extern IntPtr SetFocus(IntPtr hWnd); -} - - - -public partial class WindowsAPI -{ - // [DllImport("user32.dll", CharSet = CharSet.Unicode, SetLastError = true)] - // internal static extern int GetWindowText(IntPtr hWnd, StringBuilder lpString, int nMaxCount); [DllImport("user32.dll", SetLastError = true)] public static extern IntPtr GetFocus(); @@ -376,9 +398,23 @@ public partial class WindowsAPI [DllImport("user32.dll", SetLastError = true)] public static extern IntPtr GetParent(IntPtr hWnd); - [DllImport("kernel32.dll")] - public static extern long GetHandleInformation(long hObject, ref long lpdwFlags); + [DllImport("user32.dll")] + public static extern int ToAscii(int uVirtKey, int uScancode, byte[] lpdKeyState, byte[] lpwTransKey, int fuState); + + [DllImport("user32.dll", SetLastError = true)] + public static extern IntPtr GetActiveWindow(); + + [DllImport("user32.dll", SetLastError = true)] + public static extern long GetWindowThreadProcessId(IntPtr hwnd, ref int lpdwProcessId); + + [DllImport("user32.dll", SetLastError = true)] + public static extern bool IsIconic(int hWnd); + [DllImport("user32.dll")] + public static extern bool IsWindowEnabled(IntPtr hWnd); + #endregion + + #region 钩子 [DllImport("user32.dll")] public static extern IntPtr SetWindowsHookEx(HookType idHook, CallBackX86 lpfn, long hmod, int dwThreadId); @@ -397,24 +433,6 @@ public partial class WindowsAPI [DllImport("user32.dll")] public static extern int CallNextHookEx(int hHook, int ncode, int wParam, int lParam); - [DllImport("kernel32.dll")] - public static extern IntPtr GetModuleHandle(string ModuleName); - - [DllImport("user32.dll")] - public static extern int ToAscii(int uVirtKey, int uScancode, byte[] lpdKeyState, byte[] lpwTransKey, int fuState); - - [DllImport("user32.dll", SetLastError = true)] - public static extern IntPtr GetActiveWindow(); - - [DllImport("user32.dll", SetLastError = true)] - public static extern long GetWindowThreadProcessId(IntPtr hwnd, ref int lpdwProcessId); - - [DllImport("user32.dll", SetLastError = true)] - public static extern bool IsIconic(int hWnd); - - [DllImport("user32.dll")] - public static extern bool IsWindowEnabled(IntPtr hWnd); - public delegate IntPtr CallBackX86(int nCode, int wParam, int lParam); public delegate IntPtr CallBackX64(int nCode, int wParam, long lParam); public delegate IntPtr CallBack(int nCode, int wParam, int lParam); @@ -424,6 +442,7 @@ public enum HookType WH_KEYBOARD_LL = 13, WH_MOUSE_LL = 14, } + #endregion [DllImport("user32.dll")] public static extern bool GetWindowRect(IntPtr hwnd, ref IntRect lpRect); @@ -575,44 +594,4 @@ public override int GetHashCode() } #endregion } -} - - -#if true2 -// arx剪贴板头文件的枚举 -enum eClipInfoFlags -{ - kbDragGeometry = 0x01, -}; - -enum eXrefType -{ - kXrefTypeAttach = 1, - kXrefTypeOverlay = 2 -}; - -enum eExpandedClipDataTypes -{ - kDcPlotStyles = 1, - kDcXrefs = 2, - kDcLayouts = 3, - kDcBlocks = 4, - kDcLayers = 5, - kDcDrawings = 6, - kDcLinetypes = 7, - kDcTextStyles = 8, - kDcDimStyles = 9, - kDcBlocksWithAttdef = 10, - //#ifdef ADCHATCH - kDcHatches = 11, - //#endif - kTpXrefs = 12, - kTpImages = 13, - kTpTable = 14, - kDcTableStyles = 15, - kDcMultileaderStyles = 16, - kDcVisualStyles = 17, - kDcSectionViewStyles = 18, - kDcDetailViewStyles = 19, -}; -#endif \ No newline at end of file +} \ No newline at end of file diff --git a/src/IFoxCAD.Cad.Shared/ExtensionMethod/EntityBoundingInfo.cs b/src/IFoxCAD.Cad.Shared/ExtensionMethod/EntityBoundingInfo.cs index 99b9708..baf2fc4 100644 --- a/src/IFoxCAD.Cad.Shared/ExtensionMethod/EntityBoundingInfo.cs +++ b/src/IFoxCAD.Cad.Shared/ExtensionMethod/EntityBoundingInfo.cs @@ -172,17 +172,18 @@ public static BoundingInfo GetBoundingInfo(Entity ent) // 错误类型处理 if (ent is AttributeDefinition) // 属性定义 return new(Point3d.Origin, Point3d.Origin, false); - else if (ent is Xline xline)// 参照线 + if (ent is Xline xline)// 参照线 return new(xline.BasePoint, xline.BasePoint, true); - else if (ent is Ray ray)// 射线 + if (ent is Ray ray)// 射线 return new(ray.BasePoint, ray.BasePoint, true); #endif // 指定类型处理 if (ent is BlockReference brf) return GetBoxInfoInBlockReference(brf); - else if (ent is MText mText) + if (ent is MText mText) return GetBoxInfoInMText(mText); - else if (!_typeNames.Contains(ent.GetType().Name)) // 屏蔽天正等等缺失包围盒的类型 + + if (!_typeNames.Contains(ent.GetType().Name)) // 屏蔽天正等等缺失包围盒的类型 try { return new(ent.GeometricExtents.MinPoint, ent.GeometricExtents.MaxPoint, true); diff --git a/tests/TestShared/Copyclip.cs b/tests/TestShared/Copyclip.cs index cc88f35..a997f19 100644 --- a/tests/TestShared/Copyclip.cs +++ b/tests/TestShared/Copyclip.cs @@ -442,9 +442,9 @@ void Paste(bool isBlock) // 预览并获取交互点 // 天正此处可能存在失败:天正图元不给你jig接口调用之类的 using var moveJig = new JigEx((mousePoint, drawEntitys) => { - var blockref = new BlockReference(Point3d.Origin, btr.ObjectId); - blockref.Move(Point3d.Origin, mousePoint); - drawEntitys.Enqueue(blockref); + var brf = new BlockReference(Point3d.Origin, btr.ObjectId); + brf.Move(Point3d.Origin, mousePoint); + drawEntitys.Enqueue(brf); }); var jppo = moveJig.SetOptions(cadClipType.Point); jppo.Keywords.Add(" ", " ", "<空格取消>"); diff --git a/tests/TestShared/TestBlock.cs b/tests/TestShared/TestBlock.cs index 8e24532..39ad9aa 100644 --- a/tests/TestShared/TestBlock.cs +++ b/tests/TestShared/TestBlock.cs @@ -160,11 +160,12 @@ public void Test_AddAttsDef() { using DBTrans tr = new(); var blockid = Env.Editor.GetEntity("pick block:").ObjectId; - var blockref = tr.GetObject(blockid)!.BlockTableRecord; - + var btf = tr.GetObject(blockid); + if (btf is null) + return; var att1 = new AttributeDefinition() { Position = new Point3d(20, 20, 0), Tag = "addtagTest1", Height = 1, TextString = "valueTest1" }; var att2 = new AttributeDefinition() { Position = new Point3d(10, 12, 0), Tag = "tagTest2", Height = 1, TextString = "valueTest2" }; - tr.BlockTable.AddAttsToBlocks(blockref, new() { att1, att2 }); + tr.BlockTable.AddAttsToBlocks(btf.BlockTableRecord, new() { att1, att2 }); } [CommandMethod(nameof(Test_BlockNullBug))] @@ -201,13 +202,13 @@ public void Test_ClipBlock() }); // tr.BlockTable.Add("hah"); var id = tr.CurrentSpace.InsertBlock(new Point3d(0, 0, 0), "test1"); - var bref = tr.GetObject(id)!; + var brf1 = tr.GetObject(id)!; var pts = new List { new Point3d(3, 3, 0), new Point3d(7, 3, 0), new Point3d(7, 7, 0), new Point3d(3, 7, 0) }; - bref.ClipBlockRef(pts); + brf1.ClipBlockRef(pts); var id1 = tr.CurrentSpace.InsertBlock(new Point3d(20, 20, 0), "test1"); - var bref1 = tr.GetObject(id); - bref1?.ClipBlockRef(new Point3d(13, 13, 0), new Point3d(17, 17, 0)); + var brf2 = tr.GetObject(id); + brf2?.ClipBlockRef(new Point3d(13, 13, 0), new Point3d(17, 17, 0)); } // 给用户的测试程序,不知道对错 @@ -239,8 +240,8 @@ public void Test_Block_ej() }); var id = tr.ModelSpace.InsertBlock(Point3d.Origin, blockdef);// 插入块参照 - var entTest = tr.GetObject(id); - entTest?.Draw(); + var brf = tr.GetObject(id); + brf?.Draw(); } using DBTrans tr2 = new(); @@ -252,12 +253,12 @@ public void Test_Block_ej() if (per.Status != PromptStatus.OK) return; - var Bref = tr2.GetObject(per.ObjectId)!; + var brf2 = tr2.GetObject(per.ObjectId)!; // var BTR = tr.GetObject(Bref.BlockTableRecord, OpenMode.ForWrite); //// 如果知道块名字BTRName // BlockTableRecord BTR = tr.GetObject(tr.BlockTable[blockName], OpenMode.ForWrite); - var btr = tr2.BlockTable[Bref.Name]; + var btr = tr2.BlockTable[brf2.Name]; tr2.BlockTable.Change(btr, ltr => { foreach (ObjectId oid in ltr) @@ -427,12 +428,12 @@ public void Test_QuickBlockDef1() // { // var bt = tr1.GetObject(db.BlockTableId, OpenMode.ForRead) as BlockTable; var btr1 = tr.GetObject(db.CurrentSpaceId, OpenMode.ForWrite) as BlockTableRecord; - var br = new BlockReference(Point3d.Origin, bt[blockName]) + var brf = new BlockReference(Point3d.Origin, bt[blockName]) { ScaleFactors = default }; - btr1!.AppendEntity(br); - tr.AddNewlyCreatedDBObject(br, true); + btr1!.AppendEntity(brf); + tr.AddNewlyCreatedDBObject(brf, true); btr1.DowngradeOpen(); ed.Regen(); tr.Commit(); @@ -460,8 +461,8 @@ void ChangeDynameicBlock() }; var blockid = Env.Editor.GetEntity("选择个块").ObjectId; using DBTrans tr = new(); - var blockref = tr.GetObject(blockid)!; - blockref.ChangeBlockProperty(pro); + var brf = tr.GetObject(blockid)!; + brf.ChangeBlockProperty(pro); // 这是第一个函数的用法 } -- Gitee From 5ca3d352d6c8236970e23edecf548afc48c7c0b1 Mon Sep 17 00:00:00 2001 From: liuqihong <540762622@qq.com> Date: Fri, 28 Oct 2022 01:29:19 +0800 Subject: [PATCH 584/675] =?UTF-8?q?=E6=B7=BB=E5=8A=A0=E5=8A=A8=E6=80=81?= =?UTF-8?q?=E8=AF=BB=E5=8F=96PE=E4=BF=A1=E6=81=AF=20=E8=AF=BB=E5=8F=96exe?= =?UTF-8?q?=E6=8E=A5=E5=8F=A3=E7=9A=84=E6=97=B6=E5=80=99=E5=B0=B1=E5=8F=AF?= =?UTF-8?q?=E4=BB=A5=E5=8A=A8=E6=80=81=E8=8E=B7=E5=8F=96=E4=BA=86?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../IFoxCAD.Cad.Shared.projitems | 2 + .../Runtime/PE/AcadPeInfo.cs | 310 ++++ .../Runtime/PE/ProgramPE.cs | 1567 +++++++++++++++++ 3 files changed, 1879 insertions(+) create mode 100644 src/IFoxCAD.Cad.Shared/Runtime/PE/AcadPeInfo.cs create mode 100644 src/IFoxCAD.Cad.Shared/Runtime/PE/ProgramPE.cs diff --git a/src/IFoxCAD.Cad.Shared/IFoxCAD.Cad.Shared.projitems b/src/IFoxCAD.Cad.Shared/IFoxCAD.Cad.Shared.projitems index 79335fe..b26dc9d 100644 --- a/src/IFoxCAD.Cad.Shared/IFoxCAD.Cad.Shared.projitems +++ b/src/IFoxCAD.Cad.Shared/IFoxCAD.Cad.Shared.projitems @@ -50,6 +50,8 @@ + + diff --git a/src/IFoxCAD.Cad.Shared/Runtime/PE/AcadPeInfo.cs b/src/IFoxCAD.Cad.Shared/Runtime/PE/AcadPeInfo.cs new file mode 100644 index 0000000..8455623 --- /dev/null +++ b/src/IFoxCAD.Cad.Shared/Runtime/PE/AcadPeInfo.cs @@ -0,0 +1,310 @@ +using System.Diagnostics; + +namespace IFoxCAD.Cad; + +// 选择模式 +[Flags] +public enum AcadPeEnum : byte +{ + AcadExe = 1, + AccoreDll = 2, + All = AcadExe | AccoreDll, +} + +// 这里的枚举对应 GetMethodException 错误值 +[Flags] +public enum GetMethodErrorNum : byte +{ + Ok = 0, + NoModule = 1, + NoFuncName = 2, +} + +// 自动获取本工程上面的发送命令的接口 +public class AcadPeInfo +{ + #region 静态单例获取exe/dll信息 + static PeInfo? _PeForAcadExe; + public static PeInfo? PeForAcadExe + { + get + { + if (_PeForAcadExe is null) + { + // 拿到acad.exe路径,获取所有的函数名 + var acadexeFullName = Process.GetCurrentProcess().MainModule.FileName; + _PeForAcadExe = new PeInfo(acadexeFullName); + } + return _PeForAcadExe; + } + } + + static PeInfo? _PeForAccoreDll; + public static PeInfo? PeForAccoreDll + { + get + { + if (_PeForAccoreDll is null) + { + // 拿到accore.dll路径,获取所有的函数名 + var acadexeFullName = Process.GetCurrentProcess().MainModule.FileName; + var accore = Path.GetDirectoryName(acadexeFullName) + "\\accore.dll"; + if (File.Exists(accore))// 08没有,高版本分离的 + _PeForAccoreDll = new PeInfo(accore); + } + return _PeForAccoreDll; + } + } + + /// + /// 同名函数指针们 + /// + static List? _Methods; + public List? Methods + { + get + { + if (_Methods is null) + { + _Methods = new(); + + if ((_acadPeEnum & AcadPeEnum.AcadExe) == AcadPeEnum.AcadExe && PeForAcadExe != null) + GetPeMethod(PeForAcadExe); + if ((_acadPeEnum & AcadPeEnum.AccoreDll) == AcadPeEnum.AccoreDll && PeForAccoreDll != null) + GetPeMethod(PeForAccoreDll); + } + return _Methods; + } + } + #endregion + + #region 字段/构造 + /// + /// 用于查找PE不带修饰的函数名 + /// + string _findFuncName; + /// + /// 枚举查找对象 + /// + AcadPeEnum _acadPeEnum; + + /// + /// 通过函数名获取指针,指定类型 + /// + /// 不带修饰的函数名 + /// 读取哪个cad内部文件的枚举(目前只支持两个) + public AcadPeInfo(string methodName, AcadPeEnum acadPeEnum) + { + _findFuncName = methodName; + _acadPeEnum = acadPeEnum; + } + + /// + /// 获取CAD的函数指针 + /// + /// 委托 + /// 不带修饰的函数名 + /// 读取哪个cad内部文件的枚举(目前只支持两个) + /// 委托 + public static TDelegate? GetDelegate(string methodName, AcadPeEnum acadPeEnum) + where TDelegate : class + { + return new AcadPeInfo(methodName, acadPeEnum) + .GetDelegate(); + } + #endregion + + #region 方法 + /// + /// 储存旧值<去除修饰函数名(查找的),带修饰函数名们> + /// + static Dictionary> _Dict = new(); + + /// + /// 返回函数指针 + /// + /// Pe信息:可能来自exe/dll + /// 错误信息 + GetMethodErrorNum GetPeMethod(PeInfo peInfo) + { + var identifyStr = _findFuncName + ";" + peInfo.FullName; + if (_Dict.ContainsKey(identifyStr))// 如果已经找过,直接返回 + { + _Methods = _Dict[identifyStr]; + } + else + { + _Methods ??= new(); + try + { + PeFunction.Finds(peInfo, _findFuncName, _Methods); + if (_Methods.Count != 0)// 此时从不含有 + _Dict.Add(identifyStr, _Methods); + } + catch (GetPeMethodException ex) + { return (GetMethodErrorNum)ex.ErrorNum; } + } + return GetMethodErrorNum.Ok; + } + + /// + /// 转为委托 + /// + /// 委托对象 + /// + public TDelegate? GetDelegate() where TDelegate : class + { + if (Methods is null || Methods.Count == 0) + return null; + + TDelegate? func = null; + + /* + * 0x01 + * 这里永远不报错,但是不代表不会出错. + * 调用C盘exe/dll时需要权限, + * 所以会出现:[DLLImport]可以,直接运行cad也可以,但是调试不行. + * 此时可以提权vs再调试,有时候会出现:调试不显示东西,但是运行是对的. + * + * 0x02 + * 出错时候用完整的修饰名 + * + * 0x03 + * 这里可能同时存在acad.exe和accore.dll相同指针? + * 所以我是用排序方法找最短的指针,所以它是第First个. + */ + + // 排序,最少长度原则本身就是让完全相同字符串在最前面 + // 这里替换为有序哈希,因为我总是需要不带修饰的返回函数,所以是排序长度的第一个 + _Methods = _Methods.OrderBy(str => str.CName?.Length) + .ThenBy(str => str.MethodName.Length) + .ToList(); + + func = Marshal.GetDelegateForFunctionPointer(Methods.First().GetProcAddress(), typeof(TDelegate)) as TDelegate; + return func; + } + #endregion +} + +/// +/// 通过名字查找exe/dll内所有名字 +/// +public class PeFunction +{ + #region 字段/构造 + string? _CName; + /// + /// 纯c语言名 + /// + public string? CName + { + get + { + if (_CName is null && MethodName is not null) + { + _CName = MethodName.Replace("?", string.Empty); // 剔除cpp前缀 + int num = _CName.IndexOf("@"); + if (num > -1) + _CName = _CName.Substring(0, num); // 剔除参数部分 + } + return _CName; + } + } + + /// + /// 模块文件路径 + /// + public string? ModuleFullName; + /// + /// 模块指针 + /// + public IntPtr ModuleIntPtr; + /// + /// 函数名 + /// + public string MethodName; + /// + /// 通过名字查找exe/dll内所有名字 + /// + /// 没修饰的方法名 + public PeFunction(string methodName) + { + MethodName = methodName; + } + #endregion + + /// + /// 获取函数指针 + /// + public IntPtr GetProcAddress() + { + return WindowsAPI.GetProcAddress(ModuleIntPtr, MethodName); + } + + /// + /// 通过名字查找exe/dll内所有名字 + /// + /// pe结构 + /// 用于查找的方法名 + /// 返回函数集合 + public static void Finds(PeInfo peInfo, + string findFuncName, + List funcAdress_Out) + { + if (findFuncName == null) + throw new GetPeMethodException(2, "没有找到对应的函数:" + findFuncName); + + var peModuleFullName = peInfo.FullName; + if (peModuleFullName == null) + throw new GetPeMethodException(1, "找不到模块:" + peModuleFullName + "当前程序没有加载这个东西?"); + var hModule = WindowsAPI.GetModuleHandle(peModuleFullName); // 执行前必须加载了先,acad.exe/accore.dll + if (hModule == IntPtr.Zero) + throw new GetPeMethodException(1, "找不到模块:" + peModuleFullName + "当前程序没有加载这个东西?"); + + // 遍历函数接口名单 + var names = peInfo.ExportDirectory?.FunctionNames(); + if (names == null) + throw new ArgumentException(nameof(names)); + + foreach (var name in names) + { + if (name.Contains(findFuncName))// 这里是名称含有,不是容器含有 + { + var fn = new PeFunction(name) + { + ModuleFullName = peModuleFullName, + ModuleIntPtr = hModule + }; + funcAdress_Out.Add(fn); + } + } + } +} + + + +/// +/// 错误信息 +/// +public class GetPeMethodException : ApplicationException +{ + public int ErrorNum; + public string? ErrorMsg; + public Exception? InnerException1; + + public GetPeMethodException(string msg) : base(msg) + { + ErrorMsg = msg; + } + + public GetPeMethodException(int errorNum, string msg) : base(msg) + { + ErrorNum = errorNum; + } + + public GetPeMethodException(string msg, Exception innerException) : base(msg, innerException) + { + InnerException1 = innerException; + ErrorMsg = msg; + } +} \ No newline at end of file diff --git a/src/IFoxCAD.Cad.Shared/Runtime/PE/ProgramPE.cs b/src/IFoxCAD.Cad.Shared/Runtime/PE/ProgramPE.cs new file mode 100644 index 0000000..29c5c02 --- /dev/null +++ b/src/IFoxCAD.Cad.Shared/Runtime/PE/ProgramPE.cs @@ -0,0 +1,1567 @@ +namespace IFoxCAD.Cad; + +using System; +using System.Collections; +using System.Collections.Generic; +using System.Data; +using System.IO; +using System.Text; + + +/* 来源 https://blog.csdn.net/zgke/article/details/2955560 我在他基础上面增加了X64的处理 + * 调用例子 + static void Main(string[] args) + { + var path = @"C:\Program Files\Autodesk\AutoCAD 2021\acad.exe"; + // path = @"G:\AutoCAD 2008\acad.exe"; + + var pe = new JoinBox.BasalCurrency.PeInfo(path); + + // 输出所有的函数名 + var sb = new StringBuilder(); + foreach (var item in pe.ExportDirectory.NameList) + { + sb.Append(Environment.NewLine); + var str = System.Text.Encoding.Default.GetString(item as byte[]); + sb.Append(str); + } + Debug.WriteLine(sb.ToString()); + + // 原作者的封装 + var ss = pe.GetPETable(); + foreach (var item in ss.Tables) + { + } + } +*/ + +/// +/// 微软软件结构PE信息 +/// +public class PeInfo +{ + #region 成员 + /// + /// 获取是否正常打开文件 + /// + public bool OpenFile { get; private set; } = false; + public DosHeader? DosHeader { get; private set; } + public DosStub? DosStub { get; private set; } + public PEHeader? PEHeader { get; private set; } + public OptionalHeader? OptionalHeader { get; private set; } + public OptionalDirAttrib? OptionalDirAttrib { get; private set; } + public SectionTable? SectionTable { get; private set; } + /// + /// 函数接口名单 + /// + public ExportDirectory? ExportDirectory { get; private set; } + public ImportDirectory? ImportDirectory { get; private set; } + public ResourceDirectory? ResourceDirectory { get; private set; } + /// + /// PE文件完整路径 + /// + public string? FullName; + + bool _IsX86 = true; + + /// + /// 全部文件数据 + /// + readonly byte[]? _PEFileByte; + /// + /// 文件读取的位置 + /// + long _PEFileIndex = 0; + #endregion + + #region 构造 + public PeInfo(string fullName) + { + if (fullName is null) + throw new ArgumentException(nameof(fullName)); ; + + FullName = fullName; + FileStream? file = null; + OpenFile = false; + try + { + // 文件流 + file = new FileStream(fullName, FileMode.Open, FileAccess.Read, FileShare.ReadWrite);// FileShare才能进c盘 + _PEFileByte = new byte[file.Length]; + file.Read(_PEFileByte, 0, _PEFileByte.Length); + LoadFile(); + OpenFile = true; + } + catch (Exception) { throw; } + finally + { + file?.Close(); + } + } + #endregion + + #region 读表方法 + /// + /// 开始读取 + /// + private void LoadFile() + { + LoadDosHeader(); // 获取DOS头,为了兼容DOS所以首先处理这个,然后才到PE头 + LoadDosStub(); // 获取DOS的身体 + LoadPEHeader(); // PE头 + LoadOptionalHeader(); // PE头扩展 + LoadOptionalDirAttrib(); // 获取选项目录属性 + LoadSectionTable(); // 获取节表 + LoadExportDirectory(); // 获取输出表 + LoadImportDirectory(); // 获取输入表 + LoadResourceDirectory(); // 获取资源目录 + } + + /// + /// 获得DOS头 + /// + private void LoadDosHeader() + { + DosHeader = new DosHeader + { + FileStarIndex = _PEFileIndex + }; + Loadbyte(ref DosHeader.e_magic); + Loadbyte(ref DosHeader.e_cblp); + Loadbyte(ref DosHeader.e_cp); + Loadbyte(ref DosHeader.e_crlc); + Loadbyte(ref DosHeader.e_cparhdr); + Loadbyte(ref DosHeader.e_minalloc); + Loadbyte(ref DosHeader.e_maxalloc); + Loadbyte(ref DosHeader.e_ss); + Loadbyte(ref DosHeader.e_sp); + Loadbyte(ref DosHeader.e_csum); + Loadbyte(ref DosHeader.e_ip); + Loadbyte(ref DosHeader.e_cs); + Loadbyte(ref DosHeader.e_rva); + Loadbyte(ref DosHeader.e_fg); + Loadbyte(ref DosHeader.e_bl1); + Loadbyte(ref DosHeader.e_oemid); + Loadbyte(ref DosHeader.e_oeminfo); + Loadbyte(ref DosHeader.e_bl2); + Loadbyte(ref DosHeader.e_PESTAR); + + DosHeader.FileEndIndex = _PEFileIndex; + } + + /// + /// 获得DOS SUB字段 + /// + private void LoadDosStub() + { + if (DosHeader is null) + return; + + long Size = GetLong(DosHeader.e_PESTAR) - _PEFileIndex; // 获得SUB的大小 + DosStub = new DosStub(Size) + { + FileStarIndex = _PEFileIndex + }; + Loadbyte(ref DosStub.DosStubData); + DosStub.FileEndIndex = _PEFileIndex; + } + + /// + /// 获得PE的文件头 + /// + /// + private void LoadPEHeader() + { + PEHeader = new PEHeader + { + FileStarIndex = _PEFileIndex + }; + Loadbyte(ref PEHeader.Header); + Loadbyte(ref PEHeader.Machine);// [76 1]==[0x4C 1]是x32, + _IsX86 = PEHeader.Machine[0] == 0x4c && PEHeader.Machine[1] == 0x1; + // (PEHeader.Machine[0] == 0x64 && PEHeader.Machine[1] == 0x86)// x64 + Loadbyte(ref PEHeader.NumberOfSections); + Loadbyte(ref PEHeader.TimeDateStamp); + Loadbyte(ref PEHeader.PointerToSymbolTable); + Loadbyte(ref PEHeader.NumberOfSymbols); + Loadbyte(ref PEHeader.SizeOfOptionalHeader); + Loadbyte(ref PEHeader.Characteristics); + PEHeader.FileEndIndex = _PEFileIndex; + } + + /// + /// 获得OPTIONAL PE扩展属性 + /// + /// + private void LoadOptionalHeader() + { + // 这里必须通过PE文件来判断它是一个什么架构,从而使用x86-x64 + OptionalHeader = new OptionalHeader(_IsX86) + { + FileStarIndex = _PEFileIndex + }; + Loadbyte(ref OptionalHeader.Magic); + Loadbyte(ref OptionalHeader.MajorLinkerVersion); + Loadbyte(ref OptionalHeader.MinorLinkerVersion); + Loadbyte(ref OptionalHeader.SizeOfCode); + Loadbyte(ref OptionalHeader.SizeOfInitializedData); + Loadbyte(ref OptionalHeader.SizeOfUninitializedData); + Loadbyte(ref OptionalHeader.AddressOfEntryPoint); + Loadbyte(ref OptionalHeader.BaseOfCode); + Loadbyte(ref OptionalHeader.ImageBase); + Loadbyte(ref OptionalHeader.BaseOfData); + Loadbyte(ref OptionalHeader.SectionAlignment); + Loadbyte(ref OptionalHeader.FileAlignment); + + Loadbyte(ref OptionalHeader.MajorOperatingSystemVersion); + Loadbyte(ref OptionalHeader.MinorOperatingSystemVersion); + Loadbyte(ref OptionalHeader.MajorImageVersion); + Loadbyte(ref OptionalHeader.MinorImageVersion); + Loadbyte(ref OptionalHeader.MajorSubsystemVersion); + Loadbyte(ref OptionalHeader.MinorSubsystemVersion); + Loadbyte(ref OptionalHeader.Win32VersionValue); + Loadbyte(ref OptionalHeader.SizeOfImage); + Loadbyte(ref OptionalHeader.SizeOfHeards); + Loadbyte(ref OptionalHeader.CheckSum); + Loadbyte(ref OptionalHeader.Subsystem); + Loadbyte(ref OptionalHeader.DLLCharacteristics); + Loadbyte(ref OptionalHeader.SizeOfStackReserve); + Loadbyte(ref OptionalHeader.SizeOfStackCommit); + Loadbyte(ref OptionalHeader.SizeOfHeapReserve); + Loadbyte(ref OptionalHeader.SizeOfHeapCommit); + Loadbyte(ref OptionalHeader.LoaderFlags); + Loadbyte(ref OptionalHeader.NumberOfRvaAndSizes); + + OptionalHeader.FileEndIndex = _PEFileIndex; + } + + /// + /// 获取目录表 + /// + /// + private void LoadOptionalDirAttrib() + { + if (OptionalHeader is null) + return; + + OptionalDirAttrib = new OptionalDirAttrib + { + FileStarIndex = _PEFileIndex + }; + + long DirCount = GetLong(OptionalHeader.NumberOfRvaAndSizes);// 这里导致无法使用64位 + for (int i = 0; i != DirCount; i++) + { + OptionalDirAttrib.DirAttrib? directAttrib = new(); + Loadbyte(ref directAttrib.DirRva); + Loadbyte(ref directAttrib.DirSize); + OptionalDirAttrib.DirByte.Add(directAttrib); + } + OptionalDirAttrib.FileEndIndex = _PEFileIndex; + } + + /// + /// 获取节表 + /// + private void LoadSectionTable() + { + if (PEHeader is null) + return; + + SectionTable = new SectionTable(); + long Count = GetLong(PEHeader.NumberOfSections); + SectionTable.FileStarIndex = _PEFileIndex; + for (long i = 0; i != Count; i++) + { + var Section = new SectionTable.SectionData(); + + Loadbyte(ref Section.SectName); + Loadbyte(ref Section.VirtualAddress); + Loadbyte(ref Section.SizeOfRawDataRVA); + Loadbyte(ref Section.SizeOfRawDataSize); + Loadbyte(ref Section.PointerToRawData); + Loadbyte(ref Section.PointerToRelocations); + Loadbyte(ref Section.PointerToLinenumbers); + Loadbyte(ref Section.NumberOfRelocations); + Loadbyte(ref Section.NumberOfLinenumbers); + Loadbyte(ref Section.Characteristics); + SectionTable.Section.Add(Section); + } + SectionTable.FileEndIndex = _PEFileIndex; + } + + /// + /// 读取输出表 + /// + private void LoadExportDirectory() + { + if (OptionalDirAttrib is null) + return; + if (OptionalDirAttrib.DirByte.Count == 0) + return; + + if (OptionalDirAttrib.DirByte[0] is not OptionalDirAttrib.DirAttrib exporRVA || + GetLong(exporRVA.DirRva) == 0) + return; + + long exporAddress = GetLong(exporRVA.DirRva); // 获取的位置 + ExportDirectory = new ExportDirectory(); + + if (SectionTable is null) + return; + + for (int i = 0; i != SectionTable.Section.Count; i++) // 循环节表 + { + if (SectionTable.Section[i] is not SectionTable.SectionData sect) + continue; + + long starRva = GetLong(sect.SizeOfRawDataRVA); + long endRva = GetLong(sect.SizeOfRawDataSize); + + if (exporAddress >= starRva && exporAddress < starRva + endRva) + { + _PEFileIndex = exporAddress - GetLong(sect.SizeOfRawDataRVA) + GetLong(sect.PointerToRawData); + + ExportDirectory.FileStarIndex = _PEFileIndex; + ExportDirectory.FileEndIndex = _PEFileIndex + GetLong(exporRVA.DirSize); + + Loadbyte(ref ExportDirectory.Characteristics); + Loadbyte(ref ExportDirectory.TimeDateStamp); + Loadbyte(ref ExportDirectory.MajorVersion); + Loadbyte(ref ExportDirectory.MinorVersion); + Loadbyte(ref ExportDirectory.Name); + Loadbyte(ref ExportDirectory.Base); + Loadbyte(ref ExportDirectory.NumberOfFunctions); + Loadbyte(ref ExportDirectory.NumberOfNames); + Loadbyte(ref ExportDirectory.AddressOfFunctions); + Loadbyte(ref ExportDirectory.AddressOfNames); + Loadbyte(ref ExportDirectory.AddressOfNameOrdinals); + + _PEFileIndex = GetLong(ExportDirectory.AddressOfFunctions) - GetLong(sect.SizeOfRawDataRVA) + GetLong(sect.PointerToRawData); + long endIndex = GetLong(ExportDirectory.AddressOfNames) - GetLong(sect.SizeOfRawDataRVA) + GetLong(sect.PointerToRawData); + long numb = (endIndex - _PEFileIndex) / 4; + for (long z = 0; z != numb; z++) + { + byte[] Data = new byte[4]; + Loadbyte(ref Data); + ExportDirectory.AddressOfFunctionsList.Add(Data); + } + + _PEFileIndex = endIndex; + endIndex = GetLong(ExportDirectory.AddressOfNameOrdinals) - GetLong(sect.SizeOfRawDataRVA) + GetLong(sect.PointerToRawData); + numb = (endIndex - _PEFileIndex) / 4; + for (long z = 0; z != numb; z++) + { + byte[] Data = new byte[4]; + Loadbyte(ref Data); + ExportDirectory.AddressOfNamesList.Add(Data); + } + + _PEFileIndex = endIndex; + endIndex = GetLong(ExportDirectory.Name) - GetLong(sect.SizeOfRawDataRVA) + GetLong(sect.PointerToRawData); + numb = (endIndex - _PEFileIndex) / 2; + for (long z = 0; z != numb; z++) + { + byte[] Data = new byte[2]; + Loadbyte(ref Data); + ExportDirectory.AddressOfNameOrdinalsList.Add(Data); + } + + _PEFileIndex = endIndex; + + if (_PEFileByte is not null) + { + long ReadIndex = 0; + while (true) + { + if (_PEFileByte[_PEFileIndex + ReadIndex] == 0) + { + if (_PEFileByte[_PEFileIndex + ReadIndex + 1] == 0) + break; + + var Date = new byte[ReadIndex]; + Loadbyte(ref Date); + ExportDirectory.FunctionNamesByte.Add(Date); + + _PEFileIndex++; + ReadIndex = 0; + } + ReadIndex++; + } + } + break; + } + } + } + + /// + /// 读取输入表 + /// + private void LoadImportDirectory() + { + if (OptionalDirAttrib is null) + return; + if (OptionalDirAttrib.DirByte.Count < 1) + return; + if (OptionalDirAttrib.DirByte[1] is not OptionalDirAttrib.DirAttrib ImporRVA) + return; + + long ImporAddress = GetLong(ImporRVA.DirRva); // 获取的位置 + if (ImporAddress == 0) + return; + long ImporSize = GetLong(ImporRVA.DirSize); // 获取大小 + + ImportDirectory = new ImportDirectory(); + + long SizeRva = 0; + long PointerRva = 0; + + long StarRva = 0; + long EndRva = 0; + + #region 获取位置 + if (SectionTable is null) + return; + for (int i = 0; i != SectionTable.Section.Count; i++) // 循环节表 + { + if (SectionTable.Section[i] is not SectionTable.SectionData Sect) + continue; + + StarRva = GetLong(Sect.SizeOfRawDataRVA); + EndRva = GetLong(Sect.SizeOfRawDataSize); + + if (ImporAddress >= StarRva && ImporAddress < StarRva + EndRva) + { + SizeRva = GetLong(Sect.SizeOfRawDataRVA); + PointerRva = GetLong(Sect.PointerToRawData); + _PEFileIndex = ImporAddress - SizeRva + PointerRva; + + ImportDirectory.FileStarIndex = _PEFileIndex; + ImportDirectory.FileEndIndex = _PEFileIndex + ImporSize; + break; + } + } + + if (SizeRva == 0 && PointerRva == 0) + return; + #endregion + + #region 输入表结构 + while (true) + { + var import = new ImportDirectory.ImportDate(); + Loadbyte(ref import.OriginalFirstThunk); + Loadbyte(ref import.TimeDateStamp); + Loadbyte(ref import.ForwarderChain); + Loadbyte(ref import.Name); + Loadbyte(ref import.FirstThunk); + + if (GetLong(import.Name) == 0) + break; + ImportDirectory.ImportList.Add(import); // 添加 + } + #endregion + + + #region 获取输入DLL名称 + for (int z = 0; z != ImportDirectory.ImportList.Count; z++) // 获取引入DLL名字 + { + if (ImportDirectory.ImportList[z] is not ImportDirectory.ImportDate Import) + continue; + + long ImportDLLName = GetLong(Import.Name) - SizeRva + PointerRva; + _PEFileIndex = ImportDLLName; + long ReadCount = 0; + while (_PEFileByte is not null) // 获取引入名 + { + if (_PEFileByte[_PEFileIndex + ReadCount] == 0) + { + Import.DLLName = new byte[ReadCount]; + Loadbyte(ref Import.DLLName); + break; + } + ReadCount++; + } + } + #endregion + + #region 获取引入方法 先获取地址 然后获取名字和头 + for (int z = 0; z != ImportDirectory.ImportList.Count; z++) // 获取引入方法 + { + if (ImportDirectory.ImportList[z] is not ImportDirectory.ImportDate import) + continue; + + long importDLLName = GetLong(import.OriginalFirstThunk) - SizeRva + PointerRva; + _PEFileIndex = importDLLName; + while (true) + { + var function = new ImportDirectory.ImportDate.FunctionList(); + Loadbyte(ref function.OriginalFirst); + + long loadIndex = GetLong(function.OriginalFirst); + if (loadIndex == 0) + break; + long oldIndex = _PEFileIndex; + + _PEFileIndex = loadIndex - SizeRva + PointerRva; + + if (loadIndex >= StarRva && loadIndex < StarRva + EndRva) // 发现有些数字超级大 + { + int ReadCount = 0; + + while (_PEFileByte is not null) + { + if (ReadCount == 0) + Loadbyte(ref function.FunctionHead); + if (_PEFileByte[_PEFileIndex + ReadCount] == 0) + { + byte[] FunctionName = new byte[ReadCount]; + Loadbyte(ref FunctionName); + function.FunctionName = FunctionName; + + break; + } + ReadCount++; + } + } + else + { + function.FunctionName = new byte[1]; + } + + _PEFileIndex = oldIndex; + import.DLLFunctionList.Add(function); + } + } + #endregion + } + + /// + /// 读取资源表 + /// + private void LoadResourceDirectory() + { + #region 初始化 + if (OptionalDirAttrib is null || OptionalDirAttrib.DirByte.Count < 3) + return; + if (OptionalDirAttrib.DirByte[2] is not OptionalDirAttrib.DirAttrib ImporRVA) + return; + + long ImporAddress = GetLong(ImporRVA.DirRva); // 获取的位置 + if (ImporAddress == 0) + return; + long ImporSize = GetLong(ImporRVA.DirSize); // 获取大小 + + ResourceDirectory = new ResourceDirectory(); + + long SizeRva = 0; + long PointerRva = 0; + long StarRva = 0; + long PEIndex = 0; + #endregion + + #region 获取位置 + if (SectionTable is null) + return; + + for (int i = 0; i != SectionTable.Section.Count; i++) // 循环节表 + { + if (SectionTable.Section[i] is not SectionTable.SectionData sect) + continue; + + StarRva = GetLong(sect.SizeOfRawDataRVA); + var EndRva = GetLong(sect.SizeOfRawDataSize); + if (ImporAddress >= StarRva && ImporAddress < StarRva + EndRva) + { + SizeRva = GetLong(sect.SizeOfRawDataRVA); + PointerRva = GetLong(sect.PointerToRawData); + _PEFileIndex = ImporAddress - SizeRva + PointerRva; + PEIndex = _PEFileIndex; + ResourceDirectory.FileStarIndex = _PEFileIndex; + ResourceDirectory.FileEndIndex = _PEFileIndex + ImporSize; + break; + } + } + + if (SizeRva == 0 && PointerRva == 0) + return; + #endregion + + AddResourceNode(ResourceDirectory, PEIndex, 0, StarRva); + } + + /// + /// 添加资源节点 + /// + /// + /// + /// + /// + private void AddResourceNode(ResourceDirectory node, long PEIndex, long RVA, long resourSectRva) + { + _PEFileIndex = PEIndex + RVA; // 设置位置 + Loadbyte(ref node.Characteristics); + Loadbyte(ref node.TimeDateStamp); + Loadbyte(ref node.MajorVersion); + Loadbyte(ref node.MinorVersion); + Loadbyte(ref node.NumberOfNamedEntries); + Loadbyte(ref node.NumberOfIdEntries); + + long NameRVA = GetLong(node.NumberOfNamedEntries); + for (int i = 0; i != NameRVA; i++) + { + var Entry = new ResourceDirectory.DirectoryEntry(); + Loadbyte(ref Entry.Name); + Loadbyte(ref Entry.Id); + byte[] temp = new byte[2]; + temp[0] = Entry.Name[0]; + temp[1] = Entry.Name[1]; + + if (_PEFileByte is null) + return; + + long NameIndex = GetLong(temp) + PEIndex; + temp[0] = _PEFileByte[NameIndex + 0]; + temp[1] = _PEFileByte[NameIndex + 1]; + + long NameCount = GetLong(temp); + node.Name = new byte[NameCount * 2]; + + for (int z = 0; z != node.Name.Length; z++) + node.Name[z] = _PEFileByte[NameIndex + 2 + z]; + // System.Windows.Forms.MessageBox.Show(GetString(Entry.ID)); + + temp[0] = Entry.Id[2]; + temp[1] = Entry.Id[3]; + + long oldIndex = _PEFileIndex; + + if (GetLong(temp) == 0) + { + temp[0] = Entry.Id[0]; + temp[1] = Entry.Id[1]; + + _PEFileIndex = GetLong(temp) + PEIndex; + + var dataRVA = new ResourceDirectory.DirectoryEntry.DataEntry(); + + Loadbyte(ref dataRVA.ResourRVA); + Loadbyte(ref dataRVA.ResourSize); + Loadbyte(ref dataRVA.ResourTest); + Loadbyte(ref dataRVA.ResourWen); + + _PEFileIndex = oldIndex; + Entry.DataEntryList.Add(dataRVA); + // System.Windows.Forms.MessageBox.Show(GetString(DataRVA.ResourRVA)+"*"+GetString(DataRVA.ResourSize)); + } + else + { + temp[0] = Entry.Id[0]; + temp[1] = Entry.Id[1]; + + var Resource = new ResourceDirectory(); + Entry.NodeDirectoryList.Add(Resource); + AddResourceNode(Resource, PEIndex, GetLong(temp), resourSectRva); + } + _PEFileIndex = oldIndex; + node.EntryList.Add(Entry); + } + + long Count = GetLong(node.NumberOfIdEntries); + for (int i = 0; i != Count; i++) + { + var entry = new ResourceDirectory.DirectoryEntry(); + Loadbyte(ref entry.Name); + Loadbyte(ref entry.Id); + // System.Windows.Forms.MessageBox.Show(GetString(Entry.Name)+"_"+GetString(Entry.Id)); + + byte[] temp = new byte[2]; + temp[0] = entry.Id[2]; + temp[1] = entry.Id[3]; + + long OldIndex = _PEFileIndex; + + if (GetLong(temp) == 0) + { + temp[0] = entry.Id[0]; + temp[1] = entry.Id[1]; + + _PEFileIndex = GetLong(temp) + PEIndex; + + var dataRVA = new ResourceDirectory.DirectoryEntry.DataEntry(); + Loadbyte(ref dataRVA.ResourRVA); + Loadbyte(ref dataRVA.ResourSize); + Loadbyte(ref dataRVA.ResourTest); + Loadbyte(ref dataRVA.ResourWen); + + long FileRva = GetLong(dataRVA.ResourRVA) - resourSectRva + PEIndex; + + dataRVA.FileStarIndex = FileRva; + dataRVA.FileEndIndex = FileRva + GetLong(dataRVA.ResourSize); + + _PEFileIndex = OldIndex; + entry.DataEntryList.Add(dataRVA); + // System.Windows.Forms.MessageBox.Show(GetString(DataRVA.ResourRVA)+"*"+GetString(DataRVA.ResourSize)); + } + else + { + temp[0] = entry.Id[0]; + temp[1] = entry.Id[1]; + var Resource = new ResourceDirectory(); + entry.NodeDirectoryList.Add(Resource); + AddResourceNode(Resource, PEIndex, GetLong(temp), resourSectRva); + } + _PEFileIndex = OldIndex; + node.EntryList.Add(entry); + } + } + + #endregion + + #region 工具方法 + /// + /// 读数据 读byte[]的数量 会改边PEFileIndex的值 + /// + /// + private void Loadbyte(ref byte[] data) + { + if (_PEFileByte is null) + return; + + for (int i = 0; i != data.Length; i++) + { + data[i] = _PEFileByte[_PEFileIndex]; + _PEFileIndex++; + } + } + /// + /// 转换byte为字符串 + /// + /// byte[] + /// AA BB CC DD + private string GetString(byte[] data) + { + string Temp = ""; + for (int i = 0; i != data.Length - 1; i++) + Temp += data[i].ToString("X02") + " "; + + Temp += data[data.Length - 1].ToString("X02"); + // Temp += data[^1].ToString("X02"); + return Temp; + } + /// + /// 转换字符为显示数据 + /// + /// byte[] + /// ASCII DEFAULT UNICODE BYTE + /// + private string GetString(byte[] data, string type) + { + if (type.Trim().ToUpper() == "ASCII") + return System.Text.Encoding.ASCII.GetString(data); + if (type.Trim().ToUpper() == "DEFAULT") + return System.Text.Encoding.Default.GetString(data); + if (type.Trim().ToUpper() == "UNICODE") + return System.Text.Encoding.Unicode.GetString(data); + if (type.Trim().ToUpper() == "BYTE") + { + string Temp = ""; + for (int i = data.Length - 1; i != 0; i--) + Temp += data[i].ToString("X02") + " "; + Temp += data[0].ToString("X02"); + return Temp; + } + return GetInt(data); + } + /// + /// 转换BYTE为INT + /// + /// + /// + static string GetInt(byte[] data) + { + string Temp = ""; + for (int i = 0; i != data.Length - 1; i++) + { + int ByteInt = (int)data[i]; + Temp += ByteInt.ToString() + " "; + } + int EndByteInt = (int)data[data.Length - 1]; + // int EndByteInt = (int)data[^1]; + Temp += EndByteInt.ToString(); + return Temp; + } + /// + /// 转换数据为LONG + /// + /// + /// + private long GetLong(byte[] data) + { + if (data.Length > 4) + return 0; + + string MC = ""; + // if (data.Length <= 4) + for (int i = data.Length - 1; i != -1; i--) + MC += data[i].ToString("X02"); + return Convert.ToInt64(MC, 16); + } + /// + /// 添加一行信息 + /// + /// 表 + /// 数据 + /// 名称 + /// 说明 + private void AddTableRow(DataTable refTable, byte[]? data, string name, string describe) + { + if (data == null) + throw new ArgumentException(nameof(data)); + + refTable.Rows.Add( + new string[] + { + name, + data.Length.ToString(), + GetString(data), + GetLong(data).ToString(), + GetString(data,"ASCII"), + describe + }); + } + #endregion + + #region Table绘制 + /// + /// 获取PE信息 DataSet方式 + /// + /// 多个表 最后资源表 绘制成树结构TABLE + public DataSet? GetPETable() + { + if (OpenFile == false) + return null; + + var ds = new DataSet("PEFile"); + var a1 = TableDosHeader(); + if (a1 is not null) + ds.Tables.Add(a1); + var a2 = TablePEHeader(); + if (a2 is not null) + ds.Tables.Add(a2); + var a3 = TableOptionalHeader(); + if (a3 is not null) + ds.Tables.Add(a3); + var a4 = TableOptionalDirAttrib(); + if (a4 is not null) + ds.Tables.Add(a4); + var a5 = TableSectionData(); + if (a5 is not null) + ds.Tables.Add(a5); + + + var a6 = TableExportDirectory(); + var a7 = TableExportFunction(); + if (a6 is not null) + ds.Tables.Add(a6); + if (a7 is not null) + ds.Tables.Add(a7); + + var a8 = TableImportDirectory(); + var a9 = TableImportFunction(); + if (a8 is not null) + ds.Tables.Add(a8); + if (a9 is not null) + ds.Tables.Add(a9); + + var a10 = TableResourceDirectory(); + if (a10 is not null) + ds.Tables.Add(a10); + + return ds; + } + + private DataTable? TableDosHeader() + { + if (DosHeader is null) + return null; + + var returnTable = new DataTable("DosHeader FileStar{" + DosHeader.FileStarIndex.ToString() + "}FileEnd{" + DosHeader.FileEndIndex.ToString() + "}"); + returnTable.Columns.Add("Name"); + returnTable.Columns.Add("Size"); + returnTable.Columns.Add("Value16"); + returnTable.Columns.Add("Value10"); + returnTable.Columns.Add("ASCII"); + returnTable.Columns.Add("Describe"); + + AddTableRow(returnTable, DosHeader.e_magic, "e_magic", "魔术数字"); + AddTableRow(returnTable, DosHeader.e_cblp, "e_cblp", "文件最后页的字节数"); + AddTableRow(returnTable, DosHeader.e_cp, "e_cp", "文件页数"); + AddTableRow(returnTable, DosHeader.e_crlc, "e_crlc", "重定义元素个数"); + AddTableRow(returnTable, DosHeader.e_cparhdr, "e_cparhdr", "头部尺寸,以段落为单位"); + AddTableRow(returnTable, DosHeader.e_minalloc, "e_minalloc", "所需的最小附加段"); + AddTableRow(returnTable, DosHeader.e_maxalloc, "e_maxalloc", "所需的最大附加段"); + AddTableRow(returnTable, DosHeader.e_ss, "e_ss", "初始的SS值(相对偏移量)"); + AddTableRow(returnTable, DosHeader.e_sp, "e_sp", "初始的SP值"); + AddTableRow(returnTable, DosHeader.e_csum, "e_csum", "校验和"); + AddTableRow(returnTable, DosHeader.e_ip, "e_ip", "初始的IP值"); + AddTableRow(returnTable, DosHeader.e_cs, "e_cs", "初始的CS值(相对偏移量)"); + AddTableRow(returnTable, DosHeader.e_rva, "e_rva", ""); + AddTableRow(returnTable, DosHeader.e_fg, "e_fg", ""); + AddTableRow(returnTable, DosHeader.e_bl1, "e_bl1", ""); + AddTableRow(returnTable, DosHeader.e_oemid, "e_oemid", ""); + AddTableRow(returnTable, DosHeader.e_oeminfo, "e_oeminfo", ""); + AddTableRow(returnTable, DosHeader.e_bl2, "e_bl2", ""); + AddTableRow(returnTable, DosHeader.e_PESTAR, "e_PESTAR", "PE开始 +本结构的位置"); + + return returnTable; + } + + private DataTable? TablePEHeader() + { + if (PEHeader is null) + return null; + + var returnTable = new DataTable("PeHeader FileStar{" + PEHeader.FileStarIndex.ToString() + "}FileEnd{" + PEHeader.FileEndIndex.ToString() + "}"); + returnTable.Columns.Add("Name"); + returnTable.Columns.Add("Size"); + returnTable.Columns.Add("Value16"); + returnTable.Columns.Add("Value10"); + returnTable.Columns.Add("ASCII"); + returnTable.Columns.Add("Describe"); + + + AddTableRow(returnTable, PEHeader.Header, "Header", "PE文件标记"); + AddTableRow(returnTable, PEHeader.Machine, "Machine", "该文件运行所要求的CPU.对于Intel平台,该值是IMAGE_FILE_MACHINE_I386 (14Ch).我们尝试了LUEVELSMEYER的pe.txt声明的14Dh和14Eh,但Windows不能正确执行. "); + AddTableRow(returnTable, PEHeader.NumberOfSections, "NumberOfSections", "文件的节数目.如果我们要在文件中增加或删除一个节,就需要修改这个值."); + AddTableRow(returnTable, PEHeader.TimeDateStamp, "TimeDateStamp", "文件创建日期和时间. "); + AddTableRow(returnTable, PEHeader.PointerToSymbolTable, "PointerToSymbolTable", "用于调试. "); + AddTableRow(returnTable, PEHeader.NumberOfSymbols, "NumberOfSymbols", "用于调试. "); + AddTableRow(returnTable, PEHeader.SizeOfOptionalHeader, "SizeOfOptionalHeader", "指示紧随本结构之后的 OptionalHeader 结构大小,必须为有效值."); + AddTableRow(returnTable, PEHeader.Characteristics, "Characteristics", "关于文件信息的标记,比如文件是exe还是dll."); + + return returnTable; + } + + private DataTable? TableOptionalHeader() + { + if (OptionalHeader is null) + return null; + + var returnTable = new DataTable("OptionalHeader FileStar{" + OptionalHeader.FileStarIndex.ToString() + "}FileEnd{" + OptionalHeader.FileEndIndex.ToString() + "}"); + returnTable.Columns.Add("Name"); + returnTable.Columns.Add("Size"); + returnTable.Columns.Add("Value16"); + returnTable.Columns.Add("Value10"); + returnTable.Columns.Add("ASCII"); + returnTable.Columns.Add("Describe"); + + AddTableRow(returnTable, OptionalHeader.Magic, "Magic", "Magic 010B=普通可以执行,0107=ROM映像"); + AddTableRow(returnTable, OptionalHeader.MajorLinkerVersion, "MajorLinkerVersion", "主版本号"); + AddTableRow(returnTable, OptionalHeader.MinorLinkerVersion, "MinorLinkerVersion", "副版本号"); + AddTableRow(returnTable, OptionalHeader.SizeOfCode, "SizeOfCode", "代码段大小"); + AddTableRow(returnTable, OptionalHeader.SizeOfInitializedData, "SizeOfInitializedData", "已初始化数据大小"); + AddTableRow(returnTable, OptionalHeader.SizeOfUninitializedData, "SizeOfUninitializedData", "未初始化数据大小"); + AddTableRow(returnTable, OptionalHeader.AddressOfEntryPoint, "AddressOfEntryPoint", "执行将从这里开始(RVA)"); + AddTableRow(returnTable, OptionalHeader.BaseOfCode, "BaseOfCode", "代码基址(RVA)"); + AddTableRow(returnTable, OptionalHeader.ImageBase, "ImageBase", "数据基址(RVA)"); + if (_IsX86) + { + AddTableRow(returnTable, OptionalHeader.BaseOfData, "ImageFileCode", "映象文件基址"); + } + AddTableRow(returnTable, OptionalHeader.SectionAlignment, "SectionAlign", "区段列队"); + AddTableRow(returnTable, OptionalHeader.MajorOperatingSystemVersion, "MajorOSV", "文件列队"); + AddTableRow(returnTable, OptionalHeader.MinorOperatingSystemVersion, "MinorOSV", "操作系统主版本号"); + AddTableRow(returnTable, OptionalHeader.MajorImageVersion, "MajorImageVer", "映象文件主版本号"); + AddTableRow(returnTable, OptionalHeader.MinorImageVersion, "MinorImageVer", "映象文件副版本号"); + AddTableRow(returnTable, OptionalHeader.MajorSubsystemVersion, "MajorSV", "子操作系统主版本号"); + AddTableRow(returnTable, OptionalHeader.MinorSubsystemVersion, "MinorSV", "子操作系统副版本号"); + AddTableRow(returnTable, OptionalHeader.Win32VersionValue, "UNKNOW", "Win32版本值"); + AddTableRow(returnTable, OptionalHeader.SizeOfImage, "SizeOfImage", "映象文件大小"); + AddTableRow(returnTable, OptionalHeader.SizeOfHeards, "SizeOfHeards", "标志头大小"); + AddTableRow(returnTable, OptionalHeader.CheckSum, "CheckSum", "文件效验"); + AddTableRow(returnTable, OptionalHeader.Subsystem, "Subsystem", "子系统(映象文件)1本地 2WINDOWS-GUI 3WINDOWS-CUI 4 POSIX-CUI"); + AddTableRow(returnTable, OptionalHeader.DLLCharacteristics, "DLL_Characteristics", "DLL标记"); + AddTableRow(returnTable, OptionalHeader.SizeOfStackReserve, "Bsize", "保留栈的大小"); + AddTableRow(returnTable, OptionalHeader.SizeOfStackCommit, "TimeBsize", "初始时指定栈大小"); + AddTableRow(returnTable, OptionalHeader.SizeOfHeapReserve, "AucBsize", "保留堆的大小"); + AddTableRow(returnTable, OptionalHeader.SizeOfHeapCommit, "SizeOfBsize", "初始时指定堆大小"); + AddTableRow(returnTable, OptionalHeader.LoaderFlags, "FuckBsize", "加载器标志"); + AddTableRow(returnTable, OptionalHeader.NumberOfRvaAndSizes, "DirectCount", "数据目录数"); + + return returnTable; + } + + private DataTable? TableOptionalDirAttrib() + { + if (OptionalDirAttrib is null) + return null; + + var returnTable = new DataTable( + "OptionalDirAttrib FileStar{" + + OptionalDirAttrib.FileStarIndex.ToString() + + "}FileEnd{" + + OptionalDirAttrib.FileEndIndex.ToString() + + "}"); + returnTable.Columns.Add("Name"); + returnTable.Columns.Add("Size"); + returnTable.Columns.Add("Value16"); + returnTable.Columns.Add("Value10"); + returnTable.Columns.Add("ASCII"); + returnTable.Columns.Add("Describe"); + + var tableName = new Hashtable + { + { 0, "输出表" }, + { 1, "输入表" }, + { 2, "资源表" }, + { 3, "异常表" }, + { 4, "安全表" }, + { 5, "基部重定位表" }, + { 6, "调试数据" }, + { 7, "版权数据" }, + { 8, "全局PTR" }, + { 9, "TLS表" }, + { 10, "装入配置表" }, + { 11, "其他表1" }, + { 12, "其他表2" }, + { 13, "其他表3" }, + { 14, "其他表4" }, + { 15, "其他表5" } + }; + + for (int i = 0; i != OptionalDirAttrib.DirByte.Count; i++) + { + if (OptionalDirAttrib.DirByte[i] is not OptionalDirAttrib.DirAttrib MyDirByte) + continue; + + string? Name; + var tn = tableName[i]; + if (tn is not null) + Name = tn.ToString(); + else + Name = $"未知表{i}"; + AddTableRow(returnTable, MyDirByte.DirRva, Name!, "地址"); + AddTableRow(returnTable, MyDirByte.DirSize, "", "大小"); + } + return returnTable; + } + + private DataTable? TableSectionData() + { + if (SectionTable is null) + return null; + + var returnTable = new DataTable( + "SectionData FileStar{" + + SectionTable.FileStarIndex.ToString() + + "}FileEnd{" + + SectionTable.FileEndIndex.ToString() + + "}"); + returnTable.Columns.Add("Name"); + returnTable.Columns.Add("Size"); + returnTable.Columns.Add("Value16"); + returnTable.Columns.Add("Value10"); + returnTable.Columns.Add("ASCII"); + returnTable.Columns.Add("Describe"); + + for (int i = 0; i != SectionTable.Section.Count; i++) + { + if (SectionTable.Section[i] is not SectionTable.SectionData SectionDate) + continue; + + AddTableRow(returnTable, SectionDate.SectName, "SectName", "名字"); + AddTableRow(returnTable, SectionDate.VirtualAddress, "VirtualAddress", "虚拟内存地址"); + AddTableRow(returnTable, SectionDate.SizeOfRawDataRVA, "SizeOfRawDataRVA", "RVA偏移"); + AddTableRow(returnTable, SectionDate.SizeOfRawDataSize, "SizeOfRawDataSize", "RVA大小"); + AddTableRow(returnTable, SectionDate.PointerToRawData, "PointerToRawData", "指向RAW数据"); + AddTableRow(returnTable, SectionDate.PointerToRelocations, "PointerToRelocations", "指向定位号"); + AddTableRow(returnTable, SectionDate.PointerToLinenumbers, "PointerToLinenumbers", "指向行数"); + AddTableRow(returnTable, SectionDate.NumberOfRelocations, "NumberOfRelocations", "定位号"); + AddTableRow(returnTable, SectionDate.NumberOfLinenumbers, "NumberOfLinenumbers", "行数号"); + AddTableRow(returnTable, SectionDate.Characteristics, "Characteristics", "区段标记"); + } + return returnTable; + } + + private DataTable? TableExportDirectory() + { + if (ExportDirectory is null) + return null; + + var returnTable = new DataTable( + "ExportDirectory FileStar{" + + ExportDirectory.FileStarIndex.ToString() + + "}FileEnd{" + + ExportDirectory.FileEndIndex.ToString() + + "}"); + returnTable.Columns.Add("Name"); + returnTable.Columns.Add("Size"); + returnTable.Columns.Add("Value16"); + returnTable.Columns.Add("Value10"); + returnTable.Columns.Add("ASCII"); + returnTable.Columns.Add("Describe"); + + AddTableRow(returnTable, ExportDirectory.Characteristics, "Characteristics", "一个保留字段,目前为止值为0."); + AddTableRow(returnTable, ExportDirectory.TimeDateStamp, "TimeDateStamp", "产生的时间."); + AddTableRow(returnTable, ExportDirectory.MajorVersion, "MajorVersion", "主版本号"); + AddTableRow(returnTable, ExportDirectory.MinorVersion, "MinorVersion", "副版本号"); + AddTableRow(returnTable, ExportDirectory.Name, "Name", "一个RVA,指向一个dll的名称的ascii字符串."); + AddTableRow(returnTable, ExportDirectory.Base, "Base", "输出函数的起始序号.一般为1."); + AddTableRow(returnTable, ExportDirectory.NumberOfFunctions, "NumberOfFunctions", "输出函数入口地址的数组 中的元素个数."); + AddTableRow(returnTable, ExportDirectory.NumberOfNames, "NumberOfNames", "输出函数名的指针的数组 中的元素个数,也是输出函数名对应的序号的数组 中的元素个数."); + AddTableRow(returnTable, ExportDirectory.AddressOfFunctions, "AddressOfFunctions", "一个RVA,指向输出函数入口地址的数组."); + AddTableRow(returnTable, ExportDirectory.AddressOfNames, "AddressOfNames", "一个RVA,指向输出函数名的指针的数组."); + AddTableRow(returnTable, ExportDirectory.AddressOfNameOrdinals, "AddressOfNameOrdinals", "一个RVA,指向输出函数名对应的序号的数组."); + + return returnTable; + } + private DataTable? TableExportFunction() + { + if (ExportDirectory is null) + return null; + + var returnTable = new DataTable("ExportFunctionList"); + returnTable.Columns.Add("Name"); + returnTable.Columns.Add("Size"); + returnTable.Columns.Add("Value16"); + returnTable.Columns.Add("Value10"); + returnTable.Columns.Add("ASCII"); + returnTable.Columns.Add("Describe"); + + for (int i = 0; i != ExportDirectory.FunctionNamesByte.Count; i++) + { + AddTableRow(returnTable, + ExportDirectory.FunctionNamesByte[i], + "Name", + "_ExportDirectory.Name-Sect.SizeOfRawDataRVA+Sect.PointerToRawData"); + } + + for (int i = 0; i != ExportDirectory.AddressOfNamesList.Count; i++) + { + if (ExportDirectory.AddressOfNamesList[i] is not byte[] a) + continue; + AddTableRow(returnTable, a, "NamesList", ""); + } + + for (int i = 0; i != ExportDirectory.AddressOfFunctionsList.Count; i++) + { + if (ExportDirectory.AddressOfFunctionsList[i] is not byte[] a) + continue; + AddTableRow(returnTable, a, "Functions", ""); + } + + for (int i = 0; i != ExportDirectory.AddressOfNameOrdinalsList.Count; i++) + { + if (ExportDirectory.AddressOfNameOrdinalsList[i] is not byte[] a) + continue; + AddTableRow(returnTable, a, "NameOrdinals", ""); + } + return returnTable; + } + private DataTable? TableImportDirectory() + { + if (ImportDirectory is null) + return null; + + var returnTable = new DataTable("ImportDirectory FileStar{" + ImportDirectory.FileStarIndex.ToString() + "}FileEnd{" + ImportDirectory.FileEndIndex.ToString() + "}"); + returnTable.Columns.Add("Name"); + returnTable.Columns.Add("Size"); + returnTable.Columns.Add("Value16"); + returnTable.Columns.Add("Value10"); + returnTable.Columns.Add("ASCII"); + returnTable.Columns.Add("Describe"); + + for (int i = 0; i != ImportDirectory.ImportList.Count; i++) + { + if (ImportDirectory.ImportList[i] is not ImportDirectory.ImportDate ImportByte) + continue; + + AddTableRow(returnTable, ImportByte.DLLName, "输入DLL名称", "**********"); + AddTableRow(returnTable, ImportByte.OriginalFirstThunk, "OriginalFirstThunk", "这里实际上保存着一个RVA,这个RVA指向一个DWORD数组,这个数组可以叫做输入查询表.每个数组元素,或者叫一个表项,保存着一个指向函数名的RVA或者保存着一个函数的序号."); + AddTableRow(returnTable, ImportByte.TimeDateStamp, "TimeDateStamp", "当这个值为0的时候,表明还没有bind.不为0的话,表示已经bind过了.有关bind的内容后面介绍."); + AddTableRow(returnTable, ImportByte.ForwarderChain, "ForwarderChain", ""); + AddTableRow(returnTable, ImportByte.Name, "Name", "一个RVA,这个RVA指向一个ascii以空字符结束的字符串,这个字符串就是本结构对应的dll文件的名字."); + AddTableRow(returnTable, ImportByte.FirstThunk, "FirstThunk", "一个RVA,这个RVA指向一个DWORD数组,这个数组可以叫输入地址表.如果bind了的话,这个数组的每个元素,就是一个输入函数的入口地址."); + } + + return returnTable; + } + private DataTable? TableImportFunction() + { + if (ImportDirectory is null) + return null; + + var returnTable = new DataTable("ImportFunctionList"); + returnTable.Columns.Add("Name"); + returnTable.Columns.Add("Size"); + returnTable.Columns.Add("Value16"); + returnTable.Columns.Add("Value10"); + returnTable.Columns.Add("ASCII"); + returnTable.Columns.Add("Describe"); + + for (int i = 0; i != ImportDirectory.ImportList.Count; i++) + { + if (ImportDirectory.ImportList[i] is not ImportDirectory.ImportDate ImportByte) + continue; + + AddTableRow(returnTable, ImportByte.DLLName, "DLL-Name", "**********"); + + for (int z = 0; z != ImportByte.DLLFunctionList.Count; z++) + { + if (ImportByte.DLLFunctionList[z] is not ImportDirectory.ImportDate.FunctionList Function) + continue; + + AddTableRow(returnTable, Function.FunctionName, "FunctionName", ""); + AddTableRow(returnTable, Function.FunctionHead, "FunctionHead", ""); + AddTableRow(returnTable, Function.OriginalFirst, "OriginalFirstThunk", ""); + } + } + return returnTable; + } + private DataTable? TableResourceDirectory() + { + if (ResourceDirectory is null) + return null; + var returnTable = new DataTable("ResourceDirectory FileStar{" + ResourceDirectory.FileStarIndex.ToString() + "}FileEnd{" + ResourceDirectory.FileEndIndex.ToString() + "}"); + returnTable.Columns.Add("GUID"); + returnTable.Columns.Add("Text"); + returnTable.Columns.Add("ParentID"); + AddResourceDirectoryRow(returnTable, ResourceDirectory, ""); + return returnTable; + } + private void AddResourceDirectoryRow(DataTable myTable, ResourceDirectory Node, string parentID) + { + string Name = ""; + if (Node.Name is not null) + Name = GetString(Node.Name, "UNICODE"); + + for (int i = 0; i != Node.EntryList.Count; i++) + { + if (Node.EntryList[i] is not ResourceDirectory.DirectoryEntry Entry) + continue; + + long ID = GetLong(Entry.Name); + + string GUID = Guid.NewGuid().ToString(); + + string IDNAME = "ID{" + ID + "}"; + if (Name.Length != 0) + IDNAME += "Name{" + Name + "}"; + + if (parentID.Length == 0) + { + IDNAME += ID switch + { + 1 => "Type{Cursor}", + 2 => "Type{Bitmap}", + 3 => "Type{Icon}", + 4 => "Type{Cursor}", + 5 => "Type{Menu}", + 6 => "Type{Dialog}", + 7 => "Type{String Table}", + 8 => "Type{Font Directory}", + 9 => "Type{Font}", + 10 => "Type{Accelerators}", + 11 => "Type{Unformatted}", + 12 => "Type{Message Table}", + 13 => "Type{Group Cursor}", + 14 => "Type{Group Icon}", + 15 => "Type{Information}", + 16 => "Type{Version}", + _ => "Type{未定义}", + }; + } + + myTable.Rows.Add(new string[] { GUID, IDNAME, parentID }); + + for (int z = 0; z != Entry.DataEntryList.Count; z++) + { + if (Entry.DataEntryList[z] is not ResourceDirectory.DirectoryEntry.DataEntry Data) + continue; + + string Text = "Address{" + GetString(Data.ResourRVA) + "} Size{" + GetString(Data.ResourSize) + "} FileBegin{" + Data.FileStarIndex.ToString() + "-" + Data.FileEndIndex.ToString() + "}"; + + myTable.Rows.Add(new string[] { Guid.NewGuid().ToString(), Text, GUID }); + } + + for (int z = 0; z != Entry.NodeDirectoryList.Count; z++) + { + if (Entry.NodeDirectoryList[z] is not ResourceDirectory a) + continue; + AddResourceDirectoryRow(myTable, a, GUID); + } + } + } + #endregion +} + +#region 类 +/// +/// DOS文件都MS开始 +/// +public class DosHeader // IMAGE_DOS_HEADER +{ + public byte[] e_magic = new byte[2]; // 魔术数字 + public byte[] e_cblp = new byte[2]; // 文件最后页的字节数 + public byte[] e_cp = new byte[2]; // 文件页数 + public byte[] e_crlc = new byte[2]; // 重定义元素个数 + public byte[] e_cparhdr = new byte[2]; // 头部尺寸,以段落为单位 + public byte[] e_minalloc = new byte[2]; // 所需的最小附加段 + public byte[] e_maxalloc = new byte[2]; // 所需的最大附加段 + public byte[] e_ss = new byte[2]; // 初始的SS值(相对偏移量) + public byte[] e_sp = new byte[2]; // 初始的SP值 + public byte[] e_csum = new byte[2]; // 校验和 + public byte[] e_ip = new byte[2]; // 初始的IP值 + public byte[] e_cs = new byte[2]; // 初始的CS值(相对偏移量) + public byte[] e_rva = new byte[2]; + public byte[] e_fg = new byte[2]; + public byte[] e_bl1 = new byte[8]; + public byte[] e_oemid = new byte[2]; + public byte[] e_oeminfo = new byte[2]; + public byte[] e_bl2 = new byte[20]; + public byte[] e_PESTAR = new byte[2]; // PE开始 +自己的位置........重点 + + public long FileStarIndex = 0; + public long FileEndIndex = 0; +} + +/// +/// DOS程序 提示 +/// +public class DosStub +{ + public byte[] DosStubData; + public DosStub(long Size) + { + DosStubData = new byte[Size]; + } + public long FileStarIndex = 0; + public long FileEndIndex = 0; +} + +/// +/// PE文件头 +/// +public class PEHeader // IMAGE_FILE_HEADER +{ + public byte[] Header = new byte[4];// PE文件标记 + public byte[] Machine = new byte[2];// 该文件运行所要求的CPU.对于Intel平台,该值是IMAGE_FILE_MACHINE_I386 (14Ch).我们尝试了LUEVELSMEYER的pe.txt声明的14Dh和14Eh,但Windows不能正确执行.看起来,除了禁止程序执行之外,本域对我们来说用处不大. + public byte[] NumberOfSections = new byte[2];// 文件的节数目.如果我们要在文件中增加或删除一个节,就需要修改这个值. + public byte[] TimeDateStamp = new byte[4];// 文件创建日期和时间.我们不感兴趣. + public byte[] PointerToSymbolTable = new byte[4];// 用于调试. + public byte[] NumberOfSymbols = new byte[4];// 用于调试. + public byte[] SizeOfOptionalHeader = new byte[2];// 指示紧随本结构之后的 OptionalHeader 结构大小,必须为有效值. IMAGE_OPTIONAL_HEADER32 结构大小 + public byte[] Characteristics = new byte[2];// 关于文件信息的标记,比如文件是exe还是dll. + + public long FileStarIndex = 0; + public long FileEndIndex = 0; +} + +/// +/// PE头扩展 +/// +// https://docs.microsoft.com/en-us/windows/win32/api/winnt/ns-winnt-image_optional_header32 // IMAGE_OPTIONAL_HEADER32 +// https://docs.microsoft.com/en-us/windows/win32/api/winnt/ns-winnt-image_optional_header64 // IMAGE_OPTIONAL_HEADER64 +public class OptionalHeader +{ + public byte[] Magic = new byte[2]; // Magic 010B=普通可以执行,0107=ROM映像 + public byte[] MajorLinkerVersion = new byte[1]; // 主版本号 + public byte[] MinorLinkerVersion = new byte[1]; // 副版本号 + public byte[] SizeOfCode = new byte[4]; // 代码段大小 + public byte[] SizeOfInitializedData = new byte[4]; // 已初始化数据大小 + public byte[] SizeOfUninitializedData = new byte[4]; // 未初始化数据大小 + public byte[] AddressOfEntryPoint = new byte[4]; // 执行将从这里开始(RVA).........入口点指向,dll填充0 + public byte[] BaseOfCode = new byte[4]; // 代码基址(RVA) + + public byte[] BaseOfData = new byte[4]; // 映象文件基址 + + public byte[] ImageBase = new byte[4]; // 数据基址(RVA) + + public byte[] SectionAlignment = new byte[4]; // 区段列队..... + public byte[] FileAlignment = new byte[4]; // 文件列队 + + public byte[] MajorOperatingSystemVersion = new byte[2]; // 操作系统主版本号 + public byte[] MinorOperatingSystemVersion = new byte[2]; // 操作系统副版本号 + public byte[] MajorImageVersion = new byte[2]; // 映象文件主版本号 + public byte[] MinorImageVersion = new byte[2]; // 映象文件副版本号 + public byte[] MajorSubsystemVersion = new byte[2]; // 子操作系统主版本号 + public byte[] MinorSubsystemVersion = new byte[2]; // 子操作系统副版本号 + public byte[] Win32VersionValue = new byte[4]; // Win32版本值 + public byte[] SizeOfImage = new byte[4]; // 映象文件大小 + public byte[] SizeOfHeards = new byte[4]; // 标志头大小 + public byte[] CheckSum = new byte[4]; // 文件效验 + public byte[] Subsystem = new byte[2]; // 子系统(映象文件)1本地 2WINDOWS-GUI 3WINDOWS-CUI 4 POSIX-CUI + public byte[] DLLCharacteristics = new byte[2]; // DLL标记 + + public byte[] SizeOfStackReserve = new byte[4]; // 保留栈的大小 + public byte[] SizeOfStackCommit = new byte[4]; // 初始时指定栈大小 + public byte[] SizeOfHeapReserve = new byte[4]; // 保留堆的大小 + public byte[] SizeOfHeapCommit = new byte[4]; // 初始时指定堆大小 + public byte[] LoaderFlags = new byte[4]; // 加载器标志 + public byte[] NumberOfRvaAndSizes = new byte[4]; // 数据目录数 + + public long FileStarIndex = 0; + public long FileEndIndex = 0; + + public OptionalHeader(bool is32) + { + if (!is32) + { + // X64没有了,但是为了代码保留修改幅度不大,所以置0 + BaseOfData = new byte[0];// x64必须置于0 + // x64长度增加的 + int ulonglong = 8; + ImageBase = new byte[ulonglong]; // 数据基址(RVA) + SizeOfStackReserve = new byte[ulonglong]; // 保留栈的大小 + SizeOfStackCommit = new byte[ulonglong]; // 初始时指定栈大小 + SizeOfHeapReserve = new byte[ulonglong]; // 保留堆的大小 + SizeOfHeapCommit = new byte[ulonglong]; // 初始时指定堆大小 + } + } +} + +/// +/// 目录结构 +/// +public class OptionalDirAttrib +{ + public ArrayList DirByte = new(); + public class DirAttrib + { + public byte[] DirRva = new byte[4]; // 地址 + public byte[] DirSize = new byte[4]; // 大小 + } + public long FileStarIndex = 0; + public long FileEndIndex = 0; +} + +/// +/// 节表 +/// +public class SectionTable +{ + public ArrayList Section = new(); + public class SectionData + { + public byte[] SectName = new byte[8]; // 名字 + public byte[] VirtualAddress = new byte[4]; // 虚拟内存地址 + public byte[] SizeOfRawDataRVA = new byte[4]; // RVA偏移 + public byte[] SizeOfRawDataSize = new byte[4]; // RVA大小 + public byte[] PointerToRawData = new byte[4]; // 指向RAW数据 + public byte[] PointerToRelocations = new byte[4]; // 指向定位号 + public byte[] PointerToLinenumbers = new byte[4]; // 指向行数 + public byte[] NumberOfRelocations = new byte[2]; // 定位号 + public byte[] NumberOfLinenumbers = new byte[2]; // 行数号 + public byte[] Characteristics = new byte[4]; // 区段标记 + } + + public long FileStarIndex = 0; + public long FileEndIndex = 0; +} + +/// +/// 输出表 +/// +public class ExportDirectory +{ + public byte[] Characteristics = new byte[4]; // 一个保留字段,目前为止值为0. + public byte[] TimeDateStamp = new byte[4]; // 产生的时间 + public byte[] MajorVersion = new byte[2]; // 主版本号 + public byte[] MinorVersion = new byte[2]; // 副版本号 + public byte[] Name = new byte[4]; // 一个RVA,指向一个dll的名称的ascii字符串 + public byte[] Base = new byte[4]; // 输出函数的起始序号.一般为1 + public byte[] NumberOfFunctions = new byte[4]; // 输出函数入口地址的数组中的元素个数 + public byte[] NumberOfNames = new byte[4]; // 输出函数名的指针的数组中的元素个数,也是输出函数名对应的序号的数组中的元素个数 + public byte[] AddressOfFunctions = new byte[4]; // 一个RVA,指向输出函数入口地址的数组 + public byte[] AddressOfNames = new byte[4]; // 一个RVA,指向输出函数名的指针的数组 + public byte[] AddressOfNameOrdinals = new byte[4]; // 一个RVA,指向输出函数名对应的序号的数组 + + public ArrayList AddressOfFunctionsList = new(); + public ArrayList AddressOfNamesList = new(); + public ArrayList AddressOfNameOrdinalsList = new(); + /// + /// 函数指针名称集合 + /// + public List FunctionNamesByte = new(); + public long FileStarIndex = 0; + public long FileEndIndex = 0; + + /// + /// 获取函数名 + /// + public HashSet FunctionNames() + { + HashSet names = new(); + for (int i = 0; i < FunctionNamesByte.Count; i++) + names.Add(Encoding.Default.GetString(FunctionNamesByte[i])); + return names; + } +} + + +/// +/// 输入表 +/// +public class ImportDirectory +{ + public ArrayList ImportList = new(); + + public class ImportDate + { + public byte[] OriginalFirstThunk = new byte[4]; // 这里实际上保存着一个RVA,这个RVA指向一个DWORD数组,这个数组可以叫做输入查询表.每个数组元素,或者叫一个表项,保存着一个指向函数名的RVA或者保存着一个函数的序号. + public byte[] TimeDateStamp = new byte[4]; // 当这个值为0的时候,表明还没有bind.不为0的话,表示已经bind过了.有关bind的内容后面介绍. + public byte[] ForwarderChain = new byte[4]; + public byte[] Name = new byte[4]; // 一个RVA,这个RVA指向一个ascii以空字符结束的字符串,这个字符串就是本结构对应的dll文件的名字. + public byte[] FirstThunk = new byte[4]; // 一个RVA,这个RVA指向一个DWORD数组,这个数组可以叫输入地址表.如果bind了的话,这个数组的每个元素,就是一个输入函数的入口地址. + + public byte[]? DLLName; // DLL名称 + public ArrayList DLLFunctionList = new(); + public class FunctionList + { + public byte[] OriginalFirst = new byte[4]; + public byte[]? FunctionName; + public byte[] FunctionHead = new byte[2]; + } + } + public long FileStarIndex = 0; + public long FileEndIndex = 0; +} + +/// +/// 资源表 +/// +public class ResourceDirectory +{ + public byte[] Characteristics = new byte[4]; + public byte[] TimeDateStamp = new byte[4]; + public byte[] MajorVersion = new byte[2]; + public byte[] MinorVersion = new byte[2]; + public byte[] NumberOfNamedEntries = new byte[2]; + public byte[] NumberOfIdEntries = new byte[2]; + public byte[]? Name; + public ArrayList EntryList = new(); + + public class DirectoryEntry + { + public byte[] Name = new byte[4]; + public byte[] Id = new byte[4]; + public ArrayList DataEntryList = new(); + public ArrayList NodeDirectoryList = new(); + + public class DataEntry + { + public byte[] ResourRVA = new byte[4]; + public byte[] ResourSize = new byte[4]; + public byte[] ResourTest = new byte[4]; + public byte[] ResourWen = new byte[4]; + + public long FileStarIndex = 0; + public long FileEndIndex = 0; + } + } + + public long FileStarIndex = 0; + public long FileEndIndex = 0; +} +#endregion \ No newline at end of file -- Gitee From 4a0ae397b80f4b3766942baea2c82bf0b44dace3 Mon Sep 17 00:00:00 2001 From: liuqihong <540762622@qq.com> Date: Fri, 28 Oct 2022 12:28:26 +0800 Subject: [PATCH 585/675] =?UTF-8?q?=E5=8A=A0=E5=85=A5=E5=90=8C=E6=AD=A5?= =?UTF-8?q?=E5=91=BD=E4=BB=A4=E7=9A=84=E6=96=B9=E6=B3=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../ExtensionMethod/EditorEx.cs | 46 +++--- .../Runtime/FileOpenMode.cs | 8 +- src/IFoxCAD.Cad.Shared/Runtime/PE/PostCmd.cs | 134 ++++++++++++++++++ 3 files changed, 157 insertions(+), 31 deletions(-) create mode 100644 src/IFoxCAD.Cad.Shared/Runtime/PE/PostCmd.cs diff --git a/src/IFoxCAD.Cad.Shared/ExtensionMethod/EditorEx.cs b/src/IFoxCAD.Cad.Shared/ExtensionMethod/EditorEx.cs index 049c27c..12f7ee1 100644 --- a/src/IFoxCAD.Cad.Shared/ExtensionMethod/EditorEx.cs +++ b/src/IFoxCAD.Cad.Shared/ExtensionMethod/EditorEx.cs @@ -1020,14 +1020,12 @@ public static PromptResult GetString(this Editor ed, string Message, string Defa [System.Security.SuppressUnmanagedCodeSecurity]// 初始化默认值 static extern int AcedEvaluateLisp(string lispLine, out IntPtr result); -#pragma warning disable CA2101 // 指定对 P/Invoke 字符串参数进行封送处理 #if NET35 [DllImport("acad.exe", #else [DllImport("accore.dll", #endif CharSet = CharSet.Auto, CallingConvention = CallingConvention.Cdecl, EntryPoint = "ads_queueexpr")] -#pragma warning restore CA2101 // 指定对 P/Invoke 字符串参数进行封送处理 static extern int Ads_queueexpr(string strExpr); public enum RunLispFlag : byte @@ -1037,6 +1035,24 @@ public enum RunLispFlag : byte SendStringToExecute = 4, } + /* + * 测试命令: + * [CommandMethod(nameof(CmdTest_RunLisp))] + * public static void CmdTest_RunLisp() + * { + * var res = RunLisp("(setq abc 10)"); + * } + * 调用方式: + * (command "CmdTest_RunLisp1") + * bug说明: + * AcedEvaluateLisp 接口 + * 在高版本调用时候没有运行成功,使得 !abc 没有值 + * 在cad08成功,此bug与CommandFlags无关 + * 解决方案: + * 0x01 用异步接口,但是这样是显式调用了: + * (setq thisdrawing (vla-get-activedocument (vlax-get-acad-object)))(vla-SendCommand thisdrawing "CmdTest_RunLisp1 ") + * 0x02 使用 Ads_queueexpr 接口 + */ /// /// 发送lisp语句字符串到cad执行 /// @@ -1044,35 +1060,13 @@ public enum RunLispFlag : byte /// lisp语句 /// 运行方式 /// 缓冲结果,返回值 -#pragma warning disable IDE0060 // 删除未使用的参数 - public static ResultBuffer? RunLisp(this Editor ed, - string lispCode, - RunLispFlag flag = RunLispFlag.AdsQueueexpr) -#pragma warning restore IDE0060 // 删除未使用的参数 + public static ResultBuffer? RunLisp(this Editor ed, string lispCode, RunLispFlag flag = RunLispFlag.AdsQueueexpr) { - /* - * 测试命令: - * [CommandMethod(nameof(CmdTest_RunLisp))] - * public static void CmdTest_RunLisp() - * { - * var res = RunLisp("(setq abc 10)"); - * } - * 调用方式: - * (command "CmdTest_RunLisp1") - * bug说明: - * AcedEvaluateLisp 接口 - * 在高版本调用时候没有运行成功,使得 !abc 没有值 - * 在cad08成功,此bug与CommandFlags无关 - * 解决方案: - * 0x01 用异步接口,但是这样是显式调用了: - * (setq thisdrawing (vla-get-activedocument (vlax-get-acad-object)))(vla-SendCommand thisdrawing "CmdTest_RunLisp1 ") - * 0x02 使用 Ads_queueexpr 接口 - */ if ((flag & RunLispFlag.AdsQueueexpr) == RunLispFlag.AdsQueueexpr) { // 这个在08/12发送lisp不会出错,但是发送bo命令出错了. // 0x01 设置 CommandFlags.Session 可以同步, - // 0x02 自执行发送lisp都是异步,(用来发送 含有(command)的lisp的) + // 0x02 自执行发送lisp都是异步,用来发送 含有(command)的lisp的 _ = Ads_queueexpr(lispCode + "\n"); } if ((flag & RunLispFlag.AcedEvaluateLisp) == RunLispFlag.AcedEvaluateLisp) diff --git a/src/IFoxCAD.Cad.Shared/Runtime/FileOpenMode.cs b/src/IFoxCAD.Cad.Shared/Runtime/FileOpenMode.cs index ad825f4..55dcefd 100644 --- a/src/IFoxCAD.Cad.Shared/Runtime/FileOpenMode.cs +++ b/src/IFoxCAD.Cad.Shared/Runtime/FileOpenMode.cs @@ -1,5 +1,3 @@ -using IFoxCAD.Cad; - #if ac2008 // NET35 namespace Autodesk.AutoCAD.DatabaseServices { @@ -76,11 +74,11 @@ public static void SetFocusToDwgView() // -------->msctls_statusbar32 // -------->DwgView var docW = Acap.DocumentManager.MdiActiveDocument.Window.Handle; - var msctls_statusbar32 = WindowsAPI.GetTopWindow(docW); - window = WindowsAPI.GetWindow(msctls_statusbar32, 2U); + var msctls_statusbar32 = IFoxCAD.Cad.WindowsAPI.GetTopWindow(docW); + window = IFoxCAD.Cad.WindowsAPI.GetWindow(msctls_statusbar32, 2U); } if (window != IntPtr.Zero) - WindowsAPI.SetFocus(window); + IFoxCAD.Cad.WindowsAPI.SetFocus(window); } } } diff --git a/src/IFoxCAD.Cad.Shared/Runtime/PE/PostCmd.cs b/src/IFoxCAD.Cad.Shared/Runtime/PE/PostCmd.cs new file mode 100644 index 0000000..245f470 --- /dev/null +++ b/src/IFoxCAD.Cad.Shared/Runtime/PE/PostCmd.cs @@ -0,0 +1,134 @@ +namespace IFoxCAD.Cad; + +public class PostCmd +{ + /* + *[DllImport("accore.dll", EntryPoint = "acedCommand")] + *static extern int AcedCommand(IntPtr vlist); + */ + delegate int DelegateAcedCommand(IntPtr strExpr); + static DelegateAcedCommand? _AcedCommand; + /// + /// 发送命令(同步) + /// + public static int AcedCommand(IntPtr strExpr) + { + if (_AcedCommand is null) + { + string str = "acedCommand"; + _AcedCommand = + AcadPeInfo.GetDelegate(str, AcadPeEnum.All); + } + if (_AcedCommand is null) + return -1; + return _AcedCommand.Invoke(strExpr);// 调用方法 + } + + + /* + *[DllImport("acad.exe", CharSet = CharSet.Auto, CallingConvention = CallingConvention.Cdecl, + * EntryPoint = "?acedPostCommand@@YAHPB_W@Z")] + *public static extern int AcedPostCommand(string strExpr); + */ + delegate int DelegateAcedPostCommand(byte[] strExpr); + static DelegateAcedPostCommand? _AcedPostCommand; + /// + /// 发送命令(同步) + /// 这个可以在多线程发送 + /// + public static int AcedPostCommand(string strExpr) + { + if (_AcedPostCommand is null) + { + string str = "acedPostCommand"; + _AcedPostCommand = + AcadPeInfo.GetDelegate(str, AcadPeEnum.All); + } + // 不然到CAD之后会乱码 + byte[] bytes = Encoding.Unicode.GetBytes(strExpr); + + if (_AcedPostCommand is null) + return -1; + return _AcedPostCommand.Invoke(bytes);// 调用方法 + } + + delegate int DelegateAcedInvoke(byte[] strExpr); + static DelegateAcedInvoke? _AcedInvoke; + /// + /// 发送命令(同步) + /// + public static int AcedInvoke(string strExpr) + { + if (_AcedInvoke is null) + { + string str = "acedInvoke"; + _AcedInvoke = + AcadPeInfo.GetDelegate(str, AcadPeEnum.All); + } + // 不然到CAD之后会乱码 + byte[] bytes = Encoding.Unicode.GetBytes(strExpr); + + if (_AcedInvoke is null) + return -1; + return _AcedInvoke.Invoke(bytes);// 调用方法 + } + + + /* +#if NET35 || NET40 + [DllImport("acad.exe", EntryPoint = "acedCmd", CharSet = CharSet.Auto, CallingConvention = CallingConvention.Cdecl)] +#else + // cad2015的AcedCmd改为AcedCmdS.参数也改了,不过不影响pe读取,accore.dll是2013版本后才开始改 + [DllImport("accore.dll", EntryPoint = "acedCmdS", CharSet = CharSet.Auto, CallingConvention = CallingConvention.Cdecl)] + static extern int AcedCmd(IntPtr rbp, bool forFutureUse = false, IntPtr pForFutureUse = IntPtr.Zero); +#endif + static extern int AcedCmd(IntPtr rbp); + public static int AcedCmd(ResultBuffer args) + { + if (Acap.DocumentManager.IsApplicationContext) + return 0; + else + return AcedCmd(args.UnmanagedObject); + } + */ + delegate int DelegateAcedCmd(IntPtr rb); + static DelegateAcedCmd? _AcedCmd; + /// + /// 发送命令(同步)如果2015.+这里报错,那么表示vs需要提权测试 + /// + public static int AcedCmd(ResultBuffer args) + { + if (Acap.DocumentManager.IsApplicationContext) + return 0; + if (_AcedCmd is null) + { + string str = "acedCmd"; + if (Acap.Version.Major >= 20)// 2015.+ + str += "S"; + _AcedCmd = + AcadPeInfo.GetDelegate(str, AcadPeEnum.All); + } + if (_AcedCmd is null) + return -1; + var reNum = _AcedCmd.Invoke(args.UnmanagedObject); + if (reNum != 5100)// 5100正确 + throw new ArgumentException("发送命令出错,是否vs权限不足?"); + return reNum; + } + + /// + /// 发送命令(异步) + /// + public static object? ActiveCmd(string str) + { + object[] commandArray = { str + "\n" }; +#if zcad + var App = Acap.ZcadApplication; +#else + var App = Acap.AcadApplication; +#endif + // activeDocument 加载lisp第二个文档有问题,似乎要切换了才能 + var activeDocument = App.GetType().InvokeMember("ActiveDocument", BindingFlags.GetProperty, null, App, null); + return activeDocument?.GetType().InvokeMember("SendCommand", BindingFlags.InvokeMethod, null, activeDocument, commandArray); + } +} \ No newline at end of file -- Gitee From 25bc24678e7a570abc8f5a1973197074f38281a5 Mon Sep 17 00:00:00 2001 From: liuqihong <540762622@qq.com> Date: Fri, 28 Oct 2022 12:29:32 +0800 Subject: [PATCH 586/675] =?UTF-8?q?=E5=B7=A5=E7=A8=8B=E5=8A=A0=E5=85=A5?= =?UTF-8?q?=E5=90=8C=E6=AD=A5=E5=91=BD=E4=BB=A4=E6=96=87=E4=BB=B6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/IFoxCAD.Cad.Shared/IFoxCAD.Cad.Shared.projitems | 1 + 1 file changed, 1 insertion(+) diff --git a/src/IFoxCAD.Cad.Shared/IFoxCAD.Cad.Shared.projitems b/src/IFoxCAD.Cad.Shared/IFoxCAD.Cad.Shared.projitems index b26dc9d..6e613c7 100644 --- a/src/IFoxCAD.Cad.Shared/IFoxCAD.Cad.Shared.projitems +++ b/src/IFoxCAD.Cad.Shared/IFoxCAD.Cad.Shared.projitems @@ -51,6 +51,7 @@ + -- Gitee From e913a16260e52e3ea1b96b29b06493579d21950a Mon Sep 17 00:00:00 2001 From: liuqihong <540762622@qq.com> Date: Fri, 28 Oct 2022 14:19:41 +0800 Subject: [PATCH 587/675] =?UTF-8?q?=E5=8A=A0=E5=85=A5DBmod=E5=8F=98?= =?UTF-8?q?=E9=87=8F=E7=9A=84=E4=BF=AE=E6=94=B9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../IFoxCAD.Cad.Shared.projitems | 1 + .../Runtime/PE/AcadPeInfo.cs | 47 ++++++++---- src/IFoxCAD.Cad.Shared/Runtime/PE/DBmod.cs | 72 +++++++++++++++++++ src/IFoxCAD.Cad.Shared/Runtime/PE/PostCmd.cs | 8 +-- tests/TestShared/TestEnv.cs | 2 + 5 files changed, 114 insertions(+), 16 deletions(-) create mode 100644 src/IFoxCAD.Cad.Shared/Runtime/PE/DBmod.cs diff --git a/src/IFoxCAD.Cad.Shared/IFoxCAD.Cad.Shared.projitems b/src/IFoxCAD.Cad.Shared/IFoxCAD.Cad.Shared.projitems index 6e613c7..7700de1 100644 --- a/src/IFoxCAD.Cad.Shared/IFoxCAD.Cad.Shared.projitems +++ b/src/IFoxCAD.Cad.Shared/IFoxCAD.Cad.Shared.projitems @@ -51,6 +51,7 @@ + diff --git a/src/IFoxCAD.Cad.Shared/Runtime/PE/AcadPeInfo.cs b/src/IFoxCAD.Cad.Shared/Runtime/PE/AcadPeInfo.cs index 8455623..051302d 100644 --- a/src/IFoxCAD.Cad.Shared/Runtime/PE/AcadPeInfo.cs +++ b/src/IFoxCAD.Cad.Shared/Runtime/PE/AcadPeInfo.cs @@ -8,7 +8,8 @@ public enum AcadPeEnum : byte { AcadExe = 1, AccoreDll = 2, - All = AcadExe | AccoreDll, + Acdb = 4, + ExeAndCore = AcadExe | AccoreDll, } // 这里的枚举对应 GetMethodException 错误值 @@ -31,9 +32,9 @@ public static PeInfo? PeForAcadExe { if (_PeForAcadExe is null) { - // 拿到acad.exe路径,获取所有的函数名 - var acadexeFullName = Process.GetCurrentProcess().MainModule.FileName; - _PeForAcadExe = new PeInfo(acadexeFullName); + // 获取此acad.exe获取所有的函数名 + var file = Process.GetCurrentProcess().MainModule.FileName; + _PeForAcadExe = new PeInfo(file); } return _PeForAcadExe; } @@ -46,16 +47,33 @@ public static PeInfo? PeForAccoreDll { if (_PeForAccoreDll is null) { - // 拿到accore.dll路径,获取所有的函数名 - var acadexeFullName = Process.GetCurrentProcess().MainModule.FileName; - var accore = Path.GetDirectoryName(acadexeFullName) + "\\accore.dll"; - if (File.Exists(accore))// 08没有,高版本分离的 - _PeForAccoreDll = new PeInfo(accore); + // 获取此dll所有的函数名 + var file = Process.GetCurrentProcess().MainModule.FileName; + var dll = Path.GetDirectoryName(file) + "\\accore.dll"; + if (File.Exists(dll))// 08没有,高版本分离的 + _PeForAccoreDll = new PeInfo(dll); } return _PeForAccoreDll; } } + static PeInfo? _PeForAcdbDll; + public static PeInfo? PeForAcdbDll + { + get + { + if (_PeForAcdbDll is null) + { + // 获取此dll所有的函数名 + var file = Process.GetCurrentProcess().MainModule.FileName; + var dll = Path.GetDirectoryName(file) + $"\\acdb{Acap.Version.Major}.dll"; + if (File.Exists(dll)) + _PeForAcdbDll = new PeInfo(dll); + } + return _PeForAcdbDll; + } + } + /// /// 同名函数指针们 /// @@ -68,10 +86,12 @@ public List? Methods { _Methods = new(); - if ((_acadPeEnum & AcadPeEnum.AcadExe) == AcadPeEnum.AcadExe && PeForAcadExe != null) + if ((_acadPeEnum & AcadPeEnum.AcadExe) == AcadPeEnum.AcadExe) GetPeMethod(PeForAcadExe); - if ((_acadPeEnum & AcadPeEnum.AccoreDll) == AcadPeEnum.AccoreDll && PeForAccoreDll != null) + if ((_acadPeEnum & AcadPeEnum.AccoreDll) == AcadPeEnum.AccoreDll) GetPeMethod(PeForAccoreDll); + if ((_acadPeEnum & AcadPeEnum.Acdb) == AcadPeEnum.Acdb) + GetPeMethod(PeForAcdbDll); } return _Methods; } @@ -125,8 +145,11 @@ public AcadPeInfo(string methodName, AcadPeEnum acadPeEnum) ///
    /// Pe信息:可能来自exe/dll /// 错误信息 - GetMethodErrorNum GetPeMethod(PeInfo peInfo) + GetMethodErrorNum GetPeMethod(PeInfo? peInfo) { + if (peInfo == null) + throw new ArgumentNullException(nameof(peInfo)); + var identifyStr = _findFuncName + ";" + peInfo.FullName; if (_Dict.ContainsKey(identifyStr))// 如果已经找过,直接返回 { diff --git a/src/IFoxCAD.Cad.Shared/Runtime/PE/DBmod.cs b/src/IFoxCAD.Cad.Shared/Runtime/PE/DBmod.cs new file mode 100644 index 0000000..bde7611 --- /dev/null +++ b/src/IFoxCAD.Cad.Shared/Runtime/PE/DBmod.cs @@ -0,0 +1,72 @@ +namespace IFoxCAD.Cad; + +/// +/// 获取数据库修改状态 +/// +/// 相关链接 +/// +[Flags] +public enum DBmod : byte +{ + [Description("数据库冇修改")] + DatabaseNoModifies = 0, + [Description("数据库有修改")] + Database = 1, + [Description("变量有修改")] + Value = 4, + [Description("窗口有修改")] + Window = 8, + [Description("视图有修改")] + View = 16, + [Description("字段有修改")] + Field = 32 +} + +public class DBmodEx +{ + public static DBmod DBmod => (DBmod)byte.Parse(Env.GetVar("dbmod").ToString()); + + delegate long DelegateAcdbSetDbmod(IntPtr db, DBmod newValue); + static DelegateAcdbSetDbmod? _AcdbSetDbmod; + public static long AcdbSetDbmod(IntPtr db, DBmod newValue) + { + if (_AcdbSetDbmod is null) + { + string str = "acdbSetDbmod"; + _AcdbSetDbmod = + AcadPeInfo.GetDelegate(str, AcadPeEnum.Acdb); + } + if (_AcdbSetDbmod is null) + return -1; + return _AcdbSetDbmod.Invoke(db, newValue);// 调用方法 + } + + /// + /// Dbmod 不被修改的任务 + /// + /// + public static void DbmodReductionTask(Action action) + { + var dm = Acap.DocumentManager; + if (dm.Count == 0) + return; + var doc = dm.MdiActiveDocument; + if (doc is null) + return; + var db = doc.Database; + var old = DBmod; + action.Invoke(); + if (old == DBmod.DatabaseNoModifies && DBmod != DBmod.DatabaseNoModifies) + AcdbSetDbmod(db.UnmanagedObject, DBmod.DatabaseNoModifies); + } + + //[CommandMethod(nameof(TestCmd_AcdbSetDbmodChange))] + //public void TestCmd_AcdbSetDbmodChange() + //{ + // DbmodReductionTask(() => { + // using DBTrans tr = new(); + // Line line = new(new Point3d(0, 0, 0), new Point3d(1, 1, 0)); + // tr.CurrentSpace.AddEntity(line); + // }); + //} +} \ No newline at end of file diff --git a/src/IFoxCAD.Cad.Shared/Runtime/PE/PostCmd.cs b/src/IFoxCAD.Cad.Shared/Runtime/PE/PostCmd.cs index 245f470..10e14fa 100644 --- a/src/IFoxCAD.Cad.Shared/Runtime/PE/PostCmd.cs +++ b/src/IFoxCAD.Cad.Shared/Runtime/PE/PostCmd.cs @@ -17,7 +17,7 @@ public static int AcedCommand(IntPtr strExpr) { string str = "acedCommand"; _AcedCommand = - AcadPeInfo.GetDelegate(str, AcadPeEnum.All); + AcadPeInfo.GetDelegate(str, AcadPeEnum.ExeAndCore); } if (_AcedCommand is null) return -1; @@ -42,7 +42,7 @@ public static int AcedPostCommand(string strExpr) { string str = "acedPostCommand"; _AcedPostCommand = - AcadPeInfo.GetDelegate(str, AcadPeEnum.All); + AcadPeInfo.GetDelegate(str, AcadPeEnum.ExeAndCore); } // 不然到CAD之后会乱码 byte[] bytes = Encoding.Unicode.GetBytes(strExpr); @@ -63,7 +63,7 @@ public static int AcedInvoke(string strExpr) { string str = "acedInvoke"; _AcedInvoke = - AcadPeInfo.GetDelegate(str, AcadPeEnum.All); + AcadPeInfo.GetDelegate(str, AcadPeEnum.ExeAndCore); } // 不然到CAD之后会乱码 byte[] bytes = Encoding.Unicode.GetBytes(strExpr); @@ -106,7 +106,7 @@ public static int AcedCmd(ResultBuffer args) if (Acap.Version.Major >= 20)// 2015.+ str += "S"; _AcedCmd = - AcadPeInfo.GetDelegate(str, AcadPeEnum.All); + AcadPeInfo.GetDelegate(str, AcadPeEnum.ExeAndCore); } if (_AcedCmd is null) return -1; diff --git a/tests/TestShared/TestEnv.cs b/tests/TestShared/TestEnv.cs index 9ebe091..dc3d3aa 100644 --- a/tests/TestShared/TestEnv.cs +++ b/tests/TestShared/TestEnv.cs @@ -68,6 +68,8 @@ public void Test_GetVar() Env.SetVar("dbmod1", 1); } + + //[CommandMethod(nameof(Test_DwgVersion))] //public void TestDwgVersion() //{ -- Gitee From 2e74edff0662a9c29e01d23a94bf6bb93fadf3ec Mon Sep 17 00:00:00 2001 From: liuqihong <540762622@qq.com> Date: Fri, 28 Oct 2022 14:37:36 +0800 Subject: [PATCH 588/675] =?UTF-8?q?=E4=BF=AE=E6=94=B9=E6=96=B9=E6=B3=95?= =?UTF-8?q?=E5=90=8D?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/IFoxCAD.Cad.Shared/Runtime/PE/DBmod.cs | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/src/IFoxCAD.Cad.Shared/Runtime/PE/DBmod.cs b/src/IFoxCAD.Cad.Shared/Runtime/PE/DBmod.cs index bde7611..f4ab413 100644 --- a/src/IFoxCAD.Cad.Shared/Runtime/PE/DBmod.cs +++ b/src/IFoxCAD.Cad.Shared/Runtime/PE/DBmod.cs @@ -45,7 +45,7 @@ public static long AcdbSetDbmod(IntPtr db, DBmod newValue) /// Dbmod 不被修改的任务 ///
    /// - public static void DbmodReductionTask(Action action) + public static void DBmodTask(Action action) { var dm = Acap.DocumentManager; if (dm.Count == 0) @@ -54,16 +54,17 @@ public static void DbmodReductionTask(Action action) if (doc is null) return; var db = doc.Database; - var old = DBmod; + + var bak = DBmod; action.Invoke(); - if (old == DBmod.DatabaseNoModifies && DBmod != DBmod.DatabaseNoModifies) + if (bak == DBmod.DatabaseNoModifies && DBmod != DBmod.DatabaseNoModifies) AcdbSetDbmod(db.UnmanagedObject, DBmod.DatabaseNoModifies); } //[CommandMethod(nameof(TestCmd_AcdbSetDbmodChange))] //public void TestCmd_AcdbSetDbmodChange() //{ - // DbmodReductionTask(() => { + // DbmodTask(() => { // using DBTrans tr = new(); // Line line = new(new Point3d(0, 0, 0), new Point3d(1, 1, 0)); // tr.CurrentSpace.AddEntity(line); -- Gitee From 14ecfc390ce88b49e8f4d63cb4f46eab80efa88e Mon Sep 17 00:00:00 2001 From: liuqihong <540762622@qq.com> Date: Fri, 28 Oct 2022 21:54:20 +0800 Subject: [PATCH 589/675] =?UTF-8?q?=E6=8F=90=E4=BE=9B=E4=B8=80=E4=B8=AA?= =?UTF-8?q?=E7=9B=B4=E6=8E=A5=E4=BF=AE=E6=94=B9dbmod=E4=B8=BA0=E7=9A=84?= =?UTF-8?q?=E6=96=B9=E6=B3=95=E7=94=A8=E4=BA=8E=E5=AF=B9=E6=8A=97lisp?= =?UTF-8?q?=E6=8F=92=E4=BB=B6=E5=88=9D=E5=A7=8B=E5=8C=96?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/IFoxCAD.Cad.Shared/Runtime/PE/DBmod.cs | 47 ++++++++++---- src/IFoxCAD.Cad.Shared/Runtime/PE/PostCmd.cs | 65 +++++++++----------- 2 files changed, 62 insertions(+), 50 deletions(-) diff --git a/src/IFoxCAD.Cad.Shared/Runtime/PE/DBmod.cs b/src/IFoxCAD.Cad.Shared/Runtime/PE/DBmod.cs index f4ab413..18a28d5 100644 --- a/src/IFoxCAD.Cad.Shared/Runtime/PE/DBmod.cs +++ b/src/IFoxCAD.Cad.Shared/Runtime/PE/DBmod.cs @@ -1,4 +1,6 @@ -namespace IFoxCAD.Cad; +using System.Windows.Controls; + +namespace IFoxCAD.Cad; /// /// 获取数据库修改状态 @@ -27,18 +29,15 @@ public class DBmodEx public static DBmod DBmod => (DBmod)byte.Parse(Env.GetVar("dbmod").ToString()); delegate long DelegateAcdbSetDbmod(IntPtr db, DBmod newValue); - static DelegateAcdbSetDbmod? _AcdbSetDbmod; + static DelegateAcdbSetDbmod? acdbSetDbmod;//别改名称 + public static long AcdbSetDbmod(IntPtr db, DBmod newValue) { - if (_AcdbSetDbmod is null) - { - string str = "acdbSetDbmod"; - _AcdbSetDbmod = - AcadPeInfo.GetDelegate(str, AcadPeEnum.Acdb); - } - if (_AcdbSetDbmod is null) + acdbSetDbmod ??= AcadPeInfo.GetDelegate( + nameof(acdbSetDbmod), AcadPeEnum.Acdb); + if (acdbSetDbmod is null) return -1; - return _AcdbSetDbmod.Invoke(db, newValue);// 调用方法 + return acdbSetDbmod.Invoke(db, newValue);// 调用方法 } /// @@ -53,18 +52,40 @@ public static void DBmodTask(Action action) var doc = dm.MdiActiveDocument; if (doc is null) return; - var db = doc.Database; var bak = DBmod; action.Invoke(); if (bak == DBmod.DatabaseNoModifies && DBmod != DBmod.DatabaseNoModifies) - AcdbSetDbmod(db.UnmanagedObject, DBmod.DatabaseNoModifies); + AcdbSetDbmod(doc.Database.UnmanagedObject, DBmod.DatabaseNoModifies); } + static bool _flag = true; + /// + /// 请在无法处理的初始化才使用它 + /// (源泉在初始化的时候进行了修改数据库,所以必须要用一个新线程等待lisp执行完成才可以) + /// + public static void DatabaseNoModifies() + { + if (_flag)//仅执行一次,在初始化时候 + { + var dm = Acap.DocumentManager; + if (dm.Count == 0) + return; + var doc = dm.MdiActiveDocument; + if (doc is null) + return; + + if (DBmod != DBmod.DatabaseNoModifies) + AcdbSetDbmod(doc.Database.UnmanagedObject, DBmod.DatabaseNoModifies); + _flag = false; + } + } + + //[CommandMethod(nameof(TestCmd_AcdbSetDbmodChange))] //public void TestCmd_AcdbSetDbmodChange() //{ - // DbmodTask(() => { + // DBmodTask(() => { // using DBTrans tr = new(); // Line line = new(new Point3d(0, 0, 0), new Point3d(1, 1, 0)); // tr.CurrentSpace.AddEntity(line); diff --git a/src/IFoxCAD.Cad.Shared/Runtime/PE/PostCmd.cs b/src/IFoxCAD.Cad.Shared/Runtime/PE/PostCmd.cs index 10e14fa..19cd170 100644 --- a/src/IFoxCAD.Cad.Shared/Runtime/PE/PostCmd.cs +++ b/src/IFoxCAD.Cad.Shared/Runtime/PE/PostCmd.cs @@ -7,21 +7,17 @@ public class PostCmd *static extern int AcedCommand(IntPtr vlist); */ delegate int DelegateAcedCommand(IntPtr strExpr); - static DelegateAcedCommand? _AcedCommand; + static DelegateAcedCommand? acedCommand;//别改名称 /// /// 发送命令(同步) /// public static int AcedCommand(IntPtr strExpr) { - if (_AcedCommand is null) - { - string str = "acedCommand"; - _AcedCommand = - AcadPeInfo.GetDelegate(str, AcadPeEnum.ExeAndCore); - } - if (_AcedCommand is null) + acedCommand ??= AcadPeInfo.GetDelegate( + nameof(acedCommand), AcadPeEnum.ExeAndCore); + if (acedCommand is null) return -1; - return _AcedCommand.Invoke(strExpr);// 调用方法 + return acedCommand.Invoke(strExpr);// 调用方法 } @@ -31,46 +27,39 @@ public static int AcedCommand(IntPtr strExpr) *public static extern int AcedPostCommand(string strExpr); */ delegate int DelegateAcedPostCommand(byte[] strExpr); - static DelegateAcedPostCommand? _AcedPostCommand; + static DelegateAcedPostCommand? acedPostCommand;//别改名称 /// /// 发送命令(同步) /// 这个可以在多线程发送 /// public static int AcedPostCommand(string strExpr) { - if (_AcedPostCommand is null) - { - string str = "acedPostCommand"; - _AcedPostCommand = - AcadPeInfo.GetDelegate(str, AcadPeEnum.ExeAndCore); - } + acedPostCommand ??= AcadPeInfo.GetDelegate( + nameof(acedPostCommand), AcadPeEnum.ExeAndCore); + // 不然到CAD之后会乱码 byte[] bytes = Encoding.Unicode.GetBytes(strExpr); - - if (_AcedPostCommand is null) + if (acedPostCommand is null) return -1; - return _AcedPostCommand.Invoke(bytes);// 调用方法 + return acedPostCommand.Invoke(bytes);// 调用方法 } delegate int DelegateAcedInvoke(byte[] strExpr); - static DelegateAcedInvoke? _AcedInvoke; + static DelegateAcedInvoke? acedInvoke;//别改名称 /// /// 发送命令(同步) /// public static int AcedInvoke(string strExpr) { - if (_AcedInvoke is null) - { - string str = "acedInvoke"; - _AcedInvoke = - AcadPeInfo.GetDelegate(str, AcadPeEnum.ExeAndCore); - } + acedInvoke ??= AcadPeInfo.GetDelegate( + nameof(acedInvoke), AcadPeEnum.ExeAndCore); + // 不然到CAD之后会乱码 byte[] bytes = Encoding.Unicode.GetBytes(strExpr); - if (_AcedInvoke is null) + if (acedInvoke is null) return -1; - return _AcedInvoke.Invoke(bytes);// 调用方法 + return acedInvoke.Invoke(bytes);// 调用方法 } @@ -92,7 +81,7 @@ public static int AcedCmd(ResultBuffer args) } */ delegate int DelegateAcedCmd(IntPtr rb); - static DelegateAcedCmd? _AcedCmd; + static DelegateAcedCmd? acedCmd;//别改名称 /// /// 发送命令(同步)如果2015.+这里报错,那么表示vs需要提权测试 /// @@ -100,17 +89,19 @@ public static int AcedCmd(ResultBuffer args) { if (Acap.DocumentManager.IsApplicationContext) return 0; - if (_AcedCmd is null) + if (acedCmd is null) { - string str = "acedCmd"; + string str = nameof(acedCmd); if (Acap.Version.Major >= 20)// 2015.+ str += "S"; - _AcedCmd = - AcadPeInfo.GetDelegate(str, AcadPeEnum.ExeAndCore); + + acedCmd = AcadPeInfo.GetDelegate( + str, AcadPeEnum.ExeAndCore); } - if (_AcedCmd is null) + if (acedCmd is null) return -1; - var reNum = _AcedCmd.Invoke(args.UnmanagedObject); + + var reNum = acedCmd.Invoke(args.UnmanagedObject); if (reNum != 5100)// 5100正确 throw new ArgumentException("发送命令出错,是否vs权限不足?"); return reNum; @@ -128,7 +119,7 @@ public static int AcedCmd(ResultBuffer args) var App = Acap.AcadApplication; #endif // activeDocument 加载lisp第二个文档有问题,似乎要切换了才能 - var activeDocument = App.GetType().InvokeMember("ActiveDocument", BindingFlags.GetProperty, null, App, null); - return activeDocument?.GetType().InvokeMember("SendCommand", BindingFlags.InvokeMethod, null, activeDocument, commandArray); + var doc = App.GetType().InvokeMember("ActiveDocument", BindingFlags.GetProperty, null, App, null); + return doc?.GetType().InvokeMember("SendCommand", BindingFlags.InvokeMethod, null, doc, commandArray); } } \ No newline at end of file -- Gitee From 6f23075325a1e0884fef7f9ee6dc76007a26efc7 Mon Sep 17 00:00:00 2001 From: liuqihong <540762622@qq.com> Date: Sat, 29 Oct 2022 00:54:18 +0800 Subject: [PATCH 590/675] =?UTF-8?q?=E5=B1=8F=E8=94=BD=E4=B8=80=E4=BA=9B?= =?UTF-8?q?=E6=B5=8B=E8=AF=95=E5=86=85=E5=AE=B9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/IFoxCAD.LoadForm/DefaultAssemblyResolve.cs | 4 +--- tests/TestShared/CmdINI.cs | 10 ++++++++-- tests/TestShared/Copyclip.cs | 2 +- 3 files changed, 10 insertions(+), 6 deletions(-) diff --git a/src/IFoxCAD.LoadForm/DefaultAssemblyResolve.cs b/src/IFoxCAD.LoadForm/DefaultAssemblyResolve.cs index fc69703..4fc2129 100644 --- a/src/IFoxCAD.LoadForm/DefaultAssemblyResolve.cs +++ b/src/IFoxCAD.LoadForm/DefaultAssemblyResolve.cs @@ -35,8 +35,6 @@ public class AssemblyHelper // 否则a.dll引用b.dll函数的时候,b.dll修改重生成之后, // 加载进去会调用第一个版本的b.dll, // vs会迭代程序版本号的*,所以最后的可用就是循环到最后的. - - for (int i = 0; i < cadAss.Length; i++) { if (GetAssemblyName(cadAss[i].GetName().FullName) != ag) @@ -53,7 +51,7 @@ public class AssemblyHelper for (int i = 0; i < cadAss.Length; i++) sb.AppendLine("-------匹配对象:: " + GetAssemblyName(cadAss[i].GetName().FullName)); Debug.WriteLine(sb.ToString()); - Debugger.Break(); + //Debugger.Break(); } return result; } diff --git a/tests/TestShared/CmdINI.cs b/tests/TestShared/CmdINI.cs index 69cb5ca..983e7a7 100644 --- a/tests/TestShared/CmdINI.cs +++ b/tests/TestShared/CmdINI.cs @@ -1,3 +1,5 @@ +//#define givePeopleTest + using System.Diagnostics; namespace Test; @@ -26,11 +28,13 @@ public class AutoRegAssemEx : AutoRegAssem { public AutoRegAssemEx() : base(AutoRegConfig.All) { - Env.Printl($"{nameof(AutoRegAssemEx)}构造函数,开始自动执行\r\n"); CmdInit.AutoRegAssemEx = this; +#if givePeopleTest #if Debug // 此处用来反射本程序集,检查是否存在重复命令 AutoReflection.DebugCheckCmdRecurrence(); +#endif + Env.Printl($"{nameof(AutoRegAssemEx)}构造函数,开始自动执行\r\n"); #endif } } @@ -66,6 +70,7 @@ public void IFoxRemoveReg() } } +#if givePeopleTest /* * 自动执行:特性 */ @@ -144,4 +149,5 @@ public void Terminate() // System.Windows.Forms.MessageBox.Show(e.Message); // } } -} \ No newline at end of file +} +#endif \ No newline at end of file diff --git a/tests/TestShared/Copyclip.cs b/tests/TestShared/Copyclip.cs index a997f19..c3ea6c1 100644 --- a/tests/TestShared/Copyclip.cs +++ b/tests/TestShared/Copyclip.cs @@ -29,7 +29,7 @@ public class Copyclip { #region 命令 #if test - [IFoxInitialize] + //[IFoxInitialize] 惊惊: 遇到了高版本无法导出WMF,放弃此功能,等待有缘人 public void Init() { Acap.DocumentManager.DocumentLockModeChanged -- Gitee From 1702f0f4221a6e847aeb25c779a9e51c457454ad Mon Sep 17 00:00:00 2001 From: liuqihong <540762622@qq.com> Date: Sat, 29 Oct 2022 13:29:37 +0800 Subject: [PATCH 591/675] =?UTF-8?q?=E4=BC=98=E5=8C=96=E4=BB=A3=E7=A0=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/IFoxCAD.Cad.Shared/Runtime/PE/DBmod.cs | 10 +- src/IFoxCAD.Cad.Shared/Runtime/PE/PostCmd.cs | 162 +++++++++++-------- 2 files changed, 102 insertions(+), 70 deletions(-) diff --git a/src/IFoxCAD.Cad.Shared/Runtime/PE/DBmod.cs b/src/IFoxCAD.Cad.Shared/Runtime/PE/DBmod.cs index 18a28d5..7502e25 100644 --- a/src/IFoxCAD.Cad.Shared/Runtime/PE/DBmod.cs +++ b/src/IFoxCAD.Cad.Shared/Runtime/PE/DBmod.cs @@ -1,6 +1,4 @@ -using System.Windows.Controls; - -namespace IFoxCAD.Cad; +namespace IFoxCAD.Cad; /// /// 获取数据库修改状态 @@ -8,7 +6,7 @@ namespace IFoxCAD.Cad; /// 相关链接 /// [Flags] -public enum DBmod : byte +public enum DBmod : short { [Description("数据库冇修改")] DatabaseNoModifies = 0, @@ -26,7 +24,7 @@ public enum DBmod : byte public class DBmodEx { - public static DBmod DBmod => (DBmod)byte.Parse(Env.GetVar("dbmod").ToString()); + public static DBmod DBmod => (DBmod)Env.GetVar("dbmod"); delegate long DelegateAcdbSetDbmod(IntPtr db, DBmod newValue); static DelegateAcdbSetDbmod? acdbSetDbmod;//别改名称 @@ -66,7 +64,7 @@ public static void DBmodTask(Action action) /// public static void DatabaseNoModifies() { - if (_flag)//仅执行一次,在初始化时候 + if (_flag)// 仅执行一次,在初始化时候 { var dm = Acap.DocumentManager; if (dm.Count == 0) diff --git a/src/IFoxCAD.Cad.Shared/Runtime/PE/PostCmd.cs b/src/IFoxCAD.Cad.Shared/Runtime/PE/PostCmd.cs index 19cd170..b34ff03 100644 --- a/src/IFoxCAD.Cad.Shared/Runtime/PE/PostCmd.cs +++ b/src/IFoxCAD.Cad.Shared/Runtime/PE/PostCmd.cs @@ -3,116 +3,114 @@ public class PostCmd { /* +#if NET35 || NET40 +[DllImport("acad.exe", EntryPoint = "acedCmd", CharSet = CharSet.Auto, CallingConvention = CallingConvention.Cdecl)] +#else +// cad2015的AcedCmd改为AcedCmdS.参数也改了,不过不影响pe读取,accore.dll是2013版本后才开始改 +[DllImport("accore.dll", EntryPoint = "acedCmdS", CharSet = CharSet.Auto, CallingConvention = CallingConvention.Cdecl)] +static extern int AcedCmd(IntPtr rbp, bool forFutureUse = false, IntPtr pForFutureUse = IntPtr.Zero); +#endif +static extern int AcedCmd(IntPtr rbp); +public static int AcedCmd(ResultBuffer args) +{ + if (Acap.DocumentManager.IsApplicationContext) + return 0; + else + return AcedCmd(args.UnmanagedObject); +} + */ + delegate int DelegateAcedCmd(IntPtr parameter); + static DelegateAcedCmd? acedCmd;//nameof 别改名称 + /// + /// 发送命令(同步)如果2015.+这里报错,那么表示vs需要提权测试 + /// + public static int AcedCmd(ResultBuffer args) + { + if (Acap.DocumentManager.IsApplicationContext) + return 0; + if (acedCmd is null) + { + string str = nameof(acedCmd); + if (Acap.Version.Major >= 20)// 2015.+ + str += "S"; + + acedCmd = AcadPeInfo.GetDelegate( + str, AcadPeEnum.ExeAndCore); + } + if (acedCmd is null) + return 0; + + var result = acedCmd.Invoke(args.UnmanagedObject); + if (result != (int)PromptStatus.OK) + throw new ArgumentException("发送命令出错,是否vs权限不足?"); + return result; + } + + /* *[DllImport("accore.dll", EntryPoint = "acedCommand")] *static extern int AcedCommand(IntPtr vlist); */ - delegate int DelegateAcedCommand(IntPtr strExpr); - static DelegateAcedCommand? acedCommand;//别改名称 + delegate int DelegateAcedCommand(IntPtr parameter); + static DelegateAcedCommand? acedCommand;//nameof 别改名称 /// /// 发送命令(同步) /// - public static int AcedCommand(IntPtr strExpr) + public static int AcedCommand(IntPtr args) { acedCommand ??= AcadPeInfo.GetDelegate( nameof(acedCommand), AcadPeEnum.ExeAndCore); if (acedCommand is null) - return -1; - return acedCommand.Invoke(strExpr);// 调用方法 + return 0; + return acedCommand.Invoke(args);// 调用方法 } - /* *[DllImport("acad.exe", CharSet = CharSet.Auto, CallingConvention = CallingConvention.Cdecl, * EntryPoint = "?acedPostCommand@@YAHPB_W@Z")] *public static extern int AcedPostCommand(string strExpr); */ - delegate int DelegateAcedPostCommand(byte[] strExpr); - static DelegateAcedPostCommand? acedPostCommand;//别改名称 + delegate int DelegateAcedPostCommand(byte[] parameter); + static DelegateAcedPostCommand? acedPostCommand;//nameof 别改名称 /// /// 发送命令(同步) /// 这个可以在多线程发送 /// - public static int AcedPostCommand(string strExpr) + public static int AcedPostCommand(string args) { acedPostCommand ??= AcadPeInfo.GetDelegate( nameof(acedPostCommand), AcadPeEnum.ExeAndCore); // 不然到CAD之后会乱码 - byte[] bytes = Encoding.Unicode.GetBytes(strExpr); + byte[] bytes = Encoding.Unicode.GetBytes(args); if (acedPostCommand is null) - return -1; + return 0; return acedPostCommand.Invoke(bytes);// 调用方法 } - delegate int DelegateAcedInvoke(byte[] strExpr); - static DelegateAcedInvoke? acedInvoke;//别改名称 + delegate int DelegateAcedInvoke(byte[] parameter); + static DelegateAcedInvoke? acedInvoke;//nameof 别改名称 /// /// 发送命令(同步) /// - public static int AcedInvoke(string strExpr) + public static int AcedInvoke(string args) { acedInvoke ??= AcadPeInfo.GetDelegate( nameof(acedInvoke), AcadPeEnum.ExeAndCore); // 不然到CAD之后会乱码 - byte[] bytes = Encoding.Unicode.GetBytes(strExpr); + byte[] bytes = Encoding.Unicode.GetBytes(args); if (acedInvoke is null) - return -1; - return acedInvoke.Invoke(bytes);// 调用方法 - } - - - /* -#if NET35 || NET40 - [DllImport("acad.exe", EntryPoint = "acedCmd", CharSet = CharSet.Auto, CallingConvention = CallingConvention.Cdecl)] -#else - // cad2015的AcedCmd改为AcedCmdS.参数也改了,不过不影响pe读取,accore.dll是2013版本后才开始改 - [DllImport("accore.dll", EntryPoint = "acedCmdS", CharSet = CharSet.Auto, CallingConvention = CallingConvention.Cdecl)] - static extern int AcedCmd(IntPtr rbp, bool forFutureUse = false, IntPtr pForFutureUse = IntPtr.Zero); -#endif - static extern int AcedCmd(IntPtr rbp); - public static int AcedCmd(ResultBuffer args) - { - if (Acap.DocumentManager.IsApplicationContext) return 0; - else - return AcedCmd(args.UnmanagedObject); - } - */ - delegate int DelegateAcedCmd(IntPtr rb); - static DelegateAcedCmd? acedCmd;//别改名称 - /// - /// 发送命令(同步)如果2015.+这里报错,那么表示vs需要提权测试 - /// - public static int AcedCmd(ResultBuffer args) - { - if (Acap.DocumentManager.IsApplicationContext) - return 0; - if (acedCmd is null) - { - string str = nameof(acedCmd); - if (Acap.Version.Major >= 20)// 2015.+ - str += "S"; - - acedCmd = AcadPeInfo.GetDelegate( - str, AcadPeEnum.ExeAndCore); - } - if (acedCmd is null) - return -1; - - var reNum = acedCmd.Invoke(args.UnmanagedObject); - if (reNum != 5100)// 5100正确 - throw new ArgumentException("发送命令出错,是否vs权限不足?"); - return reNum; + return acedInvoke.Invoke(bytes);// 调用方法 } /// - /// 发送命令(异步) + /// 发送命令(异步)+CommandFlags.Session可以同步发送 /// - public static object? ActiveCmd(string str) + public static object? AsyncCommand(string args) { - object[] commandArray = { str + "\n" }; + object[] commandArray = { args + "\n" }; #if zcad var App = Acap.ZcadApplication; #else @@ -122,4 +120,40 @@ public static int AcedCmd(ResultBuffer args) var doc = App.GetType().InvokeMember("ActiveDocument", BindingFlags.GetProperty, null, App, null); return doc?.GetType().InvokeMember("SendCommand", BindingFlags.InvokeMethod, null, doc, commandArray); } + + /* + * 发送命令会记录在命令历史 + * 发送lisp的(command "xx")就不会 + */ + public static bool SendCommand(string args) + { + // cmd 需要加\n吗? + if (!Acap.DocumentManager.IsApplicationContext) + { + int ret; + // ret = AcedCmd(ResultBuffer); + // ret = AcedCommand(IntPtr) + // ret = AcedPostCommand(args); + ret = AcedInvoke(args); + + //不太确定所有的发送 返回值int 是不是 PromptStatus + // if (ret == (int)PromptStatus.OK) + // return true; + if (ret != 0) + return true; + } + else + { + var dm = Acap.DocumentManager; + var doc = dm.MdiActiveDocument; + if (doc != null) + { + // 此处+CommandFlags.Session可以同步发送,bo命令可以,其他是否可以? + // AsyncCommand()异步com的 + doc.SendStringToExecute(args, true, false, false); + return true; + } + } + return false; + } } \ No newline at end of file -- Gitee From f5c01bf4dad2d9e5ce3d6e5ab3d9e7d30fd357bf Mon Sep 17 00:00:00 2001 From: liuqihong <540762622@qq.com> Date: Sat, 29 Oct 2022 15:30:13 +0800 Subject: [PATCH 592/675] =?UTF-8?q?=E4=BC=98=E5=8C=96=E5=8F=91=E9=80=81?= =?UTF-8?q?=E5=91=BD=E4=BB=A4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../ExtensionMethod/EditorEx.cs | 5 +- src/IFoxCAD.Cad.Shared/ResultData/LispList.cs | 6 +- .../ResultData/TypedValueList.cs | 5 +- src/IFoxCAD.Cad.Shared/Runtime/PE/PostCmd.cs | 152 +++++++++++------- 4 files changed, 105 insertions(+), 63 deletions(-) diff --git a/src/IFoxCAD.Cad.Shared/ExtensionMethod/EditorEx.cs b/src/IFoxCAD.Cad.Shared/ExtensionMethod/EditorEx.cs index 12f7ee1..aff65da 100644 --- a/src/IFoxCAD.Cad.Shared/ExtensionMethod/EditorEx.cs +++ b/src/IFoxCAD.Cad.Shared/ExtensionMethod/EditorEx.cs @@ -1073,13 +1073,14 @@ public enum RunLispFlag : byte { _ = AcedEvaluateLisp(lispCode, out IntPtr rb); if (rb != IntPtr.Zero) - return DisposableWrapper.Create(typeof(ResultBuffer), rb, true) as ResultBuffer; + return (ResultBuffer)DisposableWrapper.Create(typeof(ResultBuffer), rb, true); } if ((flag & RunLispFlag.SendStringToExecute) == RunLispFlag.SendStringToExecute) { var dm = Acap.DocumentManager; var doc = dm.MdiActiveDocument; - doc.SendStringToExecute(lispCode + "\n", false, false, false); + if (doc != null) + doc.SendStringToExecute(lispCode + "\n", false, false, false); } return null; } diff --git a/src/IFoxCAD.Cad.Shared/ResultData/LispList.cs b/src/IFoxCAD.Cad.Shared/ResultData/LispList.cs index 8c72669..2488541 100644 --- a/src/IFoxCAD.Cad.Shared/ResultData/LispList.cs +++ b/src/IFoxCAD.Cad.Shared/ResultData/LispList.cs @@ -34,7 +34,7 @@ public virtual List Value value.InsertRange(1, this); return value; } - } + } #endregion #region 添加数据 @@ -184,7 +184,7 @@ public void Add(LispList value) /// /// LispList 隐式转换到 ResultBuffer /// - /// TypedValueList 实例 + /// TypedValueList 实例,要using public static implicit operator ResultBuffer(LispList values) => new(values.Value.ToArray()); /// /// TypedValue 数组隐式转换到 LispList @@ -192,4 +192,4 @@ public void Add(LispList value) /// TypedValue 数组 public static implicit operator LispList(TypedValue[] values) => new(values); #endregion -} +} \ No newline at end of file diff --git a/src/IFoxCAD.Cad.Shared/ResultData/TypedValueList.cs b/src/IFoxCAD.Cad.Shared/ResultData/TypedValueList.cs index 896a226..16970d2 100644 --- a/src/IFoxCAD.Cad.Shared/ResultData/TypedValueList.cs +++ b/src/IFoxCAD.Cad.Shared/ResultData/TypedValueList.cs @@ -57,7 +57,8 @@ public virtual void Add(int code, object obj) /// ResultBuffer 字符串 public override string ToString() { - return new ResultBuffer(this).ToString(); + using ResultBuffer a = new(this); + return a.ToString(); } #endregion -} +} \ No newline at end of file diff --git a/src/IFoxCAD.Cad.Shared/Runtime/PE/PostCmd.cs b/src/IFoxCAD.Cad.Shared/Runtime/PE/PostCmd.cs index b34ff03..798cfd8 100644 --- a/src/IFoxCAD.Cad.Shared/Runtime/PE/PostCmd.cs +++ b/src/IFoxCAD.Cad.Shared/Runtime/PE/PostCmd.cs @@ -3,28 +3,28 @@ public class PostCmd { /* -#if NET35 || NET40 -[DllImport("acad.exe", EntryPoint = "acedCmd", CharSet = CharSet.Auto, CallingConvention = CallingConvention.Cdecl)] -#else -// cad2015的AcedCmd改为AcedCmdS.参数也改了,不过不影响pe读取,accore.dll是2013版本后才开始改 -[DllImport("accore.dll", EntryPoint = "acedCmdS", CharSet = CharSet.Auto, CallingConvention = CallingConvention.Cdecl)] -static extern int AcedCmd(IntPtr rbp, bool forFutureUse = false, IntPtr pForFutureUse = IntPtr.Zero); -#endif -static extern int AcedCmd(IntPtr rbp); -public static int AcedCmd(ResultBuffer args) -{ - if (Acap.DocumentManager.IsApplicationContext) - return 0; - else - return AcedCmd(args.UnmanagedObject); -} - */ + * #if NET35 || NET40 + * [DllImport("acad.exe", EntryPoint = "acedCmd", CharSet = CharSet.Auto, CallingConvention = CallingConvention.Cdecl)] + * #else + * // cad2015的AcedCmd改为AcedCmdS.参数也改了,不过不影响pe读取,accore.dll是2013版本后才开始改 + * [DllImport("accore.dll", EntryPoint = "acedCmdS", CharSet = CharSet.Auto, CallingConvention = CallingConvention.Cdecl)] + * static extern int AcedCmd(IntPtr rbp, bool forFutureUse = false, IntPtr pForFutureUse = IntPtr.Zero); + * #endif + * static extern int AcedCmd(IntPtr rbp); + * public static int AcedCmd(ResultBuffer args) + * { + * if (Acap.DocumentManager.IsApplicationContext) + * return 0; + * else + * return AcedCmd(args.UnmanagedObject); + * } + */ delegate int DelegateAcedCmd(IntPtr parameter); static DelegateAcedCmd? acedCmd;//nameof 别改名称 /// /// 发送命令(同步)如果2015.+这里报错,那么表示vs需要提权测试 /// - public static int AcedCmd(ResultBuffer args) + static PromptStatus AcedCmd(ResultBuffer args) { if (Acap.DocumentManager.IsApplicationContext) return 0; @@ -40,42 +40,42 @@ public static int AcedCmd(ResultBuffer args) if (acedCmd is null) return 0; - var result = acedCmd.Invoke(args.UnmanagedObject); - if (result != (int)PromptStatus.OK) + var result = (PromptStatus)acedCmd.Invoke(args.UnmanagedObject); + if (result != PromptStatus.OK) throw new ArgumentException("发送命令出错,是否vs权限不足?"); return result; } /* - *[DllImport("accore.dll", EntryPoint = "acedCommand")] - *static extern int AcedCommand(IntPtr vlist); - */ + * [DllImport("accore.dll", EntryPoint = "acedCommand")] + * static extern int AcedCommand(IntPtr vlist); + */ delegate int DelegateAcedCommand(IntPtr parameter); static DelegateAcedCommand? acedCommand;//nameof 别改名称 /// /// 发送命令(同步) /// - public static int AcedCommand(IntPtr args) + static PromptStatus AcedCommand(IntPtr args) { acedCommand ??= AcadPeInfo.GetDelegate( nameof(acedCommand), AcadPeEnum.ExeAndCore); if (acedCommand is null) - return 0; - return acedCommand.Invoke(args);// 调用方法 + return PromptStatus.Error; + return (PromptStatus)acedCommand.Invoke(args);// 调用方法 } /* - *[DllImport("acad.exe", CharSet = CharSet.Auto, CallingConvention = CallingConvention.Cdecl, - * EntryPoint = "?acedPostCommand@@YAHPB_W@Z")] - *public static extern int AcedPostCommand(string strExpr); - */ + * [DllImport("acad.exe", CharSet = CharSet.Auto, CallingConvention = CallingConvention.Cdecl, + * EntryPoint = "?acedPostCommand@@YAHPB_W@Z")] + * public static extern int AcedPostCommand(string strExpr); + */ delegate int DelegateAcedPostCommand(byte[] parameter); static DelegateAcedPostCommand? acedPostCommand;//nameof 别改名称 /// /// 发送命令(同步) /// 这个可以在多线程发送 /// - public static int AcedPostCommand(string args) + static PromptStatus AcedPostCommand(string args) { acedPostCommand ??= AcadPeInfo.GetDelegate( nameof(acedPostCommand), AcadPeEnum.ExeAndCore); @@ -83,8 +83,8 @@ public static int AcedPostCommand(string args) // 不然到CAD之后会乱码 byte[] bytes = Encoding.Unicode.GetBytes(args); if (acedPostCommand is null) - return 0; - return acedPostCommand.Invoke(bytes);// 调用方法 + return PromptStatus.Error; + return (PromptStatus)acedPostCommand.Invoke(bytes);// 调用方法 } delegate int DelegateAcedInvoke(byte[] parameter); @@ -92,7 +92,7 @@ public static int AcedPostCommand(string args) /// /// 发送命令(同步) /// - public static int AcedInvoke(string args) + static PromptStatus AcedInvoke(string args) { acedInvoke ??= AcadPeInfo.GetDelegate( nameof(acedInvoke), AcadPeEnum.ExeAndCore); @@ -101,14 +101,14 @@ public static int AcedInvoke(string args) byte[] bytes = Encoding.Unicode.GetBytes(args); if (acedInvoke is null) - return 0; - return acedInvoke.Invoke(bytes);// 调用方法 + return PromptStatus.Error; + return (PromptStatus)acedInvoke.Invoke(bytes);// 调用方法 } /// /// 发送命令(异步)+CommandFlags.Session可以同步发送 /// - public static object? AsyncCommand(string args) + static void AsyncCommand(string args) { object[] commandArray = { args + "\n" }; #if zcad @@ -117,43 +117,83 @@ public static int AcedInvoke(string args) var App = Acap.AcadApplication; #endif // activeDocument 加载lisp第二个文档有问题,似乎要切换了才能 - var doc = App.GetType().InvokeMember("ActiveDocument", BindingFlags.GetProperty, null, App, null); - return doc?.GetType().InvokeMember("SendCommand", BindingFlags.InvokeMethod, null, doc, commandArray); + var doc = App.GetType() + .InvokeMember("ActiveDocument", BindingFlags.GetProperty, null, App, null); + doc?.GetType() + .InvokeMember("SendCommand", BindingFlags.InvokeMethod, null, doc, commandArray);// 返回值是null + } + + public enum RunCmdFlag : byte + { + AcedCmd = 1, + AcedCommand = 2, + AcedPostCommand = 4, + AcedInvoke = 8, + SendStringToExecute = 16, + AsyncCommand = 32, } /* * 发送命令会记录在命令历史 * 发送lisp的(command "xx")就不会 */ - public static bool SendCommand(string args) + public static PromptStatus SendCommand(ResultBuffer args) + { + return AcedCmd(args); + } + public static PromptStatus SendCommand(IntPtr args) + { + return AcedCommand(args); + } + public static PromptStatus SendCommand(string args, RunCmdFlag flag) { - // cmd 需要加\n吗? + PromptStatus ret = PromptStatus.OK; if (!Acap.DocumentManager.IsApplicationContext) { - int ret; - // ret = AcedCmd(ResultBuffer); - // ret = AcedCommand(IntPtr) - // ret = AcedPostCommand(args); - ret = AcedInvoke(args); - - //不太确定所有的发送 返回值int 是不是 PromptStatus - // if (ret == (int)PromptStatus.OK) - // return true; - if (ret != 0) - return true; + if ((flag & RunCmdFlag.AcedCmd) == RunCmdFlag.AcedCmd) + { + using ResultBuffer rb = new() + { + new((int)LispDataType.Text, args), + }; + ret = SendCommand(rb); + } + if ((flag & RunCmdFlag.AcedCommand) == RunCmdFlag.AcedCommand) + { + // 此处是这样转换吗? + using ResultBuffer rb = new() + { + new((int)LispDataType.Text, args), + }; + ret = SendCommand(rb.UnmanagedObject); + } + if ((flag & RunCmdFlag.AcedPostCommand) == RunCmdFlag.AcedPostCommand) + { + ret = AcedPostCommand(args); + } + if ((flag & RunCmdFlag.AcedInvoke) == RunCmdFlag.AcedInvoke) + { + ret = AcedInvoke(args); + } } else { var dm = Acap.DocumentManager; var doc = dm.MdiActiveDocument; - if (doc != null) + if (doc == null) + return PromptStatus.Error; + + if ((flag & RunCmdFlag.SendStringToExecute) == RunCmdFlag.SendStringToExecute) { - // 此处+CommandFlags.Session可以同步发送,bo命令可以,其他是否可以? - // AsyncCommand()异步com的 doc.SendStringToExecute(args, true, false, false); - return true; + } + if ((flag & RunCmdFlag.AsyncCommand) == RunCmdFlag.AsyncCommand) + { + // 此处+CommandFlags.Session可以同步发送,bo命令可以,其他是否可以? + // 仿人工输入,像lisp一样可以直接发送关键字 + AsyncCommand(args); } } - return false; + return ret; } } \ No newline at end of file -- Gitee From 5df6e08ba4b43fa93994f25cd6b29eb41d815754 Mon Sep 17 00:00:00 2001 From: liuqihong <540762622@qq.com> Date: Sat, 29 Oct 2022 22:51:42 +0800 Subject: [PATCH 593/675] =?UTF-8?q?=E5=A2=9E=E5=8A=A0=E6=B3=A8=E9=87=8A?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/IFoxCAD.Cad.Shared/Copyclip/WindowsAPI.cs | 6 +- src/IFoxCAD.Cad.Shared/Runtime/PE/PostCmd.cs | 8 +- .../SelectionFilter/OpComp.cs | 2 +- .../Cmd.cs" | 1 + .../IMEControl.cs" | 111 +++++++++++------- 5 files changed, 81 insertions(+), 47 deletions(-) diff --git a/src/IFoxCAD.Cad.Shared/Copyclip/WindowsAPI.cs b/src/IFoxCAD.Cad.Shared/Copyclip/WindowsAPI.cs index 301257a..8634da3 100644 --- a/src/IFoxCAD.Cad.Shared/Copyclip/WindowsAPI.cs +++ b/src/IFoxCAD.Cad.Shared/Copyclip/WindowsAPI.cs @@ -267,7 +267,7 @@ public partial class WindowsAPI public static extern uint GetWindowThreadProcessId(IntPtr hWnd, out uint lpdwProcessId); /// - /// 焦点 + /// 设置焦点 /// /// /// @@ -327,11 +327,13 @@ public partial class WindowsAPI /// 布局码 [DllImport("user32.dll")] public static extern int GetKeyboardLayout(int threadid); + + /// /// 获取按键的当前状态 /// /// 按键虚拟代码 - /// 按键状态值,高位为1表示按下(<0),0表示弹起(>0) + /// 表示没按下>0;按下<0 [DllImport("user32.dll")] public static extern short GetKeyState(int nVirtKey); /// diff --git a/src/IFoxCAD.Cad.Shared/Runtime/PE/PostCmd.cs b/src/IFoxCAD.Cad.Shared/Runtime/PE/PostCmd.cs index 798cfd8..cd0b39e 100644 --- a/src/IFoxCAD.Cad.Shared/Runtime/PE/PostCmd.cs +++ b/src/IFoxCAD.Cad.Shared/Runtime/PE/PostCmd.cs @@ -112,13 +112,13 @@ static void AsyncCommand(string args) { object[] commandArray = { args + "\n" }; #if zcad - var App = Acap.ZcadApplication; + var com = Acap.ZcadApplication; #else - var App = Acap.AcadApplication; + var com = Acap.AcadApplication; #endif // activeDocument 加载lisp第二个文档有问题,似乎要切换了才能 - var doc = App.GetType() - .InvokeMember("ActiveDocument", BindingFlags.GetProperty, null, App, null); + var doc = com.GetType() + .InvokeMember("ActiveDocument", BindingFlags.GetProperty, null, com, null); doc?.GetType() .InvokeMember("SendCommand", BindingFlags.InvokeMethod, null, doc, commandArray);// 返回值是null } diff --git a/src/IFoxCAD.Cad.Shared/SelectionFilter/OpComp.cs b/src/IFoxCAD.Cad.Shared/SelectionFilter/OpComp.cs index 9727b72..040090a 100644 --- a/src/IFoxCAD.Cad.Shared/SelectionFilter/OpComp.cs +++ b/src/IFoxCAD.Cad.Shared/SelectionFilter/OpComp.cs @@ -76,4 +76,4 @@ public override IEnumerable GetValues() yield return new TypedValue(-4, Content); yield return Value; } -} +} \ No newline at end of file diff --git "a/tests/TestAcad09plus/\350\276\223\345\205\245\346\263\225\350\207\252\345\212\250\345\210\207\346\215\242/\350\265\253\346\200\235\350\276\223\345\205\245\346\263\225/Cmd.cs" "b/tests/TestAcad09plus/\350\276\223\345\205\245\346\263\225\350\207\252\345\212\250\345\210\207\346\215\242/\350\265\253\346\200\235\350\276\223\345\205\245\346\263\225/Cmd.cs" index 8f8e5d5..2845f79 100644 --- "a/tests/TestAcad09plus/\350\276\223\345\205\245\346\263\225\350\207\252\345\212\250\345\210\207\346\215\242/\350\265\253\346\200\235\350\276\223\345\205\245\346\263\225/Cmd.cs" +++ "b/tests/TestAcad09plus/\350\276\223\345\205\245\346\263\225\350\207\252\345\212\250\345\210\207\346\215\242/\350\265\253\346\200\235\350\276\223\345\205\245\346\263\225/Cmd.cs" @@ -24,6 +24,7 @@ public void Gstar_IMEFilter() [CommandMethod(nameof(Gstar_IMEFilterSettings))] public void Gstar_IMEFilterSettings() { + /*cad21若使用进程模式,则搜狗拦截不到,并且破坏了内存*/ Autodesk.AutoCAD.Internal.Utils.SetFocusToDwgView(); ShowWPFWindowCentered.Show(new SettingsWindow(), true); } diff --git "a/tests/TestAcad09plus/\350\276\223\345\205\245\346\263\225\350\207\252\345\212\250\345\210\207\346\215\242/\350\265\253\346\200\235\350\276\223\345\205\245\346\263\225/IMEControl.cs" "b/tests/TestAcad09plus/\350\276\223\345\205\245\346\263\225\350\207\252\345\212\250\345\210\207\346\215\242/\350\265\253\346\200\235\350\276\223\345\205\245\346\263\225/IMEControl.cs" index e3924f3..2c31254 100644 --- "a/tests/TestAcad09plus/\350\276\223\345\205\245\346\263\225\350\207\252\345\212\250\345\210\207\346\215\242/\350\265\253\346\200\235\350\276\223\345\205\245\346\263\225/IMEControl.cs" +++ "b/tests/TestAcad09plus/\350\276\223\345\205\245\346\263\225\350\207\252\345\212\250\345\210\207\346\215\242/\350\265\253\346\200\235\350\276\223\345\205\245\346\263\225/IMEControl.cs" @@ -6,6 +6,7 @@ namespace Gstar_IMEFilter; public class IMEControl { static readonly Regex CMDReg = new("\\(C:.*\\)"); + /*某些窗口没有 WM_KEYDOWN 消息,就只有 WM_KEYUP 消息*/ const int WM_KEYDOWN = 256; const int WM_KEYUP = 257; //const int WM_CHAR = 258; @@ -13,17 +14,19 @@ public class IMEControl internal static string[] DefaultCMDs; internal static List ExceptCMDs; - internal static int AcadPID; internal static IntPtr hnexthookproc; internal static WindowsAPI.CallBackX86? HookProcX86; internal static WindowsAPI.CallBackX64? HookProcX64; internal static WindowsAPI.CallBack? HookProc; + internal static Process _Process; + internal static int AcadPID => _Process.Id; + static IMEControl() { DefaultCMDs = new string[] { "MTEXT", "DDEDIT", "MTEDIT", "TABLEDIT", "MLEADER", "QLEADER", "MLEADERCONTENTEDIT", "MLEADEREDIT", "TEXTEDIT", "TEXT", "QLEADER" }; ExceptCMDs = new(); - AcadPID = Process.GetCurrentProcess().Id; + _Process = Process.GetCurrentProcess(); hnexthookproc = IntPtr.Zero; CheckLowLevelHooksTimeout(); @@ -38,6 +41,9 @@ static IMEControl() DefaultCMDs = lines.Split(new string[] { "," }, StringSplitOptions.RemoveEmptyEntries); } + // 优化内存,减少消息循环时候,频繁创建此类 + static StringBuilder _lpClassName = new(byte.MaxValue); + public static bool IMEHook(int nCode, int wParam, int lParam) { var dm = Acap.DocumentManager; @@ -49,59 +55,74 @@ public static bool IMEHook(int nCode, int wParam, int lParam) if (!WindowsAPI.IsWindowEnabled(Acap.MainWindow.Handle)) return false; + // 括免命令进行中输入会触发,实现在括免命令中允许输入法 string input = doc.CommandInProgress; Match match = CMDReg.Match(input); if (match.Success) input = input.Substring(checked(match.Index + 3), checked(match.Length - 4)); - if (ExceptCMDs.Contains(input.ToUpper())) return false; - if (wParam >= 65 && wParam <= 90 - || (wParam == 32 || wParam == 188) - || (wParam == 190 || wParam == 222 || (wParam == 189 || wParam == 13)) - || (wParam == 187 || wParam == 186 || (wParam == 191 || wParam == 192) || - (wParam == 219 || wParam == 220 || (wParam == 221 || wParam == 222))) - || (wParam == 223 || wParam >= 48 && wParam <= 57 || (wParam == 27 || wParam >= 96 && wParam <= 105) || wParam == 110)) + // 键盘按键值 + if ((65 <= wParam && wParam <= 90/*a~z*/) || + (48 <= wParam && wParam <= 57/*数字键*/) || + (96 <= wParam && wParam <= 105/*小数字键盘数字*/) || + wParam == 27/*esc*/ || + wParam == 32/*空格*/ || + wParam == 13/*回车,大小回车都是它*/ || + wParam == 186/*;*/ || + wParam == 187/*=*/ || + wParam == 188/*,*/ || + wParam == 189/*-*/ || + wParam == 190/*.*/ || + wParam == 191/*?*/ || + wParam == 192/*`~*/ || + wParam == 219/*[*/ || + wParam == 220/*\*/ || + wParam == 221/*]*/ || + wParam == 222/*'*/ || + wParam == 223 || + wParam == 110/*小数字键盘.*/) { - StringBuilder lpClassName1 = new(byte.MaxValue); - IntPtr focus = WindowsAPI.GetFocus(); - WindowsAPI.GetClassName(focus, lpClassName1, checked(lpClassName1.Capacity + 1)); + //Debug.WriteLine(wParam); - string left = lpClassName1.ToString().ToLower(); - if (left.StartsWith("afx")) + var focus = WindowsAPI.GetFocus(); + WindowsAPI.GetClassName(focus, _lpClassName, checked(_lpClassName.Capacity + 1)); + + string left = _lpClassName.ToString().ToLower(); + if (left.StartsWith("afx"))// 在08输入的都从这里进入 { - WindowsAPI.PostMessage(focus, WM_KEYDOWN, new IntPtr(wParam), new IntPtr(65537)); + //Debug.WriteLine("afx"); + + WindowsAPI.PostMessage(focus, WM_KEYDOWN, new IntPtr(wParam), new IntPtr(0x10001)); return true; } - if (left.StartsWith("hwndwrapper")) + if (left.StartsWith("hwndwrapper"))//cad21会进入,高版本的命令提示器? { - IntPtr parent = WindowsAPI.GetParent(focus); + //Debug.WriteLine("hwndwrapper"); + var parent = WindowsAPI.GetParent(focus); StringBuilder lpString = new(byte.MaxValue); WindowsAPI.GetWindowText(parent, lpString, checked(lpString.Capacity + 1)); - if (lpString.ToString().ToLower() == "CLI Palette".ToLower()) + if (lpString.ToString().ToLower() != "cli palette")//"CLI Palette".ToLower() { - WindowsAPI.PostMessage(focus, WM_KEYUP, new IntPtr(wParam), new IntPtr(65537)); - return true; - } - - StringBuilder lpClassName2 = new(byte.MaxValue); - WindowsAPI.GetClassName(parent, lpClassName2, checked(lpClassName2.Capacity + 1)); - if (lpClassName2.ToString().ToLower().StartsWith("afxmdiframe")) - { - WindowsAPI.PostMessage(focus, WM_KEYUP, new IntPtr(wParam), new IntPtr(65537)); - return true; + WindowsAPI.GetClassName(parent, _lpClassName, checked(_lpClassName.Capacity + 1)); + if (!_lpClassName.ToString().ToLower().StartsWith("afxmdiframe")) + return false; } + WindowsAPI.PostMessage(focus, WM_KEYUP, new IntPtr(wParam), new IntPtr(0x10001)); + return true; } if (left.StartsWith("edit")) { - IntPtr parent = WindowsAPI.GetParent(focus); - StringBuilder lpClassName2 = new(byte.MaxValue); - WindowsAPI.GetClassName(parent, lpClassName2, checked(lpClassName2.Capacity + 1)); - if (lpClassName2.ToString().ToLower().StartsWith("afx") && WindowsAPI.GetParent(parent) != doc.Window.Handle) + //Debug.WriteLine("edit"); + + var parent = WindowsAPI.GetParent(focus); + WindowsAPI.GetClassName(parent, _lpClassName, checked(_lpClassName.Capacity + 1)); + if (_lpClassName.ToString().ToLower().StartsWith("afx") && + WindowsAPI.GetParent(parent) != doc.Window.Handle) { WindowsAPI.PostMessage(focus, WM_KEYDOWN, new IntPtr(wParam), new IntPtr(3735553)); return true; @@ -109,18 +130,21 @@ public static bool IMEHook(int nCode, int wParam, int lParam) } if (left == "cicerouiwndframe") + { + //Debug.WriteLine("cicerouiwndframe"); return true; + } } return false; } internal static void UnIMEHook() { - if (!(hnexthookproc != IntPtr.Zero)) - return; - - WindowsAPI.UnhookWindowsHookEx(hnexthookproc); - hnexthookproc = IntPtr.Zero; + if (hnexthookproc != IntPtr.Zero) + { + WindowsAPI.UnhookWindowsHookEx(hnexthookproc); + hnexthookproc = IntPtr.Zero; + } } internal static void SetIMEHook() @@ -150,7 +174,7 @@ internal static void SetIMEHook() return; } - var moduleHandle = (long)WindowsAPI.GetModuleHandle(Process.GetCurrentProcess().MainModule.ModuleName); + var moduleHandle = (long)WindowsAPI.GetModuleHandle(_Process.MainModule.ModuleName); if (Marshal.SizeOf(typeof(IntPtr)) == 4) { HookProcX86 = (nCode, wParam, lParam) => { @@ -196,8 +220,10 @@ static bool Mk2(int nCode, int wParam, IntPtr lParam) if (Control.ModifierKeys == Keys.None) { var hook = KeyboardHookStruct.Create(lParam); - if (WindowsAPI.GetKeyState(162) < 0 || WindowsAPI.GetKeyState(163) < 0 || - WindowsAPI.GetKeyState(17) < 0 || WindowsAPI.GetKeyState(262144) < 0) + if (WindowsAPI.GetKeyState(162) < 0 || + WindowsAPI.GetKeyState(163) < 0 || + WindowsAPI.GetKeyState(17) < 0 || + WindowsAPI.GetKeyState(262144) < 0) return false; if (IMEHook(nCode, hook.vkCode, 0)) return true; @@ -211,6 +237,11 @@ static bool Mk2(int nCode, int wParam, IntPtr lParam) return false; } + /// + /// 注册表增加低级钩子超时处理,防止系统不允许, + /// 否则:偶发性出现 键盘钩子不能用了,而且退出时产生 1404 错误 + /// https://www.cnblogs.com/songr/p/5131655.html + /// static void CheckLowLevelHooksTimeout() { const string llh = "LowLevelHooksTimeout"; -- Gitee From 2762f13421f7708f2aa735fe84b663a9329b7a26 Mon Sep 17 00:00:00 2001 From: liuqihong <540762622@qq.com> Date: Sun, 30 Oct 2022 00:37:48 +0800 Subject: [PATCH 594/675] =?UTF-8?q?=E5=88=86=E7=A6=BB=E5=9F=BA=E7=A1=80?= =?UTF-8?q?=E8=AE=A1=E6=97=B6=E5=99=A8=E5=88=B0Basal,Cad=E5=B7=A5=E7=A8=8B?= =?UTF-8?q?=E4=B8=AD=E4=BF=9D=E7=95=99=E8=B0=83=E7=94=A8=E6=96=B9=E6=B3=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/IFoxCAD.Basal/General/Timer.cs | 134 ++++++++++++++++ .../ExtensionMethod/Tools.cs | 149 ++---------------- tests/TestShared/TestQuadTree.cs | 8 +- 3 files changed, 147 insertions(+), 144 deletions(-) create mode 100644 src/IFoxCAD.Basal/General/Timer.cs diff --git a/src/IFoxCAD.Basal/General/Timer.cs b/src/IFoxCAD.Basal/General/Timer.cs new file mode 100644 index 0000000..3eee06c --- /dev/null +++ b/src/IFoxCAD.Basal/General/Timer.cs @@ -0,0 +1,134 @@ +namespace IFoxCAD.Basal; + +/* +// 测试例子,同时验证两个计时器 +var stopwatch = new Stopwatch(); +Timer.RunTime(() => { + stopwatch.Start(); + for (int i = 0; i < 10000000; i++) + i++; + stopwatch.Stop(); +}, Timer.TimeEnum.Millisecond, "运行:"); +Console.WriteLine("运行毫秒:" + stopwatch.ElapsedMilliseconds); + */ + +using System.ComponentModel; +using System.Runtime.InteropServices; + +public class Timer +{ + [Flags] + public enum TimeEnum + { + /// + /// 秒 + /// + Second, + /// + /// 毫秒 + /// + Millisecond, + /// + /// 微秒 + /// + Microsecond, + /// + /// 纳秒 + /// + Nanosecond, + } + + [DllImport("Kernel32.dll")] + static extern bool QueryPerformanceCounter(out long lpPerformanceCount); + + /// + /// 这个函数会检索性能计数器的频率. + /// 性能计数器的频率在系统启动时是固定的,并且在所有处理器上都是一致的 + /// 因此,只需在应用初始化时查询频率,即可缓存结果 + /// 在运行 Windows XP 或更高版本的系统上,该函数将始终成功,因此永远不会返回零 + /// + /// + /// + [DllImport("Kernel32.dll")] + static extern bool QueryPerformanceFrequency(out long lpFrequency); + + long _startTime, _stopTime; + readonly long _freq; + + public Timer() + { + _startTime = 0; + _stopTime = 0; + + if (!QueryPerformanceFrequency(out _freq)) + throw new Win32Exception("不支持高性能计数器"); + } + + /// + /// 开始计时器 + /// + public void Start() + { + System.Threading.Thread.Sleep(0); + QueryPerformanceCounter(out _startTime); + } + + /// + /// 停止计时器 + /// + public void Stop() + { + QueryPerformanceCounter(out _stopTime); + _Second = (double)(_stopTime - _startTime) / _freq; + } + double _Second = 0; + + // 返回计时器经过时间 + public double Second => _Second; + public double Millisecond => _Second * 1000.0; + public double Microsecond => _Second * 1000000.0; + public double Nanosecond => _Second * 1000000000.0; + + public static double RunTime(Action action, + TimeEnum timeEnum = TimeEnum.Millisecond) + { + var nanoSecond = new Timer(); + nanoSecond.Start(); + action(); + nanoSecond.Stop(); + + var time = 0.0; + switch (timeEnum) + { + case TimeEnum.Second: + time = nanoSecond.Second; + break; + case TimeEnum.Millisecond: + time = nanoSecond.Millisecond; + break; + case TimeEnum.Microsecond: + time = nanoSecond.Microsecond; + break; + case TimeEnum.Nanosecond: + time = nanoSecond.Nanosecond; + break; + } + //string timeNameZn = ""; + //switch (timeEnum) + //{ + // case TimeEnum.Second: + // timeNameZn = " 秒"; + // break; + // case TimeEnum.Millisecond: + // timeNameZn = " 毫秒"; + // break; + // case TimeEnum.Microsecond: + // timeNameZn = " 微秒"; + // break; + // case TimeEnum.Nanosecond: + // timeNameZn = " 纳秒"; + // break; + //} + return time; + } +} \ No newline at end of file diff --git a/src/IFoxCAD.Cad.Shared/ExtensionMethod/Tools.cs b/src/IFoxCAD.Cad.Shared/ExtensionMethod/Tools.cs index 32359c9..1127722 100644 --- a/src/IFoxCAD.Cad.Shared/ExtensionMethod/Tools.cs +++ b/src/IFoxCAD.Cad.Shared/ExtensionMethod/Tools.cs @@ -1,4 +1,6 @@ -namespace IFoxCAD.Cad; +using static IFoxCAD.Basal.Timer; + +namespace IFoxCAD.Cad; public static class Tools { @@ -29,9 +31,9 @@ public static void TestTimes2(int count, string message, Action action) /// [System.Diagnostics.DebuggerStepThrough] public static void TestTimes(int count, string message, Action action, - Timer.TimeEnum timeEnum = Timer.TimeEnum.Millisecond) + TimeEnum timeEnum = TimeEnum.Millisecond) { - double time = Timer.RunTime(() => { + var time = RunTime(() => { for (int i = 0; i < count; i++) action(); }, timeEnum); @@ -39,153 +41,20 @@ public static void TestTimes(int count, string message, Action action, string timeNameZn = ""; switch (timeEnum) { - case Timer.TimeEnum.Second: + case TimeEnum.Second: timeNameZn = " 秒"; break; - case Timer.TimeEnum.Millisecond: + case TimeEnum.Millisecond: timeNameZn = " 毫秒"; break; - case Timer.TimeEnum.Microsecond: + case TimeEnum.Microsecond: timeNameZn = " 微秒"; break; - case Timer.TimeEnum.Nanosecond: + case TimeEnum.Nanosecond: timeNameZn = " 纳秒"; break; } Env.Print($"{message} 代码执行 {count} 次的时间:{time} ({timeNameZn})"); } -} - -/* -// 测试例子,同时验证两个计时器 -var stopwatch = new Stopwatch(); -Timer.RunTime(() => { - stopwatch.Start(); - for (int i = 0; i < 10000000; i++) - i++; - stopwatch.Stop(); -}, Timer.TimeEnum.Millisecond, "运行:"); -Console.WriteLine("运行毫秒:" + stopwatch.ElapsedMilliseconds); - */ - -public class Timer -{ - [Flags] - public enum TimeEnum - { - /// - /// 秒 - /// - Second, - /// - /// 毫秒 - /// - Millisecond, - /// - /// 微秒 - /// - Microsecond, - /// - /// 纳秒 - /// - Nanosecond, - } - - [DllImport("Kernel32.dll")] - static extern bool QueryPerformanceCounter(out long lpPerformanceCount); - - /// - /// 这个函数会检索性能计数器的频率. - /// 性能计数器的频率在系统启动时是固定的,并且在所有处理器上都是一致的 - /// 因此,只需在应用初始化时查询频率,即可缓存结果 - /// 在运行 Windows XP 或更高版本的系统上,该函数将始终成功,因此永远不会返回零 - /// - /// - /// - [DllImport("Kernel32.dll")] - static extern bool QueryPerformanceFrequency(out long lpFrequency); - - long _startTime, _stopTime; - readonly long _freq; - - public Timer() - { - _startTime = 0; - _stopTime = 0; - - if (!QueryPerformanceFrequency(out _freq)) - throw new Win32Exception("不支持高性能计数器"); - } - - /// - /// 开始计时器 - /// - public void Start() - { - System.Threading.Thread.Sleep(0); - QueryPerformanceCounter(out _startTime); - } - - /// - /// 停止计时器 - /// - public void Stop() - { - QueryPerformanceCounter(out _stopTime); - _Second = (double)(_stopTime - _startTime) / _freq; - } - double _Second = 0; - - // 返回计时器经过时间 - public double Second => _Second; - public double Millisecond => _Second * 1000.0; - public double Microsecond => _Second * 1000000.0; - public double Nanosecond => _Second * 1000000000.0; - - public static double RunTime(Action action, TimeEnum timeEnum = TimeEnum.Millisecond, string? msg = null) - { - var nanoSecond = new Timer(); - nanoSecond.Start(); - action(); - nanoSecond.Stop(); - - double time = 0; - switch (timeEnum) - { - case TimeEnum.Second: - time = nanoSecond.Second; - break; - case TimeEnum.Millisecond: - time = nanoSecond.Millisecond; - break; - case TimeEnum.Microsecond: - time = nanoSecond.Microsecond; - break; - case TimeEnum.Nanosecond: - time = nanoSecond.Nanosecond; - break; - } - if (msg != null) - { - string timeNameZn = ""; - switch (timeEnum) - { - case TimeEnum.Second: - timeNameZn = " 秒"; - break; - case TimeEnum.Millisecond: - timeNameZn = " 毫秒"; - break; - case TimeEnum.Microsecond: - timeNameZn = " 微秒"; - break; - case TimeEnum.Nanosecond: - timeNameZn = " 纳秒"; - break; - } - Env.Print(msg + " " + time + timeNameZn); - } - return time; - } } \ No newline at end of file diff --git a/tests/TestShared/TestQuadTree.cs b/tests/TestShared/TestQuadTree.cs index 4c129ee..8e16117 100644 --- a/tests/TestShared/TestQuadTree.cs +++ b/tests/TestShared/TestQuadTree.cs @@ -87,7 +87,7 @@ public void Test_QuadTree() // 随机图元生成 List ces = new(); // 用于随机获取图元 - Timer.RunTime(() => { + Tools.TestTimes(1, "画圆消耗时间:", () => { // 生成外边界和随机圆形 var grc = GenerateRandomCircle(maximumItems, dbExt); foreach (var ent in grc) @@ -111,13 +111,13 @@ public void Test_QuadTree() var dbPointCe = new CadEntity(entId, entRect); ces.Add(dbPointCe); } - }, Timer.TimeEnum.Millisecond, "画圆消耗时间:");// 30万图元±3秒.cad2021 + });// 30万图元±3秒.cad2021 // 测试只加入四叉树的时间 - Timer.RunTime(() => { + Tools.TestTimes(1, "插入四叉树时间:", () => { for (int i = 0; i < ces.Count; i++) _quadTreeRoot.Insert(ces[i]); - }, Timer.TimeEnum.Millisecond, "插入四叉树时间:");// 30万图元±0.7秒.cad2021 + });// 30万图元±0.7秒.cad2021 tr.Editor?.WriteMessage($"\n加入图元数量:{maximumItems}"); } -- Gitee From 9a65d5c0709ae9c7004292838e53b36a34bf8cc9 Mon Sep 17 00:00:00 2001 From: liuqihong <540762622@qq.com> Date: Sun, 30 Oct 2022 01:09:53 +0800 Subject: [PATCH 595/675] =?UTF-8?q?=E5=BA=8F=E5=88=97=E5=8C=96=E9=80=9F?= =?UTF-8?q?=E5=BA=A6=E6=B5=8B=E8=AF=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- tests/TestAcad08/TestAcad08.csproj | 3 +- tests/TestShared/TestShared.projitems | 1 + tests/TestShared/TestXRecord.cs | 147 ++++++++++++++++++++++++++ 3 files changed, 150 insertions(+), 1 deletion(-) create mode 100644 tests/TestShared/TestXRecord.cs diff --git a/tests/TestAcad08/TestAcad08.csproj b/tests/TestAcad08/TestAcad08.csproj index f8150a0..80f7a0b 100644 --- a/tests/TestAcad08/TestAcad08.csproj +++ b/tests/TestAcad08/TestAcad08.csproj @@ -11,7 +11,7 @@ - $(Configuration);acad;ac2008;ac2009 + $(Configuration);acad;ac2008;ac2009;NewtonsoftJson @@ -28,6 +28,7 @@ + diff --git a/tests/TestShared/TestShared.projitems b/tests/TestShared/TestShared.projitems index 595b36a..7c5a772 100644 --- a/tests/TestShared/TestShared.projitems +++ b/tests/TestShared/TestShared.projitems @@ -32,6 +32,7 @@ + \ No newline at end of file diff --git a/tests/TestShared/TestXRecord.cs b/tests/TestShared/TestXRecord.cs new file mode 100644 index 0000000..8fe4578 --- /dev/null +++ b/tests/TestShared/TestXRecord.cs @@ -0,0 +1,147 @@ +#if NewtonsoftJson +namespace Test_XRecord; + +using IFoxCAD.Cad; +using Newtonsoft.Json; +using System.Collections.Generic; +using static IFoxCAD.Cad.WindowsAPI; + +public class TestCmd_XRecord +{ + [CommandMethod(nameof(TestSerializeSetXRecord))] + public void TestSerializeSetXRecord() + { + var prs = Env.Editor.SSGet("\n 序列化,选择多段线:"); + if (prs.Status != PromptStatus.OK) + return; + + using var tr = new DBTrans(); + var pls = prs.Value.GetEntities(); + Tools.TestTimes(1, nameof(TestSerializeSetXRecord), () => { + foreach (var pl in pls) + { + if (pl == null) + continue; + + TestABCList datas = new(); + for (int i = 0; i < 5; i++) + { + datas.Add(new() + { + AAA = i, + BBB = $"{i}", + CCCC = i * 0.5, + DDDD = i % 2 != 0, + EEEE = new(0, i, 0) + }); + } + + using (pl.ForWrite()) + pl.SerializeXRecord(datas); + } + }); + } + + + [CommandMethod(nameof(TestDeserializeGetXRecord))] + public void TestDeserializeGetXRecord() + { + var prs = Env.Editor.GetEntity("\n 反序列化,选择多段线:"); + if (prs.Status != PromptStatus.OK) + return; + + using var tr = new DBTrans(); + Tools.TestTimes(1, nameof(TestDeserializeGetXRecord), () => { + var pl = prs.ObjectId.GetObject(); + if (pl == null) + return; + var data = pl.DeserializeXRecord(); + if (data == null) + return; + Env.Printl(data); + }); + } +} + +public class TestABCList : List +{ + public override string ToString() + { + var sb = new StringBuilder(); + for (int i = 0; i < Count; i++) + sb.Append(this[i]); + return sb.ToString(); + } +} + +public class TestABC +{ + public int AAA { get; set; } + public string? BBB { get; set; } + public double CCCC { get; set; } + public bool DDDD { get; set; } + public Point3D EEEE { get; set; } + + public override string ToString() + { + return JsonConvert.SerializeObject(this, XRecordHelper._sset); + } +} + + +public static class XRecordHelper +{ + internal static JsonSerializerSettings _sset = new() + { + Formatting = Formatting.Indented, + TypeNameHandling = TypeNameHandling.Auto + }; + static string[] _newl = new string[] { Environment.NewLine }; + + /// + /// 设定信息 + /// + /// 储存对象 + /// 储存数据 + public static void SerializeXRecord(this DBObject dbo, T data) + { + var xd = dbo.GetXDictionary(); + if (xd == null) + return; + var json = JsonConvert.SerializeObject(data, _sset); + var arrStr = json.Split(_newl, StringSplitOptions.None); + XRecordDataList datas = new(); + for (int i = 0; i < arrStr.Length; i++) + datas.Add(DxfCode.XTextString, arrStr[i]); + xd.SetXRecord(typeof(T).FullName, datas); + } + + + internal static JsonSerializerSettings _sset2 = new() + { + Formatting = Formatting.Indented, + TypeNameHandling = TypeNameHandling.All, + }; + /// + /// 提取信息 + /// + /// 类型 + /// 储存对象 + /// 提取数据生成的对象 + public static T? DeserializeXRecord(this DBObject dbo) + { + var xd = dbo.GetXDictionary(); + if (xd == null) + return default; + + var datas = xd.GetXRecord(typeof(T).FullName); + if (datas == null) + return default; + + var sb = new StringBuilder(); + for (int i = 0; i < datas.Count; i++) + sb.Append(datas[i].Value); + return JsonConvert.DeserializeObject(sb.ToString(), _sset2); + } +} +#endif \ No newline at end of file -- Gitee From ee0cb01e75dbaa5da40491cf636c4dfb3f84ddba Mon Sep 17 00:00:00 2001 From: liuqihong <540762622@qq.com> Date: Sun, 30 Oct 2022 03:23:07 +0800 Subject: [PATCH 596/675] =?UTF-8?q?=E4=BC=98=E5=8C=96=E9=80=9F=E5=BA=A6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/IFoxCAD.Cad.Shared/Copyclip/WindowsAPI.cs | 13 ++++ .../ExtensionMethod/DBDictionaryEx.cs | 29 ++++---- .../IMEControl.cs" | 16 +++-- tests/TestShared/TestMarshal.cs | 2 +- tests/TestShared/TestXRecord.cs | 66 +++++++++---------- 5 files changed, 74 insertions(+), 52 deletions(-) diff --git a/src/IFoxCAD.Cad.Shared/Copyclip/WindowsAPI.cs b/src/IFoxCAD.Cad.Shared/Copyclip/WindowsAPI.cs index 8634da3..960d2b1 100644 --- a/src/IFoxCAD.Cad.Shared/Copyclip/WindowsAPI.cs +++ b/src/IFoxCAD.Cad.Shared/Copyclip/WindowsAPI.cs @@ -4,6 +4,7 @@ namespace IFoxCAD.Cad; using System; using System.Diagnostics; using System.Threading; +using System.Windows.Forms; public partial class WindowsAPI { @@ -544,6 +545,7 @@ public IntSize(int cx, int cy) [Serializable] [DebuggerDisplay("{DebuggerDisplay,nq}")] [DebuggerTypeProxy(typeof(Point3D))] + [StructLayout(LayoutKind.Sequential)] public struct Point3D : IEquatable { [DebuggerBrowsable(DebuggerBrowsableState.Never)] @@ -570,6 +572,17 @@ public static implicit operator Point3d(Point3D pt) } public override string ToString() => $"({X},{Y},{Z})"; + public static Point3D Create(IntPtr intPtr) + { + return (Point3D)Marshal.PtrToStructure(intPtr, typeof(Point3D)); + } + + public void PutCreate(IntPtr intPtr) + { + Marshal.StructureToPtr(this, intPtr, true); + } + + #region 重载运算符_比较 public bool Equals(Point3D other) { diff --git a/src/IFoxCAD.Cad.Shared/ExtensionMethod/DBDictionaryEx.cs b/src/IFoxCAD.Cad.Shared/ExtensionMethod/DBDictionaryEx.cs index 7fea5c1..a19b33a 100644 --- a/src/IFoxCAD.Cad.Shared/ExtensionMethod/DBDictionaryEx.cs +++ b/src/IFoxCAD.Cad.Shared/ExtensionMethod/DBDictionaryEx.cs @@ -51,14 +51,14 @@ public static IEnumerable GetAllObjects(this DBDictionary dict, DBTrans? t /// 字典 /// 事务 /// 键 - /// 值 - public static void SetAt(this DBDictionary dict, string key, T obj, Transaction? trans = null) where T : DBObject + /// 值 + public static void SetAt(this DBDictionary dict, string key, T newValue, Transaction? trans = null) where T : DBObject { trans ??= DBTrans.Top.Transaction; using (dict.ForWrite()) { - dict.SetAt(key, obj); - trans.AddNewlyCreatedDBObject(obj, true); + dict.SetAt(key, newValue); + trans.AddNewlyCreatedDBObject(newValue, true); } } @@ -72,10 +72,10 @@ public static void SetAt(this DBDictionary dict, string key, T obj, Transacti /// 扩展数据 public static XRecordDataList? GetXRecord(this DBDictionary dict, string key) { - Xrecord? rec = dict.GetAt(key); - if (rec is not null) - return rec.Data; - return null; + var rec = dict.GetAt(key); + if (rec is null) + return null; + return rec.Data; } /// @@ -86,8 +86,8 @@ public static void SetAt(this DBDictionary dict, string key, T obj, Transacti /// 键值 public static void SetXRecord(this DBDictionary dict, string key, XRecordDataList rb) { - using var data = new Xrecord { Data = rb }; - dict.SetAt(key, data); + using var newValue = new Xrecord { Data = rb }; + dict.SetAt(key, newValue); } #endregion @@ -124,10 +124,12 @@ public static DataTable CreateDataTable(Dictionary colTypes, o DataTable table = new(); foreach (var t in colTypes) table.AppendColumn(t.Value, t.Key); + var ncol = colTypes.Count; - var nrow = content.GetLength(0); var types = new CellType[ncol]; colTypes.Values.CopyTo(types, 0); + + var nrow = content.GetLength(0); for (int i = 0; i < nrow; i++) { DataCellCollection row = new(); @@ -208,7 +210,10 @@ public static void SetValue(this DataCell cell, CellType type, object value) /// 是否创建子字典 /// 键值列表 /// 字典 - public static DBDictionary? GetSubDictionary(this DBDictionary dict, bool createSubDictionary, IEnumerable dictNames, DBTrans? trans = null) + public static DBDictionary? GetSubDictionary(this DBDictionary dict, + bool createSubDictionary, + IEnumerable dictNames, + DBTrans? trans = null) { DBDictionary? newdict = null; trans ??= DBTrans.Top; diff --git "a/tests/TestAcad09plus/\350\276\223\345\205\245\346\263\225\350\207\252\345\212\250\345\210\207\346\215\242/\350\265\253\346\200\235\350\276\223\345\205\245\346\263\225/IMEControl.cs" "b/tests/TestAcad09plus/\350\276\223\345\205\245\346\263\225\350\207\252\345\212\250\345\210\207\346\215\242/\350\265\253\346\200\235\350\276\223\345\205\245\346\263\225/IMEControl.cs" index 2c31254..e225458 100644 --- "a/tests/TestAcad09plus/\350\276\223\345\205\245\346\263\225\350\207\252\345\212\250\345\210\207\346\215\242/\350\265\253\346\200\235\350\276\223\345\205\245\346\263\225/IMEControl.cs" +++ "b/tests/TestAcad09plus/\350\276\223\345\205\245\346\263\225\350\207\252\345\212\250\345\210\207\346\215\242/\350\265\253\346\200\235\350\276\223\345\205\245\346\263\225/IMEControl.cs" @@ -1,7 +1,7 @@ -using System.Diagnostics; -using System.Windows.Forms; +namespace Gstar_IMEFilter; -namespace Gstar_IMEFilter; +using System.Diagnostics; +using System.Windows.Forms; public class IMEControl { @@ -250,6 +250,12 @@ static void CheckLowLevelHooksTimeout() registryKey.SetValue(llh, 25000, RegistryValueKind.DWord); } + + [ComVisible(true)] + [Serializable] + //[DebuggerDisplay("{DebuggerDisplay,nq}")] + //[DebuggerTypeProxy(typeof(KeyboardHookStruct))] + [StructLayout(LayoutKind.Sequential)] public struct KeyboardHookStruct { public int vkCode; @@ -258,9 +264,9 @@ public struct KeyboardHookStruct public int Time; public int DwExtraInfo; - public static KeyboardHookStruct Create(IntPtr lParam) + public static KeyboardHookStruct Create(IntPtr intPtr) { - return (KeyboardHookStruct)Marshal.PtrToStructure(lParam, typeof(KeyboardHookStruct)); + return (KeyboardHookStruct)Marshal.PtrToStructure(intPtr, typeof(KeyboardHookStruct)); } } } \ No newline at end of file diff --git a/tests/TestShared/TestMarshal.cs b/tests/TestShared/TestMarshal.cs index 963b802..1e0fba5 100644 --- a/tests/TestShared/TestMarshal.cs +++ b/tests/TestShared/TestMarshal.cs @@ -85,7 +85,7 @@ public void Test_Marshal() var p = new IntPtr(&pt); var result2 = (Point3D)Marshal.PtrToStructure(p, typeof(Point3D)); result2.X = 220; - Marshal.StructureToPtr(result2, p, true); + result2.PutCreate(p); } "封送法:".Print(); pt.Print(); diff --git a/tests/TestShared/TestXRecord.cs b/tests/TestShared/TestXRecord.cs index 8fe4578..566ef9e 100644 --- a/tests/TestShared/TestXRecord.cs +++ b/tests/TestShared/TestXRecord.cs @@ -1,9 +1,10 @@ #if NewtonsoftJson namespace Test_XRecord; -using IFoxCAD.Cad; -using Newtonsoft.Json; using System.Collections.Generic; +using System.Diagnostics; +using Newtonsoft.Json; +using IFoxCAD.Cad; using static IFoxCAD.Cad.WindowsAPI; public class TestCmd_XRecord @@ -29,7 +30,7 @@ public void TestSerializeSetXRecord() datas.Add(new() { AAA = i, - BBB = $"{i}", + BBB = i.ToString(), CCCC = i * 0.5, DDDD = i % 2 != 0, EEEE = new(0, i, 0) @@ -37,7 +38,7 @@ public void TestSerializeSetXRecord() } using (pl.ForWrite()) - pl.SerializeXRecord(datas); + pl.SerializeToXRecord(datas); } }); } @@ -51,36 +52,39 @@ public void TestDeserializeGetXRecord() return; using var tr = new DBTrans(); + TestABCList? datas = null; Tools.TestTimes(1, nameof(TestDeserializeGetXRecord), () => { var pl = prs.ObjectId.GetObject(); if (pl == null) return; - var data = pl.DeserializeXRecord(); - if (data == null) - return; - Env.Printl(data); + datas = pl.DeserializeToXRecord(); }); + if (datas == null) + return; + for (int i = 0; i < datas.Count; i++) + Env.Printl(datas[i]); } } public class TestABCList : List { - public override string ToString() - { - var sb = new StringBuilder(); - for (int i = 0; i < Count; i++) - sb.Append(this[i]); - return sb.ToString(); - } } +[ComVisible(true)] +[Serializable] +[DebuggerDisplay("{DebuggerDisplay,nq}")] +[DebuggerTypeProxy(typeof(TestABC))] +[StructLayout(LayoutKind.Sequential, Pack = 4)] public class TestABC { - public int AAA { get; set; } - public string? BBB { get; set; } - public double CCCC { get; set; } - public bool DDDD { get; set; } - public Point3D EEEE { get; set; } + [DebuggerBrowsable(DebuggerBrowsableState.Never)] + private string DebuggerDisplay => ToString(); + + public int AAA; + public string? BBB; + public double CCCC; + public bool DDDD; + public Point3D EEEE; public override string ToString() { @@ -96,39 +100,33 @@ public static class XRecordHelper Formatting = Formatting.Indented, TypeNameHandling = TypeNameHandling.Auto }; - static string[] _newl = new string[] { Environment.NewLine }; /// /// 设定信息 /// /// 储存对象 /// 储存数据 - public static void SerializeXRecord(this DBObject dbo, T data) + public static void SerializeToXRecord(this DBObject dbo, T data) { var xd = dbo.GetXDictionary(); if (xd == null) return; + var json = JsonConvert.SerializeObject(data, _sset); - var arrStr = json.Split(_newl, StringSplitOptions.None); - XRecordDataList datas = new(); - for (int i = 0; i < arrStr.Length; i++) - datas.Add(DxfCode.XTextString, arrStr[i]); + XRecordDataList datas = new() + { + { DxfCode.XTextString, json } + }; xd.SetXRecord(typeof(T).FullName, datas); } - - internal static JsonSerializerSettings _sset2 = new() - { - Formatting = Formatting.Indented, - TypeNameHandling = TypeNameHandling.All, - }; /// /// 提取信息 /// /// 类型 /// 储存对象 /// 提取数据生成的对象 - public static T? DeserializeXRecord(this DBObject dbo) + public static T? DeserializeToXRecord(this DBObject dbo) { var xd = dbo.GetXDictionary(); if (xd == null) @@ -141,7 +139,7 @@ public static void SerializeXRecord(this DBObject dbo, T data) var sb = new StringBuilder(); for (int i = 0; i < datas.Count; i++) sb.Append(datas[i].Value); - return JsonConvert.DeserializeObject(sb.ToString(), _sset2); + return JsonConvert.DeserializeObject(sb.ToString(), _sset); } } #endif \ No newline at end of file -- Gitee From c97031f5403fa0c441b4aa82a67aa78c846b9101 Mon Sep 17 00:00:00 2001 From: liuqihong <540762622@qq.com> Date: Sun, 30 Oct 2022 21:16:14 +0800 Subject: [PATCH 597/675] =?UTF-8?q?=E8=AF=BB=E5=86=99XRecord?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/IFoxCAD.Cad.Shared/Copyclip/EmfTool.cs | 46 +++--- .../Copyclip/TagClipboardInfo.cs | 8 + src/IFoxCAD.Cad.Shared/Copyclip/WindowsAPI.cs | 50 +++--- .../ExtensionMethod/DBDictionaryEx.cs | 3 +- .../ResultData/XRecordDataList.cs | 2 - tests/TestShared/Copyclip.cs | 29 +++- tests/TestShared/TestMarshal.cs | 10 ++ tests/TestShared/TestXRecord.cs | 144 +++++++++++++----- 8 files changed, 198 insertions(+), 94 deletions(-) diff --git a/src/IFoxCAD.Cad.Shared/Copyclip/EmfTool.cs b/src/IFoxCAD.Cad.Shared/Copyclip/EmfTool.cs index 7040654..4a5d82c 100644 --- a/src/IFoxCAD.Cad.Shared/Copyclip/EmfTool.cs +++ b/src/IFoxCAD.Cad.Shared/Copyclip/EmfTool.cs @@ -16,7 +16,6 @@ using Size = System.Drawing.Size; using static IFoxCAD.Cad.WindowsAPI; - // DWORD == uint // WORD == ushort // LONG == int @@ -106,7 +105,7 @@ public static IntPtr Wmf2Emf(string wmfFile) file.Read(fileByte, 0, fileByte.Length); file.Close(); - bool bts = WindowsAPI.BytesToStruct(fileByte, out PlaceableMetaHeader sWMF, out int sWMFsize); + bool bts = BytesToStruct(fileByte, out PlaceableMetaHeader sWMF, out int sWMFsize); if (!bts) throw new IOException("失败:类型转换,路径:" + file); @@ -126,13 +125,12 @@ public static IntPtr Wmf2Emf(string wmfFile) int iOffset = 0; if (sWMF.IsActivity) iOffset = sWMFsize; - IntPtr intPtr = Marshal.UnsafeAddrOfPinnedArrayElement(fileByte, iOffset); - - WindowsAPI.StructToPtr(mpType, mpPtr => { + IntPtr fileIntPtr = Marshal.UnsafeAddrOfPinnedArrayElement(fileByte, iOffset); + unsafe + { hEMF = EmfTool.SetWinMetaFileBits( - (uint)fileByte.Length, intPtr, IntPtr.Zero, mpPtr); - }); - + (uint)fileByte.Length, fileIntPtr, IntPtr.Zero, new IntPtr(&mpType)); + } return hEMF; } } @@ -156,6 +154,8 @@ public void Wmf2Emf(string wmfFile) /// public EnhMetaHeader? CreateEnhMetaHeader() { + if (EmfHandle == IntPtr.Zero) + return null; return EnhMetaHeader.Create(EmfHandle); } } @@ -247,11 +247,11 @@ public override string ToString() /// /// /// - public static EnhMetaHeader? Create(string wmf) + public static EnhMetaHeader Create(string wmf) { var emf = PlaceableMetaHeader.Wmf2Emf(wmf); if (emf == IntPtr.Zero) - throw new ArgumentException(null, nameof(wmf)); + throw new ArgumentException(nameof(emf)); return Create(emf); } @@ -263,20 +263,16 @@ public override string ToString() /// 也就是的返回值 /// /// - public static EnhMetaHeader? Create(IntPtr clipTypeData) + public static EnhMetaHeader Create(IntPtr clipTypeData) { - if (clipTypeData == IntPtr.Zero) - return null; - - EnhMetaHeader? result = null; var len = EmfTool.GetEnhMetaFileHeader(clipTypeData, 0, IntPtr.Zero); - if (len != 0) - { - IntPtr header = Marshal.AllocHGlobal((int)len); - EmfTool.GetEnhMetaFileHeader(clipTypeData, len, header); - result = (EnhMetaHeader)Marshal.PtrToStructure(header, typeof(EnhMetaHeader)); - Marshal.FreeHGlobal(header); - } + if (len == 0) + throw new ArgumentException(nameof(len)); + + IntPtr header = Marshal.AllocHGlobal((int)len); + EmfTool.GetEnhMetaFileHeader(clipTypeData, len, header); + var result = (EnhMetaHeader)Marshal.PtrToStructure(header, typeof(EnhMetaHeader)); + Marshal.FreeHGlobal(header); return result; } } @@ -557,11 +553,7 @@ public static void SetEnhMetaFileDescriptionEx(ref IntPtr hMetaFile, string desc if (hMetaFile == IntPtr.Zero) throw new ArgumentNullException(nameof(hMetaFile)); - var em = EnhMetaHeader.Create(hMetaFile);//emf结构 GetEnhMetaFileHeader - if (em == null) - return; - - var emh = em.Value; + var emh = EnhMetaHeader.Create(hMetaFile);//emf结构 GetEnhMetaFileHeader // 创建画布句柄 IntRect intRect = emh.rclFrame; //new(0, 0, 0, 0); var hMetaDC = EmfTool.CreateEnhMetaFile(IntPtr.Zero, null!, ref intRect, desc); diff --git a/src/IFoxCAD.Cad.Shared/Copyclip/TagClipboardInfo.cs b/src/IFoxCAD.Cad.Shared/Copyclip/TagClipboardInfo.cs index 5c2402f..1e3327e 100644 --- a/src/IFoxCAD.Cad.Shared/Copyclip/TagClipboardInfo.cs +++ b/src/IFoxCAD.Cad.Shared/Copyclip/TagClipboardInfo.cs @@ -189,6 +189,14 @@ public override int GetHashCode() nType ^ chData.GetHashCode(); } + + public IntPtr CloneToIntPtr() + { + var newPtr = Marshal.AllocHGlobal(Marshal.SizeOf(this)); + if (newPtr != IntPtr.Zero) + Marshal.StructureToPtr(this, newPtr, true); + return newPtr; + } #endregion } diff --git a/src/IFoxCAD.Cad.Shared/Copyclip/WindowsAPI.cs b/src/IFoxCAD.Cad.Shared/Copyclip/WindowsAPI.cs index 960d2b1..6d8580e 100644 --- a/src/IFoxCAD.Cad.Shared/Copyclip/WindowsAPI.cs +++ b/src/IFoxCAD.Cad.Shared/Copyclip/WindowsAPI.cs @@ -124,7 +124,7 @@ public static bool BytesToStruct(byte[] bytes, out T? result, out int typeSiz IntPtr structPtr = Marshal.AllocHGlobal(typeSize); // 将byte数组拷到分配好的内存空间 Marshal.Copy(bytes, 0, structPtr, typeSize); - // 将内存空间转换为目标结构体 + // 将内存空间转换为目标结构体,转类型的时候会拷贝一次 result = (T)Marshal.PtrToStructure(structPtr, structType); // 释放内存空间 Marshal.FreeHGlobal(structPtr); @@ -133,21 +133,29 @@ public static bool BytesToStruct(byte[] bytes, out T? result, out int typeSiz /// /// 结构体转byte数组 + /// unmanaged /// /// 要转换的结构体 - public static byte[] StructToBytes(object? structObj) + public static byte[] StructToBytes(T structObj) where T : unmanaged { // 得到结构体的大小 - int typeSize = Marshal.SizeOf(structObj); + var typeSize = Marshal.SizeOf(structObj); // 从内存空间拷到byte数组 - byte[] bytes = new byte[typeSize]; - - StructToPtr(structObj, structPtr => { - Marshal.Copy(structPtr, bytes, 0, typeSize); - }); + var bytes = new byte[typeSize]; + unsafe + { + Marshal.Copy(new IntPtr(&structObj), bytes, 0, typeSize); + } +#if true20221030 + // 安全写法效率太低了 + StructToPtr(structObj, structPtr => { + Marshal.Copy(structPtr, bytes, 0, typeSize); + }); +#endif return bytes; } +#if true20221030 /// /// 结构体转指针 /// @@ -156,25 +164,32 @@ public static byte[] StructToBytes(object? structObj) /// 释放申请的内存 /// 是否锁定内存 /// - public static void StructToPtr(object? structObj, + public static void StructToPtr(T structObj, Action? task = null, bool freeHGlobal = true, bool lockPrt = true) { - if (structObj == null) - throw new ArgumentNullException(nameof(structObj)); IntPtr newPtr = Marshal.AllocHGlobal(Marshal.SizeOf(structObj)); if (newPtr == IntPtr.Zero) throw new ArgumentException(nameof(newPtr)); + try { // 剪贴板写入的时候不允许锁定内存,否则在频繁触发剪贴板将导致卡死程序 if (lockPrt) + { GlobalLockTask(newPtr, ptr => { - ToPtr(structObj, task, ptr); + // 将结构体拷到分配好的内存空间 + Marshal.StructureToPtr(structObj, newPtr, true); + task?.Invoke(newPtr); }); + } else - ToPtr(structObj, task, newPtr); + { + // 将结构体拷到分配好的内存空间 + Marshal.StructureToPtr(structObj, newPtr, true); + task?.Invoke(newPtr); + } } catch (Exception e) { @@ -186,15 +201,8 @@ public static void StructToPtr(object? structObj, if (freeHGlobal && newPtr != IntPtr.Zero) Marshal.FreeHGlobal(newPtr); } - - // 将结构体拷到分配好的内存空间 - static void ToPtr(object? structObj, Action? task, IntPtr newPtr) - { - Marshal.StructureToPtr(structObj, newPtr, true); - task?.Invoke(newPtr); - } } - +#endif #endregion } diff --git a/src/IFoxCAD.Cad.Shared/ExtensionMethod/DBDictionaryEx.cs b/src/IFoxCAD.Cad.Shared/ExtensionMethod/DBDictionaryEx.cs index a19b33a..3375e55 100644 --- a/src/IFoxCAD.Cad.Shared/ExtensionMethod/DBDictionaryEx.cs +++ b/src/IFoxCAD.Cad.Shared/ExtensionMethod/DBDictionaryEx.cs @@ -224,7 +224,7 @@ public static void SetValue(this DataCell cell, CellType type, object value) foreach (string name in dictNames) { - if (dict!.Contains(name)) + if (dict.Contains(name)) { newdict = dict.GetAt(name, trans); } @@ -247,7 +247,6 @@ public static void SetValue(this DataCell cell, CellType type, object value) return null; } } - return newdict; } diff --git a/src/IFoxCAD.Cad.Shared/ResultData/XRecordDataList.cs b/src/IFoxCAD.Cad.Shared/ResultData/XRecordDataList.cs index 1fb19d8..224064f 100644 --- a/src/IFoxCAD.Cad.Shared/ResultData/XRecordDataList.cs +++ b/src/IFoxCAD.Cad.Shared/ResultData/XRecordDataList.cs @@ -26,9 +26,7 @@ public XRecordDataList(IEnumerable values) : base(values) { } public override void Add(int code, object obj) { if (code >= 1000) - { throw new System.Exception("传入的组码值不是 XRecordData 有效范围!"); - } Add(new TypedValue(code, obj)); } diff --git a/tests/TestShared/Copyclip.cs b/tests/TestShared/Copyclip.cs index c3ea6c1..a94e51b 100644 --- a/tests/TestShared/Copyclip.cs +++ b/tests/TestShared/Copyclip.cs @@ -29,11 +29,20 @@ public class Copyclip { #region 命令 #if test - //[IFoxInitialize] 惊惊: 遇到了高版本无法导出WMF,放弃此功能,等待有缘人 + static bool _IsRunIFoxCopyClip = false; + [IFoxInitialize] // 惊惊: 遇到了高版本无法导出WMF,放弃此功能,等待有缘人 public void Init() { Acap.DocumentManager.DocumentLockModeChanged += DocumentManager_DocumentLockModeChanged; + Env.Printl($"※剪贴板控制※\n{nameof(Copyclip_Switch)} - 切换开关\n"); + } + + [CommandMethod(nameof(Copyclip_Switch))] + public void Copyclip_Switch() + { + _IsRunIFoxCopyClip = !_IsRunIFoxCopyClip; + Env.Printl("已经 " + (_IsRunIFoxCopyClip ? "开启" : "禁用") + " 剪贴板+"); } /// @@ -43,6 +52,9 @@ public void Init() /// void DocumentManager_DocumentLockModeChanged(object sender, DocumentLockModeChangedEventArgs e) { + if (!_IsRunIFoxCopyClip) + return; + var up = e.GlobalCommandName.ToUpper(); string? cmd = null; @@ -252,7 +264,6 @@ void Copy(bool getPoint, bool isEraseSsget = false) // 保存文件 //var emfsave = Path.ChangeExtension(cadClipType.File, ".emf"); //EmfTool.Save(emf, emfsave); - #endregion #region 写入 AutoCAD.R17 数据 @@ -312,11 +323,13 @@ void Copy(bool getPoint, bool isEraseSsget = false) // 必须一次性写入剪贴板,详见 OpenClipboardTask var cadClipFormat = ClipTool.RegisterClipboardFormat(ClipboardEnv.CadVer); + + // 剪贴板需要的指针: 克隆一个新的,不释放内存,不锁定内存(否则高频触发时候卡死) + var cadClipData = cadClipType.CloneToIntPtr(); + bool getFlag = ClipTool.OpenClipboardTask(true, () => { // 写入剪贴板: cad图元 - WindowsAPI.StructToPtr(cadClipType, cadClipData => { - ClipTool.SetClipboardData(cadClipFormat, cadClipData); - }, false/*不释放内存*/, false/*不锁定内存(否则高频触发时候卡死)*/); + ClipTool.SetClipboardData(cadClipFormat, cadClipData); // 写入剪贴板: wmf,使得在粘贴链接的时候可以用 if (hMetaFile != IntPtr.Zero) @@ -524,7 +537,8 @@ void Paste(bool isBlock) return; } - int a4 = 1 | 2 | 4; + int a4 = 1; + //int a4 = 1 | 2 | 4; if ((a4 & 1) == 1) { // 获取描述 @@ -536,8 +550,7 @@ void Paste(bool isBlock) { // 获取文件信息 var obj = EnhMetaHeader.Create(clipTypeData); - if (obj != null) - msg.AppendLine("EnhMetaHeader::" + obj.Value.ToString()); + msg.AppendLine("EnhMetaHeader::" + obj.ToString()); } if ((a4 & 4) == 4) { diff --git a/tests/TestShared/TestMarshal.cs b/tests/TestShared/TestMarshal.cs index 1e0fba5..a824f2e 100644 --- a/tests/TestShared/TestMarshal.cs +++ b/tests/TestShared/TestMarshal.cs @@ -5,6 +5,16 @@ namespace TestShared; public class TestMarshal { + [CommandMethod(nameof(TestToBytes))] + public void TestToBytes() + { + var pt = new Point3d(123, 456, 789); + var bytes = StructToBytes(pt); + BytesToStruct(bytes, out Point3d pt2, out int pt2size); + Env.Printl(pt2); + Env.Printl(pt2size); + } + [CommandMethod(nameof(Test_ChangeLinePoint))] public void Test_ChangeLinePoint() { diff --git a/tests/TestShared/TestXRecord.cs b/tests/TestShared/TestXRecord.cs index 566ef9e..108251d 100644 --- a/tests/TestShared/TestXRecord.cs +++ b/tests/TestShared/TestXRecord.cs @@ -1,10 +1,9 @@ #if NewtonsoftJson + namespace Test_XRecord; -using System.Collections.Generic; using System.Diagnostics; using Newtonsoft.Json; -using IFoxCAD.Cad; using static IFoxCAD.Cad.WindowsAPI; public class TestCmd_XRecord @@ -25,7 +24,7 @@ public void TestSerializeSetXRecord() continue; TestABCList datas = new(); - for (int i = 0; i < 5; i++) + for (int i = 0; i < 1000; i++) { datas.Add(new() { @@ -39,11 +38,40 @@ public void TestSerializeSetXRecord() using (pl.ForWrite()) pl.SerializeToXRecord(datas); + + // 写入 + + // 读取 + //foreach (var nodItem in tr.NamedObjectsDict) + //{ + // var name = nodItem.Key.ToString(); + // var id = nodItem.Value; + // if (!id.IsOk()) + // continue; + + // var obj = id.GetObject(); + // if (obj is DBDictionary dic)// 条目 + // { + // nameSizes.Add(new(name, dic.Count, id)); + // } + // else if (obj is Xrecord xRec) + // { + // var buffer = xRec.Data; // 是data不是xdata + // if (buffer is not null) + // { + // var values = buffer.AsArray(); + // nameSizes.Add(new(name, values.Length, id)); + // } + // } + // else + // { + // Env.Printl("关联标注的反应器??"); + // } + //} } }); } - [CommandMethod(nameof(TestDeserializeGetXRecord))] public void TestDeserializeGetXRecord() { @@ -52,6 +80,7 @@ public void TestDeserializeGetXRecord() return; using var tr = new DBTrans(); + TestABCList? datas = null; Tools.TestTimes(1, nameof(TestDeserializeGetXRecord), () => { var pl = prs.ObjectId.GetObject(); @@ -66,33 +95,6 @@ public void TestDeserializeGetXRecord() } } -public class TestABCList : List -{ -} - -[ComVisible(true)] -[Serializable] -[DebuggerDisplay("{DebuggerDisplay,nq}")] -[DebuggerTypeProxy(typeof(TestABC))] -[StructLayout(LayoutKind.Sequential, Pack = 4)] -public class TestABC -{ - [DebuggerBrowsable(DebuggerBrowsableState.Never)] - private string DebuggerDisplay => ToString(); - - public int AAA; - public string? BBB; - public double CCCC; - public bool DDDD; - public Point3D EEEE; - - public override string ToString() - { - return JsonConvert.SerializeObject(this, XRecordHelper._sset); - } -} - - public static class XRecordHelper { internal static JsonSerializerSettings _sset = new() @@ -112,11 +114,58 @@ public static void SerializeToXRecord(this DBObject dbo, T data) if (xd == null) return; + // XRecordDataList 不能超过2G大小 + const int G2 = 2147483647; + var json = JsonConvert.SerializeObject(data, _sset); - XRecordDataList datas = new() + var buffer = Encoding.UTF8.GetBytes(json); + + if (buffer.Length < G2) + { + Set16(xd, json, buffer); + return; + } + // 大于2G + BytesTask(buffer, G2, bts => { + Set16(xd, json, bts); + }); + } + + static void BytesTask(byte[] buffer, int max, Action action) + { + int index = 0; + while (index < buffer.Length) + { + // 每次 max,然后末尾剩余就单独 + byte[] bts; + if (buffer.Length - index > max) + bts = new byte[max]; + else + bts = new byte[buffer.Length - index]; + + for (int i = 0; i < bts.Length; i++) + bts[i] = buffer[index++]; + + action.Invoke(bts); + } + } + + private static void Set16(DBDictionary xd, string json, byte[] buffer) + { + // 单条只能 16KiBit => 2048 * 16 == 32768 + const int KiBit16 = 2048 * 16; + + XRecordDataList datas = new(); + if (buffer.Length < KiBit16) + { + datas.Add(DxfCode.XTextString, json); + } + else { - { DxfCode.XTextString, json } - }; + BytesTask(buffer, KiBit16, bts => { + datas.Add(DxfCode.XTextString, Encoding.UTF8.GetString(bts)); + }); + } xd.SetXRecord(typeof(T).FullName, datas); } @@ -139,7 +188,34 @@ public static void SerializeToXRecord(this DBObject dbo, T data) var sb = new StringBuilder(); for (int i = 0; i < datas.Count; i++) sb.Append(datas[i].Value); + return JsonConvert.DeserializeObject(sb.ToString(), _sset); } } + +public class TestABCList : List +{ +} + +[ComVisible(true)] +[Serializable] +[DebuggerDisplay("{DebuggerDisplay,nq}")] +[DebuggerTypeProxy(typeof(TestABC))] +[StructLayout(LayoutKind.Sequential, Pack = 4)] +public class TestABC +{ + [DebuggerBrowsable(DebuggerBrowsableState.Never)] + private string DebuggerDisplay => ToString(); + + public int AAA; + public string? BBB; + public double CCCC; + public bool DDDD; + public Point3D EEEE; + + public override string ToString() + { + return JsonConvert.SerializeObject(this, XRecordHelper._sset); + } +} #endif \ No newline at end of file -- Gitee From 64e2eb185c18aad9b14ba037741fd1df4756cea3 Mon Sep 17 00:00:00 2001 From: liuqihong <540762622@qq.com> Date: Sun, 30 Oct 2022 21:17:01 +0800 Subject: [PATCH 598/675] =?UTF-8?q?=E8=AF=BB=E5=86=99XRecord?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- tests/TestShared/TestXRecord.cs | 30 ------------------------------ 1 file changed, 30 deletions(-) diff --git a/tests/TestShared/TestXRecord.cs b/tests/TestShared/TestXRecord.cs index 108251d..3e91957 100644 --- a/tests/TestShared/TestXRecord.cs +++ b/tests/TestShared/TestXRecord.cs @@ -38,36 +38,6 @@ public void TestSerializeSetXRecord() using (pl.ForWrite()) pl.SerializeToXRecord(datas); - - // 写入 - - // 读取 - //foreach (var nodItem in tr.NamedObjectsDict) - //{ - // var name = nodItem.Key.ToString(); - // var id = nodItem.Value; - // if (!id.IsOk()) - // continue; - - // var obj = id.GetObject(); - // if (obj is DBDictionary dic)// 条目 - // { - // nameSizes.Add(new(name, dic.Count, id)); - // } - // else if (obj is Xrecord xRec) - // { - // var buffer = xRec.Data; // 是data不是xdata - // if (buffer is not null) - // { - // var values = buffer.AsArray(); - // nameSizes.Add(new(name, values.Length, id)); - // } - // } - // else - // { - // Env.Printl("关联标注的反应器??"); - // } - //} } }); } -- Gitee From a817e53d23889d14824cbfe918bc198fda8f2d23 Mon Sep 17 00:00:00 2001 From: liuqihong <540762622@qq.com> Date: Mon, 31 Oct 2022 13:50:33 +0800 Subject: [PATCH 599/675] =?UTF-8?q?data=E5=92=8Cxdata=E6=B5=8B=E8=AF=95?= =?UTF-8?q?=E4=BF=AE=E6=94=B9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../ExtensionMethod/DBDictionaryEx.cs | 4 +- src/IFoxCAD.Cad.Shared/ResultData/LispList.cs | 3 +- .../ResultData/XRecordDataList.cs | 3 +- .../ResultData/XdataList.cs | 2 +- tests/TestConsole/Program.cs | 3 +- tests/TestShared/TestXRecord.cs | 161 ++++++++++++++---- 6 files changed, 136 insertions(+), 40 deletions(-) diff --git a/src/IFoxCAD.Cad.Shared/ExtensionMethod/DBDictionaryEx.cs b/src/IFoxCAD.Cad.Shared/ExtensionMethod/DBDictionaryEx.cs index 3375e55..e099a93 100644 --- a/src/IFoxCAD.Cad.Shared/ExtensionMethod/DBDictionaryEx.cs +++ b/src/IFoxCAD.Cad.Shared/ExtensionMethod/DBDictionaryEx.cs @@ -86,7 +86,9 @@ public static void SetAt(this DBDictionary dict, string key, T newValue, Tran /// 键值 public static void SetXRecord(this DBDictionary dict, string key, XRecordDataList rb) { - using var newValue = new Xrecord { Data = rb }; + // DxfCode.300 字符串可以写 Data + // DxfCode.1004 内存流不给写 Data,只能去写 XData + using Xrecord newValue = new() { Data = rb }; dict.SetAt(key, newValue); } #endregion diff --git a/src/IFoxCAD.Cad.Shared/ResultData/LispList.cs b/src/IFoxCAD.Cad.Shared/ResultData/LispList.cs index 2488541..c186e43 100644 --- a/src/IFoxCAD.Cad.Shared/ResultData/LispList.cs +++ b/src/IFoxCAD.Cad.Shared/ResultData/LispList.cs @@ -46,9 +46,8 @@ public virtual List Value public override void Add(int code, object? obj) { if (code < 5000) - { throw new System.Exception("传入的组码值不是 lisp数据 有效范围!"); - } + Add(new TypedValue(code, obj)); } diff --git a/src/IFoxCAD.Cad.Shared/ResultData/XRecordDataList.cs b/src/IFoxCAD.Cad.Shared/ResultData/XRecordDataList.cs index 224064f..708e155 100644 --- a/src/IFoxCAD.Cad.Shared/ResultData/XRecordDataList.cs +++ b/src/IFoxCAD.Cad.Shared/ResultData/XRecordDataList.cs @@ -27,6 +27,7 @@ public override void Add(int code, object obj) { if (code >= 1000) throw new System.Exception("传入的组码值不是 XRecordData 有效范围!"); + Add(new TypedValue(code, obj)); } @@ -63,4 +64,4 @@ public void Add(DxfCode code, object obj) /// TypedValue 数组 public static implicit operator XRecordDataList(TypedValue[] values) => new(values); #endregion -} +} \ No newline at end of file diff --git a/src/IFoxCAD.Cad.Shared/ResultData/XdataList.cs b/src/IFoxCAD.Cad.Shared/ResultData/XdataList.cs index 7cf088c..94f04d7 100644 --- a/src/IFoxCAD.Cad.Shared/ResultData/XdataList.cs +++ b/src/IFoxCAD.Cad.Shared/ResultData/XdataList.cs @@ -26,7 +26,7 @@ public XDataList(IEnumerable values) : base(values) { } public override void Add(int code, object obj) { if (code < 1000 || code > 1071) - throw new System.Exception("传入的组码值不是XData有效范围!"); + throw new System.Exception("传入的组码值不是 XData 有效范围!"); Add(new TypedValue(code, obj)); } diff --git a/tests/TestConsole/Program.cs b/tests/TestConsole/Program.cs index 10e0f5d..b596a5e 100644 --- a/tests/TestConsole/Program.cs +++ b/tests/TestConsole/Program.cs @@ -9,7 +9,7 @@ using System.Text; using System.Diagnostics; - +#if true namespace CalculatorDemo { class Program @@ -64,6 +64,7 @@ public static void ForEach(this IEnumerable source, Action action) } } } +#endif #if true2 diff --git a/tests/TestShared/TestXRecord.cs b/tests/TestShared/TestXRecord.cs index 3e91957..c37ae37 100644 --- a/tests/TestShared/TestXRecord.cs +++ b/tests/TestShared/TestXRecord.cs @@ -1,11 +1,13 @@ -#if NewtonsoftJson - -namespace Test_XRecord; +//#define ExtendedDataBinaryChunk +#define XTextString +#if NewtonsoftJson using System.Diagnostics; using Newtonsoft.Json; using static IFoxCAD.Cad.WindowsAPI; +namespace Test_XRecord; + public class TestCmd_XRecord { [CommandMethod(nameof(TestSerializeSetXRecord))] @@ -37,7 +39,10 @@ public void TestSerializeSetXRecord() } using (pl.ForWrite()) + { pl.SerializeToXRecord(datas); + //pl.SerializeToXRecord(datas); + } } }); } @@ -56,17 +61,44 @@ public void TestDeserializeGetXRecord() var pl = prs.ObjectId.GetObject(); if (pl == null) return; +#if XTextString datas = pl.DeserializeToXRecord(); +#endif + +#if ExtendedDataBinaryChunk + // 这里有数据容量限制,而且很小 + var xd = pl.GetXDictionary(); + if (xd == null) + return; + if (xd.XData == null) + return; + + XDataList data = xd.XData; + var sb = new StringBuilder(); + data.ForEach(a => { + if (a.TypeCode == (short)DxfCode.ExtendedDataBinaryChunk) + if (a.Value is byte[] bytes) + sb.Append(Encoding.UTF8.GetString(bytes)); + }); + datas = JsonConvert.DeserializeObject(sb.ToString(), XRecordHelper._sset); +#endif }); if (datas == null) + { + Env.Printl("没有反序列的东西"); return; + } + + var sb = new StringBuilder(); for (int i = 0; i < datas.Count; i++) - Env.Printl(datas[i]); + sb.Append(datas[i]); + Env.Printl(sb); } } public static class XRecordHelper { + #region 序列化方式 internal static JsonSerializerSettings _sset = new() { Formatting = Formatting.Indented, @@ -85,22 +117,41 @@ public static void SerializeToXRecord(this DBObject dbo, T data) return; // XRecordDataList 不能超过2G大小 - const int G2 = 2147483647; + const int GigaByte2 = 2147483647; + // 单条只能 16KiBit => 2048 * 16 == 32768 + const int KiBit16 = (2048 * 16) - 1; - var json = JsonConvert.SerializeObject(data, _sset); - var buffer = Encoding.UTF8.GetBytes(json); + // 就算这个写法支持,计算机也不一定有那么多内存,所以遇到此情况最好换成内存拷贝 + var json = JsonConvert.SerializeObject(data, _sset);// 此时内存占用了2G + var buffer = Encoding.UTF8.GetBytes(json); // 此时内存又占用了2G.. - if (buffer.Length < G2) - { - Set16(xd, json, buffer); - return; - } - // 大于2G - BytesTask(buffer, G2, bts => { - Set16(xd, json, bts); + BytesTask(buffer, GigaByte2, bts => { +#if XTextString + XRecordDataList datas = new(); + BytesTask(buffer, KiBit16, bts => { + datas.Add(DxfCode.XTextString, Encoding.UTF8.GetString(bts)); // 这对的 + // datas.Add(DxfCode.XTextString, bts);//这样bte变成 "System.Byte[]" + }); + xd.SetXRecord(typeof(T).FullName, datas); +#endif + +#if ExtendedDataBinaryChunk + // 这里有数据容量限制,而且很小 + var appname = typeof(T).FullName; + DBTrans.Top.RegAppTable.Add(appname); + + XDataList datas = new(); + datas.Add(DxfCode.ExtendedDataRegAppName, appname); + BytesTask(buffer, KiBit16, bts => { + datas.Add(DxfCode.ExtendedDataBinaryChunk, bts); + }); + using (xd.ForWrite()) + xd.XData = datas; // Autodesk.AutoCAD.Runtime.Exception:“eXdataSizeExceeded” +#endif }); } + [DebuggerHidden] static void BytesTask(byte[] buffer, int max, Action action) { int index = 0; @@ -120,25 +171,6 @@ static void BytesTask(byte[] buffer, int max, Action action) } } - private static void Set16(DBDictionary xd, string json, byte[] buffer) - { - // 单条只能 16KiBit => 2048 * 16 == 32768 - const int KiBit16 = 2048 * 16; - - XRecordDataList datas = new(); - if (buffer.Length < KiBit16) - { - datas.Add(DxfCode.XTextString, json); - } - else - { - BytesTask(buffer, KiBit16, bts => { - datas.Add(DxfCode.XTextString, Encoding.UTF8.GetString(bts)); - }); - } - xd.SetXRecord(typeof(T).FullName, datas); - } - /// /// 提取信息 /// @@ -161,6 +193,67 @@ private static void Set16(DBDictionary xd, string json, byte[] buffer) return JsonConvert.DeserializeObject(sb.ToString(), _sset); } + #endregion + +#if NET35 + /// + /// 设置描述(容量无限) + /// + /// + /// + /// + public static void SetSummaryInfoAtt(this Database db, string key, string value) + { + var info = new DatabaseSummaryInfoBuilder(db.SummaryInfo); + if (!info.CustomProperties.ContainsKey(key)) + info.CustomProperties.Add(key, value); + else + info.CustomProperties[key] = value; + db.SummaryInfo = info.ToDatabaseSummaryInfo(); + } + /// + /// 获取描述 + /// + /// + /// + /// + public static object? GetSummaryInfoAtt(this Database db, string key) + { + var info = new DatabaseSummaryInfoBuilder(db.SummaryInfo); + if (info.CustomProperties.ContainsKey(key)) + return info.CustomProperties[key]; + return null; + } +#else + /// + /// 设置描述(容量无限) + /// + /// + /// + /// + public static void SetSummaryInfoAtt(this Database db, string key, object value) + { + var info = new DatabaseSummaryInfoBuilder(db.SummaryInfo); + if (!info.CustomPropertyTable.Contains(key)) + info.CustomPropertyTable.Add(key, value); + else + info.CustomPropertyTable[key] = value; + db.SummaryInfo = info.ToDatabaseSummaryInfo(); + } + /// + /// 获取描述 + /// + /// + /// + /// + public static object? GetSummaryInfoAtt(this Database db, string key) + { + var info = new DatabaseSummaryInfoBuilder(db.SummaryInfo); + if (info.CustomPropertyTable.Contains(key)) + return info.CustomPropertyTable[key]; + return null; + } +#endif } public class TestABCList : List -- Gitee From 32fa61f4b71d2178fded5672acf1d557a72d9ff7 Mon Sep 17 00:00:00 2001 From: liuqihong <540762622@qq.com> Date: Mon, 31 Oct 2022 21:41:21 +0800 Subject: [PATCH 600/675] =?UTF-8?q?=E5=88=A9=E7=94=A8=E4=B8=8D=E5=AE=89?= =?UTF-8?q?=E5=85=A8=E6=93=8D=E4=BD=9C=E4=BC=98=E5=8C=96=E6=95=88=E7=8E=87?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/IFoxCAD.Cad.Shared/Copyclip/EmfTool.cs | 18 ++++++--- src/IFoxCAD.Cad.Shared/Copyclip/WindowsAPI.cs | 39 ++++++++++++++----- tests/TestShared/TestMarshal.cs | 9 ++--- tests/TestShared/TestXRecord.cs | 5 +-- 4 files changed, 47 insertions(+), 24 deletions(-) diff --git a/src/IFoxCAD.Cad.Shared/Copyclip/EmfTool.cs b/src/IFoxCAD.Cad.Shared/Copyclip/EmfTool.cs index 4a5d82c..308d891 100644 --- a/src/IFoxCAD.Cad.Shared/Copyclip/EmfTool.cs +++ b/src/IFoxCAD.Cad.Shared/Copyclip/EmfTool.cs @@ -105,10 +105,12 @@ public static IntPtr Wmf2Emf(string wmfFile) file.Read(fileByte, 0, fileByte.Length); file.Close(); - bool bts = BytesToStruct(fileByte, out PlaceableMetaHeader sWMF, out int sWMFsize); - if (!bts) + var ss = BytesToStruct(fileByte); + if (ss == null) throw new IOException("失败:类型转换,路径:" + file); + var sWMF = ss.Value; + // 转为emf的地址 IntPtr hEMF = IntPtr.Zero; @@ -124,12 +126,16 @@ public static IntPtr Wmf2Emf(string wmfFile) // byte[] 指针偏移 int iOffset = 0; if (sWMF.IsActivity) - iOffset = sWMFsize; - IntPtr fileIntPtr = Marshal.UnsafeAddrOfPinnedArrayElement(fileByte, iOffset); + iOffset = Marshal.SizeOf(typeof(PlaceableMetaHeader)); + unsafe { - hEMF = EmfTool.SetWinMetaFileBits( - (uint)fileByte.Length, fileIntPtr, IntPtr.Zero, new IntPtr(&mpType)); + // 安全指针方法 + //IntPtr fileIntPtr = Marshal.UnsafeAddrOfPinnedArrayElement(fileByte, iOffset); + // 不安全指针方法 + fixed (byte* fileIntPtr = &fileByte[iOffset]) + hEMF = EmfTool.SetWinMetaFileBits( + (uint)fileByte.Length, new IntPtr(fileIntPtr), IntPtr.Zero, new IntPtr(&mpType)); } return hEMF; } diff --git a/src/IFoxCAD.Cad.Shared/Copyclip/WindowsAPI.cs b/src/IFoxCAD.Cad.Shared/Copyclip/WindowsAPI.cs index 6d8580e..965ec26 100644 --- a/src/IFoxCAD.Cad.Shared/Copyclip/WindowsAPI.cs +++ b/src/IFoxCAD.Cad.Shared/Copyclip/WindowsAPI.cs @@ -108,27 +108,48 @@ public static bool GlobalLockTask(IntPtr data, Action task) /// byte数组转结构体 /// /// byte数组 - /// 返回的结构体 /// 返回的结构大小 - /// 转换后的结构体 - public static bool BytesToStruct(byte[] bytes, out T? result, out int typeSize) + /// 返回的结构体 + [Obsolete("效率太低", true)] + public static T? BytesToStruct(byte[] bytes, out int typeSize) { - result = default; var structType = typeof(T); - // 得到结构体的大小 typeSize = Marshal.SizeOf(structType); if (typeSize > bytes.Length) - return false; + return default; + // 安全写法效率太低了 // 分配结构体大小的内存空间 IntPtr structPtr = Marshal.AllocHGlobal(typeSize); + // 将byte数组拷到分配好的内存空间 Marshal.Copy(bytes, 0, structPtr, typeSize); - // 将内存空间转换为目标结构体,转类型的时候会拷贝一次 - result = (T)Marshal.PtrToStructure(structPtr, structType); + // 将内存空间转换为目标结构体; + // 转类型的时候会拷贝一次,看它们地址验证 &result != &structPtr + var result = (T)Marshal.PtrToStructure(structPtr, structType); + // 释放内存空间 Marshal.FreeHGlobal(structPtr); - return true; + return result; + } + + /// + /// byte数组转结构体 + /// + /// byte数组 + /// 返回的结构体 + public static T? BytesToStruct(byte[] bytes) where T : unmanaged + { + T? result = null; + unsafe + { + // 安全指针方法 + // var pB = Marshal.UnsafeAddrOfPinnedArrayElement(bytes, 0); + // 不安全指针方法 + fixed (byte* pB = &bytes[0]) + result = (T)Marshal.PtrToStructure(new IntPtr(pB), typeof(T)); + } + return result; } /// diff --git a/tests/TestShared/TestMarshal.cs b/tests/TestShared/TestMarshal.cs index a824f2e..3cdc0d9 100644 --- a/tests/TestShared/TestMarshal.cs +++ b/tests/TestShared/TestMarshal.cs @@ -8,11 +8,10 @@ public class TestMarshal [CommandMethod(nameof(TestToBytes))] public void TestToBytes() { - var pt = new Point3d(123, 456, 789); - var bytes = StructToBytes(pt); - BytesToStruct(bytes, out Point3d pt2, out int pt2size); - Env.Printl(pt2); - Env.Printl(pt2size); + var ptA = new Point3d(123, 456, 789); + var bytes = StructToBytes(ptA); + var ptB = BytesToStruct(bytes); + Env.Printl(ptB!.Value); } [CommandMethod(nameof(Test_ChangeLinePoint))] diff --git a/tests/TestShared/TestXRecord.cs b/tests/TestShared/TestXRecord.cs index c37ae37..313bf8c 100644 --- a/tests/TestShared/TestXRecord.cs +++ b/tests/TestShared/TestXRecord.cs @@ -39,10 +39,7 @@ public void TestSerializeSetXRecord() } using (pl.ForWrite()) - { pl.SerializeToXRecord(datas); - //pl.SerializeToXRecord(datas); - } } }); } @@ -130,7 +127,7 @@ public static void SerializeToXRecord(this DBObject dbo, T data) XRecordDataList datas = new(); BytesTask(buffer, KiBit16, bts => { datas.Add(DxfCode.XTextString, Encoding.UTF8.GetString(bts)); // 这对的 - // datas.Add(DxfCode.XTextString, bts);//这样bte变成 "System.Byte[]" + // datas.Add(DxfCode.XTextString, bts);//这样 bts 变成 "System.Byte[]" }); xd.SetXRecord(typeof(T).FullName, datas); #endif -- Gitee From fb69fc778dd053b1d424e8bc106d22fa4097be85 Mon Sep 17 00:00:00 2001 From: liuqihong <540762622@qq.com> Date: Tue, 1 Nov 2022 14:36:15 +0800 Subject: [PATCH 601/675] =?UTF-8?q?=E8=BE=93=E5=85=A5=E6=B3=95bug:=20?= =?UTF-8?q?=E5=BD=93=E5=89=8D=E7=84=A6=E7=82=B9=E5=92=8C=E5=BD=93=E5=89=8D?= =?UTF-8?q?=E5=B7=A5=E4=BD=9C=E7=AA=97=E5=8F=A3=E4=B8=8D=E6=98=AF=E5=90=8C?= =?UTF-8?q?=E4=B8=80=E4=B8=AA=20=E4=BC=98=E5=8C=96=E5=86=85=E5=AD=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/IFoxCAD.Cad.Shared/Copyclip/EmfTool.cs | 28 ++-- .../Copyclip/TagClipboardInfo.cs | 10 +- src/IFoxCAD.Cad.Shared/Copyclip/WindowsAPI.cs | 22 ++-- .../IMEControl.cs" | 121 ++++++++++++------ .../Window/SettingsWindow.xaml.cs" | 2 +- tests/TestShared/Copyclip.cs | 2 +- tests/TestShared/TestMarshal.cs | 10 +- 7 files changed, 109 insertions(+), 86 deletions(-) diff --git a/src/IFoxCAD.Cad.Shared/Copyclip/EmfTool.cs b/src/IFoxCAD.Cad.Shared/Copyclip/EmfTool.cs index 308d891..2b93449 100644 --- a/src/IFoxCAD.Cad.Shared/Copyclip/EmfTool.cs +++ b/src/IFoxCAD.Cad.Shared/Copyclip/EmfTool.cs @@ -1,20 +1,13 @@ namespace IFoxCAD.Cad; using System; -using System.Drawing.Drawing2D; +using System.Diagnostics; using System.Drawing; using System.Drawing.Imaging; -using System.Diagnostics; -using System.Windows; -using System.Security.Policy; -using System.Security.Cryptography; -using System.Windows.Interop; using System.Text; -using static System.Windows.Forms.VisualStyles.VisualStyleElement.TextBox; -using System.Xml; +using static IFoxCAD.Cad.WindowsAPI; using Point = System.Drawing.Point; using Size = System.Drawing.Size; -using static IFoxCAD.Cad.WindowsAPI; // DWORD == uint // WORD == ushort @@ -158,10 +151,10 @@ public void Wmf2Emf(string wmfFile) /// 获取emf结构 /// /// - public EnhMetaHeader? CreateEnhMetaHeader() + public EnhMetaHeader CreateEnhMetaHeader() { if (EmfHandle == IntPtr.Zero) - return null; + throw new ArgumentException(nameof(EmfHandle) + "== IntPtr.Zero"); return EnhMetaHeader.Create(EmfHandle); } } @@ -262,22 +255,23 @@ public static EnhMetaHeader Create(string wmf) } /// - /// 创建 + /// 通过emf指针创建 /// - /// - /// 参数1的结构体首地址
    + /// 参数1的结构体首地址
    /// 也就是的返回值 /// /// - public static EnhMetaHeader Create(IntPtr clipTypeData) + public static EnhMetaHeader Create(IntPtr emf) { - var len = EmfTool.GetEnhMetaFileHeader(clipTypeData, 0, IntPtr.Zero); + var len = EmfTool.GetEnhMetaFileHeader(emf, 0, IntPtr.Zero); if (len == 0) throw new ArgumentException(nameof(len)); IntPtr header = Marshal.AllocHGlobal((int)len); - EmfTool.GetEnhMetaFileHeader(clipTypeData, len, header); + EmfTool.GetEnhMetaFileHeader(emf, len, header);//这里是切割获取内部的bytes,存放在header + var result = (EnhMetaHeader)Marshal.PtrToStructure(header, typeof(EnhMetaHeader)); + Marshal.FreeHGlobal(header); return result; } diff --git a/src/IFoxCAD.Cad.Shared/Copyclip/TagClipboardInfo.cs b/src/IFoxCAD.Cad.Shared/Copyclip/TagClipboardInfo.cs index 1e3327e..4d050b4 100644 --- a/src/IFoxCAD.Cad.Shared/Copyclip/TagClipboardInfo.cs +++ b/src/IFoxCAD.Cad.Shared/Copyclip/TagClipboardInfo.cs @@ -190,12 +190,12 @@ public override int GetHashCode() chData.GetHashCode(); } - public IntPtr CloneToIntPtr() + public IntPtr CloneToPtr() { - var newPtr = Marshal.AllocHGlobal(Marshal.SizeOf(this)); - if (newPtr != IntPtr.Zero) - Marshal.StructureToPtr(this, newPtr, true); - return newPtr; + var lParam = Marshal.AllocHGlobal(Marshal.SizeOf(this)); + if (lParam != IntPtr.Zero) + Marshal.StructureToPtr(this, lParam, true); + return lParam; } #endregion } diff --git a/src/IFoxCAD.Cad.Shared/Copyclip/WindowsAPI.cs b/src/IFoxCAD.Cad.Shared/Copyclip/WindowsAPI.cs index 965ec26..ad42451 100644 --- a/src/IFoxCAD.Cad.Shared/Copyclip/WindowsAPI.cs +++ b/src/IFoxCAD.Cad.Shared/Copyclip/WindowsAPI.cs @@ -449,26 +449,20 @@ public static GuiThreadInfo Create(uint windowThreadProcessId) #region 钩子 [DllImport("user32.dll")] public static extern IntPtr SetWindowsHookEx(HookType idHook, CallBackX86 lpfn, long hmod, int dwThreadId); - [DllImport("user32.dll")] public static extern IntPtr SetWindowsHookEx(HookType idHook, CallBack lpfn, int hmod, int dwThreadId); - [DllImport("user32.dll")] public static extern IntPtr SetWindowsHookEx(HookType idHook, CallBackX64 lpfn, long hmod, int dwThreadId); - [DllImport("user32.dll")] - public static extern int UnhookWindowsHookEx(IntPtr hHook); - + public static extern IntPtr UnhookWindowsHookEx(IntPtr hHook); [DllImport("user32.dll")] - public static extern int CallNextHookEx(IntPtr hHook, int ncode, int wParam, long lParam); - + public static extern IntPtr CallNextHookEx(IntPtr hHook, int ncode, int wParam, long lParam); [DllImport("user32.dll")] - public static extern int CallNextHookEx(int hHook, int ncode, int wParam, int lParam); - + public static extern IntPtr CallNextHookEx(int hHook, int ncode, int wParam, int lParam); public delegate IntPtr CallBackX86(int nCode, int wParam, int lParam); public delegate IntPtr CallBackX64(int nCode, int wParam, long lParam); public delegate IntPtr CallBack(int nCode, int wParam, int lParam); - public enum HookType + public enum HookType : int { WH_KEYBOARD = 2, WH_KEYBOARD_LL = 13, @@ -601,14 +595,14 @@ public static implicit operator Point3d(Point3D pt) } public override string ToString() => $"({X},{Y},{Z})"; - public static Point3D Create(IntPtr intPtr) + public static Point3D Create(IntPtr lParam) { - return (Point3D)Marshal.PtrToStructure(intPtr, typeof(Point3D)); + return (Point3D)Marshal.PtrToStructure(lParam, typeof(Point3D)); } - public void PutCreate(IntPtr intPtr) + public void ToPtr(IntPtr lParam) { - Marshal.StructureToPtr(this, intPtr, true); + Marshal.StructureToPtr(this, lParam, true); } diff --git "a/tests/TestAcad09plus/\350\276\223\345\205\245\346\263\225\350\207\252\345\212\250\345\210\207\346\215\242/\350\265\253\346\200\235\350\276\223\345\205\245\346\263\225/IMEControl.cs" "b/tests/TestAcad09plus/\350\276\223\345\205\245\346\263\225\350\207\252\345\212\250\345\210\207\346\215\242/\350\265\253\346\200\235\350\276\223\345\205\245\346\263\225/IMEControl.cs" index e225458..6995567 100644 --- "a/tests/TestAcad09plus/\350\276\223\345\205\245\346\263\225\350\207\252\345\212\250\345\210\207\346\215\242/\350\265\253\346\200\235\350\276\223\345\205\245\346\263\225/IMEControl.cs" +++ "b/tests/TestAcad09plus/\350\276\223\345\205\245\346\263\225\350\207\252\345\212\250\345\210\207\346\215\242/\350\265\253\346\200\235\350\276\223\345\205\245\346\263\225/IMEControl.cs" @@ -5,6 +5,8 @@ public class IMEControl { + internal static HashSet DefaultCMDs; + internal static HashSet ExceptCMDs; static readonly Regex CMDReg = new("\\(C:.*\\)"); /*某些窗口没有 WM_KEYDOWN 消息,就只有 WM_KEYUP 消息*/ const int WM_KEYDOWN = 256; @@ -12,38 +14,46 @@ public class IMEControl //const int WM_CHAR = 258; //const int WM_KILLFOCUS = 8; - internal static string[] DefaultCMDs; - internal static List ExceptCMDs; - internal static IntPtr hnexthookproc; internal static WindowsAPI.CallBackX86? HookProcX86; internal static WindowsAPI.CallBackX64? HookProcX64; internal static WindowsAPI.CallBack? HookProc; - + internal static IntPtr _NextHookProc;//挂载成功的标记 internal static Process _Process; internal static int AcadPID => _Process.Id; static IMEControl() { - DefaultCMDs = new string[] { "MTEXT", "DDEDIT", "MTEDIT", "TABLEDIT", "MLEADER", "QLEADER", "MLEADERCONTENTEDIT", "MLEADEREDIT", "TEXTEDIT", "TEXT", "QLEADER" }; - ExceptCMDs = new(); + _NextHookProc = IntPtr.Zero; _Process = Process.GetCurrentProcess(); - hnexthookproc = IntPtr.Zero; CheckLowLevelHooksTimeout(); + ExceptCMDs = new(); + DefaultCMDs = new() { "MTEXT", "DDEDIT", "MTEDIT", "TABLEDIT", "MLEADER", + "QLEADER", "MLEADERCONTENTEDIT", "MLEADEREDIT", "TEXTEDIT", "TEXT", "QLEADER" }; string? lines = null; var ftFile = Path.Combine(Settings.MyDir, "ExceptCMDs.ft"); if (File.Exists(ftFile)) lines = File.ReadAllText(ftFile, Encoding.UTF8); else Debug.WriteLine("配置文件丢失: " + ftFile); - if (lines != null) - DefaultCMDs = lines.Split(new string[] { "," }, StringSplitOptions.RemoveEmptyEntries); + { + var ls = lines.Split(new string[] { "," }, StringSplitOptions.RemoveEmptyEntries); + for (int i = 0; i < ls.Length; i++) + DefaultCMDs.Add(ls[i]); + } } // 优化内存,减少消息循环时候,频繁创建此类 static StringBuilder _lpClassName = new(byte.MaxValue); + /// + /// 钩子的消息处理 + /// + /// + /// + /// + /// false不终止回调,true终止回调 public static bool IMEHook(int nCode, int wParam, int lParam) { var dm = Acap.DocumentManager; @@ -88,11 +98,23 @@ public static bool IMEHook(int nCode, int wParam, int lParam) var focus = WindowsAPI.GetFocus(); WindowsAPI.GetClassName(focus, _lpClassName, checked(_lpClassName.Capacity + 1)); - string left = _lpClassName.ToString().ToLower(); if (left.StartsWith("afx"))// 在08输入的都从这里进入 { - //Debug.WriteLine("afx"); + Debug.WriteLine($"afx::{DateTime.Now}"); + + { + // cad启动时候会滚动某些信息,此时立马鼠标点入到vs中,此时vs无法输入, + // 被拦截到了"afx"处理,然后所有的输入都跑cad了,需要加入如下代码 + var focusW = WindowsAPI.GetForegroundWindow(); + WindowsAPI.GetClassName(focusW, _lpClassName, checked(_lpClassName.Capacity + 1)); + left = _lpClassName.ToString().ToLower(); + // 点入vs是 hwndwrapper + // 点入qq是 txguifoundation + Debug.WriteLine($"{left}.....{DateTime.Now}"); + if (!left.StartsWith("afx")) + return false; + } WindowsAPI.PostMessage(focus, WM_KEYDOWN, new IntPtr(wParam), new IntPtr(0x10001)); return true; @@ -100,9 +122,11 @@ public static bool IMEHook(int nCode, int wParam, int lParam) if (left.StartsWith("hwndwrapper"))//cad21会进入,高版本的命令提示器? { - //Debug.WriteLine("hwndwrapper"); + Debug.WriteLine($"hwndwrapper::{DateTime.Now}"); var parent = WindowsAPI.GetParent(focus); + if (parent == IntPtr.Zero) + return false; StringBuilder lpString = new(byte.MaxValue); WindowsAPI.GetWindowText(parent, lpString, checked(lpString.Capacity + 1)); if (lpString.ToString().ToLower() != "cli palette")//"CLI Palette".ToLower() @@ -117,7 +141,7 @@ public static bool IMEHook(int nCode, int wParam, int lParam) if (left.StartsWith("edit")) { - //Debug.WriteLine("edit"); + Debug.WriteLine($"edit::{DateTime.Now}"); var parent = WindowsAPI.GetParent(focus); WindowsAPI.GetClassName(parent, _lpClassName, checked(_lpClassName.Capacity + 1)); @@ -131,6 +155,7 @@ public static bool IMEHook(int nCode, int wParam, int lParam) if (left == "cicerouiwndframe") { + Debug.WriteLine($"cicerouiwndframe::{DateTime.Now}"); //Debug.WriteLine("cicerouiwndframe"); return true; } @@ -140,37 +165,40 @@ public static bool IMEHook(int nCode, int wParam, int lParam) internal static void UnIMEHook() { - if (hnexthookproc != IntPtr.Zero) + if (_NextHookProc != IntPtr.Zero) { - WindowsAPI.UnhookWindowsHookEx(hnexthookproc); - hnexthookproc = IntPtr.Zero; + WindowsAPI.UnhookWindowsHookEx(_NextHookProc); + _NextHookProc = IntPtr.Zero; } } internal static void SetIMEHook() { UnIMEHook(); - ExceptCMDs.Clear(); - if (hnexthookproc != IntPtr.Zero || !Settings.Use) + if (_NextHookProc != IntPtr.Zero || !Settings.Use) return; - ExceptCMDs.AddRange(DefaultCMDs); - ExceptCMDs.AddRange(Settings.UserFilter.Split(new string[] { "," }, StringSplitOptions.RemoveEmptyEntries)); + ExceptCMDs.Clear(); + foreach (var item in DefaultCMDs) + ExceptCMDs.Add(item); + var ss = Settings.UserFilter.Split(new string[] { "," }, StringSplitOptions.RemoveEmptyEntries); + foreach (var item in ss) + ExceptCMDs.Add(item); if (Settings.IMEStyle != Settings.IMEStyleS.Global) { - HookProc = (int nCode, int wParam, int lParam) => { + HookProc = (nCode, wParam, lParam) => { if (nCode >= 0) { - bool flag = !(lParam > 0 & (lParam & -1073741823) == 1) || + bool flag = !((lParam > 0) & ((lParam & -1073741823) == 1)) || (Control.ModifierKeys != Keys.None && Control.ModifierKeys != Keys.Shift) || !IMEHook(nCode, wParam, lParam); if (!flag) return (IntPtr)1; } - return (IntPtr)WindowsAPI.CallNextHookEx(hnexthookproc, nCode, wParam, lParam); + return WindowsAPI.CallNextHookEx(_NextHookProc, nCode, wParam, lParam); }; - hnexthookproc = WindowsAPI.SetWindowsHookEx(WindowsAPI.HookType.WH_KEYBOARD, HookProc, 0, WindowsAPI.GetCurrentThreadId()); + _NextHookProc = WindowsAPI.SetWindowsHookEx(WindowsAPI.HookType.WH_KEYBOARD, HookProc, 0, WindowsAPI.GetCurrentThreadId()); return; } @@ -180,18 +208,18 @@ internal static void SetIMEHook() HookProcX86 = (nCode, wParam, lParam) => { if (!MK1(nCode, wParam) && Mk2(nCode, wParam, new IntPtr(lParam))) return (IntPtr)1; - return (IntPtr)WindowsAPI.CallNextHookEx(hnexthookproc, nCode, wParam, lParam); + return WindowsAPI.CallNextHookEx(_NextHookProc, nCode, wParam, lParam); }; - hnexthookproc = WindowsAPI.SetWindowsHookEx(WindowsAPI.HookType.WH_KEYBOARD_LL, HookProcX86, moduleHandle, 0); + _NextHookProc = WindowsAPI.SetWindowsHookEx(WindowsAPI.HookType.WH_KEYBOARD_LL, HookProcX86, moduleHandle, 0); } else { HookProcX64 = (nCode, wParam, lParam) => { if (!MK1(nCode, wParam) && Mk2(nCode, wParam, new IntPtr(lParam))) return (IntPtr)1; - return (IntPtr)WindowsAPI.CallNextHookEx(hnexthookproc, nCode, wParam, lParam); + return WindowsAPI.CallNextHookEx(_NextHookProc, nCode, wParam, lParam); }; - hnexthookproc = WindowsAPI.SetWindowsHookEx(WindowsAPI.HookType.WH_KEYBOARD_LL, HookProcX64, moduleHandle, 0); + _NextHookProc = WindowsAPI.SetWindowsHookEx(WindowsAPI.HookType.WH_KEYBOARD_LL, HookProcX64, moduleHandle, 0); } } @@ -217,21 +245,23 @@ static bool Mk2(int nCode, int wParam, IntPtr lParam) if (lpdwProcessId != AcadPID) return false; + KeyboardHookStruct? key = null; if (Control.ModifierKeys == Keys.None) { - var hook = KeyboardHookStruct.Create(lParam); + key = KeyboardHookStruct.Create(lParam); if (WindowsAPI.GetKeyState(162) < 0 || WindowsAPI.GetKeyState(163) < 0 || WindowsAPI.GetKeyState(17) < 0 || - WindowsAPI.GetKeyState(262144) < 0) + WindowsAPI.GetKeyState(262144/*alt键*/) < 0) return false; - if (IMEHook(nCode, hook.vkCode, 0)) + + if (IMEHook(nCode, key.Value.VkCode, 0)) return true; } - if (Control.ModifierKeys == Keys.Shift) + else if (Control.ModifierKeys == Keys.Shift) { - var hook = KeyboardHookStruct.Create(lParam); - if (IMEHook(nCode, hook.vkCode, 0)) + key ??= KeyboardHookStruct.Create(lParam); + if (IMEHook(nCode, key.Value.VkCode, 0)) return true; } return false; @@ -250,7 +280,9 @@ static void CheckLowLevelHooksTimeout() registryKey.SetValue(llh, 25000, RegistryValueKind.DWord); } - + /// + /// Hook键盘数据结构 + /// [ComVisible(true)] [Serializable] //[DebuggerDisplay("{DebuggerDisplay,nq}")] @@ -258,15 +290,20 @@ static void CheckLowLevelHooksTimeout() [StructLayout(LayoutKind.Sequential)] public struct KeyboardHookStruct { - public int vkCode; - public int ScanCode; - public int Flags; - public int Time; - public int DwExtraInfo; + public int VkCode; // 键码,该代码必须有一个价值的范围1至254 + public int ScanCode; // 指定的硬件扫描码的关键 + public int Flags; // 键标志 + public int Time; // 指定的时间戳记的这个讯息 + public int DwExtraInfo; // 指定额外信息相关的信息 + + public static KeyboardHookStruct Create(IntPtr lParam) + { + return (KeyboardHookStruct)Marshal.PtrToStructure(lParam, typeof(KeyboardHookStruct)); + } - public static KeyboardHookStruct Create(IntPtr intPtr) + public void ToPtr(IntPtr lParam) { - return (KeyboardHookStruct)Marshal.PtrToStructure(intPtr, typeof(KeyboardHookStruct)); + Marshal.StructureToPtr(this, lParam, true); } } } \ No newline at end of file diff --git "a/tests/TestAcad09plus/\350\276\223\345\205\245\346\263\225\350\207\252\345\212\250\345\210\207\346\215\242/\350\265\253\346\200\235\350\276\223\345\205\245\346\263\225/Window/SettingsWindow.xaml.cs" "b/tests/TestAcad09plus/\350\276\223\345\205\245\346\263\225\350\207\252\345\212\250\345\210\207\346\215\242/\350\265\253\346\200\235\350\276\223\345\205\245\346\263\225/Window/SettingsWindow.xaml.cs" index 42f2e14..fab2e05 100644 --- "a/tests/TestAcad09plus/\350\276\223\345\205\245\346\263\225\350\207\252\345\212\250\345\210\207\346\215\242/\350\265\253\346\200\235\350\276\223\345\205\245\346\263\225/Window/SettingsWindow.xaml.cs" +++ "b/tests/TestAcad09plus/\350\276\223\345\205\245\346\263\225\350\207\252\345\212\250\345\210\207\346\215\242/\350\265\253\346\200\235\350\276\223\345\205\245\346\263\225/Window/SettingsWindow.xaml.cs" @@ -28,7 +28,7 @@ private void Window_Loaded(object sender, RoutedEventArgs e) { CBox.SelectedIndex = (int)Settings.IMEStyle; ExCMD.Text = Settings.UserFilter; - DeCMD.Text = string.Join(",", IMEControl.DefaultCMDs); + DeCMD.Text = string.Join(",", IMEControl.DefaultCMDs.ToArray()); } private void Window_PreviewKeyDown(object sender, KeyEventArgs e) diff --git a/tests/TestShared/Copyclip.cs b/tests/TestShared/Copyclip.cs index a94e51b..1cc63ba 100644 --- a/tests/TestShared/Copyclip.cs +++ b/tests/TestShared/Copyclip.cs @@ -325,7 +325,7 @@ void Copy(bool getPoint, bool isEraseSsget = false) var cadClipFormat = ClipTool.RegisterClipboardFormat(ClipboardEnv.CadVer); // 剪贴板需要的指针: 克隆一个新的,不释放内存,不锁定内存(否则高频触发时候卡死) - var cadClipData = cadClipType.CloneToIntPtr(); + var cadClipData = cadClipType.CloneToPtr(); bool getFlag = ClipTool.OpenClipboardTask(true, () => { // 写入剪贴板: cad图元 diff --git a/tests/TestShared/TestMarshal.cs b/tests/TestShared/TestMarshal.cs index 3cdc0d9..755cb0e 100644 --- a/tests/TestShared/TestMarshal.cs +++ b/tests/TestShared/TestMarshal.cs @@ -60,9 +60,7 @@ public void Test_ImplicitPoint3D() var a1 = (Point3D*)&pt1; Debug.WriteLine("指针类型转换,获取x::" + a1->X); - var a = (IntPtr)(&pt1);//explicit 显式转换 == new - var pt2 = (Point3D)Marshal.PtrToStructure(a, typeof(Point3D)); - Debug.WriteLine("pt1转IntPtr地址::" + (int)&a); + var pt2 = Point3D.Create(new IntPtr(&pt1)); Debug.WriteLine("pt1地址::" + (int)&pt1); Debug.WriteLine("pt2地址::" + (int)&pt2); Debug.Assert(&pt1 == &pt2);//不相等,是申请了新内存 @@ -92,15 +90,15 @@ public void Test_Marshal() unsafe { var p = new IntPtr(&pt); - var result2 = (Point3D)Marshal.PtrToStructure(p, typeof(Point3D)); + var result2 = Point3D.Create(p); result2.X = 220; - result2.PutCreate(p); + result2.ToPtr(p); } "封送法:".Print(); pt.Print(); // 拷贝到数组,还原指针到结构,最后将内存空间转换为目标结构体 - // 这不闹嘛~ + // 浪费内存,这不闹嘛~ int typeSize = Marshal.SizeOf(pt); byte[] bytes = new byte[typeSize]; IntPtr structPtr = Marshal.AllocHGlobal(Marshal.SizeOf(pt)); -- Gitee From 1f30c1b29c130008173ca1df0d8e2d79c0bf65ff Mon Sep 17 00:00:00 2001 From: liuqihong <540762622@qq.com> Date: Tue, 1 Nov 2022 18:22:58 +0800 Subject: [PATCH 602/675] =?UTF-8?q?=E4=BC=98=E5=8C=96api=E8=B0=83=E7=94=A8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/IFoxCAD.Cad.Shared/Copyclip/WindowsAPI.cs | 2 +- .../InputBinding/Inputting.cs" | 186 +++--- .../InputBinding/InputtingForm.Designer.cs" | 542 +++++++++--------- .../InputBinding/InputtingForm.cs" | 102 ++-- 4 files changed, 417 insertions(+), 415 deletions(-) diff --git a/src/IFoxCAD.Cad.Shared/Copyclip/WindowsAPI.cs b/src/IFoxCAD.Cad.Shared/Copyclip/WindowsAPI.cs index ad42451..3f1baa1 100644 --- a/src/IFoxCAD.Cad.Shared/Copyclip/WindowsAPI.cs +++ b/src/IFoxCAD.Cad.Shared/Copyclip/WindowsAPI.cs @@ -266,7 +266,7 @@ public partial class WindowsAPI /// 输入法打开状态 ///
    /// - /// 非0值打开,0值关闭 + /// 非0打开,0关闭;(true中文,false英文) [DllImport("imm32.dll")] public static extern bool ImmGetOpenStatus(IntPtr hwnd); #endregion diff --git "a/tests/TestAcad09plus/\350\276\223\345\205\245\346\263\225\350\207\252\345\212\250\345\210\207\346\215\242/InputBinding/Inputting.cs" "b/tests/TestAcad09plus/\350\276\223\345\205\245\346\263\225\350\207\252\345\212\250\345\210\207\346\215\242/InputBinding/Inputting.cs" index b9278c8..721e561 100644 --- "a/tests/TestAcad09plus/\350\276\223\345\205\245\346\263\225\350\207\252\345\212\250\345\210\207\346\215\242/InputBinding/Inputting.cs" +++ "b/tests/TestAcad09plus/\350\276\223\345\205\245\346\263\225\350\207\252\345\212\250\345\210\207\346\215\242/InputBinding/Inputting.cs" @@ -9,11 +9,9 @@ */ namespace InputBinding; - using Autodesk.AutoCAD.Internal; using System.Threading; using System.Windows.Forms; -using static WindowsAPI; public class Inputting { @@ -114,7 +112,7 @@ bool AcadVersion19() && InputVar.CmdActive && IsTrigger() && _hwndCapture == IntPtr.Zero - && GetKeyboardLayout(GetCurrentThreadId()) < 0; + && WindowsAPI.GetKeyboardLayout(WindowsAPI.GetCurrentThreadId()) < 0; } public bool PreFilterMessage(ref Message m) @@ -128,9 +126,9 @@ public bool PreFilterMessage(ref Message m) if (m.Msg == 512 && _shiftYN1 && AcadVersion19()) { - _hanCount1Add = GetWindowTextLength(_hwndCaret); + _hanCount1Add = WindowsAPI.GetWindowTextLength(_hwndCaret); StringBuilder text = new(2 * _hanCount1Add); - GetWindowText(_hwndFocus, text, text.Capacity); + WindowsAPI.GetWindowText(_hwndFocus, text, text.Capacity); if (!text.ToString().Contains(InputVar.StatusBar)) { Utils.SetFocusToDwgView(); @@ -146,9 +144,9 @@ public bool PreFilterMessage(ref Message m) { if (!_shiftYN1 && AcadVersion19()) { - _hanCount1Add = GetWindowTextLength(_hwndCaret); + _hanCount1Add = WindowsAPI.GetWindowTextLength(_hwndCaret); StringBuilder text = new(2 * _hanCount1Add); - GetWindowText(_hwndFocus, text, text.Capacity); + WindowsAPI.GetWindowText(_hwndFocus, text, text.Capacity); if (!text.ToString().Contains(InputVar.StatusBar)) { Utils.SetFocusToDwgView(); @@ -166,7 +164,7 @@ public bool PreFilterMessage(ref Message m) IntPtr window = IntPtr.Zero; - var virtualKey = ImmGetVirtualKey(m.HWnd); + var virtualKey = WindowsAPI.ImmGetVirtualKey(m.HWnd); var vk = (int)virtualKey; if (vk != 16 && vk != 17 && vk != 18 && vk != 91 && InputVar.CmdActive) { @@ -174,22 +172,22 @@ public bool PreFilterMessage(ref Message m) if (_hwndFocus == _hwndCaret) { Env.Printl(InputVar.CmdStr); - StringBuilder text = new(2 * GetWindowTextLength(_hwndCaret)); - GetWindowText(_hwndCaret, text, text.Capacity); + StringBuilder text = new(2 * WindowsAPI.GetWindowTextLength(_hwndCaret)); + WindowsAPI.GetWindowText(_hwndCaret, text, text.Capacity); if (text.ToString().Contains(InputVar.CmdStr)) containCmdStr = true; } if (containCmdStr || IsTrigger()) { - _hanCount1Add = GetWindowTextLength(_hwndCaret); + _hanCount1Add = WindowsAPI.GetWindowTextLength(_hwndCaret); StringBuilder text = new(2 * _hanCount1Add); - GetWindowText(_hwndFocus, text, text.Capacity); + WindowsAPI.GetWindowText(_hwndFocus, text, text.Capacity); if (text.ToString().Contains(InputVar.StatusBar)) return false; - ImmGetConversionStatus(ImmGetContext(_hwndFocus), out _iMode3, out _); + WindowsAPI.ImmGetConversionStatus(WindowsAPI.ImmGetContext(_hwndFocus), out _iMode3, out _); if (_hwndCapture == IntPtr.Zero) Utils.SetFocusToDwgView(); @@ -198,13 +196,13 @@ public bool PreFilterMessage(ref Message m) { if (containCmdStr) window = _hwndCaret; - else if (!InputVar.DY) + else if (!InputVar.去多余字母) { var doc = Acap.DocumentManager.MdiActiveDocument; - window = GetWindow(GetTopWindow(doc.Window.Handle), 2U); + window = WindowsAPI.GetWindow(WindowsAPI.GetTopWindow(doc.Window.Handle), 2U); } } - else if (!InputVar.DY || _hwndCapture != IntPtr.Zero) + else if (!InputVar.去多余字母 || _hwndCapture != IntPtr.Zero) window = m.HWnd; } } @@ -213,7 +211,7 @@ public bool PreFilterMessage(ref Message m) if (window != IntPtr.Zero) { - PostMessage(window, m.Msg, virtualKey, m.LParam); + WindowsAPI.PostMessage(window, m.Msg, virtualKey, m.LParam); return true; } return false; @@ -235,21 +233,21 @@ public static void NewThread1() && (InputVar.CmdActive || !InputVar.TextEditor || _hwndActive == _hwndFocus)) return; - _hanCount1Add = GetWindowTextLength(_hwndFocus); + _hanCount1Add = WindowsAPI.GetWindowTextLength(_hwndFocus); StringBuilder text = new(2 * _hanCount1Add); - GetWindowText(_hwndFocus, text, text.Capacity); + WindowsAPI.GetWindowText(_hwndFocus, text, text.Capacity); var textStr = text.ToString(); if (textStr != string.Empty && !textStr.Contains(InputVar.StatusBar)) { - var context = ImmGetContext(_hwndFocus); - ImmGetConversionStatus(context, out _iMode1, out _); - _status = ImmGetOpenStatus(context); + var context = WindowsAPI.ImmGetContext(_hwndFocus); + WindowsAPI.ImmGetConversionStatus(context, out _iMode1, out _); + _status = WindowsAPI.ImmGetOpenStatus(context); _hanCount1 = GetHanNum(textStr); var length = _hanCount1Add - 2 * _hanCount1; - if (InputVar.Windows10 || InputVar.CK || InputVar.CS || InputVar.AS) + if (InputVar.Windows10 || InputVar.CtrlAndSpace || InputVar.CtrlAndShift || InputVar.WinAndSpace) { if (!InputVar.CMD) { @@ -263,7 +261,7 @@ public static void NewThread1() else InputVar.CMD = false; } - else if (InputVar.SF || InputVar.CT) + else if (InputVar.Shift || InputVar.Ctrl) { if (_iMode3 != -1) { @@ -301,9 +299,9 @@ public static ResultBuffer InputLispDoubleClick(ResultBuffer rb) var length = hanCountAdd - 2 * hanCount; IsTrigger(); - var context = ImmGetContext(_hwndFocus); - ImmGetConversionStatus(context, out _iMode1, out _); - _status = ImmGetOpenStatus(context); + var context = WindowsAPI.ImmGetContext(_hwndFocus); + WindowsAPI.ImmGetConversionStatus(context, out _iMode1, out _); + _status = WindowsAPI.ImmGetOpenStatus(context); new Thread(() => { _mre3.WaitOne(); @@ -315,7 +313,7 @@ public static ResultBuffer InputLispDoubleClick(ResultBuffer rb) } Thread.Sleep(50); - if (InputVar.Windows10 || InputVar.CK || InputVar.CS || InputVar.AS) + if (InputVar.Windows10 || InputVar.CtrlAndSpace || InputVar.CtrlAndShift || InputVar.WinAndSpace) { if (!(_status && length > hanCount)) if (_status || length > hanCount) @@ -324,8 +322,8 @@ public static ResultBuffer InputLispDoubleClick(ResultBuffer rb) } else { - if (!InputVar.SF - && !InputVar.CT || length <= hanCount + if (!InputVar.Shift + && !InputVar.Ctrl || length <= hanCount && _iMode3 == _iMode1 && _iMode3 != -1 || length > hanCount && _iMode3 > _iMode1 @@ -369,11 +367,11 @@ public static ResultBuffer InputLispStart(ResultBuffer rb) Thread.Sleep(100); IsTrigger(); - var context = ImmGetContext(_hwndFocus); - ImmGetConversionStatus(context, out _iMode1, out _); - _status = ImmGetOpenStatus(context); + var context = WindowsAPI.ImmGetContext(_hwndFocus); + WindowsAPI.ImmGetConversionStatus(context, out _iMode1, out _); + _status = WindowsAPI.ImmGetOpenStatus(context); - if (InputVar.Windows10 || InputVar.CK || InputVar.CS || InputVar.AS) + if (InputVar.Windows10 || InputVar.CtrlAndSpace || InputVar.CtrlAndShift || InputVar.WinAndSpace) { if (_status && length > hanCount) { @@ -397,7 +395,7 @@ public static ResultBuffer InputLispStart(ResultBuffer rb) } else { - if (!InputVar.SF && !InputVar.CT) + if (!InputVar.Shift && !InputVar.Ctrl) return; if (length <= hanCount && _iMode3 == _iMode1 && _iMode3 != -1) { @@ -448,11 +446,11 @@ public static ResultBuffer InputLispEnd(ResultBuffer rb) Thread.Sleep(100); IsTrigger(); - var context = ImmGetContext(_hwndFocus); - ImmGetConversionStatus(context, out _iMode1, out _); - _status = ImmGetOpenStatus(context); + var context = WindowsAPI.ImmGetContext(_hwndFocus); + WindowsAPI.ImmGetConversionStatus(context, out _iMode1, out _); + _status = WindowsAPI.ImmGetOpenStatus(context); - if (InputVar.Windows10 || InputVar.CK || InputVar.CS || InputVar.AS) + if (InputVar.Windows10 || InputVar.CtrlAndSpace || InputVar.CtrlAndShift || InputVar.WinAndSpace) { if (_status && length > hanCount) PressKey(); @@ -462,8 +460,8 @@ public static ResultBuffer InputLispEnd(ResultBuffer rb) } else { - if (!InputVar.SF - && !InputVar.CT || length <= hanCount + if (!InputVar.Shift + && !InputVar.Ctrl || length <= hanCount && _iMode3 == _iMode1 && _iMode3 != -1 || length > hanCount && _iMode3 > _iMode1 @@ -485,62 +483,65 @@ public static ResultBuffer InputLispEnd(ResultBuffer rb) } /// - /// 处理按键事件 + /// 切换输入法 /// public static void PressKey() { - if (InputVar.SF) + // 需要用户先设置一下自己是怎么切换输入法的 + // 16 == shift + // 32 回车 + if (InputVar.Shift) { if (_iMode3 == -1) Thread.Sleep(400); - KeybdEvent(16, 0, 0, 0); - KeybdEvent(16, 0, 2, 0); + WindowsAPI.KeybdEvent(16, 0, 0, 0); + WindowsAPI.KeybdEvent(16, 0, 2, 0); } - else if (InputVar.CT) + else if (InputVar.Ctrl) { if (_iMode3 == -1) Thread.Sleep(400); - KeybdEvent(17, 0, 0, 0); - KeybdEvent(17, 0, 2, 0); + WindowsAPI.KeybdEvent(17, 0, 0, 0); + WindowsAPI.KeybdEvent(17, 0, 2, 0); } - else if (InputVar.CK) + else if (InputVar.CtrlAndSpace) { if (_iMode3 == -1) Thread.Sleep(400); - KeybdEvent(17, 0, 0, 0); - KeybdEvent(32, 0, 0, 0); - KeybdEvent(32, 0, 2, 0); - KeybdEvent(17, 0, 2, 0); - if (GetKeyState(20) != 1) + WindowsAPI.KeybdEvent(17, 0, 0, 0); + WindowsAPI.KeybdEvent(32, 0, 0, 0); + WindowsAPI.KeybdEvent(32, 0, 2, 0); + WindowsAPI.KeybdEvent(17, 0, 2, 0); + if (WindowsAPI.GetKeyState(20) != 1) return; - KeybdEvent(20, 0, 0, 0); - KeybdEvent(20, 0, 2, 0); + WindowsAPI.KeybdEvent(20, 0, 0, 0); + WindowsAPI.KeybdEvent(20, 0, 2, 0); } - else if (InputVar.CS) + else if (InputVar.CtrlAndShift) { if (_iMode3 == -1) Thread.Sleep(400); - KeybdEvent(16, 0, 0, 0); - KeybdEvent(17, 0, 0, 0); - KeybdEvent(17, 0, 2, 0); - KeybdEvent(16, 0, 2, 0); - if (GetKeyState(20) != 1) + WindowsAPI.KeybdEvent(16, 0, 0, 0); + WindowsAPI.KeybdEvent(17, 0, 0, 0); + WindowsAPI.KeybdEvent(17, 0, 2, 0); + WindowsAPI.KeybdEvent(16, 0, 2, 0); + if (WindowsAPI.GetKeyState(20) != 1) return; - KeybdEvent(20, 0, 0, 0); - KeybdEvent(20, 0, 2, 0); + WindowsAPI.KeybdEvent(20, 0, 0, 0); + WindowsAPI.KeybdEvent(20, 0, 2, 0); } - else if (InputVar.AS) + else if (InputVar.WinAndSpace) { if (_iMode3 == -1) Thread.Sleep(400); - KeybdEvent(91, 0, 0, 0); - KeybdEvent(32, 0, 0, 0); - KeybdEvent(32, 0, 2, 0); - KeybdEvent(91, 0, 2, 0); - if (GetKeyState(20) != 1) + WindowsAPI.KeybdEvent(91, 0, 0, 0); + WindowsAPI.KeybdEvent(32, 0, 0, 0); + WindowsAPI.KeybdEvent(32, 0, 2, 0); + WindowsAPI.KeybdEvent(91, 0, 2, 0); + if (WindowsAPI.GetKeyState(20) != 1) return; - KeybdEvent(20, 0, 0, 0); - KeybdEvent(20, 0, 2, 0); + WindowsAPI.KeybdEvent(20, 0, 0, 0); + WindowsAPI.KeybdEvent(20, 0, 2, 0); } else { @@ -556,9 +557,9 @@ public static void NewThread100() Thread.Sleep(100); IsTrigger(); - var context = ImmGetContext(_hwndFocus); - ImmGetConversionStatus(context, out _iMode2, out _); - _status = ImmGetOpenStatus(context); + var context = WindowsAPI.ImmGetContext(_hwndFocus); + WindowsAPI.ImmGetConversionStatus(context, out _iMode2, out _); + _status = WindowsAPI.ImmGetOpenStatus(context); var length = _hanCount1Add - 2 * _hanCount1; @@ -588,8 +589,8 @@ public static void NewThread100() /// public static bool IsTrigger() { - var pid = GetWindowThreadProcessId(GetForegroundWindow(), out uint lpdwProcessId); - var lpgui = GuiThreadInfo.Create(pid); + var pid = WindowsAPI.GetWindowThreadProcessId(WindowsAPI.GetForegroundWindow(), out uint lpdwProcessId); + var lpgui = WindowsAPI.GuiThreadInfo.Create(pid); _hwndActive = lpgui.hwndActive; _hwndFocus = lpgui.hwndFocus; _hwndCaret = lpgui.hwndCaret; @@ -618,13 +619,14 @@ public static int GetHanNum(string str) public class InputVar { - public static bool DY; - public static bool SF; - public static bool CT; - public static bool CK; - public static bool CS; - public static bool AS; - public static bool SL; + public static bool 去多余字母; + public static bool Shift; + public static bool Ctrl; + public static bool CtrlAndSpace; + public static bool CtrlAndShift; + public static bool WinAndSpace; + + public static bool 增加字母; public static bool DK; public static bool CMD; @@ -674,22 +676,22 @@ public static void LoadConfig() { if (!File.Exists(Section)) { - InputVar.SF = true; + InputVar.Shift = true; return; } var str = GetValue(Section, "Shift切换"); - InputVar.SF = str == "True"; + InputVar.Shift = str == "True"; str = GetValue(Section, "Ctrl切换"); - InputVar.CT = str == "True"; + InputVar.Ctrl = str == "True"; str = GetValue(Section, "Ctrl+空格"); - InputVar.CK = str == "True"; + InputVar.CtrlAndSpace = str == "True"; str = GetValue(Section, "Ctrl+Shift"); - InputVar.CS = str == "True"; + InputVar.CtrlAndShift = str == "True"; str = GetValue(Section, "Win+空格"); - InputVar.AS = str == "True"; + InputVar.WinAndSpace = str == "True"; str = GetValue(Section, "去多余字母"); - InputVar.DY = str == "True"; + InputVar.去多余字母 = str == "True"; str = GetValue(Section, "增加字母"); - InputVar.SL = str == "True"; + InputVar.增加字母 = str == "True"; } } \ No newline at end of file diff --git "a/tests/TestAcad09plus/\350\276\223\345\205\245\346\263\225\350\207\252\345\212\250\345\210\207\346\215\242/InputBinding/InputtingForm.Designer.cs" "b/tests/TestAcad09plus/\350\276\223\345\205\245\346\263\225\350\207\252\345\212\250\345\210\207\346\215\242/InputBinding/InputtingForm.Designer.cs" index f2fdfa9..4fdb39c 100644 --- "a/tests/TestAcad09plus/\350\276\223\345\205\245\346\263\225\350\207\252\345\212\250\345\210\207\346\215\242/InputBinding/InputtingForm.Designer.cs" +++ "b/tests/TestAcad09plus/\350\276\223\345\205\245\346\263\225\350\207\252\345\212\250\345\210\207\346\215\242/InputBinding/InputtingForm.Designer.cs" @@ -11,270 +11,270 @@ partial class InputtingForm ///
    private void InitializeComponent() { - this.button1 = new System.Windows.Forms.Button(); - this.button2 = new System.Windows.Forms.Button(); - this.button3 = new System.Windows.Forms.Button(); - this.checkBox1 = new System.Windows.Forms.CheckBox(); - this.checkBox2 = new System.Windows.Forms.CheckBox(); - this.checkBox3 = new System.Windows.Forms.CheckBox(); - this.checkBox4 = new System.Windows.Forms.CheckBox(); - this.checkBox5 = new System.Windows.Forms.CheckBox(); - this.checkBox6 = new System.Windows.Forms.CheckBox(); - this.checkBox7 = new System.Windows.Forms.CheckBox(); - this.label1 = new System.Windows.Forms.Label(); - this.label2 = new System.Windows.Forms.Label(); - this.label3 = new System.Windows.Forms.Label(); - this.label5 = new System.Windows.Forms.Label(); - this.label4 = new System.Windows.Forms.Label(); - this.groupBox1 = new System.Windows.Forms.GroupBox(); - this.groupBox2 = new System.Windows.Forms.GroupBox(); - this.groupBox3 = new System.Windows.Forms.GroupBox(); - this.groupBox1.SuspendLayout(); - this.groupBox2.SuspendLayout(); - this.groupBox3.SuspendLayout(); - this.SuspendLayout(); - // - //button1 - // - this.button1.Font = new System.Drawing.Font("微软雅黑", 10.5F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(134))); - this.button1.Location = new System.Drawing.Point(81, 489); - this.button1.Name = "button1"; - this.button1.Size = new System.Drawing.Size(142, 32); - this.button1.TabIndex = 3; - this.button1.Text = "保存配置并退出"; - this.button1.UseVisualStyleBackColor = true; - this.button1.Click += new System.EventHandler(this.Button1_Click); - // - //button2 - // - this.button2.Font = new System.Drawing.Font("微软雅黑", 9F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(134))); - this.button2.Location = new System.Drawing.Point(15, 155); - this.button2.Name = "button2"; - this.button2.Size = new System.Drawing.Size(116, 32); - this.button2.TabIndex = 9; - this.button2.Text = "临时关闭自动切换"; - this.button2.UseVisualStyleBackColor = true; - this.button2.Click += new System.EventHandler(this.Button2_Click); - // - //button3 - // - this.button3.Font = new System.Drawing.Font("微软雅黑", 9F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(134))); - this.button3.Location = new System.Drawing.Point(137, 155); - this.button3.Name = "button3"; - this.button3.Size = new System.Drawing.Size(132, 32); - this.button3.TabIndex = 10; - this.button3.Text = "打开自动切换"; - this.button3.UseVisualStyleBackColor = true; - this.button3.Click += new System.EventHandler(this.Button3_Click); - // - //checkBox1 - // - this.checkBox1.AutoSize = true; - this.checkBox1.Checked = true; - this.checkBox1.CheckState = System.Windows.Forms.CheckState.Checked; - this.checkBox1.Font = new System.Drawing.Font("微软雅黑", 10.5F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(134))); - this.checkBox1.Location = new System.Drawing.Point(10, 54); - this.checkBox1.Name = "checkBox1"; - this.checkBox1.Size = new System.Drawing.Size(167, 24); - this.checkBox1.TabIndex = 6; - this.checkBox1.Text = "Shift切换中英文(默认)"; - this.checkBox1.UseVisualStyleBackColor = true; - this.checkBox1.CheckedChanged += new System.EventHandler(this.CheckBox1_CheckedChanged); - // - //checkBox2 - // - this.checkBox2.AutoSize = true; - this.checkBox2.Font = new System.Drawing.Font("微软雅黑", 10.5F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(134))); - this.checkBox2.Location = new System.Drawing.Point(10, 84); - this.checkBox2.Name = "checkBox2"; - this.checkBox2.Size = new System.Drawing.Size(121, 24); - this.checkBox2.TabIndex = 7; - this.checkBox2.Text = "Ctrl切换中英文"; - this.checkBox2.UseVisualStyleBackColor = true; - this.checkBox2.CheckedChanged += new System.EventHandler(this.CheckBox2_CheckedChanged); - // - //checkBox3 - // - this.checkBox3.AutoSize = true; - this.checkBox3.Enabled = false; - this.checkBox3.Font = new System.Drawing.Font("微软雅黑", 10.5F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(134))); - this.checkBox3.Location = new System.Drawing.Point(15, 25); - this.checkBox3.Name = "checkBox3"; - this.checkBox3.Size = new System.Drawing.Size(196, 24); - this.checkBox3.TabIndex = 8; - this.checkBox3.Text = "消除切换后命令行多余字母"; - this.checkBox3.UseVisualStyleBackColor = true; - this.checkBox3.CheckedChanged += new System.EventHandler(this.CheckBox3_CheckedChanged); - // - //checkBox4 - // - this.checkBox4.AutoSize = true; - this.checkBox4.Font = new System.Drawing.Font("微软雅黑", 10.5F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(134))); - this.checkBox4.Location = new System.Drawing.Point(10, 54); - this.checkBox4.Name = "checkBox4"; - this.checkBox4.Size = new System.Drawing.Size(89, 24); - this.checkBox4.TabIndex = 11; - this.checkBox4.Text = "Ctrl+空格"; - this.checkBox4.UseVisualStyleBackColor = true; - this.checkBox4.CheckedChanged += new System.EventHandler(this.CheckBox4_CheckedChanged); - // - //checkBox5 - // - this.checkBox5.AutoSize = true; - this.checkBox5.Font = new System.Drawing.Font("微软雅黑", 10.5F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(134))); - this.checkBox5.Location = new System.Drawing.Point(10, 84); - this.checkBox5.Name = "checkBox5"; - this.checkBox5.Size = new System.Drawing.Size(92, 24); - this.checkBox5.TabIndex = 12; - this.checkBox5.Text = "Ctrl+Shift"; - this.checkBox5.UseVisualStyleBackColor = true; - this.checkBox5.CheckedChanged += new System.EventHandler(this.CheckBox5_CheckedChanged); - // - //checkBox6 - // - this.checkBox6.AutoSize = true; - this.checkBox6.Font = new System.Drawing.Font("微软雅黑", 10.5F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(134))); - this.checkBox6.Location = new System.Drawing.Point(10, 114); - this.checkBox6.Name = "checkBox6"; - this.checkBox6.Size = new System.Drawing.Size(93, 24); - this.checkBox6.TabIndex = 13; - this.checkBox6.Text = "Win+空格"; - this.checkBox6.UseVisualStyleBackColor = true; - this.checkBox6.CheckedChanged += new System.EventHandler(this.CheckBox6_CheckedChanged); - // - //checkBox7 - // - this.checkBox7.AutoSize = true; - this.checkBox7.Enabled = false; - this.checkBox7.Font = new System.Drawing.Font("微软雅黑", 10.5F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(134))); - this.checkBox7.Location = new System.Drawing.Point(15, 91); - this.checkBox7.Name = "checkBox7"; - this.checkBox7.Size = new System.Drawing.Size(238, 24); - this.checkBox7.TabIndex = 18; - this.checkBox7.Text = "解决切换后需多按一次命令首字母"; - this.checkBox7.UseVisualStyleBackColor = true; - this.checkBox7.CheckedChanged += new System.EventHandler(this.CheckBox7_CheckedChanged); - // - //label1 - // - this.label1.AutoSize = true; - this.label1.Font = new System.Drawing.Font("微软雅黑", 9F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(134))); - this.label1.Location = new System.Drawing.Point(87, 524); - this.label1.Name = "label1"; - this.label1.Size = new System.Drawing.Size(142, 17); - this.label1.TabIndex = 4; - this.label1.Text = "(.ini配置文件在dll文件夹)"; - // - //label2 - // - this.label2.AutoSize = true; - this.label2.Font = new System.Drawing.Font("微软雅黑", 10.5F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(134))); - this.label2.Location = new System.Drawing.Point(6, 26); - this.label2.Name = "label2"; - this.label2.Size = new System.Drawing.Size(169, 20); - this.label2.TabIndex = 16; - this.label2.Text = "中/英文状态切换快捷键:"; - // - //label3 - // - this.label3.AutoSize = true; - this.label3.Font = new System.Drawing.Font("微软雅黑", 10.5F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(134))); - this.label3.Location = new System.Drawing.Point(6, 26); - this.label3.Name = "label3"; - this.label3.Size = new System.Drawing.Size(214, 20); - this.label3.TabIndex = 17; - this.label3.Text = "中文(CH)/英文(EN)切换快捷键:"; - // - //label5 - // - this.label5.AutoSize = true; - this.label5.Enabled = false; - this.label5.Font = new System.Drawing.Font("微软雅黑", 9F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(134))); - this.label5.Location = new System.Drawing.Point(12, 117); - this.label5.Name = "label5"; - this.label5.Size = new System.Drawing.Size(240, 34); - this.label5.TabIndex = 19; - this.label5.Text = "(部分输入法和CAD版本存在切换后\r\n需多按一次首命令首字母,勾选此项可解决)"; - // - //label4 - // - this.label4.AutoSize = true; - this.label4.Enabled = false; - this.label4.Font = new System.Drawing.Font("微软雅黑", 9F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(134))); - this.label4.Location = new System.Drawing.Point(12, 51); - this.label4.Name = "label4"; - this.label4.Size = new System.Drawing.Size(204, 34); - this.label4.TabIndex = 17; - this.label4.Text = "(部分输入法和CAD版本存在切换后\r\n命令行有多余字母,勾选此项可消除)"; - // - //groupBox1 - // - this.groupBox1.Controls.Add(this.label2); - this.groupBox1.Controls.Add(this.checkBox1); - this.groupBox1.Controls.Add(this.checkBox2); - this.groupBox1.Font = new System.Drawing.Font("微软雅黑", 10.5F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(134))); - this.groupBox1.Location = new System.Drawing.Point(12, 12); - this.groupBox1.Name = "groupBox1"; - this.groupBox1.Size = new System.Drawing.Size(275, 117); - this.groupBox1.TabIndex = 14; - this.groupBox1.TabStop = false; - this.groupBox1.Text = "Shift懒人版"; - // - //groupBox2 - // - this.groupBox2.Controls.Add(this.label3); - this.groupBox2.Controls.Add(this.checkBox4); - this.groupBox2.Controls.Add(this.checkBox6); - this.groupBox2.Controls.Add(this.checkBox5); - this.groupBox2.Font = new System.Drawing.Font("微软雅黑", 10.5F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(134))); - this.groupBox2.Location = new System.Drawing.Point(12, 135); - this.groupBox2.Name = "groupBox2"; - this.groupBox2.Size = new System.Drawing.Size(275, 149); - this.groupBox2.TabIndex = 15; - this.groupBox2.TabStop = false; - this.groupBox2.Text = "进阶版"; - // - //groupBox3 - // - this.groupBox3.Controls.Add(this.button2); - this.groupBox3.Controls.Add(this.button3); - this.groupBox3.Controls.Add(this.label4); - this.groupBox3.Controls.Add(this.label5); - this.groupBox3.Controls.Add(this.checkBox3); - this.groupBox3.Controls.Add(this.checkBox7); - this.groupBox3.Font = new System.Drawing.Font("微软雅黑", 10.5F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(134))); - this.groupBox3.Location = new System.Drawing.Point(12, 290); - this.groupBox3.Name = "groupBox3"; - this.groupBox3.Size = new System.Drawing.Size(275, 193); - this.groupBox3.TabIndex = 16; - this.groupBox3.TabStop = false; - this.groupBox3.Text = "其它"; - // - //InputtingForm - // - this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 12F); - this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; - this.ClientSize = new System.Drawing.Size(296, 550); - this.Controls.Add(this.groupBox3); - this.Controls.Add(this.groupBox2); - this.Controls.Add(this.groupBox1); - this.Controls.Add(this.button1); - this.Controls.Add(this.label1); - this.FormBorderStyle = System.Windows.Forms.FormBorderStyle.FixedDialog; - this.MaximizeBox = false; - this.MinimizeBox = false; - this.Name = "InputtingForm"; - this.StartPosition = System.Windows.Forms.FormStartPosition.CenterScreen; - this.Text = "输入法自动切换"; - this.Load += new System.EventHandler(this.Form1_Load); - this.groupBox1.ResumeLayout(false); - this.groupBox1.PerformLayout(); - this.groupBox2.ResumeLayout(false); - this.groupBox2.PerformLayout(); - this.groupBox3.ResumeLayout(false); - this.groupBox3.PerformLayout(); - this.ResumeLayout(false); - this.PerformLayout(); + this.button1 = new System.Windows.Forms.Button(); + this.button2 = new System.Windows.Forms.Button(); + this.button3 = new System.Windows.Forms.Button(); + this.checkBox1_shift = new System.Windows.Forms.CheckBox(); + this.checkBox2_ctrl = new System.Windows.Forms.CheckBox(); + this.checkBox3_切换后多余字母 = new System.Windows.Forms.CheckBox(); + this.checkBox4_ctrlAddSpace = new System.Windows.Forms.CheckBox(); + this.checkBox5_ctrlAndShift = new System.Windows.Forms.CheckBox(); + this.checkBox6_winAndSpace = new System.Windows.Forms.CheckBox(); + this.checkBox7_切换后首字母 = new System.Windows.Forms.CheckBox(); + this.label1 = new System.Windows.Forms.Label(); + this.label2 = new System.Windows.Forms.Label(); + this.label3 = new System.Windows.Forms.Label(); + this.label5 = new System.Windows.Forms.Label(); + this.label4 = new System.Windows.Forms.Label(); + this.groupBox1 = new System.Windows.Forms.GroupBox(); + this.groupBox2 = new System.Windows.Forms.GroupBox(); + this.groupBox3 = new System.Windows.Forms.GroupBox(); + this.groupBox1.SuspendLayout(); + this.groupBox2.SuspendLayout(); + this.groupBox3.SuspendLayout(); + this.SuspendLayout(); + // + // button1 + // + this.button1.Font = new System.Drawing.Font("微软雅黑", 10.5F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(134))); + this.button1.Location = new System.Drawing.Point(81, 489); + this.button1.Name = "button1"; + this.button1.Size = new System.Drawing.Size(142, 32); + this.button1.TabIndex = 3; + this.button1.Text = "保存配置并退出"; + this.button1.UseVisualStyleBackColor = true; + this.button1.Click += new System.EventHandler(this.Button1_Click); + // + // button2 + // + this.button2.Font = new System.Drawing.Font("微软雅黑", 9F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(134))); + this.button2.Location = new System.Drawing.Point(15, 155); + this.button2.Name = "button2"; + this.button2.Size = new System.Drawing.Size(116, 32); + this.button2.TabIndex = 9; + this.button2.Text = "临时关闭自动切换"; + this.button2.UseVisualStyleBackColor = true; + this.button2.Click += new System.EventHandler(this.Button2_Click); + // + // button3 + // + this.button3.Font = new System.Drawing.Font("微软雅黑", 9F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(134))); + this.button3.Location = new System.Drawing.Point(137, 155); + this.button3.Name = "button3"; + this.button3.Size = new System.Drawing.Size(132, 32); + this.button3.TabIndex = 10; + this.button3.Text = "打开自动切换"; + this.button3.UseVisualStyleBackColor = true; + this.button3.Click += new System.EventHandler(this.Button3_Click); + // + // checkBox1_shift + // + this.checkBox1_shift.AutoSize = true; + this.checkBox1_shift.Checked = true; + this.checkBox1_shift.CheckState = System.Windows.Forms.CheckState.Checked; + this.checkBox1_shift.Font = new System.Drawing.Font("微软雅黑", 10.5F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(134))); + this.checkBox1_shift.Location = new System.Drawing.Point(10, 54); + this.checkBox1_shift.Name = "checkBox1_shift"; + this.checkBox1_shift.Size = new System.Drawing.Size(167, 24); + this.checkBox1_shift.TabIndex = 6; + this.checkBox1_shift.Text = "Shift切换中英文(默认)"; + this.checkBox1_shift.UseVisualStyleBackColor = true; + this.checkBox1_shift.CheckedChanged += new System.EventHandler(this.CheckBox1_CheckedChanged); + // + // checkBox2_ctrl + // + this.checkBox2_ctrl.AutoSize = true; + this.checkBox2_ctrl.Font = new System.Drawing.Font("微软雅黑", 10.5F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(134))); + this.checkBox2_ctrl.Location = new System.Drawing.Point(10, 84); + this.checkBox2_ctrl.Name = "checkBox2_ctrl"; + this.checkBox2_ctrl.Size = new System.Drawing.Size(121, 24); + this.checkBox2_ctrl.TabIndex = 7; + this.checkBox2_ctrl.Text = "Ctrl切换中英文"; + this.checkBox2_ctrl.UseVisualStyleBackColor = true; + this.checkBox2_ctrl.CheckedChanged += new System.EventHandler(this.CheckBox2_CheckedChanged); + // + // checkBox3_切换后多余字母 + // + this.checkBox3_切换后多余字母.AutoSize = true; + this.checkBox3_切换后多余字母.Enabled = false; + this.checkBox3_切换后多余字母.Font = new System.Drawing.Font("微软雅黑", 10.5F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(134))); + this.checkBox3_切换后多余字母.Location = new System.Drawing.Point(15, 25); + this.checkBox3_切换后多余字母.Name = "checkBox3_切换后多余字母"; + this.checkBox3_切换后多余字母.Size = new System.Drawing.Size(196, 24); + this.checkBox3_切换后多余字母.TabIndex = 8; + this.checkBox3_切换后多余字母.Text = "消除切换后命令行多余字母"; + this.checkBox3_切换后多余字母.UseVisualStyleBackColor = true; + this.checkBox3_切换后多余字母.CheckedChanged += new System.EventHandler(this.CheckBox3_CheckedChanged); + // + // checkBox4_ctrlAddSpace + // + this.checkBox4_ctrlAddSpace.AutoSize = true; + this.checkBox4_ctrlAddSpace.Font = new System.Drawing.Font("微软雅黑", 10.5F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(134))); + this.checkBox4_ctrlAddSpace.Location = new System.Drawing.Point(10, 54); + this.checkBox4_ctrlAddSpace.Name = "checkBox4_ctrlAddSpace"; + this.checkBox4_ctrlAddSpace.Size = new System.Drawing.Size(89, 24); + this.checkBox4_ctrlAddSpace.TabIndex = 11; + this.checkBox4_ctrlAddSpace.Text = "Ctrl+空格"; + this.checkBox4_ctrlAddSpace.UseVisualStyleBackColor = true; + this.checkBox4_ctrlAddSpace.CheckedChanged += new System.EventHandler(this.CheckBox4_CheckedChanged); + // + // checkBox5_ctrlAndShift + // + this.checkBox5_ctrlAndShift.AutoSize = true; + this.checkBox5_ctrlAndShift.Font = new System.Drawing.Font("微软雅黑", 10.5F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(134))); + this.checkBox5_ctrlAndShift.Location = new System.Drawing.Point(10, 84); + this.checkBox5_ctrlAndShift.Name = "checkBox5_ctrlAndShift"; + this.checkBox5_ctrlAndShift.Size = new System.Drawing.Size(92, 24); + this.checkBox5_ctrlAndShift.TabIndex = 12; + this.checkBox5_ctrlAndShift.Text = "Ctrl+Shift"; + this.checkBox5_ctrlAndShift.UseVisualStyleBackColor = true; + this.checkBox5_ctrlAndShift.CheckedChanged += new System.EventHandler(this.CheckBox5_CheckedChanged); + // + // checkBox6_winAndSpace + // + this.checkBox6_winAndSpace.AutoSize = true; + this.checkBox6_winAndSpace.Font = new System.Drawing.Font("微软雅黑", 10.5F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(134))); + this.checkBox6_winAndSpace.Location = new System.Drawing.Point(10, 114); + this.checkBox6_winAndSpace.Name = "checkBox6_winAndSpace"; + this.checkBox6_winAndSpace.Size = new System.Drawing.Size(93, 24); + this.checkBox6_winAndSpace.TabIndex = 13; + this.checkBox6_winAndSpace.Text = "Win+空格"; + this.checkBox6_winAndSpace.UseVisualStyleBackColor = true; + this.checkBox6_winAndSpace.CheckedChanged += new System.EventHandler(this.CheckBox6_CheckedChanged); + // + // checkBox7_切换后首字母 + // + this.checkBox7_切换后首字母.AutoSize = true; + this.checkBox7_切换后首字母.Enabled = false; + this.checkBox7_切换后首字母.Font = new System.Drawing.Font("微软雅黑", 10.5F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(134))); + this.checkBox7_切换后首字母.Location = new System.Drawing.Point(15, 91); + this.checkBox7_切换后首字母.Name = "checkBox7_切换后首字母"; + this.checkBox7_切换后首字母.Size = new System.Drawing.Size(238, 24); + this.checkBox7_切换后首字母.TabIndex = 18; + this.checkBox7_切换后首字母.Text = "解决切换后需多按一次命令首字母"; + this.checkBox7_切换后首字母.UseVisualStyleBackColor = true; + this.checkBox7_切换后首字母.CheckedChanged += new System.EventHandler(this.CheckBox7_CheckedChanged); + // + // label1 + // + this.label1.AutoSize = true; + this.label1.Font = new System.Drawing.Font("微软雅黑", 9F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(134))); + this.label1.Location = new System.Drawing.Point(87, 524); + this.label1.Name = "label1"; + this.label1.Size = new System.Drawing.Size(142, 17); + this.label1.TabIndex = 4; + this.label1.Text = "(.ini配置文件在dll文件夹)"; + // + // label2 + // + this.label2.AutoSize = true; + this.label2.Font = new System.Drawing.Font("微软雅黑", 10.5F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(134))); + this.label2.Location = new System.Drawing.Point(6, 26); + this.label2.Name = "label2"; + this.label2.Size = new System.Drawing.Size(169, 20); + this.label2.TabIndex = 16; + this.label2.Text = "中/英文状态切换快捷键:"; + // + // label3 + // + this.label3.AutoSize = true; + this.label3.Font = new System.Drawing.Font("微软雅黑", 10.5F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(134))); + this.label3.Location = new System.Drawing.Point(6, 26); + this.label3.Name = "label3"; + this.label3.Size = new System.Drawing.Size(214, 20); + this.label3.TabIndex = 17; + this.label3.Text = "中文(CH)/英文(EN)切换快捷键:"; + // + // label5 + // + this.label5.AutoSize = true; + this.label5.Enabled = false; + this.label5.Font = new System.Drawing.Font("微软雅黑", 9F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(134))); + this.label5.Location = new System.Drawing.Point(12, 117); + this.label5.Name = "label5"; + this.label5.Size = new System.Drawing.Size(240, 34); + this.label5.TabIndex = 19; + this.label5.Text = "(部分输入法和CAD版本存在切换后\r\n需多按一次首命令首字母,勾选此项可解决)"; + // + // label4 + // + this.label4.AutoSize = true; + this.label4.Enabled = false; + this.label4.Font = new System.Drawing.Font("微软雅黑", 9F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(134))); + this.label4.Location = new System.Drawing.Point(12, 51); + this.label4.Name = "label4"; + this.label4.Size = new System.Drawing.Size(204, 34); + this.label4.TabIndex = 17; + this.label4.Text = "(部分输入法和CAD版本存在切换后\r\n命令行有多余字母,勾选此项可消除)"; + // + // groupBox1 + // + this.groupBox1.Controls.Add(this.label2); + this.groupBox1.Controls.Add(this.checkBox1_shift); + this.groupBox1.Controls.Add(this.checkBox2_ctrl); + this.groupBox1.Font = new System.Drawing.Font("微软雅黑", 10.5F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(134))); + this.groupBox1.Location = new System.Drawing.Point(12, 12); + this.groupBox1.Name = "groupBox1"; + this.groupBox1.Size = new System.Drawing.Size(275, 117); + this.groupBox1.TabIndex = 14; + this.groupBox1.TabStop = false; + this.groupBox1.Text = "Shift懒人版"; + // + // groupBox2 + // + this.groupBox2.Controls.Add(this.label3); + this.groupBox2.Controls.Add(this.checkBox4_ctrlAddSpace); + this.groupBox2.Controls.Add(this.checkBox6_winAndSpace); + this.groupBox2.Controls.Add(this.checkBox5_ctrlAndShift); + this.groupBox2.Font = new System.Drawing.Font("微软雅黑", 10.5F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(134))); + this.groupBox2.Location = new System.Drawing.Point(12, 135); + this.groupBox2.Name = "groupBox2"; + this.groupBox2.Size = new System.Drawing.Size(275, 149); + this.groupBox2.TabIndex = 15; + this.groupBox2.TabStop = false; + this.groupBox2.Text = "进阶版"; + // + // groupBox3 + // + this.groupBox3.Controls.Add(this.button2); + this.groupBox3.Controls.Add(this.button3); + this.groupBox3.Controls.Add(this.label4); + this.groupBox3.Controls.Add(this.label5); + this.groupBox3.Controls.Add(this.checkBox3_切换后多余字母); + this.groupBox3.Controls.Add(this.checkBox7_切换后首字母); + this.groupBox3.Font = new System.Drawing.Font("微软雅黑", 10.5F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(134))); + this.groupBox3.Location = new System.Drawing.Point(12, 290); + this.groupBox3.Name = "groupBox3"; + this.groupBox3.Size = new System.Drawing.Size(275, 193); + this.groupBox3.TabIndex = 16; + this.groupBox3.TabStop = false; + this.groupBox3.Text = "其它"; + // + // InputtingForm + // + this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 12F); + this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; + this.ClientSize = new System.Drawing.Size(296, 550); + this.Controls.Add(this.groupBox3); + this.Controls.Add(this.groupBox2); + this.Controls.Add(this.groupBox1); + this.Controls.Add(this.button1); + this.Controls.Add(this.label1); + this.FormBorderStyle = System.Windows.Forms.FormBorderStyle.FixedDialog; + this.MaximizeBox = false; + this.MinimizeBox = false; + this.Name = "InputtingForm"; + this.StartPosition = System.Windows.Forms.FormStartPosition.CenterScreen; + this.Text = "输入法自动切换"; + this.Load += new System.EventHandler(this.Form1_Load); + this.groupBox1.ResumeLayout(false); + this.groupBox1.PerformLayout(); + this.groupBox2.ResumeLayout(false); + this.groupBox2.PerformLayout(); + this.groupBox3.ResumeLayout(false); + this.groupBox3.PerformLayout(); + this.ResumeLayout(false); + this.PerformLayout(); } @@ -283,13 +283,13 @@ private void InitializeComponent() private System.Windows.Forms.Button button2; private System.Windows.Forms.Button button3; - private System.Windows.Forms.CheckBox checkBox1; - private System.Windows.Forms.CheckBox checkBox2; - private System.Windows.Forms.CheckBox checkBox3; - private System.Windows.Forms.CheckBox checkBox4; - private System.Windows.Forms.CheckBox checkBox5; - private System.Windows.Forms.CheckBox checkBox6; - private System.Windows.Forms.CheckBox checkBox7; + private System.Windows.Forms.CheckBox checkBox1_shift; + private System.Windows.Forms.CheckBox checkBox2_ctrl; + private System.Windows.Forms.CheckBox checkBox3_切换后多余字母; + private System.Windows.Forms.CheckBox checkBox4_ctrlAddSpace; + private System.Windows.Forms.CheckBox checkBox5_ctrlAndShift; + private System.Windows.Forms.CheckBox checkBox6_winAndSpace; + private System.Windows.Forms.CheckBox checkBox7_切换后首字母; private System.Windows.Forms.GroupBox groupBox1; private System.Windows.Forms.GroupBox groupBox2; private System.Windows.Forms.GroupBox groupBox3; diff --git "a/tests/TestAcad09plus/\350\276\223\345\205\245\346\263\225\350\207\252\345\212\250\345\210\207\346\215\242/InputBinding/InputtingForm.cs" "b/tests/TestAcad09plus/\350\276\223\345\205\245\346\263\225\350\207\252\345\212\250\345\210\207\346\215\242/InputBinding/InputtingForm.cs" index 04c27d7..5d24110 100644 --- "a/tests/TestAcad09plus/\350\276\223\345\205\245\346\263\225\350\207\252\345\212\250\345\210\207\346\215\242/InputBinding/InputtingForm.cs" +++ "b/tests/TestAcad09plus/\350\276\223\345\205\245\346\263\225\350\207\252\345\212\250\345\210\207\346\215\242/InputBinding/InputtingForm.cs" @@ -12,24 +12,24 @@ private void Form1_Load(object sender, EventArgs e) { if (!File.Exists(InputHelper.SectionFile)) { - InputVar.SF = true; + InputVar.Shift = true; return; } var str = InputHelper.GetValue(InputHelper.Section, "Shift切换"); - checkBox1.Checked = InputVar.SF = bool.Parse(str); + checkBox1_shift.Checked = InputVar.Shift = bool.Parse(str); str = InputHelper.GetValue(InputHelper.Section, "Ctrl切换"); - checkBox2.Checked = InputVar.CT = bool.Parse(str); + checkBox2_ctrl.Checked = InputVar.Ctrl = bool.Parse(str); str = InputHelper.GetValue(InputHelper.Section, "Ctrl+空格"); - checkBox4.Checked = InputVar.CK = bool.Parse(str); + checkBox4_ctrlAddSpace.Checked = InputVar.CtrlAndSpace = bool.Parse(str); str = InputHelper.GetValue(InputHelper.Section, "Ctrl+Shift"); - checkBox5.Checked = InputVar.CS = bool.Parse(str); + checkBox5_ctrlAndShift.Checked = InputVar.CtrlAndShift = bool.Parse(str); str = InputHelper.GetValue(InputHelper.Section, "Win+空格"); - checkBox6.Checked = InputVar.AS = bool.Parse(str); + checkBox6_winAndSpace.Checked = InputVar.WinAndSpace = bool.Parse(str); str = InputHelper.GetValue(InputHelper.Section, "去多余字母"); - checkBox3.Checked = InputVar.DY = bool.Parse(str); + checkBox3_切换后多余字母.Checked = InputVar.去多余字母 = bool.Parse(str); str = InputHelper.GetValue(InputHelper.Section, "增加字母"); - checkBox7.Checked = InputVar.SL = bool.Parse(str); + checkBox7_切换后首字母.Checked = InputVar.增加字母 = bool.Parse(str); } /// /// 保存配置并退出 @@ -40,20 +40,20 @@ private void Button1_Click(object sender, EventArgs e) { try { - InputVar.SF = checkBox1.Checked; - InputVar.CT = checkBox2.Checked; - InputVar.DY = checkBox3.Checked; - InputVar.CK = checkBox4.Checked; - InputVar.CS = checkBox5.Checked; - InputVar.AS = checkBox6.Checked; - InputVar.SL = checkBox7.Checked; - InputHelper.WritePrivateProfileString(InputHelper.Section, "Shift切换", checkBox1.Checked.ToString(), InputHelper.SectionFile); - InputHelper.WritePrivateProfileString(InputHelper.Section, "Ctrl切换", checkBox2.Checked.ToString(), InputHelper.SectionFile); - InputHelper.WritePrivateProfileString(InputHelper.Section, "Ctrl+空格", checkBox4.Checked.ToString(), InputHelper.SectionFile); - InputHelper.WritePrivateProfileString(InputHelper.Section, "Ctrl+Shift", checkBox5.Checked.ToString(), InputHelper.SectionFile); - InputHelper.WritePrivateProfileString(InputHelper.Section, "Win+空格", checkBox6.Checked.ToString(), InputHelper.SectionFile); - InputHelper.WritePrivateProfileString(InputHelper.Section, "去多余字母", checkBox3.Checked.ToString(), InputHelper.SectionFile); - InputHelper.WritePrivateProfileString(InputHelper.Section, "增加字母", checkBox7.Checked.ToString(), InputHelper.SectionFile); + InputVar.Shift = checkBox1_shift.Checked; + InputVar.Ctrl = checkBox2_ctrl.Checked; + InputVar.CtrlAndSpace = checkBox4_ctrlAddSpace.Checked; + InputVar.CtrlAndShift = checkBox5_ctrlAndShift.Checked; + InputVar.WinAndSpace = checkBox6_winAndSpace.Checked; + InputVar.去多余字母 = checkBox3_切换后多余字母.Checked; + InputVar.增加字母 = checkBox7_切换后首字母.Checked; + InputHelper.WritePrivateProfileString(InputHelper.Section, "Shift切换", checkBox1_shift.Checked.ToString(), InputHelper.SectionFile); + InputHelper.WritePrivateProfileString(InputHelper.Section, "Ctrl切换", checkBox2_ctrl.Checked.ToString(), InputHelper.SectionFile); + InputHelper.WritePrivateProfileString(InputHelper.Section, "Ctrl+空格", checkBox4_ctrlAddSpace.Checked.ToString(), InputHelper.SectionFile); + InputHelper.WritePrivateProfileString(InputHelper.Section, "Ctrl+Shift", checkBox5_ctrlAndShift.Checked.ToString(), InputHelper.SectionFile); + InputHelper.WritePrivateProfileString(InputHelper.Section, "Win+空格", checkBox6_winAndSpace.Checked.ToString(), InputHelper.SectionFile); + InputHelper.WritePrivateProfileString(InputHelper.Section, "去多余字母", checkBox3_切换后多余字母.Checked.ToString(), InputHelper.SectionFile); + InputHelper.WritePrivateProfileString(InputHelper.Section, "增加字母", checkBox7_切换后首字母.Checked.ToString(), InputHelper.SectionFile); if (InputHelper.SectionFile != null) MessageBox.Show("保存成功"); @@ -90,12 +90,12 @@ private void Button3_Click(object sender, EventArgs e) /// private void CheckBox1_CheckedChanged(object sender, EventArgs e) { - if (!checkBox1.Checked) + if (!checkBox1_shift.Checked) return; - checkBox2.Checked = false; - checkBox4.Checked = false; - checkBox5.Checked = false; - checkBox6.Checked = false; + checkBox2_ctrl.Checked = false; + checkBox4_ctrlAddSpace.Checked = false; + checkBox5_ctrlAndShift.Checked = false; + checkBox6_winAndSpace.Checked = false; } /// /// Ctrl切换中英文 @@ -104,12 +104,12 @@ private void CheckBox1_CheckedChanged(object sender, EventArgs e) /// private void CheckBox2_CheckedChanged(object sender, EventArgs e) { - if (!checkBox2.Checked) + if (!checkBox2_ctrl.Checked) return; - checkBox1.Checked = false; - checkBox4.Checked = false; - checkBox5.Checked = false; - checkBox6.Checked = false; + checkBox1_shift.Checked = false; + checkBox4_ctrlAddSpace.Checked = false; + checkBox5_ctrlAndShift.Checked = false; + checkBox6_winAndSpace.Checked = false; } /// /// 消除切换后命令行多余字母 @@ -118,9 +118,9 @@ private void CheckBox2_CheckedChanged(object sender, EventArgs e) /// private void CheckBox3_CheckedChanged(object sender, EventArgs e) { - if (!checkBox3.Checked) + if (!checkBox3_切换后多余字母.Checked) return; - checkBox7.Checked = false; + checkBox7_切换后首字母.Checked = false; } /// /// Ctrl+空格 @@ -129,12 +129,12 @@ private void CheckBox3_CheckedChanged(object sender, EventArgs e) /// private void CheckBox4_CheckedChanged(object sender, EventArgs e) { - if (!checkBox4.Checked) + if (!checkBox4_ctrlAddSpace.Checked) return; - checkBox1.Checked = - checkBox2.Checked = - checkBox5.Checked = - checkBox6.Checked = false; + checkBox1_shift.Checked = + checkBox2_ctrl.Checked = + checkBox5_ctrlAndShift.Checked = + checkBox6_winAndSpace.Checked = false; } /// /// Ctrl+Shift @@ -143,12 +143,12 @@ private void CheckBox4_CheckedChanged(object sender, EventArgs e) /// private void CheckBox5_CheckedChanged(object sender, EventArgs e) { - if (!checkBox5.Checked) + if (!checkBox5_ctrlAndShift.Checked) return; - checkBox1.Checked = - checkBox2.Checked = - checkBox4.Checked = - checkBox6.Checked = false; + checkBox1_shift.Checked = + checkBox2_ctrl.Checked = + checkBox4_ctrlAddSpace.Checked = + checkBox6_winAndSpace.Checked = false; } /// /// Win+空格 @@ -157,12 +157,12 @@ private void CheckBox5_CheckedChanged(object sender, EventArgs e) /// private void CheckBox6_CheckedChanged(object sender, EventArgs e) { - if (!checkBox6.Checked) + if (!checkBox6_winAndSpace.Checked) return; - checkBox1.Checked = - checkBox2.Checked = - checkBox4.Checked = - checkBox5.Checked = false; + checkBox1_shift.Checked = + checkBox2_ctrl.Checked = + checkBox4_ctrlAddSpace.Checked = + checkBox5_ctrlAndShift.Checked = false; } /// /// 解决切换后需多按一次命令首字母 @@ -171,8 +171,8 @@ private void CheckBox6_CheckedChanged(object sender, EventArgs e) /// private void CheckBox7_CheckedChanged(object sender, EventArgs e) { - if (!checkBox7.Checked) + if (!checkBox7_切换后首字母.Checked) return; - checkBox3.Checked = false; + checkBox3_切换后多余字母.Checked = false; } } \ No newline at end of file -- Gitee From d872cd3f207c226199a4f45e5fd5b00652770098 Mon Sep 17 00:00:00 2001 From: liuqihong <540762622@qq.com> Date: Tue, 1 Nov 2022 20:41:16 +0800 Subject: [PATCH 603/675] =?UTF-8?q?=E8=BE=93=E5=85=A5=E6=B3=95=E6=B7=BB?= =?UTF-8?q?=E5=8A=A0=E6=8B=AC=E5=85=8D=E5=91=BD=E4=BB=A4=E5=88=87=E6=8D=A2?= =?UTF-8?q?=E5=88=B0=E4=B8=AD=E6=96=87?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/IFoxCAD.Basal/General/LoopState.cs | 14 ++- .../Cmd.cs" | 37 +++++- .../IMEControl.cs" | 119 ++++++++++++++++-- .../Settings.cs" | 86 +++++++++---- .../Window/SettingsWindow.xaml.cs" | 2 +- 5 files changed, 215 insertions(+), 43 deletions(-) diff --git a/src/IFoxCAD.Basal/General/LoopState.cs b/src/IFoxCAD.Basal/General/LoopState.cs index bc9f5f5..50a6912 100644 --- a/src/IFoxCAD.Basal/General/LoopState.cs +++ b/src/IFoxCAD.Basal/General/LoopState.cs @@ -15,12 +15,18 @@ public class LoopState private volatile int _LoopStateFlags = PLS_NONE; public bool IsRun => _LoopStateFlags == PLS_NONE; - public bool IsCancel => _LoopStateFlags == PLS_CANCELED; - public bool IsExceptional => _LoopStateFlags == PLS_EXCEPTIONAL; - + public bool IsExceptional => (_LoopStateFlags & PLS_EXCEPTIONAL) == PLS_EXCEPTIONAL; public bool IsBreak => (_LoopStateFlags & PLS_BROKEN) == PLS_BROKEN; public bool IsStop => (_LoopStateFlags & PLS_STOPPED) == PLS_STOPPED; - public void Stop() => _LoopStateFlags = PLS_STOPPED; + public bool IsCancel => (_LoopStateFlags & PLS_CANCELED) == PLS_CANCELED; + + public void Exceptional() + { + if ((_LoopStateFlags & PLS_EXCEPTIONAL) != PLS_EXCEPTIONAL) + _LoopStateFlags |= PLS_EXCEPTIONAL; + } public void Break() => _LoopStateFlags = PLS_BROKEN; + public void Stop() => _LoopStateFlags = PLS_STOPPED; + public void Cancel() => _LoopStateFlags = PLS_CANCELED; } #line default \ No newline at end of file diff --git "a/tests/TestAcad09plus/\350\276\223\345\205\245\346\263\225\350\207\252\345\212\250\345\210\207\346\215\242/\350\265\253\346\200\235\350\276\223\345\205\245\346\263\225/Cmd.cs" "b/tests/TestAcad09plus/\350\276\223\345\205\245\346\263\225\350\207\252\345\212\250\345\210\207\346\215\242/\350\265\253\346\200\235\350\276\223\345\205\245\346\263\225/Cmd.cs" index 2845f79..52e97d4 100644 --- "a/tests/TestAcad09plus/\350\276\223\345\205\245\346\263\225\350\207\252\345\212\250\345\210\207\346\215\242/\350\265\253\346\200\235\350\276\223\345\205\245\346\263\225/Cmd.cs" +++ "b/tests/TestAcad09plus/\350\276\223\345\205\245\346\263\225\350\207\252\345\212\250\345\210\207\346\215\242/\350\265\253\346\200\235\350\276\223\345\205\245\346\263\225/Cmd.cs" @@ -1,11 +1,16 @@ -namespace Gstar_IMEFilter; +using System.Diagnostics; + +namespace Gstar_IMEFilter; public class Cmd { [IFoxInitialize] public void Initialize() { - Env.Printl($"※输入法控制※\n{nameof(Gstar_IMEFilter)} - 切换开关\n" + nameof(Gstar_IMEFilterSettings) + " - 设置\n"); + Env.Printl($"※输入法控制※" + + $"\n{nameof(Gstar_IMEFilter1)} - 切换开关" + + $"\n{nameof(Gstar_IMEFilter2)} - 控制模式" + + $"\n{nameof(Gstar_IMEFilterSettings)} - 设置"); DocReactor.IntialReactor(); Settings.LoadSettings(); IMEControl.SetIMEHook(); @@ -14,13 +19,37 @@ public void Initialize() }; } - [CommandMethod(nameof(Gstar_IMEFilter))] - public void Gstar_IMEFilter() + + + [CommandMethod(nameof(Gstar_IMEFilter1))] + public void Gstar_IMEFilter1() { Settings.Use = !Settings.Use; Env.Printl("已经 " + (Settings.Use ? "开启" : "禁用") + " 输入法+"); } + [CommandMethod(nameof(Gstar_IMEFilter2))] + public void Gstar_IMEFilter2() + { + // 存在重复的关键字首字母,所以用数字代替 + PromptIntegerOptions options = new($"\n输入法切换模式:"); + foreach (var suit in Enum.GetValues(typeof(IMESwitchMode))) + options.Keywords.Add($"{suit}", $"{suit}", $"{suit}({(int)suit})", true, true); + options.Keywords.Default = Settings.IMEInputSwitch.ToString(); + var result = Env.Editor.GetInteger(options); + + if (result.Status != PromptStatus.OK) + return; + var max = Enum.GetValues(typeof(IMESwitchMode)).Cast().Max(); + if (result.Value > max) + { + Env.Printl("设置失败:数值过大"); + return; + } + Settings.IMEInputSwitch = (IMESwitchMode)result.Value; + Env.Printl($"设置为: {Settings.IMEInputSwitch}"); + } + [CommandMethod(nameof(Gstar_IMEFilterSettings))] public void Gstar_IMEFilterSettings() { diff --git "a/tests/TestAcad09plus/\350\276\223\345\205\245\346\263\225\350\207\252\345\212\250\345\210\207\346\215\242/\350\265\253\346\200\235\350\276\223\345\205\245\346\263\225/IMEControl.cs" "b/tests/TestAcad09plus/\350\276\223\345\205\245\346\263\225\350\207\252\345\212\250\345\210\207\346\215\242/\350\265\253\346\200\235\350\276\223\345\205\245\346\263\225/IMEControl.cs" index 6995567..96bd899 100644 --- "a/tests/TestAcad09plus/\350\276\223\345\205\245\346\263\225\350\207\252\345\212\250\345\210\207\346\215\242/\350\265\253\346\200\235\350\276\223\345\205\245\346\263\225/IMEControl.cs" +++ "b/tests/TestAcad09plus/\350\276\223\345\205\245\346\263\225\350\207\252\345\212\250\345\210\207\346\215\242/\350\265\253\346\200\235\350\276\223\345\205\245\346\263\225/IMEControl.cs" @@ -3,23 +3,26 @@ using System.Diagnostics; using System.Windows.Forms; + + public class IMEControl { internal static HashSet DefaultCMDs; internal static HashSet ExceptCMDs; + static readonly Regex CMDReg = new("\\(C:.*\\)"); /*某些窗口没有 WM_KEYDOWN 消息,就只有 WM_KEYUP 消息*/ const int WM_KEYDOWN = 256; const int WM_KEYUP = 257; - //const int WM_CHAR = 258; - //const int WM_KILLFOCUS = 8; internal static WindowsAPI.CallBackX86? HookProcX86; internal static WindowsAPI.CallBackX64? HookProcX64; internal static WindowsAPI.CallBack? HookProc; internal static IntPtr _NextHookProc;//挂载成功的标记 internal static Process _Process; - internal static int AcadPID => _Process.Id; + + // 优化内存,减少消息循环时候,频繁创建此类 + static StringBuilder _lpClassName = new(byte.MaxValue); static IMEControl() { @@ -42,20 +45,41 @@ static IMEControl() for (int i = 0; i < ls.Length; i++) DefaultCMDs.Add(ls[i]); } + + Acap.DocumentManager.DocumentLockModeChanged += DocumentManager_DocumentLockModeChanged; + } + + // TODO 关键字是中文输入,自动切换到英文 CommandLineMonitor 可以获取关键字 + + // 此状态用于括免命令图中自动切换到中文, + // 命令中依然能够切换到英文输入, + // 命令结束时候从否决事件恢复拦截 + public static LoopState SendKeyState = new(); + static void DocumentManager_DocumentLockModeChanged(object sender, DocumentLockModeChangedEventArgs e) + { + if (e.GlobalCommandName == "#") + return; + if (!e.GlobalCommandName.StartsWith("#")) + return; + var up = e.GlobalCommandName.ToUpper()[1..]; + + if (ExceptCMDs.Contains(up)) + SendKeyState = new(); } - // 优化内存,减少消息循环时候,频繁创建此类 - static StringBuilder _lpClassName = new(byte.MaxValue); /// /// 钩子的消息处理 - /// + /// r /// /// /// /// false不终止回调,true终止回调 public static bool IMEHook(int nCode, int wParam, int lParam) { + if (SendKeyState.IsExceptional) + return false; + var dm = Acap.DocumentManager; if (dm.Count == 0) return false; @@ -70,8 +94,80 @@ public static bool IMEHook(int nCode, int wParam, int lParam) Match match = CMDReg.Match(input); if (match.Success) input = input.Substring(checked(match.Index + 3), checked(match.Length - 4)); + if (ExceptCMDs.Contains(input.ToUpper())) + { + // 检测是否中文状态,不是就切为中文状态 + // 切换只能发生在第一次,第2+次需要不执行 + var focusW = WindowsAPI.GetForegroundWindow(); + var context = WindowsAPI.ImmGetContext(focusW); + WindowsAPI.ImmGetConversionStatus(context, out int mode/*输入模式*/, out _); + if (!WindowsAPI.ImmGetOpenStatus(context)) + { + Debug.WriteLine("现在是英文,切换到中文"); + SendKeyState.Exceptional(); + switch (Settings.IMEInputSwitch) + { + case IMESwitchMode.Shift: + { + WindowsAPI.KeybdEvent(16, 0, 0, 0); + WindowsAPI.KeybdEvent(16, 0, 2, 0); + } + break; + case IMESwitchMode.Ctrl: + { + WindowsAPI.KeybdEvent(17, 0, 0, 0); + WindowsAPI.KeybdEvent(17, 0, 2, 0); + } + break; + case IMESwitchMode.CtrlAndSpace: + { + WindowsAPI.KeybdEvent(17, 0, 0, 0); + WindowsAPI.KeybdEvent(32, 0, 0, 0); + WindowsAPI.KeybdEvent(32, 0, 2, 0); + WindowsAPI.KeybdEvent(17, 0, 2, 0); + if (WindowsAPI.GetKeyState(20) == 1) + { + WindowsAPI.KeybdEvent(20, 0, 0, 0); + WindowsAPI.KeybdEvent(20, 0, 2, 0); + } + } + break; + case IMESwitchMode.CtrlAndShift: + { + WindowsAPI.KeybdEvent(16, 0, 0, 0); + WindowsAPI.KeybdEvent(17, 0, 0, 0); + WindowsAPI.KeybdEvent(17, 0, 2, 0); + WindowsAPI.KeybdEvent(16, 0, 2, 0); + if (WindowsAPI.GetKeyState(20) == 1) + { + WindowsAPI.KeybdEvent(20, 0, 0, 0); + WindowsAPI.KeybdEvent(20, 0, 2, 0); + } + } + break; + case IMESwitchMode.WinAndSpace: + { + WindowsAPI.KeybdEvent(91, 0, 0, 0); + WindowsAPI.KeybdEvent(32, 0, 0, 0); + WindowsAPI.KeybdEvent(32, 0, 2, 0); + WindowsAPI.KeybdEvent(91, 0, 2, 0); + if (WindowsAPI.GetKeyState(20) == 1) + { + WindowsAPI.KeybdEvent(20, 0, 0, 0); + WindowsAPI.KeybdEvent(20, 0, 2, 0); + } + } + break; + } + } + else + { + Debug.WriteLine("现在是中文,保持不变,在命令否决事件上面恢复拦截"); + SendKeyState.Exceptional(); + } return false; + } // 键盘按键值 if ((65 <= wParam && wParam <= 90/*a~z*/) || @@ -104,14 +200,14 @@ public static bool IMEHook(int nCode, int wParam, int lParam) Debug.WriteLine($"afx::{DateTime.Now}"); { - // cad启动时候会滚动某些信息,此时立马鼠标点入到vs中,此时vs无法输入, - // 被拦截到了"afx"处理,然后所有的输入都跑cad了,需要加入如下代码 + // cad启动时候会滚动某些信息,此时立马鼠标点入到vs中, + // 此时vs无法输入,被拦截到了"afx"处理,然后所有的输入都跑cad了,需要加入如下代码: var focusW = WindowsAPI.GetForegroundWindow(); WindowsAPI.GetClassName(focusW, _lpClassName, checked(_lpClassName.Capacity + 1)); left = _lpClassName.ToString().ToLower(); // 点入vs是 hwndwrapper // 点入qq是 txguifoundation - Debug.WriteLine($"{left}.....{DateTime.Now}"); + Debug.WriteLine($"afx...{left}...{DateTime.Now}"); if (!left.StartsWith("afx")) return false; } @@ -156,7 +252,6 @@ public static bool IMEHook(int nCode, int wParam, int lParam) if (left == "cicerouiwndframe") { Debug.WriteLine($"cicerouiwndframe::{DateTime.Now}"); - //Debug.WriteLine("cicerouiwndframe"); return true; } } @@ -185,7 +280,7 @@ internal static void SetIMEHook() foreach (var item in ss) ExceptCMDs.Add(item); - if (Settings.IMEStyle != Settings.IMEStyleS.Global) + if (Settings.IMEStyle != IMEHookStyle.Global) { HookProc = (nCode, wParam, lParam) => { if (nCode >= 0) @@ -242,7 +337,7 @@ static bool Mk2(int nCode, int wParam, IntPtr lParam) focus = WindowsAPI.GetForegroundWindow(); WindowsAPI.GetWindowThreadProcessId(focus, out uint lpdwProcessId); - if (lpdwProcessId != AcadPID) + if (lpdwProcessId != _Process.Id) return false; KeyboardHookStruct? key = null; diff --git "a/tests/TestAcad09plus/\350\276\223\345\205\245\346\263\225\350\207\252\345\212\250\345\210\207\346\215\242/\350\265\253\346\200\235\350\276\223\345\205\245\346\263\225/Settings.cs" "b/tests/TestAcad09plus/\350\276\223\345\205\245\346\263\225\350\207\252\345\212\250\345\210\207\346\215\242/\350\265\253\346\200\235\350\276\223\345\205\245\346\263\225/Settings.cs" index 3312625..ed655b3 100644 --- "a/tests/TestAcad09plus/\350\276\223\345\205\245\346\263\225\350\207\252\345\212\250\345\210\207\346\215\242/\350\265\253\346\200\235\350\276\223\345\205\245\346\263\225/Settings.cs" +++ "b/tests/TestAcad09plus/\350\276\223\345\205\245\346\263\225\350\207\252\345\212\250\345\210\207\346\215\242/\350\265\253\346\200\235\350\276\223\345\205\245\346\263\225/Settings.cs" @@ -1,14 +1,14 @@ using System.Xml; namespace Gstar_IMEFilter; - public class Settings { static string _MyDir = ""; static string _MySettingsPath = ""; static bool _Use = true; internal static string _UserFilter = ""; - internal static IMEStyleS _IMEStyle = IMEStyleS.Global; + internal static IMEHookStyle _IMEStyle = IMEHookStyle.Global; + internal static IMESwitchMode _IMEInputSwitch = IMESwitchMode.Shift; internal static string MyDir { @@ -55,7 +55,7 @@ public static bool Use } } - public static IMEStyleS IMEStyle + public static IMEHookStyle IMEStyle { get => _IMEStyle; set @@ -68,6 +68,18 @@ public static IMEStyleS IMEStyle } } + public static IMESwitchMode IMEInputSwitch + { + get => _IMEInputSwitch; + set + { + if (_IMEInputSwitch == value) + return; + _IMEInputSwitch = value; + SaveSettings(); + } + } + public static void LoadSettings() { if (!File.Exists(MySettingsPath)) @@ -82,15 +94,25 @@ public static void LoadSettings() switch (left) { case "use": - bool.TryParse(xmlReader.ReadInnerXml(), out _Use); + { + bool.TryParse(xmlReader.ReadInnerXml(), out _Use); + } break; case "userfilter": - _UserFilter = xmlReader.ReadInnerXml().ToUpper(); + { + _UserFilter = xmlReader.ReadInnerXml().ToUpper(); + } break; case "imestyle": { - int.TryParse(xmlReader.ReadInnerXml(), out int imes); - _IMEStyle = (IMEStyleS)imes; + int.TryParse(xmlReader.ReadInnerXml(), out int ime); + _IMEStyle = (IMEHookStyle)ime; + } + break; + case "imeinputswitch": + { + int.TryParse(xmlReader.ReadInnerXml(), out int ime); + _IMEInputSwitch = (IMESwitchMode)ime; } break; } @@ -112,20 +134,25 @@ internal static void SaveSettings() xmlWriter.WriteComment("输入法+"); xmlWriter.WriteStartElement("settings"); - xmlWriter.WriteStartElement("use"); - xmlWriter.WriteString(Use.ToString()); - xmlWriter.WriteEndElement(); + { + xmlWriter.WriteStartElement("use"); + xmlWriter.WriteString(Use.ToString()); + xmlWriter.WriteEndElement(); - xmlWriter.WriteStartElement("userfilter"); - xmlWriter.WriteString(UserFilter); - xmlWriter.WriteEndElement(); - xmlWriter.WriteStartElement("imestyle"); + xmlWriter.WriteStartElement("userfilter"); + xmlWriter.WriteString(UserFilter); + xmlWriter.WriteEndElement(); - xmlWriter.WriteString(((int)IMEStyle).ToString()); - xmlWriter.WriteEndElement(); + xmlWriter.WriteStartElement("imestyle"); + xmlWriter.WriteString(((int)IMEStyle).ToString()); + xmlWriter.WriteEndElement(); + + xmlWriter.WriteStartElement("imeinputswitch"); + xmlWriter.WriteString(((int)IMEInputSwitch).ToString()); + xmlWriter.WriteEndElement(); + } xmlWriter.WriteEndElement(); xmlWriter.WriteEndDocument(); - xmlWriter.Close(); } catch (Exception ex) @@ -133,10 +160,25 @@ internal static void SaveSettings() throw ex; } } +} - public enum IMEStyleS - { - Global, - Process, - } +/// +/// 钩子样式 +/// +public enum IMEHookStyle : int +{ + Global,//全局钩子控制 + Process,//进程钩子控制 +} + +/// +/// 切换输入法方式 +/// +public enum IMESwitchMode : int +{ + Shift, + Ctrl, + CtrlAndSpace, + CtrlAndShift, + WinAndSpace, } \ No newline at end of file diff --git "a/tests/TestAcad09plus/\350\276\223\345\205\245\346\263\225\350\207\252\345\212\250\345\210\207\346\215\242/\350\265\253\346\200\235\350\276\223\345\205\245\346\263\225/Window/SettingsWindow.xaml.cs" "b/tests/TestAcad09plus/\350\276\223\345\205\245\346\263\225\350\207\252\345\212\250\345\210\207\346\215\242/\350\265\253\346\200\235\350\276\223\345\205\245\346\263\225/Window/SettingsWindow.xaml.cs" index fab2e05..7fb0580 100644 --- "a/tests/TestAcad09plus/\350\276\223\345\205\245\346\263\225\350\207\252\345\212\250\345\210\207\346\215\242/\350\265\253\346\200\235\350\276\223\345\205\245\346\263\225/Window/SettingsWindow.xaml.cs" +++ "b/tests/TestAcad09plus/\350\276\223\345\205\245\346\263\225\350\207\252\345\212\250\345\210\207\346\215\242/\350\265\253\346\200\235\350\276\223\345\205\245\346\263\225/Window/SettingsWindow.xaml.cs" @@ -18,7 +18,7 @@ public SettingsWindow() private void Button_Click(object sender, RoutedEventArgs e) { Settings._UserFilter = ExCMD.Text; - Settings._IMEStyle = (Settings.IMEStyleS)CBox.SelectedIndex; + Settings._IMEStyle = (IMEHookStyle)CBox.SelectedIndex; Settings.SaveSettings(); IMEControl.SetIMEHook(); DialogResult = true; -- Gitee From 86100dba5ee0f3bccd1bbd4506c5b65d143f6b87 Mon Sep 17 00:00:00 2001 From: liuqihong <540762622@qq.com> Date: Wed, 2 Nov 2022 15:04:27 +0800 Subject: [PATCH 604/675] =?UTF-8?q?=E4=BC=98=E5=8C=96=E4=BB=A3=E7=A0=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/IFoxCAD.Cad.Shared/Copyclip/WindowsAPI.cs | 38 ++ .../IMEControl.cs" | 342 +++++++++--------- 2 files changed, 202 insertions(+), 178 deletions(-) diff --git a/src/IFoxCAD.Cad.Shared/Copyclip/WindowsAPI.cs b/src/IFoxCAD.Cad.Shared/Copyclip/WindowsAPI.cs index 3f1baa1..2b77e10 100644 --- a/src/IFoxCAD.Cad.Shared/Copyclip/WindowsAPI.cs +++ b/src/IFoxCAD.Cad.Shared/Copyclip/WindowsAPI.cs @@ -468,6 +468,44 @@ public enum HookType : int WH_KEYBOARD_LL = 13, WH_MOUSE_LL = 14, } + /// + /// Hook键盘数据结构 + /// + [ComVisible(true)] + [Serializable] + //[DebuggerDisplay("{DebuggerDisplay,nq}")] + //[DebuggerTypeProxy(typeof(KeyboardHookStruct))] + [StructLayout(LayoutKind.Sequential)] + public struct KeyboardHookStruct + { + public int VkCode; // 键码,该代码必须有一个价值的范围1至254 + public int ScanCode; // 指定的硬件扫描码的关键 + public int Flags; // 键标志 + public int Time; // 指定的时间戳记的这个讯息 + public int DwExtraInfo; // 指定额外信息相关的信息 + + public static KeyboardHookStruct Create(IntPtr lParam) + { + return (KeyboardHookStruct)Marshal.PtrToStructure(lParam, typeof(KeyboardHookStruct)); + } + + public void ToPtr(IntPtr lParam) + { + Marshal.StructureToPtr(this, lParam, true); + } + } + /// + /// 注册表增加低级钩子超时处理,防止系统不允许, + /// 否则:偶发性出现 键盘钩子不能用了,而且退出时产生 1404 错误 + /// https://www.cnblogs.com/songr/p/5131655.html + /// + public static void CheckLowLevelHooksTimeout() + { + const string llh = "LowLevelHooksTimeout"; + using var registryKey = Registry.CurrentUser.OpenSubKey("Control Panel\\Desktop", true); + if ((int)registryKey.GetValue(llh, 0) == 0) + registryKey.SetValue(llh, 25000, RegistryValueKind.DWord); + } #endregion [DllImport("user32.dll")] diff --git "a/tests/TestAcad09plus/\350\276\223\345\205\245\346\263\225\350\207\252\345\212\250\345\210\207\346\215\242/\350\265\253\346\200\235\350\276\223\345\205\245\346\263\225/IMEControl.cs" "b/tests/TestAcad09plus/\350\276\223\345\205\245\346\263\225\350\207\252\345\212\250\345\210\207\346\215\242/\350\265\253\346\200\235\350\276\223\345\205\245\346\263\225/IMEControl.cs" index 96bd899..a1eb1dd 100644 --- "a/tests/TestAcad09plus/\350\276\223\345\205\245\346\263\225\350\207\252\345\212\250\345\210\207\346\215\242/\350\265\253\346\200\235\350\276\223\345\205\245\346\263\225/IMEControl.cs" +++ "b/tests/TestAcad09plus/\350\276\223\345\205\245\346\263\225\350\207\252\345\212\250\345\210\207\346\215\242/\350\265\253\346\200\235\350\276\223\345\205\245\346\263\225/IMEControl.cs" @@ -1,9 +1,9 @@ namespace Gstar_IMEFilter; using System.Diagnostics; +using System.Windows.Controls; using System.Windows.Forms; - - +using Control = System.Windows.Forms.Control; public class IMEControl { @@ -23,25 +23,28 @@ public class IMEControl // 优化内存,减少消息循环时候,频繁创建此类 static StringBuilder _lpClassName = new(byte.MaxValue); + static string[] _separator = new string[] { "," }; static IMEControl() { _NextHookProc = IntPtr.Zero; _Process = Process.GetCurrentProcess(); - CheckLowLevelHooksTimeout(); + WindowsAPI.CheckLowLevelHooksTimeout(); ExceptCMDs = new(); DefaultCMDs = new() { "MTEXT", "DDEDIT", "MTEDIT", "TABLEDIT", "MLEADER", "QLEADER", "MLEADERCONTENTEDIT", "MLEADEREDIT", "TEXTEDIT", "TEXT", "QLEADER" }; + string? lines = null; var ftFile = Path.Combine(Settings.MyDir, "ExceptCMDs.ft"); if (File.Exists(ftFile)) lines = File.ReadAllText(ftFile, Encoding.UTF8); else Debug.WriteLine("配置文件丢失: " + ftFile); + if (lines != null) { - var ls = lines.Split(new string[] { "," }, StringSplitOptions.RemoveEmptyEntries); + var ls = lines.Split(_separator, StringSplitOptions.RemoveEmptyEntries); for (int i = 0; i < ls.Length; i++) DefaultCMDs.Add(ls[i]); } @@ -49,11 +52,16 @@ static IMEControl() Acap.DocumentManager.DocumentLockModeChanged += DocumentManager_DocumentLockModeChanged; } - // TODO 关键字是中文输入,自动切换到英文 CommandLineMonitor 可以获取关键字 + #region 切换输入法 + // TODO 关键字是中文输入,自动切换到英文 + // 命令行监控服务 实例 得到命令行监控 + // var commandLineMonitor = CommandLineMonitorServices.Instance().GetCommandLineMonitor(doc); - // 此状态用于括免命令图中自动切换到中文, - // 命令中依然能够切换到英文输入, - // 命令结束时候从否决事件恢复拦截 + /// + /// 此状态用于括免命令图中自动切换到中文,
    + /// 命令中依然能够切换到英文输入,
    + /// 命令结束时候从否决事件恢复拦截
    + ///
    public static LoopState SendKeyState = new(); static void DocumentManager_DocumentLockModeChanged(object sender, DocumentLockModeChangedEventArgs e) { @@ -62,11 +70,145 @@ static void DocumentManager_DocumentLockModeChanged(object sender, DocumentLockM if (!e.GlobalCommandName.StartsWith("#")) return; var up = e.GlobalCommandName.ToUpper()[1..]; - if (ExceptCMDs.Contains(up)) SendKeyState = new(); } + /// + /// 切换输入法 + /// + static void IMESwitch() + { + // 检测是否中文状态,不是就切为中文状态 + // 切换只能发生在第一次,第2+次需要不执行 + var focusW = WindowsAPI.GetForegroundWindow(); + var context = WindowsAPI.ImmGetContext(focusW); + WindowsAPI.ImmGetConversionStatus(context, out int mode/*输入模式*/, out _); + if (!WindowsAPI.ImmGetOpenStatus(context)) + { + Debug.WriteLine("现在是英文状态,切换到中文"); + SendKeyState.Exceptional(); + + switch (Settings.IMEInputSwitch) + { + case IMESwitchMode.Shift: + { + WindowsAPI.KeybdEvent(16, 0, 0, 0); + WindowsAPI.KeybdEvent(16, 0, 2, 0); + } + break; + case IMESwitchMode.Ctrl: + { + WindowsAPI.KeybdEvent(17, 0, 0, 0); + WindowsAPI.KeybdEvent(17, 0, 2, 0); + } + break; + case IMESwitchMode.CtrlAndSpace: + { + WindowsAPI.KeybdEvent(17, 0, 0, 0); + WindowsAPI.KeybdEvent(32, 0, 0, 0); + WindowsAPI.KeybdEvent(32, 0, 2, 0); + WindowsAPI.KeybdEvent(17, 0, 2, 0); + if (WindowsAPI.GetKeyState(20) == 1) + { + WindowsAPI.KeybdEvent(20, 0, 0, 0); + WindowsAPI.KeybdEvent(20, 0, 2, 0); + } + } + break; + case IMESwitchMode.CtrlAndShift: + { + WindowsAPI.KeybdEvent(16, 0, 0, 0); + WindowsAPI.KeybdEvent(17, 0, 0, 0); + WindowsAPI.KeybdEvent(17, 0, 2, 0); + WindowsAPI.KeybdEvent(16, 0, 2, 0); + if (WindowsAPI.GetKeyState(20) == 1) + { + WindowsAPI.KeybdEvent(20, 0, 0, 0); + WindowsAPI.KeybdEvent(20, 0, 2, 0); + } + } + break; + case IMESwitchMode.WinAndSpace: + { + WindowsAPI.KeybdEvent(91, 0, 0, 0); + WindowsAPI.KeybdEvent(32, 0, 0, 0); + WindowsAPI.KeybdEvent(32, 0, 2, 0); + WindowsAPI.KeybdEvent(91, 0, 2, 0); + if (WindowsAPI.GetKeyState(20) == 1) + { + WindowsAPI.KeybdEvent(20, 0, 0, 0); + WindowsAPI.KeybdEvent(20, 0, 2, 0); + } + } + break; + } + } + else + { + // 中文状态虽然不变, + // 但是为了避免命令中用户手动切换 中文切换到英文,然后再触发上面 英文状态转中文逻辑, + // 所以此处也要设置状态 + // 在命令否决事件上面恢复拦截 + Debug.WriteLine("现在是中文状态,保持不变"); + SendKeyState.Exceptional(); + } + } + #endregion + + /// + /// 设置钩子 + /// + internal static void SetIMEHook() + { + UnIMEHook(); + if (_NextHookProc != IntPtr.Zero || !Settings.Use) + return; + + ExceptCMDs.Clear(); + foreach (var item in DefaultCMDs) + ExceptCMDs.Add(item); + var ss = Settings.UserFilter.Split(_separator, StringSplitOptions.RemoveEmptyEntries); + foreach (var item in ss) + ExceptCMDs.Add(item); + + if (Settings.IMEStyle != IMEHookStyle.Global) + { + HookProc = (nCode, wParam, lParam) => { + if (nCode >= 0) + { + bool flag = !((lParam > 0) & ((lParam & -1073741823) == 1)) || + (Control.ModifierKeys != Keys.None && Control.ModifierKeys != Keys.Shift) || + !IMEHook(nCode, wParam, lParam); + if (!flag) + return (IntPtr)1; + } + return WindowsAPI.CallNextHookEx(_NextHookProc, nCode, wParam, lParam); + }; + _NextHookProc = WindowsAPI.SetWindowsHookEx(WindowsAPI.HookType.WH_KEYBOARD, HookProc, 0, WindowsAPI.GetCurrentThreadId()); + return; + } + + var moduleHandle = (long)WindowsAPI.GetModuleHandle(_Process.MainModule.ModuleName); + if (Marshal.SizeOf(typeof(IntPtr)) == 4) + { + HookProcX86 = (nCode, wParam, lParam) => { + if (!MK1(nCode, wParam) && Mk2(nCode, wParam, new IntPtr(lParam))) + return (IntPtr)1; + return WindowsAPI.CallNextHookEx(_NextHookProc, nCode, wParam, lParam); + }; + _NextHookProc = WindowsAPI.SetWindowsHookEx(WindowsAPI.HookType.WH_KEYBOARD_LL, HookProcX86, moduleHandle, 0); + } + else + { + HookProcX64 = (nCode, wParam, lParam) => { + if (!MK1(nCode, wParam) && Mk2(nCode, wParam, new IntPtr(lParam))) + return (IntPtr)1; + return WindowsAPI.CallNextHookEx(_NextHookProc, nCode, wParam, lParam); + }; + _NextHookProc = WindowsAPI.SetWindowsHookEx(WindowsAPI.HookType.WH_KEYBOARD_LL, HookProcX64, moduleHandle, 0); + } + } /// /// 钩子的消息处理 @@ -97,75 +239,7 @@ public static bool IMEHook(int nCode, int wParam, int lParam) if (ExceptCMDs.Contains(input.ToUpper())) { - // 检测是否中文状态,不是就切为中文状态 - // 切换只能发生在第一次,第2+次需要不执行 - var focusW = WindowsAPI.GetForegroundWindow(); - var context = WindowsAPI.ImmGetContext(focusW); - WindowsAPI.ImmGetConversionStatus(context, out int mode/*输入模式*/, out _); - if (!WindowsAPI.ImmGetOpenStatus(context)) - { - Debug.WriteLine("现在是英文,切换到中文"); - SendKeyState.Exceptional(); - switch (Settings.IMEInputSwitch) - { - case IMESwitchMode.Shift: - { - WindowsAPI.KeybdEvent(16, 0, 0, 0); - WindowsAPI.KeybdEvent(16, 0, 2, 0); - } - break; - case IMESwitchMode.Ctrl: - { - WindowsAPI.KeybdEvent(17, 0, 0, 0); - WindowsAPI.KeybdEvent(17, 0, 2, 0); - } - break; - case IMESwitchMode.CtrlAndSpace: - { - WindowsAPI.KeybdEvent(17, 0, 0, 0); - WindowsAPI.KeybdEvent(32, 0, 0, 0); - WindowsAPI.KeybdEvent(32, 0, 2, 0); - WindowsAPI.KeybdEvent(17, 0, 2, 0); - if (WindowsAPI.GetKeyState(20) == 1) - { - WindowsAPI.KeybdEvent(20, 0, 0, 0); - WindowsAPI.KeybdEvent(20, 0, 2, 0); - } - } - break; - case IMESwitchMode.CtrlAndShift: - { - WindowsAPI.KeybdEvent(16, 0, 0, 0); - WindowsAPI.KeybdEvent(17, 0, 0, 0); - WindowsAPI.KeybdEvent(17, 0, 2, 0); - WindowsAPI.KeybdEvent(16, 0, 2, 0); - if (WindowsAPI.GetKeyState(20) == 1) - { - WindowsAPI.KeybdEvent(20, 0, 0, 0); - WindowsAPI.KeybdEvent(20, 0, 2, 0); - } - } - break; - case IMESwitchMode.WinAndSpace: - { - WindowsAPI.KeybdEvent(91, 0, 0, 0); - WindowsAPI.KeybdEvent(32, 0, 0, 0); - WindowsAPI.KeybdEvent(32, 0, 2, 0); - WindowsAPI.KeybdEvent(91, 0, 2, 0); - if (WindowsAPI.GetKeyState(20) == 1) - { - WindowsAPI.KeybdEvent(20, 0, 0, 0); - WindowsAPI.KeybdEvent(20, 0, 2, 0); - } - } - break; - } - } - else - { - Debug.WriteLine("现在是中文,保持不变,在命令否决事件上面恢复拦截"); - SendKeyState.Exceptional(); - } + IMESwitch(); return false; } @@ -200,13 +274,13 @@ public static bool IMEHook(int nCode, int wParam, int lParam) Debug.WriteLine($"afx::{DateTime.Now}"); { - // cad启动时候会滚动某些信息,此时立马鼠标点入到vs中, - // 此时vs无法输入,被拦截到了"afx"处理,然后所有的输入都跑cad了,需要加入如下代码: + // cad08启动时候会滚动某些信息,此时立马鼠标狂点入到vs中,vs就会无法输入 + // 会被拦截到了"afx"处理,然后所有的输入都跑cad了,需要加入如下代码: var focusW = WindowsAPI.GetForegroundWindow(); WindowsAPI.GetClassName(focusW, _lpClassName, checked(_lpClassName.Capacity + 1)); left = _lpClassName.ToString().ToLower(); - // 点入vs是 hwndwrapper - // 点入qq是 txguifoundation + // 狂点入vs是 hwndwrapper + // 狂点入qq是 txguifoundation Debug.WriteLine($"afx...{left}...{DateTime.Now}"); if (!left.StartsWith("afx")) return false; @@ -258,66 +332,6 @@ public static bool IMEHook(int nCode, int wParam, int lParam) return false; } - internal static void UnIMEHook() - { - if (_NextHookProc != IntPtr.Zero) - { - WindowsAPI.UnhookWindowsHookEx(_NextHookProc); - _NextHookProc = IntPtr.Zero; - } - } - - internal static void SetIMEHook() - { - UnIMEHook(); - if (_NextHookProc != IntPtr.Zero || !Settings.Use) - return; - - ExceptCMDs.Clear(); - foreach (var item in DefaultCMDs) - ExceptCMDs.Add(item); - var ss = Settings.UserFilter.Split(new string[] { "," }, StringSplitOptions.RemoveEmptyEntries); - foreach (var item in ss) - ExceptCMDs.Add(item); - - if (Settings.IMEStyle != IMEHookStyle.Global) - { - HookProc = (nCode, wParam, lParam) => { - if (nCode >= 0) - { - bool flag = !((lParam > 0) & ((lParam & -1073741823) == 1)) || - (Control.ModifierKeys != Keys.None && Control.ModifierKeys != Keys.Shift) || - !IMEHook(nCode, wParam, lParam); - if (!flag) - return (IntPtr)1; - } - return WindowsAPI.CallNextHookEx(_NextHookProc, nCode, wParam, lParam); - }; - _NextHookProc = WindowsAPI.SetWindowsHookEx(WindowsAPI.HookType.WH_KEYBOARD, HookProc, 0, WindowsAPI.GetCurrentThreadId()); - return; - } - - var moduleHandle = (long)WindowsAPI.GetModuleHandle(_Process.MainModule.ModuleName); - if (Marshal.SizeOf(typeof(IntPtr)) == 4) - { - HookProcX86 = (nCode, wParam, lParam) => { - if (!MK1(nCode, wParam) && Mk2(nCode, wParam, new IntPtr(lParam))) - return (IntPtr)1; - return WindowsAPI.CallNextHookEx(_NextHookProc, nCode, wParam, lParam); - }; - _NextHookProc = WindowsAPI.SetWindowsHookEx(WindowsAPI.HookType.WH_KEYBOARD_LL, HookProcX86, moduleHandle, 0); - } - else - { - HookProcX64 = (nCode, wParam, lParam) => { - if (!MK1(nCode, wParam) && Mk2(nCode, wParam, new IntPtr(lParam))) - return (IntPtr)1; - return WindowsAPI.CallNextHookEx(_NextHookProc, nCode, wParam, lParam); - }; - _NextHookProc = WindowsAPI.SetWindowsHookEx(WindowsAPI.HookType.WH_KEYBOARD_LL, HookProcX64, moduleHandle, 0); - } - } - static bool MK1(int nCode, int wParam) { return !Settings.Use || @@ -340,10 +354,10 @@ static bool Mk2(int nCode, int wParam, IntPtr lParam) if (lpdwProcessId != _Process.Id) return false; - KeyboardHookStruct? key = null; + WindowsAPI.KeyboardHookStruct? key = null; if (Control.ModifierKeys == Keys.None) { - key = KeyboardHookStruct.Create(lParam); + key = WindowsAPI.KeyboardHookStruct.Create(lParam); if (WindowsAPI.GetKeyState(162) < 0 || WindowsAPI.GetKeyState(163) < 0 || WindowsAPI.GetKeyState(17) < 0 || @@ -355,7 +369,7 @@ static bool Mk2(int nCode, int wParam, IntPtr lParam) } else if (Control.ModifierKeys == Keys.Shift) { - key ??= KeyboardHookStruct.Create(lParam); + key ??= WindowsAPI.KeyboardHookStruct.Create(lParam); if (IMEHook(nCode, key.Value.VkCode, 0)) return true; } @@ -363,42 +377,14 @@ static bool Mk2(int nCode, int wParam, IntPtr lParam) } /// - /// 注册表增加低级钩子超时处理,防止系统不允许, - /// 否则:偶发性出现 键盘钩子不能用了,而且退出时产生 1404 错误 - /// https://www.cnblogs.com/songr/p/5131655.html + /// 卸载钩子 /// - static void CheckLowLevelHooksTimeout() - { - const string llh = "LowLevelHooksTimeout"; - using var registryKey = Registry.CurrentUser.OpenSubKey("Control Panel\\Desktop", true); - if ((int)registryKey.GetValue(llh, 0) == 0) - registryKey.SetValue(llh, 25000, RegistryValueKind.DWord); - } - - /// - /// Hook键盘数据结构 - /// - [ComVisible(true)] - [Serializable] - //[DebuggerDisplay("{DebuggerDisplay,nq}")] - //[DebuggerTypeProxy(typeof(KeyboardHookStruct))] - [StructLayout(LayoutKind.Sequential)] - public struct KeyboardHookStruct + internal static void UnIMEHook() { - public int VkCode; // 键码,该代码必须有一个价值的范围1至254 - public int ScanCode; // 指定的硬件扫描码的关键 - public int Flags; // 键标志 - public int Time; // 指定的时间戳记的这个讯息 - public int DwExtraInfo; // 指定额外信息相关的信息 - - public static KeyboardHookStruct Create(IntPtr lParam) - { - return (KeyboardHookStruct)Marshal.PtrToStructure(lParam, typeof(KeyboardHookStruct)); - } - - public void ToPtr(IntPtr lParam) + if (_NextHookProc != IntPtr.Zero) { - Marshal.StructureToPtr(this, lParam, true); + WindowsAPI.UnhookWindowsHookEx(_NextHookProc); + _NextHookProc = IntPtr.Zero; } } } \ No newline at end of file -- Gitee From 084a529c9f1514c79d1753ec3f7ed81920774c65 Mon Sep 17 00:00:00 2001 From: liuqihong <540762622@qq.com> Date: Wed, 2 Nov 2022 16:05:41 +0800 Subject: [PATCH 605/675] =?UTF-8?q?=E4=BC=98=E5=8C=96=E4=BB=A3=E7=A0=81:?= =?UTF-8?q?=E5=88=A0=E9=99=A4=E8=BE=93=E5=85=A5=E6=B3=95=E9=92=A9=E5=AD=90?= =?UTF-8?q?=E7=9A=84=E5=A4=9A=E4=BD=99=E9=A1=B9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/IFoxCAD.Cad.Shared/Copyclip/WindowsAPI.cs | 22 ++---- .../Cmd.cs" | 6 +- .../IMEControl.cs" | 79 ++++++++----------- .../Settings.cs" | 4 +- tests/TestShared/TestAOP.cs | 2 +- 5 files changed, 46 insertions(+), 67 deletions(-) diff --git a/src/IFoxCAD.Cad.Shared/Copyclip/WindowsAPI.cs b/src/IFoxCAD.Cad.Shared/Copyclip/WindowsAPI.cs index 2b77e10..c8ddcfa 100644 --- a/src/IFoxCAD.Cad.Shared/Copyclip/WindowsAPI.cs +++ b/src/IFoxCAD.Cad.Shared/Copyclip/WindowsAPI.cs @@ -447,22 +447,14 @@ public static GuiThreadInfo Create(uint windowThreadProcessId) #endregion #region 钩子 + public delegate IntPtr CallBack(int nCode, int wParam, IntPtr lParam); [DllImport("user32.dll")] - public static extern IntPtr SetWindowsHookEx(HookType idHook, CallBackX86 lpfn, long hmod, int dwThreadId); - [DllImport("user32.dll")] - public static extern IntPtr SetWindowsHookEx(HookType idHook, CallBack lpfn, int hmod, int dwThreadId); - [DllImport("user32.dll")] - public static extern IntPtr SetWindowsHookEx(HookType idHook, CallBackX64 lpfn, long hmod, int dwThreadId); + public static extern IntPtr SetWindowsHookEx(HookType idHook, CallBack lpfn, IntPtr hmod, int dwThreadId); [DllImport("user32.dll")] public static extern IntPtr UnhookWindowsHookEx(IntPtr hHook); [DllImport("user32.dll")] - public static extern IntPtr CallNextHookEx(IntPtr hHook, int ncode, int wParam, long lParam); - [DllImport("user32.dll")] - public static extern IntPtr CallNextHookEx(int hHook, int ncode, int wParam, int lParam); - public delegate IntPtr CallBackX86(int nCode, int wParam, int lParam); - public delegate IntPtr CallBackX64(int nCode, int wParam, long lParam); - public delegate IntPtr CallBack(int nCode, int wParam, int lParam); - public enum HookType : int + public static extern IntPtr CallNextHookEx(IntPtr hHook, int ncode, int wParam, IntPtr lParam); + public enum HookType : byte { WH_KEYBOARD = 2, WH_KEYBOARD_LL = 13, @@ -507,14 +499,14 @@ public static void CheckLowLevelHooksTimeout() registryKey.SetValue(llh, 25000, RegistryValueKind.DWord); } #endregion - - [DllImport("user32.dll")] - public static extern bool GetWindowRect(IntPtr hwnd, ref IntRect lpRect); } public partial class WindowsAPI { + [DllImport("user32.dll")] + public static extern bool GetWindowRect(IntPtr hwnd, ref IntRect lpRect); + [ComVisible(true)] [Serializable] [StructLayout(LayoutKind.Sequential)] diff --git "a/tests/TestAcad09plus/\350\276\223\345\205\245\346\263\225\350\207\252\345\212\250\345\210\207\346\215\242/\350\265\253\346\200\235\350\276\223\345\205\245\346\263\225/Cmd.cs" "b/tests/TestAcad09plus/\350\276\223\345\205\245\346\263\225\350\207\252\345\212\250\345\210\207\346\215\242/\350\265\253\346\200\235\350\276\223\345\205\245\346\263\225/Cmd.cs" index 52e97d4..1801c16 100644 --- "a/tests/TestAcad09plus/\350\276\223\345\205\245\346\263\225\350\207\252\345\212\250\345\210\207\346\215\242/\350\265\253\346\200\235\350\276\223\345\205\245\346\263\225/Cmd.cs" +++ "b/tests/TestAcad09plus/\350\276\223\345\205\245\346\263\225\350\207\252\345\212\250\345\210\207\346\215\242/\350\265\253\346\200\235\350\276\223\345\205\245\346\263\225/Cmd.cs" @@ -1,6 +1,4 @@ -using System.Diagnostics; - -namespace Gstar_IMEFilter; +namespace Gstar_IMEFilter; public class Cmd { @@ -19,8 +17,6 @@ public void Initialize() }; } - - [CommandMethod(nameof(Gstar_IMEFilter1))] public void Gstar_IMEFilter1() { diff --git "a/tests/TestAcad09plus/\350\276\223\345\205\245\346\263\225\350\207\252\345\212\250\345\210\207\346\215\242/\350\265\253\346\200\235\350\276\223\345\205\245\346\263\225/IMEControl.cs" "b/tests/TestAcad09plus/\350\276\223\345\205\245\346\263\225\350\207\252\345\212\250\345\210\207\346\215\242/\350\265\253\346\200\235\350\276\223\345\205\245\346\263\225/IMEControl.cs" index a1eb1dd..bb6b26c 100644 --- "a/tests/TestAcad09plus/\350\276\223\345\205\245\346\263\225\350\207\252\345\212\250\345\210\207\346\215\242/\350\265\253\346\200\235\350\276\223\345\205\245\346\263\225/IMEControl.cs" +++ "b/tests/TestAcad09plus/\350\276\223\345\205\245\346\263\225\350\207\252\345\212\250\345\210\207\346\215\242/\350\265\253\346\200\235\350\276\223\345\205\245\346\263\225/IMEControl.cs" @@ -1,7 +1,6 @@ namespace Gstar_IMEFilter; using System.Diagnostics; -using System.Windows.Controls; using System.Windows.Forms; using Control = System.Windows.Forms.Control; @@ -15,11 +14,9 @@ public class IMEControl const int WM_KEYDOWN = 256; const int WM_KEYUP = 257; - internal static WindowsAPI.CallBackX86? HookProcX86; - internal static WindowsAPI.CallBackX64? HookProcX64; - internal static WindowsAPI.CallBack? HookProc; - internal static IntPtr _NextHookProc;//挂载成功的标记 - internal static Process _Process; + static WindowsAPI.CallBack? _hookProc; + static IntPtr _nextHookProc; + static Process _process; // 优化内存,减少消息循环时候,频繁创建此类 static StringBuilder _lpClassName = new(byte.MaxValue); @@ -27,8 +24,8 @@ public class IMEControl static IMEControl() { - _NextHookProc = IntPtr.Zero; - _Process = Process.GetCurrentProcess(); + _nextHookProc = IntPtr.Zero; + _process = Process.GetCurrentProcess(); WindowsAPI.CheckLowLevelHooksTimeout(); ExceptCMDs = new(); @@ -58,7 +55,7 @@ static IMEControl() // var commandLineMonitor = CommandLineMonitorServices.Instance().GetCommandLineMonitor(doc); /// - /// 此状态用于括免命令图中自动切换到中文,
    + /// 此状态用于豁免命令图中自动切换到中文,
    /// 命令中依然能够切换到英文输入,
    /// 命令结束时候从否决事件恢复拦截
    ///
    @@ -80,7 +77,7 @@ static void DocumentManager_DocumentLockModeChanged(object sender, DocumentLockM static void IMESwitch() { // 检测是否中文状态,不是就切为中文状态 - // 切换只能发生在第一次,第2+次需要不执行 + // 切换只能发生在第一次,第2.+次需要不执行 var focusW = WindowsAPI.GetForegroundWindow(); var context = WindowsAPI.ImmGetContext(focusW); WindowsAPI.ImmGetConversionStatus(context, out int mode/*输入模式*/, out _); @@ -162,7 +159,7 @@ static void IMESwitch() internal static void SetIMEHook() { UnIMEHook(); - if (_NextHookProc != IntPtr.Zero || !Settings.Use) + if (_nextHookProc != IntPtr.Zero || !Settings.Use) return; ExceptCMDs.Clear(); @@ -174,40 +171,35 @@ internal static void SetIMEHook() if (Settings.IMEStyle != IMEHookStyle.Global) { - HookProc = (nCode, wParam, lParam) => { + _hookProc = (nCode, wParam, lParam) => { if (nCode >= 0) { - bool flag = !((lParam > 0) & ((lParam & -1073741823) == 1)) || + // 这里会处理键入时留存首字母的问题 + var lp = lParam.ToInt64(); + bool flag = !((lp > 0) & ((lp & -1073741823/*0xC0000001 按下了一次的意思吧*/) == 1)) || (Control.ModifierKeys != Keys.None && Control.ModifierKeys != Keys.Shift) || !IMEHook(nCode, wParam, lParam); if (!flag) return (IntPtr)1; } - return WindowsAPI.CallNextHookEx(_NextHookProc, nCode, wParam, lParam); + return WindowsAPI.CallNextHookEx(_nextHookProc, nCode, wParam, lParam); }; - _NextHookProc = WindowsAPI.SetWindowsHookEx(WindowsAPI.HookType.WH_KEYBOARD, HookProc, 0, WindowsAPI.GetCurrentThreadId()); + _nextHookProc = WindowsAPI.SetWindowsHookEx(WindowsAPI.HookType.WH_KEYBOARD, _hookProc, + IntPtr.Zero, WindowsAPI.GetCurrentThreadId()); return; } - var moduleHandle = (long)WindowsAPI.GetModuleHandle(_Process.MainModule.ModuleName); - if (Marshal.SizeOf(typeof(IntPtr)) == 4) - { - HookProcX86 = (nCode, wParam, lParam) => { - if (!MK1(nCode, wParam) && Mk2(nCode, wParam, new IntPtr(lParam))) - return (IntPtr)1; - return WindowsAPI.CallNextHookEx(_NextHookProc, nCode, wParam, lParam); - }; - _NextHookProc = WindowsAPI.SetWindowsHookEx(WindowsAPI.HookType.WH_KEYBOARD_LL, HookProcX86, moduleHandle, 0); - } - else - { - HookProcX64 = (nCode, wParam, lParam) => { - if (!MK1(nCode, wParam) && Mk2(nCode, wParam, new IntPtr(lParam))) + var moduleHandle = WindowsAPI.GetModuleHandle(_process.MainModule.ModuleName); + _hookProc = (nCode, wParam, lParam) => { + if (nCode >= 0) + { + if (!MK1(wParam) && Mk2(nCode, wParam, lParam)) return (IntPtr)1; - return WindowsAPI.CallNextHookEx(_NextHookProc, nCode, wParam, lParam); - }; - _NextHookProc = WindowsAPI.SetWindowsHookEx(WindowsAPI.HookType.WH_KEYBOARD_LL, HookProcX64, moduleHandle, 0); - } + } + return WindowsAPI.CallNextHookEx(_nextHookProc, nCode, wParam, lParam); + }; + _nextHookProc = WindowsAPI.SetWindowsHookEx(WindowsAPI.HookType.WH_KEYBOARD_LL, + _hookProc, moduleHandle, 0); } /// @@ -217,7 +209,7 @@ internal static void SetIMEHook() /// /// /// false不终止回调,true终止回调 - public static bool IMEHook(int nCode, int wParam, int lParam) + public static bool IMEHook(int nCode, int wParam, IntPtr lParam) { if (SendKeyState.IsExceptional) return false; @@ -231,7 +223,7 @@ public static bool IMEHook(int nCode, int wParam, int lParam) if (!WindowsAPI.IsWindowEnabled(Acap.MainWindow.Handle)) return false; - // 括免命令进行中输入会触发,实现在括免命令中允许输入法 + // 豁免命令进行中输入会触发,实现在豁免命令中允许输入法 string input = doc.CommandInProgress; Match match = CMDReg.Match(input); if (match.Success) @@ -332,14 +324,13 @@ public static bool IMEHook(int nCode, int wParam, int lParam) return false; } - static bool MK1(int nCode, int wParam) + static bool MK1(int wParam) { return !Settings.Use || WindowsAPI.IsIconic(Acap.MainWindow.Handle.ToInt32()) || WindowsAPI.GetKeyState(91) < 0 || WindowsAPI.GetKeyState(92) < 0 || - wParam != WM_KEYDOWN || - nCode < 0; + wParam != WM_KEYDOWN; } static bool Mk2(int nCode, int wParam, IntPtr lParam) @@ -351,7 +342,7 @@ static bool Mk2(int nCode, int wParam, IntPtr lParam) focus = WindowsAPI.GetForegroundWindow(); WindowsAPI.GetWindowThreadProcessId(focus, out uint lpdwProcessId); - if (lpdwProcessId != _Process.Id) + if (lpdwProcessId != _process.Id) return false; WindowsAPI.KeyboardHookStruct? key = null; @@ -364,13 +355,13 @@ static bool Mk2(int nCode, int wParam, IntPtr lParam) WindowsAPI.GetKeyState(262144/*alt键*/) < 0) return false; - if (IMEHook(nCode, key.Value.VkCode, 0)) + if (IMEHook(nCode, key.Value.VkCode, IntPtr.Zero)) return true; } else if (Control.ModifierKeys == Keys.Shift) { key ??= WindowsAPI.KeyboardHookStruct.Create(lParam); - if (IMEHook(nCode, key.Value.VkCode, 0)) + if (IMEHook(nCode, key.Value.VkCode, IntPtr.Zero)) return true; } return false; @@ -381,10 +372,10 @@ static bool Mk2(int nCode, int wParam, IntPtr lParam) /// internal static void UnIMEHook() { - if (_NextHookProc != IntPtr.Zero) + if (_nextHookProc != IntPtr.Zero) { - WindowsAPI.UnhookWindowsHookEx(_NextHookProc); - _NextHookProc = IntPtr.Zero; + WindowsAPI.UnhookWindowsHookEx(_nextHookProc); + _nextHookProc = IntPtr.Zero; } } } \ No newline at end of file diff --git "a/tests/TestAcad09plus/\350\276\223\345\205\245\346\263\225\350\207\252\345\212\250\345\210\207\346\215\242/\350\265\253\346\200\235\350\276\223\345\205\245\346\263\225/Settings.cs" "b/tests/TestAcad09plus/\350\276\223\345\205\245\346\263\225\350\207\252\345\212\250\345\210\207\346\215\242/\350\265\253\346\200\235\350\276\223\345\205\245\346\263\225/Settings.cs" index ed655b3..9ccfbac 100644 --- "a/tests/TestAcad09plus/\350\276\223\345\205\245\346\263\225\350\207\252\345\212\250\345\210\207\346\215\242/\350\265\253\346\200\235\350\276\223\345\205\245\346\263\225/Settings.cs" +++ "b/tests/TestAcad09plus/\350\276\223\345\205\245\346\263\225\350\207\252\345\212\250\345\210\207\346\215\242/\350\265\253\346\200\235\350\276\223\345\205\245\346\263\225/Settings.cs" @@ -165,7 +165,7 @@ internal static void SaveSettings() /// /// 钩子样式 /// -public enum IMEHookStyle : int +public enum IMEHookStyle : byte { Global,//全局钩子控制 Process,//进程钩子控制 @@ -174,7 +174,7 @@ public enum IMEHookStyle : int /// /// 切换输入法方式 /// -public enum IMESwitchMode : int +public enum IMESwitchMode : byte { Shift, Ctrl, diff --git a/tests/TestShared/TestAOP.cs b/tests/TestShared/TestAOP.cs index 4edef4d..872bf35 100644 --- a/tests/TestShared/TestAOP.cs +++ b/tests/TestShared/TestAOP.cs @@ -8,7 +8,7 @@ * 所以需要用户手动进行AOP.Run(), * 默认情况不侵入用户的命令,必须用户手动启用此功能; * 启动执行策略之后,侵入命名空间下的命令, - * 此时有拒绝特性的策略保证括免,因为用户肯定是想少写一个事务注入的特性; + * 此时有拒绝特性的策略保证豁免,因为用户肯定是想少写一个事务注入的特性; */ public class AutoAOP { -- Gitee From c9f72b0aeb627a7bc283fc536e47238c8c931c99 Mon Sep 17 00:00:00 2001 From: liuqihong <540762622@qq.com> Date: Wed, 2 Nov 2022 22:38:51 +0800 Subject: [PATCH 606/675] =?UTF-8?q?=E4=BC=98=E5=8C=96=E4=BB=A3=E7=A0=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/IFoxCAD.Basal/General/DebugHelper.cs | 14 + .../Algorithms/QuadTree/QuadTreeNode.cs | 252 +++++++++--------- .../Algorithms/QuadTree/Rect.cs | 9 +- .../Copyclip/TagClipboardInfo.cs | 6 +- src/IFoxCAD.Cad.Shared/Copyclip/WindowsAPI.cs | 2 +- src/IFoxCAD.Cad.Shared/Runtime/Env.cs | 2 + src/IFoxCAD.Cad.Shared/Runtime/IAutoGo.cs | 2 +- src/IFoxCAD.Cad.Shared/Runtime/Log.cs | 32 +-- .../Runtime/PE/ProgramPE.cs | 2 +- .../Cmd.cs" | 14 +- .../IMEControl.cs" | 164 +++++++----- .../Settings.cs" | 3 +- .../Window/SettingsWindow.xaml.cs" | 3 +- tests/TestShared/CmdINI.cs | 4 +- tests/TestShared/Copyclip.cs | 4 +- tests/TestShared/TestMarshal.cs | 6 +- tests/TestShared/TestPoint.cs | 2 +- 17 files changed, 290 insertions(+), 231 deletions(-) create mode 100644 src/IFoxCAD.Basal/General/DebugHelper.cs diff --git a/src/IFoxCAD.Basal/General/DebugHelper.cs b/src/IFoxCAD.Basal/General/DebugHelper.cs new file mode 100644 index 0000000..aba6955 --- /dev/null +++ b/src/IFoxCAD.Basal/General/DebugHelper.cs @@ -0,0 +1,14 @@ +namespace IFoxCAD.Basal; + +public static class Debugx +{ + public static void Printl(object message) + { + var str = message + $"::{DateTime.Now.ToLongDateString() + DateTime.Now.TimeOfDay}"; +#if DEBUG + System.Diagnostics.Debug.WriteLine(str); +#else + System.Diagnostics.Trace.WriteLine(str); +#endif + } +} \ No newline at end of file diff --git a/src/IFoxCAD.Cad.Shared/Algorithms/QuadTree/QuadTreeNode.cs b/src/IFoxCAD.Cad.Shared/Algorithms/QuadTree/QuadTreeNode.cs index 017eace..7898b84 100644 --- a/src/IFoxCAD.Cad.Shared/Algorithms/QuadTree/QuadTreeNode.cs +++ b/src/IFoxCAD.Cad.Shared/Algorithms/QuadTree/QuadTreeNode.cs @@ -4,8 +4,8 @@ /// 子节点 ///
    /// -public class QuadTreeNode - : Rect +public class QuadTreeNode + : Rect where TEntity : QuadEntity { #region 成员 @@ -108,13 +108,13 @@ public int CountSubTree /// 节点深度 public QuadTreeNode(Rect box, QuadTreeNode? parent, int depth) { - _X = box._X; - _Y = box._Y; - _Right = box._Right; - _Top = box._Top; + _X = box._X; + _Y = box._Y; + _Right = box._Right; + _Top = box._Top; - Parent = parent; - Depth = depth; + Parent = parent; + Depth = depth; Contents = new(); } #endregion @@ -160,7 +160,7 @@ public QuadTreeNode(Rect box, QuadTreeNode? parent, int depth) { if (!Contains(ent)) { - // Debug.WriteLine("不在四叉树边界范围"); + // Debugx.Printl("不在四叉树边界范围"); // Trace.WriteLine("不在四叉树边界范围"); return null; } @@ -547,30 +547,30 @@ void GetCurrentContents(Rect queryArea, List results) switch (findMode) { case QuadTreeFindMode.Top: - { - // 下格才获取上格,否则导致做了无用功,上格就直接获取邻居了 - if (parent.LeftBottomTree == queryNode || parent.RightBottomTree == queryNode) - resultEntity = GetNearestNeighbor(parent, findMode, queryArea); - } - break; + { + // 下格才获取上格,否则导致做了无用功,上格就直接获取邻居了 + if (parent.LeftBottomTree == queryNode || parent.RightBottomTree == queryNode) + resultEntity = GetNearestNeighbor(parent, findMode, queryArea); + } + break; case QuadTreeFindMode.Bottom: - { - if (parent.LeftTopTree == queryNode || parent.RightTopTree == queryNode) - resultEntity = GetNearestNeighbor(parent, findMode, queryArea); - } - break; + { + if (parent.LeftTopTree == queryNode || parent.RightTopTree == queryNode) + resultEntity = GetNearestNeighbor(parent, findMode, queryArea); + } + break; case QuadTreeFindMode.Left: - { - if (parent.RightTopTree == queryNode || parent.RightBottomTree == queryNode) - resultEntity = GetNearestNeighbor(parent, findMode, queryArea); - } - break; + { + if (parent.RightTopTree == queryNode || parent.RightBottomTree == queryNode) + resultEntity = GetNearestNeighbor(parent, findMode, queryArea); + } + break; case QuadTreeFindMode.Right: - { - if (parent.LeftTopTree == queryNode || parent.LeftBottomTree == queryNode) - resultEntity = GetNearestNeighbor(parent, findMode, queryArea); - } - break; + { + if (parent.LeftTopTree == queryNode || parent.LeftBottomTree == queryNode) + resultEntity = GetNearestNeighbor(parent, findMode, queryArea); + } + break; } } if (resultEntity is not null) @@ -614,54 +614,50 @@ void GetCurrentContents(Rect queryArea, List results) switch (findMode) { case QuadTreeFindMode.Top: - { - // 取出Y比queryArea的还大的一个,是最近的那个 - var qy = qcent.Y; - queryNode.ContentsSubTree(lst); - lst.ForEach(ent => - { - if (ent.CenterPoint.Y > qy) - lst.Add(ent); - }); - lst = lst.OrderBy(ent => ent.CenterPoint.Y).ToList(); - } - break; + { + // 取出Y比queryArea的还大的一个,是最近的那个 + var qy = qcent.Y; + queryNode.ContentsSubTree(lst); + lst.ForEach(ent => { + if (ent.CenterPoint.Y > qy) + lst.Add(ent); + }); + lst = lst.OrderBy(ent => ent.CenterPoint.Y).ToList(); + } + break; case QuadTreeFindMode.Bottom: - { - var qy = qcent.Y; - queryNode.ContentsSubTree(lst); - lst.ForEach(ent => - { - if (ent.CenterPoint.Y < qy) - lst.Add(ent); - }); - lst = lst.OrderByDescending(ent => ent.CenterPoint.Y).ToList(); - } - break; + { + var qy = qcent.Y; + queryNode.ContentsSubTree(lst); + lst.ForEach(ent => { + if (ent.CenterPoint.Y < qy) + lst.Add(ent); + }); + lst = lst.OrderByDescending(ent => ent.CenterPoint.Y).ToList(); + } + break; case QuadTreeFindMode.Left: - { - var qx = qcent.Y; - queryNode.ContentsSubTree(lst); - lst.ForEach(ent => - { - if (ent.CenterPoint.X > qx) - lst.Add(ent); - }); - lst = lst.OrderBy(ent => ent.CenterPoint.X).ToList(); - } - break; + { + var qx = qcent.Y; + queryNode.ContentsSubTree(lst); + lst.ForEach(ent => { + if (ent.CenterPoint.X > qx) + lst.Add(ent); + }); + lst = lst.OrderBy(ent => ent.CenterPoint.X).ToList(); + } + break; case QuadTreeFindMode.Right: - { - var qx = qcent.Y; - queryNode.ContentsSubTree(lst); - lst.ForEach(ent => - { - if (ent.CenterPoint.X < qx) - lst.Add(ent); - }); - lst = lst.OrderByDescending(ent => ent.CenterPoint.X).ToList(); - } - break; + { + var qx = qcent.Y; + queryNode.ContentsSubTree(lst); + lst.ForEach(ent => { + if (ent.CenterPoint.X < qx) + lst.Add(ent); + }); + lst = lst.OrderByDescending(ent => ent.CenterPoint.X).ToList(); + } + break; } if (lst.Count > 0) @@ -705,62 +701,62 @@ QuadTreeNode GetMinNode(Rect queryArea) switch (findMode) { case QuadTreeFindMode.Top: - { - // 判断当前节点在父节点的位置,如果是在 下格 就取对应的 上格 - if (tar == parent.LeftBottomTree) - return parent.LeftTopTree; - if (tar == parent.RightBottomTree) - return parent.RightTopTree; - // 否则就是上格 - // 找父节点的北方邻居..也就是在爷节点上面找 - // 递归,此时必然是 下格,就必然返回 上格,然后退出递归 - var parentNeibor = FindNeiborNode(parent, QuadTreeFindMode.Top); - // 父节点的北方邻居 无 子节点 - if (parentNeibor is null || parentNeibor.RightTopTree is null) - return parentNeibor;// 返回父节点的北方邻居,比较大 - // 父节点的北方邻居 有 子节点,剩下条件就只有这两 - - // 如果直接返回,那么是(相同或更大), - // 而找邻近图元需要的是这个(相同或更大)下面的图元,在外面对这个格子内图元用坐标分析就好了 - if (tar == parent.LeftTopTree) - return parentNeibor.LeftBottomTree; - return parentNeibor.RightBottomTree; - } + { + // 判断当前节点在父节点的位置,如果是在 下格 就取对应的 上格 + if (tar == parent.LeftBottomTree) + return parent.LeftTopTree; + if (tar == parent.RightBottomTree) + return parent.RightTopTree; + // 否则就是上格 + // 找父节点的北方邻居..也就是在爷节点上面找 + // 递归,此时必然是 下格,就必然返回 上格,然后退出递归 + var parentNeibor = FindNeiborNode(parent, QuadTreeFindMode.Top); + // 父节点的北方邻居 无 子节点 + if (parentNeibor is null || parentNeibor.RightTopTree is null) + return parentNeibor;// 返回父节点的北方邻居,比较大 + // 父节点的北方邻居 有 子节点,剩下条件就只有这两 + + // 如果直接返回,那么是(相同或更大), + // 而找邻近图元需要的是这个(相同或更大)下面的图元,在外面对这个格子内图元用坐标分析就好了 + if (tar == parent.LeftTopTree) + return parentNeibor.LeftBottomTree; + return parentNeibor.RightBottomTree; + } case QuadTreeFindMode.Bottom: - { - if (tar == parent.LeftTopTree) - return parent.LeftBottomTree; - if (tar == parent.RightTopTree) - return parent.RightBottomTree; - var parentNeibor = FindNeiborNode(parent, QuadTreeFindMode.Bottom); - if (parentNeibor is null || parentNeibor.RightTopTree is null) - return parentNeibor; - if (tar == parent.LeftBottomTree) - return parentNeibor.LeftTopTree; - return parentNeibor.RightTopTree; - } + { + if (tar == parent.LeftTopTree) + return parent.LeftBottomTree; + if (tar == parent.RightTopTree) + return parent.RightBottomTree; + var parentNeibor = FindNeiborNode(parent, QuadTreeFindMode.Bottom); + if (parentNeibor is null || parentNeibor.RightTopTree is null) + return parentNeibor; + if (tar == parent.LeftBottomTree) + return parentNeibor.LeftTopTree; + return parentNeibor.RightTopTree; + } case QuadTreeFindMode.Right: - { - if (tar == parent.LeftTopTree) - return parent.RightTopTree; - if (tar == parent.LeftBottomTree) - return parent.RightBottomTree; - var parentNeibor = FindNeiborNode(parent, QuadTreeFindMode.Right); - if (tar == parent.RightTopTree) - return parentNeibor?.LeftTopTree; - return parentNeibor?.LeftBottomTree; - } + { + if (tar == parent.LeftTopTree) + return parent.RightTopTree; + if (tar == parent.LeftBottomTree) + return parent.RightBottomTree; + var parentNeibor = FindNeiborNode(parent, QuadTreeFindMode.Right); + if (tar == parent.RightTopTree) + return parentNeibor?.LeftTopTree; + return parentNeibor?.LeftBottomTree; + } case QuadTreeFindMode.Left: - { - if (tar == parent.RightTopTree) - return parent.LeftTopTree; - if (tar == parent.RightBottomTree) - return parent.LeftBottomTree; - var parentNeibor = FindNeiborNode(parent, QuadTreeFindMode.Left); - if (tar == parent.LeftTopTree) - return parentNeibor?.RightTopTree; - return parentNeibor?.RightBottomTree; - } + { + if (tar == parent.RightTopTree) + return parent.LeftTopTree; + if (tar == parent.RightBottomTree) + return parent.LeftBottomTree; + var parentNeibor = FindNeiborNode(parent, QuadTreeFindMode.Left); + if (tar == parent.LeftTopTree) + return parentNeibor?.RightTopTree; + return parentNeibor?.RightBottomTree; + } } return null; } diff --git a/src/IFoxCAD.Cad.Shared/Algorithms/QuadTree/Rect.cs b/src/IFoxCAD.Cad.Shared/Algorithms/QuadTree/Rect.cs index 6ed1681..4a44663 100644 --- a/src/IFoxCAD.Cad.Shared/Algorithms/QuadTree/Rect.cs +++ b/src/IFoxCAD.Cad.Shared/Algorithms/QuadTree/Rect.cs @@ -312,10 +312,11 @@ public static bool IsRectAngle(List? ptList, double tolerance = 1e-8) throw new ArgumentNullException(nameof(ptList)); var pts = ptList.ToList(); - /* 消重,不这里设置,否则这不是一个正确的单元测试 - * // var ptList = pts.Distinct().ToList(); - * var ptList = pts.DistinctExBy((a, b) => a.DistanceTo(b) < 1e-6).ToList(); - */ + /* + * 消重,不这里设置,否则这不是一个正确的单元测试 + * // var ptList = pts.Distinct().ToList(); + * var ptList = pts.DistinctExBy((a, b) => a.DistanceTo(b) < 1e-6).ToList(); + */ if (ptList.Count == 5) { // 首尾点相同移除最后 diff --git a/src/IFoxCAD.Cad.Shared/Copyclip/TagClipboardInfo.cs b/src/IFoxCAD.Cad.Shared/Copyclip/TagClipboardInfo.cs index 4d050b4..272754a 100644 --- a/src/IFoxCAD.Cad.Shared/Copyclip/TagClipboardInfo.cs +++ b/src/IFoxCAD.Cad.Shared/Copyclip/TagClipboardInfo.cs @@ -127,11 +127,11 @@ public void testAcedGetAcadDwgview() var a = AcedGetAcadDwgview().ToString(); //自动执行的时候就存在了 var b = ed.CurrentViewportObjectId.ToString(); - Debug.WriteLine("a == b:" + a == b);//不对 + Debugx.Printl("a == b:" + a == b);//不对 var tab = ed.GetCurrentView(); var c = tab.ObjectId.ToString(); - Debug.WriteLine("a == c:" + a == c);//不对 + Debugx.Printl("a == c:" + a == c);//不对 } */ @@ -330,7 +330,7 @@ public static bool OpenClipboardTask(bool isWrite, Action action) catch (Exception e) { Debugger.Break(); - Debug.WriteLine(e.Message); + Debugx.Printl(e.Message); } finally { diff --git a/src/IFoxCAD.Cad.Shared/Copyclip/WindowsAPI.cs b/src/IFoxCAD.Cad.Shared/Copyclip/WindowsAPI.cs index c8ddcfa..0c7284a 100644 --- a/src/IFoxCAD.Cad.Shared/Copyclip/WindowsAPI.cs +++ b/src/IFoxCAD.Cad.Shared/Copyclip/WindowsAPI.cs @@ -215,7 +215,7 @@ public static void StructToPtr(T structObj, catch (Exception e) { Debugger.Break(); - Debug.WriteLine(e.Message); + Debugx.Printl(e.Message); } finally { diff --git a/src/IFoxCAD.Cad.Shared/Runtime/Env.cs b/src/IFoxCAD.Cad.Shared/Runtime/Env.cs index 0a4697d..6702418 100644 --- a/src/IFoxCAD.Cad.Shared/Runtime/Env.cs +++ b/src/IFoxCAD.Cad.Shared/Runtime/Env.cs @@ -1,3 +1,5 @@ +using System.Diagnostics; + namespace IFoxCAD.Cad; /// diff --git a/src/IFoxCAD.Cad.Shared/Runtime/IAutoGo.cs b/src/IFoxCAD.Cad.Shared/Runtime/IAutoGo.cs index f9e018d..e908897 100644 --- a/src/IFoxCAD.Cad.Shared/Runtime/IAutoGo.cs +++ b/src/IFoxCAD.Cad.Shared/Runtime/IAutoGo.cs @@ -218,7 +218,7 @@ public static void AppDomainGetTypes(Action action, string? dllNameWithout catch (System.Exception e) { #if DEBUG - Debug.WriteLine($"出错:{nameof(AppDomainGetTypes)};计数{error};错误信息:{e.Message}"); + Debugx.Printl($"出错:{nameof(AppDomainGetTypes)};计数{error};错误信息:{e.Message}"); Debugger.Break(); #endif } diff --git a/src/IFoxCAD.Cad.Shared/Runtime/Log.cs b/src/IFoxCAD.Cad.Shared/Runtime/Log.cs index fe05b5a..812d694 100644 --- a/src/IFoxCAD.Cad.Shared/Runtime/Log.cs +++ b/src/IFoxCAD.Cad.Shared/Runtime/Log.cs @@ -282,27 +282,27 @@ static string LogAction(Exception? ex, switch (target) { case LogTarget.File: - logger = new FileLogger(); - logger.WriteLog(logtxtJson); - break; + logger = new FileLogger(); + logger.WriteLog(logtxtJson); + break; case LogTarget.FileNotException: - logger = new FileLogger(); - logger.WriteLog(logtxtJson); - break; + logger = new FileLogger(); + logger.WriteLog(logtxtJson); + break; case LogTarget.Database: - logger = new DBLogger(); - logger.WriteLog(logtxtJson); - break; + logger = new DBLogger(); + logger.WriteLog(logtxtJson); + break; case LogTarget.EventLog: - logger = new EventLogger(); - logger.WriteLog(logtxtJson); - break; + logger = new EventLogger(); + logger.WriteLog(logtxtJson); + break; } } if (FlagOutVsOutput) { - Debug.WriteLine("错误日志: " + LogAddress); + Debugx.Printl("错误日志: " + LogAddress); Debug.Write(logtxtJson); } return logtxtJson; @@ -437,9 +437,9 @@ static Log() if (printDebugWindow) { - Debug.WriteLine("错误日志: " + _logAddress); + Debugx.Printl("错误日志: " + _logAddress); Debug.Write(logtxtJson); - // Debugger.Break(); + // Debugger.Break(); // Debug.Assert(false, "终止进程"); } return logtxtJson; @@ -449,5 +449,5 @@ static Log() _logWriteLock.ExitWriteLock();// 解锁 读写锁 } } -} +} #endif \ No newline at end of file diff --git a/src/IFoxCAD.Cad.Shared/Runtime/PE/ProgramPE.cs b/src/IFoxCAD.Cad.Shared/Runtime/PE/ProgramPE.cs index 29c5c02..e1f834a 100644 --- a/src/IFoxCAD.Cad.Shared/Runtime/PE/ProgramPE.cs +++ b/src/IFoxCAD.Cad.Shared/Runtime/PE/ProgramPE.cs @@ -25,7 +25,7 @@ static void Main(string[] args) var str = System.Text.Encoding.Default.GetString(item as byte[]); sb.Append(str); } - Debug.WriteLine(sb.ToString()); + Debugx.Printl(sb.ToString()); // 原作者的封装 var ss = pe.GetPETable(); diff --git "a/tests/TestAcad09plus/\350\276\223\345\205\245\346\263\225\350\207\252\345\212\250\345\210\207\346\215\242/\350\265\253\346\200\235\350\276\223\345\205\245\346\263\225/Cmd.cs" "b/tests/TestAcad09plus/\350\276\223\345\205\245\346\263\225\350\207\252\345\212\250\345\210\207\346\215\242/\350\265\253\346\200\235\350\276\223\345\205\245\346\263\225/Cmd.cs" index 1801c16..94991f7 100644 --- "a/tests/TestAcad09plus/\350\276\223\345\205\245\346\263\225\350\207\252\345\212\250\345\210\207\346\215\242/\350\265\253\346\200\235\350\276\223\345\205\245\346\263\225/Cmd.cs" +++ "b/tests/TestAcad09plus/\350\276\223\345\205\245\346\263\225\350\207\252\345\212\250\345\210\207\346\215\242/\350\265\253\346\200\235\350\276\223\345\205\245\346\263\225/Cmd.cs" @@ -5,15 +5,16 @@ public class Cmd [IFoxInitialize] public void Initialize() { - Env.Printl($"※输入法控制※" + - $"\n{nameof(Gstar_IMEFilter1)} - 切换开关" + - $"\n{nameof(Gstar_IMEFilter2)} - 控制模式" + + Env.Printl($"※输入法+控制※" + + $"\n{nameof(Gstar_IMEFilter1)} - 启用拦截开关" + + $"\n{nameof(Gstar_IMEFilter2)} - 输入法切换模式" + $"\n{nameof(Gstar_IMEFilterSettings)} - 设置"); DocReactor.IntialReactor(); Settings.LoadSettings(); IMEControl.SetIMEHook(); Acap.QuitWillStart += (s, e) => { IMEControl.UnIMEHook(); + IMEControl.SaveFt(); }; } @@ -21,21 +22,22 @@ public void Initialize() public void Gstar_IMEFilter1() { Settings.Use = !Settings.Use; - Env.Printl("已经 " + (Settings.Use ? "开启" : "禁用") + " 输入法+"); + Env.Printl("已经 " + (Settings.Use ? "开启" : "禁用") + " 输入法+"); } [CommandMethod(nameof(Gstar_IMEFilter2))] public void Gstar_IMEFilter2() { // 存在重复的关键字首字母,所以用数字代替 - PromptIntegerOptions options = new($"\n输入法切换模式:"); + PromptIntegerOptions options = new($"\n输入法切换模式: "); foreach (var suit in Enum.GetValues(typeof(IMESwitchMode))) options.Keywords.Add($"{suit}", $"{suit}", $"{suit}({(int)suit})", true, true); options.Keywords.Default = Settings.IMEInputSwitch.ToString(); - var result = Env.Editor.GetInteger(options); + var result = Env.Editor.GetInteger(options); if (result.Status != PromptStatus.OK) return; + var max = Enum.GetValues(typeof(IMESwitchMode)).Cast().Max(); if (result.Value > max) { diff --git "a/tests/TestAcad09plus/\350\276\223\345\205\245\346\263\225\350\207\252\345\212\250\345\210\207\346\215\242/\350\265\253\346\200\235\350\276\223\345\205\245\346\263\225/IMEControl.cs" "b/tests/TestAcad09plus/\350\276\223\345\205\245\346\263\225\350\207\252\345\212\250\345\210\207\346\215\242/\350\265\253\346\200\235\350\276\223\345\205\245\346\263\225/IMEControl.cs" index bb6b26c..0396e8a 100644 --- "a/tests/TestAcad09plus/\350\276\223\345\205\245\346\263\225\350\207\252\345\212\250\345\210\207\346\215\242/\350\265\253\346\200\235\350\276\223\345\205\245\346\263\225/IMEControl.cs" +++ "b/tests/TestAcad09plus/\350\276\223\345\205\245\346\263\225\350\207\252\345\212\250\345\210\207\346\215\242/\350\265\253\346\200\235\350\276\223\345\205\245\346\263\225/IMEControl.cs" @@ -6,8 +6,10 @@ public class IMEControl { - internal static HashSet DefaultCMDs; - internal static HashSet ExceptCMDs; + // 豁免命令组: 默认和配置的 + internal static HashSet DefaultCmds; + // 豁免命令组: 默认和配置的+用户面板输入的 + internal static HashSet ExceptCmds; static readonly Regex CMDReg = new("\\(C:.*\\)"); /*某些窗口没有 WM_KEYDOWN 消息,就只有 WM_KEYUP 消息*/ @@ -21,6 +23,7 @@ public class IMEControl // 优化内存,减少消息循环时候,频繁创建此类 static StringBuilder _lpClassName = new(byte.MaxValue); static string[] _separator = new string[] { "," }; + static string _ftFile; static IMEControl() { @@ -28,27 +31,37 @@ static IMEControl() _process = Process.GetCurrentProcess(); WindowsAPI.CheckLowLevelHooksTimeout(); - ExceptCMDs = new(); - DefaultCMDs = new() { "MTEXT", "DDEDIT", "MTEDIT", "TABLEDIT", "MLEADER", + ExceptCmds = new(); + DefaultCmds = new() { "MTEXT", "DDEDIT", "MTEDIT", "TABLEDIT", "MLEADER", "QLEADER", "MLEADERCONTENTEDIT", "MLEADEREDIT", "TEXTEDIT", "TEXT", "QLEADER" }; string? lines = null; - var ftFile = Path.Combine(Settings.MyDir, "ExceptCMDs.ft"); - if (File.Exists(ftFile)) - lines = File.ReadAllText(ftFile, Encoding.UTF8); + _ftFile = Path.Combine(Settings.MyDir, nameof(Gstar_IMEFilter) + nameof(ExceptCmds) + ".ft"); + if (File.Exists(_ftFile)) + lines = File.ReadAllText(_ftFile, Encoding.UTF8); else - Debug.WriteLine("配置文件丢失: " + ftFile); - + Debugx.Printl("配置文件丢失: " + _ftFile); if (lines != null) { var ls = lines.Split(_separator, StringSplitOptions.RemoveEmptyEntries); for (int i = 0; i < ls.Length; i++) - DefaultCMDs.Add(ls[i]); + DefaultCmds.Add(ls[i]); } - Acap.DocumentManager.DocumentLockModeChanged += DocumentManager_DocumentLockModeChanged; } + public static void SaveFt() + { + HashSet lst = new(); + foreach (var item in ExceptCmds) + if (!DefaultCmds.Contains(item)) + lst.Add(item); + if (!lst.Any()) + return; + var jo = string.Join(",", lst.ToArray()); + File.WriteAllText(_ftFile, jo, Encoding.UTF8); + } + #region 切换输入法 // TODO 关键字是中文输入,自动切换到英文 // 命令行监控服务 实例 得到命令行监控 @@ -67,7 +80,7 @@ static void DocumentManager_DocumentLockModeChanged(object sender, DocumentLockM if (!e.GlobalCommandName.StartsWith("#")) return; var up = e.GlobalCommandName.ToUpper()[1..]; - if (ExceptCMDs.Contains(up)) + if (ExceptCmds.Contains(up)) SendKeyState = new(); } @@ -83,7 +96,7 @@ static void IMESwitch() WindowsAPI.ImmGetConversionStatus(context, out int mode/*输入模式*/, out _); if (!WindowsAPI.ImmGetOpenStatus(context)) { - Debug.WriteLine("现在是英文状态,切换到中文"); + Debugx.Printl("现在是英文状态,切换到中文"); SendKeyState.Exceptional(); switch (Settings.IMEInputSwitch) @@ -147,7 +160,7 @@ static void IMESwitch() // 但是为了避免命令中用户手动切换 中文切换到英文,然后再触发上面 英文状态转中文逻辑, // 所以此处也要设置状态 // 在命令否决事件上面恢复拦截 - Debug.WriteLine("现在是中文状态,保持不变"); + Debugx.Printl("现在是中文状态,保持不变"); SendKeyState.Exceptional(); } } @@ -162,25 +175,36 @@ internal static void SetIMEHook() if (_nextHookProc != IntPtr.Zero || !Settings.Use) return; - ExceptCMDs.Clear(); - foreach (var item in DefaultCMDs) - ExceptCMDs.Add(item); + ExceptCmds.Clear(); + foreach (var item in DefaultCmds) + ExceptCmds.Add(item); var ss = Settings.UserFilter.Split(_separator, StringSplitOptions.RemoveEmptyEntries); foreach (var item in ss) - ExceptCMDs.Add(item); + ExceptCmds.Add(item); - if (Settings.IMEStyle != IMEHookStyle.Global) + if (Settings.IMEStyle == IMEHookStyle.Process) { + Debugx.Printl($"切换到进程钩子控制:{DateTime.Now}"); _hookProc = (nCode, wParam, lParam) => { if (nCode >= 0) { // 这里会处理键入时留存首字母的问题 + // 搜狗输入法如果连续按着,那么此时拦截失效 + // 高版本cad基本上不能用进程钩子 Gstar_IMEFilterSettings var lp = lParam.ToInt64(); - bool flag = !((lp > 0) & ((lp & -1073741823/*0xC0000001 按下了一次的意思吧*/) == 1)) || - (Control.ModifierKeys != Keys.None && Control.ModifierKeys != Keys.Shift) || - !IMEHook(nCode, wParam, lParam); - if (!flag) - return (IntPtr)1; + if ((lp > 0) && ((lp & 0xC0000001) == 1))//按下某个键 + { + // 如果是ctrl就跳过 + if (Control.ModifierKeys == Keys.None || Control.ModifierKeys == Keys.Shift) + { + Debugx.Printl($"进程钩子按了这个{Control.ModifierKeys}^{DateTime.Now}"); + if (IMEHook(nCode, wParam, lParam)) + { + Debugx.Printl($"进程钩子拦截成功^{DateTime.Now}"); + return (IntPtr)1; + } + } + } } return WindowsAPI.CallNextHookEx(_nextHookProc, nCode, wParam, lParam); }; @@ -189,31 +213,30 @@ internal static void SetIMEHook() return; } - var moduleHandle = WindowsAPI.GetModuleHandle(_process.MainModule.ModuleName); - _hookProc = (nCode, wParam, lParam) => { - if (nCode >= 0) - { - if (!MK1(wParam) && Mk2(nCode, wParam, lParam)) - return (IntPtr)1; - } - return WindowsAPI.CallNextHookEx(_nextHookProc, nCode, wParam, lParam); - }; - _nextHookProc = WindowsAPI.SetWindowsHookEx(WindowsAPI.HookType.WH_KEYBOARD_LL, - _hookProc, moduleHandle, 0); + if (Settings.IMEStyle == IMEHookStyle.Global) + { + Debugx.Printl($"切换到全局钩子控制:{DateTime.Now}"); + var moduleHandle = WindowsAPI.GetModuleHandle(_process.MainModule.ModuleName); + _hookProc = (nCode, wParam, lParam) => { + if (nCode >= 0) + { + if (!MK1(wParam) && Mk2(nCode, wParam, lParam)) + return (IntPtr)1; + } + return WindowsAPI.CallNextHookEx(_nextHookProc, nCode, wParam, lParam); + }; + _nextHookProc = WindowsAPI.SetWindowsHookEx(WindowsAPI.HookType.WH_KEYBOARD_LL, + _hookProc, moduleHandle, 0); + return; + } } /// - /// 钩子的消息处理 - /// r - /// - /// - /// - /// false不终止回调,true终止回调 - public static bool IMEHook(int nCode, int wParam, IntPtr lParam) + /// 豁免命令处理 + /// + /// + static bool ExceptCmdsTask() { - if (SendKeyState.IsExceptional) - return false; - var dm = Acap.DocumentManager; if (dm.Count == 0) return false; @@ -229,11 +252,25 @@ public static bool IMEHook(int nCode, int wParam, IntPtr lParam) if (match.Success) input = input.Substring(checked(match.Index + 3), checked(match.Length - 4)); - if (ExceptCMDs.Contains(input.ToUpper())) + if (ExceptCmds.Contains(input.ToUpper())) { IMESwitch(); return false; } + return true; + } + + /// + /// 钩子的消息处理 + /// r + /// + /// + /// + /// false不终止回调,true终止回调 + public static bool IMEHook(int nCode, int wParam, IntPtr lParam) + { + if (SendKeyState.IsExceptional) + return false; // 键盘按键值 if ((65 <= wParam && wParam <= 90/*a~z*/) || @@ -256,35 +293,39 @@ public static bool IMEHook(int nCode, int wParam, IntPtr lParam) wParam == 223 || wParam == 110/*小数字键盘.*/) { - //Debug.WriteLine(wParam); + //Debugx.Printl(wParam); + + // 先判断键入的数字更快,再判断豁免命令 + if (!ExceptCmdsTask()) + return false; var focus = WindowsAPI.GetFocus(); WindowsAPI.GetClassName(focus, _lpClassName, checked(_lpClassName.Capacity + 1)); string left = _lpClassName.ToString().ToLower(); if (left.StartsWith("afx"))// 在08输入的都从这里进入 { - Debug.WriteLine($"afx::{DateTime.Now}"); - { - // cad08启动时候会滚动某些信息,此时立马鼠标狂点入到vs中,vs就会无法输入 - // 会被拦截到了"afx"处理,然后所有的输入都跑cad了,需要加入如下代码: var focusW = WindowsAPI.GetForegroundWindow(); WindowsAPI.GetClassName(focusW, _lpClassName, checked(_lpClassName.Capacity + 1)); left = _lpClassName.ToString().ToLower(); - // 狂点入vs是 hwndwrapper - // 狂点入qq是 txguifoundation - Debug.WriteLine($"afx...{left}...{DateTime.Now}"); + // cad08启动时候会滚动某些信息,此时鼠标狂点入到vs代码编辑器中,然后等一段时间cad完成,vs就会无法输入了. + // 会被拦截到了这个"afx"处理,所有的输入都跑cad了,需要加入如下代码进行处理: + // 狂点鼠标进入vs是 hwndwrapper + // 狂点鼠标进入qq是 txguifoundation + // Debugx.Printl($"afx...{left}...{DateTime.Now}"); if (!left.StartsWith("afx")) + { + Debugx.Printl($"afx...拦截...{DateTime.Now}"); return false; + } } - WindowsAPI.PostMessage(focus, WM_KEYDOWN, new IntPtr(wParam), new IntPtr(0x10001)); return true; } - if (left.StartsWith("hwndwrapper"))//cad21会进入,高版本的命令提示器? + if (left.StartsWith("hwndwrapper"))//cad21会进入 { - Debug.WriteLine($"hwndwrapper::{DateTime.Now}"); + Debugx.Printl($"hwndwrapper::{DateTime.Now}"); var parent = WindowsAPI.GetParent(focus); if (parent == IntPtr.Zero) @@ -303,21 +344,24 @@ public static bool IMEHook(int nCode, int wParam, IntPtr lParam) if (left.StartsWith("edit")) { - Debug.WriteLine($"edit::{DateTime.Now}"); + Debugx.Printl($"edit::{DateTime.Now}"); var parent = WindowsAPI.GetParent(focus); WindowsAPI.GetClassName(parent, _lpClassName, checked(_lpClassName.Capacity + 1)); + + var dm = Acap.DocumentManager; + var doc = dm.MdiActiveDocument; if (_lpClassName.ToString().ToLower().StartsWith("afx") && WindowsAPI.GetParent(parent) != doc.Window.Handle) { - WindowsAPI.PostMessage(focus, WM_KEYDOWN, new IntPtr(wParam), new IntPtr(3735553)); + WindowsAPI.PostMessage(focus, WM_KEYDOWN, new IntPtr(wParam), new IntPtr(0x390001/*3735553*/)); return true; } } if (left == "cicerouiwndframe") { - Debug.WriteLine($"cicerouiwndframe::{DateTime.Now}"); + Debugx.Printl($"cicerouiwndframe::{DateTime.Now}"); return true; } } diff --git "a/tests/TestAcad09plus/\350\276\223\345\205\245\346\263\225\350\207\252\345\212\250\345\210\207\346\215\242/\350\265\253\346\200\235\350\276\223\345\205\245\346\263\225/Settings.cs" "b/tests/TestAcad09plus/\350\276\223\345\205\245\346\263\225\350\207\252\345\212\250\345\210\207\346\215\242/\350\265\253\346\200\235\350\276\223\345\205\245\346\263\225/Settings.cs" index 9ccfbac..141629c 100644 --- "a/tests/TestAcad09plus/\350\276\223\345\205\245\346\263\225\350\207\252\345\212\250\345\210\207\346\215\242/\350\265\253\346\200\235\350\276\223\345\205\245\346\263\225/Settings.cs" +++ "b/tests/TestAcad09plus/\350\276\223\345\205\245\346\263\225\350\207\252\345\212\250\345\210\207\346\215\242/\350\265\253\346\200\235\350\276\223\345\205\245\346\263\225/Settings.cs" @@ -129,7 +129,7 @@ internal static void SaveSettings() NewLineChars = Environment.NewLine }; - XmlWriter xmlWriter = XmlWriter.Create(MySettingsPath, settings); + using var xmlWriter = XmlWriter.Create(MySettingsPath, settings); xmlWriter.WriteStartDocument(1 != 0); xmlWriter.WriteComment("输入法+"); @@ -153,7 +153,6 @@ internal static void SaveSettings() } xmlWriter.WriteEndElement(); xmlWriter.WriteEndDocument(); - xmlWriter.Close(); } catch (Exception ex) { diff --git "a/tests/TestAcad09plus/\350\276\223\345\205\245\346\263\225\350\207\252\345\212\250\345\210\207\346\215\242/\350\265\253\346\200\235\350\276\223\345\205\245\346\263\225/Window/SettingsWindow.xaml.cs" "b/tests/TestAcad09plus/\350\276\223\345\205\245\346\263\225\350\207\252\345\212\250\345\210\207\346\215\242/\350\265\253\346\200\235\350\276\223\345\205\245\346\263\225/Window/SettingsWindow.xaml.cs" index 7fb0580..6a28b49 100644 --- "a/tests/TestAcad09plus/\350\276\223\345\205\245\346\263\225\350\207\252\345\212\250\345\210\207\346\215\242/\350\265\253\346\200\235\350\276\223\345\205\245\346\263\225/Window/SettingsWindow.xaml.cs" +++ "b/tests/TestAcad09plus/\350\276\223\345\205\245\346\263\225\350\207\252\345\212\250\345\210\207\346\215\242/\350\265\253\346\200\235\350\276\223\345\205\245\346\263\225/Window/SettingsWindow.xaml.cs" @@ -21,6 +21,7 @@ private void Button_Click(object sender, RoutedEventArgs e) Settings._IMEStyle = (IMEHookStyle)CBox.SelectedIndex; Settings.SaveSettings(); IMEControl.SetIMEHook(); + IMEControl.SaveFt(); DialogResult = true; } @@ -28,7 +29,7 @@ private void Window_Loaded(object sender, RoutedEventArgs e) { CBox.SelectedIndex = (int)Settings.IMEStyle; ExCMD.Text = Settings.UserFilter; - DeCMD.Text = string.Join(",", IMEControl.DefaultCMDs.ToArray()); + DeCMD.Text = string.Join(",", IMEControl.DefaultCmds.ToArray()); } private void Window_PreviewKeyDown(object sender, KeyEventArgs e) diff --git a/tests/TestShared/CmdINI.cs b/tests/TestShared/CmdINI.cs index 983e7a7..5819bf7 100644 --- a/tests/TestShared/CmdINI.cs +++ b/tests/TestShared/CmdINI.cs @@ -99,7 +99,7 @@ public void Initialize2() // // 您可以写一些其他的释放动作,例如资源回收之类的 // Env.Printl($"\n 结束自动执行 Terminate \r\n"); // // 改用 - // Debug.WriteLine($"\n 结束自动执行 Terminate \r\n"); + // Debugx.Printl($"\n 结束自动执行 Terminate \r\n"); // } // catch (System.Exception e) // { @@ -138,7 +138,7 @@ public void Initialize() public void Terminate() { - Debug.WriteLine($"开始自动执行,{nameof(IFoxAutoGo)}接口调用:{nameof(Terminate)}::{TestInt}"); + Debugx.Printl($"开始自动执行,{nameof(IFoxAutoGo)}接口调用:{nameof(Terminate)}::{TestInt}"); // try // { // // 注意此时编辑器已经回收,所以此句没用,并引发错误 diff --git a/tests/TestShared/Copyclip.cs b/tests/TestShared/Copyclip.cs index 1cc63ba..47a0426 100644 --- a/tests/TestShared/Copyclip.cs +++ b/tests/TestShared/Copyclip.cs @@ -589,13 +589,13 @@ void Paste(bool isBlock) catch (Exception e) { Debugger.Break(); - Debug.WriteLine(e.ToString()); + Debugx.Printl(e); } } catch (Exception e)//{"剪贴板上的数据无效 (异常来自 HRESULT:0x800401D3 (CLIPBRD_E_BAD_DATA))"} { Debugger.Break(); - Debug.WriteLine(e.ToString()); + Debugx.Printl(e); } finally { diff --git a/tests/TestShared/TestMarshal.cs b/tests/TestShared/TestMarshal.cs index 755cb0e..dff1303 100644 --- a/tests/TestShared/TestMarshal.cs +++ b/tests/TestShared/TestMarshal.cs @@ -58,11 +58,11 @@ public void Test_ImplicitPoint3D() { Point3d pt1 = new(1, 56, 89); var a1 = (Point3D*)&pt1; - Debug.WriteLine("指针类型转换,获取x::" + a1->X); + Debugx.Printl("指针类型转换,获取x::" + a1->X); var pt2 = Point3D.Create(new IntPtr(&pt1)); - Debug.WriteLine("pt1地址::" + (int)&pt1); - Debug.WriteLine("pt2地址::" + (int)&pt2); + Debugx.Printl("pt1地址::" + (int)&pt1); + Debugx.Printl("pt2地址::" + (int)&pt2); Debug.Assert(&pt1 == &pt2);//不相等,是申请了新内存 } } diff --git a/tests/TestShared/TestPoint.cs b/tests/TestShared/TestPoint.cs index 72c0adb..f3ba101 100644 --- a/tests/TestShared/TestPoint.cs +++ b/tests/TestShared/TestPoint.cs @@ -29,7 +29,7 @@ public void Test_PtSortedSet() foreach (var item in ss1) { if (item.X > 3 && item.X < 7) - Debug.WriteLine(item); + Debugx.Printl(item); else if (item.X >= 7) break; } -- Gitee From e9631b60706d2cd68a86521812719c4c010656c7 Mon Sep 17 00:00:00 2001 From: liuqihong <540762622@qq.com> Date: Thu, 3 Nov 2022 02:31:30 +0800 Subject: [PATCH 607/675] =?UTF-8?q?=E5=A2=9E=E5=8A=A0StatusBar=E6=B5=8B?= =?UTF-8?q?=E8=AF=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/IFoxCAD.Cad.Shared/Runtime/LateBinding.cs | 39 ++++++ .../DefaultAssemblyResolve.cs | 3 + tests/TestShared/TestShared.projitems | 1 + tests/TestShared/TestStatusBar.cs | 129 ++++++++++++++++++ 4 files changed, 172 insertions(+) create mode 100644 tests/TestShared/TestStatusBar.cs diff --git a/src/IFoxCAD.Cad.Shared/Runtime/LateBinding.cs b/src/IFoxCAD.Cad.Shared/Runtime/LateBinding.cs index 21df19c..bb12125 100644 --- a/src/IFoxCAD.Cad.Shared/Runtime/LateBinding.cs +++ b/src/IFoxCAD.Cad.Shared/Runtime/LateBinding.cs @@ -5,36 +5,75 @@ ///
    public static class LateBinding { + /// + /// 从运行对象表 (ROT) 获取指定对象的运行实例 + /// + /// + /// public static object GetInstance(string appName) { return Marshal.GetActiveObject(appName); } + /// + /// 创建实例 + /// + /// + /// public static object CreateInstance(string appName) { return Activator.CreateInstance(Type.GetTypeFromProgID(appName)); } + /// + /// 获取或创建实例 + /// + /// + /// public static object GetOrCreateInstance(string appName) { try { return GetInstance(appName); } catch { return CreateInstance(appName); } } + /// + /// 释放实例 + /// + /// public static void ReleaseInstance(this object obj) { Marshal.ReleaseComObject(obj); } + /// + /// 获取属性 + /// + /// + /// + /// + /// public static object GetProperty(this object obj, string propName, params object[] parameter) { return obj.GetType().InvokeMember(propName, BindingFlags.GetProperty, null, obj, parameter); } + /// + /// 设置属性 + /// + /// + /// + /// public static void SetProperty(this object obj, string propName, params object[] parameter) { obj.GetType().InvokeMember(propName, BindingFlags.SetProperty, null, obj, parameter); } + /// + /// 执行函数 + /// + /// + /// + /// + /// public static object Invoke(this object obj, string memberName, params object[] parameter) { return obj.GetType().InvokeMember(memberName, diff --git a/src/IFoxCAD.LoadForm/DefaultAssemblyResolve.cs b/src/IFoxCAD.LoadForm/DefaultAssemblyResolve.cs index 4fc2129..7426ce4 100644 --- a/src/IFoxCAD.LoadForm/DefaultAssemblyResolve.cs +++ b/src/IFoxCAD.LoadForm/DefaultAssemblyResolve.cs @@ -51,6 +51,9 @@ public class AssemblyHelper for (int i = 0; i < cadAss.Length; i++) sb.AppendLine("-------匹配对象:: " + GetAssemblyName(cadAss[i].GetName().FullName)); Debug.WriteLine(sb.ToString()); + + Debug.WriteLine("无法处理的错误,杀死当前进程"); + System.Diagnostics.Process.GetCurrentProcess().Kill(); //Debugger.Break(); } return result; diff --git a/tests/TestShared/TestShared.projitems b/tests/TestShared/TestShared.projitems index 7c5a772..accfb91 100644 --- a/tests/TestShared/TestShared.projitems +++ b/tests/TestShared/TestShared.projitems @@ -9,6 +9,7 @@ TestShared + diff --git a/tests/TestShared/TestStatusBar.cs b/tests/TestShared/TestStatusBar.cs new file mode 100644 index 0000000..4eaf33c --- /dev/null +++ b/tests/TestShared/TestStatusBar.cs @@ -0,0 +1,129 @@ +namespace Test; +using Autodesk.AutoCAD.Windows; +using IFoxCAD.Cad; + +public class TestStatusBar +{ + static string _name = nameof(Gstar_IMEFilter); + static Pane? _pane = null; + + [IFoxInitialize] + [CommandMethod(nameof(TestAddPane))] + public static void TestAddPane() + { + if (_pane is not null) + return; + + // 遍历当前 + var panes = Acap.StatusBar.Panes; + for (int i = 0; i < panes.Count(); i++) + { + if (panes[i].ToolTipText == _name) + { + _pane = panes[i]; + break; + } + } + + if (_pane is not null) + return; + + // 没有找到的话,进行初始化 + _pane = new() + { + ToolTipText = _name, + Text = "打开", + Style = PaneStyles.Command | PaneStyles.PopUp/* | PaneStyles.NoBorders | PaneStyles.Stretch | PaneStyles.PopUp|PaneStyles.PopOut*/, + }; + _pane.MouseDown += Pane_MouseDown; + panes.Insert(0, _pane); + Acap.StatusBar.Update(); + } + + // 删除Pane + [CommandMethod(nameof(TestRemovePane))] + public static void TestRemovePane() + { + if (_pane is null) + return; + + // cad08需要用这样的方式才能保证移除后更新界面 + var panes = Acap.StatusBar.Panes; + for (int i = panes.Count() - 1; i >= 0; i--) + if (panes[i] == _pane) + panes.RemoveAt(i); + _pane.Dispose(); + _pane = null; + + // cad08用这样的方式移除后无法更新界面,而高版本可以 + //Acap.StatusBar.Panes.Remove(_pane); + //Acap.StatusBar.Update(); + //_pane.Dispose(); + //_pane = null; + //Acap.UpdateScreen(); + //RedrawEx.Redraw(); + //System.Windows.Forms.Application.DoEvents(); + } + + [CommandMethod(nameof(PaneSwitch))] + public static void PaneSwitch() + { + if (_pane is null) + TestAddPane(); + else + TestRemovePane(); + } + + /// + /// 改变状态既能命令又能点击 + /// + [CommandMethod(nameof(ChangePaneType))] + public static void ChangePaneType() + { + if (_pane is null) + return; + _pane.Text = _pane.Text == "打开" ? "关闭" : "打开"; + //cad08要加这个才会变字,而高版本不用 + Acap.StatusBar.Update(); + } + + static void Pane_MouseDown(object sender, StatusBarMouseDownEventArgs e) + { + switch (e.Button) + { + case System.Windows.Forms.MouseButtons.Left: + { + ChangePaneType(); + Env.Printl("按了左键"); + } + break; + case System.Windows.Forms.MouseButtons.None: + break; + case System.Windows.Forms.MouseButtons.Right: + { + Env.Printl("按了右键,弹菜单"); + } + break; + case System.Windows.Forms.MouseButtons.Middle: + break; + case System.Windows.Forms.MouseButtons.XButton1: + break; + case System.Windows.Forms.MouseButtons.XButton2: + break; + default: + break; + } + } +} + +public static class PaneHelper +{ + public static int Count(this PaneCollection panes) + { +#if NET35 + return panes.get_Count(); +#else + return panes.Count; +#endif + } +} \ No newline at end of file -- Gitee From 57802a8123c66924409764c933875d29f6eca116 Mon Sep 17 00:00:00 2001 From: liuqihong <540762622@qq.com> Date: Thu, 3 Nov 2022 09:28:04 +0800 Subject: [PATCH 608/675] =?UTF-8?q?=E7=94=A8=E4=B8=80=E4=B8=AAdebugx?= =?UTF-8?q?=E7=9A=84=E7=8E=AF=E5=A2=83=E5=8F=98=E9=87=8F=E6=8E=A7=E5=88=B6?= =?UTF-8?q?vs=E7=9A=84=E8=BE=93=E5=87=BA?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/IFoxCAD.Basal/General/DebugHelper.cs | 7 +++++++ src/IFoxCAD.Cad.Shared/Runtime/Env.cs | 2 +- src/IFoxCAD.Cad.Shared/Runtime/IAutoGo.cs | 2 +- tests/TestShared/CmdINI.cs | 16 ++++++++++++++++ 4 files changed, 25 insertions(+), 2 deletions(-) diff --git a/src/IFoxCAD.Basal/General/DebugHelper.cs b/src/IFoxCAD.Basal/General/DebugHelper.cs index aba6955..40fb904 100644 --- a/src/IFoxCAD.Basal/General/DebugHelper.cs +++ b/src/IFoxCAD.Basal/General/DebugHelper.cs @@ -2,8 +2,15 @@ public static class Debugx { + /// + /// cad命令切换: IFoxDebugx + /// + /// public static void Printl(object message) { + var flag = Environment.GetEnvironmentVariable("debugx", EnvironmentVariableTarget.User); + if (flag == null || flag == "0") + return; var str = message + $"::{DateTime.Now.ToLongDateString() + DateTime.Now.TimeOfDay}"; #if DEBUG System.Diagnostics.Debug.WriteLine(str); diff --git a/src/IFoxCAD.Cad.Shared/Runtime/Env.cs b/src/IFoxCAD.Cad.Shared/Runtime/Env.cs index 6702418..f80a4ee 100644 --- a/src/IFoxCAD.Cad.Shared/Runtime/Env.cs +++ b/src/IFoxCAD.Cad.Shared/Runtime/Env.cs @@ -517,7 +517,7 @@ public static void SetVar(string? varName, object? value, bool echo = true) public static string GetEnv(string? name) { // 它将混合查询以下路径: - // acad2008注册表路径: 计算机\HKEY_CURRENT_USER\SOFTWARE\Autodesk\AutoCAD\R17.1\ACAD - 6001:804\FixedProfile\General + // acad2008注册表路径: 计算机\HKEY_CURRENT_USER\SOFTWARE\Autodesk\AutoCAD\R17.1\ACAD-6001:804\FixedProfile\General // 用户: 计算机\HKEY_CURRENT_USER\Environment // 系统: 计算机\HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\Session Manager\Environment diff --git a/src/IFoxCAD.Cad.Shared/Runtime/IAutoGo.cs b/src/IFoxCAD.Cad.Shared/Runtime/IAutoGo.cs index e908897..738ba19 100644 --- a/src/IFoxCAD.Cad.Shared/Runtime/IAutoGo.cs +++ b/src/IFoxCAD.Cad.Shared/Runtime/IAutoGo.cs @@ -132,7 +132,7 @@ public void Initialize() RunFunctions(_InitializeList); } } - catch (System.Exception) + catch (System.Exception e) { Debugger.Break(); } diff --git a/tests/TestShared/CmdINI.cs b/tests/TestShared/CmdINI.cs index 5819bf7..c5679cf 100644 --- a/tests/TestShared/CmdINI.cs +++ b/tests/TestShared/CmdINI.cs @@ -68,6 +68,22 @@ public void IFoxRemoveReg() AutoRegAssemEx?.UnRegApp(); AutoRegAssemEx = null; } + + [CommandMethod(nameof(Debugx))] + public void Debugx() + { + var flag = Environment.GetEnvironmentVariable("debugx", EnvironmentVariableTarget.User); + if (flag == null || flag == "0") + { + Environment.SetEnvironmentVariable("debugx", "1", EnvironmentVariableTarget.User); + Env.Printl($"vs输出 -- 已启用"); + } + else + { + Environment.SetEnvironmentVariable("debugx", "0", EnvironmentVariableTarget.User); + Env.Printl($"vs输出 -- 已禁用"); + } + } } #if givePeopleTest -- Gitee From 4e8ce8515af7b87d872d512ed6e89c08b188170a Mon Sep 17 00:00:00 2001 From: liuqihong <540762622@qq.com> Date: Thu, 3 Nov 2022 09:30:00 +0800 Subject: [PATCH 609/675] =?UTF-8?q?=E8=BE=93=E5=85=A5=E6=B3=95=E5=A2=9E?= =?UTF-8?q?=E5=8A=A0StatusBar=E6=8E=A7=E5=88=B6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Cmd.cs" | 36 +------ .../DocReactor.cs" | 2 +- .../IMEControl.cs" | 16 +-- .../Settings.cs" | 98 +++++++++---------- .../StatusBar.cs" | 79 +++++++-------- .../Window/SettingsWindow.xaml.cs" | 4 +- tests/TestShared/TestShared.projitems | 1 - 7 files changed, 93 insertions(+), 143 deletions(-) rename tests/TestShared/TestStatusBar.cs => "tests/TestAcad09plus/\350\276\223\345\205\245\346\263\225\350\207\252\345\212\250\345\210\207\346\215\242/\350\265\253\346\200\235\350\276\223\345\205\245\346\263\225/StatusBar.cs" (53%) diff --git "a/tests/TestAcad09plus/\350\276\223\345\205\245\346\263\225\350\207\252\345\212\250\345\210\207\346\215\242/\350\265\253\346\200\235\350\276\223\345\205\245\346\263\225/Cmd.cs" "b/tests/TestAcad09plus/\350\276\223\345\205\245\346\263\225\350\207\252\345\212\250\345\210\207\346\215\242/\350\265\253\346\200\235\350\276\223\345\205\245\346\263\225/Cmd.cs" index 94991f7..df61f91 100644 --- "a/tests/TestAcad09plus/\350\276\223\345\205\245\346\263\225\350\207\252\345\212\250\345\210\207\346\215\242/\350\265\253\346\200\235\350\276\223\345\205\245\346\263\225/Cmd.cs" +++ "b/tests/TestAcad09plus/\350\276\223\345\205\245\346\263\225\350\207\252\345\212\250\345\210\207\346\215\242/\350\265\253\346\200\235\350\276\223\345\205\245\346\263\225/Cmd.cs" @@ -5,10 +5,7 @@ public class Cmd [IFoxInitialize] public void Initialize() { - Env.Printl($"※输入法+控制※" + - $"\n{nameof(Gstar_IMEFilter1)} - 启用拦截开关" + - $"\n{nameof(Gstar_IMEFilter2)} - 输入法切换模式" + - $"\n{nameof(Gstar_IMEFilterSettings)} - 设置"); + Env.Printl($"※输入法+控制※{nameof(Gstar_IMEFilterSettings)} - 设置"); DocReactor.IntialReactor(); Settings.LoadSettings(); IMEControl.SetIMEHook(); @@ -16,36 +13,7 @@ public void Initialize() IMEControl.UnIMEHook(); IMEControl.SaveFt(); }; - } - - [CommandMethod(nameof(Gstar_IMEFilter1))] - public void Gstar_IMEFilter1() - { - Settings.Use = !Settings.Use; - Env.Printl("已经 " + (Settings.Use ? "开启" : "禁用") + " 输入法+"); - } - - [CommandMethod(nameof(Gstar_IMEFilter2))] - public void Gstar_IMEFilter2() - { - // 存在重复的关键字首字母,所以用数字代替 - PromptIntegerOptions options = new($"\n输入法切换模式: "); - foreach (var suit in Enum.GetValues(typeof(IMESwitchMode))) - options.Keywords.Add($"{suit}", $"{suit}", $"{suit}({(int)suit})", true, true); - options.Keywords.Default = Settings.IMEInputSwitch.ToString(); - - var result = Env.Editor.GetInteger(options); - if (result.Status != PromptStatus.OK) - return; - - var max = Enum.GetValues(typeof(IMESwitchMode)).Cast().Max(); - if (result.Value > max) - { - Env.Printl("设置失败:数值过大"); - return; - } - Settings.IMEInputSwitch = (IMESwitchMode)result.Value; - Env.Printl($"设置为: {Settings.IMEInputSwitch}"); + StatusBar.IMEAddPane(); } [CommandMethod(nameof(Gstar_IMEFilterSettings))] diff --git "a/tests/TestAcad09plus/\350\276\223\345\205\245\346\263\225\350\207\252\345\212\250\345\210\207\346\215\242/\350\265\253\346\200\235\350\276\223\345\205\245\346\263\225/DocReactor.cs" "b/tests/TestAcad09plus/\350\276\223\345\205\245\346\263\225\350\207\252\345\212\250\345\210\207\346\215\242/\350\265\253\346\200\235\350\276\223\345\205\245\346\263\225/DocReactor.cs" index 8586b85..a1ebce1 100644 --- "a/tests/TestAcad09plus/\350\276\223\345\205\245\346\263\225\350\207\252\345\212\250\345\210\207\346\215\242/\350\265\253\346\200\235\350\276\223\345\205\245\346\263\225/DocReactor.cs" +++ "b/tests/TestAcad09plus/\350\276\223\345\205\245\346\263\225\350\207\252\345\212\250\345\210\207\346\215\242/\350\265\253\346\200\235\350\276\223\345\205\245\346\263\225/DocReactor.cs" @@ -29,7 +29,7 @@ static void DocumentCreated(object sender, DocumentCollectionEventArgs e) static void CommandWillStart(object sender, CommandEventArgs e) { - if (!Settings.Use || ((Document)sender).Editor.IsQuiescentForTransparentCommand()) + if (Settings.IMEInputSwitch == 0 || ((Document)sender).Editor.IsQuiescentForTransparentCommand()) return; var gName = e.GlobalCommandName; diff --git "a/tests/TestAcad09plus/\350\276\223\345\205\245\346\263\225\350\207\252\345\212\250\345\210\207\346\215\242/\350\265\253\346\200\235\350\276\223\345\205\245\346\263\225/IMEControl.cs" "b/tests/TestAcad09plus/\350\276\223\345\205\245\346\263\225\350\207\252\345\212\250\345\210\207\346\215\242/\350\265\253\346\200\235\350\276\223\345\205\245\346\263\225/IMEControl.cs" index 0396e8a..ebff1ed 100644 --- "a/tests/TestAcad09plus/\350\276\223\345\205\245\346\263\225\350\207\252\345\212\250\345\210\207\346\215\242/\350\265\253\346\200\235\350\276\223\345\205\245\346\263\225/IMEControl.cs" +++ "b/tests/TestAcad09plus/\350\276\223\345\205\245\346\263\225\350\207\252\345\212\250\345\210\207\346\215\242/\350\265\253\346\200\235\350\276\223\345\205\245\346\263\225/IMEControl.cs" @@ -172,7 +172,7 @@ static void IMESwitch() internal static void SetIMEHook() { UnIMEHook(); - if (_nextHookProc != IntPtr.Zero || !Settings.Use) + if (_nextHookProc != IntPtr.Zero) return; ExceptCmds.Clear(); @@ -182,7 +182,7 @@ internal static void SetIMEHook() foreach (var item in ss) ExceptCmds.Add(item); - if (Settings.IMEStyle == IMEHookStyle.Process) + if (Settings.IMEHookStyle == IMEHookStyle.Process) { Debugx.Printl($"切换到进程钩子控制:{DateTime.Now}"); _hookProc = (nCode, wParam, lParam) => { @@ -213,7 +213,7 @@ internal static void SetIMEHook() return; } - if (Settings.IMEStyle == IMEHookStyle.Global) + if (Settings.IMEHookStyle == IMEHookStyle.Global) { Debugx.Printl($"切换到全局钩子控制:{DateTime.Now}"); var moduleHandle = WindowsAPI.GetModuleHandle(_process.MainModule.ModuleName); @@ -370,11 +370,11 @@ public static bool IMEHook(int nCode, int wParam, IntPtr lParam) static bool MK1(int wParam) { - return !Settings.Use || - WindowsAPI.IsIconic(Acap.MainWindow.Handle.ToInt32()) || - WindowsAPI.GetKeyState(91) < 0 || - WindowsAPI.GetKeyState(92) < 0 || - wParam != WM_KEYDOWN; + return Settings.IMEInputSwitch == 0 || + WindowsAPI.IsIconic(Acap.MainWindow.Handle.ToInt32()) || + WindowsAPI.GetKeyState(91) < 0 || + WindowsAPI.GetKeyState(92) < 0 || + wParam != WM_KEYDOWN; } static bool Mk2(int nCode, int wParam, IntPtr lParam) diff --git "a/tests/TestAcad09plus/\350\276\223\345\205\245\346\263\225\350\207\252\345\212\250\345\210\207\346\215\242/\350\265\253\346\200\235\350\276\223\345\205\245\346\263\225/Settings.cs" "b/tests/TestAcad09plus/\350\276\223\345\205\245\346\263\225\350\207\252\345\212\250\345\210\207\346\215\242/\350\265\253\346\200\235\350\276\223\345\205\245\346\263\225/Settings.cs" index 141629c..09e91a8 100644 --- "a/tests/TestAcad09plus/\350\276\223\345\205\245\346\263\225\350\207\252\345\212\250\345\210\207\346\215\242/\350\265\253\346\200\235\350\276\223\345\205\245\346\263\225/Settings.cs" +++ "b/tests/TestAcad09plus/\350\276\223\345\205\245\346\263\225\350\207\252\345\212\250\345\210\207\346\215\242/\350\265\253\346\200\235\350\276\223\345\205\245\346\263\225/Settings.cs" @@ -1,13 +1,13 @@ -using System.Xml; +using System.Diagnostics; +using System.Xml; namespace Gstar_IMEFilter; public class Settings { static string _MyDir = ""; static string _MySettingsPath = ""; - static bool _Use = true; internal static string _UserFilter = ""; - internal static IMEHookStyle _IMEStyle = IMEHookStyle.Global; + internal static IMEHookStyle _IMEHookStyle = IMEHookStyle.Global; internal static IMESwitchMode _IMEInputSwitch = IMESwitchMode.Shift; internal static string MyDir @@ -42,27 +42,14 @@ public static string UserFilter } } - public static bool Use + public static IMEHookStyle IMEHookStyle { - get => _Use; + get => _IMEHookStyle; set { - if (_Use == value) + if (_IMEHookStyle == value) return; - _Use = value; - SaveSettings(); - IMEControl.SetIMEHook(); - } - } - - public static IMEHookStyle IMEStyle - { - get => _IMEStyle; - set - { - if (_IMEStyle == value) - return; - _IMEStyle = value; + _IMEHookStyle = value; SaveSettings(); IMEControl.SetIMEHook(); } @@ -85,38 +72,41 @@ public static void LoadSettings() if (!File.Exists(MySettingsPath)) return; - using var xmlReader = XmlReader.Create(MySettingsPath); - while (xmlReader.Read()) + try { - if (xmlReader.NodeType != XmlNodeType.Element) - continue; - string left = xmlReader.Name.ToLower(); - switch (left) + using var xmlReader = XmlReader.Create(MySettingsPath); + while (xmlReader.Read()) { - case "use": - { - bool.TryParse(xmlReader.ReadInnerXml(), out _Use); - } - break; - case "userfilter": - { - _UserFilter = xmlReader.ReadInnerXml().ToUpper(); - } - break; - case "imestyle": + if (xmlReader.NodeType != XmlNodeType.Element) + continue; + string left = xmlReader.Name.ToLower(); + switch (left) { - int.TryParse(xmlReader.ReadInnerXml(), out int ime); - _IMEStyle = (IMEHookStyle)ime; + case "userfilter": + { + _UserFilter = xmlReader.ReadInnerXml().ToUpper(); + } + break; + case "imehookstyle": + { + int.TryParse(xmlReader.ReadInnerXml(), out int ime); + _IMEHookStyle = (IMEHookStyle)ime; + } + break; + case "imeinputswitch": + { + int.TryParse(xmlReader.ReadInnerXml(), out int ime); + _IMEInputSwitch = (IMESwitchMode)ime; + } + break; } - break; - case "imeinputswitch": - { - int.TryParse(xmlReader.ReadInnerXml(), out int ime); - _IMEInputSwitch = (IMESwitchMode)ime; - } - break; } } + catch (Exception ex) + { + Debugger.Break(); + throw ex; + } } internal static void SaveSettings() @@ -135,16 +125,12 @@ internal static void SaveSettings() xmlWriter.WriteStartElement("settings"); { - xmlWriter.WriteStartElement("use"); - xmlWriter.WriteString(Use.ToString()); - xmlWriter.WriteEndElement(); - xmlWriter.WriteStartElement("userfilter"); xmlWriter.WriteString(UserFilter); xmlWriter.WriteEndElement(); - xmlWriter.WriteStartElement("imestyle"); - xmlWriter.WriteString(((int)IMEStyle).ToString()); + xmlWriter.WriteStartElement("imehookstyle"); + xmlWriter.WriteString(((int)IMEHookStyle).ToString()); xmlWriter.WriteEndElement(); xmlWriter.WriteStartElement("imeinputswitch"); @@ -156,6 +142,7 @@ internal static void SaveSettings() } catch (Exception ex) { + Debugger.Break(); throw ex; } } @@ -175,9 +162,16 @@ public enum IMEHookStyle : byte ///
    public enum IMESwitchMode : byte { + [Description("输入法关闭")] + Disable, + [Description("输入法开启:Shift")] Shift, + [Description("输入法开启:Ctrl")] Ctrl, + [Description("输入法开启:CtrlAndSpace")] CtrlAndSpace, + [Description("输入法开启:CtrlAndShift")] CtrlAndShift, + [Description("输入法开启:WinAndSpace")] WinAndSpace, } \ No newline at end of file diff --git a/tests/TestShared/TestStatusBar.cs "b/tests/TestAcad09plus/\350\276\223\345\205\245\346\263\225\350\207\252\345\212\250\345\210\207\346\215\242/\350\265\253\346\200\235\350\276\223\345\205\245\346\263\225/StatusBar.cs" similarity index 53% rename from tests/TestShared/TestStatusBar.cs rename to "tests/TestAcad09plus/\350\276\223\345\205\245\346\263\225\350\207\252\345\212\250\345\210\207\346\215\242/\350\265\253\346\200\235\350\276\223\345\205\245\346\263\225/StatusBar.cs" index 4eaf33c..880ba87 100644 --- a/tests/TestShared/TestStatusBar.cs +++ "b/tests/TestAcad09plus/\350\276\223\345\205\245\346\263\225\350\207\252\345\212\250\345\210\207\346\215\242/\350\265\253\346\200\235\350\276\223\345\205\245\346\263\225/StatusBar.cs" @@ -1,15 +1,14 @@ -namespace Test; +namespace Gstar_IMEFilter; + using Autodesk.AutoCAD.Windows; -using IFoxCAD.Cad; +using System.Windows.Forms; -public class TestStatusBar +public class StatusBar { static string _name = nameof(Gstar_IMEFilter); static Pane? _pane = null; - [IFoxInitialize] - [CommandMethod(nameof(TestAddPane))] - public static void TestAddPane() + public static void IMEAddPane() { if (_pane is not null) return; @@ -32,17 +31,19 @@ public static void TestAddPane() _pane = new() { ToolTipText = _name, - Text = "打开", - Style = PaneStyles.Command | PaneStyles.PopUp/* | PaneStyles.NoBorders | PaneStyles.Stretch | PaneStyles.PopUp|PaneStyles.PopOut*/, + Text = GetUseText(), + Style = PaneStyles.Command | PaneStyles.PopUp, + /* PaneStyles.NoBorders | + * PaneStyles.Stretch | + * PaneStyles.PopOut + */ }; _pane.MouseDown += Pane_MouseDown; panes.Insert(0, _pane); Acap.StatusBar.Update(); } - // 删除Pane - [CommandMethod(nameof(TestRemovePane))] - public static void TestRemovePane() + public static void IMERemovePane() { if (_pane is null) return; @@ -65,53 +66,41 @@ public static void TestRemovePane() //System.Windows.Forms.Application.DoEvents(); } - [CommandMethod(nameof(PaneSwitch))] - public static void PaneSwitch() - { - if (_pane is null) - TestAddPane(); - else - TestRemovePane(); - } - - /// - /// 改变状态既能命令又能点击 - /// - [CommandMethod(nameof(ChangePaneType))] - public static void ChangePaneType() + static string GetUseText() { - if (_pane is null) - return; - _pane.Text = _pane.Text == "打开" ? "关闭" : "打开"; - //cad08要加这个才会变字,而高版本不用 - Acap.StatusBar.Update(); + return EnumEx.GetDesc(Settings.IMEInputSwitch); } static void Pane_MouseDown(object sender, StatusBarMouseDownEventArgs e) { + // 它就只支持两个枚举 switch (e.Button) { - case System.Windows.Forms.MouseButtons.Left: + case MouseButtons.Left: { - ChangePaneType(); - Env.Printl("按了左键"); + if (_pane is null) + return; + + var max = Enum.GetValues(typeof(IMESwitchMode)).Cast().Max(); + // 防白痴,一直点选择模式,最后是关闭.右键可以直接关闭 + if ((int)Settings.IMEInputSwitch + 1 < max) + ++Settings.IMEInputSwitch; + else + Settings.IMEInputSwitch = 0; + _pane.Text = GetUseText(); + Acap.StatusBar.Update(); } break; - case System.Windows.Forms.MouseButtons.None: - break; - case System.Windows.Forms.MouseButtons.Right: + case MouseButtons.Right: { - Env.Printl("按了右键,弹菜单"); + if (_pane is null) + return; + + Settings.IMEInputSwitch = 0; + _pane.Text = GetUseText(); + Acap.StatusBar.Update(); } break; - case System.Windows.Forms.MouseButtons.Middle: - break; - case System.Windows.Forms.MouseButtons.XButton1: - break; - case System.Windows.Forms.MouseButtons.XButton2: - break; - default: - break; } } } diff --git "a/tests/TestAcad09plus/\350\276\223\345\205\245\346\263\225\350\207\252\345\212\250\345\210\207\346\215\242/\350\265\253\346\200\235\350\276\223\345\205\245\346\263\225/Window/SettingsWindow.xaml.cs" "b/tests/TestAcad09plus/\350\276\223\345\205\245\346\263\225\350\207\252\345\212\250\345\210\207\346\215\242/\350\265\253\346\200\235\350\276\223\345\205\245\346\263\225/Window/SettingsWindow.xaml.cs" index 6a28b49..ff280a9 100644 --- "a/tests/TestAcad09plus/\350\276\223\345\205\245\346\263\225\350\207\252\345\212\250\345\210\207\346\215\242/\350\265\253\346\200\235\350\276\223\345\205\245\346\263\225/Window/SettingsWindow.xaml.cs" +++ "b/tests/TestAcad09plus/\350\276\223\345\205\245\346\263\225\350\207\252\345\212\250\345\210\207\346\215\242/\350\265\253\346\200\235\350\276\223\345\205\245\346\263\225/Window/SettingsWindow.xaml.cs" @@ -18,7 +18,7 @@ public SettingsWindow() private void Button_Click(object sender, RoutedEventArgs e) { Settings._UserFilter = ExCMD.Text; - Settings._IMEStyle = (IMEHookStyle)CBox.SelectedIndex; + Settings._IMEHookStyle = (IMEHookStyle)CBox.SelectedIndex; Settings.SaveSettings(); IMEControl.SetIMEHook(); IMEControl.SaveFt(); @@ -27,7 +27,7 @@ private void Button_Click(object sender, RoutedEventArgs e) private void Window_Loaded(object sender, RoutedEventArgs e) { - CBox.SelectedIndex = (int)Settings.IMEStyle; + CBox.SelectedIndex = (int)Settings.IMEHookStyle; ExCMD.Text = Settings.UserFilter; DeCMD.Text = string.Join(",", IMEControl.DefaultCmds.ToArray()); } diff --git a/tests/TestShared/TestShared.projitems b/tests/TestShared/TestShared.projitems index accfb91..7c5a772 100644 --- a/tests/TestShared/TestShared.projitems +++ b/tests/TestShared/TestShared.projitems @@ -9,7 +9,6 @@ TestShared - -- Gitee From 0c923332f20b6294e91a24387181504445854db5 Mon Sep 17 00:00:00 2001 From: liuqihong <540762622@qq.com> Date: Thu, 3 Nov 2022 10:53:24 +0800 Subject: [PATCH 610/675] =?UTF-8?q?=E4=BF=AE=E5=A4=8Dbug(#I5VHQE)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../ExtensionMethod/DBObjectEx.cs | 2 +- tests/TestShared/TestAddEntity.cs | 35 +++++++++---------- 2 files changed, 17 insertions(+), 20 deletions(-) diff --git a/src/IFoxCAD.Cad.Shared/ExtensionMethod/DBObjectEx.cs b/src/IFoxCAD.Cad.Shared/ExtensionMethod/DBObjectEx.cs index 91d9160..9733d94 100644 --- a/src/IFoxCAD.Cad.Shared/ExtensionMethod/DBObjectEx.cs +++ b/src/IFoxCAD.Cad.Shared/ExtensionMethod/DBObjectEx.cs @@ -69,7 +69,7 @@ public static void ChangeXData(this DBObject obj, string appName, DxfCode dxfCod return; for (int i = indexs.Count - 1; i >= 0; i--) - data[i] = new TypedValue((short)dxfCode, newvalue); + data[indexs[i]] = new TypedValue((short)dxfCode, newvalue); using (obj.ForWrite()) obj.XData = data; diff --git a/tests/TestShared/TestAddEntity.cs b/tests/TestShared/TestAddEntity.cs index 87884fe..3df018f 100644 --- a/tests/TestShared/TestAddEntity.cs +++ b/tests/TestShared/TestAddEntity.cs @@ -188,6 +188,7 @@ public void Test_AddPolyline2() // 测试扩展数据 + static readonly string _appname = "myapp2"; // 增 [CommandMethod(nameof(Test_AddXdata))] public void Test_AddXdata() @@ -225,7 +226,6 @@ public void Test_AddXdata() [CommandMethod(nameof(Test_RemoveXdata))] public void Test_RemoveXdata() { - var appname = "myapp2"; var res = Env.Editor.GetEntity("\n select the entity:"); if (res.Status == PromptStatus.OK) { @@ -236,10 +236,10 @@ public void Test_RemoveXdata() Env.Printl("\n移除前:" + ent.XData.ToString()); - ent.RemoveXData(appname, DxfCode.ExtendedDataAsciiString); + ent.RemoveXData(_appname, DxfCode.ExtendedDataAsciiString); Env.Printl("\n移除成员后:" + ent.XData.ToString()); - ent.RemoveXData(appname); + ent.RemoveXData(_appname); Env.Printl("\n移除appName后:" + ent.XData.ToString()); } } @@ -264,7 +264,7 @@ public void Test_GetXdata() // } // 查询appName里面是否含有某个 - var appname = "myapp2"; + var res = Env.Editor.GetEntity("\n select the entity:"); if (res.Status == PromptStatus.OK) { @@ -273,13 +273,13 @@ public void Test_GetXdata() return; XDataList data = ent.XData; - if (data.Contains(appname)) - Env.Printl("含有appName:" + appname); + if (data.Contains(_appname)) + Env.Printl("含有appName:" + _appname); else - Env.Printl("不含有appName:" + appname); + Env.Printl("不含有appName:" + _appname); var str = "要移除的我"; - if (data.Contains(appname, str)) + if (data.Contains(_appname, str)) Env.Printl("含有内容:" + str); else Env.Printl("不含有内容:" + str); @@ -289,23 +289,20 @@ public void Test_GetXdata() [CommandMethod(nameof(Test_ChangeXdata))] public void Test_ChangeXdata() { - var appname = "myapp"; var res = Env.Editor.GetEntity("\n select the entity:"); - if (res.Status == PromptStatus.OK) - { - using DBTrans tr = new(); - var data = tr.GetObject(res.ObjectId)!; - data.ChangeXData(appname, DxfCode.ExtendedDataAsciiString, "change"); + if (res.Status != PromptStatus.OK) + return; + using DBTrans tr = new(); + var data = tr.GetObject(res.ObjectId)!; + data.ChangeXData(_appname, DxfCode.ExtendedDataAsciiString, "change"); - Env.Printl(data.XData.ToString()); - } + if (data.XData == null) + return; + Env.Printl(data.XData.ToString()); } - - - [CommandMethod(nameof(Test_PrintLayerName))] public void Test_PrintLayerName() { -- Gitee From 5f0bac08fb8b2ca4d1e2894bc36324f64f05e442 Mon Sep 17 00:00:00 2001 From: liuqihong <540762622@qq.com> Date: Thu, 3 Nov 2022 11:08:46 +0800 Subject: [PATCH 611/675] =?UTF-8?q?=E4=BF=AE=E5=A4=8D=E4=BA=86=E6=B2=A1?= =?UTF-8?q?=E6=9C=89WinAndSpace?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Settings.cs" | 15 ++++++++------- .../StatusBar.cs" | 4 ++-- 2 files changed, 10 insertions(+), 9 deletions(-) diff --git "a/tests/TestAcad09plus/\350\276\223\345\205\245\346\263\225\350\207\252\345\212\250\345\210\207\346\215\242/\350\265\253\346\200\235\350\276\223\345\205\245\346\263\225/Settings.cs" "b/tests/TestAcad09plus/\350\276\223\345\205\245\346\263\225\350\207\252\345\212\250\345\210\207\346\215\242/\350\265\253\346\200\235\350\276\223\345\205\245\346\263\225/Settings.cs" index 09e91a8..ef0e452 100644 --- "a/tests/TestAcad09plus/\350\276\223\345\205\245\346\263\225\350\207\252\345\212\250\345\210\207\346\215\242/\350\265\253\346\200\235\350\276\223\345\205\245\346\263\225/Settings.cs" +++ "b/tests/TestAcad09plus/\350\276\223\345\205\245\346\263\225\350\207\252\345\212\250\345\210\207\346\215\242/\350\265\253\346\200\235\350\276\223\345\205\245\346\263\225/Settings.cs" @@ -158,20 +158,21 @@ public enum IMEHookStyle : byte } /// -/// 切换输入法方式 +/// 切换输入法方式
    +/// 作用的地方仅为输入豁免命令时候自动切换到中文 ///
    public enum IMESwitchMode : byte { - [Description("输入法关闭")] + [Description("输入法拦截关闭")] Disable, - [Description("输入法开启:Shift")] + [Description("输入法拦截开启:Shift")] Shift, - [Description("输入法开启:Ctrl")] + [Description("输入法拦截开启:Ctrl")] Ctrl, - [Description("输入法开启:CtrlAndSpace")] + [Description("输入法拦截开启:CtrlAndSpace")] CtrlAndSpace, - [Description("输入法开启:CtrlAndShift")] + [Description("输入法拦截开启:CtrlAndShift")] CtrlAndShift, - [Description("输入法开启:WinAndSpace")] + [Description("输入法拦截开启:WinAndSpace")] WinAndSpace, } \ No newline at end of file diff --git "a/tests/TestAcad09plus/\350\276\223\345\205\245\346\263\225\350\207\252\345\212\250\345\210\207\346\215\242/\350\265\253\346\200\235\350\276\223\345\205\245\346\263\225/StatusBar.cs" "b/tests/TestAcad09plus/\350\276\223\345\205\245\346\263\225\350\207\252\345\212\250\345\210\207\346\215\242/\350\265\253\346\200\235\350\276\223\345\205\245\346\263\225/StatusBar.cs" index 880ba87..95fb1bf 100644 --- "a/tests/TestAcad09plus/\350\276\223\345\205\245\346\263\225\350\207\252\345\212\250\345\210\207\346\215\242/\350\265\253\346\200\235\350\276\223\345\205\245\346\263\225/StatusBar.cs" +++ "b/tests/TestAcad09plus/\350\276\223\345\205\245\346\263\225\350\207\252\345\212\250\345\210\207\346\215\242/\350\265\253\346\200\235\350\276\223\345\205\245\346\263\225/StatusBar.cs" @@ -70,6 +70,7 @@ static string GetUseText() { return EnumEx.GetDesc(Settings.IMEInputSwitch); } + static readonly int _IMESwitchModeMax = Enum.GetValues(typeof(IMESwitchMode)).Cast().Max(); static void Pane_MouseDown(object sender, StatusBarMouseDownEventArgs e) { @@ -81,9 +82,8 @@ static void Pane_MouseDown(object sender, StatusBarMouseDownEventArgs e) if (_pane is null) return; - var max = Enum.GetValues(typeof(IMESwitchMode)).Cast().Max(); // 防白痴,一直点选择模式,最后是关闭.右键可以直接关闭 - if ((int)Settings.IMEInputSwitch + 1 < max) + if ((int)Settings.IMEInputSwitch < _IMESwitchModeMax) ++Settings.IMEInputSwitch; else Settings.IMEInputSwitch = 0; -- Gitee From 77fd5e20cbd02af05bf54dcf21578b4a2d8818a3 Mon Sep 17 00:00:00 2001 From: liuqihong <540762622@qq.com> Date: Thu, 3 Nov 2022 12:49:04 +0800 Subject: [PATCH 612/675] =?UTF-8?q?=E4=BC=98=E5=8C=96=E6=B3=A8=E9=87=8A?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/IFoxCAD.Cad.Shared/Copyclip/WindowsAPI.cs | 3 +-- .../StatusBar.cs" | 16 +++++++--------- 2 files changed, 8 insertions(+), 11 deletions(-) diff --git a/src/IFoxCAD.Cad.Shared/Copyclip/WindowsAPI.cs b/src/IFoxCAD.Cad.Shared/Copyclip/WindowsAPI.cs index 0c7284a..70b2f39 100644 --- a/src/IFoxCAD.Cad.Shared/Copyclip/WindowsAPI.cs +++ b/src/IFoxCAD.Cad.Shared/Copyclip/WindowsAPI.cs @@ -446,7 +446,7 @@ public static GuiThreadInfo Create(uint windowThreadProcessId) public static extern bool IsWindowEnabled(IntPtr hWnd); #endregion - #region 钩子 + #region 键盘钩子 public delegate IntPtr CallBack(int nCode, int wParam, IntPtr lParam); [DllImport("user32.dll")] public static extern IntPtr SetWindowsHookEx(HookType idHook, CallBack lpfn, IntPtr hmod, int dwThreadId); @@ -480,7 +480,6 @@ public static KeyboardHookStruct Create(IntPtr lParam) { return (KeyboardHookStruct)Marshal.PtrToStructure(lParam, typeof(KeyboardHookStruct)); } - public void ToPtr(IntPtr lParam) { Marshal.StructureToPtr(this, lParam, true); diff --git "a/tests/TestAcad09plus/\350\276\223\345\205\245\346\263\225\350\207\252\345\212\250\345\210\207\346\215\242/\350\265\253\346\200\235\350\276\223\345\205\245\346\263\225/StatusBar.cs" "b/tests/TestAcad09plus/\350\276\223\345\205\245\346\263\225\350\207\252\345\212\250\345\210\207\346\215\242/\350\265\253\346\200\235\350\276\223\345\205\245\346\263\225/StatusBar.cs" index 95fb1bf..f5e4acc 100644 --- "a/tests/TestAcad09plus/\350\276\223\345\205\245\346\263\225\350\207\252\345\212\250\345\210\207\346\215\242/\350\265\253\346\200\235\350\276\223\345\205\245\346\263\225/StatusBar.cs" +++ "b/tests/TestAcad09plus/\350\276\223\345\205\245\346\263\225\350\207\252\345\212\250\345\210\207\346\215\242/\350\265\253\346\200\235\350\276\223\345\205\245\346\263\225/StatusBar.cs" @@ -70,20 +70,20 @@ static string GetUseText() { return EnumEx.GetDesc(Settings.IMEInputSwitch); } - static readonly int _IMESwitchModeMax = Enum.GetValues(typeof(IMESwitchMode)).Cast().Max(); + static readonly IMESwitchMode _ismMax = Enum.GetValues(typeof(IMESwitchMode)).Cast().Max(); static void Pane_MouseDown(object sender, StatusBarMouseDownEventArgs e) { + if (_pane is null) + return; + // 它就只支持两个枚举 switch (e.Button) { case MouseButtons.Left: { - if (_pane is null) - return; - - // 防白痴,一直点选择模式,最后是关闭.右键可以直接关闭 - if ((int)Settings.IMEInputSwitch < _IMESwitchModeMax) + // 防白痴,一个环形选择模式 + if (Settings.IMEInputSwitch < _ismMax) ++Settings.IMEInputSwitch; else Settings.IMEInputSwitch = 0; @@ -93,9 +93,7 @@ static void Pane_MouseDown(object sender, StatusBarMouseDownEventArgs e) break; case MouseButtons.Right: { - if (_pane is null) - return; - + // 右键可以直接关闭 Settings.IMEInputSwitch = 0; _pane.Text = GetUseText(); Acap.StatusBar.Update(); -- Gitee From 4f31d76e7756be37a0fb6b962d6431a8d046291e Mon Sep 17 00:00:00 2001 From: liuqihong <540762622@qq.com> Date: Thu, 3 Nov 2022 21:08:15 +0800 Subject: [PATCH 613/675] =?UTF-8?q?=E8=BE=93=E5=85=A5=E6=B3=95=E7=9A=84?= =?UTF-8?q?=E5=88=87=E6=8D=A2=E6=A8=A1=E5=BC=8F=E5=A2=9E=E5=8A=A0=E6=81=A2?= =?UTF-8?q?=E5=A4=8D=E7=8A=B6=E6=80=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/IFoxCAD.Basal/General/DebugHelper.cs | 15 +- src/IFoxCAD.Basal/General/LoopState.cs | 23 +- .../Cmd.cs" | 4 - .../DocReactor.cs" | 3 +- .../IMEControl.cs" | 360 ++++++++++++------ .../Settings.cs" | 71 ++-- .../StatusBar.cs" | 4 +- .../Window/SettingsWindow.xaml.cs" | 6 +- 8 files changed, 328 insertions(+), 158 deletions(-) diff --git a/src/IFoxCAD.Basal/General/DebugHelper.cs b/src/IFoxCAD.Basal/General/DebugHelper.cs index 40fb904..c23342b 100644 --- a/src/IFoxCAD.Basal/General/DebugHelper.cs +++ b/src/IFoxCAD.Basal/General/DebugHelper.cs @@ -3,19 +3,22 @@ public static class Debugx { /// - /// cad命令切换: IFoxDebugx + /// cad命令切换: Debugx /// - /// - public static void Printl(object message) + /// 打印信息 + /// 打印时间 + public static void Printl(object message, bool time = true) { var flag = Environment.GetEnvironmentVariable("debugx", EnvironmentVariableTarget.User); if (flag == null || flag == "0") return; - var str = message + $"::{DateTime.Now.ToLongDateString() + DateTime.Now.TimeOfDay}"; + + if (time) + message = $"{DateTime.Now.ToLongDateString() + DateTime.Now.TimeOfDay}::" + message; #if DEBUG - System.Diagnostics.Debug.WriteLine(str); + System.Diagnostics.Debug.WriteLine(message); #else - System.Diagnostics.Trace.WriteLine(str); + System.Diagnostics.Trace.WriteLine(message); #endif } } \ No newline at end of file diff --git a/src/IFoxCAD.Basal/General/LoopState.cs b/src/IFoxCAD.Basal/General/LoopState.cs index 50a6912..d25fe2a 100644 --- a/src/IFoxCAD.Basal/General/LoopState.cs +++ b/src/IFoxCAD.Basal/General/LoopState.cs @@ -12,21 +12,22 @@ public class LoopState const int PLS_STOPPED = 4; const int PLS_CANCELED = 8; - private volatile int _LoopStateFlags = PLS_NONE; + private volatile int _flag = PLS_NONE; - public bool IsRun => _LoopStateFlags == PLS_NONE; - public bool IsExceptional => (_LoopStateFlags & PLS_EXCEPTIONAL) == PLS_EXCEPTIONAL; - public bool IsBreak => (_LoopStateFlags & PLS_BROKEN) == PLS_BROKEN; - public bool IsStop => (_LoopStateFlags & PLS_STOPPED) == PLS_STOPPED; - public bool IsCancel => (_LoopStateFlags & PLS_CANCELED) == PLS_CANCELED; + public bool IsRun => _flag == PLS_NONE; + public bool IsExceptional => (_flag & PLS_EXCEPTIONAL) == PLS_EXCEPTIONAL; + public bool IsBreak => (_flag & PLS_BROKEN) == PLS_BROKEN; + public bool IsStop => (_flag & PLS_STOPPED) == PLS_STOPPED; + public bool IsCancel => (_flag & PLS_CANCELED) == PLS_CANCELED; public void Exceptional() { - if ((_LoopStateFlags & PLS_EXCEPTIONAL) != PLS_EXCEPTIONAL) - _LoopStateFlags |= PLS_EXCEPTIONAL; + if ((_flag & PLS_EXCEPTIONAL) != PLS_EXCEPTIONAL) + _flag |= PLS_EXCEPTIONAL; } - public void Break() => _LoopStateFlags = PLS_BROKEN; - public void Stop() => _LoopStateFlags = PLS_STOPPED; - public void Cancel() => _LoopStateFlags = PLS_CANCELED; + public void Break() => _flag = PLS_BROKEN; + public void Stop() => _flag = PLS_STOPPED; + public void Cancel() => _flag = PLS_CANCELED; + public void Reset() => _flag = PLS_NONE; } #line default \ No newline at end of file diff --git "a/tests/TestAcad09plus/\350\276\223\345\205\245\346\263\225\350\207\252\345\212\250\345\210\207\346\215\242/\350\265\253\346\200\235\350\276\223\345\205\245\346\263\225/Cmd.cs" "b/tests/TestAcad09plus/\350\276\223\345\205\245\346\263\225\350\207\252\345\212\250\345\210\207\346\215\242/\350\265\253\346\200\235\350\276\223\345\205\245\346\263\225/Cmd.cs" index df61f91..88de1ea 100644 --- "a/tests/TestAcad09plus/\350\276\223\345\205\245\346\263\225\350\207\252\345\212\250\345\210\207\346\215\242/\350\265\253\346\200\235\350\276\223\345\205\245\346\263\225/Cmd.cs" +++ "b/tests/TestAcad09plus/\350\276\223\345\205\245\346\263\225\350\207\252\345\212\250\345\210\207\346\215\242/\350\265\253\346\200\235\350\276\223\345\205\245\346\263\225/Cmd.cs" @@ -9,10 +9,6 @@ public void Initialize() DocReactor.IntialReactor(); Settings.LoadSettings(); IMEControl.SetIMEHook(); - Acap.QuitWillStart += (s, e) => { - IMEControl.UnIMEHook(); - IMEControl.SaveFt(); - }; StatusBar.IMEAddPane(); } diff --git "a/tests/TestAcad09plus/\350\276\223\345\205\245\346\263\225\350\207\252\345\212\250\345\210\207\346\215\242/\350\265\253\346\200\235\350\276\223\345\205\245\346\263\225/DocReactor.cs" "b/tests/TestAcad09plus/\350\276\223\345\205\245\346\263\225\350\207\252\345\212\250\345\210\207\346\215\242/\350\265\253\346\200\235\350\276\223\345\205\245\346\263\225/DocReactor.cs" index a1ebce1..bc2b514 100644 --- "a/tests/TestAcad09plus/\350\276\223\345\205\245\346\263\225\350\207\252\345\212\250\345\210\207\346\215\242/\350\265\253\346\200\235\350\276\223\345\205\245\346\263\225/DocReactor.cs" +++ "b/tests/TestAcad09plus/\350\276\223\345\205\245\346\263\225\350\207\252\345\212\250\345\210\207\346\215\242/\350\265\253\346\200\235\350\276\223\345\205\245\346\263\225/DocReactor.cs" @@ -29,7 +29,8 @@ static void DocumentCreated(object sender, DocumentCollectionEventArgs e) static void CommandWillStart(object sender, CommandEventArgs e) { - if (Settings.IMEInputSwitch == 0 || ((Document)sender).Editor.IsQuiescentForTransparentCommand()) + if (Settings.IMEInputSwitch == IMESwitchMode.Disable || + ((Document)sender).Editor.IsQuiescentForTransparentCommand()) return; var gName = e.GlobalCommandName; diff --git "a/tests/TestAcad09plus/\350\276\223\345\205\245\346\263\225\350\207\252\345\212\250\345\210\207\346\215\242/\350\265\253\346\200\235\350\276\223\345\205\245\346\263\225/IMEControl.cs" "b/tests/TestAcad09plus/\350\276\223\345\205\245\346\263\225\350\207\252\345\212\250\345\210\207\346\215\242/\350\265\253\346\200\235\350\276\223\345\205\245\346\263\225/IMEControl.cs" index ebff1ed..224575c 100644 --- "a/tests/TestAcad09plus/\350\276\223\345\205\245\346\263\225\350\207\252\345\212\250\345\210\207\346\215\242/\350\265\253\346\200\235\350\276\223\345\205\245\346\263\225/IMEControl.cs" +++ "b/tests/TestAcad09plus/\350\276\223\345\205\245\346\263\225\350\207\252\345\212\250\345\210\207\346\215\242/\350\265\253\346\200\235\350\276\223\345\205\245\346\263\225/IMEControl.cs" @@ -1,15 +1,24 @@ namespace Gstar_IMEFilter; using System.Diagnostics; +using System.Linq; +using System.Windows.Controls; using System.Windows.Forms; using Control = System.Windows.Forms.Control; public class IMEControl { // 豁免命令组: 默认和配置的 - internal static HashSet DefaultCmds; + internal static HashSet DefaultCmds_AutoEn2Cn; // 豁免命令组: 默认和配置的+用户面板输入的 - internal static HashSet ExceptCmds; + internal static HashSet ExceptCmds_AutoEn2Cn; + static string _ftFile_AutoEn2Cn; + + // 豁免命令组: 默认和配置的 + internal static HashSet DefaultCmds_AutoCn2En; + // 括免命令组: 自动切换为英文输入法 + internal static HashSet ExceptCmds_AutoCn2En; + static string _ftFile_AutoCn2En; static readonly Regex CMDReg = new("\\(C:.*\\)"); /*某些窗口没有 WM_KEYDOWN 消息,就只有 WM_KEYUP 消息*/ @@ -23,7 +32,7 @@ public class IMEControl // 优化内存,减少消息循环时候,频繁创建此类 static StringBuilder _lpClassName = new(byte.MaxValue); static string[] _separator = new string[] { "," }; - static string _ftFile; + static IMEControl() { @@ -31,137 +40,264 @@ static IMEControl() _process = Process.GetCurrentProcess(); WindowsAPI.CheckLowLevelHooksTimeout(); - ExceptCmds = new(); - DefaultCmds = new() { "MTEXT", "DDEDIT", "MTEDIT", "TABLEDIT", "MLEADER", + ExceptCmds_AutoEn2Cn = new(); + { + DefaultCmds_AutoEn2Cn = new() { "MTEXT", "DDEDIT", "MTEDIT", "TABLEDIT", "MLEADER", "QLEADER", "MLEADERCONTENTEDIT", "MLEADEREDIT", "TEXTEDIT", "TEXT", "QLEADER" }; - - string? lines = null; - _ftFile = Path.Combine(Settings.MyDir, nameof(Gstar_IMEFilter) + nameof(ExceptCmds) + ".ft"); - if (File.Exists(_ftFile)) - lines = File.ReadAllText(_ftFile, Encoding.UTF8); - else - Debugx.Printl("配置文件丢失: " + _ftFile); - if (lines != null) + string? lines = null; + _ftFile_AutoEn2Cn = Path.Combine(Settings.MyDir, nameof(Gstar_IMEFilter) + nameof(ExceptCmds_AutoEn2Cn) + ".ft"); + if (File.Exists(_ftFile_AutoEn2Cn)) + lines = File.ReadAllText(_ftFile_AutoEn2Cn, Encoding.UTF8); + else + Debugx.Printl("配置文件丢失: " + _ftFile_AutoEn2Cn); + if (lines != null) + { + var ls = lines.Split(_separator, StringSplitOptions.RemoveEmptyEntries); + for (int i = 0; i < ls.Length; i++) + DefaultCmds_AutoEn2Cn.Add(ls[i]); + } + } + ExceptCmds_AutoCn2En = new(); { - var ls = lines.Split(_separator, StringSplitOptions.RemoveEmptyEntries); - for (int i = 0; i < ls.Length; i++) - DefaultCmds.Add(ls[i]); + DefaultCmds_AutoCn2En = new() { "BLOCK", "GROUP" }; + string? lines = null; + _ftFile_AutoCn2En = Path.Combine(Settings.MyDir, nameof(Gstar_IMEFilter) + nameof(ExceptCmds_AutoCn2En) + ".ft"); + if (File.Exists(_ftFile_AutoCn2En)) + lines = File.ReadAllText(_ftFile_AutoCn2En, Encoding.UTF8); + else + Debugx.Printl("配置文件丢失: " + _ftFile_AutoCn2En); + if (lines != null) + { + var ls = lines.Split(_separator, StringSplitOptions.RemoveEmptyEntries); + for (int i = 0; i < ls.Length; i++) + DefaultCmds_AutoCn2En.Add(ls[i]); + } } - Acap.DocumentManager.DocumentLockModeChanged += DocumentManager_DocumentLockModeChanged; + + // 命令反应器 + var dm = Acap.DocumentManager; + if (dm.Count != 0) + foreach (Document doc in dm) + { + doc.CommandWillStart += Doc_CommandWillStart; + doc.CommandEnded += Doc_CommandEnded; + } + + // 卸载钩子 + Acap.QuitWillStart += (s, e) => { + IMEControl.UnIMEHook(); + IMEControl.SaveFt(); + }; } public static void SaveFt() { HashSet lst = new(); - foreach (var item in ExceptCmds) - if (!DefaultCmds.Contains(item)) + foreach (var item in ExceptCmds_AutoEn2Cn) + if (!DefaultCmds_AutoEn2Cn.Contains(item)) lst.Add(item); - if (!lst.Any()) - return; - var jo = string.Join(",", lst.ToArray()); - File.WriteAllText(_ftFile, jo, Encoding.UTF8); + if (lst.Any()) + { + var jo = string.Join(",", lst.ToArray()); + File.WriteAllText(_ftFile_AutoEn2Cn, jo, Encoding.UTF8); + } + + lst.Clear(); + foreach (var item in ExceptCmds_AutoCn2En) + if (!DefaultCmds_AutoCn2En.Contains(item)) + lst.Add(item); + if (lst.Any()) + { + var jo = string.Join(",", lst.ToArray()); + File.WriteAllText(_ftFile_AutoCn2En, jo, Encoding.UTF8); + } } #region 切换输入法 - // TODO 关键字是中文输入,自动切换到英文 + // 关键字问题: + // 是中文输入,自动切换到英文 // 命令行监控服务 实例 得到命令行监控 // var commandLineMonitor = CommandLineMonitorServices.Instance().GetCommandLineMonitor(doc); /// /// 此状态用于豁免命令图中自动切换到中文,
    /// 命令中依然能够切换到英文输入,
    - /// 命令结束时候从否决事件恢复拦截
    + /// 命令结束时候从 命令结束反应器 恢复拦截
    ///
    - public static LoopState SendKeyState = new(); - static void DocumentManager_DocumentLockModeChanged(object sender, DocumentLockModeChangedEventArgs e) - { - if (e.GlobalCommandName == "#") - return; - if (!e.GlobalCommandName.StartsWith("#")) - return; - var up = e.GlobalCommandName.ToUpper()[1..]; - if (ExceptCmds.Contains(up)) - SendKeyState = new(); - } + static LoopState _sendKeyState = new(); - /// - /// 切换输入法 - /// - static void IMESwitch() + // 命令结束反应器 + static void Doc_CommandEnded(object sender, CommandEventArgs e) { - // 检测是否中文状态,不是就切为中文状态 - // 切换只能发生在第一次,第2.+次需要不执行 - var focusW = WindowsAPI.GetForegroundWindow(); - var context = WindowsAPI.ImmGetContext(focusW); - WindowsAPI.ImmGetConversionStatus(context, out int mode/*输入模式*/, out _); - if (!WindowsAPI.ImmGetOpenStatus(context)) + /* + * 英文状态 IsStop + * 英文状态和被程序切换 IsStop && IsExceptional + * 中文状态 IsBreak + * 中文状态和被程序切换 IsBreak && IsExceptional + * 保持不变 IsCancel + * 钩子不走任何 !Run 状态 + */ + // 如果程序切换了,就恢复原本的 + // 当前是英文状态被切换到中文(当前),{然后用户切换了英文,此时应该保证是用户},而不是发送切换(会这样变成中文) + if (ExceptCmds_AutoEn2Cn.Contains(e.GlobalCommandName)) { - Debugx.Printl("现在是英文状态,切换到中文"); - SendKeyState.Exceptional(); - - switch (Settings.IMEInputSwitch) + if (_sendKeyState.IsStop && _sendKeyState.IsExceptional) { - case IMESwitchMode.Shift: + if (IsOpenIEM()) { - WindowsAPI.KeybdEvent(16, 0, 0, 0); - WindowsAPI.KeybdEvent(16, 0, 2, 0); + SendKey(); + Debugx.Printl("恢复}", false); } - break; - case IMESwitchMode.Ctrl: + else { - WindowsAPI.KeybdEvent(17, 0, 0, 0); - WindowsAPI.KeybdEvent(17, 0, 2, 0); + Debugx.Printl("不恢复}"); } - break; - case IMESwitchMode.CtrlAndSpace: - { - WindowsAPI.KeybdEvent(17, 0, 0, 0); - WindowsAPI.KeybdEvent(32, 0, 0, 0); - WindowsAPI.KeybdEvent(32, 0, 2, 0); - WindowsAPI.KeybdEvent(17, 0, 2, 0); - if (WindowsAPI.GetKeyState(20) == 1) - { - WindowsAPI.KeybdEvent(20, 0, 0, 0); - WindowsAPI.KeybdEvent(20, 0, 2, 0); - } - } - break; - case IMESwitchMode.CtrlAndShift: + } + _sendKeyState.Reset(); + } + if (ExceptCmds_AutoCn2En.Contains(e.GlobalCommandName)) + { + if (_sendKeyState.IsBreak && _sendKeyState.IsExceptional) + { + if (!IsOpenIEM()) { - WindowsAPI.KeybdEvent(16, 0, 0, 0); - WindowsAPI.KeybdEvent(17, 0, 0, 0); - WindowsAPI.KeybdEvent(17, 0, 2, 0); - WindowsAPI.KeybdEvent(16, 0, 2, 0); - if (WindowsAPI.GetKeyState(20) == 1) - { - WindowsAPI.KeybdEvent(20, 0, 0, 0); - WindowsAPI.KeybdEvent(20, 0, 2, 0); - } + SendKey(); + Debugx.Printl("恢复}"); } - break; - case IMESwitchMode.WinAndSpace: + else { - WindowsAPI.KeybdEvent(91, 0, 0, 0); - WindowsAPI.KeybdEvent(32, 0, 0, 0); - WindowsAPI.KeybdEvent(32, 0, 2, 0); - WindowsAPI.KeybdEvent(91, 0, 2, 0); - if (WindowsAPI.GetKeyState(20) == 1) - { - WindowsAPI.KeybdEvent(20, 0, 0, 0); - WindowsAPI.KeybdEvent(20, 0, 2, 0); - } + Debugx.Printl("不恢复}"); } - break; } + _sendKeyState.Reset(); + } + } + // 命令开始反应器 + static void Doc_CommandWillStart(object sender, CommandEventArgs e) + { + if (ExceptCmds_AutoCn2En.Contains(e.GlobalCommandName)) + { + IMESwitch_AutoCn2En(); + return; + } + } + + + /// + /// 如果是中文输入法状态就是true + /// + /// + static bool IsOpenIEM() + { + var focusW = WindowsAPI.GetForegroundWindow(); + var context = WindowsAPI.ImmGetContext(focusW); + WindowsAPI.ImmGetConversionStatus(context, out int mode/*输入模式*/, out _); + return WindowsAPI.ImmGetOpenStatus(context); + } + + /// + /// 切换输入法(英文状态就切到中文) + /// + static void IMESwitch_AutoEn2Cn() + { + if (Settings.IMEInputSwitch == IMESwitchMode.NotSwitch) + return; + // 切换只能发生在第一次,第2.+次需要不执行 + if (!IsOpenIEM()) + { + Debugx.Printl("现在是英文状态,切换前{"); + _sendKeyState.Stop(); + _sendKeyState.Exceptional(); + SendKey(); } else { // 中文状态虽然不变, // 但是为了避免命令中用户手动切换 中文切换到英文,然后再触发上面 英文状态转中文逻辑, // 所以此处也要设置状态 - // 在命令否决事件上面恢复拦截 Debugx.Printl("现在是中文状态,保持不变"); - SendKeyState.Exceptional(); + _sendKeyState.Cancel(); + } + } + + /// + /// 切换输入法(中文状态就切到英文) + /// + static void IMESwitch_AutoCn2En() + { + if (Settings.IMEInputSwitch == IMESwitchMode.NotSwitch) + return; + // 切换只能发生在第一次,第2.+次需要不执行 + if (!IsOpenIEM()) + { + Debugx.Printl("现在是英文状态,保持不变"); + _sendKeyState.Cancel(); + } + else + { + Debugx.Printl("现在是中文状态,切换前{"); + _sendKeyState.Break(); + _sendKeyState.Exceptional(); + SendKey(); + } + } + + static void SendKey() + { + Debugx.Printl("触发了切换输入法", false); + switch (Settings.IMEInputSwitch) + { + case IMESwitchMode.Shift: + { + WindowsAPI.KeybdEvent(16, 0, 0, 0); + WindowsAPI.KeybdEvent(16, 0, 2, 0); + } + break; + case IMESwitchMode.Ctrl: + { + WindowsAPI.KeybdEvent(17, 0, 0, 0); + WindowsAPI.KeybdEvent(17, 0, 2, 0); + } + break; + case IMESwitchMode.CtrlAndSpace: + { + WindowsAPI.KeybdEvent(17, 0, 0, 0); + WindowsAPI.KeybdEvent(32, 0, 0, 0); + WindowsAPI.KeybdEvent(32, 0, 2, 0); + WindowsAPI.KeybdEvent(17, 0, 2, 0); + if (WindowsAPI.GetKeyState(20) == 1) + { + WindowsAPI.KeybdEvent(20, 0, 0, 0); + WindowsAPI.KeybdEvent(20, 0, 2, 0); + } + } + break; + case IMESwitchMode.CtrlAndShift: + { + WindowsAPI.KeybdEvent(16, 0, 0, 0); + WindowsAPI.KeybdEvent(17, 0, 0, 0); + WindowsAPI.KeybdEvent(17, 0, 2, 0); + WindowsAPI.KeybdEvent(16, 0, 2, 0); + if (WindowsAPI.GetKeyState(20) == 1) + { + WindowsAPI.KeybdEvent(20, 0, 0, 0); + WindowsAPI.KeybdEvent(20, 0, 2, 0); + } + } + break; + case IMESwitchMode.WinAndSpace: + { + WindowsAPI.KeybdEvent(91, 0, 0, 0); + WindowsAPI.KeybdEvent(32, 0, 0, 0); + WindowsAPI.KeybdEvent(32, 0, 2, 0); + WindowsAPI.KeybdEvent(91, 0, 2, 0); + if (WindowsAPI.GetKeyState(20) == 1) + { + WindowsAPI.KeybdEvent(20, 0, 0, 0); + WindowsAPI.KeybdEvent(20, 0, 2, 0); + } + } + break; } } #endregion @@ -175,12 +311,23 @@ internal static void SetIMEHook() if (_nextHookProc != IntPtr.Zero) return; - ExceptCmds.Clear(); - foreach (var item in DefaultCmds) - ExceptCmds.Add(item); - var ss = Settings.UserFilter.Split(_separator, StringSplitOptions.RemoveEmptyEntries); - foreach (var item in ss) - ExceptCmds.Add(item); + ExceptCmds_AutoEn2Cn.Clear(); + { + foreach (var item in DefaultCmds_AutoEn2Cn) + ExceptCmds_AutoEn2Cn.Add(item); + var ss = Settings.UserFilter_AutoEn2Cn.Split(_separator, StringSplitOptions.RemoveEmptyEntries); + foreach (var item in ss) + ExceptCmds_AutoEn2Cn.Add(item); + } + + ExceptCmds_AutoCn2En.Clear(); + { + foreach (var item in DefaultCmds_AutoCn2En) + ExceptCmds_AutoCn2En.Add(item); + var ss = Settings.UserFilter_AutoCn2En.Split(_separator, StringSplitOptions.RemoveEmptyEntries); + foreach (var item in ss) + ExceptCmds_AutoCn2En.Add(item); + } if (Settings.IMEHookStyle == IMEHookStyle.Process) { @@ -188,9 +335,8 @@ internal static void SetIMEHook() _hookProc = (nCode, wParam, lParam) => { if (nCode >= 0) { - // 这里会处理键入时留存首字母的问题 + // 高版本cad基本上不能用进程钩子: // 搜狗输入法如果连续按着,那么此时拦截失效 - // 高版本cad基本上不能用进程钩子 Gstar_IMEFilterSettings var lp = lParam.ToInt64(); if ((lp > 0) && ((lp & 0xC0000001) == 1))//按下某个键 { @@ -252,9 +398,9 @@ static bool ExceptCmdsTask() if (match.Success) input = input.Substring(checked(match.Index + 3), checked(match.Length - 4)); - if (ExceptCmds.Contains(input.ToUpper())) + if (ExceptCmds_AutoEn2Cn.Contains(input.ToUpper())) { - IMESwitch(); + IMESwitch_AutoEn2Cn(); return false; } return true; @@ -269,7 +415,7 @@ static bool ExceptCmdsTask() /// false不终止回调,true终止回调 public static bool IMEHook(int nCode, int wParam, IntPtr lParam) { - if (SendKeyState.IsExceptional) + if (_sendKeyState.IsExceptional) return false; // 键盘按键值 @@ -370,7 +516,7 @@ public static bool IMEHook(int nCode, int wParam, IntPtr lParam) static bool MK1(int wParam) { - return Settings.IMEInputSwitch == 0 || + return Settings.IMEInputSwitch == IMESwitchMode.Disable || WindowsAPI.IsIconic(Acap.MainWindow.Handle.ToInt32()) || WindowsAPI.GetKeyState(91) < 0 || WindowsAPI.GetKeyState(92) < 0 || diff --git "a/tests/TestAcad09plus/\350\276\223\345\205\245\346\263\225\350\207\252\345\212\250\345\210\207\346\215\242/\350\265\253\346\200\235\350\276\223\345\205\245\346\263\225/Settings.cs" "b/tests/TestAcad09plus/\350\276\223\345\205\245\346\263\225\350\207\252\345\212\250\345\210\207\346\215\242/\350\265\253\346\200\235\350\276\223\345\205\245\346\263\225/Settings.cs" index ef0e452..df7642d 100644 --- "a/tests/TestAcad09plus/\350\276\223\345\205\245\346\263\225\350\207\252\345\212\250\345\210\207\346\215\242/\350\265\253\346\200\235\350\276\223\345\205\245\346\263\225/Settings.cs" +++ "b/tests/TestAcad09plus/\350\276\223\345\205\245\346\263\225\350\207\252\345\212\250\345\210\207\346\215\242/\350\265\253\346\200\235\350\276\223\345\205\245\346\263\225/Settings.cs" @@ -5,11 +5,6 @@ namespace Gstar_IMEFilter; public class Settings { static string _MyDir = ""; - static string _MySettingsPath = ""; - internal static string _UserFilter = ""; - internal static IMEHookStyle _IMEHookStyle = IMEHookStyle.Global; - internal static IMESwitchMode _IMEInputSwitch = IMESwitchMode.Shift; - internal static string MyDir { get @@ -20,6 +15,7 @@ internal static string MyDir } } + static string _MySettingsPath = ""; public static string MySettingsPath { get @@ -30,18 +26,33 @@ public static string MySettingsPath } } - public static string UserFilter + internal static string _UserFilter_AutoEn2Cn = ""; + public static string UserFilter_AutoEn2Cn + { + get => _UserFilter_AutoEn2Cn; + set + { + if (_UserFilter_AutoEn2Cn.Length == 0) + return; + _UserFilter_AutoEn2Cn = value; + SaveSettings(); + } + } + + internal static string _UserFilter_AutoCn2En = ""; + public static string UserFilter_AutoCn2En { - get => _UserFilter; + get => _UserFilter_AutoCn2En; set { - if (_UserFilter.Length == 0) + if (_UserFilter_AutoCn2En.Length == 0) return; - _UserFilter = value; + _UserFilter_AutoCn2En = value; SaveSettings(); } } + internal static IMEHookStyle _IMEHookStyle = IMEHookStyle.Global; public static IMEHookStyle IMEHookStyle { get => _IMEHookStyle; @@ -55,6 +66,7 @@ public static IMEHookStyle IMEHookStyle } } + internal static IMESwitchMode _IMEInputSwitch = IMESwitchMode.Shift; public static IMESwitchMode IMEInputSwitch { get => _IMEInputSwitch; @@ -82,18 +94,23 @@ public static void LoadSettings() string left = xmlReader.Name.ToLower(); switch (left) { - case "userfilter": + case nameof(UserFilter_AutoEn2Cn): { - _UserFilter = xmlReader.ReadInnerXml().ToUpper(); + _UserFilter_AutoEn2Cn = xmlReader.ReadInnerXml().ToUpper(); } break; - case "imehookstyle": + case nameof(UserFilter_AutoCn2En): + { + _UserFilter_AutoCn2En = xmlReader.ReadInnerXml().ToUpper(); + } + break; + case nameof(IMEHookStyle): { int.TryParse(xmlReader.ReadInnerXml(), out int ime); _IMEHookStyle = (IMEHookStyle)ime; } break; - case "imeinputswitch": + case nameof(IMEInputSwitch): { int.TryParse(xmlReader.ReadInnerXml(), out int ime); _IMEInputSwitch = (IMESwitchMode)ime; @@ -123,17 +140,21 @@ internal static void SaveSettings() xmlWriter.WriteStartDocument(1 != 0); xmlWriter.WriteComment("输入法+"); - xmlWriter.WriteStartElement("settings"); + xmlWriter.WriteStartElement(nameof(Settings)); { - xmlWriter.WriteStartElement("userfilter"); - xmlWriter.WriteString(UserFilter); + xmlWriter.WriteStartElement(nameof(UserFilter_AutoEn2Cn)); + xmlWriter.WriteString(UserFilter_AutoEn2Cn); + xmlWriter.WriteEndElement(); + + xmlWriter.WriteStartElement(nameof(UserFilter_AutoCn2En)); + xmlWriter.WriteString(UserFilter_AutoCn2En); xmlWriter.WriteEndElement(); - xmlWriter.WriteStartElement("imehookstyle"); + xmlWriter.WriteStartElement(nameof(IMEHookStyle)); xmlWriter.WriteString(((int)IMEHookStyle).ToString()); xmlWriter.WriteEndElement(); - xmlWriter.WriteStartElement("imeinputswitch"); + xmlWriter.WriteStartElement(nameof(IMEInputSwitch)); xmlWriter.WriteString(((int)IMEInputSwitch).ToString()); xmlWriter.WriteEndElement(); } @@ -163,16 +184,18 @@ public enum IMEHookStyle : byte ///
    public enum IMESwitchMode : byte { - [Description("输入法拦截关闭")] + [Description("输入拦截关闭")] Disable, - [Description("输入法拦截开启:Shift")] + [Description("输入拦截开启:不切换")] + NotSwitch, + [Description("输入拦截开启:Shift")] Shift, - [Description("输入法拦截开启:Ctrl")] + [Description("输入拦截开启:Ctrl")] Ctrl, - [Description("输入法拦截开启:CtrlAndSpace")] + [Description("输入拦截开启:CtrlAndSpace")] CtrlAndSpace, - [Description("输入法拦截开启:CtrlAndShift")] + [Description("输入拦截开启:CtrlAndShift")] CtrlAndShift, - [Description("输入法拦截开启:WinAndSpace")] + [Description("输入拦截开启:WinAndSpace")] WinAndSpace, } \ No newline at end of file diff --git "a/tests/TestAcad09plus/\350\276\223\345\205\245\346\263\225\350\207\252\345\212\250\345\210\207\346\215\242/\350\265\253\346\200\235\350\276\223\345\205\245\346\263\225/StatusBar.cs" "b/tests/TestAcad09plus/\350\276\223\345\205\245\346\263\225\350\207\252\345\212\250\345\210\207\346\215\242/\350\265\253\346\200\235\350\276\223\345\205\245\346\263\225/StatusBar.cs" index f5e4acc..21b5c9a 100644 --- "a/tests/TestAcad09plus/\350\276\223\345\205\245\346\263\225\350\207\252\345\212\250\345\210\207\346\215\242/\350\265\253\346\200\235\350\276\223\345\205\245\346\263\225/StatusBar.cs" +++ "b/tests/TestAcad09plus/\350\276\223\345\205\245\346\263\225\350\207\252\345\212\250\345\210\207\346\215\242/\350\265\253\346\200\235\350\276\223\345\205\245\346\263\225/StatusBar.cs" @@ -86,7 +86,7 @@ static void Pane_MouseDown(object sender, StatusBarMouseDownEventArgs e) if (Settings.IMEInputSwitch < _ismMax) ++Settings.IMEInputSwitch; else - Settings.IMEInputSwitch = 0; + Settings.IMEInputSwitch = IMESwitchMode.Disable; _pane.Text = GetUseText(); Acap.StatusBar.Update(); } @@ -94,7 +94,7 @@ static void Pane_MouseDown(object sender, StatusBarMouseDownEventArgs e) case MouseButtons.Right: { // 右键可以直接关闭 - Settings.IMEInputSwitch = 0; + Settings.IMEInputSwitch = IMESwitchMode.Disable; _pane.Text = GetUseText(); Acap.StatusBar.Update(); } diff --git "a/tests/TestAcad09plus/\350\276\223\345\205\245\346\263\225\350\207\252\345\212\250\345\210\207\346\215\242/\350\265\253\346\200\235\350\276\223\345\205\245\346\263\225/Window/SettingsWindow.xaml.cs" "b/tests/TestAcad09plus/\350\276\223\345\205\245\346\263\225\350\207\252\345\212\250\345\210\207\346\215\242/\350\265\253\346\200\235\350\276\223\345\205\245\346\263\225/Window/SettingsWindow.xaml.cs" index ff280a9..cfc5e87 100644 --- "a/tests/TestAcad09plus/\350\276\223\345\205\245\346\263\225\350\207\252\345\212\250\345\210\207\346\215\242/\350\265\253\346\200\235\350\276\223\345\205\245\346\263\225/Window/SettingsWindow.xaml.cs" +++ "b/tests/TestAcad09plus/\350\276\223\345\205\245\346\263\225\350\207\252\345\212\250\345\210\207\346\215\242/\350\265\253\346\200\235\350\276\223\345\205\245\346\263\225/Window/SettingsWindow.xaml.cs" @@ -17,7 +17,7 @@ public SettingsWindow() private void Button_Click(object sender, RoutedEventArgs e) { - Settings._UserFilter = ExCMD.Text; + Settings._UserFilter_AutoEn2Cn = ExCMD.Text; Settings._IMEHookStyle = (IMEHookStyle)CBox.SelectedIndex; Settings.SaveSettings(); IMEControl.SetIMEHook(); @@ -28,8 +28,8 @@ private void Button_Click(object sender, RoutedEventArgs e) private void Window_Loaded(object sender, RoutedEventArgs e) { CBox.SelectedIndex = (int)Settings.IMEHookStyle; - ExCMD.Text = Settings.UserFilter; - DeCMD.Text = string.Join(",", IMEControl.DefaultCmds.ToArray()); + ExCMD.Text = Settings.UserFilter_AutoEn2Cn; + DeCMD.Text = string.Join(",", IMEControl.DefaultCmds_AutoEn2Cn.ToArray()); } private void Window_PreviewKeyDown(object sender, KeyEventArgs e) -- Gitee From ded081fa745075914afa3db57e7136e573112f9c Mon Sep 17 00:00:00 2001 From: liuqihong <540762622@qq.com> Date: Fri, 4 Nov 2022 22:13:13 +0800 Subject: [PATCH 614/675] =?UTF-8?q?=E7=A7=BB=E9=99=A4=E5=A4=9A=E4=BD=99?= =?UTF-8?q?=E9=A1=B9=E7=9B=AE?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- tests/TestAcad08/TestAcad08.csproj | 10 +- .../Cmd.cs" | 2 +- .../DPI.cs" | 2 + .../DocReactor.cs" | 0 .../IMEControl.cs" | 0 .../ResourceDictionary/ButtonDictionary.xaml" | 0 .../ComboboxDictionary.xaml" | 0 .../ComboboxitemDictionary.xaml" | 0 .../ScrollviewDictionary.xaml" | 0 .../TextboxDictionary.xaml" | 0 .../Settings.cs" | 2 +- .../ShowWPFWindowCentered.cs" | 0 .../StatusBar.cs" | 0 .../Window/SettingsWindow.xaml" | 0 .../Window/SettingsWindow.xaml.cs" | 0 .../InputBinding/Inputting.cs" | 697 ------------------ .../InputBinding/InputtingForm.Designer.cs" | 302 -------- .../InputBinding/InputtingForm.cs" | 178 ----- .../InputBinding/InputtingForm.resx" | 120 --- 19 files changed, 9 insertions(+), 1304 deletions(-) rename "tests/TestAcad09plus/\350\276\223\345\205\245\346\263\225\350\207\252\345\212\250\345\210\207\346\215\242/\350\265\253\346\200\235\350\276\223\345\205\245\346\263\225/Cmd.cs" => "tests/TestAcad09plus/\346\213\246\346\210\252\350\276\223\345\205\245\346\263\225/Cmd.cs" (85%) rename "tests/TestAcad09plus/\350\276\223\345\205\245\346\263\225\350\207\252\345\212\250\345\210\207\346\215\242/\350\265\253\346\200\235\350\276\223\345\205\245\346\263\225/DPI.cs" => "tests/TestAcad09plus/\346\213\246\346\210\252\350\276\223\345\205\245\346\263\225/DPI.cs" (74%) rename "tests/TestAcad09plus/\350\276\223\345\205\245\346\263\225\350\207\252\345\212\250\345\210\207\346\215\242/\350\265\253\346\200\235\350\276\223\345\205\245\346\263\225/DocReactor.cs" => "tests/TestAcad09plus/\346\213\246\346\210\252\350\276\223\345\205\245\346\263\225/DocReactor.cs" (100%) rename "tests/TestAcad09plus/\350\276\223\345\205\245\346\263\225\350\207\252\345\212\250\345\210\207\346\215\242/\350\265\253\346\200\235\350\276\223\345\205\245\346\263\225/IMEControl.cs" => "tests/TestAcad09plus/\346\213\246\346\210\252\350\276\223\345\205\245\346\263\225/IMEControl.cs" (100%) rename "tests/TestAcad09plus/\350\276\223\345\205\245\346\263\225\350\207\252\345\212\250\345\210\207\346\215\242/\350\265\253\346\200\235\350\276\223\345\205\245\346\263\225/ResourceDictionary/ButtonDictionary.xaml" => "tests/TestAcad09plus/\346\213\246\346\210\252\350\276\223\345\205\245\346\263\225/ResourceDictionary/ButtonDictionary.xaml" (100%) rename "tests/TestAcad09plus/\350\276\223\345\205\245\346\263\225\350\207\252\345\212\250\345\210\207\346\215\242/\350\265\253\346\200\235\350\276\223\345\205\245\346\263\225/ResourceDictionary/ComboboxDictionary.xaml" => "tests/TestAcad09plus/\346\213\246\346\210\252\350\276\223\345\205\245\346\263\225/ResourceDictionary/ComboboxDictionary.xaml" (100%) rename "tests/TestAcad09plus/\350\276\223\345\205\245\346\263\225\350\207\252\345\212\250\345\210\207\346\215\242/\350\265\253\346\200\235\350\276\223\345\205\245\346\263\225/ResourceDictionary/ComboboxitemDictionary.xaml" => "tests/TestAcad09plus/\346\213\246\346\210\252\350\276\223\345\205\245\346\263\225/ResourceDictionary/ComboboxitemDictionary.xaml" (100%) rename "tests/TestAcad09plus/\350\276\223\345\205\245\346\263\225\350\207\252\345\212\250\345\210\207\346\215\242/\350\265\253\346\200\235\350\276\223\345\205\245\346\263\225/ResourceDictionary/ScrollviewDictionary.xaml" => "tests/TestAcad09plus/\346\213\246\346\210\252\350\276\223\345\205\245\346\263\225/ResourceDictionary/ScrollviewDictionary.xaml" (100%) rename "tests/TestAcad09plus/\350\276\223\345\205\245\346\263\225\350\207\252\345\212\250\345\210\207\346\215\242/\350\265\253\346\200\235\350\276\223\345\205\245\346\263\225/ResourceDictionary/TextboxDictionary.xaml" => "tests/TestAcad09plus/\346\213\246\346\210\252\350\276\223\345\205\245\346\263\225/ResourceDictionary/TextboxDictionary.xaml" (100%) rename "tests/TestAcad09plus/\350\276\223\345\205\245\346\263\225\350\207\252\345\212\250\345\210\207\346\215\242/\350\265\253\346\200\235\350\276\223\345\205\245\346\263\225/Settings.cs" => "tests/TestAcad09plus/\346\213\246\346\210\252\350\276\223\345\205\245\346\263\225/Settings.cs" (99%) rename "tests/TestAcad09plus/\350\276\223\345\205\245\346\263\225\350\207\252\345\212\250\345\210\207\346\215\242/\350\265\253\346\200\235\350\276\223\345\205\245\346\263\225/ShowWPFWindowCentered.cs" => "tests/TestAcad09plus/\346\213\246\346\210\252\350\276\223\345\205\245\346\263\225/ShowWPFWindowCentered.cs" (100%) rename "tests/TestAcad09plus/\350\276\223\345\205\245\346\263\225\350\207\252\345\212\250\345\210\207\346\215\242/\350\265\253\346\200\235\350\276\223\345\205\245\346\263\225/StatusBar.cs" => "tests/TestAcad09plus/\346\213\246\346\210\252\350\276\223\345\205\245\346\263\225/StatusBar.cs" (100%) rename "tests/TestAcad09plus/\350\276\223\345\205\245\346\263\225\350\207\252\345\212\250\345\210\207\346\215\242/\350\265\253\346\200\235\350\276\223\345\205\245\346\263\225/Window/SettingsWindow.xaml" => "tests/TestAcad09plus/\346\213\246\346\210\252\350\276\223\345\205\245\346\263\225/Window/SettingsWindow.xaml" (100%) rename "tests/TestAcad09plus/\350\276\223\345\205\245\346\263\225\350\207\252\345\212\250\345\210\207\346\215\242/\350\265\253\346\200\235\350\276\223\345\205\245\346\263\225/Window/SettingsWindow.xaml.cs" => "tests/TestAcad09plus/\346\213\246\346\210\252\350\276\223\345\205\245\346\263\225/Window/SettingsWindow.xaml.cs" (100%) delete mode 100644 "tests/TestAcad09plus/\350\276\223\345\205\245\346\263\225\350\207\252\345\212\250\345\210\207\346\215\242/InputBinding/Inputting.cs" delete mode 100644 "tests/TestAcad09plus/\350\276\223\345\205\245\346\263\225\350\207\252\345\212\250\345\210\207\346\215\242/InputBinding/InputtingForm.Designer.cs" delete mode 100644 "tests/TestAcad09plus/\350\276\223\345\205\245\346\263\225\350\207\252\345\212\250\345\210\207\346\215\242/InputBinding/InputtingForm.cs" delete mode 100644 "tests/TestAcad09plus/\350\276\223\345\205\245\346\263\225\350\207\252\345\212\250\345\210\207\346\215\242/InputBinding/InputtingForm.resx" diff --git a/tests/TestAcad08/TestAcad08.csproj b/tests/TestAcad08/TestAcad08.csproj index 80f7a0b..d4ec8a3 100644 --- a/tests/TestAcad08/TestAcad08.csproj +++ b/tests/TestAcad08/TestAcad08.csproj @@ -33,16 +33,16 @@ - + - - + + MSBuild:Compile - - + + MSBuild:Compile diff --git "a/tests/TestAcad09plus/\350\276\223\345\205\245\346\263\225\350\207\252\345\212\250\345\210\207\346\215\242/\350\265\253\346\200\235\350\276\223\345\205\245\346\263\225/Cmd.cs" "b/tests/TestAcad09plus/\346\213\246\346\210\252\350\276\223\345\205\245\346\263\225/Cmd.cs" similarity index 85% rename from "tests/TestAcad09plus/\350\276\223\345\205\245\346\263\225\350\207\252\345\212\250\345\210\207\346\215\242/\350\265\253\346\200\235\350\276\223\345\205\245\346\263\225/Cmd.cs" rename to "tests/TestAcad09plus/\346\213\246\346\210\252\350\276\223\345\205\245\346\263\225/Cmd.cs" index 88de1ea..90c7bb7 100644 --- "a/tests/TestAcad09plus/\350\276\223\345\205\245\346\263\225\350\207\252\345\212\250\345\210\207\346\215\242/\350\265\253\346\200\235\350\276\223\345\205\245\346\263\225/Cmd.cs" +++ "b/tests/TestAcad09plus/\346\213\246\346\210\252\350\276\223\345\205\245\346\263\225/Cmd.cs" @@ -5,7 +5,7 @@ public class Cmd [IFoxInitialize] public void Initialize() { - Env.Printl($"※输入法+控制※{nameof(Gstar_IMEFilterSettings)} - 设置"); + Env.Printl($"※拦截输入法控制※{nameof(Gstar_IMEFilterSettings)} - 设置"); DocReactor.IntialReactor(); Settings.LoadSettings(); IMEControl.SetIMEHook(); diff --git "a/tests/TestAcad09plus/\350\276\223\345\205\245\346\263\225\350\207\252\345\212\250\345\210\207\346\215\242/\350\265\253\346\200\235\350\276\223\345\205\245\346\263\225/DPI.cs" "b/tests/TestAcad09plus/\346\213\246\346\210\252\350\276\223\345\205\245\346\263\225/DPI.cs" similarity index 74% rename from "tests/TestAcad09plus/\350\276\223\345\205\245\346\263\225\350\207\252\345\212\250\345\210\207\346\215\242/\350\265\253\346\200\235\350\276\223\345\205\245\346\263\225/DPI.cs" rename to "tests/TestAcad09plus/\346\213\246\346\210\252\350\276\223\345\205\245\346\263\225/DPI.cs" index 5fb3622..065d200 100644 --- "a/tests/TestAcad09plus/\350\276\223\345\205\245\346\263\225\350\207\252\345\212\250\345\210\207\346\215\242/\350\265\253\346\200\235\350\276\223\345\205\245\346\263\225/DPI.cs" +++ "b/tests/TestAcad09plus/\346\213\246\346\210\252\350\276\223\345\205\245\346\263\225/DPI.cs" @@ -1,8 +1,10 @@ using System.Drawing; +using System.Runtime.CompilerServices; namespace Gstar_IMEFilter; public class DPI { + [MethodImpl] public static double CurrentDPI() => (double)Graphics.FromHwnd(IntPtr.Zero).DpiX / 96.0; } \ No newline at end of file diff --git "a/tests/TestAcad09plus/\350\276\223\345\205\245\346\263\225\350\207\252\345\212\250\345\210\207\346\215\242/\350\265\253\346\200\235\350\276\223\345\205\245\346\263\225/DocReactor.cs" "b/tests/TestAcad09plus/\346\213\246\346\210\252\350\276\223\345\205\245\346\263\225/DocReactor.cs" similarity index 100% rename from "tests/TestAcad09plus/\350\276\223\345\205\245\346\263\225\350\207\252\345\212\250\345\210\207\346\215\242/\350\265\253\346\200\235\350\276\223\345\205\245\346\263\225/DocReactor.cs" rename to "tests/TestAcad09plus/\346\213\246\346\210\252\350\276\223\345\205\245\346\263\225/DocReactor.cs" diff --git "a/tests/TestAcad09plus/\350\276\223\345\205\245\346\263\225\350\207\252\345\212\250\345\210\207\346\215\242/\350\265\253\346\200\235\350\276\223\345\205\245\346\263\225/IMEControl.cs" "b/tests/TestAcad09plus/\346\213\246\346\210\252\350\276\223\345\205\245\346\263\225/IMEControl.cs" similarity index 100% rename from "tests/TestAcad09plus/\350\276\223\345\205\245\346\263\225\350\207\252\345\212\250\345\210\207\346\215\242/\350\265\253\346\200\235\350\276\223\345\205\245\346\263\225/IMEControl.cs" rename to "tests/TestAcad09plus/\346\213\246\346\210\252\350\276\223\345\205\245\346\263\225/IMEControl.cs" diff --git "a/tests/TestAcad09plus/\350\276\223\345\205\245\346\263\225\350\207\252\345\212\250\345\210\207\346\215\242/\350\265\253\346\200\235\350\276\223\345\205\245\346\263\225/ResourceDictionary/ButtonDictionary.xaml" "b/tests/TestAcad09plus/\346\213\246\346\210\252\350\276\223\345\205\245\346\263\225/ResourceDictionary/ButtonDictionary.xaml" similarity index 100% rename from "tests/TestAcad09plus/\350\276\223\345\205\245\346\263\225\350\207\252\345\212\250\345\210\207\346\215\242/\350\265\253\346\200\235\350\276\223\345\205\245\346\263\225/ResourceDictionary/ButtonDictionary.xaml" rename to "tests/TestAcad09plus/\346\213\246\346\210\252\350\276\223\345\205\245\346\263\225/ResourceDictionary/ButtonDictionary.xaml" diff --git "a/tests/TestAcad09plus/\350\276\223\345\205\245\346\263\225\350\207\252\345\212\250\345\210\207\346\215\242/\350\265\253\346\200\235\350\276\223\345\205\245\346\263\225/ResourceDictionary/ComboboxDictionary.xaml" "b/tests/TestAcad09plus/\346\213\246\346\210\252\350\276\223\345\205\245\346\263\225/ResourceDictionary/ComboboxDictionary.xaml" similarity index 100% rename from "tests/TestAcad09plus/\350\276\223\345\205\245\346\263\225\350\207\252\345\212\250\345\210\207\346\215\242/\350\265\253\346\200\235\350\276\223\345\205\245\346\263\225/ResourceDictionary/ComboboxDictionary.xaml" rename to "tests/TestAcad09plus/\346\213\246\346\210\252\350\276\223\345\205\245\346\263\225/ResourceDictionary/ComboboxDictionary.xaml" diff --git "a/tests/TestAcad09plus/\350\276\223\345\205\245\346\263\225\350\207\252\345\212\250\345\210\207\346\215\242/\350\265\253\346\200\235\350\276\223\345\205\245\346\263\225/ResourceDictionary/ComboboxitemDictionary.xaml" "b/tests/TestAcad09plus/\346\213\246\346\210\252\350\276\223\345\205\245\346\263\225/ResourceDictionary/ComboboxitemDictionary.xaml" similarity index 100% rename from "tests/TestAcad09plus/\350\276\223\345\205\245\346\263\225\350\207\252\345\212\250\345\210\207\346\215\242/\350\265\253\346\200\235\350\276\223\345\205\245\346\263\225/ResourceDictionary/ComboboxitemDictionary.xaml" rename to "tests/TestAcad09plus/\346\213\246\346\210\252\350\276\223\345\205\245\346\263\225/ResourceDictionary/ComboboxitemDictionary.xaml" diff --git "a/tests/TestAcad09plus/\350\276\223\345\205\245\346\263\225\350\207\252\345\212\250\345\210\207\346\215\242/\350\265\253\346\200\235\350\276\223\345\205\245\346\263\225/ResourceDictionary/ScrollviewDictionary.xaml" "b/tests/TestAcad09plus/\346\213\246\346\210\252\350\276\223\345\205\245\346\263\225/ResourceDictionary/ScrollviewDictionary.xaml" similarity index 100% rename from "tests/TestAcad09plus/\350\276\223\345\205\245\346\263\225\350\207\252\345\212\250\345\210\207\346\215\242/\350\265\253\346\200\235\350\276\223\345\205\245\346\263\225/ResourceDictionary/ScrollviewDictionary.xaml" rename to "tests/TestAcad09plus/\346\213\246\346\210\252\350\276\223\345\205\245\346\263\225/ResourceDictionary/ScrollviewDictionary.xaml" diff --git "a/tests/TestAcad09plus/\350\276\223\345\205\245\346\263\225\350\207\252\345\212\250\345\210\207\346\215\242/\350\265\253\346\200\235\350\276\223\345\205\245\346\263\225/ResourceDictionary/TextboxDictionary.xaml" "b/tests/TestAcad09plus/\346\213\246\346\210\252\350\276\223\345\205\245\346\263\225/ResourceDictionary/TextboxDictionary.xaml" similarity index 100% rename from "tests/TestAcad09plus/\350\276\223\345\205\245\346\263\225\350\207\252\345\212\250\345\210\207\346\215\242/\350\265\253\346\200\235\350\276\223\345\205\245\346\263\225/ResourceDictionary/TextboxDictionary.xaml" rename to "tests/TestAcad09plus/\346\213\246\346\210\252\350\276\223\345\205\245\346\263\225/ResourceDictionary/TextboxDictionary.xaml" diff --git "a/tests/TestAcad09plus/\350\276\223\345\205\245\346\263\225\350\207\252\345\212\250\345\210\207\346\215\242/\350\265\253\346\200\235\350\276\223\345\205\245\346\263\225/Settings.cs" "b/tests/TestAcad09plus/\346\213\246\346\210\252\350\276\223\345\205\245\346\263\225/Settings.cs" similarity index 99% rename from "tests/TestAcad09plus/\350\276\223\345\205\245\346\263\225\350\207\252\345\212\250\345\210\207\346\215\242/\350\265\253\346\200\235\350\276\223\345\205\245\346\263\225/Settings.cs" rename to "tests/TestAcad09plus/\346\213\246\346\210\252\350\276\223\345\205\245\346\263\225/Settings.cs" index df7642d..1dc84b0 100644 --- "a/tests/TestAcad09plus/\350\276\223\345\205\245\346\263\225\350\207\252\345\212\250\345\210\207\346\215\242/\350\265\253\346\200\235\350\276\223\345\205\245\346\263\225/Settings.cs" +++ "b/tests/TestAcad09plus/\346\213\246\346\210\252\350\276\223\345\205\245\346\263\225/Settings.cs" @@ -138,7 +138,7 @@ internal static void SaveSettings() using var xmlWriter = XmlWriter.Create(MySettingsPath, settings); xmlWriter.WriteStartDocument(1 != 0); - xmlWriter.WriteComment("输入法+"); + xmlWriter.WriteComment("拦截输入法"); xmlWriter.WriteStartElement(nameof(Settings)); { diff --git "a/tests/TestAcad09plus/\350\276\223\345\205\245\346\263\225\350\207\252\345\212\250\345\210\207\346\215\242/\350\265\253\346\200\235\350\276\223\345\205\245\346\263\225/ShowWPFWindowCentered.cs" "b/tests/TestAcad09plus/\346\213\246\346\210\252\350\276\223\345\205\245\346\263\225/ShowWPFWindowCentered.cs" similarity index 100% rename from "tests/TestAcad09plus/\350\276\223\345\205\245\346\263\225\350\207\252\345\212\250\345\210\207\346\215\242/\350\265\253\346\200\235\350\276\223\345\205\245\346\263\225/ShowWPFWindowCentered.cs" rename to "tests/TestAcad09plus/\346\213\246\346\210\252\350\276\223\345\205\245\346\263\225/ShowWPFWindowCentered.cs" diff --git "a/tests/TestAcad09plus/\350\276\223\345\205\245\346\263\225\350\207\252\345\212\250\345\210\207\346\215\242/\350\265\253\346\200\235\350\276\223\345\205\245\346\263\225/StatusBar.cs" "b/tests/TestAcad09plus/\346\213\246\346\210\252\350\276\223\345\205\245\346\263\225/StatusBar.cs" similarity index 100% rename from "tests/TestAcad09plus/\350\276\223\345\205\245\346\263\225\350\207\252\345\212\250\345\210\207\346\215\242/\350\265\253\346\200\235\350\276\223\345\205\245\346\263\225/StatusBar.cs" rename to "tests/TestAcad09plus/\346\213\246\346\210\252\350\276\223\345\205\245\346\263\225/StatusBar.cs" diff --git "a/tests/TestAcad09plus/\350\276\223\345\205\245\346\263\225\350\207\252\345\212\250\345\210\207\346\215\242/\350\265\253\346\200\235\350\276\223\345\205\245\346\263\225/Window/SettingsWindow.xaml" "b/tests/TestAcad09plus/\346\213\246\346\210\252\350\276\223\345\205\245\346\263\225/Window/SettingsWindow.xaml" similarity index 100% rename from "tests/TestAcad09plus/\350\276\223\345\205\245\346\263\225\350\207\252\345\212\250\345\210\207\346\215\242/\350\265\253\346\200\235\350\276\223\345\205\245\346\263\225/Window/SettingsWindow.xaml" rename to "tests/TestAcad09plus/\346\213\246\346\210\252\350\276\223\345\205\245\346\263\225/Window/SettingsWindow.xaml" diff --git "a/tests/TestAcad09plus/\350\276\223\345\205\245\346\263\225\350\207\252\345\212\250\345\210\207\346\215\242/\350\265\253\346\200\235\350\276\223\345\205\245\346\263\225/Window/SettingsWindow.xaml.cs" "b/tests/TestAcad09plus/\346\213\246\346\210\252\350\276\223\345\205\245\346\263\225/Window/SettingsWindow.xaml.cs" similarity index 100% rename from "tests/TestAcad09plus/\350\276\223\345\205\245\346\263\225\350\207\252\345\212\250\345\210\207\346\215\242/\350\265\253\346\200\235\350\276\223\345\205\245\346\263\225/Window/SettingsWindow.xaml.cs" rename to "tests/TestAcad09plus/\346\213\246\346\210\252\350\276\223\345\205\245\346\263\225/Window/SettingsWindow.xaml.cs" diff --git "a/tests/TestAcad09plus/\350\276\223\345\205\245\346\263\225\350\207\252\345\212\250\345\210\207\346\215\242/InputBinding/Inputting.cs" "b/tests/TestAcad09plus/\350\276\223\345\205\245\346\263\225\350\207\252\345\212\250\345\210\207\346\215\242/InputBinding/Inputting.cs" deleted file mode 100644 index 721e561..0000000 --- "a/tests/TestAcad09plus/\350\276\223\345\205\245\346\263\225\350\207\252\345\212\250\345\210\207\346\215\242/InputBinding/Inputting.cs" +++ /dev/null @@ -1,697 +0,0 @@ -#pragma warning disable 0169 - -/* - * 0x01 赫思是键盘钩子: - * 可以在08.+使用; - * 0x02 InputBinding是切换输入法: - * 目前仅高版本, - * 08需要把文档栏工程的子类化拉取过来才可以使用 - */ - -namespace InputBinding; -using Autodesk.AutoCAD.Internal; -using System.Threading; -using System.Windows.Forms; - -public class Inputting -{ - static AppMessageFilter? _appMsgFilter = null; - static bool _shiftYN1 = false; - static bool _shiftYN2 = false; - static readonly ManualResetEvent _mre1 = new(false); - static readonly ManualResetEvent _mre2 = new(false); - static readonly ManualResetEvent _mre3 = new(false); - static readonly ManualResetEvent _mre4 = new(false); - static readonly ManualResetEvent _mre5 = new(false); - static int _iMode1 = -1; - static int _iMode2 = -1; - static int _iMode3 = -1; - /// - /// 是否卸载输入法切换 - /// - static bool _unloadYN = false; - /// - /// 当前输入法的状态:true中文,false英文 - /// - static bool _status = false; - static long _time; - - static int _hanCount1; - static int _hanCount1Add; - - /// - /// 具有焦点的窗口的句柄 - /// - static IntPtr _hwndFocus; - /// - /// 显示插入记号的窗口的句柄 - /// - static IntPtr _hwndCaret; - /// - /// 捕获鼠标的窗口的句柄 - /// - static IntPtr _hwndCapture; - /// - /// 线程内活动窗口的句柄 - /// - static IntPtr _hwndActive; - - [CommandMethod(nameof(ModalDialog))] - public void ModalDialog() - { - Acap.ShowModalDialog(new InputtingForm()); - } - - #region NewThread1 - //[IFoxInitialize] 需要自动加载时候启动它,但是它和赫思冲突 - public void InputBindingInitialize() - { - InputHelper.LoadConfig(); - Acap.PreTranslateMessage += Acap_PreTranslateMessage; - } - - /// - /// 载入和卸载输入法 - /// - [CommandMethod(nameof(IFoxInput))] - public static void IFoxInput() - { - if (!_unloadYN) - { - _unloadYN = true; - Acap.PreTranslateMessage += Acap_PreTranslateMessage; - Env.Printl("已卸载输入法自动切换"); - } - else - { - _unloadYN = false; - Acap.PreTranslateMessage += Acap_PreTranslateMessage; - Env.Printl("已加载输入法自动切换"); - } - } - - public static void Acap_PreTranslateMessage(object sender, PreTranslateMessageEventArgs e) - { - if (!_unloadYN) - { - _appMsgFilter = new(); - Application.AddMessageFilter(_appMsgFilter); - } - else - { - Application.RemoveMessageFilter(_appMsgFilter); - } - Acap.PreTranslateMessage -= Acap_PreTranslateMessage; - } - - public class AppMessageFilter : IMessageFilter - { - bool AcadVersion19() - { - return Acap.Version.Major >= 19 - && InputVar.CmdActive - && IsTrigger() - && _hwndCapture == IntPtr.Zero - && WindowsAPI.GetKeyboardLayout(WindowsAPI.GetCurrentThreadId()) < 0; - } - - public bool PreFilterMessage(ref Message m) - { - if (m.Msg == 515) - { - new Thread(new ThreadStart(NewThread1)).Start(); - _mre1.Set(); - return false; - } - - if (m.Msg == 512 && _shiftYN1 && AcadVersion19()) - { - _hanCount1Add = WindowsAPI.GetWindowTextLength(_hwndCaret); - StringBuilder text = new(2 * _hanCount1Add); - WindowsAPI.GetWindowText(_hwndFocus, text, text.Capacity); - if (!text.ToString().Contains(InputVar.StatusBar)) - { - Utils.SetFocusToDwgView(); - _shiftYN1 = false; - } - return false; - } - - if (m.Msg != 256) - return false; - - if (m.WParam != (IntPtr)229) - { - if (!_shiftYN1 && AcadVersion19()) - { - _hanCount1Add = WindowsAPI.GetWindowTextLength(_hwndCaret); - StringBuilder text = new(2 * _hanCount1Add); - WindowsAPI.GetWindowText(_hwndFocus, text, text.Capacity); - if (!text.ToString().Contains(InputVar.StatusBar)) - { - Utils.SetFocusToDwgView(); - _shiftYN1 = true; - } - } - return false; - } - - if (_shiftYN2) - { - _shiftYN2 = false; - return false; - } - - IntPtr window = IntPtr.Zero; - - var virtualKey = WindowsAPI.ImmGetVirtualKey(m.HWnd); - var vk = (int)virtualKey; - if (vk != 16 && vk != 17 && vk != 18 && vk != 91 && InputVar.CmdActive) - { - bool containCmdStr = false; - if (_hwndFocus == _hwndCaret) - { - Env.Printl(InputVar.CmdStr); - StringBuilder text = new(2 * WindowsAPI.GetWindowTextLength(_hwndCaret)); - WindowsAPI.GetWindowText(_hwndCaret, text, text.Capacity); - if (text.ToString().Contains(InputVar.CmdStr)) - containCmdStr = true; - } - - if (containCmdStr || IsTrigger()) - { - _hanCount1Add = WindowsAPI.GetWindowTextLength(_hwndCaret); - StringBuilder text = new(2 * _hanCount1Add); - WindowsAPI.GetWindowText(_hwndFocus, text, text.Capacity); - - if (text.ToString().Contains(InputVar.StatusBar)) - return false; - - WindowsAPI.ImmGetConversionStatus(WindowsAPI.ImmGetContext(_hwndFocus), out _iMode3, out _); - if (_hwndCapture == IntPtr.Zero) - Utils.SetFocusToDwgView(); - - PressKey(); - if (_hwndFocus == _hwndCaret) - { - if (containCmdStr) - window = _hwndCaret; - else if (!InputVar.去多余字母) - { - var doc = Acap.DocumentManager.MdiActiveDocument; - window = WindowsAPI.GetWindow(WindowsAPI.GetTopWindow(doc.Window.Handle), 2U); - } - } - else if (!InputVar.去多余字母 || _hwndCapture != IntPtr.Zero) - window = m.HWnd; - } - } - else if (InputVar.Windows10) - window = m.HWnd; - - if (window != IntPtr.Zero) - { - WindowsAPI.PostMessage(window, m.Msg, virtualKey, m.LParam); - return true; - } - return false; - } - } - - public static void NewThread1() - { - _mre1.WaitOne(); - Thread.Sleep(200); - if (InputVar.DK) - { - InputVar.DK = false; - return; - } - - if ((!InputVar.CmdActive || IsTrigger()) - && (InputVar.CmdActive || !InputVar.TextEditor || (_hwndCaret == IntPtr.Zero)) - && (InputVar.CmdActive || !InputVar.TextEditor || _hwndActive == _hwndFocus)) - return; - - _hanCount1Add = WindowsAPI.GetWindowTextLength(_hwndFocus); - StringBuilder text = new(2 * _hanCount1Add); - WindowsAPI.GetWindowText(_hwndFocus, text, text.Capacity); - - var textStr = text.ToString(); - if (textStr != string.Empty && !textStr.Contains(InputVar.StatusBar)) - { - var context = WindowsAPI.ImmGetContext(_hwndFocus); - WindowsAPI.ImmGetConversionStatus(context, out _iMode1, out _); - _status = WindowsAPI.ImmGetOpenStatus(context); - - _hanCount1 = GetHanNum(textStr); - var length = _hanCount1Add - 2 * _hanCount1; - - if (InputVar.Windows10 || InputVar.CtrlAndSpace || InputVar.CtrlAndShift || InputVar.WinAndSpace) - { - if (!InputVar.CMD) - { - if ((_status && length > _hanCount1) || - (!_status && length <= _hanCount1)) - { - PressKey(); - InputVar.CMD = false; - } - } - else - InputVar.CMD = false; - } - else if (InputVar.Shift || InputVar.Ctrl) - { - if (_iMode3 != -1) - { - if ((length <= _hanCount1 && _iMode3 == _iMode1) || - (length > _hanCount1 && _iMode3 > _iMode1)) - InputVar.CMD = false; - } - else if (!InputVar.CMD) - { - PressKey(); - new Thread(new ThreadStart(NewThread100)).Start(); - _mre2.Set(); - } - } - } - } - #endregion - - - /// - /// lisp Input判断文字中中英文的数量决定切换中英文 - /// - /// 文字 - [LispFunction(nameof(InputLispDoubleClick))] - public static ResultBuffer InputLispDoubleClick(ResultBuffer rb) - { - if (rb is null || _unloadYN) - return null!; - - InputVar.DK = true; - string str = rb.AsArray()[0].ToString(); - - var hanCount = GetHanNum(str); - var hanCountAdd = str.Length + hanCount; - var length = hanCountAdd - 2 * hanCount; - - IsTrigger(); - var context = WindowsAPI.ImmGetContext(_hwndFocus); - WindowsAPI.ImmGetConversionStatus(context, out _iMode1, out _); - _status = WindowsAPI.ImmGetOpenStatus(context); - - new Thread(() => { - _mre3.WaitOne(); - Thread.Sleep(50); - if (InputVar.CMD) - { - InputVar.CMD = false; - return; - } - - Thread.Sleep(50); - if (InputVar.Windows10 || InputVar.CtrlAndSpace || InputVar.CtrlAndShift || InputVar.WinAndSpace) - { - if (!(_status && length > hanCount)) - if (_status || length > hanCount) - return; - PressKey(); - } - else - { - if (!InputVar.Shift - && !InputVar.Ctrl || length <= hanCount - && _iMode3 == _iMode1 - && _iMode3 != -1 || length > hanCount - && _iMode3 > _iMode1 - && _iMode3 != -1) - return; - - PressKey(); - _hanCount1Add = hanCountAdd; - _hanCount1 = hanCount; - new Thread(new ThreadStart(NewThread100)).Start(); - _mre2.Set(); - } - }).Start(); - - _mre3.Set(); - return null!; - } - - /// - /// lisp Input命令开始 - /// - /// - /// - [LispFunction(nameof(InputLispStart))] - public static ResultBuffer InputLispStart(ResultBuffer rb) - { - if (rb is null || _unloadYN) - return null!; - - string str = rb.AsArray()[0].ToString(); - if (str != "True") - return null!; - - InputVar.CMD = true; - var hanCount = GetHanNum(str); - var hanCountAdd = str.Length + hanCount; - var length = hanCountAdd - 2 * hanCount; - - new Thread(() => { - _mre4.WaitOne(); - Thread.Sleep(100); - - IsTrigger(); - var context = WindowsAPI.ImmGetContext(_hwndFocus); - WindowsAPI.ImmGetConversionStatus(context, out _iMode1, out _); - _status = WindowsAPI.ImmGetOpenStatus(context); - - if (InputVar.Windows10 || InputVar.CtrlAndSpace || InputVar.CtrlAndShift || InputVar.WinAndSpace) - { - if (_status && length > hanCount) - { - Thread.Sleep(100); - PressKey(); - Thread.Sleep(500); - InputVar.CMD = false; - } - else if (!_status && length <= hanCount) - { - Thread.Sleep(100); - PressKey(); - Thread.Sleep(500); - InputVar.CMD = false; - } - else - { - Thread.Sleep(500); - InputVar.CMD = false; - } - } - else - { - if (!InputVar.Shift && !InputVar.Ctrl) - return; - if (length <= hanCount && _iMode3 == _iMode1 && _iMode3 != -1) - { - Thread.Sleep(500); - InputVar.CMD = false; - } - else if (length > hanCount && _iMode3 > _iMode1 && _iMode3 != -1) - { - Thread.Sleep(500); - InputVar.CMD = false; - } - else - { - Thread.Sleep(100); - PressKey(); - _hanCount1Add = hanCountAdd; - _hanCount1 = hanCount; - new Thread(new ThreadStart(NewThread100)).Start(); - _mre2.Set(); - } - } - }).Start(); - - _mre4.Set(); - return null!; - } - - /// - /// lisp Input命令结束 - /// - /// - /// - [LispFunction(nameof(InputLispEnd))] - public static ResultBuffer InputLispEnd(ResultBuffer rb) - { - if (rb is null || _unloadYN) - return null!; - - string str = rb.AsArray()[0].ToString(); - if (str != "True") - { - var hanCount = GetHanNum(str); - var hanCountAdd = str.Length + hanCount; - var length = hanCountAdd - 2 * hanCount; - - new Thread(() => { - _mre5.WaitOne(); - Thread.Sleep(100); - - IsTrigger(); - var context = WindowsAPI.ImmGetContext(_hwndFocus); - WindowsAPI.ImmGetConversionStatus(context, out _iMode1, out _); - _status = WindowsAPI.ImmGetOpenStatus(context); - - if (InputVar.Windows10 || InputVar.CtrlAndSpace || InputVar.CtrlAndShift || InputVar.WinAndSpace) - { - if (_status && length > hanCount) - PressKey(); - if (_status || length > hanCount) - return; - PressKey(); - } - else - { - if (!InputVar.Shift - && !InputVar.Ctrl || length <= hanCount - && _iMode3 == _iMode1 - && _iMode3 != -1 || length > hanCount - && _iMode3 > _iMode1 - && _iMode3 != -1) - return; - - PressKey(); - _hanCount1Add = hanCountAdd; - _hanCount1 = hanCount; - new Thread(new ThreadStart(NewThread100)).Start(); - _mre2.Set(); - } - }).Start(); - - - _mre5.Set(); - } - return null!; - } - - /// - /// 切换输入法 - /// - public static void PressKey() - { - // 需要用户先设置一下自己是怎么切换输入法的 - // 16 == shift - // 32 回车 - if (InputVar.Shift) - { - if (_iMode3 == -1) - Thread.Sleep(400); - WindowsAPI.KeybdEvent(16, 0, 0, 0); - WindowsAPI.KeybdEvent(16, 0, 2, 0); - } - else if (InputVar.Ctrl) - { - if (_iMode3 == -1) - Thread.Sleep(400); - WindowsAPI.KeybdEvent(17, 0, 0, 0); - WindowsAPI.KeybdEvent(17, 0, 2, 0); - } - else if (InputVar.CtrlAndSpace) - { - if (_iMode3 == -1) - Thread.Sleep(400); - WindowsAPI.KeybdEvent(17, 0, 0, 0); - WindowsAPI.KeybdEvent(32, 0, 0, 0); - WindowsAPI.KeybdEvent(32, 0, 2, 0); - WindowsAPI.KeybdEvent(17, 0, 2, 0); - if (WindowsAPI.GetKeyState(20) != 1) - return; - WindowsAPI.KeybdEvent(20, 0, 0, 0); - WindowsAPI.KeybdEvent(20, 0, 2, 0); - } - else if (InputVar.CtrlAndShift) - { - if (_iMode3 == -1) - Thread.Sleep(400); - WindowsAPI.KeybdEvent(16, 0, 0, 0); - WindowsAPI.KeybdEvent(17, 0, 0, 0); - WindowsAPI.KeybdEvent(17, 0, 2, 0); - WindowsAPI.KeybdEvent(16, 0, 2, 0); - if (WindowsAPI.GetKeyState(20) != 1) - return; - WindowsAPI.KeybdEvent(20, 0, 0, 0); - WindowsAPI.KeybdEvent(20, 0, 2, 0); - } - else if (InputVar.WinAndSpace) - { - if (_iMode3 == -1) - Thread.Sleep(400); - WindowsAPI.KeybdEvent(91, 0, 0, 0); - WindowsAPI.KeybdEvent(32, 0, 0, 0); - WindowsAPI.KeybdEvent(32, 0, 2, 0); - WindowsAPI.KeybdEvent(91, 0, 2, 0); - if (WindowsAPI.GetKeyState(20) != 1) - return; - WindowsAPI.KeybdEvent(20, 0, 0, 0); - WindowsAPI.KeybdEvent(20, 0, 2, 0); - } - else - { - var t1 = DateTime.Now.Ticks / 10000L; - _shiftYN2 = Math.Abs(_time - t1) < 80L; - _time = t1; - } - } - - public static void NewThread100() - { - _mre2.WaitOne(); - Thread.Sleep(100); - - IsTrigger(); - var context = WindowsAPI.ImmGetContext(_hwndFocus); - WindowsAPI.ImmGetConversionStatus(context, out _iMode2, out _); - _status = WindowsAPI.ImmGetOpenStatus(context); - - var length = _hanCount1Add - 2 * _hanCount1; - - if (length <= _hanCount1 - && _iMode1 > _iMode2 - && _iMode1 != _iMode3) - PressKey(); - - if (length > _hanCount1 - && _iMode1 < _iMode2 - && _iMode1 != _iMode3) - PressKey(); - - if (_iMode1 > _iMode2 && _iMode1 > _iMode3) - _iMode3 = _iMode1; - - if (_iMode1 < _iMode2 && _iMode3 < _iMode2) - _iMode3 = _iMode2; - - Thread.Sleep(300); - InputVar.DK = InputVar.CMD = false; - } - - /// - /// 窗体是否激活状态 - /// - /// - public static bool IsTrigger() - { - var pid = WindowsAPI.GetWindowThreadProcessId(WindowsAPI.GetForegroundWindow(), out uint lpdwProcessId); - var lpgui = WindowsAPI.GuiThreadInfo.Create(pid); - _hwndActive = lpgui.hwndActive; - _hwndFocus = lpgui.hwndFocus; - _hwndCaret = lpgui.hwndCaret; - _hwndCapture = lpgui.hwndCapture; - return lpgui.flags == 0; - } - - /// - /// 判断汉字的数量 - /// - /// 文字 - /// - public static int GetHanNum(string str) - { - // TODO 20221025 此处用正则效率低下,应该改用ASCII - int num = 0; - Regex regex = new(@"^[\u4E00-\u9FA5]{0,}$"); //匹配中文字符 - - for (int index = 0; index < str.Length; ++index) - if (regex.IsMatch(str[index].ToString())) - ++num; - - return num; - } -} - -public class InputVar -{ - public static bool 去多余字母; - public static bool Shift; - public static bool Ctrl; - public static bool CtrlAndSpace; - public static bool CtrlAndShift; - public static bool WinAndSpace; - - public static bool 增加字母; - public static bool DK; - public static bool CMD; - - public static bool Windows10 => Environment.OSVersion.Version.CompareTo(new Version("6.2")) >= 0; - public static string StatusBar = "StatusBar"; - public static string CmdStr = "命令:"; - - //[System.Runtime.CompilerServices.MethodImpl(System.Runtime.CompilerServices.MethodImplOptions.AggressiveInlining)] - public static bool CmdActive => (short)Acap.GetSystemVariable("cmdactive") == 0; - public static bool TextEditor => (short)Acap.GetSystemVariable("texteditor") == 0; -} - -public class InputHelper -{ - /// - /// 写入.ini配置 - /// - [DllImport("kernel32.dll")] - public static extern long WritePrivateProfileString(string section, string key, string value, string filepath); - - /// - /// 读取.ini配置 - /// - [DllImport("kernel32.dll")] - public static extern int GetPrivateProfileString(string section, string key, string def, StringBuilder returnvalue, int buffersize, string? filepath); - - /// - /// 返回键值对的值 - /// - /// 配置名称 - /// 键值对的键 - /// - public static string GetValue(string section, string key) - { - StringBuilder result = new(); - GetPrivateProfileString(section, key, "", result, 1024, InputHelper.SectionFile); - return result.ToString(); - } - - public static string SectionFile => $"{Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location)}\\{Section}.ini"; - public const string Section = "输入法配置"; - - /// - /// 加载配置 - /// - public static void LoadConfig() - { - if (!File.Exists(Section)) - { - InputVar.Shift = true; - return; - } - var str = GetValue(Section, "Shift切换"); - InputVar.Shift = str == "True"; - str = GetValue(Section, "Ctrl切换"); - InputVar.Ctrl = str == "True"; - str = GetValue(Section, "Ctrl+空格"); - InputVar.CtrlAndSpace = str == "True"; - str = GetValue(Section, "Ctrl+Shift"); - InputVar.CtrlAndShift = str == "True"; - str = GetValue(Section, "Win+空格"); - InputVar.WinAndSpace = str == "True"; - str = GetValue(Section, "去多余字母"); - InputVar.去多余字母 = str == "True"; - str = GetValue(Section, "增加字母"); - InputVar.增加字母 = str == "True"; - } -} \ No newline at end of file diff --git "a/tests/TestAcad09plus/\350\276\223\345\205\245\346\263\225\350\207\252\345\212\250\345\210\207\346\215\242/InputBinding/InputtingForm.Designer.cs" "b/tests/TestAcad09plus/\350\276\223\345\205\245\346\263\225\350\207\252\345\212\250\345\210\207\346\215\242/InputBinding/InputtingForm.Designer.cs" deleted file mode 100644 index 4fdb39c..0000000 --- "a/tests/TestAcad09plus/\350\276\223\345\205\245\346\263\225\350\207\252\345\212\250\345\210\207\346\215\242/InputBinding/InputtingForm.Designer.cs" +++ /dev/null @@ -1,302 +0,0 @@ -namespace InputBinding; - -partial class InputtingForm -{ - - #region Windows Form Designer generated code - - /// - ///Required method for Designer support - do not modify - ///the contents of this method with the code editor. - /// - private void InitializeComponent() - { - this.button1 = new System.Windows.Forms.Button(); - this.button2 = new System.Windows.Forms.Button(); - this.button3 = new System.Windows.Forms.Button(); - this.checkBox1_shift = new System.Windows.Forms.CheckBox(); - this.checkBox2_ctrl = new System.Windows.Forms.CheckBox(); - this.checkBox3_切换后多余字母 = new System.Windows.Forms.CheckBox(); - this.checkBox4_ctrlAddSpace = new System.Windows.Forms.CheckBox(); - this.checkBox5_ctrlAndShift = new System.Windows.Forms.CheckBox(); - this.checkBox6_winAndSpace = new System.Windows.Forms.CheckBox(); - this.checkBox7_切换后首字母 = new System.Windows.Forms.CheckBox(); - this.label1 = new System.Windows.Forms.Label(); - this.label2 = new System.Windows.Forms.Label(); - this.label3 = new System.Windows.Forms.Label(); - this.label5 = new System.Windows.Forms.Label(); - this.label4 = new System.Windows.Forms.Label(); - this.groupBox1 = new System.Windows.Forms.GroupBox(); - this.groupBox2 = new System.Windows.Forms.GroupBox(); - this.groupBox3 = new System.Windows.Forms.GroupBox(); - this.groupBox1.SuspendLayout(); - this.groupBox2.SuspendLayout(); - this.groupBox3.SuspendLayout(); - this.SuspendLayout(); - // - // button1 - // - this.button1.Font = new System.Drawing.Font("微软雅黑", 10.5F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(134))); - this.button1.Location = new System.Drawing.Point(81, 489); - this.button1.Name = "button1"; - this.button1.Size = new System.Drawing.Size(142, 32); - this.button1.TabIndex = 3; - this.button1.Text = "保存配置并退出"; - this.button1.UseVisualStyleBackColor = true; - this.button1.Click += new System.EventHandler(this.Button1_Click); - // - // button2 - // - this.button2.Font = new System.Drawing.Font("微软雅黑", 9F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(134))); - this.button2.Location = new System.Drawing.Point(15, 155); - this.button2.Name = "button2"; - this.button2.Size = new System.Drawing.Size(116, 32); - this.button2.TabIndex = 9; - this.button2.Text = "临时关闭自动切换"; - this.button2.UseVisualStyleBackColor = true; - this.button2.Click += new System.EventHandler(this.Button2_Click); - // - // button3 - // - this.button3.Font = new System.Drawing.Font("微软雅黑", 9F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(134))); - this.button3.Location = new System.Drawing.Point(137, 155); - this.button3.Name = "button3"; - this.button3.Size = new System.Drawing.Size(132, 32); - this.button3.TabIndex = 10; - this.button3.Text = "打开自动切换"; - this.button3.UseVisualStyleBackColor = true; - this.button3.Click += new System.EventHandler(this.Button3_Click); - // - // checkBox1_shift - // - this.checkBox1_shift.AutoSize = true; - this.checkBox1_shift.Checked = true; - this.checkBox1_shift.CheckState = System.Windows.Forms.CheckState.Checked; - this.checkBox1_shift.Font = new System.Drawing.Font("微软雅黑", 10.5F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(134))); - this.checkBox1_shift.Location = new System.Drawing.Point(10, 54); - this.checkBox1_shift.Name = "checkBox1_shift"; - this.checkBox1_shift.Size = new System.Drawing.Size(167, 24); - this.checkBox1_shift.TabIndex = 6; - this.checkBox1_shift.Text = "Shift切换中英文(默认)"; - this.checkBox1_shift.UseVisualStyleBackColor = true; - this.checkBox1_shift.CheckedChanged += new System.EventHandler(this.CheckBox1_CheckedChanged); - // - // checkBox2_ctrl - // - this.checkBox2_ctrl.AutoSize = true; - this.checkBox2_ctrl.Font = new System.Drawing.Font("微软雅黑", 10.5F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(134))); - this.checkBox2_ctrl.Location = new System.Drawing.Point(10, 84); - this.checkBox2_ctrl.Name = "checkBox2_ctrl"; - this.checkBox2_ctrl.Size = new System.Drawing.Size(121, 24); - this.checkBox2_ctrl.TabIndex = 7; - this.checkBox2_ctrl.Text = "Ctrl切换中英文"; - this.checkBox2_ctrl.UseVisualStyleBackColor = true; - this.checkBox2_ctrl.CheckedChanged += new System.EventHandler(this.CheckBox2_CheckedChanged); - // - // checkBox3_切换后多余字母 - // - this.checkBox3_切换后多余字母.AutoSize = true; - this.checkBox3_切换后多余字母.Enabled = false; - this.checkBox3_切换后多余字母.Font = new System.Drawing.Font("微软雅黑", 10.5F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(134))); - this.checkBox3_切换后多余字母.Location = new System.Drawing.Point(15, 25); - this.checkBox3_切换后多余字母.Name = "checkBox3_切换后多余字母"; - this.checkBox3_切换后多余字母.Size = new System.Drawing.Size(196, 24); - this.checkBox3_切换后多余字母.TabIndex = 8; - this.checkBox3_切换后多余字母.Text = "消除切换后命令行多余字母"; - this.checkBox3_切换后多余字母.UseVisualStyleBackColor = true; - this.checkBox3_切换后多余字母.CheckedChanged += new System.EventHandler(this.CheckBox3_CheckedChanged); - // - // checkBox4_ctrlAddSpace - // - this.checkBox4_ctrlAddSpace.AutoSize = true; - this.checkBox4_ctrlAddSpace.Font = new System.Drawing.Font("微软雅黑", 10.5F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(134))); - this.checkBox4_ctrlAddSpace.Location = new System.Drawing.Point(10, 54); - this.checkBox4_ctrlAddSpace.Name = "checkBox4_ctrlAddSpace"; - this.checkBox4_ctrlAddSpace.Size = new System.Drawing.Size(89, 24); - this.checkBox4_ctrlAddSpace.TabIndex = 11; - this.checkBox4_ctrlAddSpace.Text = "Ctrl+空格"; - this.checkBox4_ctrlAddSpace.UseVisualStyleBackColor = true; - this.checkBox4_ctrlAddSpace.CheckedChanged += new System.EventHandler(this.CheckBox4_CheckedChanged); - // - // checkBox5_ctrlAndShift - // - this.checkBox5_ctrlAndShift.AutoSize = true; - this.checkBox5_ctrlAndShift.Font = new System.Drawing.Font("微软雅黑", 10.5F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(134))); - this.checkBox5_ctrlAndShift.Location = new System.Drawing.Point(10, 84); - this.checkBox5_ctrlAndShift.Name = "checkBox5_ctrlAndShift"; - this.checkBox5_ctrlAndShift.Size = new System.Drawing.Size(92, 24); - this.checkBox5_ctrlAndShift.TabIndex = 12; - this.checkBox5_ctrlAndShift.Text = "Ctrl+Shift"; - this.checkBox5_ctrlAndShift.UseVisualStyleBackColor = true; - this.checkBox5_ctrlAndShift.CheckedChanged += new System.EventHandler(this.CheckBox5_CheckedChanged); - // - // checkBox6_winAndSpace - // - this.checkBox6_winAndSpace.AutoSize = true; - this.checkBox6_winAndSpace.Font = new System.Drawing.Font("微软雅黑", 10.5F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(134))); - this.checkBox6_winAndSpace.Location = new System.Drawing.Point(10, 114); - this.checkBox6_winAndSpace.Name = "checkBox6_winAndSpace"; - this.checkBox6_winAndSpace.Size = new System.Drawing.Size(93, 24); - this.checkBox6_winAndSpace.TabIndex = 13; - this.checkBox6_winAndSpace.Text = "Win+空格"; - this.checkBox6_winAndSpace.UseVisualStyleBackColor = true; - this.checkBox6_winAndSpace.CheckedChanged += new System.EventHandler(this.CheckBox6_CheckedChanged); - // - // checkBox7_切换后首字母 - // - this.checkBox7_切换后首字母.AutoSize = true; - this.checkBox7_切换后首字母.Enabled = false; - this.checkBox7_切换后首字母.Font = new System.Drawing.Font("微软雅黑", 10.5F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(134))); - this.checkBox7_切换后首字母.Location = new System.Drawing.Point(15, 91); - this.checkBox7_切换后首字母.Name = "checkBox7_切换后首字母"; - this.checkBox7_切换后首字母.Size = new System.Drawing.Size(238, 24); - this.checkBox7_切换后首字母.TabIndex = 18; - this.checkBox7_切换后首字母.Text = "解决切换后需多按一次命令首字母"; - this.checkBox7_切换后首字母.UseVisualStyleBackColor = true; - this.checkBox7_切换后首字母.CheckedChanged += new System.EventHandler(this.CheckBox7_CheckedChanged); - // - // label1 - // - this.label1.AutoSize = true; - this.label1.Font = new System.Drawing.Font("微软雅黑", 9F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(134))); - this.label1.Location = new System.Drawing.Point(87, 524); - this.label1.Name = "label1"; - this.label1.Size = new System.Drawing.Size(142, 17); - this.label1.TabIndex = 4; - this.label1.Text = "(.ini配置文件在dll文件夹)"; - // - // label2 - // - this.label2.AutoSize = true; - this.label2.Font = new System.Drawing.Font("微软雅黑", 10.5F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(134))); - this.label2.Location = new System.Drawing.Point(6, 26); - this.label2.Name = "label2"; - this.label2.Size = new System.Drawing.Size(169, 20); - this.label2.TabIndex = 16; - this.label2.Text = "中/英文状态切换快捷键:"; - // - // label3 - // - this.label3.AutoSize = true; - this.label3.Font = new System.Drawing.Font("微软雅黑", 10.5F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(134))); - this.label3.Location = new System.Drawing.Point(6, 26); - this.label3.Name = "label3"; - this.label3.Size = new System.Drawing.Size(214, 20); - this.label3.TabIndex = 17; - this.label3.Text = "中文(CH)/英文(EN)切换快捷键:"; - // - // label5 - // - this.label5.AutoSize = true; - this.label5.Enabled = false; - this.label5.Font = new System.Drawing.Font("微软雅黑", 9F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(134))); - this.label5.Location = new System.Drawing.Point(12, 117); - this.label5.Name = "label5"; - this.label5.Size = new System.Drawing.Size(240, 34); - this.label5.TabIndex = 19; - this.label5.Text = "(部分输入法和CAD版本存在切换后\r\n需多按一次首命令首字母,勾选此项可解决)"; - // - // label4 - // - this.label4.AutoSize = true; - this.label4.Enabled = false; - this.label4.Font = new System.Drawing.Font("微软雅黑", 9F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(134))); - this.label4.Location = new System.Drawing.Point(12, 51); - this.label4.Name = "label4"; - this.label4.Size = new System.Drawing.Size(204, 34); - this.label4.TabIndex = 17; - this.label4.Text = "(部分输入法和CAD版本存在切换后\r\n命令行有多余字母,勾选此项可消除)"; - // - // groupBox1 - // - this.groupBox1.Controls.Add(this.label2); - this.groupBox1.Controls.Add(this.checkBox1_shift); - this.groupBox1.Controls.Add(this.checkBox2_ctrl); - this.groupBox1.Font = new System.Drawing.Font("微软雅黑", 10.5F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(134))); - this.groupBox1.Location = new System.Drawing.Point(12, 12); - this.groupBox1.Name = "groupBox1"; - this.groupBox1.Size = new System.Drawing.Size(275, 117); - this.groupBox1.TabIndex = 14; - this.groupBox1.TabStop = false; - this.groupBox1.Text = "Shift懒人版"; - // - // groupBox2 - // - this.groupBox2.Controls.Add(this.label3); - this.groupBox2.Controls.Add(this.checkBox4_ctrlAddSpace); - this.groupBox2.Controls.Add(this.checkBox6_winAndSpace); - this.groupBox2.Controls.Add(this.checkBox5_ctrlAndShift); - this.groupBox2.Font = new System.Drawing.Font("微软雅黑", 10.5F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(134))); - this.groupBox2.Location = new System.Drawing.Point(12, 135); - this.groupBox2.Name = "groupBox2"; - this.groupBox2.Size = new System.Drawing.Size(275, 149); - this.groupBox2.TabIndex = 15; - this.groupBox2.TabStop = false; - this.groupBox2.Text = "进阶版"; - // - // groupBox3 - // - this.groupBox3.Controls.Add(this.button2); - this.groupBox3.Controls.Add(this.button3); - this.groupBox3.Controls.Add(this.label4); - this.groupBox3.Controls.Add(this.label5); - this.groupBox3.Controls.Add(this.checkBox3_切换后多余字母); - this.groupBox3.Controls.Add(this.checkBox7_切换后首字母); - this.groupBox3.Font = new System.Drawing.Font("微软雅黑", 10.5F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(134))); - this.groupBox3.Location = new System.Drawing.Point(12, 290); - this.groupBox3.Name = "groupBox3"; - this.groupBox3.Size = new System.Drawing.Size(275, 193); - this.groupBox3.TabIndex = 16; - this.groupBox3.TabStop = false; - this.groupBox3.Text = "其它"; - // - // InputtingForm - // - this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 12F); - this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; - this.ClientSize = new System.Drawing.Size(296, 550); - this.Controls.Add(this.groupBox3); - this.Controls.Add(this.groupBox2); - this.Controls.Add(this.groupBox1); - this.Controls.Add(this.button1); - this.Controls.Add(this.label1); - this.FormBorderStyle = System.Windows.Forms.FormBorderStyle.FixedDialog; - this.MaximizeBox = false; - this.MinimizeBox = false; - this.Name = "InputtingForm"; - this.StartPosition = System.Windows.Forms.FormStartPosition.CenterScreen; - this.Text = "输入法自动切换"; - this.Load += new System.EventHandler(this.Form1_Load); - this.groupBox1.ResumeLayout(false); - this.groupBox1.PerformLayout(); - this.groupBox2.ResumeLayout(false); - this.groupBox2.PerformLayout(); - this.groupBox3.ResumeLayout(false); - this.groupBox3.PerformLayout(); - this.ResumeLayout(false); - this.PerformLayout(); - - } - - #endregion - private System.Windows.Forms.Button button1; - private System.Windows.Forms.Button button2; - private System.Windows.Forms.Button button3; - - private System.Windows.Forms.CheckBox checkBox1_shift; - private System.Windows.Forms.CheckBox checkBox2_ctrl; - private System.Windows.Forms.CheckBox checkBox3_切换后多余字母; - private System.Windows.Forms.CheckBox checkBox4_ctrlAddSpace; - private System.Windows.Forms.CheckBox checkBox5_ctrlAndShift; - private System.Windows.Forms.CheckBox checkBox6_winAndSpace; - private System.Windows.Forms.CheckBox checkBox7_切换后首字母; - private System.Windows.Forms.GroupBox groupBox1; - private System.Windows.Forms.GroupBox groupBox2; - private System.Windows.Forms.GroupBox groupBox3; - - private System.Windows.Forms.Label label1; - private System.Windows.Forms.Label label2; - private System.Windows.Forms.Label label3; - private System.Windows.Forms.Label label4; - private System.Windows.Forms.Label label5; -} \ No newline at end of file diff --git "a/tests/TestAcad09plus/\350\276\223\345\205\245\346\263\225\350\207\252\345\212\250\345\210\207\346\215\242/InputBinding/InputtingForm.cs" "b/tests/TestAcad09plus/\350\276\223\345\205\245\346\263\225\350\207\252\345\212\250\345\210\207\346\215\242/InputBinding/InputtingForm.cs" deleted file mode 100644 index 5d24110..0000000 --- "a/tests/TestAcad09plus/\350\276\223\345\205\245\346\263\225\350\207\252\345\212\250\345\210\207\346\215\242/InputBinding/InputtingForm.cs" +++ /dev/null @@ -1,178 +0,0 @@ -namespace InputBinding; -using System.Windows.Forms; - -public partial class InputtingForm : Form -{ - public InputtingForm() - { - InitializeComponent(); - } - - private void Form1_Load(object sender, EventArgs e) - { - if (!File.Exists(InputHelper.SectionFile)) - { - InputVar.Shift = true; - return; - } - - var str = InputHelper.GetValue(InputHelper.Section, "Shift切换"); - checkBox1_shift.Checked = InputVar.Shift = bool.Parse(str); - str = InputHelper.GetValue(InputHelper.Section, "Ctrl切换"); - checkBox2_ctrl.Checked = InputVar.Ctrl = bool.Parse(str); - str = InputHelper.GetValue(InputHelper.Section, "Ctrl+空格"); - checkBox4_ctrlAddSpace.Checked = InputVar.CtrlAndSpace = bool.Parse(str); - str = InputHelper.GetValue(InputHelper.Section, "Ctrl+Shift"); - checkBox5_ctrlAndShift.Checked = InputVar.CtrlAndShift = bool.Parse(str); - str = InputHelper.GetValue(InputHelper.Section, "Win+空格"); - checkBox6_winAndSpace.Checked = InputVar.WinAndSpace = bool.Parse(str); - str = InputHelper.GetValue(InputHelper.Section, "去多余字母"); - checkBox3_切换后多余字母.Checked = InputVar.去多余字母 = bool.Parse(str); - str = InputHelper.GetValue(InputHelper.Section, "增加字母"); - checkBox7_切换后首字母.Checked = InputVar.增加字母 = bool.Parse(str); - } - /// - /// 保存配置并退出 - /// - /// - /// - private void Button1_Click(object sender, EventArgs e) - { - try - { - InputVar.Shift = checkBox1_shift.Checked; - InputVar.Ctrl = checkBox2_ctrl.Checked; - InputVar.CtrlAndSpace = checkBox4_ctrlAddSpace.Checked; - InputVar.CtrlAndShift = checkBox5_ctrlAndShift.Checked; - InputVar.WinAndSpace = checkBox6_winAndSpace.Checked; - InputVar.去多余字母 = checkBox3_切换后多余字母.Checked; - InputVar.增加字母 = checkBox7_切换后首字母.Checked; - InputHelper.WritePrivateProfileString(InputHelper.Section, "Shift切换", checkBox1_shift.Checked.ToString(), InputHelper.SectionFile); - InputHelper.WritePrivateProfileString(InputHelper.Section, "Ctrl切换", checkBox2_ctrl.Checked.ToString(), InputHelper.SectionFile); - InputHelper.WritePrivateProfileString(InputHelper.Section, "Ctrl+空格", checkBox4_ctrlAddSpace.Checked.ToString(), InputHelper.SectionFile); - InputHelper.WritePrivateProfileString(InputHelper.Section, "Ctrl+Shift", checkBox5_ctrlAndShift.Checked.ToString(), InputHelper.SectionFile); - InputHelper.WritePrivateProfileString(InputHelper.Section, "Win+空格", checkBox6_winAndSpace.Checked.ToString(), InputHelper.SectionFile); - InputHelper.WritePrivateProfileString(InputHelper.Section, "去多余字母", checkBox3_切换后多余字母.Checked.ToString(), InputHelper.SectionFile); - InputHelper.WritePrivateProfileString(InputHelper.Section, "增加字母", checkBox7_切换后首字母.Checked.ToString(), InputHelper.SectionFile); - - if (InputHelper.SectionFile != null) - MessageBox.Show("保存成功"); - } - catch - { - MessageBox.Show("保存失败"); - } - Close(); - } - /// - /// 临时关闭自动切换 - /// - /// - /// - private void Button2_Click(object sender, EventArgs e) - { - Inputting.IFoxInput(); - } - /// - /// 打开自动切换 - /// - /// - /// - private void Button3_Click(object sender, EventArgs e) - { - Inputting.IFoxInput(); - } - - /// - /// Shift切换中英文(默认) - /// - /// - /// - private void CheckBox1_CheckedChanged(object sender, EventArgs e) - { - if (!checkBox1_shift.Checked) - return; - checkBox2_ctrl.Checked = false; - checkBox4_ctrlAddSpace.Checked = false; - checkBox5_ctrlAndShift.Checked = false; - checkBox6_winAndSpace.Checked = false; - } - /// - /// Ctrl切换中英文 - /// - /// - /// - private void CheckBox2_CheckedChanged(object sender, EventArgs e) - { - if (!checkBox2_ctrl.Checked) - return; - checkBox1_shift.Checked = false; - checkBox4_ctrlAddSpace.Checked = false; - checkBox5_ctrlAndShift.Checked = false; - checkBox6_winAndSpace.Checked = false; - } - /// - /// 消除切换后命令行多余字母 - /// - /// - /// - private void CheckBox3_CheckedChanged(object sender, EventArgs e) - { - if (!checkBox3_切换后多余字母.Checked) - return; - checkBox7_切换后首字母.Checked = false; - } - /// - /// Ctrl+空格 - /// - /// - /// - private void CheckBox4_CheckedChanged(object sender, EventArgs e) - { - if (!checkBox4_ctrlAddSpace.Checked) - return; - checkBox1_shift.Checked = - checkBox2_ctrl.Checked = - checkBox5_ctrlAndShift.Checked = - checkBox6_winAndSpace.Checked = false; - } - /// - /// Ctrl+Shift - /// - /// - /// - private void CheckBox5_CheckedChanged(object sender, EventArgs e) - { - if (!checkBox5_ctrlAndShift.Checked) - return; - checkBox1_shift.Checked = - checkBox2_ctrl.Checked = - checkBox4_ctrlAddSpace.Checked = - checkBox6_winAndSpace.Checked = false; - } - /// - /// Win+空格 - /// - /// - /// - private void CheckBox6_CheckedChanged(object sender, EventArgs e) - { - if (!checkBox6_winAndSpace.Checked) - return; - checkBox1_shift.Checked = - checkBox2_ctrl.Checked = - checkBox4_ctrlAddSpace.Checked = - checkBox5_ctrlAndShift.Checked = false; - } - /// - /// 解决切换后需多按一次命令首字母 - /// - /// - /// - private void CheckBox7_CheckedChanged(object sender, EventArgs e) - { - if (!checkBox7_切换后首字母.Checked) - return; - checkBox3_切换后多余字母.Checked = false; - } -} \ No newline at end of file diff --git "a/tests/TestAcad09plus/\350\276\223\345\205\245\346\263\225\350\207\252\345\212\250\345\210\207\346\215\242/InputBinding/InputtingForm.resx" "b/tests/TestAcad09plus/\350\276\223\345\205\245\346\263\225\350\207\252\345\212\250\345\210\207\346\215\242/InputBinding/InputtingForm.resx" deleted file mode 100644 index d58980a..0000000 --- "a/tests/TestAcad09plus/\350\276\223\345\205\245\346\263\225\350\207\252\345\212\250\345\210\207\346\215\242/InputBinding/InputtingForm.resx" +++ /dev/null @@ -1,120 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - text/microsoft-resx - - - 2.0 - - - System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - - - System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - - \ No newline at end of file -- Gitee From 5f58e4d0d4849f6f8dcadc5907e3271b4bddb72a Mon Sep 17 00:00:00 2001 From: liuqihong <540762622@qq.com> Date: Sat, 5 Nov 2022 21:13:41 +0800 Subject: [PATCH 615/675] =?UTF-8?q?=E4=BF=AE=E6=94=B9=E8=BE=93=E5=85=A5?= =?UTF-8?q?=E6=B3=95=E5=87=BD=E6=95=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../IMEControl.cs" | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git "a/tests/TestAcad09plus/\346\213\246\346\210\252\350\276\223\345\205\245\346\263\225/IMEControl.cs" "b/tests/TestAcad09plus/\346\213\246\346\210\252\350\276\223\345\205\245\346\263\225/IMEControl.cs" index 224575c..e9b15f7 100644 --- "a/tests/TestAcad09plus/\346\213\246\346\210\252\350\276\223\345\205\245\346\263\225/IMEControl.cs" +++ "b/tests/TestAcad09plus/\346\213\246\346\210\252\350\276\223\345\205\245\346\263\225/IMEControl.cs" @@ -381,7 +381,7 @@ internal static void SetIMEHook() /// 豁免命令处理 ///
    /// - static bool ExceptCmdsTask() + static bool ExceptCmds_AutoEn2Cn_Task() { var dm = Acap.DocumentManager; if (dm.Count == 0) @@ -442,7 +442,7 @@ public static bool IMEHook(int nCode, int wParam, IntPtr lParam) //Debugx.Printl(wParam); // 先判断键入的数字更快,再判断豁免命令 - if (!ExceptCmdsTask()) + if (!ExceptCmds_AutoEn2Cn_Task()) return false; var focus = WindowsAPI.GetFocus(); -- Gitee From d6d2ad59cc6a003d872657bed3cb12ff6f96af0e Mon Sep 17 00:00:00 2001 From: liuqihong <540762622@qq.com> Date: Sat, 5 Nov 2022 21:14:18 +0800 Subject: [PATCH 616/675] =?UTF-8?q?=E7=A8=8B=E5=BA=8F=E9=9B=86=E6=8B=A6?= =?UTF-8?q?=E6=88=AA=E9=94=99=E8=AF=AF?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/IFoxCAD.Basal/General/DebugHelper.cs | 12 +++- src/IFoxCAD.LoadForm/AssemblyDependent.cs | 15 ++++- .../DefaultAssemblyResolve.cs | 65 ++++++++++++++----- tests/TestAcad08/TestAcad08.csproj | 2 +- tests/TestAcad09plus/TestAcad09plus.csproj | 2 +- 5 files changed, 75 insertions(+), 21 deletions(-) diff --git a/src/IFoxCAD.Basal/General/DebugHelper.cs b/src/IFoxCAD.Basal/General/DebugHelper.cs index c23342b..0423702 100644 --- a/src/IFoxCAD.Basal/General/DebugHelper.cs +++ b/src/IFoxCAD.Basal/General/DebugHelper.cs @@ -1,4 +1,6 @@ -namespace IFoxCAD.Basal; +using System.Threading; + +namespace IFoxCAD.Basal; public static class Debugx { @@ -14,11 +16,17 @@ public static void Printl(object message, bool time = true) return; if (time) - message = $"{DateTime.Now.ToLongDateString() + DateTime.Now.TimeOfDay}::" + message; + message = $"{DateTime.Now.ToLongDateString() + DateTime.Now.TimeOfDay}\n" + + $"\t\tThreadId:{Thread.CurrentThread.ManagedThreadId}\n" + + $"\t\t{message}"; #if DEBUG + //System.Diagnostics.Debug.Indent(); System.Diagnostics.Debug.WriteLine(message); + //System.Diagnostics.Debug.Unindent(); #else + //System.Diagnostics.Debug.Indent(); System.Diagnostics.Trace.WriteLine(message); + //System.Diagnostics.Debug.Unindent(); #endif } } \ No newline at end of file diff --git a/src/IFoxCAD.LoadForm/AssemblyDependent.cs b/src/IFoxCAD.LoadForm/AssemblyDependent.cs index f7d3987..0197c0c 100644 --- a/src/IFoxCAD.LoadForm/AssemblyDependent.cs +++ b/src/IFoxCAD.LoadForm/AssemblyDependent.cs @@ -1,5 +1,6 @@ #define HarmonyPatch #define HarmonyPatch_1 +//#define HarmonyPatch_2 namespace IFoxCAD.LoadEx; @@ -16,7 +17,8 @@ public class AssemblyDependent : IDisposable #region 字段和事件 /// - /// 当前域加载事件,运行时出错的话,就靠这个事件来解决 + /// 当前域加载事件
    + /// 运行时出错的话,就靠这个事件来解决 ///
    public event ResolveEventHandler CurrentDomainAssemblyResolveEvent { @@ -24,6 +26,15 @@ public event ResolveEventHandler CurrentDomainAssemblyResolveEvent remove { AppDomain.CurrentDomain.AssemblyResolve -= value; } } + /// + /// 当前域参照加载事件 + /// + public event ResolveEventHandler CurrentDomainReflectionOnlyAssemblyResolveEvent + { + add { AppDomain.CurrentDomain.ReflectionOnlyAssemblyResolve += value; } + remove { AppDomain.CurrentDomain.ReflectionOnlyAssemblyResolve -= value; } + } + /// /// 拦截cad的Loader异常:默认是 /// @@ -38,6 +49,7 @@ public AssemblyDependent() { // 初始化一次,反复load CurrentDomainAssemblyResolveEvent += AssemblyHelper.DefaultAssemblyResolve; + CurrentDomainReflectionOnlyAssemblyResolveEvent += AssemblyHelper.ReflectionOnlyAssemblyResolve; } #endregion @@ -401,6 +413,7 @@ protected virtual void Dispose(bool disposing) IsDisposed = true; CurrentDomainAssemblyResolveEvent -= AssemblyHelper.DefaultAssemblyResolve; + CurrentDomainReflectionOnlyAssemblyResolveEvent -= AssemblyHelper.ReflectionOnlyAssemblyResolve; } #endregion } diff --git a/src/IFoxCAD.LoadForm/DefaultAssemblyResolve.cs b/src/IFoxCAD.LoadForm/DefaultAssemblyResolve.cs index 7426ce4..a1c9b51 100644 --- a/src/IFoxCAD.LoadForm/DefaultAssemblyResolve.cs +++ b/src/IFoxCAD.LoadForm/DefaultAssemblyResolve.cs @@ -1,9 +1,16 @@ namespace IFoxCAD.LoadEx; using System.Diagnostics; +using System.Threading; public class AssemblyHelper { + public static Assembly? ReflectionOnlyAssemblyResolve(object sender, ResolveEventArgs e) + { + var cadAss = AppDomain.CurrentDomain.ReflectionOnlyGetAssemblies(); + return Resolve(cadAss, sender, e); + } + /// /// /// 程序域运行事件 @@ -16,46 +23,72 @@ public class AssemblyHelper /// /// /// - /// + /// /// 程序集如果为空就不会调用 - public static Assembly? DefaultAssemblyResolve(object sender, ResolveEventArgs args) + public static Assembly? DefaultAssemblyResolve(object sender, ResolveEventArgs e) { - Assembly? result = null; var cadAss = AppDomain.CurrentDomain.GetAssemblies(); + return Resolve(cadAss, sender, e); + } + public static Assembly? Resolve(Assembly[] cadAss, object sender, ResolveEventArgs e) + { // 名称和版本号都一致的,调用它 - result = cadAss.FirstOrDefault(ass => ass.GetName().FullName == args.Name); + var result = cadAss.FirstOrDefault(ass => ass.GetName().FullName == e.Name); if (result != null) return result; // 获取名称一致,但是版本号不同的,调用最后的可用版本 - var ag = GetAssemblyName(args.Name); - + var ag = GetAssemblyName(e.Name); // 获取最后一个符合条件的, - // 否则a.dll引用b.dll函数的时候,b.dll修改重生成之后, - // 加载进去会调用第一个版本的b.dll, + // 否则a.dll引用b.dll函数的时候,b.dll修改重生成之后,加载进去会调用第一个版本的b.dll, // vs会迭代程序版本号的*,所以最后的可用就是循环到最后的. for (int i = 0; i < cadAss.Length; i++) + if (GetAssemblyName(cadAss[i].GetName().FullName) == ag) + result = cadAss[i]; + + // 惊惊:我的 cad21+vs22容易触发这个WPF资源的问题 + // 偶发性,怀疑是载入顺序的导致 + // 当cad界面完成之后找的话,大概率能够通过下面语句跳过 + // 如果初始化遇到了, + // 0x01 或者是某个多线程忘记lock变量,然后导致整个逻辑错误 + // 0x02 建议重新安装 + if (result == null) { - if (GetAssemblyName(cadAss[i].GetName().FullName) != ag) - continue; - result = cadAss[i]; + //args.Name = "Microsoft.VisualStudio.DesignTools.WpfTap.resources, Version=17.0.0.0, Culture=zh-CN, PublicKeyToken=b03f5f7f11d50a3a" + if (e.Name.Contains("Microsoft.VisualStudio.DesignTools.WpfTap.resources")) + { + Debug.WriteLine($"{nameof(LoadEx)}::程序集找不到::Microsoft.VisualStudio.DesignTools.WpfTap.resources"); + //var str = @"C:\Program Files\Microsoft Visual Studio\2022\Enterprise\Common7\IDE\CommonExtensions\Microsoft\XamlDiagnostics\Framework\x64\Microsoft.VisualStudio.DesignTools.WpfTap.dll"; + //result = Assembly.Load(str);// 这里又循环到开头了 + //result = Assembly.ReflectionOnlyLoad(str);// 这里又循环到开头了,提示它是保护的 + + // 这里不是抓资源的程序集,而是抓了它本体,然后发现偶尔能通过,很奇怪. + for (int i = 0; i < cadAss.Length; i++) + if (cadAss[i].GetName().Name.Contains("WpfTap")) + result = cadAss[i]; + } + Thread.Sleep(5000); } if (result == null) { var sb = new StringBuilder(); - sb.AppendLine(nameof(DefaultAssemblyResolve) + "出错,没有在本程序集中找到它"); - sb.AppendLine("++参数名:: " + GetAssemblyName(args.Name)); - sb.AppendLine("++参数完整信息:: " + args.Name); + sb.AppendLine($"{nameof(LoadEx)}------------------------------------------------------------"); + sb.AppendLine(nameof(DefaultAssemblyResolve) + "出错,程序集无法找到它"); + sb.AppendLine("++参数名:: " + GetAssemblyName(e.Name)); + sb.AppendLine("++参数完整信息:: " + e.Name); for (int i = 0; i < cadAss.Length; i++) sb.AppendLine("-------匹配对象:: " + GetAssemblyName(cadAss[i].GetName().FullName)); + + sb.AppendLine($"程序集找不到,遇到无法处理的错误,杀死当前进程!"); + sb.AppendLine($"{nameof(LoadEx)}------------------------------------------------------------"); Debug.WriteLine(sb.ToString()); - Debug.WriteLine("无法处理的错误,杀死当前进程"); - System.Diagnostics.Process.GetCurrentProcess().Kill(); + Process.GetCurrentProcess().Kill(); //Debugger.Break(); } + return result; } diff --git a/tests/TestAcad08/TestAcad08.csproj b/tests/TestAcad08/TestAcad08.csproj index d4ec8a3..3e2cae2 100644 --- a/tests/TestAcad08/TestAcad08.csproj +++ b/tests/TestAcad08/TestAcad08.csproj @@ -15,7 +15,7 @@ - + diff --git a/tests/TestAcad09plus/TestAcad09plus.csproj b/tests/TestAcad09plus/TestAcad09plus.csproj index e0afab6..1e25e45 100644 --- a/tests/TestAcad09plus/TestAcad09plus.csproj +++ b/tests/TestAcad09plus/TestAcad09plus.csproj @@ -15,7 +15,7 @@ - + -- Gitee From 340ae4f1007e17a7f6328e23bb1ee788d02a4d03 Mon Sep 17 00:00:00 2001 From: liuqihong <540762622@qq.com> Date: Sun, 6 Nov 2022 20:58:44 +0800 Subject: [PATCH 617/675] =?UTF-8?q?=E9=AB=98=E7=89=88=E6=9C=AC=E7=A8=8B?= =?UTF-8?q?=E5=BA=8F=E9=9B=86=E5=8A=A0=E8=BD=BD=E9=9C=80=E8=A6=81=E4=B8=80?= =?UTF-8?q?=E4=B8=AA=E8=B5=84=E6=BA=90dll=E7=9A=84=E9=94=99=E8=AF=AF(https?= =?UTF-8?q?://stackoverflow.com/questions/4368201)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../DefaultAssemblyResolve.cs | 27 +++++-------------- 1 file changed, 7 insertions(+), 20 deletions(-) diff --git a/src/IFoxCAD.LoadForm/DefaultAssemblyResolve.cs b/src/IFoxCAD.LoadForm/DefaultAssemblyResolve.cs index a1c9b51..1e79e27 100644 --- a/src/IFoxCAD.LoadForm/DefaultAssemblyResolve.cs +++ b/src/IFoxCAD.LoadForm/DefaultAssemblyResolve.cs @@ -47,28 +47,15 @@ public class AssemblyHelper if (GetAssemblyName(cadAss[i].GetName().FullName) == ag) result = cadAss[i]; - // 惊惊:我的 cad21+vs22容易触发这个WPF资源的问题 - // 偶发性,怀疑是载入顺序的导致 - // 当cad界面完成之后找的话,大概率能够通过下面语句跳过 - // 如果初始化遇到了, - // 0x01 或者是某个多线程忘记lock变量,然后导致整个逻辑错误 - // 0x02 建议重新安装 if (result == null) { - //args.Name = "Microsoft.VisualStudio.DesignTools.WpfTap.resources, Version=17.0.0.0, Culture=zh-CN, PublicKeyToken=b03f5f7f11d50a3a" - if (e.Name.Contains("Microsoft.VisualStudio.DesignTools.WpfTap.resources")) - { - Debug.WriteLine($"{nameof(LoadEx)}::程序集找不到::Microsoft.VisualStudio.DesignTools.WpfTap.resources"); - //var str = @"C:\Program Files\Microsoft Visual Studio\2022\Enterprise\Common7\IDE\CommonExtensions\Microsoft\XamlDiagnostics\Framework\x64\Microsoft.VisualStudio.DesignTools.WpfTap.dll"; - //result = Assembly.Load(str);// 这里又循环到开头了 - //result = Assembly.ReflectionOnlyLoad(str);// 这里又循环到开头了,提示它是保护的 - - // 这里不是抓资源的程序集,而是抓了它本体,然后发现偶尔能通过,很奇怪. - for (int i = 0; i < cadAss.Length; i++) - if (cadAss[i].GetName().Name.Contains("WpfTap")) - result = cadAss[i]; - } - Thread.Sleep(5000); + // 惊惊: cad21+vs22 容易触发这个资源的问题 + // https://stackoverflow.com/questions/4368201/ + string[] fields = e.Name.Split(','); + string name = fields[0]; + string culture = fields[2]; + if (name.EndsWith(".resources") && !culture.EndsWith("neutral")) + return null; } if (result == null) -- Gitee From c182c5b283384304bfe5df98779433ec3d070fa8 Mon Sep 17 00:00:00 2001 From: liuqihong <540762622@qq.com> Date: Fri, 11 Nov 2022 02:15:20 +0800 Subject: [PATCH 618/675] =?UTF-8?q?=E4=BF=AE=E6=94=B9=E5=91=BD=E4=BB=A4?= =?UTF-8?q?=E4=B8=BAnameof=E5=BD=A2=E5=BC=8F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/IFoxCAD.Basal/General/DebugHelper.cs | 15 ++++---- src/IFoxCAD.Basal/Sortedset/SR.cs | 7 +--- .../Algorithms/QuadTree/QuadTree.cs | 34 +++++++++---------- src/IFoxCAD.Cad.Shared/Copyclip/WindowsAPI.cs | 2 +- .../ExtensionMethod/Curve3dEx.cs | 11 +++--- .../AttachmentPointHelper.cs" | 4 +-- src/IFoxCAD.Cad.Shared/Runtime/DBTrans.cs | 5 ++- .../Runtime/PE/AcadPeInfo.cs | 4 +-- src/IFoxCAD.Cad.Shared/Runtime/RedrawEx.cs | 4 +-- src/IFoxCAD.Cad.Shared/Runtime/Utils.cs | 26 +++++++------- .../DefaultAssemblyResolve.cs | 10 ++++-- src/IFoxCAD.LoadForm/TestNetLoad.cs | 4 +-- .../IMEControl.cs" | 2 ++ tests/TestShared/Copyclip.cs | 7 ++-- tests/TestShared/TestLisp.cs | 2 +- tests/TestShared/TestXrefEx.cs | 6 ++-- 16 files changed, 75 insertions(+), 68 deletions(-) diff --git a/src/IFoxCAD.Basal/General/DebugHelper.cs b/src/IFoxCAD.Basal/General/DebugHelper.cs index 0423702..58ae2fc 100644 --- a/src/IFoxCAD.Basal/General/DebugHelper.cs +++ b/src/IFoxCAD.Basal/General/DebugHelper.cs @@ -1,4 +1,5 @@ -using System.Threading; +using System.Diagnostics; +using System.Threading; namespace IFoxCAD.Basal; @@ -9,6 +10,7 @@ public static class Debugx ///
    /// 打印信息 /// 打印时间 + [DebuggerHidden] public static void Printl(object message, bool time = true) { var flag = Environment.GetEnvironmentVariable("debugx", EnvironmentVariableTarget.User); @@ -16,17 +18,16 @@ public static void Printl(object message, bool time = true) return; if (time) - message = $"{DateTime.Now.ToLongDateString() + DateTime.Now.TimeOfDay}\n" + - $"\t\tThreadId:{Thread.CurrentThread.ManagedThreadId}\n" + + //message = $"{DateTime.Now.ToLongDateString() + DateTime.Now.TimeOfDay}\n" + + message = $"{DateTime.Now.TimeOfDay} ThreadId:{Thread.CurrentThread.ManagedThreadId}\n" + $"\t\t{message}"; -#if DEBUG + //System.Diagnostics.Debug.Indent(); +#if DEBUG System.Diagnostics.Debug.WriteLine(message); - //System.Diagnostics.Debug.Unindent(); #else - //System.Diagnostics.Debug.Indent(); System.Diagnostics.Trace.WriteLine(message); - //System.Diagnostics.Debug.Unindent(); #endif + //System.Diagnostics.Debug.Unindent(); } } \ No newline at end of file diff --git a/src/IFoxCAD.Basal/Sortedset/SR.cs b/src/IFoxCAD.Basal/Sortedset/SR.cs index 59e10a7..88b2bcf 100644 --- a/src/IFoxCAD.Basal/Sortedset/SR.cs +++ b/src/IFoxCAD.Basal/Sortedset/SR.cs @@ -120,13 +120,8 @@ internal static string GetString(CultureInfo culture, string name, params object string res = sys.resources.GetString(name, culture); System.Diagnostics.Debug.Assert(res != null, string.Format(CultureInfo.CurrentCulture, "String resource {0} not found.", new object[] { name })); if (args != null && args.Length > 0) - { return string.Format(CultureInfo.CurrentCulture, res, args); - } - else - { - return res; - } + return res; } internal static string GetString(string name) diff --git a/src/IFoxCAD.Cad.Shared/Algorithms/QuadTree/QuadTree.cs b/src/IFoxCAD.Cad.Shared/Algorithms/QuadTree/QuadTree.cs index 7032b10..267cc4a 100644 --- a/src/IFoxCAD.Cad.Shared/Algorithms/QuadTree/QuadTree.cs +++ b/src/IFoxCAD.Cad.Shared/Algorithms/QuadTree/QuadTree.cs @@ -180,26 +180,26 @@ public List Query(Rect rect, QuadTreeSelectMode selectMode = QuadTreeSe { case QuadTreeSelectMode.IntersectsWith: case QuadTreeSelectMode.Contains: - /* 由于红黑树的方法 _points.GetViewBetween() - * 过滤只能过滤X区间,Y区间还是要过滤, - * 那么我就只能用这样的方法加速了 - * - * 而更好的方式是不用红黑树,去加入一个点云数据来进行,可谓是编程无极限.... - */ - while (ptge.MoveNext()) + /* 由于红黑树的方法 _points.GetViewBetween() + * 过滤只能过滤X区间,Y区间还是要过滤, + * 那么我就只能用这样的方法加速了 + * + * 而更好的方式是不用红黑树,去加入一个点云数据来进行,可谓是编程无极限.... + */ + while (ptge.MoveNext()) + { + var ptEnt = ptge.Current; + if (rect._X <= ptEnt._X && ptEnt._X <= rect._Right) { - var ptEnt = ptge.Current; - if (rect._X <= ptEnt._X && ptEnt._X <= rect._Right) - { - if (rect._Y <= ptEnt._Y && ptEnt._Y <= rect.Top) - results.Add(ptEnt); - } - else if (ptEnt._X > rect._Right) - break;// 超过后面范围就break,因为红黑树已经排序 + if (rect._Y <= ptEnt._Y && ptEnt._Y <= rect.Top) + results.Add(ptEnt); } - break; + else if (ptEnt._X > rect._Right) + break;// 超过后面范围就break,因为红黑树已经排序 + } + break; default: - throw new("四叉树:" + nameof(selectMode)); + throw new("四叉树:" + nameof(selectMode)); } return results; } diff --git a/src/IFoxCAD.Cad.Shared/Copyclip/WindowsAPI.cs b/src/IFoxCAD.Cad.Shared/Copyclip/WindowsAPI.cs index 70b2f39..a87f6ae 100644 --- a/src/IFoxCAD.Cad.Shared/Copyclip/WindowsAPI.cs +++ b/src/IFoxCAD.Cad.Shared/Copyclip/WindowsAPI.cs @@ -424,7 +424,7 @@ public static GuiThreadInfo Create(uint windowThreadProcessId) [DllImport("user32.dll", SetLastError = true)] public static extern IntPtr GetFocus(); - [DllImport("user32.dll ")] + [DllImport("user32.dll")] public static extern IntPtr SendMessage(IntPtr hwnd, int msg, IntPtr wParam, IntPtr lParam); [DllImport("user32.dll", SetLastError = true)] diff --git a/src/IFoxCAD.Cad.Shared/ExtensionMethod/Curve3dEx.cs b/src/IFoxCAD.Cad.Shared/ExtensionMethod/Curve3dEx.cs index 2ed17cf..f4a2ce4 100644 --- a/src/IFoxCAD.Cad.Shared/ExtensionMethod/Curve3dEx.cs +++ b/src/IFoxCAD.Cad.Shared/ExtensionMethod/Curve3dEx.cs @@ -1,4 +1,6 @@ -namespace IFoxCAD.Cad; +using System.Runtime.CompilerServices; + +namespace IFoxCAD.Cad; /// /// 三维解析类曲线转换为三维实体曲线扩展类 @@ -12,6 +14,7 @@ public static class Curve3dEx /// 第一个数 /// 第二个数 /// 两个数的差值的绝对值小于容差返回 ,反之返回 + [MethodImpl] public static bool IsEqualPoint(this Tolerance tol, double d1, double d2) { return Math.Abs(d1 - d2) < tol.EqualPoint; @@ -24,14 +27,14 @@ public static bool IsEqualPoint(this Tolerance tol, double d1, double d2) /// /// 三维解析类曲线 /// 曲线参数的列表 - public static List GetParamsAtIntersectionPoints(this Curve3d c3d) + public static List GetParamsAtIntersectionPoints(this Curve3d c3d, bool sort = true) { CurveCurveIntersector3d cci = new(c3d, c3d, Vector3d.ZAxis); List pars = new(); for (int i = 0; i < cci.NumberOfIntersectionPoints; i++) pars.AddRange(cci.GetIntersectionParameters(i)); - - pars.Sort(); + if (sort) + pars.Sort(); return pars; } diff --git "a/src/IFoxCAD.Cad.Shared/ExtensionMethod/\346\226\260\345\273\272\346\226\207\345\255\227/AttachmentPointHelper.cs" "b/src/IFoxCAD.Cad.Shared/ExtensionMethod/\346\226\260\345\273\272\346\226\207\345\255\227/AttachmentPointHelper.cs" index e630a51..975e8a7 100644 --- "a/src/IFoxCAD.Cad.Shared/ExtensionMethod/\346\226\260\345\273\272\346\226\207\345\255\227/AttachmentPointHelper.cs" +++ "b/src/IFoxCAD.Cad.Shared/ExtensionMethod/\346\226\260\345\273\272\346\226\207\345\255\227/AttachmentPointHelper.cs" @@ -91,5 +91,5 @@ public static Dictionary GetEnumDic(Type enumType) } } return dic; -} -#endif +} +#endif \ No newline at end of file diff --git a/src/IFoxCAD.Cad.Shared/Runtime/DBTrans.cs b/src/IFoxCAD.Cad.Shared/Runtime/DBTrans.cs index 16abef4..ba89402 100644 --- a/src/IFoxCAD.Cad.Shared/Runtime/DBTrans.cs +++ b/src/IFoxCAD.Cad.Shared/Runtime/DBTrans.cs @@ -670,10 +670,9 @@ protected virtual void Dispose(bool disposing) // 将cad事务进行销毁 if (!Transaction.IsDisposed) Transaction.Dispose(); - - // 将文档锁销毁 - _documentLock?.Dispose(); } + // 将文档锁销毁 + _documentLock?.Dispose(); // 将当前事务栈弹栈 _dBTrans.Pop(); diff --git a/src/IFoxCAD.Cad.Shared/Runtime/PE/AcadPeInfo.cs b/src/IFoxCAD.Cad.Shared/Runtime/PE/AcadPeInfo.cs index 051302d..3957a68 100644 --- a/src/IFoxCAD.Cad.Shared/Runtime/PE/AcadPeInfo.cs +++ b/src/IFoxCAD.Cad.Shared/Runtime/PE/AcadPeInfo.cs @@ -74,10 +74,10 @@ public static PeInfo? PeForAcdbDll } } + List? _Methods; // 这个不是静态的 /// /// 同名函数指针们 /// - static List? _Methods; public List? Methods { get @@ -148,7 +148,7 @@ public AcadPeInfo(string methodName, AcadPeEnum acadPeEnum) GetMethodErrorNum GetPeMethod(PeInfo? peInfo) { if (peInfo == null) - throw new ArgumentNullException(nameof(peInfo)); + return GetMethodErrorNum.NoFuncName;// cad08需要检查 AccoreDll 的时候跳过 var identifyStr = _findFuncName + ";" + peInfo.FullName; if (_Dict.ContainsKey(identifyStr))// 如果已经找过,直接返回 diff --git a/src/IFoxCAD.Cad.Shared/Runtime/RedrawEx.cs b/src/IFoxCAD.Cad.Shared/Runtime/RedrawEx.cs index 78822ee..3799eed 100644 --- a/src/IFoxCAD.Cad.Shared/Runtime/RedrawEx.cs +++ b/src/IFoxCAD.Cad.Shared/Runtime/RedrawEx.cs @@ -1,7 +1,7 @@ namespace IFoxCAD.Cad; [Flags] -public enum BrightEntity +public enum BrightEntity : int { /// /// 块更新 @@ -38,7 +38,7 @@ public enum BrightEntity } [Flags] -public enum BrightEditor +public enum BrightEditor : int { /// /// 刷新屏幕,图元不生成(例如块还是旧的显示) diff --git a/src/IFoxCAD.Cad.Shared/Runtime/Utils.cs b/src/IFoxCAD.Cad.Shared/Runtime/Utils.cs index 6c668da..28c47c2 100644 --- a/src/IFoxCAD.Cad.Shared/Runtime/Utils.cs +++ b/src/IFoxCAD.Cad.Shared/Runtime/Utils.cs @@ -48,19 +48,19 @@ static int GetAcDbObjectId(IntPtr db, Handle handle, out ObjectId id, bool creat switch (Acap.Version.Major) { case 17: - { - if (IntPtr.Size == 4) - return getAcDbObjectId17x32(db, out id, createIfNotFound, ref handle, reserved); - else - return getAcDbObjectId17x64(db, out id, createIfNotFound, ref handle, reserved); - } + { + if (IntPtr.Size == 4) + return getAcDbObjectId17x32(db, out id, createIfNotFound, ref handle, reserved); + else + return getAcDbObjectId17x64(db, out id, createIfNotFound, ref handle, reserved); + } case 18: - { - if (IntPtr.Size == 4) - return getAcDbObjectId18x32(db, out id, createIfNotFound, ref handle, reserved); - else - return getAcDbObjectId18x64(db, out id, createIfNotFound, ref handle, reserved); - } + { + if (IntPtr.Size == 4) + return getAcDbObjectId18x32(db, out id, createIfNotFound, ref handle, reserved); + else + return getAcDbObjectId18x64(db, out id, createIfNotFound, ref handle, reserved); + } } return -1; } @@ -93,6 +93,6 @@ public static ObjectId TryGetObjectId(Database db, Handle handle) // var a = Enum.Parse(typeof(DwgVersion), "AC1800"); // Enum.TryParse() // return vernum + 986; - + // } } \ No newline at end of file diff --git a/src/IFoxCAD.LoadForm/DefaultAssemblyResolve.cs b/src/IFoxCAD.LoadForm/DefaultAssemblyResolve.cs index 1e79e27..0662dfd 100644 --- a/src/IFoxCAD.LoadForm/DefaultAssemblyResolve.cs +++ b/src/IFoxCAD.LoadForm/DefaultAssemblyResolve.cs @@ -1,7 +1,6 @@ namespace IFoxCAD.LoadEx; using System.Diagnostics; -using System.Threading; public class AssemblyHelper { @@ -49,7 +48,7 @@ public class AssemblyHelper if (result == null) { - // 惊惊: cad21+vs22 容易触发这个资源的问题 + // 惊惊: acad21+vs22 容易触发这个资源的问题 // https://stackoverflow.com/questions/4368201/ string[] fields = e.Name.Split(','); string name = fields[0]; @@ -58,6 +57,13 @@ public class AssemblyHelper return null; } + if (result == null) + { + // acad08debug的时候查看一些变量时候,会弹出找不到它,并且闪退 + if (ag == "Microsoft.CSharp") + return null; + } + if (result == null) { var sb = new StringBuilder(); diff --git a/src/IFoxCAD.LoadForm/TestNetLoad.cs b/src/IFoxCAD.LoadForm/TestNetLoad.cs index 1c15706..4451f38 100644 --- a/src/IFoxCAD.LoadForm/TestNetLoad.cs +++ b/src/IFoxCAD.LoadForm/TestNetLoad.cs @@ -4,8 +4,8 @@ namespace Test; public class NetLoad { static IFoxCAD.LoadEx.LoaderForm? _form; - [CommandMethod("loadx")] - public static void Test_NetLoadx() + [CommandMethod(nameof(Loadx))] + public static void Loadx() { if (_form == null || _form.IsDisposed) _form = new(); diff --git "a/tests/TestAcad09plus/\346\213\246\346\210\252\350\276\223\345\205\245\346\263\225/IMEControl.cs" "b/tests/TestAcad09plus/\346\213\246\346\210\252\350\276\223\345\205\245\346\263\225/IMEControl.cs" index e9b15f7..208e17e 100644 --- "a/tests/TestAcad09plus/\346\213\246\346\210\252\350\276\223\345\205\245\346\263\225/IMEControl.cs" +++ "b/tests/TestAcad09plus/\346\213\246\346\210\252\350\276\223\345\205\245\346\263\225/IMEControl.cs" @@ -311,6 +311,7 @@ internal static void SetIMEHook() if (_nextHookProc != IntPtr.Zero) return; + #region 读取配置 ExceptCmds_AutoEn2Cn.Clear(); { foreach (var item in DefaultCmds_AutoEn2Cn) @@ -328,6 +329,7 @@ internal static void SetIMEHook() foreach (var item in ss) ExceptCmds_AutoCn2En.Add(item); } + #endregion if (Settings.IMEHookStyle == IMEHookStyle.Process) { diff --git a/tests/TestShared/Copyclip.cs b/tests/TestShared/Copyclip.cs index 47a0426..5fdc45e 100644 --- a/tests/TestShared/Copyclip.cs +++ b/tests/TestShared/Copyclip.cs @@ -33,8 +33,7 @@ public class Copyclip [IFoxInitialize] // 惊惊: 遇到了高版本无法导出WMF,放弃此功能,等待有缘人 public void Init() { - Acap.DocumentManager.DocumentLockModeChanged - += DocumentManager_DocumentLockModeChanged; + Acap.DocumentManager.DocumentLockModeChanged += Dm_VetoCommand; Env.Printl($"※剪贴板控制※\n{nameof(Copyclip_Switch)} - 切换开关\n"); } @@ -50,8 +49,10 @@ public void Copyclip_Switch() /// /// /// - void DocumentManager_DocumentLockModeChanged(object sender, DocumentLockModeChangedEventArgs e) + void Dm_VetoCommand(object sender, DocumentLockModeChangedEventArgs e) { + if (string.IsNullOrEmpty(e.GlobalCommandName) || e.GlobalCommandName == "#") + return; if (!_IsRunIFoxCopyClip) return; diff --git a/tests/TestShared/TestLisp.cs b/tests/TestShared/TestLisp.cs index 91f9079..8f79a39 100644 --- a/tests/TestShared/TestLisp.cs +++ b/tests/TestShared/TestLisp.cs @@ -11,7 +11,7 @@ public static object LispTest_RunLisp(ResultBuffer rb) } // 模态命令,只有当CAD发出命令提示或当前没有其他的命令或程序活动的时候才可以被触发 - [CommandMethod("CmdTest_RunLisp1", CommandFlags.Modal)] + [CommandMethod("CmdTest_RunLisp1")] // 透明命令,可以在一个命令提示输入的时候触发例如正交切换,zoom等 [CommandMethod("CmdTest_RunLisp2", CommandFlags.Transparent)] // 选择图元之后执行命令将可以从 获取图元 diff --git a/tests/TestShared/TestXrefEx.cs b/tests/TestShared/TestXrefEx.cs index bb9d458..8142200 100644 --- a/tests/TestShared/TestXrefEx.cs +++ b/tests/TestShared/TestXrefEx.cs @@ -3,7 +3,7 @@ public class TestCmd_BindXrefs { //后台绑定 - [CommandMethod("Test_Bind1")] + [CommandMethod(nameof(Test_Bind1))] public static void Test_Bind1() { string fileName = @"D:\Test.dwg"; @@ -14,11 +14,11 @@ public static void Test_Bind1() } //前台绑定 - [CommandMethod("Test_Bind2")] + [CommandMethod(nameof(Test_Bind2))] public static void Test_Bind2() { using var tr = new DBTrans(); tr.XrefFactory(XrefModes.Bind); tr.SaveDwgFile(); } -} +} \ No newline at end of file -- Gitee From 48c1937c93bfbc7ff9ec05ce91a608d043d79c77 Mon Sep 17 00:00:00 2001 From: liuqihong <540762622@qq.com> Date: Sat, 12 Nov 2022 05:02:24 +0800 Subject: [PATCH 619/675] =?UTF-8?q?=E7=A8=8B=E5=BA=8F=E6=8E=A7=E5=88=B6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/IFoxCAD.Basal/General/LoopState.cs | 34 ++++++++++++++++++++++++++ 1 file changed, 34 insertions(+) diff --git a/src/IFoxCAD.Basal/General/LoopState.cs b/src/IFoxCAD.Basal/General/LoopState.cs index d25fe2a..76ff63f 100644 --- a/src/IFoxCAD.Basal/General/LoopState.cs +++ b/src/IFoxCAD.Basal/General/LoopState.cs @@ -30,4 +30,38 @@ public void Exceptional() public void Cancel() => _flag = PLS_CANCELED; public void Reset() => _flag = PLS_NONE; } +#line default + +/// +/// 控制程序流程 +/// +public class ProState +{ + const int PLS_NONE = 0; // 初始化(构造就立马运行,将导致构造函数中也被检测,这是浪费性能及挖坑给自己的) + const int PLS_RUN = 1; // 运行 + const int PLS_BROKEN = 2; + const int PLS_STOPPED = 4; + const int PLS_CANCELED = 8; + const int PLS_EXCEPTIONAL = 16; // 异常 用于附加状态 + + private volatile int _flag = PLS_NONE; + + public bool IsNone => _flag == PLS_NONE; + public bool IsRun => (_flag & PLS_RUN) == PLS_RUN; + public bool IsBreak => (_flag & PLS_BROKEN) == PLS_BROKEN; + public bool IsStop => (_flag & PLS_STOPPED) == PLS_STOPPED; + public bool IsCancel => (_flag & PLS_CANCELED) == PLS_CANCELED; + public bool IsExceptional => (_flag & PLS_EXCEPTIONAL) == PLS_EXCEPTIONAL; + + public void Exceptional() + { + if ((_flag & PLS_EXCEPTIONAL) != PLS_EXCEPTIONAL) + _flag |= PLS_EXCEPTIONAL; + } + public void Break() => _flag = PLS_BROKEN; + public void Stop() => _flag = PLS_STOPPED; + public void Cancel() => _flag = PLS_CANCELED; + public void Start() => _flag = PLS_RUN; + public void None() => _flag = PLS_NONE; +} #line default \ No newline at end of file -- Gitee From 2df0ea835739f23cee78585dcf543d42f32f767a Mon Sep 17 00:00:00 2001 From: liuqihong <540762622@qq.com> Date: Sat, 12 Nov 2022 05:03:25 +0800 Subject: [PATCH 620/675] =?UTF-8?q?=E4=BC=98=E5=8C=96=E4=BB=A3=E7=A0=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 优化代码 --- .../Algorithms/QuadTree/Rect.cs | 4 +- src/IFoxCAD.Cad.Shared/Copyclip/EmfTool.cs | 7 +-- src/IFoxCAD.Cad.Shared/Copyclip/WindowsAPI.cs | 13 +++-- .../ExtensionMethod/Curve3dEx.cs | 42 ++++++-------- .../ExtensionMethod/SymbolTableRecordEx.cs | 2 - .../HatchConverter.cs" | 57 ++++++++++++------- tests/TestShared/Copyclip.cs | 4 +- tests/TestShared/TestMarshal.cs | 2 +- 8 files changed, 72 insertions(+), 59 deletions(-) diff --git a/src/IFoxCAD.Cad.Shared/Algorithms/QuadTree/Rect.cs b/src/IFoxCAD.Cad.Shared/Algorithms/QuadTree/Rect.cs index 4a44663..9e10b97 100644 --- a/src/IFoxCAD.Cad.Shared/Algorithms/QuadTree/Rect.cs +++ b/src/IFoxCAD.Cad.Shared/Algorithms/QuadTree/Rect.cs @@ -1,4 +1,5 @@ using System.Diagnostics; +using System.Runtime.CompilerServices; namespace IFoxCAD.Cad; @@ -256,7 +257,8 @@ public bool Contains(Rect rect) /// 一个点在内部就是碰撞 /// /// - /// + /// true内部 + [MethodImpl] public bool IntersectsWith(Rect rect) { return rect._X <= _Right && _X <= rect._Right && diff --git a/src/IFoxCAD.Cad.Shared/Copyclip/EmfTool.cs b/src/IFoxCAD.Cad.Shared/Copyclip/EmfTool.cs index 2b93449..773cf6d 100644 --- a/src/IFoxCAD.Cad.Shared/Copyclip/EmfTool.cs +++ b/src/IFoxCAD.Cad.Shared/Copyclip/EmfTool.cs @@ -98,12 +98,7 @@ public static IntPtr Wmf2Emf(string wmfFile) file.Read(fileByte, 0, fileByte.Length); file.Close(); - var ss = BytesToStruct(fileByte); - if (ss == null) - throw new IOException("失败:类型转换,路径:" + file); - - var sWMF = ss.Value; - + var sWMF = BytesToStruct(fileByte); // 转为emf的地址 IntPtr hEMF = IntPtr.Zero; diff --git a/src/IFoxCAD.Cad.Shared/Copyclip/WindowsAPI.cs b/src/IFoxCAD.Cad.Shared/Copyclip/WindowsAPI.cs index a87f6ae..069e074 100644 --- a/src/IFoxCAD.Cad.Shared/Copyclip/WindowsAPI.cs +++ b/src/IFoxCAD.Cad.Shared/Copyclip/WindowsAPI.cs @@ -3,6 +3,7 @@ namespace IFoxCAD.Cad; using System; using System.Diagnostics; +using System.Runtime.CompilerServices; using System.Threading; using System.Windows.Forms; @@ -138,16 +139,19 @@ public static bool GlobalLockTask(IntPtr data, Action task) ///
    /// byte数组 /// 返回的结构体 - public static T? BytesToStruct(byte[] bytes) where T : unmanaged + [MethodImpl] + public static T? BytesToStruct(byte[] bytes) { - T? result = null; + T? result = default; unsafe { // 安全指针方法 // var pB = Marshal.UnsafeAddrOfPinnedArrayElement(bytes, 0); // 不安全指针方法 fixed (byte* pB = &bytes[0]) - result = (T)Marshal.PtrToStructure(new IntPtr(pB), typeof(T)); + { + result = (T?)Marshal.PtrToStructure(new IntPtr(pB), typeof(T)); + } } return result; } @@ -157,7 +161,8 @@ public static bool GlobalLockTask(IntPtr data, Action task) /// unmanaged ///
    /// 要转换的结构体 - public static byte[] StructToBytes(T structObj) where T : unmanaged + [MethodImpl] + public static byte[] StructToBytes(T structObj) where T : unmanaged/*非托管的T从来不为空*/ { // 得到结构体的大小 var typeSize = Marshal.SizeOf(structObj); diff --git a/src/IFoxCAD.Cad.Shared/ExtensionMethod/Curve3dEx.cs b/src/IFoxCAD.Cad.Shared/ExtensionMethod/Curve3dEx.cs index f4a2ce4..aef668c 100644 --- a/src/IFoxCAD.Cad.Shared/ExtensionMethod/Curve3dEx.cs +++ b/src/IFoxCAD.Cad.Shared/ExtensionMethod/Curve3dEx.cs @@ -149,18 +149,16 @@ public static bool IsCircular(this Curve3d curve) /// 三维复合曲线 /// 曲线参数列表 /// 三维复合曲线列表 - public static List GetSplitCurves(this CompositeCurve3d c3d, List pars) + public static List? GetSplitCurves(this CompositeCurve3d c3d, List pars) { // 曲线参数剔除重复的 pars.Sort(); for (int i = pars.Count - 1; i > 0; i--) - { if (Tolerance.Global.IsEqualPoint(pars[i], pars[i - 1])) pars.RemoveAt(i); - } if (pars.Count == 0) - return new List(); + return null; // 这个是曲线参数类 var inter = c3d.GetInterval(); @@ -169,31 +167,27 @@ public static List GetSplitCurves(this CompositeCurve3d c3d, L if (c3ds.Length == 1 && c3ds[0].IsClosed()) { // 闭合曲线不允许打断于一点 - if (pars.Count > 1) + if (pars.Count < 2) + return null; + + // 如果包含起点 + if (Tolerance.Global.IsEqualPoint(pars[0], inter.LowerBound)) { - // 如果包含起点 - if (Tolerance.Global.IsEqualPoint(pars[0], inter.LowerBound)) - { - pars[0] = inter.LowerBound; - // 又包含终点,去除终点 - if (Tolerance.Global.IsEqualPoint(pars[^1], inter.UpperBound)) - { - pars.RemoveAt(pars.Count - 1); - if (pars.Count == 1) - return new List(); - } - } - else if (Tolerance.Global.IsEqualPoint(pars[^1], inter.UpperBound)) + pars[0] = inter.LowerBound; + // 又包含终点,去除终点 + if (Tolerance.Global.IsEqualPoint(pars[^1], inter.UpperBound)) { - pars[^1] = inter.UpperBound; + pars.RemoveAt(pars.Count - 1); + if (pars.Count == 1) + return null; } - // 加入第一点以支持反向打断 - pars.Add(pars[0]); } - else + else if (Tolerance.Global.IsEqualPoint(pars[^1], inter.UpperBound)) { - return new List(); + pars[^1] = inter.UpperBound; } + // 加入第一点以支持反向打断 + pars.Add(pars[0]); } else { @@ -244,7 +238,7 @@ public static List GetSplitCurves(this CompositeCurve3d c3d, L if (c3d.IsClosed() && c3ds.Length > 1) { - var cus1 = curves[curves.Count - 1].GetCurves(); + var cus1 = curves[^1].GetCurves(); var cus2 = curves[0].GetCurves(); var cs = cus1.Combine2(cus2); curves[^1] = new CompositeCurve3d(cs); diff --git a/src/IFoxCAD.Cad.Shared/ExtensionMethod/SymbolTableRecordEx.cs b/src/IFoxCAD.Cad.Shared/ExtensionMethod/SymbolTableRecordEx.cs index d895a40..ecd1e31 100644 --- a/src/IFoxCAD.Cad.Shared/ExtensionMethod/SymbolTableRecordEx.cs +++ b/src/IFoxCAD.Cad.Shared/ExtensionMethod/SymbolTableRecordEx.cs @@ -309,8 +309,6 @@ public static ObjectId AddArc(this BlockTableRecord btr, var arc = EntityEx.CreateArc(startPoint, pointOnArc, endPoint); return btr.AddEnt(arc, action, trans); } - - // TODO: 所有涉及默认无参构造的实体类型,都需要调用SetDatabaseDefaults(); #endregion #region 获取实体/实体id diff --git "a/src/IFoxCAD.Cad.Shared/ExtensionMethod/\346\226\260\345\273\272\345\241\253\345\205\205/HatchConverter.cs" "b/src/IFoxCAD.Cad.Shared/ExtensionMethod/\346\226\260\345\273\272\345\241\253\345\205\205/HatchConverter.cs" index e38b8e0..a04ba02 100644 --- "a/src/IFoxCAD.Cad.Shared/ExtensionMethod/\346\226\260\345\273\272\345\241\253\345\205\205/HatchConverter.cs" +++ "b/src/IFoxCAD.Cad.Shared/ExtensionMethod/\346\226\260\345\273\272\345\241\253\345\205\205/HatchConverter.cs" @@ -67,7 +67,7 @@ public ObjectId OldHatchId readonly List _hcDatas; /// - /// 生成的填充边界id + /// 填充边界id(生成的/已存在反应器的直接提取) /// public List BoundaryIds; #endregion @@ -90,10 +90,33 @@ public HatchConverter(Hatch hatch) : this() { _oldHatch = hatch; - // 不能在提取信息的时候进行新建cad图元, - // 否则cad将会提示遗忘释放 - hatch.ForEach(loop => { - var hcData = new HatchConverterData(); + if (hatch.Associative) + { + // 填充边界反应器 + var assIds = hatch.GetAssociatedObjectIds(); + if (assIds != null) + { + foreach (ObjectId id in assIds) + if (id.IsOk()) + BoundaryIds.Add(id); + + if (BoundaryIds.Count == 0) + { + throw new ArgumentException("关联的填充边界被删除后没有清理反应器,请调用:" + + "\n hatch.RemoveAssociatedObjectIds()" + + "\n hatch.Associative = false"); + } + } + } + } + + /// + /// 提取边界信息 + /// + public void GetBoundarysData() + { + _oldHatch?.ForEach(loop => { + HatchConverterData hcData = new(); bool isCurve2d = true; if (loop.IsPolyline) @@ -239,7 +262,7 @@ static void HatchLoopIsCurve2d(HatchLoop loop, HatchConverterData hcData) /// 创建边界图元 ///
    /// 返回图元 - public void CreateBoundaryEntitys(List outEnts) + public void CreateBoundary(List outEnts) { for (int i = 0; i < _hcDatas.Count; i++) { @@ -294,15 +317,11 @@ public ObjectId CreateBoundarysAndHatchToMsPs(BlockTableRecord btrOfAddEntitySpa bool createHatchFlag = true, Transaction? trans = null) { - // 重设边界之前肯定是有边界才可以 - if (BoundaryIds.Count == 0) - { - List boundaryEntitys = new(); - CreateBoundaryEntitys(boundaryEntitys); - boundaryEntitys.ForEach(ent => { - BoundaryIds.Add(btrOfAddEntitySpace.AddEntity(ent)); - }); - } + List boEnts = new(); + CreateBoundary(boEnts); + boEnts.ForEach(ent => { + BoundaryIds.Add(btrOfAddEntitySpace.AddEntity(ent)); + }); if (!createHatchFlag) return ObjectId.Null; @@ -333,16 +352,16 @@ public ObjectId CreateBoundarysAndHatchToMsPs(BlockTableRecord btrOfAddEntitySpa return newHatchId; } - /// /// 重设边界 /// /// /// 边界关联 - void ResetBoundary(Hatch hatch, - bool boundaryAssociative = true) + void ResetBoundary(Hatch hatch, bool boundaryAssociative = true) { - // 删除原有边界 + if (BoundaryIds.Count == 0) + return; + while (hatch.NumberOfLoops != 0) hatch.RemoveLoopAt(0); diff --git a/tests/TestShared/Copyclip.cs b/tests/TestShared/Copyclip.cs index 5fdc45e..cc4558c 100644 --- a/tests/TestShared/Copyclip.cs +++ b/tests/TestShared/Copyclip.cs @@ -79,7 +79,7 @@ void Dm_VetoCommand(object sender, DocumentLockModeChangedEventArgs e) #if PASTECLIP if (up == "PASTECLIP")// 粘贴 { - // TODO === 完成之后此处将会移除 + // === 完成之后此处将会移除 // 粘贴文本的生成单行文字/多行文字,这些还需要自己去实现 var getClip = ClipTool.GetClipboard(ClipboardEnv.CadVer, out TagClipboardInfo tag); if (!getClip) @@ -91,7 +91,7 @@ void Dm_VetoCommand(object sender, DocumentLockModeChangedEventArgs e) } else if (up == "PASTEBLOCK") //ctrl+shift+v 粘贴为块 { - // TODO === 完成之后此处将会移除 + // === 完成之后此处将会移除 var getClip = ClipTool.GetClipboard(ClipboardEnv.CadVer, out TagClipboardInfo tag); if (!getClip) return; diff --git a/tests/TestShared/TestMarshal.cs b/tests/TestShared/TestMarshal.cs index dff1303..2b8514e 100644 --- a/tests/TestShared/TestMarshal.cs +++ b/tests/TestShared/TestMarshal.cs @@ -11,7 +11,7 @@ public void TestToBytes() var ptA = new Point3d(123, 456, 789); var bytes = StructToBytes(ptA); var ptB = BytesToStruct(bytes); - Env.Printl(ptB!.Value); + Env.Printl(ptB); } [CommandMethod(nameof(Test_ChangeLinePoint))] -- Gitee From 27d2a78f6a27fe966b84fcdbb129c910d3a71468 Mon Sep 17 00:00:00 2001 From: liuqihong <540762622@qq.com> Date: Thu, 1 Dec 2022 04:28:05 +0800 Subject: [PATCH 621/675] =?UTF-8?q?=E6=9B=B2=E7=BA=BF=E5=88=87=E5=89=B2?= =?UTF-8?q?=E5=87=BD=E6=95=B0=E4=BF=AE=E6=94=B9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../ExtensionMethod/Curve3dEx.cs | 38 ++++++++++--------- .../ExtensionMethod/EditorEx.cs | 7 ++-- src/IFoxCAD.Cad.Shared/Runtime/LateBinding.cs | 2 +- 3 files changed, 26 insertions(+), 21 deletions(-) diff --git a/src/IFoxCAD.Cad.Shared/ExtensionMethod/Curve3dEx.cs b/src/IFoxCAD.Cad.Shared/ExtensionMethod/Curve3dEx.cs index aef668c..c12275b 100644 --- a/src/IFoxCAD.Cad.Shared/ExtensionMethod/Curve3dEx.cs +++ b/src/IFoxCAD.Cad.Shared/ExtensionMethod/Curve3dEx.cs @@ -152,11 +152,13 @@ public static bool IsCircular(this Curve3d curve) public static List? GetSplitCurves(this CompositeCurve3d c3d, List pars) { // 曲线参数剔除重复的 - pars.Sort(); - for (int i = pars.Count - 1; i > 0; i--) - if (Tolerance.Global.IsEqualPoint(pars[i], pars[i - 1])) - pars.RemoveAt(i); - + if (pars.Count > 0) + { + pars.Sort(); + for (int i = pars.Count - 1; i > 0; i--) + if (Tolerance.Global.IsEqualPoint(pars[i], pars[i - 1])) + pars.RemoveAt(i); + } if (pars.Count == 0) return null; @@ -203,9 +205,10 @@ public static bool IsCircular(this Curve3d curve) } List curves = new(); + List cc3ds = new(); for (int i = 0; i < pars.Count - 1; i++) { - List cc3ds = new(); + cc3ds.Clear(); // 复合曲线参数转换到包含曲线参数 var cp1 = c3d.GlobalToLocalParameter(pars[i]); var cp2 = c3d.GlobalToLocalParameter(pars[i + 1]); @@ -223,27 +226,28 @@ public static bool IsCircular(this Curve3d curve) c3ds[cp1.SegmentIndex].GetSubCurve( cp1.LocalParameter, inter.UpperBound)); + for (int j = cp1.SegmentIndex + 1; j < cp2.SegmentIndex; j++) - { cc3ds.Add((Curve3d)c3ds[j].Clone()); - } + inter = c3ds[cp2.SegmentIndex].GetInterval(); cc3ds.Add( c3ds[cp2.SegmentIndex].GetSubCurve( inter.LowerBound, cp2.LocalParameter)); } - curves.Add(new CompositeCurve3d(cc3ds.ToArray())); + curves.Add(new(cc3ds.ToArray())); } - if (c3d.IsClosed() && c3ds.Length > 1) - { - var cus1 = curves[^1].GetCurves(); - var cus2 = curves[0].GetCurves(); - var cs = cus1.Combine2(cus2); - curves[^1] = new CompositeCurve3d(cs); - curves.RemoveAt(0); - } + // 封闭多段线 口口 并排形状,第二个口切割不成功,注释下面就成功了 + //if (c3d.IsClosed() && c3ds.Length > 1) + //{ + // var cus1 = curves[^1].GetCurves(); + // var cus2 = curves[0].GetCurves(); + // var cs = cus1.Combine2(cus2); + // curves[^1] = new(cs); + // curves.RemoveAt(0); + //} return curves; } diff --git a/src/IFoxCAD.Cad.Shared/ExtensionMethod/EditorEx.cs b/src/IFoxCAD.Cad.Shared/ExtensionMethod/EditorEx.cs index aff65da..f9ec949 100644 --- a/src/IFoxCAD.Cad.Shared/ExtensionMethod/EditorEx.cs +++ b/src/IFoxCAD.Cad.Shared/ExtensionMethod/EditorEx.cs @@ -1,4 +1,6 @@ -namespace IFoxCAD.Cad; +using IFoxCAD.Com; + +namespace IFoxCAD.Cad; /// /// 命令行扩展类 @@ -1079,8 +1081,7 @@ public enum RunLispFlag : byte { var dm = Acap.DocumentManager; var doc = dm.MdiActiveDocument; - if (doc != null) - doc.SendStringToExecute(lispCode + "\n", false, false, false); + doc?.SendStringToExecute(lispCode + "\n", false, false, false); } return null; } diff --git a/src/IFoxCAD.Cad.Shared/Runtime/LateBinding.cs b/src/IFoxCAD.Cad.Shared/Runtime/LateBinding.cs index bb12125..45cafa9 100644 --- a/src/IFoxCAD.Cad.Shared/Runtime/LateBinding.cs +++ b/src/IFoxCAD.Cad.Shared/Runtime/LateBinding.cs @@ -1,4 +1,4 @@ -namespace IFoxCAD.Cad; +namespace IFoxCAD.Com; /// /// 后绑代码工具 -- Gitee From 10b1183c627269b515f73df3798e768a77ce4dea Mon Sep 17 00:00:00 2001 From: liuqihong <540762622@qq.com> Date: Wed, 7 Dec 2022 04:14:15 +0800 Subject: [PATCH 622/675] =?UTF-8?q?=E4=BF=AE=E6=94=B9Abort=E6=97=A0?= =?UTF-8?q?=E6=95=88(#I647JO)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/IFoxCAD.Cad.Shared/Runtime/DBTrans.cs | 25 ++++++++++++----------- 1 file changed, 13 insertions(+), 12 deletions(-) diff --git a/src/IFoxCAD.Cad.Shared/Runtime/DBTrans.cs b/src/IFoxCAD.Cad.Shared/Runtime/DBTrans.cs index ba89402..90fc981 100644 --- a/src/IFoxCAD.Cad.Shared/Runtime/DBTrans.cs +++ b/src/IFoxCAD.Cad.Shared/Runtime/DBTrans.cs @@ -14,25 +14,25 @@ namespace IFoxCAD.Cad; public class DBTrans : IDisposable { [DebuggerBrowsable(DebuggerBrowsableState.Never)] - private string DebuggerDisplay => ToString(" | "); + string DebuggerDisplay => ToString(" | "); #region 私有字段 /// - /// 文档锁 + /// 事务栈 /// - private readonly DocumentLock? _documentLock; + static readonly Stack _dBTrans = new(); /// - /// 是否提交事务 + /// 文档锁 /// - private readonly bool _commit; + readonly DocumentLock? _documentLock; /// - /// 事务栈 + /// 是否提交事务 /// - private static readonly Stack _dBTrans = new(); + bool _commit; /// /// 文件名 /// - private readonly string? _fileName; + readonly string? _fileName; #endregion #region 公开属性 @@ -606,7 +606,8 @@ public void Task(Action action, bool handlingDBTextDeviation = true) /// public void Abort() { - Dispose(false); + _commit = false; + Dispose(); } /// @@ -614,7 +615,8 @@ public void Abort() /// public void Commit() { - Dispose(true); + _commit = true; + Dispose(); } public bool IsDisposed { get; private set; } = false; @@ -653,11 +655,10 @@ protected virtual void Dispose(bool disposing) // 致命错误时候此处是空,直接跳过 if (Transaction != null) { - if (_commit)// disposing + if (_commit) { // 刷新队列(后台不刷新) Editor?.Redraw(); - // 调用cad的事务进行提交,释放托管状态(托管对象) Transaction.Commit(); } -- Gitee From 9987d0b3a7e8523314ed51e7a5defd336799efc7 Mon Sep 17 00:00:00 2001 From: liuqihong <540762622@qq.com> Date: Wed, 7 Dec 2022 22:02:29 +0800 Subject: [PATCH 623/675] =?UTF-8?q?=E5=8A=A0=E5=85=A5=E6=8B=89=E4=BC=B8?= =?UTF-8?q?=E5=A1=AB=E5=85=85?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../IFoxCAD.Cad.Shared.projitems | 4 +- src/IFoxCAD.Cad.Shared/WindowsAPI/Enums.cs | 1071 +++++++++++++++++ .../WindowsAPI/MouseHook.cs | 293 +++++ .../{Copyclip => WindowsAPI}/WindowsAPI.cs | 18 +- ...63\351\224\256\350\217\234\345\215\225.cs" | 184 +++ ...53\345\205\205\344\272\213\344\273\266.cs" | 1029 ++++++++++++++++ .../HatchHelper.cs" | 124 ++ ...350\275\254cad\345\235\220\346\240\207.cs" | 133 ++ ...06\347\232\204\344\276\213\345\255\220.cs" | 94 ++ ...3\345\205\205\350\276\271\347\225\214.txt" | 104 ++ ...71\347\202\271\344\276\213\345\255\220.cs" | 100 ++ .../IMEControl.cs" | 4 +- 12 files changed, 3141 insertions(+), 17 deletions(-) create mode 100644 src/IFoxCAD.Cad.Shared/WindowsAPI/Enums.cs create mode 100644 src/IFoxCAD.Cad.Shared/WindowsAPI/MouseHook.cs rename src/IFoxCAD.Cad.Shared/{Copyclip => WindowsAPI}/WindowsAPI.cs (98%) create mode 100644 "tests/TestAcad08/\346\213\211\344\274\270\345\241\253\345\205\205/01.\346\213\211\344\274\270\345\241\253\345\205\205\345\217\263\351\224\256\350\217\234\345\215\225.cs" create mode 100644 "tests/TestAcad08/\346\213\211\344\274\270\345\241\253\345\205\205/02.\346\213\211\344\274\270\345\241\253\345\205\205\344\272\213\344\273\266.cs" create mode 100644 "tests/TestAcad08/\346\213\211\344\274\270\345\241\253\345\205\205/HatchHelper.cs" create mode 100644 "tests/TestAcad08/\346\213\211\344\274\270\345\241\253\345\205\205/\345\261\217\345\271\225\345\235\220\346\240\207\350\275\254cad\345\235\220\346\240\207.cs" create mode 100644 "tests/TestAcad08/\346\213\211\344\274\270\345\241\253\345\205\205/\350\277\207\347\250\213\346\226\207\344\273\266/\344\270\200\344\270\252\346\227\240\346\263\225\347\247\273\345\212\250\347\272\242\350\211\262\345\234\206\347\232\204\344\276\213\345\255\220.cs" create mode 100644 "tests/TestAcad08/\346\213\211\344\274\270\345\241\253\345\205\205/\350\277\207\347\250\213\346\226\207\344\273\266/\350\207\252\345\256\232\344\271\211\345\233\276\345\205\203\345\241\253\345\205\205\350\276\271\347\225\214.txt" create mode 100644 "tests/TestAcad08/\346\213\211\344\274\270\345\241\253\345\205\205/\350\277\207\347\250\213\346\226\207\344\273\266/\350\216\267\345\217\226\345\244\271\347\202\271\344\276\213\345\255\220.cs" diff --git a/src/IFoxCAD.Cad.Shared/IFoxCAD.Cad.Shared.projitems b/src/IFoxCAD.Cad.Shared/IFoxCAD.Cad.Shared.projitems index 7700de1..3493040 100644 --- a/src/IFoxCAD.Cad.Shared/IFoxCAD.Cad.Shared.projitems +++ b/src/IFoxCAD.Cad.Shared/IFoxCAD.Cad.Shared.projitems @@ -83,6 +83,8 @@ - + + + \ No newline at end of file diff --git a/src/IFoxCAD.Cad.Shared/WindowsAPI/Enums.cs b/src/IFoxCAD.Cad.Shared/WindowsAPI/Enums.cs new file mode 100644 index 0000000..e70dfd6 --- /dev/null +++ b/src/IFoxCAD.Cad.Shared/WindowsAPI/Enums.cs @@ -0,0 +1,1071 @@ +#if true +namespace IFoxCAD.Cad; + +// https://blog.csdn.net/qq_43812868/article/details/108587936 +[Flags] +public enum TH32CS : uint +{ + /// + /// 原因在于如果不采用改参数的话,有可能快照会占用整个堆的空间 + /// + TH32CS_SNAPNOHEAPS = 0x40000000, + /// + /// 声明快照句柄是可继承的 + /// + TH32CS_INHERIT = 0x80000000, + /// + /// 在快照中包含在th32ProcessID中指定的进程的所有的堆 + /// + TH32CS_SNAPHEAPLIST = 0x00000001, + /// + /// 在快照中包含系统中所有的进程 + /// + TH32CS_SNAPPROCESS = 0x00000002, + /// + /// 在快照中包含系统中所有的线程 + /// + TH32CS_SNAPTHREAD = 0x00000004, + /// + /// 在快照中包含在th32ProcessID中指定的进程的所有的模块 + /// + TH32CS_SNAPMODULE = 0x00000008, + /// + /// 在快照中包含系统中所有的进程和线程 + /// + TH32CS_SNAPALL = TH32CS_SNAPHEAPLIST | TH32CS_SNAPPROCESS | TH32CS_SNAPTHREAD | TH32CS_SNAPMODULE, +} + +/// +/// 设置的钩子类型 +/// +[Flags] +public enum HookType : int +{ + /// + /// WH_MSGFILTER 和 WH_SYSMSGFILTER Hooks使我们可以监视菜单,滚动 + /// 条,消息框,对话框消息并且发现用户使用ALT+TAB or ALT+ESC 组合键切换窗口。 + /// WH_MSGFILTER Hook只能监视传递到菜单,滚动条,消息框的消息,以及传递到通 + /// 过安装了Hook子过程的应用程序建立的对话框的消息。WH_SYSMSGFILTER Hook + /// 监视所有应用程序消息。 + /// + /// WH_MSGFILTER 和 WH_SYSMSGFILTER Hooks使我们可以在模式循环期间 + /// 过滤消息,这等价于在主消息循环中过滤消息。 + /// + /// 通过调用CallMsgFilter function可以直接的调用WH_MSGFILTER Hook。通过使用这 + /// 个函数,应用程序能够在模式循环期间使用相同的代码去过滤消息,如同在主消息循 + /// 环里一样 + /// + WH_MSGFILTER = -1, + /// + /// WH_JOURNALRECORD Hook用来监视和记录输入事件。典型的,可以使用这 + /// 个Hook记录连续的鼠标和键盘事件,然后通过使用WH_JOURNALPLAYBACK Hook + /// 来回放。WH_JOURNALRECORD Hook是全局Hook,它不能象线程特定Hook一样 + /// 使用。WH_JOURNALRECORD是system-wide local hooks,它们不会被注射到任何行 + /// 程地址空间 + /// + WH_JOURNALRECORD = 0, + /// + /// WH_JOURNALPLAYBACK Hook使应用程序可以插入消息到系统消息队列。可 + /// 以使用这个Hook回放通过使用WH_JOURNALRECORD Hook记录下来的连续的鼠 + /// 标和键盘事件。只要WH_JOURNALPLAYBACK Hook已经安装,正常的鼠标和键盘 + /// 事件就是无效的。WH_JOURNALPLAYBACK Hook是全局Hook,它不能象线程特定 + /// Hook一样使用。WH_JOURNALPLAYBACK Hook返回超时值,这个值告诉系统在处 + /// 理来自回放Hook当前消息之前需要等待多长时间(毫秒)。这就使Hook可以控制实 + /// 时事件的回放。WH_JOURNALPLAYBACK是system-wide local hooks,它们不会被 + /// 注射到任何行程地址空间 + /// + WH_JOURNALPLAYBACK = 1, + /// + /// 在应用程序中,WH_KEYBOARD Hook用来监视WM_KEYDOWN and + /// WM_KEYUP消息,这些消息通过GetMessage or PeekMessage function返回。可以使 + /// 用这个Hook来监视输入到消息队列中的键盘消息 + /// + WH_KEYBOARD = 2, + /// + /// 应用程序使用WH_GETMESSAGE Hook来监视从GetMessage or PeekMessage函 + /// 数返回的消息。你可以使用WH_GETMESSAGE Hook去监视鼠标和键盘输入,以及 + /// 其它发送到消息队列中的消息 + /// + WH_GETMESSAGE = 3, + /// + /// 监视发送到窗口过程的消息,系统在消息发送到接收窗口过程之前调用 + /// + WH_CALLWNDPROC = 4, + /// + /// 在以下事件之前,系统都会调用WH_CBT Hook子过程,这些事件包括: + /// 1. 激活,建立,销毁,最小化,最大化,移动,改变尺寸等窗口事件; + /// 2. 完成系统指令; + /// 3. 来自系统消息队列中的移动鼠标,键盘事件; + /// 4. 设置输入焦点事件; + /// 5. 同步系统消息队列事件。 + /// Hook子过程的返回值确定系统是否允许或者防止这些操作中的一个 + /// + WH_CBT = 5, + /// + /// WH_MSGFILTER 和 WH_SYSMSGFILTER Hooks使我们可以监视菜单,滚动 + /// 条,消息框,对话框消息并且发现用户使用ALT+TAB or ALT+ESC 组合键切换窗口。 + /// WH_MSGFILTER Hook只能监视传递到菜单,滚动条,消息框的消息,以及传递到通 + /// 过安装了Hook子过程的应用程序建立的对话框的消息。WH_SYSMSGFILTER Hook + /// 监视所有应用程序消息。 + /// + /// WH_MSGFILTER 和 WH_SYSMSGFILTER Hooks使我们可以在模式循环期间 + /// 过滤消息,这等价于在主消息循环中过滤消息。 + /// + /// 通过调用CallMsgFilter function可以直接的调用WH_MSGFILTER Hook。通过使用这 + /// 个函数,应用程序能够在模式循环期间使用相同的代码去过滤消息,如同在主消息循 + /// 环里一样 + /// + WH_SYSMSGFILTER = 6, + /// + /// WH_MOUSE Hook监视从GetMessage 或者 PeekMessage 函数返回的鼠标消息。 + /// 使用这个Hook监视输入到消息队列中的鼠标消息 + /// + WH_MOUSE = 7, + /// + /// 当调用GetMessage 或 PeekMessage 来从消息队列种查询非鼠标、键盘消息时 + /// + WH_HARDWARE = 8, + /// + /// 在系统调用系统中与其它Hook关联的Hook子过程之前,系统会调用 + /// WH_DEBUG Hook子过程。你可以使用这个Hook来决定是否允许系统调用与其它 + /// Hook关联的Hook子过程 + /// + WH_DEBUG = 9, + /// + /// 外壳应用程序可以使用WH_SHELL Hook去接收重要的通知。当外壳应用程序是 + /// 激活的并且当顶层窗口建立或者销毁时,系统调用WH_SHELL Hook子过程。 + /// WH_SHELL 共有5钟情况: + /// 1. 只要有个top-level、unowned 窗口被产生、起作用、或是被摧毁; + /// 2. 当Taskbar需要重画某个按钮; + /// 3. 当系统需要显示关于Taskbar的一个程序的最小化形式; + /// 4. 当目前的键盘布局状态改变; + /// 5. 当使用者按Ctrl+Esc去执行Task Manager(或相同级别的程序)。 + /// + /// 按照惯例,外壳应用程序都不接收WH_SHELL消息。所以,在应用程序能够接 + /// 收WH_SHELL消息之前,应用程序必须调用SystemParametersInfo function注册它自 + /// 己 + /// + WH_SHELL = 10, + /// + /// 当应用程序的前台线程处于空闲状态时,可以使用WH_FOREGROUNDIDLE + /// Hook执行低优先级的任务。当应用程序的前台线程大概要变成空闲状态时,系统就 + /// 会调用WH_FOREGROUNDIDLE Hook子过程 + /// + WH_FOREGROUNDIDLE = 11, + /// + /// 监视发送到窗口过程的消息,系统在消息发送到接收窗口过程之后调用 + /// + WH_CALLWNDPROCRET = 12, + /// + /// 监视键盘消息 + /// + WH_KEYBOARD_LL = 13, + /// + /// 监视鼠标消息 + /// + WH_MOUSE_LL = 14 +} + +/// +/// 消息类型 +/// 作为SendMessage和PostMessage的参数 +/// +[Flags] +public enum WM : uint +{ + /// + /// 创建一个窗口 + /// + WM_CREATE = 0x01, + /// + /// 当一个窗口被破坏时发送 + /// + WM_DESTROY = 0x02, + /// + /// 移动一个窗口 + /// + WM_MOVE = 0x03, + /// + /// 改变一个窗口的大小 + /// + WM_SIZE = 0x05, + /// + /// 一个窗口被激活或失去激活状态 + /// + WM_ACTIVATE = 0x06, + /// + /// 一个窗口获得焦点 + /// + WM_SETFOCUS = 0x07, + /// + /// 一个窗口失去焦点 + /// + WM_KILLFOCUS = 0x08, + /// + /// 一个窗口改变成Enable状态 + /// + WM_ENABLE = 0x0A, + /// + /// 设置窗口是否能重画 + /// + WM_SETREDRAW = 0x0B, + /// + /// 应用程序发送此消息来设置一个窗口的文本 + /// + WM_SETTEXT = 0x0C, + /// + /// 应用程序发送此消息来复制对应窗口的文本到缓冲区 + /// + WM_GETTEXT = 0x0D, + /// + /// 得到与一个窗口有关的文本的长度(不包含空字符) + /// + WM_GETTEXTLENGTH = 0x0E, + /// + /// 要求一个窗口重画自己 + /// + WM_PAINT = 0x0F, + /// + /// 当一个窗口或应用程序要关闭时发送一个信号 + /// + WM_CLOSE = 0x10, + /// + /// 当用户选择结束对话框或程序自己调用ExitWindows函数 + /// + WM_QUERYENDSESSION = 0x11, + /// + /// 用来结束程序运行 + /// + WM_QUIT = 0x12, + /// + /// 当用户窗口恢复以前的大小位置时,把此消息发送给某个图标 + /// + WM_QUERYOPEN = 0x13, + /// + /// 当窗口背景必须被擦除时(例在窗口改变大小时) + /// + WM_ERASEBKGND = 0x14, + /// + /// 当系统颜色改变时,发送此消息给所有顶级窗口 + /// + WM_SYSCOLORCHANGE = 0x15, + /// + /// 当系统进程发出WM_QUERYENDSESSION消息后,此消息发送给应用程序,通知它对话是否结束 + /// + WM_ENDSESSION = 0x16, + /// + /// 当隐藏或显示窗口是发送此消息给这个窗口 + /// + WM_SHOWWINDOW = 0x18, + /// + /// 发此消息给应用程序哪个窗口是激活的,哪个是非激活的 + /// + WM_ACTIVATEAPP = 0x1C, + /// + /// 当系统的字体资源库变化时发送此消息给所有顶级窗口 + /// + WM_FONTCHANGE = 0x1D, + /// + /// 当系统的时间变化时发送此消息给所有顶级窗口 + /// + WM_TIMECHANGE = 0x1E, + /// + /// 发送此消息来取消某种正在进行的摸态(操作) + /// + WM_CANCELMODE = 0x1F, + /// + /// 如果鼠标引起光标在某个窗口中移动且鼠标输入没有被捕获时,就发消息给某个窗口 + /// + WM_SETCURSOR = 0x20, + /// + /// 当光标在某个非激活的窗口中而用户正按着鼠标的某个键发送此消息给当前窗口 + /// + WM_MOUSEACTIVATE = 0x21, + /// + /// 发送此消息给MDI子窗口当用户点击此窗口的标题栏或当窗口被激活,移动,改变大小 + /// + WM_CHILDACTIVATE = 0x22, + /// + /// 此消息由基于计算机的训练程序发送,通过WH_JOURNALPALYBACK的hook程序分离出用户输入消息 + /// + WM_QUEUESYNC = 0x23, + /// + /// 此消息发送给窗口当它将要改变大小或位置 + /// + WM_GETMINMAXINFO = 0x24, + /// + /// 发送给最小化窗口当它图标将要被重画 + /// + WM_PAINTICON = 0x26, + /// + /// 此消息发送给某个最小化窗口,仅当它在画图标前它的背景必须被重画 + /// + WM_ICONERASEBKGND = 0x27, + /// + /// 发送此消息给一个对话框程序去更改焦点位置 + /// + WM_NEXTDLGCTL = 0x28, + /// + /// 每当打印管理列队增加或减少一条作业时发出此消息 + /// + WM_SPOOLERSTATUS = 0x2A, + /// + /// 当button,combobox,listbox,menu的可视外观改变时发送 + /// + WM_DRAWITEM = 0x2B, + /// + /// 当button, combo box, list box, list view control, or menu item 被创建时 + /// + WM_MEASUREITEM = 0x2C, + /// + /// 此消息有一个LBS_WANTKEYBOARDINPUT风格的发出给它的所有者来响应WM_KEYDOWN消息 + /// + WM_VKEYTOITEM = 0x2E, + /// + /// 此消息由一个LBS_WANTKEYBOARDINPUT风格的列表框发送给他的所有者来响应WM_CHAR消息 + /// + WM_CHARTOITEM = 0x2F, + /// + /// 当绘制文本时程序发送此消息得到控件要用的颜色 + /// + WM_SETFONT = 0x30, + /// + /// 应用程序发送此消息得到当前控件绘制文本的字体 + /// + WM_GETFONT = 0x31, + /// + /// 应用程序发送此消息让一个窗口与一个热键相关连 + /// + WM_SETHOTKEY = 0x32, + /// + /// 应用程序发送此消息来判断热键与某个窗口是否有关联 + /// + WM_GETHOTKEY = 0x33, + /// + /// 此消息发送给最小化窗口,当此窗口将要被拖放而它的类中没有定义图标,应用程序能返回一个图标或光标的句柄,当用户拖放图标时系统显示这个图标或光标 + /// + WM_QUERYDRAGICON = 0x37, + /// + /// 发送此消息来判定combobox或listbox新增加的项的相对位置 + /// + WM_COMPAREITEM = 0x39, + /// + /// 显示内存已经很少了 + /// + WM_COMPACTING = 0x41, + /// + /// 窗口大小和位置将要被改变时,来调用Setwindowpos函数或其它窗口管理函数 + /// + WM_WINDOWPOSCHANGING = 0x46, + /// + /// 窗口大小和位置已经被改变后,来调用Setwindowpos函数或其它窗口管理函数 + /// + WM_WINDOWPOSCHANGED = 0x47, + /// + /// 当系统将要进入暂停状态时发送此消息 + /// + WM_POWER = 0x48, + /// + /// 当一个应用程序传递数据给另一个应用程序时发送此消息 + /// + WM_COPYDATA = 0x4A, + /// + /// 当某个用户取消程序日志激活状态,提交此消息给程序 + /// + WM_CANCELJOURNA = 0x4B, + /// + /// 当某个控件的某个事件已经发生或这个控件需要得到一些信息时,发送此消息给它的父窗口 + /// + WM_NOTIFY = 0x4E, + /// + /// 当用户选择某种输入语言,或输入语言的热键改变 + /// + WM_INPUTLANGCHANGEREQUEST = 0x50, + /// + /// 当平台现场已经被改变后发送此消息给受影响的最顶级窗口 + /// + WM_INPUTLANGCHANGE = 0x51, + /// + /// 当程序已经初始化windows帮助例程时发送此消息给应用程序 + /// + WM_TCARD = 0x52, + /// + /// 此消息显示用户按下了F1,如果某个菜单是激活的,就发送此消息个此窗口关联的菜单,否则就发送给有焦点的窗口,如果当前都没有焦点,就把此消息发送给当前激活的窗口 + /// + WM_HELP = 0x53, + /// + /// 当用户已经登入或退出后发送此消息给所有的窗口,当用户登入或退出时系统更新用户的具体设置信息,在用户更新设置时系统马上发送此消息 + /// + WM_USERCHANGED = 0x54, + /// + /// 公用控件,自定义控件和他们的父窗口通过此消息来判断控件是使用ANSI还是UNICODE结构 + /// + WM_NOTIFYFORMAT = 0x55, + /// + /// 当用户某个窗口中点击了一下右键就发送此消息给这个窗口 + /// + WM_CONTEXTMENU = 0x7B, + /// + /// 当调用SETWINDOWLONG函数将要改变一个或多个窗口的风格时发送此消息给那个窗口 + /// + WM_STYLECHANGING = 0x7C, + /// + /// 当调用SETWINDOWLONG函数一个或多个窗口的风格后发送此消息给那个窗口 + /// + WM_STYLECHANGED = 0x7D, + /// + /// 当显示器的分辨率改变后发送此消息给所有的窗口 + /// + WM_DISPLAYCHANGE = 0x7E, + /// + /// 此消息发送给某个窗口来返回与某个窗口有关连的大图标或小图标的句柄 + /// + WM_GETICON = 0x7F, + /// + /// 程序发送此消息让一个新的大图标或小图标与某个窗口关联 + /// + WM_SETICON = 0x80, + /// + /// 当某个窗口第一次被创建时,此消息在WM_CREATE消息发送前发送 + /// + WM_NCCREATE = 0x81, + /// + /// 此消息通知某个窗口,非客户区正在销毁 + /// + WM_NCDESTROY = 0x82, + /// + /// 当某个窗口的客户区域必须被核算时发送此消息 + /// + WM_NCCALCSIZE = 0x83, + /// + /// 移动鼠标/按住/释放鼠标时 + /// + WM_NCHITTEST = 0x84, + /// + /// 程序发送此消息给某个窗口当它(窗口)的框架必须被绘制时 + /// + WM_NCPAINT = 0x85, + /// + /// 此消息发送给某个窗口仅当它的非客户区需要被改变来显示是激活还是非激活状态 + /// + WM_NCACTIVATE = 0x86, + /// + /// 发送此消息给某个与对话框程序关联的控件,widdows控制方位键和TAB键使输入进入此控件通过应 + /// + WM_GETDLGCODE = 0x87, + /// + /// 当光标在一个窗口的非客户区内移动时发送此消息给这个窗口 非客户区为:窗体的标题栏及窗的边框体 + /// + WM_NCMOUSEMOVE = 0xA0, + /// + /// 当光标在一个窗口的非客户区同时按下鼠标左键时提交此消息 + /// + WM_NCLBUTTONDOWN = 0xA1, + /// + /// 当用户释放鼠标左键同时光标某个窗口在非客户区时发送此消息 + /// + WM_NCLBUTTONUP = 0xA2, + /// + /// 当用户双击鼠标左键同时光标某个窗口在非客户区时发送此消息 + /// + WM_NCLBUTTONDBLCLK = 0xA3, + /// + /// 当用户按下鼠标右键同时光标又在窗口的非客户区时发送此消息 + /// + WM_NCRBUTTONDOWN = 0xA4, + /// + /// 当用户释放鼠标右键同时光标又在窗口的非客户区时发送此消息 + /// + WM_NCRBUTTONUP = 0xA5, + /// + /// 当用户双击鼠标右键同时光标某个窗口在非客户区时发送此消息 + /// + WM_NCRBUTTONDBLCLK = 0xA6, + /// + /// 当用户按下鼠标中键同时光标又在窗口的非客户区时发送此消息 + /// + WM_NCMBUTTONDOWN = 0xA7, + /// + /// 当用户释放鼠标中键同时光标又在窗口的非客户区时发送此消息 + /// + WM_NCMBUTTONUP = 0xA8, + /// + /// 当用户双击鼠标中键同时光标又在窗口的非客户区时发送此消息 + /// + WM_NCMBUTTONDBLCLK = 0xA9, + + // 所有的键盘消息只有中间的八种,也就是 WM_KEYDOWN 到 WM_SYSDEADCHAR + /// + /// 按下一个键 == WM_KEYDOWN + /// + WM_KEYFIRST = 0x0100, + /// + /// 按下一个键 + /// + WM_KEYDOWN = 0x0100, + /// + /// 释放一个键 + /// + WM_KEYUP = 0x0101, + /// + /// 按下某键,并已发出WM_KEYDOWN, WM_KEYUP消息 + /// + WM_CHAR = 0x102, + /// + /// 当用translatemessage函数翻译WM_KEYUP消息时发送此消息给拥有焦点的窗口 + /// + WM_DEADCHAR = 0x103, + /// + /// 当用户按住ALT键同时按下其它键时提交此消息给拥有焦点的窗口 + /// + WM_SYSKEYDOWN = 0x104, + /// + /// 当用户释放一个键同时ALT 键还按着时提交此消息给拥有焦点的窗口 + /// + WM_SYSKEYUP = 0x105, + /// + /// 当WM_SYSKEYDOWN消息被TRANSLATEMESSAGE函数翻译后提交此消息给拥有焦点的窗口 + /// + WM_SYSCHAR = 0x106, + /// + /// 当WM_SYSKEYDOWN消息被TRANSLATEMESSAGE函数翻译后发送此消息给拥有焦点的窗口 + /// + WM_SYSDEADCHAR = 0x107, + /// + /// 在一个对话框程序被显示前发送此消息给它,通常用此消息初始化控件和执行其它任务 + /// + WM_INITDIALOG = 0x110, + /// + /// 当用户选择一条菜单命令项或当某个控件发送一条消息给它的父窗口 + /// + WM_COMMAND = 0x111, + + /// + /// 当用户选择窗口菜单的一条命令或最大化最小化时窗口前会收到此消息 + /// + WM_SYSCOMMAND = 0x112, + /// + /// 发生了定时器事件 + /// + WM_TIMER = 0x113, + /// + /// 当一个窗口标准水平滚动条产生一个滚动事件时发送此消息给那个窗口,也发送给拥有它的控件 + /// + WM_HSCROLL = 0x114, + /// + /// 当一个窗口标准垂直滚动条产生一个滚动事件时发送此消息给那个窗口也,发送给拥有它的控件 + /// + WM_VSCROLL = 0x115, + /// + /// 当一个菜单将要被激活时发送此消息,它发生在用户菜单条中的某项或按下某个菜单键,它允许程序在显示前更改菜单 + /// + WM_INITMENU = 0x116, + /// + /// 当一个下拉菜单或子菜单将要被激活时发送此消息,它允许程序在它显示前更改菜单,而不要改变全部 + /// + WM_INITMENUPOPUP = 0x117, + /// + /// 当用户选择一条菜单项时发送此消息给菜单的所有者(一般是窗口) + /// + WM_MENUSELECT = 0x11F, + /// + /// 当菜单已被激活用户按下了某个键(不同于加速键),发送此消息给菜单的所有者 + /// + WM_MENUCHAR = 0x120, + /// + /// 当一个模态对话框或菜单进入空载状态时发送此消息给它的所有者,一个模态对话框或菜单进入空载状态就是在处理完一条或几条先前的消息后没有消息它的列队中等待 + /// + WM_ENTERIDLE = 0x121, + /// + /// 在windows绘制消息框前发送此消息给消息框的所有者窗口,通过响应这条消息,所有者窗口可以通过使用给定的相关显示设备的句柄来设置消息框的文本和背景颜色 + /// + WM_CTLCOLORMSGBOX = 0x132, + /// + /// 当一个编辑型控件将要被绘制时发送此消息给它的父窗口通过响应这条消息,所有者窗口可以通过使用给定的相关显示设备的句柄来设置编辑框的文本和背景颜色 + /// + WM_CTLCOLOREDIT = 0x133, + + /// + /// 当一个列表框控件将要被绘制前发送此消息给它的父窗口通过响应这条消息,所有者窗口可以通过使用给定的相关显示设备的句柄来设置列表框的文本和背景颜色 + /// + WM_CTLCOLORLISTBOX = 0x134, + /// + /// 当一个按钮控件将要被绘制时发送此消息给它的父窗口通过响应这条消息,所有者窗口可以通过使用给定的相关显示设备的句柄来设置按纽的文本和背景颜色 + /// + WM_CTLCOLORBTN = 0x135, + /// + /// 当一个对话框控件将要被绘制前发送此消息给它的父窗口通过响应这条消息,所有者窗口可以通过使用给定的相关显示设备的句柄来设置对话框的文本背景颜色 + /// + WM_CTLCOLORDLG = 0x136, + /// + /// 当一个滚动条控件将要被绘制时发送此消息给它的父窗口通过响应这条消息,所有者窗口可以通过使用给定的相关显示设备的句柄来设置滚动条的背景颜色 + /// + WM_CTLCOLORSCROLLBAR = 0x137, + /// + /// 当一个静态控件将要被绘制时发送此消息给它的父窗口通过响应这条消息,所有者窗口可以通过使用给定的相关显示设备的句柄来设置静态控件的文本和背景颜色 + /// + WM_CTLCOLORSTATIC = 0x138, + + /// + /// 当鼠标轮子转动时发送此消息个当前有焦点的控件 + /// + WM_MOUSEWHEEL = 0x20A, + /// + /// 双击鼠标中键 + /// + WM_MBUTTONDBLCLK = 0x209, + /// + /// 释放鼠标中键 + /// + WM_MBUTTONUP = 0x208, + /// + /// 移动鼠标时发生,同WM_MOUSEFIRST + /// + WM_MOUSEMOVE = 0x200, + /// + /// 按下鼠标左键 + /// + WM_LBUTTONDOWN = 0x201, + /// + /// 释放鼠标左键 + /// + WM_LBUTTONUP = 0x202, + /// + /// 双击鼠标左键 + /// + WM_LBUTTONDBLCLK = 0x203, + /// + /// 按下鼠标右键 + /// + WM_RBUTTONDOWN = 0x204, + /// + /// 释放鼠标右键 + /// + WM_RBUTTONUP = 0x205, + /// + /// 双击鼠标右键 + /// + WM_RBUTTONDBLCLK = 0x206, + /// + /// 按下鼠标中键 + /// + WM_MBUTTONDOWN = 0x207, + + WM_USER = 0x0400, + + /// + /// 执行复制成功 + /// + WM_CLIPBOARDUPDATE = 0x031D, +} + +// https://blog.csdn.net/biyusr/article/details/108376195 +public enum MOUSEEVENTF : int +{ + /// + /// 移动鼠标 + /// + MOVE = 0x0001, + /// + /// 模拟鼠标左键按下 + /// + LEFTDOWN = 0x0002, + /// + /// 模拟鼠标左键抬起 + /// + LEFTUP = 0x0004, + /// + /// 模拟鼠标右键按下 + /// + RIGHTDOWN = 0x0008, + /// + /// 模拟鼠标右键抬起 + /// + RIGHTUP = 0x0010, + /// + /// 模拟鼠标中键按下 + /// + MIDDLEDOWN = 0x0020, + /// + /// 模拟鼠标中键抬起 + /// + MIDDLEUP = 0x0040, + /// + /// 标示是否采用绝对坐标 + /// + ABSOLUTE = 0x8000, + /// + /// 模拟鼠标滚轮滚动操作,必须配合dwData参数 + /// + WHEEL = 0x0800, +} + +// C#使用SendMessage发送组合键 +// https://www.cnblogs.com/johnsonton/articles/2331430.html +// Windows 使用的256个虚拟键码 +[Flags]// 打印的时候可以有名称输出,而不是值输出 +public enum VK : int +{ + VK_LBUTTON = 0x1, + VK_RBUTTON = 0x2, + VK_CANCEL = 0x3, + VK_MBUTTON = 0x4, + VK_BACK = 0x8, + VK_TAB = 0x9, + VK_CLEAR = 0xC, + VK_RETURN = 0xD, + VK_SHIFT = 0x10, + VK_CONTROL = 0x11, + VK_MENU = 0x12,// VK_ALT + VK_ALT = 0x12, + VK_PAUSE = 0x13, + VK_CAPITAL = 0x14, + VK_ESCAPE = 0x1B, + VK_SPACE = 0x20, + VK_PRIOR = 0x21, + VK_NEXT = 0x22, + VK_END = 0x23, + VK_HOME = 0x24, + VK_LEFT = 0x25, + VK_UP = 0x26, + VK_RIGHT = 0x27, + VK_DOWN = 0x28, + VK_Select = 0x29, + VK_PRINT = 0x2A, + VK_EXECUTE = 0x2B, + VK_SNAPSHOT = 0x2C, + VK_Insert = 0x2D, + VK_Delete = 0x2E, + VK_HELP = 0x2F, + VK_0 = 0x30, + VK_1 = 0x31, + VK_2 = 0x32, + VK_3 = 0x33, + VK_4 = 0x34, + VK_5 = 0x35, + VK_6 = 0x36, + VK_7 = 0x37, + VK_8 = 0x38, + VK_9 = 0x39, + VK_A = 0x41, + VK_B = 0x42, + VK_C = 0x43, + VK_D = 0x44, + VK_E = 0x45, + VK_F = 0x46, + VK_G = 0x47, + VK_H = 0x48, + VK_I = 0x49, + VK_J = 0x4A, + VK_K = 0x4B, + VK_L = 0x4C, + VK_M = 0x4D, + VK_N = 0x4E, + VK_O = 0x4F, + VK_P = 0x50, + VK_Q = 0x51, + VK_R = 0x52, + VK_S = 0x53, + VK_T = 0x54, + VK_U = 0x55, + VK_V = 0x56, + VK_W = 0x57, + VK_X = 0x58, + VK_Y = 0x59, + VK_Z = 0x5A, + VK_STARTKEY = 0x5B, + VK_CONTEXTKEY = 0x5D, + VK_NUMPAD0 = 0x60, + VK_NUMPAD1 = 0x61, + VK_NUMPAD2 = 0x62, + VK_NUMPAD3 = 0x63, + VK_NUMPAD4 = 0x64, + VK_NUMPAD5 = 0x65, + VK_NUMPAD6 = 0x66, + VK_NUMPAD7 = 0x67, + VK_NUMPAD8 = 0x68, + VK_NUMPAD9 = 0x69, + VK_MULTIPLY = 0x6A, + VK_ADD = 0x6B, + VK_SEPARATOR = 0x6C, + VK_SUBTRACT = 0x6D, + VK_DECIMAL = 0x6E, + VK_DIVIDE = 0x6F, + VK_F1 = 0x70, + VK_F2 = 0x71, + VK_F3 = 0x72, + VK_F4 = 0x73, + VK_F5 = 0x74, + VK_F6 = 0x75, + VK_F7 = 0x76, + VK_F8 = 0x77, + VK_F9 = 0x78, + VK_F10 = 0x79, + VK_F11 = 0x7A, + VK_F12 = 0x7B, + VK_F13 = 0x7C, + VK_F14 = 0x7D, + VK_F15 = 0x7E, + VK_F16 = 0x7F, + VK_F17 = 0x80, + VK_F18 = 0x81, + VK_F19 = 0x82, + VK_F20 = 0x83, + VK_F21 = 0x84, + VK_F22 = 0x85, + VK_F23 = 0x86, + VK_F24 = 0x87, + VK_NUMLOCK = 0x90, + VK_OEM_SCROLL = 0x91, + VK_OEM_1 = 0xBA, + VK_OEM_PLUS = 0xBB, + VK_OEM_COMMA = 0xBC, + VK_OEM_MINUS = 0xBD, + VK_OEM_PERIOD = 0xBE, + VK_OEM_2 = 0xBF, + VK_OEM_3 = 0xC0, + VK_OEM_4 = 0xDB, + VK_OEM_5 = 0xDC, + VK_OEM_6 = 0xDD, + VK_OEM_7 = 0xDE, + VK_OEM_8 = 0xDF, + VK_ICO_F17 = 0xE0, + VK_ICO_F18 = 0xE1, + VK_OEM102 = 0xE2, + VK_ICO_HELP = 0xE3, + VK_ICO_00 = 0xE4, + VK_ICO_CLEAR = 0xE6, + VK_OEM_RESET = 0xE9, + VK_OEM_JUMP = 0xEA, + VK_OEM_PA1 = 0xEB, + VK_OEM_PA2 = 0xEC, + VK_OEM_PA3 = 0xED, + VK_OEM_WSCTRL = 0xEE, + VK_OEM_CUSEL = 0xEF, + VK_OEM_ATTN = 0xF0, + VK_OEM_FINNISH = 0xF1, + VK_OEM_COPY = 0xF2, + VK_OEM_AUTO = 0xF3, + VK_OEM_ENLW = 0xF4, + VK_OEM_BACKTAB = 0xF5, + VK_ATTN = 0xF6, + VK_CRSEL = 0xF7, + VK_EXSEL = 0xF8, + VK_EREOF = 0xF9, + VK_PLAY = 0xFA, + VK_ZOOM = 0xFB, + VK_NONAME = 0xFC, + VK_PA1 = 0xFD, + VK_OEM_CLEAR = 0xFE, +} + +[Flags] +public enum SC : uint +{ + // 窗体关闭消息 + SC_CLOSE = 0xf060, + // 窗体最小化消息 + SC_MINIMIZE = 0xf020, + // 窗体最大化消息 + SC_MAXIMIZE = 0xf030, + // 窗体正常态消息 SC_RESTORE = 0xf120, + SC_NOMAL = 0xf120, +} + +[Flags] +public enum NCmdShow : uint +{ + /// + /// 隐藏窗口并激活其他窗口。nCmdShow + /// + SW_HIDE = 0, + /// + /// 正常态的窗口(非最大化和非最小化) + /// 激活并显示一个窗口。如果窗口被最小化或最大化,系统将其恢复到原来的尺寸和大小。应用程序在第一次显示窗口的时候应该指定此标志 + /// + SW_SHOWNORMAL = 1, + /// + /// 激活窗口并将其最小化 + /// + SW_SHOWMINIMIZED = 2, + /// + /// 激活窗口并将其最大化 + /// + SW_SHOWMAXIMIZED = 3, + /// + /// 最大化指定的窗口 + /// + SW_MAXIMIZE = 3, + /// + /// 以窗口最近一次的大小和状态显示窗口。激活窗口仍然维持激活状态 + /// + SW_SHOWNOACTIVATE = 4, + /// + /// 在窗口原来的位置以原来的尺寸激活和显示窗口 + /// + SW_SHOW = 5, + /// + /// 最小化指定的窗口并且激活在Z序中的下一个顶层窗口 + /// + SW_MINIMIZE = 6, + /// + /// 窗口最小化,激活窗口仍然维持激活状态 + /// + SW_SHOWMINNOACTIVE = 7, + /// + /// 以窗口原来的状态显示窗口。激活窗口仍然维持激活状态 + /// + SW_SHOWNA = 8, + /// + /// 激活并显示窗口。如果窗口最小化或最大化,则系统将窗口恢复到原来的尺寸和位置。在恢复最小化窗口时,应用程序应该指定这个标志 + /// + SW_RESTORE = 9, + /// + /// 依据在STARTUPINFO结构中指定的SW_FLAG标志设定显示状态,STARTUPINFO 结构是由启动应用程序的程序传递给CreateProcess函数的 + /// + SW_SHOWDEFAULT = 10, + /// + /// 在WindowNT5.0中最小化窗口,即使拥有窗口的线程被挂起也会最小化。在从其他线程最小化窗口时才使用这个参数 + /// + SW_FORCEMINIMIZE = 11, +} + +public enum WS : uint +{ + // 窗口风格 + WS_CAPTION = 0xC00000, // 带标题栏的窗口 + WS_MAXIMIZEBOX = 0x10000, // 带最大化按钮的窗口 + WS_MINIMIZEBOX = 0x20000, // 带最小化按钮的窗口 + WS_SYSMENU = 0x80000, // 带系统菜单的窗口 + WS_CLIPSIBLINGS = 0x4000000, // 不重绘层叠子窗口 + WS_CLIPCHILDREN = 0x2000000, // 绘图时排子窗口区域 + WS_OVERLAPPED = 0x0, // 具有标题栏和边框的层叠窗口 + WS_THICKFRAME = 0x40000, // 具有可调边框 + + // 具有标题栏、窗口菜单、可调边框和最大化、最小化按钮的窗口 + WS_OVERLAPPEDWINDOW = WS_OVERLAPPED | WS_CAPTION | WS_SYSMENU | WS_THICKFRAME | WS_MINIMIZEBOX | WS_MAXIMIZEBOX, + + WS_GROUP = 0x20000, // 指定一组控制的第一个控制 + WS_POPUP = 0x80000000, // 弹出式窗口 + WS_BORDER = 0x800000, // 单边框窗口 + WS_POPUPWINDOW = WS_POPUP | WS_BORDER | WS_SYSMENU, // 具有单边框、标题栏菜单的弹出式窗口 + WS_MINIMIZE = 0x20000000, // 窗口最小化 + WS_VISIBLE = 0x10000000, // 窗口可见 + WS_DISABLED = 0x8000000, // 窗口被禁用 + WS_MAXIMIZE = 0x1000000, // 窗口最大化 + WS_DLGFRAME = 0x400000, // 对话框边框风格 + WS_VSCROLL = 0x200000, // 具有垂直滚动条 + WS_HSCROLL = 0x100000, // 具有水平滚动条 + WS_TABSTOP = 0x10000, // 具有TAB键控制 + WS_CHILD = 0x40000000, // 设置窗口属性为child 多文档界面的子窗体 + WS_CHILDWINDOW = WS_CHILD, // 具有子窗口 + + // 扩展风格 + WS_EX_WINDOWEDGE = 0x100, // 窗口具有凸起的3D边框 + WS_EX_CLIENTEDGE = 0x200, // 窗口具有阴影边界 + WS_EX_TOOLWINDOW = 0x80, // 小标题工具窗口 + WS_EX_TOPMOST = 0x8, // 窗口总在顶层 const int WS_EX_TOPMOST = 0x00000008; + WS_EX_OVERLAPPEDWINDOW = WS_EX_WINDOWEDGE | WS_EX_CLIENTEDGE, // WS_EX-CLIENTEDGE和WS_EX_WINDOWEDGE的组合 + WS_EX_PALETTEWINDOW = WS_EX_WINDOWEDGE | WS_EX_TOOLWINDOW | WS_EX_TOPMOST, // WS_EX_WINDOWEDGE和WS_EX_TOOLWINDOW和WS_EX_TOPMOST的组合 + WS_EX_DLGMODALFRAME = 0x1, // 带双边的窗口 + WS_EX_NOPARENTNOTIFY = 0x4, // 窗口在创建和销毁时不向父窗口发送WM_PARENTNOTIFY消息 + WS_EX_TRANSPARENT = 0x20, // 窗口透眀 + WS_EX_MDICHILD = 0x40, // MDI子窗口 + WS_EX_CONTEXTHELP = 0x400, // 标题栏包含问号联机帮助按钮 + WS_EX_RIGHT = 0x1000, // 窗口具有右对齐属性 + WS_EX_RTLREADING = 0x2000, // 窗口文本自右向左显示 + WS_EX_LEFTSCROLLBAR = 0x4000, // 标题栏在客户区的左边 + WS_EX_CONTROLPARENT = 0x10000, // 允许用户使用Tab键在窗口的子窗口间搜索 + WS_EX_STATICEDGE = 0x20000, // 为不接受用户输入的项创建一个三维边界风格 + WS_EX_APPWINDOW = 0x40000, // 在任务栏上显示顶层窗口的标题按钮 + WS_EX_LAYERED = 0x80000, // 窗口具有透眀属性(Win2000)以上 + WS_EX_NOINHERITLAYOUT = 0x100000, // 窗口布局不传递给子窗口(Win2000)以上 + WS_EX_LAYOUTRTL = 0x400000, // 水平起点在右边的窗口 + WS_EX_NOACTIVATE = 0x8000000, // 窗口不会变成前台窗口(Win2000)以上 + WS_EX_LEFT = 0x0, // 窗口具有左对齐属性 + WS_EX_LTRREADING = 0x0, // 窗口文本自左向右显示 + WS_EX_RIGHTSCROLLBAR = 0x0, // 垂直滚动条在窗口的右边界 + WS_EX_ACCEPTFILES = 0x10, // 接受文件拖曳 + WS_EX_COMPOSITED = 0x2000000, // 窗体所有子窗口使用双缓冲从低到高绘制(XP) +} + +public enum GWL : int +{ + /// + /// 获取、设置窗口过程的地址 + /// + GWL_WNDPROC = -4, + /// + /// 获取应用程序的实例句柄 + /// + GWL_HINSTANCE = -6, + /// + /// 获取父窗口句柄 + /// + GWL_HWNDPARENT = -8, + /// + /// 获取窗口标识 + /// + GWL_ID = -12, + /// + /// 获取、设置窗口样式 + /// + GWL_STYLE = -16, + /// + /// 获取、设置窗口扩展样式 + /// + GWL_EXSTYLE = -20, + /// + /// 获取、设置与窗口关联的自定义数据 + /// + GWL_USERDATA = -21, +} + +public enum GetWindowCmd : uint +{ + /// + /// 返回的句柄标识了在Z序最高端的相同类型的窗口。 + /// 如果指定窗口是最高端窗口,则该句柄标识了在Z序最高端的最高端窗口; + /// 如果指定窗口是顶层窗口,则该句柄标识了在z序最高端的顶层窗口: + /// 如果指定窗口是子窗口,则句柄标识了在Z序最高端的同属窗口。 + /// + GW_HWNDFIRST = 0, + /// + /// 返回的句柄标识了在z序最低端的相同类型的窗口。 + /// 如果指定窗口是最高端窗口,则该柄标识了在z序最低端的最高端窗口: + /// 如果指定窗口是顶层窗口,则该句柄标识了在z序最低端的顶层窗口; + /// 如果指定窗口是子窗口,则句柄标识了在Z序最低端的同属窗口。 + /// + GW_HWNDLAST = 1, + /// + /// 返回的句柄标识了在Z序中指定窗口下的相同类型的窗口。 + /// 如果指定窗口是最高端窗口,则该句柄标识了在指定窗口下的最高端窗口: + /// 如果指定窗口是顶层窗口,则该句柄标识了在指定窗口下的顶层窗口; + /// 如果指定窗口是子窗口,则句柄标识了在指定窗口下的同属窗口。 + /// + GW_HWNDNEXT = 2, + /// + /// 返回的句柄标识了在Z序中指定窗口上的相同类型的窗口。 + /// 如果指定窗口是最高端窗口,则该句柄标识了在指定窗口上的最高端窗口; + /// 如果指定窗口是顶层窗口,则该句柄标识了在指定窗口上的顶层窗口; + /// 如果指定窗口是子窗口,则句柄标识了在指定窗口上的同属窗口。 + /// + GW_HWNDPREV = 3, + /// + /// 返回的句柄标识了指定窗口的所有者窗口(如果存在)。 + /// GW_OWNER与GW_CHILD不是相对的参数,没有父窗口的含义,如果想得到父窗口请使用GetParent()。 + /// 例如:例如有时对话框的控件的GW_OWNER,是不存在的。 + /// + GW_OWNER = 4, + /// + /// 如果指定窗口是父窗口,则获得的是在Tab序顶端的子窗口的句柄,否则为NULL。 + /// 函数仅检查指定父窗口的子窗口,不检查继承窗口。 + /// + GW_CHILD = 5, + /// + /// (WindowsNT 5.0)返回的句柄标识了属于指定窗口的处于使能状态弹出式窗口(检索使用第一个由GW_HWNDNEXT 查找到的满足前述条件的窗口); + /// 如果无使能窗口,则获得的句柄与指定窗口相同。 + /// + GW_ENABLEDPOPUP = 6 +} +#endif \ No newline at end of file diff --git a/src/IFoxCAD.Cad.Shared/WindowsAPI/MouseHook.cs b/src/IFoxCAD.Cad.Shared/WindowsAPI/MouseHook.cs new file mode 100644 index 0000000..2d6b013 --- /dev/null +++ b/src/IFoxCAD.Cad.Shared/WindowsAPI/MouseHook.cs @@ -0,0 +1,293 @@ +namespace IFoxCAD.Cad; + +using System; +using System.Diagnostics; +using System.Drawing; +using System.Runtime.InteropServices; +using System.Windows.Forms; + +public class MouseHook +{ + /// + /// 鼠标按下事件 + /// + public event MouseEventHandler? MouseDown; + /// + /// 松开鼠标事件 + /// + public event MouseEventHandler? MouseUp; + /// + /// 鼠标移动事件 + /// + public event MouseEventHandler? MouseMove; + /// + /// 鼠标滚轮事件 + /// + public event MouseEventHandler? MouseWheel; + /// + /// 鼠标单击事件 + /// + public event EventHandler? Click; + /// + /// 鼠标双击事件 + /// + public event EventHandler? DoubleClick; + + + bool _isHookBreak = false; + /// + /// 否决本次输入:设置不向下回调 + /// + public void Vote() + { + _isHookBreak = true; + } + + /// 不要试图省略此变量,否则将会导致GC变量池满后释放
    + /// 提示:激活 CallbackOnCollectedDelegate 托管调试助手(MDA) + internal static WindowsAPI.CallBack? HookProc; + internal static IntPtr _NextHookProc;//挂载成功的标记 + public readonly Process Process; + + + [DllImport("user32.dll", EntryPoint = "GetDoubleClickTime")] + public extern static int GetDoubleClickTime(); + static readonly Stopwatch _watch = new(); + + /// + /// 安装鼠标钩子 + /// + /// 低级钩子超时时间 + public MouseHook(int setLowLevel = 25000) + { + _NextHookProc = IntPtr.Zero; + Process = Process.GetCurrentProcess(); + WindowsAPI.CheckLowLevelHooksTimeout(setLowLevel); + _watch.Start(); + } + + void UnHook() + { + if (_NextHookProc != IntPtr.Zero) + { + WindowsAPI.UnhookWindowsHookEx(_NextHookProc); + _NextHookProc = IntPtr.Zero; + } + } + + /// + /// 设置钩子 + /// + /// false进程钩子,true全局钩子 + public void SetHook(bool processHook = false) + { + UnHook(); + if (_NextHookProc != IntPtr.Zero) + return; + + if (processHook) + { + HookProc = (nCode, wParam, lParam) => { + if (nCode >= 0 && HookTask(nCode, wParam, lParam)) + return (IntPtr)1; + return WindowsAPI.CallNextHookEx(_NextHookProc, nCode, wParam, lParam); + }; + _NextHookProc = WindowsAPI.SetWindowsHookEx(HookType.WH_MOUSE, HookProc, + IntPtr.Zero, WindowsAPI.GetCurrentThreadId()); + } + else + { + var moduleHandle = WindowsAPI.GetModuleHandle(Process.MainModule.ModuleName); + HookProc = (nCode, wParam, lParam) => { + if (nCode >= 0 && HookTask(nCode, wParam, lParam)) + return (IntPtr)1; + return WindowsAPI.CallNextHookEx(_NextHookProc, nCode, wParam, lParam); + }; + _NextHookProc = WindowsAPI.SetWindowsHookEx(HookType.WH_MOUSE_LL, HookProc, + moduleHandle, 0); + } + } + + + + MouseButtons _button; + int _clickCount = 0; + bool _down = false; + bool _up = false; + bool _ck = false; + + /// + /// 钩子的消息处理 + /// + /// + /// + /// + /// false不终止回调,true终止回调 + bool HookTask(int nCode, int wParam, IntPtr lParam) + { + if (MouseDown is null + && MouseUp is null + && MouseMove is null + && MouseWheel is null + && Click is null + && DoubleClick is null) + return false; + + _button = MouseButtons.None; + _clickCount = 0; + _down = false; + _up = false; + _ck = false; + + switch ((WM)wParam) + { + case WM.WM_LBUTTONDOWN: + _button = MouseButtons.Left; + _clickCount = 1; + _down = true; + _ck = true; + break; + case WM.WM_LBUTTONUP: + _button = MouseButtons.Left; + _clickCount = 1; + _up = true; + break; + case WM.WM_LBUTTONDBLCLK: + _button = MouseButtons.Left; + _clickCount = 2; + _ck = true; + break; + case WM.WM_RBUTTONDOWN: + _button = MouseButtons.Right; + _clickCount = 1; + _down = true; + _ck = true; + break; + case WM.WM_RBUTTONUP: + _button = MouseButtons.Right; + _clickCount = 1; + _up = true; + break; + case WM.WM_RBUTTONDBLCLK: + _button = MouseButtons.Right; + _clickCount = 2; + _ck = true; + break; + case WM.WM_MBUTTONDOWN: + _button = MouseButtons.Middle; + _clickCount = 1; + _ck = true; + break; + case WM.WM_MOUSEWHEEL: + // 滚轮 + break; + case WM.WM_MOUSEMOVE: + // 移动 + // 假设想要限制鼠标在屏幕中的移动区域能够在此处设置 + // 后期须要考虑实际的x y的容差 + // if (!Screen.PrimaryScreen.Bounds.Contains(e.X, e.Y)) + // // return 1; + // if (button == MouseButtons.Left) + // { + // GetCursorPos(out POINT pt); + // // 防止频繁获取导致出错 + // if (pt0ld.Leng(pt) > 20) + // pt0ld = pt; + // } + break; + } + + // 从回调函数中得到鼠标的信息 + var mouseMsg = MouseHookStruct.Create(lParam); + MouseEventArgs e = new(_button, _clickCount, mouseMsg.Point.X, mouseMsg.Point.Y, 0); + if (_down) + MouseDown?.Invoke(this, e); + if (_up) + MouseUp?.Invoke(this, e); + if (_ck) + Click?.Invoke(this, e); + if (_clickCount == 2) + { + // 如果不用时间控制,那么双击会执行两次 + if (_watch.Elapsed.TotalMilliseconds > GetDoubleClickTime()) + { + DoubleClick?.Invoke(this, e); + _watch.Reset(); + _watch.Start(); + } + } + MouseMove?.Invoke(this, e); + MouseWheel?.Invoke(this, e); + + // 屏蔽此输入 + if (_isHookBreak) + return true; + + return false; + } + + + /// + /// Hook鼠标数据结构 + /// + [StructLayout(LayoutKind.Sequential)] + public struct MouseHookStruct + { + /// + /// 鼠标在屏幕上的x,y坐标 + /// + public Point Point; + /// + /// 点击窗体的句柄 + /// + public IntPtr hWnd; + /// + /// 消息 + /// + public int wHitTestCode; + /// + /// 扩展信息,可以使用GetMessageExtraInfo的返回值 + /// + public int dwExtraInfo; + + public static MouseHookStruct Create(IntPtr lParam) + { + return (MouseHookStruct)Marshal.PtrToStructure(lParam, typeof(MouseHookStruct)); + } + + public void ToPtr(IntPtr lParam) + { + Marshal.StructureToPtr(this, lParam, true); + } + } + + + #region IDisposable接口相关函数 + public bool IsDisposed { get; private set; } = false; + + /// + /// 手动调用释放 + /// + public void Dispose() + { + Dispose(true); + GC.SuppressFinalize(this); + } + + /// + /// 析构函数调用释放 + /// + ~MouseHook() + { + Dispose(false); + } + + protected virtual void Dispose(bool disposing) + { + // 不重复释放,并设置已经释放 + if (IsDisposed) return; + IsDisposed = true; + UnHook(); + } + #endregion +} \ No newline at end of file diff --git a/src/IFoxCAD.Cad.Shared/Copyclip/WindowsAPI.cs b/src/IFoxCAD.Cad.Shared/WindowsAPI/WindowsAPI.cs similarity index 98% rename from src/IFoxCAD.Cad.Shared/Copyclip/WindowsAPI.cs rename to src/IFoxCAD.Cad.Shared/WindowsAPI/WindowsAPI.cs index 069e074..b1a7f92 100644 --- a/src/IFoxCAD.Cad.Shared/Copyclip/WindowsAPI.cs +++ b/src/IFoxCAD.Cad.Shared/WindowsAPI/WindowsAPI.cs @@ -1,12 +1,9 @@ #define Marshal -namespace IFoxCAD.Cad; - -using System; using System.Diagnostics; using System.Runtime.CompilerServices; using System.Threading; -using System.Windows.Forms; +namespace IFoxCAD.Cad; public partial class WindowsAPI { #region kernel32 @@ -459,12 +456,6 @@ public static GuiThreadInfo Create(uint windowThreadProcessId) public static extern IntPtr UnhookWindowsHookEx(IntPtr hHook); [DllImport("user32.dll")] public static extern IntPtr CallNextHookEx(IntPtr hHook, int ncode, int wParam, IntPtr lParam); - public enum HookType : byte - { - WH_KEYBOARD = 2, - WH_KEYBOARD_LL = 13, - WH_MOUSE_LL = 14, - } /// /// Hook键盘数据结构 /// @@ -495,17 +486,16 @@ public void ToPtr(IntPtr lParam) /// 否则:偶发性出现 键盘钩子不能用了,而且退出时产生 1404 错误 /// https://www.cnblogs.com/songr/p/5131655.html ///
    - public static void CheckLowLevelHooksTimeout() + public static void CheckLowLevelHooksTimeout(int setLowLevel = 25000) { const string llh = "LowLevelHooksTimeout"; using var registryKey = Registry.CurrentUser.OpenSubKey("Control Panel\\Desktop", true); - if ((int)registryKey.GetValue(llh, 0) == 0) - registryKey.SetValue(llh, 25000, RegistryValueKind.DWord); + if ((int)registryKey.GetValue(llh, 0) < setLowLevel) + registryKey.SetValue(llh, setLowLevel, RegistryValueKind.DWord); } #endregion } - public partial class WindowsAPI { [DllImport("user32.dll")] diff --git "a/tests/TestAcad08/\346\213\211\344\274\270\345\241\253\345\205\205/01.\346\213\211\344\274\270\345\241\253\345\205\205\345\217\263\351\224\256\350\217\234\345\215\225.cs" "b/tests/TestAcad08/\346\213\211\344\274\270\345\241\253\345\205\205/01.\346\213\211\344\274\270\345\241\253\345\205\205\345\217\263\351\224\256\350\217\234\345\215\225.cs" new file mode 100644 index 0000000..6afeef7 --- /dev/null +++ "b/tests/TestAcad08/\346\213\211\344\274\270\345\241\253\345\205\205/01.\346\213\211\344\274\270\345\241\253\345\205\205\345\217\263\351\224\256\350\217\234\345\215\225.cs" @@ -0,0 +1,184 @@ +using Autodesk.AutoCAD.Windows; +using System.Diagnostics; +using static IFoxCAD.Cad.PostCmd; +using MenuItem = Autodesk.AutoCAD.Windows.MenuItem; + +namespace JoinBoxAcad; +public class HatchPick +{ + [IFoxInitialize] + [CommandMethod(nameof(HatchPickInit))] + public void HatchPickInit() + { + Env.Printl($"※拉伸填充控制※\n{nameof(HatchPickSwitch)} - 切换开关\n"); + + if (Debugger.IsAttached) + Env.SetVar("hpscale", 22); + // 设定高版本双击填充启动修改面板 + // JoinBoxAcad.Menu.Cui.CuiInit(); + LoadHelper(true); + } + + // 只能命令卸载哦,因为关闭cad是不需要卸载的 + [CommandMethod(nameof(UnLoadHatchPick))] + public void UnLoadHatchPick() + { + LoadHelper(false); + } + + [CommandMethod(nameof(HatchPickSwitch))] + public void HatchPickSwitch() + { + if (HatchPickEvent.State.IsStop) + { + Env.Printl("已经 卸载 拉伸填充控制+ 用: " + nameof(HatchPickInit) + " 加载"); + return; + } + + if (HatchPickEvent.State.IsRun) + HatchPickEvent.State.Break(); + else + HatchPickEvent.State.Start(); + Env.Printl("已经 " + (HatchPickEvent.State.IsRun ? "开启" : "禁用") + " 拉伸填充控制+"); + } + + + internal static Dictionary MapDocHatchPickEvent = new(); + void LoadHelper(bool isLoad) + { + var dm = Acap.DocumentManager; + if (dm is null || dm.Count == 0) + return; + if (isLoad) + { + dm.DocumentCreated += Dm_DocumentCreated; + Dm_DocumentCreated(); // 自执行一次 + AddRightClickMenu(); + HatchPickEvent.AddInit(); + } + else + { + HatchPickEvent.RemoveInit(); + dm.DocumentCreated -= Dm_DocumentCreated; + UnDocumentCreated(); + HatchPick.RemoveRightClickMenu(); + } + } + + /// + /// 文档创建反应器 + /// + void Dm_DocumentCreated(object? sender = null, DocumentCollectionEventArgs? e = null) + { + var dm = Acap.DocumentManager; + if (dm is null || dm.Count == 0) + return; + var doc = dm.MdiActiveDocument; + if (doc is null) + return; + if (!MapDocHatchPickEvent.ContainsKey(doc)) + MapDocHatchPickEvent.Add(doc, new HatchPickEvent(doc)); + } + + /// + /// 卸载文档创建反应器 + /// + static void UnDocumentCreated() + { + var dm = Acap.DocumentManager; + if (dm is null || dm.Count == 0) + return; + var doc = dm.MdiActiveDocument; + if (doc is null) + return; + if (MapDocHatchPickEvent.ContainsKey(doc)) + { + MapDocHatchPickEvent[doc].Dispose(); + MapDocHatchPickEvent.Remove(doc); + } + } + + + + private const string V0 = "拉伸填充-开"; + private const string V1 = "拉伸填充-关";// (面板的独立填充必须关,否则致命错误) + private const string V2 = "独立填充";//(快捷,不需要关...目前还是会崩溃) + static readonly HashSet _menuItems = new() { V0, V1, V2 }; + static readonly ContextMenuExtension _contextMenu = new() { Title = "惊惊盒子" }; + /// + /// 添加右键菜单 + /// + void AddRightClickMenu() + { + // 右键菜单 + foreach (var item in _menuItems) + { + MenuItem mi = new(item); // 添加菜单项 + mi.Click += MenuItemClick; // 添加单击事件 + + //mi.MenuItems.Add(new MenuItem("改颜色1")); // 二级菜单 + _contextMenu.MenuItems.Add(mi); // 提交 + } + Acap.AddDefaultContextMenuExtension(_contextMenu);// 添加默认上下文菜单扩展,带标题的 + + //加入到某一种对象的右键菜单中 + //RXClass rxClass = Entity.GetClass(typeof(BlockReference)); + //Acap.AddObjectContextMenuExtension(rxClass, contextMenu); + //// 选择实体右键菜单才有用. 获得实体所属的RXClass类型 + // RXClass rx = RXObject.GetClass(typeof(Entity)); + // Acap.AddObjectContextMenuExtension(rx, contextMenu); // 这里为什么又可以不带标题 + } + + /// + /// 卸载右键菜单 + /// + static void RemoveRightClickMenu() + { + if (_contextMenu is null) + return; + Acap.RemoveDefaultContextMenuExtension(_contextMenu); + } + + /// + /// 右键点击触发 + /// + /// + /// + void MenuItemClick(object sender, EventArgs e) + { + // 获取发出命令的快捷菜单项 + if (sender is not MenuItem mi) + return; + + // 根据快捷菜单项的名字,分别调用对应的命令 + if (!_menuItems.Contains(mi.Text)) + return; + + switch (mi.Text) + { + case V0: + HatchPickEvent.State.Start(); + break; + case V1: + HatchPickEvent.State.Break(); + break; + case V2: + { + HatchPickEvent.State.Break(); + PromptSelectionOptions pso = new() + { + AllowDuplicates = true, // 不允许重复选择 + SingleOnly = true, // 隐含窗口选择(不需要空格确认) + }; + var ssPsr = Env.Editor.GetSelection(pso, HatchPickEvent.FilterForHatch); + if (ssPsr.Status != PromptStatus.OK) + return; + + Env.Editor.SetImpliedSelection(ssPsr.Value.GetObjectIds()); + SendCommand("-hatchedit H ", RunCmdFlag.AcedPostCommand); + HatchPickEvent.State.Start(); + } + break; + } + } +} \ No newline at end of file diff --git "a/tests/TestAcad08/\346\213\211\344\274\270\345\241\253\345\205\205/02.\346\213\211\344\274\270\345\241\253\345\205\205\344\272\213\344\273\266.cs" "b/tests/TestAcad08/\346\213\211\344\274\270\345\241\253\345\205\205/02.\346\213\211\344\274\270\345\241\253\345\205\205\344\272\213\344\273\266.cs" new file mode 100644 index 0000000..af84682 --- /dev/null +++ "b/tests/TestAcad08/\346\213\211\344\274\270\345\241\253\345\205\205/02.\346\213\211\344\274\270\345\241\253\345\205\205\344\272\213\344\273\266.cs" @@ -0,0 +1,1029 @@ +using System.Drawing; +using System.Runtime.CompilerServices; +using System.Windows.Forms; +using static IFoxCAD.Cad.PostCmd; + +namespace JoinBoxAcad; + +public class HatchPickEvent : IDisposable +{ + #region 静态成员 + public static ProState State = new(); + // 选择集过滤器 + public static readonly SelectionFilter FilterForHatch = new(new TypedValue[] { new TypedValue((int)DxfCode.Start, "HATCH") }); + // 临时标记(重设选择集会触发一次选择集反应器) + static bool _selectChangedStop = false; + // 临时选择集用 + static List _hatchIds = new(); + // 获取夹点在哪个图元边界上面,是为true + static bool _pickInBo = false; + static bool _vetoProperties = false; + private Tolerance _tol = new(1e-6, 1e-6); + + public static void AddInit() + { + HatchHook.SetHook(); + // 全局事件重复+=是不需要担心的 + Acap.DocumentManager.DocumentLockModeChanged += Dm_VetoCommand; + State.Start(); + } + + public static void RemoveInit() + { + State.Stop(); + Acap.DocumentManager.DocumentLockModeChanged -= Dm_VetoCommand; + HatchHook.RemoveHook(); + } + + /// + /// 反应器->命令否决触发命令前(不可锁文档) + /// + /// + /// + public static void Dm_VetoCommand(object sender, DocumentLockModeChangedEventArgs e) + { + if (!State.IsRun) + return; + if (string.IsNullOrEmpty(e.GlobalCommandName) || e.GlobalCommandName == "#") + return; + switch (e.GlobalCommandName.ToUpper()) + { + case "PROPERTIES": // 特性面板 + { + // 事件顺序问题: + // 开cad之后第一次双击必弹出特性面板 + // 所以这里直接删除填充边界 + HatchPick.MapDocHatchPickEvent[e.Document].SetPropertiesInfoTask(); + if (_vetoProperties) + { + Debugx.Printl("Dm_VetoCommand 否决了"); + e.Veto(); + _vetoProperties = false; + // 发送编辑填充命令 + SendCommand("_hatchedit ", RunCmdFlag.AcedPostCommand); + return; + } + Debugx.Printl("Dm_VetoCommand 没否决"); + } + break; + } + } + + + #endregion + + #region 动态成员 + /// + /// 在位编辑器 记录全图选择集的填充 + /// + readonly HashSet _refeditSsgeting = new(); + /// + /// 在位编辑器 获取当前选择集做差集=>内部填充 + /// + readonly HashSet _refeditSsgeted = new(); + // map<填充id,边界转换器> + readonly Dictionary _mapHatchConv = new(); + readonly Document _doc; + public HatchPickEvent(Document doc) + { + _doc = doc; + LoadHelper(true); + } + + void LoadHelper(bool isLoad) + { + if (isLoad) + { + _doc.ImpliedSelectionChanged += Md_ImpliedSelectionChanged; + _doc.CommandWillStart += Md_CommandWillStart; + _doc.LispWillStart += Md_LispWillStart; + _doc.CommandEnded += Md_CommandEnded; + _doc.Database.ObjectErased += DB_ObjectErased; + _doc.Database.ObjectModified += DB_ObjectModified; + } + else + { + _doc.ImpliedSelectionChanged -= Md_ImpliedSelectionChanged; + _doc.CommandWillStart -= Md_CommandWillStart; + _doc.LispWillStart -= Md_LispWillStart; + _doc.CommandEnded -= Md_CommandEnded; + _doc.Database.ObjectErased -= DB_ObjectErased; + _doc.Database.ObjectModified -= DB_ObjectModified; + } + } + #endregion + + #region 事件 + /// + /// 反应器->command命令完成前 + /// + /// + /// + void Md_CommandWillStart(object sender, CommandEventArgs e) + { + if (!State.IsRun) + return; + + // 此处无法使用文档锁,否则将导致文档锁无法释放,然后ctrl+z失败 + var cmdup = e.GlobalCommandName.ToUpper(); + Debugx.Printl("Md_CommandWillStart::" + cmdup); + + switch (cmdup) + { + case "REFEDIT": + { + // 在位编辑命令,执行前,获取当前空间所有填充 + var prompt = Env.Editor.SelectAll(FilterForHatch); + if (prompt.Status != PromptStatus.OK) + return; + using DBTrans tr = new(); + GetHatchIds(prompt); + if (_hatchIds.Count == 0) + return; + for (int i = 0; i < _hatchIds.Count; i++) + _refeditSsgeting.Add(_hatchIds[i]); + } + break; + } + + // 拉伸夹点命令前触发 + if (cmdup != "GRIP_STRETCH") + { + EraseAllHatchBorders(); + } + else + { + var mp = HatchHook.MouseStartPoint; + var mouseStart = Screen.ScreenToCad(mp); + Debugx.Printl("mouseStart,屏幕点::" + mp); + Debugx.Printl("mouseStart,cad点::" + mouseStart); + + // 获取当前选择的对象,然后提取所有的夹点 + var prompt = Env.Editor.SelectImplied(); + if (prompt.Status != PromptStatus.OK) + return; + using DBTrans tr = new(); + GetHatchIds(prompt); + if (_hatchIds.Count == 0) + return; + + // TODO 屏幕像素点转cad点的误差,要随着视口高度而动态计算....这里的计算可能不太正确 + var tol = (double)Env.GetVar("viewsize") / 10; + Debugx.Printl("tol::" + tol); + + // 0x01 移动了矩形填充中间的夹点,删除边界,并且重新生成填充和边界 + // 0x02 移动了填充边界上的夹点,不处理,然后它会通过关联进行自己修改 + _pickInBo = false; + for (int i = 0; i < _hatchIds.Count; i++) + { + var hatId = _hatchIds[i]; + if (!_mapHatchConv.ContainsKey(hatId)) + continue; + + _mapHatchConv[hatId].BoundaryIds.ForEach((id, idState) => { + var boEnt = tr.GetObject(id); + if (boEnt == null) + return; + + // 获取夹点在哪个图元边界上 + HashSet boPts = new(); + if (boEnt is Circle circle) + { + // 圆形的边界夹点是: 圆心+半径 + var x = circle.Center.X; + var y = circle.Center.Y; + var z = circle.Center.Z; + var r = circle.Radius; + boPts.Add(new(x + r, y, z));//上 + boPts.Add(new(x - r, y, z));//下 + boPts.Add(new(x, y - r, z));//左 + boPts.Add(new(x, y + r, z));//右 + } + else + { + // 获取所有的边点 + // 这里圆形会获取圆心,所以剔除圆形 + var tmp = GetEntityPoint3ds(boEnt); + for (int j = 0; j < tmp.Count; j++) + boPts.Add(tmp[j]); + } + + if (boEnt is Arc arc) + { + if (!arc.StartPoint.IsEqualTo(arc.EndPoint, _tol)) + { + // 圆弧的腰点 + var arc2 = arc.GetPointAtDist(arc.GetDistAtPoint(arc.EndPoint) * 0.5); + boPts.Add(arc2); + } + } + else if (boEnt is Polyline pl) + { + for (int j = 0; j < pl.NumberOfVertices; j++) + { + var bulge = pl.GetBulgeAt(j); + if (bulge == 0.0) + continue; + // 有凸度就是有每段的中点 + var pta = pl.GetPoint2dAt(j); + Point2d ptb; + if (j + 1 < pl.NumberOfVertices) + ptb = pl.GetPoint2dAt(j + 1); + else + ptb = pl.GetPoint2dAt(0); + + var p = MathHelper.GetArcMidPoint(pta, ptb, bulge); + boPts.Add(p.Point3d()); + } + } + + boPts.ForEach((pt, ptState) => { + var dist = pt.DistanceTo(mouseStart); + //Debugx.Printl("pt::" + pt + " dist::" + dist); + if (dist < tol) + { + ptState.Break(); + _pickInBo = true; + } + }); + }); + + // 点在边界上:就不处理了,它会通过cad的关联填充反应器自动修改 + if (_pickInBo) + Debugx.Printl("夹点在边界上"); + else + Debugx.Printl("夹点不在边界上"); + } + } + } + + /// + /// 图元拉伸点 + /// + /// + /// + static List GetEntityPoint3ds(Entity ent) + { + var pts3d = new Point3dCollection(); + ent.GetStretchPoints(pts3d); + return pts3d.Cast().ToList(); + } + + + + + /// + /// 反应器->command命令完成后 + /// + /// + /// + void Md_CommandEnded(object sender, CommandEventArgs e) + { + if (!State.IsRun) + return; + + var cmdup = e.GlobalCommandName.ToUpper(); + switch (cmdup) + { + case "REFEDIT": + { + Debugx.Printl("Md_CommandEnded:: REFEDIT"); + + // 在位编辑命令,执行后,获取当前空间所有填充 + var prompt = Env.Editor.SelectAll(FilterForHatch); + if (prompt.Status != PromptStatus.OK) + return; + + using DBTrans tr = new(); + GetHatchIds(prompt); + if (_hatchIds.Count == 0) + return; + + for (int i = 0; i < _hatchIds.Count; i++) + if (!_refeditSsgeting.Contains(_hatchIds[i]))//Except + _refeditSsgeted.Add(_hatchIds[i]); + + var sb = new StringBuilder(); + foreach (var id in _refeditSsgeted) + sb.AppendLine(id.ToString()); + Env.Printl("块内填充id:" + sb.ToString()); + } + break; + case "REFSET": // 加减在位编辑图元 + { + Debugx.Printl("Md_CommandEnded:: REFSET"); + + // 命令历史的最后一行是:添加/删除 + var last = Env.GetVar("lastprompt").ToString(); + if (last is null) + return; + + // 完成后必然有上次选择集 + var prompt = Env.Editor.SelectPrevious(); + if (prompt.Status != PromptStatus.OK) + return; + using DBTrans tr = new(); + GetHatchIds(prompt); + if (_hatchIds.Count == 0) + return; + + // 就是因为无法遍历到在位编辑的块内图元,只能进行布尔运算 + if (last.Contains("添加") || last.Contains("Added"))// 中英文cad + { + for (int i = 0; i < _hatchIds.Count; i++) + { + _refeditSsgeting.Remove(_hatchIds[i]); + _refeditSsgeted.Add(_hatchIds[i]); + } + return; + } + if (last.Contains("删除") || last.Contains("Removed"))// 中英文cad + { + for (int i = 0; i < _hatchIds.Count; i++) + { + _refeditSsgeted.Remove(_hatchIds[i]); + _refeditSsgeting.Add(_hatchIds[i]); + } + return; + } + } + break; + case "REFCLOSE":// 保存块,清空集合 + { + Debugx.Printl("Md_CommandEnded:: REFCLOSE"); + _refeditSsgeted.Clear(); + _refeditSsgeting.Clear(); + } + break; + case "GRIP_STRETCH":// 拉伸夹点命令后触发 + { + // 夹点在边界上,退出 + if (_pickInBo) + return; + + // 夹点不在边界上: + // cad会平移填充,在这之后,我们删除填充边界,重建填充边界 + var prompt = Env.Editor.SelectImplied(); + if (prompt.Status != PromptStatus.OK) + return; + using DBTrans tr = new(); + GetHatchIds(prompt); + if (_hatchIds.Count == 0) + return; + + // 删除指定填充的边界,并清理关联反应器 + HashSet idsOfSsget = new(); + foreach (var hatId in _hatchIds) + { + idsOfSsget.Add(hatId); + + if (!_mapHatchConv.ContainsKey(hatId)) + continue; + bool clearFlag = false; + _mapHatchConv[hatId].BoundaryIds.ForEach(boId => { + if (!boId.IsOk()) + return; + var boEnt = tr.GetObject(boId); + if (boEnt == null) + return; + if (!HatchPickEnv.IsMeCreate(boEnt)) + return; + boId.Erase(); + clearFlag = true; + }); + + if (!clearFlag) + return; + + _mapHatchConv[hatId].BoundaryIds.Clear(); + + // 清理填充反应器 + var hatch = tr.GetObject(hatId); + if (hatch == null) + return; + using (hatch.ForWrite()) + RemoveAssociative(hatch); + CreatHatchConverter(hatch, idsOfSsget); + } + SetImpliedSelection(idsOfSsget); + } + break; + } + } + + /// + /// 获取选择集上的填充,在缓存内提取 + /// + /// + /// + static void GetHatchIds(PromptSelectionResult psr, DBTrans? tr = null) + { + tr ??= DBTrans.Top; + _hatchIds.Clear(); + var ids = psr.Value.GetObjectIds(); + for (int i = 0; i < ids.Length; i++) + { + var hatch = tr.GetObject(ids[i]); + if (hatch is not null) + _hatchIds.Add(ids[i]); + } + } + + /// + /// 反应器->lisp命令 + /// + void Md_LispWillStart(object sender, LispWillStartEventArgs e) + { + if (!State.IsRun) + return; + + using DBTrans tr = new(doclock: true); + EraseAllHatchBorders(); + } + + /// + /// 反应器->选择集 + /// + /// + /// + void Md_ImpliedSelectionChanged(object sender, EventArgs e) + { + if (!State.IsRun) + return; + + // 此处必须要文档锁 + if (_selectChangedStop) + { + _selectChangedStop = false; + return; + } + Debugx.Printl("Md_ImpliedSelectionChanged"); + + using DBTrans tr = new(doclock: true); + var prompt = Env.Editor.SelectImplied(); + if (prompt.Status != PromptStatus.OK) + { + EraseAllHatchBorders(); + return; + } + + // 获取图层锁定的记录,用于跳过 + Dictionary islocks = new(); + foreach (var layerRecord in tr.LayerTable.GetRecords()) + if (!layerRecord.IsErased)// 08符号表记录保留了这个 + islocks.Add(layerRecord.Name, layerRecord.IsLocked); + + // 遍历选择,创建边界转换器 + // 重设选择集 + HashSet idsOfSsget = new(); + foreach (var entId in prompt.Value.GetObjectIds()) + { + idsOfSsget.Add(entId); + var hatch = tr.GetObject(entId, openLockedLayer: true); + if (hatch is null) + continue; + if (islocks[hatch.Layer]) + continue; + // 重复选择 || 在位编辑外 + if (_mapHatchConv.ContainsKey(entId) || _refeditSsgeting.Contains(entId)) + continue; + CreatHatchConverter(hatch, idsOfSsget); + } + SetImpliedSelection(idsOfSsget); + } + + /// + /// 创建填充和填充边界转换器 + /// + /// + /// + /// + void CreatHatchConverter(Hatch hatch, HashSet outSsgetIds, DBTrans? tr = null) + { + tr ??= DBTrans.Top; + + var hc = new HatchConverter(hatch); + ObjectId newid; + + // 如果边界在图纸上没有删除(删除就不是关联的), + // 那就不创建新的,然后选中它们 + if (hc.BoundaryIds.Count != 0) + { + Debugx.Printl("CreatHatchConverter:: 加入了现有边界到选择集"); + + // 加入选择集 + foreach (var item in hc.BoundaryIds) + outSsgetIds.Add(item); + outSsgetIds.Add(hatch.ObjectId); + newid = hatch.ObjectId; + } + else + { + Debugx.Printl("CreatHatchConverter:: 创建新填充和边界"); + + // 创建新填充和边界 + hc.GetBoundarysData(); + newid = hc.CreateBoundarysAndHatchToMsPs(tr.CurrentSpace, trans: tr); + HatchPickEnv.SetMeXData(newid, hc.BoundaryIds); + + // 清理上次,删除边界和填充 + if (_mapHatchConv.ContainsKey(hatch.ObjectId)) + { + var boIds = _mapHatchConv[hatch.ObjectId].BoundaryIds; + for (int i = 0; i < boIds.Count; i++) + boIds[i].Erase(); + _mapHatchConv.Remove(hatch.ObjectId); + } + // 删除选中的 + hatch.ObjectId.Erase(); + } + + if (!_mapHatchConv.ContainsKey(newid)) + _mapHatchConv.Add(newid, hc); + else + _mapHatchConv[newid] = hc; + + if (newid == hatch.ObjectId) + return; + + // 优先: 块内含有旧的,就加入新的 + if (_refeditSsgeted.Contains(hatch.ObjectId)) + { + _refeditSsgeted.Remove(hatch.ObjectId); + _refeditSsgeted.Add(newid); + } + else if (_refeditSsgeting.Contains(hatch.ObjectId)) + { + _refeditSsgeting.Remove(hatch.ObjectId); + _refeditSsgeting.Add(newid); + } + } + + /// + /// 重设选择集 + /// + /// 加入选择集的成员 + void SetImpliedSelection(HashSet setImpSelect) + { + // 获取填充 + foreach (var id in _mapHatchConv.Keys) + setImpSelect.Add(id); + + // 获取填充边界 + foreach (var item in _mapHatchConv.Values) + foreach (var id in item.BoundaryIds) + setImpSelect.Add(id); + + // 设置选择集,没有标记的话会死循环 + _selectChangedStop = true; + Env.Editor.SetImpliedSelection(setImpSelect.ToArray()); + } + + /// + /// 删除全部填充边界 + /// + void EraseAllHatchBorders() + { + if (_mapHatchConv.Count == 0) + return; + foreach (var dict in _mapHatchConv) + { + dict.Value.BoundaryIds.ForEach(boId => { + if (!boId.IsOk()) + return; + using DBTrans tr = new(database: boId.Database); + var boEnt = tr.GetObject(boId, OpenMode.ForWrite); + if (boEnt == null) + return; + // 删除填充边界并清理关联反应器 + if (!HatchPickEnv.IsMeCreate(boEnt)) + return; + boEnt.Erase(); + if (dict.Key.IsOk()) + { + var hatch = tr.GetObject(dict.Key, OpenMode.ForWrite); + if (hatch == null) + return; + RemoveAssociative(hatch); + } + }); + } + _mapHatchConv.Clear(); + } + + /// + /// 移除关联反应器 + /// + /// + static void RemoveAssociative(Hatch hatch) + { + // 撤回填充,没有边界就移除关联反应器 + if (!hatch.Associative) + return; + + // 填充边界反应器 + var assIds = hatch.GetAssociatedObjectIds(); + if (assIds == null) + return; + bool isok = true; + foreach (ObjectId id in assIds) + { + if (!id.IsOk()) + { + isok = false; + break; + } + } + // 这里边界id已经删除了,所以移除会导致异常 + if (isok) + hatch.RemoveAssociatedObjectIds(); + // 取消关联反应器才能生成的正确 + hatch.Associative = false; + } + + /// + /// 撤回事件(获取删除对象) + /// + /// + /// + static void DB_ObjectErased(object sender, ObjectErasedEventArgs e) + { + if (!State.IsRun) + return; + + // object erased. + if (e.Erased) + { + return; + } + + // UNDO + if (e.DBObject is Hatch hatch) + { + if (HatchPickEnv.IsMeCreate(hatch)) + RemoveAssociative(hatch); + } + else if (e.DBObject is Entity boEnt) + { + // 撤回边界 + if (HatchPickEnv.IsMeCreate(boEnt)) + { + boEnt.Erase(); + // 通过xdata回溯填充,清理关联反应器 + if (boEnt.XData != null) + { + using DBTrans tr = new(); + var hatchId = HatchPickEnv.GetXdataHatch(boEnt); + if (hatchId.IsOk()) + { + var hatchEnt = tr.GetObject(hatchId, OpenMode.ForWrite); + if (hatchEnt != null) + RemoveAssociative(hatchEnt); + } + } + } + } + } + + /// + /// 撤回事件(更改时触发) + /// 它会获取有修改步骤的图元id + /// + /// + /// + static void DB_ObjectModified(object sender, ObjectEventArgs e) + { + if (!State.IsRun) + return; + + // 然后删除我制造的拉伸填充上面的关联反应器 + if (!e.DBObject.IsUndoing) + return; + if (e.DBObject.IsErased) + return; + //是我生成的填充才删除关联 + if (e.DBObject is Hatch hatch) + { + if (HatchPickEnv.IsMeCreate(hatch)) + RemoveAssociative(hatch); + } + } + + void SetPropertiesInfoTask() + { + // 原有选择集 + var prompt = Env.Editor.SelectImplied(); + if (prompt.Status != PromptStatus.OK) + return; + + using DBTrans tr = new(); + + // 获取记录的边界 + HashSet boAll = new(); + foreach (var hc in _mapHatchConv.Values) + foreach (var boid in hc.BoundaryIds) + boAll.Add(boid); + + // 获取选择集上面所有的填充,如果没有填充就结束(不屏蔽特性面板) + bool hasHatch = false; + HashSet idsOfSsget = new(); + foreach (var id in prompt.Value.GetObjectIds()) + { + // 含有填充 + if (_mapHatchConv.ContainsKey(id)) + hasHatch = true; + // 排除边界的加入 + if (!boAll.Contains(id)) + idsOfSsget.Add(id); + } + if (!hasHatch) + return; + + // 删除填充边界,并清理关联反应器 + EraseAllHatchBorders(); + + // 重设选择集 提供给后续命令判断 + SetImpliedSelection(idsOfSsget); + + // 如果有填充才否决 + _vetoProperties = idsOfSsget.Count != 0; + } + #endregion + + #region IDisposable接口相关函数 + public bool IsDisposed { get; private set; } = false; + + /// + /// 手动调用释放 + /// + public void Dispose() + { + Dispose(true); + GC.SuppressFinalize(this); + } + + /// + /// 析构函数调用释放 + /// + ~HatchPickEvent() + { + Dispose(false); + } + + protected virtual void Dispose(bool disposing) + { + // 不重复释放 + if (IsDisposed) return; + IsDisposed = true; + + if (_doc.IsDisposed) + return; + LoadHelper(false); + } + #endregion +} + +/// +/// 填充的鼠标钩子 +/// +public static class HatchHook +{ + static readonly MouseHook MouseHook; + // 夹点拉伸前的点,拉伸后利用命令后反应器去处理"GRIP_STRETCH" + static volatile int _X; + static volatile int _Y; + public static Point MouseStartPoint { get => new(_X, _Y); } + + /// + /// 鼠标双击事件 + /// + public static event EventHandler? DoubleClick; + + static HatchHook() + { + MouseHook = new(); + } + + /// + /// 查找主线程
    + /// 代替
    + /// 托管线程和他们不一样: + ///
    + /// 主窗口 + /// 进程ID + /// 线程ID + [DllImport("user32.dll", SetLastError = true)] + static extern uint GetWindowThreadProcessId(IntPtr hWnd, out uint lpdwProcessId); + + [DllImport("user32.dll")] + static extern bool IsWindowEnabled(IntPtr hWnd); + /// + /// 获取当前窗口 + /// + /// 当前窗口标识符 + [DllImport("user32.dll")] + static extern IntPtr GetForegroundWindow(); + + public static void SetHook() + { + // 如果是全局钩子会发生偶尔失效的情况,改用进程钩子反而好多了 + MouseHook.SetHook(true); + MouseHook.MouseDown += (sender, e) => { + // 此处断点时候就会使得钩子失效 + if (!IsWindowEnabled(Acap.MainWindow.Handle)) + return; + // 进程号拦截 + GetWindowThreadProcessId(GetForegroundWindow(), out uint winId); + if (MouseHook.Process.Id != winId) + return; + if (e.Button == MouseButtons.Left) + { + _X = e.X; + _Y = e.Y; + } + }; + + MouseHook.DoubleClick += (sender, e) => { + // 此处断点时候就会使得钩子失效 + if (!IsWindowEnabled(Acap.MainWindow.Handle)) + return; + // 进程号拦截 + GetWindowThreadProcessId(GetForegroundWindow(), out uint winId); + if (MouseHook.Process.Id != winId) + return; + DoubleClick?.Invoke(sender, e); + }; + } + + public static void RemoveHook() + { + MouseHook?.Dispose(); + } +} + +public static class HatchPickEnv +{ + //static readonly string _appName = nameof(JoinBox); + //static readonly string _data = nameof(CreateBoundary); + + static readonly string _appName = "JoinBox"; + static readonly string _data = "CreateBoundary"; + + /// + /// 判断图元是否由 我的转换器 创建(相对的是直接提取现有图元边界) + /// + /// 任何图元 + /// + public static bool IsMeCreate(Entity entity) + { + if (entity.XData == null) + return false; + var xl = (XDataList)entity.XData; + return xl.Contains(_appName, _data); + } + + /// + /// 我的转换器 xdata数据模板 + /// + /// + /// + /// + public static ResultBuffer GetMeBuffer(Handle hatchHandle, DBTrans? trans = null) + { + trans ??= DBTrans.Top; + trans.RegAppTable.Add(_appName); // add函数会默认的在存在这个名字的时候返回这个名字的regapp的id,不存在就新建 + ResultBuffer resBuf = new() + { + new((int)DxfCode.ExtendedDataRegAppName, _appName), + new((int)DxfCode.ExtendedDataAsciiString,_data), + new((int)DxfCode.ExtendedDataHandle, hatchHandle),//边界回溯这个填充的句柄,如果创建新填充,就需要再去改 + }; + return resBuf; + } + + /// + /// 填充和边界上面增加xdata,实现区分原生和我的数据 + /// + /// + /// + /// + public static void SetMeXData(ObjectId newHatchId, List boIds, DBTrans? trans = null) + { + trans ??= DBTrans.Top; + var hatchEnt = trans.GetObject(newHatchId); + if (hatchEnt != null) + using (hatchEnt.ForWrite()) + hatchEnt.XData = GetMeBuffer(hatchEnt.Handle, trans); // 设置xdata仅仅为debug可以通过鼠标悬停看见它数据,因此设置为自己 + + // 修改边界的xdata为新填充的 + boIds.ForEach(id => { + var boEnt = trans.GetObject(id); + if (boEnt is null) + return; + using (boEnt.ForWrite()) + { + boEnt.RemoveXData(_appName); + boEnt.XData = GetMeBuffer(newHatchId.Handle, trans); + } + }); + } + + /// + /// 通过边界ent获取填充id + /// + /// 边界图元 + /// + /// + public static ObjectId GetXdataHatch(Entity boEntity, DBTrans? trans = null) + { + if (boEntity.XData == null) + return ObjectId.Null; + XDataList data = boEntity.XData; + + if (!data.Contains(_appName, _data)) + return ObjectId.Null; + + var indexs = data.GetXdataAppIndex(_appName, new DxfCode[] { DxfCode.ExtendedDataHandle }); + if (indexs.Count == 0) + return ObjectId.Null; + + trans ??= DBTrans.Top; + return trans.GetObjectId(data[indexs[0]].Value.ToString()); + } +} + +public static class MathHelper +{ + /// + /// 圆弧的腰点 + /// + /// 圆弧点1 + /// 圆弧点3 + /// 凸度 + /// 返回腰点 + /// + [MethodImpl] + public static Point2d GetArcMidPoint(Point2d arc1, Point2d arc3, double bulge) + { + if (bulge == 0) + throw new ArgumentException("凸度为0,此线是平的"); + + var center = GetArcBulgeCenter(arc1, arc3, bulge); + var angle1 = center.GetVectorTo(arc1).GetAngle2XAxis(); + var angle3 = center.GetVectorTo(arc3).GetAngle2XAxis(); + // 利用边点进行旋转,就得到腰点,旋转角/2 + // 需要注意镜像的多段线 + double angle = angle3 - angle1; + if (bulge > 0) + { + if (angle < 0) + angle += Math.PI * 2; + } + else + { + if (angle > 0) + angle += Math.PI * 2; + } + return arc1.RotateBy(angle / 2, center); + } + /// http://bbs.xdcad.net/thread-722387-1-1.html + /// https://blog.csdn.net/jiangyb999/article/details/89366912 + /// + /// 凸度求圆心 + /// + /// 圆弧头点 + /// 圆弧尾点 + /// 凸度 + /// 圆心 + [MethodImpl] + public static Point2d GetArcBulgeCenter(Point2d arc1, Point2d arc3, double bulge) + { + if (bulge == 0) + throw new ArgumentException("凸度为0,此线是平的"); + + var x1 = arc1.X; + var y1 = arc1.Y; + var x2 = arc3.X; + var y2 = arc3.Y; + + var b = (1 / bulge - bulge) / 2; + var x = (x1 + x2 - b * (y2 - y1)) / 2; + var y = (y1 + y2 + b * (x2 - x1)) / 2; + return new Point2d(x, y); + } + + /// + /// X轴到向量的弧度,cad的获取的弧度是1PI,所以转换为2PI(上小,下大) + /// + /// 向量 + /// X轴到向量的弧度 + public static double GetAngle2XAxis(this Vector2d ve, double tolerance = 1e-6) + { + const double Tau = Math.PI + Math.PI; + // 世界重合到用户 Vector3d.XAxis->两点向量 + double al = Vector2d.XAxis.GetAngleTo(ve); + al = ve.Y > 0 ? al : Tau - al; // 逆时针为正,大于0是上半圆,小于则是下半圆,如果-负值控制正反 + al = Math.Abs(Tau - al) <= tolerance ? 0 : al; + return al; + } +} \ No newline at end of file diff --git "a/tests/TestAcad08/\346\213\211\344\274\270\345\241\253\345\205\205/HatchHelper.cs" "b/tests/TestAcad08/\346\213\211\344\274\270\345\241\253\345\205\205/HatchHelper.cs" new file mode 100644 index 0000000..6d6c243 --- /dev/null +++ "b/tests/TestAcad08/\346\213\211\344\274\270\345\241\253\345\205\205/HatchHelper.cs" @@ -0,0 +1,124 @@ +namespace JoinBoxAcad; + +public static class HatchHelper +{ + /// + /// 遍历填充每条边 + /// + /// + /// + public static void ForEach(this Hatch hatch, Action action) + { + for (int i = 0; i < hatch.NumberOfLoops; i++) + action.Invoke(hatch.GetLoopAt(i)); + } + + +#if false + /// + /// 分离填充边界 + /// 将外边界和包含的边界成为一个集 + /// + /// 填充的边界(只有多段线和圆) + /// 多个id集,id中第一个是外边界 + public static IEnumerable[] SeparationBorder(this IEnumerable bianjie) + { + IEnumerable[] objectIds = null; + if (bianjie.Length < 1) return null; + + Database db = bianjie[0].Database; + + // 首先获取一个图元,这个图元默认成边界,看它是否包含第二个,如果是,加入a集 + // 如果不是,它是否把自己包含了, + // 如果是,把它加入a集,并下次使用它来成为外边界 + // 如果不是,则为新边界,把它加入b集 + List alist = new List(); // 边界包含 + List blist = new List(); // 有另外的边界 + List> clist = new List>();// 边界集 + + using (Transaction tr = db.TransactionManager.StartTransaction()) + { + while (true) + { + for (int i = 0; i < bianjie.Length; i++) + { + Entity ent = bianjie[i].ObjectIdToEntity(false); + ent.UpgradeOpen(); + for (int j = i + 1; j < bianjie.Length; j++) + { + if (ent is Polyline polyline)// 多段线 + { + } + else if (ent is Circle circle) // 圆 + { + } + } + ent.DowngradeOpen(); + } + + if (blist.Count == 0)// 没有其他边界就结束循环 + { + break; + } + // 把blist的第一个用来作为新的外边界 + } + } + return objectIds; + } + + /// + /// 判断边界是否包含图元 布尔算法... + /// + /// 边界 + /// 包含的图元 + /// 是true,否false + public static bool BorderIntoCollect(this ObjectId border, ObjectId include) + { + bool flag = false; + Database db = border.Database; + using (Transaction tr = db.TransactionManager.StartTransaction()) + { + Entity ent = border.ObjectIdToEntity(false); + Entity ent2 = include.ObjectIdToEntity(false); + + if (ent is Polyline polyline)// 多段线边界 + { + if (ent2 is Polyline polyline2)// 多段线 + { + } + else if (ent2 is Circle circle2) // 圆 + { + // 判断圆心在多段线内 + if (circle2.Center.RayCasting(polyline.GetPolylinePoints()) != 3) + { + if (true)// 半径若大于多段线最长那段,表示包含不到,是圆包含了多段线(含有弧度就错了) + { + flag = true; + } + } + else // 圆心不在边界内,判断边界是否有交点 + { + } + } + } + else if (ent is Circle circle) // 圆边界 + { + if (ent2 is Polyline polyline2)// 多段线 + { + } + else if (ent2 is Circle circle2) // 圆 + { + // 填充边界不存在交集 + // 两个圆心的距离>两个圆的半径和=两圆分离 + double length = circle.Center.GetDistanceBetweenTwoPoint(circle2.Center); + if (length < circle.Radius + circle2.Radius) + { + flag = true; + } + } + } + return flag; + } + } +#endif +} \ No newline at end of file diff --git "a/tests/TestAcad08/\346\213\211\344\274\270\345\241\253\345\205\205/\345\261\217\345\271\225\345\235\220\346\240\207\350\275\254cad\345\235\220\346\240\207.cs" "b/tests/TestAcad08/\346\213\211\344\274\270\345\241\253\345\205\205/\345\261\217\345\271\225\345\235\220\346\240\207\350\275\254cad\345\235\220\346\240\207.cs" new file mode 100644 index 0000000..00e7c64 --- /dev/null +++ "b/tests/TestAcad08/\346\213\211\344\274\270\345\241\253\345\205\205/\345\261\217\345\271\225\345\235\220\346\240\207\350\275\254cad\345\235\220\346\240\207.cs" @@ -0,0 +1,133 @@ +//#define cpp +namespace JoinBoxAcad; + +using System.Drawing; +using static IFoxCAD.Cad.WindowsAPI; + +public partial class Screen +{ + [CommandMethod(nameof(GetScreenToCadxx))] + public static void GetScreenToCadxx() + { + var dm = Acap.DocumentManager; + var doc = dm.MdiActiveDocument; + var ed = doc.Editor; + var ucsPoint = GetScreenToCad(); + ed.WriteMessage(ucsPoint.ToString() + "\n"); + } + + /// + /// 屏幕坐标转cad坐标 + /// + public static Point3d GetScreenToCad() + { + // 两种获取方式都可以 + var cursorPos = System.Windows.Forms.Control.MousePosition; + // GetCursorPos(out Point cursorPos); + return ScreenToCad(cursorPos); + } + + /// + /// 屏幕像素点转cad图纸坐标点 + /// + /// 屏幕像素点 + /// 返回ucs的点 + public static Point3d ScreenToCad(Point cursorPos) + { + var dm = Acap.DocumentManager; + var doc = dm.MdiActiveDocument; + var ed = doc.Editor; + var mid = WindowsAPI.GetParent(doc.Window.Handle); + + ScreenToClient(mid, ref cursorPos); + var vn = ed.GetViewportNumber(cursorPos);// System.Windows.Forms.Control.MousePosition + var wcsPoint = ed.PointToWorld(cursorPos, vn); + var ucsPoint = wcsPoint.TransformBy(doc.Editor.CurrentUserCoordinateSystem.Inverse()); + return ucsPoint; + } + + /// + /// 屏幕坐标到客户区坐标转换 + /// + /// 窗口句柄 + /// 点结构,返回屏幕坐标 + /// + [DllImport("user32.dll")] + public static extern bool ScreenToClient(IntPtr hWnd, ref Point lpPoint); + [DllImport("user32.dll")] + static extern bool ClientToScreen(IntPtr hWnd, ref Point lpPoint); + + public static Point CadToScreen(Point3d pt3d, Point mousePosition) + { + var dm = Acap.DocumentManager; + var doc = dm.MdiActiveDocument; + var ed = doc.Editor; + var mid = WindowsAPI.GetParent(doc.Window.Handle); + + var vn = ed.GetViewportNumber(mousePosition);//System.Windows.Forms.Control.MousePosition + var ptScr = Env.Editor.PointToScreen(pt3d, vn);// 高版本这个不一样,转为客户区 + var ptScrWin = new Point((int)ptScr.X, (int)ptScr.Y); + ClientToScreen(mid, ref ptScrWin); + return ptScrWin; + } + + //#if NET35 + // [DllImport("acad.exe", EntryPoint = "?acedGetAcadDwgView@@YAPAVCView@@XZ")] //acad08 + //#else + // [DllImport("acad.exe", EntryPoint = "?acedGetAcadDwgView@@YAPEAVCView@@XZ")]//acad21 + //#endif + // static extern IntPtr AcedGetAcadDwgview(); + + delegate IntPtr DelegateAcedGetAcadDwgview(); + static DelegateAcedGetAcadDwgview? acedGetAcadDwgView; + /// + /// 获取视口指针 + /// + public static IntPtr AcedGetAcadDwgview() + { + if (acedGetAcadDwgView is null) + { + acedGetAcadDwgView = AcadPeInfo + .GetDelegate( + nameof(acedGetAcadDwgView), AcadPeEnum.AcadExe); + } + if (acedGetAcadDwgView is not null) + return acedGetAcadDwgView.Invoke();// 调用方法 + return IntPtr.Zero; + } + + delegate int DelegateAcedGetWinNum(int x, int y); + static DelegateAcedGetWinNum? acedGetWinNum; + /// + /// 获取窗口数字 + /// + public static int AcedGetWinNum(int x, int y) + { + if (acedGetWinNum is null) + acedGetWinNum = AcadPeInfo + .GetDelegate( + nameof(acedGetWinNum), AcadPeEnum.ExeAndCore); + if (acedGetWinNum is not null) + return acedGetWinNum.Invoke(x, y);// 调用方法 + return 0; + } + + /// + /// 将坐标从绘图窗口转换为活动视口坐标系 + /// + /// + /// + /// + /// +#if NET35 + // 此处都是acad08这个有重载,不知道PeInfo能不能正常运行 + [DllImport("acad.exe", EntryPoint = "?acedCoordFromPixelToWorld@@YAHHVCPoint@@QAN@Z")] + static extern int AcedCoordFromPixelToWorld(int windnum, Point pt, out Point3D ptOut); + + [DllImport("acad.exe", EntryPoint = "?acedCoordFromPixelToWorld@@YAXABVCPoint@@QAN@Z")]//这个重载参数不知道 + static extern int AcedCoordFromPixelToWorld(Point pt, out Point3D ptOut); +#else + [DllImport("accore.dll", EntryPoint = "?acedCoordFromPixelToWorld@@YAHHVCPoint@@QEAN@Z")] + static extern int AcedCoordFromPixelToWorld(int windnum, Point pt, out Point3D ptOut); +#endif +} \ No newline at end of file diff --git "a/tests/TestAcad08/\346\213\211\344\274\270\345\241\253\345\205\205/\350\277\207\347\250\213\346\226\207\344\273\266/\344\270\200\344\270\252\346\227\240\346\263\225\347\247\273\345\212\250\347\272\242\350\211\262\345\234\206\347\232\204\344\276\213\345\255\220.cs" "b/tests/TestAcad08/\346\213\211\344\274\270\345\241\253\345\205\205/\350\277\207\347\250\213\346\226\207\344\273\266/\344\270\200\344\270\252\346\227\240\346\263\225\347\247\273\345\212\250\347\272\242\350\211\262\345\234\206\347\232\204\344\276\213\345\255\220.cs" new file mode 100644 index 0000000..4e45d21 --- /dev/null +++ "b/tests/TestAcad08/\346\213\211\344\274\270\345\241\253\345\205\205/\350\277\207\347\250\213\346\226\207\344\273\266/\344\270\200\344\270\252\346\227\240\346\263\225\347\247\273\345\212\250\347\272\242\350\211\262\345\234\206\347\232\204\344\276\213\345\255\220.cs" @@ -0,0 +1,94 @@ +// 一个无法移动红色圆的例子 https://www.keanw.com/2008/08/rolling-back-th.html +#if true2 +namespace JoinBoxAcad; + +public class CmdReactor +{ + Document? _doc; + ObjectIdCollection _ids = new(); + Point3dCollection _pts = new(); + + [CommandMethod(nameof(Test_REACTOR))] + public void Test_REACTOR() + { + _doc = Acap.DocumentManager.MdiActiveDocument; + _doc.CommandWillStart += Doc_CommandWillStart; + } + + /// + /// 挂载一个命令反应器,如果出现了move就挂载一个 + /// + /// + /// + void Doc_CommandWillStart(object sender, CommandEventArgs e) + { + if (e.GlobalCommandName == "MOVE") + { + _ids.Clear(); + _pts.Clear(); + + if (_doc is null) + return; + _doc.Database.ObjectOpenedForModify += Db_ObjectOpenedForModify; + _doc.CommandCancelled += Doc_CommandEnded; + _doc.CommandEnded += Doc_CommandEnded; + _doc.CommandFailed += Doc_CommandEnded; + } + } + + /// + /// 卸载一堆反应器 + /// + void RemoveEventHandlers() + { + if (_doc is null) + return; + _doc.CommandCancelled -= Doc_CommandEnded; + _doc.CommandEnded -= Doc_CommandEnded; + _doc.CommandFailed -= Doc_CommandEnded; + _doc.Database.ObjectOpenedForModify -= Db_ObjectOpenedForModify; + } + + void Doc_CommandEnded(object sender, CommandEventArgs e) + { + // 在恢复位置之前删除数据库reactor + RemoveEventHandlers(); + RollbackLocations(); + } + + /// + /// 颜色是1的圆加入集合 + /// + /// + /// + void Db_ObjectOpenedForModify(object sender, ObjectEventArgs e) + { + if (e.DBObject is Circle circle && circle.ColorIndex == 1)// 如果颜色是1 + { + // 不含有就加入集合 + if (!_ids.Collection.Contains(circle.ObjectId)) + { + _ids.Add(circle.ObjectId); + _pts.Add(circle.Center); + } + } + } + + /// + /// 修改圆心 + /// + void RollbackLocations() + { + Debug.Assert(_ids.Count == _pts.Count, "预计相同数量的ID和位置"); + _doc?.Database.Action(tr => { + int i = 0; + foreach (ObjectId id in _ids.Collection) + { + var circle = tr.GetObject(id, OpenMode.ForWrite) as Circle; + if (circle is not null) + circle.Center = _pts[i++]; + } + }); + } +} +#endif \ No newline at end of file diff --git "a/tests/TestAcad08/\346\213\211\344\274\270\345\241\253\345\205\205/\350\277\207\347\250\213\346\226\207\344\273\266/\350\207\252\345\256\232\344\271\211\345\233\276\345\205\203\345\241\253\345\205\205\350\276\271\347\225\214.txt" "b/tests/TestAcad08/\346\213\211\344\274\270\345\241\253\345\205\205/\350\277\207\347\250\213\346\226\207\344\273\266/\350\207\252\345\256\232\344\271\211\345\233\276\345\205\203\345\241\253\345\205\205\350\276\271\347\225\214.txt" new file mode 100644 index 0000000..50ccab0 --- /dev/null +++ "b/tests/TestAcad08/\346\213\211\344\274\270\345\241\253\345\205\205/\350\277\207\347\250\213\346\226\207\344\273\266/\350\207\252\345\256\232\344\271\211\345\233\276\345\205\203\345\241\253\345\205\205\350\276\271\347\225\214.txt" @@ -0,0 +1,104 @@ +拉伸填充bug: +1号: 不闭合的多段线,新建边界会丢失一个不和头点重叠的倒数第二个点. +与头点重叠是ok的 +闭合是ok的 +解决:因为倒数第二个点要从 curve.EndPoint 获取. + +2号: +如果画了一个矩形,然后填充,再删除边界,再点选填充,那么通过生成出来关联标注, +移动第一个点的时候,是没有响应的,移动第二次的时候才会响应. + +两种想法解决这个问题, +a:生成之后,用移动矩阵影响一下关联反应器. + 答:不会触发的,即使我分成两个事务分别提交. +b:先克隆一个填充,再创建关联边界. + 答:可行的,但是会导致3号问题的发生. + +3号: +由于我是生成填充的,所以在在位编辑的时候会在长事务内部: + +【贵妃】惊惊 2019/7/13 17:52:02 +我遇到了一个问题,如果是在位编辑的时候,当前空间是模型空间,那么我用函数克隆一个块外的东西到模型空间,实际上会克隆到在位编辑的内部... +我都不知道怎么处理这个情况了..莫非要关闭用户的在位编辑状态么.. + +【才人】edata 2019/7/13 17:57:15 +在位编辑是这样的. + +【贵妃】惊惊 2019/7/13 17:58:08 +那桌子是怎么控制在位编辑-减出去块外的? + +【才人】edata 2019/7/13 17:58:20 +在位编辑实际上是对当前空间的修改,然后移动回块定义.. + +【才人】edata 2019/7/13 17:58:44 +这个就不是很清楚了... + +【贵妃】惊惊 2019/7/13 17:58:47 +也就是长事务上面记录了要移动回去的id? +如果减选了就是剔除了id? + +【才人】edata 2019/7/13 17:59:27 +你能卡到这个长事务吗? + +【贵妃】惊惊 2019/7/13 17:59:39 +net貌似无法控制长事务呀 +应该桌子有考虑到的,只是我还没有挖出来具体怎么处理的.. + +【才人】edata 2019/7/13 18:08:59 +用命令去移除当前在位编辑. + +我用了命令去移除块外的填充和边界,这样操作是可行的, +然后又产生了一个问题,如果是块内的填充,我并不想减去. +想法: 用命令反应器在在位编辑前获取当前空间所有的图元,然后在位编辑时候就知道两个集合的交叉部分了 +答:证明是可以分类出来的,但是会引起一个问题,在位编辑复制的填充id不在任何一个表上. + +判断执行的时候不是选择前的填充均执行产生边界(这样就不用复制反应器了 +再检测命令 _refset 加减三个集合的填充 +但是命令反应器无法检测二级命令,只能通过 lastprompt 获取最后一行命令,判断添加或者删除 + +命令完成后,执行选择上次选择,便是_refset命令的选择 +然后把id改成加减到判断的两个集合中 + + + + + +U 大写命令,用户回滚的时会导致集合信息不符: +之前koz找到的方法,先选择在位编辑命令触发时的所有图元,再选择触发后的所有图元,分别建立两个集合储存,然后进行差集运算, +得出那些是块内图元,这个方法是可行的,然后我发现在使用+加入块内和-减出块内这也都可以判断, +问题就是....如果是使用了U回滚,那么我将不知道如何修改这两个集合.... + +检测在位编辑命令启动之后,而无结束命令的时候, +期间如果用户使用了u就提示用户是否禁止拉伸填充. + +挂载一个图元的反应器,如果使用了命令U,那么判断填充是否被更改了-> ObjectOpenedForModify 反应器 +如果更改了,更改方式无法知道?? 可能是改颜色,也可能是会在块内外加减操作更改. +无解决方案! + + + +拉伸非圆的时候,如果中心移动,那么边界不会跟随 +发生了拉伸命令的时候, +答: 选择的对象有填充,就克隆填充,并且删除原有的边界,及填充 + + + + +频闪控制 +grips=0 + + + + +编辑填充 +样条曲线边界生成 + +在cad2008设计一条填充边界 +填充边界的自定义图元名称叫做: HatchBoundary +设定HatchBoundary变量,0为使用,1为不使用边界 +想法破产:不会自定义图元. + + + +在位编辑的锁定图元的方法,该不是一个隐藏图层导致的吧. +不是,但是在位编辑的时候出现了一个图层 0-RefEdit0 \ No newline at end of file diff --git "a/tests/TestAcad08/\346\213\211\344\274\270\345\241\253\345\205\205/\350\277\207\347\250\213\346\226\207\344\273\266/\350\216\267\345\217\226\345\244\271\347\202\271\344\276\213\345\255\220.cs" "b/tests/TestAcad08/\346\213\211\344\274\270\345\241\253\345\205\205/\350\277\207\347\250\213\346\226\207\344\273\266/\350\216\267\345\217\226\345\244\271\347\202\271\344\276\213\345\255\220.cs" new file mode 100644 index 0000000..a2d7a98 --- /dev/null +++ "b/tests/TestAcad08/\346\213\211\344\274\270\345\241\253\345\205\205/\350\277\207\347\250\213\346\226\207\344\273\266/\350\216\267\345\217\226\345\244\271\347\202\271\344\276\213\345\255\220.cs" @@ -0,0 +1,100 @@ +#if !ac2008 +namespace GripOverruleTest; + +// https://through-the-interface.typepad.com/through_the_interface/2009/08/knowing-when-an-autocad-object-is-grip-edited-using-overrules-in-net.html +public class GripVectorOverrule : GripOverrule +{ + // A static pointer to our overrule instance + static public GripVectorOverrule theOverrule = new(); + // A flag to indicate whether we're overruling + static bool overruling = false; + // A single set of grips would not have worked in + // the case where multiple objects were selected. + static Dictionary _gripDict = new(); + + private string GetKey(Entity e) + { + // Generate a key based on the name of the object's type + // and its geometric extents + // (We cannot use the ObjectId, as this is null during + // grip-stretch operations.) + return e.GetType().Name + ":" + e.GeometricExtents.ToString(); + } + // Save the locations of the grips for a particular entity + private void StoreGripInfo(Entity e, Point3dCollection grips) + { + string key = GetKey(e); + if (_gripDict.ContainsKey(key)) + { + // Clear the grips if any already associated + Point3dCollection grps = _gripDict[key]; + using (grps) + grps.Clear(); + _gripDict.Remove(key); + } + // Now we add our grips + Point3d[] pts = new Point3d[grips.Count]; + grips.CopyTo(pts, 0); + Point3dCollection gps = new(pts); + _gripDict.Add(key, gps); + } + // Get the locations of the grips for an entity + private Point3dCollection? RetrieveGripInfo(Entity e) + { + Point3dCollection? grips = null; + string key = GetKey(e); + if (_gripDict.ContainsKey(key)) + grips = _gripDict[key]; + return grips; + } + public override void GetGripPoints(Entity e, Point3dCollection grips, IntegerCollection snaps, IntegerCollection geomIds) + { + base.GetGripPoints(e, grips, snaps, geomIds); + StoreGripInfo(e, grips); + } + public override void MoveGripPointsAt(Entity e, IntegerCollection indices, Vector3d offset) + { + var dm = Acap.DocumentManager; + var doc = dm.MdiActiveDocument; + var ed = doc.Editor; + + var grips = RetrieveGripInfo(e); + if (grips != null) + { + // Could get multiple points moved at once, + // hence the integer collection + foreach (int i in indices) + { + // Get the grip point from our internal state + Point3d pt = grips[i]; + // Draw a vector from the grip point to the newly + // offset location, using the index into the + // grip array as the color (excluding colours 0 and 7). + // These vectors don't getting cleared, which makes + // for a fun effect. + ed.DrawVector( + pt, + pt + offset, + (i >= 6 ? i + 2 : i + 1), // exclude colours 0 and 7 + false + ); + } + } + base.MoveGripPointsAt(e, indices, offset); + } + [CommandMethod(nameof(GripOverruleOnOff))] + public static void GripOverruleOnOff() + { + var dm = Acap.DocumentManager; + var doc = dm.MdiActiveDocument; + var ed = doc.Editor; + if (overruling) + RemoveOverrule(GetClass(typeof(Entity)), theOverrule); + else + AddOverrule(GetClass(typeof(Entity)), theOverrule, true); + overruling = !overruling; + Overruling = overruling; + ed.WriteMessage("\nGrip overruling turned {0}.", overruling ? "on" : "off"); + } +} +#endif \ No newline at end of file diff --git "a/tests/TestAcad09plus/\346\213\246\346\210\252\350\276\223\345\205\245\346\263\225/IMEControl.cs" "b/tests/TestAcad09plus/\346\213\246\346\210\252\350\276\223\345\205\245\346\263\225/IMEControl.cs" index 208e17e..b54b48f 100644 --- "a/tests/TestAcad09plus/\346\213\246\346\210\252\350\276\223\345\205\245\346\263\225/IMEControl.cs" +++ "b/tests/TestAcad09plus/\346\213\246\346\210\252\350\276\223\345\205\245\346\263\225/IMEControl.cs" @@ -356,7 +356,7 @@ internal static void SetIMEHook() } return WindowsAPI.CallNextHookEx(_nextHookProc, nCode, wParam, lParam); }; - _nextHookProc = WindowsAPI.SetWindowsHookEx(WindowsAPI.HookType.WH_KEYBOARD, _hookProc, + _nextHookProc = WindowsAPI.SetWindowsHookEx(HookType.WH_KEYBOARD, _hookProc, IntPtr.Zero, WindowsAPI.GetCurrentThreadId()); return; } @@ -373,7 +373,7 @@ internal static void SetIMEHook() } return WindowsAPI.CallNextHookEx(_nextHookProc, nCode, wParam, lParam); }; - _nextHookProc = WindowsAPI.SetWindowsHookEx(WindowsAPI.HookType.WH_KEYBOARD_LL, + _nextHookProc = WindowsAPI.SetWindowsHookEx(HookType.WH_KEYBOARD_LL, _hookProc, moduleHandle, 0); return; } -- Gitee From 6a337ae13d74842631405cc85d6c0c7540f95d08 Mon Sep 17 00:00:00 2001 From: liuqihong <540762622@qq.com> Date: Wed, 7 Dec 2022 22:20:04 +0800 Subject: [PATCH 624/675] =?UTF-8?q?=E5=A4=87=E6=B3=A8=E8=87=B4=E5=91=BD?= =?UTF-8?q?=E9=94=99=E8=AF=AF?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../HatchConverter.cs" | 10 ++++++++++ ...345\241\253\345\205\205\344\272\213\344\273\266.cs" | 2 +- 2 files changed, 11 insertions(+), 1 deletion(-) diff --git "a/src/IFoxCAD.Cad.Shared/ExtensionMethod/\346\226\260\345\273\272\345\241\253\345\205\205/HatchConverter.cs" "b/src/IFoxCAD.Cad.Shared/ExtensionMethod/\346\226\260\345\273\272\345\241\253\345\205\205/HatchConverter.cs" index a04ba02..ea7ca2e 100644 --- "a/src/IFoxCAD.Cad.Shared/ExtensionMethod/\346\226\260\345\273\272\345\241\253\345\205\205/HatchConverter.cs" +++ "b/src/IFoxCAD.Cad.Shared/ExtensionMethod/\346\226\260\345\273\272\345\241\253\345\205\205/HatchConverter.cs" @@ -362,6 +362,16 @@ void ResetBoundary(Hatch hatch, bool boundaryAssociative = true) if (BoundaryIds.Count == 0) return; + // todo ------ acad08分离填充报错: Microsoft Visual Studio C 运行库在 acad.exe 中检测到一个错误 + // 0x01 测试命令 CmdTest_CreateHatch 创建是可以分离的, + // 那么可能是 克隆后 修改导致的, + // 我是克隆了之后移除原有边界,为了一些xdata之类的 + // 0x02 测试了 hatch.SetDatabaseDefaults(); 并不是因为这个 + // 0x03 测试了 v1110 不移除原有边界,而是加入了之后再移除旧的边界,也是一样 + // 要处理这个问题,我想:自己实现一个分离填充,不用cad自带的,然后单独填充每个. + // 填充边界的算法是扫描线算法.这样就可以绕过去了...发现过于麻烦,放弃... + + // v1110 删除原有边界 while (hatch.NumberOfLoops != 0) hatch.RemoveLoopAt(0); diff --git "a/tests/TestAcad08/\346\213\211\344\274\270\345\241\253\345\205\205/02.\346\213\211\344\274\270\345\241\253\345\205\205\344\272\213\344\273\266.cs" "b/tests/TestAcad08/\346\213\211\344\274\270\345\241\253\345\205\205/02.\346\213\211\344\274\270\345\241\253\345\205\205\344\272\213\344\273\266.cs" index af84682..f08eea8 100644 --- "a/tests/TestAcad08/\346\213\211\344\274\270\345\241\253\345\205\205/02.\346\213\211\344\274\270\345\241\253\345\205\205\344\272\213\344\273\266.cs" +++ "b/tests/TestAcad08/\346\213\211\344\274\270\345\241\253\345\205\205/02.\346\213\211\344\274\270\345\241\253\345\205\205\344\272\213\344\273\266.cs" @@ -701,7 +701,7 @@ static void DB_ObjectModified(object sender, ObjectEventArgs e) return; if (e.DBObject.IsErased) return; - //是我生成的填充才删除关联 + // 是我生成的填充才删除关联 if (e.DBObject is Hatch hatch) { if (HatchPickEnv.IsMeCreate(hatch)) -- Gitee From 8fcb984810a89aec98d39eba9afa00cff07f25f4 Mon Sep 17 00:00:00 2001 From: vicwjb Date: Sun, 1 Jan 2023 01:10:53 +0800 Subject: [PATCH 625/675] =?UTF-8?q?=E8=B0=83=E6=95=B4basal=E9=A1=B9?= =?UTF-8?q?=E7=9B=AE=E7=9A=84=E6=9E=B6=E6=9E=84=EF=BC=8C=E6=94=AF=E6=8C=81?= =?UTF-8?q?=E5=85=B1=E4=BA=AB=E9=A1=B9=E7=9B=AE=EF=BC=8C=E6=BA=90=E7=A0=81?= =?UTF-8?q?=E5=8C=85=EF=BC=8CACAD=E5=B9=B3=E5=8F=B0=E5=8C=85=EF=BC=8C?= =?UTF-8?q?=E5=90=8E=E7=BB=AD=E8=80=83=E8=99=91=E5=A2=9E=E5=8A=A0=E6=B5=A9?= =?UTF-8?q?=E8=BE=B0=E5=92=8C=E4=B8=AD=E6=9C=9B=E5=B9=B3=E5=8F=B0=E5=8C=85?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- IFoxCAD.sln | 22 ++++++++ src/Basal/Directory.Build.props | 26 +++++++++ .../IFoxCAD.Basal.ACAD}/GlobalUsings.cs | 0 .../IFoxCAD.Basal.ACAD.csproj | 13 +++++ .../IFoxCAD.Basal.Shared}/CLS/Index.cs | 0 .../IFoxCAD.Basal.Shared}/CLS/Range.cs | 0 .../CLS/RuntimeHelpers.cs | 0 .../CLS/TupleElementNamesAttribute.cs | 0 .../IFoxCAD.Basal.Shared}/CLS/ValueTuple.cs | 0 .../IFoxCAD.Basal.Shared}/General/ArrayEx.cs | 0 .../General/DebugHelper.cs | 0 .../IFoxCAD.Basal.Shared}/General/DictEx.cs | 0 .../IFoxCAD.Basal.Shared}/General/EnumEx.cs | 0 .../General/LinkedHashMap.cs | 0 .../General/LinkedHashSet.cs | 0 .../IFoxCAD.Basal.Shared}/General/LinqEx.cs | 0 .../IFoxCAD.Basal.Shared}/General/ListEx.cs | 0 .../IFoxCAD.Basal.Shared}/General/LoopList.cs | 0 .../General/LoopState.cs | 0 .../IFoxCAD.Basal.Shared}/General/RandomEx.cs | 0 .../IFoxCAD.Basal.Shared}/General/Timer.cs | 0 .../IFoxCAD.Basal.Shared.projitems | 35 ++++++++++++ .../IFoxCAD.Basal.Shared.shproj | 13 +++++ .../IFoxCAD.Basal.Shared}/Sortedset/ISet.cs | 0 .../IFoxCAD.Basal.Shared}/Sortedset/SR.cs | 0 .../Sortedset/Sortedset.cs | 0 .../Sortedset/ThrowHelper.cs | 0 .../Sortedset/bithelper.cs | 0 .../IFoxCAD.Basal.Source.csproj | 55 +++++++++++++++++++ src/IFoxCAD.Basal/IFoxCAD.Basal.csproj | 34 ------------ .../IFoxCAD.Aacad08/IFoxCAD.Acad08.csproj | 4 -- .../IFoxCAD.Acad09plus.csproj | 4 -- .../IFoxCAD.Gcad/IFoxCAD.Gcad.csproj | 4 -- .../IFoxCAD.Zcad/IFoxCAD.Zcad.csproj | 4 -- tests/TestConsole/TestConsole.csproj | 4 -- 35 files changed, 164 insertions(+), 54 deletions(-) create mode 100644 src/Basal/Directory.Build.props rename src/{IFoxCAD.Basal/General => Basal/IFoxCAD.Basal.ACAD}/GlobalUsings.cs (100%) create mode 100644 src/Basal/IFoxCAD.Basal.ACAD/IFoxCAD.Basal.ACAD.csproj rename src/{IFoxCAD.Basal => Basal/IFoxCAD.Basal.Shared}/CLS/Index.cs (100%) rename src/{IFoxCAD.Basal => Basal/IFoxCAD.Basal.Shared}/CLS/Range.cs (100%) rename src/{IFoxCAD.Basal => Basal/IFoxCAD.Basal.Shared}/CLS/RuntimeHelpers.cs (100%) rename src/{IFoxCAD.Basal => Basal/IFoxCAD.Basal.Shared}/CLS/TupleElementNamesAttribute.cs (100%) rename src/{IFoxCAD.Basal => Basal/IFoxCAD.Basal.Shared}/CLS/ValueTuple.cs (100%) rename src/{IFoxCAD.Basal => Basal/IFoxCAD.Basal.Shared}/General/ArrayEx.cs (100%) rename src/{IFoxCAD.Basal => Basal/IFoxCAD.Basal.Shared}/General/DebugHelper.cs (100%) rename src/{IFoxCAD.Basal => Basal/IFoxCAD.Basal.Shared}/General/DictEx.cs (100%) rename src/{IFoxCAD.Basal => Basal/IFoxCAD.Basal.Shared}/General/EnumEx.cs (100%) rename src/{IFoxCAD.Basal => Basal/IFoxCAD.Basal.Shared}/General/LinkedHashMap.cs (100%) rename src/{IFoxCAD.Basal => Basal/IFoxCAD.Basal.Shared}/General/LinkedHashSet.cs (100%) rename src/{IFoxCAD.Basal => Basal/IFoxCAD.Basal.Shared}/General/LinqEx.cs (100%) rename src/{IFoxCAD.Basal => Basal/IFoxCAD.Basal.Shared}/General/ListEx.cs (100%) rename src/{IFoxCAD.Basal => Basal/IFoxCAD.Basal.Shared}/General/LoopList.cs (100%) rename src/{IFoxCAD.Basal => Basal/IFoxCAD.Basal.Shared}/General/LoopState.cs (100%) rename src/{IFoxCAD.Basal => Basal/IFoxCAD.Basal.Shared}/General/RandomEx.cs (100%) rename src/{IFoxCAD.Basal => Basal/IFoxCAD.Basal.Shared}/General/Timer.cs (100%) create mode 100644 src/Basal/IFoxCAD.Basal.Shared/IFoxCAD.Basal.Shared.projitems create mode 100644 src/Basal/IFoxCAD.Basal.Shared/IFoxCAD.Basal.Shared.shproj rename src/{IFoxCAD.Basal => Basal/IFoxCAD.Basal.Shared}/Sortedset/ISet.cs (100%) rename src/{IFoxCAD.Basal => Basal/IFoxCAD.Basal.Shared}/Sortedset/SR.cs (100%) rename src/{IFoxCAD.Basal => Basal/IFoxCAD.Basal.Shared}/Sortedset/Sortedset.cs (100%) rename src/{IFoxCAD.Basal => Basal/IFoxCAD.Basal.Shared}/Sortedset/ThrowHelper.cs (100%) rename src/{IFoxCAD.Basal => Basal/IFoxCAD.Basal.Shared}/Sortedset/bithelper.cs (100%) create mode 100644 src/Basal/IFoxCAD.Basal.Source/IFoxCAD.Basal.Source.csproj delete mode 100644 src/IFoxCAD.Basal/IFoxCAD.Basal.csproj diff --git a/IFoxCAD.sln b/IFoxCAD.sln index 2a9f817..a0b6ce4 100644 --- a/IFoxCAD.sln +++ b/IFoxCAD.sln @@ -40,6 +40,14 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "IFoxCAD.Gcad", "src\IFoxCAD EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "IFoxCAD.Zcad", "src\IFoxCAD.Cad\IFoxCAD.Zcad\IFoxCAD.Zcad.csproj", "{751D15FC-B664-4B71-BB99-529E22EE1C4F}" EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Basal", "Basal", "{1A5C27C1-FEF3-40A0-A1EE-3BC7BF2BD67A}" +EndProject +Project("{D954291E-2A0B-460D-934E-DC6B0785DB48}") = "IFoxCAD.Basal.Shared", "src\Basal\IFoxCAD.Basal.Shared\IFoxCAD.Basal.Shared.shproj", "{C823514A-2BC2-45C2-ACED-18924A7B80BF}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "IFoxCAD.Basal.ACAD", "src\Basal\IFoxCAD.Basal.ACAD\IFoxCAD.Basal.ACAD.csproj", "{D1951425-0DE8-4DFB-B5F9-E79574D2F32D}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "IFoxCAD.Basal.Source", "src\Basal\IFoxCAD.Basal.Source\IFoxCAD.Basal.Source.csproj", "{BF0A5851-549D-48AC-9EE6-2AB866090D24}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -86,6 +94,14 @@ Global {751D15FC-B664-4B71-BB99-529E22EE1C4F}.Debug|Any CPU.Build.0 = Debug|Any CPU {751D15FC-B664-4B71-BB99-529E22EE1C4F}.Release|Any CPU.ActiveCfg = Release|Any CPU {751D15FC-B664-4B71-BB99-529E22EE1C4F}.Release|Any CPU.Build.0 = Release|Any CPU + {D1951425-0DE8-4DFB-B5F9-E79574D2F32D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {D1951425-0DE8-4DFB-B5F9-E79574D2F32D}.Debug|Any CPU.Build.0 = Debug|Any CPU + {D1951425-0DE8-4DFB-B5F9-E79574D2F32D}.Release|Any CPU.ActiveCfg = Release|Any CPU + {D1951425-0DE8-4DFB-B5F9-E79574D2F32D}.Release|Any CPU.Build.0 = Release|Any CPU + {BF0A5851-549D-48AC-9EE6-2AB866090D24}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {BF0A5851-549D-48AC-9EE6-2AB866090D24}.Debug|Any CPU.Build.0 = Debug|Any CPU + {BF0A5851-549D-48AC-9EE6-2AB866090D24}.Release|Any CPU.ActiveCfg = Release|Any CPU + {BF0A5851-549D-48AC-9EE6-2AB866090D24}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -100,6 +116,9 @@ Global {5F478F68-19BC-4A3A-AF39-8728F8D396DD} = {C0BEFC15-6DF7-4636-BC76-4A0B88231470} {D0CE63A9-2DBA-4263-9749-E8CD3EA0DB57} = {465C4E39-FBA2-417A-AB31-BDC01601F420} {751D15FC-B664-4B71-BB99-529E22EE1C4F} = {465C4E39-FBA2-417A-AB31-BDC01601F420} + {C823514A-2BC2-45C2-ACED-18924A7B80BF} = {1A5C27C1-FEF3-40A0-A1EE-3BC7BF2BD67A} + {D1951425-0DE8-4DFB-B5F9-E79574D2F32D} = {1A5C27C1-FEF3-40A0-A1EE-3BC7BF2BD67A} + {BF0A5851-549D-48AC-9EE6-2AB866090D24} = {1A5C27C1-FEF3-40A0-A1EE-3BC7BF2BD67A} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {31D6C754-CF6B-4AB0-9861-6923DD312350} @@ -110,8 +129,11 @@ Global src\IFoxCAD.Cad.Shared\IFoxCAD.Cad.Shared.projitems*{751d15fc-b664-4b71-bb99-529e22ee1c4f}*SharedItemsImports = 5 src\IFoxCAD.Cad.Shared\IFoxCAD.Cad.Shared.projitems*{82fb8450-b971-4e30-859f-4b2ddb81f590}*SharedItemsImports = 13 tests\TestShared\TestShared.projitems*{8c573180-523d-427d-8030-caa7d6b5eb6b}*SharedItemsImports = 5 + src\Basal\IFoxCAD.Basal.Shared\IFoxCAD.Basal.Shared.projitems*{bf0a5851-549d-48ac-9ee6-2ab866090d24}*SharedItemsImports = 5 + src\Basal\IFoxCAD.Basal.Shared\IFoxCAD.Basal.Shared.projitems*{c823514a-2bc2-45c2-aced-18924a7b80bf}*SharedItemsImports = 13 src\IFoxCAD.Cad.Shared\IFoxCAD.Cad.Shared.projitems*{cc46e4e2-90df-48f5-b3d7-83fafed58f2a}*SharedItemsImports = 5 tests\TestShared\TestShared.projitems*{ced63d2d-0af6-4831-806d-5e8e9b0d0a07}*SharedItemsImports = 13 src\IFoxCAD.Cad.Shared\IFoxCAD.Cad.Shared.projitems*{d0ce63a9-2dba-4263-9749-e8cd3ea0db57}*SharedItemsImports = 5 + src\Basal\IFoxCAD.Basal.Shared\IFoxCAD.Basal.Shared.projitems*{d1951425-0de8-4dfb-b5f9-e79574d2f32d}*SharedItemsImports = 5 EndGlobalSection EndGlobal diff --git a/src/Basal/Directory.Build.props b/src/Basal/Directory.Build.props new file mode 100644 index 0000000..87e3b31 --- /dev/null +++ b/src/Basal/Directory.Build.props @@ -0,0 +1,26 @@ + + + + 0.5.0-alpha4 + 支持源码包。 + + + + preview + enable + true + InspireFunction + xsfhlzh;vicwjb;liuqihong + InspireFunction + 基于.NET的二次开发基本类库. + MIT + true + https://gitee.com/inspirefunction/ifoxcad + https://gitee.com/inspirefunction/ifoxcad.git + git + IFoxCAD;C#;NET;Common;Basal + $(MSBuildThisFileDirectory)bin\$(Configuration)\ + + + + \ No newline at end of file diff --git a/src/IFoxCAD.Basal/General/GlobalUsings.cs b/src/Basal/IFoxCAD.Basal.ACAD/GlobalUsings.cs similarity index 100% rename from src/IFoxCAD.Basal/General/GlobalUsings.cs rename to src/Basal/IFoxCAD.Basal.ACAD/GlobalUsings.cs diff --git a/src/Basal/IFoxCAD.Basal.ACAD/IFoxCAD.Basal.ACAD.csproj b/src/Basal/IFoxCAD.Basal.ACAD/IFoxCAD.Basal.ACAD.csproj new file mode 100644 index 0000000..0f2eb78 --- /dev/null +++ b/src/Basal/IFoxCAD.Basal.ACAD/IFoxCAD.Basal.ACAD.csproj @@ -0,0 +1,13 @@ + + + + NET35;NET40;NET45 + true + True + None + + + + + + diff --git a/src/IFoxCAD.Basal/CLS/Index.cs b/src/Basal/IFoxCAD.Basal.Shared/CLS/Index.cs similarity index 100% rename from src/IFoxCAD.Basal/CLS/Index.cs rename to src/Basal/IFoxCAD.Basal.Shared/CLS/Index.cs diff --git a/src/IFoxCAD.Basal/CLS/Range.cs b/src/Basal/IFoxCAD.Basal.Shared/CLS/Range.cs similarity index 100% rename from src/IFoxCAD.Basal/CLS/Range.cs rename to src/Basal/IFoxCAD.Basal.Shared/CLS/Range.cs diff --git a/src/IFoxCAD.Basal/CLS/RuntimeHelpers.cs b/src/Basal/IFoxCAD.Basal.Shared/CLS/RuntimeHelpers.cs similarity index 100% rename from src/IFoxCAD.Basal/CLS/RuntimeHelpers.cs rename to src/Basal/IFoxCAD.Basal.Shared/CLS/RuntimeHelpers.cs diff --git a/src/IFoxCAD.Basal/CLS/TupleElementNamesAttribute.cs b/src/Basal/IFoxCAD.Basal.Shared/CLS/TupleElementNamesAttribute.cs similarity index 100% rename from src/IFoxCAD.Basal/CLS/TupleElementNamesAttribute.cs rename to src/Basal/IFoxCAD.Basal.Shared/CLS/TupleElementNamesAttribute.cs diff --git a/src/IFoxCAD.Basal/CLS/ValueTuple.cs b/src/Basal/IFoxCAD.Basal.Shared/CLS/ValueTuple.cs similarity index 100% rename from src/IFoxCAD.Basal/CLS/ValueTuple.cs rename to src/Basal/IFoxCAD.Basal.Shared/CLS/ValueTuple.cs diff --git a/src/IFoxCAD.Basal/General/ArrayEx.cs b/src/Basal/IFoxCAD.Basal.Shared/General/ArrayEx.cs similarity index 100% rename from src/IFoxCAD.Basal/General/ArrayEx.cs rename to src/Basal/IFoxCAD.Basal.Shared/General/ArrayEx.cs diff --git a/src/IFoxCAD.Basal/General/DebugHelper.cs b/src/Basal/IFoxCAD.Basal.Shared/General/DebugHelper.cs similarity index 100% rename from src/IFoxCAD.Basal/General/DebugHelper.cs rename to src/Basal/IFoxCAD.Basal.Shared/General/DebugHelper.cs diff --git a/src/IFoxCAD.Basal/General/DictEx.cs b/src/Basal/IFoxCAD.Basal.Shared/General/DictEx.cs similarity index 100% rename from src/IFoxCAD.Basal/General/DictEx.cs rename to src/Basal/IFoxCAD.Basal.Shared/General/DictEx.cs diff --git a/src/IFoxCAD.Basal/General/EnumEx.cs b/src/Basal/IFoxCAD.Basal.Shared/General/EnumEx.cs similarity index 100% rename from src/IFoxCAD.Basal/General/EnumEx.cs rename to src/Basal/IFoxCAD.Basal.Shared/General/EnumEx.cs diff --git a/src/IFoxCAD.Basal/General/LinkedHashMap.cs b/src/Basal/IFoxCAD.Basal.Shared/General/LinkedHashMap.cs similarity index 100% rename from src/IFoxCAD.Basal/General/LinkedHashMap.cs rename to src/Basal/IFoxCAD.Basal.Shared/General/LinkedHashMap.cs diff --git a/src/IFoxCAD.Basal/General/LinkedHashSet.cs b/src/Basal/IFoxCAD.Basal.Shared/General/LinkedHashSet.cs similarity index 100% rename from src/IFoxCAD.Basal/General/LinkedHashSet.cs rename to src/Basal/IFoxCAD.Basal.Shared/General/LinkedHashSet.cs diff --git a/src/IFoxCAD.Basal/General/LinqEx.cs b/src/Basal/IFoxCAD.Basal.Shared/General/LinqEx.cs similarity index 100% rename from src/IFoxCAD.Basal/General/LinqEx.cs rename to src/Basal/IFoxCAD.Basal.Shared/General/LinqEx.cs diff --git a/src/IFoxCAD.Basal/General/ListEx.cs b/src/Basal/IFoxCAD.Basal.Shared/General/ListEx.cs similarity index 100% rename from src/IFoxCAD.Basal/General/ListEx.cs rename to src/Basal/IFoxCAD.Basal.Shared/General/ListEx.cs diff --git a/src/IFoxCAD.Basal/General/LoopList.cs b/src/Basal/IFoxCAD.Basal.Shared/General/LoopList.cs similarity index 100% rename from src/IFoxCAD.Basal/General/LoopList.cs rename to src/Basal/IFoxCAD.Basal.Shared/General/LoopList.cs diff --git a/src/IFoxCAD.Basal/General/LoopState.cs b/src/Basal/IFoxCAD.Basal.Shared/General/LoopState.cs similarity index 100% rename from src/IFoxCAD.Basal/General/LoopState.cs rename to src/Basal/IFoxCAD.Basal.Shared/General/LoopState.cs diff --git a/src/IFoxCAD.Basal/General/RandomEx.cs b/src/Basal/IFoxCAD.Basal.Shared/General/RandomEx.cs similarity index 100% rename from src/IFoxCAD.Basal/General/RandomEx.cs rename to src/Basal/IFoxCAD.Basal.Shared/General/RandomEx.cs diff --git a/src/IFoxCAD.Basal/General/Timer.cs b/src/Basal/IFoxCAD.Basal.Shared/General/Timer.cs similarity index 100% rename from src/IFoxCAD.Basal/General/Timer.cs rename to src/Basal/IFoxCAD.Basal.Shared/General/Timer.cs diff --git a/src/Basal/IFoxCAD.Basal.Shared/IFoxCAD.Basal.Shared.projitems b/src/Basal/IFoxCAD.Basal.Shared/IFoxCAD.Basal.Shared.projitems new file mode 100644 index 0000000..ddbbfb7 --- /dev/null +++ b/src/Basal/IFoxCAD.Basal.Shared/IFoxCAD.Basal.Shared.projitems @@ -0,0 +1,35 @@ + + + + $(MSBuildAllProjects);$(MSBuildThisFileFullPath) + true + c823514a-2bc2-45c2-aced-18924a7b80bf + + + IFoxCAD.Basal.Shared + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/Basal/IFoxCAD.Basal.Shared/IFoxCAD.Basal.Shared.shproj b/src/Basal/IFoxCAD.Basal.Shared/IFoxCAD.Basal.Shared.shproj new file mode 100644 index 0000000..7432ebd --- /dev/null +++ b/src/Basal/IFoxCAD.Basal.Shared/IFoxCAD.Basal.Shared.shproj @@ -0,0 +1,13 @@ + + + + c823514a-2bc2-45c2-aced-18924a7b80bf + 14.0 + + + + + + + + diff --git a/src/IFoxCAD.Basal/Sortedset/ISet.cs b/src/Basal/IFoxCAD.Basal.Shared/Sortedset/ISet.cs similarity index 100% rename from src/IFoxCAD.Basal/Sortedset/ISet.cs rename to src/Basal/IFoxCAD.Basal.Shared/Sortedset/ISet.cs diff --git a/src/IFoxCAD.Basal/Sortedset/SR.cs b/src/Basal/IFoxCAD.Basal.Shared/Sortedset/SR.cs similarity index 100% rename from src/IFoxCAD.Basal/Sortedset/SR.cs rename to src/Basal/IFoxCAD.Basal.Shared/Sortedset/SR.cs diff --git a/src/IFoxCAD.Basal/Sortedset/Sortedset.cs b/src/Basal/IFoxCAD.Basal.Shared/Sortedset/Sortedset.cs similarity index 100% rename from src/IFoxCAD.Basal/Sortedset/Sortedset.cs rename to src/Basal/IFoxCAD.Basal.Shared/Sortedset/Sortedset.cs diff --git a/src/IFoxCAD.Basal/Sortedset/ThrowHelper.cs b/src/Basal/IFoxCAD.Basal.Shared/Sortedset/ThrowHelper.cs similarity index 100% rename from src/IFoxCAD.Basal/Sortedset/ThrowHelper.cs rename to src/Basal/IFoxCAD.Basal.Shared/Sortedset/ThrowHelper.cs diff --git a/src/IFoxCAD.Basal/Sortedset/bithelper.cs b/src/Basal/IFoxCAD.Basal.Shared/Sortedset/bithelper.cs similarity index 100% rename from src/IFoxCAD.Basal/Sortedset/bithelper.cs rename to src/Basal/IFoxCAD.Basal.Shared/Sortedset/bithelper.cs diff --git a/src/Basal/IFoxCAD.Basal.Source/IFoxCAD.Basal.Source.csproj b/src/Basal/IFoxCAD.Basal.Source/IFoxCAD.Basal.Source.csproj new file mode 100644 index 0000000..9272ddf --- /dev/null +++ b/src/Basal/IFoxCAD.Basal.Source/IFoxCAD.Basal.Source.csproj @@ -0,0 +1,55 @@ + + + + + + netstandard1.0 + true + $(AssemblyName) + $(Version) + true + + + + CS8021 + true + false + contentFiles + true + false + false + true + + + + + + + + true + $(ContentTargetFolders)\cs\any\$(PackageId)\ + + + true + $(ContentTargetFolders)\any\any\$(PackageId)\ + + + + + + + false + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/IFoxCAD.Basal/IFoxCAD.Basal.csproj b/src/IFoxCAD.Basal/IFoxCAD.Basal.csproj deleted file mode 100644 index dbc9743..0000000 --- a/src/IFoxCAD.Basal/IFoxCAD.Basal.csproj +++ /dev/null @@ -1,34 +0,0 @@ - - - - preview - enable - - NET35;NET40;NET45 - true - 0.4 - InspireFunction - xsfhlzh;vicwjb;liuqihong - 基于.NET的二次开发基本类库 - InspireFunction - https://gitee.com/inspirefunction/ifoxcad - https://gitee.com/inspirefunction/ifoxcad.git - git - IFoxCAD;C#;NET;Common;Basal - 直接sortedset. - true - true - LICENSE - true - True - none - - - - - True - - - - - diff --git a/src/IFoxCAD.Cad/IFoxCAD.Aacad08/IFoxCAD.Acad08.csproj b/src/IFoxCAD.Cad/IFoxCAD.Aacad08/IFoxCAD.Acad08.csproj index e99be5d..44ce792 100644 --- a/src/IFoxCAD.Cad/IFoxCAD.Aacad08/IFoxCAD.Acad08.csproj +++ b/src/IFoxCAD.Cad/IFoxCAD.Aacad08/IFoxCAD.Acad08.csproj @@ -49,10 +49,6 @@ --> - - - - diff --git a/src/IFoxCAD.Cad/IFoxCAD.Aacad09plus/IFoxCAD.Acad09plus.csproj b/src/IFoxCAD.Cad/IFoxCAD.Aacad09plus/IFoxCAD.Acad09plus.csproj index 071ebc3..90b92ba 100644 --- a/src/IFoxCAD.Cad/IFoxCAD.Aacad09plus/IFoxCAD.Acad09plus.csproj +++ b/src/IFoxCAD.Cad/IFoxCAD.Aacad09plus/IFoxCAD.Acad09plus.csproj @@ -61,10 +61,6 @@ --> - - - - diff --git a/src/IFoxCAD.Cad/IFoxCAD.Gcad/IFoxCAD.Gcad.csproj b/src/IFoxCAD.Cad/IFoxCAD.Gcad/IFoxCAD.Gcad.csproj index 314cd87..c4c523b 100644 --- a/src/IFoxCAD.Cad/IFoxCAD.Gcad/IFoxCAD.Gcad.csproj +++ b/src/IFoxCAD.Cad/IFoxCAD.Gcad/IFoxCAD.Gcad.csproj @@ -50,10 +50,6 @@ --> - - - - diff --git a/src/IFoxCAD.Cad/IFoxCAD.Zcad/IFoxCAD.Zcad.csproj b/src/IFoxCAD.Cad/IFoxCAD.Zcad/IFoxCAD.Zcad.csproj index 893a212..79977c4 100644 --- a/src/IFoxCAD.Cad/IFoxCAD.Zcad/IFoxCAD.Zcad.csproj +++ b/src/IFoxCAD.Cad/IFoxCAD.Zcad/IFoxCAD.Zcad.csproj @@ -58,10 +58,6 @@ --> - - - - diff --git a/tests/TestConsole/TestConsole.csproj b/tests/TestConsole/TestConsole.csproj index 0e0c1b2..43c900a 100644 --- a/tests/TestConsole/TestConsole.csproj +++ b/tests/TestConsole/TestConsole.csproj @@ -10,10 +10,6 @@ preview - - - - -- Gitee From 87adbebac99ddb3da77217b217d7fd7859b6de7a Mon Sep 17 00:00:00 2001 From: vicwjb Date: Tue, 3 Jan 2023 23:11:36 +0800 Subject: [PATCH 626/675] =?UTF-8?q?=E6=9B=B4=E6=96=B0=E8=A7=A3=E5=86=B3?= =?UTF-8?q?=E6=96=B9=E6=A1=88=E6=96=87=E4=BB=B6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- IFoxCAD.sln | 6 ------ 1 file changed, 6 deletions(-) diff --git a/IFoxCAD.sln b/IFoxCAD.sln index a0b6ce4..9821f1a 100644 --- a/IFoxCAD.sln +++ b/IFoxCAD.sln @@ -14,8 +14,6 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "IFoxCAD.WPF", "src\IFoxCAD.WPF\IFoxCAD.WPF.csproj", "{D820D629-1AB3-4BE3-A772-CB753D98CCB8}" EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "IFoxCAD.Basal", "src\IFoxCAD.Basal\IFoxCAD.Basal.csproj", "{40BF07C4-100A-4810-A27B-4587CFB38E6E}" -EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "TestConsole", "tests\TestConsole\TestConsole.csproj", "{E2873F0B-CAD2-45E8-8FF0-C05C0C6AD955}" EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "IFoxCAD.Acad08", "src\IFoxCAD.Cad\IFoxCAD.Aacad08\IFoxCAD.Acad08.csproj", "{36F87D06-88B3-45E3-A2A8-0FC737B25428}" @@ -58,10 +56,6 @@ Global {D820D629-1AB3-4BE3-A772-CB753D98CCB8}.Debug|Any CPU.Build.0 = Debug|Any CPU {D820D629-1AB3-4BE3-A772-CB753D98CCB8}.Release|Any CPU.ActiveCfg = Release|Any CPU {D820D629-1AB3-4BE3-A772-CB753D98CCB8}.Release|Any CPU.Build.0 = Release|Any CPU - {40BF07C4-100A-4810-A27B-4587CFB38E6E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {40BF07C4-100A-4810-A27B-4587CFB38E6E}.Debug|Any CPU.Build.0 = Debug|Any CPU - {40BF07C4-100A-4810-A27B-4587CFB38E6E}.Release|Any CPU.ActiveCfg = Release|Any CPU - {40BF07C4-100A-4810-A27B-4587CFB38E6E}.Release|Any CPU.Build.0 = Release|Any CPU {E2873F0B-CAD2-45E8-8FF0-C05C0C6AD955}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {E2873F0B-CAD2-45E8-8FF0-C05C0C6AD955}.Debug|Any CPU.Build.0 = Debug|Any CPU {E2873F0B-CAD2-45E8-8FF0-C05C0C6AD955}.Release|Any CPU.ActiveCfg = Release|Any CPU -- Gitee From eaac3125fc80d2d89faceaabb37065c44b899eba Mon Sep 17 00:00:00 2001 From: vicwjb Date: Wed, 4 Jan 2023 00:06:18 +0800 Subject: [PATCH 627/675] =?UTF-8?q?=E7=A7=BB=E9=99=A4loadx=E9=A1=B9?= =?UTF-8?q?=E7=9B=AE=EF=BC=8C=E8=BD=AC=E7=A7=BB=E8=87=B3ifoxcadtools?= =?UTF-8?q?=E9=A1=B9=E7=9B=AE=EF=BC=8C=E4=BF=9D=E6=8C=81ifoxcad=E7=B1=BB?= =?UTF-8?q?=E5=BA=93=E7=9A=84=E5=8D=95=E4=B8=80=E6=80=A7=EF=BC=8Cloadx?= =?UTF-8?q?=E5=B1=9E=E4=BA=8E=E5=85=B7=E4=BD=93=E5=B7=A5=E5=85=B7=E7=B1=BB?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- IFoxCAD.sln | 10 +- .../IFoxCAD.Acad09plus.csproj | 4 + src/IFoxCAD.LoadForm/AssemblyDependent.cs | 557 ------------------ .../DefaultAssemblyResolve.cs | 92 --- src/IFoxCAD.LoadForm/GlobalUsings.cs | 16 - src/IFoxCAD.LoadForm/IFoxCAD.LoadEx.csproj | 30 - src/IFoxCAD.LoadForm/LoaderForm.Designer.cs | 136 ----- src/IFoxCAD.LoadForm/LoaderForm.cs | 107 ---- src/IFoxCAD.LoadForm/LoaderForm.resx | 126 ---- src/IFoxCAD.LoadForm/TestNetLoad.cs | 28 - 10 files changed, 6 insertions(+), 1100 deletions(-) delete mode 100644 src/IFoxCAD.LoadForm/AssemblyDependent.cs delete mode 100644 src/IFoxCAD.LoadForm/DefaultAssemblyResolve.cs delete mode 100644 src/IFoxCAD.LoadForm/GlobalUsings.cs delete mode 100644 src/IFoxCAD.LoadForm/IFoxCAD.LoadEx.csproj delete mode 100644 src/IFoxCAD.LoadForm/LoaderForm.Designer.cs delete mode 100644 src/IFoxCAD.LoadForm/LoaderForm.cs delete mode 100644 src/IFoxCAD.LoadForm/LoaderForm.resx delete mode 100644 src/IFoxCAD.LoadForm/TestNetLoad.cs diff --git a/IFoxCAD.sln b/IFoxCAD.sln index 9821f1a..df2e98f 100644 --- a/IFoxCAD.sln +++ b/IFoxCAD.sln @@ -32,8 +32,6 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "TestAcad09plus", "tests\Tes EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Test", "Test", "{C0BEFC15-6DF7-4636-BC76-4A0B88231470}" EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "IFoxCAD.LoadEx", "src\IFoxCAD.LoadForm\IFoxCAD.LoadEx.csproj", "{BFA1C130-8D87-45D0-8E79-301C1841A7A1}" -EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "IFoxCAD.Gcad", "src\IFoxCAD.Cad\IFoxCAD.Gcad\IFoxCAD.Gcad.csproj", "{D0CE63A9-2DBA-4263-9749-E8CD3EA0DB57}" EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "IFoxCAD.Zcad", "src\IFoxCAD.Cad\IFoxCAD.Zcad\IFoxCAD.Zcad.csproj", "{751D15FC-B664-4B71-BB99-529E22EE1C4F}" @@ -42,9 +40,9 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Basal", "Basal", "{1A5C27C1 EndProject Project("{D954291E-2A0B-460D-934E-DC6B0785DB48}") = "IFoxCAD.Basal.Shared", "src\Basal\IFoxCAD.Basal.Shared\IFoxCAD.Basal.Shared.shproj", "{C823514A-2BC2-45C2-ACED-18924A7B80BF}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "IFoxCAD.Basal.ACAD", "src\Basal\IFoxCAD.Basal.ACAD\IFoxCAD.Basal.ACAD.csproj", "{D1951425-0DE8-4DFB-B5F9-E79574D2F32D}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "IFoxCAD.Basal.ACAD", "src\Basal\IFoxCAD.Basal.ACAD\IFoxCAD.Basal.ACAD.csproj", "{D1951425-0DE8-4DFB-B5F9-E79574D2F32D}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "IFoxCAD.Basal.Source", "src\Basal\IFoxCAD.Basal.Source\IFoxCAD.Basal.Source.csproj", "{BF0A5851-549D-48AC-9EE6-2AB866090D24}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "IFoxCAD.Basal.Source", "src\Basal\IFoxCAD.Basal.Source\IFoxCAD.Basal.Source.csproj", "{BF0A5851-549D-48AC-9EE6-2AB866090D24}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution @@ -76,10 +74,6 @@ Global {5F478F68-19BC-4A3A-AF39-8728F8D396DD}.Debug|Any CPU.Build.0 = Debug|Any CPU {5F478F68-19BC-4A3A-AF39-8728F8D396DD}.Release|Any CPU.ActiveCfg = Release|Any CPU {5F478F68-19BC-4A3A-AF39-8728F8D396DD}.Release|Any CPU.Build.0 = Release|Any CPU - {BFA1C130-8D87-45D0-8E79-301C1841A7A1}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {BFA1C130-8D87-45D0-8E79-301C1841A7A1}.Debug|Any CPU.Build.0 = Debug|Any CPU - {BFA1C130-8D87-45D0-8E79-301C1841A7A1}.Release|Any CPU.ActiveCfg = Release|Any CPU - {BFA1C130-8D87-45D0-8E79-301C1841A7A1}.Release|Any CPU.Build.0 = Release|Any CPU {D0CE63A9-2DBA-4263-9749-E8CD3EA0DB57}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {D0CE63A9-2DBA-4263-9749-E8CD3EA0DB57}.Debug|Any CPU.Build.0 = Debug|Any CPU {D0CE63A9-2DBA-4263-9749-E8CD3EA0DB57}.Release|Any CPU.ActiveCfg = Release|Any CPU diff --git a/src/IFoxCAD.Cad/IFoxCAD.Aacad09plus/IFoxCAD.Acad09plus.csproj b/src/IFoxCAD.Cad/IFoxCAD.Aacad09plus/IFoxCAD.Acad09plus.csproj index 90b92ba..4e66292 100644 --- a/src/IFoxCAD.Cad/IFoxCAD.Aacad09plus/IFoxCAD.Acad09plus.csproj +++ b/src/IFoxCAD.Cad/IFoxCAD.Aacad09plus/IFoxCAD.Acad09plus.csproj @@ -63,6 +63,10 @@ + + + + diff --git a/src/IFoxCAD.LoadForm/AssemblyDependent.cs b/src/IFoxCAD.LoadForm/AssemblyDependent.cs deleted file mode 100644 index 0197c0c..0000000 --- a/src/IFoxCAD.LoadForm/AssemblyDependent.cs +++ /dev/null @@ -1,557 +0,0 @@ -#define HarmonyPatch -#define HarmonyPatch_1 -//#define HarmonyPatch_2 - -namespace IFoxCAD.LoadEx; - -#if HarmonyPatch_1 -[HarmonyPatch("Autodesk.AutoCAD.ApplicationServices.ExtensionLoader", "OnAssemblyLoad")] -#endif -public class AssemblyDependent : IDisposable -{ -#if HarmonyPatch - // 这个是不能删除的,否则就不执行了 - // HarmonyPatch hook method 返回 false 表示拦截原函数 - public static bool Prefix() { return false; } -#endif - - #region 字段和事件 - /// - /// 当前域加载事件
    - /// 运行时出错的话,就靠这个事件来解决 - ///
    - public event ResolveEventHandler CurrentDomainAssemblyResolveEvent - { - add { AppDomain.CurrentDomain.AssemblyResolve += value; } - remove { AppDomain.CurrentDomain.AssemblyResolve -= value; } - } - - /// - /// 当前域参照加载事件 - /// - public event ResolveEventHandler CurrentDomainReflectionOnlyAssemblyResolveEvent - { - add { AppDomain.CurrentDomain.ReflectionOnlyAssemblyResolve += value; } - remove { AppDomain.CurrentDomain.ReflectionOnlyAssemblyResolve -= value; } - } - - /// - /// 拦截cad的Loader异常:默认是 - /// - public bool PatchExtensionLoader = false; - #endregion - - #region 构造 - /// - /// 链式加载dll依赖 - /// - public AssemblyDependent() - { - // 初始化一次,反复load - CurrentDomainAssemblyResolveEvent += AssemblyHelper.DefaultAssemblyResolve; - CurrentDomainReflectionOnlyAssemblyResolveEvent += AssemblyHelper.ReflectionOnlyAssemblyResolve; - } - #endregion - - #region 获取加载链 - - /// - /// 加载程序集 - /// - /// dll的文件位置 - /// 返回加载链 - /// true字节加载,false文件加载 - /// 参数 是否加载成功 - /// 链条后面的不再理会,因为相同的dll引用辨识无意义 - /// - public bool Load(string? dllFullName, List loadStates, bool byteLoad = true) - { - if (dllFullName == null) - throw new ArgumentNullException(nameof(dllFullName)); - - dllFullName = Path.GetFullPath(dllFullName);// 相对路径要先转换 - if (!File.Exists(dllFullName)) - throw new ArgumentException("路径不存在"); - - // 程序集数组要动态获取(每次Load的时候), - // 否则会变成一个固定数组,造成加载了之后也不会出现成员 - var cadAssembly = AppDomain.CurrentDomain.GetAssemblies(); - var cadAssemblyRef = AppDomain.CurrentDomain.ReflectionOnlyGetAssemblies(); - - List allRefs = new(); - GetAllRefPaths(cadAssembly, cadAssemblyRef, dllFullName, allRefs); - - bool dllFullNameLoadOk = false; - - // 查询加载链逆向加载,确保前面不丢失 - // 这里有问题,从尾巴开始的,就一定是没有任何引用吗? - for (int i = allRefs.Count - 1; i >= 0; i--) - { - var allRef = allRefs[i]; - - // 路径转程序集名 - var an = AssemblyName.GetAssemblyName(allRef).FullName; - var assembly = cadAssembly.FirstOrDefault(a => a.FullName == an); - if (assembly != null) - { - loadStates.Add(new LoadState(allRef, false));// 版本号没变不加载 - continue; - } - - // 有一次true,就是true - if (allRef == dllFullName) - dllFullNameLoadOk = true; - - try - { - var ass = GetPdbAssembly(allRef); - -#if Debug_WriteLine_null - if (ass == null) - System.Diagnostics.Debug - .WriteLine($"****{nameof(Load)}:此文件无加载了pdb对象:" + allRef); -#endif - -#if Debug_WriteLine_notnull - if (ass != null) - System.Diagnostics.Debug - .WriteLine($"****{nameof(Load)}:此文件加载了pdb对象:" + allRef); -#endif - if (ass == null) - if (byteLoad) - ass = Assembly.Load(File.ReadAllBytes(allRef)); - else - ass = Assembly.LoadFile(allRef); - loadStates.Add(new LoadState(allRef, true, ass));/*加载成功*/ - } - catch { loadStates.Add(new LoadState(allRef, false));/*错误造成*/ } - } - return dllFullNameLoadOk; - } - - /// - /// 在debug模式的时候才获取PBD调试信息 - /// - /// - /// - /// - Assembly? GetPdbAssembly(string? path) - { -#if DEBUG - // 为了实现Debug时候出现断点,见链接,加依赖 - // https://www.cnblogs.com/DasonKwok/p/10510218.html - // https://www.cnblogs.com/DasonKwok/p/10523279.html - - var dir = Path.GetDirectoryName(path); - var pdbName = Path.GetFileNameWithoutExtension(path) + ".pdb"; - var pdbFullName = Path.Combine(dir, pdbName); - if (File.Exists(pdbFullName)) - return Assembly.Load(File.ReadAllBytes(path), File.ReadAllBytes(pdbFullName)); -#endif - return null; - } - - /// - /// 递归获取加载链 - /// - /// 程序集_内存区 - /// 程序集_映射区 - /// dll文件位置 - /// 返回的集合 - /// - void GetAllRefPaths(Assembly[] cadAssembly, - Assembly[] cadAssemblyRef, - string? dllFullName, - List dllFullNamesOut) - { - if (dllFullName == null) - throw new ArgumentNullException(nameof(dllFullName)); - - if (dllFullNamesOut.Contains(dllFullName) || !File.Exists(dllFullName)) - return; - dllFullNamesOut.Add(dllFullName); - - var assemblyAsRef = GetAssembly(cadAssembly, cadAssemblyRef, dllFullName); - if (assemblyAsRef == null) - return; - - var sb = new StringBuilder(); - // dll拖拉加载路径-搜索路径(可以增加到这个dll下面的所有文件夹?) - sb.Append(Path.GetDirectoryName(dllFullName)); - sb.Append("\\"); - - // 遍历依赖,如果存在dll拖拉加载目录就加入dlls集合 - var asse = assemblyAsRef.GetReferencedAssemblies(); - for (int i = 0; i < asse.Length; i++) - { - var path = sb.ToString() + asse[i].Name; - var paths = new string[] - { - path + ".dll", - path + ".exe" - }; - for (int j = 0; j < paths.Length; j++) - GetAllRefPaths(cadAssembly, cadAssemblyRef, paths[j], dllFullNamesOut); - } - } - - /// - /// 在内存区和映射区找已经加载的程序集 - /// - /// 程序集_内存区 - /// 程序集_映射区 - /// dll文件位置 - /// - Assembly? GetAssembly(Assembly[] cadAssembly, - Assembly[] cadAssemblyRef, - string? dllFullName) - { - // 路径转程序集名 - var assName = AssemblyName.GetAssemblyName(dllFullName).FullName; - // 在当前程序域的 assemblyAs内存区 和 assemblyAsRef映射区 找这个程序集名 - var assemblyAs = cadAssembly.FirstOrDefault(ass => ass.FullName == assName); - - // 内存区有表示加载过 - // 映射区有表示查找过,但没有加载(一般来说不存在.只是debug会注释掉 Assembly.Load 的时候用来测试) - if (assemblyAs != null) - return assemblyAs; - - // 映射区 - var assemblyAsRef = cadAssemblyRef.FirstOrDefault(ass => ass.FullName == assName); - - // 内存区和映射区都没有的话就把dll加载到映射区,用来找依赖表 - if (assemblyAsRef != null) - return assemblyAsRef; - - var byteRef = File.ReadAllBytes(dllFullName); - if (PatchExtensionLoader) - { -#if HarmonyPatch_1 - /* QQ1548253108:这里会报错,他提供了解决方案. - * 方案一: - * 在类上面加 [HarmonyPatch("Autodesk.AutoCAD.ApplicationServices.ExtensionLoader", "OnAssemblyLoad")] - */ - const string ext = "Autodesk.AutoCAD.ApplicationServices.ExtensionLoader"; - Harmony hm = new(ext); - hm.PatchAll(); - assemblyAsRef = Assembly.ReflectionOnlyLoad(byteRef); - hm.UnpatchAll(ext); -#endif -#if HarmonyPatch_2 - // 方案二:跟cad耦合了 - const string ext = "Autodesk.AutoCAD.ApplicationServices.ExtensionLoader"; - var docAss = typeof(Autodesk.AutoCAD.ApplicationServices.Document).Assembly; - var a = docAss.GetType(ext); - var b = a.GetMethod("OnAssemblyLoad"); - Harmony hm = new(ext); - hm.Patch(b, new HarmonyMethod(GetType(), "Dummy")); - assemblyAsRef = Assembly.ReflectionOnlyLoad(byteRef); - hm.UnpatchAll(ext); -#endif - } - else - { - /* - * 0x01 此句没有依赖会直接报错 - * assemblyAsRef = Assembly.ReflectionOnlyLoad(dllFullName); - * 0x02 重复加载无修改的同一个dll,会出现如下异常: - * System.IO.FileLoadException: - * “API 限制: 程序集“”已从其他位置加载。无法从同一个 Appdomain 中的另一新位置加载该程序集。” - * catch 兜不住的,仍然会在cad上面打印,原因是程序集数组要动态获取(已改) - */ - try - { - assemblyAsRef = Assembly.ReflectionOnlyLoad(byteRef); - } - catch (System.IO.FileLoadException) - { } - } - return assemblyAsRef; - } - - - /// - /// 加载信息 - /// - public static string? PrintMessage(List loadStates, - PrintModes modes = PrintModes.All) - { - if (loadStates == null) - return null; - - var sb = new StringBuilder(); - var ok = loadStates.FindAll(a => a.State); - var no = loadStates.FindAll(a => !a.State); - - if ((modes & PrintModes.Yes) == PrintModes.Yes) - { - if (ok.Count != 0) - { - sb.Append("** 这些文件加载成功!"); - foreach (var item in ok) - { - sb.Append(Environment.NewLine); - sb.Append("++ "); - sb.Append(item.DllFullName); - } - sb.Append(Environment.NewLine); - sb.Append(Environment.NewLine); - } - } - - if ((modes & PrintModes.No) == PrintModes.No) - { - if (no.Count != 0) - { - sb.Append("** 这些文件已被加载过,同时重复名称和版本号,跳过!"); - foreach (var item in no) - { - sb.Append(Environment.NewLine); - sb.Append("-- "); - sb.Append(item.DllFullName); - } - } - } - return sb.ToString(); - } - #endregion - - #region 删除文件 - /// - /// Debug的时候删除obj目录,防止占用 - /// - /// dll文件位置 - public void DebugDelObjFiles(string dllFullName) - { - var filename = Path.GetFileNameWithoutExtension(dllFullName); - var path = Path.GetDirectoryName(dllFullName); - - var pdb = path + "\\" + filename + ".pdb"; - if (File.Exists(pdb)) - File.Delete(pdb); - - var list = path.Split('\\'); - if (list[list.Length - 1] == "Debug" && list[list.Length - 2] == "bin") - { - var projobj = path.Substring(0, path.LastIndexOf("bin")) + "obj"; - FileEx.DeleteFolder(projobj); - } - } - #endregion - - #region 移动文件 - /// - /// Debug的时候移动obj目录,防止占用 - /// - public void DebugMoveObjFiles(string? dllFullName, Action action) - { - // 临时文件夹_pdb的,无论是否创建这里都应该进行删除 - const string Temp = "Temp"; - - string? temp_Pdb_dest = null; - string? temp_Pdb_source = null; - string? temp_Obj_dest = null; ; - string? temp_Obj_source = null; - try - { - var filename = Path.GetFileNameWithoutExtension(dllFullName); - var path = Path.GetDirectoryName(dllFullName); - - // 新建文件夹_临时目录 - temp_Pdb_dest = path + $"\\{Temp}\\"; - // 移动文件进去 - temp_Pdb_source = path + "\\" + filename + ".pdb"; - FileEx.MoveFolder(temp_Pdb_source, temp_Pdb_dest); - - // 检查是否存在obj文件夹,有就递归移动 - var list = path.Split('\\'); - if (list[list.Length - 1] == "Debug" && list[list.Length - 2] == "bin") - { - var proj = path.Substring(0, path.LastIndexOf("bin")); - temp_Obj_source = proj + "obj"; - temp_Obj_dest = proj + $"{Temp}"; - FileEx.MoveFolder(temp_Obj_source, temp_Obj_dest); - } - action.Invoke(); - } - finally - { - // 还原移动 - FileEx.MoveFolder(temp_Pdb_dest, temp_Pdb_source); - FileEx.DeleteFolder(temp_Pdb_dest); - - FileEx.MoveFolder(temp_Obj_dest, temp_Obj_source); - FileEx.DeleteFolder(temp_Obj_dest); - } - } - #endregion - - #region IDisposable接口相关函数 - public bool IsDisposed { get; private set; } = false; - - /// - /// 手动调用释放 - /// - public void Dispose() - { - Dispose(true); - GC.SuppressFinalize(this); - } - - /// - /// 析构函数调用释放 - /// - ~AssemblyDependent() - { - Dispose(false); - } - - protected virtual void Dispose(bool disposing) - { - // 不重复释放,并设置已经释放 - if (IsDisposed) return; - IsDisposed = true; - - CurrentDomainAssemblyResolveEvent -= AssemblyHelper.DefaultAssemblyResolve; - CurrentDomainReflectionOnlyAssemblyResolveEvent -= AssemblyHelper.ReflectionOnlyAssemblyResolve; - } - #endregion -} - -public enum PrintModes -{ - Yes = 1, - No = 2, - All = Yes | No, -} - -/// -/// 加载程序集和加载状态 -/// -public struct LoadState -{ - public Assembly? Assembly; - public string DllFullName; - public bool State; - public LoadState(string dllFullName, bool state, Assembly? assembly = null) - { - DllFullName = dllFullName; - State = state; - Assembly = assembly; - } -} - -public class FileEx -{ - /// - /// 判断含有文件名和后缀 - /// - /// 路径或者完整文件路径 - static bool ContainFileName(string? pathOrFile) - { - // 判断输入的是单文件,它可能不存在 - var a = Path.GetDirectoryName(pathOrFile); - var b = Path.GetFileName(pathOrFile); - var c = Path.GetExtension(pathOrFile); - // 是文件 - return a.Length > 0 && b.Length > 0 && c.Length > 0; - } - - /// - /// 移动文件夹中的所有文件夹与文件到另一个文件夹 - /// - /// 源文件夹 - /// 目标文件夹 - public static void MoveFolder(string? sourcePathOrFile, string? destPath) - { - if (sourcePathOrFile is null) - throw new ArgumentException(nameof(sourcePathOrFile)); - if (destPath is null) - throw new ArgumentException(nameof(destPath)); - - if (ContainFileName(destPath)) - destPath = Path.GetDirectoryName(destPath); - - // 目标目录不存在则创建 - if (!Directory.Exists(destPath)) - Directory.CreateDirectory(destPath); - - if (ContainFileName(sourcePathOrFile)) - { - // 如果是单个文件,就移动到目录就好了 - if (File.Exists(sourcePathOrFile)) - { - destPath += "\\" + Path.GetFileName(sourcePathOrFile); - File.Move(sourcePathOrFile, destPath); - return; - } - return; - } - - // 如果是文件就改为路径 - if (!Directory.Exists(sourcePathOrFile)) - { - sourcePathOrFile = Path.GetDirectoryName(sourcePathOrFile); - if (!Directory.Exists(sourcePathOrFile)) - throw new DirectoryNotFoundException("源目录不存在!"); - } - MoveFolder2(sourcePathOrFile, destPath); - } - - /// - /// 移动文件夹中的所有文件夹与文件到另一个文件夹 - /// - /// 源文件夹 - /// 目标文件夹 - static void MoveFolder2(string sourcePath, string destPath) - { - // 目标目录不存在则创建 - if (!Directory.Exists(destPath)) - Directory.CreateDirectory(destPath); - - // 获得源文件下所有文件 - var files = new List(Directory.GetFiles(sourcePath)); - files.ForEach(c => { - string destFile = Path.Combine(destPath, Path.GetFileName(c)); - // 覆盖模式 - if (File.Exists(destFile)) - try { File.Delete(destFile); } - catch { } - File.Move(c, destFile); - }); - // 获得源文件下所有目录文件 - List folders = new(Directory.GetDirectories(sourcePath)); - - folders.ForEach(c => { - string destDir = Path.Combine(destPath, Path.GetFileName(c)); - // Directory.Move必须要在同一个根目录下移动才有效,不能在不同卷中移动。 - // Directory.Move(c, destDir); - - // 采用递归的方法实现 - MoveFolder2(c, destDir); - }); - } - - /// - /// 递归删除文件夹目录及文件 - /// - /// - /// - public static void DeleteFolder(string? dir) - { - if (dir is null) - throw new ArgumentException(nameof(dir)); - if (!Directory.Exists(dir)) // 存在文件夹 - return; - var se = Directory.GetFileSystemEntries(dir); - for (int i = 0; i < se.Length; i++) - { - if (File.Exists(se[i])) - try { File.Delete(se[i]); /*直接删除其中的文件*/ } - catch { } - else - DeleteFolder(se[i]); // 递归删除子文件夹 - } - Directory.Delete(dir, true); // 删除已空文件夹 - } -} \ No newline at end of file diff --git a/src/IFoxCAD.LoadForm/DefaultAssemblyResolve.cs b/src/IFoxCAD.LoadForm/DefaultAssemblyResolve.cs deleted file mode 100644 index 0662dfd..0000000 --- a/src/IFoxCAD.LoadForm/DefaultAssemblyResolve.cs +++ /dev/null @@ -1,92 +0,0 @@ -namespace IFoxCAD.LoadEx; - -using System.Diagnostics; - -public class AssemblyHelper -{ - public static Assembly? ReflectionOnlyAssemblyResolve(object sender, ResolveEventArgs e) - { - var cadAss = AppDomain.CurrentDomain.ReflectionOnlyGetAssemblies(); - return Resolve(cadAss, sender, e); - } - - /// - /// - /// 程序域运行事件 - /// 这相当于是dll注入的意思,只是动态加载的这个"dll"不存在实体,只是一段内存. - /// 它总是被 事件使用 - /// 0x01 动态加载要注意所有的引用外的dll的加载顺序 - /// 0x02 指定版本: Acad2008若没有这个事件,会使动态命令执行时候无法引用当前的程序集函数 - /// 0x03 目录构成: 动态加载时,dll的地址会在系统的动态目录里,而它所处的程序集(运行域)是在动态目录里. - /// 0x04 命令构成: cad自带的netload会把所处的运行域给改到cad自己的,而动态加载不通过netload,所以要自己去改. - /// - /// - /// - /// - /// 程序集如果为空就不会调用 - public static Assembly? DefaultAssemblyResolve(object sender, ResolveEventArgs e) - { - var cadAss = AppDomain.CurrentDomain.GetAssemblies(); - return Resolve(cadAss, sender, e); - } - - public static Assembly? Resolve(Assembly[] cadAss, object sender, ResolveEventArgs e) - { - // 名称和版本号都一致的,调用它 - var result = cadAss.FirstOrDefault(ass => ass.GetName().FullName == e.Name); - if (result != null) - return result; - - // 获取名称一致,但是版本号不同的,调用最后的可用版本 - var ag = GetAssemblyName(e.Name); - // 获取最后一个符合条件的, - // 否则a.dll引用b.dll函数的时候,b.dll修改重生成之后,加载进去会调用第一个版本的b.dll, - // vs会迭代程序版本号的*,所以最后的可用就是循环到最后的. - for (int i = 0; i < cadAss.Length; i++) - if (GetAssemblyName(cadAss[i].GetName().FullName) == ag) - result = cadAss[i]; - - if (result == null) - { - // 惊惊: acad21+vs22 容易触发这个资源的问题 - // https://stackoverflow.com/questions/4368201/ - string[] fields = e.Name.Split(','); - string name = fields[0]; - string culture = fields[2]; - if (name.EndsWith(".resources") && !culture.EndsWith("neutral")) - return null; - } - - if (result == null) - { - // acad08debug的时候查看一些变量时候,会弹出找不到它,并且闪退 - if (ag == "Microsoft.CSharp") - return null; - } - - if (result == null) - { - var sb = new StringBuilder(); - sb.AppendLine($"{nameof(LoadEx)}------------------------------------------------------------"); - sb.AppendLine(nameof(DefaultAssemblyResolve) + "出错,程序集无法找到它"); - sb.AppendLine("++参数名:: " + GetAssemblyName(e.Name)); - sb.AppendLine("++参数完整信息:: " + e.Name); - for (int i = 0; i < cadAss.Length; i++) - sb.AppendLine("-------匹配对象:: " + GetAssemblyName(cadAss[i].GetName().FullName)); - - sb.AppendLine($"程序集找不到,遇到无法处理的错误,杀死当前进程!"); - sb.AppendLine($"{nameof(LoadEx)}------------------------------------------------------------"); - Debug.WriteLine(sb.ToString()); - - Process.GetCurrentProcess().Kill(); - //Debugger.Break(); - } - - return result; - } - - static string GetAssemblyName(string argString) - { - return argString.Substring(0, argString.IndexOf(',')); - } -} \ No newline at end of file diff --git a/src/IFoxCAD.LoadForm/GlobalUsings.cs b/src/IFoxCAD.LoadForm/GlobalUsings.cs deleted file mode 100644 index 5f10b33..0000000 --- a/src/IFoxCAD.LoadForm/GlobalUsings.cs +++ /dev/null @@ -1,16 +0,0 @@ -/// 系统引用 -global using System; -global using System.Collections.Generic; -global using System.IO; -global using System.Linq; -global using System.Text; -global using System.Reflection; -global using System.Runtime.CompilerServices; - -/// autocad 引用 -// global using Autodesk.AutoCAD.ApplicationServices; -// global using Autodesk.AutoCAD.Runtime; -// global using Acap = Autodesk.AutoCAD.ApplicationServices.Application; -// global using Autodesk.AutoCAD.EditorInput; - -global using HarmonyLib; diff --git a/src/IFoxCAD.LoadForm/IFoxCAD.LoadEx.csproj b/src/IFoxCAD.LoadForm/IFoxCAD.LoadEx.csproj deleted file mode 100644 index 176ef27..0000000 --- a/src/IFoxCAD.LoadForm/IFoxCAD.LoadEx.csproj +++ /dev/null @@ -1,30 +0,0 @@ - - - - preview - enable - - NET35;NET40;NET45 - true - true - True - - - - - - - - - - LoaderForm.cs - - - - - - LoaderForm.cs - - - - diff --git a/src/IFoxCAD.LoadForm/LoaderForm.Designer.cs b/src/IFoxCAD.LoadForm/LoaderForm.Designer.cs deleted file mode 100644 index 9123118..0000000 --- a/src/IFoxCAD.LoadForm/LoaderForm.Designer.cs +++ /dev/null @@ -1,136 +0,0 @@ -namespace IFoxCAD.LoadEx; - -partial class LoaderForm -{ - /// - /// Required designer variable. - /// - private System.ComponentModel.IContainer components = null; - - /// - /// Clean up any resources being used. - /// - /// true if managed resources should be disposed; otherwise, false. - protected override void Dispose(bool disposing) - { - if (disposing && (components != null)) - { - components.Dispose(); - } - base.Dispose(disposing); - } - - #region Windows Form Designer generated code - - /// - /// Required method for Designer support - do not modify - /// the contents of this method with the code editor. - /// - private void InitializeComponent() - { - this.components = new System.ComponentModel.Container(); - this.loadDlldialog = new System.Windows.Forms.OpenFileDialog(); - this.textBox1 = new System.Windows.Forms.TextBox(); - this.button1 = new System.Windows.Forms.Button(); - this.label1 = new System.Windows.Forms.Label(); - this.button2 = new System.Windows.Forms.Button(); - this.label2 = new System.Windows.Forms.Label(); - this.toolTip1 = new System.Windows.Forms.ToolTip(this.components); - this.SuspendLayout(); - // - // textBox1 - // - this.textBox1.Location = new System.Drawing.Point(80, 35); - this.textBox1.Name = "textBox1"; - this.textBox1.Size = new System.Drawing.Size(655, 29); - this.textBox1.TabIndex = 0; - this.textBox1.TextChanged += new System.EventHandler(this.TextBox1_TextChanged); - // - // button1 - // - this.button1.AutoSize = true; - this.button1.Location = new System.Drawing.Point(375, 86); - this.button1.Name = "button1"; - this.button1.Size = new System.Drawing.Size(177, 42); - this.button1.TabIndex = 1; - this.button1.Text = "手动选择DLL文件"; - this.button1.UseVisualStyleBackColor = true; - this.button1.Click += new System.EventHandler(this.Button1_Click); - // - // label1 - // - this.label1.AutoSize = true; - this.label1.Font = new System.Drawing.Font("微软雅黑", 12F, System.Drawing.FontStyle.Bold, System.Drawing.GraphicsUnit.Point, ((byte)(134))); - this.label1.ForeColor = System.Drawing.Color.Red; - this.label1.Location = new System.Drawing.Point(25, 97); - this.label1.Name = "label1"; - this.label1.Size = new System.Drawing.Size(344, 22); - this.label1.TabIndex = 2; - this.label1.Text = "直接拖拽DLL文件到本窗体可自动加载DLL文件"; - // - // button2 - // - this.button2.AutoSize = true; - this.button2.Location = new System.Drawing.Point(558, 86); - this.button2.Name = "button2"; - this.button2.Size = new System.Drawing.Size(177, 42); - this.button2.TabIndex = 1; - this.button2.Text = "加载"; - this.button2.UseVisualStyleBackColor = true; - this.button2.Click += new System.EventHandler(this.Button2_Click); - // - // label2 - // - this.label2.AutoSize = true; - this.label2.Location = new System.Drawing.Point(25, 38); - this.label2.Name = "label2"; - this.label2.Size = new System.Drawing.Size(46, 21); - this.label2.TabIndex = 3; - this.label2.Text = "路径:"; - // - // toolTip1 - // - this.toolTip1.AutomaticDelay = 200; - this.toolTip1.AutoPopDelay = 3000; - this.toolTip1.InitialDelay = 200; - this.toolTip1.ReshowDelay = 40; - this.toolTip1.ToolTipIcon = System.Windows.Forms.ToolTipIcon.Info; - // - // LoaderForm - // - this.AllowDrop = true; - this.AutoScaleDimensions = new System.Drawing.SizeF(10F, 21F); - this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; - this.ClientSize = new System.Drawing.Size(752, 140); - this.Controls.Add(this.label2); - this.Controls.Add(this.label1); - this.Controls.Add(this.button2); - this.Controls.Add(this.button1); - this.Controls.Add(this.textBox1); - this.Font = new System.Drawing.Font("微软雅黑", 12F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(134))); - this.FormBorderStyle = System.Windows.Forms.FormBorderStyle.FixedSingle; - this.Margin = new System.Windows.Forms.Padding(4, 5, 4, 5); - this.MaximizeBox = false; - this.MinimizeBox = false; - this.Name = "LoaderForm"; - this.StartPosition = System.Windows.Forms.FormStartPosition.CenterScreen; - this.Text = "netloadx .net dll 加载器"; - this.Load += new System.EventHandler(this.LoaderForm_Load); - this.DragDrop += new System.Windows.Forms.DragEventHandler(this.LoaderForm_DragDrop); - this.DragEnter += new System.Windows.Forms.DragEventHandler(this.LoaderForm_DragEnter); - this.KeyDown += new System.Windows.Forms.KeyEventHandler(this.LoaderForm_KeyDown); - this.ResumeLayout(false); - this.PerformLayout(); - - } - - #endregion - - private System.Windows.Forms.OpenFileDialog loadDlldialog; - private System.Windows.Forms.TextBox textBox1; - private System.Windows.Forms.Button button1; - private System.Windows.Forms.Label label1; - private System.Windows.Forms.Button button2; - private System.Windows.Forms.Label label2; - private System.Windows.Forms.ToolTip toolTip1; -} \ No newline at end of file diff --git a/src/IFoxCAD.LoadForm/LoaderForm.cs b/src/IFoxCAD.LoadForm/LoaderForm.cs deleted file mode 100644 index 57eca28..0000000 --- a/src/IFoxCAD.LoadForm/LoaderForm.cs +++ /dev/null @@ -1,107 +0,0 @@ -namespace IFoxCAD.LoadEx; - -using System.Windows.Forms; - -public partial class LoaderForm : Form -{ - public string? DllPath; - readonly AssemblyDependent _ad; - - public LoaderForm() - { - // Owner = form; - // MdiParent = form; - InitializeComponent(); - _ad = new(); - } - - void LoaderForm_Load(object sender, EventArgs e) - { - StartPosition = FormStartPosition.CenterScreen;// 在当前屏幕中央 - if (DllPath != null) - textBox1.Text = DllPath; - } - - void TextBox1_TextChanged(object sender, EventArgs e) - { - DllPath = textBox1.Text; - if (string.IsNullOrEmpty(DllPath?.Trim())) - return; - toolTip1.SetToolTip(textBox1, Path.GetFullPath(textBox1.Text)); - } - - void Button1_Click(object sender, EventArgs e) - { - loadDlldialog.Filter = "dll文件(*.dll)|*.dll"; - loadDlldialog.CheckFileExists = true; - - var dr = loadDlldialog.ShowDialog(); - if (dr == DialogResult.OK) - textBox1.Text = Path.GetFullPath(loadDlldialog.FileName); - } - - void Button2_Click(object sender, EventArgs e) - { - if (!File.Exists(textBox1.Text)) - { - MessageBox.Show($"文件 {textBox1.Text} 不存在!", "提示", MessageBoxButtons.OK); - return; - } - LoadDlls(new() { textBox1.Text }); - } - - // 鼠标拖拽文件到窗口 - void LoaderForm_DragDrop(object sender, DragEventArgs e) - { - var paths = (string[])e.Data.GetData(DataFormats.FileDrop); - - var pathHash = new HashSet(); - for (int i = 0; i < paths.Length; i++) - { - if (!paths[i].EndsWith(".dll", StringComparison.OrdinalIgnoreCase)) - continue; - pathHash.Add(paths[i]); - } - - if (pathHash.Count == 0) - return; - - DllPath = textBox1.Text = pathHash.First(); - LoadDlls(pathHash); - } - - /// - /// 加载插件 - /// - /// - void LoadDlls(HashSet paths) - { - if (paths.Count == 0) - return; - - List ls = new(); - foreach (var item in paths) - _ad.Load(item, ls); - var msg = AssemblyDependent.PrintMessage(ls); - if (msg != null) - MessageBox.Show(msg, "加载完毕!"); - else - MessageBox.Show("无任何信息", "加载出现问题!"); - } - - // 鼠标样式修改 - void LoaderForm_DragEnter(object sender, DragEventArgs e) - { - if (e.Data.GetDataPresent(DataFormats.FileDrop)) - e.Effect = DragDropEffects.All; - else - e.Effect = DragDropEffects.None; - } - - void LoaderForm_KeyDown(object sender, KeyEventArgs e) - { - if (e.KeyCode == Keys.Enter) - Button2_Click(sender, e); - } -} - diff --git a/src/IFoxCAD.LoadForm/LoaderForm.resx b/src/IFoxCAD.LoadForm/LoaderForm.resx deleted file mode 100644 index 9361c31..0000000 --- a/src/IFoxCAD.LoadForm/LoaderForm.resx +++ /dev/null @@ -1,126 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - text/microsoft-resx - - - 2.0 - - - System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - - - System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - - - 17, 17 - - - 172, 17 - - \ No newline at end of file diff --git a/src/IFoxCAD.LoadForm/TestNetLoad.cs b/src/IFoxCAD.LoadForm/TestNetLoad.cs deleted file mode 100644 index 4451f38..0000000 --- a/src/IFoxCAD.LoadForm/TestNetLoad.cs +++ /dev/null @@ -1,28 +0,0 @@ -#if test -namespace Test; - -public class NetLoad -{ - static IFoxCAD.LoadEx.LoaderForm? _form; - [CommandMethod(nameof(Loadx))] - public static void Loadx() - { - if (_form == null || _form.IsDisposed) - _form = new(); - -#if NET35 - _form.DllPath = "G:\\K01.惊惊连盒\\net35\\JoinBoxAcad.dll"; -#else - _form.DllPath = "G:\\K01.惊惊连盒\\net48\\JoinBoxAcad.dll"; -#endif - - if (!_form.Visible) - _form.Visible = true; - - _form.Show(); - _form.Focus(); - - // Acap.ShowModalDialog(_form); - } -} -#endif \ No newline at end of file -- Gitee From 14bd39f23c104ea7f4d806541bd9c7395297fb52 Mon Sep 17 00:00:00 2001 From: vicwjb Date: Wed, 4 Jan 2023 23:36:55 +0800 Subject: [PATCH 628/675] =?UTF-8?q?=E5=B0=86=E8=BE=93=E5=85=A5=E6=B3=95?= =?UTF-8?q?=E6=8B=A6=E6=88=AA=E4=BB=A3=E7=A0=81=E7=A7=BB=E8=87=B3ifoxcadto?= =?UTF-8?q?ols=E9=A1=B9=E7=9B=AE?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Cmd.cs" | 22 - .../DPI.cs" | 10 - .../DocReactor.cs" | 43 -- .../IMEControl.cs" | 573 ------------------ .../ResourceDictionary/ButtonDictionary.xaml" | 374 ------------ .../ComboboxDictionary.xaml" | 345 ----------- .../ComboboxitemDictionary.xaml" | 108 ---- .../ScrollviewDictionary.xaml" | 150 ----- .../TextboxDictionary.xaml" | 28 - .../Settings.cs" | 201 ------ .../ShowWPFWindowCentered.cs" | 31 - .../StatusBar.cs" | 116 ---- .../Window/SettingsWindow.xaml" | 52 -- .../Window/SettingsWindow.xaml.cs" | 41 -- 14 files changed, 2094 deletions(-) delete mode 100644 "tests/TestAcad09plus/\346\213\246\346\210\252\350\276\223\345\205\245\346\263\225/Cmd.cs" delete mode 100644 "tests/TestAcad09plus/\346\213\246\346\210\252\350\276\223\345\205\245\346\263\225/DPI.cs" delete mode 100644 "tests/TestAcad09plus/\346\213\246\346\210\252\350\276\223\345\205\245\346\263\225/DocReactor.cs" delete mode 100644 "tests/TestAcad09plus/\346\213\246\346\210\252\350\276\223\345\205\245\346\263\225/IMEControl.cs" delete mode 100644 "tests/TestAcad09plus/\346\213\246\346\210\252\350\276\223\345\205\245\346\263\225/ResourceDictionary/ButtonDictionary.xaml" delete mode 100644 "tests/TestAcad09plus/\346\213\246\346\210\252\350\276\223\345\205\245\346\263\225/ResourceDictionary/ComboboxDictionary.xaml" delete mode 100644 "tests/TestAcad09plus/\346\213\246\346\210\252\350\276\223\345\205\245\346\263\225/ResourceDictionary/ComboboxitemDictionary.xaml" delete mode 100644 "tests/TestAcad09plus/\346\213\246\346\210\252\350\276\223\345\205\245\346\263\225/ResourceDictionary/ScrollviewDictionary.xaml" delete mode 100644 "tests/TestAcad09plus/\346\213\246\346\210\252\350\276\223\345\205\245\346\263\225/ResourceDictionary/TextboxDictionary.xaml" delete mode 100644 "tests/TestAcad09plus/\346\213\246\346\210\252\350\276\223\345\205\245\346\263\225/Settings.cs" delete mode 100644 "tests/TestAcad09plus/\346\213\246\346\210\252\350\276\223\345\205\245\346\263\225/ShowWPFWindowCentered.cs" delete mode 100644 "tests/TestAcad09plus/\346\213\246\346\210\252\350\276\223\345\205\245\346\263\225/StatusBar.cs" delete mode 100644 "tests/TestAcad09plus/\346\213\246\346\210\252\350\276\223\345\205\245\346\263\225/Window/SettingsWindow.xaml" delete mode 100644 "tests/TestAcad09plus/\346\213\246\346\210\252\350\276\223\345\205\245\346\263\225/Window/SettingsWindow.xaml.cs" diff --git "a/tests/TestAcad09plus/\346\213\246\346\210\252\350\276\223\345\205\245\346\263\225/Cmd.cs" "b/tests/TestAcad09plus/\346\213\246\346\210\252\350\276\223\345\205\245\346\263\225/Cmd.cs" deleted file mode 100644 index 90c7bb7..0000000 --- "a/tests/TestAcad09plus/\346\213\246\346\210\252\350\276\223\345\205\245\346\263\225/Cmd.cs" +++ /dev/null @@ -1,22 +0,0 @@ -namespace Gstar_IMEFilter; - -public class Cmd -{ - [IFoxInitialize] - public void Initialize() - { - Env.Printl($"※拦截输入法控制※{nameof(Gstar_IMEFilterSettings)} - 设置"); - DocReactor.IntialReactor(); - Settings.LoadSettings(); - IMEControl.SetIMEHook(); - StatusBar.IMEAddPane(); - } - - [CommandMethod(nameof(Gstar_IMEFilterSettings))] - public void Gstar_IMEFilterSettings() - { - /*cad21若使用进程模式,则搜狗拦截不到,并且破坏了内存*/ - Autodesk.AutoCAD.Internal.Utils.SetFocusToDwgView(); - ShowWPFWindowCentered.Show(new SettingsWindow(), true); - } -} \ No newline at end of file diff --git "a/tests/TestAcad09plus/\346\213\246\346\210\252\350\276\223\345\205\245\346\263\225/DPI.cs" "b/tests/TestAcad09plus/\346\213\246\346\210\252\350\276\223\345\205\245\346\263\225/DPI.cs" deleted file mode 100644 index 065d200..0000000 --- "a/tests/TestAcad09plus/\346\213\246\346\210\252\350\276\223\345\205\245\346\263\225/DPI.cs" +++ /dev/null @@ -1,10 +0,0 @@ -using System.Drawing; -using System.Runtime.CompilerServices; - -namespace Gstar_IMEFilter; - -public class DPI -{ - [MethodImpl] - public static double CurrentDPI() => (double)Graphics.FromHwnd(IntPtr.Zero).DpiX / 96.0; -} \ No newline at end of file diff --git "a/tests/TestAcad09plus/\346\213\246\346\210\252\350\276\223\345\205\245\346\263\225/DocReactor.cs" "b/tests/TestAcad09plus/\346\213\246\346\210\252\350\276\223\345\205\245\346\263\225/DocReactor.cs" deleted file mode 100644 index bc2b514..0000000 --- "a/tests/TestAcad09plus/\346\213\246\346\210\252\350\276\223\345\205\245\346\263\225/DocReactor.cs" +++ /dev/null @@ -1,43 +0,0 @@ -namespace Gstar_IMEFilter; - -public class DocReactor -{ - internal static void IntialReactor() - { - var dm = Acap.DocumentManager; - // 现有文档 - foreach (Document doc in dm) - doc.CommandWillStart += CommandWillStart; - // 文档创建事件 - dm.DocumentCreated += DocumentCreated; - } - - internal static void RemoveReactor() - { - var dm = Acap.DocumentManager; - // 现有文档 - foreach (Document doc in dm) - doc.CommandWillStart -= CommandWillStart; - // 文档创建事件 - dm.DocumentCreated -= DocumentCreated; - } - - static void DocumentCreated(object sender, DocumentCollectionEventArgs e) - { - e.Document.CommandWillStart += CommandWillStart; - } - - static void CommandWillStart(object sender, CommandEventArgs e) - { - if (Settings.IMEInputSwitch == IMESwitchMode.Disable || - ((Document)sender).Editor.IsQuiescentForTransparentCommand()) - return; - - var gName = e.GlobalCommandName; - if (gName == "-HATCHEDIT" || gName == "UNDO") - return; - - // 此函数将焦点设置为视图: - Autodesk.AutoCAD.Internal.Utils.SetFocusToDwgView(); - } -} \ No newline at end of file diff --git "a/tests/TestAcad09plus/\346\213\246\346\210\252\350\276\223\345\205\245\346\263\225/IMEControl.cs" "b/tests/TestAcad09plus/\346\213\246\346\210\252\350\276\223\345\205\245\346\263\225/IMEControl.cs" deleted file mode 100644 index b54b48f..0000000 --- "a/tests/TestAcad09plus/\346\213\246\346\210\252\350\276\223\345\205\245\346\263\225/IMEControl.cs" +++ /dev/null @@ -1,573 +0,0 @@ -namespace Gstar_IMEFilter; - -using System.Diagnostics; -using System.Linq; -using System.Windows.Controls; -using System.Windows.Forms; -using Control = System.Windows.Forms.Control; - -public class IMEControl -{ - // 豁免命令组: 默认和配置的 - internal static HashSet DefaultCmds_AutoEn2Cn; - // 豁免命令组: 默认和配置的+用户面板输入的 - internal static HashSet ExceptCmds_AutoEn2Cn; - static string _ftFile_AutoEn2Cn; - - // 豁免命令组: 默认和配置的 - internal static HashSet DefaultCmds_AutoCn2En; - // 括免命令组: 自动切换为英文输入法 - internal static HashSet ExceptCmds_AutoCn2En; - static string _ftFile_AutoCn2En; - - static readonly Regex CMDReg = new("\\(C:.*\\)"); - /*某些窗口没有 WM_KEYDOWN 消息,就只有 WM_KEYUP 消息*/ - const int WM_KEYDOWN = 256; - const int WM_KEYUP = 257; - - static WindowsAPI.CallBack? _hookProc; - static IntPtr _nextHookProc; - static Process _process; - - // 优化内存,减少消息循环时候,频繁创建此类 - static StringBuilder _lpClassName = new(byte.MaxValue); - static string[] _separator = new string[] { "," }; - - - static IMEControl() - { - _nextHookProc = IntPtr.Zero; - _process = Process.GetCurrentProcess(); - WindowsAPI.CheckLowLevelHooksTimeout(); - - ExceptCmds_AutoEn2Cn = new(); - { - DefaultCmds_AutoEn2Cn = new() { "MTEXT", "DDEDIT", "MTEDIT", "TABLEDIT", "MLEADER", - "QLEADER", "MLEADERCONTENTEDIT", "MLEADEREDIT", "TEXTEDIT", "TEXT", "QLEADER" }; - string? lines = null; - _ftFile_AutoEn2Cn = Path.Combine(Settings.MyDir, nameof(Gstar_IMEFilter) + nameof(ExceptCmds_AutoEn2Cn) + ".ft"); - if (File.Exists(_ftFile_AutoEn2Cn)) - lines = File.ReadAllText(_ftFile_AutoEn2Cn, Encoding.UTF8); - else - Debugx.Printl("配置文件丢失: " + _ftFile_AutoEn2Cn); - if (lines != null) - { - var ls = lines.Split(_separator, StringSplitOptions.RemoveEmptyEntries); - for (int i = 0; i < ls.Length; i++) - DefaultCmds_AutoEn2Cn.Add(ls[i]); - } - } - ExceptCmds_AutoCn2En = new(); - { - DefaultCmds_AutoCn2En = new() { "BLOCK", "GROUP" }; - string? lines = null; - _ftFile_AutoCn2En = Path.Combine(Settings.MyDir, nameof(Gstar_IMEFilter) + nameof(ExceptCmds_AutoCn2En) + ".ft"); - if (File.Exists(_ftFile_AutoCn2En)) - lines = File.ReadAllText(_ftFile_AutoCn2En, Encoding.UTF8); - else - Debugx.Printl("配置文件丢失: " + _ftFile_AutoCn2En); - if (lines != null) - { - var ls = lines.Split(_separator, StringSplitOptions.RemoveEmptyEntries); - for (int i = 0; i < ls.Length; i++) - DefaultCmds_AutoCn2En.Add(ls[i]); - } - } - - // 命令反应器 - var dm = Acap.DocumentManager; - if (dm.Count != 0) - foreach (Document doc in dm) - { - doc.CommandWillStart += Doc_CommandWillStart; - doc.CommandEnded += Doc_CommandEnded; - } - - // 卸载钩子 - Acap.QuitWillStart += (s, e) => { - IMEControl.UnIMEHook(); - IMEControl.SaveFt(); - }; - } - - public static void SaveFt() - { - HashSet lst = new(); - foreach (var item in ExceptCmds_AutoEn2Cn) - if (!DefaultCmds_AutoEn2Cn.Contains(item)) - lst.Add(item); - if (lst.Any()) - { - var jo = string.Join(",", lst.ToArray()); - File.WriteAllText(_ftFile_AutoEn2Cn, jo, Encoding.UTF8); - } - - lst.Clear(); - foreach (var item in ExceptCmds_AutoCn2En) - if (!DefaultCmds_AutoCn2En.Contains(item)) - lst.Add(item); - if (lst.Any()) - { - var jo = string.Join(",", lst.ToArray()); - File.WriteAllText(_ftFile_AutoCn2En, jo, Encoding.UTF8); - } - } - - #region 切换输入法 - // 关键字问题: - // 是中文输入,自动切换到英文 - // 命令行监控服务 实例 得到命令行监控 - // var commandLineMonitor = CommandLineMonitorServices.Instance().GetCommandLineMonitor(doc); - - /// - /// 此状态用于豁免命令图中自动切换到中文,
    - /// 命令中依然能够切换到英文输入,
    - /// 命令结束时候从 命令结束反应器 恢复拦截
    - ///
    - static LoopState _sendKeyState = new(); - - // 命令结束反应器 - static void Doc_CommandEnded(object sender, CommandEventArgs e) - { - /* - * 英文状态 IsStop - * 英文状态和被程序切换 IsStop && IsExceptional - * 中文状态 IsBreak - * 中文状态和被程序切换 IsBreak && IsExceptional - * 保持不变 IsCancel - * 钩子不走任何 !Run 状态 - */ - // 如果程序切换了,就恢复原本的 - // 当前是英文状态被切换到中文(当前),{然后用户切换了英文,此时应该保证是用户},而不是发送切换(会这样变成中文) - if (ExceptCmds_AutoEn2Cn.Contains(e.GlobalCommandName)) - { - if (_sendKeyState.IsStop && _sendKeyState.IsExceptional) - { - if (IsOpenIEM()) - { - SendKey(); - Debugx.Printl("恢复}", false); - } - else - { - Debugx.Printl("不恢复}"); - } - } - _sendKeyState.Reset(); - } - if (ExceptCmds_AutoCn2En.Contains(e.GlobalCommandName)) - { - if (_sendKeyState.IsBreak && _sendKeyState.IsExceptional) - { - if (!IsOpenIEM()) - { - SendKey(); - Debugx.Printl("恢复}"); - } - else - { - Debugx.Printl("不恢复}"); - } - } - _sendKeyState.Reset(); - } - } - // 命令开始反应器 - static void Doc_CommandWillStart(object sender, CommandEventArgs e) - { - if (ExceptCmds_AutoCn2En.Contains(e.GlobalCommandName)) - { - IMESwitch_AutoCn2En(); - return; - } - } - - - /// - /// 如果是中文输入法状态就是true - /// - /// - static bool IsOpenIEM() - { - var focusW = WindowsAPI.GetForegroundWindow(); - var context = WindowsAPI.ImmGetContext(focusW); - WindowsAPI.ImmGetConversionStatus(context, out int mode/*输入模式*/, out _); - return WindowsAPI.ImmGetOpenStatus(context); - } - - /// - /// 切换输入法(英文状态就切到中文) - /// - static void IMESwitch_AutoEn2Cn() - { - if (Settings.IMEInputSwitch == IMESwitchMode.NotSwitch) - return; - // 切换只能发生在第一次,第2.+次需要不执行 - if (!IsOpenIEM()) - { - Debugx.Printl("现在是英文状态,切换前{"); - _sendKeyState.Stop(); - _sendKeyState.Exceptional(); - SendKey(); - } - else - { - // 中文状态虽然不变, - // 但是为了避免命令中用户手动切换 中文切换到英文,然后再触发上面 英文状态转中文逻辑, - // 所以此处也要设置状态 - Debugx.Printl("现在是中文状态,保持不变"); - _sendKeyState.Cancel(); - } - } - - /// - /// 切换输入法(中文状态就切到英文) - /// - static void IMESwitch_AutoCn2En() - { - if (Settings.IMEInputSwitch == IMESwitchMode.NotSwitch) - return; - // 切换只能发生在第一次,第2.+次需要不执行 - if (!IsOpenIEM()) - { - Debugx.Printl("现在是英文状态,保持不变"); - _sendKeyState.Cancel(); - } - else - { - Debugx.Printl("现在是中文状态,切换前{"); - _sendKeyState.Break(); - _sendKeyState.Exceptional(); - SendKey(); - } - } - - static void SendKey() - { - Debugx.Printl("触发了切换输入法", false); - switch (Settings.IMEInputSwitch) - { - case IMESwitchMode.Shift: - { - WindowsAPI.KeybdEvent(16, 0, 0, 0); - WindowsAPI.KeybdEvent(16, 0, 2, 0); - } - break; - case IMESwitchMode.Ctrl: - { - WindowsAPI.KeybdEvent(17, 0, 0, 0); - WindowsAPI.KeybdEvent(17, 0, 2, 0); - } - break; - case IMESwitchMode.CtrlAndSpace: - { - WindowsAPI.KeybdEvent(17, 0, 0, 0); - WindowsAPI.KeybdEvent(32, 0, 0, 0); - WindowsAPI.KeybdEvent(32, 0, 2, 0); - WindowsAPI.KeybdEvent(17, 0, 2, 0); - if (WindowsAPI.GetKeyState(20) == 1) - { - WindowsAPI.KeybdEvent(20, 0, 0, 0); - WindowsAPI.KeybdEvent(20, 0, 2, 0); - } - } - break; - case IMESwitchMode.CtrlAndShift: - { - WindowsAPI.KeybdEvent(16, 0, 0, 0); - WindowsAPI.KeybdEvent(17, 0, 0, 0); - WindowsAPI.KeybdEvent(17, 0, 2, 0); - WindowsAPI.KeybdEvent(16, 0, 2, 0); - if (WindowsAPI.GetKeyState(20) == 1) - { - WindowsAPI.KeybdEvent(20, 0, 0, 0); - WindowsAPI.KeybdEvent(20, 0, 2, 0); - } - } - break; - case IMESwitchMode.WinAndSpace: - { - WindowsAPI.KeybdEvent(91, 0, 0, 0); - WindowsAPI.KeybdEvent(32, 0, 0, 0); - WindowsAPI.KeybdEvent(32, 0, 2, 0); - WindowsAPI.KeybdEvent(91, 0, 2, 0); - if (WindowsAPI.GetKeyState(20) == 1) - { - WindowsAPI.KeybdEvent(20, 0, 0, 0); - WindowsAPI.KeybdEvent(20, 0, 2, 0); - } - } - break; - } - } - #endregion - - /// - /// 设置钩子 - /// - internal static void SetIMEHook() - { - UnIMEHook(); - if (_nextHookProc != IntPtr.Zero) - return; - - #region 读取配置 - ExceptCmds_AutoEn2Cn.Clear(); - { - foreach (var item in DefaultCmds_AutoEn2Cn) - ExceptCmds_AutoEn2Cn.Add(item); - var ss = Settings.UserFilter_AutoEn2Cn.Split(_separator, StringSplitOptions.RemoveEmptyEntries); - foreach (var item in ss) - ExceptCmds_AutoEn2Cn.Add(item); - } - - ExceptCmds_AutoCn2En.Clear(); - { - foreach (var item in DefaultCmds_AutoCn2En) - ExceptCmds_AutoCn2En.Add(item); - var ss = Settings.UserFilter_AutoCn2En.Split(_separator, StringSplitOptions.RemoveEmptyEntries); - foreach (var item in ss) - ExceptCmds_AutoCn2En.Add(item); - } - #endregion - - if (Settings.IMEHookStyle == IMEHookStyle.Process) - { - Debugx.Printl($"切换到进程钩子控制:{DateTime.Now}"); - _hookProc = (nCode, wParam, lParam) => { - if (nCode >= 0) - { - // 高版本cad基本上不能用进程钩子: - // 搜狗输入法如果连续按着,那么此时拦截失效 - var lp = lParam.ToInt64(); - if ((lp > 0) && ((lp & 0xC0000001) == 1))//按下某个键 - { - // 如果是ctrl就跳过 - if (Control.ModifierKeys == Keys.None || Control.ModifierKeys == Keys.Shift) - { - Debugx.Printl($"进程钩子按了这个{Control.ModifierKeys}^{DateTime.Now}"); - if (IMEHook(nCode, wParam, lParam)) - { - Debugx.Printl($"进程钩子拦截成功^{DateTime.Now}"); - return (IntPtr)1; - } - } - } - } - return WindowsAPI.CallNextHookEx(_nextHookProc, nCode, wParam, lParam); - }; - _nextHookProc = WindowsAPI.SetWindowsHookEx(HookType.WH_KEYBOARD, _hookProc, - IntPtr.Zero, WindowsAPI.GetCurrentThreadId()); - return; - } - - if (Settings.IMEHookStyle == IMEHookStyle.Global) - { - Debugx.Printl($"切换到全局钩子控制:{DateTime.Now}"); - var moduleHandle = WindowsAPI.GetModuleHandle(_process.MainModule.ModuleName); - _hookProc = (nCode, wParam, lParam) => { - if (nCode >= 0) - { - if (!MK1(wParam) && Mk2(nCode, wParam, lParam)) - return (IntPtr)1; - } - return WindowsAPI.CallNextHookEx(_nextHookProc, nCode, wParam, lParam); - }; - _nextHookProc = WindowsAPI.SetWindowsHookEx(HookType.WH_KEYBOARD_LL, - _hookProc, moduleHandle, 0); - return; - } - } - - /// - /// 豁免命令处理 - /// - /// - static bool ExceptCmds_AutoEn2Cn_Task() - { - var dm = Acap.DocumentManager; - if (dm.Count == 0) - return false; - var doc = dm.MdiActiveDocument; - if (doc == null) - return false; - if (!WindowsAPI.IsWindowEnabled(Acap.MainWindow.Handle)) - return false; - - // 豁免命令进行中输入会触发,实现在豁免命令中允许输入法 - string input = doc.CommandInProgress; - Match match = CMDReg.Match(input); - if (match.Success) - input = input.Substring(checked(match.Index + 3), checked(match.Length - 4)); - - if (ExceptCmds_AutoEn2Cn.Contains(input.ToUpper())) - { - IMESwitch_AutoEn2Cn(); - return false; - } - return true; - } - - /// - /// 钩子的消息处理 - /// r - /// - /// - /// - /// false不终止回调,true终止回调 - public static bool IMEHook(int nCode, int wParam, IntPtr lParam) - { - if (_sendKeyState.IsExceptional) - return false; - - // 键盘按键值 - if ((65 <= wParam && wParam <= 90/*a~z*/) || - (48 <= wParam && wParam <= 57/*数字键*/) || - (96 <= wParam && wParam <= 105/*小数字键盘数字*/) || - wParam == 27/*esc*/ || - wParam == 32/*空格*/ || - wParam == 13/*回车,大小回车都是它*/ || - wParam == 186/*;*/ || - wParam == 187/*=*/ || - wParam == 188/*,*/ || - wParam == 189/*-*/ || - wParam == 190/*.*/ || - wParam == 191/*?*/ || - wParam == 192/*`~*/ || - wParam == 219/*[*/ || - wParam == 220/*\*/ || - wParam == 221/*]*/ || - wParam == 222/*'*/ || - wParam == 223 || - wParam == 110/*小数字键盘.*/) - { - //Debugx.Printl(wParam); - - // 先判断键入的数字更快,再判断豁免命令 - if (!ExceptCmds_AutoEn2Cn_Task()) - return false; - - var focus = WindowsAPI.GetFocus(); - WindowsAPI.GetClassName(focus, _lpClassName, checked(_lpClassName.Capacity + 1)); - string left = _lpClassName.ToString().ToLower(); - if (left.StartsWith("afx"))// 在08输入的都从这里进入 - { - { - var focusW = WindowsAPI.GetForegroundWindow(); - WindowsAPI.GetClassName(focusW, _lpClassName, checked(_lpClassName.Capacity + 1)); - left = _lpClassName.ToString().ToLower(); - // cad08启动时候会滚动某些信息,此时鼠标狂点入到vs代码编辑器中,然后等一段时间cad完成,vs就会无法输入了. - // 会被拦截到了这个"afx"处理,所有的输入都跑cad了,需要加入如下代码进行处理: - // 狂点鼠标进入vs是 hwndwrapper - // 狂点鼠标进入qq是 txguifoundation - // Debugx.Printl($"afx...{left}...{DateTime.Now}"); - if (!left.StartsWith("afx")) - { - Debugx.Printl($"afx...拦截...{DateTime.Now}"); - return false; - } - } - WindowsAPI.PostMessage(focus, WM_KEYDOWN, new IntPtr(wParam), new IntPtr(0x10001)); - return true; - } - - if (left.StartsWith("hwndwrapper"))//cad21会进入 - { - Debugx.Printl($"hwndwrapper::{DateTime.Now}"); - - var parent = WindowsAPI.GetParent(focus); - if (parent == IntPtr.Zero) - return false; - StringBuilder lpString = new(byte.MaxValue); - WindowsAPI.GetWindowText(parent, lpString, checked(lpString.Capacity + 1)); - if (lpString.ToString().ToLower() != "cli palette")//"CLI Palette".ToLower() - { - WindowsAPI.GetClassName(parent, _lpClassName, checked(_lpClassName.Capacity + 1)); - if (!_lpClassName.ToString().ToLower().StartsWith("afxmdiframe")) - return false; - } - WindowsAPI.PostMessage(focus, WM_KEYUP, new IntPtr(wParam), new IntPtr(0x10001)); - return true; - } - - if (left.StartsWith("edit")) - { - Debugx.Printl($"edit::{DateTime.Now}"); - - var parent = WindowsAPI.GetParent(focus); - WindowsAPI.GetClassName(parent, _lpClassName, checked(_lpClassName.Capacity + 1)); - - var dm = Acap.DocumentManager; - var doc = dm.MdiActiveDocument; - if (_lpClassName.ToString().ToLower().StartsWith("afx") && - WindowsAPI.GetParent(parent) != doc.Window.Handle) - { - WindowsAPI.PostMessage(focus, WM_KEYDOWN, new IntPtr(wParam), new IntPtr(0x390001/*3735553*/)); - return true; - } - } - - if (left == "cicerouiwndframe") - { - Debugx.Printl($"cicerouiwndframe::{DateTime.Now}"); - return true; - } - } - return false; - } - - static bool MK1(int wParam) - { - return Settings.IMEInputSwitch == IMESwitchMode.Disable || - WindowsAPI.IsIconic(Acap.MainWindow.Handle.ToInt32()) || - WindowsAPI.GetKeyState(91) < 0 || - WindowsAPI.GetKeyState(92) < 0 || - wParam != WM_KEYDOWN; - } - - static bool Mk2(int nCode, int wParam, IntPtr lParam) - { - IntPtr focus; - if (Marshal.SizeOf(typeof(IntPtr)) == 4) - focus = WindowsAPI.GetFocus(); - else - focus = WindowsAPI.GetForegroundWindow(); - - WindowsAPI.GetWindowThreadProcessId(focus, out uint lpdwProcessId); - if (lpdwProcessId != _process.Id) - return false; - - WindowsAPI.KeyboardHookStruct? key = null; - if (Control.ModifierKeys == Keys.None) - { - key = WindowsAPI.KeyboardHookStruct.Create(lParam); - if (WindowsAPI.GetKeyState(162) < 0 || - WindowsAPI.GetKeyState(163) < 0 || - WindowsAPI.GetKeyState(17) < 0 || - WindowsAPI.GetKeyState(262144/*alt键*/) < 0) - return false; - - if (IMEHook(nCode, key.Value.VkCode, IntPtr.Zero)) - return true; - } - else if (Control.ModifierKeys == Keys.Shift) - { - key ??= WindowsAPI.KeyboardHookStruct.Create(lParam); - if (IMEHook(nCode, key.Value.VkCode, IntPtr.Zero)) - return true; - } - return false; - } - - /// - /// 卸载钩子 - /// - internal static void UnIMEHook() - { - if (_nextHookProc != IntPtr.Zero) - { - WindowsAPI.UnhookWindowsHookEx(_nextHookProc); - _nextHookProc = IntPtr.Zero; - } - } -} \ No newline at end of file diff --git "a/tests/TestAcad09plus/\346\213\246\346\210\252\350\276\223\345\205\245\346\263\225/ResourceDictionary/ButtonDictionary.xaml" "b/tests/TestAcad09plus/\346\213\246\346\210\252\350\276\223\345\205\245\346\263\225/ResourceDictionary/ButtonDictionary.xaml" deleted file mode 100644 index 50e85e1..0000000 --- "a/tests/TestAcad09plus/\346\213\246\346\210\252\350\276\223\345\205\245\346\263\225/ResourceDictionary/ButtonDictionary.xaml" +++ /dev/null @@ -1,374 +0,0 @@ - - - - - - - - - - \ No newline at end of file diff --git "a/tests/TestAcad09plus/\346\213\246\346\210\252\350\276\223\345\205\245\346\263\225/ResourceDictionary/ComboboxDictionary.xaml" "b/tests/TestAcad09plus/\346\213\246\346\210\252\350\276\223\345\205\245\346\263\225/ResourceDictionary/ComboboxDictionary.xaml" deleted file mode 100644 index 7a7848a..0000000 --- "a/tests/TestAcad09plus/\346\213\246\346\210\252\350\276\223\345\205\245\346\263\225/ResourceDictionary/ComboboxDictionary.xaml" +++ /dev/null @@ -1,345 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git "a/tests/TestAcad09plus/\346\213\246\346\210\252\350\276\223\345\205\245\346\263\225/ResourceDictionary/ComboboxitemDictionary.xaml" "b/tests/TestAcad09plus/\346\213\246\346\210\252\350\276\223\345\205\245\346\263\225/ResourceDictionary/ComboboxitemDictionary.xaml" deleted file mode 100644 index 05b89cb..0000000 --- "a/tests/TestAcad09plus/\346\213\246\346\210\252\350\276\223\345\205\245\346\263\225/ResourceDictionary/ComboboxitemDictionary.xaml" +++ /dev/null @@ -1,108 +0,0 @@ - - - - - - - - - - - - - - \ No newline at end of file diff --git "a/tests/TestAcad09plus/\346\213\246\346\210\252\350\276\223\345\205\245\346\263\225/ResourceDictionary/ScrollviewDictionary.xaml" "b/tests/TestAcad09plus/\346\213\246\346\210\252\350\276\223\345\205\245\346\263\225/ResourceDictionary/ScrollviewDictionary.xaml" deleted file mode 100644 index e19dfd8..0000000 --- "a/tests/TestAcad09plus/\346\213\246\346\210\252\350\276\223\345\205\245\346\263\225/ResourceDictionary/ScrollviewDictionary.xaml" +++ /dev/null @@ -1,150 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git "a/tests/TestAcad09plus/\346\213\246\346\210\252\350\276\223\345\205\245\346\263\225/ResourceDictionary/TextboxDictionary.xaml" "b/tests/TestAcad09plus/\346\213\246\346\210\252\350\276\223\345\205\245\346\263\225/ResourceDictionary/TextboxDictionary.xaml" deleted file mode 100644 index 76d441c..0000000 --- "a/tests/TestAcad09plus/\346\213\246\346\210\252\350\276\223\345\205\245\346\263\225/ResourceDictionary/TextboxDictionary.xaml" +++ /dev/null @@ -1,28 +0,0 @@ - - - \ No newline at end of file diff --git "a/tests/TestAcad09plus/\346\213\246\346\210\252\350\276\223\345\205\245\346\263\225/Settings.cs" "b/tests/TestAcad09plus/\346\213\246\346\210\252\350\276\223\345\205\245\346\263\225/Settings.cs" deleted file mode 100644 index 1dc84b0..0000000 --- "a/tests/TestAcad09plus/\346\213\246\346\210\252\350\276\223\345\205\245\346\263\225/Settings.cs" +++ /dev/null @@ -1,201 +0,0 @@ -using System.Diagnostics; -using System.Xml; - -namespace Gstar_IMEFilter; -public class Settings -{ - static string _MyDir = ""; - internal static string MyDir - { - get - { - if (_MyDir.Length == 0) - _MyDir = Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location); - return _MyDir; - } - } - - static string _MySettingsPath = ""; - public static string MySettingsPath - { - get - { - if (_MySettingsPath.Length == 0) - _MySettingsPath = Path.Combine(MyDir, nameof(Gstar_IMEFilter) + ".xml"); - return _MySettingsPath; - } - } - - internal static string _UserFilter_AutoEn2Cn = ""; - public static string UserFilter_AutoEn2Cn - { - get => _UserFilter_AutoEn2Cn; - set - { - if (_UserFilter_AutoEn2Cn.Length == 0) - return; - _UserFilter_AutoEn2Cn = value; - SaveSettings(); - } - } - - internal static string _UserFilter_AutoCn2En = ""; - public static string UserFilter_AutoCn2En - { - get => _UserFilter_AutoCn2En; - set - { - if (_UserFilter_AutoCn2En.Length == 0) - return; - _UserFilter_AutoCn2En = value; - SaveSettings(); - } - } - - internal static IMEHookStyle _IMEHookStyle = IMEHookStyle.Global; - public static IMEHookStyle IMEHookStyle - { - get => _IMEHookStyle; - set - { - if (_IMEHookStyle == value) - return; - _IMEHookStyle = value; - SaveSettings(); - IMEControl.SetIMEHook(); - } - } - - internal static IMESwitchMode _IMEInputSwitch = IMESwitchMode.Shift; - public static IMESwitchMode IMEInputSwitch - { - get => _IMEInputSwitch; - set - { - if (_IMEInputSwitch == value) - return; - _IMEInputSwitch = value; - SaveSettings(); - } - } - - public static void LoadSettings() - { - if (!File.Exists(MySettingsPath)) - return; - - try - { - using var xmlReader = XmlReader.Create(MySettingsPath); - while (xmlReader.Read()) - { - if (xmlReader.NodeType != XmlNodeType.Element) - continue; - string left = xmlReader.Name.ToLower(); - switch (left) - { - case nameof(UserFilter_AutoEn2Cn): - { - _UserFilter_AutoEn2Cn = xmlReader.ReadInnerXml().ToUpper(); - } - break; - case nameof(UserFilter_AutoCn2En): - { - _UserFilter_AutoCn2En = xmlReader.ReadInnerXml().ToUpper(); - } - break; - case nameof(IMEHookStyle): - { - int.TryParse(xmlReader.ReadInnerXml(), out int ime); - _IMEHookStyle = (IMEHookStyle)ime; - } - break; - case nameof(IMEInputSwitch): - { - int.TryParse(xmlReader.ReadInnerXml(), out int ime); - _IMEInputSwitch = (IMESwitchMode)ime; - } - break; - } - } - } - catch (Exception ex) - { - Debugger.Break(); - throw ex; - } - } - - internal static void SaveSettings() - { - try - { - XmlWriterSettings settings = new() - { - Indent = 1 != 0, - NewLineChars = Environment.NewLine - }; - - using var xmlWriter = XmlWriter.Create(MySettingsPath, settings); - xmlWriter.WriteStartDocument(1 != 0); - xmlWriter.WriteComment("拦截输入法"); - - xmlWriter.WriteStartElement(nameof(Settings)); - { - xmlWriter.WriteStartElement(nameof(UserFilter_AutoEn2Cn)); - xmlWriter.WriteString(UserFilter_AutoEn2Cn); - xmlWriter.WriteEndElement(); - - xmlWriter.WriteStartElement(nameof(UserFilter_AutoCn2En)); - xmlWriter.WriteString(UserFilter_AutoCn2En); - xmlWriter.WriteEndElement(); - - xmlWriter.WriteStartElement(nameof(IMEHookStyle)); - xmlWriter.WriteString(((int)IMEHookStyle).ToString()); - xmlWriter.WriteEndElement(); - - xmlWriter.WriteStartElement(nameof(IMEInputSwitch)); - xmlWriter.WriteString(((int)IMEInputSwitch).ToString()); - xmlWriter.WriteEndElement(); - } - xmlWriter.WriteEndElement(); - xmlWriter.WriteEndDocument(); - } - catch (Exception ex) - { - Debugger.Break(); - throw ex; - } - } -} - -/// -/// 钩子样式 -/// -public enum IMEHookStyle : byte -{ - Global,//全局钩子控制 - Process,//进程钩子控制 -} - -/// -/// 切换输入法方式
    -/// 作用的地方仅为输入豁免命令时候自动切换到中文 -///
    -public enum IMESwitchMode : byte -{ - [Description("输入拦截关闭")] - Disable, - [Description("输入拦截开启:不切换")] - NotSwitch, - [Description("输入拦截开启:Shift")] - Shift, - [Description("输入拦截开启:Ctrl")] - Ctrl, - [Description("输入拦截开启:CtrlAndSpace")] - CtrlAndSpace, - [Description("输入拦截开启:CtrlAndShift")] - CtrlAndShift, - [Description("输入拦截开启:WinAndSpace")] - WinAndSpace, -} \ No newline at end of file diff --git "a/tests/TestAcad09plus/\346\213\246\346\210\252\350\276\223\345\205\245\346\263\225/ShowWPFWindowCentered.cs" "b/tests/TestAcad09plus/\346\213\246\346\210\252\350\276\223\345\205\245\346\263\225/ShowWPFWindowCentered.cs" deleted file mode 100644 index 1599036..0000000 --- "a/tests/TestAcad09plus/\346\213\246\346\210\252\350\276\223\345\205\245\346\263\225/ShowWPFWindowCentered.cs" +++ /dev/null @@ -1,31 +0,0 @@ -using System.Windows; -using System.Windows.Interop; - -namespace Gstar_IMEFilter; - -public class ShowWPFWindowCentered -{ - internal static bool Show(Window window, bool modal) - { - new WindowInteropHelper(window).Owner = Acap.MainWindow.Handle; - WindowsAPI.IntRect lpRect = new(); - WindowsAPI.GetWindowRect(Acap.MainWindow.Handle, ref lpRect); - - var dpi = DPI.CurrentDPI(); - window.Left = (lpRect.Width / dpi - window.Width) / 2.0 - + lpRect.Left / dpi; - - window.Top = (lpRect.Height / dpi - window.Height) / 2.0 - + lpRect.Top / dpi; - - if (modal) - { - var flag = window.ShowDialog(); - if (flag is null) - return false; - return flag.Value; - } - window.Show(); - return false; - } -} \ No newline at end of file diff --git "a/tests/TestAcad09plus/\346\213\246\346\210\252\350\276\223\345\205\245\346\263\225/StatusBar.cs" "b/tests/TestAcad09plus/\346\213\246\346\210\252\350\276\223\345\205\245\346\263\225/StatusBar.cs" deleted file mode 100644 index 21b5c9a..0000000 --- "a/tests/TestAcad09plus/\346\213\246\346\210\252\350\276\223\345\205\245\346\263\225/StatusBar.cs" +++ /dev/null @@ -1,116 +0,0 @@ -namespace Gstar_IMEFilter; - -using Autodesk.AutoCAD.Windows; -using System.Windows.Forms; - -public class StatusBar -{ - static string _name = nameof(Gstar_IMEFilter); - static Pane? _pane = null; - - public static void IMEAddPane() - { - if (_pane is not null) - return; - - // 遍历当前 - var panes = Acap.StatusBar.Panes; - for (int i = 0; i < panes.Count(); i++) - { - if (panes[i].ToolTipText == _name) - { - _pane = panes[i]; - break; - } - } - - if (_pane is not null) - return; - - // 没有找到的话,进行初始化 - _pane = new() - { - ToolTipText = _name, - Text = GetUseText(), - Style = PaneStyles.Command | PaneStyles.PopUp, - /* PaneStyles.NoBorders | - * PaneStyles.Stretch | - * PaneStyles.PopOut - */ - }; - _pane.MouseDown += Pane_MouseDown; - panes.Insert(0, _pane); - Acap.StatusBar.Update(); - } - - public static void IMERemovePane() - { - if (_pane is null) - return; - - // cad08需要用这样的方式才能保证移除后更新界面 - var panes = Acap.StatusBar.Panes; - for (int i = panes.Count() - 1; i >= 0; i--) - if (panes[i] == _pane) - panes.RemoveAt(i); - _pane.Dispose(); - _pane = null; - - // cad08用这样的方式移除后无法更新界面,而高版本可以 - //Acap.StatusBar.Panes.Remove(_pane); - //Acap.StatusBar.Update(); - //_pane.Dispose(); - //_pane = null; - //Acap.UpdateScreen(); - //RedrawEx.Redraw(); - //System.Windows.Forms.Application.DoEvents(); - } - - static string GetUseText() - { - return EnumEx.GetDesc(Settings.IMEInputSwitch); - } - static readonly IMESwitchMode _ismMax = Enum.GetValues(typeof(IMESwitchMode)).Cast().Max(); - - static void Pane_MouseDown(object sender, StatusBarMouseDownEventArgs e) - { - if (_pane is null) - return; - - // 它就只支持两个枚举 - switch (e.Button) - { - case MouseButtons.Left: - { - // 防白痴,一个环形选择模式 - if (Settings.IMEInputSwitch < _ismMax) - ++Settings.IMEInputSwitch; - else - Settings.IMEInputSwitch = IMESwitchMode.Disable; - _pane.Text = GetUseText(); - Acap.StatusBar.Update(); - } - break; - case MouseButtons.Right: - { - // 右键可以直接关闭 - Settings.IMEInputSwitch = IMESwitchMode.Disable; - _pane.Text = GetUseText(); - Acap.StatusBar.Update(); - } - break; - } - } -} - -public static class PaneHelper -{ - public static int Count(this PaneCollection panes) - { -#if NET35 - return panes.get_Count(); -#else - return panes.Count; -#endif - } -} \ No newline at end of file diff --git "a/tests/TestAcad09plus/\346\213\246\346\210\252\350\276\223\345\205\245\346\263\225/Window/SettingsWindow.xaml" "b/tests/TestAcad09plus/\346\213\246\346\210\252\350\276\223\345\205\245\346\263\225/Window/SettingsWindow.xaml" deleted file mode 100644 index c0a0da8..0000000 --- "a/tests/TestAcad09plus/\346\213\246\346\210\252\350\276\223\345\205\245\346\263\225/Window/SettingsWindow.xaml" +++ /dev/null @@ -1,52 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git "a/tests/TestAcad09plus/\346\213\246\346\210\252\350\276\223\345\205\245\346\263\225/Window/SettingsWindow.xaml.cs" "b/tests/TestAcad09plus/\346\213\246\346\210\252\350\276\223\345\205\245\346\263\225/Window/SettingsWindow.xaml.cs" deleted file mode 100644 index cfc5e87..0000000 --- "a/tests/TestAcad09plus/\346\213\246\346\210\252\350\276\223\345\205\245\346\263\225/Window/SettingsWindow.xaml.cs" +++ /dev/null @@ -1,41 +0,0 @@ -using System.Windows; -using System.Windows.Input; - -namespace Gstar_IMEFilter; - -/// -/// SettingsWindow.xaml 的交互逻辑 -/// -public partial class SettingsWindow : Window -{ - public SettingsWindow() - { - InitializeComponent(); - Loaded += Window_Loaded; - PreviewKeyDown += Window_PreviewKeyDown; - } - - private void Button_Click(object sender, RoutedEventArgs e) - { - Settings._UserFilter_AutoEn2Cn = ExCMD.Text; - Settings._IMEHookStyle = (IMEHookStyle)CBox.SelectedIndex; - Settings.SaveSettings(); - IMEControl.SetIMEHook(); - IMEControl.SaveFt(); - DialogResult = true; - } - - private void Window_Loaded(object sender, RoutedEventArgs e) - { - CBox.SelectedIndex = (int)Settings.IMEHookStyle; - ExCMD.Text = Settings.UserFilter_AutoEn2Cn; - DeCMD.Text = string.Join(",", IMEControl.DefaultCmds_AutoEn2Cn.ToArray()); - } - - private void Window_PreviewKeyDown(object sender, KeyEventArgs e) - { - if (e.Key != Key.Escape) - return; - DialogResult = false; - } -} \ No newline at end of file -- Gitee From bc42759e35ac8f0cc3f837d9df8f741044e49cf5 Mon Sep 17 00:00:00 2001 From: vicwjb Date: Fri, 6 Jan 2023 00:16:18 +0800 Subject: [PATCH 629/675] =?UTF-8?q?=E8=B0=83=E6=95=B4=E6=9E=B6=E6=9E=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- IFoxCAD.sln | 75 +- .../CLS/Index.cs | 0 .../CLS/Range.cs | 0 .../CLS/RuntimeHelpers.cs | 0 .../CLS/TupleElementNamesAttribute.cs | 0 .../CLS/ValueTuple.cs | 0 .../General/ArrayEx.cs | 0 .../General/DebugHelper.cs | 0 .../General/DictEx.cs | 0 .../General/EnumEx.cs | 0 .../General/LinkedHashMap.cs | 0 .../General/LinkedHashSet.cs | 0 .../General/LinqEx.cs | 0 .../General/ListEx.cs | 0 .../General/LoopList.cs | 0 .../General/LoopState.cs | 0 .../General/RandomEx.cs | 0 .../General/Timer.cs | 0 .../IFox.Basal.Shared.projitems} | 2 +- .../IFox.Basal.Shared.shproj} | 2 +- .../Sortedset/ISet.cs | 0 .../Sortedset/SR.cs | 0 .../Sortedset/Sortedset.cs | 0 .../Sortedset/ThrowHelper.cs | 0 .../Sortedset/bithelper.cs | 0 .../IFox.Basal.Source.csproj} | 5 +- .../GlobalUsings.cs | 0 .../IFox.Basal.csproj} | 2 +- src/CAD/Directory.Build.props | 26 + src/CAD/IFox.CAD.ACAD/GlobalUsings.cs | 49 + src/CAD/IFox.CAD.ACAD/IFox.CAD.ACAD.csproj | 62 + src/CAD/IFox.CAD.GCAD/IFox.CAD.GCAD.csproj | 7 + .../IFox.CAD.Shared/Algorithms/Graph/Graph.cs | 656 +++++++ .../Algorithms/Graph/IGraph.cs | 98 ++ .../Algorithms/QuadTree/QuadEntity.cs | 25 + .../Algorithms/QuadTree/QuadTree.cs | 259 +++ .../Algorithms/QuadTree/QuadTreeEvn.cs | 26 + .../Algorithms/QuadTree/QuadTreeNode.cs | 816 +++++++++ .../Algorithms/QuadTree/QuadTreeSelectMode.cs | 21 + .../Algorithms/QuadTree/Rect.cs | 608 +++++++ .../IFox.CAD.Shared/Copyclip/BitmapTool.cs | 153 ++ src/CAD/IFox.CAD.Shared/Copyclip/EmfTool.cs | 922 ++++++++++ .../Copyclip/TagClipboardInfo.cs | 659 +++++++ .../ExtensionMethod/BulgeVertexWidth.cs | 85 + .../ExtensionMethod/CollectionEx.cs | 289 +++ .../ExtensionMethod/Curve2dEx.cs | 296 ++++ .../ExtensionMethod/Curve3dEx.cs | 556 ++++++ .../ExtensionMethod/CurveEx.cs | 723 ++++++++ .../ExtensionMethod/DBDictionaryEx.cs | 373 ++++ .../ExtensionMethod/DBObjectEx.cs | 153 ++ .../ExtensionMethod/DBTransEx.cs | 91 + .../ExtensionMethod/EditorEx.cs | 1170 ++++++++++++ .../ExtensionMethod/EntityBoundingInfo.cs | 281 +++ .../ExtensionMethod/EntityEx.cs | 474 +++++ .../IFox.CAD.Shared/ExtensionMethod/Enums.cs | 174 ++ .../ExtensionMethod/Filer/DwgFiler.cs | 506 ++++++ .../ExtensionMethod/Filer/DwgFilerEx.cs | 87 + .../ExtensionMethod/Filer/DxfFiler.cs | 221 +++ .../ExtensionMethod/GeometryEx.cs | 671 +++++++ .../IFox.CAD.Shared/ExtensionMethod/JigEx.cs | 407 +++++ .../ExtensionMethod/JigExTransient.cs | 149 ++ .../ExtensionMethod/JsonConverter.cs | 116 ++ .../IFox.CAD.Shared/ExtensionMethod/ObjEx.cs | 21 + .../ExtensionMethod/ObjectIdEx.cs | 98 ++ .../ExtensionMethod/PointEx.cs | 138 ++ .../ExtensionMethod/SelectionSetEx.cs | 150 ++ .../ExtensionMethod/SymbolTableEx.cs | 358 ++++ .../ExtensionMethod/SymbolTableRecordEx.cs | 521 ++++++ .../IFox.CAD.Shared/ExtensionMethod/Tools.cs | 60 + .../IFox.CAD.Shared/ExtensionMethod/XrefEx.cs | 656 +++++++ .../HatchConverter.cs" | 395 +++++ .../HatchEx.cs" | 15 + .../HatchInfo.cs" | 353 ++++ .../AttachmentPointHelper.cs" | 95 + .../TextEntityAdd.cs" | 62 + .../TextInfo.cs" | 178 ++ .../IFox.CAD.Shared/IFox.CAD.Shared.projitems | 90 + .../IFox.CAD.Shared/IFox.CAD.Shared.shproj | 13 + .../ResultData/LispDottedPair.cs | 68 + .../IFox.CAD.Shared/ResultData/LispList.cs | 194 ++ .../ResultData/TypedValueList.cs | 64 + .../ResultData/XRecordDataList.cs | 67 + .../IFox.CAD.Shared/ResultData/XdataList.cs | 146 ++ src/CAD/IFox.CAD.Shared/Runtime/AOP.cs | 99 ++ src/CAD/IFox.CAD.Shared/Runtime/AcadEMR.cs | 149 ++ .../IFox.CAD.Shared/Runtime/AcadVersion.cs | 70 + src/CAD/IFox.CAD.Shared/Runtime/AssemInfo.cs | 85 + .../IFox.CAD.Shared/Runtime/AutoRegAssem.cs | 180 ++ src/CAD/IFox.CAD.Shared/Runtime/CadVersion.cs | 92 + src/CAD/IFox.CAD.Shared/Runtime/DBTrans.cs | 711 ++++++++ src/CAD/IFox.CAD.Shared/Runtime/Env.cs | 749 ++++++++ .../IFox.CAD.Shared/Runtime/FileOpenMode.cs | 85 + src/CAD/IFox.CAD.Shared/Runtime/IAutoGo.cs | 403 +++++ .../IFox.CAD.Shared/Runtime/LateBinding.cs | 83 + src/CAD/IFox.CAD.Shared/Runtime/Log.cs | 453 +++++ .../Runtime/MethodInfoHelper.cs | 64 + .../IFox.CAD.Shared/Runtime/PE/AcadPeInfo.cs | 333 ++++ src/CAD/IFox.CAD.Shared/Runtime/PE/DBmod.cs | 92 + src/CAD/IFox.CAD.Shared/Runtime/PE/PostCmd.cs | 199 +++ .../IFox.CAD.Shared/Runtime/PE/ProgramPE.cs | 1567 +++++++++++++++++ src/CAD/IFox.CAD.Shared/Runtime/RedrawEx.cs | 191 ++ .../IFox.CAD.Shared/Runtime/SymbolTable.cs | 408 +++++ src/CAD/IFox.CAD.Shared/Runtime/Utils.cs | 98 ++ .../IFox.CAD.Shared/SelectionFilter/OpComp.cs | 79 + .../SelectionFilter/OpEqual.cs | 86 + .../SelectionFilter/OpFilter.cs | 341 ++++ .../IFox.CAD.Shared/SelectionFilter/OpList.cs | 167 ++ .../IFox.CAD.Shared/SelectionFilter/OpLogi.cs | 133 ++ src/CAD/IFox.CAD.Shared/WindowsAPI/Enums.cs | 1071 +++++++++++ .../IFox.CAD.Shared/WindowsAPI/MouseHook.cs | 293 +++ .../IFox.CAD.Shared/WindowsAPI/WindowsAPI.cs | 659 +++++++ .../IFox.CAD.Source/IFox.CAD.Source.csproj | 54 + src/CAD/IFox.CAD.ZCAD/IFox.CAD.ZCAD.csproj | 7 + .../IFoxCAD.Acad09plus.csproj | 2 +- 114 files changed, 24017 insertions(+), 23 deletions(-) rename src/Basal/{IFoxCAD.Basal.Shared => IFox.Basal.Shared}/CLS/Index.cs (100%) rename src/Basal/{IFoxCAD.Basal.Shared => IFox.Basal.Shared}/CLS/Range.cs (100%) rename src/Basal/{IFoxCAD.Basal.Shared => IFox.Basal.Shared}/CLS/RuntimeHelpers.cs (100%) rename src/Basal/{IFoxCAD.Basal.Shared => IFox.Basal.Shared}/CLS/TupleElementNamesAttribute.cs (100%) rename src/Basal/{IFoxCAD.Basal.Shared => IFox.Basal.Shared}/CLS/ValueTuple.cs (100%) rename src/Basal/{IFoxCAD.Basal.Shared => IFox.Basal.Shared}/General/ArrayEx.cs (100%) rename src/Basal/{IFoxCAD.Basal.Shared => IFox.Basal.Shared}/General/DebugHelper.cs (100%) rename src/Basal/{IFoxCAD.Basal.Shared => IFox.Basal.Shared}/General/DictEx.cs (100%) rename src/Basal/{IFoxCAD.Basal.Shared => IFox.Basal.Shared}/General/EnumEx.cs (100%) rename src/Basal/{IFoxCAD.Basal.Shared => IFox.Basal.Shared}/General/LinkedHashMap.cs (100%) rename src/Basal/{IFoxCAD.Basal.Shared => IFox.Basal.Shared}/General/LinkedHashSet.cs (100%) rename src/Basal/{IFoxCAD.Basal.Shared => IFox.Basal.Shared}/General/LinqEx.cs (100%) rename src/Basal/{IFoxCAD.Basal.Shared => IFox.Basal.Shared}/General/ListEx.cs (100%) rename src/Basal/{IFoxCAD.Basal.Shared => IFox.Basal.Shared}/General/LoopList.cs (100%) rename src/Basal/{IFoxCAD.Basal.Shared => IFox.Basal.Shared}/General/LoopState.cs (100%) rename src/Basal/{IFoxCAD.Basal.Shared => IFox.Basal.Shared}/General/RandomEx.cs (100%) rename src/Basal/{IFoxCAD.Basal.Shared => IFox.Basal.Shared}/General/Timer.cs (100%) rename src/Basal/{IFoxCAD.Basal.Shared/IFoxCAD.Basal.Shared.projitems => IFox.Basal.Shared/IFox.Basal.Shared.projitems} (96%) rename src/Basal/{IFoxCAD.Basal.Shared/IFoxCAD.Basal.Shared.shproj => IFox.Basal.Shared/IFox.Basal.Shared.shproj} (93%) rename src/Basal/{IFoxCAD.Basal.Shared => IFox.Basal.Shared}/Sortedset/ISet.cs (100%) rename src/Basal/{IFoxCAD.Basal.Shared => IFox.Basal.Shared}/Sortedset/SR.cs (100%) rename src/Basal/{IFoxCAD.Basal.Shared => IFox.Basal.Shared}/Sortedset/Sortedset.cs (100%) rename src/Basal/{IFoxCAD.Basal.Shared => IFox.Basal.Shared}/Sortedset/ThrowHelper.cs (100%) rename src/Basal/{IFoxCAD.Basal.Shared => IFox.Basal.Shared}/Sortedset/bithelper.cs (100%) rename src/Basal/{IFoxCAD.Basal.Source/IFoxCAD.Basal.Source.csproj => IFox.Basal.Source/IFox.Basal.Source.csproj} (90%) rename src/Basal/{IFoxCAD.Basal.ACAD => IFox.Basal}/GlobalUsings.cs (100%) rename src/Basal/{IFoxCAD.Basal.ACAD/IFoxCAD.Basal.ACAD.csproj => IFox.Basal/IFox.Basal.csproj} (75%) create mode 100644 src/CAD/Directory.Build.props create mode 100644 src/CAD/IFox.CAD.ACAD/GlobalUsings.cs create mode 100644 src/CAD/IFox.CAD.ACAD/IFox.CAD.ACAD.csproj create mode 100644 src/CAD/IFox.CAD.GCAD/IFox.CAD.GCAD.csproj create mode 100644 src/CAD/IFox.CAD.Shared/Algorithms/Graph/Graph.cs create mode 100644 src/CAD/IFox.CAD.Shared/Algorithms/Graph/IGraph.cs create mode 100644 src/CAD/IFox.CAD.Shared/Algorithms/QuadTree/QuadEntity.cs create mode 100644 src/CAD/IFox.CAD.Shared/Algorithms/QuadTree/QuadTree.cs create mode 100644 src/CAD/IFox.CAD.Shared/Algorithms/QuadTree/QuadTreeEvn.cs create mode 100644 src/CAD/IFox.CAD.Shared/Algorithms/QuadTree/QuadTreeNode.cs create mode 100644 src/CAD/IFox.CAD.Shared/Algorithms/QuadTree/QuadTreeSelectMode.cs create mode 100644 src/CAD/IFox.CAD.Shared/Algorithms/QuadTree/Rect.cs create mode 100644 src/CAD/IFox.CAD.Shared/Copyclip/BitmapTool.cs create mode 100644 src/CAD/IFox.CAD.Shared/Copyclip/EmfTool.cs create mode 100644 src/CAD/IFox.CAD.Shared/Copyclip/TagClipboardInfo.cs create mode 100644 src/CAD/IFox.CAD.Shared/ExtensionMethod/BulgeVertexWidth.cs create mode 100644 src/CAD/IFox.CAD.Shared/ExtensionMethod/CollectionEx.cs create mode 100644 src/CAD/IFox.CAD.Shared/ExtensionMethod/Curve2dEx.cs create mode 100644 src/CAD/IFox.CAD.Shared/ExtensionMethod/Curve3dEx.cs create mode 100644 src/CAD/IFox.CAD.Shared/ExtensionMethod/CurveEx.cs create mode 100644 src/CAD/IFox.CAD.Shared/ExtensionMethod/DBDictionaryEx.cs create mode 100644 src/CAD/IFox.CAD.Shared/ExtensionMethod/DBObjectEx.cs create mode 100644 src/CAD/IFox.CAD.Shared/ExtensionMethod/DBTransEx.cs create mode 100644 src/CAD/IFox.CAD.Shared/ExtensionMethod/EditorEx.cs create mode 100644 src/CAD/IFox.CAD.Shared/ExtensionMethod/EntityBoundingInfo.cs create mode 100644 src/CAD/IFox.CAD.Shared/ExtensionMethod/EntityEx.cs create mode 100644 src/CAD/IFox.CAD.Shared/ExtensionMethod/Enums.cs create mode 100644 src/CAD/IFox.CAD.Shared/ExtensionMethod/Filer/DwgFiler.cs create mode 100644 src/CAD/IFox.CAD.Shared/ExtensionMethod/Filer/DwgFilerEx.cs create mode 100644 src/CAD/IFox.CAD.Shared/ExtensionMethod/Filer/DxfFiler.cs create mode 100644 src/CAD/IFox.CAD.Shared/ExtensionMethod/GeometryEx.cs create mode 100644 src/CAD/IFox.CAD.Shared/ExtensionMethod/JigEx.cs create mode 100644 src/CAD/IFox.CAD.Shared/ExtensionMethod/JigExTransient.cs create mode 100644 src/CAD/IFox.CAD.Shared/ExtensionMethod/JsonConverter.cs create mode 100644 src/CAD/IFox.CAD.Shared/ExtensionMethod/ObjEx.cs create mode 100644 src/CAD/IFox.CAD.Shared/ExtensionMethod/ObjectIdEx.cs create mode 100644 src/CAD/IFox.CAD.Shared/ExtensionMethod/PointEx.cs create mode 100644 src/CAD/IFox.CAD.Shared/ExtensionMethod/SelectionSetEx.cs create mode 100644 src/CAD/IFox.CAD.Shared/ExtensionMethod/SymbolTableEx.cs create mode 100644 src/CAD/IFox.CAD.Shared/ExtensionMethod/SymbolTableRecordEx.cs create mode 100644 src/CAD/IFox.CAD.Shared/ExtensionMethod/Tools.cs create mode 100644 src/CAD/IFox.CAD.Shared/ExtensionMethod/XrefEx.cs create mode 100644 "src/CAD/IFox.CAD.Shared/ExtensionMethod/\346\226\260\345\273\272\345\241\253\345\205\205/HatchConverter.cs" create mode 100644 "src/CAD/IFox.CAD.Shared/ExtensionMethod/\346\226\260\345\273\272\345\241\253\345\205\205/HatchEx.cs" create mode 100644 "src/CAD/IFox.CAD.Shared/ExtensionMethod/\346\226\260\345\273\272\345\241\253\345\205\205/HatchInfo.cs" create mode 100644 "src/CAD/IFox.CAD.Shared/ExtensionMethod/\346\226\260\345\273\272\346\226\207\345\255\227/AttachmentPointHelper.cs" create mode 100644 "src/CAD/IFox.CAD.Shared/ExtensionMethod/\346\226\260\345\273\272\346\226\207\345\255\227/TextEntityAdd.cs" create mode 100644 "src/CAD/IFox.CAD.Shared/ExtensionMethod/\346\226\260\345\273\272\346\226\207\345\255\227/TextInfo.cs" create mode 100644 src/CAD/IFox.CAD.Shared/IFox.CAD.Shared.projitems create mode 100644 src/CAD/IFox.CAD.Shared/IFox.CAD.Shared.shproj create mode 100644 src/CAD/IFox.CAD.Shared/ResultData/LispDottedPair.cs create mode 100644 src/CAD/IFox.CAD.Shared/ResultData/LispList.cs create mode 100644 src/CAD/IFox.CAD.Shared/ResultData/TypedValueList.cs create mode 100644 src/CAD/IFox.CAD.Shared/ResultData/XRecordDataList.cs create mode 100644 src/CAD/IFox.CAD.Shared/ResultData/XdataList.cs create mode 100644 src/CAD/IFox.CAD.Shared/Runtime/AOP.cs create mode 100644 src/CAD/IFox.CAD.Shared/Runtime/AcadEMR.cs create mode 100644 src/CAD/IFox.CAD.Shared/Runtime/AcadVersion.cs create mode 100644 src/CAD/IFox.CAD.Shared/Runtime/AssemInfo.cs create mode 100644 src/CAD/IFox.CAD.Shared/Runtime/AutoRegAssem.cs create mode 100644 src/CAD/IFox.CAD.Shared/Runtime/CadVersion.cs create mode 100644 src/CAD/IFox.CAD.Shared/Runtime/DBTrans.cs create mode 100644 src/CAD/IFox.CAD.Shared/Runtime/Env.cs create mode 100644 src/CAD/IFox.CAD.Shared/Runtime/FileOpenMode.cs create mode 100644 src/CAD/IFox.CAD.Shared/Runtime/IAutoGo.cs create mode 100644 src/CAD/IFox.CAD.Shared/Runtime/LateBinding.cs create mode 100644 src/CAD/IFox.CAD.Shared/Runtime/Log.cs create mode 100644 src/CAD/IFox.CAD.Shared/Runtime/MethodInfoHelper.cs create mode 100644 src/CAD/IFox.CAD.Shared/Runtime/PE/AcadPeInfo.cs create mode 100644 src/CAD/IFox.CAD.Shared/Runtime/PE/DBmod.cs create mode 100644 src/CAD/IFox.CAD.Shared/Runtime/PE/PostCmd.cs create mode 100644 src/CAD/IFox.CAD.Shared/Runtime/PE/ProgramPE.cs create mode 100644 src/CAD/IFox.CAD.Shared/Runtime/RedrawEx.cs create mode 100644 src/CAD/IFox.CAD.Shared/Runtime/SymbolTable.cs create mode 100644 src/CAD/IFox.CAD.Shared/Runtime/Utils.cs create mode 100644 src/CAD/IFox.CAD.Shared/SelectionFilter/OpComp.cs create mode 100644 src/CAD/IFox.CAD.Shared/SelectionFilter/OpEqual.cs create mode 100644 src/CAD/IFox.CAD.Shared/SelectionFilter/OpFilter.cs create mode 100644 src/CAD/IFox.CAD.Shared/SelectionFilter/OpList.cs create mode 100644 src/CAD/IFox.CAD.Shared/SelectionFilter/OpLogi.cs create mode 100644 src/CAD/IFox.CAD.Shared/WindowsAPI/Enums.cs create mode 100644 src/CAD/IFox.CAD.Shared/WindowsAPI/MouseHook.cs create mode 100644 src/CAD/IFox.CAD.Shared/WindowsAPI/WindowsAPI.cs create mode 100644 src/CAD/IFox.CAD.Source/IFox.CAD.Source.csproj create mode 100644 src/CAD/IFox.CAD.ZCAD/IFox.CAD.ZCAD.csproj diff --git a/IFoxCAD.sln b/IFoxCAD.sln index df2e98f..37b66a4 100644 --- a/IFoxCAD.sln +++ b/IFoxCAD.sln @@ -37,12 +37,30 @@ EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "IFoxCAD.Zcad", "src\IFoxCAD.Cad\IFoxCAD.Zcad\IFoxCAD.Zcad.csproj", "{751D15FC-B664-4B71-BB99-529E22EE1C4F}" EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Basal", "Basal", "{1A5C27C1-FEF3-40A0-A1EE-3BC7BF2BD67A}" + ProjectSection(SolutionItems) = preProject + src\Basal\Directory.Build.props = src\Basal\Directory.Build.props + EndProjectSection +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "IFox.Basal", "src\Basal\IFox.Basal\IFox.Basal.csproj", "{FF4E1CDE-EEB3-4BE8-8C1D-6B5B17A299C7}" +EndProject +Project("{D954291E-2A0B-460D-934E-DC6B0785DB48}") = "IFox.Basal.Shared", "src\Basal\IFox.Basal.Shared\IFox.Basal.Shared.shproj", "{C823514A-2BC2-45C2-ACED-18924A7B80BF}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "IFox.Basal.Source", "src\Basal\IFox.Basal.Source\IFox.Basal.Source.csproj", "{D0C6824C-53A3-4C16-AE7B-3227F18C5039}" +EndProject +Project("{D954291E-2A0B-460D-934E-DC6B0785DB48}") = "IFox.CAD.Shared", "src\CAD\IFox.CAD.Shared\IFox.CAD.Shared.shproj", "{20F254F7-AEE5-42AE-A9B3-149BBDC7397F}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "CAD", "CAD", "{B2AB7DC7-DBF4-4197-808B-0D2A6613AF5E}" + ProjectSection(SolutionItems) = preProject + src\CAD\Directory.Build.props = src\CAD\Directory.Build.props + EndProjectSection +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "IFox.CAD.Source", "src\CAD\IFox.CAD.Source\IFox.CAD.Source.csproj", "{88F3CF78-23F5-494B-9D8F-2C6B4467E0B4}" EndProject -Project("{D954291E-2A0B-460D-934E-DC6B0785DB48}") = "IFoxCAD.Basal.Shared", "src\Basal\IFoxCAD.Basal.Shared\IFoxCAD.Basal.Shared.shproj", "{C823514A-2BC2-45C2-ACED-18924A7B80BF}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "IFox.CAD.ACAD", "src\CAD\IFox.CAD.ACAD\IFox.CAD.ACAD.csproj", "{EF20E632-70A9-40EB-8C9A-539FD85FAFAD}" EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "IFoxCAD.Basal.ACAD", "src\Basal\IFoxCAD.Basal.ACAD\IFoxCAD.Basal.ACAD.csproj", "{D1951425-0DE8-4DFB-B5F9-E79574D2F32D}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "IFox.CAD.GCAD", "src\CAD\IFox.CAD.GCAD\IFox.CAD.GCAD.csproj", "{F388F4B9-F636-414A-9BC9-2B008517B441}" EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "IFoxCAD.Basal.Source", "src\Basal\IFoxCAD.Basal.Source\IFoxCAD.Basal.Source.csproj", "{BF0A5851-549D-48AC-9EE6-2AB866090D24}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "IFox.CAD.ZCAD", "src\CAD\IFox.CAD.ZCAD\IFox.CAD.ZCAD.csproj", "{3D7D095D-EF0A-40B3-9776-5F08C61FD614}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution @@ -62,6 +80,8 @@ Global {36F87D06-88B3-45E3-A2A8-0FC737B25428}.Debug|Any CPU.Build.0 = Debug|Any CPU {36F87D06-88B3-45E3-A2A8-0FC737B25428}.Release|Any CPU.ActiveCfg = Release|Any CPU {36F87D06-88B3-45E3-A2A8-0FC737B25428}.Release|Any CPU.Build.0 = Release|Any CPU + {82FB8450-B971-4E30-859F-4B2DDB81F590}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {82FB8450-B971-4E30-859F-4B2DDB81F590}.Release|Any CPU.ActiveCfg = Release|Any CPU {CC46E4E2-90DF-48F5-B3D7-83FAFED58F2A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {CC46E4E2-90DF-48F5-B3D7-83FAFED58F2A}.Debug|Any CPU.Build.0 = Debug|Any CPU {CC46E4E2-90DF-48F5-B3D7-83FAFED58F2A}.Release|Any CPU.ActiveCfg = Release|Any CPU @@ -82,14 +102,30 @@ Global {751D15FC-B664-4B71-BB99-529E22EE1C4F}.Debug|Any CPU.Build.0 = Debug|Any CPU {751D15FC-B664-4B71-BB99-529E22EE1C4F}.Release|Any CPU.ActiveCfg = Release|Any CPU {751D15FC-B664-4B71-BB99-529E22EE1C4F}.Release|Any CPU.Build.0 = Release|Any CPU - {D1951425-0DE8-4DFB-B5F9-E79574D2F32D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {D1951425-0DE8-4DFB-B5F9-E79574D2F32D}.Debug|Any CPU.Build.0 = Debug|Any CPU - {D1951425-0DE8-4DFB-B5F9-E79574D2F32D}.Release|Any CPU.ActiveCfg = Release|Any CPU - {D1951425-0DE8-4DFB-B5F9-E79574D2F32D}.Release|Any CPU.Build.0 = Release|Any CPU - {BF0A5851-549D-48AC-9EE6-2AB866090D24}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {BF0A5851-549D-48AC-9EE6-2AB866090D24}.Debug|Any CPU.Build.0 = Debug|Any CPU - {BF0A5851-549D-48AC-9EE6-2AB866090D24}.Release|Any CPU.ActiveCfg = Release|Any CPU - {BF0A5851-549D-48AC-9EE6-2AB866090D24}.Release|Any CPU.Build.0 = Release|Any CPU + {FF4E1CDE-EEB3-4BE8-8C1D-6B5B17A299C7}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {FF4E1CDE-EEB3-4BE8-8C1D-6B5B17A299C7}.Debug|Any CPU.Build.0 = Debug|Any CPU + {FF4E1CDE-EEB3-4BE8-8C1D-6B5B17A299C7}.Release|Any CPU.ActiveCfg = Release|Any CPU + {FF4E1CDE-EEB3-4BE8-8C1D-6B5B17A299C7}.Release|Any CPU.Build.0 = Release|Any CPU + {D0C6824C-53A3-4C16-AE7B-3227F18C5039}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {D0C6824C-53A3-4C16-AE7B-3227F18C5039}.Debug|Any CPU.Build.0 = Debug|Any CPU + {D0C6824C-53A3-4C16-AE7B-3227F18C5039}.Release|Any CPU.ActiveCfg = Release|Any CPU + {D0C6824C-53A3-4C16-AE7B-3227F18C5039}.Release|Any CPU.Build.0 = Release|Any CPU + {88F3CF78-23F5-494B-9D8F-2C6B4467E0B4}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {88F3CF78-23F5-494B-9D8F-2C6B4467E0B4}.Debug|Any CPU.Build.0 = Debug|Any CPU + {88F3CF78-23F5-494B-9D8F-2C6B4467E0B4}.Release|Any CPU.ActiveCfg = Release|Any CPU + {88F3CF78-23F5-494B-9D8F-2C6B4467E0B4}.Release|Any CPU.Build.0 = Release|Any CPU + {EF20E632-70A9-40EB-8C9A-539FD85FAFAD}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {EF20E632-70A9-40EB-8C9A-539FD85FAFAD}.Debug|Any CPU.Build.0 = Debug|Any CPU + {EF20E632-70A9-40EB-8C9A-539FD85FAFAD}.Release|Any CPU.ActiveCfg = Release|Any CPU + {EF20E632-70A9-40EB-8C9A-539FD85FAFAD}.Release|Any CPU.Build.0 = Release|Any CPU + {F388F4B9-F636-414A-9BC9-2B008517B441}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {F388F4B9-F636-414A-9BC9-2B008517B441}.Debug|Any CPU.Build.0 = Debug|Any CPU + {F388F4B9-F636-414A-9BC9-2B008517B441}.Release|Any CPU.ActiveCfg = Release|Any CPU + {F388F4B9-F636-414A-9BC9-2B008517B441}.Release|Any CPU.Build.0 = Release|Any CPU + {3D7D095D-EF0A-40B3-9776-5F08C61FD614}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {3D7D095D-EF0A-40B3-9776-5F08C61FD614}.Debug|Any CPU.Build.0 = Debug|Any CPU + {3D7D095D-EF0A-40B3-9776-5F08C61FD614}.Release|Any CPU.ActiveCfg = Release|Any CPU + {3D7D095D-EF0A-40B3-9776-5F08C61FD614}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -104,24 +140,31 @@ Global {5F478F68-19BC-4A3A-AF39-8728F8D396DD} = {C0BEFC15-6DF7-4636-BC76-4A0B88231470} {D0CE63A9-2DBA-4263-9749-E8CD3EA0DB57} = {465C4E39-FBA2-417A-AB31-BDC01601F420} {751D15FC-B664-4B71-BB99-529E22EE1C4F} = {465C4E39-FBA2-417A-AB31-BDC01601F420} + {FF4E1CDE-EEB3-4BE8-8C1D-6B5B17A299C7} = {1A5C27C1-FEF3-40A0-A1EE-3BC7BF2BD67A} {C823514A-2BC2-45C2-ACED-18924A7B80BF} = {1A5C27C1-FEF3-40A0-A1EE-3BC7BF2BD67A} - {D1951425-0DE8-4DFB-B5F9-E79574D2F32D} = {1A5C27C1-FEF3-40A0-A1EE-3BC7BF2BD67A} - {BF0A5851-549D-48AC-9EE6-2AB866090D24} = {1A5C27C1-FEF3-40A0-A1EE-3BC7BF2BD67A} + {D0C6824C-53A3-4C16-AE7B-3227F18C5039} = {1A5C27C1-FEF3-40A0-A1EE-3BC7BF2BD67A} + {20F254F7-AEE5-42AE-A9B3-149BBDC7397F} = {B2AB7DC7-DBF4-4197-808B-0D2A6613AF5E} + {88F3CF78-23F5-494B-9D8F-2C6B4467E0B4} = {B2AB7DC7-DBF4-4197-808B-0D2A6613AF5E} + {EF20E632-70A9-40EB-8C9A-539FD85FAFAD} = {B2AB7DC7-DBF4-4197-808B-0D2A6613AF5E} + {F388F4B9-F636-414A-9BC9-2B008517B441} = {B2AB7DC7-DBF4-4197-808B-0D2A6613AF5E} + {3D7D095D-EF0A-40B3-9776-5F08C61FD614} = {B2AB7DC7-DBF4-4197-808B-0D2A6613AF5E} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {31D6C754-CF6B-4AB0-9861-6923DD312350} EndGlobalSection GlobalSection(SharedMSBuildProjectFiles) = preSolution + src\CAD\IFox.CAD.Shared\IFox.CAD.Shared.projitems*{20f254f7-aee5-42ae-a9b3-149bbdc7397f}*SharedItemsImports = 13 src\IFoxCAD.Cad.Shared\IFoxCAD.Cad.Shared.projitems*{36f87d06-88b3-45e3-a2a8-0fc737b25428}*SharedItemsImports = 5 tests\TestShared\TestShared.projitems*{5f478f68-19bc-4a3a-af39-8728f8d396dd}*SharedItemsImports = 5 src\IFoxCAD.Cad.Shared\IFoxCAD.Cad.Shared.projitems*{751d15fc-b664-4b71-bb99-529e22ee1c4f}*SharedItemsImports = 5 src\IFoxCAD.Cad.Shared\IFoxCAD.Cad.Shared.projitems*{82fb8450-b971-4e30-859f-4b2ddb81f590}*SharedItemsImports = 13 + src\CAD\IFox.CAD.Shared\IFox.CAD.Shared.projitems*{88f3cf78-23f5-494b-9d8f-2c6b4467e0b4}*SharedItemsImports = 5 tests\TestShared\TestShared.projitems*{8c573180-523d-427d-8030-caa7d6b5eb6b}*SharedItemsImports = 5 - src\Basal\IFoxCAD.Basal.Shared\IFoxCAD.Basal.Shared.projitems*{bf0a5851-549d-48ac-9ee6-2ab866090d24}*SharedItemsImports = 5 - src\Basal\IFoxCAD.Basal.Shared\IFoxCAD.Basal.Shared.projitems*{c823514a-2bc2-45c2-aced-18924a7b80bf}*SharedItemsImports = 13 + src\Basal\IFox.Basal.Shared\IFox.Basal.Shared.projitems*{c823514a-2bc2-45c2-aced-18924a7b80bf}*SharedItemsImports = 13 src\IFoxCAD.Cad.Shared\IFoxCAD.Cad.Shared.projitems*{cc46e4e2-90df-48f5-b3d7-83fafed58f2a}*SharedItemsImports = 5 tests\TestShared\TestShared.projitems*{ced63d2d-0af6-4831-806d-5e8e9b0d0a07}*SharedItemsImports = 13 + src\Basal\IFox.Basal.Shared\IFox.Basal.Shared.projitems*{d0c6824c-53a3-4c16-ae7b-3227f18c5039}*SharedItemsImports = 5 src\IFoxCAD.Cad.Shared\IFoxCAD.Cad.Shared.projitems*{d0ce63a9-2dba-4263-9749-e8cd3ea0db57}*SharedItemsImports = 5 - src\Basal\IFoxCAD.Basal.Shared\IFoxCAD.Basal.Shared.projitems*{d1951425-0de8-4dfb-b5f9-e79574d2f32d}*SharedItemsImports = 5 + src\Basal\IFox.Basal.Shared\IFox.Basal.Shared.projitems*{ff4e1cde-eeb3-4be8-8c1d-6b5b17a299c7}*SharedItemsImports = 5 EndGlobalSection EndGlobal diff --git a/src/Basal/IFoxCAD.Basal.Shared/CLS/Index.cs b/src/Basal/IFox.Basal.Shared/CLS/Index.cs similarity index 100% rename from src/Basal/IFoxCAD.Basal.Shared/CLS/Index.cs rename to src/Basal/IFox.Basal.Shared/CLS/Index.cs diff --git a/src/Basal/IFoxCAD.Basal.Shared/CLS/Range.cs b/src/Basal/IFox.Basal.Shared/CLS/Range.cs similarity index 100% rename from src/Basal/IFoxCAD.Basal.Shared/CLS/Range.cs rename to src/Basal/IFox.Basal.Shared/CLS/Range.cs diff --git a/src/Basal/IFoxCAD.Basal.Shared/CLS/RuntimeHelpers.cs b/src/Basal/IFox.Basal.Shared/CLS/RuntimeHelpers.cs similarity index 100% rename from src/Basal/IFoxCAD.Basal.Shared/CLS/RuntimeHelpers.cs rename to src/Basal/IFox.Basal.Shared/CLS/RuntimeHelpers.cs diff --git a/src/Basal/IFoxCAD.Basal.Shared/CLS/TupleElementNamesAttribute.cs b/src/Basal/IFox.Basal.Shared/CLS/TupleElementNamesAttribute.cs similarity index 100% rename from src/Basal/IFoxCAD.Basal.Shared/CLS/TupleElementNamesAttribute.cs rename to src/Basal/IFox.Basal.Shared/CLS/TupleElementNamesAttribute.cs diff --git a/src/Basal/IFoxCAD.Basal.Shared/CLS/ValueTuple.cs b/src/Basal/IFox.Basal.Shared/CLS/ValueTuple.cs similarity index 100% rename from src/Basal/IFoxCAD.Basal.Shared/CLS/ValueTuple.cs rename to src/Basal/IFox.Basal.Shared/CLS/ValueTuple.cs diff --git a/src/Basal/IFoxCAD.Basal.Shared/General/ArrayEx.cs b/src/Basal/IFox.Basal.Shared/General/ArrayEx.cs similarity index 100% rename from src/Basal/IFoxCAD.Basal.Shared/General/ArrayEx.cs rename to src/Basal/IFox.Basal.Shared/General/ArrayEx.cs diff --git a/src/Basal/IFoxCAD.Basal.Shared/General/DebugHelper.cs b/src/Basal/IFox.Basal.Shared/General/DebugHelper.cs similarity index 100% rename from src/Basal/IFoxCAD.Basal.Shared/General/DebugHelper.cs rename to src/Basal/IFox.Basal.Shared/General/DebugHelper.cs diff --git a/src/Basal/IFoxCAD.Basal.Shared/General/DictEx.cs b/src/Basal/IFox.Basal.Shared/General/DictEx.cs similarity index 100% rename from src/Basal/IFoxCAD.Basal.Shared/General/DictEx.cs rename to src/Basal/IFox.Basal.Shared/General/DictEx.cs diff --git a/src/Basal/IFoxCAD.Basal.Shared/General/EnumEx.cs b/src/Basal/IFox.Basal.Shared/General/EnumEx.cs similarity index 100% rename from src/Basal/IFoxCAD.Basal.Shared/General/EnumEx.cs rename to src/Basal/IFox.Basal.Shared/General/EnumEx.cs diff --git a/src/Basal/IFoxCAD.Basal.Shared/General/LinkedHashMap.cs b/src/Basal/IFox.Basal.Shared/General/LinkedHashMap.cs similarity index 100% rename from src/Basal/IFoxCAD.Basal.Shared/General/LinkedHashMap.cs rename to src/Basal/IFox.Basal.Shared/General/LinkedHashMap.cs diff --git a/src/Basal/IFoxCAD.Basal.Shared/General/LinkedHashSet.cs b/src/Basal/IFox.Basal.Shared/General/LinkedHashSet.cs similarity index 100% rename from src/Basal/IFoxCAD.Basal.Shared/General/LinkedHashSet.cs rename to src/Basal/IFox.Basal.Shared/General/LinkedHashSet.cs diff --git a/src/Basal/IFoxCAD.Basal.Shared/General/LinqEx.cs b/src/Basal/IFox.Basal.Shared/General/LinqEx.cs similarity index 100% rename from src/Basal/IFoxCAD.Basal.Shared/General/LinqEx.cs rename to src/Basal/IFox.Basal.Shared/General/LinqEx.cs diff --git a/src/Basal/IFoxCAD.Basal.Shared/General/ListEx.cs b/src/Basal/IFox.Basal.Shared/General/ListEx.cs similarity index 100% rename from src/Basal/IFoxCAD.Basal.Shared/General/ListEx.cs rename to src/Basal/IFox.Basal.Shared/General/ListEx.cs diff --git a/src/Basal/IFoxCAD.Basal.Shared/General/LoopList.cs b/src/Basal/IFox.Basal.Shared/General/LoopList.cs similarity index 100% rename from src/Basal/IFoxCAD.Basal.Shared/General/LoopList.cs rename to src/Basal/IFox.Basal.Shared/General/LoopList.cs diff --git a/src/Basal/IFoxCAD.Basal.Shared/General/LoopState.cs b/src/Basal/IFox.Basal.Shared/General/LoopState.cs similarity index 100% rename from src/Basal/IFoxCAD.Basal.Shared/General/LoopState.cs rename to src/Basal/IFox.Basal.Shared/General/LoopState.cs diff --git a/src/Basal/IFoxCAD.Basal.Shared/General/RandomEx.cs b/src/Basal/IFox.Basal.Shared/General/RandomEx.cs similarity index 100% rename from src/Basal/IFoxCAD.Basal.Shared/General/RandomEx.cs rename to src/Basal/IFox.Basal.Shared/General/RandomEx.cs diff --git a/src/Basal/IFoxCAD.Basal.Shared/General/Timer.cs b/src/Basal/IFox.Basal.Shared/General/Timer.cs similarity index 100% rename from src/Basal/IFoxCAD.Basal.Shared/General/Timer.cs rename to src/Basal/IFox.Basal.Shared/General/Timer.cs diff --git a/src/Basal/IFoxCAD.Basal.Shared/IFoxCAD.Basal.Shared.projitems b/src/Basal/IFox.Basal.Shared/IFox.Basal.Shared.projitems similarity index 96% rename from src/Basal/IFoxCAD.Basal.Shared/IFoxCAD.Basal.Shared.projitems rename to src/Basal/IFox.Basal.Shared/IFox.Basal.Shared.projitems index ddbbfb7..18c5d65 100644 --- a/src/Basal/IFoxCAD.Basal.Shared/IFoxCAD.Basal.Shared.projitems +++ b/src/Basal/IFox.Basal.Shared/IFox.Basal.Shared.projitems @@ -6,7 +6,7 @@ c823514a-2bc2-45c2-aced-18924a7b80bf - IFoxCAD.Basal.Shared + IFox.Basal.Shared diff --git a/src/Basal/IFoxCAD.Basal.Shared/IFoxCAD.Basal.Shared.shproj b/src/Basal/IFox.Basal.Shared/IFox.Basal.Shared.shproj similarity index 93% rename from src/Basal/IFoxCAD.Basal.Shared/IFoxCAD.Basal.Shared.shproj rename to src/Basal/IFox.Basal.Shared/IFox.Basal.Shared.shproj index 7432ebd..e04b5b6 100644 --- a/src/Basal/IFoxCAD.Basal.Shared/IFoxCAD.Basal.Shared.shproj +++ b/src/Basal/IFox.Basal.Shared/IFox.Basal.Shared.shproj @@ -8,6 +8,6 @@ - + diff --git a/src/Basal/IFoxCAD.Basal.Shared/Sortedset/ISet.cs b/src/Basal/IFox.Basal.Shared/Sortedset/ISet.cs similarity index 100% rename from src/Basal/IFoxCAD.Basal.Shared/Sortedset/ISet.cs rename to src/Basal/IFox.Basal.Shared/Sortedset/ISet.cs diff --git a/src/Basal/IFoxCAD.Basal.Shared/Sortedset/SR.cs b/src/Basal/IFox.Basal.Shared/Sortedset/SR.cs similarity index 100% rename from src/Basal/IFoxCAD.Basal.Shared/Sortedset/SR.cs rename to src/Basal/IFox.Basal.Shared/Sortedset/SR.cs diff --git a/src/Basal/IFoxCAD.Basal.Shared/Sortedset/Sortedset.cs b/src/Basal/IFox.Basal.Shared/Sortedset/Sortedset.cs similarity index 100% rename from src/Basal/IFoxCAD.Basal.Shared/Sortedset/Sortedset.cs rename to src/Basal/IFox.Basal.Shared/Sortedset/Sortedset.cs diff --git a/src/Basal/IFoxCAD.Basal.Shared/Sortedset/ThrowHelper.cs b/src/Basal/IFox.Basal.Shared/Sortedset/ThrowHelper.cs similarity index 100% rename from src/Basal/IFoxCAD.Basal.Shared/Sortedset/ThrowHelper.cs rename to src/Basal/IFox.Basal.Shared/Sortedset/ThrowHelper.cs diff --git a/src/Basal/IFoxCAD.Basal.Shared/Sortedset/bithelper.cs b/src/Basal/IFox.Basal.Shared/Sortedset/bithelper.cs similarity index 100% rename from src/Basal/IFoxCAD.Basal.Shared/Sortedset/bithelper.cs rename to src/Basal/IFox.Basal.Shared/Sortedset/bithelper.cs diff --git a/src/Basal/IFoxCAD.Basal.Source/IFoxCAD.Basal.Source.csproj b/src/Basal/IFox.Basal.Source/IFox.Basal.Source.csproj similarity index 90% rename from src/Basal/IFoxCAD.Basal.Source/IFoxCAD.Basal.Source.csproj rename to src/Basal/IFox.Basal.Source/IFox.Basal.Source.csproj index 9272ddf..9a26109 100644 --- a/src/Basal/IFoxCAD.Basal.Source/IFoxCAD.Basal.Source.csproj +++ b/src/Basal/IFox.Basal.Source/IFox.Basal.Source.csproj @@ -22,7 +22,7 @@ - + @@ -38,11 +38,10 @@ - + false - diff --git a/src/Basal/IFoxCAD.Basal.ACAD/GlobalUsings.cs b/src/Basal/IFox.Basal/GlobalUsings.cs similarity index 100% rename from src/Basal/IFoxCAD.Basal.ACAD/GlobalUsings.cs rename to src/Basal/IFox.Basal/GlobalUsings.cs diff --git a/src/Basal/IFoxCAD.Basal.ACAD/IFoxCAD.Basal.ACAD.csproj b/src/Basal/IFox.Basal/IFox.Basal.csproj similarity index 75% rename from src/Basal/IFoxCAD.Basal.ACAD/IFoxCAD.Basal.ACAD.csproj rename to src/Basal/IFox.Basal/IFox.Basal.csproj index 0f2eb78..4c0606d 100644 --- a/src/Basal/IFoxCAD.Basal.ACAD/IFoxCAD.Basal.ACAD.csproj +++ b/src/Basal/IFox.Basal/IFox.Basal.csproj @@ -7,7 +7,7 @@ None - + diff --git a/src/CAD/Directory.Build.props b/src/CAD/Directory.Build.props new file mode 100644 index 0000000..9c6e57f --- /dev/null +++ b/src/CAD/Directory.Build.props @@ -0,0 +1,26 @@ + + + + 0.5.0-alpha4 + 支持源码包。 + + + + preview + enable + true + InspireFunction + xsfhlzh;vicwjb;liuqihong + InspireFunction + 基于.NET的二次开发基本类库. + MIT + true + https://gitee.com/inspirefunction/ifoxcad + https://gitee.com/inspirefunction/ifoxcad.git + git + IFoxCAD;CAD;AutoCad;C#;NET;GStarCAD;ZWCAD + $(MSBuildThisFileDirectory)bin\$(Configuration)\ + + + + \ No newline at end of file diff --git a/src/CAD/IFox.CAD.ACAD/GlobalUsings.cs b/src/CAD/IFox.CAD.ACAD/GlobalUsings.cs new file mode 100644 index 0000000..4227a00 --- /dev/null +++ b/src/CAD/IFox.CAD.ACAD/GlobalUsings.cs @@ -0,0 +1,49 @@ +/// 系统引用 +global using System; +global using System.Collections; +global using System.Collections.Generic; +global using System.IO; +global using System.Linq; +global using System.Text; +global using System.Reflection; +global using System.Text.RegularExpressions; +global using Microsoft.Win32; +global using System.ComponentModel; +global using System.Runtime.InteropServices; +global using System.Collections.Specialized; + +global using Exception = System.Exception; + +global using Registry = Microsoft.Win32.Registry; +global using RegistryKey = Microsoft.Win32.RegistryKey; + +/// cad 引用 +global using Autodesk.AutoCAD.ApplicationServices; +global using Autodesk.AutoCAD.EditorInput; +global using Autodesk.AutoCAD.Colors; +global using Autodesk.AutoCAD.DatabaseServices; +global using Autodesk.AutoCAD.Geometry; +global using Autodesk.AutoCAD.Runtime; +global using Acap = Autodesk.AutoCAD.ApplicationServices.Application; +global using Acgi = Autodesk.AutoCAD.GraphicsInterface; + +global using Autodesk.AutoCAD.DatabaseServices.Filters; +global using Autodesk.AutoCAD; + +// jig命名空间会引起Viewport/Polyline等等重义,最好逐个引入 using Autodesk.AutoCAD.GraphicsInterface +global using Autodesk.AutoCAD.GraphicsInterface; +global using WorldDraw = Autodesk.AutoCAD.GraphicsInterface.WorldDraw; +global using Manager = Autodesk.AutoCAD.GraphicsSystem.Manager; +global using Group = Autodesk.AutoCAD.DatabaseServices.Group; +global using Viewport = Autodesk.AutoCAD.DatabaseServices.Viewport; +global using Polyline = Autodesk.AutoCAD.DatabaseServices.Polyline; +global using Cad_DwgFiler = Autodesk.AutoCAD.DatabaseServices.DwgFiler; +global using Cad_DxfFiler = Autodesk.AutoCAD.DatabaseServices.DxfFiler; +global using Cad_ErrorStatus = Autodesk.AutoCAD.Runtime.ErrorStatus; + +/// ifoxcad.basal 引用 +global using IFoxCAD.Basal; + +#if !NewtonsoftJson +global using System.Web.Script.Serialization; +#endif \ No newline at end of file diff --git a/src/CAD/IFox.CAD.ACAD/IFox.CAD.ACAD.csproj b/src/CAD/IFox.CAD.ACAD/IFox.CAD.ACAD.csproj new file mode 100644 index 0000000..e95415e --- /dev/null +++ b/src/CAD/IFox.CAD.ACAD/IFox.CAD.ACAD.csproj @@ -0,0 +1,62 @@ + + + + NET35;NET40;NET45 + + + + + + + true + true + + + + + True + + + + + DEBUG + + + $(Configuration);acad;ac2009 + + + $(Configuration);acad;ac2013 + + + $(Configuration);acad;ac2015 + + + + + + + + + + + + + + + + True + + + + + + + + + + + diff --git a/src/CAD/IFox.CAD.GCAD/IFox.CAD.GCAD.csproj b/src/CAD/IFox.CAD.GCAD/IFox.CAD.GCAD.csproj new file mode 100644 index 0000000..9f5c4f4 --- /dev/null +++ b/src/CAD/IFox.CAD.GCAD/IFox.CAD.GCAD.csproj @@ -0,0 +1,7 @@ + + + + netstandard2.0 + + + diff --git a/src/CAD/IFox.CAD.Shared/Algorithms/Graph/Graph.cs b/src/CAD/IFox.CAD.Shared/Algorithms/Graph/Graph.cs new file mode 100644 index 0000000..255d2b3 --- /dev/null +++ b/src/CAD/IFox.CAD.Shared/Algorithms/Graph/Graph.cs @@ -0,0 +1,656 @@ +namespace IFoxCAD.Cad; +using Exception = System.Exception; + +/// +/// 无权无向图实现 +/// IEnumerable 枚举所有顶点; +/// +public sealed class Graph : IGraph, IEnumerable +{ + #region 字段及属性 + /// + /// 存储所有节点的字典,key为顶点的类型,value为邻接表,类型是hashset,不可重复添加点 + /// + /// + readonly Dictionary> vertices = new(); + /// + /// 邻接边表,key为顶点的类型,value为邻接边表,类型是hashset,不可重复添加边 + /// + readonly Dictionary> edges = new(); + /// + /// 为加快索引,引入hash检索 + /// + readonly Dictionary vertexs = new(); + + public int VerticesCount => vertices.Count; + + /// + /// Returns a reference vertex. + /// Time complexity: O(1). + /// + private IGraphVertex? ReferenceVertex + { + get + { + using (var enumerator = vertexs.GetEnumerator()) + { + if (enumerator.MoveNext()) + { + return enumerator.Current.Value; + } + } + + return null; + } + } + IGraphVertex? IGraph.ReferenceVertex => ReferenceVertex; + /// + /// 目前点增加点的顺序号,这个点号不随删点而减少的 + /// + private int insertCount; + #endregion + + #region 构造函数 + public Graph() + { + insertCount = 0; // 每次新建对象就将顶点顺序号归零 + } + #endregion + + #region 顶点及边_增 + /// + /// 向该图添加一个新顶点,但是无边; + /// + /// 点 + /// 创建的顶点 + public IGraphVertex AddVertex(Point3d pt) + { + var str = pt.GetHashString(); + if (vertexs.ContainsKey(str)) + return vertexs[str]; + + var vertex = new GraphVertex(pt, insertCount++); + vertices.Add(vertex, new HashSet()); + edges.Add(vertex, new HashSet()); + + vertexs[str] = vertex; + + return vertex; + } + + /// + /// 向该图添加一个边; + /// + /// + public void AddEdge(Curve3d curve) + { + if (curve == null) + throw new ArgumentNullException(nameof(curve)); + + var start = AddVertex(curve.StartPoint); + var end = AddVertex(curve.EndPoint); + + // 添加起点的邻接表和邻接边 + vertices[start].Add(end); + edges[start].Add(new GraphEdge(end, curve)); + + // 为了保证点顺序,每个点的邻接边必须按起点-终点,所以添加曲线终点时,将添加一个方向的曲线 + var curtmp = (Curve3d)curve.Clone(); + curtmp = curtmp.GetReverseParameterCurve(); + + // 添加终点的邻接表和邻接边 + vertices[end].Add(start); + edges[end].Add(new GraphEdge(start, curtmp)); + } + #endregion + + #region 顶点及边_删 + /// + /// 从此图中删除现有顶点; + /// + /// 点 + public void RemoveVertex(Point3d pt) + { + var str = pt.GetHashString(); + if (vertexs.ContainsKey(str)) + { + var vertex = vertexs[str]; + + // 删除邻接表里的vertex点,先删除后面的遍历可以少一轮 + vertices.Remove(vertex!); + + // 删除其他顶点的邻接表里的vertex点 + foreach (var item in vertices.Values) + item.Remove(vertex!); + + // 删除邻接边表里的vertex点,先删除后面的遍历可以少一轮 + edges.Remove(vertex!); + + // 删除其他顶点的邻接边表的指向vertex的边 + foreach (var item in edges.Values) + { + item.RemoveWhere(x => vertex.Equals(x.TargetVertex)); + // foreach (var edge in item) + // { + // if (vertex.Equals(edge.TargetVertex)) + // item.Remove(edge); + // } + } + vertexs.Remove(str); + } + } + + /// + /// 从此图中删除一条边; + /// + /// 曲线 + public void RemoveEdge(Curve3d curve) + { + if (curve == null) + throw new ArgumentNullException(nameof(curve)); + + RemoveVertex(curve.StartPoint); + RemoveVertex(curve.EndPoint); + } + #endregion + + #region 顶点和边_查 + /// + /// 我们在给定的来源和目的地之间是否有边? + /// + /// 起点 + /// 终点 + /// 有边返回 ,反之返回 + public bool HasEdge(IGraphVertex source, IGraphVertex dest) + { + if (!vertices.ContainsKey(source) || !vertices.ContainsKey(dest)) + throw new ArgumentException("源或目标不在此图中;"); + + foreach (var item in edges[source]) + { + if (item.TargetVertex == dest) + return true; + } + return false; + } + + /// + /// 获取边 + /// + /// 起点 + /// 终点 + /// + /// 传入的点不在图中时抛出参数异常 + public IEdge? GetEdge(IGraphVertex source, IGraphVertex dest) + { + if (!vertices.ContainsKey(source) || !vertices.ContainsKey(dest)) + throw new ArgumentException("源或目标不在此图中;"); + + foreach (var item in edges[source]) + { + if (item.TargetVertex.Equals(dest)) + return item; + } + return null; + } + + /// + /// 是否存在顶点,此函数目前未发现有啥用 + /// + /// 顶点 + /// 存在顶点返回 ,反之返回 + public bool ContainsVertex(IGraphVertex value) + { + return vertices.ContainsKey(value); + } + #endregion + + #region 获取邻接表和曲线 + /// + /// 获取顶点的邻接表 + /// + /// 顶点 + /// 邻接表 + public HashSet GetAdjacencyList(IGraphVertex vertex) + { + return vertices[vertex]; + } + + /// + /// 获取顶点的邻接边表 + /// + /// 顶点 + /// 邻接边表 + public HashSet GetAdjacencyEdge(IGraphVertex vertex) + { + return edges[vertex]; + } + + /// + /// 根据顶点表获取曲线集合 + /// + /// 顶点表 + /// 曲线表 + public List GetCurves(List graphVertices) + { + var curves = new List(); + for (int i = 0; i < graphVertices.Count - 1; i++) + { + var cur = graphVertices[i]; + var next = graphVertices[i + 1]; + var edge = GetEdge(cur, next); + if (edge is not null) + curves.Add(edge.TargetEdge); + } + var lastedge = GetEdge(graphVertices[^1], graphVertices[0]); + if (lastedge is not null) + curves.Add(lastedge.TargetEdge); + + return curves; + } + #endregion + + #region 克隆及接口实现 + /// + /// 克隆此图;目测是深克隆 + /// + [System.Diagnostics.DebuggerStepThrough] + public Graph Clone() + { + var newGraph = new Graph(); + + foreach (var vertex in edges.Values) + foreach (var item in vertex) + newGraph.AddEdge(item.TargetEdge); + + return newGraph; + } + + IGraph IGraph.Clone() + { + return Clone(); + } + + [System.Diagnostics.DebuggerStepThrough] + public IEnumerator GetEnumerator() + { + return VerticesAsEnumberable.GetEnumerator(); + } + + [System.Diagnostics.DebuggerStepThrough] + IEnumerator? IEnumerable.GetEnumerator() + { + return GetEnumerator() as IEnumerator; + } + + public IEnumerable VerticesAsEnumberable => + vertices.Select(x => x.Key); + #endregion + + #region 方法 + /// + /// 输出点的邻接表的可读字符串 + /// + /// + public string ToReadable() + { + int i = 1; + string output = string.Empty; + foreach (var node in vertices) + { + var adjacents = string.Empty; + + output = string.Format("{1}\r\n{0}-{2}: [", i, output, node.Key.Data.ToString()); + + foreach (var adjacentNode in node.Value) + adjacents = string.Format("{0}{1},", adjacents, adjacentNode.Data.ToString()); + + if (adjacents.Length > 0) + adjacents = adjacents.TrimEnd(new char[] { ',', ' ' }); + + output = string.Format("{0}{1}]", output, adjacents); + i++; + } + return output; + } + #endregion +} + + +/// +/// 邻接表图实现的顶点; +/// IEnumerable 枚举所有邻接点; +/// +public sealed class GraphVertex : IGraphVertex, IEquatable, IComparable, IComparable +{ + #region 属性 + public Point3d Data { get; set; } + public int Index { get; set; } + #endregion + + #region 构造 + /// + /// 邻接表图实现的顶点 + /// + /// 点 + /// 所在节点索引 + public GraphVertex(Point3d value, int index) + { + Data = value; + Index = index; + } + #endregion + + #region 重载运算符_比较 + public bool Equals(IGraphVertex other) + { + return Index == other.Index; + } + + public override bool Equals(object obj) + { + if (obj is null) + return false; + if (obj is not IGraphVertex vertex) + return false; + else + return Equals(vertex); + } + + public override int GetHashCode() + { + return Index; + } + + public int CompareTo(IGraphVertex other) + { + if (Equals(other)) + return 0; + + if (Index < other.Index) + return -1; + else + return 1; + } + + int IComparable.CompareTo(IGraphVertex other) + { + return CompareTo(other); + } + + public int CompareTo(object obj) + { + if (obj is null) + return 1; + + try + { + var other = (GraphVertex)obj; + return CompareTo(other); + } + catch (Exception) + { + throw new ArgumentException("Object is not a IGraphVertex"); + } + } + + public static bool operator ==(GraphVertex person1, GraphVertex person2) + { + if (person1 is null || person2 is null) + return Equals(person1, person2); + + return person1.Equals(person2); + } + + public static bool operator !=(GraphVertex person1, GraphVertex person2) + { + if (person1 is null || person2 is null) + return !Equals(person1, person2); + + return !person1.Equals(person2); + } + #endregion +} + + +/// +/// 无向图中边的定义 +/// +public sealed class GraphEdge : IEdge, IEquatable +{ + #region 属性 + public IGraphVertex TargetVertex { get; set; } + public Curve3d TargetEdge { get; set; } + #endregion + + #region 构造 + /// + /// 无向图中边的定义 + /// + /// 下一点 + /// 下一点之间的曲线 + public GraphEdge(IGraphVertex target, Curve3d edge) + { + TargetVertex = target; + TargetEdge = edge; + } + #endregion + + #region 重载运算符_比较 + public bool Equals(GraphEdge other) + { + if (other is null) + return false; + return TargetVertex == other.TargetVertex && + TargetEdge == other.TargetEdge; + } + public override bool Equals(object obj) + { + if (obj is null) + return false; + if (obj is not GraphEdge personObj) + return false; + else + return Equals(personObj); + } + + public override int GetHashCode() + { + return (TargetVertex.GetHashCode(), TargetEdge.GetHashCode()).GetHashCode(); + } + public static bool operator ==(GraphEdge person1, GraphEdge person2) + { + if (person1 is null || person2 is null) + return Equals(person1, person2); + + return person1.Equals(person2); + } + public static bool operator !=(GraphEdge person1, GraphEdge person2) + { + if (person1 is null || person2 is null) + return !Equals(person1, person2); + + return !person1.Equals(person2); + } + #endregion +} + + +/// +/// 深度优先搜索; +/// +public sealed class DepthFirst +{ + #region 公共方法 + /// + /// 存储所有的边 + /// +#if true + public List> Curve3ds { get; } = new(); +#else + public List> Curve3ds { get; } = new(); +#endif + private HashSet Curved { get; } = new(); + + + /// + /// 找出所有的路径 + /// + /// 图 + public void FindAll(IGraph graph) + { + var total = new HashSet(); + // var graphtmp = graph.Clone(); + foreach (var item in graph.VerticesAsEnumberable) + { + Dfs(graph, new LinkedHashSet { item }, total); + total.Add(item); + } + } + #endregion + + #region 内部方法 + /// + /// 递归 DFS; + /// + /// 图 + /// 已经遍历的路径 +#if true + void Dfs(IGraph graph, LinkedHashSet visited, HashSet totalVisited) + { + var adjlist = graph.GetAdjacencyList(/*startNode*/ visited.First!.Value); // O(1) + foreach (var nextNode in adjlist) // O(n) + { + if (totalVisited.Contains(nextNode)) + { + continue; + } + // 如果下一个点未遍历过 + if (!visited.Contains(nextNode)) // O(1) + { + // 将下一点加入路径集合,并进行下一次递归 + var sub = new LinkedHashSet { nextNode }; + sub.AddRange(visited); // O(n) + Dfs(graph, sub, totalVisited); + } + // 如果下一点遍历过,并且路径大于2,说明已经找到起点 + else if (visited.Count > 2 && nextNode.Equals(visited.Last!.Value)) + { + // 将重复的路径进行过滤,并把新的路径存入结果 + var curstr = Gethashstring(visited); // O(n) + if (Isnew(curstr)) // O(1) + { + Curve3ds.Add(visited); + Curved.Add(curstr.Item1); + } + } + } + } + + + + +#else + + void Dfs(IGraph graph, List visited) + { + var startNode = visited[0]; + IGraphVertex nextNode; + List sub; + + var adjlist = graph.GetAdjacencyList(startNode).ToList(); // O(n) + for (int i = 0; i < adjlist.Count; i++) // O(n) + { + nextNode = adjlist[i]; + + // 如果下一个点未遍历过 + if (!visited.Contains(nextNode)) // O(n) + { + // 将下一点加入路径集合,并进行下一次递归 + sub = new List { nextNode }; + sub.AddRange(visited); // O(n) + Dfs(graph, sub); + } + + // 如果下一点遍历过,并且路径大于2,说明已经找到起点 + else if (visited.Count > 2 && nextNode.Equals(visited[^1])) + { + // 将重复的路径进行过滤,并把新的路径存入结果 + var cur = RotateToSmallest(visited); // O(n) + var inv = Invert(cur,cur[0]); // O(n) + + var curstr = Gethashstring(cur,inv); + // Env.Print(curstr); + if (Isnew(curstr)) + { + Curve3ds.Add(cur); + Curved.Add(curstr.Item1); + } + } + } + } +#endif + + + + + + + + + /// + /// 将列表旋转到最小的值为列表起点 + /// + /// + /// + static List RotateToSmallest(List lst) + { + var index = lst.IndexOf(lst.Min()); + return lst.Skip(index).Concat(lst.Take(index)).ToList(); + } + + /// + /// 将列表反向,并旋转到起点为最小值 + /// + /// + /// + static List Invert(List lst, IGraphVertex vertex) + { + var tmp = lst.ToList(); + tmp.Reverse(); + var index = tmp.IndexOf(vertex); + return tmp.Skip(index).Concat(lst.Take(index)).ToList(); + } + + static (string, string) Gethashstring(List pathone, List pathtwo) + { + var one = new string[pathone.Count]; + var two = new string[pathtwo.Count]; + for (int i = 0; i < pathone.Count; i++) + { + one[i] = pathone[i].Index.ToString(); + two[i] = pathtwo[i].Index.ToString(); + } + return (string.Join("-", one), string.Join("-", two)); + } + + static (string, string) Gethashstring(LinkedHashSet path) + { + var one = new string[path.Count]; + var two = new string[path.Count]; + path.For(path.MinNode!, (i, ver1, ver2) => { + one[i] = ver1.Index.ToString(); + two[i] = ver2.Index.ToString(); + }); + return (string.Join("-", one), string.Join("-", two)); + } + + + bool Isnew((string, string) path) + { + return !Curved.Contains(path.Item1) && !Curved.Contains(path.Item2); + } + + + #endregion +} \ No newline at end of file diff --git a/src/CAD/IFox.CAD.Shared/Algorithms/Graph/IGraph.cs b/src/CAD/IFox.CAD.Shared/Algorithms/Graph/IGraph.cs new file mode 100644 index 0000000..73547b7 --- /dev/null +++ b/src/CAD/IFox.CAD.Shared/Algorithms/Graph/IGraph.cs @@ -0,0 +1,98 @@ +namespace IFoxCAD.Cad; + +/// +/// 无向图 +/// +public interface IGraph +{ + /// + /// 顶点的数量 + /// + /// + int VerticesCount { get; } + + /// + /// 是否存在顶点 + /// + /// 顶点键 + /// + bool ContainsVertex(IGraphVertex key); + + /// + /// 顶点的迭代器 + /// + /// + IEnumerable VerticesAsEnumberable { get; } + + /// + /// 是否有边 + /// + /// 源顶点 + /// 目的顶点 + /// + bool HasEdge(IGraphVertex source, IGraphVertex destination); + /// + /// 图克隆函数 + /// + /// + IGraph Clone(); + /// + /// 获取边 + /// + /// + /// + /// + IEdge? GetEdge(IGraphVertex source, IGraphVertex dest); + /// + /// 邻接表 + /// + /// + /// + HashSet GetAdjacencyList(IGraphVertex vertex); + /// + /// 邻接边表 + /// + /// + /// + HashSet GetAdjacencyEdge(IGraphVertex vertex); + IGraphVertex? ReferenceVertex { get; } + + void RemoveVertex(Point3d pt); + void RemoveEdge(Curve3d curve); + +} + +/// +/// 无向图顶点 +/// +/// 顶点数据类型 +public interface IGraphVertex : IComparable +{ + /// + /// 顶点的键 + /// + /// + int Index { get; set; } + + /// + /// 顶点的数据 + /// + Point3d Data { get; } +} +/// +/// 无向图边 +/// +public interface IEdge +{ + /// + /// 边 + /// + Curve3d TargetEdge { get; } + /// + /// 目标顶点 + /// + IGraphVertex TargetVertex { get; } +} + + + diff --git a/src/CAD/IFox.CAD.Shared/Algorithms/QuadTree/QuadEntity.cs b/src/CAD/IFox.CAD.Shared/Algorithms/QuadTree/QuadEntity.cs new file mode 100644 index 0000000..c5f7f11 --- /dev/null +++ b/src/CAD/IFox.CAD.Shared/Algorithms/QuadTree/QuadEntity.cs @@ -0,0 +1,25 @@ +namespace IFoxCAD.Cad; + +/* + * 这个类存在的意义是为了不暴露Rect类字段 + * 同时利用了Rect类字段的快速 + * 提供到外面去再继承 + */ + +/// +/// 四叉树图元 +/// +public class QuadEntity : Rect +{ + /// + /// 四叉树图元 + /// + /// 包围盒 + public QuadEntity(Rect box) + { + _X = box._X; + _Y = box._Y; + _Top = box._Top; + _Right = box._Right; + } +} \ No newline at end of file diff --git a/src/CAD/IFox.CAD.Shared/Algorithms/QuadTree/QuadTree.cs b/src/CAD/IFox.CAD.Shared/Algorithms/QuadTree/QuadTree.cs new file mode 100644 index 0000000..267cc4a --- /dev/null +++ b/src/CAD/IFox.CAD.Shared/Algorithms/QuadTree/QuadTree.cs @@ -0,0 +1,259 @@ +/* + * 四叉树维基百科 http://en.wikipedia.org/wiki/Quadtree + * 四叉树是一种分区空间的算法,更快找出内部或外部给定区域. + * 通过一个正交矩形边界进行中心点分裂四个正交矩形, + * 插入时候会一直分裂四个正交矩形, + * 当分裂四个节点都无法单独拥有 图元包围盒 就停止分裂,并且你属于这四个节点的父亲. + * (不包含就是面积少了,就这么一句话看代码看半天), + * 还可以通过限制树的深度实现加速. + * + * 第一版: https://www.codeproject.com/script/Articles/ViewDownloads.aspx?aid=30535 + * + * 第二版: 找邻居 + * https://blog.csdn.net/dive_shallow/article/details/112438050 + * https://geidav.wordpress.com/2017/12/02/advanced-octrees-4-finding-neighbor-nodes/ + * + * 1.根节点:控制根节点从而控制所有节点 + * 2.子节点:包含自身根节点,插入矩形的时候进行递归分裂自身,和实现查找. + * 3.接口:约束都要有正交矩形,否则无法调用"包含"方法 + * 4.选择模式:模仿cad的窗选和框选 + */ +namespace IFoxCAD.Cad; + +/// +/// 根节点控制器 +/// +/// 类型接口约束必须有正交矩形 +public class QuadTree where TEntity : QuadEntity +{ + #region 成员 + /// + /// 根节点 + /// + QuadTreeNode _rootNode; + + /// + /// 四叉树节点的数目 + /// + public int Count { get => _rootNode.CountSubTree; } + + /// + /// 点容器(红黑树) + /// + SortedSet _points; + #endregion + + #region 构造 + /// + /// 四叉树根节点控制器 + /// + /// 四叉树矩形范围 + public QuadTree(Rect rect) + { + _rootNode = new QuadTreeNode(rect, null, 0);// 初始化根节点 + _points = new(); + } + #endregion + + #region 方法 + /// + /// 通过根节点插入数据项 + /// + /// + public void Insert(TEntity ent) + { + /* + * 图元点 是不分裂空间的,加入一个红黑树内部. + */ + if (ent.IsPoint) + { + _points.Add(ent); + return; + } + + while (!_rootNode.Contains(ent)) + { + /* + * 四叉树插入时候,如果超出根边界,就需要扩展 + * 扩展时候有一个要求,当前边界要作为扩展边界的一个象限,也就是反向分裂 + * + * 创建新根,计算原根在新根的位置, + * 替换指针:获取新分裂的节点的父节点,判断它哪个儿子是它, + * 替换之后可能仍然不包含图元边界,再循环计算. + */ + var sq_Left = _rootNode._X; + var sq_Botton = _rootNode._Y; + var sq_Right = _rootNode._Right; + var sq_Top = _rootNode._Top; + if (ent._Y >= _rootNode._Y)// 上↑增殖 + { + if (ent._X >= _rootNode._X) + { + // 右上↗增殖 + sq_Right += _rootNode.Width; + sq_Top += _rootNode.Height; + } + else + { + // 左上↖增殖 + sq_Left -= _rootNode.Width; + sq_Top += _rootNode.Height; + } + } + else// 在下↓ + { + if (ent._X >= _rootNode._X) + { + // 右下↘增殖 + sq_Right += _rootNode.Width; + sq_Botton -= _rootNode.Height; + } + else + { + // 左下↙增殖 + sq_Left -= _rootNode.Width; + sq_Botton -= _rootNode.Height; + } + } + // 扩大2次方 + var rectSquare = new Rect(sq_Left, sq_Botton, sq_Right, sq_Top); + + // 四叉树的旧根要作为四分之一插入 + // 新根中计算原根 + // 把 旧根节点 连接到 新根节点 上面,然后新根成为根 + var newRoot = new QuadTreeNode(rectSquare, null, 0); + var insert = newRoot.Insert(_rootNode); + if (insert is null) + throw new("四叉树:新根尺寸不对"); + if (!insert.Equals(_rootNode)) + throw new("四叉树:新旧节点大小不一致,无法连接"); + + var insPar = insert.Parent; + _rootNode.Parent = insPar; + if (insPar is null) + return; + + if (_rootNode.Equals(insPar.RightTopTree)) + insPar.RightTopTree = _rootNode; + else if (_rootNode.Equals(insPar.RightBottomTree)) + insPar.RightBottomTree = _rootNode; + else if (_rootNode.Equals(insPar.LeftBottomTree)) + insPar.LeftBottomTree = _rootNode; + else if (_rootNode.Equals(insPar.LeftTopTree)) + insPar.LeftTopTree = _rootNode; + else + throw new("四叉树:新节点不对,无法连接"); + + // 其后的子节点层数全部增加层数, + // 要加多少层取决于当前根边界属于新根边界的所在层 + var depth = insert.Depth; + if (depth == 0) + throw new("四叉树:插入节点是0,造成错误"); + _rootNode.ForEach(node => { + node.Depth += depth; + return false; + }); + + // 交换根控制 + _rootNode = newRoot; + } + + _rootNode.Insert(ent); + } + + + /// + /// 查询四叉树,返回给定区域的数据项 + /// + /// 矩形选区查询 + /// + public List Query(Rect rect, QuadTreeSelectMode selectMode = QuadTreeSelectMode.IntersectsWith) + { + QuadTreeEvn.SelectMode = selectMode; + + var results = new List(); + // 选择图元 + _rootNode.Query(rect, results); + // 选择点 + var ptge = _points.GetEnumerator(); + switch (selectMode) + { + case QuadTreeSelectMode.IntersectsWith: + case QuadTreeSelectMode.Contains: + /* 由于红黑树的方法 _points.GetViewBetween() + * 过滤只能过滤X区间,Y区间还是要过滤, + * 那么我就只能用这样的方法加速了 + * + * 而更好的方式是不用红黑树,去加入一个点云数据来进行,可谓是编程无极限.... + */ + while (ptge.MoveNext()) + { + var ptEnt = ptge.Current; + if (rect._X <= ptEnt._X && ptEnt._X <= rect._Right) + { + if (rect._Y <= ptEnt._Y && ptEnt._Y <= rect.Top) + results.Add(ptEnt); + } + else if (ptEnt._X > rect._Right) + break;// 超过后面范围就break,因为红黑树已经排序 + } + break; + default: + throw new("四叉树:" + nameof(selectMode)); + } + return results; + } + + /// + /// 删除子节点 + /// + /// 根据范围删除 + public void Remove(Rect rect) + { + _rootNode.Remove(rect); + } + + /// + /// 删除子节点 + /// + /// 根据图元删除 + public void Remove(TEntity ent) + { + _rootNode.Remove(ent); + } + + /// + /// 找到附近节点图元 + /// + [Obsolete("找附近节点的并不是最近的图元")] + public TEntity? FindNeibor(Rect rect, QuadTreeFindMode findMode) + { + return _rootNode.FindNeibor(rect, findMode); + } + + /// + /// 找到附近图元 + /// + /// + /// + public TEntity? FindNearEntity(Rect rect) + { + return _rootNode.FindNearEntity(rect); + } + + /// + /// 执行四叉树中特定的行为 + /// + /// + public void ForEach(QTAction action) + { + _rootNode.ForEach(action); + } + + /// + /// 委托:四叉树节点上执行一个操作 + /// + /// + public delegate bool QTAction(QuadTreeNode obj); + #endregion +} \ No newline at end of file diff --git a/src/CAD/IFox.CAD.Shared/Algorithms/QuadTree/QuadTreeEvn.cs b/src/CAD/IFox.CAD.Shared/Algorithms/QuadTree/QuadTreeEvn.cs new file mode 100644 index 0000000..16d518b --- /dev/null +++ b/src/CAD/IFox.CAD.Shared/Algorithms/QuadTree/QuadTreeEvn.cs @@ -0,0 +1,26 @@ +#pragma warning disable CA2211 // 非常量字段应当不可见 +namespace IFoxCAD.Cad; + +public class QuadTreeEvn +{ + /// + /// 最小的节点有一个面积(一定要大于0) + /// + public static double MinArea = 1e-6; + + /// + /// 选择模式 + /// + public static QuadTreeSelectMode SelectMode; + + /// + /// 最大深度 + /// + public static int QuadTreeMaximumDepth = 2046; + + /// + /// 节点内容超过就分裂 + /// + public static int QuadTreeContentsCountSplit = 20; +} +#pragma warning restore CA2211 // 非常量字段应当不可见 \ No newline at end of file diff --git a/src/CAD/IFox.CAD.Shared/Algorithms/QuadTree/QuadTreeNode.cs b/src/CAD/IFox.CAD.Shared/Algorithms/QuadTree/QuadTreeNode.cs new file mode 100644 index 0000000..7898b84 --- /dev/null +++ b/src/CAD/IFox.CAD.Shared/Algorithms/QuadTree/QuadTreeNode.cs @@ -0,0 +1,816 @@ +namespace IFoxCAD.Cad; + +/// +/// 子节点 +/// +/// +public class QuadTreeNode + : Rect + where TEntity : QuadEntity +{ + #region 成员 + /// + /// 子节点:第一象限:右上↗ + /// + public QuadTreeNode? RightTopTree; + /// + /// 子节点:第二象限:左上↖ + /// + public QuadTreeNode? LeftTopTree; + /// + /// 子节点:第三象限:左下↙ + /// + public QuadTreeNode? LeftBottomTree; + /// + /// 子节点:第四象限:右下↘ + /// + public QuadTreeNode? RightBottomTree; + /// + /// 所有子节点 + /// + QuadTreeNode[] Nodes + { + get + { + return new QuadTreeNode[] + { + RightTopTree!, + LeftTopTree!, + LeftBottomTree!, + RightBottomTree!, + }; + } + } + /// + /// 所有子节点是空的 + /// + bool NodesIsEmpty => RightTopTree is null && LeftTopTree is null && LeftBottomTree is null && RightBottomTree is null; + + /// + /// 父节点 + /// + public QuadTreeNode? Parent; + /// + /// 节点的在四叉树的深度 + /// + public int Depth; + + // 注意,内容没有限制:这不是 impement 四叉树的标准方法 + /// (节点图元是交叉线压着的,并不是矩形范围内全部,因为这是四叉树的特性决定) + /// + /// 本节点:内容 + /// + public List Contents; + + /// + /// 本节点和旗下所有子节点:内容群 + /// + public void ContentsSubTree(List results) + { + if (Contents is null) + return; + results.AddRange(Contents); + var nodes = Nodes; + for (int i = 0; i < nodes.Length; i++) + nodes[i]?.ContentsSubTree(results); + } + + /// + /// 本节点和旗下所有子节点:内容群数量 + /// + public int CountSubTree + { + get + { + if (Contents is null) + return 0; + int count = Contents.Count; + + var nodes = Nodes; + for (int i = 0; i < nodes.Length; i++) + { + var node = nodes[i]; + if (node is null) + continue; + count += node.CountSubTree; + } + return count; + } + } + #endregion + + #region 构造 + /// + /// 四叉树节点 + /// + /// 当前节点边界 + /// 父节点 + /// 节点深度 + public QuadTreeNode(Rect box, QuadTreeNode? parent, int depth) + { + _X = box._X; + _Y = box._Y; + _Right = box._Right; + _Top = box._Top; + + Parent = parent; + Depth = depth; + Contents = new(); + } + #endregion + + #region 增 + /// + /// 将原有节点插入用 + /// + /// + internal QuadTreeNode? Insert(Rect rect) + { + if (!Contains(rect)) + return null; + + // 四叉树分裂,将当前节点分为四个子节点 + if (NodesIsEmpty) + CreateChildren(); + + // 当前节点边界 包含 图元包围盒 就插入 + // 退出递归:4个节点都不完全包含 + // 4个节点的上层 + var nodes = Nodes; + for (int i = 0; i < nodes.Length; i++) + { + var node = nodes[i]; + if (node is null) + continue; + + if (node.Equals(rect)) + { + rect = node; + return node.Insert(rect); + } + } + return this; + } + + /// + /// 将数据项递归插入四叉树 + /// + /// + public QuadTreeNode? Insert(TEntity ent) + { + if (!Contains(ent)) + { + // Debugx.Printl("不在四叉树边界范围"); + // Trace.WriteLine("不在四叉树边界范围"); + return null; + } + + // if (ent.IsPoint) + // { + // // 找到最后一层包含它的节点,然后加入它 + // // 因此是跳过分裂矩形的,以免造成无限递归 + // var minNode = GetMinNode(ent); + // minNode.Contents.Add(ent); + // return minNode; + // } + +#if true2 + // 方案二: + // 内容数超过才分裂,防止树深度过高,但是多选过滤时候慢一点 + if (Contents.Count > QuadTreeEvn.QuadTreeContentsCountSplit) + { + // 分裂出四个子节点 + if (_nodesIsEmpty) + { + CreateChildren(); + // 分裂之后将当前层的内容扔到四个子节点, + // 如果被压着,那么就不会扔到下面 + for (int i = Contents.Count - 1; i >= 0; i--) + { + var minNode = GetMinNode(Contents[i].Box); + minNode.Contents.Add(Contents[i]); + Contents.RemoveAt(i); + } + } + else + { + // 没有分裂的话,就递归 + // 退出递归:4个节点都不完全包含,内容就是他们的父亲 + var nodes = _Nodes; + for (int i = 0; i < nodes.Length; i++) + { + var node = nodes[i]; + if (node is null) + continue; + + // 这里需要中断.(匿名方法ForEach无法中断,会造成父节点加入内容) + if (node.Contains(ent)) + return node.Insert(ent); + } + } + } +#else + // 方案一:分裂到最细节点 + + // 分裂出四个子节点 + if (NodesIsEmpty) + CreateChildren(); + + // 4个子节点开始递归 + // 退出递归:4个节点都不完全包含,内容就是他们的父亲 + var nodes = Nodes; + for (int i = 0; i < nodes.Length; i++) + { + var node = nodes[i]; + if (node is null) + continue; + // 这里需要中断.(匿名方法ForEach无法中断,会造成父节点加入内容) + if (node.Contains(ent)) + return node.Insert(ent); + } +#endif + + // 为什么要用容器? + // 相同包围盒或者四叉树分割线压着多个. + this.Contents.Add(ent); + return this; + } + + /// + /// 创建子节点 + /// + void CreateChildren() + { + // 最小面积控制节点深度,但是这样可能导致树分成高,引起爆栈 + if (Depth > QuadTreeEvn.QuadTreeMaximumDepth) + return; + var recs = RectSplit(this); + var de = Depth + 1; + RightTopTree = new QuadTreeNode(recs[0], this, de); + LeftTopTree = new QuadTreeNode(recs[1], this, de); + LeftBottomTree = new QuadTreeNode(recs[2], this, de); + RightBottomTree = new QuadTreeNode(recs[3], this, de); + } + + /// + /// 矩形分裂为四个 + /// + /// + /// + static Rect[] RectSplit(Rect box) + { + var halfWidth = box.Width / 2.0; + var halfHeight = box.Height / 2.0; + + var upperRight = new Rect(box._X + halfWidth, box._Y + halfHeight, box._Right, box._Top); + var upperLeft = new Rect(box._X, box._Y + halfHeight, box._Right - halfWidth, box._Top); + var lowerleft = new Rect(box._X, box._Y, box._Right - halfWidth, box._Top - halfHeight);// 基础 + var lowerRight = new Rect(box._X + halfWidth, box._Y, box._Right, box._Top - halfHeight); + + // 依照象限顺序输出 + return new Rect[] { upperRight, upperLeft, lowerleft, lowerRight }; + } + #endregion + + #region 删 + /// + /// 删除图元 + /// + /// 根据图元删除 + public bool Remove(TEntity easeEnt) + { + // 通过图元id删除无疑是非常低效的, + // 1.相当于在所有的容器查找它,但是移除只会移除一次, + // 因此必须要求图元只会加入一次,才能中断检索剩余分支. + // 2.这个代价还是太高,因此我们还是要默认条件,图元载入一次之后,不再改动. + // 3.不再改动也不太合理,因为cad图元还是可以修改的 + + // 1.处理内容 + if (Contents.Remove(easeEnt)) + { + if (CountSubTree == 0) + this.Clear(this); + return true; + } + + // 2.递归子节点移除 + var nodes = Nodes; + for (int i = 0; i < nodes.Length; i++) + { + var node = nodes[i]; + if (node is null) + continue; + if (node.Remove(easeEnt)) // 递归进入子节点删除内容 + return true; // 删除成功就中断其他节点的搜索 + } + return false; + } + + /// + /// 递归进入最下层节点,然后开始清理 + /// + /// + void Clear(QuadTreeNode node) + { + var nodes = Nodes; + for (int i = 0; i < nodes.Length; i++) + nodes[i]?.Clear(nodes[i]); + + node.Contents.Clear(); + // node.Contents = null;// 重复加入时候会出错 + node.RightTopTree = null; + node.LeftTopTree = null; + node.LeftBottomTree = null; + node.RightBottomTree = null; + node.Parent = null; + // node.Box = zoreRect; + } + + /// + /// 删除子节点内容 + /// + /// 根据范围删除 + public void Remove(Rect queryArea) + { + // 本节点内容移除 + if (Contents is not null && Contents.Count > 0)// 从最上层的根节点开始进入 + { + for (int i = Contents.Count - 1; i >= 0; i--) + { + var ent = Contents[i]; + // 移除之后,如果容器是0,那么这里不能直接 Contents=null, + // 因为此节点下面可能还有节点, + // 需要判断了其后数量0才可以清理. + // 否则其后还有内容,那么此节点就是仍然可以用的. + if (queryArea.Contains(ent)) + Contents.Remove(ent); + } + } + + // 同插入一样 + // 跳到指定节点再搜索这个节点下面的图元 + var nodes = Nodes; + for (int i = 0; i < nodes.Length; i++) + { + var node = nodes[i]; + if (node is null) + continue; + if (node.NodesIsEmpty) + continue; + + // 此节点边界 完全包含 查询区域,则转到该节点,并跳过其余节点(打断此循环) + if (node.Contains(queryArea)) + { + node.Remove(queryArea); + break; + } + // 查询区域 完全包含 此节点边界,提取此节点全部内容 + // 跳过分析碰撞,并继续循环搜索其他节点 + if (queryArea.Contains(node)) + { + node.Clear(node); + continue; + } + // 查询区域 与 此节点四边形边线碰撞 查询该四边形中,并继续循环搜索其他节点 + // 1,角点碰撞 2,边界碰撞 + if (node.IntersectsWith(queryArea)) + node.Remove(queryArea); + } + + // 本节点内容移除之后,旗下还有内容的话, + // 会跳过此处,再进入子节点进行递归,直到最后一个节点 + if (CountSubTree == 0) + Clear(this); + } + #endregion + + #region 查 + /// + /// 查询范围内的实体 + /// + /// 查询矩形 + /// + public void Query(Rect queryArea, List results) + { + GetCurrentContents(queryArea, results); + + // 遍历子节点 + var nodes = Nodes; + for (int i = 0; i < nodes.Length; i++) + { + var node = nodes[i]; + if (node is null) + continue; + // 子节点的4个子节点都是空的, + // 那么表示元素会在子节点这一层啊... + if (node.NodesIsEmpty) + continue; + + // 此节点边界 完全包含 查询区域,则转到该节点,并跳过其余节点(打断此循环) + if (node.Contains(queryArea)) + { + node.Query(queryArea, results); + break; + } + // 查询区域 完全包含 此节点边界,提取此节点全部内容 + // 跳过分析碰撞,并继续循环搜索其他节点 + if (queryArea.Contains(node)) + { + node.ContentsSubTree(results); + continue; + } + // 查询区域 与 此节点四边形边线碰撞 查询该四边形中,并继续循环搜索其他节点 + // 1,角点碰撞 2,边界碰撞 + if (node.IntersectsWith(queryArea)) + node.Query(queryArea, results); + } + } + + /// + /// 获取本节点内容 + /// + /// + /// + void GetCurrentContents(Rect queryArea, List results) + { + // 遍历当前节点内容,加入方式取决于碰撞模式 + if (QuadTreeEvn.SelectMode == QuadTreeSelectMode.IntersectsWith) + { + for (int i = 0; i < Contents.Count; i++) + { + var ent = Contents[i]; + if (queryArea.IntersectsWith(ent)) + results.Add(ent); + } + } + else + { + for (int i = 0; i < Contents.Count; i++) + { + var ent = Contents[i]; + if (queryArea.Contains(ent)) + results.Add(ent); + } + } + } + + /// + /// 找临近图元 + /// + /// 查找矩形 + /// + public TEntity? FindNearEntity(Rect queryArea) + { + TEntity? resultEntity = default; + // 1.找到 查找矩形 所在的节点,利用此节点的矩形. + var queryNode = GetMinNode(queryArea); + var queryAreaCenter = queryArea.CenterPoint; + + // 2.从根开始搜索 + // 如果搜索父亲的父亲的...内容群,它不是距离最近的,只是节点(亲属关系)最近 + // 储存找过的<图元,距离> + var entDic = new Dictionary(); + + var old = QuadTreeEvn.SelectMode; + QuadTreeEvn.SelectMode = QuadTreeSelectMode.IntersectsWith; + while (true) + { + // 循环找父节点大小 + var hw = queryNode.Width / 2.0; + var hh = queryNode.Height / 2.0; + // 3.利用选区中心扩展一个节点边界大小的矩形.从而选择图元 + // 再判断图元的与目标的距离,找到最小距离,即为最近 + var minPt = new Point2d(queryAreaCenter.X - hw, queryAreaCenter.Y - hh); + var maxPt = new Point2d(queryAreaCenter.X + hw, queryAreaCenter.Y + hh); + var ents = new List(); + Query(new Rect(minPt, maxPt), ents); + for (int i = 0; i < ents.Count; i++) + { + var ent = ents[i]; + if (entDic.ContainsKey(ent)) + continue; + var dis = ent.CenterPoint.GetDistanceTo(queryAreaCenter); + if (dis > 1e-6)// 剔除本身 + entDic.Add(ent, dis); + } + if (entDic.Count > 0) + { + resultEntity = entDic.OrderBy(a => a.Value).First().Key; + break; + } + if (queryNode.Parent is null)// 最顶层就退出 + break; + queryNode = queryNode.Parent;// 利用父节点矩形进行变大选区 + } + QuadTreeEvn.SelectMode = old; + return resultEntity; + } + + /// + /// 找临近节点的图元 + /// + /// 查找矩形 + /// 查找什么方向 + /// + [Obsolete("找附近节点的并不是最近的图元")] + public TEntity? FindNeibor(Rect queryArea, QuadTreeFindMode findMode) + { + TEntity? resultEntity = default; + // 1.找到 查找矩形 所在的节点,利用此节点的矩形. + // 2.利用节点矩形是分裂的特点,边和边必然贴合. + // 3.找到方向 findMode 拥有的节点,然后查找节点的内容 + var queryNode = GetMinNode(queryArea); + + bool whileFlag = true; + // 同一个节点可能包含邻居,因为四叉树的加入是图元压线, + // 那么就在这里搜就得了,用中心点决定空间位置 + // 但是本空间的图元可能都比它矮,无法满足条件 + if (queryNode.CountSubTree > 1) + { + resultEntity = GetNearestNeighbor(queryNode, findMode, queryArea); + if (resultEntity is null || resultEntity.CenterPoint == queryArea.CenterPoint) + whileFlag = true; + else + whileFlag = false; + } + + while (whileFlag) + { + // 同一个父节点是临近的,优先获取 兄弟节点 的内容. + // 循环了第二次是北方兄弟的节点, + // 但是这不是一个找到临近图元的方法, + // 因为临近的可能是父亲的父亲的父亲...另一个函数 FindNearEntity 写 + // 本方案也仅仅作为找北方节点 + var parent = queryNode.Parent; + if (parent is not null) + { + switch (findMode) + { + case QuadTreeFindMode.Top: + { + // 下格才获取上格,否则导致做了无用功,上格就直接获取邻居了 + if (parent.LeftBottomTree == queryNode || parent.RightBottomTree == queryNode) + resultEntity = GetNearestNeighbor(parent, findMode, queryArea); + } + break; + case QuadTreeFindMode.Bottom: + { + if (parent.LeftTopTree == queryNode || parent.RightTopTree == queryNode) + resultEntity = GetNearestNeighbor(parent, findMode, queryArea); + } + break; + case QuadTreeFindMode.Left: + { + if (parent.RightTopTree == queryNode || parent.RightBottomTree == queryNode) + resultEntity = GetNearestNeighbor(parent, findMode, queryArea); + } + break; + case QuadTreeFindMode.Right: + { + if (parent.LeftTopTree == queryNode || parent.LeftBottomTree == queryNode) + resultEntity = GetNearestNeighbor(parent, findMode, queryArea); + } + break; + } + } + if (resultEntity is not null) + break; + + // 通过 所在节点 找 邻居节点, + // 拿到 邻居节点 下面的所有内容(图元) + // 内容可能是空的,再从邻居那往北找...如果找到了四叉树最外层,仍然没有内容,退出循环 + var neiborNode = FindNeiborNode(queryNode, findMode); + if (neiborNode is null) + continue; + if (neiborNode.CountSubTree > 0) + { + resultEntity = GetNearestNeighbor(neiborNode, findMode, queryArea); + break; + } + if (neiborNode.Parent is null)// 如果找到了四叉树最外层,仍然没有内容,退出循环 + break; + queryNode = neiborNode; + } + + return resultEntity; + } + + /// + /// 查找节点的(本内容和子内容)与(查找面积)矩形中点对比,找到最近一个内容 + /// + /// 查找面积 + /// 查找方向 + /// 查找节点 + /// + static TEntity? GetNearestNeighbor(QuadTreeNode queryNode, + QuadTreeFindMode findMode, + Rect queryArea) + { + TEntity? results = default; + + var lst = new List(); + var qcent = queryArea.CenterPoint; + + switch (findMode) + { + case QuadTreeFindMode.Top: + { + // 取出Y比queryArea的还大的一个,是最近的那个 + var qy = qcent.Y; + queryNode.ContentsSubTree(lst); + lst.ForEach(ent => { + if (ent.CenterPoint.Y > qy) + lst.Add(ent); + }); + lst = lst.OrderBy(ent => ent.CenterPoint.Y).ToList(); + } + break; + case QuadTreeFindMode.Bottom: + { + var qy = qcent.Y; + queryNode.ContentsSubTree(lst); + lst.ForEach(ent => { + if (ent.CenterPoint.Y < qy) + lst.Add(ent); + }); + lst = lst.OrderByDescending(ent => ent.CenterPoint.Y).ToList(); + } + break; + case QuadTreeFindMode.Left: + { + var qx = qcent.Y; + queryNode.ContentsSubTree(lst); + lst.ForEach(ent => { + if (ent.CenterPoint.X > qx) + lst.Add(ent); + }); + lst = lst.OrderBy(ent => ent.CenterPoint.X).ToList(); + } + break; + case QuadTreeFindMode.Right: + { + var qx = qcent.Y; + queryNode.ContentsSubTree(lst); + lst.ForEach(ent => { + if (ent.CenterPoint.X < qx) + lst.Add(ent); + }); + lst = lst.OrderByDescending(ent => ent.CenterPoint.X).ToList(); + } + break; + } + + if (lst.Count > 0) + return lst[0];// 可能就是本体重叠 + return results; + } + + /// + /// 找包含它的最小分支 + /// + /// 查询的矩形 + /// 节点 + QuadTreeNode GetMinNode(Rect queryArea) + { + var nodes = Nodes; + for (int i = 0; i < nodes.Length; i++) + { + var node = nodes[i]; + if (node is null) + continue; + + // 边界包含查询面积,那么再递归查询, + // 直到最后四个都不包含,那么上一个就是图元所在节点 + if (node.Contains(queryArea)) + return node.GetMinNode(queryArea);// 中断后面的范围,才可以返回正确的 + } + return this; + } + + /// + /// 四叉树找邻居节点(相同或更大) + /// + /// 源节点 + /// 方向 + /// + QuadTreeNode? FindNeiborNode(QuadTreeNode tar, QuadTreeFindMode findMode) + { + var parent = tar.Parent; + if (parent is null) + return null; + switch (findMode) + { + case QuadTreeFindMode.Top: + { + // 判断当前节点在父节点的位置,如果是在 下格 就取对应的 上格 + if (tar == parent.LeftBottomTree) + return parent.LeftTopTree; + if (tar == parent.RightBottomTree) + return parent.RightTopTree; + // 否则就是上格 + // 找父节点的北方邻居..也就是在爷节点上面找 + // 递归,此时必然是 下格,就必然返回 上格,然后退出递归 + var parentNeibor = FindNeiborNode(parent, QuadTreeFindMode.Top); + // 父节点的北方邻居 无 子节点 + if (parentNeibor is null || parentNeibor.RightTopTree is null) + return parentNeibor;// 返回父节点的北方邻居,比较大 + // 父节点的北方邻居 有 子节点,剩下条件就只有这两 + + // 如果直接返回,那么是(相同或更大), + // 而找邻近图元需要的是这个(相同或更大)下面的图元,在外面对这个格子内图元用坐标分析就好了 + if (tar == parent.LeftTopTree) + return parentNeibor.LeftBottomTree; + return parentNeibor.RightBottomTree; + } + case QuadTreeFindMode.Bottom: + { + if (tar == parent.LeftTopTree) + return parent.LeftBottomTree; + if (tar == parent.RightTopTree) + return parent.RightBottomTree; + var parentNeibor = FindNeiborNode(parent, QuadTreeFindMode.Bottom); + if (parentNeibor is null || parentNeibor.RightTopTree is null) + return parentNeibor; + if (tar == parent.LeftBottomTree) + return parentNeibor.LeftTopTree; + return parentNeibor.RightTopTree; + } + case QuadTreeFindMode.Right: + { + if (tar == parent.LeftTopTree) + return parent.RightTopTree; + if (tar == parent.LeftBottomTree) + return parent.RightBottomTree; + var parentNeibor = FindNeiborNode(parent, QuadTreeFindMode.Right); + if (tar == parent.RightTopTree) + return parentNeibor?.LeftTopTree; + return parentNeibor?.LeftBottomTree; + } + case QuadTreeFindMode.Left: + { + if (tar == parent.RightTopTree) + return parent.LeftTopTree; + if (tar == parent.RightBottomTree) + return parent.LeftBottomTree; + var parentNeibor = FindNeiborNode(parent, QuadTreeFindMode.Left); + if (tar == parent.LeftTopTree) + return parentNeibor?.RightTopTree; + return parentNeibor?.RightBottomTree; + } + } + return null; + } + #endregion + + #region 改 + /// + /// 所有的点归类到最小包围它的空间 + /// + // public void PointsToMinNode() + // { + // ForEach(node => + // { + // for (int i = 0; i < node.Contents.Count; i++) + // { + // var ent = node.Contents[i]; + // if (ent.IsPoint) + // { + // // 如果最小包含!=当前,就是没有放在最适合的位置 + // var queryNode = GetMinNode(ent); + // if (queryNode != node) + // { + // node.Remove(ent); + // queryNode.Contents.Add(ent); + // } + // } + // } + // return false; + // }); + // } + #endregion + + #region 方法 + /// + /// 递归全部节点(提供给根用的,所以是全部) + /// + /// QTAction + public bool ForEach(QuadTree.QTAction action) + { + // 执行本节点 + if (action(this)) + return true; + + // 递归执行本节点的子节点 + var nodes = Nodes; + for (int i = 0; i < nodes.Length; i++) + { + var node = nodes[i]; + if (node is null) + continue; + if (node.ForEach(action)) + break; + } + return false; + } + #endregion +} \ No newline at end of file diff --git a/src/CAD/IFox.CAD.Shared/Algorithms/QuadTree/QuadTreeSelectMode.cs b/src/CAD/IFox.CAD.Shared/Algorithms/QuadTree/QuadTreeSelectMode.cs new file mode 100644 index 0000000..2cc2e37 --- /dev/null +++ b/src/CAD/IFox.CAD.Shared/Algorithms/QuadTree/QuadTreeSelectMode.cs @@ -0,0 +1,21 @@ +namespace IFoxCAD.Cad; + +/// +/// 四叉树选择模式 +/// +public enum QuadTreeSelectMode +{ + IntersectsWith, // 碰撞到就选中 + Contains, // 全包含才选中 +} + +/// +/// 四叉树查找方向 +/// +public enum QuadTreeFindMode +{ + Top = 1, // 上 + Bottom = 2, // 下 + Left = 4, // 左 + Right = 8, // 右 +} \ No newline at end of file diff --git a/src/CAD/IFox.CAD.Shared/Algorithms/QuadTree/Rect.cs b/src/CAD/IFox.CAD.Shared/Algorithms/QuadTree/Rect.cs new file mode 100644 index 0000000..9e10b97 --- /dev/null +++ b/src/CAD/IFox.CAD.Shared/Algorithms/QuadTree/Rect.cs @@ -0,0 +1,608 @@ +using System.Diagnostics; +using System.Runtime.CompilerServices; + +namespace IFoxCAD.Cad; + +/// +/// Linq Distinct 消重比较两点在容差范围内就去除 +/// +public class TolerancePoint2d : IEqualityComparer +{ + readonly double _tolerance; + public TolerancePoint2d(double tolerance = 1e-6) + { + _tolerance = tolerance; + } + + public bool Equals(Point2d a, Point2d b)// Point3d是struct不会为null + { + /*默认规则是==是0容差,Eq是有容差*/ + // 方形限定 + // 在 0~1e-6 范围实现 圆形限定 则计算部分在浮点数6位后,没有啥意义 + // 在 0~1e-6 范围实现 从时间和CPU消耗来说,圆形限定 都没有 方形限定 的好 + if (_tolerance <= 1e-6) + return Math.Abs(a.X - b.X) <= _tolerance && Math.Abs(a.Y - b.Y) <= _tolerance; + + // 圆形限定 + // DistanceTo 分别对XYZ进行了一次乘法,也是总数3次乘法,然后求了一次平方根 + // (X86.CPU.FSQRT指令用的牛顿迭代法/软件层面可以使用快速平方根....我还以为CPU会采取快速平方根这样的取表操作) + return a.IsEqualTo(b, new Tolerance(_tolerance, _tolerance)); + } + + public int GetHashCode(Point2d obj) + { + // 结构体直接返回 obj.GetHashCode(); Point3d ToleranceDistinct3d + // 因为结构体是用可值叠加来判断?或者因为结构体兼备了一些享元模式的状态? + // 而类是构造的指针,所以取哈希值要改成x+y+z..s给Equals判断用,+是会溢出,所以用^ + return (int)obj.X ^ (int)obj.Y;// ^ (int)obj.Z; + } +} + + +[Serializable] +[StructLayout(LayoutKind.Sequential)] +[DebuggerDisplay("{DebuggerDisplay,nq}")] +[DebuggerTypeProxy(typeof(Rect))] +public class Rect : IEquatable, IComparable +{ + [DebuggerBrowsable(DebuggerBrowsableState.Never)] + private string DebuggerDisplay => ToString("f4"); + +#pragma warning disable CA2211 // 非常量字段应当不可见 + public static TolerancePoint2d RectTolerance = new(1e-6); + public static Tolerance CadTolerance = new(1e-6, 1e-6); +#pragma warning restore CA2211 // 非常量字段应当不可见 + + #region 字段 + // 这里的成员不要用{get}封装成属性,否则会导致跳转了一次函数, + // 10w图元将会从187毫秒变成400毫秒 + // 不用 protected 否则子类传入Rect对象进来无法用 + internal double _X; + internal double _Y; + internal double _Right; + internal double _Top; + #endregion + + #region 成员 + public double X => _X; + public double Y => _Y; + public double Left => _X; + public double Bottom => _Y; + public double Right => _Right; + public double Top => _Top; + + public double Width => _Right - _X; + public double Height => _Top - _Y; + public double Area + { + get + { + var ar = (_Right - _X) * (_Top - _Y); + return ar < 1e-10 ? 0 : ar; + } + } + + public Point2d MinPoint => LeftLower; + public Point2d MaxPoint => RightUpper; + public Point2d CenterPoint => Midst; + + /// + /// 左下Min + /// + public Point2d LeftLower => new(_X, _Y); + + /// + /// 左中 + /// + public Point2d LeftMidst => new(_X, Midst.Y); + + /// + /// 左上 + /// + public Point2d LeftUpper => new(_X, _Top); + + /// + /// 右上Max + /// + public Point2d RightUpper => new(_Right, _Top); + + /// + /// 右中 + /// + public Point2d RightMidst => new(_Right, Midst.Y); + + /// + /// 右下 + /// + public Point2d RightBottom => new(_Right, _Y); + + /// + /// 中间 + /// + public Point2d Midst => new(((_Right - _X) * 0.5) + _X, ((_Top - _Y) * 0.5) + _Y); + + /// + /// 中上 + /// + public Point2d MidstUpper => new(Midst.X, _Top); + + /// + /// 中下 + /// + public Point2d MidstBottom => new(Midst.X, _Y); + + /// + /// 是一个点 + /// 水平或垂直直线包围盒是面积是0,所以面积是0不一定是点 + /// + public bool IsPoint => Math.Abs(_X - _Right) < 1e-10 && Math.Abs(_Y - _Top) < 1e-10; + #endregion + + #region 构造 + public Rect() + { + } + + /// + /// 矩形类 + /// + /// 左 + /// 下 + /// 右 + /// 上 + public Rect(double left, double bottom, double right, double top) + { + _X = left; + _Y = bottom; + _Right = right; + _Top = top; + } + + /// + /// 构造矩形类 + /// + /// + /// + /// 是否检查大小 + public Rect(Point2d p1, Point2d p3, bool check = false) + { + if (check) + { + _X = Math.Min(p1.X, p3.X); + _Y = Math.Min(p1.Y, p3.Y); + _Right = Math.Max(p1.X, p3.X); + _Top = Math.Max(p1.Y, p3.Y); + } + else + { + _X = p1.X; + _Y = p1.Y; + _Right = p3.X; + _Top = p3.Y; + } + } + #endregion + + #region 重载运算符_比较 + public override bool Equals(object? obj) + { + return this.Equals(obj as Rect); + } + public bool Equals(Rect? b) + { + return this.Equals(b, 1e-6);/*默认规则是==是0容差,Eq是有容差*/ + } + public static bool operator !=(Rect? a, Rect? b) + { + return !(a == b); + } + public static bool operator ==(Rect? a, Rect? b) + { + // 此处地方不允许使用==null,因为此处是定义 + if (b is null) + return a is null; + else if (a is null) + return false; + if (ReferenceEquals(a, b))// 同一对象 + return true; + + return a.Equals(b, 0); + } + + /// + /// 比较核心 + /// + public bool Equals(Rect? b, double tolerance = 1e-6) + { + if (b is null) + return false; + if (ReferenceEquals(this, b)) // 同一对象 + return true; + + return Math.Abs(_X - b._X) < tolerance && + Math.Abs(_Right - b._Right) < tolerance && + Math.Abs(_Top - b._Top) < tolerance && + Math.Abs(_Y - b._Y) < tolerance; + } + + public override int GetHashCode() + { + return (((int)_X ^ (int)_Y).GetHashCode() ^ (int)_Right).GetHashCode() ^ (int)_Top; + } + #endregion + + #region 包含 + public bool Contains(Point2d Point2d) + { + return Contains(Point2d.X, Point2d.Y); + } + public bool Contains(double x, double y) + { + return _X <= x && x <= _Right && + _Y <= y && y <= _Top; + } + + /// + /// 四个点都在内部就是包含 + /// + /// + /// + public bool Contains(Rect rect) + { + return _X <= rect._X && rect._Right <= _Right && + _Y <= rect._Y && rect._Top <= _Top; + } + + /// + /// 一个点在内部就是碰撞 + /// + /// + /// true内部 + [MethodImpl] + public bool IntersectsWith(Rect rect) + { + return rect._X <= _Right && _X <= rect._Right && + rect._Top >= _Y && rect._Y <= _Top; + } + #endregion + + #region 方法 + /// + /// 获取共点 + /// + /// + public Point2d[] GetCommonPoint(Rect other) + { + return ToPoints().Intersect(other.ToPoints(), RectTolerance).ToArray(); + } + + public Point2d[] ToPoints() + { + Point2d a = MinPoint;// min + Point2d b = new(_Right, _Y); + Point2d c = MaxPoint;// max + Point2d d = new(_X, _Top); + return new Point2d[] { a, b, c, d }; + } + + public (Point2d boxMin, Point2d boxRigthDown, Point2d boxMax, Point2d boxLeftUp) ToPoints4() + { + Point2d a = MinPoint;// min + Point2d b = new(_Right, _Y); + Point2d c = MaxPoint;// max + Point2d d = new(_X, _Top); + return (a, b, c, d); + } + + /// + /// 四周膨胀 + /// + /// + public Rect Expand(double d) + { + return new Rect(_X - d, _Y - d, _Right + d, _Top + d); + } + + /// + /// 是否矩形(带角度) + /// + /// + /// + public static bool IsRectAngle(List? ptList, double tolerance = 1e-8) + { + if (ptList == null) + throw new ArgumentNullException(nameof(ptList)); + + var pts = ptList.ToList(); + /* + * 消重,不这里设置,否则这不是一个正确的单元测试 + * // var ptList = pts.Distinct().ToList(); + * var ptList = pts.DistinctExBy((a, b) => a.DistanceTo(b) < 1e-6).ToList(); + */ + if (ptList.Count == 5) + { + // 首尾点相同移除最后 + if (pts[0].IsEqualTo(pts[^1], CadTolerance)) + pts.RemoveAt(pts.Count - 1); + } + if (pts.Count != 4) + return false; + + // 最快的方案 + // 点乘求值法:(为了处理 正梯形/平行四边形 需要三次) + // 这里的容差要在1e-8内,因为点乘的三次浮点数乘法会令精度变低 + var dot = DotProductValue(pts[0], pts[1], pts[3]); + if (Math.Abs(dot) < tolerance) + { + dot = DotProductValue(pts[1], pts[2], pts[0]); + if (Math.Abs(dot) < tolerance) + { + dot = DotProductValue(pts[2], pts[3], pts[1]); + return Math.Abs(dot) < tolerance; + } + } + return false; + } + + /// + /// 点积,求值 + /// 1.是两个向量的长度与它们夹角余弦的积 + /// 2.求四个点是否矩形使用 + /// + /// 原点 + /// 点 + /// 点 + /// 0方向相同,夹角0~90度;=0相互垂直;<0方向相反,夹角90~180度]]> + static double DotProductValue(Point2d o, Point2d a, Point2d b) + { + var oa = o.GetVectorTo(a); + var ob = o.GetVectorTo(b); + return (oa.X * ob.X) + (oa.Y * ob.Y); + } + + /// + /// 是否轴向矩形(无角度) + /// + public static bool IsRect(List? ptList, double tolerance = 1e-10) + { + if (ptList == null) + throw new ArgumentNullException(nameof(ptList)); + + var pts = ptList.ToList(); + if (ptList.Count == 5) + { + // 首尾点相同移除最后 + if (pts[0].IsEqualTo(pts[^1], CadTolerance)) + pts.RemoveAt(pts.Count - 1); + } + if (pts.Count != 4) + return false; + + return Math.Abs(pts[0].X - pts[3].X) < tolerance && + Math.Abs(pts[0].Y - pts[1].Y) < tolerance && + Math.Abs(pts[1].X - pts[2].X) < tolerance && + Math.Abs(pts[2].Y - pts[3].Y) < tolerance; + } + + /// + /// 获取点集的包围盒的最小点和最大点(无角度) + /// + /// + public static (Point2d boxMin, Point2d boxMax) GetMinMax(IEnumerable pts) + { + var xMin = double.MaxValue; + var xMax = double.MinValue; + var yMin = double.MaxValue; + var yMax = double.MinValue; + // var zMin = double.MaxValue; + // var zMax = double.MinValue; + + pts.ForEach(p => { + xMin = Math.Min(p.X, xMin); + xMax = Math.Max(p.X, xMax); + yMin = Math.Min(p.Y, yMin); + yMax = Math.Max(p.Y, yMax); + // zMin = Math.Min(p.Z, zMin); + // zMax = Math.Max(p.Z, zMax); + }); + return (new Point2d(xMin, yMin), new Point2d(xMax, yMax)); + } + + /// + /// 矩形点序逆时针排列,将min点[0],max点是[3](带角度) + /// + /// + /// + public static bool RectAnglePointOrder(List? pts) + { + if (pts == null) + throw new ArgumentNullException(nameof(pts)); + + if (!Rect.IsRectAngle(pts)) + return false; + + // 获取min和max点(非包围盒) + pts = pts.OrderBy(a => a.X).ThenBy(a => a.Y).ToList(); + var minPt = pts.First(); + var maxPt = pts.Last(); + var link = new LoopList(); + link.AddRange(pts); + + pts.Clear(); + // 排序这四个点,左下/右下/右上/左上 + var node = link.Find(minPt); + for (int i = 0; i < 4; i++) + { + pts.Add(node!.Value); + node = node.Next; + } + // 保证是逆时针 + var isAcw = CrossAclockwise(pts[0], pts[1], pts[2]); + if (!isAcw) + (pts[3], pts[1]) = (pts[1], pts[3]); + return true; + } + + /// + /// 叉积,二维叉乘计算 + /// + /// 传参是向量,表示原点是0,0 + /// 传参是向量,表示原点是0,0 + /// 其模为a与b构成的平行四边形面积 + static double Cross(Vector2d a, Vector2d b) + { + return a.X * b.Y - a.Y * b.X; + } + + /// + /// 叉积,二维叉乘计算 + /// + /// 原点 + /// oa向量 + /// ob向量,此为判断点 + /// 返回值有正负,表示绕原点四象限的位置变换,也就是有向面积 + static double Cross(Point2d o, Point2d a, Point2d b) + { + return Cross(o.GetVectorTo(a), o.GetVectorTo(b)); + } + + /// + /// 叉积,逆时针方向为真 + /// + /// 直线点1 + /// 直线点2 + /// 判断点 + /// b点在oa的逆时针 + static bool CrossAclockwise(Point2d o, Point2d a, Point2d b) + { + return Cross(o, a, b) > -1e-6;// 浮点数容差考虑 + } + +#if !WinForm + public Entity ToPolyLine() + { + var bv = new List(); + var pts = ToPoints(); + Polyline pl = new(); + pl.SetDatabaseDefaults(); + pts.ForEach((vertex, state, index) => { + pl.AddVertexAt(index, vertex, 0, 0, 0); + }); + return pl; + } +#endif + + /// + /// 列扫碰撞检测(碰撞算法) + /// 比四叉树还快哦~ + /// + /// + /// 继承Rect的集合 + /// 先处理集合每一个成员;返回true就跳过后续委托 + /// 碰撞,返回两个碰撞的成员;返回true就跳过后续委托 + /// 后处理集合每一个成员 + public static void XCollision(List box, + Func firstProcessing, + Func collisionProcessing, + Action lastProcessing) where T : Rect + { + // 先排序X:不需要Y排序,因为Y的上下浮动不共X .ThenBy(a => a.Box.Y) + // 因为先排序就可以有序遍历x区间,超过就break,达到更快 + box = box.OrderBy(a => a._X).ToList(); + + // 遍历所有图元 + for (int i = 0; i < box.Count; i++) + { + var oneRect = box[i]; + if (firstProcessing(oneRect)) + continue; + + bool actionlast = true; + + // 搜索范围要在 one 的头尾中间的部分 + for (int j = i + 1; j < box.Count; j++) + { + var twoRect = box[j]; + // x碰撞:矩形2的Left 在 矩形1[Left-Right]闭区间;穿过的话,也必然有自己的Left因此不需要处理 + if (oneRect._X <= twoRect._X && twoRect._X <= oneRect._Right) + { + // y碰撞,那就是真的碰撞了 + if ((oneRect._Top >= twoRect._Top && twoRect._Top >= oneRect._Y) /*包容上边*/ + || (oneRect._Top >= twoRect._Y && twoRect._Y >= oneRect._Y) /*包容下边*/ + || (twoRect._Top >= oneRect._Top && oneRect._Y >= twoRect._Y)) /*穿过*/ + { + if (collisionProcessing(oneRect, twoRect)) + actionlast = false; + } + // 这里想中断y高过它的无意义比较, + // 但是必须排序Y,而排序Y必须同X,而这里不是同X(而是同X区间),所以不能中断 + // 而做到X区间排序,就必须创造一个集合,再排序这个集合, + // 会导致每个图元都拥有一次X区间集合,开销更巨大(因此放弃). + } + else + break;// 因为已经排序了,后续的必然超过 x碰撞区间 + } + + if (actionlast) + lastProcessing(oneRect); + } + } + + #endregion + + #region 转换类型 +#if !WinForm + // 隐式转换(相当于是重载赋值运算符) + // public static implicit operator Rect(System.Windows.Rect rect) + // { + // return new Rect(rect.Left, rect.Bottom, rect.Right, rect.Top); + // } + public static implicit operator Rect(System.Drawing.RectangleF rect) + { + return new Rect(rect.Left, rect.Bottom, rect.Right, rect.Top); + } + public static implicit operator Rect(System.Drawing.Rectangle rect) + { + return new Rect(rect.Left, rect.Bottom, rect.Right, rect.Top); + } +#endif + + #region ToString + public sealed override string ToString() + { + return ToString(null, null); + } + public string ToString(IFormatProvider? provider) + { + return ToString(null, provider); + } + public string ToString(string? format = null, IFormatProvider? formatProvider = null) + { + return $"({_X.ToString(format, formatProvider)},{_Y.ToString(format, formatProvider)})," + + $"({_Right.ToString(format, formatProvider)},{_Top.ToString(format, formatProvider)})"; + + // return $"X={_X.ToString(format, formatProvider)}," + + // $"Y={_Y.ToString(format, formatProvider)}," + + // $"Right={_Right.ToString(format, formatProvider)}," + + // $"Top={_Top.ToString(format, formatProvider)}"; + } + + /*为了红黑树,加入这个*/ + public int CompareTo(Rect rect) + { + if (rect == null) + return -1; + if (_X < rect._X) + return -1; + else if (_X > rect._X) + return 1; + else if (_Y < rect._Y)/*x是一样的*/ + return -1; + else if (_Y > rect._Y) + return 1; + return 0;/*全部一样*/ + } + #endregion + + #endregion +} \ No newline at end of file diff --git a/src/CAD/IFox.CAD.Shared/Copyclip/BitmapTool.cs b/src/CAD/IFox.CAD.Shared/Copyclip/BitmapTool.cs new file mode 100644 index 0000000..9ce266b --- /dev/null +++ b/src/CAD/IFox.CAD.Shared/Copyclip/BitmapTool.cs @@ -0,0 +1,153 @@ +namespace IFoxCAD.Cad; + +using System; + +public class BitmapTool +{ + // https://blog.csdn.net/shellching/article/details/18405185 + /// Windows不允许程序员直接访问硬件, + /// 它对屏幕的操作是通过环境设备,也就是DC来完成的 + /// 屏幕上的每一个窗口都对应一个DC,可以把DC想象成一个视频缓冲区, + /// 对这这个缓冲区的操作,会表现在这个缓冲区对应的屏幕窗口上. + /// 在窗口的DC之外,可以建立自己的DC,就是说它不对应窗口, + /// 这个方法就是 CreateCompatibleDC,这个DC就是一个内存缓冲区, + /// 通过这个DC你可以把和它兼容的窗口DC保存到这个DC中, + /// 就是说你可以通过它在不同的DC之间拷贝数据. + /// 例如:你先在这个DC中建立好数据,然后在拷贝到窗口的DC就是完成了这个窗口的刷新 + + /// + /// 检索指定窗口的工作区的显示设备上下文(DC)的句柄
    + /// 显示设备上下文可以在随后的图形显示界面(GDI)函数中使用,
    + /// 以在窗口的工作区中绘制
    + ///
    + /// + /// + [DllImport("user32.dll", SetLastError = true)] + public static extern IntPtr GetDC(IntPtr hWnd); + + [DllImport("user32.dll")] + public static extern bool ReleaseDC(IntPtr hWnd, IntPtr hDC); + + /// + /// 创建DC + /// + /// + /// + [DllImport("gdi32.dll", EntryPoint = "CreateCompatibleDC", SetLastError = true)] + public static extern IntPtr CreateCompatibleDC([In] IntPtr hdc); + + /// + /// Creates a bitmap compatible with the device that is associated with the specified device context. + /// + /// A handle to a device context. + /// The bitmap width, in pixels. + /// The bitmap height, in pixels. + /// If the function succeeds, the return value is a handle to the compatible bitmap (DDB). If the function fails, the return value is . + [DllImport("gdi32.dll", EntryPoint = "CreateCompatibleBitmap")] + public static extern IntPtr CreateCompatibleBitmap([In] IntPtr hdc, int nWidth, int nHeight); + + /// Selects an object into the specified device context (DC). The new object replaces the previous object of the same type. + /// A handle to the DC. + /// A handle to the object to be selected. + /// + /// If the selected object is not a region and the function succeeds, the return value is a handle to the object being replaced. If the selected object is a region and the function succeeds, the return value is one of the following values. + /// SIMPLEREGION - Region consists of a single rectangle. + /// COMPLEXREGION - Region consists of more than one rectangle. + /// NULLREGION - Region is empty. + /// If an error occurs and the selected object is not a region, the return value is NULL. Otherwise, it is HGDI_ERROR. + /// + /// + /// This function returns the previously selected object of the specified type. An application should always replace a new object with the original, default object after it has finished drawing with the new object. + /// An application cannot select a single bitmap into more than one DC at a time. + /// ICM: If the object being selected is a brush or a pen, color management is performed. + /// + [DllImport("gdi32.dll", EntryPoint = "SelectObject")] + public static extern IntPtr SelectObject([In] IntPtr hdc, [In] IntPtr hgdiobj); + + /// Deletes a logical pen, brush, font, bitmap, region, or palette, freeing all system resources associated with the object. After the object is deleted, the specified handle is no longer valid. + /// A handle to a logical pen, brush, font, bitmap, region, or palette. + /// + /// If the function succeeds, the return value is nonzero. + /// If the specified handle is not valid or is currently selected into a DC, the return value is zero. + /// + /// + /// Do not delete a drawing object (pen or brush) while it is still selected into a DC. + /// When a pattern brush is deleted, the bitmap associated with the brush is not deleted. The bitmap must be deleted independently. + /// + [DllImport("gdi32.dll", EntryPoint = "DeleteObject")] + [return: MarshalAs(UnmanagedType.Bool)] + public static extern bool DeleteObject([In] IntPtr hObject); + + /// + /// 指定的源设备环境区域中的像素进行位块转换,以传送到目标设备环境 + /// + /// Handle to the destination device context. + /// The leftmost x-coordinate of the destination rectangle (in pixels). + /// The topmost y-coordinate of the destination rectangle (in pixels). + /// The width of the source and destination rectangles (in pixels). + /// The height of the source and the destination rectangles (in pixels). + /// Handle to the source device context. + /// The leftmost x-coordinate of the source rectangle (in pixels). + /// The topmost y-coordinate of the source rectangle (in pixels). + /// A raster-operation code. + /// + /// true if the operation succeedes, false otherwise. To get extended error information, call . + /// + [DllImport("gdi32.dll", EntryPoint = "BitBlt", SetLastError = true)] + [return: MarshalAs(UnmanagedType.Bool)] + public static extern bool BitBlt([In] IntPtr hdc, int nXDest, int nYDest, int nWidth, int nHeight, [In] IntPtr hdcSrc, int nXSrc, int nYSrc, TernaryRasterOperations dwRop); + + public enum TernaryRasterOperations : uint + { + SRCCOPY = 0x00CC0020, + SRCPAINT = 0x00EE0086, + SRCAND = 0x008800C6, + SRCINVERT = 0x00660046, + SRCERASE = 0x00440328, + NOTSRCCOPY = 0x00330008, + NOTSRCERASE = 0x001100A6, + MERGECOPY = 0x00C000CA, + MERGEPAINT = 0x00BB0226, + PATCOPY = 0x00F00021, + PATPAINT = 0x00FB0A09, + PATINVERT = 0x005A0049, + DSTINVERT = 0x00550009, + BLACKNESS = 0x00000042, + WHITENESS = 0x00FF0062, + CAPTUREBLT = 0x40000000 //only if WinVer >= 5.0.0 (see wingdi.h) + } + + /// + /// 截图成为BMP + /// + /// 截图的窗口 + /// 扔出BMP执行任务 + /// + public static void CaptureWndImage(IntPtr hWnd, Action action) + { + if (action == null) + throw new ArgumentNullException(nameof(action)); + + var hDC = GetDC(hWnd); + var hMemDC = CreateCompatibleDC(hDC); + if (hMemDC == IntPtr.Zero) + return; + + WindowsAPI.GetClientRect(hWnd, out WindowsAPI.IntRect rcClient); + int width = rcClient.Right - rcClient.Left; + int height = rcClient.Bottom - rcClient.Top; + + var hBitmap = CreateCompatibleBitmap(hDC, width, height); + if (hBitmap != IntPtr.Zero) + { + SelectObject(hMemDC, hBitmap); + if (BitBlt(hMemDC, 0, 0, width, height, + hDC, 0, 0, TernaryRasterOperations.SRCCOPY)) + { + action.Invoke(hBitmap); + } + DeleteObject(hBitmap); + } + DeleteObject(hMemDC); + } +} \ No newline at end of file diff --git a/src/CAD/IFox.CAD.Shared/Copyclip/EmfTool.cs b/src/CAD/IFox.CAD.Shared/Copyclip/EmfTool.cs new file mode 100644 index 0000000..773cf6d --- /dev/null +++ b/src/CAD/IFox.CAD.Shared/Copyclip/EmfTool.cs @@ -0,0 +1,922 @@ +namespace IFoxCAD.Cad; + +using System; +using System.Diagnostics; +using System.Drawing; +using System.Drawing.Imaging; +using System.Text; +using static IFoxCAD.Cad.WindowsAPI; +using Point = System.Drawing.Point; +using Size = System.Drawing.Size; + +// DWORD == uint +// WORD == ushort +// LONG == int + +/* + * Console.WriteLine(Marshal.SizeOf(typeof(PlaceableMetaHeader))); + * Console.WriteLine(Marshal.SizeOf(typeof(WindowsMetaHeader))); + * Console.WriteLine(Marshal.SizeOf(typeof(StandardMetaRecord))); + */ + + +// https://learn.microsoft.com/en-us/windows/win32/api/wingdi/ns-wingdi-metafilepict +// http://www.cppblog.com/zwp/archive/2012/02/25/60225.html +[Serializable] +[StructLayout(LayoutKind.Sequential, Pack = 2)] +public struct MetaFilePict +{ + public MappingModes mm; + public int xExt; + public int yExt; + public IntPtr hMF; //内存图元文件的句柄 +} + +public enum MappingModes : int +{ + MM_TEXT = 1, + MM_LOMETRIC = 2, + MM_HIMETRIC = 3, + MM_LOENGLISH = 4,//逻辑坐标的单位为0.01英寸 + MM_HIENGLISH = 5, + MM_TWIPS = 6, + MM_ISOTROPIC = 7, + MM_ANISOTROPIC = 8, + + //Minimum and Maximum Mapping Mode values + MM_MIN = MM_TEXT, + MM_MAX = MM_ANISOTROPIC, + MM_MAX_FIXEDSCALE = MM_TWIPS, +} + + +//WMF 文件格式: +//https://blog.51cto.com/chenyanxi/803247 +//文件缩放信息:22字节 +[Serializable] +[StructLayout(LayoutKind.Sequential, Pack = 2)] +public struct PlaceableMetaHeader +{ + public uint Key; /* 固定大小以相反顺序出现 9AC6CDD7h */ + public ushort Handle; /* Metafile HANDLE number (always 0) */ + + public short Left; /* Left coordinate in metafile units */ + public short Top; /* Top coordinate in metafile units */ + public short Right; /* Right coordinate in metafile units */ + public short Bottom; /* Bottom coordinate in metafile units */ + + public ushort Inch; /* Number of metafile units per inch */ + public uint Reserved; /* Reserved (always 0) */ + public ushort Checksum; /* Checksum value for previous 10 WORDs */ + + // 微软的wmf文件分为两种一种是标准的图元文件, + // 一种是活动式图元文件,活动式图元文件 与 标准的图元文件 的主要区别是, + // 活动式图元文件包含了图像的原始大小和缩放信息. + /// + /// 是活动式图元文件 + /// + public bool IsActivity => Key == 0x9AC6CDD7; + + /// + /// wmf转为emf
    + ///
    + /// 文件路径 + /// + /// 错误: ;
    + /// 成功: 返回一个增强型图元 emf文件句柄 (位于内存中) + ///
    + /// + public static IntPtr Wmf2Emf(string wmfFile) + { + using FileStream file = new(wmfFile, FileMode.Open, FileAccess.Read, FileShare.ReadWrite); // FileShare才能进c盘 + if (file.Length == 0) + throw new IOException("文件字节0长:" + file); + if (file.Length < 5) + throw new IOException("无法校验文件签名:" + file); + + var fileByte = new byte[file.Length]; + file.Read(fileByte, 0, fileByte.Length); + file.Close(); + + var sWMF = BytesToStruct(fileByte); + // 转为emf的地址 + IntPtr hEMF = IntPtr.Zero; + + // 控制输出的时候跟cad一样带有一个矩形框边界,而不是所有图元的包围盒作为边界 + var mpType = new MetaFilePict + { + mm = MappingModes.MM_ANISOTROPIC, + xExt = sWMF.Right - sWMF.Left, + yExt = sWMF.Bottom - sWMF.Top, + hMF = IntPtr.Zero + }; + + // byte[] 指针偏移 + int iOffset = 0; + if (sWMF.IsActivity) + iOffset = Marshal.SizeOf(typeof(PlaceableMetaHeader)); + + unsafe + { + // 安全指针方法 + //IntPtr fileIntPtr = Marshal.UnsafeAddrOfPinnedArrayElement(fileByte, iOffset); + // 不安全指针方法 + fixed (byte* fileIntPtr = &fileByte[iOffset]) + hEMF = EmfTool.SetWinMetaFileBits( + (uint)fileByte.Length, new IntPtr(fileIntPtr), IntPtr.Zero, new IntPtr(&mpType)); + } + return hEMF; + } +} + +public class Emf +{ + public IntPtr EmfHandle; + + /// + /// 转换wmf到emf + /// + /// + public void Wmf2Emf(string wmfFile) + { + EmfHandle = PlaceableMetaHeader.Wmf2Emf(wmfFile);//emf文件句柄 + } + + /// + /// 获取emf结构 + /// + /// + public EnhMetaHeader CreateEnhMetaHeader() + { + if (EmfHandle == IntPtr.Zero) + throw new ArgumentException(nameof(EmfHandle) + "== IntPtr.Zero"); + return EnhMetaHeader.Create(EmfHandle); + } +} + +//紧接文件缩放信息的是 WMFHEAD, 18字节 +[Serializable] +[StructLayout(LayoutKind.Sequential, Pack = 2)] +public struct WindowsMetaHeader +{ + public ushort FileType; /* Type of metafile (0=memory, 1=disk) */ + public ushort HeaderSize; /* Size of header in WORDS (always 9) */ + public ushort Version; /* Version of Microsoft Windows used */ + public uint FileSize; /* Total size of the metafile in WORDs */ + public ushort NumOfObjects; /* Number of objects in the file */ + public uint MaxRecordSize; /* The size of largest record in WORDs */ + public ushort NumOfParams;    /* Not Used (always 0) */ +} + +//紧接 WMFHEAD 的是 WMFRECORD, 14字节 +[Serializable] +[StructLayout(LayoutKind.Sequential, Pack = 2)] +public struct StandardMetaRecord +{ + public uint Size; /* Total size of the record in WORDs */ + public ushort Function; /* Function number (defined in WINDOWS.H) */ + public ushort[] Parameters;  /* Parameter values passed to function */ +} + +// 文件结构:头记录(ENHMETAHEADER),各记录(ENHMETARECORD),文件结尾(EMR_EOF) +// https://www.cnblogs.com/5iedu/p/4706327.html +[Serializable] +[StructLayout(LayoutKind.Sequential, Pack = 2)] +public struct EnhMetaHeader +{ + [Description("记录类型")] + public uint iType; + [Description("结构大小")] + public int nSize; //注意这个大小是含描述字符串的长度,即等于sizeof(ENHMETAHEADER)+nDescription*2 + [Description("外接矩形(单位是像素)")] + public IntRect rclBounds; + [Description("图片矩形(单位是 0.1 毫米)")] + public IntRect rclFrame; + [Description("文件签名")] + public uint dSignature; + [Description("文件版本")] + public uint nVersion; + [Description("文件尺寸")] + public uint nBytes; + [Description("记录数")] + public uint nRecords; + [Description("句柄数")] + public ushort nHandles; + [Description("保留")] + public ushort sReserved; + [Description("说明文本的长度")] + public uint nDescription; + [Description("说明文本的偏移量")] + public uint offDescription; + [Description("调色板的元素数")] + public uint nPalEntries; + [Description("分辨率(像素)")] + public IntSize szlDevice; + [Description("分辨率(毫米)")] + public IntSize szlMillimeters; + [Description("像素格式的尺寸")] + public uint cbPixelFormat; + [Description("像素格式的起始偏移位置")] + public uint offPixelFormat; + [Description("在不含OpenGL记录时,该值为FALSE")] + public uint bOpenGL; + [Description("参考设备的尺寸(微米)")] + public IntSize szlMicrometers; + + public override string ToString() + { + //var tp = GetType(); + //var sb = new StringBuilder(); + //sb.AppendLine(EnumEx.GetDesc(tp, nameof(iType)) + ":" + iType); + + // 输出json + // NET472 System.Text.Json + var serializer = new System.Web.Script.Serialization.JavaScriptSerializer(); + string jsonString = serializer.Serialize(this); + return jsonString; + } + + /// + /// 通过wmf创建 + /// + /// + /// + public static EnhMetaHeader Create(string wmf) + { + var emf = PlaceableMetaHeader.Wmf2Emf(wmf); + if (emf == IntPtr.Zero) + throw new ArgumentException(nameof(emf)); + return Create(emf); + } + + /// + /// 通过emf指针创建 + /// + /// 参数1的结构体首地址
    + /// 也就是的返回值 + /// + /// + public static EnhMetaHeader Create(IntPtr emf) + { + var len = EmfTool.GetEnhMetaFileHeader(emf, 0, IntPtr.Zero); + if (len == 0) + throw new ArgumentException(nameof(len)); + + IntPtr header = Marshal.AllocHGlobal((int)len); + EmfTool.GetEnhMetaFileHeader(emf, len, header);//这里是切割获取内部的bytes,存放在header + + var result = (EnhMetaHeader)Marshal.PtrToStructure(header, typeof(EnhMetaHeader)); + + Marshal.FreeHGlobal(header); + return result; + } +} + + + +public static class EmfTool +{ + /// + /// 保存 + /// + /// GetEnhMetaFileBits 参数1的结构体首地址 + /// 保存路径 + /// + public static void Save(IntPtr clipTypeData, string file) + { + // 保存emf文件 + // https://blog.csdn.net/tigertianx/article/details/7098490 + var len = EmfTool.GetEnhMetaFileBits(clipTypeData, 0, null!); + if (len != 0) + { + var bytes = new byte[len]; + _ = EmfTool.GetEnhMetaFileBits(clipTypeData, len, bytes); + + using MemoryStream ms1 = new(bytes); + using var bm = Image.FromStream(ms1);//此方法emf保存成任何版本都会变成png + bm.Save(file); + } + } + + /// + /// 返回对一个增强型图元文件的说明 + /// + /// 目标增强型图元文件的句柄 + /// lpszDescription缓冲区的长度 + /// 指定一个预先初始化好的字串缓冲区,准备随同图元文件说明载入; + /// 参考 CreateEnhMetaFile 函数,了解增强型图元文件说明字串的具体格式 + /// + [DllImport("gdi32.dll")] + static extern uint GetEnhMetaFileDescription(IntPtr hemf, uint cchBuffer, [MarshalAs(UnmanagedType.LPStr)] StringBuilder lpDescription); + + /// + /// 获取emf描述 + /// + /// 文件句柄 + /// 描述的内容 + [System.Diagnostics.DebuggerStepThrough] + [System.CodeDom.Compiler.GeneratedCode("InteropSignatureToolkit", "0.9 Beta1")]//初始化时指定生成代码的工具的名称和版本 + public static string? GetEnhMetaFileDescriptionEx(IntPtr clipTypeData) + { + var len = GetEnhMetaFileDescription(clipTypeData, 0, null!); + if (len != 0) + { + StringBuilder desc = new((int)len); + GetEnhMetaFileDescription(clipTypeData, (uint)desc.Capacity, desc); + return desc.ToString(); + } + return null; + } + + public enum DeviceCap : int + { + /// + /// Device driver version + /// + DRIVERVERSION = 0, + /// + /// Device classification + /// + TECHNOLOGY = 2, + /// + /// Horizontal size in millimeters + /// + HORZSIZE = 4, + /// + /// Vertical size in millimeters + /// + VERTSIZE = 6, + /// + /// Horizontal width in pixels + /// + HORZRES = 8, + /// + /// Vertical height in pixels + /// + VERTRES = 10, + /// + /// Number of bits per pixel + /// + BITSPIXEL = 12, + /// + /// Number of planes + /// + PLANES = 14, + /// + /// Number of brushes the device has + /// + NUMBRUSHES = 16, + /// + /// Number of pens the device has + /// + NUMPENS = 18, + /// + /// Number of markers the device has + /// + NUMMARKERS = 20, + /// + /// Number of fonts the device has + /// + NUMFONTS = 22, + /// + /// Number of colors the device supports + /// + NUMCOLORS = 24, + /// + /// Size required for device descriptor + /// + PDEVICESIZE = 26, + /// + /// Curve capabilities + /// + CURVECAPS = 28, + /// + /// Line capabilities + /// + LINECAPS = 30, + /// + /// Polygonal capabilities + /// + POLYGONALCAPS = 32, + /// + /// Text capabilities + /// + TEXTCAPS = 34, + /// + /// Clipping capabilities + /// + CLIPCAPS = 36, + /// + /// Bitblt capabilities + /// + RASTERCAPS = 38, + /// + /// Length of the X leg + /// + ASPECTX = 40, + /// + /// Length of the Y leg + /// + ASPECTY = 42, + /// + /// Length of the hypotenuse + /// + ASPECTXY = 44, + /// + /// Shading and Blending caps + /// + SHADEBLENDCAPS = 45, + + /// + /// Logical pixels inch in X + /// + LOGPIXELSX = 88, + /// + /// Logical pixels inch in Y + /// + LOGPIXELSY = 90, + + /// + /// Number of entries in physical palette + /// + SIZEPALETTE = 104, + /// + /// Number of reserved entries in palette + /// + NUMRESERVED = 106, + /// + /// Actual color resolution + /// + COLORRES = 108, + + // Printing related DeviceCaps. These replace the appropriate Escapes + /// + /// Physical Width in device units + /// + PHYSICALWIDTH = 110, + /// + /// Physical Height in device units + /// + PHYSICALHEIGHT = 111, + /// + /// Physical Printable Area x margin + /// + PHYSICALOFFSETX = 112, + /// + /// Physical Printable Area y margin + /// + PHYSICALOFFSETY = 113, + /// + /// Scaling factor x + /// + SCALINGFACTORX = 114, + /// + /// Scaling factor y + /// + SCALINGFACTORY = 115, + + /// + /// Current vertical refresh rate of the display device (for displays only) in Hz + /// + VREFRESH = 116, + /// + /// Vertical height of entire desktop in pixels + /// + DESKTOPVERTRES = 117, + /// + /// Horizontal width of entire desktop in pixels + /// + DESKTOPHORZRES = 118, + /// + /// Preferred blt alignment + /// + BLTALIGNMENT = 119 + } + + [DllImport("gdi32.dll")] + static extern int GetDeviceCaps(IntPtr hDC, DeviceCap nIndex); + + [DllImport("gdi32.dll")] + static extern int SetMapMode(IntPtr hDC, MappingModes fnMapMode); + + [DllImport("gdi32.dll")] + static extern bool SetViewportOrgEx(IntPtr hDC, int x, int y, Point[] prevPoint); + + [DllImport("gdi32.dll")] + static extern bool SetWindowOrgEx(IntPtr hDC, int x, int y, Point[] prevPoint); + + [DllImport("gdi32.dll")] + static extern bool SetViewportExtEx(IntPtr hDC, int nExtentX, int nExtentY, Size[] prevSize); + + [DllImport("gdi32.dll")] + static extern bool SetWindowExtEx(IntPtr hDC, int nExtentX, int nExtentY, Size[] prevSize); + + [DllImport("Gdi32.dll")] + public static extern int CreatePen(int nPenStyle, int nWidth, int nColor); + + [DllImport("Gdi32.dll")] + public static extern int GetStockObject(int nStockBrush); + + [DllImport("Gdi32.dll")] + public static extern int SelectObject(IntPtr hDC, int hGdiObject); + + [DllImport("Gdi32.dll")] + public static extern int DeleteObject(int hBitmap); + + [DllImport("Gdi32.dll")] + public static extern int MoveToEx(IntPtr hDC, int x, int y, int nPreviousPoint); + + [DllImport("Gdi32.dll")] + public static extern int LineTo(IntPtr hDC, int x, int y); + + [DllImport("Gdi32.dll")] + public static extern int Rectangle(IntPtr hDC, int nLeft, int nTop, int nRight, int nBottom); + + [DllImport("Gdi32.dll")] + public static extern bool DPtoLP(IntPtr hdc, [In, Out] Point[] lpPoints, int nCount); + + + /// + /// 设置emf描述 + /// + /// emf文件句柄 + /// 设置描述 + /// 新的emf指针 + /// + public static void SetEnhMetaFileDescriptionEx(ref IntPtr hMetaFile, string desc) + { + if (hMetaFile == IntPtr.Zero) + throw new ArgumentNullException(nameof(hMetaFile)); + + var emh = EnhMetaHeader.Create(hMetaFile);//emf结构 GetEnhMetaFileHeader + // 创建画布句柄 + IntRect intRect = emh.rclFrame; //new(0, 0, 0, 0); + var hMetaDC = EmfTool.CreateEnhMetaFile(IntPtr.Zero, null!, ref intRect, desc); + if (hMetaDC == IntPtr.Zero) + return; + //SetMapMode(hMetaDC, MappingModes.MM_ANISOTROPIC); // 默认的就是这个模式 + //SetMapMode(hMetaDC, MappingModes.MM_HIMETRIC);//逻辑单位:0.01mm + + // 设置单位 + //var size = new IntSize(0, 0); + //EmfTool.SetWindowExtEx(hMetaDC, 0, 0, ref size); + //EmfTool.SetViewportExtEx(hMetaDC, 0, 0, ref size); + //EmfTool.GetEnhMetaFilePaletteEntries() 统一调色 + //SetViewportOrgEx(hMetaDC, 0, 0, null!);//将视口原点设在左下角 + + // 旧的克隆到新的 + /* + * 第18章 图元文件_18.2 增强型图元文件(emf)(2) + * https://blog.51cto.com/u_15082403/3724715 + * 方案2——利用图像的物理尺寸 + * 通过rclFrame字段(是设备单位:0.01mm)显示出来的刻度尺,这样不管在视频显示器 + * 打印机上,显示出来的刻度尺都较为真实 + */ + //目标设备信息 + int cxMms = GetDeviceCaps(hMetaDC, DeviceCap.HORZSIZE);//宽度(单位:mm) + int cyMms = GetDeviceCaps(hMetaDC, DeviceCap.VERTSIZE);//高度(单位:mm) + var cxArea = cxMms; + var cyArea = cyMms; +#if true2 + int cxPix = GetDeviceCaps(hMetaDC, DeviceCap.HORZRES);//宽度(单位:像素) + int cyPix = GetDeviceCaps(hMetaDC, DeviceCap.VERTRES);//高度(单位:像素) + int cxImage = emh.rclFrame.Right - emh.rclFrame.Left; //单位:0.01mm + int cyImage = emh.rclFrame.Bottom - emh.rclFrame.Top; + + // 设置之后图像就没有拉伸了,但是跑偏了 + //将图元文件大小(0.01mm为单位)转换为像素大小 + cxImage = cxImage * cxPix / cxMms / 100; + cyImage = cyImage * cyPix / cyMms / 100; + + //在指定的矩形区内,水平和垂直居中显示图元文件,同时保证了区域的大小为cxImage和cyImage + int left = (cxArea - cxImage) / 2; + int right = (cxArea + cxImage) / 2; + int top = (cyArea - cyImage) / 2; + int bottom = (cyArea + cyImage) / 2; +#else + cxArea = 0; + cyArea = 0; + SetMapMode(hMetaDC, MappingModes.MM_HIMETRIC);//逻辑单位:0.01mm + SetViewportOrgEx(hMetaDC, 0, cyArea, null!);//将视口原点设在左下角 + var pt = new Point(cxArea, 0); + + int cxImage = emh.rclFrame.Right - emh.rclFrame.Left; //单位:0.01mm + int cyImage = emh.rclFrame.Bottom - emh.rclFrame.Top; + + //在指定的矩形区内,水平和垂直居中显示图元文件,同时保证了区域的大小为cxImage和cyImage + int left = (pt.X - cxImage) / 2; + int right = (pt.X + cxImage) / 2; + int top = (pt.Y + cyImage) / 2; //注意,这里与前面例子不同 + int bottom = (pt.Y - cyImage) / 2; //注意,这里与前面例子不同 +#endif + var rect = new IntRect(left, top, right, bottom); + + // 图像拉伸了 + //bool pef = EmfTool.EnumEnhMetaFile(hMetaDC, hMetaFile, IntPtr.Zero, IntPtr.Zero, ref emhValue.rclFrame);// 这个失败 + bool pef = EmfTool.PlayEnhMetaFile(hMetaDC, hMetaFile, ref rect); + if (!pef) + { + DeleteObject(hMetaDC); + Debugger.Break(); + return; + } + // 删除旧的图元文件句柄,返回新的 + var del = EmfTool.DeleteEnhMetaFile(hMetaFile); + if (del) + hMetaFile = EmfTool.CloseEnhMetaFile(hMetaDC); + } + + [DllImport("gdi32.dll", EntryPoint = "GetEnhMetaFileHeader")] + public static extern uint GetEnhMetaFileHeader(IntPtr hemf, uint cbBuffer, IntPtr /*ENHMETAHEADER*/ lpemh); + + + /// + /// 将一个标准Windows图元文件转换成增强型图元文件 + /// + /// 数组的长度 + /// + /// 数组包含了标准图元文件数据.
    + /// 常用 GetMetaFileBitsEx 或 GetWinMetaFileBits 函数获得 + /// + /// + /// 用于决定原始格式及图元文件分辨率的一个参考设备场景;
    + /// 采用显示器分辨率为: + /// + /// + /// 定义一个图元文件附加参考信息的结构
    + /// 为null时,会假定使用当前显示器的 MM_ANISOTROPIC 映射模式 + /// + /// + /// 错误: ;
    + /// 成功: 返回一个增强型图元emf文件的指针(位于内存中) + ///
    + [DllImport("gdi32.dll", EntryPoint = "SetWinMetaFileBits")] + public static extern IntPtr SetWinMetaFileBits(uint nSize, IntPtr lpMeta16Data, IntPtr hdcRef, IntPtr lpMFP); + /// + /// 获取矢量图的byte + /// + /// + /// + /// + /// + [DllImport("gdi32.dll")] + public static extern uint GetEnhMetaFileBits(IntPtr hemf, uint cbBuffer, byte[] lpbBuffer); + /// + /// byte转换矢量图 + /// + /// + /// + /// + [DllImport("gdi32.dll")] + public static extern IntPtr SetEnhMetaFileBits(uint cbBuffer, byte[] lpBuffer); + /// + /// 删除矢量图 + /// + /// + /// + [DllImport("gdi32.dll")] + public static extern bool DeleteEnhMetaFile(IntPtr hemf); + + /// + /// 创建emf
    + /// https://www.cnblogs.com/5iedu/p/4706327.html + ///
    + /// 参考设备环境,null以整个屏幕为参考 + /// 指定文件名时,创建磁盘文件(.EMF),为null时创建内存图元文件 + /// 用于描述图元文件的大小和位置(以0.01mm为单位),可用它精确定义图元文件的物理尺寸 + /// 对图元文件的一段说明.包括创建应用程序的名字、一个NULL字符、对图元文件的一段说明以及两个NULL字符. + /// 返回画布句柄DC(图元文件句柄得调用 CloseEnhMetaFile 函数) + [DllImport("gdi32.dll", SetLastError = true)] + public static extern IntPtr CreateEnhMetaFile(IntPtr hdcRef, string szFilename, ref IntRect lpRect, string lpDescription); + + [DllImport("gdi32.dll", SetLastError = true)] + public static extern bool DeleteObject(IntPtr hdcRef); + + /// + /// 在指定的设备场景中画一个增强型图元文件;
    + /// 与标准图元文件不同,完成回放后,增强型图元文件会恢复设备场景以前的状态 + ///
    + /// 画布句柄 + /// 欲描绘的emf的图元文件句柄 + /// 指定显示区域(逻辑单位)GDI会缩放图像以适应该矩形范围 + /// + [DllImport("gdi32.dll")] + public static extern bool PlayEnhMetaFile(IntPtr hdcRef, IntPtr hemf, ref IntRect lpRect); + + // https://blog.csdn.net/hongke457546235/article/details/17404715 + /// + /// 逻辑单位设置窗口单位 + /// {只能在 MM_ISOTROPIC 或 MM_ANISOTROPIC 模式下使用下面两个函数} + /// + /// 画布句柄 + /// 以逻辑单位表示的新窗口区域的高度 + /// 以逻辑单位表示的新窗口区域的宽度 + /// 保存函数调用前窗口区域尺寸的SIZE结构地址,NULL则表示忽略调用前的尺寸 + [DllImport("gdi32.dll")] + public static extern bool SetWindowExtEx(IntPtr hdcRef, int nHeight, int nWidth, ref IntSize lpSize); + + /// + /// 视口区域的定义 + /// {只能在 MM_ISOTROPIC 或 MM_ANISOTROPIC 模式下使用下面两个函数} + /// + /// + /// + /// + /// + [DllImport("gdi32.dll")] + public static extern bool SetViewportExtEx(IntPtr hdcRef, int nHeight, int nWidth, ref IntSize lpSize); + + /// + /// 旧emf绘制新的hdcEMF中(即回放) + /// + /// 画布句柄 + /// 图元文件句柄 + /// 回调函数 + /// 传给回调函数的额外参数 + /// 在指定的矩形区内显示图元文件 + /// + [DllImport("gdi32.dll", SetLastError = true)] + public static extern bool EnumEnhMetaFile(IntPtr hdcRef, IntPtr hmf, IntPtr proc, IntPtr procParam, ref IntRect lpRect); + + /// + /// 返回图元文件句柄 + /// + /// 画布句柄 + [DllImport("gdi32.dll", SetLastError = true)] + public static extern IntPtr CloseEnhMetaFile(IntPtr hdcRef); + + // https://zhidao.baidu.com/question/646739770512964165/answer/1616737219.html?qq-pf-to=pcqq.c2c + //16位的函数 + [DllImport("gdi32.dll")] + public static extern IntPtr GetMetaFile(string path); + //32位的函数 + [DllImport("gdi32.dll")] + public static extern IntPtr GetEnhMetaFile(string path); + + + + /// + /// EMF保存到文件或者路径 + /// + /// EMF要复制的增强型图元文件的句柄 + /// 指向目标文件名称的指针,为NULL则将源图元文件复制到内存中 + /// + [DllImport("gdi32.dll")] + public static extern IntPtr CopyEnhMetaFile(IntPtr hemfSrc, string? lpszFile); + + /// + /// 矢量图保存 + /// + /// + /// + public static void SaveMetaFile(this Metafile file, string emfName) + { + //MetafileHeader metafileHeader = file.GetMetafileHeader(); //这句话可要可不要 + IntPtr h = file.GetHenhmetafile(); + CopyEnhMetaFile(h, emfName); + DeleteEnhMetaFile(h); + } + + /// + /// 矢量图 转换 byte[] + /// + /// + /// + public static byte[]? ToByteArray(this Image image) + { + return ToByteArray((Metafile)image); + } + + // https://www.pinvoke.net/default.aspx/gdi32.getenhmetafile + /// + /// 矢量图 转换 byte[] + /// + /// + /// + public static byte[]? ToByteArray(this Metafile mf) + { + byte[]? arr = null; + IntPtr handle = mf.GetHenhmetafile(); + if (handle != IntPtr.Zero) + { + var size = GetEnhMetaFileBits(handle, 0, null!); + if (size != 0) + { + arr = new byte[size]; + _ = GetEnhMetaFileBits(handle, size, arr); + } + DeleteEnhMetaFile(handle); + } + return arr; + } + + /// + /// byte[] 转换 矢量图 + /// + /// + /// 返回值true删除句柄 + /// + public static void ToMetafile(byte[] data, Func task) + { + if (task == null) + throw new ArgumentNullException(nameof(task)); + + IntPtr hemf = SetEnhMetaFileBits((uint)data.Length, data); + using var mf = new Metafile(hemf, true); + if (task.Invoke(mf)) // 对图像进行操作,就不能进行删除句柄 + DeleteEnhMetaFile(hemf); + } + + +#if false + /// + /// c#获取wmf方式 + /// + /// + /// + public static IntPtr GetMetafile(string wmfFile) + { + using FileStream file = new(wmfFile, FileMode.Open, FileAccess.Read, FileShare.ReadWrite); // FileShare才能进c盘 + var hEMF2 = IntPtr.Zero; + + using Metafile mf = new(file); + var hEMF = mf.GetHenhmetafile(); + if (hEMF != IntPtr.Zero) + hEMF2 = CopyEnhMetaFile(hEMF, null);// 这句: 句柄无效..cad的wmf文件不识别 + //EmfTool.DeleteEnhMetaFile(hEMF);//托管类应该是封装好的 + return hEMF2; + } + + + /* + * // 这是c#写入wmf流程 + * // c#画的wmf格式是可以的...用这样方式生成的就是可以写剪贴板 + * WindowsAPI.GetClientRect(doc.Window.Handle, out IntRect rcClient); + * int width = rcClient.Right - rcClient.Left; + * int height = rcClient.Bottom - rcClient.Top; + * EmfTool.Export(wmf, width, height);//cad的命令wmfin:不能导入c#自绘的 + * + * //c#方法,但是它读取不了cad的wmf + * wmfMeta = EmfTool.GetMetafile(wmf); + */ + + /// + /// 导出为 Emf 或 Wmf 文件 + /// 相关链接 + /// + /// 文件路径 + /// 窗口宽度 + /// 窗口高度 + /// 是否成功 + public static bool Export(string filePath, int width, int height) + { + try + { + using Bitmap bmp = new(width, height); + using Graphics gs = Graphics.FromImage(bmp); + using Metafile mf = new(filePath, gs.GetHdc()); + using Graphics g = Graphics.FromImage(mf); + Draw(g); + g.Save(); + return true; + } + catch { return false; } + } + + + /// + /// 绘制图形 + /// + /// 用于绘图的Graphics对象 + static void Draw(Graphics g) + { + HatchBrush hb = new(HatchStyle.LightUpwardDiagonal, Color.Black, Color.White); + + g.FillEllipse(Brushes.Gray, 10f, 10f, 200, 200); + g.DrawEllipse(new Pen(Color.Black, 1f), 10f, 10f, 200, 200); + + g.FillEllipse(hb, 30f, 95f, 30, 30); + g.DrawEllipse(new Pen(Color.Black, 1f), 30f, 95f, 30, 30); + + g.FillEllipse(hb, 160f, 95f, 30, 30); + g.DrawEllipse(new Pen(Color.Black, 1f), 160f, 95f, 30, 30); + + g.FillEllipse(hb, 95f, 30f, 30, 30); + g.DrawEllipse(new Pen(Color.Black, 1f), 95f, 30f, 30, 30); + + g.FillEllipse(hb, 95f, 160f, 30, 30); + g.DrawEllipse(new Pen(Color.Black, 1f), 95f, 160f, 30, 30); + + g.FillEllipse(Brushes.Blue, 60f, 60f, 100, 100); + g.DrawEllipse(new Pen(Color.Black, 1f), 60f, 60f, 100, 100); + + g.FillEllipse(Brushes.BlanchedAlmond, 95f, 95f, 30, 30); + g.DrawEllipse(new Pen(Color.Black, 1f), 95f, 95f, 30, 30); + + g.DrawRectangle(new Pen(Brushes.Blue, 0.1f), 6, 6, 208, 208); + + g.DrawLine(new Pen(Color.Black, 0.1f), 110f, 110f, 220f, 25f); + g.DrawString("剖面图", new Font("宋体", 9f), Brushes.Green, 220f, 20f); + } +#endif +} \ No newline at end of file diff --git a/src/CAD/IFox.CAD.Shared/Copyclip/TagClipboardInfo.cs b/src/CAD/IFox.CAD.Shared/Copyclip/TagClipboardInfo.cs new file mode 100644 index 0000000..272754a --- /dev/null +++ b/src/CAD/IFox.CAD.Shared/Copyclip/TagClipboardInfo.cs @@ -0,0 +1,659 @@ +namespace IFoxCAD.Cad; + +using System; +using System.Diagnostics; +using System.Text; +using static IFoxCAD.Cad.WindowsAPI; + +public class ClipboardEnv +{ + // 0x01 将r17写死,代表每个cad版本都去找它,实现不隔离cad版本 + public static string CadVer = "AutoCAD.r17"; + // 0x02 当前版本在r17找不到的时候找,避免按需加载插件的时候无法获取剪贴板 + public static string CadCurrentVer = $"AutoCAD.r{Acap.Version.Major}"; +} + +/// +/// ARX剪贴板结构 +/// +[Serializable] +[StructLayout(LayoutKind.Sequential, Pack = 8, CharSet = CharSet.Unicode/*此参数将导致260*2*/)] +public struct TagClipboardInfo : IEquatable +{ + #region 字段,对应arx结构的,不要改动,本结构也不允许再加字段 + [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 260)] + public string szTempFile; // 临时文件夹的dwg文件 + [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 260)] + public string szSourceFile; // 文件名从中做出选择..是不是指定块表记录? + [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 4)] + public string szSignature; + public int nFlags; // kbDragGeometry: 从AutoCAD拖动几何图形 + public Point3D dptInsert; // 插入点的原始世界坐标' + public IntRect rectGDI; // GDI coord 选择集的边界矩形 + public IntPtr mpView; // 用于验证这个对象是在这个视图中创建的 (HWND*) + public int dwThreadId; // AutoCAD thread 创建数据对象 + public int nLen; // 下一段的长度的数据,如果有的话,从chData + public int nType; // 类型的数据,如果有(eExpandedClipDataTypes) + [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 1)] + public string chData; // 数据的开始,如果有 + #endregion + + #region 属性,可以改动 + public string File => szTempFile; + public Point3d Point => dptInsert; + +#pragma warning disable CA2211 // 非常量字段应当不可见 + public static IntPtr AcadDwgview + = IntPtr.Zero; + //= AcedGetAcadDwgview(); // c#需要收集这个函数,我先不写,免得中间版本挂了 + + public static int MainWindowThreadId = + (int)WindowsAPI.GetWindowThreadProcessId(Acap.MainWindow.Handle, out uint processId); +#pragma warning restore CA2211 // 非常量字段应当不可见 + #endregion + + #region 构造 + /// + /// cad剪贴板 + /// + /// 临时dwg的保存路径 + /// 粘贴点 + public TagClipboardInfo(string tmpFile, Point3d insert) + { + szTempFile = tmpFile; + szSourceFile = string.Empty; + szSignature = "R15"; //恒定是这个 + nFlags = 0; + dptInsert = insert; + rectGDI = IntRect.Zero; + nLen = 0; + nType = 0; + chData = string.Empty; + + // mpView threadid 可能是用来删除的,用于剪贴板回调清理资源时候判断信息 + mpView = AcadDwgview; + dwThreadId = MainWindowThreadId; + } + #endregion + + #region 方法 + public override string ToString() + { + var sb = new StringBuilder(); + sb.AppendLine($"szTempFile:{szTempFile}"); + sb.AppendLine($"szSourceFile:{szSourceFile}"); + sb.AppendLine($"szSignature:{szSignature}"); + sb.AppendLine($"nFlags:{nFlags}"); + sb.AppendLine($"dptInsert:{dptInsert}"); + sb.AppendLine($"rectGDI:{rectGDI}"); + sb.AppendLine($"mpView:{mpView}"); + sb.AppendLine($"dwThreadId:{dwThreadId}"); + sb.AppendLine($"nLen:{nLen}"); + sb.AppendLine($"nType:{nType}"); + sb.AppendLine($"chData:{chData}"); + return sb.ToString(); + } + #endregion + + #region 测试大小 + void GetSize() + { + var v_1 = Marshal.OffsetOf(typeof(TagClipboardInfo), nameof(TagClipboardInfo.szTempFile)).ToInt32(); + var v_2 = Marshal.OffsetOf(typeof(TagClipboardInfo), nameof(TagClipboardInfo.szSourceFile)).ToInt32(); + var v_3 = Marshal.OffsetOf(typeof(TagClipboardInfo), nameof(TagClipboardInfo.szSignature)).ToInt32(); + var v_4 = Marshal.OffsetOf(typeof(TagClipboardInfo), nameof(TagClipboardInfo.nFlags)).ToInt32(); + var v_5 = Marshal.OffsetOf(typeof(TagClipboardInfo), nameof(TagClipboardInfo.dptInsert)).ToInt32(); + var v_6 = Marshal.OffsetOf(typeof(TagClipboardInfo), nameof(TagClipboardInfo.rectGDI)).ToInt32(); + var v_7 = Marshal.OffsetOf(typeof(TagClipboardInfo), nameof(TagClipboardInfo.mpView)).ToInt32(); + var v_8 = Marshal.OffsetOf(typeof(TagClipboardInfo), nameof(TagClipboardInfo.dwThreadId)).ToInt32(); + var v_9 = Marshal.OffsetOf(typeof(TagClipboardInfo), nameof(TagClipboardInfo.nLen)).ToInt32(); + var v_10 = Marshal.OffsetOf(typeof(TagClipboardInfo), nameof(TagClipboardInfo.nType)).ToInt32(); + var v_11 = Marshal.OffsetOf(typeof(TagClipboardInfo), nameof(TagClipboardInfo.chData)).ToInt32(); + var v_12 = Marshal.SizeOf(typeof(TagClipboardInfo)); //1120 + + var v_a = Marshal.SizeOf(typeof(Point3D));//24 + var v_b = Marshal.SizeOf(typeof(IntRect));//16 + } + #endregion + + #region 视口指针 + /* + [CommandMethod(nameof(Test_AcedGetAcadDwgview))] + public void testAcedGetAcadDwgview() + { + var dm = Acap.DocumentManager; + var doc = dm.MdiActiveDocument; + var ed = doc.Editor; + + var a = AcedGetAcadDwgview().ToString(); //自动执行的时候就存在了 + var b = ed.CurrentViewportObjectId.ToString(); + Debugx.Printl("a == b:" + a == b);//不对 + + var tab = ed.GetCurrentView(); + var c = tab.ObjectId.ToString(); + Debugx.Printl("a == c:" + a == c);//不对 + } + */ + + /// + /// 获取视口指针 + /// +#if NET35 + [DllImport("acad.exe", EntryPoint = "?acedGetAcadDwgView@@YAPAVCView@@XZ")] //acad08 +#else + [DllImport("acad.exe", EntryPoint = "?acedGetAcadDwgView@@YAPEAVCView@@XZ")]//acad21 +#endif + static extern IntPtr AcedGetAcadDwgview(); + #endregion + + #region 重载运算符_比较 + public bool Equals(TagClipboardInfo other) + { + return + szTempFile == other.szTempFile && + szSourceFile == other.szSourceFile && + szSignature == other.szSignature && + nFlags == other.nFlags && + dptInsert == other.dptInsert && + rectGDI == other.rectGDI && + mpView == other.mpView && + dwThreadId == other.dwThreadId && + nLen == other.nLen && + nType == other.nType && + chData == other.chData; + } + public static bool operator !=(TagClipboardInfo a, TagClipboardInfo b) + { + return !(a == b); + } + public static bool operator ==(TagClipboardInfo a, TagClipboardInfo b) + { + return a.Equals(b); + } + public override bool Equals(object obj) + { + return obj is TagClipboardInfo info && Equals(info); + } + public override int GetHashCode() + { + return + szTempFile.GetHashCode() ^ + szSourceFile.GetHashCode() ^ + szSignature.GetHashCode() ^ + nFlags ^ + dptInsert.GetHashCode() ^ + rectGDI.GetHashCode() ^ + mpView.GetHashCode() ^ + dwThreadId ^ + nLen ^ + nType ^ + chData.GetHashCode(); + } + + public IntPtr CloneToPtr() + { + var lParam = Marshal.AllocHGlobal(Marshal.SizeOf(this)); + if (lParam != IntPtr.Zero) + Marshal.StructureToPtr(this, lParam, true); + return lParam; + } + #endregion +} + + +/* + * OLE 剪贴板说明 https://blog.csdn.net/chinabinlang/article/details/9815495 + * 写入时候注意: + * 0x01 c#自带的是com剪贴板 + * 最好不要使用,它不能在已经打开的剪贴板中使用, + * 也无法写入多个cf对象,也就是复制bitmap的时候会覆盖cad图元 + * Clipboard.SetImage(bitmap); + * 0x02 + * 剪贴板写入各种类型 https://blog.csdn.net/glt3953/article/details/8808262 + * + */ + +public partial class ClipTool +{ + /// + /// 侦听剪贴板 + /// + /// + /// + [DllImport("user32.dll", SetLastError = true)] + public static extern bool AddClipboardFormatListener(IntPtr hWnd); + /// + /// 移除侦听剪贴板 + /// + /// + /// + [DllImport("user32.dll", SetLastError = true)] + public static extern bool RemoveClipboardFormatListener(IntPtr hWnd); + /// + /// 将CWnd加入一个窗口链 + /// 每当剪贴板的内容发生变化时,就会通知这些窗口 + /// + /// 句柄 + /// 返回剪贴板观察器链中下一个窗口的句柄 + [DllImport("User32.dll")] + public static extern int SetClipboardViewer(IntPtr hWndNewViewer); + /// + /// 从剪贴板链中移出的窗口句柄 + /// + /// 从剪贴板链中移出的窗口句柄 + /// hWndRemove的下一个在剪贴板链中的窗口句柄 + /// 如果成功,非零;否则为0。 + [DllImport("User32.dll", CharSet = CharSet.Auto)] + public static extern bool ChangeClipboardChain(IntPtr hWndRemove, IntPtr hWndNewNext); + + + /// + /// 开启剪贴板
    + /// 如果另一个窗口已经打开剪贴板,函数会失败.每次成功调用后都应有调用. + ///
    + /// + /// + [DllImport("user32.dll", SetLastError = true)] + static extern bool OpenClipboard(IntPtr hWndNewOwner); + /// + /// 关闭剪贴板 + /// + /// + [DllImport("user32.dll", SetLastError = true)] + public static extern bool CloseClipboard(); + /// + /// 根据数据格式获取剪贴板 + /// + /// 数据格式名称 + /// + [DllImport("user32.dll", SetLastError = true)] + public static extern uint RegisterClipboardFormat(string lpszFormat); + /// + /// 获取剪贴板 + /// + /// 通常为但是cad有自己的位码 + /// + [DllImport("user32.dll", SetLastError = true)] + public static extern IntPtr GetClipboardData(uint uFormat); + /// + /// 设置剪贴板 + /// + /// 通常为但是cad有自己的位码 + /// 指定具有指定格式的数据的句柄,
    + /// 该参数为空则为延迟提交:
    + /// 有其他程序对剪切板中的数据进行请求时,该程序才会将指定格式的数据写入到剪切板中. + /// + /// + [DllImport("user32.dll", SetLastError = true)] + public static extern IntPtr SetClipboardData(uint uFormat, IntPtr hMem); + /// + /// 清空剪切板并释放剪切板内数据的句柄 + /// + /// + [DllImport("user32.dll", SetLastError = true)] + public static extern bool EmptyClipboard(); + /// + /// 枚举剪贴板内数据类型 + /// + /// + /// + [DllImport("user32.dll", SetLastError = true)] + public static extern uint EnumClipboardFormats(uint format); + + + /// + /// 打开剪贴板
    + /// 写入之前必须清空,
    + /// 否则将导致发送 WM_DESTROYCLIPBOARD 消息到上一次剪贴板拥有者释放资源
    + /// 所以写入的时候必须一次性写入多个cf
    + ///
    + /// 接收返回的栈空间指针用于释放 + /// true写入,false读取 + /// + /// + [System.Diagnostics.DebuggerStepThrough] + public static bool OpenClipboardTask(bool isWrite, Action action) + { + if (action == null) + throw new ArgumentNullException(nameof(action)); + + bool openFlag = false; + try + { + openFlag = OpenClipboard(IntPtr.Zero); + if (!openFlag) + return false; + if (isWrite) + EmptyClipboard(); + action.Invoke(); + } + catch (Exception e) + { + Debugger.Break(); + Debugx.Printl(e.Message); + } + finally + { + if (openFlag) + CloseClipboard(); + } + return openFlag; + } + + /// + /// 获取剪贴板 + /// + /// 剪贴板的索引名 + /// 返回的结构 + public static bool GetClipboard(string clipKey, out T? tag) + { + bool locked = false; + T? result = default; + + ClipTool.OpenClipboardTask(false, () => { + // 读取剪贴板的指定数据 + var clipKeyFormat = RegisterClipboardFormat(clipKey);//ClipboardEnv.CadVer + var clipTypeData = GetClipboardData(clipKeyFormat); + + // 剪贴板的数据拷贝进去结构体中,会依照数据长度进行拷贝 + locked = WindowsAPI.GlobalLockTask(clipTypeData, ptr => { + // 非托管内存块->托管对象 + result = (T)Marshal.PtrToStructure(ptr, typeof(T)); + }); + }); + + tag = result; + return locked; + } +} + +#if true2 +// 无法备份emf内容 +// https://blog.csdn.net/vencon_s/article/details/46345083 +public static class ClipEx +{ + /// + /// 剪贴板数据保存目标数据列表 + /// + static readonly List _bytes = new(); + /// + /// 剪贴板数据类型列表 + /// + static readonly List _formats = new(); + + /// + /// 遍历剪贴板保存内容 + /// + /// true成功,false失败 + public static bool SaveClip() + { + bool result = ClipTool.OpenClipboardTask(false, free => { + _bytes.Clear(); + _formats.Clear(); + + uint cf = 0; + while (true) + { + cf = ClipTool.EnumClipboardFormats(cf);// 枚举剪贴板所有数据类型 + if (cf == 0) + break; + + _formats.Add(cf); + IntPtr clipTypeData = ClipTool.GetClipboardData(cf); + var locked = WindowsAPI.GlobalLockTask(clipTypeData, prt => { + uint size = WindowsAPI.GlobalSize(clipTypeData); + if (size > 0) + { + var buffer = new byte[size]; + Marshal.Copy(prt, buffer, 0, buffer.Length);// 将剪贴板数据保存到自定义字节数组 + _bytes.Add(buffer); + } + }); + } + }); + if (result) + result = _formats.Count > 0; + return result; + } + + /// + /// 恢复保存的数据 + /// + /// true成功,false失败 + public static bool RestoreClip() + { + if (_formats.Count <= 0) + return false; + + bool result = ClipTool.OpenClipboardTask(true, free => { + for (int i = 0; i < _formats.Count; i++) + { + int size = _bytes[i].Length; + IntPtr structPtr = Marshal.AllocHGlobal(size); + if (size > 0) + { + Marshal.Copy(_bytes[i], 0, structPtr, size); + ClipTool.SetClipboardData(_formats[i], structPtr); + } + } + }); + + if (result) + result = _formats.Count > 0; + return result; + } +} +#endif + + + +/// +/// 剪贴板的CF,也就是它的key +/// +public enum ClipboardFormat : uint +{ + /// + /// Text format. Each line ends with a carriage return/linefeed (CR-LF) combination. A null character signals + /// the end of the data. Use this format for ANSI text. + /// + CF_TEXT = 1, + + /// + /// A handle to a bitmap (HBITMAP). + /// + CF_BITMAP = 2, + + /// + /// Handle to a metafile picture format as defined by the METAFILEPICT structure. When passing a + /// CF_METAFILEPICT handle by means of DDE, the application responsible for deleting hMem should + /// also free the metafile referred to by the CF_METAFILEPICT handle. + /// + CF_METAFILEPICT = 3, + + /// + /// Microsoft Symbolic Link (SYLK) format. + /// + CF_SYLK = 4, + + /// + /// Software Arts' Data Interchange Format. + /// + CF_DIF = 5, + + /// + /// Tagged-image file format. + /// + CF_TIFF = 6, + + /// + /// Text format containing characters in the OEM character set. Each line ends with a carriage return/linefeed + /// (CR-LF) combination. A null character signals the end of the data. + /// + CF_OEMTEXT = 7, + + /// + /// A memory object containing a BITMAPINFO structure followed by the bitmap bits. + /// + CF_DIB = 8, + + /// + /// Handle to a color palette. Whenever an application places data in the clipboard that depends on or assumes + /// a color palette, it should place the palette on the clipboard as well. If the clipboard contains data in + /// the (logical color palette) format, the application should use the + /// SelectPalette and RealizePalette functions to realize (compare) any other data in the + /// clipboard against that logical palette. When displaying clipboard data, the clipboard always uses as its + /// current palette any object on the clipboard that is in the CF_PALETTE format. + /// + CF_PALETTE = 9, + + /// + /// Data for the pen extensions to the Microsoft Windows for Pen Computing. + /// + CF_PENDATA = 10, + + /// + /// Represents audio data more complex than can be represented in a CF_WAVE standard wave format. + /// + CF_RIFF = 11, + + /// + /// Represents audio data in one of the standard wave formats, such as 11 kHz or 22 kHz PCM. + /// + CF_WAVE = 12, + + /// + /// Unicode text format. Each line ends with a carriage return/linefeed (CR-LF) combination. A null character + /// signals the end of the data. + /// + CF_UNICODETEXT = 13, + + /// + /// A handle to an enhanced metafile (HENHMETAFILE). + /// + CF_ENHMETAFILE = 14, + + /// + /// A handle to type HDROP that identifies a list of files. An application can retrieve information + /// about the files by passing the handle to the DragQueryFile function. + /// + CF_HDROP = 15, + + /// + /// The data is a handle to the locale identifier associated with text in the clipboard. When you close the + /// clipboard, if it contains CF_TEXT data but no CF_LOCALE data, the system automatically sets + /// the CF_LOCALE format to the current input language. You can use the CF_LOCALE format to + /// associate a different locale with the clipboard text. + /// An application that pastes text from the clipboard can retrieve this format to determine which character + /// set was used to generate the text. + /// Note that the clipboard does not support plain text in multiple character sets. To achieve this, use a + /// formatted text data type such as RTF instead. + /// The system uses the code page associated with CF_LOCALE to implicitly convert from + /// to . Therefore, the correct code page table is used for + /// the conversion. + /// + CF_LOCALE = 16, + + /// + /// A memory object containing a BITMAPV5HEADER structure followed by the bitmap color space + /// information and the bitmap bits. + /// + CF_DIBV5 = 17, + + /// + /// Owner-display format. The clipboard owner must display and update the clipboard viewer window, and receive + /// the , , + /// , , and + /// messages. The hMem parameter must be null. + /// + CF_OWNERDISPLAY = 0x0080, + + /// + /// Text display format associated with a private format. The hMem parameter must be a handle to data + /// that can be displayed in text format in lieu of the privately formatted data. + /// + CF_DSPTEXT = 0x0081, + + /// + /// Bitmap display format associated with a private format. The hMem parameter must be a handle to + /// data that can be displayed in bitmap format in lieu of the privately formatted data. + /// + CF_DSPBITMAP = 0x0082, + + /// + /// Metafile-picture display format associated with a private format. The hMem parameter must be a + /// handle to data that can be displayed in metafile-picture format in lieu of the privately formatted data. + /// + CF_DSPMETAFILEPICT = 0x0083, + + /// + /// Enhanced metafile display format associated with a private format. The hMem parameter must be a + /// handle to data that can be displayed in enhanced metafile format in lieu of the privately formatted data. + /// + CF_DSPENHMETAFILE = 0x008E, + + /// + /// Start of a range of integer values for application-defined GDI object clipboard formats. The end of the + /// range is . Handles associated with clipboard formats in this range are not + /// automatically deleted using the GlobalFree function when the clipboard is emptied. Also, when using + /// values in this range, the hMem parameter is not a handle to a GDI object, but is a handle allocated + /// by the GlobalAlloc function with the GMEM_MOVEABLE flag. + /// + CF_GDIOBJFIRST = 0x0300, + + /// + /// See . + /// + CF_GDIOBJLAST = 0x03FF, + + /// + /// Start of a range of integer values for private clipboard formats. The range ends with + /// . Handles associated with private clipboard formats are not freed + /// automatically, the clipboard owner must free such handles, typically in response to the + /// message. + /// + CF_PRIVATEFIRST = 0x0200, + + /// + /// See . + /// + CF_PRIVATELAST = 0x02FF, +} + +#if true2 +// arx剪贴板头文件的枚举 +enum eClipInfoFlags +{ + kbDragGeometry = 0x01, +}; + +enum eXrefType +{ + kXrefTypeAttach = 1, + kXrefTypeOverlay = 2 +}; + +enum eExpandedClipDataTypes +{ + kDcPlotStyles = 1, + kDcXrefs = 2, + kDcLayouts = 3, + kDcBlocks = 4, + kDcLayers = 5, + kDcDrawings = 6, + kDcLinetypes = 7, + kDcTextStyles = 8, + kDcDimStyles = 9, + kDcBlocksWithAttdef = 10, + //#ifdef ADCHATCH + kDcHatches = 11, + //#endif + kTpXrefs = 12, + kTpImages = 13, + kTpTable = 14, + kDcTableStyles = 15, + kDcMultileaderStyles = 16, + kDcVisualStyles = 17, + kDcSectionViewStyles = 18, + kDcDetailViewStyles = 19, +}; +#endif \ No newline at end of file diff --git a/src/CAD/IFox.CAD.Shared/ExtensionMethod/BulgeVertexWidth.cs b/src/CAD/IFox.CAD.Shared/ExtensionMethod/BulgeVertexWidth.cs new file mode 100644 index 0000000..1f9e08d --- /dev/null +++ b/src/CAD/IFox.CAD.Shared/ExtensionMethod/BulgeVertexWidth.cs @@ -0,0 +1,85 @@ +namespace IFoxCAD.Cad; + +/// +/// 多段线的顶点,凸度,头宽,尾宽 +/// +[Serializable] +public class BulgeVertexWidth +{ + /// + /// 顶点X + /// + public double X; + /// + /// 顶点Y + /// + public double Y; + /// + /// 凸度 + /// + public double Bulge; + /// + /// 头宽 + /// + public double StartWidth; + /// + /// 尾宽 + /// + public double EndWidth; + + public Point2d Vertex => new(X, Y); + + public BulgeVertexWidth() { } + + /// + /// 多段线的顶点,凸度,头宽,尾宽 + /// + public BulgeVertexWidth(double vertex_X, double vertex_Y, + double bulge = 0, + double startWidth = 0, + double endWidth = 0) + { + X = vertex_X; + Y = vertex_Y; + Bulge = bulge; + StartWidth = startWidth; + EndWidth = endWidth; + } + + /// + /// 多段线的顶点,凸度,头宽,尾宽 + /// + public BulgeVertexWidth(Point2d vertex, + double bulge = 0, + double startWidth = 0, + double endWidth = 0) + : this(vertex.X, vertex.Y, bulge, startWidth, endWidth) + { } + + /// + /// 多段线的顶点,凸度,头宽,尾宽 + /// + public BulgeVertexWidth(BulgeVertex bv) + : this(bv.Vertex.X, bv.Vertex.Y, bv.Bulge) + { } + + /// + /// 多段线的顶点,凸度,头宽,尾宽 + /// + /// 多段线 + /// 子段编号 + public BulgeVertexWidth(Polyline pl, int index) + { + var pt = pl.GetPoint2dAt(index);// 这里可以3d + X = pt.X; + Y = pt.Y; + Bulge = pl.GetBulgeAt(index); + StartWidth = pl.GetStartWidthAt(index); + EndWidth = pl.GetEndWidthAt(index); + } + + public BulgeVertex ToBulgeVertex() + { + return new BulgeVertex(Vertex, Bulge); + } +} diff --git a/src/CAD/IFox.CAD.Shared/ExtensionMethod/CollectionEx.cs b/src/CAD/IFox.CAD.Shared/ExtensionMethod/CollectionEx.cs new file mode 100644 index 0000000..acd120a --- /dev/null +++ b/src/CAD/IFox.CAD.Shared/ExtensionMethod/CollectionEx.cs @@ -0,0 +1,289 @@ +using System.ComponentModel; +using System.Xml.Linq; +using static System.Windows.Forms.AxHost; + +namespace IFoxCAD.Cad; + +/// +/// 集合扩展类 +/// +public static class CollectionEx +{ + /// + /// 对象id迭代器转换为集合 + /// + /// 对象id的迭代器 + /// 对象id集合,记得释放 + [System.Diagnostics.DebuggerStepThrough] + public static ObjectIdCollection ToCollection(this IEnumerable ids) + { + return new ObjectIdCollection(ids.ToArray()); + } + + /// + /// 实体迭代器转换为集合 + /// + /// 对象类型 + /// 实体对象的迭代器 + /// 实体集合,记得释放 + [System.Diagnostics.DebuggerStepThrough] + public static DBObjectCollection ToCollection(this IEnumerable objs) where T : DBObject + { + DBObjectCollection objCol = new(); + foreach (T obj in objs) + objCol.Add(obj); + return objCol; + } + + /// + /// double 数值迭代器转换为 double 数值集合 + /// + /// double 数值迭代器 + /// 数值集合,它没有Dispose + [System.Diagnostics.DebuggerStepThrough] + public static DoubleCollection ToCollection(this IEnumerable doubles) + { + return new DoubleCollection(doubles.ToArray()); + } + + /// + /// 二维点迭代器转换为二维点集合 + /// + /// 二维点迭代器 + /// 二维点集合,!acad记得释放 + [System.Diagnostics.DebuggerStepThrough] + public static Point2dCollection ToCollection(this IEnumerable pts) + { + return new Point2dCollection(pts.ToArray()); + } + + /// + /// 三维点迭代器转换为三维点集合 + /// + /// 三维点迭代器 + /// 三维点集合,记得释放 + [System.Diagnostics.DebuggerStepThrough] + public static Point3dCollection ToCollection(this IEnumerable pts) + { + return new Point3dCollection(pts.ToArray()); + } + + /// + /// 对象id集合转换为对象id列表 + /// + /// 对象id集合 + /// 对象id列表 + [System.Diagnostics.DebuggerStepThrough] + public static List ToList(this ObjectIdCollection ids) + { + return ids.Cast().ToList(); + } + + + /// + /// 遍历集合,执行委托 + /// + /// 集合值的类型 + /// 集合 + /// 委托 + [System.Diagnostics.DebuggerStepThrough] //[DebuggerHidden] 两个特性差不多 + public static void ForEach(this IEnumerable source, Action action) + { + // 这里不要嵌套调用ForEach委托, + // 因为这样可以在调用函数上断点直接跑Action内,不会进入此处(除了cad之外); + // 而cad很奇怪,只能用预处理方式避免 + // 嵌套调用ForEach委托: + // source.ForEach((a, _, _) => { + // action.Invoke(a); + // }); + + foreach (var element in source) + action.Invoke(element); + } + + /// + /// 遍历集合,执行委托(允许循环中断) + /// + /// 集合值的类型 + /// 集合 + /// 委托 + [System.Diagnostics.DebuggerStepThrough] + public static void ForEach(this IEnumerable source, Action action) + { + // 这里不要嵌套调用ForEach委托, + // 因为这样可以在调用函数上断点直接跑Action内,不会进入此处(除了cad之外); + // 而cad很奇怪,只能用预处理方式避免 + // 嵌套调用ForEach委托: + // source.ForEach((a, b, _) => { + // action.Invoke(a, b); + // }); + + LoopState state = new();/*这种方式比Action改Func更友好*/ + foreach (var element in source) + { + action.Invoke(element, state); + if (!state.IsRun) + break; + } + } + + /// + /// 遍历集合,执行委托(允许循环中断,输出索引值) + /// + /// 集合值的类型 + /// 集合 + /// 委托 + [System.Diagnostics.DebuggerStepThrough] + public static void ForEach(this IEnumerable source, Action action) + { + int i = 0; + LoopState state = new();/*这种方式比Action改Func更友好*/ + foreach (var element in source) + { + action.Invoke(element, state, i); + if (!state.IsRun) + break; + i++; + } + } + + + #region 关键字集合 + public enum KeywordName + { + GlobalName, + LocalName, + DisplayName, + } + + /// + /// 含有关键字 + /// + /// 关键字集合 + /// 关键字 + /// 关键字容器字段名 + /// true含有 + [System.Diagnostics.DebuggerStepThrough] + public static bool Contains(this KeywordCollection collection, string name, + KeywordName keywordName = KeywordName.GlobalName) + { + bool contains = false; + switch (keywordName) + { + case KeywordName.GlobalName: + for (int i = 0; i < collection.Count; i++) + { +#if gcad + var item = collection.get_Item(i); +#else + var item = collection[i]; +#endif + if (item.GlobalName == name) + { + contains = true; + break; + } + } + break; + case KeywordName.LocalName: + for (int i = 0; i < collection.Count; i++) + { +#if gcad + var item = collection.get_Item(i); +#else + var item = collection[i]; +#endif + if (item.LocalName == name) + { + contains = true; + break; + } + } + break; + case KeywordName.DisplayName: + for (int i = 0; i < collection.Count; i++) + { +#if gcad + var item = collection.get_Item(i); +#else + var item = collection[i]; +#endif + if (item.DisplayName == name) + { + contains = true; + break; + } + } + break; + } + return contains; + } + + /// + /// 获取词典, + /// KeywordCollection是允许重复关键字的,没有哈希索引,在多次判断时候会遍历多次O(n),所以生成一个词典进行O(1) + /// + /// + /// + [System.Diagnostics.DebuggerStepThrough] + public static Dictionary GetDict(this KeywordCollection collection) + { + Dictionary map = new(); + for (int i = 0; i < collection.Count; i++) + { +#if gcad + var item = collection.get_Item(i); +#else + var item = collection[i]; +#endif + map.Add(item.GlobalName, item.DisplayName); + } + return map; + } + #endregion + + + #region IdMapping + /// + /// 旧块名 + /// + /// + /// + [System.Diagnostics.DebuggerStepThrough] + public static List GetKeys(this IdMapping idmap) + { + List ids = new(); + foreach (IdPair item in idmap) + ids.Add(item.Key); + return ids; + } + + /// + /// 新块名 + /// + /// + /// + [System.Diagnostics.DebuggerStepThrough] + public static List GetValues(this IdMapping idmap) + { + List ids = new(); + foreach (IdPair item in idmap) + ids.Add(item.Value); + return ids; + } + + /// + /// 转换为词典 + /// + /// + /// + [System.Diagnostics.DebuggerStepThrough] + public static Dictionary ToDictionary(this IdMapping mapping) + { + var keyValuePairs = new Dictionary(); + foreach (IdPair item in mapping) + keyValuePairs.Add(item.Key, item.Value); + return keyValuePairs; + } + #endregion +} \ No newline at end of file diff --git a/src/CAD/IFox.CAD.Shared/ExtensionMethod/Curve2dEx.cs b/src/CAD/IFox.CAD.Shared/ExtensionMethod/Curve2dEx.cs new file mode 100644 index 0000000..07db654 --- /dev/null +++ b/src/CAD/IFox.CAD.Shared/ExtensionMethod/Curve2dEx.cs @@ -0,0 +1,296 @@ +namespace IFoxCAD.Cad; + +/// +/// 二维解析类曲线转换为二维实体曲线扩展类 +/// +public static class Curve2dEx +{ + internal static readonly Plane _planeCache = new(); + + #region Curve2d + + /// + /// 按矩阵转换Ge2d曲线为Db曲线 + /// + /// Ge2d曲线 + /// 曲线转换矩阵 + /// Db曲线 + public static Curve? ToCurve(this Curve2d curve, Matrix3d mat) + { + return curve switch + { + LineSegment2d li => ToCurve(li, mat), + NurbCurve2d nu => ToCurve(nu, mat), + EllipticalArc2d el => ToCurve(el, mat), + CircularArc2d ci => ToCurve(ci, mat), + PolylineCurve2d po => ToCurve(po, mat), + Line2d l2 => ToCurve(l2, mat), + CompositeCurve2d co => ToCurve(co, mat), + _ => null + }; + } + + #endregion Curve2d + + #region CircularArc2d + + /// + /// 判断点是否位于圆内及圆上 + /// + /// 二维解析类圆弧对象 + /// 二维点 + /// 位于圆内及圆上返回 ,反之返回 + public static bool IsIn(this CircularArc2d ca2d, Point2d pnt) + { + return ca2d.IsOn(pnt) || ca2d.IsInside(pnt); + } + + /// + /// 将二维解析类圆弧转换为实体圆或者圆弧,然后进行矩阵变换 + /// + /// 二维解析类圆弧对象 + /// 变换矩阵 + /// 实体圆或者圆弧 + public static Curve ToCurve(this CircularArc2d ca2d, Matrix3d mat) + { + Curve c = ToCurve(ca2d); + c.TransformBy(mat); + return c; + } + + /// + /// 将二维解析类圆弧转换为实体圆或者圆弧 + /// + /// 二维解析类圆弧对象 + /// 实体圆或者圆弧 + public static Curve ToCurve(this CircularArc2d ca2d) + { + if (ca2d.IsClosed()) + return ToCircle(ca2d); + else + return ToArc(ca2d); + } + + /// + /// 将二维解析类圆弧转换为实体圆 + /// + /// 二维解析类圆弧对象 + /// 实体圆 + public static Circle ToCircle(this CircularArc2d c2d) + { + return + new Circle( + new Point3d(_planeCache, c2d.Center), + Vector3d.ZAxis, + c2d.Radius); + } + + /// + /// 将二维解析类圆弧转换为实体圆弧 + /// + /// 二维解析类圆弧对象 + /// 圆弧 + public static Arc ToArc(this CircularArc2d a2d) + { + double startangle, endangle; + double refangle = a2d.ReferenceVector.Angle; + + if (a2d.IsClockWise) + { + startangle = -a2d.EndAngle - refangle; + endangle = -a2d.StartAngle - refangle; + } + else + { + startangle = a2d.StartAngle + refangle; + endangle = a2d.EndAngle + refangle; + } + + return + new Arc( + new Point3d(_planeCache, a2d.Center), + Vector3d.ZAxis, + a2d.Radius, + startangle, + endangle); + } + + #endregion CircularArc2d + + #region EllipticalArc2d + + // 椭圆弧 + /// + /// 将二维解析类椭圆弧转换为实体椭圆弧,然后进行矩阵变换 + /// + /// 二维解析类椭圆弧对象 + /// 变换矩阵 + /// 实体椭圆弧 + public static Ellipse ToCurve(this EllipticalArc2d ea2d, Matrix3d mat) + { + Ellipse e = ToCurve(ea2d); + e.TransformBy(mat); + return e; + } + + /// + /// 将二维解析类椭圆弧转换为实体椭圆弧 + /// + /// 二维解析类椭圆弧对象 + /// 实体椭圆弧 + public static Ellipse ToCurve(this EllipticalArc2d ea2d) + { + Ellipse ell = new( + new Point3d(_planeCache, ea2d.Center), + Vector3d.ZAxis, + new Vector3d(_planeCache, ea2d.MajorAxis) * ea2d.MajorRadius, + ea2d.MinorRadius / ea2d.MajorRadius, + 0, + Math.PI * 2); + if (!ea2d.IsClosed()) + { + if (ea2d.IsClockWise) + { + ell.StartAngle = -ell.GetAngleAtParameter(ea2d.EndAngle); + ell.EndAngle = -ell.GetAngleAtParameter(ea2d.StartAngle); + } + else + { + ell.StartAngle = ell.GetAngleAtParameter(ea2d.StartAngle); + ell.EndAngle = ell.GetAngleAtParameter(ea2d.EndAngle); + } + } + return ell; + } + + #endregion EllipticalArc2d + + #region Line2d + + /// + /// 将二维解析类直线转换为实体类构造线 + /// + /// 二维解析类直线 + /// 实体类构造线 + public static Xline ToCurve(this Line2d line2d) + { + return new Xline + { + BasePoint = new Point3d(_planeCache, line2d.PointOnLine), + SecondPoint = new Point3d(_planeCache, line2d.PointOnLine + line2d.Direction) + }; + } + + /// + /// 将二维解析类直线转换为实体类构造线,然后进行矩阵变换 + /// + /// 二维解析类直线 + /// 变换矩阵 + /// 实体类构造线 + public static Xline ToCurve(this Line2d line2d, Matrix3d mat) + { + Xline xl = ToCurve(line2d); + xl.TransformBy(mat); + return xl; + } + + /// + /// 将二维解析类构造线转换为二维解析类线段 + /// + /// 二维解析类构造线 + /// 起点参数 + /// 终点参数 + /// 二维解析类线段 + public static LineSegment2d ToLineSegment2d(this Line2d line2d, double fromParameter, double toParameter) + { + return + new LineSegment2d + ( + line2d.EvaluatePoint(fromParameter), + line2d.EvaluatePoint(toParameter) + ); + } + + #endregion Line2d + + #region LineSegment2d + + /// + /// 将二维解析类线段转换为实体类直线,并进行矩阵变换 + /// + /// 二维解析类线段 + /// 变换矩阵 + /// 实体类直线 + public static Line ToCurve(this LineSegment2d ls2d, Matrix3d mat) + { + Line l = ToCurve(ls2d); + l.TransformBy(mat); + return l; + } + + /// + /// 将二维解析类线段转换为实体类直线 + /// + /// 二维解析类线段 + /// 实体类直线 + public static Line ToCurve(this LineSegment2d ls2d) + { + return + new Line( + new Point3d(_planeCache, ls2d.StartPoint), + new Point3d(_planeCache, ls2d.EndPoint)); + } + + #endregion LineSegment2d + + #region NurbCurve2d + + /// + /// 将二维解析类BURB曲线转换为实体类样条曲线,并进行矩阵变换 + /// + /// 二维解析类BURB曲线 + /// 变换矩阵 + /// 实体类样条曲线 + public static Spline ToCurve(this NurbCurve2d nc2d, Matrix3d mat) + { + Spline spl = ToCurve(nc2d); + spl.TransformBy(mat); + return spl; + } + + /// + /// 将二维解析类BURB曲线转换为实体类样条曲线 + /// + /// 二维解析类BURB曲线 + /// 实体类样条曲线 + public static Spline ToCurve(this NurbCurve2d nc2d) + { + using Point3dCollection ctlpnts = new(); + for (int i = 0; i < nc2d.NumControlPoints; i++) + ctlpnts.Add(new Point3d(_planeCache, nc2d.GetControlPointAt(i))); + + DoubleCollection knots = new(); + for (int i = 0; i < nc2d.Knots.Count; i++) + knots.Add(nc2d.Knots[i]); + + DoubleCollection weights = new(); + for (int i = 0; i < nc2d.NumWeights; i++) + weights.Add(nc2d.GetWeightAt(i)); + + NurbCurve2dData ncdata = nc2d.DefinitionData; + + return + new Spline( + ncdata.Degree, + ncdata.Rational, + nc2d.IsClosed(), + ncdata.Periodic, + ctlpnts, + knots, + weights, + 0, + nc2d.Knots.Tolerance); + } + + #endregion NurbCurve2d +} \ No newline at end of file diff --git a/src/CAD/IFox.CAD.Shared/ExtensionMethod/Curve3dEx.cs b/src/CAD/IFox.CAD.Shared/ExtensionMethod/Curve3dEx.cs new file mode 100644 index 0000000..c12275b --- /dev/null +++ b/src/CAD/IFox.CAD.Shared/ExtensionMethod/Curve3dEx.cs @@ -0,0 +1,556 @@ +using System.Runtime.CompilerServices; + +namespace IFoxCAD.Cad; + +/// +/// 三维解析类曲线转换为三维实体曲线扩展类 +/// +public static class Curve3dEx +{ + /// + /// 判断两个浮点数是否相等 + /// + /// 容差 + /// 第一个数 + /// 第二个数 + /// 两个数的差值的绝对值小于容差返回 ,反之返回 + [MethodImpl] + public static bool IsEqualPoint(this Tolerance tol, double d1, double d2) + { + return Math.Abs(d1 - d2) < tol.EqualPoint; + } + + #region Curve3d + + /// + /// 获取三维解析类曲线(自交曲线)的交点参数 + /// + /// 三维解析类曲线 + /// 曲线参数的列表 + public static List GetParamsAtIntersectionPoints(this Curve3d c3d, bool sort = true) + { + CurveCurveIntersector3d cci = new(c3d, c3d, Vector3d.ZAxis); + List pars = new(); + for (int i = 0; i < cci.NumberOfIntersectionPoints; i++) + pars.AddRange(cci.GetIntersectionParameters(i)); + if (sort) + pars.Sort(); + return pars; + } + + /// + /// 获取三维解析类子曲线 + /// + /// 三维解析类曲线 + /// 子段曲线起点参数 + /// 子段曲线终点参数 + /// 三维解析类曲线 + public static Curve3d GetSubCurve(this Curve3d curve, double from, double to) + { + Interval inter = curve.GetInterval(); + bool atStart = Tolerance.Global.IsEqualPoint(inter.LowerBound, from); + bool atEnd = Tolerance.Global.IsEqualPoint(inter.UpperBound, to); + if (atStart && atEnd) + return (Curve3d)curve.Clone(); + if (curve is NurbCurve3d) + { + if (from < to) + { + NurbCurve3d clone = (NurbCurve3d)curve.Clone(); + if (atStart || atEnd) + { + clone.HardTrimByParams(from, to); + return clone; + } + else + { + clone.HardTrimByParams(inter.LowerBound, to); + clone.HardTrimByParams(from, to); + return clone; + } + } + else + { + NurbCurve3d clone1 = (NurbCurve3d)curve.Clone(); + clone1.HardTrimByParams(from, inter.UpperBound); + NurbCurve3d clone2 = (NurbCurve3d)curve.Clone(); + clone2.HardTrimByParams(inter.LowerBound, to); + clone1.JoinWith(clone2); + return clone1; + } + } + else + { + Curve3d clone = (Curve3d)curve.Clone(); + clone.SetInterval(new Interval(from, to, Tolerance.Global.EqualPoint)); + return clone; + } + } + + /// + /// 将三维解析类曲线转换为三维实体类曲线 + /// + /// 三维解析类曲线 + /// 三维实体类曲线 + public static Curve? ToCurve(this Curve3d curve) + { + return curve switch + { + CompositeCurve3d co => ToCurve(co), + LineSegment3d li => ToCurve(li), + EllipticalArc3d el => ToCurve(el), + CircularArc3d ci => ToCurve(ci), + NurbCurve3d nu => ToCurve(nu), + PolylineCurve3d pl => ToCurve(pl), + Line3d l3 => ToCurve(l3), + _ => null + }; + } + + /// + /// 将三维解析类曲线转换为三维解析类Nurb曲线 + /// + /// 三维解析类曲线 + /// 三维解析类Nurb曲线 + public static NurbCurve3d? ToNurbCurve3d(this Curve3d curve) + { + return curve switch + { + LineSegment3d line => new NurbCurve3d(line), + EllipticalArc3d el => new NurbCurve3d(el), + CircularArc3d cir => new NurbCurve3d(ToEllipticalArc3d(cir)), + NurbCurve3d nur => nur, + PolylineCurve3d pl => new NurbCurve3d(3, pl, false), + _ => null + }; + } + + #endregion Curve3d + + #region CompositeCurve3d + + /// + /// 判断是否为圆和椭圆 + /// + /// 三维解析类曲线 + /// 完整圆及完整的椭圆返回 ,反之返回 + public static bool IsCircular(this Curve3d curve) + { + return curve switch + { + CircularArc3d or EllipticalArc3d => curve.IsClosed(), + _ => false + }; + } + + /// + /// 将三维复合曲线按曲线参数分割 + /// + /// 三维复合曲线 + /// 曲线参数列表 + /// 三维复合曲线列表 + public static List? GetSplitCurves(this CompositeCurve3d c3d, List pars) + { + // 曲线参数剔除重复的 + if (pars.Count > 0) + { + pars.Sort(); + for (int i = pars.Count - 1; i > 0; i--) + if (Tolerance.Global.IsEqualPoint(pars[i], pars[i - 1])) + pars.RemoveAt(i); + } + if (pars.Count == 0) + return null; + + // 这个是曲线参数类 + var inter = c3d.GetInterval(); + // 曲线们 + var c3ds = c3d.GetCurves(); + if (c3ds.Length == 1 && c3ds[0].IsClosed()) + { + // 闭合曲线不允许打断于一点 + if (pars.Count < 2) + return null; + + // 如果包含起点 + if (Tolerance.Global.IsEqualPoint(pars[0], inter.LowerBound)) + { + pars[0] = inter.LowerBound; + // 又包含终点,去除终点 + if (Tolerance.Global.IsEqualPoint(pars[^1], inter.UpperBound)) + { + pars.RemoveAt(pars.Count - 1); + if (pars.Count == 1) + return null; + } + } + else if (Tolerance.Global.IsEqualPoint(pars[^1], inter.UpperBound)) + { + pars[^1] = inter.UpperBound; + } + // 加入第一点以支持反向打断 + pars.Add(pars[0]); + } + else + { + // 非闭合曲线加入起点和终点 + if (Tolerance.Global.IsEqualPoint(pars[0], inter.LowerBound)) + pars[0] = inter.LowerBound; + else + pars.Insert(0, inter.LowerBound); + if (Tolerance.Global.IsEqualPoint(pars[^1], inter.UpperBound)) + pars[^1] = inter.UpperBound; + else + pars.Add(inter.UpperBound); + } + + List curves = new(); + List cc3ds = new(); + for (int i = 0; i < pars.Count - 1; i++) + { + cc3ds.Clear(); + // 复合曲线参数转换到包含曲线参数 + var cp1 = c3d.GlobalToLocalParameter(pars[i]); + var cp2 = c3d.GlobalToLocalParameter(pars[i + 1]); + if (cp1.SegmentIndex == cp2.SegmentIndex) + { + cc3ds.Add( + c3ds[cp1.SegmentIndex].GetSubCurve( + cp1.LocalParameter, + cp2.LocalParameter)); + } + else + { + inter = c3ds[cp1.SegmentIndex].GetInterval(); + cc3ds.Add( + c3ds[cp1.SegmentIndex].GetSubCurve( + cp1.LocalParameter, + inter.UpperBound)); + + for (int j = cp1.SegmentIndex + 1; j < cp2.SegmentIndex; j++) + cc3ds.Add((Curve3d)c3ds[j].Clone()); + + inter = c3ds[cp2.SegmentIndex].GetInterval(); + cc3ds.Add( + c3ds[cp2.SegmentIndex].GetSubCurve( + inter.LowerBound, + cp2.LocalParameter)); + } + curves.Add(new(cc3ds.ToArray())); + } + + // 封闭多段线 口口 并排形状,第二个口切割不成功,注释下面就成功了 + //if (c3d.IsClosed() && c3ds.Length > 1) + //{ + // var cus1 = curves[^1].GetCurves(); + // var cus2 = curves[0].GetCurves(); + // var cs = cus1.Combine2(cus2); + // curves[^1] = new(cs); + // curves.RemoveAt(0); + //} + return curves; + } + + /// + /// 将复合曲线转换为实体类曲线 + /// + /// 三维复合曲线 + /// 实体曲线 + public static Curve? ToCurve(this CompositeCurve3d curve) + { + Curve3d[] cs = curve.GetCurves(); + if (cs.Length == 0) + return null; + if (cs.Length == 1) + return ToCurve(cs[0]); + + bool hasNurb = false; + + for (int i = 0; i < cs.Length; i++) + { + var c = cs[i]; + if (c is NurbCurve3d || c is EllipticalArc3d) + { + hasNurb = true; + break; + } + } + if (hasNurb) + { + var nc3d = cs[0].ToNurbCurve3d(); + for (int i = 1; i < cs.Length; i++) + nc3d?.JoinWith(cs[i].ToNurbCurve3d()); + return nc3d?.ToCurve(); + } + + return ToPolyline(curve); + } + + /// + /// 将三维复合曲线转换为实体类多段线 + /// + /// 三维复合曲线 + /// 实体类多段线 + public static Polyline ToPolyline(this CompositeCurve3d cc3d) + { + Polyline pl = new(); + pl.SetDatabaseDefaults(); + pl.Elevation = cc3d.StartPoint[2]; + + Plane plane = pl.GetPlane(); + Point2d endver = Point2d.Origin; + int i = 0; + foreach (Curve3d c3d in cc3d.GetCurves()) + { + if (c3d is CircularArc3d ca3d) + { + double b = Math.Tan(0.25 * (ca3d.EndAngle - ca3d.StartAngle)) * ca3d.Normal[2]; + pl.AddVertexAt(i, c3d.StartPoint.Convert2d(plane), b, 0, 0); + endver = c3d.EndPoint.Convert2d(plane); + } + else + { + pl.AddVertexAt(i, c3d.StartPoint.Convert2d(plane), 0, 0, 0); + endver = c3d.EndPoint.Convert2d(plane); + } + i++; + } + pl.AddVertexAt(i, endver, 0, 0, 0); + return pl; + } + + #endregion CompositeCurve3d + + #region Line3d + + /// + /// 将解析类三维构造线转换为实体类构造线 + /// + /// 解析类三维构造线 + /// 实体类构造线 + public static Xline ToCurve(this Line3d line3d) + { + return + new Xline + { + BasePoint = line3d.PointOnLine, + SecondPoint = line3d.PointOnLine + line3d.Direction + }; + } + + /// + /// 将三维解析类构造线转换为三维解析类线段 + /// + /// 三维解析类构造线 + /// 起点参数 + /// 终点参数 + /// 三维解析类线段 + public static LineSegment3d ToLineSegment3d(this Line3d line3d, double fromParameter, double toParameter) + { + return + new LineSegment3d + ( + line3d.EvaluatePoint(fromParameter), + line3d.EvaluatePoint(toParameter) + ); + } + + #endregion Line3d + + #region LineSegment3d + + /// + /// 将三维解析类线段转换为实体类直线 + /// + /// 三维解析类线段 + /// 实体类直线 + public static Line ToCurve(this LineSegment3d lineSeg3d) + { + return new Line(lineSeg3d.StartPoint, lineSeg3d.EndPoint); + } + + #endregion LineSegment3d + + #region CircularArc3d + + /// + /// 将三维解析类圆/弧转换为实体圆/弧 + /// + /// 三维解析类圆/弧 + /// 实体圆/弧 + public static Curve ToCurve(this CircularArc3d ca3d) + { + if (ca3d.IsClosed()) + { + return ToCircle(ca3d); + } + else + { + return ToArc(ca3d); + } + } + + /// + /// 将三维解析类圆/弧转换为实体圆 + /// + /// 三维解析类圆/弧 + /// 实体圆 + public static Circle ToCircle(this CircularArc3d ca3d) => + new(ca3d.Center, ca3d.Normal, ca3d.Radius); + + /// + /// 将三维解析类圆/弧转换为实体圆弧 + /// + /// 三维解析类圆/弧 + /// 实体圆弧 + public static Arc ToArc(this CircularArc3d ca3d) + { + // 必须新建,而不能直接使用GetPlane()获取 + double angle = ca3d.ReferenceVector.AngleOnPlane(new Plane(ca3d.Center, ca3d.Normal)); + return new Arc(ca3d.Center, ca3d.Normal, ca3d.Radius, ca3d.StartAngle + angle, ca3d.EndAngle + angle); + } + + /// + /// 将三维解析类圆/弧转换为三维解析类椭圆弧 + /// + /// 三维解析类圆/弧 + /// 三维解析类椭圆弧 + public static EllipticalArc3d ToEllipticalArc3d(this CircularArc3d ca3d) + { + Vector3d zaxis = ca3d.Normal; + Vector3d xaxis = ca3d.ReferenceVector; + Vector3d yaxis = zaxis.CrossProduct(xaxis); + + return + new EllipticalArc3d( + ca3d.Center, + xaxis, + yaxis, + ca3d.Radius, + ca3d.Radius, + ca3d.StartAngle, + ca3d.EndAngle); + } + + /// + /// 将三维解析类圆/弧转换为三维解析类Nurb曲线 + /// + /// 三维解析类圆/弧 + /// 三维解析类Nurb曲线 + public static NurbCurve3d ToNurbCurve3d(this CircularArc3d ca3d) + { + EllipticalArc3d ea3d = ToEllipticalArc3d(ca3d); + NurbCurve3d nc3d = new(ea3d); + return nc3d; + } + + #endregion CircularArc3d + + #region EllipticalArc3d + + /// + /// 将三维解析类椭圆弧转换为实体类椭圆弧 + /// + /// 三维解析类椭圆弧 + /// 实体类椭圆弧 + public static Ellipse ToCurve(this EllipticalArc3d ea3d) + { + Ellipse ell = + new( + ea3d.Center, + ea3d.Normal, + ea3d.MajorAxis * ea3d.MajorRadius, + ea3d.MinorRadius / ea3d.MajorRadius, + 0, + Math.PI * 2); + // Ge椭圆角度就是Db椭圆的参数 + if (!ea3d.IsClosed()) + { + ell.StartAngle = ell.GetAngleAtParameter(ea3d.StartAngle); + ell.EndAngle = ell.GetAngleAtParameter(ea3d.EndAngle); + } + return ell; + } + + #endregion EllipticalArc3d + + #region NurbCurve3d + + /// + /// 将三维解析类Nurb曲线转换为实体类样条曲线 + /// + /// 三维解析类Nurb曲线 + /// 实体类样条曲线 + public static Spline ToCurve(this NurbCurve3d nc3d) + { + Spline spl; + if (nc3d.HasFitData) + { + NurbCurve3dFitData fdata = nc3d.FitData; + if (fdata.TangentsExist) + { + spl = new Spline( + fdata.FitPoints, + fdata.StartTangent, + fdata.EndTangent, + nc3d.Order, + fdata.FitTolerance.EqualPoint); + } + else + { + spl = new Spline( + fdata.FitPoints, + nc3d.Order, + fdata.FitTolerance.EqualPoint); + } + } + else + { + DoubleCollection knots = new(); + foreach (double knot in nc3d.Knots) + knots.Add(knot); + + NurbCurve3dData ncdata = nc3d.DefinitionData; + + spl = new Spline( + ncdata.Degree, + ncdata.Rational, + nc3d.IsClosed(), + ncdata.Periodic, + ncdata.ControlPoints, + knots, + ncdata.Weights, + Tolerance.Global.EqualPoint, + ncdata.Knots.Tolerance); + } + return spl; + } + + #endregion NurbCurve3d + + #region PolylineCurve3d + + /// + /// 将三维解析类多段线转换为实体类三维多段线 + /// + /// 三维解析类多段线 + /// 实体类三维多段线 + public static Polyline3d ToCurve(this PolylineCurve3d pl3d) + { + using Point3dCollection pnts = new(); + + for (int i = 0; i < pl3d.NumberOfControlPoints; i++) + pnts.Add(pl3d.ControlPointAt(i)); + + bool closed = false; + int n = pnts.Count - 1; + if (pnts[0] == pnts[n]) + { + pnts.RemoveAt(n); + closed = true; + } + return new Polyline3d(Poly3dType.SimplePoly, pnts, closed); + } + + #endregion PolylineCurve3d +} \ No newline at end of file diff --git a/src/CAD/IFox.CAD.Shared/ExtensionMethod/CurveEx.cs b/src/CAD/IFox.CAD.Shared/ExtensionMethod/CurveEx.cs new file mode 100644 index 0000000..68b286f --- /dev/null +++ b/src/CAD/IFox.CAD.Shared/ExtensionMethod/CurveEx.cs @@ -0,0 +1,723 @@ +namespace IFoxCAD.Cad; + +/// +/// 实体类曲线扩展类 +/// +public static class CurveEx +{ + /// + /// 曲线长度 + /// + /// 曲线 + /// 长度 + public static double GetLength(this Curve curve) + { + return curve.GetDistanceAtParameter(curve.EndParam); + } + + /// + /// 获取分割曲线集合 + /// + /// 曲线 + /// 打断参数表 + /// 打断后曲线的集合 + public static IEnumerable GetSplitCurves(this Curve curve, IEnumerable pars) + { + if (pars is null) + throw new ArgumentNullException(nameof(pars)); + + return + curve + .GetSplitCurves(new DoubleCollection(pars.ToArray())) + .Cast(); + } + + /// + /// 获取分割曲线集合 + /// + /// 曲线 + /// 打断参数表 + /// 对参数表是否进行排序 + /// + /// 按参数值升序排序
    + /// 不排序,默认值 + ///
    + /// + /// 打断后曲线的集合 + public static IEnumerable GetSplitCurves(this Curve curve, IEnumerable pars, bool isOrder = false) + { + if (pars is null) + throw new ArgumentNullException(nameof(pars)); + if (isOrder) + pars = pars.OrderBy(x => x); + + return + curve + .GetSplitCurves(new DoubleCollection(pars.ToArray())) + .Cast(); + } + + /// + /// 获取分割曲线集合 + /// + /// 曲线 + /// 打断点表 + /// 打断后曲线的集合 + public static IEnumerable GetSplitCurves(this Curve curve, IEnumerable points) + { + if (points is null) + throw new ArgumentNullException(nameof(points)); + + using var pts = new Point3dCollection(points.ToArray()); + return curve.GetSplitCurves(pts).Cast(); + } + + /// + /// 获取分割曲线集合 + /// + /// 曲线 + /// 打断点表 + /// 对点表是否进行排序 + /// + /// 按参数值升序排序
    + /// 不排序,默认值 + ///
    + /// + /// 打断后曲线的集合 + public static IEnumerable GetSplitCurves(this Curve curve, IEnumerable points, bool isOrder = false) + { + if (points is null) + throw new ArgumentNullException(nameof(points)); + + if (isOrder) + points = points.OrderBy(point => { + var pt = curve.GetClosestPointTo(point, false); + return curve.GetParameterAtPoint(pt); + }); + + using Point3dCollection pts = new(points.ToArray()); + return curve.GetSplitCurves(pts).Cast(); + } + + /// + /// 获取曲线集所围成的封闭区域的曲线集,注意此函数不能处理平行边(两个点及两条线组成的闭合环) + /// + /// 曲线集合 + /// 所有的闭合环的曲线集合 + public static IEnumerable GetAllCycle(this IEnumerable curves) + { + if (curves is null) + throw new ArgumentNullException(nameof(curves)); + + // 新建图 + var graph = new Graph(); + foreach (var curve in curves) + { +#if !gcad +#if NET35 + graph.AddEdge(curve.ToCurve3d()!); +#else + graph.AddEdge(curve.GetGeCurve()); +#endif +#else + graph.AddEdge(curve.ToCurve3d()!); +#endif + } + // 新建 dfs + var dfs = new DepthFirst(); + // 查询全部的 闭合环 + dfs.FindAll(graph); + // 遍历闭合环的列表,将每个闭合环转换为实体曲线 + var res = new List(); + foreach (var item in dfs.Curve3ds) + { + var curve = graph.GetCurves(item.ToList()).ToArray(); + var comcur = new CompositeCurve3d(curve).ToCurve(); + if (comcur is not null) + res.Add(comcur); + } + return res; + } + /// + /// 曲线打断 + /// + /// 曲线列表 + /// 打断后的曲线列表 + public static List BreakCurve(this List curves) + { + if (curves is null) + throw new ArgumentNullException(nameof(curves)); + + var geCurves = new List(); // 存储曲线转换后的复合曲线 + var paramss = new List>(); // 存储每个曲线的交点参数值 + + for (int i = 0; i < curves.Count; i++) + { + var cc3d = curves[i].ToCompositeCurve3d(); + if (cc3d is not null) + { + geCurves.Add(cc3d); + paramss.Add(new List()); + } + } + + // var oldCurves = new List(); + var newCurves = new List(); + var cci3d = new CurveCurveIntersector3d(); + + for (int i = 0; i < curves.Count; i++) + { + var gc1 = geCurves[i]; + var pars1 = paramss[i]; // 引用 + for (int j = i; j < curves.Count; j++) + { + var gc2 = geCurves[j]; + var pars2 = paramss[j]; // 引用 + + cci3d.Set(gc1, gc2, Vector3d.ZAxis); + + for (int k = 0; k < cci3d.NumberOfIntersectionPoints; k++) + { + var pars = cci3d.GetIntersectionParameters(k); + pars1.Add(pars[0]); // 引用修改会同步到源对象 + pars2.Add(pars[1]); // 引用修改会同步到源对象 + } + } + + if (pars1.Count > 0) + { + var c3ds = gc1.GetSplitCurves(pars1); + if (c3ds.Count > 1) + { + foreach (var c3d in c3ds) + { + var c3dCur = c3d.ToCurve(); + if (c3dCur is not null) + { + c3dCur.SetPropertiesFrom(curves[i]); + newCurves.Add(c3dCur); + } + } + // oldCurves.Add(curves[i]); + } + } + } + + return newCurves; + } + + // 转换DBCurve为GeCurved + + #region Curve + + /// + /// 将曲线转换为ge曲线,此函数将在未来淘汰,二惊加油 + /// + /// 曲线 + /// ge曲线 + public static Curve3d? ToCurve3d(this Curve curve) + { + return curve switch + { + Line li => ToCurve3d(li), + Circle ci => ToCurve3d(ci), + Arc arc => ToCurve3d(arc), + Ellipse el => ToCurve3d(el), + Polyline pl => ToCurve3d(pl), + Polyline2d pl2 => ToCurve3d(pl2), + Polyline3d pl3 => ToCurve3d(pl3), + Spline sp => ToCurve3d(sp), + _ => null + }; + } + + /// + /// 将曲线转换为复合曲线 + /// + /// 曲线 + /// 复合曲线 + public static CompositeCurve3d? ToCompositeCurve3d(this Curve curve) + { + return curve switch + { + Line li => new CompositeCurve3d(new Curve3d[] { ToCurve3d(li) }), + Circle ci => new CompositeCurve3d(new Curve3d[] { ToCurve3d(ci) }), + Arc arc => new CompositeCurve3d(new Curve3d[] { ToCurve3d(arc) }), + Ellipse el => new CompositeCurve3d(new Curve3d[] { ToCurve3d(el) }), + Polyline pl => new CompositeCurve3d(new Curve3d[] { ToCurve3d(pl) }), + + Polyline2d pl2 => new CompositeCurve3d(new Curve3d[] { ToCurve3d(pl2)! }), + Polyline3d pl3 => new CompositeCurve3d(new Curve3d[] { ToCurve3d(pl3) }), + Spline sp => new CompositeCurve3d(new Curve3d[] { ToCurve3d(sp) }), + _ => null + }; + } + + /// + /// 将曲线转换为Nurb曲线 + /// + /// 曲线 + /// Nurb曲线 + public static NurbCurve3d? ToNurbCurve3d(this Curve curve) + { + return curve switch + { + Line li => ToNurbCurve3d(li), + Circle ci => ToNurbCurve3d(ci), + Arc arc => ToNurbCurve3d(arc), + Ellipse el => ToNurbCurve3d(el), + Polyline pl => ToNurbCurve3d(pl), + Polyline2d pl2 => ToNurbCurve3d(pl2), + Polyline3d pl3 => ToNurbCurve3d(pl3), + Spline sp => ToNurbCurve3d(sp), + _ => null + }; + } + + #region Line + + /// + /// 将直线转换为ge直线 + /// + /// 直线 + /// ge直线 + public static LineSegment3d ToCurve3d(this Line line) + { + return new LineSegment3d(line.StartPoint, line.EndPoint); + } + + /// + /// 将直线转换为Nurb曲线 + /// + /// 直线 + /// Nurb曲线 + public static NurbCurve3d ToNurbCurve3d(this Line line) + { + return new NurbCurve3d(ToCurve3d(line)); + } + + #endregion Line + + #region Circle + + /// + /// 将圆转换为ge圆弧曲线 + /// + /// 圆 + /// ge圆弧曲线 + public static CircularArc3d ToCurve3d(this Circle cir) + { + return + new CircularArc3d( + cir.Center, + cir.Normal, + cir.Radius); + } + + /// + /// 将圆转换为ge椭圆曲线 + /// + /// 圆 + /// ge椭圆曲线 + public static EllipticalArc3d ToEllipticalArc3d(this Circle cir) + { + return ToCurve3d(cir).ToEllipticalArc3d(); + } + + /// + /// 将圆转换为Nurb曲线 + /// + /// 圆 + /// Nurb曲线 + public static NurbCurve3d ToNurbCurve3d(this Circle cir) + { + return new NurbCurve3d(ToEllipticalArc3d(cir)); + } + + #endregion Circle + + #region Arc + + /// + /// 将圆弧转换为ge圆弧曲线 + /// + /// 圆弧 + /// ge圆弧曲线 + public static CircularArc3d ToCurve3d(this Arc arc) + { + Plane plane = new(arc.Center, arc.Normal); + + return + new CircularArc3d( + arc.Center, + arc.Normal, + plane.GetCoordinateSystem().Xaxis, + arc.Radius, + arc.StartAngle, + arc.EndAngle + ); + } + + /// + /// 将圆弧转换为ge椭圆曲线 + /// + /// 圆弧 + /// ge椭圆曲线 + public static EllipticalArc3d ToEllipticalArc3d(this Arc arc) + { + return ToCurve3d(arc).ToEllipticalArc3d(); + } + + /// + /// 将圆弧转换为三维Nurb曲线 + /// + /// 圆弧 + /// 三维Nurb曲线 + public static NurbCurve3d ToNurbCurve3d(this Arc arc) + { + return new NurbCurve3d(ToEllipticalArc3d(arc)); + } + + #endregion Arc + + #region Ellipse + + /// + /// 将椭圆转换为三维ge椭圆曲线 + /// + /// 椭圆 + /// 三维ge椭圆曲线 + public static EllipticalArc3d ToCurve3d(this Ellipse ell) + { + return + new EllipticalArc3d( + ell.Center, + ell.MajorAxis.GetNormal(), + ell.MinorAxis.GetNormal(), + ell.MajorRadius, + ell.MinorRadius, + ell.StartParam, + ell.EndParam); + } + + /// + /// 将椭圆转换为三维Nurb曲线 + /// + /// 椭圆 + /// 三维Nurb曲线 + public static NurbCurve3d ToNurbCurve3d(this Ellipse ell) + { + return new NurbCurve3d(ToCurve3d(ell)); + } + + #endregion Ellipse + + #region Spline + + /// + /// 将样条曲线转换为三维Nurb曲线 + /// + /// 样条曲线 + /// 三维Nurb曲线 + public static NurbCurve3d ToCurve3d(this Spline spl) + { + NurbCurve3d nc3d; + NurbsData ndata = spl.NurbsData; + KnotCollection knots = new(); + foreach (Double knot in ndata.GetKnots()) + knots.Add(knot); + + if (ndata.Rational) + { + nc3d = + new NurbCurve3d( + ndata.Degree, + knots, + ndata.GetControlPoints(), + ndata.GetWeights(), + ndata.Periodic); + } + else + { + nc3d = + new NurbCurve3d( + ndata.Degree, + knots, + ndata.GetControlPoints(), + ndata.Periodic); + } + + if (spl.HasFitData) + { + var fdata = spl.FitData; + var vec = new Vector3d(); + if (fdata.TangentsExist && (fdata.StartTangent != vec || fdata.EndTangent != vec)) + nc3d.SetFitData(fdata.GetFitPoints(), fdata.StartTangent, fdata.EndTangent); + } + return nc3d; + } + + #endregion Spline + + #region Polyline2d + + /// + /// 将二维多段线转换为三维ge曲线 + /// + /// 二维多段线 + /// 三维ge曲线 + public static Curve3d? ToCurve3d(this Polyline2d pl2d) + { + switch (pl2d.PolyType) + { + case Poly2dType.SimplePoly: + case Poly2dType.FitCurvePoly: + Polyline pl = new(); + pl.SetDatabaseDefaults(); + pl.ConvertFrom(pl2d, false); + return ToCurve3d(pl); + default: + return ToNurbCurve3d(pl2d); + } + + // Polyline pl = new Polyline(); + // pl.ConvertFrom(pl2d, false); + // return ToCurve3d(pl); + } + + /// + /// 将二维多段线转换为三维Nurb曲线 + /// + /// 二维多段线 + /// 三维Nurb曲线 + public static NurbCurve3d? ToNurbCurve3d(this Polyline2d pl2d) + { + switch (pl2d.PolyType) + { + case Poly2dType.SimplePoly: + case Poly2dType.FitCurvePoly: + Polyline pl = new(); + pl.SetDatabaseDefaults(); + pl.ConvertFrom(pl2d, false); + return ToNurbCurve3d(pl); + + default: + return ToCurve3d(pl2d.Spline); + } + } + + /// + /// 将二维多段线转换为三维ge多段线 + /// + /// 二维多段线 + /// 三维ge多段线 + public static PolylineCurve3d ToPolylineCurve3d(this Polyline2d pl) + { + using Point3dCollection pnts = new(); + foreach (Vertex2d ver in pl) + pnts.Add(ver.Position); + return new PolylineCurve3d(pnts); + } + + #endregion Polyline2d + + #region Polyline3d + + /// + /// 将三维多段线转换为三维曲线 + /// + /// 三维多段线 + /// 三维曲线 + public static Curve3d ToCurve3d(this Polyline3d pl3d) + { + return pl3d.PolyType switch + { + Poly3dType.SimplePoly => ToPolylineCurve3d(pl3d), + _ => ToNurbCurve3d(pl3d), + }; + } + + /// + /// 将三维多段线转换为三维Nurb曲线 + /// + /// 三维多段线 + /// 三维Nurb曲线 + public static NurbCurve3d ToNurbCurve3d(this Polyline3d pl3d) + { + return ToCurve3d(pl3d.Spline); + } + + /// + /// 将三维多段线转换为三维ge多段线 + /// + /// 三维多段线 + /// 三维ge多段线 + public static PolylineCurve3d ToPolylineCurve3d(this Polyline3d pl) + { + using Point3dCollection pnts = new(); + foreach (ObjectId id in pl) + { + var ver = id.GetObject(OpenMode.ForRead); + if (ver != null) + pnts.Add(ver.Position); + } + return new PolylineCurve3d(pnts); + } + + #endregion Polyline3d + + #region Polyline + + /// + /// 多段线转换为复合曲线 + /// + /// 多段线对象 + /// 复合曲线对象 + public static CompositeCurve3d ToCurve3d(this Polyline pl) + { + List c3ds = new(); + + for (int i = 0; i < pl.NumberOfVertices; i++) + { + switch (pl.GetSegmentType(i)) + { + case SegmentType.Line: + c3ds.Add(pl.GetLineSegmentAt(i)); + break; + + case SegmentType.Arc: + c3ds.Add(pl.GetArcSegmentAt(i)); + break; + + default: + break; + } + } + return new CompositeCurve3d(c3ds.ToArray()); + } + + /// + /// 多段线转换为Nurb曲线 + /// + /// 多段线 + /// Nurb曲线 + public static NurbCurve3d? ToNurbCurve3d(this Polyline pl) + { + NurbCurve3d? nc3d = null; + for (int i = 0; i < pl.NumberOfVertices; i++) + { + NurbCurve3d? nc3dtemp = null; + switch (pl.GetSegmentType(i)) + { + case SegmentType.Line: + nc3dtemp = new NurbCurve3d(pl.GetLineSegmentAt(i)); + break; + + case SegmentType.Arc: + nc3dtemp = pl.GetArcSegmentAt(i).ToNurbCurve3d(); + break; + + default: + break; + } + if (nc3d is null) + nc3d = nc3dtemp; + else if (nc3dtemp is not null) + nc3d.JoinWith(nc3dtemp); + } + return nc3d; + } + + /// + /// 为优化多段线倒角 + /// + /// 优化多段线 + /// 顶点索引号 + /// 倒角半径 + /// 倒角类型 + public static void ChamferAt(this Polyline polyline, int index, double radius, bool isFillet) + { + if (index < 1 || index > polyline.NumberOfVertices - 2) + throw new System.Exception("错误的索引号"); + + if (SegmentType.Line != polyline.GetSegmentType(index - 1) || + SegmentType.Line != polyline.GetSegmentType(index)) + throw new System.Exception("非直线段不能倒角"); + + // 获取当前索引号的前后两段直线,并组合为Ge复合曲线 + Curve3d[] c3ds = + new Curve3d[] + { + polyline.GetLineSegmentAt(index - 1), + polyline.GetLineSegmentAt(index) + }; + CompositeCurve3d cc3d = new(c3ds); + + // 试倒直角 + // 子曲线的个数有三种情况: + // 1、=3时倒角方向正确 + // 2、=2时倒角方向相反 + // 3、=0或为直线时失败 + c3ds = + cc3d.GetTrimmedOffset + ( + radius, + Vector3d.ZAxis, + OffsetCurveExtensionType.Chamfer + ); + + if (c3ds.Length > 0 && c3ds[0] is CompositeCurve3d) + { + var newcc3d = c3ds[0] as CompositeCurve3d; + c3ds = newcc3d!.GetCurves(); + if (c3ds.Length == 3) + { + c3ds = cc3d.GetTrimmedOffset + ( + -radius, + Vector3d.ZAxis, + OffsetCurveExtensionType.Chamfer + ); + if (c3ds.Length == 0 || c3ds[0] is LineSegment3d) + throw new System.Exception("倒角半径过大"); + } + else if (c3ds.Length == 2) + { + radius = -radius; + } + } + else + { + throw new System.Exception("倒角半径过大"); + } + + // GetTrimmedOffset会生成倒角+偏移,故先反方向倒角,再倒回 + c3ds = cc3d.GetTrimmedOffset + ( + -radius, + Vector3d.ZAxis, + OffsetCurveExtensionType.Extend + ); + OffsetCurveExtensionType type = + isFillet ? + OffsetCurveExtensionType.Fillet : OffsetCurveExtensionType.Chamfer; + c3ds = c3ds[0].GetTrimmedOffset + ( + radius, + Vector3d.ZAxis, + type + ); + + // 将结果Ge曲线转为Db曲线,并将相关的数值反映到原曲线 + var plTemp = c3ds[0].ToCurve() as Polyline; + if (plTemp is null) + return; + polyline.RemoveVertexAt(index); + polyline.AddVertexAt(index, plTemp.GetPoint2dAt(1), plTemp.GetBulgeAt(1), 0, 0); + polyline.AddVertexAt(index + 1, plTemp.GetPoint2dAt(2), 0, 0, 0); + } + + #endregion Polyline + + #endregion +} \ No newline at end of file diff --git a/src/CAD/IFox.CAD.Shared/ExtensionMethod/DBDictionaryEx.cs b/src/CAD/IFox.CAD.Shared/ExtensionMethod/DBDictionaryEx.cs new file mode 100644 index 0000000..e099a93 --- /dev/null +++ b/src/CAD/IFox.CAD.Shared/ExtensionMethod/DBDictionaryEx.cs @@ -0,0 +1,373 @@ +namespace IFoxCAD.Cad; + +/// +/// 字典扩展类 +/// +public static class DBDictionaryEx +{ + /// + /// 获取字典里的全部对象 + /// + /// 对象类型的泛型 + /// 字典 + /// 事务 + /// 对象迭代器 + [System.Diagnostics.DebuggerStepThrough] + public static IEnumerable GetAllObjects(this DBDictionary dict, DBTrans? trans = null) where T : DBObject + { + trans ??= DBTrans.Top; + foreach (DBDictionaryEntry e in dict) + { + var ent = trans.GetObject(e.Value); + if (ent is not null) + yield return ent; + } + } + + /// + /// 获取字典内指定key的对象 + /// + /// 对象类型的泛型 + /// 字典 + /// 事务 + /// 指定的键值 + /// T 类型的对象 + public static T? GetAt(this DBDictionary dict, string key, DBTrans? trans = null) where T : DBObject + { + trans ??= DBTrans.Top; + if (dict.Contains(key)) + { + ObjectId id = dict.GetAt(key); + if (!id.IsNull) + return trans.GetObject(id); + } + return null; + } + + /// + /// 添加条目(键值对)到字典 + /// + /// 对象类型 + /// 字典 + /// 事务 + /// 键 + /// 值 + public static void SetAt(this DBDictionary dict, string key, T newValue, Transaction? trans = null) where T : DBObject + { + trans ??= DBTrans.Top.Transaction; + using (dict.ForWrite()) + { + dict.SetAt(key, newValue); + trans.AddNewlyCreatedDBObject(newValue, true); + } + } + + #region XRecord + + /// + /// 从字典中获取扩展数据 + /// + /// 字典 + /// 键值 + /// 扩展数据 + public static XRecordDataList? GetXRecord(this DBDictionary dict, string key) + { + var rec = dict.GetAt(key); + if (rec is null) + return null; + return rec.Data; + } + + /// + /// 保存扩展数据到字典 + /// + /// 扩展数据 + /// 字典 + /// 键值 + public static void SetXRecord(this DBDictionary dict, string key, XRecordDataList rb) + { + // DxfCode.300 字符串可以写 Data + // DxfCode.1004 内存流不给写 Data,只能去写 XData + using Xrecord newValue = new() { Data = rb }; + dict.SetAt(key, newValue); + } + #endregion + + /// + /// 获取扩展字典 + /// + /// 对象 + /// 事务 + /// 扩展字典对象 + public static DBDictionary? GetXDictionary(this DBObject obj, DBTrans? trans = null) + { + trans ??= DBTrans.Top; + ObjectId id = obj.ExtensionDictionary; + if (id.IsNull) + { + using (obj.ForWrite()) + obj.CreateExtensionDictionary(); + + id = obj.ExtensionDictionary; + } + return id.GetObject(trans: trans); + } + + #region 数据表 + + /// + /// 创建数据表 + /// + /// 原数据类型的字典 + /// 表元素(二维数组) + /// 数据表 + public static DataTable CreateDataTable(Dictionary colTypes, object[,] content) + { + DataTable table = new(); + foreach (var t in colTypes) + table.AppendColumn(t.Value, t.Key); + + var ncol = colTypes.Count; + var types = new CellType[ncol]; + colTypes.Values.CopyTo(types, 0); + + var nrow = content.GetLength(0); + for (int i = 0; i < nrow; i++) + { + DataCellCollection row = new(); + for (int j = 0; j < ncol; j++) + { + var cell = new DataCell(); + cell.SetValue(types[j], content[i, j]); + row.Add(cell); + } + table.AppendRow(row, true); + } + return table; + } + + /// + /// 设定单元格数据 + /// + /// 单元格 + /// 类型 + /// 数据 + public static void SetValue(this DataCell cell, CellType type, object value) + { + switch (type) + { + case CellType.Bool: + cell.SetBool((bool)value); + break; + + case CellType.CharPtr: + cell.SetString((string)value); + break; + + case CellType.Integer: + cell.SetInteger((int)value); + break; + + case CellType.Double: + cell.SetDouble((double)value); + break; + + case CellType.ObjectId: + cell.SetObjectId((ObjectId)value); + break; + + case CellType.Point: + cell.SetPoint((Point3d)value); + break; + + case CellType.Vector: + cell.SetVector((Vector3d)value); + break; + + case CellType.HardOwnerId: + cell.SetHardOwnershipId((ObjectId)value); + break; + + case CellType.HardPtrId: + cell.SetHardPointerId((ObjectId)value); + break; + + case CellType.SoftOwnerId: + cell.SetSoftOwnershipId((ObjectId)value); + break; + + case CellType.SoftPtrId: + cell.SetSoftPointerId((ObjectId)value); + break; + } + } + #endregion + + #region 子字典 + /// + /// 获取子字典 + /// + /// 根字典 + /// 事务 + /// 是否创建子字典 + /// 键值列表 + /// 字典 + public static DBDictionary? GetSubDictionary(this DBDictionary dict, + bool createSubDictionary, + IEnumerable dictNames, + DBTrans? trans = null) + { + DBDictionary? newdict = null; + trans ??= DBTrans.Top; + if (createSubDictionary) + { + using (dict.ForWrite()) + dict.TreatElementsAsHard = true; + + foreach (string name in dictNames) + { + if (dict.Contains(name)) + { + newdict = dict.GetAt(name, trans); + } + else + { + DBDictionary subDict = new(); + dict.SetAt(name, subDict, trans); + newdict = subDict; + newdict.TreatElementsAsHard = true; + } + } + } + else + { + foreach (string name in dictNames) + { + if (dict.Contains(name)) + newdict = dict.GetAt(name, trans); + else + return null; + } + } + return newdict; + } + + + ///// + ///// 获取对象扩展字典的子字典 + ///// + ///// 对象 + ///// 事务 + ///// 是否创建子字典 + ///// 键值列表 + ///// 字典 + // public static DBDictionary GetSubDictionary(this DBObject obj, bool createSubDictionary, params string[] dictNames) + // { + // return obj.GetXDictionary().GetSubDictionary(createSubDictionary, dictNames); + // } + + #endregion + + #region 组字典 + /// + /// 添加编组 + /// + /// 组名 + /// 实体Id集合 + /// 编组Id + public static ObjectId AddGroup(this DBDictionary dict, string name, ObjectIdCollection ids) + { + if (dict.Contains(name)) + return ObjectId.Null; + + using (dict.ForWrite()) + { + Group g = new(); + g.Append(ids); + dict.SetAt(name, g); + DBTrans.Top.Transaction.AddNewlyCreatedDBObject(g, true); + return g.ObjectId; + } + } + + /// + /// 添加编组 + /// + /// 组名 + /// 实体Id集合 + /// 编组Id + public static ObjectId AddGroup(this DBDictionary dict, string name, IEnumerable ids) + { + if (dict.Contains(name)) + return ObjectId.Null; + + using ObjectIdCollection idc = new(ids.ToArray());//需要using吗? 暂无测试 + return dict.AddGroup(name, idc); + } + + + /// + /// 按选择条件获取编组集合 + /// + /// 选择条件,过滤函数 + /// g.NumEntities < 2);]]> + /// 编组集合 + public static IEnumerable GetGroups(this DBDictionary dict, Func func) + { + return dict.GetAllObjects() + .Where(func); + } + + /// + /// 返回实体的所在编组的集合 + /// + /// 图元实体 + /// 编组集合 + public static IEnumerable GetGroups(this Entity ent) + { + return ent.GetPersistentReactorIds() + .Cast() + .Select(id => id.GetObject()) + .OfType(); + } + + /// + /// 移除所有的空组 + /// + /// 被移除编组的名称集合 + public static List RemoveNullGroup(this DBDictionary dict) + { + var groups = dict.GetGroups(g => g.NumEntities < 2); + List names = new(); + foreach (Group g in groups) + { + names.Add(g.Name); + using (g.ForWrite()) + g.Erase(); + } + return names; + } + + /// + /// 移除所有空组 + /// + /// 过滤条件,过滤要删除的组名的规则函数 + /// RemoveNullGroup(g => g.StartsWith("hah")) + /// 被移除编组的名称集合 + public static List RemoveNullGroup(this DBDictionary dict, Func func) + { + var groups = dict.GetGroups(g => g.NumEntities < 2); + List names = new(); + foreach (Group g in groups) + { + if (func(g.Name)) + { + names.Add(g.Name); + using (g.ForWrite()) + g.Erase(); + } + } + return names; + } + #endregion +} \ No newline at end of file diff --git a/src/CAD/IFox.CAD.Shared/ExtensionMethod/DBObjectEx.cs b/src/CAD/IFox.CAD.Shared/ExtensionMethod/DBObjectEx.cs new file mode 100644 index 0000000..9733d94 --- /dev/null +++ b/src/CAD/IFox.CAD.Shared/ExtensionMethod/DBObjectEx.cs @@ -0,0 +1,153 @@ +using System.Linq; + +namespace IFoxCAD.Cad; + +/// +/// 实体对象扩展类 +/// +public static class DBObjectEx +{ + #region Xdata扩展 + /// + /// 删除扩展数据 + /// + /// 对象实例 + /// 应用程序名称 + /// 要删除数据的组码 + public static void RemoveXData(this DBObject obj, string appName, DxfCode dxfCode) + { + if (obj.XData == null) + return; + XDataList data = obj.XData; + + // 测试命令 addxdata removexdata + // 移除指定App的扩展 + var indexs = data.GetXdataAppIndex(appName, new DxfCode[] { dxfCode }); + if (indexs.Count == 0) + return; + + for (int i = indexs.Count - 1; i >= 0; i--) + data.RemoveAt(indexs[i]); + + using (obj.ForWrite()) + obj.XData = data; + } + /// + /// 删除扩展数据 + /// + /// 对象实例 + /// 应用程序名称 + public static void RemoveXData(this DBObject obj, string appName) + { + if (obj.XData == null) + return; + foreach (var data in obj.XData) + { + // 直接赋值进去等于清空名称 + using var rb = new ResultBuffer(); + rb.Add(new((int)DxfCode.ExtendedDataRegAppName, appName)); + using (obj.ForWrite()) + obj.XData = rb; + } + } + + /// + /// 修改扩展数据 + /// + /// 对象实例 + /// 应用程序名称 + /// 要修改数据的组码 + /// 新的数据 + public static void ChangeXData(this DBObject obj, string appName, DxfCode dxfCode, object newvalue) + { + if (obj.XData == null) + return; + XDataList data = obj.XData; + + var indexs = data.GetXdataAppIndex(appName, new DxfCode[] { dxfCode }); + if (indexs.Count == 0) + return; + + for (int i = indexs.Count - 1; i >= 0; i--) + data[indexs[i]] = new TypedValue((short)dxfCode, newvalue); + + using (obj.ForWrite()) + obj.XData = data; + } + #endregion + + #region 读写模式切换 + +#line hidden // 调试的时候跳过它 + /// + /// 实体自动管理读写函数 + /// + /// 实体类型 + /// 实体对象 + /// 操作委托 + public static void ForWrite(this T obj, Action action) where T : DBObject + { + var _isNotifyEnabled = obj.IsNotifyEnabled; + var _isWriteEnabled = obj.IsWriteEnabled; + if (_isNotifyEnabled) + obj.UpgradeFromNotify(); + else if (!_isWriteEnabled) + obj.UpgradeOpen(); + + action?.Invoke(obj); + + if (_isNotifyEnabled) + obj.DowngradeToNotify(_isWriteEnabled); + else if (!_isWriteEnabled) + obj.DowngradeOpen(); + } + + /// + /// 打开模式提权 + /// + /// 实体对象 + /// 提权类对象 + public static UpgradeOpenManager ForWrite(this DBObject obj) + { + return new UpgradeOpenManager(obj); + } + + /// + /// 提权类 + /// + public class UpgradeOpenManager : IDisposable + { + private readonly DBObject _obj; + private readonly bool _isNotifyEnabled; + private readonly bool _isWriteEnabled; + + internal UpgradeOpenManager(DBObject obj) + { + _obj = obj; + _isNotifyEnabled = _obj.IsNotifyEnabled; + _isWriteEnabled = _obj.IsWriteEnabled; + if (_isNotifyEnabled) + _obj.UpgradeFromNotify(); + else if (!_isWriteEnabled) + _obj.UpgradeOpen(); + } + + #region IDisposable 成员 + + /// + /// 注销函数 + /// + public void Dispose() + { + if (_isNotifyEnabled) + _obj.DowngradeToNotify(_isWriteEnabled); + else if (!_isWriteEnabled) + _obj.DowngradeOpen(); + GC.SuppressFinalize(this); + } + + #endregion IDisposable 成员 + } +#line default + #endregion +} \ No newline at end of file diff --git a/src/CAD/IFox.CAD.Shared/ExtensionMethod/DBTransEx.cs b/src/CAD/IFox.CAD.Shared/ExtensionMethod/DBTransEx.cs new file mode 100644 index 0000000..fdd4e91 --- /dev/null +++ b/src/CAD/IFox.CAD.Shared/ExtensionMethod/DBTransEx.cs @@ -0,0 +1,91 @@ +#define lack_test + +namespace IFoxCAD.Cad; + +#if lack_test +public static class DBTransEx +{ + /* + * 0x01 + * db.Purge(ids)是获取未硬引用(无引用?)的对象,也就可以删除的. + * 0x02 + * 如果一个图元引用一个图层, + * 假设这个图元是可以删除的(实际上它可能来自于词典记录的id) => 那么它被 db.Purge(ids) 识别, + * 但是这个图层因为有硬引用,所以不被 db.Purge(ids) 识别, + * 只能删除图元之后,循环第二次再通过 db.Purge(ids) 获取图层id. + * 0x03 + * 因为删除之后,符号表内的引用可能被修改,因此需要重复遍历符号表. + * 0x04 + * 试试循环事务 + * 0x05 + * 无法过滤外部参照图层,使得全部图层打开了 + */ + + /// + /// 清理符号表 + /// + /// + /// + /// 排除外部参照:默认true,为false时候会令图层全部显示再清理,包括冻结 + public static void Purge(this DBTrans tr, SymModes sym = SymModes.All, bool excludeXref = true) + { + using ObjectIdCollection ids = new(); + var db = tr.Database; + + if ((sym & SymModes.BlockTable) == SymModes.BlockTable) + { + if (!excludeXref) + GetAllIds(tr, tr.BlockTable, ids, excludeXref); + else + tr.BlockTable.ForEach(tabRec => { + if (!tabRec.IsFromExternalReference) + ids.Add(tabRec.Id); + }, checkIdOk: true); + } + if ((sym & SymModes.DimStyleTable) == SymModes.DimStyleTable) + GetAllIds(tr, tr.DimStyleTable, ids, excludeXref); + if ((sym & SymModes.LayerTable) == SymModes.LayerTable) + GetAllIds(tr, tr.LayerTable, ids, excludeXref); + if ((sym & SymModes.LinetypeTable) == SymModes.LinetypeTable) + GetAllIds(tr, tr.LinetypeTable, ids, excludeXref); + if ((sym & SymModes.TextStyleTable) == SymModes.TextStyleTable) + GetAllIds(tr, tr.TextStyleTable, ids, excludeXref); + if ((sym & SymModes.ViewportTable) == SymModes.ViewportTable) + GetAllIds(tr, tr.ViewportTable, ids, excludeXref); + if ((sym & SymModes.RegAppTable) == SymModes.RegAppTable) + GetAllIds(tr, tr.RegAppTable, ids, excludeXref); + + // SHUN007 说这两个表可能有错误 + if ((sym & SymModes.ViewTable) == SymModes.ViewTable) + GetAllIds(tr, tr.ViewTable, ids, excludeXref); + if ((sym & SymModes.UcsTable) == SymModes.UcsTable) + GetAllIds(tr, tr.UcsTable, ids, excludeXref); + + // Purge是查询能够清理的对象 + db.Purge(ids); + foreach (ObjectId id in ids) + id.Erase(); + } + + static void GetAllIds(DBTrans tr, + SymbolTable symbolTable, + ObjectIdCollection ids, + bool excludeXref = true) + where TTable : SymbolTable + where TRecord : SymbolTableRecord, new() + { + if (!excludeXref) + symbolTable.ForEach(id => ids.Add(id)); + else + { + symbolTable.ForEach(id => { + var tabRec = tr.GetObject(id); + if (tabRec == null) + return; + if (!tabRec.Name.Contains("|")) + ids.Add(tabRec.Id); + }); + } + } +} +#endif \ No newline at end of file diff --git a/src/CAD/IFox.CAD.Shared/ExtensionMethod/EditorEx.cs b/src/CAD/IFox.CAD.Shared/ExtensionMethod/EditorEx.cs new file mode 100644 index 0000000..f9ec949 --- /dev/null +++ b/src/CAD/IFox.CAD.Shared/ExtensionMethod/EditorEx.cs @@ -0,0 +1,1170 @@ +using IFoxCAD.Com; + +namespace IFoxCAD.Cad; + +/// +/// 命令行扩展类 +/// +public static class EditorEx +{ + #region 选择集 + /// + /// 选择穿过一个点的对象 + /// + /// 命令行对象 + /// 点 + /// 过滤器 + /// 选择集结果类 + public static PromptSelectionResult SelectAtPoint(this Editor editor, Point3d point, + SelectionFilter? filter = default) + { + return editor.SelectCrossingWindow(point, point, filter); + } + + /// + /// 根据线宽创建图层选择集 + /// + /// 命令行对象 + /// 线宽 + /// 图层选择集 + public static SelectionSet SelectByLineWeight(this Editor editor, LineWeight lineWeight) + { + OpFilter filter = new OpEqual(370, lineWeight); + + var lays = + DBTrans.Top.LayerTable + .GetRecords() + .Where(ltr => ltr.LineWeight == lineWeight) + .Select(ltr => ltr.Name) + .ToArray(); + + if (lays.Length > 0) + { + filter = + new OpOr + { + filter, + new OpAnd + { + { 8, string.Join(",", lays) }, + { 370, LineWeight.ByLayer } + } + }; + } + + var res = editor.SelectAll(filter); + return res.Value; + } + + /// + /// 选择集 + /// + /// 命令行对象 + /// 模式 + /// 过滤器 + /// 消息 + /// 关键字和回调函数 + /// + public static PromptSelectionResult SSGet(this Editor editor, + string? mode = null, + SelectionFilter? filter = null, + string[]? messages = null, + Dictionary? keywords = null) + { + PromptSelectionOptions pso = new(); + PromptSelectionResult ss; + if (mode is not null) + { + mode = mode.ToUpper(); + pso.SinglePickInSpace = mode.Contains(":A"); + pso.RejectObjectsFromNonCurrentSpace = mode.Contains(":C"); + pso.AllowDuplicates = mode.Contains(":D"); + pso.SelectEverythingInAperture = mode.Contains(":E"); + pso.RejectObjectsOnLockedLayers = mode.Contains(":L"); + pso.PrepareOptionalDetails = mode.Contains(":N"); + pso.SingleOnly = mode.Contains(":S"); + pso.RejectPaperspaceViewport = mode.Contains(":V"); + pso.AllowSubSelections = mode.Contains("-A"); + pso.ForceSubSelections = mode.Contains("-F"); + } + if (messages is not null) + { + pso.MessageForAdding = messages[0]; + pso.MessageForRemoval = messages[1]; + } + + if (keywords is not null) + { + foreach (var keyword in keywords.Keys) + pso.Keywords.Add(keyword); + if (pso.MessageForRemoval is null) + pso.MessageForAdding = "选择对象"; + pso.MessageForAdding += $"[{string.Join(" / ", keywords.Keys.ToArray())}]"; + pso.KeywordInput += (s, e) => { + if (keywords.ContainsKey(e.Input)) + keywords[e.Input].Invoke(); + }; + } + try + { + if (filter is not null) + ss = editor.GetSelection(pso, filter); + else + ss = editor.GetSelection(pso); + } + catch (Exception) + { + //editor.WriteMessage($"\nKey is {e.Message}"); + throw; + } + return ss; + } + + + /* + * // 定义选择集选项 + * var pso = new PromptSelectionOptions + * { + * AllowDuplicates = false, // 重复选择 + * }; + * + * // getai遍历全图选择块有用到 + * var dic = new Dictionary() { + * { "Z,全部同名", ()=> { + * getai = BlockHelper.EnumAttIdentical.AllBlockName; + * SendEsc.Esc(); + * }}, + * { "X,动态块显示", ()=> { + * getai = BlockHelper.EnumAttIdentical.Display; + * }}, + * { "V,属性值-默认", ()=> { + * getai = BlockHelper.EnumAttIdentical.DisplayAndTagText; + * }}, + * // 允许以下操作,相同的会加入前面的 + * // { "V,属性值-默认|X,啊啊啊啊", ()=> { + * + * // }}, + * }; + * pso.SsgetAddKeys(dic); + * + * // 创建选择集过滤器,只选择块对象 + * var filList = new TypedValue[] { new TypedValue((int)DxfCode.Start, "INSERT") }; + * var filter = new SelectionFilter(filList); + * ssPsr = ed.GetSelection(pso, filter); + */ + + /// + /// 添加选择集关键字和回调 + /// + /// 选择集配置 + /// 关键字,回调委托 + /// + public static void SsgetAddKeys(this PromptSelectionOptions pso, + Dictionary dicActions) + { + Dictionary tmp = new(); + // 后缀名的|号切割,移除掉,组合成新的加入tmp + for (int i = dicActions.Count - 1; i >= 0; i--) + { + var pair = dicActions.ElementAt(i); + var key = pair.Key; + var keySp = key.Split('|'); + if (keySp.Length < 2) + continue; + + for (int j = 0; j < keySp.Length; j++) + { + var item = keySp[j]; + // 防止多个后缀通过|符越过词典约束同名 + // 后缀(key)含有,而且Action(value)不同,就把Action(value)累加到后面. + if (dicActions.ContainsKey(item)) + { + if (dicActions[item] != dicActions[key]) + dicActions[item] += dicActions[key]; + } + else + tmp.Add(item, dicActions[key]); + } + dicActions.Remove(key); + } + + foreach (var item in tmp) + dicActions.Add(item.Key, item.Value); + + // 去除关键字重复的,把重复的执行动作移动到前面 + for (int i = 0; i < dicActions.Count; i++) + { + var pair1 = dicActions.ElementAt(i); + var key1 = pair1.Key; + + for (int j = dicActions.Count - 1; j > i; j--) + { + var pair2 = dicActions.ElementAt(j); + var key2 = pair2.Key; + + if (key1.Split(',')[0] == key2.Split(',')[0]) + { + if (dicActions[key1] != dicActions[key2]) + dicActions[key1] += dicActions[key2]; + dicActions.Remove(key2); + } + } + } + + foreach (var item in dicActions) + { + var keySplitS = item.Key.Split(new string[] { ",", "|" }, StringSplitOptions.RemoveEmptyEntries); + for (int i = 0; i < keySplitS.Length; i += 2) + pso.Keywords.Add(keySplitS[i], keySplitS[i], + keySplitS[i + 1] + "(" + keySplitS[i] + ")"); + } + + // 回调的时候我想用Dict的O(1)索引, + // 但是此函数内进行new Dictionary() 在函数栈释放的时候,它被释放掉了. + // 因此 dicActions 参数的生命周期 + tmp = new(dicActions); + dicActions.Clear(); + foreach (var item in tmp) + dicActions.Add(item.Key.Split(',')[0], item.Value); + + var keyWords = pso.Keywords; + // 从选择集命令中显示关键字 + pso.MessageForAdding = keyWords.GetDisplayString(true); + // 关键字回调事件 ssget关键字 + pso.KeywordInput += (sender, e) => { + dicActions[e.Input].Invoke(); + }; + } + + + + + + + // #region 即时选择样板 + // /// + // /// 即时选择,框选更新关键字 + // /// + // public static void SelectTest() + // { + // Env.Editor.WriteMessage("\n[白嫖工具]--测试"); + // // 激活选中事件 + // Env.Editor.SelectionAdded += SelectTest_SelectionAdded; + // // 初始化坐标系 + // Env.Editor.CurrentUserCoordinateSystem = Matrix3d.Identity; + + // // 创建过滤器 + // var sf = new OpEqual(0, "arc"); + // var pso = new PromptSelectionOptions + // { + // MessageForAdding = "\n请选择对象:" + // }; + + // pso.Keywords.Add("Z"); + // pso.Keywords.Add("X"); + // pso.Keywords.Add("Q"); + // // 注册关键字 + // pso.KeywordInput += SelectTest_KeywordInput; + // try + // { + // // 用户选择 + // var psr = Env.Editor.GetSelection(pso, sf); + // // 处理代码 + + + // } + // catch (Exception ex)// 捕获关键字 + // { + // if (ex.Message == "XuError") + // { + // // 关闭关键字事件 + // pso.KeywordInput -= SelectTest_KeywordInput; + // // 关闭选中事件 + // Env.Editor.SelectionAdded -= SelectTest_SelectionAdded; + // // 重新调用自身 + // ZengLiangYuanJiao(); + // } + // } + // // 关闭关键字事件 + // pso.KeywordInput -= SelectTest_KeywordInput; + // // 关闭选中事件 + // Env.Editor.SelectionAdded -= SelectTest_SelectionAdded; + // } + + // /// + // /// 即时选择 + // /// + // /// + // /// + // private static void SelectTest_SelectionAdded(object sender, SelectionAddedEventArgs e) + // { + // // 关闭选中事件 + // Env.Editor.SelectionAdded -= SelectTest_SelectionAdded; + // using (var tr = new DBTrans()) + // { + // // 处理代码 + // for (int i = 0; i < e.AddedObjects.Count; i++) + // { + // // 处理完移除已处理的对象 + // e.Remove(i); + // } + // } + // // 激活选中事件 + // Env.Editor.SelectionAdded += SelectTest_SelectionAdded; + // } + + // /// + // /// 关键字响应 + // /// + // /// + // /// + // private static void SelectTest_KeywordInput(object sender, SelectionTextInputEventArgs e) + // { + // // 获取关键字 + // switch (e.Input) + // { + // case "Z": + // break; + // case "X": + // break; + + // case "Q": + // break; + // } + // // 抛出异常,用于更新提示信息 + // throw new ArgumentException("XuError"); + // } + // #endregion + #endregion + + #region Info + + /// + /// 带错误提示对话框的打印信息函数 + /// + /// 带格式项的字符串 + /// 指定格式化的对象数组 + public static void StreamMessage(string format, params object[] args) + { + StreamMessage(string.Format(format, args)); + } + + /// + /// 带错误提示对话框的打印信息函数 + /// + /// 打印信息 + public static void StreamMessage(string message) + { + try + { + if (HasEditor()) + WriteMessage(message); + else + InfoMessageBox(message); + } + catch (System.Exception ex) + { + Message(ex); + } + } + + /// + /// 异常信息对话框 + /// + /// 异常 + public static void Message(System.Exception ex) + { + try + { + System.Windows.Forms.MessageBox.Show( + ex.ToString(), + "Error", + System.Windows.Forms.MessageBoxButtons.OK, + System.Windows.Forms.MessageBoxIcon.Error); + } + catch + { + } + } + + /// + /// 提示信息对话框 + /// + /// 对话框的标题 + /// 对话框文本 + public static void InfoMessageBox(string caption, string message) + { + try + { + System.Windows.Forms.MessageBox.Show( + message, + caption, + System.Windows.Forms.MessageBoxButtons.OK, + System.Windows.Forms.MessageBoxIcon.Information); + } + catch (System.Exception ex) + { + Message(ex); + } + } + + /// + /// 提示信息对话框 + /// + /// 对话框的标题 + /// 带格式化项的对话框文本 + /// 指定格式化的对象数组 + public static void InfoMessageBox(string caption, string format, params object[] args) + { + InfoMessageBox(caption, string.Format(format, args)); + } + + /// + /// 提示信息对话框,默认标题为NFox.Cad + /// + /// 对话框文本 + public static void InfoMessageBox(string message) + { + InfoMessageBox("NFox.Cad", message); + } + + /// + /// 提示信息对话框 + /// + /// 带格式化项的对话框文本 + /// 指定格式化的对象数组 + public static void InfoMessageBox(string format, params object[] args) + { + InfoMessageBox(string.Format(format, args)); + } + + /// + /// 命令行打印字符串 + /// + /// 字符串 + public static void WriteMessage(string message) + { + try + { + if (Acceptable()) + Acap.DocumentManager.MdiActiveDocument.Editor.WriteMessage("\n" + message); + else + return; + } + catch (System.Exception ex) + { + Message(ex); + } + } + + /// + /// 命令行打印字符串 + /// + /// 带格式化项的文本 + /// 指定格式化的对象数组 + public static void WriteMessage(string format, params object[] args) + { + WriteMessage(string.Format(format, args)); + } + + /// + /// 判断是否有活动的编辑器对象 + /// + /// 有,没有 + public static bool HasEditor() + { + return Acap.DocumentManager.MdiActiveDocument is not null + && Acap.DocumentManager.Count != 0 + && Acap.DocumentManager.MdiActiveDocument.Editor is not null; + } + + /// + /// 判断是否可以打印字符串 + /// + /// 可以打印,不可以打印 + public static bool Acceptable() + { + return HasEditor() + && !Acap.DocumentManager.MdiActiveDocument.Editor.IsDragging; + } + + #endregion Info + + #region 画矢量线 + + /// + /// 根据点表返回矢量线的列表 + /// + /// 点表 + /// 是否闭合, 为闭合, 为不闭合 + /// + public static List GetLines(IEnumerable pnts, bool isClosed) + { + var itor = pnts.GetEnumerator(); + if (!itor.MoveNext()) + return new List(); + + List values = new(); + + TypedValue tvFirst = new((int)LispDataType.Point2d, itor.Current); + TypedValue tv1; + TypedValue tv2 = tvFirst; + + while (itor.MoveNext()) + { + tv1 = tv2; + tv2 = new TypedValue((int)LispDataType.Point2d, itor.Current); + values.Add(tv1); + values.Add(tv2); + } + + if (isClosed) + { + values.Add(tv2); + values.Add(tvFirst); + } + + return values; + } + + /// + /// 画矢量线 + /// + /// 编辑器对象 + /// 点表 + /// 颜色码 + /// 是否闭合, 为闭合, 为不闭合 + public static void DrawVectors(this Editor editor, IEnumerable pnts, short colorIndex, bool isClosed) + { + var rlst = + new LispList { { LispDataType.Int16, colorIndex } }; + rlst.AddRange(GetLines(pnts, isClosed)); + editor.DrawVectors(rlst, editor.CurrentUserCoordinateSystem); + } + + /// + /// 画矢量线 + /// + /// 编辑器对象 + /// 点表 + /// 颜色码 + public static void DrawVectors(this Editor editor, IEnumerable pnts, short colorIndex) + { + editor.DrawVectors(pnts, colorIndex, false); + } + + /// + /// 用矢量线画近似圆(正多边形) + /// + /// 编辑器对象 + /// 点表 + /// 颜色码 + /// 半径 + /// 多边形边的个数 + public static void DrawCircles(this Editor editor, IEnumerable pnts, short colorIndex, double radius, int numEdges) + { + var rlst = + new LispList { { LispDataType.Int16, colorIndex } }; + + foreach (Point2d pnt in pnts) + { + Vector2d vec = Vector2d.XAxis * radius; + double angle = Math.PI * 2 / numEdges; + + List tpnts = new() + { + pnt + vec + }; + for (int i = 1; i < numEdges; i++) + { + tpnts.Add(pnt + vec.RotateBy(angle * i)); + } + + rlst.AddRange(GetLines(tpnts, true)); + } + editor.DrawVectors(rlst, editor.CurrentUserCoordinateSystem); + } + + /// + /// 用矢量线画近似圆(正多边形) + /// + /// 编辑器对象 + /// 点 + /// 颜色码 + /// 半径 + /// 多边形边的个数 + public static void DrawCircle(this Editor editor, Point2d pnt, short colorIndex, double radius, int numEdges) + { + Vector2d vec = Vector2d.XAxis * radius; + double angle = Math.PI * 2 / numEdges; + + List pnts = new() + { + pnt + vec + }; + for (int i = 1; i < numEdges; i++) + pnts.Add(pnt + vec.RotateBy(angle * i)); + + editor.DrawVectors(pnts, colorIndex, true); + } + + #endregion + + #region 矩阵 + + /// + /// 获取UCS到WCS的矩阵 + /// + /// 命令行对象 + /// 变换矩阵 + public static Matrix3d GetMatrixFromUcsToWcs(this Editor editor) + { + return editor.CurrentUserCoordinateSystem; + } + + /// + /// 获取WCS到UCS的矩阵 + /// + /// 命令行对象 + /// 变换矩阵 + public static Matrix3d GetMatrixFromWcsToUcs(this Editor editor) + { + return editor.CurrentUserCoordinateSystem.Inverse(); + } + + /// + /// 获取MDCS(模型空间)到WCS的矩阵 + /// + /// 命令行对象 + /// 变换矩阵 + public static Matrix3d GetMatrixFromMDcsToWcs(this Editor editor) + { + Matrix3d mat; + using ViewTableRecord vtr = editor.GetCurrentView(); + mat = Matrix3d.PlaneToWorld(vtr.ViewDirection); + mat = Matrix3d.Displacement(vtr.Target - Point3d.Origin) * mat; + return Matrix3d.Rotation(-vtr.ViewTwist, vtr.ViewDirection, vtr.Target) * mat; + } + + /// + /// 获取WCS到MDCS(模型空间)的矩阵 + /// + /// 命令行对象 + /// 变换矩阵 + public static Matrix3d GetMatrixFromWcsToMDcs(this Editor editor) + { + return editor.GetMatrixFromMDcsToWcs().Inverse(); + } + + /// + /// 获取MDCS(模型空间)到PDCS(图纸空间)的矩阵 + /// + /// 命令行对象 + /// 变换矩阵 + public static Matrix3d GetMatrixFromMDcsToPDcs(this Editor editor) + { + if ((short)Env.GetVar("TILEMODE") == 1) + throw new ArgumentException("TILEMODE == 1..Espace papier uniquement"); + + Matrix3d mat = Matrix3d.Identity; + using DBTrans tr = new(); + var vp = tr.GetObject(editor.CurrentViewportObjectId); + if (vp == null) + return mat; + + if (vp.Number == 1) + { + try + { + editor.SwitchToModelSpace(); + vp = tr.GetObject(editor.CurrentViewportObjectId); + editor.SwitchToPaperSpace(); + } + catch + { + throw new Exception("Aucun fenêtre active...ErrorStatus.InvalidInput"); + } + } + if (vp == null) + return mat; + + Point3d vCtr = new(vp.ViewCenter.X, vp.ViewCenter.Y, 0.0); + mat = Matrix3d.Displacement(vCtr.GetAsVector().Negate()); + mat = Matrix3d.Displacement(vp.CenterPoint.GetAsVector()) * mat; + mat = Matrix3d.Scaling(vp.CustomScale, vp.CenterPoint) * mat; + return mat; + } + + /// + /// 获取PDCS(图纸空间)到MDCS(模型空间)的矩阵 + /// + /// 命令行对象 + /// 变换矩阵 + public static Matrix3d GetMatrixFromPDcsToMDcs(this Editor editor) + { + return editor.GetMatrixFromMDcsToPDcs().Inverse(); + } + + /// + /// 获取变换矩阵 + /// + /// 命令行对象 + /// 源坐标系 + /// 目标坐标系 + /// 变换矩阵 + public static Matrix3d GetMatrix(this Editor editor, CoordinateSystemCode from, CoordinateSystemCode to) + { +#if ac2009 + switch (from) + { + case CoordinateSystemCode.Wcs: + switch (to) + { + case CoordinateSystemCode.Ucs: + return editor.GetMatrixFromWcsToUcs(); + + case CoordinateSystemCode.MDcs: + return editor.GetMatrixFromMDcsToWcs(); + + case CoordinateSystemCode.PDcs: + throw new Exception("To be used only with DCS...ErrorStatus.InvalidInput"); + } + break; + case CoordinateSystemCode.Ucs: + switch (to) + { + case CoordinateSystemCode.Wcs: + return editor.GetMatrixFromUcsToWcs(); + + case CoordinateSystemCode.MDcs: + return editor.GetMatrixFromUcsToWcs() * editor.GetMatrixFromWcsToMDcs(); + + case CoordinateSystemCode.PDcs: + throw new Exception("To be used only with DCS... ErrorStatus.InvalidInput"); + } + break; + case CoordinateSystemCode.MDcs: + switch (to) + { + case CoordinateSystemCode.Wcs: + return editor.GetMatrixFromMDcsToWcs(); + + case CoordinateSystemCode.Ucs: + return editor.GetMatrixFromMDcsToWcs() * editor.GetMatrixFromWcsToUcs(); + + case CoordinateSystemCode.PDcs: + return editor.GetMatrixFromMDcsToPDcs(); + } + break; + case CoordinateSystemCode.PDcs: + switch (to) + { + case CoordinateSystemCode.Wcs: + throw new Exception("To be used only with DCS... ErrorStatus.InvalidInput"); + case CoordinateSystemCode.Ucs: + throw new Exception("To be used only with DCS... ErrorStatus.InvalidInput"); + case CoordinateSystemCode.MDcs: + return editor.GetMatrixFromPDcsToMDcs(); + } + break; + } + return Matrix3d.Identity; +#else + return (from, to) switch + { + (CoordinateSystemCode.Wcs, CoordinateSystemCode.Ucs) => editor.GetMatrixFromWcsToUcs(), + (CoordinateSystemCode.Wcs, CoordinateSystemCode.MDcs) => editor.GetMatrixFromWcsToMDcs(), + (CoordinateSystemCode.Ucs, CoordinateSystemCode.Wcs) => editor.GetMatrixFromUcsToWcs(), + (CoordinateSystemCode.Ucs, CoordinateSystemCode.MDcs) => editor.GetMatrixFromUcsToWcs() * editor.GetMatrixFromWcsToMDcs(), + (CoordinateSystemCode.MDcs, CoordinateSystemCode.Wcs) => editor.GetMatrixFromMDcsToWcs(), + (CoordinateSystemCode.MDcs, CoordinateSystemCode.Ucs) => editor.GetMatrixFromMDcsToWcs() * editor.GetMatrixFromWcsToUcs(), + (CoordinateSystemCode.MDcs, CoordinateSystemCode.PDcs) => editor.GetMatrixFromMDcsToPDcs(), + (CoordinateSystemCode.PDcs, CoordinateSystemCode.MDcs) => editor.GetMatrixFromPDcsToMDcs(), + (CoordinateSystemCode.PDcs, CoordinateSystemCode.Wcs or CoordinateSystemCode.Ucs) + or (CoordinateSystemCode.Wcs or CoordinateSystemCode.Ucs, CoordinateSystemCode.PDcs) => throw new Exception("To be used only with DCS...ErrorStatus.InvalidInput"), + (_, _) => Matrix3d.Identity + }; +#endif + } + + #endregion + + #region 缩放 + + /// + /// 缩放窗口范围 + /// + /// 命令行对象 + /// 窗口左下点 + /// 窗口右上点 + public static void ZoomWindow(this Editor ed, Point3d minPoint, Point3d maxPoint) + { + ViewTableRecord vtr = new(); + vtr.CopyFrom(ed.GetCurrentView()); + + var oldpnts = new Point3d[] { minPoint, maxPoint }; + var pnts = new Point3d[8]; + var dpnts = new Point3d[8]; + + var mat = ed.GetMatrixFromWcsToMDcs(); + for (int i = 0; i < 2; i++) + for (int j = 0; j < 2; j++) + for (int k = 0; k < 2; k++) + { + int n = i * 4 + j * 2 + k; + pnts[n] = new Point3d(oldpnts[i][0], oldpnts[j][1], oldpnts[k][2]); + dpnts[n] = pnts[n].TransformBy(mat); + } + + double xmin, xmax, ymin, ymax; + xmin = xmax = dpnts[0][0]; + ymin = ymax = dpnts[0][1]; + for (int i = 1; i < 8; i++) + { + xmin = Math.Min(xmin, dpnts[i][0]); + xmax = Math.Max(xmax, dpnts[i][0]); + ymin = Math.Min(ymin, dpnts[i][1]); + ymax = Math.Max(ymax, dpnts[i][1]); + } + + vtr.Width = xmax - xmin; + vtr.Height = ymax - ymin; + vtr.CenterPoint = (dpnts[0] + (dpnts[7] - dpnts[0]) / 2) + .Convert2d(Curve2dEx._planeCache); + + ed.SetCurrentView(vtr); + ed.Regen(); + } + + /// + /// 缩放窗口范围 + /// + /// 命令行对象 + /// 窗口范围点 + public static void ZoomWindow(this Editor ed, Extents3d ext) + { + ZoomWindow(ed, ext.MinPoint, ext.MaxPoint); + } + + /// + /// 缩放比例 + /// + /// 命令行对象 + /// 中心点 + /// 窗口宽 + /// 窗口高 + public static void Zoom(this Editor ed, Point3d CenPt, double width, double height) + { + using ViewTableRecord view = ed.GetCurrentView(); + view.Width = width; + view.Height = height; + view.CenterPoint = new Point2d(CenPt.X, CenPt.Y); + ed.SetCurrentView(view);// 更新当前视图 + } + + /// + /// 缩放窗口范围 + /// + /// 命令行对象 + /// 第一点 + /// 对角点 + /// 偏移距离 + public static void ZoomWindow(this Editor ed, Point3d lpt, Point3d rpt, double offsetDist = 0.00) + { + Extents3d extents = new(); + extents.AddPoint(lpt); + extents.AddPoint(rpt); + rpt = extents.MaxPoint + new Vector3d(offsetDist, offsetDist, 0); + lpt = extents.MinPoint - new Vector3d(offsetDist, offsetDist, 0); + Vector3d ver = rpt - lpt; + ed.Zoom(lpt + ver / 2, ver.X, ver.Y); + } + + + /// + /// 获取有效的数据库范围 + /// + /// 数据库 + /// 容差值:图元包围盒会超过数据库边界,用此参数扩大边界 + /// + public static Extents3d? GetValidExtents3d(this Database db, double extention = 1e-6) + { + db.UpdateExt(true);// 更新当前模型空间的范围 + var ve = new Vector3d(extention, extention, extention); + // 数据库没有图元的时候,min是大,max是小,导致新建出错 + // 数据如下: + // min.X == 1E20 && min.Y == 1E20 && min.Z == 1E20 && + // max.X == -1E20 && max.Y == -1E20 && max.Z == -1E20) + var a = db.Extmin; + var b = db.Extmax; + if (a.X < b.X && a.Y < b.Y) + return new Extents3d(db.Extmin - ve, db.Extmax + ve); + + return null; + } + + /// + /// 动态缩放 + /// + /// 命令行对象 + /// 偏移距离 + public static void ZoomExtents(this Editor ed, double offsetDist = 0.00) + { + Database db = ed.Document.Database; + // db.UpdateExt(true); // GetValidExtents3d内提供了 + var dbExtent = db.GetValidExtents3d(); + if (dbExtent == null) + ed.ZoomWindow(Point3d.Origin, new Point3d(1, 1, 0), offsetDist); + else + ed.ZoomWindow(db.Extmin, db.Extmax, offsetDist); + } + + /// + /// 根据实体对象的范围显示视图 + /// + /// 命令行对象 + /// Entity对象 + /// 偏移距离 + public static void ZoomObject(this Editor ed, Entity ent, double offsetDist = 0.00) + { + Extents3d ext = ent.GeometricExtents; + ed.ZoomWindow(ext.MinPoint, ext.MaxPoint, offsetDist); + } + + #endregion + + #region Get交互类 + + /// + /// 获取Point + /// + /// 命令行对象 + /// 提示信息 + /// 提示使用的基点 + /// + public static PromptPointResult GetPoint(this Editor ed, string Message, Point3d BasePoint) + { + PromptPointOptions ptOp = new(Message) + { + BasePoint = BasePoint, + UseBasePoint = true + }; + return ed.GetPoint(ptOp); + } + + /// + /// 获取double值 + /// + /// 命令行对象 + /// 提示信息 + /// double默认值 + /// + public static PromptDoubleResult GetDouble(this Editor ed, string Message, double DefaultValue = 1.0) + { + PromptDoubleOptions douOp = new(Message) + { + DefaultValue = DefaultValue + }; + return ed.GetDouble(douOp); + } + + /// + /// 获取int值 + /// + /// 命令行对象 + /// 提示信息 + /// double默认值 + /// + public static PromptIntegerResult GetInteger(this Editor ed, string Message, int DefaultValue = 1) + { + PromptIntegerOptions douOp = new(Message) + { + DefaultValue = DefaultValue + }; + return ed.GetInteger(douOp); + } + + /// + /// 获取string值 + /// + /// 命令行对象 + /// 提示信息 + /// string默认值 + /// + public static PromptResult GetString(this Editor ed, string Message, string DefaultValue = "") + { + PromptStringOptions strOp = new(Message) + { + DefaultValue = DefaultValue + }; + return ed.GetString(strOp); + } + + #endregion + + #region 执行lisp +#if NET35 + [DllImport("acad.exe", +#else + [DllImport("accore.dll", +#endif + CallingConvention = CallingConvention.Cdecl, EntryPoint = "acedInvoke")] + static extern int AcedInvoke(IntPtr args, out IntPtr result); + +#if NET35 + [DllImport("acad.exe", CharSet = CharSet.Unicode, CallingConvention = CallingConvention.Cdecl, + EntryPoint = "?acedEvaluateLisp@@YAHPB_WAAPAUresbuf@@@Z")] +#else + // 高版本此接口不能使用lisp(command "xx"),但是可以直接在自动运行接口上 + [DllImport("accore.dll", CharSet = CharSet.Unicode, CallingConvention = CallingConvention.Cdecl, + EntryPoint = "?acedEvaluateLisp@@YAHPEB_WAEAPEAUresbuf@@@Z")] +#endif + [System.Security.SuppressUnmanagedCodeSecurity]// 初始化默认值 + static extern int AcedEvaluateLisp(string lispLine, out IntPtr result); + +#if NET35 + [DllImport("acad.exe", +#else + [DllImport("accore.dll", +#endif + CharSet = CharSet.Auto, CallingConvention = CallingConvention.Cdecl, EntryPoint = "ads_queueexpr")] + static extern int Ads_queueexpr(string strExpr); + + public enum RunLispFlag : byte + { + AdsQueueexpr = 1, + AcedEvaluateLisp = 2, + SendStringToExecute = 4, + } + + /* + * 测试命令: + * [CommandMethod(nameof(CmdTest_RunLisp))] + * public static void CmdTest_RunLisp() + * { + * var res = RunLisp("(setq abc 10)"); + * } + * 调用方式: + * (command "CmdTest_RunLisp1") + * bug说明: + * AcedEvaluateLisp 接口 + * 在高版本调用时候没有运行成功,使得 !abc 没有值 + * 在cad08成功,此bug与CommandFlags无关 + * 解决方案: + * 0x01 用异步接口,但是这样是显式调用了: + * (setq thisdrawing (vla-get-activedocument (vlax-get-acad-object)))(vla-SendCommand thisdrawing "CmdTest_RunLisp1 ") + * 0x02 使用 Ads_queueexpr 接口 + */ + /// + /// 发送lisp语句字符串到cad执行 + /// + /// 编辑器对象 + /// lisp语句 + /// 运行方式 + /// 缓冲结果,返回值 + public static ResultBuffer? RunLisp(this Editor ed, string lispCode, RunLispFlag flag = RunLispFlag.AdsQueueexpr) + { + if ((flag & RunLispFlag.AdsQueueexpr) == RunLispFlag.AdsQueueexpr) + { + // 这个在08/12发送lisp不会出错,但是发送bo命令出错了. + // 0x01 设置 CommandFlags.Session 可以同步, + // 0x02 自执行发送lisp都是异步,用来发送 含有(command)的lisp的 + _ = Ads_queueexpr(lispCode + "\n"); + } + if ((flag & RunLispFlag.AcedEvaluateLisp) == RunLispFlag.AcedEvaluateLisp) + { + _ = AcedEvaluateLisp(lispCode, out IntPtr rb); + if (rb != IntPtr.Zero) + return (ResultBuffer)DisposableWrapper.Create(typeof(ResultBuffer), rb, true); + } + if ((flag & RunLispFlag.SendStringToExecute) == RunLispFlag.SendStringToExecute) + { + var dm = Acap.DocumentManager; + var doc = dm.MdiActiveDocument; + doc?.SendStringToExecute(lispCode + "\n", false, false, false); + } + return null; + } + #endregion + + #region Export + /// + /// 输出WMF
    + /// 此函数不适用于后台 + ///
    + /// 命令行对象 + /// 保存文件 + /// 选择集的对象,为null时候手选 + /// 是否清空选择集 + /// + public static void ComExportWMF(this Editor editor, string saveFile, + ObjectId[]? ids = null, bool wmfSetDel = false) + { + if (string.IsNullOrEmpty(saveFile)) + throw new ArgumentNullException(nameof(saveFile)); + if (File.Exists(saveFile)) + throw new FileFormatException("文件重复:" + saveFile); + + var dm = Acap.DocumentManager; + if (dm.Count == 0) + return; + + // 剔除后缀 + int dot = saveFile.LastIndexOf('.'); + if (dot != -1) + { + // 因为文件名可以有.所以后缀点必须是最后\的后面 + int s = saveFile.LastIndexOf('\\'); + if (s < dot) + saveFile = saveFile.Substring(0, dot); + } + + // ActiveSelectionSet: + // 第一次执行会触发选择,再次重复命令执行的时候,它会无法再选择(即使清空选择集). + // 因此此处netAPI进行选择,它就能读取当前选择集缓冲区的对象 + if (ids == null || ids.Length == 0) + { + var psr = editor.SelectImplied();// 预选 + if (psr.Status != PromptStatus.OK) + psr = editor.GetSelection();// 手选 + if (psr.Status != PromptStatus.OK) + return; + ids = psr.Value.GetObjectIds(); + } + editor.SetImpliedSelection(ids); + +#if zcad + var com = Acap.ZcadApplication; +#else + var com = Acap.AcadApplication; +#endif + var doc = com.GetProperty("ActiveDocument"); + var wmfSet = doc.GetProperty("ActiveSelectionSet"); + + // TODO 20221007 导出wmf的bug + // cad21 先net选择,再进行,此处再选择一次? + // cad21 调试期间无法选择性粘贴? + var exp = doc.Invoke("Export", saveFile, "wmf", wmfSet); // JPGOUT,PNGOUT + if (wmfSetDel) + wmfSet.Invoke("Delete"); + } + #endregion + + /// + /// 可以发送透明命令的状态
    + /// 福萝卜:这个应该是修正ribbon里输入丢焦点的问题,低版本可以不要 + ///
    + /// + /// + public static bool IsQuiescentForTransparentCommand(this Editor ed) + { +#if NET35 + //if (ed.IsQuiescent) + //{ + //} + return true; +#else + return ed.IsQuiescentForTransparentCommand; +#endif + } +} \ No newline at end of file diff --git a/src/CAD/IFox.CAD.Shared/ExtensionMethod/EntityBoundingInfo.cs b/src/CAD/IFox.CAD.Shared/ExtensionMethod/EntityBoundingInfo.cs new file mode 100644 index 0000000..baf2fc4 --- /dev/null +++ b/src/CAD/IFox.CAD.Shared/ExtensionMethod/EntityBoundingInfo.cs @@ -0,0 +1,281 @@ +#define Debug_Cause_Error +namespace IFoxCAD.Cad; + +/// +/// AABB和OBB信息 +/// +public struct BoundingInfo +{ + public double MinX; + public double MinY; + public double MinZ; + + public double MaxX; + public double MaxY; + public double MaxZ; + + /// + /// AABB这里永远是0 + /// + public double Angle; + public bool IsEffective; + + public Point3d Min => new(MinX, MinY, MinZ); + public Point3d Max => new(MaxX, MaxY, MaxZ); + public Extents3d Extents3d => new(Min, Max); + public Extents2d Extents2d => new(MinX, MinY, MaxX, MaxY); + + public BoundingInfo(double minX, double minY, double minZ, + double maxX, double maxY, double maxZ, + bool isEffective, double angle = 0) + { + MinX = minX; + MinY = minY; + MinZ = minZ; + MaxX = maxX; + MaxY = maxY; + MaxZ = maxZ; + IsEffective = isEffective; + Angle = angle; + } + + public BoundingInfo(Point3d min, Point3d max, bool isEffective, double angle = 0) + : this(min.X, min.Y, min.Z, + max.X, max.Y, max.Z, + isEffective, angle) + { } + + // public BoundingInfo(Rect rect, double angle = 0) + // { + // MinX = rect.X; + // MinY = rect.Y; + // MinZ = 0; + // MaxX = rect.Right; + // MaxY = rect.Top; + // MaxZ = 0; + // Angle = angle; + // } + + public override string ToString() + { + return Extents3d.ToString(); + } + + public void Move(Point3d pt1, Point3d pt2) + { + var ve = pt1 - pt2; + MinX -= ve.X; + MinY -= ve.Y; + MinZ -= ve.Z; + MaxX -= ve.X; + MaxY -= ve.Y; + MaxZ -= ve.Z; + } +} + +public class EntityBoundingInfo +{ + #region 保存异常类型的日志 + /// + /// 包围盒错误文件路径 + /// + public static string BoxLogAddress + { + get + { + _BoxLogAddress ??= LogHelper.GetDefaultOption(nameof(GetBoundingInfo) + ".config"); + return _BoxLogAddress; + } + set { _BoxLogAddress = value; } + } + static string? _BoxLogAddress; + static readonly HashSet _typeNames; + /// + /// 为了保证更好的性能, + /// 只是在第一次调用此功能的时候进行读取, + /// 免得高频调用时候高频触发磁盘 + /// + static EntityBoundingInfo() + { + _typeNames = new(); + if (!File.Exists(BoxLogAddress)) + return; + ExceptionToHashSet(); + } + + /// + /// 读取日志的异常到容器 + /// + static void ExceptionToHashSet() + { + var old_LogAddress = LogHelper.LogAddress; + try + { + LogHelper.OptionFile(BoxLogAddress); + var logTxts = new FileLogger().ReadLog(); + for (int i = 0; i < logTxts.Length; i++) + { + var line = logTxts[i]; + if (line.Contains(nameof(LogTxt.备注信息))) + { + int index = line.IndexOf(":"); + index = line.IndexOf("\"", index) + 1;// 1是"\"" + int index2 = line.IndexOf("\"", index); + var msg = line.Substring(index, index2 - index); + _typeNames.Add(msg); + } + } + } + finally + { + LogHelper.LogAddress = old_LogAddress; + } + } + + /// + /// 写入容器类型到异常日志 + /// + /// + /// + static void ExceptionToLog(Exception e, Entity ent) + { + // 无法处理的错误类型将被记录 + // 如果错误无法try,而是cad直接致命错误,那么此处也不会被写入, + // 这种情况无法避免程序安全性,总不能写了日志再去删除日志词条,这样会造成频繁IO的 + // 遇到一个不认识的类型再去写入?然后记录它是否可以写入? + var old_LogAddress = LogHelper.LogAddress; + var old_FlagOutFile = LogHelper.FlagOutFile; + try + { + LogHelper.FlagOutFile = true; + LogHelper.OptionFile(BoxLogAddress); + e.WriteLog(ent.GetType().Name, LogTarget.FileNotException); + } + finally + { + LogHelper.LogAddress = old_LogAddress; + LogHelper.FlagOutFile = old_FlagOutFile; + } + } + #endregion + + /// + /// 获取图元包围盒 + /// + /// + /// (左下角,右上角,是否有效) + /// 异常: + /// 会将包围盒类型记录到所属路径中,以此查询 + public static BoundingInfo GetBoundingInfo(Entity ent) + { +#if Debug_Cause_Error + // 错误类型处理 + if (ent is AttributeDefinition) // 属性定义 + return new(Point3d.Origin, Point3d.Origin, false); + if (ent is Xline xline)// 参照线 + return new(xline.BasePoint, xline.BasePoint, true); + if (ent is Ray ray)// 射线 + return new(ray.BasePoint, ray.BasePoint, true); +#endif + // 指定类型处理 + if (ent is BlockReference brf) + return GetBoxInfoInBlockReference(brf); + if (ent is MText mText) + return GetBoxInfoInMText(mText); + + if (!_typeNames.Contains(ent.GetType().Name)) // 屏蔽天正等等缺失包围盒的类型 + try + { + return new(ent.GeometricExtents.MinPoint, ent.GeometricExtents.MaxPoint, true); + } + catch (Exception e) { ExceptionToLog(e, ent); } + return new(Point3d.Origin, Point3d.Origin, false); + } + + /// + /// 处理块参照 + /// + static BoundingInfo GetBoxInfoInBlockReference(BlockReference brf) + { + try + { + // 这个获取是原点附近,需要平移到块基点 + var fit = brf.GeometryExtentsBestFit(); + var minX = fit.MinPoint.X + brf.Position.X; + var minY = fit.MinPoint.Y + brf.Position.Y; + var minZ = fit.MinPoint.Z + brf.Position.Z; + var maxX = fit.MaxPoint.X + brf.Position.X; + var maxY = fit.MaxPoint.Y + brf.Position.Y; + var maxZ = fit.MaxPoint.Z + brf.Position.Z; + return new(minX, minY, minZ, maxX, maxY, maxZ, true); + } + catch + { + // 如果是一条参照线的组块,将导致获取包围盒时报错 + // 0x01 是否需要进入块内,然后拿到每个图元的BasePoint再计算中点?感觉过于复杂. + // 0x02 这个时候拿基点走就算了 + return new(brf.Position, brf.Position, true); + } + } + + /// + /// 处理多行文字 + /// + static BoundingInfo GetBoxInfoInMText(MText mtxt) + { + /* + * MText Aussehen + * ------------------------------------ + * | | | + * | | |ht + * | | | + * |-----wl-------插入点------wr-------| + * | | | + * | | |hb + * | | | + * ------------------------------------ + */ + + double width = mtxt.ActualWidth;// 实际宽度 + double height = mtxt.ActualHeight;// 实际高度 + double wl = 0.0; + double hb = 0.0; + + // 对齐方式 + switch (mtxt.Attachment) + { + case AttachmentPoint.TopCenter: + case AttachmentPoint.MiddleCenter: + case AttachmentPoint.BottomCenter: + wl = width * -0.5; + break; + case AttachmentPoint.TopRight: + case AttachmentPoint.MiddleRight: + case AttachmentPoint.BottomRight: + wl = -width; + break; + } + switch (mtxt.Attachment) + { + case AttachmentPoint.TopLeft: + case AttachmentPoint.TopCenter: + case AttachmentPoint.TopRight: + hb = -height;// 下边线到插入点的距离 + break; + case AttachmentPoint.MiddleLeft: + case AttachmentPoint.MiddleCenter: + case AttachmentPoint.MiddleRight: + hb = height * -0.5; + break; + } + + double wr = width + wl; + double ht = height + hb; + + Point3d center = mtxt.Location; + return new(center.X + wl, center.Y + hb, 0, + center.X + wr, center.Y + ht, 0, + true, + mtxt.Rotation); + } +} \ No newline at end of file diff --git a/src/CAD/IFox.CAD.Shared/ExtensionMethod/EntityEx.cs b/src/CAD/IFox.CAD.Shared/ExtensionMethod/EntityEx.cs new file mode 100644 index 0000000..d839ba8 --- /dev/null +++ b/src/CAD/IFox.CAD.Shared/ExtensionMethod/EntityEx.cs @@ -0,0 +1,474 @@ +namespace IFoxCAD.Cad; + + +/// +/// 实体图元类扩展函数 +/// +public static class EntityEx +{ + #region 多段线端点坐标 + /// + /// 获取二维多段线的端点坐标 + /// + /// 二维多段线 + /// 事务 + /// 端点坐标集合 + public static IEnumerable GetPoints(this Polyline2d pl2d, DBTrans? trans = null) + { + trans ??= DBTrans.Top; + foreach (ObjectId id in pl2d) + yield return trans.GetObject(id)!.Position; + } + + /// + /// 获取三维多段线的端点坐标 + /// + /// 三维多段线 + /// 事务 + /// 端点坐标集合 + public static IEnumerable GetPoints(this Polyline3d pl3d, DBTrans? trans = null) + { + trans ??= DBTrans.Top; + foreach (ObjectId id in pl3d) + yield return trans.GetObject(id, OpenMode.ForRead)!.Position; + } + + /// + /// 获取多段线的端点坐标 + /// + /// 多段线 + /// 端点坐标集合 + public static IEnumerable GetPoints(this Polyline pl) + { + return + Enumerable + .Range(0, pl.NumberOfVertices) + .Select(pl.GetPoint3dAt); + } + #endregion + + #region 实体线性变换 + + /// + /// 移动实体 + /// + /// 实体 + /// 基点 + /// 目标点 + public static void Move(this Entity ent, Point3d from, Point3d to) + { + ent.TransformBy(Matrix3d.Displacement(to - from)); + } + + /// + /// 缩放实体 + /// + /// 实体 + /// 缩放基点坐标 + /// 缩放比例 + public static void Scale(this Entity ent, Point3d center, double scaleValue) + { + ent.TransformBy(Matrix3d.Scaling(scaleValue, center)); + } + + /// + /// 旋转实体 + /// + /// 实体 + /// 旋转中心 + /// 转角,弧度制,正数为顺时针 + /// 旋转平面的法向矢量 + public static void Rotation(this Entity ent, Point3d center, double angle, Vector3d normal) + { + ent.TransformBy(Matrix3d.Rotation(angle, normal, center)); + } + + /// + /// 在XY平面内旋转实体 + /// + /// 实体 + /// 旋转中心 + /// 转角,弧度制,正数为顺时针 + public static void Rotation(this Entity ent, Point3d center, double angle) + { + ent.TransformBy(Matrix3d.Rotation(angle, Vector3d.ZAxis.TransformBy(ent.Ecs), center)); + } + + /// + /// 按对称轴镜像实体 + /// + /// 实体 + /// 对称轴起点 + /// 对称轴终点 + public static void Mirror(this Entity ent, Point3d startPoint, Point3d endPoint) + { + ent.TransformBy(Matrix3d.Mirroring(new Line3d(startPoint, endPoint))); + } + + /// + /// 按对称面镜像实体 + /// + /// 实体 + /// 对称平面 + public static void Mirror(this Entity ent, Plane plane) + { + ent.TransformBy(Matrix3d.Mirroring(plane)); + } + + /// + /// 按对称点镜像实体 + /// + /// 实体 + /// 对称点 + public static void Mirror(this Entity ent, Point3d basePoint) + { + ent.TransformBy(Matrix3d.Mirroring(basePoint)); + } + + #endregion + + #region 实体范围 + /// + /// 获取实体集合的范围 + /// + /// 实体迭代器 + /// 实体集合的范围 + public static Extents3d GetExtents(this IEnumerable ents) + { + var ext = new Extents3d(); + foreach (var item in ents) + { + ext.AddExtents(item.GeometricExtents); + } + return ext; + } + #endregion + + #region 单行文字 + + /// + /// 更正单行文字的镜像属性 + /// + /// 单行文字 + public static void ValidateMirror(this DBText txt) + { + if (!txt.Database.Mirrtext) + { + txt.IsMirroredInX = false; + txt.IsMirroredInY = false; + } + } + #endregion + + #region 多行文字 + + /// + /// 炸散多行文字 + /// + /// 存储多行文字炸散之后的对象的类型 + /// 多行文字 + /// 存储对象变量 + /// 回调函数,用于处理炸散之后的对象 + /// + /// 多行文字炸散后的对象
    + /// 回调函数处理的结果 + ///
    + /// + public static void ExplodeFragments(this MText mt, T obj, Func mTextFragmentCallback) + { + mt.ExplodeFragments((f, o) => mTextFragmentCallback(f, (T)o), obj); + } + + /// + /// 获取多行文字的无格式文本 + /// + /// 多行文字 + /// 文本 + public static string GetUnFormatString(this MText mt) + { + List strs = new(); + mt.ExplodeFragments( + strs, + (f, o) => { + o.Add(f.Text); + return MTextFragmentCallbackStatus.Continue; + }); + return string.Join("", strs.ToArray()); + } + #endregion + + #region 圆弧 + + /// + /// 根据圆心、起点、终点来创建圆弧(二维) + /// + /// 起点 + /// 圆心 + /// 终点 + /// 圆弧 + public static Arc CreateArcSCE(Point3d startPoint, Point3d centerPoint, Point3d endPoint) + { + Arc arc = new(); + arc.SetDatabaseDefaults(); + arc.Center = centerPoint; + arc.Radius = centerPoint.DistanceTo(startPoint); + Vector2d startVector = new(startPoint.X - centerPoint.X, startPoint.Y - centerPoint.Y); + Vector2d endVector = new(endPoint.X - centerPoint.X, endPoint.Y - centerPoint.Y); + // 计算起始和终止角度 + arc.StartAngle = startVector.Angle; + arc.EndAngle = endVector.Angle; + return arc; + } + /// + /// 三点法创建圆弧(二维) + /// + /// 圆弧对象 + /// 起点 + /// 圆弧上的点 + /// 终点 + /// 圆弧 + public static Arc CreateArc(Point3d startPoint, Point3d pointOnArc, Point3d endPoint) + { + // 创建一个几何类的圆弧对象 + CircularArc3d geArc = new(startPoint, pointOnArc, endPoint); + // 将几何类圆弧对象的圆心和半径赋值给圆弧 +#if !gcad +#if NET35 + return (Arc)geArc.ToCurve(); +#else + return (Arc)Curve.CreateFromGeCurve(geArc); +#endif +#else + return (Arc)geArc.ToCurve(); +#endif + } + + /// + /// 根据起点、圆心和圆弧角度创建圆弧(二维) + /// + /// 圆弧对象 + /// 起点 + /// 圆心 + /// 圆弧角度 + /// 圆弧 + public static Arc CreateArc(Point3d startPoint, Point3d centerPoint, double angle) + { + Arc arc = new(); + arc.SetDatabaseDefaults(); + arc.Center = centerPoint; + arc.Radius = centerPoint.DistanceTo(startPoint); + Vector2d startVector = new(startPoint.X - centerPoint.X, startPoint.Y - centerPoint.Y); + arc.StartAngle = startVector.Angle; + arc.EndAngle = startVector.Angle + angle; + return arc; + } + + #endregion + + #region 圆 + + /// + /// 两点创建圆(两点中点为圆心) + /// + /// 起点 + /// 终点 + /// + public static Circle CreateCircle(Point3d startPoint, Point3d endPoint) + { + Circle circle = new(); + circle.SetDatabaseDefaults(); + circle.Center = startPoint.GetMidPointTo(endPoint); + circle.Radius = startPoint.DistanceTo(endPoint) * 0.5; + return circle; + } + + /// + /// 三点法创建圆(失败则返回Null) + /// + /// 第一点 + /// 第二点 + /// 第三点 + /// + public static Circle? CreateCircle(Point3d pt1, Point3d pt2, Point3d pt3) + { + // 先判断三点是否共线,得到pt1点指向pt2、pt2点的矢量 + Vector3d va = pt1.GetVectorTo(pt2); + Vector3d vb = pt1.GetVectorTo(pt3); + // 如两矢量夹角为0或180度(π弧度),则三点共线. + if (va.GetAngleTo(vb) == 0 | va.GetAngleTo(vb) == Math.PI) + return null; + + // 创建一个几何类的圆弧对象 + CircularArc3d geArc = new(pt1, pt2, pt3); + geArc.ToCircle(); + return geArc.ToCircle(); + } + + /// + /// 通过圆心,半径绘制圆形 + /// + /// 圆心 + /// 半径 + /// 图形的ObjectId + public static Circle? CreateCircle(Point3d center, double radius, double vex = 0, double vey = 0, double vez = 1) + { + return new Circle(center, new Vector3d(vex, vey, vez), radius);// 平面法向量XY方向 + } + + #endregion + + #region 块参照 + + #region 裁剪块参照 + + private const string filterDictName = "ACAD_FILTER"; + private const string spatialName = "SPATIAL"; + + /// + /// 裁剪块参照 + /// + /// 块参照 + /// 裁剪多边形点表 + public static void ClipBlockRef(this BlockReference bref, IEnumerable pt3ds) + { + Matrix3d mat = bref.BlockTransform.Inverse(); + var pts = + pt3ds + .Select(p => p.TransformBy(mat)) + .Select(p => new Point2d(p.X, p.Y)) + .ToCollection(); + + SpatialFilterDefinition sfd = new(pts, Vector3d.ZAxis, 0.0, 0.0, 0.0, true); + using SpatialFilter sf = new() { Definition = sfd }; + var dict = bref.GetXDictionary()!.GetSubDictionary(true, new string[] { filterDictName })!; + dict.SetAt(spatialName, sf); + // SetToDictionary(dict, spatialName, sf); + } + + /// + /// 裁剪块参照 + /// + /// 块参照 + /// 第一角点 + /// 第二角点 + public static void ClipBlockRef(this BlockReference bref, Point3d pt1, Point3d pt2) + { + Matrix3d mat = bref.BlockTransform.Inverse(); + pt1 = pt1.TransformBy(mat); + pt2 = pt2.TransformBy(mat); + + Point2dCollection pts = new() + { + new Point2d(Math.Min(pt1.X, pt2.X), Math.Min(pt1.Y, pt2.Y)), + new Point2d(Math.Max(pt1.X, pt2.X), Math.Max(pt1.Y, pt2.Y)) + }; + + using SpatialFilter sf = new() + { + Definition = new(pts, Vector3d.ZAxis, 0.0, 0.0, 0.0, true) + }; + var dict = bref.GetXDictionary()! + .GetSubDictionary(true, new string[] { filterDictName })!; + dict.SetAt(spatialName, sf); + // SetToDictionary(dict, spatialName, sf); +#if !acad + pts.Dispose(); +#endif + } + #endregion + + #region 属性 + /// + /// 更新动态块属性值 + /// + /// 动态块 + /// 属性值字典 + public static void ChangeBlockProperty(this BlockReference blockReference, + Dictionary propertyNameValues) + { + if (!blockReference.IsDynamicBlock) + return; + + using (blockReference.ForWrite()) + { + foreach (DynamicBlockReferenceProperty item in blockReference.DynamicBlockReferencePropertyCollection) + if (propertyNameValues.ContainsKey(item.PropertyName)) + item.Value = propertyNameValues[item.PropertyName]; + } + } + #endregion + + /// + /// 获取图元包围盒 + /// + /// + /// 包围盒信息 + /// 异常: + /// 会将包围盒类型记录到所属路径中,以此查询 + public static BoundingInfo GetBoundingBoxEx(this Entity ent) + { + return EntityBoundingInfo.GetBoundingInfo(ent); + } + +#line hidden + /// + /// 遍历块内 + /// + /// + /// + /// + /// + [System.Diagnostics.DebuggerStepThrough] + public static void ForEach(this BlockReference brf, Action action, DBTrans? tr = null) + { + if (action == null) + throw new ArgumentNullException(nameof(action)); + + tr ??= DBTrans.Top; + var btr = tr.GetObject(brf.BlockTableRecord); + if (btr == null) + return; + btr.ForEach(action); + } + /// + /// 遍历块内 + /// + /// + /// + /// + /// + [System.Diagnostics.DebuggerStepThrough] + public static void ForEach(this BlockReference brf, Action action, DBTrans? tr = null) + { + if (action == null) + throw new ArgumentNullException(nameof(action)); + + tr ??= DBTrans.Top; + var btr = tr.GetObject(brf.BlockTableRecord); + if (btr == null) + return; + btr.ForEach(action); + } + /// + /// 遍历块内 + /// + /// + /// + /// + /// + [System.Diagnostics.DebuggerStepThrough] + public static void ForEach(this BlockReference brf, Action action, DBTrans? tr = null) + { + if (action == null) + throw new ArgumentNullException(nameof(action)); + + tr ??= DBTrans.Top; + var btr = tr.GetObject(brf.BlockTableRecord); + if (btr == null) + return; + btr.ForEach(action); + } +#line default + + #endregion +} \ No newline at end of file diff --git a/src/CAD/IFox.CAD.Shared/ExtensionMethod/Enums.cs b/src/CAD/IFox.CAD.Shared/ExtensionMethod/Enums.cs new file mode 100644 index 0000000..d100ded --- /dev/null +++ b/src/CAD/IFox.CAD.Shared/ExtensionMethod/Enums.cs @@ -0,0 +1,174 @@ +namespace IFoxCAD.Cad; + +/// +/// 参照路径转换 +/// +public enum PathConverterModes : byte +{ + /// + /// 相对路径 + /// + Relative, + /// + /// 绝对路径 + /// + Complete +} + +/// +/// 参照绑定 +/// +public enum XrefModes : byte +{ + /// + /// 卸载 + /// + Unload, + /// + /// 重载 + /// + Reload, + /// + /// 拆离 + /// + Detach, + /// + /// 绑定 + /// + Bind, +} + +public enum SymModes : ushort +{ + /// + /// 块表 + /// + BlockTable = 1, + + /// + /// 图层表 + /// + LayerTable = 2, + /// + /// 文字样式表 + /// + TextStyleTable = 4, + /// + /// 注册应用程序表 + /// + RegAppTable = 8, + /// + /// 标注样式表 + /// + DimStyleTable = 16, + /// + /// 线型表 + /// + LinetypeTable = 32, + Option1 = LayerTable | TextStyleTable | DimStyleTable | LinetypeTable | RegAppTable, + + /// + /// 用户坐标系表 + /// + UcsTable = 64, + /// + /// 视图表 + /// + ViewTable = 128, + /// + /// 视口表 + /// + ViewportTable = 256, + Option2 = UcsTable | ViewTable | ViewportTable, + + // 全部 + All = BlockTable | Option1 | Option2 +} + + +/// +/// 坐标系类型枚举 +/// +public enum CoordinateSystemCode +{ + /// + /// 世界坐标系 + /// + Wcs = 0, + + /// + /// 用户坐标系 + /// + Ucs, + + /// + /// 模型空间坐标系 + /// + MDcs, + + /// + /// 图纸空间坐标系 + /// + PDcs +} + +/// +/// 方向的枚举 +/// +public enum OrientationType +{ + /// + /// 左转或逆时针 + /// + CounterClockWise, + /// + /// 右转或顺时针 + /// + ClockWise, + /// + /// 重合或平行 + /// + Parallel +} + +/// +/// 点与多边形的关系类型枚举 +/// +public enum PointOnRegionType +{ + /// + /// 多边形内部 + /// + Inside, + + /// + /// 多边形上 + /// + On, + + /// + /// 多边形外 + /// + Outside, + + /// + /// 错误 + /// + Error +} + + + +public enum FontTTF +{ + [Description("宋体.ttf")] + 宋体, + [Description("simfang.ttf")] + 仿宋, + [Description("FSGB2312.ttf")] + 仿宋GB2312, + [Description("Arial.ttf")] + Arial, + [Description("Romans")] + Romans +} \ No newline at end of file diff --git a/src/CAD/IFox.CAD.Shared/ExtensionMethod/Filer/DwgFiler.cs b/src/CAD/IFox.CAD.Shared/ExtensionMethod/Filer/DwgFiler.cs new file mode 100644 index 0000000..6820f37 --- /dev/null +++ b/src/CAD/IFox.CAD.Shared/ExtensionMethod/Filer/DwgFiler.cs @@ -0,0 +1,506 @@ +namespace IFoxCAD.Cad; + +/* + Arx自定义实体类,加 读函数(assertReadEnabled)和写函数(assertWriteEnabled) + + 所有属性位置都不要改动位置,因为涉及序列化 + [Serializable] 设置类 可以序列化 + [Newtonsoft.Json.JsonIgnore] 设置成员 不可序列化 +*/ + +#if NewtonsoftJson +[JsonConverter(typeof(ObjectIdConverter))] +#endif +[Serializable] +public class DwgFiler : Cad_DwgFiler +{ +#if NET35 + public int m_Position; +#else + public long m_Position; +#endif + public FilerType m_FilerType; + public Cad_ErrorStatus m_FilerStatus; + public List AddressList; + public int AddressListPt = 0; + public List BinaryChunkList; + public int BinaryChunkListPt = 0; + public List BooleanList; + public int BooleanListPt = 0; + public List ByteList; + public int ByteListPt = 0; + public List BytesList; + public int BytesListPt = 0; + public List DoubleList; + public int DoubleListPt = 0; + public List HandleList; + public int HandleListPt = 0; + //[NonSerialized] + //[ScriptIgnore] + public List HardOwnershipIdList; + public int HardOwnershipIdListPt = 0; + //[NonSerialized] + //[ScriptIgnore] + public List HardPointerIdList; + public int HardPointerIdListPt = 0; + public List Int16List; + public int Int16ListPt = 0; + public List Int32List; + public int Int32ListPt = 0; +#if !NET35 + public List Int64List; + public int Int64ListPt = 0; +#endif + public List Point2dList; + public int Point2dListPt = 0; + public List Point3dList; + public int Point3dListPt = 0; + public List Scale3dList; + public int Scale3dListPt = 0; + //[NonSerialized] + //[ScriptIgnore] + public List SoftOwnershipIdList; + public int SoftOwnershipIdListPt = 0; + //[NonSerialized] + //[ScriptIgnore] + public List SoftPointerIdList; + public int SoftPointerIdListPt = 0; + public List StringList; + public int StringListPt = 0; + public List Uint16List; + public int uint16ListPt = 0; + public List Uint32List; + public int uint32ListPt = 0; +#if !NET35 + public List Uint64List; + public int uint64ListPt = 0; +#endif + public List Vector2dList; + public int Vector2dListPt = 0; + public List Vector3dList; + public int Vector3dListPt = 0; + + public DwgFiler() + { + m_Position = 0; + m_FilerType = FilerType.CopyFiler; + m_FilerStatus = Cad_ErrorStatus.OK; + AddressList = new(); + BinaryChunkList = new(); + BooleanList = new(); + ByteList = new(); + BytesList = new(); + DoubleList = new(); + HandleList = new(); + HardOwnershipIdList = new(); + HardPointerIdList = new(); + Int16List = new(); + Int32List = new(); +#if !NET35 + Int64List = new(); +#endif + Point2dList = new(); + Point3dList = new(); + Scale3dList = new(); + SoftOwnershipIdList = new(); + SoftPointerIdList = new(); + StringList = new(); + Uint16List = new(); + Uint32List = new(); +#if !NET35 + Uint64List = new(); +#endif + Vector2dList = new(); + Vector3dList = new(); + } + +#if NET35 + public override int Position => m_Position; +#else + public override long Position => m_Position; +#endif + public override FilerType FilerType => m_FilerType; + + public override Cad_ErrorStatus FilerStatus + { + get { return m_FilerStatus; } + set { m_FilerStatus = value; } + } + + public override IntPtr ReadAddress() + { + if (AddressList.Count == 0) + return new(); + return AddressList[AddressListPt++]; + } + + public override byte[]? ReadBinaryChunk() + { + if (BinaryChunkList.Count == 0) + return null; + return BinaryChunkList[BinaryChunkListPt++]; + } + + public override bool ReadBoolean() + { + if (BooleanList.Count == 0) + return false; + return BooleanList[BooleanListPt++]; + } + + public override byte ReadByte() + { + if (ByteList.Count == 0) + return 0; + return ByteList[ByteListPt++]; + } + + public override void ReadBytes(byte[] value) + { + if (ByteList.Count == 0) + return; + value = new byte[BytesList[BytesListPt].Length]; + BytesList[BytesListPt++].CopyTo(value, 0); + } + + public override double ReadDouble() + { + if (DoubleList.Count == 0) + return 0; + return DoubleList[DoubleListPt++]; + } + + public override Handle ReadHandle() + { + if (HandleList.Count == 0) + return new(); + return HandleList[HandleListPt++]; + } + + public override ObjectId ReadHardOwnershipId() + { + if (HardOwnershipIdList.Count == 0) + return new(); + return HardOwnershipIdList[HardOwnershipIdListPt++]; + } + + public override ObjectId ReadHardPointerId() + { + if (HardPointerIdList.Count == 0) + return new(); + return HardPointerIdList[HardPointerIdListPt++]; + } + + public override short ReadInt16() + { + if (Int16List.Count == 0) + return 0; + return Int16List[Int16ListPt++]; + } + + public override int ReadInt32() + { + if (Int32List.Count == 0) + return 0; + return Int32List[Int32ListPt++]; + } + +#if !NET35 + public override long ReadInt64() + { + if (Int64List.Count == 0) + return 0; + return Int64List[Int64ListPt++]; + } +#endif + + public override Point2d ReadPoint2d() + { + if (Point2dList.Count == 0) + return new(); + return Point2dList[Point2dListPt++]; + } + + public override Point3d ReadPoint3d() + { + if (Point3dList.Count == 0) + return new(); + return Point3dList[Point3dListPt++]; + } + + public override Scale3d ReadScale3d() + { + if (Scale3dList.Count == 0) + return new(); + return Scale3dList[Scale3dListPt++]; + } + + public override ObjectId ReadSoftOwnershipId() + { + if (SoftOwnershipIdList.Count == 0) + return new(); + return SoftOwnershipIdList[SoftOwnershipIdListPt++]; + } + + public override ObjectId ReadSoftPointerId() + { + if (SoftPointerIdList.Count == 0) + return new(); + return SoftPointerIdList[SoftPointerIdListPt++]; + } + + public override string? ReadString() + { + if (StringList.Count == 0) + return null; + return StringList[StringListPt++]; + } + + public override ushort ReadUInt16() + { + if (Uint16List.Count == 0) + return 0; + return Uint16List[uint16ListPt++]; + } + + public override uint ReadUInt32() + { + if (Uint32List.Count == 0) + return 0; + return Uint32List[uint32ListPt++]; + } + +#if !NET35 + public override ulong ReadUInt64() + { + if (Uint64List.Count == 0) + return 0; + return Uint64List[uint64ListPt++]; + } +#endif + + public override Vector2d ReadVector2d() + { + if (Vector2dList.Count == 0) + return new(); + return Vector2dList[Vector2dListPt++]; + } + + public override Vector3d ReadVector3d() + { + if (Vector3dList.Count == 0) + return new(); + return Vector3dList[Vector3dListPt++]; + } + + public override void ResetFilerStatus() + { + AddressList.Clear(); + AddressListPt = 0; + BinaryChunkList.Clear(); + BinaryChunkListPt = 0; + BooleanList.Clear(); + BooleanListPt = 0; + ByteList.Clear(); + ByteListPt = 0; + BytesList.Clear(); + BytesListPt = 0; + DoubleList.Clear(); + DoubleListPt = 0; + HandleList.Clear(); + HandleListPt = 0; + HardOwnershipIdList.Clear(); + HardOwnershipIdListPt = 0; + HardPointerIdList.Clear(); + HardPointerIdListPt = 0; + Int16List.Clear(); + Int16ListPt = 0; + Int32List.Clear(); + Int32ListPt = 0; +#if !NET35 + Int64List.Clear(); + Int64ListPt = 0; +#endif + Point2dList.Clear(); + Point2dListPt = 0; + Point3dList.Clear(); + Point3dListPt = 0; + Scale3dList.Clear(); + Scale3dListPt = 0; + SoftOwnershipIdList.Clear(); + SoftOwnershipIdListPt = 0; + SoftPointerIdList.Clear(); + SoftPointerIdListPt = 0; + StringList.Clear(); + StringListPt = 0; + Uint16List.Clear(); + uint16ListPt = 0; + Uint32List.Clear(); + uint32ListPt = 0; +#if !NET35 + Uint64List.Clear(); + uint64ListPt = 0; +#endif + Vector2dList.Clear(); + Vector2dListPt = 0; + Vector3dList.Clear(); + Vector3dListPt = 0; + + m_FilerType = FilerType.CopyFiler; + } + +#if zcad // 中望官方的问题 + public override void Seek(int offset, int method) + { + var ed = Acap.DocumentManager.MdiActiveDocument.Editor; + ed.WriteMessage(MethodInfo.GetCurrentMethod().Name + " = " + " \n "); + } + public override void Seek(long offset, int method) + { + Seek((int)offset, method); + } +#endif + +#if acad || gcad + public override void Seek( +#if NET35 + int +#else + long +#endif + offset, int method) + { + var ed = Acap.DocumentManager.MdiActiveDocument.Editor; + ed.WriteMessage(MethodInfo.GetCurrentMethod().Name + " = " + " \n "); + } +#endif + + public override void WriteAddress(IntPtr value) + { + AddressList.Add(value); + } + + public override void WriteBinaryChunk(byte[] chunk) + { + BinaryChunkList.Add(chunk); + } + + public override void WriteBoolean(bool value) + { + BooleanList.Add(value); + } + + public override void WriteByte(byte value) + { + ByteList.Add(value); + } + + public override void WriteBytes(byte[] value) + { + BytesList.Add(value); + } + + public override void WriteDouble(double value) + { + DoubleList.Add(value); + } + + public override void WriteHandle(Handle handle) + { + HandleList.Add(handle); + } + + public override void WriteHardOwnershipId(ObjectId value) + { + HardOwnershipIdList.Add(value); + } + + public override void WriteHardPointerId(ObjectId value) + { + HardPointerIdList.Add(value); + } + + public override void WriteInt16(short value) + { + Int16List.Add(value); + } + + public override void WriteInt32(int value) + { + Int32List.Add(value); + } + +#if !NET35 + public override void WriteInt64(long value) + { + Int64List.Add(value); + } +#endif + public override void WritePoint2d(Point2d value) + { + Point2dList.Add(value); + } + + public override void WritePoint3d(Point3d value) + { + Point3dList.Add(value); + } + + public override void WriteScale3d(Scale3d value) + { + Scale3dList.Add(value); + } + + public override void WriteSoftOwnershipId(ObjectId value) + { + SoftOwnershipIdList.Add(value); + } + + public override void WriteSoftPointerId(ObjectId value) + { + SoftPointerIdList.Add(value); + } + + public override void WriteString(string value) + { + StringList.Add(value); + } + + public override void WriteUInt16(ushort value) + { + Uint16List.Add(value); + } + + public override void WriteUInt32(uint value) + { + Uint32List.Add(value); + } + +#if !NET35 + public override void WriteUInt64(ulong value) + { + Uint64List.Add(value); + } +#endif + + public override void WriteVector2d(Vector2d value) + { + Vector2dList.Add(value); + } + + public override void WriteVector3d(Vector3d value) + { + Vector3dList.Add(value); + } + + public override string ToString() + { +#if NewtonsoftJson + return JsonConvert.SerializeObject(this, Formatting.Indented); +#else + JavaScriptSerializer serializer = new(); + serializer.RegisterConverters(new[] { new ObjectIdConverter() }); + return serializer.Serialize(this); +#endif + } +} \ No newline at end of file diff --git a/src/CAD/IFox.CAD.Shared/ExtensionMethod/Filer/DwgFilerEx.cs b/src/CAD/IFox.CAD.Shared/ExtensionMethod/Filer/DwgFilerEx.cs new file mode 100644 index 0000000..f91fb4c --- /dev/null +++ b/src/CAD/IFox.CAD.Shared/ExtensionMethod/Filer/DwgFilerEx.cs @@ -0,0 +1,87 @@ +namespace IFoxCAD.Cad; + +/// +/// Dwg序列化 +/// +public class DwgFilerEx +{ + #region 成员 + DBObject? _entity; + public DwgFiler DwgFiler { get; private set; } + #endregion + + #region 构造 + /// + /// Dwg序列化 + /// + public DwgFilerEx(DwgFiler? Cad_DwgFiler = null) + { + if (Cad_DwgFiler == null) + Cad_DwgFiler = new(); + DwgFiler = Cad_DwgFiler; + } + + /// + /// Dwg序列化 + /// + public DwgFilerEx(DBObject entity) : this() + { + DwgOut(entity); + } + + #endregion + + #region 方法 + public void DwgOut(DBObject entity) + { + _entity = entity; + _entity.DwgOut(DwgFiler); + } + + public void DwgIn() + { + _entity?.DwgIn(DwgFiler); + } + + /// + /// 反序列化 + /// + /// + /// + public static DwgFilerEx? DeserializeObject(string json) + { +#if NewtonsoftJson + return JsonConvert.DeserializeObject(json); +#else + JavaScriptSerializer serializer = new(); + serializer.RegisterConverters(new[] { new ObjectIdConverter() }); + return serializer.Deserialize(json); +#endif + } + + /// + /// 序列化 + /// + /// + public string SerializeObject() + { +#if NewtonsoftJson + return JsonConvert.SerializeObject(DwgFiler, Formatting.Indented); +#else + JavaScriptSerializer serializer = new(); + serializer.RegisterConverters(new[] { new ObjectIdConverter() }); + return serializer.Serialize(DwgFiler); +#endif + } + + public override string ToString() + { + return DwgFiler.ToString(); + } + + public static implicit operator Cad_DwgFiler(DwgFilerEx df) + { + return df.DwgFiler; + } + #endregion +} \ No newline at end of file diff --git a/src/CAD/IFox.CAD.Shared/ExtensionMethod/Filer/DxfFiler.cs b/src/CAD/IFox.CAD.Shared/ExtensionMethod/Filer/DxfFiler.cs new file mode 100644 index 0000000..975c505 --- /dev/null +++ b/src/CAD/IFox.CAD.Shared/ExtensionMethod/Filer/DxfFiler.cs @@ -0,0 +1,221 @@ +#if acad +namespace IFoxCAD.Cad; + +/* 此处暂未完成,无任何测试,尚且不知道怎么用 */ +using System.Runtime.Remoting; + +public class DxfFiler : Cad_DxfFiler +{ + public DxfFiler(IntPtr unmanagedPointer, [MarshalAs(UnmanagedType.U1)] bool autoDelete) : base(unmanagedPointer, autoDelete) + { + } + + public override bool IsModifyingExistingObject => base.IsModifyingExistingObject; + + public override double Thickness => base.Thickness; + + public override double Elevation => base.Elevation; + + public override bool AtEmbeddedObjectStart => base.AtEmbeddedObjectStart; + + public override bool AtEndOfObject => base.AtEndOfObject; + + public override bool AtExtendedData => base.AtExtendedData; + + public override bool AtEndOfFile => base.AtEndOfFile; + + public override int Precision { get => base.Precision; set => base.Precision = value; } + + public override string ErrorMessage => base.ErrorMessage; + + public override bool AtSubclassData(string value) + { + return base.AtSubclassData(value); + } + + public override object Clone() + { + return base.Clone(); + } + + public override void CopyFrom(RXObject source) + { + base.CopyFrom(source); + } + + public override ObjRef CreateObjRef(Type requestedType) + { + return base.CreateObjRef(requestedType); + } + + public override bool Equals(object obj) + { + return base.Equals(obj); + } + + public override void FilerStatus() + { + base.FilerStatus(); + } + + public override int GetHashCode() + { + return base.GetHashCode(); + } + + public override void HaltAtClassBoundaries(bool value) + { + base.HaltAtClassBoundaries(value); + } + + public override object InitializeLifetimeService() + { + return base.InitializeLifetimeService(); + } + + public override void PushBackItem() + { + base.PushBackItem(); + } + + public override ResultBuffer ReadResultBuffer() + { + return base.ReadResultBuffer(); + } + + public override void ResetFilerStatus() + { + base.ResetFilerStatus(); + } + + public override int RewindFiler() + { + return base.RewindFiler(); + } + + public override void SetError(string format, params string[] values) + { + base.SetError(format, values); + } + + public override void SetError(Cad_ErrorStatus value, string format, params string[] values) + { + base.SetError(value, format, values); + } + + public override string ToString() + { + return base.ToString(); + } + + public override void WriteBool(DxfCode opCode, bool value) + { + base.WriteBool(opCode, value); + } + + public override void WriteBoolean(DxfCode opCode, bool value) + { + base.WriteBoolean(opCode, value); + } + + public override void WriteByte(DxfCode opCode, byte value) + { + base.WriteByte(opCode, value); + } + + public override void WriteBytes(DxfCode opCode, byte[] chunk) + { + base.WriteBytes(opCode, chunk); + } + + public override void WriteDouble(DxfCode opCode, double value, int precision) + { + base.WriteDouble(opCode, value, precision); + } + + public override void WriteEmbeddedObjectStart() + { + base.WriteEmbeddedObjectStart(); + } + + public override void WriteHandle(DxfCode opCode, Handle value) + { + base.WriteHandle(opCode, value); + } + + public override void WriteInt16(DxfCode opCode, short value) + { + base.WriteInt16(opCode, value); + } + + public override void WriteInt32(DxfCode opCode, int value) + { + base.WriteInt32(opCode, value); + } + + public override void WriteObjectId(DxfCode opCode, ObjectId value) + { + base.WriteObjectId(opCode, value); + } + + public override void WritePoint2d(DxfCode opCode, Point2d value, int precision) + { + base.WritePoint2d(opCode, value, precision); + } + + public override void WritePoint3d(DxfCode opCode, Point3d value, int precision) + { + base.WritePoint3d(opCode, value, precision); + } + + public override void WriteResultBuffer(ResultBuffer buffer) + { + base.WriteResultBuffer(buffer); + } + + public override void WriteScale3d(DxfCode opCode, Scale3d value, int precision) + { + base.WriteScale3d(opCode, value, precision); + } + + public override void WriteString(DxfCode opCode, string value) + { + base.WriteString(opCode, value); + } + + public override void WriteUInt16(DxfCode opCode, ushort value) + { + base.WriteUInt16(opCode, value); + } + + public override void WriteUInt32(DxfCode opCode, uint value) + { + base.WriteUInt32(opCode, value); + } + + public override void WriteVector2d(DxfCode opCode, Vector2d value, int precision) + { + base.WriteVector2d(opCode, value, precision); + } + + public override void WriteVector3d(DxfCode opCode, Vector3d value, int precision) + { + base.WriteVector3d(opCode, value, precision); + } + + public override void WriteXDataStart() + { + base.WriteXDataStart(); + } + + protected override void DeleteUnmanagedObject() + { + base.DeleteUnmanagedObject(); + } + + protected override void Dispose(bool disposing) + { + base.Dispose(disposing); + } +} +#endif \ No newline at end of file diff --git a/src/CAD/IFox.CAD.Shared/ExtensionMethod/GeometryEx.cs b/src/CAD/IFox.CAD.Shared/ExtensionMethod/GeometryEx.cs new file mode 100644 index 0000000..58e9da3 --- /dev/null +++ b/src/CAD/IFox.CAD.Shared/ExtensionMethod/GeometryEx.cs @@ -0,0 +1,671 @@ +namespace IFoxCAD.Cad; + +using System.Drawing; + +/// +/// 图形扩展类 +/// +public static class GeometryEx +{ + #region Point&Circle + + /// + /// 判断点与多边形的关系 + /// + /// 多边形顶点集合 + /// 点 + /// 点与多边形的关系 + public static PointOnRegionType PointOnRegion(this IEnumerable pts, Point2d pt) + { + // 遍历点集并生成首尾连接的多边形 + var ptlst = new LoopList(pts); + if (ptlst.Count < 3) + return PointOnRegionType.Error; + + var ls2ds = new List(); + foreach (var node in ptlst.GetNodes()) + { + ls2ds.Add(new LineSegment2d(node.Value, node.Next!.Value)); + } + var cc2d = new CompositeCurve2d(ls2ds.ToArray()); + + // 在多边形上? + if (cc2d.IsOn(pt)) + return PointOnRegionType.On; + + // 在最小包围矩形外? + var bb2d = cc2d.BoundBlock; + if (!bb2d.Contains(pt)) + return PointOnRegionType.Outside; + + // + bool flag = false; + foreach (var node in ptlst.GetNodes()) + { + var pt1 = node.Value; + var pt2 = node.Next!.Value; + if (pt.Y < pt1.Y && pt.Y < pt2.Y) + continue; + if (pt1.X < pt.X && pt2.X < pt.X) + continue; + Vector2d vec = pt2 - pt1; + double t = (pt.X - pt1.X) / vec.X; + double y = t * vec.Y + pt1.Y; + if (y < pt.Y && t >= 0 && t <= 1) + flag = !flag; + } + return + flag ? + PointOnRegionType.Inside : PointOnRegionType.Outside; + } + + /// + /// 判断点与多边形的关系 + /// + /// 多边形顶点集合 + /// 点 + /// 点与多边形的关系 + public static PointOnRegionType PointOnRegion(this IEnumerable pts, Point3d pt) + { + // 遍历点集并生成首尾连接的多边形 + var ptlst = new LoopList(pts); + if (ptlst.First!.Value == ptlst.Last!.Value) + ptlst.RemoveLast(); + if (ptlst.Count < 3) + return PointOnRegionType.Error; + + var ls3ds = new List(); + foreach (var node in ptlst.GetNodes()) + ls3ds.Add(new LineSegment3d(node.Value, node.Next!.Value)); + var cc3d = new CompositeCurve3d(ls3ds.ToArray()); + + // 在多边形上? + if (cc3d.IsOn(pt)) + return PointOnRegionType.On; + + // 在最小包围矩形外? + var bb2d = cc3d.BoundBlock; + if (!bb2d.Contains(pt)) + return PointOnRegionType.Outside; + + // + bool flag = false; + foreach (var node in ptlst.GetNodes()) + { + var pt1 = node.Value; + var pt2 = node.Next!.Value; + if (pt.Y < pt1.Y && pt.Y < pt2.Y) + continue; + if (pt1.X < pt.X && pt2.X < pt.X) + continue; + Vector3d vec = pt2 - pt1; + double t = (pt.X - pt1.X) / vec.X; + double y = t * vec.Y + pt1.Y; + if (y < pt.Y && t >= 0 && t <= 1) + flag = !flag; + } + return + flag ? + PointOnRegionType.Inside : PointOnRegionType.Outside; + } + + /// + /// 按两点返回最小包围圆 + /// + /// 基准点 + /// 基准点 + /// 输出圆上的点 + /// 解析类圆对象 + public static CircularArc2d GetMinCircle(Point2d pt1, Point2d pt2, out LoopList ptlst) + { + ptlst = new LoopList { pt1, pt2 }; + return + new CircularArc2d + ( + (pt1 + pt2.GetAsVector()) / 2, + pt1.GetDistanceTo(pt2) / 2 + ); + } + + /// + /// 按三点返回最小包围圆 + /// + /// 基准点 + /// 基准点 + /// 基准点 + /// 输出圆上的点 + /// 解析类圆对象 + public static CircularArc2d GetMinCircle(Point2d pt1, Point2d pt2, Point2d pt3, out LoopList ptlst) + { + ptlst = new LoopList { pt1, pt2, pt3 }; + + // 遍历各点与下一点的向量长度,找到距离最大的两个点 + LoopListNode maxNode = + ptlst.GetNodes().FindByMax + ( + out double maxLength, + node => node.Value.GetDistanceTo(node.Next!.Value) + ); + + // 以两点做最小包围圆 + CircularArc2d ca2d = + GetMinCircle(maxNode.Value, maxNode.Next!.Value, out LoopList tptlst); + + // 如果另一点属于该圆 + if (ca2d.IsIn(maxNode.Previous!.Value)) + { + // 返回 + ptlst = tptlst; + return ca2d; + } + // 否则按三点做圆 + // ptlst.SetFirst(maxNode); + ptlst = new LoopList { maxNode.Value, maxNode.Next.Value, maxNode.Previous.Value }; + ca2d = new CircularArc2d(pt1, pt2, pt3); + ca2d.SetAngles(0, Math.PI * 2); + return ca2d; + } + + /// + /// 按四点返回最小包围圆 + /// + /// 基准点 + /// 基准点 + /// 基准点 + /// 基准点 + /// 输出圆上的点 + /// 解析类圆对象 + public static CircularArc2d? GetMinCircle(Point2d pt1, Point2d pt2, Point2d pt3, Point2d pt4, out LoopList? ptlst) + { + var iniptlst = new LoopList() { pt1, pt2, pt3, pt4 }; + ptlst = null; + CircularArc2d? ca2d = null; + + // 遍历C43的组合,环链表的优势在这里 + foreach (var firstNode in iniptlst.GetNodes()) + { + // 获取各组合下三点的最小包围圆 + var secondNode = firstNode.Next; + var thirdNode = secondNode!.Next; + var tca2d = GetMinCircle(firstNode.Value, secondNode.Value, thirdNode!.Value, out LoopList tptlst); + + // 如果另一点属于该圆,并且半径小于当前值就把它做为候选解 + if (!tca2d.IsIn(firstNode.Previous!.Value)) + continue; + if (ca2d is null || tca2d.Radius < ca2d.Radius) + { + ca2d = tca2d; + ptlst = tptlst; + } + } + + // 返回直径最小的圆 + return ca2d; + } + + /// + /// 计算三点围成的有向面积 + /// + /// 基准点 + /// 第一点 + /// 第二点 + /// 三点围成的三角形的有向面积 + private static double CalArea(Point2d ptBase, Point2d pt1, Point2d pt2) + { + return (pt2 - ptBase).DotProduct((pt1 - ptBase).GetPerpendicularVector()) * 0.5; + } + /// + /// 计算三点围成的三角形的真实面积 + /// + /// 基准点 + /// 第一点 + /// 第二点 + /// 三点围成的三角形的真实面积 + public static double GetArea(this Point2d ptBase, Point2d pt1, Point2d pt2) + { + return Math.Abs(CalArea(ptBase, pt1, pt2)); + } + + /// + /// 判断三点是否为逆时针,也就是说判断三点是否为左转 + /// + /// 基点 + /// 第一点 + /// 第二点 + /// OrientationType 类型值 + public static OrientationType IsClockWise(this Point2d ptBase, Point2d pt1, Point2d pt2) + { + return CalArea(ptBase, pt1, pt2) switch + { + > 0 => OrientationType.CounterClockWise, + < 0 => OrientationType.ClockWise, + _ => OrientationType.Parallel + }; + } + + /// + /// 计算两个二维向量围成的平行四边形的有向面积 + /// + /// 基向量 + /// 向量 + /// 有向面积 + private static double CalArea(Vector2d vecBase, Vector2d vec) + { + return vec.DotProduct(vecBase.GetPerpendicularVector()) / 2; + } + + /// + /// 计算两个二维向量围成的平行四边形的真实面积 + /// + /// 基向量 + /// 向量 + /// 真实面积 + public static double GetArea(Vector2d vecBase, Vector2d vec) + { + return Math.Abs(CalArea(vecBase, vec)); + } + + /// + /// 判断两个二维向量是否左转 + /// + /// 基向量 + /// 向量 + /// OrientationType 类型值 + public static OrientationType IsClockWise(Vector2d vecBase, Vector2d vec) + { + return CalArea(vecBase, vec) switch + { + > 0 => OrientationType.CounterClockWise, + < 0 => OrientationType.ClockWise, + _ => OrientationType.Parallel + }; + } + + #region PointList + + /// + /// 计算点集的有向面积 + /// + /// 点集 + /// 有向面积 + private static double CalArea(IEnumerable pnts) + { + var itor = pnts.GetEnumerator(); + if (!itor.MoveNext()) + throw new ArgumentNullException(nameof(pnts)); + var start = itor.Current; + Point2d p1, p2 = start; + double area = 0; + + while (itor.MoveNext()) + { + p1 = p2; + p2 = itor.Current; + area += (p1.X * p2.Y - p2.X * p1.Y); + } + + area = (area + (p2.X * start.Y - start.X * p2.Y)) / 2.0; + return area; + } + /// + /// 计算点集的真实面积 + /// + /// 点集 + /// 面积 + public static double GetArea(this IEnumerable pnts) + { + return Math.Abs(CalArea(pnts)); + } + + /// + /// 判断点集的点序 + /// + /// 点集 + /// OrientationType 类型值 + public static OrientationType IsClockWise(this IEnumerable pnts) + { + return CalArea(pnts) switch + { + < 0 => OrientationType.ClockWise, + > 0 => OrientationType.CounterClockWise, + _ => OrientationType.Parallel + }; + } + + /// + /// 按点集返回最小包围圆 + /// + /// 点集 + /// 输出圆上的点 + /// 解析类圆对象 + public static CircularArc2d? GetMinCircle(this List pnts, out LoopList? ptlst) + { + // 点数较小时直接返回 + switch (pnts.Count) + { + case 0: + ptlst = null; + return null; + + case 1: + ptlst = new LoopList { pnts[0] }; + return new CircularArc2d(pnts[0], 0); + + case 2: + return GetMinCircle(pnts[0], pnts[1], out ptlst); + + case 3: + return GetMinCircle(pnts[0], pnts[1], pnts[2], out ptlst); + + case 4: + return GetMinCircle(pnts[0], pnts[1], pnts[2], pnts[3], out ptlst); + } + + // 按前三点计算最小包围圆 + var tpnts = new Point2d[4]; + pnts.CopyTo(0, tpnts, 0, 3); + var ca2d = GetMinCircle(tpnts[0], tpnts[1], tpnts[2], out ptlst); + + // 找到点集中距离圆心的最远点为第四点 + tpnts[3] = pnts.FindByMax(pnt => ca2d.Center.GetDistanceTo(pnt)); + + // 如果最远点属于圆结束 + while (!ca2d.IsIn(tpnts[3])) + { + // 如果最远点不属于圆,按此四点计算最小包围圆 + ca2d = GetMinCircle(tpnts[0], tpnts[1], tpnts[2], tpnts[3], out ptlst); + + // 将结果作为新的前三点 + if (ptlst!.Count == 3) + { + tpnts[2] = ptlst.Last!.Value; + } + else + { + // 第三点取另两点中距离圆心较远的点 + // 按算法中描述的任选其中一点的话,还是无法收敛...... + tpnts[2] = + tpnts.Except(ptlst) + .FindByMax(pnt => ca2d!.Center.GetDistanceTo(pnt)); + } + tpnts[0] = ptlst.First!.Value; + tpnts[1] = ptlst.First.Next!.Value; + + // 按此三点计算最小包围圆 + ca2d = GetMinCircle(tpnts[0], tpnts[1], tpnts[2], out ptlst); + + // 找到点集中圆心的最远点为第四点 + tpnts[3] = pnts.FindByMax(pnt => ca2d.Center.GetDistanceTo(pnt)); + } + + return ca2d; + } + + /// + /// 获取点集的凸包 + /// + /// 点集 + /// 凸包 + public static List? ConvexHull(this List points) + { + if (points is null) + return null; + + if (points.Count <= 1) + return points; + + int n = points.Count, k = 0; + List H = new(new Point2d[2 * n]); + + points.Sort((a, b) => + a.X == b.X ? a.Y.CompareTo(b.Y) : a.X.CompareTo(b.X)); + + // Build lower hull + for (int i = 0; i < n; ++i) + { + while (k >= 2 && IsClockWise(H[k - 2], H[k - 1], points[i]) == OrientationType.CounterClockWise) + k--; + H[k++] = points[i]; + } + + // Build upper hull + for (int i = n - 2, t = k + 1; i >= 0; i--) + { + while (k >= t && IsClockWise(H[k - 2], H[k - 1], points[i]) == OrientationType.CounterClockWise) + k--; + H[k++] = points[i]; + } + return H.Take(k - 1).ToList(); + } + + + #endregion PointList + + #endregion Point&Circle + + #region Ucs + + /// + /// ucs到wcs的点变换 + /// + /// 点 + /// 变换后的点 + public static Point3d Ucs2Wcs(this Point3d point) + { + return point.TransformBy(Env.Editor.CurrentUserCoordinateSystem); + } + + /// + /// wcs到ucs的点变换 + /// + /// 点 + /// 变换后的点 + public static Point3d Wcs2Ucs(this Point3d point) + { + return point.TransformBy(Env.Editor.CurrentUserCoordinateSystem.Inverse()); + } + + /// + /// ucs到wcs的向量变换 + /// + /// 向量 + /// 变换后的向量 + public static Vector3d Ucs2Wcs(this Vector3d vec) + { + return vec.TransformBy(Env.Editor.CurrentUserCoordinateSystem); + } + + /// + /// wcs到ucs的向量变换 + /// + /// 向量 + /// 变换后的向量 + public static Vector3d Wcs2Ucs(this Vector3d vec) + { + return vec.TransformBy(Env.Editor.CurrentUserCoordinateSystem.Inverse()); + } + + /// + /// 模拟 trans 函数 + /// + /// 点 + /// 源坐标系 + /// 目标坐标系 + /// 变换后的点 + public static Point3d Trans(this Point3d point, CoordinateSystemCode from, CoordinateSystemCode to) + { + return Env.Editor.GetMatrix(from, to) * point; + } + + /// + /// 模拟 trans 函数 + /// + /// 向量 + /// 源坐标系 + /// 目标坐标系 + /// 变换后的向量 + public static Vector3d Trans(this Vector3d vec, CoordinateSystemCode from, CoordinateSystemCode to) + { + return vec.TransformBy(Env.Editor.GetMatrix(from, to)); + } + + /// + /// wcs到dcs的点变换 + /// + /// 点 + /// 是否为图纸空间 + /// 变换后的点 + public static Point3d Wcs2Dcs(this Point3d point, bool atPaperSpace) + { + return + Trans( + point, + CoordinateSystemCode.Wcs, atPaperSpace ? CoordinateSystemCode.PDcs : CoordinateSystemCode.MDcs + ); + } + + /// + /// wcs到dcs的向量变换 + /// + /// 向量 + /// 是否为图纸空间 + /// 变换后的向量 + public static Vector3d Wcs2Dcs(this Vector3d vec, bool atPaperSpace) + { + return + Trans( + vec, + CoordinateSystemCode.Wcs, atPaperSpace ? CoordinateSystemCode.PDcs : CoordinateSystemCode.MDcs + ); + } + + #endregion Ucs + + + /// + /// 返回不等比例变换矩阵 + /// + /// 基点 + /// x方向比例 + /// y方向比例 + /// z方向比例 + /// 三维矩阵 + public static Matrix3d GetScaleMatrix(this Point3d point, double x, double y, double z) + { + double[] matdata = new double[16]; + matdata[0] = x; + matdata[3] = point.X * (1 - x); + matdata[5] = y; + matdata[7] = point.Y * (1 - y); + matdata[10] = z; + matdata[11] = point.Z * (1 - z); + matdata[15] = 1; + return new(matdata); + } + + /// + /// 获取坐标范围的大小 + /// + /// 坐标范围 + /// 尺寸对象 + public static Size GetSize(this Extents3d ext) + { + int width = (int)Math.Floor(ext.MaxPoint.X - ext.MinPoint.X); + int height = (int)Math.Ceiling(ext.MaxPoint.Y - ext.MinPoint.Y); + return new(width, height); + } + + /// + /// 将三维点转换为二维点 + /// + /// 三维点 + /// 二维点 + public static Point2d Point2d(this Point3d pt) + { + return new(pt.X, pt.Y); + } + /// + /// 将三维点集转换为二维点集 + /// + /// 三维点集 + /// 二维点集 + public static IEnumerable Point2d(this IEnumerable pts) + { + return pts.Select(pt => pt.Point2d()); + } + /// + /// 将二维点转换为三维点 + /// + /// 二维点 + /// Z值 + /// 三维点 + public static Point3d Point3d(this Point2d pt, double z = 0) + { + return new(pt.X, pt.Y, z); + } + + /// + /// 获取两个点之间的中点 + /// + /// 第一点 + /// 第二点 + /// 返回两个点之间的中点 + public static Point3d GetMidPointTo(this Point3d pt1, Point3d pt2) + { + return new(pt1.X * 0.5 + pt2.X * 0.5, + pt1.Y * 0.5 + pt2.Y * 0.5, + pt1.Z * 0.5 + pt2.Z * 0.5); + } + + /// + /// 获取两个点之间的中点 + /// + /// 第一点 + /// 第二点 + /// 返回两个点之间的中点 + public static Point2d GetMidPointTo(this Point2d pt1, Point2d pt2) + { + // (pt1 + pt2) / 2; // 溢出风险 + return new(pt1.X * 0.5 + pt2.X * 0.5, + pt1.Y * 0.5 + pt2.Y * 0.5); + } + + /// + /// 根据世界坐标计算用户坐标 + /// + /// 基点世界坐标 + /// 基点用户坐标 + /// 目标世界坐标 + /// 坐标网旋转角,按x轴正向逆时针弧度 + /// 目标用户坐标 + public static Point3d TransPoint(this Point3d basePt, Point3d userPt, Point3d transPt, double ang) + { + Matrix3d transMat = Matrix3d.Displacement(userPt - basePt); + Matrix3d roMat = Matrix3d.Rotation(-ang, Vector3d.ZAxis, userPt); + return transPt.TransformBy(roMat * transMat); + } + /// + /// 计算指定距离和角度的点 + /// + /// 本函数仅适用于x-y平面 + /// 基点 + /// 角度,x轴正向逆时针弧度 + /// 距离 + /// 目标点 + public static Point3d Polar(this Point3d pt, double ang, double len) + { + return pt + Vector3d.XAxis.RotateBy(ang, Vector3d.ZAxis) * len; + } + /// + /// 计算指定距离和角度的点 + /// + /// 本函数仅适用于x-y平面 + /// 基点 + /// 角度,x轴正向逆时针弧度 + /// 距离 + /// 目标点 + public static Point2d Polar(this Point2d pt, double ang, double len) + { + return pt + Vector2d.XAxis.RotateBy(ang) * len; + } +} \ No newline at end of file diff --git a/src/CAD/IFox.CAD.Shared/ExtensionMethod/JigEx.cs b/src/CAD/IFox.CAD.Shared/ExtensionMethod/JigEx.cs new file mode 100644 index 0000000..5b6d69c --- /dev/null +++ b/src/CAD/IFox.CAD.Shared/ExtensionMethod/JigEx.cs @@ -0,0 +1,407 @@ +namespace IFoxCAD.Cad; + +/* 封装jig + * 20220726 隐藏事件,利用函数进行数据库图元重绘 + * 20220710 修改SetOption()的空格结束,并添加例子到IFox + * 20220503 cad22需要防止刷新过程中更改队列,是因为允许函数重入导致,08不会有. + * 20220326 重绘图元的函数用错了,现在修正过来 + * 20211216 加入块表时候做一个差集,剔除临时图元 + * 20211209 补充正交变量设置和回收设置 + * 作者: 惊惊⎛⎝◕⏝⏝◕。⎠⎞ ⎛⎝≥⏝⏝0⎠⎞ ⎛⎝⓿⏝⏝⓿。⎠⎞ ⎛⎝≥⏝⏝≤⎠⎞ + * 博客: https://www.cnblogs.com/JJBox/p/15650770.html + */ + +public delegate void WorldDrawEvent(WorldDraw draw); +public class JigEx : DrawJig, IDisposable +{ + #region 成员 + /// + /// 事件:亮显/暗显会被刷新冲刷掉,所以这个事件用于补充非刷新的工作 + /// + event WorldDrawEvent? WorldDrawEvent; + /// + /// 最后的鼠标点,用来确认长度 + /// + public Point3d MousePointWcsLast; + /// + /// 最后的图元,用来生成 + /// + public Entity[] Entitys => _drawEntitys.ToArray(); + + + readonly Action>? _mouseAction; + readonly Tolerance _tolerance;// 容差 + + readonly Queue _drawEntitys;// 重复生成的图元,放在这里刷新 + JigPromptPointOptions? _options;// jig鼠标配置 + bool _worldDrawFlag = false; // 20220503 + + bool _systemVariables_Orthomode = false; + bool SystemVariables_Orthomode // 正交修改还原 + { + get => _systemVariables_Orthomode; + set + { + // 1正交,0非正交 // setvar: https://www.cnblogs.com/JJBox/p/10209541.html + if (Env.OrthoMode != value) + Env.OrthoMode = _systemVariables_Orthomode = value; + } + } + #endregion + + #region 构造 + /// + /// 在界面绘制图元 + /// + JigEx() + { + _drawEntitys = new(); + DimensionEntitys = new(); + } + + /// + /// 在界面绘制图元 + /// + /// + /// 用来频繁执行的回调:
    + /// 鼠标点;
    + /// 加入新建的图元,鼠标采样期间会Dispose图元的;
    + /// 所以已经在数据库图元利用事件加入,不要在此加入;
    + /// + /// 鼠标移动的容差 + public JigEx(Action>? action = null, double tolerance = 1e-6) : this() + { + _mouseAction = action; + _tolerance = new(tolerance, tolerance); + } + #endregion + + #region 重写 + /// + /// 鼠标采样器 + /// + /// + /// 返回状态:令频繁刷新结束 + protected override SamplerStatus Sampler(JigPrompts prompts) + { + if (_worldDrawFlag) + return SamplerStatus.NoChange;// OK的时候拖动鼠标与否都不出现图元 + + if (_options is null) + throw new NullReferenceException(nameof(_options)); + + var pro = prompts.AcquirePoint(_options); + if (pro.Status == PromptStatus.Keyword) + return SamplerStatus.OK; + else if (pro.Status != PromptStatus.OK) + return SamplerStatus.Cancel; + + // 上次鼠标点不同(一定要这句,不然图元刷新太快会看到奇怪的边线) + var mousePointWcs = pro.Value; + + // == 是比较类字段,但是最好转为哈希比较. + // IsEqualTo 是方形判断(仅加法),但是cad是距离. + // Distance 是圆形判断(会求平方根,使用了牛顿迭代), + // 大量数据(十万以上/频繁刷新)面前会显得非常慢. + if (mousePointWcs.IsEqualTo(MousePointWcsLast, _tolerance)) + return SamplerStatus.NoChange; + + // 上次循环的缓冲区图元清理,否则将会在vs输出遗忘 Dispose + while (_drawEntitys.Count > 0) + _drawEntitys.Dequeue().Dispose(); + + // 委托把容器扔出去接收新创建的图元,然后给重绘更新 + _mouseAction?.Invoke(mousePointWcs, _drawEntitys); + MousePointWcsLast = mousePointWcs; + + return SamplerStatus.OK; + } + + /// + /// 重绘已在数据库的图元 + /// + /// 0x01 此处不加入newEntity的,它们在构造函数的参数回调处加入,它们会进行频繁new和Dispose从而避免遗忘释放
    + /// 0x02 此处用于重绘已经在数据的图元
    + /// 0x03 此处用于图元亮显暗显,因为会被重绘冲刷掉所以独立出来不重绘,它们也往往已经存在数据库的 + ///
    + ///
    + /// + /// newEntity只会存在一个图元队列中,而数据库图元可以分多个集合 + /// 例如: 集合A亮显时 集合B暗显/集合B亮显时 集合A暗显,所以我没有设计多个"数据库图元集合"存放,而是由用户在构造函数外自行创建 + /// + /// + public void DatabaseEntityDraw(WorldDrawEvent action) + { + WorldDrawEvent = action; + } + + /* WorldDraw 封装外的操作说明: + * 0x01 + * 我有一个业务是一次性生成四个方向的箭头,因为cad08缺少瞬时图元, + * 那么可以先提交一次事务,再开一个事务,把Entity传给jig,最后选择删除部分. + * 虽然这个是可行的方案,但是Entity穿越事务本身来说是非必要不使用的. + * 0x02 + * 四个箭头最近鼠标的亮显,其余淡显, + * 在jig使用淡显ent.Unhighlight和亮显ent.Highlight() + * 需要绕过重绘,否则重绘将导致图元频闪,令这两个操作失效, + * 此时需要自定义一个集合 EntityList (不使用本函数的_drawEntitys) + * 再将 EntityList 传给 WorldDrawEvent 事件,事件内实现亮显和淡显(事件已经利用 DatabaseEntityDraw函数进行提供). + * 0x03 + * draw.Geometry.Draw(_drawEntitys[i]); + * 此函数有问题,acad08克隆一份数组也可以用来刷新, + * 而arx上面的jig只能一次改一个,所以可以用此函数. + * 起因是此函数属于异步刷新, + * 同步上下文的刷新是 RawGeometry + * 0x04 + * cad22测试出现,08不会, + * draw.RawGeometry.Draw(ent);会跳到 Sampler(),所以设置 _worldDrawFlag + * 但是禁止重绘重入的话(令图元不频繁重绘),那么鼠标停着的时候就看不见图元, + * 所以只能重绘结束的时候才允许鼠标采集,采集过程的时候不会触发重绘, + * 这样才可以保证容器在重绘中不被更改. + */ + /// + /// 重绘图形 + /// + protected override bool WorldDraw(WorldDraw draw) + { + _worldDrawFlag = true; + WorldDrawEvent?.Invoke(draw); + _drawEntitys.ForEach(ent => { + draw.RawGeometry.Draw(ent); + }); + _worldDrawFlag = false; + return true; + } + #endregion + + #region 方法 + /// + /// 鼠标配置:基点 + /// + /// 基点 + /// 光标绑定 + /// 提示信息 + /// 正交开关 + public JigPromptPointOptions SetOptions(Point3d basePoint, + CursorType cursorType = CursorType.RubberBand, + string msg = "点选第二点", + bool orthomode = false) + { + if (orthomode) + SystemVariables_Orthomode = true; + + _options = JigPointOptions(); + _options.Message = Environment.NewLine + msg; + _options.Cursor = cursorType; // 光标绑定 + _options.UseBasePoint = true; // 基点打开 + _options.BasePoint = basePoint; // 基点设定 + return _options; + } + + /// + /// 鼠标配置:提示信息,关键字 + /// + /// 信息 + /// 关键字 + /// 正交开关 + /// + public JigPromptPointOptions SetOptions(string msg, + Dictionary? keywords = null, + bool orthomode = false) + { + if (orthomode) + SystemVariables_Orthomode = true; + + _options = JigPointOptions(); + _options.Message = Environment.NewLine + msg; + + // 加入关键字,加入时候将空格内容放到最后 + string spaceValue = string.Empty; + const string spaceKey = " "; + + if (keywords != null) + foreach (var item in keywords) + if (item.Key == spaceKey) + spaceValue = item.Value; + else + _options.Keywords.Add(item.Key, item.Key, item.Value); + + /// 因为默认配置函数导致此处空格触发是无效的, + /// 但是用户如果想触发,就需要在外部减去默认UserInputControls配置 + /// 要放最后,才能优先触发其他关键字 + if (spaceValue != string.Empty) + _options.Keywords.Add(spaceKey, spaceKey, spaceValue); + else + _options.Keywords.Add(spaceKey, spaceKey, "<空格退出>"); + + // 外部设置减去配置 + // _options.UserInputControls = + // _options.UserInputControls + // ^ UserInputControls.NullResponseAccepted // 输入了鼠标右键,结束jig + // ^ UserInputControls.AnyBlankTerminatesInput; // 空格或回车,结束jig; + return _options; + } + + /// + /// 鼠标配置:自定义 + /// + /// + /// 正交开关 + public void SetOptions(Action action, bool orthomode = false) + { + if (orthomode) + SystemVariables_Orthomode = true; + + _options = new JigPromptPointOptions(); + action.Invoke(_options); + } + + /// + /// 执行 + /// + /// + public PromptResult Drag() + { + // jig功能必然是当前前台文档,所以封装内部更好调用 + var dm = Acap.DocumentManager; + var doc = dm.MdiActiveDocument; + var ed = doc.Editor; + var dr = ed.Drag(this); + + if (SystemVariables_Orthomode) + SystemVariables_Orthomode = !SystemVariables_Orthomode; + return dr; + } + + /// + /// 最后一次的图元加入数据库 + /// + /// 加入此空间 + /// 不生成的图元用于排除,例如刷新时候的提示文字 + /// 加入数据库的id集合 + public IEnumerable? AddEntityToMsPs(BlockTableRecord btrOfAddEntitySpace, + IEnumerable? removeEntity = null) + { + // 内部用 _drawEntitys 外部用 Entitys,减少一层转换 + if (_drawEntitys.Count == 0) + return null; + + IEnumerable es = _drawEntitys; + if (removeEntity != null) + es = es.Except(removeEntity);// 差集 + + return btrOfAddEntitySpace.AddEntity(es); + } + + + #region 配置 + /// + /// 用户输入控制默认配置 + /// 令jig.Drag().Status == + /// + /// + static JigPromptPointOptions JigPointOptions() + { + return new JigPromptPointOptions() + { + UserInputControls = + UserInputControls.GovernedByUCSDetect // 由UCS探测用 + | UserInputControls.Accept3dCoordinates // 接受三维坐标 + | UserInputControls.NullResponseAccepted // 输入了鼠标右键,结束jig + | UserInputControls.AnyBlankTerminatesInput // 空格或回车,结束jig; + }; + } + + /// + /// 空格默认是, + /// 将它设置为 + /// + public void SetSpaceIsKeyword() + { + var opt = _options; + if (opt == null) + throw new ArgumentNullException(nameof(_options)); + + if ((opt.UserInputControls & UserInputControls.NullResponseAccepted) == UserInputControls.NullResponseAccepted) + opt.UserInputControls ^= UserInputControls.NullResponseAccepted; // 输入了鼠标右键,结束jig + if ((opt.UserInputControls & UserInputControls.AnyBlankTerminatesInput) == UserInputControls.AnyBlankTerminatesInput) + opt.UserInputControls ^= UserInputControls.AnyBlankTerminatesInput; // 空格或回车,结束jig + } + #endregion + + #region 注释数据 + /// + /// 注释数据,可以在缩放的时候不受影响 + /// + public DynamicDimensionDataCollection DimensionEntitys; + + /// + /// 重写注释数据 + /// + /// + /// + protected override DynamicDimensionDataCollection GetDynamicDimensionData(double dimScale) + { + base.GetDynamicDimensionData(dimScale); + return DimensionEntitys; + } + + #endregion + #endregion + + #region IDisposable接口相关函数 + public bool IsDisposed { get; private set; } = false; + + /// + /// 手动调用释放 + /// + public void Dispose() + { + Dispose(true); + GC.SuppressFinalize(this); + } + + /// + /// 析构函数调用释放 + /// + ~JigEx() + { + Dispose(false); + } + + protected virtual void Dispose(bool disposing) + { + // 不重复释放,并设置已经释放 + if (IsDisposed) return; + IsDisposed = true; + + // 最后一次的图元如果没有加入数据库,就在此销毁,所以JigEx调用的时候加using + _drawEntitys?.ForEach(ent => { + if (ent.Database == null && !ent.IsDisposed) + ent.Dispose(); + }); + _drawEntitys?.Clear(); + } + #endregion + + // 此处无测试 + // protected override void ViewportDraw(ViewportDraw draw) + // { + // base.ViewportDraw(draw); + // } +} + +#if false +| UserInputControls.DoNotEchoCancelForCtrlC // 不要取消CtrlC的回音 +| UserInputControls.DoNotUpdateLastPoint // 不要更新最后一点 +| UserInputControls.NoDwgLimitsChecking // 没有Dwg限制检查 +| UserInputControls.NoZeroResponseAccepted // 接受非零响应 +| UserInputControls.NoNegativeResponseAccepted // 不否定回复已被接受 +| UserInputControls.Accept3dCoordinates // 返回点的三维坐标,是转换坐标系了? +| UserInputControls.AcceptMouseUpAsPoint // 接受释放按键时的点而不是按下时 + +| UserInputControls.InitialBlankTerminatesInput // 初始 空格或回车,结束jig +| UserInputControls.AcceptOtherInputString // 接受其他输入字符串 +| UserInputControls.NoZDirectionOrtho // 无方向正射,直接输入数字时以基点到当前点作为方向 +| UserInputControls.UseBasePointElevation // 使用基点高程,基点的Z高度探测 +#endif \ No newline at end of file diff --git a/src/CAD/IFox.CAD.Shared/ExtensionMethod/JigExTransient.cs b/src/CAD/IFox.CAD.Shared/ExtensionMethod/JigExTransient.cs new file mode 100644 index 0000000..c7532b9 --- /dev/null +++ b/src/CAD/IFox.CAD.Shared/ExtensionMethod/JigExTransient.cs @@ -0,0 +1,149 @@ +#if acad && !ac2008 +namespace IFoxCAD.Cad; + +/// +/// 瞬态容器 +/// +public class JigExTransient : IDisposable +{ + #region 私有字段 + // 整数集,暂时不知道有什么意义 + IntegerCollection _integerCollection; + // 维护集合 + HashSet _entities; + #endregion + + #region 公开属性 + /// + /// 对象集合 + /// + public Entity[] Entities => _entities.ToArray(); + /// + /// 数量 + /// + public int Count => _entities.Count; + #endregion + + #region 构造函数 + /// + /// 瞬态容器 + /// + public JigExTransient() + { + _integerCollection = new(); + _entities = new(); + } + #endregion + + #region 方法 + /// + /// 判断瞬态容器里是否含有对象 + /// + /// 对象 + /// 含有返回true + public bool Contains(Entity ent) + { + return _entities.Contains(ent); + } + + /// + /// 向瞬态容器中添加对象 + /// + /// 图元 + /// 绘图模式 + public void Add(Entity ent, TransientDrawingMode tdm = TransientDrawingMode.Main) + { + if (_entities.Add(ent)) + { + TransientManager + .CurrentTransientManager + .AddTransient(ent, tdm, 128, _integerCollection); + } + } + + + /// + /// 从瞬态容器中移除图元 + /// + /// 已经加入瞬态容器的图元 + public void Remove(Entity ent) + { + if (!Contains(ent)) + return; + TransientManager + .CurrentTransientManager + .EraseTransient(ent, _integerCollection); + _entities.Remove(ent); + } + + /// + /// 清空瞬态容器并移除图元显示 + /// + public void Clear() + { + foreach (var ent in _entities) + { + TransientManager + .CurrentTransientManager + .EraseTransient(ent, _integerCollection); + } + _entities.Clear(); + } + + + /// + /// 更新单个显示 + /// + /// 已经加入瞬态容器的图元 + public void Update(Entity ent) + { + if (!Contains(ent)) + return; + TransientManager + .CurrentTransientManager + .UpdateTransient(ent, _integerCollection); + } + + /// + /// 更新全部显示 + /// + public void UpdateAll() + { + foreach (var ent in _entities) + Update(ent); + } + #endregion + + #region IDisposable接口相关函数 + public bool IsDisposed { get; private set; } = false; + + /// + /// 手动释放 + /// + public void Dispose() + { + Dispose(true); + GC.SuppressFinalize(this); + } + + /// + /// 析构函数调用释放 + /// + ~JigExTransient() + { + Dispose(false); + } + + /// + /// 销毁瞬态容器 + /// + protected virtual void Dispose(bool disposing) + { + if (IsDisposed) return; + IsDisposed = true; + + Clear();// 清空瞬态容器并移除对象在图纸上的显示 + } + #endregion +} +#endif \ No newline at end of file diff --git a/src/CAD/IFox.CAD.Shared/ExtensionMethod/JsonConverter.cs b/src/CAD/IFox.CAD.Shared/ExtensionMethod/JsonConverter.cs new file mode 100644 index 0000000..c1044a9 --- /dev/null +++ b/src/CAD/IFox.CAD.Shared/ExtensionMethod/JsonConverter.cs @@ -0,0 +1,116 @@ +namespace IFoxCAD.Cad; + +#if NewtonsoftJson +/* + * 参考 https://www.cnblogs.com/fps2tao/p/14798710.html + * json类型转换器,使用方法: + * 在类上面增加此特性: [JsonConverter(typeof(ObjectIdConverter))] + */ +/// +/// json转换器 +/// +public class ObjectIdConverter : JsonConverter +{ + /// + /// 约束类型 + /// + public override bool CanConvert(Type objectType) + { + return typeof(ObjectId) == objectType; + } + + /// + /// 反序列化_把字符串生成对象 + /// + public override object ReadJson(JsonReader reader, Type objectType, object? existingValue, JsonSerializer serializer) + { + if (reader.Value == null) + return ObjectId.Null; + try + { + using DBTrans tr = new(); + var id = tr.GetObjectId(reader.Value.ToString()); + return id; + } + catch { return ObjectId.Null; } + } + + /// + /// 序列化_写入json + /// + public override void WriteJson(JsonWriter writer, object? value, JsonSerializer serializer) + { + if (value is ObjectId id) + writer.WriteValue(id == ObjectId.Null ? 0 : id.Handle.Value); + } +} +#else +/* + * 参考 https://developer.aliyun.com/article/51053 + * json类型转换器,使用方法: + * public static string SerializeToJson(object obj) + * { + * JavaScriptSerializer serializer = new(); + * serializer.RegisterConverters(new[] { new ObjectIdConverter() }); + * return serializer.Serialize(obj); + * } + * + * public static T DeserializeJson(string jsonString) + * { + * JavaScriptSerializer serializer = new(); + * serializer.RegisterConverters(new[] { new ObjectIdConverter() }); + * return serializer.Deserialize(jsonString); + * } + */ +/// +/// json转换器 +/// +public class ObjectIdConverter : JavaScriptConverter +{ + const string _id = nameof(ObjectId); + + /// + /// 约束类型 + /// + public override IEnumerable SupportedTypes => new[] { typeof(ObjectId) }; + + /// + /// 序列化_写入json + /// + public override IDictionary Serialize(object obj, JavaScriptSerializer serializer) + { + if (obj is not ObjectId id) + return null!; + + Dictionary result = new() + { + { _id, id == ObjectId.Null ? 0 : id.Handle.Value } + }; + return result; + } + + /// + /// 反序列化_把字符串生成对象 + /// + public override object Deserialize(IDictionary dictionary, Type type, JavaScriptSerializer serializer) + { + if (dictionary == null) + throw new ArgumentNullException(nameof(dictionary)); + + if (type != typeof(ObjectId)) + return null!; + + ObjectId id = ObjectId.Null; + try + { + if (dictionary.TryGetValue(_id, out object va)) + { + using DBTrans tr = new(); + id = tr.GetObjectId(va.ToString()); + } + } + catch { } + return id; + } +} +#endif \ No newline at end of file diff --git a/src/CAD/IFox.CAD.Shared/ExtensionMethod/ObjEx.cs b/src/CAD/IFox.CAD.Shared/ExtensionMethod/ObjEx.cs new file mode 100644 index 0000000..2ccc389 --- /dev/null +++ b/src/CAD/IFox.CAD.Shared/ExtensionMethod/ObjEx.cs @@ -0,0 +1,21 @@ +namespace IFoxCAD.Cad; + +public static class ObjEx +{ + /// + /// cad的打印 + /// + /// + public static void Print(this object obj) + { + Acap.DocumentManager.MdiActiveDocument.Editor.WriteMessage($"{obj}\n"); + } + /// + /// 系统的打印 + /// + /// + public static void PrintLine(this object obj) + { + Console.WriteLine(obj.ToString()); + } +} \ No newline at end of file diff --git a/src/CAD/IFox.CAD.Shared/ExtensionMethod/ObjectIdEx.cs b/src/CAD/IFox.CAD.Shared/ExtensionMethod/ObjectIdEx.cs new file mode 100644 index 0000000..a329da5 --- /dev/null +++ b/src/CAD/IFox.CAD.Shared/ExtensionMethod/ObjectIdEx.cs @@ -0,0 +1,98 @@ +namespace IFoxCAD.Cad; + +/// +/// 对象id扩展类 +/// +public static class ObjectIdEx +{ + #region GetObject + /// + /// 获取指定类型对象 + /// + /// 指定的泛型 + /// 对象id + /// 打开模式 + /// 事务 + /// 是否打开已删除对象,默认为不打开 + /// 是否打开锁定图层对象,默认为不打开 + /// 指定类型对象 + public static T? GetObject(this ObjectId id, + OpenMode openMode = OpenMode.ForRead, + Transaction? trans = null, + bool openErased = false, + bool openLockedLayer = false) where T : DBObject + { + trans ??= DBTrans.Top.Transaction; + return trans.GetObject(id, openMode, openErased, openLockedLayer) as T; + } + + /// + /// 获取指定类型对象集合 + /// + /// 指定的泛型 + /// 对象id集合 + /// 打开模式 + /// 事务 + /// 是否打开已删除对象,默认为不打开 + /// 是否打开锁定图层对象,默认为不打开 + /// 指定类型对象集合 + [System.Diagnostics.DebuggerStepThrough] + public static IEnumerable GetObject(this IEnumerable ids, + OpenMode openMode = OpenMode.ForRead, + Transaction? trans = null, + bool openErased = false, + bool openLockedLayer = false) where T : DBObject + { + trans ??= DBTrans.Top.Transaction; + return ids.Select(id => id.GetObject(openMode, trans, openErased, openLockedLayer)); + } + + /// + /// 返回符合类型的对象id + /// + /// 对象类型 + /// 对象id集合 + /// 对象id集合 + public static IEnumerable OfType(this IEnumerable ids) where T : DBObject + { + string dxfName = RXClass.GetClass(typeof(T)).DxfName; + return ids.Where(id => id.ObjectClass().DxfName == dxfName); + } + #endregion GetObject + + public static RXClass ObjectClass(this ObjectId id) + { +#if NET35 + return RXClass.GetClass(id.GetType()); +#else + return id.ObjectClass; +#endif + } + + /// + /// id是否有效,未被删除 + /// + /// 对象id + /// id有效返回 ,反之返回 + public static bool IsOk(this ObjectId id) + { + return !id.IsNull && id.IsValid && !id.IsErased && !id.IsEffectivelyErased && id.IsResident; + } + + /// + /// 删除id代表的对象 + /// + /// 对象id + public static void Erase(this ObjectId id) + { + if (id.IsOk()) + { + var ent = id.GetObject()!; + using (ent.ForWrite()) + { + ent.Erase(); + }// 第一种读写权限自动转换写法 + // Env.Editor.Regen(); + } + } +} \ No newline at end of file diff --git a/src/CAD/IFox.CAD.Shared/ExtensionMethod/PointEx.cs b/src/CAD/IFox.CAD.Shared/ExtensionMethod/PointEx.cs new file mode 100644 index 0000000..99187e8 --- /dev/null +++ b/src/CAD/IFox.CAD.Shared/ExtensionMethod/PointEx.cs @@ -0,0 +1,138 @@ +namespace IFoxCAD.Cad; + +public static class PointEx +{ + /// + /// 获取点的hash字符串,同时可以作为pt的字符串表示 + /// + /// 点 + /// 指示计算几维坐标的标志,1为计算x,2为计算x,y,其他为计算x,y,z + /// 保留的小数位数 + /// hash字符串 + public static string GetHashString(this Point3d pt, int xyz = 3, int decimalRetain = 6) + { + var de = $"f{decimalRetain}"; + return xyz switch + { + 1 => $"({pt.X.ToString(de)})", + 2 => $"({pt.X.ToString(de)},{pt.Y.ToString(de)})", + _ => $"({pt.X.ToString(de)},{pt.Y.ToString(de)},{pt.Z.ToString(de)})" + }; + } + + // 为了频繁触发所以弄个缓存 + static Plane? _PlaneCache; + /// + /// 两点计算弧度范围0到2Pi + /// + /// 起点 + /// 终点 + /// 方向 + /// 弧度值 + public static double GetAngle(this Point3d startPoint, Point3d endPoint, Vector3d? direction = null) + { + if (direction != null) + _PlaneCache = new Plane(Point3d.Origin, direction.Value); + if (_PlaneCache == null) + _PlaneCache = new Plane(Point3d.Origin, Vector3d.ZAxis); + return startPoint.GetVectorTo(endPoint).AngleOnPlane(_PlaneCache); + } + /// + /// 两点计算弧度范围0到2Pi + /// + /// 起点 + /// 终点 + /// 弧度值 + public static double GetAngle(this Point2d startPoint, Point2d endPoint) + { + return startPoint.GetVectorTo(endPoint).Angle; + } + + /// + /// 获取中点 + /// + /// + /// + /// + public static Point2d GetCenter(this Point2d a, Point2d b) + { + // (p1 + p2) / 2; // 溢出风险 + return new Point2d(a.X * 0.5 + b.X * 0.5, + a.Y * 0.5 + b.Y * 0.5); + } + + /// http://www.lee-mac.com/bulgeconversion.html + /// + /// 求凸度,判断三点是否一条直线上 + /// + /// 圆弧起点 + /// 圆弧腰点 + /// 圆弧尾点 + /// 逆时针为正,顺时针为负 + public static double GetArcBulge(this Point2d arc1, Point2d arc2, Point2d arc3, double tol = 1e-10) + { + double dStartAngle = arc2.GetAngle(arc1); + double dEndAngle = arc2.GetAngle(arc3); + // 求的P1P2与P1P3夹角 + var talAngle = (Math.PI - dStartAngle + dEndAngle) / 2; + // 凸度==拱高/半弦长==拱高比值/半弦长比值 + // 有了比值就不需要拿到拱高值和半弦长值了,因为接下来是相除得凸度 + double bulge = Math.Sin(talAngle) / Math.Cos(talAngle); + + // 处理精度 + if (bulge > 0.9999 && bulge < 1.0001) + bulge = 1; + else if (bulge < -0.9999 && bulge > -1.0001) + bulge = -1; + else if (Math.Abs(bulge) < tol) + bulge = 0; + return bulge; + } + + + #region 首尾相连 + /// + /// 首尾相连 + /// + [System.Diagnostics.DebuggerStepThrough] + public static void End2End(this Point2dCollection ptcol) + { + if (ptcol == null) + throw new ArgumentNullException(nameof(ptcol)); + + if (ptcol.Count == 0 || ptcol[0].Equals(ptcol[^1]))// 首尾相同直接返回 + return; + + // 首尾不同,去加一个到最后 + var lst = new Point2d[ptcol.Count + 1]; + for (int i = 0; i < lst.Length; i++) + lst[i] = ptcol[i]; + lst[^1] = lst[0]; + + ptcol.Clear(); + ptcol.AddRange(lst); + } + /// + /// 首尾相连 + /// + [System.Diagnostics.DebuggerStepThrough] + public static void End2End(this Point3dCollection ptcol) + { + if (ptcol == null) + throw new ArgumentNullException(nameof(ptcol)); + + if (ptcol.Count == 0 || ptcol[0].Equals(ptcol[^1]))// 首尾相同直接返回 + return; + + // 首尾不同,去加一个到最后 + var lst = new Point3d[ptcol.Count + 1]; + for (int i = 0; i < lst.Length; i++) + lst[i] = ptcol[i]; + lst[^1] = lst[0]; + + ptcol.Clear(); + for (int i = 0; i < lst.Length; i++) + ptcol.Add(lst[i]); + } + #endregion +} \ No newline at end of file diff --git a/src/CAD/IFox.CAD.Shared/ExtensionMethod/SelectionSetEx.cs b/src/CAD/IFox.CAD.Shared/ExtensionMethod/SelectionSetEx.cs new file mode 100644 index 0000000..432ed01 --- /dev/null +++ b/src/CAD/IFox.CAD.Shared/ExtensionMethod/SelectionSetEx.cs @@ -0,0 +1,150 @@ +namespace IFoxCAD.Cad; + +/// +/// 选择集扩展类 +/// +public static class SelectionSetEx +{ + #region 获取对象id + /// + /// 获取已选择的对象 + /// + /// 选择集 + /// 已选择的对象集合 + [System.Diagnostics.DebuggerStepThrough] + public static IEnumerable GetSelectedObjects(this SelectionSet ss) + { + return ss.Cast(); + } + + /// + /// 获取已选择的对象 + /// + /// 已选择的对象泛型 + /// 选择集 + /// 已选择的对象集合 + [System.Diagnostics.DebuggerStepThrough] + public static IEnumerable GetSelectObjects(this SelectionSet ss) where T : SelectedObject + { + return ss.Cast().OfType(); + } + + /// + /// 从选择集中获取对象id + /// + /// 图元类型 + /// 选择集 + /// 已选择的对象id集合 + [System.Diagnostics.DebuggerStepThrough] + public static IEnumerable GetObjectIds(this SelectionSet ss) where T : Entity + { + string dxfName = RXClass.GetClass(typeof(T)).DxfName; + return + ss + .GetObjectIds() + .Where(id => id.ObjectClass().DxfName == dxfName); + } + + /// + /// 将选择集的对象按类型分组 + /// + /// 选择集 + /// 分组后的类型/对象id集合 + [System.Diagnostics.DebuggerStepThrough] + public static IEnumerable> GetObjectIdGroup(this SelectionSet ss) + { + return + ss + .GetObjectIds() + .GroupBy(id => id.ObjectClass().DxfName); + } + #endregion + + #region 获取实体对象 + + /// + /// 获取指定类型图元 + /// + /// 指定类型 + /// 选择集 + /// 打开模式 + /// 事务 + /// 是否打开已删除对象,默认为不打开 + /// 是否打开锁定图层对象,默认为不打开 + /// 图元集合 + [System.Diagnostics.DebuggerStepThrough] + public static IEnumerable GetEntities(this SelectionSet ss, + OpenMode openMode = OpenMode.ForRead, + DBTrans? trans = null, + bool openErased = false, + bool openLockedLayer = false) where T : Entity + { + if (ss is null) + throw new ArgumentNullException(nameof(ss)); + + trans ??= DBTrans.Top; + return ss.GetObjectIds() + .Select(id => trans.GetObject(id, openMode, openErased, openLockedLayer)) + .Where(ent => ent != null); + } + #endregion + + #region ForEach + /// + /// 遍历选择集 + /// + /// 指定图元类型 + /// 选择集 + /// 处理函数;(图元) + /// 打开模式 + /// 事务 + /// 是否打开已删除对象,默认为不打开 + /// 是否打开锁定图层对象,默认为不打开 + [System.Diagnostics.DebuggerStepThrough] + public static void ForEach(this SelectionSet ss, + Action action, + OpenMode openMode = OpenMode.ForRead, + DBTrans? tr = default, + bool openErased = false, + bool openLockedLayer = false) where T : Entity + { + ForEach(ss, (ent, state) => { + action.Invoke(ent); + }, openMode, tr, openErased, openLockedLayer); + } + + /// + /// 遍历选择集 + /// + /// 指定图元类型 + /// 选择集 + /// 处理函数;(图元,终止方式) + /// 打开模式 + /// 事务 + /// 是否打开已删除对象,默认为不打开 + /// 是否打开锁定图层对象,默认为不打开 + /// + [System.Diagnostics.DebuggerStepThrough] + public static void ForEach(this SelectionSet ss, + Action action, + OpenMode openMode = OpenMode.ForRead, + DBTrans? trans = null, + bool openErased = false, + bool openLockedLayer = false) where T : Entity + { + if (action == null) + throw new ArgumentNullException(nameof(action)); + + trans ??= DBTrans.Top; + + LoopState state = new(); + var ents = ss.GetEntities(openMode, trans, openErased, openLockedLayer); + foreach (var ent in ents) + { + action.Invoke(ent, state); + if (!state.IsRun) + break; + } + } + #endregion +} \ No newline at end of file diff --git a/src/CAD/IFox.CAD.Shared/ExtensionMethod/SymbolTableEx.cs b/src/CAD/IFox.CAD.Shared/ExtensionMethod/SymbolTableEx.cs new file mode 100644 index 0000000..4703c78 --- /dev/null +++ b/src/CAD/IFox.CAD.Shared/ExtensionMethod/SymbolTableEx.cs @@ -0,0 +1,358 @@ +namespace IFoxCAD.Cad; + +/// +/// 符号表类扩展函数 +/// +public static class SymbolTableEx +{ + #region 图层表 + /// + /// 添加图层 + /// + /// 图层符号表 + /// 图层名 + /// 图层颜色 + /// 图层id + public static ObjectId Add(this SymbolTable table, string name, Color color) + { + return table.Add(name, lt => lt.Color = color); + } + /// + /// 添加图层 + /// + /// 图层符号表 + /// 图层名 + /// 图层颜色索引值 + /// 图层id + public static ObjectId Add(this SymbolTable table, string name, int colorIndex) + { + colorIndex %= 256;// 防止输入的颜色超出256 + colorIndex = Math.Abs(colorIndex);// 防止负数 + return table.Add(name, lt => lt.Color = Color.FromColorIndex(ColorMethod.ByColor, (short)colorIndex)); + } + /// + /// 更改图层名 + /// + /// 图层符号表 + /// 旧图层名 + /// 新图层名 + public static ObjectId Rename(this SymbolTable table, string Oldname, string NewName) + { + if (!table.Has(Oldname)) + return ObjectId.Null; + + table.Change(Oldname, layer => { + layer.Name = NewName; + }); + return table[NewName]; + } + /// + /// 删除图层 + /// + /// 层表 + /// 图层名 + /// 成功返回 ,失败返回 + public static bool Delete(this SymbolTable table, string name) + { + if (name == "0" || name == "Defpoints" || !table.Has(name) || table[name] == DBTrans.Top.Database.Clayer) + return false; + + table.CurrentSymbolTable.GenerateUsageData(); + var ltr = table.GetRecord(name); + if (ltr is null) + return false; + + if (ltr.IsUsed) + return false; + using (ltr.ForWrite()) + ltr.Erase(); + return true; + } + #endregion + + #region 块表 + /// + /// 添加块定义 + /// + /// 块表 + /// 块名 + /// 对所添加块表的委托n + /// 添加图元的委托 + /// 添加属性定义的委托 + /// 块定义id + /// TODO: 需要测试匿名块等特殊的块是否能定义 + public static ObjectId Add(this SymbolTable table, + string name, + Action? action = null, + Func>? ents = null, + Func>? attdef = null) + { + return table.Add(name, btr => { + action?.Invoke(btr); + + var entsres = ents?.Invoke(); + if (entsres is not null) + btr.AddEntity(entsres); + + var adddefres = attdef?.Invoke(); + if (adddefres is not null) + btr.AddEntity(adddefres); + }); + } + /// + /// 添加块定义 + /// + /// 块表 + /// 块名 + /// 图元 + /// 属性定义 + /// + public static ObjectId Add(this SymbolTable table, + string name, + IEnumerable? ents = null, + IEnumerable? attdef = null) + { + return table.Add(name, btr => { + if (ents is not null) + btr.AddEntity(ents); + if (attdef is not null) + btr.AddEntity(attdef); + }); + } + + /// + /// 添加块定义 + /// + /// 块表 + /// 块名 + /// 图元(包括属性) + /// + public static ObjectId Add(this SymbolTable table, string name, params Entity[] ents) + { + return table.Add(name, null, () => { return ents; }); + } + + /// + /// 添加属性到块定义 + /// + /// 块表 + /// 块定义id + /// 属性列表 + public static void AddAttsToBlocks(this SymbolTable table, + ObjectId id, + List atts) + { + List attTags = new(); + table.Change(id, btr => { + + btr.GetEntities() + .ForEach(def => attTags.Add(def.Tag.ToUpper())); + + for (int i = 0; i < atts.Count; i++) + if (!attTags.Contains(atts[i].Tag.ToUpper())) + btr.AddEntity(atts[i]); + }); + } + /// + /// 添加属性到块定义 + /// + /// 块表 + /// 块定义名字 + /// 属性列表 + public static void AddAttsToBlocks(this SymbolTable table, + string name, + List atts) + { + List attTags = new(); + table.Change(name, btr => { + + btr.GetEntities() + .ForEach(def => attTags.Add(def.Tag.ToUpper())); + + for (int i = 0; i < atts.Count; i++) + if (!attTags.Contains(atts[i].Tag.ToUpper())) + btr.AddEntity(atts[i]); + }); + } + + /// + /// 从文件中获取块定义 + /// + /// 块表 + /// 文件名 + /// 是否覆盖 + /// 块定义Id + public static ObjectId GetBlockFrom(this SymbolTable table, string fileName, bool over) + { + // FileInfo fi = new(fileName); + // string blkdefname = fi.Name; + // if (blkdefname.Contains(".")) + // blkdefname = blkdefname.Substring(0, blkdefname.LastIndexOf('.')); + + string blkdefname = SymbolUtilityServices.RepairSymbolName( + SymbolUtilityServices.GetSymbolNameFromPathName(fileName, "dwg"), false); + + ObjectId id = table[blkdefname]; + bool has = id != ObjectId.Null; + if ((has && over) || !has) + { + Database db = new(false, true); + db.ReadDwgFile(fileName, FileShare.Read, true, null); + db.CloseInput(true); + id = table.Database.Insert(BlockTableRecord.ModelSpace, blkdefname, db, false); + } + + return id; + } + + + + /// + /// 从文件中获取块定义 + /// + /// 块表 + /// 文件名 + /// 块定义名 + /// 是否覆盖 + /// 块定义Id + public static ObjectId GetBlockFrom(this SymbolTable table, + string fileName, + string blockName, + bool over) + { + return table.GetRecordFrom(t => t.BlockTable, fileName, blockName, over); + } + #endregion + + + #region 线型表 + /// + /// 添加线型 + /// + /// 线型表 + /// 线型名 + /// 线型说明 + /// 线型长度 + /// 笔画长度数组 + /// 线型id + public static ObjectId Add(this SymbolTable table, string name, string description, double length, double[] dash) + { + return table.Add( + name, + ltt => { + ltt.AsciiDescription = description; + ltt.PatternLength = length; // 线型的总长度 + ltt.NumDashes = dash.Length; // 组成线型的笔画数目 + for (int i = 0; i < dash.Length; i++) + { + ltt.SetDashLengthAt(i, dash[i]); + } + // ltt.SetDashLengthAt(0, 0.5); // 0.5个单位的划线 + // ltt.SetDashLengthAt(1, -0.25); // 0.25个单位的空格 + // ltt.SetDashLengthAt(2, 0); // 一个点 + // ltt.SetDashLengthAt(3, -0.25); // 0.25个单位的空格 + } + ); + } + #endregion + + #region 文字样式表 + /// + /// 添加文字样式记录 + /// + /// 文字样式表 + /// 文字样式名 + /// 字体名 + /// 宽度比例 + /// 文字样式Id + public static ObjectId Add(this SymbolTable table, + string textStyleName, + string font, + double xscale = 1.0) + { + return + table.Add( + textStyleName, + tstr => { + tstr.Name = textStyleName; + tstr.FileName = font; + tstr.XScale = xscale; + }); + } + /// + /// 添加文字样式记录 + /// + /// 文字样式表 + /// 文字样式名 + /// 字体名枚举 + /// 宽度比例 + /// 文字样式Id + public static ObjectId Add(this SymbolTable table, + string textStyleName, + FontTTF fontTTF, + double xscale = 1.0) + { + return table.Add(textStyleName, fontTTF.GetDesc(), xscale); + } + + /// + ///

    添加文字样式记录,如果存在就默认强制替换

    + /// 此函数为了 而设 + ///
    + /// 文字样式表 + /// 文字样式名 + /// 字体名 + /// 大字体名 + /// 宽度比例 + /// 高度 + /// 是否强制替换 + /// 文字样式Id + public static ObjectId AddWithChange(this SymbolTable table, + string textStyleName, + string smallFont, + string bigFont = "", + double xScale = 1, + double height = 0, + bool forceChange = true) + { + if (forceChange && table.Has(textStyleName)) + { + table.Change(textStyleName, ttr => { + ttr.FileName = smallFont; + ttr.XScale = xScale; + ttr.TextSize = height; + if (bigFont != string.Empty) + ttr.BigFontFileName = bigFont; + }); + return table[textStyleName]; + } + return table.Add(textStyleName, ttr => { + ttr.FileName = smallFont; + ttr.XScale = xScale; + ttr.TextSize = height; + }); + } + + + #endregion + + #region 注册应用程序表 + + #endregion + + #region 标注样式表 + + #endregion + + #region 用户坐标系表 + + #endregion + + #region 视图表 + + #endregion + + #region 视口表 + + #endregion +} diff --git a/src/CAD/IFox.CAD.Shared/ExtensionMethod/SymbolTableRecordEx.cs b/src/CAD/IFox.CAD.Shared/ExtensionMethod/SymbolTableRecordEx.cs new file mode 100644 index 0000000..ecd1e31 --- /dev/null +++ b/src/CAD/IFox.CAD.Shared/ExtensionMethod/SymbolTableRecordEx.cs @@ -0,0 +1,521 @@ +namespace IFoxCAD.Cad; + +/// +/// 符号表记录扩展类 +/// +public static class SymbolTableRecordEx +{ + #region 块表记录 + + #region 克隆实体id + /// + /// 深度克隆id到块表记录 + /// + /// 0x01 此方法不允许是未添加数据库的图元,因此它是id
    + /// 0x02 若为未添加数据库图元,则利用entity.Clone();同时不需要考虑动态块属性,可以使用entity.GetTransformedCopy + ///
    + ///
    + /// + /// + /// 克隆到当前块表记录,相当于原地克隆
    + /// 克隆到目标块表记录内,相当于制作新块 + ///
    + /// + /// 图元id集合,注意所有成员都要在同一个空间中 + /// 返回克隆后的id词典 + public static void DeepCloneEx(this BlockTableRecord btr, ObjectIdCollection objIds, IdMapping maoOut) + { + if (objIds is null || objIds.Count == 0) + throw new ArgumentNullException(nameof(objIds)); + + var db = objIds[0].Database; + using (btr.ForWrite()) + { + try + { + db.DeepCloneObjects(objIds, btr.ObjectId, maoOut, false); + + // 不在此提取,为了此函数被高频调用 + // 获取克隆键值对(旧块名,新块名) + // foreach (ObjectId item in blockIds) + // result.Add(mapping[item].Value); + } + catch (System.Exception e) + { + LogHelper.FlagOutVsOutput = true; + e.WriteLog("深度克隆出错了"); + } + } + } + + /// + /// 克隆图元实体(这个函数有问题,会出现偶尔成功,偶尔失败,拖动过变成匿名块) + /// 若为块则进行设置属性,因此控制动态块属性丢失; + /// + /// 图元 + /// 矩阵 + // public static void EntityTransformedCopy(this Entity ent, Matrix3d matrix) + // { + // var entNew = ent.GetTransformedCopy(matrix); + // if (ent is BlockReference blockReference) + // entNew.SetPropertiesFrom(blockReference); + // } + + #endregion + + #region 添加实体 + /// + /// 添加实体对象 + /// + /// 块表记录 + /// 实体 + /// 事务管理器 + /// 对象 id + public static ObjectId AddEntity(this BlockTableRecord btr, Entity entity, + Transaction? trans = null) + { + // if (entity is null) + // throw new ArgumentNullException(nameof(entity), "对象为 null"); + + ObjectId id; + trans ??= DBTrans.Top.Transaction; + using (btr.ForWrite()) + { + id = btr.AppendEntity(entity); + trans.AddNewlyCreatedDBObject(entity, true); + } + return id; + } + + /// + /// 添加实体集合 + /// + /// 实体类型 + /// 块表记录 + /// 实体集合 + /// 事务 + /// 对象 id 列表 + public static IEnumerable AddEntity(this BlockTableRecord btr, IEnumerable ents, + Transaction? trans = null) where T : Entity + { + // if (ents.Any(ent => ent is null)) + // throw new ArgumentNullException(nameof(ents), "实体集合内存在 null 对象"); + + trans ??= DBTrans.Top.Transaction; + using (btr.ForWrite()) + { + return ents.Select(ent => { + ObjectId id = btr.AppendEntity(ent); + trans.AddNewlyCreatedDBObject(ent, true); + return id; + }).ToList(); + } + } + + /// + /// 添加多个实体 + /// + /// 块表记录 + /// 实体集合 + /// 对象 id 列表 + public static IEnumerable AddEntity(this BlockTableRecord btr, params Entity[] ents) + { + return btr.AddEntity(ents, null); + } + #endregion + + #region 添加图元 + /// + /// 在指定绘图空间添加图元 + /// + /// 图元类型 + /// 绘图空间 + /// 图元对象 + /// 图元属性设置委托 + /// 事务管理器 + /// 图元id + private static ObjectId AddEnt(this BlockTableRecord btr, T ent, + Action? action, Transaction? trans = null) where T : Entity + { + action?.Invoke(ent); + return btr.AddEntity(ent, trans); + } + /// + /// 委托式的添加图元 + /// + /// 块表 + /// 返回图元的委托 + /// 事务 + /// 图元id,如果委托返回 null,则为 ObjectId.Null + public static ObjectId AddEnt(this BlockTableRecord btr, Func action, Transaction? trans = null) + { + var ent = action.Invoke(); + if (ent is null) + return ObjectId.Null; + + return btr.AddEntity(ent, trans); + } + + /// + /// 在指定绘图空间添加直线 + /// + /// 事务管理器 + /// 起点 + /// 终点 + /// 绘图空间 + /// 直线属性设置委托 + /// 直线的id + public static ObjectId AddLine(this BlockTableRecord btr, Point3d start, Point3d end, + Action? action = null, Transaction? trans = null) + { + var line = new Line(start, end); + return btr.AddEnt(line, action, trans); + } + /// + /// 在指定绘图空间X-Y平面添加圆 + /// + /// 绘图空间 + /// 圆心 + /// 半径 + /// 圆属性设置委托 + /// 事务管理器 + /// 圆的id + public static ObjectId AddCircle(this BlockTableRecord btr, Point3d center, double radius, + Action? action = null, Transaction? trans = null) + { + var circle = new Circle(center, Vector3d.ZAxis, radius); + return btr.AddEnt(circle, action, trans); + } + + /// + /// 在指定绘图空间X-Y平面3点画外接圆 + /// + /// 绘图空间 + /// 第一点 + /// 第二点 + /// 第三点 + /// 圆属性设置委托 + /// 事务管理器 + /// 三点有外接圆则返回圆的id,否则返回ObjectId.Null + public static ObjectId AddCircle(this BlockTableRecord btr, Point3d p0, Point3d p1, Point3d p2, + Action? action = null, Transaction? trans = null) + { + var circle = EntityEx.CreateCircle(p0, p1, p2); + // return circle is not null ? btr.AddEnt(circle, action, trans) : throw new ArgumentNullException(nameof(circle), "对象为 null"); + if (circle is null) + throw new ArgumentNullException(nameof(circle), "对象为 null"); + + return btr.AddEnt(circle, action, trans); + } + /// + /// 在指定的绘图空间添加轻多段线 + /// + /// 绘图空间 + /// 多段线信息 + /// 线宽 + /// 是否闭合 + /// 轻多段线属性设置委托 + /// 事务管理器 + /// 轻多段线id + public static ObjectId AddPline(this BlockTableRecord btr, + List bvws, + double? constantWidth = null, + bool isClosed = true, + Action? action = null, Transaction? trans = null) + { + Polyline pl = new(); + pl.SetDatabaseDefaults(); + if (constantWidth is not null) + { + for (int i = 0; i < bvws.Count; i++) + pl.AddVertexAt(i, bvws[i].Vertex, bvws[i].Bulge, constantWidth.Value, constantWidth.Value); + } + else + { + for (int i = 0; i < bvws.Count; i++) + pl.AddVertexAt(i, bvws[i].Vertex, bvws[i].Bulge, bvws[i].StartWidth, bvws[i].EndWidth); + } + pl.Closed = isClosed;// 闭合 + return btr.AddEnt(pl, action, trans); + } + /// + /// 在指定的绘图空间添加轻多段线 + /// + /// 绘图空间 + /// 端点表 + /// 凸度表 + /// 端点的起始宽度 + /// 端点的终止宽度 + /// 轻多段线属性设置委托 + /// 事务管理器 + /// 轻多段线id + public static ObjectId AddPline(this BlockTableRecord btr, + List pts, + List? bulges = null, + List? startWidths = null, + List? endWidths = null, + Action? action = null, + Transaction? trans = null) + { + bulges ??= new(new double[pts.Count]); + startWidths ??= new(new double[pts.Count]); + endWidths ??= new(new double[pts.Count]); + + Polyline pl = new(); + pl.SetDatabaseDefaults(); + + for (int i = 0; i < pts.Count; i++) + pl.AddVertexAt(i, pts[i].Point2d(), bulges[i], startWidths[i], endWidths[i]); + return btr.AddEnt(pl, action, trans); + } + + /// + /// 在指定的绘图空间添加轻多段线 + /// + /// 绘图空间 + /// 端点表,利用元组(Point3d pt, double bulge, double startWidth, double endWidth) + /// 轻多段线属性设置委托 + /// 事务管理器 + /// 轻多段线id + public static ObjectId AddPline(this BlockTableRecord btr, + List<(Point3d pt, double bulge, double startWidth, double endWidth)> pts, + Action? action = null, + Transaction? trans = null) + { + Polyline pl = new(); + pl.SetDatabaseDefaults(); + + pts.ForEach((vertex, state, index) => { + pl.AddVertexAt(index, vertex.pt.Point2d(), vertex.bulge, vertex.startWidth, vertex.endWidth); + }); + + return btr.AddEnt(pl, action, trans); + } + + /// + /// 在指定绘图空间X-Y平面3点画圆弧 + /// + /// 绘图空间 + /// 圆弧起点 + /// 圆弧上的点 + /// 圆弧终点 + /// 圆弧属性设置委托 + /// 事务管理器 + /// 圆弧id + public static ObjectId AddArc(this BlockTableRecord btr, + Point3d startPoint, Point3d pointOnArc, Point3d endPoint, + Action? action = null, Transaction? trans = null) + { + var arc = EntityEx.CreateArc(startPoint, pointOnArc, endPoint); + return btr.AddEnt(arc, action, trans); + } + #endregion + + #region 获取实体/实体id + /// + /// 获取块表记录内的指定类型的实体 + /// (此处不会检查id.IsOk()) + /// + /// 实体类型 + /// 块表记录 + /// 打开模式 + /// 事务 + /// 是否打开已删除对象,默认为不打开 + /// 是否打开锁定图层对象,默认为不打开 + /// 实体集合 + public static IEnumerable GetEntities(this BlockTableRecord btr, + OpenMode openMode = OpenMode.ForRead, + Transaction? trans = null, + bool openErased = false, + bool openLockedLayer = false) where T : Entity + { + trans ??= DBTrans.Top.Transaction; + return + btr + .Cast() + .Select(id => trans.GetObject(id, openMode, openErased, openLockedLayer)) + .OfType(); + } + + /// + /// 按类型获取实体Id + /// + /// 实体类型 + /// 块表记录 + /// 实体Id集合 + public static IEnumerable GetObjectIds(this BlockTableRecord btr) where T : Entity + { + string dxfName = RXClass.GetClass(typeof(T)).DxfName; + return btr.Cast() + .Where(id => id.ObjectClass()?.DxfName == dxfName); + } + + /// + /// 按类型获取实体Id的分组 + /// + /// 块表记录 + /// 实体Id分组 + public static IEnumerable> GetObjectIds(this BlockTableRecord btr) + { + return btr.Cast() + .GroupBy(id => id.ObjectClass().DxfName); + } + + + /// + /// 获取绘制顺序表 + /// + /// 块表 + /// 事务 + /// 是否打开已删除对象,默认为不打开 + /// 是否打开锁定图层对象,默认为不打开 + /// 绘制顺序表 + public static DrawOrderTable? GetDrawOrderTable(this BlockTableRecord btr, + Transaction? trans = null, + bool openErased = false, + bool openLockedLayer = false) + { + trans ??= DBTrans.Top.Transaction; + return trans.GetObject(btr.DrawOrderTableId, OpenMode.ForRead, + openErased, openLockedLayer) as DrawOrderTable; + } + #endregion + + #region 插入块参照 + /// + /// 插入块参照 + /// + /// 块表记录 + /// 插入点 + /// 块名 + /// 块插入比例,默认为1 + /// 块插入旋转角(弧度),默认为0 + /// 属性字典{Tag,Value},默认为null + /// 事务 + /// 块参照对象id + public static ObjectId InsertBlock(this BlockTableRecord blockTableRecord, Point3d position, + string blockName, + Scale3d scale = default, + double rotation = default, + Dictionary? atts = null, + Transaction? trans = null) + { + trans ??= DBTrans.Top.Transaction; + if (!DBTrans.Top.BlockTable.Has(blockName)) + { + DBTrans.Top.Editor?.WriteMessage($"\n不存在名字为{blockName}的块定义。"); + return ObjectId.Null; + } + return blockTableRecord.InsertBlock(position, DBTrans.Top.BlockTable[blockName], scale, rotation, atts, trans); + } + /// + /// 插入块参照 + /// + /// 插入点 + /// 块定义id + /// 块插入比例,默认为1 + /// 块插入旋转角(弧度),默认为0 + /// 属性字典{Tag,Value},默认为null + /// 块参照对象id + public static ObjectId InsertBlock(this BlockTableRecord blockTableRecord, + Point3d position, + ObjectId blockId, + Scale3d scale = default, + double rotation = default, + Dictionary? atts = null, + Transaction? trans = null) + { + trans ??= DBTrans.Top.Transaction; + if (!DBTrans.Top.BlockTable.Has(blockId)) + { + DBTrans.Top.Editor?.WriteMessage($"\n不存在块定义。"); + return ObjectId.Null; + } + using var blockref = new BlockReference(position, blockId) + { + ScaleFactors = scale, + Rotation = rotation + }; + var objid = blockTableRecord.AddEntity(blockref); + + if (atts != null) + { + var btr = DBTrans.Top.GetObject(blockref.BlockTableRecord)!; + if (btr.HasAttributeDefinitions) + { + var attdefs = btr.GetEntities(); + foreach (var attdef in attdefs) + { + using AttributeReference attref = new(); + attref.SetDatabaseDefaults(); + attref.SetAttributeFromBlock(attdef, blockref.BlockTransform); + attref.Position = attdef.Position.TransformBy(blockref.BlockTransform); + attref.AdjustAlignment(DBTrans.Top.Database); + + if (atts.ContainsKey(attdef.Tag)) + attref.TextString = atts[attdef.Tag]; + + blockref.AttributeCollection.AppendAttribute(attref); + trans.AddNewlyCreatedDBObject(attref, true); + } + } + } + return objid; + } + #endregion + + #endregion + + #region 遍历 +#line hidden // 调试的时候跳过它 + /// + /// 遍历符号表记录,执行委托 + /// + /// 要运行的委托 + public static void ForEach(this TRecord record, Action task) + where TRecord : SymbolTableRecord, IEnumerable + { + foreach (ObjectId id in record) + task.Invoke(id); + } + + /// + /// 遍历符号表记录,执行委托(允许循环中断) + /// + /// 要执行的委托 + public static void ForEach(this TRecord record, Action task) + where TRecord : SymbolTableRecord, IEnumerable + { + LoopState state = new();/*这种方式比Action改Func更友好*/ + foreach (ObjectId id in record) + { + task.Invoke(id, state); + if (!state.IsRun) + break; + } + } + + /// + /// 遍历符号表记录,执行委托(允许循环中断,输出索引值) + /// + /// 要执行的委托 + [System.Diagnostics.DebuggerStepThrough] + public static void ForEach(this TRecord record, Action task) + where TRecord : SymbolTableRecord, IEnumerable + { + if (task == null) + throw new ArgumentNullException(nameof(task)); + + int i = 0; + LoopState state = new();/*这种方式比Action改Func更友好*/ + foreach (ObjectId id in record) + { + task.Invoke(id, state, i); + if (!state.IsRun) + break; + i++; + } + } +#line default + #endregion +} \ No newline at end of file diff --git a/src/CAD/IFox.CAD.Shared/ExtensionMethod/Tools.cs b/src/CAD/IFox.CAD.Shared/ExtensionMethod/Tools.cs new file mode 100644 index 0000000..1127722 --- /dev/null +++ b/src/CAD/IFox.CAD.Shared/ExtensionMethod/Tools.cs @@ -0,0 +1,60 @@ +using static IFoxCAD.Basal.Timer; + +namespace IFoxCAD.Cad; + +public static class Tools +{ + /// + /// 计时器 + /// + [System.Diagnostics.DebuggerStepThrough] + public static void TestTimes2(int count, string message, Action action) + { + System.Diagnostics.Stopwatch watch = new(); + watch.Start(); // 开始监视代码运行时间 + for (int i = 0; i < count; i++) + action.Invoke();// 需要测试的代码 + watch.Stop(); // 停止监视 + TimeSpan timespan = watch.Elapsed; // 获取当前实例测量得出的总时间 + double time = timespan.TotalMilliseconds; + string name = "毫秒"; + if (timespan.TotalMilliseconds > 1000) + { + time = timespan.TotalSeconds; + name = "秒"; + } + Env.Print($"{message} 代码执行 {count} 次的时间:{time} ({name})"); // 总毫秒数 + } + + /// + /// 纳秒计时器 + /// + [System.Diagnostics.DebuggerStepThrough] + public static void TestTimes(int count, string message, Action action, + TimeEnum timeEnum = TimeEnum.Millisecond) + { + var time = RunTime(() => { + for (int i = 0; i < count; i++) + action(); + }, timeEnum); + + string timeNameZn = ""; + switch (timeEnum) + { + case TimeEnum.Second: + timeNameZn = " 秒"; + break; + case TimeEnum.Millisecond: + timeNameZn = " 毫秒"; + break; + case TimeEnum.Microsecond: + timeNameZn = " 微秒"; + break; + case TimeEnum.Nanosecond: + timeNameZn = " 纳秒"; + break; + } + + Env.Print($"{message} 代码执行 {count} 次的时间:{time} ({timeNameZn})"); + } +} \ No newline at end of file diff --git a/src/CAD/IFox.CAD.Shared/ExtensionMethod/XrefEx.cs b/src/CAD/IFox.CAD.Shared/ExtensionMethod/XrefEx.cs new file mode 100644 index 0000000..9ecdaa8 --- /dev/null +++ b/src/CAD/IFox.CAD.Shared/ExtensionMethod/XrefEx.cs @@ -0,0 +1,656 @@ +// #define error_demo + +namespace IFoxCAD.Cad; + +#region 参照工厂 +public interface IXrefBindModes +{ + /// + /// 卸载 + /// + public void Unload(); + /// + /// 重载 + /// + public void Reload(); + /// + /// 拆离 + /// + public void Detach(); + /// + /// 绑定 + /// + public void Bind(); +} + +public class XrefFactory : IXrefBindModes +{ + #region 私有字段 + readonly DBTrans _tr; + /// + /// 要处理的参照名称,就处理所有 + /// + readonly HashSet? _xrefNames; + #endregion + + #region 公开字段 + /// + /// 解析外部参照:线性引擎
    + /// 默认
    + /// 时会在cad命令历史打印一些AEC信息,并导致绑定慢一点...具体作用不详
    + ///
    + public bool UseThreadEngine = false; + /// + /// 解析外部参照:仅处理 Unresolved_未融入(未解析)的参照
    + /// 默认 + ///
    + public bool DoNewOnly = true; + /// + /// 解析外部参照:包含僵尸参照 + /// + public bool IncludeGhosts = true; + + + /// + /// 绑定模式和双美元符号相关(与cad保持相同的默认)
    + /// 为绑定模式,产生双美元; + /// 为插入模式,块重名会以本图覆盖; + ///
    + public bool BindOrInsert = false; + /// + /// bind时候是否拆离参照
    + /// 默认:学官方的绑定后自动拆离 + ///
    + public bool AutoDetach = true; + /// + /// bind时候是否删除被卸载的嵌套参照
    + /// 默认 + ///
    + public bool EraseNested = true; + /// + /// bind时候控制绑定的符号表:请保持默认
    + /// 目前仅推荐用于
    + /// 其他项有异常:
    + ///
    + public SymModes SymModesBind = SymModes.LayerTable; + #endregion + + #region 构造 + /// + /// 参照工厂 + /// + /// + /// 要处理的参照名称,就处理所有 + public XrefFactory(DBTrans tr, HashSet? xrefNames = null) + { + _tr = tr; + _xrefNames = xrefNames; + } + #endregion + + #region 重写 + public void Bind() + { + // 此功能有绑定出错的问题 + // db.BindXrefs(xrefIds, true); + + // 绑定后会自动拆离 + // 此功能修补了上面缺失 + DoubleBind(); + } + + public void Detach() + { + using ObjectIdCollection xrefIds = new(); + GetAllXrefNode(xrefIds); + foreach (ObjectId id in xrefIds) + _tr.Database.DetachXref(id); + } + + public void Reload() + { + using ObjectIdCollection xrefIds = new(); + GetAllXrefNode(xrefIds); + if (xrefIds.Count > 0) + _tr.Database.ReloadXrefs(xrefIds); + } + + public void Unload() + { + using ObjectIdCollection xrefIds = new(); + GetAllXrefNode(xrefIds); + if (xrefIds.Count > 0) + _tr.Database.UnloadXrefs(xrefIds); + } + #endregion + + #region 双重绑定 + /// + /// 获取参照 + /// + /// 返回全部参照id + void GetAllXrefNode(ObjectIdCollection xrefIds) + { + // 储存要处理的参照id + //var xrefIds = new ObjectIdCollection(); + XrefNodeForEach((xNodeName, xNodeId, xNodeStatus, xNodeIsNested) => { + if (XrefNamesContains(xNodeName)) + xrefIds.Add(xNodeId); + }); + } + + bool XrefNamesContains(string xNodeName) + { + // 为空的时候全部加入 || 有内容时候含有目标 + return _xrefNames is null || _xrefNames.Contains(xNodeName); + } + + /// + /// 遍历参照 + /// + /// (参照名,参照块表记录id,参照状态,是否嵌入) + void XrefNodeForEach(Action action) + { + // btRec.IsFromOverlayReference 是覆盖 + // btRec.GetXrefDatabase(true) 外部参照数据库 + + if (action == null) + return; + + // 解析外部参照:此功能不能锁定文档 + _tr.Database.ResolveXrefs(UseThreadEngine, DoNewOnly); + + var xg = _tr.Database.GetHostDwgXrefGraph(IncludeGhosts); + for (int i = 0; i < xg.NumNodes; i++) + { + var xNode = xg.GetXrefNode(i); + if (!xNode.BlockTableRecordId.IsOk()) + continue; + + action.Invoke(xNode.Name, + xNode.BlockTableRecordId, + xNode.XrefStatus, + xNode.IsNested); + } + } + + /// + /// 符号表记录加入容器 + /// + static void AddedxbindIds(ObjectIdCollection xbindXrefsIds, + SymbolTable symbolTable) + where TTable : SymbolTable + where TRecord : SymbolTableRecord, new() + { + symbolTable.ForEach(tabRec => { + if (tabRec.IsResolved) + xbindXrefsIds.Add(tabRec.ObjectId); + }, checkIdOk: true); + } + + + void GetXBindIds(ObjectIdCollection xbindIds) + { + // xbind + // 0x01 它是用来绑其他符号表,绑块表会有异常 + // 0x02 集合若有问题,就会出现eWrongObjectType + //var xbindIds = new ObjectIdCollection(); + + // 起初测试是将九大符号表记录均加入的,但经实测不行...(为什么?存疑) + #region Option1 + if ((SymModesBind & SymModes.LayerTable) == SymModes.LayerTable) + AddedxbindIds(xbindIds, _tr.LayerTable); + + if ((SymModesBind & SymModes.TextStyleTable) == SymModes.TextStyleTable) + AddedxbindIds(xbindIds, _tr.TextStyleTable); + + if ((SymModesBind & SymModes.RegAppTable) == SymModes.RegAppTable) + AddedxbindIds(xbindIds, _tr.RegAppTable); + + if ((SymModesBind & SymModes.DimStyleTable) == SymModes.DimStyleTable) + AddedxbindIds(xbindIds, _tr.DimStyleTable); + + if ((SymModesBind & SymModes.LinetypeTable) == SymModes.LinetypeTable) + AddedxbindIds(xbindIds, _tr.LinetypeTable); + #endregion + + #region Option2 + if ((SymModesBind & SymModes.UcsTable) == SymModes.UcsTable) + AddedxbindIds(xbindIds, _tr.UcsTable); + + if ((SymModesBind & SymModes.ViewTable) == SymModes.ViewTable) + AddedxbindIds(xbindIds, _tr.ViewTable); + + if ((SymModesBind & SymModes.ViewportTable) == SymModes.ViewportTable) + AddedxbindIds(xbindIds, _tr.ViewportTable); + #endregion + } + + void GetBindIds(ObjectIdCollection bindIds) + { + // bind 只绑块表 + //var bindIds = new ObjectIdCollection(); + + _tr.BlockTable.ForEach(btr => { + if (btr.IsLayout) + return; + + // 外部参照 && 已融入 + if (btr.IsFromExternalReference && btr.IsResolved) + bindIds.Add(btr.ObjectId); + }, checkIdOk: true); + } + + /// + /// 获取可以拆离的ids + /// + /// 返回已卸载中含有嵌套的参照,要重载之后才能绑定 + /// 返回未参照中嵌套的参照,直接拆离 + List GetDetachIds(Dictionary nested) + { + // 直接拆离的id + List detachIds = new(); + + // 收集要处理的id + XrefNodeForEach((xNodeName, xNodeId, xNodeStatus, xNodeIsNested) => { + switch (xNodeStatus) + { + case XrefStatus.Unresolved:// 未融入_ResolveXrefs参数2 + break; + case XrefStatus.FileNotFound:// 未融入(未解析)_未找到文件 + break; + case XrefStatus.Unreferenced:// 未参照 + { + // 为空的时候全部加入 || 有内容时候含有目标 + if (XrefNamesContains(xNodeName)) + detachIds.Add(xNodeId); + } + break; + case XrefStatus.Unloaded:// 已卸载 + { + // 为空的时候全部加入 || 有内容时候含有目标 + if (XrefNamesContains(xNodeName)) + { + var btr = _tr.GetObject(xNodeId); + if (btr != null && btr.IsFromExternalReference) + { + if (!xNodeIsNested) + detachIds.Add(xNodeId); + else if (!nested.ContainsKey(xNodeId)) + nested.Add(xNodeId, xNodeName);// 嵌套参照 + } + } + } + break; + case XrefStatus.Resolved:// 已融入_就是可以绑定的 + break; + case XrefStatus.NotAnXref:// 不是外部参照 + break; + default: + break; + } + }); + return detachIds; + } + + /// + /// 双重绑定参照 + /// 参考链接 + /// + void DoubleBind() + { + Dictionary nested = new(); + var detachIds = GetDetachIds(nested); + + // 拆离:未参照的文件 + if (AutoDetach) + { + for (int i = 0; i < detachIds.Count; i++) + _tr.Database.DetachXref(detachIds[i]); + } + // 重载:嵌套参照已卸载了,需要重载之后才能进行绑定 + var keys = nested.Keys; + if (keys.Count > 0) + { + using ObjectIdCollection idc = new(keys.ToArray()); + _tr.Database.ReloadXrefs(idc); + } + + // 绑定:切勿交换,否则会绑定无效 + using ObjectIdCollection bindIds = new(); + using ObjectIdCollection xbindIds = new(); + GetBindIds(bindIds); + GetXBindIds(xbindIds); + if (xbindIds.Count > 0) + _tr.Database.XBindXrefs(xbindIds, BindOrInsert); + if (bindIds.Count > 0) + _tr.Database.BindXrefs(bindIds, BindOrInsert); + + + // 内部删除嵌套参照的块操作 + if (EraseNested) + { +#if ac2008 + // 因为Acad08索引器存在会暴露isErase的(桌子底层的原因), + // 也就是可能获取两个名称一样的,只能用遍历的方式进行 + HashSet nestedHash = new(); + foreach (var item in nested) + nestedHash.Add(item.Value); + + // 遍历全图,找到参照名称一样的删除 + _tr.BlockTable.ForEach(btr => { + if (btr.IsLayout) + return; + if (nestedHash.Contains(btr.Name)) + { + btr.UpgradeOpen(); + btr.Erase(); + btr.DowngradeOpen(); + btr.Dispose(); + } + }, checkIdOk: true); +#else + foreach (var item in nested) + { + var name = item.Value; + if (_tr.BlockTable.Has(name)) + _tr.GetObject(_tr.BlockTable[name], OpenMode.ForWrite)? + .Erase(); + } +#endif + } + } + #endregion +} + + + +public static class XrefEx +{ + /// + /// 外部参照工厂 + /// + /// + /// 处理参照的枚举 + /// 要处理的参照名称,就处理所有 + public static void XrefFactory(this DBTrans tr, + XrefModes xrefModes, + HashSet? xrefNames = null) + { + var xf = new XrefFactory(tr, xrefNames); + tr.Task(() => { + switch (xrefModes) + { + case XrefModes.Unload: + xf.Unload(); + break; + case XrefModes.Reload: + xf.Reload(); + break; + case XrefModes.Detach: + xf.Detach(); + break; + case XrefModes.Bind: + xf.Bind(); + break; + default: + break; + } + }); + } +} + +#endregion + +#region 参照路径工具类 +/// +/// 获取外部参照的路径 +/// +public class XrefPath +{ + #region 属性 + /// + /// 基础路径 + /// + public string CurrentDatabasePath; + /// + /// 是否外部参照 + /// + public bool IsFromExternalReference { get; private set; } + /// + /// 外部参照保存的路径 + /// + /// 它们会是以下任一路径:
    + /// 0x01 相对路径
    + /// 0x02 绝对路径
    + /// 0x03 共目录优先找到的路径(文件夹整体移动会发生此类情况) + ///
    + ///
    + public string? PathSave { get; private set; } + /// + /// 找到的路径(参照面板的名称) + /// 路径不存在时,返回是外部参照dwg文件路径 + /// + public string? PathDescribe { get; private set; } + + string? _PathComplete; + /// + /// 绝对路径 + /// + public string? PathComplete => _PathComplete ??= + PathConverter(CurrentDatabasePath, PathDescribe, PathConverterModes.Complete); + + string? _PathRelative; + /// + /// 相对路径 + /// + public string? PathRelative => _PathRelative ??= + PathConverter(CurrentDatabasePath, PathComplete, PathConverterModes.Relative); + #endregion + + #region 构造 + /// + /// 获取外部参照的路径 + /// + /// 外部参照图元 + /// 事务 + /// 是否外部参照 + public XrefPath(BlockReference brf, DBTrans tr) + { + if (brf == null) + throw new ArgumentNullException(nameof(brf)); + + CurrentDatabasePath = Path.GetDirectoryName(tr.Database.Filename); + + var btRec = tr.GetObject(brf.BlockTableRecord);// 块表记录 + if (btRec == null) + return; + + IsFromExternalReference = btRec.IsFromExternalReference; + if (!IsFromExternalReference) + return; + + // 相对路径==".\\AA.dwg" + // 无路径=="AA.dwg" + PathSave = btRec.PathName; + + if ((!string.IsNullOrEmpty(PathSave) && PathSave[0] == '.') || File.Exists(PathSave)) + { + // 相对路径||绝对路径 + PathDescribe = PathSave; + } + else + { + // 无路径 + var db = btRec.GetXrefDatabase(true); + PathDescribe = db.Filename; + } + } + #endregion + + #region 静态函数 + /// + /// 获取相对路径或者绝对路径 + /// 参考链接 + /// + /// 基础目录(末尾无斜杠) + /// 相对路径或者绝对路径 + /// 依照枚举返回对应的字符串 + /// + public static string? PathConverter(string? directory, string? fileRelations, PathConverterModes converterModes) + { + if (directory == null) + throw new ArgumentNullException(nameof(directory)); + if (fileRelations == null) + throw new ArgumentNullException(nameof(fileRelations)); + + string? result = null; + switch (converterModes) + { + case PathConverterModes.Relative: + result = GetRelativePath(directory, fileRelations); + break; + case PathConverterModes.Complete: + result = GetCompletePath(directory, fileRelations); + break; + default: + break; + } + return result; + } + +#if error_demo + /// + /// 绝对路径->相对路径 + /// + /// 绝对路径 + /// 相对关系 + /// + /// StringHelper.GetRelativePath("G:\\A1.项目\\20190920金山谷黄宅\\01.饰施图\\03.平面图", + /// "G:\\A1.项目\\20190920金山谷黄宅\\01.饰施图\\01.辅助文件\\图框\\A3图框.dwg"); + public static string GetRelativePath(string strDbPath, string strXrefPath) + { + Uri uri1 = new(strXrefPath); + Uri uri2 = new(strDbPath); + Uri relativeUri = uri2.MakeRelativeUri(uri1); + // 测试例子变成 01.%E8%BE%85%E5%8A%A9%E6%96%87%E4%BB%B6/%E5%9B%BE%E6%A1%86/A3%E5%9B%BE%E6%A1%86.dwg + string str = relativeUri.ToString(); + + // 因为这里不会实现".\A.dwg"而是"A.dwg",所以加入这个操作,满足同目录文件 + var strs = str.Split('\\'); + if (strs.Length == 1) + str = ".\\" + str; + return str; + } +#else + /// + /// 绝对路径->相对路径 + /// + /// 相对关系:文件夹路径 + /// 完整路径:文件路径 + /// 相对路径 + /// "..\\01.辅助文件\\图框\\A3图框.dwg" + /// ]]> + static string GetRelativePath(string directory, string file) + { + string[] directorys = directory.Split('\\'); + string[] files = file.Split('\\'); + // 获取两条路径中的最短路径 + int getMinLength = directorys.Length < files.Length ? directorys.Length : files.Length; + + // 用于确定我们退出的循环中的位置。 + int lastCommonRoot = -1; + int index; + // 找到共根 + for (index = 0; index < getMinLength; index++) + { + if (directorys[index] != files[index]) + break; + lastCommonRoot = index; + } + // 如果我们没有找到一个共同的前缀,那么抛出 + if (lastCommonRoot == -1) + throw new ArgumentException("路径没有公共相同路径部分"); + + // 建立相对路径 + var result = new StringBuilder(); + for (index = lastCommonRoot + 1; index < directorys.Length; index++) + if (directorys[index].Length > 0) + result.Append("..\\");// 上级目录加入 + + // 添加文件夹 + for (index = lastCommonRoot + 1; index < files.Length - 1; index++) + result.Append(files[index] + "\\"); + + // 本级目录 + if (result.Length == 0) + result.Append(".\\"); + // result.Append(strXrefPaths[^1]);// 下级目录加入 + result.Append(files[files.Length - 1]);// 下级目录加入 + return result.ToString(); + } +#endif + + /// + /// 相对路径->绝对路径 + /// + /// 文件夹路径 + /// 相对关系:有..的 + /// 完整路径 + /// "G:\\A1.项目\\20190920金山谷黄宅\\01.饰施图\\01.辅助文件\\图框\\A3图框.dwg" + /// ]]> + static string? GetCompletePath(string directory, string relativePath) + { + if (relativePath is null || relativePath.Trim() == string.Empty) + return null; + + var relativeName = Path.GetDirectoryName(relativePath); + if (relativeName is null) + return null; + + if (relativePath[0] != '.') + return relativePath; + + const char slash = '\\'; + + // 判断向上删除几个 + var path_xiangduis = relativeName.Split(slash); + int index = 0; + for (int i = 0; i < path_xiangduis.Length; i++) + { + if (path_xiangduis[i] != "..") + break; + index++; + } + + var result = new StringBuilder(); + // 前段 + var path_dwgs = directory.Split(slash); + path_dwgs = path_dwgs.Where(s => !string.IsNullOrEmpty(s)).ToArray();// 清理空数组 + for (int i = 0; i < path_dwgs.Length - index; i++) + { + result.Append(path_dwgs[i]); + result.Append(slash); + } + // 后段 + for (int i = 0; i < path_xiangduis.Length; i++) + { + var item = path_xiangduis[i]; + if (item != "." && item != "..") + { + result.Append(item); + result.Append(slash); + } + } + result.Append(Path.GetFileName(relativePath)); + return result.ToString(); + } + #endregion +} +#endregion \ No newline at end of file diff --git "a/src/CAD/IFox.CAD.Shared/ExtensionMethod/\346\226\260\345\273\272\345\241\253\345\205\205/HatchConverter.cs" "b/src/CAD/IFox.CAD.Shared/ExtensionMethod/\346\226\260\345\273\272\345\241\253\345\205\205/HatchConverter.cs" new file mode 100644 index 0000000..ea7ca2e --- /dev/null +++ "b/src/CAD/IFox.CAD.Shared/ExtensionMethod/\346\226\260\345\273\272\345\241\253\345\205\205/HatchConverter.cs" @@ -0,0 +1,395 @@ +namespace IFoxCAD.Cad; + +using System.Data; +using PointV = Point2d; + +/// +/// 填充边界转换器 +/// +public class HatchConverter +{ + #region 辅助类 + /// + /// 生成圆形数据 + /// + class CircleData + { + public PointV Center; + public double Radius; + + /// + /// 生成圆形数据 + /// + /// 对称点1 + /// 对称点2 + public CircleData(PointV symmetryAxisPoint1, PointV symmetryAxisPoint2) + { + Center = symmetryAxisPoint1.GetCenter(symmetryAxisPoint2); + Radius = symmetryAxisPoint1.GetDistanceTo(symmetryAxisPoint2) * 0.5; + } + } + + /// + /// 填充转换器的数据 + /// + class HatchConverterData + { + public List PolyLineData; + public List CircleData; + public List SplineData; + + /// + /// 填充转换器的数据 + /// + public HatchConverterData() + { + PolyLineData = new(); + CircleData = new(); + SplineData = new(); + } + } + #endregion + + #region 成员 + /// + /// 外部只能调用id,否则跨事务造成错误 + /// + public ObjectId OldHatchId + { + get + { + if (_oldHatch is null) + return ObjectId.Null; + return _oldHatch.ObjectId; + } + } + readonly Hatch? _oldHatch; + + readonly List _hcDatas; + /// + /// 填充边界id(生成的/已存在反应器的直接提取) + /// + public List BoundaryIds; + #endregion + + #region 构造 + /// + /// 填充边界转换器 + /// + HatchConverter() + { + _hcDatas = new(); + BoundaryIds = new(); + } + + /// + /// 填充边界转换器 + /// + /// 需要转化的Hatch对象 + public HatchConverter(Hatch hatch) : this() + { + _oldHatch = hatch; + + if (hatch.Associative) + { + // 填充边界反应器 + var assIds = hatch.GetAssociatedObjectIds(); + if (assIds != null) + { + foreach (ObjectId id in assIds) + if (id.IsOk()) + BoundaryIds.Add(id); + + if (BoundaryIds.Count == 0) + { + throw new ArgumentException("关联的填充边界被删除后没有清理反应器,请调用:" + + "\n hatch.RemoveAssociatedObjectIds()" + + "\n hatch.Associative = false"); + } + } + } + } + + /// + /// 提取边界信息 + /// + public void GetBoundarysData() + { + _oldHatch?.ForEach(loop => { + HatchConverterData hcData = new(); + + bool isCurve2d = true; + if (loop.IsPolyline) + { + // 边界是多段线 + HatchLoopIsPolyline(loop, hcData); + isCurve2d = false; + } + else + { + if (loop.Curves.Count == 2)// 1是不可能的,大于2的是曲线 + { + // 边界是曲线,过滤可能是圆形的情况 + var cir = TwoArcFormOneCircle(loop); + if (cir is not null) + { + hcData.CircleData.Add(cir); + isCurve2d = false; + } + } + } + + // 边界是曲线 + if (isCurve2d) + HatchLoopIsCurve2d(loop, hcData); + + _hcDatas.Add(hcData); + }); + } + #endregion + + #region 方法 + /// + /// 多段线处理 + /// + /// 填充边界 + /// 收集图元信息 + static void HatchLoopIsPolyline(HatchLoop loop, HatchConverterData hcData) + { + if (loop is null) + throw new ArgumentNullException(nameof(loop)); + + if (hcData is null) + throw new ArgumentNullException(nameof(hcData)); + + // 判断为圆形: + // 上下两个圆弧,然后填充,就会生成此种填充 + // 顶点数是3,凸度是半圆,两个半圆就是一个圆形 + if (loop.Polyline.Count == 3 && loop.Polyline[0].Bulge == 1 && loop.Polyline[1].Bulge == 1 || + loop.Polyline.Count == 3 && loop.Polyline[0].Bulge == -1 && loop.Polyline[1].Bulge == -1) + { + hcData.CircleData.Add(new CircleData(loop.Polyline[0].Vertex, loop.Polyline[1].Vertex)); + } + else + { + // 遍历多段线信息 + var bvc = loop.Polyline; + for (int i = 0; i < bvc.Count; i++) + hcData.PolyLineData.Add(new BulgeVertexWidth(bvc[i])); + } + } + + /// + /// 两个圆弧组成圆形 + /// + /// + /// + static CircleData? TwoArcFormOneCircle(HatchLoop loop) + { + if (loop is null) + throw new ArgumentNullException(nameof(loop)); + + if (loop.Curves.Count != 2) + throw new ArgumentException( + "边界非多段线,而且点数!=2,点数为:" + nameof(loop.Curves.Count) + ";两个矩形交集的时候会出现此情况."); + + CircleData? circular = null; + + // 判断为圆形: + // 用一条(不是两条)多段线画出两条圆弧为正圆,就会生成此种填充 + // 边界为曲线,数量为2,可能是两个半圆曲线,如果是,就加入圆形数据中 + + // 第一段 + var getCurves1Pts = loop.Curves[0].GetSamplePoints(3); // 曲线取样点分两份(3点) + var mid1Pt = getCurves1Pts[1]; // 腰点 + double bulge1 = loop.Curves[0].StartPoint.GetArcBulge(mid1Pt, loop.Curves[0].EndPoint); + + // 第二段 + var getCurves2Pts = loop.Curves[1].GetSamplePoints(3); + var mid2Pt = getCurves2Pts[1]; + double bulge2 = loop.Curves[1].StartPoint.GetArcBulge(mid2Pt, loop.Curves[1].EndPoint); + + // 第一段上弧&&第二段反弧 || 第一段反弧&&第二段上弧 + if (bulge1 == -1 && bulge2 == -1 || bulge1 == 1 && bulge2 == 1) + circular = new CircleData(loop.Curves[0].StartPoint, loop.Curves[1].StartPoint); // 两个起点就是对称点 + + return circular; + } + + /// + /// 处理边界曲线 + /// + /// 填充边界 + /// 收集图元信息 + static void HatchLoopIsCurve2d(HatchLoop loop, HatchConverterData hcData) + { + // 取每一段曲线,曲线可能是直线来的,但是圆弧会按照顶点来分段 + int curveIsClosed = 0; + + // 遍历边界的多个子段 + foreach (Curve2d curve in loop.Curves) + { + // 计数用于实现闭合 + curveIsClosed++; + if (curve is NurbCurve2d spl) + { + // 判断为样条曲线: + hcData.SplineData.Add(spl); + continue; + } + + var pts = curve.GetSamplePoints(3); + var midPt = pts[1]; + if (curve.StartPoint.IsEqualTo(curve.EndPoint, new Tolerance(1e-6, 1e-6)))// 首尾相同,就是圆形 + { + // 判断为圆形: + // 获取起点,然后采样三点,中间就是对称点(直径点) + hcData.CircleData.Add(new CircleData(curve.StartPoint, midPt)); + continue; + } + + // 判断为多段线,圆弧: + double bulge = curve.StartPoint.GetArcBulge(midPt, curve.EndPoint); + hcData.PolyLineData.Add(new BulgeVertexWidth(curve.StartPoint, bulge)); + + // 末尾点,不闭合的情况下就要获取这个 + if (curveIsClosed == loop.Curves.Count) + hcData.PolyLineData.Add(new BulgeVertexWidth(curve.EndPoint, 0)); + } + } + + /// + /// 创建边界图元 + /// + /// 返回图元 + public void CreateBoundary(List outEnts) + { + for (int i = 0; i < _hcDatas.Count; i++) + { + var data = _hcDatas[i]; + + // 生成边界:多段线 + if (data.PolyLineData.Count > 0) + { + Polyline pl = new(); + pl.SetDatabaseDefaults(); + for (int j = 0; j < data.PolyLineData.Count; j++) + { + pl.AddVertexAt(j, + data.PolyLineData[j].Vertex, + data.PolyLineData[j].Bulge, + data.PolyLineData[j].StartWidth, + data.PolyLineData[j].EndWidth); + } + outEnts.Add(pl); + } + + // 生成边界:圆 + data.CircleData.ForEach(item => { + outEnts.Add(new Circle(item.Center.Point3d(), Vector3d.ZAxis, item.Radius)); + }); + + // 生成边界:样条曲线 + data.SplineData.ForEach(item => { + outEnts.Add(item.ToCurve()); + }); + } + + if (_oldHatch is not null) + { + outEnts.ForEach(ent => { + ent.Color = _oldHatch.Color; + ent.Layer = _oldHatch.Layer; + }); + } + } + + + /// + /// 创建边界图元和新填充到当前空间 + /// + /// + /// 边界关联 + /// 是否创建填充,false则只创建边界 + /// 新填充id,边界在获取 + public ObjectId CreateBoundarysAndHatchToMsPs(BlockTableRecord btrOfAddEntitySpace, + bool boundaryAssociative = true, + bool createHatchFlag = true, + Transaction? trans = null) + { + List boEnts = new(); + CreateBoundary(boEnts); + boEnts.ForEach(ent => { + BoundaryIds.Add(btrOfAddEntitySpace.AddEntity(ent)); + }); + + if (!createHatchFlag) + return ObjectId.Null; + /* + * 此处为什么要克隆填充,而不是新建填充? + * 因为填充如果是新建的,那么将会丢失基点,概念如下: + * 两个一样的填充,平移其中一个,那么再提取他们的基点会是一样的! + * 所以生成时候就不等同于画面相同. + * 也因为我不知道什么新建方式可以新建一模一样的填充,因此使用了克隆 + * 那么它的平移后的基点在哪里呢? + */ + + using ObjectIdCollection idc = new(new ObjectId[] { OldHatchId }); + using IdMapping map = new(); + btrOfAddEntitySpace.DeepCloneEx(idc, map); + var newHatchId = map.GetValues()[0]; + trans ??= DBTrans.Top.Transaction; + + bool openErased = false; + bool openLockedLayer = false; + var hatchEnt = trans.GetObject(newHatchId, OpenMode.ForWrite, + openErased, openLockedLayer) as Hatch; + if (hatchEnt != null) + { + ResetBoundary(hatchEnt, boundaryAssociative); + hatchEnt.DowngradeOpen(); + } + return newHatchId; + } + + /// + /// 重设边界 + /// + /// + /// 边界关联 + void ResetBoundary(Hatch hatch, bool boundaryAssociative = true) + { + if (BoundaryIds.Count == 0) + return; + + // todo ------ acad08分离填充报错: Microsoft Visual Studio C 运行库在 acad.exe 中检测到一个错误 + // 0x01 测试命令 CmdTest_CreateHatch 创建是可以分离的, + // 那么可能是 克隆后 修改导致的, + // 我是克隆了之后移除原有边界,为了一些xdata之类的 + // 0x02 测试了 hatch.SetDatabaseDefaults(); 并不是因为这个 + // 0x03 测试了 v1110 不移除原有边界,而是加入了之后再移除旧的边界,也是一样 + // 要处理这个问题,我想:自己实现一个分离填充,不用cad自带的,然后单独填充每个. + // 填充边界的算法是扫描线算法.这样就可以绕过去了...发现过于麻烦,放弃... + + // v1110 删除原有边界 + while (hatch.NumberOfLoops != 0) + hatch.RemoveLoopAt(0); + + hatch.Associative = boundaryAssociative; + + using ObjectIdCollection obIds = new(); + for (int i = 0; i < BoundaryIds.Count; i++) + { + obIds.Clear(); + obIds.Add(BoundaryIds[i]); + // 要先添加最外面的边界 + if (i == 0) + hatch.AppendLoop(HatchLoopTypes.Outermost, obIds); + else + hatch.AppendLoop(HatchLoopTypes.Default, obIds); + } + // 计算填充并显示 + hatch.EvaluateHatch(true); + } + #endregion +} \ No newline at end of file diff --git "a/src/CAD/IFox.CAD.Shared/ExtensionMethod/\346\226\260\345\273\272\345\241\253\345\205\205/HatchEx.cs" "b/src/CAD/IFox.CAD.Shared/ExtensionMethod/\346\226\260\345\273\272\345\241\253\345\205\205/HatchEx.cs" new file mode 100644 index 0000000..8bfd043 --- /dev/null +++ "b/src/CAD/IFox.CAD.Shared/ExtensionMethod/\346\226\260\345\273\272\345\241\253\345\205\205/HatchEx.cs" @@ -0,0 +1,15 @@ +namespace IFoxCAD.Cad; + +public static class HatchEx +{ + /// + /// 遍历填充每条边 + /// + /// + /// + public static void ForEach(this Hatch hatch, Action action) + { + for (int i = 0; i < hatch.NumberOfLoops; i++) + action.Invoke(hatch.GetLoopAt(i)); + } +} \ No newline at end of file diff --git "a/src/CAD/IFox.CAD.Shared/ExtensionMethod/\346\226\260\345\273\272\345\241\253\345\205\205/HatchInfo.cs" "b/src/CAD/IFox.CAD.Shared/ExtensionMethod/\346\226\260\345\273\272\345\241\253\345\205\205/HatchInfo.cs" new file mode 100644 index 0000000..9f4e90b --- /dev/null +++ "b/src/CAD/IFox.CAD.Shared/ExtensionMethod/\346\226\260\345\273\272\345\241\253\345\205\205/HatchInfo.cs" @@ -0,0 +1,353 @@ +namespace IFoxCAD.Cad; + +/* + * 添加的第一个边界必须是外边界,即用于定义图案填充最外面的边界。 + * 要添加外部边界,请使用添加环的类型为 HatchLoopTypes.Outermost 常量的 AppendLoop 方法, + * 一旦外边界被定义,就可以继续添加另外的边界。 + * 添加内部边界请使用带 HatchLoopTypes.Default 常量的 AppendLoop 方法。 + * + * 多个外边界的时候,添加的是(外边界,外边界,外边界,普通边界....) + * 多个外边界的时候,添加的是(外边界,普通边界.....外边界,普通边界....) + */ + +/// +/// 图案填充 +/// +public class HatchInfo +{ + #region 成员 + /// + /// 边界id(最外面放第一) + /// + readonly List _boundaryIds; + /// + /// 填充图元 + /// + readonly Hatch _hatch; + /// + /// 边界关联(此处不能直接=>给填充成员,因为它会加入反应器) + /// + readonly bool _boundaryAssociative; + /// + /// 填充的名称:用户定义(固定名称)/渐变/填充依据定义文件 + /// + string? _hatchName; + /// + /// 填充模式类型(预定义/用户定义/自定义) + /// + HatchPatternType _patternTypeHatch; + /// + /// 渐变模式类型 + /// + GradientPatternType _patternTypeGradient; + /// + /// 比例/间距 + /// + double Scale => _hatch.PatternScale; + /// + /// 角度 + /// + double Angle => _hatch.PatternAngle; + #endregion + + #region 构造 + HatchInfo() + { + _hatch = new Hatch(); + _hatch.SetDatabaseDefaults(); + _boundaryIds = new(); + } + + /// + /// 图案填充 + /// + /// 关联边界 + /// 填充原点 + /// 比例 + /// 角度 + public HatchInfo(bool boundaryAssociative = true, + Point2d? hatchOrigin = null, + double hatchScale = 1, + double hatchAngle = 0) : this() + { + if (hatchScale <= 0) + throw new ArgumentException("填充比例不允许小于等于0"); + + _hatch.PatternScale = hatchScale;// 填充比例 + _hatch.PatternAngle = hatchAngle;// 填充角度 + _boundaryAssociative = boundaryAssociative; + + hatchOrigin ??= Point2d.Origin; + _hatch.Origin = hatchOrigin.Value; // 填充原点 + } + + /// + /// 图案填充 + /// + /// 边界 + /// 关联边界 + /// 填充原点 + /// 比例 + /// 角度 + public HatchInfo(IEnumerable boundaryIds, + bool boundaryAssociative = true, + Point2d? hatchOrigin = null, + double hatchScale = 1, + double hatchAngle = 0) + : this(boundaryAssociative, hatchOrigin, hatchScale, hatchAngle) + { + _boundaryIds.AddRange(boundaryIds); + } + + #endregion + + #region 方法 + /// + /// 模式1:预定义 + /// + public HatchInfo Mode1PreDefined(string name) + { + _hatchName = name; + _hatch.HatchObjectType = HatchObjectType.HatchObject; // 对象类型(填充/渐变) + _patternTypeHatch = HatchPatternType.PreDefined; + return this; + } + + /// + /// 模式2:用户定义 + /// + /// 是否双向 + public HatchInfo Mode2UserDefined(bool patternDouble = true) + { + _hatchName = "_USER"; + _hatch.HatchObjectType = HatchObjectType.HatchObject; // 对象类型(填充/渐变) + _patternTypeHatch = HatchPatternType.UserDefined; + + _hatch.PatternDouble = patternDouble; // 是否双向(必须写在 SetHatchPattern 之前) + _hatch.PatternSpace = Scale; // 间距(必须写在 SetHatchPattern 之前) + return this; + } + + /// + /// 模式3:自定义 + /// + /// + public HatchInfo Mode3UserDefined(string name) + { + _hatchName = name; + _hatch.HatchObjectType = HatchObjectType.HatchObject; // 对象类型(填充/渐变) + _patternTypeHatch = HatchPatternType.CustomDefined; + return this; + } + + /// + /// 模式4:渐变填充 + /// + /// 渐变填充名称 + /// 渐变色起始颜色 + /// 渐变色结束颜色 + /// 渐变移动 + /// 色调值 + /// 单色双色 + public HatchInfo Mode4Gradient(GradientName name, Color colorStart, Color colorEnd, + float gradientShift = 0, + float shadeTintValue = 0, + bool gradientOneColorMode = false) + { + // entget渐变的名字必然是"SOLID",但是这里作为"渐变"名,而不是"填充"名 + _hatchName = name.ToString(); + _hatch.HatchObjectType = HatchObjectType.GradientObject; // 对象类型(填充/渐变) + _patternTypeGradient = GradientPatternType.PreDefinedGradient;// 模式4:渐变 + // _patternTypeGradient = GradientPatternType.UserDefinedGradient;// 模式5:渐变..这种模式干啥用呢 + + // 设置渐变色填充的起始和结束颜色 + var gColor1 = new GradientColor(colorStart, 0); + var gColor2 = new GradientColor(colorEnd, 1); + _hatch.SetGradientColors(new GradientColor[] { gColor1, gColor2 }); + + _hatch.GradientShift = gradientShift; // 梯度位移 + _hatch.ShadeTintValue = shadeTintValue; // 阴影色值 + _hatch.GradientOneColorMode = gradientOneColorMode;// 渐变单色/双色 + _hatch.GradientAngle = Angle; // 渐变角度 + + return this; + } + + /// + /// 构建 + /// + /// 将填充加入此空间 + public ObjectId Build(BlockTableRecord btrOfAddEntitySpace) + { + // 加入数据库 + var hatchId = btrOfAddEntitySpace.AddEntity(_hatch); + + // 设置模式:渐变/填充 + if (_hatch.HatchObjectType == HatchObjectType.GradientObject) + _hatch.SetGradient(_patternTypeGradient, _hatchName); + else + _hatch.SetHatchPattern(_patternTypeHatch, _hatchName); + + // 关联边界,如果不先添加数据库空间内就会出错 + // 为 true 会加入反应器,因此比较慢(二维码将会十几秒才生成好),视需求而定. + _hatch.Associative = _boundaryAssociative; + + // 利用 AppendLoop 重载加入,这里就不处理 + if (_boundaryIds.Count > 0) + AppendLoop(_boundaryIds, HatchLoopTypes.Default); + + // 计算填充并显示(若边界出错,这句会异常) + _hatch.EvaluateHatch(true); + + return hatchId; + } + + /// + /// 执行图元的属性修改 + /// + /// 扔出填充实体 + public HatchInfo Action(Action action) + { + action(_hatch); + return this; + } + + /// + /// 清空边界集合 + /// + public HatchInfo ClearBoundary() + { + _boundaryIds.Clear(); + return this; + } + + /// + /// 删除边界图元 + /// + public HatchInfo EraseBoundary() + { + for (int i = 0; i < _boundaryIds.Count; i++) + _boundaryIds[i].Erase(); + return this; + } + + /// + /// 加入边界 + /// + /// 边界id + /// 加入方式 + void AppendLoop(IEnumerable boundaryIds, + HatchLoopTypes hatchLoopTypes = HatchLoopTypes.Default) + { + using ObjectIdCollection obIds = new(); + // 边界是闭合的,而且已经加入数据库 + // 填充闭合环类型.最外面 + foreach (var border in boundaryIds) + { + obIds.Clear(); + obIds.Add(border); + _hatch.AppendLoop(hatchLoopTypes, obIds); + } + } + + /// + /// 加入边界(仿高版本的填充函数) + /// + /// 点集 + /// 凸度集 + /// 加入此空间 + /// 加入方式 + /// + public HatchInfo AppendLoop(Point2dCollection pts, + DoubleCollection bluges, + BlockTableRecord btrOfAddEntitySpace, + HatchLoopTypes hatchLoopTypes = HatchLoopTypes.Default) + { + if (pts == null) + throw new ArgumentNullException(nameof(pts)); + + pts.End2End(); +#if NET35 + _boundaryIds.Add(CreateAddBoundary(pts, bluges, btrOfAddEntitySpace)); +#else + // 2011新增API,可以不生成图元的情况下加入边界, + // 通过这里进入的话,边界 _boundaryIds 是空的,那么 Build() 时候就需要过滤空的 + _hatch.AppendLoop(hatchLoopTypes, pts, bluges); +#endif + return this; + } + +#if NET35 + /// + /// 通过点集和凸度生成边界的多段线 + /// + /// 点集 + /// 凸度集 + /// 加入此空间 + /// 多段线id + static ObjectId CreateAddBoundary(Point2dCollection? pts, + DoubleCollection? bluges, + BlockTableRecord btrOfAddEntitySpace) + { + if (pts is null) + throw new ArgumentException(null, nameof(pts)); + if (bluges is null) + throw new ArgumentException(null, nameof(bluges)); + + var bvws = new List(); + + var itor1 = pts.GetEnumerator(); + var itor2 = bluges.GetEnumerator(); + while (itor1.MoveNext() && itor2.MoveNext()) + bvws.Add(new BulgeVertexWidth(itor1.Current, itor2.Current)); + + return btrOfAddEntitySpace.AddPline(bvws); + } +#endif + #endregion + + #region 枚举 + /// + /// 渐变色填充的图案名称 + /// + public enum GradientName + { + /// + /// 线状渐变 + /// + Linear, + /// + /// 圆柱状渐变 + /// + Cylinder, + /// + /// 反圆柱状渐变 + /// + Invcylinder, + /// + /// 球状渐变 + /// + Spherical, + /// + /// 反球状渐变 + /// + Invspherical, + /// + /// 半球状渐变 + /// + Hemisperical, + /// + /// 反半球状渐变 + /// + InvHemisperical, + /// + /// 抛物面状渐变 + /// + Curved, + /// + /// 反抛物面状渐变 + /// + Incurved + } + #endregion +} \ No newline at end of file diff --git "a/src/CAD/IFox.CAD.Shared/ExtensionMethod/\346\226\260\345\273\272\346\226\207\345\255\227/AttachmentPointHelper.cs" "b/src/CAD/IFox.CAD.Shared/ExtensionMethod/\346\226\260\345\273\272\346\226\207\345\255\227/AttachmentPointHelper.cs" new file mode 100644 index 0000000..975e8a7 --- /dev/null +++ "b/src/CAD/IFox.CAD.Shared/ExtensionMethod/\346\226\260\345\273\272\346\226\207\345\255\227/AttachmentPointHelper.cs" @@ -0,0 +1,95 @@ +namespace IFoxCAD.Cad; + +public static class AttachmentPointHelper +{ + static readonly Dictionary _alignment = new() + { + { "左上", AttachmentPoint.TopLeft }, + { "中上", AttachmentPoint.TopCenter },// 单行的对齐 + { "右上", AttachmentPoint.TopRight }, + + { "左中", AttachmentPoint.MiddleLeft }, + { "正中", AttachmentPoint.MiddleCenter },// 多行的正中 + { "右中", AttachmentPoint.MiddleRight }, + + { "左对齐", AttachmentPoint.BaseLeft },// ※优先(放在前面优先获取) + { "左", AttachmentPoint.BaseLeft }, + + { "中间", AttachmentPoint.BaseMid }, + + { "右对齐", AttachmentPoint.BaseRight },// ※优先(放在前面优先获取) + { "右", AttachmentPoint.BaseRight }, + + { "左下", AttachmentPoint.BottomLeft }, + { "中下", AttachmentPoint.BottomCenter }, + { "右下", AttachmentPoint.BottomRight }, + + { "对齐", AttachmentPoint.BaseAlign },// ※优先(放在前面优先获取) + { "调整", AttachmentPoint.BaseAlign }, + + { "居中", AttachmentPoint.BaseCenter },// 单行的中 + { "铺满", AttachmentPoint.BaseFit }, + }; + + /// + /// 输入文字获得对齐方式 + /// + /// + /// + public static AttachmentPoint Get(string key) + { + return _alignment[key]; + } + + /// + /// 输入对齐方式获得文字 + /// + /// + /// + public static string Get(AttachmentPoint value) + { + return _alignment.FirstOrDefault(q => q.Value == value).Key; + } +} + +#if false +// 反射描述 +// 这些东西cad没有用到啊...所以不纳入了 +public enum AttachmentPoint2 +{ + [Description("下对齐")] + BottomAlign = 14, + [Description("中对齐")] + MiddleAlign = 15,// 0xF + [Description("上对齐")] + TopAlign = 16,// 0x10 + [Description("下铺满")] + BottomFit = 18, + [Description("中铺满")] + MiddleFit = 19, + [Description("上铺满")] + TopFit = 20, + [Description("下居中")] + BottomMid = 22, + [Description("中居中")] + MiddleMid = 23, + [Description("下居中")] + TopMid = 24, +} + +public static Dictionary GetEnumDic(Type enumType) +{ + Dictionary dic = new(); + var fieldinfos = enumType.GetFields(); + for (int i = 0; i < fieldinfos.Length; i++) + { + var field = fieldinfos[i]; + if (field.FieldType.IsEnum) + { + var objs = field.GetCustomAttributes(typeof(DescriptionAttribute), false); + dic.Add(field.Name, ((DescriptionAttribute)objs[0]).Description); + } + } + return dic; +} +#endif \ No newline at end of file diff --git "a/src/CAD/IFox.CAD.Shared/ExtensionMethod/\346\226\260\345\273\272\346\226\207\345\255\227/TextEntityAdd.cs" "b/src/CAD/IFox.CAD.Shared/ExtensionMethod/\346\226\260\345\273\272\346\226\207\345\255\227/TextEntityAdd.cs" new file mode 100644 index 0000000..5b37a31 --- /dev/null +++ "b/src/CAD/IFox.CAD.Shared/ExtensionMethod/\346\226\260\345\273\272\346\226\207\345\255\227/TextEntityAdd.cs" @@ -0,0 +1,62 @@ +namespace IFoxCAD.Cad; + +public static partial class EntityAdd +{ + /// + /// 创建单行文字 + /// + /// 数据库 + /// 内容 + /// 插入点 + /// 字体高度 + /// 文字样式 + /// 对齐方式 + /// 对齐点,因样式 可能无效 + /// + public static Entity AddDBTextToEntity(this Database db, + string textContents, + Point3d position, + double textHigh = 2.5, + ObjectId? textStyleId = null, + AttachmentPoint justify = AttachmentPoint.BaseLeft, + Point3d? justifyPoint = null) + { + var TextInfo = new TextInfo( + textContents, + position, + justify, + justifyPoint, + textStyleId, + textHigh, + db); + return TextInfo.AddDBTextToEntity(); + } + + /// + /// 新建多行文字 + /// + /// 数据库 + /// 内容 + /// 插入点 + /// 字体高度 + /// 文字样式 + /// 对齐方式 + /// + public static Entity AddMTextToEntity(this Database db, + string textContents, + Point3d position, + double textHigh = 2.5, + ObjectId? textStyleId = null, + AttachmentPoint justify = AttachmentPoint.BaseLeft) + { + var TextInfo = new TextInfo( + textContents, + position, + justify, + null, + textStyleId, + textHigh, + db); + return TextInfo.AddMTextToEntity(); + } +} diff --git "a/src/CAD/IFox.CAD.Shared/ExtensionMethod/\346\226\260\345\273\272\346\226\207\345\255\227/TextInfo.cs" "b/src/CAD/IFox.CAD.Shared/ExtensionMethod/\346\226\260\345\273\272\346\226\207\345\255\227/TextInfo.cs" new file mode 100644 index 0000000..d8bfd7a --- /dev/null +++ "b/src/CAD/IFox.CAD.Shared/ExtensionMethod/\346\226\260\345\273\272\346\226\207\345\255\227/TextInfo.cs" @@ -0,0 +1,178 @@ +namespace IFoxCAD.Cad; + +/// +/// 文字信息类 +/// +public class TextInfo +{ + readonly Database? Database; + readonly string? Contents; + readonly Point3d Position; + + public string TextJustifyCn => AttachmentPointHelper.Get(TextJustify); + readonly AttachmentPoint TextJustify; + readonly Point3d? AlignmentPoint; + + readonly double TextHeight; + readonly ObjectId? TextStyleId; + + /// + /// 文字信息类 + /// + /// 内容 + /// 基点 + /// 对齐方式 + /// 对齐点(对齐方式是左,此参数无效,为null不为左就报错) + /// 文字样式id + /// 文字高度 + /// 数据库 + public TextInfo(string? contents, + Point3d position, + AttachmentPoint justify, + Point3d? justifyPoint = null, + ObjectId? textStyleId = null, + double textHeight = 2.5, + Database? database = null) + { + Contents = contents; + Position = position; + TextJustify = justify; + + if (justifyPoint is null && TextJustify != AttachmentPoint.BaseLeft) + throw new ArgumentNullException(nameof(justifyPoint)); + + AlignmentPoint = justifyPoint; + TextHeight = textHeight; + TextStyleId = textStyleId; + Database = database; + } + + /// + /// 创建单行文字 + /// + public DBText AddDBTextToEntity() + { + if (string.IsNullOrEmpty(Contents)) + throw new ArgumentNullException(nameof(Contents) + "创建文字无内容"); + + var acText = new DBText(); + acText.SetDatabaseDefaults(); + + if (Database is not null) + acText.SetDatabaseDefaults(Database);// 我的默认值是填满的,所以可以不需要 + + if (TextStyleId is not null) + acText.SetTextStyleId(TextStyleId.Value); + + acText.Height = TextHeight; // 高度 + acText.TextString = Contents; // 内容 + acText.Position = Position; // 插入点(一定要先设置) + acText.Justify = TextJustify; // 使他们对齐 + // acText.HorizontalMode + + if (AlignmentPoint is not null) + acText.AlignmentPoint = AlignmentPoint.Value; + else if (acText.Justify != AttachmentPoint.BaseLeft) + acText.AlignmentPoint = Position; + + if (Database is not null) + acText.AdjustAlignment(Database); + return acText; + } + + /// + /// 创建多行文字 + /// + /// + public MText AddMTextToEntity() + { + if (string.IsNullOrEmpty(Contents)) + throw new ArgumentNullException(nameof(Contents) + "创建文字无内容"); + + var mText = new MText(); + mText.SetDatabaseDefaults(); + + if (Database is not null) + mText.SetDatabaseDefaults(Database); + + if (TextStyleId is not null) + mText.SetTextStyleId(TextStyleId.Value); + + mText.TextHeight = TextHeight; // 高度 + mText.Contents = Contents; // 内容 + mText.Location = Position; // 插入点(一定要先设置) + + // mText.SetAttachmentMovingLocation(TextJustify); + mText.Attachment = TextJustify;// 使他们对齐 + + return mText; + } +} + +// 反射设定对象的文字样式id +public static partial class TextInfoHelper +{ + /// + /// 设置文字样式id + /// + /// 单行文字 + /// 文字样式表记录id + public static void SetTextStyleId(this DBText acText, ObjectId ltrObjectId) + { + SetEntityTxtStyleId(acText, ltrObjectId); + } + + /// + /// 设置文字样式id + /// + /// 多行文字 + /// 文字样式表记录id + public static void SetTextStyleId(this MText acText, ObjectId ltrObjectId) + { + SetEntityTxtStyleId(acText, ltrObjectId); + } + + static void SetEntityTxtStyleId(Entity acText, ObjectId ltrObjectId) + { + GetTextStyleIdType(acText)?.SetValue(acText, ltrObjectId, null); + } + + /// + /// 获取文字样式id + /// + public static ObjectId GetTextStyleId(this DBText acText) + { + return GetEntityTxtStyleId(acText); + } + + /// + /// 获取文字样式id + /// + public static ObjectId GetTextStyleId(this MText acText) + { + return GetEntityTxtStyleId(acText); + } + + static ObjectId GetEntityTxtStyleId(Entity acText) + { + var result = ObjectId.Null; + var id = GetTextStyleIdType(acText)?.GetValue(acText, null); + if (id != null) + result = (ObjectId)id; + return result; + } + + static PropertyInfo? _textStyleId = null; + static PropertyInfo GetTextStyleIdType(Entity acText) + { + if (_textStyleId == null) + { + var entType = acText.GetType(); + var prs = entType.GetProperties(); + _textStyleId = prs.FirstOrDefault(a => a.Name == "TextStyle");// 反射获取属性 + if (_textStyleId == null) + _textStyleId = prs.FirstOrDefault(a => a.Name == "TextStyleId");// 反射获取属性 + } + return _textStyleId; + } +} diff --git a/src/CAD/IFox.CAD.Shared/IFox.CAD.Shared.projitems b/src/CAD/IFox.CAD.Shared/IFox.CAD.Shared.projitems new file mode 100644 index 0000000..6974a96 --- /dev/null +++ b/src/CAD/IFox.CAD.Shared/IFox.CAD.Shared.projitems @@ -0,0 +1,90 @@ + + + + $(MSBuildAllProjects);$(MSBuildThisFileFullPath) + true + 20f254f7-aee5-42ae-a9b3-149bbdc7397f + + + IFox.CAD.Shared + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/CAD/IFox.CAD.Shared/IFox.CAD.Shared.shproj b/src/CAD/IFox.CAD.Shared/IFox.CAD.Shared.shproj new file mode 100644 index 0000000..ac52df3 --- /dev/null +++ b/src/CAD/IFox.CAD.Shared/IFox.CAD.Shared.shproj @@ -0,0 +1,13 @@ + + + + 20f254f7-aee5-42ae-a9b3-149bbdc7397f + 14.0 + + + + + + + + diff --git a/src/CAD/IFox.CAD.Shared/ResultData/LispDottedPair.cs b/src/CAD/IFox.CAD.Shared/ResultData/LispDottedPair.cs new file mode 100644 index 0000000..009d13e --- /dev/null +++ b/src/CAD/IFox.CAD.Shared/ResultData/LispDottedPair.cs @@ -0,0 +1,68 @@ +namespace IFoxCAD.Cad; + +/// +/// lisp点对表的数据封装类 +/// +public class LispDottedPair : LispList +{ + #region 构造函数 + /// + /// 默认无参构造函数 + /// + public LispDottedPair() + { + } + + /// + /// 构造函数 + /// + /// TypedValue 迭代器 + public LispDottedPair(IEnumerable values) : base(values) + { + } + /// + /// 构造函数 + /// + /// 点对表左数 + /// 点对表右数 + public LispDottedPair(TypedValue left, TypedValue right) + { + Add(left); + Add(right); + } + #endregion + + #region 重写 + /// + /// 点对表的值 + /// + public override List Value + { + get + { + var value = new List + { + new TypedValue((int)LispDataType.ListBegin,-1), + new TypedValue((int)LispDataType.DottedPair,-1) + }; + value.InsertRange(1, this); + return value; + } + } + #endregion + + #region 转换器 + + /// + /// LispDottedPair 隐式转换到 TypedValue 数组 + /// + /// TypedValueList 实例 + public static implicit operator TypedValue[](LispDottedPair values) => values.Value.ToArray(); + /// + /// LispDottedPair 隐式转换到 ResultBuffer + /// + /// TypedValueList 实例 + public static implicit operator ResultBuffer(LispDottedPair values) => new(values.Value.ToArray()); + + #endregion +} diff --git a/src/CAD/IFox.CAD.Shared/ResultData/LispList.cs b/src/CAD/IFox.CAD.Shared/ResultData/LispList.cs new file mode 100644 index 0000000..c186e43 --- /dev/null +++ b/src/CAD/IFox.CAD.Shared/ResultData/LispList.cs @@ -0,0 +1,194 @@ +namespace IFoxCAD.Cad; + +/// +/// lisp数据封装类 +/// +public class LispList : TypedValueList +{ + #region 构造函数 + /// + /// 默认构造函数 + /// + public LispList() { } + + /// + /// 构造函数 + /// + /// TypedValue 迭代器 + public LispList(IEnumerable values) : base(values) { } + #endregion + + #region 重写 + /// + /// lisp 列表的值 + /// + public virtual List Value + { + get + { + var value = new List + { + new TypedValue((int)LispDataType.ListBegin,-1), + new TypedValue((int)LispDataType.ListEnd,-1) + }; + value.InsertRange(1, this); + return value; + } + } + #endregion + + #region 添加数据 + /// + /// 添加数据 + /// + /// 组码 + /// 组码值 + public override void Add(int code, object? obj) + { + if (code < 5000) + throw new System.Exception("传入的组码值不是 lisp数据 有效范围!"); + + Add(new TypedValue(code, obj)); + } + + /// + /// 添加数据 + /// + /// dxfcode枚举值 + /// 组码值 + public void Add(LispDataType code, object? obj) + { + Add((int)code, obj); + } + /// + /// 添加数据,参数为true时添加 lisp 中的 T,false时添加 lisp 中的 nil + /// + /// bool 型的数据 + public void Add(bool value) + { + if (value) + { + Add(LispDataType.T_atom, true); + } + else + { + Add(LispDataType.Nil, null); + } + } + /// + /// 添加字符串 + /// + /// 字符串 + public void Add(string value) + { + Add(LispDataType.Text, value); + } + /// + /// 添加短整型数 + /// + /// 短整型数 + public void Add(short value) + { + Add(LispDataType.Int16, value); + } + /// + /// 添加整型数 + /// + /// 整型数 + public void Add(int value) + { + Add(LispDataType.Int32, value); + } + /// + /// 添加浮点数 + /// + /// 浮点数 + public void Add(double value) + { + Add(LispDataType.Double, value); + } + /// + /// 添加对象id + /// + /// 对象id + public void Add(ObjectId value) + { + Add(LispDataType.ObjectId, value); + } + /// + /// 添加选择集 + /// + /// 选择集 + public void Add(SelectionSet value) + { + Add(LispDataType.SelectionSet, value); + } + /// + /// 添加二维点 + /// + /// 二维点 + public void Add(Point2d value) + { + Add(LispDataType.Point2d, value); + } + /// + /// 添加三维点 + /// + /// 三维点 + public void Add(Point3d value) + { + Add(LispDataType.Point3d, value); + } + /// + /// 添加二维点 + /// + /// X + /// Y + public void Add(double x, double y) + { + Add(LispDataType.Point2d, new Point2d(x, y)); + } + /// + /// 添加三维点 + /// + /// X + /// Y + /// Z + public void Add(double x, double y, double z) + { + Add(LispDataType.Point3d, new Point3d(x, y, z)); + } + /// + /// 添加列表 + /// + /// lisp 列表 + public void Add(LispList value) + { + this.AddRange(value.Value); + } + + #endregion + + #region 转换器 + /// + /// ResultBuffer 隐式转换到 LispList + /// + /// ResultBuffer 实例 + public static implicit operator LispList(ResultBuffer buffer) => new(buffer.AsArray()); + /// + /// LispList 隐式转换到 TypedValue 数组 + /// + /// TypedValueList 实例 + public static implicit operator TypedValue[](LispList values) => values.Value.ToArray(); + /// + /// LispList 隐式转换到 ResultBuffer + /// + /// TypedValueList 实例,要using + public static implicit operator ResultBuffer(LispList values) => new(values.Value.ToArray()); + /// + /// TypedValue 数组隐式转换到 LispList + /// + /// TypedValue 数组 + public static implicit operator LispList(TypedValue[] values) => new(values); + #endregion +} \ No newline at end of file diff --git a/src/CAD/IFox.CAD.Shared/ResultData/TypedValueList.cs b/src/CAD/IFox.CAD.Shared/ResultData/TypedValueList.cs new file mode 100644 index 0000000..16970d2 --- /dev/null +++ b/src/CAD/IFox.CAD.Shared/ResultData/TypedValueList.cs @@ -0,0 +1,64 @@ +namespace IFoxCAD.Cad; + +/// +/// 用于集中管理扩展数据/扩展字典/resultbuffer的类 +/// +public class TypedValueList : List +{ + #region 构造函数 + /// + /// 默认无参构造函数 + /// + public TypedValueList() { } + /// + /// 采用 TypedValue 迭代器构造 TypedValueList + /// + /// + public TypedValueList(IEnumerable values) : base(values) { } + #endregion + + #region 添加数据 + /// + /// 添加数据 + /// + /// 组码 + /// 组码值 + public virtual void Add(int code, object obj) + { + Add(new TypedValue(code, obj)); + } + + #endregion + + #region 转换器 + /// + /// ResultBuffer 隐式转换到 TypedValueList + /// + /// ResultBuffer 实例 + public static implicit operator TypedValueList(ResultBuffer buffer) => new(buffer.AsArray()); + /// + /// TypedValueList 隐式转换到 TypedValue 数组 + /// + /// TypedValueList 实例 + public static implicit operator TypedValue[](TypedValueList values) => values.ToArray(); + /// + /// TypedValueList 隐式转换到 ResultBuffer + /// + /// TypedValueList 实例 + public static implicit operator ResultBuffer(TypedValueList values) => new(values); + /// + /// TypedValue 数组隐式转换到 TypedValueList + /// + /// TypedValue 数组 + public static implicit operator TypedValueList(TypedValue[] values) => new(values); + /// + /// 转换为字符串 + /// + /// ResultBuffer 字符串 + public override string ToString() + { + using ResultBuffer a = new(this); + return a.ToString(); + } + #endregion +} \ No newline at end of file diff --git a/src/CAD/IFox.CAD.Shared/ResultData/XRecordDataList.cs b/src/CAD/IFox.CAD.Shared/ResultData/XRecordDataList.cs new file mode 100644 index 0000000..708e155 --- /dev/null +++ b/src/CAD/IFox.CAD.Shared/ResultData/XRecordDataList.cs @@ -0,0 +1,67 @@ +namespace IFoxCAD.Cad; + +/// +/// 扩展字典数据封装类 +/// +public class XRecordDataList : TypedValueList +{ + #region 构造函数 + /// + /// 扩展字典数据封装类 + /// + public XRecordDataList() { } + + /// + /// 扩展字典数据封装类 + /// + public XRecordDataList(IEnumerable values) : base(values) { } + #endregion + + #region 添加数据 + /// + /// 添加数据 + /// + /// 组码 + /// 组码值 + public override void Add(int code, object obj) + { + if (code >= 1000) + throw new System.Exception("传入的组码值不是 XRecordData 有效范围!"); + + Add(new TypedValue(code, obj)); + } + + /// + /// 添加数据 + /// + /// dxfcode枚举值 + /// 组码值 + public void Add(DxfCode code, object obj) + { + Add((int)code, obj); + } + #endregion + + #region 转换器 + /// + /// ResultBuffer 隐式转换到 XRecordDataList + /// + /// ResultBuffer 实例 + public static implicit operator XRecordDataList(ResultBuffer buffer) => new(buffer.AsArray()); + /// + /// XRecordDataList 隐式转换到 TypedValue 数组 + /// + /// TypedValueList 实例 + public static implicit operator TypedValue[](XRecordDataList values) => values.ToArray(); + /// + /// XRecordDataList 隐式转换到 ResultBuffer + /// + /// TypedValueList 实例s + public static implicit operator ResultBuffer(XRecordDataList values) => new(values); + /// + /// TypedValue 数组隐式转换到 XRecordDataList + /// + /// TypedValue 数组 + public static implicit operator XRecordDataList(TypedValue[] values) => new(values); + #endregion +} \ No newline at end of file diff --git a/src/CAD/IFox.CAD.Shared/ResultData/XdataList.cs b/src/CAD/IFox.CAD.Shared/ResultData/XdataList.cs new file mode 100644 index 0000000..94f04d7 --- /dev/null +++ b/src/CAD/IFox.CAD.Shared/ResultData/XdataList.cs @@ -0,0 +1,146 @@ +namespace IFoxCAD.Cad; + +/// +/// 扩展数据封装类 +/// +public class XDataList : TypedValueList +{ + #region 构造函数 + /// + /// 扩展数据封装类 + /// + public XDataList() { } + + /// + /// 扩展数据封装类 + /// + public XDataList(IEnumerable values) : base(values) { } + #endregion + + #region 添加数据 + /// + /// 添加数据 + /// + /// 组码 + /// 组码值 + public override void Add(int code, object obj) + { + if (code < 1000 || code > 1071) + throw new System.Exception("传入的组码值不是 XData 有效范围!"); + + Add(new TypedValue(code, obj)); + } + + /// + /// 添加数据 + /// + /// dxfcode枚举值 + /// 组码值 + public void Add(DxfCode code, object obj) + { + Add((int)code, obj); + } + + /// + /// 是否含有注册名 + /// + /// 注册名 + public bool Contains(string appName) + { + bool result = false; + RangeTask(appName, (tv, state, i) => { + result = true; + state.Break(); + }); + return result; + } + + /// + /// 注册名下含有指定成员 + /// + /// 注册名 + /// 内容 + public bool Contains(string appName, object value) + { + bool result = false; + RangeTask(appName, (tv, state, i) => { + if (tv.Value.Equals(value)) + { + result = true; + state.Break(); + } + }); + return result; + } + + /// + /// 获取appName的索引区间 + /// + /// 注册名称 + /// 任务组码对象 + /// 返回任务组码的索引 + public List GetXdataAppIndex(string appName, DxfCode[] dxfCodes) + { + List indexs = new(); + RangeTask(appName, (tv, state, i) => { + if (dxfCodes.Contains((DxfCode)tv.TypeCode)) + indexs.Add(i); + }); + return indexs; + } + + /// + /// 区间任务 + /// + /// + void RangeTask(string appName, Action action) + { + LoopState state = new(); + // 在名称和名称之间找 + int appNameIndex = -1; + for (int i = 0; i < this.Count; i++) + { + if (this[i].TypeCode == (short)DxfCode.ExtendedDataRegAppName) + { + if (this[i].Value.ToString() == appName) + { + appNameIndex = i; + continue; + } + if (appNameIndex != -1)//找到了下一个名称 + break; + } + if (appNameIndex != -1) // 找下一个的时候,获取任务(移除)的对象 + { + action(this[i], state, i); + if (!state.IsRun) + break; + } + } + } + + #endregion + + #region 转换器 + /// + /// ResultBuffer 隐式转换到 XDataList + /// + /// ResultBuffer 实例 + public static implicit operator XDataList(ResultBuffer buffer) => new(buffer.AsArray()); + /// + /// XDataList 隐式转换到 TypedValue 数组 + /// + /// TypedValueList 实例 + public static implicit operator TypedValue[](XDataList values) => values.ToArray(); + /// + /// XDataList 隐式转换到 ResultBuffer + /// + /// TypedValueList 实例 + public static implicit operator ResultBuffer(XDataList values) => new(values); + /// + /// TypedValue 数组隐式转换到 XDataList + /// + /// TypedValue 数组 + public static implicit operator XDataList(TypedValue[] values) => new(values); + #endregion +} \ No newline at end of file diff --git a/src/CAD/IFox.CAD.Shared/Runtime/AOP.cs b/src/CAD/IFox.CAD.Shared/Runtime/AOP.cs new file mode 100644 index 0000000..4bb5cb3 --- /dev/null +++ b/src/CAD/IFox.CAD.Shared/Runtime/AOP.cs @@ -0,0 +1,99 @@ +// namespace IFoxCAD.Cad; +// using HarmonyLib; + +// public class IFoxRefuseInjectionTransaction : Attribute +// { +// /// +// /// 拒绝注入事务 +// /// +// public IFoxRefuseInjectionTransaction() +// { +// } +// } + +// public class AOP +// { +// /// +// /// 在此命名空间下的命令末尾注入清空事务栈函数 +// /// +// public static void Run(string nameSpace) +// { +// Dictionary cmdDic = new(); +// AutoClass.AppDomainGetTypes(type => { +// if (type.Namespace != nameSpace) +// return; +// // 类上面特性 +// if (type.IsClass) +// { +// var attr = type.GetCustomAttributes(true); +// if (RefuseInjectionTransaction(attr)) +// return; +// } + +// // 函数上面特性 +// var mets = type.GetMethods();// 获得它的成员函数 +// for (int ii = 0; ii < mets.Length; ii++) +// { +// var method = mets[ii]; +// // 找到特性,特性下面的方法要是Public,否则就被编译器优化掉了. +// var attr = method.GetCustomAttributes(true); +// for (int jj = 0; jj < attr.Length; jj++) +// if (attr[jj] is CommandMethodAttribute cmdAtt) +// { +// if (!RefuseInjectionTransaction(attr)) +// cmdDic.Add(cmdAtt.GlobalName, (cmdAtt, type, method)); +// } +// } +// }); + +// // 运行的命令写在了Test.dll,当然不是ifox.cad类库内了.... +// if (cmdDic.Count == 0) +// return; + +// var harmony = new Harmony(nameSpace); +// var mPrefix = SymbolExtensions.GetMethodInfo(() => IFoxCmdAddFirst());// 进入函数前 +// var mPostfix = SymbolExtensions.GetMethodInfo(() => IFoxCmdAddLast());// 进入函数后 +// var mp1 = new HarmonyMethod(mPrefix); +// var mp2 = new HarmonyMethod(mPostfix); + +// foreach (var item in cmdDic) +// { +// // 原函数执行(空间type,函数名) +// var mOriginal = AccessTools.Method(item.Value.MetType, item.Value.MetInfo.Name); +// // mOriginal.Invoke(); +// // 新函数执行:创造两个函数加入里面 +// var newMet = harmony.Patch(mOriginal, mp1, mp2); +// // newMet.Invoke(); +// } +// } + +// /// +// /// 拒绝注入事务 +// /// +// /// 属性 +// /// +// private static bool RefuseInjectionTransaction(object[] attr) +// { +// bool refuseInjectionTransaction = false; +// for (int kk = 0; kk < attr.Length; kk++) +// { +// if (attr[kk] is IFoxRefuseInjectionTransaction) +// { +// refuseInjectionTransaction = true; +// break; +// } +// } +// return refuseInjectionTransaction; +// } + +// public static void IFoxCmdAddFirst() +// { +// // 此生命周期会在静态事务栈上面,被无限延长 +// var _ = DBTrans.Top; +// } + +// public static void IFoxCmdAddLast() +// { +// DBTrans.FinishDatabase(); +// } +// } \ No newline at end of file diff --git a/src/CAD/IFox.CAD.Shared/Runtime/AcadEMR.cs b/src/CAD/IFox.CAD.Shared/Runtime/AcadEMR.cs new file mode 100644 index 0000000..4b8b106 --- /dev/null +++ b/src/CAD/IFox.CAD.Shared/Runtime/AcadEMR.cs @@ -0,0 +1,149 @@ +#if true +namespace IFoxCAD.Cad; + +using System.Diagnostics; + +// 作者: [VB.net]福萝卜 莱昂纳多·胖子 +// Email:oneeshine@163.com +// QQ: 461884072 +// 测试 2006-2019+ + +/// +/// 去教育版 +/// +/// +internal class AcadEMR +{ + /// + /// 释放库 + /// + /// 句柄 + /// + [DllImport("kernel32.dll", CharSet = CharSet.Auto)] + static extern IntPtr FreeLibrary(IntPtr loadLibraryIntPtr); + + /// + /// 获取一个应用程序或dll的模块句柄,要求已经载入 + /// + /// + /// + [DllImport("kernel32.dll", CharSet = CharSet.Auto, SetLastError = true)] + static extern IntPtr GetModuleHandle(string name); + + /// + /// 获取要引入的函数,将符号名或标识号转换为DLL内部地址 + /// + /// exe/dll句柄 + /// 接口名 + /// + [DllImport("kernel32.dll", SetLastError = true, CharSet = CharSet.Ansi, ExactSpelling = true)] + static extern IntPtr GetProcAddress(IntPtr hModule, string procName); + + /// + /// 虚拟保护 + /// + /// + /// + /// + /// + /// + [DllImport("kernel32.dll", CharSet = CharSet.Auto, SetLastError = true)] + static extern bool VirtualProtect(IntPtr lpAddress, IntPtr dwSize, uint flNewProtect, ref uint lpflOldProtect); + + + /// + /// 移除教育版 + /// + /// 打印出错信息 + public static void Remove(bool echoes = false) + { + var dllName = Env.GetAcapVersionDll(); + IntPtr moduleHandle = GetModuleHandle(dllName); + if (moduleHandle == IntPtr.Zero) + { + if (echoes) + Env.Printl(typeof(AcadEMR).FullName + "." + nameof(Remove) + "找不到模块:" + dllName); + return; + } + + string funcname = System.Text.Encoding.Unicode.GetString(new byte[] { 63 }); + if (IntPtr.Size == 4) + funcname += "isEMR@AcDbDatabase@@QBE_NXZ"; + else + funcname += "isEMR@AcDbDatabase@@QEBA_NXZ"; + + IntPtr funcAdress = GetProcAddress(moduleHandle, funcname); + if (funcAdress == IntPtr.Zero) + { + if (echoes) + Env.Printl("无法找指定函数:" + funcname); + return; + } + + IntPtr ptr; + if (IntPtr.Size == 4) + ptr = new IntPtr(funcAdress.ToInt32() + 3); + else + ptr = new IntPtr(funcAdress.ToInt64() + 4); + + if (!CheckFunc(ref ptr, 51, 2))// 08 通过此处 + if (echoes) + Env.Printl("无法验证函数体:0x33"); + IntPtr destPtr = ptr; + + if (!CheckFunc(ref ptr, 57, 6))// 08 无法通过此处,所以只是打印提示 + if (echoes) + Env.Printl("无法验证函数体:0x39"); + if (!CheckFunc(ref ptr, 15, 2))// 08 无法通过此处,所以只是打印提示 + if (echoes) + Env.Printl("无法验证函数体:0x0F"); + + uint flag = default; + uint tccc = default; + + IntPtr ip100 = new(100); + if (!VirtualProtect(destPtr, ip100, 64, ref flag))// 修改内存权限 + { + if (echoes) + Env.Printl("内存模式修改失败!"); + return; + } + + Marshal.WriteByte(destPtr, 137); + VirtualProtect(destPtr, ip100, flag, ref tccc);// 恢复内存权限 + } + + /// + /// 验证函数体 + /// + /// + /// + /// + /// + static bool CheckFunc(ref IntPtr adress, byte val, int len) + { + if (Marshal.ReadByte(adress) == 233) + { + if (IntPtr.Size == 4) + { + var pass = Marshal.ReadInt32(new IntPtr(adress.ToInt32() + 1)); + adress = new IntPtr(adress.ToInt32() + pass + 5); + } + else + { + var pass = Marshal.ReadInt64(new IntPtr(adress.ToInt64() + 1)); + adress = new IntPtr(adress.ToInt64() + pass + 5); + } + } + if (Marshal.ReadByte(adress) == val) + { + if (IntPtr.Size == 4) + adress = new IntPtr(adress.ToInt32() + len); + else + adress = new IntPtr(adress.ToInt64() + len); + return true; + } + return false; + } +} +#endif \ No newline at end of file diff --git a/src/CAD/IFox.CAD.Shared/Runtime/AcadVersion.cs b/src/CAD/IFox.CAD.Shared/Runtime/AcadVersion.cs new file mode 100644 index 0000000..61aa620 --- /dev/null +++ b/src/CAD/IFox.CAD.Shared/Runtime/AcadVersion.cs @@ -0,0 +1,70 @@ +namespace IFoxCAD.Cad; + +/// +/// cad版本号类 +/// +public static class AcadVersion +{ + private static readonly string _pattern = @"Autodesk\\AutoCAD\\R(\d+)\.(\d+)\\.*?"; + + /// + /// 所有安装的cad的版本号 + /// + public static List Versions + { + get + { + string[] copys = Registry.LocalMachine + .OpenSubKey(@"SOFTWARE\Autodesk\Hardcopy") + .GetValueNames(); + + var _versions = new List(); + for (int i = 0; i < copys.Length; i++) + { + if (!Regex.IsMatch(copys[i], _pattern)) + continue; + + var gs = Regex.Match(copys[i], _pattern).Groups; + var ver = new CadVersion + { + ProductRootKey = copys[i], + ProductName = Registry.LocalMachine + .OpenSubKey("SOFTWARE") + .OpenSubKey(copys[i]) + .GetValue("ProductName") + .ToString(), + + Major = int.Parse(gs[1].Value), + Minor = int.Parse(gs[2].Value), + }; + _versions.Add(ver); + } + return _versions; + } + } + + /// 已打开的cad的版本号 + /// 已打开cad的application对象 + /// cad版本号对象 + public static CadVersion? FromApp(object app) + { + if (app == null) + throw new ArgumentNullException(nameof(app)); + + string acver = app.GetType() + .InvokeMember( + "Version", + BindingFlags.GetProperty, + null, + app, + new object[0]).ToString(); + + var gs = Regex.Match(acver, @"(\d+)\.(\d+).*?").Groups; + int major = int.Parse(gs[1].Value); + int minor = int.Parse(gs[2].Value); + for (int i = 0; i < Versions.Count; i++) + if (Versions[i].Major == major && Versions[i].Minor == minor) + return Versions[i]; + return null; + } +} \ No newline at end of file diff --git a/src/CAD/IFox.CAD.Shared/Runtime/AssemInfo.cs b/src/CAD/IFox.CAD.Shared/Runtime/AssemInfo.cs new file mode 100644 index 0000000..23e5c7d --- /dev/null +++ b/src/CAD/IFox.CAD.Shared/Runtime/AssemInfo.cs @@ -0,0 +1,85 @@ +namespace IFoxCAD.Cad; + +/// +/// 程序集信息 +/// +[Serializable] +public struct AssemInfo +{ + /// + /// 注册名 + /// + public string Name; + + /// + /// 程序集全名 + /// + public string Fullname; + + /// + /// 程序集路径 + /// + public string Loader; + + /// + /// 加载方式 + /// + public AssemLoadType LoadType; + + /// + /// 程序集说明 + /// + public string Description; +} + + +/// +/// 程序集加载类型 +/// +public enum AssemLoadType +{ + /// + /// 启动 + /// + Startting = 2, + + /// + /// 随命令 + /// + ByCommand = 12, + + /// + /// 无效 + /// + Disabled = 20 +} + + +/// +/// 注册中心配置信息 +/// +public enum AutoRegConfig +{ + /// + /// 不进行任何操作 + /// + Undefined = 0, + /// + /// 注册表 + /// + Regedit = 1, + /// + /// 反射特性 + /// + ReflectionAttribute = 2, + /// + /// 反射接口 + /// + ReflectionInterface = 4, + /// + /// 移除教育版 + /// + RemoveEMR = 8, + + All = Regedit | ReflectionAttribute | ReflectionInterface | RemoveEMR, +} \ No newline at end of file diff --git a/src/CAD/IFox.CAD.Shared/Runtime/AutoRegAssem.cs b/src/CAD/IFox.CAD.Shared/Runtime/AutoRegAssem.cs new file mode 100644 index 0000000..e8bb720 --- /dev/null +++ b/src/CAD/IFox.CAD.Shared/Runtime/AutoRegAssem.cs @@ -0,0 +1,180 @@ +namespace IFoxCAD.Cad; + + +/// +/// 注册中心 +/// +/// 初始化程序集信息写入注册表并反射特性和接口
    +/// 启动cad后的执行顺序为:
    +/// 1:程序集配置中心构造函数
    +/// 2:特性..(多个)
    +/// 3:接口..(多个)
    +///
    +///
    +public abstract class AutoRegAssem : IExtensionApplication +{ + #region 字段 + readonly AssemInfo _info; + readonly AutoReflection? _autoRef; + #endregion + + #region 静态方法 + /// + /// 程序集的路径 + /// + public static FileInfo Location => new(Assembly.GetCallingAssembly().Location); + /// + /// 程序集的目录 + /// + public static DirectoryInfo CurrDirectory => Location.Directory; + /// + /// 获取程序集的目录 + /// + /// 程序集 + /// 路径对象 + public static DirectoryInfo GetDirectory(Assembly? assem) + { + if (assem is null) + throw new(nameof(assem)); + return new FileInfo(assem.Location).Directory; + } + #endregion + + #region 构造函数 + /// + /// 注册中心 + /// + /// 配置项目 + public AutoRegAssem(AutoRegConfig autoRegConfig) + { + var assem = Assembly.GetCallingAssembly(); + _info = new() + { + Loader = assem.Location, + Fullname = assem.FullName, + Name = assem.GetName().Name, + LoadType = AssemLoadType.Startting + }; + + if ((autoRegConfig & AutoRegConfig.Regedit) == AutoRegConfig.Regedit) + { + if (!SearchForReg()) + RegApp(); + } + + if ((autoRegConfig & AutoRegConfig.RemoveEMR) == AutoRegConfig.RemoveEMR) + AcadEMR.Remove(); + + // 实例化了 AutoClass 之后会自动执行 IFoxAutoGo 接口下面的类, + // 以及自动执行特性 [IFoxInitialize] + // 类库用户不在此处进行其他代码,而是实现特性 + if ((autoRegConfig & AutoRegConfig.ReflectionInterface) == AutoRegConfig.ReflectionInterface || + (autoRegConfig & AutoRegConfig.ReflectionAttribute) == AutoRegConfig.ReflectionAttribute) + { + _autoRef = new AutoReflection(_info.Name, autoRegConfig); + _autoRef.Initialize(); + } + } + #endregion + + #region RegApp + + /// + /// 获取当前cad注册表位置 + /// + /// 打开权限 + /// + public static RegistryKey GetAcAppKey(bool writable = true) + { + RegistryKey? ackey = null; + var hc = HostApplicationServices.Current; +#if acad || zcad // 中望此处缺乏测试 +#if NET35 + string key = hc.RegistryProductRootKey; +#else + string key = hc.UserRegistryProductRootKey; +#endif + ackey = Registry.CurrentUser.OpenSubKey(key, writable); +#endif + +#if gcad + // gcad 此处是否仍然有bug? 等待其他人测试反馈 + var key = hc.RegistryProductRootKey; // 浩辰2020读出来是"" + string regedit = ""; + if (Env.GetAcadVersion() == 2019) + regedit = @"Software\Gstarsoft\GstarCAD\R" + 19 + @"\zh-CN"; + if (Env.GetAcadVersion() == 2020) + regedit = @"Software\Gstarsoft\GstarCAD\B" + 20 + @"\zh-CN"; + ackey = Registry.CurrentUser.OpenSubKey(regedit, writable); +#endif + + return ackey.CreateSubKey("Applications"); + } + + /// + /// 卸载注册表信息 + /// + public bool UnRegApp() + { + var appkey = GetAcAppKey(); + if (appkey.SubKeyCount == 0) + return false; + + var regApps = appkey.GetSubKeyNames(); + if (regApps.Contains(_info.Name)) + { + appkey.DeleteSubKey(_info.Name, false); + return true; + } + return false; + } + + /// + /// 是否已经存在注册表 + /// + /// + bool SearchForReg() + { + // 在使用netloadx的时候,此处注册表是失效的,具体原因要进行netloadx测试 + var appkey = GetAcAppKey(); + if (appkey.SubKeyCount == 0) + return false; + + var regApps = appkey.GetSubKeyNames(); + if (regApps.Contains(_info.Name)) + { + // 20220409 bug:文件名相同,路径不同,需要判断路径 + var info = appkey.OpenSubKey(_info.Name); + return info.GetValue("LOADER")?.ToString().ToLower() == _info.Loader.ToLower(); + } + return false; + } + + /// + /// 在注册表写入自动加载的程序集信息 + /// + public void RegApp() + { + var appkey = GetAcAppKey(); + var rk = appkey.CreateSubKey(_info.Name); + rk.SetValue("DESCRIPTION", _info.Fullname, RegistryValueKind.String); + rk.SetValue("LOADCTRLS", _info.LoadType, RegistryValueKind.DWord); + rk.SetValue("LOADER", _info.Loader, RegistryValueKind.String); + rk.SetValue("MANAGED", 1, RegistryValueKind.DWord); + appkey.Close(); + } + + // 这里的是不会自动执行的 + public void Initialize() + { + } + public void Terminate() + { + } + + ~AutoRegAssem() + { + _autoRef?.Terminate(); + } + #endregion RegApp +} \ No newline at end of file diff --git a/src/CAD/IFox.CAD.Shared/Runtime/CadVersion.cs b/src/CAD/IFox.CAD.Shared/Runtime/CadVersion.cs new file mode 100644 index 0000000..d9614bf --- /dev/null +++ b/src/CAD/IFox.CAD.Shared/Runtime/CadVersion.cs @@ -0,0 +1,92 @@ +namespace IFoxCAD.Cad; + +#if ac2009 + +public class CadVersion +{ + /// + /// 主版本 + /// + public int Major { get; set; } + + /// + /// 次版本 + /// + public int Minor { get; set; } + + /// + /// 版本号 + /// + public double ProgId => double.Parse($"{Major}.{Minor}"); + + /// + /// 注册表名称 + /// + public string? ProductName { get; set; } + + /// + /// 注册表位置 + /// + public string? ProductRootKey { get; set; } + + /// + /// 转换为字符串 + /// + /// 表示版本号的字符串 + public override string ToString() + { + return $"名称:{ProductName}\n版本号:{ProgId}\n注册表位置:{ProductRootKey}"; + } + // public override bool Equals(object obj) + // { + // return base.Equals(obj); + // } + + // public override int GetHashCode() + // { + // return base.GetHashCode(); + // } + + // // public override string ToString() + // // { + // // return base.ToString(); + // // } +} +#else +public record CadVersion +{ + /// + /// 主版本 + /// + public int Major; + + /// + /// 次版本 + /// + public int Minor; + + /// + /// 版本号 + /// + public double ProgId => double.Parse($"{Major}.{Minor}"); + + /// + /// 注册表名称 + /// + public string? ProductName; + + /// + /// 注册表位置 + /// + public string? ProductRootKey; + + /// + /// 转换为字符串 + /// + /// 表示版本号的字符串 + public override string ToString() + { + return $"名称:{ProductName}\n版本号:{ProgId}\n注册表位置:{ProductRootKey}"; + } +} +#endif \ No newline at end of file diff --git a/src/CAD/IFox.CAD.Shared/Runtime/DBTrans.cs b/src/CAD/IFox.CAD.Shared/Runtime/DBTrans.cs new file mode 100644 index 0000000..90fc981 --- /dev/null +++ b/src/CAD/IFox.CAD.Shared/Runtime/DBTrans.cs @@ -0,0 +1,711 @@ +namespace IFoxCAD.Cad; + +using System.Diagnostics; +using System.IO; +using System.Threading; +using System.Windows.Forms; + +/// +/// 事务栈 +/// 隐匿事务在数据库其中担任的角色 +/// +[DebuggerDisplay("{DebuggerDisplay,nq}")] +[DebuggerTypeProxy(typeof(DBTrans))] +public class DBTrans : IDisposable +{ + [DebuggerBrowsable(DebuggerBrowsableState.Never)] + string DebuggerDisplay => ToString(" | "); + + #region 私有字段 + /// + /// 事务栈 + /// + static readonly Stack _dBTrans = new(); + /// + /// 文档锁 + /// + readonly DocumentLock? _documentLock; + /// + /// 是否提交事务 + /// + bool _commit; + /// + /// 文件名 + /// + readonly string? _fileName; + #endregion + + #region 公开属性 + /// + /// 返回当前事务 + /// + public static DBTrans Top + { + get + { + /* + * 0x01 + * 事务栈上面有事务,这个事务属于当前文档, + * 那么直接提交原本事务然后再开一个(一直把栈前面的同数据库提交清空) + * 那不就发生跨事务读取图元了吗?....否决 + * + * 0x02 + * 跨文档事务出错 Autodesk.AutoCAD.Runtime.Exception:“eNotFromThisDocument” + * Curves.GetEntities()会从Top获取事务(Top会new一个),此时会是当前文档; + * 然后命令文中发生了 using DBTrans tr = new(); + * 当退出命令此事务释放,但是从来不释放Top, + * 然后我新建了一个文档,再进行命令=>又进入Top,Top返回了前一个文档的事务 + * 因此所以无法清理栈,所以Dispose不触发,导致无法刷新图元和Ctrl+Z出错 + * 所以用AOP方式修复 + * + * 0x03 + * 经过艰苦卓绝的测试,aop模式由于不能断点调试,所以暂时放弃。 + */ + + // 由于大量的函数依赖本属性,强迫用户先开启事务 + if (_dBTrans.Count == 0) + throw new ArgumentNullException("事务栈没有任何事务,请在调用前创建:" + nameof(DBTrans)); + var trans = _dBTrans.Peek(); + return trans; + } + } + /// + /// 文档 + /// + public Document? Document { get; private set; } + /// + /// 命令行 + /// + public Editor? Editor { get; private set; } + /// + /// 事务管理器 + /// + public Transaction Transaction { get; private set; } + /// + /// 数据库 + /// + public Database Database { get; private set; } + #endregion + + #region 构造函数 + /// + /// 事务栈 + /// 默认构造函数,默认为打开当前文档,默认提交事务 + /// + /// 要打开的文档 + /// 事务是否提交 + /// 是否锁文档 + public DBTrans(Document? doc = null, bool commit = true, bool doclock = false) + { + Document = doc ?? Acap.DocumentManager.MdiActiveDocument; + Database = Document.Database; + Editor = Document.Editor; + Transaction = Database.TransactionManager.StartTransaction(); + _commit = commit; + if (doclock) + _documentLock = Document.LockDocument(); + + _dBTrans.Push(this); + } + + /// + /// 事务栈 + /// 打开数据库,默认提交事务 + /// + /// 要打开的数据库 + /// 事务是否提交 + public DBTrans(Database database, bool commit = true) + { + Database = database; + Transaction = Database.TransactionManager.StartTransaction(); + _commit = commit; + _dBTrans.Push(this); + } + + /// + /// 事务栈 + /// 打开文件,默认提交事务 + /// + /// 要打开的文件名 + /// 事务是否提交 + /// 开图模式 + /// 密码 + /// 后台打开false;前台打开true(必须设置CommandFlags.Session) + public DBTrans(string fileName, + bool commit = true, + FileOpenMode fileOpenMode = FileOpenMode.OpenForReadAndWriteNoShare, + string? password = null, + bool activeOpen = false) + { + if (fileName == null || string.IsNullOrEmpty(fileName.Trim())) + throw new ArgumentNullException(nameof(fileName)); + + _fileName = fileName.Replace("/", "\\");// doc.Name总是"D:\\JX.dwg" + + // 此处若为失败的文件名,那么保存的时候就会丢失名称, + // 因此用 _fileName 储存 + if (!File.Exists(_fileName)) + { + if (activeOpen) + { + throw new IOException("错误:事务栈明确为前台开图时,文件不存在"); + } + else + { + // cad08测试: + // 第2个参数使用false,将导致关闭cad的时候出现致命错误: + // Unhandled Access Violation Reading Ox113697a0 Exception at 4b4154h + Database = new Database(true, true); + } + } + else + { + var doc = Acap.DocumentManager + .Cast() + .FirstOrDefault(doc => !doc.IsDisposed && doc.Name == _fileName); + + if (activeOpen) + { + if (doc is null) + { + try + { + // 设置命令标记: CommandFlags.Session + // 若没有设置: Open()之后的会进入中断状态(不会执行,直到切换文档ctrl+tab或者关闭文档) + doc = Acap.DocumentManager.Open(fileName, fileOpenMode == FileOpenMode.OpenForReadAndReadShare, password); + } + catch (Exception e) + { + throw new IOException($"错误:此文件打开错误:{fileName}\n错误信息:{e.Message}"); + } + } + // 设置命令标记: CommandFlags.Session + // 若没有设置: doc.IsActive 会异常 + if (!doc.IsActive) + Acap.DocumentManager.MdiActiveDocument = doc; + + // Open()是跨文档,所以必须要锁文档 + // 否则 Editor?.Redraw() 的 tm.QueueForGraphicsFlush() 将报错提示文档锁 + _documentLock = doc.LockDocument(); + + Database = doc.Database; + Document = doc; + Editor = doc.Editor; + } + else + { + if (doc is null) + { + Database = new Database(false, true); + if (Path.GetExtension(_fileName).ToLower().Contains("dxf")) + { + Database.DxfIn(_fileName, null); + } + else + { +#if ac2008 + Database.ReadDwgFile(_fileName, FileOpenModeHelper.GetFileShare(fileOpenMode), true, password); +#else + Database.ReadDwgFile(_fileName, fileOpenMode, true, password); +#endif + } + Database.CloseInput(true); + } + else + { + Database = doc.Database; + Document = doc; + Editor = doc.Editor; + } + } + } + + Transaction = Database.TransactionManager.StartTransaction(); + _commit = commit; + _dBTrans.Push(this); + } + #endregion + + #region 类型转换 + /// + /// 隐式转换为Transaction + /// + /// 事务管理器 + /// 事务管理器 + public static implicit operator Transaction(DBTrans tr) + { + return tr.Transaction; + } + #endregion + + #region 符号表 + + /// + /// 块表 + /// + public SymbolTable BlockTable => _BlockTable ??= new(this, Database.BlockTableId); + SymbolTable? _BlockTable; + /// + /// 当前绘图空间 + /// + public BlockTableRecord CurrentSpace => BlockTable.GetRecord(Database.CurrentSpaceId)!; + /// + /// 模型空间 + /// + public BlockTableRecord ModelSpace => BlockTable.GetRecord(BlockTable.CurrentSymbolTable[BlockTableRecord.ModelSpace])!; + /// + /// 图纸空间 + /// + public BlockTableRecord PaperSpace => BlockTable.GetRecord(BlockTable.CurrentSymbolTable[BlockTableRecord.PaperSpace])!; + /// + /// 层表 + /// + public SymbolTable LayerTable => _LayerTable ??= new(this, Database.LayerTableId); + SymbolTable? _LayerTable; + /// + /// 文字样式表 + /// + public SymbolTable TextStyleTable => _TextStyleTable ??= new(this, Database.TextStyleTableId); + SymbolTable? _TextStyleTable; + /// + /// 注册应用程序表 + /// + public SymbolTable RegAppTable => _RegAppTable ??= new(this, Database.RegAppTableId); + SymbolTable? _RegAppTable; + /// + /// 标注样式表 + /// + public SymbolTable DimStyleTable => _DimStyleTable ??= new(this, Database.DimStyleTableId); + SymbolTable? _DimStyleTable; + /// + /// 线型表 + /// + public SymbolTable LinetypeTable => _LinetypeTable ??= new(this, Database.LinetypeTableId); + SymbolTable? _LinetypeTable; + /// + /// 用户坐标系表 + /// + public SymbolTable UcsTable => _UcsTable ??= new(this, Database.UcsTableId); + SymbolTable? _UcsTable; + /// + /// 视图表 + /// + public SymbolTable ViewTable => _ViewTable ??= new(this, Database.ViewTableId); + SymbolTable? _ViewTable; + /// + /// 视口表 + /// + public SymbolTable ViewportTable => _ViewportTable ??= new(this, Database.ViewportTableId); + SymbolTable? _ViewportTable; + #endregion + + #region 字典 + /// + /// 命名对象字典 + /// + public DBDictionary NamedObjectsDict => GetObject(Database.NamedObjectsDictionaryId)!; + /// + /// 组字典 + /// + public DBDictionary GroupDict => GetObject(Database.GroupDictionaryId)!; + /// + /// 多重引线样式字典 + /// + public DBDictionary MLeaderStyleDict => GetObject(Database.MLeaderStyleDictionaryId)!; + /// + /// 多线样式字典 + /// + public DBDictionary MLStyleDict => GetObject(Database.MLStyleDictionaryId)!; + /// + /// 材质字典 + /// + public DBDictionary MaterialDict => GetObject(Database.MaterialDictionaryId)!; + /// + /// 表格样式字典 + /// + public DBDictionary TableStyleDict => GetObject(Database.TableStyleDictionaryId)!; + /// + /// 视觉样式字典 + /// + public DBDictionary VisualStyleDict => GetObject(Database.VisualStyleDictionaryId)!; + /// + /// 颜色字典 + /// + public DBDictionary ColorDict => GetObject(Database.ColorDictionaryId)!; + /// + /// 打印设置字典 + /// + public DBDictionary PlotSettingsDict => GetObject(Database.PlotSettingsDictionaryId)!; + /// + /// 打印样式表名字典 + /// + public DBDictionary PlotStyleNameDict => GetObject(Database.PlotStyleNameDictionaryId)!; + /// + /// 布局字典 + /// + public DBDictionary LayoutDict => GetObject(Database.LayoutDictionaryId)!; + +#if !zcad // 中望官方的问题 + /// + /// 数据链接字典 + /// + public DBDictionary DataLinkDict => GetObject(Database.DataLinkDictionaryId)!; + +#if !ac2009 + /// + /// 详细视图样式字典 + /// + public DBDictionary DetailViewStyleDict => GetObject(Database.DetailViewStyleDictionaryId)!; + /// + /// 剖面视图样式字典 + /// + public DBDictionary SectionViewStyleDict => GetObject(Database.SectionViewStyleDictionaryId)!; +#endif +#endif + #endregion + + #region 获取对象 + /// + /// 根据对象id获取图元对象 + /// + /// 要获取的图元对象的类型 + /// 对象id + /// 打开模式,默认为只读 + /// 是否打开已删除对象,默认为不打开 + /// 是否打开锁定图层对象,默认为不打开 + /// 图元对象,类型不匹配时返回 + public T? GetObject(ObjectId id, + OpenMode openMode = OpenMode.ForRead, + bool openErased = false, + bool openLockedLayer = false) where T : DBObject + { + return Transaction.GetObject(id, openMode, openErased, openLockedLayer) as T; + } + + /// + /// 根据对象句柄字符串获取对象Id + /// + /// 句柄字符串 + /// 对象id + public ObjectId GetObjectId(string handleString) + { + var hanle = new Handle(Convert.ToInt64(handleString, 16)); + // return Database.GetObjectId(false, hanle, 0); + return DBTransHelper.TryGetObjectId(Database, hanle); + } + #endregion + + #region 保存文件 + /// + /// 保存文件 + /// + /// + public void SaveDwgFile(DwgVersion version = DwgVersion.AC1800) + { + SaveFile(version); + } + + /// + /// 保存文件
    + ///
    + /// 默认2004dwg;若保存dxf则需要在路径输入扩展名 + /// 为true时候无效,将变为自动识别环境变量 + /// 另存为文件,前台将调用时它将无效,将变为弹出面板 + /// 保存路径失败的提示 + public void SaveFile(DwgVersion version = DwgVersion.AC1800, + bool automatic = true, + string? saveAsFile = null, + bool echoes = true) + { + // 遍历当前所有文档,文档必然是前台的 + Document? doc = null; + foreach (Document docItem in Acap.DocumentManager) + { + if (docItem.Database.Filename == this.Database.Filename) + { + doc = docItem; + break; + } + } + // 前台开图,使用命令保存;不需要切换文档 + if (doc != null) + { + if (saveAsFile == null) + doc.SendStringToExecute("_qsave\n", false, true, true); + else + /// 无法把 给这个面板 + doc.SendStringToExecute($"_Saveas\n", false, true, true); + return; + } + + // 后台开图,用数据库保存 + string? fileMsg; + bool creatFlag = false; + saveAsFile = saveAsFile?.Trim(); + if (string.IsNullOrEmpty(saveAsFile)) + { + fileMsg = _fileName; + saveAsFile = fileMsg; + //creatFlag = true; + } + else + { + fileMsg = saveAsFile; + + // 路径失败也保存到桌面 + var path = Path.GetDirectoryName(saveAsFile); + if (string.IsNullOrEmpty(path)) + { + creatFlag = true; + } + else if (!Directory.Exists(path)) + { + try { Directory.CreateDirectory(path); } + catch { creatFlag = true; } + } + + // 文件名缺失时 + if (!creatFlag && + string.IsNullOrEmpty(Path.GetFileName(saveAsFile).Trim())) + creatFlag = true; + } + if (saveAsFile != null) + { + var fileNameWith = Path.GetFileNameWithoutExtension(saveAsFile).Trim(); + if (string.IsNullOrEmpty(fileNameWith)) + creatFlag = true; + } + else + { + creatFlag = true; + } + + if (creatFlag) + { + var (error, file) = GetOrCreateSaveAsFile(); + if (echoes && error) + MessageBox.Show($"错误参数:\n{fileMsg}\n\n它将保存:\n{file}", "错误的文件路径"); + saveAsFile = file; + } + + if (Path.GetExtension(saveAsFile).ToLower().Contains("dxf")) + { + // dxf用任何版本号都会报错 +#if acad || gcad + Database.DxfOut(saveAsFile, 7, true); +#endif + +#if zcad // 中望这里没有测试 + Database.DxfOut(saveAsFile, 7, version, true); +#endif + return; + } + + if (automatic) + version = Env.GetDefaultDwgVersion(); + + // dwg需要版本号,而dxf不用,dwg用dxf版本号会报错 + // 若扩展名和版本号冲突,按照扩展名为准 + if (version.IsDxfVersion()) + version = DwgVersion.Current; + + Database.SaveAs(saveAsFile, version); + } + + + /// + /// 获取文件名,无效的话就制造 + /// + /// + (bool error, string path) GetOrCreateSaveAsFile() + { + var file = Database.Filename; + if (!string.IsNullOrEmpty(file)) + return (false, file); + + // 为了防止用户输入了错误的路径造成无法保存, + // 所以此处将进行保存到桌面, + // 而不是弹出警告就结束 + // 防止前台关闭了所有文档导致没有Editor,所以使用 MessageBox 发送警告 + var fileName = Path.GetFileNameWithoutExtension(_fileName).Trim(); + var fileExt = Path.GetExtension(_fileName); + + if (string.IsNullOrEmpty(fileName)) + fileName = DateTime.Now.ToString("--yyMMdd--hhmmssffff"); + if (string.IsNullOrEmpty(fileExt)) + fileExt = ".dwg"; + + // 构造函数(fileName)用了不存在的路径进行后台打开,就会出现此问题 + // 测试命令 FileNotExist + var dir = Environment.GetFolderPath(Environment.SpecialFolder.DesktopDirectory) + + "\\后台保存出错的文件\\"; + + if (!Directory.Exists(dir)) + Directory.CreateDirectory(dir); + + file = dir + fileName + fileExt; + while (File.Exists(file)) + { + var time = DateTime.Now.ToString("--yyMMdd--hhmmssffff"); + file = dir + fileName + time + fileExt; + Thread.Sleep(100); + } + return (true, file); + } + + #endregion + + #region 前台后台任务 + /// + /// 前台后台任务分别处理 + /// + /// + /// 备注:
    + /// 0x01 文字偏移问题主要出现线性引擎函数上面,
    + /// 在 参照绑定/深度克隆 的底层共用此函数导致
    + /// 0x02 后台是利用前台当前数据库进行处理的
    + /// 0x03 跨进程通讯暂无测试(可能存在bug)
    + ///
    + /// 委托 + /// 开启单行文字偏移处理 + public void Task(Action action, bool handlingDBTextDeviation = true) + { + if (action == null) + throw new ArgumentNullException(nameof(action)); + + // 前台开图 || 后台直接处理 + if (Document != null || !handlingDBTextDeviation) + { + action.Invoke(); + return; + } + + // 后台 + // 这种情况发生在关闭了所有文档之后,进行跨进程通讯 + // 此处要先获取激活的文档,不能直接获取当前数据库否则异常 + var dm = Acap.DocumentManager; + var doc = dm.MdiActiveDocument; + if (doc == null) + { + action.Invoke(); + return; + } + // 处理单行文字偏移 + // 前台绑定参照的时候不能用它,否则抛出异常:eWasErased + // 所以本函数自动识别前后台做处理 + var dbBak = doc.Database; + HostApplicationServices.WorkingDatabase = Database; + action.Invoke(); + HostApplicationServices.WorkingDatabase = dbBak; + } + #endregion + + #region IDisposable接口相关函数 + /// + /// 取消事务 + /// + public void Abort() + { + _commit = false; + Dispose(); + } + + /// + /// 提交事务 + /// + public void Commit() + { + _commit = true; + Dispose(); + } + + public bool IsDisposed { get; private set; } = false; + + /// + /// 手动调用释放 + /// + public void Dispose() + { + Dispose(true); + GC.SuppressFinalize(this); + } + + /// + /// 析构函数调用释放 + /// + ~DBTrans() + { + Dispose(false); + } + + protected virtual void Dispose(bool disposing) + { + /* 事务dispose流程: + * 1. 根据传入的参数确定是否提交,true为提交,false为不提交 + * 2. 如果锁文档就将文档锁dispose + * 3. 不管是否提交,既然进入dispose,就要将事务栈的当前事务弹出 + * 注意这里的事务栈不是cad的事务管理器,而是dbtrans的事务 + * 4. 清理非托管的字段 + */ + + // 不重复释放,并设置已经释放 + if (IsDisposed) return; + IsDisposed = true; + + // 致命错误时候此处是空,直接跳过 + if (Transaction != null) + { + if (_commit) + { + // 刷新队列(后台不刷新) + Editor?.Redraw(); + // 调用cad的事务进行提交,释放托管状态(托管对象) + Transaction.Commit(); + } + else + { + // 否则取消所有的修改 + Transaction.Abort(); + } + + // 将cad事务进行销毁 + if (!Transaction.IsDisposed) + Transaction.Dispose(); + } + // 将文档锁销毁 + _documentLock?.Dispose(); + + // 将当前事务栈弹栈 + _dBTrans.Pop(); + } + #endregion + + #region ToString + public override string ToString() + { + return ToString(null, null); + } + public string ToString(IFormatProvider? provider) + { + return ToString(null, provider); + } + public string ToString(string? format = null, IFormatProvider? formatProvider = null) + { + List lines = new(); + lines.Add($"StackCount = {_dBTrans.Count}"); + lines.Add($"_fileName = \"{_fileName}\""); + lines.Add($"_commit = {_commit}"); + lines.Add($"_documentLock = {_documentLock != null}"); + + lines.Add($"Document = {Document != null}"); + lines.Add($"Editor = {Editor != null}"); + lines.Add($"Transaction = {Transaction != null}"); + lines.Add($"Database = {Database != null}"); + + if (!string.IsNullOrEmpty(format)) + return string.Join(format, lines.ToArray()); + + return string.Join("\n", lines.ToArray()); + } + #endregion +} \ No newline at end of file diff --git a/src/CAD/IFox.CAD.Shared/Runtime/Env.cs b/src/CAD/IFox.CAD.Shared/Runtime/Env.cs new file mode 100644 index 0000000..f80a4ee --- /dev/null +++ b/src/CAD/IFox.CAD.Shared/Runtime/Env.cs @@ -0,0 +1,749 @@ +using System.Diagnostics; + +namespace IFoxCAD.Cad; + +/// +/// 系统管理类 +/// +/// 封装了一些系统 osmode;cmdecho;dimblk 系统变量
    +/// 封装了常用的 文档 编辑器 数据库等对象为静态变量
    +/// 封装了配置页面的注册表信息获取函数 +///
    +///
    +public static class Env +{ + #region Goal + + /// + /// 当前的数据库 + /// + public static Database Database => HostApplicationServices.WorkingDatabase; + + /// + /// 当前文档 + /// + public static Document Document => Acap.DocumentManager.MdiActiveDocument; + + /// + /// 编辑器对象 + /// + public static Editor Editor => Document.Editor; + + /// + /// 图形管理器 + /// + public static Manager GsManager => Document.GraphicsManager; + + #endregion Goal + + #region Preferences + +#if !zcad // 中望官方的问题 + /// + /// 获取当前配置的数据 + /// + /// 小节名 + /// 数据名 + /// 对象 + public static object GetCurrentProfileProperty(string subSectionName, string propertyName) + { + UserConfigurationManager ucm = Acap.UserConfigurationManager; + IConfigurationSection cpf = ucm.OpenCurrentProfile(); + IConfigurationSection ss = cpf.OpenSubsection(subSectionName); + return ss.ReadProperty(propertyName, ""); + } + + + /// + /// 获取对话框配置的数据 + /// + /// 对话框对象 + /// 配置项 + public static IConfigurationSection GetDialogSection(object dialog) + { + UserConfigurationManager ucm = Acap.UserConfigurationManager; + IConfigurationSection ds = ucm.OpenDialogSection(dialog); + return ds; + } + + /// + /// 获取公共配置的数据 + /// + /// 数据名 + /// 配置项 + public static IConfigurationSection GetGlobalSection(string propertyName) + { + UserConfigurationManager ucm = Acap.UserConfigurationManager; + IConfigurationSection gs = ucm.OpenGlobalSection(); + IConfigurationSection ss = gs.OpenSubsection(propertyName); + return ss; + } +#endif + #endregion Preferences + + #region Enum + /// + /// 控制在AutoLISP的command函数运行时AutoCAD是否回显提示和输入, 为显示, 为不显示 + /// + public static bool CmdEcho + { + get => Convert.ToInt16(Acap.GetSystemVariable("cmdecho")) == 1; + set => Acap.SetSystemVariable("cmdecho", Convert.ToInt16(value)); + } + + /// + /// 控制在光标是否为正交模式, 为打开正交, 为关闭正交 + /// + public static bool OrthoMode + { + get => Convert.ToInt16(Acap.GetSystemVariable("ORTHOMODE")) == 1; + set => Acap.SetSystemVariable("ORTHOMODE", Convert.ToInt16(value)); + } + + #region Dimblk + + /// + /// 标注箭头类型 + /// + public enum DimblkType + { + /// + /// 实心闭合 + /// + Defult, + + /// + /// 点 + /// + Dot, + + /// + /// 小点 + /// + DotSmall, + + /// + /// 空心点 + /// + DotBlank, + + /// + /// 原点标记 + /// + Origin, + + /// + /// 原点标记2 + /// + Origin2, + + /// + /// 打开 + /// + Open, + + /// + /// 直角 + /// + Open90, + + /// + /// 30度角 + /// + Open30, + + /// + /// 闭合 + /// + Closed, + + /// + /// 空心小点 + /// + Small, + + /// + /// 无 + /// + None, + + /// + /// 倾斜 + /// + Oblique, + + /// + /// 实心框 + /// + BoxFilled, + + /// + /// 方框 + /// + BoxBlank, + + /// + /// 空心闭合 + /// + ClosedBlank, + + /// + /// 实心基准三角形 + /// + DatumFilled, + + /// + /// 基准三角形 + /// + DatumBlank, + + /// + /// 完整标记 + /// + Integral, + + /// + /// 建筑标记 + /// + ArchTick + } + + private static readonly Dictionary dimdescdict = new() + { + { "实心闭合", DimblkType.Defult }, + { "点", DimblkType.Dot }, + { "小点", DimblkType.DotSmall }, + { "空心点", DimblkType.DotBlank }, + { "原点标记", DimblkType.Origin }, + { "原点标记 2", DimblkType.Origin2 }, + { "打开", DimblkType.Open }, + { "直角", DimblkType.Open90 }, + { "30 度角", DimblkType.Open30 }, + { "闭合", DimblkType.Closed }, + { "空心小点", DimblkType.Small }, + { "无", DimblkType.None }, + { "倾斜", DimblkType.Oblique }, + { "实心框", DimblkType.BoxFilled }, + { "方框", DimblkType.BoxBlank }, + { "空心闭合", DimblkType.ClosedBlank }, + { "实心基准三角形", DimblkType.DatumFilled }, + { "基准三角形", DimblkType.DatumBlank }, + { "完整标记", DimblkType.Integral }, + { "建筑标记", DimblkType.ArchTick }, + + { "", DimblkType.Defult }, + { "_DOT", DimblkType.Dot }, + { "_DOTSMALL", DimblkType.DotSmall }, + { "_DOTBLANK", DimblkType.DotBlank }, + { "_ORIGIN", DimblkType.Origin }, + { "_ORIGIN2", DimblkType.Origin2 }, + { "_OPEN", DimblkType.Open }, + { "_OPEN90", DimblkType.Open90 }, + { "_OPEN30", DimblkType.Open30 }, + { "_CLOSED", DimblkType.Closed }, + { "_SMALL", DimblkType.Small }, + { "_NONE", DimblkType.None }, + { "_OBLIQUE", DimblkType.Oblique }, + { "_BOXFILLED", DimblkType.BoxFilled }, + { "_BOXBLANK", DimblkType.BoxBlank }, + { "_CLOSEDBLANK", DimblkType.ClosedBlank }, + { "_DATUMFILLED", DimblkType.DatumFilled }, + { "_DATUMBLANK", DimblkType.DatumBlank }, + { "_INTEGRAL", DimblkType.Integral }, + { "_ARCHTICK", DimblkType.ArchTick }, + }; + + + + /// + /// 标注箭头属性 + /// + public static DimblkType Dimblk + { + get + { + string s = ((string)Acap.GetSystemVariable("dimblk")).ToUpper(); + // if (string.IsNullOrEmpty(s)) + // { + // return DimblkType.Defult; + // } + // else + // { + // if (dimdescdict.TryGetValue(s, out DimblkType value)) + // { + // return value; + // } + // return s.ToEnum(); + // // return s.FromDescName(); + // } + return dimdescdict[s]; + } + set + { + string s = GetDimblkName(value); + Acap.SetSystemVariable("dimblk", s); + } + } + + /// + /// 获取标注箭头名 + /// + /// 标注箭头类型 + /// 箭头名 + public static string GetDimblkName(DimblkType dimblk) + { + return + dimblk == DimblkType.Defult + ? + "." + : + "_" + dimblk.GetName(); + } + + /// + /// 获取标注箭头ID + /// + /// 标注箭头类型 + /// 箭头ID + public static ObjectId GetDimblkId(DimblkType dimblk) + { + DimblkType oldDimblk = Dimblk; + Dimblk = dimblk; + ObjectId id = HostApplicationServices.WorkingDatabase.Dimblk; + Dimblk = oldDimblk; + return id; + } + + #endregion Dimblk + + #region OsMode + + /// + /// 捕捉模式系统变量类型 + /// + public enum OSModeType + { + /// + /// 无 + /// + None = 0, + + /// + /// 端点 + /// + End = 1, + + /// + /// 中点 + /// + Middle = 2, + + /// + /// 圆心 + /// + Center = 4, + + /// + /// 节点 + /// + Node = 8, + + /// + /// 象限点 + /// + Quadrant = 16, + + /// + /// 交点 + /// + Intersection = 32, + + /// + /// 插入点 + /// + Insert = 64, + + /// + /// 垂足 + /// + Pedal = 128, + + /// + /// 切点 + /// + Tangent = 256, + + /// + /// 最近点 + /// + Nearest = 512, + + /// + /// 几何中心 + /// + Quick = 1024, + + /// + /// 外观交点 + /// + Appearance = 2048, + + /// + /// 延伸 + /// + Extension = 4096, + + /// + /// 平行 + /// + Parallel = 8192 + } + + /// + /// 捕捉模式系统变量 + /// + public static OSModeType OSMode + { + get + { + return (OSModeType)Convert.ToInt16(Acap.GetSystemVariable("osmode")); + } + set + { + Acap.SetSystemVariable("osmode", (int)value); + } + } + /// + /// 捕捉模式osm1是否包含osm2 + /// + /// 原模式 + /// 要比较的模式 + /// 包含时返回 ,不包含时返回 + public static bool Include(this OSModeType osm1, OSModeType osm2) + { + return (osm1 & osm2) == osm2; + } + #endregion OsMode + + + private static string GetName(this T value) + { + return Enum.GetName(typeof(T), value); + } + + #endregion Enum + + #region 系统变量 + /// + /// 获取cad系统变量 + /// + /// 变量名 + /// 变量值 + public static object GetVar(string? varName) + { + return Acap.GetSystemVariable(varName); + } + /// + /// 设置cad系统变量
    + /// 0x01 建议先获取现有变量值和设置的是否相同,否则直接设置会发生异常
    + /// 0x02 建议锁文档,否则 Psltscale 设置发生异常
    + /// 发生异常的时候vs输出窗口会打印一下,但是如果不介意也没啥问题 + ///
    + /// 变量名 + /// 变量值 + /// 输出异常,默认true;此设置仅为打印到命令栏,无法控制vs输出 + public static void SetVar(string? varName, object? value, bool echo = true) + { + try + { + Acap.SetSystemVariable(varName, value); + } + catch (System.Exception) + { + if (echo) + Env.Print($"{varName} 是不存在的变量!"); + } + } + #endregion + + #region 环境变量 +#if acad +#if NET35 + [System.Security.SuppressUnmanagedCodeSecurity] + [DllImport("acad.exe", CharSet = CharSet.Auto, CallingConvention = CallingConvention.Cdecl, EntryPoint = "acedGetEnv")] + static extern int AcedGetEnv(string? envName, StringBuilder ReturnValue); + + [System.Security.SuppressUnmanagedCodeSecurity] + [DllImport("acad.exe", CharSet = CharSet.Auto, CallingConvention = CallingConvention.Cdecl, EntryPoint = "acedSetEnv")] + static extern int AcedSetEnv(string? envName, StringBuilder NewValue); +#else + [System.Security.SuppressUnmanagedCodeSecurity] + [DllImport("accore.dll", CharSet = CharSet.Auto, CallingConvention = CallingConvention.Cdecl, EntryPoint = "acedGetEnv")] + static extern int AcedGetEnv(string? envName, StringBuilder ReturnValue); + + [System.Security.SuppressUnmanagedCodeSecurity] + [DllImport("accore.dll", CharSet = CharSet.Auto, CallingConvention = CallingConvention.Cdecl, EntryPoint = "acedSetEnv")] + static extern int AcedSetEnv(string? envName, StringBuilder NewValue); +#endif +#endif + +#if gcad + [System.Security.SuppressUnmanagedCodeSecurity] + [DllImport("gced.dll", CharSet = CharSet.Auto, CallingConvention = CallingConvention.Cdecl, EntryPoint = "gcedGetEnv")] + static extern int AcedGetEnv(string? envName, StringBuilder ReturnValue); + + [System.Security.SuppressUnmanagedCodeSecurity] + [DllImport("gced.dll", CharSet = CharSet.Auto, CallingConvention = CallingConvention.Cdecl, EntryPoint = "gcedSetEnv")] + static extern int AcedSetEnv(string? envName, StringBuilder NewValue); +#endif + + // TODO: 中望没有测试,此处仅为不报错;本工程所有含有"中望"均存在问题 +#if zcad + [System.Security.SuppressUnmanagedCodeSecurity] + [DllImport("zced.dll", CharSet = CharSet.Auto, CallingConvention = CallingConvention.Cdecl, EntryPoint = "zcedGetEnv")] + static extern int AcedGetEnv(string? envName, StringBuilder ReturnValue); + + [System.Security.SuppressUnmanagedCodeSecurity] + [DllImport("zced.dll", CharSet = CharSet.Auto, CallingConvention = CallingConvention.Cdecl, EntryPoint = "zcedSetEnv")] + static extern int AcedSetEnv(string? envName, StringBuilder NewValue); +#endif + + /// + /// 读取acad环境变量
    + /// 也能获取win环境变量 + ///
    + /// 变量名 + /// 返回值从不为null,需判断 + public static string GetEnv(string? name) + { + // 它将混合查询以下路径: + // acad2008注册表路径: 计算机\HKEY_CURRENT_USER\SOFTWARE\Autodesk\AutoCAD\R17.1\ACAD-6001:804\FixedProfile\General + // 用户: 计算机\HKEY_CURRENT_USER\Environment + // 系统: 计算机\HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\Session Manager\Environment + + // GetEnv("Path")长度很长: + // 可用内存 (最新格式) 1 MB (标准格式) + // https://docs.microsoft.com/zh-cn/windows/win32/sysinfo/registry-element-size-limits + + var sbRes = new StringBuilder(1 << 23); + _ = AcedGetEnv(name, sbRes); + return sbRes.ToString(); + } + + /// + /// 设置acad环境变量
    + /// 它是不会报错的,但是直接设置会写入注册表的,
    + /// 如果是设置高低版本cad不同的变量,建议先读取判断再设置
    + ///
    + /// 变量名 + /// 变量值 + /// + public static int SetEnv(string? name, string? var) + { + return AcedSetEnv(name, new StringBuilder(var)); + } + #endregion + + #region win环境变量/由于 Aced的 能够同时获取此变量与cad内的,所以废弃 + // /// + // /// 获取系统环境变量 + // /// + // /// 变量名 + // /// 指定的环境变量的值;或者如果找不到环境变量,则返回 null + // public static string? GetEnv(string? var) + // { + // // 从当前进程或者从当前用户或本地计算机的 Windows 操作系统注册表项检索环境变量的值 + // // 用户: 计算机\HKEY_CURRENT_USER\Environment + // // 系统: 计算机\HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\Session Manager\Environment + // return Environment.GetEnvironmentVariable(var); + // } + // /// + // /// 设置系统环境变量 + // /// + // /// 变量名 + // /// 变量值 + // public static void SetEnv(string? var, string? value) + // { + // // 创建、修改或删除当前进程中或者为当前用户或本地计算机保留的 Windows 操作系统注册表项中存储的环境变量 + // Environment.SetEnvironmentVariable(var, value); + // } + #endregion + + + /// + /// 命令行打印,会自动调用对象的toString函数 + /// + /// 要打印的对象 + public static void Print(object message) => Editor.WriteMessage($"{message}\n"); + public static void Printl(object message) => Editor.WriteMessage($"{Environment.NewLine}{message}\n"); + + /// + /// 判断当前是否在UCS坐标下 + /// + /// Bool + public static bool IsUcs() => (short)GetVar("WORLDUCS") == 0; + + + #region dwg版本号/cad版本号/年份 + /// + /// 获取当前配置文件的保存版本 + /// + /// + public static DwgVersion GetDefaultDwgVersion() + { + DwgVersion version; + var ffs = Env.GetEnv("DefaultFormatForSave"); + version = ffs switch + { + "1" => DwgVersion.AC1009,// R12/LT12 dxf + "8" => DwgVersion.AC1014,// R14/LT98/LT97 dwg + "12" => DwgVersion.AC1015,// 2000 dwg + "13" => DwgVersion.AC1800a,// 2000 dxf + "24" => DwgVersion.AC1800,// 2004 dwg + "25" => (DwgVersion)26,// 2004 dxf + "36" => (DwgVersion)27,// 2007 dwg DwgVersion.AC1021 + "37" => (DwgVersion)28,// 2007 dxf + + // "38" => (DwgVersion),// dwt 样板文件...啊惊没找到这个是什么 + "48" => (DwgVersion)29,// 2010 dwg DwgVersion.AC1024 + "49" => (DwgVersion)30,// 2010 dxf + "60" => (DwgVersion)31,// 2013 dwg DwgVersion.AC1027 + "61" => (DwgVersion)32,// 2013 dxf + "64" => (DwgVersion)33,// 2018 dwg DwgVersion.AC1032 + "65" => (DwgVersion)34,// 2018 dxf + _ => throw new NotImplementedException(),// 提醒维护 + }; + return version; + } + + /// + /// 是否为dxf版本号 + /// + /// + /// + public static bool IsDxfVersion(this DwgVersion dwgVersion) + { + var result = (int)dwgVersion switch + { + 16 => true,// R12/LT12 dxf + 24 => true,// 2000 dxf + 26 => true,// 2004 dxf + 28 => true,// 2007 dxf + 30 => true,// 2010 dxf + 32 => true,// 2013 dxf + 34 => true,// 2018 dxf + _ => false, + }; + return result; + } + + /// + /// 获取cad年份 + /// + /// 超出年份就报错 + public static int GetAcadVersion() + { + var ver = Acap.Version.Major + "." + Acap.Version.Minor; + int acarVarNum = ver switch + { + "16.2" => 2006, + "17.0" => 2007, + "17.1" => 2008, + "17.2" => 2009, + "18.0" => 2010, + "18.1" => 2011, + "18.2" => 2012, + "19.0" => 2013, + "19.1" => 2014, + "20.0" => 2015, + "20.1" => 2016, + "21.0" => 2017, + "22.0" => 2018, + "23.0" => 2019, + "23.1" => 2020, + "24.0" => 2021, + "24.1" => 2022, + _ => throw new NotImplementedException(), + }; + return acarVarNum; + } + + public static string GetAcapVersionDll(string str = "acdb") + { + return str + Acap.Version.Major + ".dll"; + } + #endregion + + + #region cad变量功能延伸 + /// + /// 设置cad系统变量
    + /// 提供一个反序列化后,无cad异常输出的功能
    + /// 注意,您需要再此执行时候设置文档锁
    + ///
    + /// 否则也将导致修改数据库异常
    + ///
    + /// + /// + /// 成功返回当前值,失败null + /// + public static object? SetVarEx(string? key, string? value) + { + if (key == null) + throw new ArgumentNullException(nameof(key)); + if (value == null) + throw new ArgumentNullException(nameof(value)); + + var currentVar = Env.GetVar(key); + if (currentVar == null) + return null; + + object? valueType = currentVar.GetType().Name switch + { + "String" => value.Replace("\"", string.Empty), + "Double" => double.Parse(value), + "Int16" => short.Parse(value), + "Int32" => int.Parse(value), + _ => throw new NotImplementedException(), + }; + + // 相同的参数进行设置会发生一次异常 + if (currentVar.ToString().ToUpper() != valueType!.ToString().ToUpper()) + Env.SetVar(key, valueType); + + return currentVar; + } + + /// + /// 设置新系统变量,返回现有系统变量 + /// + /// 设置的变量词典 + /// 返回现有变量词典,然后下次就可以利用它进行设置回来了 + public static Dictionary SaveCadVar(Dictionary args) + { + if (args is null) + throw new ArgumentNullException(nameof(args)); + + var dict = new Dictionary(); + foreach (var item in args) + { + // 判断是否为系统变量 + var ok = SetVarEx(item.Key, item.Value); + if (ok != null) + { + dict.Add(item.Key, ok.ToString()); + continue; + } + + // 判断是否为系统变量 + var envstr = Env.GetEnv(item.Key); + if (!string.IsNullOrEmpty(envstr)) + { + Env.SetEnv(item.Key, item.Value); + dict.Add(item.Key, envstr); + } + } + return dict; + } + #endregion +} \ No newline at end of file diff --git a/src/CAD/IFox.CAD.Shared/Runtime/FileOpenMode.cs b/src/CAD/IFox.CAD.Shared/Runtime/FileOpenMode.cs new file mode 100644 index 0000000..55dcefd --- /dev/null +++ b/src/CAD/IFox.CAD.Shared/Runtime/FileOpenMode.cs @@ -0,0 +1,85 @@ +#if ac2008 // NET35 +namespace Autodesk.AutoCAD.DatabaseServices +{ + [Wrapper("AcDbDatabase::OpenMode")] + public enum FileOpenMode + { + /// + /// 只读模式打开 + /// + OpenForReadAndReadShare = 1, + OpenForReadAndWriteNoShare = 2, + OpenForReadAndAllShare = 3, + OpenTryForReadShare = 4, + } + + public static class FileOpenModeHelper + { + /* + * 这个开图方式会致命错误,不清楚怎么用的文件句柄开图 + * using FileStream fileStream = new(_fileName, FileMode.Open, fileAccess, GetFileShare(fileOpenMode)); + * Database.ReadDwgFile(fileStream.SafeFileHandle.DangerousGetHandle(), true, password); + */ + public static FileShare GetFileShare(FileOpenMode fileOpenMode) + { + // FileAccess fileAccess = FileAccess.Read; + FileShare fileShare = FileShare.Read; + switch (fileOpenMode) + { + // 不完美匹配 + case FileOpenMode.OpenTryForReadShare: + // fileAccess = FileAccess.ReadWrite; + fileShare = FileShare.ReadWrite; + break; + // 完美匹配 + case FileOpenMode.OpenForReadAndAllShare: + // fileAccess = FileAccess.ReadWrite; + fileShare = FileShare.ReadWrite; + break; + // 完美匹配 + case FileOpenMode.OpenForReadAndWriteNoShare: + // fileAccess = FileAccess.ReadWrite; + fileShare = FileShare.None; + break; + // 完美匹配 + case FileOpenMode.OpenForReadAndReadShare: + // fileAccess = FileAccess.Read; + fileShare = FileShare.Read; + break; + } + return fileShare; + } + } +} +#endif + +#if NET35 +namespace Autodesk.AutoCAD.Internal +{ + public class Utils + { + public static void SetFocusToDwgView() + { + IntPtr window; + if (Acap.DocumentManager.Count == 0) + { + window = Acap.MainWindow.Handle; + } + else + { + // 它们是层级关系 + // Main + // -->MDI(大小被 DwgView 局限) + // ---->docW(比MDI大) + // -------->msctls_statusbar32 + // -------->DwgView + var docW = Acap.DocumentManager.MdiActiveDocument.Window.Handle; + var msctls_statusbar32 = IFoxCAD.Cad.WindowsAPI.GetTopWindow(docW); + window = IFoxCAD.Cad.WindowsAPI.GetWindow(msctls_statusbar32, 2U); + } + if (window != IntPtr.Zero) + IFoxCAD.Cad.WindowsAPI.SetFocus(window); + } + } +} +#endif \ No newline at end of file diff --git a/src/CAD/IFox.CAD.Shared/Runtime/IAutoGo.cs b/src/CAD/IFox.CAD.Shared/Runtime/IAutoGo.cs new file mode 100644 index 0000000..738ba19 --- /dev/null +++ b/src/CAD/IFox.CAD.Shared/Runtime/IAutoGo.cs @@ -0,0 +1,403 @@ +namespace IFoxCAD.Cad; + +using System.Collections.ObjectModel; +using System.Diagnostics; + +/// +/// 加载时优先级 +/// +[Flags] +public enum Sequence : byte +{ + First,// 最先 + Last, // 最后 +} + +/// +/// 加载时自动执行接口 +/// +public interface IFoxAutoGo +{ + // 控制加载顺序 + Sequence SequenceId(); + // 关闭cad的时候会自动执行 + void Terminate(); + // 打开cad的时候会自动执行 + void Initialize(); +} + +/// +/// 加载时自动执行特性 +/// +[AttributeUsage(AttributeTargets.Method)] +public class IFoxInitialize : Attribute +{ + /// + /// 优先级 + /// + internal Sequence SequenceId; + /// + /// 用于初始化;用于结束回收 + /// + internal bool IsInitialize; + /// + /// 用于初始化和结束回收 + /// + /// 优先级 + /// 用于初始化;用于结束回收 + public IFoxInitialize(Sequence sequence = Sequence.Last, bool isInitialize = true) + { + SequenceId = sequence; + IsInitialize = isInitialize; + } +} + +// 为了解决IExtensionApplication在一个dll内无法多次实现接口的关系 +// 所以在这里反射加载所有的 IAutoGo ,以达到能分开写"启动运行"函数的目的 +class RunClass +{ + public Sequence Sequence { get; } + readonly MethodInfo _methodInfo; + object? _instance; + /// + /// 执行此方法 + /// + /// + /// + /// 已经创建的对象 + public RunClass(MethodInfo method, Sequence sequence, object? instance = null) + { + _methodInfo = method; + Sequence = sequence; + _instance = instance; + } + + /// + /// 运行方法 + /// + public void Run() + { + _methodInfo.Invoke(ref _instance); + } +} + +/// +/// 此类作为加载后cad自动运行接口的一部分,用于反射特性和接口 +/// +/// 启动cad后的执行顺序为:
    +/// 1:特性..(多个)
    +/// 2:接口..(多个) +///
    +///
    +public class AutoReflection +{ + static List _InitializeList = new(); // 储存方法用于初始化 + static List _TerminateList = new(); // 储存方法用于结束释放 + + readonly string _dllName; + readonly AutoRegConfig _autoRegConfig; + + /// + /// 反射执行 + /// + /// 1.特性:
    + /// 2.接口: + ///
    + ///
    + /// 约束在此dll进行加速 + public AutoReflection(string dllName, AutoRegConfig configInfo) + { + _dllName = dllName; + _autoRegConfig = configInfo; + } + + // 启动cad的时候会自动执行 + public void Initialize() + { + try + { + // 收集特性,包括启动时和关闭时 + if ((_autoRegConfig & AutoRegConfig.ReflectionAttribute) == AutoRegConfig.ReflectionAttribute) + GetAttributeFunctions(_InitializeList, _TerminateList); + + if ((_autoRegConfig & AutoRegConfig.ReflectionInterface) == AutoRegConfig.ReflectionInterface) + { + GetInterfaceFunctions(_InitializeList, nameof(Initialize), _TerminateList, nameof(Terminate)); + } + + if (_InitializeList.Count > 0) + { + // 按照 SequenceId 排序_升序 + _InitializeList = _InitializeList.OrderBy(runClass => runClass.Sequence).ToList(); + RunFunctions(_InitializeList); + } + } + catch (System.Exception e) + { + Debugger.Break(); + } + } + + // 关闭cad的时候会自动执行 + public void Terminate() + { + try + { + //if ((_autoRegConfig & AutoRegConfig.ReflectionInterface) == AutoRegConfig.ReflectionInterface) + // GetInterfaceFunctions(_TerminateList, nameof(Terminate)); + + if (_TerminateList.Count > 0) + { + // 按照 SequenceId 排序_降序 + _TerminateList = _TerminateList.OrderByDescending(runClass => runClass.Sequence).ToList(); + RunFunctions(_TerminateList); + } + } + catch (System.Exception e) + { + Env.Printl(e.Message); + Debugger.Break(); + } + } + + /// + /// 遍历程序域下所有类型 + /// + /// 输出每个成员执行 + /// 过滤此dll,不含后缀 + public static void AppDomainGetTypes(Action action, string? dllNameWithoutExtension = null) + { +#if DEBUG + int error = 0; +#endif + try + { + var assemblies = AppDomain.CurrentDomain.GetAssemblies(); +#if !NET35 + // cad2021出现如下报错 + // System.NotSupportedException:动态程序集中不支持已调用的成员 + // assemblies = assemblies.Where(p => !p.IsDynamic).ToArray();// 这个要容器类型转换 + assemblies = Array.FindAll(assemblies, p => !p.IsDynamic); +#endif + // 主程序域 + for (int ii = 0; ii < assemblies.Length; ii++) + { + var assembly = assemblies[ii]; + + // 获取类型集合,反射时候还依赖其他的dll就会这个错误 + // 此通讯库要跳过,否则会报错. + var location = Path.GetFileNameWithoutExtension(assembly.Location); + if (dllNameWithoutExtension != null && location != dllNameWithoutExtension) + continue; + if (location == "AcInfoCenterConn")// 通讯库 + continue; + + Type[]? types = null; + try + { + types = assembly.GetTypes(); + } + catch (ReflectionTypeLoadException) { continue; } + + if (types is null) + continue; + + for (int jj = 0; jj < types.Length; jj++) + { + var type = types[jj]; + if (type is not null) + { +#if DEBUG + ++error; +#endif + action.Invoke(type); + } + } + } + } + catch (System.Exception e) + { +#if DEBUG + Debugx.Printl($"出错:{nameof(AppDomainGetTypes)};计数{error};错误信息:{e.Message}"); + Debugger.Break(); +#endif + } + } + + /// + /// 收集接口下的函数 + /// + /// 储存要运行的方法 + /// 查找方法名 + /// + void GetInterfaceFunctions(List initializes, string initializeName, + List terminates, string terminateName) + { + AppDomainGetTypes(type => { + // 接口的静态类屏蔽,继承接口无法使用静态类,因此跳过 + if (type.IsAbstract) + return; + + var ints = type.GetInterfaces(); + for (int sss = 0; sss < ints.Length; sss++) + { + if (ints[sss].Name != nameof(IFoxAutoGo)) + continue; + + Sequence? sequence = null; + MethodInfo? initialize = null; + MethodInfo? terminate = null; + object? instance = null; + + var mets = type.GetMethods(); + for (int jj = 0; jj < mets.Length; jj++) + { + var method = mets[jj]; + + // 接口的静态方法屏蔽,继承的方法也不可能是静态的,因此跳过 + if (method.IsAbstract) + continue; + + if (method.Name == nameof(IFoxAutoGo.SequenceId)) + { + // 避免触发两次构造函数,所以这里需要ref构造的对象出来 + var obj = method.Invoke(ref instance); + if (obj is not null) + sequence = (Sequence)obj; + continue; + } + if (method.Name == initializeName) + { + initialize = method; + continue; + } + if (method.Name == terminateName) + { + terminate = method; + continue; + } + if (sequence is not null && initialize is not null && terminate is not null) + break; + } + + // 避免在terminate释放的时候去再次构造,所以需要一次性收集 + // 若是释放的时候去再次构造: + // 0x01 initialize构造的字段拥有的资源就处于系统释放了,这是不合理的 + // 0x02 同时也发生了,为了释放而去构造这个操作 + var seq = sequence is null ? Sequence.Last : sequence.Value; + if (initialize is not null) + initializes.Add(new(initialize, seq, instance)); + if (terminate is not null) + terminates.Add(new(terminate, seq, instance)); + break; + } + }, _dllName); + } + + /// + /// 收集特性下的函数 + /// + void GetAttributeFunctions(List initializes, + List terminates) + { + AppDomainGetTypes(type => { + if (!type.IsClass) + return; + + // 特性的静态类不屏蔽 + //if (type.IsAbstract) + // return; + + var mets = type.GetMethods(); + for (int ii = 0; ii < mets.Length; ii++) + { + var method = mets[ii]; + + // 特性的静态方法不屏蔽 + //if (method.IsAbstract) + // continue; + + var attr = method.GetCustomAttributes(true); + for (int jj = 0; jj < attr.Length; jj++) + { + if (attr[jj] is IFoxInitialize jjAtt) + { + var runc = new RunClass(method, jjAtt.SequenceId); + if (jjAtt.IsInitialize) + initializes.Add(runc); + else + terminates.Add(runc); + break; + } + } + } + }, _dllName); + } + + /// + /// 执行收集到的函数 + /// + static void RunFunctions(List runClassList) + { + for (int i = 0; i < runClassList.Count; i++) + runClassList[i].Run(); + runClassList.Clear(); + } + +#if Debug + /// + /// 检查当前程序域重复出现命令, + /// 当出现重复时候将引起断点 + /// + public static void DebugCheckCmdRecurrence() + { + HashSet keys = new(); + + // 本dll中存在冲突命令,此时cad自动接口可以运行,但是加载命令之后会报错,因此利用断点告诉程序员 + AutoReflection.AppDomainGetTypes(type => { + var mets = type.GetMethods(); + for (int ii = 0; ii < mets.Length; ii++) + { + var method = mets[ii]; + var attr = method.GetCustomAttributes(true); + for (int jj = 0; jj < attr.Length; jj++) + if (attr[jj] is CommandMethodAttribute att) + { + if (keys.Contains(att.GlobalName)) + Debugger.Break(); + keys.Add(att.GlobalName); + } + } + }, Assembly.GetCallingAssembly().GetName().Name); + + // 其他dll中存在冲突命令,此时会覆盖命令,友好的提示程序员 + keys.Clear(); + HashSet msgMod = new(); + AutoReflection.AppDomainGetTypes(type => { + var mets = type.GetMethods(); + for (int ii = 0; ii < mets.Length; ii++) + { + var method = mets[ii]; + var attr = method.GetCustomAttributes(true); + for (int jj = 0; jj < attr.Length; jj++) + if (attr[jj] is CommandMethodAttribute att) + { + if (keys.Contains(att.GlobalName)) + msgMod.Add(att.GlobalName); + keys.Add(att.GlobalName); + } + } + }); + var sb = new StringBuilder(); + foreach (string key in msgMod) + sb.AppendLine(key); + if (sb.Length != 0) + { + Env.Printl("当前cad环境加载的多个DLL中存在重复命令将被覆盖:"); + Env.Printl("{"); + Env.Printl(sb.ToString()); + Env.Printl("}"); + } + } +#endif +} \ No newline at end of file diff --git a/src/CAD/IFox.CAD.Shared/Runtime/LateBinding.cs b/src/CAD/IFox.CAD.Shared/Runtime/LateBinding.cs new file mode 100644 index 0000000..45cafa9 --- /dev/null +++ b/src/CAD/IFox.CAD.Shared/Runtime/LateBinding.cs @@ -0,0 +1,83 @@ +namespace IFoxCAD.Com; + +/// +/// 后绑代码工具 +/// +public static class LateBinding +{ + /// + /// 从运行对象表 (ROT) 获取指定对象的运行实例 + /// + /// + /// + public static object GetInstance(string appName) + { + return Marshal.GetActiveObject(appName); + } + /// + /// 创建实例 + /// + /// + /// + public static object CreateInstance(string appName) + { + return Activator.CreateInstance(Type.GetTypeFromProgID(appName)); + } + /// + /// 获取或创建实例 + /// + /// + /// + public static object GetOrCreateInstance(string appName) + { + try { return GetInstance(appName); } + catch { return CreateInstance(appName); } + } + /// + /// 释放实例 + /// + /// + public static void ReleaseInstance(this object obj) + { + Marshal.ReleaseComObject(obj); + } + + /// + /// 获取属性 + /// + /// + /// + /// + /// + public static object GetProperty(this object obj, string propName, params object[] parameter) + { + return obj.GetType().InvokeMember(propName, + BindingFlags.GetProperty, + null, obj, parameter); + } + /// + /// 设置属性 + /// + /// + /// + /// + public static void SetProperty(this object obj, string propName, params object[] parameter) + { + obj.GetType().InvokeMember(propName, + BindingFlags.SetProperty, + null, obj, parameter); + } + /// + /// 执行函数 + /// + /// + /// + /// + /// + public static object Invoke(this object obj, string memberName, params object[] parameter) + { + return obj.GetType().InvokeMember(memberName, + BindingFlags.Public | BindingFlags.InvokeMethod, + null, obj, parameter); + } +} \ No newline at end of file diff --git a/src/CAD/IFox.CAD.Shared/Runtime/Log.cs b/src/CAD/IFox.CAD.Shared/Runtime/Log.cs new file mode 100644 index 0000000..812d694 --- /dev/null +++ b/src/CAD/IFox.CAD.Shared/Runtime/Log.cs @@ -0,0 +1,453 @@ +namespace IFoxCAD.Cad; + +using System; +using System.Diagnostics; +using System.Threading; +using Exception = Exception; + +#region 写入日志到不同的环境中 +// https://zhuanlan.zhihu.com/p/338492989 +public abstract class LogBase +{ + public abstract void DeleteLog(); + public abstract string[] ReadLog(); + public abstract void WriteLog(string message); +} + +/// +/// 日志输出环境 +/// +public enum LogTarget +{ + /// + /// 文件(包含错误和备注) + /// + File = 1, + /// + /// 文件(不包含错误,也就是只写备注信息) + /// + FileNotException = 2, + /// + /// 数据库 + /// + Database = 4, + /// + /// windows日志 + /// + EventLog = 8, +} + +/// +/// 写入到文件中 +/// +public class FileLogger : LogBase +{ + public override void DeleteLog() + { + File.Delete(LogHelper.LogAddress); + } + public override string[] ReadLog() + { + List lines = new(); + using (var sr = new StreamReader(LogHelper.LogAddress, true/*自动识别文件头*/)) + { + string line; + while ((line = sr.ReadLine()) != null) + lines.Add(line); + } + return lines.ToArray(); + } + public override void WriteLog(string? message) + { + // 把异常信息输出到文件 + var sw = new StreamWriter(LogHelper.LogAddress, true/*当天日志文件存在就追加,否则就创建*/); + sw.Write(message); + sw.Flush(); + sw.Close(); + sw.Dispose(); + } +} + +/// +/// 写入到数据库(暂时不支持) +/// +public class DBLogger : LogBase +{ + public override void DeleteLog() + { + throw new NotImplementedException(); + } + public override string[] ReadLog() + { + throw new NotImplementedException(); + } + public override void WriteLog(string? message) + { + throw new NotImplementedException(); + } +} + +/// +/// 写入到win日志 +/// +public class EventLogger : LogBase +{ + // 需要win权限 + // https://blog.csdn.net/weixin_38208401/article/details/77870909 + // NET50要加 + // https://docs.microsoft.com/en-us/answers/questions/526018/windows-event-log-with-net-5.html + + public string LogName = "IFoxCadLog"; + public override void DeleteLog() + { +#if !NET5_0 && !NET6_0 + if (EventLog.Exists(LogName)) + EventLog.Delete(LogName); +#endif + } + public override string[] ReadLog() + { + List lines = new(); +#if !NET5_0 && !NET6_0 + try + { + EventLog eventLog = new() + { + Log = LogName + }; + foreach (EventLogEntry entry in eventLog.Entries) + lines.Add(entry.Message); + } + catch (System.Security.SecurityException e) + { + throw new Exception("您没有权限读取win日志::" + e.Message); + } +#endif + return lines.ToArray(); + } + public override void WriteLog(string? message) + { +#if !NET5_0 && !NET6_0 + try + { + EventLog eventLog = new() + { + Source = LogName + }; + eventLog.WriteEntry(message, EventLogEntryType.Information); + } + catch (System.Security.SecurityException e) + { + throw new Exception("您没有权限写入win日志::" + e.Message); + } +#endif + } +} + +#endregion + +#region 静态方法 +public static class LogHelper +{ +#pragma warning disable CA2211 // 非常量字段应当不可见 + /// + /// 日志文件完整路径 + /// + public static string? LogAddress; + /// + /// 输出错误信息到日志文件的开关 + /// + public static bool FlagOutFile = false; + /// + /// 输出错误信息到vs输出窗口的开关 + /// + public static bool FlagOutVsOutput = true; +#pragma warning restore CA2211 // 非常量字段应当不可见 + + /// + /// 读写锁 + /// 当资源处于写入模式时,其他线程写入需要等待本次写入结束之后才能继续写入 + /// + static readonly ReaderWriterLockSlim _logWriteLock = new(); + + /// + /// 提供给外部设置log文件保存路径 + /// + /// null就生成默认配置 + public static void OptionFile(string? newlogAddress = null) + { + _logWriteLock.EnterWriteLock();// 写模式锁定 读写锁 + try + { + LogAddress = newlogAddress; + if (string.IsNullOrEmpty(LogAddress)) + LogAddress = GetDefaultOption(DateTime.Now.ToString("yy-MM-dd") + ".log"); + } + finally + { + _logWriteLock.ExitWriteLock();// 解锁 读写锁 + } + } + + /// + /// 输入文件名,获取保存路径的完整路径 + /// + /// 文件名,null获取默认路径 + /// 创建路径 + /// 完整路径 + public static string GetDefaultOption(string fileName, bool createDirectory = true) + { + // 微软回复:静态构造函数只会被调用一次, + // 并且在它执行完成之前,任何其它线程都不能创建这个类的实例或使用这个类的静态成员 + // https://blog.csdn.net/weixin_34204722/article/details/90095812 + var sb = new StringBuilder(); + sb.Append(Environment.CurrentDirectory); + sb.Append("\\ErrorLog"); + + // 新建文件夹 + if (createDirectory) + { + var path = sb.ToString(); + if (!Directory.Exists(path)) + { + // 设置文件夹属性为普通 + Directory.CreateDirectory(path) + .Attributes = FileAttributes.Normal; + } + } + sb.Append('\\'); + sb.Append(fileName); + return sb.ToString(); + } + + public static string WriteLog(this string? message, + LogTarget target = LogTarget.File) + { + if (message == null) + return string.Empty; + return LogAction(null, message, target); + } + + public static string WriteLog(this Exception? exception, + LogTarget target = LogTarget.File) + { + if (exception == null) + return string.Empty; + return LogAction(exception, null, target); + } + + public static string WriteLog(this Exception? exception, string? message, + LogTarget target = LogTarget.File) + { + if (exception == null) + return string.Empty; + return LogAction(exception, message, target); + } + + + /// 错误 + /// 备注信息 + /// 记录方式 + static string LogAction(Exception? ex, + string? message, + LogTarget target) + { + if (ex == null && message == null) + return string.Empty; + + if (LogAddress == null) + { + if (target == LogTarget.File || + target == LogTarget.FileNotException) + OptionFile(); + } + + // 不写入错误 + if (target == LogTarget.FileNotException) + ex = null; + + try + { + _logWriteLock.EnterWriteLock();// 写模式锁定 读写锁 + + var logtxt = new LogTxt(ex, message); + // var logtxtJson = Newtonsoft.Json.JsonConvert.SerializeObject(logtxt, Formatting.Indented); + var logtxtJson = logtxt?.ToString(); + if (logtxtJson == null) + return string.Empty; + + if (FlagOutFile) + { + LogBase? logger; + switch (target) + { + case LogTarget.File: + logger = new FileLogger(); + logger.WriteLog(logtxtJson); + break; + case LogTarget.FileNotException: + logger = new FileLogger(); + logger.WriteLog(logtxtJson); + break; + case LogTarget.Database: + logger = new DBLogger(); + logger.WriteLog(logtxtJson); + break; + case LogTarget.EventLog: + logger = new EventLogger(); + logger.WriteLog(logtxtJson); + break; + } + } + + if (FlagOutVsOutput) + { + Debugx.Printl("错误日志: " + LogAddress); + Debug.Write(logtxtJson); + } + return logtxtJson; + } + finally + { + _logWriteLock.ExitWriteLock();// 解锁 读写锁 + } + } +} +#endregion + +#region 序列化 +[Serializable] +public class LogTxt +{ + public string? 当前时间; + public string? 备注信息; + public string? 异常信息; + public string? 异常对象; + public string? 触发方法; + public string? 调用堆栈; + + public LogTxt() { } + + public LogTxt(Exception? ex, string? message) : this() + { + if (ex == null && message == null) + throw new ArgumentNullException(nameof(ex)); + + // 以不同语言显示日期 + // DateTime.Now.ToString("f", new System.Globalization.CultureInfo("es-ES")) + // DateTime.Now.ToString("f", new System.Globalization.CultureInfo("zh-cn")) + // 为了最小信息熵,所以用这样的格式,并且我喜欢补0 + 当前时间 = DateTime.Now.ToString("yy-MM-dd hh:mm:ss"); + + if (ex != null) + { + 异常信息 = ex.Message; + 异常对象 = ex.Source; + 触发方法 = ex.TargetSite == null ? string.Empty : ex.TargetSite.ToString(); + 调用堆栈 = ex.StackTrace == null ? string.Empty : ex.StackTrace.Trim(); + } + if (message != null) + 备注信息 = message; + } + + /// 为了不引入json的dll,所以这里自己构造 + public override string? ToString() + { + var sb = new StringBuilder(); + sb.Append('{'); + sb.Append(Environment.NewLine); + sb.AppendLine($" \"{nameof(当前时间)}\": \"{当前时间}\""); + sb.AppendLine($" \"{nameof(备注信息)}\": \"{备注信息}\""); + sb.AppendLine($" \"{nameof(异常信息)}\": \"{异常信息}\""); + sb.AppendLine($" \"{nameof(异常对象)}\": \"{异常对象}\""); + sb.AppendLine($" \"{nameof(触发方法)}\": \"{触发方法}\""); + sb.AppendLine($" \"{nameof(调用堆栈)}\": \"{调用堆栈}\""); + sb.Append('}'); + return sb.ToString(); + } +} +#endregion + + +#if false // 最简单的实现 +public static class Log +{ + /// + /// 读写锁 + /// 当资源处于写入模式时,其他线程写入需要等待本次写入结束之后才能继续写入 + /// + static readonly ReaderWriterLockSlim _logWriteLock = new(); + + /// + /// 日志文件完整路径 + /// + static readonly string _logAddress; + + static Log() + { + // 微软回复:静态构造函数只会被调用一次, + // 并且在它执行完成之前,任何其它线程都不能创建这个类的实例或使用这个类的静态成员 + // https://blog.csdn.net/weixin_34204722/article/details/90095812 + var sb = new StringBuilder(); + sb.Append(Environment.CurrentDirectory); + sb.Append("\\ErrorLog"); + + // 新建文件夹 + var path = sb.ToString(); + if (!Directory.Exists(path)) + { + Directory.CreateDirectory(path) + .Attributes = FileAttributes.Normal; // 设置文件夹属性为普通 + } + + sb.Append('\\'); + sb.Append(DateTime.Now.ToString("yy-MM-dd")); + sb.Append(".log"); + _logAddress = sb.ToString(); + } + + + /// + /// 将异常打印到日志文件 + /// + /// 异常 + /// 备注 + /// DEBUG模式打印到vs输出窗口 + public static string? WriteLog(this Exception? ex, + string? remarks = null, + bool printDebugWindow = true) + { + try + { + _logWriteLock.EnterWriteLock();// 写模式锁定 读写锁 + + var logtxt = new LogTxt(ex, remarks); + // var logtxtJson = Newtonsoft.Json.JsonConvert.SerializeObject(logtxt, Formatting.Indented); + var logtxtJson = logtxt.ToString(); + + if (logtxtJson == null) + return string.Empty; + + // 把异常信息输出到文件 + var sw = new StreamWriter(_logAddress, true/*当天日志文件存在就追加,否则就创建*/); + sw.Write(logtxtJson); + sw.Flush(); + sw.Close(); + sw.Dispose(); + + if (printDebugWindow) + { + Debugx.Printl("错误日志: " + _logAddress); + Debug.Write(logtxtJson); + // Debugger.Break(); + // Debug.Assert(false, "终止进程"); + } + return logtxtJson; + } + finally + { + _logWriteLock.ExitWriteLock();// 解锁 读写锁 + } + } +} +#endif \ No newline at end of file diff --git a/src/CAD/IFox.CAD.Shared/Runtime/MethodInfoHelper.cs b/src/CAD/IFox.CAD.Shared/Runtime/MethodInfoHelper.cs new file mode 100644 index 0000000..1111037 --- /dev/null +++ b/src/CAD/IFox.CAD.Shared/Runtime/MethodInfoHelper.cs @@ -0,0 +1,64 @@ +namespace IFoxCAD.Cad; + +internal static class MethodInfoHelper +{ +#if cache + private static readonly Dictionary methodDic = new(); +#endif + + /// + /// 执行函数 + /// + /// 函数 + /// 已经外部创建的对象,为空则此处创建 + public static object? Invoke(this MethodInfo methodInfo, ref object? instance) + { + if (methodInfo == null) + throw new ArgumentNullException(nameof(methodInfo)); + + object? result = null; + if (methodInfo.IsStatic) + { + // 新函数指针进入此处 + // 参数数量一定要匹配,为null则参数个数不同导致报错, + // 参数为stirng[],则可以传入object[]代替,其他参数是否还可以实现默认构造? + var args = new List { }; + var paramInfos = methodInfo.GetParameters(); + for (int i = 0; i < paramInfos.Length; i++) + args.Add(null!); + result = methodInfo.Invoke(null, args.ToArray());// 静态调用 + } + else + { +#if cache + // 原命令的函数指针进入此处 + // object instance; + if (methodDic.ContainsKey(methodInfo)) + instance = methodDic[methodInfo]; +#endif + if (instance == null) + { + var reftype = methodInfo.ReflectedType; + if (reftype == null) + return null; + + var fullName = reftype.FullName; // 命名空间+类 + if (fullName == null) + return null; + + var type = reftype.Assembly.GetType(fullName); + if (type == null) + return null; + + instance = Activator.CreateInstance(type);// 构造类 +#if cache + if (!type.IsAbstract)// 无法创建抽象类成员 + methodDic.Add(methodInfo, instance); +#endif + } + if (instance != null) + result = methodInfo.Invoke(instance, null); // 非静态,调用实例化方法 + } + return result; + } +} \ No newline at end of file diff --git a/src/CAD/IFox.CAD.Shared/Runtime/PE/AcadPeInfo.cs b/src/CAD/IFox.CAD.Shared/Runtime/PE/AcadPeInfo.cs new file mode 100644 index 0000000..3957a68 --- /dev/null +++ b/src/CAD/IFox.CAD.Shared/Runtime/PE/AcadPeInfo.cs @@ -0,0 +1,333 @@ +using System.Diagnostics; + +namespace IFoxCAD.Cad; + +// 选择模式 +[Flags] +public enum AcadPeEnum : byte +{ + AcadExe = 1, + AccoreDll = 2, + Acdb = 4, + ExeAndCore = AcadExe | AccoreDll, +} + +// 这里的枚举对应 GetMethodException 错误值 +[Flags] +public enum GetMethodErrorNum : byte +{ + Ok = 0, + NoModule = 1, + NoFuncName = 2, +} + +// 自动获取本工程上面的发送命令的接口 +public class AcadPeInfo +{ + #region 静态单例获取exe/dll信息 + static PeInfo? _PeForAcadExe; + public static PeInfo? PeForAcadExe + { + get + { + if (_PeForAcadExe is null) + { + // 获取此acad.exe获取所有的函数名 + var file = Process.GetCurrentProcess().MainModule.FileName; + _PeForAcadExe = new PeInfo(file); + } + return _PeForAcadExe; + } + } + + static PeInfo? _PeForAccoreDll; + public static PeInfo? PeForAccoreDll + { + get + { + if (_PeForAccoreDll is null) + { + // 获取此dll所有的函数名 + var file = Process.GetCurrentProcess().MainModule.FileName; + var dll = Path.GetDirectoryName(file) + "\\accore.dll"; + if (File.Exists(dll))// 08没有,高版本分离的 + _PeForAccoreDll = new PeInfo(dll); + } + return _PeForAccoreDll; + } + } + + static PeInfo? _PeForAcdbDll; + public static PeInfo? PeForAcdbDll + { + get + { + if (_PeForAcdbDll is null) + { + // 获取此dll所有的函数名 + var file = Process.GetCurrentProcess().MainModule.FileName; + var dll = Path.GetDirectoryName(file) + $"\\acdb{Acap.Version.Major}.dll"; + if (File.Exists(dll)) + _PeForAcdbDll = new PeInfo(dll); + } + return _PeForAcdbDll; + } + } + + List? _Methods; // 这个不是静态的 + /// + /// 同名函数指针们 + /// + public List? Methods + { + get + { + if (_Methods is null) + { + _Methods = new(); + + if ((_acadPeEnum & AcadPeEnum.AcadExe) == AcadPeEnum.AcadExe) + GetPeMethod(PeForAcadExe); + if ((_acadPeEnum & AcadPeEnum.AccoreDll) == AcadPeEnum.AccoreDll) + GetPeMethod(PeForAccoreDll); + if ((_acadPeEnum & AcadPeEnum.Acdb) == AcadPeEnum.Acdb) + GetPeMethod(PeForAcdbDll); + } + return _Methods; + } + } + #endregion + + #region 字段/构造 + /// + /// 用于查找PE不带修饰的函数名 + /// + string _findFuncName; + /// + /// 枚举查找对象 + /// + AcadPeEnum _acadPeEnum; + + /// + /// 通过函数名获取指针,指定类型 + /// + /// 不带修饰的函数名 + /// 读取哪个cad内部文件的枚举(目前只支持两个) + public AcadPeInfo(string methodName, AcadPeEnum acadPeEnum) + { + _findFuncName = methodName; + _acadPeEnum = acadPeEnum; + } + + /// + /// 获取CAD的函数指针 + /// + /// 委托 + /// 不带修饰的函数名 + /// 读取哪个cad内部文件的枚举(目前只支持两个) + /// 委托 + public static TDelegate? GetDelegate(string methodName, AcadPeEnum acadPeEnum) + where TDelegate : class + { + return new AcadPeInfo(methodName, acadPeEnum) + .GetDelegate(); + } + #endregion + + #region 方法 + /// + /// 储存旧值<去除修饰函数名(查找的),带修饰函数名们> + /// + static Dictionary> _Dict = new(); + + /// + /// 返回函数指针 + /// + /// Pe信息:可能来自exe/dll + /// 错误信息 + GetMethodErrorNum GetPeMethod(PeInfo? peInfo) + { + if (peInfo == null) + return GetMethodErrorNum.NoFuncName;// cad08需要检查 AccoreDll 的时候跳过 + + var identifyStr = _findFuncName + ";" + peInfo.FullName; + if (_Dict.ContainsKey(identifyStr))// 如果已经找过,直接返回 + { + _Methods = _Dict[identifyStr]; + } + else + { + _Methods ??= new(); + try + { + PeFunction.Finds(peInfo, _findFuncName, _Methods); + if (_Methods.Count != 0)// 此时从不含有 + _Dict.Add(identifyStr, _Methods); + } + catch (GetPeMethodException ex) + { return (GetMethodErrorNum)ex.ErrorNum; } + } + return GetMethodErrorNum.Ok; + } + + /// + /// 转为委托 + /// + /// 委托对象 + /// + public TDelegate? GetDelegate() where TDelegate : class + { + if (Methods is null || Methods.Count == 0) + return null; + + TDelegate? func = null; + + /* + * 0x01 + * 这里永远不报错,但是不代表不会出错. + * 调用C盘exe/dll时需要权限, + * 所以会出现:[DLLImport]可以,直接运行cad也可以,但是调试不行. + * 此时可以提权vs再调试,有时候会出现:调试不显示东西,但是运行是对的. + * + * 0x02 + * 出错时候用完整的修饰名 + * + * 0x03 + * 这里可能同时存在acad.exe和accore.dll相同指针? + * 所以我是用排序方法找最短的指针,所以它是第First个. + */ + + // 排序,最少长度原则本身就是让完全相同字符串在最前面 + // 这里替换为有序哈希,因为我总是需要不带修饰的返回函数,所以是排序长度的第一个 + _Methods = _Methods.OrderBy(str => str.CName?.Length) + .ThenBy(str => str.MethodName.Length) + .ToList(); + + func = Marshal.GetDelegateForFunctionPointer(Methods.First().GetProcAddress(), typeof(TDelegate)) as TDelegate; + return func; + } + #endregion +} + +/// +/// 通过名字查找exe/dll内所有名字 +/// +public class PeFunction +{ + #region 字段/构造 + string? _CName; + /// + /// 纯c语言名 + /// + public string? CName + { + get + { + if (_CName is null && MethodName is not null) + { + _CName = MethodName.Replace("?", string.Empty); // 剔除cpp前缀 + int num = _CName.IndexOf("@"); + if (num > -1) + _CName = _CName.Substring(0, num); // 剔除参数部分 + } + return _CName; + } + } + + /// + /// 模块文件路径 + /// + public string? ModuleFullName; + /// + /// 模块指针 + /// + public IntPtr ModuleIntPtr; + /// + /// 函数名 + /// + public string MethodName; + /// + /// 通过名字查找exe/dll内所有名字 + /// + /// 没修饰的方法名 + public PeFunction(string methodName) + { + MethodName = methodName; + } + #endregion + + /// + /// 获取函数指针 + /// + public IntPtr GetProcAddress() + { + return WindowsAPI.GetProcAddress(ModuleIntPtr, MethodName); + } + + /// + /// 通过名字查找exe/dll内所有名字 + /// + /// pe结构 + /// 用于查找的方法名 + /// 返回函数集合 + public static void Finds(PeInfo peInfo, + string findFuncName, + List funcAdress_Out) + { + if (findFuncName == null) + throw new GetPeMethodException(2, "没有找到对应的函数:" + findFuncName); + + var peModuleFullName = peInfo.FullName; + if (peModuleFullName == null) + throw new GetPeMethodException(1, "找不到模块:" + peModuleFullName + "当前程序没有加载这个东西?"); + var hModule = WindowsAPI.GetModuleHandle(peModuleFullName); // 执行前必须加载了先,acad.exe/accore.dll + if (hModule == IntPtr.Zero) + throw new GetPeMethodException(1, "找不到模块:" + peModuleFullName + "当前程序没有加载这个东西?"); + + // 遍历函数接口名单 + var names = peInfo.ExportDirectory?.FunctionNames(); + if (names == null) + throw new ArgumentException(nameof(names)); + + foreach (var name in names) + { + if (name.Contains(findFuncName))// 这里是名称含有,不是容器含有 + { + var fn = new PeFunction(name) + { + ModuleFullName = peModuleFullName, + ModuleIntPtr = hModule + }; + funcAdress_Out.Add(fn); + } + } + } +} + + + +/// +/// 错误信息 +/// +public class GetPeMethodException : ApplicationException +{ + public int ErrorNum; + public string? ErrorMsg; + public Exception? InnerException1; + + public GetPeMethodException(string msg) : base(msg) + { + ErrorMsg = msg; + } + + public GetPeMethodException(int errorNum, string msg) : base(msg) + { + ErrorNum = errorNum; + } + + public GetPeMethodException(string msg, Exception innerException) : base(msg, innerException) + { + InnerException1 = innerException; + ErrorMsg = msg; + } +} \ No newline at end of file diff --git a/src/CAD/IFox.CAD.Shared/Runtime/PE/DBmod.cs b/src/CAD/IFox.CAD.Shared/Runtime/PE/DBmod.cs new file mode 100644 index 0000000..7502e25 --- /dev/null +++ b/src/CAD/IFox.CAD.Shared/Runtime/PE/DBmod.cs @@ -0,0 +1,92 @@ +namespace IFoxCAD.Cad; + +/// +/// 获取数据库修改状态 +/// +/// 相关链接 +/// +[Flags] +public enum DBmod : short +{ + [Description("数据库冇修改")] + DatabaseNoModifies = 0, + [Description("数据库有修改")] + Database = 1, + [Description("变量有修改")] + Value = 4, + [Description("窗口有修改")] + Window = 8, + [Description("视图有修改")] + View = 16, + [Description("字段有修改")] + Field = 32 +} + +public class DBmodEx +{ + public static DBmod DBmod => (DBmod)Env.GetVar("dbmod"); + + delegate long DelegateAcdbSetDbmod(IntPtr db, DBmod newValue); + static DelegateAcdbSetDbmod? acdbSetDbmod;//别改名称 + + public static long AcdbSetDbmod(IntPtr db, DBmod newValue) + { + acdbSetDbmod ??= AcadPeInfo.GetDelegate( + nameof(acdbSetDbmod), AcadPeEnum.Acdb); + if (acdbSetDbmod is null) + return -1; + return acdbSetDbmod.Invoke(db, newValue);// 调用方法 + } + + /// + /// Dbmod 不被修改的任务 + /// + /// + public static void DBmodTask(Action action) + { + var dm = Acap.DocumentManager; + if (dm.Count == 0) + return; + var doc = dm.MdiActiveDocument; + if (doc is null) + return; + + var bak = DBmod; + action.Invoke(); + if (bak == DBmod.DatabaseNoModifies && DBmod != DBmod.DatabaseNoModifies) + AcdbSetDbmod(doc.Database.UnmanagedObject, DBmod.DatabaseNoModifies); + } + + static bool _flag = true; + /// + /// 请在无法处理的初始化才使用它 + /// (源泉在初始化的时候进行了修改数据库,所以必须要用一个新线程等待lisp执行完成才可以) + /// + public static void DatabaseNoModifies() + { + if (_flag)// 仅执行一次,在初始化时候 + { + var dm = Acap.DocumentManager; + if (dm.Count == 0) + return; + var doc = dm.MdiActiveDocument; + if (doc is null) + return; + + if (DBmod != DBmod.DatabaseNoModifies) + AcdbSetDbmod(doc.Database.UnmanagedObject, DBmod.DatabaseNoModifies); + _flag = false; + } + } + + + //[CommandMethod(nameof(TestCmd_AcdbSetDbmodChange))] + //public void TestCmd_AcdbSetDbmodChange() + //{ + // DBmodTask(() => { + // using DBTrans tr = new(); + // Line line = new(new Point3d(0, 0, 0), new Point3d(1, 1, 0)); + // tr.CurrentSpace.AddEntity(line); + // }); + //} +} \ No newline at end of file diff --git a/src/CAD/IFox.CAD.Shared/Runtime/PE/PostCmd.cs b/src/CAD/IFox.CAD.Shared/Runtime/PE/PostCmd.cs new file mode 100644 index 0000000..cd0b39e --- /dev/null +++ b/src/CAD/IFox.CAD.Shared/Runtime/PE/PostCmd.cs @@ -0,0 +1,199 @@ +namespace IFoxCAD.Cad; + +public class PostCmd +{ + /* + * #if NET35 || NET40 + * [DllImport("acad.exe", EntryPoint = "acedCmd", CharSet = CharSet.Auto, CallingConvention = CallingConvention.Cdecl)] + * #else + * // cad2015的AcedCmd改为AcedCmdS.参数也改了,不过不影响pe读取,accore.dll是2013版本后才开始改 + * [DllImport("accore.dll", EntryPoint = "acedCmdS", CharSet = CharSet.Auto, CallingConvention = CallingConvention.Cdecl)] + * static extern int AcedCmd(IntPtr rbp, bool forFutureUse = false, IntPtr pForFutureUse = IntPtr.Zero); + * #endif + * static extern int AcedCmd(IntPtr rbp); + * public static int AcedCmd(ResultBuffer args) + * { + * if (Acap.DocumentManager.IsApplicationContext) + * return 0; + * else + * return AcedCmd(args.UnmanagedObject); + * } + */ + delegate int DelegateAcedCmd(IntPtr parameter); + static DelegateAcedCmd? acedCmd;//nameof 别改名称 + /// + /// 发送命令(同步)如果2015.+这里报错,那么表示vs需要提权测试 + /// + static PromptStatus AcedCmd(ResultBuffer args) + { + if (Acap.DocumentManager.IsApplicationContext) + return 0; + if (acedCmd is null) + { + string str = nameof(acedCmd); + if (Acap.Version.Major >= 20)// 2015.+ + str += "S"; + + acedCmd = AcadPeInfo.GetDelegate( + str, AcadPeEnum.ExeAndCore); + } + if (acedCmd is null) + return 0; + + var result = (PromptStatus)acedCmd.Invoke(args.UnmanagedObject); + if (result != PromptStatus.OK) + throw new ArgumentException("发送命令出错,是否vs权限不足?"); + return result; + } + + /* + * [DllImport("accore.dll", EntryPoint = "acedCommand")] + * static extern int AcedCommand(IntPtr vlist); + */ + delegate int DelegateAcedCommand(IntPtr parameter); + static DelegateAcedCommand? acedCommand;//nameof 别改名称 + /// + /// 发送命令(同步) + /// + static PromptStatus AcedCommand(IntPtr args) + { + acedCommand ??= AcadPeInfo.GetDelegate( + nameof(acedCommand), AcadPeEnum.ExeAndCore); + if (acedCommand is null) + return PromptStatus.Error; + return (PromptStatus)acedCommand.Invoke(args);// 调用方法 + } + + /* + * [DllImport("acad.exe", CharSet = CharSet.Auto, CallingConvention = CallingConvention.Cdecl, + * EntryPoint = "?acedPostCommand@@YAHPB_W@Z")] + * public static extern int AcedPostCommand(string strExpr); + */ + delegate int DelegateAcedPostCommand(byte[] parameter); + static DelegateAcedPostCommand? acedPostCommand;//nameof 别改名称 + /// + /// 发送命令(同步) + /// 这个可以在多线程发送 + /// + static PromptStatus AcedPostCommand(string args) + { + acedPostCommand ??= AcadPeInfo.GetDelegate( + nameof(acedPostCommand), AcadPeEnum.ExeAndCore); + + // 不然到CAD之后会乱码 + byte[] bytes = Encoding.Unicode.GetBytes(args); + if (acedPostCommand is null) + return PromptStatus.Error; + return (PromptStatus)acedPostCommand.Invoke(bytes);// 调用方法 + } + + delegate int DelegateAcedInvoke(byte[] parameter); + static DelegateAcedInvoke? acedInvoke;//nameof 别改名称 + /// + /// 发送命令(同步) + /// + static PromptStatus AcedInvoke(string args) + { + acedInvoke ??= AcadPeInfo.GetDelegate( + nameof(acedInvoke), AcadPeEnum.ExeAndCore); + + // 不然到CAD之后会乱码 + byte[] bytes = Encoding.Unicode.GetBytes(args); + + if (acedInvoke is null) + return PromptStatus.Error; + return (PromptStatus)acedInvoke.Invoke(bytes);// 调用方法 + } + + /// + /// 发送命令(异步)+CommandFlags.Session可以同步发送 + /// + static void AsyncCommand(string args) + { + object[] commandArray = { args + "\n" }; +#if zcad + var com = Acap.ZcadApplication; +#else + var com = Acap.AcadApplication; +#endif + // activeDocument 加载lisp第二个文档有问题,似乎要切换了才能 + var doc = com.GetType() + .InvokeMember("ActiveDocument", BindingFlags.GetProperty, null, com, null); + doc?.GetType() + .InvokeMember("SendCommand", BindingFlags.InvokeMethod, null, doc, commandArray);// 返回值是null + } + + public enum RunCmdFlag : byte + { + AcedCmd = 1, + AcedCommand = 2, + AcedPostCommand = 4, + AcedInvoke = 8, + SendStringToExecute = 16, + AsyncCommand = 32, + } + + /* + * 发送命令会记录在命令历史 + * 发送lisp的(command "xx")就不会 + */ + public static PromptStatus SendCommand(ResultBuffer args) + { + return AcedCmd(args); + } + public static PromptStatus SendCommand(IntPtr args) + { + return AcedCommand(args); + } + public static PromptStatus SendCommand(string args, RunCmdFlag flag) + { + PromptStatus ret = PromptStatus.OK; + if (!Acap.DocumentManager.IsApplicationContext) + { + if ((flag & RunCmdFlag.AcedCmd) == RunCmdFlag.AcedCmd) + { + using ResultBuffer rb = new() + { + new((int)LispDataType.Text, args), + }; + ret = SendCommand(rb); + } + if ((flag & RunCmdFlag.AcedCommand) == RunCmdFlag.AcedCommand) + { + // 此处是这样转换吗? + using ResultBuffer rb = new() + { + new((int)LispDataType.Text, args), + }; + ret = SendCommand(rb.UnmanagedObject); + } + if ((flag & RunCmdFlag.AcedPostCommand) == RunCmdFlag.AcedPostCommand) + { + ret = AcedPostCommand(args); + } + if ((flag & RunCmdFlag.AcedInvoke) == RunCmdFlag.AcedInvoke) + { + ret = AcedInvoke(args); + } + } + else + { + var dm = Acap.DocumentManager; + var doc = dm.MdiActiveDocument; + if (doc == null) + return PromptStatus.Error; + + if ((flag & RunCmdFlag.SendStringToExecute) == RunCmdFlag.SendStringToExecute) + { + doc.SendStringToExecute(args, true, false, false); + } + if ((flag & RunCmdFlag.AsyncCommand) == RunCmdFlag.AsyncCommand) + { + // 此处+CommandFlags.Session可以同步发送,bo命令可以,其他是否可以? + // 仿人工输入,像lisp一样可以直接发送关键字 + AsyncCommand(args); + } + } + return ret; + } +} \ No newline at end of file diff --git a/src/CAD/IFox.CAD.Shared/Runtime/PE/ProgramPE.cs b/src/CAD/IFox.CAD.Shared/Runtime/PE/ProgramPE.cs new file mode 100644 index 0000000..e1f834a --- /dev/null +++ b/src/CAD/IFox.CAD.Shared/Runtime/PE/ProgramPE.cs @@ -0,0 +1,1567 @@ +namespace IFoxCAD.Cad; + +using System; +using System.Collections; +using System.Collections.Generic; +using System.Data; +using System.IO; +using System.Text; + + +/* 来源 https://blog.csdn.net/zgke/article/details/2955560 我在他基础上面增加了X64的处理 + * 调用例子 + static void Main(string[] args) + { + var path = @"C:\Program Files\Autodesk\AutoCAD 2021\acad.exe"; + // path = @"G:\AutoCAD 2008\acad.exe"; + + var pe = new JoinBox.BasalCurrency.PeInfo(path); + + // 输出所有的函数名 + var sb = new StringBuilder(); + foreach (var item in pe.ExportDirectory.NameList) + { + sb.Append(Environment.NewLine); + var str = System.Text.Encoding.Default.GetString(item as byte[]); + sb.Append(str); + } + Debugx.Printl(sb.ToString()); + + // 原作者的封装 + var ss = pe.GetPETable(); + foreach (var item in ss.Tables) + { + } + } +*/ + +/// +/// 微软软件结构PE信息 +/// +public class PeInfo +{ + #region 成员 + /// + /// 获取是否正常打开文件 + /// + public bool OpenFile { get; private set; } = false; + public DosHeader? DosHeader { get; private set; } + public DosStub? DosStub { get; private set; } + public PEHeader? PEHeader { get; private set; } + public OptionalHeader? OptionalHeader { get; private set; } + public OptionalDirAttrib? OptionalDirAttrib { get; private set; } + public SectionTable? SectionTable { get; private set; } + /// + /// 函数接口名单 + /// + public ExportDirectory? ExportDirectory { get; private set; } + public ImportDirectory? ImportDirectory { get; private set; } + public ResourceDirectory? ResourceDirectory { get; private set; } + /// + /// PE文件完整路径 + /// + public string? FullName; + + bool _IsX86 = true; + + /// + /// 全部文件数据 + /// + readonly byte[]? _PEFileByte; + /// + /// 文件读取的位置 + /// + long _PEFileIndex = 0; + #endregion + + #region 构造 + public PeInfo(string fullName) + { + if (fullName is null) + throw new ArgumentException(nameof(fullName)); ; + + FullName = fullName; + FileStream? file = null; + OpenFile = false; + try + { + // 文件流 + file = new FileStream(fullName, FileMode.Open, FileAccess.Read, FileShare.ReadWrite);// FileShare才能进c盘 + _PEFileByte = new byte[file.Length]; + file.Read(_PEFileByte, 0, _PEFileByte.Length); + LoadFile(); + OpenFile = true; + } + catch (Exception) { throw; } + finally + { + file?.Close(); + } + } + #endregion + + #region 读表方法 + /// + /// 开始读取 + /// + private void LoadFile() + { + LoadDosHeader(); // 获取DOS头,为了兼容DOS所以首先处理这个,然后才到PE头 + LoadDosStub(); // 获取DOS的身体 + LoadPEHeader(); // PE头 + LoadOptionalHeader(); // PE头扩展 + LoadOptionalDirAttrib(); // 获取选项目录属性 + LoadSectionTable(); // 获取节表 + LoadExportDirectory(); // 获取输出表 + LoadImportDirectory(); // 获取输入表 + LoadResourceDirectory(); // 获取资源目录 + } + + /// + /// 获得DOS头 + /// + private void LoadDosHeader() + { + DosHeader = new DosHeader + { + FileStarIndex = _PEFileIndex + }; + Loadbyte(ref DosHeader.e_magic); + Loadbyte(ref DosHeader.e_cblp); + Loadbyte(ref DosHeader.e_cp); + Loadbyte(ref DosHeader.e_crlc); + Loadbyte(ref DosHeader.e_cparhdr); + Loadbyte(ref DosHeader.e_minalloc); + Loadbyte(ref DosHeader.e_maxalloc); + Loadbyte(ref DosHeader.e_ss); + Loadbyte(ref DosHeader.e_sp); + Loadbyte(ref DosHeader.e_csum); + Loadbyte(ref DosHeader.e_ip); + Loadbyte(ref DosHeader.e_cs); + Loadbyte(ref DosHeader.e_rva); + Loadbyte(ref DosHeader.e_fg); + Loadbyte(ref DosHeader.e_bl1); + Loadbyte(ref DosHeader.e_oemid); + Loadbyte(ref DosHeader.e_oeminfo); + Loadbyte(ref DosHeader.e_bl2); + Loadbyte(ref DosHeader.e_PESTAR); + + DosHeader.FileEndIndex = _PEFileIndex; + } + + /// + /// 获得DOS SUB字段 + /// + private void LoadDosStub() + { + if (DosHeader is null) + return; + + long Size = GetLong(DosHeader.e_PESTAR) - _PEFileIndex; // 获得SUB的大小 + DosStub = new DosStub(Size) + { + FileStarIndex = _PEFileIndex + }; + Loadbyte(ref DosStub.DosStubData); + DosStub.FileEndIndex = _PEFileIndex; + } + + /// + /// 获得PE的文件头 + /// + /// + private void LoadPEHeader() + { + PEHeader = new PEHeader + { + FileStarIndex = _PEFileIndex + }; + Loadbyte(ref PEHeader.Header); + Loadbyte(ref PEHeader.Machine);// [76 1]==[0x4C 1]是x32, + _IsX86 = PEHeader.Machine[0] == 0x4c && PEHeader.Machine[1] == 0x1; + // (PEHeader.Machine[0] == 0x64 && PEHeader.Machine[1] == 0x86)// x64 + Loadbyte(ref PEHeader.NumberOfSections); + Loadbyte(ref PEHeader.TimeDateStamp); + Loadbyte(ref PEHeader.PointerToSymbolTable); + Loadbyte(ref PEHeader.NumberOfSymbols); + Loadbyte(ref PEHeader.SizeOfOptionalHeader); + Loadbyte(ref PEHeader.Characteristics); + PEHeader.FileEndIndex = _PEFileIndex; + } + + /// + /// 获得OPTIONAL PE扩展属性 + /// + /// + private void LoadOptionalHeader() + { + // 这里必须通过PE文件来判断它是一个什么架构,从而使用x86-x64 + OptionalHeader = new OptionalHeader(_IsX86) + { + FileStarIndex = _PEFileIndex + }; + Loadbyte(ref OptionalHeader.Magic); + Loadbyte(ref OptionalHeader.MajorLinkerVersion); + Loadbyte(ref OptionalHeader.MinorLinkerVersion); + Loadbyte(ref OptionalHeader.SizeOfCode); + Loadbyte(ref OptionalHeader.SizeOfInitializedData); + Loadbyte(ref OptionalHeader.SizeOfUninitializedData); + Loadbyte(ref OptionalHeader.AddressOfEntryPoint); + Loadbyte(ref OptionalHeader.BaseOfCode); + Loadbyte(ref OptionalHeader.ImageBase); + Loadbyte(ref OptionalHeader.BaseOfData); + Loadbyte(ref OptionalHeader.SectionAlignment); + Loadbyte(ref OptionalHeader.FileAlignment); + + Loadbyte(ref OptionalHeader.MajorOperatingSystemVersion); + Loadbyte(ref OptionalHeader.MinorOperatingSystemVersion); + Loadbyte(ref OptionalHeader.MajorImageVersion); + Loadbyte(ref OptionalHeader.MinorImageVersion); + Loadbyte(ref OptionalHeader.MajorSubsystemVersion); + Loadbyte(ref OptionalHeader.MinorSubsystemVersion); + Loadbyte(ref OptionalHeader.Win32VersionValue); + Loadbyte(ref OptionalHeader.SizeOfImage); + Loadbyte(ref OptionalHeader.SizeOfHeards); + Loadbyte(ref OptionalHeader.CheckSum); + Loadbyte(ref OptionalHeader.Subsystem); + Loadbyte(ref OptionalHeader.DLLCharacteristics); + Loadbyte(ref OptionalHeader.SizeOfStackReserve); + Loadbyte(ref OptionalHeader.SizeOfStackCommit); + Loadbyte(ref OptionalHeader.SizeOfHeapReserve); + Loadbyte(ref OptionalHeader.SizeOfHeapCommit); + Loadbyte(ref OptionalHeader.LoaderFlags); + Loadbyte(ref OptionalHeader.NumberOfRvaAndSizes); + + OptionalHeader.FileEndIndex = _PEFileIndex; + } + + /// + /// 获取目录表 + /// + /// + private void LoadOptionalDirAttrib() + { + if (OptionalHeader is null) + return; + + OptionalDirAttrib = new OptionalDirAttrib + { + FileStarIndex = _PEFileIndex + }; + + long DirCount = GetLong(OptionalHeader.NumberOfRvaAndSizes);// 这里导致无法使用64位 + for (int i = 0; i != DirCount; i++) + { + OptionalDirAttrib.DirAttrib? directAttrib = new(); + Loadbyte(ref directAttrib.DirRva); + Loadbyte(ref directAttrib.DirSize); + OptionalDirAttrib.DirByte.Add(directAttrib); + } + OptionalDirAttrib.FileEndIndex = _PEFileIndex; + } + + /// + /// 获取节表 + /// + private void LoadSectionTable() + { + if (PEHeader is null) + return; + + SectionTable = new SectionTable(); + long Count = GetLong(PEHeader.NumberOfSections); + SectionTable.FileStarIndex = _PEFileIndex; + for (long i = 0; i != Count; i++) + { + var Section = new SectionTable.SectionData(); + + Loadbyte(ref Section.SectName); + Loadbyte(ref Section.VirtualAddress); + Loadbyte(ref Section.SizeOfRawDataRVA); + Loadbyte(ref Section.SizeOfRawDataSize); + Loadbyte(ref Section.PointerToRawData); + Loadbyte(ref Section.PointerToRelocations); + Loadbyte(ref Section.PointerToLinenumbers); + Loadbyte(ref Section.NumberOfRelocations); + Loadbyte(ref Section.NumberOfLinenumbers); + Loadbyte(ref Section.Characteristics); + SectionTable.Section.Add(Section); + } + SectionTable.FileEndIndex = _PEFileIndex; + } + + /// + /// 读取输出表 + /// + private void LoadExportDirectory() + { + if (OptionalDirAttrib is null) + return; + if (OptionalDirAttrib.DirByte.Count == 0) + return; + + if (OptionalDirAttrib.DirByte[0] is not OptionalDirAttrib.DirAttrib exporRVA || + GetLong(exporRVA.DirRva) == 0) + return; + + long exporAddress = GetLong(exporRVA.DirRva); // 获取的位置 + ExportDirectory = new ExportDirectory(); + + if (SectionTable is null) + return; + + for (int i = 0; i != SectionTable.Section.Count; i++) // 循环节表 + { + if (SectionTable.Section[i] is not SectionTable.SectionData sect) + continue; + + long starRva = GetLong(sect.SizeOfRawDataRVA); + long endRva = GetLong(sect.SizeOfRawDataSize); + + if (exporAddress >= starRva && exporAddress < starRva + endRva) + { + _PEFileIndex = exporAddress - GetLong(sect.SizeOfRawDataRVA) + GetLong(sect.PointerToRawData); + + ExportDirectory.FileStarIndex = _PEFileIndex; + ExportDirectory.FileEndIndex = _PEFileIndex + GetLong(exporRVA.DirSize); + + Loadbyte(ref ExportDirectory.Characteristics); + Loadbyte(ref ExportDirectory.TimeDateStamp); + Loadbyte(ref ExportDirectory.MajorVersion); + Loadbyte(ref ExportDirectory.MinorVersion); + Loadbyte(ref ExportDirectory.Name); + Loadbyte(ref ExportDirectory.Base); + Loadbyte(ref ExportDirectory.NumberOfFunctions); + Loadbyte(ref ExportDirectory.NumberOfNames); + Loadbyte(ref ExportDirectory.AddressOfFunctions); + Loadbyte(ref ExportDirectory.AddressOfNames); + Loadbyte(ref ExportDirectory.AddressOfNameOrdinals); + + _PEFileIndex = GetLong(ExportDirectory.AddressOfFunctions) - GetLong(sect.SizeOfRawDataRVA) + GetLong(sect.PointerToRawData); + long endIndex = GetLong(ExportDirectory.AddressOfNames) - GetLong(sect.SizeOfRawDataRVA) + GetLong(sect.PointerToRawData); + long numb = (endIndex - _PEFileIndex) / 4; + for (long z = 0; z != numb; z++) + { + byte[] Data = new byte[4]; + Loadbyte(ref Data); + ExportDirectory.AddressOfFunctionsList.Add(Data); + } + + _PEFileIndex = endIndex; + endIndex = GetLong(ExportDirectory.AddressOfNameOrdinals) - GetLong(sect.SizeOfRawDataRVA) + GetLong(sect.PointerToRawData); + numb = (endIndex - _PEFileIndex) / 4; + for (long z = 0; z != numb; z++) + { + byte[] Data = new byte[4]; + Loadbyte(ref Data); + ExportDirectory.AddressOfNamesList.Add(Data); + } + + _PEFileIndex = endIndex; + endIndex = GetLong(ExportDirectory.Name) - GetLong(sect.SizeOfRawDataRVA) + GetLong(sect.PointerToRawData); + numb = (endIndex - _PEFileIndex) / 2; + for (long z = 0; z != numb; z++) + { + byte[] Data = new byte[2]; + Loadbyte(ref Data); + ExportDirectory.AddressOfNameOrdinalsList.Add(Data); + } + + _PEFileIndex = endIndex; + + if (_PEFileByte is not null) + { + long ReadIndex = 0; + while (true) + { + if (_PEFileByte[_PEFileIndex + ReadIndex] == 0) + { + if (_PEFileByte[_PEFileIndex + ReadIndex + 1] == 0) + break; + + var Date = new byte[ReadIndex]; + Loadbyte(ref Date); + ExportDirectory.FunctionNamesByte.Add(Date); + + _PEFileIndex++; + ReadIndex = 0; + } + ReadIndex++; + } + } + break; + } + } + } + + /// + /// 读取输入表 + /// + private void LoadImportDirectory() + { + if (OptionalDirAttrib is null) + return; + if (OptionalDirAttrib.DirByte.Count < 1) + return; + if (OptionalDirAttrib.DirByte[1] is not OptionalDirAttrib.DirAttrib ImporRVA) + return; + + long ImporAddress = GetLong(ImporRVA.DirRva); // 获取的位置 + if (ImporAddress == 0) + return; + long ImporSize = GetLong(ImporRVA.DirSize); // 获取大小 + + ImportDirectory = new ImportDirectory(); + + long SizeRva = 0; + long PointerRva = 0; + + long StarRva = 0; + long EndRva = 0; + + #region 获取位置 + if (SectionTable is null) + return; + for (int i = 0; i != SectionTable.Section.Count; i++) // 循环节表 + { + if (SectionTable.Section[i] is not SectionTable.SectionData Sect) + continue; + + StarRva = GetLong(Sect.SizeOfRawDataRVA); + EndRva = GetLong(Sect.SizeOfRawDataSize); + + if (ImporAddress >= StarRva && ImporAddress < StarRva + EndRva) + { + SizeRva = GetLong(Sect.SizeOfRawDataRVA); + PointerRva = GetLong(Sect.PointerToRawData); + _PEFileIndex = ImporAddress - SizeRva + PointerRva; + + ImportDirectory.FileStarIndex = _PEFileIndex; + ImportDirectory.FileEndIndex = _PEFileIndex + ImporSize; + break; + } + } + + if (SizeRva == 0 && PointerRva == 0) + return; + #endregion + + #region 输入表结构 + while (true) + { + var import = new ImportDirectory.ImportDate(); + Loadbyte(ref import.OriginalFirstThunk); + Loadbyte(ref import.TimeDateStamp); + Loadbyte(ref import.ForwarderChain); + Loadbyte(ref import.Name); + Loadbyte(ref import.FirstThunk); + + if (GetLong(import.Name) == 0) + break; + ImportDirectory.ImportList.Add(import); // 添加 + } + #endregion + + + #region 获取输入DLL名称 + for (int z = 0; z != ImportDirectory.ImportList.Count; z++) // 获取引入DLL名字 + { + if (ImportDirectory.ImportList[z] is not ImportDirectory.ImportDate Import) + continue; + + long ImportDLLName = GetLong(Import.Name) - SizeRva + PointerRva; + _PEFileIndex = ImportDLLName; + long ReadCount = 0; + while (_PEFileByte is not null) // 获取引入名 + { + if (_PEFileByte[_PEFileIndex + ReadCount] == 0) + { + Import.DLLName = new byte[ReadCount]; + Loadbyte(ref Import.DLLName); + break; + } + ReadCount++; + } + } + #endregion + + #region 获取引入方法 先获取地址 然后获取名字和头 + for (int z = 0; z != ImportDirectory.ImportList.Count; z++) // 获取引入方法 + { + if (ImportDirectory.ImportList[z] is not ImportDirectory.ImportDate import) + continue; + + long importDLLName = GetLong(import.OriginalFirstThunk) - SizeRva + PointerRva; + _PEFileIndex = importDLLName; + while (true) + { + var function = new ImportDirectory.ImportDate.FunctionList(); + Loadbyte(ref function.OriginalFirst); + + long loadIndex = GetLong(function.OriginalFirst); + if (loadIndex == 0) + break; + long oldIndex = _PEFileIndex; + + _PEFileIndex = loadIndex - SizeRva + PointerRva; + + if (loadIndex >= StarRva && loadIndex < StarRva + EndRva) // 发现有些数字超级大 + { + int ReadCount = 0; + + while (_PEFileByte is not null) + { + if (ReadCount == 0) + Loadbyte(ref function.FunctionHead); + if (_PEFileByte[_PEFileIndex + ReadCount] == 0) + { + byte[] FunctionName = new byte[ReadCount]; + Loadbyte(ref FunctionName); + function.FunctionName = FunctionName; + + break; + } + ReadCount++; + } + } + else + { + function.FunctionName = new byte[1]; + } + + _PEFileIndex = oldIndex; + import.DLLFunctionList.Add(function); + } + } + #endregion + } + + /// + /// 读取资源表 + /// + private void LoadResourceDirectory() + { + #region 初始化 + if (OptionalDirAttrib is null || OptionalDirAttrib.DirByte.Count < 3) + return; + if (OptionalDirAttrib.DirByte[2] is not OptionalDirAttrib.DirAttrib ImporRVA) + return; + + long ImporAddress = GetLong(ImporRVA.DirRva); // 获取的位置 + if (ImporAddress == 0) + return; + long ImporSize = GetLong(ImporRVA.DirSize); // 获取大小 + + ResourceDirectory = new ResourceDirectory(); + + long SizeRva = 0; + long PointerRva = 0; + long StarRva = 0; + long PEIndex = 0; + #endregion + + #region 获取位置 + if (SectionTable is null) + return; + + for (int i = 0; i != SectionTable.Section.Count; i++) // 循环节表 + { + if (SectionTable.Section[i] is not SectionTable.SectionData sect) + continue; + + StarRva = GetLong(sect.SizeOfRawDataRVA); + var EndRva = GetLong(sect.SizeOfRawDataSize); + if (ImporAddress >= StarRva && ImporAddress < StarRva + EndRva) + { + SizeRva = GetLong(sect.SizeOfRawDataRVA); + PointerRva = GetLong(sect.PointerToRawData); + _PEFileIndex = ImporAddress - SizeRva + PointerRva; + PEIndex = _PEFileIndex; + ResourceDirectory.FileStarIndex = _PEFileIndex; + ResourceDirectory.FileEndIndex = _PEFileIndex + ImporSize; + break; + } + } + + if (SizeRva == 0 && PointerRva == 0) + return; + #endregion + + AddResourceNode(ResourceDirectory, PEIndex, 0, StarRva); + } + + /// + /// 添加资源节点 + /// + /// + /// + /// + /// + private void AddResourceNode(ResourceDirectory node, long PEIndex, long RVA, long resourSectRva) + { + _PEFileIndex = PEIndex + RVA; // 设置位置 + Loadbyte(ref node.Characteristics); + Loadbyte(ref node.TimeDateStamp); + Loadbyte(ref node.MajorVersion); + Loadbyte(ref node.MinorVersion); + Loadbyte(ref node.NumberOfNamedEntries); + Loadbyte(ref node.NumberOfIdEntries); + + long NameRVA = GetLong(node.NumberOfNamedEntries); + for (int i = 0; i != NameRVA; i++) + { + var Entry = new ResourceDirectory.DirectoryEntry(); + Loadbyte(ref Entry.Name); + Loadbyte(ref Entry.Id); + byte[] temp = new byte[2]; + temp[0] = Entry.Name[0]; + temp[1] = Entry.Name[1]; + + if (_PEFileByte is null) + return; + + long NameIndex = GetLong(temp) + PEIndex; + temp[0] = _PEFileByte[NameIndex + 0]; + temp[1] = _PEFileByte[NameIndex + 1]; + + long NameCount = GetLong(temp); + node.Name = new byte[NameCount * 2]; + + for (int z = 0; z != node.Name.Length; z++) + node.Name[z] = _PEFileByte[NameIndex + 2 + z]; + // System.Windows.Forms.MessageBox.Show(GetString(Entry.ID)); + + temp[0] = Entry.Id[2]; + temp[1] = Entry.Id[3]; + + long oldIndex = _PEFileIndex; + + if (GetLong(temp) == 0) + { + temp[0] = Entry.Id[0]; + temp[1] = Entry.Id[1]; + + _PEFileIndex = GetLong(temp) + PEIndex; + + var dataRVA = new ResourceDirectory.DirectoryEntry.DataEntry(); + + Loadbyte(ref dataRVA.ResourRVA); + Loadbyte(ref dataRVA.ResourSize); + Loadbyte(ref dataRVA.ResourTest); + Loadbyte(ref dataRVA.ResourWen); + + _PEFileIndex = oldIndex; + Entry.DataEntryList.Add(dataRVA); + // System.Windows.Forms.MessageBox.Show(GetString(DataRVA.ResourRVA)+"*"+GetString(DataRVA.ResourSize)); + } + else + { + temp[0] = Entry.Id[0]; + temp[1] = Entry.Id[1]; + + var Resource = new ResourceDirectory(); + Entry.NodeDirectoryList.Add(Resource); + AddResourceNode(Resource, PEIndex, GetLong(temp), resourSectRva); + } + _PEFileIndex = oldIndex; + node.EntryList.Add(Entry); + } + + long Count = GetLong(node.NumberOfIdEntries); + for (int i = 0; i != Count; i++) + { + var entry = new ResourceDirectory.DirectoryEntry(); + Loadbyte(ref entry.Name); + Loadbyte(ref entry.Id); + // System.Windows.Forms.MessageBox.Show(GetString(Entry.Name)+"_"+GetString(Entry.Id)); + + byte[] temp = new byte[2]; + temp[0] = entry.Id[2]; + temp[1] = entry.Id[3]; + + long OldIndex = _PEFileIndex; + + if (GetLong(temp) == 0) + { + temp[0] = entry.Id[0]; + temp[1] = entry.Id[1]; + + _PEFileIndex = GetLong(temp) + PEIndex; + + var dataRVA = new ResourceDirectory.DirectoryEntry.DataEntry(); + Loadbyte(ref dataRVA.ResourRVA); + Loadbyte(ref dataRVA.ResourSize); + Loadbyte(ref dataRVA.ResourTest); + Loadbyte(ref dataRVA.ResourWen); + + long FileRva = GetLong(dataRVA.ResourRVA) - resourSectRva + PEIndex; + + dataRVA.FileStarIndex = FileRva; + dataRVA.FileEndIndex = FileRva + GetLong(dataRVA.ResourSize); + + _PEFileIndex = OldIndex; + entry.DataEntryList.Add(dataRVA); + // System.Windows.Forms.MessageBox.Show(GetString(DataRVA.ResourRVA)+"*"+GetString(DataRVA.ResourSize)); + } + else + { + temp[0] = entry.Id[0]; + temp[1] = entry.Id[1]; + var Resource = new ResourceDirectory(); + entry.NodeDirectoryList.Add(Resource); + AddResourceNode(Resource, PEIndex, GetLong(temp), resourSectRva); + } + _PEFileIndex = OldIndex; + node.EntryList.Add(entry); + } + } + + #endregion + + #region 工具方法 + /// + /// 读数据 读byte[]的数量 会改边PEFileIndex的值 + /// + /// + private void Loadbyte(ref byte[] data) + { + if (_PEFileByte is null) + return; + + for (int i = 0; i != data.Length; i++) + { + data[i] = _PEFileByte[_PEFileIndex]; + _PEFileIndex++; + } + } + /// + /// 转换byte为字符串 + /// + /// byte[] + /// AA BB CC DD + private string GetString(byte[] data) + { + string Temp = ""; + for (int i = 0; i != data.Length - 1; i++) + Temp += data[i].ToString("X02") + " "; + + Temp += data[data.Length - 1].ToString("X02"); + // Temp += data[^1].ToString("X02"); + return Temp; + } + /// + /// 转换字符为显示数据 + /// + /// byte[] + /// ASCII DEFAULT UNICODE BYTE + /// + private string GetString(byte[] data, string type) + { + if (type.Trim().ToUpper() == "ASCII") + return System.Text.Encoding.ASCII.GetString(data); + if (type.Trim().ToUpper() == "DEFAULT") + return System.Text.Encoding.Default.GetString(data); + if (type.Trim().ToUpper() == "UNICODE") + return System.Text.Encoding.Unicode.GetString(data); + if (type.Trim().ToUpper() == "BYTE") + { + string Temp = ""; + for (int i = data.Length - 1; i != 0; i--) + Temp += data[i].ToString("X02") + " "; + Temp += data[0].ToString("X02"); + return Temp; + } + return GetInt(data); + } + /// + /// 转换BYTE为INT + /// + /// + /// + static string GetInt(byte[] data) + { + string Temp = ""; + for (int i = 0; i != data.Length - 1; i++) + { + int ByteInt = (int)data[i]; + Temp += ByteInt.ToString() + " "; + } + int EndByteInt = (int)data[data.Length - 1]; + // int EndByteInt = (int)data[^1]; + Temp += EndByteInt.ToString(); + return Temp; + } + /// + /// 转换数据为LONG + /// + /// + /// + private long GetLong(byte[] data) + { + if (data.Length > 4) + return 0; + + string MC = ""; + // if (data.Length <= 4) + for (int i = data.Length - 1; i != -1; i--) + MC += data[i].ToString("X02"); + return Convert.ToInt64(MC, 16); + } + /// + /// 添加一行信息 + /// + /// 表 + /// 数据 + /// 名称 + /// 说明 + private void AddTableRow(DataTable refTable, byte[]? data, string name, string describe) + { + if (data == null) + throw new ArgumentException(nameof(data)); + + refTable.Rows.Add( + new string[] + { + name, + data.Length.ToString(), + GetString(data), + GetLong(data).ToString(), + GetString(data,"ASCII"), + describe + }); + } + #endregion + + #region Table绘制 + /// + /// 获取PE信息 DataSet方式 + /// + /// 多个表 最后资源表 绘制成树结构TABLE + public DataSet? GetPETable() + { + if (OpenFile == false) + return null; + + var ds = new DataSet("PEFile"); + var a1 = TableDosHeader(); + if (a1 is not null) + ds.Tables.Add(a1); + var a2 = TablePEHeader(); + if (a2 is not null) + ds.Tables.Add(a2); + var a3 = TableOptionalHeader(); + if (a3 is not null) + ds.Tables.Add(a3); + var a4 = TableOptionalDirAttrib(); + if (a4 is not null) + ds.Tables.Add(a4); + var a5 = TableSectionData(); + if (a5 is not null) + ds.Tables.Add(a5); + + + var a6 = TableExportDirectory(); + var a7 = TableExportFunction(); + if (a6 is not null) + ds.Tables.Add(a6); + if (a7 is not null) + ds.Tables.Add(a7); + + var a8 = TableImportDirectory(); + var a9 = TableImportFunction(); + if (a8 is not null) + ds.Tables.Add(a8); + if (a9 is not null) + ds.Tables.Add(a9); + + var a10 = TableResourceDirectory(); + if (a10 is not null) + ds.Tables.Add(a10); + + return ds; + } + + private DataTable? TableDosHeader() + { + if (DosHeader is null) + return null; + + var returnTable = new DataTable("DosHeader FileStar{" + DosHeader.FileStarIndex.ToString() + "}FileEnd{" + DosHeader.FileEndIndex.ToString() + "}"); + returnTable.Columns.Add("Name"); + returnTable.Columns.Add("Size"); + returnTable.Columns.Add("Value16"); + returnTable.Columns.Add("Value10"); + returnTable.Columns.Add("ASCII"); + returnTable.Columns.Add("Describe"); + + AddTableRow(returnTable, DosHeader.e_magic, "e_magic", "魔术数字"); + AddTableRow(returnTable, DosHeader.e_cblp, "e_cblp", "文件最后页的字节数"); + AddTableRow(returnTable, DosHeader.e_cp, "e_cp", "文件页数"); + AddTableRow(returnTable, DosHeader.e_crlc, "e_crlc", "重定义元素个数"); + AddTableRow(returnTable, DosHeader.e_cparhdr, "e_cparhdr", "头部尺寸,以段落为单位"); + AddTableRow(returnTable, DosHeader.e_minalloc, "e_minalloc", "所需的最小附加段"); + AddTableRow(returnTable, DosHeader.e_maxalloc, "e_maxalloc", "所需的最大附加段"); + AddTableRow(returnTable, DosHeader.e_ss, "e_ss", "初始的SS值(相对偏移量)"); + AddTableRow(returnTable, DosHeader.e_sp, "e_sp", "初始的SP值"); + AddTableRow(returnTable, DosHeader.e_csum, "e_csum", "校验和"); + AddTableRow(returnTable, DosHeader.e_ip, "e_ip", "初始的IP值"); + AddTableRow(returnTable, DosHeader.e_cs, "e_cs", "初始的CS值(相对偏移量)"); + AddTableRow(returnTable, DosHeader.e_rva, "e_rva", ""); + AddTableRow(returnTable, DosHeader.e_fg, "e_fg", ""); + AddTableRow(returnTable, DosHeader.e_bl1, "e_bl1", ""); + AddTableRow(returnTable, DosHeader.e_oemid, "e_oemid", ""); + AddTableRow(returnTable, DosHeader.e_oeminfo, "e_oeminfo", ""); + AddTableRow(returnTable, DosHeader.e_bl2, "e_bl2", ""); + AddTableRow(returnTable, DosHeader.e_PESTAR, "e_PESTAR", "PE开始 +本结构的位置"); + + return returnTable; + } + + private DataTable? TablePEHeader() + { + if (PEHeader is null) + return null; + + var returnTable = new DataTable("PeHeader FileStar{" + PEHeader.FileStarIndex.ToString() + "}FileEnd{" + PEHeader.FileEndIndex.ToString() + "}"); + returnTable.Columns.Add("Name"); + returnTable.Columns.Add("Size"); + returnTable.Columns.Add("Value16"); + returnTable.Columns.Add("Value10"); + returnTable.Columns.Add("ASCII"); + returnTable.Columns.Add("Describe"); + + + AddTableRow(returnTable, PEHeader.Header, "Header", "PE文件标记"); + AddTableRow(returnTable, PEHeader.Machine, "Machine", "该文件运行所要求的CPU.对于Intel平台,该值是IMAGE_FILE_MACHINE_I386 (14Ch).我们尝试了LUEVELSMEYER的pe.txt声明的14Dh和14Eh,但Windows不能正确执行. "); + AddTableRow(returnTable, PEHeader.NumberOfSections, "NumberOfSections", "文件的节数目.如果我们要在文件中增加或删除一个节,就需要修改这个值."); + AddTableRow(returnTable, PEHeader.TimeDateStamp, "TimeDateStamp", "文件创建日期和时间. "); + AddTableRow(returnTable, PEHeader.PointerToSymbolTable, "PointerToSymbolTable", "用于调试. "); + AddTableRow(returnTable, PEHeader.NumberOfSymbols, "NumberOfSymbols", "用于调试. "); + AddTableRow(returnTable, PEHeader.SizeOfOptionalHeader, "SizeOfOptionalHeader", "指示紧随本结构之后的 OptionalHeader 结构大小,必须为有效值."); + AddTableRow(returnTable, PEHeader.Characteristics, "Characteristics", "关于文件信息的标记,比如文件是exe还是dll."); + + return returnTable; + } + + private DataTable? TableOptionalHeader() + { + if (OptionalHeader is null) + return null; + + var returnTable = new DataTable("OptionalHeader FileStar{" + OptionalHeader.FileStarIndex.ToString() + "}FileEnd{" + OptionalHeader.FileEndIndex.ToString() + "}"); + returnTable.Columns.Add("Name"); + returnTable.Columns.Add("Size"); + returnTable.Columns.Add("Value16"); + returnTable.Columns.Add("Value10"); + returnTable.Columns.Add("ASCII"); + returnTable.Columns.Add("Describe"); + + AddTableRow(returnTable, OptionalHeader.Magic, "Magic", "Magic 010B=普通可以执行,0107=ROM映像"); + AddTableRow(returnTable, OptionalHeader.MajorLinkerVersion, "MajorLinkerVersion", "主版本号"); + AddTableRow(returnTable, OptionalHeader.MinorLinkerVersion, "MinorLinkerVersion", "副版本号"); + AddTableRow(returnTable, OptionalHeader.SizeOfCode, "SizeOfCode", "代码段大小"); + AddTableRow(returnTable, OptionalHeader.SizeOfInitializedData, "SizeOfInitializedData", "已初始化数据大小"); + AddTableRow(returnTable, OptionalHeader.SizeOfUninitializedData, "SizeOfUninitializedData", "未初始化数据大小"); + AddTableRow(returnTable, OptionalHeader.AddressOfEntryPoint, "AddressOfEntryPoint", "执行将从这里开始(RVA)"); + AddTableRow(returnTable, OptionalHeader.BaseOfCode, "BaseOfCode", "代码基址(RVA)"); + AddTableRow(returnTable, OptionalHeader.ImageBase, "ImageBase", "数据基址(RVA)"); + if (_IsX86) + { + AddTableRow(returnTable, OptionalHeader.BaseOfData, "ImageFileCode", "映象文件基址"); + } + AddTableRow(returnTable, OptionalHeader.SectionAlignment, "SectionAlign", "区段列队"); + AddTableRow(returnTable, OptionalHeader.MajorOperatingSystemVersion, "MajorOSV", "文件列队"); + AddTableRow(returnTable, OptionalHeader.MinorOperatingSystemVersion, "MinorOSV", "操作系统主版本号"); + AddTableRow(returnTable, OptionalHeader.MajorImageVersion, "MajorImageVer", "映象文件主版本号"); + AddTableRow(returnTable, OptionalHeader.MinorImageVersion, "MinorImageVer", "映象文件副版本号"); + AddTableRow(returnTable, OptionalHeader.MajorSubsystemVersion, "MajorSV", "子操作系统主版本号"); + AddTableRow(returnTable, OptionalHeader.MinorSubsystemVersion, "MinorSV", "子操作系统副版本号"); + AddTableRow(returnTable, OptionalHeader.Win32VersionValue, "UNKNOW", "Win32版本值"); + AddTableRow(returnTable, OptionalHeader.SizeOfImage, "SizeOfImage", "映象文件大小"); + AddTableRow(returnTable, OptionalHeader.SizeOfHeards, "SizeOfHeards", "标志头大小"); + AddTableRow(returnTable, OptionalHeader.CheckSum, "CheckSum", "文件效验"); + AddTableRow(returnTable, OptionalHeader.Subsystem, "Subsystem", "子系统(映象文件)1本地 2WINDOWS-GUI 3WINDOWS-CUI 4 POSIX-CUI"); + AddTableRow(returnTable, OptionalHeader.DLLCharacteristics, "DLL_Characteristics", "DLL标记"); + AddTableRow(returnTable, OptionalHeader.SizeOfStackReserve, "Bsize", "保留栈的大小"); + AddTableRow(returnTable, OptionalHeader.SizeOfStackCommit, "TimeBsize", "初始时指定栈大小"); + AddTableRow(returnTable, OptionalHeader.SizeOfHeapReserve, "AucBsize", "保留堆的大小"); + AddTableRow(returnTable, OptionalHeader.SizeOfHeapCommit, "SizeOfBsize", "初始时指定堆大小"); + AddTableRow(returnTable, OptionalHeader.LoaderFlags, "FuckBsize", "加载器标志"); + AddTableRow(returnTable, OptionalHeader.NumberOfRvaAndSizes, "DirectCount", "数据目录数"); + + return returnTable; + } + + private DataTable? TableOptionalDirAttrib() + { + if (OptionalDirAttrib is null) + return null; + + var returnTable = new DataTable( + "OptionalDirAttrib FileStar{" + + OptionalDirAttrib.FileStarIndex.ToString() + + "}FileEnd{" + + OptionalDirAttrib.FileEndIndex.ToString() + + "}"); + returnTable.Columns.Add("Name"); + returnTable.Columns.Add("Size"); + returnTable.Columns.Add("Value16"); + returnTable.Columns.Add("Value10"); + returnTable.Columns.Add("ASCII"); + returnTable.Columns.Add("Describe"); + + var tableName = new Hashtable + { + { 0, "输出表" }, + { 1, "输入表" }, + { 2, "资源表" }, + { 3, "异常表" }, + { 4, "安全表" }, + { 5, "基部重定位表" }, + { 6, "调试数据" }, + { 7, "版权数据" }, + { 8, "全局PTR" }, + { 9, "TLS表" }, + { 10, "装入配置表" }, + { 11, "其他表1" }, + { 12, "其他表2" }, + { 13, "其他表3" }, + { 14, "其他表4" }, + { 15, "其他表5" } + }; + + for (int i = 0; i != OptionalDirAttrib.DirByte.Count; i++) + { + if (OptionalDirAttrib.DirByte[i] is not OptionalDirAttrib.DirAttrib MyDirByte) + continue; + + string? Name; + var tn = tableName[i]; + if (tn is not null) + Name = tn.ToString(); + else + Name = $"未知表{i}"; + AddTableRow(returnTable, MyDirByte.DirRva, Name!, "地址"); + AddTableRow(returnTable, MyDirByte.DirSize, "", "大小"); + } + return returnTable; + } + + private DataTable? TableSectionData() + { + if (SectionTable is null) + return null; + + var returnTable = new DataTable( + "SectionData FileStar{" + + SectionTable.FileStarIndex.ToString() + + "}FileEnd{" + + SectionTable.FileEndIndex.ToString() + + "}"); + returnTable.Columns.Add("Name"); + returnTable.Columns.Add("Size"); + returnTable.Columns.Add("Value16"); + returnTable.Columns.Add("Value10"); + returnTable.Columns.Add("ASCII"); + returnTable.Columns.Add("Describe"); + + for (int i = 0; i != SectionTable.Section.Count; i++) + { + if (SectionTable.Section[i] is not SectionTable.SectionData SectionDate) + continue; + + AddTableRow(returnTable, SectionDate.SectName, "SectName", "名字"); + AddTableRow(returnTable, SectionDate.VirtualAddress, "VirtualAddress", "虚拟内存地址"); + AddTableRow(returnTable, SectionDate.SizeOfRawDataRVA, "SizeOfRawDataRVA", "RVA偏移"); + AddTableRow(returnTable, SectionDate.SizeOfRawDataSize, "SizeOfRawDataSize", "RVA大小"); + AddTableRow(returnTable, SectionDate.PointerToRawData, "PointerToRawData", "指向RAW数据"); + AddTableRow(returnTable, SectionDate.PointerToRelocations, "PointerToRelocations", "指向定位号"); + AddTableRow(returnTable, SectionDate.PointerToLinenumbers, "PointerToLinenumbers", "指向行数"); + AddTableRow(returnTable, SectionDate.NumberOfRelocations, "NumberOfRelocations", "定位号"); + AddTableRow(returnTable, SectionDate.NumberOfLinenumbers, "NumberOfLinenumbers", "行数号"); + AddTableRow(returnTable, SectionDate.Characteristics, "Characteristics", "区段标记"); + } + return returnTable; + } + + private DataTable? TableExportDirectory() + { + if (ExportDirectory is null) + return null; + + var returnTable = new DataTable( + "ExportDirectory FileStar{" + + ExportDirectory.FileStarIndex.ToString() + + "}FileEnd{" + + ExportDirectory.FileEndIndex.ToString() + + "}"); + returnTable.Columns.Add("Name"); + returnTable.Columns.Add("Size"); + returnTable.Columns.Add("Value16"); + returnTable.Columns.Add("Value10"); + returnTable.Columns.Add("ASCII"); + returnTable.Columns.Add("Describe"); + + AddTableRow(returnTable, ExportDirectory.Characteristics, "Characteristics", "一个保留字段,目前为止值为0."); + AddTableRow(returnTable, ExportDirectory.TimeDateStamp, "TimeDateStamp", "产生的时间."); + AddTableRow(returnTable, ExportDirectory.MajorVersion, "MajorVersion", "主版本号"); + AddTableRow(returnTable, ExportDirectory.MinorVersion, "MinorVersion", "副版本号"); + AddTableRow(returnTable, ExportDirectory.Name, "Name", "一个RVA,指向一个dll的名称的ascii字符串."); + AddTableRow(returnTable, ExportDirectory.Base, "Base", "输出函数的起始序号.一般为1."); + AddTableRow(returnTable, ExportDirectory.NumberOfFunctions, "NumberOfFunctions", "输出函数入口地址的数组 中的元素个数."); + AddTableRow(returnTable, ExportDirectory.NumberOfNames, "NumberOfNames", "输出函数名的指针的数组 中的元素个数,也是输出函数名对应的序号的数组 中的元素个数."); + AddTableRow(returnTable, ExportDirectory.AddressOfFunctions, "AddressOfFunctions", "一个RVA,指向输出函数入口地址的数组."); + AddTableRow(returnTable, ExportDirectory.AddressOfNames, "AddressOfNames", "一个RVA,指向输出函数名的指针的数组."); + AddTableRow(returnTable, ExportDirectory.AddressOfNameOrdinals, "AddressOfNameOrdinals", "一个RVA,指向输出函数名对应的序号的数组."); + + return returnTable; + } + private DataTable? TableExportFunction() + { + if (ExportDirectory is null) + return null; + + var returnTable = new DataTable("ExportFunctionList"); + returnTable.Columns.Add("Name"); + returnTable.Columns.Add("Size"); + returnTable.Columns.Add("Value16"); + returnTable.Columns.Add("Value10"); + returnTable.Columns.Add("ASCII"); + returnTable.Columns.Add("Describe"); + + for (int i = 0; i != ExportDirectory.FunctionNamesByte.Count; i++) + { + AddTableRow(returnTable, + ExportDirectory.FunctionNamesByte[i], + "Name", + "_ExportDirectory.Name-Sect.SizeOfRawDataRVA+Sect.PointerToRawData"); + } + + for (int i = 0; i != ExportDirectory.AddressOfNamesList.Count; i++) + { + if (ExportDirectory.AddressOfNamesList[i] is not byte[] a) + continue; + AddTableRow(returnTable, a, "NamesList", ""); + } + + for (int i = 0; i != ExportDirectory.AddressOfFunctionsList.Count; i++) + { + if (ExportDirectory.AddressOfFunctionsList[i] is not byte[] a) + continue; + AddTableRow(returnTable, a, "Functions", ""); + } + + for (int i = 0; i != ExportDirectory.AddressOfNameOrdinalsList.Count; i++) + { + if (ExportDirectory.AddressOfNameOrdinalsList[i] is not byte[] a) + continue; + AddTableRow(returnTable, a, "NameOrdinals", ""); + } + return returnTable; + } + private DataTable? TableImportDirectory() + { + if (ImportDirectory is null) + return null; + + var returnTable = new DataTable("ImportDirectory FileStar{" + ImportDirectory.FileStarIndex.ToString() + "}FileEnd{" + ImportDirectory.FileEndIndex.ToString() + "}"); + returnTable.Columns.Add("Name"); + returnTable.Columns.Add("Size"); + returnTable.Columns.Add("Value16"); + returnTable.Columns.Add("Value10"); + returnTable.Columns.Add("ASCII"); + returnTable.Columns.Add("Describe"); + + for (int i = 0; i != ImportDirectory.ImportList.Count; i++) + { + if (ImportDirectory.ImportList[i] is not ImportDirectory.ImportDate ImportByte) + continue; + + AddTableRow(returnTable, ImportByte.DLLName, "输入DLL名称", "**********"); + AddTableRow(returnTable, ImportByte.OriginalFirstThunk, "OriginalFirstThunk", "这里实际上保存着一个RVA,这个RVA指向一个DWORD数组,这个数组可以叫做输入查询表.每个数组元素,或者叫一个表项,保存着一个指向函数名的RVA或者保存着一个函数的序号."); + AddTableRow(returnTable, ImportByte.TimeDateStamp, "TimeDateStamp", "当这个值为0的时候,表明还没有bind.不为0的话,表示已经bind过了.有关bind的内容后面介绍."); + AddTableRow(returnTable, ImportByte.ForwarderChain, "ForwarderChain", ""); + AddTableRow(returnTable, ImportByte.Name, "Name", "一个RVA,这个RVA指向一个ascii以空字符结束的字符串,这个字符串就是本结构对应的dll文件的名字."); + AddTableRow(returnTable, ImportByte.FirstThunk, "FirstThunk", "一个RVA,这个RVA指向一个DWORD数组,这个数组可以叫输入地址表.如果bind了的话,这个数组的每个元素,就是一个输入函数的入口地址."); + } + + return returnTable; + } + private DataTable? TableImportFunction() + { + if (ImportDirectory is null) + return null; + + var returnTable = new DataTable("ImportFunctionList"); + returnTable.Columns.Add("Name"); + returnTable.Columns.Add("Size"); + returnTable.Columns.Add("Value16"); + returnTable.Columns.Add("Value10"); + returnTable.Columns.Add("ASCII"); + returnTable.Columns.Add("Describe"); + + for (int i = 0; i != ImportDirectory.ImportList.Count; i++) + { + if (ImportDirectory.ImportList[i] is not ImportDirectory.ImportDate ImportByte) + continue; + + AddTableRow(returnTable, ImportByte.DLLName, "DLL-Name", "**********"); + + for (int z = 0; z != ImportByte.DLLFunctionList.Count; z++) + { + if (ImportByte.DLLFunctionList[z] is not ImportDirectory.ImportDate.FunctionList Function) + continue; + + AddTableRow(returnTable, Function.FunctionName, "FunctionName", ""); + AddTableRow(returnTable, Function.FunctionHead, "FunctionHead", ""); + AddTableRow(returnTable, Function.OriginalFirst, "OriginalFirstThunk", ""); + } + } + return returnTable; + } + private DataTable? TableResourceDirectory() + { + if (ResourceDirectory is null) + return null; + var returnTable = new DataTable("ResourceDirectory FileStar{" + ResourceDirectory.FileStarIndex.ToString() + "}FileEnd{" + ResourceDirectory.FileEndIndex.ToString() + "}"); + returnTable.Columns.Add("GUID"); + returnTable.Columns.Add("Text"); + returnTable.Columns.Add("ParentID"); + AddResourceDirectoryRow(returnTable, ResourceDirectory, ""); + return returnTable; + } + private void AddResourceDirectoryRow(DataTable myTable, ResourceDirectory Node, string parentID) + { + string Name = ""; + if (Node.Name is not null) + Name = GetString(Node.Name, "UNICODE"); + + for (int i = 0; i != Node.EntryList.Count; i++) + { + if (Node.EntryList[i] is not ResourceDirectory.DirectoryEntry Entry) + continue; + + long ID = GetLong(Entry.Name); + + string GUID = Guid.NewGuid().ToString(); + + string IDNAME = "ID{" + ID + "}"; + if (Name.Length != 0) + IDNAME += "Name{" + Name + "}"; + + if (parentID.Length == 0) + { + IDNAME += ID switch + { + 1 => "Type{Cursor}", + 2 => "Type{Bitmap}", + 3 => "Type{Icon}", + 4 => "Type{Cursor}", + 5 => "Type{Menu}", + 6 => "Type{Dialog}", + 7 => "Type{String Table}", + 8 => "Type{Font Directory}", + 9 => "Type{Font}", + 10 => "Type{Accelerators}", + 11 => "Type{Unformatted}", + 12 => "Type{Message Table}", + 13 => "Type{Group Cursor}", + 14 => "Type{Group Icon}", + 15 => "Type{Information}", + 16 => "Type{Version}", + _ => "Type{未定义}", + }; + } + + myTable.Rows.Add(new string[] { GUID, IDNAME, parentID }); + + for (int z = 0; z != Entry.DataEntryList.Count; z++) + { + if (Entry.DataEntryList[z] is not ResourceDirectory.DirectoryEntry.DataEntry Data) + continue; + + string Text = "Address{" + GetString(Data.ResourRVA) + "} Size{" + GetString(Data.ResourSize) + "} FileBegin{" + Data.FileStarIndex.ToString() + "-" + Data.FileEndIndex.ToString() + "}"; + + myTable.Rows.Add(new string[] { Guid.NewGuid().ToString(), Text, GUID }); + } + + for (int z = 0; z != Entry.NodeDirectoryList.Count; z++) + { + if (Entry.NodeDirectoryList[z] is not ResourceDirectory a) + continue; + AddResourceDirectoryRow(myTable, a, GUID); + } + } + } + #endregion +} + +#region 类 +/// +/// DOS文件都MS开始 +/// +public class DosHeader // IMAGE_DOS_HEADER +{ + public byte[] e_magic = new byte[2]; // 魔术数字 + public byte[] e_cblp = new byte[2]; // 文件最后页的字节数 + public byte[] e_cp = new byte[2]; // 文件页数 + public byte[] e_crlc = new byte[2]; // 重定义元素个数 + public byte[] e_cparhdr = new byte[2]; // 头部尺寸,以段落为单位 + public byte[] e_minalloc = new byte[2]; // 所需的最小附加段 + public byte[] e_maxalloc = new byte[2]; // 所需的最大附加段 + public byte[] e_ss = new byte[2]; // 初始的SS值(相对偏移量) + public byte[] e_sp = new byte[2]; // 初始的SP值 + public byte[] e_csum = new byte[2]; // 校验和 + public byte[] e_ip = new byte[2]; // 初始的IP值 + public byte[] e_cs = new byte[2]; // 初始的CS值(相对偏移量) + public byte[] e_rva = new byte[2]; + public byte[] e_fg = new byte[2]; + public byte[] e_bl1 = new byte[8]; + public byte[] e_oemid = new byte[2]; + public byte[] e_oeminfo = new byte[2]; + public byte[] e_bl2 = new byte[20]; + public byte[] e_PESTAR = new byte[2]; // PE开始 +自己的位置........重点 + + public long FileStarIndex = 0; + public long FileEndIndex = 0; +} + +/// +/// DOS程序 提示 +/// +public class DosStub +{ + public byte[] DosStubData; + public DosStub(long Size) + { + DosStubData = new byte[Size]; + } + public long FileStarIndex = 0; + public long FileEndIndex = 0; +} + +/// +/// PE文件头 +/// +public class PEHeader // IMAGE_FILE_HEADER +{ + public byte[] Header = new byte[4];// PE文件标记 + public byte[] Machine = new byte[2];// 该文件运行所要求的CPU.对于Intel平台,该值是IMAGE_FILE_MACHINE_I386 (14Ch).我们尝试了LUEVELSMEYER的pe.txt声明的14Dh和14Eh,但Windows不能正确执行.看起来,除了禁止程序执行之外,本域对我们来说用处不大. + public byte[] NumberOfSections = new byte[2];// 文件的节数目.如果我们要在文件中增加或删除一个节,就需要修改这个值. + public byte[] TimeDateStamp = new byte[4];// 文件创建日期和时间.我们不感兴趣. + public byte[] PointerToSymbolTable = new byte[4];// 用于调试. + public byte[] NumberOfSymbols = new byte[4];// 用于调试. + public byte[] SizeOfOptionalHeader = new byte[2];// 指示紧随本结构之后的 OptionalHeader 结构大小,必须为有效值. IMAGE_OPTIONAL_HEADER32 结构大小 + public byte[] Characteristics = new byte[2];// 关于文件信息的标记,比如文件是exe还是dll. + + public long FileStarIndex = 0; + public long FileEndIndex = 0; +} + +/// +/// PE头扩展 +/// +// https://docs.microsoft.com/en-us/windows/win32/api/winnt/ns-winnt-image_optional_header32 // IMAGE_OPTIONAL_HEADER32 +// https://docs.microsoft.com/en-us/windows/win32/api/winnt/ns-winnt-image_optional_header64 // IMAGE_OPTIONAL_HEADER64 +public class OptionalHeader +{ + public byte[] Magic = new byte[2]; // Magic 010B=普通可以执行,0107=ROM映像 + public byte[] MajorLinkerVersion = new byte[1]; // 主版本号 + public byte[] MinorLinkerVersion = new byte[1]; // 副版本号 + public byte[] SizeOfCode = new byte[4]; // 代码段大小 + public byte[] SizeOfInitializedData = new byte[4]; // 已初始化数据大小 + public byte[] SizeOfUninitializedData = new byte[4]; // 未初始化数据大小 + public byte[] AddressOfEntryPoint = new byte[4]; // 执行将从这里开始(RVA).........入口点指向,dll填充0 + public byte[] BaseOfCode = new byte[4]; // 代码基址(RVA) + + public byte[] BaseOfData = new byte[4]; // 映象文件基址 + + public byte[] ImageBase = new byte[4]; // 数据基址(RVA) + + public byte[] SectionAlignment = new byte[4]; // 区段列队..... + public byte[] FileAlignment = new byte[4]; // 文件列队 + + public byte[] MajorOperatingSystemVersion = new byte[2]; // 操作系统主版本号 + public byte[] MinorOperatingSystemVersion = new byte[2]; // 操作系统副版本号 + public byte[] MajorImageVersion = new byte[2]; // 映象文件主版本号 + public byte[] MinorImageVersion = new byte[2]; // 映象文件副版本号 + public byte[] MajorSubsystemVersion = new byte[2]; // 子操作系统主版本号 + public byte[] MinorSubsystemVersion = new byte[2]; // 子操作系统副版本号 + public byte[] Win32VersionValue = new byte[4]; // Win32版本值 + public byte[] SizeOfImage = new byte[4]; // 映象文件大小 + public byte[] SizeOfHeards = new byte[4]; // 标志头大小 + public byte[] CheckSum = new byte[4]; // 文件效验 + public byte[] Subsystem = new byte[2]; // 子系统(映象文件)1本地 2WINDOWS-GUI 3WINDOWS-CUI 4 POSIX-CUI + public byte[] DLLCharacteristics = new byte[2]; // DLL标记 + + public byte[] SizeOfStackReserve = new byte[4]; // 保留栈的大小 + public byte[] SizeOfStackCommit = new byte[4]; // 初始时指定栈大小 + public byte[] SizeOfHeapReserve = new byte[4]; // 保留堆的大小 + public byte[] SizeOfHeapCommit = new byte[4]; // 初始时指定堆大小 + public byte[] LoaderFlags = new byte[4]; // 加载器标志 + public byte[] NumberOfRvaAndSizes = new byte[4]; // 数据目录数 + + public long FileStarIndex = 0; + public long FileEndIndex = 0; + + public OptionalHeader(bool is32) + { + if (!is32) + { + // X64没有了,但是为了代码保留修改幅度不大,所以置0 + BaseOfData = new byte[0];// x64必须置于0 + // x64长度增加的 + int ulonglong = 8; + ImageBase = new byte[ulonglong]; // 数据基址(RVA) + SizeOfStackReserve = new byte[ulonglong]; // 保留栈的大小 + SizeOfStackCommit = new byte[ulonglong]; // 初始时指定栈大小 + SizeOfHeapReserve = new byte[ulonglong]; // 保留堆的大小 + SizeOfHeapCommit = new byte[ulonglong]; // 初始时指定堆大小 + } + } +} + +/// +/// 目录结构 +/// +public class OptionalDirAttrib +{ + public ArrayList DirByte = new(); + public class DirAttrib + { + public byte[] DirRva = new byte[4]; // 地址 + public byte[] DirSize = new byte[4]; // 大小 + } + public long FileStarIndex = 0; + public long FileEndIndex = 0; +} + +/// +/// 节表 +/// +public class SectionTable +{ + public ArrayList Section = new(); + public class SectionData + { + public byte[] SectName = new byte[8]; // 名字 + public byte[] VirtualAddress = new byte[4]; // 虚拟内存地址 + public byte[] SizeOfRawDataRVA = new byte[4]; // RVA偏移 + public byte[] SizeOfRawDataSize = new byte[4]; // RVA大小 + public byte[] PointerToRawData = new byte[4]; // 指向RAW数据 + public byte[] PointerToRelocations = new byte[4]; // 指向定位号 + public byte[] PointerToLinenumbers = new byte[4]; // 指向行数 + public byte[] NumberOfRelocations = new byte[2]; // 定位号 + public byte[] NumberOfLinenumbers = new byte[2]; // 行数号 + public byte[] Characteristics = new byte[4]; // 区段标记 + } + + public long FileStarIndex = 0; + public long FileEndIndex = 0; +} + +/// +/// 输出表 +/// +public class ExportDirectory +{ + public byte[] Characteristics = new byte[4]; // 一个保留字段,目前为止值为0. + public byte[] TimeDateStamp = new byte[4]; // 产生的时间 + public byte[] MajorVersion = new byte[2]; // 主版本号 + public byte[] MinorVersion = new byte[2]; // 副版本号 + public byte[] Name = new byte[4]; // 一个RVA,指向一个dll的名称的ascii字符串 + public byte[] Base = new byte[4]; // 输出函数的起始序号.一般为1 + public byte[] NumberOfFunctions = new byte[4]; // 输出函数入口地址的数组中的元素个数 + public byte[] NumberOfNames = new byte[4]; // 输出函数名的指针的数组中的元素个数,也是输出函数名对应的序号的数组中的元素个数 + public byte[] AddressOfFunctions = new byte[4]; // 一个RVA,指向输出函数入口地址的数组 + public byte[] AddressOfNames = new byte[4]; // 一个RVA,指向输出函数名的指针的数组 + public byte[] AddressOfNameOrdinals = new byte[4]; // 一个RVA,指向输出函数名对应的序号的数组 + + public ArrayList AddressOfFunctionsList = new(); + public ArrayList AddressOfNamesList = new(); + public ArrayList AddressOfNameOrdinalsList = new(); + /// + /// 函数指针名称集合 + /// + public List FunctionNamesByte = new(); + public long FileStarIndex = 0; + public long FileEndIndex = 0; + + /// + /// 获取函数名 + /// + public HashSet FunctionNames() + { + HashSet names = new(); + for (int i = 0; i < FunctionNamesByte.Count; i++) + names.Add(Encoding.Default.GetString(FunctionNamesByte[i])); + return names; + } +} + + +/// +/// 输入表 +/// +public class ImportDirectory +{ + public ArrayList ImportList = new(); + + public class ImportDate + { + public byte[] OriginalFirstThunk = new byte[4]; // 这里实际上保存着一个RVA,这个RVA指向一个DWORD数组,这个数组可以叫做输入查询表.每个数组元素,或者叫一个表项,保存着一个指向函数名的RVA或者保存着一个函数的序号. + public byte[] TimeDateStamp = new byte[4]; // 当这个值为0的时候,表明还没有bind.不为0的话,表示已经bind过了.有关bind的内容后面介绍. + public byte[] ForwarderChain = new byte[4]; + public byte[] Name = new byte[4]; // 一个RVA,这个RVA指向一个ascii以空字符结束的字符串,这个字符串就是本结构对应的dll文件的名字. + public byte[] FirstThunk = new byte[4]; // 一个RVA,这个RVA指向一个DWORD数组,这个数组可以叫输入地址表.如果bind了的话,这个数组的每个元素,就是一个输入函数的入口地址. + + public byte[]? DLLName; // DLL名称 + public ArrayList DLLFunctionList = new(); + public class FunctionList + { + public byte[] OriginalFirst = new byte[4]; + public byte[]? FunctionName; + public byte[] FunctionHead = new byte[2]; + } + } + public long FileStarIndex = 0; + public long FileEndIndex = 0; +} + +/// +/// 资源表 +/// +public class ResourceDirectory +{ + public byte[] Characteristics = new byte[4]; + public byte[] TimeDateStamp = new byte[4]; + public byte[] MajorVersion = new byte[2]; + public byte[] MinorVersion = new byte[2]; + public byte[] NumberOfNamedEntries = new byte[2]; + public byte[] NumberOfIdEntries = new byte[2]; + public byte[]? Name; + public ArrayList EntryList = new(); + + public class DirectoryEntry + { + public byte[] Name = new byte[4]; + public byte[] Id = new byte[4]; + public ArrayList DataEntryList = new(); + public ArrayList NodeDirectoryList = new(); + + public class DataEntry + { + public byte[] ResourRVA = new byte[4]; + public byte[] ResourSize = new byte[4]; + public byte[] ResourTest = new byte[4]; + public byte[] ResourWen = new byte[4]; + + public long FileStarIndex = 0; + public long FileEndIndex = 0; + } + } + + public long FileStarIndex = 0; + public long FileEndIndex = 0; +} +#endregion \ No newline at end of file diff --git a/src/CAD/IFox.CAD.Shared/Runtime/RedrawEx.cs b/src/CAD/IFox.CAD.Shared/Runtime/RedrawEx.cs new file mode 100644 index 0000000..3799eed --- /dev/null +++ b/src/CAD/IFox.CAD.Shared/Runtime/RedrawEx.cs @@ -0,0 +1,191 @@ +namespace IFoxCAD.Cad; + +[Flags] +public enum BrightEntity : int +{ + /// + /// 块更新 + /// + RecordGraphicsModified = 1, + /// + /// 标注更新 + /// + RecomputeDimensionBlock = 2, + /// + /// 重画 + /// + Draw = 4, + /// + /// 亮显 + /// + Highlight = 8, + /// + /// 亮显取消 + /// + Unhighlight = 16, + /// + /// 显示图元 + /// + VisibleTrue = 32, + /// + /// 隐藏图元 + /// + VisibleFalse = 64, + /// + /// 平移更新,可以令ctrl+z撤回时候保证刷新 + /// + MoveZero = 128, +} + +[Flags] +public enum BrightEditor : int +{ + /// + /// 刷新屏幕,图元不生成(例如块还是旧的显示) + /// + UpdateScreen = 1, + /// + /// 刷新全图 + /// + Regen = 2, + /// + /// 清空选择集 + /// + SelectionClean = 4, + /// + /// 视口外 + /// + ViewportsFrom = 8, + /// + /// 视口内 + /// + ViewportsIn = 16, +} + +public static class RedrawEx +{ + /// + /// 刷新屏幕 + /// + /// 编辑器 + /// 图元,调用时候图元必须提权 + public static void Redraw(this Editor ed, Entity? ent = null) + { + // 刷新图元 + ent?.Redraw(BrightEntity.Draw | + BrightEntity.RecordGraphicsModified | + BrightEntity.RecomputeDimensionBlock | + BrightEntity.MoveZero); + // 刷新 + ed.Redraw(BrightEditor.UpdateScreen); + + /* + * 我发现命令加 CommandFlags.Redraw 就不需要以下处理了: + * 数据库事务和文档事务不一样,文档事务有刷新函数. + * var doc = Acap.DocumentManager.MdiActiveDocument; + * var ed = doc.Editor; + * var tm = doc.TransactionManager; + * tm.QueueForGraphicsFlush();// 如果在最外层事务结束之前需要更新图形,此句把目前为止所做的更改放入 刷新队列 + * tm.FlushGraphics(); // 将当前 刷新队列 的图形提交到显示器 + * ed.UpdateScreen(); // 仅刷新屏幕,图元不生成(例如块还是旧的显示) + */ + var dm = Acap.DocumentManager; + var doc = dm.MdiActiveDocument; + var tm = doc.TransactionManager; + tm.QueueForGraphicsFlush(); + tm.FlushGraphics(); + + // acad2014及以上要加,立即处理队列上面的消息 + System.Windows.Forms.Application.DoEvents(); + } + + /// + /// 刷新屏幕 + /// + /// 编辑器 + /// 更新的方式 + public static void Redraw(this Editor ed, BrightEditor bright) + { + if ((bright & BrightEditor.UpdateScreen) == BrightEditor.UpdateScreen) + { + // 两个函数底层差不多 + // Acap.UpdateScreen(); + ed.UpdateScreen(); + } + + if ((bright & BrightEditor.Regen) == BrightEditor.Regen) + ed.Regen(); + + if ((bright & BrightEditor.SelectionClean) == BrightEditor.SelectionClean) + ed.SetImpliedSelection(new ObjectId[0]); + + if ((bright & BrightEditor.ViewportsFrom) == BrightEditor.ViewportsFrom) + ed.UpdateTiledViewportsFromDatabase(); // 更新视口外 + + if ((bright & BrightEditor.ViewportsIn) == BrightEditor.ViewportsIn) + ed.UpdateTiledViewportsInDatabase(); // 更新视口内 + } + + /// + /// 更改图元显示 + /// + /// 图元,调用时候图元必须提权 + /// 更新的方式 + public static void Redraw(this Entity ent, BrightEntity bright) + { + // 调用时候图元必须提权,参数true表示关闭图元后进行UpData,实现局部刷新块. + if ((bright & BrightEntity.RecordGraphicsModified) == BrightEntity.RecordGraphicsModified) + ent.RecordGraphicsModified(true); + + if ((bright & BrightEntity.RecomputeDimensionBlock) == BrightEntity.RecomputeDimensionBlock) + if (ent is Dimension dim) + dim.RecomputeDimensionBlock(true); + + if ((bright & BrightEntity.Draw) == BrightEntity.Draw) + ent.Draw(); + + if ((bright & BrightEntity.Highlight) == BrightEntity.Highlight) + ent.Highlight(); + + if ((bright & BrightEntity.Unhighlight) == BrightEntity.Unhighlight) + ent.Unhighlight(); + + if ((bright & BrightEntity.VisibleTrue) == BrightEntity.VisibleTrue) + ent.Visible = true; + + if ((bright & BrightEntity.VisibleFalse) == BrightEntity.VisibleFalse) + ent.Visible = false; + + // 戴耀辉: + // 删除块内图元的时候需要刷新块, + // 用 RecordGraphicsModified 显示是没有问题, + // 但是 ctrl+z 撤销会有显示问题, + // 所以平移0可以在撤回数据库的时候刷新指定图元 + if ((bright & BrightEntity.MoveZero) == BrightEntity.MoveZero) + ent.Move(Point3d.Origin, Point3d.Origin); + } + + + #region 实体刷新 + /// + /// 刷新实体显示 + /// + /// 实体对象 + [Obsolete("此处已经被RedrawEx代替")] + public static void Flush(this Entity entity, DBTrans? trans = null) + { + trans ??= DBTrans.Top; + entity.RecordGraphicsModified(true); + trans.Transaction.TransactionManager.QueueForGraphicsFlush(); + trans.Document?.TransactionManager.FlushGraphics(); + } + + /// + /// 刷新实体显示 + /// + /// 实体id + [Obsolete("此处已经被RedrawEx代替")] + public static void Flush(this ObjectId id) + => Flush(DBTrans.Top.GetObject(id)!); + #endregion +} \ No newline at end of file diff --git a/src/CAD/IFox.CAD.Shared/Runtime/SymbolTable.cs b/src/CAD/IFox.CAD.Shared/Runtime/SymbolTable.cs new file mode 100644 index 0000000..1a0c0ef --- /dev/null +++ b/src/CAD/IFox.CAD.Shared/Runtime/SymbolTable.cs @@ -0,0 +1,408 @@ +using static System.Windows.Forms.AxHost; + +namespace IFoxCAD.Cad; + +public class SymbolTable : IEnumerable + where TTable : SymbolTable + where TRecord : SymbolTableRecord, new() +{ + #region 程序集内部属性 + /// + /// 事务管理器 + /// + internal DBTrans DTrans { get; private set; } + /// + /// 数据库 + /// + internal Database Database { get; private set; } + + #endregion + + #region 公开属性 + /// + /// 当前符号表 + /// + public TTable CurrentSymbolTable { get; private set; } + #endregion + + #region 构造函数 + /// + /// 构造函数,初始化Trans和CurrentSymbolTable属性 + /// + /// 事务管理器 + /// 符号表id + /// 默认行为:例如打开隐藏图层 + internal SymbolTable(DBTrans tr, ObjectId tableId, bool defaultBehavior = true) + { + DTrans = tr; + Database = tr.Database; + CurrentSymbolTable = DTrans.GetObject(tableId)!; + + if (!defaultBehavior) + return; + + if (CurrentSymbolTable is LayerTable layer) + { + // 层表包含隐藏的,全部显示出来 + layer = layer.IncludingHidden; + if (layer is TTable tt) + CurrentSymbolTable = tt; + } + } + + #endregion + + #region 索引器 + /// + /// 索引器 + /// + /// 对象名称 + /// 对象的id + public ObjectId this[string key] + { + get + { + if (Has(key)) + return CurrentSymbolTable[key]; + return ObjectId.Null; + } + } + #endregion + + #region Has + /// + /// 判断是否存在符号表记录 + /// + /// 记录名 + /// 存在返回 , 不存在返回 + public bool Has(string key) + { + return CurrentSymbolTable.Has(key); + } + /// + /// 判断是否存在符号表记录 + /// + /// 记录id + /// 存在返回 , 不存在返回 + public bool Has(ObjectId objectId) + { + return CurrentSymbolTable.Has(objectId); + } + #endregion + + #region 添加符号表记录 + /// + /// 添加符号表记录 + /// + /// 符号表记录 + /// 对象id + private ObjectId Add(TRecord record) + { + ObjectId id; + using (CurrentSymbolTable.ForWrite()) + { + id = CurrentSymbolTable.Add(record); + DTrans.Transaction.AddNewlyCreatedDBObject(record, true); + } + return id; + } + /// + /// 添加符号表记录 + /// + /// 符号表记录名 + /// 符号表记录处理函数的无返回值委托 + /// 对象id + public ObjectId Add(string name, Action? action = null) + { + ObjectId id = this[name]; + if (id.IsNull) + { + var record = new TRecord() + { + Name = name + }; + id = Add(record); + using (record.ForWrite()) + action?.Invoke(record); + } + return id; + } + #endregion + + #region 删除符号表记录 + /// + /// 删除符号表记录 + /// + /// 符号表记录对象 + private static void Remove(TRecord record) + { + using (record.ForWrite()) + record.Erase(); + } + + /// + /// 删除符号表记录 + /// + /// 符号表记录名 + public void Remove(string name) + { + var record = GetRecord(name); + if (record is not null) + Remove(record); + } + + /// + /// 删除符号表记录 + /// + /// 符号表记录对象id + public void Remove(ObjectId id) + { + var record = GetRecord(id); + if (record is not null) + Remove(record); + } + #endregion + + #region 修改符号表记录 + /// + /// 修改符号表 + /// + /// 符号表记录名 + /// 修改委托 + [System.Diagnostics.DebuggerStepThrough] + public void Change(string name, Action action) + { + var record = GetRecord(name); + if (record is not null) + { + using (record.ForWrite()) + action.Invoke(record); + } + } + + /// + /// 修改符号表 + /// + /// 符号表记录id + /// 修改委托 + [System.Diagnostics.DebuggerStepThrough] + public void Change(ObjectId id, Action action) + { + var record = GetRecord(id); + if (record is not null) + { + using (record.ForWrite()) + action.Invoke(record); + } + } + #endregion + + #region 获取符号表记录 + /// + /// 获取符号表记录 + /// + /// 符号表记录的id + /// 打开模式 + /// 是否打开已删除对象,默认为不打开 + /// 是否打开锁定图层对象,默认为不打开 + /// 符号表记录 + [System.Diagnostics.DebuggerStepThrough] + public TRecord? GetRecord(ObjectId id, + OpenMode openMode = OpenMode.ForRead, + bool openErased = false, + bool openLockedLayer = false) + { + return DTrans.GetObject(id, openMode, openErased, openLockedLayer); + } + + /// + /// 获取符号表记录 + /// + /// 符号表记录名 + /// 打开模式 + /// 是否打开已删除对象,默认为不打开 + /// 是否打开锁定图层对象,默认为不打开 + /// 符号表记录 + [System.Diagnostics.DebuggerStepThrough] + public TRecord? GetRecord(string name, + OpenMode openMode = OpenMode.ForRead, + bool openErased = false, + bool openLockedLayer = false) + { + return GetRecord(this[name], openMode, openErased, openLockedLayer); + } + + /// + /// 获取符号表记录 + /// + /// 符号表记录集合 + public IEnumerable GetRecords() + { + foreach (var item in this) + { + var record = GetRecord(item); + if (record is not null) + yield return record; + } + } + + /// + /// 获取符号表记录的名字集合 + /// + /// 记录的名字集合 + public IEnumerable GetRecordNames() => GetRecords().Select(record => record.Name); + + /// + /// 获取符合过滤条件的符号表记录名字集合 + /// + /// 过滤器委托 + /// 记录的名字集合 + public IEnumerable GetRecordNames(Func filter) + { + foreach (var item in this) + { + var record = GetRecord(item); + if (record is not null && filter.Invoke(record)) + yield return record.Name; + } + } + + /// + /// 从源数据库拷贝符号表记录 + /// + /// 符号表 + /// 符号表记录名 + /// 是否覆盖, 为覆盖, 为不覆盖 + /// 对象id + public ObjectId GetRecordFrom(SymbolTable table, string name, bool over) + { + if (table is null) + throw new ArgumentNullException(nameof(table), "对象为null"); + + ObjectId rid = this[name]; + bool has = rid != ObjectId.Null; + if ((has && over) || !has) + { + ObjectId id = table[name]; + using IdMapping map = new(); + using ObjectIdCollection ids = new() { id }; + table.Database.WblockCloneObjects( + ids, + CurrentSymbolTable.Id, + map, + DuplicateRecordCloning.Replace, + false); + rid = map[id].Value; + } + return rid; + } + + /// + /// 从文件拷贝符号表记录 + /// + /// 符号表过滤器 + /// 文件名 + /// 符号表记录名 + /// 是否覆盖, 为覆盖, 为不覆盖 + /// 对象id + internal ObjectId GetRecordFrom(Func> tableSelector, + string fileName, + string name, + bool over) + { + using DBTrans tr = new(fileName); + return GetRecordFrom(tableSelector(tr), name, over); + } + #endregion + + #region 遍历 +#line hidden // 调试的时候跳过它 + /// + /// 遍历符号表,执行委托 + /// + /// 要运行的委托 + /// 打开模式,默认为只读 + /// 检查id是否删除,默认true + /// 是否打开已删除对象,默认为不打开 + /// 是否打开锁定图层对象,默认为不打开 + public void ForEach(Action task, + OpenMode openMode = OpenMode.ForRead, + bool checkIdOk = true, + bool openErased = false, + bool openLockedLayer = false) + { + ForEach((a, _, _) => { + task.Invoke(a);//由于此处是委托,所以 DebuggerStepThrough 特性会进入,改用预处理方式避免 + }, openMode, checkIdOk, openErased, openLockedLayer); + } + + /// + /// 遍历符号表,执行委托(允许循环中断) + /// + /// 要执行的委托 + /// 打开模式,默认为只读 + /// 检查id是否删除,默认true + /// 是否打开已删除对象,默认为不打开 + /// 是否打开锁定图层对象,默认为不打开 + public void ForEach(Action task, + OpenMode openMode = OpenMode.ForRead, + bool checkIdOk = true, + bool openErased = false, + bool openLockedLayer = false) + { + ForEach((a, b, _) => { + task.Invoke(a, b); + }, openMode, checkIdOk, openErased, openLockedLayer); + } + + /// + /// 遍历符号表,执行委托(允许循环中断,输出索引值) + /// + /// 要执行的委托 + /// 打开模式,默认为只读 + /// 检查id是否删除,默认true + /// 是否打开已删除对象,默认为不打开 + /// 是否打开锁定图层对象,默认为不打开 + [System.Diagnostics.DebuggerStepThrough] + public void ForEach(Action task, + OpenMode openMode = OpenMode.ForRead, + bool checkIdOk = true, + bool openErased = false, + bool openLockedLayer = false) + { + if (task == null) + throw new ArgumentNullException(nameof(task)); + + LoopState state = new();/*这种方式比Action改Func更友好*/ + int i = 0; + foreach (var id in this) + { + if (checkIdOk && !id.IsOk()) + continue; + var record = GetRecord(id, openMode, openErased, openLockedLayer); + if (record is not null) + task.Invoke(record, state, i); + if (!state.IsRun) + break; + i++; + } + } +#line default + + #endregion + + #region IEnumerable 成员 + [System.Diagnostics.DebuggerStepThrough] + public IEnumerator GetEnumerator() + { + foreach (var id in CurrentSymbolTable) + yield return id; + } + + [System.Diagnostics.DebuggerStepThrough] + IEnumerator IEnumerable.GetEnumerator() + { + return GetEnumerator(); + } + #endregion +} \ No newline at end of file diff --git a/src/CAD/IFox.CAD.Shared/Runtime/Utils.cs b/src/CAD/IFox.CAD.Shared/Runtime/Utils.cs new file mode 100644 index 0000000..28c47c2 --- /dev/null +++ b/src/CAD/IFox.CAD.Shared/Runtime/Utils.cs @@ -0,0 +1,98 @@ +namespace IFoxCAD.Cad; + +using System; + +public class DBTransHelper +{ + /* + * id = db.GetObjectId(false, handle, 0); + * 参数意义: db.GetObjectId(如果没有找到就创建,句柄号,标记..将来备用) + * 在vs的输出会一直抛出: + * 引发的异常:“Autodesk.AutoCAD.Runtime.Exception”(位于 AcdbMgd.dll 中) + * "eUnknownHandle" + * 这就是为什么慢的原因,所以直接运行就好了!而Debug还是需要用arx的API替代. + */ + + [System.Security.SuppressUnmanagedCodeSecurity] + [DllImport("acdb17.dll", CallingConvention = CallingConvention.ThisCall/*08的调用约定 高版本是__cdecl*/, + EntryPoint = "?getAcDbObjectId@AcDbDatabase@@QAE?AW4ErrorStatus@Acad@@AAVAcDbObjectId@@_NABVAcDbHandle@@K@Z")] + extern static int getAcDbObjectId17x32(IntPtr db, out ObjectId id, [MarshalAs(UnmanagedType.U1)] bool createnew, ref Handle h, uint reserved); + + [System.Security.SuppressUnmanagedCodeSecurity] + [DllImport("acdb17.dll", CallingConvention = CallingConvention.ThisCall/*08的调用约定 高版本是__cdecl*/, + EntryPoint = "?getAcDbObjectId@AcDbDatabase@@QEAA?AW4ErrorStatus@Acad@@AEAVAcDbObjectId@@_NAEBVAcDbHandle@@K@Z")] + extern static int getAcDbObjectId17x64(IntPtr db, out ObjectId id, [MarshalAs(UnmanagedType.U1)] bool createnew, ref Handle h, uint reserved); + + [System.Security.SuppressUnmanagedCodeSecurity] + [DllImport("acdb18.dll", CallingConvention = CallingConvention.ThisCall/*08的调用约定 高版本是__cdecl*/, + EntryPoint = "?getAcDbObjectId@AcDbDatabase@@QAE?AW4ErrorStatus@Acad@@AAVAcDbObjectId@@_NABVAcDbHandle@@K@Z")] + extern static int getAcDbObjectId18x32(IntPtr db, out ObjectId id, [MarshalAs(UnmanagedType.U1)] bool createnew, ref Handle h, uint reserved); + + [System.Security.SuppressUnmanagedCodeSecurity] + [DllImport("acdb18.dll", CallingConvention = CallingConvention.ThisCall/*08的调用约定 高版本是__cdecl*/, + EntryPoint = "?getAcDbObjectId@AcDbDatabase@@QEAA?AW4ErrorStatus@Acad@@AEAVAcDbObjectId@@_NAEBVAcDbHandle@@K@Z")] + extern static int getAcDbObjectId18x64(IntPtr db, out ObjectId id, [MarshalAs(UnmanagedType.U1)] bool createnew, ref Handle h, uint reserved); + + /// + /// 句柄转id,NET35(08~12)专用的 + /// + /// 数据库 + /// 句柄 + /// 返回的id + /// 不存在则创建 + /// 保留,用于未来 + /// 成功0,其他值都是错误.可以强转ErrorStatus + static int GetAcDbObjectId(IntPtr db, Handle handle, out ObjectId id, bool createIfNotFound = false, uint reserved = 0) + { + id = ObjectId.Null; + switch (Acap.Version.Major) + { + case 17: + { + if (IntPtr.Size == 4) + return getAcDbObjectId17x32(db, out id, createIfNotFound, ref handle, reserved); + else + return getAcDbObjectId17x64(db, out id, createIfNotFound, ref handle, reserved); + } + case 18: + { + if (IntPtr.Size == 4) + return getAcDbObjectId18x32(db, out id, createIfNotFound, ref handle, reserved); + else + return getAcDbObjectId18x64(db, out id, createIfNotFound, ref handle, reserved); + } + } + return -1; + } + + /// + /// 句柄转id + /// + /// 数据库 + /// 句柄 + /// id + public static ObjectId TryGetObjectId(Database db, Handle handle) + { +#if !NET35 + // 高版本直接利用 + var es = db.TryGetObjectId(handle, out ObjectId id); + // if (!es) +#else + var es = GetAcDbObjectId(db.UnmanagedObject, handle, out ObjectId id); + // if (ErrorStatus.OK != (ErrorStatus)es) +#endif + return id; + } + + // public static int GetCadFileVersion(string filename) + // { + // var bytes = File.ReadAllBytes(filename); + // var headstr = Encoding.Default.GetString(bytes)[0..6]; + // if (!headstr.StartsWith("AC")) return 0; + // var vernum = int.Parse(headstr.Replace("AC", "")); + // var a = Enum.Parse(typeof(DwgVersion), "AC1800"); + // Enum.TryParse() + // return vernum + 986; + + // } +} \ No newline at end of file diff --git a/src/CAD/IFox.CAD.Shared/SelectionFilter/OpComp.cs b/src/CAD/IFox.CAD.Shared/SelectionFilter/OpComp.cs new file mode 100644 index 0000000..040090a --- /dev/null +++ b/src/CAD/IFox.CAD.Shared/SelectionFilter/OpComp.cs @@ -0,0 +1,79 @@ +namespace IFoxCAD.Cad; + +/// +/// 比较运算符类 +/// +public class OpComp : OpEqual +{ + /// + /// 比较运算符,如: + /// "<=" + /// 以及合并比较运算符: + /// "<=,<=,=" + /// + public string Content { get; } + + /// + /// 符号名 + /// + public override string Name + { + get { return "Comp"; } + } + + /// + /// 比较运算符类构造函数 + /// + /// 运算符 + /// 数据 + public OpComp(string content, TypedValue value) + : base(value) + { + Content = content; + } + + /// + /// 比较运算符类构造函数 + /// + /// 运算符 + /// 组码 + public OpComp(string content, int code) + : base(code) + { + Content = content; + } + + /// + /// 比较运算符类构造函数 + /// + /// 运算符 + /// 组码 + /// 组码值 + public OpComp(string content, int code, object value) + : base(code, value) + { + Content = content; + } + + /// + /// 比较运算符类构造函数 + /// + /// 运算符 + /// 组码 + /// 组码值 + public OpComp(string content, DxfCode code, object value) + : base(code, value) + { + Content = content; + } + + /// + /// 获取过滤器数据迭代器 + /// + /// TypedValue迭代器 + public override IEnumerable GetValues() + { + yield return new TypedValue(-4, Content); + yield return Value; + } +} \ No newline at end of file diff --git a/src/CAD/IFox.CAD.Shared/SelectionFilter/OpEqual.cs b/src/CAD/IFox.CAD.Shared/SelectionFilter/OpEqual.cs new file mode 100644 index 0000000..370a0c1 --- /dev/null +++ b/src/CAD/IFox.CAD.Shared/SelectionFilter/OpEqual.cs @@ -0,0 +1,86 @@ +namespace IFoxCAD.Cad; + +/// +/// 相等运算符类 +/// +public class OpEqual : OpFilter +{ + /// + /// 组码与匹配值的TypedValue类型值 + /// + public TypedValue Value { get; private set; } + + /// + /// 符号名 + /// + public override string Name + { + get { return "Equal"; } + } + + /// + /// 相等运算符类构造函数 + /// + /// 组码 + public OpEqual(int code) + { + Value = new TypedValue(code); + } + + /// + /// 相等运算符类构造函数 + /// + /// 组码 + /// 组码值 + public OpEqual(int code, object value) + { + Value = new TypedValue(code, value); + } + + /// + /// 相等运算符类构造函数 + /// + /// 组码 + /// 组码值 + public OpEqual(DxfCode code, object value) + { + Value = new TypedValue((int)code, value); + } + + /// + /// 相等运算符类构造函数 + /// + /// 组码与组码值的TypedValue类型值 + internal OpEqual(TypedValue value) + { + Value = value; + } + + /// + /// 过滤器数据迭代器 + /// + /// TypedValue迭代器 + public override IEnumerable GetValues() + { + yield return Value; + } + + /// + /// 设置数据 + /// + /// 组码值 + public void SetValue(object value) + { + Value = new TypedValue(Value.TypeCode, value); + } + + /// + /// 设置数据 + /// + /// 组码 + /// 组码值 + public void SetValue(int code, object value) + { + Value = new TypedValue(code, value); + } +} diff --git a/src/CAD/IFox.CAD.Shared/SelectionFilter/OpFilter.cs b/src/CAD/IFox.CAD.Shared/SelectionFilter/OpFilter.cs new file mode 100644 index 0000000..48843f4 --- /dev/null +++ b/src/CAD/IFox.CAD.Shared/SelectionFilter/OpFilter.cs @@ -0,0 +1,341 @@ +namespace IFoxCAD.Cad; + +/// +/// 选择集过滤器抽象类 +/// +public abstract class OpFilter +{ + /// + /// 过滤器的名字 + /// + public abstract string Name { get; } + + /// + /// 只读属性,表示这个过滤器取反 + /// + public OpFilter Not => new OpNot(this); + + /// + /// 获取TypedValue类型的值的迭代器的抽象方法,子类必须重写 + /// + /// TypedValue迭代器 + public abstract IEnumerable GetValues(); + + /// + /// 非操作符,返回的是OpFilter类型变量的 属性 + /// + /// OpFilter类型变量 + /// OpFilter对象 + public static OpFilter operator !(OpFilter item) + { + return item.Not; + } + + /// + /// 过滤器值转换为 TypedValue 类型数组 + /// + /// TypedValue数组 + public TypedValue[] ToArray() + { + return GetValues().ToArray(); + } + + /// + /// 隐式类型转换,将自定义的过滤器转换为 Autocad 认识的选择集过滤器 + /// + /// 过滤器对象 + /// + /// 选择集过滤器. + /// + public static implicit operator SelectionFilter(OpFilter item) + { + return new SelectionFilter(item.ToArray()); + } + + /// + /// 转换为字符串 + /// + /// 字符串 + public override string ToString() + { + var sb = new StringBuilder(); + foreach (var value in GetValues()) + sb.Append(value); + return sb.ToString(); + } + + /// + /// 构建过滤器 + /// + /// + /// 举两个利用构建函数创建选择集过滤的例子 + /// + /// !(e.Dxf(0) == "line" & e.Dxf(8) == "0") + /// | e.Dxf(0) != "circle" & e.Dxf(8) == "2" & e.Dxf(10) >= p); + /// + /// 例子2: + /// var f2 = OpFilter.Build( + /// e => e.Or( + /// !e.And(e.Dxf(0) == "line", e.Dxf(8) == "0"), + /// e.And(e.Dxf(0) != "circle", e.Dxf(8) == "2", + /// e.Dxf(10) <= new Point3d(10, 10, 0)))); + /// ]]> + /// + /// 构建过滤器的函数委托 + /// 过滤器 + public static OpFilter Build(Func func) + { + return func(new Op()).Filter!; + } + + #region Operator + + /// + /// 过滤器操作符类 + /// + public class Op + { + /// + /// 过滤器属性 + /// + internal OpFilter? Filter { get; private set; } + + internal Op() { } + + private Op(OpFilter filter) + { + Filter = filter; + } + + /// + /// AND 操作符 + /// + /// 操作符类型的可变参数 + /// Op对象 +#pragma warning disable CA1822 // 将成员标记为 static + public Op And(params Op[] args) +#pragma warning restore CA1822 // 将成员标记为 static + { + var filter = new OpAnd(); + foreach (var op in args) + filter.Add(op.Filter!); + return new Op(filter); + } + + /// + /// or 操作符 + /// + /// 操作符类型的可变参数 + /// Op对象 +#pragma warning disable CA1822 // 将成员标记为 static + public Op Or(params Op[] args) +#pragma warning restore CA1822 // 将成员标记为 static + { + var filter = new OpOr(); + foreach (var op in args) + filter.Add(op.Filter!); + return new Op(filter); + } + + /// + /// dxf 操作符,此函数只能用于过滤器中,不是组码操作函数 + /// + /// 组码 + /// Op对象 +#pragma warning disable CA1822 // 将成员标记为 static + public Op Dxf(int code) +#pragma warning restore CA1822 // 将成员标记为 static + { + return new Op(new OpEqual(code)); + } + + /// + /// dxf 操作符,此函数只能用于过滤器中,不是组码操作函数 + /// + /// 组码 + /// 关系运算符的值,比如">,>,=" + /// Op对象 +#pragma warning disable CA1822 // 将成员标记为 static + public Op Dxf(int code, string content) +#pragma warning restore CA1822 // 将成员标记为 static + { + return new Op(new OpComp(content, code)); + } + + /// + /// 非操作符 + /// + /// 过滤器操作符对象 + /// Op对象 + public static Op operator !(Op right) + { + right.Filter = !right.Filter!; + return right; + } + + /// + /// 相等操作符 + /// + /// 过滤器操作符对象 + /// 数据 + /// Op对象 + public static Op operator ==(Op left, object right) + { + var eq = (OpEqual)left.Filter!; + eq.SetValue(right); + return left; + } + + /// + /// 不等操作符 + /// + /// 过滤器操作符对象 + /// 数据 + /// Op对象 + public static Op operator !=(Op left, object right) + { + var eq = (OpEqual)left.Filter!; + eq.SetValue(right); + left.Filter = eq.Not; + return left; + } + + private static Op GetCompOp(string content, Op left, object right) + { + var eq = (OpEqual)left.Filter!; + var comp = new OpComp(content, eq.Value.TypeCode, right); + return new Op(comp); + } + + /// + /// 大于操作符 + /// + /// 过滤器操作符对象 + /// 数据 + /// Op对象 + public static Op operator >(Op left, object right) + { + return GetCompOp(">", left, right); + } + + /// + /// 小于操作符 + /// + /// 过滤器操作符对象 + /// 数据 + /// Op对象 + public static Op operator <(Op left, object right) + { + return GetCompOp("<", left, right); + } + + /// + /// 大于等于操作符 + /// + /// 过滤器操作符对象 + /// 数据 + /// Op对象 + public static Op operator >=(Op left, object right) + { + return GetCompOp(">=", left, right); + } + + /// + /// 小于等于操作符 + /// + /// 过滤器操作符对象 + /// 数据 + /// Op对象 + public static Op operator <=(Op left, object right) + { + return GetCompOp("<=", left, right); + } + + /// + /// 大于等于操作符 + /// + /// 过滤器操作符对象 + /// 点 + /// Op对象 + public static Op operator >=(Op left, Point3d right) + { + return GetCompOp(">,>,*", left, right); + } + + /// + /// 小于等于操作符 + /// + /// 过滤器操作符对象 + /// 点 + /// Op对象 + public static Op operator <=(Op left, Point3d right) + { + return GetCompOp("<,<,*", left, right); + } + + /// + /// 并操作符 + /// + /// 过滤器操作符对象 + /// 过滤器操作符对象 + /// Op对象 + public static Op operator &(Op left, Op right) + { + var filter = new OpAnd + { + left.Filter!, + right.Filter! + }; + return new Op(filter); + } + + /// + /// 或操作符 + /// + /// 过滤器操作符对象 + /// 过滤器操作符对象 + /// Op对象 + public static Op operator |(Op left, Op right) + { + var filter = new OpOr + { + left.Filter!, + right.Filter! + }; + return new Op(filter); + } + + /// + /// 异或操作符 + /// + /// 过滤器操作符对象 + /// 过滤器操作符对象 + /// Op对象 + public static Op operator ^(Op left, Op right) + { + var filter = new OpXor(left.Filter!, right.Filter!); + return new Op(filter); + } + + /// + /// 比较函数 + /// + /// 对象 + /// + /// 是否相等 + /// + public override bool Equals(object obj) => base.Equals(obj); + + /// + /// 获取HashCode + /// + /// HashCode + public override int GetHashCode() => base.GetHashCode(); + } + + #endregion Operator +} diff --git a/src/CAD/IFox.CAD.Shared/SelectionFilter/OpList.cs b/src/CAD/IFox.CAD.Shared/SelectionFilter/OpList.cs new file mode 100644 index 0000000..5870be9 --- /dev/null +++ b/src/CAD/IFox.CAD.Shared/SelectionFilter/OpList.cs @@ -0,0 +1,167 @@ +namespace IFoxCAD.Cad; + +/// +/// 逻辑操作符的列表抽象类 +/// +public abstract class OpList : OpLogi +{ + /// + /// 过滤器列表 + /// + protected List _lst = new(); + + /// + /// 添加过滤器条件的虚函数,子类可以重写 + /// + /// 举个利用这个类及其子类创建选择集过滤的例子 + /// + /// ,>,*" } + /// }, + /// }; + /// ]]> + /// + /// 过滤器对象 + public virtual void Add(OpFilter value) + { + _lst.Add(value); + } + + /// + /// 添加过滤条件 + /// + /// 逻辑非~ + /// 组码 + /// 组码值 + public void Add(string speccode, int code, object value) + { + if (speccode == "~") + _lst.Add(new OpEqual(code, value).Not); + } + + /// + /// 添加过滤条件 + /// + /// 组码 + /// 组码值 + public void Add(int code, object value) + { + _lst.Add(new OpEqual(code, value)); + } + + /// + /// 添加过滤条件 + /// + /// 组码 + /// 组码值 + public void Add(DxfCode code, object value) + { + _lst.Add(new OpEqual(code, value)); + } + + /// + /// 添加过滤条件 + /// + /// 组码 + /// 组码值 + /// 比较运算符 + public void Add(int code, object value, string comp) + { + _lst.Add(new OpComp(comp, code, value)); + } + + /// + /// 添加过滤条件 + /// + /// 组码 + /// 组码值 + /// 比较运算符 + public void Add(DxfCode code, object value, string comp) + { + _lst.Add(new OpComp(comp, code, value)); + } + + /// + /// 过滤器迭代器 + /// + /// OpFilter迭代器 + [System.Diagnostics.DebuggerStepThrough] + public override IEnumerator GetEnumerator() + { + foreach (var value in _lst) + yield return value; + } +} + +/// +/// 逻辑与类 +/// +public class OpAnd : OpList +{ + /// + /// 符号名 + /// + public override string Name + { + get { return "And"; } + } + + /// + /// 添加过滤条件 + /// + /// 过滤器对象 + public override void Add(OpFilter value) + { + if (value is OpAnd opand) + { + foreach (var item in opand) + _lst.Add(item); + } + else + { + _lst.Add(value); + } + } +} + +/// +/// 逻辑或类 +/// +public class OpOr : OpList +{ + /// + /// 符号名 + /// + public override string Name + { + get { return "Or"; } + } + + /// + /// 添加过滤条件 + /// + /// 过滤器对象 + public override void Add(OpFilter value) + { + if (value is OpOr opor) + { + foreach (var item in opor) + _lst.Add(item); + } + else + { + _lst.Add(value); + } + } +} \ No newline at end of file diff --git a/src/CAD/IFox.CAD.Shared/SelectionFilter/OpLogi.cs b/src/CAD/IFox.CAD.Shared/SelectionFilter/OpLogi.cs new file mode 100644 index 0000000..bdacb86 --- /dev/null +++ b/src/CAD/IFox.CAD.Shared/SelectionFilter/OpLogi.cs @@ -0,0 +1,133 @@ +namespace IFoxCAD.Cad; + +/// +/// 过滤器逻辑运算符抽象类 +/// +public abstract class OpLogi : OpFilter, IEnumerable +{ + /// + /// 返回-4组码的开始内容 + /// + public TypedValue First + { + get { return new TypedValue(-4, $"<{Name}"); } + } + + /// + /// 返回-4组码的结束内容 + /// + public TypedValue Last + { + get { return new TypedValue(-4, $"{Name}>"); } + } + + /// + /// 获取过滤条件 + /// + /// TypedValue迭代器 + //[System.Diagnostics.DebuggerStepThrough] + public override IEnumerable GetValues() + { + yield return First; + foreach (var item in this) + { + foreach (var value in item.GetValues()) + yield return value; + } + yield return Last; + } + + /// + /// 获取迭代器 + /// + /// OpFilter迭代器 + [System.Diagnostics.DebuggerStepThrough] + public abstract IEnumerator GetEnumerator(); + + [System.Diagnostics.DebuggerStepThrough] + IEnumerator IEnumerable.GetEnumerator() + { + return GetEnumerator(); + } +} + +/// +/// 逻辑非类 +/// +public class OpNot : OpLogi +{ + private OpFilter Value { get; } + + /// + /// 逻辑非类构造函数 + /// + /// OpFilter数据 + public OpNot(OpFilter value) + { + Value = value; + } + + /// + /// 符号名 + /// + public override string Name + { + get { return "Not"; } + } + + /// + /// 获取迭代器 + /// + /// OpFilter迭代器 + [System.Diagnostics.DebuggerStepThrough] + public override IEnumerator GetEnumerator() + { + yield return Value; + } +} + +/// +/// 逻辑异或类 +/// +public class OpXor : OpLogi +{ + /// + /// 左操作数 + /// + public OpFilter Left { get; } + + /// + /// 右操作数 + /// + public OpFilter Right { get; } + + /// + /// 逻辑异或类构造函数 + /// + /// 左操作数 + /// 右操作数 + public OpXor(OpFilter left, OpFilter right) + { + Left = left; + Right = right; + } + + /// + /// 符号名 + /// + public override string Name + { + get { return "Xor"; } + } + + /// + /// 获取迭代器 + /// + /// 选择集过滤器类型迭代器 + [System.Diagnostics.DebuggerStepThrough] + public override IEnumerator GetEnumerator() + { + yield return Left; + yield return Right; + } +} \ No newline at end of file diff --git a/src/CAD/IFox.CAD.Shared/WindowsAPI/Enums.cs b/src/CAD/IFox.CAD.Shared/WindowsAPI/Enums.cs new file mode 100644 index 0000000..e70dfd6 --- /dev/null +++ b/src/CAD/IFox.CAD.Shared/WindowsAPI/Enums.cs @@ -0,0 +1,1071 @@ +#if true +namespace IFoxCAD.Cad; + +// https://blog.csdn.net/qq_43812868/article/details/108587936 +[Flags] +public enum TH32CS : uint +{ + /// + /// 原因在于如果不采用改参数的话,有可能快照会占用整个堆的空间 + /// + TH32CS_SNAPNOHEAPS = 0x40000000, + /// + /// 声明快照句柄是可继承的 + /// + TH32CS_INHERIT = 0x80000000, + /// + /// 在快照中包含在th32ProcessID中指定的进程的所有的堆 + /// + TH32CS_SNAPHEAPLIST = 0x00000001, + /// + /// 在快照中包含系统中所有的进程 + /// + TH32CS_SNAPPROCESS = 0x00000002, + /// + /// 在快照中包含系统中所有的线程 + /// + TH32CS_SNAPTHREAD = 0x00000004, + /// + /// 在快照中包含在th32ProcessID中指定的进程的所有的模块 + /// + TH32CS_SNAPMODULE = 0x00000008, + /// + /// 在快照中包含系统中所有的进程和线程 + /// + TH32CS_SNAPALL = TH32CS_SNAPHEAPLIST | TH32CS_SNAPPROCESS | TH32CS_SNAPTHREAD | TH32CS_SNAPMODULE, +} + +/// +/// 设置的钩子类型 +/// +[Flags] +public enum HookType : int +{ + /// + /// WH_MSGFILTER 和 WH_SYSMSGFILTER Hooks使我们可以监视菜单,滚动 + /// 条,消息框,对话框消息并且发现用户使用ALT+TAB or ALT+ESC 组合键切换窗口。 + /// WH_MSGFILTER Hook只能监视传递到菜单,滚动条,消息框的消息,以及传递到通 + /// 过安装了Hook子过程的应用程序建立的对话框的消息。WH_SYSMSGFILTER Hook + /// 监视所有应用程序消息。 + /// + /// WH_MSGFILTER 和 WH_SYSMSGFILTER Hooks使我们可以在模式循环期间 + /// 过滤消息,这等价于在主消息循环中过滤消息。 + /// + /// 通过调用CallMsgFilter function可以直接的调用WH_MSGFILTER Hook。通过使用这 + /// 个函数,应用程序能够在模式循环期间使用相同的代码去过滤消息,如同在主消息循 + /// 环里一样 + /// + WH_MSGFILTER = -1, + /// + /// WH_JOURNALRECORD Hook用来监视和记录输入事件。典型的,可以使用这 + /// 个Hook记录连续的鼠标和键盘事件,然后通过使用WH_JOURNALPLAYBACK Hook + /// 来回放。WH_JOURNALRECORD Hook是全局Hook,它不能象线程特定Hook一样 + /// 使用。WH_JOURNALRECORD是system-wide local hooks,它们不会被注射到任何行 + /// 程地址空间 + /// + WH_JOURNALRECORD = 0, + /// + /// WH_JOURNALPLAYBACK Hook使应用程序可以插入消息到系统消息队列。可 + /// 以使用这个Hook回放通过使用WH_JOURNALRECORD Hook记录下来的连续的鼠 + /// 标和键盘事件。只要WH_JOURNALPLAYBACK Hook已经安装,正常的鼠标和键盘 + /// 事件就是无效的。WH_JOURNALPLAYBACK Hook是全局Hook,它不能象线程特定 + /// Hook一样使用。WH_JOURNALPLAYBACK Hook返回超时值,这个值告诉系统在处 + /// 理来自回放Hook当前消息之前需要等待多长时间(毫秒)。这就使Hook可以控制实 + /// 时事件的回放。WH_JOURNALPLAYBACK是system-wide local hooks,它们不会被 + /// 注射到任何行程地址空间 + /// + WH_JOURNALPLAYBACK = 1, + /// + /// 在应用程序中,WH_KEYBOARD Hook用来监视WM_KEYDOWN and + /// WM_KEYUP消息,这些消息通过GetMessage or PeekMessage function返回。可以使 + /// 用这个Hook来监视输入到消息队列中的键盘消息 + /// + WH_KEYBOARD = 2, + /// + /// 应用程序使用WH_GETMESSAGE Hook来监视从GetMessage or PeekMessage函 + /// 数返回的消息。你可以使用WH_GETMESSAGE Hook去监视鼠标和键盘输入,以及 + /// 其它发送到消息队列中的消息 + /// + WH_GETMESSAGE = 3, + /// + /// 监视发送到窗口过程的消息,系统在消息发送到接收窗口过程之前调用 + /// + WH_CALLWNDPROC = 4, + /// + /// 在以下事件之前,系统都会调用WH_CBT Hook子过程,这些事件包括: + /// 1. 激活,建立,销毁,最小化,最大化,移动,改变尺寸等窗口事件; + /// 2. 完成系统指令; + /// 3. 来自系统消息队列中的移动鼠标,键盘事件; + /// 4. 设置输入焦点事件; + /// 5. 同步系统消息队列事件。 + /// Hook子过程的返回值确定系统是否允许或者防止这些操作中的一个 + /// + WH_CBT = 5, + /// + /// WH_MSGFILTER 和 WH_SYSMSGFILTER Hooks使我们可以监视菜单,滚动 + /// 条,消息框,对话框消息并且发现用户使用ALT+TAB or ALT+ESC 组合键切换窗口。 + /// WH_MSGFILTER Hook只能监视传递到菜单,滚动条,消息框的消息,以及传递到通 + /// 过安装了Hook子过程的应用程序建立的对话框的消息。WH_SYSMSGFILTER Hook + /// 监视所有应用程序消息。 + /// + /// WH_MSGFILTER 和 WH_SYSMSGFILTER Hooks使我们可以在模式循环期间 + /// 过滤消息,这等价于在主消息循环中过滤消息。 + /// + /// 通过调用CallMsgFilter function可以直接的调用WH_MSGFILTER Hook。通过使用这 + /// 个函数,应用程序能够在模式循环期间使用相同的代码去过滤消息,如同在主消息循 + /// 环里一样 + /// + WH_SYSMSGFILTER = 6, + /// + /// WH_MOUSE Hook监视从GetMessage 或者 PeekMessage 函数返回的鼠标消息。 + /// 使用这个Hook监视输入到消息队列中的鼠标消息 + /// + WH_MOUSE = 7, + /// + /// 当调用GetMessage 或 PeekMessage 来从消息队列种查询非鼠标、键盘消息时 + /// + WH_HARDWARE = 8, + /// + /// 在系统调用系统中与其它Hook关联的Hook子过程之前,系统会调用 + /// WH_DEBUG Hook子过程。你可以使用这个Hook来决定是否允许系统调用与其它 + /// Hook关联的Hook子过程 + /// + WH_DEBUG = 9, + /// + /// 外壳应用程序可以使用WH_SHELL Hook去接收重要的通知。当外壳应用程序是 + /// 激活的并且当顶层窗口建立或者销毁时,系统调用WH_SHELL Hook子过程。 + /// WH_SHELL 共有5钟情况: + /// 1. 只要有个top-level、unowned 窗口被产生、起作用、或是被摧毁; + /// 2. 当Taskbar需要重画某个按钮; + /// 3. 当系统需要显示关于Taskbar的一个程序的最小化形式; + /// 4. 当目前的键盘布局状态改变; + /// 5. 当使用者按Ctrl+Esc去执行Task Manager(或相同级别的程序)。 + /// + /// 按照惯例,外壳应用程序都不接收WH_SHELL消息。所以,在应用程序能够接 + /// 收WH_SHELL消息之前,应用程序必须调用SystemParametersInfo function注册它自 + /// 己 + /// + WH_SHELL = 10, + /// + /// 当应用程序的前台线程处于空闲状态时,可以使用WH_FOREGROUNDIDLE + /// Hook执行低优先级的任务。当应用程序的前台线程大概要变成空闲状态时,系统就 + /// 会调用WH_FOREGROUNDIDLE Hook子过程 + /// + WH_FOREGROUNDIDLE = 11, + /// + /// 监视发送到窗口过程的消息,系统在消息发送到接收窗口过程之后调用 + /// + WH_CALLWNDPROCRET = 12, + /// + /// 监视键盘消息 + /// + WH_KEYBOARD_LL = 13, + /// + /// 监视鼠标消息 + /// + WH_MOUSE_LL = 14 +} + +/// +/// 消息类型 +/// 作为SendMessage和PostMessage的参数 +/// +[Flags] +public enum WM : uint +{ + /// + /// 创建一个窗口 + /// + WM_CREATE = 0x01, + /// + /// 当一个窗口被破坏时发送 + /// + WM_DESTROY = 0x02, + /// + /// 移动一个窗口 + /// + WM_MOVE = 0x03, + /// + /// 改变一个窗口的大小 + /// + WM_SIZE = 0x05, + /// + /// 一个窗口被激活或失去激活状态 + /// + WM_ACTIVATE = 0x06, + /// + /// 一个窗口获得焦点 + /// + WM_SETFOCUS = 0x07, + /// + /// 一个窗口失去焦点 + /// + WM_KILLFOCUS = 0x08, + /// + /// 一个窗口改变成Enable状态 + /// + WM_ENABLE = 0x0A, + /// + /// 设置窗口是否能重画 + /// + WM_SETREDRAW = 0x0B, + /// + /// 应用程序发送此消息来设置一个窗口的文本 + /// + WM_SETTEXT = 0x0C, + /// + /// 应用程序发送此消息来复制对应窗口的文本到缓冲区 + /// + WM_GETTEXT = 0x0D, + /// + /// 得到与一个窗口有关的文本的长度(不包含空字符) + /// + WM_GETTEXTLENGTH = 0x0E, + /// + /// 要求一个窗口重画自己 + /// + WM_PAINT = 0x0F, + /// + /// 当一个窗口或应用程序要关闭时发送一个信号 + /// + WM_CLOSE = 0x10, + /// + /// 当用户选择结束对话框或程序自己调用ExitWindows函数 + /// + WM_QUERYENDSESSION = 0x11, + /// + /// 用来结束程序运行 + /// + WM_QUIT = 0x12, + /// + /// 当用户窗口恢复以前的大小位置时,把此消息发送给某个图标 + /// + WM_QUERYOPEN = 0x13, + /// + /// 当窗口背景必须被擦除时(例在窗口改变大小时) + /// + WM_ERASEBKGND = 0x14, + /// + /// 当系统颜色改变时,发送此消息给所有顶级窗口 + /// + WM_SYSCOLORCHANGE = 0x15, + /// + /// 当系统进程发出WM_QUERYENDSESSION消息后,此消息发送给应用程序,通知它对话是否结束 + /// + WM_ENDSESSION = 0x16, + /// + /// 当隐藏或显示窗口是发送此消息给这个窗口 + /// + WM_SHOWWINDOW = 0x18, + /// + /// 发此消息给应用程序哪个窗口是激活的,哪个是非激活的 + /// + WM_ACTIVATEAPP = 0x1C, + /// + /// 当系统的字体资源库变化时发送此消息给所有顶级窗口 + /// + WM_FONTCHANGE = 0x1D, + /// + /// 当系统的时间变化时发送此消息给所有顶级窗口 + /// + WM_TIMECHANGE = 0x1E, + /// + /// 发送此消息来取消某种正在进行的摸态(操作) + /// + WM_CANCELMODE = 0x1F, + /// + /// 如果鼠标引起光标在某个窗口中移动且鼠标输入没有被捕获时,就发消息给某个窗口 + /// + WM_SETCURSOR = 0x20, + /// + /// 当光标在某个非激活的窗口中而用户正按着鼠标的某个键发送此消息给当前窗口 + /// + WM_MOUSEACTIVATE = 0x21, + /// + /// 发送此消息给MDI子窗口当用户点击此窗口的标题栏或当窗口被激活,移动,改变大小 + /// + WM_CHILDACTIVATE = 0x22, + /// + /// 此消息由基于计算机的训练程序发送,通过WH_JOURNALPALYBACK的hook程序分离出用户输入消息 + /// + WM_QUEUESYNC = 0x23, + /// + /// 此消息发送给窗口当它将要改变大小或位置 + /// + WM_GETMINMAXINFO = 0x24, + /// + /// 发送给最小化窗口当它图标将要被重画 + /// + WM_PAINTICON = 0x26, + /// + /// 此消息发送给某个最小化窗口,仅当它在画图标前它的背景必须被重画 + /// + WM_ICONERASEBKGND = 0x27, + /// + /// 发送此消息给一个对话框程序去更改焦点位置 + /// + WM_NEXTDLGCTL = 0x28, + /// + /// 每当打印管理列队增加或减少一条作业时发出此消息 + /// + WM_SPOOLERSTATUS = 0x2A, + /// + /// 当button,combobox,listbox,menu的可视外观改变时发送 + /// + WM_DRAWITEM = 0x2B, + /// + /// 当button, combo box, list box, list view control, or menu item 被创建时 + /// + WM_MEASUREITEM = 0x2C, + /// + /// 此消息有一个LBS_WANTKEYBOARDINPUT风格的发出给它的所有者来响应WM_KEYDOWN消息 + /// + WM_VKEYTOITEM = 0x2E, + /// + /// 此消息由一个LBS_WANTKEYBOARDINPUT风格的列表框发送给他的所有者来响应WM_CHAR消息 + /// + WM_CHARTOITEM = 0x2F, + /// + /// 当绘制文本时程序发送此消息得到控件要用的颜色 + /// + WM_SETFONT = 0x30, + /// + /// 应用程序发送此消息得到当前控件绘制文本的字体 + /// + WM_GETFONT = 0x31, + /// + /// 应用程序发送此消息让一个窗口与一个热键相关连 + /// + WM_SETHOTKEY = 0x32, + /// + /// 应用程序发送此消息来判断热键与某个窗口是否有关联 + /// + WM_GETHOTKEY = 0x33, + /// + /// 此消息发送给最小化窗口,当此窗口将要被拖放而它的类中没有定义图标,应用程序能返回一个图标或光标的句柄,当用户拖放图标时系统显示这个图标或光标 + /// + WM_QUERYDRAGICON = 0x37, + /// + /// 发送此消息来判定combobox或listbox新增加的项的相对位置 + /// + WM_COMPAREITEM = 0x39, + /// + /// 显示内存已经很少了 + /// + WM_COMPACTING = 0x41, + /// + /// 窗口大小和位置将要被改变时,来调用Setwindowpos函数或其它窗口管理函数 + /// + WM_WINDOWPOSCHANGING = 0x46, + /// + /// 窗口大小和位置已经被改变后,来调用Setwindowpos函数或其它窗口管理函数 + /// + WM_WINDOWPOSCHANGED = 0x47, + /// + /// 当系统将要进入暂停状态时发送此消息 + /// + WM_POWER = 0x48, + /// + /// 当一个应用程序传递数据给另一个应用程序时发送此消息 + /// + WM_COPYDATA = 0x4A, + /// + /// 当某个用户取消程序日志激活状态,提交此消息给程序 + /// + WM_CANCELJOURNA = 0x4B, + /// + /// 当某个控件的某个事件已经发生或这个控件需要得到一些信息时,发送此消息给它的父窗口 + /// + WM_NOTIFY = 0x4E, + /// + /// 当用户选择某种输入语言,或输入语言的热键改变 + /// + WM_INPUTLANGCHANGEREQUEST = 0x50, + /// + /// 当平台现场已经被改变后发送此消息给受影响的最顶级窗口 + /// + WM_INPUTLANGCHANGE = 0x51, + /// + /// 当程序已经初始化windows帮助例程时发送此消息给应用程序 + /// + WM_TCARD = 0x52, + /// + /// 此消息显示用户按下了F1,如果某个菜单是激活的,就发送此消息个此窗口关联的菜单,否则就发送给有焦点的窗口,如果当前都没有焦点,就把此消息发送给当前激活的窗口 + /// + WM_HELP = 0x53, + /// + /// 当用户已经登入或退出后发送此消息给所有的窗口,当用户登入或退出时系统更新用户的具体设置信息,在用户更新设置时系统马上发送此消息 + /// + WM_USERCHANGED = 0x54, + /// + /// 公用控件,自定义控件和他们的父窗口通过此消息来判断控件是使用ANSI还是UNICODE结构 + /// + WM_NOTIFYFORMAT = 0x55, + /// + /// 当用户某个窗口中点击了一下右键就发送此消息给这个窗口 + /// + WM_CONTEXTMENU = 0x7B, + /// + /// 当调用SETWINDOWLONG函数将要改变一个或多个窗口的风格时发送此消息给那个窗口 + /// + WM_STYLECHANGING = 0x7C, + /// + /// 当调用SETWINDOWLONG函数一个或多个窗口的风格后发送此消息给那个窗口 + /// + WM_STYLECHANGED = 0x7D, + /// + /// 当显示器的分辨率改变后发送此消息给所有的窗口 + /// + WM_DISPLAYCHANGE = 0x7E, + /// + /// 此消息发送给某个窗口来返回与某个窗口有关连的大图标或小图标的句柄 + /// + WM_GETICON = 0x7F, + /// + /// 程序发送此消息让一个新的大图标或小图标与某个窗口关联 + /// + WM_SETICON = 0x80, + /// + /// 当某个窗口第一次被创建时,此消息在WM_CREATE消息发送前发送 + /// + WM_NCCREATE = 0x81, + /// + /// 此消息通知某个窗口,非客户区正在销毁 + /// + WM_NCDESTROY = 0x82, + /// + /// 当某个窗口的客户区域必须被核算时发送此消息 + /// + WM_NCCALCSIZE = 0x83, + /// + /// 移动鼠标/按住/释放鼠标时 + /// + WM_NCHITTEST = 0x84, + /// + /// 程序发送此消息给某个窗口当它(窗口)的框架必须被绘制时 + /// + WM_NCPAINT = 0x85, + /// + /// 此消息发送给某个窗口仅当它的非客户区需要被改变来显示是激活还是非激活状态 + /// + WM_NCACTIVATE = 0x86, + /// + /// 发送此消息给某个与对话框程序关联的控件,widdows控制方位键和TAB键使输入进入此控件通过应 + /// + WM_GETDLGCODE = 0x87, + /// + /// 当光标在一个窗口的非客户区内移动时发送此消息给这个窗口 非客户区为:窗体的标题栏及窗的边框体 + /// + WM_NCMOUSEMOVE = 0xA0, + /// + /// 当光标在一个窗口的非客户区同时按下鼠标左键时提交此消息 + /// + WM_NCLBUTTONDOWN = 0xA1, + /// + /// 当用户释放鼠标左键同时光标某个窗口在非客户区时发送此消息 + /// + WM_NCLBUTTONUP = 0xA2, + /// + /// 当用户双击鼠标左键同时光标某个窗口在非客户区时发送此消息 + /// + WM_NCLBUTTONDBLCLK = 0xA3, + /// + /// 当用户按下鼠标右键同时光标又在窗口的非客户区时发送此消息 + /// + WM_NCRBUTTONDOWN = 0xA4, + /// + /// 当用户释放鼠标右键同时光标又在窗口的非客户区时发送此消息 + /// + WM_NCRBUTTONUP = 0xA5, + /// + /// 当用户双击鼠标右键同时光标某个窗口在非客户区时发送此消息 + /// + WM_NCRBUTTONDBLCLK = 0xA6, + /// + /// 当用户按下鼠标中键同时光标又在窗口的非客户区时发送此消息 + /// + WM_NCMBUTTONDOWN = 0xA7, + /// + /// 当用户释放鼠标中键同时光标又在窗口的非客户区时发送此消息 + /// + WM_NCMBUTTONUP = 0xA8, + /// + /// 当用户双击鼠标中键同时光标又在窗口的非客户区时发送此消息 + /// + WM_NCMBUTTONDBLCLK = 0xA9, + + // 所有的键盘消息只有中间的八种,也就是 WM_KEYDOWN 到 WM_SYSDEADCHAR + /// + /// 按下一个键 == WM_KEYDOWN + /// + WM_KEYFIRST = 0x0100, + /// + /// 按下一个键 + /// + WM_KEYDOWN = 0x0100, + /// + /// 释放一个键 + /// + WM_KEYUP = 0x0101, + /// + /// 按下某键,并已发出WM_KEYDOWN, WM_KEYUP消息 + /// + WM_CHAR = 0x102, + /// + /// 当用translatemessage函数翻译WM_KEYUP消息时发送此消息给拥有焦点的窗口 + /// + WM_DEADCHAR = 0x103, + /// + /// 当用户按住ALT键同时按下其它键时提交此消息给拥有焦点的窗口 + /// + WM_SYSKEYDOWN = 0x104, + /// + /// 当用户释放一个键同时ALT 键还按着时提交此消息给拥有焦点的窗口 + /// + WM_SYSKEYUP = 0x105, + /// + /// 当WM_SYSKEYDOWN消息被TRANSLATEMESSAGE函数翻译后提交此消息给拥有焦点的窗口 + /// + WM_SYSCHAR = 0x106, + /// + /// 当WM_SYSKEYDOWN消息被TRANSLATEMESSAGE函数翻译后发送此消息给拥有焦点的窗口 + /// + WM_SYSDEADCHAR = 0x107, + /// + /// 在一个对话框程序被显示前发送此消息给它,通常用此消息初始化控件和执行其它任务 + /// + WM_INITDIALOG = 0x110, + /// + /// 当用户选择一条菜单命令项或当某个控件发送一条消息给它的父窗口 + /// + WM_COMMAND = 0x111, + + /// + /// 当用户选择窗口菜单的一条命令或最大化最小化时窗口前会收到此消息 + /// + WM_SYSCOMMAND = 0x112, + /// + /// 发生了定时器事件 + /// + WM_TIMER = 0x113, + /// + /// 当一个窗口标准水平滚动条产生一个滚动事件时发送此消息给那个窗口,也发送给拥有它的控件 + /// + WM_HSCROLL = 0x114, + /// + /// 当一个窗口标准垂直滚动条产生一个滚动事件时发送此消息给那个窗口也,发送给拥有它的控件 + /// + WM_VSCROLL = 0x115, + /// + /// 当一个菜单将要被激活时发送此消息,它发生在用户菜单条中的某项或按下某个菜单键,它允许程序在显示前更改菜单 + /// + WM_INITMENU = 0x116, + /// + /// 当一个下拉菜单或子菜单将要被激活时发送此消息,它允许程序在它显示前更改菜单,而不要改变全部 + /// + WM_INITMENUPOPUP = 0x117, + /// + /// 当用户选择一条菜单项时发送此消息给菜单的所有者(一般是窗口) + /// + WM_MENUSELECT = 0x11F, + /// + /// 当菜单已被激活用户按下了某个键(不同于加速键),发送此消息给菜单的所有者 + /// + WM_MENUCHAR = 0x120, + /// + /// 当一个模态对话框或菜单进入空载状态时发送此消息给它的所有者,一个模态对话框或菜单进入空载状态就是在处理完一条或几条先前的消息后没有消息它的列队中等待 + /// + WM_ENTERIDLE = 0x121, + /// + /// 在windows绘制消息框前发送此消息给消息框的所有者窗口,通过响应这条消息,所有者窗口可以通过使用给定的相关显示设备的句柄来设置消息框的文本和背景颜色 + /// + WM_CTLCOLORMSGBOX = 0x132, + /// + /// 当一个编辑型控件将要被绘制时发送此消息给它的父窗口通过响应这条消息,所有者窗口可以通过使用给定的相关显示设备的句柄来设置编辑框的文本和背景颜色 + /// + WM_CTLCOLOREDIT = 0x133, + + /// + /// 当一个列表框控件将要被绘制前发送此消息给它的父窗口通过响应这条消息,所有者窗口可以通过使用给定的相关显示设备的句柄来设置列表框的文本和背景颜色 + /// + WM_CTLCOLORLISTBOX = 0x134, + /// + /// 当一个按钮控件将要被绘制时发送此消息给它的父窗口通过响应这条消息,所有者窗口可以通过使用给定的相关显示设备的句柄来设置按纽的文本和背景颜色 + /// + WM_CTLCOLORBTN = 0x135, + /// + /// 当一个对话框控件将要被绘制前发送此消息给它的父窗口通过响应这条消息,所有者窗口可以通过使用给定的相关显示设备的句柄来设置对话框的文本背景颜色 + /// + WM_CTLCOLORDLG = 0x136, + /// + /// 当一个滚动条控件将要被绘制时发送此消息给它的父窗口通过响应这条消息,所有者窗口可以通过使用给定的相关显示设备的句柄来设置滚动条的背景颜色 + /// + WM_CTLCOLORSCROLLBAR = 0x137, + /// + /// 当一个静态控件将要被绘制时发送此消息给它的父窗口通过响应这条消息,所有者窗口可以通过使用给定的相关显示设备的句柄来设置静态控件的文本和背景颜色 + /// + WM_CTLCOLORSTATIC = 0x138, + + /// + /// 当鼠标轮子转动时发送此消息个当前有焦点的控件 + /// + WM_MOUSEWHEEL = 0x20A, + /// + /// 双击鼠标中键 + /// + WM_MBUTTONDBLCLK = 0x209, + /// + /// 释放鼠标中键 + /// + WM_MBUTTONUP = 0x208, + /// + /// 移动鼠标时发生,同WM_MOUSEFIRST + /// + WM_MOUSEMOVE = 0x200, + /// + /// 按下鼠标左键 + /// + WM_LBUTTONDOWN = 0x201, + /// + /// 释放鼠标左键 + /// + WM_LBUTTONUP = 0x202, + /// + /// 双击鼠标左键 + /// + WM_LBUTTONDBLCLK = 0x203, + /// + /// 按下鼠标右键 + /// + WM_RBUTTONDOWN = 0x204, + /// + /// 释放鼠标右键 + /// + WM_RBUTTONUP = 0x205, + /// + /// 双击鼠标右键 + /// + WM_RBUTTONDBLCLK = 0x206, + /// + /// 按下鼠标中键 + /// + WM_MBUTTONDOWN = 0x207, + + WM_USER = 0x0400, + + /// + /// 执行复制成功 + /// + WM_CLIPBOARDUPDATE = 0x031D, +} + +// https://blog.csdn.net/biyusr/article/details/108376195 +public enum MOUSEEVENTF : int +{ + /// + /// 移动鼠标 + /// + MOVE = 0x0001, + /// + /// 模拟鼠标左键按下 + /// + LEFTDOWN = 0x0002, + /// + /// 模拟鼠标左键抬起 + /// + LEFTUP = 0x0004, + /// + /// 模拟鼠标右键按下 + /// + RIGHTDOWN = 0x0008, + /// + /// 模拟鼠标右键抬起 + /// + RIGHTUP = 0x0010, + /// + /// 模拟鼠标中键按下 + /// + MIDDLEDOWN = 0x0020, + /// + /// 模拟鼠标中键抬起 + /// + MIDDLEUP = 0x0040, + /// + /// 标示是否采用绝对坐标 + /// + ABSOLUTE = 0x8000, + /// + /// 模拟鼠标滚轮滚动操作,必须配合dwData参数 + /// + WHEEL = 0x0800, +} + +// C#使用SendMessage发送组合键 +// https://www.cnblogs.com/johnsonton/articles/2331430.html +// Windows 使用的256个虚拟键码 +[Flags]// 打印的时候可以有名称输出,而不是值输出 +public enum VK : int +{ + VK_LBUTTON = 0x1, + VK_RBUTTON = 0x2, + VK_CANCEL = 0x3, + VK_MBUTTON = 0x4, + VK_BACK = 0x8, + VK_TAB = 0x9, + VK_CLEAR = 0xC, + VK_RETURN = 0xD, + VK_SHIFT = 0x10, + VK_CONTROL = 0x11, + VK_MENU = 0x12,// VK_ALT + VK_ALT = 0x12, + VK_PAUSE = 0x13, + VK_CAPITAL = 0x14, + VK_ESCAPE = 0x1B, + VK_SPACE = 0x20, + VK_PRIOR = 0x21, + VK_NEXT = 0x22, + VK_END = 0x23, + VK_HOME = 0x24, + VK_LEFT = 0x25, + VK_UP = 0x26, + VK_RIGHT = 0x27, + VK_DOWN = 0x28, + VK_Select = 0x29, + VK_PRINT = 0x2A, + VK_EXECUTE = 0x2B, + VK_SNAPSHOT = 0x2C, + VK_Insert = 0x2D, + VK_Delete = 0x2E, + VK_HELP = 0x2F, + VK_0 = 0x30, + VK_1 = 0x31, + VK_2 = 0x32, + VK_3 = 0x33, + VK_4 = 0x34, + VK_5 = 0x35, + VK_6 = 0x36, + VK_7 = 0x37, + VK_8 = 0x38, + VK_9 = 0x39, + VK_A = 0x41, + VK_B = 0x42, + VK_C = 0x43, + VK_D = 0x44, + VK_E = 0x45, + VK_F = 0x46, + VK_G = 0x47, + VK_H = 0x48, + VK_I = 0x49, + VK_J = 0x4A, + VK_K = 0x4B, + VK_L = 0x4C, + VK_M = 0x4D, + VK_N = 0x4E, + VK_O = 0x4F, + VK_P = 0x50, + VK_Q = 0x51, + VK_R = 0x52, + VK_S = 0x53, + VK_T = 0x54, + VK_U = 0x55, + VK_V = 0x56, + VK_W = 0x57, + VK_X = 0x58, + VK_Y = 0x59, + VK_Z = 0x5A, + VK_STARTKEY = 0x5B, + VK_CONTEXTKEY = 0x5D, + VK_NUMPAD0 = 0x60, + VK_NUMPAD1 = 0x61, + VK_NUMPAD2 = 0x62, + VK_NUMPAD3 = 0x63, + VK_NUMPAD4 = 0x64, + VK_NUMPAD5 = 0x65, + VK_NUMPAD6 = 0x66, + VK_NUMPAD7 = 0x67, + VK_NUMPAD8 = 0x68, + VK_NUMPAD9 = 0x69, + VK_MULTIPLY = 0x6A, + VK_ADD = 0x6B, + VK_SEPARATOR = 0x6C, + VK_SUBTRACT = 0x6D, + VK_DECIMAL = 0x6E, + VK_DIVIDE = 0x6F, + VK_F1 = 0x70, + VK_F2 = 0x71, + VK_F3 = 0x72, + VK_F4 = 0x73, + VK_F5 = 0x74, + VK_F6 = 0x75, + VK_F7 = 0x76, + VK_F8 = 0x77, + VK_F9 = 0x78, + VK_F10 = 0x79, + VK_F11 = 0x7A, + VK_F12 = 0x7B, + VK_F13 = 0x7C, + VK_F14 = 0x7D, + VK_F15 = 0x7E, + VK_F16 = 0x7F, + VK_F17 = 0x80, + VK_F18 = 0x81, + VK_F19 = 0x82, + VK_F20 = 0x83, + VK_F21 = 0x84, + VK_F22 = 0x85, + VK_F23 = 0x86, + VK_F24 = 0x87, + VK_NUMLOCK = 0x90, + VK_OEM_SCROLL = 0x91, + VK_OEM_1 = 0xBA, + VK_OEM_PLUS = 0xBB, + VK_OEM_COMMA = 0xBC, + VK_OEM_MINUS = 0xBD, + VK_OEM_PERIOD = 0xBE, + VK_OEM_2 = 0xBF, + VK_OEM_3 = 0xC0, + VK_OEM_4 = 0xDB, + VK_OEM_5 = 0xDC, + VK_OEM_6 = 0xDD, + VK_OEM_7 = 0xDE, + VK_OEM_8 = 0xDF, + VK_ICO_F17 = 0xE0, + VK_ICO_F18 = 0xE1, + VK_OEM102 = 0xE2, + VK_ICO_HELP = 0xE3, + VK_ICO_00 = 0xE4, + VK_ICO_CLEAR = 0xE6, + VK_OEM_RESET = 0xE9, + VK_OEM_JUMP = 0xEA, + VK_OEM_PA1 = 0xEB, + VK_OEM_PA2 = 0xEC, + VK_OEM_PA3 = 0xED, + VK_OEM_WSCTRL = 0xEE, + VK_OEM_CUSEL = 0xEF, + VK_OEM_ATTN = 0xF0, + VK_OEM_FINNISH = 0xF1, + VK_OEM_COPY = 0xF2, + VK_OEM_AUTO = 0xF3, + VK_OEM_ENLW = 0xF4, + VK_OEM_BACKTAB = 0xF5, + VK_ATTN = 0xF6, + VK_CRSEL = 0xF7, + VK_EXSEL = 0xF8, + VK_EREOF = 0xF9, + VK_PLAY = 0xFA, + VK_ZOOM = 0xFB, + VK_NONAME = 0xFC, + VK_PA1 = 0xFD, + VK_OEM_CLEAR = 0xFE, +} + +[Flags] +public enum SC : uint +{ + // 窗体关闭消息 + SC_CLOSE = 0xf060, + // 窗体最小化消息 + SC_MINIMIZE = 0xf020, + // 窗体最大化消息 + SC_MAXIMIZE = 0xf030, + // 窗体正常态消息 SC_RESTORE = 0xf120, + SC_NOMAL = 0xf120, +} + +[Flags] +public enum NCmdShow : uint +{ + /// + /// 隐藏窗口并激活其他窗口。nCmdShow + /// + SW_HIDE = 0, + /// + /// 正常态的窗口(非最大化和非最小化) + /// 激活并显示一个窗口。如果窗口被最小化或最大化,系统将其恢复到原来的尺寸和大小。应用程序在第一次显示窗口的时候应该指定此标志 + /// + SW_SHOWNORMAL = 1, + /// + /// 激活窗口并将其最小化 + /// + SW_SHOWMINIMIZED = 2, + /// + /// 激活窗口并将其最大化 + /// + SW_SHOWMAXIMIZED = 3, + /// + /// 最大化指定的窗口 + /// + SW_MAXIMIZE = 3, + /// + /// 以窗口最近一次的大小和状态显示窗口。激活窗口仍然维持激活状态 + /// + SW_SHOWNOACTIVATE = 4, + /// + /// 在窗口原来的位置以原来的尺寸激活和显示窗口 + /// + SW_SHOW = 5, + /// + /// 最小化指定的窗口并且激活在Z序中的下一个顶层窗口 + /// + SW_MINIMIZE = 6, + /// + /// 窗口最小化,激活窗口仍然维持激活状态 + /// + SW_SHOWMINNOACTIVE = 7, + /// + /// 以窗口原来的状态显示窗口。激活窗口仍然维持激活状态 + /// + SW_SHOWNA = 8, + /// + /// 激活并显示窗口。如果窗口最小化或最大化,则系统将窗口恢复到原来的尺寸和位置。在恢复最小化窗口时,应用程序应该指定这个标志 + /// + SW_RESTORE = 9, + /// + /// 依据在STARTUPINFO结构中指定的SW_FLAG标志设定显示状态,STARTUPINFO 结构是由启动应用程序的程序传递给CreateProcess函数的 + /// + SW_SHOWDEFAULT = 10, + /// + /// 在WindowNT5.0中最小化窗口,即使拥有窗口的线程被挂起也会最小化。在从其他线程最小化窗口时才使用这个参数 + /// + SW_FORCEMINIMIZE = 11, +} + +public enum WS : uint +{ + // 窗口风格 + WS_CAPTION = 0xC00000, // 带标题栏的窗口 + WS_MAXIMIZEBOX = 0x10000, // 带最大化按钮的窗口 + WS_MINIMIZEBOX = 0x20000, // 带最小化按钮的窗口 + WS_SYSMENU = 0x80000, // 带系统菜单的窗口 + WS_CLIPSIBLINGS = 0x4000000, // 不重绘层叠子窗口 + WS_CLIPCHILDREN = 0x2000000, // 绘图时排子窗口区域 + WS_OVERLAPPED = 0x0, // 具有标题栏和边框的层叠窗口 + WS_THICKFRAME = 0x40000, // 具有可调边框 + + // 具有标题栏、窗口菜单、可调边框和最大化、最小化按钮的窗口 + WS_OVERLAPPEDWINDOW = WS_OVERLAPPED | WS_CAPTION | WS_SYSMENU | WS_THICKFRAME | WS_MINIMIZEBOX | WS_MAXIMIZEBOX, + + WS_GROUP = 0x20000, // 指定一组控制的第一个控制 + WS_POPUP = 0x80000000, // 弹出式窗口 + WS_BORDER = 0x800000, // 单边框窗口 + WS_POPUPWINDOW = WS_POPUP | WS_BORDER | WS_SYSMENU, // 具有单边框、标题栏菜单的弹出式窗口 + WS_MINIMIZE = 0x20000000, // 窗口最小化 + WS_VISIBLE = 0x10000000, // 窗口可见 + WS_DISABLED = 0x8000000, // 窗口被禁用 + WS_MAXIMIZE = 0x1000000, // 窗口最大化 + WS_DLGFRAME = 0x400000, // 对话框边框风格 + WS_VSCROLL = 0x200000, // 具有垂直滚动条 + WS_HSCROLL = 0x100000, // 具有水平滚动条 + WS_TABSTOP = 0x10000, // 具有TAB键控制 + WS_CHILD = 0x40000000, // 设置窗口属性为child 多文档界面的子窗体 + WS_CHILDWINDOW = WS_CHILD, // 具有子窗口 + + // 扩展风格 + WS_EX_WINDOWEDGE = 0x100, // 窗口具有凸起的3D边框 + WS_EX_CLIENTEDGE = 0x200, // 窗口具有阴影边界 + WS_EX_TOOLWINDOW = 0x80, // 小标题工具窗口 + WS_EX_TOPMOST = 0x8, // 窗口总在顶层 const int WS_EX_TOPMOST = 0x00000008; + WS_EX_OVERLAPPEDWINDOW = WS_EX_WINDOWEDGE | WS_EX_CLIENTEDGE, // WS_EX-CLIENTEDGE和WS_EX_WINDOWEDGE的组合 + WS_EX_PALETTEWINDOW = WS_EX_WINDOWEDGE | WS_EX_TOOLWINDOW | WS_EX_TOPMOST, // WS_EX_WINDOWEDGE和WS_EX_TOOLWINDOW和WS_EX_TOPMOST的组合 + WS_EX_DLGMODALFRAME = 0x1, // 带双边的窗口 + WS_EX_NOPARENTNOTIFY = 0x4, // 窗口在创建和销毁时不向父窗口发送WM_PARENTNOTIFY消息 + WS_EX_TRANSPARENT = 0x20, // 窗口透眀 + WS_EX_MDICHILD = 0x40, // MDI子窗口 + WS_EX_CONTEXTHELP = 0x400, // 标题栏包含问号联机帮助按钮 + WS_EX_RIGHT = 0x1000, // 窗口具有右对齐属性 + WS_EX_RTLREADING = 0x2000, // 窗口文本自右向左显示 + WS_EX_LEFTSCROLLBAR = 0x4000, // 标题栏在客户区的左边 + WS_EX_CONTROLPARENT = 0x10000, // 允许用户使用Tab键在窗口的子窗口间搜索 + WS_EX_STATICEDGE = 0x20000, // 为不接受用户输入的项创建一个三维边界风格 + WS_EX_APPWINDOW = 0x40000, // 在任务栏上显示顶层窗口的标题按钮 + WS_EX_LAYERED = 0x80000, // 窗口具有透眀属性(Win2000)以上 + WS_EX_NOINHERITLAYOUT = 0x100000, // 窗口布局不传递给子窗口(Win2000)以上 + WS_EX_LAYOUTRTL = 0x400000, // 水平起点在右边的窗口 + WS_EX_NOACTIVATE = 0x8000000, // 窗口不会变成前台窗口(Win2000)以上 + WS_EX_LEFT = 0x0, // 窗口具有左对齐属性 + WS_EX_LTRREADING = 0x0, // 窗口文本自左向右显示 + WS_EX_RIGHTSCROLLBAR = 0x0, // 垂直滚动条在窗口的右边界 + WS_EX_ACCEPTFILES = 0x10, // 接受文件拖曳 + WS_EX_COMPOSITED = 0x2000000, // 窗体所有子窗口使用双缓冲从低到高绘制(XP) +} + +public enum GWL : int +{ + /// + /// 获取、设置窗口过程的地址 + /// + GWL_WNDPROC = -4, + /// + /// 获取应用程序的实例句柄 + /// + GWL_HINSTANCE = -6, + /// + /// 获取父窗口句柄 + /// + GWL_HWNDPARENT = -8, + /// + /// 获取窗口标识 + /// + GWL_ID = -12, + /// + /// 获取、设置窗口样式 + /// + GWL_STYLE = -16, + /// + /// 获取、设置窗口扩展样式 + /// + GWL_EXSTYLE = -20, + /// + /// 获取、设置与窗口关联的自定义数据 + /// + GWL_USERDATA = -21, +} + +public enum GetWindowCmd : uint +{ + /// + /// 返回的句柄标识了在Z序最高端的相同类型的窗口。 + /// 如果指定窗口是最高端窗口,则该句柄标识了在Z序最高端的最高端窗口; + /// 如果指定窗口是顶层窗口,则该句柄标识了在z序最高端的顶层窗口: + /// 如果指定窗口是子窗口,则句柄标识了在Z序最高端的同属窗口。 + /// + GW_HWNDFIRST = 0, + /// + /// 返回的句柄标识了在z序最低端的相同类型的窗口。 + /// 如果指定窗口是最高端窗口,则该柄标识了在z序最低端的最高端窗口: + /// 如果指定窗口是顶层窗口,则该句柄标识了在z序最低端的顶层窗口; + /// 如果指定窗口是子窗口,则句柄标识了在Z序最低端的同属窗口。 + /// + GW_HWNDLAST = 1, + /// + /// 返回的句柄标识了在Z序中指定窗口下的相同类型的窗口。 + /// 如果指定窗口是最高端窗口,则该句柄标识了在指定窗口下的最高端窗口: + /// 如果指定窗口是顶层窗口,则该句柄标识了在指定窗口下的顶层窗口; + /// 如果指定窗口是子窗口,则句柄标识了在指定窗口下的同属窗口。 + /// + GW_HWNDNEXT = 2, + /// + /// 返回的句柄标识了在Z序中指定窗口上的相同类型的窗口。 + /// 如果指定窗口是最高端窗口,则该句柄标识了在指定窗口上的最高端窗口; + /// 如果指定窗口是顶层窗口,则该句柄标识了在指定窗口上的顶层窗口; + /// 如果指定窗口是子窗口,则句柄标识了在指定窗口上的同属窗口。 + /// + GW_HWNDPREV = 3, + /// + /// 返回的句柄标识了指定窗口的所有者窗口(如果存在)。 + /// GW_OWNER与GW_CHILD不是相对的参数,没有父窗口的含义,如果想得到父窗口请使用GetParent()。 + /// 例如:例如有时对话框的控件的GW_OWNER,是不存在的。 + /// + GW_OWNER = 4, + /// + /// 如果指定窗口是父窗口,则获得的是在Tab序顶端的子窗口的句柄,否则为NULL。 + /// 函数仅检查指定父窗口的子窗口,不检查继承窗口。 + /// + GW_CHILD = 5, + /// + /// (WindowsNT 5.0)返回的句柄标识了属于指定窗口的处于使能状态弹出式窗口(检索使用第一个由GW_HWNDNEXT 查找到的满足前述条件的窗口); + /// 如果无使能窗口,则获得的句柄与指定窗口相同。 + /// + GW_ENABLEDPOPUP = 6 +} +#endif \ No newline at end of file diff --git a/src/CAD/IFox.CAD.Shared/WindowsAPI/MouseHook.cs b/src/CAD/IFox.CAD.Shared/WindowsAPI/MouseHook.cs new file mode 100644 index 0000000..2d6b013 --- /dev/null +++ b/src/CAD/IFox.CAD.Shared/WindowsAPI/MouseHook.cs @@ -0,0 +1,293 @@ +namespace IFoxCAD.Cad; + +using System; +using System.Diagnostics; +using System.Drawing; +using System.Runtime.InteropServices; +using System.Windows.Forms; + +public class MouseHook +{ + /// + /// 鼠标按下事件 + /// + public event MouseEventHandler? MouseDown; + /// + /// 松开鼠标事件 + /// + public event MouseEventHandler? MouseUp; + /// + /// 鼠标移动事件 + /// + public event MouseEventHandler? MouseMove; + /// + /// 鼠标滚轮事件 + /// + public event MouseEventHandler? MouseWheel; + /// + /// 鼠标单击事件 + /// + public event EventHandler? Click; + /// + /// 鼠标双击事件 + /// + public event EventHandler? DoubleClick; + + + bool _isHookBreak = false; + /// + /// 否决本次输入:设置不向下回调 + /// + public void Vote() + { + _isHookBreak = true; + } + + /// 不要试图省略此变量,否则将会导致GC变量池满后释放
    + /// 提示:激活 CallbackOnCollectedDelegate 托管调试助手(MDA) + internal static WindowsAPI.CallBack? HookProc; + internal static IntPtr _NextHookProc;//挂载成功的标记 + public readonly Process Process; + + + [DllImport("user32.dll", EntryPoint = "GetDoubleClickTime")] + public extern static int GetDoubleClickTime(); + static readonly Stopwatch _watch = new(); + + /// + /// 安装鼠标钩子 + /// + /// 低级钩子超时时间 + public MouseHook(int setLowLevel = 25000) + { + _NextHookProc = IntPtr.Zero; + Process = Process.GetCurrentProcess(); + WindowsAPI.CheckLowLevelHooksTimeout(setLowLevel); + _watch.Start(); + } + + void UnHook() + { + if (_NextHookProc != IntPtr.Zero) + { + WindowsAPI.UnhookWindowsHookEx(_NextHookProc); + _NextHookProc = IntPtr.Zero; + } + } + + /// + /// 设置钩子 + /// + /// false进程钩子,true全局钩子 + public void SetHook(bool processHook = false) + { + UnHook(); + if (_NextHookProc != IntPtr.Zero) + return; + + if (processHook) + { + HookProc = (nCode, wParam, lParam) => { + if (nCode >= 0 && HookTask(nCode, wParam, lParam)) + return (IntPtr)1; + return WindowsAPI.CallNextHookEx(_NextHookProc, nCode, wParam, lParam); + }; + _NextHookProc = WindowsAPI.SetWindowsHookEx(HookType.WH_MOUSE, HookProc, + IntPtr.Zero, WindowsAPI.GetCurrentThreadId()); + } + else + { + var moduleHandle = WindowsAPI.GetModuleHandle(Process.MainModule.ModuleName); + HookProc = (nCode, wParam, lParam) => { + if (nCode >= 0 && HookTask(nCode, wParam, lParam)) + return (IntPtr)1; + return WindowsAPI.CallNextHookEx(_NextHookProc, nCode, wParam, lParam); + }; + _NextHookProc = WindowsAPI.SetWindowsHookEx(HookType.WH_MOUSE_LL, HookProc, + moduleHandle, 0); + } + } + + + + MouseButtons _button; + int _clickCount = 0; + bool _down = false; + bool _up = false; + bool _ck = false; + + /// + /// 钩子的消息处理 + /// + /// + /// + /// + /// false不终止回调,true终止回调 + bool HookTask(int nCode, int wParam, IntPtr lParam) + { + if (MouseDown is null + && MouseUp is null + && MouseMove is null + && MouseWheel is null + && Click is null + && DoubleClick is null) + return false; + + _button = MouseButtons.None; + _clickCount = 0; + _down = false; + _up = false; + _ck = false; + + switch ((WM)wParam) + { + case WM.WM_LBUTTONDOWN: + _button = MouseButtons.Left; + _clickCount = 1; + _down = true; + _ck = true; + break; + case WM.WM_LBUTTONUP: + _button = MouseButtons.Left; + _clickCount = 1; + _up = true; + break; + case WM.WM_LBUTTONDBLCLK: + _button = MouseButtons.Left; + _clickCount = 2; + _ck = true; + break; + case WM.WM_RBUTTONDOWN: + _button = MouseButtons.Right; + _clickCount = 1; + _down = true; + _ck = true; + break; + case WM.WM_RBUTTONUP: + _button = MouseButtons.Right; + _clickCount = 1; + _up = true; + break; + case WM.WM_RBUTTONDBLCLK: + _button = MouseButtons.Right; + _clickCount = 2; + _ck = true; + break; + case WM.WM_MBUTTONDOWN: + _button = MouseButtons.Middle; + _clickCount = 1; + _ck = true; + break; + case WM.WM_MOUSEWHEEL: + // 滚轮 + break; + case WM.WM_MOUSEMOVE: + // 移动 + // 假设想要限制鼠标在屏幕中的移动区域能够在此处设置 + // 后期须要考虑实际的x y的容差 + // if (!Screen.PrimaryScreen.Bounds.Contains(e.X, e.Y)) + // // return 1; + // if (button == MouseButtons.Left) + // { + // GetCursorPos(out POINT pt); + // // 防止频繁获取导致出错 + // if (pt0ld.Leng(pt) > 20) + // pt0ld = pt; + // } + break; + } + + // 从回调函数中得到鼠标的信息 + var mouseMsg = MouseHookStruct.Create(lParam); + MouseEventArgs e = new(_button, _clickCount, mouseMsg.Point.X, mouseMsg.Point.Y, 0); + if (_down) + MouseDown?.Invoke(this, e); + if (_up) + MouseUp?.Invoke(this, e); + if (_ck) + Click?.Invoke(this, e); + if (_clickCount == 2) + { + // 如果不用时间控制,那么双击会执行两次 + if (_watch.Elapsed.TotalMilliseconds > GetDoubleClickTime()) + { + DoubleClick?.Invoke(this, e); + _watch.Reset(); + _watch.Start(); + } + } + MouseMove?.Invoke(this, e); + MouseWheel?.Invoke(this, e); + + // 屏蔽此输入 + if (_isHookBreak) + return true; + + return false; + } + + + /// + /// Hook鼠标数据结构 + /// + [StructLayout(LayoutKind.Sequential)] + public struct MouseHookStruct + { + /// + /// 鼠标在屏幕上的x,y坐标 + /// + public Point Point; + /// + /// 点击窗体的句柄 + /// + public IntPtr hWnd; + /// + /// 消息 + /// + public int wHitTestCode; + /// + /// 扩展信息,可以使用GetMessageExtraInfo的返回值 + /// + public int dwExtraInfo; + + public static MouseHookStruct Create(IntPtr lParam) + { + return (MouseHookStruct)Marshal.PtrToStructure(lParam, typeof(MouseHookStruct)); + } + + public void ToPtr(IntPtr lParam) + { + Marshal.StructureToPtr(this, lParam, true); + } + } + + + #region IDisposable接口相关函数 + public bool IsDisposed { get; private set; } = false; + + /// + /// 手动调用释放 + /// + public void Dispose() + { + Dispose(true); + GC.SuppressFinalize(this); + } + + /// + /// 析构函数调用释放 + /// + ~MouseHook() + { + Dispose(false); + } + + protected virtual void Dispose(bool disposing) + { + // 不重复释放,并设置已经释放 + if (IsDisposed) return; + IsDisposed = true; + UnHook(); + } + #endregion +} \ No newline at end of file diff --git a/src/CAD/IFox.CAD.Shared/WindowsAPI/WindowsAPI.cs b/src/CAD/IFox.CAD.Shared/WindowsAPI/WindowsAPI.cs new file mode 100644 index 0000000..b1a7f92 --- /dev/null +++ b/src/CAD/IFox.CAD.Shared/WindowsAPI/WindowsAPI.cs @@ -0,0 +1,659 @@ +#define Marshal +using System.Diagnostics; +using System.Runtime.CompilerServices; +using System.Threading; + +namespace IFoxCAD.Cad; +public partial class WindowsAPI +{ + #region kernel32 + // https://blog.csdn.net/haelang/article/details/45147121 + [DllImport("kernel32.dll")] + public extern static uint GetLastError(); + + [DllImport("kernel32.dll")] + public static extern long GetHandleInformation(long hObject, ref long lpdwFlags); + + [DllImport("kernel32.dll")] + public static extern IntPtr GetModuleHandle(string ModuleName); + + [DllImport("kernel32.dll")] + public static extern int GetCurrentThreadId(); + + /// + /// 获取要引入的函数,将符号名或标识号转换为DLL内部地址 + /// + /// exe/dll句柄 + /// 接口名 + /// + [DllImport("kernel32.dll", SetLastError = true, CharSet = CharSet.Ansi, ExactSpelling = true)] + public static extern IntPtr GetProcAddress(IntPtr hModule, string procName); + + /// + /// 锁定内存 + /// + /// + /// + [DllImport("kernel32.dll", SetLastError = true)] + static extern IntPtr GlobalLock(IntPtr hMem); + /// + /// 解锁内存 + /// + /// + /// + [DllImport("kernel32.dll", SetLastError = true)] + static extern bool GlobalUnlock(IntPtr hMem); +#if !Marshal + /* + const int GMEM_MOVEABLE = 0x0002; + IntPtr newPtr = WindowsAPI.GlobalAlloc(GMEM_MOVEABLE, Marshal.SizeOf(structObj)); + */ + /// + /// 从堆中分配内存 + /// 被代替: Marshal.AllocHGlobal + /// + /// 分配方式 + /// 分配的字节数 + /// + [DllImport("kernel32.dll", SetLastError = true)] + public static extern IntPtr GlobalAlloc(uint uFlags, int dwBytes); + /// + /// 释放堆内存 + /// 被代替: Marshal.FreeHGlobal + /// + /// 产生的句柄 + /// + [DllImport("kernel32.dll", SetLastError = true)] + public static extern IntPtr GlobalFree(IntPtr hMem); +#endif + /// + /// 获取内存块大小 + /// + /// + /// + [DllImport("kernel32.dll", SetLastError = true)] + public static extern uint GlobalSize(IntPtr hMem); + + /// + /// 锁定和释放内存 + /// + /// 锁定数据对象指针 + /// 返回锁定的内存片段指针,锁定期间执行任务 + /// 是否锁定成功 + /// + public static bool GlobalLockTask(IntPtr data, Action task) + { + if (task == null) + throw new ArgumentNullException(nameof(task)); + if (data == IntPtr.Zero) + return false; + + try + { + var ptr = GlobalLock(data); + // 有几率导致无效锁定: + // 重复复制同一个图元时,第二次是 IntPtr.Zero, + // 第三次就又可以复制了 + if (ptr == IntPtr.Zero) + return false; + task.Invoke(ptr); + } + finally { GlobalUnlock(data); } + return true; + } + + /// + /// byte数组转结构体 + /// + /// byte数组 + /// 返回的结构大小 + /// 返回的结构体 + [Obsolete("效率太低", true)] + public static T? BytesToStruct(byte[] bytes, out int typeSize) + { + var structType = typeof(T); + typeSize = Marshal.SizeOf(structType); + if (typeSize > bytes.Length) + return default; + + // 安全写法效率太低了 + // 分配结构体大小的内存空间 + IntPtr structPtr = Marshal.AllocHGlobal(typeSize); + + // 将byte数组拷到分配好的内存空间 + Marshal.Copy(bytes, 0, structPtr, typeSize); + // 将内存空间转换为目标结构体; + // 转类型的时候会拷贝一次,看它们地址验证 &result != &structPtr + var result = (T)Marshal.PtrToStructure(structPtr, structType); + + // 释放内存空间 + Marshal.FreeHGlobal(structPtr); + return result; + } + + /// + /// byte数组转结构体 + /// + /// byte数组 + /// 返回的结构体 + [MethodImpl] + public static T? BytesToStruct(byte[] bytes) + { + T? result = default; + unsafe + { + // 安全指针方法 + // var pB = Marshal.UnsafeAddrOfPinnedArrayElement(bytes, 0); + // 不安全指针方法 + fixed (byte* pB = &bytes[0]) + { + result = (T?)Marshal.PtrToStructure(new IntPtr(pB), typeof(T)); + } + } + return result; + } + + /// + /// 结构体转byte数组 + /// unmanaged + /// + /// 要转换的结构体 + [MethodImpl] + public static byte[] StructToBytes(T structObj) where T : unmanaged/*非托管的T从来不为空*/ + { + // 得到结构体的大小 + var typeSize = Marshal.SizeOf(structObj); + // 从内存空间拷到byte数组 + var bytes = new byte[typeSize]; + unsafe + { + Marshal.Copy(new IntPtr(&structObj), bytes, 0, typeSize); + } +#if true20221030 + // 安全写法效率太低了 + StructToPtr(structObj, structPtr => { + Marshal.Copy(structPtr, bytes, 0, typeSize); + }); +#endif + return bytes; + } + +#if true20221030 + /// + /// 结构体转指针 + /// + /// 要转换的结构体 + /// 输出指针 + /// 释放申请的内存 + /// 是否锁定内存 + /// + public static void StructToPtr(T structObj, + Action? task = null, + bool freeHGlobal = true, + bool lockPrt = true) + { + IntPtr newPtr = Marshal.AllocHGlobal(Marshal.SizeOf(structObj)); + if (newPtr == IntPtr.Zero) + throw new ArgumentException(nameof(newPtr)); + + try + { + // 剪贴板写入的时候不允许锁定内存,否则在频繁触发剪贴板将导致卡死程序 + if (lockPrt) + { + GlobalLockTask(newPtr, ptr => { + // 将结构体拷到分配好的内存空间 + Marshal.StructureToPtr(structObj, newPtr, true); + task?.Invoke(newPtr); + }); + } + else + { + // 将结构体拷到分配好的内存空间 + Marshal.StructureToPtr(structObj, newPtr, true); + task?.Invoke(newPtr); + } + } + catch (Exception e) + { + Debugger.Break(); + Debugx.Printl(e.Message); + } + finally + { + if (freeHGlobal && newPtr != IntPtr.Zero) + Marshal.FreeHGlobal(newPtr); + } + } +#endif + #endregion +} + +public partial class WindowsAPI +{ + #region imm32 + /// + /// 获取输入法的虚拟键码 + /// + /// + /// + [DllImport("imm32.dll")] + public static extern IntPtr ImmGetVirtualKey(IntPtr hWnd); + /// + /// 获取输入法状态 + /// + /// 输入法标识符 + /// 输入模式 + /// 指向函数在其中检索句子模式值的变量的指针 + /// + [DllImport("imm32.dll")] + public static extern bool ImmGetConversionStatus(IntPtr himc, out int lpdw, out int lpdw2); + + /// + /// 获取指定窗口的输入法状态 + /// + /// 窗口句柄 + /// + [DllImport("imm32.dll")] + public static extern IntPtr ImmGetContext(IntPtr hwnd); + /// + /// 设置输入法的当前状态 + /// + /// 窗口句柄 + /// + /// + [DllImport("imm32.dll")] + public static extern bool ImmSetOpenStatus(IntPtr hwnd, bool fOpen); + /// + /// 输入法打开状态 + /// + /// + /// 非0打开,0关闭;(true中文,false英文) + [DllImport("imm32.dll")] + public static extern bool ImmGetOpenStatus(IntPtr hwnd); + #endregion +} + +public partial class WindowsAPI +{ + #region user32 + + /// + /// 获取窗口客户区的大小,客户区为窗口中除标题栏,菜单栏之外的地方 + /// + /// + /// + /// + [DllImport("user32.dll", CharSet = CharSet.Auto, EntryPoint = "GetClientRect")] + public static extern bool GetClientRect(IntPtr hwnd, out IntRect lpRect); + + /// + /// 查找主线程
    + /// 代替
    + /// 托管线程和他们不一样: + ///
    + /// 主窗口 + /// 进程ID + /// 线程ID + [DllImport("user32.dll", SetLastError = true)] + public static extern uint GetWindowThreadProcessId(IntPtr hWnd, out uint lpdwProcessId); + + /// + /// 设置焦点 + /// + /// + /// + [DllImport("user32.dll")] + public static extern IntPtr SetFocus(IntPtr hWnd); + + /// + /// 获取当前窗口 + /// + /// 当前窗口标识符 + [DllImport("user32.dll")] + public static extern IntPtr GetForegroundWindow(); + /// + /// 将一个消息的组成部分合成一个消息并放入对应线程消息队列的方法 + /// + /// 控件句柄 + /// 消息是什么。键盘按键、鼠标点击还是其他 + /// + /// + /// + [DllImport("user32.dll")] + public static extern bool PostMessage(IntPtr hhwnd, int msg, IntPtr wparam, IntPtr lparam); + /// + /// 发送击键 + /// + /// + /// + /// + /// + [DllImport("user32.dll", EntryPoint = "keybd_event")] + public static extern void KeybdEvent(byte bVk, byte bScan, int dwFlags, int dwExtraInfo); + /// + /// 获取窗口文字的长度 + /// + /// 窗口标识符 + /// 文字长度 + [DllImport("user32.dll")] + public static extern int GetWindowTextLength(IntPtr hWnd); + /// + /// 获取窗口的标题 + /// + /// 窗口标识符 + /// 窗口文字 + /// 文字长度 + /// + [DllImport("User32.dll", CharSet = CharSet.Auto)] + public static extern int GetWindowText(IntPtr hWnd, StringBuilder text, int nMaxCount); + + // [DllImport("user32.dll", CharSet = CharSet.Unicode, SetLastError = true)] + // internal static extern int GetWindowText(IntPtr hWnd, StringBuilder lpString, int nMaxCount); + + + /// + /// 获取某个线程的输入法布局 + /// + /// 线程ID + /// 布局码 + [DllImport("user32.dll")] + public static extern int GetKeyboardLayout(int threadid); + + + /// + /// 获取按键的当前状态 + /// + /// 按键虚拟代码 + /// 表示没按下>0;按下<0 + [DllImport("user32.dll")] + public static extern short GetKeyState(int nVirtKey); + /// + /// 检索指定窗口所属的类的名称。 + /// + /// 窗口标识符 + /// + /// + /// + [DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)] + public static extern int GetClassName(IntPtr hWnd, StringBuilder lpClassName, int nMaxCount); + + [DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)] + public static extern IntPtr GetWindow(IntPtr hWnd, uint uCmd); + + [DllImport("user32.DLL", CharSet = CharSet.Unicode, CallingConvention = CallingConvention.StdCall, SetLastError = true)] + public static extern IntPtr GetTopWindow(IntPtr hWnd); + + + /// + /// 获取线程对应的窗体信息 + /// + /// 线程 + /// + /// + [DllImport("user32.dll")] + [return: MarshalAs(UnmanagedType.Bool)] + static extern bool GetGUIThreadInfo(uint idThread, ref GuiThreadInfo lpgui); + + /// + /// 获取线程对应的窗体信息 + /// + [StructLayout(LayoutKind.Sequential)] + public struct GuiThreadInfo + { + public int cbSize; + public int flags; + public IntPtr hwndActive; + public IntPtr hwndFocus; + public IntPtr hwndCapture; + public IntPtr hwndMenuOwner; + public IntPtr hwndMoveSize; + public IntPtr hwndCaret; + public System.Drawing.Rectangle rcCaret; + + public static GuiThreadInfo Create(uint windowThreadProcessId) + { + if (windowThreadProcessId == 0) + throw new ArgumentNullException(nameof(windowThreadProcessId)); + + GuiThreadInfo gti = new(); + gti.cbSize = Marshal.SizeOf(gti); + GetGUIThreadInfo(windowThreadProcessId, ref gti); + return gti; + } + } + + [DllImport("user32.dll", SetLastError = true)] + public static extern IntPtr GetFocus(); + + [DllImport("user32.dll")] + public static extern IntPtr SendMessage(IntPtr hwnd, int msg, IntPtr wParam, IntPtr lParam); + + [DllImport("user32.dll", SetLastError = true)] + public static extern IntPtr GetParent(IntPtr hWnd); + + [DllImport("user32.dll")] + public static extern int ToAscii(int uVirtKey, int uScancode, byte[] lpdKeyState, byte[] lpwTransKey, int fuState); + + [DllImport("user32.dll", SetLastError = true)] + public static extern IntPtr GetActiveWindow(); + + [DllImport("user32.dll", SetLastError = true)] + public static extern long GetWindowThreadProcessId(IntPtr hwnd, ref int lpdwProcessId); + + [DllImport("user32.dll", SetLastError = true)] + public static extern bool IsIconic(int hWnd); + + [DllImport("user32.dll")] + public static extern bool IsWindowEnabled(IntPtr hWnd); + #endregion + + #region 键盘钩子 + public delegate IntPtr CallBack(int nCode, int wParam, IntPtr lParam); + [DllImport("user32.dll")] + public static extern IntPtr SetWindowsHookEx(HookType idHook, CallBack lpfn, IntPtr hmod, int dwThreadId); + [DllImport("user32.dll")] + public static extern IntPtr UnhookWindowsHookEx(IntPtr hHook); + [DllImport("user32.dll")] + public static extern IntPtr CallNextHookEx(IntPtr hHook, int ncode, int wParam, IntPtr lParam); + /// + /// Hook键盘数据结构 + /// + [ComVisible(true)] + [Serializable] + //[DebuggerDisplay("{DebuggerDisplay,nq}")] + //[DebuggerTypeProxy(typeof(KeyboardHookStruct))] + [StructLayout(LayoutKind.Sequential)] + public struct KeyboardHookStruct + { + public int VkCode; // 键码,该代码必须有一个价值的范围1至254 + public int ScanCode; // 指定的硬件扫描码的关键 + public int Flags; // 键标志 + public int Time; // 指定的时间戳记的这个讯息 + public int DwExtraInfo; // 指定额外信息相关的信息 + + public static KeyboardHookStruct Create(IntPtr lParam) + { + return (KeyboardHookStruct)Marshal.PtrToStructure(lParam, typeof(KeyboardHookStruct)); + } + public void ToPtr(IntPtr lParam) + { + Marshal.StructureToPtr(this, lParam, true); + } + } + /// + /// 注册表增加低级钩子超时处理,防止系统不允许, + /// 否则:偶发性出现 键盘钩子不能用了,而且退出时产生 1404 错误 + /// https://www.cnblogs.com/songr/p/5131655.html + /// + public static void CheckLowLevelHooksTimeout(int setLowLevel = 25000) + { + const string llh = "LowLevelHooksTimeout"; + using var registryKey = Registry.CurrentUser.OpenSubKey("Control Panel\\Desktop", true); + if ((int)registryKey.GetValue(llh, 0) < setLowLevel) + registryKey.SetValue(llh, setLowLevel, RegistryValueKind.DWord); + } + #endregion +} + +public partial class WindowsAPI +{ + [DllImport("user32.dll")] + public static extern bool GetWindowRect(IntPtr hwnd, ref IntRect lpRect); + + [ComVisible(true)] + [Serializable] + [StructLayout(LayoutKind.Sequential)] + [DebuggerDisplay("{DebuggerDisplay,nq}")] + [DebuggerTypeProxy(typeof(IntRect))] + public struct IntRect + { + [DebuggerBrowsable(DebuggerBrowsableState.Never)] + private string DebuggerDisplay => $"(Left:{_Left},Top:{_Top},Right:{_Right},Bottom:{_Bottom})"; + + int _Left; + int _Top; + int _Right; + int _Bottom; + public int Left => _Left; + public int Top => _Top; + public int Right => _Right; + public int Bottom => _Bottom; + public int Width => checked(Right - Left); + public int Height => checked(Bottom - Top); + + public IntRect(int left, int top, int right, int bottom) + { + _Left = left; + _Top = top; + _Right = right; + _Bottom = bottom; + } + + static readonly IntRect _Zero = new(0, 0, 0, 0); + public static IntRect Zero => _Zero; + + public override string ToString() => $"({_Left},{_Top},{_Right},{_Bottom})"; + + #region 重载运算符_比较 + public bool Equals(IntRect other) + { + return + _Left == other._Left && + _Top == other._Top && + _Right == other._Right && + _Bottom == other._Bottom; + } + public static bool operator !=(IntRect a, IntRect b) + { + return !(a == b); + } + public static bool operator ==(IntRect a, IntRect b) + { + return a.Equals(b); + } + public override bool Equals(object obj) + { + return obj is IntRect d && Equals(d); + } + public override int GetHashCode() + { + return ((_Left, _Top).GetHashCode(), _Right).GetHashCode() ^ _Bottom.GetHashCode(); + } + + public IntRect Clone() + { + return (IntRect)MemberwiseClone(); + } + #endregion + } + + [ComVisible(true)] + [Serializable] + [DebuggerDisplay("{DebuggerDisplay,nq}")] + [DebuggerTypeProxy(typeof(IntSize))] + [StructLayout(LayoutKind.Sequential)] + public struct IntSize + { + [DebuggerBrowsable(DebuggerBrowsableState.Never)] + private string DebuggerDisplay => $"(Hight:{Hight},Width:{Width})"; + public int Hight; + public int Width; + + public IntSize(int cx, int cy) + { + Hight = cx; + Width = cy; + } + public override string ToString() => $"({Hight},{Width})"; + } + + [ComVisible(true)] + [Serializable] + [DebuggerDisplay("{DebuggerDisplay,nq}")] + [DebuggerTypeProxy(typeof(Point3D))] + [StructLayout(LayoutKind.Sequential)] + public struct Point3D : IEquatable + { + [DebuggerBrowsable(DebuggerBrowsableState.Never)] + private string DebuggerDisplay => $"(X:{X},Y:{Y},Z:{Z})"; + + /* 由于此类是用来优化,从而实现字段修改,因此直接暴露字段减少栈帧 */ + public double X; + public double Y; + public double Z; + + public Point3D(double x, double y, double z) + { + X = x; + Y = y; + Z = z; + } + public static implicit operator Point3D(Point3d pt) + { + return new Point3D(pt.X, pt.Y, pt.Z); + } + public static implicit operator Point3d(Point3D pt) + { + return new Point3d(pt.X, pt.Y, pt.Z); + } + public override string ToString() => $"({X},{Y},{Z})"; + + public static Point3D Create(IntPtr lParam) + { + return (Point3D)Marshal.PtrToStructure(lParam, typeof(Point3D)); + } + + public void ToPtr(IntPtr lParam) + { + Marshal.StructureToPtr(this, lParam, true); + } + + + #region 重载运算符_比较 + public bool Equals(Point3D other) + { + return + X == other.X && + Y == other.Y && + Z == other.Z; + } + public static bool operator !=(Point3D a, Point3D b) + { + return !(a == b); + } + public static bool operator ==(Point3D a, Point3D b) + { + return a.Equals(b); + } + public override bool Equals(object obj) + { + return obj is Point3D d && Equals(d); + } + public override int GetHashCode() + { + return (X, Y).GetHashCode() ^ Z.GetHashCode(); + } + #endregion + } +} \ No newline at end of file diff --git a/src/CAD/IFox.CAD.Source/IFox.CAD.Source.csproj b/src/CAD/IFox.CAD.Source/IFox.CAD.Source.csproj new file mode 100644 index 0000000..4b1bc06 --- /dev/null +++ b/src/CAD/IFox.CAD.Source/IFox.CAD.Source.csproj @@ -0,0 +1,54 @@ + + + + + + netstandard1.0 + true + $(AssemblyName) + $(Version) + true + + + + CS8021 + true + false + contentFiles + true + false + false + true + + + + + + + + true + $(ContentTargetFolders)\cs\any\$(PackageId)\ + + + true + $(ContentTargetFolders)\any\any\$(PackageId)\ + + + + + + + false + + + + + + + + + + + + diff --git a/src/CAD/IFox.CAD.ZCAD/IFox.CAD.ZCAD.csproj b/src/CAD/IFox.CAD.ZCAD/IFox.CAD.ZCAD.csproj new file mode 100644 index 0000000..9f5c4f4 --- /dev/null +++ b/src/CAD/IFox.CAD.ZCAD/IFox.CAD.ZCAD.csproj @@ -0,0 +1,7 @@ + + + + netstandard2.0 + + + diff --git a/src/IFoxCAD.Cad/IFoxCAD.Aacad09plus/IFoxCAD.Acad09plus.csproj b/src/IFoxCAD.Cad/IFoxCAD.Aacad09plus/IFoxCAD.Acad09plus.csproj index 4e66292..1a76995 100644 --- a/src/IFoxCAD.Cad/IFoxCAD.Aacad09plus/IFoxCAD.Acad09plus.csproj +++ b/src/IFoxCAD.Cad/IFoxCAD.Aacad09plus/IFoxCAD.Acad09plus.csproj @@ -64,7 +64,7 @@ - + -- Gitee From 38c797bdbc14260784c6b33bb6ac810a1987995b Mon Sep 17 00:00:00 2001 From: vicwjb Date: Sat, 7 Jan 2023 23:45:31 +0800 Subject: [PATCH 630/675] =?UTF-8?q?=E8=B0=83=E6=95=B4cad=E7=9B=AE=E5=BD=95?= =?UTF-8?q?=EF=BC=8C=E5=88=A0=E9=99=A4ifoxcad.cad=E8=80=81=E9=A1=B9?= =?UTF-8?q?=E7=9B=AE?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- IFoxCAD.sln | 43 +- src/CAD/Directory.Build.props | 21 +- src/CAD/IFox.CAD.ACAD/IFox.CAD.ACAD.csproj | 26 +- .../IFox.CAD.GCAD}/GlobalUsings.cs | 0 src/CAD/IFox.CAD.GCAD/IFox.CAD.GCAD.csproj | 42 +- .../IFox.CAD.ZCAD}/GlobalUsings.cs | 0 src/CAD/IFox.CAD.ZCAD/IFox.CAD.ZCAD.csproj | 45 +- .../Algorithms/Graph/Graph.cs | 656 ------- .../Algorithms/Graph/IGraph.cs | 98 -- .../Algorithms/QuadTree/QuadEntity.cs | 25 - .../Algorithms/QuadTree/QuadTree.cs | 259 --- .../Algorithms/QuadTree/QuadTreeEvn.cs | 26 - .../Algorithms/QuadTree/QuadTreeNode.cs | 816 --------- .../Algorithms/QuadTree/QuadTreeSelectMode.cs | 21 - .../Algorithms/QuadTree/Rect.cs | 608 ------- src/IFoxCAD.Cad.Shared/Copyclip/BitmapTool.cs | 153 -- src/IFoxCAD.Cad.Shared/Copyclip/EmfTool.cs | 922 ---------- .../Copyclip/TagClipboardInfo.cs | 659 ------- .../ExtensionMethod/BulgeVertexWidth.cs | 85 - .../ExtensionMethod/CollectionEx.cs | 289 --- .../ExtensionMethod/Curve2dEx.cs | 296 ---- .../ExtensionMethod/Curve3dEx.cs | 556 ------ .../ExtensionMethod/CurveEx.cs | 723 -------- .../ExtensionMethod/DBDictionaryEx.cs | 373 ---- .../ExtensionMethod/DBObjectEx.cs | 153 -- .../ExtensionMethod/DBTransEx.cs | 91 - .../ExtensionMethod/EditorEx.cs | 1170 ------------ .../ExtensionMethod/EntityBoundingInfo.cs | 281 --- .../ExtensionMethod/EntityEx.cs | 474 ----- .../ExtensionMethod/Enums.cs | 174 -- .../ExtensionMethod/Filer/DwgFiler.cs | 506 ------ .../ExtensionMethod/Filer/DwgFilerEx.cs | 87 - .../ExtensionMethod/Filer/DxfFiler.cs | 221 --- .../ExtensionMethod/GeometryEx.cs | 671 ------- .../ExtensionMethod/JigEx.cs | 407 ----- .../ExtensionMethod/JigExTransient.cs | 149 -- .../ExtensionMethod/JsonConverter.cs | 116 -- .../ExtensionMethod/ObjEx.cs | 21 - .../ExtensionMethod/ObjectIdEx.cs | 98 -- .../ExtensionMethod/PointEx.cs | 138 -- .../ExtensionMethod/SelectionSetEx.cs | 150 -- .../ExtensionMethod/SymbolTableEx.cs | 358 ---- .../ExtensionMethod/SymbolTableRecordEx.cs | 521 ------ .../ExtensionMethod/Tools.cs | 60 - .../ExtensionMethod/XrefEx.cs | 656 ------- .../HatchConverter.cs" | 395 ----- .../HatchEx.cs" | 15 - .../HatchInfo.cs" | 353 ---- .../AttachmentPointHelper.cs" | 95 - .../TextEntityAdd.cs" | 62 - .../TextInfo.cs" | 178 -- .../IFoxCAD.Cad.Shared.projitems | 90 - .../IFoxCAD.Cad.Shared.shproj | 13 - .../ResultData/LispDottedPair.cs | 68 - src/IFoxCAD.Cad.Shared/ResultData/LispList.cs | 194 -- .../ResultData/TypedValueList.cs | 64 - .../ResultData/XRecordDataList.cs | 67 - .../ResultData/XdataList.cs | 146 -- src/IFoxCAD.Cad.Shared/Runtime/AOP.cs | 99 -- src/IFoxCAD.Cad.Shared/Runtime/AcadEMR.cs | 149 -- src/IFoxCAD.Cad.Shared/Runtime/AcadVersion.cs | 70 - src/IFoxCAD.Cad.Shared/Runtime/AssemInfo.cs | 85 - .../Runtime/AutoRegAssem.cs | 180 -- src/IFoxCAD.Cad.Shared/Runtime/CadVersion.cs | 92 - src/IFoxCAD.Cad.Shared/Runtime/DBTrans.cs | 711 -------- src/IFoxCAD.Cad.Shared/Runtime/Env.cs | 749 -------- .../Runtime/FileOpenMode.cs | 85 - src/IFoxCAD.Cad.Shared/Runtime/IAutoGo.cs | 403 ----- src/IFoxCAD.Cad.Shared/Runtime/LateBinding.cs | 83 - src/IFoxCAD.Cad.Shared/Runtime/Log.cs | 453 ----- .../Runtime/MethodInfoHelper.cs | 64 - .../Runtime/PE/AcadPeInfo.cs | 333 ---- src/IFoxCAD.Cad.Shared/Runtime/PE/DBmod.cs | 92 - src/IFoxCAD.Cad.Shared/Runtime/PE/PostCmd.cs | 199 --- .../Runtime/PE/ProgramPE.cs | 1567 ----------------- src/IFoxCAD.Cad.Shared/Runtime/RedrawEx.cs | 191 -- src/IFoxCAD.Cad.Shared/Runtime/SymbolTable.cs | 408 ----- src/IFoxCAD.Cad.Shared/Runtime/Utils.cs | 98 -- .../SelectionFilter/OpComp.cs | 79 - .../SelectionFilter/OpEqual.cs | 86 - .../SelectionFilter/OpFilter.cs | 341 ---- .../SelectionFilter/OpList.cs | 167 -- .../SelectionFilter/OpLogi.cs | 133 -- src/IFoxCAD.Cad.Shared/WindowsAPI/Enums.cs | 1071 ----------- .../WindowsAPI/MouseHook.cs | 293 --- .../WindowsAPI/WindowsAPI.cs | 659 ------- .../IFoxCAD.Aacad08/IFoxCAD.Acad08.csproj | 62 - .../IFoxCAD.Aacad09plus/GlobalUsings.cs | 49 - .../IFoxCAD.Acad09plus.csproj | 74 - .../IFoxCAD.Gcad/IFoxCAD.Gcad.csproj | 59 - .../IFoxCAD.Zcad/IFoxCAD.Zcad.csproj | 67 - tests/TestAcad08/TestAcad08.csproj | 5 - tests/TestAcad09plus/TestAcad09plus.csproj | 1 - 93 files changed, 105 insertions(+), 24136 deletions(-) rename src/{IFoxCAD.Cad/IFoxCAD.Gcad => CAD/IFox.CAD.GCAD}/GlobalUsings.cs (100%) rename src/{IFoxCAD.Cad/IFoxCAD.Zcad => CAD/IFox.CAD.ZCAD}/GlobalUsings.cs (100%) delete mode 100644 src/IFoxCAD.Cad.Shared/Algorithms/Graph/Graph.cs delete mode 100644 src/IFoxCAD.Cad.Shared/Algorithms/Graph/IGraph.cs delete mode 100644 src/IFoxCAD.Cad.Shared/Algorithms/QuadTree/QuadEntity.cs delete mode 100644 src/IFoxCAD.Cad.Shared/Algorithms/QuadTree/QuadTree.cs delete mode 100644 src/IFoxCAD.Cad.Shared/Algorithms/QuadTree/QuadTreeEvn.cs delete mode 100644 src/IFoxCAD.Cad.Shared/Algorithms/QuadTree/QuadTreeNode.cs delete mode 100644 src/IFoxCAD.Cad.Shared/Algorithms/QuadTree/QuadTreeSelectMode.cs delete mode 100644 src/IFoxCAD.Cad.Shared/Algorithms/QuadTree/Rect.cs delete mode 100644 src/IFoxCAD.Cad.Shared/Copyclip/BitmapTool.cs delete mode 100644 src/IFoxCAD.Cad.Shared/Copyclip/EmfTool.cs delete mode 100644 src/IFoxCAD.Cad.Shared/Copyclip/TagClipboardInfo.cs delete mode 100644 src/IFoxCAD.Cad.Shared/ExtensionMethod/BulgeVertexWidth.cs delete mode 100644 src/IFoxCAD.Cad.Shared/ExtensionMethod/CollectionEx.cs delete mode 100644 src/IFoxCAD.Cad.Shared/ExtensionMethod/Curve2dEx.cs delete mode 100644 src/IFoxCAD.Cad.Shared/ExtensionMethod/Curve3dEx.cs delete mode 100644 src/IFoxCAD.Cad.Shared/ExtensionMethod/CurveEx.cs delete mode 100644 src/IFoxCAD.Cad.Shared/ExtensionMethod/DBDictionaryEx.cs delete mode 100644 src/IFoxCAD.Cad.Shared/ExtensionMethod/DBObjectEx.cs delete mode 100644 src/IFoxCAD.Cad.Shared/ExtensionMethod/DBTransEx.cs delete mode 100644 src/IFoxCAD.Cad.Shared/ExtensionMethod/EditorEx.cs delete mode 100644 src/IFoxCAD.Cad.Shared/ExtensionMethod/EntityBoundingInfo.cs delete mode 100644 src/IFoxCAD.Cad.Shared/ExtensionMethod/EntityEx.cs delete mode 100644 src/IFoxCAD.Cad.Shared/ExtensionMethod/Enums.cs delete mode 100644 src/IFoxCAD.Cad.Shared/ExtensionMethod/Filer/DwgFiler.cs delete mode 100644 src/IFoxCAD.Cad.Shared/ExtensionMethod/Filer/DwgFilerEx.cs delete mode 100644 src/IFoxCAD.Cad.Shared/ExtensionMethod/Filer/DxfFiler.cs delete mode 100644 src/IFoxCAD.Cad.Shared/ExtensionMethod/GeometryEx.cs delete mode 100644 src/IFoxCAD.Cad.Shared/ExtensionMethod/JigEx.cs delete mode 100644 src/IFoxCAD.Cad.Shared/ExtensionMethod/JigExTransient.cs delete mode 100644 src/IFoxCAD.Cad.Shared/ExtensionMethod/JsonConverter.cs delete mode 100644 src/IFoxCAD.Cad.Shared/ExtensionMethod/ObjEx.cs delete mode 100644 src/IFoxCAD.Cad.Shared/ExtensionMethod/ObjectIdEx.cs delete mode 100644 src/IFoxCAD.Cad.Shared/ExtensionMethod/PointEx.cs delete mode 100644 src/IFoxCAD.Cad.Shared/ExtensionMethod/SelectionSetEx.cs delete mode 100644 src/IFoxCAD.Cad.Shared/ExtensionMethod/SymbolTableEx.cs delete mode 100644 src/IFoxCAD.Cad.Shared/ExtensionMethod/SymbolTableRecordEx.cs delete mode 100644 src/IFoxCAD.Cad.Shared/ExtensionMethod/Tools.cs delete mode 100644 src/IFoxCAD.Cad.Shared/ExtensionMethod/XrefEx.cs delete mode 100644 "src/IFoxCAD.Cad.Shared/ExtensionMethod/\346\226\260\345\273\272\345\241\253\345\205\205/HatchConverter.cs" delete mode 100644 "src/IFoxCAD.Cad.Shared/ExtensionMethod/\346\226\260\345\273\272\345\241\253\345\205\205/HatchEx.cs" delete mode 100644 "src/IFoxCAD.Cad.Shared/ExtensionMethod/\346\226\260\345\273\272\345\241\253\345\205\205/HatchInfo.cs" delete mode 100644 "src/IFoxCAD.Cad.Shared/ExtensionMethod/\346\226\260\345\273\272\346\226\207\345\255\227/AttachmentPointHelper.cs" delete mode 100644 "src/IFoxCAD.Cad.Shared/ExtensionMethod/\346\226\260\345\273\272\346\226\207\345\255\227/TextEntityAdd.cs" delete mode 100644 "src/IFoxCAD.Cad.Shared/ExtensionMethod/\346\226\260\345\273\272\346\226\207\345\255\227/TextInfo.cs" delete mode 100644 src/IFoxCAD.Cad.Shared/IFoxCAD.Cad.Shared.projitems delete mode 100644 src/IFoxCAD.Cad.Shared/IFoxCAD.Cad.Shared.shproj delete mode 100644 src/IFoxCAD.Cad.Shared/ResultData/LispDottedPair.cs delete mode 100644 src/IFoxCAD.Cad.Shared/ResultData/LispList.cs delete mode 100644 src/IFoxCAD.Cad.Shared/ResultData/TypedValueList.cs delete mode 100644 src/IFoxCAD.Cad.Shared/ResultData/XRecordDataList.cs delete mode 100644 src/IFoxCAD.Cad.Shared/ResultData/XdataList.cs delete mode 100644 src/IFoxCAD.Cad.Shared/Runtime/AOP.cs delete mode 100644 src/IFoxCAD.Cad.Shared/Runtime/AcadEMR.cs delete mode 100644 src/IFoxCAD.Cad.Shared/Runtime/AcadVersion.cs delete mode 100644 src/IFoxCAD.Cad.Shared/Runtime/AssemInfo.cs delete mode 100644 src/IFoxCAD.Cad.Shared/Runtime/AutoRegAssem.cs delete mode 100644 src/IFoxCAD.Cad.Shared/Runtime/CadVersion.cs delete mode 100644 src/IFoxCAD.Cad.Shared/Runtime/DBTrans.cs delete mode 100644 src/IFoxCAD.Cad.Shared/Runtime/Env.cs delete mode 100644 src/IFoxCAD.Cad.Shared/Runtime/FileOpenMode.cs delete mode 100644 src/IFoxCAD.Cad.Shared/Runtime/IAutoGo.cs delete mode 100644 src/IFoxCAD.Cad.Shared/Runtime/LateBinding.cs delete mode 100644 src/IFoxCAD.Cad.Shared/Runtime/Log.cs delete mode 100644 src/IFoxCAD.Cad.Shared/Runtime/MethodInfoHelper.cs delete mode 100644 src/IFoxCAD.Cad.Shared/Runtime/PE/AcadPeInfo.cs delete mode 100644 src/IFoxCAD.Cad.Shared/Runtime/PE/DBmod.cs delete mode 100644 src/IFoxCAD.Cad.Shared/Runtime/PE/PostCmd.cs delete mode 100644 src/IFoxCAD.Cad.Shared/Runtime/PE/ProgramPE.cs delete mode 100644 src/IFoxCAD.Cad.Shared/Runtime/RedrawEx.cs delete mode 100644 src/IFoxCAD.Cad.Shared/Runtime/SymbolTable.cs delete mode 100644 src/IFoxCAD.Cad.Shared/Runtime/Utils.cs delete mode 100644 src/IFoxCAD.Cad.Shared/SelectionFilter/OpComp.cs delete mode 100644 src/IFoxCAD.Cad.Shared/SelectionFilter/OpEqual.cs delete mode 100644 src/IFoxCAD.Cad.Shared/SelectionFilter/OpFilter.cs delete mode 100644 src/IFoxCAD.Cad.Shared/SelectionFilter/OpList.cs delete mode 100644 src/IFoxCAD.Cad.Shared/SelectionFilter/OpLogi.cs delete mode 100644 src/IFoxCAD.Cad.Shared/WindowsAPI/Enums.cs delete mode 100644 src/IFoxCAD.Cad.Shared/WindowsAPI/MouseHook.cs delete mode 100644 src/IFoxCAD.Cad.Shared/WindowsAPI/WindowsAPI.cs delete mode 100644 src/IFoxCAD.Cad/IFoxCAD.Aacad08/IFoxCAD.Acad08.csproj delete mode 100644 src/IFoxCAD.Cad/IFoxCAD.Aacad09plus/GlobalUsings.cs delete mode 100644 src/IFoxCAD.Cad/IFoxCAD.Aacad09plus/IFoxCAD.Acad09plus.csproj delete mode 100644 src/IFoxCAD.Cad/IFoxCAD.Gcad/IFoxCAD.Gcad.csproj delete mode 100644 src/IFoxCAD.Cad/IFoxCAD.Zcad/IFoxCAD.Zcad.csproj diff --git a/IFoxCAD.sln b/IFoxCAD.sln index 37b66a4..ac6aac5 100644 --- a/IFoxCAD.sln +++ b/IFoxCAD.sln @@ -16,14 +16,6 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "IFoxCAD.WPF", "src\IFoxCAD. EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "TestConsole", "tests\TestConsole\TestConsole.csproj", "{E2873F0B-CAD2-45E8-8FF0-C05C0C6AD955}" EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "IFoxCAD.Acad08", "src\IFoxCAD.Cad\IFoxCAD.Aacad08\IFoxCAD.Acad08.csproj", "{36F87D06-88B3-45E3-A2A8-0FC737B25428}" -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "IFox.Cad.Library", "IFox.Cad.Library", "{465C4E39-FBA2-417A-AB31-BDC01601F420}" -EndProject -Project("{D954291E-2A0B-460D-934E-DC6B0785DB48}") = "IFoxCAD.Cad.Shared", "src\IFoxCAD.Cad.Shared\IFoxCAD.Cad.Shared.shproj", "{82FB8450-B971-4E30-859F-4B2DDB81F590}" -EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "IFoxCAD.Acad09plus", "src\IFoxCAD.Cad\IFoxCAD.Aacad09plus\IFoxCAD.Acad09plus.csproj", "{CC46E4E2-90DF-48F5-B3D7-83FAFED58F2A}" -EndProject Project("{D954291E-2A0B-460D-934E-DC6B0785DB48}") = "TestShared", "tests\TestShared\TestShared.shproj", "{CED63D2D-0AF6-4831-806D-5E8E9B0D0A07}" EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "TestAcad08", "tests\TestAcad08\TestAcad08.csproj", "{8C573180-523D-427D-8030-CAA7D6B5EB6B}" @@ -32,10 +24,6 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "TestAcad09plus", "tests\Tes EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Test", "Test", "{C0BEFC15-6DF7-4636-BC76-4A0B88231470}" EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "IFoxCAD.Gcad", "src\IFoxCAD.Cad\IFoxCAD.Gcad\IFoxCAD.Gcad.csproj", "{D0CE63A9-2DBA-4263-9749-E8CD3EA0DB57}" -EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "IFoxCAD.Zcad", "src\IFoxCAD.Cad\IFoxCAD.Zcad\IFoxCAD.Zcad.csproj", "{751D15FC-B664-4B71-BB99-529E22EE1C4F}" -EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Basal", "Basal", "{1A5C27C1-FEF3-40A0-A1EE-3BC7BF2BD67A}" ProjectSection(SolutionItems) = preProject src\Basal\Directory.Build.props = src\Basal\Directory.Build.props @@ -76,16 +64,6 @@ Global {E2873F0B-CAD2-45E8-8FF0-C05C0C6AD955}.Debug|Any CPU.Build.0 = Debug|Any CPU {E2873F0B-CAD2-45E8-8FF0-C05C0C6AD955}.Release|Any CPU.ActiveCfg = Release|Any CPU {E2873F0B-CAD2-45E8-8FF0-C05C0C6AD955}.Release|Any CPU.Build.0 = Release|Any CPU - {36F87D06-88B3-45E3-A2A8-0FC737B25428}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {36F87D06-88B3-45E3-A2A8-0FC737B25428}.Debug|Any CPU.Build.0 = Debug|Any CPU - {36F87D06-88B3-45E3-A2A8-0FC737B25428}.Release|Any CPU.ActiveCfg = Release|Any CPU - {36F87D06-88B3-45E3-A2A8-0FC737B25428}.Release|Any CPU.Build.0 = Release|Any CPU - {82FB8450-B971-4E30-859F-4B2DDB81F590}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {82FB8450-B971-4E30-859F-4B2DDB81F590}.Release|Any CPU.ActiveCfg = Release|Any CPU - {CC46E4E2-90DF-48F5-B3D7-83FAFED58F2A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {CC46E4E2-90DF-48F5-B3D7-83FAFED58F2A}.Debug|Any CPU.Build.0 = Debug|Any CPU - {CC46E4E2-90DF-48F5-B3D7-83FAFED58F2A}.Release|Any CPU.ActiveCfg = Release|Any CPU - {CC46E4E2-90DF-48F5-B3D7-83FAFED58F2A}.Release|Any CPU.Build.0 = Release|Any CPU {8C573180-523D-427D-8030-CAA7D6B5EB6B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {8C573180-523D-427D-8030-CAA7D6B5EB6B}.Debug|Any CPU.Build.0 = Debug|Any CPU {8C573180-523D-427D-8030-CAA7D6B5EB6B}.Release|Any CPU.ActiveCfg = Release|Any CPU @@ -94,14 +72,6 @@ Global {5F478F68-19BC-4A3A-AF39-8728F8D396DD}.Debug|Any CPU.Build.0 = Debug|Any CPU {5F478F68-19BC-4A3A-AF39-8728F8D396DD}.Release|Any CPU.ActiveCfg = Release|Any CPU {5F478F68-19BC-4A3A-AF39-8728F8D396DD}.Release|Any CPU.Build.0 = Release|Any CPU - {D0CE63A9-2DBA-4263-9749-E8CD3EA0DB57}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {D0CE63A9-2DBA-4263-9749-E8CD3EA0DB57}.Debug|Any CPU.Build.0 = Debug|Any CPU - {D0CE63A9-2DBA-4263-9749-E8CD3EA0DB57}.Release|Any CPU.ActiveCfg = Release|Any CPU - {D0CE63A9-2DBA-4263-9749-E8CD3EA0DB57}.Release|Any CPU.Build.0 = Release|Any CPU - {751D15FC-B664-4B71-BB99-529E22EE1C4F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {751D15FC-B664-4B71-BB99-529E22EE1C4F}.Debug|Any CPU.Build.0 = Debug|Any CPU - {751D15FC-B664-4B71-BB99-529E22EE1C4F}.Release|Any CPU.ActiveCfg = Release|Any CPU - {751D15FC-B664-4B71-BB99-529E22EE1C4F}.Release|Any CPU.Build.0 = Release|Any CPU {FF4E1CDE-EEB3-4BE8-8C1D-6B5B17A299C7}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {FF4E1CDE-EEB3-4BE8-8C1D-6B5B17A299C7}.Debug|Any CPU.Build.0 = Debug|Any CPU {FF4E1CDE-EEB3-4BE8-8C1D-6B5B17A299C7}.Release|Any CPU.ActiveCfg = Release|Any CPU @@ -132,14 +102,9 @@ Global EndGlobalSection GlobalSection(NestedProjects) = preSolution {E2873F0B-CAD2-45E8-8FF0-C05C0C6AD955} = {C0BEFC15-6DF7-4636-BC76-4A0B88231470} - {36F87D06-88B3-45E3-A2A8-0FC737B25428} = {465C4E39-FBA2-417A-AB31-BDC01601F420} - {82FB8450-B971-4E30-859F-4B2DDB81F590} = {465C4E39-FBA2-417A-AB31-BDC01601F420} - {CC46E4E2-90DF-48F5-B3D7-83FAFED58F2A} = {465C4E39-FBA2-417A-AB31-BDC01601F420} {CED63D2D-0AF6-4831-806D-5E8E9B0D0A07} = {C0BEFC15-6DF7-4636-BC76-4A0B88231470} {8C573180-523D-427D-8030-CAA7D6B5EB6B} = {C0BEFC15-6DF7-4636-BC76-4A0B88231470} {5F478F68-19BC-4A3A-AF39-8728F8D396DD} = {C0BEFC15-6DF7-4636-BC76-4A0B88231470} - {D0CE63A9-2DBA-4263-9749-E8CD3EA0DB57} = {465C4E39-FBA2-417A-AB31-BDC01601F420} - {751D15FC-B664-4B71-BB99-529E22EE1C4F} = {465C4E39-FBA2-417A-AB31-BDC01601F420} {FF4E1CDE-EEB3-4BE8-8C1D-6B5B17A299C7} = {1A5C27C1-FEF3-40A0-A1EE-3BC7BF2BD67A} {C823514A-2BC2-45C2-ACED-18924A7B80BF} = {1A5C27C1-FEF3-40A0-A1EE-3BC7BF2BD67A} {D0C6824C-53A3-4C16-AE7B-3227F18C5039} = {1A5C27C1-FEF3-40A0-A1EE-3BC7BF2BD67A} @@ -154,17 +119,15 @@ Global EndGlobalSection GlobalSection(SharedMSBuildProjectFiles) = preSolution src\CAD\IFox.CAD.Shared\IFox.CAD.Shared.projitems*{20f254f7-aee5-42ae-a9b3-149bbdc7397f}*SharedItemsImports = 13 - src\IFoxCAD.Cad.Shared\IFoxCAD.Cad.Shared.projitems*{36f87d06-88b3-45e3-a2a8-0fc737b25428}*SharedItemsImports = 5 + src\CAD\IFox.CAD.Shared\IFox.CAD.Shared.projitems*{3d7d095d-ef0a-40b3-9776-5f08c61fd614}*SharedItemsImports = 5 tests\TestShared\TestShared.projitems*{5f478f68-19bc-4a3a-af39-8728f8d396dd}*SharedItemsImports = 5 - src\IFoxCAD.Cad.Shared\IFoxCAD.Cad.Shared.projitems*{751d15fc-b664-4b71-bb99-529e22ee1c4f}*SharedItemsImports = 5 - src\IFoxCAD.Cad.Shared\IFoxCAD.Cad.Shared.projitems*{82fb8450-b971-4e30-859f-4b2ddb81f590}*SharedItemsImports = 13 src\CAD\IFox.CAD.Shared\IFox.CAD.Shared.projitems*{88f3cf78-23f5-494b-9d8f-2c6b4467e0b4}*SharedItemsImports = 5 tests\TestShared\TestShared.projitems*{8c573180-523d-427d-8030-caa7d6b5eb6b}*SharedItemsImports = 5 src\Basal\IFox.Basal.Shared\IFox.Basal.Shared.projitems*{c823514a-2bc2-45c2-aced-18924a7b80bf}*SharedItemsImports = 13 - src\IFoxCAD.Cad.Shared\IFoxCAD.Cad.Shared.projitems*{cc46e4e2-90df-48f5-b3d7-83fafed58f2a}*SharedItemsImports = 5 tests\TestShared\TestShared.projitems*{ced63d2d-0af6-4831-806d-5e8e9b0d0a07}*SharedItemsImports = 13 src\Basal\IFox.Basal.Shared\IFox.Basal.Shared.projitems*{d0c6824c-53a3-4c16-ae7b-3227f18c5039}*SharedItemsImports = 5 - src\IFoxCAD.Cad.Shared\IFoxCAD.Cad.Shared.projitems*{d0ce63a9-2dba-4263-9749-e8cd3ea0db57}*SharedItemsImports = 5 + src\CAD\IFox.CAD.Shared\IFox.CAD.Shared.projitems*{ef20e632-70a9-40eb-8c9a-539fd85fafad}*SharedItemsImports = 5 + src\CAD\IFox.CAD.Shared\IFox.CAD.Shared.projitems*{f388f4b9-f636-414a-9bc9-2b008517b441}*SharedItemsImports = 5 src\Basal\IFox.Basal.Shared\IFox.Basal.Shared.projitems*{ff4e1cde-eeb3-4be8-8c1d-6b5b17a299c7}*SharedItemsImports = 5 EndGlobalSection EndGlobal diff --git a/src/CAD/Directory.Build.props b/src/CAD/Directory.Build.props index 9c6e57f..104a9e3 100644 --- a/src/CAD/Directory.Build.props +++ b/src/CAD/Directory.Build.props @@ -1,14 +1,22 @@  - - + + 0.5.0-alpha4 支持源码包。 - - preview - enable + + preview + enable true + $(MSBuildThisFileDirectory)bin\$(Configuration)\ + true + true + True + + + + InspireFunction xsfhlzh;vicwjb;liuqihong InspireFunction @@ -19,8 +27,7 @@ https://gitee.com/inspirefunction/ifoxcad.git git IFoxCAD;CAD;AutoCad;C#;NET;GStarCAD;ZWCAD - $(MSBuildThisFileDirectory)bin\$(Configuration)\ - + \ No newline at end of file diff --git a/src/CAD/IFox.CAD.ACAD/IFox.CAD.ACAD.csproj b/src/CAD/IFox.CAD.ACAD/IFox.CAD.ACAD.csproj index e95415e..d53c17d 100644 --- a/src/CAD/IFox.CAD.ACAD/IFox.CAD.ACAD.csproj +++ b/src/CAD/IFox.CAD.ACAD/IFox.CAD.ACAD.csproj @@ -1,22 +1,10 @@ - + NET35;NET40;NET45 - - - - - - true - true - - - - - True - + true + true @@ -50,13 +38,17 @@ + + + + - + diff --git a/src/IFoxCAD.Cad/IFoxCAD.Gcad/GlobalUsings.cs b/src/CAD/IFox.CAD.GCAD/GlobalUsings.cs similarity index 100% rename from src/IFoxCAD.Cad/IFoxCAD.Gcad/GlobalUsings.cs rename to src/CAD/IFox.CAD.GCAD/GlobalUsings.cs diff --git a/src/CAD/IFox.CAD.GCAD/IFox.CAD.GCAD.csproj b/src/CAD/IFox.CAD.GCAD/IFox.CAD.GCAD.csproj index 9f5c4f4..2c06cff 100644 --- a/src/CAD/IFox.CAD.GCAD/IFox.CAD.GCAD.csproj +++ b/src/CAD/IFox.CAD.GCAD/IFox.CAD.GCAD.csproj @@ -1,7 +1,41 @@ - + - - netstandard2.0 - + + NET48 + true + true + + + + + DEBUG + + + $(Configuration);gcad + + + + + runtime + + + + + + True + + + + + + + + + + + + + + diff --git a/src/IFoxCAD.Cad/IFoxCAD.Zcad/GlobalUsings.cs b/src/CAD/IFox.CAD.ZCAD/GlobalUsings.cs similarity index 100% rename from src/IFoxCAD.Cad/IFoxCAD.Zcad/GlobalUsings.cs rename to src/CAD/IFox.CAD.ZCAD/GlobalUsings.cs diff --git a/src/CAD/IFox.CAD.ZCAD/IFox.CAD.ZCAD.csproj b/src/CAD/IFox.CAD.ZCAD/IFox.CAD.ZCAD.csproj index 9f5c4f4..8f243fb 100644 --- a/src/CAD/IFox.CAD.ZCAD/IFox.CAD.ZCAD.csproj +++ b/src/CAD/IFox.CAD.ZCAD/IFox.CAD.ZCAD.csproj @@ -1,7 +1,44 @@ - + - - netstandard2.0 - + + NET48 + + true + true + + + + DEBUG + + + $(Configuration);zcad + + + + runtime + + + + + + True + + + + + + + + + + + + + + + + + + diff --git a/src/IFoxCAD.Cad.Shared/Algorithms/Graph/Graph.cs b/src/IFoxCAD.Cad.Shared/Algorithms/Graph/Graph.cs deleted file mode 100644 index 255d2b3..0000000 --- a/src/IFoxCAD.Cad.Shared/Algorithms/Graph/Graph.cs +++ /dev/null @@ -1,656 +0,0 @@ -namespace IFoxCAD.Cad; -using Exception = System.Exception; - -/// -/// 无权无向图实现 -/// IEnumerable 枚举所有顶点; -/// -public sealed class Graph : IGraph, IEnumerable -{ - #region 字段及属性 - /// - /// 存储所有节点的字典,key为顶点的类型,value为邻接表,类型是hashset,不可重复添加点 - /// - /// - readonly Dictionary> vertices = new(); - /// - /// 邻接边表,key为顶点的类型,value为邻接边表,类型是hashset,不可重复添加边 - /// - readonly Dictionary> edges = new(); - /// - /// 为加快索引,引入hash检索 - /// - readonly Dictionary vertexs = new(); - - public int VerticesCount => vertices.Count; - - /// - /// Returns a reference vertex. - /// Time complexity: O(1). - /// - private IGraphVertex? ReferenceVertex - { - get - { - using (var enumerator = vertexs.GetEnumerator()) - { - if (enumerator.MoveNext()) - { - return enumerator.Current.Value; - } - } - - return null; - } - } - IGraphVertex? IGraph.ReferenceVertex => ReferenceVertex; - /// - /// 目前点增加点的顺序号,这个点号不随删点而减少的 - /// - private int insertCount; - #endregion - - #region 构造函数 - public Graph() - { - insertCount = 0; // 每次新建对象就将顶点顺序号归零 - } - #endregion - - #region 顶点及边_增 - /// - /// 向该图添加一个新顶点,但是无边; - /// - /// 点 - /// 创建的顶点 - public IGraphVertex AddVertex(Point3d pt) - { - var str = pt.GetHashString(); - if (vertexs.ContainsKey(str)) - return vertexs[str]; - - var vertex = new GraphVertex(pt, insertCount++); - vertices.Add(vertex, new HashSet()); - edges.Add(vertex, new HashSet()); - - vertexs[str] = vertex; - - return vertex; - } - - /// - /// 向该图添加一个边; - /// - /// - public void AddEdge(Curve3d curve) - { - if (curve == null) - throw new ArgumentNullException(nameof(curve)); - - var start = AddVertex(curve.StartPoint); - var end = AddVertex(curve.EndPoint); - - // 添加起点的邻接表和邻接边 - vertices[start].Add(end); - edges[start].Add(new GraphEdge(end, curve)); - - // 为了保证点顺序,每个点的邻接边必须按起点-终点,所以添加曲线终点时,将添加一个方向的曲线 - var curtmp = (Curve3d)curve.Clone(); - curtmp = curtmp.GetReverseParameterCurve(); - - // 添加终点的邻接表和邻接边 - vertices[end].Add(start); - edges[end].Add(new GraphEdge(start, curtmp)); - } - #endregion - - #region 顶点及边_删 - /// - /// 从此图中删除现有顶点; - /// - /// 点 - public void RemoveVertex(Point3d pt) - { - var str = pt.GetHashString(); - if (vertexs.ContainsKey(str)) - { - var vertex = vertexs[str]; - - // 删除邻接表里的vertex点,先删除后面的遍历可以少一轮 - vertices.Remove(vertex!); - - // 删除其他顶点的邻接表里的vertex点 - foreach (var item in vertices.Values) - item.Remove(vertex!); - - // 删除邻接边表里的vertex点,先删除后面的遍历可以少一轮 - edges.Remove(vertex!); - - // 删除其他顶点的邻接边表的指向vertex的边 - foreach (var item in edges.Values) - { - item.RemoveWhere(x => vertex.Equals(x.TargetVertex)); - // foreach (var edge in item) - // { - // if (vertex.Equals(edge.TargetVertex)) - // item.Remove(edge); - // } - } - vertexs.Remove(str); - } - } - - /// - /// 从此图中删除一条边; - /// - /// 曲线 - public void RemoveEdge(Curve3d curve) - { - if (curve == null) - throw new ArgumentNullException(nameof(curve)); - - RemoveVertex(curve.StartPoint); - RemoveVertex(curve.EndPoint); - } - #endregion - - #region 顶点和边_查 - /// - /// 我们在给定的来源和目的地之间是否有边? - /// - /// 起点 - /// 终点 - /// 有边返回 ,反之返回 - public bool HasEdge(IGraphVertex source, IGraphVertex dest) - { - if (!vertices.ContainsKey(source) || !vertices.ContainsKey(dest)) - throw new ArgumentException("源或目标不在此图中;"); - - foreach (var item in edges[source]) - { - if (item.TargetVertex == dest) - return true; - } - return false; - } - - /// - /// 获取边 - /// - /// 起点 - /// 终点 - /// - /// 传入的点不在图中时抛出参数异常 - public IEdge? GetEdge(IGraphVertex source, IGraphVertex dest) - { - if (!vertices.ContainsKey(source) || !vertices.ContainsKey(dest)) - throw new ArgumentException("源或目标不在此图中;"); - - foreach (var item in edges[source]) - { - if (item.TargetVertex.Equals(dest)) - return item; - } - return null; - } - - /// - /// 是否存在顶点,此函数目前未发现有啥用 - /// - /// 顶点 - /// 存在顶点返回 ,反之返回 - public bool ContainsVertex(IGraphVertex value) - { - return vertices.ContainsKey(value); - } - #endregion - - #region 获取邻接表和曲线 - /// - /// 获取顶点的邻接表 - /// - /// 顶点 - /// 邻接表 - public HashSet GetAdjacencyList(IGraphVertex vertex) - { - return vertices[vertex]; - } - - /// - /// 获取顶点的邻接边表 - /// - /// 顶点 - /// 邻接边表 - public HashSet GetAdjacencyEdge(IGraphVertex vertex) - { - return edges[vertex]; - } - - /// - /// 根据顶点表获取曲线集合 - /// - /// 顶点表 - /// 曲线表 - public List GetCurves(List graphVertices) - { - var curves = new List(); - for (int i = 0; i < graphVertices.Count - 1; i++) - { - var cur = graphVertices[i]; - var next = graphVertices[i + 1]; - var edge = GetEdge(cur, next); - if (edge is not null) - curves.Add(edge.TargetEdge); - } - var lastedge = GetEdge(graphVertices[^1], graphVertices[0]); - if (lastedge is not null) - curves.Add(lastedge.TargetEdge); - - return curves; - } - #endregion - - #region 克隆及接口实现 - /// - /// 克隆此图;目测是深克隆 - /// - [System.Diagnostics.DebuggerStepThrough] - public Graph Clone() - { - var newGraph = new Graph(); - - foreach (var vertex in edges.Values) - foreach (var item in vertex) - newGraph.AddEdge(item.TargetEdge); - - return newGraph; - } - - IGraph IGraph.Clone() - { - return Clone(); - } - - [System.Diagnostics.DebuggerStepThrough] - public IEnumerator GetEnumerator() - { - return VerticesAsEnumberable.GetEnumerator(); - } - - [System.Diagnostics.DebuggerStepThrough] - IEnumerator? IEnumerable.GetEnumerator() - { - return GetEnumerator() as IEnumerator; - } - - public IEnumerable VerticesAsEnumberable => - vertices.Select(x => x.Key); - #endregion - - #region 方法 - /// - /// 输出点的邻接表的可读字符串 - /// - /// - public string ToReadable() - { - int i = 1; - string output = string.Empty; - foreach (var node in vertices) - { - var adjacents = string.Empty; - - output = string.Format("{1}\r\n{0}-{2}: [", i, output, node.Key.Data.ToString()); - - foreach (var adjacentNode in node.Value) - adjacents = string.Format("{0}{1},", adjacents, adjacentNode.Data.ToString()); - - if (adjacents.Length > 0) - adjacents = adjacents.TrimEnd(new char[] { ',', ' ' }); - - output = string.Format("{0}{1}]", output, adjacents); - i++; - } - return output; - } - #endregion -} - - -/// -/// 邻接表图实现的顶点; -/// IEnumerable 枚举所有邻接点; -/// -public sealed class GraphVertex : IGraphVertex, IEquatable, IComparable, IComparable -{ - #region 属性 - public Point3d Data { get; set; } - public int Index { get; set; } - #endregion - - #region 构造 - /// - /// 邻接表图实现的顶点 - /// - /// 点 - /// 所在节点索引 - public GraphVertex(Point3d value, int index) - { - Data = value; - Index = index; - } - #endregion - - #region 重载运算符_比较 - public bool Equals(IGraphVertex other) - { - return Index == other.Index; - } - - public override bool Equals(object obj) - { - if (obj is null) - return false; - if (obj is not IGraphVertex vertex) - return false; - else - return Equals(vertex); - } - - public override int GetHashCode() - { - return Index; - } - - public int CompareTo(IGraphVertex other) - { - if (Equals(other)) - return 0; - - if (Index < other.Index) - return -1; - else - return 1; - } - - int IComparable.CompareTo(IGraphVertex other) - { - return CompareTo(other); - } - - public int CompareTo(object obj) - { - if (obj is null) - return 1; - - try - { - var other = (GraphVertex)obj; - return CompareTo(other); - } - catch (Exception) - { - throw new ArgumentException("Object is not a IGraphVertex"); - } - } - - public static bool operator ==(GraphVertex person1, GraphVertex person2) - { - if (person1 is null || person2 is null) - return Equals(person1, person2); - - return person1.Equals(person2); - } - - public static bool operator !=(GraphVertex person1, GraphVertex person2) - { - if (person1 is null || person2 is null) - return !Equals(person1, person2); - - return !person1.Equals(person2); - } - #endregion -} - - -/// -/// 无向图中边的定义 -/// -public sealed class GraphEdge : IEdge, IEquatable -{ - #region 属性 - public IGraphVertex TargetVertex { get; set; } - public Curve3d TargetEdge { get; set; } - #endregion - - #region 构造 - /// - /// 无向图中边的定义 - /// - /// 下一点 - /// 下一点之间的曲线 - public GraphEdge(IGraphVertex target, Curve3d edge) - { - TargetVertex = target; - TargetEdge = edge; - } - #endregion - - #region 重载运算符_比较 - public bool Equals(GraphEdge other) - { - if (other is null) - return false; - return TargetVertex == other.TargetVertex && - TargetEdge == other.TargetEdge; - } - public override bool Equals(object obj) - { - if (obj is null) - return false; - if (obj is not GraphEdge personObj) - return false; - else - return Equals(personObj); - } - - public override int GetHashCode() - { - return (TargetVertex.GetHashCode(), TargetEdge.GetHashCode()).GetHashCode(); - } - public static bool operator ==(GraphEdge person1, GraphEdge person2) - { - if (person1 is null || person2 is null) - return Equals(person1, person2); - - return person1.Equals(person2); - } - public static bool operator !=(GraphEdge person1, GraphEdge person2) - { - if (person1 is null || person2 is null) - return !Equals(person1, person2); - - return !person1.Equals(person2); - } - #endregion -} - - -/// -/// 深度优先搜索; -/// -public sealed class DepthFirst -{ - #region 公共方法 - /// - /// 存储所有的边 - /// -#if true - public List> Curve3ds { get; } = new(); -#else - public List> Curve3ds { get; } = new(); -#endif - private HashSet Curved { get; } = new(); - - - /// - /// 找出所有的路径 - /// - /// 图 - public void FindAll(IGraph graph) - { - var total = new HashSet(); - // var graphtmp = graph.Clone(); - foreach (var item in graph.VerticesAsEnumberable) - { - Dfs(graph, new LinkedHashSet { item }, total); - total.Add(item); - } - } - #endregion - - #region 内部方法 - /// - /// 递归 DFS; - /// - /// 图 - /// 已经遍历的路径 -#if true - void Dfs(IGraph graph, LinkedHashSet visited, HashSet totalVisited) - { - var adjlist = graph.GetAdjacencyList(/*startNode*/ visited.First!.Value); // O(1) - foreach (var nextNode in adjlist) // O(n) - { - if (totalVisited.Contains(nextNode)) - { - continue; - } - // 如果下一个点未遍历过 - if (!visited.Contains(nextNode)) // O(1) - { - // 将下一点加入路径集合,并进行下一次递归 - var sub = new LinkedHashSet { nextNode }; - sub.AddRange(visited); // O(n) - Dfs(graph, sub, totalVisited); - } - // 如果下一点遍历过,并且路径大于2,说明已经找到起点 - else if (visited.Count > 2 && nextNode.Equals(visited.Last!.Value)) - { - // 将重复的路径进行过滤,并把新的路径存入结果 - var curstr = Gethashstring(visited); // O(n) - if (Isnew(curstr)) // O(1) - { - Curve3ds.Add(visited); - Curved.Add(curstr.Item1); - } - } - } - } - - - - -#else - - void Dfs(IGraph graph, List visited) - { - var startNode = visited[0]; - IGraphVertex nextNode; - List sub; - - var adjlist = graph.GetAdjacencyList(startNode).ToList(); // O(n) - for (int i = 0; i < adjlist.Count; i++) // O(n) - { - nextNode = adjlist[i]; - - // 如果下一个点未遍历过 - if (!visited.Contains(nextNode)) // O(n) - { - // 将下一点加入路径集合,并进行下一次递归 - sub = new List { nextNode }; - sub.AddRange(visited); // O(n) - Dfs(graph, sub); - } - - // 如果下一点遍历过,并且路径大于2,说明已经找到起点 - else if (visited.Count > 2 && nextNode.Equals(visited[^1])) - { - // 将重复的路径进行过滤,并把新的路径存入结果 - var cur = RotateToSmallest(visited); // O(n) - var inv = Invert(cur,cur[0]); // O(n) - - var curstr = Gethashstring(cur,inv); - // Env.Print(curstr); - if (Isnew(curstr)) - { - Curve3ds.Add(cur); - Curved.Add(curstr.Item1); - } - } - } - } -#endif - - - - - - - - - /// - /// 将列表旋转到最小的值为列表起点 - /// - /// - /// - static List RotateToSmallest(List lst) - { - var index = lst.IndexOf(lst.Min()); - return lst.Skip(index).Concat(lst.Take(index)).ToList(); - } - - /// - /// 将列表反向,并旋转到起点为最小值 - /// - /// - /// - static List Invert(List lst, IGraphVertex vertex) - { - var tmp = lst.ToList(); - tmp.Reverse(); - var index = tmp.IndexOf(vertex); - return tmp.Skip(index).Concat(lst.Take(index)).ToList(); - } - - static (string, string) Gethashstring(List pathone, List pathtwo) - { - var one = new string[pathone.Count]; - var two = new string[pathtwo.Count]; - for (int i = 0; i < pathone.Count; i++) - { - one[i] = pathone[i].Index.ToString(); - two[i] = pathtwo[i].Index.ToString(); - } - return (string.Join("-", one), string.Join("-", two)); - } - - static (string, string) Gethashstring(LinkedHashSet path) - { - var one = new string[path.Count]; - var two = new string[path.Count]; - path.For(path.MinNode!, (i, ver1, ver2) => { - one[i] = ver1.Index.ToString(); - two[i] = ver2.Index.ToString(); - }); - return (string.Join("-", one), string.Join("-", two)); - } - - - bool Isnew((string, string) path) - { - return !Curved.Contains(path.Item1) && !Curved.Contains(path.Item2); - } - - - #endregion -} \ No newline at end of file diff --git a/src/IFoxCAD.Cad.Shared/Algorithms/Graph/IGraph.cs b/src/IFoxCAD.Cad.Shared/Algorithms/Graph/IGraph.cs deleted file mode 100644 index 73547b7..0000000 --- a/src/IFoxCAD.Cad.Shared/Algorithms/Graph/IGraph.cs +++ /dev/null @@ -1,98 +0,0 @@ -namespace IFoxCAD.Cad; - -/// -/// 无向图 -/// -public interface IGraph -{ - /// - /// 顶点的数量 - /// - /// - int VerticesCount { get; } - - /// - /// 是否存在顶点 - /// - /// 顶点键 - /// - bool ContainsVertex(IGraphVertex key); - - /// - /// 顶点的迭代器 - /// - /// - IEnumerable VerticesAsEnumberable { get; } - - /// - /// 是否有边 - /// - /// 源顶点 - /// 目的顶点 - /// - bool HasEdge(IGraphVertex source, IGraphVertex destination); - /// - /// 图克隆函数 - /// - /// - IGraph Clone(); - /// - /// 获取边 - /// - /// - /// - /// - IEdge? GetEdge(IGraphVertex source, IGraphVertex dest); - /// - /// 邻接表 - /// - /// - /// - HashSet GetAdjacencyList(IGraphVertex vertex); - /// - /// 邻接边表 - /// - /// - /// - HashSet GetAdjacencyEdge(IGraphVertex vertex); - IGraphVertex? ReferenceVertex { get; } - - void RemoveVertex(Point3d pt); - void RemoveEdge(Curve3d curve); - -} - -/// -/// 无向图顶点 -/// -/// 顶点数据类型 -public interface IGraphVertex : IComparable -{ - /// - /// 顶点的键 - /// - /// - int Index { get; set; } - - /// - /// 顶点的数据 - /// - Point3d Data { get; } -} -/// -/// 无向图边 -/// -public interface IEdge -{ - /// - /// 边 - /// - Curve3d TargetEdge { get; } - /// - /// 目标顶点 - /// - IGraphVertex TargetVertex { get; } -} - - - diff --git a/src/IFoxCAD.Cad.Shared/Algorithms/QuadTree/QuadEntity.cs b/src/IFoxCAD.Cad.Shared/Algorithms/QuadTree/QuadEntity.cs deleted file mode 100644 index c5f7f11..0000000 --- a/src/IFoxCAD.Cad.Shared/Algorithms/QuadTree/QuadEntity.cs +++ /dev/null @@ -1,25 +0,0 @@ -namespace IFoxCAD.Cad; - -/* - * 这个类存在的意义是为了不暴露Rect类字段 - * 同时利用了Rect类字段的快速 - * 提供到外面去再继承 - */ - -/// -/// 四叉树图元 -/// -public class QuadEntity : Rect -{ - /// - /// 四叉树图元 - /// - /// 包围盒 - public QuadEntity(Rect box) - { - _X = box._X; - _Y = box._Y; - _Top = box._Top; - _Right = box._Right; - } -} \ No newline at end of file diff --git a/src/IFoxCAD.Cad.Shared/Algorithms/QuadTree/QuadTree.cs b/src/IFoxCAD.Cad.Shared/Algorithms/QuadTree/QuadTree.cs deleted file mode 100644 index 267cc4a..0000000 --- a/src/IFoxCAD.Cad.Shared/Algorithms/QuadTree/QuadTree.cs +++ /dev/null @@ -1,259 +0,0 @@ -/* - * 四叉树维基百科 http://en.wikipedia.org/wiki/Quadtree - * 四叉树是一种分区空间的算法,更快找出内部或外部给定区域. - * 通过一个正交矩形边界进行中心点分裂四个正交矩形, - * 插入时候会一直分裂四个正交矩形, - * 当分裂四个节点都无法单独拥有 图元包围盒 就停止分裂,并且你属于这四个节点的父亲. - * (不包含就是面积少了,就这么一句话看代码看半天), - * 还可以通过限制树的深度实现加速. - * - * 第一版: https://www.codeproject.com/script/Articles/ViewDownloads.aspx?aid=30535 - * - * 第二版: 找邻居 - * https://blog.csdn.net/dive_shallow/article/details/112438050 - * https://geidav.wordpress.com/2017/12/02/advanced-octrees-4-finding-neighbor-nodes/ - * - * 1.根节点:控制根节点从而控制所有节点 - * 2.子节点:包含自身根节点,插入矩形的时候进行递归分裂自身,和实现查找. - * 3.接口:约束都要有正交矩形,否则无法调用"包含"方法 - * 4.选择模式:模仿cad的窗选和框选 - */ -namespace IFoxCAD.Cad; - -/// -/// 根节点控制器 -/// -/// 类型接口约束必须有正交矩形 -public class QuadTree where TEntity : QuadEntity -{ - #region 成员 - /// - /// 根节点 - /// - QuadTreeNode _rootNode; - - /// - /// 四叉树节点的数目 - /// - public int Count { get => _rootNode.CountSubTree; } - - /// - /// 点容器(红黑树) - /// - SortedSet _points; - #endregion - - #region 构造 - /// - /// 四叉树根节点控制器 - /// - /// 四叉树矩形范围 - public QuadTree(Rect rect) - { - _rootNode = new QuadTreeNode(rect, null, 0);// 初始化根节点 - _points = new(); - } - #endregion - - #region 方法 - /// - /// 通过根节点插入数据项 - /// - /// - public void Insert(TEntity ent) - { - /* - * 图元点 是不分裂空间的,加入一个红黑树内部. - */ - if (ent.IsPoint) - { - _points.Add(ent); - return; - } - - while (!_rootNode.Contains(ent)) - { - /* - * 四叉树插入时候,如果超出根边界,就需要扩展 - * 扩展时候有一个要求,当前边界要作为扩展边界的一个象限,也就是反向分裂 - * - * 创建新根,计算原根在新根的位置, - * 替换指针:获取新分裂的节点的父节点,判断它哪个儿子是它, - * 替换之后可能仍然不包含图元边界,再循环计算. - */ - var sq_Left = _rootNode._X; - var sq_Botton = _rootNode._Y; - var sq_Right = _rootNode._Right; - var sq_Top = _rootNode._Top; - if (ent._Y >= _rootNode._Y)// 上↑增殖 - { - if (ent._X >= _rootNode._X) - { - // 右上↗增殖 - sq_Right += _rootNode.Width; - sq_Top += _rootNode.Height; - } - else - { - // 左上↖增殖 - sq_Left -= _rootNode.Width; - sq_Top += _rootNode.Height; - } - } - else// 在下↓ - { - if (ent._X >= _rootNode._X) - { - // 右下↘增殖 - sq_Right += _rootNode.Width; - sq_Botton -= _rootNode.Height; - } - else - { - // 左下↙增殖 - sq_Left -= _rootNode.Width; - sq_Botton -= _rootNode.Height; - } - } - // 扩大2次方 - var rectSquare = new Rect(sq_Left, sq_Botton, sq_Right, sq_Top); - - // 四叉树的旧根要作为四分之一插入 - // 新根中计算原根 - // 把 旧根节点 连接到 新根节点 上面,然后新根成为根 - var newRoot = new QuadTreeNode(rectSquare, null, 0); - var insert = newRoot.Insert(_rootNode); - if (insert is null) - throw new("四叉树:新根尺寸不对"); - if (!insert.Equals(_rootNode)) - throw new("四叉树:新旧节点大小不一致,无法连接"); - - var insPar = insert.Parent; - _rootNode.Parent = insPar; - if (insPar is null) - return; - - if (_rootNode.Equals(insPar.RightTopTree)) - insPar.RightTopTree = _rootNode; - else if (_rootNode.Equals(insPar.RightBottomTree)) - insPar.RightBottomTree = _rootNode; - else if (_rootNode.Equals(insPar.LeftBottomTree)) - insPar.LeftBottomTree = _rootNode; - else if (_rootNode.Equals(insPar.LeftTopTree)) - insPar.LeftTopTree = _rootNode; - else - throw new("四叉树:新节点不对,无法连接"); - - // 其后的子节点层数全部增加层数, - // 要加多少层取决于当前根边界属于新根边界的所在层 - var depth = insert.Depth; - if (depth == 0) - throw new("四叉树:插入节点是0,造成错误"); - _rootNode.ForEach(node => { - node.Depth += depth; - return false; - }); - - // 交换根控制 - _rootNode = newRoot; - } - - _rootNode.Insert(ent); - } - - - /// - /// 查询四叉树,返回给定区域的数据项 - /// - /// 矩形选区查询 - /// - public List Query(Rect rect, QuadTreeSelectMode selectMode = QuadTreeSelectMode.IntersectsWith) - { - QuadTreeEvn.SelectMode = selectMode; - - var results = new List(); - // 选择图元 - _rootNode.Query(rect, results); - // 选择点 - var ptge = _points.GetEnumerator(); - switch (selectMode) - { - case QuadTreeSelectMode.IntersectsWith: - case QuadTreeSelectMode.Contains: - /* 由于红黑树的方法 _points.GetViewBetween() - * 过滤只能过滤X区间,Y区间还是要过滤, - * 那么我就只能用这样的方法加速了 - * - * 而更好的方式是不用红黑树,去加入一个点云数据来进行,可谓是编程无极限.... - */ - while (ptge.MoveNext()) - { - var ptEnt = ptge.Current; - if (rect._X <= ptEnt._X && ptEnt._X <= rect._Right) - { - if (rect._Y <= ptEnt._Y && ptEnt._Y <= rect.Top) - results.Add(ptEnt); - } - else if (ptEnt._X > rect._Right) - break;// 超过后面范围就break,因为红黑树已经排序 - } - break; - default: - throw new("四叉树:" + nameof(selectMode)); - } - return results; - } - - /// - /// 删除子节点 - /// - /// 根据范围删除 - public void Remove(Rect rect) - { - _rootNode.Remove(rect); - } - - /// - /// 删除子节点 - /// - /// 根据图元删除 - public void Remove(TEntity ent) - { - _rootNode.Remove(ent); - } - - /// - /// 找到附近节点图元 - /// - [Obsolete("找附近节点的并不是最近的图元")] - public TEntity? FindNeibor(Rect rect, QuadTreeFindMode findMode) - { - return _rootNode.FindNeibor(rect, findMode); - } - - /// - /// 找到附近图元 - /// - /// - /// - public TEntity? FindNearEntity(Rect rect) - { - return _rootNode.FindNearEntity(rect); - } - - /// - /// 执行四叉树中特定的行为 - /// - /// - public void ForEach(QTAction action) - { - _rootNode.ForEach(action); - } - - /// - /// 委托:四叉树节点上执行一个操作 - /// - /// - public delegate bool QTAction(QuadTreeNode obj); - #endregion -} \ No newline at end of file diff --git a/src/IFoxCAD.Cad.Shared/Algorithms/QuadTree/QuadTreeEvn.cs b/src/IFoxCAD.Cad.Shared/Algorithms/QuadTree/QuadTreeEvn.cs deleted file mode 100644 index 16d518b..0000000 --- a/src/IFoxCAD.Cad.Shared/Algorithms/QuadTree/QuadTreeEvn.cs +++ /dev/null @@ -1,26 +0,0 @@ -#pragma warning disable CA2211 // 非常量字段应当不可见 -namespace IFoxCAD.Cad; - -public class QuadTreeEvn -{ - /// - /// 最小的节点有一个面积(一定要大于0) - /// - public static double MinArea = 1e-6; - - /// - /// 选择模式 - /// - public static QuadTreeSelectMode SelectMode; - - /// - /// 最大深度 - /// - public static int QuadTreeMaximumDepth = 2046; - - /// - /// 节点内容超过就分裂 - /// - public static int QuadTreeContentsCountSplit = 20; -} -#pragma warning restore CA2211 // 非常量字段应当不可见 \ No newline at end of file diff --git a/src/IFoxCAD.Cad.Shared/Algorithms/QuadTree/QuadTreeNode.cs b/src/IFoxCAD.Cad.Shared/Algorithms/QuadTree/QuadTreeNode.cs deleted file mode 100644 index 7898b84..0000000 --- a/src/IFoxCAD.Cad.Shared/Algorithms/QuadTree/QuadTreeNode.cs +++ /dev/null @@ -1,816 +0,0 @@ -namespace IFoxCAD.Cad; - -/// -/// 子节点 -/// -/// -public class QuadTreeNode - : Rect - where TEntity : QuadEntity -{ - #region 成员 - /// - /// 子节点:第一象限:右上↗ - /// - public QuadTreeNode? RightTopTree; - /// - /// 子节点:第二象限:左上↖ - /// - public QuadTreeNode? LeftTopTree; - /// - /// 子节点:第三象限:左下↙ - /// - public QuadTreeNode? LeftBottomTree; - /// - /// 子节点:第四象限:右下↘ - /// - public QuadTreeNode? RightBottomTree; - /// - /// 所有子节点 - /// - QuadTreeNode[] Nodes - { - get - { - return new QuadTreeNode[] - { - RightTopTree!, - LeftTopTree!, - LeftBottomTree!, - RightBottomTree!, - }; - } - } - /// - /// 所有子节点是空的 - /// - bool NodesIsEmpty => RightTopTree is null && LeftTopTree is null && LeftBottomTree is null && RightBottomTree is null; - - /// - /// 父节点 - /// - public QuadTreeNode? Parent; - /// - /// 节点的在四叉树的深度 - /// - public int Depth; - - // 注意,内容没有限制:这不是 impement 四叉树的标准方法 - /// (节点图元是交叉线压着的,并不是矩形范围内全部,因为这是四叉树的特性决定) - /// - /// 本节点:内容 - /// - public List Contents; - - /// - /// 本节点和旗下所有子节点:内容群 - /// - public void ContentsSubTree(List results) - { - if (Contents is null) - return; - results.AddRange(Contents); - var nodes = Nodes; - for (int i = 0; i < nodes.Length; i++) - nodes[i]?.ContentsSubTree(results); - } - - /// - /// 本节点和旗下所有子节点:内容群数量 - /// - public int CountSubTree - { - get - { - if (Contents is null) - return 0; - int count = Contents.Count; - - var nodes = Nodes; - for (int i = 0; i < nodes.Length; i++) - { - var node = nodes[i]; - if (node is null) - continue; - count += node.CountSubTree; - } - return count; - } - } - #endregion - - #region 构造 - /// - /// 四叉树节点 - /// - /// 当前节点边界 - /// 父节点 - /// 节点深度 - public QuadTreeNode(Rect box, QuadTreeNode? parent, int depth) - { - _X = box._X; - _Y = box._Y; - _Right = box._Right; - _Top = box._Top; - - Parent = parent; - Depth = depth; - Contents = new(); - } - #endregion - - #region 增 - /// - /// 将原有节点插入用 - /// - /// - internal QuadTreeNode? Insert(Rect rect) - { - if (!Contains(rect)) - return null; - - // 四叉树分裂,将当前节点分为四个子节点 - if (NodesIsEmpty) - CreateChildren(); - - // 当前节点边界 包含 图元包围盒 就插入 - // 退出递归:4个节点都不完全包含 - // 4个节点的上层 - var nodes = Nodes; - for (int i = 0; i < nodes.Length; i++) - { - var node = nodes[i]; - if (node is null) - continue; - - if (node.Equals(rect)) - { - rect = node; - return node.Insert(rect); - } - } - return this; - } - - /// - /// 将数据项递归插入四叉树 - /// - /// - public QuadTreeNode? Insert(TEntity ent) - { - if (!Contains(ent)) - { - // Debugx.Printl("不在四叉树边界范围"); - // Trace.WriteLine("不在四叉树边界范围"); - return null; - } - - // if (ent.IsPoint) - // { - // // 找到最后一层包含它的节点,然后加入它 - // // 因此是跳过分裂矩形的,以免造成无限递归 - // var minNode = GetMinNode(ent); - // minNode.Contents.Add(ent); - // return minNode; - // } - -#if true2 - // 方案二: - // 内容数超过才分裂,防止树深度过高,但是多选过滤时候慢一点 - if (Contents.Count > QuadTreeEvn.QuadTreeContentsCountSplit) - { - // 分裂出四个子节点 - if (_nodesIsEmpty) - { - CreateChildren(); - // 分裂之后将当前层的内容扔到四个子节点, - // 如果被压着,那么就不会扔到下面 - for (int i = Contents.Count - 1; i >= 0; i--) - { - var minNode = GetMinNode(Contents[i].Box); - minNode.Contents.Add(Contents[i]); - Contents.RemoveAt(i); - } - } - else - { - // 没有分裂的话,就递归 - // 退出递归:4个节点都不完全包含,内容就是他们的父亲 - var nodes = _Nodes; - for (int i = 0; i < nodes.Length; i++) - { - var node = nodes[i]; - if (node is null) - continue; - - // 这里需要中断.(匿名方法ForEach无法中断,会造成父节点加入内容) - if (node.Contains(ent)) - return node.Insert(ent); - } - } - } -#else - // 方案一:分裂到最细节点 - - // 分裂出四个子节点 - if (NodesIsEmpty) - CreateChildren(); - - // 4个子节点开始递归 - // 退出递归:4个节点都不完全包含,内容就是他们的父亲 - var nodes = Nodes; - for (int i = 0; i < nodes.Length; i++) - { - var node = nodes[i]; - if (node is null) - continue; - // 这里需要中断.(匿名方法ForEach无法中断,会造成父节点加入内容) - if (node.Contains(ent)) - return node.Insert(ent); - } -#endif - - // 为什么要用容器? - // 相同包围盒或者四叉树分割线压着多个. - this.Contents.Add(ent); - return this; - } - - /// - /// 创建子节点 - /// - void CreateChildren() - { - // 最小面积控制节点深度,但是这样可能导致树分成高,引起爆栈 - if (Depth > QuadTreeEvn.QuadTreeMaximumDepth) - return; - var recs = RectSplit(this); - var de = Depth + 1; - RightTopTree = new QuadTreeNode(recs[0], this, de); - LeftTopTree = new QuadTreeNode(recs[1], this, de); - LeftBottomTree = new QuadTreeNode(recs[2], this, de); - RightBottomTree = new QuadTreeNode(recs[3], this, de); - } - - /// - /// 矩形分裂为四个 - /// - /// - /// - static Rect[] RectSplit(Rect box) - { - var halfWidth = box.Width / 2.0; - var halfHeight = box.Height / 2.0; - - var upperRight = new Rect(box._X + halfWidth, box._Y + halfHeight, box._Right, box._Top); - var upperLeft = new Rect(box._X, box._Y + halfHeight, box._Right - halfWidth, box._Top); - var lowerleft = new Rect(box._X, box._Y, box._Right - halfWidth, box._Top - halfHeight);// 基础 - var lowerRight = new Rect(box._X + halfWidth, box._Y, box._Right, box._Top - halfHeight); - - // 依照象限顺序输出 - return new Rect[] { upperRight, upperLeft, lowerleft, lowerRight }; - } - #endregion - - #region 删 - /// - /// 删除图元 - /// - /// 根据图元删除 - public bool Remove(TEntity easeEnt) - { - // 通过图元id删除无疑是非常低效的, - // 1.相当于在所有的容器查找它,但是移除只会移除一次, - // 因此必须要求图元只会加入一次,才能中断检索剩余分支. - // 2.这个代价还是太高,因此我们还是要默认条件,图元载入一次之后,不再改动. - // 3.不再改动也不太合理,因为cad图元还是可以修改的 - - // 1.处理内容 - if (Contents.Remove(easeEnt)) - { - if (CountSubTree == 0) - this.Clear(this); - return true; - } - - // 2.递归子节点移除 - var nodes = Nodes; - for (int i = 0; i < nodes.Length; i++) - { - var node = nodes[i]; - if (node is null) - continue; - if (node.Remove(easeEnt)) // 递归进入子节点删除内容 - return true; // 删除成功就中断其他节点的搜索 - } - return false; - } - - /// - /// 递归进入最下层节点,然后开始清理 - /// - /// - void Clear(QuadTreeNode node) - { - var nodes = Nodes; - for (int i = 0; i < nodes.Length; i++) - nodes[i]?.Clear(nodes[i]); - - node.Contents.Clear(); - // node.Contents = null;// 重复加入时候会出错 - node.RightTopTree = null; - node.LeftTopTree = null; - node.LeftBottomTree = null; - node.RightBottomTree = null; - node.Parent = null; - // node.Box = zoreRect; - } - - /// - /// 删除子节点内容 - /// - /// 根据范围删除 - public void Remove(Rect queryArea) - { - // 本节点内容移除 - if (Contents is not null && Contents.Count > 0)// 从最上层的根节点开始进入 - { - for (int i = Contents.Count - 1; i >= 0; i--) - { - var ent = Contents[i]; - // 移除之后,如果容器是0,那么这里不能直接 Contents=null, - // 因为此节点下面可能还有节点, - // 需要判断了其后数量0才可以清理. - // 否则其后还有内容,那么此节点就是仍然可以用的. - if (queryArea.Contains(ent)) - Contents.Remove(ent); - } - } - - // 同插入一样 - // 跳到指定节点再搜索这个节点下面的图元 - var nodes = Nodes; - for (int i = 0; i < nodes.Length; i++) - { - var node = nodes[i]; - if (node is null) - continue; - if (node.NodesIsEmpty) - continue; - - // 此节点边界 完全包含 查询区域,则转到该节点,并跳过其余节点(打断此循环) - if (node.Contains(queryArea)) - { - node.Remove(queryArea); - break; - } - // 查询区域 完全包含 此节点边界,提取此节点全部内容 - // 跳过分析碰撞,并继续循环搜索其他节点 - if (queryArea.Contains(node)) - { - node.Clear(node); - continue; - } - // 查询区域 与 此节点四边形边线碰撞 查询该四边形中,并继续循环搜索其他节点 - // 1,角点碰撞 2,边界碰撞 - if (node.IntersectsWith(queryArea)) - node.Remove(queryArea); - } - - // 本节点内容移除之后,旗下还有内容的话, - // 会跳过此处,再进入子节点进行递归,直到最后一个节点 - if (CountSubTree == 0) - Clear(this); - } - #endregion - - #region 查 - /// - /// 查询范围内的实体 - /// - /// 查询矩形 - /// - public void Query(Rect queryArea, List results) - { - GetCurrentContents(queryArea, results); - - // 遍历子节点 - var nodes = Nodes; - for (int i = 0; i < nodes.Length; i++) - { - var node = nodes[i]; - if (node is null) - continue; - // 子节点的4个子节点都是空的, - // 那么表示元素会在子节点这一层啊... - if (node.NodesIsEmpty) - continue; - - // 此节点边界 完全包含 查询区域,则转到该节点,并跳过其余节点(打断此循环) - if (node.Contains(queryArea)) - { - node.Query(queryArea, results); - break; - } - // 查询区域 完全包含 此节点边界,提取此节点全部内容 - // 跳过分析碰撞,并继续循环搜索其他节点 - if (queryArea.Contains(node)) - { - node.ContentsSubTree(results); - continue; - } - // 查询区域 与 此节点四边形边线碰撞 查询该四边形中,并继续循环搜索其他节点 - // 1,角点碰撞 2,边界碰撞 - if (node.IntersectsWith(queryArea)) - node.Query(queryArea, results); - } - } - - /// - /// 获取本节点内容 - /// - /// - /// - void GetCurrentContents(Rect queryArea, List results) - { - // 遍历当前节点内容,加入方式取决于碰撞模式 - if (QuadTreeEvn.SelectMode == QuadTreeSelectMode.IntersectsWith) - { - for (int i = 0; i < Contents.Count; i++) - { - var ent = Contents[i]; - if (queryArea.IntersectsWith(ent)) - results.Add(ent); - } - } - else - { - for (int i = 0; i < Contents.Count; i++) - { - var ent = Contents[i]; - if (queryArea.Contains(ent)) - results.Add(ent); - } - } - } - - /// - /// 找临近图元 - /// - /// 查找矩形 - /// - public TEntity? FindNearEntity(Rect queryArea) - { - TEntity? resultEntity = default; - // 1.找到 查找矩形 所在的节点,利用此节点的矩形. - var queryNode = GetMinNode(queryArea); - var queryAreaCenter = queryArea.CenterPoint; - - // 2.从根开始搜索 - // 如果搜索父亲的父亲的...内容群,它不是距离最近的,只是节点(亲属关系)最近 - // 储存找过的<图元,距离> - var entDic = new Dictionary(); - - var old = QuadTreeEvn.SelectMode; - QuadTreeEvn.SelectMode = QuadTreeSelectMode.IntersectsWith; - while (true) - { - // 循环找父节点大小 - var hw = queryNode.Width / 2.0; - var hh = queryNode.Height / 2.0; - // 3.利用选区中心扩展一个节点边界大小的矩形.从而选择图元 - // 再判断图元的与目标的距离,找到最小距离,即为最近 - var minPt = new Point2d(queryAreaCenter.X - hw, queryAreaCenter.Y - hh); - var maxPt = new Point2d(queryAreaCenter.X + hw, queryAreaCenter.Y + hh); - var ents = new List(); - Query(new Rect(minPt, maxPt), ents); - for (int i = 0; i < ents.Count; i++) - { - var ent = ents[i]; - if (entDic.ContainsKey(ent)) - continue; - var dis = ent.CenterPoint.GetDistanceTo(queryAreaCenter); - if (dis > 1e-6)// 剔除本身 - entDic.Add(ent, dis); - } - if (entDic.Count > 0) - { - resultEntity = entDic.OrderBy(a => a.Value).First().Key; - break; - } - if (queryNode.Parent is null)// 最顶层就退出 - break; - queryNode = queryNode.Parent;// 利用父节点矩形进行变大选区 - } - QuadTreeEvn.SelectMode = old; - return resultEntity; - } - - /// - /// 找临近节点的图元 - /// - /// 查找矩形 - /// 查找什么方向 - /// - [Obsolete("找附近节点的并不是最近的图元")] - public TEntity? FindNeibor(Rect queryArea, QuadTreeFindMode findMode) - { - TEntity? resultEntity = default; - // 1.找到 查找矩形 所在的节点,利用此节点的矩形. - // 2.利用节点矩形是分裂的特点,边和边必然贴合. - // 3.找到方向 findMode 拥有的节点,然后查找节点的内容 - var queryNode = GetMinNode(queryArea); - - bool whileFlag = true; - // 同一个节点可能包含邻居,因为四叉树的加入是图元压线, - // 那么就在这里搜就得了,用中心点决定空间位置 - // 但是本空间的图元可能都比它矮,无法满足条件 - if (queryNode.CountSubTree > 1) - { - resultEntity = GetNearestNeighbor(queryNode, findMode, queryArea); - if (resultEntity is null || resultEntity.CenterPoint == queryArea.CenterPoint) - whileFlag = true; - else - whileFlag = false; - } - - while (whileFlag) - { - // 同一个父节点是临近的,优先获取 兄弟节点 的内容. - // 循环了第二次是北方兄弟的节点, - // 但是这不是一个找到临近图元的方法, - // 因为临近的可能是父亲的父亲的父亲...另一个函数 FindNearEntity 写 - // 本方案也仅仅作为找北方节点 - var parent = queryNode.Parent; - if (parent is not null) - { - switch (findMode) - { - case QuadTreeFindMode.Top: - { - // 下格才获取上格,否则导致做了无用功,上格就直接获取邻居了 - if (parent.LeftBottomTree == queryNode || parent.RightBottomTree == queryNode) - resultEntity = GetNearestNeighbor(parent, findMode, queryArea); - } - break; - case QuadTreeFindMode.Bottom: - { - if (parent.LeftTopTree == queryNode || parent.RightTopTree == queryNode) - resultEntity = GetNearestNeighbor(parent, findMode, queryArea); - } - break; - case QuadTreeFindMode.Left: - { - if (parent.RightTopTree == queryNode || parent.RightBottomTree == queryNode) - resultEntity = GetNearestNeighbor(parent, findMode, queryArea); - } - break; - case QuadTreeFindMode.Right: - { - if (parent.LeftTopTree == queryNode || parent.LeftBottomTree == queryNode) - resultEntity = GetNearestNeighbor(parent, findMode, queryArea); - } - break; - } - } - if (resultEntity is not null) - break; - - // 通过 所在节点 找 邻居节点, - // 拿到 邻居节点 下面的所有内容(图元) - // 内容可能是空的,再从邻居那往北找...如果找到了四叉树最外层,仍然没有内容,退出循环 - var neiborNode = FindNeiborNode(queryNode, findMode); - if (neiborNode is null) - continue; - if (neiborNode.CountSubTree > 0) - { - resultEntity = GetNearestNeighbor(neiborNode, findMode, queryArea); - break; - } - if (neiborNode.Parent is null)// 如果找到了四叉树最外层,仍然没有内容,退出循环 - break; - queryNode = neiborNode; - } - - return resultEntity; - } - - /// - /// 查找节点的(本内容和子内容)与(查找面积)矩形中点对比,找到最近一个内容 - /// - /// 查找面积 - /// 查找方向 - /// 查找节点 - /// - static TEntity? GetNearestNeighbor(QuadTreeNode queryNode, - QuadTreeFindMode findMode, - Rect queryArea) - { - TEntity? results = default; - - var lst = new List(); - var qcent = queryArea.CenterPoint; - - switch (findMode) - { - case QuadTreeFindMode.Top: - { - // 取出Y比queryArea的还大的一个,是最近的那个 - var qy = qcent.Y; - queryNode.ContentsSubTree(lst); - lst.ForEach(ent => { - if (ent.CenterPoint.Y > qy) - lst.Add(ent); - }); - lst = lst.OrderBy(ent => ent.CenterPoint.Y).ToList(); - } - break; - case QuadTreeFindMode.Bottom: - { - var qy = qcent.Y; - queryNode.ContentsSubTree(lst); - lst.ForEach(ent => { - if (ent.CenterPoint.Y < qy) - lst.Add(ent); - }); - lst = lst.OrderByDescending(ent => ent.CenterPoint.Y).ToList(); - } - break; - case QuadTreeFindMode.Left: - { - var qx = qcent.Y; - queryNode.ContentsSubTree(lst); - lst.ForEach(ent => { - if (ent.CenterPoint.X > qx) - lst.Add(ent); - }); - lst = lst.OrderBy(ent => ent.CenterPoint.X).ToList(); - } - break; - case QuadTreeFindMode.Right: - { - var qx = qcent.Y; - queryNode.ContentsSubTree(lst); - lst.ForEach(ent => { - if (ent.CenterPoint.X < qx) - lst.Add(ent); - }); - lst = lst.OrderByDescending(ent => ent.CenterPoint.X).ToList(); - } - break; - } - - if (lst.Count > 0) - return lst[0];// 可能就是本体重叠 - return results; - } - - /// - /// 找包含它的最小分支 - /// - /// 查询的矩形 - /// 节点 - QuadTreeNode GetMinNode(Rect queryArea) - { - var nodes = Nodes; - for (int i = 0; i < nodes.Length; i++) - { - var node = nodes[i]; - if (node is null) - continue; - - // 边界包含查询面积,那么再递归查询, - // 直到最后四个都不包含,那么上一个就是图元所在节点 - if (node.Contains(queryArea)) - return node.GetMinNode(queryArea);// 中断后面的范围,才可以返回正确的 - } - return this; - } - - /// - /// 四叉树找邻居节点(相同或更大) - /// - /// 源节点 - /// 方向 - /// - QuadTreeNode? FindNeiborNode(QuadTreeNode tar, QuadTreeFindMode findMode) - { - var parent = tar.Parent; - if (parent is null) - return null; - switch (findMode) - { - case QuadTreeFindMode.Top: - { - // 判断当前节点在父节点的位置,如果是在 下格 就取对应的 上格 - if (tar == parent.LeftBottomTree) - return parent.LeftTopTree; - if (tar == parent.RightBottomTree) - return parent.RightTopTree; - // 否则就是上格 - // 找父节点的北方邻居..也就是在爷节点上面找 - // 递归,此时必然是 下格,就必然返回 上格,然后退出递归 - var parentNeibor = FindNeiborNode(parent, QuadTreeFindMode.Top); - // 父节点的北方邻居 无 子节点 - if (parentNeibor is null || parentNeibor.RightTopTree is null) - return parentNeibor;// 返回父节点的北方邻居,比较大 - // 父节点的北方邻居 有 子节点,剩下条件就只有这两 - - // 如果直接返回,那么是(相同或更大), - // 而找邻近图元需要的是这个(相同或更大)下面的图元,在外面对这个格子内图元用坐标分析就好了 - if (tar == parent.LeftTopTree) - return parentNeibor.LeftBottomTree; - return parentNeibor.RightBottomTree; - } - case QuadTreeFindMode.Bottom: - { - if (tar == parent.LeftTopTree) - return parent.LeftBottomTree; - if (tar == parent.RightTopTree) - return parent.RightBottomTree; - var parentNeibor = FindNeiborNode(parent, QuadTreeFindMode.Bottom); - if (parentNeibor is null || parentNeibor.RightTopTree is null) - return parentNeibor; - if (tar == parent.LeftBottomTree) - return parentNeibor.LeftTopTree; - return parentNeibor.RightTopTree; - } - case QuadTreeFindMode.Right: - { - if (tar == parent.LeftTopTree) - return parent.RightTopTree; - if (tar == parent.LeftBottomTree) - return parent.RightBottomTree; - var parentNeibor = FindNeiborNode(parent, QuadTreeFindMode.Right); - if (tar == parent.RightTopTree) - return parentNeibor?.LeftTopTree; - return parentNeibor?.LeftBottomTree; - } - case QuadTreeFindMode.Left: - { - if (tar == parent.RightTopTree) - return parent.LeftTopTree; - if (tar == parent.RightBottomTree) - return parent.LeftBottomTree; - var parentNeibor = FindNeiborNode(parent, QuadTreeFindMode.Left); - if (tar == parent.LeftTopTree) - return parentNeibor?.RightTopTree; - return parentNeibor?.RightBottomTree; - } - } - return null; - } - #endregion - - #region 改 - /// - /// 所有的点归类到最小包围它的空间 - /// - // public void PointsToMinNode() - // { - // ForEach(node => - // { - // for (int i = 0; i < node.Contents.Count; i++) - // { - // var ent = node.Contents[i]; - // if (ent.IsPoint) - // { - // // 如果最小包含!=当前,就是没有放在最适合的位置 - // var queryNode = GetMinNode(ent); - // if (queryNode != node) - // { - // node.Remove(ent); - // queryNode.Contents.Add(ent); - // } - // } - // } - // return false; - // }); - // } - #endregion - - #region 方法 - /// - /// 递归全部节点(提供给根用的,所以是全部) - /// - /// QTAction - public bool ForEach(QuadTree.QTAction action) - { - // 执行本节点 - if (action(this)) - return true; - - // 递归执行本节点的子节点 - var nodes = Nodes; - for (int i = 0; i < nodes.Length; i++) - { - var node = nodes[i]; - if (node is null) - continue; - if (node.ForEach(action)) - break; - } - return false; - } - #endregion -} \ No newline at end of file diff --git a/src/IFoxCAD.Cad.Shared/Algorithms/QuadTree/QuadTreeSelectMode.cs b/src/IFoxCAD.Cad.Shared/Algorithms/QuadTree/QuadTreeSelectMode.cs deleted file mode 100644 index 2cc2e37..0000000 --- a/src/IFoxCAD.Cad.Shared/Algorithms/QuadTree/QuadTreeSelectMode.cs +++ /dev/null @@ -1,21 +0,0 @@ -namespace IFoxCAD.Cad; - -/// -/// 四叉树选择模式 -/// -public enum QuadTreeSelectMode -{ - IntersectsWith, // 碰撞到就选中 - Contains, // 全包含才选中 -} - -/// -/// 四叉树查找方向 -/// -public enum QuadTreeFindMode -{ - Top = 1, // 上 - Bottom = 2, // 下 - Left = 4, // 左 - Right = 8, // 右 -} \ No newline at end of file diff --git a/src/IFoxCAD.Cad.Shared/Algorithms/QuadTree/Rect.cs b/src/IFoxCAD.Cad.Shared/Algorithms/QuadTree/Rect.cs deleted file mode 100644 index 9e10b97..0000000 --- a/src/IFoxCAD.Cad.Shared/Algorithms/QuadTree/Rect.cs +++ /dev/null @@ -1,608 +0,0 @@ -using System.Diagnostics; -using System.Runtime.CompilerServices; - -namespace IFoxCAD.Cad; - -/// -/// Linq Distinct 消重比较两点在容差范围内就去除 -/// -public class TolerancePoint2d : IEqualityComparer -{ - readonly double _tolerance; - public TolerancePoint2d(double tolerance = 1e-6) - { - _tolerance = tolerance; - } - - public bool Equals(Point2d a, Point2d b)// Point3d是struct不会为null - { - /*默认规则是==是0容差,Eq是有容差*/ - // 方形限定 - // 在 0~1e-6 范围实现 圆形限定 则计算部分在浮点数6位后,没有啥意义 - // 在 0~1e-6 范围实现 从时间和CPU消耗来说,圆形限定 都没有 方形限定 的好 - if (_tolerance <= 1e-6) - return Math.Abs(a.X - b.X) <= _tolerance && Math.Abs(a.Y - b.Y) <= _tolerance; - - // 圆形限定 - // DistanceTo 分别对XYZ进行了一次乘法,也是总数3次乘法,然后求了一次平方根 - // (X86.CPU.FSQRT指令用的牛顿迭代法/软件层面可以使用快速平方根....我还以为CPU会采取快速平方根这样的取表操作) - return a.IsEqualTo(b, new Tolerance(_tolerance, _tolerance)); - } - - public int GetHashCode(Point2d obj) - { - // 结构体直接返回 obj.GetHashCode(); Point3d ToleranceDistinct3d - // 因为结构体是用可值叠加来判断?或者因为结构体兼备了一些享元模式的状态? - // 而类是构造的指针,所以取哈希值要改成x+y+z..s给Equals判断用,+是会溢出,所以用^ - return (int)obj.X ^ (int)obj.Y;// ^ (int)obj.Z; - } -} - - -[Serializable] -[StructLayout(LayoutKind.Sequential)] -[DebuggerDisplay("{DebuggerDisplay,nq}")] -[DebuggerTypeProxy(typeof(Rect))] -public class Rect : IEquatable, IComparable -{ - [DebuggerBrowsable(DebuggerBrowsableState.Never)] - private string DebuggerDisplay => ToString("f4"); - -#pragma warning disable CA2211 // 非常量字段应当不可见 - public static TolerancePoint2d RectTolerance = new(1e-6); - public static Tolerance CadTolerance = new(1e-6, 1e-6); -#pragma warning restore CA2211 // 非常量字段应当不可见 - - #region 字段 - // 这里的成员不要用{get}封装成属性,否则会导致跳转了一次函数, - // 10w图元将会从187毫秒变成400毫秒 - // 不用 protected 否则子类传入Rect对象进来无法用 - internal double _X; - internal double _Y; - internal double _Right; - internal double _Top; - #endregion - - #region 成员 - public double X => _X; - public double Y => _Y; - public double Left => _X; - public double Bottom => _Y; - public double Right => _Right; - public double Top => _Top; - - public double Width => _Right - _X; - public double Height => _Top - _Y; - public double Area - { - get - { - var ar = (_Right - _X) * (_Top - _Y); - return ar < 1e-10 ? 0 : ar; - } - } - - public Point2d MinPoint => LeftLower; - public Point2d MaxPoint => RightUpper; - public Point2d CenterPoint => Midst; - - /// - /// 左下Min - /// - public Point2d LeftLower => new(_X, _Y); - - /// - /// 左中 - /// - public Point2d LeftMidst => new(_X, Midst.Y); - - /// - /// 左上 - /// - public Point2d LeftUpper => new(_X, _Top); - - /// - /// 右上Max - /// - public Point2d RightUpper => new(_Right, _Top); - - /// - /// 右中 - /// - public Point2d RightMidst => new(_Right, Midst.Y); - - /// - /// 右下 - /// - public Point2d RightBottom => new(_Right, _Y); - - /// - /// 中间 - /// - public Point2d Midst => new(((_Right - _X) * 0.5) + _X, ((_Top - _Y) * 0.5) + _Y); - - /// - /// 中上 - /// - public Point2d MidstUpper => new(Midst.X, _Top); - - /// - /// 中下 - /// - public Point2d MidstBottom => new(Midst.X, _Y); - - /// - /// 是一个点 - /// 水平或垂直直线包围盒是面积是0,所以面积是0不一定是点 - /// - public bool IsPoint => Math.Abs(_X - _Right) < 1e-10 && Math.Abs(_Y - _Top) < 1e-10; - #endregion - - #region 构造 - public Rect() - { - } - - /// - /// 矩形类 - /// - /// 左 - /// 下 - /// 右 - /// 上 - public Rect(double left, double bottom, double right, double top) - { - _X = left; - _Y = bottom; - _Right = right; - _Top = top; - } - - /// - /// 构造矩形类 - /// - /// - /// - /// 是否检查大小 - public Rect(Point2d p1, Point2d p3, bool check = false) - { - if (check) - { - _X = Math.Min(p1.X, p3.X); - _Y = Math.Min(p1.Y, p3.Y); - _Right = Math.Max(p1.X, p3.X); - _Top = Math.Max(p1.Y, p3.Y); - } - else - { - _X = p1.X; - _Y = p1.Y; - _Right = p3.X; - _Top = p3.Y; - } - } - #endregion - - #region 重载运算符_比较 - public override bool Equals(object? obj) - { - return this.Equals(obj as Rect); - } - public bool Equals(Rect? b) - { - return this.Equals(b, 1e-6);/*默认规则是==是0容差,Eq是有容差*/ - } - public static bool operator !=(Rect? a, Rect? b) - { - return !(a == b); - } - public static bool operator ==(Rect? a, Rect? b) - { - // 此处地方不允许使用==null,因为此处是定义 - if (b is null) - return a is null; - else if (a is null) - return false; - if (ReferenceEquals(a, b))// 同一对象 - return true; - - return a.Equals(b, 0); - } - - /// - /// 比较核心 - /// - public bool Equals(Rect? b, double tolerance = 1e-6) - { - if (b is null) - return false; - if (ReferenceEquals(this, b)) // 同一对象 - return true; - - return Math.Abs(_X - b._X) < tolerance && - Math.Abs(_Right - b._Right) < tolerance && - Math.Abs(_Top - b._Top) < tolerance && - Math.Abs(_Y - b._Y) < tolerance; - } - - public override int GetHashCode() - { - return (((int)_X ^ (int)_Y).GetHashCode() ^ (int)_Right).GetHashCode() ^ (int)_Top; - } - #endregion - - #region 包含 - public bool Contains(Point2d Point2d) - { - return Contains(Point2d.X, Point2d.Y); - } - public bool Contains(double x, double y) - { - return _X <= x && x <= _Right && - _Y <= y && y <= _Top; - } - - /// - /// 四个点都在内部就是包含 - /// - /// - /// - public bool Contains(Rect rect) - { - return _X <= rect._X && rect._Right <= _Right && - _Y <= rect._Y && rect._Top <= _Top; - } - - /// - /// 一个点在内部就是碰撞 - /// - /// - /// true内部 - [MethodImpl] - public bool IntersectsWith(Rect rect) - { - return rect._X <= _Right && _X <= rect._Right && - rect._Top >= _Y && rect._Y <= _Top; - } - #endregion - - #region 方法 - /// - /// 获取共点 - /// - /// - public Point2d[] GetCommonPoint(Rect other) - { - return ToPoints().Intersect(other.ToPoints(), RectTolerance).ToArray(); - } - - public Point2d[] ToPoints() - { - Point2d a = MinPoint;// min - Point2d b = new(_Right, _Y); - Point2d c = MaxPoint;// max - Point2d d = new(_X, _Top); - return new Point2d[] { a, b, c, d }; - } - - public (Point2d boxMin, Point2d boxRigthDown, Point2d boxMax, Point2d boxLeftUp) ToPoints4() - { - Point2d a = MinPoint;// min - Point2d b = new(_Right, _Y); - Point2d c = MaxPoint;// max - Point2d d = new(_X, _Top); - return (a, b, c, d); - } - - /// - /// 四周膨胀 - /// - /// - public Rect Expand(double d) - { - return new Rect(_X - d, _Y - d, _Right + d, _Top + d); - } - - /// - /// 是否矩形(带角度) - /// - /// - /// - public static bool IsRectAngle(List? ptList, double tolerance = 1e-8) - { - if (ptList == null) - throw new ArgumentNullException(nameof(ptList)); - - var pts = ptList.ToList(); - /* - * 消重,不这里设置,否则这不是一个正确的单元测试 - * // var ptList = pts.Distinct().ToList(); - * var ptList = pts.DistinctExBy((a, b) => a.DistanceTo(b) < 1e-6).ToList(); - */ - if (ptList.Count == 5) - { - // 首尾点相同移除最后 - if (pts[0].IsEqualTo(pts[^1], CadTolerance)) - pts.RemoveAt(pts.Count - 1); - } - if (pts.Count != 4) - return false; - - // 最快的方案 - // 点乘求值法:(为了处理 正梯形/平行四边形 需要三次) - // 这里的容差要在1e-8内,因为点乘的三次浮点数乘法会令精度变低 - var dot = DotProductValue(pts[0], pts[1], pts[3]); - if (Math.Abs(dot) < tolerance) - { - dot = DotProductValue(pts[1], pts[2], pts[0]); - if (Math.Abs(dot) < tolerance) - { - dot = DotProductValue(pts[2], pts[3], pts[1]); - return Math.Abs(dot) < tolerance; - } - } - return false; - } - - /// - /// 点积,求值 - /// 1.是两个向量的长度与它们夹角余弦的积 - /// 2.求四个点是否矩形使用 - /// - /// 原点 - /// 点 - /// 点 - /// 0方向相同,夹角0~90度;=0相互垂直;<0方向相反,夹角90~180度]]> - static double DotProductValue(Point2d o, Point2d a, Point2d b) - { - var oa = o.GetVectorTo(a); - var ob = o.GetVectorTo(b); - return (oa.X * ob.X) + (oa.Y * ob.Y); - } - - /// - /// 是否轴向矩形(无角度) - /// - public static bool IsRect(List? ptList, double tolerance = 1e-10) - { - if (ptList == null) - throw new ArgumentNullException(nameof(ptList)); - - var pts = ptList.ToList(); - if (ptList.Count == 5) - { - // 首尾点相同移除最后 - if (pts[0].IsEqualTo(pts[^1], CadTolerance)) - pts.RemoveAt(pts.Count - 1); - } - if (pts.Count != 4) - return false; - - return Math.Abs(pts[0].X - pts[3].X) < tolerance && - Math.Abs(pts[0].Y - pts[1].Y) < tolerance && - Math.Abs(pts[1].X - pts[2].X) < tolerance && - Math.Abs(pts[2].Y - pts[3].Y) < tolerance; - } - - /// - /// 获取点集的包围盒的最小点和最大点(无角度) - /// - /// - public static (Point2d boxMin, Point2d boxMax) GetMinMax(IEnumerable pts) - { - var xMin = double.MaxValue; - var xMax = double.MinValue; - var yMin = double.MaxValue; - var yMax = double.MinValue; - // var zMin = double.MaxValue; - // var zMax = double.MinValue; - - pts.ForEach(p => { - xMin = Math.Min(p.X, xMin); - xMax = Math.Max(p.X, xMax); - yMin = Math.Min(p.Y, yMin); - yMax = Math.Max(p.Y, yMax); - // zMin = Math.Min(p.Z, zMin); - // zMax = Math.Max(p.Z, zMax); - }); - return (new Point2d(xMin, yMin), new Point2d(xMax, yMax)); - } - - /// - /// 矩形点序逆时针排列,将min点[0],max点是[3](带角度) - /// - /// - /// - public static bool RectAnglePointOrder(List? pts) - { - if (pts == null) - throw new ArgumentNullException(nameof(pts)); - - if (!Rect.IsRectAngle(pts)) - return false; - - // 获取min和max点(非包围盒) - pts = pts.OrderBy(a => a.X).ThenBy(a => a.Y).ToList(); - var minPt = pts.First(); - var maxPt = pts.Last(); - var link = new LoopList(); - link.AddRange(pts); - - pts.Clear(); - // 排序这四个点,左下/右下/右上/左上 - var node = link.Find(minPt); - for (int i = 0; i < 4; i++) - { - pts.Add(node!.Value); - node = node.Next; - } - // 保证是逆时针 - var isAcw = CrossAclockwise(pts[0], pts[1], pts[2]); - if (!isAcw) - (pts[3], pts[1]) = (pts[1], pts[3]); - return true; - } - - /// - /// 叉积,二维叉乘计算 - /// - /// 传参是向量,表示原点是0,0 - /// 传参是向量,表示原点是0,0 - /// 其模为a与b构成的平行四边形面积 - static double Cross(Vector2d a, Vector2d b) - { - return a.X * b.Y - a.Y * b.X; - } - - /// - /// 叉积,二维叉乘计算 - /// - /// 原点 - /// oa向量 - /// ob向量,此为判断点 - /// 返回值有正负,表示绕原点四象限的位置变换,也就是有向面积 - static double Cross(Point2d o, Point2d a, Point2d b) - { - return Cross(o.GetVectorTo(a), o.GetVectorTo(b)); - } - - /// - /// 叉积,逆时针方向为真 - /// - /// 直线点1 - /// 直线点2 - /// 判断点 - /// b点在oa的逆时针 - static bool CrossAclockwise(Point2d o, Point2d a, Point2d b) - { - return Cross(o, a, b) > -1e-6;// 浮点数容差考虑 - } - -#if !WinForm - public Entity ToPolyLine() - { - var bv = new List(); - var pts = ToPoints(); - Polyline pl = new(); - pl.SetDatabaseDefaults(); - pts.ForEach((vertex, state, index) => { - pl.AddVertexAt(index, vertex, 0, 0, 0); - }); - return pl; - } -#endif - - /// - /// 列扫碰撞检测(碰撞算法) - /// 比四叉树还快哦~ - /// - /// - /// 继承Rect的集合 - /// 先处理集合每一个成员;返回true就跳过后续委托 - /// 碰撞,返回两个碰撞的成员;返回true就跳过后续委托 - /// 后处理集合每一个成员 - public static void XCollision(List box, - Func firstProcessing, - Func collisionProcessing, - Action lastProcessing) where T : Rect - { - // 先排序X:不需要Y排序,因为Y的上下浮动不共X .ThenBy(a => a.Box.Y) - // 因为先排序就可以有序遍历x区间,超过就break,达到更快 - box = box.OrderBy(a => a._X).ToList(); - - // 遍历所有图元 - for (int i = 0; i < box.Count; i++) - { - var oneRect = box[i]; - if (firstProcessing(oneRect)) - continue; - - bool actionlast = true; - - // 搜索范围要在 one 的头尾中间的部分 - for (int j = i + 1; j < box.Count; j++) - { - var twoRect = box[j]; - // x碰撞:矩形2的Left 在 矩形1[Left-Right]闭区间;穿过的话,也必然有自己的Left因此不需要处理 - if (oneRect._X <= twoRect._X && twoRect._X <= oneRect._Right) - { - // y碰撞,那就是真的碰撞了 - if ((oneRect._Top >= twoRect._Top && twoRect._Top >= oneRect._Y) /*包容上边*/ - || (oneRect._Top >= twoRect._Y && twoRect._Y >= oneRect._Y) /*包容下边*/ - || (twoRect._Top >= oneRect._Top && oneRect._Y >= twoRect._Y)) /*穿过*/ - { - if (collisionProcessing(oneRect, twoRect)) - actionlast = false; - } - // 这里想中断y高过它的无意义比较, - // 但是必须排序Y,而排序Y必须同X,而这里不是同X(而是同X区间),所以不能中断 - // 而做到X区间排序,就必须创造一个集合,再排序这个集合, - // 会导致每个图元都拥有一次X区间集合,开销更巨大(因此放弃). - } - else - break;// 因为已经排序了,后续的必然超过 x碰撞区间 - } - - if (actionlast) - lastProcessing(oneRect); - } - } - - #endregion - - #region 转换类型 -#if !WinForm - // 隐式转换(相当于是重载赋值运算符) - // public static implicit operator Rect(System.Windows.Rect rect) - // { - // return new Rect(rect.Left, rect.Bottom, rect.Right, rect.Top); - // } - public static implicit operator Rect(System.Drawing.RectangleF rect) - { - return new Rect(rect.Left, rect.Bottom, rect.Right, rect.Top); - } - public static implicit operator Rect(System.Drawing.Rectangle rect) - { - return new Rect(rect.Left, rect.Bottom, rect.Right, rect.Top); - } -#endif - - #region ToString - public sealed override string ToString() - { - return ToString(null, null); - } - public string ToString(IFormatProvider? provider) - { - return ToString(null, provider); - } - public string ToString(string? format = null, IFormatProvider? formatProvider = null) - { - return $"({_X.ToString(format, formatProvider)},{_Y.ToString(format, formatProvider)})," + - $"({_Right.ToString(format, formatProvider)},{_Top.ToString(format, formatProvider)})"; - - // return $"X={_X.ToString(format, formatProvider)}," + - // $"Y={_Y.ToString(format, formatProvider)}," + - // $"Right={_Right.ToString(format, formatProvider)}," + - // $"Top={_Top.ToString(format, formatProvider)}"; - } - - /*为了红黑树,加入这个*/ - public int CompareTo(Rect rect) - { - if (rect == null) - return -1; - if (_X < rect._X) - return -1; - else if (_X > rect._X) - return 1; - else if (_Y < rect._Y)/*x是一样的*/ - return -1; - else if (_Y > rect._Y) - return 1; - return 0;/*全部一样*/ - } - #endregion - - #endregion -} \ No newline at end of file diff --git a/src/IFoxCAD.Cad.Shared/Copyclip/BitmapTool.cs b/src/IFoxCAD.Cad.Shared/Copyclip/BitmapTool.cs deleted file mode 100644 index 9ce266b..0000000 --- a/src/IFoxCAD.Cad.Shared/Copyclip/BitmapTool.cs +++ /dev/null @@ -1,153 +0,0 @@ -namespace IFoxCAD.Cad; - -using System; - -public class BitmapTool -{ - // https://blog.csdn.net/shellching/article/details/18405185 - /// Windows不允许程序员直接访问硬件, - /// 它对屏幕的操作是通过环境设备,也就是DC来完成的 - /// 屏幕上的每一个窗口都对应一个DC,可以把DC想象成一个视频缓冲区, - /// 对这这个缓冲区的操作,会表现在这个缓冲区对应的屏幕窗口上. - /// 在窗口的DC之外,可以建立自己的DC,就是说它不对应窗口, - /// 这个方法就是 CreateCompatibleDC,这个DC就是一个内存缓冲区, - /// 通过这个DC你可以把和它兼容的窗口DC保存到这个DC中, - /// 就是说你可以通过它在不同的DC之间拷贝数据. - /// 例如:你先在这个DC中建立好数据,然后在拷贝到窗口的DC就是完成了这个窗口的刷新 - - /// - /// 检索指定窗口的工作区的显示设备上下文(DC)的句柄
    - /// 显示设备上下文可以在随后的图形显示界面(GDI)函数中使用,
    - /// 以在窗口的工作区中绘制
    - ///
    - /// - /// - [DllImport("user32.dll", SetLastError = true)] - public static extern IntPtr GetDC(IntPtr hWnd); - - [DllImport("user32.dll")] - public static extern bool ReleaseDC(IntPtr hWnd, IntPtr hDC); - - /// - /// 创建DC - /// - /// - /// - [DllImport("gdi32.dll", EntryPoint = "CreateCompatibleDC", SetLastError = true)] - public static extern IntPtr CreateCompatibleDC([In] IntPtr hdc); - - /// - /// Creates a bitmap compatible with the device that is associated with the specified device context. - /// - /// A handle to a device context. - /// The bitmap width, in pixels. - /// The bitmap height, in pixels. - /// If the function succeeds, the return value is a handle to the compatible bitmap (DDB). If the function fails, the return value is . - [DllImport("gdi32.dll", EntryPoint = "CreateCompatibleBitmap")] - public static extern IntPtr CreateCompatibleBitmap([In] IntPtr hdc, int nWidth, int nHeight); - - /// Selects an object into the specified device context (DC). The new object replaces the previous object of the same type. - /// A handle to the DC. - /// A handle to the object to be selected. - /// - /// If the selected object is not a region and the function succeeds, the return value is a handle to the object being replaced. If the selected object is a region and the function succeeds, the return value is one of the following values. - /// SIMPLEREGION - Region consists of a single rectangle. - /// COMPLEXREGION - Region consists of more than one rectangle. - /// NULLREGION - Region is empty. - /// If an error occurs and the selected object is not a region, the return value is NULL. Otherwise, it is HGDI_ERROR. - /// - /// - /// This function returns the previously selected object of the specified type. An application should always replace a new object with the original, default object after it has finished drawing with the new object. - /// An application cannot select a single bitmap into more than one DC at a time. - /// ICM: If the object being selected is a brush or a pen, color management is performed. - /// - [DllImport("gdi32.dll", EntryPoint = "SelectObject")] - public static extern IntPtr SelectObject([In] IntPtr hdc, [In] IntPtr hgdiobj); - - /// Deletes a logical pen, brush, font, bitmap, region, or palette, freeing all system resources associated with the object. After the object is deleted, the specified handle is no longer valid. - /// A handle to a logical pen, brush, font, bitmap, region, or palette. - /// - /// If the function succeeds, the return value is nonzero. - /// If the specified handle is not valid or is currently selected into a DC, the return value is zero. - /// - /// - /// Do not delete a drawing object (pen or brush) while it is still selected into a DC. - /// When a pattern brush is deleted, the bitmap associated with the brush is not deleted. The bitmap must be deleted independently. - /// - [DllImport("gdi32.dll", EntryPoint = "DeleteObject")] - [return: MarshalAs(UnmanagedType.Bool)] - public static extern bool DeleteObject([In] IntPtr hObject); - - /// - /// 指定的源设备环境区域中的像素进行位块转换,以传送到目标设备环境 - /// - /// Handle to the destination device context. - /// The leftmost x-coordinate of the destination rectangle (in pixels). - /// The topmost y-coordinate of the destination rectangle (in pixels). - /// The width of the source and destination rectangles (in pixels). - /// The height of the source and the destination rectangles (in pixels). - /// Handle to the source device context. - /// The leftmost x-coordinate of the source rectangle (in pixels). - /// The topmost y-coordinate of the source rectangle (in pixels). - /// A raster-operation code. - /// - /// true if the operation succeedes, false otherwise. To get extended error information, call . - /// - [DllImport("gdi32.dll", EntryPoint = "BitBlt", SetLastError = true)] - [return: MarshalAs(UnmanagedType.Bool)] - public static extern bool BitBlt([In] IntPtr hdc, int nXDest, int nYDest, int nWidth, int nHeight, [In] IntPtr hdcSrc, int nXSrc, int nYSrc, TernaryRasterOperations dwRop); - - public enum TernaryRasterOperations : uint - { - SRCCOPY = 0x00CC0020, - SRCPAINT = 0x00EE0086, - SRCAND = 0x008800C6, - SRCINVERT = 0x00660046, - SRCERASE = 0x00440328, - NOTSRCCOPY = 0x00330008, - NOTSRCERASE = 0x001100A6, - MERGECOPY = 0x00C000CA, - MERGEPAINT = 0x00BB0226, - PATCOPY = 0x00F00021, - PATPAINT = 0x00FB0A09, - PATINVERT = 0x005A0049, - DSTINVERT = 0x00550009, - BLACKNESS = 0x00000042, - WHITENESS = 0x00FF0062, - CAPTUREBLT = 0x40000000 //only if WinVer >= 5.0.0 (see wingdi.h) - } - - /// - /// 截图成为BMP - /// - /// 截图的窗口 - /// 扔出BMP执行任务 - /// - public static void CaptureWndImage(IntPtr hWnd, Action action) - { - if (action == null) - throw new ArgumentNullException(nameof(action)); - - var hDC = GetDC(hWnd); - var hMemDC = CreateCompatibleDC(hDC); - if (hMemDC == IntPtr.Zero) - return; - - WindowsAPI.GetClientRect(hWnd, out WindowsAPI.IntRect rcClient); - int width = rcClient.Right - rcClient.Left; - int height = rcClient.Bottom - rcClient.Top; - - var hBitmap = CreateCompatibleBitmap(hDC, width, height); - if (hBitmap != IntPtr.Zero) - { - SelectObject(hMemDC, hBitmap); - if (BitBlt(hMemDC, 0, 0, width, height, - hDC, 0, 0, TernaryRasterOperations.SRCCOPY)) - { - action.Invoke(hBitmap); - } - DeleteObject(hBitmap); - } - DeleteObject(hMemDC); - } -} \ No newline at end of file diff --git a/src/IFoxCAD.Cad.Shared/Copyclip/EmfTool.cs b/src/IFoxCAD.Cad.Shared/Copyclip/EmfTool.cs deleted file mode 100644 index 773cf6d..0000000 --- a/src/IFoxCAD.Cad.Shared/Copyclip/EmfTool.cs +++ /dev/null @@ -1,922 +0,0 @@ -namespace IFoxCAD.Cad; - -using System; -using System.Diagnostics; -using System.Drawing; -using System.Drawing.Imaging; -using System.Text; -using static IFoxCAD.Cad.WindowsAPI; -using Point = System.Drawing.Point; -using Size = System.Drawing.Size; - -// DWORD == uint -// WORD == ushort -// LONG == int - -/* - * Console.WriteLine(Marshal.SizeOf(typeof(PlaceableMetaHeader))); - * Console.WriteLine(Marshal.SizeOf(typeof(WindowsMetaHeader))); - * Console.WriteLine(Marshal.SizeOf(typeof(StandardMetaRecord))); - */ - - -// https://learn.microsoft.com/en-us/windows/win32/api/wingdi/ns-wingdi-metafilepict -// http://www.cppblog.com/zwp/archive/2012/02/25/60225.html -[Serializable] -[StructLayout(LayoutKind.Sequential, Pack = 2)] -public struct MetaFilePict -{ - public MappingModes mm; - public int xExt; - public int yExt; - public IntPtr hMF; //内存图元文件的句柄 -} - -public enum MappingModes : int -{ - MM_TEXT = 1, - MM_LOMETRIC = 2, - MM_HIMETRIC = 3, - MM_LOENGLISH = 4,//逻辑坐标的单位为0.01英寸 - MM_HIENGLISH = 5, - MM_TWIPS = 6, - MM_ISOTROPIC = 7, - MM_ANISOTROPIC = 8, - - //Minimum and Maximum Mapping Mode values - MM_MIN = MM_TEXT, - MM_MAX = MM_ANISOTROPIC, - MM_MAX_FIXEDSCALE = MM_TWIPS, -} - - -//WMF 文件格式: -//https://blog.51cto.com/chenyanxi/803247 -//文件缩放信息:22字节 -[Serializable] -[StructLayout(LayoutKind.Sequential, Pack = 2)] -public struct PlaceableMetaHeader -{ - public uint Key; /* 固定大小以相反顺序出现 9AC6CDD7h */ - public ushort Handle; /* Metafile HANDLE number (always 0) */ - - public short Left; /* Left coordinate in metafile units */ - public short Top; /* Top coordinate in metafile units */ - public short Right; /* Right coordinate in metafile units */ - public short Bottom; /* Bottom coordinate in metafile units */ - - public ushort Inch; /* Number of metafile units per inch */ - public uint Reserved; /* Reserved (always 0) */ - public ushort Checksum; /* Checksum value for previous 10 WORDs */ - - // 微软的wmf文件分为两种一种是标准的图元文件, - // 一种是活动式图元文件,活动式图元文件 与 标准的图元文件 的主要区别是, - // 活动式图元文件包含了图像的原始大小和缩放信息. - /// - /// 是活动式图元文件 - /// - public bool IsActivity => Key == 0x9AC6CDD7; - - /// - /// wmf转为emf
    - ///
    - /// 文件路径 - /// - /// 错误: ;
    - /// 成功: 返回一个增强型图元 emf文件句柄 (位于内存中) - ///
    - /// - public static IntPtr Wmf2Emf(string wmfFile) - { - using FileStream file = new(wmfFile, FileMode.Open, FileAccess.Read, FileShare.ReadWrite); // FileShare才能进c盘 - if (file.Length == 0) - throw new IOException("文件字节0长:" + file); - if (file.Length < 5) - throw new IOException("无法校验文件签名:" + file); - - var fileByte = new byte[file.Length]; - file.Read(fileByte, 0, fileByte.Length); - file.Close(); - - var sWMF = BytesToStruct(fileByte); - // 转为emf的地址 - IntPtr hEMF = IntPtr.Zero; - - // 控制输出的时候跟cad一样带有一个矩形框边界,而不是所有图元的包围盒作为边界 - var mpType = new MetaFilePict - { - mm = MappingModes.MM_ANISOTROPIC, - xExt = sWMF.Right - sWMF.Left, - yExt = sWMF.Bottom - sWMF.Top, - hMF = IntPtr.Zero - }; - - // byte[] 指针偏移 - int iOffset = 0; - if (sWMF.IsActivity) - iOffset = Marshal.SizeOf(typeof(PlaceableMetaHeader)); - - unsafe - { - // 安全指针方法 - //IntPtr fileIntPtr = Marshal.UnsafeAddrOfPinnedArrayElement(fileByte, iOffset); - // 不安全指针方法 - fixed (byte* fileIntPtr = &fileByte[iOffset]) - hEMF = EmfTool.SetWinMetaFileBits( - (uint)fileByte.Length, new IntPtr(fileIntPtr), IntPtr.Zero, new IntPtr(&mpType)); - } - return hEMF; - } -} - -public class Emf -{ - public IntPtr EmfHandle; - - /// - /// 转换wmf到emf - /// - /// - public void Wmf2Emf(string wmfFile) - { - EmfHandle = PlaceableMetaHeader.Wmf2Emf(wmfFile);//emf文件句柄 - } - - /// - /// 获取emf结构 - /// - /// - public EnhMetaHeader CreateEnhMetaHeader() - { - if (EmfHandle == IntPtr.Zero) - throw new ArgumentException(nameof(EmfHandle) + "== IntPtr.Zero"); - return EnhMetaHeader.Create(EmfHandle); - } -} - -//紧接文件缩放信息的是 WMFHEAD, 18字节 -[Serializable] -[StructLayout(LayoutKind.Sequential, Pack = 2)] -public struct WindowsMetaHeader -{ - public ushort FileType; /* Type of metafile (0=memory, 1=disk) */ - public ushort HeaderSize; /* Size of header in WORDS (always 9) */ - public ushort Version; /* Version of Microsoft Windows used */ - public uint FileSize; /* Total size of the metafile in WORDs */ - public ushort NumOfObjects; /* Number of objects in the file */ - public uint MaxRecordSize; /* The size of largest record in WORDs */ - public ushort NumOfParams;    /* Not Used (always 0) */ -} - -//紧接 WMFHEAD 的是 WMFRECORD, 14字节 -[Serializable] -[StructLayout(LayoutKind.Sequential, Pack = 2)] -public struct StandardMetaRecord -{ - public uint Size; /* Total size of the record in WORDs */ - public ushort Function; /* Function number (defined in WINDOWS.H) */ - public ushort[] Parameters;  /* Parameter values passed to function */ -} - -// 文件结构:头记录(ENHMETAHEADER),各记录(ENHMETARECORD),文件结尾(EMR_EOF) -// https://www.cnblogs.com/5iedu/p/4706327.html -[Serializable] -[StructLayout(LayoutKind.Sequential, Pack = 2)] -public struct EnhMetaHeader -{ - [Description("记录类型")] - public uint iType; - [Description("结构大小")] - public int nSize; //注意这个大小是含描述字符串的长度,即等于sizeof(ENHMETAHEADER)+nDescription*2 - [Description("外接矩形(单位是像素)")] - public IntRect rclBounds; - [Description("图片矩形(单位是 0.1 毫米)")] - public IntRect rclFrame; - [Description("文件签名")] - public uint dSignature; - [Description("文件版本")] - public uint nVersion; - [Description("文件尺寸")] - public uint nBytes; - [Description("记录数")] - public uint nRecords; - [Description("句柄数")] - public ushort nHandles; - [Description("保留")] - public ushort sReserved; - [Description("说明文本的长度")] - public uint nDescription; - [Description("说明文本的偏移量")] - public uint offDescription; - [Description("调色板的元素数")] - public uint nPalEntries; - [Description("分辨率(像素)")] - public IntSize szlDevice; - [Description("分辨率(毫米)")] - public IntSize szlMillimeters; - [Description("像素格式的尺寸")] - public uint cbPixelFormat; - [Description("像素格式的起始偏移位置")] - public uint offPixelFormat; - [Description("在不含OpenGL记录时,该值为FALSE")] - public uint bOpenGL; - [Description("参考设备的尺寸(微米)")] - public IntSize szlMicrometers; - - public override string ToString() - { - //var tp = GetType(); - //var sb = new StringBuilder(); - //sb.AppendLine(EnumEx.GetDesc(tp, nameof(iType)) + ":" + iType); - - // 输出json - // NET472 System.Text.Json - var serializer = new System.Web.Script.Serialization.JavaScriptSerializer(); - string jsonString = serializer.Serialize(this); - return jsonString; - } - - /// - /// 通过wmf创建 - /// - /// - /// - public static EnhMetaHeader Create(string wmf) - { - var emf = PlaceableMetaHeader.Wmf2Emf(wmf); - if (emf == IntPtr.Zero) - throw new ArgumentException(nameof(emf)); - return Create(emf); - } - - /// - /// 通过emf指针创建 - /// - /// 参数1的结构体首地址
    - /// 也就是的返回值 - /// - /// - public static EnhMetaHeader Create(IntPtr emf) - { - var len = EmfTool.GetEnhMetaFileHeader(emf, 0, IntPtr.Zero); - if (len == 0) - throw new ArgumentException(nameof(len)); - - IntPtr header = Marshal.AllocHGlobal((int)len); - EmfTool.GetEnhMetaFileHeader(emf, len, header);//这里是切割获取内部的bytes,存放在header - - var result = (EnhMetaHeader)Marshal.PtrToStructure(header, typeof(EnhMetaHeader)); - - Marshal.FreeHGlobal(header); - return result; - } -} - - - -public static class EmfTool -{ - /// - /// 保存 - /// - /// GetEnhMetaFileBits 参数1的结构体首地址 - /// 保存路径 - /// - public static void Save(IntPtr clipTypeData, string file) - { - // 保存emf文件 - // https://blog.csdn.net/tigertianx/article/details/7098490 - var len = EmfTool.GetEnhMetaFileBits(clipTypeData, 0, null!); - if (len != 0) - { - var bytes = new byte[len]; - _ = EmfTool.GetEnhMetaFileBits(clipTypeData, len, bytes); - - using MemoryStream ms1 = new(bytes); - using var bm = Image.FromStream(ms1);//此方法emf保存成任何版本都会变成png - bm.Save(file); - } - } - - /// - /// 返回对一个增强型图元文件的说明 - /// - /// 目标增强型图元文件的句柄 - /// lpszDescription缓冲区的长度 - /// 指定一个预先初始化好的字串缓冲区,准备随同图元文件说明载入; - /// 参考 CreateEnhMetaFile 函数,了解增强型图元文件说明字串的具体格式 - /// - [DllImport("gdi32.dll")] - static extern uint GetEnhMetaFileDescription(IntPtr hemf, uint cchBuffer, [MarshalAs(UnmanagedType.LPStr)] StringBuilder lpDescription); - - /// - /// 获取emf描述 - /// - /// 文件句柄 - /// 描述的内容 - [System.Diagnostics.DebuggerStepThrough] - [System.CodeDom.Compiler.GeneratedCode("InteropSignatureToolkit", "0.9 Beta1")]//初始化时指定生成代码的工具的名称和版本 - public static string? GetEnhMetaFileDescriptionEx(IntPtr clipTypeData) - { - var len = GetEnhMetaFileDescription(clipTypeData, 0, null!); - if (len != 0) - { - StringBuilder desc = new((int)len); - GetEnhMetaFileDescription(clipTypeData, (uint)desc.Capacity, desc); - return desc.ToString(); - } - return null; - } - - public enum DeviceCap : int - { - /// - /// Device driver version - /// - DRIVERVERSION = 0, - /// - /// Device classification - /// - TECHNOLOGY = 2, - /// - /// Horizontal size in millimeters - /// - HORZSIZE = 4, - /// - /// Vertical size in millimeters - /// - VERTSIZE = 6, - /// - /// Horizontal width in pixels - /// - HORZRES = 8, - /// - /// Vertical height in pixels - /// - VERTRES = 10, - /// - /// Number of bits per pixel - /// - BITSPIXEL = 12, - /// - /// Number of planes - /// - PLANES = 14, - /// - /// Number of brushes the device has - /// - NUMBRUSHES = 16, - /// - /// Number of pens the device has - /// - NUMPENS = 18, - /// - /// Number of markers the device has - /// - NUMMARKERS = 20, - /// - /// Number of fonts the device has - /// - NUMFONTS = 22, - /// - /// Number of colors the device supports - /// - NUMCOLORS = 24, - /// - /// Size required for device descriptor - /// - PDEVICESIZE = 26, - /// - /// Curve capabilities - /// - CURVECAPS = 28, - /// - /// Line capabilities - /// - LINECAPS = 30, - /// - /// Polygonal capabilities - /// - POLYGONALCAPS = 32, - /// - /// Text capabilities - /// - TEXTCAPS = 34, - /// - /// Clipping capabilities - /// - CLIPCAPS = 36, - /// - /// Bitblt capabilities - /// - RASTERCAPS = 38, - /// - /// Length of the X leg - /// - ASPECTX = 40, - /// - /// Length of the Y leg - /// - ASPECTY = 42, - /// - /// Length of the hypotenuse - /// - ASPECTXY = 44, - /// - /// Shading and Blending caps - /// - SHADEBLENDCAPS = 45, - - /// - /// Logical pixels inch in X - /// - LOGPIXELSX = 88, - /// - /// Logical pixels inch in Y - /// - LOGPIXELSY = 90, - - /// - /// Number of entries in physical palette - /// - SIZEPALETTE = 104, - /// - /// Number of reserved entries in palette - /// - NUMRESERVED = 106, - /// - /// Actual color resolution - /// - COLORRES = 108, - - // Printing related DeviceCaps. These replace the appropriate Escapes - /// - /// Physical Width in device units - /// - PHYSICALWIDTH = 110, - /// - /// Physical Height in device units - /// - PHYSICALHEIGHT = 111, - /// - /// Physical Printable Area x margin - /// - PHYSICALOFFSETX = 112, - /// - /// Physical Printable Area y margin - /// - PHYSICALOFFSETY = 113, - /// - /// Scaling factor x - /// - SCALINGFACTORX = 114, - /// - /// Scaling factor y - /// - SCALINGFACTORY = 115, - - /// - /// Current vertical refresh rate of the display device (for displays only) in Hz - /// - VREFRESH = 116, - /// - /// Vertical height of entire desktop in pixels - /// - DESKTOPVERTRES = 117, - /// - /// Horizontal width of entire desktop in pixels - /// - DESKTOPHORZRES = 118, - /// - /// Preferred blt alignment - /// - BLTALIGNMENT = 119 - } - - [DllImport("gdi32.dll")] - static extern int GetDeviceCaps(IntPtr hDC, DeviceCap nIndex); - - [DllImport("gdi32.dll")] - static extern int SetMapMode(IntPtr hDC, MappingModes fnMapMode); - - [DllImport("gdi32.dll")] - static extern bool SetViewportOrgEx(IntPtr hDC, int x, int y, Point[] prevPoint); - - [DllImport("gdi32.dll")] - static extern bool SetWindowOrgEx(IntPtr hDC, int x, int y, Point[] prevPoint); - - [DllImport("gdi32.dll")] - static extern bool SetViewportExtEx(IntPtr hDC, int nExtentX, int nExtentY, Size[] prevSize); - - [DllImport("gdi32.dll")] - static extern bool SetWindowExtEx(IntPtr hDC, int nExtentX, int nExtentY, Size[] prevSize); - - [DllImport("Gdi32.dll")] - public static extern int CreatePen(int nPenStyle, int nWidth, int nColor); - - [DllImport("Gdi32.dll")] - public static extern int GetStockObject(int nStockBrush); - - [DllImport("Gdi32.dll")] - public static extern int SelectObject(IntPtr hDC, int hGdiObject); - - [DllImport("Gdi32.dll")] - public static extern int DeleteObject(int hBitmap); - - [DllImport("Gdi32.dll")] - public static extern int MoveToEx(IntPtr hDC, int x, int y, int nPreviousPoint); - - [DllImport("Gdi32.dll")] - public static extern int LineTo(IntPtr hDC, int x, int y); - - [DllImport("Gdi32.dll")] - public static extern int Rectangle(IntPtr hDC, int nLeft, int nTop, int nRight, int nBottom); - - [DllImport("Gdi32.dll")] - public static extern bool DPtoLP(IntPtr hdc, [In, Out] Point[] lpPoints, int nCount); - - - /// - /// 设置emf描述 - /// - /// emf文件句柄 - /// 设置描述 - /// 新的emf指针 - /// - public static void SetEnhMetaFileDescriptionEx(ref IntPtr hMetaFile, string desc) - { - if (hMetaFile == IntPtr.Zero) - throw new ArgumentNullException(nameof(hMetaFile)); - - var emh = EnhMetaHeader.Create(hMetaFile);//emf结构 GetEnhMetaFileHeader - // 创建画布句柄 - IntRect intRect = emh.rclFrame; //new(0, 0, 0, 0); - var hMetaDC = EmfTool.CreateEnhMetaFile(IntPtr.Zero, null!, ref intRect, desc); - if (hMetaDC == IntPtr.Zero) - return; - //SetMapMode(hMetaDC, MappingModes.MM_ANISOTROPIC); // 默认的就是这个模式 - //SetMapMode(hMetaDC, MappingModes.MM_HIMETRIC);//逻辑单位:0.01mm - - // 设置单位 - //var size = new IntSize(0, 0); - //EmfTool.SetWindowExtEx(hMetaDC, 0, 0, ref size); - //EmfTool.SetViewportExtEx(hMetaDC, 0, 0, ref size); - //EmfTool.GetEnhMetaFilePaletteEntries() 统一调色 - //SetViewportOrgEx(hMetaDC, 0, 0, null!);//将视口原点设在左下角 - - // 旧的克隆到新的 - /* - * 第18章 图元文件_18.2 增强型图元文件(emf)(2) - * https://blog.51cto.com/u_15082403/3724715 - * 方案2——利用图像的物理尺寸 - * 通过rclFrame字段(是设备单位:0.01mm)显示出来的刻度尺,这样不管在视频显示器 - * 打印机上,显示出来的刻度尺都较为真实 - */ - //目标设备信息 - int cxMms = GetDeviceCaps(hMetaDC, DeviceCap.HORZSIZE);//宽度(单位:mm) - int cyMms = GetDeviceCaps(hMetaDC, DeviceCap.VERTSIZE);//高度(单位:mm) - var cxArea = cxMms; - var cyArea = cyMms; -#if true2 - int cxPix = GetDeviceCaps(hMetaDC, DeviceCap.HORZRES);//宽度(单位:像素) - int cyPix = GetDeviceCaps(hMetaDC, DeviceCap.VERTRES);//高度(单位:像素) - int cxImage = emh.rclFrame.Right - emh.rclFrame.Left; //单位:0.01mm - int cyImage = emh.rclFrame.Bottom - emh.rclFrame.Top; - - // 设置之后图像就没有拉伸了,但是跑偏了 - //将图元文件大小(0.01mm为单位)转换为像素大小 - cxImage = cxImage * cxPix / cxMms / 100; - cyImage = cyImage * cyPix / cyMms / 100; - - //在指定的矩形区内,水平和垂直居中显示图元文件,同时保证了区域的大小为cxImage和cyImage - int left = (cxArea - cxImage) / 2; - int right = (cxArea + cxImage) / 2; - int top = (cyArea - cyImage) / 2; - int bottom = (cyArea + cyImage) / 2; -#else - cxArea = 0; - cyArea = 0; - SetMapMode(hMetaDC, MappingModes.MM_HIMETRIC);//逻辑单位:0.01mm - SetViewportOrgEx(hMetaDC, 0, cyArea, null!);//将视口原点设在左下角 - var pt = new Point(cxArea, 0); - - int cxImage = emh.rclFrame.Right - emh.rclFrame.Left; //单位:0.01mm - int cyImage = emh.rclFrame.Bottom - emh.rclFrame.Top; - - //在指定的矩形区内,水平和垂直居中显示图元文件,同时保证了区域的大小为cxImage和cyImage - int left = (pt.X - cxImage) / 2; - int right = (pt.X + cxImage) / 2; - int top = (pt.Y + cyImage) / 2; //注意,这里与前面例子不同 - int bottom = (pt.Y - cyImage) / 2; //注意,这里与前面例子不同 -#endif - var rect = new IntRect(left, top, right, bottom); - - // 图像拉伸了 - //bool pef = EmfTool.EnumEnhMetaFile(hMetaDC, hMetaFile, IntPtr.Zero, IntPtr.Zero, ref emhValue.rclFrame);// 这个失败 - bool pef = EmfTool.PlayEnhMetaFile(hMetaDC, hMetaFile, ref rect); - if (!pef) - { - DeleteObject(hMetaDC); - Debugger.Break(); - return; - } - // 删除旧的图元文件句柄,返回新的 - var del = EmfTool.DeleteEnhMetaFile(hMetaFile); - if (del) - hMetaFile = EmfTool.CloseEnhMetaFile(hMetaDC); - } - - [DllImport("gdi32.dll", EntryPoint = "GetEnhMetaFileHeader")] - public static extern uint GetEnhMetaFileHeader(IntPtr hemf, uint cbBuffer, IntPtr /*ENHMETAHEADER*/ lpemh); - - - /// - /// 将一个标准Windows图元文件转换成增强型图元文件 - /// - /// 数组的长度 - /// - /// 数组包含了标准图元文件数据.
    - /// 常用 GetMetaFileBitsEx 或 GetWinMetaFileBits 函数获得 - /// - /// - /// 用于决定原始格式及图元文件分辨率的一个参考设备场景;
    - /// 采用显示器分辨率为: - /// - /// - /// 定义一个图元文件附加参考信息的结构
    - /// 为null时,会假定使用当前显示器的 MM_ANISOTROPIC 映射模式 - /// - /// - /// 错误: ;
    - /// 成功: 返回一个增强型图元emf文件的指针(位于内存中) - ///
    - [DllImport("gdi32.dll", EntryPoint = "SetWinMetaFileBits")] - public static extern IntPtr SetWinMetaFileBits(uint nSize, IntPtr lpMeta16Data, IntPtr hdcRef, IntPtr lpMFP); - /// - /// 获取矢量图的byte - /// - /// - /// - /// - /// - [DllImport("gdi32.dll")] - public static extern uint GetEnhMetaFileBits(IntPtr hemf, uint cbBuffer, byte[] lpbBuffer); - /// - /// byte转换矢量图 - /// - /// - /// - /// - [DllImport("gdi32.dll")] - public static extern IntPtr SetEnhMetaFileBits(uint cbBuffer, byte[] lpBuffer); - /// - /// 删除矢量图 - /// - /// - /// - [DllImport("gdi32.dll")] - public static extern bool DeleteEnhMetaFile(IntPtr hemf); - - /// - /// 创建emf
    - /// https://www.cnblogs.com/5iedu/p/4706327.html - ///
    - /// 参考设备环境,null以整个屏幕为参考 - /// 指定文件名时,创建磁盘文件(.EMF),为null时创建内存图元文件 - /// 用于描述图元文件的大小和位置(以0.01mm为单位),可用它精确定义图元文件的物理尺寸 - /// 对图元文件的一段说明.包括创建应用程序的名字、一个NULL字符、对图元文件的一段说明以及两个NULL字符. - /// 返回画布句柄DC(图元文件句柄得调用 CloseEnhMetaFile 函数) - [DllImport("gdi32.dll", SetLastError = true)] - public static extern IntPtr CreateEnhMetaFile(IntPtr hdcRef, string szFilename, ref IntRect lpRect, string lpDescription); - - [DllImport("gdi32.dll", SetLastError = true)] - public static extern bool DeleteObject(IntPtr hdcRef); - - /// - /// 在指定的设备场景中画一个增强型图元文件;
    - /// 与标准图元文件不同,完成回放后,增强型图元文件会恢复设备场景以前的状态 - ///
    - /// 画布句柄 - /// 欲描绘的emf的图元文件句柄 - /// 指定显示区域(逻辑单位)GDI会缩放图像以适应该矩形范围 - /// - [DllImport("gdi32.dll")] - public static extern bool PlayEnhMetaFile(IntPtr hdcRef, IntPtr hemf, ref IntRect lpRect); - - // https://blog.csdn.net/hongke457546235/article/details/17404715 - /// - /// 逻辑单位设置窗口单位 - /// {只能在 MM_ISOTROPIC 或 MM_ANISOTROPIC 模式下使用下面两个函数} - /// - /// 画布句柄 - /// 以逻辑单位表示的新窗口区域的高度 - /// 以逻辑单位表示的新窗口区域的宽度 - /// 保存函数调用前窗口区域尺寸的SIZE结构地址,NULL则表示忽略调用前的尺寸 - [DllImport("gdi32.dll")] - public static extern bool SetWindowExtEx(IntPtr hdcRef, int nHeight, int nWidth, ref IntSize lpSize); - - /// - /// 视口区域的定义 - /// {只能在 MM_ISOTROPIC 或 MM_ANISOTROPIC 模式下使用下面两个函数} - /// - /// - /// - /// - /// - [DllImport("gdi32.dll")] - public static extern bool SetViewportExtEx(IntPtr hdcRef, int nHeight, int nWidth, ref IntSize lpSize); - - /// - /// 旧emf绘制新的hdcEMF中(即回放) - /// - /// 画布句柄 - /// 图元文件句柄 - /// 回调函数 - /// 传给回调函数的额外参数 - /// 在指定的矩形区内显示图元文件 - /// - [DllImport("gdi32.dll", SetLastError = true)] - public static extern bool EnumEnhMetaFile(IntPtr hdcRef, IntPtr hmf, IntPtr proc, IntPtr procParam, ref IntRect lpRect); - - /// - /// 返回图元文件句柄 - /// - /// 画布句柄 - [DllImport("gdi32.dll", SetLastError = true)] - public static extern IntPtr CloseEnhMetaFile(IntPtr hdcRef); - - // https://zhidao.baidu.com/question/646739770512964165/answer/1616737219.html?qq-pf-to=pcqq.c2c - //16位的函数 - [DllImport("gdi32.dll")] - public static extern IntPtr GetMetaFile(string path); - //32位的函数 - [DllImport("gdi32.dll")] - public static extern IntPtr GetEnhMetaFile(string path); - - - - /// - /// EMF保存到文件或者路径 - /// - /// EMF要复制的增强型图元文件的句柄 - /// 指向目标文件名称的指针,为NULL则将源图元文件复制到内存中 - /// - [DllImport("gdi32.dll")] - public static extern IntPtr CopyEnhMetaFile(IntPtr hemfSrc, string? lpszFile); - - /// - /// 矢量图保存 - /// - /// - /// - public static void SaveMetaFile(this Metafile file, string emfName) - { - //MetafileHeader metafileHeader = file.GetMetafileHeader(); //这句话可要可不要 - IntPtr h = file.GetHenhmetafile(); - CopyEnhMetaFile(h, emfName); - DeleteEnhMetaFile(h); - } - - /// - /// 矢量图 转换 byte[] - /// - /// - /// - public static byte[]? ToByteArray(this Image image) - { - return ToByteArray((Metafile)image); - } - - // https://www.pinvoke.net/default.aspx/gdi32.getenhmetafile - /// - /// 矢量图 转换 byte[] - /// - /// - /// - public static byte[]? ToByteArray(this Metafile mf) - { - byte[]? arr = null; - IntPtr handle = mf.GetHenhmetafile(); - if (handle != IntPtr.Zero) - { - var size = GetEnhMetaFileBits(handle, 0, null!); - if (size != 0) - { - arr = new byte[size]; - _ = GetEnhMetaFileBits(handle, size, arr); - } - DeleteEnhMetaFile(handle); - } - return arr; - } - - /// - /// byte[] 转换 矢量图 - /// - /// - /// 返回值true删除句柄 - /// - public static void ToMetafile(byte[] data, Func task) - { - if (task == null) - throw new ArgumentNullException(nameof(task)); - - IntPtr hemf = SetEnhMetaFileBits((uint)data.Length, data); - using var mf = new Metafile(hemf, true); - if (task.Invoke(mf)) // 对图像进行操作,就不能进行删除句柄 - DeleteEnhMetaFile(hemf); - } - - -#if false - /// - /// c#获取wmf方式 - /// - /// - /// - public static IntPtr GetMetafile(string wmfFile) - { - using FileStream file = new(wmfFile, FileMode.Open, FileAccess.Read, FileShare.ReadWrite); // FileShare才能进c盘 - var hEMF2 = IntPtr.Zero; - - using Metafile mf = new(file); - var hEMF = mf.GetHenhmetafile(); - if (hEMF != IntPtr.Zero) - hEMF2 = CopyEnhMetaFile(hEMF, null);// 这句: 句柄无效..cad的wmf文件不识别 - //EmfTool.DeleteEnhMetaFile(hEMF);//托管类应该是封装好的 - return hEMF2; - } - - - /* - * // 这是c#写入wmf流程 - * // c#画的wmf格式是可以的...用这样方式生成的就是可以写剪贴板 - * WindowsAPI.GetClientRect(doc.Window.Handle, out IntRect rcClient); - * int width = rcClient.Right - rcClient.Left; - * int height = rcClient.Bottom - rcClient.Top; - * EmfTool.Export(wmf, width, height);//cad的命令wmfin:不能导入c#自绘的 - * - * //c#方法,但是它读取不了cad的wmf - * wmfMeta = EmfTool.GetMetafile(wmf); - */ - - /// - /// 导出为 Emf 或 Wmf 文件 - /// 相关链接 - /// - /// 文件路径 - /// 窗口宽度 - /// 窗口高度 - /// 是否成功 - public static bool Export(string filePath, int width, int height) - { - try - { - using Bitmap bmp = new(width, height); - using Graphics gs = Graphics.FromImage(bmp); - using Metafile mf = new(filePath, gs.GetHdc()); - using Graphics g = Graphics.FromImage(mf); - Draw(g); - g.Save(); - return true; - } - catch { return false; } - } - - - /// - /// 绘制图形 - /// - /// 用于绘图的Graphics对象 - static void Draw(Graphics g) - { - HatchBrush hb = new(HatchStyle.LightUpwardDiagonal, Color.Black, Color.White); - - g.FillEllipse(Brushes.Gray, 10f, 10f, 200, 200); - g.DrawEllipse(new Pen(Color.Black, 1f), 10f, 10f, 200, 200); - - g.FillEllipse(hb, 30f, 95f, 30, 30); - g.DrawEllipse(new Pen(Color.Black, 1f), 30f, 95f, 30, 30); - - g.FillEllipse(hb, 160f, 95f, 30, 30); - g.DrawEllipse(new Pen(Color.Black, 1f), 160f, 95f, 30, 30); - - g.FillEllipse(hb, 95f, 30f, 30, 30); - g.DrawEllipse(new Pen(Color.Black, 1f), 95f, 30f, 30, 30); - - g.FillEllipse(hb, 95f, 160f, 30, 30); - g.DrawEllipse(new Pen(Color.Black, 1f), 95f, 160f, 30, 30); - - g.FillEllipse(Brushes.Blue, 60f, 60f, 100, 100); - g.DrawEllipse(new Pen(Color.Black, 1f), 60f, 60f, 100, 100); - - g.FillEllipse(Brushes.BlanchedAlmond, 95f, 95f, 30, 30); - g.DrawEllipse(new Pen(Color.Black, 1f), 95f, 95f, 30, 30); - - g.DrawRectangle(new Pen(Brushes.Blue, 0.1f), 6, 6, 208, 208); - - g.DrawLine(new Pen(Color.Black, 0.1f), 110f, 110f, 220f, 25f); - g.DrawString("剖面图", new Font("宋体", 9f), Brushes.Green, 220f, 20f); - } -#endif -} \ No newline at end of file diff --git a/src/IFoxCAD.Cad.Shared/Copyclip/TagClipboardInfo.cs b/src/IFoxCAD.Cad.Shared/Copyclip/TagClipboardInfo.cs deleted file mode 100644 index 272754a..0000000 --- a/src/IFoxCAD.Cad.Shared/Copyclip/TagClipboardInfo.cs +++ /dev/null @@ -1,659 +0,0 @@ -namespace IFoxCAD.Cad; - -using System; -using System.Diagnostics; -using System.Text; -using static IFoxCAD.Cad.WindowsAPI; - -public class ClipboardEnv -{ - // 0x01 将r17写死,代表每个cad版本都去找它,实现不隔离cad版本 - public static string CadVer = "AutoCAD.r17"; - // 0x02 当前版本在r17找不到的时候找,避免按需加载插件的时候无法获取剪贴板 - public static string CadCurrentVer = $"AutoCAD.r{Acap.Version.Major}"; -} - -/// -/// ARX剪贴板结构 -/// -[Serializable] -[StructLayout(LayoutKind.Sequential, Pack = 8, CharSet = CharSet.Unicode/*此参数将导致260*2*/)] -public struct TagClipboardInfo : IEquatable -{ - #region 字段,对应arx结构的,不要改动,本结构也不允许再加字段 - [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 260)] - public string szTempFile; // 临时文件夹的dwg文件 - [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 260)] - public string szSourceFile; // 文件名从中做出选择..是不是指定块表记录? - [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 4)] - public string szSignature; - public int nFlags; // kbDragGeometry: 从AutoCAD拖动几何图形 - public Point3D dptInsert; // 插入点的原始世界坐标' - public IntRect rectGDI; // GDI coord 选择集的边界矩形 - public IntPtr mpView; // 用于验证这个对象是在这个视图中创建的 (HWND*) - public int dwThreadId; // AutoCAD thread 创建数据对象 - public int nLen; // 下一段的长度的数据,如果有的话,从chData - public int nType; // 类型的数据,如果有(eExpandedClipDataTypes) - [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 1)] - public string chData; // 数据的开始,如果有 - #endregion - - #region 属性,可以改动 - public string File => szTempFile; - public Point3d Point => dptInsert; - -#pragma warning disable CA2211 // 非常量字段应当不可见 - public static IntPtr AcadDwgview - = IntPtr.Zero; - //= AcedGetAcadDwgview(); // c#需要收集这个函数,我先不写,免得中间版本挂了 - - public static int MainWindowThreadId = - (int)WindowsAPI.GetWindowThreadProcessId(Acap.MainWindow.Handle, out uint processId); -#pragma warning restore CA2211 // 非常量字段应当不可见 - #endregion - - #region 构造 - /// - /// cad剪贴板 - /// - /// 临时dwg的保存路径 - /// 粘贴点 - public TagClipboardInfo(string tmpFile, Point3d insert) - { - szTempFile = tmpFile; - szSourceFile = string.Empty; - szSignature = "R15"; //恒定是这个 - nFlags = 0; - dptInsert = insert; - rectGDI = IntRect.Zero; - nLen = 0; - nType = 0; - chData = string.Empty; - - // mpView threadid 可能是用来删除的,用于剪贴板回调清理资源时候判断信息 - mpView = AcadDwgview; - dwThreadId = MainWindowThreadId; - } - #endregion - - #region 方法 - public override string ToString() - { - var sb = new StringBuilder(); - sb.AppendLine($"szTempFile:{szTempFile}"); - sb.AppendLine($"szSourceFile:{szSourceFile}"); - sb.AppendLine($"szSignature:{szSignature}"); - sb.AppendLine($"nFlags:{nFlags}"); - sb.AppendLine($"dptInsert:{dptInsert}"); - sb.AppendLine($"rectGDI:{rectGDI}"); - sb.AppendLine($"mpView:{mpView}"); - sb.AppendLine($"dwThreadId:{dwThreadId}"); - sb.AppendLine($"nLen:{nLen}"); - sb.AppendLine($"nType:{nType}"); - sb.AppendLine($"chData:{chData}"); - return sb.ToString(); - } - #endregion - - #region 测试大小 - void GetSize() - { - var v_1 = Marshal.OffsetOf(typeof(TagClipboardInfo), nameof(TagClipboardInfo.szTempFile)).ToInt32(); - var v_2 = Marshal.OffsetOf(typeof(TagClipboardInfo), nameof(TagClipboardInfo.szSourceFile)).ToInt32(); - var v_3 = Marshal.OffsetOf(typeof(TagClipboardInfo), nameof(TagClipboardInfo.szSignature)).ToInt32(); - var v_4 = Marshal.OffsetOf(typeof(TagClipboardInfo), nameof(TagClipboardInfo.nFlags)).ToInt32(); - var v_5 = Marshal.OffsetOf(typeof(TagClipboardInfo), nameof(TagClipboardInfo.dptInsert)).ToInt32(); - var v_6 = Marshal.OffsetOf(typeof(TagClipboardInfo), nameof(TagClipboardInfo.rectGDI)).ToInt32(); - var v_7 = Marshal.OffsetOf(typeof(TagClipboardInfo), nameof(TagClipboardInfo.mpView)).ToInt32(); - var v_8 = Marshal.OffsetOf(typeof(TagClipboardInfo), nameof(TagClipboardInfo.dwThreadId)).ToInt32(); - var v_9 = Marshal.OffsetOf(typeof(TagClipboardInfo), nameof(TagClipboardInfo.nLen)).ToInt32(); - var v_10 = Marshal.OffsetOf(typeof(TagClipboardInfo), nameof(TagClipboardInfo.nType)).ToInt32(); - var v_11 = Marshal.OffsetOf(typeof(TagClipboardInfo), nameof(TagClipboardInfo.chData)).ToInt32(); - var v_12 = Marshal.SizeOf(typeof(TagClipboardInfo)); //1120 - - var v_a = Marshal.SizeOf(typeof(Point3D));//24 - var v_b = Marshal.SizeOf(typeof(IntRect));//16 - } - #endregion - - #region 视口指针 - /* - [CommandMethod(nameof(Test_AcedGetAcadDwgview))] - public void testAcedGetAcadDwgview() - { - var dm = Acap.DocumentManager; - var doc = dm.MdiActiveDocument; - var ed = doc.Editor; - - var a = AcedGetAcadDwgview().ToString(); //自动执行的时候就存在了 - var b = ed.CurrentViewportObjectId.ToString(); - Debugx.Printl("a == b:" + a == b);//不对 - - var tab = ed.GetCurrentView(); - var c = tab.ObjectId.ToString(); - Debugx.Printl("a == c:" + a == c);//不对 - } - */ - - /// - /// 获取视口指针 - /// -#if NET35 - [DllImport("acad.exe", EntryPoint = "?acedGetAcadDwgView@@YAPAVCView@@XZ")] //acad08 -#else - [DllImport("acad.exe", EntryPoint = "?acedGetAcadDwgView@@YAPEAVCView@@XZ")]//acad21 -#endif - static extern IntPtr AcedGetAcadDwgview(); - #endregion - - #region 重载运算符_比较 - public bool Equals(TagClipboardInfo other) - { - return - szTempFile == other.szTempFile && - szSourceFile == other.szSourceFile && - szSignature == other.szSignature && - nFlags == other.nFlags && - dptInsert == other.dptInsert && - rectGDI == other.rectGDI && - mpView == other.mpView && - dwThreadId == other.dwThreadId && - nLen == other.nLen && - nType == other.nType && - chData == other.chData; - } - public static bool operator !=(TagClipboardInfo a, TagClipboardInfo b) - { - return !(a == b); - } - public static bool operator ==(TagClipboardInfo a, TagClipboardInfo b) - { - return a.Equals(b); - } - public override bool Equals(object obj) - { - return obj is TagClipboardInfo info && Equals(info); - } - public override int GetHashCode() - { - return - szTempFile.GetHashCode() ^ - szSourceFile.GetHashCode() ^ - szSignature.GetHashCode() ^ - nFlags ^ - dptInsert.GetHashCode() ^ - rectGDI.GetHashCode() ^ - mpView.GetHashCode() ^ - dwThreadId ^ - nLen ^ - nType ^ - chData.GetHashCode(); - } - - public IntPtr CloneToPtr() - { - var lParam = Marshal.AllocHGlobal(Marshal.SizeOf(this)); - if (lParam != IntPtr.Zero) - Marshal.StructureToPtr(this, lParam, true); - return lParam; - } - #endregion -} - - -/* - * OLE 剪贴板说明 https://blog.csdn.net/chinabinlang/article/details/9815495 - * 写入时候注意: - * 0x01 c#自带的是com剪贴板 - * 最好不要使用,它不能在已经打开的剪贴板中使用, - * 也无法写入多个cf对象,也就是复制bitmap的时候会覆盖cad图元 - * Clipboard.SetImage(bitmap); - * 0x02 - * 剪贴板写入各种类型 https://blog.csdn.net/glt3953/article/details/8808262 - * - */ - -public partial class ClipTool -{ - /// - /// 侦听剪贴板 - /// - /// - /// - [DllImport("user32.dll", SetLastError = true)] - public static extern bool AddClipboardFormatListener(IntPtr hWnd); - /// - /// 移除侦听剪贴板 - /// - /// - /// - [DllImport("user32.dll", SetLastError = true)] - public static extern bool RemoveClipboardFormatListener(IntPtr hWnd); - /// - /// 将CWnd加入一个窗口链 - /// 每当剪贴板的内容发生变化时,就会通知这些窗口 - /// - /// 句柄 - /// 返回剪贴板观察器链中下一个窗口的句柄 - [DllImport("User32.dll")] - public static extern int SetClipboardViewer(IntPtr hWndNewViewer); - /// - /// 从剪贴板链中移出的窗口句柄 - /// - /// 从剪贴板链中移出的窗口句柄 - /// hWndRemove的下一个在剪贴板链中的窗口句柄 - /// 如果成功,非零;否则为0。 - [DllImport("User32.dll", CharSet = CharSet.Auto)] - public static extern bool ChangeClipboardChain(IntPtr hWndRemove, IntPtr hWndNewNext); - - - /// - /// 开启剪贴板
    - /// 如果另一个窗口已经打开剪贴板,函数会失败.每次成功调用后都应有调用. - ///
    - /// - /// - [DllImport("user32.dll", SetLastError = true)] - static extern bool OpenClipboard(IntPtr hWndNewOwner); - /// - /// 关闭剪贴板 - /// - /// - [DllImport("user32.dll", SetLastError = true)] - public static extern bool CloseClipboard(); - /// - /// 根据数据格式获取剪贴板 - /// - /// 数据格式名称 - /// - [DllImport("user32.dll", SetLastError = true)] - public static extern uint RegisterClipboardFormat(string lpszFormat); - /// - /// 获取剪贴板 - /// - /// 通常为但是cad有自己的位码 - /// - [DllImport("user32.dll", SetLastError = true)] - public static extern IntPtr GetClipboardData(uint uFormat); - /// - /// 设置剪贴板 - /// - /// 通常为但是cad有自己的位码 - /// 指定具有指定格式的数据的句柄,
    - /// 该参数为空则为延迟提交:
    - /// 有其他程序对剪切板中的数据进行请求时,该程序才会将指定格式的数据写入到剪切板中. - /// - /// - [DllImport("user32.dll", SetLastError = true)] - public static extern IntPtr SetClipboardData(uint uFormat, IntPtr hMem); - /// - /// 清空剪切板并释放剪切板内数据的句柄 - /// - /// - [DllImport("user32.dll", SetLastError = true)] - public static extern bool EmptyClipboard(); - /// - /// 枚举剪贴板内数据类型 - /// - /// - /// - [DllImport("user32.dll", SetLastError = true)] - public static extern uint EnumClipboardFormats(uint format); - - - /// - /// 打开剪贴板
    - /// 写入之前必须清空,
    - /// 否则将导致发送 WM_DESTROYCLIPBOARD 消息到上一次剪贴板拥有者释放资源
    - /// 所以写入的时候必须一次性写入多个cf
    - ///
    - /// 接收返回的栈空间指针用于释放 - /// true写入,false读取 - /// - /// - [System.Diagnostics.DebuggerStepThrough] - public static bool OpenClipboardTask(bool isWrite, Action action) - { - if (action == null) - throw new ArgumentNullException(nameof(action)); - - bool openFlag = false; - try - { - openFlag = OpenClipboard(IntPtr.Zero); - if (!openFlag) - return false; - if (isWrite) - EmptyClipboard(); - action.Invoke(); - } - catch (Exception e) - { - Debugger.Break(); - Debugx.Printl(e.Message); - } - finally - { - if (openFlag) - CloseClipboard(); - } - return openFlag; - } - - /// - /// 获取剪贴板 - /// - /// 剪贴板的索引名 - /// 返回的结构 - public static bool GetClipboard(string clipKey, out T? tag) - { - bool locked = false; - T? result = default; - - ClipTool.OpenClipboardTask(false, () => { - // 读取剪贴板的指定数据 - var clipKeyFormat = RegisterClipboardFormat(clipKey);//ClipboardEnv.CadVer - var clipTypeData = GetClipboardData(clipKeyFormat); - - // 剪贴板的数据拷贝进去结构体中,会依照数据长度进行拷贝 - locked = WindowsAPI.GlobalLockTask(clipTypeData, ptr => { - // 非托管内存块->托管对象 - result = (T)Marshal.PtrToStructure(ptr, typeof(T)); - }); - }); - - tag = result; - return locked; - } -} - -#if true2 -// 无法备份emf内容 -// https://blog.csdn.net/vencon_s/article/details/46345083 -public static class ClipEx -{ - /// - /// 剪贴板数据保存目标数据列表 - /// - static readonly List _bytes = new(); - /// - /// 剪贴板数据类型列表 - /// - static readonly List _formats = new(); - - /// - /// 遍历剪贴板保存内容 - /// - /// true成功,false失败 - public static bool SaveClip() - { - bool result = ClipTool.OpenClipboardTask(false, free => { - _bytes.Clear(); - _formats.Clear(); - - uint cf = 0; - while (true) - { - cf = ClipTool.EnumClipboardFormats(cf);// 枚举剪贴板所有数据类型 - if (cf == 0) - break; - - _formats.Add(cf); - IntPtr clipTypeData = ClipTool.GetClipboardData(cf); - var locked = WindowsAPI.GlobalLockTask(clipTypeData, prt => { - uint size = WindowsAPI.GlobalSize(clipTypeData); - if (size > 0) - { - var buffer = new byte[size]; - Marshal.Copy(prt, buffer, 0, buffer.Length);// 将剪贴板数据保存到自定义字节数组 - _bytes.Add(buffer); - } - }); - } - }); - if (result) - result = _formats.Count > 0; - return result; - } - - /// - /// 恢复保存的数据 - /// - /// true成功,false失败 - public static bool RestoreClip() - { - if (_formats.Count <= 0) - return false; - - bool result = ClipTool.OpenClipboardTask(true, free => { - for (int i = 0; i < _formats.Count; i++) - { - int size = _bytes[i].Length; - IntPtr structPtr = Marshal.AllocHGlobal(size); - if (size > 0) - { - Marshal.Copy(_bytes[i], 0, structPtr, size); - ClipTool.SetClipboardData(_formats[i], structPtr); - } - } - }); - - if (result) - result = _formats.Count > 0; - return result; - } -} -#endif - - - -/// -/// 剪贴板的CF,也就是它的key -/// -public enum ClipboardFormat : uint -{ - /// - /// Text format. Each line ends with a carriage return/linefeed (CR-LF) combination. A null character signals - /// the end of the data. Use this format for ANSI text. - /// - CF_TEXT = 1, - - /// - /// A handle to a bitmap (HBITMAP). - /// - CF_BITMAP = 2, - - /// - /// Handle to a metafile picture format as defined by the METAFILEPICT structure. When passing a - /// CF_METAFILEPICT handle by means of DDE, the application responsible for deleting hMem should - /// also free the metafile referred to by the CF_METAFILEPICT handle. - /// - CF_METAFILEPICT = 3, - - /// - /// Microsoft Symbolic Link (SYLK) format. - /// - CF_SYLK = 4, - - /// - /// Software Arts' Data Interchange Format. - /// - CF_DIF = 5, - - /// - /// Tagged-image file format. - /// - CF_TIFF = 6, - - /// - /// Text format containing characters in the OEM character set. Each line ends with a carriage return/linefeed - /// (CR-LF) combination. A null character signals the end of the data. - /// - CF_OEMTEXT = 7, - - /// - /// A memory object containing a BITMAPINFO structure followed by the bitmap bits. - /// - CF_DIB = 8, - - /// - /// Handle to a color palette. Whenever an application places data in the clipboard that depends on or assumes - /// a color palette, it should place the palette on the clipboard as well. If the clipboard contains data in - /// the (logical color palette) format, the application should use the - /// SelectPalette and RealizePalette functions to realize (compare) any other data in the - /// clipboard against that logical palette. When displaying clipboard data, the clipboard always uses as its - /// current palette any object on the clipboard that is in the CF_PALETTE format. - /// - CF_PALETTE = 9, - - /// - /// Data for the pen extensions to the Microsoft Windows for Pen Computing. - /// - CF_PENDATA = 10, - - /// - /// Represents audio data more complex than can be represented in a CF_WAVE standard wave format. - /// - CF_RIFF = 11, - - /// - /// Represents audio data in one of the standard wave formats, such as 11 kHz or 22 kHz PCM. - /// - CF_WAVE = 12, - - /// - /// Unicode text format. Each line ends with a carriage return/linefeed (CR-LF) combination. A null character - /// signals the end of the data. - /// - CF_UNICODETEXT = 13, - - /// - /// A handle to an enhanced metafile (HENHMETAFILE). - /// - CF_ENHMETAFILE = 14, - - /// - /// A handle to type HDROP that identifies a list of files. An application can retrieve information - /// about the files by passing the handle to the DragQueryFile function. - /// - CF_HDROP = 15, - - /// - /// The data is a handle to the locale identifier associated with text in the clipboard. When you close the - /// clipboard, if it contains CF_TEXT data but no CF_LOCALE data, the system automatically sets - /// the CF_LOCALE format to the current input language. You can use the CF_LOCALE format to - /// associate a different locale with the clipboard text. - /// An application that pastes text from the clipboard can retrieve this format to determine which character - /// set was used to generate the text. - /// Note that the clipboard does not support plain text in multiple character sets. To achieve this, use a - /// formatted text data type such as RTF instead. - /// The system uses the code page associated with CF_LOCALE to implicitly convert from - /// to . Therefore, the correct code page table is used for - /// the conversion. - /// - CF_LOCALE = 16, - - /// - /// A memory object containing a BITMAPV5HEADER structure followed by the bitmap color space - /// information and the bitmap bits. - /// - CF_DIBV5 = 17, - - /// - /// Owner-display format. The clipboard owner must display and update the clipboard viewer window, and receive - /// the , , - /// , , and - /// messages. The hMem parameter must be null. - /// - CF_OWNERDISPLAY = 0x0080, - - /// - /// Text display format associated with a private format. The hMem parameter must be a handle to data - /// that can be displayed in text format in lieu of the privately formatted data. - /// - CF_DSPTEXT = 0x0081, - - /// - /// Bitmap display format associated with a private format. The hMem parameter must be a handle to - /// data that can be displayed in bitmap format in lieu of the privately formatted data. - /// - CF_DSPBITMAP = 0x0082, - - /// - /// Metafile-picture display format associated with a private format. The hMem parameter must be a - /// handle to data that can be displayed in metafile-picture format in lieu of the privately formatted data. - /// - CF_DSPMETAFILEPICT = 0x0083, - - /// - /// Enhanced metafile display format associated with a private format. The hMem parameter must be a - /// handle to data that can be displayed in enhanced metafile format in lieu of the privately formatted data. - /// - CF_DSPENHMETAFILE = 0x008E, - - /// - /// Start of a range of integer values for application-defined GDI object clipboard formats. The end of the - /// range is . Handles associated with clipboard formats in this range are not - /// automatically deleted using the GlobalFree function when the clipboard is emptied. Also, when using - /// values in this range, the hMem parameter is not a handle to a GDI object, but is a handle allocated - /// by the GlobalAlloc function with the GMEM_MOVEABLE flag. - /// - CF_GDIOBJFIRST = 0x0300, - - /// - /// See . - /// - CF_GDIOBJLAST = 0x03FF, - - /// - /// Start of a range of integer values for private clipboard formats. The range ends with - /// . Handles associated with private clipboard formats are not freed - /// automatically, the clipboard owner must free such handles, typically in response to the - /// message. - /// - CF_PRIVATEFIRST = 0x0200, - - /// - /// See . - /// - CF_PRIVATELAST = 0x02FF, -} - -#if true2 -// arx剪贴板头文件的枚举 -enum eClipInfoFlags -{ - kbDragGeometry = 0x01, -}; - -enum eXrefType -{ - kXrefTypeAttach = 1, - kXrefTypeOverlay = 2 -}; - -enum eExpandedClipDataTypes -{ - kDcPlotStyles = 1, - kDcXrefs = 2, - kDcLayouts = 3, - kDcBlocks = 4, - kDcLayers = 5, - kDcDrawings = 6, - kDcLinetypes = 7, - kDcTextStyles = 8, - kDcDimStyles = 9, - kDcBlocksWithAttdef = 10, - //#ifdef ADCHATCH - kDcHatches = 11, - //#endif - kTpXrefs = 12, - kTpImages = 13, - kTpTable = 14, - kDcTableStyles = 15, - kDcMultileaderStyles = 16, - kDcVisualStyles = 17, - kDcSectionViewStyles = 18, - kDcDetailViewStyles = 19, -}; -#endif \ No newline at end of file diff --git a/src/IFoxCAD.Cad.Shared/ExtensionMethod/BulgeVertexWidth.cs b/src/IFoxCAD.Cad.Shared/ExtensionMethod/BulgeVertexWidth.cs deleted file mode 100644 index 1f9e08d..0000000 --- a/src/IFoxCAD.Cad.Shared/ExtensionMethod/BulgeVertexWidth.cs +++ /dev/null @@ -1,85 +0,0 @@ -namespace IFoxCAD.Cad; - -/// -/// 多段线的顶点,凸度,头宽,尾宽 -/// -[Serializable] -public class BulgeVertexWidth -{ - /// - /// 顶点X - /// - public double X; - /// - /// 顶点Y - /// - public double Y; - /// - /// 凸度 - /// - public double Bulge; - /// - /// 头宽 - /// - public double StartWidth; - /// - /// 尾宽 - /// - public double EndWidth; - - public Point2d Vertex => new(X, Y); - - public BulgeVertexWidth() { } - - /// - /// 多段线的顶点,凸度,头宽,尾宽 - /// - public BulgeVertexWidth(double vertex_X, double vertex_Y, - double bulge = 0, - double startWidth = 0, - double endWidth = 0) - { - X = vertex_X; - Y = vertex_Y; - Bulge = bulge; - StartWidth = startWidth; - EndWidth = endWidth; - } - - /// - /// 多段线的顶点,凸度,头宽,尾宽 - /// - public BulgeVertexWidth(Point2d vertex, - double bulge = 0, - double startWidth = 0, - double endWidth = 0) - : this(vertex.X, vertex.Y, bulge, startWidth, endWidth) - { } - - /// - /// 多段线的顶点,凸度,头宽,尾宽 - /// - public BulgeVertexWidth(BulgeVertex bv) - : this(bv.Vertex.X, bv.Vertex.Y, bv.Bulge) - { } - - /// - /// 多段线的顶点,凸度,头宽,尾宽 - /// - /// 多段线 - /// 子段编号 - public BulgeVertexWidth(Polyline pl, int index) - { - var pt = pl.GetPoint2dAt(index);// 这里可以3d - X = pt.X; - Y = pt.Y; - Bulge = pl.GetBulgeAt(index); - StartWidth = pl.GetStartWidthAt(index); - EndWidth = pl.GetEndWidthAt(index); - } - - public BulgeVertex ToBulgeVertex() - { - return new BulgeVertex(Vertex, Bulge); - } -} diff --git a/src/IFoxCAD.Cad.Shared/ExtensionMethod/CollectionEx.cs b/src/IFoxCAD.Cad.Shared/ExtensionMethod/CollectionEx.cs deleted file mode 100644 index acd120a..0000000 --- a/src/IFoxCAD.Cad.Shared/ExtensionMethod/CollectionEx.cs +++ /dev/null @@ -1,289 +0,0 @@ -using System.ComponentModel; -using System.Xml.Linq; -using static System.Windows.Forms.AxHost; - -namespace IFoxCAD.Cad; - -/// -/// 集合扩展类 -/// -public static class CollectionEx -{ - /// - /// 对象id迭代器转换为集合 - /// - /// 对象id的迭代器 - /// 对象id集合,记得释放 - [System.Diagnostics.DebuggerStepThrough] - public static ObjectIdCollection ToCollection(this IEnumerable ids) - { - return new ObjectIdCollection(ids.ToArray()); - } - - /// - /// 实体迭代器转换为集合 - /// - /// 对象类型 - /// 实体对象的迭代器 - /// 实体集合,记得释放 - [System.Diagnostics.DebuggerStepThrough] - public static DBObjectCollection ToCollection(this IEnumerable objs) where T : DBObject - { - DBObjectCollection objCol = new(); - foreach (T obj in objs) - objCol.Add(obj); - return objCol; - } - - /// - /// double 数值迭代器转换为 double 数值集合 - /// - /// double 数值迭代器 - /// 数值集合,它没有Dispose - [System.Diagnostics.DebuggerStepThrough] - public static DoubleCollection ToCollection(this IEnumerable doubles) - { - return new DoubleCollection(doubles.ToArray()); - } - - /// - /// 二维点迭代器转换为二维点集合 - /// - /// 二维点迭代器 - /// 二维点集合,!acad记得释放 - [System.Diagnostics.DebuggerStepThrough] - public static Point2dCollection ToCollection(this IEnumerable pts) - { - return new Point2dCollection(pts.ToArray()); - } - - /// - /// 三维点迭代器转换为三维点集合 - /// - /// 三维点迭代器 - /// 三维点集合,记得释放 - [System.Diagnostics.DebuggerStepThrough] - public static Point3dCollection ToCollection(this IEnumerable pts) - { - return new Point3dCollection(pts.ToArray()); - } - - /// - /// 对象id集合转换为对象id列表 - /// - /// 对象id集合 - /// 对象id列表 - [System.Diagnostics.DebuggerStepThrough] - public static List ToList(this ObjectIdCollection ids) - { - return ids.Cast().ToList(); - } - - - /// - /// 遍历集合,执行委托 - /// - /// 集合值的类型 - /// 集合 - /// 委托 - [System.Diagnostics.DebuggerStepThrough] //[DebuggerHidden] 两个特性差不多 - public static void ForEach(this IEnumerable source, Action action) - { - // 这里不要嵌套调用ForEach委托, - // 因为这样可以在调用函数上断点直接跑Action内,不会进入此处(除了cad之外); - // 而cad很奇怪,只能用预处理方式避免 - // 嵌套调用ForEach委托: - // source.ForEach((a, _, _) => { - // action.Invoke(a); - // }); - - foreach (var element in source) - action.Invoke(element); - } - - /// - /// 遍历集合,执行委托(允许循环中断) - /// - /// 集合值的类型 - /// 集合 - /// 委托 - [System.Diagnostics.DebuggerStepThrough] - public static void ForEach(this IEnumerable source, Action action) - { - // 这里不要嵌套调用ForEach委托, - // 因为这样可以在调用函数上断点直接跑Action内,不会进入此处(除了cad之外); - // 而cad很奇怪,只能用预处理方式避免 - // 嵌套调用ForEach委托: - // source.ForEach((a, b, _) => { - // action.Invoke(a, b); - // }); - - LoopState state = new();/*这种方式比Action改Func更友好*/ - foreach (var element in source) - { - action.Invoke(element, state); - if (!state.IsRun) - break; - } - } - - /// - /// 遍历集合,执行委托(允许循环中断,输出索引值) - /// - /// 集合值的类型 - /// 集合 - /// 委托 - [System.Diagnostics.DebuggerStepThrough] - public static void ForEach(this IEnumerable source, Action action) - { - int i = 0; - LoopState state = new();/*这种方式比Action改Func更友好*/ - foreach (var element in source) - { - action.Invoke(element, state, i); - if (!state.IsRun) - break; - i++; - } - } - - - #region 关键字集合 - public enum KeywordName - { - GlobalName, - LocalName, - DisplayName, - } - - /// - /// 含有关键字 - /// - /// 关键字集合 - /// 关键字 - /// 关键字容器字段名 - /// true含有 - [System.Diagnostics.DebuggerStepThrough] - public static bool Contains(this KeywordCollection collection, string name, - KeywordName keywordName = KeywordName.GlobalName) - { - bool contains = false; - switch (keywordName) - { - case KeywordName.GlobalName: - for (int i = 0; i < collection.Count; i++) - { -#if gcad - var item = collection.get_Item(i); -#else - var item = collection[i]; -#endif - if (item.GlobalName == name) - { - contains = true; - break; - } - } - break; - case KeywordName.LocalName: - for (int i = 0; i < collection.Count; i++) - { -#if gcad - var item = collection.get_Item(i); -#else - var item = collection[i]; -#endif - if (item.LocalName == name) - { - contains = true; - break; - } - } - break; - case KeywordName.DisplayName: - for (int i = 0; i < collection.Count; i++) - { -#if gcad - var item = collection.get_Item(i); -#else - var item = collection[i]; -#endif - if (item.DisplayName == name) - { - contains = true; - break; - } - } - break; - } - return contains; - } - - /// - /// 获取词典, - /// KeywordCollection是允许重复关键字的,没有哈希索引,在多次判断时候会遍历多次O(n),所以生成一个词典进行O(1) - /// - /// - /// - [System.Diagnostics.DebuggerStepThrough] - public static Dictionary GetDict(this KeywordCollection collection) - { - Dictionary map = new(); - for (int i = 0; i < collection.Count; i++) - { -#if gcad - var item = collection.get_Item(i); -#else - var item = collection[i]; -#endif - map.Add(item.GlobalName, item.DisplayName); - } - return map; - } - #endregion - - - #region IdMapping - /// - /// 旧块名 - /// - /// - /// - [System.Diagnostics.DebuggerStepThrough] - public static List GetKeys(this IdMapping idmap) - { - List ids = new(); - foreach (IdPair item in idmap) - ids.Add(item.Key); - return ids; - } - - /// - /// 新块名 - /// - /// - /// - [System.Diagnostics.DebuggerStepThrough] - public static List GetValues(this IdMapping idmap) - { - List ids = new(); - foreach (IdPair item in idmap) - ids.Add(item.Value); - return ids; - } - - /// - /// 转换为词典 - /// - /// - /// - [System.Diagnostics.DebuggerStepThrough] - public static Dictionary ToDictionary(this IdMapping mapping) - { - var keyValuePairs = new Dictionary(); - foreach (IdPair item in mapping) - keyValuePairs.Add(item.Key, item.Value); - return keyValuePairs; - } - #endregion -} \ No newline at end of file diff --git a/src/IFoxCAD.Cad.Shared/ExtensionMethod/Curve2dEx.cs b/src/IFoxCAD.Cad.Shared/ExtensionMethod/Curve2dEx.cs deleted file mode 100644 index 07db654..0000000 --- a/src/IFoxCAD.Cad.Shared/ExtensionMethod/Curve2dEx.cs +++ /dev/null @@ -1,296 +0,0 @@ -namespace IFoxCAD.Cad; - -/// -/// 二维解析类曲线转换为二维实体曲线扩展类 -/// -public static class Curve2dEx -{ - internal static readonly Plane _planeCache = new(); - - #region Curve2d - - /// - /// 按矩阵转换Ge2d曲线为Db曲线 - /// - /// Ge2d曲线 - /// 曲线转换矩阵 - /// Db曲线 - public static Curve? ToCurve(this Curve2d curve, Matrix3d mat) - { - return curve switch - { - LineSegment2d li => ToCurve(li, mat), - NurbCurve2d nu => ToCurve(nu, mat), - EllipticalArc2d el => ToCurve(el, mat), - CircularArc2d ci => ToCurve(ci, mat), - PolylineCurve2d po => ToCurve(po, mat), - Line2d l2 => ToCurve(l2, mat), - CompositeCurve2d co => ToCurve(co, mat), - _ => null - }; - } - - #endregion Curve2d - - #region CircularArc2d - - /// - /// 判断点是否位于圆内及圆上 - /// - /// 二维解析类圆弧对象 - /// 二维点 - /// 位于圆内及圆上返回 ,反之返回 - public static bool IsIn(this CircularArc2d ca2d, Point2d pnt) - { - return ca2d.IsOn(pnt) || ca2d.IsInside(pnt); - } - - /// - /// 将二维解析类圆弧转换为实体圆或者圆弧,然后进行矩阵变换 - /// - /// 二维解析类圆弧对象 - /// 变换矩阵 - /// 实体圆或者圆弧 - public static Curve ToCurve(this CircularArc2d ca2d, Matrix3d mat) - { - Curve c = ToCurve(ca2d); - c.TransformBy(mat); - return c; - } - - /// - /// 将二维解析类圆弧转换为实体圆或者圆弧 - /// - /// 二维解析类圆弧对象 - /// 实体圆或者圆弧 - public static Curve ToCurve(this CircularArc2d ca2d) - { - if (ca2d.IsClosed()) - return ToCircle(ca2d); - else - return ToArc(ca2d); - } - - /// - /// 将二维解析类圆弧转换为实体圆 - /// - /// 二维解析类圆弧对象 - /// 实体圆 - public static Circle ToCircle(this CircularArc2d c2d) - { - return - new Circle( - new Point3d(_planeCache, c2d.Center), - Vector3d.ZAxis, - c2d.Radius); - } - - /// - /// 将二维解析类圆弧转换为实体圆弧 - /// - /// 二维解析类圆弧对象 - /// 圆弧 - public static Arc ToArc(this CircularArc2d a2d) - { - double startangle, endangle; - double refangle = a2d.ReferenceVector.Angle; - - if (a2d.IsClockWise) - { - startangle = -a2d.EndAngle - refangle; - endangle = -a2d.StartAngle - refangle; - } - else - { - startangle = a2d.StartAngle + refangle; - endangle = a2d.EndAngle + refangle; - } - - return - new Arc( - new Point3d(_planeCache, a2d.Center), - Vector3d.ZAxis, - a2d.Radius, - startangle, - endangle); - } - - #endregion CircularArc2d - - #region EllipticalArc2d - - // 椭圆弧 - /// - /// 将二维解析类椭圆弧转换为实体椭圆弧,然后进行矩阵变换 - /// - /// 二维解析类椭圆弧对象 - /// 变换矩阵 - /// 实体椭圆弧 - public static Ellipse ToCurve(this EllipticalArc2d ea2d, Matrix3d mat) - { - Ellipse e = ToCurve(ea2d); - e.TransformBy(mat); - return e; - } - - /// - /// 将二维解析类椭圆弧转换为实体椭圆弧 - /// - /// 二维解析类椭圆弧对象 - /// 实体椭圆弧 - public static Ellipse ToCurve(this EllipticalArc2d ea2d) - { - Ellipse ell = new( - new Point3d(_planeCache, ea2d.Center), - Vector3d.ZAxis, - new Vector3d(_planeCache, ea2d.MajorAxis) * ea2d.MajorRadius, - ea2d.MinorRadius / ea2d.MajorRadius, - 0, - Math.PI * 2); - if (!ea2d.IsClosed()) - { - if (ea2d.IsClockWise) - { - ell.StartAngle = -ell.GetAngleAtParameter(ea2d.EndAngle); - ell.EndAngle = -ell.GetAngleAtParameter(ea2d.StartAngle); - } - else - { - ell.StartAngle = ell.GetAngleAtParameter(ea2d.StartAngle); - ell.EndAngle = ell.GetAngleAtParameter(ea2d.EndAngle); - } - } - return ell; - } - - #endregion EllipticalArc2d - - #region Line2d - - /// - /// 将二维解析类直线转换为实体类构造线 - /// - /// 二维解析类直线 - /// 实体类构造线 - public static Xline ToCurve(this Line2d line2d) - { - return new Xline - { - BasePoint = new Point3d(_planeCache, line2d.PointOnLine), - SecondPoint = new Point3d(_planeCache, line2d.PointOnLine + line2d.Direction) - }; - } - - /// - /// 将二维解析类直线转换为实体类构造线,然后进行矩阵变换 - /// - /// 二维解析类直线 - /// 变换矩阵 - /// 实体类构造线 - public static Xline ToCurve(this Line2d line2d, Matrix3d mat) - { - Xline xl = ToCurve(line2d); - xl.TransformBy(mat); - return xl; - } - - /// - /// 将二维解析类构造线转换为二维解析类线段 - /// - /// 二维解析类构造线 - /// 起点参数 - /// 终点参数 - /// 二维解析类线段 - public static LineSegment2d ToLineSegment2d(this Line2d line2d, double fromParameter, double toParameter) - { - return - new LineSegment2d - ( - line2d.EvaluatePoint(fromParameter), - line2d.EvaluatePoint(toParameter) - ); - } - - #endregion Line2d - - #region LineSegment2d - - /// - /// 将二维解析类线段转换为实体类直线,并进行矩阵变换 - /// - /// 二维解析类线段 - /// 变换矩阵 - /// 实体类直线 - public static Line ToCurve(this LineSegment2d ls2d, Matrix3d mat) - { - Line l = ToCurve(ls2d); - l.TransformBy(mat); - return l; - } - - /// - /// 将二维解析类线段转换为实体类直线 - /// - /// 二维解析类线段 - /// 实体类直线 - public static Line ToCurve(this LineSegment2d ls2d) - { - return - new Line( - new Point3d(_planeCache, ls2d.StartPoint), - new Point3d(_planeCache, ls2d.EndPoint)); - } - - #endregion LineSegment2d - - #region NurbCurve2d - - /// - /// 将二维解析类BURB曲线转换为实体类样条曲线,并进行矩阵变换 - /// - /// 二维解析类BURB曲线 - /// 变换矩阵 - /// 实体类样条曲线 - public static Spline ToCurve(this NurbCurve2d nc2d, Matrix3d mat) - { - Spline spl = ToCurve(nc2d); - spl.TransformBy(mat); - return spl; - } - - /// - /// 将二维解析类BURB曲线转换为实体类样条曲线 - /// - /// 二维解析类BURB曲线 - /// 实体类样条曲线 - public static Spline ToCurve(this NurbCurve2d nc2d) - { - using Point3dCollection ctlpnts = new(); - for (int i = 0; i < nc2d.NumControlPoints; i++) - ctlpnts.Add(new Point3d(_planeCache, nc2d.GetControlPointAt(i))); - - DoubleCollection knots = new(); - for (int i = 0; i < nc2d.Knots.Count; i++) - knots.Add(nc2d.Knots[i]); - - DoubleCollection weights = new(); - for (int i = 0; i < nc2d.NumWeights; i++) - weights.Add(nc2d.GetWeightAt(i)); - - NurbCurve2dData ncdata = nc2d.DefinitionData; - - return - new Spline( - ncdata.Degree, - ncdata.Rational, - nc2d.IsClosed(), - ncdata.Periodic, - ctlpnts, - knots, - weights, - 0, - nc2d.Knots.Tolerance); - } - - #endregion NurbCurve2d -} \ No newline at end of file diff --git a/src/IFoxCAD.Cad.Shared/ExtensionMethod/Curve3dEx.cs b/src/IFoxCAD.Cad.Shared/ExtensionMethod/Curve3dEx.cs deleted file mode 100644 index c12275b..0000000 --- a/src/IFoxCAD.Cad.Shared/ExtensionMethod/Curve3dEx.cs +++ /dev/null @@ -1,556 +0,0 @@ -using System.Runtime.CompilerServices; - -namespace IFoxCAD.Cad; - -/// -/// 三维解析类曲线转换为三维实体曲线扩展类 -/// -public static class Curve3dEx -{ - /// - /// 判断两个浮点数是否相等 - /// - /// 容差 - /// 第一个数 - /// 第二个数 - /// 两个数的差值的绝对值小于容差返回 ,反之返回 - [MethodImpl] - public static bool IsEqualPoint(this Tolerance tol, double d1, double d2) - { - return Math.Abs(d1 - d2) < tol.EqualPoint; - } - - #region Curve3d - - /// - /// 获取三维解析类曲线(自交曲线)的交点参数 - /// - /// 三维解析类曲线 - /// 曲线参数的列表 - public static List GetParamsAtIntersectionPoints(this Curve3d c3d, bool sort = true) - { - CurveCurveIntersector3d cci = new(c3d, c3d, Vector3d.ZAxis); - List pars = new(); - for (int i = 0; i < cci.NumberOfIntersectionPoints; i++) - pars.AddRange(cci.GetIntersectionParameters(i)); - if (sort) - pars.Sort(); - return pars; - } - - /// - /// 获取三维解析类子曲线 - /// - /// 三维解析类曲线 - /// 子段曲线起点参数 - /// 子段曲线终点参数 - /// 三维解析类曲线 - public static Curve3d GetSubCurve(this Curve3d curve, double from, double to) - { - Interval inter = curve.GetInterval(); - bool atStart = Tolerance.Global.IsEqualPoint(inter.LowerBound, from); - bool atEnd = Tolerance.Global.IsEqualPoint(inter.UpperBound, to); - if (atStart && atEnd) - return (Curve3d)curve.Clone(); - if (curve is NurbCurve3d) - { - if (from < to) - { - NurbCurve3d clone = (NurbCurve3d)curve.Clone(); - if (atStart || atEnd) - { - clone.HardTrimByParams(from, to); - return clone; - } - else - { - clone.HardTrimByParams(inter.LowerBound, to); - clone.HardTrimByParams(from, to); - return clone; - } - } - else - { - NurbCurve3d clone1 = (NurbCurve3d)curve.Clone(); - clone1.HardTrimByParams(from, inter.UpperBound); - NurbCurve3d clone2 = (NurbCurve3d)curve.Clone(); - clone2.HardTrimByParams(inter.LowerBound, to); - clone1.JoinWith(clone2); - return clone1; - } - } - else - { - Curve3d clone = (Curve3d)curve.Clone(); - clone.SetInterval(new Interval(from, to, Tolerance.Global.EqualPoint)); - return clone; - } - } - - /// - /// 将三维解析类曲线转换为三维实体类曲线 - /// - /// 三维解析类曲线 - /// 三维实体类曲线 - public static Curve? ToCurve(this Curve3d curve) - { - return curve switch - { - CompositeCurve3d co => ToCurve(co), - LineSegment3d li => ToCurve(li), - EllipticalArc3d el => ToCurve(el), - CircularArc3d ci => ToCurve(ci), - NurbCurve3d nu => ToCurve(nu), - PolylineCurve3d pl => ToCurve(pl), - Line3d l3 => ToCurve(l3), - _ => null - }; - } - - /// - /// 将三维解析类曲线转换为三维解析类Nurb曲线 - /// - /// 三维解析类曲线 - /// 三维解析类Nurb曲线 - public static NurbCurve3d? ToNurbCurve3d(this Curve3d curve) - { - return curve switch - { - LineSegment3d line => new NurbCurve3d(line), - EllipticalArc3d el => new NurbCurve3d(el), - CircularArc3d cir => new NurbCurve3d(ToEllipticalArc3d(cir)), - NurbCurve3d nur => nur, - PolylineCurve3d pl => new NurbCurve3d(3, pl, false), - _ => null - }; - } - - #endregion Curve3d - - #region CompositeCurve3d - - /// - /// 判断是否为圆和椭圆 - /// - /// 三维解析类曲线 - /// 完整圆及完整的椭圆返回 ,反之返回 - public static bool IsCircular(this Curve3d curve) - { - return curve switch - { - CircularArc3d or EllipticalArc3d => curve.IsClosed(), - _ => false - }; - } - - /// - /// 将三维复合曲线按曲线参数分割 - /// - /// 三维复合曲线 - /// 曲线参数列表 - /// 三维复合曲线列表 - public static List? GetSplitCurves(this CompositeCurve3d c3d, List pars) - { - // 曲线参数剔除重复的 - if (pars.Count > 0) - { - pars.Sort(); - for (int i = pars.Count - 1; i > 0; i--) - if (Tolerance.Global.IsEqualPoint(pars[i], pars[i - 1])) - pars.RemoveAt(i); - } - if (pars.Count == 0) - return null; - - // 这个是曲线参数类 - var inter = c3d.GetInterval(); - // 曲线们 - var c3ds = c3d.GetCurves(); - if (c3ds.Length == 1 && c3ds[0].IsClosed()) - { - // 闭合曲线不允许打断于一点 - if (pars.Count < 2) - return null; - - // 如果包含起点 - if (Tolerance.Global.IsEqualPoint(pars[0], inter.LowerBound)) - { - pars[0] = inter.LowerBound; - // 又包含终点,去除终点 - if (Tolerance.Global.IsEqualPoint(pars[^1], inter.UpperBound)) - { - pars.RemoveAt(pars.Count - 1); - if (pars.Count == 1) - return null; - } - } - else if (Tolerance.Global.IsEqualPoint(pars[^1], inter.UpperBound)) - { - pars[^1] = inter.UpperBound; - } - // 加入第一点以支持反向打断 - pars.Add(pars[0]); - } - else - { - // 非闭合曲线加入起点和终点 - if (Tolerance.Global.IsEqualPoint(pars[0], inter.LowerBound)) - pars[0] = inter.LowerBound; - else - pars.Insert(0, inter.LowerBound); - if (Tolerance.Global.IsEqualPoint(pars[^1], inter.UpperBound)) - pars[^1] = inter.UpperBound; - else - pars.Add(inter.UpperBound); - } - - List curves = new(); - List cc3ds = new(); - for (int i = 0; i < pars.Count - 1; i++) - { - cc3ds.Clear(); - // 复合曲线参数转换到包含曲线参数 - var cp1 = c3d.GlobalToLocalParameter(pars[i]); - var cp2 = c3d.GlobalToLocalParameter(pars[i + 1]); - if (cp1.SegmentIndex == cp2.SegmentIndex) - { - cc3ds.Add( - c3ds[cp1.SegmentIndex].GetSubCurve( - cp1.LocalParameter, - cp2.LocalParameter)); - } - else - { - inter = c3ds[cp1.SegmentIndex].GetInterval(); - cc3ds.Add( - c3ds[cp1.SegmentIndex].GetSubCurve( - cp1.LocalParameter, - inter.UpperBound)); - - for (int j = cp1.SegmentIndex + 1; j < cp2.SegmentIndex; j++) - cc3ds.Add((Curve3d)c3ds[j].Clone()); - - inter = c3ds[cp2.SegmentIndex].GetInterval(); - cc3ds.Add( - c3ds[cp2.SegmentIndex].GetSubCurve( - inter.LowerBound, - cp2.LocalParameter)); - } - curves.Add(new(cc3ds.ToArray())); - } - - // 封闭多段线 口口 并排形状,第二个口切割不成功,注释下面就成功了 - //if (c3d.IsClosed() && c3ds.Length > 1) - //{ - // var cus1 = curves[^1].GetCurves(); - // var cus2 = curves[0].GetCurves(); - // var cs = cus1.Combine2(cus2); - // curves[^1] = new(cs); - // curves.RemoveAt(0); - //} - return curves; - } - - /// - /// 将复合曲线转换为实体类曲线 - /// - /// 三维复合曲线 - /// 实体曲线 - public static Curve? ToCurve(this CompositeCurve3d curve) - { - Curve3d[] cs = curve.GetCurves(); - if (cs.Length == 0) - return null; - if (cs.Length == 1) - return ToCurve(cs[0]); - - bool hasNurb = false; - - for (int i = 0; i < cs.Length; i++) - { - var c = cs[i]; - if (c is NurbCurve3d || c is EllipticalArc3d) - { - hasNurb = true; - break; - } - } - if (hasNurb) - { - var nc3d = cs[0].ToNurbCurve3d(); - for (int i = 1; i < cs.Length; i++) - nc3d?.JoinWith(cs[i].ToNurbCurve3d()); - return nc3d?.ToCurve(); - } - - return ToPolyline(curve); - } - - /// - /// 将三维复合曲线转换为实体类多段线 - /// - /// 三维复合曲线 - /// 实体类多段线 - public static Polyline ToPolyline(this CompositeCurve3d cc3d) - { - Polyline pl = new(); - pl.SetDatabaseDefaults(); - pl.Elevation = cc3d.StartPoint[2]; - - Plane plane = pl.GetPlane(); - Point2d endver = Point2d.Origin; - int i = 0; - foreach (Curve3d c3d in cc3d.GetCurves()) - { - if (c3d is CircularArc3d ca3d) - { - double b = Math.Tan(0.25 * (ca3d.EndAngle - ca3d.StartAngle)) * ca3d.Normal[2]; - pl.AddVertexAt(i, c3d.StartPoint.Convert2d(plane), b, 0, 0); - endver = c3d.EndPoint.Convert2d(plane); - } - else - { - pl.AddVertexAt(i, c3d.StartPoint.Convert2d(plane), 0, 0, 0); - endver = c3d.EndPoint.Convert2d(plane); - } - i++; - } - pl.AddVertexAt(i, endver, 0, 0, 0); - return pl; - } - - #endregion CompositeCurve3d - - #region Line3d - - /// - /// 将解析类三维构造线转换为实体类构造线 - /// - /// 解析类三维构造线 - /// 实体类构造线 - public static Xline ToCurve(this Line3d line3d) - { - return - new Xline - { - BasePoint = line3d.PointOnLine, - SecondPoint = line3d.PointOnLine + line3d.Direction - }; - } - - /// - /// 将三维解析类构造线转换为三维解析类线段 - /// - /// 三维解析类构造线 - /// 起点参数 - /// 终点参数 - /// 三维解析类线段 - public static LineSegment3d ToLineSegment3d(this Line3d line3d, double fromParameter, double toParameter) - { - return - new LineSegment3d - ( - line3d.EvaluatePoint(fromParameter), - line3d.EvaluatePoint(toParameter) - ); - } - - #endregion Line3d - - #region LineSegment3d - - /// - /// 将三维解析类线段转换为实体类直线 - /// - /// 三维解析类线段 - /// 实体类直线 - public static Line ToCurve(this LineSegment3d lineSeg3d) - { - return new Line(lineSeg3d.StartPoint, lineSeg3d.EndPoint); - } - - #endregion LineSegment3d - - #region CircularArc3d - - /// - /// 将三维解析类圆/弧转换为实体圆/弧 - /// - /// 三维解析类圆/弧 - /// 实体圆/弧 - public static Curve ToCurve(this CircularArc3d ca3d) - { - if (ca3d.IsClosed()) - { - return ToCircle(ca3d); - } - else - { - return ToArc(ca3d); - } - } - - /// - /// 将三维解析类圆/弧转换为实体圆 - /// - /// 三维解析类圆/弧 - /// 实体圆 - public static Circle ToCircle(this CircularArc3d ca3d) => - new(ca3d.Center, ca3d.Normal, ca3d.Radius); - - /// - /// 将三维解析类圆/弧转换为实体圆弧 - /// - /// 三维解析类圆/弧 - /// 实体圆弧 - public static Arc ToArc(this CircularArc3d ca3d) - { - // 必须新建,而不能直接使用GetPlane()获取 - double angle = ca3d.ReferenceVector.AngleOnPlane(new Plane(ca3d.Center, ca3d.Normal)); - return new Arc(ca3d.Center, ca3d.Normal, ca3d.Radius, ca3d.StartAngle + angle, ca3d.EndAngle + angle); - } - - /// - /// 将三维解析类圆/弧转换为三维解析类椭圆弧 - /// - /// 三维解析类圆/弧 - /// 三维解析类椭圆弧 - public static EllipticalArc3d ToEllipticalArc3d(this CircularArc3d ca3d) - { - Vector3d zaxis = ca3d.Normal; - Vector3d xaxis = ca3d.ReferenceVector; - Vector3d yaxis = zaxis.CrossProduct(xaxis); - - return - new EllipticalArc3d( - ca3d.Center, - xaxis, - yaxis, - ca3d.Radius, - ca3d.Radius, - ca3d.StartAngle, - ca3d.EndAngle); - } - - /// - /// 将三维解析类圆/弧转换为三维解析类Nurb曲线 - /// - /// 三维解析类圆/弧 - /// 三维解析类Nurb曲线 - public static NurbCurve3d ToNurbCurve3d(this CircularArc3d ca3d) - { - EllipticalArc3d ea3d = ToEllipticalArc3d(ca3d); - NurbCurve3d nc3d = new(ea3d); - return nc3d; - } - - #endregion CircularArc3d - - #region EllipticalArc3d - - /// - /// 将三维解析类椭圆弧转换为实体类椭圆弧 - /// - /// 三维解析类椭圆弧 - /// 实体类椭圆弧 - public static Ellipse ToCurve(this EllipticalArc3d ea3d) - { - Ellipse ell = - new( - ea3d.Center, - ea3d.Normal, - ea3d.MajorAxis * ea3d.MajorRadius, - ea3d.MinorRadius / ea3d.MajorRadius, - 0, - Math.PI * 2); - // Ge椭圆角度就是Db椭圆的参数 - if (!ea3d.IsClosed()) - { - ell.StartAngle = ell.GetAngleAtParameter(ea3d.StartAngle); - ell.EndAngle = ell.GetAngleAtParameter(ea3d.EndAngle); - } - return ell; - } - - #endregion EllipticalArc3d - - #region NurbCurve3d - - /// - /// 将三维解析类Nurb曲线转换为实体类样条曲线 - /// - /// 三维解析类Nurb曲线 - /// 实体类样条曲线 - public static Spline ToCurve(this NurbCurve3d nc3d) - { - Spline spl; - if (nc3d.HasFitData) - { - NurbCurve3dFitData fdata = nc3d.FitData; - if (fdata.TangentsExist) - { - spl = new Spline( - fdata.FitPoints, - fdata.StartTangent, - fdata.EndTangent, - nc3d.Order, - fdata.FitTolerance.EqualPoint); - } - else - { - spl = new Spline( - fdata.FitPoints, - nc3d.Order, - fdata.FitTolerance.EqualPoint); - } - } - else - { - DoubleCollection knots = new(); - foreach (double knot in nc3d.Knots) - knots.Add(knot); - - NurbCurve3dData ncdata = nc3d.DefinitionData; - - spl = new Spline( - ncdata.Degree, - ncdata.Rational, - nc3d.IsClosed(), - ncdata.Periodic, - ncdata.ControlPoints, - knots, - ncdata.Weights, - Tolerance.Global.EqualPoint, - ncdata.Knots.Tolerance); - } - return spl; - } - - #endregion NurbCurve3d - - #region PolylineCurve3d - - /// - /// 将三维解析类多段线转换为实体类三维多段线 - /// - /// 三维解析类多段线 - /// 实体类三维多段线 - public static Polyline3d ToCurve(this PolylineCurve3d pl3d) - { - using Point3dCollection pnts = new(); - - for (int i = 0; i < pl3d.NumberOfControlPoints; i++) - pnts.Add(pl3d.ControlPointAt(i)); - - bool closed = false; - int n = pnts.Count - 1; - if (pnts[0] == pnts[n]) - { - pnts.RemoveAt(n); - closed = true; - } - return new Polyline3d(Poly3dType.SimplePoly, pnts, closed); - } - - #endregion PolylineCurve3d -} \ No newline at end of file diff --git a/src/IFoxCAD.Cad.Shared/ExtensionMethod/CurveEx.cs b/src/IFoxCAD.Cad.Shared/ExtensionMethod/CurveEx.cs deleted file mode 100644 index 68b286f..0000000 --- a/src/IFoxCAD.Cad.Shared/ExtensionMethod/CurveEx.cs +++ /dev/null @@ -1,723 +0,0 @@ -namespace IFoxCAD.Cad; - -/// -/// 实体类曲线扩展类 -/// -public static class CurveEx -{ - /// - /// 曲线长度 - /// - /// 曲线 - /// 长度 - public static double GetLength(this Curve curve) - { - return curve.GetDistanceAtParameter(curve.EndParam); - } - - /// - /// 获取分割曲线集合 - /// - /// 曲线 - /// 打断参数表 - /// 打断后曲线的集合 - public static IEnumerable GetSplitCurves(this Curve curve, IEnumerable pars) - { - if (pars is null) - throw new ArgumentNullException(nameof(pars)); - - return - curve - .GetSplitCurves(new DoubleCollection(pars.ToArray())) - .Cast(); - } - - /// - /// 获取分割曲线集合 - /// - /// 曲线 - /// 打断参数表 - /// 对参数表是否进行排序 - /// - /// 按参数值升序排序
    - /// 不排序,默认值 - ///
    - /// - /// 打断后曲线的集合 - public static IEnumerable GetSplitCurves(this Curve curve, IEnumerable pars, bool isOrder = false) - { - if (pars is null) - throw new ArgumentNullException(nameof(pars)); - if (isOrder) - pars = pars.OrderBy(x => x); - - return - curve - .GetSplitCurves(new DoubleCollection(pars.ToArray())) - .Cast(); - } - - /// - /// 获取分割曲线集合 - /// - /// 曲线 - /// 打断点表 - /// 打断后曲线的集合 - public static IEnumerable GetSplitCurves(this Curve curve, IEnumerable points) - { - if (points is null) - throw new ArgumentNullException(nameof(points)); - - using var pts = new Point3dCollection(points.ToArray()); - return curve.GetSplitCurves(pts).Cast(); - } - - /// - /// 获取分割曲线集合 - /// - /// 曲线 - /// 打断点表 - /// 对点表是否进行排序 - /// - /// 按参数值升序排序
    - /// 不排序,默认值 - ///
    - /// - /// 打断后曲线的集合 - public static IEnumerable GetSplitCurves(this Curve curve, IEnumerable points, bool isOrder = false) - { - if (points is null) - throw new ArgumentNullException(nameof(points)); - - if (isOrder) - points = points.OrderBy(point => { - var pt = curve.GetClosestPointTo(point, false); - return curve.GetParameterAtPoint(pt); - }); - - using Point3dCollection pts = new(points.ToArray()); - return curve.GetSplitCurves(pts).Cast(); - } - - /// - /// 获取曲线集所围成的封闭区域的曲线集,注意此函数不能处理平行边(两个点及两条线组成的闭合环) - /// - /// 曲线集合 - /// 所有的闭合环的曲线集合 - public static IEnumerable GetAllCycle(this IEnumerable curves) - { - if (curves is null) - throw new ArgumentNullException(nameof(curves)); - - // 新建图 - var graph = new Graph(); - foreach (var curve in curves) - { -#if !gcad -#if NET35 - graph.AddEdge(curve.ToCurve3d()!); -#else - graph.AddEdge(curve.GetGeCurve()); -#endif -#else - graph.AddEdge(curve.ToCurve3d()!); -#endif - } - // 新建 dfs - var dfs = new DepthFirst(); - // 查询全部的 闭合环 - dfs.FindAll(graph); - // 遍历闭合环的列表,将每个闭合环转换为实体曲线 - var res = new List(); - foreach (var item in dfs.Curve3ds) - { - var curve = graph.GetCurves(item.ToList()).ToArray(); - var comcur = new CompositeCurve3d(curve).ToCurve(); - if (comcur is not null) - res.Add(comcur); - } - return res; - } - /// - /// 曲线打断 - /// - /// 曲线列表 - /// 打断后的曲线列表 - public static List BreakCurve(this List curves) - { - if (curves is null) - throw new ArgumentNullException(nameof(curves)); - - var geCurves = new List(); // 存储曲线转换后的复合曲线 - var paramss = new List>(); // 存储每个曲线的交点参数值 - - for (int i = 0; i < curves.Count; i++) - { - var cc3d = curves[i].ToCompositeCurve3d(); - if (cc3d is not null) - { - geCurves.Add(cc3d); - paramss.Add(new List()); - } - } - - // var oldCurves = new List(); - var newCurves = new List(); - var cci3d = new CurveCurveIntersector3d(); - - for (int i = 0; i < curves.Count; i++) - { - var gc1 = geCurves[i]; - var pars1 = paramss[i]; // 引用 - for (int j = i; j < curves.Count; j++) - { - var gc2 = geCurves[j]; - var pars2 = paramss[j]; // 引用 - - cci3d.Set(gc1, gc2, Vector3d.ZAxis); - - for (int k = 0; k < cci3d.NumberOfIntersectionPoints; k++) - { - var pars = cci3d.GetIntersectionParameters(k); - pars1.Add(pars[0]); // 引用修改会同步到源对象 - pars2.Add(pars[1]); // 引用修改会同步到源对象 - } - } - - if (pars1.Count > 0) - { - var c3ds = gc1.GetSplitCurves(pars1); - if (c3ds.Count > 1) - { - foreach (var c3d in c3ds) - { - var c3dCur = c3d.ToCurve(); - if (c3dCur is not null) - { - c3dCur.SetPropertiesFrom(curves[i]); - newCurves.Add(c3dCur); - } - } - // oldCurves.Add(curves[i]); - } - } - } - - return newCurves; - } - - // 转换DBCurve为GeCurved - - #region Curve - - /// - /// 将曲线转换为ge曲线,此函数将在未来淘汰,二惊加油 - /// - /// 曲线 - /// ge曲线 - public static Curve3d? ToCurve3d(this Curve curve) - { - return curve switch - { - Line li => ToCurve3d(li), - Circle ci => ToCurve3d(ci), - Arc arc => ToCurve3d(arc), - Ellipse el => ToCurve3d(el), - Polyline pl => ToCurve3d(pl), - Polyline2d pl2 => ToCurve3d(pl2), - Polyline3d pl3 => ToCurve3d(pl3), - Spline sp => ToCurve3d(sp), - _ => null - }; - } - - /// - /// 将曲线转换为复合曲线 - /// - /// 曲线 - /// 复合曲线 - public static CompositeCurve3d? ToCompositeCurve3d(this Curve curve) - { - return curve switch - { - Line li => new CompositeCurve3d(new Curve3d[] { ToCurve3d(li) }), - Circle ci => new CompositeCurve3d(new Curve3d[] { ToCurve3d(ci) }), - Arc arc => new CompositeCurve3d(new Curve3d[] { ToCurve3d(arc) }), - Ellipse el => new CompositeCurve3d(new Curve3d[] { ToCurve3d(el) }), - Polyline pl => new CompositeCurve3d(new Curve3d[] { ToCurve3d(pl) }), - - Polyline2d pl2 => new CompositeCurve3d(new Curve3d[] { ToCurve3d(pl2)! }), - Polyline3d pl3 => new CompositeCurve3d(new Curve3d[] { ToCurve3d(pl3) }), - Spline sp => new CompositeCurve3d(new Curve3d[] { ToCurve3d(sp) }), - _ => null - }; - } - - /// - /// 将曲线转换为Nurb曲线 - /// - /// 曲线 - /// Nurb曲线 - public static NurbCurve3d? ToNurbCurve3d(this Curve curve) - { - return curve switch - { - Line li => ToNurbCurve3d(li), - Circle ci => ToNurbCurve3d(ci), - Arc arc => ToNurbCurve3d(arc), - Ellipse el => ToNurbCurve3d(el), - Polyline pl => ToNurbCurve3d(pl), - Polyline2d pl2 => ToNurbCurve3d(pl2), - Polyline3d pl3 => ToNurbCurve3d(pl3), - Spline sp => ToNurbCurve3d(sp), - _ => null - }; - } - - #region Line - - /// - /// 将直线转换为ge直线 - /// - /// 直线 - /// ge直线 - public static LineSegment3d ToCurve3d(this Line line) - { - return new LineSegment3d(line.StartPoint, line.EndPoint); - } - - /// - /// 将直线转换为Nurb曲线 - /// - /// 直线 - /// Nurb曲线 - public static NurbCurve3d ToNurbCurve3d(this Line line) - { - return new NurbCurve3d(ToCurve3d(line)); - } - - #endregion Line - - #region Circle - - /// - /// 将圆转换为ge圆弧曲线 - /// - /// 圆 - /// ge圆弧曲线 - public static CircularArc3d ToCurve3d(this Circle cir) - { - return - new CircularArc3d( - cir.Center, - cir.Normal, - cir.Radius); - } - - /// - /// 将圆转换为ge椭圆曲线 - /// - /// 圆 - /// ge椭圆曲线 - public static EllipticalArc3d ToEllipticalArc3d(this Circle cir) - { - return ToCurve3d(cir).ToEllipticalArc3d(); - } - - /// - /// 将圆转换为Nurb曲线 - /// - /// 圆 - /// Nurb曲线 - public static NurbCurve3d ToNurbCurve3d(this Circle cir) - { - return new NurbCurve3d(ToEllipticalArc3d(cir)); - } - - #endregion Circle - - #region Arc - - /// - /// 将圆弧转换为ge圆弧曲线 - /// - /// 圆弧 - /// ge圆弧曲线 - public static CircularArc3d ToCurve3d(this Arc arc) - { - Plane plane = new(arc.Center, arc.Normal); - - return - new CircularArc3d( - arc.Center, - arc.Normal, - plane.GetCoordinateSystem().Xaxis, - arc.Radius, - arc.StartAngle, - arc.EndAngle - ); - } - - /// - /// 将圆弧转换为ge椭圆曲线 - /// - /// 圆弧 - /// ge椭圆曲线 - public static EllipticalArc3d ToEllipticalArc3d(this Arc arc) - { - return ToCurve3d(arc).ToEllipticalArc3d(); - } - - /// - /// 将圆弧转换为三维Nurb曲线 - /// - /// 圆弧 - /// 三维Nurb曲线 - public static NurbCurve3d ToNurbCurve3d(this Arc arc) - { - return new NurbCurve3d(ToEllipticalArc3d(arc)); - } - - #endregion Arc - - #region Ellipse - - /// - /// 将椭圆转换为三维ge椭圆曲线 - /// - /// 椭圆 - /// 三维ge椭圆曲线 - public static EllipticalArc3d ToCurve3d(this Ellipse ell) - { - return - new EllipticalArc3d( - ell.Center, - ell.MajorAxis.GetNormal(), - ell.MinorAxis.GetNormal(), - ell.MajorRadius, - ell.MinorRadius, - ell.StartParam, - ell.EndParam); - } - - /// - /// 将椭圆转换为三维Nurb曲线 - /// - /// 椭圆 - /// 三维Nurb曲线 - public static NurbCurve3d ToNurbCurve3d(this Ellipse ell) - { - return new NurbCurve3d(ToCurve3d(ell)); - } - - #endregion Ellipse - - #region Spline - - /// - /// 将样条曲线转换为三维Nurb曲线 - /// - /// 样条曲线 - /// 三维Nurb曲线 - public static NurbCurve3d ToCurve3d(this Spline spl) - { - NurbCurve3d nc3d; - NurbsData ndata = spl.NurbsData; - KnotCollection knots = new(); - foreach (Double knot in ndata.GetKnots()) - knots.Add(knot); - - if (ndata.Rational) - { - nc3d = - new NurbCurve3d( - ndata.Degree, - knots, - ndata.GetControlPoints(), - ndata.GetWeights(), - ndata.Periodic); - } - else - { - nc3d = - new NurbCurve3d( - ndata.Degree, - knots, - ndata.GetControlPoints(), - ndata.Periodic); - } - - if (spl.HasFitData) - { - var fdata = spl.FitData; - var vec = new Vector3d(); - if (fdata.TangentsExist && (fdata.StartTangent != vec || fdata.EndTangent != vec)) - nc3d.SetFitData(fdata.GetFitPoints(), fdata.StartTangent, fdata.EndTangent); - } - return nc3d; - } - - #endregion Spline - - #region Polyline2d - - /// - /// 将二维多段线转换为三维ge曲线 - /// - /// 二维多段线 - /// 三维ge曲线 - public static Curve3d? ToCurve3d(this Polyline2d pl2d) - { - switch (pl2d.PolyType) - { - case Poly2dType.SimplePoly: - case Poly2dType.FitCurvePoly: - Polyline pl = new(); - pl.SetDatabaseDefaults(); - pl.ConvertFrom(pl2d, false); - return ToCurve3d(pl); - default: - return ToNurbCurve3d(pl2d); - } - - // Polyline pl = new Polyline(); - // pl.ConvertFrom(pl2d, false); - // return ToCurve3d(pl); - } - - /// - /// 将二维多段线转换为三维Nurb曲线 - /// - /// 二维多段线 - /// 三维Nurb曲线 - public static NurbCurve3d? ToNurbCurve3d(this Polyline2d pl2d) - { - switch (pl2d.PolyType) - { - case Poly2dType.SimplePoly: - case Poly2dType.FitCurvePoly: - Polyline pl = new(); - pl.SetDatabaseDefaults(); - pl.ConvertFrom(pl2d, false); - return ToNurbCurve3d(pl); - - default: - return ToCurve3d(pl2d.Spline); - } - } - - /// - /// 将二维多段线转换为三维ge多段线 - /// - /// 二维多段线 - /// 三维ge多段线 - public static PolylineCurve3d ToPolylineCurve3d(this Polyline2d pl) - { - using Point3dCollection pnts = new(); - foreach (Vertex2d ver in pl) - pnts.Add(ver.Position); - return new PolylineCurve3d(pnts); - } - - #endregion Polyline2d - - #region Polyline3d - - /// - /// 将三维多段线转换为三维曲线 - /// - /// 三维多段线 - /// 三维曲线 - public static Curve3d ToCurve3d(this Polyline3d pl3d) - { - return pl3d.PolyType switch - { - Poly3dType.SimplePoly => ToPolylineCurve3d(pl3d), - _ => ToNurbCurve3d(pl3d), - }; - } - - /// - /// 将三维多段线转换为三维Nurb曲线 - /// - /// 三维多段线 - /// 三维Nurb曲线 - public static NurbCurve3d ToNurbCurve3d(this Polyline3d pl3d) - { - return ToCurve3d(pl3d.Spline); - } - - /// - /// 将三维多段线转换为三维ge多段线 - /// - /// 三维多段线 - /// 三维ge多段线 - public static PolylineCurve3d ToPolylineCurve3d(this Polyline3d pl) - { - using Point3dCollection pnts = new(); - foreach (ObjectId id in pl) - { - var ver = id.GetObject(OpenMode.ForRead); - if (ver != null) - pnts.Add(ver.Position); - } - return new PolylineCurve3d(pnts); - } - - #endregion Polyline3d - - #region Polyline - - /// - /// 多段线转换为复合曲线 - /// - /// 多段线对象 - /// 复合曲线对象 - public static CompositeCurve3d ToCurve3d(this Polyline pl) - { - List c3ds = new(); - - for (int i = 0; i < pl.NumberOfVertices; i++) - { - switch (pl.GetSegmentType(i)) - { - case SegmentType.Line: - c3ds.Add(pl.GetLineSegmentAt(i)); - break; - - case SegmentType.Arc: - c3ds.Add(pl.GetArcSegmentAt(i)); - break; - - default: - break; - } - } - return new CompositeCurve3d(c3ds.ToArray()); - } - - /// - /// 多段线转换为Nurb曲线 - /// - /// 多段线 - /// Nurb曲线 - public static NurbCurve3d? ToNurbCurve3d(this Polyline pl) - { - NurbCurve3d? nc3d = null; - for (int i = 0; i < pl.NumberOfVertices; i++) - { - NurbCurve3d? nc3dtemp = null; - switch (pl.GetSegmentType(i)) - { - case SegmentType.Line: - nc3dtemp = new NurbCurve3d(pl.GetLineSegmentAt(i)); - break; - - case SegmentType.Arc: - nc3dtemp = pl.GetArcSegmentAt(i).ToNurbCurve3d(); - break; - - default: - break; - } - if (nc3d is null) - nc3d = nc3dtemp; - else if (nc3dtemp is not null) - nc3d.JoinWith(nc3dtemp); - } - return nc3d; - } - - /// - /// 为优化多段线倒角 - /// - /// 优化多段线 - /// 顶点索引号 - /// 倒角半径 - /// 倒角类型 - public static void ChamferAt(this Polyline polyline, int index, double radius, bool isFillet) - { - if (index < 1 || index > polyline.NumberOfVertices - 2) - throw new System.Exception("错误的索引号"); - - if (SegmentType.Line != polyline.GetSegmentType(index - 1) || - SegmentType.Line != polyline.GetSegmentType(index)) - throw new System.Exception("非直线段不能倒角"); - - // 获取当前索引号的前后两段直线,并组合为Ge复合曲线 - Curve3d[] c3ds = - new Curve3d[] - { - polyline.GetLineSegmentAt(index - 1), - polyline.GetLineSegmentAt(index) - }; - CompositeCurve3d cc3d = new(c3ds); - - // 试倒直角 - // 子曲线的个数有三种情况: - // 1、=3时倒角方向正确 - // 2、=2时倒角方向相反 - // 3、=0或为直线时失败 - c3ds = - cc3d.GetTrimmedOffset - ( - radius, - Vector3d.ZAxis, - OffsetCurveExtensionType.Chamfer - ); - - if (c3ds.Length > 0 && c3ds[0] is CompositeCurve3d) - { - var newcc3d = c3ds[0] as CompositeCurve3d; - c3ds = newcc3d!.GetCurves(); - if (c3ds.Length == 3) - { - c3ds = cc3d.GetTrimmedOffset - ( - -radius, - Vector3d.ZAxis, - OffsetCurveExtensionType.Chamfer - ); - if (c3ds.Length == 0 || c3ds[0] is LineSegment3d) - throw new System.Exception("倒角半径过大"); - } - else if (c3ds.Length == 2) - { - radius = -radius; - } - } - else - { - throw new System.Exception("倒角半径过大"); - } - - // GetTrimmedOffset会生成倒角+偏移,故先反方向倒角,再倒回 - c3ds = cc3d.GetTrimmedOffset - ( - -radius, - Vector3d.ZAxis, - OffsetCurveExtensionType.Extend - ); - OffsetCurveExtensionType type = - isFillet ? - OffsetCurveExtensionType.Fillet : OffsetCurveExtensionType.Chamfer; - c3ds = c3ds[0].GetTrimmedOffset - ( - radius, - Vector3d.ZAxis, - type - ); - - // 将结果Ge曲线转为Db曲线,并将相关的数值反映到原曲线 - var plTemp = c3ds[0].ToCurve() as Polyline; - if (plTemp is null) - return; - polyline.RemoveVertexAt(index); - polyline.AddVertexAt(index, plTemp.GetPoint2dAt(1), plTemp.GetBulgeAt(1), 0, 0); - polyline.AddVertexAt(index + 1, plTemp.GetPoint2dAt(2), 0, 0, 0); - } - - #endregion Polyline - - #endregion -} \ No newline at end of file diff --git a/src/IFoxCAD.Cad.Shared/ExtensionMethod/DBDictionaryEx.cs b/src/IFoxCAD.Cad.Shared/ExtensionMethod/DBDictionaryEx.cs deleted file mode 100644 index e099a93..0000000 --- a/src/IFoxCAD.Cad.Shared/ExtensionMethod/DBDictionaryEx.cs +++ /dev/null @@ -1,373 +0,0 @@ -namespace IFoxCAD.Cad; - -/// -/// 字典扩展类 -/// -public static class DBDictionaryEx -{ - /// - /// 获取字典里的全部对象 - /// - /// 对象类型的泛型 - /// 字典 - /// 事务 - /// 对象迭代器 - [System.Diagnostics.DebuggerStepThrough] - public static IEnumerable GetAllObjects(this DBDictionary dict, DBTrans? trans = null) where T : DBObject - { - trans ??= DBTrans.Top; - foreach (DBDictionaryEntry e in dict) - { - var ent = trans.GetObject(e.Value); - if (ent is not null) - yield return ent; - } - } - - /// - /// 获取字典内指定key的对象 - /// - /// 对象类型的泛型 - /// 字典 - /// 事务 - /// 指定的键值 - /// T 类型的对象 - public static T? GetAt(this DBDictionary dict, string key, DBTrans? trans = null) where T : DBObject - { - trans ??= DBTrans.Top; - if (dict.Contains(key)) - { - ObjectId id = dict.GetAt(key); - if (!id.IsNull) - return trans.GetObject(id); - } - return null; - } - - /// - /// 添加条目(键值对)到字典 - /// - /// 对象类型 - /// 字典 - /// 事务 - /// 键 - /// 值 - public static void SetAt(this DBDictionary dict, string key, T newValue, Transaction? trans = null) where T : DBObject - { - trans ??= DBTrans.Top.Transaction; - using (dict.ForWrite()) - { - dict.SetAt(key, newValue); - trans.AddNewlyCreatedDBObject(newValue, true); - } - } - - #region XRecord - - /// - /// 从字典中获取扩展数据 - /// - /// 字典 - /// 键值 - /// 扩展数据 - public static XRecordDataList? GetXRecord(this DBDictionary dict, string key) - { - var rec = dict.GetAt(key); - if (rec is null) - return null; - return rec.Data; - } - - /// - /// 保存扩展数据到字典 - /// - /// 扩展数据 - /// 字典 - /// 键值 - public static void SetXRecord(this DBDictionary dict, string key, XRecordDataList rb) - { - // DxfCode.300 字符串可以写 Data - // DxfCode.1004 内存流不给写 Data,只能去写 XData - using Xrecord newValue = new() { Data = rb }; - dict.SetAt(key, newValue); - } - #endregion - - /// - /// 获取扩展字典 - /// - /// 对象 - /// 事务 - /// 扩展字典对象 - public static DBDictionary? GetXDictionary(this DBObject obj, DBTrans? trans = null) - { - trans ??= DBTrans.Top; - ObjectId id = obj.ExtensionDictionary; - if (id.IsNull) - { - using (obj.ForWrite()) - obj.CreateExtensionDictionary(); - - id = obj.ExtensionDictionary; - } - return id.GetObject(trans: trans); - } - - #region 数据表 - - /// - /// 创建数据表 - /// - /// 原数据类型的字典 - /// 表元素(二维数组) - /// 数据表 - public static DataTable CreateDataTable(Dictionary colTypes, object[,] content) - { - DataTable table = new(); - foreach (var t in colTypes) - table.AppendColumn(t.Value, t.Key); - - var ncol = colTypes.Count; - var types = new CellType[ncol]; - colTypes.Values.CopyTo(types, 0); - - var nrow = content.GetLength(0); - for (int i = 0; i < nrow; i++) - { - DataCellCollection row = new(); - for (int j = 0; j < ncol; j++) - { - var cell = new DataCell(); - cell.SetValue(types[j], content[i, j]); - row.Add(cell); - } - table.AppendRow(row, true); - } - return table; - } - - /// - /// 设定单元格数据 - /// - /// 单元格 - /// 类型 - /// 数据 - public static void SetValue(this DataCell cell, CellType type, object value) - { - switch (type) - { - case CellType.Bool: - cell.SetBool((bool)value); - break; - - case CellType.CharPtr: - cell.SetString((string)value); - break; - - case CellType.Integer: - cell.SetInteger((int)value); - break; - - case CellType.Double: - cell.SetDouble((double)value); - break; - - case CellType.ObjectId: - cell.SetObjectId((ObjectId)value); - break; - - case CellType.Point: - cell.SetPoint((Point3d)value); - break; - - case CellType.Vector: - cell.SetVector((Vector3d)value); - break; - - case CellType.HardOwnerId: - cell.SetHardOwnershipId((ObjectId)value); - break; - - case CellType.HardPtrId: - cell.SetHardPointerId((ObjectId)value); - break; - - case CellType.SoftOwnerId: - cell.SetSoftOwnershipId((ObjectId)value); - break; - - case CellType.SoftPtrId: - cell.SetSoftPointerId((ObjectId)value); - break; - } - } - #endregion - - #region 子字典 - /// - /// 获取子字典 - /// - /// 根字典 - /// 事务 - /// 是否创建子字典 - /// 键值列表 - /// 字典 - public static DBDictionary? GetSubDictionary(this DBDictionary dict, - bool createSubDictionary, - IEnumerable dictNames, - DBTrans? trans = null) - { - DBDictionary? newdict = null; - trans ??= DBTrans.Top; - if (createSubDictionary) - { - using (dict.ForWrite()) - dict.TreatElementsAsHard = true; - - foreach (string name in dictNames) - { - if (dict.Contains(name)) - { - newdict = dict.GetAt(name, trans); - } - else - { - DBDictionary subDict = new(); - dict.SetAt(name, subDict, trans); - newdict = subDict; - newdict.TreatElementsAsHard = true; - } - } - } - else - { - foreach (string name in dictNames) - { - if (dict.Contains(name)) - newdict = dict.GetAt(name, trans); - else - return null; - } - } - return newdict; - } - - - ///// - ///// 获取对象扩展字典的子字典 - ///// - ///// 对象 - ///// 事务 - ///// 是否创建子字典 - ///// 键值列表 - ///// 字典 - // public static DBDictionary GetSubDictionary(this DBObject obj, bool createSubDictionary, params string[] dictNames) - // { - // return obj.GetXDictionary().GetSubDictionary(createSubDictionary, dictNames); - // } - - #endregion - - #region 组字典 - /// - /// 添加编组 - /// - /// 组名 - /// 实体Id集合 - /// 编组Id - public static ObjectId AddGroup(this DBDictionary dict, string name, ObjectIdCollection ids) - { - if (dict.Contains(name)) - return ObjectId.Null; - - using (dict.ForWrite()) - { - Group g = new(); - g.Append(ids); - dict.SetAt(name, g); - DBTrans.Top.Transaction.AddNewlyCreatedDBObject(g, true); - return g.ObjectId; - } - } - - /// - /// 添加编组 - /// - /// 组名 - /// 实体Id集合 - /// 编组Id - public static ObjectId AddGroup(this DBDictionary dict, string name, IEnumerable ids) - { - if (dict.Contains(name)) - return ObjectId.Null; - - using ObjectIdCollection idc = new(ids.ToArray());//需要using吗? 暂无测试 - return dict.AddGroup(name, idc); - } - - - /// - /// 按选择条件获取编组集合 - /// - /// 选择条件,过滤函数 - /// g.NumEntities < 2);]]> - /// 编组集合 - public static IEnumerable GetGroups(this DBDictionary dict, Func func) - { - return dict.GetAllObjects() - .Where(func); - } - - /// - /// 返回实体的所在编组的集合 - /// - /// 图元实体 - /// 编组集合 - public static IEnumerable GetGroups(this Entity ent) - { - return ent.GetPersistentReactorIds() - .Cast() - .Select(id => id.GetObject()) - .OfType(); - } - - /// - /// 移除所有的空组 - /// - /// 被移除编组的名称集合 - public static List RemoveNullGroup(this DBDictionary dict) - { - var groups = dict.GetGroups(g => g.NumEntities < 2); - List names = new(); - foreach (Group g in groups) - { - names.Add(g.Name); - using (g.ForWrite()) - g.Erase(); - } - return names; - } - - /// - /// 移除所有空组 - /// - /// 过滤条件,过滤要删除的组名的规则函数 - /// RemoveNullGroup(g => g.StartsWith("hah")) - /// 被移除编组的名称集合 - public static List RemoveNullGroup(this DBDictionary dict, Func func) - { - var groups = dict.GetGroups(g => g.NumEntities < 2); - List names = new(); - foreach (Group g in groups) - { - if (func(g.Name)) - { - names.Add(g.Name); - using (g.ForWrite()) - g.Erase(); - } - } - return names; - } - #endregion -} \ No newline at end of file diff --git a/src/IFoxCAD.Cad.Shared/ExtensionMethod/DBObjectEx.cs b/src/IFoxCAD.Cad.Shared/ExtensionMethod/DBObjectEx.cs deleted file mode 100644 index 9733d94..0000000 --- a/src/IFoxCAD.Cad.Shared/ExtensionMethod/DBObjectEx.cs +++ /dev/null @@ -1,153 +0,0 @@ -using System.Linq; - -namespace IFoxCAD.Cad; - -/// -/// 实体对象扩展类 -/// -public static class DBObjectEx -{ - #region Xdata扩展 - /// - /// 删除扩展数据 - /// - /// 对象实例 - /// 应用程序名称 - /// 要删除数据的组码 - public static void RemoveXData(this DBObject obj, string appName, DxfCode dxfCode) - { - if (obj.XData == null) - return; - XDataList data = obj.XData; - - // 测试命令 addxdata removexdata - // 移除指定App的扩展 - var indexs = data.GetXdataAppIndex(appName, new DxfCode[] { dxfCode }); - if (indexs.Count == 0) - return; - - for (int i = indexs.Count - 1; i >= 0; i--) - data.RemoveAt(indexs[i]); - - using (obj.ForWrite()) - obj.XData = data; - } - /// - /// 删除扩展数据 - /// - /// 对象实例 - /// 应用程序名称 - public static void RemoveXData(this DBObject obj, string appName) - { - if (obj.XData == null) - return; - foreach (var data in obj.XData) - { - // 直接赋值进去等于清空名称 - using var rb = new ResultBuffer(); - rb.Add(new((int)DxfCode.ExtendedDataRegAppName, appName)); - using (obj.ForWrite()) - obj.XData = rb; - } - } - - /// - /// 修改扩展数据 - /// - /// 对象实例 - /// 应用程序名称 - /// 要修改数据的组码 - /// 新的数据 - public static void ChangeXData(this DBObject obj, string appName, DxfCode dxfCode, object newvalue) - { - if (obj.XData == null) - return; - XDataList data = obj.XData; - - var indexs = data.GetXdataAppIndex(appName, new DxfCode[] { dxfCode }); - if (indexs.Count == 0) - return; - - for (int i = indexs.Count - 1; i >= 0; i--) - data[indexs[i]] = new TypedValue((short)dxfCode, newvalue); - - using (obj.ForWrite()) - obj.XData = data; - } - #endregion - - #region 读写模式切换 - -#line hidden // 调试的时候跳过它 - /// - /// 实体自动管理读写函数 - /// - /// 实体类型 - /// 实体对象 - /// 操作委托 - public static void ForWrite(this T obj, Action action) where T : DBObject - { - var _isNotifyEnabled = obj.IsNotifyEnabled; - var _isWriteEnabled = obj.IsWriteEnabled; - if (_isNotifyEnabled) - obj.UpgradeFromNotify(); - else if (!_isWriteEnabled) - obj.UpgradeOpen(); - - action?.Invoke(obj); - - if (_isNotifyEnabled) - obj.DowngradeToNotify(_isWriteEnabled); - else if (!_isWriteEnabled) - obj.DowngradeOpen(); - } - - /// - /// 打开模式提权 - /// - /// 实体对象 - /// 提权类对象 - public static UpgradeOpenManager ForWrite(this DBObject obj) - { - return new UpgradeOpenManager(obj); - } - - /// - /// 提权类 - /// - public class UpgradeOpenManager : IDisposable - { - private readonly DBObject _obj; - private readonly bool _isNotifyEnabled; - private readonly bool _isWriteEnabled; - - internal UpgradeOpenManager(DBObject obj) - { - _obj = obj; - _isNotifyEnabled = _obj.IsNotifyEnabled; - _isWriteEnabled = _obj.IsWriteEnabled; - if (_isNotifyEnabled) - _obj.UpgradeFromNotify(); - else if (!_isWriteEnabled) - _obj.UpgradeOpen(); - } - - #region IDisposable 成员 - - /// - /// 注销函数 - /// - public void Dispose() - { - if (_isNotifyEnabled) - _obj.DowngradeToNotify(_isWriteEnabled); - else if (!_isWriteEnabled) - _obj.DowngradeOpen(); - GC.SuppressFinalize(this); - } - - #endregion IDisposable 成员 - } -#line default - #endregion -} \ No newline at end of file diff --git a/src/IFoxCAD.Cad.Shared/ExtensionMethod/DBTransEx.cs b/src/IFoxCAD.Cad.Shared/ExtensionMethod/DBTransEx.cs deleted file mode 100644 index fdd4e91..0000000 --- a/src/IFoxCAD.Cad.Shared/ExtensionMethod/DBTransEx.cs +++ /dev/null @@ -1,91 +0,0 @@ -#define lack_test - -namespace IFoxCAD.Cad; - -#if lack_test -public static class DBTransEx -{ - /* - * 0x01 - * db.Purge(ids)是获取未硬引用(无引用?)的对象,也就可以删除的. - * 0x02 - * 如果一个图元引用一个图层, - * 假设这个图元是可以删除的(实际上它可能来自于词典记录的id) => 那么它被 db.Purge(ids) 识别, - * 但是这个图层因为有硬引用,所以不被 db.Purge(ids) 识别, - * 只能删除图元之后,循环第二次再通过 db.Purge(ids) 获取图层id. - * 0x03 - * 因为删除之后,符号表内的引用可能被修改,因此需要重复遍历符号表. - * 0x04 - * 试试循环事务 - * 0x05 - * 无法过滤外部参照图层,使得全部图层打开了 - */ - - /// - /// 清理符号表 - /// - /// - /// - /// 排除外部参照:默认true,为false时候会令图层全部显示再清理,包括冻结 - public static void Purge(this DBTrans tr, SymModes sym = SymModes.All, bool excludeXref = true) - { - using ObjectIdCollection ids = new(); - var db = tr.Database; - - if ((sym & SymModes.BlockTable) == SymModes.BlockTable) - { - if (!excludeXref) - GetAllIds(tr, tr.BlockTable, ids, excludeXref); - else - tr.BlockTable.ForEach(tabRec => { - if (!tabRec.IsFromExternalReference) - ids.Add(tabRec.Id); - }, checkIdOk: true); - } - if ((sym & SymModes.DimStyleTable) == SymModes.DimStyleTable) - GetAllIds(tr, tr.DimStyleTable, ids, excludeXref); - if ((sym & SymModes.LayerTable) == SymModes.LayerTable) - GetAllIds(tr, tr.LayerTable, ids, excludeXref); - if ((sym & SymModes.LinetypeTable) == SymModes.LinetypeTable) - GetAllIds(tr, tr.LinetypeTable, ids, excludeXref); - if ((sym & SymModes.TextStyleTable) == SymModes.TextStyleTable) - GetAllIds(tr, tr.TextStyleTable, ids, excludeXref); - if ((sym & SymModes.ViewportTable) == SymModes.ViewportTable) - GetAllIds(tr, tr.ViewportTable, ids, excludeXref); - if ((sym & SymModes.RegAppTable) == SymModes.RegAppTable) - GetAllIds(tr, tr.RegAppTable, ids, excludeXref); - - // SHUN007 说这两个表可能有错误 - if ((sym & SymModes.ViewTable) == SymModes.ViewTable) - GetAllIds(tr, tr.ViewTable, ids, excludeXref); - if ((sym & SymModes.UcsTable) == SymModes.UcsTable) - GetAllIds(tr, tr.UcsTable, ids, excludeXref); - - // Purge是查询能够清理的对象 - db.Purge(ids); - foreach (ObjectId id in ids) - id.Erase(); - } - - static void GetAllIds(DBTrans tr, - SymbolTable symbolTable, - ObjectIdCollection ids, - bool excludeXref = true) - where TTable : SymbolTable - where TRecord : SymbolTableRecord, new() - { - if (!excludeXref) - symbolTable.ForEach(id => ids.Add(id)); - else - { - symbolTable.ForEach(id => { - var tabRec = tr.GetObject(id); - if (tabRec == null) - return; - if (!tabRec.Name.Contains("|")) - ids.Add(tabRec.Id); - }); - } - } -} -#endif \ No newline at end of file diff --git a/src/IFoxCAD.Cad.Shared/ExtensionMethod/EditorEx.cs b/src/IFoxCAD.Cad.Shared/ExtensionMethod/EditorEx.cs deleted file mode 100644 index f9ec949..0000000 --- a/src/IFoxCAD.Cad.Shared/ExtensionMethod/EditorEx.cs +++ /dev/null @@ -1,1170 +0,0 @@ -using IFoxCAD.Com; - -namespace IFoxCAD.Cad; - -/// -/// 命令行扩展类 -/// -public static class EditorEx -{ - #region 选择集 - /// - /// 选择穿过一个点的对象 - /// - /// 命令行对象 - /// 点 - /// 过滤器 - /// 选择集结果类 - public static PromptSelectionResult SelectAtPoint(this Editor editor, Point3d point, - SelectionFilter? filter = default) - { - return editor.SelectCrossingWindow(point, point, filter); - } - - /// - /// 根据线宽创建图层选择集 - /// - /// 命令行对象 - /// 线宽 - /// 图层选择集 - public static SelectionSet SelectByLineWeight(this Editor editor, LineWeight lineWeight) - { - OpFilter filter = new OpEqual(370, lineWeight); - - var lays = - DBTrans.Top.LayerTable - .GetRecords() - .Where(ltr => ltr.LineWeight == lineWeight) - .Select(ltr => ltr.Name) - .ToArray(); - - if (lays.Length > 0) - { - filter = - new OpOr - { - filter, - new OpAnd - { - { 8, string.Join(",", lays) }, - { 370, LineWeight.ByLayer } - } - }; - } - - var res = editor.SelectAll(filter); - return res.Value; - } - - /// - /// 选择集 - /// - /// 命令行对象 - /// 模式 - /// 过滤器 - /// 消息 - /// 关键字和回调函数 - /// - public static PromptSelectionResult SSGet(this Editor editor, - string? mode = null, - SelectionFilter? filter = null, - string[]? messages = null, - Dictionary? keywords = null) - { - PromptSelectionOptions pso = new(); - PromptSelectionResult ss; - if (mode is not null) - { - mode = mode.ToUpper(); - pso.SinglePickInSpace = mode.Contains(":A"); - pso.RejectObjectsFromNonCurrentSpace = mode.Contains(":C"); - pso.AllowDuplicates = mode.Contains(":D"); - pso.SelectEverythingInAperture = mode.Contains(":E"); - pso.RejectObjectsOnLockedLayers = mode.Contains(":L"); - pso.PrepareOptionalDetails = mode.Contains(":N"); - pso.SingleOnly = mode.Contains(":S"); - pso.RejectPaperspaceViewport = mode.Contains(":V"); - pso.AllowSubSelections = mode.Contains("-A"); - pso.ForceSubSelections = mode.Contains("-F"); - } - if (messages is not null) - { - pso.MessageForAdding = messages[0]; - pso.MessageForRemoval = messages[1]; - } - - if (keywords is not null) - { - foreach (var keyword in keywords.Keys) - pso.Keywords.Add(keyword); - if (pso.MessageForRemoval is null) - pso.MessageForAdding = "选择对象"; - pso.MessageForAdding += $"[{string.Join(" / ", keywords.Keys.ToArray())}]"; - pso.KeywordInput += (s, e) => { - if (keywords.ContainsKey(e.Input)) - keywords[e.Input].Invoke(); - }; - } - try - { - if (filter is not null) - ss = editor.GetSelection(pso, filter); - else - ss = editor.GetSelection(pso); - } - catch (Exception) - { - //editor.WriteMessage($"\nKey is {e.Message}"); - throw; - } - return ss; - } - - - /* - * // 定义选择集选项 - * var pso = new PromptSelectionOptions - * { - * AllowDuplicates = false, // 重复选择 - * }; - * - * // getai遍历全图选择块有用到 - * var dic = new Dictionary() { - * { "Z,全部同名", ()=> { - * getai = BlockHelper.EnumAttIdentical.AllBlockName; - * SendEsc.Esc(); - * }}, - * { "X,动态块显示", ()=> { - * getai = BlockHelper.EnumAttIdentical.Display; - * }}, - * { "V,属性值-默认", ()=> { - * getai = BlockHelper.EnumAttIdentical.DisplayAndTagText; - * }}, - * // 允许以下操作,相同的会加入前面的 - * // { "V,属性值-默认|X,啊啊啊啊", ()=> { - * - * // }}, - * }; - * pso.SsgetAddKeys(dic); - * - * // 创建选择集过滤器,只选择块对象 - * var filList = new TypedValue[] { new TypedValue((int)DxfCode.Start, "INSERT") }; - * var filter = new SelectionFilter(filList); - * ssPsr = ed.GetSelection(pso, filter); - */ - - /// - /// 添加选择集关键字和回调 - /// - /// 选择集配置 - /// 关键字,回调委托 - /// - public static void SsgetAddKeys(this PromptSelectionOptions pso, - Dictionary dicActions) - { - Dictionary tmp = new(); - // 后缀名的|号切割,移除掉,组合成新的加入tmp - for (int i = dicActions.Count - 1; i >= 0; i--) - { - var pair = dicActions.ElementAt(i); - var key = pair.Key; - var keySp = key.Split('|'); - if (keySp.Length < 2) - continue; - - for (int j = 0; j < keySp.Length; j++) - { - var item = keySp[j]; - // 防止多个后缀通过|符越过词典约束同名 - // 后缀(key)含有,而且Action(value)不同,就把Action(value)累加到后面. - if (dicActions.ContainsKey(item)) - { - if (dicActions[item] != dicActions[key]) - dicActions[item] += dicActions[key]; - } - else - tmp.Add(item, dicActions[key]); - } - dicActions.Remove(key); - } - - foreach (var item in tmp) - dicActions.Add(item.Key, item.Value); - - // 去除关键字重复的,把重复的执行动作移动到前面 - for (int i = 0; i < dicActions.Count; i++) - { - var pair1 = dicActions.ElementAt(i); - var key1 = pair1.Key; - - for (int j = dicActions.Count - 1; j > i; j--) - { - var pair2 = dicActions.ElementAt(j); - var key2 = pair2.Key; - - if (key1.Split(',')[0] == key2.Split(',')[0]) - { - if (dicActions[key1] != dicActions[key2]) - dicActions[key1] += dicActions[key2]; - dicActions.Remove(key2); - } - } - } - - foreach (var item in dicActions) - { - var keySplitS = item.Key.Split(new string[] { ",", "|" }, StringSplitOptions.RemoveEmptyEntries); - for (int i = 0; i < keySplitS.Length; i += 2) - pso.Keywords.Add(keySplitS[i], keySplitS[i], - keySplitS[i + 1] + "(" + keySplitS[i] + ")"); - } - - // 回调的时候我想用Dict的O(1)索引, - // 但是此函数内进行new Dictionary() 在函数栈释放的时候,它被释放掉了. - // 因此 dicActions 参数的生命周期 - tmp = new(dicActions); - dicActions.Clear(); - foreach (var item in tmp) - dicActions.Add(item.Key.Split(',')[0], item.Value); - - var keyWords = pso.Keywords; - // 从选择集命令中显示关键字 - pso.MessageForAdding = keyWords.GetDisplayString(true); - // 关键字回调事件 ssget关键字 - pso.KeywordInput += (sender, e) => { - dicActions[e.Input].Invoke(); - }; - } - - - - - - - // #region 即时选择样板 - // /// - // /// 即时选择,框选更新关键字 - // /// - // public static void SelectTest() - // { - // Env.Editor.WriteMessage("\n[白嫖工具]--测试"); - // // 激活选中事件 - // Env.Editor.SelectionAdded += SelectTest_SelectionAdded; - // // 初始化坐标系 - // Env.Editor.CurrentUserCoordinateSystem = Matrix3d.Identity; - - // // 创建过滤器 - // var sf = new OpEqual(0, "arc"); - // var pso = new PromptSelectionOptions - // { - // MessageForAdding = "\n请选择对象:" - // }; - - // pso.Keywords.Add("Z"); - // pso.Keywords.Add("X"); - // pso.Keywords.Add("Q"); - // // 注册关键字 - // pso.KeywordInput += SelectTest_KeywordInput; - // try - // { - // // 用户选择 - // var psr = Env.Editor.GetSelection(pso, sf); - // // 处理代码 - - - // } - // catch (Exception ex)// 捕获关键字 - // { - // if (ex.Message == "XuError") - // { - // // 关闭关键字事件 - // pso.KeywordInput -= SelectTest_KeywordInput; - // // 关闭选中事件 - // Env.Editor.SelectionAdded -= SelectTest_SelectionAdded; - // // 重新调用自身 - // ZengLiangYuanJiao(); - // } - // } - // // 关闭关键字事件 - // pso.KeywordInput -= SelectTest_KeywordInput; - // // 关闭选中事件 - // Env.Editor.SelectionAdded -= SelectTest_SelectionAdded; - // } - - // /// - // /// 即时选择 - // /// - // /// - // /// - // private static void SelectTest_SelectionAdded(object sender, SelectionAddedEventArgs e) - // { - // // 关闭选中事件 - // Env.Editor.SelectionAdded -= SelectTest_SelectionAdded; - // using (var tr = new DBTrans()) - // { - // // 处理代码 - // for (int i = 0; i < e.AddedObjects.Count; i++) - // { - // // 处理完移除已处理的对象 - // e.Remove(i); - // } - // } - // // 激活选中事件 - // Env.Editor.SelectionAdded += SelectTest_SelectionAdded; - // } - - // /// - // /// 关键字响应 - // /// - // /// - // /// - // private static void SelectTest_KeywordInput(object sender, SelectionTextInputEventArgs e) - // { - // // 获取关键字 - // switch (e.Input) - // { - // case "Z": - // break; - // case "X": - // break; - - // case "Q": - // break; - // } - // // 抛出异常,用于更新提示信息 - // throw new ArgumentException("XuError"); - // } - // #endregion - #endregion - - #region Info - - /// - /// 带错误提示对话框的打印信息函数 - /// - /// 带格式项的字符串 - /// 指定格式化的对象数组 - public static void StreamMessage(string format, params object[] args) - { - StreamMessage(string.Format(format, args)); - } - - /// - /// 带错误提示对话框的打印信息函数 - /// - /// 打印信息 - public static void StreamMessage(string message) - { - try - { - if (HasEditor()) - WriteMessage(message); - else - InfoMessageBox(message); - } - catch (System.Exception ex) - { - Message(ex); - } - } - - /// - /// 异常信息对话框 - /// - /// 异常 - public static void Message(System.Exception ex) - { - try - { - System.Windows.Forms.MessageBox.Show( - ex.ToString(), - "Error", - System.Windows.Forms.MessageBoxButtons.OK, - System.Windows.Forms.MessageBoxIcon.Error); - } - catch - { - } - } - - /// - /// 提示信息对话框 - /// - /// 对话框的标题 - /// 对话框文本 - public static void InfoMessageBox(string caption, string message) - { - try - { - System.Windows.Forms.MessageBox.Show( - message, - caption, - System.Windows.Forms.MessageBoxButtons.OK, - System.Windows.Forms.MessageBoxIcon.Information); - } - catch (System.Exception ex) - { - Message(ex); - } - } - - /// - /// 提示信息对话框 - /// - /// 对话框的标题 - /// 带格式化项的对话框文本 - /// 指定格式化的对象数组 - public static void InfoMessageBox(string caption, string format, params object[] args) - { - InfoMessageBox(caption, string.Format(format, args)); - } - - /// - /// 提示信息对话框,默认标题为NFox.Cad - /// - /// 对话框文本 - public static void InfoMessageBox(string message) - { - InfoMessageBox("NFox.Cad", message); - } - - /// - /// 提示信息对话框 - /// - /// 带格式化项的对话框文本 - /// 指定格式化的对象数组 - public static void InfoMessageBox(string format, params object[] args) - { - InfoMessageBox(string.Format(format, args)); - } - - /// - /// 命令行打印字符串 - /// - /// 字符串 - public static void WriteMessage(string message) - { - try - { - if (Acceptable()) - Acap.DocumentManager.MdiActiveDocument.Editor.WriteMessage("\n" + message); - else - return; - } - catch (System.Exception ex) - { - Message(ex); - } - } - - /// - /// 命令行打印字符串 - /// - /// 带格式化项的文本 - /// 指定格式化的对象数组 - public static void WriteMessage(string format, params object[] args) - { - WriteMessage(string.Format(format, args)); - } - - /// - /// 判断是否有活动的编辑器对象 - /// - /// 有,没有 - public static bool HasEditor() - { - return Acap.DocumentManager.MdiActiveDocument is not null - && Acap.DocumentManager.Count != 0 - && Acap.DocumentManager.MdiActiveDocument.Editor is not null; - } - - /// - /// 判断是否可以打印字符串 - /// - /// 可以打印,不可以打印 - public static bool Acceptable() - { - return HasEditor() - && !Acap.DocumentManager.MdiActiveDocument.Editor.IsDragging; - } - - #endregion Info - - #region 画矢量线 - - /// - /// 根据点表返回矢量线的列表 - /// - /// 点表 - /// 是否闭合, 为闭合, 为不闭合 - /// - public static List GetLines(IEnumerable pnts, bool isClosed) - { - var itor = pnts.GetEnumerator(); - if (!itor.MoveNext()) - return new List(); - - List values = new(); - - TypedValue tvFirst = new((int)LispDataType.Point2d, itor.Current); - TypedValue tv1; - TypedValue tv2 = tvFirst; - - while (itor.MoveNext()) - { - tv1 = tv2; - tv2 = new TypedValue((int)LispDataType.Point2d, itor.Current); - values.Add(tv1); - values.Add(tv2); - } - - if (isClosed) - { - values.Add(tv2); - values.Add(tvFirst); - } - - return values; - } - - /// - /// 画矢量线 - /// - /// 编辑器对象 - /// 点表 - /// 颜色码 - /// 是否闭合, 为闭合, 为不闭合 - public static void DrawVectors(this Editor editor, IEnumerable pnts, short colorIndex, bool isClosed) - { - var rlst = - new LispList { { LispDataType.Int16, colorIndex } }; - rlst.AddRange(GetLines(pnts, isClosed)); - editor.DrawVectors(rlst, editor.CurrentUserCoordinateSystem); - } - - /// - /// 画矢量线 - /// - /// 编辑器对象 - /// 点表 - /// 颜色码 - public static void DrawVectors(this Editor editor, IEnumerable pnts, short colorIndex) - { - editor.DrawVectors(pnts, colorIndex, false); - } - - /// - /// 用矢量线画近似圆(正多边形) - /// - /// 编辑器对象 - /// 点表 - /// 颜色码 - /// 半径 - /// 多边形边的个数 - public static void DrawCircles(this Editor editor, IEnumerable pnts, short colorIndex, double radius, int numEdges) - { - var rlst = - new LispList { { LispDataType.Int16, colorIndex } }; - - foreach (Point2d pnt in pnts) - { - Vector2d vec = Vector2d.XAxis * radius; - double angle = Math.PI * 2 / numEdges; - - List tpnts = new() - { - pnt + vec - }; - for (int i = 1; i < numEdges; i++) - { - tpnts.Add(pnt + vec.RotateBy(angle * i)); - } - - rlst.AddRange(GetLines(tpnts, true)); - } - editor.DrawVectors(rlst, editor.CurrentUserCoordinateSystem); - } - - /// - /// 用矢量线画近似圆(正多边形) - /// - /// 编辑器对象 - /// 点 - /// 颜色码 - /// 半径 - /// 多边形边的个数 - public static void DrawCircle(this Editor editor, Point2d pnt, short colorIndex, double radius, int numEdges) - { - Vector2d vec = Vector2d.XAxis * radius; - double angle = Math.PI * 2 / numEdges; - - List pnts = new() - { - pnt + vec - }; - for (int i = 1; i < numEdges; i++) - pnts.Add(pnt + vec.RotateBy(angle * i)); - - editor.DrawVectors(pnts, colorIndex, true); - } - - #endregion - - #region 矩阵 - - /// - /// 获取UCS到WCS的矩阵 - /// - /// 命令行对象 - /// 变换矩阵 - public static Matrix3d GetMatrixFromUcsToWcs(this Editor editor) - { - return editor.CurrentUserCoordinateSystem; - } - - /// - /// 获取WCS到UCS的矩阵 - /// - /// 命令行对象 - /// 变换矩阵 - public static Matrix3d GetMatrixFromWcsToUcs(this Editor editor) - { - return editor.CurrentUserCoordinateSystem.Inverse(); - } - - /// - /// 获取MDCS(模型空间)到WCS的矩阵 - /// - /// 命令行对象 - /// 变换矩阵 - public static Matrix3d GetMatrixFromMDcsToWcs(this Editor editor) - { - Matrix3d mat; - using ViewTableRecord vtr = editor.GetCurrentView(); - mat = Matrix3d.PlaneToWorld(vtr.ViewDirection); - mat = Matrix3d.Displacement(vtr.Target - Point3d.Origin) * mat; - return Matrix3d.Rotation(-vtr.ViewTwist, vtr.ViewDirection, vtr.Target) * mat; - } - - /// - /// 获取WCS到MDCS(模型空间)的矩阵 - /// - /// 命令行对象 - /// 变换矩阵 - public static Matrix3d GetMatrixFromWcsToMDcs(this Editor editor) - { - return editor.GetMatrixFromMDcsToWcs().Inverse(); - } - - /// - /// 获取MDCS(模型空间)到PDCS(图纸空间)的矩阵 - /// - /// 命令行对象 - /// 变换矩阵 - public static Matrix3d GetMatrixFromMDcsToPDcs(this Editor editor) - { - if ((short)Env.GetVar("TILEMODE") == 1) - throw new ArgumentException("TILEMODE == 1..Espace papier uniquement"); - - Matrix3d mat = Matrix3d.Identity; - using DBTrans tr = new(); - var vp = tr.GetObject(editor.CurrentViewportObjectId); - if (vp == null) - return mat; - - if (vp.Number == 1) - { - try - { - editor.SwitchToModelSpace(); - vp = tr.GetObject(editor.CurrentViewportObjectId); - editor.SwitchToPaperSpace(); - } - catch - { - throw new Exception("Aucun fenêtre active...ErrorStatus.InvalidInput"); - } - } - if (vp == null) - return mat; - - Point3d vCtr = new(vp.ViewCenter.X, vp.ViewCenter.Y, 0.0); - mat = Matrix3d.Displacement(vCtr.GetAsVector().Negate()); - mat = Matrix3d.Displacement(vp.CenterPoint.GetAsVector()) * mat; - mat = Matrix3d.Scaling(vp.CustomScale, vp.CenterPoint) * mat; - return mat; - } - - /// - /// 获取PDCS(图纸空间)到MDCS(模型空间)的矩阵 - /// - /// 命令行对象 - /// 变换矩阵 - public static Matrix3d GetMatrixFromPDcsToMDcs(this Editor editor) - { - return editor.GetMatrixFromMDcsToPDcs().Inverse(); - } - - /// - /// 获取变换矩阵 - /// - /// 命令行对象 - /// 源坐标系 - /// 目标坐标系 - /// 变换矩阵 - public static Matrix3d GetMatrix(this Editor editor, CoordinateSystemCode from, CoordinateSystemCode to) - { -#if ac2009 - switch (from) - { - case CoordinateSystemCode.Wcs: - switch (to) - { - case CoordinateSystemCode.Ucs: - return editor.GetMatrixFromWcsToUcs(); - - case CoordinateSystemCode.MDcs: - return editor.GetMatrixFromMDcsToWcs(); - - case CoordinateSystemCode.PDcs: - throw new Exception("To be used only with DCS...ErrorStatus.InvalidInput"); - } - break; - case CoordinateSystemCode.Ucs: - switch (to) - { - case CoordinateSystemCode.Wcs: - return editor.GetMatrixFromUcsToWcs(); - - case CoordinateSystemCode.MDcs: - return editor.GetMatrixFromUcsToWcs() * editor.GetMatrixFromWcsToMDcs(); - - case CoordinateSystemCode.PDcs: - throw new Exception("To be used only with DCS... ErrorStatus.InvalidInput"); - } - break; - case CoordinateSystemCode.MDcs: - switch (to) - { - case CoordinateSystemCode.Wcs: - return editor.GetMatrixFromMDcsToWcs(); - - case CoordinateSystemCode.Ucs: - return editor.GetMatrixFromMDcsToWcs() * editor.GetMatrixFromWcsToUcs(); - - case CoordinateSystemCode.PDcs: - return editor.GetMatrixFromMDcsToPDcs(); - } - break; - case CoordinateSystemCode.PDcs: - switch (to) - { - case CoordinateSystemCode.Wcs: - throw new Exception("To be used only with DCS... ErrorStatus.InvalidInput"); - case CoordinateSystemCode.Ucs: - throw new Exception("To be used only with DCS... ErrorStatus.InvalidInput"); - case CoordinateSystemCode.MDcs: - return editor.GetMatrixFromPDcsToMDcs(); - } - break; - } - return Matrix3d.Identity; -#else - return (from, to) switch - { - (CoordinateSystemCode.Wcs, CoordinateSystemCode.Ucs) => editor.GetMatrixFromWcsToUcs(), - (CoordinateSystemCode.Wcs, CoordinateSystemCode.MDcs) => editor.GetMatrixFromWcsToMDcs(), - (CoordinateSystemCode.Ucs, CoordinateSystemCode.Wcs) => editor.GetMatrixFromUcsToWcs(), - (CoordinateSystemCode.Ucs, CoordinateSystemCode.MDcs) => editor.GetMatrixFromUcsToWcs() * editor.GetMatrixFromWcsToMDcs(), - (CoordinateSystemCode.MDcs, CoordinateSystemCode.Wcs) => editor.GetMatrixFromMDcsToWcs(), - (CoordinateSystemCode.MDcs, CoordinateSystemCode.Ucs) => editor.GetMatrixFromMDcsToWcs() * editor.GetMatrixFromWcsToUcs(), - (CoordinateSystemCode.MDcs, CoordinateSystemCode.PDcs) => editor.GetMatrixFromMDcsToPDcs(), - (CoordinateSystemCode.PDcs, CoordinateSystemCode.MDcs) => editor.GetMatrixFromPDcsToMDcs(), - (CoordinateSystemCode.PDcs, CoordinateSystemCode.Wcs or CoordinateSystemCode.Ucs) - or (CoordinateSystemCode.Wcs or CoordinateSystemCode.Ucs, CoordinateSystemCode.PDcs) => throw new Exception("To be used only with DCS...ErrorStatus.InvalidInput"), - (_, _) => Matrix3d.Identity - }; -#endif - } - - #endregion - - #region 缩放 - - /// - /// 缩放窗口范围 - /// - /// 命令行对象 - /// 窗口左下点 - /// 窗口右上点 - public static void ZoomWindow(this Editor ed, Point3d minPoint, Point3d maxPoint) - { - ViewTableRecord vtr = new(); - vtr.CopyFrom(ed.GetCurrentView()); - - var oldpnts = new Point3d[] { minPoint, maxPoint }; - var pnts = new Point3d[8]; - var dpnts = new Point3d[8]; - - var mat = ed.GetMatrixFromWcsToMDcs(); - for (int i = 0; i < 2; i++) - for (int j = 0; j < 2; j++) - for (int k = 0; k < 2; k++) - { - int n = i * 4 + j * 2 + k; - pnts[n] = new Point3d(oldpnts[i][0], oldpnts[j][1], oldpnts[k][2]); - dpnts[n] = pnts[n].TransformBy(mat); - } - - double xmin, xmax, ymin, ymax; - xmin = xmax = dpnts[0][0]; - ymin = ymax = dpnts[0][1]; - for (int i = 1; i < 8; i++) - { - xmin = Math.Min(xmin, dpnts[i][0]); - xmax = Math.Max(xmax, dpnts[i][0]); - ymin = Math.Min(ymin, dpnts[i][1]); - ymax = Math.Max(ymax, dpnts[i][1]); - } - - vtr.Width = xmax - xmin; - vtr.Height = ymax - ymin; - vtr.CenterPoint = (dpnts[0] + (dpnts[7] - dpnts[0]) / 2) - .Convert2d(Curve2dEx._planeCache); - - ed.SetCurrentView(vtr); - ed.Regen(); - } - - /// - /// 缩放窗口范围 - /// - /// 命令行对象 - /// 窗口范围点 - public static void ZoomWindow(this Editor ed, Extents3d ext) - { - ZoomWindow(ed, ext.MinPoint, ext.MaxPoint); - } - - /// - /// 缩放比例 - /// - /// 命令行对象 - /// 中心点 - /// 窗口宽 - /// 窗口高 - public static void Zoom(this Editor ed, Point3d CenPt, double width, double height) - { - using ViewTableRecord view = ed.GetCurrentView(); - view.Width = width; - view.Height = height; - view.CenterPoint = new Point2d(CenPt.X, CenPt.Y); - ed.SetCurrentView(view);// 更新当前视图 - } - - /// - /// 缩放窗口范围 - /// - /// 命令行对象 - /// 第一点 - /// 对角点 - /// 偏移距离 - public static void ZoomWindow(this Editor ed, Point3d lpt, Point3d rpt, double offsetDist = 0.00) - { - Extents3d extents = new(); - extents.AddPoint(lpt); - extents.AddPoint(rpt); - rpt = extents.MaxPoint + new Vector3d(offsetDist, offsetDist, 0); - lpt = extents.MinPoint - new Vector3d(offsetDist, offsetDist, 0); - Vector3d ver = rpt - lpt; - ed.Zoom(lpt + ver / 2, ver.X, ver.Y); - } - - - /// - /// 获取有效的数据库范围 - /// - /// 数据库 - /// 容差值:图元包围盒会超过数据库边界,用此参数扩大边界 - /// - public static Extents3d? GetValidExtents3d(this Database db, double extention = 1e-6) - { - db.UpdateExt(true);// 更新当前模型空间的范围 - var ve = new Vector3d(extention, extention, extention); - // 数据库没有图元的时候,min是大,max是小,导致新建出错 - // 数据如下: - // min.X == 1E20 && min.Y == 1E20 && min.Z == 1E20 && - // max.X == -1E20 && max.Y == -1E20 && max.Z == -1E20) - var a = db.Extmin; - var b = db.Extmax; - if (a.X < b.X && a.Y < b.Y) - return new Extents3d(db.Extmin - ve, db.Extmax + ve); - - return null; - } - - /// - /// 动态缩放 - /// - /// 命令行对象 - /// 偏移距离 - public static void ZoomExtents(this Editor ed, double offsetDist = 0.00) - { - Database db = ed.Document.Database; - // db.UpdateExt(true); // GetValidExtents3d内提供了 - var dbExtent = db.GetValidExtents3d(); - if (dbExtent == null) - ed.ZoomWindow(Point3d.Origin, new Point3d(1, 1, 0), offsetDist); - else - ed.ZoomWindow(db.Extmin, db.Extmax, offsetDist); - } - - /// - /// 根据实体对象的范围显示视图 - /// - /// 命令行对象 - /// Entity对象 - /// 偏移距离 - public static void ZoomObject(this Editor ed, Entity ent, double offsetDist = 0.00) - { - Extents3d ext = ent.GeometricExtents; - ed.ZoomWindow(ext.MinPoint, ext.MaxPoint, offsetDist); - } - - #endregion - - #region Get交互类 - - /// - /// 获取Point - /// - /// 命令行对象 - /// 提示信息 - /// 提示使用的基点 - /// - public static PromptPointResult GetPoint(this Editor ed, string Message, Point3d BasePoint) - { - PromptPointOptions ptOp = new(Message) - { - BasePoint = BasePoint, - UseBasePoint = true - }; - return ed.GetPoint(ptOp); - } - - /// - /// 获取double值 - /// - /// 命令行对象 - /// 提示信息 - /// double默认值 - /// - public static PromptDoubleResult GetDouble(this Editor ed, string Message, double DefaultValue = 1.0) - { - PromptDoubleOptions douOp = new(Message) - { - DefaultValue = DefaultValue - }; - return ed.GetDouble(douOp); - } - - /// - /// 获取int值 - /// - /// 命令行对象 - /// 提示信息 - /// double默认值 - /// - public static PromptIntegerResult GetInteger(this Editor ed, string Message, int DefaultValue = 1) - { - PromptIntegerOptions douOp = new(Message) - { - DefaultValue = DefaultValue - }; - return ed.GetInteger(douOp); - } - - /// - /// 获取string值 - /// - /// 命令行对象 - /// 提示信息 - /// string默认值 - /// - public static PromptResult GetString(this Editor ed, string Message, string DefaultValue = "") - { - PromptStringOptions strOp = new(Message) - { - DefaultValue = DefaultValue - }; - return ed.GetString(strOp); - } - - #endregion - - #region 执行lisp -#if NET35 - [DllImport("acad.exe", -#else - [DllImport("accore.dll", -#endif - CallingConvention = CallingConvention.Cdecl, EntryPoint = "acedInvoke")] - static extern int AcedInvoke(IntPtr args, out IntPtr result); - -#if NET35 - [DllImport("acad.exe", CharSet = CharSet.Unicode, CallingConvention = CallingConvention.Cdecl, - EntryPoint = "?acedEvaluateLisp@@YAHPB_WAAPAUresbuf@@@Z")] -#else - // 高版本此接口不能使用lisp(command "xx"),但是可以直接在自动运行接口上 - [DllImport("accore.dll", CharSet = CharSet.Unicode, CallingConvention = CallingConvention.Cdecl, - EntryPoint = "?acedEvaluateLisp@@YAHPEB_WAEAPEAUresbuf@@@Z")] -#endif - [System.Security.SuppressUnmanagedCodeSecurity]// 初始化默认值 - static extern int AcedEvaluateLisp(string lispLine, out IntPtr result); - -#if NET35 - [DllImport("acad.exe", -#else - [DllImport("accore.dll", -#endif - CharSet = CharSet.Auto, CallingConvention = CallingConvention.Cdecl, EntryPoint = "ads_queueexpr")] - static extern int Ads_queueexpr(string strExpr); - - public enum RunLispFlag : byte - { - AdsQueueexpr = 1, - AcedEvaluateLisp = 2, - SendStringToExecute = 4, - } - - /* - * 测试命令: - * [CommandMethod(nameof(CmdTest_RunLisp))] - * public static void CmdTest_RunLisp() - * { - * var res = RunLisp("(setq abc 10)"); - * } - * 调用方式: - * (command "CmdTest_RunLisp1") - * bug说明: - * AcedEvaluateLisp 接口 - * 在高版本调用时候没有运行成功,使得 !abc 没有值 - * 在cad08成功,此bug与CommandFlags无关 - * 解决方案: - * 0x01 用异步接口,但是这样是显式调用了: - * (setq thisdrawing (vla-get-activedocument (vlax-get-acad-object)))(vla-SendCommand thisdrawing "CmdTest_RunLisp1 ") - * 0x02 使用 Ads_queueexpr 接口 - */ - /// - /// 发送lisp语句字符串到cad执行 - /// - /// 编辑器对象 - /// lisp语句 - /// 运行方式 - /// 缓冲结果,返回值 - public static ResultBuffer? RunLisp(this Editor ed, string lispCode, RunLispFlag flag = RunLispFlag.AdsQueueexpr) - { - if ((flag & RunLispFlag.AdsQueueexpr) == RunLispFlag.AdsQueueexpr) - { - // 这个在08/12发送lisp不会出错,但是发送bo命令出错了. - // 0x01 设置 CommandFlags.Session 可以同步, - // 0x02 自执行发送lisp都是异步,用来发送 含有(command)的lisp的 - _ = Ads_queueexpr(lispCode + "\n"); - } - if ((flag & RunLispFlag.AcedEvaluateLisp) == RunLispFlag.AcedEvaluateLisp) - { - _ = AcedEvaluateLisp(lispCode, out IntPtr rb); - if (rb != IntPtr.Zero) - return (ResultBuffer)DisposableWrapper.Create(typeof(ResultBuffer), rb, true); - } - if ((flag & RunLispFlag.SendStringToExecute) == RunLispFlag.SendStringToExecute) - { - var dm = Acap.DocumentManager; - var doc = dm.MdiActiveDocument; - doc?.SendStringToExecute(lispCode + "\n", false, false, false); - } - return null; - } - #endregion - - #region Export - /// - /// 输出WMF
    - /// 此函数不适用于后台 - ///
    - /// 命令行对象 - /// 保存文件 - /// 选择集的对象,为null时候手选 - /// 是否清空选择集 - /// - public static void ComExportWMF(this Editor editor, string saveFile, - ObjectId[]? ids = null, bool wmfSetDel = false) - { - if (string.IsNullOrEmpty(saveFile)) - throw new ArgumentNullException(nameof(saveFile)); - if (File.Exists(saveFile)) - throw new FileFormatException("文件重复:" + saveFile); - - var dm = Acap.DocumentManager; - if (dm.Count == 0) - return; - - // 剔除后缀 - int dot = saveFile.LastIndexOf('.'); - if (dot != -1) - { - // 因为文件名可以有.所以后缀点必须是最后\的后面 - int s = saveFile.LastIndexOf('\\'); - if (s < dot) - saveFile = saveFile.Substring(0, dot); - } - - // ActiveSelectionSet: - // 第一次执行会触发选择,再次重复命令执行的时候,它会无法再选择(即使清空选择集). - // 因此此处netAPI进行选择,它就能读取当前选择集缓冲区的对象 - if (ids == null || ids.Length == 0) - { - var psr = editor.SelectImplied();// 预选 - if (psr.Status != PromptStatus.OK) - psr = editor.GetSelection();// 手选 - if (psr.Status != PromptStatus.OK) - return; - ids = psr.Value.GetObjectIds(); - } - editor.SetImpliedSelection(ids); - -#if zcad - var com = Acap.ZcadApplication; -#else - var com = Acap.AcadApplication; -#endif - var doc = com.GetProperty("ActiveDocument"); - var wmfSet = doc.GetProperty("ActiveSelectionSet"); - - // TODO 20221007 导出wmf的bug - // cad21 先net选择,再进行,此处再选择一次? - // cad21 调试期间无法选择性粘贴? - var exp = doc.Invoke("Export", saveFile, "wmf", wmfSet); // JPGOUT,PNGOUT - if (wmfSetDel) - wmfSet.Invoke("Delete"); - } - #endregion - - /// - /// 可以发送透明命令的状态
    - /// 福萝卜:这个应该是修正ribbon里输入丢焦点的问题,低版本可以不要 - ///
    - /// - /// - public static bool IsQuiescentForTransparentCommand(this Editor ed) - { -#if NET35 - //if (ed.IsQuiescent) - //{ - //} - return true; -#else - return ed.IsQuiescentForTransparentCommand; -#endif - } -} \ No newline at end of file diff --git a/src/IFoxCAD.Cad.Shared/ExtensionMethod/EntityBoundingInfo.cs b/src/IFoxCAD.Cad.Shared/ExtensionMethod/EntityBoundingInfo.cs deleted file mode 100644 index baf2fc4..0000000 --- a/src/IFoxCAD.Cad.Shared/ExtensionMethod/EntityBoundingInfo.cs +++ /dev/null @@ -1,281 +0,0 @@ -#define Debug_Cause_Error -namespace IFoxCAD.Cad; - -/// -/// AABB和OBB信息 -/// -public struct BoundingInfo -{ - public double MinX; - public double MinY; - public double MinZ; - - public double MaxX; - public double MaxY; - public double MaxZ; - - /// - /// AABB这里永远是0 - /// - public double Angle; - public bool IsEffective; - - public Point3d Min => new(MinX, MinY, MinZ); - public Point3d Max => new(MaxX, MaxY, MaxZ); - public Extents3d Extents3d => new(Min, Max); - public Extents2d Extents2d => new(MinX, MinY, MaxX, MaxY); - - public BoundingInfo(double minX, double minY, double minZ, - double maxX, double maxY, double maxZ, - bool isEffective, double angle = 0) - { - MinX = minX; - MinY = minY; - MinZ = minZ; - MaxX = maxX; - MaxY = maxY; - MaxZ = maxZ; - IsEffective = isEffective; - Angle = angle; - } - - public BoundingInfo(Point3d min, Point3d max, bool isEffective, double angle = 0) - : this(min.X, min.Y, min.Z, - max.X, max.Y, max.Z, - isEffective, angle) - { } - - // public BoundingInfo(Rect rect, double angle = 0) - // { - // MinX = rect.X; - // MinY = rect.Y; - // MinZ = 0; - // MaxX = rect.Right; - // MaxY = rect.Top; - // MaxZ = 0; - // Angle = angle; - // } - - public override string ToString() - { - return Extents3d.ToString(); - } - - public void Move(Point3d pt1, Point3d pt2) - { - var ve = pt1 - pt2; - MinX -= ve.X; - MinY -= ve.Y; - MinZ -= ve.Z; - MaxX -= ve.X; - MaxY -= ve.Y; - MaxZ -= ve.Z; - } -} - -public class EntityBoundingInfo -{ - #region 保存异常类型的日志 - /// - /// 包围盒错误文件路径 - /// - public static string BoxLogAddress - { - get - { - _BoxLogAddress ??= LogHelper.GetDefaultOption(nameof(GetBoundingInfo) + ".config"); - return _BoxLogAddress; - } - set { _BoxLogAddress = value; } - } - static string? _BoxLogAddress; - static readonly HashSet _typeNames; - /// - /// 为了保证更好的性能, - /// 只是在第一次调用此功能的时候进行读取, - /// 免得高频调用时候高频触发磁盘 - /// - static EntityBoundingInfo() - { - _typeNames = new(); - if (!File.Exists(BoxLogAddress)) - return; - ExceptionToHashSet(); - } - - /// - /// 读取日志的异常到容器 - /// - static void ExceptionToHashSet() - { - var old_LogAddress = LogHelper.LogAddress; - try - { - LogHelper.OptionFile(BoxLogAddress); - var logTxts = new FileLogger().ReadLog(); - for (int i = 0; i < logTxts.Length; i++) - { - var line = logTxts[i]; - if (line.Contains(nameof(LogTxt.备注信息))) - { - int index = line.IndexOf(":"); - index = line.IndexOf("\"", index) + 1;// 1是"\"" - int index2 = line.IndexOf("\"", index); - var msg = line.Substring(index, index2 - index); - _typeNames.Add(msg); - } - } - } - finally - { - LogHelper.LogAddress = old_LogAddress; - } - } - - /// - /// 写入容器类型到异常日志 - /// - /// - /// - static void ExceptionToLog(Exception e, Entity ent) - { - // 无法处理的错误类型将被记录 - // 如果错误无法try,而是cad直接致命错误,那么此处也不会被写入, - // 这种情况无法避免程序安全性,总不能写了日志再去删除日志词条,这样会造成频繁IO的 - // 遇到一个不认识的类型再去写入?然后记录它是否可以写入? - var old_LogAddress = LogHelper.LogAddress; - var old_FlagOutFile = LogHelper.FlagOutFile; - try - { - LogHelper.FlagOutFile = true; - LogHelper.OptionFile(BoxLogAddress); - e.WriteLog(ent.GetType().Name, LogTarget.FileNotException); - } - finally - { - LogHelper.LogAddress = old_LogAddress; - LogHelper.FlagOutFile = old_FlagOutFile; - } - } - #endregion - - /// - /// 获取图元包围盒 - /// - /// - /// (左下角,右上角,是否有效) - /// 异常: - /// 会将包围盒类型记录到所属路径中,以此查询 - public static BoundingInfo GetBoundingInfo(Entity ent) - { -#if Debug_Cause_Error - // 错误类型处理 - if (ent is AttributeDefinition) // 属性定义 - return new(Point3d.Origin, Point3d.Origin, false); - if (ent is Xline xline)// 参照线 - return new(xline.BasePoint, xline.BasePoint, true); - if (ent is Ray ray)// 射线 - return new(ray.BasePoint, ray.BasePoint, true); -#endif - // 指定类型处理 - if (ent is BlockReference brf) - return GetBoxInfoInBlockReference(brf); - if (ent is MText mText) - return GetBoxInfoInMText(mText); - - if (!_typeNames.Contains(ent.GetType().Name)) // 屏蔽天正等等缺失包围盒的类型 - try - { - return new(ent.GeometricExtents.MinPoint, ent.GeometricExtents.MaxPoint, true); - } - catch (Exception e) { ExceptionToLog(e, ent); } - return new(Point3d.Origin, Point3d.Origin, false); - } - - /// - /// 处理块参照 - /// - static BoundingInfo GetBoxInfoInBlockReference(BlockReference brf) - { - try - { - // 这个获取是原点附近,需要平移到块基点 - var fit = brf.GeometryExtentsBestFit(); - var minX = fit.MinPoint.X + brf.Position.X; - var minY = fit.MinPoint.Y + brf.Position.Y; - var minZ = fit.MinPoint.Z + brf.Position.Z; - var maxX = fit.MaxPoint.X + brf.Position.X; - var maxY = fit.MaxPoint.Y + brf.Position.Y; - var maxZ = fit.MaxPoint.Z + brf.Position.Z; - return new(minX, minY, minZ, maxX, maxY, maxZ, true); - } - catch - { - // 如果是一条参照线的组块,将导致获取包围盒时报错 - // 0x01 是否需要进入块内,然后拿到每个图元的BasePoint再计算中点?感觉过于复杂. - // 0x02 这个时候拿基点走就算了 - return new(brf.Position, brf.Position, true); - } - } - - /// - /// 处理多行文字 - /// - static BoundingInfo GetBoxInfoInMText(MText mtxt) - { - /* - * MText Aussehen - * ------------------------------------ - * | | | - * | | |ht - * | | | - * |-----wl-------插入点------wr-------| - * | | | - * | | |hb - * | | | - * ------------------------------------ - */ - - double width = mtxt.ActualWidth;// 实际宽度 - double height = mtxt.ActualHeight;// 实际高度 - double wl = 0.0; - double hb = 0.0; - - // 对齐方式 - switch (mtxt.Attachment) - { - case AttachmentPoint.TopCenter: - case AttachmentPoint.MiddleCenter: - case AttachmentPoint.BottomCenter: - wl = width * -0.5; - break; - case AttachmentPoint.TopRight: - case AttachmentPoint.MiddleRight: - case AttachmentPoint.BottomRight: - wl = -width; - break; - } - switch (mtxt.Attachment) - { - case AttachmentPoint.TopLeft: - case AttachmentPoint.TopCenter: - case AttachmentPoint.TopRight: - hb = -height;// 下边线到插入点的距离 - break; - case AttachmentPoint.MiddleLeft: - case AttachmentPoint.MiddleCenter: - case AttachmentPoint.MiddleRight: - hb = height * -0.5; - break; - } - - double wr = width + wl; - double ht = height + hb; - - Point3d center = mtxt.Location; - return new(center.X + wl, center.Y + hb, 0, - center.X + wr, center.Y + ht, 0, - true, - mtxt.Rotation); - } -} \ No newline at end of file diff --git a/src/IFoxCAD.Cad.Shared/ExtensionMethod/EntityEx.cs b/src/IFoxCAD.Cad.Shared/ExtensionMethod/EntityEx.cs deleted file mode 100644 index d839ba8..0000000 --- a/src/IFoxCAD.Cad.Shared/ExtensionMethod/EntityEx.cs +++ /dev/null @@ -1,474 +0,0 @@ -namespace IFoxCAD.Cad; - - -/// -/// 实体图元类扩展函数 -/// -public static class EntityEx -{ - #region 多段线端点坐标 - /// - /// 获取二维多段线的端点坐标 - /// - /// 二维多段线 - /// 事务 - /// 端点坐标集合 - public static IEnumerable GetPoints(this Polyline2d pl2d, DBTrans? trans = null) - { - trans ??= DBTrans.Top; - foreach (ObjectId id in pl2d) - yield return trans.GetObject(id)!.Position; - } - - /// - /// 获取三维多段线的端点坐标 - /// - /// 三维多段线 - /// 事务 - /// 端点坐标集合 - public static IEnumerable GetPoints(this Polyline3d pl3d, DBTrans? trans = null) - { - trans ??= DBTrans.Top; - foreach (ObjectId id in pl3d) - yield return trans.GetObject(id, OpenMode.ForRead)!.Position; - } - - /// - /// 获取多段线的端点坐标 - /// - /// 多段线 - /// 端点坐标集合 - public static IEnumerable GetPoints(this Polyline pl) - { - return - Enumerable - .Range(0, pl.NumberOfVertices) - .Select(pl.GetPoint3dAt); - } - #endregion - - #region 实体线性变换 - - /// - /// 移动实体 - /// - /// 实体 - /// 基点 - /// 目标点 - public static void Move(this Entity ent, Point3d from, Point3d to) - { - ent.TransformBy(Matrix3d.Displacement(to - from)); - } - - /// - /// 缩放实体 - /// - /// 实体 - /// 缩放基点坐标 - /// 缩放比例 - public static void Scale(this Entity ent, Point3d center, double scaleValue) - { - ent.TransformBy(Matrix3d.Scaling(scaleValue, center)); - } - - /// - /// 旋转实体 - /// - /// 实体 - /// 旋转中心 - /// 转角,弧度制,正数为顺时针 - /// 旋转平面的法向矢量 - public static void Rotation(this Entity ent, Point3d center, double angle, Vector3d normal) - { - ent.TransformBy(Matrix3d.Rotation(angle, normal, center)); - } - - /// - /// 在XY平面内旋转实体 - /// - /// 实体 - /// 旋转中心 - /// 转角,弧度制,正数为顺时针 - public static void Rotation(this Entity ent, Point3d center, double angle) - { - ent.TransformBy(Matrix3d.Rotation(angle, Vector3d.ZAxis.TransformBy(ent.Ecs), center)); - } - - /// - /// 按对称轴镜像实体 - /// - /// 实体 - /// 对称轴起点 - /// 对称轴终点 - public static void Mirror(this Entity ent, Point3d startPoint, Point3d endPoint) - { - ent.TransformBy(Matrix3d.Mirroring(new Line3d(startPoint, endPoint))); - } - - /// - /// 按对称面镜像实体 - /// - /// 实体 - /// 对称平面 - public static void Mirror(this Entity ent, Plane plane) - { - ent.TransformBy(Matrix3d.Mirroring(plane)); - } - - /// - /// 按对称点镜像实体 - /// - /// 实体 - /// 对称点 - public static void Mirror(this Entity ent, Point3d basePoint) - { - ent.TransformBy(Matrix3d.Mirroring(basePoint)); - } - - #endregion - - #region 实体范围 - /// - /// 获取实体集合的范围 - /// - /// 实体迭代器 - /// 实体集合的范围 - public static Extents3d GetExtents(this IEnumerable ents) - { - var ext = new Extents3d(); - foreach (var item in ents) - { - ext.AddExtents(item.GeometricExtents); - } - return ext; - } - #endregion - - #region 单行文字 - - /// - /// 更正单行文字的镜像属性 - /// - /// 单行文字 - public static void ValidateMirror(this DBText txt) - { - if (!txt.Database.Mirrtext) - { - txt.IsMirroredInX = false; - txt.IsMirroredInY = false; - } - } - #endregion - - #region 多行文字 - - /// - /// 炸散多行文字 - /// - /// 存储多行文字炸散之后的对象的类型 - /// 多行文字 - /// 存储对象变量 - /// 回调函数,用于处理炸散之后的对象 - /// - /// 多行文字炸散后的对象
    - /// 回调函数处理的结果 - ///
    - /// - public static void ExplodeFragments(this MText mt, T obj, Func mTextFragmentCallback) - { - mt.ExplodeFragments((f, o) => mTextFragmentCallback(f, (T)o), obj); - } - - /// - /// 获取多行文字的无格式文本 - /// - /// 多行文字 - /// 文本 - public static string GetUnFormatString(this MText mt) - { - List strs = new(); - mt.ExplodeFragments( - strs, - (f, o) => { - o.Add(f.Text); - return MTextFragmentCallbackStatus.Continue; - }); - return string.Join("", strs.ToArray()); - } - #endregion - - #region 圆弧 - - /// - /// 根据圆心、起点、终点来创建圆弧(二维) - /// - /// 起点 - /// 圆心 - /// 终点 - /// 圆弧 - public static Arc CreateArcSCE(Point3d startPoint, Point3d centerPoint, Point3d endPoint) - { - Arc arc = new(); - arc.SetDatabaseDefaults(); - arc.Center = centerPoint; - arc.Radius = centerPoint.DistanceTo(startPoint); - Vector2d startVector = new(startPoint.X - centerPoint.X, startPoint.Y - centerPoint.Y); - Vector2d endVector = new(endPoint.X - centerPoint.X, endPoint.Y - centerPoint.Y); - // 计算起始和终止角度 - arc.StartAngle = startVector.Angle; - arc.EndAngle = endVector.Angle; - return arc; - } - /// - /// 三点法创建圆弧(二维) - /// - /// 圆弧对象 - /// 起点 - /// 圆弧上的点 - /// 终点 - /// 圆弧 - public static Arc CreateArc(Point3d startPoint, Point3d pointOnArc, Point3d endPoint) - { - // 创建一个几何类的圆弧对象 - CircularArc3d geArc = new(startPoint, pointOnArc, endPoint); - // 将几何类圆弧对象的圆心和半径赋值给圆弧 -#if !gcad -#if NET35 - return (Arc)geArc.ToCurve(); -#else - return (Arc)Curve.CreateFromGeCurve(geArc); -#endif -#else - return (Arc)geArc.ToCurve(); -#endif - } - - /// - /// 根据起点、圆心和圆弧角度创建圆弧(二维) - /// - /// 圆弧对象 - /// 起点 - /// 圆心 - /// 圆弧角度 - /// 圆弧 - public static Arc CreateArc(Point3d startPoint, Point3d centerPoint, double angle) - { - Arc arc = new(); - arc.SetDatabaseDefaults(); - arc.Center = centerPoint; - arc.Radius = centerPoint.DistanceTo(startPoint); - Vector2d startVector = new(startPoint.X - centerPoint.X, startPoint.Y - centerPoint.Y); - arc.StartAngle = startVector.Angle; - arc.EndAngle = startVector.Angle + angle; - return arc; - } - - #endregion - - #region 圆 - - /// - /// 两点创建圆(两点中点为圆心) - /// - /// 起点 - /// 终点 - /// - public static Circle CreateCircle(Point3d startPoint, Point3d endPoint) - { - Circle circle = new(); - circle.SetDatabaseDefaults(); - circle.Center = startPoint.GetMidPointTo(endPoint); - circle.Radius = startPoint.DistanceTo(endPoint) * 0.5; - return circle; - } - - /// - /// 三点法创建圆(失败则返回Null) - /// - /// 第一点 - /// 第二点 - /// 第三点 - /// - public static Circle? CreateCircle(Point3d pt1, Point3d pt2, Point3d pt3) - { - // 先判断三点是否共线,得到pt1点指向pt2、pt2点的矢量 - Vector3d va = pt1.GetVectorTo(pt2); - Vector3d vb = pt1.GetVectorTo(pt3); - // 如两矢量夹角为0或180度(π弧度),则三点共线. - if (va.GetAngleTo(vb) == 0 | va.GetAngleTo(vb) == Math.PI) - return null; - - // 创建一个几何类的圆弧对象 - CircularArc3d geArc = new(pt1, pt2, pt3); - geArc.ToCircle(); - return geArc.ToCircle(); - } - - /// - /// 通过圆心,半径绘制圆形 - /// - /// 圆心 - /// 半径 - /// 图形的ObjectId - public static Circle? CreateCircle(Point3d center, double radius, double vex = 0, double vey = 0, double vez = 1) - { - return new Circle(center, new Vector3d(vex, vey, vez), radius);// 平面法向量XY方向 - } - - #endregion - - #region 块参照 - - #region 裁剪块参照 - - private const string filterDictName = "ACAD_FILTER"; - private const string spatialName = "SPATIAL"; - - /// - /// 裁剪块参照 - /// - /// 块参照 - /// 裁剪多边形点表 - public static void ClipBlockRef(this BlockReference bref, IEnumerable pt3ds) - { - Matrix3d mat = bref.BlockTransform.Inverse(); - var pts = - pt3ds - .Select(p => p.TransformBy(mat)) - .Select(p => new Point2d(p.X, p.Y)) - .ToCollection(); - - SpatialFilterDefinition sfd = new(pts, Vector3d.ZAxis, 0.0, 0.0, 0.0, true); - using SpatialFilter sf = new() { Definition = sfd }; - var dict = bref.GetXDictionary()!.GetSubDictionary(true, new string[] { filterDictName })!; - dict.SetAt(spatialName, sf); - // SetToDictionary(dict, spatialName, sf); - } - - /// - /// 裁剪块参照 - /// - /// 块参照 - /// 第一角点 - /// 第二角点 - public static void ClipBlockRef(this BlockReference bref, Point3d pt1, Point3d pt2) - { - Matrix3d mat = bref.BlockTransform.Inverse(); - pt1 = pt1.TransformBy(mat); - pt2 = pt2.TransformBy(mat); - - Point2dCollection pts = new() - { - new Point2d(Math.Min(pt1.X, pt2.X), Math.Min(pt1.Y, pt2.Y)), - new Point2d(Math.Max(pt1.X, pt2.X), Math.Max(pt1.Y, pt2.Y)) - }; - - using SpatialFilter sf = new() - { - Definition = new(pts, Vector3d.ZAxis, 0.0, 0.0, 0.0, true) - }; - var dict = bref.GetXDictionary()! - .GetSubDictionary(true, new string[] { filterDictName })!; - dict.SetAt(spatialName, sf); - // SetToDictionary(dict, spatialName, sf); -#if !acad - pts.Dispose(); -#endif - } - #endregion - - #region 属性 - /// - /// 更新动态块属性值 - /// - /// 动态块 - /// 属性值字典 - public static void ChangeBlockProperty(this BlockReference blockReference, - Dictionary propertyNameValues) - { - if (!blockReference.IsDynamicBlock) - return; - - using (blockReference.ForWrite()) - { - foreach (DynamicBlockReferenceProperty item in blockReference.DynamicBlockReferencePropertyCollection) - if (propertyNameValues.ContainsKey(item.PropertyName)) - item.Value = propertyNameValues[item.PropertyName]; - } - } - #endregion - - /// - /// 获取图元包围盒 - /// - /// - /// 包围盒信息 - /// 异常: - /// 会将包围盒类型记录到所属路径中,以此查询 - public static BoundingInfo GetBoundingBoxEx(this Entity ent) - { - return EntityBoundingInfo.GetBoundingInfo(ent); - } - -#line hidden - /// - /// 遍历块内 - /// - /// - /// - /// - /// - [System.Diagnostics.DebuggerStepThrough] - public static void ForEach(this BlockReference brf, Action action, DBTrans? tr = null) - { - if (action == null) - throw new ArgumentNullException(nameof(action)); - - tr ??= DBTrans.Top; - var btr = tr.GetObject(brf.BlockTableRecord); - if (btr == null) - return; - btr.ForEach(action); - } - /// - /// 遍历块内 - /// - /// - /// - /// - /// - [System.Diagnostics.DebuggerStepThrough] - public static void ForEach(this BlockReference brf, Action action, DBTrans? tr = null) - { - if (action == null) - throw new ArgumentNullException(nameof(action)); - - tr ??= DBTrans.Top; - var btr = tr.GetObject(brf.BlockTableRecord); - if (btr == null) - return; - btr.ForEach(action); - } - /// - /// 遍历块内 - /// - /// - /// - /// - /// - [System.Diagnostics.DebuggerStepThrough] - public static void ForEach(this BlockReference brf, Action action, DBTrans? tr = null) - { - if (action == null) - throw new ArgumentNullException(nameof(action)); - - tr ??= DBTrans.Top; - var btr = tr.GetObject(brf.BlockTableRecord); - if (btr == null) - return; - btr.ForEach(action); - } -#line default - - #endregion -} \ No newline at end of file diff --git a/src/IFoxCAD.Cad.Shared/ExtensionMethod/Enums.cs b/src/IFoxCAD.Cad.Shared/ExtensionMethod/Enums.cs deleted file mode 100644 index d100ded..0000000 --- a/src/IFoxCAD.Cad.Shared/ExtensionMethod/Enums.cs +++ /dev/null @@ -1,174 +0,0 @@ -namespace IFoxCAD.Cad; - -/// -/// 参照路径转换 -/// -public enum PathConverterModes : byte -{ - /// - /// 相对路径 - /// - Relative, - /// - /// 绝对路径 - /// - Complete -} - -/// -/// 参照绑定 -/// -public enum XrefModes : byte -{ - /// - /// 卸载 - /// - Unload, - /// - /// 重载 - /// - Reload, - /// - /// 拆离 - /// - Detach, - /// - /// 绑定 - /// - Bind, -} - -public enum SymModes : ushort -{ - /// - /// 块表 - /// - BlockTable = 1, - - /// - /// 图层表 - /// - LayerTable = 2, - /// - /// 文字样式表 - /// - TextStyleTable = 4, - /// - /// 注册应用程序表 - /// - RegAppTable = 8, - /// - /// 标注样式表 - /// - DimStyleTable = 16, - /// - /// 线型表 - /// - LinetypeTable = 32, - Option1 = LayerTable | TextStyleTable | DimStyleTable | LinetypeTable | RegAppTable, - - /// - /// 用户坐标系表 - /// - UcsTable = 64, - /// - /// 视图表 - /// - ViewTable = 128, - /// - /// 视口表 - /// - ViewportTable = 256, - Option2 = UcsTable | ViewTable | ViewportTable, - - // 全部 - All = BlockTable | Option1 | Option2 -} - - -/// -/// 坐标系类型枚举 -/// -public enum CoordinateSystemCode -{ - /// - /// 世界坐标系 - /// - Wcs = 0, - - /// - /// 用户坐标系 - /// - Ucs, - - /// - /// 模型空间坐标系 - /// - MDcs, - - /// - /// 图纸空间坐标系 - /// - PDcs -} - -/// -/// 方向的枚举 -/// -public enum OrientationType -{ - /// - /// 左转或逆时针 - /// - CounterClockWise, - /// - /// 右转或顺时针 - /// - ClockWise, - /// - /// 重合或平行 - /// - Parallel -} - -/// -/// 点与多边形的关系类型枚举 -/// -public enum PointOnRegionType -{ - /// - /// 多边形内部 - /// - Inside, - - /// - /// 多边形上 - /// - On, - - /// - /// 多边形外 - /// - Outside, - - /// - /// 错误 - /// - Error -} - - - -public enum FontTTF -{ - [Description("宋体.ttf")] - 宋体, - [Description("simfang.ttf")] - 仿宋, - [Description("FSGB2312.ttf")] - 仿宋GB2312, - [Description("Arial.ttf")] - Arial, - [Description("Romans")] - Romans -} \ No newline at end of file diff --git a/src/IFoxCAD.Cad.Shared/ExtensionMethod/Filer/DwgFiler.cs b/src/IFoxCAD.Cad.Shared/ExtensionMethod/Filer/DwgFiler.cs deleted file mode 100644 index 6820f37..0000000 --- a/src/IFoxCAD.Cad.Shared/ExtensionMethod/Filer/DwgFiler.cs +++ /dev/null @@ -1,506 +0,0 @@ -namespace IFoxCAD.Cad; - -/* - Arx自定义实体类,加 读函数(assertReadEnabled)和写函数(assertWriteEnabled) - - 所有属性位置都不要改动位置,因为涉及序列化 - [Serializable] 设置类 可以序列化 - [Newtonsoft.Json.JsonIgnore] 设置成员 不可序列化 -*/ - -#if NewtonsoftJson -[JsonConverter(typeof(ObjectIdConverter))] -#endif -[Serializable] -public class DwgFiler : Cad_DwgFiler -{ -#if NET35 - public int m_Position; -#else - public long m_Position; -#endif - public FilerType m_FilerType; - public Cad_ErrorStatus m_FilerStatus; - public List AddressList; - public int AddressListPt = 0; - public List BinaryChunkList; - public int BinaryChunkListPt = 0; - public List BooleanList; - public int BooleanListPt = 0; - public List ByteList; - public int ByteListPt = 0; - public List BytesList; - public int BytesListPt = 0; - public List DoubleList; - public int DoubleListPt = 0; - public List HandleList; - public int HandleListPt = 0; - //[NonSerialized] - //[ScriptIgnore] - public List HardOwnershipIdList; - public int HardOwnershipIdListPt = 0; - //[NonSerialized] - //[ScriptIgnore] - public List HardPointerIdList; - public int HardPointerIdListPt = 0; - public List Int16List; - public int Int16ListPt = 0; - public List Int32List; - public int Int32ListPt = 0; -#if !NET35 - public List Int64List; - public int Int64ListPt = 0; -#endif - public List Point2dList; - public int Point2dListPt = 0; - public List Point3dList; - public int Point3dListPt = 0; - public List Scale3dList; - public int Scale3dListPt = 0; - //[NonSerialized] - //[ScriptIgnore] - public List SoftOwnershipIdList; - public int SoftOwnershipIdListPt = 0; - //[NonSerialized] - //[ScriptIgnore] - public List SoftPointerIdList; - public int SoftPointerIdListPt = 0; - public List StringList; - public int StringListPt = 0; - public List Uint16List; - public int uint16ListPt = 0; - public List Uint32List; - public int uint32ListPt = 0; -#if !NET35 - public List Uint64List; - public int uint64ListPt = 0; -#endif - public List Vector2dList; - public int Vector2dListPt = 0; - public List Vector3dList; - public int Vector3dListPt = 0; - - public DwgFiler() - { - m_Position = 0; - m_FilerType = FilerType.CopyFiler; - m_FilerStatus = Cad_ErrorStatus.OK; - AddressList = new(); - BinaryChunkList = new(); - BooleanList = new(); - ByteList = new(); - BytesList = new(); - DoubleList = new(); - HandleList = new(); - HardOwnershipIdList = new(); - HardPointerIdList = new(); - Int16List = new(); - Int32List = new(); -#if !NET35 - Int64List = new(); -#endif - Point2dList = new(); - Point3dList = new(); - Scale3dList = new(); - SoftOwnershipIdList = new(); - SoftPointerIdList = new(); - StringList = new(); - Uint16List = new(); - Uint32List = new(); -#if !NET35 - Uint64List = new(); -#endif - Vector2dList = new(); - Vector3dList = new(); - } - -#if NET35 - public override int Position => m_Position; -#else - public override long Position => m_Position; -#endif - public override FilerType FilerType => m_FilerType; - - public override Cad_ErrorStatus FilerStatus - { - get { return m_FilerStatus; } - set { m_FilerStatus = value; } - } - - public override IntPtr ReadAddress() - { - if (AddressList.Count == 0) - return new(); - return AddressList[AddressListPt++]; - } - - public override byte[]? ReadBinaryChunk() - { - if (BinaryChunkList.Count == 0) - return null; - return BinaryChunkList[BinaryChunkListPt++]; - } - - public override bool ReadBoolean() - { - if (BooleanList.Count == 0) - return false; - return BooleanList[BooleanListPt++]; - } - - public override byte ReadByte() - { - if (ByteList.Count == 0) - return 0; - return ByteList[ByteListPt++]; - } - - public override void ReadBytes(byte[] value) - { - if (ByteList.Count == 0) - return; - value = new byte[BytesList[BytesListPt].Length]; - BytesList[BytesListPt++].CopyTo(value, 0); - } - - public override double ReadDouble() - { - if (DoubleList.Count == 0) - return 0; - return DoubleList[DoubleListPt++]; - } - - public override Handle ReadHandle() - { - if (HandleList.Count == 0) - return new(); - return HandleList[HandleListPt++]; - } - - public override ObjectId ReadHardOwnershipId() - { - if (HardOwnershipIdList.Count == 0) - return new(); - return HardOwnershipIdList[HardOwnershipIdListPt++]; - } - - public override ObjectId ReadHardPointerId() - { - if (HardPointerIdList.Count == 0) - return new(); - return HardPointerIdList[HardPointerIdListPt++]; - } - - public override short ReadInt16() - { - if (Int16List.Count == 0) - return 0; - return Int16List[Int16ListPt++]; - } - - public override int ReadInt32() - { - if (Int32List.Count == 0) - return 0; - return Int32List[Int32ListPt++]; - } - -#if !NET35 - public override long ReadInt64() - { - if (Int64List.Count == 0) - return 0; - return Int64List[Int64ListPt++]; - } -#endif - - public override Point2d ReadPoint2d() - { - if (Point2dList.Count == 0) - return new(); - return Point2dList[Point2dListPt++]; - } - - public override Point3d ReadPoint3d() - { - if (Point3dList.Count == 0) - return new(); - return Point3dList[Point3dListPt++]; - } - - public override Scale3d ReadScale3d() - { - if (Scale3dList.Count == 0) - return new(); - return Scale3dList[Scale3dListPt++]; - } - - public override ObjectId ReadSoftOwnershipId() - { - if (SoftOwnershipIdList.Count == 0) - return new(); - return SoftOwnershipIdList[SoftOwnershipIdListPt++]; - } - - public override ObjectId ReadSoftPointerId() - { - if (SoftPointerIdList.Count == 0) - return new(); - return SoftPointerIdList[SoftPointerIdListPt++]; - } - - public override string? ReadString() - { - if (StringList.Count == 0) - return null; - return StringList[StringListPt++]; - } - - public override ushort ReadUInt16() - { - if (Uint16List.Count == 0) - return 0; - return Uint16List[uint16ListPt++]; - } - - public override uint ReadUInt32() - { - if (Uint32List.Count == 0) - return 0; - return Uint32List[uint32ListPt++]; - } - -#if !NET35 - public override ulong ReadUInt64() - { - if (Uint64List.Count == 0) - return 0; - return Uint64List[uint64ListPt++]; - } -#endif - - public override Vector2d ReadVector2d() - { - if (Vector2dList.Count == 0) - return new(); - return Vector2dList[Vector2dListPt++]; - } - - public override Vector3d ReadVector3d() - { - if (Vector3dList.Count == 0) - return new(); - return Vector3dList[Vector3dListPt++]; - } - - public override void ResetFilerStatus() - { - AddressList.Clear(); - AddressListPt = 0; - BinaryChunkList.Clear(); - BinaryChunkListPt = 0; - BooleanList.Clear(); - BooleanListPt = 0; - ByteList.Clear(); - ByteListPt = 0; - BytesList.Clear(); - BytesListPt = 0; - DoubleList.Clear(); - DoubleListPt = 0; - HandleList.Clear(); - HandleListPt = 0; - HardOwnershipIdList.Clear(); - HardOwnershipIdListPt = 0; - HardPointerIdList.Clear(); - HardPointerIdListPt = 0; - Int16List.Clear(); - Int16ListPt = 0; - Int32List.Clear(); - Int32ListPt = 0; -#if !NET35 - Int64List.Clear(); - Int64ListPt = 0; -#endif - Point2dList.Clear(); - Point2dListPt = 0; - Point3dList.Clear(); - Point3dListPt = 0; - Scale3dList.Clear(); - Scale3dListPt = 0; - SoftOwnershipIdList.Clear(); - SoftOwnershipIdListPt = 0; - SoftPointerIdList.Clear(); - SoftPointerIdListPt = 0; - StringList.Clear(); - StringListPt = 0; - Uint16List.Clear(); - uint16ListPt = 0; - Uint32List.Clear(); - uint32ListPt = 0; -#if !NET35 - Uint64List.Clear(); - uint64ListPt = 0; -#endif - Vector2dList.Clear(); - Vector2dListPt = 0; - Vector3dList.Clear(); - Vector3dListPt = 0; - - m_FilerType = FilerType.CopyFiler; - } - -#if zcad // 中望官方的问题 - public override void Seek(int offset, int method) - { - var ed = Acap.DocumentManager.MdiActiveDocument.Editor; - ed.WriteMessage(MethodInfo.GetCurrentMethod().Name + " = " + " \n "); - } - public override void Seek(long offset, int method) - { - Seek((int)offset, method); - } -#endif - -#if acad || gcad - public override void Seek( -#if NET35 - int -#else - long -#endif - offset, int method) - { - var ed = Acap.DocumentManager.MdiActiveDocument.Editor; - ed.WriteMessage(MethodInfo.GetCurrentMethod().Name + " = " + " \n "); - } -#endif - - public override void WriteAddress(IntPtr value) - { - AddressList.Add(value); - } - - public override void WriteBinaryChunk(byte[] chunk) - { - BinaryChunkList.Add(chunk); - } - - public override void WriteBoolean(bool value) - { - BooleanList.Add(value); - } - - public override void WriteByte(byte value) - { - ByteList.Add(value); - } - - public override void WriteBytes(byte[] value) - { - BytesList.Add(value); - } - - public override void WriteDouble(double value) - { - DoubleList.Add(value); - } - - public override void WriteHandle(Handle handle) - { - HandleList.Add(handle); - } - - public override void WriteHardOwnershipId(ObjectId value) - { - HardOwnershipIdList.Add(value); - } - - public override void WriteHardPointerId(ObjectId value) - { - HardPointerIdList.Add(value); - } - - public override void WriteInt16(short value) - { - Int16List.Add(value); - } - - public override void WriteInt32(int value) - { - Int32List.Add(value); - } - -#if !NET35 - public override void WriteInt64(long value) - { - Int64List.Add(value); - } -#endif - public override void WritePoint2d(Point2d value) - { - Point2dList.Add(value); - } - - public override void WritePoint3d(Point3d value) - { - Point3dList.Add(value); - } - - public override void WriteScale3d(Scale3d value) - { - Scale3dList.Add(value); - } - - public override void WriteSoftOwnershipId(ObjectId value) - { - SoftOwnershipIdList.Add(value); - } - - public override void WriteSoftPointerId(ObjectId value) - { - SoftPointerIdList.Add(value); - } - - public override void WriteString(string value) - { - StringList.Add(value); - } - - public override void WriteUInt16(ushort value) - { - Uint16List.Add(value); - } - - public override void WriteUInt32(uint value) - { - Uint32List.Add(value); - } - -#if !NET35 - public override void WriteUInt64(ulong value) - { - Uint64List.Add(value); - } -#endif - - public override void WriteVector2d(Vector2d value) - { - Vector2dList.Add(value); - } - - public override void WriteVector3d(Vector3d value) - { - Vector3dList.Add(value); - } - - public override string ToString() - { -#if NewtonsoftJson - return JsonConvert.SerializeObject(this, Formatting.Indented); -#else - JavaScriptSerializer serializer = new(); - serializer.RegisterConverters(new[] { new ObjectIdConverter() }); - return serializer.Serialize(this); -#endif - } -} \ No newline at end of file diff --git a/src/IFoxCAD.Cad.Shared/ExtensionMethod/Filer/DwgFilerEx.cs b/src/IFoxCAD.Cad.Shared/ExtensionMethod/Filer/DwgFilerEx.cs deleted file mode 100644 index f91fb4c..0000000 --- a/src/IFoxCAD.Cad.Shared/ExtensionMethod/Filer/DwgFilerEx.cs +++ /dev/null @@ -1,87 +0,0 @@ -namespace IFoxCAD.Cad; - -/// -/// Dwg序列化 -/// -public class DwgFilerEx -{ - #region 成员 - DBObject? _entity; - public DwgFiler DwgFiler { get; private set; } - #endregion - - #region 构造 - /// - /// Dwg序列化 - /// - public DwgFilerEx(DwgFiler? Cad_DwgFiler = null) - { - if (Cad_DwgFiler == null) - Cad_DwgFiler = new(); - DwgFiler = Cad_DwgFiler; - } - - /// - /// Dwg序列化 - /// - public DwgFilerEx(DBObject entity) : this() - { - DwgOut(entity); - } - - #endregion - - #region 方法 - public void DwgOut(DBObject entity) - { - _entity = entity; - _entity.DwgOut(DwgFiler); - } - - public void DwgIn() - { - _entity?.DwgIn(DwgFiler); - } - - /// - /// 反序列化 - /// - /// - /// - public static DwgFilerEx? DeserializeObject(string json) - { -#if NewtonsoftJson - return JsonConvert.DeserializeObject(json); -#else - JavaScriptSerializer serializer = new(); - serializer.RegisterConverters(new[] { new ObjectIdConverter() }); - return serializer.Deserialize(json); -#endif - } - - /// - /// 序列化 - /// - /// - public string SerializeObject() - { -#if NewtonsoftJson - return JsonConvert.SerializeObject(DwgFiler, Formatting.Indented); -#else - JavaScriptSerializer serializer = new(); - serializer.RegisterConverters(new[] { new ObjectIdConverter() }); - return serializer.Serialize(DwgFiler); -#endif - } - - public override string ToString() - { - return DwgFiler.ToString(); - } - - public static implicit operator Cad_DwgFiler(DwgFilerEx df) - { - return df.DwgFiler; - } - #endregion -} \ No newline at end of file diff --git a/src/IFoxCAD.Cad.Shared/ExtensionMethod/Filer/DxfFiler.cs b/src/IFoxCAD.Cad.Shared/ExtensionMethod/Filer/DxfFiler.cs deleted file mode 100644 index 975c505..0000000 --- a/src/IFoxCAD.Cad.Shared/ExtensionMethod/Filer/DxfFiler.cs +++ /dev/null @@ -1,221 +0,0 @@ -#if acad -namespace IFoxCAD.Cad; - -/* 此处暂未完成,无任何测试,尚且不知道怎么用 */ -using System.Runtime.Remoting; - -public class DxfFiler : Cad_DxfFiler -{ - public DxfFiler(IntPtr unmanagedPointer, [MarshalAs(UnmanagedType.U1)] bool autoDelete) : base(unmanagedPointer, autoDelete) - { - } - - public override bool IsModifyingExistingObject => base.IsModifyingExistingObject; - - public override double Thickness => base.Thickness; - - public override double Elevation => base.Elevation; - - public override bool AtEmbeddedObjectStart => base.AtEmbeddedObjectStart; - - public override bool AtEndOfObject => base.AtEndOfObject; - - public override bool AtExtendedData => base.AtExtendedData; - - public override bool AtEndOfFile => base.AtEndOfFile; - - public override int Precision { get => base.Precision; set => base.Precision = value; } - - public override string ErrorMessage => base.ErrorMessage; - - public override bool AtSubclassData(string value) - { - return base.AtSubclassData(value); - } - - public override object Clone() - { - return base.Clone(); - } - - public override void CopyFrom(RXObject source) - { - base.CopyFrom(source); - } - - public override ObjRef CreateObjRef(Type requestedType) - { - return base.CreateObjRef(requestedType); - } - - public override bool Equals(object obj) - { - return base.Equals(obj); - } - - public override void FilerStatus() - { - base.FilerStatus(); - } - - public override int GetHashCode() - { - return base.GetHashCode(); - } - - public override void HaltAtClassBoundaries(bool value) - { - base.HaltAtClassBoundaries(value); - } - - public override object InitializeLifetimeService() - { - return base.InitializeLifetimeService(); - } - - public override void PushBackItem() - { - base.PushBackItem(); - } - - public override ResultBuffer ReadResultBuffer() - { - return base.ReadResultBuffer(); - } - - public override void ResetFilerStatus() - { - base.ResetFilerStatus(); - } - - public override int RewindFiler() - { - return base.RewindFiler(); - } - - public override void SetError(string format, params string[] values) - { - base.SetError(format, values); - } - - public override void SetError(Cad_ErrorStatus value, string format, params string[] values) - { - base.SetError(value, format, values); - } - - public override string ToString() - { - return base.ToString(); - } - - public override void WriteBool(DxfCode opCode, bool value) - { - base.WriteBool(opCode, value); - } - - public override void WriteBoolean(DxfCode opCode, bool value) - { - base.WriteBoolean(opCode, value); - } - - public override void WriteByte(DxfCode opCode, byte value) - { - base.WriteByte(opCode, value); - } - - public override void WriteBytes(DxfCode opCode, byte[] chunk) - { - base.WriteBytes(opCode, chunk); - } - - public override void WriteDouble(DxfCode opCode, double value, int precision) - { - base.WriteDouble(opCode, value, precision); - } - - public override void WriteEmbeddedObjectStart() - { - base.WriteEmbeddedObjectStart(); - } - - public override void WriteHandle(DxfCode opCode, Handle value) - { - base.WriteHandle(opCode, value); - } - - public override void WriteInt16(DxfCode opCode, short value) - { - base.WriteInt16(opCode, value); - } - - public override void WriteInt32(DxfCode opCode, int value) - { - base.WriteInt32(opCode, value); - } - - public override void WriteObjectId(DxfCode opCode, ObjectId value) - { - base.WriteObjectId(opCode, value); - } - - public override void WritePoint2d(DxfCode opCode, Point2d value, int precision) - { - base.WritePoint2d(opCode, value, precision); - } - - public override void WritePoint3d(DxfCode opCode, Point3d value, int precision) - { - base.WritePoint3d(opCode, value, precision); - } - - public override void WriteResultBuffer(ResultBuffer buffer) - { - base.WriteResultBuffer(buffer); - } - - public override void WriteScale3d(DxfCode opCode, Scale3d value, int precision) - { - base.WriteScale3d(opCode, value, precision); - } - - public override void WriteString(DxfCode opCode, string value) - { - base.WriteString(opCode, value); - } - - public override void WriteUInt16(DxfCode opCode, ushort value) - { - base.WriteUInt16(opCode, value); - } - - public override void WriteUInt32(DxfCode opCode, uint value) - { - base.WriteUInt32(opCode, value); - } - - public override void WriteVector2d(DxfCode opCode, Vector2d value, int precision) - { - base.WriteVector2d(opCode, value, precision); - } - - public override void WriteVector3d(DxfCode opCode, Vector3d value, int precision) - { - base.WriteVector3d(opCode, value, precision); - } - - public override void WriteXDataStart() - { - base.WriteXDataStart(); - } - - protected override void DeleteUnmanagedObject() - { - base.DeleteUnmanagedObject(); - } - - protected override void Dispose(bool disposing) - { - base.Dispose(disposing); - } -} -#endif \ No newline at end of file diff --git a/src/IFoxCAD.Cad.Shared/ExtensionMethod/GeometryEx.cs b/src/IFoxCAD.Cad.Shared/ExtensionMethod/GeometryEx.cs deleted file mode 100644 index 58e9da3..0000000 --- a/src/IFoxCAD.Cad.Shared/ExtensionMethod/GeometryEx.cs +++ /dev/null @@ -1,671 +0,0 @@ -namespace IFoxCAD.Cad; - -using System.Drawing; - -/// -/// 图形扩展类 -/// -public static class GeometryEx -{ - #region Point&Circle - - /// - /// 判断点与多边形的关系 - /// - /// 多边形顶点集合 - /// 点 - /// 点与多边形的关系 - public static PointOnRegionType PointOnRegion(this IEnumerable pts, Point2d pt) - { - // 遍历点集并生成首尾连接的多边形 - var ptlst = new LoopList(pts); - if (ptlst.Count < 3) - return PointOnRegionType.Error; - - var ls2ds = new List(); - foreach (var node in ptlst.GetNodes()) - { - ls2ds.Add(new LineSegment2d(node.Value, node.Next!.Value)); - } - var cc2d = new CompositeCurve2d(ls2ds.ToArray()); - - // 在多边形上? - if (cc2d.IsOn(pt)) - return PointOnRegionType.On; - - // 在最小包围矩形外? - var bb2d = cc2d.BoundBlock; - if (!bb2d.Contains(pt)) - return PointOnRegionType.Outside; - - // - bool flag = false; - foreach (var node in ptlst.GetNodes()) - { - var pt1 = node.Value; - var pt2 = node.Next!.Value; - if (pt.Y < pt1.Y && pt.Y < pt2.Y) - continue; - if (pt1.X < pt.X && pt2.X < pt.X) - continue; - Vector2d vec = pt2 - pt1; - double t = (pt.X - pt1.X) / vec.X; - double y = t * vec.Y + pt1.Y; - if (y < pt.Y && t >= 0 && t <= 1) - flag = !flag; - } - return - flag ? - PointOnRegionType.Inside : PointOnRegionType.Outside; - } - - /// - /// 判断点与多边形的关系 - /// - /// 多边形顶点集合 - /// 点 - /// 点与多边形的关系 - public static PointOnRegionType PointOnRegion(this IEnumerable pts, Point3d pt) - { - // 遍历点集并生成首尾连接的多边形 - var ptlst = new LoopList(pts); - if (ptlst.First!.Value == ptlst.Last!.Value) - ptlst.RemoveLast(); - if (ptlst.Count < 3) - return PointOnRegionType.Error; - - var ls3ds = new List(); - foreach (var node in ptlst.GetNodes()) - ls3ds.Add(new LineSegment3d(node.Value, node.Next!.Value)); - var cc3d = new CompositeCurve3d(ls3ds.ToArray()); - - // 在多边形上? - if (cc3d.IsOn(pt)) - return PointOnRegionType.On; - - // 在最小包围矩形外? - var bb2d = cc3d.BoundBlock; - if (!bb2d.Contains(pt)) - return PointOnRegionType.Outside; - - // - bool flag = false; - foreach (var node in ptlst.GetNodes()) - { - var pt1 = node.Value; - var pt2 = node.Next!.Value; - if (pt.Y < pt1.Y && pt.Y < pt2.Y) - continue; - if (pt1.X < pt.X && pt2.X < pt.X) - continue; - Vector3d vec = pt2 - pt1; - double t = (pt.X - pt1.X) / vec.X; - double y = t * vec.Y + pt1.Y; - if (y < pt.Y && t >= 0 && t <= 1) - flag = !flag; - } - return - flag ? - PointOnRegionType.Inside : PointOnRegionType.Outside; - } - - /// - /// 按两点返回最小包围圆 - /// - /// 基准点 - /// 基准点 - /// 输出圆上的点 - /// 解析类圆对象 - public static CircularArc2d GetMinCircle(Point2d pt1, Point2d pt2, out LoopList ptlst) - { - ptlst = new LoopList { pt1, pt2 }; - return - new CircularArc2d - ( - (pt1 + pt2.GetAsVector()) / 2, - pt1.GetDistanceTo(pt2) / 2 - ); - } - - /// - /// 按三点返回最小包围圆 - /// - /// 基准点 - /// 基准点 - /// 基准点 - /// 输出圆上的点 - /// 解析类圆对象 - public static CircularArc2d GetMinCircle(Point2d pt1, Point2d pt2, Point2d pt3, out LoopList ptlst) - { - ptlst = new LoopList { pt1, pt2, pt3 }; - - // 遍历各点与下一点的向量长度,找到距离最大的两个点 - LoopListNode maxNode = - ptlst.GetNodes().FindByMax - ( - out double maxLength, - node => node.Value.GetDistanceTo(node.Next!.Value) - ); - - // 以两点做最小包围圆 - CircularArc2d ca2d = - GetMinCircle(maxNode.Value, maxNode.Next!.Value, out LoopList tptlst); - - // 如果另一点属于该圆 - if (ca2d.IsIn(maxNode.Previous!.Value)) - { - // 返回 - ptlst = tptlst; - return ca2d; - } - // 否则按三点做圆 - // ptlst.SetFirst(maxNode); - ptlst = new LoopList { maxNode.Value, maxNode.Next.Value, maxNode.Previous.Value }; - ca2d = new CircularArc2d(pt1, pt2, pt3); - ca2d.SetAngles(0, Math.PI * 2); - return ca2d; - } - - /// - /// 按四点返回最小包围圆 - /// - /// 基准点 - /// 基准点 - /// 基准点 - /// 基准点 - /// 输出圆上的点 - /// 解析类圆对象 - public static CircularArc2d? GetMinCircle(Point2d pt1, Point2d pt2, Point2d pt3, Point2d pt4, out LoopList? ptlst) - { - var iniptlst = new LoopList() { pt1, pt2, pt3, pt4 }; - ptlst = null; - CircularArc2d? ca2d = null; - - // 遍历C43的组合,环链表的优势在这里 - foreach (var firstNode in iniptlst.GetNodes()) - { - // 获取各组合下三点的最小包围圆 - var secondNode = firstNode.Next; - var thirdNode = secondNode!.Next; - var tca2d = GetMinCircle(firstNode.Value, secondNode.Value, thirdNode!.Value, out LoopList tptlst); - - // 如果另一点属于该圆,并且半径小于当前值就把它做为候选解 - if (!tca2d.IsIn(firstNode.Previous!.Value)) - continue; - if (ca2d is null || tca2d.Radius < ca2d.Radius) - { - ca2d = tca2d; - ptlst = tptlst; - } - } - - // 返回直径最小的圆 - return ca2d; - } - - /// - /// 计算三点围成的有向面积 - /// - /// 基准点 - /// 第一点 - /// 第二点 - /// 三点围成的三角形的有向面积 - private static double CalArea(Point2d ptBase, Point2d pt1, Point2d pt2) - { - return (pt2 - ptBase).DotProduct((pt1 - ptBase).GetPerpendicularVector()) * 0.5; - } - /// - /// 计算三点围成的三角形的真实面积 - /// - /// 基准点 - /// 第一点 - /// 第二点 - /// 三点围成的三角形的真实面积 - public static double GetArea(this Point2d ptBase, Point2d pt1, Point2d pt2) - { - return Math.Abs(CalArea(ptBase, pt1, pt2)); - } - - /// - /// 判断三点是否为逆时针,也就是说判断三点是否为左转 - /// - /// 基点 - /// 第一点 - /// 第二点 - /// OrientationType 类型值 - public static OrientationType IsClockWise(this Point2d ptBase, Point2d pt1, Point2d pt2) - { - return CalArea(ptBase, pt1, pt2) switch - { - > 0 => OrientationType.CounterClockWise, - < 0 => OrientationType.ClockWise, - _ => OrientationType.Parallel - }; - } - - /// - /// 计算两个二维向量围成的平行四边形的有向面积 - /// - /// 基向量 - /// 向量 - /// 有向面积 - private static double CalArea(Vector2d vecBase, Vector2d vec) - { - return vec.DotProduct(vecBase.GetPerpendicularVector()) / 2; - } - - /// - /// 计算两个二维向量围成的平行四边形的真实面积 - /// - /// 基向量 - /// 向量 - /// 真实面积 - public static double GetArea(Vector2d vecBase, Vector2d vec) - { - return Math.Abs(CalArea(vecBase, vec)); - } - - /// - /// 判断两个二维向量是否左转 - /// - /// 基向量 - /// 向量 - /// OrientationType 类型值 - public static OrientationType IsClockWise(Vector2d vecBase, Vector2d vec) - { - return CalArea(vecBase, vec) switch - { - > 0 => OrientationType.CounterClockWise, - < 0 => OrientationType.ClockWise, - _ => OrientationType.Parallel - }; - } - - #region PointList - - /// - /// 计算点集的有向面积 - /// - /// 点集 - /// 有向面积 - private static double CalArea(IEnumerable pnts) - { - var itor = pnts.GetEnumerator(); - if (!itor.MoveNext()) - throw new ArgumentNullException(nameof(pnts)); - var start = itor.Current; - Point2d p1, p2 = start; - double area = 0; - - while (itor.MoveNext()) - { - p1 = p2; - p2 = itor.Current; - area += (p1.X * p2.Y - p2.X * p1.Y); - } - - area = (area + (p2.X * start.Y - start.X * p2.Y)) / 2.0; - return area; - } - /// - /// 计算点集的真实面积 - /// - /// 点集 - /// 面积 - public static double GetArea(this IEnumerable pnts) - { - return Math.Abs(CalArea(pnts)); - } - - /// - /// 判断点集的点序 - /// - /// 点集 - /// OrientationType 类型值 - public static OrientationType IsClockWise(this IEnumerable pnts) - { - return CalArea(pnts) switch - { - < 0 => OrientationType.ClockWise, - > 0 => OrientationType.CounterClockWise, - _ => OrientationType.Parallel - }; - } - - /// - /// 按点集返回最小包围圆 - /// - /// 点集 - /// 输出圆上的点 - /// 解析类圆对象 - public static CircularArc2d? GetMinCircle(this List pnts, out LoopList? ptlst) - { - // 点数较小时直接返回 - switch (pnts.Count) - { - case 0: - ptlst = null; - return null; - - case 1: - ptlst = new LoopList { pnts[0] }; - return new CircularArc2d(pnts[0], 0); - - case 2: - return GetMinCircle(pnts[0], pnts[1], out ptlst); - - case 3: - return GetMinCircle(pnts[0], pnts[1], pnts[2], out ptlst); - - case 4: - return GetMinCircle(pnts[0], pnts[1], pnts[2], pnts[3], out ptlst); - } - - // 按前三点计算最小包围圆 - var tpnts = new Point2d[4]; - pnts.CopyTo(0, tpnts, 0, 3); - var ca2d = GetMinCircle(tpnts[0], tpnts[1], tpnts[2], out ptlst); - - // 找到点集中距离圆心的最远点为第四点 - tpnts[3] = pnts.FindByMax(pnt => ca2d.Center.GetDistanceTo(pnt)); - - // 如果最远点属于圆结束 - while (!ca2d.IsIn(tpnts[3])) - { - // 如果最远点不属于圆,按此四点计算最小包围圆 - ca2d = GetMinCircle(tpnts[0], tpnts[1], tpnts[2], tpnts[3], out ptlst); - - // 将结果作为新的前三点 - if (ptlst!.Count == 3) - { - tpnts[2] = ptlst.Last!.Value; - } - else - { - // 第三点取另两点中距离圆心较远的点 - // 按算法中描述的任选其中一点的话,还是无法收敛...... - tpnts[2] = - tpnts.Except(ptlst) - .FindByMax(pnt => ca2d!.Center.GetDistanceTo(pnt)); - } - tpnts[0] = ptlst.First!.Value; - tpnts[1] = ptlst.First.Next!.Value; - - // 按此三点计算最小包围圆 - ca2d = GetMinCircle(tpnts[0], tpnts[1], tpnts[2], out ptlst); - - // 找到点集中圆心的最远点为第四点 - tpnts[3] = pnts.FindByMax(pnt => ca2d.Center.GetDistanceTo(pnt)); - } - - return ca2d; - } - - /// - /// 获取点集的凸包 - /// - /// 点集 - /// 凸包 - public static List? ConvexHull(this List points) - { - if (points is null) - return null; - - if (points.Count <= 1) - return points; - - int n = points.Count, k = 0; - List H = new(new Point2d[2 * n]); - - points.Sort((a, b) => - a.X == b.X ? a.Y.CompareTo(b.Y) : a.X.CompareTo(b.X)); - - // Build lower hull - for (int i = 0; i < n; ++i) - { - while (k >= 2 && IsClockWise(H[k - 2], H[k - 1], points[i]) == OrientationType.CounterClockWise) - k--; - H[k++] = points[i]; - } - - // Build upper hull - for (int i = n - 2, t = k + 1; i >= 0; i--) - { - while (k >= t && IsClockWise(H[k - 2], H[k - 1], points[i]) == OrientationType.CounterClockWise) - k--; - H[k++] = points[i]; - } - return H.Take(k - 1).ToList(); - } - - - #endregion PointList - - #endregion Point&Circle - - #region Ucs - - /// - /// ucs到wcs的点变换 - /// - /// 点 - /// 变换后的点 - public static Point3d Ucs2Wcs(this Point3d point) - { - return point.TransformBy(Env.Editor.CurrentUserCoordinateSystem); - } - - /// - /// wcs到ucs的点变换 - /// - /// 点 - /// 变换后的点 - public static Point3d Wcs2Ucs(this Point3d point) - { - return point.TransformBy(Env.Editor.CurrentUserCoordinateSystem.Inverse()); - } - - /// - /// ucs到wcs的向量变换 - /// - /// 向量 - /// 变换后的向量 - public static Vector3d Ucs2Wcs(this Vector3d vec) - { - return vec.TransformBy(Env.Editor.CurrentUserCoordinateSystem); - } - - /// - /// wcs到ucs的向量变换 - /// - /// 向量 - /// 变换后的向量 - public static Vector3d Wcs2Ucs(this Vector3d vec) - { - return vec.TransformBy(Env.Editor.CurrentUserCoordinateSystem.Inverse()); - } - - /// - /// 模拟 trans 函数 - /// - /// 点 - /// 源坐标系 - /// 目标坐标系 - /// 变换后的点 - public static Point3d Trans(this Point3d point, CoordinateSystemCode from, CoordinateSystemCode to) - { - return Env.Editor.GetMatrix(from, to) * point; - } - - /// - /// 模拟 trans 函数 - /// - /// 向量 - /// 源坐标系 - /// 目标坐标系 - /// 变换后的向量 - public static Vector3d Trans(this Vector3d vec, CoordinateSystemCode from, CoordinateSystemCode to) - { - return vec.TransformBy(Env.Editor.GetMatrix(from, to)); - } - - /// - /// wcs到dcs的点变换 - /// - /// 点 - /// 是否为图纸空间 - /// 变换后的点 - public static Point3d Wcs2Dcs(this Point3d point, bool atPaperSpace) - { - return - Trans( - point, - CoordinateSystemCode.Wcs, atPaperSpace ? CoordinateSystemCode.PDcs : CoordinateSystemCode.MDcs - ); - } - - /// - /// wcs到dcs的向量变换 - /// - /// 向量 - /// 是否为图纸空间 - /// 变换后的向量 - public static Vector3d Wcs2Dcs(this Vector3d vec, bool atPaperSpace) - { - return - Trans( - vec, - CoordinateSystemCode.Wcs, atPaperSpace ? CoordinateSystemCode.PDcs : CoordinateSystemCode.MDcs - ); - } - - #endregion Ucs - - - /// - /// 返回不等比例变换矩阵 - /// - /// 基点 - /// x方向比例 - /// y方向比例 - /// z方向比例 - /// 三维矩阵 - public static Matrix3d GetScaleMatrix(this Point3d point, double x, double y, double z) - { - double[] matdata = new double[16]; - matdata[0] = x; - matdata[3] = point.X * (1 - x); - matdata[5] = y; - matdata[7] = point.Y * (1 - y); - matdata[10] = z; - matdata[11] = point.Z * (1 - z); - matdata[15] = 1; - return new(matdata); - } - - /// - /// 获取坐标范围的大小 - /// - /// 坐标范围 - /// 尺寸对象 - public static Size GetSize(this Extents3d ext) - { - int width = (int)Math.Floor(ext.MaxPoint.X - ext.MinPoint.X); - int height = (int)Math.Ceiling(ext.MaxPoint.Y - ext.MinPoint.Y); - return new(width, height); - } - - /// - /// 将三维点转换为二维点 - /// - /// 三维点 - /// 二维点 - public static Point2d Point2d(this Point3d pt) - { - return new(pt.X, pt.Y); - } - /// - /// 将三维点集转换为二维点集 - /// - /// 三维点集 - /// 二维点集 - public static IEnumerable Point2d(this IEnumerable pts) - { - return pts.Select(pt => pt.Point2d()); - } - /// - /// 将二维点转换为三维点 - /// - /// 二维点 - /// Z值 - /// 三维点 - public static Point3d Point3d(this Point2d pt, double z = 0) - { - return new(pt.X, pt.Y, z); - } - - /// - /// 获取两个点之间的中点 - /// - /// 第一点 - /// 第二点 - /// 返回两个点之间的中点 - public static Point3d GetMidPointTo(this Point3d pt1, Point3d pt2) - { - return new(pt1.X * 0.5 + pt2.X * 0.5, - pt1.Y * 0.5 + pt2.Y * 0.5, - pt1.Z * 0.5 + pt2.Z * 0.5); - } - - /// - /// 获取两个点之间的中点 - /// - /// 第一点 - /// 第二点 - /// 返回两个点之间的中点 - public static Point2d GetMidPointTo(this Point2d pt1, Point2d pt2) - { - // (pt1 + pt2) / 2; // 溢出风险 - return new(pt1.X * 0.5 + pt2.X * 0.5, - pt1.Y * 0.5 + pt2.Y * 0.5); - } - - /// - /// 根据世界坐标计算用户坐标 - /// - /// 基点世界坐标 - /// 基点用户坐标 - /// 目标世界坐标 - /// 坐标网旋转角,按x轴正向逆时针弧度 - /// 目标用户坐标 - public static Point3d TransPoint(this Point3d basePt, Point3d userPt, Point3d transPt, double ang) - { - Matrix3d transMat = Matrix3d.Displacement(userPt - basePt); - Matrix3d roMat = Matrix3d.Rotation(-ang, Vector3d.ZAxis, userPt); - return transPt.TransformBy(roMat * transMat); - } - /// - /// 计算指定距离和角度的点 - /// - /// 本函数仅适用于x-y平面 - /// 基点 - /// 角度,x轴正向逆时针弧度 - /// 距离 - /// 目标点 - public static Point3d Polar(this Point3d pt, double ang, double len) - { - return pt + Vector3d.XAxis.RotateBy(ang, Vector3d.ZAxis) * len; - } - /// - /// 计算指定距离和角度的点 - /// - /// 本函数仅适用于x-y平面 - /// 基点 - /// 角度,x轴正向逆时针弧度 - /// 距离 - /// 目标点 - public static Point2d Polar(this Point2d pt, double ang, double len) - { - return pt + Vector2d.XAxis.RotateBy(ang) * len; - } -} \ No newline at end of file diff --git a/src/IFoxCAD.Cad.Shared/ExtensionMethod/JigEx.cs b/src/IFoxCAD.Cad.Shared/ExtensionMethod/JigEx.cs deleted file mode 100644 index 5b6d69c..0000000 --- a/src/IFoxCAD.Cad.Shared/ExtensionMethod/JigEx.cs +++ /dev/null @@ -1,407 +0,0 @@ -namespace IFoxCAD.Cad; - -/* 封装jig - * 20220726 隐藏事件,利用函数进行数据库图元重绘 - * 20220710 修改SetOption()的空格结束,并添加例子到IFox - * 20220503 cad22需要防止刷新过程中更改队列,是因为允许函数重入导致,08不会有. - * 20220326 重绘图元的函数用错了,现在修正过来 - * 20211216 加入块表时候做一个差集,剔除临时图元 - * 20211209 补充正交变量设置和回收设置 - * 作者: 惊惊⎛⎝◕⏝⏝◕。⎠⎞ ⎛⎝≥⏝⏝0⎠⎞ ⎛⎝⓿⏝⏝⓿。⎠⎞ ⎛⎝≥⏝⏝≤⎠⎞ - * 博客: https://www.cnblogs.com/JJBox/p/15650770.html - */ - -public delegate void WorldDrawEvent(WorldDraw draw); -public class JigEx : DrawJig, IDisposable -{ - #region 成员 - /// - /// 事件:亮显/暗显会被刷新冲刷掉,所以这个事件用于补充非刷新的工作 - /// - event WorldDrawEvent? WorldDrawEvent; - /// - /// 最后的鼠标点,用来确认长度 - /// - public Point3d MousePointWcsLast; - /// - /// 最后的图元,用来生成 - /// - public Entity[] Entitys => _drawEntitys.ToArray(); - - - readonly Action>? _mouseAction; - readonly Tolerance _tolerance;// 容差 - - readonly Queue _drawEntitys;// 重复生成的图元,放在这里刷新 - JigPromptPointOptions? _options;// jig鼠标配置 - bool _worldDrawFlag = false; // 20220503 - - bool _systemVariables_Orthomode = false; - bool SystemVariables_Orthomode // 正交修改还原 - { - get => _systemVariables_Orthomode; - set - { - // 1正交,0非正交 // setvar: https://www.cnblogs.com/JJBox/p/10209541.html - if (Env.OrthoMode != value) - Env.OrthoMode = _systemVariables_Orthomode = value; - } - } - #endregion - - #region 构造 - /// - /// 在界面绘制图元 - /// - JigEx() - { - _drawEntitys = new(); - DimensionEntitys = new(); - } - - /// - /// 在界面绘制图元 - /// - /// - /// 用来频繁执行的回调:
    - /// 鼠标点;
    - /// 加入新建的图元,鼠标采样期间会Dispose图元的;
    - /// 所以已经在数据库图元利用事件加入,不要在此加入;
    - /// - /// 鼠标移动的容差 - public JigEx(Action>? action = null, double tolerance = 1e-6) : this() - { - _mouseAction = action; - _tolerance = new(tolerance, tolerance); - } - #endregion - - #region 重写 - /// - /// 鼠标采样器 - /// - /// - /// 返回状态:令频繁刷新结束 - protected override SamplerStatus Sampler(JigPrompts prompts) - { - if (_worldDrawFlag) - return SamplerStatus.NoChange;// OK的时候拖动鼠标与否都不出现图元 - - if (_options is null) - throw new NullReferenceException(nameof(_options)); - - var pro = prompts.AcquirePoint(_options); - if (pro.Status == PromptStatus.Keyword) - return SamplerStatus.OK; - else if (pro.Status != PromptStatus.OK) - return SamplerStatus.Cancel; - - // 上次鼠标点不同(一定要这句,不然图元刷新太快会看到奇怪的边线) - var mousePointWcs = pro.Value; - - // == 是比较类字段,但是最好转为哈希比较. - // IsEqualTo 是方形判断(仅加法),但是cad是距离. - // Distance 是圆形判断(会求平方根,使用了牛顿迭代), - // 大量数据(十万以上/频繁刷新)面前会显得非常慢. - if (mousePointWcs.IsEqualTo(MousePointWcsLast, _tolerance)) - return SamplerStatus.NoChange; - - // 上次循环的缓冲区图元清理,否则将会在vs输出遗忘 Dispose - while (_drawEntitys.Count > 0) - _drawEntitys.Dequeue().Dispose(); - - // 委托把容器扔出去接收新创建的图元,然后给重绘更新 - _mouseAction?.Invoke(mousePointWcs, _drawEntitys); - MousePointWcsLast = mousePointWcs; - - return SamplerStatus.OK; - } - - /// - /// 重绘已在数据库的图元 - /// - /// 0x01 此处不加入newEntity的,它们在构造函数的参数回调处加入,它们会进行频繁new和Dispose从而避免遗忘释放
    - /// 0x02 此处用于重绘已经在数据的图元
    - /// 0x03 此处用于图元亮显暗显,因为会被重绘冲刷掉所以独立出来不重绘,它们也往往已经存在数据库的 - ///
    - ///
    - /// - /// newEntity只会存在一个图元队列中,而数据库图元可以分多个集合 - /// 例如: 集合A亮显时 集合B暗显/集合B亮显时 集合A暗显,所以我没有设计多个"数据库图元集合"存放,而是由用户在构造函数外自行创建 - /// - /// - public void DatabaseEntityDraw(WorldDrawEvent action) - { - WorldDrawEvent = action; - } - - /* WorldDraw 封装外的操作说明: - * 0x01 - * 我有一个业务是一次性生成四个方向的箭头,因为cad08缺少瞬时图元, - * 那么可以先提交一次事务,再开一个事务,把Entity传给jig,最后选择删除部分. - * 虽然这个是可行的方案,但是Entity穿越事务本身来说是非必要不使用的. - * 0x02 - * 四个箭头最近鼠标的亮显,其余淡显, - * 在jig使用淡显ent.Unhighlight和亮显ent.Highlight() - * 需要绕过重绘,否则重绘将导致图元频闪,令这两个操作失效, - * 此时需要自定义一个集合 EntityList (不使用本函数的_drawEntitys) - * 再将 EntityList 传给 WorldDrawEvent 事件,事件内实现亮显和淡显(事件已经利用 DatabaseEntityDraw函数进行提供). - * 0x03 - * draw.Geometry.Draw(_drawEntitys[i]); - * 此函数有问题,acad08克隆一份数组也可以用来刷新, - * 而arx上面的jig只能一次改一个,所以可以用此函数. - * 起因是此函数属于异步刷新, - * 同步上下文的刷新是 RawGeometry - * 0x04 - * cad22测试出现,08不会, - * draw.RawGeometry.Draw(ent);会跳到 Sampler(),所以设置 _worldDrawFlag - * 但是禁止重绘重入的话(令图元不频繁重绘),那么鼠标停着的时候就看不见图元, - * 所以只能重绘结束的时候才允许鼠标采集,采集过程的时候不会触发重绘, - * 这样才可以保证容器在重绘中不被更改. - */ - /// - /// 重绘图形 - /// - protected override bool WorldDraw(WorldDraw draw) - { - _worldDrawFlag = true; - WorldDrawEvent?.Invoke(draw); - _drawEntitys.ForEach(ent => { - draw.RawGeometry.Draw(ent); - }); - _worldDrawFlag = false; - return true; - } - #endregion - - #region 方法 - /// - /// 鼠标配置:基点 - /// - /// 基点 - /// 光标绑定 - /// 提示信息 - /// 正交开关 - public JigPromptPointOptions SetOptions(Point3d basePoint, - CursorType cursorType = CursorType.RubberBand, - string msg = "点选第二点", - bool orthomode = false) - { - if (orthomode) - SystemVariables_Orthomode = true; - - _options = JigPointOptions(); - _options.Message = Environment.NewLine + msg; - _options.Cursor = cursorType; // 光标绑定 - _options.UseBasePoint = true; // 基点打开 - _options.BasePoint = basePoint; // 基点设定 - return _options; - } - - /// - /// 鼠标配置:提示信息,关键字 - /// - /// 信息 - /// 关键字 - /// 正交开关 - /// - public JigPromptPointOptions SetOptions(string msg, - Dictionary? keywords = null, - bool orthomode = false) - { - if (orthomode) - SystemVariables_Orthomode = true; - - _options = JigPointOptions(); - _options.Message = Environment.NewLine + msg; - - // 加入关键字,加入时候将空格内容放到最后 - string spaceValue = string.Empty; - const string spaceKey = " "; - - if (keywords != null) - foreach (var item in keywords) - if (item.Key == spaceKey) - spaceValue = item.Value; - else - _options.Keywords.Add(item.Key, item.Key, item.Value); - - /// 因为默认配置函数导致此处空格触发是无效的, - /// 但是用户如果想触发,就需要在外部减去默认UserInputControls配置 - /// 要放最后,才能优先触发其他关键字 - if (spaceValue != string.Empty) - _options.Keywords.Add(spaceKey, spaceKey, spaceValue); - else - _options.Keywords.Add(spaceKey, spaceKey, "<空格退出>"); - - // 外部设置减去配置 - // _options.UserInputControls = - // _options.UserInputControls - // ^ UserInputControls.NullResponseAccepted // 输入了鼠标右键,结束jig - // ^ UserInputControls.AnyBlankTerminatesInput; // 空格或回车,结束jig; - return _options; - } - - /// - /// 鼠标配置:自定义 - /// - /// - /// 正交开关 - public void SetOptions(Action action, bool orthomode = false) - { - if (orthomode) - SystemVariables_Orthomode = true; - - _options = new JigPromptPointOptions(); - action.Invoke(_options); - } - - /// - /// 执行 - /// - /// - public PromptResult Drag() - { - // jig功能必然是当前前台文档,所以封装内部更好调用 - var dm = Acap.DocumentManager; - var doc = dm.MdiActiveDocument; - var ed = doc.Editor; - var dr = ed.Drag(this); - - if (SystemVariables_Orthomode) - SystemVariables_Orthomode = !SystemVariables_Orthomode; - return dr; - } - - /// - /// 最后一次的图元加入数据库 - /// - /// 加入此空间 - /// 不生成的图元用于排除,例如刷新时候的提示文字 - /// 加入数据库的id集合 - public IEnumerable? AddEntityToMsPs(BlockTableRecord btrOfAddEntitySpace, - IEnumerable? removeEntity = null) - { - // 内部用 _drawEntitys 外部用 Entitys,减少一层转换 - if (_drawEntitys.Count == 0) - return null; - - IEnumerable es = _drawEntitys; - if (removeEntity != null) - es = es.Except(removeEntity);// 差集 - - return btrOfAddEntitySpace.AddEntity(es); - } - - - #region 配置 - /// - /// 用户输入控制默认配置 - /// 令jig.Drag().Status == - /// - /// - static JigPromptPointOptions JigPointOptions() - { - return new JigPromptPointOptions() - { - UserInputControls = - UserInputControls.GovernedByUCSDetect // 由UCS探测用 - | UserInputControls.Accept3dCoordinates // 接受三维坐标 - | UserInputControls.NullResponseAccepted // 输入了鼠标右键,结束jig - | UserInputControls.AnyBlankTerminatesInput // 空格或回车,结束jig; - }; - } - - /// - /// 空格默认是, - /// 将它设置为 - /// - public void SetSpaceIsKeyword() - { - var opt = _options; - if (opt == null) - throw new ArgumentNullException(nameof(_options)); - - if ((opt.UserInputControls & UserInputControls.NullResponseAccepted) == UserInputControls.NullResponseAccepted) - opt.UserInputControls ^= UserInputControls.NullResponseAccepted; // 输入了鼠标右键,结束jig - if ((opt.UserInputControls & UserInputControls.AnyBlankTerminatesInput) == UserInputControls.AnyBlankTerminatesInput) - opt.UserInputControls ^= UserInputControls.AnyBlankTerminatesInput; // 空格或回车,结束jig - } - #endregion - - #region 注释数据 - /// - /// 注释数据,可以在缩放的时候不受影响 - /// - public DynamicDimensionDataCollection DimensionEntitys; - - /// - /// 重写注释数据 - /// - /// - /// - protected override DynamicDimensionDataCollection GetDynamicDimensionData(double dimScale) - { - base.GetDynamicDimensionData(dimScale); - return DimensionEntitys; - } - - #endregion - #endregion - - #region IDisposable接口相关函数 - public bool IsDisposed { get; private set; } = false; - - /// - /// 手动调用释放 - /// - public void Dispose() - { - Dispose(true); - GC.SuppressFinalize(this); - } - - /// - /// 析构函数调用释放 - /// - ~JigEx() - { - Dispose(false); - } - - protected virtual void Dispose(bool disposing) - { - // 不重复释放,并设置已经释放 - if (IsDisposed) return; - IsDisposed = true; - - // 最后一次的图元如果没有加入数据库,就在此销毁,所以JigEx调用的时候加using - _drawEntitys?.ForEach(ent => { - if (ent.Database == null && !ent.IsDisposed) - ent.Dispose(); - }); - _drawEntitys?.Clear(); - } - #endregion - - // 此处无测试 - // protected override void ViewportDraw(ViewportDraw draw) - // { - // base.ViewportDraw(draw); - // } -} - -#if false -| UserInputControls.DoNotEchoCancelForCtrlC // 不要取消CtrlC的回音 -| UserInputControls.DoNotUpdateLastPoint // 不要更新最后一点 -| UserInputControls.NoDwgLimitsChecking // 没有Dwg限制检查 -| UserInputControls.NoZeroResponseAccepted // 接受非零响应 -| UserInputControls.NoNegativeResponseAccepted // 不否定回复已被接受 -| UserInputControls.Accept3dCoordinates // 返回点的三维坐标,是转换坐标系了? -| UserInputControls.AcceptMouseUpAsPoint // 接受释放按键时的点而不是按下时 - -| UserInputControls.InitialBlankTerminatesInput // 初始 空格或回车,结束jig -| UserInputControls.AcceptOtherInputString // 接受其他输入字符串 -| UserInputControls.NoZDirectionOrtho // 无方向正射,直接输入数字时以基点到当前点作为方向 -| UserInputControls.UseBasePointElevation // 使用基点高程,基点的Z高度探测 -#endif \ No newline at end of file diff --git a/src/IFoxCAD.Cad.Shared/ExtensionMethod/JigExTransient.cs b/src/IFoxCAD.Cad.Shared/ExtensionMethod/JigExTransient.cs deleted file mode 100644 index c7532b9..0000000 --- a/src/IFoxCAD.Cad.Shared/ExtensionMethod/JigExTransient.cs +++ /dev/null @@ -1,149 +0,0 @@ -#if acad && !ac2008 -namespace IFoxCAD.Cad; - -/// -/// 瞬态容器 -/// -public class JigExTransient : IDisposable -{ - #region 私有字段 - // 整数集,暂时不知道有什么意义 - IntegerCollection _integerCollection; - // 维护集合 - HashSet _entities; - #endregion - - #region 公开属性 - /// - /// 对象集合 - /// - public Entity[] Entities => _entities.ToArray(); - /// - /// 数量 - /// - public int Count => _entities.Count; - #endregion - - #region 构造函数 - /// - /// 瞬态容器 - /// - public JigExTransient() - { - _integerCollection = new(); - _entities = new(); - } - #endregion - - #region 方法 - /// - /// 判断瞬态容器里是否含有对象 - /// - /// 对象 - /// 含有返回true - public bool Contains(Entity ent) - { - return _entities.Contains(ent); - } - - /// - /// 向瞬态容器中添加对象 - /// - /// 图元 - /// 绘图模式 - public void Add(Entity ent, TransientDrawingMode tdm = TransientDrawingMode.Main) - { - if (_entities.Add(ent)) - { - TransientManager - .CurrentTransientManager - .AddTransient(ent, tdm, 128, _integerCollection); - } - } - - - /// - /// 从瞬态容器中移除图元 - /// - /// 已经加入瞬态容器的图元 - public void Remove(Entity ent) - { - if (!Contains(ent)) - return; - TransientManager - .CurrentTransientManager - .EraseTransient(ent, _integerCollection); - _entities.Remove(ent); - } - - /// - /// 清空瞬态容器并移除图元显示 - /// - public void Clear() - { - foreach (var ent in _entities) - { - TransientManager - .CurrentTransientManager - .EraseTransient(ent, _integerCollection); - } - _entities.Clear(); - } - - - /// - /// 更新单个显示 - /// - /// 已经加入瞬态容器的图元 - public void Update(Entity ent) - { - if (!Contains(ent)) - return; - TransientManager - .CurrentTransientManager - .UpdateTransient(ent, _integerCollection); - } - - /// - /// 更新全部显示 - /// - public void UpdateAll() - { - foreach (var ent in _entities) - Update(ent); - } - #endregion - - #region IDisposable接口相关函数 - public bool IsDisposed { get; private set; } = false; - - /// - /// 手动释放 - /// - public void Dispose() - { - Dispose(true); - GC.SuppressFinalize(this); - } - - /// - /// 析构函数调用释放 - /// - ~JigExTransient() - { - Dispose(false); - } - - /// - /// 销毁瞬态容器 - /// - protected virtual void Dispose(bool disposing) - { - if (IsDisposed) return; - IsDisposed = true; - - Clear();// 清空瞬态容器并移除对象在图纸上的显示 - } - #endregion -} -#endif \ No newline at end of file diff --git a/src/IFoxCAD.Cad.Shared/ExtensionMethod/JsonConverter.cs b/src/IFoxCAD.Cad.Shared/ExtensionMethod/JsonConverter.cs deleted file mode 100644 index c1044a9..0000000 --- a/src/IFoxCAD.Cad.Shared/ExtensionMethod/JsonConverter.cs +++ /dev/null @@ -1,116 +0,0 @@ -namespace IFoxCAD.Cad; - -#if NewtonsoftJson -/* - * 参考 https://www.cnblogs.com/fps2tao/p/14798710.html - * json类型转换器,使用方法: - * 在类上面增加此特性: [JsonConverter(typeof(ObjectIdConverter))] - */ -/// -/// json转换器 -/// -public class ObjectIdConverter : JsonConverter -{ - /// - /// 约束类型 - /// - public override bool CanConvert(Type objectType) - { - return typeof(ObjectId) == objectType; - } - - /// - /// 反序列化_把字符串生成对象 - /// - public override object ReadJson(JsonReader reader, Type objectType, object? existingValue, JsonSerializer serializer) - { - if (reader.Value == null) - return ObjectId.Null; - try - { - using DBTrans tr = new(); - var id = tr.GetObjectId(reader.Value.ToString()); - return id; - } - catch { return ObjectId.Null; } - } - - /// - /// 序列化_写入json - /// - public override void WriteJson(JsonWriter writer, object? value, JsonSerializer serializer) - { - if (value is ObjectId id) - writer.WriteValue(id == ObjectId.Null ? 0 : id.Handle.Value); - } -} -#else -/* - * 参考 https://developer.aliyun.com/article/51053 - * json类型转换器,使用方法: - * public static string SerializeToJson(object obj) - * { - * JavaScriptSerializer serializer = new(); - * serializer.RegisterConverters(new[] { new ObjectIdConverter() }); - * return serializer.Serialize(obj); - * } - * - * public static T DeserializeJson(string jsonString) - * { - * JavaScriptSerializer serializer = new(); - * serializer.RegisterConverters(new[] { new ObjectIdConverter() }); - * return serializer.Deserialize(jsonString); - * } - */ -/// -/// json转换器 -/// -public class ObjectIdConverter : JavaScriptConverter -{ - const string _id = nameof(ObjectId); - - /// - /// 约束类型 - /// - public override IEnumerable SupportedTypes => new[] { typeof(ObjectId) }; - - /// - /// 序列化_写入json - /// - public override IDictionary Serialize(object obj, JavaScriptSerializer serializer) - { - if (obj is not ObjectId id) - return null!; - - Dictionary result = new() - { - { _id, id == ObjectId.Null ? 0 : id.Handle.Value } - }; - return result; - } - - /// - /// 反序列化_把字符串生成对象 - /// - public override object Deserialize(IDictionary dictionary, Type type, JavaScriptSerializer serializer) - { - if (dictionary == null) - throw new ArgumentNullException(nameof(dictionary)); - - if (type != typeof(ObjectId)) - return null!; - - ObjectId id = ObjectId.Null; - try - { - if (dictionary.TryGetValue(_id, out object va)) - { - using DBTrans tr = new(); - id = tr.GetObjectId(va.ToString()); - } - } - catch { } - return id; - } -} -#endif \ No newline at end of file diff --git a/src/IFoxCAD.Cad.Shared/ExtensionMethod/ObjEx.cs b/src/IFoxCAD.Cad.Shared/ExtensionMethod/ObjEx.cs deleted file mode 100644 index 2ccc389..0000000 --- a/src/IFoxCAD.Cad.Shared/ExtensionMethod/ObjEx.cs +++ /dev/null @@ -1,21 +0,0 @@ -namespace IFoxCAD.Cad; - -public static class ObjEx -{ - /// - /// cad的打印 - /// - /// - public static void Print(this object obj) - { - Acap.DocumentManager.MdiActiveDocument.Editor.WriteMessage($"{obj}\n"); - } - /// - /// 系统的打印 - /// - /// - public static void PrintLine(this object obj) - { - Console.WriteLine(obj.ToString()); - } -} \ No newline at end of file diff --git a/src/IFoxCAD.Cad.Shared/ExtensionMethod/ObjectIdEx.cs b/src/IFoxCAD.Cad.Shared/ExtensionMethod/ObjectIdEx.cs deleted file mode 100644 index a329da5..0000000 --- a/src/IFoxCAD.Cad.Shared/ExtensionMethod/ObjectIdEx.cs +++ /dev/null @@ -1,98 +0,0 @@ -namespace IFoxCAD.Cad; - -/// -/// 对象id扩展类 -/// -public static class ObjectIdEx -{ - #region GetObject - /// - /// 获取指定类型对象 - /// - /// 指定的泛型 - /// 对象id - /// 打开模式 - /// 事务 - /// 是否打开已删除对象,默认为不打开 - /// 是否打开锁定图层对象,默认为不打开 - /// 指定类型对象 - public static T? GetObject(this ObjectId id, - OpenMode openMode = OpenMode.ForRead, - Transaction? trans = null, - bool openErased = false, - bool openLockedLayer = false) where T : DBObject - { - trans ??= DBTrans.Top.Transaction; - return trans.GetObject(id, openMode, openErased, openLockedLayer) as T; - } - - /// - /// 获取指定类型对象集合 - /// - /// 指定的泛型 - /// 对象id集合 - /// 打开模式 - /// 事务 - /// 是否打开已删除对象,默认为不打开 - /// 是否打开锁定图层对象,默认为不打开 - /// 指定类型对象集合 - [System.Diagnostics.DebuggerStepThrough] - public static IEnumerable GetObject(this IEnumerable ids, - OpenMode openMode = OpenMode.ForRead, - Transaction? trans = null, - bool openErased = false, - bool openLockedLayer = false) where T : DBObject - { - trans ??= DBTrans.Top.Transaction; - return ids.Select(id => id.GetObject(openMode, trans, openErased, openLockedLayer)); - } - - /// - /// 返回符合类型的对象id - /// - /// 对象类型 - /// 对象id集合 - /// 对象id集合 - public static IEnumerable OfType(this IEnumerable ids) where T : DBObject - { - string dxfName = RXClass.GetClass(typeof(T)).DxfName; - return ids.Where(id => id.ObjectClass().DxfName == dxfName); - } - #endregion GetObject - - public static RXClass ObjectClass(this ObjectId id) - { -#if NET35 - return RXClass.GetClass(id.GetType()); -#else - return id.ObjectClass; -#endif - } - - /// - /// id是否有效,未被删除 - /// - /// 对象id - /// id有效返回 ,反之返回 - public static bool IsOk(this ObjectId id) - { - return !id.IsNull && id.IsValid && !id.IsErased && !id.IsEffectivelyErased && id.IsResident; - } - - /// - /// 删除id代表的对象 - /// - /// 对象id - public static void Erase(this ObjectId id) - { - if (id.IsOk()) - { - var ent = id.GetObject()!; - using (ent.ForWrite()) - { - ent.Erase(); - }// 第一种读写权限自动转换写法 - // Env.Editor.Regen(); - } - } -} \ No newline at end of file diff --git a/src/IFoxCAD.Cad.Shared/ExtensionMethod/PointEx.cs b/src/IFoxCAD.Cad.Shared/ExtensionMethod/PointEx.cs deleted file mode 100644 index 99187e8..0000000 --- a/src/IFoxCAD.Cad.Shared/ExtensionMethod/PointEx.cs +++ /dev/null @@ -1,138 +0,0 @@ -namespace IFoxCAD.Cad; - -public static class PointEx -{ - /// - /// 获取点的hash字符串,同时可以作为pt的字符串表示 - /// - /// 点 - /// 指示计算几维坐标的标志,1为计算x,2为计算x,y,其他为计算x,y,z - /// 保留的小数位数 - /// hash字符串 - public static string GetHashString(this Point3d pt, int xyz = 3, int decimalRetain = 6) - { - var de = $"f{decimalRetain}"; - return xyz switch - { - 1 => $"({pt.X.ToString(de)})", - 2 => $"({pt.X.ToString(de)},{pt.Y.ToString(de)})", - _ => $"({pt.X.ToString(de)},{pt.Y.ToString(de)},{pt.Z.ToString(de)})" - }; - } - - // 为了频繁触发所以弄个缓存 - static Plane? _PlaneCache; - /// - /// 两点计算弧度范围0到2Pi - /// - /// 起点 - /// 终点 - /// 方向 - /// 弧度值 - public static double GetAngle(this Point3d startPoint, Point3d endPoint, Vector3d? direction = null) - { - if (direction != null) - _PlaneCache = new Plane(Point3d.Origin, direction.Value); - if (_PlaneCache == null) - _PlaneCache = new Plane(Point3d.Origin, Vector3d.ZAxis); - return startPoint.GetVectorTo(endPoint).AngleOnPlane(_PlaneCache); - } - /// - /// 两点计算弧度范围0到2Pi - /// - /// 起点 - /// 终点 - /// 弧度值 - public static double GetAngle(this Point2d startPoint, Point2d endPoint) - { - return startPoint.GetVectorTo(endPoint).Angle; - } - - /// - /// 获取中点 - /// - /// - /// - /// - public static Point2d GetCenter(this Point2d a, Point2d b) - { - // (p1 + p2) / 2; // 溢出风险 - return new Point2d(a.X * 0.5 + b.X * 0.5, - a.Y * 0.5 + b.Y * 0.5); - } - - /// http://www.lee-mac.com/bulgeconversion.html - /// - /// 求凸度,判断三点是否一条直线上 - /// - /// 圆弧起点 - /// 圆弧腰点 - /// 圆弧尾点 - /// 逆时针为正,顺时针为负 - public static double GetArcBulge(this Point2d arc1, Point2d arc2, Point2d arc3, double tol = 1e-10) - { - double dStartAngle = arc2.GetAngle(arc1); - double dEndAngle = arc2.GetAngle(arc3); - // 求的P1P2与P1P3夹角 - var talAngle = (Math.PI - dStartAngle + dEndAngle) / 2; - // 凸度==拱高/半弦长==拱高比值/半弦长比值 - // 有了比值就不需要拿到拱高值和半弦长值了,因为接下来是相除得凸度 - double bulge = Math.Sin(talAngle) / Math.Cos(talAngle); - - // 处理精度 - if (bulge > 0.9999 && bulge < 1.0001) - bulge = 1; - else if (bulge < -0.9999 && bulge > -1.0001) - bulge = -1; - else if (Math.Abs(bulge) < tol) - bulge = 0; - return bulge; - } - - - #region 首尾相连 - /// - /// 首尾相连 - /// - [System.Diagnostics.DebuggerStepThrough] - public static void End2End(this Point2dCollection ptcol) - { - if (ptcol == null) - throw new ArgumentNullException(nameof(ptcol)); - - if (ptcol.Count == 0 || ptcol[0].Equals(ptcol[^1]))// 首尾相同直接返回 - return; - - // 首尾不同,去加一个到最后 - var lst = new Point2d[ptcol.Count + 1]; - for (int i = 0; i < lst.Length; i++) - lst[i] = ptcol[i]; - lst[^1] = lst[0]; - - ptcol.Clear(); - ptcol.AddRange(lst); - } - /// - /// 首尾相连 - /// - [System.Diagnostics.DebuggerStepThrough] - public static void End2End(this Point3dCollection ptcol) - { - if (ptcol == null) - throw new ArgumentNullException(nameof(ptcol)); - - if (ptcol.Count == 0 || ptcol[0].Equals(ptcol[^1]))// 首尾相同直接返回 - return; - - // 首尾不同,去加一个到最后 - var lst = new Point3d[ptcol.Count + 1]; - for (int i = 0; i < lst.Length; i++) - lst[i] = ptcol[i]; - lst[^1] = lst[0]; - - ptcol.Clear(); - for (int i = 0; i < lst.Length; i++) - ptcol.Add(lst[i]); - } - #endregion -} \ No newline at end of file diff --git a/src/IFoxCAD.Cad.Shared/ExtensionMethod/SelectionSetEx.cs b/src/IFoxCAD.Cad.Shared/ExtensionMethod/SelectionSetEx.cs deleted file mode 100644 index 432ed01..0000000 --- a/src/IFoxCAD.Cad.Shared/ExtensionMethod/SelectionSetEx.cs +++ /dev/null @@ -1,150 +0,0 @@ -namespace IFoxCAD.Cad; - -/// -/// 选择集扩展类 -/// -public static class SelectionSetEx -{ - #region 获取对象id - /// - /// 获取已选择的对象 - /// - /// 选择集 - /// 已选择的对象集合 - [System.Diagnostics.DebuggerStepThrough] - public static IEnumerable GetSelectedObjects(this SelectionSet ss) - { - return ss.Cast(); - } - - /// - /// 获取已选择的对象 - /// - /// 已选择的对象泛型 - /// 选择集 - /// 已选择的对象集合 - [System.Diagnostics.DebuggerStepThrough] - public static IEnumerable GetSelectObjects(this SelectionSet ss) where T : SelectedObject - { - return ss.Cast().OfType(); - } - - /// - /// 从选择集中获取对象id - /// - /// 图元类型 - /// 选择集 - /// 已选择的对象id集合 - [System.Diagnostics.DebuggerStepThrough] - public static IEnumerable GetObjectIds(this SelectionSet ss) where T : Entity - { - string dxfName = RXClass.GetClass(typeof(T)).DxfName; - return - ss - .GetObjectIds() - .Where(id => id.ObjectClass().DxfName == dxfName); - } - - /// - /// 将选择集的对象按类型分组 - /// - /// 选择集 - /// 分组后的类型/对象id集合 - [System.Diagnostics.DebuggerStepThrough] - public static IEnumerable> GetObjectIdGroup(this SelectionSet ss) - { - return - ss - .GetObjectIds() - .GroupBy(id => id.ObjectClass().DxfName); - } - #endregion - - #region 获取实体对象 - - /// - /// 获取指定类型图元 - /// - /// 指定类型 - /// 选择集 - /// 打开模式 - /// 事务 - /// 是否打开已删除对象,默认为不打开 - /// 是否打开锁定图层对象,默认为不打开 - /// 图元集合 - [System.Diagnostics.DebuggerStepThrough] - public static IEnumerable GetEntities(this SelectionSet ss, - OpenMode openMode = OpenMode.ForRead, - DBTrans? trans = null, - bool openErased = false, - bool openLockedLayer = false) where T : Entity - { - if (ss is null) - throw new ArgumentNullException(nameof(ss)); - - trans ??= DBTrans.Top; - return ss.GetObjectIds() - .Select(id => trans.GetObject(id, openMode, openErased, openLockedLayer)) - .Where(ent => ent != null); - } - #endregion - - #region ForEach - /// - /// 遍历选择集 - /// - /// 指定图元类型 - /// 选择集 - /// 处理函数;(图元) - /// 打开模式 - /// 事务 - /// 是否打开已删除对象,默认为不打开 - /// 是否打开锁定图层对象,默认为不打开 - [System.Diagnostics.DebuggerStepThrough] - public static void ForEach(this SelectionSet ss, - Action action, - OpenMode openMode = OpenMode.ForRead, - DBTrans? tr = default, - bool openErased = false, - bool openLockedLayer = false) where T : Entity - { - ForEach(ss, (ent, state) => { - action.Invoke(ent); - }, openMode, tr, openErased, openLockedLayer); - } - - /// - /// 遍历选择集 - /// - /// 指定图元类型 - /// 选择集 - /// 处理函数;(图元,终止方式) - /// 打开模式 - /// 事务 - /// 是否打开已删除对象,默认为不打开 - /// 是否打开锁定图层对象,默认为不打开 - /// - [System.Diagnostics.DebuggerStepThrough] - public static void ForEach(this SelectionSet ss, - Action action, - OpenMode openMode = OpenMode.ForRead, - DBTrans? trans = null, - bool openErased = false, - bool openLockedLayer = false) where T : Entity - { - if (action == null) - throw new ArgumentNullException(nameof(action)); - - trans ??= DBTrans.Top; - - LoopState state = new(); - var ents = ss.GetEntities(openMode, trans, openErased, openLockedLayer); - foreach (var ent in ents) - { - action.Invoke(ent, state); - if (!state.IsRun) - break; - } - } - #endregion -} \ No newline at end of file diff --git a/src/IFoxCAD.Cad.Shared/ExtensionMethod/SymbolTableEx.cs b/src/IFoxCAD.Cad.Shared/ExtensionMethod/SymbolTableEx.cs deleted file mode 100644 index 4703c78..0000000 --- a/src/IFoxCAD.Cad.Shared/ExtensionMethod/SymbolTableEx.cs +++ /dev/null @@ -1,358 +0,0 @@ -namespace IFoxCAD.Cad; - -/// -/// 符号表类扩展函数 -/// -public static class SymbolTableEx -{ - #region 图层表 - /// - /// 添加图层 - /// - /// 图层符号表 - /// 图层名 - /// 图层颜色 - /// 图层id - public static ObjectId Add(this SymbolTable table, string name, Color color) - { - return table.Add(name, lt => lt.Color = color); - } - /// - /// 添加图层 - /// - /// 图层符号表 - /// 图层名 - /// 图层颜色索引值 - /// 图层id - public static ObjectId Add(this SymbolTable table, string name, int colorIndex) - { - colorIndex %= 256;// 防止输入的颜色超出256 - colorIndex = Math.Abs(colorIndex);// 防止负数 - return table.Add(name, lt => lt.Color = Color.FromColorIndex(ColorMethod.ByColor, (short)colorIndex)); - } - /// - /// 更改图层名 - /// - /// 图层符号表 - /// 旧图层名 - /// 新图层名 - public static ObjectId Rename(this SymbolTable table, string Oldname, string NewName) - { - if (!table.Has(Oldname)) - return ObjectId.Null; - - table.Change(Oldname, layer => { - layer.Name = NewName; - }); - return table[NewName]; - } - /// - /// 删除图层 - /// - /// 层表 - /// 图层名 - /// 成功返回 ,失败返回 - public static bool Delete(this SymbolTable table, string name) - { - if (name == "0" || name == "Defpoints" || !table.Has(name) || table[name] == DBTrans.Top.Database.Clayer) - return false; - - table.CurrentSymbolTable.GenerateUsageData(); - var ltr = table.GetRecord(name); - if (ltr is null) - return false; - - if (ltr.IsUsed) - return false; - using (ltr.ForWrite()) - ltr.Erase(); - return true; - } - #endregion - - #region 块表 - /// - /// 添加块定义 - /// - /// 块表 - /// 块名 - /// 对所添加块表的委托n - /// 添加图元的委托 - /// 添加属性定义的委托 - /// 块定义id - /// TODO: 需要测试匿名块等特殊的块是否能定义 - public static ObjectId Add(this SymbolTable table, - string name, - Action? action = null, - Func>? ents = null, - Func>? attdef = null) - { - return table.Add(name, btr => { - action?.Invoke(btr); - - var entsres = ents?.Invoke(); - if (entsres is not null) - btr.AddEntity(entsres); - - var adddefres = attdef?.Invoke(); - if (adddefres is not null) - btr.AddEntity(adddefres); - }); - } - /// - /// 添加块定义 - /// - /// 块表 - /// 块名 - /// 图元 - /// 属性定义 - /// - public static ObjectId Add(this SymbolTable table, - string name, - IEnumerable? ents = null, - IEnumerable? attdef = null) - { - return table.Add(name, btr => { - if (ents is not null) - btr.AddEntity(ents); - if (attdef is not null) - btr.AddEntity(attdef); - }); - } - - /// - /// 添加块定义 - /// - /// 块表 - /// 块名 - /// 图元(包括属性) - /// - public static ObjectId Add(this SymbolTable table, string name, params Entity[] ents) - { - return table.Add(name, null, () => { return ents; }); - } - - /// - /// 添加属性到块定义 - /// - /// 块表 - /// 块定义id - /// 属性列表 - public static void AddAttsToBlocks(this SymbolTable table, - ObjectId id, - List atts) - { - List attTags = new(); - table.Change(id, btr => { - - btr.GetEntities() - .ForEach(def => attTags.Add(def.Tag.ToUpper())); - - for (int i = 0; i < atts.Count; i++) - if (!attTags.Contains(atts[i].Tag.ToUpper())) - btr.AddEntity(atts[i]); - }); - } - /// - /// 添加属性到块定义 - /// - /// 块表 - /// 块定义名字 - /// 属性列表 - public static void AddAttsToBlocks(this SymbolTable table, - string name, - List atts) - { - List attTags = new(); - table.Change(name, btr => { - - btr.GetEntities() - .ForEach(def => attTags.Add(def.Tag.ToUpper())); - - for (int i = 0; i < atts.Count; i++) - if (!attTags.Contains(atts[i].Tag.ToUpper())) - btr.AddEntity(atts[i]); - }); - } - - /// - /// 从文件中获取块定义 - /// - /// 块表 - /// 文件名 - /// 是否覆盖 - /// 块定义Id - public static ObjectId GetBlockFrom(this SymbolTable table, string fileName, bool over) - { - // FileInfo fi = new(fileName); - // string blkdefname = fi.Name; - // if (blkdefname.Contains(".")) - // blkdefname = blkdefname.Substring(0, blkdefname.LastIndexOf('.')); - - string blkdefname = SymbolUtilityServices.RepairSymbolName( - SymbolUtilityServices.GetSymbolNameFromPathName(fileName, "dwg"), false); - - ObjectId id = table[blkdefname]; - bool has = id != ObjectId.Null; - if ((has && over) || !has) - { - Database db = new(false, true); - db.ReadDwgFile(fileName, FileShare.Read, true, null); - db.CloseInput(true); - id = table.Database.Insert(BlockTableRecord.ModelSpace, blkdefname, db, false); - } - - return id; - } - - - - /// - /// 从文件中获取块定义 - /// - /// 块表 - /// 文件名 - /// 块定义名 - /// 是否覆盖 - /// 块定义Id - public static ObjectId GetBlockFrom(this SymbolTable table, - string fileName, - string blockName, - bool over) - { - return table.GetRecordFrom(t => t.BlockTable, fileName, blockName, over); - } - #endregion - - - #region 线型表 - /// - /// 添加线型 - /// - /// 线型表 - /// 线型名 - /// 线型说明 - /// 线型长度 - /// 笔画长度数组 - /// 线型id - public static ObjectId Add(this SymbolTable table, string name, string description, double length, double[] dash) - { - return table.Add( - name, - ltt => { - ltt.AsciiDescription = description; - ltt.PatternLength = length; // 线型的总长度 - ltt.NumDashes = dash.Length; // 组成线型的笔画数目 - for (int i = 0; i < dash.Length; i++) - { - ltt.SetDashLengthAt(i, dash[i]); - } - // ltt.SetDashLengthAt(0, 0.5); // 0.5个单位的划线 - // ltt.SetDashLengthAt(1, -0.25); // 0.25个单位的空格 - // ltt.SetDashLengthAt(2, 0); // 一个点 - // ltt.SetDashLengthAt(3, -0.25); // 0.25个单位的空格 - } - ); - } - #endregion - - #region 文字样式表 - /// - /// 添加文字样式记录 - /// - /// 文字样式表 - /// 文字样式名 - /// 字体名 - /// 宽度比例 - /// 文字样式Id - public static ObjectId Add(this SymbolTable table, - string textStyleName, - string font, - double xscale = 1.0) - { - return - table.Add( - textStyleName, - tstr => { - tstr.Name = textStyleName; - tstr.FileName = font; - tstr.XScale = xscale; - }); - } - /// - /// 添加文字样式记录 - /// - /// 文字样式表 - /// 文字样式名 - /// 字体名枚举 - /// 宽度比例 - /// 文字样式Id - public static ObjectId Add(this SymbolTable table, - string textStyleName, - FontTTF fontTTF, - double xscale = 1.0) - { - return table.Add(textStyleName, fontTTF.GetDesc(), xscale); - } - - /// - ///

    添加文字样式记录,如果存在就默认强制替换

    - /// 此函数为了 而设 - ///
    - /// 文字样式表 - /// 文字样式名 - /// 字体名 - /// 大字体名 - /// 宽度比例 - /// 高度 - /// 是否强制替换 - /// 文字样式Id - public static ObjectId AddWithChange(this SymbolTable table, - string textStyleName, - string smallFont, - string bigFont = "", - double xScale = 1, - double height = 0, - bool forceChange = true) - { - if (forceChange && table.Has(textStyleName)) - { - table.Change(textStyleName, ttr => { - ttr.FileName = smallFont; - ttr.XScale = xScale; - ttr.TextSize = height; - if (bigFont != string.Empty) - ttr.BigFontFileName = bigFont; - }); - return table[textStyleName]; - } - return table.Add(textStyleName, ttr => { - ttr.FileName = smallFont; - ttr.XScale = xScale; - ttr.TextSize = height; - }); - } - - - #endregion - - #region 注册应用程序表 - - #endregion - - #region 标注样式表 - - #endregion - - #region 用户坐标系表 - - #endregion - - #region 视图表 - - #endregion - - #region 视口表 - - #endregion -} diff --git a/src/IFoxCAD.Cad.Shared/ExtensionMethod/SymbolTableRecordEx.cs b/src/IFoxCAD.Cad.Shared/ExtensionMethod/SymbolTableRecordEx.cs deleted file mode 100644 index ecd1e31..0000000 --- a/src/IFoxCAD.Cad.Shared/ExtensionMethod/SymbolTableRecordEx.cs +++ /dev/null @@ -1,521 +0,0 @@ -namespace IFoxCAD.Cad; - -/// -/// 符号表记录扩展类 -/// -public static class SymbolTableRecordEx -{ - #region 块表记录 - - #region 克隆实体id - /// - /// 深度克隆id到块表记录 - /// - /// 0x01 此方法不允许是未添加数据库的图元,因此它是id
    - /// 0x02 若为未添加数据库图元,则利用entity.Clone();同时不需要考虑动态块属性,可以使用entity.GetTransformedCopy - ///
    - ///
    - /// - /// - /// 克隆到当前块表记录,相当于原地克隆
    - /// 克隆到目标块表记录内,相当于制作新块 - ///
    - /// - /// 图元id集合,注意所有成员都要在同一个空间中 - /// 返回克隆后的id词典 - public static void DeepCloneEx(this BlockTableRecord btr, ObjectIdCollection objIds, IdMapping maoOut) - { - if (objIds is null || objIds.Count == 0) - throw new ArgumentNullException(nameof(objIds)); - - var db = objIds[0].Database; - using (btr.ForWrite()) - { - try - { - db.DeepCloneObjects(objIds, btr.ObjectId, maoOut, false); - - // 不在此提取,为了此函数被高频调用 - // 获取克隆键值对(旧块名,新块名) - // foreach (ObjectId item in blockIds) - // result.Add(mapping[item].Value); - } - catch (System.Exception e) - { - LogHelper.FlagOutVsOutput = true; - e.WriteLog("深度克隆出错了"); - } - } - } - - /// - /// 克隆图元实体(这个函数有问题,会出现偶尔成功,偶尔失败,拖动过变成匿名块) - /// 若为块则进行设置属性,因此控制动态块属性丢失; - /// - /// 图元 - /// 矩阵 - // public static void EntityTransformedCopy(this Entity ent, Matrix3d matrix) - // { - // var entNew = ent.GetTransformedCopy(matrix); - // if (ent is BlockReference blockReference) - // entNew.SetPropertiesFrom(blockReference); - // } - - #endregion - - #region 添加实体 - /// - /// 添加实体对象 - /// - /// 块表记录 - /// 实体 - /// 事务管理器 - /// 对象 id - public static ObjectId AddEntity(this BlockTableRecord btr, Entity entity, - Transaction? trans = null) - { - // if (entity is null) - // throw new ArgumentNullException(nameof(entity), "对象为 null"); - - ObjectId id; - trans ??= DBTrans.Top.Transaction; - using (btr.ForWrite()) - { - id = btr.AppendEntity(entity); - trans.AddNewlyCreatedDBObject(entity, true); - } - return id; - } - - /// - /// 添加实体集合 - /// - /// 实体类型 - /// 块表记录 - /// 实体集合 - /// 事务 - /// 对象 id 列表 - public static IEnumerable AddEntity(this BlockTableRecord btr, IEnumerable ents, - Transaction? trans = null) where T : Entity - { - // if (ents.Any(ent => ent is null)) - // throw new ArgumentNullException(nameof(ents), "实体集合内存在 null 对象"); - - trans ??= DBTrans.Top.Transaction; - using (btr.ForWrite()) - { - return ents.Select(ent => { - ObjectId id = btr.AppendEntity(ent); - trans.AddNewlyCreatedDBObject(ent, true); - return id; - }).ToList(); - } - } - - /// - /// 添加多个实体 - /// - /// 块表记录 - /// 实体集合 - /// 对象 id 列表 - public static IEnumerable AddEntity(this BlockTableRecord btr, params Entity[] ents) - { - return btr.AddEntity(ents, null); - } - #endregion - - #region 添加图元 - /// - /// 在指定绘图空间添加图元 - /// - /// 图元类型 - /// 绘图空间 - /// 图元对象 - /// 图元属性设置委托 - /// 事务管理器 - /// 图元id - private static ObjectId AddEnt(this BlockTableRecord btr, T ent, - Action? action, Transaction? trans = null) where T : Entity - { - action?.Invoke(ent); - return btr.AddEntity(ent, trans); - } - /// - /// 委托式的添加图元 - /// - /// 块表 - /// 返回图元的委托 - /// 事务 - /// 图元id,如果委托返回 null,则为 ObjectId.Null - public static ObjectId AddEnt(this BlockTableRecord btr, Func action, Transaction? trans = null) - { - var ent = action.Invoke(); - if (ent is null) - return ObjectId.Null; - - return btr.AddEntity(ent, trans); - } - - /// - /// 在指定绘图空间添加直线 - /// - /// 事务管理器 - /// 起点 - /// 终点 - /// 绘图空间 - /// 直线属性设置委托 - /// 直线的id - public static ObjectId AddLine(this BlockTableRecord btr, Point3d start, Point3d end, - Action? action = null, Transaction? trans = null) - { - var line = new Line(start, end); - return btr.AddEnt(line, action, trans); - } - /// - /// 在指定绘图空间X-Y平面添加圆 - /// - /// 绘图空间 - /// 圆心 - /// 半径 - /// 圆属性设置委托 - /// 事务管理器 - /// 圆的id - public static ObjectId AddCircle(this BlockTableRecord btr, Point3d center, double radius, - Action? action = null, Transaction? trans = null) - { - var circle = new Circle(center, Vector3d.ZAxis, radius); - return btr.AddEnt(circle, action, trans); - } - - /// - /// 在指定绘图空间X-Y平面3点画外接圆 - /// - /// 绘图空间 - /// 第一点 - /// 第二点 - /// 第三点 - /// 圆属性设置委托 - /// 事务管理器 - /// 三点有外接圆则返回圆的id,否则返回ObjectId.Null - public static ObjectId AddCircle(this BlockTableRecord btr, Point3d p0, Point3d p1, Point3d p2, - Action? action = null, Transaction? trans = null) - { - var circle = EntityEx.CreateCircle(p0, p1, p2); - // return circle is not null ? btr.AddEnt(circle, action, trans) : throw new ArgumentNullException(nameof(circle), "对象为 null"); - if (circle is null) - throw new ArgumentNullException(nameof(circle), "对象为 null"); - - return btr.AddEnt(circle, action, trans); - } - /// - /// 在指定的绘图空间添加轻多段线 - /// - /// 绘图空间 - /// 多段线信息 - /// 线宽 - /// 是否闭合 - /// 轻多段线属性设置委托 - /// 事务管理器 - /// 轻多段线id - public static ObjectId AddPline(this BlockTableRecord btr, - List bvws, - double? constantWidth = null, - bool isClosed = true, - Action? action = null, Transaction? trans = null) - { - Polyline pl = new(); - pl.SetDatabaseDefaults(); - if (constantWidth is not null) - { - for (int i = 0; i < bvws.Count; i++) - pl.AddVertexAt(i, bvws[i].Vertex, bvws[i].Bulge, constantWidth.Value, constantWidth.Value); - } - else - { - for (int i = 0; i < bvws.Count; i++) - pl.AddVertexAt(i, bvws[i].Vertex, bvws[i].Bulge, bvws[i].StartWidth, bvws[i].EndWidth); - } - pl.Closed = isClosed;// 闭合 - return btr.AddEnt(pl, action, trans); - } - /// - /// 在指定的绘图空间添加轻多段线 - /// - /// 绘图空间 - /// 端点表 - /// 凸度表 - /// 端点的起始宽度 - /// 端点的终止宽度 - /// 轻多段线属性设置委托 - /// 事务管理器 - /// 轻多段线id - public static ObjectId AddPline(this BlockTableRecord btr, - List pts, - List? bulges = null, - List? startWidths = null, - List? endWidths = null, - Action? action = null, - Transaction? trans = null) - { - bulges ??= new(new double[pts.Count]); - startWidths ??= new(new double[pts.Count]); - endWidths ??= new(new double[pts.Count]); - - Polyline pl = new(); - pl.SetDatabaseDefaults(); - - for (int i = 0; i < pts.Count; i++) - pl.AddVertexAt(i, pts[i].Point2d(), bulges[i], startWidths[i], endWidths[i]); - return btr.AddEnt(pl, action, trans); - } - - /// - /// 在指定的绘图空间添加轻多段线 - /// - /// 绘图空间 - /// 端点表,利用元组(Point3d pt, double bulge, double startWidth, double endWidth) - /// 轻多段线属性设置委托 - /// 事务管理器 - /// 轻多段线id - public static ObjectId AddPline(this BlockTableRecord btr, - List<(Point3d pt, double bulge, double startWidth, double endWidth)> pts, - Action? action = null, - Transaction? trans = null) - { - Polyline pl = new(); - pl.SetDatabaseDefaults(); - - pts.ForEach((vertex, state, index) => { - pl.AddVertexAt(index, vertex.pt.Point2d(), vertex.bulge, vertex.startWidth, vertex.endWidth); - }); - - return btr.AddEnt(pl, action, trans); - } - - /// - /// 在指定绘图空间X-Y平面3点画圆弧 - /// - /// 绘图空间 - /// 圆弧起点 - /// 圆弧上的点 - /// 圆弧终点 - /// 圆弧属性设置委托 - /// 事务管理器 - /// 圆弧id - public static ObjectId AddArc(this BlockTableRecord btr, - Point3d startPoint, Point3d pointOnArc, Point3d endPoint, - Action? action = null, Transaction? trans = null) - { - var arc = EntityEx.CreateArc(startPoint, pointOnArc, endPoint); - return btr.AddEnt(arc, action, trans); - } - #endregion - - #region 获取实体/实体id - /// - /// 获取块表记录内的指定类型的实体 - /// (此处不会检查id.IsOk()) - /// - /// 实体类型 - /// 块表记录 - /// 打开模式 - /// 事务 - /// 是否打开已删除对象,默认为不打开 - /// 是否打开锁定图层对象,默认为不打开 - /// 实体集合 - public static IEnumerable GetEntities(this BlockTableRecord btr, - OpenMode openMode = OpenMode.ForRead, - Transaction? trans = null, - bool openErased = false, - bool openLockedLayer = false) where T : Entity - { - trans ??= DBTrans.Top.Transaction; - return - btr - .Cast() - .Select(id => trans.GetObject(id, openMode, openErased, openLockedLayer)) - .OfType(); - } - - /// - /// 按类型获取实体Id - /// - /// 实体类型 - /// 块表记录 - /// 实体Id集合 - public static IEnumerable GetObjectIds(this BlockTableRecord btr) where T : Entity - { - string dxfName = RXClass.GetClass(typeof(T)).DxfName; - return btr.Cast() - .Where(id => id.ObjectClass()?.DxfName == dxfName); - } - - /// - /// 按类型获取实体Id的分组 - /// - /// 块表记录 - /// 实体Id分组 - public static IEnumerable> GetObjectIds(this BlockTableRecord btr) - { - return btr.Cast() - .GroupBy(id => id.ObjectClass().DxfName); - } - - - /// - /// 获取绘制顺序表 - /// - /// 块表 - /// 事务 - /// 是否打开已删除对象,默认为不打开 - /// 是否打开锁定图层对象,默认为不打开 - /// 绘制顺序表 - public static DrawOrderTable? GetDrawOrderTable(this BlockTableRecord btr, - Transaction? trans = null, - bool openErased = false, - bool openLockedLayer = false) - { - trans ??= DBTrans.Top.Transaction; - return trans.GetObject(btr.DrawOrderTableId, OpenMode.ForRead, - openErased, openLockedLayer) as DrawOrderTable; - } - #endregion - - #region 插入块参照 - /// - /// 插入块参照 - /// - /// 块表记录 - /// 插入点 - /// 块名 - /// 块插入比例,默认为1 - /// 块插入旋转角(弧度),默认为0 - /// 属性字典{Tag,Value},默认为null - /// 事务 - /// 块参照对象id - public static ObjectId InsertBlock(this BlockTableRecord blockTableRecord, Point3d position, - string blockName, - Scale3d scale = default, - double rotation = default, - Dictionary? atts = null, - Transaction? trans = null) - { - trans ??= DBTrans.Top.Transaction; - if (!DBTrans.Top.BlockTable.Has(blockName)) - { - DBTrans.Top.Editor?.WriteMessage($"\n不存在名字为{blockName}的块定义。"); - return ObjectId.Null; - } - return blockTableRecord.InsertBlock(position, DBTrans.Top.BlockTable[blockName], scale, rotation, atts, trans); - } - /// - /// 插入块参照 - /// - /// 插入点 - /// 块定义id - /// 块插入比例,默认为1 - /// 块插入旋转角(弧度),默认为0 - /// 属性字典{Tag,Value},默认为null - /// 块参照对象id - public static ObjectId InsertBlock(this BlockTableRecord blockTableRecord, - Point3d position, - ObjectId blockId, - Scale3d scale = default, - double rotation = default, - Dictionary? atts = null, - Transaction? trans = null) - { - trans ??= DBTrans.Top.Transaction; - if (!DBTrans.Top.BlockTable.Has(blockId)) - { - DBTrans.Top.Editor?.WriteMessage($"\n不存在块定义。"); - return ObjectId.Null; - } - using var blockref = new BlockReference(position, blockId) - { - ScaleFactors = scale, - Rotation = rotation - }; - var objid = blockTableRecord.AddEntity(blockref); - - if (atts != null) - { - var btr = DBTrans.Top.GetObject(blockref.BlockTableRecord)!; - if (btr.HasAttributeDefinitions) - { - var attdefs = btr.GetEntities(); - foreach (var attdef in attdefs) - { - using AttributeReference attref = new(); - attref.SetDatabaseDefaults(); - attref.SetAttributeFromBlock(attdef, blockref.BlockTransform); - attref.Position = attdef.Position.TransformBy(blockref.BlockTransform); - attref.AdjustAlignment(DBTrans.Top.Database); - - if (atts.ContainsKey(attdef.Tag)) - attref.TextString = atts[attdef.Tag]; - - blockref.AttributeCollection.AppendAttribute(attref); - trans.AddNewlyCreatedDBObject(attref, true); - } - } - } - return objid; - } - #endregion - - #endregion - - #region 遍历 -#line hidden // 调试的时候跳过它 - /// - /// 遍历符号表记录,执行委托 - /// - /// 要运行的委托 - public static void ForEach(this TRecord record, Action task) - where TRecord : SymbolTableRecord, IEnumerable - { - foreach (ObjectId id in record) - task.Invoke(id); - } - - /// - /// 遍历符号表记录,执行委托(允许循环中断) - /// - /// 要执行的委托 - public static void ForEach(this TRecord record, Action task) - where TRecord : SymbolTableRecord, IEnumerable - { - LoopState state = new();/*这种方式比Action改Func更友好*/ - foreach (ObjectId id in record) - { - task.Invoke(id, state); - if (!state.IsRun) - break; - } - } - - /// - /// 遍历符号表记录,执行委托(允许循环中断,输出索引值) - /// - /// 要执行的委托 - [System.Diagnostics.DebuggerStepThrough] - public static void ForEach(this TRecord record, Action task) - where TRecord : SymbolTableRecord, IEnumerable - { - if (task == null) - throw new ArgumentNullException(nameof(task)); - - int i = 0; - LoopState state = new();/*这种方式比Action改Func更友好*/ - foreach (ObjectId id in record) - { - task.Invoke(id, state, i); - if (!state.IsRun) - break; - i++; - } - } -#line default - #endregion -} \ No newline at end of file diff --git a/src/IFoxCAD.Cad.Shared/ExtensionMethod/Tools.cs b/src/IFoxCAD.Cad.Shared/ExtensionMethod/Tools.cs deleted file mode 100644 index 1127722..0000000 --- a/src/IFoxCAD.Cad.Shared/ExtensionMethod/Tools.cs +++ /dev/null @@ -1,60 +0,0 @@ -using static IFoxCAD.Basal.Timer; - -namespace IFoxCAD.Cad; - -public static class Tools -{ - /// - /// 计时器 - /// - [System.Diagnostics.DebuggerStepThrough] - public static void TestTimes2(int count, string message, Action action) - { - System.Diagnostics.Stopwatch watch = new(); - watch.Start(); // 开始监视代码运行时间 - for (int i = 0; i < count; i++) - action.Invoke();// 需要测试的代码 - watch.Stop(); // 停止监视 - TimeSpan timespan = watch.Elapsed; // 获取当前实例测量得出的总时间 - double time = timespan.TotalMilliseconds; - string name = "毫秒"; - if (timespan.TotalMilliseconds > 1000) - { - time = timespan.TotalSeconds; - name = "秒"; - } - Env.Print($"{message} 代码执行 {count} 次的时间:{time} ({name})"); // 总毫秒数 - } - - /// - /// 纳秒计时器 - /// - [System.Diagnostics.DebuggerStepThrough] - public static void TestTimes(int count, string message, Action action, - TimeEnum timeEnum = TimeEnum.Millisecond) - { - var time = RunTime(() => { - for (int i = 0; i < count; i++) - action(); - }, timeEnum); - - string timeNameZn = ""; - switch (timeEnum) - { - case TimeEnum.Second: - timeNameZn = " 秒"; - break; - case TimeEnum.Millisecond: - timeNameZn = " 毫秒"; - break; - case TimeEnum.Microsecond: - timeNameZn = " 微秒"; - break; - case TimeEnum.Nanosecond: - timeNameZn = " 纳秒"; - break; - } - - Env.Print($"{message} 代码执行 {count} 次的时间:{time} ({timeNameZn})"); - } -} \ No newline at end of file diff --git a/src/IFoxCAD.Cad.Shared/ExtensionMethod/XrefEx.cs b/src/IFoxCAD.Cad.Shared/ExtensionMethod/XrefEx.cs deleted file mode 100644 index 9ecdaa8..0000000 --- a/src/IFoxCAD.Cad.Shared/ExtensionMethod/XrefEx.cs +++ /dev/null @@ -1,656 +0,0 @@ -// #define error_demo - -namespace IFoxCAD.Cad; - -#region 参照工厂 -public interface IXrefBindModes -{ - /// - /// 卸载 - /// - public void Unload(); - /// - /// 重载 - /// - public void Reload(); - /// - /// 拆离 - /// - public void Detach(); - /// - /// 绑定 - /// - public void Bind(); -} - -public class XrefFactory : IXrefBindModes -{ - #region 私有字段 - readonly DBTrans _tr; - /// - /// 要处理的参照名称,就处理所有 - /// - readonly HashSet? _xrefNames; - #endregion - - #region 公开字段 - /// - /// 解析外部参照:线性引擎
    - /// 默认
    - /// 时会在cad命令历史打印一些AEC信息,并导致绑定慢一点...具体作用不详
    - ///
    - public bool UseThreadEngine = false; - /// - /// 解析外部参照:仅处理 Unresolved_未融入(未解析)的参照
    - /// 默认 - ///
    - public bool DoNewOnly = true; - /// - /// 解析外部参照:包含僵尸参照 - /// - public bool IncludeGhosts = true; - - - /// - /// 绑定模式和双美元符号相关(与cad保持相同的默认)
    - /// 为绑定模式,产生双美元; - /// 为插入模式,块重名会以本图覆盖; - ///
    - public bool BindOrInsert = false; - /// - /// bind时候是否拆离参照
    - /// 默认:学官方的绑定后自动拆离 - ///
    - public bool AutoDetach = true; - /// - /// bind时候是否删除被卸载的嵌套参照
    - /// 默认 - ///
    - public bool EraseNested = true; - /// - /// bind时候控制绑定的符号表:请保持默认
    - /// 目前仅推荐用于
    - /// 其他项有异常:
    - ///
    - public SymModes SymModesBind = SymModes.LayerTable; - #endregion - - #region 构造 - /// - /// 参照工厂 - /// - /// - /// 要处理的参照名称,就处理所有 - public XrefFactory(DBTrans tr, HashSet? xrefNames = null) - { - _tr = tr; - _xrefNames = xrefNames; - } - #endregion - - #region 重写 - public void Bind() - { - // 此功能有绑定出错的问题 - // db.BindXrefs(xrefIds, true); - - // 绑定后会自动拆离 - // 此功能修补了上面缺失 - DoubleBind(); - } - - public void Detach() - { - using ObjectIdCollection xrefIds = new(); - GetAllXrefNode(xrefIds); - foreach (ObjectId id in xrefIds) - _tr.Database.DetachXref(id); - } - - public void Reload() - { - using ObjectIdCollection xrefIds = new(); - GetAllXrefNode(xrefIds); - if (xrefIds.Count > 0) - _tr.Database.ReloadXrefs(xrefIds); - } - - public void Unload() - { - using ObjectIdCollection xrefIds = new(); - GetAllXrefNode(xrefIds); - if (xrefIds.Count > 0) - _tr.Database.UnloadXrefs(xrefIds); - } - #endregion - - #region 双重绑定 - /// - /// 获取参照 - /// - /// 返回全部参照id - void GetAllXrefNode(ObjectIdCollection xrefIds) - { - // 储存要处理的参照id - //var xrefIds = new ObjectIdCollection(); - XrefNodeForEach((xNodeName, xNodeId, xNodeStatus, xNodeIsNested) => { - if (XrefNamesContains(xNodeName)) - xrefIds.Add(xNodeId); - }); - } - - bool XrefNamesContains(string xNodeName) - { - // 为空的时候全部加入 || 有内容时候含有目标 - return _xrefNames is null || _xrefNames.Contains(xNodeName); - } - - /// - /// 遍历参照 - /// - /// (参照名,参照块表记录id,参照状态,是否嵌入) - void XrefNodeForEach(Action action) - { - // btRec.IsFromOverlayReference 是覆盖 - // btRec.GetXrefDatabase(true) 外部参照数据库 - - if (action == null) - return; - - // 解析外部参照:此功能不能锁定文档 - _tr.Database.ResolveXrefs(UseThreadEngine, DoNewOnly); - - var xg = _tr.Database.GetHostDwgXrefGraph(IncludeGhosts); - for (int i = 0; i < xg.NumNodes; i++) - { - var xNode = xg.GetXrefNode(i); - if (!xNode.BlockTableRecordId.IsOk()) - continue; - - action.Invoke(xNode.Name, - xNode.BlockTableRecordId, - xNode.XrefStatus, - xNode.IsNested); - } - } - - /// - /// 符号表记录加入容器 - /// - static void AddedxbindIds(ObjectIdCollection xbindXrefsIds, - SymbolTable symbolTable) - where TTable : SymbolTable - where TRecord : SymbolTableRecord, new() - { - symbolTable.ForEach(tabRec => { - if (tabRec.IsResolved) - xbindXrefsIds.Add(tabRec.ObjectId); - }, checkIdOk: true); - } - - - void GetXBindIds(ObjectIdCollection xbindIds) - { - // xbind - // 0x01 它是用来绑其他符号表,绑块表会有异常 - // 0x02 集合若有问题,就会出现eWrongObjectType - //var xbindIds = new ObjectIdCollection(); - - // 起初测试是将九大符号表记录均加入的,但经实测不行...(为什么?存疑) - #region Option1 - if ((SymModesBind & SymModes.LayerTable) == SymModes.LayerTable) - AddedxbindIds(xbindIds, _tr.LayerTable); - - if ((SymModesBind & SymModes.TextStyleTable) == SymModes.TextStyleTable) - AddedxbindIds(xbindIds, _tr.TextStyleTable); - - if ((SymModesBind & SymModes.RegAppTable) == SymModes.RegAppTable) - AddedxbindIds(xbindIds, _tr.RegAppTable); - - if ((SymModesBind & SymModes.DimStyleTable) == SymModes.DimStyleTable) - AddedxbindIds(xbindIds, _tr.DimStyleTable); - - if ((SymModesBind & SymModes.LinetypeTable) == SymModes.LinetypeTable) - AddedxbindIds(xbindIds, _tr.LinetypeTable); - #endregion - - #region Option2 - if ((SymModesBind & SymModes.UcsTable) == SymModes.UcsTable) - AddedxbindIds(xbindIds, _tr.UcsTable); - - if ((SymModesBind & SymModes.ViewTable) == SymModes.ViewTable) - AddedxbindIds(xbindIds, _tr.ViewTable); - - if ((SymModesBind & SymModes.ViewportTable) == SymModes.ViewportTable) - AddedxbindIds(xbindIds, _tr.ViewportTable); - #endregion - } - - void GetBindIds(ObjectIdCollection bindIds) - { - // bind 只绑块表 - //var bindIds = new ObjectIdCollection(); - - _tr.BlockTable.ForEach(btr => { - if (btr.IsLayout) - return; - - // 外部参照 && 已融入 - if (btr.IsFromExternalReference && btr.IsResolved) - bindIds.Add(btr.ObjectId); - }, checkIdOk: true); - } - - /// - /// 获取可以拆离的ids - /// - /// 返回已卸载中含有嵌套的参照,要重载之后才能绑定 - /// 返回未参照中嵌套的参照,直接拆离 - List GetDetachIds(Dictionary nested) - { - // 直接拆离的id - List detachIds = new(); - - // 收集要处理的id - XrefNodeForEach((xNodeName, xNodeId, xNodeStatus, xNodeIsNested) => { - switch (xNodeStatus) - { - case XrefStatus.Unresolved:// 未融入_ResolveXrefs参数2 - break; - case XrefStatus.FileNotFound:// 未融入(未解析)_未找到文件 - break; - case XrefStatus.Unreferenced:// 未参照 - { - // 为空的时候全部加入 || 有内容时候含有目标 - if (XrefNamesContains(xNodeName)) - detachIds.Add(xNodeId); - } - break; - case XrefStatus.Unloaded:// 已卸载 - { - // 为空的时候全部加入 || 有内容时候含有目标 - if (XrefNamesContains(xNodeName)) - { - var btr = _tr.GetObject(xNodeId); - if (btr != null && btr.IsFromExternalReference) - { - if (!xNodeIsNested) - detachIds.Add(xNodeId); - else if (!nested.ContainsKey(xNodeId)) - nested.Add(xNodeId, xNodeName);// 嵌套参照 - } - } - } - break; - case XrefStatus.Resolved:// 已融入_就是可以绑定的 - break; - case XrefStatus.NotAnXref:// 不是外部参照 - break; - default: - break; - } - }); - return detachIds; - } - - /// - /// 双重绑定参照 - /// 参考链接 - /// - void DoubleBind() - { - Dictionary nested = new(); - var detachIds = GetDetachIds(nested); - - // 拆离:未参照的文件 - if (AutoDetach) - { - for (int i = 0; i < detachIds.Count; i++) - _tr.Database.DetachXref(detachIds[i]); - } - // 重载:嵌套参照已卸载了,需要重载之后才能进行绑定 - var keys = nested.Keys; - if (keys.Count > 0) - { - using ObjectIdCollection idc = new(keys.ToArray()); - _tr.Database.ReloadXrefs(idc); - } - - // 绑定:切勿交换,否则会绑定无效 - using ObjectIdCollection bindIds = new(); - using ObjectIdCollection xbindIds = new(); - GetBindIds(bindIds); - GetXBindIds(xbindIds); - if (xbindIds.Count > 0) - _tr.Database.XBindXrefs(xbindIds, BindOrInsert); - if (bindIds.Count > 0) - _tr.Database.BindXrefs(bindIds, BindOrInsert); - - - // 内部删除嵌套参照的块操作 - if (EraseNested) - { -#if ac2008 - // 因为Acad08索引器存在会暴露isErase的(桌子底层的原因), - // 也就是可能获取两个名称一样的,只能用遍历的方式进行 - HashSet nestedHash = new(); - foreach (var item in nested) - nestedHash.Add(item.Value); - - // 遍历全图,找到参照名称一样的删除 - _tr.BlockTable.ForEach(btr => { - if (btr.IsLayout) - return; - if (nestedHash.Contains(btr.Name)) - { - btr.UpgradeOpen(); - btr.Erase(); - btr.DowngradeOpen(); - btr.Dispose(); - } - }, checkIdOk: true); -#else - foreach (var item in nested) - { - var name = item.Value; - if (_tr.BlockTable.Has(name)) - _tr.GetObject(_tr.BlockTable[name], OpenMode.ForWrite)? - .Erase(); - } -#endif - } - } - #endregion -} - - - -public static class XrefEx -{ - /// - /// 外部参照工厂 - /// - /// - /// 处理参照的枚举 - /// 要处理的参照名称,就处理所有 - public static void XrefFactory(this DBTrans tr, - XrefModes xrefModes, - HashSet? xrefNames = null) - { - var xf = new XrefFactory(tr, xrefNames); - tr.Task(() => { - switch (xrefModes) - { - case XrefModes.Unload: - xf.Unload(); - break; - case XrefModes.Reload: - xf.Reload(); - break; - case XrefModes.Detach: - xf.Detach(); - break; - case XrefModes.Bind: - xf.Bind(); - break; - default: - break; - } - }); - } -} - -#endregion - -#region 参照路径工具类 -/// -/// 获取外部参照的路径 -/// -public class XrefPath -{ - #region 属性 - /// - /// 基础路径 - /// - public string CurrentDatabasePath; - /// - /// 是否外部参照 - /// - public bool IsFromExternalReference { get; private set; } - /// - /// 外部参照保存的路径 - /// - /// 它们会是以下任一路径:
    - /// 0x01 相对路径
    - /// 0x02 绝对路径
    - /// 0x03 共目录优先找到的路径(文件夹整体移动会发生此类情况) - ///
    - ///
    - public string? PathSave { get; private set; } - /// - /// 找到的路径(参照面板的名称) - /// 路径不存在时,返回是外部参照dwg文件路径 - /// - public string? PathDescribe { get; private set; } - - string? _PathComplete; - /// - /// 绝对路径 - /// - public string? PathComplete => _PathComplete ??= - PathConverter(CurrentDatabasePath, PathDescribe, PathConverterModes.Complete); - - string? _PathRelative; - /// - /// 相对路径 - /// - public string? PathRelative => _PathRelative ??= - PathConverter(CurrentDatabasePath, PathComplete, PathConverterModes.Relative); - #endregion - - #region 构造 - /// - /// 获取外部参照的路径 - /// - /// 外部参照图元 - /// 事务 - /// 是否外部参照 - public XrefPath(BlockReference brf, DBTrans tr) - { - if (brf == null) - throw new ArgumentNullException(nameof(brf)); - - CurrentDatabasePath = Path.GetDirectoryName(tr.Database.Filename); - - var btRec = tr.GetObject(brf.BlockTableRecord);// 块表记录 - if (btRec == null) - return; - - IsFromExternalReference = btRec.IsFromExternalReference; - if (!IsFromExternalReference) - return; - - // 相对路径==".\\AA.dwg" - // 无路径=="AA.dwg" - PathSave = btRec.PathName; - - if ((!string.IsNullOrEmpty(PathSave) && PathSave[0] == '.') || File.Exists(PathSave)) - { - // 相对路径||绝对路径 - PathDescribe = PathSave; - } - else - { - // 无路径 - var db = btRec.GetXrefDatabase(true); - PathDescribe = db.Filename; - } - } - #endregion - - #region 静态函数 - /// - /// 获取相对路径或者绝对路径 - /// 参考链接 - /// - /// 基础目录(末尾无斜杠) - /// 相对路径或者绝对路径 - /// 依照枚举返回对应的字符串 - /// - public static string? PathConverter(string? directory, string? fileRelations, PathConverterModes converterModes) - { - if (directory == null) - throw new ArgumentNullException(nameof(directory)); - if (fileRelations == null) - throw new ArgumentNullException(nameof(fileRelations)); - - string? result = null; - switch (converterModes) - { - case PathConverterModes.Relative: - result = GetRelativePath(directory, fileRelations); - break; - case PathConverterModes.Complete: - result = GetCompletePath(directory, fileRelations); - break; - default: - break; - } - return result; - } - -#if error_demo - /// - /// 绝对路径->相对路径 - /// - /// 绝对路径 - /// 相对关系 - /// - /// StringHelper.GetRelativePath("G:\\A1.项目\\20190920金山谷黄宅\\01.饰施图\\03.平面图", - /// "G:\\A1.项目\\20190920金山谷黄宅\\01.饰施图\\01.辅助文件\\图框\\A3图框.dwg"); - public static string GetRelativePath(string strDbPath, string strXrefPath) - { - Uri uri1 = new(strXrefPath); - Uri uri2 = new(strDbPath); - Uri relativeUri = uri2.MakeRelativeUri(uri1); - // 测试例子变成 01.%E8%BE%85%E5%8A%A9%E6%96%87%E4%BB%B6/%E5%9B%BE%E6%A1%86/A3%E5%9B%BE%E6%A1%86.dwg - string str = relativeUri.ToString(); - - // 因为这里不会实现".\A.dwg"而是"A.dwg",所以加入这个操作,满足同目录文件 - var strs = str.Split('\\'); - if (strs.Length == 1) - str = ".\\" + str; - return str; - } -#else - /// - /// 绝对路径->相对路径 - /// - /// 相对关系:文件夹路径 - /// 完整路径:文件路径 - /// 相对路径 - /// "..\\01.辅助文件\\图框\\A3图框.dwg" - /// ]]> - static string GetRelativePath(string directory, string file) - { - string[] directorys = directory.Split('\\'); - string[] files = file.Split('\\'); - // 获取两条路径中的最短路径 - int getMinLength = directorys.Length < files.Length ? directorys.Length : files.Length; - - // 用于确定我们退出的循环中的位置。 - int lastCommonRoot = -1; - int index; - // 找到共根 - for (index = 0; index < getMinLength; index++) - { - if (directorys[index] != files[index]) - break; - lastCommonRoot = index; - } - // 如果我们没有找到一个共同的前缀,那么抛出 - if (lastCommonRoot == -1) - throw new ArgumentException("路径没有公共相同路径部分"); - - // 建立相对路径 - var result = new StringBuilder(); - for (index = lastCommonRoot + 1; index < directorys.Length; index++) - if (directorys[index].Length > 0) - result.Append("..\\");// 上级目录加入 - - // 添加文件夹 - for (index = lastCommonRoot + 1; index < files.Length - 1; index++) - result.Append(files[index] + "\\"); - - // 本级目录 - if (result.Length == 0) - result.Append(".\\"); - // result.Append(strXrefPaths[^1]);// 下级目录加入 - result.Append(files[files.Length - 1]);// 下级目录加入 - return result.ToString(); - } -#endif - - /// - /// 相对路径->绝对路径 - /// - /// 文件夹路径 - /// 相对关系:有..的 - /// 完整路径 - /// "G:\\A1.项目\\20190920金山谷黄宅\\01.饰施图\\01.辅助文件\\图框\\A3图框.dwg" - /// ]]> - static string? GetCompletePath(string directory, string relativePath) - { - if (relativePath is null || relativePath.Trim() == string.Empty) - return null; - - var relativeName = Path.GetDirectoryName(relativePath); - if (relativeName is null) - return null; - - if (relativePath[0] != '.') - return relativePath; - - const char slash = '\\'; - - // 判断向上删除几个 - var path_xiangduis = relativeName.Split(slash); - int index = 0; - for (int i = 0; i < path_xiangduis.Length; i++) - { - if (path_xiangduis[i] != "..") - break; - index++; - } - - var result = new StringBuilder(); - // 前段 - var path_dwgs = directory.Split(slash); - path_dwgs = path_dwgs.Where(s => !string.IsNullOrEmpty(s)).ToArray();// 清理空数组 - for (int i = 0; i < path_dwgs.Length - index; i++) - { - result.Append(path_dwgs[i]); - result.Append(slash); - } - // 后段 - for (int i = 0; i < path_xiangduis.Length; i++) - { - var item = path_xiangduis[i]; - if (item != "." && item != "..") - { - result.Append(item); - result.Append(slash); - } - } - result.Append(Path.GetFileName(relativePath)); - return result.ToString(); - } - #endregion -} -#endregion \ No newline at end of file diff --git "a/src/IFoxCAD.Cad.Shared/ExtensionMethod/\346\226\260\345\273\272\345\241\253\345\205\205/HatchConverter.cs" "b/src/IFoxCAD.Cad.Shared/ExtensionMethod/\346\226\260\345\273\272\345\241\253\345\205\205/HatchConverter.cs" deleted file mode 100644 index ea7ca2e..0000000 --- "a/src/IFoxCAD.Cad.Shared/ExtensionMethod/\346\226\260\345\273\272\345\241\253\345\205\205/HatchConverter.cs" +++ /dev/null @@ -1,395 +0,0 @@ -namespace IFoxCAD.Cad; - -using System.Data; -using PointV = Point2d; - -/// -/// 填充边界转换器 -/// -public class HatchConverter -{ - #region 辅助类 - /// - /// 生成圆形数据 - /// - class CircleData - { - public PointV Center; - public double Radius; - - /// - /// 生成圆形数据 - /// - /// 对称点1 - /// 对称点2 - public CircleData(PointV symmetryAxisPoint1, PointV symmetryAxisPoint2) - { - Center = symmetryAxisPoint1.GetCenter(symmetryAxisPoint2); - Radius = symmetryAxisPoint1.GetDistanceTo(symmetryAxisPoint2) * 0.5; - } - } - - /// - /// 填充转换器的数据 - /// - class HatchConverterData - { - public List PolyLineData; - public List CircleData; - public List SplineData; - - /// - /// 填充转换器的数据 - /// - public HatchConverterData() - { - PolyLineData = new(); - CircleData = new(); - SplineData = new(); - } - } - #endregion - - #region 成员 - /// - /// 外部只能调用id,否则跨事务造成错误 - /// - public ObjectId OldHatchId - { - get - { - if (_oldHatch is null) - return ObjectId.Null; - return _oldHatch.ObjectId; - } - } - readonly Hatch? _oldHatch; - - readonly List _hcDatas; - /// - /// 填充边界id(生成的/已存在反应器的直接提取) - /// - public List BoundaryIds; - #endregion - - #region 构造 - /// - /// 填充边界转换器 - /// - HatchConverter() - { - _hcDatas = new(); - BoundaryIds = new(); - } - - /// - /// 填充边界转换器 - /// - /// 需要转化的Hatch对象 - public HatchConverter(Hatch hatch) : this() - { - _oldHatch = hatch; - - if (hatch.Associative) - { - // 填充边界反应器 - var assIds = hatch.GetAssociatedObjectIds(); - if (assIds != null) - { - foreach (ObjectId id in assIds) - if (id.IsOk()) - BoundaryIds.Add(id); - - if (BoundaryIds.Count == 0) - { - throw new ArgumentException("关联的填充边界被删除后没有清理反应器,请调用:" + - "\n hatch.RemoveAssociatedObjectIds()" + - "\n hatch.Associative = false"); - } - } - } - } - - /// - /// 提取边界信息 - /// - public void GetBoundarysData() - { - _oldHatch?.ForEach(loop => { - HatchConverterData hcData = new(); - - bool isCurve2d = true; - if (loop.IsPolyline) - { - // 边界是多段线 - HatchLoopIsPolyline(loop, hcData); - isCurve2d = false; - } - else - { - if (loop.Curves.Count == 2)// 1是不可能的,大于2的是曲线 - { - // 边界是曲线,过滤可能是圆形的情况 - var cir = TwoArcFormOneCircle(loop); - if (cir is not null) - { - hcData.CircleData.Add(cir); - isCurve2d = false; - } - } - } - - // 边界是曲线 - if (isCurve2d) - HatchLoopIsCurve2d(loop, hcData); - - _hcDatas.Add(hcData); - }); - } - #endregion - - #region 方法 - /// - /// 多段线处理 - /// - /// 填充边界 - /// 收集图元信息 - static void HatchLoopIsPolyline(HatchLoop loop, HatchConverterData hcData) - { - if (loop is null) - throw new ArgumentNullException(nameof(loop)); - - if (hcData is null) - throw new ArgumentNullException(nameof(hcData)); - - // 判断为圆形: - // 上下两个圆弧,然后填充,就会生成此种填充 - // 顶点数是3,凸度是半圆,两个半圆就是一个圆形 - if (loop.Polyline.Count == 3 && loop.Polyline[0].Bulge == 1 && loop.Polyline[1].Bulge == 1 || - loop.Polyline.Count == 3 && loop.Polyline[0].Bulge == -1 && loop.Polyline[1].Bulge == -1) - { - hcData.CircleData.Add(new CircleData(loop.Polyline[0].Vertex, loop.Polyline[1].Vertex)); - } - else - { - // 遍历多段线信息 - var bvc = loop.Polyline; - for (int i = 0; i < bvc.Count; i++) - hcData.PolyLineData.Add(new BulgeVertexWidth(bvc[i])); - } - } - - /// - /// 两个圆弧组成圆形 - /// - /// - /// - static CircleData? TwoArcFormOneCircle(HatchLoop loop) - { - if (loop is null) - throw new ArgumentNullException(nameof(loop)); - - if (loop.Curves.Count != 2) - throw new ArgumentException( - "边界非多段线,而且点数!=2,点数为:" + nameof(loop.Curves.Count) + ";两个矩形交集的时候会出现此情况."); - - CircleData? circular = null; - - // 判断为圆形: - // 用一条(不是两条)多段线画出两条圆弧为正圆,就会生成此种填充 - // 边界为曲线,数量为2,可能是两个半圆曲线,如果是,就加入圆形数据中 - - // 第一段 - var getCurves1Pts = loop.Curves[0].GetSamplePoints(3); // 曲线取样点分两份(3点) - var mid1Pt = getCurves1Pts[1]; // 腰点 - double bulge1 = loop.Curves[0].StartPoint.GetArcBulge(mid1Pt, loop.Curves[0].EndPoint); - - // 第二段 - var getCurves2Pts = loop.Curves[1].GetSamplePoints(3); - var mid2Pt = getCurves2Pts[1]; - double bulge2 = loop.Curves[1].StartPoint.GetArcBulge(mid2Pt, loop.Curves[1].EndPoint); - - // 第一段上弧&&第二段反弧 || 第一段反弧&&第二段上弧 - if (bulge1 == -1 && bulge2 == -1 || bulge1 == 1 && bulge2 == 1) - circular = new CircleData(loop.Curves[0].StartPoint, loop.Curves[1].StartPoint); // 两个起点就是对称点 - - return circular; - } - - /// - /// 处理边界曲线 - /// - /// 填充边界 - /// 收集图元信息 - static void HatchLoopIsCurve2d(HatchLoop loop, HatchConverterData hcData) - { - // 取每一段曲线,曲线可能是直线来的,但是圆弧会按照顶点来分段 - int curveIsClosed = 0; - - // 遍历边界的多个子段 - foreach (Curve2d curve in loop.Curves) - { - // 计数用于实现闭合 - curveIsClosed++; - if (curve is NurbCurve2d spl) - { - // 判断为样条曲线: - hcData.SplineData.Add(spl); - continue; - } - - var pts = curve.GetSamplePoints(3); - var midPt = pts[1]; - if (curve.StartPoint.IsEqualTo(curve.EndPoint, new Tolerance(1e-6, 1e-6)))// 首尾相同,就是圆形 - { - // 判断为圆形: - // 获取起点,然后采样三点,中间就是对称点(直径点) - hcData.CircleData.Add(new CircleData(curve.StartPoint, midPt)); - continue; - } - - // 判断为多段线,圆弧: - double bulge = curve.StartPoint.GetArcBulge(midPt, curve.EndPoint); - hcData.PolyLineData.Add(new BulgeVertexWidth(curve.StartPoint, bulge)); - - // 末尾点,不闭合的情况下就要获取这个 - if (curveIsClosed == loop.Curves.Count) - hcData.PolyLineData.Add(new BulgeVertexWidth(curve.EndPoint, 0)); - } - } - - /// - /// 创建边界图元 - /// - /// 返回图元 - public void CreateBoundary(List outEnts) - { - for (int i = 0; i < _hcDatas.Count; i++) - { - var data = _hcDatas[i]; - - // 生成边界:多段线 - if (data.PolyLineData.Count > 0) - { - Polyline pl = new(); - pl.SetDatabaseDefaults(); - for (int j = 0; j < data.PolyLineData.Count; j++) - { - pl.AddVertexAt(j, - data.PolyLineData[j].Vertex, - data.PolyLineData[j].Bulge, - data.PolyLineData[j].StartWidth, - data.PolyLineData[j].EndWidth); - } - outEnts.Add(pl); - } - - // 生成边界:圆 - data.CircleData.ForEach(item => { - outEnts.Add(new Circle(item.Center.Point3d(), Vector3d.ZAxis, item.Radius)); - }); - - // 生成边界:样条曲线 - data.SplineData.ForEach(item => { - outEnts.Add(item.ToCurve()); - }); - } - - if (_oldHatch is not null) - { - outEnts.ForEach(ent => { - ent.Color = _oldHatch.Color; - ent.Layer = _oldHatch.Layer; - }); - } - } - - - /// - /// 创建边界图元和新填充到当前空间 - /// - /// - /// 边界关联 - /// 是否创建填充,false则只创建边界 - /// 新填充id,边界在获取 - public ObjectId CreateBoundarysAndHatchToMsPs(BlockTableRecord btrOfAddEntitySpace, - bool boundaryAssociative = true, - bool createHatchFlag = true, - Transaction? trans = null) - { - List boEnts = new(); - CreateBoundary(boEnts); - boEnts.ForEach(ent => { - BoundaryIds.Add(btrOfAddEntitySpace.AddEntity(ent)); - }); - - if (!createHatchFlag) - return ObjectId.Null; - /* - * 此处为什么要克隆填充,而不是新建填充? - * 因为填充如果是新建的,那么将会丢失基点,概念如下: - * 两个一样的填充,平移其中一个,那么再提取他们的基点会是一样的! - * 所以生成时候就不等同于画面相同. - * 也因为我不知道什么新建方式可以新建一模一样的填充,因此使用了克隆 - * 那么它的平移后的基点在哪里呢? - */ - - using ObjectIdCollection idc = new(new ObjectId[] { OldHatchId }); - using IdMapping map = new(); - btrOfAddEntitySpace.DeepCloneEx(idc, map); - var newHatchId = map.GetValues()[0]; - trans ??= DBTrans.Top.Transaction; - - bool openErased = false; - bool openLockedLayer = false; - var hatchEnt = trans.GetObject(newHatchId, OpenMode.ForWrite, - openErased, openLockedLayer) as Hatch; - if (hatchEnt != null) - { - ResetBoundary(hatchEnt, boundaryAssociative); - hatchEnt.DowngradeOpen(); - } - return newHatchId; - } - - /// - /// 重设边界 - /// - /// - /// 边界关联 - void ResetBoundary(Hatch hatch, bool boundaryAssociative = true) - { - if (BoundaryIds.Count == 0) - return; - - // todo ------ acad08分离填充报错: Microsoft Visual Studio C 运行库在 acad.exe 中检测到一个错误 - // 0x01 测试命令 CmdTest_CreateHatch 创建是可以分离的, - // 那么可能是 克隆后 修改导致的, - // 我是克隆了之后移除原有边界,为了一些xdata之类的 - // 0x02 测试了 hatch.SetDatabaseDefaults(); 并不是因为这个 - // 0x03 测试了 v1110 不移除原有边界,而是加入了之后再移除旧的边界,也是一样 - // 要处理这个问题,我想:自己实现一个分离填充,不用cad自带的,然后单独填充每个. - // 填充边界的算法是扫描线算法.这样就可以绕过去了...发现过于麻烦,放弃... - - // v1110 删除原有边界 - while (hatch.NumberOfLoops != 0) - hatch.RemoveLoopAt(0); - - hatch.Associative = boundaryAssociative; - - using ObjectIdCollection obIds = new(); - for (int i = 0; i < BoundaryIds.Count; i++) - { - obIds.Clear(); - obIds.Add(BoundaryIds[i]); - // 要先添加最外面的边界 - if (i == 0) - hatch.AppendLoop(HatchLoopTypes.Outermost, obIds); - else - hatch.AppendLoop(HatchLoopTypes.Default, obIds); - } - // 计算填充并显示 - hatch.EvaluateHatch(true); - } - #endregion -} \ No newline at end of file diff --git "a/src/IFoxCAD.Cad.Shared/ExtensionMethod/\346\226\260\345\273\272\345\241\253\345\205\205/HatchEx.cs" "b/src/IFoxCAD.Cad.Shared/ExtensionMethod/\346\226\260\345\273\272\345\241\253\345\205\205/HatchEx.cs" deleted file mode 100644 index 8bfd043..0000000 --- "a/src/IFoxCAD.Cad.Shared/ExtensionMethod/\346\226\260\345\273\272\345\241\253\345\205\205/HatchEx.cs" +++ /dev/null @@ -1,15 +0,0 @@ -namespace IFoxCAD.Cad; - -public static class HatchEx -{ - /// - /// 遍历填充每条边 - /// - /// - /// - public static void ForEach(this Hatch hatch, Action action) - { - for (int i = 0; i < hatch.NumberOfLoops; i++) - action.Invoke(hatch.GetLoopAt(i)); - } -} \ No newline at end of file diff --git "a/src/IFoxCAD.Cad.Shared/ExtensionMethod/\346\226\260\345\273\272\345\241\253\345\205\205/HatchInfo.cs" "b/src/IFoxCAD.Cad.Shared/ExtensionMethod/\346\226\260\345\273\272\345\241\253\345\205\205/HatchInfo.cs" deleted file mode 100644 index 9f4e90b..0000000 --- "a/src/IFoxCAD.Cad.Shared/ExtensionMethod/\346\226\260\345\273\272\345\241\253\345\205\205/HatchInfo.cs" +++ /dev/null @@ -1,353 +0,0 @@ -namespace IFoxCAD.Cad; - -/* - * 添加的第一个边界必须是外边界,即用于定义图案填充最外面的边界。 - * 要添加外部边界,请使用添加环的类型为 HatchLoopTypes.Outermost 常量的 AppendLoop 方法, - * 一旦外边界被定义,就可以继续添加另外的边界。 - * 添加内部边界请使用带 HatchLoopTypes.Default 常量的 AppendLoop 方法。 - * - * 多个外边界的时候,添加的是(外边界,外边界,外边界,普通边界....) - * 多个外边界的时候,添加的是(外边界,普通边界.....外边界,普通边界....) - */ - -/// -/// 图案填充 -/// -public class HatchInfo -{ - #region 成员 - /// - /// 边界id(最外面放第一) - /// - readonly List _boundaryIds; - /// - /// 填充图元 - /// - readonly Hatch _hatch; - /// - /// 边界关联(此处不能直接=>给填充成员,因为它会加入反应器) - /// - readonly bool _boundaryAssociative; - /// - /// 填充的名称:用户定义(固定名称)/渐变/填充依据定义文件 - /// - string? _hatchName; - /// - /// 填充模式类型(预定义/用户定义/自定义) - /// - HatchPatternType _patternTypeHatch; - /// - /// 渐变模式类型 - /// - GradientPatternType _patternTypeGradient; - /// - /// 比例/间距 - /// - double Scale => _hatch.PatternScale; - /// - /// 角度 - /// - double Angle => _hatch.PatternAngle; - #endregion - - #region 构造 - HatchInfo() - { - _hatch = new Hatch(); - _hatch.SetDatabaseDefaults(); - _boundaryIds = new(); - } - - /// - /// 图案填充 - /// - /// 关联边界 - /// 填充原点 - /// 比例 - /// 角度 - public HatchInfo(bool boundaryAssociative = true, - Point2d? hatchOrigin = null, - double hatchScale = 1, - double hatchAngle = 0) : this() - { - if (hatchScale <= 0) - throw new ArgumentException("填充比例不允许小于等于0"); - - _hatch.PatternScale = hatchScale;// 填充比例 - _hatch.PatternAngle = hatchAngle;// 填充角度 - _boundaryAssociative = boundaryAssociative; - - hatchOrigin ??= Point2d.Origin; - _hatch.Origin = hatchOrigin.Value; // 填充原点 - } - - /// - /// 图案填充 - /// - /// 边界 - /// 关联边界 - /// 填充原点 - /// 比例 - /// 角度 - public HatchInfo(IEnumerable boundaryIds, - bool boundaryAssociative = true, - Point2d? hatchOrigin = null, - double hatchScale = 1, - double hatchAngle = 0) - : this(boundaryAssociative, hatchOrigin, hatchScale, hatchAngle) - { - _boundaryIds.AddRange(boundaryIds); - } - - #endregion - - #region 方法 - /// - /// 模式1:预定义 - /// - public HatchInfo Mode1PreDefined(string name) - { - _hatchName = name; - _hatch.HatchObjectType = HatchObjectType.HatchObject; // 对象类型(填充/渐变) - _patternTypeHatch = HatchPatternType.PreDefined; - return this; - } - - /// - /// 模式2:用户定义 - /// - /// 是否双向 - public HatchInfo Mode2UserDefined(bool patternDouble = true) - { - _hatchName = "_USER"; - _hatch.HatchObjectType = HatchObjectType.HatchObject; // 对象类型(填充/渐变) - _patternTypeHatch = HatchPatternType.UserDefined; - - _hatch.PatternDouble = patternDouble; // 是否双向(必须写在 SetHatchPattern 之前) - _hatch.PatternSpace = Scale; // 间距(必须写在 SetHatchPattern 之前) - return this; - } - - /// - /// 模式3:自定义 - /// - /// - public HatchInfo Mode3UserDefined(string name) - { - _hatchName = name; - _hatch.HatchObjectType = HatchObjectType.HatchObject; // 对象类型(填充/渐变) - _patternTypeHatch = HatchPatternType.CustomDefined; - return this; - } - - /// - /// 模式4:渐变填充 - /// - /// 渐变填充名称 - /// 渐变色起始颜色 - /// 渐变色结束颜色 - /// 渐变移动 - /// 色调值 - /// 单色双色 - public HatchInfo Mode4Gradient(GradientName name, Color colorStart, Color colorEnd, - float gradientShift = 0, - float shadeTintValue = 0, - bool gradientOneColorMode = false) - { - // entget渐变的名字必然是"SOLID",但是这里作为"渐变"名,而不是"填充"名 - _hatchName = name.ToString(); - _hatch.HatchObjectType = HatchObjectType.GradientObject; // 对象类型(填充/渐变) - _patternTypeGradient = GradientPatternType.PreDefinedGradient;// 模式4:渐变 - // _patternTypeGradient = GradientPatternType.UserDefinedGradient;// 模式5:渐变..这种模式干啥用呢 - - // 设置渐变色填充的起始和结束颜色 - var gColor1 = new GradientColor(colorStart, 0); - var gColor2 = new GradientColor(colorEnd, 1); - _hatch.SetGradientColors(new GradientColor[] { gColor1, gColor2 }); - - _hatch.GradientShift = gradientShift; // 梯度位移 - _hatch.ShadeTintValue = shadeTintValue; // 阴影色值 - _hatch.GradientOneColorMode = gradientOneColorMode;// 渐变单色/双色 - _hatch.GradientAngle = Angle; // 渐变角度 - - return this; - } - - /// - /// 构建 - /// - /// 将填充加入此空间 - public ObjectId Build(BlockTableRecord btrOfAddEntitySpace) - { - // 加入数据库 - var hatchId = btrOfAddEntitySpace.AddEntity(_hatch); - - // 设置模式:渐变/填充 - if (_hatch.HatchObjectType == HatchObjectType.GradientObject) - _hatch.SetGradient(_patternTypeGradient, _hatchName); - else - _hatch.SetHatchPattern(_patternTypeHatch, _hatchName); - - // 关联边界,如果不先添加数据库空间内就会出错 - // 为 true 会加入反应器,因此比较慢(二维码将会十几秒才生成好),视需求而定. - _hatch.Associative = _boundaryAssociative; - - // 利用 AppendLoop 重载加入,这里就不处理 - if (_boundaryIds.Count > 0) - AppendLoop(_boundaryIds, HatchLoopTypes.Default); - - // 计算填充并显示(若边界出错,这句会异常) - _hatch.EvaluateHatch(true); - - return hatchId; - } - - /// - /// 执行图元的属性修改 - /// - /// 扔出填充实体 - public HatchInfo Action(Action action) - { - action(_hatch); - return this; - } - - /// - /// 清空边界集合 - /// - public HatchInfo ClearBoundary() - { - _boundaryIds.Clear(); - return this; - } - - /// - /// 删除边界图元 - /// - public HatchInfo EraseBoundary() - { - for (int i = 0; i < _boundaryIds.Count; i++) - _boundaryIds[i].Erase(); - return this; - } - - /// - /// 加入边界 - /// - /// 边界id - /// 加入方式 - void AppendLoop(IEnumerable boundaryIds, - HatchLoopTypes hatchLoopTypes = HatchLoopTypes.Default) - { - using ObjectIdCollection obIds = new(); - // 边界是闭合的,而且已经加入数据库 - // 填充闭合环类型.最外面 - foreach (var border in boundaryIds) - { - obIds.Clear(); - obIds.Add(border); - _hatch.AppendLoop(hatchLoopTypes, obIds); - } - } - - /// - /// 加入边界(仿高版本的填充函数) - /// - /// 点集 - /// 凸度集 - /// 加入此空间 - /// 加入方式 - /// - public HatchInfo AppendLoop(Point2dCollection pts, - DoubleCollection bluges, - BlockTableRecord btrOfAddEntitySpace, - HatchLoopTypes hatchLoopTypes = HatchLoopTypes.Default) - { - if (pts == null) - throw new ArgumentNullException(nameof(pts)); - - pts.End2End(); -#if NET35 - _boundaryIds.Add(CreateAddBoundary(pts, bluges, btrOfAddEntitySpace)); -#else - // 2011新增API,可以不生成图元的情况下加入边界, - // 通过这里进入的话,边界 _boundaryIds 是空的,那么 Build() 时候就需要过滤空的 - _hatch.AppendLoop(hatchLoopTypes, pts, bluges); -#endif - return this; - } - -#if NET35 - /// - /// 通过点集和凸度生成边界的多段线 - /// - /// 点集 - /// 凸度集 - /// 加入此空间 - /// 多段线id - static ObjectId CreateAddBoundary(Point2dCollection? pts, - DoubleCollection? bluges, - BlockTableRecord btrOfAddEntitySpace) - { - if (pts is null) - throw new ArgumentException(null, nameof(pts)); - if (bluges is null) - throw new ArgumentException(null, nameof(bluges)); - - var bvws = new List(); - - var itor1 = pts.GetEnumerator(); - var itor2 = bluges.GetEnumerator(); - while (itor1.MoveNext() && itor2.MoveNext()) - bvws.Add(new BulgeVertexWidth(itor1.Current, itor2.Current)); - - return btrOfAddEntitySpace.AddPline(bvws); - } -#endif - #endregion - - #region 枚举 - /// - /// 渐变色填充的图案名称 - /// - public enum GradientName - { - /// - /// 线状渐变 - /// - Linear, - /// - /// 圆柱状渐变 - /// - Cylinder, - /// - /// 反圆柱状渐变 - /// - Invcylinder, - /// - /// 球状渐变 - /// - Spherical, - /// - /// 反球状渐变 - /// - Invspherical, - /// - /// 半球状渐变 - /// - Hemisperical, - /// - /// 反半球状渐变 - /// - InvHemisperical, - /// - /// 抛物面状渐变 - /// - Curved, - /// - /// 反抛物面状渐变 - /// - Incurved - } - #endregion -} \ No newline at end of file diff --git "a/src/IFoxCAD.Cad.Shared/ExtensionMethod/\346\226\260\345\273\272\346\226\207\345\255\227/AttachmentPointHelper.cs" "b/src/IFoxCAD.Cad.Shared/ExtensionMethod/\346\226\260\345\273\272\346\226\207\345\255\227/AttachmentPointHelper.cs" deleted file mode 100644 index 975e8a7..0000000 --- "a/src/IFoxCAD.Cad.Shared/ExtensionMethod/\346\226\260\345\273\272\346\226\207\345\255\227/AttachmentPointHelper.cs" +++ /dev/null @@ -1,95 +0,0 @@ -namespace IFoxCAD.Cad; - -public static class AttachmentPointHelper -{ - static readonly Dictionary _alignment = new() - { - { "左上", AttachmentPoint.TopLeft }, - { "中上", AttachmentPoint.TopCenter },// 单行的对齐 - { "右上", AttachmentPoint.TopRight }, - - { "左中", AttachmentPoint.MiddleLeft }, - { "正中", AttachmentPoint.MiddleCenter },// 多行的正中 - { "右中", AttachmentPoint.MiddleRight }, - - { "左对齐", AttachmentPoint.BaseLeft },// ※优先(放在前面优先获取) - { "左", AttachmentPoint.BaseLeft }, - - { "中间", AttachmentPoint.BaseMid }, - - { "右对齐", AttachmentPoint.BaseRight },// ※优先(放在前面优先获取) - { "右", AttachmentPoint.BaseRight }, - - { "左下", AttachmentPoint.BottomLeft }, - { "中下", AttachmentPoint.BottomCenter }, - { "右下", AttachmentPoint.BottomRight }, - - { "对齐", AttachmentPoint.BaseAlign },// ※优先(放在前面优先获取) - { "调整", AttachmentPoint.BaseAlign }, - - { "居中", AttachmentPoint.BaseCenter },// 单行的中 - { "铺满", AttachmentPoint.BaseFit }, - }; - - /// - /// 输入文字获得对齐方式 - /// - /// - /// - public static AttachmentPoint Get(string key) - { - return _alignment[key]; - } - - /// - /// 输入对齐方式获得文字 - /// - /// - /// - public static string Get(AttachmentPoint value) - { - return _alignment.FirstOrDefault(q => q.Value == value).Key; - } -} - -#if false -// 反射描述 -// 这些东西cad没有用到啊...所以不纳入了 -public enum AttachmentPoint2 -{ - [Description("下对齐")] - BottomAlign = 14, - [Description("中对齐")] - MiddleAlign = 15,// 0xF - [Description("上对齐")] - TopAlign = 16,// 0x10 - [Description("下铺满")] - BottomFit = 18, - [Description("中铺满")] - MiddleFit = 19, - [Description("上铺满")] - TopFit = 20, - [Description("下居中")] - BottomMid = 22, - [Description("中居中")] - MiddleMid = 23, - [Description("下居中")] - TopMid = 24, -} - -public static Dictionary GetEnumDic(Type enumType) -{ - Dictionary dic = new(); - var fieldinfos = enumType.GetFields(); - for (int i = 0; i < fieldinfos.Length; i++) - { - var field = fieldinfos[i]; - if (field.FieldType.IsEnum) - { - var objs = field.GetCustomAttributes(typeof(DescriptionAttribute), false); - dic.Add(field.Name, ((DescriptionAttribute)objs[0]).Description); - } - } - return dic; -} -#endif \ No newline at end of file diff --git "a/src/IFoxCAD.Cad.Shared/ExtensionMethod/\346\226\260\345\273\272\346\226\207\345\255\227/TextEntityAdd.cs" "b/src/IFoxCAD.Cad.Shared/ExtensionMethod/\346\226\260\345\273\272\346\226\207\345\255\227/TextEntityAdd.cs" deleted file mode 100644 index 5b37a31..0000000 --- "a/src/IFoxCAD.Cad.Shared/ExtensionMethod/\346\226\260\345\273\272\346\226\207\345\255\227/TextEntityAdd.cs" +++ /dev/null @@ -1,62 +0,0 @@ -namespace IFoxCAD.Cad; - -public static partial class EntityAdd -{ - /// - /// 创建单行文字 - /// - /// 数据库 - /// 内容 - /// 插入点 - /// 字体高度 - /// 文字样式 - /// 对齐方式 - /// 对齐点,因样式 可能无效 - /// - public static Entity AddDBTextToEntity(this Database db, - string textContents, - Point3d position, - double textHigh = 2.5, - ObjectId? textStyleId = null, - AttachmentPoint justify = AttachmentPoint.BaseLeft, - Point3d? justifyPoint = null) - { - var TextInfo = new TextInfo( - textContents, - position, - justify, - justifyPoint, - textStyleId, - textHigh, - db); - return TextInfo.AddDBTextToEntity(); - } - - /// - /// 新建多行文字 - /// - /// 数据库 - /// 内容 - /// 插入点 - /// 字体高度 - /// 文字样式 - /// 对齐方式 - /// - public static Entity AddMTextToEntity(this Database db, - string textContents, - Point3d position, - double textHigh = 2.5, - ObjectId? textStyleId = null, - AttachmentPoint justify = AttachmentPoint.BaseLeft) - { - var TextInfo = new TextInfo( - textContents, - position, - justify, - null, - textStyleId, - textHigh, - db); - return TextInfo.AddMTextToEntity(); - } -} diff --git "a/src/IFoxCAD.Cad.Shared/ExtensionMethod/\346\226\260\345\273\272\346\226\207\345\255\227/TextInfo.cs" "b/src/IFoxCAD.Cad.Shared/ExtensionMethod/\346\226\260\345\273\272\346\226\207\345\255\227/TextInfo.cs" deleted file mode 100644 index d8bfd7a..0000000 --- "a/src/IFoxCAD.Cad.Shared/ExtensionMethod/\346\226\260\345\273\272\346\226\207\345\255\227/TextInfo.cs" +++ /dev/null @@ -1,178 +0,0 @@ -namespace IFoxCAD.Cad; - -/// -/// 文字信息类 -/// -public class TextInfo -{ - readonly Database? Database; - readonly string? Contents; - readonly Point3d Position; - - public string TextJustifyCn => AttachmentPointHelper.Get(TextJustify); - readonly AttachmentPoint TextJustify; - readonly Point3d? AlignmentPoint; - - readonly double TextHeight; - readonly ObjectId? TextStyleId; - - /// - /// 文字信息类 - /// - /// 内容 - /// 基点 - /// 对齐方式 - /// 对齐点(对齐方式是左,此参数无效,为null不为左就报错) - /// 文字样式id - /// 文字高度 - /// 数据库 - public TextInfo(string? contents, - Point3d position, - AttachmentPoint justify, - Point3d? justifyPoint = null, - ObjectId? textStyleId = null, - double textHeight = 2.5, - Database? database = null) - { - Contents = contents; - Position = position; - TextJustify = justify; - - if (justifyPoint is null && TextJustify != AttachmentPoint.BaseLeft) - throw new ArgumentNullException(nameof(justifyPoint)); - - AlignmentPoint = justifyPoint; - TextHeight = textHeight; - TextStyleId = textStyleId; - Database = database; - } - - /// - /// 创建单行文字 - /// - public DBText AddDBTextToEntity() - { - if (string.IsNullOrEmpty(Contents)) - throw new ArgumentNullException(nameof(Contents) + "创建文字无内容"); - - var acText = new DBText(); - acText.SetDatabaseDefaults(); - - if (Database is not null) - acText.SetDatabaseDefaults(Database);// 我的默认值是填满的,所以可以不需要 - - if (TextStyleId is not null) - acText.SetTextStyleId(TextStyleId.Value); - - acText.Height = TextHeight; // 高度 - acText.TextString = Contents; // 内容 - acText.Position = Position; // 插入点(一定要先设置) - acText.Justify = TextJustify; // 使他们对齐 - // acText.HorizontalMode - - if (AlignmentPoint is not null) - acText.AlignmentPoint = AlignmentPoint.Value; - else if (acText.Justify != AttachmentPoint.BaseLeft) - acText.AlignmentPoint = Position; - - if (Database is not null) - acText.AdjustAlignment(Database); - return acText; - } - - /// - /// 创建多行文字 - /// - /// - public MText AddMTextToEntity() - { - if (string.IsNullOrEmpty(Contents)) - throw new ArgumentNullException(nameof(Contents) + "创建文字无内容"); - - var mText = new MText(); - mText.SetDatabaseDefaults(); - - if (Database is not null) - mText.SetDatabaseDefaults(Database); - - if (TextStyleId is not null) - mText.SetTextStyleId(TextStyleId.Value); - - mText.TextHeight = TextHeight; // 高度 - mText.Contents = Contents; // 内容 - mText.Location = Position; // 插入点(一定要先设置) - - // mText.SetAttachmentMovingLocation(TextJustify); - mText.Attachment = TextJustify;// 使他们对齐 - - return mText; - } -} - -// 反射设定对象的文字样式id -public static partial class TextInfoHelper -{ - /// - /// 设置文字样式id - /// - /// 单行文字 - /// 文字样式表记录id - public static void SetTextStyleId(this DBText acText, ObjectId ltrObjectId) - { - SetEntityTxtStyleId(acText, ltrObjectId); - } - - /// - /// 设置文字样式id - /// - /// 多行文字 - /// 文字样式表记录id - public static void SetTextStyleId(this MText acText, ObjectId ltrObjectId) - { - SetEntityTxtStyleId(acText, ltrObjectId); - } - - static void SetEntityTxtStyleId(Entity acText, ObjectId ltrObjectId) - { - GetTextStyleIdType(acText)?.SetValue(acText, ltrObjectId, null); - } - - /// - /// 获取文字样式id - /// - public static ObjectId GetTextStyleId(this DBText acText) - { - return GetEntityTxtStyleId(acText); - } - - /// - /// 获取文字样式id - /// - public static ObjectId GetTextStyleId(this MText acText) - { - return GetEntityTxtStyleId(acText); - } - - static ObjectId GetEntityTxtStyleId(Entity acText) - { - var result = ObjectId.Null; - var id = GetTextStyleIdType(acText)?.GetValue(acText, null); - if (id != null) - result = (ObjectId)id; - return result; - } - - static PropertyInfo? _textStyleId = null; - static PropertyInfo GetTextStyleIdType(Entity acText) - { - if (_textStyleId == null) - { - var entType = acText.GetType(); - var prs = entType.GetProperties(); - _textStyleId = prs.FirstOrDefault(a => a.Name == "TextStyle");// 反射获取属性 - if (_textStyleId == null) - _textStyleId = prs.FirstOrDefault(a => a.Name == "TextStyleId");// 反射获取属性 - } - return _textStyleId; - } -} diff --git a/src/IFoxCAD.Cad.Shared/IFoxCAD.Cad.Shared.projitems b/src/IFoxCAD.Cad.Shared/IFoxCAD.Cad.Shared.projitems deleted file mode 100644 index 3493040..0000000 --- a/src/IFoxCAD.Cad.Shared/IFoxCAD.Cad.Shared.projitems +++ /dev/null @@ -1,90 +0,0 @@ - - - - $(MSBuildAllProjects);$(MSBuildThisFileFullPath) - true - 82fb8450-b971-4e30-859f-4b2ddb81f590 - - - IFoxCAD.Cad.Shared - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/src/IFoxCAD.Cad.Shared/IFoxCAD.Cad.Shared.shproj b/src/IFoxCAD.Cad.Shared/IFoxCAD.Cad.Shared.shproj deleted file mode 100644 index 75a3d5e..0000000 --- a/src/IFoxCAD.Cad.Shared/IFoxCAD.Cad.Shared.shproj +++ /dev/null @@ -1,13 +0,0 @@ - - - - 82fb8450-b971-4e30-859f-4b2ddb81f590 - 14.0 - - - - - - - - diff --git a/src/IFoxCAD.Cad.Shared/ResultData/LispDottedPair.cs b/src/IFoxCAD.Cad.Shared/ResultData/LispDottedPair.cs deleted file mode 100644 index 009d13e..0000000 --- a/src/IFoxCAD.Cad.Shared/ResultData/LispDottedPair.cs +++ /dev/null @@ -1,68 +0,0 @@ -namespace IFoxCAD.Cad; - -/// -/// lisp点对表的数据封装类 -/// -public class LispDottedPair : LispList -{ - #region 构造函数 - /// - /// 默认无参构造函数 - /// - public LispDottedPair() - { - } - - /// - /// 构造函数 - /// - /// TypedValue 迭代器 - public LispDottedPair(IEnumerable values) : base(values) - { - } - /// - /// 构造函数 - /// - /// 点对表左数 - /// 点对表右数 - public LispDottedPair(TypedValue left, TypedValue right) - { - Add(left); - Add(right); - } - #endregion - - #region 重写 - /// - /// 点对表的值 - /// - public override List Value - { - get - { - var value = new List - { - new TypedValue((int)LispDataType.ListBegin,-1), - new TypedValue((int)LispDataType.DottedPair,-1) - }; - value.InsertRange(1, this); - return value; - } - } - #endregion - - #region 转换器 - - /// - /// LispDottedPair 隐式转换到 TypedValue 数组 - /// - /// TypedValueList 实例 - public static implicit operator TypedValue[](LispDottedPair values) => values.Value.ToArray(); - /// - /// LispDottedPair 隐式转换到 ResultBuffer - /// - /// TypedValueList 实例 - public static implicit operator ResultBuffer(LispDottedPair values) => new(values.Value.ToArray()); - - #endregion -} diff --git a/src/IFoxCAD.Cad.Shared/ResultData/LispList.cs b/src/IFoxCAD.Cad.Shared/ResultData/LispList.cs deleted file mode 100644 index c186e43..0000000 --- a/src/IFoxCAD.Cad.Shared/ResultData/LispList.cs +++ /dev/null @@ -1,194 +0,0 @@ -namespace IFoxCAD.Cad; - -/// -/// lisp数据封装类 -/// -public class LispList : TypedValueList -{ - #region 构造函数 - /// - /// 默认构造函数 - /// - public LispList() { } - - /// - /// 构造函数 - /// - /// TypedValue 迭代器 - public LispList(IEnumerable values) : base(values) { } - #endregion - - #region 重写 - /// - /// lisp 列表的值 - /// - public virtual List Value - { - get - { - var value = new List - { - new TypedValue((int)LispDataType.ListBegin,-1), - new TypedValue((int)LispDataType.ListEnd,-1) - }; - value.InsertRange(1, this); - return value; - } - } - #endregion - - #region 添加数据 - /// - /// 添加数据 - /// - /// 组码 - /// 组码值 - public override void Add(int code, object? obj) - { - if (code < 5000) - throw new System.Exception("传入的组码值不是 lisp数据 有效范围!"); - - Add(new TypedValue(code, obj)); - } - - /// - /// 添加数据 - /// - /// dxfcode枚举值 - /// 组码值 - public void Add(LispDataType code, object? obj) - { - Add((int)code, obj); - } - /// - /// 添加数据,参数为true时添加 lisp 中的 T,false时添加 lisp 中的 nil - /// - /// bool 型的数据 - public void Add(bool value) - { - if (value) - { - Add(LispDataType.T_atom, true); - } - else - { - Add(LispDataType.Nil, null); - } - } - /// - /// 添加字符串 - /// - /// 字符串 - public void Add(string value) - { - Add(LispDataType.Text, value); - } - /// - /// 添加短整型数 - /// - /// 短整型数 - public void Add(short value) - { - Add(LispDataType.Int16, value); - } - /// - /// 添加整型数 - /// - /// 整型数 - public void Add(int value) - { - Add(LispDataType.Int32, value); - } - /// - /// 添加浮点数 - /// - /// 浮点数 - public void Add(double value) - { - Add(LispDataType.Double, value); - } - /// - /// 添加对象id - /// - /// 对象id - public void Add(ObjectId value) - { - Add(LispDataType.ObjectId, value); - } - /// - /// 添加选择集 - /// - /// 选择集 - public void Add(SelectionSet value) - { - Add(LispDataType.SelectionSet, value); - } - /// - /// 添加二维点 - /// - /// 二维点 - public void Add(Point2d value) - { - Add(LispDataType.Point2d, value); - } - /// - /// 添加三维点 - /// - /// 三维点 - public void Add(Point3d value) - { - Add(LispDataType.Point3d, value); - } - /// - /// 添加二维点 - /// - /// X - /// Y - public void Add(double x, double y) - { - Add(LispDataType.Point2d, new Point2d(x, y)); - } - /// - /// 添加三维点 - /// - /// X - /// Y - /// Z - public void Add(double x, double y, double z) - { - Add(LispDataType.Point3d, new Point3d(x, y, z)); - } - /// - /// 添加列表 - /// - /// lisp 列表 - public void Add(LispList value) - { - this.AddRange(value.Value); - } - - #endregion - - #region 转换器 - /// - /// ResultBuffer 隐式转换到 LispList - /// - /// ResultBuffer 实例 - public static implicit operator LispList(ResultBuffer buffer) => new(buffer.AsArray()); - /// - /// LispList 隐式转换到 TypedValue 数组 - /// - /// TypedValueList 实例 - public static implicit operator TypedValue[](LispList values) => values.Value.ToArray(); - /// - /// LispList 隐式转换到 ResultBuffer - /// - /// TypedValueList 实例,要using - public static implicit operator ResultBuffer(LispList values) => new(values.Value.ToArray()); - /// - /// TypedValue 数组隐式转换到 LispList - /// - /// TypedValue 数组 - public static implicit operator LispList(TypedValue[] values) => new(values); - #endregion -} \ No newline at end of file diff --git a/src/IFoxCAD.Cad.Shared/ResultData/TypedValueList.cs b/src/IFoxCAD.Cad.Shared/ResultData/TypedValueList.cs deleted file mode 100644 index 16970d2..0000000 --- a/src/IFoxCAD.Cad.Shared/ResultData/TypedValueList.cs +++ /dev/null @@ -1,64 +0,0 @@ -namespace IFoxCAD.Cad; - -/// -/// 用于集中管理扩展数据/扩展字典/resultbuffer的类 -/// -public class TypedValueList : List -{ - #region 构造函数 - /// - /// 默认无参构造函数 - /// - public TypedValueList() { } - /// - /// 采用 TypedValue 迭代器构造 TypedValueList - /// - /// - public TypedValueList(IEnumerable values) : base(values) { } - #endregion - - #region 添加数据 - /// - /// 添加数据 - /// - /// 组码 - /// 组码值 - public virtual void Add(int code, object obj) - { - Add(new TypedValue(code, obj)); - } - - #endregion - - #region 转换器 - /// - /// ResultBuffer 隐式转换到 TypedValueList - /// - /// ResultBuffer 实例 - public static implicit operator TypedValueList(ResultBuffer buffer) => new(buffer.AsArray()); - /// - /// TypedValueList 隐式转换到 TypedValue 数组 - /// - /// TypedValueList 实例 - public static implicit operator TypedValue[](TypedValueList values) => values.ToArray(); - /// - /// TypedValueList 隐式转换到 ResultBuffer - /// - /// TypedValueList 实例 - public static implicit operator ResultBuffer(TypedValueList values) => new(values); - /// - /// TypedValue 数组隐式转换到 TypedValueList - /// - /// TypedValue 数组 - public static implicit operator TypedValueList(TypedValue[] values) => new(values); - /// - /// 转换为字符串 - /// - /// ResultBuffer 字符串 - public override string ToString() - { - using ResultBuffer a = new(this); - return a.ToString(); - } - #endregion -} \ No newline at end of file diff --git a/src/IFoxCAD.Cad.Shared/ResultData/XRecordDataList.cs b/src/IFoxCAD.Cad.Shared/ResultData/XRecordDataList.cs deleted file mode 100644 index 708e155..0000000 --- a/src/IFoxCAD.Cad.Shared/ResultData/XRecordDataList.cs +++ /dev/null @@ -1,67 +0,0 @@ -namespace IFoxCAD.Cad; - -/// -/// 扩展字典数据封装类 -/// -public class XRecordDataList : TypedValueList -{ - #region 构造函数 - /// - /// 扩展字典数据封装类 - /// - public XRecordDataList() { } - - /// - /// 扩展字典数据封装类 - /// - public XRecordDataList(IEnumerable values) : base(values) { } - #endregion - - #region 添加数据 - /// - /// 添加数据 - /// - /// 组码 - /// 组码值 - public override void Add(int code, object obj) - { - if (code >= 1000) - throw new System.Exception("传入的组码值不是 XRecordData 有效范围!"); - - Add(new TypedValue(code, obj)); - } - - /// - /// 添加数据 - /// - /// dxfcode枚举值 - /// 组码值 - public void Add(DxfCode code, object obj) - { - Add((int)code, obj); - } - #endregion - - #region 转换器 - /// - /// ResultBuffer 隐式转换到 XRecordDataList - /// - /// ResultBuffer 实例 - public static implicit operator XRecordDataList(ResultBuffer buffer) => new(buffer.AsArray()); - /// - /// XRecordDataList 隐式转换到 TypedValue 数组 - /// - /// TypedValueList 实例 - public static implicit operator TypedValue[](XRecordDataList values) => values.ToArray(); - /// - /// XRecordDataList 隐式转换到 ResultBuffer - /// - /// TypedValueList 实例s - public static implicit operator ResultBuffer(XRecordDataList values) => new(values); - /// - /// TypedValue 数组隐式转换到 XRecordDataList - /// - /// TypedValue 数组 - public static implicit operator XRecordDataList(TypedValue[] values) => new(values); - #endregion -} \ No newline at end of file diff --git a/src/IFoxCAD.Cad.Shared/ResultData/XdataList.cs b/src/IFoxCAD.Cad.Shared/ResultData/XdataList.cs deleted file mode 100644 index 94f04d7..0000000 --- a/src/IFoxCAD.Cad.Shared/ResultData/XdataList.cs +++ /dev/null @@ -1,146 +0,0 @@ -namespace IFoxCAD.Cad; - -/// -/// 扩展数据封装类 -/// -public class XDataList : TypedValueList -{ - #region 构造函数 - /// - /// 扩展数据封装类 - /// - public XDataList() { } - - /// - /// 扩展数据封装类 - /// - public XDataList(IEnumerable values) : base(values) { } - #endregion - - #region 添加数据 - /// - /// 添加数据 - /// - /// 组码 - /// 组码值 - public override void Add(int code, object obj) - { - if (code < 1000 || code > 1071) - throw new System.Exception("传入的组码值不是 XData 有效范围!"); - - Add(new TypedValue(code, obj)); - } - - /// - /// 添加数据 - /// - /// dxfcode枚举值 - /// 组码值 - public void Add(DxfCode code, object obj) - { - Add((int)code, obj); - } - - /// - /// 是否含有注册名 - /// - /// 注册名 - public bool Contains(string appName) - { - bool result = false; - RangeTask(appName, (tv, state, i) => { - result = true; - state.Break(); - }); - return result; - } - - /// - /// 注册名下含有指定成员 - /// - /// 注册名 - /// 内容 - public bool Contains(string appName, object value) - { - bool result = false; - RangeTask(appName, (tv, state, i) => { - if (tv.Value.Equals(value)) - { - result = true; - state.Break(); - } - }); - return result; - } - - /// - /// 获取appName的索引区间 - /// - /// 注册名称 - /// 任务组码对象 - /// 返回任务组码的索引 - public List GetXdataAppIndex(string appName, DxfCode[] dxfCodes) - { - List indexs = new(); - RangeTask(appName, (tv, state, i) => { - if (dxfCodes.Contains((DxfCode)tv.TypeCode)) - indexs.Add(i); - }); - return indexs; - } - - /// - /// 区间任务 - /// - /// - void RangeTask(string appName, Action action) - { - LoopState state = new(); - // 在名称和名称之间找 - int appNameIndex = -1; - for (int i = 0; i < this.Count; i++) - { - if (this[i].TypeCode == (short)DxfCode.ExtendedDataRegAppName) - { - if (this[i].Value.ToString() == appName) - { - appNameIndex = i; - continue; - } - if (appNameIndex != -1)//找到了下一个名称 - break; - } - if (appNameIndex != -1) // 找下一个的时候,获取任务(移除)的对象 - { - action(this[i], state, i); - if (!state.IsRun) - break; - } - } - } - - #endregion - - #region 转换器 - /// - /// ResultBuffer 隐式转换到 XDataList - /// - /// ResultBuffer 实例 - public static implicit operator XDataList(ResultBuffer buffer) => new(buffer.AsArray()); - /// - /// XDataList 隐式转换到 TypedValue 数组 - /// - /// TypedValueList 实例 - public static implicit operator TypedValue[](XDataList values) => values.ToArray(); - /// - /// XDataList 隐式转换到 ResultBuffer - /// - /// TypedValueList 实例 - public static implicit operator ResultBuffer(XDataList values) => new(values); - /// - /// TypedValue 数组隐式转换到 XDataList - /// - /// TypedValue 数组 - public static implicit operator XDataList(TypedValue[] values) => new(values); - #endregion -} \ No newline at end of file diff --git a/src/IFoxCAD.Cad.Shared/Runtime/AOP.cs b/src/IFoxCAD.Cad.Shared/Runtime/AOP.cs deleted file mode 100644 index 4bb5cb3..0000000 --- a/src/IFoxCAD.Cad.Shared/Runtime/AOP.cs +++ /dev/null @@ -1,99 +0,0 @@ -// namespace IFoxCAD.Cad; -// using HarmonyLib; - -// public class IFoxRefuseInjectionTransaction : Attribute -// { -// /// -// /// 拒绝注入事务 -// /// -// public IFoxRefuseInjectionTransaction() -// { -// } -// } - -// public class AOP -// { -// /// -// /// 在此命名空间下的命令末尾注入清空事务栈函数 -// /// -// public static void Run(string nameSpace) -// { -// Dictionary cmdDic = new(); -// AutoClass.AppDomainGetTypes(type => { -// if (type.Namespace != nameSpace) -// return; -// // 类上面特性 -// if (type.IsClass) -// { -// var attr = type.GetCustomAttributes(true); -// if (RefuseInjectionTransaction(attr)) -// return; -// } - -// // 函数上面特性 -// var mets = type.GetMethods();// 获得它的成员函数 -// for (int ii = 0; ii < mets.Length; ii++) -// { -// var method = mets[ii]; -// // 找到特性,特性下面的方法要是Public,否则就被编译器优化掉了. -// var attr = method.GetCustomAttributes(true); -// for (int jj = 0; jj < attr.Length; jj++) -// if (attr[jj] is CommandMethodAttribute cmdAtt) -// { -// if (!RefuseInjectionTransaction(attr)) -// cmdDic.Add(cmdAtt.GlobalName, (cmdAtt, type, method)); -// } -// } -// }); - -// // 运行的命令写在了Test.dll,当然不是ifox.cad类库内了.... -// if (cmdDic.Count == 0) -// return; - -// var harmony = new Harmony(nameSpace); -// var mPrefix = SymbolExtensions.GetMethodInfo(() => IFoxCmdAddFirst());// 进入函数前 -// var mPostfix = SymbolExtensions.GetMethodInfo(() => IFoxCmdAddLast());// 进入函数后 -// var mp1 = new HarmonyMethod(mPrefix); -// var mp2 = new HarmonyMethod(mPostfix); - -// foreach (var item in cmdDic) -// { -// // 原函数执行(空间type,函数名) -// var mOriginal = AccessTools.Method(item.Value.MetType, item.Value.MetInfo.Name); -// // mOriginal.Invoke(); -// // 新函数执行:创造两个函数加入里面 -// var newMet = harmony.Patch(mOriginal, mp1, mp2); -// // newMet.Invoke(); -// } -// } - -// /// -// /// 拒绝注入事务 -// /// -// /// 属性 -// /// -// private static bool RefuseInjectionTransaction(object[] attr) -// { -// bool refuseInjectionTransaction = false; -// for (int kk = 0; kk < attr.Length; kk++) -// { -// if (attr[kk] is IFoxRefuseInjectionTransaction) -// { -// refuseInjectionTransaction = true; -// break; -// } -// } -// return refuseInjectionTransaction; -// } - -// public static void IFoxCmdAddFirst() -// { -// // 此生命周期会在静态事务栈上面,被无限延长 -// var _ = DBTrans.Top; -// } - -// public static void IFoxCmdAddLast() -// { -// DBTrans.FinishDatabase(); -// } -// } \ No newline at end of file diff --git a/src/IFoxCAD.Cad.Shared/Runtime/AcadEMR.cs b/src/IFoxCAD.Cad.Shared/Runtime/AcadEMR.cs deleted file mode 100644 index 4b8b106..0000000 --- a/src/IFoxCAD.Cad.Shared/Runtime/AcadEMR.cs +++ /dev/null @@ -1,149 +0,0 @@ -#if true -namespace IFoxCAD.Cad; - -using System.Diagnostics; - -// 作者: [VB.net]福萝卜 莱昂纳多·胖子 -// Email:oneeshine@163.com -// QQ: 461884072 -// 测试 2006-2019+ - -/// -/// 去教育版 -/// -/// -internal class AcadEMR -{ - /// - /// 释放库 - /// - /// 句柄 - /// - [DllImport("kernel32.dll", CharSet = CharSet.Auto)] - static extern IntPtr FreeLibrary(IntPtr loadLibraryIntPtr); - - /// - /// 获取一个应用程序或dll的模块句柄,要求已经载入 - /// - /// - /// - [DllImport("kernel32.dll", CharSet = CharSet.Auto, SetLastError = true)] - static extern IntPtr GetModuleHandle(string name); - - /// - /// 获取要引入的函数,将符号名或标识号转换为DLL内部地址 - /// - /// exe/dll句柄 - /// 接口名 - /// - [DllImport("kernel32.dll", SetLastError = true, CharSet = CharSet.Ansi, ExactSpelling = true)] - static extern IntPtr GetProcAddress(IntPtr hModule, string procName); - - /// - /// 虚拟保护 - /// - /// - /// - /// - /// - /// - [DllImport("kernel32.dll", CharSet = CharSet.Auto, SetLastError = true)] - static extern bool VirtualProtect(IntPtr lpAddress, IntPtr dwSize, uint flNewProtect, ref uint lpflOldProtect); - - - /// - /// 移除教育版 - /// - /// 打印出错信息 - public static void Remove(bool echoes = false) - { - var dllName = Env.GetAcapVersionDll(); - IntPtr moduleHandle = GetModuleHandle(dllName); - if (moduleHandle == IntPtr.Zero) - { - if (echoes) - Env.Printl(typeof(AcadEMR).FullName + "." + nameof(Remove) + "找不到模块:" + dllName); - return; - } - - string funcname = System.Text.Encoding.Unicode.GetString(new byte[] { 63 }); - if (IntPtr.Size == 4) - funcname += "isEMR@AcDbDatabase@@QBE_NXZ"; - else - funcname += "isEMR@AcDbDatabase@@QEBA_NXZ"; - - IntPtr funcAdress = GetProcAddress(moduleHandle, funcname); - if (funcAdress == IntPtr.Zero) - { - if (echoes) - Env.Printl("无法找指定函数:" + funcname); - return; - } - - IntPtr ptr; - if (IntPtr.Size == 4) - ptr = new IntPtr(funcAdress.ToInt32() + 3); - else - ptr = new IntPtr(funcAdress.ToInt64() + 4); - - if (!CheckFunc(ref ptr, 51, 2))// 08 通过此处 - if (echoes) - Env.Printl("无法验证函数体:0x33"); - IntPtr destPtr = ptr; - - if (!CheckFunc(ref ptr, 57, 6))// 08 无法通过此处,所以只是打印提示 - if (echoes) - Env.Printl("无法验证函数体:0x39"); - if (!CheckFunc(ref ptr, 15, 2))// 08 无法通过此处,所以只是打印提示 - if (echoes) - Env.Printl("无法验证函数体:0x0F"); - - uint flag = default; - uint tccc = default; - - IntPtr ip100 = new(100); - if (!VirtualProtect(destPtr, ip100, 64, ref flag))// 修改内存权限 - { - if (echoes) - Env.Printl("内存模式修改失败!"); - return; - } - - Marshal.WriteByte(destPtr, 137); - VirtualProtect(destPtr, ip100, flag, ref tccc);// 恢复内存权限 - } - - /// - /// 验证函数体 - /// - /// - /// - /// - /// - static bool CheckFunc(ref IntPtr adress, byte val, int len) - { - if (Marshal.ReadByte(adress) == 233) - { - if (IntPtr.Size == 4) - { - var pass = Marshal.ReadInt32(new IntPtr(adress.ToInt32() + 1)); - adress = new IntPtr(adress.ToInt32() + pass + 5); - } - else - { - var pass = Marshal.ReadInt64(new IntPtr(adress.ToInt64() + 1)); - adress = new IntPtr(adress.ToInt64() + pass + 5); - } - } - if (Marshal.ReadByte(adress) == val) - { - if (IntPtr.Size == 4) - adress = new IntPtr(adress.ToInt32() + len); - else - adress = new IntPtr(adress.ToInt64() + len); - return true; - } - return false; - } -} -#endif \ No newline at end of file diff --git a/src/IFoxCAD.Cad.Shared/Runtime/AcadVersion.cs b/src/IFoxCAD.Cad.Shared/Runtime/AcadVersion.cs deleted file mode 100644 index 61aa620..0000000 --- a/src/IFoxCAD.Cad.Shared/Runtime/AcadVersion.cs +++ /dev/null @@ -1,70 +0,0 @@ -namespace IFoxCAD.Cad; - -/// -/// cad版本号类 -/// -public static class AcadVersion -{ - private static readonly string _pattern = @"Autodesk\\AutoCAD\\R(\d+)\.(\d+)\\.*?"; - - /// - /// 所有安装的cad的版本号 - /// - public static List Versions - { - get - { - string[] copys = Registry.LocalMachine - .OpenSubKey(@"SOFTWARE\Autodesk\Hardcopy") - .GetValueNames(); - - var _versions = new List(); - for (int i = 0; i < copys.Length; i++) - { - if (!Regex.IsMatch(copys[i], _pattern)) - continue; - - var gs = Regex.Match(copys[i], _pattern).Groups; - var ver = new CadVersion - { - ProductRootKey = copys[i], - ProductName = Registry.LocalMachine - .OpenSubKey("SOFTWARE") - .OpenSubKey(copys[i]) - .GetValue("ProductName") - .ToString(), - - Major = int.Parse(gs[1].Value), - Minor = int.Parse(gs[2].Value), - }; - _versions.Add(ver); - } - return _versions; - } - } - - /// 已打开的cad的版本号 - /// 已打开cad的application对象 - /// cad版本号对象 - public static CadVersion? FromApp(object app) - { - if (app == null) - throw new ArgumentNullException(nameof(app)); - - string acver = app.GetType() - .InvokeMember( - "Version", - BindingFlags.GetProperty, - null, - app, - new object[0]).ToString(); - - var gs = Regex.Match(acver, @"(\d+)\.(\d+).*?").Groups; - int major = int.Parse(gs[1].Value); - int minor = int.Parse(gs[2].Value); - for (int i = 0; i < Versions.Count; i++) - if (Versions[i].Major == major && Versions[i].Minor == minor) - return Versions[i]; - return null; - } -} \ No newline at end of file diff --git a/src/IFoxCAD.Cad.Shared/Runtime/AssemInfo.cs b/src/IFoxCAD.Cad.Shared/Runtime/AssemInfo.cs deleted file mode 100644 index 23e5c7d..0000000 --- a/src/IFoxCAD.Cad.Shared/Runtime/AssemInfo.cs +++ /dev/null @@ -1,85 +0,0 @@ -namespace IFoxCAD.Cad; - -/// -/// 程序集信息 -/// -[Serializable] -public struct AssemInfo -{ - /// - /// 注册名 - /// - public string Name; - - /// - /// 程序集全名 - /// - public string Fullname; - - /// - /// 程序集路径 - /// - public string Loader; - - /// - /// 加载方式 - /// - public AssemLoadType LoadType; - - /// - /// 程序集说明 - /// - public string Description; -} - - -/// -/// 程序集加载类型 -/// -public enum AssemLoadType -{ - /// - /// 启动 - /// - Startting = 2, - - /// - /// 随命令 - /// - ByCommand = 12, - - /// - /// 无效 - /// - Disabled = 20 -} - - -/// -/// 注册中心配置信息 -/// -public enum AutoRegConfig -{ - /// - /// 不进行任何操作 - /// - Undefined = 0, - /// - /// 注册表 - /// - Regedit = 1, - /// - /// 反射特性 - /// - ReflectionAttribute = 2, - /// - /// 反射接口 - /// - ReflectionInterface = 4, - /// - /// 移除教育版 - /// - RemoveEMR = 8, - - All = Regedit | ReflectionAttribute | ReflectionInterface | RemoveEMR, -} \ No newline at end of file diff --git a/src/IFoxCAD.Cad.Shared/Runtime/AutoRegAssem.cs b/src/IFoxCAD.Cad.Shared/Runtime/AutoRegAssem.cs deleted file mode 100644 index e8bb720..0000000 --- a/src/IFoxCAD.Cad.Shared/Runtime/AutoRegAssem.cs +++ /dev/null @@ -1,180 +0,0 @@ -namespace IFoxCAD.Cad; - - -/// -/// 注册中心 -/// -/// 初始化程序集信息写入注册表并反射特性和接口
    -/// 启动cad后的执行顺序为:
    -/// 1:程序集配置中心构造函数
    -/// 2:特性..(多个)
    -/// 3:接口..(多个)
    -///
    -///
    -public abstract class AutoRegAssem : IExtensionApplication -{ - #region 字段 - readonly AssemInfo _info; - readonly AutoReflection? _autoRef; - #endregion - - #region 静态方法 - /// - /// 程序集的路径 - /// - public static FileInfo Location => new(Assembly.GetCallingAssembly().Location); - /// - /// 程序集的目录 - /// - public static DirectoryInfo CurrDirectory => Location.Directory; - /// - /// 获取程序集的目录 - /// - /// 程序集 - /// 路径对象 - public static DirectoryInfo GetDirectory(Assembly? assem) - { - if (assem is null) - throw new(nameof(assem)); - return new FileInfo(assem.Location).Directory; - } - #endregion - - #region 构造函数 - /// - /// 注册中心 - /// - /// 配置项目 - public AutoRegAssem(AutoRegConfig autoRegConfig) - { - var assem = Assembly.GetCallingAssembly(); - _info = new() - { - Loader = assem.Location, - Fullname = assem.FullName, - Name = assem.GetName().Name, - LoadType = AssemLoadType.Startting - }; - - if ((autoRegConfig & AutoRegConfig.Regedit) == AutoRegConfig.Regedit) - { - if (!SearchForReg()) - RegApp(); - } - - if ((autoRegConfig & AutoRegConfig.RemoveEMR) == AutoRegConfig.RemoveEMR) - AcadEMR.Remove(); - - // 实例化了 AutoClass 之后会自动执行 IFoxAutoGo 接口下面的类, - // 以及自动执行特性 [IFoxInitialize] - // 类库用户不在此处进行其他代码,而是实现特性 - if ((autoRegConfig & AutoRegConfig.ReflectionInterface) == AutoRegConfig.ReflectionInterface || - (autoRegConfig & AutoRegConfig.ReflectionAttribute) == AutoRegConfig.ReflectionAttribute) - { - _autoRef = new AutoReflection(_info.Name, autoRegConfig); - _autoRef.Initialize(); - } - } - #endregion - - #region RegApp - - /// - /// 获取当前cad注册表位置 - /// - /// 打开权限 - /// - public static RegistryKey GetAcAppKey(bool writable = true) - { - RegistryKey? ackey = null; - var hc = HostApplicationServices.Current; -#if acad || zcad // 中望此处缺乏测试 -#if NET35 - string key = hc.RegistryProductRootKey; -#else - string key = hc.UserRegistryProductRootKey; -#endif - ackey = Registry.CurrentUser.OpenSubKey(key, writable); -#endif - -#if gcad - // gcad 此处是否仍然有bug? 等待其他人测试反馈 - var key = hc.RegistryProductRootKey; // 浩辰2020读出来是"" - string regedit = ""; - if (Env.GetAcadVersion() == 2019) - regedit = @"Software\Gstarsoft\GstarCAD\R" + 19 + @"\zh-CN"; - if (Env.GetAcadVersion() == 2020) - regedit = @"Software\Gstarsoft\GstarCAD\B" + 20 + @"\zh-CN"; - ackey = Registry.CurrentUser.OpenSubKey(regedit, writable); -#endif - - return ackey.CreateSubKey("Applications"); - } - - /// - /// 卸载注册表信息 - /// - public bool UnRegApp() - { - var appkey = GetAcAppKey(); - if (appkey.SubKeyCount == 0) - return false; - - var regApps = appkey.GetSubKeyNames(); - if (regApps.Contains(_info.Name)) - { - appkey.DeleteSubKey(_info.Name, false); - return true; - } - return false; - } - - /// - /// 是否已经存在注册表 - /// - /// - bool SearchForReg() - { - // 在使用netloadx的时候,此处注册表是失效的,具体原因要进行netloadx测试 - var appkey = GetAcAppKey(); - if (appkey.SubKeyCount == 0) - return false; - - var regApps = appkey.GetSubKeyNames(); - if (regApps.Contains(_info.Name)) - { - // 20220409 bug:文件名相同,路径不同,需要判断路径 - var info = appkey.OpenSubKey(_info.Name); - return info.GetValue("LOADER")?.ToString().ToLower() == _info.Loader.ToLower(); - } - return false; - } - - /// - /// 在注册表写入自动加载的程序集信息 - /// - public void RegApp() - { - var appkey = GetAcAppKey(); - var rk = appkey.CreateSubKey(_info.Name); - rk.SetValue("DESCRIPTION", _info.Fullname, RegistryValueKind.String); - rk.SetValue("LOADCTRLS", _info.LoadType, RegistryValueKind.DWord); - rk.SetValue("LOADER", _info.Loader, RegistryValueKind.String); - rk.SetValue("MANAGED", 1, RegistryValueKind.DWord); - appkey.Close(); - } - - // 这里的是不会自动执行的 - public void Initialize() - { - } - public void Terminate() - { - } - - ~AutoRegAssem() - { - _autoRef?.Terminate(); - } - #endregion RegApp -} \ No newline at end of file diff --git a/src/IFoxCAD.Cad.Shared/Runtime/CadVersion.cs b/src/IFoxCAD.Cad.Shared/Runtime/CadVersion.cs deleted file mode 100644 index d9614bf..0000000 --- a/src/IFoxCAD.Cad.Shared/Runtime/CadVersion.cs +++ /dev/null @@ -1,92 +0,0 @@ -namespace IFoxCAD.Cad; - -#if ac2009 - -public class CadVersion -{ - /// - /// 主版本 - /// - public int Major { get; set; } - - /// - /// 次版本 - /// - public int Minor { get; set; } - - /// - /// 版本号 - /// - public double ProgId => double.Parse($"{Major}.{Minor}"); - - /// - /// 注册表名称 - /// - public string? ProductName { get; set; } - - /// - /// 注册表位置 - /// - public string? ProductRootKey { get; set; } - - /// - /// 转换为字符串 - /// - /// 表示版本号的字符串 - public override string ToString() - { - return $"名称:{ProductName}\n版本号:{ProgId}\n注册表位置:{ProductRootKey}"; - } - // public override bool Equals(object obj) - // { - // return base.Equals(obj); - // } - - // public override int GetHashCode() - // { - // return base.GetHashCode(); - // } - - // // public override string ToString() - // // { - // // return base.ToString(); - // // } -} -#else -public record CadVersion -{ - /// - /// 主版本 - /// - public int Major; - - /// - /// 次版本 - /// - public int Minor; - - /// - /// 版本号 - /// - public double ProgId => double.Parse($"{Major}.{Minor}"); - - /// - /// 注册表名称 - /// - public string? ProductName; - - /// - /// 注册表位置 - /// - public string? ProductRootKey; - - /// - /// 转换为字符串 - /// - /// 表示版本号的字符串 - public override string ToString() - { - return $"名称:{ProductName}\n版本号:{ProgId}\n注册表位置:{ProductRootKey}"; - } -} -#endif \ No newline at end of file diff --git a/src/IFoxCAD.Cad.Shared/Runtime/DBTrans.cs b/src/IFoxCAD.Cad.Shared/Runtime/DBTrans.cs deleted file mode 100644 index 90fc981..0000000 --- a/src/IFoxCAD.Cad.Shared/Runtime/DBTrans.cs +++ /dev/null @@ -1,711 +0,0 @@ -namespace IFoxCAD.Cad; - -using System.Diagnostics; -using System.IO; -using System.Threading; -using System.Windows.Forms; - -/// -/// 事务栈 -/// 隐匿事务在数据库其中担任的角色 -/// -[DebuggerDisplay("{DebuggerDisplay,nq}")] -[DebuggerTypeProxy(typeof(DBTrans))] -public class DBTrans : IDisposable -{ - [DebuggerBrowsable(DebuggerBrowsableState.Never)] - string DebuggerDisplay => ToString(" | "); - - #region 私有字段 - /// - /// 事务栈 - /// - static readonly Stack _dBTrans = new(); - /// - /// 文档锁 - /// - readonly DocumentLock? _documentLock; - /// - /// 是否提交事务 - /// - bool _commit; - /// - /// 文件名 - /// - readonly string? _fileName; - #endregion - - #region 公开属性 - /// - /// 返回当前事务 - /// - public static DBTrans Top - { - get - { - /* - * 0x01 - * 事务栈上面有事务,这个事务属于当前文档, - * 那么直接提交原本事务然后再开一个(一直把栈前面的同数据库提交清空) - * 那不就发生跨事务读取图元了吗?....否决 - * - * 0x02 - * 跨文档事务出错 Autodesk.AutoCAD.Runtime.Exception:“eNotFromThisDocument” - * Curves.GetEntities()会从Top获取事务(Top会new一个),此时会是当前文档; - * 然后命令文中发生了 using DBTrans tr = new(); - * 当退出命令此事务释放,但是从来不释放Top, - * 然后我新建了一个文档,再进行命令=>又进入Top,Top返回了前一个文档的事务 - * 因此所以无法清理栈,所以Dispose不触发,导致无法刷新图元和Ctrl+Z出错 - * 所以用AOP方式修复 - * - * 0x03 - * 经过艰苦卓绝的测试,aop模式由于不能断点调试,所以暂时放弃。 - */ - - // 由于大量的函数依赖本属性,强迫用户先开启事务 - if (_dBTrans.Count == 0) - throw new ArgumentNullException("事务栈没有任何事务,请在调用前创建:" + nameof(DBTrans)); - var trans = _dBTrans.Peek(); - return trans; - } - } - /// - /// 文档 - /// - public Document? Document { get; private set; } - /// - /// 命令行 - /// - public Editor? Editor { get; private set; } - /// - /// 事务管理器 - /// - public Transaction Transaction { get; private set; } - /// - /// 数据库 - /// - public Database Database { get; private set; } - #endregion - - #region 构造函数 - /// - /// 事务栈 - /// 默认构造函数,默认为打开当前文档,默认提交事务 - /// - /// 要打开的文档 - /// 事务是否提交 - /// 是否锁文档 - public DBTrans(Document? doc = null, bool commit = true, bool doclock = false) - { - Document = doc ?? Acap.DocumentManager.MdiActiveDocument; - Database = Document.Database; - Editor = Document.Editor; - Transaction = Database.TransactionManager.StartTransaction(); - _commit = commit; - if (doclock) - _documentLock = Document.LockDocument(); - - _dBTrans.Push(this); - } - - /// - /// 事务栈 - /// 打开数据库,默认提交事务 - /// - /// 要打开的数据库 - /// 事务是否提交 - public DBTrans(Database database, bool commit = true) - { - Database = database; - Transaction = Database.TransactionManager.StartTransaction(); - _commit = commit; - _dBTrans.Push(this); - } - - /// - /// 事务栈 - /// 打开文件,默认提交事务 - /// - /// 要打开的文件名 - /// 事务是否提交 - /// 开图模式 - /// 密码 - /// 后台打开false;前台打开true(必须设置CommandFlags.Session) - public DBTrans(string fileName, - bool commit = true, - FileOpenMode fileOpenMode = FileOpenMode.OpenForReadAndWriteNoShare, - string? password = null, - bool activeOpen = false) - { - if (fileName == null || string.IsNullOrEmpty(fileName.Trim())) - throw new ArgumentNullException(nameof(fileName)); - - _fileName = fileName.Replace("/", "\\");// doc.Name总是"D:\\JX.dwg" - - // 此处若为失败的文件名,那么保存的时候就会丢失名称, - // 因此用 _fileName 储存 - if (!File.Exists(_fileName)) - { - if (activeOpen) - { - throw new IOException("错误:事务栈明确为前台开图时,文件不存在"); - } - else - { - // cad08测试: - // 第2个参数使用false,将导致关闭cad的时候出现致命错误: - // Unhandled Access Violation Reading Ox113697a0 Exception at 4b4154h - Database = new Database(true, true); - } - } - else - { - var doc = Acap.DocumentManager - .Cast() - .FirstOrDefault(doc => !doc.IsDisposed && doc.Name == _fileName); - - if (activeOpen) - { - if (doc is null) - { - try - { - // 设置命令标记: CommandFlags.Session - // 若没有设置: Open()之后的会进入中断状态(不会执行,直到切换文档ctrl+tab或者关闭文档) - doc = Acap.DocumentManager.Open(fileName, fileOpenMode == FileOpenMode.OpenForReadAndReadShare, password); - } - catch (Exception e) - { - throw new IOException($"错误:此文件打开错误:{fileName}\n错误信息:{e.Message}"); - } - } - // 设置命令标记: CommandFlags.Session - // 若没有设置: doc.IsActive 会异常 - if (!doc.IsActive) - Acap.DocumentManager.MdiActiveDocument = doc; - - // Open()是跨文档,所以必须要锁文档 - // 否则 Editor?.Redraw() 的 tm.QueueForGraphicsFlush() 将报错提示文档锁 - _documentLock = doc.LockDocument(); - - Database = doc.Database; - Document = doc; - Editor = doc.Editor; - } - else - { - if (doc is null) - { - Database = new Database(false, true); - if (Path.GetExtension(_fileName).ToLower().Contains("dxf")) - { - Database.DxfIn(_fileName, null); - } - else - { -#if ac2008 - Database.ReadDwgFile(_fileName, FileOpenModeHelper.GetFileShare(fileOpenMode), true, password); -#else - Database.ReadDwgFile(_fileName, fileOpenMode, true, password); -#endif - } - Database.CloseInput(true); - } - else - { - Database = doc.Database; - Document = doc; - Editor = doc.Editor; - } - } - } - - Transaction = Database.TransactionManager.StartTransaction(); - _commit = commit; - _dBTrans.Push(this); - } - #endregion - - #region 类型转换 - /// - /// 隐式转换为Transaction - /// - /// 事务管理器 - /// 事务管理器 - public static implicit operator Transaction(DBTrans tr) - { - return tr.Transaction; - } - #endregion - - #region 符号表 - - /// - /// 块表 - /// - public SymbolTable BlockTable => _BlockTable ??= new(this, Database.BlockTableId); - SymbolTable? _BlockTable; - /// - /// 当前绘图空间 - /// - public BlockTableRecord CurrentSpace => BlockTable.GetRecord(Database.CurrentSpaceId)!; - /// - /// 模型空间 - /// - public BlockTableRecord ModelSpace => BlockTable.GetRecord(BlockTable.CurrentSymbolTable[BlockTableRecord.ModelSpace])!; - /// - /// 图纸空间 - /// - public BlockTableRecord PaperSpace => BlockTable.GetRecord(BlockTable.CurrentSymbolTable[BlockTableRecord.PaperSpace])!; - /// - /// 层表 - /// - public SymbolTable LayerTable => _LayerTable ??= new(this, Database.LayerTableId); - SymbolTable? _LayerTable; - /// - /// 文字样式表 - /// - public SymbolTable TextStyleTable => _TextStyleTable ??= new(this, Database.TextStyleTableId); - SymbolTable? _TextStyleTable; - /// - /// 注册应用程序表 - /// - public SymbolTable RegAppTable => _RegAppTable ??= new(this, Database.RegAppTableId); - SymbolTable? _RegAppTable; - /// - /// 标注样式表 - /// - public SymbolTable DimStyleTable => _DimStyleTable ??= new(this, Database.DimStyleTableId); - SymbolTable? _DimStyleTable; - /// - /// 线型表 - /// - public SymbolTable LinetypeTable => _LinetypeTable ??= new(this, Database.LinetypeTableId); - SymbolTable? _LinetypeTable; - /// - /// 用户坐标系表 - /// - public SymbolTable UcsTable => _UcsTable ??= new(this, Database.UcsTableId); - SymbolTable? _UcsTable; - /// - /// 视图表 - /// - public SymbolTable ViewTable => _ViewTable ??= new(this, Database.ViewTableId); - SymbolTable? _ViewTable; - /// - /// 视口表 - /// - public SymbolTable ViewportTable => _ViewportTable ??= new(this, Database.ViewportTableId); - SymbolTable? _ViewportTable; - #endregion - - #region 字典 - /// - /// 命名对象字典 - /// - public DBDictionary NamedObjectsDict => GetObject(Database.NamedObjectsDictionaryId)!; - /// - /// 组字典 - /// - public DBDictionary GroupDict => GetObject(Database.GroupDictionaryId)!; - /// - /// 多重引线样式字典 - /// - public DBDictionary MLeaderStyleDict => GetObject(Database.MLeaderStyleDictionaryId)!; - /// - /// 多线样式字典 - /// - public DBDictionary MLStyleDict => GetObject(Database.MLStyleDictionaryId)!; - /// - /// 材质字典 - /// - public DBDictionary MaterialDict => GetObject(Database.MaterialDictionaryId)!; - /// - /// 表格样式字典 - /// - public DBDictionary TableStyleDict => GetObject(Database.TableStyleDictionaryId)!; - /// - /// 视觉样式字典 - /// - public DBDictionary VisualStyleDict => GetObject(Database.VisualStyleDictionaryId)!; - /// - /// 颜色字典 - /// - public DBDictionary ColorDict => GetObject(Database.ColorDictionaryId)!; - /// - /// 打印设置字典 - /// - public DBDictionary PlotSettingsDict => GetObject(Database.PlotSettingsDictionaryId)!; - /// - /// 打印样式表名字典 - /// - public DBDictionary PlotStyleNameDict => GetObject(Database.PlotStyleNameDictionaryId)!; - /// - /// 布局字典 - /// - public DBDictionary LayoutDict => GetObject(Database.LayoutDictionaryId)!; - -#if !zcad // 中望官方的问题 - /// - /// 数据链接字典 - /// - public DBDictionary DataLinkDict => GetObject(Database.DataLinkDictionaryId)!; - -#if !ac2009 - /// - /// 详细视图样式字典 - /// - public DBDictionary DetailViewStyleDict => GetObject(Database.DetailViewStyleDictionaryId)!; - /// - /// 剖面视图样式字典 - /// - public DBDictionary SectionViewStyleDict => GetObject(Database.SectionViewStyleDictionaryId)!; -#endif -#endif - #endregion - - #region 获取对象 - /// - /// 根据对象id获取图元对象 - /// - /// 要获取的图元对象的类型 - /// 对象id - /// 打开模式,默认为只读 - /// 是否打开已删除对象,默认为不打开 - /// 是否打开锁定图层对象,默认为不打开 - /// 图元对象,类型不匹配时返回 - public T? GetObject(ObjectId id, - OpenMode openMode = OpenMode.ForRead, - bool openErased = false, - bool openLockedLayer = false) where T : DBObject - { - return Transaction.GetObject(id, openMode, openErased, openLockedLayer) as T; - } - - /// - /// 根据对象句柄字符串获取对象Id - /// - /// 句柄字符串 - /// 对象id - public ObjectId GetObjectId(string handleString) - { - var hanle = new Handle(Convert.ToInt64(handleString, 16)); - // return Database.GetObjectId(false, hanle, 0); - return DBTransHelper.TryGetObjectId(Database, hanle); - } - #endregion - - #region 保存文件 - /// - /// 保存文件 - /// - /// - public void SaveDwgFile(DwgVersion version = DwgVersion.AC1800) - { - SaveFile(version); - } - - /// - /// 保存文件
    - ///
    - /// 默认2004dwg;若保存dxf则需要在路径输入扩展名 - /// 为true时候无效,将变为自动识别环境变量 - /// 另存为文件,前台将调用时它将无效,将变为弹出面板 - /// 保存路径失败的提示 - public void SaveFile(DwgVersion version = DwgVersion.AC1800, - bool automatic = true, - string? saveAsFile = null, - bool echoes = true) - { - // 遍历当前所有文档,文档必然是前台的 - Document? doc = null; - foreach (Document docItem in Acap.DocumentManager) - { - if (docItem.Database.Filename == this.Database.Filename) - { - doc = docItem; - break; - } - } - // 前台开图,使用命令保存;不需要切换文档 - if (doc != null) - { - if (saveAsFile == null) - doc.SendStringToExecute("_qsave\n", false, true, true); - else - /// 无法把 给这个面板 - doc.SendStringToExecute($"_Saveas\n", false, true, true); - return; - } - - // 后台开图,用数据库保存 - string? fileMsg; - bool creatFlag = false; - saveAsFile = saveAsFile?.Trim(); - if (string.IsNullOrEmpty(saveAsFile)) - { - fileMsg = _fileName; - saveAsFile = fileMsg; - //creatFlag = true; - } - else - { - fileMsg = saveAsFile; - - // 路径失败也保存到桌面 - var path = Path.GetDirectoryName(saveAsFile); - if (string.IsNullOrEmpty(path)) - { - creatFlag = true; - } - else if (!Directory.Exists(path)) - { - try { Directory.CreateDirectory(path); } - catch { creatFlag = true; } - } - - // 文件名缺失时 - if (!creatFlag && - string.IsNullOrEmpty(Path.GetFileName(saveAsFile).Trim())) - creatFlag = true; - } - if (saveAsFile != null) - { - var fileNameWith = Path.GetFileNameWithoutExtension(saveAsFile).Trim(); - if (string.IsNullOrEmpty(fileNameWith)) - creatFlag = true; - } - else - { - creatFlag = true; - } - - if (creatFlag) - { - var (error, file) = GetOrCreateSaveAsFile(); - if (echoes && error) - MessageBox.Show($"错误参数:\n{fileMsg}\n\n它将保存:\n{file}", "错误的文件路径"); - saveAsFile = file; - } - - if (Path.GetExtension(saveAsFile).ToLower().Contains("dxf")) - { - // dxf用任何版本号都会报错 -#if acad || gcad - Database.DxfOut(saveAsFile, 7, true); -#endif - -#if zcad // 中望这里没有测试 - Database.DxfOut(saveAsFile, 7, version, true); -#endif - return; - } - - if (automatic) - version = Env.GetDefaultDwgVersion(); - - // dwg需要版本号,而dxf不用,dwg用dxf版本号会报错 - // 若扩展名和版本号冲突,按照扩展名为准 - if (version.IsDxfVersion()) - version = DwgVersion.Current; - - Database.SaveAs(saveAsFile, version); - } - - - /// - /// 获取文件名,无效的话就制造 - /// - /// - (bool error, string path) GetOrCreateSaveAsFile() - { - var file = Database.Filename; - if (!string.IsNullOrEmpty(file)) - return (false, file); - - // 为了防止用户输入了错误的路径造成无法保存, - // 所以此处将进行保存到桌面, - // 而不是弹出警告就结束 - // 防止前台关闭了所有文档导致没有Editor,所以使用 MessageBox 发送警告 - var fileName = Path.GetFileNameWithoutExtension(_fileName).Trim(); - var fileExt = Path.GetExtension(_fileName); - - if (string.IsNullOrEmpty(fileName)) - fileName = DateTime.Now.ToString("--yyMMdd--hhmmssffff"); - if (string.IsNullOrEmpty(fileExt)) - fileExt = ".dwg"; - - // 构造函数(fileName)用了不存在的路径进行后台打开,就会出现此问题 - // 测试命令 FileNotExist - var dir = Environment.GetFolderPath(Environment.SpecialFolder.DesktopDirectory) - + "\\后台保存出错的文件\\"; - - if (!Directory.Exists(dir)) - Directory.CreateDirectory(dir); - - file = dir + fileName + fileExt; - while (File.Exists(file)) - { - var time = DateTime.Now.ToString("--yyMMdd--hhmmssffff"); - file = dir + fileName + time + fileExt; - Thread.Sleep(100); - } - return (true, file); - } - - #endregion - - #region 前台后台任务 - /// - /// 前台后台任务分别处理 - /// - /// - /// 备注:
    - /// 0x01 文字偏移问题主要出现线性引擎函数上面,
    - /// 在 参照绑定/深度克隆 的底层共用此函数导致
    - /// 0x02 后台是利用前台当前数据库进行处理的
    - /// 0x03 跨进程通讯暂无测试(可能存在bug)
    - ///
    - /// 委托 - /// 开启单行文字偏移处理 - public void Task(Action action, bool handlingDBTextDeviation = true) - { - if (action == null) - throw new ArgumentNullException(nameof(action)); - - // 前台开图 || 后台直接处理 - if (Document != null || !handlingDBTextDeviation) - { - action.Invoke(); - return; - } - - // 后台 - // 这种情况发生在关闭了所有文档之后,进行跨进程通讯 - // 此处要先获取激活的文档,不能直接获取当前数据库否则异常 - var dm = Acap.DocumentManager; - var doc = dm.MdiActiveDocument; - if (doc == null) - { - action.Invoke(); - return; - } - // 处理单行文字偏移 - // 前台绑定参照的时候不能用它,否则抛出异常:eWasErased - // 所以本函数自动识别前后台做处理 - var dbBak = doc.Database; - HostApplicationServices.WorkingDatabase = Database; - action.Invoke(); - HostApplicationServices.WorkingDatabase = dbBak; - } - #endregion - - #region IDisposable接口相关函数 - /// - /// 取消事务 - /// - public void Abort() - { - _commit = false; - Dispose(); - } - - /// - /// 提交事务 - /// - public void Commit() - { - _commit = true; - Dispose(); - } - - public bool IsDisposed { get; private set; } = false; - - /// - /// 手动调用释放 - /// - public void Dispose() - { - Dispose(true); - GC.SuppressFinalize(this); - } - - /// - /// 析构函数调用释放 - /// - ~DBTrans() - { - Dispose(false); - } - - protected virtual void Dispose(bool disposing) - { - /* 事务dispose流程: - * 1. 根据传入的参数确定是否提交,true为提交,false为不提交 - * 2. 如果锁文档就将文档锁dispose - * 3. 不管是否提交,既然进入dispose,就要将事务栈的当前事务弹出 - * 注意这里的事务栈不是cad的事务管理器,而是dbtrans的事务 - * 4. 清理非托管的字段 - */ - - // 不重复释放,并设置已经释放 - if (IsDisposed) return; - IsDisposed = true; - - // 致命错误时候此处是空,直接跳过 - if (Transaction != null) - { - if (_commit) - { - // 刷新队列(后台不刷新) - Editor?.Redraw(); - // 调用cad的事务进行提交,释放托管状态(托管对象) - Transaction.Commit(); - } - else - { - // 否则取消所有的修改 - Transaction.Abort(); - } - - // 将cad事务进行销毁 - if (!Transaction.IsDisposed) - Transaction.Dispose(); - } - // 将文档锁销毁 - _documentLock?.Dispose(); - - // 将当前事务栈弹栈 - _dBTrans.Pop(); - } - #endregion - - #region ToString - public override string ToString() - { - return ToString(null, null); - } - public string ToString(IFormatProvider? provider) - { - return ToString(null, provider); - } - public string ToString(string? format = null, IFormatProvider? formatProvider = null) - { - List lines = new(); - lines.Add($"StackCount = {_dBTrans.Count}"); - lines.Add($"_fileName = \"{_fileName}\""); - lines.Add($"_commit = {_commit}"); - lines.Add($"_documentLock = {_documentLock != null}"); - - lines.Add($"Document = {Document != null}"); - lines.Add($"Editor = {Editor != null}"); - lines.Add($"Transaction = {Transaction != null}"); - lines.Add($"Database = {Database != null}"); - - if (!string.IsNullOrEmpty(format)) - return string.Join(format, lines.ToArray()); - - return string.Join("\n", lines.ToArray()); - } - #endregion -} \ No newline at end of file diff --git a/src/IFoxCAD.Cad.Shared/Runtime/Env.cs b/src/IFoxCAD.Cad.Shared/Runtime/Env.cs deleted file mode 100644 index f80a4ee..0000000 --- a/src/IFoxCAD.Cad.Shared/Runtime/Env.cs +++ /dev/null @@ -1,749 +0,0 @@ -using System.Diagnostics; - -namespace IFoxCAD.Cad; - -/// -/// 系统管理类 -/// -/// 封装了一些系统 osmode;cmdecho;dimblk 系统变量
    -/// 封装了常用的 文档 编辑器 数据库等对象为静态变量
    -/// 封装了配置页面的注册表信息获取函数 -///
    -///
    -public static class Env -{ - #region Goal - - /// - /// 当前的数据库 - /// - public static Database Database => HostApplicationServices.WorkingDatabase; - - /// - /// 当前文档 - /// - public static Document Document => Acap.DocumentManager.MdiActiveDocument; - - /// - /// 编辑器对象 - /// - public static Editor Editor => Document.Editor; - - /// - /// 图形管理器 - /// - public static Manager GsManager => Document.GraphicsManager; - - #endregion Goal - - #region Preferences - -#if !zcad // 中望官方的问题 - /// - /// 获取当前配置的数据 - /// - /// 小节名 - /// 数据名 - /// 对象 - public static object GetCurrentProfileProperty(string subSectionName, string propertyName) - { - UserConfigurationManager ucm = Acap.UserConfigurationManager; - IConfigurationSection cpf = ucm.OpenCurrentProfile(); - IConfigurationSection ss = cpf.OpenSubsection(subSectionName); - return ss.ReadProperty(propertyName, ""); - } - - - /// - /// 获取对话框配置的数据 - /// - /// 对话框对象 - /// 配置项 - public static IConfigurationSection GetDialogSection(object dialog) - { - UserConfigurationManager ucm = Acap.UserConfigurationManager; - IConfigurationSection ds = ucm.OpenDialogSection(dialog); - return ds; - } - - /// - /// 获取公共配置的数据 - /// - /// 数据名 - /// 配置项 - public static IConfigurationSection GetGlobalSection(string propertyName) - { - UserConfigurationManager ucm = Acap.UserConfigurationManager; - IConfigurationSection gs = ucm.OpenGlobalSection(); - IConfigurationSection ss = gs.OpenSubsection(propertyName); - return ss; - } -#endif - #endregion Preferences - - #region Enum - /// - /// 控制在AutoLISP的command函数运行时AutoCAD是否回显提示和输入, 为显示, 为不显示 - /// - public static bool CmdEcho - { - get => Convert.ToInt16(Acap.GetSystemVariable("cmdecho")) == 1; - set => Acap.SetSystemVariable("cmdecho", Convert.ToInt16(value)); - } - - /// - /// 控制在光标是否为正交模式, 为打开正交, 为关闭正交 - /// - public static bool OrthoMode - { - get => Convert.ToInt16(Acap.GetSystemVariable("ORTHOMODE")) == 1; - set => Acap.SetSystemVariable("ORTHOMODE", Convert.ToInt16(value)); - } - - #region Dimblk - - /// - /// 标注箭头类型 - /// - public enum DimblkType - { - /// - /// 实心闭合 - /// - Defult, - - /// - /// 点 - /// - Dot, - - /// - /// 小点 - /// - DotSmall, - - /// - /// 空心点 - /// - DotBlank, - - /// - /// 原点标记 - /// - Origin, - - /// - /// 原点标记2 - /// - Origin2, - - /// - /// 打开 - /// - Open, - - /// - /// 直角 - /// - Open90, - - /// - /// 30度角 - /// - Open30, - - /// - /// 闭合 - /// - Closed, - - /// - /// 空心小点 - /// - Small, - - /// - /// 无 - /// - None, - - /// - /// 倾斜 - /// - Oblique, - - /// - /// 实心框 - /// - BoxFilled, - - /// - /// 方框 - /// - BoxBlank, - - /// - /// 空心闭合 - /// - ClosedBlank, - - /// - /// 实心基准三角形 - /// - DatumFilled, - - /// - /// 基准三角形 - /// - DatumBlank, - - /// - /// 完整标记 - /// - Integral, - - /// - /// 建筑标记 - /// - ArchTick - } - - private static readonly Dictionary dimdescdict = new() - { - { "实心闭合", DimblkType.Defult }, - { "点", DimblkType.Dot }, - { "小点", DimblkType.DotSmall }, - { "空心点", DimblkType.DotBlank }, - { "原点标记", DimblkType.Origin }, - { "原点标记 2", DimblkType.Origin2 }, - { "打开", DimblkType.Open }, - { "直角", DimblkType.Open90 }, - { "30 度角", DimblkType.Open30 }, - { "闭合", DimblkType.Closed }, - { "空心小点", DimblkType.Small }, - { "无", DimblkType.None }, - { "倾斜", DimblkType.Oblique }, - { "实心框", DimblkType.BoxFilled }, - { "方框", DimblkType.BoxBlank }, - { "空心闭合", DimblkType.ClosedBlank }, - { "实心基准三角形", DimblkType.DatumFilled }, - { "基准三角形", DimblkType.DatumBlank }, - { "完整标记", DimblkType.Integral }, - { "建筑标记", DimblkType.ArchTick }, - - { "", DimblkType.Defult }, - { "_DOT", DimblkType.Dot }, - { "_DOTSMALL", DimblkType.DotSmall }, - { "_DOTBLANK", DimblkType.DotBlank }, - { "_ORIGIN", DimblkType.Origin }, - { "_ORIGIN2", DimblkType.Origin2 }, - { "_OPEN", DimblkType.Open }, - { "_OPEN90", DimblkType.Open90 }, - { "_OPEN30", DimblkType.Open30 }, - { "_CLOSED", DimblkType.Closed }, - { "_SMALL", DimblkType.Small }, - { "_NONE", DimblkType.None }, - { "_OBLIQUE", DimblkType.Oblique }, - { "_BOXFILLED", DimblkType.BoxFilled }, - { "_BOXBLANK", DimblkType.BoxBlank }, - { "_CLOSEDBLANK", DimblkType.ClosedBlank }, - { "_DATUMFILLED", DimblkType.DatumFilled }, - { "_DATUMBLANK", DimblkType.DatumBlank }, - { "_INTEGRAL", DimblkType.Integral }, - { "_ARCHTICK", DimblkType.ArchTick }, - }; - - - - /// - /// 标注箭头属性 - /// - public static DimblkType Dimblk - { - get - { - string s = ((string)Acap.GetSystemVariable("dimblk")).ToUpper(); - // if (string.IsNullOrEmpty(s)) - // { - // return DimblkType.Defult; - // } - // else - // { - // if (dimdescdict.TryGetValue(s, out DimblkType value)) - // { - // return value; - // } - // return s.ToEnum(); - // // return s.FromDescName(); - // } - return dimdescdict[s]; - } - set - { - string s = GetDimblkName(value); - Acap.SetSystemVariable("dimblk", s); - } - } - - /// - /// 获取标注箭头名 - /// - /// 标注箭头类型 - /// 箭头名 - public static string GetDimblkName(DimblkType dimblk) - { - return - dimblk == DimblkType.Defult - ? - "." - : - "_" + dimblk.GetName(); - } - - /// - /// 获取标注箭头ID - /// - /// 标注箭头类型 - /// 箭头ID - public static ObjectId GetDimblkId(DimblkType dimblk) - { - DimblkType oldDimblk = Dimblk; - Dimblk = dimblk; - ObjectId id = HostApplicationServices.WorkingDatabase.Dimblk; - Dimblk = oldDimblk; - return id; - } - - #endregion Dimblk - - #region OsMode - - /// - /// 捕捉模式系统变量类型 - /// - public enum OSModeType - { - /// - /// 无 - /// - None = 0, - - /// - /// 端点 - /// - End = 1, - - /// - /// 中点 - /// - Middle = 2, - - /// - /// 圆心 - /// - Center = 4, - - /// - /// 节点 - /// - Node = 8, - - /// - /// 象限点 - /// - Quadrant = 16, - - /// - /// 交点 - /// - Intersection = 32, - - /// - /// 插入点 - /// - Insert = 64, - - /// - /// 垂足 - /// - Pedal = 128, - - /// - /// 切点 - /// - Tangent = 256, - - /// - /// 最近点 - /// - Nearest = 512, - - /// - /// 几何中心 - /// - Quick = 1024, - - /// - /// 外观交点 - /// - Appearance = 2048, - - /// - /// 延伸 - /// - Extension = 4096, - - /// - /// 平行 - /// - Parallel = 8192 - } - - /// - /// 捕捉模式系统变量 - /// - public static OSModeType OSMode - { - get - { - return (OSModeType)Convert.ToInt16(Acap.GetSystemVariable("osmode")); - } - set - { - Acap.SetSystemVariable("osmode", (int)value); - } - } - /// - /// 捕捉模式osm1是否包含osm2 - /// - /// 原模式 - /// 要比较的模式 - /// 包含时返回 ,不包含时返回 - public static bool Include(this OSModeType osm1, OSModeType osm2) - { - return (osm1 & osm2) == osm2; - } - #endregion OsMode - - - private static string GetName(this T value) - { - return Enum.GetName(typeof(T), value); - } - - #endregion Enum - - #region 系统变量 - /// - /// 获取cad系统变量 - /// - /// 变量名 - /// 变量值 - public static object GetVar(string? varName) - { - return Acap.GetSystemVariable(varName); - } - /// - /// 设置cad系统变量
    - /// 0x01 建议先获取现有变量值和设置的是否相同,否则直接设置会发生异常
    - /// 0x02 建议锁文档,否则 Psltscale 设置发生异常
    - /// 发生异常的时候vs输出窗口会打印一下,但是如果不介意也没啥问题 - ///
    - /// 变量名 - /// 变量值 - /// 输出异常,默认true;此设置仅为打印到命令栏,无法控制vs输出 - public static void SetVar(string? varName, object? value, bool echo = true) - { - try - { - Acap.SetSystemVariable(varName, value); - } - catch (System.Exception) - { - if (echo) - Env.Print($"{varName} 是不存在的变量!"); - } - } - #endregion - - #region 环境变量 -#if acad -#if NET35 - [System.Security.SuppressUnmanagedCodeSecurity] - [DllImport("acad.exe", CharSet = CharSet.Auto, CallingConvention = CallingConvention.Cdecl, EntryPoint = "acedGetEnv")] - static extern int AcedGetEnv(string? envName, StringBuilder ReturnValue); - - [System.Security.SuppressUnmanagedCodeSecurity] - [DllImport("acad.exe", CharSet = CharSet.Auto, CallingConvention = CallingConvention.Cdecl, EntryPoint = "acedSetEnv")] - static extern int AcedSetEnv(string? envName, StringBuilder NewValue); -#else - [System.Security.SuppressUnmanagedCodeSecurity] - [DllImport("accore.dll", CharSet = CharSet.Auto, CallingConvention = CallingConvention.Cdecl, EntryPoint = "acedGetEnv")] - static extern int AcedGetEnv(string? envName, StringBuilder ReturnValue); - - [System.Security.SuppressUnmanagedCodeSecurity] - [DllImport("accore.dll", CharSet = CharSet.Auto, CallingConvention = CallingConvention.Cdecl, EntryPoint = "acedSetEnv")] - static extern int AcedSetEnv(string? envName, StringBuilder NewValue); -#endif -#endif - -#if gcad - [System.Security.SuppressUnmanagedCodeSecurity] - [DllImport("gced.dll", CharSet = CharSet.Auto, CallingConvention = CallingConvention.Cdecl, EntryPoint = "gcedGetEnv")] - static extern int AcedGetEnv(string? envName, StringBuilder ReturnValue); - - [System.Security.SuppressUnmanagedCodeSecurity] - [DllImport("gced.dll", CharSet = CharSet.Auto, CallingConvention = CallingConvention.Cdecl, EntryPoint = "gcedSetEnv")] - static extern int AcedSetEnv(string? envName, StringBuilder NewValue); -#endif - - // TODO: 中望没有测试,此处仅为不报错;本工程所有含有"中望"均存在问题 -#if zcad - [System.Security.SuppressUnmanagedCodeSecurity] - [DllImport("zced.dll", CharSet = CharSet.Auto, CallingConvention = CallingConvention.Cdecl, EntryPoint = "zcedGetEnv")] - static extern int AcedGetEnv(string? envName, StringBuilder ReturnValue); - - [System.Security.SuppressUnmanagedCodeSecurity] - [DllImport("zced.dll", CharSet = CharSet.Auto, CallingConvention = CallingConvention.Cdecl, EntryPoint = "zcedSetEnv")] - static extern int AcedSetEnv(string? envName, StringBuilder NewValue); -#endif - - /// - /// 读取acad环境变量
    - /// 也能获取win环境变量 - ///
    - /// 变量名 - /// 返回值从不为null,需判断 - public static string GetEnv(string? name) - { - // 它将混合查询以下路径: - // acad2008注册表路径: 计算机\HKEY_CURRENT_USER\SOFTWARE\Autodesk\AutoCAD\R17.1\ACAD-6001:804\FixedProfile\General - // 用户: 计算机\HKEY_CURRENT_USER\Environment - // 系统: 计算机\HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\Session Manager\Environment - - // GetEnv("Path")长度很长: - // 可用内存 (最新格式) 1 MB (标准格式) - // https://docs.microsoft.com/zh-cn/windows/win32/sysinfo/registry-element-size-limits - - var sbRes = new StringBuilder(1 << 23); - _ = AcedGetEnv(name, sbRes); - return sbRes.ToString(); - } - - /// - /// 设置acad环境变量
    - /// 它是不会报错的,但是直接设置会写入注册表的,
    - /// 如果是设置高低版本cad不同的变量,建议先读取判断再设置
    - ///
    - /// 变量名 - /// 变量值 - /// - public static int SetEnv(string? name, string? var) - { - return AcedSetEnv(name, new StringBuilder(var)); - } - #endregion - - #region win环境变量/由于 Aced的 能够同时获取此变量与cad内的,所以废弃 - // /// - // /// 获取系统环境变量 - // /// - // /// 变量名 - // /// 指定的环境变量的值;或者如果找不到环境变量,则返回 null - // public static string? GetEnv(string? var) - // { - // // 从当前进程或者从当前用户或本地计算机的 Windows 操作系统注册表项检索环境变量的值 - // // 用户: 计算机\HKEY_CURRENT_USER\Environment - // // 系统: 计算机\HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\Session Manager\Environment - // return Environment.GetEnvironmentVariable(var); - // } - // /// - // /// 设置系统环境变量 - // /// - // /// 变量名 - // /// 变量值 - // public static void SetEnv(string? var, string? value) - // { - // // 创建、修改或删除当前进程中或者为当前用户或本地计算机保留的 Windows 操作系统注册表项中存储的环境变量 - // Environment.SetEnvironmentVariable(var, value); - // } - #endregion - - - /// - /// 命令行打印,会自动调用对象的toString函数 - /// - /// 要打印的对象 - public static void Print(object message) => Editor.WriteMessage($"{message}\n"); - public static void Printl(object message) => Editor.WriteMessage($"{Environment.NewLine}{message}\n"); - - /// - /// 判断当前是否在UCS坐标下 - /// - /// Bool - public static bool IsUcs() => (short)GetVar("WORLDUCS") == 0; - - - #region dwg版本号/cad版本号/年份 - /// - /// 获取当前配置文件的保存版本 - /// - /// - public static DwgVersion GetDefaultDwgVersion() - { - DwgVersion version; - var ffs = Env.GetEnv("DefaultFormatForSave"); - version = ffs switch - { - "1" => DwgVersion.AC1009,// R12/LT12 dxf - "8" => DwgVersion.AC1014,// R14/LT98/LT97 dwg - "12" => DwgVersion.AC1015,// 2000 dwg - "13" => DwgVersion.AC1800a,// 2000 dxf - "24" => DwgVersion.AC1800,// 2004 dwg - "25" => (DwgVersion)26,// 2004 dxf - "36" => (DwgVersion)27,// 2007 dwg DwgVersion.AC1021 - "37" => (DwgVersion)28,// 2007 dxf - - // "38" => (DwgVersion),// dwt 样板文件...啊惊没找到这个是什么 - "48" => (DwgVersion)29,// 2010 dwg DwgVersion.AC1024 - "49" => (DwgVersion)30,// 2010 dxf - "60" => (DwgVersion)31,// 2013 dwg DwgVersion.AC1027 - "61" => (DwgVersion)32,// 2013 dxf - "64" => (DwgVersion)33,// 2018 dwg DwgVersion.AC1032 - "65" => (DwgVersion)34,// 2018 dxf - _ => throw new NotImplementedException(),// 提醒维护 - }; - return version; - } - - /// - /// 是否为dxf版本号 - /// - /// - /// - public static bool IsDxfVersion(this DwgVersion dwgVersion) - { - var result = (int)dwgVersion switch - { - 16 => true,// R12/LT12 dxf - 24 => true,// 2000 dxf - 26 => true,// 2004 dxf - 28 => true,// 2007 dxf - 30 => true,// 2010 dxf - 32 => true,// 2013 dxf - 34 => true,// 2018 dxf - _ => false, - }; - return result; - } - - /// - /// 获取cad年份 - /// - /// 超出年份就报错 - public static int GetAcadVersion() - { - var ver = Acap.Version.Major + "." + Acap.Version.Minor; - int acarVarNum = ver switch - { - "16.2" => 2006, - "17.0" => 2007, - "17.1" => 2008, - "17.2" => 2009, - "18.0" => 2010, - "18.1" => 2011, - "18.2" => 2012, - "19.0" => 2013, - "19.1" => 2014, - "20.0" => 2015, - "20.1" => 2016, - "21.0" => 2017, - "22.0" => 2018, - "23.0" => 2019, - "23.1" => 2020, - "24.0" => 2021, - "24.1" => 2022, - _ => throw new NotImplementedException(), - }; - return acarVarNum; - } - - public static string GetAcapVersionDll(string str = "acdb") - { - return str + Acap.Version.Major + ".dll"; - } - #endregion - - - #region cad变量功能延伸 - /// - /// 设置cad系统变量
    - /// 提供一个反序列化后,无cad异常输出的功能
    - /// 注意,您需要再此执行时候设置文档锁
    - ///
    - /// 否则也将导致修改数据库异常
    - ///
    - /// - /// - /// 成功返回当前值,失败null - /// - public static object? SetVarEx(string? key, string? value) - { - if (key == null) - throw new ArgumentNullException(nameof(key)); - if (value == null) - throw new ArgumentNullException(nameof(value)); - - var currentVar = Env.GetVar(key); - if (currentVar == null) - return null; - - object? valueType = currentVar.GetType().Name switch - { - "String" => value.Replace("\"", string.Empty), - "Double" => double.Parse(value), - "Int16" => short.Parse(value), - "Int32" => int.Parse(value), - _ => throw new NotImplementedException(), - }; - - // 相同的参数进行设置会发生一次异常 - if (currentVar.ToString().ToUpper() != valueType!.ToString().ToUpper()) - Env.SetVar(key, valueType); - - return currentVar; - } - - /// - /// 设置新系统变量,返回现有系统变量 - /// - /// 设置的变量词典 - /// 返回现有变量词典,然后下次就可以利用它进行设置回来了 - public static Dictionary SaveCadVar(Dictionary args) - { - if (args is null) - throw new ArgumentNullException(nameof(args)); - - var dict = new Dictionary(); - foreach (var item in args) - { - // 判断是否为系统变量 - var ok = SetVarEx(item.Key, item.Value); - if (ok != null) - { - dict.Add(item.Key, ok.ToString()); - continue; - } - - // 判断是否为系统变量 - var envstr = Env.GetEnv(item.Key); - if (!string.IsNullOrEmpty(envstr)) - { - Env.SetEnv(item.Key, item.Value); - dict.Add(item.Key, envstr); - } - } - return dict; - } - #endregion -} \ No newline at end of file diff --git a/src/IFoxCAD.Cad.Shared/Runtime/FileOpenMode.cs b/src/IFoxCAD.Cad.Shared/Runtime/FileOpenMode.cs deleted file mode 100644 index 55dcefd..0000000 --- a/src/IFoxCAD.Cad.Shared/Runtime/FileOpenMode.cs +++ /dev/null @@ -1,85 +0,0 @@ -#if ac2008 // NET35 -namespace Autodesk.AutoCAD.DatabaseServices -{ - [Wrapper("AcDbDatabase::OpenMode")] - public enum FileOpenMode - { - /// - /// 只读模式打开 - /// - OpenForReadAndReadShare = 1, - OpenForReadAndWriteNoShare = 2, - OpenForReadAndAllShare = 3, - OpenTryForReadShare = 4, - } - - public static class FileOpenModeHelper - { - /* - * 这个开图方式会致命错误,不清楚怎么用的文件句柄开图 - * using FileStream fileStream = new(_fileName, FileMode.Open, fileAccess, GetFileShare(fileOpenMode)); - * Database.ReadDwgFile(fileStream.SafeFileHandle.DangerousGetHandle(), true, password); - */ - public static FileShare GetFileShare(FileOpenMode fileOpenMode) - { - // FileAccess fileAccess = FileAccess.Read; - FileShare fileShare = FileShare.Read; - switch (fileOpenMode) - { - // 不完美匹配 - case FileOpenMode.OpenTryForReadShare: - // fileAccess = FileAccess.ReadWrite; - fileShare = FileShare.ReadWrite; - break; - // 完美匹配 - case FileOpenMode.OpenForReadAndAllShare: - // fileAccess = FileAccess.ReadWrite; - fileShare = FileShare.ReadWrite; - break; - // 完美匹配 - case FileOpenMode.OpenForReadAndWriteNoShare: - // fileAccess = FileAccess.ReadWrite; - fileShare = FileShare.None; - break; - // 完美匹配 - case FileOpenMode.OpenForReadAndReadShare: - // fileAccess = FileAccess.Read; - fileShare = FileShare.Read; - break; - } - return fileShare; - } - } -} -#endif - -#if NET35 -namespace Autodesk.AutoCAD.Internal -{ - public class Utils - { - public static void SetFocusToDwgView() - { - IntPtr window; - if (Acap.DocumentManager.Count == 0) - { - window = Acap.MainWindow.Handle; - } - else - { - // 它们是层级关系 - // Main - // -->MDI(大小被 DwgView 局限) - // ---->docW(比MDI大) - // -------->msctls_statusbar32 - // -------->DwgView - var docW = Acap.DocumentManager.MdiActiveDocument.Window.Handle; - var msctls_statusbar32 = IFoxCAD.Cad.WindowsAPI.GetTopWindow(docW); - window = IFoxCAD.Cad.WindowsAPI.GetWindow(msctls_statusbar32, 2U); - } - if (window != IntPtr.Zero) - IFoxCAD.Cad.WindowsAPI.SetFocus(window); - } - } -} -#endif \ No newline at end of file diff --git a/src/IFoxCAD.Cad.Shared/Runtime/IAutoGo.cs b/src/IFoxCAD.Cad.Shared/Runtime/IAutoGo.cs deleted file mode 100644 index 738ba19..0000000 --- a/src/IFoxCAD.Cad.Shared/Runtime/IAutoGo.cs +++ /dev/null @@ -1,403 +0,0 @@ -namespace IFoxCAD.Cad; - -using System.Collections.ObjectModel; -using System.Diagnostics; - -/// -/// 加载时优先级 -/// -[Flags] -public enum Sequence : byte -{ - First,// 最先 - Last, // 最后 -} - -/// -/// 加载时自动执行接口 -/// -public interface IFoxAutoGo -{ - // 控制加载顺序 - Sequence SequenceId(); - // 关闭cad的时候会自动执行 - void Terminate(); - // 打开cad的时候会自动执行 - void Initialize(); -} - -/// -/// 加载时自动执行特性 -/// -[AttributeUsage(AttributeTargets.Method)] -public class IFoxInitialize : Attribute -{ - /// - /// 优先级 - /// - internal Sequence SequenceId; - /// - /// 用于初始化;用于结束回收 - /// - internal bool IsInitialize; - /// - /// 用于初始化和结束回收 - /// - /// 优先级 - /// 用于初始化;用于结束回收 - public IFoxInitialize(Sequence sequence = Sequence.Last, bool isInitialize = true) - { - SequenceId = sequence; - IsInitialize = isInitialize; - } -} - -// 为了解决IExtensionApplication在一个dll内无法多次实现接口的关系 -// 所以在这里反射加载所有的 IAutoGo ,以达到能分开写"启动运行"函数的目的 -class RunClass -{ - public Sequence Sequence { get; } - readonly MethodInfo _methodInfo; - object? _instance; - /// - /// 执行此方法 - /// - /// - /// - /// 已经创建的对象 - public RunClass(MethodInfo method, Sequence sequence, object? instance = null) - { - _methodInfo = method; - Sequence = sequence; - _instance = instance; - } - - /// - /// 运行方法 - /// - public void Run() - { - _methodInfo.Invoke(ref _instance); - } -} - -/// -/// 此类作为加载后cad自动运行接口的一部分,用于反射特性和接口 -/// -/// 启动cad后的执行顺序为:
    -/// 1:特性..(多个)
    -/// 2:接口..(多个) -///
    -///
    -public class AutoReflection -{ - static List _InitializeList = new(); // 储存方法用于初始化 - static List _TerminateList = new(); // 储存方法用于结束释放 - - readonly string _dllName; - readonly AutoRegConfig _autoRegConfig; - - /// - /// 反射执行 - /// - /// 1.特性:
    - /// 2.接口: - ///
    - ///
    - /// 约束在此dll进行加速 - public AutoReflection(string dllName, AutoRegConfig configInfo) - { - _dllName = dllName; - _autoRegConfig = configInfo; - } - - // 启动cad的时候会自动执行 - public void Initialize() - { - try - { - // 收集特性,包括启动时和关闭时 - if ((_autoRegConfig & AutoRegConfig.ReflectionAttribute) == AutoRegConfig.ReflectionAttribute) - GetAttributeFunctions(_InitializeList, _TerminateList); - - if ((_autoRegConfig & AutoRegConfig.ReflectionInterface) == AutoRegConfig.ReflectionInterface) - { - GetInterfaceFunctions(_InitializeList, nameof(Initialize), _TerminateList, nameof(Terminate)); - } - - if (_InitializeList.Count > 0) - { - // 按照 SequenceId 排序_升序 - _InitializeList = _InitializeList.OrderBy(runClass => runClass.Sequence).ToList(); - RunFunctions(_InitializeList); - } - } - catch (System.Exception e) - { - Debugger.Break(); - } - } - - // 关闭cad的时候会自动执行 - public void Terminate() - { - try - { - //if ((_autoRegConfig & AutoRegConfig.ReflectionInterface) == AutoRegConfig.ReflectionInterface) - // GetInterfaceFunctions(_TerminateList, nameof(Terminate)); - - if (_TerminateList.Count > 0) - { - // 按照 SequenceId 排序_降序 - _TerminateList = _TerminateList.OrderByDescending(runClass => runClass.Sequence).ToList(); - RunFunctions(_TerminateList); - } - } - catch (System.Exception e) - { - Env.Printl(e.Message); - Debugger.Break(); - } - } - - /// - /// 遍历程序域下所有类型 - /// - /// 输出每个成员执行 - /// 过滤此dll,不含后缀 - public static void AppDomainGetTypes(Action action, string? dllNameWithoutExtension = null) - { -#if DEBUG - int error = 0; -#endif - try - { - var assemblies = AppDomain.CurrentDomain.GetAssemblies(); -#if !NET35 - // cad2021出现如下报错 - // System.NotSupportedException:动态程序集中不支持已调用的成员 - // assemblies = assemblies.Where(p => !p.IsDynamic).ToArray();// 这个要容器类型转换 - assemblies = Array.FindAll(assemblies, p => !p.IsDynamic); -#endif - // 主程序域 - for (int ii = 0; ii < assemblies.Length; ii++) - { - var assembly = assemblies[ii]; - - // 获取类型集合,反射时候还依赖其他的dll就会这个错误 - // 此通讯库要跳过,否则会报错. - var location = Path.GetFileNameWithoutExtension(assembly.Location); - if (dllNameWithoutExtension != null && location != dllNameWithoutExtension) - continue; - if (location == "AcInfoCenterConn")// 通讯库 - continue; - - Type[]? types = null; - try - { - types = assembly.GetTypes(); - } - catch (ReflectionTypeLoadException) { continue; } - - if (types is null) - continue; - - for (int jj = 0; jj < types.Length; jj++) - { - var type = types[jj]; - if (type is not null) - { -#if DEBUG - ++error; -#endif - action.Invoke(type); - } - } - } - } - catch (System.Exception e) - { -#if DEBUG - Debugx.Printl($"出错:{nameof(AppDomainGetTypes)};计数{error};错误信息:{e.Message}"); - Debugger.Break(); -#endif - } - } - - /// - /// 收集接口下的函数 - /// - /// 储存要运行的方法 - /// 查找方法名 - /// - void GetInterfaceFunctions(List initializes, string initializeName, - List terminates, string terminateName) - { - AppDomainGetTypes(type => { - // 接口的静态类屏蔽,继承接口无法使用静态类,因此跳过 - if (type.IsAbstract) - return; - - var ints = type.GetInterfaces(); - for (int sss = 0; sss < ints.Length; sss++) - { - if (ints[sss].Name != nameof(IFoxAutoGo)) - continue; - - Sequence? sequence = null; - MethodInfo? initialize = null; - MethodInfo? terminate = null; - object? instance = null; - - var mets = type.GetMethods(); - for (int jj = 0; jj < mets.Length; jj++) - { - var method = mets[jj]; - - // 接口的静态方法屏蔽,继承的方法也不可能是静态的,因此跳过 - if (method.IsAbstract) - continue; - - if (method.Name == nameof(IFoxAutoGo.SequenceId)) - { - // 避免触发两次构造函数,所以这里需要ref构造的对象出来 - var obj = method.Invoke(ref instance); - if (obj is not null) - sequence = (Sequence)obj; - continue; - } - if (method.Name == initializeName) - { - initialize = method; - continue; - } - if (method.Name == terminateName) - { - terminate = method; - continue; - } - if (sequence is not null && initialize is not null && terminate is not null) - break; - } - - // 避免在terminate释放的时候去再次构造,所以需要一次性收集 - // 若是释放的时候去再次构造: - // 0x01 initialize构造的字段拥有的资源就处于系统释放了,这是不合理的 - // 0x02 同时也发生了,为了释放而去构造这个操作 - var seq = sequence is null ? Sequence.Last : sequence.Value; - if (initialize is not null) - initializes.Add(new(initialize, seq, instance)); - if (terminate is not null) - terminates.Add(new(terminate, seq, instance)); - break; - } - }, _dllName); - } - - /// - /// 收集特性下的函数 - /// - void GetAttributeFunctions(List initializes, - List terminates) - { - AppDomainGetTypes(type => { - if (!type.IsClass) - return; - - // 特性的静态类不屏蔽 - //if (type.IsAbstract) - // return; - - var mets = type.GetMethods(); - for (int ii = 0; ii < mets.Length; ii++) - { - var method = mets[ii]; - - // 特性的静态方法不屏蔽 - //if (method.IsAbstract) - // continue; - - var attr = method.GetCustomAttributes(true); - for (int jj = 0; jj < attr.Length; jj++) - { - if (attr[jj] is IFoxInitialize jjAtt) - { - var runc = new RunClass(method, jjAtt.SequenceId); - if (jjAtt.IsInitialize) - initializes.Add(runc); - else - terminates.Add(runc); - break; - } - } - } - }, _dllName); - } - - /// - /// 执行收集到的函数 - /// - static void RunFunctions(List runClassList) - { - for (int i = 0; i < runClassList.Count; i++) - runClassList[i].Run(); - runClassList.Clear(); - } - -#if Debug - /// - /// 检查当前程序域重复出现命令, - /// 当出现重复时候将引起断点 - /// - public static void DebugCheckCmdRecurrence() - { - HashSet keys = new(); - - // 本dll中存在冲突命令,此时cad自动接口可以运行,但是加载命令之后会报错,因此利用断点告诉程序员 - AutoReflection.AppDomainGetTypes(type => { - var mets = type.GetMethods(); - for (int ii = 0; ii < mets.Length; ii++) - { - var method = mets[ii]; - var attr = method.GetCustomAttributes(true); - for (int jj = 0; jj < attr.Length; jj++) - if (attr[jj] is CommandMethodAttribute att) - { - if (keys.Contains(att.GlobalName)) - Debugger.Break(); - keys.Add(att.GlobalName); - } - } - }, Assembly.GetCallingAssembly().GetName().Name); - - // 其他dll中存在冲突命令,此时会覆盖命令,友好的提示程序员 - keys.Clear(); - HashSet msgMod = new(); - AutoReflection.AppDomainGetTypes(type => { - var mets = type.GetMethods(); - for (int ii = 0; ii < mets.Length; ii++) - { - var method = mets[ii]; - var attr = method.GetCustomAttributes(true); - for (int jj = 0; jj < attr.Length; jj++) - if (attr[jj] is CommandMethodAttribute att) - { - if (keys.Contains(att.GlobalName)) - msgMod.Add(att.GlobalName); - keys.Add(att.GlobalName); - } - } - }); - var sb = new StringBuilder(); - foreach (string key in msgMod) - sb.AppendLine(key); - if (sb.Length != 0) - { - Env.Printl("当前cad环境加载的多个DLL中存在重复命令将被覆盖:"); - Env.Printl("{"); - Env.Printl(sb.ToString()); - Env.Printl("}"); - } - } -#endif -} \ No newline at end of file diff --git a/src/IFoxCAD.Cad.Shared/Runtime/LateBinding.cs b/src/IFoxCAD.Cad.Shared/Runtime/LateBinding.cs deleted file mode 100644 index 45cafa9..0000000 --- a/src/IFoxCAD.Cad.Shared/Runtime/LateBinding.cs +++ /dev/null @@ -1,83 +0,0 @@ -namespace IFoxCAD.Com; - -/// -/// 后绑代码工具 -/// -public static class LateBinding -{ - /// - /// 从运行对象表 (ROT) 获取指定对象的运行实例 - /// - /// - /// - public static object GetInstance(string appName) - { - return Marshal.GetActiveObject(appName); - } - /// - /// 创建实例 - /// - /// - /// - public static object CreateInstance(string appName) - { - return Activator.CreateInstance(Type.GetTypeFromProgID(appName)); - } - /// - /// 获取或创建实例 - /// - /// - /// - public static object GetOrCreateInstance(string appName) - { - try { return GetInstance(appName); } - catch { return CreateInstance(appName); } - } - /// - /// 释放实例 - /// - /// - public static void ReleaseInstance(this object obj) - { - Marshal.ReleaseComObject(obj); - } - - /// - /// 获取属性 - /// - /// - /// - /// - /// - public static object GetProperty(this object obj, string propName, params object[] parameter) - { - return obj.GetType().InvokeMember(propName, - BindingFlags.GetProperty, - null, obj, parameter); - } - /// - /// 设置属性 - /// - /// - /// - /// - public static void SetProperty(this object obj, string propName, params object[] parameter) - { - obj.GetType().InvokeMember(propName, - BindingFlags.SetProperty, - null, obj, parameter); - } - /// - /// 执行函数 - /// - /// - /// - /// - /// - public static object Invoke(this object obj, string memberName, params object[] parameter) - { - return obj.GetType().InvokeMember(memberName, - BindingFlags.Public | BindingFlags.InvokeMethod, - null, obj, parameter); - } -} \ No newline at end of file diff --git a/src/IFoxCAD.Cad.Shared/Runtime/Log.cs b/src/IFoxCAD.Cad.Shared/Runtime/Log.cs deleted file mode 100644 index 812d694..0000000 --- a/src/IFoxCAD.Cad.Shared/Runtime/Log.cs +++ /dev/null @@ -1,453 +0,0 @@ -namespace IFoxCAD.Cad; - -using System; -using System.Diagnostics; -using System.Threading; -using Exception = Exception; - -#region 写入日志到不同的环境中 -// https://zhuanlan.zhihu.com/p/338492989 -public abstract class LogBase -{ - public abstract void DeleteLog(); - public abstract string[] ReadLog(); - public abstract void WriteLog(string message); -} - -/// -/// 日志输出环境 -/// -public enum LogTarget -{ - /// - /// 文件(包含错误和备注) - /// - File = 1, - /// - /// 文件(不包含错误,也就是只写备注信息) - /// - FileNotException = 2, - /// - /// 数据库 - /// - Database = 4, - /// - /// windows日志 - /// - EventLog = 8, -} - -/// -/// 写入到文件中 -/// -public class FileLogger : LogBase -{ - public override void DeleteLog() - { - File.Delete(LogHelper.LogAddress); - } - public override string[] ReadLog() - { - List lines = new(); - using (var sr = new StreamReader(LogHelper.LogAddress, true/*自动识别文件头*/)) - { - string line; - while ((line = sr.ReadLine()) != null) - lines.Add(line); - } - return lines.ToArray(); - } - public override void WriteLog(string? message) - { - // 把异常信息输出到文件 - var sw = new StreamWriter(LogHelper.LogAddress, true/*当天日志文件存在就追加,否则就创建*/); - sw.Write(message); - sw.Flush(); - sw.Close(); - sw.Dispose(); - } -} - -/// -/// 写入到数据库(暂时不支持) -/// -public class DBLogger : LogBase -{ - public override void DeleteLog() - { - throw new NotImplementedException(); - } - public override string[] ReadLog() - { - throw new NotImplementedException(); - } - public override void WriteLog(string? message) - { - throw new NotImplementedException(); - } -} - -/// -/// 写入到win日志 -/// -public class EventLogger : LogBase -{ - // 需要win权限 - // https://blog.csdn.net/weixin_38208401/article/details/77870909 - // NET50要加 - // https://docs.microsoft.com/en-us/answers/questions/526018/windows-event-log-with-net-5.html - - public string LogName = "IFoxCadLog"; - public override void DeleteLog() - { -#if !NET5_0 && !NET6_0 - if (EventLog.Exists(LogName)) - EventLog.Delete(LogName); -#endif - } - public override string[] ReadLog() - { - List lines = new(); -#if !NET5_0 && !NET6_0 - try - { - EventLog eventLog = new() - { - Log = LogName - }; - foreach (EventLogEntry entry in eventLog.Entries) - lines.Add(entry.Message); - } - catch (System.Security.SecurityException e) - { - throw new Exception("您没有权限读取win日志::" + e.Message); - } -#endif - return lines.ToArray(); - } - public override void WriteLog(string? message) - { -#if !NET5_0 && !NET6_0 - try - { - EventLog eventLog = new() - { - Source = LogName - }; - eventLog.WriteEntry(message, EventLogEntryType.Information); - } - catch (System.Security.SecurityException e) - { - throw new Exception("您没有权限写入win日志::" + e.Message); - } -#endif - } -} - -#endregion - -#region 静态方法 -public static class LogHelper -{ -#pragma warning disable CA2211 // 非常量字段应当不可见 - /// - /// 日志文件完整路径 - /// - public static string? LogAddress; - /// - /// 输出错误信息到日志文件的开关 - /// - public static bool FlagOutFile = false; - /// - /// 输出错误信息到vs输出窗口的开关 - /// - public static bool FlagOutVsOutput = true; -#pragma warning restore CA2211 // 非常量字段应当不可见 - - /// - /// 读写锁 - /// 当资源处于写入模式时,其他线程写入需要等待本次写入结束之后才能继续写入 - /// - static readonly ReaderWriterLockSlim _logWriteLock = new(); - - /// - /// 提供给外部设置log文件保存路径 - /// - /// null就生成默认配置 - public static void OptionFile(string? newlogAddress = null) - { - _logWriteLock.EnterWriteLock();// 写模式锁定 读写锁 - try - { - LogAddress = newlogAddress; - if (string.IsNullOrEmpty(LogAddress)) - LogAddress = GetDefaultOption(DateTime.Now.ToString("yy-MM-dd") + ".log"); - } - finally - { - _logWriteLock.ExitWriteLock();// 解锁 读写锁 - } - } - - /// - /// 输入文件名,获取保存路径的完整路径 - /// - /// 文件名,null获取默认路径 - /// 创建路径 - /// 完整路径 - public static string GetDefaultOption(string fileName, bool createDirectory = true) - { - // 微软回复:静态构造函数只会被调用一次, - // 并且在它执行完成之前,任何其它线程都不能创建这个类的实例或使用这个类的静态成员 - // https://blog.csdn.net/weixin_34204722/article/details/90095812 - var sb = new StringBuilder(); - sb.Append(Environment.CurrentDirectory); - sb.Append("\\ErrorLog"); - - // 新建文件夹 - if (createDirectory) - { - var path = sb.ToString(); - if (!Directory.Exists(path)) - { - // 设置文件夹属性为普通 - Directory.CreateDirectory(path) - .Attributes = FileAttributes.Normal; - } - } - sb.Append('\\'); - sb.Append(fileName); - return sb.ToString(); - } - - public static string WriteLog(this string? message, - LogTarget target = LogTarget.File) - { - if (message == null) - return string.Empty; - return LogAction(null, message, target); - } - - public static string WriteLog(this Exception? exception, - LogTarget target = LogTarget.File) - { - if (exception == null) - return string.Empty; - return LogAction(exception, null, target); - } - - public static string WriteLog(this Exception? exception, string? message, - LogTarget target = LogTarget.File) - { - if (exception == null) - return string.Empty; - return LogAction(exception, message, target); - } - - - /// 错误 - /// 备注信息 - /// 记录方式 - static string LogAction(Exception? ex, - string? message, - LogTarget target) - { - if (ex == null && message == null) - return string.Empty; - - if (LogAddress == null) - { - if (target == LogTarget.File || - target == LogTarget.FileNotException) - OptionFile(); - } - - // 不写入错误 - if (target == LogTarget.FileNotException) - ex = null; - - try - { - _logWriteLock.EnterWriteLock();// 写模式锁定 读写锁 - - var logtxt = new LogTxt(ex, message); - // var logtxtJson = Newtonsoft.Json.JsonConvert.SerializeObject(logtxt, Formatting.Indented); - var logtxtJson = logtxt?.ToString(); - if (logtxtJson == null) - return string.Empty; - - if (FlagOutFile) - { - LogBase? logger; - switch (target) - { - case LogTarget.File: - logger = new FileLogger(); - logger.WriteLog(logtxtJson); - break; - case LogTarget.FileNotException: - logger = new FileLogger(); - logger.WriteLog(logtxtJson); - break; - case LogTarget.Database: - logger = new DBLogger(); - logger.WriteLog(logtxtJson); - break; - case LogTarget.EventLog: - logger = new EventLogger(); - logger.WriteLog(logtxtJson); - break; - } - } - - if (FlagOutVsOutput) - { - Debugx.Printl("错误日志: " + LogAddress); - Debug.Write(logtxtJson); - } - return logtxtJson; - } - finally - { - _logWriteLock.ExitWriteLock();// 解锁 读写锁 - } - } -} -#endregion - -#region 序列化 -[Serializable] -public class LogTxt -{ - public string? 当前时间; - public string? 备注信息; - public string? 异常信息; - public string? 异常对象; - public string? 触发方法; - public string? 调用堆栈; - - public LogTxt() { } - - public LogTxt(Exception? ex, string? message) : this() - { - if (ex == null && message == null) - throw new ArgumentNullException(nameof(ex)); - - // 以不同语言显示日期 - // DateTime.Now.ToString("f", new System.Globalization.CultureInfo("es-ES")) - // DateTime.Now.ToString("f", new System.Globalization.CultureInfo("zh-cn")) - // 为了最小信息熵,所以用这样的格式,并且我喜欢补0 - 当前时间 = DateTime.Now.ToString("yy-MM-dd hh:mm:ss"); - - if (ex != null) - { - 异常信息 = ex.Message; - 异常对象 = ex.Source; - 触发方法 = ex.TargetSite == null ? string.Empty : ex.TargetSite.ToString(); - 调用堆栈 = ex.StackTrace == null ? string.Empty : ex.StackTrace.Trim(); - } - if (message != null) - 备注信息 = message; - } - - /// 为了不引入json的dll,所以这里自己构造 - public override string? ToString() - { - var sb = new StringBuilder(); - sb.Append('{'); - sb.Append(Environment.NewLine); - sb.AppendLine($" \"{nameof(当前时间)}\": \"{当前时间}\""); - sb.AppendLine($" \"{nameof(备注信息)}\": \"{备注信息}\""); - sb.AppendLine($" \"{nameof(异常信息)}\": \"{异常信息}\""); - sb.AppendLine($" \"{nameof(异常对象)}\": \"{异常对象}\""); - sb.AppendLine($" \"{nameof(触发方法)}\": \"{触发方法}\""); - sb.AppendLine($" \"{nameof(调用堆栈)}\": \"{调用堆栈}\""); - sb.Append('}'); - return sb.ToString(); - } -} -#endregion - - -#if false // 最简单的实现 -public static class Log -{ - /// - /// 读写锁 - /// 当资源处于写入模式时,其他线程写入需要等待本次写入结束之后才能继续写入 - /// - static readonly ReaderWriterLockSlim _logWriteLock = new(); - - /// - /// 日志文件完整路径 - /// - static readonly string _logAddress; - - static Log() - { - // 微软回复:静态构造函数只会被调用一次, - // 并且在它执行完成之前,任何其它线程都不能创建这个类的实例或使用这个类的静态成员 - // https://blog.csdn.net/weixin_34204722/article/details/90095812 - var sb = new StringBuilder(); - sb.Append(Environment.CurrentDirectory); - sb.Append("\\ErrorLog"); - - // 新建文件夹 - var path = sb.ToString(); - if (!Directory.Exists(path)) - { - Directory.CreateDirectory(path) - .Attributes = FileAttributes.Normal; // 设置文件夹属性为普通 - } - - sb.Append('\\'); - sb.Append(DateTime.Now.ToString("yy-MM-dd")); - sb.Append(".log"); - _logAddress = sb.ToString(); - } - - - /// - /// 将异常打印到日志文件 - /// - /// 异常 - /// 备注 - /// DEBUG模式打印到vs输出窗口 - public static string? WriteLog(this Exception? ex, - string? remarks = null, - bool printDebugWindow = true) - { - try - { - _logWriteLock.EnterWriteLock();// 写模式锁定 读写锁 - - var logtxt = new LogTxt(ex, remarks); - // var logtxtJson = Newtonsoft.Json.JsonConvert.SerializeObject(logtxt, Formatting.Indented); - var logtxtJson = logtxt.ToString(); - - if (logtxtJson == null) - return string.Empty; - - // 把异常信息输出到文件 - var sw = new StreamWriter(_logAddress, true/*当天日志文件存在就追加,否则就创建*/); - sw.Write(logtxtJson); - sw.Flush(); - sw.Close(); - sw.Dispose(); - - if (printDebugWindow) - { - Debugx.Printl("错误日志: " + _logAddress); - Debug.Write(logtxtJson); - // Debugger.Break(); - // Debug.Assert(false, "终止进程"); - } - return logtxtJson; - } - finally - { - _logWriteLock.ExitWriteLock();// 解锁 读写锁 - } - } -} -#endif \ No newline at end of file diff --git a/src/IFoxCAD.Cad.Shared/Runtime/MethodInfoHelper.cs b/src/IFoxCAD.Cad.Shared/Runtime/MethodInfoHelper.cs deleted file mode 100644 index 1111037..0000000 --- a/src/IFoxCAD.Cad.Shared/Runtime/MethodInfoHelper.cs +++ /dev/null @@ -1,64 +0,0 @@ -namespace IFoxCAD.Cad; - -internal static class MethodInfoHelper -{ -#if cache - private static readonly Dictionary methodDic = new(); -#endif - - /// - /// 执行函数 - /// - /// 函数 - /// 已经外部创建的对象,为空则此处创建 - public static object? Invoke(this MethodInfo methodInfo, ref object? instance) - { - if (methodInfo == null) - throw new ArgumentNullException(nameof(methodInfo)); - - object? result = null; - if (methodInfo.IsStatic) - { - // 新函数指针进入此处 - // 参数数量一定要匹配,为null则参数个数不同导致报错, - // 参数为stirng[],则可以传入object[]代替,其他参数是否还可以实现默认构造? - var args = new List { }; - var paramInfos = methodInfo.GetParameters(); - for (int i = 0; i < paramInfos.Length; i++) - args.Add(null!); - result = methodInfo.Invoke(null, args.ToArray());// 静态调用 - } - else - { -#if cache - // 原命令的函数指针进入此处 - // object instance; - if (methodDic.ContainsKey(methodInfo)) - instance = methodDic[methodInfo]; -#endif - if (instance == null) - { - var reftype = methodInfo.ReflectedType; - if (reftype == null) - return null; - - var fullName = reftype.FullName; // 命名空间+类 - if (fullName == null) - return null; - - var type = reftype.Assembly.GetType(fullName); - if (type == null) - return null; - - instance = Activator.CreateInstance(type);// 构造类 -#if cache - if (!type.IsAbstract)// 无法创建抽象类成员 - methodDic.Add(methodInfo, instance); -#endif - } - if (instance != null) - result = methodInfo.Invoke(instance, null); // 非静态,调用实例化方法 - } - return result; - } -} \ No newline at end of file diff --git a/src/IFoxCAD.Cad.Shared/Runtime/PE/AcadPeInfo.cs b/src/IFoxCAD.Cad.Shared/Runtime/PE/AcadPeInfo.cs deleted file mode 100644 index 3957a68..0000000 --- a/src/IFoxCAD.Cad.Shared/Runtime/PE/AcadPeInfo.cs +++ /dev/null @@ -1,333 +0,0 @@ -using System.Diagnostics; - -namespace IFoxCAD.Cad; - -// 选择模式 -[Flags] -public enum AcadPeEnum : byte -{ - AcadExe = 1, - AccoreDll = 2, - Acdb = 4, - ExeAndCore = AcadExe | AccoreDll, -} - -// 这里的枚举对应 GetMethodException 错误值 -[Flags] -public enum GetMethodErrorNum : byte -{ - Ok = 0, - NoModule = 1, - NoFuncName = 2, -} - -// 自动获取本工程上面的发送命令的接口 -public class AcadPeInfo -{ - #region 静态单例获取exe/dll信息 - static PeInfo? _PeForAcadExe; - public static PeInfo? PeForAcadExe - { - get - { - if (_PeForAcadExe is null) - { - // 获取此acad.exe获取所有的函数名 - var file = Process.GetCurrentProcess().MainModule.FileName; - _PeForAcadExe = new PeInfo(file); - } - return _PeForAcadExe; - } - } - - static PeInfo? _PeForAccoreDll; - public static PeInfo? PeForAccoreDll - { - get - { - if (_PeForAccoreDll is null) - { - // 获取此dll所有的函数名 - var file = Process.GetCurrentProcess().MainModule.FileName; - var dll = Path.GetDirectoryName(file) + "\\accore.dll"; - if (File.Exists(dll))// 08没有,高版本分离的 - _PeForAccoreDll = new PeInfo(dll); - } - return _PeForAccoreDll; - } - } - - static PeInfo? _PeForAcdbDll; - public static PeInfo? PeForAcdbDll - { - get - { - if (_PeForAcdbDll is null) - { - // 获取此dll所有的函数名 - var file = Process.GetCurrentProcess().MainModule.FileName; - var dll = Path.GetDirectoryName(file) + $"\\acdb{Acap.Version.Major}.dll"; - if (File.Exists(dll)) - _PeForAcdbDll = new PeInfo(dll); - } - return _PeForAcdbDll; - } - } - - List? _Methods; // 这个不是静态的 - /// - /// 同名函数指针们 - /// - public List? Methods - { - get - { - if (_Methods is null) - { - _Methods = new(); - - if ((_acadPeEnum & AcadPeEnum.AcadExe) == AcadPeEnum.AcadExe) - GetPeMethod(PeForAcadExe); - if ((_acadPeEnum & AcadPeEnum.AccoreDll) == AcadPeEnum.AccoreDll) - GetPeMethod(PeForAccoreDll); - if ((_acadPeEnum & AcadPeEnum.Acdb) == AcadPeEnum.Acdb) - GetPeMethod(PeForAcdbDll); - } - return _Methods; - } - } - #endregion - - #region 字段/构造 - /// - /// 用于查找PE不带修饰的函数名 - /// - string _findFuncName; - /// - /// 枚举查找对象 - /// - AcadPeEnum _acadPeEnum; - - /// - /// 通过函数名获取指针,指定类型 - /// - /// 不带修饰的函数名 - /// 读取哪个cad内部文件的枚举(目前只支持两个) - public AcadPeInfo(string methodName, AcadPeEnum acadPeEnum) - { - _findFuncName = methodName; - _acadPeEnum = acadPeEnum; - } - - /// - /// 获取CAD的函数指针 - /// - /// 委托 - /// 不带修饰的函数名 - /// 读取哪个cad内部文件的枚举(目前只支持两个) - /// 委托 - public static TDelegate? GetDelegate(string methodName, AcadPeEnum acadPeEnum) - where TDelegate : class - { - return new AcadPeInfo(methodName, acadPeEnum) - .GetDelegate(); - } - #endregion - - #region 方法 - /// - /// 储存旧值<去除修饰函数名(查找的),带修饰函数名们> - /// - static Dictionary> _Dict = new(); - - /// - /// 返回函数指针 - /// - /// Pe信息:可能来自exe/dll - /// 错误信息 - GetMethodErrorNum GetPeMethod(PeInfo? peInfo) - { - if (peInfo == null) - return GetMethodErrorNum.NoFuncName;// cad08需要检查 AccoreDll 的时候跳过 - - var identifyStr = _findFuncName + ";" + peInfo.FullName; - if (_Dict.ContainsKey(identifyStr))// 如果已经找过,直接返回 - { - _Methods = _Dict[identifyStr]; - } - else - { - _Methods ??= new(); - try - { - PeFunction.Finds(peInfo, _findFuncName, _Methods); - if (_Methods.Count != 0)// 此时从不含有 - _Dict.Add(identifyStr, _Methods); - } - catch (GetPeMethodException ex) - { return (GetMethodErrorNum)ex.ErrorNum; } - } - return GetMethodErrorNum.Ok; - } - - /// - /// 转为委托 - /// - /// 委托对象 - /// - public TDelegate? GetDelegate() where TDelegate : class - { - if (Methods is null || Methods.Count == 0) - return null; - - TDelegate? func = null; - - /* - * 0x01 - * 这里永远不报错,但是不代表不会出错. - * 调用C盘exe/dll时需要权限, - * 所以会出现:[DLLImport]可以,直接运行cad也可以,但是调试不行. - * 此时可以提权vs再调试,有时候会出现:调试不显示东西,但是运行是对的. - * - * 0x02 - * 出错时候用完整的修饰名 - * - * 0x03 - * 这里可能同时存在acad.exe和accore.dll相同指针? - * 所以我是用排序方法找最短的指针,所以它是第First个. - */ - - // 排序,最少长度原则本身就是让完全相同字符串在最前面 - // 这里替换为有序哈希,因为我总是需要不带修饰的返回函数,所以是排序长度的第一个 - _Methods = _Methods.OrderBy(str => str.CName?.Length) - .ThenBy(str => str.MethodName.Length) - .ToList(); - - func = Marshal.GetDelegateForFunctionPointer(Methods.First().GetProcAddress(), typeof(TDelegate)) as TDelegate; - return func; - } - #endregion -} - -/// -/// 通过名字查找exe/dll内所有名字 -/// -public class PeFunction -{ - #region 字段/构造 - string? _CName; - /// - /// 纯c语言名 - /// - public string? CName - { - get - { - if (_CName is null && MethodName is not null) - { - _CName = MethodName.Replace("?", string.Empty); // 剔除cpp前缀 - int num = _CName.IndexOf("@"); - if (num > -1) - _CName = _CName.Substring(0, num); // 剔除参数部分 - } - return _CName; - } - } - - /// - /// 模块文件路径 - /// - public string? ModuleFullName; - /// - /// 模块指针 - /// - public IntPtr ModuleIntPtr; - /// - /// 函数名 - /// - public string MethodName; - /// - /// 通过名字查找exe/dll内所有名字 - /// - /// 没修饰的方法名 - public PeFunction(string methodName) - { - MethodName = methodName; - } - #endregion - - /// - /// 获取函数指针 - /// - public IntPtr GetProcAddress() - { - return WindowsAPI.GetProcAddress(ModuleIntPtr, MethodName); - } - - /// - /// 通过名字查找exe/dll内所有名字 - /// - /// pe结构 - /// 用于查找的方法名 - /// 返回函数集合 - public static void Finds(PeInfo peInfo, - string findFuncName, - List funcAdress_Out) - { - if (findFuncName == null) - throw new GetPeMethodException(2, "没有找到对应的函数:" + findFuncName); - - var peModuleFullName = peInfo.FullName; - if (peModuleFullName == null) - throw new GetPeMethodException(1, "找不到模块:" + peModuleFullName + "当前程序没有加载这个东西?"); - var hModule = WindowsAPI.GetModuleHandle(peModuleFullName); // 执行前必须加载了先,acad.exe/accore.dll - if (hModule == IntPtr.Zero) - throw new GetPeMethodException(1, "找不到模块:" + peModuleFullName + "当前程序没有加载这个东西?"); - - // 遍历函数接口名单 - var names = peInfo.ExportDirectory?.FunctionNames(); - if (names == null) - throw new ArgumentException(nameof(names)); - - foreach (var name in names) - { - if (name.Contains(findFuncName))// 这里是名称含有,不是容器含有 - { - var fn = new PeFunction(name) - { - ModuleFullName = peModuleFullName, - ModuleIntPtr = hModule - }; - funcAdress_Out.Add(fn); - } - } - } -} - - - -/// -/// 错误信息 -/// -public class GetPeMethodException : ApplicationException -{ - public int ErrorNum; - public string? ErrorMsg; - public Exception? InnerException1; - - public GetPeMethodException(string msg) : base(msg) - { - ErrorMsg = msg; - } - - public GetPeMethodException(int errorNum, string msg) : base(msg) - { - ErrorNum = errorNum; - } - - public GetPeMethodException(string msg, Exception innerException) : base(msg, innerException) - { - InnerException1 = innerException; - ErrorMsg = msg; - } -} \ No newline at end of file diff --git a/src/IFoxCAD.Cad.Shared/Runtime/PE/DBmod.cs b/src/IFoxCAD.Cad.Shared/Runtime/PE/DBmod.cs deleted file mode 100644 index 7502e25..0000000 --- a/src/IFoxCAD.Cad.Shared/Runtime/PE/DBmod.cs +++ /dev/null @@ -1,92 +0,0 @@ -namespace IFoxCAD.Cad; - -/// -/// 获取数据库修改状态 -/// -/// 相关链接 -/// -[Flags] -public enum DBmod : short -{ - [Description("数据库冇修改")] - DatabaseNoModifies = 0, - [Description("数据库有修改")] - Database = 1, - [Description("变量有修改")] - Value = 4, - [Description("窗口有修改")] - Window = 8, - [Description("视图有修改")] - View = 16, - [Description("字段有修改")] - Field = 32 -} - -public class DBmodEx -{ - public static DBmod DBmod => (DBmod)Env.GetVar("dbmod"); - - delegate long DelegateAcdbSetDbmod(IntPtr db, DBmod newValue); - static DelegateAcdbSetDbmod? acdbSetDbmod;//别改名称 - - public static long AcdbSetDbmod(IntPtr db, DBmod newValue) - { - acdbSetDbmod ??= AcadPeInfo.GetDelegate( - nameof(acdbSetDbmod), AcadPeEnum.Acdb); - if (acdbSetDbmod is null) - return -1; - return acdbSetDbmod.Invoke(db, newValue);// 调用方法 - } - - /// - /// Dbmod 不被修改的任务 - /// - /// - public static void DBmodTask(Action action) - { - var dm = Acap.DocumentManager; - if (dm.Count == 0) - return; - var doc = dm.MdiActiveDocument; - if (doc is null) - return; - - var bak = DBmod; - action.Invoke(); - if (bak == DBmod.DatabaseNoModifies && DBmod != DBmod.DatabaseNoModifies) - AcdbSetDbmod(doc.Database.UnmanagedObject, DBmod.DatabaseNoModifies); - } - - static bool _flag = true; - /// - /// 请在无法处理的初始化才使用它 - /// (源泉在初始化的时候进行了修改数据库,所以必须要用一个新线程等待lisp执行完成才可以) - /// - public static void DatabaseNoModifies() - { - if (_flag)// 仅执行一次,在初始化时候 - { - var dm = Acap.DocumentManager; - if (dm.Count == 0) - return; - var doc = dm.MdiActiveDocument; - if (doc is null) - return; - - if (DBmod != DBmod.DatabaseNoModifies) - AcdbSetDbmod(doc.Database.UnmanagedObject, DBmod.DatabaseNoModifies); - _flag = false; - } - } - - - //[CommandMethod(nameof(TestCmd_AcdbSetDbmodChange))] - //public void TestCmd_AcdbSetDbmodChange() - //{ - // DBmodTask(() => { - // using DBTrans tr = new(); - // Line line = new(new Point3d(0, 0, 0), new Point3d(1, 1, 0)); - // tr.CurrentSpace.AddEntity(line); - // }); - //} -} \ No newline at end of file diff --git a/src/IFoxCAD.Cad.Shared/Runtime/PE/PostCmd.cs b/src/IFoxCAD.Cad.Shared/Runtime/PE/PostCmd.cs deleted file mode 100644 index cd0b39e..0000000 --- a/src/IFoxCAD.Cad.Shared/Runtime/PE/PostCmd.cs +++ /dev/null @@ -1,199 +0,0 @@ -namespace IFoxCAD.Cad; - -public class PostCmd -{ - /* - * #if NET35 || NET40 - * [DllImport("acad.exe", EntryPoint = "acedCmd", CharSet = CharSet.Auto, CallingConvention = CallingConvention.Cdecl)] - * #else - * // cad2015的AcedCmd改为AcedCmdS.参数也改了,不过不影响pe读取,accore.dll是2013版本后才开始改 - * [DllImport("accore.dll", EntryPoint = "acedCmdS", CharSet = CharSet.Auto, CallingConvention = CallingConvention.Cdecl)] - * static extern int AcedCmd(IntPtr rbp, bool forFutureUse = false, IntPtr pForFutureUse = IntPtr.Zero); - * #endif - * static extern int AcedCmd(IntPtr rbp); - * public static int AcedCmd(ResultBuffer args) - * { - * if (Acap.DocumentManager.IsApplicationContext) - * return 0; - * else - * return AcedCmd(args.UnmanagedObject); - * } - */ - delegate int DelegateAcedCmd(IntPtr parameter); - static DelegateAcedCmd? acedCmd;//nameof 别改名称 - /// - /// 发送命令(同步)如果2015.+这里报错,那么表示vs需要提权测试 - /// - static PromptStatus AcedCmd(ResultBuffer args) - { - if (Acap.DocumentManager.IsApplicationContext) - return 0; - if (acedCmd is null) - { - string str = nameof(acedCmd); - if (Acap.Version.Major >= 20)// 2015.+ - str += "S"; - - acedCmd = AcadPeInfo.GetDelegate( - str, AcadPeEnum.ExeAndCore); - } - if (acedCmd is null) - return 0; - - var result = (PromptStatus)acedCmd.Invoke(args.UnmanagedObject); - if (result != PromptStatus.OK) - throw new ArgumentException("发送命令出错,是否vs权限不足?"); - return result; - } - - /* - * [DllImport("accore.dll", EntryPoint = "acedCommand")] - * static extern int AcedCommand(IntPtr vlist); - */ - delegate int DelegateAcedCommand(IntPtr parameter); - static DelegateAcedCommand? acedCommand;//nameof 别改名称 - /// - /// 发送命令(同步) - /// - static PromptStatus AcedCommand(IntPtr args) - { - acedCommand ??= AcadPeInfo.GetDelegate( - nameof(acedCommand), AcadPeEnum.ExeAndCore); - if (acedCommand is null) - return PromptStatus.Error; - return (PromptStatus)acedCommand.Invoke(args);// 调用方法 - } - - /* - * [DllImport("acad.exe", CharSet = CharSet.Auto, CallingConvention = CallingConvention.Cdecl, - * EntryPoint = "?acedPostCommand@@YAHPB_W@Z")] - * public static extern int AcedPostCommand(string strExpr); - */ - delegate int DelegateAcedPostCommand(byte[] parameter); - static DelegateAcedPostCommand? acedPostCommand;//nameof 别改名称 - /// - /// 发送命令(同步) - /// 这个可以在多线程发送 - /// - static PromptStatus AcedPostCommand(string args) - { - acedPostCommand ??= AcadPeInfo.GetDelegate( - nameof(acedPostCommand), AcadPeEnum.ExeAndCore); - - // 不然到CAD之后会乱码 - byte[] bytes = Encoding.Unicode.GetBytes(args); - if (acedPostCommand is null) - return PromptStatus.Error; - return (PromptStatus)acedPostCommand.Invoke(bytes);// 调用方法 - } - - delegate int DelegateAcedInvoke(byte[] parameter); - static DelegateAcedInvoke? acedInvoke;//nameof 别改名称 - /// - /// 发送命令(同步) - /// - static PromptStatus AcedInvoke(string args) - { - acedInvoke ??= AcadPeInfo.GetDelegate( - nameof(acedInvoke), AcadPeEnum.ExeAndCore); - - // 不然到CAD之后会乱码 - byte[] bytes = Encoding.Unicode.GetBytes(args); - - if (acedInvoke is null) - return PromptStatus.Error; - return (PromptStatus)acedInvoke.Invoke(bytes);// 调用方法 - } - - /// - /// 发送命令(异步)+CommandFlags.Session可以同步发送 - /// - static void AsyncCommand(string args) - { - object[] commandArray = { args + "\n" }; -#if zcad - var com = Acap.ZcadApplication; -#else - var com = Acap.AcadApplication; -#endif - // activeDocument 加载lisp第二个文档有问题,似乎要切换了才能 - var doc = com.GetType() - .InvokeMember("ActiveDocument", BindingFlags.GetProperty, null, com, null); - doc?.GetType() - .InvokeMember("SendCommand", BindingFlags.InvokeMethod, null, doc, commandArray);// 返回值是null - } - - public enum RunCmdFlag : byte - { - AcedCmd = 1, - AcedCommand = 2, - AcedPostCommand = 4, - AcedInvoke = 8, - SendStringToExecute = 16, - AsyncCommand = 32, - } - - /* - * 发送命令会记录在命令历史 - * 发送lisp的(command "xx")就不会 - */ - public static PromptStatus SendCommand(ResultBuffer args) - { - return AcedCmd(args); - } - public static PromptStatus SendCommand(IntPtr args) - { - return AcedCommand(args); - } - public static PromptStatus SendCommand(string args, RunCmdFlag flag) - { - PromptStatus ret = PromptStatus.OK; - if (!Acap.DocumentManager.IsApplicationContext) - { - if ((flag & RunCmdFlag.AcedCmd) == RunCmdFlag.AcedCmd) - { - using ResultBuffer rb = new() - { - new((int)LispDataType.Text, args), - }; - ret = SendCommand(rb); - } - if ((flag & RunCmdFlag.AcedCommand) == RunCmdFlag.AcedCommand) - { - // 此处是这样转换吗? - using ResultBuffer rb = new() - { - new((int)LispDataType.Text, args), - }; - ret = SendCommand(rb.UnmanagedObject); - } - if ((flag & RunCmdFlag.AcedPostCommand) == RunCmdFlag.AcedPostCommand) - { - ret = AcedPostCommand(args); - } - if ((flag & RunCmdFlag.AcedInvoke) == RunCmdFlag.AcedInvoke) - { - ret = AcedInvoke(args); - } - } - else - { - var dm = Acap.DocumentManager; - var doc = dm.MdiActiveDocument; - if (doc == null) - return PromptStatus.Error; - - if ((flag & RunCmdFlag.SendStringToExecute) == RunCmdFlag.SendStringToExecute) - { - doc.SendStringToExecute(args, true, false, false); - } - if ((flag & RunCmdFlag.AsyncCommand) == RunCmdFlag.AsyncCommand) - { - // 此处+CommandFlags.Session可以同步发送,bo命令可以,其他是否可以? - // 仿人工输入,像lisp一样可以直接发送关键字 - AsyncCommand(args); - } - } - return ret; - } -} \ No newline at end of file diff --git a/src/IFoxCAD.Cad.Shared/Runtime/PE/ProgramPE.cs b/src/IFoxCAD.Cad.Shared/Runtime/PE/ProgramPE.cs deleted file mode 100644 index e1f834a..0000000 --- a/src/IFoxCAD.Cad.Shared/Runtime/PE/ProgramPE.cs +++ /dev/null @@ -1,1567 +0,0 @@ -namespace IFoxCAD.Cad; - -using System; -using System.Collections; -using System.Collections.Generic; -using System.Data; -using System.IO; -using System.Text; - - -/* 来源 https://blog.csdn.net/zgke/article/details/2955560 我在他基础上面增加了X64的处理 - * 调用例子 - static void Main(string[] args) - { - var path = @"C:\Program Files\Autodesk\AutoCAD 2021\acad.exe"; - // path = @"G:\AutoCAD 2008\acad.exe"; - - var pe = new JoinBox.BasalCurrency.PeInfo(path); - - // 输出所有的函数名 - var sb = new StringBuilder(); - foreach (var item in pe.ExportDirectory.NameList) - { - sb.Append(Environment.NewLine); - var str = System.Text.Encoding.Default.GetString(item as byte[]); - sb.Append(str); - } - Debugx.Printl(sb.ToString()); - - // 原作者的封装 - var ss = pe.GetPETable(); - foreach (var item in ss.Tables) - { - } - } -*/ - -/// -/// 微软软件结构PE信息 -/// -public class PeInfo -{ - #region 成员 - /// - /// 获取是否正常打开文件 - /// - public bool OpenFile { get; private set; } = false; - public DosHeader? DosHeader { get; private set; } - public DosStub? DosStub { get; private set; } - public PEHeader? PEHeader { get; private set; } - public OptionalHeader? OptionalHeader { get; private set; } - public OptionalDirAttrib? OptionalDirAttrib { get; private set; } - public SectionTable? SectionTable { get; private set; } - /// - /// 函数接口名单 - /// - public ExportDirectory? ExportDirectory { get; private set; } - public ImportDirectory? ImportDirectory { get; private set; } - public ResourceDirectory? ResourceDirectory { get; private set; } - /// - /// PE文件完整路径 - /// - public string? FullName; - - bool _IsX86 = true; - - /// - /// 全部文件数据 - /// - readonly byte[]? _PEFileByte; - /// - /// 文件读取的位置 - /// - long _PEFileIndex = 0; - #endregion - - #region 构造 - public PeInfo(string fullName) - { - if (fullName is null) - throw new ArgumentException(nameof(fullName)); ; - - FullName = fullName; - FileStream? file = null; - OpenFile = false; - try - { - // 文件流 - file = new FileStream(fullName, FileMode.Open, FileAccess.Read, FileShare.ReadWrite);// FileShare才能进c盘 - _PEFileByte = new byte[file.Length]; - file.Read(_PEFileByte, 0, _PEFileByte.Length); - LoadFile(); - OpenFile = true; - } - catch (Exception) { throw; } - finally - { - file?.Close(); - } - } - #endregion - - #region 读表方法 - /// - /// 开始读取 - /// - private void LoadFile() - { - LoadDosHeader(); // 获取DOS头,为了兼容DOS所以首先处理这个,然后才到PE头 - LoadDosStub(); // 获取DOS的身体 - LoadPEHeader(); // PE头 - LoadOptionalHeader(); // PE头扩展 - LoadOptionalDirAttrib(); // 获取选项目录属性 - LoadSectionTable(); // 获取节表 - LoadExportDirectory(); // 获取输出表 - LoadImportDirectory(); // 获取输入表 - LoadResourceDirectory(); // 获取资源目录 - } - - /// - /// 获得DOS头 - /// - private void LoadDosHeader() - { - DosHeader = new DosHeader - { - FileStarIndex = _PEFileIndex - }; - Loadbyte(ref DosHeader.e_magic); - Loadbyte(ref DosHeader.e_cblp); - Loadbyte(ref DosHeader.e_cp); - Loadbyte(ref DosHeader.e_crlc); - Loadbyte(ref DosHeader.e_cparhdr); - Loadbyte(ref DosHeader.e_minalloc); - Loadbyte(ref DosHeader.e_maxalloc); - Loadbyte(ref DosHeader.e_ss); - Loadbyte(ref DosHeader.e_sp); - Loadbyte(ref DosHeader.e_csum); - Loadbyte(ref DosHeader.e_ip); - Loadbyte(ref DosHeader.e_cs); - Loadbyte(ref DosHeader.e_rva); - Loadbyte(ref DosHeader.e_fg); - Loadbyte(ref DosHeader.e_bl1); - Loadbyte(ref DosHeader.e_oemid); - Loadbyte(ref DosHeader.e_oeminfo); - Loadbyte(ref DosHeader.e_bl2); - Loadbyte(ref DosHeader.e_PESTAR); - - DosHeader.FileEndIndex = _PEFileIndex; - } - - /// - /// 获得DOS SUB字段 - /// - private void LoadDosStub() - { - if (DosHeader is null) - return; - - long Size = GetLong(DosHeader.e_PESTAR) - _PEFileIndex; // 获得SUB的大小 - DosStub = new DosStub(Size) - { - FileStarIndex = _PEFileIndex - }; - Loadbyte(ref DosStub.DosStubData); - DosStub.FileEndIndex = _PEFileIndex; - } - - /// - /// 获得PE的文件头 - /// - /// - private void LoadPEHeader() - { - PEHeader = new PEHeader - { - FileStarIndex = _PEFileIndex - }; - Loadbyte(ref PEHeader.Header); - Loadbyte(ref PEHeader.Machine);// [76 1]==[0x4C 1]是x32, - _IsX86 = PEHeader.Machine[0] == 0x4c && PEHeader.Machine[1] == 0x1; - // (PEHeader.Machine[0] == 0x64 && PEHeader.Machine[1] == 0x86)// x64 - Loadbyte(ref PEHeader.NumberOfSections); - Loadbyte(ref PEHeader.TimeDateStamp); - Loadbyte(ref PEHeader.PointerToSymbolTable); - Loadbyte(ref PEHeader.NumberOfSymbols); - Loadbyte(ref PEHeader.SizeOfOptionalHeader); - Loadbyte(ref PEHeader.Characteristics); - PEHeader.FileEndIndex = _PEFileIndex; - } - - /// - /// 获得OPTIONAL PE扩展属性 - /// - /// - private void LoadOptionalHeader() - { - // 这里必须通过PE文件来判断它是一个什么架构,从而使用x86-x64 - OptionalHeader = new OptionalHeader(_IsX86) - { - FileStarIndex = _PEFileIndex - }; - Loadbyte(ref OptionalHeader.Magic); - Loadbyte(ref OptionalHeader.MajorLinkerVersion); - Loadbyte(ref OptionalHeader.MinorLinkerVersion); - Loadbyte(ref OptionalHeader.SizeOfCode); - Loadbyte(ref OptionalHeader.SizeOfInitializedData); - Loadbyte(ref OptionalHeader.SizeOfUninitializedData); - Loadbyte(ref OptionalHeader.AddressOfEntryPoint); - Loadbyte(ref OptionalHeader.BaseOfCode); - Loadbyte(ref OptionalHeader.ImageBase); - Loadbyte(ref OptionalHeader.BaseOfData); - Loadbyte(ref OptionalHeader.SectionAlignment); - Loadbyte(ref OptionalHeader.FileAlignment); - - Loadbyte(ref OptionalHeader.MajorOperatingSystemVersion); - Loadbyte(ref OptionalHeader.MinorOperatingSystemVersion); - Loadbyte(ref OptionalHeader.MajorImageVersion); - Loadbyte(ref OptionalHeader.MinorImageVersion); - Loadbyte(ref OptionalHeader.MajorSubsystemVersion); - Loadbyte(ref OptionalHeader.MinorSubsystemVersion); - Loadbyte(ref OptionalHeader.Win32VersionValue); - Loadbyte(ref OptionalHeader.SizeOfImage); - Loadbyte(ref OptionalHeader.SizeOfHeards); - Loadbyte(ref OptionalHeader.CheckSum); - Loadbyte(ref OptionalHeader.Subsystem); - Loadbyte(ref OptionalHeader.DLLCharacteristics); - Loadbyte(ref OptionalHeader.SizeOfStackReserve); - Loadbyte(ref OptionalHeader.SizeOfStackCommit); - Loadbyte(ref OptionalHeader.SizeOfHeapReserve); - Loadbyte(ref OptionalHeader.SizeOfHeapCommit); - Loadbyte(ref OptionalHeader.LoaderFlags); - Loadbyte(ref OptionalHeader.NumberOfRvaAndSizes); - - OptionalHeader.FileEndIndex = _PEFileIndex; - } - - /// - /// 获取目录表 - /// - /// - private void LoadOptionalDirAttrib() - { - if (OptionalHeader is null) - return; - - OptionalDirAttrib = new OptionalDirAttrib - { - FileStarIndex = _PEFileIndex - }; - - long DirCount = GetLong(OptionalHeader.NumberOfRvaAndSizes);// 这里导致无法使用64位 - for (int i = 0; i != DirCount; i++) - { - OptionalDirAttrib.DirAttrib? directAttrib = new(); - Loadbyte(ref directAttrib.DirRva); - Loadbyte(ref directAttrib.DirSize); - OptionalDirAttrib.DirByte.Add(directAttrib); - } - OptionalDirAttrib.FileEndIndex = _PEFileIndex; - } - - /// - /// 获取节表 - /// - private void LoadSectionTable() - { - if (PEHeader is null) - return; - - SectionTable = new SectionTable(); - long Count = GetLong(PEHeader.NumberOfSections); - SectionTable.FileStarIndex = _PEFileIndex; - for (long i = 0; i != Count; i++) - { - var Section = new SectionTable.SectionData(); - - Loadbyte(ref Section.SectName); - Loadbyte(ref Section.VirtualAddress); - Loadbyte(ref Section.SizeOfRawDataRVA); - Loadbyte(ref Section.SizeOfRawDataSize); - Loadbyte(ref Section.PointerToRawData); - Loadbyte(ref Section.PointerToRelocations); - Loadbyte(ref Section.PointerToLinenumbers); - Loadbyte(ref Section.NumberOfRelocations); - Loadbyte(ref Section.NumberOfLinenumbers); - Loadbyte(ref Section.Characteristics); - SectionTable.Section.Add(Section); - } - SectionTable.FileEndIndex = _PEFileIndex; - } - - /// - /// 读取输出表 - /// - private void LoadExportDirectory() - { - if (OptionalDirAttrib is null) - return; - if (OptionalDirAttrib.DirByte.Count == 0) - return; - - if (OptionalDirAttrib.DirByte[0] is not OptionalDirAttrib.DirAttrib exporRVA || - GetLong(exporRVA.DirRva) == 0) - return; - - long exporAddress = GetLong(exporRVA.DirRva); // 获取的位置 - ExportDirectory = new ExportDirectory(); - - if (SectionTable is null) - return; - - for (int i = 0; i != SectionTable.Section.Count; i++) // 循环节表 - { - if (SectionTable.Section[i] is not SectionTable.SectionData sect) - continue; - - long starRva = GetLong(sect.SizeOfRawDataRVA); - long endRva = GetLong(sect.SizeOfRawDataSize); - - if (exporAddress >= starRva && exporAddress < starRva + endRva) - { - _PEFileIndex = exporAddress - GetLong(sect.SizeOfRawDataRVA) + GetLong(sect.PointerToRawData); - - ExportDirectory.FileStarIndex = _PEFileIndex; - ExportDirectory.FileEndIndex = _PEFileIndex + GetLong(exporRVA.DirSize); - - Loadbyte(ref ExportDirectory.Characteristics); - Loadbyte(ref ExportDirectory.TimeDateStamp); - Loadbyte(ref ExportDirectory.MajorVersion); - Loadbyte(ref ExportDirectory.MinorVersion); - Loadbyte(ref ExportDirectory.Name); - Loadbyte(ref ExportDirectory.Base); - Loadbyte(ref ExportDirectory.NumberOfFunctions); - Loadbyte(ref ExportDirectory.NumberOfNames); - Loadbyte(ref ExportDirectory.AddressOfFunctions); - Loadbyte(ref ExportDirectory.AddressOfNames); - Loadbyte(ref ExportDirectory.AddressOfNameOrdinals); - - _PEFileIndex = GetLong(ExportDirectory.AddressOfFunctions) - GetLong(sect.SizeOfRawDataRVA) + GetLong(sect.PointerToRawData); - long endIndex = GetLong(ExportDirectory.AddressOfNames) - GetLong(sect.SizeOfRawDataRVA) + GetLong(sect.PointerToRawData); - long numb = (endIndex - _PEFileIndex) / 4; - for (long z = 0; z != numb; z++) - { - byte[] Data = new byte[4]; - Loadbyte(ref Data); - ExportDirectory.AddressOfFunctionsList.Add(Data); - } - - _PEFileIndex = endIndex; - endIndex = GetLong(ExportDirectory.AddressOfNameOrdinals) - GetLong(sect.SizeOfRawDataRVA) + GetLong(sect.PointerToRawData); - numb = (endIndex - _PEFileIndex) / 4; - for (long z = 0; z != numb; z++) - { - byte[] Data = new byte[4]; - Loadbyte(ref Data); - ExportDirectory.AddressOfNamesList.Add(Data); - } - - _PEFileIndex = endIndex; - endIndex = GetLong(ExportDirectory.Name) - GetLong(sect.SizeOfRawDataRVA) + GetLong(sect.PointerToRawData); - numb = (endIndex - _PEFileIndex) / 2; - for (long z = 0; z != numb; z++) - { - byte[] Data = new byte[2]; - Loadbyte(ref Data); - ExportDirectory.AddressOfNameOrdinalsList.Add(Data); - } - - _PEFileIndex = endIndex; - - if (_PEFileByte is not null) - { - long ReadIndex = 0; - while (true) - { - if (_PEFileByte[_PEFileIndex + ReadIndex] == 0) - { - if (_PEFileByte[_PEFileIndex + ReadIndex + 1] == 0) - break; - - var Date = new byte[ReadIndex]; - Loadbyte(ref Date); - ExportDirectory.FunctionNamesByte.Add(Date); - - _PEFileIndex++; - ReadIndex = 0; - } - ReadIndex++; - } - } - break; - } - } - } - - /// - /// 读取输入表 - /// - private void LoadImportDirectory() - { - if (OptionalDirAttrib is null) - return; - if (OptionalDirAttrib.DirByte.Count < 1) - return; - if (OptionalDirAttrib.DirByte[1] is not OptionalDirAttrib.DirAttrib ImporRVA) - return; - - long ImporAddress = GetLong(ImporRVA.DirRva); // 获取的位置 - if (ImporAddress == 0) - return; - long ImporSize = GetLong(ImporRVA.DirSize); // 获取大小 - - ImportDirectory = new ImportDirectory(); - - long SizeRva = 0; - long PointerRva = 0; - - long StarRva = 0; - long EndRva = 0; - - #region 获取位置 - if (SectionTable is null) - return; - for (int i = 0; i != SectionTable.Section.Count; i++) // 循环节表 - { - if (SectionTable.Section[i] is not SectionTable.SectionData Sect) - continue; - - StarRva = GetLong(Sect.SizeOfRawDataRVA); - EndRva = GetLong(Sect.SizeOfRawDataSize); - - if (ImporAddress >= StarRva && ImporAddress < StarRva + EndRva) - { - SizeRva = GetLong(Sect.SizeOfRawDataRVA); - PointerRva = GetLong(Sect.PointerToRawData); - _PEFileIndex = ImporAddress - SizeRva + PointerRva; - - ImportDirectory.FileStarIndex = _PEFileIndex; - ImportDirectory.FileEndIndex = _PEFileIndex + ImporSize; - break; - } - } - - if (SizeRva == 0 && PointerRva == 0) - return; - #endregion - - #region 输入表结构 - while (true) - { - var import = new ImportDirectory.ImportDate(); - Loadbyte(ref import.OriginalFirstThunk); - Loadbyte(ref import.TimeDateStamp); - Loadbyte(ref import.ForwarderChain); - Loadbyte(ref import.Name); - Loadbyte(ref import.FirstThunk); - - if (GetLong(import.Name) == 0) - break; - ImportDirectory.ImportList.Add(import); // 添加 - } - #endregion - - - #region 获取输入DLL名称 - for (int z = 0; z != ImportDirectory.ImportList.Count; z++) // 获取引入DLL名字 - { - if (ImportDirectory.ImportList[z] is not ImportDirectory.ImportDate Import) - continue; - - long ImportDLLName = GetLong(Import.Name) - SizeRva + PointerRva; - _PEFileIndex = ImportDLLName; - long ReadCount = 0; - while (_PEFileByte is not null) // 获取引入名 - { - if (_PEFileByte[_PEFileIndex + ReadCount] == 0) - { - Import.DLLName = new byte[ReadCount]; - Loadbyte(ref Import.DLLName); - break; - } - ReadCount++; - } - } - #endregion - - #region 获取引入方法 先获取地址 然后获取名字和头 - for (int z = 0; z != ImportDirectory.ImportList.Count; z++) // 获取引入方法 - { - if (ImportDirectory.ImportList[z] is not ImportDirectory.ImportDate import) - continue; - - long importDLLName = GetLong(import.OriginalFirstThunk) - SizeRva + PointerRva; - _PEFileIndex = importDLLName; - while (true) - { - var function = new ImportDirectory.ImportDate.FunctionList(); - Loadbyte(ref function.OriginalFirst); - - long loadIndex = GetLong(function.OriginalFirst); - if (loadIndex == 0) - break; - long oldIndex = _PEFileIndex; - - _PEFileIndex = loadIndex - SizeRva + PointerRva; - - if (loadIndex >= StarRva && loadIndex < StarRva + EndRva) // 发现有些数字超级大 - { - int ReadCount = 0; - - while (_PEFileByte is not null) - { - if (ReadCount == 0) - Loadbyte(ref function.FunctionHead); - if (_PEFileByte[_PEFileIndex + ReadCount] == 0) - { - byte[] FunctionName = new byte[ReadCount]; - Loadbyte(ref FunctionName); - function.FunctionName = FunctionName; - - break; - } - ReadCount++; - } - } - else - { - function.FunctionName = new byte[1]; - } - - _PEFileIndex = oldIndex; - import.DLLFunctionList.Add(function); - } - } - #endregion - } - - /// - /// 读取资源表 - /// - private void LoadResourceDirectory() - { - #region 初始化 - if (OptionalDirAttrib is null || OptionalDirAttrib.DirByte.Count < 3) - return; - if (OptionalDirAttrib.DirByte[2] is not OptionalDirAttrib.DirAttrib ImporRVA) - return; - - long ImporAddress = GetLong(ImporRVA.DirRva); // 获取的位置 - if (ImporAddress == 0) - return; - long ImporSize = GetLong(ImporRVA.DirSize); // 获取大小 - - ResourceDirectory = new ResourceDirectory(); - - long SizeRva = 0; - long PointerRva = 0; - long StarRva = 0; - long PEIndex = 0; - #endregion - - #region 获取位置 - if (SectionTable is null) - return; - - for (int i = 0; i != SectionTable.Section.Count; i++) // 循环节表 - { - if (SectionTable.Section[i] is not SectionTable.SectionData sect) - continue; - - StarRva = GetLong(sect.SizeOfRawDataRVA); - var EndRva = GetLong(sect.SizeOfRawDataSize); - if (ImporAddress >= StarRva && ImporAddress < StarRva + EndRva) - { - SizeRva = GetLong(sect.SizeOfRawDataRVA); - PointerRva = GetLong(sect.PointerToRawData); - _PEFileIndex = ImporAddress - SizeRva + PointerRva; - PEIndex = _PEFileIndex; - ResourceDirectory.FileStarIndex = _PEFileIndex; - ResourceDirectory.FileEndIndex = _PEFileIndex + ImporSize; - break; - } - } - - if (SizeRva == 0 && PointerRva == 0) - return; - #endregion - - AddResourceNode(ResourceDirectory, PEIndex, 0, StarRva); - } - - /// - /// 添加资源节点 - /// - /// - /// - /// - /// - private void AddResourceNode(ResourceDirectory node, long PEIndex, long RVA, long resourSectRva) - { - _PEFileIndex = PEIndex + RVA; // 设置位置 - Loadbyte(ref node.Characteristics); - Loadbyte(ref node.TimeDateStamp); - Loadbyte(ref node.MajorVersion); - Loadbyte(ref node.MinorVersion); - Loadbyte(ref node.NumberOfNamedEntries); - Loadbyte(ref node.NumberOfIdEntries); - - long NameRVA = GetLong(node.NumberOfNamedEntries); - for (int i = 0; i != NameRVA; i++) - { - var Entry = new ResourceDirectory.DirectoryEntry(); - Loadbyte(ref Entry.Name); - Loadbyte(ref Entry.Id); - byte[] temp = new byte[2]; - temp[0] = Entry.Name[0]; - temp[1] = Entry.Name[1]; - - if (_PEFileByte is null) - return; - - long NameIndex = GetLong(temp) + PEIndex; - temp[0] = _PEFileByte[NameIndex + 0]; - temp[1] = _PEFileByte[NameIndex + 1]; - - long NameCount = GetLong(temp); - node.Name = new byte[NameCount * 2]; - - for (int z = 0; z != node.Name.Length; z++) - node.Name[z] = _PEFileByte[NameIndex + 2 + z]; - // System.Windows.Forms.MessageBox.Show(GetString(Entry.ID)); - - temp[0] = Entry.Id[2]; - temp[1] = Entry.Id[3]; - - long oldIndex = _PEFileIndex; - - if (GetLong(temp) == 0) - { - temp[0] = Entry.Id[0]; - temp[1] = Entry.Id[1]; - - _PEFileIndex = GetLong(temp) + PEIndex; - - var dataRVA = new ResourceDirectory.DirectoryEntry.DataEntry(); - - Loadbyte(ref dataRVA.ResourRVA); - Loadbyte(ref dataRVA.ResourSize); - Loadbyte(ref dataRVA.ResourTest); - Loadbyte(ref dataRVA.ResourWen); - - _PEFileIndex = oldIndex; - Entry.DataEntryList.Add(dataRVA); - // System.Windows.Forms.MessageBox.Show(GetString(DataRVA.ResourRVA)+"*"+GetString(DataRVA.ResourSize)); - } - else - { - temp[0] = Entry.Id[0]; - temp[1] = Entry.Id[1]; - - var Resource = new ResourceDirectory(); - Entry.NodeDirectoryList.Add(Resource); - AddResourceNode(Resource, PEIndex, GetLong(temp), resourSectRva); - } - _PEFileIndex = oldIndex; - node.EntryList.Add(Entry); - } - - long Count = GetLong(node.NumberOfIdEntries); - for (int i = 0; i != Count; i++) - { - var entry = new ResourceDirectory.DirectoryEntry(); - Loadbyte(ref entry.Name); - Loadbyte(ref entry.Id); - // System.Windows.Forms.MessageBox.Show(GetString(Entry.Name)+"_"+GetString(Entry.Id)); - - byte[] temp = new byte[2]; - temp[0] = entry.Id[2]; - temp[1] = entry.Id[3]; - - long OldIndex = _PEFileIndex; - - if (GetLong(temp) == 0) - { - temp[0] = entry.Id[0]; - temp[1] = entry.Id[1]; - - _PEFileIndex = GetLong(temp) + PEIndex; - - var dataRVA = new ResourceDirectory.DirectoryEntry.DataEntry(); - Loadbyte(ref dataRVA.ResourRVA); - Loadbyte(ref dataRVA.ResourSize); - Loadbyte(ref dataRVA.ResourTest); - Loadbyte(ref dataRVA.ResourWen); - - long FileRva = GetLong(dataRVA.ResourRVA) - resourSectRva + PEIndex; - - dataRVA.FileStarIndex = FileRva; - dataRVA.FileEndIndex = FileRva + GetLong(dataRVA.ResourSize); - - _PEFileIndex = OldIndex; - entry.DataEntryList.Add(dataRVA); - // System.Windows.Forms.MessageBox.Show(GetString(DataRVA.ResourRVA)+"*"+GetString(DataRVA.ResourSize)); - } - else - { - temp[0] = entry.Id[0]; - temp[1] = entry.Id[1]; - var Resource = new ResourceDirectory(); - entry.NodeDirectoryList.Add(Resource); - AddResourceNode(Resource, PEIndex, GetLong(temp), resourSectRva); - } - _PEFileIndex = OldIndex; - node.EntryList.Add(entry); - } - } - - #endregion - - #region 工具方法 - /// - /// 读数据 读byte[]的数量 会改边PEFileIndex的值 - /// - /// - private void Loadbyte(ref byte[] data) - { - if (_PEFileByte is null) - return; - - for (int i = 0; i != data.Length; i++) - { - data[i] = _PEFileByte[_PEFileIndex]; - _PEFileIndex++; - } - } - /// - /// 转换byte为字符串 - /// - /// byte[] - /// AA BB CC DD - private string GetString(byte[] data) - { - string Temp = ""; - for (int i = 0; i != data.Length - 1; i++) - Temp += data[i].ToString("X02") + " "; - - Temp += data[data.Length - 1].ToString("X02"); - // Temp += data[^1].ToString("X02"); - return Temp; - } - /// - /// 转换字符为显示数据 - /// - /// byte[] - /// ASCII DEFAULT UNICODE BYTE - /// - private string GetString(byte[] data, string type) - { - if (type.Trim().ToUpper() == "ASCII") - return System.Text.Encoding.ASCII.GetString(data); - if (type.Trim().ToUpper() == "DEFAULT") - return System.Text.Encoding.Default.GetString(data); - if (type.Trim().ToUpper() == "UNICODE") - return System.Text.Encoding.Unicode.GetString(data); - if (type.Trim().ToUpper() == "BYTE") - { - string Temp = ""; - for (int i = data.Length - 1; i != 0; i--) - Temp += data[i].ToString("X02") + " "; - Temp += data[0].ToString("X02"); - return Temp; - } - return GetInt(data); - } - /// - /// 转换BYTE为INT - /// - /// - /// - static string GetInt(byte[] data) - { - string Temp = ""; - for (int i = 0; i != data.Length - 1; i++) - { - int ByteInt = (int)data[i]; - Temp += ByteInt.ToString() + " "; - } - int EndByteInt = (int)data[data.Length - 1]; - // int EndByteInt = (int)data[^1]; - Temp += EndByteInt.ToString(); - return Temp; - } - /// - /// 转换数据为LONG - /// - /// - /// - private long GetLong(byte[] data) - { - if (data.Length > 4) - return 0; - - string MC = ""; - // if (data.Length <= 4) - for (int i = data.Length - 1; i != -1; i--) - MC += data[i].ToString("X02"); - return Convert.ToInt64(MC, 16); - } - /// - /// 添加一行信息 - /// - /// 表 - /// 数据 - /// 名称 - /// 说明 - private void AddTableRow(DataTable refTable, byte[]? data, string name, string describe) - { - if (data == null) - throw new ArgumentException(nameof(data)); - - refTable.Rows.Add( - new string[] - { - name, - data.Length.ToString(), - GetString(data), - GetLong(data).ToString(), - GetString(data,"ASCII"), - describe - }); - } - #endregion - - #region Table绘制 - /// - /// 获取PE信息 DataSet方式 - /// - /// 多个表 最后资源表 绘制成树结构TABLE - public DataSet? GetPETable() - { - if (OpenFile == false) - return null; - - var ds = new DataSet("PEFile"); - var a1 = TableDosHeader(); - if (a1 is not null) - ds.Tables.Add(a1); - var a2 = TablePEHeader(); - if (a2 is not null) - ds.Tables.Add(a2); - var a3 = TableOptionalHeader(); - if (a3 is not null) - ds.Tables.Add(a3); - var a4 = TableOptionalDirAttrib(); - if (a4 is not null) - ds.Tables.Add(a4); - var a5 = TableSectionData(); - if (a5 is not null) - ds.Tables.Add(a5); - - - var a6 = TableExportDirectory(); - var a7 = TableExportFunction(); - if (a6 is not null) - ds.Tables.Add(a6); - if (a7 is not null) - ds.Tables.Add(a7); - - var a8 = TableImportDirectory(); - var a9 = TableImportFunction(); - if (a8 is not null) - ds.Tables.Add(a8); - if (a9 is not null) - ds.Tables.Add(a9); - - var a10 = TableResourceDirectory(); - if (a10 is not null) - ds.Tables.Add(a10); - - return ds; - } - - private DataTable? TableDosHeader() - { - if (DosHeader is null) - return null; - - var returnTable = new DataTable("DosHeader FileStar{" + DosHeader.FileStarIndex.ToString() + "}FileEnd{" + DosHeader.FileEndIndex.ToString() + "}"); - returnTable.Columns.Add("Name"); - returnTable.Columns.Add("Size"); - returnTable.Columns.Add("Value16"); - returnTable.Columns.Add("Value10"); - returnTable.Columns.Add("ASCII"); - returnTable.Columns.Add("Describe"); - - AddTableRow(returnTable, DosHeader.e_magic, "e_magic", "魔术数字"); - AddTableRow(returnTable, DosHeader.e_cblp, "e_cblp", "文件最后页的字节数"); - AddTableRow(returnTable, DosHeader.e_cp, "e_cp", "文件页数"); - AddTableRow(returnTable, DosHeader.e_crlc, "e_crlc", "重定义元素个数"); - AddTableRow(returnTable, DosHeader.e_cparhdr, "e_cparhdr", "头部尺寸,以段落为单位"); - AddTableRow(returnTable, DosHeader.e_minalloc, "e_minalloc", "所需的最小附加段"); - AddTableRow(returnTable, DosHeader.e_maxalloc, "e_maxalloc", "所需的最大附加段"); - AddTableRow(returnTable, DosHeader.e_ss, "e_ss", "初始的SS值(相对偏移量)"); - AddTableRow(returnTable, DosHeader.e_sp, "e_sp", "初始的SP值"); - AddTableRow(returnTable, DosHeader.e_csum, "e_csum", "校验和"); - AddTableRow(returnTable, DosHeader.e_ip, "e_ip", "初始的IP值"); - AddTableRow(returnTable, DosHeader.e_cs, "e_cs", "初始的CS值(相对偏移量)"); - AddTableRow(returnTable, DosHeader.e_rva, "e_rva", ""); - AddTableRow(returnTable, DosHeader.e_fg, "e_fg", ""); - AddTableRow(returnTable, DosHeader.e_bl1, "e_bl1", ""); - AddTableRow(returnTable, DosHeader.e_oemid, "e_oemid", ""); - AddTableRow(returnTable, DosHeader.e_oeminfo, "e_oeminfo", ""); - AddTableRow(returnTable, DosHeader.e_bl2, "e_bl2", ""); - AddTableRow(returnTable, DosHeader.e_PESTAR, "e_PESTAR", "PE开始 +本结构的位置"); - - return returnTable; - } - - private DataTable? TablePEHeader() - { - if (PEHeader is null) - return null; - - var returnTable = new DataTable("PeHeader FileStar{" + PEHeader.FileStarIndex.ToString() + "}FileEnd{" + PEHeader.FileEndIndex.ToString() + "}"); - returnTable.Columns.Add("Name"); - returnTable.Columns.Add("Size"); - returnTable.Columns.Add("Value16"); - returnTable.Columns.Add("Value10"); - returnTable.Columns.Add("ASCII"); - returnTable.Columns.Add("Describe"); - - - AddTableRow(returnTable, PEHeader.Header, "Header", "PE文件标记"); - AddTableRow(returnTable, PEHeader.Machine, "Machine", "该文件运行所要求的CPU.对于Intel平台,该值是IMAGE_FILE_MACHINE_I386 (14Ch).我们尝试了LUEVELSMEYER的pe.txt声明的14Dh和14Eh,但Windows不能正确执行. "); - AddTableRow(returnTable, PEHeader.NumberOfSections, "NumberOfSections", "文件的节数目.如果我们要在文件中增加或删除一个节,就需要修改这个值."); - AddTableRow(returnTable, PEHeader.TimeDateStamp, "TimeDateStamp", "文件创建日期和时间. "); - AddTableRow(returnTable, PEHeader.PointerToSymbolTable, "PointerToSymbolTable", "用于调试. "); - AddTableRow(returnTable, PEHeader.NumberOfSymbols, "NumberOfSymbols", "用于调试. "); - AddTableRow(returnTable, PEHeader.SizeOfOptionalHeader, "SizeOfOptionalHeader", "指示紧随本结构之后的 OptionalHeader 结构大小,必须为有效值."); - AddTableRow(returnTable, PEHeader.Characteristics, "Characteristics", "关于文件信息的标记,比如文件是exe还是dll."); - - return returnTable; - } - - private DataTable? TableOptionalHeader() - { - if (OptionalHeader is null) - return null; - - var returnTable = new DataTable("OptionalHeader FileStar{" + OptionalHeader.FileStarIndex.ToString() + "}FileEnd{" + OptionalHeader.FileEndIndex.ToString() + "}"); - returnTable.Columns.Add("Name"); - returnTable.Columns.Add("Size"); - returnTable.Columns.Add("Value16"); - returnTable.Columns.Add("Value10"); - returnTable.Columns.Add("ASCII"); - returnTable.Columns.Add("Describe"); - - AddTableRow(returnTable, OptionalHeader.Magic, "Magic", "Magic 010B=普通可以执行,0107=ROM映像"); - AddTableRow(returnTable, OptionalHeader.MajorLinkerVersion, "MajorLinkerVersion", "主版本号"); - AddTableRow(returnTable, OptionalHeader.MinorLinkerVersion, "MinorLinkerVersion", "副版本号"); - AddTableRow(returnTable, OptionalHeader.SizeOfCode, "SizeOfCode", "代码段大小"); - AddTableRow(returnTable, OptionalHeader.SizeOfInitializedData, "SizeOfInitializedData", "已初始化数据大小"); - AddTableRow(returnTable, OptionalHeader.SizeOfUninitializedData, "SizeOfUninitializedData", "未初始化数据大小"); - AddTableRow(returnTable, OptionalHeader.AddressOfEntryPoint, "AddressOfEntryPoint", "执行将从这里开始(RVA)"); - AddTableRow(returnTable, OptionalHeader.BaseOfCode, "BaseOfCode", "代码基址(RVA)"); - AddTableRow(returnTable, OptionalHeader.ImageBase, "ImageBase", "数据基址(RVA)"); - if (_IsX86) - { - AddTableRow(returnTable, OptionalHeader.BaseOfData, "ImageFileCode", "映象文件基址"); - } - AddTableRow(returnTable, OptionalHeader.SectionAlignment, "SectionAlign", "区段列队"); - AddTableRow(returnTable, OptionalHeader.MajorOperatingSystemVersion, "MajorOSV", "文件列队"); - AddTableRow(returnTable, OptionalHeader.MinorOperatingSystemVersion, "MinorOSV", "操作系统主版本号"); - AddTableRow(returnTable, OptionalHeader.MajorImageVersion, "MajorImageVer", "映象文件主版本号"); - AddTableRow(returnTable, OptionalHeader.MinorImageVersion, "MinorImageVer", "映象文件副版本号"); - AddTableRow(returnTable, OptionalHeader.MajorSubsystemVersion, "MajorSV", "子操作系统主版本号"); - AddTableRow(returnTable, OptionalHeader.MinorSubsystemVersion, "MinorSV", "子操作系统副版本号"); - AddTableRow(returnTable, OptionalHeader.Win32VersionValue, "UNKNOW", "Win32版本值"); - AddTableRow(returnTable, OptionalHeader.SizeOfImage, "SizeOfImage", "映象文件大小"); - AddTableRow(returnTable, OptionalHeader.SizeOfHeards, "SizeOfHeards", "标志头大小"); - AddTableRow(returnTable, OptionalHeader.CheckSum, "CheckSum", "文件效验"); - AddTableRow(returnTable, OptionalHeader.Subsystem, "Subsystem", "子系统(映象文件)1本地 2WINDOWS-GUI 3WINDOWS-CUI 4 POSIX-CUI"); - AddTableRow(returnTable, OptionalHeader.DLLCharacteristics, "DLL_Characteristics", "DLL标记"); - AddTableRow(returnTable, OptionalHeader.SizeOfStackReserve, "Bsize", "保留栈的大小"); - AddTableRow(returnTable, OptionalHeader.SizeOfStackCommit, "TimeBsize", "初始时指定栈大小"); - AddTableRow(returnTable, OptionalHeader.SizeOfHeapReserve, "AucBsize", "保留堆的大小"); - AddTableRow(returnTable, OptionalHeader.SizeOfHeapCommit, "SizeOfBsize", "初始时指定堆大小"); - AddTableRow(returnTable, OptionalHeader.LoaderFlags, "FuckBsize", "加载器标志"); - AddTableRow(returnTable, OptionalHeader.NumberOfRvaAndSizes, "DirectCount", "数据目录数"); - - return returnTable; - } - - private DataTable? TableOptionalDirAttrib() - { - if (OptionalDirAttrib is null) - return null; - - var returnTable = new DataTable( - "OptionalDirAttrib FileStar{" - + OptionalDirAttrib.FileStarIndex.ToString() - + "}FileEnd{" - + OptionalDirAttrib.FileEndIndex.ToString() - + "}"); - returnTable.Columns.Add("Name"); - returnTable.Columns.Add("Size"); - returnTable.Columns.Add("Value16"); - returnTable.Columns.Add("Value10"); - returnTable.Columns.Add("ASCII"); - returnTable.Columns.Add("Describe"); - - var tableName = new Hashtable - { - { 0, "输出表" }, - { 1, "输入表" }, - { 2, "资源表" }, - { 3, "异常表" }, - { 4, "安全表" }, - { 5, "基部重定位表" }, - { 6, "调试数据" }, - { 7, "版权数据" }, - { 8, "全局PTR" }, - { 9, "TLS表" }, - { 10, "装入配置表" }, - { 11, "其他表1" }, - { 12, "其他表2" }, - { 13, "其他表3" }, - { 14, "其他表4" }, - { 15, "其他表5" } - }; - - for (int i = 0; i != OptionalDirAttrib.DirByte.Count; i++) - { - if (OptionalDirAttrib.DirByte[i] is not OptionalDirAttrib.DirAttrib MyDirByte) - continue; - - string? Name; - var tn = tableName[i]; - if (tn is not null) - Name = tn.ToString(); - else - Name = $"未知表{i}"; - AddTableRow(returnTable, MyDirByte.DirRva, Name!, "地址"); - AddTableRow(returnTable, MyDirByte.DirSize, "", "大小"); - } - return returnTable; - } - - private DataTable? TableSectionData() - { - if (SectionTable is null) - return null; - - var returnTable = new DataTable( - "SectionData FileStar{" - + SectionTable.FileStarIndex.ToString() - + "}FileEnd{" - + SectionTable.FileEndIndex.ToString() - + "}"); - returnTable.Columns.Add("Name"); - returnTable.Columns.Add("Size"); - returnTable.Columns.Add("Value16"); - returnTable.Columns.Add("Value10"); - returnTable.Columns.Add("ASCII"); - returnTable.Columns.Add("Describe"); - - for (int i = 0; i != SectionTable.Section.Count; i++) - { - if (SectionTable.Section[i] is not SectionTable.SectionData SectionDate) - continue; - - AddTableRow(returnTable, SectionDate.SectName, "SectName", "名字"); - AddTableRow(returnTable, SectionDate.VirtualAddress, "VirtualAddress", "虚拟内存地址"); - AddTableRow(returnTable, SectionDate.SizeOfRawDataRVA, "SizeOfRawDataRVA", "RVA偏移"); - AddTableRow(returnTable, SectionDate.SizeOfRawDataSize, "SizeOfRawDataSize", "RVA大小"); - AddTableRow(returnTable, SectionDate.PointerToRawData, "PointerToRawData", "指向RAW数据"); - AddTableRow(returnTable, SectionDate.PointerToRelocations, "PointerToRelocations", "指向定位号"); - AddTableRow(returnTable, SectionDate.PointerToLinenumbers, "PointerToLinenumbers", "指向行数"); - AddTableRow(returnTable, SectionDate.NumberOfRelocations, "NumberOfRelocations", "定位号"); - AddTableRow(returnTable, SectionDate.NumberOfLinenumbers, "NumberOfLinenumbers", "行数号"); - AddTableRow(returnTable, SectionDate.Characteristics, "Characteristics", "区段标记"); - } - return returnTable; - } - - private DataTable? TableExportDirectory() - { - if (ExportDirectory is null) - return null; - - var returnTable = new DataTable( - "ExportDirectory FileStar{" - + ExportDirectory.FileStarIndex.ToString() - + "}FileEnd{" - + ExportDirectory.FileEndIndex.ToString() - + "}"); - returnTable.Columns.Add("Name"); - returnTable.Columns.Add("Size"); - returnTable.Columns.Add("Value16"); - returnTable.Columns.Add("Value10"); - returnTable.Columns.Add("ASCII"); - returnTable.Columns.Add("Describe"); - - AddTableRow(returnTable, ExportDirectory.Characteristics, "Characteristics", "一个保留字段,目前为止值为0."); - AddTableRow(returnTable, ExportDirectory.TimeDateStamp, "TimeDateStamp", "产生的时间."); - AddTableRow(returnTable, ExportDirectory.MajorVersion, "MajorVersion", "主版本号"); - AddTableRow(returnTable, ExportDirectory.MinorVersion, "MinorVersion", "副版本号"); - AddTableRow(returnTable, ExportDirectory.Name, "Name", "一个RVA,指向一个dll的名称的ascii字符串."); - AddTableRow(returnTable, ExportDirectory.Base, "Base", "输出函数的起始序号.一般为1."); - AddTableRow(returnTable, ExportDirectory.NumberOfFunctions, "NumberOfFunctions", "输出函数入口地址的数组 中的元素个数."); - AddTableRow(returnTable, ExportDirectory.NumberOfNames, "NumberOfNames", "输出函数名的指针的数组 中的元素个数,也是输出函数名对应的序号的数组 中的元素个数."); - AddTableRow(returnTable, ExportDirectory.AddressOfFunctions, "AddressOfFunctions", "一个RVA,指向输出函数入口地址的数组."); - AddTableRow(returnTable, ExportDirectory.AddressOfNames, "AddressOfNames", "一个RVA,指向输出函数名的指针的数组."); - AddTableRow(returnTable, ExportDirectory.AddressOfNameOrdinals, "AddressOfNameOrdinals", "一个RVA,指向输出函数名对应的序号的数组."); - - return returnTable; - } - private DataTable? TableExportFunction() - { - if (ExportDirectory is null) - return null; - - var returnTable = new DataTable("ExportFunctionList"); - returnTable.Columns.Add("Name"); - returnTable.Columns.Add("Size"); - returnTable.Columns.Add("Value16"); - returnTable.Columns.Add("Value10"); - returnTable.Columns.Add("ASCII"); - returnTable.Columns.Add("Describe"); - - for (int i = 0; i != ExportDirectory.FunctionNamesByte.Count; i++) - { - AddTableRow(returnTable, - ExportDirectory.FunctionNamesByte[i], - "Name", - "_ExportDirectory.Name-Sect.SizeOfRawDataRVA+Sect.PointerToRawData"); - } - - for (int i = 0; i != ExportDirectory.AddressOfNamesList.Count; i++) - { - if (ExportDirectory.AddressOfNamesList[i] is not byte[] a) - continue; - AddTableRow(returnTable, a, "NamesList", ""); - } - - for (int i = 0; i != ExportDirectory.AddressOfFunctionsList.Count; i++) - { - if (ExportDirectory.AddressOfFunctionsList[i] is not byte[] a) - continue; - AddTableRow(returnTable, a, "Functions", ""); - } - - for (int i = 0; i != ExportDirectory.AddressOfNameOrdinalsList.Count; i++) - { - if (ExportDirectory.AddressOfNameOrdinalsList[i] is not byte[] a) - continue; - AddTableRow(returnTable, a, "NameOrdinals", ""); - } - return returnTable; - } - private DataTable? TableImportDirectory() - { - if (ImportDirectory is null) - return null; - - var returnTable = new DataTable("ImportDirectory FileStar{" + ImportDirectory.FileStarIndex.ToString() + "}FileEnd{" + ImportDirectory.FileEndIndex.ToString() + "}"); - returnTable.Columns.Add("Name"); - returnTable.Columns.Add("Size"); - returnTable.Columns.Add("Value16"); - returnTable.Columns.Add("Value10"); - returnTable.Columns.Add("ASCII"); - returnTable.Columns.Add("Describe"); - - for (int i = 0; i != ImportDirectory.ImportList.Count; i++) - { - if (ImportDirectory.ImportList[i] is not ImportDirectory.ImportDate ImportByte) - continue; - - AddTableRow(returnTable, ImportByte.DLLName, "输入DLL名称", "**********"); - AddTableRow(returnTable, ImportByte.OriginalFirstThunk, "OriginalFirstThunk", "这里实际上保存着一个RVA,这个RVA指向一个DWORD数组,这个数组可以叫做输入查询表.每个数组元素,或者叫一个表项,保存着一个指向函数名的RVA或者保存着一个函数的序号."); - AddTableRow(returnTable, ImportByte.TimeDateStamp, "TimeDateStamp", "当这个值为0的时候,表明还没有bind.不为0的话,表示已经bind过了.有关bind的内容后面介绍."); - AddTableRow(returnTable, ImportByte.ForwarderChain, "ForwarderChain", ""); - AddTableRow(returnTable, ImportByte.Name, "Name", "一个RVA,这个RVA指向一个ascii以空字符结束的字符串,这个字符串就是本结构对应的dll文件的名字."); - AddTableRow(returnTable, ImportByte.FirstThunk, "FirstThunk", "一个RVA,这个RVA指向一个DWORD数组,这个数组可以叫输入地址表.如果bind了的话,这个数组的每个元素,就是一个输入函数的入口地址."); - } - - return returnTable; - } - private DataTable? TableImportFunction() - { - if (ImportDirectory is null) - return null; - - var returnTable = new DataTable("ImportFunctionList"); - returnTable.Columns.Add("Name"); - returnTable.Columns.Add("Size"); - returnTable.Columns.Add("Value16"); - returnTable.Columns.Add("Value10"); - returnTable.Columns.Add("ASCII"); - returnTable.Columns.Add("Describe"); - - for (int i = 0; i != ImportDirectory.ImportList.Count; i++) - { - if (ImportDirectory.ImportList[i] is not ImportDirectory.ImportDate ImportByte) - continue; - - AddTableRow(returnTable, ImportByte.DLLName, "DLL-Name", "**********"); - - for (int z = 0; z != ImportByte.DLLFunctionList.Count; z++) - { - if (ImportByte.DLLFunctionList[z] is not ImportDirectory.ImportDate.FunctionList Function) - continue; - - AddTableRow(returnTable, Function.FunctionName, "FunctionName", ""); - AddTableRow(returnTable, Function.FunctionHead, "FunctionHead", ""); - AddTableRow(returnTable, Function.OriginalFirst, "OriginalFirstThunk", ""); - } - } - return returnTable; - } - private DataTable? TableResourceDirectory() - { - if (ResourceDirectory is null) - return null; - var returnTable = new DataTable("ResourceDirectory FileStar{" + ResourceDirectory.FileStarIndex.ToString() + "}FileEnd{" + ResourceDirectory.FileEndIndex.ToString() + "}"); - returnTable.Columns.Add("GUID"); - returnTable.Columns.Add("Text"); - returnTable.Columns.Add("ParentID"); - AddResourceDirectoryRow(returnTable, ResourceDirectory, ""); - return returnTable; - } - private void AddResourceDirectoryRow(DataTable myTable, ResourceDirectory Node, string parentID) - { - string Name = ""; - if (Node.Name is not null) - Name = GetString(Node.Name, "UNICODE"); - - for (int i = 0; i != Node.EntryList.Count; i++) - { - if (Node.EntryList[i] is not ResourceDirectory.DirectoryEntry Entry) - continue; - - long ID = GetLong(Entry.Name); - - string GUID = Guid.NewGuid().ToString(); - - string IDNAME = "ID{" + ID + "}"; - if (Name.Length != 0) - IDNAME += "Name{" + Name + "}"; - - if (parentID.Length == 0) - { - IDNAME += ID switch - { - 1 => "Type{Cursor}", - 2 => "Type{Bitmap}", - 3 => "Type{Icon}", - 4 => "Type{Cursor}", - 5 => "Type{Menu}", - 6 => "Type{Dialog}", - 7 => "Type{String Table}", - 8 => "Type{Font Directory}", - 9 => "Type{Font}", - 10 => "Type{Accelerators}", - 11 => "Type{Unformatted}", - 12 => "Type{Message Table}", - 13 => "Type{Group Cursor}", - 14 => "Type{Group Icon}", - 15 => "Type{Information}", - 16 => "Type{Version}", - _ => "Type{未定义}", - }; - } - - myTable.Rows.Add(new string[] { GUID, IDNAME, parentID }); - - for (int z = 0; z != Entry.DataEntryList.Count; z++) - { - if (Entry.DataEntryList[z] is not ResourceDirectory.DirectoryEntry.DataEntry Data) - continue; - - string Text = "Address{" + GetString(Data.ResourRVA) + "} Size{" + GetString(Data.ResourSize) + "} FileBegin{" + Data.FileStarIndex.ToString() + "-" + Data.FileEndIndex.ToString() + "}"; - - myTable.Rows.Add(new string[] { Guid.NewGuid().ToString(), Text, GUID }); - } - - for (int z = 0; z != Entry.NodeDirectoryList.Count; z++) - { - if (Entry.NodeDirectoryList[z] is not ResourceDirectory a) - continue; - AddResourceDirectoryRow(myTable, a, GUID); - } - } - } - #endregion -} - -#region 类 -/// -/// DOS文件都MS开始 -/// -public class DosHeader // IMAGE_DOS_HEADER -{ - public byte[] e_magic = new byte[2]; // 魔术数字 - public byte[] e_cblp = new byte[2]; // 文件最后页的字节数 - public byte[] e_cp = new byte[2]; // 文件页数 - public byte[] e_crlc = new byte[2]; // 重定义元素个数 - public byte[] e_cparhdr = new byte[2]; // 头部尺寸,以段落为单位 - public byte[] e_minalloc = new byte[2]; // 所需的最小附加段 - public byte[] e_maxalloc = new byte[2]; // 所需的最大附加段 - public byte[] e_ss = new byte[2]; // 初始的SS值(相对偏移量) - public byte[] e_sp = new byte[2]; // 初始的SP值 - public byte[] e_csum = new byte[2]; // 校验和 - public byte[] e_ip = new byte[2]; // 初始的IP值 - public byte[] e_cs = new byte[2]; // 初始的CS值(相对偏移量) - public byte[] e_rva = new byte[2]; - public byte[] e_fg = new byte[2]; - public byte[] e_bl1 = new byte[8]; - public byte[] e_oemid = new byte[2]; - public byte[] e_oeminfo = new byte[2]; - public byte[] e_bl2 = new byte[20]; - public byte[] e_PESTAR = new byte[2]; // PE开始 +自己的位置........重点 - - public long FileStarIndex = 0; - public long FileEndIndex = 0; -} - -/// -/// DOS程序 提示 -/// -public class DosStub -{ - public byte[] DosStubData; - public DosStub(long Size) - { - DosStubData = new byte[Size]; - } - public long FileStarIndex = 0; - public long FileEndIndex = 0; -} - -/// -/// PE文件头 -/// -public class PEHeader // IMAGE_FILE_HEADER -{ - public byte[] Header = new byte[4];// PE文件标记 - public byte[] Machine = new byte[2];// 该文件运行所要求的CPU.对于Intel平台,该值是IMAGE_FILE_MACHINE_I386 (14Ch).我们尝试了LUEVELSMEYER的pe.txt声明的14Dh和14Eh,但Windows不能正确执行.看起来,除了禁止程序执行之外,本域对我们来说用处不大. - public byte[] NumberOfSections = new byte[2];// 文件的节数目.如果我们要在文件中增加或删除一个节,就需要修改这个值. - public byte[] TimeDateStamp = new byte[4];// 文件创建日期和时间.我们不感兴趣. - public byte[] PointerToSymbolTable = new byte[4];// 用于调试. - public byte[] NumberOfSymbols = new byte[4];// 用于调试. - public byte[] SizeOfOptionalHeader = new byte[2];// 指示紧随本结构之后的 OptionalHeader 结构大小,必须为有效值. IMAGE_OPTIONAL_HEADER32 结构大小 - public byte[] Characteristics = new byte[2];// 关于文件信息的标记,比如文件是exe还是dll. - - public long FileStarIndex = 0; - public long FileEndIndex = 0; -} - -/// -/// PE头扩展 -/// -// https://docs.microsoft.com/en-us/windows/win32/api/winnt/ns-winnt-image_optional_header32 // IMAGE_OPTIONAL_HEADER32 -// https://docs.microsoft.com/en-us/windows/win32/api/winnt/ns-winnt-image_optional_header64 // IMAGE_OPTIONAL_HEADER64 -public class OptionalHeader -{ - public byte[] Magic = new byte[2]; // Magic 010B=普通可以执行,0107=ROM映像 - public byte[] MajorLinkerVersion = new byte[1]; // 主版本号 - public byte[] MinorLinkerVersion = new byte[1]; // 副版本号 - public byte[] SizeOfCode = new byte[4]; // 代码段大小 - public byte[] SizeOfInitializedData = new byte[4]; // 已初始化数据大小 - public byte[] SizeOfUninitializedData = new byte[4]; // 未初始化数据大小 - public byte[] AddressOfEntryPoint = new byte[4]; // 执行将从这里开始(RVA).........入口点指向,dll填充0 - public byte[] BaseOfCode = new byte[4]; // 代码基址(RVA) - - public byte[] BaseOfData = new byte[4]; // 映象文件基址 - - public byte[] ImageBase = new byte[4]; // 数据基址(RVA) - - public byte[] SectionAlignment = new byte[4]; // 区段列队..... - public byte[] FileAlignment = new byte[4]; // 文件列队 - - public byte[] MajorOperatingSystemVersion = new byte[2]; // 操作系统主版本号 - public byte[] MinorOperatingSystemVersion = new byte[2]; // 操作系统副版本号 - public byte[] MajorImageVersion = new byte[2]; // 映象文件主版本号 - public byte[] MinorImageVersion = new byte[2]; // 映象文件副版本号 - public byte[] MajorSubsystemVersion = new byte[2]; // 子操作系统主版本号 - public byte[] MinorSubsystemVersion = new byte[2]; // 子操作系统副版本号 - public byte[] Win32VersionValue = new byte[4]; // Win32版本值 - public byte[] SizeOfImage = new byte[4]; // 映象文件大小 - public byte[] SizeOfHeards = new byte[4]; // 标志头大小 - public byte[] CheckSum = new byte[4]; // 文件效验 - public byte[] Subsystem = new byte[2]; // 子系统(映象文件)1本地 2WINDOWS-GUI 3WINDOWS-CUI 4 POSIX-CUI - public byte[] DLLCharacteristics = new byte[2]; // DLL标记 - - public byte[] SizeOfStackReserve = new byte[4]; // 保留栈的大小 - public byte[] SizeOfStackCommit = new byte[4]; // 初始时指定栈大小 - public byte[] SizeOfHeapReserve = new byte[4]; // 保留堆的大小 - public byte[] SizeOfHeapCommit = new byte[4]; // 初始时指定堆大小 - public byte[] LoaderFlags = new byte[4]; // 加载器标志 - public byte[] NumberOfRvaAndSizes = new byte[4]; // 数据目录数 - - public long FileStarIndex = 0; - public long FileEndIndex = 0; - - public OptionalHeader(bool is32) - { - if (!is32) - { - // X64没有了,但是为了代码保留修改幅度不大,所以置0 - BaseOfData = new byte[0];// x64必须置于0 - // x64长度增加的 - int ulonglong = 8; - ImageBase = new byte[ulonglong]; // 数据基址(RVA) - SizeOfStackReserve = new byte[ulonglong]; // 保留栈的大小 - SizeOfStackCommit = new byte[ulonglong]; // 初始时指定栈大小 - SizeOfHeapReserve = new byte[ulonglong]; // 保留堆的大小 - SizeOfHeapCommit = new byte[ulonglong]; // 初始时指定堆大小 - } - } -} - -/// -/// 目录结构 -/// -public class OptionalDirAttrib -{ - public ArrayList DirByte = new(); - public class DirAttrib - { - public byte[] DirRva = new byte[4]; // 地址 - public byte[] DirSize = new byte[4]; // 大小 - } - public long FileStarIndex = 0; - public long FileEndIndex = 0; -} - -/// -/// 节表 -/// -public class SectionTable -{ - public ArrayList Section = new(); - public class SectionData - { - public byte[] SectName = new byte[8]; // 名字 - public byte[] VirtualAddress = new byte[4]; // 虚拟内存地址 - public byte[] SizeOfRawDataRVA = new byte[4]; // RVA偏移 - public byte[] SizeOfRawDataSize = new byte[4]; // RVA大小 - public byte[] PointerToRawData = new byte[4]; // 指向RAW数据 - public byte[] PointerToRelocations = new byte[4]; // 指向定位号 - public byte[] PointerToLinenumbers = new byte[4]; // 指向行数 - public byte[] NumberOfRelocations = new byte[2]; // 定位号 - public byte[] NumberOfLinenumbers = new byte[2]; // 行数号 - public byte[] Characteristics = new byte[4]; // 区段标记 - } - - public long FileStarIndex = 0; - public long FileEndIndex = 0; -} - -/// -/// 输出表 -/// -public class ExportDirectory -{ - public byte[] Characteristics = new byte[4]; // 一个保留字段,目前为止值为0. - public byte[] TimeDateStamp = new byte[4]; // 产生的时间 - public byte[] MajorVersion = new byte[2]; // 主版本号 - public byte[] MinorVersion = new byte[2]; // 副版本号 - public byte[] Name = new byte[4]; // 一个RVA,指向一个dll的名称的ascii字符串 - public byte[] Base = new byte[4]; // 输出函数的起始序号.一般为1 - public byte[] NumberOfFunctions = new byte[4]; // 输出函数入口地址的数组中的元素个数 - public byte[] NumberOfNames = new byte[4]; // 输出函数名的指针的数组中的元素个数,也是输出函数名对应的序号的数组中的元素个数 - public byte[] AddressOfFunctions = new byte[4]; // 一个RVA,指向输出函数入口地址的数组 - public byte[] AddressOfNames = new byte[4]; // 一个RVA,指向输出函数名的指针的数组 - public byte[] AddressOfNameOrdinals = new byte[4]; // 一个RVA,指向输出函数名对应的序号的数组 - - public ArrayList AddressOfFunctionsList = new(); - public ArrayList AddressOfNamesList = new(); - public ArrayList AddressOfNameOrdinalsList = new(); - /// - /// 函数指针名称集合 - /// - public List FunctionNamesByte = new(); - public long FileStarIndex = 0; - public long FileEndIndex = 0; - - /// - /// 获取函数名 - /// - public HashSet FunctionNames() - { - HashSet names = new(); - for (int i = 0; i < FunctionNamesByte.Count; i++) - names.Add(Encoding.Default.GetString(FunctionNamesByte[i])); - return names; - } -} - - -/// -/// 输入表 -/// -public class ImportDirectory -{ - public ArrayList ImportList = new(); - - public class ImportDate - { - public byte[] OriginalFirstThunk = new byte[4]; // 这里实际上保存着一个RVA,这个RVA指向一个DWORD数组,这个数组可以叫做输入查询表.每个数组元素,或者叫一个表项,保存着一个指向函数名的RVA或者保存着一个函数的序号. - public byte[] TimeDateStamp = new byte[4]; // 当这个值为0的时候,表明还没有bind.不为0的话,表示已经bind过了.有关bind的内容后面介绍. - public byte[] ForwarderChain = new byte[4]; - public byte[] Name = new byte[4]; // 一个RVA,这个RVA指向一个ascii以空字符结束的字符串,这个字符串就是本结构对应的dll文件的名字. - public byte[] FirstThunk = new byte[4]; // 一个RVA,这个RVA指向一个DWORD数组,这个数组可以叫输入地址表.如果bind了的话,这个数组的每个元素,就是一个输入函数的入口地址. - - public byte[]? DLLName; // DLL名称 - public ArrayList DLLFunctionList = new(); - public class FunctionList - { - public byte[] OriginalFirst = new byte[4]; - public byte[]? FunctionName; - public byte[] FunctionHead = new byte[2]; - } - } - public long FileStarIndex = 0; - public long FileEndIndex = 0; -} - -/// -/// 资源表 -/// -public class ResourceDirectory -{ - public byte[] Characteristics = new byte[4]; - public byte[] TimeDateStamp = new byte[4]; - public byte[] MajorVersion = new byte[2]; - public byte[] MinorVersion = new byte[2]; - public byte[] NumberOfNamedEntries = new byte[2]; - public byte[] NumberOfIdEntries = new byte[2]; - public byte[]? Name; - public ArrayList EntryList = new(); - - public class DirectoryEntry - { - public byte[] Name = new byte[4]; - public byte[] Id = new byte[4]; - public ArrayList DataEntryList = new(); - public ArrayList NodeDirectoryList = new(); - - public class DataEntry - { - public byte[] ResourRVA = new byte[4]; - public byte[] ResourSize = new byte[4]; - public byte[] ResourTest = new byte[4]; - public byte[] ResourWen = new byte[4]; - - public long FileStarIndex = 0; - public long FileEndIndex = 0; - } - } - - public long FileStarIndex = 0; - public long FileEndIndex = 0; -} -#endregion \ No newline at end of file diff --git a/src/IFoxCAD.Cad.Shared/Runtime/RedrawEx.cs b/src/IFoxCAD.Cad.Shared/Runtime/RedrawEx.cs deleted file mode 100644 index 3799eed..0000000 --- a/src/IFoxCAD.Cad.Shared/Runtime/RedrawEx.cs +++ /dev/null @@ -1,191 +0,0 @@ -namespace IFoxCAD.Cad; - -[Flags] -public enum BrightEntity : int -{ - /// - /// 块更新 - /// - RecordGraphicsModified = 1, - /// - /// 标注更新 - /// - RecomputeDimensionBlock = 2, - /// - /// 重画 - /// - Draw = 4, - /// - /// 亮显 - /// - Highlight = 8, - /// - /// 亮显取消 - /// - Unhighlight = 16, - /// - /// 显示图元 - /// - VisibleTrue = 32, - /// - /// 隐藏图元 - /// - VisibleFalse = 64, - /// - /// 平移更新,可以令ctrl+z撤回时候保证刷新 - /// - MoveZero = 128, -} - -[Flags] -public enum BrightEditor : int -{ - /// - /// 刷新屏幕,图元不生成(例如块还是旧的显示) - /// - UpdateScreen = 1, - /// - /// 刷新全图 - /// - Regen = 2, - /// - /// 清空选择集 - /// - SelectionClean = 4, - /// - /// 视口外 - /// - ViewportsFrom = 8, - /// - /// 视口内 - /// - ViewportsIn = 16, -} - -public static class RedrawEx -{ - /// - /// 刷新屏幕 - /// - /// 编辑器 - /// 图元,调用时候图元必须提权 - public static void Redraw(this Editor ed, Entity? ent = null) - { - // 刷新图元 - ent?.Redraw(BrightEntity.Draw | - BrightEntity.RecordGraphicsModified | - BrightEntity.RecomputeDimensionBlock | - BrightEntity.MoveZero); - // 刷新 - ed.Redraw(BrightEditor.UpdateScreen); - - /* - * 我发现命令加 CommandFlags.Redraw 就不需要以下处理了: - * 数据库事务和文档事务不一样,文档事务有刷新函数. - * var doc = Acap.DocumentManager.MdiActiveDocument; - * var ed = doc.Editor; - * var tm = doc.TransactionManager; - * tm.QueueForGraphicsFlush();// 如果在最外层事务结束之前需要更新图形,此句把目前为止所做的更改放入 刷新队列 - * tm.FlushGraphics(); // 将当前 刷新队列 的图形提交到显示器 - * ed.UpdateScreen(); // 仅刷新屏幕,图元不生成(例如块还是旧的显示) - */ - var dm = Acap.DocumentManager; - var doc = dm.MdiActiveDocument; - var tm = doc.TransactionManager; - tm.QueueForGraphicsFlush(); - tm.FlushGraphics(); - - // acad2014及以上要加,立即处理队列上面的消息 - System.Windows.Forms.Application.DoEvents(); - } - - /// - /// 刷新屏幕 - /// - /// 编辑器 - /// 更新的方式 - public static void Redraw(this Editor ed, BrightEditor bright) - { - if ((bright & BrightEditor.UpdateScreen) == BrightEditor.UpdateScreen) - { - // 两个函数底层差不多 - // Acap.UpdateScreen(); - ed.UpdateScreen(); - } - - if ((bright & BrightEditor.Regen) == BrightEditor.Regen) - ed.Regen(); - - if ((bright & BrightEditor.SelectionClean) == BrightEditor.SelectionClean) - ed.SetImpliedSelection(new ObjectId[0]); - - if ((bright & BrightEditor.ViewportsFrom) == BrightEditor.ViewportsFrom) - ed.UpdateTiledViewportsFromDatabase(); // 更新视口外 - - if ((bright & BrightEditor.ViewportsIn) == BrightEditor.ViewportsIn) - ed.UpdateTiledViewportsInDatabase(); // 更新视口内 - } - - /// - /// 更改图元显示 - /// - /// 图元,调用时候图元必须提权 - /// 更新的方式 - public static void Redraw(this Entity ent, BrightEntity bright) - { - // 调用时候图元必须提权,参数true表示关闭图元后进行UpData,实现局部刷新块. - if ((bright & BrightEntity.RecordGraphicsModified) == BrightEntity.RecordGraphicsModified) - ent.RecordGraphicsModified(true); - - if ((bright & BrightEntity.RecomputeDimensionBlock) == BrightEntity.RecomputeDimensionBlock) - if (ent is Dimension dim) - dim.RecomputeDimensionBlock(true); - - if ((bright & BrightEntity.Draw) == BrightEntity.Draw) - ent.Draw(); - - if ((bright & BrightEntity.Highlight) == BrightEntity.Highlight) - ent.Highlight(); - - if ((bright & BrightEntity.Unhighlight) == BrightEntity.Unhighlight) - ent.Unhighlight(); - - if ((bright & BrightEntity.VisibleTrue) == BrightEntity.VisibleTrue) - ent.Visible = true; - - if ((bright & BrightEntity.VisibleFalse) == BrightEntity.VisibleFalse) - ent.Visible = false; - - // 戴耀辉: - // 删除块内图元的时候需要刷新块, - // 用 RecordGraphicsModified 显示是没有问题, - // 但是 ctrl+z 撤销会有显示问题, - // 所以平移0可以在撤回数据库的时候刷新指定图元 - if ((bright & BrightEntity.MoveZero) == BrightEntity.MoveZero) - ent.Move(Point3d.Origin, Point3d.Origin); - } - - - #region 实体刷新 - /// - /// 刷新实体显示 - /// - /// 实体对象 - [Obsolete("此处已经被RedrawEx代替")] - public static void Flush(this Entity entity, DBTrans? trans = null) - { - trans ??= DBTrans.Top; - entity.RecordGraphicsModified(true); - trans.Transaction.TransactionManager.QueueForGraphicsFlush(); - trans.Document?.TransactionManager.FlushGraphics(); - } - - /// - /// 刷新实体显示 - /// - /// 实体id - [Obsolete("此处已经被RedrawEx代替")] - public static void Flush(this ObjectId id) - => Flush(DBTrans.Top.GetObject(id)!); - #endregion -} \ No newline at end of file diff --git a/src/IFoxCAD.Cad.Shared/Runtime/SymbolTable.cs b/src/IFoxCAD.Cad.Shared/Runtime/SymbolTable.cs deleted file mode 100644 index 1a0c0ef..0000000 --- a/src/IFoxCAD.Cad.Shared/Runtime/SymbolTable.cs +++ /dev/null @@ -1,408 +0,0 @@ -using static System.Windows.Forms.AxHost; - -namespace IFoxCAD.Cad; - -public class SymbolTable : IEnumerable - where TTable : SymbolTable - where TRecord : SymbolTableRecord, new() -{ - #region 程序集内部属性 - /// - /// 事务管理器 - /// - internal DBTrans DTrans { get; private set; } - /// - /// 数据库 - /// - internal Database Database { get; private set; } - - #endregion - - #region 公开属性 - /// - /// 当前符号表 - /// - public TTable CurrentSymbolTable { get; private set; } - #endregion - - #region 构造函数 - /// - /// 构造函数,初始化Trans和CurrentSymbolTable属性 - /// - /// 事务管理器 - /// 符号表id - /// 默认行为:例如打开隐藏图层 - internal SymbolTable(DBTrans tr, ObjectId tableId, bool defaultBehavior = true) - { - DTrans = tr; - Database = tr.Database; - CurrentSymbolTable = DTrans.GetObject(tableId)!; - - if (!defaultBehavior) - return; - - if (CurrentSymbolTable is LayerTable layer) - { - // 层表包含隐藏的,全部显示出来 - layer = layer.IncludingHidden; - if (layer is TTable tt) - CurrentSymbolTable = tt; - } - } - - #endregion - - #region 索引器 - /// - /// 索引器 - /// - /// 对象名称 - /// 对象的id - public ObjectId this[string key] - { - get - { - if (Has(key)) - return CurrentSymbolTable[key]; - return ObjectId.Null; - } - } - #endregion - - #region Has - /// - /// 判断是否存在符号表记录 - /// - /// 记录名 - /// 存在返回 , 不存在返回 - public bool Has(string key) - { - return CurrentSymbolTable.Has(key); - } - /// - /// 判断是否存在符号表记录 - /// - /// 记录id - /// 存在返回 , 不存在返回 - public bool Has(ObjectId objectId) - { - return CurrentSymbolTable.Has(objectId); - } - #endregion - - #region 添加符号表记录 - /// - /// 添加符号表记录 - /// - /// 符号表记录 - /// 对象id - private ObjectId Add(TRecord record) - { - ObjectId id; - using (CurrentSymbolTable.ForWrite()) - { - id = CurrentSymbolTable.Add(record); - DTrans.Transaction.AddNewlyCreatedDBObject(record, true); - } - return id; - } - /// - /// 添加符号表记录 - /// - /// 符号表记录名 - /// 符号表记录处理函数的无返回值委托 - /// 对象id - public ObjectId Add(string name, Action? action = null) - { - ObjectId id = this[name]; - if (id.IsNull) - { - var record = new TRecord() - { - Name = name - }; - id = Add(record); - using (record.ForWrite()) - action?.Invoke(record); - } - return id; - } - #endregion - - #region 删除符号表记录 - /// - /// 删除符号表记录 - /// - /// 符号表记录对象 - private static void Remove(TRecord record) - { - using (record.ForWrite()) - record.Erase(); - } - - /// - /// 删除符号表记录 - /// - /// 符号表记录名 - public void Remove(string name) - { - var record = GetRecord(name); - if (record is not null) - Remove(record); - } - - /// - /// 删除符号表记录 - /// - /// 符号表记录对象id - public void Remove(ObjectId id) - { - var record = GetRecord(id); - if (record is not null) - Remove(record); - } - #endregion - - #region 修改符号表记录 - /// - /// 修改符号表 - /// - /// 符号表记录名 - /// 修改委托 - [System.Diagnostics.DebuggerStepThrough] - public void Change(string name, Action action) - { - var record = GetRecord(name); - if (record is not null) - { - using (record.ForWrite()) - action.Invoke(record); - } - } - - /// - /// 修改符号表 - /// - /// 符号表记录id - /// 修改委托 - [System.Diagnostics.DebuggerStepThrough] - public void Change(ObjectId id, Action action) - { - var record = GetRecord(id); - if (record is not null) - { - using (record.ForWrite()) - action.Invoke(record); - } - } - #endregion - - #region 获取符号表记录 - /// - /// 获取符号表记录 - /// - /// 符号表记录的id - /// 打开模式 - /// 是否打开已删除对象,默认为不打开 - /// 是否打开锁定图层对象,默认为不打开 - /// 符号表记录 - [System.Diagnostics.DebuggerStepThrough] - public TRecord? GetRecord(ObjectId id, - OpenMode openMode = OpenMode.ForRead, - bool openErased = false, - bool openLockedLayer = false) - { - return DTrans.GetObject(id, openMode, openErased, openLockedLayer); - } - - /// - /// 获取符号表记录 - /// - /// 符号表记录名 - /// 打开模式 - /// 是否打开已删除对象,默认为不打开 - /// 是否打开锁定图层对象,默认为不打开 - /// 符号表记录 - [System.Diagnostics.DebuggerStepThrough] - public TRecord? GetRecord(string name, - OpenMode openMode = OpenMode.ForRead, - bool openErased = false, - bool openLockedLayer = false) - { - return GetRecord(this[name], openMode, openErased, openLockedLayer); - } - - /// - /// 获取符号表记录 - /// - /// 符号表记录集合 - public IEnumerable GetRecords() - { - foreach (var item in this) - { - var record = GetRecord(item); - if (record is not null) - yield return record; - } - } - - /// - /// 获取符号表记录的名字集合 - /// - /// 记录的名字集合 - public IEnumerable GetRecordNames() => GetRecords().Select(record => record.Name); - - /// - /// 获取符合过滤条件的符号表记录名字集合 - /// - /// 过滤器委托 - /// 记录的名字集合 - public IEnumerable GetRecordNames(Func filter) - { - foreach (var item in this) - { - var record = GetRecord(item); - if (record is not null && filter.Invoke(record)) - yield return record.Name; - } - } - - /// - /// 从源数据库拷贝符号表记录 - /// - /// 符号表 - /// 符号表记录名 - /// 是否覆盖, 为覆盖, 为不覆盖 - /// 对象id - public ObjectId GetRecordFrom(SymbolTable table, string name, bool over) - { - if (table is null) - throw new ArgumentNullException(nameof(table), "对象为null"); - - ObjectId rid = this[name]; - bool has = rid != ObjectId.Null; - if ((has && over) || !has) - { - ObjectId id = table[name]; - using IdMapping map = new(); - using ObjectIdCollection ids = new() { id }; - table.Database.WblockCloneObjects( - ids, - CurrentSymbolTable.Id, - map, - DuplicateRecordCloning.Replace, - false); - rid = map[id].Value; - } - return rid; - } - - /// - /// 从文件拷贝符号表记录 - /// - /// 符号表过滤器 - /// 文件名 - /// 符号表记录名 - /// 是否覆盖, 为覆盖, 为不覆盖 - /// 对象id - internal ObjectId GetRecordFrom(Func> tableSelector, - string fileName, - string name, - bool over) - { - using DBTrans tr = new(fileName); - return GetRecordFrom(tableSelector(tr), name, over); - } - #endregion - - #region 遍历 -#line hidden // 调试的时候跳过它 - /// - /// 遍历符号表,执行委托 - /// - /// 要运行的委托 - /// 打开模式,默认为只读 - /// 检查id是否删除,默认true - /// 是否打开已删除对象,默认为不打开 - /// 是否打开锁定图层对象,默认为不打开 - public void ForEach(Action task, - OpenMode openMode = OpenMode.ForRead, - bool checkIdOk = true, - bool openErased = false, - bool openLockedLayer = false) - { - ForEach((a, _, _) => { - task.Invoke(a);//由于此处是委托,所以 DebuggerStepThrough 特性会进入,改用预处理方式避免 - }, openMode, checkIdOk, openErased, openLockedLayer); - } - - /// - /// 遍历符号表,执行委托(允许循环中断) - /// - /// 要执行的委托 - /// 打开模式,默认为只读 - /// 检查id是否删除,默认true - /// 是否打开已删除对象,默认为不打开 - /// 是否打开锁定图层对象,默认为不打开 - public void ForEach(Action task, - OpenMode openMode = OpenMode.ForRead, - bool checkIdOk = true, - bool openErased = false, - bool openLockedLayer = false) - { - ForEach((a, b, _) => { - task.Invoke(a, b); - }, openMode, checkIdOk, openErased, openLockedLayer); - } - - /// - /// 遍历符号表,执行委托(允许循环中断,输出索引值) - /// - /// 要执行的委托 - /// 打开模式,默认为只读 - /// 检查id是否删除,默认true - /// 是否打开已删除对象,默认为不打开 - /// 是否打开锁定图层对象,默认为不打开 - [System.Diagnostics.DebuggerStepThrough] - public void ForEach(Action task, - OpenMode openMode = OpenMode.ForRead, - bool checkIdOk = true, - bool openErased = false, - bool openLockedLayer = false) - { - if (task == null) - throw new ArgumentNullException(nameof(task)); - - LoopState state = new();/*这种方式比Action改Func更友好*/ - int i = 0; - foreach (var id in this) - { - if (checkIdOk && !id.IsOk()) - continue; - var record = GetRecord(id, openMode, openErased, openLockedLayer); - if (record is not null) - task.Invoke(record, state, i); - if (!state.IsRun) - break; - i++; - } - } -#line default - - #endregion - - #region IEnumerable 成员 - [System.Diagnostics.DebuggerStepThrough] - public IEnumerator GetEnumerator() - { - foreach (var id in CurrentSymbolTable) - yield return id; - } - - [System.Diagnostics.DebuggerStepThrough] - IEnumerator IEnumerable.GetEnumerator() - { - return GetEnumerator(); - } - #endregion -} \ No newline at end of file diff --git a/src/IFoxCAD.Cad.Shared/Runtime/Utils.cs b/src/IFoxCAD.Cad.Shared/Runtime/Utils.cs deleted file mode 100644 index 28c47c2..0000000 --- a/src/IFoxCAD.Cad.Shared/Runtime/Utils.cs +++ /dev/null @@ -1,98 +0,0 @@ -namespace IFoxCAD.Cad; - -using System; - -public class DBTransHelper -{ - /* - * id = db.GetObjectId(false, handle, 0); - * 参数意义: db.GetObjectId(如果没有找到就创建,句柄号,标记..将来备用) - * 在vs的输出会一直抛出: - * 引发的异常:“Autodesk.AutoCAD.Runtime.Exception”(位于 AcdbMgd.dll 中) - * "eUnknownHandle" - * 这就是为什么慢的原因,所以直接运行就好了!而Debug还是需要用arx的API替代. - */ - - [System.Security.SuppressUnmanagedCodeSecurity] - [DllImport("acdb17.dll", CallingConvention = CallingConvention.ThisCall/*08的调用约定 高版本是__cdecl*/, - EntryPoint = "?getAcDbObjectId@AcDbDatabase@@QAE?AW4ErrorStatus@Acad@@AAVAcDbObjectId@@_NABVAcDbHandle@@K@Z")] - extern static int getAcDbObjectId17x32(IntPtr db, out ObjectId id, [MarshalAs(UnmanagedType.U1)] bool createnew, ref Handle h, uint reserved); - - [System.Security.SuppressUnmanagedCodeSecurity] - [DllImport("acdb17.dll", CallingConvention = CallingConvention.ThisCall/*08的调用约定 高版本是__cdecl*/, - EntryPoint = "?getAcDbObjectId@AcDbDatabase@@QEAA?AW4ErrorStatus@Acad@@AEAVAcDbObjectId@@_NAEBVAcDbHandle@@K@Z")] - extern static int getAcDbObjectId17x64(IntPtr db, out ObjectId id, [MarshalAs(UnmanagedType.U1)] bool createnew, ref Handle h, uint reserved); - - [System.Security.SuppressUnmanagedCodeSecurity] - [DllImport("acdb18.dll", CallingConvention = CallingConvention.ThisCall/*08的调用约定 高版本是__cdecl*/, - EntryPoint = "?getAcDbObjectId@AcDbDatabase@@QAE?AW4ErrorStatus@Acad@@AAVAcDbObjectId@@_NABVAcDbHandle@@K@Z")] - extern static int getAcDbObjectId18x32(IntPtr db, out ObjectId id, [MarshalAs(UnmanagedType.U1)] bool createnew, ref Handle h, uint reserved); - - [System.Security.SuppressUnmanagedCodeSecurity] - [DllImport("acdb18.dll", CallingConvention = CallingConvention.ThisCall/*08的调用约定 高版本是__cdecl*/, - EntryPoint = "?getAcDbObjectId@AcDbDatabase@@QEAA?AW4ErrorStatus@Acad@@AEAVAcDbObjectId@@_NAEBVAcDbHandle@@K@Z")] - extern static int getAcDbObjectId18x64(IntPtr db, out ObjectId id, [MarshalAs(UnmanagedType.U1)] bool createnew, ref Handle h, uint reserved); - - /// - /// 句柄转id,NET35(08~12)专用的 - /// - /// 数据库 - /// 句柄 - /// 返回的id - /// 不存在则创建 - /// 保留,用于未来 - /// 成功0,其他值都是错误.可以强转ErrorStatus - static int GetAcDbObjectId(IntPtr db, Handle handle, out ObjectId id, bool createIfNotFound = false, uint reserved = 0) - { - id = ObjectId.Null; - switch (Acap.Version.Major) - { - case 17: - { - if (IntPtr.Size == 4) - return getAcDbObjectId17x32(db, out id, createIfNotFound, ref handle, reserved); - else - return getAcDbObjectId17x64(db, out id, createIfNotFound, ref handle, reserved); - } - case 18: - { - if (IntPtr.Size == 4) - return getAcDbObjectId18x32(db, out id, createIfNotFound, ref handle, reserved); - else - return getAcDbObjectId18x64(db, out id, createIfNotFound, ref handle, reserved); - } - } - return -1; - } - - /// - /// 句柄转id - /// - /// 数据库 - /// 句柄 - /// id - public static ObjectId TryGetObjectId(Database db, Handle handle) - { -#if !NET35 - // 高版本直接利用 - var es = db.TryGetObjectId(handle, out ObjectId id); - // if (!es) -#else - var es = GetAcDbObjectId(db.UnmanagedObject, handle, out ObjectId id); - // if (ErrorStatus.OK != (ErrorStatus)es) -#endif - return id; - } - - // public static int GetCadFileVersion(string filename) - // { - // var bytes = File.ReadAllBytes(filename); - // var headstr = Encoding.Default.GetString(bytes)[0..6]; - // if (!headstr.StartsWith("AC")) return 0; - // var vernum = int.Parse(headstr.Replace("AC", "")); - // var a = Enum.Parse(typeof(DwgVersion), "AC1800"); - // Enum.TryParse() - // return vernum + 986; - - // } -} \ No newline at end of file diff --git a/src/IFoxCAD.Cad.Shared/SelectionFilter/OpComp.cs b/src/IFoxCAD.Cad.Shared/SelectionFilter/OpComp.cs deleted file mode 100644 index 040090a..0000000 --- a/src/IFoxCAD.Cad.Shared/SelectionFilter/OpComp.cs +++ /dev/null @@ -1,79 +0,0 @@ -namespace IFoxCAD.Cad; - -/// -/// 比较运算符类 -/// -public class OpComp : OpEqual -{ - /// - /// 比较运算符,如: - /// "<=" - /// 以及合并比较运算符: - /// "<=,<=,=" - /// - public string Content { get; } - - /// - /// 符号名 - /// - public override string Name - { - get { return "Comp"; } - } - - /// - /// 比较运算符类构造函数 - /// - /// 运算符 - /// 数据 - public OpComp(string content, TypedValue value) - : base(value) - { - Content = content; - } - - /// - /// 比较运算符类构造函数 - /// - /// 运算符 - /// 组码 - public OpComp(string content, int code) - : base(code) - { - Content = content; - } - - /// - /// 比较运算符类构造函数 - /// - /// 运算符 - /// 组码 - /// 组码值 - public OpComp(string content, int code, object value) - : base(code, value) - { - Content = content; - } - - /// - /// 比较运算符类构造函数 - /// - /// 运算符 - /// 组码 - /// 组码值 - public OpComp(string content, DxfCode code, object value) - : base(code, value) - { - Content = content; - } - - /// - /// 获取过滤器数据迭代器 - /// - /// TypedValue迭代器 - public override IEnumerable GetValues() - { - yield return new TypedValue(-4, Content); - yield return Value; - } -} \ No newline at end of file diff --git a/src/IFoxCAD.Cad.Shared/SelectionFilter/OpEqual.cs b/src/IFoxCAD.Cad.Shared/SelectionFilter/OpEqual.cs deleted file mode 100644 index 370a0c1..0000000 --- a/src/IFoxCAD.Cad.Shared/SelectionFilter/OpEqual.cs +++ /dev/null @@ -1,86 +0,0 @@ -namespace IFoxCAD.Cad; - -/// -/// 相等运算符类 -/// -public class OpEqual : OpFilter -{ - /// - /// 组码与匹配值的TypedValue类型值 - /// - public TypedValue Value { get; private set; } - - /// - /// 符号名 - /// - public override string Name - { - get { return "Equal"; } - } - - /// - /// 相等运算符类构造函数 - /// - /// 组码 - public OpEqual(int code) - { - Value = new TypedValue(code); - } - - /// - /// 相等运算符类构造函数 - /// - /// 组码 - /// 组码值 - public OpEqual(int code, object value) - { - Value = new TypedValue(code, value); - } - - /// - /// 相等运算符类构造函数 - /// - /// 组码 - /// 组码值 - public OpEqual(DxfCode code, object value) - { - Value = new TypedValue((int)code, value); - } - - /// - /// 相等运算符类构造函数 - /// - /// 组码与组码值的TypedValue类型值 - internal OpEqual(TypedValue value) - { - Value = value; - } - - /// - /// 过滤器数据迭代器 - /// - /// TypedValue迭代器 - public override IEnumerable GetValues() - { - yield return Value; - } - - /// - /// 设置数据 - /// - /// 组码值 - public void SetValue(object value) - { - Value = new TypedValue(Value.TypeCode, value); - } - - /// - /// 设置数据 - /// - /// 组码 - /// 组码值 - public void SetValue(int code, object value) - { - Value = new TypedValue(code, value); - } -} diff --git a/src/IFoxCAD.Cad.Shared/SelectionFilter/OpFilter.cs b/src/IFoxCAD.Cad.Shared/SelectionFilter/OpFilter.cs deleted file mode 100644 index 48843f4..0000000 --- a/src/IFoxCAD.Cad.Shared/SelectionFilter/OpFilter.cs +++ /dev/null @@ -1,341 +0,0 @@ -namespace IFoxCAD.Cad; - -/// -/// 选择集过滤器抽象类 -/// -public abstract class OpFilter -{ - /// - /// 过滤器的名字 - /// - public abstract string Name { get; } - - /// - /// 只读属性,表示这个过滤器取反 - /// - public OpFilter Not => new OpNot(this); - - /// - /// 获取TypedValue类型的值的迭代器的抽象方法,子类必须重写 - /// - /// TypedValue迭代器 - public abstract IEnumerable GetValues(); - - /// - /// 非操作符,返回的是OpFilter类型变量的 属性 - /// - /// OpFilter类型变量 - /// OpFilter对象 - public static OpFilter operator !(OpFilter item) - { - return item.Not; - } - - /// - /// 过滤器值转换为 TypedValue 类型数组 - /// - /// TypedValue数组 - public TypedValue[] ToArray() - { - return GetValues().ToArray(); - } - - /// - /// 隐式类型转换,将自定义的过滤器转换为 Autocad 认识的选择集过滤器 - /// - /// 过滤器对象 - /// - /// 选择集过滤器. - /// - public static implicit operator SelectionFilter(OpFilter item) - { - return new SelectionFilter(item.ToArray()); - } - - /// - /// 转换为字符串 - /// - /// 字符串 - public override string ToString() - { - var sb = new StringBuilder(); - foreach (var value in GetValues()) - sb.Append(value); - return sb.ToString(); - } - - /// - /// 构建过滤器 - /// - /// - /// 举两个利用构建函数创建选择集过滤的例子 - /// - /// !(e.Dxf(0) == "line" & e.Dxf(8) == "0") - /// | e.Dxf(0) != "circle" & e.Dxf(8) == "2" & e.Dxf(10) >= p); - /// - /// 例子2: - /// var f2 = OpFilter.Build( - /// e => e.Or( - /// !e.And(e.Dxf(0) == "line", e.Dxf(8) == "0"), - /// e.And(e.Dxf(0) != "circle", e.Dxf(8) == "2", - /// e.Dxf(10) <= new Point3d(10, 10, 0)))); - /// ]]> - /// - /// 构建过滤器的函数委托 - /// 过滤器 - public static OpFilter Build(Func func) - { - return func(new Op()).Filter!; - } - - #region Operator - - /// - /// 过滤器操作符类 - /// - public class Op - { - /// - /// 过滤器属性 - /// - internal OpFilter? Filter { get; private set; } - - internal Op() { } - - private Op(OpFilter filter) - { - Filter = filter; - } - - /// - /// AND 操作符 - /// - /// 操作符类型的可变参数 - /// Op对象 -#pragma warning disable CA1822 // 将成员标记为 static - public Op And(params Op[] args) -#pragma warning restore CA1822 // 将成员标记为 static - { - var filter = new OpAnd(); - foreach (var op in args) - filter.Add(op.Filter!); - return new Op(filter); - } - - /// - /// or 操作符 - /// - /// 操作符类型的可变参数 - /// Op对象 -#pragma warning disable CA1822 // 将成员标记为 static - public Op Or(params Op[] args) -#pragma warning restore CA1822 // 将成员标记为 static - { - var filter = new OpOr(); - foreach (var op in args) - filter.Add(op.Filter!); - return new Op(filter); - } - - /// - /// dxf 操作符,此函数只能用于过滤器中,不是组码操作函数 - /// - /// 组码 - /// Op对象 -#pragma warning disable CA1822 // 将成员标记为 static - public Op Dxf(int code) -#pragma warning restore CA1822 // 将成员标记为 static - { - return new Op(new OpEqual(code)); - } - - /// - /// dxf 操作符,此函数只能用于过滤器中,不是组码操作函数 - /// - /// 组码 - /// 关系运算符的值,比如">,>,=" - /// Op对象 -#pragma warning disable CA1822 // 将成员标记为 static - public Op Dxf(int code, string content) -#pragma warning restore CA1822 // 将成员标记为 static - { - return new Op(new OpComp(content, code)); - } - - /// - /// 非操作符 - /// - /// 过滤器操作符对象 - /// Op对象 - public static Op operator !(Op right) - { - right.Filter = !right.Filter!; - return right; - } - - /// - /// 相等操作符 - /// - /// 过滤器操作符对象 - /// 数据 - /// Op对象 - public static Op operator ==(Op left, object right) - { - var eq = (OpEqual)left.Filter!; - eq.SetValue(right); - return left; - } - - /// - /// 不等操作符 - /// - /// 过滤器操作符对象 - /// 数据 - /// Op对象 - public static Op operator !=(Op left, object right) - { - var eq = (OpEqual)left.Filter!; - eq.SetValue(right); - left.Filter = eq.Not; - return left; - } - - private static Op GetCompOp(string content, Op left, object right) - { - var eq = (OpEqual)left.Filter!; - var comp = new OpComp(content, eq.Value.TypeCode, right); - return new Op(comp); - } - - /// - /// 大于操作符 - /// - /// 过滤器操作符对象 - /// 数据 - /// Op对象 - public static Op operator >(Op left, object right) - { - return GetCompOp(">", left, right); - } - - /// - /// 小于操作符 - /// - /// 过滤器操作符对象 - /// 数据 - /// Op对象 - public static Op operator <(Op left, object right) - { - return GetCompOp("<", left, right); - } - - /// - /// 大于等于操作符 - /// - /// 过滤器操作符对象 - /// 数据 - /// Op对象 - public static Op operator >=(Op left, object right) - { - return GetCompOp(">=", left, right); - } - - /// - /// 小于等于操作符 - /// - /// 过滤器操作符对象 - /// 数据 - /// Op对象 - public static Op operator <=(Op left, object right) - { - return GetCompOp("<=", left, right); - } - - /// - /// 大于等于操作符 - /// - /// 过滤器操作符对象 - /// 点 - /// Op对象 - public static Op operator >=(Op left, Point3d right) - { - return GetCompOp(">,>,*", left, right); - } - - /// - /// 小于等于操作符 - /// - /// 过滤器操作符对象 - /// 点 - /// Op对象 - public static Op operator <=(Op left, Point3d right) - { - return GetCompOp("<,<,*", left, right); - } - - /// - /// 并操作符 - /// - /// 过滤器操作符对象 - /// 过滤器操作符对象 - /// Op对象 - public static Op operator &(Op left, Op right) - { - var filter = new OpAnd - { - left.Filter!, - right.Filter! - }; - return new Op(filter); - } - - /// - /// 或操作符 - /// - /// 过滤器操作符对象 - /// 过滤器操作符对象 - /// Op对象 - public static Op operator |(Op left, Op right) - { - var filter = new OpOr - { - left.Filter!, - right.Filter! - }; - return new Op(filter); - } - - /// - /// 异或操作符 - /// - /// 过滤器操作符对象 - /// 过滤器操作符对象 - /// Op对象 - public static Op operator ^(Op left, Op right) - { - var filter = new OpXor(left.Filter!, right.Filter!); - return new Op(filter); - } - - /// - /// 比较函数 - /// - /// 对象 - /// - /// 是否相等 - /// - public override bool Equals(object obj) => base.Equals(obj); - - /// - /// 获取HashCode - /// - /// HashCode - public override int GetHashCode() => base.GetHashCode(); - } - - #endregion Operator -} diff --git a/src/IFoxCAD.Cad.Shared/SelectionFilter/OpList.cs b/src/IFoxCAD.Cad.Shared/SelectionFilter/OpList.cs deleted file mode 100644 index 5870be9..0000000 --- a/src/IFoxCAD.Cad.Shared/SelectionFilter/OpList.cs +++ /dev/null @@ -1,167 +0,0 @@ -namespace IFoxCAD.Cad; - -/// -/// 逻辑操作符的列表抽象类 -/// -public abstract class OpList : OpLogi -{ - /// - /// 过滤器列表 - /// - protected List _lst = new(); - - /// - /// 添加过滤器条件的虚函数,子类可以重写 - /// - /// 举个利用这个类及其子类创建选择集过滤的例子 - /// - /// ,>,*" } - /// }, - /// }; - /// ]]> - /// - /// 过滤器对象 - public virtual void Add(OpFilter value) - { - _lst.Add(value); - } - - /// - /// 添加过滤条件 - /// - /// 逻辑非~ - /// 组码 - /// 组码值 - public void Add(string speccode, int code, object value) - { - if (speccode == "~") - _lst.Add(new OpEqual(code, value).Not); - } - - /// - /// 添加过滤条件 - /// - /// 组码 - /// 组码值 - public void Add(int code, object value) - { - _lst.Add(new OpEqual(code, value)); - } - - /// - /// 添加过滤条件 - /// - /// 组码 - /// 组码值 - public void Add(DxfCode code, object value) - { - _lst.Add(new OpEqual(code, value)); - } - - /// - /// 添加过滤条件 - /// - /// 组码 - /// 组码值 - /// 比较运算符 - public void Add(int code, object value, string comp) - { - _lst.Add(new OpComp(comp, code, value)); - } - - /// - /// 添加过滤条件 - /// - /// 组码 - /// 组码值 - /// 比较运算符 - public void Add(DxfCode code, object value, string comp) - { - _lst.Add(new OpComp(comp, code, value)); - } - - /// - /// 过滤器迭代器 - /// - /// OpFilter迭代器 - [System.Diagnostics.DebuggerStepThrough] - public override IEnumerator GetEnumerator() - { - foreach (var value in _lst) - yield return value; - } -} - -/// -/// 逻辑与类 -/// -public class OpAnd : OpList -{ - /// - /// 符号名 - /// - public override string Name - { - get { return "And"; } - } - - /// - /// 添加过滤条件 - /// - /// 过滤器对象 - public override void Add(OpFilter value) - { - if (value is OpAnd opand) - { - foreach (var item in opand) - _lst.Add(item); - } - else - { - _lst.Add(value); - } - } -} - -/// -/// 逻辑或类 -/// -public class OpOr : OpList -{ - /// - /// 符号名 - /// - public override string Name - { - get { return "Or"; } - } - - /// - /// 添加过滤条件 - /// - /// 过滤器对象 - public override void Add(OpFilter value) - { - if (value is OpOr opor) - { - foreach (var item in opor) - _lst.Add(item); - } - else - { - _lst.Add(value); - } - } -} \ No newline at end of file diff --git a/src/IFoxCAD.Cad.Shared/SelectionFilter/OpLogi.cs b/src/IFoxCAD.Cad.Shared/SelectionFilter/OpLogi.cs deleted file mode 100644 index bdacb86..0000000 --- a/src/IFoxCAD.Cad.Shared/SelectionFilter/OpLogi.cs +++ /dev/null @@ -1,133 +0,0 @@ -namespace IFoxCAD.Cad; - -/// -/// 过滤器逻辑运算符抽象类 -/// -public abstract class OpLogi : OpFilter, IEnumerable -{ - /// - /// 返回-4组码的开始内容 - /// - public TypedValue First - { - get { return new TypedValue(-4, $"<{Name}"); } - } - - /// - /// 返回-4组码的结束内容 - /// - public TypedValue Last - { - get { return new TypedValue(-4, $"{Name}>"); } - } - - /// - /// 获取过滤条件 - /// - /// TypedValue迭代器 - //[System.Diagnostics.DebuggerStepThrough] - public override IEnumerable GetValues() - { - yield return First; - foreach (var item in this) - { - foreach (var value in item.GetValues()) - yield return value; - } - yield return Last; - } - - /// - /// 获取迭代器 - /// - /// OpFilter迭代器 - [System.Diagnostics.DebuggerStepThrough] - public abstract IEnumerator GetEnumerator(); - - [System.Diagnostics.DebuggerStepThrough] - IEnumerator IEnumerable.GetEnumerator() - { - return GetEnumerator(); - } -} - -/// -/// 逻辑非类 -/// -public class OpNot : OpLogi -{ - private OpFilter Value { get; } - - /// - /// 逻辑非类构造函数 - /// - /// OpFilter数据 - public OpNot(OpFilter value) - { - Value = value; - } - - /// - /// 符号名 - /// - public override string Name - { - get { return "Not"; } - } - - /// - /// 获取迭代器 - /// - /// OpFilter迭代器 - [System.Diagnostics.DebuggerStepThrough] - public override IEnumerator GetEnumerator() - { - yield return Value; - } -} - -/// -/// 逻辑异或类 -/// -public class OpXor : OpLogi -{ - /// - /// 左操作数 - /// - public OpFilter Left { get; } - - /// - /// 右操作数 - /// - public OpFilter Right { get; } - - /// - /// 逻辑异或类构造函数 - /// - /// 左操作数 - /// 右操作数 - public OpXor(OpFilter left, OpFilter right) - { - Left = left; - Right = right; - } - - /// - /// 符号名 - /// - public override string Name - { - get { return "Xor"; } - } - - /// - /// 获取迭代器 - /// - /// 选择集过滤器类型迭代器 - [System.Diagnostics.DebuggerStepThrough] - public override IEnumerator GetEnumerator() - { - yield return Left; - yield return Right; - } -} \ No newline at end of file diff --git a/src/IFoxCAD.Cad.Shared/WindowsAPI/Enums.cs b/src/IFoxCAD.Cad.Shared/WindowsAPI/Enums.cs deleted file mode 100644 index e70dfd6..0000000 --- a/src/IFoxCAD.Cad.Shared/WindowsAPI/Enums.cs +++ /dev/null @@ -1,1071 +0,0 @@ -#if true -namespace IFoxCAD.Cad; - -// https://blog.csdn.net/qq_43812868/article/details/108587936 -[Flags] -public enum TH32CS : uint -{ - /// - /// 原因在于如果不采用改参数的话,有可能快照会占用整个堆的空间 - /// - TH32CS_SNAPNOHEAPS = 0x40000000, - /// - /// 声明快照句柄是可继承的 - /// - TH32CS_INHERIT = 0x80000000, - /// - /// 在快照中包含在th32ProcessID中指定的进程的所有的堆 - /// - TH32CS_SNAPHEAPLIST = 0x00000001, - /// - /// 在快照中包含系统中所有的进程 - /// - TH32CS_SNAPPROCESS = 0x00000002, - /// - /// 在快照中包含系统中所有的线程 - /// - TH32CS_SNAPTHREAD = 0x00000004, - /// - /// 在快照中包含在th32ProcessID中指定的进程的所有的模块 - /// - TH32CS_SNAPMODULE = 0x00000008, - /// - /// 在快照中包含系统中所有的进程和线程 - /// - TH32CS_SNAPALL = TH32CS_SNAPHEAPLIST | TH32CS_SNAPPROCESS | TH32CS_SNAPTHREAD | TH32CS_SNAPMODULE, -} - -/// -/// 设置的钩子类型 -/// -[Flags] -public enum HookType : int -{ - /// - /// WH_MSGFILTER 和 WH_SYSMSGFILTER Hooks使我们可以监视菜单,滚动 - /// 条,消息框,对话框消息并且发现用户使用ALT+TAB or ALT+ESC 组合键切换窗口。 - /// WH_MSGFILTER Hook只能监视传递到菜单,滚动条,消息框的消息,以及传递到通 - /// 过安装了Hook子过程的应用程序建立的对话框的消息。WH_SYSMSGFILTER Hook - /// 监视所有应用程序消息。 - /// - /// WH_MSGFILTER 和 WH_SYSMSGFILTER Hooks使我们可以在模式循环期间 - /// 过滤消息,这等价于在主消息循环中过滤消息。 - /// - /// 通过调用CallMsgFilter function可以直接的调用WH_MSGFILTER Hook。通过使用这 - /// 个函数,应用程序能够在模式循环期间使用相同的代码去过滤消息,如同在主消息循 - /// 环里一样 - /// - WH_MSGFILTER = -1, - /// - /// WH_JOURNALRECORD Hook用来监视和记录输入事件。典型的,可以使用这 - /// 个Hook记录连续的鼠标和键盘事件,然后通过使用WH_JOURNALPLAYBACK Hook - /// 来回放。WH_JOURNALRECORD Hook是全局Hook,它不能象线程特定Hook一样 - /// 使用。WH_JOURNALRECORD是system-wide local hooks,它们不会被注射到任何行 - /// 程地址空间 - /// - WH_JOURNALRECORD = 0, - /// - /// WH_JOURNALPLAYBACK Hook使应用程序可以插入消息到系统消息队列。可 - /// 以使用这个Hook回放通过使用WH_JOURNALRECORD Hook记录下来的连续的鼠 - /// 标和键盘事件。只要WH_JOURNALPLAYBACK Hook已经安装,正常的鼠标和键盘 - /// 事件就是无效的。WH_JOURNALPLAYBACK Hook是全局Hook,它不能象线程特定 - /// Hook一样使用。WH_JOURNALPLAYBACK Hook返回超时值,这个值告诉系统在处 - /// 理来自回放Hook当前消息之前需要等待多长时间(毫秒)。这就使Hook可以控制实 - /// 时事件的回放。WH_JOURNALPLAYBACK是system-wide local hooks,它们不会被 - /// 注射到任何行程地址空间 - /// - WH_JOURNALPLAYBACK = 1, - /// - /// 在应用程序中,WH_KEYBOARD Hook用来监视WM_KEYDOWN and - /// WM_KEYUP消息,这些消息通过GetMessage or PeekMessage function返回。可以使 - /// 用这个Hook来监视输入到消息队列中的键盘消息 - /// - WH_KEYBOARD = 2, - /// - /// 应用程序使用WH_GETMESSAGE Hook来监视从GetMessage or PeekMessage函 - /// 数返回的消息。你可以使用WH_GETMESSAGE Hook去监视鼠标和键盘输入,以及 - /// 其它发送到消息队列中的消息 - /// - WH_GETMESSAGE = 3, - /// - /// 监视发送到窗口过程的消息,系统在消息发送到接收窗口过程之前调用 - /// - WH_CALLWNDPROC = 4, - /// - /// 在以下事件之前,系统都会调用WH_CBT Hook子过程,这些事件包括: - /// 1. 激活,建立,销毁,最小化,最大化,移动,改变尺寸等窗口事件; - /// 2. 完成系统指令; - /// 3. 来自系统消息队列中的移动鼠标,键盘事件; - /// 4. 设置输入焦点事件; - /// 5. 同步系统消息队列事件。 - /// Hook子过程的返回值确定系统是否允许或者防止这些操作中的一个 - /// - WH_CBT = 5, - /// - /// WH_MSGFILTER 和 WH_SYSMSGFILTER Hooks使我们可以监视菜单,滚动 - /// 条,消息框,对话框消息并且发现用户使用ALT+TAB or ALT+ESC 组合键切换窗口。 - /// WH_MSGFILTER Hook只能监视传递到菜单,滚动条,消息框的消息,以及传递到通 - /// 过安装了Hook子过程的应用程序建立的对话框的消息。WH_SYSMSGFILTER Hook - /// 监视所有应用程序消息。 - /// - /// WH_MSGFILTER 和 WH_SYSMSGFILTER Hooks使我们可以在模式循环期间 - /// 过滤消息,这等价于在主消息循环中过滤消息。 - /// - /// 通过调用CallMsgFilter function可以直接的调用WH_MSGFILTER Hook。通过使用这 - /// 个函数,应用程序能够在模式循环期间使用相同的代码去过滤消息,如同在主消息循 - /// 环里一样 - /// - WH_SYSMSGFILTER = 6, - /// - /// WH_MOUSE Hook监视从GetMessage 或者 PeekMessage 函数返回的鼠标消息。 - /// 使用这个Hook监视输入到消息队列中的鼠标消息 - /// - WH_MOUSE = 7, - /// - /// 当调用GetMessage 或 PeekMessage 来从消息队列种查询非鼠标、键盘消息时 - /// - WH_HARDWARE = 8, - /// - /// 在系统调用系统中与其它Hook关联的Hook子过程之前,系统会调用 - /// WH_DEBUG Hook子过程。你可以使用这个Hook来决定是否允许系统调用与其它 - /// Hook关联的Hook子过程 - /// - WH_DEBUG = 9, - /// - /// 外壳应用程序可以使用WH_SHELL Hook去接收重要的通知。当外壳应用程序是 - /// 激活的并且当顶层窗口建立或者销毁时,系统调用WH_SHELL Hook子过程。 - /// WH_SHELL 共有5钟情况: - /// 1. 只要有个top-level、unowned 窗口被产生、起作用、或是被摧毁; - /// 2. 当Taskbar需要重画某个按钮; - /// 3. 当系统需要显示关于Taskbar的一个程序的最小化形式; - /// 4. 当目前的键盘布局状态改变; - /// 5. 当使用者按Ctrl+Esc去执行Task Manager(或相同级别的程序)。 - /// - /// 按照惯例,外壳应用程序都不接收WH_SHELL消息。所以,在应用程序能够接 - /// 收WH_SHELL消息之前,应用程序必须调用SystemParametersInfo function注册它自 - /// 己 - /// - WH_SHELL = 10, - /// - /// 当应用程序的前台线程处于空闲状态时,可以使用WH_FOREGROUNDIDLE - /// Hook执行低优先级的任务。当应用程序的前台线程大概要变成空闲状态时,系统就 - /// 会调用WH_FOREGROUNDIDLE Hook子过程 - /// - WH_FOREGROUNDIDLE = 11, - /// - /// 监视发送到窗口过程的消息,系统在消息发送到接收窗口过程之后调用 - /// - WH_CALLWNDPROCRET = 12, - /// - /// 监视键盘消息 - /// - WH_KEYBOARD_LL = 13, - /// - /// 监视鼠标消息 - /// - WH_MOUSE_LL = 14 -} - -/// -/// 消息类型 -/// 作为SendMessage和PostMessage的参数 -/// -[Flags] -public enum WM : uint -{ - /// - /// 创建一个窗口 - /// - WM_CREATE = 0x01, - /// - /// 当一个窗口被破坏时发送 - /// - WM_DESTROY = 0x02, - /// - /// 移动一个窗口 - /// - WM_MOVE = 0x03, - /// - /// 改变一个窗口的大小 - /// - WM_SIZE = 0x05, - /// - /// 一个窗口被激活或失去激活状态 - /// - WM_ACTIVATE = 0x06, - /// - /// 一个窗口获得焦点 - /// - WM_SETFOCUS = 0x07, - /// - /// 一个窗口失去焦点 - /// - WM_KILLFOCUS = 0x08, - /// - /// 一个窗口改变成Enable状态 - /// - WM_ENABLE = 0x0A, - /// - /// 设置窗口是否能重画 - /// - WM_SETREDRAW = 0x0B, - /// - /// 应用程序发送此消息来设置一个窗口的文本 - /// - WM_SETTEXT = 0x0C, - /// - /// 应用程序发送此消息来复制对应窗口的文本到缓冲区 - /// - WM_GETTEXT = 0x0D, - /// - /// 得到与一个窗口有关的文本的长度(不包含空字符) - /// - WM_GETTEXTLENGTH = 0x0E, - /// - /// 要求一个窗口重画自己 - /// - WM_PAINT = 0x0F, - /// - /// 当一个窗口或应用程序要关闭时发送一个信号 - /// - WM_CLOSE = 0x10, - /// - /// 当用户选择结束对话框或程序自己调用ExitWindows函数 - /// - WM_QUERYENDSESSION = 0x11, - /// - /// 用来结束程序运行 - /// - WM_QUIT = 0x12, - /// - /// 当用户窗口恢复以前的大小位置时,把此消息发送给某个图标 - /// - WM_QUERYOPEN = 0x13, - /// - /// 当窗口背景必须被擦除时(例在窗口改变大小时) - /// - WM_ERASEBKGND = 0x14, - /// - /// 当系统颜色改变时,发送此消息给所有顶级窗口 - /// - WM_SYSCOLORCHANGE = 0x15, - /// - /// 当系统进程发出WM_QUERYENDSESSION消息后,此消息发送给应用程序,通知它对话是否结束 - /// - WM_ENDSESSION = 0x16, - /// - /// 当隐藏或显示窗口是发送此消息给这个窗口 - /// - WM_SHOWWINDOW = 0x18, - /// - /// 发此消息给应用程序哪个窗口是激活的,哪个是非激活的 - /// - WM_ACTIVATEAPP = 0x1C, - /// - /// 当系统的字体资源库变化时发送此消息给所有顶级窗口 - /// - WM_FONTCHANGE = 0x1D, - /// - /// 当系统的时间变化时发送此消息给所有顶级窗口 - /// - WM_TIMECHANGE = 0x1E, - /// - /// 发送此消息来取消某种正在进行的摸态(操作) - /// - WM_CANCELMODE = 0x1F, - /// - /// 如果鼠标引起光标在某个窗口中移动且鼠标输入没有被捕获时,就发消息给某个窗口 - /// - WM_SETCURSOR = 0x20, - /// - /// 当光标在某个非激活的窗口中而用户正按着鼠标的某个键发送此消息给当前窗口 - /// - WM_MOUSEACTIVATE = 0x21, - /// - /// 发送此消息给MDI子窗口当用户点击此窗口的标题栏或当窗口被激活,移动,改变大小 - /// - WM_CHILDACTIVATE = 0x22, - /// - /// 此消息由基于计算机的训练程序发送,通过WH_JOURNALPALYBACK的hook程序分离出用户输入消息 - /// - WM_QUEUESYNC = 0x23, - /// - /// 此消息发送给窗口当它将要改变大小或位置 - /// - WM_GETMINMAXINFO = 0x24, - /// - /// 发送给最小化窗口当它图标将要被重画 - /// - WM_PAINTICON = 0x26, - /// - /// 此消息发送给某个最小化窗口,仅当它在画图标前它的背景必须被重画 - /// - WM_ICONERASEBKGND = 0x27, - /// - /// 发送此消息给一个对话框程序去更改焦点位置 - /// - WM_NEXTDLGCTL = 0x28, - /// - /// 每当打印管理列队增加或减少一条作业时发出此消息 - /// - WM_SPOOLERSTATUS = 0x2A, - /// - /// 当button,combobox,listbox,menu的可视外观改变时发送 - /// - WM_DRAWITEM = 0x2B, - /// - /// 当button, combo box, list box, list view control, or menu item 被创建时 - /// - WM_MEASUREITEM = 0x2C, - /// - /// 此消息有一个LBS_WANTKEYBOARDINPUT风格的发出给它的所有者来响应WM_KEYDOWN消息 - /// - WM_VKEYTOITEM = 0x2E, - /// - /// 此消息由一个LBS_WANTKEYBOARDINPUT风格的列表框发送给他的所有者来响应WM_CHAR消息 - /// - WM_CHARTOITEM = 0x2F, - /// - /// 当绘制文本时程序发送此消息得到控件要用的颜色 - /// - WM_SETFONT = 0x30, - /// - /// 应用程序发送此消息得到当前控件绘制文本的字体 - /// - WM_GETFONT = 0x31, - /// - /// 应用程序发送此消息让一个窗口与一个热键相关连 - /// - WM_SETHOTKEY = 0x32, - /// - /// 应用程序发送此消息来判断热键与某个窗口是否有关联 - /// - WM_GETHOTKEY = 0x33, - /// - /// 此消息发送给最小化窗口,当此窗口将要被拖放而它的类中没有定义图标,应用程序能返回一个图标或光标的句柄,当用户拖放图标时系统显示这个图标或光标 - /// - WM_QUERYDRAGICON = 0x37, - /// - /// 发送此消息来判定combobox或listbox新增加的项的相对位置 - /// - WM_COMPAREITEM = 0x39, - /// - /// 显示内存已经很少了 - /// - WM_COMPACTING = 0x41, - /// - /// 窗口大小和位置将要被改变时,来调用Setwindowpos函数或其它窗口管理函数 - /// - WM_WINDOWPOSCHANGING = 0x46, - /// - /// 窗口大小和位置已经被改变后,来调用Setwindowpos函数或其它窗口管理函数 - /// - WM_WINDOWPOSCHANGED = 0x47, - /// - /// 当系统将要进入暂停状态时发送此消息 - /// - WM_POWER = 0x48, - /// - /// 当一个应用程序传递数据给另一个应用程序时发送此消息 - /// - WM_COPYDATA = 0x4A, - /// - /// 当某个用户取消程序日志激活状态,提交此消息给程序 - /// - WM_CANCELJOURNA = 0x4B, - /// - /// 当某个控件的某个事件已经发生或这个控件需要得到一些信息时,发送此消息给它的父窗口 - /// - WM_NOTIFY = 0x4E, - /// - /// 当用户选择某种输入语言,或输入语言的热键改变 - /// - WM_INPUTLANGCHANGEREQUEST = 0x50, - /// - /// 当平台现场已经被改变后发送此消息给受影响的最顶级窗口 - /// - WM_INPUTLANGCHANGE = 0x51, - /// - /// 当程序已经初始化windows帮助例程时发送此消息给应用程序 - /// - WM_TCARD = 0x52, - /// - /// 此消息显示用户按下了F1,如果某个菜单是激活的,就发送此消息个此窗口关联的菜单,否则就发送给有焦点的窗口,如果当前都没有焦点,就把此消息发送给当前激活的窗口 - /// - WM_HELP = 0x53, - /// - /// 当用户已经登入或退出后发送此消息给所有的窗口,当用户登入或退出时系统更新用户的具体设置信息,在用户更新设置时系统马上发送此消息 - /// - WM_USERCHANGED = 0x54, - /// - /// 公用控件,自定义控件和他们的父窗口通过此消息来判断控件是使用ANSI还是UNICODE结构 - /// - WM_NOTIFYFORMAT = 0x55, - /// - /// 当用户某个窗口中点击了一下右键就发送此消息给这个窗口 - /// - WM_CONTEXTMENU = 0x7B, - /// - /// 当调用SETWINDOWLONG函数将要改变一个或多个窗口的风格时发送此消息给那个窗口 - /// - WM_STYLECHANGING = 0x7C, - /// - /// 当调用SETWINDOWLONG函数一个或多个窗口的风格后发送此消息给那个窗口 - /// - WM_STYLECHANGED = 0x7D, - /// - /// 当显示器的分辨率改变后发送此消息给所有的窗口 - /// - WM_DISPLAYCHANGE = 0x7E, - /// - /// 此消息发送给某个窗口来返回与某个窗口有关连的大图标或小图标的句柄 - /// - WM_GETICON = 0x7F, - /// - /// 程序发送此消息让一个新的大图标或小图标与某个窗口关联 - /// - WM_SETICON = 0x80, - /// - /// 当某个窗口第一次被创建时,此消息在WM_CREATE消息发送前发送 - /// - WM_NCCREATE = 0x81, - /// - /// 此消息通知某个窗口,非客户区正在销毁 - /// - WM_NCDESTROY = 0x82, - /// - /// 当某个窗口的客户区域必须被核算时发送此消息 - /// - WM_NCCALCSIZE = 0x83, - /// - /// 移动鼠标/按住/释放鼠标时 - /// - WM_NCHITTEST = 0x84, - /// - /// 程序发送此消息给某个窗口当它(窗口)的框架必须被绘制时 - /// - WM_NCPAINT = 0x85, - /// - /// 此消息发送给某个窗口仅当它的非客户区需要被改变来显示是激活还是非激活状态 - /// - WM_NCACTIVATE = 0x86, - /// - /// 发送此消息给某个与对话框程序关联的控件,widdows控制方位键和TAB键使输入进入此控件通过应 - /// - WM_GETDLGCODE = 0x87, - /// - /// 当光标在一个窗口的非客户区内移动时发送此消息给这个窗口 非客户区为:窗体的标题栏及窗的边框体 - /// - WM_NCMOUSEMOVE = 0xA0, - /// - /// 当光标在一个窗口的非客户区同时按下鼠标左键时提交此消息 - /// - WM_NCLBUTTONDOWN = 0xA1, - /// - /// 当用户释放鼠标左键同时光标某个窗口在非客户区时发送此消息 - /// - WM_NCLBUTTONUP = 0xA2, - /// - /// 当用户双击鼠标左键同时光标某个窗口在非客户区时发送此消息 - /// - WM_NCLBUTTONDBLCLK = 0xA3, - /// - /// 当用户按下鼠标右键同时光标又在窗口的非客户区时发送此消息 - /// - WM_NCRBUTTONDOWN = 0xA4, - /// - /// 当用户释放鼠标右键同时光标又在窗口的非客户区时发送此消息 - /// - WM_NCRBUTTONUP = 0xA5, - /// - /// 当用户双击鼠标右键同时光标某个窗口在非客户区时发送此消息 - /// - WM_NCRBUTTONDBLCLK = 0xA6, - /// - /// 当用户按下鼠标中键同时光标又在窗口的非客户区时发送此消息 - /// - WM_NCMBUTTONDOWN = 0xA7, - /// - /// 当用户释放鼠标中键同时光标又在窗口的非客户区时发送此消息 - /// - WM_NCMBUTTONUP = 0xA8, - /// - /// 当用户双击鼠标中键同时光标又在窗口的非客户区时发送此消息 - /// - WM_NCMBUTTONDBLCLK = 0xA9, - - // 所有的键盘消息只有中间的八种,也就是 WM_KEYDOWN 到 WM_SYSDEADCHAR - /// - /// 按下一个键 == WM_KEYDOWN - /// - WM_KEYFIRST = 0x0100, - /// - /// 按下一个键 - /// - WM_KEYDOWN = 0x0100, - /// - /// 释放一个键 - /// - WM_KEYUP = 0x0101, - /// - /// 按下某键,并已发出WM_KEYDOWN, WM_KEYUP消息 - /// - WM_CHAR = 0x102, - /// - /// 当用translatemessage函数翻译WM_KEYUP消息时发送此消息给拥有焦点的窗口 - /// - WM_DEADCHAR = 0x103, - /// - /// 当用户按住ALT键同时按下其它键时提交此消息给拥有焦点的窗口 - /// - WM_SYSKEYDOWN = 0x104, - /// - /// 当用户释放一个键同时ALT 键还按着时提交此消息给拥有焦点的窗口 - /// - WM_SYSKEYUP = 0x105, - /// - /// 当WM_SYSKEYDOWN消息被TRANSLATEMESSAGE函数翻译后提交此消息给拥有焦点的窗口 - /// - WM_SYSCHAR = 0x106, - /// - /// 当WM_SYSKEYDOWN消息被TRANSLATEMESSAGE函数翻译后发送此消息给拥有焦点的窗口 - /// - WM_SYSDEADCHAR = 0x107, - /// - /// 在一个对话框程序被显示前发送此消息给它,通常用此消息初始化控件和执行其它任务 - /// - WM_INITDIALOG = 0x110, - /// - /// 当用户选择一条菜单命令项或当某个控件发送一条消息给它的父窗口 - /// - WM_COMMAND = 0x111, - - /// - /// 当用户选择窗口菜单的一条命令或最大化最小化时窗口前会收到此消息 - /// - WM_SYSCOMMAND = 0x112, - /// - /// 发生了定时器事件 - /// - WM_TIMER = 0x113, - /// - /// 当一个窗口标准水平滚动条产生一个滚动事件时发送此消息给那个窗口,也发送给拥有它的控件 - /// - WM_HSCROLL = 0x114, - /// - /// 当一个窗口标准垂直滚动条产生一个滚动事件时发送此消息给那个窗口也,发送给拥有它的控件 - /// - WM_VSCROLL = 0x115, - /// - /// 当一个菜单将要被激活时发送此消息,它发生在用户菜单条中的某项或按下某个菜单键,它允许程序在显示前更改菜单 - /// - WM_INITMENU = 0x116, - /// - /// 当一个下拉菜单或子菜单将要被激活时发送此消息,它允许程序在它显示前更改菜单,而不要改变全部 - /// - WM_INITMENUPOPUP = 0x117, - /// - /// 当用户选择一条菜单项时发送此消息给菜单的所有者(一般是窗口) - /// - WM_MENUSELECT = 0x11F, - /// - /// 当菜单已被激活用户按下了某个键(不同于加速键),发送此消息给菜单的所有者 - /// - WM_MENUCHAR = 0x120, - /// - /// 当一个模态对话框或菜单进入空载状态时发送此消息给它的所有者,一个模态对话框或菜单进入空载状态就是在处理完一条或几条先前的消息后没有消息它的列队中等待 - /// - WM_ENTERIDLE = 0x121, - /// - /// 在windows绘制消息框前发送此消息给消息框的所有者窗口,通过响应这条消息,所有者窗口可以通过使用给定的相关显示设备的句柄来设置消息框的文本和背景颜色 - /// - WM_CTLCOLORMSGBOX = 0x132, - /// - /// 当一个编辑型控件将要被绘制时发送此消息给它的父窗口通过响应这条消息,所有者窗口可以通过使用给定的相关显示设备的句柄来设置编辑框的文本和背景颜色 - /// - WM_CTLCOLOREDIT = 0x133, - - /// - /// 当一个列表框控件将要被绘制前发送此消息给它的父窗口通过响应这条消息,所有者窗口可以通过使用给定的相关显示设备的句柄来设置列表框的文本和背景颜色 - /// - WM_CTLCOLORLISTBOX = 0x134, - /// - /// 当一个按钮控件将要被绘制时发送此消息给它的父窗口通过响应这条消息,所有者窗口可以通过使用给定的相关显示设备的句柄来设置按纽的文本和背景颜色 - /// - WM_CTLCOLORBTN = 0x135, - /// - /// 当一个对话框控件将要被绘制前发送此消息给它的父窗口通过响应这条消息,所有者窗口可以通过使用给定的相关显示设备的句柄来设置对话框的文本背景颜色 - /// - WM_CTLCOLORDLG = 0x136, - /// - /// 当一个滚动条控件将要被绘制时发送此消息给它的父窗口通过响应这条消息,所有者窗口可以通过使用给定的相关显示设备的句柄来设置滚动条的背景颜色 - /// - WM_CTLCOLORSCROLLBAR = 0x137, - /// - /// 当一个静态控件将要被绘制时发送此消息给它的父窗口通过响应这条消息,所有者窗口可以通过使用给定的相关显示设备的句柄来设置静态控件的文本和背景颜色 - /// - WM_CTLCOLORSTATIC = 0x138, - - /// - /// 当鼠标轮子转动时发送此消息个当前有焦点的控件 - /// - WM_MOUSEWHEEL = 0x20A, - /// - /// 双击鼠标中键 - /// - WM_MBUTTONDBLCLK = 0x209, - /// - /// 释放鼠标中键 - /// - WM_MBUTTONUP = 0x208, - /// - /// 移动鼠标时发生,同WM_MOUSEFIRST - /// - WM_MOUSEMOVE = 0x200, - /// - /// 按下鼠标左键 - /// - WM_LBUTTONDOWN = 0x201, - /// - /// 释放鼠标左键 - /// - WM_LBUTTONUP = 0x202, - /// - /// 双击鼠标左键 - /// - WM_LBUTTONDBLCLK = 0x203, - /// - /// 按下鼠标右键 - /// - WM_RBUTTONDOWN = 0x204, - /// - /// 释放鼠标右键 - /// - WM_RBUTTONUP = 0x205, - /// - /// 双击鼠标右键 - /// - WM_RBUTTONDBLCLK = 0x206, - /// - /// 按下鼠标中键 - /// - WM_MBUTTONDOWN = 0x207, - - WM_USER = 0x0400, - - /// - /// 执行复制成功 - /// - WM_CLIPBOARDUPDATE = 0x031D, -} - -// https://blog.csdn.net/biyusr/article/details/108376195 -public enum MOUSEEVENTF : int -{ - /// - /// 移动鼠标 - /// - MOVE = 0x0001, - /// - /// 模拟鼠标左键按下 - /// - LEFTDOWN = 0x0002, - /// - /// 模拟鼠标左键抬起 - /// - LEFTUP = 0x0004, - /// - /// 模拟鼠标右键按下 - /// - RIGHTDOWN = 0x0008, - /// - /// 模拟鼠标右键抬起 - /// - RIGHTUP = 0x0010, - /// - /// 模拟鼠标中键按下 - /// - MIDDLEDOWN = 0x0020, - /// - /// 模拟鼠标中键抬起 - /// - MIDDLEUP = 0x0040, - /// - /// 标示是否采用绝对坐标 - /// - ABSOLUTE = 0x8000, - /// - /// 模拟鼠标滚轮滚动操作,必须配合dwData参数 - /// - WHEEL = 0x0800, -} - -// C#使用SendMessage发送组合键 -// https://www.cnblogs.com/johnsonton/articles/2331430.html -// Windows 使用的256个虚拟键码 -[Flags]// 打印的时候可以有名称输出,而不是值输出 -public enum VK : int -{ - VK_LBUTTON = 0x1, - VK_RBUTTON = 0x2, - VK_CANCEL = 0x3, - VK_MBUTTON = 0x4, - VK_BACK = 0x8, - VK_TAB = 0x9, - VK_CLEAR = 0xC, - VK_RETURN = 0xD, - VK_SHIFT = 0x10, - VK_CONTROL = 0x11, - VK_MENU = 0x12,// VK_ALT - VK_ALT = 0x12, - VK_PAUSE = 0x13, - VK_CAPITAL = 0x14, - VK_ESCAPE = 0x1B, - VK_SPACE = 0x20, - VK_PRIOR = 0x21, - VK_NEXT = 0x22, - VK_END = 0x23, - VK_HOME = 0x24, - VK_LEFT = 0x25, - VK_UP = 0x26, - VK_RIGHT = 0x27, - VK_DOWN = 0x28, - VK_Select = 0x29, - VK_PRINT = 0x2A, - VK_EXECUTE = 0x2B, - VK_SNAPSHOT = 0x2C, - VK_Insert = 0x2D, - VK_Delete = 0x2E, - VK_HELP = 0x2F, - VK_0 = 0x30, - VK_1 = 0x31, - VK_2 = 0x32, - VK_3 = 0x33, - VK_4 = 0x34, - VK_5 = 0x35, - VK_6 = 0x36, - VK_7 = 0x37, - VK_8 = 0x38, - VK_9 = 0x39, - VK_A = 0x41, - VK_B = 0x42, - VK_C = 0x43, - VK_D = 0x44, - VK_E = 0x45, - VK_F = 0x46, - VK_G = 0x47, - VK_H = 0x48, - VK_I = 0x49, - VK_J = 0x4A, - VK_K = 0x4B, - VK_L = 0x4C, - VK_M = 0x4D, - VK_N = 0x4E, - VK_O = 0x4F, - VK_P = 0x50, - VK_Q = 0x51, - VK_R = 0x52, - VK_S = 0x53, - VK_T = 0x54, - VK_U = 0x55, - VK_V = 0x56, - VK_W = 0x57, - VK_X = 0x58, - VK_Y = 0x59, - VK_Z = 0x5A, - VK_STARTKEY = 0x5B, - VK_CONTEXTKEY = 0x5D, - VK_NUMPAD0 = 0x60, - VK_NUMPAD1 = 0x61, - VK_NUMPAD2 = 0x62, - VK_NUMPAD3 = 0x63, - VK_NUMPAD4 = 0x64, - VK_NUMPAD5 = 0x65, - VK_NUMPAD6 = 0x66, - VK_NUMPAD7 = 0x67, - VK_NUMPAD8 = 0x68, - VK_NUMPAD9 = 0x69, - VK_MULTIPLY = 0x6A, - VK_ADD = 0x6B, - VK_SEPARATOR = 0x6C, - VK_SUBTRACT = 0x6D, - VK_DECIMAL = 0x6E, - VK_DIVIDE = 0x6F, - VK_F1 = 0x70, - VK_F2 = 0x71, - VK_F3 = 0x72, - VK_F4 = 0x73, - VK_F5 = 0x74, - VK_F6 = 0x75, - VK_F7 = 0x76, - VK_F8 = 0x77, - VK_F9 = 0x78, - VK_F10 = 0x79, - VK_F11 = 0x7A, - VK_F12 = 0x7B, - VK_F13 = 0x7C, - VK_F14 = 0x7D, - VK_F15 = 0x7E, - VK_F16 = 0x7F, - VK_F17 = 0x80, - VK_F18 = 0x81, - VK_F19 = 0x82, - VK_F20 = 0x83, - VK_F21 = 0x84, - VK_F22 = 0x85, - VK_F23 = 0x86, - VK_F24 = 0x87, - VK_NUMLOCK = 0x90, - VK_OEM_SCROLL = 0x91, - VK_OEM_1 = 0xBA, - VK_OEM_PLUS = 0xBB, - VK_OEM_COMMA = 0xBC, - VK_OEM_MINUS = 0xBD, - VK_OEM_PERIOD = 0xBE, - VK_OEM_2 = 0xBF, - VK_OEM_3 = 0xC0, - VK_OEM_4 = 0xDB, - VK_OEM_5 = 0xDC, - VK_OEM_6 = 0xDD, - VK_OEM_7 = 0xDE, - VK_OEM_8 = 0xDF, - VK_ICO_F17 = 0xE0, - VK_ICO_F18 = 0xE1, - VK_OEM102 = 0xE2, - VK_ICO_HELP = 0xE3, - VK_ICO_00 = 0xE4, - VK_ICO_CLEAR = 0xE6, - VK_OEM_RESET = 0xE9, - VK_OEM_JUMP = 0xEA, - VK_OEM_PA1 = 0xEB, - VK_OEM_PA2 = 0xEC, - VK_OEM_PA3 = 0xED, - VK_OEM_WSCTRL = 0xEE, - VK_OEM_CUSEL = 0xEF, - VK_OEM_ATTN = 0xF0, - VK_OEM_FINNISH = 0xF1, - VK_OEM_COPY = 0xF2, - VK_OEM_AUTO = 0xF3, - VK_OEM_ENLW = 0xF4, - VK_OEM_BACKTAB = 0xF5, - VK_ATTN = 0xF6, - VK_CRSEL = 0xF7, - VK_EXSEL = 0xF8, - VK_EREOF = 0xF9, - VK_PLAY = 0xFA, - VK_ZOOM = 0xFB, - VK_NONAME = 0xFC, - VK_PA1 = 0xFD, - VK_OEM_CLEAR = 0xFE, -} - -[Flags] -public enum SC : uint -{ - // 窗体关闭消息 - SC_CLOSE = 0xf060, - // 窗体最小化消息 - SC_MINIMIZE = 0xf020, - // 窗体最大化消息 - SC_MAXIMIZE = 0xf030, - // 窗体正常态消息 SC_RESTORE = 0xf120, - SC_NOMAL = 0xf120, -} - -[Flags] -public enum NCmdShow : uint -{ - /// - /// 隐藏窗口并激活其他窗口。nCmdShow - /// - SW_HIDE = 0, - /// - /// 正常态的窗口(非最大化和非最小化) - /// 激活并显示一个窗口。如果窗口被最小化或最大化,系统将其恢复到原来的尺寸和大小。应用程序在第一次显示窗口的时候应该指定此标志 - /// - SW_SHOWNORMAL = 1, - /// - /// 激活窗口并将其最小化 - /// - SW_SHOWMINIMIZED = 2, - /// - /// 激活窗口并将其最大化 - /// - SW_SHOWMAXIMIZED = 3, - /// - /// 最大化指定的窗口 - /// - SW_MAXIMIZE = 3, - /// - /// 以窗口最近一次的大小和状态显示窗口。激活窗口仍然维持激活状态 - /// - SW_SHOWNOACTIVATE = 4, - /// - /// 在窗口原来的位置以原来的尺寸激活和显示窗口 - /// - SW_SHOW = 5, - /// - /// 最小化指定的窗口并且激活在Z序中的下一个顶层窗口 - /// - SW_MINIMIZE = 6, - /// - /// 窗口最小化,激活窗口仍然维持激活状态 - /// - SW_SHOWMINNOACTIVE = 7, - /// - /// 以窗口原来的状态显示窗口。激活窗口仍然维持激活状态 - /// - SW_SHOWNA = 8, - /// - /// 激活并显示窗口。如果窗口最小化或最大化,则系统将窗口恢复到原来的尺寸和位置。在恢复最小化窗口时,应用程序应该指定这个标志 - /// - SW_RESTORE = 9, - /// - /// 依据在STARTUPINFO结构中指定的SW_FLAG标志设定显示状态,STARTUPINFO 结构是由启动应用程序的程序传递给CreateProcess函数的 - /// - SW_SHOWDEFAULT = 10, - /// - /// 在WindowNT5.0中最小化窗口,即使拥有窗口的线程被挂起也会最小化。在从其他线程最小化窗口时才使用这个参数 - /// - SW_FORCEMINIMIZE = 11, -} - -public enum WS : uint -{ - // 窗口风格 - WS_CAPTION = 0xC00000, // 带标题栏的窗口 - WS_MAXIMIZEBOX = 0x10000, // 带最大化按钮的窗口 - WS_MINIMIZEBOX = 0x20000, // 带最小化按钮的窗口 - WS_SYSMENU = 0x80000, // 带系统菜单的窗口 - WS_CLIPSIBLINGS = 0x4000000, // 不重绘层叠子窗口 - WS_CLIPCHILDREN = 0x2000000, // 绘图时排子窗口区域 - WS_OVERLAPPED = 0x0, // 具有标题栏和边框的层叠窗口 - WS_THICKFRAME = 0x40000, // 具有可调边框 - - // 具有标题栏、窗口菜单、可调边框和最大化、最小化按钮的窗口 - WS_OVERLAPPEDWINDOW = WS_OVERLAPPED | WS_CAPTION | WS_SYSMENU | WS_THICKFRAME | WS_MINIMIZEBOX | WS_MAXIMIZEBOX, - - WS_GROUP = 0x20000, // 指定一组控制的第一个控制 - WS_POPUP = 0x80000000, // 弹出式窗口 - WS_BORDER = 0x800000, // 单边框窗口 - WS_POPUPWINDOW = WS_POPUP | WS_BORDER | WS_SYSMENU, // 具有单边框、标题栏菜单的弹出式窗口 - WS_MINIMIZE = 0x20000000, // 窗口最小化 - WS_VISIBLE = 0x10000000, // 窗口可见 - WS_DISABLED = 0x8000000, // 窗口被禁用 - WS_MAXIMIZE = 0x1000000, // 窗口最大化 - WS_DLGFRAME = 0x400000, // 对话框边框风格 - WS_VSCROLL = 0x200000, // 具有垂直滚动条 - WS_HSCROLL = 0x100000, // 具有水平滚动条 - WS_TABSTOP = 0x10000, // 具有TAB键控制 - WS_CHILD = 0x40000000, // 设置窗口属性为child 多文档界面的子窗体 - WS_CHILDWINDOW = WS_CHILD, // 具有子窗口 - - // 扩展风格 - WS_EX_WINDOWEDGE = 0x100, // 窗口具有凸起的3D边框 - WS_EX_CLIENTEDGE = 0x200, // 窗口具有阴影边界 - WS_EX_TOOLWINDOW = 0x80, // 小标题工具窗口 - WS_EX_TOPMOST = 0x8, // 窗口总在顶层 const int WS_EX_TOPMOST = 0x00000008; - WS_EX_OVERLAPPEDWINDOW = WS_EX_WINDOWEDGE | WS_EX_CLIENTEDGE, // WS_EX-CLIENTEDGE和WS_EX_WINDOWEDGE的组合 - WS_EX_PALETTEWINDOW = WS_EX_WINDOWEDGE | WS_EX_TOOLWINDOW | WS_EX_TOPMOST, // WS_EX_WINDOWEDGE和WS_EX_TOOLWINDOW和WS_EX_TOPMOST的组合 - WS_EX_DLGMODALFRAME = 0x1, // 带双边的窗口 - WS_EX_NOPARENTNOTIFY = 0x4, // 窗口在创建和销毁时不向父窗口发送WM_PARENTNOTIFY消息 - WS_EX_TRANSPARENT = 0x20, // 窗口透眀 - WS_EX_MDICHILD = 0x40, // MDI子窗口 - WS_EX_CONTEXTHELP = 0x400, // 标题栏包含问号联机帮助按钮 - WS_EX_RIGHT = 0x1000, // 窗口具有右对齐属性 - WS_EX_RTLREADING = 0x2000, // 窗口文本自右向左显示 - WS_EX_LEFTSCROLLBAR = 0x4000, // 标题栏在客户区的左边 - WS_EX_CONTROLPARENT = 0x10000, // 允许用户使用Tab键在窗口的子窗口间搜索 - WS_EX_STATICEDGE = 0x20000, // 为不接受用户输入的项创建一个三维边界风格 - WS_EX_APPWINDOW = 0x40000, // 在任务栏上显示顶层窗口的标题按钮 - WS_EX_LAYERED = 0x80000, // 窗口具有透眀属性(Win2000)以上 - WS_EX_NOINHERITLAYOUT = 0x100000, // 窗口布局不传递给子窗口(Win2000)以上 - WS_EX_LAYOUTRTL = 0x400000, // 水平起点在右边的窗口 - WS_EX_NOACTIVATE = 0x8000000, // 窗口不会变成前台窗口(Win2000)以上 - WS_EX_LEFT = 0x0, // 窗口具有左对齐属性 - WS_EX_LTRREADING = 0x0, // 窗口文本自左向右显示 - WS_EX_RIGHTSCROLLBAR = 0x0, // 垂直滚动条在窗口的右边界 - WS_EX_ACCEPTFILES = 0x10, // 接受文件拖曳 - WS_EX_COMPOSITED = 0x2000000, // 窗体所有子窗口使用双缓冲从低到高绘制(XP) -} - -public enum GWL : int -{ - /// - /// 获取、设置窗口过程的地址 - /// - GWL_WNDPROC = -4, - /// - /// 获取应用程序的实例句柄 - /// - GWL_HINSTANCE = -6, - /// - /// 获取父窗口句柄 - /// - GWL_HWNDPARENT = -8, - /// - /// 获取窗口标识 - /// - GWL_ID = -12, - /// - /// 获取、设置窗口样式 - /// - GWL_STYLE = -16, - /// - /// 获取、设置窗口扩展样式 - /// - GWL_EXSTYLE = -20, - /// - /// 获取、设置与窗口关联的自定义数据 - /// - GWL_USERDATA = -21, -} - -public enum GetWindowCmd : uint -{ - /// - /// 返回的句柄标识了在Z序最高端的相同类型的窗口。 - /// 如果指定窗口是最高端窗口,则该句柄标识了在Z序最高端的最高端窗口; - /// 如果指定窗口是顶层窗口,则该句柄标识了在z序最高端的顶层窗口: - /// 如果指定窗口是子窗口,则句柄标识了在Z序最高端的同属窗口。 - /// - GW_HWNDFIRST = 0, - /// - /// 返回的句柄标识了在z序最低端的相同类型的窗口。 - /// 如果指定窗口是最高端窗口,则该柄标识了在z序最低端的最高端窗口: - /// 如果指定窗口是顶层窗口,则该句柄标识了在z序最低端的顶层窗口; - /// 如果指定窗口是子窗口,则句柄标识了在Z序最低端的同属窗口。 - /// - GW_HWNDLAST = 1, - /// - /// 返回的句柄标识了在Z序中指定窗口下的相同类型的窗口。 - /// 如果指定窗口是最高端窗口,则该句柄标识了在指定窗口下的最高端窗口: - /// 如果指定窗口是顶层窗口,则该句柄标识了在指定窗口下的顶层窗口; - /// 如果指定窗口是子窗口,则句柄标识了在指定窗口下的同属窗口。 - /// - GW_HWNDNEXT = 2, - /// - /// 返回的句柄标识了在Z序中指定窗口上的相同类型的窗口。 - /// 如果指定窗口是最高端窗口,则该句柄标识了在指定窗口上的最高端窗口; - /// 如果指定窗口是顶层窗口,则该句柄标识了在指定窗口上的顶层窗口; - /// 如果指定窗口是子窗口,则句柄标识了在指定窗口上的同属窗口。 - /// - GW_HWNDPREV = 3, - /// - /// 返回的句柄标识了指定窗口的所有者窗口(如果存在)。 - /// GW_OWNER与GW_CHILD不是相对的参数,没有父窗口的含义,如果想得到父窗口请使用GetParent()。 - /// 例如:例如有时对话框的控件的GW_OWNER,是不存在的。 - /// - GW_OWNER = 4, - /// - /// 如果指定窗口是父窗口,则获得的是在Tab序顶端的子窗口的句柄,否则为NULL。 - /// 函数仅检查指定父窗口的子窗口,不检查继承窗口。 - /// - GW_CHILD = 5, - /// - /// (WindowsNT 5.0)返回的句柄标识了属于指定窗口的处于使能状态弹出式窗口(检索使用第一个由GW_HWNDNEXT 查找到的满足前述条件的窗口); - /// 如果无使能窗口,则获得的句柄与指定窗口相同。 - /// - GW_ENABLEDPOPUP = 6 -} -#endif \ No newline at end of file diff --git a/src/IFoxCAD.Cad.Shared/WindowsAPI/MouseHook.cs b/src/IFoxCAD.Cad.Shared/WindowsAPI/MouseHook.cs deleted file mode 100644 index 2d6b013..0000000 --- a/src/IFoxCAD.Cad.Shared/WindowsAPI/MouseHook.cs +++ /dev/null @@ -1,293 +0,0 @@ -namespace IFoxCAD.Cad; - -using System; -using System.Diagnostics; -using System.Drawing; -using System.Runtime.InteropServices; -using System.Windows.Forms; - -public class MouseHook -{ - /// - /// 鼠标按下事件 - /// - public event MouseEventHandler? MouseDown; - /// - /// 松开鼠标事件 - /// - public event MouseEventHandler? MouseUp; - /// - /// 鼠标移动事件 - /// - public event MouseEventHandler? MouseMove; - /// - /// 鼠标滚轮事件 - /// - public event MouseEventHandler? MouseWheel; - /// - /// 鼠标单击事件 - /// - public event EventHandler? Click; - /// - /// 鼠标双击事件 - /// - public event EventHandler? DoubleClick; - - - bool _isHookBreak = false; - /// - /// 否决本次输入:设置不向下回调 - /// - public void Vote() - { - _isHookBreak = true; - } - - /// 不要试图省略此变量,否则将会导致GC变量池满后释放
    - /// 提示:激活 CallbackOnCollectedDelegate 托管调试助手(MDA) - internal static WindowsAPI.CallBack? HookProc; - internal static IntPtr _NextHookProc;//挂载成功的标记 - public readonly Process Process; - - - [DllImport("user32.dll", EntryPoint = "GetDoubleClickTime")] - public extern static int GetDoubleClickTime(); - static readonly Stopwatch _watch = new(); - - /// - /// 安装鼠标钩子 - /// - /// 低级钩子超时时间 - public MouseHook(int setLowLevel = 25000) - { - _NextHookProc = IntPtr.Zero; - Process = Process.GetCurrentProcess(); - WindowsAPI.CheckLowLevelHooksTimeout(setLowLevel); - _watch.Start(); - } - - void UnHook() - { - if (_NextHookProc != IntPtr.Zero) - { - WindowsAPI.UnhookWindowsHookEx(_NextHookProc); - _NextHookProc = IntPtr.Zero; - } - } - - /// - /// 设置钩子 - /// - /// false进程钩子,true全局钩子 - public void SetHook(bool processHook = false) - { - UnHook(); - if (_NextHookProc != IntPtr.Zero) - return; - - if (processHook) - { - HookProc = (nCode, wParam, lParam) => { - if (nCode >= 0 && HookTask(nCode, wParam, lParam)) - return (IntPtr)1; - return WindowsAPI.CallNextHookEx(_NextHookProc, nCode, wParam, lParam); - }; - _NextHookProc = WindowsAPI.SetWindowsHookEx(HookType.WH_MOUSE, HookProc, - IntPtr.Zero, WindowsAPI.GetCurrentThreadId()); - } - else - { - var moduleHandle = WindowsAPI.GetModuleHandle(Process.MainModule.ModuleName); - HookProc = (nCode, wParam, lParam) => { - if (nCode >= 0 && HookTask(nCode, wParam, lParam)) - return (IntPtr)1; - return WindowsAPI.CallNextHookEx(_NextHookProc, nCode, wParam, lParam); - }; - _NextHookProc = WindowsAPI.SetWindowsHookEx(HookType.WH_MOUSE_LL, HookProc, - moduleHandle, 0); - } - } - - - - MouseButtons _button; - int _clickCount = 0; - bool _down = false; - bool _up = false; - bool _ck = false; - - /// - /// 钩子的消息处理 - /// - /// - /// - /// - /// false不终止回调,true终止回调 - bool HookTask(int nCode, int wParam, IntPtr lParam) - { - if (MouseDown is null - && MouseUp is null - && MouseMove is null - && MouseWheel is null - && Click is null - && DoubleClick is null) - return false; - - _button = MouseButtons.None; - _clickCount = 0; - _down = false; - _up = false; - _ck = false; - - switch ((WM)wParam) - { - case WM.WM_LBUTTONDOWN: - _button = MouseButtons.Left; - _clickCount = 1; - _down = true; - _ck = true; - break; - case WM.WM_LBUTTONUP: - _button = MouseButtons.Left; - _clickCount = 1; - _up = true; - break; - case WM.WM_LBUTTONDBLCLK: - _button = MouseButtons.Left; - _clickCount = 2; - _ck = true; - break; - case WM.WM_RBUTTONDOWN: - _button = MouseButtons.Right; - _clickCount = 1; - _down = true; - _ck = true; - break; - case WM.WM_RBUTTONUP: - _button = MouseButtons.Right; - _clickCount = 1; - _up = true; - break; - case WM.WM_RBUTTONDBLCLK: - _button = MouseButtons.Right; - _clickCount = 2; - _ck = true; - break; - case WM.WM_MBUTTONDOWN: - _button = MouseButtons.Middle; - _clickCount = 1; - _ck = true; - break; - case WM.WM_MOUSEWHEEL: - // 滚轮 - break; - case WM.WM_MOUSEMOVE: - // 移动 - // 假设想要限制鼠标在屏幕中的移动区域能够在此处设置 - // 后期须要考虑实际的x y的容差 - // if (!Screen.PrimaryScreen.Bounds.Contains(e.X, e.Y)) - // // return 1; - // if (button == MouseButtons.Left) - // { - // GetCursorPos(out POINT pt); - // // 防止频繁获取导致出错 - // if (pt0ld.Leng(pt) > 20) - // pt0ld = pt; - // } - break; - } - - // 从回调函数中得到鼠标的信息 - var mouseMsg = MouseHookStruct.Create(lParam); - MouseEventArgs e = new(_button, _clickCount, mouseMsg.Point.X, mouseMsg.Point.Y, 0); - if (_down) - MouseDown?.Invoke(this, e); - if (_up) - MouseUp?.Invoke(this, e); - if (_ck) - Click?.Invoke(this, e); - if (_clickCount == 2) - { - // 如果不用时间控制,那么双击会执行两次 - if (_watch.Elapsed.TotalMilliseconds > GetDoubleClickTime()) - { - DoubleClick?.Invoke(this, e); - _watch.Reset(); - _watch.Start(); - } - } - MouseMove?.Invoke(this, e); - MouseWheel?.Invoke(this, e); - - // 屏蔽此输入 - if (_isHookBreak) - return true; - - return false; - } - - - /// - /// Hook鼠标数据结构 - /// - [StructLayout(LayoutKind.Sequential)] - public struct MouseHookStruct - { - /// - /// 鼠标在屏幕上的x,y坐标 - /// - public Point Point; - /// - /// 点击窗体的句柄 - /// - public IntPtr hWnd; - /// - /// 消息 - /// - public int wHitTestCode; - /// - /// 扩展信息,可以使用GetMessageExtraInfo的返回值 - /// - public int dwExtraInfo; - - public static MouseHookStruct Create(IntPtr lParam) - { - return (MouseHookStruct)Marshal.PtrToStructure(lParam, typeof(MouseHookStruct)); - } - - public void ToPtr(IntPtr lParam) - { - Marshal.StructureToPtr(this, lParam, true); - } - } - - - #region IDisposable接口相关函数 - public bool IsDisposed { get; private set; } = false; - - /// - /// 手动调用释放 - /// - public void Dispose() - { - Dispose(true); - GC.SuppressFinalize(this); - } - - /// - /// 析构函数调用释放 - /// - ~MouseHook() - { - Dispose(false); - } - - protected virtual void Dispose(bool disposing) - { - // 不重复释放,并设置已经释放 - if (IsDisposed) return; - IsDisposed = true; - UnHook(); - } - #endregion -} \ No newline at end of file diff --git a/src/IFoxCAD.Cad.Shared/WindowsAPI/WindowsAPI.cs b/src/IFoxCAD.Cad.Shared/WindowsAPI/WindowsAPI.cs deleted file mode 100644 index b1a7f92..0000000 --- a/src/IFoxCAD.Cad.Shared/WindowsAPI/WindowsAPI.cs +++ /dev/null @@ -1,659 +0,0 @@ -#define Marshal -using System.Diagnostics; -using System.Runtime.CompilerServices; -using System.Threading; - -namespace IFoxCAD.Cad; -public partial class WindowsAPI -{ - #region kernel32 - // https://blog.csdn.net/haelang/article/details/45147121 - [DllImport("kernel32.dll")] - public extern static uint GetLastError(); - - [DllImport("kernel32.dll")] - public static extern long GetHandleInformation(long hObject, ref long lpdwFlags); - - [DllImport("kernel32.dll")] - public static extern IntPtr GetModuleHandle(string ModuleName); - - [DllImport("kernel32.dll")] - public static extern int GetCurrentThreadId(); - - /// - /// 获取要引入的函数,将符号名或标识号转换为DLL内部地址 - /// - /// exe/dll句柄 - /// 接口名 - /// - [DllImport("kernel32.dll", SetLastError = true, CharSet = CharSet.Ansi, ExactSpelling = true)] - public static extern IntPtr GetProcAddress(IntPtr hModule, string procName); - - /// - /// 锁定内存 - /// - /// - /// - [DllImport("kernel32.dll", SetLastError = true)] - static extern IntPtr GlobalLock(IntPtr hMem); - /// - /// 解锁内存 - /// - /// - /// - [DllImport("kernel32.dll", SetLastError = true)] - static extern bool GlobalUnlock(IntPtr hMem); -#if !Marshal - /* - const int GMEM_MOVEABLE = 0x0002; - IntPtr newPtr = WindowsAPI.GlobalAlloc(GMEM_MOVEABLE, Marshal.SizeOf(structObj)); - */ - /// - /// 从堆中分配内存 - /// 被代替: Marshal.AllocHGlobal - /// - /// 分配方式 - /// 分配的字节数 - /// - [DllImport("kernel32.dll", SetLastError = true)] - public static extern IntPtr GlobalAlloc(uint uFlags, int dwBytes); - /// - /// 释放堆内存 - /// 被代替: Marshal.FreeHGlobal - /// - /// 产生的句柄 - /// - [DllImport("kernel32.dll", SetLastError = true)] - public static extern IntPtr GlobalFree(IntPtr hMem); -#endif - /// - /// 获取内存块大小 - /// - /// - /// - [DllImport("kernel32.dll", SetLastError = true)] - public static extern uint GlobalSize(IntPtr hMem); - - /// - /// 锁定和释放内存 - /// - /// 锁定数据对象指针 - /// 返回锁定的内存片段指针,锁定期间执行任务 - /// 是否锁定成功 - /// - public static bool GlobalLockTask(IntPtr data, Action task) - { - if (task == null) - throw new ArgumentNullException(nameof(task)); - if (data == IntPtr.Zero) - return false; - - try - { - var ptr = GlobalLock(data); - // 有几率导致无效锁定: - // 重复复制同一个图元时,第二次是 IntPtr.Zero, - // 第三次就又可以复制了 - if (ptr == IntPtr.Zero) - return false; - task.Invoke(ptr); - } - finally { GlobalUnlock(data); } - return true; - } - - /// - /// byte数组转结构体 - /// - /// byte数组 - /// 返回的结构大小 - /// 返回的结构体 - [Obsolete("效率太低", true)] - public static T? BytesToStruct(byte[] bytes, out int typeSize) - { - var structType = typeof(T); - typeSize = Marshal.SizeOf(structType); - if (typeSize > bytes.Length) - return default; - - // 安全写法效率太低了 - // 分配结构体大小的内存空间 - IntPtr structPtr = Marshal.AllocHGlobal(typeSize); - - // 将byte数组拷到分配好的内存空间 - Marshal.Copy(bytes, 0, structPtr, typeSize); - // 将内存空间转换为目标结构体; - // 转类型的时候会拷贝一次,看它们地址验证 &result != &structPtr - var result = (T)Marshal.PtrToStructure(structPtr, structType); - - // 释放内存空间 - Marshal.FreeHGlobal(structPtr); - return result; - } - - /// - /// byte数组转结构体 - /// - /// byte数组 - /// 返回的结构体 - [MethodImpl] - public static T? BytesToStruct(byte[] bytes) - { - T? result = default; - unsafe - { - // 安全指针方法 - // var pB = Marshal.UnsafeAddrOfPinnedArrayElement(bytes, 0); - // 不安全指针方法 - fixed (byte* pB = &bytes[0]) - { - result = (T?)Marshal.PtrToStructure(new IntPtr(pB), typeof(T)); - } - } - return result; - } - - /// - /// 结构体转byte数组 - /// unmanaged - /// - /// 要转换的结构体 - [MethodImpl] - public static byte[] StructToBytes(T structObj) where T : unmanaged/*非托管的T从来不为空*/ - { - // 得到结构体的大小 - var typeSize = Marshal.SizeOf(structObj); - // 从内存空间拷到byte数组 - var bytes = new byte[typeSize]; - unsafe - { - Marshal.Copy(new IntPtr(&structObj), bytes, 0, typeSize); - } -#if true20221030 - // 安全写法效率太低了 - StructToPtr(structObj, structPtr => { - Marshal.Copy(structPtr, bytes, 0, typeSize); - }); -#endif - return bytes; - } - -#if true20221030 - /// - /// 结构体转指针 - /// - /// 要转换的结构体 - /// 输出指针 - /// 释放申请的内存 - /// 是否锁定内存 - /// - public static void StructToPtr(T structObj, - Action? task = null, - bool freeHGlobal = true, - bool lockPrt = true) - { - IntPtr newPtr = Marshal.AllocHGlobal(Marshal.SizeOf(structObj)); - if (newPtr == IntPtr.Zero) - throw new ArgumentException(nameof(newPtr)); - - try - { - // 剪贴板写入的时候不允许锁定内存,否则在频繁触发剪贴板将导致卡死程序 - if (lockPrt) - { - GlobalLockTask(newPtr, ptr => { - // 将结构体拷到分配好的内存空间 - Marshal.StructureToPtr(structObj, newPtr, true); - task?.Invoke(newPtr); - }); - } - else - { - // 将结构体拷到分配好的内存空间 - Marshal.StructureToPtr(structObj, newPtr, true); - task?.Invoke(newPtr); - } - } - catch (Exception e) - { - Debugger.Break(); - Debugx.Printl(e.Message); - } - finally - { - if (freeHGlobal && newPtr != IntPtr.Zero) - Marshal.FreeHGlobal(newPtr); - } - } -#endif - #endregion -} - -public partial class WindowsAPI -{ - #region imm32 - /// - /// 获取输入法的虚拟键码 - /// - /// - /// - [DllImport("imm32.dll")] - public static extern IntPtr ImmGetVirtualKey(IntPtr hWnd); - /// - /// 获取输入法状态 - /// - /// 输入法标识符 - /// 输入模式 - /// 指向函数在其中检索句子模式值的变量的指针 - /// - [DllImport("imm32.dll")] - public static extern bool ImmGetConversionStatus(IntPtr himc, out int lpdw, out int lpdw2); - - /// - /// 获取指定窗口的输入法状态 - /// - /// 窗口句柄 - /// - [DllImport("imm32.dll")] - public static extern IntPtr ImmGetContext(IntPtr hwnd); - /// - /// 设置输入法的当前状态 - /// - /// 窗口句柄 - /// - /// - [DllImport("imm32.dll")] - public static extern bool ImmSetOpenStatus(IntPtr hwnd, bool fOpen); - /// - /// 输入法打开状态 - /// - /// - /// 非0打开,0关闭;(true中文,false英文) - [DllImport("imm32.dll")] - public static extern bool ImmGetOpenStatus(IntPtr hwnd); - #endregion -} - -public partial class WindowsAPI -{ - #region user32 - - /// - /// 获取窗口客户区的大小,客户区为窗口中除标题栏,菜单栏之外的地方 - /// - /// - /// - /// - [DllImport("user32.dll", CharSet = CharSet.Auto, EntryPoint = "GetClientRect")] - public static extern bool GetClientRect(IntPtr hwnd, out IntRect lpRect); - - /// - /// 查找主线程
    - /// 代替
    - /// 托管线程和他们不一样: - ///
    - /// 主窗口 - /// 进程ID - /// 线程ID - [DllImport("user32.dll", SetLastError = true)] - public static extern uint GetWindowThreadProcessId(IntPtr hWnd, out uint lpdwProcessId); - - /// - /// 设置焦点 - /// - /// - /// - [DllImport("user32.dll")] - public static extern IntPtr SetFocus(IntPtr hWnd); - - /// - /// 获取当前窗口 - /// - /// 当前窗口标识符 - [DllImport("user32.dll")] - public static extern IntPtr GetForegroundWindow(); - /// - /// 将一个消息的组成部分合成一个消息并放入对应线程消息队列的方法 - /// - /// 控件句柄 - /// 消息是什么。键盘按键、鼠标点击还是其他 - /// - /// - /// - [DllImport("user32.dll")] - public static extern bool PostMessage(IntPtr hhwnd, int msg, IntPtr wparam, IntPtr lparam); - /// - /// 发送击键 - /// - /// - /// - /// - /// - [DllImport("user32.dll", EntryPoint = "keybd_event")] - public static extern void KeybdEvent(byte bVk, byte bScan, int dwFlags, int dwExtraInfo); - /// - /// 获取窗口文字的长度 - /// - /// 窗口标识符 - /// 文字长度 - [DllImport("user32.dll")] - public static extern int GetWindowTextLength(IntPtr hWnd); - /// - /// 获取窗口的标题 - /// - /// 窗口标识符 - /// 窗口文字 - /// 文字长度 - /// - [DllImport("User32.dll", CharSet = CharSet.Auto)] - public static extern int GetWindowText(IntPtr hWnd, StringBuilder text, int nMaxCount); - - // [DllImport("user32.dll", CharSet = CharSet.Unicode, SetLastError = true)] - // internal static extern int GetWindowText(IntPtr hWnd, StringBuilder lpString, int nMaxCount); - - - /// - /// 获取某个线程的输入法布局 - /// - /// 线程ID - /// 布局码 - [DllImport("user32.dll")] - public static extern int GetKeyboardLayout(int threadid); - - - /// - /// 获取按键的当前状态 - /// - /// 按键虚拟代码 - /// 表示没按下>0;按下<0 - [DllImport("user32.dll")] - public static extern short GetKeyState(int nVirtKey); - /// - /// 检索指定窗口所属的类的名称。 - /// - /// 窗口标识符 - /// - /// - /// - [DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)] - public static extern int GetClassName(IntPtr hWnd, StringBuilder lpClassName, int nMaxCount); - - [DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)] - public static extern IntPtr GetWindow(IntPtr hWnd, uint uCmd); - - [DllImport("user32.DLL", CharSet = CharSet.Unicode, CallingConvention = CallingConvention.StdCall, SetLastError = true)] - public static extern IntPtr GetTopWindow(IntPtr hWnd); - - - /// - /// 获取线程对应的窗体信息 - /// - /// 线程 - /// - /// - [DllImport("user32.dll")] - [return: MarshalAs(UnmanagedType.Bool)] - static extern bool GetGUIThreadInfo(uint idThread, ref GuiThreadInfo lpgui); - - /// - /// 获取线程对应的窗体信息 - /// - [StructLayout(LayoutKind.Sequential)] - public struct GuiThreadInfo - { - public int cbSize; - public int flags; - public IntPtr hwndActive; - public IntPtr hwndFocus; - public IntPtr hwndCapture; - public IntPtr hwndMenuOwner; - public IntPtr hwndMoveSize; - public IntPtr hwndCaret; - public System.Drawing.Rectangle rcCaret; - - public static GuiThreadInfo Create(uint windowThreadProcessId) - { - if (windowThreadProcessId == 0) - throw new ArgumentNullException(nameof(windowThreadProcessId)); - - GuiThreadInfo gti = new(); - gti.cbSize = Marshal.SizeOf(gti); - GetGUIThreadInfo(windowThreadProcessId, ref gti); - return gti; - } - } - - [DllImport("user32.dll", SetLastError = true)] - public static extern IntPtr GetFocus(); - - [DllImport("user32.dll")] - public static extern IntPtr SendMessage(IntPtr hwnd, int msg, IntPtr wParam, IntPtr lParam); - - [DllImport("user32.dll", SetLastError = true)] - public static extern IntPtr GetParent(IntPtr hWnd); - - [DllImport("user32.dll")] - public static extern int ToAscii(int uVirtKey, int uScancode, byte[] lpdKeyState, byte[] lpwTransKey, int fuState); - - [DllImport("user32.dll", SetLastError = true)] - public static extern IntPtr GetActiveWindow(); - - [DllImport("user32.dll", SetLastError = true)] - public static extern long GetWindowThreadProcessId(IntPtr hwnd, ref int lpdwProcessId); - - [DllImport("user32.dll", SetLastError = true)] - public static extern bool IsIconic(int hWnd); - - [DllImport("user32.dll")] - public static extern bool IsWindowEnabled(IntPtr hWnd); - #endregion - - #region 键盘钩子 - public delegate IntPtr CallBack(int nCode, int wParam, IntPtr lParam); - [DllImport("user32.dll")] - public static extern IntPtr SetWindowsHookEx(HookType idHook, CallBack lpfn, IntPtr hmod, int dwThreadId); - [DllImport("user32.dll")] - public static extern IntPtr UnhookWindowsHookEx(IntPtr hHook); - [DllImport("user32.dll")] - public static extern IntPtr CallNextHookEx(IntPtr hHook, int ncode, int wParam, IntPtr lParam); - /// - /// Hook键盘数据结构 - /// - [ComVisible(true)] - [Serializable] - //[DebuggerDisplay("{DebuggerDisplay,nq}")] - //[DebuggerTypeProxy(typeof(KeyboardHookStruct))] - [StructLayout(LayoutKind.Sequential)] - public struct KeyboardHookStruct - { - public int VkCode; // 键码,该代码必须有一个价值的范围1至254 - public int ScanCode; // 指定的硬件扫描码的关键 - public int Flags; // 键标志 - public int Time; // 指定的时间戳记的这个讯息 - public int DwExtraInfo; // 指定额外信息相关的信息 - - public static KeyboardHookStruct Create(IntPtr lParam) - { - return (KeyboardHookStruct)Marshal.PtrToStructure(lParam, typeof(KeyboardHookStruct)); - } - public void ToPtr(IntPtr lParam) - { - Marshal.StructureToPtr(this, lParam, true); - } - } - /// - /// 注册表增加低级钩子超时处理,防止系统不允许, - /// 否则:偶发性出现 键盘钩子不能用了,而且退出时产生 1404 错误 - /// https://www.cnblogs.com/songr/p/5131655.html - /// - public static void CheckLowLevelHooksTimeout(int setLowLevel = 25000) - { - const string llh = "LowLevelHooksTimeout"; - using var registryKey = Registry.CurrentUser.OpenSubKey("Control Panel\\Desktop", true); - if ((int)registryKey.GetValue(llh, 0) < setLowLevel) - registryKey.SetValue(llh, setLowLevel, RegistryValueKind.DWord); - } - #endregion -} - -public partial class WindowsAPI -{ - [DllImport("user32.dll")] - public static extern bool GetWindowRect(IntPtr hwnd, ref IntRect lpRect); - - [ComVisible(true)] - [Serializable] - [StructLayout(LayoutKind.Sequential)] - [DebuggerDisplay("{DebuggerDisplay,nq}")] - [DebuggerTypeProxy(typeof(IntRect))] - public struct IntRect - { - [DebuggerBrowsable(DebuggerBrowsableState.Never)] - private string DebuggerDisplay => $"(Left:{_Left},Top:{_Top},Right:{_Right},Bottom:{_Bottom})"; - - int _Left; - int _Top; - int _Right; - int _Bottom; - public int Left => _Left; - public int Top => _Top; - public int Right => _Right; - public int Bottom => _Bottom; - public int Width => checked(Right - Left); - public int Height => checked(Bottom - Top); - - public IntRect(int left, int top, int right, int bottom) - { - _Left = left; - _Top = top; - _Right = right; - _Bottom = bottom; - } - - static readonly IntRect _Zero = new(0, 0, 0, 0); - public static IntRect Zero => _Zero; - - public override string ToString() => $"({_Left},{_Top},{_Right},{_Bottom})"; - - #region 重载运算符_比较 - public bool Equals(IntRect other) - { - return - _Left == other._Left && - _Top == other._Top && - _Right == other._Right && - _Bottom == other._Bottom; - } - public static bool operator !=(IntRect a, IntRect b) - { - return !(a == b); - } - public static bool operator ==(IntRect a, IntRect b) - { - return a.Equals(b); - } - public override bool Equals(object obj) - { - return obj is IntRect d && Equals(d); - } - public override int GetHashCode() - { - return ((_Left, _Top).GetHashCode(), _Right).GetHashCode() ^ _Bottom.GetHashCode(); - } - - public IntRect Clone() - { - return (IntRect)MemberwiseClone(); - } - #endregion - } - - [ComVisible(true)] - [Serializable] - [DebuggerDisplay("{DebuggerDisplay,nq}")] - [DebuggerTypeProxy(typeof(IntSize))] - [StructLayout(LayoutKind.Sequential)] - public struct IntSize - { - [DebuggerBrowsable(DebuggerBrowsableState.Never)] - private string DebuggerDisplay => $"(Hight:{Hight},Width:{Width})"; - public int Hight; - public int Width; - - public IntSize(int cx, int cy) - { - Hight = cx; - Width = cy; - } - public override string ToString() => $"({Hight},{Width})"; - } - - [ComVisible(true)] - [Serializable] - [DebuggerDisplay("{DebuggerDisplay,nq}")] - [DebuggerTypeProxy(typeof(Point3D))] - [StructLayout(LayoutKind.Sequential)] - public struct Point3D : IEquatable - { - [DebuggerBrowsable(DebuggerBrowsableState.Never)] - private string DebuggerDisplay => $"(X:{X},Y:{Y},Z:{Z})"; - - /* 由于此类是用来优化,从而实现字段修改,因此直接暴露字段减少栈帧 */ - public double X; - public double Y; - public double Z; - - public Point3D(double x, double y, double z) - { - X = x; - Y = y; - Z = z; - } - public static implicit operator Point3D(Point3d pt) - { - return new Point3D(pt.X, pt.Y, pt.Z); - } - public static implicit operator Point3d(Point3D pt) - { - return new Point3d(pt.X, pt.Y, pt.Z); - } - public override string ToString() => $"({X},{Y},{Z})"; - - public static Point3D Create(IntPtr lParam) - { - return (Point3D)Marshal.PtrToStructure(lParam, typeof(Point3D)); - } - - public void ToPtr(IntPtr lParam) - { - Marshal.StructureToPtr(this, lParam, true); - } - - - #region 重载运算符_比较 - public bool Equals(Point3D other) - { - return - X == other.X && - Y == other.Y && - Z == other.Z; - } - public static bool operator !=(Point3D a, Point3D b) - { - return !(a == b); - } - public static bool operator ==(Point3D a, Point3D b) - { - return a.Equals(b); - } - public override bool Equals(object obj) - { - return obj is Point3D d && Equals(d); - } - public override int GetHashCode() - { - return (X, Y).GetHashCode() ^ Z.GetHashCode(); - } - #endregion - } -} \ No newline at end of file diff --git a/src/IFoxCAD.Cad/IFoxCAD.Aacad08/IFoxCAD.Acad08.csproj b/src/IFoxCAD.Cad/IFoxCAD.Aacad08/IFoxCAD.Acad08.csproj deleted file mode 100644 index 44ce792..0000000 --- a/src/IFoxCAD.Cad/IFoxCAD.Aacad08/IFoxCAD.Acad08.csproj +++ /dev/null @@ -1,62 +0,0 @@ - - - - NET35 - true - enable - true - true - 0.4 - InspireFunction - xsfhlzh;vicwjb;liuqihong - 基于.NET的Cad二次开发类库 - InspireFunction - https://gitee.com/inspirefunction/ifoxcad - https://gitee.com/inspirefunction/ifoxcad.git - git - IFoxCAD;CAD;AutoCad;C#;NET - 增加很多东西. - true - true - preview - true - LICENSE - true - True - - - - - DEBUG - - - $(Configuration);acad;ac2008;ac2009 - - - - - - - - - - True - - - - - - - - - - - - - - - - - diff --git a/src/IFoxCAD.Cad/IFoxCAD.Aacad09plus/GlobalUsings.cs b/src/IFoxCAD.Cad/IFoxCAD.Aacad09plus/GlobalUsings.cs deleted file mode 100644 index 4227a00..0000000 --- a/src/IFoxCAD.Cad/IFoxCAD.Aacad09plus/GlobalUsings.cs +++ /dev/null @@ -1,49 +0,0 @@ -/// 系统引用 -global using System; -global using System.Collections; -global using System.Collections.Generic; -global using System.IO; -global using System.Linq; -global using System.Text; -global using System.Reflection; -global using System.Text.RegularExpressions; -global using Microsoft.Win32; -global using System.ComponentModel; -global using System.Runtime.InteropServices; -global using System.Collections.Specialized; - -global using Exception = System.Exception; - -global using Registry = Microsoft.Win32.Registry; -global using RegistryKey = Microsoft.Win32.RegistryKey; - -/// cad 引用 -global using Autodesk.AutoCAD.ApplicationServices; -global using Autodesk.AutoCAD.EditorInput; -global using Autodesk.AutoCAD.Colors; -global using Autodesk.AutoCAD.DatabaseServices; -global using Autodesk.AutoCAD.Geometry; -global using Autodesk.AutoCAD.Runtime; -global using Acap = Autodesk.AutoCAD.ApplicationServices.Application; -global using Acgi = Autodesk.AutoCAD.GraphicsInterface; - -global using Autodesk.AutoCAD.DatabaseServices.Filters; -global using Autodesk.AutoCAD; - -// jig命名空间会引起Viewport/Polyline等等重义,最好逐个引入 using Autodesk.AutoCAD.GraphicsInterface -global using Autodesk.AutoCAD.GraphicsInterface; -global using WorldDraw = Autodesk.AutoCAD.GraphicsInterface.WorldDraw; -global using Manager = Autodesk.AutoCAD.GraphicsSystem.Manager; -global using Group = Autodesk.AutoCAD.DatabaseServices.Group; -global using Viewport = Autodesk.AutoCAD.DatabaseServices.Viewport; -global using Polyline = Autodesk.AutoCAD.DatabaseServices.Polyline; -global using Cad_DwgFiler = Autodesk.AutoCAD.DatabaseServices.DwgFiler; -global using Cad_DxfFiler = Autodesk.AutoCAD.DatabaseServices.DxfFiler; -global using Cad_ErrorStatus = Autodesk.AutoCAD.Runtime.ErrorStatus; - -/// ifoxcad.basal 引用 -global using IFoxCAD.Basal; - -#if !NewtonsoftJson -global using System.Web.Script.Serialization; -#endif \ No newline at end of file diff --git a/src/IFoxCAD.Cad/IFoxCAD.Aacad09plus/IFoxCAD.Acad09plus.csproj b/src/IFoxCAD.Cad/IFoxCAD.Aacad09plus/IFoxCAD.Acad09plus.csproj deleted file mode 100644 index 1a76995..0000000 --- a/src/IFoxCAD.Cad/IFoxCAD.Aacad09plus/IFoxCAD.Acad09plus.csproj +++ /dev/null @@ -1,74 +0,0 @@ - - - - NET35;NET40;NET45 - true - enable - true - true - 0.4 - InspireFunction - xsfhlzh;vicwjb;liuqihong - 基于.NET的Cad二次开发类库 - InspireFunction - https://gitee.com/inspirefunction/ifoxcad - https://gitee.com/inspirefunction/ifoxcad.git - git - IFoxCAD;CAD;AutoCad;C#;NET - 增加很多东西. - true - true - preview - true - LICENSE - true - True - x64 - - - - DEBUG - - - $(Configuration);acad;ac2009 - - - $(Configuration);acad;ac2013 - - - $(Configuration);acad;ac2015 - - - - - - - - - - - - - - - - True - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/src/IFoxCAD.Cad/IFoxCAD.Gcad/IFoxCAD.Gcad.csproj b/src/IFoxCAD.Cad/IFoxCAD.Gcad/IFoxCAD.Gcad.csproj deleted file mode 100644 index c4c523b..0000000 --- a/src/IFoxCAD.Cad/IFoxCAD.Gcad/IFoxCAD.Gcad.csproj +++ /dev/null @@ -1,59 +0,0 @@ - - - - NET48 - true - enable - true - true - 0.3.7 - InspireFunction - xsfhlzh;vicwjb;liuqihong - 基于.NET的Cad二次开发类库 - InspireFunction - https://gitee.com/inspirefunction/ifoxcad - https://gitee.com/inspirefunction/ifoxcad.git - git - IFoxCAD;CAD;AutoCad;C#;NET - 增加很多东西. - true - true - preview - true - LICENSE - true - True - - - - - DEBUG - - - $(Configuration);gcad - - - - - runtime - - - - - - True - - - - - - - - - - - - - diff --git a/src/IFoxCAD.Cad/IFoxCAD.Zcad/IFoxCAD.Zcad.csproj b/src/IFoxCAD.Cad/IFoxCAD.Zcad/IFoxCAD.Zcad.csproj deleted file mode 100644 index 79977c4..0000000 --- a/src/IFoxCAD.Cad/IFoxCAD.Zcad/IFoxCAD.Zcad.csproj +++ /dev/null @@ -1,67 +0,0 @@ - - - - NET48 - true - enable - true - true - 0.3.7 - InspireFunction - xsfhlzh;vicwjb;liuqihong - 基于.NET的Cad二次开发类库 - InspireFunction - https://gitee.com/inspirefunction/ifoxcad - https://gitee.com/inspirefunction/ifoxcad.git - git - IFoxCAD;CAD;AutoCad;C#;NET - 增加很多东西. - true - true - preview - true - LICENSE - true - True - - - - - DEBUG - - - $(Configuration);zcad - - - $(Configuration);zcad - - - - - runtime - - - - - runtime - - - - - - True - - - - - - - - - - - - - diff --git a/tests/TestAcad08/TestAcad08.csproj b/tests/TestAcad08/TestAcad08.csproj index 3e2cae2..97fb1a5 100644 --- a/tests/TestAcad08/TestAcad08.csproj +++ b/tests/TestAcad08/TestAcad08.csproj @@ -14,11 +14,6 @@ $(Configuration);acad;ac2008;ac2009;NewtonsoftJson - - - - - diff --git a/tests/TestAcad09plus/TestAcad09plus.csproj b/tests/TestAcad09plus/TestAcad09plus.csproj index 1e25e45..9b3d4ea 100644 --- a/tests/TestAcad09plus/TestAcad09plus.csproj +++ b/tests/TestAcad09plus/TestAcad09plus.csproj @@ -16,7 +16,6 @@ - -- Gitee From 5107c2b37bd1d80e6b28d335ae1e32b549778ea3 Mon Sep 17 00:00:00 2001 From: vicwjb Date: Sun, 8 Jan 2023 00:02:12 +0800 Subject: [PATCH 631/675] =?UTF-8?q?=E8=B0=83=E6=95=B4=E8=A7=A3=E5=86=B3?= =?UTF-8?q?=E6=96=B9=E6=A1=88=E7=BB=93=E6=9E=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- IFoxCAD.sln | 15 +++++++++------ .../BindingErrorTraceListener.cs | 0 src/{IFoxCAD.WPF => WPF}/Converter.cs | 0 .../DependencyObjectExtensions.cs | 0 src/{IFoxCAD.WPF => WPF}/EnumSelection.cs | 0 src/{IFoxCAD.WPF => WPF}/EventBindingExtension.cs | 0 src/{IFoxCAD.WPF => WPF}/GlobalUsings.cs | 0 .../IFoxCAD.WPF.csproj => WPF/IFox.WPF.csproj} | 0 src/{IFoxCAD.WPF => WPF}/RelayCommand.cs | 0 src/{IFoxCAD.WPF => WPF}/ViewModelBase.cs | 0 tests/TestAcad09plus/TestAcad09plus.csproj | 5 ----- 11 files changed, 9 insertions(+), 11 deletions(-) rename src/{IFoxCAD.WPF => WPF}/BindingErrorTraceListener.cs (100%) rename src/{IFoxCAD.WPF => WPF}/Converter.cs (100%) rename src/{IFoxCAD.WPF => WPF}/DependencyObjectExtensions.cs (100%) rename src/{IFoxCAD.WPF => WPF}/EnumSelection.cs (100%) rename src/{IFoxCAD.WPF => WPF}/EventBindingExtension.cs (100%) rename src/{IFoxCAD.WPF => WPF}/GlobalUsings.cs (100%) rename src/{IFoxCAD.WPF/IFoxCAD.WPF.csproj => WPF/IFox.WPF.csproj} (100%) rename src/{IFoxCAD.WPF => WPF}/RelayCommand.cs (100%) rename src/{IFoxCAD.WPF => WPF}/ViewModelBase.cs (100%) diff --git a/IFoxCAD.sln b/IFoxCAD.sln index ac6aac5..00d7e00 100644 --- a/IFoxCAD.sln +++ b/IFoxCAD.sln @@ -12,8 +12,6 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution README.md = README.md EndProjectSection EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "IFoxCAD.WPF", "src\IFoxCAD.WPF\IFoxCAD.WPF.csproj", "{D820D629-1AB3-4BE3-A772-CB753D98CCB8}" -EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "TestConsole", "tests\TestConsole\TestConsole.csproj", "{E2873F0B-CAD2-45E8-8FF0-C05C0C6AD955}" EndProject Project("{D954291E-2A0B-460D-934E-DC6B0785DB48}") = "TestShared", "tests\TestShared\TestShared.shproj", "{CED63D2D-0AF6-4831-806D-5E8E9B0D0A07}" @@ -50,16 +48,16 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "IFox.CAD.GCAD", "src\CAD\IF EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "IFox.CAD.ZCAD", "src\CAD\IFox.CAD.ZCAD\IFox.CAD.ZCAD.csproj", "{3D7D095D-EF0A-40B3-9776-5F08C61FD614}" EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "WPF", "WPF", "{1EE1F817-39D3-43E5-B087-E7AD4D0BEC93}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "IFox.WPF", "src\WPF\IFox.WPF.csproj", "{6ED0A677-6621-4493-AF58-71C2BF31DFCE}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU Release|Any CPU = Release|Any CPU EndGlobalSection GlobalSection(ProjectConfigurationPlatforms) = postSolution - {D820D629-1AB3-4BE3-A772-CB753D98CCB8}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {D820D629-1AB3-4BE3-A772-CB753D98CCB8}.Debug|Any CPU.Build.0 = Debug|Any CPU - {D820D629-1AB3-4BE3-A772-CB753D98CCB8}.Release|Any CPU.ActiveCfg = Release|Any CPU - {D820D629-1AB3-4BE3-A772-CB753D98CCB8}.Release|Any CPU.Build.0 = Release|Any CPU {E2873F0B-CAD2-45E8-8FF0-C05C0C6AD955}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {E2873F0B-CAD2-45E8-8FF0-C05C0C6AD955}.Debug|Any CPU.Build.0 = Debug|Any CPU {E2873F0B-CAD2-45E8-8FF0-C05C0C6AD955}.Release|Any CPU.ActiveCfg = Release|Any CPU @@ -96,6 +94,10 @@ Global {3D7D095D-EF0A-40B3-9776-5F08C61FD614}.Debug|Any CPU.Build.0 = Debug|Any CPU {3D7D095D-EF0A-40B3-9776-5F08C61FD614}.Release|Any CPU.ActiveCfg = Release|Any CPU {3D7D095D-EF0A-40B3-9776-5F08C61FD614}.Release|Any CPU.Build.0 = Release|Any CPU + {6ED0A677-6621-4493-AF58-71C2BF31DFCE}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {6ED0A677-6621-4493-AF58-71C2BF31DFCE}.Debug|Any CPU.Build.0 = Debug|Any CPU + {6ED0A677-6621-4493-AF58-71C2BF31DFCE}.Release|Any CPU.ActiveCfg = Release|Any CPU + {6ED0A677-6621-4493-AF58-71C2BF31DFCE}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -113,6 +115,7 @@ Global {EF20E632-70A9-40EB-8C9A-539FD85FAFAD} = {B2AB7DC7-DBF4-4197-808B-0D2A6613AF5E} {F388F4B9-F636-414A-9BC9-2B008517B441} = {B2AB7DC7-DBF4-4197-808B-0D2A6613AF5E} {3D7D095D-EF0A-40B3-9776-5F08C61FD614} = {B2AB7DC7-DBF4-4197-808B-0D2A6613AF5E} + {6ED0A677-6621-4493-AF58-71C2BF31DFCE} = {1EE1F817-39D3-43E5-B087-E7AD4D0BEC93} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {31D6C754-CF6B-4AB0-9861-6923DD312350} diff --git a/src/IFoxCAD.WPF/BindingErrorTraceListener.cs b/src/WPF/BindingErrorTraceListener.cs similarity index 100% rename from src/IFoxCAD.WPF/BindingErrorTraceListener.cs rename to src/WPF/BindingErrorTraceListener.cs diff --git a/src/IFoxCAD.WPF/Converter.cs b/src/WPF/Converter.cs similarity index 100% rename from src/IFoxCAD.WPF/Converter.cs rename to src/WPF/Converter.cs diff --git a/src/IFoxCAD.WPF/DependencyObjectExtensions.cs b/src/WPF/DependencyObjectExtensions.cs similarity index 100% rename from src/IFoxCAD.WPF/DependencyObjectExtensions.cs rename to src/WPF/DependencyObjectExtensions.cs diff --git a/src/IFoxCAD.WPF/EnumSelection.cs b/src/WPF/EnumSelection.cs similarity index 100% rename from src/IFoxCAD.WPF/EnumSelection.cs rename to src/WPF/EnumSelection.cs diff --git a/src/IFoxCAD.WPF/EventBindingExtension.cs b/src/WPF/EventBindingExtension.cs similarity index 100% rename from src/IFoxCAD.WPF/EventBindingExtension.cs rename to src/WPF/EventBindingExtension.cs diff --git a/src/IFoxCAD.WPF/GlobalUsings.cs b/src/WPF/GlobalUsings.cs similarity index 100% rename from src/IFoxCAD.WPF/GlobalUsings.cs rename to src/WPF/GlobalUsings.cs diff --git a/src/IFoxCAD.WPF/IFoxCAD.WPF.csproj b/src/WPF/IFox.WPF.csproj similarity index 100% rename from src/IFoxCAD.WPF/IFoxCAD.WPF.csproj rename to src/WPF/IFox.WPF.csproj diff --git a/src/IFoxCAD.WPF/RelayCommand.cs b/src/WPF/RelayCommand.cs similarity index 100% rename from src/IFoxCAD.WPF/RelayCommand.cs rename to src/WPF/RelayCommand.cs diff --git a/src/IFoxCAD.WPF/ViewModelBase.cs b/src/WPF/ViewModelBase.cs similarity index 100% rename from src/IFoxCAD.WPF/ViewModelBase.cs rename to src/WPF/ViewModelBase.cs diff --git a/tests/TestAcad09plus/TestAcad09plus.csproj b/tests/TestAcad09plus/TestAcad09plus.csproj index 9b3d4ea..8cf77f6 100644 --- a/tests/TestAcad09plus/TestAcad09plus.csproj +++ b/tests/TestAcad09plus/TestAcad09plus.csproj @@ -14,11 +14,6 @@ $(Configuration);acad;ac2015 - - - - - TestView.xaml -- Gitee From e336a0f2c3b181d5f3c5f060235763354fae6a67 Mon Sep 17 00:00:00 2001 From: vicwjb Date: Sun, 8 Jan 2023 00:05:56 +0800 Subject: [PATCH 632/675] =?UTF-8?q?=E8=B0=83=E6=95=B4=E8=A7=A3=E5=86=B3?= =?UTF-8?q?=E6=96=B9=E6=A1=88?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- IFoxCAD.sln | 17 ++++++++++++++--- 1 file changed, 14 insertions(+), 3 deletions(-) diff --git a/IFoxCAD.sln b/IFoxCAD.sln index 00d7e00..898fc39 100644 --- a/IFoxCAD.sln +++ b/IFoxCAD.sln @@ -3,13 +3,11 @@ Microsoft Visual Studio Solution File, Format Version 12.00 # Visual Studio Version 17 VisualStudioVersion = 17.1.32113.165 MinimumVisualStudioVersion = 10.0.40219.1 -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{786E7347-B116-4F26-9AEF-33EB0AB88D58}" +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Config", "Config", "{786E7347-B116-4F26-9AEF-33EB0AB88D58}" ProjectSection(SolutionItems) = preProject .editorconfig = .editorconfig .gitattributes = .gitattributes .gitignore = .gitignore - LICENSE = LICENSE - README.md = README.md EndProjectSection EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "TestConsole", "tests\TestConsole\TestConsole.csproj", "{E2873F0B-CAD2-45E8-8FF0-C05C0C6AD955}" @@ -52,6 +50,19 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "WPF", "WPF", "{1EE1F817-39D EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "IFox.WPF", "src\WPF\IFox.WPF.csproj", "{6ED0A677-6621-4493-AF58-71C2BF31DFCE}" EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Docs", "Docs", "{DA8EA8E7-A98E-4565-A3BF-97855988DB8E}" + ProjectSection(SolutionItems) = preProject + docs\0x01代码规范.md = docs\0x01代码规范.md + docs\DBTrans.md = docs\DBTrans.md + LICENSE = LICENSE + README.md = README.md + docs\SelectionFilter.md = docs\SelectionFilter.md + docs\SymbolTable.md = docs\SymbolTable.md + docs\WPF.md = docs\WPF.md + docs\关于IFoxCAD的架构说明.md = docs\关于IFoxCAD的架构说明.md + docs\关于扩展函数的说明.md = docs\关于扩展函数的说明.md + EndProjectSection +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU -- Gitee From 7b99d1925243d66eb4052ebb5cf9a161fe14a8a3 Mon Sep 17 00:00:00 2001 From: vicwjb Date: Sun, 8 Jan 2023 23:43:50 +0800 Subject: [PATCH 633/675] =?UTF-8?q?=E7=94=9F=E6=88=90xml=E6=96=87=E6=A1=A3?= =?UTF-8?q?=E7=94=A8=E4=BA=8Echm?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- IFoxCAD.sln | 8 - src/Basal/Directory.Build.props | 2 +- src/CAD/Directory.Build.props | 1 + src/WPF/IFox.WPF.csproj | 3 +- tests/TestAcad09plus/TestAcad09plus.csproj | 5 + tests/TestAcad09plus/wpf/TestView.xaml | 2 +- ...63\351\224\256\350\217\234\345\215\225.cs" | 184 +++ ...53\345\205\205\344\272\213\344\273\266.cs" | 1029 +++++++++++++++++ .../HatchHelper.cs" | 124 ++ ...350\275\254cad\345\235\220\346\240\207.cs" | 133 +++ ...06\347\232\204\344\276\213\345\255\220.cs" | 94 ++ ...3\345\205\205\350\276\271\347\225\214.txt" | 104 ++ ...71\347\202\271\344\276\213\345\255\220.cs" | 100 ++ tests/TestConsole/TestConsole.csproj | 4 + 14 files changed, 1782 insertions(+), 11 deletions(-) create mode 100644 "tests/TestAcad09plus/\346\213\211\344\274\270\345\241\253\345\205\205/01.\346\213\211\344\274\270\345\241\253\345\205\205\345\217\263\351\224\256\350\217\234\345\215\225.cs" create mode 100644 "tests/TestAcad09plus/\346\213\211\344\274\270\345\241\253\345\205\205/02.\346\213\211\344\274\270\345\241\253\345\205\205\344\272\213\344\273\266.cs" create mode 100644 "tests/TestAcad09plus/\346\213\211\344\274\270\345\241\253\345\205\205/HatchHelper.cs" create mode 100644 "tests/TestAcad09plus/\346\213\211\344\274\270\345\241\253\345\205\205/\345\261\217\345\271\225\345\235\220\346\240\207\350\275\254cad\345\235\220\346\240\207.cs" create mode 100644 "tests/TestAcad09plus/\346\213\211\344\274\270\345\241\253\345\205\205/\350\277\207\347\250\213\346\226\207\344\273\266/\344\270\200\344\270\252\346\227\240\346\263\225\347\247\273\345\212\250\347\272\242\350\211\262\345\234\206\347\232\204\344\276\213\345\255\220.cs" create mode 100644 "tests/TestAcad09plus/\346\213\211\344\274\270\345\241\253\345\205\205/\350\277\207\347\250\213\346\226\207\344\273\266/\350\207\252\345\256\232\344\271\211\345\233\276\345\205\203\345\241\253\345\205\205\350\276\271\347\225\214.txt" create mode 100644 "tests/TestAcad09plus/\346\213\211\344\274\270\345\241\253\345\205\205/\350\277\207\347\250\213\346\226\207\344\273\266/\350\216\267\345\217\226\345\244\271\347\202\271\344\276\213\345\255\220.cs" diff --git a/IFoxCAD.sln b/IFoxCAD.sln index 898fc39..8eb4c1d 100644 --- a/IFoxCAD.sln +++ b/IFoxCAD.sln @@ -14,8 +14,6 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "TestConsole", "tests\TestCo EndProject Project("{D954291E-2A0B-460D-934E-DC6B0785DB48}") = "TestShared", "tests\TestShared\TestShared.shproj", "{CED63D2D-0AF6-4831-806D-5E8E9B0D0A07}" EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "TestAcad08", "tests\TestAcad08\TestAcad08.csproj", "{8C573180-523D-427D-8030-CAA7D6B5EB6B}" -EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "TestAcad09plus", "tests\TestAcad09plus\TestAcad09plus.csproj", "{5F478F68-19BC-4A3A-AF39-8728F8D396DD}" EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Test", "Test", "{C0BEFC15-6DF7-4636-BC76-4A0B88231470}" @@ -73,10 +71,6 @@ Global {E2873F0B-CAD2-45E8-8FF0-C05C0C6AD955}.Debug|Any CPU.Build.0 = Debug|Any CPU {E2873F0B-CAD2-45E8-8FF0-C05C0C6AD955}.Release|Any CPU.ActiveCfg = Release|Any CPU {E2873F0B-CAD2-45E8-8FF0-C05C0C6AD955}.Release|Any CPU.Build.0 = Release|Any CPU - {8C573180-523D-427D-8030-CAA7D6B5EB6B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {8C573180-523D-427D-8030-CAA7D6B5EB6B}.Debug|Any CPU.Build.0 = Debug|Any CPU - {8C573180-523D-427D-8030-CAA7D6B5EB6B}.Release|Any CPU.ActiveCfg = Release|Any CPU - {8C573180-523D-427D-8030-CAA7D6B5EB6B}.Release|Any CPU.Build.0 = Release|Any CPU {5F478F68-19BC-4A3A-AF39-8728F8D396DD}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {5F478F68-19BC-4A3A-AF39-8728F8D396DD}.Debug|Any CPU.Build.0 = Debug|Any CPU {5F478F68-19BC-4A3A-AF39-8728F8D396DD}.Release|Any CPU.ActiveCfg = Release|Any CPU @@ -116,7 +110,6 @@ Global GlobalSection(NestedProjects) = preSolution {E2873F0B-CAD2-45E8-8FF0-C05C0C6AD955} = {C0BEFC15-6DF7-4636-BC76-4A0B88231470} {CED63D2D-0AF6-4831-806D-5E8E9B0D0A07} = {C0BEFC15-6DF7-4636-BC76-4A0B88231470} - {8C573180-523D-427D-8030-CAA7D6B5EB6B} = {C0BEFC15-6DF7-4636-BC76-4A0B88231470} {5F478F68-19BC-4A3A-AF39-8728F8D396DD} = {C0BEFC15-6DF7-4636-BC76-4A0B88231470} {FF4E1CDE-EEB3-4BE8-8C1D-6B5B17A299C7} = {1A5C27C1-FEF3-40A0-A1EE-3BC7BF2BD67A} {C823514A-2BC2-45C2-ACED-18924A7B80BF} = {1A5C27C1-FEF3-40A0-A1EE-3BC7BF2BD67A} @@ -136,7 +129,6 @@ Global src\CAD\IFox.CAD.Shared\IFox.CAD.Shared.projitems*{3d7d095d-ef0a-40b3-9776-5f08c61fd614}*SharedItemsImports = 5 tests\TestShared\TestShared.projitems*{5f478f68-19bc-4a3a-af39-8728f8d396dd}*SharedItemsImports = 5 src\CAD\IFox.CAD.Shared\IFox.CAD.Shared.projitems*{88f3cf78-23f5-494b-9d8f-2c6b4467e0b4}*SharedItemsImports = 5 - tests\TestShared\TestShared.projitems*{8c573180-523d-427d-8030-caa7d6b5eb6b}*SharedItemsImports = 5 src\Basal\IFox.Basal.Shared\IFox.Basal.Shared.projitems*{c823514a-2bc2-45c2-aced-18924a7b80bf}*SharedItemsImports = 13 tests\TestShared\TestShared.projitems*{ced63d2d-0af6-4831-806d-5e8e9b0d0a07}*SharedItemsImports = 13 src\Basal\IFox.Basal.Shared\IFox.Basal.Shared.projitems*{d0c6824c-53a3-4c16-ae7b-3227f18c5039}*SharedItemsImports = 5 diff --git a/src/Basal/Directory.Build.props b/src/Basal/Directory.Build.props index 87e3b31..10a4b4e 100644 --- a/src/Basal/Directory.Build.props +++ b/src/Basal/Directory.Build.props @@ -20,7 +20,7 @@ git IFoxCAD;C#;NET;Common;Basal $(MSBuildThisFileDirectory)bin\$(Configuration)\ - + True \ No newline at end of file diff --git a/src/CAD/Directory.Build.props b/src/CAD/Directory.Build.props index 104a9e3..10d1c84 100644 --- a/src/CAD/Directory.Build.props +++ b/src/CAD/Directory.Build.props @@ -13,6 +13,7 @@ true true True + True diff --git a/src/WPF/IFox.WPF.csproj b/src/WPF/IFox.WPF.csproj index 59cd3aa..22fecf4 100644 --- a/src/WPF/IFox.WPF.csproj +++ b/src/WPF/IFox.WPF.csproj @@ -20,6 +20,7 @@ IFoxCAD;C#;NET;WPF;MVVM git 开启可空类型. + True @@ -27,7 +28,7 @@ - + diff --git a/tests/TestAcad09plus/TestAcad09plus.csproj b/tests/TestAcad09plus/TestAcad09plus.csproj index 8cf77f6..35a0636 100644 --- a/tests/TestAcad09plus/TestAcad09plus.csproj +++ b/tests/TestAcad09plus/TestAcad09plus.csproj @@ -14,6 +14,11 @@ $(Configuration);acad;ac2015 + + + + + TestView.xaml diff --git a/tests/TestAcad09plus/wpf/TestView.xaml b/tests/TestAcad09plus/wpf/TestView.xaml index bd75779..e51e2ac 100644 --- a/tests/TestAcad09plus/wpf/TestView.xaml +++ b/tests/TestAcad09plus/wpf/TestView.xaml @@ -4,7 +4,7 @@ xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:local="clr-namespace:Test.wpf" - xmlns:eb="clr-namespace:IFoxCAD.WPF;assembly=IFoxCAD.WPF" + xmlns:eb="clr-namespace:IFoxCAD.WPF;assembly=IFox.WPF" mc:Ignorable="d" Title="TestView" Height="450" Width="800" Loaded="{eb:EventBinding Command=LoadCommand}" diff --git "a/tests/TestAcad09plus/\346\213\211\344\274\270\345\241\253\345\205\205/01.\346\213\211\344\274\270\345\241\253\345\205\205\345\217\263\351\224\256\350\217\234\345\215\225.cs" "b/tests/TestAcad09plus/\346\213\211\344\274\270\345\241\253\345\205\205/01.\346\213\211\344\274\270\345\241\253\345\205\205\345\217\263\351\224\256\350\217\234\345\215\225.cs" new file mode 100644 index 0000000..6afeef7 --- /dev/null +++ "b/tests/TestAcad09plus/\346\213\211\344\274\270\345\241\253\345\205\205/01.\346\213\211\344\274\270\345\241\253\345\205\205\345\217\263\351\224\256\350\217\234\345\215\225.cs" @@ -0,0 +1,184 @@ +using Autodesk.AutoCAD.Windows; +using System.Diagnostics; +using static IFoxCAD.Cad.PostCmd; +using MenuItem = Autodesk.AutoCAD.Windows.MenuItem; + +namespace JoinBoxAcad; +public class HatchPick +{ + [IFoxInitialize] + [CommandMethod(nameof(HatchPickInit))] + public void HatchPickInit() + { + Env.Printl($"※拉伸填充控制※\n{nameof(HatchPickSwitch)} - 切换开关\n"); + + if (Debugger.IsAttached) + Env.SetVar("hpscale", 22); + // 设定高版本双击填充启动修改面板 + // JoinBoxAcad.Menu.Cui.CuiInit(); + LoadHelper(true); + } + + // 只能命令卸载哦,因为关闭cad是不需要卸载的 + [CommandMethod(nameof(UnLoadHatchPick))] + public void UnLoadHatchPick() + { + LoadHelper(false); + } + + [CommandMethod(nameof(HatchPickSwitch))] + public void HatchPickSwitch() + { + if (HatchPickEvent.State.IsStop) + { + Env.Printl("已经 卸载 拉伸填充控制+ 用: " + nameof(HatchPickInit) + " 加载"); + return; + } + + if (HatchPickEvent.State.IsRun) + HatchPickEvent.State.Break(); + else + HatchPickEvent.State.Start(); + Env.Printl("已经 " + (HatchPickEvent.State.IsRun ? "开启" : "禁用") + " 拉伸填充控制+"); + } + + + internal static Dictionary MapDocHatchPickEvent = new(); + void LoadHelper(bool isLoad) + { + var dm = Acap.DocumentManager; + if (dm is null || dm.Count == 0) + return; + if (isLoad) + { + dm.DocumentCreated += Dm_DocumentCreated; + Dm_DocumentCreated(); // 自执行一次 + AddRightClickMenu(); + HatchPickEvent.AddInit(); + } + else + { + HatchPickEvent.RemoveInit(); + dm.DocumentCreated -= Dm_DocumentCreated; + UnDocumentCreated(); + HatchPick.RemoveRightClickMenu(); + } + } + + /// + /// 文档创建反应器 + /// + void Dm_DocumentCreated(object? sender = null, DocumentCollectionEventArgs? e = null) + { + var dm = Acap.DocumentManager; + if (dm is null || dm.Count == 0) + return; + var doc = dm.MdiActiveDocument; + if (doc is null) + return; + if (!MapDocHatchPickEvent.ContainsKey(doc)) + MapDocHatchPickEvent.Add(doc, new HatchPickEvent(doc)); + } + + /// + /// 卸载文档创建反应器 + /// + static void UnDocumentCreated() + { + var dm = Acap.DocumentManager; + if (dm is null || dm.Count == 0) + return; + var doc = dm.MdiActiveDocument; + if (doc is null) + return; + if (MapDocHatchPickEvent.ContainsKey(doc)) + { + MapDocHatchPickEvent[doc].Dispose(); + MapDocHatchPickEvent.Remove(doc); + } + } + + + + private const string V0 = "拉伸填充-开"; + private const string V1 = "拉伸填充-关";// (面板的独立填充必须关,否则致命错误) + private const string V2 = "独立填充";//(快捷,不需要关...目前还是会崩溃) + static readonly HashSet _menuItems = new() { V0, V1, V2 }; + static readonly ContextMenuExtension _contextMenu = new() { Title = "惊惊盒子" }; + /// + /// 添加右键菜单 + /// + void AddRightClickMenu() + { + // 右键菜单 + foreach (var item in _menuItems) + { + MenuItem mi = new(item); // 添加菜单项 + mi.Click += MenuItemClick; // 添加单击事件 + + //mi.MenuItems.Add(new MenuItem("改颜色1")); // 二级菜单 + _contextMenu.MenuItems.Add(mi); // 提交 + } + Acap.AddDefaultContextMenuExtension(_contextMenu);// 添加默认上下文菜单扩展,带标题的 + + //加入到某一种对象的右键菜单中 + //RXClass rxClass = Entity.GetClass(typeof(BlockReference)); + //Acap.AddObjectContextMenuExtension(rxClass, contextMenu); + //// 选择实体右键菜单才有用. 获得实体所属的RXClass类型 + // RXClass rx = RXObject.GetClass(typeof(Entity)); + // Acap.AddObjectContextMenuExtension(rx, contextMenu); // 这里为什么又可以不带标题 + } + + /// + /// 卸载右键菜单 + /// + static void RemoveRightClickMenu() + { + if (_contextMenu is null) + return; + Acap.RemoveDefaultContextMenuExtension(_contextMenu); + } + + /// + /// 右键点击触发 + /// + /// + /// + void MenuItemClick(object sender, EventArgs e) + { + // 获取发出命令的快捷菜单项 + if (sender is not MenuItem mi) + return; + + // 根据快捷菜单项的名字,分别调用对应的命令 + if (!_menuItems.Contains(mi.Text)) + return; + + switch (mi.Text) + { + case V0: + HatchPickEvent.State.Start(); + break; + case V1: + HatchPickEvent.State.Break(); + break; + case V2: + { + HatchPickEvent.State.Break(); + PromptSelectionOptions pso = new() + { + AllowDuplicates = true, // 不允许重复选择 + SingleOnly = true, // 隐含窗口选择(不需要空格确认) + }; + var ssPsr = Env.Editor.GetSelection(pso, HatchPickEvent.FilterForHatch); + if (ssPsr.Status != PromptStatus.OK) + return; + + Env.Editor.SetImpliedSelection(ssPsr.Value.GetObjectIds()); + SendCommand("-hatchedit H ", RunCmdFlag.AcedPostCommand); + HatchPickEvent.State.Start(); + } + break; + } + } +} \ No newline at end of file diff --git "a/tests/TestAcad09plus/\346\213\211\344\274\270\345\241\253\345\205\205/02.\346\213\211\344\274\270\345\241\253\345\205\205\344\272\213\344\273\266.cs" "b/tests/TestAcad09plus/\346\213\211\344\274\270\345\241\253\345\205\205/02.\346\213\211\344\274\270\345\241\253\345\205\205\344\272\213\344\273\266.cs" new file mode 100644 index 0000000..f08eea8 --- /dev/null +++ "b/tests/TestAcad09plus/\346\213\211\344\274\270\345\241\253\345\205\205/02.\346\213\211\344\274\270\345\241\253\345\205\205\344\272\213\344\273\266.cs" @@ -0,0 +1,1029 @@ +using System.Drawing; +using System.Runtime.CompilerServices; +using System.Windows.Forms; +using static IFoxCAD.Cad.PostCmd; + +namespace JoinBoxAcad; + +public class HatchPickEvent : IDisposable +{ + #region 静态成员 + public static ProState State = new(); + // 选择集过滤器 + public static readonly SelectionFilter FilterForHatch = new(new TypedValue[] { new TypedValue((int)DxfCode.Start, "HATCH") }); + // 临时标记(重设选择集会触发一次选择集反应器) + static bool _selectChangedStop = false; + // 临时选择集用 + static List _hatchIds = new(); + // 获取夹点在哪个图元边界上面,是为true + static bool _pickInBo = false; + static bool _vetoProperties = false; + private Tolerance _tol = new(1e-6, 1e-6); + + public static void AddInit() + { + HatchHook.SetHook(); + // 全局事件重复+=是不需要担心的 + Acap.DocumentManager.DocumentLockModeChanged += Dm_VetoCommand; + State.Start(); + } + + public static void RemoveInit() + { + State.Stop(); + Acap.DocumentManager.DocumentLockModeChanged -= Dm_VetoCommand; + HatchHook.RemoveHook(); + } + + /// + /// 反应器->命令否决触发命令前(不可锁文档) + /// + /// + /// + public static void Dm_VetoCommand(object sender, DocumentLockModeChangedEventArgs e) + { + if (!State.IsRun) + return; + if (string.IsNullOrEmpty(e.GlobalCommandName) || e.GlobalCommandName == "#") + return; + switch (e.GlobalCommandName.ToUpper()) + { + case "PROPERTIES": // 特性面板 + { + // 事件顺序问题: + // 开cad之后第一次双击必弹出特性面板 + // 所以这里直接删除填充边界 + HatchPick.MapDocHatchPickEvent[e.Document].SetPropertiesInfoTask(); + if (_vetoProperties) + { + Debugx.Printl("Dm_VetoCommand 否决了"); + e.Veto(); + _vetoProperties = false; + // 发送编辑填充命令 + SendCommand("_hatchedit ", RunCmdFlag.AcedPostCommand); + return; + } + Debugx.Printl("Dm_VetoCommand 没否决"); + } + break; + } + } + + + #endregion + + #region 动态成员 + /// + /// 在位编辑器 记录全图选择集的填充 + /// + readonly HashSet _refeditSsgeting = new(); + /// + /// 在位编辑器 获取当前选择集做差集=>内部填充 + /// + readonly HashSet _refeditSsgeted = new(); + // map<填充id,边界转换器> + readonly Dictionary _mapHatchConv = new(); + readonly Document _doc; + public HatchPickEvent(Document doc) + { + _doc = doc; + LoadHelper(true); + } + + void LoadHelper(bool isLoad) + { + if (isLoad) + { + _doc.ImpliedSelectionChanged += Md_ImpliedSelectionChanged; + _doc.CommandWillStart += Md_CommandWillStart; + _doc.LispWillStart += Md_LispWillStart; + _doc.CommandEnded += Md_CommandEnded; + _doc.Database.ObjectErased += DB_ObjectErased; + _doc.Database.ObjectModified += DB_ObjectModified; + } + else + { + _doc.ImpliedSelectionChanged -= Md_ImpliedSelectionChanged; + _doc.CommandWillStart -= Md_CommandWillStart; + _doc.LispWillStart -= Md_LispWillStart; + _doc.CommandEnded -= Md_CommandEnded; + _doc.Database.ObjectErased -= DB_ObjectErased; + _doc.Database.ObjectModified -= DB_ObjectModified; + } + } + #endregion + + #region 事件 + /// + /// 反应器->command命令完成前 + /// + /// + /// + void Md_CommandWillStart(object sender, CommandEventArgs e) + { + if (!State.IsRun) + return; + + // 此处无法使用文档锁,否则将导致文档锁无法释放,然后ctrl+z失败 + var cmdup = e.GlobalCommandName.ToUpper(); + Debugx.Printl("Md_CommandWillStart::" + cmdup); + + switch (cmdup) + { + case "REFEDIT": + { + // 在位编辑命令,执行前,获取当前空间所有填充 + var prompt = Env.Editor.SelectAll(FilterForHatch); + if (prompt.Status != PromptStatus.OK) + return; + using DBTrans tr = new(); + GetHatchIds(prompt); + if (_hatchIds.Count == 0) + return; + for (int i = 0; i < _hatchIds.Count; i++) + _refeditSsgeting.Add(_hatchIds[i]); + } + break; + } + + // 拉伸夹点命令前触发 + if (cmdup != "GRIP_STRETCH") + { + EraseAllHatchBorders(); + } + else + { + var mp = HatchHook.MouseStartPoint; + var mouseStart = Screen.ScreenToCad(mp); + Debugx.Printl("mouseStart,屏幕点::" + mp); + Debugx.Printl("mouseStart,cad点::" + mouseStart); + + // 获取当前选择的对象,然后提取所有的夹点 + var prompt = Env.Editor.SelectImplied(); + if (prompt.Status != PromptStatus.OK) + return; + using DBTrans tr = new(); + GetHatchIds(prompt); + if (_hatchIds.Count == 0) + return; + + // TODO 屏幕像素点转cad点的误差,要随着视口高度而动态计算....这里的计算可能不太正确 + var tol = (double)Env.GetVar("viewsize") / 10; + Debugx.Printl("tol::" + tol); + + // 0x01 移动了矩形填充中间的夹点,删除边界,并且重新生成填充和边界 + // 0x02 移动了填充边界上的夹点,不处理,然后它会通过关联进行自己修改 + _pickInBo = false; + for (int i = 0; i < _hatchIds.Count; i++) + { + var hatId = _hatchIds[i]; + if (!_mapHatchConv.ContainsKey(hatId)) + continue; + + _mapHatchConv[hatId].BoundaryIds.ForEach((id, idState) => { + var boEnt = tr.GetObject(id); + if (boEnt == null) + return; + + // 获取夹点在哪个图元边界上 + HashSet boPts = new(); + if (boEnt is Circle circle) + { + // 圆形的边界夹点是: 圆心+半径 + var x = circle.Center.X; + var y = circle.Center.Y; + var z = circle.Center.Z; + var r = circle.Radius; + boPts.Add(new(x + r, y, z));//上 + boPts.Add(new(x - r, y, z));//下 + boPts.Add(new(x, y - r, z));//左 + boPts.Add(new(x, y + r, z));//右 + } + else + { + // 获取所有的边点 + // 这里圆形会获取圆心,所以剔除圆形 + var tmp = GetEntityPoint3ds(boEnt); + for (int j = 0; j < tmp.Count; j++) + boPts.Add(tmp[j]); + } + + if (boEnt is Arc arc) + { + if (!arc.StartPoint.IsEqualTo(arc.EndPoint, _tol)) + { + // 圆弧的腰点 + var arc2 = arc.GetPointAtDist(arc.GetDistAtPoint(arc.EndPoint) * 0.5); + boPts.Add(arc2); + } + } + else if (boEnt is Polyline pl) + { + for (int j = 0; j < pl.NumberOfVertices; j++) + { + var bulge = pl.GetBulgeAt(j); + if (bulge == 0.0) + continue; + // 有凸度就是有每段的中点 + var pta = pl.GetPoint2dAt(j); + Point2d ptb; + if (j + 1 < pl.NumberOfVertices) + ptb = pl.GetPoint2dAt(j + 1); + else + ptb = pl.GetPoint2dAt(0); + + var p = MathHelper.GetArcMidPoint(pta, ptb, bulge); + boPts.Add(p.Point3d()); + } + } + + boPts.ForEach((pt, ptState) => { + var dist = pt.DistanceTo(mouseStart); + //Debugx.Printl("pt::" + pt + " dist::" + dist); + if (dist < tol) + { + ptState.Break(); + _pickInBo = true; + } + }); + }); + + // 点在边界上:就不处理了,它会通过cad的关联填充反应器自动修改 + if (_pickInBo) + Debugx.Printl("夹点在边界上"); + else + Debugx.Printl("夹点不在边界上"); + } + } + } + + /// + /// 图元拉伸点 + /// + /// + /// + static List GetEntityPoint3ds(Entity ent) + { + var pts3d = new Point3dCollection(); + ent.GetStretchPoints(pts3d); + return pts3d.Cast().ToList(); + } + + + + + /// + /// 反应器->command命令完成后 + /// + /// + /// + void Md_CommandEnded(object sender, CommandEventArgs e) + { + if (!State.IsRun) + return; + + var cmdup = e.GlobalCommandName.ToUpper(); + switch (cmdup) + { + case "REFEDIT": + { + Debugx.Printl("Md_CommandEnded:: REFEDIT"); + + // 在位编辑命令,执行后,获取当前空间所有填充 + var prompt = Env.Editor.SelectAll(FilterForHatch); + if (prompt.Status != PromptStatus.OK) + return; + + using DBTrans tr = new(); + GetHatchIds(prompt); + if (_hatchIds.Count == 0) + return; + + for (int i = 0; i < _hatchIds.Count; i++) + if (!_refeditSsgeting.Contains(_hatchIds[i]))//Except + _refeditSsgeted.Add(_hatchIds[i]); + + var sb = new StringBuilder(); + foreach (var id in _refeditSsgeted) + sb.AppendLine(id.ToString()); + Env.Printl("块内填充id:" + sb.ToString()); + } + break; + case "REFSET": // 加减在位编辑图元 + { + Debugx.Printl("Md_CommandEnded:: REFSET"); + + // 命令历史的最后一行是:添加/删除 + var last = Env.GetVar("lastprompt").ToString(); + if (last is null) + return; + + // 完成后必然有上次选择集 + var prompt = Env.Editor.SelectPrevious(); + if (prompt.Status != PromptStatus.OK) + return; + using DBTrans tr = new(); + GetHatchIds(prompt); + if (_hatchIds.Count == 0) + return; + + // 就是因为无法遍历到在位编辑的块内图元,只能进行布尔运算 + if (last.Contains("添加") || last.Contains("Added"))// 中英文cad + { + for (int i = 0; i < _hatchIds.Count; i++) + { + _refeditSsgeting.Remove(_hatchIds[i]); + _refeditSsgeted.Add(_hatchIds[i]); + } + return; + } + if (last.Contains("删除") || last.Contains("Removed"))// 中英文cad + { + for (int i = 0; i < _hatchIds.Count; i++) + { + _refeditSsgeted.Remove(_hatchIds[i]); + _refeditSsgeting.Add(_hatchIds[i]); + } + return; + } + } + break; + case "REFCLOSE":// 保存块,清空集合 + { + Debugx.Printl("Md_CommandEnded:: REFCLOSE"); + _refeditSsgeted.Clear(); + _refeditSsgeting.Clear(); + } + break; + case "GRIP_STRETCH":// 拉伸夹点命令后触发 + { + // 夹点在边界上,退出 + if (_pickInBo) + return; + + // 夹点不在边界上: + // cad会平移填充,在这之后,我们删除填充边界,重建填充边界 + var prompt = Env.Editor.SelectImplied(); + if (prompt.Status != PromptStatus.OK) + return; + using DBTrans tr = new(); + GetHatchIds(prompt); + if (_hatchIds.Count == 0) + return; + + // 删除指定填充的边界,并清理关联反应器 + HashSet idsOfSsget = new(); + foreach (var hatId in _hatchIds) + { + idsOfSsget.Add(hatId); + + if (!_mapHatchConv.ContainsKey(hatId)) + continue; + bool clearFlag = false; + _mapHatchConv[hatId].BoundaryIds.ForEach(boId => { + if (!boId.IsOk()) + return; + var boEnt = tr.GetObject(boId); + if (boEnt == null) + return; + if (!HatchPickEnv.IsMeCreate(boEnt)) + return; + boId.Erase(); + clearFlag = true; + }); + + if (!clearFlag) + return; + + _mapHatchConv[hatId].BoundaryIds.Clear(); + + // 清理填充反应器 + var hatch = tr.GetObject(hatId); + if (hatch == null) + return; + using (hatch.ForWrite()) + RemoveAssociative(hatch); + CreatHatchConverter(hatch, idsOfSsget); + } + SetImpliedSelection(idsOfSsget); + } + break; + } + } + + /// + /// 获取选择集上的填充,在缓存内提取 + /// + /// + /// + static void GetHatchIds(PromptSelectionResult psr, DBTrans? tr = null) + { + tr ??= DBTrans.Top; + _hatchIds.Clear(); + var ids = psr.Value.GetObjectIds(); + for (int i = 0; i < ids.Length; i++) + { + var hatch = tr.GetObject(ids[i]); + if (hatch is not null) + _hatchIds.Add(ids[i]); + } + } + + /// + /// 反应器->lisp命令 + /// + void Md_LispWillStart(object sender, LispWillStartEventArgs e) + { + if (!State.IsRun) + return; + + using DBTrans tr = new(doclock: true); + EraseAllHatchBorders(); + } + + /// + /// 反应器->选择集 + /// + /// + /// + void Md_ImpliedSelectionChanged(object sender, EventArgs e) + { + if (!State.IsRun) + return; + + // 此处必须要文档锁 + if (_selectChangedStop) + { + _selectChangedStop = false; + return; + } + Debugx.Printl("Md_ImpliedSelectionChanged"); + + using DBTrans tr = new(doclock: true); + var prompt = Env.Editor.SelectImplied(); + if (prompt.Status != PromptStatus.OK) + { + EraseAllHatchBorders(); + return; + } + + // 获取图层锁定的记录,用于跳过 + Dictionary islocks = new(); + foreach (var layerRecord in tr.LayerTable.GetRecords()) + if (!layerRecord.IsErased)// 08符号表记录保留了这个 + islocks.Add(layerRecord.Name, layerRecord.IsLocked); + + // 遍历选择,创建边界转换器 + // 重设选择集 + HashSet idsOfSsget = new(); + foreach (var entId in prompt.Value.GetObjectIds()) + { + idsOfSsget.Add(entId); + var hatch = tr.GetObject(entId, openLockedLayer: true); + if (hatch is null) + continue; + if (islocks[hatch.Layer]) + continue; + // 重复选择 || 在位编辑外 + if (_mapHatchConv.ContainsKey(entId) || _refeditSsgeting.Contains(entId)) + continue; + CreatHatchConverter(hatch, idsOfSsget); + } + SetImpliedSelection(idsOfSsget); + } + + /// + /// 创建填充和填充边界转换器 + /// + /// + /// + /// + void CreatHatchConverter(Hatch hatch, HashSet outSsgetIds, DBTrans? tr = null) + { + tr ??= DBTrans.Top; + + var hc = new HatchConverter(hatch); + ObjectId newid; + + // 如果边界在图纸上没有删除(删除就不是关联的), + // 那就不创建新的,然后选中它们 + if (hc.BoundaryIds.Count != 0) + { + Debugx.Printl("CreatHatchConverter:: 加入了现有边界到选择集"); + + // 加入选择集 + foreach (var item in hc.BoundaryIds) + outSsgetIds.Add(item); + outSsgetIds.Add(hatch.ObjectId); + newid = hatch.ObjectId; + } + else + { + Debugx.Printl("CreatHatchConverter:: 创建新填充和边界"); + + // 创建新填充和边界 + hc.GetBoundarysData(); + newid = hc.CreateBoundarysAndHatchToMsPs(tr.CurrentSpace, trans: tr); + HatchPickEnv.SetMeXData(newid, hc.BoundaryIds); + + // 清理上次,删除边界和填充 + if (_mapHatchConv.ContainsKey(hatch.ObjectId)) + { + var boIds = _mapHatchConv[hatch.ObjectId].BoundaryIds; + for (int i = 0; i < boIds.Count; i++) + boIds[i].Erase(); + _mapHatchConv.Remove(hatch.ObjectId); + } + // 删除选中的 + hatch.ObjectId.Erase(); + } + + if (!_mapHatchConv.ContainsKey(newid)) + _mapHatchConv.Add(newid, hc); + else + _mapHatchConv[newid] = hc; + + if (newid == hatch.ObjectId) + return; + + // 优先: 块内含有旧的,就加入新的 + if (_refeditSsgeted.Contains(hatch.ObjectId)) + { + _refeditSsgeted.Remove(hatch.ObjectId); + _refeditSsgeted.Add(newid); + } + else if (_refeditSsgeting.Contains(hatch.ObjectId)) + { + _refeditSsgeting.Remove(hatch.ObjectId); + _refeditSsgeting.Add(newid); + } + } + + /// + /// 重设选择集 + /// + /// 加入选择集的成员 + void SetImpliedSelection(HashSet setImpSelect) + { + // 获取填充 + foreach (var id in _mapHatchConv.Keys) + setImpSelect.Add(id); + + // 获取填充边界 + foreach (var item in _mapHatchConv.Values) + foreach (var id in item.BoundaryIds) + setImpSelect.Add(id); + + // 设置选择集,没有标记的话会死循环 + _selectChangedStop = true; + Env.Editor.SetImpliedSelection(setImpSelect.ToArray()); + } + + /// + /// 删除全部填充边界 + /// + void EraseAllHatchBorders() + { + if (_mapHatchConv.Count == 0) + return; + foreach (var dict in _mapHatchConv) + { + dict.Value.BoundaryIds.ForEach(boId => { + if (!boId.IsOk()) + return; + using DBTrans tr = new(database: boId.Database); + var boEnt = tr.GetObject(boId, OpenMode.ForWrite); + if (boEnt == null) + return; + // 删除填充边界并清理关联反应器 + if (!HatchPickEnv.IsMeCreate(boEnt)) + return; + boEnt.Erase(); + if (dict.Key.IsOk()) + { + var hatch = tr.GetObject(dict.Key, OpenMode.ForWrite); + if (hatch == null) + return; + RemoveAssociative(hatch); + } + }); + } + _mapHatchConv.Clear(); + } + + /// + /// 移除关联反应器 + /// + /// + static void RemoveAssociative(Hatch hatch) + { + // 撤回填充,没有边界就移除关联反应器 + if (!hatch.Associative) + return; + + // 填充边界反应器 + var assIds = hatch.GetAssociatedObjectIds(); + if (assIds == null) + return; + bool isok = true; + foreach (ObjectId id in assIds) + { + if (!id.IsOk()) + { + isok = false; + break; + } + } + // 这里边界id已经删除了,所以移除会导致异常 + if (isok) + hatch.RemoveAssociatedObjectIds(); + // 取消关联反应器才能生成的正确 + hatch.Associative = false; + } + + /// + /// 撤回事件(获取删除对象) + /// + /// + /// + static void DB_ObjectErased(object sender, ObjectErasedEventArgs e) + { + if (!State.IsRun) + return; + + // object erased. + if (e.Erased) + { + return; + } + + // UNDO + if (e.DBObject is Hatch hatch) + { + if (HatchPickEnv.IsMeCreate(hatch)) + RemoveAssociative(hatch); + } + else if (e.DBObject is Entity boEnt) + { + // 撤回边界 + if (HatchPickEnv.IsMeCreate(boEnt)) + { + boEnt.Erase(); + // 通过xdata回溯填充,清理关联反应器 + if (boEnt.XData != null) + { + using DBTrans tr = new(); + var hatchId = HatchPickEnv.GetXdataHatch(boEnt); + if (hatchId.IsOk()) + { + var hatchEnt = tr.GetObject(hatchId, OpenMode.ForWrite); + if (hatchEnt != null) + RemoveAssociative(hatchEnt); + } + } + } + } + } + + /// + /// 撤回事件(更改时触发) + /// 它会获取有修改步骤的图元id + /// + /// + /// + static void DB_ObjectModified(object sender, ObjectEventArgs e) + { + if (!State.IsRun) + return; + + // 然后删除我制造的拉伸填充上面的关联反应器 + if (!e.DBObject.IsUndoing) + return; + if (e.DBObject.IsErased) + return; + // 是我生成的填充才删除关联 + if (e.DBObject is Hatch hatch) + { + if (HatchPickEnv.IsMeCreate(hatch)) + RemoveAssociative(hatch); + } + } + + void SetPropertiesInfoTask() + { + // 原有选择集 + var prompt = Env.Editor.SelectImplied(); + if (prompt.Status != PromptStatus.OK) + return; + + using DBTrans tr = new(); + + // 获取记录的边界 + HashSet boAll = new(); + foreach (var hc in _mapHatchConv.Values) + foreach (var boid in hc.BoundaryIds) + boAll.Add(boid); + + // 获取选择集上面所有的填充,如果没有填充就结束(不屏蔽特性面板) + bool hasHatch = false; + HashSet idsOfSsget = new(); + foreach (var id in prompt.Value.GetObjectIds()) + { + // 含有填充 + if (_mapHatchConv.ContainsKey(id)) + hasHatch = true; + // 排除边界的加入 + if (!boAll.Contains(id)) + idsOfSsget.Add(id); + } + if (!hasHatch) + return; + + // 删除填充边界,并清理关联反应器 + EraseAllHatchBorders(); + + // 重设选择集 提供给后续命令判断 + SetImpliedSelection(idsOfSsget); + + // 如果有填充才否决 + _vetoProperties = idsOfSsget.Count != 0; + } + #endregion + + #region IDisposable接口相关函数 + public bool IsDisposed { get; private set; } = false; + + /// + /// 手动调用释放 + /// + public void Dispose() + { + Dispose(true); + GC.SuppressFinalize(this); + } + + /// + /// 析构函数调用释放 + /// + ~HatchPickEvent() + { + Dispose(false); + } + + protected virtual void Dispose(bool disposing) + { + // 不重复释放 + if (IsDisposed) return; + IsDisposed = true; + + if (_doc.IsDisposed) + return; + LoadHelper(false); + } + #endregion +} + +/// +/// 填充的鼠标钩子 +/// +public static class HatchHook +{ + static readonly MouseHook MouseHook; + // 夹点拉伸前的点,拉伸后利用命令后反应器去处理"GRIP_STRETCH" + static volatile int _X; + static volatile int _Y; + public static Point MouseStartPoint { get => new(_X, _Y); } + + /// + /// 鼠标双击事件 + /// + public static event EventHandler? DoubleClick; + + static HatchHook() + { + MouseHook = new(); + } + + /// + /// 查找主线程
    + /// 代替
    + /// 托管线程和他们不一样: + ///
    + /// 主窗口 + /// 进程ID + /// 线程ID + [DllImport("user32.dll", SetLastError = true)] + static extern uint GetWindowThreadProcessId(IntPtr hWnd, out uint lpdwProcessId); + + [DllImport("user32.dll")] + static extern bool IsWindowEnabled(IntPtr hWnd); + /// + /// 获取当前窗口 + /// + /// 当前窗口标识符 + [DllImport("user32.dll")] + static extern IntPtr GetForegroundWindow(); + + public static void SetHook() + { + // 如果是全局钩子会发生偶尔失效的情况,改用进程钩子反而好多了 + MouseHook.SetHook(true); + MouseHook.MouseDown += (sender, e) => { + // 此处断点时候就会使得钩子失效 + if (!IsWindowEnabled(Acap.MainWindow.Handle)) + return; + // 进程号拦截 + GetWindowThreadProcessId(GetForegroundWindow(), out uint winId); + if (MouseHook.Process.Id != winId) + return; + if (e.Button == MouseButtons.Left) + { + _X = e.X; + _Y = e.Y; + } + }; + + MouseHook.DoubleClick += (sender, e) => { + // 此处断点时候就会使得钩子失效 + if (!IsWindowEnabled(Acap.MainWindow.Handle)) + return; + // 进程号拦截 + GetWindowThreadProcessId(GetForegroundWindow(), out uint winId); + if (MouseHook.Process.Id != winId) + return; + DoubleClick?.Invoke(sender, e); + }; + } + + public static void RemoveHook() + { + MouseHook?.Dispose(); + } +} + +public static class HatchPickEnv +{ + //static readonly string _appName = nameof(JoinBox); + //static readonly string _data = nameof(CreateBoundary); + + static readonly string _appName = "JoinBox"; + static readonly string _data = "CreateBoundary"; + + /// + /// 判断图元是否由 我的转换器 创建(相对的是直接提取现有图元边界) + /// + /// 任何图元 + /// + public static bool IsMeCreate(Entity entity) + { + if (entity.XData == null) + return false; + var xl = (XDataList)entity.XData; + return xl.Contains(_appName, _data); + } + + /// + /// 我的转换器 xdata数据模板 + /// + /// + /// + /// + public static ResultBuffer GetMeBuffer(Handle hatchHandle, DBTrans? trans = null) + { + trans ??= DBTrans.Top; + trans.RegAppTable.Add(_appName); // add函数会默认的在存在这个名字的时候返回这个名字的regapp的id,不存在就新建 + ResultBuffer resBuf = new() + { + new((int)DxfCode.ExtendedDataRegAppName, _appName), + new((int)DxfCode.ExtendedDataAsciiString,_data), + new((int)DxfCode.ExtendedDataHandle, hatchHandle),//边界回溯这个填充的句柄,如果创建新填充,就需要再去改 + }; + return resBuf; + } + + /// + /// 填充和边界上面增加xdata,实现区分原生和我的数据 + /// + /// + /// + /// + public static void SetMeXData(ObjectId newHatchId, List boIds, DBTrans? trans = null) + { + trans ??= DBTrans.Top; + var hatchEnt = trans.GetObject(newHatchId); + if (hatchEnt != null) + using (hatchEnt.ForWrite()) + hatchEnt.XData = GetMeBuffer(hatchEnt.Handle, trans); // 设置xdata仅仅为debug可以通过鼠标悬停看见它数据,因此设置为自己 + + // 修改边界的xdata为新填充的 + boIds.ForEach(id => { + var boEnt = trans.GetObject(id); + if (boEnt is null) + return; + using (boEnt.ForWrite()) + { + boEnt.RemoveXData(_appName); + boEnt.XData = GetMeBuffer(newHatchId.Handle, trans); + } + }); + } + + /// + /// 通过边界ent获取填充id + /// + /// 边界图元 + /// + /// + public static ObjectId GetXdataHatch(Entity boEntity, DBTrans? trans = null) + { + if (boEntity.XData == null) + return ObjectId.Null; + XDataList data = boEntity.XData; + + if (!data.Contains(_appName, _data)) + return ObjectId.Null; + + var indexs = data.GetXdataAppIndex(_appName, new DxfCode[] { DxfCode.ExtendedDataHandle }); + if (indexs.Count == 0) + return ObjectId.Null; + + trans ??= DBTrans.Top; + return trans.GetObjectId(data[indexs[0]].Value.ToString()); + } +} + +public static class MathHelper +{ + /// + /// 圆弧的腰点 + /// + /// 圆弧点1 + /// 圆弧点3 + /// 凸度 + /// 返回腰点 + /// + [MethodImpl] + public static Point2d GetArcMidPoint(Point2d arc1, Point2d arc3, double bulge) + { + if (bulge == 0) + throw new ArgumentException("凸度为0,此线是平的"); + + var center = GetArcBulgeCenter(arc1, arc3, bulge); + var angle1 = center.GetVectorTo(arc1).GetAngle2XAxis(); + var angle3 = center.GetVectorTo(arc3).GetAngle2XAxis(); + // 利用边点进行旋转,就得到腰点,旋转角/2 + // 需要注意镜像的多段线 + double angle = angle3 - angle1; + if (bulge > 0) + { + if (angle < 0) + angle += Math.PI * 2; + } + else + { + if (angle > 0) + angle += Math.PI * 2; + } + return arc1.RotateBy(angle / 2, center); + } + /// http://bbs.xdcad.net/thread-722387-1-1.html + /// https://blog.csdn.net/jiangyb999/article/details/89366912 + /// + /// 凸度求圆心 + /// + /// 圆弧头点 + /// 圆弧尾点 + /// 凸度 + /// 圆心 + [MethodImpl] + public static Point2d GetArcBulgeCenter(Point2d arc1, Point2d arc3, double bulge) + { + if (bulge == 0) + throw new ArgumentException("凸度为0,此线是平的"); + + var x1 = arc1.X; + var y1 = arc1.Y; + var x2 = arc3.X; + var y2 = arc3.Y; + + var b = (1 / bulge - bulge) / 2; + var x = (x1 + x2 - b * (y2 - y1)) / 2; + var y = (y1 + y2 + b * (x2 - x1)) / 2; + return new Point2d(x, y); + } + + /// + /// X轴到向量的弧度,cad的获取的弧度是1PI,所以转换为2PI(上小,下大) + /// + /// 向量 + /// X轴到向量的弧度 + public static double GetAngle2XAxis(this Vector2d ve, double tolerance = 1e-6) + { + const double Tau = Math.PI + Math.PI; + // 世界重合到用户 Vector3d.XAxis->两点向量 + double al = Vector2d.XAxis.GetAngleTo(ve); + al = ve.Y > 0 ? al : Tau - al; // 逆时针为正,大于0是上半圆,小于则是下半圆,如果-负值控制正反 + al = Math.Abs(Tau - al) <= tolerance ? 0 : al; + return al; + } +} \ No newline at end of file diff --git "a/tests/TestAcad09plus/\346\213\211\344\274\270\345\241\253\345\205\205/HatchHelper.cs" "b/tests/TestAcad09plus/\346\213\211\344\274\270\345\241\253\345\205\205/HatchHelper.cs" new file mode 100644 index 0000000..6d6c243 --- /dev/null +++ "b/tests/TestAcad09plus/\346\213\211\344\274\270\345\241\253\345\205\205/HatchHelper.cs" @@ -0,0 +1,124 @@ +namespace JoinBoxAcad; + +public static class HatchHelper +{ + /// + /// 遍历填充每条边 + /// + /// + /// + public static void ForEach(this Hatch hatch, Action action) + { + for (int i = 0; i < hatch.NumberOfLoops; i++) + action.Invoke(hatch.GetLoopAt(i)); + } + + +#if false + /// + /// 分离填充边界 + /// 将外边界和包含的边界成为一个集 + /// + /// 填充的边界(只有多段线和圆) + /// 多个id集,id中第一个是外边界 + public static IEnumerable[] SeparationBorder(this IEnumerable bianjie) + { + IEnumerable[] objectIds = null; + if (bianjie.Length < 1) return null; + + Database db = bianjie[0].Database; + + // 首先获取一个图元,这个图元默认成边界,看它是否包含第二个,如果是,加入a集 + // 如果不是,它是否把自己包含了, + // 如果是,把它加入a集,并下次使用它来成为外边界 + // 如果不是,则为新边界,把它加入b集 + List alist = new List(); // 边界包含 + List blist = new List(); // 有另外的边界 + List> clist = new List>();// 边界集 + + using (Transaction tr = db.TransactionManager.StartTransaction()) + { + while (true) + { + for (int i = 0; i < bianjie.Length; i++) + { + Entity ent = bianjie[i].ObjectIdToEntity(false); + ent.UpgradeOpen(); + for (int j = i + 1; j < bianjie.Length; j++) + { + if (ent is Polyline polyline)// 多段线 + { + } + else if (ent is Circle circle) // 圆 + { + } + } + ent.DowngradeOpen(); + } + + if (blist.Count == 0)// 没有其他边界就结束循环 + { + break; + } + // 把blist的第一个用来作为新的外边界 + } + } + return objectIds; + } + + /// + /// 判断边界是否包含图元 布尔算法... + /// + /// 边界 + /// 包含的图元 + /// 是true,否false + public static bool BorderIntoCollect(this ObjectId border, ObjectId include) + { + bool flag = false; + Database db = border.Database; + using (Transaction tr = db.TransactionManager.StartTransaction()) + { + Entity ent = border.ObjectIdToEntity(false); + Entity ent2 = include.ObjectIdToEntity(false); + + if (ent is Polyline polyline)// 多段线边界 + { + if (ent2 is Polyline polyline2)// 多段线 + { + } + else if (ent2 is Circle circle2) // 圆 + { + // 判断圆心在多段线内 + if (circle2.Center.RayCasting(polyline.GetPolylinePoints()) != 3) + { + if (true)// 半径若大于多段线最长那段,表示包含不到,是圆包含了多段线(含有弧度就错了) + { + flag = true; + } + } + else // 圆心不在边界内,判断边界是否有交点 + { + } + } + } + else if (ent is Circle circle) // 圆边界 + { + if (ent2 is Polyline polyline2)// 多段线 + { + } + else if (ent2 is Circle circle2) // 圆 + { + // 填充边界不存在交集 + // 两个圆心的距离>两个圆的半径和=两圆分离 + double length = circle.Center.GetDistanceBetweenTwoPoint(circle2.Center); + if (length < circle.Radius + circle2.Radius) + { + flag = true; + } + } + } + return flag; + } + } +#endif +} \ No newline at end of file diff --git "a/tests/TestAcad09plus/\346\213\211\344\274\270\345\241\253\345\205\205/\345\261\217\345\271\225\345\235\220\346\240\207\350\275\254cad\345\235\220\346\240\207.cs" "b/tests/TestAcad09plus/\346\213\211\344\274\270\345\241\253\345\205\205/\345\261\217\345\271\225\345\235\220\346\240\207\350\275\254cad\345\235\220\346\240\207.cs" new file mode 100644 index 0000000..00e7c64 --- /dev/null +++ "b/tests/TestAcad09plus/\346\213\211\344\274\270\345\241\253\345\205\205/\345\261\217\345\271\225\345\235\220\346\240\207\350\275\254cad\345\235\220\346\240\207.cs" @@ -0,0 +1,133 @@ +//#define cpp +namespace JoinBoxAcad; + +using System.Drawing; +using static IFoxCAD.Cad.WindowsAPI; + +public partial class Screen +{ + [CommandMethod(nameof(GetScreenToCadxx))] + public static void GetScreenToCadxx() + { + var dm = Acap.DocumentManager; + var doc = dm.MdiActiveDocument; + var ed = doc.Editor; + var ucsPoint = GetScreenToCad(); + ed.WriteMessage(ucsPoint.ToString() + "\n"); + } + + /// + /// 屏幕坐标转cad坐标 + /// + public static Point3d GetScreenToCad() + { + // 两种获取方式都可以 + var cursorPos = System.Windows.Forms.Control.MousePosition; + // GetCursorPos(out Point cursorPos); + return ScreenToCad(cursorPos); + } + + /// + /// 屏幕像素点转cad图纸坐标点 + /// + /// 屏幕像素点 + /// 返回ucs的点 + public static Point3d ScreenToCad(Point cursorPos) + { + var dm = Acap.DocumentManager; + var doc = dm.MdiActiveDocument; + var ed = doc.Editor; + var mid = WindowsAPI.GetParent(doc.Window.Handle); + + ScreenToClient(mid, ref cursorPos); + var vn = ed.GetViewportNumber(cursorPos);// System.Windows.Forms.Control.MousePosition + var wcsPoint = ed.PointToWorld(cursorPos, vn); + var ucsPoint = wcsPoint.TransformBy(doc.Editor.CurrentUserCoordinateSystem.Inverse()); + return ucsPoint; + } + + /// + /// 屏幕坐标到客户区坐标转换 + /// + /// 窗口句柄 + /// 点结构,返回屏幕坐标 + /// + [DllImport("user32.dll")] + public static extern bool ScreenToClient(IntPtr hWnd, ref Point lpPoint); + [DllImport("user32.dll")] + static extern bool ClientToScreen(IntPtr hWnd, ref Point lpPoint); + + public static Point CadToScreen(Point3d pt3d, Point mousePosition) + { + var dm = Acap.DocumentManager; + var doc = dm.MdiActiveDocument; + var ed = doc.Editor; + var mid = WindowsAPI.GetParent(doc.Window.Handle); + + var vn = ed.GetViewportNumber(mousePosition);//System.Windows.Forms.Control.MousePosition + var ptScr = Env.Editor.PointToScreen(pt3d, vn);// 高版本这个不一样,转为客户区 + var ptScrWin = new Point((int)ptScr.X, (int)ptScr.Y); + ClientToScreen(mid, ref ptScrWin); + return ptScrWin; + } + + //#if NET35 + // [DllImport("acad.exe", EntryPoint = "?acedGetAcadDwgView@@YAPAVCView@@XZ")] //acad08 + //#else + // [DllImport("acad.exe", EntryPoint = "?acedGetAcadDwgView@@YAPEAVCView@@XZ")]//acad21 + //#endif + // static extern IntPtr AcedGetAcadDwgview(); + + delegate IntPtr DelegateAcedGetAcadDwgview(); + static DelegateAcedGetAcadDwgview? acedGetAcadDwgView; + /// + /// 获取视口指针 + /// + public static IntPtr AcedGetAcadDwgview() + { + if (acedGetAcadDwgView is null) + { + acedGetAcadDwgView = AcadPeInfo + .GetDelegate( + nameof(acedGetAcadDwgView), AcadPeEnum.AcadExe); + } + if (acedGetAcadDwgView is not null) + return acedGetAcadDwgView.Invoke();// 调用方法 + return IntPtr.Zero; + } + + delegate int DelegateAcedGetWinNum(int x, int y); + static DelegateAcedGetWinNum? acedGetWinNum; + /// + /// 获取窗口数字 + /// + public static int AcedGetWinNum(int x, int y) + { + if (acedGetWinNum is null) + acedGetWinNum = AcadPeInfo + .GetDelegate( + nameof(acedGetWinNum), AcadPeEnum.ExeAndCore); + if (acedGetWinNum is not null) + return acedGetWinNum.Invoke(x, y);// 调用方法 + return 0; + } + + /// + /// 将坐标从绘图窗口转换为活动视口坐标系 + /// + /// + /// + /// + /// +#if NET35 + // 此处都是acad08这个有重载,不知道PeInfo能不能正常运行 + [DllImport("acad.exe", EntryPoint = "?acedCoordFromPixelToWorld@@YAHHVCPoint@@QAN@Z")] + static extern int AcedCoordFromPixelToWorld(int windnum, Point pt, out Point3D ptOut); + + [DllImport("acad.exe", EntryPoint = "?acedCoordFromPixelToWorld@@YAXABVCPoint@@QAN@Z")]//这个重载参数不知道 + static extern int AcedCoordFromPixelToWorld(Point pt, out Point3D ptOut); +#else + [DllImport("accore.dll", EntryPoint = "?acedCoordFromPixelToWorld@@YAHHVCPoint@@QEAN@Z")] + static extern int AcedCoordFromPixelToWorld(int windnum, Point pt, out Point3D ptOut); +#endif +} \ No newline at end of file diff --git "a/tests/TestAcad09plus/\346\213\211\344\274\270\345\241\253\345\205\205/\350\277\207\347\250\213\346\226\207\344\273\266/\344\270\200\344\270\252\346\227\240\346\263\225\347\247\273\345\212\250\347\272\242\350\211\262\345\234\206\347\232\204\344\276\213\345\255\220.cs" "b/tests/TestAcad09plus/\346\213\211\344\274\270\345\241\253\345\205\205/\350\277\207\347\250\213\346\226\207\344\273\266/\344\270\200\344\270\252\346\227\240\346\263\225\347\247\273\345\212\250\347\272\242\350\211\262\345\234\206\347\232\204\344\276\213\345\255\220.cs" new file mode 100644 index 0000000..4e45d21 --- /dev/null +++ "b/tests/TestAcad09plus/\346\213\211\344\274\270\345\241\253\345\205\205/\350\277\207\347\250\213\346\226\207\344\273\266/\344\270\200\344\270\252\346\227\240\346\263\225\347\247\273\345\212\250\347\272\242\350\211\262\345\234\206\347\232\204\344\276\213\345\255\220.cs" @@ -0,0 +1,94 @@ +// 一个无法移动红色圆的例子 https://www.keanw.com/2008/08/rolling-back-th.html +#if true2 +namespace JoinBoxAcad; + +public class CmdReactor +{ + Document? _doc; + ObjectIdCollection _ids = new(); + Point3dCollection _pts = new(); + + [CommandMethod(nameof(Test_REACTOR))] + public void Test_REACTOR() + { + _doc = Acap.DocumentManager.MdiActiveDocument; + _doc.CommandWillStart += Doc_CommandWillStart; + } + + /// + /// 挂载一个命令反应器,如果出现了move就挂载一个 + /// + /// + /// + void Doc_CommandWillStart(object sender, CommandEventArgs e) + { + if (e.GlobalCommandName == "MOVE") + { + _ids.Clear(); + _pts.Clear(); + + if (_doc is null) + return; + _doc.Database.ObjectOpenedForModify += Db_ObjectOpenedForModify; + _doc.CommandCancelled += Doc_CommandEnded; + _doc.CommandEnded += Doc_CommandEnded; + _doc.CommandFailed += Doc_CommandEnded; + } + } + + /// + /// 卸载一堆反应器 + /// + void RemoveEventHandlers() + { + if (_doc is null) + return; + _doc.CommandCancelled -= Doc_CommandEnded; + _doc.CommandEnded -= Doc_CommandEnded; + _doc.CommandFailed -= Doc_CommandEnded; + _doc.Database.ObjectOpenedForModify -= Db_ObjectOpenedForModify; + } + + void Doc_CommandEnded(object sender, CommandEventArgs e) + { + // 在恢复位置之前删除数据库reactor + RemoveEventHandlers(); + RollbackLocations(); + } + + /// + /// 颜色是1的圆加入集合 + /// + /// + /// + void Db_ObjectOpenedForModify(object sender, ObjectEventArgs e) + { + if (e.DBObject is Circle circle && circle.ColorIndex == 1)// 如果颜色是1 + { + // 不含有就加入集合 + if (!_ids.Collection.Contains(circle.ObjectId)) + { + _ids.Add(circle.ObjectId); + _pts.Add(circle.Center); + } + } + } + + /// + /// 修改圆心 + /// + void RollbackLocations() + { + Debug.Assert(_ids.Count == _pts.Count, "预计相同数量的ID和位置"); + _doc?.Database.Action(tr => { + int i = 0; + foreach (ObjectId id in _ids.Collection) + { + var circle = tr.GetObject(id, OpenMode.ForWrite) as Circle; + if (circle is not null) + circle.Center = _pts[i++]; + } + }); + } +} +#endif \ No newline at end of file diff --git "a/tests/TestAcad09plus/\346\213\211\344\274\270\345\241\253\345\205\205/\350\277\207\347\250\213\346\226\207\344\273\266/\350\207\252\345\256\232\344\271\211\345\233\276\345\205\203\345\241\253\345\205\205\350\276\271\347\225\214.txt" "b/tests/TestAcad09plus/\346\213\211\344\274\270\345\241\253\345\205\205/\350\277\207\347\250\213\346\226\207\344\273\266/\350\207\252\345\256\232\344\271\211\345\233\276\345\205\203\345\241\253\345\205\205\350\276\271\347\225\214.txt" new file mode 100644 index 0000000..50ccab0 --- /dev/null +++ "b/tests/TestAcad09plus/\346\213\211\344\274\270\345\241\253\345\205\205/\350\277\207\347\250\213\346\226\207\344\273\266/\350\207\252\345\256\232\344\271\211\345\233\276\345\205\203\345\241\253\345\205\205\350\276\271\347\225\214.txt" @@ -0,0 +1,104 @@ +拉伸填充bug: +1号: 不闭合的多段线,新建边界会丢失一个不和头点重叠的倒数第二个点. +与头点重叠是ok的 +闭合是ok的 +解决:因为倒数第二个点要从 curve.EndPoint 获取. + +2号: +如果画了一个矩形,然后填充,再删除边界,再点选填充,那么通过生成出来关联标注, +移动第一个点的时候,是没有响应的,移动第二次的时候才会响应. + +两种想法解决这个问题, +a:生成之后,用移动矩阵影响一下关联反应器. + 答:不会触发的,即使我分成两个事务分别提交. +b:先克隆一个填充,再创建关联边界. + 答:可行的,但是会导致3号问题的发生. + +3号: +由于我是生成填充的,所以在在位编辑的时候会在长事务内部: + +【贵妃】惊惊 2019/7/13 17:52:02 +我遇到了一个问题,如果是在位编辑的时候,当前空间是模型空间,那么我用函数克隆一个块外的东西到模型空间,实际上会克隆到在位编辑的内部... +我都不知道怎么处理这个情况了..莫非要关闭用户的在位编辑状态么.. + +【才人】edata 2019/7/13 17:57:15 +在位编辑是这样的. + +【贵妃】惊惊 2019/7/13 17:58:08 +那桌子是怎么控制在位编辑-减出去块外的? + +【才人】edata 2019/7/13 17:58:20 +在位编辑实际上是对当前空间的修改,然后移动回块定义.. + +【才人】edata 2019/7/13 17:58:44 +这个就不是很清楚了... + +【贵妃】惊惊 2019/7/13 17:58:47 +也就是长事务上面记录了要移动回去的id? +如果减选了就是剔除了id? + +【才人】edata 2019/7/13 17:59:27 +你能卡到这个长事务吗? + +【贵妃】惊惊 2019/7/13 17:59:39 +net貌似无法控制长事务呀 +应该桌子有考虑到的,只是我还没有挖出来具体怎么处理的.. + +【才人】edata 2019/7/13 18:08:59 +用命令去移除当前在位编辑. + +我用了命令去移除块外的填充和边界,这样操作是可行的, +然后又产生了一个问题,如果是块内的填充,我并不想减去. +想法: 用命令反应器在在位编辑前获取当前空间所有的图元,然后在位编辑时候就知道两个集合的交叉部分了 +答:证明是可以分类出来的,但是会引起一个问题,在位编辑复制的填充id不在任何一个表上. + +判断执行的时候不是选择前的填充均执行产生边界(这样就不用复制反应器了 +再检测命令 _refset 加减三个集合的填充 +但是命令反应器无法检测二级命令,只能通过 lastprompt 获取最后一行命令,判断添加或者删除 + +命令完成后,执行选择上次选择,便是_refset命令的选择 +然后把id改成加减到判断的两个集合中 + + + + + +U 大写命令,用户回滚的时会导致集合信息不符: +之前koz找到的方法,先选择在位编辑命令触发时的所有图元,再选择触发后的所有图元,分别建立两个集合储存,然后进行差集运算, +得出那些是块内图元,这个方法是可行的,然后我发现在使用+加入块内和-减出块内这也都可以判断, +问题就是....如果是使用了U回滚,那么我将不知道如何修改这两个集合.... + +检测在位编辑命令启动之后,而无结束命令的时候, +期间如果用户使用了u就提示用户是否禁止拉伸填充. + +挂载一个图元的反应器,如果使用了命令U,那么判断填充是否被更改了-> ObjectOpenedForModify 反应器 +如果更改了,更改方式无法知道?? 可能是改颜色,也可能是会在块内外加减操作更改. +无解决方案! + + + +拉伸非圆的时候,如果中心移动,那么边界不会跟随 +发生了拉伸命令的时候, +答: 选择的对象有填充,就克隆填充,并且删除原有的边界,及填充 + + + + +频闪控制 +grips=0 + + + + +编辑填充 +样条曲线边界生成 + +在cad2008设计一条填充边界 +填充边界的自定义图元名称叫做: HatchBoundary +设定HatchBoundary变量,0为使用,1为不使用边界 +想法破产:不会自定义图元. + + + +在位编辑的锁定图元的方法,该不是一个隐藏图层导致的吧. +不是,但是在位编辑的时候出现了一个图层 0-RefEdit0 \ No newline at end of file diff --git "a/tests/TestAcad09plus/\346\213\211\344\274\270\345\241\253\345\205\205/\350\277\207\347\250\213\346\226\207\344\273\266/\350\216\267\345\217\226\345\244\271\347\202\271\344\276\213\345\255\220.cs" "b/tests/TestAcad09plus/\346\213\211\344\274\270\345\241\253\345\205\205/\350\277\207\347\250\213\346\226\207\344\273\266/\350\216\267\345\217\226\345\244\271\347\202\271\344\276\213\345\255\220.cs" new file mode 100644 index 0000000..a2d7a98 --- /dev/null +++ "b/tests/TestAcad09plus/\346\213\211\344\274\270\345\241\253\345\205\205/\350\277\207\347\250\213\346\226\207\344\273\266/\350\216\267\345\217\226\345\244\271\347\202\271\344\276\213\345\255\220.cs" @@ -0,0 +1,100 @@ +#if !ac2008 +namespace GripOverruleTest; + +// https://through-the-interface.typepad.com/through_the_interface/2009/08/knowing-when-an-autocad-object-is-grip-edited-using-overrules-in-net.html +public class GripVectorOverrule : GripOverrule +{ + // A static pointer to our overrule instance + static public GripVectorOverrule theOverrule = new(); + // A flag to indicate whether we're overruling + static bool overruling = false; + // A single set of grips would not have worked in + // the case where multiple objects were selected. + static Dictionary _gripDict = new(); + + private string GetKey(Entity e) + { + // Generate a key based on the name of the object's type + // and its geometric extents + // (We cannot use the ObjectId, as this is null during + // grip-stretch operations.) + return e.GetType().Name + ":" + e.GeometricExtents.ToString(); + } + // Save the locations of the grips for a particular entity + private void StoreGripInfo(Entity e, Point3dCollection grips) + { + string key = GetKey(e); + if (_gripDict.ContainsKey(key)) + { + // Clear the grips if any already associated + Point3dCollection grps = _gripDict[key]; + using (grps) + grps.Clear(); + _gripDict.Remove(key); + } + // Now we add our grips + Point3d[] pts = new Point3d[grips.Count]; + grips.CopyTo(pts, 0); + Point3dCollection gps = new(pts); + _gripDict.Add(key, gps); + } + // Get the locations of the grips for an entity + private Point3dCollection? RetrieveGripInfo(Entity e) + { + Point3dCollection? grips = null; + string key = GetKey(e); + if (_gripDict.ContainsKey(key)) + grips = _gripDict[key]; + return grips; + } + public override void GetGripPoints(Entity e, Point3dCollection grips, IntegerCollection snaps, IntegerCollection geomIds) + { + base.GetGripPoints(e, grips, snaps, geomIds); + StoreGripInfo(e, grips); + } + public override void MoveGripPointsAt(Entity e, IntegerCollection indices, Vector3d offset) + { + var dm = Acap.DocumentManager; + var doc = dm.MdiActiveDocument; + var ed = doc.Editor; + + var grips = RetrieveGripInfo(e); + if (grips != null) + { + // Could get multiple points moved at once, + // hence the integer collection + foreach (int i in indices) + { + // Get the grip point from our internal state + Point3d pt = grips[i]; + // Draw a vector from the grip point to the newly + // offset location, using the index into the + // grip array as the color (excluding colours 0 and 7). + // These vectors don't getting cleared, which makes + // for a fun effect. + ed.DrawVector( + pt, + pt + offset, + (i >= 6 ? i + 2 : i + 1), // exclude colours 0 and 7 + false + ); + } + } + base.MoveGripPointsAt(e, indices, offset); + } + [CommandMethod(nameof(GripOverruleOnOff))] + public static void GripOverruleOnOff() + { + var dm = Acap.DocumentManager; + var doc = dm.MdiActiveDocument; + var ed = doc.Editor; + if (overruling) + RemoveOverrule(GetClass(typeof(Entity)), theOverrule); + else + AddOverrule(GetClass(typeof(Entity)), theOverrule, true); + overruling = !overruling; + Overruling = overruling; + ed.WriteMessage("\nGrip overruling turned {0}.", overruling ? "on" : "off"); + } +} +#endif \ No newline at end of file diff --git a/tests/TestConsole/TestConsole.csproj b/tests/TestConsole/TestConsole.csproj index 43c900a..d79b76c 100644 --- a/tests/TestConsole/TestConsole.csproj +++ b/tests/TestConsole/TestConsole.csproj @@ -10,6 +10,10 @@ preview + + + + -- Gitee From fbf22a9b6af30c97c2164807bdbdbc90951b562e Mon Sep 17 00:00:00 2001 From: vicwjb Date: Mon, 9 Jan 2023 00:06:07 +0800 Subject: [PATCH 634/675] =?UTF-8?q?=E4=BF=AE=E5=A4=8DGetEntities=E5=87=BD?= =?UTF-8?q?=E6=95=B0=E8=BF=94=E5=9B=9E=E5=80=BC=E5=90=AB=E5=8F=AF=E7=A9=BA?= =?UTF-8?q?=E7=B1=BB=E5=9E=8B=E7=9A=84=E9=97=AE=E9=A2=98=EF=BC=8Cfixed=20#?= =?UTF-8?q?I692LA?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/CAD/IFox.CAD.Shared/ExtensionMethod/SelectionSetEx.cs | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/CAD/IFox.CAD.Shared/ExtensionMethod/SelectionSetEx.cs b/src/CAD/IFox.CAD.Shared/ExtensionMethod/SelectionSetEx.cs index 432ed01..a87610f 100644 --- a/src/CAD/IFox.CAD.Shared/ExtensionMethod/SelectionSetEx.cs +++ b/src/CAD/IFox.CAD.Shared/ExtensionMethod/SelectionSetEx.cs @@ -68,12 +68,12 @@ public static IEnumerable> GetObjectIdGroup(this Sel /// 指定类型 /// 选择集 /// 打开模式 - /// 事务 + /// 事务 /// 是否打开已删除对象,默认为不打开 /// 是否打开锁定图层对象,默认为不打开 /// 图元集合 [System.Diagnostics.DebuggerStepThrough] - public static IEnumerable GetEntities(this SelectionSet ss, + public static IEnumerable GetEntities(this SelectionSet ss, OpenMode openMode = OpenMode.ForRead, DBTrans? trans = null, bool openErased = false, @@ -85,7 +85,8 @@ public static IEnumerable> GetObjectIdGroup(this Sel trans ??= DBTrans.Top; return ss.GetObjectIds() .Select(id => trans.GetObject(id, openMode, openErased, openLockedLayer)) - .Where(ent => ent != null); + .Where(ent => ent != null) + .OfType(); } #endregion -- Gitee From 1b0bd38bcf70adae0f36fb5744a392a3a5d905ff Mon Sep 17 00:00:00 2001 From: vicwjb Date: Mon, 9 Jan 2023 22:24:03 +0800 Subject: [PATCH 635/675] =?UTF-8?q?=E4=BF=AE=E5=A4=8Difox=E7=9A=84valuetup?= =?UTF-8?q?le=EF=BC=8Cindex=EF=BC=8Crange=E4=B8=8E=E5=85=B6=E4=BB=96?= =?UTF-8?q?=E4=B8=89=E6=96=B9=E5=8C=85=E5=86=B2=E7=AA=81=E7=9A=84=E9=97=AE?= =?UTF-8?q?=E9=A2=98=EF=BC=8C=E9=80=9A=E8=BF=87=E8=AE=BE=E7=BD=AE=E5=B8=B8?= =?UTF-8?q?=E9=87=8F=E6=9D=A5=E8=A7=A3=E5=86=B3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/Basal/IFox.Basal.Shared/CLS/Index.cs | 8 +++++-- src/Basal/IFox.Basal.Shared/CLS/Range.cs | 9 ++++++-- .../IFox.Basal.Shared/CLS/RuntimeHelpers.cs | 21 +++++++++++++------ src/Basal/IFox.Basal.Shared/CLS/ValueTuple.cs | 10 ++++++++- tests/TestConsole/Program.cs | 12 +++++++++++ tests/TestConsole/RuntimeHelpers.cs | 2 +- tests/TestConsole/TestConsole.csproj | 7 +++++-- 7 files changed, 55 insertions(+), 14 deletions(-) diff --git a/src/Basal/IFox.Basal.Shared/CLS/Index.cs b/src/Basal/IFox.Basal.Shared/CLS/Index.cs index a588dc6..f93c81b 100644 --- a/src/Basal/IFox.Basal.Shared/CLS/Index.cs +++ b/src/Basal/IFox.Basal.Shared/CLS/Index.cs @@ -3,9 +3,11 @@ // See the LICENSE file in the project root for more information. namespace System; +#if !NOINDEX +#if NET45 using System.Runtime.CompilerServices; - +#endif /// Represent a type can be used to index a collection either from the start or the end. /// /// Index is used by the C# compiler to support the new index syntax @@ -144,4 +146,6 @@ public override string ToString() return ((uint)Value).ToString(); } -} \ No newline at end of file +} + +#endif \ No newline at end of file diff --git a/src/Basal/IFox.Basal.Shared/CLS/Range.cs b/src/Basal/IFox.Basal.Shared/CLS/Range.cs index 43dce54..3a32c39 100644 --- a/src/Basal/IFox.Basal.Shared/CLS/Range.cs +++ b/src/Basal/IFox.Basal.Shared/CLS/Range.cs @@ -4,8 +4,11 @@ namespace System; -using System.Runtime.CompilerServices; +#if !NORANGE +#if NET45 +using System.Runtime.CompilerServices; +#endif /// Represent a range has start and end indexes. /// @@ -99,4 +102,6 @@ public override string ToString() return (start, end - start); } -} \ No newline at end of file +} + +#endif \ No newline at end of file diff --git a/src/Basal/IFox.Basal.Shared/CLS/RuntimeHelpers.cs b/src/Basal/IFox.Basal.Shared/CLS/RuntimeHelpers.cs index f0cfa9c..60d48bb 100644 --- a/src/Basal/IFox.Basal.Shared/CLS/RuntimeHelpers.cs +++ b/src/Basal/IFox.Basal.Shared/CLS/RuntimeHelpers.cs @@ -1,14 +1,23 @@ -#if NET35 + // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. namespace System.Runtime.CompilerServices; -// 编译提示多个程序集中定义,屏蔽不了,但是不影响编译 -// #pragma warning disable CS1685 // 类型与导入类型冲突 -public static class RuntimeHelpers -// #pragma warning restore CS1685 // 类型与导入类型冲突 +/* + * 1.编译提示多个程序集中定义,屏蔽不了,但是不影响编译 + * 2.发现可以通过定义一个条件编译常量来进行屏蔽。 + * 3.普通包会提示编译器缺少GetSubArray函数,但是源码包不会。所以解决方案就是使用普通包的时候安装TA.System.Runtime.CompilerServices.RuntimeHelpers.GetSubArray nuget包来解决,此包为一个符号包,生成多余的dll + */ + + +#if !NORuntimeHelpers +/// +/// +/// +internal static class RuntimeHelpers + { /// /// Slices the specified array using the specified range. @@ -42,4 +51,4 @@ public static T[] GetSubArray(T[] array, Range range) } } } -#endif \ No newline at end of file +#endif diff --git a/src/Basal/IFox.Basal.Shared/CLS/ValueTuple.cs b/src/Basal/IFox.Basal.Shared/CLS/ValueTuple.cs index 568ddb9..52ca115 100644 --- a/src/Basal/IFox.Basal.Shared/CLS/ValueTuple.cs +++ b/src/Basal/IFox.Basal.Shared/CLS/ValueTuple.cs @@ -9,6 +9,7 @@ /* * 惊惊: * 首先是因为有人想要编译的时候只形成一个dll,然后把元组塞入IFox,同时也补充了NET35没有元组的遗憾. + * * 而利用nuget元组包必然会形成依赖地狱. * * 如果你的工程使用了nuget元组包,就造成了必须要剔除IFox. @@ -17,6 +18,11 @@ * 所以元组本身就是冲突的,需要把其他元组卸载掉,由IFox提供. */ +/* + * 1. 元组是net47之后提供的特性,所以net47之前的版本是都没有的 + * 2. 通过定义常量的办法将元组屏蔽 +*/ +#if !NOTUPLEVALUE #if NET35 namespace System.Collections { @@ -2137,4 +2143,6 @@ int ITupleInternal.Size } } } -} \ No newline at end of file +} + +#endif \ No newline at end of file diff --git a/tests/TestConsole/Program.cs b/tests/TestConsole/Program.cs index b596a5e..4a2995a 100644 --- a/tests/TestConsole/Program.cs +++ b/tests/TestConsole/Program.cs @@ -16,6 +16,7 @@ class Program { static void Main(string[] args) { +#if false int nResult = AddTwoNumbers(10, 20); Console.WriteLine(nResult); @@ -27,8 +28,19 @@ static void Main(string[] args) a.ForEach(a => { Console.WriteLine(a); }); +#endif + + var aa = new int[] { 1, 2, 3, 4, 5, 6, 78, 9, 92, }; + Console.WriteLine(aa[1..2]); + } + private static int addtuple((int ,int ) b) + { + return b.Item1 + b.Item2; + } + + [DebuggerHidden] private static int AddTwoNumbers(int nNum1, int nNum2) { diff --git a/tests/TestConsole/RuntimeHelpers.cs b/tests/TestConsole/RuntimeHelpers.cs index fb91a15..c3fef85 100644 --- a/tests/TestConsole/RuntimeHelpers.cs +++ b/tests/TestConsole/RuntimeHelpers.cs @@ -3,7 +3,7 @@ // See the LICENSE file in the project root for more information. // 如果要用range的语法比如 a[1..3],那么需要将本文件复制到你的项目里 -#if true +#if false namespace System.Runtime.CompilerServices { internal static class RuntimeHelpers diff --git a/tests/TestConsole/TestConsole.csproj b/tests/TestConsole/TestConsole.csproj index d79b76c..8a7ccda 100644 --- a/tests/TestConsole/TestConsole.csproj +++ b/tests/TestConsole/TestConsole.csproj @@ -5,13 +5,16 @@ enable Exe - NET45 + NET47 enable preview - + + all + runtime; build; native; contentfiles; analyzers; buildtransitive + -- Gitee From b6e22c687436836dcc0d7d7c123085779870eef3 Mon Sep 17 00:00:00 2001 From: vicwjb Date: Mon, 9 Jan 2023 22:51:48 +0800 Subject: [PATCH 636/675] =?UTF-8?q?timer=E7=B1=BB=E5=A2=9E=E5=8A=A0?= =?UTF-8?q?=E6=B3=A8=E9=87=8A?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/Basal/IFox.Basal.Shared/General/Timer.cs | 33 +++++++++++++++++--- src/Basal/IFox.Basal/GlobalUsings.cs | 2 ++ 2 files changed, 30 insertions(+), 5 deletions(-) diff --git a/src/Basal/IFox.Basal.Shared/General/Timer.cs b/src/Basal/IFox.Basal.Shared/General/Timer.cs index 3eee06c..d489cb5 100644 --- a/src/Basal/IFox.Basal.Shared/General/Timer.cs +++ b/src/Basal/IFox.Basal.Shared/General/Timer.cs @@ -12,11 +12,14 @@ Console.WriteLine("运行毫秒:" + stopwatch.ElapsedMilliseconds); */ -using System.ComponentModel; -using System.Runtime.InteropServices; - +/// +/// 时间定时类 +/// public class Timer { + /// + /// 时间单位枚举 + /// [Flags] public enum TimeEnum { @@ -54,7 +57,10 @@ public enum TimeEnum long _startTime, _stopTime; readonly long _freq; - + /// + /// 构造函数 + /// + /// public Timer() { _startTime = 0; @@ -84,11 +90,28 @@ public void Stop() double _Second = 0; // 返回计时器经过时间 + /// + /// 秒 + /// public double Second => _Second; + /// + /// 毫秒 + /// public double Millisecond => _Second * 1000.0; + /// + /// 微秒 + /// public double Microsecond => _Second * 1000000.0; + /// + /// 纳秒 + /// public double Nanosecond => _Second * 1000000000.0; - + /// + /// 计算执行委托的时间 + /// + /// 要执行的委托 + /// 时间单位 + /// 执行委托的时间 public static double RunTime(Action action, TimeEnum timeEnum = TimeEnum.Millisecond) { diff --git a/src/Basal/IFox.Basal/GlobalUsings.cs b/src/Basal/IFox.Basal/GlobalUsings.cs index 15c8a97..9fac544 100644 --- a/src/Basal/IFox.Basal/GlobalUsings.cs +++ b/src/Basal/IFox.Basal/GlobalUsings.cs @@ -6,4 +6,6 @@ global using System.Linq; global using System.Text; global using System.Reflection; +global using System.ComponentModel; +global using System.Runtime.InteropServices; -- Gitee From 586bf7066d01ad2cfdb18e20b204997f7ec5e6d2 Mon Sep 17 00:00:00 2001 From: vicwjb Date: Mon, 9 Jan 2023 23:50:49 +0800 Subject: [PATCH 637/675] =?UTF-8?q?=E5=B0=9D=E8=AF=95=E6=99=AE=E9=80=9A?= =?UTF-8?q?=E5=8C=85=E8=A7=A3=E5=86=B3index=EF=BC=8Crange=EF=BC=8Cvaluetup?= =?UTF-8?q?le=E7=B1=BB=E5=BA=93=E5=86=B2=E7=AA=81=EF=BC=8C=E5=A4=B1?= =?UTF-8?q?=E8=B4=A5=EF=BC=81=EF=BC=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/Basal/Directory.Build.props | 2 +- src/Basal/IFox.Basal.Shared/CLS/Index.cs | 4 ++-- src/Basal/IFox.Basal.Shared/CLS/Range.cs | 4 ++-- src/Basal/IFox.Basal.Shared/CLS/RuntimeHelpers.cs | 2 +- src/Basal/IFox.Basal.Shared/CLS/ValueTuple.cs | 10 +++++++--- tests/TestConsole/Program.cs | 8 +++++++- tests/TestConsole/RuntimeHelpers.cs | 2 +- tests/TestConsole/TestConsole.csproj | 9 ++++----- 8 files changed, 25 insertions(+), 16 deletions(-) diff --git a/src/Basal/Directory.Build.props b/src/Basal/Directory.Build.props index 10a4b4e..d247f3c 100644 --- a/src/Basal/Directory.Build.props +++ b/src/Basal/Directory.Build.props @@ -1,7 +1,7 @@  - 0.5.0-alpha4 + 0.5.0-alpha6 支持源码包。 diff --git a/src/Basal/IFox.Basal.Shared/CLS/Index.cs b/src/Basal/IFox.Basal.Shared/CLS/Index.cs index f93c81b..b84f4df 100644 --- a/src/Basal/IFox.Basal.Shared/CLS/Index.cs +++ b/src/Basal/IFox.Basal.Shared/CLS/Index.cs @@ -1,9 +1,9 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. - -namespace System; #if !NOINDEX +namespace System; + #if NET45 using System.Runtime.CompilerServices; diff --git a/src/Basal/IFox.Basal.Shared/CLS/Range.cs b/src/Basal/IFox.Basal.Shared/CLS/Range.cs index 3a32c39..0318216 100644 --- a/src/Basal/IFox.Basal.Shared/CLS/Range.cs +++ b/src/Basal/IFox.Basal.Shared/CLS/Range.cs @@ -1,10 +1,10 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. - +#if !NORANGE namespace System; -#if !NORANGE + #if NET45 using System.Runtime.CompilerServices; diff --git a/src/Basal/IFox.Basal.Shared/CLS/RuntimeHelpers.cs b/src/Basal/IFox.Basal.Shared/CLS/RuntimeHelpers.cs index 60d48bb..9e9cfe4 100644 --- a/src/Basal/IFox.Basal.Shared/CLS/RuntimeHelpers.cs +++ b/src/Basal/IFox.Basal.Shared/CLS/RuntimeHelpers.cs @@ -8,7 +8,7 @@ namespace System.Runtime.CompilerServices; /* * 1.编译提示多个程序集中定义,屏蔽不了,但是不影响编译 * 2.发现可以通过定义一个条件编译常量来进行屏蔽。 - * 3.普通包会提示编译器缺少GetSubArray函数,但是源码包不会。所以解决方案就是使用普通包的时候安装TA.System.Runtime.CompilerServices.RuntimeHelpers.GetSubArray nuget包来解决,此包为一个符号包,生成多余的dll + * 3.普通包会提示编译器缺少GetSubArray函数,但是源码包不会。所以解决方案就是使用普通包的时候安装TA.System.Runtime.CompilerServices.RuntimeHelpers.GetSubArray nuget包来解决,此包为一个符号包,不会生成多余的dll */ diff --git a/src/Basal/IFox.Basal.Shared/CLS/ValueTuple.cs b/src/Basal/IFox.Basal.Shared/CLS/ValueTuple.cs index 52ca115..c209024 100644 --- a/src/Basal/IFox.Basal.Shared/CLS/ValueTuple.cs +++ b/src/Basal/IFox.Basal.Shared/CLS/ValueTuple.cs @@ -2,7 +2,7 @@ // The .NET Foundation licenses this file to you under the MIT license. // #pragma warning disable SA1141 // explicitly not using tuple syntax in tuple implementation - +#if !NOVALUETUPLE using System.Diagnostics; using System.Numerics.Hashing; @@ -20,9 +20,11 @@ /* * 1. 元组是net47之后提供的特性,所以net47之前的版本是都没有的 - * 2. 通过定义常量的办法将元组屏蔽 + * 2. 通过定义常量的办法将元组屏蔽,很奇怪的是源码包可以,但是普通包不行,所以推荐使用源码包 index和range的情况类似 + * 这种问题就没有很好的解决方式,因为用了ifox就不能用其他的index,range,valuetuple类库,但是其他的三方库却依赖 + * 这些类库,所以还是使用源码包吧。 */ -#if !NOTUPLEVALUE + #if NET35 namespace System.Collections { @@ -67,6 +69,7 @@ public static int Combine(int h1, int h2) namespace System { + // internal static class SR internal sealed partial class SR { @@ -2143,6 +2146,7 @@ int ITupleInternal.Size } } } + } #endif \ No newline at end of file diff --git a/tests/TestConsole/Program.cs b/tests/TestConsole/Program.cs index 4a2995a..514c252 100644 --- a/tests/TestConsole/Program.cs +++ b/tests/TestConsole/Program.cs @@ -31,8 +31,14 @@ static void Main(string[] args) #endif var aa = new int[] { 1, 2, 3, 4, 5, 6, 78, 9, 92, }; - Console.WriteLine(aa[1..2]); + Console.WriteLine(aa[1..^2]); + + var time = Timer.RunTime(() => { + for (int i = 0; i < 10000000; i++) + i++; + }, Timer.TimeEnum.Second); + Console.WriteLine($"代码执行的时间:{time}"); } private static int addtuple((int ,int ) b) diff --git a/tests/TestConsole/RuntimeHelpers.cs b/tests/TestConsole/RuntimeHelpers.cs index c3fef85..fb91a15 100644 --- a/tests/TestConsole/RuntimeHelpers.cs +++ b/tests/TestConsole/RuntimeHelpers.cs @@ -3,7 +3,7 @@ // See the LICENSE file in the project root for more information. // 如果要用range的语法比如 a[1..3],那么需要将本文件复制到你的项目里 -#if false +#if true namespace System.Runtime.CompilerServices { internal static class RuntimeHelpers diff --git a/tests/TestConsole/TestConsole.csproj b/tests/TestConsole/TestConsole.csproj index 8a7ccda..df0548a 100644 --- a/tests/TestConsole/TestConsole.csproj +++ b/tests/TestConsole/TestConsole.csproj @@ -5,16 +5,15 @@ enable Exe - NET47 + NET40 enable preview + NOINDEX;NORANGE;NOVALUETUPLE - - all - runtime; build; native; contentfiles; analyzers; buildtransitive - + + -- Gitee From 70bf14a2800fdb777196a963df5521f6a5716549 Mon Sep 17 00:00:00 2001 From: vicwjb Date: Tue, 10 Jan 2023 22:56:23 +0800 Subject: [PATCH 638/675] =?UTF-8?q?=E6=8B=86=E5=88=86EntityEx=E6=89=A9?= =?UTF-8?q?=E5=B1=95=E7=B1=BB=EF=BC=8Cfixed=20#I68WMS?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../ExtensionMethod/Entity/ArcEx.cs | 73 ++++ .../BlockReferenceEx.cs} | 337 +----------------- .../ExtensionMethod/Entity/CircleEx.cs | 59 +++ .../ExtensionMethod/Entity/DBTextEx.cs | 22 ++ .../{ => Entity}/EntityBoundingInfo.cs | 0 .../ExtensionMethod/Entity/EntityEx.cs | 123 +++++++ .../ExtensionMethod/Entity/MTextEx.cs | 45 +++ .../ExtensionMethod/Entity/PolylineEx.cs | 55 +++ .../IFox.CAD.Shared/IFox.CAD.Shared.projitems | 10 +- 9 files changed, 388 insertions(+), 336 deletions(-) create mode 100644 src/CAD/IFox.CAD.Shared/ExtensionMethod/Entity/ArcEx.cs rename src/CAD/IFox.CAD.Shared/ExtensionMethod/{EntityEx.cs => Entity/BlockReferenceEx.cs} (30%) create mode 100644 src/CAD/IFox.CAD.Shared/ExtensionMethod/Entity/CircleEx.cs create mode 100644 src/CAD/IFox.CAD.Shared/ExtensionMethod/Entity/DBTextEx.cs rename src/CAD/IFox.CAD.Shared/ExtensionMethod/{ => Entity}/EntityBoundingInfo.cs (100%) create mode 100644 src/CAD/IFox.CAD.Shared/ExtensionMethod/Entity/EntityEx.cs create mode 100644 src/CAD/IFox.CAD.Shared/ExtensionMethod/Entity/MTextEx.cs create mode 100644 src/CAD/IFox.CAD.Shared/ExtensionMethod/Entity/PolylineEx.cs diff --git a/src/CAD/IFox.CAD.Shared/ExtensionMethod/Entity/ArcEx.cs b/src/CAD/IFox.CAD.Shared/ExtensionMethod/Entity/ArcEx.cs new file mode 100644 index 0000000..f3ef950 --- /dev/null +++ b/src/CAD/IFox.CAD.Shared/ExtensionMethod/Entity/ArcEx.cs @@ -0,0 +1,73 @@ +namespace IFoxCAD.Cad; + +/// +/// 圆弧扩展类 +/// +public static class ArcEx +{ + #region 圆弧 + + /// + /// 根据圆心、起点、终点来创建圆弧(二维) + /// + /// 起点 + /// 圆心 + /// 终点 + /// 圆弧 + public static Arc CreateArcSCE(Point3d startPoint, Point3d centerPoint, Point3d endPoint) + { + Arc arc = new(); + arc.SetDatabaseDefaults(); + arc.Center = centerPoint; + arc.Radius = centerPoint.DistanceTo(startPoint); + Vector2d startVector = new(startPoint.X - centerPoint.X, startPoint.Y - centerPoint.Y); + Vector2d endVector = new(endPoint.X - centerPoint.X, endPoint.Y - centerPoint.Y); + // 计算起始和终止角度 + arc.StartAngle = startVector.Angle; + arc.EndAngle = endVector.Angle; + return arc; + } + /// + /// 三点法创建圆弧(二维) + /// + /// 起点 + /// 圆弧上的点 + /// 终点 + /// 圆弧 + public static Arc CreateArc(Point3d startPoint, Point3d pointOnArc, Point3d endPoint) + { + // 创建一个几何类的圆弧对象 + CircularArc3d geArc = new(startPoint, pointOnArc, endPoint); + // 将几何类圆弧对象的圆心和半径赋值给圆弧 +#if !gcad +#if NET35 + return (Arc)geArc.ToCurve(); +#else + return (Arc)Curve.CreateFromGeCurve(geArc); +#endif +#else + return (Arc)geArc.ToCurve(); +#endif + } + + /// + /// 根据起点、圆心和圆弧角度创建圆弧(二维) + /// + /// 起点 + /// 圆心 + /// 圆弧角度 + /// 圆弧 + public static Arc CreateArc(Point3d startPoint, Point3d centerPoint, double angle) + { + Arc arc = new(); + arc.SetDatabaseDefaults(); + arc.Center = centerPoint; + arc.Radius = centerPoint.DistanceTo(startPoint); + Vector2d startVector = new(startPoint.X - centerPoint.X, startPoint.Y - centerPoint.Y); + arc.StartAngle = startVector.Angle; + arc.EndAngle = startVector.Angle + angle; + return arc; + } + + #endregion +} \ No newline at end of file diff --git a/src/CAD/IFox.CAD.Shared/ExtensionMethod/EntityEx.cs b/src/CAD/IFox.CAD.Shared/ExtensionMethod/Entity/BlockReferenceEx.cs similarity index 30% rename from src/CAD/IFox.CAD.Shared/ExtensionMethod/EntityEx.cs rename to src/CAD/IFox.CAD.Shared/ExtensionMethod/Entity/BlockReferenceEx.cs index d839ba8..103cecc 100644 --- a/src/CAD/IFox.CAD.Shared/ExtensionMethod/EntityEx.cs +++ b/src/CAD/IFox.CAD.Shared/ExtensionMethod/Entity/BlockReferenceEx.cs @@ -1,324 +1,10 @@ namespace IFoxCAD.Cad; - /// -/// 实体图元类扩展函数 +/// 块参照扩展类 /// -public static class EntityEx +public static class BlockReferenceEx { - #region 多段线端点坐标 - /// - /// 获取二维多段线的端点坐标 - /// - /// 二维多段线 - /// 事务 - /// 端点坐标集合 - public static IEnumerable GetPoints(this Polyline2d pl2d, DBTrans? trans = null) - { - trans ??= DBTrans.Top; - foreach (ObjectId id in pl2d) - yield return trans.GetObject(id)!.Position; - } - - /// - /// 获取三维多段线的端点坐标 - /// - /// 三维多段线 - /// 事务 - /// 端点坐标集合 - public static IEnumerable GetPoints(this Polyline3d pl3d, DBTrans? trans = null) - { - trans ??= DBTrans.Top; - foreach (ObjectId id in pl3d) - yield return trans.GetObject(id, OpenMode.ForRead)!.Position; - } - - /// - /// 获取多段线的端点坐标 - /// - /// 多段线 - /// 端点坐标集合 - public static IEnumerable GetPoints(this Polyline pl) - { - return - Enumerable - .Range(0, pl.NumberOfVertices) - .Select(pl.GetPoint3dAt); - } - #endregion - - #region 实体线性变换 - - /// - /// 移动实体 - /// - /// 实体 - /// 基点 - /// 目标点 - public static void Move(this Entity ent, Point3d from, Point3d to) - { - ent.TransformBy(Matrix3d.Displacement(to - from)); - } - - /// - /// 缩放实体 - /// - /// 实体 - /// 缩放基点坐标 - /// 缩放比例 - public static void Scale(this Entity ent, Point3d center, double scaleValue) - { - ent.TransformBy(Matrix3d.Scaling(scaleValue, center)); - } - - /// - /// 旋转实体 - /// - /// 实体 - /// 旋转中心 - /// 转角,弧度制,正数为顺时针 - /// 旋转平面的法向矢量 - public static void Rotation(this Entity ent, Point3d center, double angle, Vector3d normal) - { - ent.TransformBy(Matrix3d.Rotation(angle, normal, center)); - } - - /// - /// 在XY平面内旋转实体 - /// - /// 实体 - /// 旋转中心 - /// 转角,弧度制,正数为顺时针 - public static void Rotation(this Entity ent, Point3d center, double angle) - { - ent.TransformBy(Matrix3d.Rotation(angle, Vector3d.ZAxis.TransformBy(ent.Ecs), center)); - } - - /// - /// 按对称轴镜像实体 - /// - /// 实体 - /// 对称轴起点 - /// 对称轴终点 - public static void Mirror(this Entity ent, Point3d startPoint, Point3d endPoint) - { - ent.TransformBy(Matrix3d.Mirroring(new Line3d(startPoint, endPoint))); - } - - /// - /// 按对称面镜像实体 - /// - /// 实体 - /// 对称平面 - public static void Mirror(this Entity ent, Plane plane) - { - ent.TransformBy(Matrix3d.Mirroring(plane)); - } - - /// - /// 按对称点镜像实体 - /// - /// 实体 - /// 对称点 - public static void Mirror(this Entity ent, Point3d basePoint) - { - ent.TransformBy(Matrix3d.Mirroring(basePoint)); - } - - #endregion - - #region 实体范围 - /// - /// 获取实体集合的范围 - /// - /// 实体迭代器 - /// 实体集合的范围 - public static Extents3d GetExtents(this IEnumerable ents) - { - var ext = new Extents3d(); - foreach (var item in ents) - { - ext.AddExtents(item.GeometricExtents); - } - return ext; - } - #endregion - - #region 单行文字 - - /// - /// 更正单行文字的镜像属性 - /// - /// 单行文字 - public static void ValidateMirror(this DBText txt) - { - if (!txt.Database.Mirrtext) - { - txt.IsMirroredInX = false; - txt.IsMirroredInY = false; - } - } - #endregion - - #region 多行文字 - - /// - /// 炸散多行文字 - /// - /// 存储多行文字炸散之后的对象的类型 - /// 多行文字 - /// 存储对象变量 - /// 回调函数,用于处理炸散之后的对象 - /// - /// 多行文字炸散后的对象
    - /// 回调函数处理的结果 - ///
    - /// - public static void ExplodeFragments(this MText mt, T obj, Func mTextFragmentCallback) - { - mt.ExplodeFragments((f, o) => mTextFragmentCallback(f, (T)o), obj); - } - - /// - /// 获取多行文字的无格式文本 - /// - /// 多行文字 - /// 文本 - public static string GetUnFormatString(this MText mt) - { - List strs = new(); - mt.ExplodeFragments( - strs, - (f, o) => { - o.Add(f.Text); - return MTextFragmentCallbackStatus.Continue; - }); - return string.Join("", strs.ToArray()); - } - #endregion - - #region 圆弧 - - /// - /// 根据圆心、起点、终点来创建圆弧(二维) - /// - /// 起点 - /// 圆心 - /// 终点 - /// 圆弧 - public static Arc CreateArcSCE(Point3d startPoint, Point3d centerPoint, Point3d endPoint) - { - Arc arc = new(); - arc.SetDatabaseDefaults(); - arc.Center = centerPoint; - arc.Radius = centerPoint.DistanceTo(startPoint); - Vector2d startVector = new(startPoint.X - centerPoint.X, startPoint.Y - centerPoint.Y); - Vector2d endVector = new(endPoint.X - centerPoint.X, endPoint.Y - centerPoint.Y); - // 计算起始和终止角度 - arc.StartAngle = startVector.Angle; - arc.EndAngle = endVector.Angle; - return arc; - } - /// - /// 三点法创建圆弧(二维) - /// - /// 圆弧对象 - /// 起点 - /// 圆弧上的点 - /// 终点 - /// 圆弧 - public static Arc CreateArc(Point3d startPoint, Point3d pointOnArc, Point3d endPoint) - { - // 创建一个几何类的圆弧对象 - CircularArc3d geArc = new(startPoint, pointOnArc, endPoint); - // 将几何类圆弧对象的圆心和半径赋值给圆弧 -#if !gcad -#if NET35 - return (Arc)geArc.ToCurve(); -#else - return (Arc)Curve.CreateFromGeCurve(geArc); -#endif -#else - return (Arc)geArc.ToCurve(); -#endif - } - - /// - /// 根据起点、圆心和圆弧角度创建圆弧(二维) - /// - /// 圆弧对象 - /// 起点 - /// 圆心 - /// 圆弧角度 - /// 圆弧 - public static Arc CreateArc(Point3d startPoint, Point3d centerPoint, double angle) - { - Arc arc = new(); - arc.SetDatabaseDefaults(); - arc.Center = centerPoint; - arc.Radius = centerPoint.DistanceTo(startPoint); - Vector2d startVector = new(startPoint.X - centerPoint.X, startPoint.Y - centerPoint.Y); - arc.StartAngle = startVector.Angle; - arc.EndAngle = startVector.Angle + angle; - return arc; - } - - #endregion - - #region 圆 - - /// - /// 两点创建圆(两点中点为圆心) - /// - /// 起点 - /// 终点 - /// - public static Circle CreateCircle(Point3d startPoint, Point3d endPoint) - { - Circle circle = new(); - circle.SetDatabaseDefaults(); - circle.Center = startPoint.GetMidPointTo(endPoint); - circle.Radius = startPoint.DistanceTo(endPoint) * 0.5; - return circle; - } - - /// - /// 三点法创建圆(失败则返回Null) - /// - /// 第一点 - /// 第二点 - /// 第三点 - /// - public static Circle? CreateCircle(Point3d pt1, Point3d pt2, Point3d pt3) - { - // 先判断三点是否共线,得到pt1点指向pt2、pt2点的矢量 - Vector3d va = pt1.GetVectorTo(pt2); - Vector3d vb = pt1.GetVectorTo(pt3); - // 如两矢量夹角为0或180度(π弧度),则三点共线. - if (va.GetAngleTo(vb) == 0 | va.GetAngleTo(vb) == Math.PI) - return null; - - // 创建一个几何类的圆弧对象 - CircularArc3d geArc = new(pt1, pt2, pt3); - geArc.ToCircle(); - return geArc.ToCircle(); - } - - /// - /// 通过圆心,半径绘制圆形 - /// - /// 圆心 - /// 半径 - /// 图形的ObjectId - public static Circle? CreateCircle(Point3d center, double radius, double vex = 0, double vey = 0, double vez = 1) - { - return new Circle(center, new Vector3d(vex, vey, vez), radius);// 平面法向量XY方向 - } - - #endregion - - #region 块参照 - #region 裁剪块参照 private const string filterDictName = "ACAD_FILTER"; @@ -334,8 +20,7 @@ public static void ClipBlockRef(this BlockReference bref, IEnumerable p Matrix3d mat = bref.BlockTransform.Inverse(); var pts = pt3ds - .Select(p => p.TransformBy(mat)) - .Select(p => new Point2d(p.X, p.Y)) + .Select(p => p.TransformBy(mat).Point2d()) .ToCollection(); SpatialFilterDefinition sfd = new(pts, Vector3d.ZAxis, 0.0, 0.0, 0.0, true); @@ -398,19 +83,6 @@ public static void ChangeBlockProperty(this BlockReference blockReference, } #endregion - /// - /// 获取图元包围盒 - /// - /// - /// 包围盒信息 - /// 异常: - /// 会将包围盒类型记录到所属路径中,以此查询 - public static BoundingInfo GetBoundingBoxEx(this Entity ent) - { - return EntityBoundingInfo.GetBoundingInfo(ent); - } - -#line hidden /// /// 遍历块内 /// @@ -468,7 +140,4 @@ public static void ForEach(this BlockReference brf, Action +/// 圆扩展类 +///
    +public static class CircleEx +{ + + /// + /// 两点创建圆(两点中点为圆心) + /// + /// 起点 + /// 终点 + /// + public static Circle CreateCircle(Point3d startPoint, Point3d endPoint) + { + Circle circle = new(); + circle.SetDatabaseDefaults(); + circle.Center = startPoint.GetMidPointTo(endPoint); + circle.Radius = startPoint.DistanceTo(endPoint) * 0.5; + return circle; + } + + /// + /// 三点法创建圆(失败则返回Null) + /// + /// 第一点 + /// 第二点 + /// 第三点 + /// + public static Circle? CreateCircle(Point3d pt1, Point3d pt2, Point3d pt3) + { + // 先判断三点是否共线,得到pt1点指向pt2、pt2点的矢量 + Vector3d va = pt1.GetVectorTo(pt2); + Vector3d vb = pt1.GetVectorTo(pt3); + // 如两矢量夹角为0或180度(π弧度),则三点共线. + if (va.GetAngleTo(vb) == 0 | va.GetAngleTo(vb) == Math.PI) + return null; + + // 创建一个几何类的圆弧对象 + CircularArc3d geArc = new(pt1, pt2, pt3); + return geArc.ToCircle(); + } + + /// + /// 通过圆心,半径绘制圆形 + /// + /// 圆心 + /// 半径 + /// 法向量的X + /// 法向量的Y + /// 法向量的Z + /// + public static Circle? CreateCircle(Point3d center, double radius, double vex = 0, double vey = 0, double vez = 1) + { + return new Circle(center, new Vector3d(vex, vey, vez), radius);// 平面法向量XY方向 + } + +} \ No newline at end of file diff --git a/src/CAD/IFox.CAD.Shared/ExtensionMethod/Entity/DBTextEx.cs b/src/CAD/IFox.CAD.Shared/ExtensionMethod/Entity/DBTextEx.cs new file mode 100644 index 0000000..5e01e58 --- /dev/null +++ b/src/CAD/IFox.CAD.Shared/ExtensionMethod/Entity/DBTextEx.cs @@ -0,0 +1,22 @@ +namespace IFoxCAD.Cad; + +/// +/// 单行文字扩展类 +/// +public static class DBTextEx +{ + + /// + /// 更正单行文字的镜像属性 + /// + /// 单行文字 + public static void ValidateMirror(this DBText txt) + { + if (!txt.Database.Mirrtext) + { + txt.IsMirroredInX = false; + txt.IsMirroredInY = false; + } + } + +} \ No newline at end of file diff --git a/src/CAD/IFox.CAD.Shared/ExtensionMethod/EntityBoundingInfo.cs b/src/CAD/IFox.CAD.Shared/ExtensionMethod/Entity/EntityBoundingInfo.cs similarity index 100% rename from src/CAD/IFox.CAD.Shared/ExtensionMethod/EntityBoundingInfo.cs rename to src/CAD/IFox.CAD.Shared/ExtensionMethod/Entity/EntityBoundingInfo.cs diff --git a/src/CAD/IFox.CAD.Shared/ExtensionMethod/Entity/EntityEx.cs b/src/CAD/IFox.CAD.Shared/ExtensionMethod/Entity/EntityEx.cs new file mode 100644 index 0000000..65f7ee4 --- /dev/null +++ b/src/CAD/IFox.CAD.Shared/ExtensionMethod/Entity/EntityEx.cs @@ -0,0 +1,123 @@ +namespace IFoxCAD.Cad; + + +/// +/// 实体图元扩展类 +/// +public static class EntityEx +{ + + + #region 实体线性变换 + + /// + /// 移动实体 + /// + /// 实体 + /// 基点 + /// 目标点 + public static void Move(this Entity ent, Point3d from, Point3d to) + { + ent.TransformBy(Matrix3d.Displacement(to - from)); + } + + /// + /// 缩放实体 + /// + /// 实体 + /// 缩放基点坐标 + /// 缩放比例 + public static void Scale(this Entity ent, Point3d center, double scaleValue) + { + ent.TransformBy(Matrix3d.Scaling(scaleValue, center)); + } + + /// + /// 旋转实体 + /// + /// 实体 + /// 旋转中心 + /// 转角,弧度制,正数为顺时针 + /// 旋转平面的法向矢量 + public static void Rotation(this Entity ent, Point3d center, double angle, Vector3d normal) + { + ent.TransformBy(Matrix3d.Rotation(angle, normal, center)); + } + + /// + /// 在XY平面内旋转实体 + /// + /// 实体 + /// 旋转中心 + /// 转角,弧度制,正数为顺时针 + public static void Rotation(this Entity ent, Point3d center, double angle) + { + ent.TransformBy(Matrix3d.Rotation(angle, Vector3d.ZAxis.TransformBy(ent.Ecs), center)); + } + + /// + /// 按对称轴镜像实体 + /// + /// 实体 + /// 对称轴起点 + /// 对称轴终点 + public static void Mirror(this Entity ent, Point3d startPoint, Point3d endPoint) + { + ent.TransformBy(Matrix3d.Mirroring(new Line3d(startPoint, endPoint))); + } + + /// + /// 按对称面镜像实体 + /// + /// 实体 + /// 对称平面 + public static void Mirror(this Entity ent, Plane plane) + { + ent.TransformBy(Matrix3d.Mirroring(plane)); + } + + /// + /// 按对称点镜像实体 + /// + /// 实体 + /// 对称点 + public static void Mirror(this Entity ent, Point3d basePoint) + { + ent.TransformBy(Matrix3d.Mirroring(basePoint)); + } + + #endregion + + #region 实体范围 + /// + /// 获取实体集合的范围 + /// + /// 实体迭代器 + /// 实体集合的范围 + public static Extents3d GetExtents(this IEnumerable ents) + { + var ext = new Extents3d(); + foreach (var item in ents) + { + ext.AddExtents(item.GeometricExtents); + } + return ext; + } + #endregion + + + + /// + /// 获取图元包围盒 + /// + /// + /// 包围盒信息 + /// 异常: + /// 会将包围盒类型记录到所属路径中,以此查询 + public static BoundingInfo GetBoundingBoxEx(this Entity ent) + { + return EntityBoundingInfo.GetBoundingInfo(ent); + } + + +} \ No newline at end of file diff --git a/src/CAD/IFox.CAD.Shared/ExtensionMethod/Entity/MTextEx.cs b/src/CAD/IFox.CAD.Shared/ExtensionMethod/Entity/MTextEx.cs new file mode 100644 index 0000000..fd39bac --- /dev/null +++ b/src/CAD/IFox.CAD.Shared/ExtensionMethod/Entity/MTextEx.cs @@ -0,0 +1,45 @@ +namespace IFoxCAD.Cad; + +/// +/// 多行文字扩展类 +/// +public static class MTextEx +{ + + /// + /// 炸散多行文字 + /// + /// 存储多行文字炸散之后的对象的类型 + /// 多行文字 + /// 存储对象变量 + /// 回调函数,用于处理炸散之后的对象 + /// + /// 多行文字炸散后的对象
    + /// 回调函数处理的结果 + ///
    + /// + public static void ExplodeFragments(this MText mt, T obj, Func mTextFragmentCallback) + { + mt.ExplodeFragments( + (f, o) => + mTextFragmentCallback(f, (T)o), obj); + } + + /// + /// 获取多行文字的无格式文本 + /// + /// 多行文字 + /// 文本 + public static string GetUnFormatString(this MText mt) + { + List strs = new(); + mt.ExplodeFragments( + strs, + (f, o) => { + o.Add(f.Text); + return MTextFragmentCallbackStatus.Continue; + }); + return string.Join("", strs.ToArray()); + } + +} \ No newline at end of file diff --git a/src/CAD/IFox.CAD.Shared/ExtensionMethod/Entity/PolylineEx.cs b/src/CAD/IFox.CAD.Shared/ExtensionMethod/Entity/PolylineEx.cs new file mode 100644 index 0000000..02681b4 --- /dev/null +++ b/src/CAD/IFox.CAD.Shared/ExtensionMethod/Entity/PolylineEx.cs @@ -0,0 +1,55 @@ +namespace IFoxCAD.Cad; + +/// +/// 多段线扩展类 +/// +public static class PolylineEx +{ + /// + /// 获取二维多段线的端点坐标 + /// + /// 二维多段线 + /// 事务 + /// 端点坐标集合 + public static IEnumerable GetPoints(this Polyline2d pl2d, DBTrans? trans = null) + { + trans ??= DBTrans.Top; + foreach (ObjectId id in pl2d) + { + var vertex = trans.GetObject(id); + if (vertex != null) + yield return vertex.Position; + } + + } + + /// + /// 获取三维多段线的端点坐标 + /// + /// 三维多段线 + /// 事务 + /// 端点坐标集合 + public static IEnumerable GetPoints(this Polyline3d pl3d, DBTrans? trans = null) + { + trans ??= DBTrans.Top; + foreach (ObjectId id in pl3d) + { + var vertex = trans.GetObject(id); + if (vertex != null) + yield return vertex.Position; + } + } + + /// + /// 获取多段线的端点坐标 + /// + /// 多段线 + /// 端点坐标集合 + public static IEnumerable GetPoints(this Polyline pl) + { + return + Enumerable + .Range(0, pl.NumberOfVertices) + .Select(pl.GetPoint3dAt); + } +} \ No newline at end of file diff --git a/src/CAD/IFox.CAD.Shared/IFox.CAD.Shared.projitems b/src/CAD/IFox.CAD.Shared/IFox.CAD.Shared.projitems index 6974a96..ecb9c9a 100644 --- a/src/CAD/IFox.CAD.Shared/IFox.CAD.Shared.projitems +++ b/src/CAD/IFox.CAD.Shared/IFox.CAD.Shared.projitems @@ -29,8 +29,14 @@ - - + + + + + + + + -- Gitee From e2bd94f07824e06eef612e5cef8d3a301851223f Mon Sep 17 00:00:00 2001 From: vicwjb Date: Tue, 10 Jan 2023 23:32:26 +0800 Subject: [PATCH 639/675] =?UTF-8?q?=E4=BF=AE=E5=A4=8D=E5=9B=A0=E6=8B=86?= =?UTF-8?q?=E5=88=86EntityEx=E7=B1=BB=E5=AF=BC=E8=87=B4=E7=9A=84=E5=BC=95?= =?UTF-8?q?=E7=94=A8=E9=94=99=E8=AF=AF?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../ExtensionMethod/SymbolTableRecordEx.cs | 4 ++-- tests/TestShared/TestAddEntity.cs | 12 ++++++------ tests/TestShared/TestEnv.cs | 9 +++++++++ tests/TestShared/TestQuadTree.cs | 2 +- 4 files changed, 18 insertions(+), 9 deletions(-) diff --git a/src/CAD/IFox.CAD.Shared/ExtensionMethod/SymbolTableRecordEx.cs b/src/CAD/IFox.CAD.Shared/ExtensionMethod/SymbolTableRecordEx.cs index ecd1e31..f2ddec2 100644 --- a/src/CAD/IFox.CAD.Shared/ExtensionMethod/SymbolTableRecordEx.cs +++ b/src/CAD/IFox.CAD.Shared/ExtensionMethod/SymbolTableRecordEx.cs @@ -200,7 +200,7 @@ public static ObjectId AddCircle(this BlockTableRecord btr, Point3d center, doub public static ObjectId AddCircle(this BlockTableRecord btr, Point3d p0, Point3d p1, Point3d p2, Action? action = null, Transaction? trans = null) { - var circle = EntityEx.CreateCircle(p0, p1, p2); + var circle = CircleEx.CreateCircle(p0, p1, p2); // return circle is not null ? btr.AddEnt(circle, action, trans) : throw new ArgumentNullException(nameof(circle), "对象为 null"); if (circle is null) throw new ArgumentNullException(nameof(circle), "对象为 null"); @@ -306,7 +306,7 @@ public static ObjectId AddArc(this BlockTableRecord btr, Point3d startPoint, Point3d pointOnArc, Point3d endPoint, Action? action = null, Transaction? trans = null) { - var arc = EntityEx.CreateArc(startPoint, pointOnArc, endPoint); + var arc = ArcEx.CreateArc(startPoint, pointOnArc, endPoint); return btr.AddEnt(arc, action, trans); } #endregion diff --git a/tests/TestShared/TestAddEntity.cs b/tests/TestShared/TestAddEntity.cs index 3df018f..9b1dcd8 100644 --- a/tests/TestShared/TestAddEntity.cs +++ b/tests/TestShared/TestAddEntity.cs @@ -53,9 +53,9 @@ public void Test_Addent() public void Test_Drawarc() { using DBTrans tr = new(); - Arc arc1 = EntityEx.CreateArcSCE(new Point3d(2, 0, 0), new Point3d(0, 0, 0), new Point3d(0, 2, 0));// 起点,圆心,终点 - Arc arc2 = EntityEx.CreateArc(new Point3d(4, 0, 0), new Point3d(0, 0, 0), Math.PI / 2); // 起点,圆心,弧度 - Arc arc3 = EntityEx.CreateArc(new Point3d(1, 0, 0), new Point3d(0, 0, 0), new Point3d(0, 1, 0)); // 起点,圆上一点,终点 + Arc arc1 = ArcEx.CreateArcSCE(new Point3d(2, 0, 0), new Point3d(0, 0, 0), new Point3d(0, 2, 0));// 起点,圆心,终点 + Arc arc2 = ArcEx.CreateArc(new Point3d(4, 0, 0), new Point3d(0, 0, 0), Math.PI / 2); // 起点,圆心,弧度 + Arc arc3 = ArcEx.CreateArc(new Point3d(1, 0, 0), new Point3d(0, 0, 0), new Point3d(0, 1, 0)); // 起点,圆上一点,终点 tr.CurrentSpace.AddEntity(arc1, arc2, arc3); tr.CurrentSpace.AddArc(new Point3d(0, 0, 0), new Point3d(1, 1, 0), new Point3d(2, 0, 0));// 起点,圆上一点,终点 } @@ -64,9 +64,9 @@ public void Test_Drawarc() public void Test_DrawCircle() { using DBTrans tr = new(); - var circle1 = EntityEx.CreateCircle(new Point3d(0, 0, 0), new Point3d(1, 0, 0)); // 起点,终点 - var circle2 = EntityEx.CreateCircle(new Point3d(-2, 0, 0), new Point3d(2, 0, 0), new Point3d(0, 2, 0));// 三点画圆,成功 - var circle3 = EntityEx.CreateCircle(new Point3d(-2, 0, 0), new Point3d(0, 0, 0), new Point3d(2, 0, 0));// 起点,圆心,终点,失败 + var circle1 = CircleEx.CreateCircle(new Point3d(0, 0, 0), new Point3d(1, 0, 0)); // 起点,终点 + var circle2 = CircleEx.CreateCircle(new Point3d(-2, 0, 0), new Point3d(2, 0, 0), new Point3d(0, 2, 0));// 三点画圆,成功 + var circle3 = CircleEx.CreateCircle(new Point3d(-2, 0, 0), new Point3d(0, 0, 0), new Point3d(2, 0, 0));// 起点,圆心,终点,失败 tr.CurrentSpace.AddEntity(circle1, circle2!); if (circle3 is not null) tr.CurrentSpace.AddEntity(circle3); diff --git a/tests/TestShared/TestEnv.cs b/tests/TestShared/TestEnv.cs index dc3d3aa..8b9d2f2 100644 --- a/tests/TestShared/TestEnv.cs +++ b/tests/TestShared/TestEnv.cs @@ -17,8 +17,17 @@ public void Test_Enum1() public void Test_Dimblk() { Env.Dimblk = Env.DimblkType.Dot; + Env.Print(Env.Dimblk); + Env.Print(Env.GetDimblkId(Env.DimblkType.Dot)); Env.Dimblk = Env.DimblkType.Defult; + Env.Print(Env.Dimblk); + Env.Print(Env.GetDimblkId(Env.DimblkType.Defult)); Env.Dimblk = Env.DimblkType.Oblique; + Env.Print(Env.Dimblk); + Env.Print(Env.GetDimblkId(Env.DimblkType.Oblique)); + Env.Dimblk = Env.DimblkType.ArchTick; + Env.Print(Env.Dimblk); + Env.Print(Env.GetDimblkId(Env.DimblkType.ArchTick)); } [CommandMethod(nameof(Test_Dimblk1))] public void Test_Dimblk1() diff --git a/tests/TestShared/TestQuadTree.cs b/tests/TestShared/TestQuadTree.cs index 8e16117..74cd52d 100644 --- a/tests/TestShared/TestQuadTree.cs +++ b/tests/TestShared/TestQuadTree.cs @@ -139,7 +139,7 @@ public void Test_QuadTree() { var x = rand.Next(x1, x2) + rand.NextDouble(); var y = rand.Next(y1, y2) + rand.NextDouble(); - yield return EntityEx.CreateCircle(new Point3d(x, y, 0), rand.Next(1, 100)); // 起点,终点 + yield return CircleEx.CreateCircle(new Point3d(x, y, 0), rand.Next(1, 100)); // 起点,终点 } } -- Gitee From 06a56281289fbf00f9b83a7059c8d387f016b56a Mon Sep 17 00:00:00 2001 From: vicwjb Date: Wed, 11 Jan 2023 23:20:28 +0800 Subject: [PATCH 640/675] =?UTF-8?q?=E8=A7=A3=E5=86=B3=E5=85=85=E5=A1=AB?= =?UTF-8?q?=E5=87=BD=E6=95=B0=E7=9A=84=E9=94=99=E8=AF=AF=20fixed=20#I67MHY?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../HatchInfo.cs" | 41 ++++++++++++++++--- tests/TestShared/TestHatchinfo.cs | 18 ++++++++ tests/TestShared/TestShared.projitems | 1 + 3 files changed, 54 insertions(+), 6 deletions(-) create mode 100644 tests/TestShared/TestHatchinfo.cs diff --git "a/src/CAD/IFox.CAD.Shared/ExtensionMethod/\346\226\260\345\273\272\345\241\253\345\205\205/HatchInfo.cs" "b/src/CAD/IFox.CAD.Shared/ExtensionMethod/\346\226\260\345\273\272\345\241\253\345\205\205/HatchInfo.cs" index 9f4e90b..cf58f07 100644 --- "a/src/CAD/IFox.CAD.Shared/ExtensionMethod/\346\226\260\345\273\272\345\241\253\345\205\205/HatchInfo.cs" +++ "b/src/CAD/IFox.CAD.Shared/ExtensionMethod/\346\226\260\345\273\272\345\241\253\345\205\205/HatchInfo.cs" @@ -48,6 +48,9 @@ public class HatchInfo /// 角度 /// double Angle => _hatch.PatternAngle; + + // 延后处理角度 + private double _angle; #endregion #region 构造 @@ -74,7 +77,7 @@ public HatchInfo(bool boundaryAssociative = true, throw new ArgumentException("填充比例不允许小于等于0"); _hatch.PatternScale = hatchScale;// 填充比例 - _hatch.PatternAngle = hatchAngle;// 填充角度 + _angle = hatchAngle;// 填充角度 _boundaryAssociative = boundaryAssociative; hatchOrigin ??= Point2d.Origin; @@ -188,6 +191,10 @@ public ObjectId Build(BlockTableRecord btrOfAddEntitySpace) else _hatch.SetHatchPattern(_patternTypeHatch, _hatchName); + // 处理充填角度 + _hatch.PatternAngle = _angle; + + // 关联边界,如果不先添加数据库空间内就会出错 // 为 true 会加入反应器,因此比较慢(二维码将会十几秒才生成好),视需求而定. _hatch.Associative = _boundaryAssociative; @@ -239,15 +246,37 @@ public HatchInfo EraseBoundary() void AppendLoop(IEnumerable boundaryIds, HatchLoopTypes hatchLoopTypes = HatchLoopTypes.Default) { - using ObjectIdCollection obIds = new(); + ObjectIdCollection obIds = new(); // 边界是闭合的,而且已经加入数据库 // 填充闭合环类型.最外面 - foreach (var border in boundaryIds) + + // 此段代码会出错的原因在于 如果是直线围成的闭合区域, + // 那么每次添加的其实一条直线,而直线不构成loop,所以不符合预期的数据,出错 + try { - obIds.Clear(); - obIds.Add(border); - _hatch.AppendLoop(hatchLoopTypes, obIds); + foreach (var border in boundaryIds) + { + obIds.Clear(); + obIds.Add(border); + _hatch.AppendLoop(hatchLoopTypes, obIds); + } // 此段代码会出错 } + catch (Exception ex) + { + + Env.Print(ex.Message); + Env.Print("发生错误,传入的边界不符合要求,请核实传入的边界是否为闭合边界列表,而不是组成边界的图元列表"); + throw; + } + + // 下面这行代码出错的原因是: 添加了重复的线条,需要进行剔除 + // _hatch.AppendLoop(hatchLoopTypes, boundaryIds.ToCollection()); + + // 这个函数还需要进行改造: + // 1. 明确传入的 boundaryIds 到底是多个边界,还是一个边界的子图元 + // 2. 根据传入的参数的不同 需要不同的处理措施 + // 3. 临时性措施 try一下抛异常,让用户确保传入的参数是准确的 + } /// diff --git a/tests/TestShared/TestHatchinfo.cs b/tests/TestShared/TestHatchinfo.cs new file mode 100644 index 0000000..52dbfaf --- /dev/null +++ b/tests/TestShared/TestHatchinfo.cs @@ -0,0 +1,18 @@ +namespace Test; + +public class TestHatchinfo +{ + [CommandMethod(" TestHatchInfo")] + public void TestHatchInfo() + { + using var tr = new DBTrans(); + var sf = new SelectionFilter(new TypedValue[] { new TypedValue(0, "*line,circle,arc") }); + var ids = Env.Editor.SSGet(null, sf).Value?.GetObjectIds(); + if (ids.Count() > 0) + { + HatchInfo hf = new HatchInfo(ids, false, null, 1, 0).Mode2UserDefined(); + hf.Build(tr.CurrentSpace); + } + } +} + diff --git a/tests/TestShared/TestShared.projitems b/tests/TestShared/TestShared.projitems index 7c5a772..a98011f 100644 --- a/tests/TestShared/TestShared.projitems +++ b/tests/TestShared/TestShared.projitems @@ -21,6 +21,7 @@ + -- Gitee From 9c412cadcb2c100ff982f77fc90e760f7ade3bb6 Mon Sep 17 00:00:00 2001 From: vicwjb Date: Thu, 12 Jan 2023 23:03:00 +0800 Subject: [PATCH 641/675] =?UTF-8?q?=E8=A1=A5=E5=85=85=E6=B3=A8=E9=87=8A?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/CAD/IFox.CAD.ACAD/GlobalUsings.cs | 6 +- src/CAD/IFox.CAD.ACAD/IFox.CAD.ACAD.csproj | 1 - .../IFox.CAD.Shared/Algorithms/Graph/Graph.cs | 97 +++++++++++++++++-- .../Algorithms/Graph/IGraph.cs | 12 ++- .../Algorithms/QuadTree/QuadTree.cs | 3 +- .../Algorithms/QuadTree/QuadTreeEvn.cs | 4 +- .../Algorithms/QuadTree/QuadTreeNode.cs | 4 +- .../Algorithms/QuadTree/QuadTreeSelectMode.cs | 30 ++++-- .../Algorithms/QuadTree/Rect.cs | 10 +- .../IFox.CAD.Shared/Copyclip/BitmapTool.cs | 18 +++- .../ExtensionMethod/SymbolTableEx.cs | 6 +- src/CAD/IFox.CAD.Shared/Runtime/IAutoGo.cs | 4 +- 12 files changed, 161 insertions(+), 34 deletions(-) diff --git a/src/CAD/IFox.CAD.ACAD/GlobalUsings.cs b/src/CAD/IFox.CAD.ACAD/GlobalUsings.cs index 4227a00..dadfe53 100644 --- a/src/CAD/IFox.CAD.ACAD/GlobalUsings.cs +++ b/src/CAD/IFox.CAD.ACAD/GlobalUsings.cs @@ -1,4 +1,4 @@ -/// 系统引用 +// 系统引用 global using System; global using System.Collections; global using System.Collections.Generic; @@ -17,7 +17,7 @@ global using Registry = Microsoft.Win32.Registry; global using RegistryKey = Microsoft.Win32.RegistryKey; -/// cad 引用 +// cad 引用 global using Autodesk.AutoCAD.ApplicationServices; global using Autodesk.AutoCAD.EditorInput; global using Autodesk.AutoCAD.Colors; @@ -41,7 +41,7 @@ global using Cad_DxfFiler = Autodesk.AutoCAD.DatabaseServices.DxfFiler; global using Cad_ErrorStatus = Autodesk.AutoCAD.Runtime.ErrorStatus; -/// ifoxcad.basal 引用 +// ifoxcad.basal 引用 global using IFoxCAD.Basal; #if !NewtonsoftJson diff --git a/src/CAD/IFox.CAD.ACAD/IFox.CAD.ACAD.csproj b/src/CAD/IFox.CAD.ACAD/IFox.CAD.ACAD.csproj index d53c17d..d93e6e3 100644 --- a/src/CAD/IFox.CAD.ACAD/IFox.CAD.ACAD.csproj +++ b/src/CAD/IFox.CAD.ACAD/IFox.CAD.ACAD.csproj @@ -2,7 +2,6 @@ NET35;NET40;NET45 - true true diff --git a/src/CAD/IFox.CAD.Shared/Algorithms/Graph/Graph.cs b/src/CAD/IFox.CAD.Shared/Algorithms/Graph/Graph.cs index 255d2b3..d772b03 100644 --- a/src/CAD/IFox.CAD.Shared/Algorithms/Graph/Graph.cs +++ b/src/CAD/IFox.CAD.Shared/Algorithms/Graph/Graph.cs @@ -21,7 +21,9 @@ public sealed class Graph : IGraph, IEnumerable /// 为加快索引,引入hash检索 /// readonly Dictionary vertexs = new(); - + /// + /// 节点数量 + /// public int VerticesCount => vertices.Count; /// @@ -51,6 +53,9 @@ private IGraphVertex? ReferenceVertex #endregion #region 构造函数 + /// + /// + /// public Graph() { insertCount = 0; // 每次新建对象就将顶点顺序号归零 @@ -271,6 +276,10 @@ IGraph IGraph.Clone() return Clone(); } + /// + /// 节点迭代器 + /// + /// [System.Diagnostics.DebuggerStepThrough] public IEnumerator GetEnumerator() { @@ -282,7 +291,9 @@ public IEnumerator GetEnumerator() { return GetEnumerator() as IEnumerator; } - + /// + /// 节点迭代器 + /// public IEnumerable VerticesAsEnumberable => vertices.Select(x => x.Key); #endregion @@ -324,7 +335,13 @@ public string ToReadable() public sealed class GraphVertex : IGraphVertex, IEquatable, IComparable, IComparable { #region 属性 + /// + /// 数据 + /// public Point3d Data { get; set; } + /// + /// 索引 + /// public int Index { get; set; } #endregion @@ -342,11 +359,20 @@ public GraphVertex(Point3d value, int index) #endregion #region 重载运算符_比较 + /// + /// 是否相等 + /// + /// + /// public bool Equals(IGraphVertex other) { return Index == other.Index; } - + /// + /// 是否相等 + /// + /// + /// public override bool Equals(object obj) { if (obj is null) @@ -356,12 +382,19 @@ public override bool Equals(object obj) else return Equals(vertex); } - + /// + /// 计算hashcode + /// + /// public override int GetHashCode() { return Index; } - + /// + /// 比较大小 + /// + /// + /// public int CompareTo(IGraphVertex other) { if (Equals(other)) @@ -377,7 +410,12 @@ int IComparable.CompareTo(IGraphVertex other) { return CompareTo(other); } - + /// + /// 比较大小 + /// + /// + /// + /// public int CompareTo(object obj) { if (obj is null) @@ -393,7 +431,12 @@ public int CompareTo(object obj) throw new ArgumentException("Object is not a IGraphVertex"); } } - + /// + /// 相等 + /// + /// + /// + /// public static bool operator ==(GraphVertex person1, GraphVertex person2) { if (person1 is null || person2 is null) @@ -401,7 +444,12 @@ public int CompareTo(object obj) return person1.Equals(person2); } - + /// + /// 不相等 + /// + /// + /// + /// public static bool operator !=(GraphVertex person1, GraphVertex person2) { if (person1 is null || person2 is null) @@ -419,7 +467,13 @@ public int CompareTo(object obj) public sealed class GraphEdge : IEdge, IEquatable { #region 属性 + /// + /// 顶点 + /// public IGraphVertex TargetVertex { get; set; } + /// + /// 边 + /// public Curve3d TargetEdge { get; set; } #endregion @@ -437,6 +491,11 @@ public GraphEdge(IGraphVertex target, Curve3d edge) #endregion #region 重载运算符_比较 + /// + /// 是否相等 + /// + /// + /// public bool Equals(GraphEdge other) { if (other is null) @@ -444,6 +503,11 @@ public bool Equals(GraphEdge other) return TargetVertex == other.TargetVertex && TargetEdge == other.TargetEdge; } + /// + /// 是否相等 + /// + /// + /// public override bool Equals(object obj) { if (obj is null) @@ -453,11 +517,20 @@ public override bool Equals(object obj) else return Equals(personObj); } - + /// + /// 获取hashcode + /// + /// public override int GetHashCode() { return (TargetVertex.GetHashCode(), TargetEdge.GetHashCode()).GetHashCode(); } + /// + /// 相等 + /// + /// + /// + /// public static bool operator ==(GraphEdge person1, GraphEdge person2) { if (person1 is null || person2 is null) @@ -465,6 +538,12 @@ public override int GetHashCode() return person1.Equals(person2); } + /// + /// 不相等 + /// + /// + /// + /// public static bool operator !=(GraphEdge person1, GraphEdge person2) { if (person1 is null || person2 is null) diff --git a/src/CAD/IFox.CAD.Shared/Algorithms/Graph/IGraph.cs b/src/CAD/IFox.CAD.Shared/Algorithms/Graph/IGraph.cs index 73547b7..3a7eec4 100644 --- a/src/CAD/IFox.CAD.Shared/Algorithms/Graph/IGraph.cs +++ b/src/CAD/IFox.CAD.Shared/Algorithms/Graph/IGraph.cs @@ -55,9 +55,19 @@ public interface IGraph /// /// HashSet GetAdjacencyEdge(IGraphVertex vertex); + /// + /// 当前的节点 + /// IGraphVertex? ReferenceVertex { get; } - + /// + /// 删除节点 + /// + /// 节点的坐标 void RemoveVertex(Point3d pt); + /// + /// 删除边 + /// + /// 曲线 void RemoveEdge(Curve3d curve); } diff --git a/src/CAD/IFox.CAD.Shared/Algorithms/QuadTree/QuadTree.cs b/src/CAD/IFox.CAD.Shared/Algorithms/QuadTree/QuadTree.cs index 267cc4a..538a620 100644 --- a/src/CAD/IFox.CAD.Shared/Algorithms/QuadTree/QuadTree.cs +++ b/src/CAD/IFox.CAD.Shared/Algorithms/QuadTree/QuadTree.cs @@ -166,7 +166,8 @@ public void Insert(TEntity ent) /// 查询四叉树,返回给定区域的数据项 /// /// 矩形选区查询 - /// + /// 查询模式 + /// 查询结果列表 public List Query(Rect rect, QuadTreeSelectMode selectMode = QuadTreeSelectMode.IntersectsWith) { QuadTreeEvn.SelectMode = selectMode; diff --git a/src/CAD/IFox.CAD.Shared/Algorithms/QuadTree/QuadTreeEvn.cs b/src/CAD/IFox.CAD.Shared/Algorithms/QuadTree/QuadTreeEvn.cs index 16d518b..f077a02 100644 --- a/src/CAD/IFox.CAD.Shared/Algorithms/QuadTree/QuadTreeEvn.cs +++ b/src/CAD/IFox.CAD.Shared/Algorithms/QuadTree/QuadTreeEvn.cs @@ -1,6 +1,8 @@ #pragma warning disable CA2211 // 非常量字段应当不可见 namespace IFoxCAD.Cad; - +/// +/// 四叉树环境变量 +/// public class QuadTreeEvn { /// diff --git a/src/CAD/IFox.CAD.Shared/Algorithms/QuadTree/QuadTreeNode.cs b/src/CAD/IFox.CAD.Shared/Algorithms/QuadTree/QuadTreeNode.cs index 7898b84..06bf472 100644 --- a/src/CAD/IFox.CAD.Shared/Algorithms/QuadTree/QuadTreeNode.cs +++ b/src/CAD/IFox.CAD.Shared/Algorithms/QuadTree/QuadTreeNode.cs @@ -388,8 +388,8 @@ public void Remove(Rect queryArea) /// /// 查询范围内的实体 /// - /// 查询矩形 - /// + /// 查询矩形 + /// 查询结果 public void Query(Rect queryArea, List results) { GetCurrentContents(queryArea, results); diff --git a/src/CAD/IFox.CAD.Shared/Algorithms/QuadTree/QuadTreeSelectMode.cs b/src/CAD/IFox.CAD.Shared/Algorithms/QuadTree/QuadTreeSelectMode.cs index 2cc2e37..624d313 100644 --- a/src/CAD/IFox.CAD.Shared/Algorithms/QuadTree/QuadTreeSelectMode.cs +++ b/src/CAD/IFox.CAD.Shared/Algorithms/QuadTree/QuadTreeSelectMode.cs @@ -5,8 +5,14 @@ /// public enum QuadTreeSelectMode { - IntersectsWith, // 碰撞到就选中 - Contains, // 全包含才选中 + /// + /// 碰撞到就选中 + /// + IntersectsWith, + /// + /// 全包含才选中 + /// + Contains, } /// @@ -14,8 +20,20 @@ public enum QuadTreeSelectMode /// public enum QuadTreeFindMode { - Top = 1, // 上 - Bottom = 2, // 下 - Left = 4, // 左 - Right = 8, // 右 + /// + /// 上 + /// + Top = 1, + /// + /// 下 + /// + Bottom = 2, + /// + /// 左 + /// + Left = 4, + /// + /// 右 + /// + Right = 8, } \ No newline at end of file diff --git a/src/CAD/IFox.CAD.Shared/Algorithms/QuadTree/Rect.cs b/src/CAD/IFox.CAD.Shared/Algorithms/QuadTree/Rect.cs index 9e10b97..a6a5d84 100644 --- a/src/CAD/IFox.CAD.Shared/Algorithms/QuadTree/Rect.cs +++ b/src/CAD/IFox.CAD.Shared/Algorithms/QuadTree/Rect.cs @@ -9,6 +9,10 @@ namespace IFoxCAD.Cad; public class TolerancePoint2d : IEqualityComparer { readonly double _tolerance; + /// + /// 构造函数 + /// + /// 容差 public TolerancePoint2d(double tolerance = 1e-6) { _tolerance = tolerance; @@ -472,13 +476,17 @@ static double Cross(Point2d o, Point2d a, Point2d b) /// 直线点1 /// 直线点2 /// 判断点 - /// b点在oa的逆时针 + /// b点在oa的逆时针时为 static bool CrossAclockwise(Point2d o, Point2d a, Point2d b) { return Cross(o, a, b) > -1e-6;// 浮点数容差考虑 } #if !WinForm + /// + /// 创建矩形范围多段线 + /// + /// 多段线对象 public Entity ToPolyLine() { var bv = new List(); diff --git a/src/CAD/IFox.CAD.Shared/Copyclip/BitmapTool.cs b/src/CAD/IFox.CAD.Shared/Copyclip/BitmapTool.cs index 9ce266b..f044095 100644 --- a/src/CAD/IFox.CAD.Shared/Copyclip/BitmapTool.cs +++ b/src/CAD/IFox.CAD.Shared/Copyclip/BitmapTool.cs @@ -1,7 +1,9 @@ namespace IFoxCAD.Cad; using System; - +/// +/// bitmap工具类 +/// public class BitmapTool { // https://blog.csdn.net/shellching/article/details/18405185 @@ -24,7 +26,12 @@ public class BitmapTool /// [DllImport("user32.dll", SetLastError = true)] public static extern IntPtr GetDC(IntPtr hWnd); - + /// + /// + /// + /// + /// + /// [DllImport("user32.dll")] public static extern bool ReleaseDC(IntPtr hWnd, IntPtr hDC); @@ -96,9 +103,13 @@ public class BitmapTool [DllImport("gdi32.dll", EntryPoint = "BitBlt", SetLastError = true)] [return: MarshalAs(UnmanagedType.Bool)] public static extern bool BitBlt([In] IntPtr hdc, int nXDest, int nYDest, int nWidth, int nHeight, [In] IntPtr hdcSrc, int nXSrc, int nYSrc, TernaryRasterOperations dwRop); - + /// + /// A raster-operation code enum + /// public enum TernaryRasterOperations : uint { + +#pragma warning disable CS1591 // 缺少对公共可见类型或成员的 XML 注释 SRCCOPY = 0x00CC0020, SRCPAINT = 0x00EE0086, SRCAND = 0x008800C6, @@ -115,6 +126,7 @@ public enum TernaryRasterOperations : uint BLACKNESS = 0x00000042, WHITENESS = 0x00FF0062, CAPTUREBLT = 0x40000000 //only if WinVer >= 5.0.0 (see wingdi.h) +#pragma warning restore CS1591 // 缺少对公共可见类型或成员的 XML 注释 } /// diff --git a/src/CAD/IFox.CAD.Shared/ExtensionMethod/SymbolTableEx.cs b/src/CAD/IFox.CAD.Shared/ExtensionMethod/SymbolTableEx.cs index 4703c78..dd5797e 100644 --- a/src/CAD/IFox.CAD.Shared/ExtensionMethod/SymbolTableEx.cs +++ b/src/CAD/IFox.CAD.Shared/ExtensionMethod/SymbolTableEx.cs @@ -184,11 +184,7 @@ public static void AddAttsToBlocks(this SymbolTable块定义Id public static ObjectId GetBlockFrom(this SymbolTable table, string fileName, bool over) { - // FileInfo fi = new(fileName); - // string blkdefname = fi.Name; - // if (blkdefname.Contains(".")) - // blkdefname = blkdefname.Substring(0, blkdefname.LastIndexOf('.')); - + string blkdefname = SymbolUtilityServices.RepairSymbolName( SymbolUtilityServices.GetSymbolNameFromPathName(fileName, "dwg"), false); diff --git a/src/CAD/IFox.CAD.Shared/Runtime/IAutoGo.cs b/src/CAD/IFox.CAD.Shared/Runtime/IAutoGo.cs index 738ba19..6eed7e2 100644 --- a/src/CAD/IFox.CAD.Shared/Runtime/IAutoGo.cs +++ b/src/CAD/IFox.CAD.Shared/Runtime/IAutoGo.cs @@ -138,7 +138,9 @@ public void Initialize() } } - // 关闭cad的时候会自动执行 + /// + /// 关闭cad的时候会自动执行 + /// public void Terminate() { try -- Gitee From 756d17608ac674383f041b3411a6cd6a319ad85e Mon Sep 17 00:00:00 2001 From: vicwjb Date: Fri, 13 Jan 2023 23:55:21 +0800 Subject: [PATCH 642/675] =?UTF-8?q?=E6=9B=B4=E6=96=B0=E6=96=87=E6=A1=A3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 159 ++++++++---------- ...66\346\236\204\350\257\264\346\230\216.md" | 153 +++++++++++++---- src/Basal/Directory.Build.props | 2 +- src/CAD/Directory.Build.props | 4 +- 4 files changed, 192 insertions(+), 126 deletions(-) diff --git a/README.md b/README.md index 5d9e254..5f08fbe 100644 --- a/README.md +++ b/README.md @@ -1,27 +1,76 @@ -# IFoxCAD +# IFoxCAD 说明 -#### 释义 -IFox中: -- IF:为Inspire Function(中文名:跃动方程)的首字母缩写 -- Fox:起初 **雪山飞狐(又狐哥)** 在明经论坛发布了[开源库](http://bbs.mjtd.com/thread-75701-1-1.html),后来狐哥自己的项目进行了极大的丰富后形成NFox类库。 +基于.NET的Cad二次开发类库。 - 在 **落魄山人(又小山山)** 开始学习c#开发后,由于这个人过于懒,不想自己写基础的函数了,所以厚颜无耻的跟狐哥要来了 **NFox** 的源码。 +#### 一、项目来源 - 在学习了一阵源码后,发现狐哥的代码惊天地,泣鬼神,惊为天人,直接跪服,从此成为狐哥的脑残粉。 +起初 **雪山飞狐(又狐哥)** 在明经论坛发布了[开源库](http://bbs.mjtd.com/thread-75701-1-1.html),后来狐哥自己的项目进行了极大的丰富后形成NFox类库。然后 **落魄山人** 在征得 雪山飞狐的同意后,对NFox类库进行了整理,增加了注释等,重新发布了NFox类库。 - 为不使明珠蒙尘,特向狐哥申请是否可以开源,经原作者狐哥同意,正式发布 **NFox** 开源类库。 +后来,经过一段时间的更新后,由于莫名其妙的原因NFox类库挂掉了。而这时山人同学已经基本吃透NFox类库,考虑到NFox的封装过于复杂,遂进行了重构。 - 后来本人多次二逼操作后, **NFox** 莫名无法运行,找不到原因,以及原代码嵌套引用多,逻辑复杂,痛定思痛,遂放弃 **NFox** 的开发,基于 **NFox** 重构为 **IFox** ,重新发布开源项目,取名为`IF+Fox=IFox`,一语双关,简洁而不简单。当然这些都是掩饰,其实就是 **I(爱)Fox(狐哥)** 。 +重构的类库命名为IFoxCAD, 寓意为:**I(爱)Fox(狐哥)**,本项目发布于**Inspire Function(中文名:跃动方程)** 组织下,感谢 **小轩轩** 给起的名字。 - **Inspire Function(中文名:跃动方程)及 IFoxCad 的名字均为小轩轩起名** +可以加群交流: -#### 介绍 +![ifoxcad用户交流群群二维码](./docs/png/ifoxcad用户交流群群二维码.png) -基于.NET的Cad二次开发类库。 +#### 二、 快速入门 -可以加群交流: +- 打开vs,新建一个standard类型的类库项目,**注意,需要选择类型的时候一定要选standard2.0** -![ifoxcad用户交流群群二维码](./docs/png/ifoxcad用户交流群群二维码.png) +- 双击项目,打开项目文件: + + - 修改项目文件里的`netcore2.0`为`NET45`。其中的net45,可以改为NET45以上的标准TFM(如:net45、net46、net47等等)。同时可以指定多版本。具体的详细的教程见 [VS通过添加不同引用库,建立多条件编译](https://www.yuque.com/vicwjb/zqpcd0/ufbwyl)。 + + - 在 ` xxx ` 中增加 `preview`,主要是为了支持最新的语法,本项目采用了最新的语法编写。项目文件现在的内容类似如下: + +```xml + + + net45 + preview + + +``` + +- 右键项目文件,选择管理nuget程序包。 + +- 在nuget程序里搜索**ifox**,记得将包括预发行版打钩。截止本文最后更新时,nuget上最新的版本为ifox.cad.source 0.5.0-alpha6版本和ifox.Basal.source 0.5.0-alpha6版本。点击安装就可以。 + +- 添加引用,在新建的项目里的cs文件里添加相关的引用 + +```csharp +using Autodesk.AutoCAD.ApplicationServices; +using Autodesk.AutoCAD.EditorInput; +using Autodesk.AutoCAD.Runtime; +using Autodesk.AutoCAD.Geometry; +using Autodesk.AutoCAD.DatabaseServices; +using IFoxCAD.Cad; +``` + +- 添加代码 + +```csharp +[CommandMethod(nameof(Hello))] +public void Hello() +{ +     using DBTrans tr = new(); +     var line1 = new Line(new Point3d(0, 0, 0), new Point3d(1, 1, 0)); +     tr.CurrentSpace.AddEntity(line1); +     // 如果您没有添加preview到项目文件里的话:按如下旧语法: +     // using(var tr = new DBTrans()) +     // { +     // var line1 = new Line(new Point3d(0, 0, 0), new Point3d(1, 1, 0)); +     // tr.CurrentSpace.AddEntity(line1); +     // } +} +``` + +这段代码就是在cad的当前空间内添加了一条直线。 + +- F6生成,然后打开cad,netload命令将刚刚生成的dll加载。 + +- 运行hello命令,然后缩放一下视图,现在一条直线和一个圆已经显示在屏幕上了。 #### 软件架构及相关说明 @@ -43,12 +92,11 @@ IFox的[jing分支](https://gitee.com/inspirefunction/ifoxcad/tree/jing/)是一 2. 一些全局命名空间的缺少,我们也建议您使用全局命名空间来补充, 您只需要按照`IFoxCAD.Cad`的`GlobalUsings.cs`文件一样添加就好了. + 3. 若您使用acad08版本,需要在您的工程中设置`ac2008`和`ac2009`(大小写敏感)两个预定义标签. 方能启用08工程中缺少的09工程才有的类. 同时我们在`IFoxCAD.Cad`中提供了这两个例子. - - #### IFoxCad 项目模版 可以在vs扩展菜单-管理扩展中搜索ifoxcad,即可安装项目模板。使用项目模版可以方便的创建支持多目标多版本的使用ifoxcad类库的项目和类。如果无法在vs的市场里下载,就去上面的QQ群里下载。 @@ -63,61 +111,7 @@ IFox的[jing分支](https://gitee.com/inspirefunction/ifoxcad/tree/jing/)是一 1. 快速入门 - - 打开vs,新建一个standard类型的类库项目,**注意,需要选择类型的时候一定要选standard2.0** ![](./docs/png/standard.png) - - - 双击项目,打开项目文件: - - - 修改项目文件里的`netcore2.0`为`NET45`。其中的net45,可以改为NET45以上的标准TFM(如:net45、net46、net47等等)。同时可以指定多版本。具体的详细的教程见 [VS通过添加不同引用库,建立多条件编译]( https://www.yuque.com/vicwjb/zqpcd0/ufbwyl)。 - - - 在 ` xxx ` 中增加 `preview`,主要是为了支持最新的语法,本项目采用了最新的语法编写。项目文件现在的内容类似如下: - - ```xml - - - net45 - preview - - - ``` - - - 右键项目文件,选择管理nuget程序包。![](./docs/png/nuget1.png) - - - 在nuget程序里搜索**ifoxcad**,直接选择最新的版本(如果您是 **net40** 或者 **net35** 的用户,可以安装 **0.1.6** 版本),然后点击安装**IFoxCAD.Cad**,nuget会自动安装ifoxcad依赖的库。(按下图绿色框框里选择浏览,程序包来源选择nuget.org,安装IFoxCAD.Cad包。IFoxCAD.Basal是IFoxCAD.Cad的依赖项会自动安装,如果要开发wpf界面的话,可以安装IFoxCAD.WPF,提供了简单的mvvm支持)![](./docs/png/nuget.png) - - - 添加引用 - - ```c# - using Autodesk.AutoCAD.ApplicationServices; - using Autodesk.AutoCAD.EditorInput; - using Autodesk.AutoCAD.Runtime; - using Autodesk.AutoCAD.Geometry; - using Autodesk.AutoCAD.DatabaseServices; - using IFoxCAD.Cad; - ``` - - - 添加代码 - - ```c# - [CommandMethod(nameof(Hello))] - public void Hello() - { - using DBTrans tr = new(); - var line1 = new Line(new Point3d(0, 0, 0), new Point3d(1, 1, 0)); - tr.CurrentSpace.AddEntity(line1); - // 如果您没有添加preview到项目文件里的话:按如下旧语法: - // using(var tr = new DBTrans()) - // { - // var line1 = new Line(new Point3d(0, 0, 0), new Point3d(1, 1, 0)); - // tr.CurrentSpace.AddEntity(line1); - // } - } - ``` - - 这段代码就是在cad的当前空间内添加了一条直线。 - - - F6生成,然后打开cad,netload命令将刚刚生成的dll加载。 - - - 运行hello命令,然后缩放一下视图,现在一条直线和一个圆已经显示在屏幕上了。 + - 2. [事务管理器用法](/docs/DBTrans.md) @@ -181,13 +175,11 @@ IFox的[jing分支](https://gitee.com/inspirefunction/ifoxcad/tree/jing/)是一 } } ``` - - - -7. 天秀的打开模式提权 +7. 天秀的打开模式提权 + 由于cad的对象是有打开模式,是否可写等等,为了安全起见,在处理对象时,一般是用读模式打开,然后需要写数据的时候在提权为写模式,然后在降级到读模式,但是这个过程中,很容易漏掉某些步骤,然后cad崩溃。为了处理这些情况,内裤提供了提权类来保证读写模式的有序转换。 - + ```c# using(line.ForWrite()) // 开启对象写模式提权事务 { @@ -197,10 +189,6 @@ IFox的[jing分支](https://gitee.com/inspirefunction/ifoxcad/tree/jing/)是一 8. 未完待续。。。。 - - - - #### 屏蔽IFox.Basal的元组功能 由于c#在每个版本提供的元组功能不一样(有的中间版本缺少),所以IFox内置了元组功能,但是内置元组又会引起某些用户的工程冲突. @@ -208,11 +196,11 @@ IFox的[jing分支](https://gitee.com/inspirefunction/ifoxcad/tree/jing/)是一 因此您需要制作一个影子工程: 1. 您需要具备使用git子模块的能力,引用jing分支中的源码. - + 子模块是为了保证您不修改IFox项目,因为你需要定期`git pull`更新组织提供的内容. - + 熟悉子模块之后,你需要把IFox项目一个个加入你的解决方案, - + 除了本次主角IFox.Basal. 2. 在子模块文件夹外新建 IFox.Basal(影) 文件夹 @@ -220,7 +208,7 @@ IFox的[jing分支](https://gitee.com/inspirefunction/ifoxcad/tree/jing/)是一 3. 复制 IFox.Basal(本) 的 .csproj 到上一步的文件夹. 4. 修改 .csproj(影) 利用引用链接的方式进行引用 IFox.Basal(本) 的文件,不引用CLS就等于屏蔽了元组 - + ```xml @@ -231,8 +219,7 @@ IFox的[jing分支](https://gitee.com/inspirefunction/ifoxcad/tree/jing/)是一 5. 修改 .csproj(影) 引入微软提供的元组 nuget: `System.ValueTuple` (或者你喜欢的) 6. 解决方案加入 .csproj(影) 之后被内部其他项目引用. - + 这个方法便可以把 影子工程 独立在IFox项目外,令`git pull`仍然有效, 并且 本体工程 不做大更改的时候,影子工程更新幅度非常少,也多亏csproj改版了,不然也没有这个骚操作. - diff --git "a/docs/\345\205\263\344\272\216IFoxCAD\347\232\204\346\236\266\346\236\204\350\257\264\346\230\216.md" "b/docs/\345\205\263\344\272\216IFoxCAD\347\232\204\346\236\266\346\236\204\350\257\264\346\230\216.md" index 69a9b79..998e12c 100644 --- "a/docs/\345\205\263\344\272\216IFoxCAD\347\232\204\346\236\266\346\236\204\350\257\264\346\230\216.md" +++ "b/docs/\345\205\263\344\272\216IFoxCAD\347\232\204\346\236\266\346\236\204\350\257\264\346\230\216.md" @@ -1,4 +1,74 @@ -# 关于IFoxCAD的架构说明 +# IFoxCAD的架构说明 + +AutoCAD 的 .net api 的架构是如下这样的: + +1. Application 对象 + +```mermaid +graph LR; +a(Application)-->DocumentManager +a-->DocumentWindowCollection +a-->InfoCenter +a-->MainWindow +a-->MenuBar +a-->MenuGroups +a-->Preferences +a-->Publisher +a-->StatusBar +a-->UserConfigurationManager +``` + +2. Document 对象 + +```mermaid +graph LR; +Application-->DocumentManager-->b[Document] +b-->Database +b-->Editor +b-->GraphicsManager +b-->StatusBar +b-->TransactionManager +b-->UserData +b-->Window +``` + +3. Database 对象 + +```mermaid +flowchart TB; +subgraph NamedDictionaris + direction TB + Layout-Dictionary-->Object + Others-->OtherObject +end +subgraph Tables + direction TB + BlockTable-->BlockTableRecord-->Entity + OthersTable-->OthersTableRecord +end +Application-->DocumentManager-->Document-->d[Database]-->Tables +d-->NamedDictionaris +``` + +4. Transation 对象 + +```mermaid +flowchart LR; +subgraph Transation + direction LR + f(StartTransation)--modify objects-->e{isOK} + e--Yes-->h(commit) + e--No-->abort +end +h--write-->d[Database] +g[Document or Database]--start-->f +``` + + + + + + IFoxCAD是基于NFOX类库的重制版,主要是提供一个最小化的内核,即DBTrans、SymbolTable、ResultData、SelectFilter等基础类,其他的功能都通过扩展方法的方式来实现。 @@ -7,33 +77,42 @@ IFoxCAD是基于NFOX类库的重制版,主要是提供一个最小化的内核 ## 一、组织结构图 - IFoxCAD - - IFoxCAD.Basal - cad以外常用的类库 - - LinqEx - linq扩展类 - - LoopList - 环链表 - - IFoxCAD.Cad - cad相关的类库 - - Runtime - 包含系统级别的功能 - - AcadVersion - cad版本号类 - - AssemInfo - 程序集信息 - - AutoRegAssem - 程序集加载类型 - - DBTrans - 事务处理类 - - Env - 系统管理类 - - SymbolTable - 符号表类 - - ExtensionMethod - 扩展函数,以Ex结尾 - - SymbolTableEx - 符号表扩展类 - - SymbolTableRecordEx - 符号表记录扩展类 - - EntityEx - 实体扩展类 - - 。。。。。。 - - ResultData - - 待补充。。。 - - SelectionFilter - - 待补充。。。 - - IFoxCAD.WPF - wpf的mvvm模式相关的类库 - - 待补充。。。 - - ![输入图片说明](https://images.gitee.com/uploads/images/2021/0701/225449_2b18eb89_9063830.png "屏幕截图.png") - ![输入图片说明](https://images.gitee.com/uploads/images/2021/0701/225550_840a862a_9063830.png "屏幕截图.png") - ![输入图片说明](https://images.gitee.com/uploads/images/2021/0701/225525_b246bbd2_9063830.png "屏幕截图.png") -## 二、关于DBTrans类的说明 + + - IFoxCAD.Basal - cad以外常用的类库 + + - LinqEx - linq扩展类 + + - LoopList - 环链表 + + - IFoxCAD.Cad - cad相关的类库 + + - Runtime - 包含系统级别的功能 + + - AcadVersion - cad版本号类 + - AssemInfo - 程序集信息 + - AutoRegAssem - 程序集加载类型 + - DBTrans - 事务处理类 + - Env - 系统管理类 + - SymbolTable - 符号表类 + + - ExtensionMethod - 扩展函数,以Ex结尾 + + - SymbolTableEx - 符号表扩展类 + - SymbolTableRecordEx - 符号表记录扩展类 + - EntityEx - 实体扩展类 + - 。。。。。。 + + - ResultData + + - 待补充。。。 + + - SelectionFilter + + - 待补充。。。 + + - IFoxCAD.WPF - wpf的mvvm模式相关的类库 + + ## 二、关于DBTrans类的说明 ### 2.1 为什么要构建DBTrans类? @@ -50,9 +129,11 @@ DBTrans类里基本的封装就是Transaction,然后是Document、Database、E DBTrans的每个实例都具有这些属性,而这些属性就对应于cad的相关类库,通过这些属性就可以对数据进行相应的操作。特别是符号表中最常用的就是块表,通过对块表的操作来实现添加图元等。 ### 2.3 DBTrans类应该具有的成员 + 为了尽量少的封装方法,减少类的复杂度,目前计划的方法主要为: 属性: + - Top ---返回当前事务 - Database ---数据库 - Document ---文档 @@ -60,11 +141,13 @@ DBTrans的每个实例都具有这些属性,而这些属性就对应于cad的 - Trans ---事务管理器 构造函数: + - DBTrans(Document doc = null, bool commit = true) - DBTrans(Database database, bool commit = true) - DBTrans(string fileName, bool commit = true) 符号表: + - BlockTable 块表 - LayerTable 层表 - TextStyleTable 文字样式表 @@ -76,10 +159,12 @@ DBTrans的每个实例都具有这些属性,而这些属性就对应于cad的 - ViewportTable 视口表 方法: + - GetObject ---根据对象id获取图元对象 - 。。。 接口: + - Abort ---放弃事务 - Commit ---提交事务 - Dispose --- 执行与释放非托管资源 @@ -96,10 +181,13 @@ DBTrans的每个实例都具有这些属性,而这些属性就对应于cad的 - 有了这个类,DBTrans类就可以直接通过属性获取符号表的关联关系,然后进行符号表的处理。 ### 3.2 SymbolTable类应该具有的成员 + 属性: + - CurrentSymbolTable ---当前的符号表对象 方法: + - this ---索引器符号表记录函数 - Add ---添加符号表记录函数 - Remove --- 删除符号表记录函数(层表请使用扩展方法Delete) @@ -112,12 +200,3 @@ DBTrans的每个实例都具有这些属性,而这些属性就对应于cad的 特殊说明:当符号表为块表时,上述函数实际操作的是块定义、属性定义等。所以为了添加图元,需要特殊写法,原因在于cad的实体都是存在符号表记录里的,通常为模型这个块表记录。 # 慢慢完善,想到哪写到哪。。。 - - - - - - - - - diff --git a/src/Basal/Directory.Build.props b/src/Basal/Directory.Build.props index d247f3c..0a91cb9 100644 --- a/src/Basal/Directory.Build.props +++ b/src/Basal/Directory.Build.props @@ -18,7 +18,7 @@ https://gitee.com/inspirefunction/ifoxcad https://gitee.com/inspirefunction/ifoxcad.git git - IFoxCAD;C#;NET;Common;Basal + IFox;C#;NET;Common;Basal $(MSBuildThisFileDirectory)bin\$(Configuration)\ True diff --git a/src/CAD/Directory.Build.props b/src/CAD/Directory.Build.props index 10d1c84..1745862 100644 --- a/src/CAD/Directory.Build.props +++ b/src/CAD/Directory.Build.props @@ -1,7 +1,7 @@  - 0.5.0-alpha4 + 0.5.0-alpha6 支持源码包。 @@ -27,7 +27,7 @@ https://gitee.com/inspirefunction/ifoxcad https://gitee.com/inspirefunction/ifoxcad.git git - IFoxCAD;CAD;AutoCad;C#;NET;GStarCAD;ZWCAD + IFox;CAD;AutoCad;C#;NET;GStarCAD;ZWCAD -- Gitee From b16a46c5a6e8d42c0828565965fa4a3da734b52b Mon Sep 17 00:00:00 2001 From: DYH <1742647821@qq.com> Date: Sat, 14 Jan 2023 11:38:33 +0800 Subject: [PATCH 643/675] =?UTF-8?q?=E5=A4=84=E7=90=86=E5=8F=AF=E7=A9=BA?= =?UTF-8?q?=E8=AD=A6=E5=91=8A?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/CAD/IFox.CAD.Shared/ExtensionMethod/CurveEx.cs | 2 +- src/CAD/IFox.CAD.Shared/Runtime/IAutoGo.cs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/CAD/IFox.CAD.Shared/ExtensionMethod/CurveEx.cs b/src/CAD/IFox.CAD.Shared/ExtensionMethod/CurveEx.cs index 68b286f..905d38f 100644 --- a/src/CAD/IFox.CAD.Shared/ExtensionMethod/CurveEx.cs +++ b/src/CAD/IFox.CAD.Shared/ExtensionMethod/CurveEx.cs @@ -187,7 +187,7 @@ public static List BreakCurve(this List curves) if (pars1.Count > 0) { var c3ds = gc1.GetSplitCurves(pars1); - if (c3ds.Count > 1) + if (c3ds is not null && c3ds.Count > 1) { foreach (var c3d in c3ds) { diff --git a/src/CAD/IFox.CAD.Shared/Runtime/IAutoGo.cs b/src/CAD/IFox.CAD.Shared/Runtime/IAutoGo.cs index 6eed7e2..3b71d4c 100644 --- a/src/CAD/IFox.CAD.Shared/Runtime/IAutoGo.cs +++ b/src/CAD/IFox.CAD.Shared/Runtime/IAutoGo.cs @@ -132,7 +132,7 @@ public void Initialize() RunFunctions(_InitializeList); } } - catch (System.Exception e) + catch { Debugger.Break(); } -- Gitee From a25fa956b7214f52e041b1ffbba6b3acfbb7b08a Mon Sep 17 00:00:00 2001 From: DYH <1742647821@qq.com> Date: Sat, 14 Jan 2023 12:19:35 +0800 Subject: [PATCH 644/675] =?UTF-8?q?=E6=B7=BB=E5=8A=A0=E7=A9=BA=E9=97=B2?= =?UTF-8?q?=E6=97=B6=E5=A4=84=E7=90=86IdleAction=E7=B1=BB?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/CAD/IFox.CAD.Shared/Runtime/IdleAction.cs | 58 +++++++++++++++++++ 1 file changed, 58 insertions(+) create mode 100644 src/CAD/IFox.CAD.Shared/Runtime/IdleAction.cs diff --git a/src/CAD/IFox.CAD.Shared/Runtime/IdleAction.cs b/src/CAD/IFox.CAD.Shared/Runtime/IdleAction.cs new file mode 100644 index 0000000..7060345 --- /dev/null +++ b/src/CAD/IFox.CAD.Shared/Runtime/IdleAction.cs @@ -0,0 +1,58 @@ +namespace IFox.CAD; + +/// +/// 空闲执行 +/// by DYH +/// 20230114 +/// +public static class IdleAction +{ + /// + /// 是否已经加载 + /// + private static bool alreadyLoad = false; + /// + /// 委托列表 + /// + private static readonly List actions = new(); + /// + /// 未处理的委托数量 + /// + public static int Count { get { return actions.Count; } } + /// + /// 添加空闲执行委托 + /// + /// 委托 + public static void Add(Action action) + { + actions.Add(action); + if (!alreadyLoad) + { + Acap.Idle -= Acap_Idle; + Acap.Idle += Acap_Idle; + alreadyLoad = true; + } + } + /// + /// 空闲处理事件 + /// + /// Acap + /// 事件参数 + private static void Acap_Idle(object sender, EventArgs e) + { + if (Count == 0) + { + alreadyLoad = false; + Acap.Idle -= Acap_Idle; + return; + } + try + { + actions[0]?.Invoke(); + } + finally + { + actions.RemoveAt(0); + } + } +} -- Gitee From d86fad8716a8bc94271d7579120e637fe0df8955 Mon Sep 17 00:00:00 2001 From: DYH <1742647821@qq.com> Date: Sat, 14 Jan 2023 14:07:23 +0800 Subject: [PATCH 645/675] =?UTF-8?q?=E4=BF=AE=E6=94=B9=E7=A9=BA=E9=97=B2?= =?UTF-8?q?=E6=97=B6=E5=A4=84=E7=90=86IdleAction=E7=B1=BB=E7=9A=84name=20s?= =?UTF-8?q?pace?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/CAD/IFox.CAD.Shared/IFox.CAD.Shared.projitems | 1 + src/CAD/IFox.CAD.Shared/Runtime/IdleAction.cs | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/src/CAD/IFox.CAD.Shared/IFox.CAD.Shared.projitems b/src/CAD/IFox.CAD.Shared/IFox.CAD.Shared.projitems index ecb9c9a..b53f25c 100644 --- a/src/CAD/IFox.CAD.Shared/IFox.CAD.Shared.projitems +++ b/src/CAD/IFox.CAD.Shared/IFox.CAD.Shared.projitems @@ -74,6 +74,7 @@ + diff --git a/src/CAD/IFox.CAD.Shared/Runtime/IdleAction.cs b/src/CAD/IFox.CAD.Shared/Runtime/IdleAction.cs index 7060345..6431391 100644 --- a/src/CAD/IFox.CAD.Shared/Runtime/IdleAction.cs +++ b/src/CAD/IFox.CAD.Shared/Runtime/IdleAction.cs @@ -1,4 +1,4 @@ -namespace IFox.CAD; +namespace IFoxCAD.Cad; /// /// 空闲执行 -- Gitee From 96993a5930dc5b4d90114ec79f9cb23b92e15736 Mon Sep 17 00:00:00 2001 From: vicwjb Date: Sat, 14 Jan 2023 16:14:22 +0800 Subject: [PATCH 646/675] =?UTF-8?q?=E8=B0=83=E6=95=B4winapi=E5=88=B0basal?= =?UTF-8?q?=E3=80=82?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/Basal/Directory.Build.props | 51 +++++++++++-------- .../IFox.Basal.Shared.projitems | 3 ++ .../IFox.Basal.Shared}/WindowsAPI/Enums.cs | 2 +- .../WindowsAPI/MouseHook.cs | 8 +-- .../WindowsAPI/WindowsAPI.cs | 21 ++++---- src/Basal/IFox.Basal/GlobalUsings.cs | 12 ++++- src/Basal/IFox.Basal/IFox.Basal.csproj | 7 ++- src/CAD/Directory.Build.props | 4 +- src/CAD/IFox.CAD.Shared/Copyclip/EmfTool.cs | 2 +- .../Copyclip/TagClipboardInfo.cs | 7 +-- .../IFox.CAD.Shared/IFox.CAD.Shared.projitems | 3 -- .../IFox.CAD.Shared/Runtime/FileOpenMode.cs | 6 +-- ...350\275\254cad\345\235\220\346\240\207.cs" | 2 +- tests/TestConsole/TestConsole.csproj | 1 - tests/TestShared/TestMarshal.cs | 2 +- 15 files changed, 71 insertions(+), 60 deletions(-) rename src/{CAD/IFox.CAD.Shared => Basal/IFox.Basal.Shared}/WindowsAPI/Enums.cs (99%) rename src/{CAD/IFox.CAD.Shared => Basal/IFox.Basal.Shared}/WindowsAPI/MouseHook.cs (98%) rename src/{CAD/IFox.CAD.Shared => Basal/IFox.Basal.Shared}/WindowsAPI/WindowsAPI.cs (98%) diff --git a/src/Basal/Directory.Build.props b/src/Basal/Directory.Build.props index 0a91cb9..539bf91 100644 --- a/src/Basal/Directory.Build.props +++ b/src/Basal/Directory.Build.props @@ -1,26 +1,37 @@  - - - 0.5.0-alpha6 - 支持源码包。 - + + + 0.5.0-alpha7 + 调整winapi到basal。 + - - preview - enable + + preview + enable true - InspireFunction - xsfhlzh;vicwjb;liuqihong - InspireFunction - 基于.NET的二次开发基本类库. - MIT - true - https://gitee.com/inspirefunction/ifoxcad - https://gitee.com/inspirefunction/ifoxcad.git - git - IFox;C#;NET;Common;Basal - $(MSBuildThisFileDirectory)bin\$(Configuration)\ + $(MSBuildThisFileDirectory)bin\$(Configuration)\ + true + true + True True - + + + + + + InspireFunction + xsfhlzh;vicwjb;liuqihong + InspireFunction + 基于.NET的二次开发基本类库. + MIT + true + https://gitee.com/inspirefunction/ifoxcad + https://gitee.com/inspirefunction/ifoxcad.git + git + IFox;C#;NET;Common;Basal + + + + \ No newline at end of file diff --git a/src/Basal/IFox.Basal.Shared/IFox.Basal.Shared.projitems b/src/Basal/IFox.Basal.Shared/IFox.Basal.Shared.projitems index 18c5d65..319c7e2 100644 --- a/src/Basal/IFox.Basal.Shared/IFox.Basal.Shared.projitems +++ b/src/Basal/IFox.Basal.Shared/IFox.Basal.Shared.projitems @@ -31,5 +31,8 @@ + + + \ No newline at end of file diff --git a/src/CAD/IFox.CAD.Shared/WindowsAPI/Enums.cs b/src/Basal/IFox.Basal.Shared/WindowsAPI/Enums.cs similarity index 99% rename from src/CAD/IFox.CAD.Shared/WindowsAPI/Enums.cs rename to src/Basal/IFox.Basal.Shared/WindowsAPI/Enums.cs index e70dfd6..ed76fd0 100644 --- a/src/CAD/IFox.CAD.Shared/WindowsAPI/Enums.cs +++ b/src/Basal/IFox.Basal.Shared/WindowsAPI/Enums.cs @@ -1,5 +1,5 @@ #if true -namespace IFoxCAD.Cad; +namespace IFoxCAD.Basal; // https://blog.csdn.net/qq_43812868/article/details/108587936 [Flags] diff --git a/src/CAD/IFox.CAD.Shared/WindowsAPI/MouseHook.cs b/src/Basal/IFox.Basal.Shared/WindowsAPI/MouseHook.cs similarity index 98% rename from src/CAD/IFox.CAD.Shared/WindowsAPI/MouseHook.cs rename to src/Basal/IFox.Basal.Shared/WindowsAPI/MouseHook.cs index 2d6b013..400e4a7 100644 --- a/src/CAD/IFox.CAD.Shared/WindowsAPI/MouseHook.cs +++ b/src/Basal/IFox.Basal.Shared/WindowsAPI/MouseHook.cs @@ -1,10 +1,4 @@ -namespace IFoxCAD.Cad; - -using System; -using System.Diagnostics; -using System.Drawing; -using System.Runtime.InteropServices; -using System.Windows.Forms; +namespace IFoxCAD.Basal; public class MouseHook { diff --git a/src/CAD/IFox.CAD.Shared/WindowsAPI/WindowsAPI.cs b/src/Basal/IFox.Basal.Shared/WindowsAPI/WindowsAPI.cs similarity index 98% rename from src/CAD/IFox.CAD.Shared/WindowsAPI/WindowsAPI.cs rename to src/Basal/IFox.Basal.Shared/WindowsAPI/WindowsAPI.cs index b1a7f92..8f8f75d 100644 --- a/src/CAD/IFox.CAD.Shared/WindowsAPI/WindowsAPI.cs +++ b/src/Basal/IFox.Basal.Shared/WindowsAPI/WindowsAPI.cs @@ -1,9 +1,6 @@ #define Marshal -using System.Diagnostics; -using System.Runtime.CompilerServices; -using System.Threading; -namespace IFoxCAD.Cad; +namespace IFoxCAD.Basal; public partial class WindowsAPI { #region kernel32 @@ -609,14 +606,14 @@ public Point3D(double x, double y, double z) Y = y; Z = z; } - public static implicit operator Point3D(Point3d pt) - { - return new Point3D(pt.X, pt.Y, pt.Z); - } - public static implicit operator Point3d(Point3D pt) - { - return new Point3d(pt.X, pt.Y, pt.Z); - } + //public static implicit operator Point3D(Point3d pt) + //{ + // return new Point3D(pt.X, pt.Y, pt.Z); + //} + //public static implicit operator Point3d(Point3D pt) + //{ + // return new Point3d(pt.X, pt.Y, pt.Z); + //} public override string ToString() => $"({X},{Y},{Z})"; public static Point3D Create(IntPtr lParam) diff --git a/src/Basal/IFox.Basal/GlobalUsings.cs b/src/Basal/IFox.Basal/GlobalUsings.cs index 9fac544..6eb467e 100644 --- a/src/Basal/IFox.Basal/GlobalUsings.cs +++ b/src/Basal/IFox.Basal/GlobalUsings.cs @@ -1,4 +1,4 @@ -/// 系统引用 +// 系统引用 global using System; global using System.Collections; global using System.Collections.Generic; @@ -9,3 +9,13 @@ global using System.ComponentModel; global using System.Runtime.InteropServices; + +global using System.Diagnostics; +global using System.Drawing; + +global using System.Windows.Forms; + +global using Microsoft.Win32; + +global using System.Runtime.CompilerServices; +global using System.Threading; \ No newline at end of file diff --git a/src/Basal/IFox.Basal/IFox.Basal.csproj b/src/Basal/IFox.Basal/IFox.Basal.csproj index 4c0606d..af00b68 100644 --- a/src/Basal/IFox.Basal/IFox.Basal.csproj +++ b/src/Basal/IFox.Basal/IFox.Basal.csproj @@ -1,10 +1,9 @@ - + NET35;NET40;NET45 - true - True - None + true + true diff --git a/src/CAD/Directory.Build.props b/src/CAD/Directory.Build.props index 1745862..889ff42 100644 --- a/src/CAD/Directory.Build.props +++ b/src/CAD/Directory.Build.props @@ -1,8 +1,8 @@  - 0.5.0-alpha6 - 支持源码包。 + 0.5.0-alpha7 + 调整winapi到basal。 diff --git a/src/CAD/IFox.CAD.Shared/Copyclip/EmfTool.cs b/src/CAD/IFox.CAD.Shared/Copyclip/EmfTool.cs index 773cf6d..32b9545 100644 --- a/src/CAD/IFox.CAD.Shared/Copyclip/EmfTool.cs +++ b/src/CAD/IFox.CAD.Shared/Copyclip/EmfTool.cs @@ -5,7 +5,7 @@ using System.Drawing; using System.Drawing.Imaging; using System.Text; -using static IFoxCAD.Cad.WindowsAPI; +using static IFoxCAD.Basal.WindowsAPI; using Point = System.Drawing.Point; using Size = System.Drawing.Size; diff --git a/src/CAD/IFox.CAD.Shared/Copyclip/TagClipboardInfo.cs b/src/CAD/IFox.CAD.Shared/Copyclip/TagClipboardInfo.cs index 272754a..978e9f8 100644 --- a/src/CAD/IFox.CAD.Shared/Copyclip/TagClipboardInfo.cs +++ b/src/CAD/IFox.CAD.Shared/Copyclip/TagClipboardInfo.cs @@ -1,9 +1,10 @@ namespace IFoxCAD.Cad; +using IFoxCAD.Com; using System; using System.Diagnostics; using System.Text; -using static IFoxCAD.Cad.WindowsAPI; +using static IFoxCAD.Basal.WindowsAPI; public class ClipboardEnv { @@ -40,7 +41,7 @@ public struct TagClipboardInfo : IEquatable #region 属性,可以改动 public string File => szTempFile; - public Point3d Point => dptInsert; + public Point3d Point => new Point3d(dptInsert.X, dptInsert.Y, dptInsert.Z); #pragma warning disable CA2211 // 非常量字段应当不可见 public static IntPtr AcadDwgview @@ -64,7 +65,7 @@ public TagClipboardInfo(string tmpFile, Point3d insert) szSourceFile = string.Empty; szSignature = "R15"; //恒定是这个 nFlags = 0; - dptInsert = insert; + dptInsert = new Point3D(insert.X, insert.Y, insert.Z); rectGDI = IntRect.Zero; nLen = 0; nType = 0; diff --git a/src/CAD/IFox.CAD.Shared/IFox.CAD.Shared.projitems b/src/CAD/IFox.CAD.Shared/IFox.CAD.Shared.projitems index ecb9c9a..61f9c88 100644 --- a/src/CAD/IFox.CAD.Shared/IFox.CAD.Shared.projitems +++ b/src/CAD/IFox.CAD.Shared/IFox.CAD.Shared.projitems @@ -89,8 +89,5 @@ - - - \ No newline at end of file diff --git a/src/CAD/IFox.CAD.Shared/Runtime/FileOpenMode.cs b/src/CAD/IFox.CAD.Shared/Runtime/FileOpenMode.cs index 55dcefd..b75d403 100644 --- a/src/CAD/IFox.CAD.Shared/Runtime/FileOpenMode.cs +++ b/src/CAD/IFox.CAD.Shared/Runtime/FileOpenMode.cs @@ -74,11 +74,11 @@ public static void SetFocusToDwgView() // -------->msctls_statusbar32 // -------->DwgView var docW = Acap.DocumentManager.MdiActiveDocument.Window.Handle; - var msctls_statusbar32 = IFoxCAD.Cad.WindowsAPI.GetTopWindow(docW); - window = IFoxCAD.Cad.WindowsAPI.GetWindow(msctls_statusbar32, 2U); + var msctls_statusbar32 = IFoxCAD.Basal.WindowsAPI.GetTopWindow(docW); + window = IFoxCAD.Basal.WindowsAPI.GetWindow(msctls_statusbar32, 2U); } if (window != IntPtr.Zero) - IFoxCAD.Cad.WindowsAPI.SetFocus(window); + IFoxCAD.Basal.WindowsAPI.SetFocus(window); } } } diff --git "a/tests/TestAcad09plus/\346\213\211\344\274\270\345\241\253\345\205\205/\345\261\217\345\271\225\345\235\220\346\240\207\350\275\254cad\345\235\220\346\240\207.cs" "b/tests/TestAcad09plus/\346\213\211\344\274\270\345\241\253\345\205\205/\345\261\217\345\271\225\345\235\220\346\240\207\350\275\254cad\345\235\220\346\240\207.cs" index 00e7c64..1856a61 100644 --- "a/tests/TestAcad09plus/\346\213\211\344\274\270\345\241\253\345\205\205/\345\261\217\345\271\225\345\235\220\346\240\207\350\275\254cad\345\235\220\346\240\207.cs" +++ "b/tests/TestAcad09plus/\346\213\211\344\274\270\345\241\253\345\205\205/\345\261\217\345\271\225\345\235\220\346\240\207\350\275\254cad\345\235\220\346\240\207.cs" @@ -2,7 +2,7 @@ namespace JoinBoxAcad; using System.Drawing; -using static IFoxCAD.Cad.WindowsAPI; +using static IFoxCAD.Basal.WindowsAPI; public partial class Screen { diff --git a/tests/TestConsole/TestConsole.csproj b/tests/TestConsole/TestConsole.csproj index df0548a..ca23f6f 100644 --- a/tests/TestConsole/TestConsole.csproj +++ b/tests/TestConsole/TestConsole.csproj @@ -13,7 +13,6 @@ - diff --git a/tests/TestShared/TestMarshal.cs b/tests/TestShared/TestMarshal.cs index 2b8514e..834fe12 100644 --- a/tests/TestShared/TestMarshal.cs +++ b/tests/TestShared/TestMarshal.cs @@ -1,5 +1,5 @@ using System.Diagnostics; -using static IFoxCAD.Cad.WindowsAPI; +using static IFoxCAD.Basal.WindowsAPI; namespace TestShared; -- Gitee From 85432e55da80c93a3e62d149a6396305407c9bcf Mon Sep 17 00:00:00 2001 From: vicwjb Date: Sat, 14 Jan 2023 21:10:36 +0800 Subject: [PATCH 647/675] =?UTF-8?q?=E6=9B=B4=E6=96=B0=E6=96=87=E6=A1=A3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 46 +++++++++++++++---- .../ExtensionMethod/EditorEx.cs | 2 +- .../IFox.CAD.Shared/ExtensionMethod/ObjEx.cs | 3 ++ 3 files changed, 41 insertions(+), 10 deletions(-) diff --git a/README.md b/README.md index 5f08fbe..083e1c3 100644 --- a/README.md +++ b/README.md @@ -68,19 +68,49 @@ public void Hello() 这段代码就是在cad的当前空间内添加了一条直线。 -- F6生成,然后打开cad,netload命令将刚刚生成的dll加载。 +- 生成,然后打开cad,netload命令将刚刚生成的dll加载。如果需要调试需要设置启动程序为cad。 -- 运行hello命令,然后缩放一下视图,现在一条直线和一个圆已经显示在屏幕上了。 +- 运行hello命令,然后缩放一下视图,现在一条直线和一个圆已经显示在屏幕上了 + +#### 三、屏蔽IFox的元组、索引、范围功能 + +特别提醒: 考虑到早期的框架没有提供System.Range类型、System.Index类型、System.ValueTuple类型,本项目IFox.Basal包里包含了他们。 如果引用了包含System.Range等类型的第三方包(如IndexRange等),请在项目文件中定义NOINDEX、NORANGE、NOVALUETUPLE常量,以避免重复定义。上述代码能起作用的前提是用源码包,普通包暂时无解。 + +```xml +NOINDEX;NORANGE;NOVALUETUPLE +``` + +#### 四、编译 IFox 源码工程 + +由于vs2022抛弃了某几个net版本,所以我们同时安装vs2019和vs2022,然后使用vs2022; +其中的原因是vs2019拥有全部net版本,而vs2022拥有最新的分析器和语法。 + +编译本项目需要你准备好git,具体的安装教程可以去网上搜索一下。当然也可以利用vs的git来完成。 + +首先在gitee上fork本项目到你的账号,然后clone到本地。 + +原生git使用命令行,打开终端/powershell/cmd,cd到你要存放的目录,然后运行下面的命令,把里面的yourname替换为你的名字,这样就在本地创建了一个ifoxcad文件夹,里面就是本项目的所有源码。 + +``` +git clone https://gitee.com/yourname/ifoxcad.git +``` + +当然也可以采用vs的图形化操作,打开vs,选择 克隆存储库-->填入仓库地址和存放路径-->点击克隆。新手小白推荐用此办法。 + +打开ifoxcad文件夹,双击解决方案文件,打开vs,等待项目打开,加载nuget包,然后生成就可以了。 + +**切记,不要用低版本的vs打开本项目,因为本项目采用了某些新的语法,所以老版本的vs是是兼容的。** + +#### IFoxCad 项目模版 + +可以在vs扩展菜单-管理扩展中搜索ifoxcad,即可安装项目模板。使用项目模版可以方便的创建支持多目标多版本的使用ifoxcad类库的项目和类。如果无法在vs的市场里下载,就去上面的QQ群里下载。 #### 软件架构及相关说明 - [软件架构说明](/docs/关于IFoxCAD的架构说明.md) - [扩展函数说明](/docs/关于扩展函数的说明.md) -#### 编译 IFox 源码工程 - -由于vs2022抛弃了某几个net版本,所以我们同时安装vs2019和vs2022,然后使用vs2022; -其中的原因是vs2019拥有全部net版本,而vs2022拥有最新的分析器和语法. +#### #### 让 IFox 作为您的子模块 @@ -97,9 +127,7 @@ IFox的[jing分支](https://gitee.com/inspirefunction/ifoxcad/tree/jing/)是一 方能启用08工程中缺少的09工程才有的类. 同时我们在`IFoxCAD.Cad`中提供了这两个例子. -#### IFoxCad 项目模版 - -可以在vs扩展菜单-管理扩展中搜索ifoxcad,即可安装项目模板。使用项目模版可以方便的创建支持多目标多版本的使用ifoxcad类库的项目和类。如果无法在vs的市场里下载,就去上面的QQ群里下载。 +#### #### 安装教程 diff --git a/src/CAD/IFox.CAD.Shared/ExtensionMethod/EditorEx.cs b/src/CAD/IFox.CAD.Shared/ExtensionMethod/EditorEx.cs index f9ec949..d432bf3 100644 --- a/src/CAD/IFox.CAD.Shared/ExtensionMethod/EditorEx.cs +++ b/src/CAD/IFox.CAD.Shared/ExtensionMethod/EditorEx.cs @@ -847,7 +847,7 @@ public static void ZoomWindow(this Editor ed, Extents3d ext) } /// - /// 缩放比例 + /// 按范围缩放 /// /// 命令行对象 /// 中心点 diff --git a/src/CAD/IFox.CAD.Shared/ExtensionMethod/ObjEx.cs b/src/CAD/IFox.CAD.Shared/ExtensionMethod/ObjEx.cs index 2ccc389..1ad2a17 100644 --- a/src/CAD/IFox.CAD.Shared/ExtensionMethod/ObjEx.cs +++ b/src/CAD/IFox.CAD.Shared/ExtensionMethod/ObjEx.cs @@ -1,5 +1,8 @@ namespace IFoxCAD.Cad; +/// +/// 对象扩展类 +/// public static class ObjEx { /// -- Gitee From 043c6fc14878d0a3b275529201c37ae886f29ba2 Mon Sep 17 00:00:00 2001 From: vicwjb Date: Sun, 15 Jan 2023 01:01:25 +0800 Subject: [PATCH 648/675] =?UTF-8?q?=E5=A2=9E=E5=8A=A0autoload=E7=B1=BB?= =?UTF-8?q?=EF=BC=8C=E5=B0=86=E5=88=9D=E5=A7=8B=E5=8C=96=E5=92=8C=E8=87=AA?= =?UTF-8?q?=E5=8A=A8=E5=8A=A0=E8=BD=BD=E5=9B=9E=E5=BD=92=E7=AE=80=E5=8D=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/Basal/Directory.Build.props | 4 +- src/CAD/Directory.Build.props | 4 +- .../IFox.CAD.Shared/IFox.CAD.Shared.projitems | 1 + src/CAD/IFox.CAD.Shared/Runtime/AutoLoad.cs | 129 ++++++++++++++++++ src/CAD/IFox.CAD.Shared/Runtime/Env.cs | 2 - tests/TestShared/CmdINI.cs | 18 ++- tests/TestShared/TestCadFilePath.cs | 30 ++++ tests/TestShared/TestShared.projitems | 1 + 8 files changed, 181 insertions(+), 8 deletions(-) create mode 100644 src/CAD/IFox.CAD.Shared/Runtime/AutoLoad.cs create mode 100644 tests/TestShared/TestCadFilePath.cs diff --git a/src/Basal/Directory.Build.props b/src/Basal/Directory.Build.props index 539bf91..b432f84 100644 --- a/src/Basal/Directory.Build.props +++ b/src/Basal/Directory.Build.props @@ -1,8 +1,8 @@  - 0.5.0-alpha7 - 调整winapi到basal。 + 0.5.0 + 发布0.5版本 diff --git a/src/CAD/Directory.Build.props b/src/CAD/Directory.Build.props index 889ff42..ac6414b 100644 --- a/src/CAD/Directory.Build.props +++ b/src/CAD/Directory.Build.props @@ -1,8 +1,8 @@  - 0.5.0-alpha7 - 调整winapi到basal。 + 0.5.0 + 发布0.5版本 diff --git a/src/CAD/IFox.CAD.Shared/IFox.CAD.Shared.projitems b/src/CAD/IFox.CAD.Shared/IFox.CAD.Shared.projitems index e75bb48..9983528 100644 --- a/src/CAD/IFox.CAD.Shared/IFox.CAD.Shared.projitems +++ b/src/CAD/IFox.CAD.Shared/IFox.CAD.Shared.projitems @@ -68,6 +68,7 @@ + diff --git a/src/CAD/IFox.CAD.Shared/Runtime/AutoLoad.cs b/src/CAD/IFox.CAD.Shared/Runtime/AutoLoad.cs new file mode 100644 index 0000000..da351b4 --- /dev/null +++ b/src/CAD/IFox.CAD.Shared/Runtime/AutoLoad.cs @@ -0,0 +1,129 @@ +namespace IFoxCAD.Cad; + +/// +/// 自动加载和初始化抽象类 +/// +public abstract class AutoLoad : IExtensionApplication +{ + private AssemInfo _info = new(); + + /// + /// 程序集的路径 + /// + public static FileInfo Location => new(Assembly.GetCallingAssembly().Location); + + /// + /// 程序集的目录 + /// + public static DirectoryInfo CurrDirectory => Location.Directory; + + /// + /// 获取程序集的目录 + /// + /// 程序集 + /// 路径对象 + public static DirectoryInfo GetDirectory(Assembly assem) + { + if (assem == null) + { + throw new(nameof(assem)); + } + return new FileInfo(assem.Location).Directory; + } + + /// + /// 初始化程序集信息 + /// + public AutoLoad() + { + Assembly assem = Assembly.GetCallingAssembly(); + _info.Loader = assem.Location; + _info.Fullname = assem.FullName; + _info.Name = assem.GetName().Name; + _info.LoadType = AssemLoadType.Startting; + + if (!SearchForReg()) + { + RegApp(); + AppendSupportPath(CurrDirectory.FullName); + } + + } + + #region RegApp + + private static RegistryKey GetAcAppKey() + { +#if NET35 + string key = HostApplicationServices.Current.RegistryProductRootKey; +#else + string key = HostApplicationServices.Current.UserRegistryProductRootKey; +#endif + RegistryKey ackey = Registry.CurrentUser.OpenSubKey(key, true); + return ackey.CreateSubKey("Applications"); + } + + private void AppendSupportPath(string path) + { +#if NET35 + string key = HostApplicationServices.Current.RegistryProductRootKey; +#else + string key = HostApplicationServices.Current.UserRegistryProductRootKey; +#endif + // 计算机\HKEY_CURRENT_USER\SOFTWARE\Autodesk\AutoCAD\R24.0\ACAD-4101:804 + RegistryKey ackey = Registry.CurrentUser.OpenSubKey($@"{key}\Profiles"); + + var listkey = ackey.GetSubKeyNames(); + foreach (var item in listkey) + { + var acadkey = ackey.OpenSubKey($@"{item}\General", true); + var name = "ACAD"; + var str = acadkey.GetValue(name)?.ToString(); + if (str is not null && !str.Contains(path)) + { + acadkey.SetValue(name, $@"{str}{path};"); + } + + } + + ackey.Close(); + } + + private bool SearchForReg() + { + RegistryKey appkey = GetAcAppKey(); + var regApps = appkey.GetSubKeyNames(); + return regApps.Contains(_info.Name); + } + + /// + /// 在注册表写入自动加载的程序集信息 + /// + public void RegApp() + { + RegistryKey appkey = GetAcAppKey(); + RegistryKey rk = appkey.CreateSubKey(_info.Name); + rk.SetValue("DESCRIPTION", _info.Fullname, RegistryValueKind.String); + rk.SetValue("LOADCTRLS", _info.LoadType, RegistryValueKind.DWord); + rk.SetValue("LOADER", _info.Loader, RegistryValueKind.String); + rk.SetValue("MANAGED", 1, RegistryValueKind.DWord); + appkey.Close(); + + } + +#endregion RegApp + +#region IExtensionApplication 成员 + + /// + /// 初始化函数 + /// + public abstract void Initialize(); + + /// + /// 结束函数 + /// + public abstract void Terminate(); + +#endregion IExtensionApplication 成员 +} diff --git a/src/CAD/IFox.CAD.Shared/Runtime/Env.cs b/src/CAD/IFox.CAD.Shared/Runtime/Env.cs index f80a4ee..e57dcda 100644 --- a/src/CAD/IFox.CAD.Shared/Runtime/Env.cs +++ b/src/CAD/IFox.CAD.Shared/Runtime/Env.cs @@ -1,5 +1,3 @@ -using System.Diagnostics; - namespace IFoxCAD.Cad; /// diff --git a/tests/TestShared/CmdINI.cs b/tests/TestShared/CmdINI.cs index c5679cf..a47007a 100644 --- a/tests/TestShared/CmdINI.cs +++ b/tests/TestShared/CmdINI.cs @@ -1,5 +1,5 @@ //#define givePeopleTest - +#if false using System.Diagnostics; namespace Test; @@ -166,4 +166,18 @@ public void Terminate() // } } } -#endif \ No newline at end of file +#endif +#endif + +public class Init : AutoLoad +{ + public override void Initialize() + { + Env.Print("loading..."); + } + + public override void Terminate() + { + Env.Print("unloading..."); + } +} \ No newline at end of file diff --git a/tests/TestShared/TestCadFilePath.cs b/tests/TestShared/TestCadFilePath.cs new file mode 100644 index 0000000..4a32dd5 --- /dev/null +++ b/tests/TestShared/TestCadFilePath.cs @@ -0,0 +1,30 @@ + +namespace Test; + +public class TestCadFilePath +{ + [CommandMethod("TestCadFilePath")] + public void TestCadFilePathfun() + { + string key = HostApplicationServices.Current.UserRegistryProductRootKey; + // 计算机\HKEY_CURRENT_USER\SOFTWARE\Autodesk\AutoCAD\R24.0\ACAD-4101:804 + RegistryKey ackey = Registry.CurrentUser.OpenSubKey(key); + var profileskey = ackey.OpenSubKey("Profiles"); + + var listkey = profileskey.GetSubKeyNames(); + foreach (var item in listkey) + { + var acadkey = profileskey.OpenSubKey($@"{item}\General",true); + var name = "ACAD"; + var str = acadkey.GetValue(name)?.ToString(); + if (str is not null && !str.Contains("nihao")) + { + Env.Print(str); + acadkey.SetValue(name, $@"{str}\nihao;", RegistryValueKind.String); + } + + + } + + } +} diff --git a/tests/TestShared/TestShared.projitems b/tests/TestShared/TestShared.projitems index a98011f..bd01c5c 100644 --- a/tests/TestShared/TestShared.projitems +++ b/tests/TestShared/TestShared.projitems @@ -14,6 +14,7 @@ + -- Gitee From 71c6596405c59159411503eee0d181b5b660927e Mon Sep 17 00:00:00 2001 From: vicwjb Date: Sun, 15 Jan 2023 17:44:39 +0800 Subject: [PATCH 649/675] =?UTF-8?q?=E4=BF=AE=E6=AD=A3autoload=E7=9A=84?= =?UTF-8?q?=E9=9A=90=E8=97=8F=E5=86=99=E6=94=AF=E6=8C=81=E6=96=87=E4=BB=B6?= =?UTF-8?q?=E8=B7=AF=E5=BE=84=E7=9A=84=E5=8A=9F=E8=83=BD=EF=BC=8C=E6=94=B9?= =?UTF-8?q?=E4=B8=BA=E6=89=8B=E5=8A=A8=E5=9C=A8=E5=88=9D=E5=A7=8B=E5=8C=96?= =?UTF-8?q?=E5=87=BD=E6=95=B0=E9=87=8C=E8=B0=83=E7=94=A8=E3=80=82?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/CAD/IFox.CAD.Shared/Runtime/AutoLoad.cs | 5 ++--- tests/TestShared/CmdINI.cs | 7 ++++++- 2 files changed, 8 insertions(+), 4 deletions(-) diff --git a/src/CAD/IFox.CAD.Shared/Runtime/AutoLoad.cs b/src/CAD/IFox.CAD.Shared/Runtime/AutoLoad.cs index da351b4..004ae50 100644 --- a/src/CAD/IFox.CAD.Shared/Runtime/AutoLoad.cs +++ b/src/CAD/IFox.CAD.Shared/Runtime/AutoLoad.cs @@ -15,7 +15,7 @@ public abstract class AutoLoad : IExtensionApplication /// /// 程序集的目录 /// - public static DirectoryInfo CurrDirectory => Location.Directory; + public static DirectoryInfo CurrentDirectory => Location.Directory; /// /// 获取程序集的目录 @@ -45,7 +45,6 @@ public AutoLoad() if (!SearchForReg()) { RegApp(); - AppendSupportPath(CurrDirectory.FullName); } } @@ -63,7 +62,7 @@ private static RegistryKey GetAcAppKey() return ackey.CreateSubKey("Applications"); } - private void AppendSupportPath(string path) + protected void AppendSupportPath(string path) { #if NET35 string key = HostApplicationServices.Current.RegistryProductRootKey; diff --git a/tests/TestShared/CmdINI.cs b/tests/TestShared/CmdINI.cs index a47007a..0eeff28 100644 --- a/tests/TestShared/CmdINI.cs +++ b/tests/TestShared/CmdINI.cs @@ -173,11 +173,16 @@ public class Init : AutoLoad { public override void Initialize() { + Env.Print("loading..."); + // 将程序的目录加入信任路径 + AppendSupportPath(CurrentDirectory.FullName); } public override void Terminate() { - Env.Print("unloading..."); + // 这里不能调用输出函数,因为这个函数执行的时候,已经没有editor对象了。 + // 所以如果不是想要在cad关闭的时候清理某些东西,这里不用写任何的代码。 + } } \ No newline at end of file -- Gitee From e6cca9ed99c6014c2f08ab9f106b0b6aaa9baa17 Mon Sep 17 00:00:00 2001 From: vicwjb Date: Sun, 15 Jan 2023 21:17:36 +0800 Subject: [PATCH 650/675] =?UTF-8?q?=E5=A2=9E=E5=8A=A0autoload=E5=8A=9F?= =?UTF-8?q?=E8=83=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 57 ++----------------------------- docs/autoreg.md | 64 +++++++++++++++++++++++++++++++++++ src/CAD/Directory.Build.props | 4 +-- 3 files changed, 69 insertions(+), 56 deletions(-) create mode 100644 docs/autoreg.md diff --git a/README.md b/README.md index 083e1c3..ca1cf7e 100644 --- a/README.md +++ b/README.md @@ -105,6 +105,8 @@ git clone https://gitee.com/yourname/ifoxcad.git 可以在vs扩展菜单-管理扩展中搜索ifoxcad,即可安装项目模板。使用项目模版可以方便的创建支持多目标多版本的使用ifoxcad类库的项目和类。如果无法在vs的市场里下载,就去上面的QQ群里下载。 +项目模版里的自动加载选择了简单api,ifox还提供了一套功能更强大的api,具体的可以参考[自动加载很初始化](/docs/autoreg/md)。 + #### 软件架构及相关说明 - [软件架构说明](/docs/关于IFoxCAD的架构说明.md) @@ -149,60 +151,7 @@ IFox的[jing分支](https://gitee.com/inspirefunction/ifoxcad/tree/jing/)是一 5. [WPF支持](/docs/WPF.md) -6. 天秀的自动加载与初始化 - - 为了将程序集的初始化和通过写注册表的方式实现自动加载统一设置,减少每次重复的工作量,类裤提供了`AutoRegAssem`抽象类来完成此功能,只要在需要初始化的类继承`AutoRegAssem`类,然后实现`Initialize()` 和`Terminate()`两个函数就可以了。 - 特别强调的是,一个程序集里只能有一个类继承,不管是不是同一个命名空间。 - - 但是为了满足开闭原则,使用特性进行分段初始化是目前最佳选择 - - ```c# - using Autodesk.AutoCAD.Runtime; - using IFoxCAD.Cad; - using System; - using System.Reflection; - - /* - * 自动执行接口 - * 这里必须要实现一次这个接口,才能使用 IFoxInitialize 特性进行自动执行 - */ - public class CmdINI : AutoRegAssem - { - // 这里可以写任何普通的函数,也可以写下面 AutoTest 类里的实现了 IFoxInitialize 特性的初始化函数 - // 继承AutoRegAssem的主要作用是写注册表用来自动加载dll,同时执行实现了 IFoxInitialize 特性的函数 - // 注意这里的自动执行是在cad启动后,加载了dll之后执行,而不是运行命令后执行。 - - [IFoxInitialize] - public void InitOne() - { - // TODO 您想在加载dll之后自动执行的函数 - // 可以随便在哪里类里 可以多次实现 IFoxInitialize 特性 - } - - } - - // 其他的类中的函数: - // 实现自动接口之后,在任意一个函数上面使用此特性,减少每次改动 CmdINI 类 - public class AutoTest - { - [IFoxInitialize] - public void Initialize() - { - // TODO 您想在加载dll之后自动执行的函数 - } - [IFoxInitialize] - public void InitTwo() - { - // TODO 您想在加载dll之后自动执行的函数 - // 可以随便在哪里类里 可以多次实现 IFoxInitialize 特性 - } - [IFoxInitialize(isInitialize: false)] // 特性的参数为false的时候就表示卸载时执行的函数 - public void Terminate() - { - // TODO 您想在关闭cad时自动执行的函数 - } - } - ``` +6. [自动加载与初始化](/docs/autoreg.md) 7. 天秀的打开模式提权 diff --git a/docs/autoreg.md b/docs/autoreg.md new file mode 100644 index 0000000..1b9d5c4 --- /dev/null +++ b/docs/autoreg.md @@ -0,0 +1,64 @@ +## 自动加载与初始化 + +### 1、简单版 + +为了将程序集的初始化和通过写注册表的方式实现自动加载统一设置,减少每次重复的工作量,类裤提供了`AutoLoad`抽象类来完成此功能,只要在需要初始化的类继承`AutoLoad`类,然后实现`Initialize()` 和 `Terminate()` 两个函数就可以了。 +特别强调的是,一个程序集里只能有一个类继承,不管是不是同一个命名空间。 + +如果要将dll的目录加入支持文件目录,请在 `Initialize` 函数中调用`AppendSupportPath(CurrentDirectory.FullName);` + +其他需要初始化执行的函数及设置都需要在 `Initialize` 函数中执行。 + +### 2、功能版 + +使用特性进行分段初始化是目前最佳选择 + +```c# + using Autodesk.AutoCAD.Runtime; + using IFoxCAD.Cad; + using System; + using System.Reflection; + +/* + * 自动执行接口 + * 这里必须要实现一次这个接口,才能使用 IFoxInitialize 特性进行自动执行 + */ +public class CmdINI : AutoRegAssem +{ + // 这里可以写任何普通的函数,也可以写下面 AutoTest 类里的实现了 IFoxInitialize 特性的初始化函数 + // 继承AutoRegAssem的主要作用是写注册表用来自动加载dll,同时执行实现了 IFoxInitialize 特性的函数 + // 注意这里的自动执行是在cad启动后,加载了dll之后执行,而不是运行命令后执行。 + + [IFoxInitialize] + public void InitOne() + { + // TODO 您想在加载dll之后自动执行的函数 + // 可以随便在哪里类里 可以多次实现 IFoxInitialize 特性 + } + +} + +// 其他的类中的函数: +// 实现自动接口之后,在任意一个函数上面使用此特性,减少每次改动 CmdINI 类 +public class AutoTest +{ + [IFoxInitialize] + public void Initialize() + { + // TODO 您想在加载dll之后自动执行的函数 + } + [IFoxInitialize] + public void InitTwo() + { + // TODO 您想在加载dll之后自动执行的函数 + // 可以随便在哪里类里 可以多次实现 IFoxInitialize 特性 + } + [IFoxInitialize(isInitialize: false)] // 特性的参数为false的时候就表示卸载时执行的函数 + public void Terminate() + { + // TODO 您想在关闭cad时自动执行的函数 + } +} +``` + +- 天秀的打开模式提权 diff --git a/src/CAD/Directory.Build.props b/src/CAD/Directory.Build.props index ac6414b..ecc2647 100644 --- a/src/CAD/Directory.Build.props +++ b/src/CAD/Directory.Build.props @@ -1,8 +1,8 @@  - 0.5.0 - 发布0.5版本 + 0.5.1 + 增加autoload。 -- Gitee From 6567d85f8faef26e55cadad412c6573a442f8820 Mon Sep 17 00:00:00 2001 From: vicwjb Date: Sun, 15 Jan 2023 21:19:02 +0800 Subject: [PATCH 651/675] =?UTF-8?q?=E6=9B=B4=E6=96=B0=E6=96=87=E6=A1=A3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 2 +- docs/autoreg.md | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index ca1cf7e..da83b6a 100644 --- a/README.md +++ b/README.md @@ -105,7 +105,7 @@ git clone https://gitee.com/yourname/ifoxcad.git 可以在vs扩展菜单-管理扩展中搜索ifoxcad,即可安装项目模板。使用项目模版可以方便的创建支持多目标多版本的使用ifoxcad类库的项目和类。如果无法在vs的市场里下载,就去上面的QQ群里下载。 -项目模版里的自动加载选择了简单api,ifox还提供了一套功能更强大的api,具体的可以参考[自动加载很初始化](/docs/autoreg/md)。 +项目模版里的自动加载选择了简单api,ifox还提供了一套功能更强大的api,具体的可以参考[自动加载很初始化](/docs/autoreg.md)。 #### 软件架构及相关说明 diff --git a/docs/autoreg.md b/docs/autoreg.md index 1b9d5c4..41056e4 100644 --- a/docs/autoreg.md +++ b/docs/autoreg.md @@ -61,4 +61,4 @@ public class AutoTest } ``` -- 天秀的打开模式提权 +- -- Gitee From e5ce0932c22e466038936d68e8c0bab5751e7562 Mon Sep 17 00:00:00 2001 From: vicwjb Date: Tue, 24 Jan 2023 00:38:07 +0800 Subject: [PATCH 652/675] =?UTF-8?q?=E5=AE=8C=E5=96=84=E6=96=87=E6=A1=A3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 119 +++++---- ...43\347\240\201\350\247\204\350\214\203.md" | 13 +- docs/DBTrans.md | 97 ++++--- docs/SelectionFilter.md | 250 +++++++++--------- docs/SymbolTable.md | 67 +++-- docs/autoreg.md | 6 +- ...66\346\236\204\350\257\264\346\230\216.md" | 8 +- 7 files changed, 271 insertions(+), 289 deletions(-) diff --git a/README.md b/README.md index da83b6a..1cb8b77 100644 --- a/README.md +++ b/README.md @@ -101,25 +101,33 @@ git clone https://gitee.com/yourname/ifoxcad.git **切记,不要用低版本的vs打开本项目,因为本项目采用了某些新的语法,所以老版本的vs是是兼容的。** -#### IFoxCad 项目模版 +#### 五、IFoxCad 项目模版 可以在vs扩展菜单-管理扩展中搜索ifoxcad,即可安装项目模板。使用项目模版可以方便的创建支持多目标多版本的使用ifoxcad类库的项目和类。如果无法在vs的市场里下载,就去上面的QQ群里下载。 -项目模版里的自动加载选择了简单api,ifox还提供了一套功能更强大的api,具体的可以参考[自动加载很初始化](/docs/autoreg.md)。 +项目模版里的自动加载选择了简单api,ifox还提供了一套功能更强大的api,具体的可以参考[自动加载和初始化](/docs/autoreg.md)。 -#### 软件架构及相关说明 - -- [软件架构说明](/docs/关于IFoxCAD的架构说明.md) -- [扩展函数说明](/docs/关于扩展函数的说明.md) +#### 六、使用IFoxCad的几种方式 -#### +目前ifox提供了三种使用方式,**建议一般的用户使用第二种源码包的形式。有志于本项目发展并想提交点代码的可以选择第三种。** -#### 让 IFox 作为您的子模块 +- 第一种是直接使用普通的nuget包。 + + 此种方式使用便捷,只要在项目中引用了IFox.CAD.ACAD的包,就可以直接使用了。缺点一是无法控制ifox提供的元组功能的屏蔽,导致和其他的三方包的冲突;二是生成目录里带有ifox的dll。 -IFox的[jing分支](https://gitee.com/inspirefunction/ifoxcad/tree/jing/)是一个多cad版本分支,您可以利用此作为您的[git项目子模块](https://www.cnblogs.com/JJBox/p/13876501.html#_label13). +- 第二种是使用源码包。 + + 此种方式使用便捷,只要在项目中引用了IFox.Basal.Source和IFox.CAD.Source两个nuget包就可以直接使用了。优点就是使用简单,生成的目录里没有ifox的dll,同时还可以通过定义预处理常量的方式屏蔽ifox提供的元组等功能。缺点就是无法修改源码。 -子模块是以`共享工程`的方式加入到您的工程的,其为`IFoxCAD.Cad.Shared`: +- 第三种是使用git子模块。 + + 此种方法使用步骤复杂,需要熟悉git及其子模块的使用,需要引用ifox里的共享项目文件。优点就是可以使用最新的代码,可以修改代码。具体的可以参考如下说明进行: +- **让 IFox 作为您的子模块** + + IFox的[jing分支](https://gitee.com/inspirefunction/ifoxcad/tree/jing/)是一个多cad版本分支,您可以利用此作为您的[git项目子模块](https://www.cnblogs.com/JJBox/p/13876501.html#_label13). + + 子模块是以`共享工程`的方式加入到您的工程的,其为`IFoxCAD.Cad.Shared`: 1. 千万不要用`IFoxCAD.Cad`内的工程作为引用,否则您将遭遇cad加载失效. 2. 一些全局命名空间的缺少,我们也建议您使用全局命名空间来补充, @@ -129,49 +137,12 @@ IFox的[jing分支](https://gitee.com/inspirefunction/ifoxcad/tree/jing/)是一 方能启用08工程中缺少的09工程才有的类. 同时我们在`IFoxCAD.Cad`中提供了这两个例子. -#### - -#### 安装教程 - -1. 新建net standard 类库 -2. 修改项目`.csproj`的`TargetFrameworks`为net45,保存重加载项目,这里需要注意和cad版本对照. -3. 右键项目,管理nuget程序包,搜索ifoxcad,安装最新版就可以了. - -#### 使用说明 - -1. 快速入门 - - - - -2. [事务管理器用法](/docs/DBTrans.md) - -3. [选择集过滤器用法](/docs/SelectionFilter.md) - -4. [符号表用法](/docs/SymbolTable.md) - -5. [WPF支持](/docs/WPF.md) - -6. [自动加载与初始化](/docs/autoreg.md) - -7. 天秀的打开模式提权 - - 由于cad的对象是有打开模式,是否可写等等,为了安全起见,在处理对象时,一般是用读模式打开,然后需要写数据的时候在提权为写模式,然后在降级到读模式,但是这个过程中,很容易漏掉某些步骤,然后cad崩溃。为了处理这些情况,内裤提供了提权类来保证读写模式的有序转换。 - - ```c# - using(line.ForWrite()) // 开启对象写模式提权事务 - { - // 处理代码 - } // 关闭事务自动处理读写模式 - ``` - -8. 未完待续。。。。 - -#### 屏蔽IFox.Basal的元组功能 - -由于c#在每个版本提供的元组功能不一样(有的中间版本缺少),所以IFox内置了元组功能,但是内置元组又会引起某些用户的工程冲突. - -因此您需要制作一个影子工程: - +4. 上面的例子告诉了大家如何使用子模块,建议直接利用testjingsource分支进行操作。 +- **屏蔽IFox.Basal的元组功能** + + 由于c#在每个版本提供的元组功能不一样(有的中间版本缺少),所以IFox内置了元组功能,但是内置元组又会引起某些用户的工程冲突. + + 因此您需要制作一个影子工程: 1. 您需要具备使用git子模块的能力,引用jing分支中的源码. 子模块是为了保证您不修改IFox项目,因为你需要定期`git pull`更新组织提供的内容. @@ -188,15 +159,49 @@ IFox的[jing分支](https://gitee.com/inspirefunction/ifoxcad/tree/jing/)是一 ```xml - - + + ``` 5. 修改 .csproj(影) 引入微软提供的元组 nuget: `System.ValueTuple` (或者你喜欢的) 6. 解决方案加入 .csproj(影) 之后被内部其他项目引用. + + 这个方法便可以把 影子工程 独立在IFox项目外,令`git pull`仍然有效, + + 并且 本体工程 不做大更改的时候,影子工程更新幅度非常少,也多亏csproj改版了,不然也没有这个骚操作. + +#### 软件架构及相关说明 + +1. [软件架构说明](/docs/关于IFoxCAD的架构说明.md) + +2. [扩展函数说明](/docs/关于扩展函数的说明.md) + +3. [事务管理器用法](/docs/DBTrans.md) -这个方法便可以把 影子工程 独立在IFox项目外,令`git pull`仍然有效, +4. [选择集过滤器用法](/docs/SelectionFilter.md) + +5. [符号表用法](/docs/SymbolTable.md) + +6. [WPF支持](/docs/WPF.md) + +7. [自动加载与初始化](/docs/autoreg.md) + +8. 天秀的打开模式提权 + + 由于cad的对象是有打开模式,是否可写等等,为了安全起见,在处理对象时,一般是用读模式打开,然后需要写数据的时候在提权为写模式,然后在降级到读模式,但是这个过程中,很容易漏掉某些步骤,然后cad崩溃。为了处理这些情况,内裤提供了提权类来保证读写模式的有序转换。 + + ```csharp + // 第一种方式,采用的是事务管理的模式 + using(line.ForWrite()) // 开启对象写模式提权事务 + { + // 处理代码 + } // 关闭事务自动处理读写模式 + // 第二种方式,采用的是委托的形式 + line.ForWrite(e => { + // 处理代码 + }); + ``` -并且 本体工程 不做大更改的时候,影子工程更新幅度非常少,也多亏csproj改版了,不然也没有这个骚操作. +9. 未完待续。。。。 diff --git "a/docs/0x01\344\273\243\347\240\201\350\247\204\350\214\203.md" "b/docs/0x01\344\273\243\347\240\201\350\247\204\350\214\203.md" index e00e396..26f0909 100644 --- "a/docs/0x01\344\273\243\347\240\201\350\247\204\350\214\203.md" +++ "b/docs/0x01\344\273\243\347\240\201\350\247\204\350\214\203.md" @@ -29,6 +29,7 @@ foreach (xx in yyy) if(!flag) 其他业务(); ``` + Good: ```c# @@ -47,7 +48,6 @@ else 主要原因是统一业务在判断分支上,能够更清晰分离逻辑代码和业务代码. - ### 0x02 分离逻辑代码和业务代码 ![img](0x01%E4%BB%A3%E7%A0%81%E8%A7%84%E8%8C%83.assets/2HJE@WH1%60PPUBOH2ZFL$BT.png) @@ -96,8 +96,6 @@ vs --> 选项 --> 文本编辑器 --> c# -->代码样式 --> 右页,基于设置 以后每次打开工程vs会自动识别这个 .editorconfig 文件,而不会用你电脑默认设置的. - - ### 0x04 所有的注释符号//后面加空格 利用此正则替换: @@ -119,8 +117,6 @@ aa//https:https://github.com/ //------------ ``` - - ## 文件管理规范 ### 0x01 (软性) @@ -130,14 +126,7 @@ aa//https:https://github.com/ .vs控制台的默认所在路径会是sln, 为了避免输入不同的相对路径时候发生错误,因而规定 - - ### 0x02 git子模块往往写入到一个统一的文件夹: assets 主要是明白哪些是人家的,哪些是我的. - - - - - diff --git a/docs/DBTrans.md b/docs/DBTrans.md index 5e75860..c805730 100644 --- a/docs/DBTrans.md +++ b/docs/DBTrans.md @@ -16,7 +16,7 @@ 事务管理器类的类名为:DBTrans。开启默认的事务管理器写法为: -```c# +```csharp using (DBTrans tr = new DBTrans()) { .... @@ -33,21 +33,21 @@ Ifoxcad 类库的符号表其实是一个符号表的泛型类,直接将符号 Ifoxcad 类库里采用如下的符号来表示9大符号表。 -| 符号表名 | 符号表含义 | -| :------------: | :--------: | -| BlockTable | 块表 | -| LayerTable | 图层表 | +| 符号表名 | 符号表含义 | +|:--------------:|:-----:| +| BlockTable | 块表 | +| LayerTable | 图层表 | | DimStyleTable | 标注样式表 | -| LinetypeTable | 线型表 | -| RegAppTable | 应用程序表 | +| LinetypeTable | 线型表 | +| RegAppTable | 应用程序表 | | TextStyleTable | 字体样式表 | -| UcsTable | 坐标系表 | -| ViewportTable | 视口表 | -| ViewTable | 视图表 | +| UcsTable | 坐标系表 | +| ViewportTable | 视口表 | +| ViewTable | 视图表 | **然后怎么使用呢?使用符号表一共分几步呢?** -```c# +```csharp using (DBTrans tr = new DBTrans()) // 第一步,开启事务 { var layerTable = tr.LayerTable;// 第二步,获取图层表 @@ -56,61 +56,61 @@ using (DBTrans tr = new DBTrans()) // 第一步,开启事务 上面是一个获取层表的例子,其他的符号表都是一样的写法,因为这些符号表都是事务管理器的属性。那么获取到符号表之后能做些什么? -- **向符号表里添加元素** - - ```c# +- **向符号表里添加元素** + + ```csharp using (DBTrans tr = new DBTrans()) { // 第一步,开启事务 - var layerTable = tr.LayerTable; - // 第二步,获取图层表 - layerTable.Add("1");// 返回值为ObjectId - // 第三步,向层表里添加一个元素,也就是新建一个图层。 + var layerTable = tr.LayerTable; + // 第二步,获取图层表 + layerTable.Add("1");// 返回值为ObjectId + // 第三步,向层表里添加一个元素,也就是新建一个图层。 } // 事务结束并自动提交 ``` - + 每个符号表都有Add函数,而且提供了不止一个重载函数。 -- **添加和获取符号表里的元素** - +- **添加和获取符号表里的元素** + 想要添加和获取符号表内的某个元素非常的简单: - - ```c# + + ```csharp using (DBTrans tr = new DBTrans()) // 第一步,开启事务 { - var layerTable = tr.LayerTable; // 第二步,获取图层表 - layerTable.Add("1"); // 第三步,添加名为“1”的图层,即新建图层 - ObjectId id = layerTable["1"]; // 第四步,获取图层“1”的id。 + var layerTable = tr.LayerTable; // 第二步,获取图层表 + layerTable.Add("1"); // 第三步,添加名为“1”的图层,即新建图层 + ObjectId id = layerTable["1"]; // 第四步,获取图层“1”的id。 } // 事务结束并自动提交 ``` - + 每个符号表都提供了索引形式的获取元素id的写法。 -- **线型表** - - ```c# +- **线型表** + + ```csharp // 两种方式 // 第一种,直接调用tr.LinetypeTable.Add("hah")函数,然后对返回值ObjectId做具体的操作。 // 第二种,直接在Action委托里把相关的操作完成。 tr.LinetypeTable.Add( - "hah", - ltt => - { - ltt.AsciiDescription = "虚线"; - ltt.PatternLength = 0.95; //线型的总长度 - ltt.NumDashes = 4; //组成线型的笔画数目 - ltt.SetDashLengthAt(0, 0.5); //0.5个单位的划线 - ltt.SetDashLengthAt(1, -0.25); //0.25个单位的空格 - ltt.SetDashLengthAt(2, 0); // 一个点 - ltt.SetDashLengthAt(3, -0.25); //0.25个单位的空格 - }); + "hah", + ltt => + { + ltt.AsciiDescription = "虚线"; + ltt.PatternLength = 0.95; //线型的总长度 + ltt.NumDashes = 4; //组成线型的笔画数目 + ltt.SetDashLengthAt(0, 0.5); //0.5个单位的划线 + ltt.SetDashLengthAt(1, -0.25); //0.25个单位的空格 + ltt.SetDashLengthAt(2, 0); // 一个点 + ltt.SetDashLengthAt(3, -0.25); //0.25个单位的空格 + }); // 这段代码同时演示了 ifoxcad 类库关于符号表的public ObjectId Add(string name, Action action)这个函数的用法。 // 或者直接调用: tr.LinetypeTable.Add("hah", "虚线",0.95,new double[]{0.5,-0.25,0,-0.25}); // 获取线型表 tr.LinetypeTable["hah"]; ``` - - **其他符号表的操作类同。如果类库没有提供的Add函数的重载,那么Action委托可以完成你想完成的所有事情。** + + **其他符号表的操作类同。如果类库没有提供的Add函数的重载,那么Action委托可以完成你想完成的所有事情。** ## 基础属性操作 @@ -125,16 +125,15 @@ using (DBTrans tr = new DBTrans()) // 第一步,开启事务 ## 字典操作(未完待续) - 扩展字典 - + `SetXRecord` 保存扩展数据到字典 - + `GetXRecord ` 获取扩展数据 - 对象字典 - + `SetToDictionary` 保存数据到字典 - + `GetFromDictionary` 从字典获取数据 - + `GetSubDictionary` 获取子对象字典 - diff --git a/docs/SelectionFilter.md b/docs/SelectionFilter.md index 1e50098..bc31b55 100644 --- a/docs/SelectionFilter.md +++ b/docs/SelectionFilter.md @@ -9,8 +9,8 @@ 默认的使用桌子api来创建选择集(带过滤器)分三步: 1. 创建一个TypedValue数组来定义过滤器条件 - - ```c# + + ```csharp TypedValue[] acTypValAr = new TypedValue[1]; // 创建数组 acTypValAr.SetValue(new TypedValue((int)DxfCode.Start, "CIRCLE"), 0); // 添加一个过滤条件,例如选择圆 @@ -24,15 +24,15 @@ ``` 2. 创建SelectionFilter对象 - - ```c# + + ```csharp // 将过滤器条件赋值给 SelectionFilter 对象 SelectionFilter acSelFtr = new SelectionFilter(acTypValAr); ``` 3. 创建选择集 - - ```c# + + ```csharp // 请求用户在图形区域选择对象 PromptSelectionResult acSSPrompt; acSSPrompt = acDocEd.GetSelection(acSelFtr); @@ -42,147 +42,147 @@ 比如: - 1. 过滤半径大于等于5.0的圆 - - ```c# - TypedValue[] acTypValAr = { - new TypedValue((int)DxfCode.Start, "CIRCLE"), - new TypedValue((int)DxfCode.Operator, ">="), - new TypedValue(40, 5) - }; - ``` - - 2. 过滤单行文本或者多行文本 - - ```c# - TypedValue[] acTypValAr = { - new TypedValue((int)DxfCode.Operator, "") - }; - ``` - - 3. 更复杂的过滤条件呢?比如选择的对象为不是位于0图层的直线,或者位于2图层的组码10的x坐标>10,y坐标>10的非圆图元。 - - 对应的lisp代码如下: - - ```lisp - '((-4 . "") - (-4 . "not>") - (-4 . "") - (8 . "2") - (-4 . ">,>,*")(10 10 10 0) +1. 过滤半径大于等于5.0的圆 + + ```csharp + TypedValue[] acTypValAr = { + new TypedValue((int)DxfCode.Start, "CIRCLE"), + new TypedValue((int)DxfCode.Operator, ">="), + new TypedValue(40, 5) + }; + ``` + +2. 过滤单行文本或者多行文本 + + ```csharp + TypedValue[] acTypValAr = { + new TypedValue((int)DxfCode.Operator, "") + }; + ``` + +3. 更复杂的过滤条件呢?比如选择的对象为不是位于0图层的直线,或者位于2图层的组码10的x坐标>10,y坐标>10的非圆图元。 + + 对应的lisp代码如下: + + ```lisp + '((-4 . "") - (-4 . "or>")) - ``` - - 对应的c#代码: - - ```c# - TypedValue[] acTypValAr = { - new TypedValue((int)DxfCode.Operator, ""), - new TypedValue((int)DxfCode.Operator, "not>"), - new TypedValue((int)DxfCode.Operator, ""), - new TypedValue((int)DxfCode.LayerName, "2"), - new TypedValue((int)DxfCode.Operator, ">,>,*"), - new TypedValue(10, new Point3d(10,10,0)), - new TypedValue((int)DxfCode.Operator, "and>"), - new TypedValue((int)DxfCode.Operator, "or>") - }; - ``` - - 这个过滤器是不是看起来很乱,一眼看去根本不知道是要过滤什么,写起来也很麻烦。所以说,虽然桌子提供了api,但是简单的过滤条件很好用,但是复杂的过滤条件就很复杂了。 - - 因此NFox内裤提供了关于选择集过滤器的辅助类来帮助用户用更简单的方式来创建选择集的过滤器。 + (-4 . "not>") + (-4 . "") + (8 . "2") + (-4 . ">,>,*")(10 10 10 0) + (-4 . "and>") + (-4 . "or>")) + ``` + + 对应的c#代码: + + ```csharp + TypedValue[] acTypValAr = { + new TypedValue((int)DxfCode.Operator, ""), + new TypedValue((int)DxfCode.Operator, "not>"), + new TypedValue((int)DxfCode.Operator, ""), + new TypedValue((int)DxfCode.LayerName, "2"), + new TypedValue((int)DxfCode.Operator, ">,>,*"), + new TypedValue(10, new Point3d(10,10,0)), + new TypedValue((int)DxfCode.Operator, "and>"), + new TypedValue((int)DxfCode.Operator, "or>") + }; + ``` + + 这个过滤器是不是看起来很乱,一眼看去根本不知道是要过滤什么,写起来也很麻烦。所以说,虽然桌子提供了api,但是简单的过滤条件很好用,但是复杂的过滤条件就很复杂了。 + + 因此IFox内裤提供了关于选择集过滤器的辅助类来帮助用户用更简单的方式来创建选择集的过滤器。 ## 内裤过滤器对象与cad过滤器对应关系 IFoxCad内裤对于DxfCode.Operator枚举构建了一些辅助函数来表达关系运算和逻辑运算;提供了dxf函数来表达组码。其对应的关系如下表: -| 内裤过滤器对象、函数 | cad .net api 过滤器对象、函数、枚举 | 备注 | -| :------------------: | :---------------------------------: | :------------------------------: | -| OpFilter | SelectionFilter | 隐式转换 | -| OpOr | "" | | -| Op.Or | "" | | -| OpAnd | "" | | -| Op.And | "" | | -| OpNot | "" | | -| OpXor | "" | | -| OpEqual | 相等运算 | | -| OpComp | 比较运算符 | | -| Dxf() | 组码函数 | 仅用于过滤器中,不是组码操作函数 | -| ! | "" | | -| == | "=" | | -| != | "!=" | | -| > | ">" | | -| < | "<" | | -| >= | ">=" 或 ">,>,*" | ">,>,*"用于跟point3d比较 | -| <= | "<=" 或 "<,<,*" | "<,<,*"用于跟point3d比较 | -| & | "" | | -| ^ | "" | | -| \| | "" | | +| 内裤过滤器对象、函数 | cad .net api 过滤器对象、函数、枚举 | 备注 | +|:----------:|:------------------------:|:-------------------:| +| OpFilter | SelectionFilter | 隐式转换 | +| OpOr | "" | | +| Op.Or | "" | | +| OpAnd | "" | | +| Op.And | "" | | +| OpNot | "" | | +| OpXor | "" | | +| OpEqual | 相等运算 | | +| OpComp | 比较运算符 | | +| Dxf() | 组码函数 | 仅用于过滤器中,不是组码操作函数 | +| ! | "" | | +| == | "=" | | +| != | "!=" | | +| > | ">" | | +| < | "<" | | +| >= | ">=" 或 ">,>,*" | ">,>,*"用于跟point3d比较 | +| <= | "<=" 或 "<,<,*" | "<,<,*"用于跟point3d比较 | +| & | "" | | +| ^ | "" | | +| \| | "" | | ## 具体用法 IFoxCad内裤提供了三种方式来构建过滤器,其实大同小异,就是写法不一样,用户可以根据自己的喜好来选择。 - 第一种 - - ```c# + + ```csharp var fd = - new OpOr //定义一个 (-4 . "") - { - !new OpAnd //定义(-4 . "")(-4 . "not>") - { - { 0, "line" }, //{组码,组码值} - { 8, "0" }, //{组码,组码值} - }, - new OpAnd //定义(-4 . "") - { - !new OpEqual(0, "circle"), //定义(-4 . "") - { 8, "2" }, //{组码,组码值} - { 10, new Point3d(10,10,0), ">,>,*" } //(-4 . ">,>,*")(10 10 10 0) - }, - }; + new OpOr //定义一个 (-4 . "") + { + !new OpAnd //定义(-4 . "")(-4 . "not>") + { + { 0, "line" }, //{组码,组码值} + { 8, "0" }, //{组码,组码值} + }, + new OpAnd //定义(-4 . "") + { + !new OpEqual(0, "circle"), //定义(-4 . "") + { 8, "2" }, //{组码,组码值} + { 10, new Point3d(10,10,0), ">,>,*" } //(-4 . ">,>,*")(10 10 10 0) + }, + }; editor.SelectAll(fd); //这里直接传入fd就可以了 ``` - + 以上代码的含义为:选择的对象为不是位于0图层的直线,或者位于2图层的组码10的x坐标>10,y坐标>10的非圆图元。其同含义的lisp代码如下: - + ```lisp '((-4 . "") - (-4 . "not>") - (-4 . "") + (-4 . "not>") + (-4 . "") (8 . "2") - (-4 . ">,>,*")(10 10 10 0) + (-4 . ">,>,*")(10 10 10 0) (-4 . "and>") (-4 . "or>")) ``` - 第二种 - - ```c# + + ```csharp var p = new Point3d(10, 10, 0); var f = OpFilter.Bulid(e => !(e.Dxf(0) == "line" & e.Dxf(8) == "0") @@ -191,12 +191,12 @@ IFoxCad内裤提供了三种方式来构建过滤器,其实大同小异,就 & e.Dxf(10) >= p); editor.SelectAll(f); //这里直接传入f就可以了 ``` - + 代码含义如第一种。 - 第三种 - - ```c# + + ```csharp var f2 = OpFilter.Bulid( e =>e.Or( !e.And(e.Dxf(0) == "line", e.Dxf(8) == "0"), @@ -204,5 +204,5 @@ IFoxCad内裤提供了三种方式来构建过滤器,其实大同小异,就 ); editor.SelectAll(f2); //这里直接传入f2就可以了 ``` - + 代码含义如第一种,第三种和第二种的写法非常像,区别就是关于 and 、or 、not 等运算符,一个是采用c#的语法,一个是采用定义的函数。and 与&等价,or与|等价,not 与!等价。 \ No newline at end of file diff --git a/docs/SymbolTable.md b/docs/SymbolTable.md index 0d45043..a5144ba 100644 --- a/docs/SymbolTable.md +++ b/docs/SymbolTable.md @@ -2,21 +2,21 @@ 每个图形文件都包含有9个固定的符号表。不能往数据库里添加新的符号表。如图层表(LayerTable),其中包含图层表记录,还有块表(BlockTable),其中包含块表记录等。所有的图形实体(线、圆、弧等等)都属于一个块表记录。缺省情况下,任何图形文件都包含为模型空间和图纸空间预定义的块表记录。每个符号表都有对应的符号表记录,可以理解为符号表是一个集合,而符号表记录是这个集合的元素。CAD的符号表和符号表记录的对应关系如下: -| 名称 | 符号表 | 符号表记录 | -| :------------: | :------------: | :------------------: | -| 块表 | BlockTable | BlockTableRecord | -| 标注样式表 | DimStyleTable | DimStyleTableRecord | -| 图层表 | LayerTable | LayerTableRecord | -| 线型表 | LinetypeTable | LinetypeTableRecord | -| 注册应用程序表 | RegAppTable | RegAppTableRecord | -| 字体样式表 | TextStyleTable | TextStyleTableRecord | -| 坐标系表 | UcsTable | UcsTableRecord | -| 视口表 | ViewportTable | ViewportTableRecord | -| 视图表 | ViewTable | ViewTableRecord | +| 名称 | 符号表 | 符号表记录 | +|:-------:|:--------------:|:--------------------:| +| 块表 | BlockTable | BlockTableRecord | +| 标注样式表 | DimStyleTable | DimStyleTableRecord | +| 图层表 | LayerTable | LayerTableRecord | +| 线型表 | LinetypeTable | LinetypeTableRecord | +| 注册应用程序表 | RegAppTable | RegAppTableRecord | +| 字体样式表 | TextStyleTable | TextStyleTableRecord | +| 坐标系表 | UcsTable | UcsTableRecord | +| 视口表 | ViewportTable | ViewportTableRecord | +| 视图表 | ViewTable | ViewTableRecord | 那么如何来操作这些符号表呢?下面是一个新建图层的例子: -```c# +```csharp Document acDoc = Application.DocumentManager.MdiActiveDocument; Database acCurDb = acDoc.Database; using (Transaction acTrans = acCurDb.TransactionManager.StartTransaction()) @@ -43,7 +43,7 @@ using (Transaction acTrans = acCurDb.TransactionManager.StartTransaction()) 上面的例子用了20多行的代码来完成一个很简单的功能,这就是AutoCAD提供的api太过于基础,没有进行进一步的封装造成。那么如果我们单独为图层表封装一个函数来处理图层表,其他的8个符号表也要同样的各自封装函数,这样看起来没什么问题,但是对于代码的复用却没有很好的考虑进去。仔细思考一下,其实对于符号来说无非就是增删改三个主要的操作等,对于符号表记录来说无非就是一些属性的操作,增加实体的操作等。那么有没有一种办法可以统一管理9个符号表呢?其实AutoCAD提供了9个符号表和符号表记录的抽象基类,SymbolTable和SymbolTableRecord,但是这两个类提供的功能又很简单,只有寥寥几个函数和属性,完全不能满足我们的需求。因此ifoxcad内裤提供了符号表类来封装9个符号表的大部分功能。那么用内裤来完成上述的操作是什么样子的呢?见下面的例子: -```c# +```csharp // 以下代码采用最新的c#版本语法 using var tr = new DBTrans(); // 打开事务 var layertable = tr.LayerTable.Add("MyLayer"); //添加图层 @@ -67,8 +67,6 @@ var layertable = tr.LayerTable.Add("MyLayer"); //添加图层 看,我教会了你操作图层表,那么其他的8个表你都会了,都是一样的操作。 - - ## 块表添加图元 一般的情况下,添加图元的操作都是要在事务里完成。目前大部分的添加图元的自定义函数都是DataBase或Editor对象的扩展函数。但是实际上添加图元的本质是读写图形数据库,具体的手段是对块表里的块表记录的读写。而实际的操作其实都是在事务里完成,所以符合cad操作规则的写法其实应该是事务作为一系列操作的主体来进行。因此ifoxcad内裤的封装思路为:扩展块表记录的函数,在事务管理器类里通过属性调用AddEntity函数来添加图元。 @@ -82,8 +80,8 @@ var layertable = tr.LayerTable.Add("MyLayer"); //添加图层 下面看示例: - 添加图元到当前空间 - - ```c# + + ```csharp // 以下代码采用最新的c#版本语法 using var tr = new DBTrans(); //开启事务管理器 var line1 = new Line(new Point3d(0, 0, 0), new Point3d(1, 1, 0)); //定义一个直线 @@ -91,8 +89,8 @@ var layertable = tr.LayerTable.Add("MyLayer"); //添加图层 ``` - 添加图元到模型/图纸空间 - - ```c# + + ```csharp // 以下代码采用最新的c#版本语法 using var tr = new DBTrans(); //开启事务管理器 var line1 = new Line(new Point3d(0, 0, 0), new Point3d(1, 1, 0)); //定义一个直线 @@ -102,48 +100,47 @@ var layertable = tr.LayerTable.Add("MyLayer"); //添加图层 ``` - 添加图元到块表 - - ```c# + + ```csharp // 以下代码采用最新的c#版本语法 using var tr = new DBTrans(); //开启事务管理器 var line1 = new Line(new Point3d(0, 0, 0), new Point3d(1, 1, 0)); //定义一个直线 var btr = tr.BlockTable.Add("test"); //定义一个块表记录 btr.AddEntity(line1); // 将直线添加到当前控件的块表记录 ``` - + 那么大家猜一猜,这个添加到块表是实现了一种什么样的功能。 - 块表 - + 块表这里需要特殊的说明一下: - + 比如说添加一个块,用如下代码: - + `tr.BlockTable.Add(blockName, btr => btr.AddEntity(ents));` - + 这里的blockName就是块名,ents就是图元列表。这种方式虽然可以更细粒度的控制定义的块。 - + 插入块参照,比如: - + `tr.InsertBlock(point,objectid); // 用于插入块参照,提供了重载函数来满足不同的需求` - 其他函数的介绍 - + `tr.BlockTable.GetRecord()` 函数,可以获取到块表的块表记录,同理层表等符号表也有同样的函数。 - + `tr.BlockTable.GetRecordFrom()` 函数,可以从文件拷贝块表记录,同理层表等符号表也有同样的函数。 - + `tr.BlockTable.GetBlockFrom()` 函数,从文件拷贝块定义,同理层表等符号表也有同样用途的函数。 - 添加图元函数 - + 内裤提供了一些便利的添加图元函数,可以不用先定义一个entity对象,然后添加到块表记录。 - - ```c# + + ```csharp using var tr = new DBTrans(); tr.CurrentSpace.AddLine(new Point3d(0,0,0),new Point3d(1,1,0)); tr.CurrentSpace.AddCircle(new Point3d(0,0,0),10); ``` - diff --git a/docs/autoreg.md b/docs/autoreg.md index 41056e4..72f377c 100644 --- a/docs/autoreg.md +++ b/docs/autoreg.md @@ -11,9 +11,9 @@ ### 2、功能版 -使用特性进行分段初始化是目前最佳选择 +使用特性进行分段初始化是目前最佳选择,下面的说明已和最新版本不符,等待修正吧。 -```c# +```csharp using Autodesk.AutoCAD.Runtime; using IFoxCAD.Cad; using System; @@ -60,5 +60,3 @@ public class AutoTest } } ``` - -- diff --git "a/docs/\345\205\263\344\272\216IFoxCAD\347\232\204\346\236\266\346\236\204\350\257\264\346\230\216.md" "b/docs/\345\205\263\344\272\216IFoxCAD\347\232\204\346\236\266\346\236\204\350\257\264\346\230\216.md" index 998e12c..f96d39d 100644 --- "a/docs/\345\205\263\344\272\216IFoxCAD\347\232\204\346\236\266\346\236\204\350\257\264\346\230\216.md" +++ "b/docs/\345\205\263\344\272\216IFoxCAD\347\232\204\346\236\266\346\236\204\350\257\264\346\230\216.md" @@ -51,7 +51,7 @@ d-->NamedDictionaris ``` 4. Transation 对象 - + ```mermaid flowchart LR; subgraph Transation @@ -64,12 +64,6 @@ h--write-->d[Database] g[Document or Database]--start-->f ``` - - - - - - IFoxCAD是基于NFOX类库的重制版,主要是提供一个最小化的内核,即DBTrans、SymbolTable、ResultData、SelectFilter等基础类,其他的功能都通过扩展方法的方式来实现。 其重制的原因在于原NFOX类库的封装过于厚重,初学者理解起来困难,重制版希望做到最小化的内核,方便理解,然后丰富的扩展函数来实现大量的功能,便于学着现有的教程中那套基于Database扩展函数封装思路的初学者快速的入门。 -- Gitee From 1040c5c8b8008707c9a6ff5490909ed7128c89ae Mon Sep 17 00:00:00 2001 From: vicwjb Date: Thu, 26 Jan 2023 22:45:19 +0800 Subject: [PATCH 653/675] =?UTF-8?q?=E6=B8=85=E7=90=86=E5=90=88=E5=B9=B6?= =?UTF-8?q?=E5=90=8E=E7=9A=84=E6=AE=8B=E4=BD=99=E6=96=87=E4=BB=B6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/IFoxCAD.Basal/ArrayEx.cs | 55 - src/IFoxCAD.Basal/CLS/Index.cs | 147 - src/IFoxCAD.Basal/CLS/Range.cs | 102 - src/IFoxCAD.Basal/CLS/RuntimeHelpers.cs | 45 - .../CLS/TupleElementNamesAttribute.cs | 50 - src/IFoxCAD.Basal/CLS/ValueTuple.cs | 2140 ------------ src/IFoxCAD.Basal/DictEx.cs | 15 - src/IFoxCAD.Basal/EnumEx.cs | 105 - src/IFoxCAD.Basal/GlobalUsings.cs | 9 - src/IFoxCAD.Basal/IFoxCAD.Basal.csproj | 34 - src/IFoxCAD.Basal/LinkedHashMap.cs | 171 - src/IFoxCAD.Basal/LinkedHashSet.cs | 221 -- src/IFoxCAD.Basal/LinqEx.cs | 335 -- src/IFoxCAD.Basal/ListEx.cs | 30 - src/IFoxCAD.Basal/LoopList.cs | 742 ----- src/IFoxCAD.Basal/LoopState.cs | 24 - src/IFoxCAD.Basal/RandomEx.cs | 202 -- src/IFoxCAD.Basal/Sortedset/ISet.cs | 65 - src/IFoxCAD.Basal/Sortedset/SR.cs | 907 ------ src/IFoxCAD.Basal/Sortedset/Sortedset.cs | 2896 ----------------- src/IFoxCAD.Basal/Sortedset/ThrowHelper.cs | 381 --- src/IFoxCAD.Basal/Sortedset/bithelper.cs | 170 - src/IFoxCAD.Cad/Algorithms/Graph/Graph.cs | 653 ---- src/IFoxCAD.Cad/Algorithms/Graph/IGraph.cs | 98 - .../Algorithms/QuadTree/QuadEntity.cs | 25 - .../Algorithms/QuadTree/QuadTree.cs | 259 -- .../Algorithms/QuadTree/QuadTreeEvn.cs | 26 - .../Algorithms/QuadTree/QuadTreeNode.cs | 820 ----- .../Algorithms/QuadTree/QuadTreeSelectMode.cs | 21 - src/IFoxCAD.Cad/Algorithms/QuadTree/Rect.cs | 605 ---- .../ExtensionMethod/BulgeVertexWidth.cs | 85 - .../ExtensionMethod/CollectionEx.cs | 224 -- src/IFoxCAD.Cad/ExtensionMethod/Curve2dEx.cs | 312 -- src/IFoxCAD.Cad/ExtensionMethod/Curve3dEx.cs | 557 ---- src/IFoxCAD.Cad/ExtensionMethod/CurveEx.cs | 723 ---- .../ExtensionMethod/DBDictionaryEx.cs | 389 --- src/IFoxCAD.Cad/ExtensionMethod/DBObjectEx.cs | 179 - src/IFoxCAD.Cad/ExtensionMethod/DBTransEx.cs | 92 - src/IFoxCAD.Cad/ExtensionMethod/EditorEx.cs | 1090 ------- .../ExtensionMethod/EntityBoundingInfo.cs | 264 -- src/IFoxCAD.Cad/ExtensionMethod/EntityEx.cs | 431 --- src/IFoxCAD.Cad/ExtensionMethod/Enums.cs | 174 - .../ExtensionMethod/Filer/DwgFiler.cs | 538 --- .../ExtensionMethod/Filer/DwgFilerEx.cs | 83 - .../ExtensionMethod/Filer/DxfFiler.cs | 219 -- src/IFoxCAD.Cad/ExtensionMethod/GeometryEx.cs | 677 ---- src/IFoxCAD.Cad/ExtensionMethod/JigEx.cs | 370 --- .../ExtensionMethod/JigExTransient.cs | 149 - src/IFoxCAD.Cad/ExtensionMethod/ObjEx.cs | 21 - src/IFoxCAD.Cad/ExtensionMethod/ObjectIdEx.cs | 92 - src/IFoxCAD.Cad/ExtensionMethod/PointEx.cs | 134 - .../ExtensionMethod/SelectionSetEx.cs | 101 - .../ExtensionMethod/SymbolTableEx.cs | 358 -- .../ExtensionMethod/SymbolTableRecordEx.cs | 463 --- src/IFoxCAD.Cad/ExtensionMethod/Tools.cs | 188 -- src/IFoxCAD.Cad/ExtensionMethod/XrefEx.cs | 652 ---- .../HatchConverter.cs" | 358 -- .../HatchEx.cs" | 15 - .../HatchInfo.cs" | 354 -- .../AttachmentPointHelper.cs" | 95 - .../TextEntityAdd.cs" | 62 - .../TextInfo.cs" | 178 - src/IFoxCAD.Cad/GlobalUsings.cs | 45 - src/IFoxCAD.Cad/IFoxCAD.Cad.csproj | 65 - src/IFoxCAD.Cad/ResultData/LispDottedPair.cs | 68 - src/IFoxCAD.Cad/ResultData/LispList.cs | 195 -- src/IFoxCAD.Cad/ResultData/TypedValueList.cs | 63 - src/IFoxCAD.Cad/ResultData/XRecordDataList.cs | 68 - src/IFoxCAD.Cad/ResultData/XdataList.cs | 68 - src/IFoxCAD.Cad/Runtime/AOP.cs | 99 - src/IFoxCAD.Cad/Runtime/AcadEMR.cs | 149 - src/IFoxCAD.Cad/Runtime/AcadVersion.cs | 70 - src/IFoxCAD.Cad/Runtime/AssemInfo.cs | 82 - src/IFoxCAD.Cad/Runtime/AutoRegAssem.cs | 175 - src/IFoxCAD.Cad/Runtime/CadVersion.cs | 92 - src/IFoxCAD.Cad/Runtime/DBTrans.cs | 639 ---- src/IFoxCAD.Cad/Runtime/Env.cs | 732 ----- src/IFoxCAD.Cad/Runtime/FileOpenMode.cs | 13 - src/IFoxCAD.Cad/Runtime/IAutoGo.cs | 311 -- src/IFoxCAD.Cad/Runtime/Log.cs | 453 --- src/IFoxCAD.Cad/Runtime/MethodInfoHelper.cs | 53 - src/IFoxCAD.Cad/Runtime/SymbolTable.cs | 364 --- src/IFoxCAD.Cad/Runtime/Utils.cs | 98 - src/IFoxCAD.Cad/SelectionFilter/OpComp.cs | 79 - src/IFoxCAD.Cad/SelectionFilter/OpEqual.cs | 86 - src/IFoxCAD.Cad/SelectionFilter/OpFilter.cs | 341 -- src/IFoxCAD.Cad/SelectionFilter/OpList.cs | 166 - src/IFoxCAD.Cad/SelectionFilter/OpLogi.cs | 128 - src/IFoxCAD.WPF/Converter.cs | 98 - src/IFoxCAD.WPF/DependencyObjectExtensions.cs | 34 - src/IFoxCAD.WPF/EventBindingExtension.cs | 210 -- src/IFoxCAD.WPF/RelayCommand.cs | 199 -- src/IFoxCAD.WPF/ViewModelBase.cs | 58 - 93 files changed, 26281 deletions(-) delete mode 100644 src/IFoxCAD.Basal/ArrayEx.cs delete mode 100644 src/IFoxCAD.Basal/CLS/Index.cs delete mode 100644 src/IFoxCAD.Basal/CLS/Range.cs delete mode 100644 src/IFoxCAD.Basal/CLS/RuntimeHelpers.cs delete mode 100644 src/IFoxCAD.Basal/CLS/TupleElementNamesAttribute.cs delete mode 100644 src/IFoxCAD.Basal/CLS/ValueTuple.cs delete mode 100644 src/IFoxCAD.Basal/DictEx.cs delete mode 100644 src/IFoxCAD.Basal/EnumEx.cs delete mode 100644 src/IFoxCAD.Basal/GlobalUsings.cs delete mode 100644 src/IFoxCAD.Basal/IFoxCAD.Basal.csproj delete mode 100644 src/IFoxCAD.Basal/LinkedHashMap.cs delete mode 100644 src/IFoxCAD.Basal/LinkedHashSet.cs delete mode 100644 src/IFoxCAD.Basal/LinqEx.cs delete mode 100644 src/IFoxCAD.Basal/ListEx.cs delete mode 100644 src/IFoxCAD.Basal/LoopList.cs delete mode 100644 src/IFoxCAD.Basal/LoopState.cs delete mode 100644 src/IFoxCAD.Basal/RandomEx.cs delete mode 100644 src/IFoxCAD.Basal/Sortedset/ISet.cs delete mode 100644 src/IFoxCAD.Basal/Sortedset/SR.cs delete mode 100644 src/IFoxCAD.Basal/Sortedset/Sortedset.cs delete mode 100644 src/IFoxCAD.Basal/Sortedset/ThrowHelper.cs delete mode 100644 src/IFoxCAD.Basal/Sortedset/bithelper.cs delete mode 100644 src/IFoxCAD.Cad/Algorithms/Graph/Graph.cs delete mode 100644 src/IFoxCAD.Cad/Algorithms/Graph/IGraph.cs delete mode 100644 src/IFoxCAD.Cad/Algorithms/QuadTree/QuadEntity.cs delete mode 100644 src/IFoxCAD.Cad/Algorithms/QuadTree/QuadTree.cs delete mode 100644 src/IFoxCAD.Cad/Algorithms/QuadTree/QuadTreeEvn.cs delete mode 100644 src/IFoxCAD.Cad/Algorithms/QuadTree/QuadTreeNode.cs delete mode 100644 src/IFoxCAD.Cad/Algorithms/QuadTree/QuadTreeSelectMode.cs delete mode 100644 src/IFoxCAD.Cad/Algorithms/QuadTree/Rect.cs delete mode 100644 src/IFoxCAD.Cad/ExtensionMethod/BulgeVertexWidth.cs delete mode 100644 src/IFoxCAD.Cad/ExtensionMethod/CollectionEx.cs delete mode 100644 src/IFoxCAD.Cad/ExtensionMethod/Curve2dEx.cs delete mode 100644 src/IFoxCAD.Cad/ExtensionMethod/Curve3dEx.cs delete mode 100644 src/IFoxCAD.Cad/ExtensionMethod/CurveEx.cs delete mode 100644 src/IFoxCAD.Cad/ExtensionMethod/DBDictionaryEx.cs delete mode 100644 src/IFoxCAD.Cad/ExtensionMethod/DBObjectEx.cs delete mode 100644 src/IFoxCAD.Cad/ExtensionMethod/DBTransEx.cs delete mode 100644 src/IFoxCAD.Cad/ExtensionMethod/EditorEx.cs delete mode 100644 src/IFoxCAD.Cad/ExtensionMethod/EntityBoundingInfo.cs delete mode 100644 src/IFoxCAD.Cad/ExtensionMethod/EntityEx.cs delete mode 100644 src/IFoxCAD.Cad/ExtensionMethod/Enums.cs delete mode 100644 src/IFoxCAD.Cad/ExtensionMethod/Filer/DwgFiler.cs delete mode 100644 src/IFoxCAD.Cad/ExtensionMethod/Filer/DwgFilerEx.cs delete mode 100644 src/IFoxCAD.Cad/ExtensionMethod/Filer/DxfFiler.cs delete mode 100644 src/IFoxCAD.Cad/ExtensionMethod/GeometryEx.cs delete mode 100644 src/IFoxCAD.Cad/ExtensionMethod/JigEx.cs delete mode 100644 src/IFoxCAD.Cad/ExtensionMethod/JigExTransient.cs delete mode 100644 src/IFoxCAD.Cad/ExtensionMethod/ObjEx.cs delete mode 100644 src/IFoxCAD.Cad/ExtensionMethod/ObjectIdEx.cs delete mode 100644 src/IFoxCAD.Cad/ExtensionMethod/PointEx.cs delete mode 100644 src/IFoxCAD.Cad/ExtensionMethod/SelectionSetEx.cs delete mode 100644 src/IFoxCAD.Cad/ExtensionMethod/SymbolTableEx.cs delete mode 100644 src/IFoxCAD.Cad/ExtensionMethod/SymbolTableRecordEx.cs delete mode 100644 src/IFoxCAD.Cad/ExtensionMethod/Tools.cs delete mode 100644 src/IFoxCAD.Cad/ExtensionMethod/XrefEx.cs delete mode 100644 "src/IFoxCAD.Cad/ExtensionMethod/\346\226\260\345\273\272\345\241\253\345\205\205/HatchConverter.cs" delete mode 100644 "src/IFoxCAD.Cad/ExtensionMethod/\346\226\260\345\273\272\345\241\253\345\205\205/HatchEx.cs" delete mode 100644 "src/IFoxCAD.Cad/ExtensionMethod/\346\226\260\345\273\272\345\241\253\345\205\205/HatchInfo.cs" delete mode 100644 "src/IFoxCAD.Cad/ExtensionMethod/\346\226\260\345\273\272\346\226\207\345\255\227/AttachmentPointHelper.cs" delete mode 100644 "src/IFoxCAD.Cad/ExtensionMethod/\346\226\260\345\273\272\346\226\207\345\255\227/TextEntityAdd.cs" delete mode 100644 "src/IFoxCAD.Cad/ExtensionMethod/\346\226\260\345\273\272\346\226\207\345\255\227/TextInfo.cs" delete mode 100644 src/IFoxCAD.Cad/GlobalUsings.cs delete mode 100644 src/IFoxCAD.Cad/IFoxCAD.Cad.csproj delete mode 100644 src/IFoxCAD.Cad/ResultData/LispDottedPair.cs delete mode 100644 src/IFoxCAD.Cad/ResultData/LispList.cs delete mode 100644 src/IFoxCAD.Cad/ResultData/TypedValueList.cs delete mode 100644 src/IFoxCAD.Cad/ResultData/XRecordDataList.cs delete mode 100644 src/IFoxCAD.Cad/ResultData/XdataList.cs delete mode 100644 src/IFoxCAD.Cad/Runtime/AOP.cs delete mode 100644 src/IFoxCAD.Cad/Runtime/AcadEMR.cs delete mode 100644 src/IFoxCAD.Cad/Runtime/AcadVersion.cs delete mode 100644 src/IFoxCAD.Cad/Runtime/AssemInfo.cs delete mode 100644 src/IFoxCAD.Cad/Runtime/AutoRegAssem.cs delete mode 100644 src/IFoxCAD.Cad/Runtime/CadVersion.cs delete mode 100644 src/IFoxCAD.Cad/Runtime/DBTrans.cs delete mode 100644 src/IFoxCAD.Cad/Runtime/Env.cs delete mode 100644 src/IFoxCAD.Cad/Runtime/FileOpenMode.cs delete mode 100644 src/IFoxCAD.Cad/Runtime/IAutoGo.cs delete mode 100644 src/IFoxCAD.Cad/Runtime/Log.cs delete mode 100644 src/IFoxCAD.Cad/Runtime/MethodInfoHelper.cs delete mode 100644 src/IFoxCAD.Cad/Runtime/SymbolTable.cs delete mode 100644 src/IFoxCAD.Cad/Runtime/Utils.cs delete mode 100644 src/IFoxCAD.Cad/SelectionFilter/OpComp.cs delete mode 100644 src/IFoxCAD.Cad/SelectionFilter/OpEqual.cs delete mode 100644 src/IFoxCAD.Cad/SelectionFilter/OpFilter.cs delete mode 100644 src/IFoxCAD.Cad/SelectionFilter/OpList.cs delete mode 100644 src/IFoxCAD.Cad/SelectionFilter/OpLogi.cs delete mode 100644 src/IFoxCAD.WPF/Converter.cs delete mode 100644 src/IFoxCAD.WPF/DependencyObjectExtensions.cs delete mode 100644 src/IFoxCAD.WPF/EventBindingExtension.cs delete mode 100644 src/IFoxCAD.WPF/RelayCommand.cs delete mode 100644 src/IFoxCAD.WPF/ViewModelBase.cs diff --git a/src/IFoxCAD.Basal/ArrayEx.cs b/src/IFoxCAD.Basal/ArrayEx.cs deleted file mode 100644 index 21947f6..0000000 --- a/src/IFoxCAD.Basal/ArrayEx.cs +++ /dev/null @@ -1,55 +0,0 @@ -namespace IFoxCAD.Basal; - -/* - * 由于linq的函数大部分带有状态机,而cad是一个单机程序, - * 使用状态机会变得缓慢,因此我们设计的时候着重于时间优化, - * 本工具类在着重于数组遍历时候替代linq - */ -public static class ArrayEx -{ - /// - /// 合并数组 - /// - /// - /// - public static T[] Combine2(this T[] a, T[] b) - { - var c = new T[a.Length + b.Length]; - Array.Copy(a, 0, c, 0, a.Length); - Array.Copy(b, 0, c, a.Length, b.Length); - return c; - } - - /// - /// 一维数组按规则消除
    - /// 本例适用于数值类型比较,特定规则比较
    - /// 如果是哈希比较,建议更改为: - /// set = new(); - /// foreach (var item in listInOut) - /// set.Add(item); - /// ]]> - ///
    - /// - /// 传入有重复成员的数组,原数组修改 - /// - /// 传出参数1:数组开头
    - /// 传出参数2:数组结尾
    - /// 返回值比较结尾为就移除
    - /// - public static void Deduplication(List lst, Func func) - { - // 头和尾比较,满足条件移除尾巴 - for (int i = 0; i < lst.Count; i++) - { - var first = lst[i]; - for (int j = lst.Count - 1; j > i/*符号是 >= 而且是i*/; j--) - { - var last = lst[j]; - if (func(first, last)) - lst.RemoveAt(j); - } - } - } - -} diff --git a/src/IFoxCAD.Basal/CLS/Index.cs b/src/IFoxCAD.Basal/CLS/Index.cs deleted file mode 100644 index a588dc6..0000000 --- a/src/IFoxCAD.Basal/CLS/Index.cs +++ /dev/null @@ -1,147 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// See the LICENSE file in the project root for more information. - -namespace System; - -using System.Runtime.CompilerServices; - -/// Represent a type can be used to index a collection either from the start or the end. -/// -/// Index is used by the C# compiler to support the new index syntax -/// -/// int[] someArray = new int[5] { 1, 2, 3, 4, 5 } ; -/// int lastElement = someArray[^1]; // lastElement = 5 -/// -/// -public readonly struct Index : IEquatable -{ - private readonly int _value; - - /// Construct an Index using a value and indicating if the index is from the start or from the end. - /// The index value. it has to be zero or positive number. - /// Indicating if the index is from the start or from the end. - /// - /// If the Index constructed from the end, index value 1 means pointing at the last element and index value 0 means pointing at beyond last element. - /// -#if NET45 - [MethodImpl(MethodImplOptions.AggressiveInlining)] -#endif - public Index(int value, bool fromEnd = false) - { - if (value < 0) - { - throw new ArgumentOutOfRangeException(nameof(value), "value must be non-negative"); - } - - if (fromEnd) - _value = ~value; - else - _value = value; - } - - // The following private constructors mainly created for perf reason to avoid the checks - private Index(int value) - { - _value = value; - } - - /// Create an Index pointing at first element. - public static Index Start => new(0); - - /// Create an Index pointing at beyond last element. - public static Index End => new(~0); - - /// Create an Index from the start at the position indicated by the value. - /// The index value from the start. -#if NET45 - [MethodImpl(MethodImplOptions.AggressiveInlining)] -#endif - public static Index FromStart(int value) - { - if (value < 0) - { - throw new ArgumentOutOfRangeException(nameof(value), "value must be non-negative"); - } - - return new Index(value); - } - - /// Create an Index from the end at the position indicated by the value. - /// The index value from the end. -#if NET45 - [MethodImpl(MethodImplOptions.AggressiveInlining)] -#endif - public static Index FromEnd(int value) - { - if (value < 0) - { - throw new ArgumentOutOfRangeException(nameof(value), "value must be non-negative"); - } - - return new Index(~value); - } - - /// Returns the index value. - public int Value - { - get - { - if (_value < 0) - return ~_value; - else - return _value; - } - } - - /// Indicates whether the index is from the start or the end. - public bool IsFromEnd => _value < 0; - - /// Calculate the offset from the start using the giving collection length. - /// The length of the collection that the Index will be used with. length has to be a positive value - /// - /// For performance reason, we don't validate the input length parameter and the returned offset value against negative values. - /// we don't validate either the returned offset is greater than the input length. - /// It is expected Index will be used with collections which always have non negative length/count. If the returned offset is negative and - /// then used to index a collection will get out of range exception which will be same affect as the validation. - /// -#if NET45 - [MethodImpl(MethodImplOptions.AggressiveInlining)] -#endif - public int GetOffset(int length) - { - int offset = _value; - if (IsFromEnd) - { - // offset = length - (~value) - // offset = length + (~(~value) + 1) - // offset = length + value + 1 - - offset += length + 1; - } - return offset; - } - - /// Indicates whether the current Index object is equal to another object of the same type. - /// An object to compare with this object - public override bool Equals(object? value) => value is Index index && _value == index._value; - - /// Indicates whether the current Index object is equal to another Index object. - /// An object to compare with this object - public bool Equals(Index other) => _value == other._value; - - /// Returns the hash code for this instance. - public override int GetHashCode() => _value; - - /// Converts integer number to an Index. - public static implicit operator Index(int value) => FromStart(value); - - /// Converts the value of the current Index object to its equivalent string representation. - public override string ToString() - { - if (IsFromEnd) - return "^" + ((uint)Value).ToString(); - - return ((uint)Value).ToString(); - } -} \ No newline at end of file diff --git a/src/IFoxCAD.Basal/CLS/Range.cs b/src/IFoxCAD.Basal/CLS/Range.cs deleted file mode 100644 index 43dce54..0000000 --- a/src/IFoxCAD.Basal/CLS/Range.cs +++ /dev/null @@ -1,102 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// See the LICENSE file in the project root for more information. - -namespace System; - -using System.Runtime.CompilerServices; - - -/// Represent a range has start and end indexes. -/// -/// Range is used by the C# compiler to support the range syntax. -/// -/// int[] someArray = new int[5] { 1, 2, 3, 4, 5 }; -/// int[] subArray1 = someArray[0..2]; // { 1, 2 } -/// int[] subArray2 = someArray[1..^0]; // { 2, 3, 4, 5 } -/// -/// -public readonly struct Range : IEquatable -{ - /// Represent the inclusive start index of the Range. - public Index Start { get; } - - /// Represent the exclusive end index of the Range. - public Index End { get; } - - /// Construct a Range object using the start and end indexes. - /// Represent the inclusive start index of the range. - /// Represent the exclusive end index of the range. - public Range(Index start, Index end) - { - Start = start; - End = end; - } - - /// Indicates whether the current Range object is equal to another object of the same type. - /// An object to compare with this object - public override bool Equals(object? value) => - value is Range r && - r.Start.Equals(Start) && - r.End.Equals(End); - - /// Indicates whether the current Range object is equal to another Range object. - /// An object to compare with this object - public bool Equals(Range other) => other.Start.Equals(Start) && other.End.Equals(End); - - /// Returns the hash code for this instance. - public override int GetHashCode() - { - return Start.GetHashCode() * 31 + End.GetHashCode(); - } - - /// Converts the value of the current Range object to its equivalent string representation. - public override string ToString() - { - return Start + ".." + End; - } - - /// Create a Range object starting from start index to the end of the collection. - public static Range StartAt(Index start) => new(start, Index.End); - - /// Create a Range object starting from first element in the collection to the end Index. - public static Range EndAt(Index end) => new(Index.Start, end); - - /// Create a Range object starting from first element to the end. - public static Range All => new(Index.Start, Index.End); - - /// Calculate the start offset and length of range object using a collection length. - /// The length of the collection that the range will be used with. length has to be a positive value. - /// - /// For performance reason, we don't validate the input length parameter against negative values. - /// It is expected Range will be used with collections which always have non negative length/count. - /// We validate the range is inside the length scope though. - /// -#if NET45 - [MethodImpl(MethodImplOptions.AggressiveInlining)] -#endif - // [CLSCompliant(false)] - public (int Offset, int Length) GetOffsetAndLength(int length) - { - int start; - Index startIndex = Start; - if (startIndex.IsFromEnd) - start = length - startIndex.Value; - else - start = startIndex.Value; - - int end; - Index endIndex = End; - if (endIndex.IsFromEnd) - end = length - endIndex.Value; - else - end = endIndex.Value; - - if ((uint)end > (uint)length || (uint)start > (uint)end) - { - throw new ArgumentOutOfRangeException(nameof(length)); - } - - return (start, end - start); - } -} \ No newline at end of file diff --git a/src/IFoxCAD.Basal/CLS/RuntimeHelpers.cs b/src/IFoxCAD.Basal/CLS/RuntimeHelpers.cs deleted file mode 100644 index 1008b09..0000000 --- a/src/IFoxCAD.Basal/CLS/RuntimeHelpers.cs +++ /dev/null @@ -1,45 +0,0 @@ -// #if NET35 -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// See the LICENSE file in the project root for more information. - -namespace System.Runtime.CompilerServices; - -// 编译提示多个程序集中定义,屏蔽不了,但是不影响编译 -// #pragma warning disable CS1685 // 类型与导入类型冲突 -public static class RuntimeHelpers -// #pragma warning restore CS1685 // 类型与导入类型冲突 -{ - /// - /// Slices the specified array using the specified range. - /// - public static T[] GetSubArray(T[] array, Range range) - { - if (array == null) - throw new ArgumentNullException(nameof(array)); - - (int offset, int length) = range.GetOffsetAndLength(array.Length); - - if (default(T)! != null || typeof(T[]) == array.GetType()) // TODO-NULLABLE: default(T) == null warning (https://github.com/dotnet/roslyn/issues/34757) - { - // We know the type of the array to be exactly T[]. - if (length == 0) - { - // return Array.Empty(); - return new T[0]; - } - - var dest = new T[length]; - Array.Copy(array, offset, dest, 0, length); - return dest; - } - else - { - // The array is actually a U[] where U:T. - T[] dest = (T[])Array.CreateInstance(array.GetType().GetElementType()!, length); - Array.Copy(array, offset, dest, 0, length); - return dest; - } - } -} -// #endif \ No newline at end of file diff --git a/src/IFoxCAD.Basal/CLS/TupleElementNamesAttribute.cs b/src/IFoxCAD.Basal/CLS/TupleElementNamesAttribute.cs deleted file mode 100644 index 9c3afb3..0000000 --- a/src/IFoxCAD.Basal/CLS/TupleElementNamesAttribute.cs +++ /dev/null @@ -1,50 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// See the LICENSE file in the project root for more information. - -using IFoxCAD.Basal; - -namespace System.Runtime.CompilerServices; - -/// -/// Indicates that the use of on a member is meant to be treated as a tuple with element names. -/// -[AttributeUsage(AttributeTargets.Field | AttributeTargets.Parameter | AttributeTargets.Property | AttributeTargets.ReturnValue | AttributeTargets.Class | AttributeTargets.Struct | AttributeTargets.Event)] -public sealed class TupleElementNamesAttribute : Attribute -{ - private readonly string[] _transformNames; - - /// - /// Initializes a new instance of the class. - /// - /// - /// Specifies, in a pre-order depth-first traversal of a type's - /// construction, which occurrences are - /// meant to carry element names. - /// - /// - /// This constructor is meant to be used on types that contain an - /// instantiation of that contains - /// element names. For instance, if C is a generic type with - /// two type parameters, then a use of the constructed type C{, might be intended to - /// treat the first type argument as a tuple with element names and the - /// second as a tuple without element names. In which case, the - /// appropriate attribute specification should use a - /// transformNames value of { "name1", "name2", null, null, - /// null }. - /// - public TupleElementNamesAttribute(string[] transformNames) - { - _transformNames = transformNames ?? throw new ArgumentNullException(nameof(transformNames)); - } - - /// - /// Specifies, in a pre-order depth-first traversal of a type's - /// construction, which elements are - /// meant to carry element names. - /// - public IList TransformNames => _transformNames; -} \ No newline at end of file diff --git a/src/IFoxCAD.Basal/CLS/ValueTuple.cs b/src/IFoxCAD.Basal/CLS/ValueTuple.cs deleted file mode 100644 index 568ddb9..0000000 --- a/src/IFoxCAD.Basal/CLS/ValueTuple.cs +++ /dev/null @@ -1,2140 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. - -// #pragma warning disable SA1141 // explicitly not using tuple syntax in tuple implementation - - -using System.Diagnostics; -using System.Numerics.Hashing; -/* - * 惊惊: - * 首先是因为有人想要编译的时候只形成一个dll,然后把元组塞入IFox,同时也补充了NET35没有元组的遗憾. - * 而利用nuget元组包必然会形成依赖地狱. - * - * 如果你的工程使用了nuget元组包,就造成了必须要剔除IFox. - * - * 改IFox的元组命名空间倒是可以分离两者,但是 vs编译器 无法识别带其他命名空间的元组. - * 所以元组本身就是冲突的,需要把其他元组卸载掉,由IFox提供. - */ - -#if NET35 -namespace System.Collections -{ - public interface IStructuralComparable - { - int CompareTo(object? other, IComparer comparer); - } - public interface IStructuralEquatable - { - bool Equals(object? other, IEqualityComparer comparer); - int GetHashCode(IEqualityComparer comparer); - } -} -#endif - - - -namespace System.Numerics.Hashing -{ - internal static class HashHelpers - { - public static readonly int RandomSeed = Guid.NewGuid().GetHashCode(); - - public static int Combine(int h1, int h2) - { - unchecked - { - // RyuJIT optimizes this to use the ROL instruction - // Related GitHub pull request: dotnet/coreclr#1830 - - // RyuJIT 对此进行了优化以使用 ROL 指令 - // 相关 GitHub 拉取请求:dotnet/coreclr#1830 - uint rol5 = ((uint)h1 << 5) | ((uint)h1 >> 27); - return ((int)rol5 + h1) ^ h2; - } - } - } -} - - - - -namespace System -{ - // internal static class SR - internal sealed partial class SR - { - // public const string ArgumentException_ValueTupleIncorrectType = "The parameter should be a ValueTuple type of appropriate arity."; - // public const string ArgumentException_ValueTupleLastArgumentNotAValueTuple = "The TRest type argument of ValueTuple`8 must be a ValueTuple."; - public const string ArgumentException_ValueTupleIncorrectType = "该参数应该是适当数量的 ValueTuple 类型."; - public const string ArgumentException_ValueTupleLastArgumentNotAValueTuple = "ValueTuple`8 的 TREST 类型参数必须是 ValueTuple."; - } - - // Helper so we can call some tuple methods recursively without knowing the underlying types. - /// - /// 帮助器,因此我们可以在不知道底层类型的情况下递归调用一些元组方法. - /// - internal interface ITupleInternal - { - int GetHashCode(IEqualityComparer comparer); - int Size { get; } - string ToStringEnd(); - } - - - // The ValueTuple types (from arity 0 to 8) comprise the runtime implementation that underlies tuples in C# and struct tuples in F#. - // Aside from created via language syntax, they are most easily created via the ValueTuple.Create factory methods. - // The System.ValueTuple types differ from the System.Tuple types in that: - // - they are structs rather than classes, - // - they are mutable rather than readonly, and - // - their members (such as Item1, Item2, etc) are fields rather than properties. - /// - /// ValueTuple 类型(从 arity 0 到 8)包含运行时实现,它是 C# 中的元组和 F# 中的结构元组的基础. - /// 除了通过语言语法创建之外,它们最容易通过 ValueTuple.Create 工厂方法创建. - /// System.ValueTuple 类型与 System.Tuple 类型的不同之处在于: - /// - 它们是结构而不是类, - /// - 它们是可变的而不是只读的,并且 - /// - 它们的成员(例如 Item1、Item2 等)是字段而不是属性. - /// - public struct ValueTuple - : IEquatable, IStructuralEquatable, IStructuralComparable, IComparable, IComparable, ITupleInternal - { - /// - /// Returns a value that indicates whether the current instance is equal to a specified object. - /// - /// The object to compare with this instance. - /// if is a . - public override bool Equals(object obj) - { - return obj is ValueTuple; - } - - /// Returns a value indicating whether this instance is equal to a specified value. - /// An instance to compare to this instance. - /// true if has the same value as this instance; otherwise, false. - public bool Equals(ValueTuple other) - { - return true; - } - - bool IStructuralEquatable.Equals(object? other, IEqualityComparer comparer) - { - return other is ValueTuple; - } - - int IComparable.CompareTo(object other) - { - if (other == null) return 1; - - if (other is not ValueTuple) - { - throw new ArgumentException(SR.ArgumentException_ValueTupleIncorrectType, nameof(other)); - } - - return 0; - } - - /// Compares this instance to a specified instance and returns an indication of their relative values. - /// An instance to compare. - /// - /// A signed number indicating the relative values of this instance and . - /// Returns less than zero if this instance is less than , zero if this - /// instance is equal to , and greater than zero if this instance is greater - /// than . - /// - public int CompareTo(ValueTuple other) - { - return 0; - } - - int IStructuralComparable.CompareTo(object? other, IComparer comparer) - { - if (other == null) return 1; - - if (other is not ValueTuple) - { - throw new ArgumentException(SR.ArgumentException_ValueTupleIncorrectType, nameof(other)); - } - - return 0; - } - - /// Returns the hash code for this instance. - /// A 32-bit signed integer hash code. - public override int GetHashCode() - { - return 0; - } - - int IStructuralEquatable.GetHashCode(IEqualityComparer comparer) - { - return 0; - } - - int ITupleInternal.GetHashCode(IEqualityComparer comparer) - { - return 0; - } - - /// - /// Returns a string that represents the value of this instance. - /// - /// The string representation of this instance. - /// - /// The string returned by this method takes the form (). - /// - public override string ToString() - { - return "()"; - } - - string ITupleInternal.ToStringEnd() - { - return ")"; - } - - int ITupleInternal.Size => 0; - - /// Creates a new struct 0-tuple. - /// A 0-tuple. - public static ValueTuple Create() => new(); - - /// Creates a new struct 1-tuple, or singleton. - /// The type of the first component of the tuple. - /// The value of the first component of the tuple. - /// A 1-tuple (singleton) whose value is (item1). - public static ValueTuple Create(T1 item1) => new(item1); - - /// Creates a new struct 2-tuple, or pair. - /// The type of the first component of the tuple. - /// The type of the second component of the tuple. - /// The value of the first component of the tuple. - /// The value of the second component of the tuple. - /// A 2-tuple (pair) whose value is (item1, item2). - public static ValueTuple Create(T1 item1, T2 item2) => new(item1, item2); - - /// Creates a new struct 3-tuple, or triple. - /// The type of the first component of the tuple. - /// The type of the second component of the tuple. - /// The type of the third component of the tuple. - /// The value of the first component of the tuple. - /// The value of the second component of the tuple. - /// The value of the third component of the tuple. - /// A 3-tuple (triple) whose value is (item1, item2, item3). - public static ValueTuple Create(T1 item1, T2 item2, T3 item3) => - new(item1, item2, item3); - - /// Creates a new struct 4-tuple, or quadruple. - /// The type of the first component of the tuple. - /// The type of the second component of the tuple. - /// The type of the third component of the tuple. - /// The type of the fourth component of the tuple. - /// The value of the first component of the tuple. - /// The value of the second component of the tuple. - /// The value of the third component of the tuple. - /// The value of the fourth component of the tuple. - /// A 4-tuple (quadruple) whose value is (item1, item2, item3, item4). - public static ValueTuple Create(T1 item1, T2 item2, T3 item3, T4 item4) => - new(item1, item2, item3, item4); - - /// Creates a new struct 5-tuple, or quintuple. - /// The type of the first component of the tuple. - /// The type of the second component of the tuple. - /// The type of the third component of the tuple. - /// The type of the fourth component of the tuple. - /// The type of the fifth component of the tuple. - /// The value of the first component of the tuple. - /// The value of the second component of the tuple. - /// The value of the third component of the tuple. - /// The value of the fourth component of the tuple. - /// The value of the fifth component of the tuple. - /// A 5-tuple (quintuple) whose value is (item1, item2, item3, item4, item5). - public static ValueTuple Create(T1 item1, T2 item2, T3 item3, T4 item4, T5 item5) => - new(item1, item2, item3, item4, item5); - - /// Creates a new struct 6-tuple, or sextuple. - /// The type of the first component of the tuple. - /// The type of the second component of the tuple. - /// The type of the third component of the tuple. - /// The type of the fourth component of the tuple. - /// The type of the fifth component of the tuple. - /// The type of the sixth component of the tuple. - /// The value of the first component of the tuple. - /// The value of the second component of the tuple. - /// The value of the third component of the tuple. - /// The value of the fourth component of the tuple. - /// The value of the fifth component of the tuple. - /// The value of the sixth component of the tuple. - /// A 6-tuple (sextuple) whose value is (item1, item2, item3, item4, item5, item6). - public static ValueTuple Create(T1 item1, T2 item2, T3 item3, T4 item4, T5 item5, T6 item6) => - new(item1, item2, item3, item4, item5, item6); - - /// Creates a new struct 7-tuple, or septuple. - /// The type of the first component of the tuple. - /// The type of the second component of the tuple. - /// The type of the third component of the tuple. - /// The type of the fourth component of the tuple. - /// The type of the fifth component of the tuple. - /// The type of the sixth component of the tuple. - /// The type of the seventh component of the tuple. - /// The value of the first component of the tuple. - /// The value of the second component of the tuple. - /// The value of the third component of the tuple. - /// The value of the fourth component of the tuple. - /// The value of the fifth component of the tuple. - /// The value of the sixth component of the tuple. - /// The value of the seventh component of the tuple. - /// A 7-tuple (septuple) whose value is (item1, item2, item3, item4, item5, item6, item7). - public static ValueTuple Create(T1 item1, T2 item2, T3 item3, T4 item4, T5 item5, T6 item6, T7 item7) => - new(item1, item2, item3, item4, item5, item6, item7); - - /// Creates a new struct 8-tuple, or octuple. - /// The type of the first component of the tuple. - /// The type of the second component of the tuple. - /// The type of the third component of the tuple. - /// The type of the fourth component of the tuple. - /// The type of the fifth component of the tuple. - /// The type of the sixth component of the tuple. - /// The type of the seventh component of the tuple. - /// The type of the eighth component of the tuple. - /// The value of the first component of the tuple. - /// The value of the second component of the tuple. - /// The value of the third component of the tuple. - /// The value of the fourth component of the tuple. - /// The value of the fifth component of the tuple. - /// The value of the sixth component of the tuple. - /// The value of the seventh component of the tuple. - /// The value of the eighth component of the tuple. - /// An 8-tuple (octuple) whose value is (item1, item2, item3, item4, item5, item6, item7, item8). - public static ValueTuple> Create(T1 item1, T2 item2, T3 item3, T4 item4, T5 item5, T6 item6, T7 item7, T8 item8) => - new(item1, item2, item3, item4, item5, item6, item7, ValueTuple.Create(item8)); - - internal static int CombineHashCodes(int h1, int h2) - { - return HashHelpers.Combine(HashHelpers.Combine(HashHelpers.RandomSeed, h1), h2); - } - - internal static int CombineHashCodes(int h1, int h2, int h3) - { - return HashHelpers.Combine(CombineHashCodes(h1, h2), h3); - } - - internal static int CombineHashCodes(int h1, int h2, int h3, int h4) - { - return HashHelpers.Combine(CombineHashCodes(h1, h2, h3), h4); - } - - internal static int CombineHashCodes(int h1, int h2, int h3, int h4, int h5) - { - return HashHelpers.Combine(CombineHashCodes(h1, h2, h3, h4), h5); - } - - internal static int CombineHashCodes(int h1, int h2, int h3, int h4, int h5, int h6) - { - return HashHelpers.Combine(CombineHashCodes(h1, h2, h3, h4, h5), h6); - } - - internal static int CombineHashCodes(int h1, int h2, int h3, int h4, int h5, int h6, int h7) - { - return HashHelpers.Combine(CombineHashCodes(h1, h2, h3, h4, h5, h6), h7); - } - - internal static int CombineHashCodes(int h1, int h2, int h3, int h4, int h5, int h6, int h7, int h8) - { - return HashHelpers.Combine(CombineHashCodes(h1, h2, h3, h4, h5, h6, h7), h8); - } - } - - /// Represents a 1-tuple, or singleton, as a value type. - /// The type of the tuple's only component. - public struct ValueTuple - : IEquatable>, IStructuralEquatable, IStructuralComparable, IComparable, IComparable>, ITupleInternal - { - /// - /// The current instance's first component. - /// - public T1 Item1; - - /// - /// Initializes a new instance of the value type. - /// - /// The value of the tuple's first component. - public ValueTuple(T1 item1) - { - Item1 = item1; - } - - /// - /// Returns a value that indicates whether the current instance is equal to a specified object. - /// - /// The object to compare with this instance. - /// if the current instance is equal to the specified object; otherwise, . - /// - /// The parameter is considered to be equal to the current instance under the following conditions: - /// - /// It is a value type. - /// Its components are of the same types as those of the current instance. - /// Its components are equal to those of the current instance. Equality is determined by the default object equality comparer for each component. - /// - /// - public override bool Equals(object obj) - { - return obj is ValueTuple tuple && Equals(tuple); - } - - /// - /// Returns a value that indicates whether the current - /// instance is equal to a specified . - /// - /// The tuple to compare with this instance. - /// if the current instance is equal to the specified tuple; otherwise, . - /// - /// The parameter is considered to be equal to the current instance if each of its field - /// is equal to that of the current instance, using the default comparer for that field's type. - /// - public bool Equals(ValueTuple other) - { - return EqualityComparer.Default.Equals(Item1, other.Item1); - } - - bool IStructuralEquatable.Equals(object? other, IEqualityComparer comparer) - { - if (other == null || other is not ValueTuple) return false; - - var objTuple = (ValueTuple)other; - - return comparer.Equals(Item1, objTuple.Item1); - } - - int IComparable.CompareTo(object other) - { - if (other == null) return 1; - - if (other is not ValueTuple) - { - throw new ArgumentException(SR.ArgumentException_ValueTupleIncorrectType, nameof(other)); - } - - var objTuple = (ValueTuple)other; - - return Comparer.Default.Compare(Item1, objTuple.Item1); - } - - /// Compares this instance to a specified instance and returns an indication of their relative values. - /// An instance to compare. - /// - /// A signed number indicating the relative values of this instance and . - /// Returns less than zero if this instance is less than , zero if this - /// instance is equal to , and greater than zero if this instance is greater - /// than . - /// - public int CompareTo(ValueTuple other) - { - return Comparer.Default.Compare(Item1, other.Item1); - } - - int IStructuralComparable.CompareTo(object? other, IComparer comparer) - { - if (other == null) return 1; - - if (other is not ValueTuple) - { - throw new ArgumentException(SR.ArgumentException_ValueTupleIncorrectType, nameof(other)); - } - - var objTuple = (ValueTuple)other; - - return comparer.Compare(Item1, objTuple.Item1); - } - - /// - /// Returns the hash code for the current instance. - /// - /// A 32-bit signed integer hash code. - public override int GetHashCode() - { - return EqualityComparer.Default.GetHashCode(Item1); - } - - int IStructuralEquatable.GetHashCode(IEqualityComparer comparer) - { - return comparer.GetHashCode(Item1); - } - - int ITupleInternal.GetHashCode(IEqualityComparer comparer) - { - return comparer.GetHashCode(Item1); - } - - /// - /// Returns a string that represents the value of this instance. - /// - /// The string representation of this instance. - /// - /// The string returned by this method takes the form (Item1), - /// where Item1 represents the value of . If the field is , - /// it is represented as . - /// - public override string ToString() - { - return "(" + Item1?.ToString() + ")"; - } - - string ITupleInternal.ToStringEnd() - { - return Item1?.ToString() + ")"; - } - - int ITupleInternal.Size => 1; - } - - /// - /// Represents a 2-tuple, or pair, as a value type. - /// - /// The type of the tuple's first component. - /// The type of the tuple's second component. - public struct ValueTuple - : IEquatable>, IStructuralEquatable, IStructuralComparable, IComparable, IComparable>, ITupleInternal - { - /// - /// The current instance's first component. - /// - public T1 Item1; - - /// - /// The current instance's first component. - /// - public T2 Item2; - - /// - /// Initializes a new instance of the value type. - /// - /// The value of the tuple's first component. - /// The value of the tuple's second component. - public ValueTuple(T1 item1, T2 item2) - { - Item1 = item1; - Item2 = item2; - } - - /// - /// Returns a value that indicates whether the current instance is equal to a specified object. - /// - /// The object to compare with this instance. - /// if the current instance is equal to the specified object; otherwise, . - /// - /// - /// The parameter is considered to be equal to the current instance under the following conditions: - /// - /// It is a value type. - /// Its components are of the same types as those of the current instance. - /// Its components are equal to those of the current instance. Equality is determined by the default object equality comparer for each component. - /// - /// - public override bool Equals(object obj) - { - return obj is ValueTuple tuple && Equals(tuple); - } - - /// - /// Returns a value that indicates whether the current instance is equal to a specified . - /// - /// The tuple to compare with this instance. - /// if the current instance is equal to the specified tuple; otherwise, . - /// - /// The parameter is considered to be equal to the current instance if each of its fields - /// are equal to that of the current instance, using the default comparer for that field's type. - /// - public bool Equals(ValueTuple other) - { - return EqualityComparer.Default.Equals(Item1, other.Item1) - && EqualityComparer.Default.Equals(Item2, other.Item2); - } - - /// - /// Returns a value that indicates whether the current instance is equal to a specified object based on a specified comparison method. - /// - /// The object to compare with this instance. - /// An object that defines the method to use to evaluate whether the two objects are equal. - /// if the current instance is equal to the specified object; otherwise, . - /// - /// - /// This member is an explicit interface member implementation. It can be used only when the - /// instance is cast to an interface. - /// - /// The implementation is called only if other is not , - /// and if it can be successfully cast (in C#) or converted (in Visual Basic) to a - /// whose components are of the same types as those of the current instance. The IStructuralEquatable.Equals(Object, IEqualityComparer) method - /// first passes the values of the objects to be compared to the - /// implementation. If this method call returns , the method is - /// called again and passed the values of the two instances. - /// - bool IStructuralEquatable.Equals(object? other, IEqualityComparer comparer) - { - if (other is null or not ValueTuple) return false; - - var objTuple = (ValueTuple)other; - - return comparer.Equals(Item1, objTuple.Item1) - && comparer.Equals(Item2, objTuple.Item2); - } - - int IComparable.CompareTo(object other) - { - if (other == null) return 1; - - if (other is not ValueTuple) - { - throw new ArgumentException(SR.ArgumentException_ValueTupleIncorrectType, nameof(other)); - } - - return CompareTo((ValueTuple)other); - } - - /// Compares this instance to a specified instance and returns an indication of their relative values. - /// An instance to compare. - /// - /// A signed number indicating the relative values of this instance and . - /// Returns less than zero if this instance is less than , zero if this - /// instance is equal to , and greater than zero if this instance is greater - /// than . - /// - public int CompareTo(ValueTuple other) - { - int c = Comparer.Default.Compare(Item1, other.Item1); - if (c != 0) return c; - - return Comparer.Default.Compare(Item2, other.Item2); - } - - int IStructuralComparable.CompareTo(object? other, IComparer comparer) - { - if (other == null) return 1; - - if (other is not ValueTuple) - { - throw new ArgumentException(SR.ArgumentException_ValueTupleIncorrectType, nameof(other)); - } - - var objTuple = (ValueTuple)other; - - int c = comparer.Compare(Item1, objTuple.Item1); - if (c != 0) return c; - - return comparer.Compare(Item2, objTuple.Item2); - } - - /// - /// Returns the hash code for the current instance. - /// - /// A 32-bit signed integer hash code. - public override int GetHashCode() - { - return ValueTuple.CombineHashCodes(EqualityComparer.Default.GetHashCode(Item1), - EqualityComparer.Default.GetHashCode(Item2)); - } - - int IStructuralEquatable.GetHashCode(IEqualityComparer comparer) - { - return GetHashCodeCore(comparer); - } - - private int GetHashCodeCore(IEqualityComparer comparer) - { - return ValueTuple.CombineHashCodes(comparer.GetHashCode(Item1), - comparer.GetHashCode(Item2)); - } - - int ITupleInternal.GetHashCode(IEqualityComparer comparer) - { - return GetHashCodeCore(comparer); - } - - /// - /// Returns a string that represents the value of this instance. - /// - /// The string representation of this instance. - /// - /// The string returned by this method takes the form (Item1, Item2), - /// where Item1 and Item2 represent the values of the - /// and fields. If either field value is , - /// it is represented as . - /// - public override string ToString() - { - return "(" + Item1?.ToString() + ", " + Item2?.ToString() + ")"; - } - - string ITupleInternal.ToStringEnd() - { - return Item1?.ToString() + ", " + Item2?.ToString() + ")"; - } - - int ITupleInternal.Size => 2; - } - - /// - /// Represents a 3-tuple, or triple, as a value type. - /// - /// The type of the tuple's first component. - /// The type of the tuple's second component. - /// The type of the tuple's third component. - public struct ValueTuple - : IEquatable>, IStructuralEquatable, IStructuralComparable, IComparable, IComparable>, ITupleInternal - { - /// - /// The current instance's first component. - /// - public T1 Item1; - /// - /// The current instance's second component. - /// - public T2 Item2; - /// - /// The current instance's third component. - /// - public T3 Item3; - - /// - /// Initializes a new instance of the value type. - /// - /// The value of the tuple's first component. - /// The value of the tuple's second component. - /// The value of the tuple's third component. - public ValueTuple(T1 item1, T2 item2, T3 item3) - { - Item1 = item1; - Item2 = item2; - Item3 = item3; - } - - /// - /// Returns a value that indicates whether the current instance is equal to a specified object. - /// - /// The object to compare with this instance. - /// if the current instance is equal to the specified object; otherwise, . - /// - /// The parameter is considered to be equal to the current instance under the following conditions: - /// - /// It is a value type. - /// Its components are of the same types as those of the current instance. - /// Its components are equal to those of the current instance. Equality is determined by the default object equality comparer for each component. - /// - /// - public override bool Equals(object obj) - { - return obj is ValueTuple tuple && Equals(tuple); - } - - /// - /// Returns a value that indicates whether the current - /// instance is equal to a specified . - /// - /// The tuple to compare with this instance. - /// if the current instance is equal to the specified tuple; otherwise, . - /// - /// The parameter is considered to be equal to the current instance if each of its fields - /// are equal to that of the current instance, using the default comparer for that field's type. - /// - public bool Equals(ValueTuple other) - { - return EqualityComparer.Default.Equals(Item1, other.Item1) - && EqualityComparer.Default.Equals(Item2, other.Item2) - && EqualityComparer.Default.Equals(Item3, other.Item3); - } - - bool IStructuralEquatable.Equals(object? other, IEqualityComparer comparer) - { - if (other == null || other is not ValueTuple) return false; - - var objTuple = (ValueTuple)other; - - return comparer.Equals(Item1, objTuple.Item1) - && comparer.Equals(Item2, objTuple.Item2) - && comparer.Equals(Item3, objTuple.Item3); - } - - int IComparable.CompareTo(object other) - { - if (other == null) return 1; - - if (other is not ValueTuple) - { - throw new ArgumentException(SR.ArgumentException_ValueTupleIncorrectType, nameof(other)); - } - - return CompareTo((ValueTuple)other); - } - - /// Compares this instance to a specified instance and returns an indication of their relative values. - /// An instance to compare. - /// - /// A signed number indicating the relative values of this instance and . - /// Returns less than zero if this instance is less than , zero if this - /// instance is equal to , and greater than zero if this instance is greater - /// than . - /// - public int CompareTo(ValueTuple other) - { - int c = Comparer.Default.Compare(Item1, other.Item1); - if (c != 0) return c; - - c = Comparer.Default.Compare(Item2, other.Item2); - if (c != 0) return c; - - return Comparer.Default.Compare(Item3, other.Item3); - } - - int IStructuralComparable.CompareTo(object? other, IComparer comparer) - { - if (other == null) return 1; - - if (other is not ValueTuple) - { - throw new ArgumentException(SR.ArgumentException_ValueTupleIncorrectType, nameof(other)); - } - - var objTuple = (ValueTuple)other; - - int c = comparer.Compare(Item1, objTuple.Item1); - if (c != 0) return c; - - c = comparer.Compare(Item2, objTuple.Item2); - if (c != 0) return c; - - return comparer.Compare(Item3, objTuple.Item3); - } - - /// - /// Returns the hash code for the current instance. - /// - /// A 32-bit signed integer hash code. - public override int GetHashCode() - { - return ValueTuple.CombineHashCodes(EqualityComparer.Default.GetHashCode(Item1), - EqualityComparer.Default.GetHashCode(Item2), - EqualityComparer.Default.GetHashCode(Item3)); - } - - int IStructuralEquatable.GetHashCode(IEqualityComparer comparer) - { - return GetHashCodeCore(comparer); - } - - private int GetHashCodeCore(IEqualityComparer comparer) - { - return ValueTuple.CombineHashCodes(comparer.GetHashCode(Item1), - comparer.GetHashCode(Item2), - comparer.GetHashCode(Item3)); - } - - int ITupleInternal.GetHashCode(IEqualityComparer comparer) - { - return GetHashCodeCore(comparer); - } - - /// - /// Returns a string that represents the value of this instance. - /// - /// The string representation of this instance. - /// - /// The string returned by this method takes the form (Item1, Item2, Item3). - /// If any field value is , it is represented as . - /// - public override string ToString() - { - return "(" + Item1?.ToString() + ", " + Item2?.ToString() + ", " + Item3?.ToString() + ")"; - } - - string ITupleInternal.ToStringEnd() - { - return Item1?.ToString() + ", " + Item2?.ToString() + ", " + Item3?.ToString() + ")"; - } - - int ITupleInternal.Size => 3; - } - - /// - /// Represents a 4-tuple, or quadruple, as a value type. - /// - /// The type of the tuple's first component. - /// The type of the tuple's second component. - /// The type of the tuple's third component. - /// The type of the tuple's fourth component. - public struct ValueTuple - : IEquatable>, IStructuralEquatable, IStructuralComparable, IComparable, IComparable>, ITupleInternal - { - /// - /// The current instance's first component. - /// - public T1 Item1; - /// - /// The current instance's second component. - /// - public T2 Item2; - /// - /// The current instance's third component. - /// - public T3 Item3; - /// - /// The current instance's fourth component. - /// - public T4 Item4; - - /// - /// Initializes a new instance of the value type. - /// - /// The value of the tuple's first component. - /// The value of the tuple's second component. - /// The value of the tuple's third component. - /// The value of the tuple's fourth component. - public ValueTuple(T1 item1, T2 item2, T3 item3, T4 item4) - { - Item1 = item1; - Item2 = item2; - Item3 = item3; - Item4 = item4; - } - - /// - /// Returns a value that indicates whether the current instance is equal to a specified object. - /// - /// The object to compare with this instance. - /// if the current instance is equal to the specified object; otherwise, . - /// - /// The parameter is considered to be equal to the current instance under the following conditions: - /// - /// It is a value type. - /// Its components are of the same types as those of the current instance. - /// Its components are equal to those of the current instance. Equality is determined by the default object equality comparer for each component. - /// - /// - public override bool Equals(object obj) - { - return obj is ValueTuple tuple && Equals(tuple); - } - - /// - /// Returns a value that indicates whether the current - /// instance is equal to a specified . - /// - /// The tuple to compare with this instance. - /// if the current instance is equal to the specified tuple; otherwise, . - /// - /// The parameter is considered to be equal to the current instance if each of its fields - /// are equal to that of the current instance, using the default comparer for that field's type. - /// - public bool Equals(ValueTuple other) - { - return EqualityComparer.Default.Equals(Item1, other.Item1) - && EqualityComparer.Default.Equals(Item2, other.Item2) - && EqualityComparer.Default.Equals(Item3, other.Item3) - && EqualityComparer.Default.Equals(Item4, other.Item4); - } - - bool IStructuralEquatable.Equals(object? other, IEqualityComparer comparer) - { - if (other == null || other is not ValueTuple) return false; - - var objTuple = (ValueTuple)other; - - return comparer.Equals(Item1, objTuple.Item1) - && comparer.Equals(Item2, objTuple.Item2) - && comparer.Equals(Item3, objTuple.Item3) - && comparer.Equals(Item4, objTuple.Item4); - } - - int IComparable.CompareTo(object other) - { - if (other == null) return 1; - - if (other is not ValueTuple) - { - throw new ArgumentException(SR.ArgumentException_ValueTupleIncorrectType, nameof(other)); - } - - return CompareTo((ValueTuple)other); - } - - /// Compares this instance to a specified instance and returns an indication of their relative values. - /// An instance to compare. - /// - /// A signed number indicating the relative values of this instance and . - /// Returns less than zero if this instance is less than , zero if this - /// instance is equal to , and greater than zero if this instance is greater - /// than . - /// - public int CompareTo(ValueTuple other) - { - int c = Comparer.Default.Compare(Item1, other.Item1); - if (c != 0) return c; - - c = Comparer.Default.Compare(Item2, other.Item2); - if (c != 0) return c; - - c = Comparer.Default.Compare(Item3, other.Item3); - if (c != 0) return c; - - return Comparer.Default.Compare(Item4, other.Item4); - } - - int IStructuralComparable.CompareTo(object? other, IComparer comparer) - { - if (other == null) return 1; - - if (other is not ValueTuple) - { - throw new ArgumentException(SR.ArgumentException_ValueTupleIncorrectType, nameof(other)); - } - - var objTuple = (ValueTuple)other; - - int c = comparer.Compare(Item1, objTuple.Item1); - if (c != 0) return c; - - c = comparer.Compare(Item2, objTuple.Item2); - if (c != 0) return c; - - c = comparer.Compare(Item3, objTuple.Item3); - if (c != 0) return c; - - return comparer.Compare(Item4, objTuple.Item4); - } - - /// - /// Returns the hash code for the current instance. - /// - /// A 32-bit signed integer hash code. - public override int GetHashCode() - { - return ValueTuple.CombineHashCodes(EqualityComparer.Default.GetHashCode(Item1), - EqualityComparer.Default.GetHashCode(Item2), - EqualityComparer.Default.GetHashCode(Item3), - EqualityComparer.Default.GetHashCode(Item4)); - } - - int IStructuralEquatable.GetHashCode(IEqualityComparer comparer) - { - return GetHashCodeCore(comparer); - } - - private int GetHashCodeCore(IEqualityComparer comparer) - { - return ValueTuple.CombineHashCodes(comparer.GetHashCode(Item1), - comparer.GetHashCode(Item2), - comparer.GetHashCode(Item3), - comparer.GetHashCode(Item4)); - } - - int ITupleInternal.GetHashCode(IEqualityComparer comparer) - { - return GetHashCodeCore(comparer); - } - - /// - /// Returns a string that represents the value of this instance. - /// - /// The string representation of this instance. - /// - /// The string returned by this method takes the form (Item1, Item2, Item3, Item4). - /// If any field value is , it is represented as . - /// - public override string ToString() - { - return "(" + Item1?.ToString() + ", " + Item2?.ToString() + ", " + Item3?.ToString() + ", " + Item4?.ToString() + ")"; - } - - string ITupleInternal.ToStringEnd() - { - return Item1?.ToString() + ", " + Item2?.ToString() + ", " + Item3?.ToString() + ", " + Item4?.ToString() + ")"; - } - - int ITupleInternal.Size => 4; - } - - /// - /// Represents a 5-tuple, or quintuple, as a value type. - /// - /// The type of the tuple's first component. - /// The type of the tuple's second component. - /// The type of the tuple's third component. - /// The type of the tuple's fourth component. - /// The type of the tuple's fifth component. - public struct ValueTuple - : IEquatable>, IStructuralEquatable, IStructuralComparable, IComparable, IComparable>, ITupleInternal - { - /// - /// The current instance's first component. - /// - public T1 Item1; - /// - /// The current instance's second component. - /// - public T2 Item2; - /// - /// The current instance's third component. - /// - public T3 Item3; - /// - /// The current instance's fourth component. - /// - public T4 Item4; - /// - /// The current instance's fifth component. - /// - public T5 Item5; - - /// - /// Initializes a new instance of the value type. - /// - /// The value of the tuple's first component. - /// The value of the tuple's second component. - /// The value of the tuple's third component. - /// The value of the tuple's fourth component. - /// The value of the tuple's fifth component. - public ValueTuple(T1 item1, T2 item2, T3 item3, T4 item4, T5 item5) - { - Item1 = item1; - Item2 = item2; - Item3 = item3; - Item4 = item4; - Item5 = item5; - } - - /// - /// Returns a value that indicates whether the current instance is equal to a specified object. - /// - /// The object to compare with this instance. - /// if the current instance is equal to the specified object; otherwise, . - /// - /// The parameter is considered to be equal to the current instance under the following conditions: - /// - /// It is a value type. - /// Its components are of the same types as those of the current instance. - /// Its components are equal to those of the current instance. Equality is determined by the default object equality comparer for each component. - /// - /// - public override bool Equals(object obj) - { - return obj is ValueTuple tuple && Equals(tuple); - } - - /// - /// Returns a value that indicates whether the current - /// instance is equal to a specified . - /// - /// The tuple to compare with this instance. - /// if the current instance is equal to the specified tuple; otherwise, . - /// - /// The parameter is considered to be equal to the current instance if each of its fields - /// are equal to that of the current instance, using the default comparer for that field's type. - /// - public bool Equals(ValueTuple other) - { - return EqualityComparer.Default.Equals(Item1, other.Item1) - && EqualityComparer.Default.Equals(Item2, other.Item2) - && EqualityComparer.Default.Equals(Item3, other.Item3) - && EqualityComparer.Default.Equals(Item4, other.Item4) - && EqualityComparer.Default.Equals(Item5, other.Item5); - } - - bool IStructuralEquatable.Equals(object? other, IEqualityComparer comparer) - { - if (other == null || other is not ValueTuple) return false; - - var objTuple = (ValueTuple)other; - - return comparer.Equals(Item1, objTuple.Item1) - && comparer.Equals(Item2, objTuple.Item2) - && comparer.Equals(Item3, objTuple.Item3) - && comparer.Equals(Item4, objTuple.Item4) - && comparer.Equals(Item5, objTuple.Item5); - } - - int IComparable.CompareTo(object other) - { - if (other == null) return 1; - - if (other is not ValueTuple) - { - throw new ArgumentException(SR.ArgumentException_ValueTupleIncorrectType, nameof(other)); - } - - return CompareTo((ValueTuple)other); - } - - /// Compares this instance to a specified instance and returns an indication of their relative values. - /// An instance to compare. - /// - /// A signed number indicating the relative values of this instance and . - /// Returns less than zero if this instance is less than , zero if this - /// instance is equal to , and greater than zero if this instance is greater - /// than . - /// - public int CompareTo(ValueTuple other) - { - int c = Comparer.Default.Compare(Item1, other.Item1); - if (c != 0) return c; - - c = Comparer.Default.Compare(Item2, other.Item2); - if (c != 0) return c; - - c = Comparer.Default.Compare(Item3, other.Item3); - if (c != 0) return c; - - c = Comparer.Default.Compare(Item4, other.Item4); - if (c != 0) return c; - - return Comparer.Default.Compare(Item5, other.Item5); - } - - int IStructuralComparable.CompareTo(object? other, IComparer comparer) - { - if (other == null) return 1; - - if (other is not ValueTuple) - { - throw new ArgumentException(SR.ArgumentException_ValueTupleIncorrectType, nameof(other)); - } - - var objTuple = (ValueTuple)other; - - int c = comparer.Compare(Item1, objTuple.Item1); - if (c != 0) return c; - - c = comparer.Compare(Item2, objTuple.Item2); - if (c != 0) return c; - - c = comparer.Compare(Item3, objTuple.Item3); - if (c != 0) return c; - - c = comparer.Compare(Item4, objTuple.Item4); - if (c != 0) return c; - - return comparer.Compare(Item5, objTuple.Item5); - } - - /// - /// Returns the hash code for the current instance. - /// - /// A 32-bit signed integer hash code. - public override int GetHashCode() - { - return ValueTuple.CombineHashCodes(EqualityComparer.Default.GetHashCode(Item1), - EqualityComparer.Default.GetHashCode(Item2), - EqualityComparer.Default.GetHashCode(Item3), - EqualityComparer.Default.GetHashCode(Item4), - EqualityComparer.Default.GetHashCode(Item5)); - } - - int IStructuralEquatable.GetHashCode(IEqualityComparer comparer) - { - return GetHashCodeCore(comparer); - } - - private int GetHashCodeCore(IEqualityComparer comparer) - { - return ValueTuple.CombineHashCodes(comparer.GetHashCode(Item1), - comparer.GetHashCode(Item2), - comparer.GetHashCode(Item3), - comparer.GetHashCode(Item4), - comparer.GetHashCode(Item5)); - } - - int ITupleInternal.GetHashCode(IEqualityComparer comparer) - { - return GetHashCodeCore(comparer); - } - - /// - /// Returns a string that represents the value of this instance. - /// - /// The string representation of this instance. - /// - /// The string returned by this method takes the form (Item1, Item2, Item3, Item4, Item5). - /// If any field value is , it is represented as . - /// - public override string ToString() - { - return "(" + Item1?.ToString() + ", " + Item2?.ToString() + ", " + Item3?.ToString() + ", " + Item4?.ToString() + ", " + Item5?.ToString() + ")"; - } - - string ITupleInternal.ToStringEnd() - { - return Item1?.ToString() + ", " + Item2?.ToString() + ", " + Item3?.ToString() + ", " + Item4?.ToString() + ", " + Item5?.ToString() + ")"; - } - - int ITupleInternal.Size => 5; - } - - /// - /// Represents a 6-tuple, or sixtuple, as a value type. - /// - /// The type of the tuple's first component. - /// The type of the tuple's second component. - /// The type of the tuple's third component. - /// The type of the tuple's fourth component. - /// The type of the tuple's fifth component. - /// The type of the tuple's sixth component. - public struct ValueTuple - : IEquatable>, IStructuralEquatable, IStructuralComparable, IComparable, IComparable>, ITupleInternal - { - /// - /// The current instance's first component. - /// - public T1 Item1; - /// - /// The current instance's second component. - /// - public T2 Item2; - /// - /// The current instance's third component. - /// - public T3 Item3; - /// - /// The current instance's fourth component. - /// - public T4 Item4; - /// - /// The current instance's fifth component. - /// - public T5 Item5; - /// - /// The current instance's sixth component. - /// - public T6 Item6; - - /// - /// Initializes a new instance of the value type. - /// - /// The value of the tuple's first component. - /// The value of the tuple's second component. - /// The value of the tuple's third component. - /// The value of the tuple's fourth component. - /// The value of the tuple's fifth component. - /// The value of the tuple's sixth component. - public ValueTuple(T1 item1, T2 item2, T3 item3, T4 item4, T5 item5, T6 item6) - { - Item1 = item1; - Item2 = item2; - Item3 = item3; - Item4 = item4; - Item5 = item5; - Item6 = item6; - } - - /// - /// Returns a value that indicates whether the current instance is equal to a specified object. - /// - /// The object to compare with this instance. - /// if the current instance is equal to the specified object; otherwise, . - /// - /// The parameter is considered to be equal to the current instance under the following conditions: - /// - /// It is a value type. - /// Its components are of the same types as those of the current instance. - /// Its components are equal to those of the current instance. Equality is determined by the default object equality comparer for each component. - /// - /// - public override bool Equals(object obj) - { - return obj is ValueTuple tuple && Equals(tuple); - } - - /// - /// Returns a value that indicates whether the current - /// instance is equal to a specified . - /// - /// The tuple to compare with this instance. - /// if the current instance is equal to the specified tuple; otherwise, . - /// - /// The parameter is considered to be equal to the current instance if each of its fields - /// are equal to that of the current instance, using the default comparer for that field's type. - /// - public bool Equals(ValueTuple other) - { - return EqualityComparer.Default.Equals(Item1, other.Item1) - && EqualityComparer.Default.Equals(Item2, other.Item2) - && EqualityComparer.Default.Equals(Item3, other.Item3) - && EqualityComparer.Default.Equals(Item4, other.Item4) - && EqualityComparer.Default.Equals(Item5, other.Item5) - && EqualityComparer.Default.Equals(Item6, other.Item6); - } - - bool IStructuralEquatable.Equals(object? other, IEqualityComparer comparer) - { - if (other == null || other is not ValueTuple) return false; - - var objTuple = (ValueTuple)other; - - return comparer.Equals(Item1, objTuple.Item1) - && comparer.Equals(Item2, objTuple.Item2) - && comparer.Equals(Item3, objTuple.Item3) - && comparer.Equals(Item4, objTuple.Item4) - && comparer.Equals(Item5, objTuple.Item5) - && comparer.Equals(Item6, objTuple.Item6); - } - - int IComparable.CompareTo(object other) - { - if (other == null) return 1; - - if (other is not ValueTuple) - { - throw new ArgumentException(SR.ArgumentException_ValueTupleIncorrectType, nameof(other)); - } - - return CompareTo((ValueTuple)other); - } - - /// Compares this instance to a specified instance and returns an indication of their relative values. - /// An instance to compare. - /// - /// A signed number indicating the relative values of this instance and . - /// Returns less than zero if this instance is less than , zero if this - /// instance is equal to , and greater than zero if this instance is greater - /// than . - /// - public int CompareTo(ValueTuple other) - { - int c = Comparer.Default.Compare(Item1, other.Item1); - if (c != 0) return c; - - c = Comparer.Default.Compare(Item2, other.Item2); - if (c != 0) return c; - - c = Comparer.Default.Compare(Item3, other.Item3); - if (c != 0) return c; - - c = Comparer.Default.Compare(Item4, other.Item4); - if (c != 0) return c; - - c = Comparer.Default.Compare(Item5, other.Item5); - if (c != 0) return c; - - return Comparer.Default.Compare(Item6, other.Item6); - } - - int IStructuralComparable.CompareTo(object? other, IComparer comparer) - { - if (other == null) return 1; - - if (other is not ValueTuple) - { - throw new ArgumentException(SR.ArgumentException_ValueTupleIncorrectType, nameof(other)); - } - - var objTuple = (ValueTuple)other; - - int c = comparer.Compare(Item1, objTuple.Item1); - if (c != 0) return c; - - c = comparer.Compare(Item2, objTuple.Item2); - if (c != 0) return c; - - c = comparer.Compare(Item3, objTuple.Item3); - if (c != 0) return c; - - c = comparer.Compare(Item4, objTuple.Item4); - if (c != 0) return c; - - c = comparer.Compare(Item5, objTuple.Item5); - if (c != 0) return c; - - return comparer.Compare(Item6, objTuple.Item6); - } - - /// - /// Returns the hash code for the current instance. - /// - /// A 32-bit signed integer hash code. - public override int GetHashCode() - { - return ValueTuple.CombineHashCodes(EqualityComparer.Default.GetHashCode(Item1), - EqualityComparer.Default.GetHashCode(Item2), - EqualityComparer.Default.GetHashCode(Item3), - EqualityComparer.Default.GetHashCode(Item4), - EqualityComparer.Default.GetHashCode(Item5), - EqualityComparer.Default.GetHashCode(Item6)); - } - - int IStructuralEquatable.GetHashCode(IEqualityComparer comparer) - { - return GetHashCodeCore(comparer); - } - - private int GetHashCodeCore(IEqualityComparer comparer) - { - return ValueTuple.CombineHashCodes(comparer.GetHashCode(Item1), - comparer.GetHashCode(Item2), - comparer.GetHashCode(Item3), - comparer.GetHashCode(Item4), - comparer.GetHashCode(Item5), - comparer.GetHashCode(Item6)); - } - - int ITupleInternal.GetHashCode(IEqualityComparer comparer) - { - return GetHashCodeCore(comparer); - } - - /// - /// Returns a string that represents the value of this instance. - /// - /// The string representation of this instance. - /// - /// The string returned by this method takes the form (Item1, Item2, Item3, Item4, Item5, Item6). - /// If any field value is , it is represented as . - /// - public override string ToString() - { - return "(" + Item1?.ToString() + ", " + Item2?.ToString() + ", " + Item3?.ToString() + ", " + Item4?.ToString() + ", " + Item5?.ToString() + ", " + Item6?.ToString() + ")"; - } - - string ITupleInternal.ToStringEnd() - { - return Item1?.ToString() + ", " + Item2?.ToString() + ", " + Item3?.ToString() + ", " + Item4?.ToString() + ", " + Item5?.ToString() + ", " + Item6?.ToString() + ")"; - } - - int ITupleInternal.Size => 6; - } - - /// - /// Represents a 7-tuple, or sentuple, as a value type. - /// - /// The type of the tuple's first component. - /// The type of the tuple's second component. - /// The type of the tuple's third component. - /// The type of the tuple's fourth component. - /// The type of the tuple's fifth component. - /// The type of the tuple's sixth component. - /// The type of the tuple's seventh component. - public struct ValueTuple - : IEquatable>, IStructuralEquatable, IStructuralComparable, IComparable, IComparable>, ITupleInternal - { - /// - /// The current instance's first component. - /// - public T1 Item1; - /// - /// The current instance's second component. - /// - public T2 Item2; - /// - /// The current instance's third component. - /// - public T3 Item3; - /// - /// The current instance's fourth component. - /// - public T4 Item4; - /// - /// The current instance's fifth component. - /// - public T5 Item5; - /// - /// The current instance's sixth component. - /// - public T6 Item6; - /// - /// The current instance's seventh component. - /// - public T7 Item7; - - /// - /// Initializes a new instance of the value type. - /// - /// The value of the tuple's first component. - /// The value of the tuple's second component. - /// The value of the tuple's third component. - /// The value of the tuple's fourth component. - /// The value of the tuple's fifth component. - /// The value of the tuple's sixth component. - /// The value of the tuple's seventh component. - public ValueTuple(T1 item1, T2 item2, T3 item3, T4 item4, T5 item5, T6 item6, T7 item7) - { - Item1 = item1; - Item2 = item2; - Item3 = item3; - Item4 = item4; - Item5 = item5; - Item6 = item6; - Item7 = item7; - } - - /// - /// Returns a value that indicates whether the current instance is equal to a specified object. - /// - /// The object to compare with this instance. - /// if the current instance is equal to the specified object; otherwise, . - /// - /// The parameter is considered to be equal to the current instance under the following conditions: - /// - /// It is a value type. - /// Its components are of the same types as those of the current instance. - /// Its components are equal to those of the current instance. Equality is determined by the default object equality comparer for each component. - /// - /// - public override bool Equals(object obj) - { - return obj is ValueTuple tuple && Equals(tuple); - } - - /// - /// Returns a value that indicates whether the current - /// instance is equal to a specified . - /// - /// The tuple to compare with this instance. - /// if the current instance is equal to the specified tuple; otherwise, . - /// - /// The parameter is considered to be equal to the current instance if each of its fields - /// are equal to that of the current instance, using the default comparer for that field's type. - /// - public bool Equals(ValueTuple other) - { - return EqualityComparer.Default.Equals(Item1, other.Item1) - && EqualityComparer.Default.Equals(Item2, other.Item2) - && EqualityComparer.Default.Equals(Item3, other.Item3) - && EqualityComparer.Default.Equals(Item4, other.Item4) - && EqualityComparer.Default.Equals(Item5, other.Item5) - && EqualityComparer.Default.Equals(Item6, other.Item6) - && EqualityComparer.Default.Equals(Item7, other.Item7); - } - - bool IStructuralEquatable.Equals(object? other, IEqualityComparer comparer) - { - if (other == null || other is not ValueTuple) return false; - - var objTuple = (ValueTuple)other; - - return comparer.Equals(Item1, objTuple.Item1) - && comparer.Equals(Item2, objTuple.Item2) - && comparer.Equals(Item3, objTuple.Item3) - && comparer.Equals(Item4, objTuple.Item4) - && comparer.Equals(Item5, objTuple.Item5) - && comparer.Equals(Item6, objTuple.Item6) - && comparer.Equals(Item7, objTuple.Item7); - } - - int IComparable.CompareTo(object other) - { - if (other == null) return 1; - - if (other is not ValueTuple) - { - throw new ArgumentException(SR.ArgumentException_ValueTupleIncorrectType, nameof(other)); - } - - return CompareTo((ValueTuple)other); - } - - /// Compares this instance to a specified instance and returns an indication of their relative values. - /// An instance to compare. - /// - /// A signed number indicating the relative values of this instance and . - /// Returns less than zero if this instance is less than , zero if this - /// instance is equal to , and greater than zero if this instance is greater - /// than . - /// - public int CompareTo(ValueTuple other) - { - int c = Comparer.Default.Compare(Item1, other.Item1); - if (c != 0) return c; - - c = Comparer.Default.Compare(Item2, other.Item2); - if (c != 0) return c; - - c = Comparer.Default.Compare(Item3, other.Item3); - if (c != 0) return c; - - c = Comparer.Default.Compare(Item4, other.Item4); - if (c != 0) return c; - - c = Comparer.Default.Compare(Item5, other.Item5); - if (c != 0) return c; - - c = Comparer.Default.Compare(Item6, other.Item6); - if (c != 0) return c; - - return Comparer.Default.Compare(Item7, other.Item7); - } - - int IStructuralComparable.CompareTo(object? other, IComparer comparer) - { - if (other == null) return 1; - - if (other is not ValueTuple) - { - throw new ArgumentException(SR.ArgumentException_ValueTupleIncorrectType, nameof(other)); - } - - var objTuple = (ValueTuple)other; - - int c = comparer.Compare(Item1, objTuple.Item1); - if (c != 0) return c; - - c = comparer.Compare(Item2, objTuple.Item2); - if (c != 0) return c; - - c = comparer.Compare(Item3, objTuple.Item3); - if (c != 0) return c; - - c = comparer.Compare(Item4, objTuple.Item4); - if (c != 0) return c; - - c = comparer.Compare(Item5, objTuple.Item5); - if (c != 0) return c; - - c = comparer.Compare(Item6, objTuple.Item6); - if (c != 0) return c; - - return comparer.Compare(Item7, objTuple.Item7); - } - - /// - /// Returns the hash code for the current instance. - /// - /// A 32-bit signed integer hash code. - public override int GetHashCode() - { - return ValueTuple.CombineHashCodes(EqualityComparer.Default.GetHashCode(Item1), - EqualityComparer.Default.GetHashCode(Item2), - EqualityComparer.Default.GetHashCode(Item3), - EqualityComparer.Default.GetHashCode(Item4), - EqualityComparer.Default.GetHashCode(Item5), - EqualityComparer.Default.GetHashCode(Item6), - EqualityComparer.Default.GetHashCode(Item7)); - } - - int IStructuralEquatable.GetHashCode(IEqualityComparer comparer) - { - return GetHashCodeCore(comparer); - } - - private int GetHashCodeCore(IEqualityComparer comparer) - { - return ValueTuple.CombineHashCodes(comparer.GetHashCode(Item1), - comparer.GetHashCode(Item2), - comparer.GetHashCode(Item3), - comparer.GetHashCode(Item4), - comparer.GetHashCode(Item5), - comparer.GetHashCode(Item6), - comparer.GetHashCode(Item7)); - } - - int ITupleInternal.GetHashCode(IEqualityComparer comparer) - { - return GetHashCodeCore(comparer); - } - - /// - /// Returns a string that represents the value of this instance. - /// - /// The string representation of this instance. - /// - /// The string returned by this method takes the form (Item1, Item2, Item3, Item4, Item5, Item6, Item7). - /// If any field value is , it is represented as . - /// - public override string ToString() - { - return "(" + Item1?.ToString() + ", " + Item2?.ToString() + ", " + Item3?.ToString() + ", " + Item4?.ToString() + ", " + Item5?.ToString() + ", " + Item6?.ToString() + ", " + Item7?.ToString() + ")"; - } - - string ITupleInternal.ToStringEnd() - { - return Item1?.ToString() + ", " + Item2?.ToString() + ", " + Item3?.ToString() + ", " + Item4?.ToString() + ", " + Item5?.ToString() + ", " + Item6?.ToString() + ", " + Item7?.ToString() + ")"; - } - - int ITupleInternal.Size => 7; - } - - /// - /// Represents an 8-tuple, or octuple, as a value type. - /// - /// The type of the tuple's first component. - /// The type of the tuple's second component. - /// The type of the tuple's third component. - /// The type of the tuple's fourth component. - /// The type of the tuple's fifth component. - /// The type of the tuple's sixth component. - /// The type of the tuple's seventh component. - /// The type of the tuple's eighth component. - public struct ValueTuple - : IEquatable>, IStructuralEquatable, IStructuralComparable, IComparable, IComparable>, ITupleInternal - where TRest : struct - { - /// - /// The current instance's first component. - /// - public T1 Item1; - /// - /// The current instance's second component. - /// - public T2 Item2; - /// - /// The current instance's third component. - /// - public T3 Item3; - /// - /// The current instance's fourth component. - /// - public T4 Item4; - /// - /// The current instance's fifth component. - /// - public T5 Item5; - /// - /// The current instance's sixth component. - /// - public T6 Item6; - /// - /// The current instance's seventh component. - /// - public T7 Item7; - /// - /// The current instance's eighth component. - /// - public TRest Rest; - - /// - /// Initializes a new instance of the value type. - /// - /// The value of the tuple's first component. - /// The value of the tuple's second component. - /// The value of the tuple's third component. - /// The value of the tuple's fourth component. - /// The value of the tuple's fifth component. - /// The value of the tuple's sixth component. - /// The value of the tuple's seventh component. - /// The value of the tuple's eight component. - public ValueTuple(T1 item1, T2 item2, T3 item3, T4 item4, T5 item5, T6 item6, T7 item7, TRest rest) - { - if (rest is not ITupleInternal) - { - throw new ArgumentException(SR.ArgumentException_ValueTupleLastArgumentNotAValueTuple); - } - - Item1 = item1; - Item2 = item2; - Item3 = item3; - Item4 = item4; - Item5 = item5; - Item6 = item6; - Item7 = item7; - Rest = rest; - } - - /// - /// Returns a value that indicates whether the current instance is equal to a specified object. - /// - /// The object to compare with this instance. - /// if the current instance is equal to the specified object; otherwise, . - /// - /// The parameter is considered to be equal to the current instance under the following conditions: - /// - /// It is a value type. - /// Its components are of the same types as those of the current instance. - /// Its components are equal to those of the current instance. Equality is determined by the default object equality comparer for each component. - /// - /// - public override bool Equals(object obj) - { - return obj is ValueTuple tuple && Equals(tuple); - } - - /// - /// Returns a value that indicates whether the current - /// instance is equal to a specified . - /// - /// The tuple to compare with this instance. - /// if the current instance is equal to the specified tuple; otherwise, . - /// - /// The parameter is considered to be equal to the current instance if each of its fields - /// are equal to that of the current instance, using the default comparer for that field's type. - /// - public bool Equals(ValueTuple other) - { - return EqualityComparer.Default.Equals(Item1, other.Item1) - && EqualityComparer.Default.Equals(Item2, other.Item2) - && EqualityComparer.Default.Equals(Item3, other.Item3) - && EqualityComparer.Default.Equals(Item4, other.Item4) - && EqualityComparer.Default.Equals(Item5, other.Item5) - && EqualityComparer.Default.Equals(Item6, other.Item6) - && EqualityComparer.Default.Equals(Item7, other.Item7) - && EqualityComparer.Default.Equals(Rest, other.Rest); - } - - bool IStructuralEquatable.Equals(object? other, IEqualityComparer comparer) - { - if (other == null || other is not ValueTuple) return false; - - var objTuple = (ValueTuple)other; - - return comparer.Equals(Item1, objTuple.Item1) - && comparer.Equals(Item2, objTuple.Item2) - && comparer.Equals(Item3, objTuple.Item3) - && comparer.Equals(Item4, objTuple.Item4) - && comparer.Equals(Item5, objTuple.Item5) - && comparer.Equals(Item6, objTuple.Item6) - && comparer.Equals(Item7, objTuple.Item7) - && comparer.Equals(Rest, objTuple.Rest); - } - - int IComparable.CompareTo(object other) - { - if (other == null) return 1; - - if (other is not ValueTuple) - { - throw new ArgumentException(SR.ArgumentException_ValueTupleIncorrectType, nameof(other)); - } - - return CompareTo((ValueTuple)other); - } - - /// Compares this instance to a specified instance and returns an indication of their relative values. - /// An instance to compare. - /// - /// A signed number indicating the relative values of this instance and . - /// Returns less than zero if this instance is less than , zero if this - /// instance is equal to , and greater than zero if this instance is greater - /// than . - /// - public int CompareTo(ValueTuple other) - { - int c = Comparer.Default.Compare(Item1, other.Item1); - if (c != 0) return c; - - c = Comparer.Default.Compare(Item2, other.Item2); - if (c != 0) return c; - - c = Comparer.Default.Compare(Item3, other.Item3); - if (c != 0) return c; - - c = Comparer.Default.Compare(Item4, other.Item4); - if (c != 0) return c; - - c = Comparer.Default.Compare(Item5, other.Item5); - if (c != 0) return c; - - c = Comparer.Default.Compare(Item6, other.Item6); - if (c != 0) return c; - - c = Comparer.Default.Compare(Item7, other.Item7); - if (c != 0) return c; - - return Comparer.Default.Compare(Rest, other.Rest); - } - - int IStructuralComparable.CompareTo(object? other, IComparer comparer) - { - if (other == null) return 1; - - if (other is not ValueTuple) - { - throw new ArgumentException(SR.ArgumentException_ValueTupleIncorrectType, nameof(other)); - } - - var objTuple = (ValueTuple)other; - - int c = comparer.Compare(Item1, objTuple.Item1); - if (c != 0) return c; - - c = comparer.Compare(Item2, objTuple.Item2); - if (c != 0) return c; - - c = comparer.Compare(Item3, objTuple.Item3); - if (c != 0) return c; - - c = comparer.Compare(Item4, objTuple.Item4); - if (c != 0) return c; - - c = comparer.Compare(Item5, objTuple.Item5); - if (c != 0) return c; - - c = comparer.Compare(Item6, objTuple.Item6); - if (c != 0) return c; - - c = comparer.Compare(Item7, objTuple.Item7); - if (c != 0) return c; - - return comparer.Compare(Rest, objTuple.Rest); - } - - /// - /// Returns the hash code for the current instance. - /// - /// A 32-bit signed integer hash code. - public override int GetHashCode() - { - // We want to have a limited hash in this case. We'll use the last 8 elements of the tuple - if (Rest is not ITupleInternal rest) - { - return ValueTuple.CombineHashCodes(EqualityComparer.Default.GetHashCode(Item1), - EqualityComparer.Default.GetHashCode(Item2), - EqualityComparer.Default.GetHashCode(Item3), - EqualityComparer.Default.GetHashCode(Item4), - EqualityComparer.Default.GetHashCode(Item5), - EqualityComparer.Default.GetHashCode(Item6), - EqualityComparer.Default.GetHashCode(Item7)); - } - - int size = rest.Size; - if (size >= 8) { return rest.GetHashCode(); } - - // In this case, the rest member has less than 8 elements so we need to combine some our elements with the elements in rest - int k = 8 - size; - switch (k) - { - case 1: - return ValueTuple.CombineHashCodes(EqualityComparer.Default.GetHashCode(Item7), - rest.GetHashCode()); - case 2: - return ValueTuple.CombineHashCodes(EqualityComparer.Default.GetHashCode(Item6), - EqualityComparer.Default.GetHashCode(Item7), - rest.GetHashCode()); - case 3: - return ValueTuple.CombineHashCodes(EqualityComparer.Default.GetHashCode(Item5), - EqualityComparer.Default.GetHashCode(Item6), - EqualityComparer.Default.GetHashCode(Item7), - rest.GetHashCode()); - case 4: - return ValueTuple.CombineHashCodes(EqualityComparer.Default.GetHashCode(Item4), - EqualityComparer.Default.GetHashCode(Item5), - EqualityComparer.Default.GetHashCode(Item6), - EqualityComparer.Default.GetHashCode(Item7), - rest.GetHashCode()); - case 5: - return ValueTuple.CombineHashCodes(EqualityComparer.Default.GetHashCode(Item3), - EqualityComparer.Default.GetHashCode(Item4), - EqualityComparer.Default.GetHashCode(Item5), - EqualityComparer.Default.GetHashCode(Item6), - EqualityComparer.Default.GetHashCode(Item7), - rest.GetHashCode()); - case 6: - return ValueTuple.CombineHashCodes(EqualityComparer.Default.GetHashCode(Item2), - EqualityComparer.Default.GetHashCode(Item3), - EqualityComparer.Default.GetHashCode(Item4), - EqualityComparer.Default.GetHashCode(Item5), - EqualityComparer.Default.GetHashCode(Item6), - EqualityComparer.Default.GetHashCode(Item7), - rest.GetHashCode()); - case 7: - case 8: - return ValueTuple.CombineHashCodes(EqualityComparer.Default.GetHashCode(Item1), - EqualityComparer.Default.GetHashCode(Item2), - EqualityComparer.Default.GetHashCode(Item3), - EqualityComparer.Default.GetHashCode(Item4), - EqualityComparer.Default.GetHashCode(Item5), - EqualityComparer.Default.GetHashCode(Item6), - EqualityComparer.Default.GetHashCode(Item7), - rest.GetHashCode()); - } - - Debug.Assert(false, "Missed all cases for computing ValueTuple hash code"); - return -1; - } - - int IStructuralEquatable.GetHashCode(IEqualityComparer comparer) - { - return GetHashCodeCore(comparer); - } - - private int GetHashCodeCore(IEqualityComparer comparer) - { - // We want to have a limited hash in this case. We'll use the last 8 elements of the tuple - if (Rest is not ITupleInternal rest) - { - return ValueTuple.CombineHashCodes( - comparer.GetHashCode(Item1), - comparer.GetHashCode(Item2), - comparer.GetHashCode(Item3), - comparer.GetHashCode(Item4), - comparer.GetHashCode(Item5), - comparer.GetHashCode(Item6), - comparer.GetHashCode(Item7)); - } - - int size = rest.Size; - if (size >= 8) { return rest.GetHashCode(comparer); } - - // In this case, the rest member has less than 8 elements so we need to combine some our elements with the elements in rest - int k = 8 - size; - switch (k) - { - case 1: - return ValueTuple.CombineHashCodes(comparer.GetHashCode(Item7), rest.GetHashCode(comparer)); - case 2: - return ValueTuple.CombineHashCodes(comparer.GetHashCode(Item6), comparer.GetHashCode(Item7), rest.GetHashCode(comparer)); - case 3: - return ValueTuple.CombineHashCodes(comparer.GetHashCode(Item5), comparer.GetHashCode(Item6), comparer.GetHashCode(Item7), - rest.GetHashCode(comparer)); - case 4: - return ValueTuple.CombineHashCodes(comparer.GetHashCode(Item4), comparer.GetHashCode(Item5), comparer.GetHashCode(Item6), - comparer.GetHashCode(Item7), rest.GetHashCode(comparer)); - case 5: - return ValueTuple.CombineHashCodes(comparer.GetHashCode(Item3), comparer.GetHashCode(Item4), comparer.GetHashCode(Item5), - comparer.GetHashCode(Item6), comparer.GetHashCode(Item7), rest.GetHashCode(comparer)); - case 6: - return ValueTuple.CombineHashCodes(comparer.GetHashCode(Item2), comparer.GetHashCode(Item3), comparer.GetHashCode(Item4), - comparer.GetHashCode(Item5), comparer.GetHashCode(Item6), comparer.GetHashCode(Item7), - rest.GetHashCode(comparer)); - case 7: - case 8: - return ValueTuple.CombineHashCodes(comparer.GetHashCode(Item1), comparer.GetHashCode(Item2), comparer.GetHashCode(Item3), - comparer.GetHashCode(Item4), comparer.GetHashCode(Item5), comparer.GetHashCode(Item6), - comparer.GetHashCode(Item7), rest.GetHashCode(comparer)); - } - - Debug.Assert(false, "Missed all cases for computing ValueTuple hash code"); - return -1; - } - - int ITupleInternal.GetHashCode(IEqualityComparer comparer) - { - return GetHashCodeCore(comparer); - } - - /// - /// Returns a string that represents the value of this instance. - /// - /// The string representation of this instance. - /// - /// The string returned by this method takes the form (Item1, Item2, Item3, Item4, Item5, Item6, Item7, Rest). - /// If any field value is , it is represented as . - /// - public override string ToString() - { - if (Rest is not ITupleInternal rest) - { - return "(" + Item1?.ToString() + ", " + Item2?.ToString() + ", " + Item3?.ToString() + ", " + Item4?.ToString() + ", " + Item5?.ToString() + ", " + Item6?.ToString() + ", " + Item7?.ToString() + ", " + Rest.ToString() + ")"; - } - else - { - return "(" + Item1?.ToString() + ", " + Item2?.ToString() + ", " + Item3?.ToString() + ", " + Item4?.ToString() + ", " + Item5?.ToString() + ", " + Item6?.ToString() + ", " + Item7?.ToString() + ", " + rest.ToStringEnd(); - } - } - - string ITupleInternal.ToStringEnd() - { - if (Rest is not ITupleInternal rest) - { - return Item1?.ToString() + ", " + Item2?.ToString() + ", " + Item3?.ToString() + ", " + Item4?.ToString() + ", " + Item5?.ToString() + ", " + Item6?.ToString() + ", " + Item7?.ToString() + ", " + Rest.ToString() + ")"; - } - else - { - return Item1?.ToString() + ", " + Item2?.ToString() + ", " + Item3?.ToString() + ", " + Item4?.ToString() + ", " + Item5?.ToString() + ", " + Item6?.ToString() + ", " + Item7?.ToString() + ", " + rest.ToStringEnd(); - } - } - - int ITupleInternal.Size - { - get - { - // ITupleInternal? rest = Rest as ITupleInternal; - // return rest == null ? 8 : 7 + rest.Size; - return Rest is not ITupleInternal rest ? 8 : 7 + rest.Size; - } - } - } -} \ No newline at end of file diff --git a/src/IFoxCAD.Basal/DictEx.cs b/src/IFoxCAD.Basal/DictEx.cs deleted file mode 100644 index 296fb16..0000000 --- a/src/IFoxCAD.Basal/DictEx.cs +++ /dev/null @@ -1,15 +0,0 @@ -namespace IFoxCAD.Basal; - -public static class DictEx -{ - // public static TKey? GetKey(this IDictionary dict!!, TKey key!!) - // { - // if (dict.ContainsKey(key)) - // { - // foreach (var item in dict.Keys) - // if (key.Equals(item)) - // return item; - // } - // return default; - // } -} diff --git a/src/IFoxCAD.Basal/EnumEx.cs b/src/IFoxCAD.Basal/EnumEx.cs deleted file mode 100644 index 46aa077..0000000 --- a/src/IFoxCAD.Basal/EnumEx.cs +++ /dev/null @@ -1,105 +0,0 @@ -namespace IFoxCAD.Basal; - -using System.ComponentModel; -using System.Linq; - -public static class EnumEx -{ - /// - /// 清理缓存 - /// - public static void CleanCache() - { - _cache.Clear(); - } - - // (类型完整名,描述组合) - static readonly Dictionary> _cache = new(); - - /// - /// 打印枚举的特性注释内容 - /// - /// 枚举 - /// 注释内容 - public static HashSet? GetAttribute(this Enum e, bool noDescrToString = true) - where T : DescriptionAttribute - { - var eType = e.GetType(); - string eFullName = eType.FullName + "." + e.ToString(); - - if (_cache.ContainsKey(eFullName)) - return _cache[eFullName]; - - var fieldInfo = eType.GetField(Enum.GetName(eType, e)); - if (fieldInfo == null) - return null!; - - // 注释存放的容器 - HashSet nodes = new(); - if (Attribute.GetCustomAttribute(fieldInfo, typeof(T)) is T attribute) - { - nodes.Add(attribute.Description); - _cache.Add(eFullName, nodes); - return nodes; - } - - // 通常到这里的就是 ALL = A | B | C - // 遍历所有的枚举,组合每个注释 - List enumHas = new(); - - // 遍历这个枚举类型,获取枚举按位包含的成员 - foreach (Enum em in Enum.GetValues(eType)) - if ((e.GetHashCode() & em.GetHashCode()) == em.GetHashCode() && - e.GetHashCode() != em.GetHashCode()) - enumHas.Add(em); - - - // 采取的行为是:注释的行为是特殊的,就按照注释的,否则,遍历子元素提取注释 - // 大的在前面才能判断是否按位包含后面的,后面的就是要移除的 - enumHas = enumHas.OrderByDescending(a => a.GetHashCode()).ToList(); - ArrayEx.Deduplication(enumHas, (a, b) => { - return (a.GetHashCode() & b.GetHashCode()) == b.GetHashCode(); - }); - - // 逆序仅仅为排序后处理,不一定和书写顺序一样,尤其是递归可能存在重复的元素 - for (int i = enumHas.Count - 1; i >= 0; i--) - { - var atts = GetAttribute(enumHas[i], noDescrToString);// 递归 - if (atts == null) - continue; - foreach (var item in atts) - nodes.Add(item); - } - - if (nodes.Count == 0 && noDescrToString) - nodes.Add(e.ToString()); - - _cache.Add(eFullName, nodes); - return nodes; - } - - /// - /// 打印枚举的特性注释内容 - /// - public static string? PrintNote(this Enum e, bool noDescToString = true) - { - var hash = GetAttribute(e, noDescToString); - if (hash != null) - return string.Join("|", hash.ToArray()); - return null; - } - - - // TODO 山人审核代码之后可以删除,这个完全被上面替代了 - public static string GetDesc(this Enum val) - { - var type = val.GetType(); - var memberInfo = type.GetMember(val.ToString()); - var attributes = memberInfo[0].GetCustomAttributes(typeof(DescriptionAttribute), false); - // 如果没有定义描述,就把当前枚举值的对应名称返回 - if (attributes is null || attributes.Length != 1) - return val.ToString(); - - return ((DescriptionAttribute)attributes.Single()).Description; - } -} \ No newline at end of file diff --git a/src/IFoxCAD.Basal/GlobalUsings.cs b/src/IFoxCAD.Basal/GlobalUsings.cs deleted file mode 100644 index 15c8a97..0000000 --- a/src/IFoxCAD.Basal/GlobalUsings.cs +++ /dev/null @@ -1,9 +0,0 @@ -/// 系统引用 -global using System; -global using System.Collections; -global using System.Collections.Generic; -global using System.IO; -global using System.Linq; -global using System.Text; -global using System.Reflection; - diff --git a/src/IFoxCAD.Basal/IFoxCAD.Basal.csproj b/src/IFoxCAD.Basal/IFoxCAD.Basal.csproj deleted file mode 100644 index dbc9743..0000000 --- a/src/IFoxCAD.Basal/IFoxCAD.Basal.csproj +++ /dev/null @@ -1,34 +0,0 @@ - - - - preview - enable - - NET35;NET40;NET45 - true - 0.4 - InspireFunction - xsfhlzh;vicwjb;liuqihong - 基于.NET的二次开发基本类库 - InspireFunction - https://gitee.com/inspirefunction/ifoxcad - https://gitee.com/inspirefunction/ifoxcad.git - git - IFoxCAD;C#;NET;Common;Basal - 直接sortedset. - true - true - LICENSE - true - True - none - - - - - True - - - - - diff --git a/src/IFoxCAD.Basal/LinkedHashMap.cs b/src/IFoxCAD.Basal/LinkedHashMap.cs deleted file mode 100644 index 0e6232c..0000000 --- a/src/IFoxCAD.Basal/LinkedHashMap.cs +++ /dev/null @@ -1,171 +0,0 @@ -namespace IFoxCAD.Basal; - - -/// -/// A least-recently-used cache stored like a dictionary. -/// -/// -/// The type of the key to the cached item -/// -/// -/// The type of the cached item. -/// -/// -/// Derived from https://stackoverflow.com/a/3719378/240845 -/// https://stackoverflow.com/users/240845/mheyman -/// -public class LinkedHashMap -{ - private readonly Dictionary> cacheMap = new(); - - private readonly LinkedList lruList = new(); - - private readonly Action? dispose; - - /// - /// Initializes a new instance of the - /// class. - /// - /// - /// Maximum number of elements to cache. - /// - /// - /// When elements cycle out of the cache, disposes them. May be null. - /// - public LinkedHashMap(int capacity, Action? dispose = null) - { - this.Capacity = capacity; - this.dispose = dispose; - } - - /// - /// Gets the capacity of the cache. - /// - public int Capacity { get; } - - /// Gets the value associated with the specified key. - /// - /// The key of the value to get. - /// - /// - /// When this method returns, contains the value associated with the specified - /// key, if the key is found; otherwise, the default value for the type of the - /// parameter. This parameter is passed - /// uninitialized. - /// - /// - /// true if the - /// contains an element with the specified key; otherwise, false. - /// - public bool TryGetValue(TKey key, out TValue? value) - { - lock (this.cacheMap) - { - if (this.cacheMap.TryGetValue(key, out LinkedListNode.MapItem> node)) - { - value = node.Value.Value; - this.lruList.Remove(node); - this.lruList.AddLast(node); - return true; - } - - value = default; - return false; - } - } - - /// - /// Looks for a value for the matching . If not found, - /// calls to retrieve the value and add it to - /// the cache. - /// - /// - /// The key of the value to look up. - /// - /// - /// Generates a value if one isn't found. - /// - /// - /// The requested value. - /// - public TValue Get(TKey key, Func valueGenerator) - { - lock (this.cacheMap) - { - TValue value; - if (this.cacheMap.TryGetValue(key, out LinkedListNode.MapItem> node)) - { - value = node.Value.Value; - this.lruList.Remove(node); - this.lruList.AddLast(node); - } - else - { - value = valueGenerator(); - if (this.cacheMap.Count >= this.Capacity) - { - this.RemoveFirst(); - } - - MapItem cacheItem = new(key, value); - node = new LinkedListNode(cacheItem); - this.lruList.AddLast(node); - this.cacheMap.Add(key, node); - } - - return value; - } - } - - /// - /// Adds the specified key and value to the dictionary. - /// - /// - /// The key of the element to add. - /// - /// - /// The value of the element to add. The value can be null for reference types. - /// - public void Add(TKey key, TValue value) - { - lock (this.cacheMap) - { - if (this.cacheMap.Count >= this.Capacity) - { - this.RemoveFirst(); - } - - MapItem cacheItem = new(key, value); - LinkedListNode node = new(cacheItem); - this.lruList.AddLast(node); - this.cacheMap.Add(key, node); - } - } - - private void RemoveFirst() - { - // Remove from LRUPriority - LinkedListNode node = this.lruList.First; - this.lruList.RemoveFirst(); - - // Remove from cache - this.cacheMap.Remove(node.Value.Key); - - // dispose - this.dispose?.Invoke(node.Value.Value); - } - - private class MapItem - { - public MapItem(TKey k, TValue v) - { - this.Key = k; - this.Value = v; - } - - public TKey Key { get; } - - public TValue Value { get; } - } -} - diff --git a/src/IFoxCAD.Basal/LinkedHashSet.cs b/src/IFoxCAD.Basal/LinkedHashSet.cs deleted file mode 100644 index 6295795..0000000 --- a/src/IFoxCAD.Basal/LinkedHashSet.cs +++ /dev/null @@ -1,221 +0,0 @@ -namespace IFoxCAD.Basal; - -public class LinkedHashSet : ICollection where T : IComparable -{ - private readonly IDictionary> m_Dictionary; - private readonly LoopList m_LinkedList; - - public LinkedHashSet() - { - m_Dictionary = new Dictionary>(); - m_LinkedList = new LoopList(); - } - - public LoopListNode? First => m_LinkedList.First; - - public LoopListNode? Last => m_LinkedList.Last; - - public LoopListNode? MinNode { get; set; } - - public bool Add(T item) - { - if (m_Dictionary.ContainsKey(item)) - return false; - var node = m_LinkedList.AddLast(item); - m_Dictionary.Add(item, node); - - if (MinNode is null) - { - MinNode = node; - } - else - { - if (item.CompareTo(MinNode.Value) < 0) - { - MinNode = node; - } - } - - - - return true; - } - - void ICollection.Add(T item) - { - Add(item); - } - - public LoopListNode AddFirst(T value) - { - if (m_Dictionary.ContainsKey(value)) - { - return m_Dictionary[value]; - } - var node = m_LinkedList.AddFirst(value); - m_Dictionary.Add(value, node); - if (MinNode is null) - { - MinNode = node; - } - else - { - if (value.CompareTo(MinNode.Value) < 0) - { - MinNode = node; - } - } - return node; - } - - public void AddRange(IEnumerable collection) - { - foreach (var item in collection) - { - Add(item); - } - } - - - public void Clear() - { - m_LinkedList.Clear(); - m_Dictionary.Clear(); - } - - public bool Remove(T item) - { - bool found = m_Dictionary.TryGetValue(item, out LoopListNode node); - if (!found) return false; - m_Dictionary.Remove(item); - m_LinkedList.Remove(node); - return true; - } - - public int Count - { - get { return m_Dictionary.Count; } - } - - public void For(LoopListNode from, Action action) - { - var first = from; - var last = from; - if(first is null) return; - - for (int i = 0; i < Count; i++) - { - - action.Invoke(i,first!.Value, last!.Value); - first = first.Next; - last = last.Previous; - } - } - - public List ToList() - { - return m_LinkedList.ToList(); - } - - public IEnumerator GetEnumerator() - { - return m_LinkedList.GetEnumerator(); - } - - IEnumerator IEnumerable.GetEnumerator() - { - return GetEnumerator(); - } - - - public bool Contains(T item) - { - return m_Dictionary.ContainsKey(item); - } - - public void CopyTo(T[] array, int arrayIndex) - { - // m_LinkedList.CopyTo(array, arrayIndex); - return; - } - - public bool SetFirst(LoopListNode node) - { - return m_LinkedList.SetFirst(node); - } - - public LinkedHashSet Clone() - { - var newset = new LinkedHashSet(); - foreach (var item in this) - { - newset.Add(item); - } - return newset; - } - - public virtual bool IsReadOnly - { - get { return m_Dictionary.IsReadOnly; } - } - - public override string ToString() - { - return m_LinkedList.ToString(); - } - - public void UnionWith(IEnumerable other) - { - throw GetNotSupportedDueToSimplification(); - } - - public void IntersectWith(IEnumerable other) - { - throw GetNotSupportedDueToSimplification(); - } - - public void ExceptWith(IEnumerable other) - { - throw GetNotSupportedDueToSimplification(); - } - - public bool IsSubsetOf(IEnumerable other) - { - throw GetNotSupportedDueToSimplification(); - } - - public void SymmetricExceptWith(IEnumerable other) - { - throw GetNotSupportedDueToSimplification(); - } - - public bool IsSupersetOf(IEnumerable other) - { - throw GetNotSupportedDueToSimplification(); - } - - public bool IsProperSupersetOf(IEnumerable other) - { - throw GetNotSupportedDueToSimplification(); - } - - public bool IsProperSubsetOf(IEnumerable other) - { - throw GetNotSupportedDueToSimplification(); - } - - public bool Overlaps(IEnumerable other) - { - throw GetNotSupportedDueToSimplification(); - } - - public bool SetEquals(IEnumerable other) - { - throw GetNotSupportedDueToSimplification(); - } - - private static Exception GetNotSupportedDueToSimplification() - { - return new NotSupportedException("This method is not supported due to simplification of example code."); - } -} diff --git a/src/IFoxCAD.Basal/LinqEx.cs b/src/IFoxCAD.Basal/LinqEx.cs deleted file mode 100644 index 8112e63..0000000 --- a/src/IFoxCAD.Basal/LinqEx.cs +++ /dev/null @@ -1,335 +0,0 @@ -namespace IFoxCAD.Basal; - -/// -/// linq 扩展类 -/// -public static class LinqEx -{ - #region FindByMax - - /// - /// 按转换函数找出序列中最大键值的对应值 - /// - /// - /// - /// 序列 - /// 转换函数 - /// 最大键值的对应值 - public static TValue FindByMax(this IEnumerable source, Func func) - where TKey : IComparable - { - var itor = source.GetEnumerator(); - if (!itor.MoveNext()) - throw new ArgumentNullException(nameof(source), "对象为 null"); - - TValue value = itor.Current; - TKey key = func(value); - - while (itor.MoveNext()) - { - TKey tkey = func(itor.Current); - if (tkey.CompareTo(key) > 0) - { - key = tkey; - value = itor.Current; - } - } - return value; - } - - /// - /// 按转换函数找出序列中最大键值的对应值 - /// - /// - /// - /// 序列 - /// 对应的最大键值 - /// 转换函数 - /// 最大键值的对应值 - public static TValue FindByMax(this IEnumerable source, out TKey maxResult, Func func) - where TKey : IComparable - { - var itor = source.GetEnumerator(); - if (!itor.MoveNext()) - throw new ArgumentNullException(nameof(source), "对象为 null"); - - TValue value = itor.Current; - TKey key = func(value); - - while (itor.MoveNext()) - { - TKey tkey = func(itor.Current); - if (tkey.CompareTo(key) > 0) - { - key = tkey; - value = itor.Current; - } - } - maxResult = key; - return value; - } - - /// - /// 按比较器找出序列中最大键值的对应值 - /// - /// - /// 序列 - /// 比较器 - /// 最大键值的对应值 - public static TValue FindByMax(this IEnumerable source, Comparison comparison) - { - var itor = source.GetEnumerator(); - if (!itor.MoveNext()) - throw new ArgumentNullException(nameof(source), "对象为 null"); - - TValue value = itor.Current; - - while (itor.MoveNext()) - { - if (comparison(itor.Current, value) > 0) - value = itor.Current; - } - return value; - } - - #endregion FindByMax - - #region FindByMin - - /// - /// 按转换函数找出序列中最小键值的对应值 - /// - /// - /// - /// 序列 - /// 对应的最小键值 - /// 转换函数 - /// 最小键值的对应值 - public static TValue FindByMin(this IEnumerable source, out TKey minKey, Func func) - where TKey : IComparable - { - var itor = source.GetEnumerator(); - if (!itor.MoveNext()) - throw new ArgumentNullException(nameof(source), "对象为 null"); - - TValue value = itor.Current; - TKey key = func(value); - - while (itor.MoveNext()) - { - TKey tkey = func(itor.Current); - if (tkey.CompareTo(key) < 0) - { - key = tkey; - value = itor.Current; - } - } - minKey = key; - return value; - } - - /// - /// 按转换函数找出序列中最小键值的对应值 - /// - /// - /// - /// 序列 - /// 转换函数 - /// 最小键值的对应值 - public static TValue FindByMin(this IEnumerable source, Func func) - where TKey : IComparable - { - var itor = source.GetEnumerator(); - if (!itor.MoveNext()) - throw new ArgumentNullException(nameof(source), "对象为 null"); - - TValue value = itor.Current; - TKey key = func(value); - - while (itor.MoveNext()) - { - TKey tkey = func(itor.Current); - if (tkey.CompareTo(key) < 0) - { - key = tkey; - value = itor.Current; - } - } - return value; - } - - /// - /// 按比较器找出序列中最小键值的对应值 - /// - /// - /// 序列 - /// 比较器 - /// 最小键值的对应值 - public static TValue FindByMin(this IEnumerable source, Comparison comparison) - { - var itor = source.GetEnumerator(); - if (!itor.MoveNext()) - throw new ArgumentNullException(nameof(source), "对象为 null"); - - TValue value = itor.Current; - - while (itor.MoveNext()) - { - if (comparison(itor.Current, value) < 0) - value = itor.Current; - } - return value; - } - - #endregion FindByMin - - #region FindByExt - - /// - /// 按转换函数找出序列中最(小/大)键值的对应值 - /// - /// - /// - /// 序列 - /// 转换函数 - /// 最(小/大)键值的对应值 - public static TValue[] FindByExt(this IEnumerable source, Func func) - where TKey : IComparable - { - var itor = source.GetEnumerator(); - if (!itor.MoveNext()) - throw new ArgumentNullException(nameof(source), "对象为 null"); - - TValue[] values = new TValue[2]; - values[0] = values[1] = itor.Current; - - TKey[] keys = new TKey[2]; - keys[0] = keys[1] = func(itor.Current); - - while (itor.MoveNext()) - { - TKey tkey = func(itor.Current); - if (tkey.CompareTo(keys[0]) < 0) - { - keys[0] = tkey; - values[0] = itor.Current; - } - else if (tkey.CompareTo(keys[1]) > 0) - { - keys[1] = tkey; - values[1] = itor.Current; - } - } - return values; - } - - /// - /// 按比较器找出序列中最(小/大)键值的对应值 - /// - /// - /// 序列 - /// 比较器 - /// 最(小/大)键值的对应值 - public static TValue[] FindByExt(this IEnumerable source, Comparison comparison) - { - var itor = source.GetEnumerator(); - if (!itor.MoveNext()) - throw new ArgumentNullException(nameof(source), "对象为 null"); - - TValue[] values = new TValue[2]; - values[0] = values[1] = itor.Current; - - while (itor.MoveNext()) - { - if (comparison(itor.Current, values[0]) < 0) - values[0] = itor.Current; - else if (comparison(itor.Current, values[1]) > 0) - values[1] = itor.Current; - } - return values; - } - - /// - /// 按转换函数找出序列中最(小/大)键值的对应键值 - /// - /// - /// - /// 序列 - /// 转换函数 - /// 最(小/大)键值 - public static TKey[] FindExt(this IEnumerable source, Func func) - where TKey : IComparable - { - var itor = source.GetEnumerator(); - if (!itor.MoveNext()) - throw new ArgumentNullException(nameof(source), "对象为 null"); - - TKey[] keys = new TKey[2]; - keys[0] = keys[1] = func(itor.Current); - - while (itor.MoveNext()) - { - TKey tkey = func(itor.Current); - if (tkey.CompareTo(keys[0]) < 0) - keys[0] = tkey; - else if (tkey.CompareTo(keys[1]) > 0) - keys[1] = tkey; - } - return keys; - } - - #endregion FindByExt - - #region Order - - /// - /// 自定义的比较泛型类 - /// - /// 泛型 - private class SpecComparer : IComparer - { - private readonly Comparison _comp; - - internal SpecComparer(Comparison comp) - { - _comp = comp; - } - - #region IComparer 成员 - public int Compare(T x, T y) - { - return _comp(x, y); - } - #endregion IComparer 成员 - } - - /// - /// 使用指定的比较器将序列按升序排序 - /// - /// 输入泛型 - /// 输出泛型 - /// 序列 - /// 用于从元素中提取键的函数 - /// 比较器 - /// 排序的序列 - public static IOrderedEnumerable OrderBy(this IEnumerable source, Func keySelector, Comparison comparison) - { - return source.OrderBy(keySelector, new SpecComparer(comparison)); - } - - /// - /// 使用指定的比较器将其后的序列按升序排序 - /// - /// 输入泛型 - /// 输出泛型 - /// 序列 - /// 用于从元素中提取键的函数 - /// 比较器 - /// 排序的序列 - public static IOrderedEnumerable ThenBy(this IOrderedEnumerable source, Func keySelector, Comparison comparison) - { - return source.ThenBy(keySelector, new SpecComparer(comparison)); - } - - #endregion Order -} diff --git a/src/IFoxCAD.Basal/ListEx.cs b/src/IFoxCAD.Basal/ListEx.cs deleted file mode 100644 index 528b6ad..0000000 --- a/src/IFoxCAD.Basal/ListEx.cs +++ /dev/null @@ -1,30 +0,0 @@ -namespace IFoxCAD.Basal; - - -public static class ListEx -{ - public static bool EqualsAll(this IList a, IList b) - { - return EqualsAll(a, b, null); - // there is a slight performance gain in passing null here. - // It is how it is done in other parts of the framework. - } - - public static bool EqualsAll(this IList a, IList b, IEqualityComparer? comparer) - { - if (a is null) - return b is null; - else if (b is null) - return false; - - if (a.Count != b.Count) - return false; - - comparer ??= EqualityComparer.Default; - - for (int i = 0; i < a.Count; i++) - if (!comparer.Equals(a[i], b[i])) - return false; - return true; - } -} diff --git a/src/IFoxCAD.Basal/LoopList.cs b/src/IFoxCAD.Basal/LoopList.cs deleted file mode 100644 index 9bd986b..0000000 --- a/src/IFoxCAD.Basal/LoopList.cs +++ /dev/null @@ -1,742 +0,0 @@ -namespace IFoxCAD.Basal; - -#line hidden // 调试的时候跳过它 - -/// -/// 环链表节点 -/// -/// -public class LoopListNode -{ - #region 成员 - /// - /// 取值 - /// - public T Value; - - /// - /// 上一个节点 - /// - public LoopListNode? Previous { internal set; get; } - - /// - /// 下一个节点 - /// - public LoopListNode? Next { internal set; get; } - - /// - /// 环链表序列 - /// - public LoopList? List { internal set; get; } - #endregion - - #region 构造 - /// - /// 环链表节点构造函数 - /// - /// 节点值 - public LoopListNode(T value, LoopList ts) - { - Value = value; - List = ts; - } - - /// - /// 获取当前节点的临近节点 - /// - /// 搜索方向标志,为向前搜索,为向后搜索 - /// - public LoopListNode? GetNext(bool forward) - { - return forward ? Next : Previous; - } - #endregion - - #region 方法 - /// - /// 无效化成员 - /// - internal void Invalidate() - { - List = null; - Next = null; - Previous = null; - } - #endregion -} - -/// -/// 环链表 -/// -/// -public class LoopList : IEnumerable, IFormattable -{ - #region 成员 - - /// - /// 节点数 - /// - public int Count { get; private set; } - - /// - /// 首节点 - /// - public LoopListNode? First { get; private set; } - - /// - /// 尾节点 - /// - public LoopListNode? Last => First?.Previous; - - - - - #endregion - - #region 构造 - - /// - /// 默认构造函数 - /// - public LoopList() { } - - /// - /// 环链表构造函数 - /// - /// 节点迭代器 - public LoopList(IEnumerable values) - { - var ge = values.GetEnumerator(); - while (ge.MoveNext()) - Add(ge.Current); - } - - #endregion - - #region 方法 - - /// - /// 设置首节点 - /// - /// 节点 - /// - public bool SetFirst(LoopListNode node) - { - if (!Contains(node)) - return false; - - First = node; - return true; - } - - /// - /// 交换两个节点的值 - /// - /// 第一个节点 - /// 第二个节点 - public void Swap(LoopListNode node1, LoopListNode node2) - { - (node2.Value, node1.Value) = (node1.Value, node2.Value); - } - - /// - /// 链内翻转 - /// - public void Reverse() - { - var first = First; - if (first is null) - return; - var last = Last; - for (int i = 0; i < Count / 2; i++) - { - Swap(first!, last!); - first = first!.Next; - last = last!.Previous; - } - } - - /// - /// 清理 - /// - public void Clear() - { - // 移除头部,表示链表再也无法遍历得到 - First = null; - Count = 0; - } - - /// - /// 从头遍历_非迭代器(此处和通用ForEach冲突,所以内部用) - /// - /// - void ForEach(Func, bool> action) - { - var node = First; - if (node is null) - return; - for (int i = 0; i < Count; i++) - { - if (action(node!)) - break; - node = node!.Next; - } - } - - /// - /// 从头遍历_非迭代器(扔出计数) - /// - /// - public void For(Func, bool> action) - { - var node = First; - if (node is null) - return; - for (int i = 0; i < Count; i++) - { - if (action(i, node!)) - break; - node = node!.Next; - } - } - - #region Contains - - /// - /// 是否包含节点 - /// - /// - /// - public bool Contains(LoopListNode node) - { - return node is not null && node.List == this; - } - - /// - /// 是否包含值 - /// - /// - /// - public bool Contains(T value) - { - bool result = false; - ForEach(node => { - if (node.Value!.Equals(value)) - { - result = true; - return true; - } - return false; - }); - return result; - } - - /// - /// 查找第一个出现的节点 - /// - /// - /// - public LoopListNode? Find(T value) - { - // LoopListNode result = null; - // ForEach(node => - // { - // if (node.Value.Equals(t2)) - // { - // result = node; - // return true; - // } - // return false; - // }); - // return result; - - LoopListNode? node = First; - var c = EqualityComparer.Default; - if (node is not null) - { - if (value is not null) - { - do - { - if (c.Equals(node!.Value, value)) - return node; - node = node.Next; - } while (node != First); - } - else - { - do - { - if (node!.Value is null) - return node; - node = node.Next; - } while (node != First); - } - } - return null; - } - - /// - /// 查找所有出现的节点 - /// - /// - /// - public IEnumerable>? Finds(T value) - { - LoopListNode? node = First; - if (node is null) - return null; - - List> result = new(); - var c = EqualityComparer.Default; - if (value is not null) - { - do - { - if (c.Equals(node!.Value, value)) - result.Add(node); - node = node.Next; - } while (node != First); - } - else - { - do - { - if (node!.Value is null) - result.Add(node); - node = node.Next; - } while (node != First); - } - return result; - } - - /// - /// 获取节点 - /// - /// - /// - public LoopListNode? GetNode(Func func) - { - LoopListNode? result = null; - ForEach(node => { - if (func(node.Value)) - { - result = node; - return true; - } - return false; - }); - return result; - } - - #endregion - - #region Add - - /// - /// 在首节点之前插入节点,并设置新节点为首节点 - /// - /// - /// - public LoopListNode AddFirst(T value) - { - var node = new LoopListNode(value, this); - - if (Count == 0) - { - First = node; - First.Previous = First.Next = node; - } - else - { - LoopListNode last = Last!; - First!.Previous = last.Next = node; - node.Next = First; - node.Previous = last; - First = node; - } - Count++; - return First; - } - - /// - /// 在尾节点之后插入节点,并设置新节点为尾节点 - /// - /// - /// - public LoopListNode Add(T value) - { - var node = new LoopListNode(value, this); - - if (Count == 0) - { - First = node; - First.Previous = First.Next = node; - } - else - { - var last = First!.Previous!; - First.Previous = last.Next = node; - node.Next = First; - node.Previous = last; - } - Count++; - return Last!; - } - - /// - /// 在尾节点之后插入节点,并设置新节点为尾节点_此函数仅为与LinkedList同名方法 - /// - /// - /// - public LoopListNode AddLast(T value) - { - return Add(value); - } - - /// - /// 容器内容全部加入到末尾 - /// - /// - public void AddRange(IEnumerable list) - { - var ge = list.GetEnumerator(); - while (ge.MoveNext()) - Add(ge.Current); - } - - /// - /// 前面增加节点 - /// - /// - /// - /// - public LoopListNode AddBefore(LoopListNode node, T value) - { - if (node == First) - return AddFirst(value); - - var tnode = new LoopListNode(value, this); - node.Previous!.Next = tnode; - tnode.Previous = node.Previous; - node.Previous = tnode; - tnode.Next = node; - Count++; - return tnode; - } - - /// - /// 后面增加节点 - /// - /// - /// - /// - public LoopListNode AddAfter(LoopListNode node, T value) - { - var tnode = new LoopListNode(value, this); - node.Next!.Previous = tnode; - tnode.Next = node.Next; - node.Next = tnode; - tnode.Previous = node; - Count++; - return tnode; - } - - #endregion - - #region Remove - - /// - /// 删除首节点 - /// - /// - public bool RemoveFirst() - { - switch (Count) - { - case 0: - return false; - - case 1: - First = null; - break; - - default: - LoopListNode last = Last!; - First = First!.Next; - First!.Previous = last; - last.Next = First; - break; - } - Count--; - return true; - } - - /// - /// 删除尾节点 - /// - /// - public bool RemoveLast() - { - switch (Count) - { - case 0: - return false; - - case 1: - First = null; - break; - - default: - LoopListNode last = Last!.Previous!; - last.Next = First; - First!.Previous = last; - break; - } - Count--; - return true; - } - - /// - /// 删除此参数节点(唯一) - /// - /// 指定节点 - /// - public bool Remove(LoopListNode node) - { - if (!Contains(node)) - return false; - InternalRemove(node); - return true; - } - - /// - /// 删除含有此参数节点(所有) - /// - /// 将移除所有含有此值 - /// - public bool Remove(T value) - { - var lst = Finds(value); - if (lst is null) - return false; - - var ge = lst!.GetEnumerator(); - while (ge.MoveNext()) - InternalRemove(ge.Current); - return true; - } - - /// - /// 删除节点_内部调用 - /// - /// 此值肯定存在当前链表 - /// - void InternalRemove(LoopListNode node) - { - if (Count == 1 || node == First) - { - RemoveFirst();// 此处会减数字 - } - else - { - node.Next!.Previous = node.Previous; - node.Previous!.Next = node.Next; - Count--; - } - node.Invalidate(); - } - - #endregion - - #region LinkTo - - /// - /// 链接两节点,并去除这两个节点间的所有节点 - /// - /// - /// - public void LinkTo(LoopListNode from, LoopListNode to) - { - if (from != to && Contains(from) && Contains(to)) - { - LoopListNode node = from.Next!; - bool isFirstChanged = false; - int number = 0; - - while (node != to) - { - if (node == First) - isFirstChanged = true; - - node = node.Next!; - number++; - } - - from.Next = to; - to.Previous = from; - - if (number > 0 && isFirstChanged) - First = to; - - Count -= number; - } - } - - /// - /// 链接两节点,并去除这两个节点间的所有节点 - /// - /// - /// - /// - public void LinkTo(LoopListNode from, LoopListNode to, int number) - { - if (from != to && Contains(from) && Contains(to)) - { - from.Next = to; - to.Previous = from; - First = to; - Count -= number; - } - } - - /// - /// 链接两节点,并去除这两个节点间的所有节点 - /// - /// - /// - /// - /// - public void LinkTo(LoopListNode from, LoopListNode to, int number, bool isFirstChanged) - { - if (from != to && Contains(from) && Contains(to)) - { - from.Next = to; - to.Previous = from; - if (isFirstChanged) - First = to; - Count -= number; - } - } - - #endregion - - #endregion - - #region IEnumerable - - /// - /// 获取节点的查询器 - /// - /// - /// - public IEnumerable> GetNodes(LoopListNode from) - { - var node = from; - for (int i = 0; i < Count; i++) - { - yield return node!; - node = node!.Next; - } - } - - - - /// - /// 获取节点的查询器 - /// - /// - public IEnumerable> GetNodes() - { - LoopListNode node = First!; - for (int i = 0; i < Count; i++) - { - yield return node!; - node = node.Next!; - } - } - - /// - /// 获取节点值的查询器 - /// - /// - public IEnumerator GetEnumerator() - { - LoopListNode node = First!; - for (int i = 0; i < Count; i++) - { - yield return node!.Value; - node = node.Next!; - } - } - - IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); - - #region IEnumerable 成员 - - IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); - - #endregion IEnumerable 成员 - - #endregion - - #region IFormattable - /// - /// 转换为字符串_格式化实现 - /// - /// - /// - /// - string IFormattable.ToString(string? format, IFormatProvider? formatProvider) - { - return ToString(format, formatProvider); - } - - /// - /// 转换为字符串_无参调用 - /// - /// - public override string ToString() - { - return ToString(null, null); - } - - /// - /// 转换为字符串_有参调用 - /// - /// - public string ToString(string? format, IFormatProvider? formatProvider = null) - { - var s = new StringBuilder(); - s.Append($"Count = {Count};"); - if (format is null) - { - s.Append("{ "); - foreach (T value in this) - s.Append($"{value} "); - s.Append(" }"); - } - return s.ToString(); - } - #endregion - - #region ICloneable - /* 山人说无法分辨ICloneable接口是深浅克隆, - * 因此不要在泛型模板实现克隆函数, - * 让用户自己来 new(xx)实现浅克隆,所以也不提供Clone()了 - * - * 因此约定了:CopyTo(T,index)是深克隆;MemberwiseClone()是浅克隆; - * public object Clone() - * { - * var lst = new LoopList>(); - * ForEach(node => { - * lst.Add(node); - * return false; - * }); - * return lst; - * } - */ - #endregion -} - -#line default \ No newline at end of file diff --git a/src/IFoxCAD.Basal/LoopState.cs b/src/IFoxCAD.Basal/LoopState.cs deleted file mode 100644 index 44b25a6..0000000 --- a/src/IFoxCAD.Basal/LoopState.cs +++ /dev/null @@ -1,24 +0,0 @@ -namespace IFoxCAD.Basal; - -/// -/// 控制循环结束 -/// -public class LoopState -{ - const int PLS_NONE = 0; - const int PLS_EXCEPTIONAL = 1; - const int PLS_BROKEN = 2; - const int PLS_STOPPED = 4; - const int PLS_CANCELED = 8; - - private volatile int _LoopStateFlags = PLS_NONE; - - public bool IsRun => _LoopStateFlags == PLS_NONE; - public bool IsCancel => _LoopStateFlags == PLS_CANCELED; - public bool IsExceptional => _LoopStateFlags == PLS_EXCEPTIONAL; - - public bool IsBreak => (_LoopStateFlags & PLS_BROKEN) == PLS_BROKEN; - public bool IsStop => (_LoopStateFlags & PLS_STOPPED) == PLS_STOPPED; - public void Stop() => _LoopStateFlags = PLS_STOPPED; - public void Break() => _LoopStateFlags = PLS_BROKEN; -} \ No newline at end of file diff --git a/src/IFoxCAD.Basal/RandomEx.cs b/src/IFoxCAD.Basal/RandomEx.cs deleted file mode 100644 index a9682f7..0000000 --- a/src/IFoxCAD.Basal/RandomEx.cs +++ /dev/null @@ -1,202 +0,0 @@ -/* -*┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━模块信息━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┓ -*┃ 作 者:YxrWendao -*┃ 创建时间:2022/8/30 22:49:30 -*┃ 模块描述:随机数生成器 -*┃ 使用范围:通用 -*┃ 说 明:本模块中除GetRandom与NextColor方法是IFoxCAD原有的以外,其他方法均通过网络收集整理而来。 -*┃ 代码版本:1.0 -*┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛ -*/ - -namespace IFoxCAD.Basal; - -/// -/// 随机值扩展类 -/// -public static class RandomEx -{ - /// - /// 生成一个指定范围的浮点数值
    - /// 相关链接 - ///
    - /// 一个随机值产生器 - /// 范围最小浮点数值 - /// 范围最大浮点数值 - /// - public static double NextDouble(Random ran, double minValue, double maxValue) - { - return ran.NextDouble() * (maxValue - minValue) + minValue; - } - /// - /// 生成一个指定范围的浮点数值 - /// - /// 范围最小浮点数值 - /// 范围最大浮点数值 - /// - public static double NextDouble(double minValue, double maxValue) - { - return NextDouble(GetRandom(), minValue, maxValue); - } - /// - /// 生成一个布尔随机数 - /// - /// - public static bool NextBool() - { - return NextBool(GetRandom()); - } - /// - /// 生成一个布尔随机数
    - ///
    - /// - public static bool NextBool(Random ran) - { - bool[] arr = { true, false }; - return arr[ran.Next(2)]; - } - /// - /// 生成一个不连续或指定值的随机值 - /// - /// 一个字符串数组 - /// - public static string NextString(string[] arr) - { - return NextString(GetRandom(), arr); - } - /// - /// 生成一个不连续或指定值的随机值 - /// - /// 一个随机值产生器 - /// 一个字符串数组 - /// - public static string NextString(Random ran, string[] arr) - { - ran ??= GetRandom(); - int n = ran.Next(arr.Length - 1); - return arr[n]; - } - /// - /// 生成一个不连续或指定值的随机值 - /// - /// 一个双精度值数组 - /// - public static double NextDouble(double[] arr) - { - return NextDouble(GetRandom(), arr); - } - /// - /// 生成不连续或指定值的随机值 - /// - /// 一个随机值产生器 - /// 一个双精度值数组 - /// - public static double NextDouble(Random ran, double[] arr) - { - ran ??= GetRandom(); - int n = ran.Next(arr.Length - 1); - return arr[n]; - } - /// - /// 生成指定范围内的整数 - /// - /// 范围最大整数值 - /// - public static int NextInt(int max) - { - return NextInt(GetRandom(), max); - } - /// - /// 生成指定范围内的整数 - /// - /// 一个随机值产生器 - /// 范围最大整数值 - /// - public static int NextInt(Random ran, int max) - { - ran ??= GetRandom(); - return ran.Next(max); - } - /// - /// 生成指定范围内的整数 - /// - /// 范围的最小整数 - /// 范围的最大整数 - /// 返回一个介于之间的整数 - public static int NextInt(int min, int max) - { - return NextInt(GetRandom(), min, max); - } - /// - /// 生成指定范围内的整数 - /// - /// 一个随机值产生器 - /// 范围的最小整数 - /// 范围的最大整数 - /// 返回一个介于之间的整数 - public static int NextInt(Random ran, int min, int max) - { - ran ??= GetRandom(); - return ran.Next(min, max); - } - - /// - /// 生成一个随机颜色 - /// - /// 返回 - public static System.Drawing.Color NextColor() - { - return NextColor(GetRandom()); - } - /// - /// 生成一个随机颜色 - /// - /// - public static System.Drawing.Color NextColor(Random ran) - { - ran ??= GetRandom(); - int R = ran.Next(255); - int G = ran.Next(255); - int B = ran.Next(255); - B = (R + G > 400) ? R + G - 400 : B;//0 : 380 - R - G; - B = (B > 255) ? 255 : B; - return System.Drawing.Color.FromArgb(R, G, B); - } - - - /* - * 知识准备: - * | 高位64位 | 低位32位 | - * Convert.ToString(int.MaxValue, 2)输出二进制 "1111111111111111111111111111111" 31个;最高位是符号位,所以少1位 - * Convert.ToString(long.MaxValue,2)输出二进制,刚好长一倍 "11111111111111111111111111111111 1111111111111111111111111111111" 63个;最高位是符号位,所以少1位 - * Convert.ToString(0xffffffffL, 2)int.MaxValue再按位多1 "1 1111111111111111111111111111111" 32个;前面的0不会打印出来 - * - * Convert.ToString(long.MaxValue>>32, 2)相当于平移高位的到低位范围,也就是上面少打印的二进制 - * 验证右移是不是高位保留,答案是 - * var a = Convert.ToInt64("101111111111111111111111111111111111111111111111111111111111111", 2); - * Convert.ToString(a >> 32,2); - * - * 解释代码: - * 0x01: - * (int)(long.MaxValue & 0xffffffffL) | (int)(long.MaxValue >> 32); - * Convert.ToString(long.MaxValue & 0xffffffffL, 2)//去掉高位:"11111111111111111111111111111111" 32个,再强转int - * 按位与&是保证符号位肯定是1,其他尽可能为0,高位被去掉只是MaxValue&0的原因,强转才是去掉高位..."尽可能"一词带来第一次随机性 - * 0x02: - * Convert.ToString((long.MaxValue >> 32), 2) //去掉低位: "1111111111111111111111111111111" 31个,再强转int - * 按位或|是尽可能为1..."尽可能"一词带来第二次随机性 - * - */ - - /// - /// 带有随机种子的随机数
    - /// 为什么这样写随机种子呢 - ///
    - /// - public static Random GetRandom() - { - var tick = DateTime.Now.Ticks; - var tickSeeds = (int)(tick & 0xffffffffL) | (int)(tick >> 32); - return new Random(tickSeeds); - } - -} diff --git a/src/IFoxCAD.Basal/Sortedset/ISet.cs b/src/IFoxCAD.Basal/Sortedset/ISet.cs deleted file mode 100644 index 22b9509..0000000 --- a/src/IFoxCAD.Basal/Sortedset/ISet.cs +++ /dev/null @@ -1,65 +0,0 @@ -#if NET35 -// ==++== -// -// Copyright (c) Microsoft Corporation. All rights reserved. -// -// ==--== -/*============================================================ -** -** Interface: ISet -** -** kimhamil -** -** -** Purpose: Base interface for all generic sets. -** -** -===========================================================*/ -namespace System.Collections.Generic -{ - using System; - using System.Runtime.CompilerServices; - - - /// - /// Generic collection that guarantees the uniqueness of its elements, as defined - /// by some comparer. It also supports basic set operations such as Union, Intersection, - /// Complement and Exclusive Complement. - /// - public interface ISet : ICollection - { - // Add ITEM to the set, return true if added, false if duplicate - new bool Add(T item); - - // Transform this set into its union with the IEnumerable other - void UnionWith(IEnumerable other); - - // Transform this set into its intersection with the IEnumberable other - void IntersectWith(IEnumerable other); - - // Transform this set so it contains no elements that are also in other - void ExceptWith(IEnumerable other); - - // Transform this set so it contains elements initially in this or in other, but not both - void SymmetricExceptWith(IEnumerable other); - - // Check if this set is a subset of other - bool IsSubsetOf(IEnumerable other); - - // Check if this set is a superset of other - bool IsSupersetOf(IEnumerable other); - - // Check if this set is a subset of other, but not the same as it - bool IsProperSupersetOf(IEnumerable other); - - // Check if this set is a superset of other, but not the same as it - bool IsProperSubsetOf(IEnumerable other); - - // Check if this set has any elements in common with other - bool Overlaps(IEnumerable other); - - // Check if this set contains the same and only the same elements as other - bool SetEquals(IEnumerable other); - } -} -#endif \ No newline at end of file diff --git a/src/IFoxCAD.Basal/Sortedset/SR.cs b/src/IFoxCAD.Basal/Sortedset/SR.cs deleted file mode 100644 index 59e10a7..0000000 --- a/src/IFoxCAD.Basal/Sortedset/SR.cs +++ /dev/null @@ -1,907 +0,0 @@ -#if NET35 -#pragma warning disable CS8603 // 可能返回 null 引用。 -namespace System; - - -using System; -using System.Reflection; -using System.Globalization; -using System.Resources; -using System.Text; -using System.ComponentModel; -using System.Collections.Generic; -using System.Diagnostics.CodeAnalysis; - -[AttributeUsage(AttributeTargets.All)] -internal sealed class SRDescriptionAttribute : DescriptionAttribute -{ - public SRDescriptionAttribute(string description) - { - DescriptionValue = SR.GetString(description); - } - - public SRDescriptionAttribute(string description, string resourceSet) - { - ResourceManager rm = new ResourceManager(resourceSet, Assembly.GetExecutingAssembly()); - DescriptionValue = rm.GetString(description); - System.Diagnostics.Debug.Assert(DescriptionValue != null, string.Format(CultureInfo.CurrentCulture, "String resource {0} not found.", new object[] { description })); - } -} - -[AttributeUsage(AttributeTargets.All)] -internal sealed class SRCategoryAttribute : CategoryAttribute -{ - private string resourceSet = String.Empty; - - public SRCategoryAttribute(string category) - : base(category) - { - } - - public SRCategoryAttribute(string category, string resourceSet) - : base(category) - { - this.resourceSet = resourceSet; - } - - protected override string GetLocalizedString(string value) - { - if (this.resourceSet.Length > 0) - { - ResourceManager rm = new ResourceManager(resourceSet, Assembly.GetExecutingAssembly()); - String localizedString = rm.GetString(value); - System.Diagnostics.Debug.Assert(localizedString != null, string.Format(CultureInfo.CurrentCulture, "String resource {0} not found.", new object[] { value })); - return localizedString; - } - else - { - return SR.GetString(value); - } - } -} - -[AttributeUsage(AttributeTargets.All)] -internal sealed class SRDisplayNameAttribute : DisplayNameAttribute -{ - public SRDisplayNameAttribute(string name) - { - DisplayNameValue = SR.GetString(name); - } - - public SRDisplayNameAttribute(string name, string resourceSet) - { - ResourceManager rm = new ResourceManager(resourceSet, Assembly.GetExecutingAssembly()); - DisplayNameValue = rm.GetString(name); - System.Diagnostics.Debug.Assert(DisplayNameValue != null, string.Format(CultureInfo.CurrentCulture, "String resource {0} not found.", new object[] { name })); - } -} - -/// -/// AutoGenerated resource class. Usage: -/// -/// string s = SR.GetString(SR.MyIdenfitier); -/// -internal sealed partial class SR -{ -#pragma warning disable CS8625 // 无法将 null 字面量转换为非 null 的引用类型。 - static SR loader = null; -#pragma warning restore CS8625 // 无法将 null 字面量转换为非 null 的引用类型。 - ResourceManager resources; - - internal SR() - { - resources = new System.Resources.ResourceManager("System.Workflow.ComponentModel.StringResources", Assembly.GetExecutingAssembly()); - } - - private static SR GetLoader() - { - if (loader == null) - loader = new SR(); - return loader; - } - - private static CultureInfo Culture - { - get { return null/*use ResourceManager default, CultureInfo.CurrentUICulture*/; } - } - - [SuppressMessage("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")] - internal static string GetString(string name, params object[] args) - { - return GetString(SR.Culture, name, args); - } - - [SuppressMessage("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")] - internal static string GetString(CultureInfo culture, string name, params object[] args) - { - SR sys = GetLoader(); - if (sys == null) - return null; - string res = sys.resources.GetString(name, culture); - System.Diagnostics.Debug.Assert(res != null, string.Format(CultureInfo.CurrentCulture, "String resource {0} not found.", new object[] { name })); - if (args != null && args.Length > 0) - { - return string.Format(CultureInfo.CurrentCulture, res, args); - } - else - { - return res; - } - } - - internal static string GetString(string name) - { - return GetString(SR.Culture, name); - } - - internal static string GetString(CultureInfo culture, string name) - { - SR sys = GetLoader(); - if (sys == null) - return null; - string res = sys.resources.GetString(name, culture); - System.Diagnostics.Debug.Assert(res != null, string.Format(CultureInfo.CurrentCulture, "String resource {0} not found.", new object[] { name })); - return res; - } - - // All these strings should be present in StringResources.resx - internal const string Activity = "Activity"; - internal const string Handlers = "Handlers"; - internal const string Conditions = "Conditions"; - internal const string ConditionedActivityConditions = "ConditionedActivityConditions"; - internal const string Correlations = "Correlations"; - internal const string CorrelationSet = "CorrelationSet"; - internal const string NameDescr = "NameDescr"; - internal const string EnabledDescr = "EnabledDescr"; - internal const string DescriptionDescr = "DescriptionDescr"; - internal const string UnlessConditionDescr = "UnlessConditionDescr"; - internal const string InitializeDescr = "InitializeDescr"; - internal const string CatchTypeDescr = "CatchTypeDescr"; - internal const string ExceptionTypeDescr = "ExceptionTypeDescr"; - internal const string FaultDescription = "FaultDescription"; - internal const string FaultTypeDescription = "FaultTypeDescription"; - internal const string ContainingAssemblyDescr = "ContainingAssemblyDescr"; - internal const string ExecutionModeDescr = "ExecutionModeDescr"; - internal const string Error_ReadOnlyTemplateActivity = "Error_ReadOnlyTemplateActivity"; - internal const string Error_TypeNotString = "Error_TypeNotString"; - internal const string Error_InvalidErrorType = "Error_InvalidErrorType"; - internal const string Error_LiteralConversionFailed = "Error_LiteralConversionFailed"; - internal const string Error_TypeNotPrimitive = "Error_TypeNotPrimitive"; - internal const string CompletedCaleeDescr = "CompletedCaleeDescr"; - internal const string ProxyClassDescr = "ProxyClassDescr"; - internal const string ActivitySetDescr = "ActivitySetDescr"; - internal const string VersionDescr = "VersionDescr"; - internal const string ActivationDescr = "ActivationDescr"; - internal const string CorrelationSetsDescr = "CorrelationSetsDescr"; - internal const string CompanionClassDescr = "CompanionClassDescr"; - internal const string TransactionTypeDescr = "TransactionTypeDescr"; - internal const string SynchronizedDescr = "SynchronizedDescr"; - internal const string IsolationLevelDescr = "IsolationLevelDescr"; - internal const string TimeoutDescr = "TimeoutDescr"; - internal const string BatchableDescr = "BatchableDescr"; - internal const string LRTTimeoutDescr = "LRTTimeoutDescr"; - internal const string OnGetCalleeCountDescr = "OnGetCalleeCountDescr"; - internal const string CompensatableActivityDescr = "CompensatableActivityDescr"; - internal const string OnAfterEventDescr = "OnAfterEventDescr"; - internal const string OnBeforeMethodInvokeDescr = "OnBeforeMethodInvokeDescr"; - internal const string AssignedToDescr = "AssignedToDescr"; - internal const string TypeDescr = "TypeDescr"; - internal const string TemplateActivityDescr = "TemplateActivityDescr"; - internal const string ErrorMessageDescr = "ErrorMessageDescr"; - internal const string WebServiceSynchronizedDescr = "WebServiceSynchronizedDescr"; - internal const string CorrelationSetDescr = "CorrelationSetDescr"; - internal const string ExecutionTypeDescr = "ExecutionTypeDescr"; - internal const string RoleDescr = "RoleDescr"; - internal const string OnInitializeClonesDescr = "OnInitializeClonesDescr"; - internal const string CorrelationSetDisplayName = "CorrelationSetDisplayName"; - internal const string PastingActivities = "PastingActivities"; - internal const string DeletingActivities = "DeletingActivities"; - internal const string DragDropActivities = "DragDropActivities"; - internal const string ChangingEnabled = "ChangingEnabled"; - internal const string ChangingHandler = "ChangingHandler"; - internal const string ChangingParameter = "ChangingParameter"; - internal const string CollectionItem = "CollectionItem"; - internal const string AddingConditionalBranch = "AddingConditionalBranch"; - internal const string AddingEventActivity = "AddingEventActivity"; - internal const string AddingListenBranch = "AddingListenBranch"; - internal const string AddingParallelBranch = "AddingParallelBranch"; - internal const string CurrentProject = "CurrentProject"; - internal const string ReferencedAssemblies = "ReferencedAssemblies"; - internal const string CollectionText = "CollectionText"; - internal const string ParameterDescription = "ParameterDescription"; - internal const string InvokeParameterDescription = "InvokeParameterDescription"; - internal const string ParametersDescription = "ParametersDescription"; - internal const string ChangingParameters = "ChangingParameters"; - internal const string Condition = "ConditionRule"; - internal const string MovingActivities = "MovingActivities"; - internal const string MemberNameDescr = "MemberNameDescr"; - internal const string OnScopeInitializedDescr = "OnScopeInitializedDescr"; - internal const string OnGeneratorInitializedDescr = "OnGeneratorInitializedDescr"; - internal const string OnScopeCompletedDescr = "OnScopeCompletedDescr"; - internal const string OnGeneratorCompletedDescr = "OnGeneratorCompletedDescr"; - internal const string DataElementRuntimeTypeDescr = "DataElementRuntimeTypeDescr"; - internal const string RuleConditionReferencesDescr = "RuleConditionReferencesDescr"; - internal const string CreateActivityFromToolbox = "CreateActivityFromToolbox"; - internal const string MoveMultipleActivities = "MoveMultipleActivities"; - internal const string MoveSingleActivity = "MoveSingleActivity"; - internal const string CutMultipleActivities = "CutMultipleActivities"; - internal const string CutSingleActivity = "CutSingleActivity"; - internal const string CutActivity = "CutActivity"; - internal const string FaultActivityDescription = "FaultActivityDescription"; - internal const string NullConditionExpression = "NullConditionExpression"; - internal const string ParameterTypeDescription = "ParameterTypeDescription"; - internal const string ParameterCategory = "ParameterCategory"; - internal const string ParameterDirectionDescription = "ParameterDirectionDescription"; - internal const string ParameterElementDescription = "ParameterElementDescription"; - internal const string ParameterDlgDescription = "ParameterDlgDescription"; - internal const string ParameterDlgHeader = "ParameterDlgHeader"; - internal const string SuspendActivityDescription = "SuspendActivityDescription"; - internal const string SuspendErrorMessageDescr = "SuspendErrorMessageDescr"; - internal const string TerminateActivityDescription = "TerminateActivityDescription"; - internal const string TerminateErrorMessageDescr = "TerminateErrorMessageDescr"; - internal const string DeclarationCategory = "DeclarationCategory"; - internal const string NoValidActivityPropertiesAvailable = "NoValidActivityPropertiesAvailable"; - internal const string ChooseActivityDatasource = "ChooseActivityDatasource"; - internal const string Promote = "Promote"; - internal const string Type = "Type"; - internal const string NoMatchingActivityProperties = "NoMatchingActivityProperties"; - internal const string ActivityBindIDDescription = "ActivityBindIDDescription"; - internal const string ActivityBindPathDescription = "ActivityBindPathDescription"; - internal const string XPathDescription = "XPathDescription"; - internal const string TransformerDescription = "TransformerDescription"; - internal const string CustomPropertiesCollectionFormHeader = "CustomPropertiesCollectionFormHeader"; - internal const string CustomPropertiesCollectionFormDescription = "CustomPropertiesCollectionFormDescription"; - internal const string BaseTypePropertyName = "BaseTypePropertyName"; - internal const string CustomActivityBaseClassTypeFilterProviderDesc = "CustomActivityBaseClassTypeFilterProviderDesc"; - internal const string CustomActivityDesignerTypeFilterProviderDesc = "CustomActivityDesignerTypeFilterProviderDesc"; - internal const string CustomActivityValidatorTypeFilterProviderDesc = "CustomActivityValidatorTypeFilterProviderDesc"; - internal const string CustomActivityExecutorTypeFilterProviderDesc = "CustomActivityExecutorTypeFilterProviderDesc"; - internal const string GenericParameters = "GenericParameters"; - internal const string ToolboxItem = "ToolboxItem"; - internal const string ToolboxItemCompanionClassDesc = "ToolboxItemCompanionClassDesc"; - internal const string Error_SerializationInsufficientState = "Error_SerializationInsufficientState"; - internal const string Error_ActivityHasParent = "Error_ActivityHasParent"; - internal const string Error_CompensantionParentNotScope = "Error_CompensantionParentNotScope"; - internal const string Error_ConditionedActivityParentNotCAG = "Error_ConditionedActivityParentNotCAG"; - internal const string Error_CorrelationTypeNotComparable = "Error_CorrelationTypeNotComparable"; - internal const string Error_ArgumentTypeNotMatchParameter = "Error_ArgumentTypeNotMatchParameter"; - internal const string Error_TypeTypeMismatch = "Error_TypeTypeMismatch"; - internal const string Error_ParameterTypeMismatch = "Error_ParameterTypeMismatch"; - internal const string Error_InvokeParameterTypeMismatch = "Error_InvokeParameterTypeMismatch"; - internal const string Error_ParameterPropertyNotSet = "Error_ParameterPropertyNotSet"; - internal const string Error_DataSourceNameNotSet = "Error_DataSourceNameNotSet"; - internal const string Error_DataSourceInvalidIdentifier = "Error_DataSourceInvalidIdentifier"; - internal const string Error_ParameterTypeNotExist = "Error_ParameterTypeNotExist"; - internal const string Error_InvalidParameterName = "Error_InvalidParameterName"; - internal const string Error_InvalidParameterType = "Error_InvalidParameterType"; - internal const string Error_InvalidParameterElement = "Error_InvalidParameterElement"; - internal const string Error_InvalidPropertyType = "Error_InvalidPropertyType"; - internal const string Error_TypeNotResolvedInMethodName = "Error_TypeNotResolvedInMethodName"; - internal const string Error_DelegateNoInvoke = "Error_DelegateNoInvoke"; - internal const string Error_TypeNotDelegate = "Error_TypeNotDelegate"; - internal const string Error_MethodSignatureMismatch = "Error_MethodSignatureMismatch"; - internal const string Error_MethodReturnTypeMismatch = "Error_MethodReturnTypeMismatch"; - internal const string Error_PropertyNotSet = "Error_PropertyNotSet"; - internal const string Error_ScopeCouldNotBeResolved = "Error_ScopeCouldNotBeResolved"; - internal const string Error_IfElseNotAllIfElseBranchDecl = "Error_ConditionalNotAllConditionalBranchDecl"; - internal const string Error_TypeTypeMismatchAmbiguity = "Error_TypeTypeMismatchAmbiguity"; - internal const string Error_InvalidCorrelationSetDatasource = "Error_InvalidCorrelationSetDatasource"; - internal const string Error_InvalidCorrelationSetType = "Error_InvalidCorrelationSetType"; - internal const string Error_MissingCorrelationParameterAttribute = "Error_MissingCorrelationParameterAttribute"; - internal const string Error_CorrelationTypeNotConsistent = "Error_CorrelationTypeNotConsistent"; - internal const string Error_CorrelationInvalid = "Error_CorrelationInvalid"; - internal const string Error_MissingDelegateMethod = "Error_MissingDelegateMethod"; - internal const string Error_MissingHostInterface = "Error_MissingHostInterface"; - internal const string Error_MissingMethodName = "Error_MissingMethodName"; - internal const string Error_NoBoundType = "Error_NoBoundType"; - internal const string Error_PortTypeNotAnInterface = "Error_PortTypeNotAnInterface"; - internal const string Error_MethodNotExists = "Error_MethodNotExists"; - internal const string Error_InvalidRequestResponseMethod = "Error_InvalidRequestResponseMethod"; - internal const string General_MissingService = "General_MissingService"; - internal const string Error_ScopeDuplicatedNameActivity = "Error_ScopeDuplicatedNameActivity"; - internal const string Error_DuplicatedActivityID = "Error_DuplicatedActivityID"; - internal const string Error_DuplicatedParameterName = "Error_DuplicatedParameterName"; - internal const string Error_ScopeMissingSerializableAttribute = "Error_ScopeMissingSerializableAttribute"; - internal const string Error_FieldNotExists = "Error_FieldNotExists"; - internal const string Error_PropertyNotExists = "Error_PropertyNotExists"; - internal const string Error_FieldTypeMismatch = "Error_FieldTypeMismatch"; - internal const string Error_PropertyTypeMismatch = "Error_PropertyTypeMismatch"; - internal const string Error_TypeNotResolvedInFieldName = "Error_TypeNotResolvedInFieldName"; - internal const string Error_TypeNotResolvedInPropertyName = "Error_TypeNotResolvedInPropertyName"; - internal const string Error_FieldGenericParamTypeMismatch = "Error_FieldGenericParamTypeMismatch"; - internal const string Error_TypeNotResolved = "Error_TypeNotResolved"; - internal const string Error_TypeIsUnboundedGeneric = "Error_TypeIsUnboundedGeneric"; - internal const string Error_MissingRootActivity = "Error_MissingRootActivity"; - internal const string Error_PropertyNotReadable = "Error_PropertyNotReadable"; - internal const string Error_PropertyNotWritable = "Error_PropertyNotWritable"; - internal const string Error_NotCompositeActivity = "Error_NotCompositeActivity"; - internal const string Error_TypeNotExist = "Error_TypeNotExist"; - internal const string Error_ActivityRefNotResolved = "Error_ActivityRefNotResolved"; - internal const string Error_ActivityRefNotMatchType = "Error_ActivityRefNotMatchType"; - internal const string Error_ActivityValidation = "Error_ActivityValidation"; - internal const string Error_ActiveChildExist = "Error_ActiveChildExist"; - internal const string Error_ActiveChildContextExist = "Error_ActiveChildContextExist"; - internal const string Error_CannotCompleteContext = "Error_CannotCompleteContext"; - internal const string Error_NoPasteSupport = "Error_NoPasteSupport"; - internal const string Error_UnknownSerializationStore = "Error_UnknownSerializationStore"; - internal const string Error_MissingCorrelationSet = "Error_MissingCorrelationSet"; - internal const string Error_CreateVariable = "Error_CreateVariable"; - internal const string Error_DuplicateCorrelationSetName = "Error_DuplicateCorrelationSetName"; - internal const string Error_DragDropInvalid = "Error_DragDropInvalid"; - internal const string AddingImplicitActivity = "AddingImplicitActivity"; - internal const string Failure_DoDefaultAction = "Failure_DoDefaultAction"; - internal const string Failure_DoDefaultActionCaption = "Failure_DoDefaultActionCaption"; - internal const string Error_FaultInsideAtomicScope = "Error_FaultInsideAtomicScope"; - internal const string Error_ListenNotMoreThanOneDelay = "Error_ListenNotMoreThanOneDelay"; - internal const string Error_AtomicScopeWithFaultHandlersActivityDecl = "Error_AtomicScopeWithFaultHandlersActivityDecl"; - internal const string Error_AtomicScopeWithCancellationHandlerActivity = "Error_AtomicScopeWithCancellationHandlerActivity"; - internal const string Error_ScopeDuplicateFaultHandlerActivityForAll = "Error_ScopeDuplicateFaultHandlerActivityForAll"; - internal const string Error_ScopeDuplicateFaultHandlerActivityFor = "Error_ScopeDuplicateFaultHandlerActivityFor"; - internal const string Error_AtomicScopeNestedInNonLRT = "Error_AtomicScopeNestedInNonLRT"; - internal const string Error_LRTScopeNestedInNonLRT = "Error_LRTScopeNestedInNonLRT"; - internal const string Error_CAGNotAllChildrenConditioned = "Error_CAGNotAllChildrenConditioned"; - internal const string Error_ConditionedActivityChildCount = "Error_ConditionedActivityChildCount"; - internal const string Error_NegativeValue = "Error_NegativeValue"; - internal const string Error_MethodWithReturnType = "Error_MethodWithReturnType"; - internal const string Error_SendReceiveOrderIncorrect = "Error_SendReceiveOrderIncorrect"; - internal const string Error_ReceiveSendOrderIncorrect = "Error_ReceiveSendOrderIncorrect"; - internal const string Error_CompensateBadNesting = "Error_CompensateBadNesting"; - internal const string Error_ReferencedAssemblyIsInvalid = "Error_ReferencedAssemblyIsInvalid"; - internal const string Error_TypeToXsdConversion = "Error_TypeToXsdConversion"; - internal const string Error_FieldTypeNotResolved = "Error_FieldTypeNotResolved"; - internal const string Error_PropertyTypeNotResolved = "Error_PropertyTypeNotResolved"; - internal const string Error_CouldNotDeserializeXomlFile = "Error_CouldNotDeserializeXomlFile"; - internal const string Error_InternalCompilerError = "Error_InternalCompilerError"; - internal const string Error_TypeNotAsseblyQualified = "Error_TypeNotAsseblyQualified"; - internal const string CompilerWarning_StandardAssemlbyInReferences = "CompilerWarning_StandardAssemlbyInReferences"; - internal const string Error_SuspendInAtomicScope = "Error_SuspendInAtomicScope"; - internal const string Error_InvalidActivityExecutionContext = "Error_InvalidActivityExecutionContext"; - internal const string Error_NoRuntimeAvailable = "Error_NoRuntimeAvailable"; - internal const string Error_CanNotChangeAtRuntime = "Error_CanNotChangeAtRuntime"; - internal const string Error_DataContextNotInitialized = "Error_DataContextNotInitialized"; - internal const string Error_DataContextAlreadyInitialized = "Error_DataContextAlreadyInitialized"; - internal const string Error_ParseActivityNameDoesNotExist = "Error_ParseActivityNameDoesNotExist"; - internal const string Error_NoParameterPropertyDeclared = "Error_NoParameterPropertyDeclared"; - internal const string Error_PropertyInvalidIdentifier = "Error_PropertyInvalidIdentifier"; - internal const string Error_WorkflowDefinitionModified = "Error_WorkflowDefinitionModified"; - internal const string Error_FieldAlreadyExist = "Error_FieldAlreadyExist"; - internal const string Failure_FieldAlreadyExist = "Failure_FieldAlreadyExist"; - internal const string Error_DifferentTypeFieldExists = "Error_DifferentTypeFieldExists"; - internal const string Error_RootActivityTypeInvalid = "Error_RootActivityTypeInvalid"; - internal const string Error_RootActivityTypeInvalid2 = "Error_RootActivityTypeInvalid2"; - internal const string Error_CannotCompile_No_XClass = "Error_CannotCompile_No_XClass"; - internal const string Error_TemplateActivityIsNotActivity = "Error_TemplateActivityIsNotActivity"; - internal const string Error_TypeIsNotRootActivity = "Error_TypeIsNotRootActivity"; - internal const string Error_NoTypeProvider = "Error_NoTypeProvider"; - internal const string Error_NotCodeGeneratorType = "Error_NotCodeGeneratorType"; - internal const string Error_NotDataContext = "Error_NotDataContext"; - internal const string Error_MissingDefaultConstructor = "Error_MissingDefaultConstructor"; - internal const string Error_ContextStackItemMissing = "Error_ContextStackItemMissing"; - internal const string Error_UnexpectedArgumentType = "Error_UnexpectedArgumentType"; - internal const string Error_EmptyArgument = "Error_EmptyArgument"; - internal const string Error_DPAlreadyExist = "Error_DPAlreadyExist"; - internal const string Error_DuplicateDynamicProperty = "Error_DuplicateDynamicProperty"; - internal const string Error_DynamicPropertyTypeValueMismatch = "Error_DynamicPropertyTypeValueMismatch"; - internal const string Error_DynamicPropertyNoSupport = "Error_DynamicPropertyNoSupport"; - internal const string Error_NoContextForDatasource = "Error_NoContextForDatasource"; - internal const string Error_NoContextForDatasourceCaption = "Error_NoContextForDatasourceCaption"; - internal const string Error_DataSourceHasParent = "Error_DataSourceHasParent"; - internal const string OnTaskCompletedDescr = "OnTaskCompletedDescr"; - internal const string OnTaskInitializedDescr = "OnTaskInitializedDescr"; - internal const string Error_InvalidXmlData = "Error_InvalidXmlData"; - internal const string Error_HandlerNotOnRoot = "Error_HandlerNotOnRoot"; - internal const string Error_InvalidArgumentIndex = "Error_InvalidArgumentIndex"; - internal const string Error_UITypeEditorTypeNotUITypeEditor = "Error_UITypeEditorTypeNotUITypeEditor"; - internal const string FilterDescription_UITypeEditor = "FilterDescription_UITypeEditor"; - internal const string Error_UserCodeFilesNotAllowed = "Error_UserCodeFilesNotAllowed"; - internal const string Error_CodeWithinNotAllowed = "Error_CodeWithinNotAllowed"; - internal const string Error_TypeNotAuthorized = "Error_TypeNotAuthorized"; - internal const string Error_CantDetermineBaseType = "Error_CantDetermineBaseType"; - internal const string Error_MultipleSelectNotSupportedForBindAndPromote = "Error_MultipleSelectNotSupportedForBindAndPromote"; - internal const string Error_CantDetermineBaseTypeCaption = "Error_CantDetermineBaseTypeCaption"; - internal const string Error_CantDeterminePropertyBaseType = "Error_CantDeterminePropertyBaseType"; - internal const string Error_NullCustomActivityTypeName = "Error_NullCustomActivityTypeName"; - internal const string Error_InvalidAttribute = "Error_InvalidAttribute"; - internal const string Error_InvalidAttributes = "Error_InvalidAttributes"; - internal const string Error_ConfigFileMissingOrInvalid = "Error_ConfigFileMissingOrInvalid"; - internal const string Error_CantHaveContextActivity = "Error_CantHaveContextActivity"; - internal const string Error_SynchronizedNeedsDataContext = "Error_SynchronizedNeedsDataContext"; - internal const string Error_MoreThanOneFaultHandlersActivityDecl = "Error_MoreThanOneFaultHandlersActivityDecl"; - internal const string Error_MoreThanOneEventHandlersDecl = "Error_MoreThanOneEventHandlersDecl"; - internal const string Error_MoreThanOneCancelHandler = "Error_MoreThanOneCancelHandler"; - internal const string Error_MetaDataInterfaceMissing = "Error_MetaDataInterfaceMissing"; - internal const string Error_NonActivityExecutor = "Error_NonActivityExecutor"; - internal const string Error_DynamicUpdateEvaluation = "Error_DynamicUpdateEvaluation"; - internal const string Error_CollectionHasNullEntry = "Error_CollectionHasNullEntry"; - internal const string Error_MissingContextProperty = "Error_MissingContextProperty"; - internal const string Error_AssociatedDesignerMissing = "Error_AssociatedDesignerMissing"; - internal const string Error_MissingContextActivityProperty = "Error_MissingContextActivityProperty"; - internal const string Error_MissingActivityProperty = "Error_MissingActivityProperty"; - internal const string Error_MissingOwnerTypeProperty = "Error_MissingOwnerTypeProperty"; - internal const string Error_DOIsNotAnActivity = "Error_DOIsNotAnActivity"; - internal const string Error_PropertyCanBeOnlyCleared = "Error_PropertyCanBeOnlyCleared"; - internal const string Error_PropertyDefaultTypeMismatch = "Error_PropertyDefaultTypeMismatch"; - internal const string Error_PropertyDefaultIsReference = "Error_PropertyDefaultIsReference"; - // workflow load errors - internal const string Error_WorkflowLoadFailed = "Error_WorkflowLoadFailed"; - internal const string Error_WorkflowLoadValidationFailed = "Error_WorkflowLoadValidationFailed"; - internal const string Error_WorkflowLoadDeserializationFailed = "Error_WorkflowLoadDeserializationFailed"; - internal const string Error_WorkflowLoadTypeMismatch = "Error_WorkflowLoadTypeMismatch"; - internal const string Error_WorkflowLoadInvalidXoml = "Error_WorkflowLoadInvalidXoml"; - internal const string Error_WorkflowLoadNotValidRootType = "Error_WorkflowLoadNotValidRootType"; - internal const string Error_CantCreateInstanceOfComponent = "Error_CantCreateInstanceOfComponent"; - internal const string Error_NotComponentFactoryType = "Error_NotComponentFactoryType"; - internal const string Error_WorkflowTerminated = "Error_WorkflowTerminated"; - - // serializer errrors - internal const string Error_SerializerAttributesFoundInComplexProperty = "Error_SerializerAttributesFoundInComplexProperty"; - internal const string Error_InvalidDataFound = "Error_InvalidDataFound"; - internal const string Error_InvalidDataFoundForType = "Error_InvalidDataFoundForType"; - internal const string Error_InvalidDataFoundForType1 = "Error_InvalidDataFoundForType1"; - internal const string Error_SerializerTypeNotResolved = "Error_SerializerTypeNotResolved"; - internal const string Error_MarkupSerializerTypeNotResolved = "Error_MarkupSerializerTypeNotResolved"; - internal const string Error_SerializerTypeNotResolvedWithInnerError = "Error_SerializerTypeNotResolvedWithInnerError"; - internal const string Error_SerializerNotAvailable = "Error_SerializerNotAvailable"; - internal const string Error_SerializerNotAvailableForSerialize = "Error_SerializerNotAvailableForSerialize"; - internal const string Error_SerializerCreateInstanceFailed = "Error_SerializerCreateInstanceFailed"; - internal const string Error_SerializerAddChildFailed = "Error_SerializerAddChildFailed"; - internal const string Error_SerializerNoPropertyAvailable = "Error_SerializerNoPropertyAvailable"; - internal const string Error_SerializerPrimitivePropertyReadOnly = "Error_SerializerPrimitivePropertyReadOnly"; - internal const string Error_SerializerCantChangeIsLocked = "Error_SerializerCantChangeIsLocked"; - internal const string Error_SerializerPrimitivePropertySetFailed = "Error_SerializerPrimitivePropertySetFailed"; - internal const string Error_SerializerPropertyGetFailed = "Error_SerializerPropertyGetFailed"; - internal const string Error_SerializerPrimitivePropertyNoLogic = "Error_SerializerPrimitivePropertyNoLogic"; - internal const string Error_SerializerPrimitivePropertyParentIsNull = "Error_SerializerPrimitivePropertyParentIsNull"; - internal const string Error_SerializerComplexPropertySetFailed = "Error_SerializerComplexPropertySetFailed"; - internal const string Error_SerializerNoChildNotion = "Error_SerializerNoChildNotion"; - internal const string Error_SerializerNoDynamicPropertySupport = "Error_SerializerNoDynamicPropertySupport"; - internal const string Error_SerializerNoSerializeLogic = "Error_SerializerNoSerializeLogic"; - internal const string Error_SerializerReadOnlyPropertyAndValueIsNull = "Error_SerializerReadOnlyPropertyAndValueIsNull"; - internal const string Error_SerializerReadOnlyParametersNoChild = "Error_SerializerReadOnlyParametersNoChild"; - internal const string Error_SerializerNotParameterBindingObject = "Error_SerializerNotParameterBindingObject"; - internal const string Error_SerializerThrewException = "Error_SerializerThrewException"; - internal const string Error_ActivityCollectionSerializer = "Error_ActivityCollectionSerializer"; - internal const string Error_MissingClassAttribute = "Error_MissingClassAttribute"; - internal const string Error_MissingClassAttributeValue = "Error_MissingClassAttributeValue"; - internal const string ExecutorCreationFailedErrorMessage = "ExecutorCreationFailedErrorMessage"; - internal const string VariableGetterCode_VB = "VariableGetterCode_VB"; - internal const string VariableGetterCode_CS = "VariableGetterCode_CS"; - internal const string VariableSetterCode_VB = "VariableSetterCode_VB"; - internal const string VariableSetterCode_CS = "VariableSetterCode_CS"; - internal const string StaticVariableGetterCode_VB = "StaticVariableGetterCode_VB"; - internal const string StaticVariableGetterCode_CS = "StaticVariableGetterCode_CS"; - internal const string StaticVariableSetterCode_VB = "StaticVariableSetterCode_VB"; - internal const string StaticVariableSetterCode_CS = "StaticVariableSetterCode_CS"; - internal const string EnterCodeBesidesCode_VB = "EnterCodeBesidesCode_VB"; - internal const string EnterCodeBesidesCode_CS = "EnterCodeBesidesCode_CS"; - internal const string LeaveCodeBesides1Code_VB = "LeaveCodeBesides1Code_VB"; - internal const string LeaveCodeBesides2Code_VB = "LeaveCodeBesides2Code_VB"; - internal const string LeaveCodeBesides1Code_CS = "LeaveCodeBesides1Code_CS"; - internal const string LeaveCodeBesides2Code_CS = "LeaveCodeBesides2Code_CS"; - internal const string VariableSetterName = "VariableSetterName"; - internal const string VariableGetterName = "VariableGetterName"; - internal const string HandlerGetterName = "HandlerGetterName"; - internal const string WorkflowCreatorName = "WorkflowCreatorName"; - internal const string ActivityMethod = "ActivityMethod"; - internal const string CustomActivityPrivateField = "CustomActivityPrivateField"; - internal const string InitializedVariableDeclaration_VB = "InitializedVariableDeclaration_VB"; - internal const string InitializedVariableDeclaration_CS = "InitializedVariableDeclaration_CS"; - internal const string In = "In"; - internal const string Out = "Out"; - internal const string Ref = "Ref"; - internal const string Required = "Required"; - internal const string Optional = "Optional"; - internal const string Parameters = "Parameters"; - internal const string Properties = "Properties"; - internal const string Error_RecursionDetected = "Error_RecursionDetected"; - internal const string Warning_UnverifiedRecursion = "Warning_UnverifiedRecursion"; - internal const string AddConstructorCode = "AddConstructorCode"; - internal const string Error_UninitializedCorrelation = "Error_UninitializedCorrelation"; - internal const string Error_CorrelationAlreadyInitialized = "Error_CorrelationAlreadyInitialized"; - internal const string Error_CorrelatedSendReceiveAtomicScope = "Error_CorrelatedSendReceiveAtomicScope"; - internal const string Warning_ActivityValidation = "Warning_ActivityValidation"; - internal const string Warning_EmptyBehaviourActivity = "Warning_EmptyBehaviourActivity"; - internal const string Error_ParallelActivationNoCorrelation = "Error_ParallelActivationNoCorrelation"; - internal const string Error_MethodNotAccessible = "Error_MethodNotAccessible"; - internal const string Error_FieldNotAccessible = "Error_FieldNotAccessible"; - internal const string Error_PropertyNotAccessible = "Error_PropertyNotAccessible"; - internal const string Error_GenericArgumentsNotAllowed = "Error_GenericArgumentsNotAllowed"; - internal const string Error_InvalidIdentifier = "Error_InvalidIdentifier"; - internal const string Error_InvalidLanguageIdentifier = "Error_InvalidLanguageIdentifier"; - internal const string DuplicateActivityIdentifier = "DuplicateActivityIdentifier"; - internal const string Error_MissingAttribute = "Error_MissingAttribute"; - internal const string Error_LoadUIPropertiesFile = "Error_LoadUIPropertiesFile"; - internal const string Error_SerializerEventGetFailed = "Error_SerializerEventGetFailed"; - internal const string Error_SerializerEventFailed = "Error_SerializerEventFailed"; - internal const string Error_SerializerNoMemberFound = "Error_SerializerNoMemberFound"; - internal const string Error_DynamicEventConflict = "Error_DynamicEventConflict"; - internal const string Error_SerializerMemberSetFailed = "Error_SerializerMemberSetFailed"; - internal const string Error_ContentPropertyCouldNotBeFound = "Error_ContentPropertyCouldNotBeFound"; - internal const string Error_ContentPropertyValueInvalid = "Error_ContentPropertyValueInvalid"; - internal const string Error_ContentPropertyNoSetter = "Error_ContentPropertyNoSetter"; - internal const string Error_ContentCanNotBeConverted = "Error_ContentCanNotBeConverted"; - internal const string Error_ContentPropertyCanNotBeNull = "Error_ContentPropertyCanNotBeNull"; - internal const string Error_SerializerTypeMismatch = "Error_SerializerTypeMismatch"; - internal const string Error_CouldNotAddValueInContentProperty = "Error_CouldNotAddValueInContentProperty"; - internal const string Error_SerializerTypeRequirement = "Error_SerializerTypeRequirement"; - internal const string Error_CanNotAddActivityInBlackBoxActivity = "Error_CanNotAddActivityInBlackBoxActivity"; - internal const string Error_ContentPropertyCanNotSupportCompactFormat = "Error_ContentPropertyCanNotSupportCompactFormat"; - internal const string Error_ContentPropertyNoMultipleContents = "Error_ContentPropertyNoMultipleContents"; - internal const string Error_InternalSerializerError = "Error_InternalSerializerError"; - internal const string Error_DictionarySerializerNonDictionaryObject = "Error_DictionarySerializerNonDictionaryObject"; - internal const string Error_DictionarySerializerKeyNotFound = "Error_DictionarySerializerKeyNotFound"; - internal const string Error_InvalidCancelActivityState = "Error_InvalidCancelActivityState"; - internal const string Error_InvalidCompensateActivityState = "Error_InvalidCompensateActivityState"; - internal const string Error_InvalidCloseActivityState = "Error_InvalidCloseActivityState"; - internal const string Error_SealedPropertyMetadata = "Error_SealedPropertyMetadata"; - internal const string Error_MemberNotFound = "Error_MemberNotFound"; - internal const string Error_EmptyPathValue = "Error_EmptyPathValue"; - internal const string Error_InvalidCompensatingState = "Error_InvalidCompensatingState"; - internal const string Error_InvalidCancelingState = "Error_InvalidCancelingState"; - internal const string Error_InvalidClosingState = "Error_InvalidClosingState"; - internal const string Error_InvalidStateToExecuteChild = "Error_InvalidStateToExecuteChild"; - internal const string Error_InvalidExecutionState = "Error_InvalidExecutionState"; - internal const string Error_InvalidInitializingState = "Error_InvalidInitializingState"; - internal const string Error_InvalidInvokingState = "Error_InvalidInvokingState"; - internal const string Error_NotRegisteredAs = "Error_NotRegisteredAs"; - internal const string Error_AlreadyRegisteredAs = "Error_AlreadyRegisteredAs"; - internal const string Error_InsertingChildControls = "Error_InsertingChildControls"; - internal const string Error_EmptyToolTipRectangle = "Error_EmptyToolTipRectangle"; - internal const string Error_EmptyRectangleValue = "Error_EmptyRectangleValue"; - internal const string Error_InvalidShadowRectangle = "Error_InvalidShadowRectangle"; - internal const string Error_InvalidShadowDepth = "Error_InvalidShadowDepth"; - internal const string Error_InvalidLightSource = "Error_InvalidLightSource"; - internal const string Error_ChangingDock = "Error_ChangingDock"; - internal const string Error_NullOrEmptyValue = "Error_NullOrEmptyValue"; - internal const string Error_InvalidStateImages = "Error_InvalidStateImages"; - internal const string Error_InvalidConnectorSegment = "Error_InvalidConnectorSegment"; - internal const string Error_InvalidConnectorSource = "Error_InvalidConnectorSource"; - internal const string Error_CreatingToolTip = "Error_CreatingToolTip"; - internal const string Error_InvalidDockStyle = "Error_InvalidDockStyle"; - internal const string Error_InvalidConnectorValue = "Error_InvalidConnectorValue"; - internal const string Error_InvalidDesignerVerbValue = "Error_InvalidDesignerVerbValue"; - internal const string Error_InvalidRuntimeType = "Error_InvalidRuntimeType"; - internal const string Error_InvalidArgumentValue = "Error_InvalidArgumentValue"; - internal const string Error_InvalidRadiusValue = "Error_InvalidRadiusValue"; - internal const string ToolTipString = "ToolTipString"; - - // Collection Editor Resources - internal const string CollectionEditorCaption = "CollectionEditorCaption"; - internal const string CollectionEditorProperties = "CollectionEditorProperties"; - internal const string CollectionEditorPropertiesMultiSelect = "CollectionEditorPropertiesMultiSelect"; - internal const string CollectionEditorPropertiesNone = "CollectionEditorPropertiesNone"; - internal const string CollectionEditorCantRemoveItem = "CollectionEditorCantRemoveItem"; - internal const string CollectionEditorUndoBatchDesc = "CollectionEditorUndoBatchDesc"; - internal const string CollectionEditorInheritedReadOnlySelection = "CollectionEditorInheritedReadOnlySelection"; - internal const string Error_ParameterAlreadyExists = "Error_ParameterAlreadyExists"; - internal const string Error_PropertyAlreadyExists = "Error_PropertyAlreadyExists"; - internal const string Error_HiddenPropertyAlreadyExists = "Error_HiddenPropertyAlreadyExists"; - internal const string Error_CorrelationInUse = "Error_CorrelationInUse"; - internal const string Error_ItemNotExists = "Error_ItemNotExists"; - internal const string Error_NoHelpAvailable = "Error_NoHelpAvailable"; - internal const string Error_DuplicateWorkflow = "Error_DuplicateWorkflow"; - internal const string Error_Recursion = "Error_Recursion"; - internal const string Error_RootActivity = "Error_RootActivity"; - internal const string Error_ConditionDefinitionDeserializationFailed = "Error_ConditionDefinitionDeserializationFailed"; - internal const string Error_InvalidConditionDefinition = "Error_InvalidConditionDefinition"; - internal const string SR_InvokeTransactionalFromAtomic = "SR_InvokeTransactionalFromAtomic"; - internal const string Error_SuspendInAtomicCallChain = "Error_SuspendInAtomicCallChain"; - internal const string Error_LiteralPassedToOutRef = "Error_LiteralPassedToOutRef"; - internal const string Error_GeneratorShouldContainSingleActivity = "Error_GeneratorShouldContainSingleActivity"; - internal const string Error_DeclaringPropertyNotSupported = "Error_DeclaringPropertyNotSupported"; - internal const string Error_DeclaringEventNotSupported = "Error_DeclaringEventNotSupported"; - internal const string Error_DynamicEventNotSupported = "Error_DynamicEventNotSupported"; - internal const string Error_DynamicPropertyNotSupported = "Error_DynamicPropertyNotSupported"; - internal const string Error_ParameterTypeResolution = "Error_ParameterTypeResolution"; - - // Dynamic Validations - internal const string Error_DynamicActivity = "Error_DynamicActivity"; - internal const string Error_DynamicActivity2 = "Error_DynamicActivity2"; - internal const string Error_CompilerValidationFailed = "Error_CompilerValidationFailed"; - internal const string Error_RuntimeValidationFailed = "Error_RuntimeValidationFailed"; - internal const string Error_TransactionAlreadyCanceled = "Error_TransactionAlreadyCanceled"; - internal const string Error_RemoveExecutingActivity = "Error_RemoveExecutingActivity"; - internal const string Error_InsideAtomicScope = "Error_InsideAtomicScope"; - internal const string SuspendReason_WorkflowChange = "SuspendReason_WorkflowChange"; - - // type filtering - internal const string FilterDescription_ParameterDeclaration = "FilterDescription_ParameterDeclaration"; - internal const string FilterDescription_GenericArgument = "FilterDescription_GenericArgument"; - - - internal const string LibraryPathIsInvalid = "LibraryPathIsInvalid"; - - // Activity Set - internal const string Error_CreateValidator = "Error_CreateValidator"; - internal const string Error_InvalidPackageFile = "Error_InvalidPackageFile"; - internal const string Error_AddAssemblyRef = "Error_AddAssemblyRef"; - internal const string Error_AssemblyBadImage = "Error_AssemblyBadImage"; - internal const string BindPropertySetterName = "BindPropertySetterName"; - - // Bind validations - internal const string Error_CannotResolveActivity = "Error_CannotResolveActivity"; - internal const string Error_CannotResolveRelativeActivity = "Error_CannotResolveRelativeActivity"; - internal const string Error_PathNotSetForActivitySource = "Error_PathNotSetForActivitySource"; - internal const string Error_InvalidMemberPath = "Error_InvalidMemberPath"; - internal const string Error_TargetTypeMismatch = "Error_TargetTypeMismatch"; - internal const string Warning_ParameterBinding = "Warning_ParameterBinding"; - internal const string Error_ReferencedActivityPropertyNotBind = "Error_ReferencedActivityPropertyNotBind"; - internal const string Error_TargetTypeDataSourcePathMismatch = "Error_TargetTypeDataSourcePathMismatch"; - internal const string Bind_ActivityDataSourceRecursionDetected = "Bind_ActivityDataSourceRecursionDetected"; - internal const string Bind_DuplicateDataSourceNames = "Bind_DuplicateDataSourceNames"; - internal const string Error_PathNotSetForXmlDataSource = "Error_PathNotSetForXmlDataSource"; - internal const string Error_XmlDocumentLoadFailed = "Error_XmlDocumentLoadFailed"; - internal const string Error_XmlDataSourceInvalidPath = "Error_XmlDataSourceInvalidPath"; - internal const string Error_XmlDataSourceMultipleNodes = "Error_XmlDataSourceMultipleNodes"; - internal const string Error_XmlDataSourceInvalidXPath = "Error_XmlDataSourceInvalidXPath"; - internal const string Error_InvalidObjectRefFormat = "Error_InvalidObjectRefFormat"; - internal const string Error_ReadOnlyDataSource = "Error_ReadOnlyDataSource"; - internal const string Error_HandlerReadOnly = "Error_HandlerReadOnly"; - internal const string Error_XmlDataSourceReadOnly = "Error_XmlDataSourceReadOnly"; - internal const string Error_DataSourceNotExist = "Error_DataSourceNotExist"; - internal const string Error_PropertyNoGetter = "Error_PropertyNoGetter"; - internal const string Error_PropertyNoSetter = "Error_PropertyNoSetter"; - internal const string Error_PropertyHasNoGetterDefined = "Error_PropertyHasNoGetterDefined"; - internal const string Error_PropertyHasNoSetterDefined = "Error_PropertyHasNoSetterDefined"; - internal const string Error_PropertyReferenceNoGetter = "Error_PropertyReferenceNoGetter"; - internal const string Error_PropertyReferenceGetterNoAccess = "Error_PropertyReferenceGetterNoAccess"; - internal const string Error_PropertyHasIndexParameters = "Error_PropertyHasIndexParameters"; - internal const string Error_ReadOnlyField = "Error_ReadOnlyField"; - internal const string Error_NoEnclosingContext = "Error_NoEnclosingContext"; - internal const string Error_NestedPersistOnClose = "Error_NestedPersistOnClose"; - internal const string Error_NestedCompensatableActivity = "Error_NestedCompensatableActivity"; - internal const string Error_InvalidActivityForObjectDatasource = "Error_InvalidActivityForObjectDatasource"; - internal const string Error_DataSourceTypeConversionFailed = "Error_DataSourceTypeConversionFailed"; - internal const string Error_BindDialogWrongPropertyType = "Error_BindDialogWrongPropertyType"; - internal const string Error_BindDialogNoValidPropertySelected = "Error_BindDialogNoValidPropertySelected"; - internal const string Error_BindDialogBindNotValid = "Error_BindDialogBindNotValid"; - internal const string Error_BindDialogCanNotBindToItself = "Error_BindDialogCanNotBindToItself"; - internal const string Error_BindActivityReference = "Error_BindActivityReference"; - internal const string Error_NoTargetTypeForMethod = "Error_NoTargetTypeForMethod"; - internal const string Error_MethodDataSourceIsReadOnly = "Error_MethodDataSourceIsReadOnly"; - internal const string Error_NotMethodDataSource = "Error_NotMethodDataSource"; - internal const string Error_MethodDataSourceWithPath = "Error_MethodDataSourceWithPath"; - internal const string Error_PathSyntax = "Error_PathSyntax"; - internal const string Error_UnmatchedParen = "Error_UnmatchedParen"; - internal const string Error_UnmatchedBracket = "Error_UnmatchedBracket"; - internal const string Error_MemberWithSameNameExists = "Error_MemberWithSameNameExists"; - internal const string Error_ActivityIdentifierCanNotBeEmpty = "Error_ActivityIdentifierCanNotBeEmpty"; - internal const string Error_InvalidActivityIdentifier = "Error_InvalidActivityIdentifier"; - internal const string Error_ActivityBindTypeConversionError = "Error_ActivityBindTypeConversionError"; - internal const string EmptyValue = "EmptyValue"; - internal const string Error_PropertyTypeNotDefined = "Error_PropertyTypeNotDefined"; - - internal const string Error_CompilationFailed = "Error_CompilationFailed"; - internal const string Error_MissingCompilationContext = "Error_MissingCompilationContext"; - - internal const string InvokeWorkflowReference_VB = "InvokeWorkflowReference_VB"; - internal const string InvokeWorkflowReference_CS = "InvokeWorkflowReference_CS"; - internal const string Error_InvalidListItem = "Error_InvalidListItem"; - - internal const string ParserMapPINoWhitespace = "ParserMapPINoWhitespace"; - internal const string ParserMapPIBadCharEqual = "ParserMapPIBadCharEqual"; - internal const string ParserMapPIBadCharQuote = "ParserMapPIBadCharQuote"; - internal const string ParserMapPIBadKey = "ParserMapPIBadKey"; - internal const string ParserMapPIMissingKey = "ParserMapPIMissingKey"; - internal const string ParserMapPIKeyNotSet = "ParserMapPIKeyNotSet"; - internal const string ParserMismatchDelimiter = "ParserMismatchDelimiter"; - internal const string ParserDanglingClause = "ParserDanglingClause"; - internal const string UnknownDefinitionTag = "UnknownDefinitionTag"; - internal const string CDATASection = "CDATASection"; - internal const string TextSection = "TextSection"; - internal const string IncorrectSyntax = "IncorrectSyntax"; - internal const string IncorrectTypeSyntax = "IncorrectTypeSyntax"; - internal const string Error_MultipleRootActivityCreator = "Error_MultipleRootActivityCreator"; - internal const string Error_MustHaveParent = "Error_MustHaveParent"; - - // Workflow References - internal const string Error_ReferenceObjNotInitialized = "Error_ReferenceObjNotInitialized"; - internal const string Error_ReferenceInitResourceManager = "Error_ReferenceInitResourceManager"; - internal const string Error_ResourceReferenceGetObject = "Error_ResourceReferenceGetObject"; - internal const string Error_RefBindCantFindRef = "Error_RefBindCantFindRef"; - internal const string Error_RefBindMissingReferenceName = "Error_RefBindMissingReferenceName"; - internal const string Error_RefBindMissingAttribute = "Error_RefBindMissingAttribute"; - internal const string Error_ReferenceLoad = "Error_ReferenceLoad"; - internal const string Error_ReferenceMissingAttribute = "Error_ReferenceMissingAttribute"; - internal const string Error_ReferenceInvalidResourceFile = "Error_ReferenceInvalidResourceFile"; - internal const string Error_ReferenceEmptyName = "Error_ReferenceEmptyName"; - - internal const string HandlerInvokerName = "HandlerInvokerName"; - internal const string HandlerInvokerSwitchPrefix_CS = "HandlerInvokerSwitchPrefix_CS"; - internal const string HandlerInvokerSwitchPrefix_VB = "HandlerInvokerSwitchPrefix_VB"; - internal const string HandlerInvokerSwitchSuffix_CS = "HandlerInvokerSwitchSuffix_CS"; - internal const string HandlerInvokerSwitchSuffix_VB = "HandlerInvokerSwitchSuffix_VB"; - internal const string HandlerInvokerCaseBegin_CS = "HandlerInvokerCaseBegin_CS"; - internal const string HandlerInvokerCaseBegin_VB = "HandlerInvokerCaseBegin_VB"; - - // Activity Category - internal const string Standard = "Standard"; - internal const string Base = "Base"; - - // CustomActivityDesigner - internal const string ValidatorCompanionClassDesc = "ValidatorCompanionClassDesc"; - internal const string ExecutorCompanionClassDesc = "ExecutorCompanionClassDesc"; - internal const string DesignerCompanionClassDesc = "DesignerCompanionClassDesc"; - internal const string CustomActivityBaseTypeDesc = "CustomActivityBaseTypeDesc"; - internal const string ActivityProperties = "ActivityProperties"; - internal const string ActivityPropertiesDesc = "ActivityPropertiesDesc"; - internal const string CompanionClasses = "CompanionClasses"; - internal const string ActivityDesc = "Activity"; - internal const string Error_TypeConversionFailed = "Error_TypeConversionFailed"; - internal const string SupportDataContext = "SupportDataContext"; - internal const string AdvancedCategory = "AdvancedCategory"; - internal const string SupportDataContextDesc = "SupportDataContextDesc"; - internal const string BaseCompanionClassName = "BaseCompanionClassName"; - internal const string BaseCompanionClassDesc = "BaseCompanionClassDesc"; - internal const string Designer = "Designer"; - internal const string Validator = "Validator"; - internal const string Executor = "Executor"; - internal const string BaseActivityType = "BaseActivityType"; - internal const string Error_NotBuiltInActivity = "Error_NotBuiltInActivity"; - internal const string NoChildActivities_Message = "NoChildActivities_Message"; - internal const string NoChildActivities_Caption = "NoChildActivities_Caption"; - internal const string Error_CustomActivityCantCreate = "Error_CustomActivityCantCreate"; - internal const string Error_CantChangeBuiltInActivity = "Error_CantChangeBuiltInActivity"; - internal const string Error_CantAddBeforeBuiltInActivity = "Error_CantAddBeforeBuiltInActivity"; - internal const string Error_CantAddAfterNonBuiltInActivity = "Error_CantAddAfterNonBuiltInActivity"; - internal const string Error_CannotAddRemoveChildActivities = "Error_CannotAddRemoveChildActivities"; - internal const string Error_CantFindBuiltInActivity = "Error_CantFindBuiltInActivity"; - internal const string Error_MissingBaseCompanionClassAttribute = "Error_MissingBaseCompanionClassAttribute"; - internal const string Error_CantFindBuiltInParent = "Error_CantFindBuiltInParent"; - internal const string Error_CantCreateInstanceOfBaseType = "Error_CantCreateInstanceOfBaseType"; - internal const string Error_CustomActivityTypeCouldNotBeFound = "Error_CustomActivityTypeCouldNotBeFound"; - internal const string None = "None"; - internal const string AtomicTransaction = "AtomicTransaction"; - internal const string LocalDataContext = "LocalDataContext"; - internal const string LocalDataContextDesc = "LocalDataContextDesc"; - internal const string CompanionClass = "CompanionClass"; - internal const string Error_AlreadyRootActivity = "Error_AlreadyRootActivity"; - internal const string RootActivityName = "RootActivityName"; - internal const string RootActivityNameDesc = "RootActivityNameDesc"; - internal const string CustomProperties = "CustomProperties"; - internal const string VisibleDescr = "VisibleDescr"; - internal const string EditableDescr = "EditableDescr"; - internal const string Error_CantCreateMethod = "Error_CantCreateMethod"; - internal const string Error_CantEditNullValue = "Error_CantEditNullValue"; - internal const string Error_CompanionTypeNotSet = "Error_CompanionTypeNotSet"; - internal const string Error_CompanionClassNameCanNotBeEmpty = "Error_CompanionClassNameCanNotBeEmpty"; - internal const string Error_CouldNotEmitFieldInLocalDataContext = "Error_CouldNotEmitFieldInLocalDataContext"; - internal const string Error_CouldNotEmitMethodInLocalDataContext = "Error_CouldNotEmitMethodInLocalDataContext"; - internal const string Error_DerivationFromTypeWithLocalDataContext = "Error_DerivationFromTypeWithLocalDataContext"; - internal const string Error_CompanionTypeDerivationError = "Error_CompanionTypeDerivationError"; - internal const string Error_CantCreateDataContextClass = "Error_CantCreateDataContextClass"; - internal const string ArrayExistingBind = "ArrayExistingBind"; - internal const string Error_NoMatchingFieldsOrProperties = "Error_NoMatchingFieldsOrProperties"; - internal const string ChooseFieldPropertyDatasource = "ChooseFieldPropertyDatasource"; - - internal const string SupportsTransaction = "SupportsTransaction"; - internal const string SupportsExceptions = "SupportsExceptions"; - internal const string SupportsCancellationHandlerActivity = "SupportsCancellationHandlerActivity"; - internal const string SupportsEvents = "SupportsEvents"; - internal const string SupportsDataSources = "SupportsDataSources"; - internal const string SupportsCompensationHandler = "SupportsCompensationHandler"; - internal const string SupportsCompensationHandlerDesc = "SupportsCompensationHandlerDesc"; - internal const string SupportsTransactionDesc = "SupportsTransactionDesc"; - internal const string SupportsExceptionsDesc = "SupportsExceptionsDesc"; - internal const string SupportsCancelHandlerDesc = "SupportsCancelHandlerDesc"; - internal const string SupportsEventsDesc = "SupportsEventsDesc"; - internal const string TransactionDesc = "TransactionDesc"; - - internal const string Error_BaseTypeMustBeActivity = "Error_BaseTypeMustBeActivity"; - internal const string ExistingActivityBindTitle = "ExistingActivityBindTitle"; - internal const string ExistingActivityBindLabel = "ExistingActivityBindLabel"; - internal const string ExistingFieldPropBindTitle = "ExistingFieldPropBindTitle"; - internal const string ExistingFieldPropBindLabel = "ExistingFieldPropBindLabel"; - internal const string ProvidesSynchronization = "ProvidesSynchronization"; - internal const string ProvidesSynchronizationDesc = "ProvidesSynchronizationDesc"; - internal const string SynchronizationHandles = "SynchronizationHandles"; - internal const string SynchronizationHandlesDesc = "SynchronizationHandlesDesc"; - - internal const string Error_TransactionAlreadyApplied = "Error_TransactionAlreadyApplied"; - internal const string Error_BindBaseTypeNotSpecified = "Error_BindBaseTypeNotSpecified"; - internal const string NonDelegateTargetType = "NonDelegateTargetType"; - internal const string Error_ClassnameNotInRootNamespace = "Error_ClassnameNotInRootNamespace"; - internal const string Error_CantUseCurrentProjectTypeAsBase = "Error_CantUseCurrentProjectTypeAsBase"; - internal const string Error_UnboundGenericType = "Error_UnboundGenericType"; - internal const string Error_UnboundGenericTypeDataSource = "Error_UnboundGenericTypeDataSource"; - internal const string Error_BaseTypeUnknown = "Error_BaseTypeUnknown"; - internal const string Error_UnconfiguredBind = "Error_UnconfiguredBind"; - internal const string Error_CanNotEmitMemberInLocalDataContext = "Error_CanNotEmitMemberInLocalDataContext"; - internal const string Error_DesignedTypeNotFound = "Error_DesignedTypeNotFound"; - internal const string Error_PathCouldNotBeResolvedToMember = "Error_PathCouldNotBeResolvedToMember"; - internal const string Error_EdittingNullCollection = "Error_EdittingNullCollection"; - internal const string Error_MoreThanOneCompensationDecl = "Error_MoreThanOneCompensationDecl"; - internal const string Error_ParentDoesNotSupportCompensation = "Error_ParentDoesNotSupportCompensation"; - internal const string Error_CantResolveEventHandler = "Error_CantResolveEventHandler"; - internal const string Error_XSDObjectTypeNotSerializable = "Error_XSDObjectTypeNotSerializable"; - internal const string AEC_InvalidActivity = "AEC_InvalidActivity"; - internal const string GetDynamicActivities_InvalidActivity = "GetDynamicActivities_InvalidActivity"; - internal const string AEC_InvalidNestedActivity = "AEC_InvalidNestedActivity"; - internal const string Error_IDNotSetForActivitySource = "Error_IDNotSetForActivitySource"; - internal const string Error_InvalidCustomPropertyName = "Error_InvalidCustomPropertyName"; - internal const string Error_InvalidCustomPropertyType = "Error_InvalidCustomPropertyType"; - - internal const string Error_DPReadOnly = "Error_DPReadOnly"; - internal const string Error_DPMetaPropertyBinding = "Error_DPMetaPropertyBinding"; - internal const string Error_DPSetValueBind = "Error_DPSetValueBind"; - internal const string Error_DPSetValueHandler = "Error_DPSetValueHandler"; - internal const string Error_DPGetValueHandler = "Error_DPGetValueHandler"; - internal const string Error_DPAddHandlerNonEvent = "Error_DPAddHandlerNonEvent"; - internal const string Error_DPAddHandlerMetaProperty = "Error_DPAddHandlerMetaProperty"; - internal const string Error_DPRemoveHandlerBind = "Error_DPRemoveHandlerBind"; - internal const string Error_LanguageNeedsToBeVBCSharp = "Error_LanguageNeedsToBeVBCSharp"; - internal const string Error_TargetFxNotSupported = "Error_TargetFxNotSupported"; - internal const string Error_CantConvertValueValue = "Error_CantConvertValueValue"; - internal const string Error_TypeIsNotValid = "Error_TypeIsNotValid"; - internal const string Error_TypePropertyInvalid = "Error_TypePropertyInvalid"; - internal const string Error_EventCantBeMetaProperty = "Error_EventCantBeMetaProperty"; - internal const string Error_EventMustBeDelegate = "Error_EventMustBeDelegate"; - internal const string Error_DPPropertyTypeMissing = "Error_DPPropertyTypeMissing"; - - internal const string TransactionalContextActivityDescription = "TransactionalContextActivityDescription"; - internal const string CompensatableTransactionalContextActivityDescription = "CompensatableTransactionalContextActivityDescription"; - internal const string SynchronizationScopeActivityDescription = "SynchronizationScopeActivityDescription"; - internal const string SequenceActivityDescription = "SequenceActivityDescription"; - internal const string CompensateActivityDescription = "CompensateActivityDescription"; - internal const string Error_CompensateBadTargetTX = "Error_CompensateBadTargetTX"; - internal const string Error_CancelHandlerParentNotScope = "Error_CancelHandlerParentNotScope"; - internal const string FaultHandlerActivityDescription = "FaultHandlerActivityDescription"; - internal const string Error_ExceptionTypeNotException = "Error_ExceptionTypeNotException"; - internal const string Error_FaultIsNotOfFaultType = "Error_FaultIsNotOfFaultType"; - internal const string Error_FaultTypeNoDefaultConstructor = "Error_FaultTypeNoDefaultConstructor"; - internal const string FilterDescription_FaultHandlerActivity = "FilterDescription_FaultHandlerActivity"; - internal const string Error_FaultHandlerActivityParentNotFaultHandlersActivity = "Error_FaultHandlerActivityParentNotFaultHandlersActivity"; - internal const string Error_FaultHandlerActivityAllMustBeLast = "Error_FaultHandlerActivityAllMustBeLast"; - internal const string Error_FaultHandlersActivityDeclNotAllFaultHandlerActivityDecl = "Error_FaultHandlersActivityDeclNotAllFaultHandlerActivityDecl"; - internal const string Error_FaultHandlerActivityWrongOrder = "Error_FaultHandlerActivityWrongOrder"; - internal const string Error_SenderMustBeActivityExecutionContext = "Error_SenderMustBeActivityExecutionContext"; - internal const string Error_XomlWorkflowHasCode = "Error_XomlWorkflowHasCode"; - internal const string Error_WrongParamForActivityResolveEventArgs = "Error_WrongParamForActivityResolveEventArgs"; - internal const string Error_ValidatorThrewException = "Error_ValidatorThrewException"; - internal const string Error_Missing_CanModifyProperties_True = "Error_Missing_CanModifyProperties_True"; - internal const string Error_Missing_CanModifyProperties_False = "Error_Missing_CanModifyProperties_False"; - internal const string Error_ModelingConstructsCanNotContainModelingConstructs = "Error_ModelingConstructsCanNotContainModelingConstructs"; - internal const string Error_RootIsNotEnabled = "Error_RootIsNotEnabled"; - internal const string Error_MissingSetAccessor = "Error_MissingSetAccessor"; - internal const string Error_MissingAddHandler = "Error_MissingAddHandler"; - internal const string Error_MissingCLRProperty = "Error_MissingCLRProperty"; - - internal const string Error_NotReadOnlyProperty = "Error_NotReadOnlyProperty"; - internal const string Error_InvalidDependencyProperty = "Error_InvalidDependencyProperty"; - internal const string Error_ActivityNameExist = "Error_ActivityNameExist"; - internal const string CannotCreateAttribute = "CannotCreateAttribute"; - internal const string NamespaceAndDeclaringTypeCannotBeNull = "NamespaceAndDeclaringTypeCannotBeNull"; - internal const string NotElementType = "NotElementType"; - - // Layout persistence errors - internal const string Error_LayoutSerializationActivityNotFound = "Error_LayoutSerializationActivityNotFound"; - internal const string Error_LayoutSerializationAssociatedActivityNotFound = "Error_LayoutSerializationAssociatedActivityNotFound"; - internal const string Error_LayoutSerializationPersistenceSupport = "Error_LayoutSerializationPersistenceSupport"; - internal const string Error_LayoutSerializationRootDesignerNotFound = "Error_LayoutSerializationRootDesignerNotFound"; - internal const string Error_ParameterCannotBeEmpty = "Error_ParameterCannotBeEmpty"; - internal const string InvalidExecutionStatus = "InvalidExecutionStatus"; - internal const string Error_LayoutDeserialization = "Error_LayoutDeserialization"; - internal const string Error_LayoutSerialization = "Error_LayoutSerialization"; - - internal const string Error_SerializerStackOverflow = "Error_SerializerStackOverflow"; - internal const string Error_InvalidActivityForWorkflowChanges = "Error_InvalidActivityForWorkflowChanges"; - internal const string Error_InvalidMemberType = "Error_InvalidMemberType"; - internal const string Error_BindPathNullValue = "Error_BindPathNullValue"; - internal const string Error_MarkupExtensionMissingTerminatingCharacter = "Error_MarkupExtensionMissingTerminatingCharacter"; - internal const string Error_MarkupExtensionDeserializeFailed = "Error_MarkupExtensionDeserializeFailed"; - internal const string Error_ApplyDynamicChangeFailed = "Error_ApplyDynamicChangeFailed"; - internal const string Error_ActivityCircularReference = "Error_ActivityCircularReference"; - internal const string Error_ValidatorTypeIsInvalid = "Error_ValidatorTypeIsInvalid"; - internal const string Error_InvalidServiceProvider = "Error_InvalidServiceProvider"; - internal const string Error_InvalidRootForWorkflowChanges = "Error_InvalidRootForWorkflowChanges"; - internal const string Error_ExtraCharacterFoundAtEnd = "Error_ExtraCharacterFoundAtEnd"; - internal const string Error_WorkflowChangesNotSupported = "Error_WorkflowChangesNotSupported"; - internal const string Error_TypeSystemAttributeArgument = "Error_TypeSystemAttributeArgument"; - - internal const string Error_InvalidElementFoundForType = "Error_InvalidElementFoundForType"; - internal const string Error_ActivitySaveLoadNotCalled = "Error_ActivitySaveLoadNotCalled"; - internal const string Error_CanNotBindProperty = "Error_CanNotBindProperty"; -} -#pragma warning restore CS8603 // 可能返回 null 引用。 - -#endif \ No newline at end of file diff --git a/src/IFoxCAD.Basal/Sortedset/Sortedset.cs b/src/IFoxCAD.Basal/Sortedset/Sortedset.cs deleted file mode 100644 index 4088582..0000000 --- a/src/IFoxCAD.Basal/Sortedset/Sortedset.cs +++ /dev/null @@ -1,2896 +0,0 @@ -#if NET35 -#pragma warning disable CS8603 // 可能返回 null 引用。 -#pragma warning disable CS8601 // 引用类型赋值可能为 null。 -#pragma warning disable CS8618 // 在退出构造函数时,不可为 null 的字段必须包含非 null 值。请考虑声明为可以为 null。 -#pragma warning disable CS8625 // 无法将 null 字面量转换为非 null 的引用类型。 -#pragma warning disable IDE0059 // 不需要赋值 -#pragma warning disable CS8600 // 将 null 字面量或可能为 null 的值转换为非 null 类型。 -#pragma warning disable CS8602 // 解引用可能出现空引用。 -#pragma warning disable CS8604 // 引用类型参数可能为 null。 -// #define USING_HASH_SET -// ==++== -// -// Copyright (c) Microsoft Corporation. All rights reserved. -// -// ==--== -/*============================================================ -** -** Class: SortedSet -** -** Purpose: A generic sorted set. -** -** Date: August 15, 2008 -** -===========================================================*/ - - -namespace System.Collections.Generic -{ - using System; - using System.Diagnostics; - using System.Diagnostics.CodeAnalysis; - using System.Runtime.Serialization; - - - - // - // A binary search tree is a red-black tree if it satisfies the following red-black properties: - // 1. Every node is either red or black - // 2. Every leaf (nil node) is black - // 3. If a node is red, then both its children are black - // 4. Every simple path from a node to a descendant leaf contains the same number of black nodes - // - // The basic idea of red-black tree is to represent 2-3-4 trees as standard BSTs but to add one extra bit of information - // per node to encode 3-nodes and 4-nodes. - // 4-nodes will be represented as: B - // R R - // 3 -node will be represented as: B or B - // R B B R - // - // For a detailed description of the algorithm, take a look at "Algorithms" by Robert Sedgewick. - // - - internal delegate bool TreeWalkPredicate(SortedSet.Node node); - - internal enum TreeRotation - { - LeftRotation = 1, - RightRotation = 2, - RightLeftRotation = 3, - LeftRightRotation = 4, - } - - [SuppressMessage("Microsoft.Naming", "CA1710:IdentifiersShouldHaveCorrectSuffix", Justification = "by design name choice")] - [DebuggerTypeProxy(nameof(SortedSet))]/*这句改了*/ - [DebuggerDisplay("Count = {Count}")] -#if !FEATURE_NETCORE - [Serializable] - public class SortedSet : ISet, ICollection, ICollection, ISerializable, IDeserializationCallback// , IReadOnlyCollection - { -#else - public class SortedSet : ISet, ICollection, ICollection, IReadOnlyCollection { -#endif // !FEATURE_NETCORE - #region local variables/constants - Node root; - IComparer comparer; - int count; - int version; - private Object _syncRoot; - - private const String ComparerName = "Comparer"; - private const String CountName = "Count"; - private const String ItemsName = "Items"; - private const String VersionName = "Version"; - // needed for enumerator - private const String TreeName = "Tree"; - private const String NodeValueName = "Item"; - private const String EnumStartName = "EnumStarted"; - private const String ReverseName = "Reverse"; - private const String EnumVersionName = "EnumVersion"; - -#if !FEATURE_NETCORE - // needed for TreeSubset - private const String minName = "Min"; - private const String maxName = "Max"; - private const String lBoundActiveName = "lBoundActive"; - private const String uBoundActiveName = "uBoundActive"; - - private SerializationInfo siInfo; // A temporary variable which we need during deserialization. -#endif - internal const int StackAllocThreshold = 100; - - #endregion - - #region Constructors - public SortedSet() - { - this.comparer = Comparer.Default; - } - - public SortedSet(IComparer comparer) - { - if (comparer == null) - { - this.comparer = Comparer.Default; - } - else - { - this.comparer = comparer; - } - } - - - public SortedSet(IEnumerable collection) : this(collection, Comparer.Default) - { - } - - public SortedSet(IEnumerable collection, IComparer comparer) - : this(comparer) - { - if (collection == null) - { - throw new ArgumentNullException("collection"); - } - - // these are explicit type checks in the mould of HashSet. It would have worked better - // with something like an ISorted (we could make this work for SortedList.Keys etc) - SortedSet baseTreeSubSet = collection as TreeSubSet; - if (collection is SortedSet baseSortedSet && baseTreeSubSet == null && AreComparersEqual(this, baseSortedSet)) - { - // breadth first traversal to recreate nodes - if (baseSortedSet.Count == 0) - { - count = 0; - version = 0; - root = null; - return; - } - - - // pre order way to replicate nodes - Stack theirStack = new(2 * Log2(baseSortedSet.Count) + 2); - Stack myStack = new(2 * Log2(baseSortedSet.Count) + 2); - Node theirCurrent = baseSortedSet.root; - Node myCurrent = (theirCurrent != null ? new SortedSet.Node(theirCurrent.Item, theirCurrent.IsRed) : null); - root = myCurrent; - while (theirCurrent != null) - { - theirStack.Push(theirCurrent); - myStack.Push(myCurrent); - myCurrent.Left = (theirCurrent.Left != null ? new SortedSet.Node(theirCurrent.Left.Item, theirCurrent.Left.IsRed) : null); - theirCurrent = theirCurrent.Left; - myCurrent = myCurrent.Left; - } - while (theirStack.Count != 0) - { - theirCurrent = theirStack.Pop(); - myCurrent = myStack.Pop(); - Node theirRight = theirCurrent.Right; - Node myRight = null; - if (theirRight != null) - { - myRight = new SortedSet.Node(theirRight.Item, theirRight.IsRed); - } - myCurrent.Right = myRight; - - while (theirRight != null) - { - theirStack.Push(theirRight); - myStack.Push(myRight); - myRight.Left = (theirRight.Left != null ? new SortedSet.Node(theirRight.Left.Item, theirRight.Left.IsRed) : null); - theirRight = theirRight.Left; - myRight = myRight.Left; - } - } - count = baseSortedSet.count; - version = 0; - } - else - { // As it stands, you're doing an NlogN sort of the collection - List els = new(collection); - els.Sort(this.comparer); - for (int i = 1; i < els.Count; i++) - { - if (comparer.Compare(els[i], els[i - 1]) == 0) - { - els.RemoveAt(i); - i--; - } - } - root = ConstructRootFromSortedArray(els.ToArray(), 0, els.Count - 1, null); - count = els.Count; - version = 0; - } - } - - -#if !FEATURE_NETCORE - - protected SortedSet(SerializationInfo info, StreamingContext context) - { - siInfo = info; - } -#endif - #endregion - - #region Bulk Operation Helpers - private void AddAllElements(IEnumerable collection) - { - foreach (T item in collection) - { - if (!this.Contains(item)) - Add(item); - } - } - - private void RemoveAllElements(IEnumerable collection) - { - T min = this.Min; - T max = this.Max; - foreach (T item in collection) - { - if (!(comparer.Compare(item, min) < 0 || comparer.Compare(item, max) > 0) && this.Contains(item)) - this.Remove(item); - } - } - - private bool ContainsAllElements(IEnumerable collection) - { - foreach (T item in collection) - { - if (!this.Contains(item)) - { - return false; - } - } - return true; - } - - // - // Do a in order walk on tree and calls the delegate for each node. - // If the action delegate returns false, stop the walk. - // - // Return true if the entire tree has been walked. - // Otherwise returns false. - // - internal bool InOrderTreeWalk(TreeWalkPredicate action) - { - return InOrderTreeWalk(action, false); - } - - // Allows for the change in traversal direction. Reverse visits nodes in descending order - internal virtual bool InOrderTreeWalk(TreeWalkPredicate action, bool reverse) - { - if (root == null) - { - return true; - } - - // The maximum height of a red-black tree is 2*lg(n+1). - // See page 264 of "Introduction to algorithms" by Thomas H. Cormen - // note: this should be logbase2, but since the stack grows itself, we - // don't want the extra cost - Stack stack = new(2 * (int)(SortedSet.Log2(Count + 1))); - Node current = root; - while (current != null) - { - stack.Push(current); - current = (reverse ? current.Right : current.Left); - } - while (stack.Count != 0) - { - current = stack.Pop(); - if (!action(current)) - { - return false; - } - - Node node = (reverse ? current.Left : current.Right); - while (node != null) - { - stack.Push(node); - node = (reverse ? node.Right : node.Left); - } - } - return true; - } - - // - // Do a left to right breadth first walk on tree and - // calls the delegate for each node. - // If the action delegate returns false, stop the walk. - // - // Return true if the entire tree has been walked. - // Otherwise returns false. - // - internal virtual bool BreadthFirstTreeWalk(TreeWalkPredicate action) - { - if (root == null) - { - return true; - } - - List processQueue = new() - { - root - }; - Node current; - - while (processQueue.Count != 0) - { - current = processQueue[0]; - processQueue.RemoveAt(0); - if (!action(current)) - { - return false; - } - if (current.Left != null) - { - processQueue.Add(current.Left); - } - if (current.Right != null) - { - processQueue.Add(current.Right); - } - } - return true; - } - #endregion - - #region Properties - public int Count - { - get - { - VersionCheck(); - return count; - } - } - - public IComparer Comparer - { - get - { - return comparer; - } - } - - bool ICollection.IsReadOnly - { - get - { - return false; - } - } - - bool ICollection.IsSynchronized - { - get - { - return false; - } - } - - object ICollection.SyncRoot - { - get - { - if (_syncRoot == null) - { - System.Threading.Interlocked.CompareExchange(ref _syncRoot, new Object(), null); - } - return _syncRoot; - } - } - #endregion - - #region Subclass helpers - - // virtual function for subclass that needs to update count - internal virtual void VersionCheck() { } - - - // virtual function for subclass that needs to do range checks - internal virtual bool IsWithinRange(T item) - { - return true; - } - #endregion - - #region ICollection Members - /// - /// Add the value ITEM to the tree, returns true if added, false if duplicate - /// - /// item to be added - public bool Add(T item) - { - return AddIfNotPresent(item); - } - - void ICollection.Add(T item) - { - AddIfNotPresent(item); - } - - - /// - /// Adds ITEM to the tree if not already present. Returns TRUE if value was successfully added - /// or FALSE if it is a duplicate - /// - internal virtual bool AddIfNotPresent(T item) - { - if (root == null) - { // empty tree - root = new Node(item, false); - count = 1; - version++; - return true; - } - - // - // Search for a node at bottom to insert the new node. - // If we can guanratee the node we found is not a 4-node, it would be easy to do insertion. - // We split 4-nodes along the search path. - // - Node current = root; - Node parent = null; - Node grandParent = null; - Node greatGrandParent = null; - - // even if we don't actually add to the set, we may be altering its structure (by doing rotations - // and such). so update version to disable any enumerators/subsets working on it - version++; - - - int order = 0; - while (current != null) - { - order = comparer.Compare(item, current.Item); - if (order == 0) - { - // We could have changed root node to red during the search process. - // We need to set it to black before we return. - root.IsRed = false; - return false; - } - - // split a 4-node into two 2-nodes - if (Is4Node(current)) - { - Split4Node(current); - // We could have introduced two consecutive red nodes after split. Fix that by rotation. - if (IsRed(parent)) - { - InsertionBalance(current, ref parent, grandParent, greatGrandParent); - } - } - greatGrandParent = grandParent; - grandParent = parent; - parent = current; - current = (order < 0) ? current.Left : current.Right; - } - - Debug.Assert(parent != null, "Parent node cannot be null here!"); - // ready to insert the new node - Node node = new(item); - if (order > 0) - { - parent.Right = node; - } - else - { - parent.Left = node; - } - - // the new node will be red, so we will need to adjust the colors if parent node is also red - if (parent.IsRed) - { - InsertionBalance(node, ref parent, grandParent, greatGrandParent); - } - - // Root node is always black - root.IsRed = false; - ++count; - return true; - } - - /// - /// Remove the T ITEM from this SortedSet. Returns true if successfully removed. - /// - /// - /// - public bool Remove(T item) - { - return this.DoRemove(item); // hack so it can be made non-virtual - } - - internal virtual bool DoRemove(T item) - { - if (root == null) - { - return false; - } - - - // Search for a node and then find its succesor. - // Then copy the item from the succesor to the matching node and delete the successor. - // If a node doesn't have a successor, we can replace it with its left child (if not empty.) - // or delete the matching node. - // - // In top-down implementation, it is important to make sure the node to be deleted is not a 2-node. - // Following code will make sure the node on the path is not a 2 Node. - - // even if we don't actually remove from the set, we may be altering its structure (by doing rotations - // and such). so update version to disable any enumerators/subsets working on it - version++; - - Node current = root; - Node parent = null; - Node grandParent = null; - Node match = null; - Node parentOfMatch = null; - bool foundMatch = false; - while (current != null) - { - if (Is2Node(current)) - { // fix up 2-Node - if (parent == null) - { // current is root. Mark it as red - current.IsRed = true; - } - else - { - Node sibling = GetSibling(current, parent); - if (sibling.IsRed) - { - // If parent is a 3-node, flip the orientation of the red link. - // We can acheive this by a single rotation - // This case is converted to one of other cased below. - Debug.Assert(!parent.IsRed, "parent must be a black node!"); - if (parent.Right == sibling) - { - RotateLeft(parent); - } - else - { - RotateRight(parent); - } - - parent.IsRed = true; - sibling.IsRed = false; // parent's color - // sibling becomes child of grandParent or root after rotation. Update link from grandParent or root - ReplaceChildOfNodeOrRoot(grandParent, parent, sibling); - // sibling will become grandParent of current node - grandParent = sibling; - if (parent == match) - { - parentOfMatch = sibling; - } - - // update sibling, this is necessary for following processing - sibling = (parent.Left == current) ? parent.Right : parent.Left; - } - Debug.Assert(sibling != null || sibling.IsRed == false, "sibling must not be null and it must be black!"); - - if (Is2Node(sibling)) - { - Merge2Nodes(parent, current, sibling); - } - else - { - // current is a 2-node and sibling is either a 3-node or a 4-node. - // We can change the color of current to red by some rotation. - TreeRotation rotation = RotationNeeded(parent, current, sibling); - Node newGrandParent = null; - switch (rotation) - { - case TreeRotation.RightRotation: - Debug.Assert(parent.Left == sibling, "sibling must be left child of parent!"); - Debug.Assert(sibling.Left.IsRed, "Left child of sibling must be red!"); - sibling.Left.IsRed = false; - newGrandParent = RotateRight(parent); - break; - case TreeRotation.LeftRotation: - Debug.Assert(parent.Right == sibling, "sibling must be left child of parent!"); - Debug.Assert(sibling.Right.IsRed, "Right child of sibling must be red!"); - sibling.Right.IsRed = false; - newGrandParent = RotateLeft(parent); - break; - - case TreeRotation.RightLeftRotation: - Debug.Assert(parent.Right == sibling, "sibling must be left child of parent!"); - Debug.Assert(sibling.Left.IsRed, "Left child of sibling must be red!"); - newGrandParent = RotateRightLeft(parent); - break; - - case TreeRotation.LeftRightRotation: - Debug.Assert(parent.Left == sibling, "sibling must be left child of parent!"); - Debug.Assert(sibling.Right.IsRed, "Right child of sibling must be red!"); - newGrandParent = RotateLeftRight(parent); - break; - } - - newGrandParent.IsRed = parent.IsRed; - parent.IsRed = false; - current.IsRed = true; - ReplaceChildOfNodeOrRoot(grandParent, parent, newGrandParent); - if (parent == match) - { - parentOfMatch = newGrandParent; - } - grandParent = newGrandParent; - } - } - } - - // we don't need to compare any more once we found the match - int order = foundMatch ? -1 : comparer.Compare(item, current.Item); - if (order == 0) - { - // save the matching node - foundMatch = true; - match = current; - parentOfMatch = parent; - } - - grandParent = parent; - parent = current; - - if (order < 0) - { - current = current.Left; - } - else - { - current = current.Right; // continue the search in right sub tree after we find a match - } - } - - // move successor to the matching node position and replace links - if (match != null) - { - ReplaceNode(match, parentOfMatch, parent, grandParent); - --count; - } - - if (root != null) - { - root.IsRed = false; - } - return foundMatch; - } - - public virtual void Clear() - { - root = null; - count = 0; - ++version; - } - - - public virtual bool Contains(T item) - { - return FindNode(item) != null; - } - - - - - public void CopyTo(T[] array) - { - CopyTo(array, 0, Count); - } - - public void CopyTo(T[] array, int index) - { - CopyTo(array, index, Count); - } - - public void CopyTo(T[] array, int index, int count) - { - if (array == null) - { - ThrowHelper.ThrowArgumentNullException(ExceptionArgument.array); - } - - if (index < 0) - { - ThrowHelper.ThrowArgumentOutOfRangeException(ExceptionArgument.index); - } - - if (count < 0) - { - throw new ArgumentOutOfRangeException("count", SR.GetString("SR.ArgumentOutOfRange_NeedNonNegNum")); - } - - // will array, starting at arrayIndex, be able to hold elements? Note: not - // checking arrayIndex >= array.Length (consistency with list of allowing - // count of 0; subsequent check takes care of the rest) - if (index > array.Length || count > array.Length - index) - { - throw new ArgumentException(SR.GetString("SR.Arg_ArrayPlusOffTooSmall")); - } - // upper bound - count += index; - - InOrderTreeWalk(delegate (Node node) - { - if (index >= count) - { - return false; - } - else - { - array[index++] = node.Item; - return true; - } - }); - } - - void ICollection.CopyTo(Array array, int index) - { - if (array == null) - { - ThrowHelper.ThrowArgumentNullException(ExceptionArgument.array); - } - - if (array.Rank != 1) - { - ThrowHelper.ThrowArgumentException(ExceptionResource.Arg_RankMultiDimNotSupported); - } - - if (array.GetLowerBound(0) != 0) - { - ThrowHelper.ThrowArgumentException(ExceptionResource.Arg_NonZeroLowerBound); - } - - if (index < 0) - { - ThrowHelper.ThrowArgumentOutOfRangeException(ExceptionArgument.arrayIndex, ExceptionResource.ArgumentOutOfRange_NeedNonNegNum); - } - - if (array.Length - index < Count) - { - ThrowHelper.ThrowArgumentException(ExceptionResource.Arg_ArrayPlusOffTooSmall); - } - - if (array is T[] tarray) - { - CopyTo(tarray, index); - } - else - { - object[] objects = array as object[]; - if (objects == null) - { - ThrowHelper.ThrowArgumentException(ExceptionResource.Argument_InvalidArrayType); - } - - try - { - InOrderTreeWalk(delegate (Node node) { objects[index++] = node.Item; return true; }); - } - catch (ArrayTypeMismatchException) - { - ThrowHelper.ThrowArgumentException(ExceptionResource.Argument_InvalidArrayType); - } - } - } - - #endregion - - #region IEnumerable members - public Enumerator GetEnumerator() - { - return new Enumerator(this); - } - - - - - IEnumerator IEnumerable.GetEnumerator() - { - return new Enumerator(this); - } - - IEnumerator IEnumerable.GetEnumerator() - { - return new Enumerator(this); - } - #endregion - - #region Tree Specific Operations - - private static Node GetSibling(Node node, Node parent) - { - if (parent.Left == node) - { - return parent.Right; - } - return parent.Left; - } - - // After calling InsertionBalance, we need to make sure current and parent up-to-date. - // It doesn't matter if we keep grandParent and greatGrantParent up-to-date - // because we won't need to split again in the next node. - // By the time we need to split again, everything will be correctly set. - // - private void InsertionBalance(Node current, ref Node parent, Node grandParent, Node greatGrandParent) - { - Debug.Assert(grandParent != null, "Grand parent cannot be null here!"); - bool parentIsOnRight = (grandParent.Right == parent); - bool currentIsOnRight = (parent.Right == current); - - Node newChildOfGreatGrandParent; - if (parentIsOnRight == currentIsOnRight) - { // same orientation, single rotation - newChildOfGreatGrandParent = currentIsOnRight ? RotateLeft(grandParent) : RotateRight(grandParent); - } - else - { // different orientaton, double rotation - newChildOfGreatGrandParent = currentIsOnRight ? RotateLeftRight(grandParent) : RotateRightLeft(grandParent); - // current node now becomes the child of greatgrandparent - parent = greatGrandParent; - } - // grand parent will become a child of either parent of current. - grandParent.IsRed = true; - newChildOfGreatGrandParent.IsRed = false; - - ReplaceChildOfNodeOrRoot(greatGrandParent, grandParent, newChildOfGreatGrandParent); - } - - private static bool Is2Node(Node node) - { - Debug.Assert(node != null, "node cannot be null!"); - return IsBlack(node) && IsNullOrBlack(node.Left) && IsNullOrBlack(node.Right); - } - - private static bool Is4Node(Node node) - { - return IsRed(node.Left) && IsRed(node.Right); - } - - private static bool IsBlack(Node node) - { - return (node != null && !node.IsRed); - } - - private static bool IsNullOrBlack(Node node) - { - return (node == null || !node.IsRed); - } - - private static bool IsRed(Node node) - { - return (node != null && node.IsRed); - } - - private static void Merge2Nodes(Node parent, Node child1, Node child2) - { - Debug.Assert(IsRed(parent), "parent must be be red"); - // combing two 2-nodes into a 4-node - parent.IsRed = false; - child1.IsRed = true; - child2.IsRed = true; - } - - // Replace the child of a parent node. - // If the parent node is null, replace the root. - private void ReplaceChildOfNodeOrRoot(Node parent, Node child, Node newChild) - { - if (parent != null) - { - if (parent.Left == child) - { - parent.Left = newChild; - } - else - { - parent.Right = newChild; - } - } - else - { - root = newChild; - } - } - - // Replace the matching node with its succesor. - private void ReplaceNode(Node match, Node parentOfMatch, Node succesor, Node parentOfSuccesor) - { - if (succesor == match) - { // this node has no successor, should only happen if right child of matching node is null. - Debug.Assert(match.Right == null, "Right child must be null!"); - succesor = match.Left; - } - else - { - Debug.Assert(parentOfSuccesor != null, "parent of successor cannot be null!"); - Debug.Assert(succesor.Left == null, "Left child of succesor must be null!"); - Debug.Assert((succesor.Right == null && succesor.IsRed) || (succesor.Right.IsRed && !succesor.IsRed), "Succesor must be in valid state"); - if (succesor.Right != null) - { - succesor.Right.IsRed = false; - } - - if (parentOfSuccesor != match) - { // detach succesor from its parent and set its right child - parentOfSuccesor.Left = succesor.Right; - succesor.Right = match.Right; - } - - succesor.Left = match.Left; - } - - if (succesor != null) - { - succesor.IsRed = match.IsRed; - } - - ReplaceChildOfNodeOrRoot(parentOfMatch, match, succesor); - } - - internal virtual Node FindNode(T item) - { - Node current = root; - while (current != null) - { - int order = comparer.Compare(item, current.Item); - if (order == 0) - { - return current; - } - else - { - current = (order < 0) ? current.Left : current.Right; - } - } - - return null; - } - - // used for bithelpers. Note that this implementation is completely different - // from the Subset's. The two should not be mixed. This indexes as if the tree were an array. - // http://en.wikipedia.org/wiki/Binary_Tree#Methods_for_storing_binary_trees - internal virtual int InternalIndexOf(T item) - { - Node current = root; - int count = 0; - while (current != null) - { - int order = comparer.Compare(item, current.Item); - if (order == 0) - { - return count; - } - else - { - current = (order < 0) ? current.Left : current.Right; - count = (order < 0) ? (2 * count + 1) : (2 * count + 2); - } - } - return -1; - } - - - - internal Node FindRange(T from, T to) - { - return FindRange(from, to, true, true); - } - internal Node FindRange(T from, T to, bool lowerBoundActive, bool upperBoundActive) - { - Node current = root; - while (current != null) - { - if (lowerBoundActive && comparer.Compare(from, current.Item) > 0) - { - current = current.Right; - } - else - { - if (upperBoundActive && comparer.Compare(to, current.Item) < 0) - { - current = current.Left; - } - else - { - return current; - } - } - } - - return null; - } - - internal void UpdateVersion() - { - ++version; - } - - - private static Node RotateLeft(Node node) - { - Node x = node.Right; - node.Right = x.Left; - x.Left = node; - return x; - } - - private static Node RotateLeftRight(Node node) - { - Node child = node.Left; - Node grandChild = child.Right; - - node.Left = grandChild.Right; - grandChild.Right = node; - child.Right = grandChild.Left; - grandChild.Left = child; - return grandChild; - } - - private static Node RotateRight(Node node) - { - Node x = node.Left; - node.Left = x.Right; - x.Right = node; - return x; - } - - private static Node RotateRightLeft(Node node) - { - Node child = node.Right; - Node grandChild = child.Left; - - node.Right = grandChild.Left; - grandChild.Left = node; - child.Left = grandChild.Right; - grandChild.Right = child; - return grandChild; - } - /// - /// Testing counter that can track rotations - /// - - - private static TreeRotation RotationNeeded(Node parent, Node current, Node sibling) - { - Debug.Assert(IsRed(sibling.Left) || IsRed(sibling.Right), "sibling must have at least one red child"); - if (IsRed(sibling.Left)) - { - if (parent.Left == current) - { - return TreeRotation.RightLeftRotation; - } - return TreeRotation.RightRotation; - } - else - { - if (parent.Left == current) - { - return TreeRotation.LeftRotation; - } - return TreeRotation.LeftRightRotation; - } - } - - /// - /// Used for deep equality of SortedSet testing - /// - /// - public static IEqualityComparer> CreateSetComparer() - { - return new SortedSetEqualityComparer(); - } - - /// - /// Create a new set comparer for this set, where this set's members' equality is defined by the - /// memberEqualityComparer. Note that this equality comparer's definition of equality must be the - /// same as this set's Comparer's definition of equality - /// - public static IEqualityComparer> CreateSetComparer(IEqualityComparer memberEqualityComparer) - { - return new SortedSetEqualityComparer(memberEqualityComparer); - } - - - /// - /// Decides whether these sets are the same, given the comparer. If the EC's are the same, we can - /// just use SetEquals, but if they aren't then we have to manually check with the given comparer - /// - internal static bool SortedSetEquals(SortedSet set1, SortedSet set2, IComparer comparer) - { - // handle null cases first - if (set1 == null) - { - return (set2 == null); - } - else if (set2 == null) - { - // set1 != null - return false; - } - - if (AreComparersEqual(set1, set2)) - { - if (set1.Count != set2.Count) - return false; - - return set1.SetEquals(set2); - } - else - { - bool found = false; - foreach (T item1 in set1) - { - found = false; - foreach (T item2 in set2) - { - if (comparer.Compare(item1, item2) == 0) - { - found = true; - break; - } - } - if (!found) - return false; - } - return true; - } - } - - - // This is a little frustrating because we can't support more sorted structures - private static bool AreComparersEqual(SortedSet set1, SortedSet set2) - { - return set1.Comparer.Equals(set2.Comparer); - } - - - private static void Split4Node(Node node) - { - node.IsRed = true; - node.Left.IsRed = false; - node.Right.IsRed = false; - } - - /// - /// Copies this to an array. Used for DebugView - /// - /// - internal T[] ToArray() - { - T[] newArray = new T[Count]; - CopyTo(newArray); - return newArray; - } - - - #endregion - - #region ISet Members - - /// - /// Transform this set into its union with the IEnumerable OTHER - /// Attempts to insert each element and rejects it if it exists. - /// NOTE: The caller object is important as UnionWith uses the Comparator - /// associated with THIS to check equality - /// Throws ArgumentNullException if OTHER is null - /// - /// - public void UnionWith(IEnumerable other) - { - if (other == null) - { - throw new ArgumentNullException("other"); - } - - SortedSet s = other as SortedSet; - TreeSubSet t = this as TreeSubSet; - - if (t != null) - VersionCheck(); - - if (s != null && t == null && this.count == 0) - { - SortedSet dummy = new(s, this.comparer); - this.root = dummy.root; - this.count = dummy.count; - this.version++; - return; - } - - - if (s != null && t == null && AreComparersEqual(this, s) && (s.Count > this.Count / 2)) - { // this actually hurts if N is much greater than M the /2 is arbitrary - // first do a merge sort to an array. - T[] merged = new T[s.Count + this.Count]; - int c = 0; - Enumerator mine = this.GetEnumerator(); - Enumerator theirs = s.GetEnumerator(); - bool mineEnded = !mine.MoveNext(), theirsEnded = !theirs.MoveNext(); - while (!mineEnded && !theirsEnded) - { - int comp = Comparer.Compare(mine.Current, theirs.Current); - if (comp < 0) - { - merged[c++] = mine.Current; - mineEnded = !mine.MoveNext(); - } - else if (comp == 0) - { - merged[c++] = theirs.Current; - mineEnded = !mine.MoveNext(); - theirsEnded = !theirs.MoveNext(); - } - else - { - merged[c++] = theirs.Current; - theirsEnded = !theirs.MoveNext(); - } - } - - if (!mineEnded || !theirsEnded) - { - Enumerator remaining = (mineEnded ? theirs : mine); - do - { - merged[c++] = remaining.Current; - } while (remaining.MoveNext()); - } - - // now merged has all c elements - - // safe to gc the root, we have all the elements - root = null; - - - root = SortedSet.ConstructRootFromSortedArray(merged, 0, c - 1, null); - count = c; - version++; - } - else - { - AddAllElements(other); - } - } - - - private static Node ConstructRootFromSortedArray(T[] arr, int startIndex, int endIndex, Node redNode) - { - // what does this do? - // you're given a sorted array... say 1 2 3 4 5 6 - // 2 cases: - // If there are odd # of elements, pick the middle element (in this case 4), and compute - // its left and right branches - // If there are even # of elements, pick the left middle element, save the right middle element - // and call the function on the rest - // 1 2 3 4 5 6 -> pick 3, save 4 and call the fn on 1,2 and 5,6 - // now add 4 as a red node to the lowest element on the right branch - // 3 3 - // 1 5 -> 1 5 - // 2 6 2 4 6 - // As we're adding to the leftmost of the right branch, nesting will not hurt the red-black properties - // Leaf nodes are red if they have no sibling (if there are 2 nodes or if a node trickles - // down to the bottom - - - // the iterative way to do this ends up wasting more space than it saves in stack frames (at - // least in what i tried) - // so we're doing this recursively - // base cases are described below - int size = endIndex - startIndex + 1; - if (size == 0) - { - return null; - } - Node root = null; - if (size == 1) - { - root = new Node(arr[startIndex], false); - if (redNode != null) - { - root.Left = redNode; - } - } - else if (size == 2) - { - root = new Node(arr[startIndex], false) - { - Right = new Node(arr[endIndex], false) - }; - root.Right.IsRed = true; - if (redNode != null) - { - root.Left = redNode; - } - } - else if (size == 3) - { - root = new Node(arr[startIndex + 1], false) - { - Left = new Node(arr[startIndex], false), - Right = new Node(arr[endIndex], false) - }; - if (redNode != null) - { - root.Left.Left = redNode; - } - } - else - { - int midpt = ((startIndex + endIndex) / 2); - root = new Node(arr[midpt], false) - { - Left = ConstructRootFromSortedArray(arr, startIndex, midpt - 1, redNode) - }; - if (size % 2 == 0) - { - root.Right = ConstructRootFromSortedArray(arr, midpt + 2, endIndex, new Node(arr[midpt + 1], true)); - } - else - { - root.Right = ConstructRootFromSortedArray(arr, midpt + 1, endIndex, null); - } - } - return root; - } - - - /// - /// Transform this set into its intersection with the IEnumerable OTHER - /// NOTE: The caller object is important as IntersectionWith uses the - /// comparator associated with THIS to check equality - /// Throws ArgumentNullException if OTHER is null - /// - /// - public virtual void IntersectWith(IEnumerable other) - { - if (other == null) - { - throw new ArgumentNullException("other"); - } - - if (Count == 0) - return; - - // HashSet optimizations can't be done until equality comparers and comparers are related - - // Technically, this would work as well with an ISorted - TreeSubSet t = this as TreeSubSet; - if (t != null) - VersionCheck(); - // only let this happen if i am also a SortedSet, not a SubSet - if (other is SortedSet s && t == null && AreComparersEqual(this, s)) - { - // first do a merge sort to an array. - T[] merged = new T[this.Count]; - int c = 0; - Enumerator mine = this.GetEnumerator(); - Enumerator theirs = s.GetEnumerator(); - bool mineEnded = !mine.MoveNext(), theirsEnded = !theirs.MoveNext(); - T max = Max; - T min = Min; - - while (!mineEnded && !theirsEnded && Comparer.Compare(theirs.Current, max) <= 0) - { - int comp = Comparer.Compare(mine.Current, theirs.Current); - if (comp < 0) - { - mineEnded = !mine.MoveNext(); - } - else if (comp == 0) - { - merged[c++] = theirs.Current; - mineEnded = !mine.MoveNext(); - theirsEnded = !theirs.MoveNext(); - } - else - { - theirsEnded = !theirs.MoveNext(); - } - } - - // now merged has all c elements - - // safe to gc the root, we have all the elements - root = null; - - root = SortedSet.ConstructRootFromSortedArray(merged, 0, c - 1, null); - count = c; - version++; - } - else - { - IntersectWithEnumerable(other); - } - } - - internal virtual void IntersectWithEnumerable(IEnumerable other) - { - // - List toSave = new(this.Count); - foreach (T item in other) - { - if (this.Contains(item)) - { - toSave.Add(item); - this.Remove(item); - } - } - this.Clear(); - AddAllElements(toSave); - } - - - - /// - /// Transform this set into its complement with the IEnumerable OTHER - /// NOTE: The caller object is important as ExceptWith uses the - /// comparator associated with THIS to check equality - /// Throws ArgumentNullException if OTHER is null - /// - /// - public void ExceptWith(IEnumerable other) - { - if (other == null) - { - throw new ArgumentNullException("other"); - } - - if (count == 0) - return; - - if (other == this) - { - this.Clear(); - return; - } - - - if (other is SortedSet asSorted && AreComparersEqual(this, asSorted)) - { - // outside range, no point doing anything - if (!(comparer.Compare(asSorted.Max, this.Min) < 0 || comparer.Compare(asSorted.Min, this.Max) > 0)) - { - T min = this.Min; - T max = this.Max; - foreach (T item in other) - { - if (comparer.Compare(item, min) < 0) - continue; - if (comparer.Compare(item, max) > 0) - break; - Remove(item); - } - } - } - else - { - RemoveAllElements(other); - } - } - - /// - /// Transform this set so it contains elements in THIS or OTHER but not both - /// NOTE: The caller object is important as SymmetricExceptWith uses the - /// comparator associated with THIS to check equality - /// Throws ArgumentNullException if OTHER is null - /// - /// - public void SymmetricExceptWith(IEnumerable other) - { - if (other == null) - { - throw new ArgumentNullException("other"); - } - - if (this.Count == 0) - { - this.UnionWith(other); - return; - } - - if (other == this) - { - this.Clear(); - return; - } - - - -#if USING_HASH_SET - HashSet asHash = other as HashSet; -#endif - if (other is SortedSet asSorted && AreComparersEqual(this, asSorted)) - { - SymmetricExceptWithSameEC(asSorted); - } -#if USING_HASH_SET - else if (asHash != null && this.comparer.Equals(Comparer.Default) && asHash.Comparer.Equals(EqualityComparer.Default)) { - SymmetricExceptWithSameEC(asHash); - } -#endif - else - { - // need perf improvement on this - T[] elements = (new List(other)).ToArray(); - Array.Sort(elements, this.Comparer); - SymmetricExceptWithSameEC(elements); - } - } - - // OTHER must be a set - internal void SymmetricExceptWithSameEC(ISet other) - { - foreach (T item in other) - { - // yes, it is classier to say - // if (!this.Remove(item))this.Add(item); - // but this ends up saving on rotations - if (this.Contains(item)) - { - this.Remove(item); - } - else - { - this.Add(item); - } - } - } - - // OTHER must be a sorted array - internal void SymmetricExceptWithSameEC(T[] other) - { - if (other.Length == 0) - { - return; - } - T last = other[0]; - for (int i = 0; i < other.Length; i++) - { - while (i < other.Length && i != 0 && comparer.Compare(other[i], last) == 0) - i++; - if (i >= other.Length) - break; - if (this.Contains(other[i])) - { - this.Remove(other[i]); - } - else - { - this.Add(other[i]); - } - last = other[i]; - } - } - - - /// - /// Checks whether this Tree is a subset of the IEnumerable other - /// - /// - /// - [System.Security.SecuritySafeCritical] - public bool IsSubsetOf(IEnumerable other) - { - if (other == null) - { - throw new ArgumentNullException("other"); - } - - if (Count == 0) - return true; - - - if (other is SortedSet asSorted && AreComparersEqual(this, asSorted)) - { - if (this.Count > asSorted.Count) - return false; - return IsSubsetOfSortedSetWithSameEC(asSorted); - } - else - { - // worst case: mark every element in my set and see if i've counted all - // O(MlogN) - - ElementCount result = CheckUniqueAndUnfoundElements(other, false); - return (result.uniqueCount == Count && result.unfoundCount >= 0); - } - } - - private bool IsSubsetOfSortedSetWithSameEC(SortedSet asSorted) - { - SortedSet prunedOther = asSorted.GetViewBetween(this.Min, this.Max); - foreach (T item in this) - { - if (!prunedOther.Contains(item)) - return false; - } - return true; - } - - - /// - /// Checks whether this Tree is a proper subset of the IEnumerable other - /// - /// - /// - [System.Security.SecuritySafeCritical] - public bool IsProperSubsetOf(IEnumerable other) - { - if (other == null) - { - throw new ArgumentNullException("other"); - } - - if ((other as ICollection) != null) - { - if (Count == 0) - return (other as ICollection).Count > 0; - } - - -#if USING_HASH_SET - // do it one way for HashSets - HashSet asHash = other as HashSet; - if (asHash != null && comparer.Equals(Comparer.Default) && asHash.Comparer.Equals(EqualityComparer.Default)) { - return asHash.IsProperSupersetOf(this); - } -#endif - // another for sorted sets with the same comparer - if (other is SortedSet asSorted && AreComparersEqual(this, asSorted)) - { - if (this.Count >= asSorted.Count) - return false; - return IsSubsetOfSortedSetWithSameEC(asSorted); - } - - - // worst case: mark every element in my set and see if i've counted all - // O(MlogN). - ElementCount result = CheckUniqueAndUnfoundElements(other, false); - return (result.uniqueCount == Count && result.unfoundCount > 0); - } - - - /// - /// Checks whether this Tree is a super set of the IEnumerable other - /// - /// - /// - public bool IsSupersetOf(IEnumerable other) - { - if (other == null) - { - throw new ArgumentNullException("other"); - } - - if ((other as ICollection) != null && (other as ICollection).Count == 0) - return true; - - // do it one way for HashSets -#if USING_HASH_SET - HashSet asHash = other as HashSet; - if (asHash != null && comparer.Equals(Comparer.Default) && asHash.Comparer.Equals(EqualityComparer.Default)) { - return asHash.IsSubsetOf(this); - } -#endif - // another for sorted sets with the same comparer - if (other is SortedSet asSorted && AreComparersEqual(this, asSorted)) - { - if (this.Count < asSorted.Count) - return false; - SortedSet pruned = GetViewBetween(asSorted.Min, asSorted.Max); - foreach (T item in asSorted) - { - if (!pruned.Contains(item)) - return false; - } - return true; - } - // and a third for everything else - return ContainsAllElements(other); - } - - /// - /// Checks whether this Tree is a proper super set of the IEnumerable other - /// - /// - /// - [System.Security.SecuritySafeCritical] - public bool IsProperSupersetOf(IEnumerable other) - { - if (other == null) - { - throw new ArgumentNullException("other"); - } - - if (Count == 0) - return false; - - if ((other as ICollection) != null && (other as ICollection).Count == 0) - return true; - -#if USING_HASH_SET - // do it one way for HashSets - - HashSet asHash = other as HashSet; - if (asHash != null && comparer.Equals(Comparer.Default) && asHash.Comparer.Equals(EqualityComparer.Default)) { - return asHash.IsProperSubsetOf(this); - } -#endif - // another way for sorted sets - if (other is SortedSet asSorted && AreComparersEqual(asSorted, this)) - { - if (asSorted.Count >= this.Count) - return false; - SortedSet pruned = GetViewBetween(asSorted.Min, asSorted.Max); - foreach (T item in asSorted) - { - if (!pruned.Contains(item)) - return false; - } - return true; - } - - - // worst case: mark every element in my set and see if i've counted all - // O(MlogN) - // slight optimization, put it into a HashSet and then check can do it in O(N+M) - // but slower in better cases + wastes space - ElementCount result = CheckUniqueAndUnfoundElements(other, true); - return (result.uniqueCount < Count && result.unfoundCount == 0); - } - - - - /// - /// Checks whether this Tree has all elements in common with IEnumerable other - /// - /// - /// - [System.Security.SecuritySafeCritical] - public bool SetEquals(IEnumerable other) - { - if (other == null) - { - throw new ArgumentNullException("other"); - } - -#if USING_HASH_SET - HashSet asHash = other as HashSet; - if (asHash != null && comparer.Equals(Comparer.Default) && asHash.Comparer.Equals(EqualityComparer.Default)) { - return asHash.SetEquals(this); - } -#endif - if (other is SortedSet asSorted && AreComparersEqual(this, asSorted)) - { - IEnumerator mine = this.GetEnumerator(); - IEnumerator theirs = asSorted.GetEnumerator(); - bool mineEnded = !mine.MoveNext(); - bool theirsEnded = !theirs.MoveNext(); - while (!mineEnded && !theirsEnded) - { - if (Comparer.Compare(mine.Current, theirs.Current) != 0) - { - return false; - } - mineEnded = !mine.MoveNext(); - theirsEnded = !theirs.MoveNext(); - } - return mineEnded && theirsEnded; - } - - // worst case: mark every element in my set and see if i've counted all - // O(N) by size of other - ElementCount result = CheckUniqueAndUnfoundElements(other, true); - return (result.uniqueCount == Count && result.unfoundCount == 0); - } - - - - /// - /// Checks whether this Tree has any elements in common with IEnumerable other - /// - /// - /// - public bool Overlaps(IEnumerable other) - { - if (other == null) - { - throw new ArgumentNullException("other"); - } - - if (this.Count == 0) - return false; - - if ((other as ICollection != null) && (other as ICollection).Count == 0) - return false; - - if (other is SortedSet asSorted && AreComparersEqual(this, asSorted) && (comparer.Compare(Min, asSorted.Max) > 0 || comparer.Compare(Max, asSorted.Min) < 0)) - { - return false; - } -#if USING_HASH_SET - HashSet asHash = other as HashSet; - if (asHash != null && comparer.Equals(Comparer.Default) && asHash.Comparer.Equals(EqualityComparer.Default)) { - return asHash.Overlaps(this); - } -#endif - foreach (T item in other) - { - if (this.Contains(item)) - { - return true; - } - } - return false; - } - - /// - /// This works similar to HashSet's CheckUniqueAndUnfound (description below), except that the bit - /// array maps differently than in the HashSet. We can only use this for the bulk boolean checks. - /// - /// Determines counts that can be used to determine equality, subset, and superset. This - /// is only used when other is an IEnumerable and not a HashSet. If other is a HashSet - /// these properties can be checked faster without use of marking because we can assume - /// other has no duplicates. - /// - /// The following count checks are performed by callers: - /// 1. Equals: checks if unfoundCount = 0 and uniqueFoundCount = Count; i.e. everything - /// in other is in this and everything in this is in other - /// 2. Subset: checks if unfoundCount >= 0 and uniqueFoundCount = Count; i.e. other may - /// have elements not in this and everything in this is in other - /// 3. Proper subset: checks if unfoundCount > 0 and uniqueFoundCount = Count; i.e - /// other must have at least one element not in this and everything in this is in other - /// 4. Proper superset: checks if unfound count = 0 and uniqueFoundCount strictly less - /// than Count; i.e. everything in other was in this and this had at least one element - /// not contained in other. - /// - /// An earlier implementation used delegates to perform these checks rather than returning - /// an ElementCount struct; however this was changed due to the perf overhead of delegates. - /// - /// - /// Allows us to finish faster for equals and proper superset - /// because unfoundCount must be 0. - /// - // - // - // - // - // - // - [System.Security.SecurityCritical] - private unsafe ElementCount CheckUniqueAndUnfoundElements(IEnumerable other, bool returnIfUnfound) - { - ElementCount result; - - // need special case in case this has no elements. - if (Count == 0) - { - int numElementsInOther = 0; - foreach (T item in other) - { - numElementsInOther++; - // break right away, all we want to know is whether other has 0 or 1 elements - break; - } - result.uniqueCount = 0; - result.unfoundCount = numElementsInOther; - return result; - } - - - int originalLastIndex = Count; - int intArrayLength = BitHelper.ToIntArrayLength(originalLastIndex); - - BitHelper bitHelper; - if (intArrayLength <= StackAllocThreshold) - { - int* bitArrayPtr = stackalloc int[intArrayLength]; - bitHelper = new BitHelper(bitArrayPtr, intArrayLength); - } - else - { - int[] bitArray = new int[intArrayLength]; - bitHelper = new BitHelper(bitArray, intArrayLength); - } - - // count of items in other not found in this - int unfoundCount = 0; - // count of unique items in other found in this - int uniqueFoundCount = 0; - - foreach (T item in other) - { - int index = InternalIndexOf(item); - if (index >= 0) - { - if (!bitHelper.IsMarked(index)) - { - // item hasn't been seen yet - bitHelper.MarkBit(index); - uniqueFoundCount++; - } - } - else - { - unfoundCount++; - if (returnIfUnfound) - { - break; - } - } - } - - result.uniqueCount = uniqueFoundCount; - result.unfoundCount = unfoundCount; - return result; - } - public int RemoveWhere(Predicate match) - { - if (match == null) - { - throw new ArgumentNullException("match"); - } - List matches = new(this.Count); - - BreadthFirstTreeWalk(delegate (Node n) - { - if (match(n.Item)) - { - matches.Add(n.Item); - } - return true; - }); - // reverse breadth first to (try to) incur low cost - int actuallyRemoved = 0; - for (int i = matches.Count - 1; i >= 0; i--) - { - if (this.Remove(matches[i])) - { - actuallyRemoved++; - } - } - - return actuallyRemoved; - } - - - #endregion - - #region ISorted Members - - - public T Min - { - get - { - T ret = default; - InOrderTreeWalk(delegate (SortedSet.Node n) { ret = n.Item; return false; }); - return ret; - } - } - - public T Max - { - get - { - T ret = default; - InOrderTreeWalk(delegate (SortedSet.Node n) { ret = n.Item; return false; }, true); - return ret; - } - } - - public IEnumerable Reverse() - { - Enumerator e = new(this, true); - while (e.MoveNext()) - { - yield return e.Current; - } - } - - - /// - /// Returns a subset of this tree ranging from values lBound to uBound - /// Any changes made to the subset reflect in the actual tree - /// - /// Lowest Value allowed in the subset - /// Highest Value allowed in the subset - public virtual SortedSet GetViewBetween(T lowerValue, T upperValue) - { - if (Comparer.Compare(lowerValue, upperValue) > 0) - { - throw new ArgumentException("lowerBound is greater than upperBound"); - } - return new TreeSubSet(this, lowerValue, upperValue, true, true); - } - -#if DEBUG - - /// - /// debug status to be checked whenever any operation is called - /// - /// - internal virtual bool VersionUpToDate() - { - return true; - } -#endif - - - /// - /// This class represents a subset view into the tree. Any changes to this view - /// are reflected in the actual tree. Uses the Comparator of the underlying tree. - /// - /// -#if !FEATURE_NETCORE - [Serializable] - internal sealed class TreeSubSet : SortedSet, ISerializable, IDeserializationCallback - { -#else - internal sealed class TreeSubSet : SortedSet { -#endif - SortedSet underlying; - T min, max; - // these exist for unbounded collections - // for instance, you could allow this subset to be defined for i>10. The set will throw if - // anything <=10 is added, but there is no upperbound. These features Head(), Tail(), were punted - // in the spec, and are not available, but the framework is there to make them available at some point. - bool lBoundActive, uBoundActive; - // used to see if the count is out of date - - -#if DEBUG - internal override bool VersionUpToDate() - { - return (this.version == underlying.version); - } -#endif - - public TreeSubSet(SortedSet Underlying, T Min, T Max, bool lowerBoundActive, bool upperBoundActive) - : base(Underlying.Comparer) - { - underlying = Underlying; - min = Min; - max = Max; - lBoundActive = lowerBoundActive; - uBoundActive = upperBoundActive; - root = underlying.FindRange(min, max, lBoundActive, uBoundActive); // root is first element within range - count = 0; - version = -1; - VersionCheckImpl(); - } - -#if !FEATURE_NETCORE - /// - /// For serialization and deserialization - /// - private TreeSubSet() - { - comparer = null; - } - - - [SuppressMessage("Microsoft.Usage", "CA2236:CallBaseClassMethodsOnISerializableTypes", Justification = "special case TreeSubSet serialization")] - private TreeSubSet(SerializationInfo info, StreamingContext context) - { - siInfo = info; - OnDeserializationImpl(info); - } -#endif // !FEATURE_NETCORE - - /// - /// Additions to this tree need to be added to the underlying tree as well - /// - - internal override bool AddIfNotPresent(T item) - { - if (!IsWithinRange(item)) - { - ThrowHelper.ThrowArgumentOutOfRangeException(ExceptionArgument.collection); - } - - bool ret = underlying.AddIfNotPresent(item); - VersionCheck(); -#if DEBUG - Debug.Assert(this.VersionUpToDate() && this.root == this.underlying.FindRange(min, max)); -#endif - - return ret; - } - - - public override bool Contains(T item) - { - VersionCheck(); -#if DEBUG - Debug.Assert(this.VersionUpToDate() && this.root == this.underlying.FindRange(min, max)); -#endif - return base.Contains(item); - } - - internal override bool DoRemove(T item) - { // todo: uppercase this and others - if (!IsWithinRange(item)) - { - return false; - } - - bool ret = underlying.Remove(item); - VersionCheck(); -#if DEBUG - Debug.Assert(this.VersionUpToDate() && this.root == this.underlying.FindRange(min, max)); -#endif - return ret; - } - - public override void Clear() - { - if (count == 0) - { - return; - } - - List toRemove = new(); - BreadthFirstTreeWalk(delegate (Node n) { toRemove.Add(n.Item); return true; }); - while (toRemove.Count != 0) - { - underlying.Remove(toRemove[^1]); - toRemove.RemoveAt(toRemove.Count - 1); - } - root = null; - count = 0; - version = underlying.version; - } - - - internal override bool IsWithinRange(T item) - { - int comp = (lBoundActive ? Comparer.Compare(min, item) : -1); - if (comp > 0) - { - return false; - } - comp = (uBoundActive ? Comparer.Compare(max, item) : 1); - if (comp < 0) - { - return false; - } - return true; - } - - internal override bool InOrderTreeWalk(TreeWalkPredicate action, Boolean reverse) - { - VersionCheck(); - - if (root == null) - { - return true; - } - - // The maximum height of a red-black tree is 2*lg(n+1). - // See page 264 of "Introduction to algorithms" by Thomas H. Cormen - Stack stack = new(2 * (int)SortedSet.Log2(count + 1)); // this is not exactly right if count is out of date, but the stack can grow - Node current = root; - while (current != null) - { - if (IsWithinRange(current.Item)) - { - stack.Push(current); - current = (reverse ? current.Right : current.Left); - } - else if (lBoundActive && Comparer.Compare(min, current.Item) > 0) - { - current = current.Right; - } - else - { - current = current.Left; - } - } - - while (stack.Count != 0) - { - current = stack.Pop(); - if (!action(current)) - { - return false; - } - - Node node = (reverse ? current.Left : current.Right); - while (node != null) - { - if (IsWithinRange(node.Item)) - { - stack.Push(node); - node = (reverse ? node.Right : node.Left); - } - else if (lBoundActive && Comparer.Compare(min, node.Item) > 0) - { - node = node.Right; - } - else - { - node = node.Left; - } - } - } - return true; - } - - internal override bool BreadthFirstTreeWalk(TreeWalkPredicate action) - { - VersionCheck(); - - if (root == null) - { - return true; - } - - List processQueue = new() - { - root - }; - Node current; - - while (processQueue.Count != 0) - { - current = processQueue[0]; - processQueue.RemoveAt(0); - if (IsWithinRange(current.Item) && !action(current)) - { - return false; - } - if (current.Left != null && (!lBoundActive || Comparer.Compare(min, current.Item) < 0)) - { - processQueue.Add(current.Left); - } - if (current.Right != null && (!uBoundActive || Comparer.Compare(max, current.Item) > 0)) - { - processQueue.Add(current.Right); - } - } - return true; - } - - internal override SortedSet.Node FindNode(T item) - { - if (!IsWithinRange(item)) - { - return null; - } - VersionCheck(); -#if DEBUG - Debug.Assert(this.VersionUpToDate() && this.root == this.underlying.FindRange(min, max)); -#endif - return base.FindNode(item); - } - - // this does indexing in an inefficient way compared to the actual sortedset, but it saves a - // lot of space - internal override int InternalIndexOf(T item) - { - int count = -1; - foreach (T i in this) - { - count++; - if (Comparer.Compare(item, i) == 0) - return count; - } -#if DEBUG - Debug.Assert(this.VersionUpToDate() && this.root == this.underlying.FindRange(min, max)); -#endif - return -1; - } - /// - /// checks whether this subset is out of date. updates if necessary. - /// - internal override void VersionCheck() - { - VersionCheckImpl(); - } - - private void VersionCheckImpl() - { - Debug.Assert(underlying != null, "Underlying set no longer exists"); - if (this.version != underlying.version) - { - this.root = underlying.FindRange(min, max, lBoundActive, uBoundActive); - this.version = underlying.version; - count = 0; - InOrderTreeWalk(delegate (Node n) { count++; return true; }); - } - } - - - - // This passes functionality down to the underlying tree, clipping edges if necessary - // There's nothing gained by having a nested subset. May as well draw it from the base - // Cannot increase the bounds of the subset, can only decrease it - public override SortedSet GetViewBetween(T lowerValue, T upperValue) - { - if (lBoundActive && Comparer.Compare(min, lowerValue) > 0) - { - // lBound = min; - throw new ArgumentOutOfRangeException("lowerValue"); - } - if (uBoundActive && Comparer.Compare(max, upperValue) < 0) - { - // uBound = max; - throw new ArgumentOutOfRangeException("upperValue"); - } - TreeSubSet ret = (TreeSubSet)underlying.GetViewBetween(lowerValue, upperValue); - return ret; - } - - internal override void IntersectWithEnumerable(IEnumerable other) - { - List toSave = new(this.Count); - foreach (T item in other) - { - if (this.Contains(item)) - { - toSave.Add(item); - this.Remove(item); - } - } - this.Clear(); - this.AddAllElements(toSave); -#if DEBUG - Debug.Assert(this.VersionUpToDate() && this.root == this.underlying.FindRange(min, max)); -#endif - } - -#if !FEATURE_NETCORE - void ISerializable.GetObjectData(SerializationInfo info, StreamingContext context) - { - GetObjectData(info, context); - } - - protected override void GetObjectData(SerializationInfo info, StreamingContext context) - { - if (info == null) - { - ThrowHelper.ThrowArgumentNullException(ExceptionArgument.info); - } - info.AddValue(maxName, max, typeof(T)); - info.AddValue(minName, min, typeof(T)); - info.AddValue(lBoundActiveName, lBoundActive); - info.AddValue(uBoundActiveName, uBoundActive); - base.GetObjectData(info, context); - } - - void IDeserializationCallback.OnDeserialization(Object sender) - { - // don't do anything here as its already been done by the constructor - // OnDeserialization(sender); - } - - protected override void OnDeserialization(Object sender) - { - OnDeserializationImpl(sender); - } - - private void OnDeserializationImpl(Object sender) - { - if (siInfo == null) - { - ThrowHelper.ThrowSerializationException(ExceptionResource.Serialization_InvalidOnDeser); - } - - comparer = (IComparer)siInfo.GetValue(ComparerName, typeof(IComparer)); - int savedCount = siInfo.GetInt32(CountName); - max = (T)siInfo.GetValue(maxName, typeof(T)); - min = (T)siInfo.GetValue(minName, typeof(T)); - lBoundActive = siInfo.GetBoolean(lBoundActiveName); - uBoundActive = siInfo.GetBoolean(uBoundActiveName); - underlying = new SortedSet(); - - if (savedCount != 0) - { - T[] items = (T[])siInfo.GetValue(ItemsName, typeof(T[])); - - if (items == null) - { - ThrowHelper.ThrowSerializationException(ExceptionResource.Serialization_MissingValues); - } - - for (int i = 0; i < items.Length; i++) - { - underlying.Add(items[i]); - } - } - underlying.version = siInfo.GetInt32(VersionName); - count = underlying.count; - version = underlying.version - 1; - VersionCheck(); // this should update the count to be right and update root to be right - - if (count != savedCount) - { - ThrowHelper.ThrowSerializationException(ExceptionResource.Serialization_MismatchedCount); - } - siInfo = null; - } -#endif // !FEATURE_NETCORE - } - - - #endregion - - #region Serialization methods - -#if !FEATURE_NETCORE - // LinkDemand here is unnecessary as this is a methodimpl and linkdemand from the interface should suffice - void ISerializable.GetObjectData(SerializationInfo info, StreamingContext context) - { - GetObjectData(info, context); - } - - protected virtual void GetObjectData(SerializationInfo info, StreamingContext context) - { - if (info == null) - { - ThrowHelper.ThrowArgumentNullException(ExceptionArgument.info); - } - - info.AddValue(CountName, count); // This is the length of the bucket array. - info.AddValue(ComparerName, comparer, typeof(IComparer)); - info.AddValue(VersionName, version); - - if (root != null) - { - T[] items = new T[Count]; - CopyTo(items, 0); - info.AddValue(ItemsName, items, typeof(T[])); - } - } - - void IDeserializationCallback.OnDeserialization(Object sender) - { - OnDeserialization(sender); - } - - protected virtual void OnDeserialization(Object sender) - { - if (comparer != null) - { - return; // Somebody had a dependency on this class and fixed us up before the ObjectManager got to it. - } - - if (siInfo == null) - { - ThrowHelper.ThrowSerializationException(ExceptionResource.Serialization_InvalidOnDeser); - } - - comparer = (IComparer)siInfo.GetValue(ComparerName, typeof(IComparer)); - int savedCount = siInfo.GetInt32(CountName); - - if (savedCount != 0) - { - T[] items = (T[])siInfo.GetValue(ItemsName, typeof(T[])); - - if (items == null) - { - ThrowHelper.ThrowSerializationException(ExceptionResource.Serialization_MissingValues); - } - - for (int i = 0; i < items.Length; i++) - { - Add(items[i]); - } - } - - version = siInfo.GetInt32(VersionName); - if (count != savedCount) - { - ThrowHelper.ThrowSerializationException(ExceptionResource.Serialization_MismatchedCount); - } - siInfo = null; - } -#endif // !FEATURE_NETCORE - #endregion - - #region Helper Classes - internal class Node - { - public bool IsRed; - public T Item; - public Node Left; - public Node Right; - - public Node(T item) - { - // The default color will be red, we never need to create a black node directly. - this.Item = item; - IsRed = true; - } - - public Node(T item, bool isRed) - { - // The default color will be red, we never need to create a black node directly. - this.Item = item; - this.IsRed = isRed; - } - } - - [SuppressMessage("Microsoft.Performance", "CA1815:OverrideEqualsAndOperatorEqualsOnValueTypes", Justification = "not an expected scenario")] -#if !FEATURE_NETCORE - [Serializable] - public struct Enumerator : IEnumerator, IEnumerator, ISerializable, IDeserializationCallback - { -#else - public struct Enumerator : IEnumerator, IEnumerator { -#endif - private SortedSet tree; - private int version; - - - private Stack.Node> stack; - private SortedSet.Node current; - static readonly SortedSet.Node dummyNode = new(default); - - private bool reverse; - -#if !FEATURE_NETCORE - private readonly SerializationInfo siInfo; -#endif - internal Enumerator(SortedSet set) - { - tree = set; - // this is a hack to make sure that the underlying subset has not been changed since - // - tree.VersionCheck(); - - version = tree.version; - - // 2lg(n + 1) is the maximum height - stack = new Stack.Node>(2 * (int)SortedSet.Log2(set.Count + 1)); - current = null; - reverse = false; -#if !FEATURE_NETCORE - siInfo = null; -#endif - Intialize(); - } - - internal Enumerator(SortedSet set, bool reverse) - { - tree = set; - // this is a hack to make sure that the underlying subset has not been changed since - // - tree.VersionCheck(); - version = tree.version; - - // 2lg(n + 1) is the maximum height - stack = new Stack.Node>(2 * (int)SortedSet.Log2(set.Count + 1)); - current = null; - this.reverse = reverse; -#if !FEATURE_NETCORE - siInfo = null; -#endif - Intialize(); - } - -#if !FEATURE_NETCORE - private Enumerator(SerializationInfo info, StreamingContext context) - { - tree = null; - version = -1; - current = null; - reverse = false; - stack = null; - this.siInfo = info; - } - - void ISerializable.GetObjectData(SerializationInfo info, StreamingContext context) - { - GetObjectData(info, context); - } - - private void GetObjectData(SerializationInfo info, StreamingContext context) - { - if (info == null) - { - ThrowHelper.ThrowArgumentNullException(ExceptionArgument.info); - } - info.AddValue(TreeName, tree, typeof(SortedSet)); - info.AddValue(EnumVersionName, version); - info.AddValue(ReverseName, reverse); - info.AddValue(EnumStartName, !NotStartedOrEnded); - info.AddValue(NodeValueName, (current == null ? dummyNode.Item : current.Item), typeof(T)); - } - - void IDeserializationCallback.OnDeserialization(Object sender) - { - OnDeserialization(sender); - } - - private void OnDeserialization(Object sender) - { - if (siInfo == null) - { - ThrowHelper.ThrowSerializationException(ExceptionResource.Serialization_InvalidOnDeser); - } - - tree = (SortedSet)siInfo.GetValue(TreeName, typeof(SortedSet)); - version = siInfo.GetInt32(EnumVersionName); - reverse = siInfo.GetBoolean(ReverseName); - bool EnumStarted = siInfo.GetBoolean(EnumStartName); - stack = new Stack.Node>(2 * (int)SortedSet.Log2(tree.Count + 1)); - current = null; - if (EnumStarted) - { - T item = (T)siInfo.GetValue(NodeValueName, typeof(T)); - Intialize(); - // go until it reaches the value we want - while (this.MoveNext()) - { - if (tree.Comparer.Compare(this.Current, item) == 0) - break; - } - } - } -#endif // !FEATURE_NETCORE - - - private void Intialize() - { - current = null; - SortedSet.Node node = tree.root; - Node next = null, other = null; - while (node != null) - { - next = (reverse ? node.Right : node.Left); - other = (reverse ? node.Left : node.Right); - if (tree.IsWithinRange(node.Item)) - { - stack.Push(node); - node = next; - } - else if (next == null || !tree.IsWithinRange(next.Item)) - { - node = other; - } - else - { - node = next; - } - } - } - - public bool MoveNext() - { - // this is a hack to make sure that the underlying subset has not been changed since - // - tree.VersionCheck(); - - if (version != tree.version) - { - ThrowHelper.ThrowInvalidOperationException(ExceptionResource.InvalidOperation_EnumFailedVersion); - } - - if (stack.Count == 0) - { - current = null; - return false; - } - - current = stack.Pop(); - SortedSet.Node node = (reverse ? current.Left : current.Right); - - Node next = null, other = null; - while (node != null) - { - next = (reverse ? node.Right : node.Left); - other = (reverse ? node.Left : node.Right); - if (tree.IsWithinRange(node.Item)) - { - stack.Push(node); - node = next; - } - else if (other == null || !tree.IsWithinRange(other.Item)) - { - node = next; - } - else - { - node = other; - } - } - return true; - } - - public void Dispose() - { - } - - public T Current - { - get - { - if (current != null) - { - return current.Item; - } - return default; - } - } - - object IEnumerator.Current - { - get - { - if (current == null) - { - ThrowHelper.ThrowInvalidOperationException(ExceptionResource.InvalidOperation_EnumOpCantHappen); - } - - return current.Item; - } - } - - internal bool NotStartedOrEnded - { - get - { - return current == null; - } - } - - internal void Reset() - { - if (version != tree.version) - { - ThrowHelper.ThrowInvalidOperationException(ExceptionResource.InvalidOperation_EnumFailedVersion); - } - - stack.Clear(); - Intialize(); - } - - void IEnumerator.Reset() - { - Reset(); - } - } - - - - internal struct ElementCount - { - internal int uniqueCount; - internal int unfoundCount; - } - #endregion - - #region misc - - /// - /// Searches the set for a given value and returns the equal value it finds, if any. - /// - /// The value to search for. - /// The value from the set that the search found, or the default value of when the search yielded no match. - /// A value indicating whether the search was successful. - /// - /// This can be useful when you want to reuse a previously stored reference instead of - /// a newly constructed one (so that more sharing of references can occur) or to look up - /// a value that has more complete data than the value you currently have, although their - /// comparer functions indicate they are equal. - /// - public bool TryGetValue(T equalValue, out T actualValue) - { - Node node = FindNode(equalValue); - if (node != null) - { - actualValue = node.Item; - return true; - } - actualValue = default; - return false; - } - - // used for set checking operations (using enumerables) that rely on counting - private static int Log2(int value) - { - // Contract.Requires(value>0) - int c = 0; - while (value > 0) - { - c++; - value >>= 1; - } - return c; - } - #endregion - } - - /// - /// A class that generates an IEqualityComparer for this SortedSet. Requires that the definition of - /// equality defined by the IComparer for this SortedSet be consistent with the default IEqualityComparer - /// for the type T. If not, such an IEqualityComparer should be provided through the constructor. - /// - internal class SortedSetEqualityComparer : IEqualityComparer> - { - private readonly IComparer comparer; - private readonly IEqualityComparer e_comparer; - - public SortedSetEqualityComparer() : this(null, null) - { - } - - public SortedSetEqualityComparer(IComparer comparer) : this(comparer, null) - { - } - - public SortedSetEqualityComparer(IEqualityComparer memberEqualityComparer) : this(null, memberEqualityComparer) - { - } - - /// - /// Create a new SetEqualityComparer, given a comparer for member order and another for member equality (these - /// must be consistent in their definition of equality) - /// - public SortedSetEqualityComparer(IComparer comparer, IEqualityComparer memberEqualityComparer) - { - if (comparer == null) - this.comparer = Comparer.Default; - else - this.comparer = comparer; - if (memberEqualityComparer == null) - e_comparer = EqualityComparer.Default; - else - e_comparer = memberEqualityComparer; - } - - - // using comparer to keep equals properties in tact; don't want to choose one of the comparers - public bool Equals(SortedSet x, SortedSet y) - { - return SortedSet.SortedSetEquals(x, y, comparer); - } - // IMPORTANT: this part uses the fact that GetHashCode() is consistent with the notion of equality in - // the set - public int GetHashCode(SortedSet obj) - { - int hashCode = 0; - if (obj != null) - { - foreach (T t in obj) - { - hashCode ^= (e_comparer.GetHashCode(t) & 0x7FFFFFFF); - } - } // else returns hashcode of 0 for null HashSets - return hashCode; - } - - // Equals method for the comparer itself. - public override bool Equals(Object obj) - { - if (obj is not SortedSetEqualityComparer comparer) - { - return false; - } - return (this.comparer == comparer.comparer); - } - - public override int GetHashCode() - { - return comparer.GetHashCode() ^ e_comparer.GetHashCode(); - } - } -} - - - -#pragma warning restore CS8604 // 引用类型参数可能为 null。 -#pragma warning restore CS8602 // 解引用可能出现空引用。 -#pragma warning restore CS8600 // 将 null 字面量或可能为 null 的值转换为非 null 类型。 -#pragma warning restore IDE0059 // 不需要赋值 -#pragma warning restore CS8625 // 无法将 null 字面量转换为非 null 的引用类型。 -#pragma warning restore CS8618 // 在退出构造函数时,不可为 null 的字段必须包含非 null 值。请考虑声明为可以为 null。 -#pragma warning restore CS8601 // 引用类型赋值可能为 null。 -#pragma warning restore CS8603 // 可能返回 null 引用。 -#endif \ No newline at end of file diff --git a/src/IFoxCAD.Basal/Sortedset/ThrowHelper.cs b/src/IFoxCAD.Basal/Sortedset/ThrowHelper.cs deleted file mode 100644 index ebb2f41..0000000 --- a/src/IFoxCAD.Basal/Sortedset/ThrowHelper.cs +++ /dev/null @@ -1,381 +0,0 @@ -#if NET35 -#pragma warning disable IDE0059 // 不需要赋值 -#pragma warning disable CS8600 // 将 null 字面量或可能为 null 的值转换为非 null 类型。 - -namespace System -{ - // This file defines an internal class used to throw exceptions in BCL code. - // The main purpose is to reduce code size. - // - // The old way to throw an exception generates quite a lot IL code and assembly code. - // Following is an example: - // C# source - // throw new ArgumentNullException("key", SR.GetString("ArgumentNull_Key")); - // IL code: - // IL_0003: ldstr "key" - // IL_0008: ldstr "ArgumentNull_Key" - // IL_000d: call string System.Environment::GetResourceString(string) - // IL_0012: newobj instance void System.ArgumentNullException::.ctor(string,string) - // IL_0017: throw - // which is 21bytes in IL. - // - // So we want to get rid of the ldstr and call to Environment.GetResource in IL. - // In order to do that, I created two enums: ExceptionResource, ExceptionArgument to represent the - // argument name and resource name in a small integer. The source code will be changed to - // ThrowHelper.ThrowArgumentNullException(ExceptionArgument.key, ExceptionResource.ArgumentNull_Key); - // - // The IL code will be 7 bytes. - // IL_0008: ldc.i4.4 - // IL_0009: ldc.i4.4 - // IL_000a: call void System.ThrowHelper::ThrowArgumentNullException(valuetype System.ExceptionArgument) - // IL_000f: ldarg.0 - // - // This will also reduce the Jitted code size a lot. - // - // It is very important we do this for generic classes because we can easily generate the same code - // multiple times for different instantiation. - // - // < - - - - - - - - - - -#if !SILVERLIGHT - using System.Runtime.Serialization; -#endif - - using System.Diagnostics; - internal static class ThrowHelper - { - internal static void ThrowWrongKeyTypeArgumentException(object key, Type targetType) - { - throw new ArgumentException(SR.GetString("SR.Arg_WrongType", key, targetType), "key"); - } - - internal static void ThrowWrongValueTypeArgumentException(object value, Type targetType) - { - throw new ArgumentException(SR.GetString("SR.Arg_WrongType", value, targetType), "value"); - } - - internal static void ThrowKeyNotFoundException() - { - throw new System.Collections.Generic.KeyNotFoundException(); - } - - internal static void ThrowArgumentException(ExceptionResource resource) - { - throw new ArgumentException(SR.GetString(GetResourceName(resource))); - } - - internal static void ThrowArgumentNullException(ExceptionArgument argument) - { - throw new ArgumentNullException(GetArgumentName(argument)); - } - - internal static void ThrowArgumentOutOfRangeException(ExceptionArgument argument) - { - throw new ArgumentOutOfRangeException(GetArgumentName(argument)); - } - - internal static void ThrowArgumentOutOfRangeException(ExceptionArgument argument, ExceptionResource resource) - { - throw new ArgumentOutOfRangeException(GetArgumentName(argument), SR.GetString(GetResourceName(resource))); - } - - internal static void ThrowInvalidOperationException(ExceptionResource resource) - { - throw new InvalidOperationException(SR.GetString(GetResourceName(resource))); - } - -#if !SILVERLIGHT - internal static void ThrowSerializationException(ExceptionResource resource) - { - throw new SerializationException(SR.GetString(GetResourceName(resource))); - } -#endif - - internal static void ThrowNotSupportedException(ExceptionResource resource) - { - throw new NotSupportedException(SR.GetString(GetResourceName(resource))); - } - - // Allow nulls for reference types and Nullable, but not for value types. - internal static void IfNullAndNullsAreIllegalThenThrow(object value, ExceptionArgument argName) - { - // Note that default(T) is not equal to null for value types except when T is Nullable. - if (value == null && !(default(T) == null)) - ThrowHelper.ThrowArgumentNullException(argName); - } - - // - // This function will convert an ExceptionArgument enum value to the argument name string. - // - internal static string GetArgumentName(ExceptionArgument argument) - { - string argumentName = null; - - switch (argument) - { - case ExceptionArgument.array: - argumentName = "array"; - break; - - case ExceptionArgument.arrayIndex: - argumentName = "arrayIndex"; - break; - - case ExceptionArgument.capacity: - argumentName = "capacity"; - break; - - case ExceptionArgument.collection: - argumentName = "collection"; - break; - - case ExceptionArgument.converter: - argumentName = "converter"; - break; - - case ExceptionArgument.count: - argumentName = "count"; - break; - - case ExceptionArgument.dictionary: - argumentName = "dictionary"; - break; - - case ExceptionArgument.index: - argumentName = "index"; - break; - - case ExceptionArgument.info: - argumentName = "info"; - break; - - case ExceptionArgument.key: - argumentName = "key"; - break; - - case ExceptionArgument.match: - argumentName = "match"; - break; - - case ExceptionArgument.obj: - argumentName = "obj"; - break; - - case ExceptionArgument.queue: - argumentName = "queue"; - break; - - case ExceptionArgument.stack: - argumentName = "stack"; - break; - - case ExceptionArgument.startIndex: - argumentName = "startIndex"; - break; - - case ExceptionArgument.value: - argumentName = "value"; - break; - - case ExceptionArgument.item: - argumentName = "item"; - break; - - default: - Debug.Assert(false, "The enum value is not defined, please checked ExceptionArgumentName Enum."); - return string.Empty; - } - - return argumentName; - } - - // - // This function will convert an ExceptionResource enum value to the resource string. - // - internal static string GetResourceName(ExceptionResource resource) - { - string resourceName = null; - - switch (resource) - { - case ExceptionResource.Argument_ImplementIComparable: - resourceName = "SR.Argument_ImplementIComparable"; - break; - - case ExceptionResource.Argument_AddingDuplicate: - resourceName = "SR.Argument_AddingDuplicate"; - break; - - case ExceptionResource.ArgumentOutOfRange_Index: - resourceName = "SR.ArgumentOutOfRange_Index"; - break; - - case ExceptionResource.ArgumentOutOfRange_NeedNonNegNum: - resourceName = "SR.ArgumentOutOfRange_NeedNonNegNum"; - break; - - case ExceptionResource.ArgumentOutOfRange_NeedNonNegNumRequired: - resourceName = "SR.ArgumentOutOfRange_NeedNonNegNumRequired"; - break; - - case ExceptionResource.ArgumentOutOfRange_SmallCapacity: - resourceName = "SR.ArgumentOutOfRange_SmallCapacity"; - break; - - case ExceptionResource.Arg_ArrayPlusOffTooSmall: - resourceName = "SR.Arg_ArrayPlusOffTooSmall"; - break; - - case ExceptionResource.Arg_RankMultiDimNotSupported: - resourceName = "SR.Arg_MultiRank"; - break; - - case ExceptionResource.Arg_NonZeroLowerBound: - resourceName = "SR.Arg_NonZeroLowerBound"; - break; - - case ExceptionResource.Argument_InvalidArrayType: - resourceName = "SR.Invalid_Array_Type"; - break; - - case ExceptionResource.Argument_InvalidOffLen: - resourceName = "SR.Argument_InvalidOffLen"; - break; - - case ExceptionResource.InvalidOperation_CannotRemoveFromStackOrQueue: - resourceName = "SR.InvalidOperation_CannotRemoveFromStackOrQueue"; - break; - - case ExceptionResource.InvalidOperation_EmptyCollection: - resourceName = "SR.InvalidOperation_EmptyCollection"; - break; - - case ExceptionResource.InvalidOperation_EmptyQueue: - resourceName = "SR.InvalidOperation_EmptyQueue"; - break; - - case ExceptionResource.InvalidOperation_EnumOpCantHappen: - resourceName = "SR.InvalidOperation_EnumOpCantHappen"; - break; - - case ExceptionResource.InvalidOperation_EnumFailedVersion: - resourceName = "SR.InvalidOperation_EnumFailedVersion"; - break; - - case ExceptionResource.InvalidOperation_EmptyStack: - resourceName = "SR.InvalidOperation_EmptyStack"; - break; - - case ExceptionResource.InvalidOperation_EnumNotStarted: - resourceName = "SR.InvalidOperation_EnumNotStarted"; - break; - - case ExceptionResource.InvalidOperation_EnumEnded: - resourceName = "SR.InvalidOperation_EnumEnded"; - break; - - case ExceptionResource.NotSupported_KeyCollectionSet: - resourceName = "SR.NotSupported_KeyCollectionSet"; - break; - - case ExceptionResource.NotSupported_SortedListNestedWrite: - resourceName = "SR.NotSupported_SortedListNestedWrite"; - break; - -#if !SILVERLIGHT - case ExceptionResource.Serialization_InvalidOnDeser: - resourceName = "SR.Serialization_InvalidOnDeser"; - break; - - case ExceptionResource.Serialization_MissingValues: - resourceName = "SR.Serialization_MissingValues"; - break; - - case ExceptionResource.Serialization_MismatchedCount: - resourceName = "SR.Serialization_MismatchedCount"; - break; -#endif - - case ExceptionResource.NotSupported_ValueCollectionSet: - resourceName = "SR.NotSupported_ValueCollectionSet"; - break; - - default: - Debug.Assert(false, "The enum value is not defined, please checked ExceptionArgumentName Enum."); - return string.Empty; - } - - return resourceName; - } - } - - // - // The convention for this enum is using the argument name as the enum name - // - internal enum ExceptionArgument - { - obj, - dictionary, - array, - info, - key, - collection, - match, - converter, - queue, - stack, - capacity, - index, - startIndex, - value, - count, - arrayIndex, - item, - } - - // - // The convention for this enum is using the resource name as the enum name - // - internal enum ExceptionResource - { - Argument_ImplementIComparable, - ArgumentOutOfRange_NeedNonNegNum, - ArgumentOutOfRange_NeedNonNegNumRequired, - Arg_ArrayPlusOffTooSmall, - Argument_AddingDuplicate, - Serialization_InvalidOnDeser, - Serialization_MismatchedCount, - Serialization_MissingValues, - Arg_RankMultiDimNotSupported, - Arg_NonZeroLowerBound, - Argument_InvalidArrayType, - NotSupported_KeyCollectionSet, - ArgumentOutOfRange_SmallCapacity, - ArgumentOutOfRange_Index, - Argument_InvalidOffLen, - NotSupported_ReadOnlyCollection, - InvalidOperation_CannotRemoveFromStackOrQueue, - InvalidOperation_EmptyCollection, - InvalidOperation_EmptyQueue, - InvalidOperation_EnumOpCantHappen, - InvalidOperation_EnumFailedVersion, - InvalidOperation_EmptyStack, - InvalidOperation_EnumNotStarted, - InvalidOperation_EnumEnded, - NotSupported_SortedListNestedWrite, - NotSupported_ValueCollectionSet, - } -} - -#pragma warning restore CS8600 // 将 null 字面量或可能为 null 的值转换为非 null 类型。 -#pragma warning restore IDE0059 // 不需要赋值 -#endif \ No newline at end of file diff --git a/src/IFoxCAD.Basal/Sortedset/bithelper.cs b/src/IFoxCAD.Basal/Sortedset/bithelper.cs deleted file mode 100644 index 665b435..0000000 --- a/src/IFoxCAD.Basal/Sortedset/bithelper.cs +++ /dev/null @@ -1,170 +0,0 @@ -#if NET35 -#pragma warning disable CS8603 // 可能返回 null 引用。 -#pragma warning disable CS8618 // 在退出构造函数时,不可为 null 的字段必须包含非 null 值。请考虑声明为可以为 null。 - - -using System; -using System.Collections; -using System.Text; - -namespace System.Collections.Generic -{ - /// - /// ABOUT: - /// Helps with operations that rely on bit marking to indicate whether an item in the - /// collection should be added, removed, visited already, etc. - /// - /// BitHelper doesn't allocate the array; you must pass in an array or ints allocated on the - /// stack or heap. ToIntArrayLength() tells you the int array size you must allocate. - /// - /// USAGE: - /// Suppose you need to represent a bit array of length (i.e. logical bit array length) - /// BIT_ARRAY_LENGTH. Then this is the suggested way to instantiate BitHelper: - /// *************************************************************************** - /// int intArrayLength = BitHelper.ToIntArrayLength(BIT_ARRAY_LENGTH); - /// BitHelper bitHelper; - /// if (intArrayLength less than stack alloc threshold) - /// int* m_arrayPtr = stackalloc int[intArrayLength]; - /// bitHelper = new BitHelper(m_arrayPtr, intArrayLength); - /// else - /// int[] m_arrayPtr = new int[intArrayLength]; - /// bitHelper = new BitHelper(m_arrayPtr, intArrayLength); - /// *************************************************************************** - /// - /// IMPORTANT: - /// The second ctor args, length, should be specified as the length of the int array, not - /// the logical bit array. Because length is used for bounds checking into the int array, - /// it's especially important to get this correct for the stackalloc version. See the code - /// samples above; this is the value gotten from ToIntArrayLength(). - /// - /// The length ctor argument is the only exception; for other methods -- MarkBit and - /// IsMarked -- pass in values as indices into the logical bit array, and it will be mapped - /// to the position within the array of ints. - /// - /// - - - - - unsafe internal class BitHelper - { // should not be serialized - private const byte MarkedBitFlag = 1; - private const byte IntSize = 32; - - // m_length of underlying int array (not logical bit array) - private int m_length; - - // ptr to stack alloc'd array of ints - [System.Security.SecurityCritical] - private int* m_arrayPtr; - - // array of ints - private int[] m_array; - - // whether to operate on stack alloc'd or heap alloc'd array - private bool useStackAlloc; - - /// - /// Instantiates a BitHelper with a heap alloc'd array of ints - /// - /// int array to hold bits - /// length of int array - // - // - // - // - [System.Security.SecurityCritical] - internal BitHelper(int* bitArrayPtr, int length) - { - this.m_arrayPtr = bitArrayPtr; - this.m_length = length; - useStackAlloc = true; - } - - /// - /// Instantiates a BitHelper with a heap alloc'd array of ints - /// - /// int array to hold bits - /// length of int array - internal BitHelper(int[] bitArray, int length) - { - this.m_array = bitArray; - this.m_length = length; - } - - /// - /// Mark bit at specified position - /// - /// - // - // - // - [System.Security.SecurityCritical] - internal unsafe void MarkBit(int bitPosition) - { - if (useStackAlloc) - { - int bitArrayIndex = bitPosition / IntSize; - if (bitArrayIndex < m_length && bitArrayIndex >= 0) - { - m_arrayPtr[bitArrayIndex] |= (MarkedBitFlag << (bitPosition % IntSize)); - } - } - else - { - int bitArrayIndex = bitPosition / IntSize; - if (bitArrayIndex < m_length && bitArrayIndex >= 0) - { - m_array[bitArrayIndex] |= (MarkedBitFlag << (bitPosition % IntSize)); - } - } - } - - /// - /// Is bit at specified position marked? - /// - /// - /// - // - // - // - [System.Security.SecurityCritical] - internal unsafe bool IsMarked(int bitPosition) - { - if (useStackAlloc) - { - int bitArrayIndex = bitPosition / IntSize; - if (bitArrayIndex < m_length && bitArrayIndex >= 0) - { - return ((m_arrayPtr[bitArrayIndex] & (MarkedBitFlag << (bitPosition % IntSize))) != 0); - } - return false; - } - else - { - int bitArrayIndex = bitPosition / IntSize; - if (bitArrayIndex < m_length && bitArrayIndex >= 0) - { - return ((m_array[bitArrayIndex] & (MarkedBitFlag << (bitPosition % IntSize))) != 0); - } - return false; - } - } - - /// - /// How many ints must be allocated to represent n bits. Returns (n+31)/32, but - /// avoids overflow - /// - /// - /// - internal static int ToIntArrayLength(int n) - { - return n > 0 ? ((n - 1) / IntSize + 1) : 0; - } - } -} - -#pragma warning restore CS8618 // 在退出构造函数时,不可为 null 的字段必须包含非 null 值。请考虑声明为可以为 null。 -#pragma warning restore CS8603 // 可能返回 null 引用。 - -#endif \ No newline at end of file diff --git a/src/IFoxCAD.Cad/Algorithms/Graph/Graph.cs b/src/IFoxCAD.Cad/Algorithms/Graph/Graph.cs deleted file mode 100644 index 64e551b..0000000 --- a/src/IFoxCAD.Cad/Algorithms/Graph/Graph.cs +++ /dev/null @@ -1,653 +0,0 @@ -namespace IFoxCAD.Cad; -using Exception = System.Exception; - -/// -/// 无权无向图实现 -/// IEnumerable 枚举所有顶点; -/// -public sealed class Graph : IGraph, IEnumerable -{ - #region 字段及属性 - /// - /// 存储所有节点的字典,key为顶点的类型,value为邻接表,类型是hashset,不可重复添加点 - /// - /// - readonly Dictionary> vertices = new(); - /// - /// 邻接边表,key为顶点的类型,value为邻接边表,类型是hashset,不可重复添加边 - /// - readonly Dictionary> edges = new(); - /// - /// 为加快索引,引入hash检索 - /// - readonly Dictionary vertexs = new(); - - public int VerticesCount => vertices.Count; - - /// - /// Returns a reference vertex. - /// Time complexity: O(1). - /// - private IGraphVertex? ReferenceVertex - { - get - { - using (var enumerator = vertexs.GetEnumerator()) - { - if (enumerator.MoveNext()) - { - return enumerator.Current.Value; - } - } - - return null; - } - } - IGraphVertex? IGraph.ReferenceVertex => ReferenceVertex; - /// - /// 目前点增加点的顺序号,这个点号不随删点而减少的 - /// - private int insertCount; - #endregion - - #region 构造函数 - public Graph() - { - insertCount = 0; // 每次新建对象就将顶点顺序号归零 - } - #endregion - - #region 顶点及边_增 - /// - /// 向该图添加一个新顶点,但是无边; - /// - /// 点 - /// 创建的顶点 - public IGraphVertex AddVertex(Point3d pt) - { - var str = pt.GetHashString(); - if (vertexs.ContainsKey(str)) - return vertexs[str]; - - var vertex = new GraphVertex(pt, insertCount++); - vertices.Add(vertex, new HashSet()); - edges.Add(vertex, new HashSet()); - - vertexs[str] = vertex; - - return vertex; - } - - /// - /// 向该图添加一个边; - /// - /// - public void AddEdge(Curve3d curve) - { - if (curve == null) - throw new ArgumentNullException(nameof(curve)); - - var start = AddVertex(curve.StartPoint); - var end = AddVertex(curve.EndPoint); - - // 添加起点的邻接表和邻接边 - vertices[start].Add(end); - edges[start].Add(new GraphEdge(end, curve)); - - // 为了保证点顺序,每个点的邻接边必须按起点-终点,所以添加曲线终点时,将添加一个方向的曲线 - var curtmp = (Curve3d)curve.Clone(); - curtmp = curtmp.GetReverseParameterCurve(); - - // 添加终点的邻接表和邻接边 - vertices[end].Add(start); - edges[end].Add(new GraphEdge(start, curtmp)); - } - #endregion - - #region 顶点及边_删 - /// - /// 从此图中删除现有顶点; - /// - /// 点 - public void RemoveVertex(Point3d pt) - { - var str = pt.GetHashString(); - if (vertexs.ContainsKey(str)) - { - var vertex = vertexs[str]; - - // 删除邻接表里的vertex点,先删除后面的遍历可以少一轮 - vertices.Remove(vertex!); - - // 删除其他顶点的邻接表里的vertex点 - foreach (var item in vertices.Values) - item.Remove(vertex!); - - // 删除邻接边表里的vertex点,先删除后面的遍历可以少一轮 - edges.Remove(vertex!); - - // 删除其他顶点的邻接边表的指向vertex的边 - foreach (var item in edges.Values) - { - item.RemoveWhere(x => vertex.Equals(x.TargetVertex)); - // foreach (var edge in item) - // { - // if (vertex.Equals(edge.TargetVertex)) - // item.Remove(edge); - // } - } - vertexs.Remove(str); - } - } - - /// - /// 从此图中删除一条边; - /// - /// 曲线 - public void RemoveEdge(Curve3d curve) - { - if (curve == null) - throw new ArgumentNullException(nameof(curve)); - - RemoveVertex(curve.StartPoint); - RemoveVertex(curve.EndPoint); - } - #endregion - - #region 顶点和边_查 - /// - /// 我们在给定的来源和目的地之间是否有边? - /// - /// 起点 - /// 终点 - /// 有边返回 ,反之返回 - public bool HasEdge(IGraphVertex source, IGraphVertex dest) - { - if (!vertices.ContainsKey(source) || !vertices.ContainsKey(dest)) - throw new ArgumentException("源或目标不在此图中;"); - - foreach (var item in edges[source]) - { - if (item.TargetVertex == dest) - return true; - } - return false; - } - - /// - /// 获取边 - /// - /// 起点 - /// 终点 - /// - /// 传入的点不在图中时抛出参数异常 - public IEdge? GetEdge(IGraphVertex source, IGraphVertex dest) - { - if (!vertices.ContainsKey(source) || !vertices.ContainsKey(dest)) - throw new ArgumentException("源或目标不在此图中;"); - - foreach (var item in edges[source]) - { - if (item.TargetVertex.Equals(dest)) - return item; - } - return null; - } - - /// - /// 是否存在顶点,此函数目前未发现有啥用 - /// - /// 顶点 - /// 存在顶点返回 ,反之返回 - public bool ContainsVertex(IGraphVertex value) - { - return vertices.ContainsKey(value); - } - #endregion - - #region 获取邻接表和曲线 - /// - /// 获取顶点的邻接表 - /// - /// 顶点 - /// 邻接表 - public HashSet GetAdjacencyList(IGraphVertex vertex) - { - return vertices[vertex]; - } - - /// - /// 获取顶点的邻接边表 - /// - /// 顶点 - /// 邻接边表 - public HashSet GetAdjacencyEdge(IGraphVertex vertex) - { - return edges[vertex]; - } - - /// - /// 根据顶点表获取曲线集合 - /// - /// 顶点表 - /// 曲线表 - public List GetCurves(List graphVertices) - { - var curves = new List(); - for (int i = 0; i < graphVertices.Count - 1; i++) - { - var cur = graphVertices[i]; - var next = graphVertices[i + 1]; - var edge = GetEdge(cur, next); - if (edge is not null) - curves.Add(edge.TargetEdge); - } - var lastedge = GetEdge(graphVertices[^1], graphVertices[0]); - if (lastedge is not null) - curves.Add(lastedge.TargetEdge); - - return curves; - } - #endregion - - #region 克隆及接口实现 - /// - /// 克隆此图;目测是深克隆 - /// - public Graph Clone() - { - var newGraph = new Graph(); - - foreach (var vertex in edges.Values) - foreach (var item in vertex) - newGraph.AddEdge(item.TargetEdge); - - return newGraph; - } - - IGraph IGraph.Clone() - { - return Clone(); - } - - public IEnumerator GetEnumerator() - { - return VerticesAsEnumberable.GetEnumerator(); - } - - IEnumerator? IEnumerable.GetEnumerator() - { - return GetEnumerator() as IEnumerator; - } - - public IEnumerable VerticesAsEnumberable => - vertices.Select(x => x.Key); - #endregion - - #region 方法 - /// - /// 输出点的邻接表的可读字符串 - /// - /// - public string ToReadable() - { - int i = 1; - string output = string.Empty; - foreach (var node in vertices) - { - var adjacents = string.Empty; - - output = string.Format("{1}\r\n{0}-{2}: [", i, output, node.Key.Data.ToString()); - - foreach (var adjacentNode in node.Value) - adjacents = string.Format("{0}{1},", adjacents, adjacentNode.Data.ToString()); - - if (adjacents.Length > 0) - adjacents = adjacents.TrimEnd(new char[] { ',', ' ' }); - - output = string.Format("{0}{1}]", output, adjacents); - i++; - } - return output; - } - #endregion -} - - -/// -/// 邻接表图实现的顶点; -/// IEnumerable 枚举所有邻接点; -/// -public sealed class GraphVertex : IGraphVertex, IEquatable, IComparable, IComparable -{ - #region 属性 - public Point3d Data { get; set; } - public int Index { get; set; } - #endregion - - #region 构造 - /// - /// 邻接表图实现的顶点 - /// - /// 点 - /// 所在节点索引 - public GraphVertex(Point3d value, int index) - { - Data = value; - Index = index; - } - #endregion - - #region 重载运算符_比较 - public bool Equals(IGraphVertex other) - { - return Index == other.Index; - } - - public override bool Equals(object obj) - { - if (obj is null) - return false; - if (obj is not IGraphVertex vertex) - return false; - else - return Equals(vertex); - } - - public override int GetHashCode() - { - return Index; - } - - public int CompareTo(IGraphVertex other) - { - if (Equals(other)) - return 0; - - if (Index < other.Index) - return -1; - else - return 1; - } - - int IComparable.CompareTo(IGraphVertex other) - { - return CompareTo(other); - } - - public int CompareTo(object obj) - { - if (obj is null) - return 1; - - try - { - var other = (GraphVertex)obj; - return CompareTo(other); - } - catch (Exception) - { - throw new ArgumentException("Object is not a IGraphVertex"); - } - } - - public static bool operator ==(GraphVertex person1, GraphVertex person2) - { - if (person1 is null || person2 is null) - return Equals(person1, person2); - - return person1.Equals(person2); - } - - public static bool operator !=(GraphVertex person1, GraphVertex person2) - { - if (person1 is null || person2 is null) - return !Equals(person1, person2); - - return !person1.Equals(person2); - } - #endregion -} - - -/// -/// 无向图中边的定义 -/// -public sealed class GraphEdge : IEdge, IEquatable -{ - #region 属性 - public IGraphVertex TargetVertex { get; set; } - public Curve3d TargetEdge { get; set; } - #endregion - - #region 构造 - /// - /// 无向图中边的定义 - /// - /// 下一点 - /// 下一点之间的曲线 - public GraphEdge(IGraphVertex target, Curve3d edge) - { - TargetVertex = target; - TargetEdge = edge; - } - #endregion - - #region 重载运算符_比较 - public bool Equals(GraphEdge other) - { - if (other is null) - return false; - return TargetVertex == other.TargetVertex && - TargetEdge == other.TargetEdge; - } - public override bool Equals(object obj) - { - if (obj is null) - return false; - if (obj is not GraphEdge personObj) - return false; - else - return Equals(personObj); - } - - public override int GetHashCode() - { - return (TargetVertex.GetHashCode(), TargetEdge.GetHashCode()).GetHashCode(); - } - public static bool operator ==(GraphEdge person1, GraphEdge person2) - { - if (person1 is null || person2 is null) - return Equals(person1, person2); - - return person1.Equals(person2); - } - public static bool operator !=(GraphEdge person1, GraphEdge person2) - { - if (person1 is null || person2 is null) - return !Equals(person1, person2); - - return !person1.Equals(person2); - } - #endregion -} - - -/// -/// 深度优先搜索; -/// -public sealed class DepthFirst -{ - #region 公共方法 - /// - /// 存储所有的边 - /// -#if true - public List> Curve3ds { get; } = new(); -#else - public List> Curve3ds { get; } = new(); -#endif - private HashSet Curved { get; } = new(); - - - /// - /// 找出所有的路径 - /// - /// 图 - public void FindAll(IGraph graph) - { - var total = new HashSet(); - // var graphtmp = graph.Clone(); - foreach (var item in graph.VerticesAsEnumberable) - { - Dfs(graph, new LinkedHashSet { item }, total); - total.Add(item); - } - } - #endregion - - #region 内部方法 - /// - /// 递归 DFS; - /// - /// 图 - /// 已经遍历的路径 -#if true - void Dfs(IGraph graph, LinkedHashSet visited, HashSet totalVisited) - { - var adjlist = graph.GetAdjacencyList(/*startNode*/ visited.First!.Value); // O(1) - foreach (var nextNode in adjlist) // O(n) - { - if (totalVisited.Contains(nextNode)) - { - continue; - } - // 如果下一个点未遍历过 - if (!visited.Contains(nextNode)) // O(1) - { - // 将下一点加入路径集合,并进行下一次递归 - var sub = new LinkedHashSet { nextNode }; - sub.AddRange(visited); // O(n) - Dfs(graph, sub, totalVisited); - } - // 如果下一点遍历过,并且路径大于2,说明已经找到起点 - else if (visited.Count > 2 && nextNode.Equals(visited.Last!.Value)) - { - // 将重复的路径进行过滤,并把新的路径存入结果 - var curstr = Gethashstring(visited); // O(n) - if (Isnew(curstr)) // O(1) - { - Curve3ds.Add(visited); - Curved.Add(curstr.Item1); - } - } - } - } - - - - -#else - - void Dfs(IGraph graph, List visited) - { - var startNode = visited[0]; - IGraphVertex nextNode; - List sub; - - var adjlist = graph.GetAdjacencyList(startNode).ToList(); // O(n) - for (int i = 0; i < adjlist.Count; i++) // O(n) - { - nextNode = adjlist[i]; - - // 如果下一个点未遍历过 - if (!visited.Contains(nextNode)) // O(n) - { - // 将下一点加入路径集合,并进行下一次递归 - sub = new List { nextNode }; - sub.AddRange(visited); // O(n) - Dfs(graph, sub); - } - - // 如果下一点遍历过,并且路径大于2,说明已经找到起点 - else if (visited.Count > 2 && nextNode.Equals(visited[^1])) - { - // 将重复的路径进行过滤,并把新的路径存入结果 - var cur = RotateToSmallest(visited); // O(n) - var inv = Invert(cur,cur[0]); // O(n) - - var curstr = Gethashstring(cur,inv); - // Env.Print(curstr); - if (Isnew(curstr)) - { - Curve3ds.Add(cur); - Curved.Add(curstr.Item1); - } - } - } - } -#endif - - - - - - - - - /// - /// 将列表旋转到最小的值为列表起点 - /// - /// - /// - static List RotateToSmallest(List lst) - { - var index = lst.IndexOf(lst.Min()); - return lst.Skip(index).Concat(lst.Take(index)).ToList(); - } - - /// - /// 将列表反向,并旋转到起点为最小值 - /// - /// - /// - static List Invert(List lst, IGraphVertex vertex) - { - var tmp = lst.ToList(); - tmp.Reverse(); - var index = tmp.IndexOf(vertex); - return tmp.Skip(index).Concat(lst.Take(index)).ToList(); - } - - static (string, string) Gethashstring(List pathone, List pathtwo) - { - var one = new string[pathone.Count]; - var two = new string[pathtwo.Count]; - for (int i = 0; i < pathone.Count; i++) - { - one[i] = pathone[i].Index.ToString(); - two[i] = pathtwo[i].Index.ToString(); - } - return (string.Join("-", one), string.Join("-", two)); - } - - static (string, string) Gethashstring(LinkedHashSet path) - { - var one = new string[path.Count]; - var two = new string[path.Count]; - path.For(path.MinNode!, (i, ver1, ver2) => { - one[i] = ver1.Index.ToString(); - two[i] = ver2.Index.ToString(); - }); - return (string.Join("-", one), string.Join("-", two)); - } - - - bool Isnew((string, string) path) - { - return !Curved.Contains(path.Item1) && !Curved.Contains(path.Item2); - } - - - #endregion -} \ No newline at end of file diff --git a/src/IFoxCAD.Cad/Algorithms/Graph/IGraph.cs b/src/IFoxCAD.Cad/Algorithms/Graph/IGraph.cs deleted file mode 100644 index 73547b7..0000000 --- a/src/IFoxCAD.Cad/Algorithms/Graph/IGraph.cs +++ /dev/null @@ -1,98 +0,0 @@ -namespace IFoxCAD.Cad; - -/// -/// 无向图 -/// -public interface IGraph -{ - /// - /// 顶点的数量 - /// - /// - int VerticesCount { get; } - - /// - /// 是否存在顶点 - /// - /// 顶点键 - /// - bool ContainsVertex(IGraphVertex key); - - /// - /// 顶点的迭代器 - /// - /// - IEnumerable VerticesAsEnumberable { get; } - - /// - /// 是否有边 - /// - /// 源顶点 - /// 目的顶点 - /// - bool HasEdge(IGraphVertex source, IGraphVertex destination); - /// - /// 图克隆函数 - /// - /// - IGraph Clone(); - /// - /// 获取边 - /// - /// - /// - /// - IEdge? GetEdge(IGraphVertex source, IGraphVertex dest); - /// - /// 邻接表 - /// - /// - /// - HashSet GetAdjacencyList(IGraphVertex vertex); - /// - /// 邻接边表 - /// - /// - /// - HashSet GetAdjacencyEdge(IGraphVertex vertex); - IGraphVertex? ReferenceVertex { get; } - - void RemoveVertex(Point3d pt); - void RemoveEdge(Curve3d curve); - -} - -/// -/// 无向图顶点 -/// -/// 顶点数据类型 -public interface IGraphVertex : IComparable -{ - /// - /// 顶点的键 - /// - /// - int Index { get; set; } - - /// - /// 顶点的数据 - /// - Point3d Data { get; } -} -/// -/// 无向图边 -/// -public interface IEdge -{ - /// - /// 边 - /// - Curve3d TargetEdge { get; } - /// - /// 目标顶点 - /// - IGraphVertex TargetVertex { get; } -} - - - diff --git a/src/IFoxCAD.Cad/Algorithms/QuadTree/QuadEntity.cs b/src/IFoxCAD.Cad/Algorithms/QuadTree/QuadEntity.cs deleted file mode 100644 index c5f7f11..0000000 --- a/src/IFoxCAD.Cad/Algorithms/QuadTree/QuadEntity.cs +++ /dev/null @@ -1,25 +0,0 @@ -namespace IFoxCAD.Cad; - -/* - * 这个类存在的意义是为了不暴露Rect类字段 - * 同时利用了Rect类字段的快速 - * 提供到外面去再继承 - */ - -/// -/// 四叉树图元 -/// -public class QuadEntity : Rect -{ - /// - /// 四叉树图元 - /// - /// 包围盒 - public QuadEntity(Rect box) - { - _X = box._X; - _Y = box._Y; - _Top = box._Top; - _Right = box._Right; - } -} \ No newline at end of file diff --git a/src/IFoxCAD.Cad/Algorithms/QuadTree/QuadTree.cs b/src/IFoxCAD.Cad/Algorithms/QuadTree/QuadTree.cs deleted file mode 100644 index 7032b10..0000000 --- a/src/IFoxCAD.Cad/Algorithms/QuadTree/QuadTree.cs +++ /dev/null @@ -1,259 +0,0 @@ -/* - * 四叉树维基百科 http://en.wikipedia.org/wiki/Quadtree - * 四叉树是一种分区空间的算法,更快找出内部或外部给定区域. - * 通过一个正交矩形边界进行中心点分裂四个正交矩形, - * 插入时候会一直分裂四个正交矩形, - * 当分裂四个节点都无法单独拥有 图元包围盒 就停止分裂,并且你属于这四个节点的父亲. - * (不包含就是面积少了,就这么一句话看代码看半天), - * 还可以通过限制树的深度实现加速. - * - * 第一版: https://www.codeproject.com/script/Articles/ViewDownloads.aspx?aid=30535 - * - * 第二版: 找邻居 - * https://blog.csdn.net/dive_shallow/article/details/112438050 - * https://geidav.wordpress.com/2017/12/02/advanced-octrees-4-finding-neighbor-nodes/ - * - * 1.根节点:控制根节点从而控制所有节点 - * 2.子节点:包含自身根节点,插入矩形的时候进行递归分裂自身,和实现查找. - * 3.接口:约束都要有正交矩形,否则无法调用"包含"方法 - * 4.选择模式:模仿cad的窗选和框选 - */ -namespace IFoxCAD.Cad; - -/// -/// 根节点控制器 -/// -/// 类型接口约束必须有正交矩形 -public class QuadTree where TEntity : QuadEntity -{ - #region 成员 - /// - /// 根节点 - /// - QuadTreeNode _rootNode; - - /// - /// 四叉树节点的数目 - /// - public int Count { get => _rootNode.CountSubTree; } - - /// - /// 点容器(红黑树) - /// - SortedSet _points; - #endregion - - #region 构造 - /// - /// 四叉树根节点控制器 - /// - /// 四叉树矩形范围 - public QuadTree(Rect rect) - { - _rootNode = new QuadTreeNode(rect, null, 0);// 初始化根节点 - _points = new(); - } - #endregion - - #region 方法 - /// - /// 通过根节点插入数据项 - /// - /// - public void Insert(TEntity ent) - { - /* - * 图元点 是不分裂空间的,加入一个红黑树内部. - */ - if (ent.IsPoint) - { - _points.Add(ent); - return; - } - - while (!_rootNode.Contains(ent)) - { - /* - * 四叉树插入时候,如果超出根边界,就需要扩展 - * 扩展时候有一个要求,当前边界要作为扩展边界的一个象限,也就是反向分裂 - * - * 创建新根,计算原根在新根的位置, - * 替换指针:获取新分裂的节点的父节点,判断它哪个儿子是它, - * 替换之后可能仍然不包含图元边界,再循环计算. - */ - var sq_Left = _rootNode._X; - var sq_Botton = _rootNode._Y; - var sq_Right = _rootNode._Right; - var sq_Top = _rootNode._Top; - if (ent._Y >= _rootNode._Y)// 上↑增殖 - { - if (ent._X >= _rootNode._X) - { - // 右上↗增殖 - sq_Right += _rootNode.Width; - sq_Top += _rootNode.Height; - } - else - { - // 左上↖增殖 - sq_Left -= _rootNode.Width; - sq_Top += _rootNode.Height; - } - } - else// 在下↓ - { - if (ent._X >= _rootNode._X) - { - // 右下↘增殖 - sq_Right += _rootNode.Width; - sq_Botton -= _rootNode.Height; - } - else - { - // 左下↙增殖 - sq_Left -= _rootNode.Width; - sq_Botton -= _rootNode.Height; - } - } - // 扩大2次方 - var rectSquare = new Rect(sq_Left, sq_Botton, sq_Right, sq_Top); - - // 四叉树的旧根要作为四分之一插入 - // 新根中计算原根 - // 把 旧根节点 连接到 新根节点 上面,然后新根成为根 - var newRoot = new QuadTreeNode(rectSquare, null, 0); - var insert = newRoot.Insert(_rootNode); - if (insert is null) - throw new("四叉树:新根尺寸不对"); - if (!insert.Equals(_rootNode)) - throw new("四叉树:新旧节点大小不一致,无法连接"); - - var insPar = insert.Parent; - _rootNode.Parent = insPar; - if (insPar is null) - return; - - if (_rootNode.Equals(insPar.RightTopTree)) - insPar.RightTopTree = _rootNode; - else if (_rootNode.Equals(insPar.RightBottomTree)) - insPar.RightBottomTree = _rootNode; - else if (_rootNode.Equals(insPar.LeftBottomTree)) - insPar.LeftBottomTree = _rootNode; - else if (_rootNode.Equals(insPar.LeftTopTree)) - insPar.LeftTopTree = _rootNode; - else - throw new("四叉树:新节点不对,无法连接"); - - // 其后的子节点层数全部增加层数, - // 要加多少层取决于当前根边界属于新根边界的所在层 - var depth = insert.Depth; - if (depth == 0) - throw new("四叉树:插入节点是0,造成错误"); - _rootNode.ForEach(node => { - node.Depth += depth; - return false; - }); - - // 交换根控制 - _rootNode = newRoot; - } - - _rootNode.Insert(ent); - } - - - /// - /// 查询四叉树,返回给定区域的数据项 - /// - /// 矩形选区查询 - /// - public List Query(Rect rect, QuadTreeSelectMode selectMode = QuadTreeSelectMode.IntersectsWith) - { - QuadTreeEvn.SelectMode = selectMode; - - var results = new List(); - // 选择图元 - _rootNode.Query(rect, results); - // 选择点 - var ptge = _points.GetEnumerator(); - switch (selectMode) - { - case QuadTreeSelectMode.IntersectsWith: - case QuadTreeSelectMode.Contains: - /* 由于红黑树的方法 _points.GetViewBetween() - * 过滤只能过滤X区间,Y区间还是要过滤, - * 那么我就只能用这样的方法加速了 - * - * 而更好的方式是不用红黑树,去加入一个点云数据来进行,可谓是编程无极限.... - */ - while (ptge.MoveNext()) - { - var ptEnt = ptge.Current; - if (rect._X <= ptEnt._X && ptEnt._X <= rect._Right) - { - if (rect._Y <= ptEnt._Y && ptEnt._Y <= rect.Top) - results.Add(ptEnt); - } - else if (ptEnt._X > rect._Right) - break;// 超过后面范围就break,因为红黑树已经排序 - } - break; - default: - throw new("四叉树:" + nameof(selectMode)); - } - return results; - } - - /// - /// 删除子节点 - /// - /// 根据范围删除 - public void Remove(Rect rect) - { - _rootNode.Remove(rect); - } - - /// - /// 删除子节点 - /// - /// 根据图元删除 - public void Remove(TEntity ent) - { - _rootNode.Remove(ent); - } - - /// - /// 找到附近节点图元 - /// - [Obsolete("找附近节点的并不是最近的图元")] - public TEntity? FindNeibor(Rect rect, QuadTreeFindMode findMode) - { - return _rootNode.FindNeibor(rect, findMode); - } - - /// - /// 找到附近图元 - /// - /// - /// - public TEntity? FindNearEntity(Rect rect) - { - return _rootNode.FindNearEntity(rect); - } - - /// - /// 执行四叉树中特定的行为 - /// - /// - public void ForEach(QTAction action) - { - _rootNode.ForEach(action); - } - - /// - /// 委托:四叉树节点上执行一个操作 - /// - /// - public delegate bool QTAction(QuadTreeNode obj); - #endregion -} \ No newline at end of file diff --git a/src/IFoxCAD.Cad/Algorithms/QuadTree/QuadTreeEvn.cs b/src/IFoxCAD.Cad/Algorithms/QuadTree/QuadTreeEvn.cs deleted file mode 100644 index 16d518b..0000000 --- a/src/IFoxCAD.Cad/Algorithms/QuadTree/QuadTreeEvn.cs +++ /dev/null @@ -1,26 +0,0 @@ -#pragma warning disable CA2211 // 非常量字段应当不可见 -namespace IFoxCAD.Cad; - -public class QuadTreeEvn -{ - /// - /// 最小的节点有一个面积(一定要大于0) - /// - public static double MinArea = 1e-6; - - /// - /// 选择模式 - /// - public static QuadTreeSelectMode SelectMode; - - /// - /// 最大深度 - /// - public static int QuadTreeMaximumDepth = 2046; - - /// - /// 节点内容超过就分裂 - /// - public static int QuadTreeContentsCountSplit = 20; -} -#pragma warning restore CA2211 // 非常量字段应当不可见 \ No newline at end of file diff --git a/src/IFoxCAD.Cad/Algorithms/QuadTree/QuadTreeNode.cs b/src/IFoxCAD.Cad/Algorithms/QuadTree/QuadTreeNode.cs deleted file mode 100644 index 017eace..0000000 --- a/src/IFoxCAD.Cad/Algorithms/QuadTree/QuadTreeNode.cs +++ /dev/null @@ -1,820 +0,0 @@ -namespace IFoxCAD.Cad; - -/// -/// 子节点 -/// -/// -public class QuadTreeNode - : Rect - where TEntity : QuadEntity -{ - #region 成员 - /// - /// 子节点:第一象限:右上↗ - /// - public QuadTreeNode? RightTopTree; - /// - /// 子节点:第二象限:左上↖ - /// - public QuadTreeNode? LeftTopTree; - /// - /// 子节点:第三象限:左下↙ - /// - public QuadTreeNode? LeftBottomTree; - /// - /// 子节点:第四象限:右下↘ - /// - public QuadTreeNode? RightBottomTree; - /// - /// 所有子节点 - /// - QuadTreeNode[] Nodes - { - get - { - return new QuadTreeNode[] - { - RightTopTree!, - LeftTopTree!, - LeftBottomTree!, - RightBottomTree!, - }; - } - } - /// - /// 所有子节点是空的 - /// - bool NodesIsEmpty => RightTopTree is null && LeftTopTree is null && LeftBottomTree is null && RightBottomTree is null; - - /// - /// 父节点 - /// - public QuadTreeNode? Parent; - /// - /// 节点的在四叉树的深度 - /// - public int Depth; - - // 注意,内容没有限制:这不是 impement 四叉树的标准方法 - /// (节点图元是交叉线压着的,并不是矩形范围内全部,因为这是四叉树的特性决定) - /// - /// 本节点:内容 - /// - public List Contents; - - /// - /// 本节点和旗下所有子节点:内容群 - /// - public void ContentsSubTree(List results) - { - if (Contents is null) - return; - results.AddRange(Contents); - var nodes = Nodes; - for (int i = 0; i < nodes.Length; i++) - nodes[i]?.ContentsSubTree(results); - } - - /// - /// 本节点和旗下所有子节点:内容群数量 - /// - public int CountSubTree - { - get - { - if (Contents is null) - return 0; - int count = Contents.Count; - - var nodes = Nodes; - for (int i = 0; i < nodes.Length; i++) - { - var node = nodes[i]; - if (node is null) - continue; - count += node.CountSubTree; - } - return count; - } - } - #endregion - - #region 构造 - /// - /// 四叉树节点 - /// - /// 当前节点边界 - /// 父节点 - /// 节点深度 - public QuadTreeNode(Rect box, QuadTreeNode? parent, int depth) - { - _X = box._X; - _Y = box._Y; - _Right = box._Right; - _Top = box._Top; - - Parent = parent; - Depth = depth; - Contents = new(); - } - #endregion - - #region 增 - /// - /// 将原有节点插入用 - /// - /// - internal QuadTreeNode? Insert(Rect rect) - { - if (!Contains(rect)) - return null; - - // 四叉树分裂,将当前节点分为四个子节点 - if (NodesIsEmpty) - CreateChildren(); - - // 当前节点边界 包含 图元包围盒 就插入 - // 退出递归:4个节点都不完全包含 - // 4个节点的上层 - var nodes = Nodes; - for (int i = 0; i < nodes.Length; i++) - { - var node = nodes[i]; - if (node is null) - continue; - - if (node.Equals(rect)) - { - rect = node; - return node.Insert(rect); - } - } - return this; - } - - /// - /// 将数据项递归插入四叉树 - /// - /// - public QuadTreeNode? Insert(TEntity ent) - { - if (!Contains(ent)) - { - // Debug.WriteLine("不在四叉树边界范围"); - // Trace.WriteLine("不在四叉树边界范围"); - return null; - } - - // if (ent.IsPoint) - // { - // // 找到最后一层包含它的节点,然后加入它 - // // 因此是跳过分裂矩形的,以免造成无限递归 - // var minNode = GetMinNode(ent); - // minNode.Contents.Add(ent); - // return minNode; - // } - -#if true2 - // 方案二: - // 内容数超过才分裂,防止树深度过高,但是多选过滤时候慢一点 - if (Contents.Count > QuadTreeEvn.QuadTreeContentsCountSplit) - { - // 分裂出四个子节点 - if (_nodesIsEmpty) - { - CreateChildren(); - // 分裂之后将当前层的内容扔到四个子节点, - // 如果被压着,那么就不会扔到下面 - for (int i = Contents.Count - 1; i >= 0; i--) - { - var minNode = GetMinNode(Contents[i].Box); - minNode.Contents.Add(Contents[i]); - Contents.RemoveAt(i); - } - } - else - { - // 没有分裂的话,就递归 - // 退出递归:4个节点都不完全包含,内容就是他们的父亲 - var nodes = _Nodes; - for (int i = 0; i < nodes.Length; i++) - { - var node = nodes[i]; - if (node is null) - continue; - - // 这里需要中断.(匿名方法ForEach无法中断,会造成父节点加入内容) - if (node.Contains(ent)) - return node.Insert(ent); - } - } - } -#else - // 方案一:分裂到最细节点 - - // 分裂出四个子节点 - if (NodesIsEmpty) - CreateChildren(); - - // 4个子节点开始递归 - // 退出递归:4个节点都不完全包含,内容就是他们的父亲 - var nodes = Nodes; - for (int i = 0; i < nodes.Length; i++) - { - var node = nodes[i]; - if (node is null) - continue; - // 这里需要中断.(匿名方法ForEach无法中断,会造成父节点加入内容) - if (node.Contains(ent)) - return node.Insert(ent); - } -#endif - - // 为什么要用容器? - // 相同包围盒或者四叉树分割线压着多个. - this.Contents.Add(ent); - return this; - } - - /// - /// 创建子节点 - /// - void CreateChildren() - { - // 最小面积控制节点深度,但是这样可能导致树分成高,引起爆栈 - if (Depth > QuadTreeEvn.QuadTreeMaximumDepth) - return; - var recs = RectSplit(this); - var de = Depth + 1; - RightTopTree = new QuadTreeNode(recs[0], this, de); - LeftTopTree = new QuadTreeNode(recs[1], this, de); - LeftBottomTree = new QuadTreeNode(recs[2], this, de); - RightBottomTree = new QuadTreeNode(recs[3], this, de); - } - - /// - /// 矩形分裂为四个 - /// - /// - /// - static Rect[] RectSplit(Rect box) - { - var halfWidth = box.Width / 2.0; - var halfHeight = box.Height / 2.0; - - var upperRight = new Rect(box._X + halfWidth, box._Y + halfHeight, box._Right, box._Top); - var upperLeft = new Rect(box._X, box._Y + halfHeight, box._Right - halfWidth, box._Top); - var lowerleft = new Rect(box._X, box._Y, box._Right - halfWidth, box._Top - halfHeight);// 基础 - var lowerRight = new Rect(box._X + halfWidth, box._Y, box._Right, box._Top - halfHeight); - - // 依照象限顺序输出 - return new Rect[] { upperRight, upperLeft, lowerleft, lowerRight }; - } - #endregion - - #region 删 - /// - /// 删除图元 - /// - /// 根据图元删除 - public bool Remove(TEntity easeEnt) - { - // 通过图元id删除无疑是非常低效的, - // 1.相当于在所有的容器查找它,但是移除只会移除一次, - // 因此必须要求图元只会加入一次,才能中断检索剩余分支. - // 2.这个代价还是太高,因此我们还是要默认条件,图元载入一次之后,不再改动. - // 3.不再改动也不太合理,因为cad图元还是可以修改的 - - // 1.处理内容 - if (Contents.Remove(easeEnt)) - { - if (CountSubTree == 0) - this.Clear(this); - return true; - } - - // 2.递归子节点移除 - var nodes = Nodes; - for (int i = 0; i < nodes.Length; i++) - { - var node = nodes[i]; - if (node is null) - continue; - if (node.Remove(easeEnt)) // 递归进入子节点删除内容 - return true; // 删除成功就中断其他节点的搜索 - } - return false; - } - - /// - /// 递归进入最下层节点,然后开始清理 - /// - /// - void Clear(QuadTreeNode node) - { - var nodes = Nodes; - for (int i = 0; i < nodes.Length; i++) - nodes[i]?.Clear(nodes[i]); - - node.Contents.Clear(); - // node.Contents = null;// 重复加入时候会出错 - node.RightTopTree = null; - node.LeftTopTree = null; - node.LeftBottomTree = null; - node.RightBottomTree = null; - node.Parent = null; - // node.Box = zoreRect; - } - - /// - /// 删除子节点内容 - /// - /// 根据范围删除 - public void Remove(Rect queryArea) - { - // 本节点内容移除 - if (Contents is not null && Contents.Count > 0)// 从最上层的根节点开始进入 - { - for (int i = Contents.Count - 1; i >= 0; i--) - { - var ent = Contents[i]; - // 移除之后,如果容器是0,那么这里不能直接 Contents=null, - // 因为此节点下面可能还有节点, - // 需要判断了其后数量0才可以清理. - // 否则其后还有内容,那么此节点就是仍然可以用的. - if (queryArea.Contains(ent)) - Contents.Remove(ent); - } - } - - // 同插入一样 - // 跳到指定节点再搜索这个节点下面的图元 - var nodes = Nodes; - for (int i = 0; i < nodes.Length; i++) - { - var node = nodes[i]; - if (node is null) - continue; - if (node.NodesIsEmpty) - continue; - - // 此节点边界 完全包含 查询区域,则转到该节点,并跳过其余节点(打断此循环) - if (node.Contains(queryArea)) - { - node.Remove(queryArea); - break; - } - // 查询区域 完全包含 此节点边界,提取此节点全部内容 - // 跳过分析碰撞,并继续循环搜索其他节点 - if (queryArea.Contains(node)) - { - node.Clear(node); - continue; - } - // 查询区域 与 此节点四边形边线碰撞 查询该四边形中,并继续循环搜索其他节点 - // 1,角点碰撞 2,边界碰撞 - if (node.IntersectsWith(queryArea)) - node.Remove(queryArea); - } - - // 本节点内容移除之后,旗下还有内容的话, - // 会跳过此处,再进入子节点进行递归,直到最后一个节点 - if (CountSubTree == 0) - Clear(this); - } - #endregion - - #region 查 - /// - /// 查询范围内的实体 - /// - /// 查询矩形 - /// - public void Query(Rect queryArea, List results) - { - GetCurrentContents(queryArea, results); - - // 遍历子节点 - var nodes = Nodes; - for (int i = 0; i < nodes.Length; i++) - { - var node = nodes[i]; - if (node is null) - continue; - // 子节点的4个子节点都是空的, - // 那么表示元素会在子节点这一层啊... - if (node.NodesIsEmpty) - continue; - - // 此节点边界 完全包含 查询区域,则转到该节点,并跳过其余节点(打断此循环) - if (node.Contains(queryArea)) - { - node.Query(queryArea, results); - break; - } - // 查询区域 完全包含 此节点边界,提取此节点全部内容 - // 跳过分析碰撞,并继续循环搜索其他节点 - if (queryArea.Contains(node)) - { - node.ContentsSubTree(results); - continue; - } - // 查询区域 与 此节点四边形边线碰撞 查询该四边形中,并继续循环搜索其他节点 - // 1,角点碰撞 2,边界碰撞 - if (node.IntersectsWith(queryArea)) - node.Query(queryArea, results); - } - } - - /// - /// 获取本节点内容 - /// - /// - /// - void GetCurrentContents(Rect queryArea, List results) - { - // 遍历当前节点内容,加入方式取决于碰撞模式 - if (QuadTreeEvn.SelectMode == QuadTreeSelectMode.IntersectsWith) - { - for (int i = 0; i < Contents.Count; i++) - { - var ent = Contents[i]; - if (queryArea.IntersectsWith(ent)) - results.Add(ent); - } - } - else - { - for (int i = 0; i < Contents.Count; i++) - { - var ent = Contents[i]; - if (queryArea.Contains(ent)) - results.Add(ent); - } - } - } - - /// - /// 找临近图元 - /// - /// 查找矩形 - /// - public TEntity? FindNearEntity(Rect queryArea) - { - TEntity? resultEntity = default; - // 1.找到 查找矩形 所在的节点,利用此节点的矩形. - var queryNode = GetMinNode(queryArea); - var queryAreaCenter = queryArea.CenterPoint; - - // 2.从根开始搜索 - // 如果搜索父亲的父亲的...内容群,它不是距离最近的,只是节点(亲属关系)最近 - // 储存找过的<图元,距离> - var entDic = new Dictionary(); - - var old = QuadTreeEvn.SelectMode; - QuadTreeEvn.SelectMode = QuadTreeSelectMode.IntersectsWith; - while (true) - { - // 循环找父节点大小 - var hw = queryNode.Width / 2.0; - var hh = queryNode.Height / 2.0; - // 3.利用选区中心扩展一个节点边界大小的矩形.从而选择图元 - // 再判断图元的与目标的距离,找到最小距离,即为最近 - var minPt = new Point2d(queryAreaCenter.X - hw, queryAreaCenter.Y - hh); - var maxPt = new Point2d(queryAreaCenter.X + hw, queryAreaCenter.Y + hh); - var ents = new List(); - Query(new Rect(minPt, maxPt), ents); - for (int i = 0; i < ents.Count; i++) - { - var ent = ents[i]; - if (entDic.ContainsKey(ent)) - continue; - var dis = ent.CenterPoint.GetDistanceTo(queryAreaCenter); - if (dis > 1e-6)// 剔除本身 - entDic.Add(ent, dis); - } - if (entDic.Count > 0) - { - resultEntity = entDic.OrderBy(a => a.Value).First().Key; - break; - } - if (queryNode.Parent is null)// 最顶层就退出 - break; - queryNode = queryNode.Parent;// 利用父节点矩形进行变大选区 - } - QuadTreeEvn.SelectMode = old; - return resultEntity; - } - - /// - /// 找临近节点的图元 - /// - /// 查找矩形 - /// 查找什么方向 - /// - [Obsolete("找附近节点的并不是最近的图元")] - public TEntity? FindNeibor(Rect queryArea, QuadTreeFindMode findMode) - { - TEntity? resultEntity = default; - // 1.找到 查找矩形 所在的节点,利用此节点的矩形. - // 2.利用节点矩形是分裂的特点,边和边必然贴合. - // 3.找到方向 findMode 拥有的节点,然后查找节点的内容 - var queryNode = GetMinNode(queryArea); - - bool whileFlag = true; - // 同一个节点可能包含邻居,因为四叉树的加入是图元压线, - // 那么就在这里搜就得了,用中心点决定空间位置 - // 但是本空间的图元可能都比它矮,无法满足条件 - if (queryNode.CountSubTree > 1) - { - resultEntity = GetNearestNeighbor(queryNode, findMode, queryArea); - if (resultEntity is null || resultEntity.CenterPoint == queryArea.CenterPoint) - whileFlag = true; - else - whileFlag = false; - } - - while (whileFlag) - { - // 同一个父节点是临近的,优先获取 兄弟节点 的内容. - // 循环了第二次是北方兄弟的节点, - // 但是这不是一个找到临近图元的方法, - // 因为临近的可能是父亲的父亲的父亲...另一个函数 FindNearEntity 写 - // 本方案也仅仅作为找北方节点 - var parent = queryNode.Parent; - if (parent is not null) - { - switch (findMode) - { - case QuadTreeFindMode.Top: - { - // 下格才获取上格,否则导致做了无用功,上格就直接获取邻居了 - if (parent.LeftBottomTree == queryNode || parent.RightBottomTree == queryNode) - resultEntity = GetNearestNeighbor(parent, findMode, queryArea); - } - break; - case QuadTreeFindMode.Bottom: - { - if (parent.LeftTopTree == queryNode || parent.RightTopTree == queryNode) - resultEntity = GetNearestNeighbor(parent, findMode, queryArea); - } - break; - case QuadTreeFindMode.Left: - { - if (parent.RightTopTree == queryNode || parent.RightBottomTree == queryNode) - resultEntity = GetNearestNeighbor(parent, findMode, queryArea); - } - break; - case QuadTreeFindMode.Right: - { - if (parent.LeftTopTree == queryNode || parent.LeftBottomTree == queryNode) - resultEntity = GetNearestNeighbor(parent, findMode, queryArea); - } - break; - } - } - if (resultEntity is not null) - break; - - // 通过 所在节点 找 邻居节点, - // 拿到 邻居节点 下面的所有内容(图元) - // 内容可能是空的,再从邻居那往北找...如果找到了四叉树最外层,仍然没有内容,退出循环 - var neiborNode = FindNeiborNode(queryNode, findMode); - if (neiborNode is null) - continue; - if (neiborNode.CountSubTree > 0) - { - resultEntity = GetNearestNeighbor(neiborNode, findMode, queryArea); - break; - } - if (neiborNode.Parent is null)// 如果找到了四叉树最外层,仍然没有内容,退出循环 - break; - queryNode = neiborNode; - } - - return resultEntity; - } - - /// - /// 查找节点的(本内容和子内容)与(查找面积)矩形中点对比,找到最近一个内容 - /// - /// 查找面积 - /// 查找方向 - /// 查找节点 - /// - static TEntity? GetNearestNeighbor(QuadTreeNode queryNode, - QuadTreeFindMode findMode, - Rect queryArea) - { - TEntity? results = default; - - var lst = new List(); - var qcent = queryArea.CenterPoint; - - switch (findMode) - { - case QuadTreeFindMode.Top: - { - // 取出Y比queryArea的还大的一个,是最近的那个 - var qy = qcent.Y; - queryNode.ContentsSubTree(lst); - lst.ForEach(ent => - { - if (ent.CenterPoint.Y > qy) - lst.Add(ent); - }); - lst = lst.OrderBy(ent => ent.CenterPoint.Y).ToList(); - } - break; - case QuadTreeFindMode.Bottom: - { - var qy = qcent.Y; - queryNode.ContentsSubTree(lst); - lst.ForEach(ent => - { - if (ent.CenterPoint.Y < qy) - lst.Add(ent); - }); - lst = lst.OrderByDescending(ent => ent.CenterPoint.Y).ToList(); - } - break; - case QuadTreeFindMode.Left: - { - var qx = qcent.Y; - queryNode.ContentsSubTree(lst); - lst.ForEach(ent => - { - if (ent.CenterPoint.X > qx) - lst.Add(ent); - }); - lst = lst.OrderBy(ent => ent.CenterPoint.X).ToList(); - } - break; - case QuadTreeFindMode.Right: - { - var qx = qcent.Y; - queryNode.ContentsSubTree(lst); - lst.ForEach(ent => - { - if (ent.CenterPoint.X < qx) - lst.Add(ent); - }); - lst = lst.OrderByDescending(ent => ent.CenterPoint.X).ToList(); - } - break; - } - - if (lst.Count > 0) - return lst[0];// 可能就是本体重叠 - return results; - } - - /// - /// 找包含它的最小分支 - /// - /// 查询的矩形 - /// 节点 - QuadTreeNode GetMinNode(Rect queryArea) - { - var nodes = Nodes; - for (int i = 0; i < nodes.Length; i++) - { - var node = nodes[i]; - if (node is null) - continue; - - // 边界包含查询面积,那么再递归查询, - // 直到最后四个都不包含,那么上一个就是图元所在节点 - if (node.Contains(queryArea)) - return node.GetMinNode(queryArea);// 中断后面的范围,才可以返回正确的 - } - return this; - } - - /// - /// 四叉树找邻居节点(相同或更大) - /// - /// 源节点 - /// 方向 - /// - QuadTreeNode? FindNeiborNode(QuadTreeNode tar, QuadTreeFindMode findMode) - { - var parent = tar.Parent; - if (parent is null) - return null; - switch (findMode) - { - case QuadTreeFindMode.Top: - { - // 判断当前节点在父节点的位置,如果是在 下格 就取对应的 上格 - if (tar == parent.LeftBottomTree) - return parent.LeftTopTree; - if (tar == parent.RightBottomTree) - return parent.RightTopTree; - // 否则就是上格 - // 找父节点的北方邻居..也就是在爷节点上面找 - // 递归,此时必然是 下格,就必然返回 上格,然后退出递归 - var parentNeibor = FindNeiborNode(parent, QuadTreeFindMode.Top); - // 父节点的北方邻居 无 子节点 - if (parentNeibor is null || parentNeibor.RightTopTree is null) - return parentNeibor;// 返回父节点的北方邻居,比较大 - // 父节点的北方邻居 有 子节点,剩下条件就只有这两 - - // 如果直接返回,那么是(相同或更大), - // 而找邻近图元需要的是这个(相同或更大)下面的图元,在外面对这个格子内图元用坐标分析就好了 - if (tar == parent.LeftTopTree) - return parentNeibor.LeftBottomTree; - return parentNeibor.RightBottomTree; - } - case QuadTreeFindMode.Bottom: - { - if (tar == parent.LeftTopTree) - return parent.LeftBottomTree; - if (tar == parent.RightTopTree) - return parent.RightBottomTree; - var parentNeibor = FindNeiborNode(parent, QuadTreeFindMode.Bottom); - if (parentNeibor is null || parentNeibor.RightTopTree is null) - return parentNeibor; - if (tar == parent.LeftBottomTree) - return parentNeibor.LeftTopTree; - return parentNeibor.RightTopTree; - } - case QuadTreeFindMode.Right: - { - if (tar == parent.LeftTopTree) - return parent.RightTopTree; - if (tar == parent.LeftBottomTree) - return parent.RightBottomTree; - var parentNeibor = FindNeiborNode(parent, QuadTreeFindMode.Right); - if (tar == parent.RightTopTree) - return parentNeibor?.LeftTopTree; - return parentNeibor?.LeftBottomTree; - } - case QuadTreeFindMode.Left: - { - if (tar == parent.RightTopTree) - return parent.LeftTopTree; - if (tar == parent.RightBottomTree) - return parent.LeftBottomTree; - var parentNeibor = FindNeiborNode(parent, QuadTreeFindMode.Left); - if (tar == parent.LeftTopTree) - return parentNeibor?.RightTopTree; - return parentNeibor?.RightBottomTree; - } - } - return null; - } - #endregion - - #region 改 - /// - /// 所有的点归类到最小包围它的空间 - /// - // public void PointsToMinNode() - // { - // ForEach(node => - // { - // for (int i = 0; i < node.Contents.Count; i++) - // { - // var ent = node.Contents[i]; - // if (ent.IsPoint) - // { - // // 如果最小包含!=当前,就是没有放在最适合的位置 - // var queryNode = GetMinNode(ent); - // if (queryNode != node) - // { - // node.Remove(ent); - // queryNode.Contents.Add(ent); - // } - // } - // } - // return false; - // }); - // } - #endregion - - #region 方法 - /// - /// 递归全部节点(提供给根用的,所以是全部) - /// - /// QTAction - public bool ForEach(QuadTree.QTAction action) - { - // 执行本节点 - if (action(this)) - return true; - - // 递归执行本节点的子节点 - var nodes = Nodes; - for (int i = 0; i < nodes.Length; i++) - { - var node = nodes[i]; - if (node is null) - continue; - if (node.ForEach(action)) - break; - } - return false; - } - #endregion -} \ No newline at end of file diff --git a/src/IFoxCAD.Cad/Algorithms/QuadTree/QuadTreeSelectMode.cs b/src/IFoxCAD.Cad/Algorithms/QuadTree/QuadTreeSelectMode.cs deleted file mode 100644 index 2cc2e37..0000000 --- a/src/IFoxCAD.Cad/Algorithms/QuadTree/QuadTreeSelectMode.cs +++ /dev/null @@ -1,21 +0,0 @@ -namespace IFoxCAD.Cad; - -/// -/// 四叉树选择模式 -/// -public enum QuadTreeSelectMode -{ - IntersectsWith, // 碰撞到就选中 - Contains, // 全包含才选中 -} - -/// -/// 四叉树查找方向 -/// -public enum QuadTreeFindMode -{ - Top = 1, // 上 - Bottom = 2, // 下 - Left = 4, // 左 - Right = 8, // 右 -} \ No newline at end of file diff --git a/src/IFoxCAD.Cad/Algorithms/QuadTree/Rect.cs b/src/IFoxCAD.Cad/Algorithms/QuadTree/Rect.cs deleted file mode 100644 index 7639104..0000000 --- a/src/IFoxCAD.Cad/Algorithms/QuadTree/Rect.cs +++ /dev/null @@ -1,605 +0,0 @@ -using System.Diagnostics; - -namespace IFoxCAD.Cad; - -/// -/// Linq Distinct 消重比较两点在容差范围内就去除 -/// -public class TolerancePoint2d : IEqualityComparer -{ - readonly double _tolerance; - public TolerancePoint2d(double tolerance = 1e-6) - { - _tolerance = tolerance; - } - - public bool Equals(Point2d a, Point2d b)// Point3d是struct不会为null - { - /*默认规则是==是0容差,Eq是有容差*/ - // 方形限定 - // 在 0~1e-6 范围实现 圆形限定 则计算部分在浮点数6位后,没有啥意义 - // 在 0~1e-6 范围实现 从时间和CPU消耗来说,圆形限定 都没有 方形限定 的好 - if (_tolerance <= 1e-6) - return Math.Abs(a.X - b.X) <= _tolerance && Math.Abs(a.Y - b.Y) <= _tolerance; - - // 圆形限定 - // DistanceTo 分别对XYZ进行了一次乘法,也是总数3次乘法,然后求了一次平方根 - // (X86.CPU.FSQRT指令用的牛顿迭代法/软件层面可以使用快速平方根....我还以为CPU会采取快速平方根这样的取表操作) - return a.IsEqualTo(b, new Tolerance(_tolerance, _tolerance)); - } - - public int GetHashCode(Point2d obj) - { - // 结构体直接返回 obj.GetHashCode(); Point3d ToleranceDistinct3d - // 因为结构体是用可值叠加来判断?或者因为结构体兼备了一些享元模式的状态? - // 而类是构造的指针,所以取哈希值要改成x+y+z..s给Equals判断用,+是会溢出,所以用^ - return (int)obj.X ^ (int)obj.Y;// ^ (int)obj.Z; - } -} - - -[Serializable] -[StructLayout(LayoutKind.Sequential)] -[DebuggerDisplay("{DebuggerDisplay,nq}")] -[DebuggerTypeProxy(typeof(Rect))] -public class Rect : IEquatable, IComparable -{ - [DebuggerBrowsable(DebuggerBrowsableState.Never)] - private string DebuggerDisplay => ToString("f4"); - -#pragma warning disable CA2211 // 非常量字段应当不可见 - public static TolerancePoint2d RectTolerance = new(1e-6); - public static Tolerance CadTolerance = new(1e-6, 1e-6); -#pragma warning restore CA2211 // 非常量字段应当不可见 - - #region 字段 - // 这里的成员不要用{get}封装成属性,否则会导致跳转了一次函数, - // 10w图元将会从187毫秒变成400毫秒 - // 不用 protected 否则子类传入Rect对象进来无法用 - internal double _X; - internal double _Y; - internal double _Right; - internal double _Top; - #endregion - - #region 成员 - public double X => _X; - public double Y => _Y; - public double Left => _X; - public double Bottom => _Y; - public double Right => _Right; - public double Top => _Top; - - public double Width => _Right - _X; - public double Height => _Top - _Y; - public double Area - { - get - { - var ar = (_Right - _X) * (_Top - _Y); - return ar < 1e-10 ? 0 : ar; - } - } - - public Point2d MinPoint => LeftLower; - public Point2d MaxPoint => RightUpper; - public Point2d CenterPoint => Midst; - - /// - /// 左下Min - /// - public Point2d LeftLower => new(_X, _Y); - - /// - /// 左中 - /// - public Point2d LeftMidst => new(_X, Midst.Y); - - /// - /// 左上 - /// - public Point2d LeftUpper => new(_X, _Top); - - /// - /// 右上Max - /// - public Point2d RightUpper => new(_Right, _Top); - - /// - /// 右中 - /// - public Point2d RightMidst => new(_Right, Midst.Y); - - /// - /// 右下 - /// - public Point2d RightBottom => new(_Right, _Y); - - /// - /// 中间 - /// - public Point2d Midst => new(((_Right - _X) * 0.5) + _X, ((_Top - _Y) * 0.5) + _Y); - - /// - /// 中上 - /// - public Point2d MidstUpper => new(Midst.X, _Top); - - /// - /// 中下 - /// - public Point2d MidstBottom => new(Midst.X, _Y); - - /// - /// 是一个点 - /// 水平或垂直直线包围盒是面积是0,所以面积是0不一定是点 - /// - public bool IsPoint => Math.Abs(_X - _Right) < 1e-10 && Math.Abs(_Y - _Top) < 1e-10; - #endregion - - #region 构造 - public Rect() { } - - /// - /// 矩形类 - /// - /// 左 - /// 下 - /// 右 - /// 上 - public Rect(double left, double bottom, double right, double top) - { - _X = left; - _Y = bottom; - _Right = right; - _Top = top; - } - - /// - /// 构造矩形类 - /// - /// - /// - /// 是否检查大小 - public Rect(Point2d p1, Point2d p3, bool check = false) - { - if (check) - { - _X = Math.Min(p1.X, p3.X); - _Y = Math.Min(p1.Y, p3.Y); - _Right = Math.Max(p1.X, p3.X); - _Top = Math.Max(p1.Y, p3.Y); - } - else - { - _X = p1.X; - _Y = p1.Y; - _Right = p3.X; - _Top = p3.Y; - } - } - #endregion - - #region 重载运算符_比较 - public override bool Equals(object? obj) - { - return this.Equals(obj as Rect); - } - public bool Equals(Rect? b) - { - return this.Equals(b, 1e-6);/*默认规则是==是0容差,Eq是有容差*/ - } - public static bool operator !=(Rect? a, Rect? b) - { - return !(a == b); - } - public static bool operator ==(Rect? a, Rect? b) - { - // 此处地方不允许使用==null,因为此处是定义 - if (b is null) - return a is null; - else if (a is null) - return false; - if (ReferenceEquals(a, b))// 同一对象 - return true; - - return a.Equals(b, 0); - } - - /// - /// 比较核心 - /// - public bool Equals(Rect? b, double tolerance = 1e-6) - { - if (b is null) - return false; - if (ReferenceEquals(this, b)) // 同一对象 - return true; - - return Math.Abs(_X - b._X) < tolerance && - Math.Abs(_Right - b._Right) < tolerance && - Math.Abs(_Top - b._Top) < tolerance && - Math.Abs(_Y - b._Y) < tolerance; - } - - public override int GetHashCode() - { - return (((int)_X ^ (int)_Y).GetHashCode() ^ (int)_Right).GetHashCode() ^ (int)_Top; - } - #endregion - - #region 包含 - public bool Contains(Point2d Point2d) - { - return Contains(Point2d.X, Point2d.Y); - } - public bool Contains(double x, double y) - { - return _X <= x && x <= _Right && - _Y <= y && y <= _Top; - } - - /// - /// 四个点都在内部就是包含 - /// - /// - /// - public bool Contains(Rect rect) - { - return _X <= rect._X && rect._Right <= _Right && - _Y <= rect._Y && rect._Top <= _Top; - } - - /// - /// 一个点在内部就是碰撞 - /// - /// - /// - public bool IntersectsWith(Rect rect) - { - return rect._X <= _Right && _X <= rect._Right && - rect._Top >= _Y && rect._Y <= _Top; - } - #endregion - - #region 方法 - /// - /// 获取共点 - /// - /// - public Point2d[] GetCommonPoint(Rect other) - { - return ToPoints().Intersect(other.ToPoints(), RectTolerance).ToArray(); - } - - public Point2d[] ToPoints() - { - Point2d a = MinPoint;// min - Point2d b = new(_Right, _Y); - Point2d c = MaxPoint;// max - Point2d d = new(_X, _Top); - return new Point2d[] { a, b, c, d }; - } - - public (Point2d boxMin, Point2d boxRigthDown, Point2d boxMax, Point2d boxLeftUp) ToPoints4() - { - Point2d a = MinPoint;// min - Point2d b = new(_Right, _Y); - Point2d c = MaxPoint;// max - Point2d d = new(_X, _Top); - return (a, b, c, d); - } - - /// - /// 四周膨胀 - /// - /// - public Rect Expand(double d) - { - return new Rect(_X - d, _Y - d, _Right + d, _Top + d); - } - - /// - /// 是否矩形(带角度) - /// - /// - /// - public static bool IsRectAngle(List? ptList, double tolerance = 1e-8) - { - if (ptList == null) - throw new ArgumentNullException(nameof(ptList)); - - var pts = ptList.ToList(); - /* 消重,不这里设置,否则这不是一个正确的单元测试 - * // var ptList = pts.Distinct().ToList(); - * var ptList = pts.DistinctExBy((a, b) => a.DistanceTo(b) < 1e-6).ToList(); - */ - if (ptList.Count == 5) - { - // 首尾点相同移除最后 - if (pts[0].IsEqualTo(pts[^1], CadTolerance)) - pts.RemoveAt(pts.Count - 1); - } - if (pts.Count != 4) - return false; - - // 最快的方案 - // 点乘求值法:(为了处理 正梯形/平行四边形 需要三次) - // 这里的容差要在1e-8内,因为点乘的三次浮点数乘法会令精度变低 - var dot = DotProductValue(pts[0], pts[1], pts[3]); - if (Math.Abs(dot) < tolerance) - { - dot = DotProductValue(pts[1], pts[2], pts[0]); - if (Math.Abs(dot) < tolerance) - { - dot = DotProductValue(pts[2], pts[3], pts[1]); - return Math.Abs(dot) < tolerance; - } - } - return false; - } - - /// - /// 点积,求值 - /// 1.是两个向量的长度与它们夹角余弦的积 - /// 2.求四个点是否矩形使用 - /// - /// 原点 - /// 点 - /// 点 - /// 0方向相同,夹角0~90度;=0相互垂直;<0方向相反,夹角90~180度]]> - static double DotProductValue(Point2d o, Point2d a, Point2d b) - { - var oa = o.GetVectorTo(a); - var ob = o.GetVectorTo(b); - return (oa.X * ob.X) + (oa.Y * ob.Y); - } - - /// - /// 是否轴向矩形(无角度) - /// - public static bool IsRect(List? ptList, double tolerance = 1e-10) - { - if (ptList == null) - throw new ArgumentNullException(nameof(ptList)); - - var pts = ptList.ToList(); - if (ptList.Count == 5) - { - // 首尾点相同移除最后 - if (pts[0].IsEqualTo(pts[^1], CadTolerance)) - pts.RemoveAt(pts.Count - 1); - } - if (pts.Count != 4) - return false; - - return Math.Abs(pts[0].X - pts[3].X) < tolerance && - Math.Abs(pts[0].Y - pts[1].Y) < tolerance && - Math.Abs(pts[1].X - pts[2].X) < tolerance && - Math.Abs(pts[2].Y - pts[3].Y) < tolerance; - } - - /// - /// 获取点集的包围盒的最小点和最大点(无角度) - /// - /// - public static (Point2d boxMin, Point2d boxMax) GetMinMax(IEnumerable pts) - { - var xMin = double.MaxValue; - var xMax = double.MinValue; - var yMin = double.MaxValue; - var yMax = double.MinValue; - // var zMin = double.MaxValue; - // var zMax = double.MinValue; - - pts.ForEach(p => { - xMin = Math.Min(p.X, xMin); - xMax = Math.Max(p.X, xMax); - yMin = Math.Min(p.Y, yMin); - yMax = Math.Max(p.Y, yMax); - // zMin = Math.Min(p.Z, zMin); - // zMax = Math.Max(p.Z, zMax); - }); - return (new Point2d(xMin, yMin), new Point2d(xMax, yMax)); - } - - /// - /// 矩形点序逆时针排列,将min点[0],max点是[3](带角度) - /// - /// - /// - public static bool RectAnglePointOrder(List? pts) - { - if (pts == null) - throw new ArgumentNullException(nameof(pts)); - - if (!Rect.IsRectAngle(pts)) - return false; - - // 获取min和max点(非包围盒) - pts = pts.OrderBy(a => a.X).ThenBy(a => a.Y).ToList(); - var minPt = pts.First(); - var maxPt = pts.Last(); - var link = new LoopList(); - link.AddRange(pts); - - pts.Clear(); - // 排序这四个点,左下/右下/右上/左上 - var node = link.Find(minPt); - for (int i = 0; i < 4; i++) - { - pts.Add(node!.Value); - node = node.Next; - } - // 保证是逆时针 - var isAcw = CrossAclockwise(pts[0], pts[1], pts[2]); - if (!isAcw) - (pts[3], pts[1]) = (pts[1], pts[3]); - return true; - } - - /// - /// 叉积,二维叉乘计算 - /// - /// 传参是向量,表示原点是0,0 - /// 传参是向量,表示原点是0,0 - /// 其模为a与b构成的平行四边形面积 - static double Cross(Vector2d a, Vector2d b) - { - return a.X * b.Y - a.Y * b.X; - } - - /// - /// 叉积,二维叉乘计算 - /// - /// 原点 - /// oa向量 - /// ob向量,此为判断点 - /// 返回值有正负,表示绕原点四象限的位置变换,也就是有向面积 - static double Cross(Point2d o, Point2d a, Point2d b) - { - return Cross(o.GetVectorTo(a), o.GetVectorTo(b)); - } - - /// - /// 叉积,逆时针方向为真 - /// - /// 直线点1 - /// 直线点2 - /// 判断点 - /// b点在oa的逆时针 - static bool CrossAclockwise(Point2d o, Point2d a, Point2d b) - { - return Cross(o, a, b) > -1e-6;// 浮点数容差考虑 - } - -#if !WinForm - public Entity ToPolyLine() - { - var bv = new List(); - var pts = ToPoints(); - Polyline pl = new(); - pl.SetDatabaseDefaults(); - pts.ForEach((i, vertex) => { - pl.AddVertexAt(i, vertex, 0, 0, 0); - }); - return pl; - } -#endif - - /// - /// 列扫碰撞检测(碰撞算法) - /// 比四叉树还快哦~ - /// - /// - /// 继承Rect的集合 - /// 先处理集合每一个成员;返回true就跳过后续委托 - /// 碰撞,返回两个碰撞的成员;返回true就跳过后续委托 - /// 后处理集合每一个成员 - public static void XCollision(List box, - Func firstProcessing, - Func collisionProcessing, - Action lastProcessing) where T : Rect - { - // 先排序X:不需要Y排序,因为Y的上下浮动不共X .ThenBy(a => a.Box.Y) - // 因为先排序就可以有序遍历x区间,超过就break,达到更快 - box = box.OrderBy(a => a._X).ToList(); - - // 遍历所有图元 - for (int i = 0; i < box.Count; i++) - { - var oneRect = box[i]; - if (firstProcessing(oneRect)) - continue; - - bool actionlast = true; - - // 搜索范围要在 one 的头尾中间的部分 - for (int j = i + 1; j < box.Count; j++) - { - var twoRect = box[j]; - // x碰撞:矩形2的Left 在 矩形1[Left-Right]闭区间;穿过的话,也必然有自己的Left因此不需要处理 - if (oneRect._X <= twoRect._X && twoRect._X <= oneRect._Right) - { - // y碰撞,那就是真的碰撞了 - if ((oneRect._Top >= twoRect._Top && twoRect._Top >= oneRect._Y) /*包容上边*/ - || (oneRect._Top >= twoRect._Y && twoRect._Y >= oneRect._Y) /*包容下边*/ - || (twoRect._Top >= oneRect._Top && oneRect._Y >= twoRect._Y)) /*穿过*/ - { - if (collisionProcessing(oneRect, twoRect)) - actionlast = false; - } - // 这里想中断y高过它的无意义比较, - // 但是必须排序Y,而排序Y必须同X,而这里不是同X(而是同X区间),所以不能中断 - // 而做到X区间排序,就必须创造一个集合,再排序这个集合, - // 会导致每个图元都拥有一次X区间集合,开销更巨大(因此放弃). - } - else - break;// 因为已经排序了,后续的必然超过 x碰撞区间 - } - - if (actionlast) - lastProcessing(oneRect); - } - } - - #endregion - - #region 转换类型 -#if !WinForm - // 隐式转换(相当于是重载赋值运算符) - // public static implicit operator Rect(System.Windows.Rect rect) - // { - // return new Rect(rect.Left, rect.Bottom, rect.Right, rect.Top); - // } - public static implicit operator Rect(System.Drawing.RectangleF rect) - { - return new Rect(rect.Left, rect.Bottom, rect.Right, rect.Top); - } - public static implicit operator Rect(System.Drawing.Rectangle rect) - { - return new Rect(rect.Left, rect.Bottom, rect.Right, rect.Top); - } -#endif - - #region ToString - public sealed override string ToString() - { - return ToString(null, null); - } - public string ToString(IFormatProvider? provider) - { - return ToString(null, provider); - } - public string ToString(string? format = null, IFormatProvider? formatProvider = null) - { - return $"({_X.ToString(format, formatProvider)},{_Y.ToString(format, formatProvider)})," + - $"({_Right.ToString(format, formatProvider)},{_Top.ToString(format, formatProvider)})"; - - // return $"X={_X.ToString(format, formatProvider)}," + - // $"Y={_Y.ToString(format, formatProvider)}," + - // $"Right={_Right.ToString(format, formatProvider)}," + - // $"Top={_Top.ToString(format, formatProvider)}"; - } - - /*为了红黑树,加入这个*/ - public int CompareTo(Rect rect) - { - if (rect == null) - return -1; - if (_X < rect._X) - return -1; - else if (_X > rect._X) - return 1; - else if (_Y < rect._Y)/*x是一样的*/ - return -1; - else if (_Y > rect._Y) - return 1; - return 0;/*全部一样*/ - } - #endregion - - #endregion - - -} diff --git a/src/IFoxCAD.Cad/ExtensionMethod/BulgeVertexWidth.cs b/src/IFoxCAD.Cad/ExtensionMethod/BulgeVertexWidth.cs deleted file mode 100644 index 1f9e08d..0000000 --- a/src/IFoxCAD.Cad/ExtensionMethod/BulgeVertexWidth.cs +++ /dev/null @@ -1,85 +0,0 @@ -namespace IFoxCAD.Cad; - -/// -/// 多段线的顶点,凸度,头宽,尾宽 -/// -[Serializable] -public class BulgeVertexWidth -{ - /// - /// 顶点X - /// - public double X; - /// - /// 顶点Y - /// - public double Y; - /// - /// 凸度 - /// - public double Bulge; - /// - /// 头宽 - /// - public double StartWidth; - /// - /// 尾宽 - /// - public double EndWidth; - - public Point2d Vertex => new(X, Y); - - public BulgeVertexWidth() { } - - /// - /// 多段线的顶点,凸度,头宽,尾宽 - /// - public BulgeVertexWidth(double vertex_X, double vertex_Y, - double bulge = 0, - double startWidth = 0, - double endWidth = 0) - { - X = vertex_X; - Y = vertex_Y; - Bulge = bulge; - StartWidth = startWidth; - EndWidth = endWidth; - } - - /// - /// 多段线的顶点,凸度,头宽,尾宽 - /// - public BulgeVertexWidth(Point2d vertex, - double bulge = 0, - double startWidth = 0, - double endWidth = 0) - : this(vertex.X, vertex.Y, bulge, startWidth, endWidth) - { } - - /// - /// 多段线的顶点,凸度,头宽,尾宽 - /// - public BulgeVertexWidth(BulgeVertex bv) - : this(bv.Vertex.X, bv.Vertex.Y, bv.Bulge) - { } - - /// - /// 多段线的顶点,凸度,头宽,尾宽 - /// - /// 多段线 - /// 子段编号 - public BulgeVertexWidth(Polyline pl, int index) - { - var pt = pl.GetPoint2dAt(index);// 这里可以3d - X = pt.X; - Y = pt.Y; - Bulge = pl.GetBulgeAt(index); - StartWidth = pl.GetStartWidthAt(index); - EndWidth = pl.GetEndWidthAt(index); - } - - public BulgeVertex ToBulgeVertex() - { - return new BulgeVertex(Vertex, Bulge); - } -} diff --git a/src/IFoxCAD.Cad/ExtensionMethod/CollectionEx.cs b/src/IFoxCAD.Cad/ExtensionMethod/CollectionEx.cs deleted file mode 100644 index 2c4e8ce..0000000 --- a/src/IFoxCAD.Cad/ExtensionMethod/CollectionEx.cs +++ /dev/null @@ -1,224 +0,0 @@ -namespace IFoxCAD.Cad; - -/// -/// 集合扩展类 -/// -public static class CollectionEx -{ - /// - /// 对象id迭代器转换为集合 - /// - /// 对象id的迭代器 - /// 对象id集合 - public static ObjectIdCollection ToCollection(this IEnumerable ids) - { - return new ObjectIdCollection(ids.ToArray()); - } - - /// - /// 实体迭代器转换为集合 - /// - /// 对象类型 - /// 实体对象的迭代器 - /// 实体集合 - public static DBObjectCollection ToCollection(this IEnumerable objs) where T : DBObject - { - DBObjectCollection objCol = new(); - foreach (T obj in objs) - objCol.Add(obj); - return objCol; - } - - /// - /// double 数值迭代器转换为 double 数值集合 - /// - /// double 数值迭代器 - /// double 数值集合 - public static DoubleCollection ToCollection(this IEnumerable doubles) - { - return new DoubleCollection(doubles.ToArray()); - } - - /// - /// 二维点迭代器转换为二维点集合 - /// - /// 二维点迭代器 - /// 二维点集合 - public static Point2dCollection ToCollection(this IEnumerable pts) - { - return new Point2dCollection(pts.ToArray()); - } - - /// - /// 三维点迭代器转换为三维点集合 - /// - /// 三维点迭代器 - /// 三维点集合 - public static Point3dCollection ToCollection(this IEnumerable pts) - { - return new Point3dCollection(pts.ToArray()); - } - - /// - /// 对象id集合转换为对象id列表 - /// - /// 对象id集合 - /// 对象id列表 - public static List ToList(this ObjectIdCollection ids) - { - return ids.Cast().ToList(); - } - - - /// - /// 遍历集合,执行委托 - /// - /// 集合值的类型 - /// 集合 - /// 委托 - public static void ForEach(this IEnumerable source, Action action) - { - if (action == null) - throw new ArgumentNullException(nameof(action)); - - foreach (var element in source) - action.Invoke(element); - } - /// - /// 遍历集合,执行委托
    - /// 输出索引值 - ///
    - /// 集合值的类型 - /// 集合 - /// 委托 - public static void ForEach(this IEnumerable source, Action action) - { - if (action == null) - throw new ArgumentNullException(nameof(action)); - - int i = 0; - foreach (var element in source) - { - action.Invoke(i, element); - i++; - } - } - /// - /// 遍历集合,执行委托
    - /// 输出索引值,允许循环中断 - ///
    - /// 集合值的类型 - /// 集合 - /// 委托 - public static void ForEach(this IEnumerable source, Action action) - { - if (action == null) - throw new ArgumentNullException(nameof(action)); - - LoopState state = new();/*这种方式比Action改Func更友好*/ - int i = 0; - foreach (var element in source) - { - action.Invoke(i, element, state); - if (!state.IsRun) - break; - i++; - } - } - - - #region 关键字集合 - public enum KeywordName - { - GlobalName, - LocalName, - DisplayName, - } - - /// - /// 含有关键字 - /// - /// 关键字集合 - /// 关键字 - /// 关键字容器字段名 - /// true含有 - public static bool Contains(this KeywordCollection collection, string name, - KeywordName keywordName = KeywordName.GlobalName) - { - bool contains = false; - switch (keywordName) - { - case KeywordName.GlobalName: - for (int i = 0; i < collection.Count; i++) - if (collection[i].GlobalName == name) - { - contains = true; - break; - } - break; - case KeywordName.LocalName: - for (int i = 0; i < collection.Count; i++) - if (collection[i].LocalName == name) - { - contains = true; - break; - } - break; - case KeywordName.DisplayName: - for (int i = 0; i < collection.Count; i++) - if (collection[i].DisplayName == name) - { - contains = true; - break; - } - break; - default: - break; - } - return contains; - } - - /// - /// 获取词典, - /// KeywordCollection是允许重复关键字的,没有哈希索引,在多次判断时候会遍历多次O(n),所以生成一个词典进行O(1) - /// - /// - /// - public static Dictionary GetDict(this KeywordCollection collection) - { - Dictionary map = new(); - for (int i = 0; i < collection.Count; i++) - map.Add(collection[i].GlobalName, collection[i].DisplayName); - return map; - } - #endregion - - - #region IdMapping - /// - /// 旧块名 - /// - /// - /// - public static List GetKeys(this IdMapping idmap) - { - List ids = new(); - foreach (IdPair item in idmap) - ids.Add(item.Key); - return ids; - } - - /// - /// 新块名 - /// - /// - /// - public static List GetValues(this IdMapping idmap) - { - List ids = new(); - foreach (IdPair item in idmap) - ids.Add(item.Value); - return ids; - } - #endregion -} \ No newline at end of file diff --git a/src/IFoxCAD.Cad/ExtensionMethod/Curve2dEx.cs b/src/IFoxCAD.Cad/ExtensionMethod/Curve2dEx.cs deleted file mode 100644 index c1c06c6..0000000 --- a/src/IFoxCAD.Cad/ExtensionMethod/Curve2dEx.cs +++ /dev/null @@ -1,312 +0,0 @@ -namespace IFoxCAD.Cad; - -/// -/// 二维解析类曲线转换为二维实体曲线扩展类 -/// -public static class Curve2dEx -{ - #region Curve2d - - /// - /// 按矩阵转换Ge2d曲线为Db曲线 - /// - /// Ge2d曲线 - /// 曲线转换矩阵 - /// Db曲线 - public static Curve? ToCurve(this Curve2d curve, Matrix3d mat) - { - return curve switch - { - LineSegment2d li => ToCurve(li, mat), - NurbCurve2d nu => ToCurve(nu, mat), - EllipticalArc2d el => ToCurve(el, mat), - CircularArc2d ci => ToCurve(ci, mat), - PolylineCurve2d po => ToCurve(po, mat), - Line2d l2 => ToCurve(l2, mat), - CompositeCurve2d co => ToCurve(co, mat), - _ => null - }; - } - - #endregion Curve2d - - #region CircularArc2d - - /// - /// 判断点是否位于圆内及圆上 - /// - /// 二维解析类圆弧对象 - /// 二维点 - /// 位于圆内及圆上返回 ,反之返回 - public static bool IsIn(this CircularArc2d ca2d, Point2d pnt) - { - return ca2d.IsOn(pnt) || ca2d.IsInside(pnt); - } - - /// - /// 将二维解析类圆弧转换为实体圆或者圆弧,然后进行矩阵变换 - /// - /// 二维解析类圆弧对象 - /// 变换矩阵 - /// 实体圆或者圆弧 - public static Curve ToCurve(this CircularArc2d ca2d, Matrix3d mat) - { - Curve c = ToCurve(ca2d); - c.TransformBy(mat); - return c; - } - - /// - /// 将二维解析类圆弧转换为实体圆或者圆弧 - /// - /// 二维解析类圆弧对象 - /// 实体圆或者圆弧 - public static Curve ToCurve(this CircularArc2d ca2d) - { - if (ca2d.IsClosed()) - { - return ToCircle(ca2d); - } - else - { - return ToArc(ca2d); - } - } - - /// - /// 将二维解析类圆弧转换为实体圆 - /// - /// 二维解析类圆弧对象 - /// 实体圆 - public static Circle ToCircle(this CircularArc2d c2d) - { - return - new Circle( - new Point3d(new Plane(), c2d.Center), - new Vector3d(0, 0, 1), - c2d.Radius); - } - - /// - /// 将二维解析类圆弧转换为实体圆弧 - /// - /// 二维解析类圆弧对象 - /// 圆弧 - public static Arc ToArc(this CircularArc2d a2d) - { - double startangle, endangle; - double refangle = a2d.ReferenceVector.Angle; - - if (a2d.IsClockWise) - { - startangle = -a2d.EndAngle - refangle; - endangle = -a2d.StartAngle - refangle; - } - else - { - startangle = a2d.StartAngle + refangle; - endangle = a2d.EndAngle + refangle; - } - - return - new Arc( - new Point3d(new Plane(), a2d.Center), - Vector3d.ZAxis, - a2d.Radius, - startangle, - endangle); - } - - #endregion CircularArc2d - - #region EllipticalArc2d - - // 椭圆弧 - /// - /// 将二维解析类椭圆弧转换为实体椭圆弧,然后进行矩阵变换 - /// - /// 二维解析类椭圆弧对象 - /// 变换矩阵 - /// 实体椭圆弧 - public static Ellipse ToCurve(this EllipticalArc2d ea2d, Matrix3d mat) - { - Ellipse e = ToCurve(ea2d); - e.TransformBy(mat); - return e; - } - - /// - /// 将二维解析类椭圆弧转换为实体椭圆弧 - /// - /// 二维解析类椭圆弧对象 - /// 实体椭圆弧 - public static Ellipse ToCurve(this EllipticalArc2d ea2d) - { - Plane plane = new(); - Ellipse ell = - new( - new Point3d(plane, ea2d.Center), - new Vector3d(0, 0, 1), - new Vector3d(plane, ea2d.MajorAxis) * ea2d.MajorRadius, - ea2d.MinorRadius / ea2d.MajorRadius, - 0, - Math.PI * 2); - if (!ea2d.IsClosed()) - { - if (ea2d.IsClockWise) - { - ell.StartAngle = -ell.GetAngleAtParameter(ea2d.EndAngle); - ell.EndAngle = -ell.GetAngleAtParameter(ea2d.StartAngle); - } - else - { - ell.StartAngle = ell.GetAngleAtParameter(ea2d.StartAngle); - ell.EndAngle = ell.GetAngleAtParameter(ea2d.EndAngle); - } - } - return ell; - } - - #endregion EllipticalArc2d - - #region Line2d - - /// - /// 将二维解析类直线转换为实体类构造线 - /// - /// 二维解析类直线 - /// 实体类构造线 - public static Xline ToCurve(this Line2d line2d) - { - Plane plane = new(); - return - new Xline - { - BasePoint = new Point3d(plane, line2d.PointOnLine), - SecondPoint = new Point3d(plane, line2d.PointOnLine + line2d.Direction) - }; - } - - /// - /// 将二维解析类直线转换为实体类构造线,然后进行矩阵变换 - /// - /// 二维解析类直线 - /// 变换矩阵 - /// 实体类构造线 - public static Xline ToCurve(this Line2d line2d, Matrix3d mat) - { - Xline xl = ToCurve(line2d); - xl.TransformBy(mat); - return xl; - } - - /// - /// 将二维解析类构造线转换为二维解析类线段 - /// - /// 二维解析类构造线 - /// 起点参数 - /// 终点参数 - /// 二维解析类线段 - public static LineSegment2d ToLineSegment2d(this Line2d line2d, double fromParameter, double toParameter) - { - return - new LineSegment2d - ( - line2d.EvaluatePoint(fromParameter), - line2d.EvaluatePoint(toParameter) - ); - } - - #endregion Line2d - - #region LineSegment2d - - /// - /// 将二维解析类线段转换为实体类直线,并进行矩阵变换 - /// - /// 二维解析类线段 - /// 变换矩阵 - /// 实体类直线 - public static Line ToCurve(this LineSegment2d ls2d, Matrix3d mat) - { - Line l = ToCurve(ls2d); - l.TransformBy(mat); - return l; - } - - /// - /// 将二维解析类线段转换为实体类直线 - /// - /// 二维解析类线段 - /// 实体类直线 - public static Line ToCurve(this LineSegment2d ls2d) - { - Plane plane = new(); - return - new Line( - new Point3d(plane, ls2d.StartPoint), - new Point3d(plane, ls2d.EndPoint)); - - } - - #endregion LineSegment2d - - #region NurbCurve2d - - /// - /// 将二维解析类BURB曲线转换为实体类样条曲线,并进行矩阵变换 - /// - /// 二维解析类BURB曲线 - /// 变换矩阵 - /// 实体类样条曲线 - public static Spline ToCurve(this NurbCurve2d nc2d, Matrix3d mat) - { - Spline spl = ToCurve(nc2d); - spl.TransformBy(mat); - return spl; - } - - /// - /// 将二维解析类BURB曲线转换为实体类样条曲线 - /// - /// 二维解析类BURB曲线 - /// 实体类样条曲线 - public static Spline ToCurve(this NurbCurve2d nc2d) - { - int i; - Plane plane = new(); - Point3dCollection ctlpnts = new(); - for (i = 0; i < nc2d.NumControlPoints; i++) - { - ctlpnts.Add(new Point3d(plane, nc2d.GetControlPointAt(i))); - } - - DoubleCollection knots = new(); - foreach (double knot in nc2d.Knots) - { - knots.Add(knot); - } - - DoubleCollection weights = new(); - for (i = 0; i < nc2d.NumWeights; i++) - { - weights.Add(nc2d.GetWeightAt(i)); - } - - NurbCurve2dData ncdata = nc2d.DefinitionData; - - return - new Spline( - ncdata.Degree, - ncdata.Rational, - nc2d.IsClosed(), - ncdata.Periodic, - ctlpnts, - knots, - weights, - 0, - nc2d.Knots.Tolerance); - } - - #endregion NurbCurve2d -} diff --git a/src/IFoxCAD.Cad/ExtensionMethod/Curve3dEx.cs b/src/IFoxCAD.Cad/ExtensionMethod/Curve3dEx.cs deleted file mode 100644 index f5ebdab..0000000 --- a/src/IFoxCAD.Cad/ExtensionMethod/Curve3dEx.cs +++ /dev/null @@ -1,557 +0,0 @@ -namespace IFoxCAD.Cad; - -/// -/// 三维解析类曲线转换为三维实体曲线扩展类 -/// -public static class Curve3dEx -{ - /// - /// 判断两个浮点数是否相等 - /// - /// 容差 - /// 第一个数 - /// 第二个数 - /// 两个数的差值的绝对值小于容差返回 ,反之返回 - public static bool IsEqualPoint(this Tolerance tol, double d1, double d2) - { - return Math.Abs(d1 - d2) < tol.EqualPoint; - } - - #region Curve3d - - /// - /// 获取三维解析类曲线(自交曲线)的交点参数 - /// - /// 三维解析类曲线 - /// 曲线参数的列表 - public static List GetParamsAtIntersectionPoints(this Curve3d c3d) - { - CurveCurveIntersector3d cci = new(c3d, c3d, Vector3d.ZAxis); - List pars = new(); - for (int i = 0; i < cci.NumberOfIntersectionPoints; i++) - pars.AddRange(cci.GetIntersectionParameters(i)); - - pars.Sort(); - return pars; - } - - /// - /// 获取三维解析类子曲线 - /// - /// 三维解析类曲线 - /// 子段曲线起点参数 - /// 子段曲线终点参数 - /// 三维解析类曲线 - public static Curve3d GetSubCurve(this Curve3d curve, double from, double to) - { - Interval inter = curve.GetInterval(); - bool atStart = Tolerance.Global.IsEqualPoint(inter.LowerBound, from); - bool atEnd = Tolerance.Global.IsEqualPoint(inter.UpperBound, to); - if (atStart && atEnd) - return (Curve3d)curve.Clone(); - if (curve is NurbCurve3d) - { - if (from < to) - { - NurbCurve3d clone = (NurbCurve3d)curve.Clone(); - if (atStart || atEnd) - { - clone.HardTrimByParams(from, to); - return clone; - } - else - { - clone.HardTrimByParams(inter.LowerBound, to); - clone.HardTrimByParams(from, to); - return clone; - } - } - else - { - NurbCurve3d clone1 = (NurbCurve3d)curve.Clone(); - clone1.HardTrimByParams(from, inter.UpperBound); - NurbCurve3d clone2 = (NurbCurve3d)curve.Clone(); - clone2.HardTrimByParams(inter.LowerBound, to); - clone1.JoinWith(clone2); - return clone1; - } - } - else - { - Curve3d clone = (Curve3d)curve.Clone(); - clone.SetInterval(new Interval(from, to, Tolerance.Global.EqualPoint)); - return clone; - } - } - - /// - /// 将三维解析类曲线转换为三维实体类曲线 - /// - /// 三维解析类曲线 - /// 三维实体类曲线 - public static Curve? ToCurve(this Curve3d curve) - { - return curve switch - { - CompositeCurve3d co => ToCurve(co), - LineSegment3d li => ToCurve(li), - EllipticalArc3d el => ToCurve(el), - CircularArc3d ci => ToCurve(ci), - NurbCurve3d nu => ToCurve(nu), - PolylineCurve3d pl => ToCurve(pl), - Line3d l3 => ToCurve(l3), - _ => null - }; - } - - /// - /// 将三维解析类曲线转换为三维解析类Nurb曲线 - /// - /// 三维解析类曲线 - /// 三维解析类Nurb曲线 - public static NurbCurve3d? ToNurbCurve3d(this Curve3d curve) - { - return curve switch - { - LineSegment3d line => new NurbCurve3d(line), - EllipticalArc3d el => new NurbCurve3d(el), - CircularArc3d cir => new NurbCurve3d(ToEllipticalArc3d(cir)), - NurbCurve3d nur => nur, - PolylineCurve3d pl => new NurbCurve3d(3, pl, false), - _ => null - }; - } - - #endregion Curve3d - - #region CompositeCurve3d - - /// - /// 判断是否为圆和椭圆 - /// - /// 三维解析类曲线 - /// 完整圆及完整的椭圆返回 ,反之返回 - public static bool IsCircular(this Curve3d curve) - { - return curve switch - { - CircularArc3d or EllipticalArc3d => curve.IsClosed(), - _ => false - }; - } - - /// - /// 将三维复合曲线按曲线参数分割 - /// - /// 三维复合曲线 - /// 曲线参数列表 - /// 三维复合曲线列表 - public static List GetSplitCurves(this CompositeCurve3d c3d, List pars) - { - // 曲线参数剔除重复的 - pars.Sort(); - for (int i = pars.Count - 1; i > 0; i--) - { - if (Tolerance.Global.IsEqualPoint(pars[i], pars[i - 1])) - pars.RemoveAt(i); - } - - if (pars.Count == 0) - return new List(); - - // 这个是曲线参数类 - var inter = c3d.GetInterval(); - // 曲线们 - var c3ds = c3d.GetCurves(); - if (c3ds.Length == 1 && c3ds[0].IsClosed()) - { - // 闭合曲线不允许打断于一点 - if (pars.Count > 1) - { - // 如果包含起点 - if (Tolerance.Global.IsEqualPoint(pars[0], inter.LowerBound)) - { - pars[0] = inter.LowerBound; - // 又包含终点,去除终点 - if (Tolerance.Global.IsEqualPoint(pars[^1], inter.UpperBound)) - { - pars.RemoveAt(pars.Count - 1); - if (pars.Count == 1) - return new List(); - } - } - else if (Tolerance.Global.IsEqualPoint(pars[^1], inter.UpperBound)) - { - pars[^1] = inter.UpperBound; - } - // 加入第一点以支持反向打断 - pars.Add(pars[0]); - } - else - { - return new List(); - } - } - else - { - // 非闭合曲线加入起点和终点 - if (Tolerance.Global.IsEqualPoint(pars[0], inter.LowerBound)) - pars[0] = inter.LowerBound; - else - pars.Insert(0, inter.LowerBound); - if (Tolerance.Global.IsEqualPoint(pars[^1], inter.UpperBound)) - pars[^1] = inter.UpperBound; - else - pars.Add(inter.UpperBound); - } - - List curves = new(); - for (int i = 0; i < pars.Count - 1; i++) - { - List cc3ds = new(); - // 复合曲线参数转换到包含曲线参数 - var cp1 = c3d.GlobalToLocalParameter(pars[i]); - var cp2 = c3d.GlobalToLocalParameter(pars[i + 1]); - if (cp1.SegmentIndex == cp2.SegmentIndex) - { - cc3ds.Add( - c3ds[cp1.SegmentIndex].GetSubCurve( - cp1.LocalParameter, - cp2.LocalParameter)); - } - else - { - inter = c3ds[cp1.SegmentIndex].GetInterval(); - cc3ds.Add( - c3ds[cp1.SegmentIndex].GetSubCurve( - cp1.LocalParameter, - inter.UpperBound)); - for (int j = cp1.SegmentIndex + 1; j < cp2.SegmentIndex; j++) - { - cc3ds.Add((Curve3d)c3ds[j].Clone()); - } - inter = c3ds[cp2.SegmentIndex].GetInterval(); - cc3ds.Add( - c3ds[cp2.SegmentIndex].GetSubCurve( - inter.LowerBound, - cp2.LocalParameter)); - } - curves.Add(new CompositeCurve3d(cc3ds.ToArray())); - } - - if (c3d.IsClosed() && c3ds.Length > 1) - { - var cus1 = curves[curves.Count - 1].GetCurves(); - var cus2 = curves[0].GetCurves(); - var cs = cus1.Combine2(cus2); - curves[^1] = new CompositeCurve3d(cs); - curves.RemoveAt(0); - } - return curves; - } - - /// - /// 将复合曲线转换为实体类曲线 - /// - /// 三维复合曲线 - /// 实体曲线 - public static Curve? ToCurve(this CompositeCurve3d curve) - { - Curve3d[] cs = curve.GetCurves(); - if (cs.Length == 0) - return null; - if (cs.Length == 1) - return ToCurve(cs[0]); - - bool hasNurb = false; - - for (int i = 0; i < cs.Length; i++) - { - var c = cs[i]; - if (c is NurbCurve3d || c is EllipticalArc3d) - { - hasNurb = true; - break; - } - } - if (hasNurb) - { - var nc3d = cs[0].ToNurbCurve3d(); - for (int i = 1; i < cs.Length; i++) - nc3d?.JoinWith(cs[i].ToNurbCurve3d()); - return nc3d?.ToCurve(); - } - - return ToPolyline(curve); - } - - /// - /// 将三维复合曲线转换为实体类多段线 - /// - /// 三维复合曲线 - /// 实体类多段线 - public static Polyline ToPolyline(this CompositeCurve3d cc3d) - { - Polyline pl = new(); - pl.SetDatabaseDefaults(); - pl.Elevation = cc3d.StartPoint[2]; - - Plane plane = pl.GetPlane(); - Point2d endver = Point2d.Origin; - int i = 0; - foreach (Curve3d c3d in cc3d.GetCurves()) - { - if (c3d is CircularArc3d ca3d) - { - double b = Math.Tan(0.25 * (ca3d.EndAngle - ca3d.StartAngle)) * ca3d.Normal[2]; - pl.AddVertexAt(i, c3d.StartPoint.Convert2d(plane), b, 0, 0); - endver = c3d.EndPoint.Convert2d(plane); - } - else - { - pl.AddVertexAt(i, c3d.StartPoint.Convert2d(plane), 0, 0, 0); - endver = c3d.EndPoint.Convert2d(plane); - } - i++; - } - pl.AddVertexAt(i, endver, 0, 0, 0); - return pl; - } - - #endregion CompositeCurve3d - - #region Line3d - - /// - /// 将解析类三维构造线转换为实体类构造线 - /// - /// 解析类三维构造线 - /// 实体类构造线 - public static Xline ToCurve(this Line3d line3d) - { - return - new Xline - { - BasePoint = line3d.PointOnLine, - SecondPoint = line3d.PointOnLine + line3d.Direction - }; - } - - /// - /// 将三维解析类构造线转换为三维解析类线段 - /// - /// 三维解析类构造线 - /// 起点参数 - /// 终点参数 - /// 三维解析类线段 - public static LineSegment3d ToLineSegment3d(this Line3d line3d, double fromParameter, double toParameter) - { - return - new LineSegment3d - ( - line3d.EvaluatePoint(fromParameter), - line3d.EvaluatePoint(toParameter) - ); - } - - #endregion Line3d - - #region LineSegment3d - - /// - /// 将三维解析类线段转换为实体类直线 - /// - /// 三维解析类线段 - /// 实体类直线 - public static Line ToCurve(this LineSegment3d lineSeg3d) - { - return new Line(lineSeg3d.StartPoint, lineSeg3d.EndPoint); - } - - #endregion LineSegment3d - - #region CircularArc3d - - /// - /// 将三维解析类圆/弧转换为实体圆/弧 - /// - /// 三维解析类圆/弧 - /// 实体圆/弧 - public static Curve ToCurve(this CircularArc3d ca3d) - { - if (ca3d.IsClosed()) - { - return ToCircle(ca3d); - } - else - { - return ToArc(ca3d); - } - } - - /// - /// 将三维解析类圆/弧转换为实体圆 - /// - /// 三维解析类圆/弧 - /// 实体圆 - public static Circle ToCircle(this CircularArc3d ca3d) => - new(ca3d.Center, ca3d.Normal, ca3d.Radius); - - /// - /// 将三维解析类圆/弧转换为实体圆弧 - /// - /// 三维解析类圆/弧 - /// 实体圆弧 - public static Arc ToArc(this CircularArc3d ca3d) - { - // 必须新建,而不能直接使用GetPlane()获取 - double angle = ca3d.ReferenceVector.AngleOnPlane(new Plane(ca3d.Center, ca3d.Normal)); - return new Arc(ca3d.Center, ca3d.Normal, ca3d.Radius, ca3d.StartAngle + angle, ca3d.EndAngle + angle); - } - - /// - /// 将三维解析类圆/弧转换为三维解析类椭圆弧 - /// - /// 三维解析类圆/弧 - /// 三维解析类椭圆弧 - public static EllipticalArc3d ToEllipticalArc3d(this CircularArc3d ca3d) - { - Vector3d zaxis = ca3d.Normal; - Vector3d xaxis = ca3d.ReferenceVector; - Vector3d yaxis = zaxis.CrossProduct(xaxis); - - return - new EllipticalArc3d( - ca3d.Center, - xaxis, - yaxis, - ca3d.Radius, - ca3d.Radius, - ca3d.StartAngle, - ca3d.EndAngle); - } - - /// - /// 将三维解析类圆/弧转换为三维解析类Nurb曲线 - /// - /// 三维解析类圆/弧 - /// 三维解析类Nurb曲线 - public static NurbCurve3d ToNurbCurve3d(this CircularArc3d ca3d) - { - EllipticalArc3d ea3d = ToEllipticalArc3d(ca3d); - NurbCurve3d nc3d = new(ea3d); - return nc3d; - } - - #endregion CircularArc3d - - #region EllipticalArc3d - - /// - /// 将三维解析类椭圆弧转换为实体类椭圆弧 - /// - /// 三维解析类椭圆弧 - /// 实体类椭圆弧 - public static Ellipse ToCurve(this EllipticalArc3d ea3d) - { - Ellipse ell = - new( - ea3d.Center, - ea3d.Normal, - ea3d.MajorAxis * ea3d.MajorRadius, - ea3d.MinorRadius / ea3d.MajorRadius, - 0, - Math.PI * 2); - // Ge椭圆角度就是Db椭圆的参数 - if (!ea3d.IsClosed()) - { - ell.StartAngle = ell.GetAngleAtParameter(ea3d.StartAngle); - ell.EndAngle = ell.GetAngleAtParameter(ea3d.EndAngle); - } - return ell; - } - - #endregion EllipticalArc3d - - #region NurbCurve3d - - /// - /// 将三维解析类Nurb曲线转换为实体类样条曲线 - /// - /// 三维解析类Nurb曲线 - /// 实体类样条曲线 - public static Spline ToCurve(this NurbCurve3d nc3d) - { - Spline spl; - if (nc3d.HasFitData) - { - NurbCurve3dFitData fdata = nc3d.FitData; - if (fdata.TangentsExist) - { - spl = new Spline( - fdata.FitPoints, - fdata.StartTangent, - fdata.EndTangent, - nc3d.Order, - fdata.FitTolerance.EqualPoint); - } - else - { - spl = new Spline( - fdata.FitPoints, - nc3d.Order, - fdata.FitTolerance.EqualPoint); - } - } - else - { - DoubleCollection knots = new(); - foreach (double knot in nc3d.Knots) - knots.Add(knot); - - NurbCurve3dData ncdata = nc3d.DefinitionData; - - spl = new Spline( - ncdata.Degree, - ncdata.Rational, - nc3d.IsClosed(), - ncdata.Periodic, - ncdata.ControlPoints, - knots, - ncdata.Weights, - Tolerance.Global.EqualPoint, - ncdata.Knots.Tolerance); - } - return spl; - } - - #endregion NurbCurve3d - - #region PolylineCurve3d - - /// - /// 将三维解析类多段线转换为实体类三维多段线 - /// - /// 三维解析类多段线 - /// 实体类三维多段线 - public static Polyline3d ToCurve(this PolylineCurve3d pl3d) - { - Point3dCollection pnts = new(); - - for (int i = 0; i < pl3d.NumberOfControlPoints; i++) - { - pnts.Add(pl3d.ControlPointAt(i)); - } - - bool closed = false; - int n = pnts.Count - 1; - if (pnts[0] == pnts[n]) - { - pnts.RemoveAt(n); - closed = true; - } - return new Polyline3d(Poly3dType.SimplePoly, pnts, closed); - } - - #endregion PolylineCurve3d -} diff --git a/src/IFoxCAD.Cad/ExtensionMethod/CurveEx.cs b/src/IFoxCAD.Cad/ExtensionMethod/CurveEx.cs deleted file mode 100644 index 41aad25..0000000 --- a/src/IFoxCAD.Cad/ExtensionMethod/CurveEx.cs +++ /dev/null @@ -1,723 +0,0 @@ -namespace IFoxCAD.Cad; - -/// -/// 实体类曲线扩展类 -/// -public static class CurveEx -{ - /// - /// 曲线长度 - /// - /// 曲线 - /// 长度 - public static double GetLength(this Curve curve) - { - return curve.GetDistanceAtParameter(curve.EndParam); - } - - /// - /// 获取分割曲线集合 - /// - /// 曲线 - /// 打断参数表 - /// 打断后曲线的集合 - public static IEnumerable GetSplitCurves(this Curve curve, IEnumerable pars) - { - if (pars is null) - throw new ArgumentNullException(nameof(pars)); - - return - curve - .GetSplitCurves(new DoubleCollection(pars.ToArray())) - .Cast(); - } - - /// - /// 获取分割曲线集合 - /// - /// 曲线 - /// 打断参数表 - /// 对参数表是否进行排序 - /// - /// 按参数值升序排序
    - /// 不排序,默认值 - ///
    - /// - /// 打断后曲线的集合 - public static IEnumerable GetSplitCurves(this Curve curve, IEnumerable pars, bool isOrder = false) - { - if (pars is null) - throw new ArgumentNullException(nameof(pars)); - if (isOrder) - pars = pars.OrderBy(x => x); - - return - curve - .GetSplitCurves(new DoubleCollection(pars.ToArray())) - .Cast(); - } - - /// - /// 获取分割曲线集合 - /// - /// 曲线 - /// 打断点表 - /// 打断后曲线的集合 - public static IEnumerable GetSplitCurves(this Curve curve, IEnumerable points) - { - if (points is null) - throw new ArgumentNullException(nameof(points)); - return - curve - .GetSplitCurves(new Point3dCollection(points.ToArray())) - .Cast(); - } - - /// - /// 获取分割曲线集合 - /// - /// 曲线 - /// 打断点表 - /// 对点表是否进行排序 - /// - /// 按参数值升序排序
    - /// 不排序,默认值 - ///
    - /// - /// 打断后曲线的集合 - public static IEnumerable GetSplitCurves(this Curve curve, IEnumerable points, bool isOrder = false) - { - if (points is null) - throw new ArgumentNullException(nameof(points)); - if (isOrder) - points = points.OrderBy(point => curve.GetParameterAtPoint( - curve.GetClosestPointTo(point, false))); - return - curve - .GetSplitCurves(new Point3dCollection(points.ToArray())) - .Cast(); - } - - /// - /// 获取曲线集所围成的封闭区域的曲线集,注意此函数不能处理平行边(两个点及两条线组成的闭合环) - /// - /// 曲线集合 - /// 所有的闭合环的曲线集合 - public static IEnumerable GetAllCycle(this IEnumerable curves) - { - if (curves is null) - throw new ArgumentNullException(nameof(curves)); - - // 新建图 - var graph = new Graph(); - foreach (var curve in curves) - { -#if NET35 - graph.AddEdge(curve.ToCurve3d()!); -#else - graph.AddEdge(curve.GetGeCurve()); -#endif - } - // 新建 dfs - var dfs = new DepthFirst(); - // 查询全部的 闭合环 - dfs.FindAll(graph); - // 遍历闭合环的列表,将每个闭合环转换为实体曲线 - var res = new List(); - foreach (var item in dfs.Curve3ds) - { - var curve = graph.GetCurves(item.ToList()).ToArray(); - var comcur = new CompositeCurve3d(curve).ToCurve(); - if (comcur is not null) - res.Add(comcur); - } - return res; - } - /// - /// 曲线打断 - /// - /// 曲线列表 - /// 打断后的曲线列表 - public static List BreakCurve(this List curves) - { - if (curves is null) - throw new ArgumentNullException(nameof(curves)); - - var geCurves = new List(); // 存储曲线转换后的复合曲线 - var paramss = new List>(); // 存储每个曲线的交点参数值 - - for (int i = 0; i < curves.Count; i++) - { - var cc3d = curves[i].ToCompositeCurve3d(); - if (cc3d is not null) - { - geCurves.Add(cc3d); - paramss.Add(new List()); - } - } - - // var oldCurves = new List(); - var newCurves = new List(); - var cci3d = new CurveCurveIntersector3d(); - - for (int i = 0; i < curves.Count; i++) - { - var gc1 = geCurves[i]; - var pars1 = paramss[i]; // 引用 - for (int j = i; j < curves.Count; j++) - { - var gc2 = geCurves[j]; - var pars2 = paramss[j]; // 引用 - - cci3d.Set(gc1, gc2, Vector3d.ZAxis); - - for (int k = 0; k < cci3d.NumberOfIntersectionPoints; k++) - { - var pars = cci3d.GetIntersectionParameters(k); - pars1.Add(pars[0]); // 引用修改会同步到源对象 - pars2.Add(pars[1]); // 引用修改会同步到源对象 - } - } - - if (pars1.Count > 0) - { - var c3ds = gc1.GetSplitCurves(pars1); - if (c3ds.Count > 1) - { - foreach (var c3d in c3ds) - { - var c3dCur = c3d.ToCurve(); - if (c3dCur is not null) - { - c3dCur.SetPropertiesFrom(curves[i]); - newCurves.Add(c3dCur); - } - } - // oldCurves.Add(curves[i]); - } - } - } - - return newCurves; - } - - // 转换DBCurve为GeCurved - - #region Curve - - /// - /// 将曲线转换为ge曲线,此函数将在未来淘汰,二惊加油 - /// - /// 曲线 - /// ge曲线 - public static Curve3d? ToCurve3d(this Curve curve) - { - return curve switch - { - Line li => ToCurve3d(li), - Circle ci => ToCurve3d(ci), - Arc arc => ToCurve3d(arc), - Ellipse el => ToCurve3d(el), - Polyline pl => ToCurve3d(pl), - Polyline2d pl2 => ToCurve3d(pl2), - Polyline3d pl3 => ToCurve3d(pl3), - Spline sp => ToCurve3d(sp), - _ => null - }; - } - - /// - /// 将曲线转换为复合曲线 - /// - /// 曲线 - /// 复合曲线 - public static CompositeCurve3d? ToCompositeCurve3d(this Curve curve) - { - return curve switch - { - Line li => new CompositeCurve3d(new Curve3d[] { ToCurve3d(li) }), - Circle ci => new CompositeCurve3d(new Curve3d[] { ToCurve3d(ci) }), - Arc arc => new CompositeCurve3d(new Curve3d[] { ToCurve3d(arc) }), - Ellipse el => new CompositeCurve3d(new Curve3d[] { ToCurve3d(el) }), - Polyline pl => new CompositeCurve3d(new Curve3d[] { ToCurve3d(pl) }), - - Polyline2d pl2 => new CompositeCurve3d(new Curve3d[] { ToCurve3d(pl2)! }), - Polyline3d pl3 => new CompositeCurve3d(new Curve3d[] { ToCurve3d(pl3) }), - Spline sp => new CompositeCurve3d(new Curve3d[] { ToCurve3d(sp) }), - _ => null - }; - } - - /// - /// 将曲线转换为Nurb曲线 - /// - /// 曲线 - /// Nurb曲线 - public static NurbCurve3d? ToNurbCurve3d(this Curve curve) - { - return curve switch - { - Line li => ToNurbCurve3d(li), - Circle ci => ToNurbCurve3d(ci), - Arc arc => ToNurbCurve3d(arc), - Ellipse el => ToNurbCurve3d(el), - Polyline pl => ToNurbCurve3d(pl), - Polyline2d pl2 => ToNurbCurve3d(pl2), - Polyline3d pl3 => ToNurbCurve3d(pl3), - Spline sp => ToNurbCurve3d(sp), - _ => null - }; - } - - #region Line - - /// - /// 将直线转换为ge直线 - /// - /// 直线 - /// ge直线 - public static LineSegment3d ToCurve3d(this Line line) - { - return new LineSegment3d(line.StartPoint, line.EndPoint); - } - - /// - /// 将直线转换为Nurb曲线 - /// - /// 直线 - /// Nurb曲线 - public static NurbCurve3d ToNurbCurve3d(this Line line) - { - return new NurbCurve3d(ToCurve3d(line)); - } - - #endregion Line - - #region Circle - - /// - /// 将圆转换为ge圆弧曲线 - /// - /// 圆 - /// ge圆弧曲线 - public static CircularArc3d ToCurve3d(this Circle cir) - { - return - new CircularArc3d( - cir.Center, - cir.Normal, - cir.Radius); - } - - /// - /// 将圆转换为ge椭圆曲线 - /// - /// 圆 - /// ge椭圆曲线 - public static EllipticalArc3d ToEllipticalArc3d(this Circle cir) - { - return ToCurve3d(cir).ToEllipticalArc3d(); - } - - /// - /// 将圆转换为Nurb曲线 - /// - /// 圆 - /// Nurb曲线 - public static NurbCurve3d ToNurbCurve3d(this Circle cir) - { - return new NurbCurve3d(ToEllipticalArc3d(cir)); - } - - #endregion Circle - - #region Arc - - /// - /// 将圆弧转换为ge圆弧曲线 - /// - /// 圆弧 - /// ge圆弧曲线 - public static CircularArc3d ToCurve3d(this Arc arc) - { - Plane plane = new(arc.Center, arc.Normal); - - return - new CircularArc3d( - arc.Center, - arc.Normal, - plane.GetCoordinateSystem().Xaxis, - arc.Radius, - arc.StartAngle, - arc.EndAngle - ); - } - - /// - /// 将圆弧转换为ge椭圆曲线 - /// - /// 圆弧 - /// ge椭圆曲线 - public static EllipticalArc3d ToEllipticalArc3d(this Arc arc) - { - return ToCurve3d(arc).ToEllipticalArc3d(); - } - - /// - /// 将圆弧转换为三维Nurb曲线 - /// - /// 圆弧 - /// 三维Nurb曲线 - public static NurbCurve3d ToNurbCurve3d(this Arc arc) - { - return new NurbCurve3d(ToEllipticalArc3d(arc)); - } - - #endregion Arc - - #region Ellipse - - /// - /// 将椭圆转换为三维ge椭圆曲线 - /// - /// 椭圆 - /// 三维ge椭圆曲线 - public static EllipticalArc3d ToCurve3d(this Ellipse ell) - { - return - new EllipticalArc3d( - ell.Center, - ell.MajorAxis.GetNormal(), - ell.MinorAxis.GetNormal(), - ell.MajorRadius, - ell.MinorRadius, - ell.StartParam, - ell.EndParam); - } - - /// - /// 将椭圆转换为三维Nurb曲线 - /// - /// 椭圆 - /// 三维Nurb曲线 - public static NurbCurve3d ToNurbCurve3d(this Ellipse ell) - { - return new NurbCurve3d(ToCurve3d(ell)); - } - - #endregion Ellipse - - #region Spline - - /// - /// 将样条曲线转换为三维Nurb曲线 - /// - /// 样条曲线 - /// 三维Nurb曲线 - public static NurbCurve3d ToCurve3d(this Spline spl) - { - NurbCurve3d nc3d; - NurbsData ndata = spl.NurbsData; - KnotCollection knots = new(); - foreach (Double knot in ndata.GetKnots()) - knots.Add(knot); - - if (ndata.Rational) - { - nc3d = - new NurbCurve3d( - ndata.Degree, - knots, - ndata.GetControlPoints(), - ndata.GetWeights(), - ndata.Periodic); - } - else - { - nc3d = - new NurbCurve3d( - ndata.Degree, - knots, - ndata.GetControlPoints(), - ndata.Periodic); - } - - if (spl.HasFitData) - { - var fdata = spl.FitData; - var vec = new Vector3d(); - if (fdata.TangentsExist && (fdata.StartTangent != vec || fdata.EndTangent != vec)) - nc3d.SetFitData(fdata.GetFitPoints(), fdata.StartTangent, fdata.EndTangent); - } - return nc3d; - } - - #endregion Spline - - #region Polyline2d - - /// - /// 将二维多段线转换为三维ge曲线 - /// - /// 二维多段线 - /// 三维ge曲线 - public static Curve3d? ToCurve3d(this Polyline2d pl2d) - { - switch (pl2d.PolyType) - { - case Poly2dType.SimplePoly: - case Poly2dType.FitCurvePoly: - Polyline pl = new(); - pl.SetDatabaseDefaults(); - pl.ConvertFrom(pl2d, false); - return ToCurve3d(pl); - default: - return ToNurbCurve3d(pl2d); - } - - // Polyline pl = new Polyline(); - // pl.ConvertFrom(pl2d, false); - // return ToCurve3d(pl); - } - - /// - /// 将二维多段线转换为三维Nurb曲线 - /// - /// 二维多段线 - /// 三维Nurb曲线 - public static NurbCurve3d? ToNurbCurve3d(this Polyline2d pl2d) - { - switch (pl2d.PolyType) - { - case Poly2dType.SimplePoly: - case Poly2dType.FitCurvePoly: - Polyline pl = new(); - pl.SetDatabaseDefaults(); - pl.ConvertFrom(pl2d, false); - return ToNurbCurve3d(pl); - - default: - return ToCurve3d(pl2d.Spline); - } - } - - /// - /// 将二维多段线转换为三维ge多段线 - /// - /// 二维多段线 - /// 三维ge多段线 - public static PolylineCurve3d ToPolylineCurve3d(this Polyline2d pl) - { - Point3dCollection pnts = new(); - foreach (Vertex2d ver in pl) - { - pnts.Add(ver.Position); - } - return new PolylineCurve3d(pnts); - } - - #endregion Polyline2d - - #region Polyline3d - - /// - /// 将三维多段线转换为三维曲线 - /// - /// 三维多段线 - /// 三维曲线 - public static Curve3d ToCurve3d(this Polyline3d pl3d) - { - return pl3d.PolyType switch - { - Poly3dType.SimplePoly => ToPolylineCurve3d(pl3d), - _ => ToNurbCurve3d(pl3d), - }; - } - - /// - /// 将三维多段线转换为三维Nurb曲线 - /// - /// 三维多段线 - /// 三维Nurb曲线 - public static NurbCurve3d ToNurbCurve3d(this Polyline3d pl3d) - { - return ToCurve3d(pl3d.Spline); - } - - /// - /// 将三维多段线转换为三维ge多段线 - /// - /// 三维多段线 - /// 三维ge多段线 - public static PolylineCurve3d ToPolylineCurve3d(this Polyline3d pl) - { - Point3dCollection pnts = new(); - foreach (ObjectId id in pl) - { - PolylineVertex3d ver = (PolylineVertex3d)id.GetObject(OpenMode.ForRead); - pnts.Add(ver.Position); - } - return new PolylineCurve3d(pnts); - } - - #endregion Polyline3d - - #region Polyline - - /// - /// 多段线转换为复合曲线 - /// - /// 多段线对象 - /// 复合曲线对象 - public static CompositeCurve3d ToCurve3d(this Polyline pl) - { - List c3ds = new(); - - for (int i = 0; i < pl.NumberOfVertices; i++) - { - switch (pl.GetSegmentType(i)) - { - case SegmentType.Line: - c3ds.Add(pl.GetLineSegmentAt(i)); - break; - - case SegmentType.Arc: - c3ds.Add(pl.GetArcSegmentAt(i)); - break; - - default: - break; - } - } - return new CompositeCurve3d(c3ds.ToArray()); - } - - /// - /// 多段线转换为Nurb曲线 - /// - /// 多段线 - /// Nurb曲线 - public static NurbCurve3d? ToNurbCurve3d(this Polyline pl) - { - NurbCurve3d? nc3d = null; - for (int i = 0; i < pl.NumberOfVertices; i++) - { - NurbCurve3d? nc3dtemp = null; - switch (pl.GetSegmentType(i)) - { - case SegmentType.Line: - nc3dtemp = new NurbCurve3d(pl.GetLineSegmentAt(i)); - break; - - case SegmentType.Arc: - nc3dtemp = pl.GetArcSegmentAt(i).ToNurbCurve3d(); - break; - - default: - break; - } - if (nc3d is null) - { - nc3d = nc3dtemp; - } - else if (nc3dtemp is not null) - { - nc3d.JoinWith(nc3dtemp); - } - } - return nc3d; - } - - /// - /// 为优化多段线倒角 - /// - /// 优化多段线 - /// 顶点索引号 - /// 倒角半径 - /// 倒角类型 - public static void ChamferAt(this Polyline polyline, int index, double radius, bool isFillet) - { - if (index < 1 || index > polyline.NumberOfVertices - 2) - throw new System.Exception("错误的索引号"); - - if (SegmentType.Line != polyline.GetSegmentType(index - 1) || - SegmentType.Line != polyline.GetSegmentType(index)) - throw new System.Exception("非直线段不能倒角"); - - // 获取当前索引号的前后两段直线,并组合为Ge复合曲线 - Curve3d[] c3ds = - new Curve3d[] - { - polyline.GetLineSegmentAt(index - 1), - polyline.GetLineSegmentAt(index) - }; - CompositeCurve3d cc3d = new(c3ds); - - // 试倒直角 - // 子曲线的个数有三种情况: - // 1、=3时倒角方向正确 - // 2、=2时倒角方向相反 - // 3、=0或为直线时失败 - c3ds = - cc3d.GetTrimmedOffset - ( - radius, - Vector3d.ZAxis, - OffsetCurveExtensionType.Chamfer - ); - - if (c3ds.Length > 0 && c3ds[0] is CompositeCurve3d) - { - var newcc3d = c3ds[0] as CompositeCurve3d; - c3ds = newcc3d!.GetCurves(); - if (c3ds.Length == 3) - { - c3ds = cc3d.GetTrimmedOffset - ( - -radius, - Vector3d.ZAxis, - OffsetCurveExtensionType.Chamfer - ); - if (c3ds.Length == 0 || c3ds[0] is LineSegment3d) - throw new System.Exception("倒角半径过大"); - } - else if (c3ds.Length == 2) - { - radius = -radius; - } - } - else - { - throw new System.Exception("倒角半径过大"); - } - - // GetTrimmedOffset会生成倒角+偏移,故先反方向倒角,再倒回 - c3ds = cc3d.GetTrimmedOffset - ( - -radius, - Vector3d.ZAxis, - OffsetCurveExtensionType.Extend - ); - OffsetCurveExtensionType type = - isFillet ? - OffsetCurveExtensionType.Fillet : OffsetCurveExtensionType.Chamfer; - c3ds = c3ds[0].GetTrimmedOffset - ( - radius, - Vector3d.ZAxis, - type - ); - - // 将结果Ge曲线转为Db曲线,并将相关的数值反映到原曲线 - var plTemp = c3ds[0].ToCurve() as Polyline; - if (plTemp is null) - return; - polyline.RemoveVertexAt(index); - polyline.AddVertexAt(index, plTemp.GetPoint2dAt(1), plTemp.GetBulgeAt(1), 0, 0); - polyline.AddVertexAt(index + 1, plTemp.GetPoint2dAt(2), 0, 0, 0); - } - - #endregion Polyline - - #endregion -} \ No newline at end of file diff --git a/src/IFoxCAD.Cad/ExtensionMethod/DBDictionaryEx.cs b/src/IFoxCAD.Cad/ExtensionMethod/DBDictionaryEx.cs deleted file mode 100644 index 719d7df..0000000 --- a/src/IFoxCAD.Cad/ExtensionMethod/DBDictionaryEx.cs +++ /dev/null @@ -1,389 +0,0 @@ -namespace IFoxCAD.Cad; - -/// -/// 字典扩展类 -/// -public static class DBDictionaryEx -{ - /// - /// 获取字典里的全部对象 - /// - /// 对象类型的泛型 - /// 字典 - /// 事务 - /// 对象迭代器 - public static IEnumerable GetAllObjects(this DBDictionary dict, DBTrans? trans = null) where T : DBObject - { - trans ??= DBTrans.Top; - foreach (DBDictionaryEntry e in dict) - { - var ent = trans.GetObject(e.Value); - if (ent is not null) - yield return ent; - } - } - - /// - /// 获取字典内指定key的对象 - /// - /// 对象类型的泛型 - /// 字典 - /// 事务 - /// 指定的键值 - /// T 类型的对象 - public static T? GetAt(this DBDictionary dict, string key, DBTrans? trans = null) where T : DBObject - { - trans ??= DBTrans.Top; - if (dict.Contains(key)) - { - ObjectId id = dict.GetAt(key); - if (!id.IsNull) - return trans.GetObject(id); - } - return null; - } - - /// - /// 添加条目(键值对)到字典 - /// - /// 对象类型 - /// 字典 - /// 事务 - /// 键 - /// 值 - public static void SetAt(this DBDictionary dict, string key, T obj, Transaction? trans = null) where T : DBObject - { - trans ??= DBTrans.Top.Transaction; - using (dict.ForWrite()) - { - dict.SetAt(key, obj); - trans.AddNewlyCreatedDBObject(obj, true); - } - } - - #region XRecord - - /// - /// 从字典中获取扩展数据 - /// - /// 字典 - /// 键值 - /// 扩展数据 - public static XRecordDataList? GetXRecord(this DBDictionary dict, string key) - { - Xrecord? rec = dict.GetAt(key); - if (rec is not null) - return rec.Data; - return null; - } - - /// - /// 保存扩展数据到字典 - /// - /// 扩展数据 - /// 字典 - /// 键值 - public static void SetXRecord(this DBDictionary dict, string key, XRecordDataList rb) - { - using var data = new Xrecord { Data = rb }; - dict.SetAt(key, data); - } - #endregion - - /// - /// 获取扩展字典 - /// - /// 对象 - /// 事务 - /// 扩展字典对象 - public static DBDictionary? GetXDictionary(this DBObject obj, DBTrans? trans = null) - { - trans ??= DBTrans.Top; - ObjectId id = obj.ExtensionDictionary; - if (id.IsNull) - { - using (obj.ForWrite()) - { - obj.CreateExtensionDictionary(); - } - - id = obj.ExtensionDictionary; - } - return id.GetObject(tr: trans); - } - - #region 数据表 - - /// - /// 创建数据表 - /// - /// 原数据类型的字典 - /// 表元素(二维数组) - /// 数据表 - public static DataTable CreateDataTable(Dictionary colTypes, object[,] content) - { - DataTable table = new(); - foreach (var t in colTypes) - table.AppendColumn(t.Value, t.Key); - var ncol = colTypes.Count; - var nrow = content.GetLength(0); - var types = new CellType[ncol]; - colTypes.Values.CopyTo(types, 0); - for (int i = 0; i < nrow; i++) - { - DataCellCollection row = new(); - for (int j = 0; j < ncol; j++) - { - var cell = new DataCell(); - cell.SetValue(types[j], content[i, j]); - row.Add(cell); - } - table.AppendRow(row, true); - } - return table; - } - - /// - /// 设定单元格数据 - /// - /// 单元格 - /// 类型 - /// 数据 - public static void SetValue(this DataCell cell, CellType type, object value) - { - - - switch (type) - { - case CellType.Bool: - cell.SetBool((bool)value); - break; - - case CellType.CharPtr: - cell.SetString((string)value); - break; - - case CellType.Integer: - cell.SetInteger((int)value); - break; - - case CellType.Double: - cell.SetDouble((double)value); - break; - - case CellType.ObjectId: - cell.SetObjectId((ObjectId)value); - break; - - case CellType.Point: - cell.SetPoint((Point3d)value); - break; - - case CellType.Vector: - cell.SetVector((Vector3d)value); - break; - - case CellType.HardOwnerId: - cell.SetHardOwnershipId((ObjectId)value); - break; - - case CellType.HardPtrId: - cell.SetHardPointerId((ObjectId)value); - break; - - case CellType.SoftOwnerId: - cell.SetSoftOwnershipId((ObjectId)value); - break; - - case CellType.SoftPtrId: - cell.SetSoftPointerId((ObjectId)value); - break; - } - } - #endregion - - #region 子字典 - /// - /// 获取子字典 - /// - /// 根字典 - /// 事务 - /// 是否创建子字典 - /// 键值列表 - /// 字典 - public static DBDictionary? GetSubDictionary(this DBDictionary dict, bool createSubDictionary, IEnumerable dictNames, DBTrans? trans = null) - { - DBDictionary? newdict = null; - trans ??= DBTrans.Top; - if (createSubDictionary) - { - using (dict.ForWrite()) - dict.TreatElementsAsHard = true; - - foreach (string name in dictNames) - { - if (dict!.Contains(name)) - { - newdict = dict.GetAt(name, trans); - } - else - { - DBDictionary subDict = new(); - dict.SetAt(name, subDict, trans); - newdict = subDict; - newdict.TreatElementsAsHard = true; - } - } - } - else - { - foreach (string name in dictNames) - { - if (dict.Contains(name)) - newdict = dict.GetAt(name, trans); - else - return null; - } - } - - return newdict; - } - - - ///// - ///// 获取对象扩展字典的子字典 - ///// - ///// 对象 - ///// 事务 - ///// 是否创建子字典 - ///// 键值列表 - ///// 字典 - // public static DBDictionary GetSubDictionary(this DBObject obj, bool createSubDictionary, params string[] dictNames) - // { - // return obj.GetXDictionary().GetSubDictionary(createSubDictionary, dictNames); - // } - - #endregion - - #region 组字典 - /// - /// 添加编组 - /// - /// 组名 - /// 实体Id集合 - /// 编组Id - public static ObjectId AddGroup(this DBDictionary dict, string name, ObjectIdCollection ids) - { - if (dict.Contains(name)) - return ObjectId.Null; - - using (dict.ForWrite()) - { - Group g = new(); - g.Append(ids); - dict.SetAt(name, g); - DBTrans.Top.Transaction.AddNewlyCreatedDBObject(g, true); - return g.ObjectId; - } - } - - /// - /// 添加编组 - /// - /// 组名 - /// 实体Id集合 - /// 编组Id - public static ObjectId AddGroup(this DBDictionary dict, string name, IEnumerable ids) - { - if (dict.Contains(name)) - return ObjectId.Null; - - return dict.AddGroup(name, new ObjectIdCollection(ids.ToArray())); - } - - - /// - /// 按选择条件获取编组集合 - /// - /// 选择条件,过滤函数 - /// g.NumEntities < 2);]]> - /// 编组集合 - public static IEnumerable GetGroups(this DBDictionary dict, Func func) - { - return dict.GetAllObjects() - .Where(func); - } - - /// - /// 返回实体的所在编组的集合 - /// - /// 图元实体 - /// 编组集合 - public static IEnumerable GetGroups(this Entity ent) - { - return ent.GetPersistentReactorIds() - .Cast() - .Select(id => id.GetObject()) - .OfType(); - } - - /// - /// 移除所有的空组 - /// - /// 被移除编组的名称集合 - public static List RemoveNullGroup(this DBDictionary dict) - { - var groups = dict.GetGroups(g => g.NumEntities < 2); - List names = new(); - foreach (Group g in groups) - { - names.Add(g.Name); - using (g.ForWrite()) - g.Erase(); - } - return names; - } - - /// - /// 移除所有空组 - /// - /// 过滤条件,过滤要删除的组名的规则函数 - /// RemoveNullGroup(g => g.StartsWith("hah")) - /// 被移除编组的名称集合 - public static List RemoveNullGroup(this DBDictionary dict, Func func) - { - var groups = dict.GetGroups(g => g.NumEntities < 2); - List names = new(); - foreach (Group g in groups) - { - if (func(g.Name)) - { - names.Add(g.Name); - using (g.ForWrite()) - g.Erase(); - } - } - return names; - } - #endregion - - - - - - - - - - - - - - - - - - - - -} diff --git a/src/IFoxCAD.Cad/ExtensionMethod/DBObjectEx.cs b/src/IFoxCAD.Cad/ExtensionMethod/DBObjectEx.cs deleted file mode 100644 index a8590f5..0000000 --- a/src/IFoxCAD.Cad/ExtensionMethod/DBObjectEx.cs +++ /dev/null @@ -1,179 +0,0 @@ -using System.Linq; - -namespace IFoxCAD.Cad; - -/// -/// 实体对象扩展类 -/// -public static class DBObjectEx -{ - #region Xdata扩展 - - /// - /// 获取appName的索引区间 - /// - /// xdata - /// 注册名称 - /// 任务组码对象 - /// 返回任务组码的索引 - static List GetXdataAppIndex(XDataList data, string appName, DxfCode[] dxfCodes) - { - List acIndex = new(); - int appNameIndex = -1; - // int appNameIndexNext = -1; - - // 先找到属于它的名字索引,然后再找到下一个不属于它名字的索引,移除中间部分 - for (int i = 0; i < data.Count; i++) - { - if (data[i].TypeCode == (short)DxfCode.ExtendedDataRegAppName) - { - if (data[i].Value.ToString() == appName) - { - appNameIndex = i; - continue; - } - if (appNameIndex != -1) - { - // 找到了后面的appName - // appNameIndexNext = i; - break; - } - } - if (appNameIndex != -1 && // 找next的时候,获取任务(移除)的对象 - dxfCodes.Contains((DxfCode)data[i].TypeCode)) - acIndex.Add(i); - } - - // 当前app索引 - // if (appNameIndex == -1) - // return; - - // 下一个app索引,如果是空,就为末尾 - // if (appNameIndexNext == -1) - // appNameIndexNext = data.Count; - - return acIndex; - } - - /// - /// 删除扩展数据 - /// - /// 对象实例 - /// 应用程序名称 - /// 要删除数据的组码 - public static void RemoveXData(this DBObject obj, string appName, DxfCode dxfCode) - { - if (obj.XData == null) - return; - XDataList data = obj.XData; - - // 测试命令 addxdata removexdata - // 移除指定App的扩展 - var indexs = GetXdataAppIndex(data, appName, new DxfCode[] { dxfCode }); - if (indexs.Count == 0) - return; - - for (int i = indexs.Count - 1; i >= 0; i--) - data.RemoveAt(indexs[i]); - - using (obj.ForWrite()) - obj.XData = data; - } - /// - /// 修改扩展数据 - /// - /// 对象实例 - /// 应用程序名称 - /// 要修改数据的组码 - /// 新的数据 - public static void ChangeXData(this DBObject obj, string appName, DxfCode dxfCode, object newvalue) - { - if (obj.XData == null) - return; - XDataList data = obj.XData; - - var indexs = GetXdataAppIndex(data, appName, new DxfCode[] { dxfCode }); - if (indexs.Count == 0) - return; - - for (int i = indexs.Count - 1; i >= 0; i--) - data[i] = new TypedValue((short)dxfCode, newvalue); - - using (obj.ForWrite()) - obj.XData = data; - } - #endregion - - #region 读写模式切换 - - /// - /// 实体自动管理读写函数 - /// - /// 实体类型 - /// 实体对象 - /// 操作委托 - public static void ForWrite(this T obj, Action action) where T : DBObject - { - var _isNotifyEnabled = obj.IsNotifyEnabled; - var _isWriteEnabled = obj.IsWriteEnabled; - if (_isNotifyEnabled) - obj.UpgradeFromNotify(); - else if (!_isWriteEnabled) - obj.UpgradeOpen(); - - action?.Invoke(obj); - - if (_isNotifyEnabled) - obj.DowngradeToNotify(_isWriteEnabled); - else if (!_isWriteEnabled) - obj.DowngradeOpen(); - } - - /// - /// 打开模式提权 - /// - /// 实体对象 - /// 提权类对象 - public static UpgradeOpenManager ForWrite(this DBObject obj) - { - return new UpgradeOpenManager(obj); - } - - /// - /// 提权类 - /// - public class UpgradeOpenManager : IDisposable - { - private readonly DBObject _obj; - private readonly bool _isNotifyEnabled; - private readonly bool _isWriteEnabled; - - internal UpgradeOpenManager(DBObject obj) - { - _obj = obj; - _isNotifyEnabled = _obj.IsNotifyEnabled; - _isWriteEnabled = _obj.IsWriteEnabled; - if (_isNotifyEnabled) - _obj.UpgradeFromNotify(); - else if (!_isWriteEnabled) - _obj.UpgradeOpen(); - } - - #region IDisposable 成员 - - /// - /// 注销函数 - /// - public void Dispose() - { - if (_isNotifyEnabled) - _obj.DowngradeToNotify(_isWriteEnabled); - else if (!_isWriteEnabled) - _obj.DowngradeOpen(); - GC.SuppressFinalize(this); - } - - #endregion IDisposable 成员 - } - #endregion -} diff --git a/src/IFoxCAD.Cad/ExtensionMethod/DBTransEx.cs b/src/IFoxCAD.Cad/ExtensionMethod/DBTransEx.cs deleted file mode 100644 index d238252..0000000 --- a/src/IFoxCAD.Cad/ExtensionMethod/DBTransEx.cs +++ /dev/null @@ -1,92 +0,0 @@ -#define lack_test - -namespace IFoxCAD.Cad; - -#if lack_test -public static class DBTransEx -{ - - /* - * 0x01 - * db.Purge(ids)是获取未硬引用(无引用?)的对象,也就可以删除的. - * 0x02 - * 如果一个图元引用一个图层, - * 假设这个图元是可以删除的(实际上它可能来自于词典记录的id) => 那么它被 db.Purge(ids) 识别, - * 但是这个图层因为有硬引用,所以不被 db.Purge(ids) 识别, - * 只能删除图元之后,循环第二次再通过 db.Purge(ids) 获取图层id. - * 0x03 - * 因为删除之后,符号表内的引用可能被修改,因此需要重复遍历符号表. - * 0x04 - * 试试循环事务 - * 0x05 - * 无法过滤外部参照图层,使得全部图层打开了 - */ - - /// - /// 清理符号表 - /// - /// - /// - /// 排除外部参照:默认true,为false时候会令图层全部显示再清理,包括冻结 - public static void Purge(this DBTrans tr, SymModes sym = SymModes.All, bool excludeXref = true) - { - var ids = new ObjectIdCollection(); - var db = tr.Database; - - if ((sym & SymModes.BlockTable) == SymModes.BlockTable) - { - if (!excludeXref) - GetAllIds(tr, tr.BlockTable, ids, excludeXref); - else - tr.BlockTable.ForEach(tabRec => { - if (!tabRec.IsFromExternalReference) - ids.Add(tabRec.Id); - }); - } - if ((sym & SymModes.DimStyleTable) == SymModes.DimStyleTable) - GetAllIds(tr, tr.DimStyleTable, ids, excludeXref); - if ((sym & SymModes.LayerTable) == SymModes.LayerTable) - GetAllIds(tr, tr.LayerTable, ids, excludeXref); - if ((sym & SymModes.LinetypeTable) == SymModes.LinetypeTable) - GetAllIds(tr, tr.LinetypeTable, ids, excludeXref); - if ((sym & SymModes.TextStyleTable) == SymModes.TextStyleTable) - GetAllIds(tr, tr.TextStyleTable, ids, excludeXref); - if ((sym & SymModes.ViewportTable) == SymModes.ViewportTable) - GetAllIds(tr, tr.ViewportTable, ids, excludeXref); - if ((sym & SymModes.RegAppTable) == SymModes.RegAppTable) - GetAllIds(tr, tr.RegAppTable, ids, excludeXref); - - // SHUN007 说这两个表可能有错误 - if ((sym & SymModes.ViewTable) == SymModes.ViewTable) - GetAllIds(tr, tr.ViewTable, ids, excludeXref); - if ((sym & SymModes.UcsTable) == SymModes.UcsTable) - GetAllIds(tr, tr.UcsTable, ids, excludeXref); - - // Purge是查询能够清理的对象 - db.Purge(ids); - foreach (ObjectId id in ids) - id.Erase(); - } - - static void GetAllIds(DBTrans tr, - SymbolTable symbolTable, - ObjectIdCollection ids, - bool excludeXref = true) - where TTable : SymbolTable - where TRecord : SymbolTableRecord, new() - { - if (!excludeXref) - symbolTable.ForEach(id => ids.Add(id)); - else - { - symbolTable.ForEach(id => { - var tabRec = tr.GetObject(id); - if (tabRec == null) - return; - if (!tabRec.Name.Contains("|")) - ids.Add(tabRec.Id); - }); - } - } -} -#endif diff --git a/src/IFoxCAD.Cad/ExtensionMethod/EditorEx.cs b/src/IFoxCAD.Cad/ExtensionMethod/EditorEx.cs deleted file mode 100644 index 8e40ebf..0000000 --- a/src/IFoxCAD.Cad/ExtensionMethod/EditorEx.cs +++ /dev/null @@ -1,1090 +0,0 @@ -namespace IFoxCAD.Cad; - -/// -/// 命令行扩展类 -/// -public static class EditorEx -{ - #region 选择集 - /// - /// 选择穿过一个点的对象 - /// - /// 命令行对象 - /// 点 - /// 过滤器 - /// 选择集结果类 - public static PromptSelectionResult SelectAtPoint(this Editor editor, Point3d point, SelectionFilter? filter = default) - { - return editor.SelectCrossingWindow(point, point, filter); - } - - /// - /// 根据线宽创建图层选择集 - /// - /// 命令行对象 - /// 线宽 - /// 图层选择集 - public static SelectionSet SelectByLineWeight(this Editor editor, LineWeight lineWeight) - { - OpFilter filter = new OpEqual(370, lineWeight); - - var lays = - DBTrans.Top.LayerTable - .GetRecords() - .Where(ltr => ltr.LineWeight == lineWeight) - .Select(ltr => ltr.Name) - .ToArray(); - - if (lays.Length > 0) - { - filter = - new OpOr - { - filter, - new OpAnd - { - { 8, string.Join(",", lays) }, - { 370, LineWeight.ByLayer } - } - }; - } - - PromptSelectionResult res = editor.SelectAll(filter); - return res.Value; - } - - public static PromptSelectionResult? SSGet(this Editor editor, string? mode = null, SelectionFilter? filter = null, string[]? messages = null, Dictionary? keywords = null) - { - var pso = new PromptSelectionOptions(); - PromptSelectionResult? ss = null; - if (mode is not null) - { - mode = mode.ToUpper(); - pso.SinglePickInSpace = mode.Contains(":A"); - pso.RejectObjectsFromNonCurrentSpace = mode.Contains(":C"); - pso.AllowDuplicates = mode.Contains(":D"); - pso.SelectEverythingInAperture = mode.Contains(":E"); - pso.RejectObjectsOnLockedLayers = mode.Contains(":L"); - pso.PrepareOptionalDetails = mode.Contains(":N"); - pso.SingleOnly = mode.Contains(":S"); - pso.RejectPaperspaceViewport = mode.Contains(":V"); - pso.AllowSubSelections = mode.Contains("-A"); - pso.ForceSubSelections = mode.Contains("-F"); - - } - if (messages is not null) - { - pso.MessageForAdding = messages[0]; - pso.MessageForRemoval = messages[1]; - } - - if (keywords is not null) - { - foreach (var keyword in keywords.Keys) - pso.Keywords.Add(keyword); - if (pso.MessageForRemoval is null) - pso.MessageForAdding = "选择对象"; - pso.MessageForAdding += $"[{string.Join(" / ", keywords.Keys.ToArray())}]"; - pso.KeywordInput += (s, e) => { - if (keywords.ContainsKey(e.Input)) - keywords[e.Input].Invoke(); - }; - - } - try - { - if (filter is not null) - ss = editor.GetSelection(pso, filter); - else - ss = editor.GetSelection(pso); - } - catch (Exception e) - { - editor.WriteMessage($"\nKey is {e.Message}"); - } - return ss; - } - - - /* - * // 定义选择集选项 - * var pso = new PromptSelectionOptions - * { - * AllowDuplicates = false, // 重复选择 - * }; - * - * // getai遍历全图选择块有用到 - * var dic = new Dictionary() { - * { "Z,全部同名", ()=> { - * getai = BlockHelper.EnumAttIdentical.AllBlockName; - * SendEsc.Esc(); - * }}, - * { "X,动态块显示", ()=> { - * getai = BlockHelper.EnumAttIdentical.Display; - * }}, - * { "V,属性值-默认", ()=> { - * getai = BlockHelper.EnumAttIdentical.DisplayAndTagText; - * }}, - * // 允许以下操作,相同的会加入前面的 - * // { "V,属性值-默认|X,啊啊啊啊", ()=> { - * - * // }}, - * }; - * pso.SsgetAddKeys(dic); - * - * // 创建选择集过滤器,只选择块对象 - * var filList = new TypedValue[] { new TypedValue((int)DxfCode.Start, "INSERT") }; - * var filter = new SelectionFilter(filList); - * ssPsr = ed.GetSelection(pso, filter); - */ - - /// - /// 添加选择集关键字和回调 - /// - /// 选择集配置 - /// 关键字,回调委托 - /// - public static void SsgetAddKeys(this PromptSelectionOptions pso, - Dictionary dicActions) - { - Dictionary tmp = new(); - // 后缀名的|号切割,移除掉,组合成新的加入tmp - for (int i = dicActions.Count - 1; i >= 0; i--) - { - var pair = dicActions.ElementAt(i); - var key = pair.Key; - var keySp = key.Split('|'); - if (keySp.Length < 2) - continue; - - for (int j = 0; j < keySp.Length; j++) - { - var item = keySp[j]; - // 防止多个后缀通过|符越过词典约束同名 - // 后缀(key)含有,而且Action(value)不同,就把Action(value)累加到后面. - if (dicActions.ContainsKey(item)) - { - if (dicActions[item] != dicActions[key]) - dicActions[item] += dicActions[key]; - } - else - tmp.Add(item, dicActions[key]); - } - dicActions.Remove(key); - } - - foreach (var item in tmp) - dicActions.Add(item.Key, item.Value); - - // 去除关键字重复的,把重复的执行动作移动到前面 - for (int i = 0; i < dicActions.Count; i++) - { - var pair1 = dicActions.ElementAt(i); - var key1 = pair1.Key; - - for (int j = dicActions.Count - 1; j > i; j--) - { - var pair2 = dicActions.ElementAt(j); - var key2 = pair2.Key; - - if (key1.Split(',')[0] == key2.Split(',')[0]) - { - if (dicActions[key1] != dicActions[key2]) - dicActions[key1] += dicActions[key2]; - dicActions.Remove(key2); - } - } - } - - foreach (var item in dicActions) - { - var keySplitS = item.Key.Split(new string[] { ",", "|" }, StringSplitOptions.RemoveEmptyEntries); - for (int i = 0; i < keySplitS.Length; i += 2) - pso.Keywords.Add(keySplitS[i], keySplitS[i], - keySplitS[i + 1] + "(" + keySplitS[i] + ")"); - } - - // 回调的时候我想用Dict的O(1)索引, - // 但是此函数内进行new Dictionary() 在函数栈释放的时候,它被释放掉了. - // 因此 dicActions 参数的生命周期 - tmp = new(dicActions); - dicActions.Clear(); - foreach (var item in tmp) - dicActions.Add(item.Key.Split(',')[0], item.Value); - - var keyWords = pso.Keywords; - // 从选择集命令中显示关键字 - pso.MessageForAdding = keyWords.GetDisplayString(true); - // 关键字回调事件 ssget关键字 - pso.KeywordInput += (sender, e) => { - dicActions[e.Input].Invoke(); - }; - } - - - - - - - // #region 即时选择样板 - // /// - // /// 即时选择,框选更新关键字 - // /// - // public static void SelectTest() - // { - // Env.Editor.WriteMessage("\n[白嫖工具]--测试"); - // // 激活选中事件 - // Env.Editor.SelectionAdded += SelectTest_SelectionAdded; - // // 初始化坐标系 - // Env.Editor.CurrentUserCoordinateSystem = Matrix3d.Identity; - - // // 创建过滤器 - // var sf = new OpEqual(0, "arc"); - // var pso = new PromptSelectionOptions - // { - // MessageForAdding = "\n请选择对象:" - // }; - - // pso.Keywords.Add("Z"); - // pso.Keywords.Add("X"); - // pso.Keywords.Add("Q"); - // // 注册关键字 - // pso.KeywordInput += SelectTest_KeywordInput; - // try - // { - // // 用户选择 - // var psr = Env.Editor.GetSelection(pso, sf); - // // 处理代码 - - - // } - // catch (Exception ex)// 捕获关键字 - // { - // if (ex.Message == "XuError") - // { - // // 关闭关键字事件 - // pso.KeywordInput -= SelectTest_KeywordInput; - // // 关闭选中事件 - // Env.Editor.SelectionAdded -= SelectTest_SelectionAdded; - // // 重新调用自身 - // ZengLiangYuanJiao(); - // } - // } - // // 关闭关键字事件 - // pso.KeywordInput -= SelectTest_KeywordInput; - // // 关闭选中事件 - // Env.Editor.SelectionAdded -= SelectTest_SelectionAdded; - // } - - // /// - // /// 即时选择 - // /// - // /// - // /// - // private static void SelectTest_SelectionAdded(object sender, SelectionAddedEventArgs e) - // { - // // 关闭选中事件 - // Env.Editor.SelectionAdded -= SelectTest_SelectionAdded; - // using (var tr = new DBTrans()) - // { - // // 处理代码 - // for (int i = 0; i < e.AddedObjects.Count; i++) - // { - - - // // 处理完移除已处理的对象 - // e.Remove(i); - // } - // } - // // 激活选中事件 - // Env.Editor.SelectionAdded += SelectTest_SelectionAdded; - // } - - // /// - // /// 关键字响应 - // /// - // /// - // /// - // private static void SelectTest_KeywordInput(object sender, SelectionTextInputEventArgs e) - // { - // // 获取关键字 - // switch (e.Input) - // { - // case "Z": - // { - // break; - // } - // case "X": - // { - // break; - // } - - // case "Q": - // { - // break; - // } - // } - // // 抛出异常,用于更新提示信息 - // throw new ArgumentException("XuError"); - // } - // #endregion - #endregion - - #region Info - - /// - /// 带错误提示对话框的打印信息函数 - /// - /// 带格式项的字符串 - /// 指定格式化的对象数组 - public static void StreamMessage(string format, params object[] args) - { - StreamMessage(string.Format(format, args)); - } - - /// - /// 带错误提示对话框的打印信息函数 - /// - /// 打印信息 - public static void StreamMessage(string message) - { - try - { - if (HasEditor()) - WriteMessage(message); - else - InfoMessageBox(message); - } - catch (System.Exception ex) - { - Message(ex); - } - } - - /// - /// 异常信息对话框 - /// - /// 异常 - public static void Message(System.Exception ex) - { - try - { - System.Windows.Forms.MessageBox.Show( - ex.ToString(), - "Error", - System.Windows.Forms.MessageBoxButtons.OK, - System.Windows.Forms.MessageBoxIcon.Error); - } - catch - { - } - } - - /// - /// 提示信息对话框 - /// - /// 对话框的标题 - /// 对话框文本 - public static void InfoMessageBox(string caption, string message) - { - try - { - System.Windows.Forms.MessageBox.Show( - message, - caption, - System.Windows.Forms.MessageBoxButtons.OK, - System.Windows.Forms.MessageBoxIcon.Information); - } - catch (System.Exception ex) - { - Message(ex); - } - } - - /// - /// 提示信息对话框 - /// - /// 对话框的标题 - /// 带格式化项的对话框文本 - /// 指定格式化的对象数组 - public static void InfoMessageBox(string caption, string format, params object[] args) - { - InfoMessageBox(caption, string.Format(format, args)); - } - - /// - /// 提示信息对话框,默认标题为NFox.Cad - /// - /// 对话框文本 - public static void InfoMessageBox(string message) - { - InfoMessageBox("NFox.Cad", message); - } - - /// - /// 提示信息对话框 - /// - /// 带格式化项的对话框文本 - /// 指定格式化的对象数组 - public static void InfoMessageBox(string format, params object[] args) - { - InfoMessageBox(string.Format(format, args)); - } - - /// - /// 命令行打印字符串 - /// - /// 字符串 - public static void WriteMessage(string message) - { - try - { - if (Acceptable()) - Acap.DocumentManager.MdiActiveDocument.Editor.WriteMessage("\n" + message); - else - return; - } - catch (System.Exception ex) - { - Message(ex); - } - } - - /// - /// 命令行打印字符串 - /// - /// 带格式化项的文本 - /// 指定格式化的对象数组 - public static void WriteMessage(string format, params object[] args) - { - WriteMessage(string.Format(format, args)); - } - - /// - /// 判断是否有活动的编辑器对象 - /// - /// 有,没有 - public static bool HasEditor() - { - return Acap.DocumentManager.MdiActiveDocument is not null - && Acap.DocumentManager.Count != 0 - && Acap.DocumentManager.MdiActiveDocument.Editor is not null; - } - - /// - /// 判断是否可以打印字符串 - /// - /// 可以打印,不可以打印 - public static bool Acceptable() - { - return HasEditor() - && !Acap.DocumentManager.MdiActiveDocument.Editor.IsDragging; - } - - #endregion Info - - #region 画矢量线 - - /// - /// 根据点表返回矢量线的列表 - /// - /// 点表 - /// 是否闭合, 为闭合, 为不闭合 - /// - public static List GetLines(IEnumerable pnts, bool isClosed) - { - - var itor = pnts.GetEnumerator(); - if (!itor.MoveNext()) - return new List(); - - List values = new(); - - TypedValue tvFirst = new((int)LispDataType.Point2d, itor.Current); - TypedValue tv1; - TypedValue tv2 = tvFirst; - - while (itor.MoveNext()) - { - tv1 = tv2; - tv2 = new TypedValue((int)LispDataType.Point2d, itor.Current); - values.Add(tv1); - values.Add(tv2); - } - - if (isClosed) - { - values.Add(tv2); - values.Add(tvFirst); - } - - return values; - } - - /// - /// 画矢量线 - /// - /// 编辑器对象 - /// 点表 - /// 颜色码 - /// 是否闭合, 为闭合, 为不闭合 - public static void DrawVectors(this Editor editor, IEnumerable pnts, short colorIndex, bool isClosed) - { - var rlst = - new LispList { { LispDataType.Int16, colorIndex } }; - rlst.AddRange(GetLines(pnts, isClosed)); - editor.DrawVectors(rlst, editor.CurrentUserCoordinateSystem); - } - - /// - /// 画矢量线 - /// - /// 编辑器对象 - /// 点表 - /// 颜色码 - public static void DrawVectors(this Editor editor, IEnumerable pnts, short colorIndex) - { - editor.DrawVectors(pnts, colorIndex, false); - } - - /// - /// 用矢量线画近似圆(正多边形) - /// - /// 编辑器对象 - /// 点表 - /// 颜色码 - /// 半径 - /// 多边形边的个数 - public static void DrawCircles(this Editor editor, IEnumerable pnts, short colorIndex, double radius, int numEdges) - { - var rlst = - new LispList { { LispDataType.Int16, colorIndex } }; - - foreach (Point2d pnt in pnts) - { - Vector2d vec = Vector2d.XAxis * radius; - double angle = Math.PI * 2 / numEdges; - - List tpnts = new() - { - pnt + vec - }; - for (int i = 1; i < numEdges; i++) - { - tpnts.Add(pnt + vec.RotateBy(angle * i)); - } - - rlst.AddRange(GetLines(tpnts, true)); - } - editor.DrawVectors(rlst, editor.CurrentUserCoordinateSystem); - } - - /// - /// 用矢量线画近似圆(正多边形) - /// - /// 编辑器对象 - /// 点 - /// 颜色码 - /// 半径 - /// 多边形边的个数 - public static void DrawCircle(this Editor editor, Point2d pnt, short colorIndex, double radius, int numEdges) - { - Vector2d vec = Vector2d.XAxis * radius; - double angle = Math.PI * 2 / numEdges; - - List pnts = new() - { - pnt + vec - }; - for (int i = 1; i < numEdges; i++) - { - pnts.Add(pnt + vec.RotateBy(angle * i)); - } - - editor.DrawVectors(pnts, colorIndex, true); - } - - #endregion - - #region 矩阵 - - /// - /// 获取UCS到WCS的矩阵 - /// - /// 命令行对象 - /// 变换矩阵 - public static Matrix3d GetMatrixFromUcsToWcs(this Editor editor) - { - return editor.CurrentUserCoordinateSystem; - } - - /// - /// 获取WCS到UCS的矩阵 - /// - /// 命令行对象 - /// 变换矩阵 - public static Matrix3d GetMatrixFromWcsToUcs(this Editor editor) - { - return editor.CurrentUserCoordinateSystem.Inverse(); - } - - /// - /// 获取MDCS(模型空间)到WCS的矩阵 - /// - /// 命令行对象 - /// 变换矩阵 - public static Matrix3d GetMatrixFromMDcsToWcs(this Editor editor) - { - Matrix3d mat; - using ViewTableRecord vtr = editor.GetCurrentView(); - mat = Matrix3d.PlaneToWorld(vtr.ViewDirection); - mat = Matrix3d.Displacement(vtr.Target - Point3d.Origin) * mat; - return Matrix3d.Rotation(-vtr.ViewTwist, vtr.ViewDirection, vtr.Target) * mat; - } - - /// - /// 获取WCS到MDCS(模型空间)的矩阵 - /// - /// 命令行对象 - /// 变换矩阵 - public static Matrix3d GetMatrixFromWcsToMDcs(this Editor editor) - { - return editor.GetMatrixFromMDcsToWcs().Inverse(); - } - - /// - /// 获取MDCS(模型空间)到PDCS(图纸空间)的矩阵 - /// - /// 命令行对象 - /// 变换矩阵 - public static Matrix3d GetMatrixFromMDcsToPDcs(this Editor editor) - { - if ((short)Env.GetVar("TILEMODE") == 1) - throw new ArgumentException("TILEMODE == 1..Espace papier uniquement"); - - Database db = editor.Document.Database; - Matrix3d mat; - using (Transaction tr = db.TransactionManager.StartTransaction()) - { - var vp = tr.GetObject(editor.CurrentViewportObjectId, OpenMode.ForRead) as Viewport; - if (vp?.Number == 1) - { - try - { - editor.SwitchToModelSpace(); - vp = tr.GetObject(editor.CurrentViewportObjectId, OpenMode.ForRead) as Viewport; - editor.SwitchToPaperSpace(); - } - catch - { - throw new Exception("Aucun fenêtre active...ErrorStatus.InvalidInput"); - } - } - Point3d vCtr = new(vp!.ViewCenter.X, vp.ViewCenter.Y, 0.0); - mat = Matrix3d.Displacement(vCtr.GetAsVector().Negate()); - mat = Matrix3d.Displacement(vp.CenterPoint.GetAsVector()) * mat; - mat = Matrix3d.Scaling(vp.CustomScale, vp.CenterPoint) * mat; - tr.Commit(); - } - return mat; - } - - /// - /// 获取PDCS(图纸空间)到MDCS(模型空间)的矩阵 - /// - /// 命令行对象 - /// 变换矩阵 - public static Matrix3d GetMatrixFromPDcsToMDcs(this Editor editor) - { - return editor.GetMatrixFromMDcsToPDcs().Inverse(); - } - - /// - /// 获取变换矩阵 - /// - /// 命令行对象 - /// 源坐标系 - /// 目标坐标系 - /// 变换矩阵 - public static Matrix3d GetMatrix(this Editor editor, CoordinateSystemCode from, CoordinateSystemCode to) - { -#if ac2009 - switch (from) - { - case CoordinateSystemCode.Wcs: - switch (to) - { - case CoordinateSystemCode.Ucs: - return editor.GetMatrixFromWcsToUcs(); - - case CoordinateSystemCode.MDcs: - return editor.GetMatrixFromMDcsToWcs(); - - case CoordinateSystemCode.PDcs: - throw new Exception("To be used only with DCS...ErrorStatus.InvalidInput"); - } - break; - case CoordinateSystemCode.Ucs: - switch (to) - { - case CoordinateSystemCode.Wcs: - return editor.GetMatrixFromUcsToWcs(); - - case CoordinateSystemCode.MDcs: - return editor.GetMatrixFromUcsToWcs() * editor.GetMatrixFromWcsToMDcs(); - - case CoordinateSystemCode.PDcs: - throw new Exception("To be used only with DCS... ErrorStatus.InvalidInput"); - } - break; - case CoordinateSystemCode.MDcs: - switch (to) - { - case CoordinateSystemCode.Wcs: - return editor.GetMatrixFromMDcsToWcs(); - - case CoordinateSystemCode.Ucs: - return editor.GetMatrixFromMDcsToWcs() * editor.GetMatrixFromWcsToUcs(); - - case CoordinateSystemCode.PDcs: - return editor.GetMatrixFromMDcsToPDcs(); - } - break; - case CoordinateSystemCode.PDcs: - switch (to) - { - case CoordinateSystemCode.Wcs: - throw new Exception("To be used only with DCS... ErrorStatus.InvalidInput"); - case CoordinateSystemCode.Ucs: - throw new Exception("To be used only with DCS... ErrorStatus.InvalidInput"); - case CoordinateSystemCode.MDcs: - return editor.GetMatrixFromPDcsToMDcs(); - } - break; - } - return Matrix3d.Identity; -#else - return (from, to) switch - { - (CoordinateSystemCode.Wcs, CoordinateSystemCode.Ucs) => editor.GetMatrixFromWcsToUcs(), - (CoordinateSystemCode.Wcs, CoordinateSystemCode.MDcs) => editor.GetMatrixFromWcsToMDcs(), - (CoordinateSystemCode.Ucs, CoordinateSystemCode.Wcs) => editor.GetMatrixFromUcsToWcs(), - (CoordinateSystemCode.Ucs, CoordinateSystemCode.MDcs) => editor.GetMatrixFromUcsToWcs() * editor.GetMatrixFromWcsToMDcs(), - (CoordinateSystemCode.MDcs, CoordinateSystemCode.Wcs) => editor.GetMatrixFromMDcsToWcs(), - (CoordinateSystemCode.MDcs, CoordinateSystemCode.Ucs) => editor.GetMatrixFromMDcsToWcs() * editor.GetMatrixFromWcsToUcs(), - (CoordinateSystemCode.MDcs, CoordinateSystemCode.PDcs) => editor.GetMatrixFromMDcsToPDcs(), - (CoordinateSystemCode.PDcs, CoordinateSystemCode.MDcs) => editor.GetMatrixFromPDcsToMDcs(), - (CoordinateSystemCode.PDcs, CoordinateSystemCode.Wcs or CoordinateSystemCode.Ucs) - or (CoordinateSystemCode.Wcs or CoordinateSystemCode.Ucs, CoordinateSystemCode.PDcs) => throw new Exception("To be used only with DCS...ErrorStatus.InvalidInput"), - (_, _) => Matrix3d.Identity - }; -#endif - } - - #endregion - - #region 缩放 - - /// - /// 缩放窗口范围 - /// - /// 命令行对象 - /// 窗口左下点 - /// 窗口右上点 - public static void ZoomWindow(this Editor ed, Point3d minPoint, Point3d maxPoint) - { - ViewTableRecord cvtr = ed.GetCurrentView(); - ViewTableRecord vtr = new(); - vtr.CopyFrom(cvtr); - - Point3d[] oldpnts = new Point3d[] { minPoint, maxPoint }; - Point3d[] pnts = new Point3d[8]; - Point3d[] dpnts = new Point3d[8]; - for (int i = 0; i < 2; i++) - { - for (int j = 0; j < 2; j++) - { - for (int k = 0; k < 2; k++) - { - int n = i * 4 + j * 2 + k; - pnts[n] = new Point3d(oldpnts[i][0], oldpnts[j][1], oldpnts[k][2]); - dpnts[n] = pnts[n].TransformBy(ed.GetMatrixFromWcsToMDcs()); - } - } - } - double xmin, xmax, ymin, ymax; - xmin = xmax = dpnts[0][0]; - ymin = ymax = dpnts[0][1]; - for (int i = 1; i < 8; i++) - { - xmin = Math.Min(xmin, dpnts[i][0]); - xmax = Math.Max(xmax, dpnts[i][0]); - ymin = Math.Min(ymin, dpnts[i][1]); - ymax = Math.Max(ymax, dpnts[i][1]); - } - - vtr.Width = xmax - xmin; - vtr.Height = ymax - ymin; - vtr.CenterPoint = (dpnts[0] + (dpnts[7] - dpnts[0]) / 2).Convert2d(new Plane()); - - ed.SetCurrentView(vtr); - ed.Regen(); - } - - /// - /// 缩放窗口范围 - /// - /// 命令行对象 - /// 窗口范围点 - public static void ZoomWindow(this Editor ed, Extents3d ext) - { - ZoomWindow(ed, ext.MinPoint, ext.MaxPoint); - } - - /// - /// 缩放比例 - /// - /// 命令行对象 - /// 中心点 - /// 窗口宽 - /// 窗口高 - public static void Zoom(this Editor ed, Point3d CenPt, double width, double height) - { - using ViewTableRecord view = ed.GetCurrentView(); - view.Width = width; - view.Height = height; - view.CenterPoint = new Point2d(CenPt.X, CenPt.Y); - ed.SetCurrentView(view);// 更新当前视图 - } - - /// - /// 缩放窗口范围 - /// - /// 命令行对象 - /// 第一点 - /// 对角点 - /// 偏移距离 - public static void ZoomWindow(this Editor ed, Point3d lpt, Point3d rpt, double offsetDist = 0.00) - { - Extents3d extents = new(); - extents.AddPoint(lpt); - extents.AddPoint(rpt); - rpt = extents.MaxPoint + new Vector3d(offsetDist, offsetDist, 0); - lpt = extents.MinPoint - new Vector3d(offsetDist, offsetDist, 0); - Vector3d ver = rpt - lpt; - ed.Zoom(lpt + ver / 2, ver.X, ver.Y); - } - - - /// - /// 获取有效的数据库范围 - /// - /// 数据库 - /// 容差值:图元包围盒会超过数据库边界,用此参数扩大边界 - /// - public static Extents3d? GetValidExtents3d(this Database db, double extention = 1e-6) - { - db.UpdateExt(true);// 更新当前模型空间的范围 - var ve = new Vector3d(extention, extention, extention); - // 数据库没有图元的时候,min是大,max是小,导致新建出错 - // 数据如下: - // min.X == 1E20 && min.Y == 1E20 && min.Z == 1E20 && - // max.X == -1E20 && max.Y == -1E20 && max.Z == -1E20) - var a = db.Extmin; - var b = db.Extmax; - if (a.X < b.X && a.Y < b.Y) - return new Extents3d(db.Extmin - ve, db.Extmax + ve); - - return null; - } - - /// - /// 动态缩放 - /// - /// 命令行对象 - /// 偏移距离 - public static void ZoomExtents(this Editor ed, double offsetDist = 0.00) - { - Database db = ed.Document.Database; - // db.UpdateExt(true); // GetValidExtents3d内提供了 - var dbExtent = db.GetValidExtents3d(); - if (dbExtent == null) - ed.ZoomWindow(Point3d.Origin, new Point3d(1, 1, 0), offsetDist); - else - ed.ZoomWindow(db.Extmin, db.Extmax, offsetDist); - } - - /// - /// 根据实体对象的范围显示视图 - /// - /// 命令行对象 - /// Entity对象 - /// 偏移距离 - public static void ZoomObject(this Editor ed, Entity ent, double offsetDist = 0.00) - { - Extents3d ext = ent.GeometricExtents; - ed.ZoomWindow(ext.MinPoint, ext.MaxPoint, offsetDist); - } - - #endregion - - #region Get交互类 - - /// - /// 获取Point - /// - /// 命令行对象 - /// 提示信息 - /// 提示使用的基点 - /// - public static PromptPointResult GetPoint(this Editor ed, string Message, Point3d BasePoint) - { - PromptPointOptions ptOp = new(Message) - { - BasePoint = BasePoint, - UseBasePoint = true - }; - return ed.GetPoint(ptOp); - } - - /// - /// 获取double值 - /// - /// 命令行对象 - /// 提示信息 - /// double默认值 - /// - public static PromptDoubleResult GetDouble(this Editor ed, string Message, double DefaultValue = 1.0) - { - PromptDoubleOptions douOp = new(Message) - { - DefaultValue = DefaultValue - }; - return ed.GetDouble(douOp); - } - - /// - /// 获取int值 - /// - /// 命令行对象 - /// 提示信息 - /// double默认值 - /// - public static PromptIntegerResult GetInteger(this Editor ed, string Message, int DefaultValue = 1) - { - PromptIntegerOptions douOp = new(Message) - { - DefaultValue = DefaultValue - }; - return ed.GetInteger(douOp); - } - - /// - /// 获取string值 - /// - /// 命令行对象 - /// 提示信息 - /// string默认值 - /// - public static PromptResult GetString(this Editor ed, string Message, string DefaultValue = "") - { - PromptStringOptions strOp = new(Message) - { - DefaultValue = DefaultValue - }; - return ed.GetString(strOp); - } - - #endregion - - #region 执行lisp -#if NET35 - [DllImport("acad.exe", CallingConvention = CallingConvention.Cdecl, EntryPoint = "acedInvoke")] -#else - [DllImport("accore.dll", CallingConvention = CallingConvention.Cdecl, EntryPoint = "acedInvoke")] -#endif - static extern int AcedInvoke(IntPtr args, out IntPtr result); - - -#if NET35 - [DllImport("acad.exe", CharSet = CharSet.Unicode, CallingConvention = CallingConvention.Cdecl, - EntryPoint = "?acedEvaluateLisp@@YAHPB_WAAPAUresbuf@@@Z")] -#else - // 高版本此接口不能使用lisp(command "xx"),但是可以直接在自动运行接口上 - [DllImport("accore.dll", CharSet = CharSet.Unicode, CallingConvention = CallingConvention.Cdecl, - EntryPoint = "?acedEvaluateLisp@@YAHPEB_WAEAPEAUresbuf@@@Z")] -#endif - [System.Security.SuppressUnmanagedCodeSecurity]// 初始化默认值 - static extern int AcedEvaluateLisp(string lispLine, out IntPtr result); - -#pragma warning disable CA2101 // 指定对 P/Invoke 字符串参数进行封送处理 -#if NET35 - [DllImport("acad.exe", CharSet = CharSet.Auto, CallingConvention = CallingConvention.Cdecl, - EntryPoint = "ads_queueexpr")] -#else - [DllImport("accore.dll", CharSet = CharSet.Auto, CallingConvention = CallingConvention.Cdecl, - EntryPoint = "ads_queueexpr")] -#endif -#pragma warning restore CA2101 // 指定对 P/Invoke 字符串参数进行封送处理 - static extern int Ads_queueexpr(string strExpr); - - public enum RunLispFlag : byte - { - AdsQueueexpr = 1, - AcedEvaluateLisp = 2, - SendStringToExecute = 4, - } - - /// - /// 发送lisp语句字符串到cad执行 - /// - /// 编辑器对象 - /// lisp语句 - /// 运行方式 - /// 缓冲结果,返回值 -#pragma warning disable IDE0060 // 删除未使用的参数 - public static ResultBuffer? RunLisp(this Editor ed, - string lispCode, - RunLispFlag flag = RunLispFlag.AdsQueueexpr) -#pragma warning restore IDE0060 // 删除未使用的参数 - { - /* - * 测试命令: - * [CommandMethod("CmdTest_RunLisp")] - * public static void CmdTest_RunLisp() - * { - * var res = SendLisp.RunLisp("(setq abc 10)"); - * } - * 调用方式: - * (command "CmdTest_RunLisp1") - * bug说明: - * AcedEvaluateLisp接口在高版本调用时候没有运行成功,使得 !abc 没有值 - * 经过测试,cad08调用成功,此bug与CommandFlags无关 - * 解决方案: - * 0x01 用异步接口,但是这样是显式调用了: - * (setq thisdrawing (vla-get-activedocument (vlax-get-acad-object)))(vla-SendCommand thisdrawing "CmdTest_RunLisp1 ") - * 0x02 使用 Ads_queueexpr 接口 - */ - if ((flag & RunLispFlag.AdsQueueexpr) == RunLispFlag.AdsQueueexpr) - { - // 这个在08/12发送lisp不会出错,但是发送bo命令出错了. - // 0x01 设置 CommandFlags.Session 可以同步, - // 0x02 自执行发送lisp都是异步,(用来发送 含有(command)的lisp的) - _ = Ads_queueexpr(lispCode + "\n"); - } - if ((flag & RunLispFlag.AcedEvaluateLisp) == RunLispFlag.AcedEvaluateLisp) - { - _ = AcedEvaluateLisp(lispCode, out IntPtr rb); - if (rb != IntPtr.Zero) - return DisposableWrapper.Create(typeof(ResultBuffer), rb, true) as ResultBuffer; - } - if ((flag & RunLispFlag.SendStringToExecute) == RunLispFlag.SendStringToExecute) - { - var dm = Acap.DocumentManager; - var doc = dm.MdiActiveDocument; - doc.SendStringToExecute(lispCode + "\n", false, false, false); - } - return null; - } - #endregion -} diff --git a/src/IFoxCAD.Cad/ExtensionMethod/EntityBoundingInfo.cs b/src/IFoxCAD.Cad/ExtensionMethod/EntityBoundingInfo.cs deleted file mode 100644 index 43010d6..0000000 --- a/src/IFoxCAD.Cad/ExtensionMethod/EntityBoundingInfo.cs +++ /dev/null @@ -1,264 +0,0 @@ -#define Debug_Cause_Error -namespace IFoxCAD.Cad; - -/// -/// AABB和OBB信息 -/// -public struct BoundingInfo -{ - public double MinX; - public double MinY; - public double MinZ; - - public double MaxX; - public double MaxY; - public double MaxZ; - - /// - /// AABB这里永远是0 - /// - public double Angle; - public bool IsEffective; - - public Point3d Min => new(MinX, MinY, MinZ); - public Point3d Max => new(MaxX, MaxY, MaxZ); - public Extents3d Extents3d => new(Min, Max); - public Extents2d Extents2d => new(MinX, MinY, MaxX, MaxY); - - public BoundingInfo(double minX, double minY, double minZ, - double maxX, double maxY, double maxZ, - bool isEffective, double angle = 0) - { - MinX = minX; - MinY = minY; - MinZ = minZ; - MaxX = maxX; - MaxY = maxY; - MaxZ = maxZ; - IsEffective = isEffective; - Angle = angle; - } - - public BoundingInfo(Point3d min, Point3d max, bool isEffective, double angle = 0) - : this(min.X, min.Y, min.Z, - max.X, max.Y, max.Z, - isEffective, angle) - { } - - // public BoundingInfo(Rect rect, double angle = 0) - // { - // MinX = rect.X; - // MinY = rect.Y; - // MinZ = 0; - // MaxX = rect.Right; - // MaxY = rect.Top; - // MaxZ = 0; - // Angle = angle; - // } -} - -public class EntityBoundingInfo -{ - #region 保存异常类型的日志 - /// - /// 包围盒错误文件路径 - /// - public static string BoxLogAddress - { - get - { - _BoxLogAddress ??= LogHelper.GetDefaultOption(nameof(GetBoundingInfo) + ".config"); - return _BoxLogAddress; - } - set { _BoxLogAddress = value; } - } - static string? _BoxLogAddress; - static readonly HashSet _typeNames; - /// - /// 为了保证更好的性能, - /// 只是在第一次调用此功能的时候进行读取, - /// 免得高频调用时候高频触发磁盘 - /// - static EntityBoundingInfo() - { - _typeNames = new(); - if (!File.Exists(BoxLogAddress)) - return; - ExceptionToHashSet(); - } - - /// - /// 读取日志的异常到容器 - /// - static void ExceptionToHashSet() - { - var old_LogAddress = LogHelper.LogAddress; - try - { - LogHelper.OptionFile(BoxLogAddress); - var logTxts = new FileLogger().ReadLog(); - for (int i = 0; i < logTxts.Length; i++) - { - var line = logTxts[i]; - if (line.Contains(nameof(LogTxt.备注信息))) - { - int index = line.IndexOf(":"); - index = line.IndexOf("\"", index) + 1;// 1是"\"" - int index2 = line.IndexOf("\"", index); - var msg = line.Substring(index, index2 - index); - _typeNames.Add(msg); - } - } - } - finally - { - LogHelper.LogAddress = old_LogAddress; - } - } - - /// - /// 写入容器类型到异常日志 - /// - /// - /// - static void ExceptionToLog(Exception e, Entity ent) - { - // 无法处理的错误类型将被记录 - // 如果错误无法try,而是cad直接致命错误,那么此处也不会被写入, - // 这种情况无法避免程序安全性,总不能写了日志再去删除日志词条,这样会造成频繁IO的 - // 遇到一个不认识的类型再去写入?然后记录它是否可以写入? - var old_LogAddress = LogHelper.LogAddress; - var old_FlagOutFile = LogHelper.FlagOutFile; - try - { - LogHelper.FlagOutFile = true; - LogHelper.OptionFile(BoxLogAddress); - e.WriteLog(ent.GetType().Name, LogTarget.FileNotException); - } - finally - { - LogHelper.LogAddress = old_LogAddress; - LogHelper.FlagOutFile = old_FlagOutFile; - } - } - #endregion - - /// - /// 获取图元包围盒 - /// - /// - /// (左下角,右上角,是否有效) - /// 异常: - /// 会将包围盒类型记录到所属路径中,以此查询 - public static BoundingInfo GetBoundingInfo(Entity ent) - { -#if Debug_Cause_Error - // 错误类型处理 - if (ent is AttributeDefinition) // 属性定义 - return new(Point3d.Origin, Point3d.Origin, false); - else if (ent is Xline xline)// 参照线 - return new(xline.BasePoint, xline.BasePoint, true); - else if (ent is Ray ray)// 射线 - return new(ray.BasePoint, ray.BasePoint, true); -#endif - // 指定类型处理 - if (ent is BlockReference brf) - return GetBoxInfoInBlockReference(brf); - else if (ent is MText mText) - return GetBoxInfoInMText(mText); - else if (!_typeNames.Contains(ent.GetType().Name)) // 屏蔽天正等等缺失包围盒的类型 - try - { - return new(ent.GeometricExtents.MinPoint, ent.GeometricExtents.MaxPoint, true); - } - catch (Exception e) { ExceptionToLog(e, ent); } - return new(Point3d.Origin, Point3d.Origin, false); - } - - /// - /// 处理块参照 - /// - static BoundingInfo GetBoxInfoInBlockReference(BlockReference brf) - { - try - { - // 这个获取是原点附近,需要平移到块基点 - var fit = brf.GeometryExtentsBestFit(); - var minX = fit.MinPoint.X + brf.Position.X; - var minY = fit.MinPoint.Y + brf.Position.Y; - var minZ = fit.MinPoint.Z + brf.Position.Z; - var maxX = fit.MaxPoint.X + brf.Position.X; - var maxY = fit.MaxPoint.Y + brf.Position.Y; - var maxZ = fit.MaxPoint.Z + brf.Position.Z; - return new(minX, minY, minZ, maxX, maxY, maxZ, true); - } - catch - { - // 如果是一条参照线的组块,将导致获取包围盒时报错 - // 0x01 是否需要进入块内,然后拿到每个图元的BasePoint再计算中点?感觉过于复杂. - // 0x02 这个时候拿基点走就算了 - return new(brf.Position, brf.Position, true); - } - } - - /// - /// 处理多行文字 - /// - static BoundingInfo GetBoxInfoInMText(MText mtxt) - { - /* - * MText Aussehen - * ------------------------------------ - * | | | - * | | |ht - * | | | - * |-----wl-------插入点------wr-------| - * | | | - * | | |hb - * | | | - * ------------------------------------ - */ - - double width = mtxt.ActualWidth;// 实际宽度 - double height = mtxt.ActualHeight;// 实际高度 - double wl = 0.0; - double hb = 0.0; - - // 对齐方式 - switch (mtxt.Attachment) - { - case AttachmentPoint.TopCenter: - case AttachmentPoint.MiddleCenter: - case AttachmentPoint.BottomCenter: - wl = width * -0.5; - break; - case AttachmentPoint.TopRight: - case AttachmentPoint.MiddleRight: - case AttachmentPoint.BottomRight: - wl = -width; - break; - } - switch (mtxt.Attachment) - { - case AttachmentPoint.TopLeft: - case AttachmentPoint.TopCenter: - case AttachmentPoint.TopRight: - hb = -height;// 下边线到插入点的距离 - break; - case AttachmentPoint.MiddleLeft: - case AttachmentPoint.MiddleCenter: - case AttachmentPoint.MiddleRight: - hb = height * -0.5; - break; - } - - double wr = width + wl; - double ht = height + hb; - - Point3d center = mtxt.Location; - return new(center.X + wl, center.Y + hb, 0, - center.X + wr, center.Y + ht, 0, - true, - mtxt.Rotation); - } -} \ No newline at end of file diff --git a/src/IFoxCAD.Cad/ExtensionMethod/EntityEx.cs b/src/IFoxCAD.Cad/ExtensionMethod/EntityEx.cs deleted file mode 100644 index 42de3f4..0000000 --- a/src/IFoxCAD.Cad/ExtensionMethod/EntityEx.cs +++ /dev/null @@ -1,431 +0,0 @@ -namespace IFoxCAD.Cad; - - -/// -/// 实体图元类扩展函数 -/// -public static class EntityEx -{ - #region 实体刷新 - /// - /// 刷新实体显示 - /// - /// 实体对象 - public static void Flush(this Entity entity, DBTrans? trans = null) - { - // if (entity is null) - // { - // throw new ArgumentNullException(nameof(entity)); - // } - trans ??= DBTrans.Top; - entity.RecordGraphicsModified(true); - trans.Transaction.TransactionManager.QueueForGraphicsFlush(); - trans.Document?.TransactionManager.FlushGraphics(); - } - - /// - /// 刷新实体显示 - /// - /// 实体id - public static void Flush(this ObjectId id) => Flush(DBTrans.Top.GetObject(id)!); - #endregion - - #region 多段线端点坐标 - /// - /// 获取二维多段线的端点坐标 - /// - /// 二维多段线 - /// 事务 - /// 端点坐标集合 - public static IEnumerable GetPoints(this Polyline2d pl2d, DBTrans? tr = null) - { - tr ??= DBTrans.Top; - foreach (ObjectId id in pl2d) - { - yield return tr.GetObject(id)!.Position; - } - } - - /// - /// 获取三维多段线的端点坐标 - /// - /// 三维多段线 - /// 事务 - /// 端点坐标集合 - public static IEnumerable GetPoints(this Polyline3d pl3d, DBTrans? tr = null) - { - tr ??= DBTrans.Top; - foreach (ObjectId id in pl3d) - { - yield return tr.GetObject(id, OpenMode.ForRead)!.Position; - } - } - - /// - /// 获取多段线的端点坐标 - /// - /// 多段线 - /// 端点坐标集合 - public static IEnumerable GetPoints(this Polyline pl) - { - return - Enumerable - .Range(0, pl.NumberOfVertices) - .Select(i => pl.GetPoint3dAt(i)); - } - #endregion - - #region 实体线性变换 - - /// - /// 移动实体 - /// - /// 实体 - /// 基点 - /// 目标点 - public static void Move(this Entity ent, Point3d from, Point3d to) - { - ent.TransformBy(Matrix3d.Displacement(to - from)); - } - - /// - /// 缩放实体 - /// - /// 实体 - /// 缩放基点坐标 - /// 缩放比例 - public static void Scale(this Entity ent, Point3d center, double scaleValue) - { - ent.TransformBy(Matrix3d.Scaling(scaleValue, center)); - } - - /// - /// 旋转实体 - /// - /// 实体 - /// 旋转中心 - /// 转角,弧度制,正数为顺时针 - /// 旋转平面的法向矢量 - public static void Rotation(this Entity ent, Point3d center, double angle, Vector3d normal) - { - ent.TransformBy(Matrix3d.Rotation(angle, normal, center)); - } - - /// - /// 在XY平面内旋转实体 - /// - /// 实体 - /// 旋转中心 - /// 转角,弧度制,正数为顺时针 - public static void Rotation(this Entity ent, Point3d center, double angle) - { - ent.TransformBy(Matrix3d.Rotation(angle, Vector3d.ZAxis.TransformBy(ent.Ecs), center)); - } - - /// - /// 按对称轴镜像实体 - /// - /// 实体 - /// 对称轴起点 - /// 对称轴终点 - public static void Mirror(this Entity ent, Point3d startPoint, Point3d endPoint) - { - ent.TransformBy(Matrix3d.Mirroring(new Line3d(startPoint, endPoint))); - } - - /// - /// 按对称面镜像实体 - /// - /// 实体 - /// 对称平面 - public static void Mirror(this Entity ent, Plane plane) - { - ent.TransformBy(Matrix3d.Mirroring(plane)); - } - - /// - /// 按对称点镜像实体 - /// - /// 实体 - /// 对称点 - public static void Mirror(this Entity ent, Point3d basePoint) - { - ent.TransformBy(Matrix3d.Mirroring(basePoint)); - } - - #endregion - - #region 实体范围 - /// - /// 获取实体集合的范围 - /// - /// 实体迭代器 - /// 实体集合的范围 - public static Extents3d GetExtents(this IEnumerable ents) - { - var ext = new Extents3d(); - foreach (var item in ents) - { - ext.AddExtents(item.GeometricExtents); - } - return ext; - } - #endregion - - #region 单行文字 - - /// - /// 更正单行文字的镜像属性 - /// - /// 单行文字 - public static void ValidateMirror(this DBText txt) - { - if (!txt.Database.Mirrtext) - { - txt.IsMirroredInX = false; - txt.IsMirroredInY = false; - } - } - #endregion - - #region 多行文字 - - /// - /// 炸散多行文字 - /// - /// 存储多行文字炸散之后的对象的类型 - /// 多行文字 - /// 存储对象变量 - /// 回调函数,用于处理炸散之后的对象 - /// - /// 多行文字炸散后的对象
    - /// 回调函数处理的结果 - ///
    - /// - public static void ExplodeFragments(this MText mt, T obj, Func mTextFragmentCallback) - { - mt.ExplodeFragments((f, o) => mTextFragmentCallback(f, (T)o), obj); - } - - /// - /// 获取多行文字的无格式文本 - /// - /// 多行文字 - /// 文本 - public static string GetUnFormatString(this MText mt) - { - List strs = new(); - mt.ExplodeFragments( - strs, - (f, o) => { - o.Add(f.Text); - return MTextFragmentCallbackStatus.Continue; - }); - return string.Join("", strs.ToArray()); - } - #endregion - - #region 圆弧 - - /// - /// 根据圆心、起点、终点来创建圆弧(二维) - /// - /// 起点 - /// 圆心 - /// 终点 - /// 圆弧 - public static Arc CreateArcSCE(Point3d startPoint, Point3d centerPoint, Point3d endPoint) - { - Arc arc = new(); - arc.SetDatabaseDefaults(); - arc.Center = centerPoint; - arc.Radius = centerPoint.DistanceTo(startPoint); - Vector2d startVector = new(startPoint.X - centerPoint.X, startPoint.Y - centerPoint.Y); - Vector2d endVector = new(endPoint.X - centerPoint.X, endPoint.Y - centerPoint.Y); - // 计算起始和终止角度 - arc.StartAngle = startVector.Angle; - arc.EndAngle = endVector.Angle; - return arc; - } - /// - /// 三点法创建圆弧(二维) - /// - /// 圆弧对象 - /// 起点 - /// 圆弧上的点 - /// 终点 - /// 圆弧 - public static Arc CreateArc(Point3d startPoint, Point3d pointOnArc, Point3d endPoint) - { - // 创建一个几何类的圆弧对象 - CircularArc3d geArc = new(startPoint, pointOnArc, endPoint); - // 将几何类圆弧对象的圆心和半径赋值给圆弧 -#if NET35 - return (Arc)geArc.ToCurve(); -#else - return (Arc)Curve.CreateFromGeCurve(geArc); -#endif - } - - /// - /// 根据起点、圆心和圆弧角度创建圆弧(二维) - /// - /// 圆弧对象 - /// 起点 - /// 圆心 - /// 圆弧角度 - /// 圆弧 - public static Arc CreateArc(Point3d startPoint, Point3d centerPoint, double angle) - { - Arc arc = new(); - arc.SetDatabaseDefaults(); - arc.Center = centerPoint; - arc.Radius = centerPoint.DistanceTo(startPoint); - Vector2d startVector = new(startPoint.X - centerPoint.X, startPoint.Y - centerPoint.Y); - arc.StartAngle = startVector.Angle; - arc.EndAngle = startVector.Angle + angle; - return arc; - } - - #endregion - - #region 圆 - - /// - /// 两点创建圆(两点中点为圆心) - /// - /// 起点 - /// 终点 - /// - public static Circle CreateCircle(Point3d startPoint, Point3d endPoint) - { - Circle circle = new(); - circle.SetDatabaseDefaults(); - circle.Center = startPoint.GetMidPointTo(endPoint); - circle.Radius = startPoint.DistanceTo(endPoint) * 0.5; - return circle; - } - - /// - /// 三点法创建圆(失败则返回Null) - /// - /// 第一点 - /// 第二点 - /// 第三点 - /// - public static Circle? CreateCircle(Point3d pt1, Point3d pt2, Point3d pt3) - { - // 先判断三点是否共线,得到pt1点指向pt2、pt2点的矢量 - Vector3d va = pt1.GetVectorTo(pt2); - Vector3d vb = pt1.GetVectorTo(pt3); - // 如两矢量夹角为0或180度(π弧度),则三点共线. - if (va.GetAngleTo(vb) == 0 | va.GetAngleTo(vb) == Math.PI) - return null; - - // 创建一个几何类的圆弧对象 - CircularArc3d geArc = new(pt1, pt2, pt3); - geArc.ToCircle(); - return geArc.ToCircle(); - } - - /// - /// 通过圆心,半径绘制圆形 - /// - /// 圆心 - /// 半径 - /// 图形的ObjectId - public static Circle? CreateCircle(Point3d center, double radius, double vex = 0, double vey = 0, double vez = 1) - { - return new Circle(center, new Vector3d(vex, vey, vez), radius);// 平面法向量XY方向 - } - - #endregion - - #region 块参照 - - #region 裁剪块参照 - - private const string filterDictName = "ACAD_FILTER"; - private const string spatialName = "SPATIAL"; - - /// - /// 裁剪块参照 - /// - /// 块参照 - /// 裁剪多边形点表 - public static void ClipBlockRef(this BlockReference bref, IEnumerable pt3ds) - { - Matrix3d mat = bref.BlockTransform.Inverse(); - var pts = - pt3ds - .Select(p => p.TransformBy(mat)) - .Select(p => new Point2d(p.X, p.Y)) - .ToCollection(); - - SpatialFilterDefinition sfd = new(pts, Vector3d.ZAxis, 0.0, 0.0, 0.0, true); - using SpatialFilter sf = new() { Definition = sfd }; - var dict = bref.GetXDictionary()!.GetSubDictionary(true, new string[] { filterDictName })!; - dict.SetAt(spatialName, sf); - // SetToDictionary(dict, spatialName, sf); - } - - /// - /// 裁剪块参照 - /// - /// 块参照 - /// 第一角点 - /// 第二角点 - public static void ClipBlockRef(this BlockReference bref, Point3d pt1, Point3d pt2) - { - Matrix3d mat = bref.BlockTransform.Inverse(); - pt1 = pt1.TransformBy(mat); - pt2 = pt2.TransformBy(mat); - - Point2dCollection pts = new() - { - new Point2d(Math.Min(pt1.X, pt2.X), Math.Min(pt1.Y, pt2.Y)), - new Point2d(Math.Max(pt1.X, pt2.X), Math.Max(pt1.Y, pt2.Y)) - }; - - SpatialFilterDefinition sfd = new(pts, Vector3d.ZAxis, 0.0, 0.0, 0.0, true); - using SpatialFilter sf = new() { Definition = sfd }; - var dict = bref.GetXDictionary()!.GetSubDictionary(true, new string[] { filterDictName })!; - dict.SetAt(spatialName, sf); - // SetToDictionary(dict, spatialName, sf); - } - #endregion - - #region 属性 - /// - /// 更新动态块属性值 - /// - /// 动态块 - /// 属性值字典 - public static void ChangeBlockProperty(this BlockReference blockReference, - Dictionary propertyNameValues) - { - if (!blockReference.IsDynamicBlock) - return; - - using (blockReference.ForWrite()) - { - foreach (DynamicBlockReferenceProperty item in blockReference.DynamicBlockReferencePropertyCollection) - if (propertyNameValues.ContainsKey(item.PropertyName)) - item.Value = propertyNameValues[item.PropertyName]; - } - } - #endregion - - /// - /// 获取图元包围盒 - /// - /// - /// 包围盒信息 - /// 异常: - /// 会将包围盒类型记录到所属路径中,以此查询 - public static BoundingInfo GetBoundingBoxEx(this Entity ent) - { - return EntityBoundingInfo.GetBoundingInfo(ent); - } - #endregion -} diff --git a/src/IFoxCAD.Cad/ExtensionMethod/Enums.cs b/src/IFoxCAD.Cad/ExtensionMethod/Enums.cs deleted file mode 100644 index d100ded..0000000 --- a/src/IFoxCAD.Cad/ExtensionMethod/Enums.cs +++ /dev/null @@ -1,174 +0,0 @@ -namespace IFoxCAD.Cad; - -/// -/// 参照路径转换 -/// -public enum PathConverterModes : byte -{ - /// - /// 相对路径 - /// - Relative, - /// - /// 绝对路径 - /// - Complete -} - -/// -/// 参照绑定 -/// -public enum XrefModes : byte -{ - /// - /// 卸载 - /// - Unload, - /// - /// 重载 - /// - Reload, - /// - /// 拆离 - /// - Detach, - /// - /// 绑定 - /// - Bind, -} - -public enum SymModes : ushort -{ - /// - /// 块表 - /// - BlockTable = 1, - - /// - /// 图层表 - /// - LayerTable = 2, - /// - /// 文字样式表 - /// - TextStyleTable = 4, - /// - /// 注册应用程序表 - /// - RegAppTable = 8, - /// - /// 标注样式表 - /// - DimStyleTable = 16, - /// - /// 线型表 - /// - LinetypeTable = 32, - Option1 = LayerTable | TextStyleTable | DimStyleTable | LinetypeTable | RegAppTable, - - /// - /// 用户坐标系表 - /// - UcsTable = 64, - /// - /// 视图表 - /// - ViewTable = 128, - /// - /// 视口表 - /// - ViewportTable = 256, - Option2 = UcsTable | ViewTable | ViewportTable, - - // 全部 - All = BlockTable | Option1 | Option2 -} - - -/// -/// 坐标系类型枚举 -/// -public enum CoordinateSystemCode -{ - /// - /// 世界坐标系 - /// - Wcs = 0, - - /// - /// 用户坐标系 - /// - Ucs, - - /// - /// 模型空间坐标系 - /// - MDcs, - - /// - /// 图纸空间坐标系 - /// - PDcs -} - -/// -/// 方向的枚举 -/// -public enum OrientationType -{ - /// - /// 左转或逆时针 - /// - CounterClockWise, - /// - /// 右转或顺时针 - /// - ClockWise, - /// - /// 重合或平行 - /// - Parallel -} - -/// -/// 点与多边形的关系类型枚举 -/// -public enum PointOnRegionType -{ - /// - /// 多边形内部 - /// - Inside, - - /// - /// 多边形上 - /// - On, - - /// - /// 多边形外 - /// - Outside, - - /// - /// 错误 - /// - Error -} - - - -public enum FontTTF -{ - [Description("宋体.ttf")] - 宋体, - [Description("simfang.ttf")] - 仿宋, - [Description("FSGB2312.ttf")] - 仿宋GB2312, - [Description("Arial.ttf")] - Arial, - [Description("Romans")] - Romans -} \ No newline at end of file diff --git a/src/IFoxCAD.Cad/ExtensionMethod/Filer/DwgFiler.cs b/src/IFoxCAD.Cad/ExtensionMethod/Filer/DwgFiler.cs deleted file mode 100644 index a412edd..0000000 --- a/src/IFoxCAD.Cad/ExtensionMethod/Filer/DwgFiler.cs +++ /dev/null @@ -1,538 +0,0 @@ -namespace IFoxCAD.Cad; -/* - Arx自定义实体类,加 读函数(assertReadEnabled)和写函数(assertWriteEnabled) - - 所有属性位置都不要改动位置,因为涉及序列化 - [Serializable] 设置类 可以序列化 - [Newtonsoft.Json.JsonIgnore] 设置成员 不可序列化 -*/ - - -[Serializable] -public class DwgFiler : Acad_DwgFiler -{ -#if NET35 - public int m_Position; -#else - public long m_Position; -#endif - public FilerType m_FilerType; - public Acad_ErrorStatus m_FilerStatus; - public List AddressList; - public int AddressListPt = 0; - public List BinaryChunkList; - public int BinaryChunkListPt = 0; - public List BooleanList; - public int BooleanListPt = 0; - public List ByteList; - public int ByteListPt = 0; - public List BytesList; - public int BytesListPt = 0; - public List DoubleList; - public int DoubleListPt = 0; - public List HandleList; - public int HandleListPt = 0; - [NonSerialized] - public List HardOwnershipIdList; - public int HardOwnershipIdListPt = 0; - [NonSerialized] - public List HardPointerIdList; - public int HardPointerIdListPt = 0; - public List Int16List; - public int Int16ListPt = 0; - public List Int32List; - public int Int32ListPt = 0; -#if !NET35 - public List Int64List; - public int Int64ListPt = 0; -#endif - public List Point2dList; - public int Point2dListPt = 0; - public List Point3dList; - public int Point3dListPt = 0; - public List Scale3dList; - public int Scale3dListPt = 0; - [NonSerialized] - public List SoftOwnershipIdList; - public int SoftOwnershipIdListPt = 0; - [NonSerialized] - public List SoftPointerIdList; - public int SoftPointerIdListPt = 0; - public List StringList; - public int StringListPt = 0; - public List Uint16List; - public int uint16ListPt = 0; - public List Uint32List; - public int uint32ListPt = 0; -#if !NET35 - public List Uint64List; - public int uint64ListPt = 0; -#endif - public List Vector2dList; - public int Vector2dListPt = 0; - public List Vector3dList; - public int Vector3dListPt = 0; - - public DwgFiler() - { - m_Position = 0; - m_FilerType = FilerType.CopyFiler; - m_FilerStatus = Acad_ErrorStatus.OK; - AddressList = new List(); - BinaryChunkList = new List(); - BooleanList = new List(); - ByteList = new List(); - BytesList = new List(); - DoubleList = new List(); - HandleList = new List(); - HardOwnershipIdList = new List(); - HardPointerIdList = new List(); - Int16List = new List(); - Int32List = new List(); -#if !NET35 - Int64List = new List(); -#endif - Point2dList = new List(); - Point3dList = new List(); - Scale3dList = new List(); - SoftOwnershipIdList = new List(); - SoftPointerIdList = new List(); - StringList = new List(); - Uint16List = new List(); - Uint32List = new List(); -#if !NET35 - Uint64List = new List(); -#endif - Vector2dList = new List(); - Vector3dList = new List(); - } - -#if NET35 - public override int Position => m_Position; -#else - public override long Position => m_Position; -#endif - public override FilerType FilerType - { - get { return this.m_FilerType; } - } - - public override Acad_ErrorStatus FilerStatus - { - get { return m_FilerStatus; } - set { m_FilerStatus = value; } - } - - public override IntPtr ReadAddress() - { - if (AddressList.Count == 0) - return new IntPtr(); - return AddressList[AddressListPt++]; - } - - public override byte[]? ReadBinaryChunk() - { - if (BinaryChunkList.Count == 0) - return null; - return BinaryChunkList[BinaryChunkListPt++]; - } - - public override bool ReadBoolean() - { - if (BooleanList.Count == 0) - return false; - return BooleanList[BooleanListPt++]; - } - - public override byte ReadByte() - { - if (ByteList.Count == 0) - return 0; - return ByteList[ByteListPt++]; - } - - public override void ReadBytes(byte[] value) - { - if (ByteList.Count == 0) - return; - value = new byte[BytesList[BytesListPt].Length]; - BytesList[BytesListPt++].CopyTo(value, 0); - } - - public override double ReadDouble() - { - if (DoubleList.Count == 0) - return 0; - return DoubleList[DoubleListPt++]; - } - - public override Handle ReadHandle() - { - if (HandleList.Count == 0) - return new Handle(); - return HandleList[HandleListPt++]; - } - - public override ObjectId ReadHardOwnershipId() - { - if (HardOwnershipIdList.Count == 0) - return new ObjectId(); - return HardOwnershipIdList[HardOwnershipIdListPt++]; - } - - public override ObjectId ReadHardPointerId() - { - if (HardPointerIdList.Count == 0) - return new ObjectId(); - return HardPointerIdList[HardPointerIdListPt++]; - } - - public override short ReadInt16() - { - if (Int16List.Count == 0) - return 0; - return Int16List[Int16ListPt++]; - } - - public override int ReadInt32() - { - if (Int32List.Count == 0) - return 0; - return Int32List[Int32ListPt++]; - } - -#if !NET35 - public override long ReadInt64() - { - if (Int64List.Count == 0) - return 0; - return Int64List[Int64ListPt++]; - } -#endif - - public override Point2d ReadPoint2d() - { - if (Point2dList.Count == 0) - return new Point2d(); - return Point2dList[Point2dListPt++]; - } - - public override Point3d ReadPoint3d() - { - if (Point3dList.Count == 0) - return new Point3d(); - return Point3dList[Point3dListPt++]; - } - - public override Scale3d ReadScale3d() - { - if (Scale3dList.Count == 0) - return new Scale3d(); - return Scale3dList[Scale3dListPt++]; - } - - public override ObjectId ReadSoftOwnershipId() - { - if (SoftOwnershipIdList.Count == 0) - return new ObjectId(); - return SoftOwnershipIdList[SoftOwnershipIdListPt++]; - } - - public override ObjectId ReadSoftPointerId() - { - if (SoftPointerIdList.Count == 0) - return new ObjectId(); - return SoftPointerIdList[SoftPointerIdListPt++]; - } - - public override string? ReadString() - { - if (StringList.Count == 0) - return null; - return StringList[StringListPt++]; - } - - public override ushort ReadUInt16() - { - if (Uint16List.Count == 0) - return 0; - return Uint16List[uint16ListPt++]; - } - - public override uint ReadUInt32() - { - if (Uint32List.Count == 0) - return 0; - return Uint32List[uint32ListPt++]; - } - -#if !NET35 - public override ulong ReadUInt64() - { - if (Uint64List.Count == 0) - return 0; - return Uint64List[uint64ListPt++]; - } -#endif - - public override Vector2d ReadVector2d() - { - if (Vector2dList.Count == 0) - return new Vector2d(); - return Vector2dList[Vector2dListPt++]; - } - - public override Vector3d ReadVector3d() - { - if (Vector3dList.Count == 0) - return new Vector3d(); - return Vector3dList[Vector3dListPt++]; - } - - public override void ResetFilerStatus() - { - AddressList.Clear(); - AddressListPt = 0; - BinaryChunkList.Clear(); - BinaryChunkListPt = 0; - BooleanList.Clear(); - BooleanListPt = 0; - ByteList.Clear(); - ByteListPt = 0; - BytesList.Clear(); - BytesListPt = 0; - DoubleList.Clear(); - DoubleListPt = 0; - HandleList.Clear(); - HandleListPt = 0; - HardOwnershipIdList.Clear(); - HardOwnershipIdListPt = 0; - HardPointerIdList.Clear(); - HardPointerIdListPt = 0; - Int16List.Clear(); - Int16ListPt = 0; - Int32List.Clear(); - Int32ListPt = 0; -#if !NET35 - Int64List.Clear(); - Int64ListPt = 0; -#endif - Point2dList.Clear(); - Point2dListPt = 0; - Point3dList.Clear(); - Point3dListPt = 0; - Scale3dList.Clear(); - Scale3dListPt = 0; - SoftOwnershipIdList.Clear(); - SoftOwnershipIdListPt = 0; - SoftPointerIdList.Clear(); - SoftPointerIdListPt = 0; - StringList.Clear(); - StringListPt = 0; - Uint16List.Clear(); - uint16ListPt = 0; - Uint32List.Clear(); - uint32ListPt = 0; -#if !NET35 - Uint64List.Clear(); - uint64ListPt = 0; -#endif - Vector2dList.Clear(); - Vector2dListPt = 0; - Vector3dList.Clear(); - Vector3dListPt = 0; - - m_FilerType = FilerType.CopyFiler; - } - - public override void Seek( -#if ac2008 || ac2009 - int -#else - long -#endif - offset, int method) - { - Editor ed = Acap.DocumentManager.MdiActiveDocument.Editor; - ed.WriteMessage(MethodInfo.GetCurrentMethod().Name + " = " + " \n "); - } - - public override void WriteAddress(IntPtr value) - { - AddressList.Add(value); - } - - public override void WriteBinaryChunk(byte[] chunk) - { - BinaryChunkList.Add(chunk); - } - - public override void WriteBoolean(bool value) - { - BooleanList.Add(value); - } - - public override void WriteByte(byte value) - { - ByteList.Add(value); - } - - public override void WriteBytes(byte[] value) - { - BytesList.Add(value); - } - - public override void WriteDouble(double value) - { - DoubleList.Add(value); - } - - public override void WriteHandle(Handle handle) - { - HandleList.Add(handle); - } - - public override void WriteHardOwnershipId(ObjectId value) - { - HardOwnershipIdList.Add(value); - } - - public override void WriteHardPointerId(ObjectId value) - { - HardPointerIdList.Add(value); - } - - public override void WriteInt16(short value) - { - Int16List.Add(value); - } - - public override void WriteInt32(int value) - { - Int32List.Add(value); - } - -#if !NET35 - public override void WriteInt64(long value) - { - Int64List.Add(value); - } -#endif - public override void WritePoint2d(Point2d value) - { - Point2dList.Add(value); - } - - public override void WritePoint3d(Point3d value) - { - Point3dList.Add(value); - } - - public override void WriteScale3d(Scale3d value) - { - Scale3dList.Add(value); - } - - public override void WriteSoftOwnershipId(ObjectId value) - { - SoftOwnershipIdList.Add(value); - } - - public override void WriteSoftPointerId(ObjectId value) - { - SoftPointerIdList.Add(value); - } - - public override void WriteString(string value) - { - StringList.Add(value); - } - - public override void WriteUInt16(ushort value) - { - Uint16List.Add(value); - } - - public override void WriteUInt32(uint value) - { - Uint32List.Add(value); - } - -#if !NET35 - public override void WriteUInt64(ulong value) - { - Uint64List.Add(value); - } -#endif - - public override void WriteVector2d(Vector2d value) - { - Vector2dList.Add(value); - } - - public override void WriteVector3d(Vector3d value) - { - Vector3dList.Add(value); - } - - public override string ToString() - { - int ptCount = AddressListPt + - BinaryChunkListPt + - BooleanListPt + - ByteListPt + - BytesListPt + - DoubleListPt + - HandleListPt + - HardOwnershipIdListPt + - HardPointerIdListPt + - Int16ListPt + - Int32ListPt + -#if !NET35 - Int64ListPt + -#endif - Point2dListPt + - Point3dListPt + - Scale3dListPt + - SoftOwnershipIdListPt + - SoftPointerIdListPt + - StringListPt + - uint16ListPt + - uint32ListPt + -#if !NET35 - uint64ListPt + -#endif - Vector2dListPt + - Vector3dListPt; - - int ltCount = AddressList.Count + - BinaryChunkList.Count + - BooleanList.Count + - ByteList.Count + - BytesList.Count + - DoubleList.Count + - HandleList.Count + - HardOwnershipIdList.Count + - HardPointerIdList.Count + - Int16List.Count + - Int32List.Count + -#if !NET35 - Int64List.Count + -#endif - Point2dList.Count + - Point3dList.Count + - Scale3dList.Count + - SoftOwnershipIdList.Count + - SoftPointerIdList.Count + - StringList.Count + - Uint16List.Count + - Uint32List.Count + -#if !NET35 - Uint64List.Count + -#endif - Vector2dList.Count + - Vector3dList.Count; - - return "\nDataIn::" + ptCount + "\nDataOut::" + ltCount; - } -} \ No newline at end of file diff --git a/src/IFoxCAD.Cad/ExtensionMethod/Filer/DwgFilerEx.cs b/src/IFoxCAD.Cad/ExtensionMethod/Filer/DwgFilerEx.cs deleted file mode 100644 index b7a4d0e..0000000 --- a/src/IFoxCAD.Cad/ExtensionMethod/Filer/DwgFilerEx.cs +++ /dev/null @@ -1,83 +0,0 @@ -namespace IFoxCAD.Cad; - -/// -/// Dwg序列化 -/// -public class DwgFilerEx -{ - #region 成员 - DBObject? _entity; - public DwgFiler DwgFiler { get; private set; } - #endregion - - #region 构造 - /// - /// Dwg序列化 - /// - public DwgFilerEx(DwgFiler? acad_DwgFiler = null) - { - if (acad_DwgFiler == null) - acad_DwgFiler = new(); - DwgFiler = acad_DwgFiler; - } - - /// - /// Dwg序列化 - /// - public DwgFilerEx(DBObject entity) : this() - { - DwgOut(entity); - } - - #endregion - - #region 方法 - public void DwgOut(DBObject entity) - { - _entity = entity; - _entity.DwgOut(DwgFiler); - } - - public void DwgIn() - { - _entity?.DwgIn(DwgFiler); - } - - /// - /// 反序列化 - /// - /// - /// - public DwgFilerEx? DeserializeObject(string json) - { - throw new ArgumentException(); - //return JsonConvert.DeserializeObject(json);// 反序列化*字符串转类 - } - - /// - /// 序列化 - /// - /// - public string SerializeObject() - { - throw new ArgumentException(); - //return JsonConvert.SerializeObject(DwgFiler, Formatting.Indented); // 序列化*类转字符串 - } - - public override string ToString() - { - // 替换中括号以外的字符串,替换逗号为换行符 https://bbs.csdn.net/topics/370134253 - //var str = SerializeObject(); - //str = str.Substring(1, str.Length - 2); - //str = Regex.Replace(str, @"(?:,)(?![^\[]*?\])", "\r\n"); - //return str; - - return DwgFiler.ToString(); - } - - public static implicit operator Acad_DwgFiler(DwgFilerEx df) - { - return df.DwgFiler; - } - #endregion -} \ No newline at end of file diff --git a/src/IFoxCAD.Cad/ExtensionMethod/Filer/DxfFiler.cs b/src/IFoxCAD.Cad/ExtensionMethod/Filer/DxfFiler.cs deleted file mode 100644 index ef9c42d..0000000 --- a/src/IFoxCAD.Cad/ExtensionMethod/Filer/DxfFiler.cs +++ /dev/null @@ -1,219 +0,0 @@ -namespace IFoxCAD.Cad; - -/* 此处暂未完成,无任何测试,尚且不知道怎么用 */ -using System.Runtime.Remoting; - -public class DxfFiler : Acad_DxfFiler -{ - public DxfFiler(IntPtr unmanagedPointer, [MarshalAs(UnmanagedType.U1)] bool autoDelete) : base(unmanagedPointer, autoDelete) - { - } - - public override bool IsModifyingExistingObject => base.IsModifyingExistingObject; - - public override double Thickness => base.Thickness; - - public override double Elevation => base.Elevation; - - public override bool AtEmbeddedObjectStart => base.AtEmbeddedObjectStart; - - public override bool AtEndOfObject => base.AtEndOfObject; - - public override bool AtExtendedData => base.AtExtendedData; - - public override bool AtEndOfFile => base.AtEndOfFile; - - public override int Precision { get => base.Precision; set => base.Precision = value; } - - public override string ErrorMessage => base.ErrorMessage; - - public override bool AtSubclassData(string value) - { - return base.AtSubclassData(value); - } - - public override object Clone() - { - return base.Clone(); - } - - public override void CopyFrom(RXObject source) - { - base.CopyFrom(source); - } - - public override ObjRef CreateObjRef(Type requestedType) - { - return base.CreateObjRef(requestedType); - } - - public override bool Equals(object obj) - { - return base.Equals(obj); - } - - public override void FilerStatus() - { - base.FilerStatus(); - } - - public override int GetHashCode() - { - return base.GetHashCode(); - } - - public override void HaltAtClassBoundaries(bool value) - { - base.HaltAtClassBoundaries(value); - } - - public override object InitializeLifetimeService() - { - return base.InitializeLifetimeService(); - } - - public override void PushBackItem() - { - base.PushBackItem(); - } - - public override ResultBuffer ReadResultBuffer() - { - return base.ReadResultBuffer(); - } - - public override void ResetFilerStatus() - { - base.ResetFilerStatus(); - } - - public override int RewindFiler() - { - return base.RewindFiler(); - } - - public override void SetError(string format, params string[] values) - { - base.SetError(format, values); - } - - public override void SetError(Acad_ErrorStatus value, string format, params string[] values) - { - base.SetError(value, format, values); - } - - public override string ToString() - { - return base.ToString(); - } - - public override void WriteBool(DxfCode opCode, bool value) - { - base.WriteBool(opCode, value); - } - - public override void WriteBoolean(DxfCode opCode, bool value) - { - base.WriteBoolean(opCode, value); - } - - public override void WriteByte(DxfCode opCode, byte value) - { - base.WriteByte(opCode, value); - } - - public override void WriteBytes(DxfCode opCode, byte[] chunk) - { - base.WriteBytes(opCode, chunk); - } - - public override void WriteDouble(DxfCode opCode, double value, int precision) - { - base.WriteDouble(opCode, value, precision); - } - - public override void WriteEmbeddedObjectStart() - { - base.WriteEmbeddedObjectStart(); - } - - public override void WriteHandle(DxfCode opCode, Handle value) - { - base.WriteHandle(opCode, value); - } - - public override void WriteInt16(DxfCode opCode, short value) - { - base.WriteInt16(opCode, value); - } - - public override void WriteInt32(DxfCode opCode, int value) - { - base.WriteInt32(opCode, value); - } - - public override void WriteObjectId(DxfCode opCode, ObjectId value) - { - base.WriteObjectId(opCode, value); - } - - public override void WritePoint2d(DxfCode opCode, Point2d value, int precision) - { - base.WritePoint2d(opCode, value, precision); - } - - public override void WritePoint3d(DxfCode opCode, Point3d value, int precision) - { - base.WritePoint3d(opCode, value, precision); - } - - public override void WriteResultBuffer(ResultBuffer buffer) - { - base.WriteResultBuffer(buffer); - } - - public override void WriteScale3d(DxfCode opCode, Scale3d value, int precision) - { - base.WriteScale3d(opCode, value, precision); - } - - public override void WriteString(DxfCode opCode, string value) - { - base.WriteString(opCode, value); - } - - public override void WriteUInt16(DxfCode opCode, ushort value) - { - base.WriteUInt16(opCode, value); - } - - public override void WriteUInt32(DxfCode opCode, uint value) - { - base.WriteUInt32(opCode, value); - } - - public override void WriteVector2d(DxfCode opCode, Vector2d value, int precision) - { - base.WriteVector2d(opCode, value, precision); - } - - public override void WriteVector3d(DxfCode opCode, Vector3d value, int precision) - { - base.WriteVector3d(opCode, value, precision); - } - - public override void WriteXDataStart() - { - base.WriteXDataStart(); - } - - protected override void DeleteUnmanagedObject() - { - base.DeleteUnmanagedObject(); - } - - protected override void Dispose(bool disposing) - { - base.Dispose(disposing); - } -} \ No newline at end of file diff --git a/src/IFoxCAD.Cad/ExtensionMethod/GeometryEx.cs b/src/IFoxCAD.Cad/ExtensionMethod/GeometryEx.cs deleted file mode 100644 index 58749b2..0000000 --- a/src/IFoxCAD.Cad/ExtensionMethod/GeometryEx.cs +++ /dev/null @@ -1,677 +0,0 @@ -namespace IFoxCAD.Cad; - -using System.Drawing; - -/// -/// 图形扩展类 -/// -public static class GeometryEx -{ - - #region Point&Circle - - /// - /// 判断点与多边形的关系 - /// - /// 多边形顶点集合 - /// 点 - /// 点与多边形的关系 - public static PointOnRegionType PointOnRegion(this IEnumerable pts, Point2d pt) - { - // 遍历点集并生成首尾连接的多边形 - var ptlst = new LoopList(pts); - if (ptlst.Count < 3) - return PointOnRegionType.Error; - - var ls2ds = new List(); - foreach (var node in ptlst.GetNodes()) - { - ls2ds.Add(new LineSegment2d(node.Value, node.Next!.Value)); - } - var cc2d = new CompositeCurve2d(ls2ds.ToArray()); - - // 在多边形上? - if (cc2d.IsOn(pt)) - return PointOnRegionType.On; - - // 在最小包围矩形外? - var bb2d = cc2d.BoundBlock; - if (!bb2d.Contains(pt)) - return PointOnRegionType.Outside; - - // - bool flag = false; - foreach (var node in ptlst.GetNodes()) - { - var pt1 = node.Value; - var pt2 = node.Next!.Value; - if (pt.Y < pt1.Y && pt.Y < pt2.Y) - continue; - if (pt1.X < pt.X && pt2.X < pt.X) - continue; - Vector2d vec = pt2 - pt1; - double t = (pt.X - pt1.X) / vec.X; - double y = t * vec.Y + pt1.Y; - if (y < pt.Y && t >= 0 && t <= 1) - flag = !flag; - } - return - flag ? - PointOnRegionType.Inside : PointOnRegionType.Outside; - } - - /// - /// 判断点与多边形的关系 - /// - /// 多边形顶点集合 - /// 点 - /// 点与多边形的关系 - public static PointOnRegionType PointOnRegion(this IEnumerable pts, Point3d pt) - { - // 遍历点集并生成首尾连接的多边形 - var ptlst = new LoopList(pts); - if (ptlst.First!.Value == ptlst.Last!.Value) - ptlst.RemoveLast(); - if (ptlst.Count < 3) - return PointOnRegionType.Error; - - var ls3ds = new List(); - foreach (var node in ptlst.GetNodes()) - ls3ds.Add(new LineSegment3d(node.Value, node.Next!.Value)); - var cc3d = new CompositeCurve3d(ls3ds.ToArray()); - - // 在多边形上? - if (cc3d.IsOn(pt)) - return PointOnRegionType.On; - - // 在最小包围矩形外? - var bb2d = cc3d.BoundBlock; - if (!bb2d.Contains(pt)) - return PointOnRegionType.Outside; - - // - bool flag = false; - foreach (var node in ptlst.GetNodes()) - { - var pt1 = node.Value; - var pt2 = node.Next!.Value; - if (pt.Y < pt1.Y && pt.Y < pt2.Y) - continue; - if (pt1.X < pt.X && pt2.X < pt.X) - continue; - Vector3d vec = pt2 - pt1; - double t = (pt.X - pt1.X) / vec.X; - double y = t * vec.Y + pt1.Y; - if (y < pt.Y && t >= 0 && t <= 1) - flag = !flag; - } - return - flag ? - PointOnRegionType.Inside : PointOnRegionType.Outside; - } - - /// - /// 按两点返回最小包围圆 - /// - /// 基准点 - /// 基准点 - /// 输出圆上的点 - /// 解析类圆对象 - public static CircularArc2d GetMinCircle(Point2d pt1, Point2d pt2, out LoopList ptlst) - { - ptlst = new LoopList { pt1, pt2 }; - return - new CircularArc2d - ( - (pt1 + pt2.GetAsVector()) / 2, - pt1.GetDistanceTo(pt2) / 2 - ); - } - - /// - /// 按三点返回最小包围圆 - /// - /// 基准点 - /// 基准点 - /// 基准点 - /// 输出圆上的点 - /// 解析类圆对象 - public static CircularArc2d GetMinCircle(Point2d pt1, Point2d pt2, Point2d pt3, out LoopList ptlst) - { - ptlst = new LoopList { pt1, pt2, pt3 }; - - // 遍历各点与下一点的向量长度,找到距离最大的两个点 - LoopListNode maxNode = - ptlst.GetNodes().FindByMax - ( - out double maxLength, - node => node.Value.GetDistanceTo(node.Next!.Value) - ); - - // 以两点做最小包围圆 - CircularArc2d ca2d = - GetMinCircle(maxNode.Value, maxNode.Next!.Value, out LoopList tptlst); - - // 如果另一点属于该圆 - if (ca2d.IsIn(maxNode.Previous!.Value)) - { - // 返回 - ptlst = tptlst; - return ca2d; - } - // 否则按三点做圆 - // ptlst.SetFirst(maxNode); - ptlst = new LoopList { maxNode.Value, maxNode.Next.Value, maxNode.Previous.Value }; - ca2d = new CircularArc2d(pt1, pt2, pt3); - ca2d.SetAngles(0, Math.PI * 2); - return ca2d; - } - - /// - /// 按四点返回最小包围圆 - /// - /// 基准点 - /// 基准点 - /// 基准点 - /// 基准点 - /// 输出圆上的点 - /// 解析类圆对象 - public static CircularArc2d? GetMinCircle(Point2d pt1, Point2d pt2, Point2d pt3, Point2d pt4, out LoopList? ptlst) - { - var iniptlst = new LoopList() { pt1, pt2, pt3, pt4 }; - ptlst = null; - CircularArc2d? ca2d = null; - - // 遍历C43的组合,环链表的优势在这里 - foreach (LoopListNode firstNode in iniptlst.GetNodes()) - { - // 获取各组合下三点的最小包围圆 - var secondNode = firstNode.Next; - var thirdNode = secondNode!.Next; - var tca2d = GetMinCircle(firstNode.Value, secondNode.Value, thirdNode!.Value, out LoopList tptlst); - - // 如果另一点属于该圆,并且半径小于当前值就把它做为候选解 - if (!tca2d.IsIn(firstNode.Previous!.Value)) - continue; - if (ca2d is null || tca2d.Radius < ca2d.Radius) - { - ca2d = tca2d; - ptlst = tptlst; - } - } - - // 返回直径最小的圆 - return ca2d; - } - - /// - /// 计算三点围成的有向面积 - /// - /// 基准点 - /// 第一点 - /// 第二点 - /// 三点围成的三角形的有向面积 - private static double CalArea(Point2d ptBase, Point2d pt1, Point2d pt2) - { - return (pt2 - ptBase).DotProduct((pt1 - ptBase).GetPerpendicularVector()) * 0.5; - } - /// - /// 计算三点围成的三角形的真实面积 - /// - /// 基准点 - /// 第一点 - /// 第二点 - /// 三点围成的三角形的真实面积 - public static double GetArea(this Point2d ptBase, Point2d pt1, Point2d pt2) - { - return Math.Abs(CalArea(ptBase, pt1, pt2)); - } - - /// - /// 判断三点是否为逆时针,也就是说判断三点是否为左转 - /// - /// 基点 - /// 第一点 - /// 第二点 - /// OrientationType 类型值 - public static OrientationType IsClockWise(this Point2d ptBase, Point2d pt1, Point2d pt2) - { - return CalArea(ptBase, pt1, pt2) switch - { - > 0 => OrientationType.CounterClockWise, - < 0 => OrientationType.ClockWise, - _ => OrientationType.Parallel - }; - } - - /// - /// 计算两个二维向量围成的平行四边形的有向面积 - /// - /// 基向量 - /// 向量 - /// 有向面积 - private static double CalArea(Vector2d vecBase, Vector2d vec) - { - return vec.DotProduct(vecBase.GetPerpendicularVector()) / 2; - } - - /// - /// 计算两个二维向量围成的平行四边形的真实面积 - /// - /// 基向量 - /// 向量 - /// 真实面积 - public static double GetArea(Vector2d vecBase, Vector2d vec) - { - return Math.Abs(CalArea(vecBase, vec)); - } - - /// - /// 判断两个二维向量是否左转 - /// - /// 基向量 - /// 向量 - /// OrientationType 类型值 - public static OrientationType IsClockWise(Vector2d vecBase, Vector2d vec) - { - return CalArea(vecBase, vec) switch - { - > 0 => OrientationType.CounterClockWise, - < 0 => OrientationType.ClockWise, - _ => OrientationType.Parallel - }; - } - - #region PointList - - /// - /// 计算点集的有向面积 - /// - /// 点集 - /// 有向面积 - private static double CalArea(IEnumerable pnts) - { - var itor = pnts.GetEnumerator(); - if (!itor.MoveNext()) - throw new ArgumentNullException(nameof(pnts)); - var start = itor.Current; - Point2d p1, p2 = start; - double area = 0; - - while (itor.MoveNext()) - { - p1 = p2; - p2 = itor.Current; - area += (p1.X * p2.Y - p2.X * p1.Y); - } - - area = (area + (p2.X * start.Y - start.X * p2.Y)) / 2.0; - return area; - } - /// - /// 计算点集的真实面积 - /// - /// 点集 - /// 面积 - public static double GetArea(this IEnumerable pnts) - { - return Math.Abs(CalArea(pnts)); - } - - /// - /// 判断点集的点序 - /// - /// 点集 - /// OrientationType 类型值 - public static OrientationType IsClockWise(this IEnumerable pnts) - { - return CalArea(pnts) switch - { - < 0 => OrientationType.ClockWise, - > 0 => OrientationType.CounterClockWise, - _ => OrientationType.Parallel - }; - } - - /// - /// 按点集返回最小包围圆 - /// - /// 点集 - /// 输出圆上的点 - /// 解析类圆对象 - public static CircularArc2d? GetMinCircle(this List pnts, out LoopList? ptlst) - { - // 点数较小时直接返回 - switch (pnts.Count) - { - case 0: - ptlst = null; - return null; - - case 1: - ptlst = new LoopList { pnts[0] }; - return new CircularArc2d(pnts[0], 0); - - case 2: - return GetMinCircle(pnts[0], pnts[1], out ptlst); - - case 3: - return GetMinCircle(pnts[0], pnts[1], pnts[2], out ptlst); - - case 4: - return GetMinCircle(pnts[0], pnts[1], pnts[2], pnts[3], out ptlst); - } - - // 按前三点计算最小包围圆 - var tpnts = new Point2d[4]; - pnts.CopyTo(0, tpnts, 0, 3); - var ca2d = GetMinCircle(tpnts[0], tpnts[1], tpnts[2], out ptlst); - - // 找到点集中距离圆心的最远点为第四点 - tpnts[3] = pnts.FindByMax(pnt => ca2d.Center.GetDistanceTo(pnt)); - - // 如果最远点属于圆结束 - while (!ca2d.IsIn(tpnts[3])) - { - // 如果最远点不属于圆,按此四点计算最小包围圆 - ca2d = GetMinCircle(tpnts[0], tpnts[1], tpnts[2], tpnts[3], out ptlst); - - // 将结果作为新的前三点 - if (ptlst!.Count == 3) - { - tpnts[2] = ptlst.Last!.Value; - } - else - { - // 第三点取另两点中距离圆心较远的点 - // 按算法中描述的任选其中一点的话,还是无法收敛...... - tpnts[2] = - tpnts.Except(ptlst) - .FindByMax(pnt => ca2d!.Center.GetDistanceTo(pnt)); - } - tpnts[0] = ptlst.First!.Value; - tpnts[1] = ptlst.First.Next!.Value; - - // 按此三点计算最小包围圆 - ca2d = GetMinCircle(tpnts[0], tpnts[1], tpnts[2], out ptlst); - - // 找到点集中圆心的最远点为第四点 - tpnts[3] = pnts.FindByMax(pnt => ca2d.Center.GetDistanceTo(pnt)); - } - - return ca2d; - } - - /// - /// 获取点集的凸包 - /// - /// 点集 - /// 凸包 - public static List? ConvexHull(this List points) - { - if (points is null) - return null; - - if (points.Count <= 1) - return points; - - int n = points.Count, k = 0; - List H = new(new Point2d[2 * n]); - - points.Sort((a, b) => - a.X == b.X ? a.Y.CompareTo(b.Y) : a.X.CompareTo(b.X)); - - // Build lower hull - for (int i = 0; i < n; ++i) - { - while (k >= 2 && IsClockWise(H[k - 2], H[k - 1], points[i]) == OrientationType.CounterClockWise) - k--; - H[k++] = points[i]; - } - - // Build upper hull - for (int i = n - 2, t = k + 1; i >= 0; i--) - { - while (k >= t && IsClockWise(H[k - 2], H[k - 1], points[i]) == OrientationType.CounterClockWise) - k--; - H[k++] = points[i]; - } - return H.Take(k - 1).ToList(); - } - - - #endregion PointList - - #endregion Point&Circle - - #region Ucs - - /// - /// ucs到wcs的点变换 - /// - /// 点 - /// 变换后的点 - public static Point3d Ucs2Wcs(this Point3d point) - { - return point.TransformBy(Env.Editor.CurrentUserCoordinateSystem); - } - - /// - /// wcs到ucs的点变换 - /// - /// 点 - /// 变换后的点 - public static Point3d Wcs2Ucs(this Point3d point) - { - return point.TransformBy(Env.Editor.CurrentUserCoordinateSystem.Inverse()); - } - - /// - /// ucs到wcs的向量变换 - /// - /// 向量 - /// 变换后的向量 - public static Vector3d Ucs2Wcs(this Vector3d vec) - { - return vec.TransformBy(Env.Editor.CurrentUserCoordinateSystem); - } - - /// - /// wcs到ucs的向量变换 - /// - /// 向量 - /// 变换后的向量 - public static Vector3d Wcs2Ucs(this Vector3d vec) - { - return vec.TransformBy(Env.Editor.CurrentUserCoordinateSystem.Inverse()); - } - - /// - /// 模拟 trans 函数 - /// - /// 点 - /// 源坐标系 - /// 目标坐标系 - /// 变换后的点 - public static Point3d Trans(this Point3d point, CoordinateSystemCode from, CoordinateSystemCode to) - { - return Env.Editor.GetMatrix(from, to) * point; - } - - /// - /// 模拟 trans 函数 - /// - /// 向量 - /// 源坐标系 - /// 目标坐标系 - /// 变换后的向量 - public static Vector3d Trans(this Vector3d vec, CoordinateSystemCode from, CoordinateSystemCode to) - { - return vec.TransformBy(Env.Editor.GetMatrix(from, to)); - } - - /// - /// wcs到dcs的点变换 - /// - /// 点 - /// 是否为图纸空间 - /// 变换后的点 - public static Point3d Wcs2Dcs(this Point3d point, bool atPaperSpace) - { - return - Trans( - point, - CoordinateSystemCode.Wcs, atPaperSpace ? CoordinateSystemCode.PDcs : CoordinateSystemCode.MDcs - ); - } - - /// - /// wcs到dcs的向量变换 - /// - /// 向量 - /// 是否为图纸空间 - /// 变换后的向量 - public static Vector3d Wcs2Dcs(this Vector3d vec, bool atPaperSpace) - { - return - Trans( - vec, - CoordinateSystemCode.Wcs, atPaperSpace ? CoordinateSystemCode.PDcs : CoordinateSystemCode.MDcs - ); - } - - #endregion Ucs - - - /// - /// 返回不等比例变换矩阵 - /// - /// 基点 - /// x方向比例 - /// y方向比例 - /// z方向比例 - /// 三维矩阵 - public static Matrix3d GetScaleMatrix(this Point3d point, double x, double y, double z) - { - double[] matdata = new double[16]; - matdata[0] = x; - matdata[3] = point.X * (1 - x); - matdata[5] = y; - matdata[7] = point.Y * (1 - y); - matdata[10] = z; - matdata[11] = point.Z * (1 - z); - matdata[15] = 1; - return new Matrix3d(matdata); - } - - /// - /// 获取坐标范围的大小 - /// - /// 坐标范围 - /// 尺寸对象 - public static Size GetSize(this Extents3d ext) - { - int width = (int)Math.Floor(ext.MaxPoint.X - ext.MinPoint.X); - int height = (int)Math.Ceiling(ext.MaxPoint.Y - ext.MinPoint.Y); - return new Size(width, height); - } - - /// - /// 将三维点转换为二维点 - /// - /// 三维点 - /// 二维点 - public static Point2d Point2d(this Point3d pt) - { - return new Point2d(pt.X, pt.Y); - } - /// - /// 将三维点集转换为二维点集 - /// - /// 三维点集 - /// 二维点集 - public static IEnumerable Point2d(this IEnumerable pts) - { - return pts.Select(pt => pt.Point2d()); - } - /// - /// 将二维点转换为三维点 - /// - /// 二维点 - /// 三维点 - public static Point3d Point3d(this Point2d pt) - { - return new Point3d(pt.X, pt.Y, 0); - } - /// - /// 将二维点转换为三维点 - /// - /// 二维点 - /// Z值 - /// 三维点 - public static Point3d Point3d(this Point2d pt, double z) - { - return new Point3d(pt.X, pt.Y, z); - } - - /// - /// 获取两个点之间的中点 - /// - /// 第一点 - /// 第二点 - /// 返回两个点之间的中点 - public static Point3d GetMidPointTo(this Point3d pt1, Point3d pt2) - { - return new Point3d((pt1.X + pt2.X) * 0.5, (pt1.Y + pt2.Y) * 0.5, (pt1.Z + pt2.Z) * 0.5); - } - - /// - /// 获取两个点之间的中点 - /// - /// 第一点 - /// 第二点 - /// 返回两个点之间的中点 - public static Point2d GetMidPointTo(this Point2d pt1, Point2d pt2) - { - return new Point2d((pt1.X + pt2.X) * 0.5, (pt1.Y + pt2.Y) * 0.5); - } - - /// - /// 根据世界坐标计算用户坐标 - /// - /// 基点世界坐标 - /// 基点用户坐标 - /// 目标世界坐标 - /// 坐标网旋转角,按x轴正向逆时针弧度 - /// 目标用户坐标 - public static Point3d TransPoint(this Point3d basePt, Point3d userPt, Point3d transPt, double ang) - { - Matrix3d transMat = Matrix3d.Displacement(userPt - basePt); - Matrix3d roMat = Matrix3d.Rotation(-ang, Vector3d.ZAxis, userPt); - return transPt.TransformBy(roMat * transMat); - } - /// - /// 计算指定距离和角度的点 - /// - /// 本函数仅适用于x-y平面 - /// 基点 - /// 角度,x轴正向逆时针弧度 - /// 距离 - /// 目标点 - public static Point3d Polar(this Point3d pt, double ang, double len) - { - return pt + Vector3d.XAxis.RotateBy(ang, Vector3d.ZAxis) * len; - } - /// - /// 计算指定距离和角度的点 - /// - /// 本函数仅适用于x-y平面 - /// 基点 - /// 角度,x轴正向逆时针弧度 - /// 距离 - /// 目标点 - public static Point2d Polar(this Point2d pt, double ang, double len) - { - return pt + Vector2d.XAxis.RotateBy(ang) * len; - } -} diff --git a/src/IFoxCAD.Cad/ExtensionMethod/JigEx.cs b/src/IFoxCAD.Cad/ExtensionMethod/JigEx.cs deleted file mode 100644 index a7e6dc1..0000000 --- a/src/IFoxCAD.Cad/ExtensionMethod/JigEx.cs +++ /dev/null @@ -1,370 +0,0 @@ -namespace IFoxCAD.Cad; - -/* 封装jig - * 20220726 隐藏事件,利用函数进行数据库图元重绘 - * 20220710 修改SetOption()的空格结束,并添加例子到IFox - * 20220503 cad22需要防止刷新过程中更改队列,是因为允许函数重入导致,08不会有. - * 20220326 重绘图元的函数用错了,现在修正过来 - * 20211216 加入块表时候做一个差集,剔除临时图元 - * 20211209 补充正交变量设置和回收设置 - * 作者: 惊惊⎛⎝◕⏝⏝◕。⎠⎞ ⎛⎝≥⏝⏝0⎠⎞ ⎛⎝⓿⏝⏝⓿。⎠⎞ ⎛⎝≥⏝⏝≤⎠⎞ - * 博客: https://www.cnblogs.com/JJBox/p/15650770.html - */ - -public delegate void WorldDrawEvent(WorldDraw draw); -public class JigEx : DrawJig -{ - #region 成员 - /// - /// 事件:亮显/暗显会被刷新冲刷掉,所以这个事件用于补充非刷新的工作 - /// - event WorldDrawEvent? WorldDrawEvent; - /// - /// 最后的鼠标点,用来确认长度 - /// - public Point3d MousePointWcsLast; - /// - /// 最后的图元,用来生成 - /// - public Entity[] Entitys => _drawEntitys.ToArray(); - - - readonly Action>? _mouseAction; - readonly Tolerance _tolerance;// 容差 - - readonly Queue _drawEntitys;// 重复生成的图元,放在这里刷新 - JigPromptPointOptions? _options;// jig鼠标配置 - bool _worldDrawFlag = false; // 20220503 - - bool _systemVariables_Orthomode = false; - bool SystemVariables_Orthomode // 正交修改还原 - { - get => _systemVariables_Orthomode; - set - { - // 1正交,0非正交 // setvar: https://www.cnblogs.com/JJBox/p/10209541.html - if (Env.OrthoMode != value) - Env.OrthoMode = _systemVariables_Orthomode = value; - } - } - #endregion - - #region 构造 - /// - /// 在界面绘制图元 - /// - JigEx() - { - _drawEntitys = new(); - DimensionEntitys = new(); - } - - /// - /// 在界面绘制图元 - /// - /// - /// 用来频繁执行的回调:
    - /// 鼠标点;
    - /// 加入新建的图元,鼠标采样期间会Dispose图元的;
    - /// 所以已经在数据库图元利用事件加入,不要在此加入;
    - /// - /// 鼠标移动的容差 - public JigEx(Action>? action = null, double tolerance = 1e-6) : this() - { - _mouseAction = action; - _tolerance = new(tolerance, tolerance); - } - #endregion - - #region 方法 - /// - /// 鼠标配置:基点 - /// - /// 基点 - /// 光标绑定 - /// 提示信息 - /// 正交开关 - public JigPromptPointOptions SetOptions(Point3d basePoint, - CursorType cursorType = CursorType.RubberBand, - string msg = "点选第二点", - bool orthomode = false) - { - if (orthomode) - SystemVariables_Orthomode = true; - - _options = JigPointOptions(); - _options.Message = Environment.NewLine + msg; - _options.Cursor = cursorType; // 光标绑定 - _options.UseBasePoint = true; // 基点打开 - _options.BasePoint = basePoint; // 基点设定 - return _options; - } - - /// - /// 鼠标配置:提示信息,关键字 - /// - /// 信息 - /// 关键字 - /// 正交开关 - /// - public JigPromptPointOptions SetOptions(string msg, - Dictionary? keywords = null, - bool orthomode = false) - { - if (orthomode) - SystemVariables_Orthomode = true; - - _options = JigPointOptions(); - _options.Message = Environment.NewLine + msg; - - // 加入关键字,加入时候将空格内容放到最后 - string spaceValue = string.Empty; - const string spaceKey = " "; - - if (keywords != null) - foreach (var item in keywords) - if (item.Key == spaceKey) - spaceValue = item.Value; - else - _options.Keywords.Add(item.Key, item.Key, item.Value); - - /// 因为默认配置函数导致此处空格触发是无效的, - /// 但是用户如果想触发,就需要在外部减去默认UserInputControls配置 - /// 要放最后,才能优先触发其他关键字 - if (spaceValue != string.Empty) - _options.Keywords.Add(spaceKey, spaceKey, spaceValue); - else - _options.Keywords.Add(spaceKey, spaceKey, "<空格退出>"); - - // 外部设置减去配置 - // _options.UserInputControls = - // _options.UserInputControls - // ^ UserInputControls.NullResponseAccepted // 输入了鼠标右键,结束jig - // ^ UserInputControls.AnyBlankTerminatesInput; // 空格或回车,结束jig; - return _options; - } - - /// - /// 鼠标配置:自定义 - /// - /// - /// 正交开关 - public void SetOptions(Action action, bool orthomode = false) - { - if (orthomode) - SystemVariables_Orthomode = true; - - _options = new JigPromptPointOptions(); - action.Invoke(_options); - } - - /// - /// 执行 - /// - /// - public PromptResult Drag() - { - // jig功能必然是当前前台文档,所以封装内部更好调用 - var dm = Acap.DocumentManager; - var doc = dm.MdiActiveDocument; - var ed = doc.Editor; - var dr = ed.Drag(this); - - if (SystemVariables_Orthomode) - SystemVariables_Orthomode = !SystemVariables_Orthomode; - return dr; - } - - /// - /// 最后一次的图元加入数据库 - /// - /// 加入此空间 - /// 不生成的图元用于排除,例如刷新时候的提示文字 - /// 加入数据库的id集合 - public IEnumerable? AddEntityToMsPs(BlockTableRecord btrOfAddEntitySpace, - IEnumerable? removeEntity = null) - { - // 内部用 _drawEntitys 外部用 Entitys,减少一层转换 - if (_drawEntitys.Count == 0) - return null; - - IEnumerable es = _drawEntitys; - if (removeEntity != null) - es = es.Except(removeEntity);// 差集 - - return btrOfAddEntitySpace.AddEntity(es); - } - #endregion - - #region 重写 - /// - /// 鼠标采样器 - /// - /// - /// 返回状态:令频繁刷新结束 - protected override SamplerStatus Sampler(JigPrompts prompts) - { - if (_worldDrawFlag) - return SamplerStatus.NoChange;// OK的时候拖动鼠标与否都不出现图元 - - if (_options is null) - throw new NullReferenceException(nameof(_options)); - - var pro = prompts.AcquirePoint(_options); - if (pro.Status == PromptStatus.Keyword) - return SamplerStatus.OK; - else if (pro.Status != PromptStatus.OK) - return SamplerStatus.Cancel; - - // 上次鼠标点不同(一定要这句,不然图元刷新太快会看到奇怪的边线) - var mousePointWcs = pro.Value; - - // == 是比较类字段,但是最好转为哈希比较. - // IsEqualTo 是方形判断(仅加法),但是cad是距离. - // Distance 是圆形判断(会求平方根,使用了牛顿迭代), - // 大量数据(十万以上/频繁刷新)面前会显得非常慢. - if (mousePointWcs.IsEqualTo(MousePointWcsLast, _tolerance)) - return SamplerStatus.NoChange; - - // 上次循环的缓冲区图元清理,否则将会在vs输出遗忘 Dispose - while (_drawEntitys.Count > 0) - _drawEntitys.Dequeue().Dispose(); - - // 委托把容器扔出去接收新创建的图元,然后给重绘更新 - _mouseAction?.Invoke(mousePointWcs, _drawEntitys); - MousePointWcsLast = mousePointWcs; - - return SamplerStatus.OK; - } - - /// - /// 重绘已在数据库的图元 - /// - /// 0x01 此处不加入newEntity的,它们在构造函数的参数回调处加入,它们会进行频繁new和Dispose从而避免遗忘释放
    - /// 0x02 此处用于重绘已经在数据的图元
    - /// 0x03 此处用于图元亮显暗显,因为会被重绘冲刷掉所以独立出来不重绘,它们也往往已经存在数据库的 - ///
    - ///
    - /// - /// newEntity只会存在一个图元队列中,而数据库图元可以分多个集合 - /// 例如: 集合A亮显时 集合B暗显/集合B亮显时 集合A暗显,所以我没有设计多个"数据库图元集合"存放,而是由用户在构造函数外自行创建 - /// - /// - public void DatabaseEntityDraw(WorldDrawEvent action) - { - WorldDrawEvent = action; - } - - /* WorldDraw 封装外的操作说明: - * 0x01 - * 我有一个业务是一次性生成四个方向的箭头,因为cad08缺少瞬时图元, - * 那么可以先提交一次事务,再开一个事务,把Entity传给jig,最后选择删除部分. - * 虽然这个是可行的方案,但是Entity穿越事务本身来说是非必要不使用的. - * 0x02 - * 四个箭头最近鼠标的亮显,其余淡显, - * 在jig使用淡显ent.Unhighlight和亮显ent.Highlight() - * 需要绕过重绘,否则重绘将导致图元频闪,令这两个操作失效, - * 此时需要自定义一个集合 EntityList (不使用本函数的_drawEntitys) - * 再将 EntityList 传给 WorldDrawEvent 事件,事件内实现亮显和淡显(事件已经利用 DatabaseEntityDraw函数进行提供). - * 0x03 - * draw.Geometry.Draw(_drawEntitys[i]); - * 此函数有问题,acad08克隆一份数组也可以用来刷新, - * 而arx上面的jig只能一次改一个,所以可以用此函数. - * 起因是此函数属于异步刷新, - * 同步上下文的刷新是 RawGeometry - * 0x04 - * cad22测试出现,08不会, - * draw.RawGeometry.Draw(ent);会跳到 Sampler(),所以设置 _worldDrawFlag - * 但是禁止重绘重入的话(令图元不频繁重绘),那么鼠标停着的时候就看不见图元, - * 所以只能重绘结束的时候才允许鼠标采集,采集过程的时候不会触发重绘, - * 这样才可以保证容器在重绘中不被更改. - */ - /// - /// 重绘图形 - /// - protected override bool WorldDraw(WorldDraw draw) - { - _worldDrawFlag = true; - WorldDrawEvent?.Invoke(draw); - _drawEntitys.ForEach(ent => { - draw.RawGeometry.Draw(ent); - }); - _worldDrawFlag = false; - return true; - } - #endregion - - - /// - /// 用户输入控制默认配置 - /// 令jig.Drag().Status == - /// - /// - static JigPromptPointOptions JigPointOptions() - { - return new JigPromptPointOptions() - { - UserInputControls = - UserInputControls.GovernedByUCSDetect // 由UCS探测用 - | UserInputControls.Accept3dCoordinates // 接受三维坐标 - | UserInputControls.NullResponseAccepted // 输入了鼠标右键,结束jig - | UserInputControls.AnyBlankTerminatesInput // 空格或回车,结束jig; - }; - } - - /// - /// 空格默认是, - /// 将它设置为 - /// - public void SetSpaceIsKeyword() - { - var opt = _options; - if (opt == null) - throw new ArgumentNullException(nameof(_options)); - - if ((opt.UserInputControls & UserInputControls.NullResponseAccepted) == UserInputControls.NullResponseAccepted) - opt.UserInputControls ^= UserInputControls.NullResponseAccepted; // 输入了鼠标右键,结束jig - if ((opt.UserInputControls & UserInputControls.AnyBlankTerminatesInput) == UserInputControls.AnyBlankTerminatesInput) - opt.UserInputControls ^= UserInputControls.AnyBlankTerminatesInput; // 空格或回车,结束jig - } - - - #region 注释数据 - /// - /// 注释数据,可以在缩放的时候不受影响 - /// - public DynamicDimensionDataCollection DimensionEntitys; - - /// - /// 重写注释数据 - /// - /// - /// - protected override DynamicDimensionDataCollection GetDynamicDimensionData(double dimScale) - { - base.GetDynamicDimensionData(dimScale); - return DimensionEntitys; - } - #endregion - - // 此处无测试 - //protected override void ViewportDraw(ViewportDraw draw) - //{ - // base.ViewportDraw(draw); - //} -} - -#if false -| UserInputControls.DoNotEchoCancelForCtrlC // 不要取消CtrlC的回音 -| UserInputControls.DoNotUpdateLastPoint // 不要更新最后一点 -| UserInputControls.NoDwgLimitsChecking // 没有Dwg限制检查 -| UserInputControls.NoZeroResponseAccepted // 接受非零响应 -| UserInputControls.NoNegativeResponseAccepted // 不否定回复已被接受 -| UserInputControls.Accept3dCoordinates // 返回点的三维坐标,是转换坐标系了? -| UserInputControls.AcceptMouseUpAsPoint // 接受释放按键时的点而不是按下时 - -| UserInputControls.InitialBlankTerminatesInput // 初始 空格或回车,结束jig -| UserInputControls.AcceptOtherInputString // 接受其他输入字符串 -| UserInputControls.NoZDirectionOrtho // 无方向正射,直接输入数字时以基点到当前点作为方向 -| UserInputControls.UseBasePointElevation // 使用基点高程,基点的Z高度探测 -#endif \ No newline at end of file diff --git a/src/IFoxCAD.Cad/ExtensionMethod/JigExTransient.cs b/src/IFoxCAD.Cad/ExtensionMethod/JigExTransient.cs deleted file mode 100644 index e161928..0000000 --- a/src/IFoxCAD.Cad/ExtensionMethod/JigExTransient.cs +++ /dev/null @@ -1,149 +0,0 @@ -#if !ac2008 -namespace IFoxCAD.Cad; - -/// -/// 瞬态容器 -/// -public class JigExTransient : IDisposable -{ - #region 私有字段 - // 整数集,暂时不知道有什么意义 - IntegerCollection _integerCollection; - // 维护集合 - HashSet _entities; - #endregion - - #region 公开属性 - /// - /// 对象集合 - /// - public Entity[] Entities => _entities.ToArray(); - /// - /// 数量 - /// - public int Count => _entities.Count; - #endregion - - #region 构造函数 - /// - /// 瞬态容器 - /// - public JigExTransient() - { - _integerCollection = new(); - _entities = new(); - } - #endregion - - #region 方法 - /// - /// 判断瞬态容器里是否含有对象 - /// - /// 对象 - /// 含有返回true - public bool Contains(Entity ent) - { - return _entities.Contains(ent); - } - - /// - /// 向瞬态容器中添加对象 - /// - /// 图元 - /// 绘图模式 - public void Add(Entity ent, TransientDrawingMode tdm = TransientDrawingMode.Main) - { - if (_entities.Add(ent)) - { - TransientManager - .CurrentTransientManager - .AddTransient(ent, tdm, 128, _integerCollection); - } - } - - - /// - /// 从瞬态容器中移除图元 - /// - /// 已经加入瞬态容器的图元 - public void Remove(Entity ent) - { - if (!Contains(ent)) - return; - TransientManager - .CurrentTransientManager - .EraseTransient(ent, _integerCollection); - _entities.Remove(ent); - } - - /// - /// 清空瞬态容器并移除图元显示 - /// - public void Clear() - { - foreach (var ent in _entities) - { - TransientManager - .CurrentTransientManager - .EraseTransient(ent, _integerCollection); - } - _entities.Clear(); - } - - - /// - /// 更新单个显示 - /// - /// 已经加入瞬态容器的图元 - public void Update(Entity ent) - { - if (!Contains(ent)) - return; - TransientManager - .CurrentTransientManager - .UpdateTransient(ent, _integerCollection); - } - - /// - /// 更新全部显示 - /// - public void UpdateAll() - { - foreach (var ent in _entities) - Update(ent); - } - #endregion - - #region IDisposable接口相关函数 - public bool IsDisposed { get; private set; } = false; - - /// - /// 手动释放 - /// - public void Dispose() - { - Dispose(true); - GC.SuppressFinalize(this); - } - - /// - /// 析构函数调用释放 - /// - ~JigExTransient() - { - Dispose(false); - } - - /// - /// 销毁瞬态容器 - /// - protected virtual void Dispose(bool disposing) - { - if (IsDisposed) return; - IsDisposed = true; - - Clear();// 清空瞬态容器并移除对象在图纸上的显示 - } - #endregion -} -#endif \ No newline at end of file diff --git a/src/IFoxCAD.Cad/ExtensionMethod/ObjEx.cs b/src/IFoxCAD.Cad/ExtensionMethod/ObjEx.cs deleted file mode 100644 index 1c90814..0000000 --- a/src/IFoxCAD.Cad/ExtensionMethod/ObjEx.cs +++ /dev/null @@ -1,21 +0,0 @@ -namespace IFoxCAD.Cad; - -public static class ObjEx -{ - /// - /// cad的打印 - /// - /// - public static void Print(this object obj) - { - Acap.DocumentManager.MdiActiveDocument.Editor.WriteMessage($"{obj}\n"); - } - /// - /// 系统的打印 - /// - /// - public static void PrintLine(this object obj) - { - Console.WriteLine(obj.ToString()); - } -} diff --git a/src/IFoxCAD.Cad/ExtensionMethod/ObjectIdEx.cs b/src/IFoxCAD.Cad/ExtensionMethod/ObjectIdEx.cs deleted file mode 100644 index 92e8b0f..0000000 --- a/src/IFoxCAD.Cad/ExtensionMethod/ObjectIdEx.cs +++ /dev/null @@ -1,92 +0,0 @@ -namespace IFoxCAD.Cad; - -/// -/// 对象id扩展类 -/// -public static class ObjectIdEx -{ - #region GetObject - - /// - /// 获取指定类型对象 - /// - /// 指定的泛型 - /// 对象id - /// 事务 - /// 打开模式 - /// 打开删除对象 - /// 指定类型对象 - public static T? GetObject(this ObjectId id, OpenMode mode = OpenMode.ForRead, bool openErased = false, Transaction? tr = default) where T : DBObject - { - tr ??= DBTrans.Top.Transaction; - // tr = Env.GetTrans(tr); - return tr.GetObject(id, mode, openErased) as T; - } - - /// - /// 获取指定类型对象集合 - /// - /// 指定的泛型 - /// 对象id集合 - /// 事务 - /// 打开模式 - /// 打开删除对象 - /// 指定类型对象集合 - public static IEnumerable GetObject(this IEnumerable ids, - OpenMode mode = OpenMode.ForRead, - bool openErased = false, - Transaction? tr = default) where T : DBObject - { - return ids.Select(id => id.GetObject(mode, openErased, tr)); - } - - /// - /// 返回符合类型的对象id - /// - /// 对象类型 - /// 对象id集合 - /// 对象id集合 - public static IEnumerable OfType(this IEnumerable ids) where T : DBObject - { - string dxfName = RXClass.GetClass(typeof(T)).DxfName; - return ids.Where(id => id.ObjectClass().DxfName == dxfName); - } - #endregion GetObject - - // Acad08缺少 id.ObjectClass 如何补偿? - public static RXClass ObjectClass(this ObjectId id) - { -#if NET35 - return RXClass.GetClass(id.GetType()); -#else - return id.ObjectClass; -#endif - } - - /// - /// id是否有效,未被删除 - /// - /// 对象id - /// id有效返回 ,反之返回 - public static bool IsOk(this ObjectId id) - { - return !id.IsNull && id.IsValid && !id.IsErased && !id.IsEffectivelyErased && id.IsResident; - } - - /// - /// 删除id代表的对象 - /// - /// 对象id - public static void Erase(this ObjectId id) - { - if (id.IsOk()) - { - var ent = id.GetObject()!; - using (ent.ForWrite()) - { - ent.Erase(); - }// 第一种读写权限自动转换写法 - // Env.Editor.Regen(); - } - } -} diff --git a/src/IFoxCAD.Cad/ExtensionMethod/PointEx.cs b/src/IFoxCAD.Cad/ExtensionMethod/PointEx.cs deleted file mode 100644 index f65c24e..0000000 --- a/src/IFoxCAD.Cad/ExtensionMethod/PointEx.cs +++ /dev/null @@ -1,134 +0,0 @@ -namespace IFoxCAD.Cad; - -public static class PointEx -{ - - /// - /// 获取点的hash字符串,同时可以作为pt的字符串表示 - /// - /// 点 - /// 指示计算几维坐标的标志,1为计算x,2为计算x,y,其他为计算x,y,z - /// 保留的小数位数 - /// hash字符串 - public static string GetHashString(this Point3d pt, int xyz = 3, int decimalRetain = 6) - { - var de = $"f{decimalRetain}"; - return xyz switch - { - 1 => $"({pt.X.ToString(de)})", - 2 => $"({pt.X.ToString(de)},{pt.Y.ToString(de)})", - _ => $"({pt.X.ToString(de)},{pt.Y.ToString(de)},{pt.Z.ToString(de)})" - }; - } - - // 为了频繁触发所以弄个全局变量 - static Plane? _Plane; - /// - /// 两点计算弧度范围0到2Pi - /// - /// 起点 - /// 终点 - /// 方向 - /// 弧度值 - public static double GetAngle(this Point3d startPoint, Point3d endPoint, Vector3d? direction = null) - { - if (direction != null) - _Plane = new Plane(Point3d.Origin, direction.Value); - if (_Plane == null) - _Plane = new Plane(Point3d.Origin, Vector3d.ZAxis); - return startPoint.GetVectorTo(endPoint).AngleOnPlane(_Plane); - } - /// - /// 两点计算弧度范围0到2Pi - /// - /// 起点 - /// 终点 - /// 弧度值 - public static double GetAngle(this Point2d startPoint, Point2d endPoint) - { - return startPoint.GetVectorTo(endPoint).Angle; - } - - /// - /// 获取中点 - /// - /// - /// - /// - public static Point2d GetCenter(this Point2d a, Point2d b) - { - // (p1 + p2) / 2; // 溢出风险 - return new Point2d(a.X * 0.5 + b.X * 0.5, - a.Y * 0.5 + b.Y * 0.5); - } - - /// http://www.lee-mac.com/bulgeconversion.html - /// - /// 求凸度,判断三点是否一条直线上 - /// - /// 圆弧起点 - /// 圆弧腰点 - /// 圆弧尾点 - /// 逆时针为正,顺时针为负 - public static double GetArcBulge(this Point2d arc1, Point2d arc2, Point2d arc3, double tol = 1e-10) - { - double dStartAngle = arc2.GetAngle(arc1); - double dEndAngle = arc2.GetAngle(arc3); - // 求的P1P2与P1P3夹角 - var talAngle = (Math.PI - dStartAngle + dEndAngle) / 2; - // 凸度==拱高/半弦长==拱高比值/半弦长比值 - // 有了比值就不需要拿到拱高值和半弦长值了,因为接下来是相除得凸度 - double bulge = Math.Sin(talAngle) / Math.Cos(talAngle); - - // 处理精度 - if (bulge > 0.9999 && bulge < 1.0001) - bulge = 1; - else if (bulge < -0.9999 && bulge > -1.0001) - bulge = -1; - else if (Math.Abs(bulge) < tol) - bulge = 0; - return bulge; - } - - - #region 首尾相连 - /// - /// 首尾相连 - /// - public static Point2dCollection End2End(this Point2dCollection ptcol) - { - if (ptcol == null) - throw new ArgumentNullException(nameof(ptcol)); - - if (ptcol.Count == 0 || ptcol[0].Equals(ptcol[^1]))// 首尾相同直接返回 - return ptcol; - - // 首尾不同,去加一个到最后 - var lst = new Point2d[ptcol.Count + 1]; - for (int i = 0; i < lst.Length; i++) - lst[i] = ptcol[i]; - lst[^1] = lst[0]; - - return new(lst); - } - /// - /// 首尾相连 - /// - public static Point3dCollection End2End(this Point3dCollection ptcol) - { - if (ptcol == null) - throw new ArgumentNullException(nameof(ptcol)); - - if (ptcol.Count == 0 || ptcol[0].Equals(ptcol[^1]))// 首尾相同直接返回 - return ptcol; - - // 首尾不同,去加一个到最后 - var lst = new Point3d[ptcol.Count + 1]; - for (int i = 0; i < lst.Length; i++) - lst[i] = ptcol[i]; - lst[^1] = lst[0]; - - return new(lst); - } - #endregion -} \ No newline at end of file diff --git a/src/IFoxCAD.Cad/ExtensionMethod/SelectionSetEx.cs b/src/IFoxCAD.Cad/ExtensionMethod/SelectionSetEx.cs deleted file mode 100644 index dbde0ac..0000000 --- a/src/IFoxCAD.Cad/ExtensionMethod/SelectionSetEx.cs +++ /dev/null @@ -1,101 +0,0 @@ -namespace IFoxCAD.Cad; - -/// -/// 选择集扩展类 -/// -public static class SelectionSetEx -{ - #region 获取对象id - /// - /// 获取已选择的对象 - /// - /// 选择集 - /// 已选择的对象集合 - public static IEnumerable GetSelectedObjects(this SelectionSet ss) - { - return ss.Cast(); - } - - /// - /// 获取已选择的对象 - /// - /// 已选择的对象泛型 - /// 选择集 - /// 已选择的对象集合 - public static IEnumerable GetSelectObjects(this SelectionSet ss) where T : SelectedObject - { - return ss.Cast().OfType(); - } - - /// - /// 从选择集中获取对象id - /// - /// 图元类型 - /// 选择集 - /// 已选择的对象id集合 - public static IEnumerable GetObjectIds(this SelectionSet ss) where T : Entity - { - string dxfName = RXClass.GetClass(typeof(T)).DxfName; - return - ss - .GetObjectIds() - .Where(id => id.ObjectClass().DxfName == dxfName); - } - - /// - /// 将选择集的对象按类型分组 - /// - /// 选择集 - /// 分组后的类型/对象id集合 - public static IEnumerable> GetObjectIdGroup(this SelectionSet ss) - { - return - ss - .GetObjectIds() - .GroupBy(id => id.ObjectClass().DxfName); - } - #endregion - - #region 获取实体对象 - - /// - /// 获取指定类型图元 - /// - /// 指定类型 - /// 选择集 - /// 事务 - /// 打开模式 - /// 图元集合 - public static IEnumerable GetEntities(this SelectionSet ss, OpenMode openMode = OpenMode.ForRead, DBTrans? tr = default) where T : Entity - { - if (ss is null) - throw new ArgumentNullException(nameof(ss)); - - tr ??= DBTrans.Top; - return - ss - .GetObjectIds() - .Select(id => tr.GetObject(id, openMode)) - .Where(ent => ent != null); - } - - #endregion - - #region ForEach - /// - /// 遍历选择集 - /// - /// 指定图元类型 - /// 选择集 - /// 事务 - /// 打开模式 - /// 处理函数 - public static void ForEach(this SelectionSet ss, Action action, OpenMode openMode = OpenMode.ForRead, DBTrans? tr = default) where T : Entity - { - foreach (T? ent in ss.GetEntities(openMode, tr)) - { - action?.Invoke(ent); - } - } - #endregion -} diff --git a/src/IFoxCAD.Cad/ExtensionMethod/SymbolTableEx.cs b/src/IFoxCAD.Cad/ExtensionMethod/SymbolTableEx.cs deleted file mode 100644 index 4703c78..0000000 --- a/src/IFoxCAD.Cad/ExtensionMethod/SymbolTableEx.cs +++ /dev/null @@ -1,358 +0,0 @@ -namespace IFoxCAD.Cad; - -/// -/// 符号表类扩展函数 -/// -public static class SymbolTableEx -{ - #region 图层表 - /// - /// 添加图层 - /// - /// 图层符号表 - /// 图层名 - /// 图层颜色 - /// 图层id - public static ObjectId Add(this SymbolTable table, string name, Color color) - { - return table.Add(name, lt => lt.Color = color); - } - /// - /// 添加图层 - /// - /// 图层符号表 - /// 图层名 - /// 图层颜色索引值 - /// 图层id - public static ObjectId Add(this SymbolTable table, string name, int colorIndex) - { - colorIndex %= 256;// 防止输入的颜色超出256 - colorIndex = Math.Abs(colorIndex);// 防止负数 - return table.Add(name, lt => lt.Color = Color.FromColorIndex(ColorMethod.ByColor, (short)colorIndex)); - } - /// - /// 更改图层名 - /// - /// 图层符号表 - /// 旧图层名 - /// 新图层名 - public static ObjectId Rename(this SymbolTable table, string Oldname, string NewName) - { - if (!table.Has(Oldname)) - return ObjectId.Null; - - table.Change(Oldname, layer => { - layer.Name = NewName; - }); - return table[NewName]; - } - /// - /// 删除图层 - /// - /// 层表 - /// 图层名 - /// 成功返回 ,失败返回 - public static bool Delete(this SymbolTable table, string name) - { - if (name == "0" || name == "Defpoints" || !table.Has(name) || table[name] == DBTrans.Top.Database.Clayer) - return false; - - table.CurrentSymbolTable.GenerateUsageData(); - var ltr = table.GetRecord(name); - if (ltr is null) - return false; - - if (ltr.IsUsed) - return false; - using (ltr.ForWrite()) - ltr.Erase(); - return true; - } - #endregion - - #region 块表 - /// - /// 添加块定义 - /// - /// 块表 - /// 块名 - /// 对所添加块表的委托n - /// 添加图元的委托 - /// 添加属性定义的委托 - /// 块定义id - /// TODO: 需要测试匿名块等特殊的块是否能定义 - public static ObjectId Add(this SymbolTable table, - string name, - Action? action = null, - Func>? ents = null, - Func>? attdef = null) - { - return table.Add(name, btr => { - action?.Invoke(btr); - - var entsres = ents?.Invoke(); - if (entsres is not null) - btr.AddEntity(entsres); - - var adddefres = attdef?.Invoke(); - if (adddefres is not null) - btr.AddEntity(adddefres); - }); - } - /// - /// 添加块定义 - /// - /// 块表 - /// 块名 - /// 图元 - /// 属性定义 - /// - public static ObjectId Add(this SymbolTable table, - string name, - IEnumerable? ents = null, - IEnumerable? attdef = null) - { - return table.Add(name, btr => { - if (ents is not null) - btr.AddEntity(ents); - if (attdef is not null) - btr.AddEntity(attdef); - }); - } - - /// - /// 添加块定义 - /// - /// 块表 - /// 块名 - /// 图元(包括属性) - /// - public static ObjectId Add(this SymbolTable table, string name, params Entity[] ents) - { - return table.Add(name, null, () => { return ents; }); - } - - /// - /// 添加属性到块定义 - /// - /// 块表 - /// 块定义id - /// 属性列表 - public static void AddAttsToBlocks(this SymbolTable table, - ObjectId id, - List atts) - { - List attTags = new(); - table.Change(id, btr => { - - btr.GetEntities() - .ForEach(def => attTags.Add(def.Tag.ToUpper())); - - for (int i = 0; i < atts.Count; i++) - if (!attTags.Contains(atts[i].Tag.ToUpper())) - btr.AddEntity(atts[i]); - }); - } - /// - /// 添加属性到块定义 - /// - /// 块表 - /// 块定义名字 - /// 属性列表 - public static void AddAttsToBlocks(this SymbolTable table, - string name, - List atts) - { - List attTags = new(); - table.Change(name, btr => { - - btr.GetEntities() - .ForEach(def => attTags.Add(def.Tag.ToUpper())); - - for (int i = 0; i < atts.Count; i++) - if (!attTags.Contains(atts[i].Tag.ToUpper())) - btr.AddEntity(atts[i]); - }); - } - - /// - /// 从文件中获取块定义 - /// - /// 块表 - /// 文件名 - /// 是否覆盖 - /// 块定义Id - public static ObjectId GetBlockFrom(this SymbolTable table, string fileName, bool over) - { - // FileInfo fi = new(fileName); - // string blkdefname = fi.Name; - // if (blkdefname.Contains(".")) - // blkdefname = blkdefname.Substring(0, blkdefname.LastIndexOf('.')); - - string blkdefname = SymbolUtilityServices.RepairSymbolName( - SymbolUtilityServices.GetSymbolNameFromPathName(fileName, "dwg"), false); - - ObjectId id = table[blkdefname]; - bool has = id != ObjectId.Null; - if ((has && over) || !has) - { - Database db = new(false, true); - db.ReadDwgFile(fileName, FileShare.Read, true, null); - db.CloseInput(true); - id = table.Database.Insert(BlockTableRecord.ModelSpace, blkdefname, db, false); - } - - return id; - } - - - - /// - /// 从文件中获取块定义 - /// - /// 块表 - /// 文件名 - /// 块定义名 - /// 是否覆盖 - /// 块定义Id - public static ObjectId GetBlockFrom(this SymbolTable table, - string fileName, - string blockName, - bool over) - { - return table.GetRecordFrom(t => t.BlockTable, fileName, blockName, over); - } - #endregion - - - #region 线型表 - /// - /// 添加线型 - /// - /// 线型表 - /// 线型名 - /// 线型说明 - /// 线型长度 - /// 笔画长度数组 - /// 线型id - public static ObjectId Add(this SymbolTable table, string name, string description, double length, double[] dash) - { - return table.Add( - name, - ltt => { - ltt.AsciiDescription = description; - ltt.PatternLength = length; // 线型的总长度 - ltt.NumDashes = dash.Length; // 组成线型的笔画数目 - for (int i = 0; i < dash.Length; i++) - { - ltt.SetDashLengthAt(i, dash[i]); - } - // ltt.SetDashLengthAt(0, 0.5); // 0.5个单位的划线 - // ltt.SetDashLengthAt(1, -0.25); // 0.25个单位的空格 - // ltt.SetDashLengthAt(2, 0); // 一个点 - // ltt.SetDashLengthAt(3, -0.25); // 0.25个单位的空格 - } - ); - } - #endregion - - #region 文字样式表 - /// - /// 添加文字样式记录 - /// - /// 文字样式表 - /// 文字样式名 - /// 字体名 - /// 宽度比例 - /// 文字样式Id - public static ObjectId Add(this SymbolTable table, - string textStyleName, - string font, - double xscale = 1.0) - { - return - table.Add( - textStyleName, - tstr => { - tstr.Name = textStyleName; - tstr.FileName = font; - tstr.XScale = xscale; - }); - } - /// - /// 添加文字样式记录 - /// - /// 文字样式表 - /// 文字样式名 - /// 字体名枚举 - /// 宽度比例 - /// 文字样式Id - public static ObjectId Add(this SymbolTable table, - string textStyleName, - FontTTF fontTTF, - double xscale = 1.0) - { - return table.Add(textStyleName, fontTTF.GetDesc(), xscale); - } - - /// - ///

    添加文字样式记录,如果存在就默认强制替换

    - /// 此函数为了 而设 - ///
    - /// 文字样式表 - /// 文字样式名 - /// 字体名 - /// 大字体名 - /// 宽度比例 - /// 高度 - /// 是否强制替换 - /// 文字样式Id - public static ObjectId AddWithChange(this SymbolTable table, - string textStyleName, - string smallFont, - string bigFont = "", - double xScale = 1, - double height = 0, - bool forceChange = true) - { - if (forceChange && table.Has(textStyleName)) - { - table.Change(textStyleName, ttr => { - ttr.FileName = smallFont; - ttr.XScale = xScale; - ttr.TextSize = height; - if (bigFont != string.Empty) - ttr.BigFontFileName = bigFont; - }); - return table[textStyleName]; - } - return table.Add(textStyleName, ttr => { - ttr.FileName = smallFont; - ttr.XScale = xScale; - ttr.TextSize = height; - }); - } - - - #endregion - - #region 注册应用程序表 - - #endregion - - #region 标注样式表 - - #endregion - - #region 用户坐标系表 - - #endregion - - #region 视图表 - - #endregion - - #region 视口表 - - #endregion -} diff --git a/src/IFoxCAD.Cad/ExtensionMethod/SymbolTableRecordEx.cs b/src/IFoxCAD.Cad/ExtensionMethod/SymbolTableRecordEx.cs deleted file mode 100644 index 4a5737e..0000000 --- a/src/IFoxCAD.Cad/ExtensionMethod/SymbolTableRecordEx.cs +++ /dev/null @@ -1,463 +0,0 @@ -namespace IFoxCAD.Cad; - -/// -/// 符号表记录扩展类 -/// -public static class SymbolTableRecordEx -{ - #region 块表记录 - - #region 克隆实体id - /// - /// 深度克隆id到块表记录 - /// - /// 0x01 此方法不允许是未添加数据库的图元,因此它是id
    - /// 0x02 若为未添加数据库图元,则利用entity.Clone();同时不需要考虑动态块属性,可以使用entity.GetTransformedCopy - ///
    - ///
    - /// - /// - /// 克隆到当前块表记录,相当于原地克隆
    - /// 克隆到目标块表记录内,相当于制作新块 - ///
    - /// - /// 图元id集合,注意所有成员都要在同一个空间中 - /// 克隆后的id词典 - public static IdMapping DeepCloneEx(this BlockTableRecord btr, ObjectIdCollection objIds) - { - if (objIds is null || objIds.Count == 0) - throw new ArgumentNullException(nameof(objIds)); - - var db = objIds[0].Database; - IdMapping mapping = new(); - using (btr.ForWrite()) - { - try - { - db.DeepCloneObjects(objIds, btr.ObjectId, mapping, false); - - // 不在此提取,为了此函数被高频调用 - // 获取克隆键值对(旧块名,新块名) - // foreach (ObjectId item in blockIds) - // result.Add(mapping[item].Value); - } - catch (System.Exception e) - { - LogHelper.FlagOutVsOutput = true; - e.WriteLog("深度克隆出错了"); - } - } - return mapping; - } - - /// - /// 克隆图元实体(这个函数有问题,会出现偶尔成功,偶尔失败,拖动过变成匿名块) - /// 若为块则进行设置属性,因此控制动态块属性丢失; - /// - /// 图元 - /// 矩阵 - // public static void EntityTransformedCopy(this Entity ent, Matrix3d matrix) - // { - // var entNew = ent.GetTransformedCopy(matrix); - // if (ent is BlockReference blockReference) - // entNew.SetPropertiesFrom(blockReference); - // } - - #endregion - - #region 添加实体 - /// - /// 添加实体对象 - /// - /// 块表记录 - /// 实体 - /// 事务管理器 - /// 对象 id - public static ObjectId AddEntity(this BlockTableRecord btr, Entity entity, - Transaction? trans = null) - { - // if (entity is null) - // throw new ArgumentNullException(nameof(entity), "对象为 null"); - - ObjectId id; - trans ??= DBTrans.Top.Transaction; - using (btr.ForWrite()) - { - id = btr.AppendEntity(entity); - trans.AddNewlyCreatedDBObject(entity, true); - } - return id; - } - - /// - /// 添加实体集合 - /// - /// 实体类型 - /// 块表记录 - /// 实体集合 - /// 事务 - /// 对象 id 列表 - public static IEnumerable AddEntity(this BlockTableRecord btr, IEnumerable ents, - Transaction? trans = null) where T : Entity - { - // if (ents.Any(ent => ent is null)) - // throw new ArgumentNullException(nameof(ents), "实体集合内存在 null 对象"); - - trans ??= DBTrans.Top.Transaction; - using (btr.ForWrite()) - { - return ents - .Select( - ent => { - ObjectId id = btr.AppendEntity(ent); - trans.AddNewlyCreatedDBObject(ent, true); - return id; - }) - .ToList(); - } - } - - /// - /// 添加多个实体 - /// - /// 块表记录 - /// 实体集合 - /// 对象 id 列表 - public static IEnumerable AddEntity(this BlockTableRecord btr, params Entity[] ents) - { - return btr.AddEntity(ents, null); - } - #endregion - - #region 添加图元 - /// - /// 在指定绘图空间添加图元 - /// - /// 图元类型 - /// 绘图空间 - /// 图元对象 - /// 图元属性设置委托 - /// 事务管理器 - /// 图元id - private static ObjectId AddEnt(this BlockTableRecord btr, T ent, Action? action, Transaction? trans) where T : Entity - { - // trans ??= DBTrans.Top.Transaction; - action?.Invoke(ent); - return btr.AddEntity(ent, trans); - } - /// - /// 委托式的添加图元 - /// - /// 块表 - /// 返回图元的委托 - /// 事务 - /// 图元id,如果委托返回 null,则为 ObjectId.Null - public static ObjectId AddEnt(this BlockTableRecord btr, Func action, Transaction? transaction) - { - // transaction ??= DBTrans.Top.Transaction; - var ent = action.Invoke(); - if (ent is null) - return ObjectId.Null; - - return btr.AddEntity(ent, transaction); - } - - /// - /// 在指定绘图空间添加直线 - /// - /// 事务管理器 - /// 起点 - /// 终点 - /// 绘图空间 - /// 直线属性设置委托 - /// 直线的id - public static ObjectId AddLine(this BlockTableRecord btr, Point3d start, Point3d end, - Action? action = default, Transaction? trans = default) - { - var line = new Line(start, end); - return btr.AddEnt(line, action, trans); - } - /// - /// 在指定绘图空间X-Y平面添加圆 - /// - /// 绘图空间 - /// 圆心 - /// 半径 - /// 圆属性设置委托 - /// 事务管理器 - /// 圆的id - public static ObjectId AddCircle(this BlockTableRecord btr, Point3d center, double radius, - Action? action = default, Transaction? trans = default) - { - var circle = new Circle(center, Vector3d.ZAxis, radius); - return btr.AddEnt(circle, action, trans); - } - - /// - /// 在指定绘图空间X-Y平面3点画外接圆 - /// - /// 绘图空间 - /// 第一点 - /// 第二点 - /// 第三点 - /// 圆属性设置委托 - /// 事务管理器 - /// 三点有外接圆则返回圆的id,否则返回ObjectId.Null - public static ObjectId AddCircle(this BlockTableRecord btr, Point3d p0, Point3d p1, Point3d p2, - Action? action = default, Transaction? trans = default) - { - var circle = EntityEx.CreateCircle(p0, p1, p2); - // return circle is not null ? btr.AddEnt(circle, action, trans) : throw new ArgumentNullException(nameof(circle), "对象为 null"); - if (circle is null) - throw new ArgumentNullException(nameof(circle), "对象为 null"); - - return btr.AddEnt(circle, action, trans); - } - /// - /// 在指定的绘图空间添加轻多段线 - /// - /// 绘图空间 - /// 多段线信息 - /// 线宽 - /// 是否闭合 - /// 轻多段线属性设置委托 - /// 事务管理器 - /// 轻多段线id - public static ObjectId AddPline(this BlockTableRecord btr, - List bvws, - double? constantWidth = null, - bool isClosed = true, - Action? action = default, - Transaction? trans = default) - { - Polyline pl = new(); - pl.SetDatabaseDefaults(); - if (constantWidth is not null) - { - for (int i = 0; i < bvws.Count; i++) - pl.AddVertexAt(i, bvws[i].Vertex, bvws[i].Bulge, constantWidth.Value, constantWidth.Value); - } - else - { - for (int i = 0; i < bvws.Count; i++) - pl.AddVertexAt(i, bvws[i].Vertex, bvws[i].Bulge, bvws[i].StartWidth, bvws[i].EndWidth); - } - pl.Closed = isClosed;// 闭合 - return btr.AddEnt(pl, action, trans); - } - /// - /// 在指定的绘图空间添加轻多段线 - /// - /// 绘图空间 - /// 端点表 - /// 凸度表 - /// 端点的起始宽度 - /// 端点的终止宽度 - /// 轻多段线属性设置委托 - /// 事务管理器 - /// 轻多段线id - public static ObjectId AddPline(this BlockTableRecord btr, - List pts, - List? bulges = default, - List? startWidths = default, - List? endWidths = default, - Action? action = default, - Transaction? trans = default) - { - bulges ??= new(new double[pts.Count]); - startWidths ??= new(new double[pts.Count]); - endWidths ??= new(new double[pts.Count]); - - Polyline pl = new(); - pl.SetDatabaseDefaults(); - - for (int i = 0; i < pts.Count; i++) - pl.AddVertexAt(i, pts[i].Point2d(), bulges[i], startWidths[i], endWidths[i]); - return btr.AddEnt(pl, action, trans); - } - - /// - /// 在指定的绘图空间添加轻多段线 - /// - /// 绘图空间 - /// 端点表,利用元组(Point3d pt, double bulge, double startWidth, double endWidth) - /// 轻多段线属性设置委托 - /// 事务管理器 - /// 轻多段线id - public static ObjectId AddPline(this BlockTableRecord btr, - List<(Point3d pt, double bulge, double startWidth, double endWidth)> pts, - Action? action = default, - Transaction? trans = default) - { - Polyline pl = new(); - pl.SetDatabaseDefaults(); - pts.ForEach((i, vertex) => { - pl.AddVertexAt(i, vertex.pt.Point2d(), vertex.bulge, vertex.startWidth, vertex.endWidth); - }); - - return btr.AddEnt(pl, action, trans); - } - - /// - /// 在指定绘图空间X-Y平面3点画圆弧 - /// - /// 绘图空间 - /// 圆弧起点 - /// 圆弧上的点 - /// 圆弧终点 - /// 圆弧属性设置委托 - /// 事务管理器 - /// 圆弧id - public static ObjectId AddArc(this BlockTableRecord btr, - Point3d startPoint, Point3d pointOnArc, Point3d endPoint, - Action? action = default, Transaction? trans = default) - { - var arc = EntityEx.CreateArc(startPoint, pointOnArc, endPoint); - return btr.AddEnt(arc, action, trans); - } - - // todo: 所有涉及默认无参构造的实体类型,都需要调用SetDatabaseDefaults(); - #endregion - - #region 获取实体/实体id - /// - /// 获取块表记录内的指定类型的实体 - /// - /// 实体类型 - /// 块表记录 - /// 打开模式 - /// 事务 - /// 实体集合 - public static IEnumerable GetEntities(this BlockTableRecord btr, - OpenMode mode = OpenMode.ForRead, - Transaction? trans = default) where T : Entity - { - trans ??= DBTrans.Top.Transaction; - return - btr - .Cast() - .Select(id => trans.GetObject(id, mode)) - .OfType(); - } - - /// - /// 按类型获取实体Id,AutoCad2010以上版本支持 - /// - /// 实体类型 - /// 块表记录 - /// 实体Id集合 - public static IEnumerable GetObjectIds(this BlockTableRecord btr) where T : Entity - { - string dxfName = RXClass.GetClass(typeof(T)).DxfName; - return btr.Cast() - .Where(id => id.ObjectClass().DxfName == dxfName); - } - - /// - /// 按类型获取实体Id的分组 - /// - /// 块表记录 - /// 实体Id分组 - public static IEnumerable> GetObjectIds(this BlockTableRecord btr) - { - return - btr - .Cast() - .GroupBy(id => id.ObjectClass().DxfName); - } - - /// - /// 获取绘制顺序表 - /// - /// 块表 - /// 事务 - /// 绘制顺序表 - public static DrawOrderTable? GetDrawOrderTable(this BlockTableRecord btr, - Transaction? trans = default) - { - trans ??= DBTrans.Top.Transaction; - return trans.GetObject(btr.DrawOrderTableId, OpenMode.ForRead) as DrawOrderTable; - } - #endregion - - #region 插入块参照 - /// - /// 插入块参照 - /// - /// 块表记录 - /// 插入点 - /// 块名 - /// 块插入比例,默认为1 - /// 块插入旋转角(弧度),默认为0 - /// 属性字典{Tag,Value},默认为null - /// 事务 - /// 块参照对象id - public static ObjectId InsertBlock(this BlockTableRecord blockTableRecord, Point3d position, - string blockName, - Scale3d scale = default, - double rotation = default, - Dictionary? atts = default, Transaction? trans = null) - { - trans ??= DBTrans.Top.Transaction; - if (!DBTrans.Top.BlockTable.Has(blockName)) - { - DBTrans.Top.Editor?.WriteMessage($"\n不存在名字为{blockName}的块定义。"); - return ObjectId.Null; - } - return blockTableRecord.InsertBlock(position, DBTrans.Top.BlockTable[blockName], scale, rotation, atts, trans); - } - /// - /// 插入块参照 - /// - /// 插入点 - /// 块定义id - /// 块插入比例,默认为1 - /// 块插入旋转角(弧度),默认为0 - /// 属性字典{Tag,Value},默认为null - /// 块参照对象id - public static ObjectId InsertBlock(this BlockTableRecord blockTableRecord, Point3d position, - ObjectId blockId, - Scale3d scale = default, - double rotation = default, - Dictionary? atts = default, Transaction? trans = null) - { - trans ??= DBTrans.Top.Transaction; - if (!DBTrans.Top.BlockTable.Has(blockId)) - { - DBTrans.Top.Editor?.WriteMessage($"\n不存在块定义。"); - return ObjectId.Null; - } - using var blockref = new BlockReference(position, blockId) - { - ScaleFactors = scale, - Rotation = rotation - }; - var objid = blockTableRecord.AddEntity(blockref); - if (atts != default) - { - var btr = DBTrans.Top.GetObject(blockref.BlockTableRecord)!; - if (btr.HasAttributeDefinitions) - { - var attdefs = btr.GetEntities(); - foreach (var attdef in attdefs) - { - using AttributeReference attref = new(); - attref.SetDatabaseDefaults(); - attref.SetAttributeFromBlock(attdef, blockref.BlockTransform); - attref.Position = attdef.Position.TransformBy(blockref.BlockTransform); - attref.AdjustAlignment(DBTrans.Top.Database); - - if (atts.ContainsKey(attdef.Tag)) - attref.TextString = atts[attdef.Tag]; - - blockref.AttributeCollection.AppendAttribute(attref); - trans.AddNewlyCreatedDBObject(attref, true); - } - } - } - return objid; - } - #endregion - - #endregion -} \ No newline at end of file diff --git a/src/IFoxCAD.Cad/ExtensionMethod/Tools.cs b/src/IFoxCAD.Cad/ExtensionMethod/Tools.cs deleted file mode 100644 index b11d81d..0000000 --- a/src/IFoxCAD.Cad/ExtensionMethod/Tools.cs +++ /dev/null @@ -1,188 +0,0 @@ -namespace IFoxCAD.Cad; - -public static class Tools -{ - public static void TestTimes2(int count, string message, Action action) - { - System.Diagnostics.Stopwatch watch = new(); - watch.Start(); // 开始监视代码运行时间 - for (int i = 0; i < count; i++) - action.Invoke();// 需要测试的代码 - watch.Stop(); // 停止监视 - TimeSpan timespan = watch.Elapsed; // 获取当前实例测量得出的总时间 - double time = timespan.TotalMilliseconds; - string name = "毫秒"; - if (timespan.TotalMilliseconds > 1000) - { - time = timespan.TotalSeconds; - name = "秒"; - } - Env.Print($"{message} 代码执行 {count} 次的时间:{time} ({name})"); // 总毫秒数 - } - - /// - /// 纳秒计时器 - /// - public static void TestTimes(int count, string message, Action action, - Timer.TimeEnum timeEnum = Timer.TimeEnum.Millisecond) - { - double time = Timer.RunTime(() => { - for (int i = 0; i < count; i++) - action(); - }, timeEnum); - - string timeNameZn = ""; - switch (timeEnum) - { - case Timer.TimeEnum.Second: - timeNameZn = " 秒"; - break; - case Timer.TimeEnum.Millisecond: - timeNameZn = " 毫秒"; - break; - case Timer.TimeEnum.Microsecond: - timeNameZn = " 微秒"; - break; - case Timer.TimeEnum.Nanosecond: - timeNameZn = " 纳秒"; - break; - } - - Env.Print($"{message} 代码执行 {count} 次的时间:{time} ({timeNameZn})"); - } -} - -/* -// 测试例子,同时验证两个计时器 -var stopwatch = new Stopwatch(); -Timer.RunTime(() => { - - stopwatch.Start(); - for (int i = 0; i < 10000000; i++) - i++; - stopwatch.Stop(); - -}, Timer.TimeEnum.Millisecond, "运行:"); -Console.WriteLine("运行毫秒:" + stopwatch.ElapsedMilliseconds); - */ - -public class Timer -{ - [Flags] - public enum TimeEnum - { - /// - /// 秒 - /// - Second, - /// - /// 毫秒 - /// - Millisecond, - /// - /// 微秒 - /// - Microsecond, - /// - /// 纳秒 - /// - Nanosecond, - } - - [DllImport("Kernel32.dll")] - static extern bool QueryPerformanceCounter(out long lpPerformanceCount); - - /// - /// 这个函数会检索性能计数器的频率. - /// 性能计数器的频率在系统启动时是固定的,并且在所有处理器上都是一致的 - /// 因此,只需在应用初始化时查询频率,即可缓存结果 - /// 在运行 Windows XP 或更高版本的系统上,该函数将始终成功,因此永远不会返回零 - /// - /// - /// - [DllImport("Kernel32.dll")] - static extern bool QueryPerformanceFrequency(out long lpFrequency); - - long _startTime, _stopTime; - readonly long _freq; - - public Timer() - { - _startTime = 0; - _stopTime = 0; - - if (!QueryPerformanceFrequency(out _freq)) - throw new Win32Exception("不支持高性能计数器"); - } - - /// - /// 开始计时器 - /// - public void Start() - { - System.Threading.Thread.Sleep(0); - QueryPerformanceCounter(out _startTime); - } - - /// - /// 停止计时器 - /// - public void Stop() - { - QueryPerformanceCounter(out _stopTime); - _Second = (double)(_stopTime - _startTime) / _freq; - } - double _Second = 0; - - // 返回计时器经过时间 - public double Second => _Second; - public double Millisecond => _Second * 1000.0; - public double Microsecond => _Second * 1000000.0; - public double Nanosecond => _Second * 1000000000.0; - - public static double RunTime(Action action, TimeEnum timeEnum = TimeEnum.Millisecond, string? msg = null) - { - var nanoSecond = new Timer(); - nanoSecond.Start(); - action(); - nanoSecond.Stop(); - - double time = 0; - switch (timeEnum) - { - case TimeEnum.Second: - time = nanoSecond.Second; - break; - case TimeEnum.Millisecond: - time = nanoSecond.Millisecond; - break; - case TimeEnum.Microsecond: - time = nanoSecond.Microsecond; - break; - case TimeEnum.Nanosecond: - time = nanoSecond.Nanosecond; - break; - } - if (msg != null) - { - string timeNameZn = ""; - switch (timeEnum) - { - case TimeEnum.Second: - timeNameZn = " 秒"; - break; - case TimeEnum.Millisecond: - timeNameZn = " 毫秒"; - break; - case TimeEnum.Microsecond: - timeNameZn = " 微秒"; - break; - case TimeEnum.Nanosecond: - timeNameZn = " 纳秒"; - break; - } - Env.Print(msg + " " + time + timeNameZn); - } - return time; - } -} \ No newline at end of file diff --git a/src/IFoxCAD.Cad/ExtensionMethod/XrefEx.cs b/src/IFoxCAD.Cad/ExtensionMethod/XrefEx.cs deleted file mode 100644 index cb57d30..0000000 --- a/src/IFoxCAD.Cad/ExtensionMethod/XrefEx.cs +++ /dev/null @@ -1,652 +0,0 @@ -// #define error_demo - -namespace IFoxCAD.Cad; - -#region 参照工厂 -public interface IXrefBindModes -{ - /// - /// 卸载 - /// - public void Unload(); - /// - /// 重载 - /// - public void Reload(); - /// - /// 拆离 - /// - public void Detach(); - /// - /// 绑定 - /// - public void Bind(); -} - -public class XrefFactory : IXrefBindModes -{ - #region 私有字段 - readonly DBTrans _tr; - /// - /// 要处理的参照名称,就处理所有 - /// - readonly HashSet? _xrefNames; - #endregion - - #region 公开字段 - /// - /// 解析外部参照:线性引擎
    - /// 默认
    - /// 时会在cad命令历史打印一些AEC信息,并导致绑定慢一点...具体作用不详
    - ///
    - public bool UseThreadEngine = false; - /// - /// 解析外部参照:仅处理 Unresolved_未融入(未解析)的参照
    - /// 默认 - ///
    - public bool DoNewOnly = true; - /// - /// 解析外部参照:包含僵尸参照 - /// - public bool IncludeGhosts = true; - - - /// - /// 绑定模式和双美元符号相关(与cad保持相同的默认)
    - /// 为绑定模式,产生双美元; - /// 为插入模式,块重名会以本图覆盖; - ///
    - public bool BindOrInsert = false; - /// - /// bind时候是否拆离参照
    - /// 默认:学官方的绑定后自动拆离 - ///
    - public bool AutoDetach = true; - /// - /// bind时候是否删除被卸载的嵌套参照
    - /// 默认 - ///
    - public bool EraseNested = true; - /// - /// bind时候控制绑定的符号表:请保持默认
    - /// 目前仅推荐用于
    - /// 其他项有异常:
    - ///
    - public SymModes SymModesBind = SymModes.LayerTable; - #endregion - - #region 构造 - /// - /// 参照工厂 - /// - /// - /// 要处理的参照名称,就处理所有 - public XrefFactory(DBTrans tr, HashSet? xrefNames = null) - { - _tr = tr; - _xrefNames = xrefNames; - } - #endregion - - #region 重写 - public void Bind() - { - // 此功能有绑定出错的问题 - // db.BindXrefs(xrefIds, true); - - // 绑定后会自动拆离 - // 此功能修补了上面缺失 - DoubleBind(); - } - - public void Detach() - { - var xrefIds = GetAllXrefNode(); - foreach (ObjectId id in xrefIds) - _tr.Database.DetachXref(id); - } - - public void Reload() - { - var xrefIds = GetAllXrefNode(); - if (xrefIds.Count > 0) - _tr.Database.ReloadXrefs(xrefIds); - } - - public void Unload() - { - var xrefIds = GetAllXrefNode(); - if (xrefIds.Count > 0) - _tr.Database.UnloadXrefs(xrefIds); - } - #endregion - - #region 双重绑定 - /// - /// 获取参照 - /// - /// 全部参照id - ObjectIdCollection GetAllXrefNode() - { - // 储存要处理的参照id - var xrefIds = new ObjectIdCollection(); - XrefNodeForEach((xNodeName, xNodeId, xNodeStatus, xNodeIsNested) => { - if (XrefNamesContains(xNodeName)) - xrefIds.Add(xNodeId); - }); - return xrefIds; - } - - bool XrefNamesContains(string xNodeName) - { - // 为空的时候全部加入 || 有内容时候含有目标 - return _xrefNames is null || _xrefNames.Contains(xNodeName); - } - - /// - /// 遍历参照 - /// - /// (参照名,参照块表记录id,参照状态,是否嵌入) - void XrefNodeForEach(Action action) - { - // btRec.IsFromOverlayReference 是覆盖 - // btRec.GetXrefDatabase(true) 外部参照数据库 - - if (action == null) - return; - - // 解析外部参照:此功能不能锁定文档 - _tr.Database.ResolveXrefs(UseThreadEngine, DoNewOnly); - - var xg = _tr.Database.GetHostDwgXrefGraph(IncludeGhosts); - for (int i = 0; i < xg.NumNodes; i++) - { - var xNode = xg.GetXrefNode(i); - if (!xNode.BlockTableRecordId.IsOk()) - continue; - - action.Invoke(xNode.Name, - xNode.BlockTableRecordId, - xNode.XrefStatus, - xNode.IsNested); - } - } - - /// - /// 符号表记录加入容器 - /// - static void AddedxbindIds(ObjectIdCollection xbindXrefsIds, - SymbolTable symbolTable) - where TTable : SymbolTable - where TRecord : SymbolTableRecord, new() - { - symbolTable.ForEach(tabRec => { - if (tabRec.IsResolved) - xbindXrefsIds.Add(tabRec.ObjectId); - }, checkIdOk: true); - } - - ObjectIdCollection GetXBindIds() - { - // xbind - // 0x01 它是用来绑其他符号表,绑块表会有异常 - // 0x02 集合若有问题,就会出现eWrongObjectType - var xbindIds = new ObjectIdCollection(); - - // 起初测试是将九大符号表记录均加入的,但经实测不行...(为什么?存疑) - #region Option1 - if ((SymModesBind & SymModes.LayerTable) == SymModes.LayerTable) - AddedxbindIds(xbindIds, _tr.LayerTable); - - if ((SymModesBind & SymModes.TextStyleTable) == SymModes.TextStyleTable) - AddedxbindIds(xbindIds, _tr.TextStyleTable); - - if ((SymModesBind & SymModes.RegAppTable) == SymModes.RegAppTable) - AddedxbindIds(xbindIds, _tr.RegAppTable); - - if ((SymModesBind & SymModes.DimStyleTable) == SymModes.DimStyleTable) - AddedxbindIds(xbindIds, _tr.DimStyleTable); - - if ((SymModesBind & SymModes.LinetypeTable) == SymModes.LinetypeTable) - AddedxbindIds(xbindIds, _tr.LinetypeTable); - #endregion - - #region Option2 - if ((SymModesBind & SymModes.UcsTable) == SymModes.UcsTable) - AddedxbindIds(xbindIds, _tr.UcsTable); - - if ((SymModesBind & SymModes.ViewTable) == SymModes.ViewTable) - AddedxbindIds(xbindIds, _tr.ViewTable); - - if ((SymModesBind & SymModes.ViewportTable) == SymModes.ViewportTable) - AddedxbindIds(xbindIds, _tr.ViewportTable); - #endregion - - return xbindIds; - } - - ObjectIdCollection GetBindIds() - { - // bind 只绑块表 - var bindIds = new ObjectIdCollection(); - - _tr.BlockTable.ForEach(btr => { - if (btr.IsLayout) - return; - - // 外部参照 && 已融入 - if (btr.IsFromExternalReference && btr.IsResolved) - bindIds.Add(btr.ObjectId); - }, checkIdOk: true); - - return bindIds; - } - - /// - /// 获取可以拆离的ids - /// - /// 返回已卸载中含有嵌套的参照,要重载之后才能绑定 - /// 返回未参照中嵌套的参照,直接拆离 - List GetDetachIds(Dictionary nested) - { - // 直接拆离的id - List detachIds = new(); - - // 收集要处理的id - XrefNodeForEach((xNodeName, xNodeId, xNodeStatus, xNodeIsNested) => { - switch (xNodeStatus) - { - case XrefStatus.Unresolved:// 未融入_ResolveXrefs参数2 - break; - case XrefStatus.FileNotFound:// 未融入(未解析)_未找到文件 - break; - case XrefStatus.Unreferenced:// 未参照 - { - // 为空的时候全部加入 || 有内容时候含有目标 - if (XrefNamesContains(xNodeName)) - detachIds.Add(xNodeId); - } - break; - case XrefStatus.Unloaded:// 已卸载 - { - // 为空的时候全部加入 || 有内容时候含有目标 - if (XrefNamesContains(xNodeName)) - { - var btr = _tr.GetObject(xNodeId); - if (btr != null && btr.IsFromExternalReference) - { - if (!xNodeIsNested) - detachIds.Add(xNodeId); - else if (!nested.ContainsKey(xNodeId)) - nested.Add(xNodeId, xNodeName);// 嵌套参照 - } - } - } - break; - case XrefStatus.Resolved:// 已融入_就是可以绑定的 - break; - case XrefStatus.NotAnXref:// 不是外部参照 - break; - default: - break; - } - }); - return detachIds; - } - - /// - /// 双重绑定参照 - /// 参考链接 - /// - void DoubleBind() - { - Dictionary nested = new(); - var detachIds = GetDetachIds(nested); - - // 拆离:未参照的文件 - if (AutoDetach) - { - for (int i = 0; i < detachIds.Count; i++) - _tr.Database.DetachXref(detachIds[i]); - } - // 重载:嵌套参照已卸载了,需要重载之后才能进行绑定 - var keys = nested.Keys; - if (keys.Count > 0) - _tr.Database.ReloadXrefs(new ObjectIdCollection(keys.ToArray())); - - // 绑定:切勿交换,否则会绑定无效 - var bindIds = GetBindIds(); - var xbindIds = GetXBindIds(); - if (xbindIds.Count > 0) - _tr.Database.XBindXrefs(xbindIds, BindOrInsert); - if (bindIds.Count > 0) - _tr.Database.BindXrefs(bindIds, BindOrInsert); - - - // 内部删除嵌套参照的块操作 - if (EraseNested) - { -#if ac2008 - // 因为Acad08索引器存在会暴露isErase的(桌子底层的原因), - // 也就是可能获取两个名称一样的,只能用遍历的方式进行 - HashSet nestedHash = new(); - foreach (var item in nested) - nestedHash.Add(item.Value); - - // 遍历全图,找到参照名称一样的删除 - _tr.BlockTable.ForEach(btr => { - if (btr.IsLayout) - return; - if (nestedHash.Contains(btr.Name)) - { - btr.UpgradeOpen(); - btr.Erase(); - btr.DowngradeOpen(); - btr.Dispose(); - } - }, checkIdOk: true); -#else - foreach (var item in nested) - { - var name = item.Value; - if (_tr.BlockTable.Has(name)) - _tr.GetObject(_tr.BlockTable[name], OpenMode.ForWrite)? - .Erase(); - } -#endif - } - } - #endregion -} - - - -public static class XrefEx -{ - /// - /// 外部参照工厂 - /// - /// - /// 处理参照的枚举 - /// 要处理的参照名称,就处理所有 - public static void XrefFactory(this DBTrans tr, - XrefModes xrefModes, - HashSet? xrefNames = null) - { - var xf = new XrefFactory(tr, xrefNames); - tr.Task(() => { - switch (xrefModes) - { - case XrefModes.Unload: - xf.Unload(); - break; - case XrefModes.Reload: - xf.Reload(); - break; - case XrefModes.Detach: - xf.Detach(); - break; - case XrefModes.Bind: - xf.Bind(); - break; - default: - break; - } - }); - } -} - -#endregion - -#region 参照路径工具类 -/// -/// 获取外部参照的路径 -/// -public class XrefPath -{ - #region 属性 - /// - /// 基础路径 - /// - public string CurrentDatabasePath; - /// - /// 是否外部参照 - /// - public bool IsFromExternalReference { get; private set; } - /// - /// 外部参照保存的路径 - /// - /// 它们会是以下任一路径:
    - /// 0x01 相对路径
    - /// 0x02 绝对路径
    - /// 0x03 共目录优先找到的路径(文件夹整体移动会发生此类情况) - ///
    - ///
    - public string? PathSave { get; private set; } - /// - /// 找到的路径(参照面板的名称) - /// 路径不存在时,返回是外部参照dwg文件路径 - /// - public string? PathDescribe { get; private set; } - - string? _PathComplete; - /// - /// 绝对路径 - /// - public string? PathComplete => _PathComplete ??= - PathConverter(CurrentDatabasePath, PathDescribe, PathConverterModes.Complete); - - string? _PathRelative; - /// - /// 相对路径 - /// - public string? PathRelative => _PathRelative ??= - PathConverter(CurrentDatabasePath, PathComplete, PathConverterModes.Relative); - #endregion - - #region 构造 - /// - /// 获取外部参照的路径 - /// - /// 外部参照图元 - /// 事务 - /// 是否外部参照 - public XrefPath(BlockReference brf, DBTrans tr) - { - if (brf == null) - throw new ArgumentNullException(nameof(brf)); - - CurrentDatabasePath = Path.GetDirectoryName(tr.Database.Filename); - - var btRec = tr.GetObject(brf.BlockTableRecord);// 块表记录 - if (btRec == null) - return; - - IsFromExternalReference = btRec.IsFromExternalReference; - if (!IsFromExternalReference) - return; - - // 相对路径==".\\AA.dwg" - // 无路径=="AA.dwg" - PathSave = btRec.PathName; - - if ((!string.IsNullOrEmpty(PathSave) && PathSave[0] == '.') || File.Exists(PathSave)) - { - // 相对路径||绝对路径 - PathDescribe = PathSave; - } - else - { - // 无路径 - var db = btRec.GetXrefDatabase(true); - PathDescribe = db.Filename; - } - } - #endregion - - #region 静态函数 - /// - /// 获取相对路径或者绝对路径 - /// 参考链接 - /// - /// 基础目录(末尾无斜杠) - /// 相对路径或者绝对路径 - /// 依照枚举返回对应的字符串 - /// - public static string? PathConverter(string? directory, string? fileRelations, PathConverterModes converterModes) - { - if (directory == null) - throw new ArgumentNullException(nameof(directory)); - if (fileRelations == null) - throw new ArgumentNullException(nameof(fileRelations)); - - string? result = null; - switch (converterModes) - { - case PathConverterModes.Relative: - result = GetRelativePath(directory, fileRelations); - break; - case PathConverterModes.Complete: - result = GetCompletePath(directory, fileRelations); - break; - default: - break; - } - return result; - } - -#if error_demo - /// - /// 绝对路径->相对路径 - /// - /// 绝对路径 - /// 相对关系 - /// - /// StringHelper.GetRelativePath("G:\\A1.项目\\20190920金山谷黄宅\\01.饰施图\\03.平面图", - /// "G:\\A1.项目\\20190920金山谷黄宅\\01.饰施图\\01.辅助文件\\图框\\A3图框.dwg"); - public static string GetRelativePath(string strDbPath, string strXrefPath) - { - Uri uri1 = new(strXrefPath); - Uri uri2 = new(strDbPath); - Uri relativeUri = uri2.MakeRelativeUri(uri1); - // 测试例子变成 01.%E8%BE%85%E5%8A%A9%E6%96%87%E4%BB%B6/%E5%9B%BE%E6%A1%86/A3%E5%9B%BE%E6%A1%86.dwg - string str = relativeUri.ToString(); - - // 因为这里不会实现".\A.dwg"而是"A.dwg",所以加入这个操作,满足同目录文件 - var strs = str.Split('\\'); - if (strs.Length == 1) - str = ".\\" + str; - return str; - } -#else - /// - /// 绝对路径->相对路径 - /// - /// 相对关系:文件夹路径 - /// 完整路径:文件路径 - /// 相对路径 - /// "..\\01.辅助文件\\图框\\A3图框.dwg" - /// ]]> - static string GetRelativePath(string directory, string file) - { - string[] directorys = directory.Split('\\'); - string[] files = file.Split('\\'); - // 获取两条路径中的最短路径 - int getMinLength = directorys.Length < files.Length ? directorys.Length : files.Length; - - // 用于确定我们退出的循环中的位置。 - int lastCommonRoot = -1; - int index; - // 找到共根 - for (index = 0; index < getMinLength; index++) - { - if (directorys[index] != files[index]) - break; - lastCommonRoot = index; - } - // 如果我们没有找到一个共同的前缀,那么抛出 - if (lastCommonRoot == -1) - throw new ArgumentException("路径没有公共相同路径部分"); - - // 建立相对路径 - var result = new StringBuilder(); - for (index = lastCommonRoot + 1; index < directorys.Length; index++) - if (directorys[index].Length > 0) - result.Append("..\\");// 上级目录加入 - - // 添加文件夹 - for (index = lastCommonRoot + 1; index < files.Length - 1; index++) - result.Append(files[index] + "\\"); - - // 本级目录 - if (result.Length == 0) - result.Append(".\\"); - // result.Append(strXrefPaths[^1]);// 下级目录加入 - result.Append(files[files.Length - 1]);// 下级目录加入 - return result.ToString(); - } -#endif - - /// - /// 相对路径->绝对路径 - /// - /// 文件夹路径 - /// 相对关系:有..的 - /// 完整路径 - /// "G:\\A1.项目\\20190920金山谷黄宅\\01.饰施图\\01.辅助文件\\图框\\A3图框.dwg" - /// ]]> - static string? GetCompletePath(string directory, string relativePath) - { - if (relativePath is null || relativePath.Trim() == string.Empty) - return null; - - var relativeName = Path.GetDirectoryName(relativePath); - if (relativeName is null) - return null; - - if (relativePath[0] != '.') - return relativePath; - - const char slash = '\\'; - - // 判断向上删除几个 - var path_xiangduis = relativeName.Split(slash); - int index = 0; - for (int i = 0; i < path_xiangduis.Length; i++) - { - if (path_xiangduis[i] != "..") - break; - index++; - } - - var result = new StringBuilder(); - // 前段 - var path_dwgs = directory.Split(slash); - path_dwgs = path_dwgs.Where(s => !string.IsNullOrEmpty(s)).ToArray();// 清理空数组 - for (int i = 0; i < path_dwgs.Length - index; i++) - { - result.Append(path_dwgs[i]); - result.Append(slash); - } - // 后段 - for (int i = 0; i < path_xiangduis.Length; i++) - { - var item = path_xiangduis[i]; - if (item != "." && item != "..") - { - result.Append(item); - result.Append(slash); - } - } - result.Append(Path.GetFileName(relativePath)); - return result.ToString(); - } - #endregion -} -#endregion \ No newline at end of file diff --git "a/src/IFoxCAD.Cad/ExtensionMethod/\346\226\260\345\273\272\345\241\253\345\205\205/HatchConverter.cs" "b/src/IFoxCAD.Cad/ExtensionMethod/\346\226\260\345\273\272\345\241\253\345\205\205/HatchConverter.cs" deleted file mode 100644 index 27d4bfe..0000000 --- "a/src/IFoxCAD.Cad/ExtensionMethod/\346\226\260\345\273\272\345\241\253\345\205\205/HatchConverter.cs" +++ /dev/null @@ -1,358 +0,0 @@ -namespace IFoxCAD.Cad; -using PointV = Point2d; - -/// -/// 填充边界转换器 -/// -public class HatchConverter -{ - #region 辅助类 - /// - /// 生成圆形数据 - /// - class CircleData - { - public PointV Center; - public double Radius; - - /// - /// 生成圆形数据 - /// - /// 对称点1 - /// 对称点2 - public CircleData(PointV symmetryAxisPoint1, PointV symmetryAxisPoint2) - { - Center = symmetryAxisPoint1.GetCenter(symmetryAxisPoint2); - Radius = symmetryAxisPoint1.GetDistanceTo(symmetryAxisPoint2) * 0.5; - } - } - - /// - /// 填充转换器的数据 - /// - class HatchConverterData - { - public List PolyLineData; - public List CircleData; - public List SplineData; - - /// - /// 填充转换器的数据 - /// - public HatchConverterData() - { - PolyLineData = new(); - CircleData = new(); - SplineData = new(); - } - } - #endregion - - #region 成员 - /// - /// 外部只能调用id,否则跨事务造成错误 - /// - public ObjectId OldHatchId - { - get - { - if (_oldHatch is null) - return ObjectId.Null; - return _oldHatch.ObjectId; - } - } - readonly Hatch? _oldHatch; - - readonly List _hcDatas; - /// - /// 生成的填充边界id - /// - public List BoundaryIds; - #endregion - - #region 构造 - /// - /// 填充边界转换器 - /// - HatchConverter() - { - _hcDatas = new(); - BoundaryIds = new(); - } - - /// - /// 填充边界转换器 - /// - /// 需要转化的Hatch对象 - public HatchConverter(Hatch hatch) : this() - { - _oldHatch = hatch; - - // 不能在提取信息的时候进行新建cad图元, - // 否则cad将会提示遗忘释放 - hatch.ForEach(loop => { - var hcData = new HatchConverterData(); - - bool isCurve2d = true; - if (loop.IsPolyline) - { - // 边界是多段线 - HatchLoopIsPolyline(loop, hcData); - isCurve2d = false; - } - else - { - if (loop.Curves.Count == 2)// 1是不可能的,大于2的是曲线 - { - // 边界是曲线,过滤可能是圆形的情况 - var cir = TwoArcFormOneCircle(loop); - if (cir is not null) - { - hcData.CircleData.Add(cir); - isCurve2d = false; - } - } - } - - // 边界是曲线 - if (isCurve2d) - HatchLoopIsCurve2d(loop, hcData); - - _hcDatas.Add(hcData); - }); - } - #endregion - - #region 方法 - /// - /// 多段线处理 - /// - /// 填充边界 - /// 收集图元信息 - static void HatchLoopIsPolyline(HatchLoop loop, HatchConverterData hcData) - { - if (loop is null) - throw new ArgumentNullException(nameof(loop)); - - if (hcData is null) - throw new ArgumentNullException(nameof(hcData)); - - // 判断为圆形: - // 上下两个圆弧,然后填充,就会生成此种填充 - // 顶点数是3,凸度是半圆,两个半圆就是一个圆形 - if (loop.Polyline.Count == 3 && loop.Polyline[0].Bulge == 1 && loop.Polyline[1].Bulge == 1 || - loop.Polyline.Count == 3 && loop.Polyline[0].Bulge == -1 && loop.Polyline[1].Bulge == -1) - { - hcData.CircleData.Add(new CircleData(loop.Polyline[0].Vertex, loop.Polyline[1].Vertex)); - } - else - { - // 遍历多段线信息 - var bvc = loop.Polyline; - for (int i = 0; i < bvc.Count; i++) - hcData.PolyLineData.Add(new BulgeVertexWidth(bvc[i])); - } - } - - /// - /// 两个圆弧组成圆形 - /// - /// - /// - static CircleData? TwoArcFormOneCircle(HatchLoop loop) - { - if (loop is null) - throw new ArgumentNullException(nameof(loop)); - - if (loop.Curves.Count != 2) - throw new ArgumentException( - "边界非多段线,而且点数!=2,点数为:" + nameof(loop.Curves.Count) + ";两个矩形交集的时候会出现此情况."); - - CircleData? circular = null; - - // 判断为圆形: - // 用一条(不是两条)多段线画出两条圆弧为正圆,就会生成此种填充 - // 边界为曲线,数量为2,可能是两个半圆曲线,如果是,就加入圆形数据中 - - // 第一段 - var getCurves1Pts = loop.Curves[0].GetSamplePoints(3); // 曲线取样点分两份(3点) - var mid1Pt = getCurves1Pts[1]; // 腰点 - double bulge1 = loop.Curves[0].StartPoint.GetArcBulge(mid1Pt, loop.Curves[0].EndPoint); - - // 第二段 - var getCurves2Pts = loop.Curves[1].GetSamplePoints(3); - var mid2Pt = getCurves2Pts[1]; - double bulge2 = loop.Curves[1].StartPoint.GetArcBulge(mid2Pt, loop.Curves[1].EndPoint); - - // 第一段上弧&&第二段反弧 || 第一段反弧&&第二段上弧 - if (bulge1 == -1 && bulge2 == -1 || bulge1 == 1 && bulge2 == 1) - circular = new CircleData(loop.Curves[0].StartPoint, loop.Curves[1].StartPoint); // 两个起点就是对称点 - - return circular; - } - - /// - /// 处理边界曲线 - /// - /// 填充边界 - /// 收集图元信息 - static void HatchLoopIsCurve2d(HatchLoop loop, HatchConverterData hcData) - { - // 取每一段曲线,曲线可能是直线来的,但是圆弧会按照顶点来分段 - int curveIsClosed = 0; - - // 遍历边界的多个子段 - foreach (Curve2d curve in loop.Curves) - { - // 计数用于实现闭合 - curveIsClosed++; - if (curve is NurbCurve2d spl) - { - // 判断为样条曲线: - hcData.SplineData.Add(spl); - continue; - } - - var pts = curve.GetSamplePoints(3); - var midPt = pts[1]; - if (curve.StartPoint.IsEqualTo(curve.EndPoint, new Tolerance(1e-6, 1e-6)))// 首尾相同,就是圆形 - { - // 判断为圆形: - // 获取起点,然后采样三点,中间就是对称点(直径点) - hcData.CircleData.Add(new CircleData(curve.StartPoint, midPt)); - continue; - } - - // 判断为多段线,圆弧: - double bulge = curve.StartPoint.GetArcBulge(midPt, curve.EndPoint); - hcData.PolyLineData.Add(new BulgeVertexWidth(curve.StartPoint, bulge)); - - // 末尾点,不闭合的情况下就要获取这个 - if (curveIsClosed == loop.Curves.Count) - hcData.PolyLineData.Add(new BulgeVertexWidth(curve.EndPoint, 0)); - } - } - - /// - /// 创建边界图元 - /// - /// 返回图元 - public void CreateBoundaryEntitys(List outEnts) - { - for (int i = 0; i < _hcDatas.Count; i++) - { - var data = _hcDatas[i]; - - // 生成边界:多段线 - if (data.PolyLineData.Count > 0) - { - Polyline pl = new(); - pl.SetDatabaseDefaults(); - for (int j = 0; j < data.PolyLineData.Count; j++) - { - pl.AddVertexAt(j, - data.PolyLineData[j].Vertex, - data.PolyLineData[j].Bulge, - data.PolyLineData[j].StartWidth, - data.PolyLineData[j].EndWidth); - } - outEnts.Add(pl); - } - - // 生成边界:圆 - data.CircleData.ForEach(item => { - outEnts.Add(new Circle(item.Center.Point3d(), Vector3d.ZAxis, item.Radius)); - }); - - // 生成边界:样条曲线 - data.SplineData.ForEach(item => { - outEnts.Add(item.ToCurve()); - }); - } - - if (_oldHatch is not null) - { - outEnts.ForEach(ent => { - ent.Color = _oldHatch.Color; - ent.Layer = _oldHatch.Layer; - }); - } - } - - - /// - /// 创建边界图元和新填充到当前空间 - /// - /// 事务 - /// 数据库 - /// 边界关联 - /// 是否创建填充,false则只创建边界 - /// 新填充id,边界在获取 - public ObjectId CreateBoundarysAndHatchToMsPs(BlockTableRecord btrOfAddEntitySpace, - bool boundaryAssociative = true, - bool createHatchFlag = true, - Transaction? trans = null) - { - // 重设边界之前肯定是有边界才可以 - if (BoundaryIds.Count == 0) - { - List boundaryEntitys = new(); - CreateBoundaryEntitys(boundaryEntitys); - boundaryEntitys.ForEach(ent => { - BoundaryIds.Add(btrOfAddEntitySpace.AddEntity(ent)); - }); - } - - if (!createHatchFlag) - return ObjectId.Null; - /* - * 此处为什么要克隆填充,而不是新建填充? - * 因为填充如果是新建的,那么将会丢失基点,概念如下: - * 两个一样的填充,平移其中一个,那么再提取他们的基点会是一样的! - * 所以生成时候就不等同于画面相同. - * 也因为我不知道什么新建方式可以新建一模一样的填充,因此使用了克隆 - * 那么它的平移后的基点在哪里呢? - */ - - var newHatchId = btrOfAddEntitySpace.DeepCloneEx(new ObjectIdCollection(new ObjectId[] { OldHatchId })).GetValues()[0]; - trans ??= DBTrans.Top.Transaction; - var hatchEnt = trans.GetObject(newHatchId, OpenMode.ForWrite) as Hatch; - if (hatchEnt != null) - { - ResetBoundary(hatchEnt, boundaryAssociative); - hatchEnt.DowngradeOpen(); - } - return newHatchId; - } - - - /// - /// 重设边界 - /// - /// - /// 边界关联 - void ResetBoundary(Hatch hatch, - bool boundaryAssociative = true) - { - // 删除原有边界 - while (hatch.NumberOfLoops != 0) - hatch.RemoveLoopAt(0); - - hatch.Associative = boundaryAssociative; - - var obIds = new ObjectIdCollection(); - for (int i = 0; i < BoundaryIds.Count; i++) - { - obIds.Clear(); - obIds.Add(BoundaryIds[i]); - // 要先添加最外面的边界 - if (i == 0) - hatch.AppendLoop(HatchLoopTypes.Outermost, obIds); - else - hatch.AppendLoop(HatchLoopTypes.Default, obIds); - } - // 计算填充并显示 - hatch.EvaluateHatch(true); - } - #endregion -} \ No newline at end of file diff --git "a/src/IFoxCAD.Cad/ExtensionMethod/\346\226\260\345\273\272\345\241\253\345\205\205/HatchEx.cs" "b/src/IFoxCAD.Cad/ExtensionMethod/\346\226\260\345\273\272\345\241\253\345\205\205/HatchEx.cs" deleted file mode 100644 index 8bfd043..0000000 --- "a/src/IFoxCAD.Cad/ExtensionMethod/\346\226\260\345\273\272\345\241\253\345\205\205/HatchEx.cs" +++ /dev/null @@ -1,15 +0,0 @@ -namespace IFoxCAD.Cad; - -public static class HatchEx -{ - /// - /// 遍历填充每条边 - /// - /// - /// - public static void ForEach(this Hatch hatch, Action action) - { - for (int i = 0; i < hatch.NumberOfLoops; i++) - action.Invoke(hatch.GetLoopAt(i)); - } -} \ No newline at end of file diff --git "a/src/IFoxCAD.Cad/ExtensionMethod/\346\226\260\345\273\272\345\241\253\345\205\205/HatchInfo.cs" "b/src/IFoxCAD.Cad/ExtensionMethod/\346\226\260\345\273\272\345\241\253\345\205\205/HatchInfo.cs" deleted file mode 100644 index d753460..0000000 --- "a/src/IFoxCAD.Cad/ExtensionMethod/\346\226\260\345\273\272\345\241\253\345\205\205/HatchInfo.cs" +++ /dev/null @@ -1,354 +0,0 @@ -namespace IFoxCAD.Cad; - -/* - * 添加的第一个边界必须是外边界,即用于定义图案填充最外面的边界。 - * 要添加外部边界,请使用添加环的类型为 HatchLoopTypes.Outermost 常量的 AppendLoop 方法, - * 一旦外边界被定义,就可以继续添加另外的边界。 - * 添加内部边界请使用带 HatchLoopTypes.Default 常量的 AppendLoop 方法。 - * - * 多个外边界的时候,添加的是(外边界,外边界,外边界,普通边界....) - * 多个外边界的时候,添加的是(外边界,普通边界.....外边界,普通边界....) - */ - -/// -/// 图案填充 -/// -public class HatchInfo -{ - #region 成员 - /// - /// 边界id(最外面放第一) - /// - readonly List _boundaryIds; - /// - /// 填充图元 - /// - readonly Hatch _hatch; - /// - /// 边界关联(此处不能直接=>给填充成员,因为它会加入反应器) - /// - readonly bool _boundaryAssociative; - /// - /// 填充的名称:用户定义(固定名称)/渐变/填充依据定义文件 - /// - string? _hatchName; - /// - /// 填充模式类型(预定义/用户定义/自定义) - /// - HatchPatternType _patternTypeHatch; - /// - /// 渐变模式类型 - /// - GradientPatternType _patternTypeGradient; - /// - /// 比例/间距 - /// - double Scale => _hatch.PatternScale; - /// - /// 角度 - /// - double Angle => _hatch.PatternAngle; - #endregion - - #region 构造 - HatchInfo() - { - _hatch = new Hatch(); - _hatch.SetDatabaseDefaults(); - _boundaryIds = new(); - } - - /// - /// 图案填充 - /// - /// 关联边界 - /// 填充原点 - /// 比例 - /// 角度 - public HatchInfo(bool boundaryAssociative = true, - Point2d? hatchOrigin = null, - double hatchScale = 1, - double hatchAngle = 0) : this() - { - if (hatchScale <= 0) - throw new ArgumentException("填充比例不允许小于等于0"); - - _hatch.PatternScale = hatchScale;// 填充比例 - _hatch.PatternAngle = hatchAngle;// 填充角度 - _boundaryAssociative = boundaryAssociative; - - hatchOrigin ??= Point2d.Origin; - _hatch.Origin = hatchOrigin.Value; // 填充原点 - } - - /// - /// 图案填充 - /// - /// 边界 - /// 关联边界 - /// 填充原点 - /// 比例 - /// 角度 - public HatchInfo(IEnumerable boundaryIds, - bool boundaryAssociative = true, - Point2d? hatchOrigin = null, - double hatchScale = 1, - double hatchAngle = 0) - : this(boundaryAssociative, hatchOrigin, hatchScale, hatchAngle) - { - _boundaryIds.AddRange(boundaryIds); - } - - #endregion - - #region 方法 - /// - /// 模式1:预定义 - /// - public HatchInfo Mode1PreDefined(string name) - { - _hatchName = name; - _hatch.HatchObjectType = HatchObjectType.HatchObject; // 对象类型(填充/渐变) - _patternTypeHatch = HatchPatternType.PreDefined; - return this; - } - - /// - /// 模式2:用户定义 - /// - /// 是否双向 - public HatchInfo Mode2UserDefined(bool patternDouble = true) - { - _hatchName = "_USER"; - _hatch.HatchObjectType = HatchObjectType.HatchObject; // 对象类型(填充/渐变) - _patternTypeHatch = HatchPatternType.UserDefined; - - _hatch.PatternDouble = patternDouble; // 是否双向(必须写在 SetHatchPattern 之前) - _hatch.PatternSpace = Scale; // 间距(必须写在 SetHatchPattern 之前) - return this; - } - - /// - /// 模式3:自定义 - /// - /// - public HatchInfo Mode3UserDefined(string name) - { - _hatchName = name; - _hatch.HatchObjectType = HatchObjectType.HatchObject; // 对象类型(填充/渐变) - _patternTypeHatch = HatchPatternType.CustomDefined; - return this; - } - - /// - /// 模式4:渐变填充 - /// - /// 渐变填充名称 - /// 渐变色起始颜色 - /// 渐变色结束颜色 - /// 渐变移动 - /// 色调值 - /// 单色双色 - public HatchInfo Mode4Gradient(GradientName name, Color colorStart, Color colorEnd, - float gradientShift = 0, - float shadeTintValue = 0, - bool gradientOneColorMode = false) - { - // entget渐变的名字必然是"SOLID",但是这里作为"渐变"名,而不是"填充"名 - _hatchName = name.ToString(); - _hatch.HatchObjectType = HatchObjectType.GradientObject; // 对象类型(填充/渐变) - _patternTypeGradient = GradientPatternType.PreDefinedGradient;// 模式4:渐变 - // _patternTypeGradient = GradientPatternType.UserDefinedGradient;// 模式5:渐变..这种模式干啥用呢 - - // 设置渐变色填充的起始和结束颜色 - var gColor1 = new GradientColor(colorStart, 0); - var gColor2 = new GradientColor(colorEnd, 1); - _hatch.SetGradientColors(new GradientColor[] { gColor1, gColor2 }); - - _hatch.GradientShift = gradientShift; // 梯度位移 - _hatch.ShadeTintValue = shadeTintValue; // 阴影色值 - _hatch.GradientOneColorMode = gradientOneColorMode;// 渐变单色/双色 - _hatch.GradientAngle = Angle; // 渐变角度 - - return this; - } - - /// - /// 构建 - /// - /// 将填充加入此空间 - public ObjectId Build(BlockTableRecord btrOfAddEntitySpace) - { - // 加入数据库 - var hatchId = btrOfAddEntitySpace.AddEntity(_hatch); - - // 设置模式:渐变/填充 - if (_hatch.HatchObjectType == HatchObjectType.GradientObject) - _hatch.SetGradient(_patternTypeGradient, _hatchName); - else - _hatch.SetHatchPattern(_patternTypeHatch, _hatchName); - - // 关联边界,如果不先添加数据库空间内就会出错 - // 为 true 会加入反应器,因此比较慢(二维码将会十几秒才生成好),视需求而定. - _hatch.Associative = _boundaryAssociative; - - // 利用 AppendLoop 重载加入,这里就不处理 - if (_boundaryIds.Count > 0) - AppendLoop(_boundaryIds, HatchLoopTypes.Default); - - // 计算填充并显示(若边界出错,这句会异常) - _hatch.EvaluateHatch(true); - - return hatchId; - } - - /// - /// 执行图元的属性修改 - /// - /// 扔出填充实体 - public HatchInfo Action(Action action) - { - action(_hatch); - return this; - } - - /// - /// 清空边界集合 - /// - public HatchInfo ClearBoundary() - { - _boundaryIds.Clear(); - return this; - } - - /// - /// 删除边界图元 - /// - public HatchInfo EraseBoundary() - { - for (int i = 0; i < _boundaryIds.Count; i++) - _boundaryIds[i].Erase(); - return this; - } - - /// - /// 加入边界 - /// - /// 边界id - /// 加入方式 - void AppendLoop(IEnumerable boundaryIds, - HatchLoopTypes hatchLoopTypes = HatchLoopTypes.Default) - { - var obIds = new ObjectIdCollection(); - // 边界是闭合的,而且已经加入数据库 - // 填充闭合环类型.最外面 - foreach (var border in boundaryIds) - { - obIds.Clear(); - obIds.Add(border); - _hatch.AppendLoop(hatchLoopTypes, obIds); - } - obIds.Dispose(); - } - - /// - /// 加入边界(仿高版本的填充函数) - /// - /// 点集 - /// 凸度集 - /// 加入此空间 - /// 加入方式 - /// - public HatchInfo AppendLoop(Point2dCollection pts, - DoubleCollection bluges, - BlockTableRecord btrOfAddEntitySpace, - HatchLoopTypes hatchLoopTypes = HatchLoopTypes.Default) - { - if (pts == null) - throw new ArgumentNullException(nameof(pts)); - - var ptsEnd2End = pts.End2End(); -#if NET35 - _boundaryIds.Add(CreateAddBoundary(ptsEnd2End, bluges, btrOfAddEntitySpace)); -#else - // 2011新增API,可以不生成图元的情况下加入边界, - // 通过这里进入的话,边界 _boundaryIds 是空的,那么 Build() 时候就需要过滤空的 - _hatch.AppendLoop(hatchLoopTypes, ptsEnd2End, bluges); -#endif - return this; - } - -#if NET35 - /// - /// 通过点集和凸度生成边界的多段线 - /// - /// 点集 - /// 凸度集 - /// 加入此空间 - /// 多段线id - static ObjectId CreateAddBoundary(Point2dCollection? pts, - DoubleCollection? bluges, - BlockTableRecord btrOfAddEntitySpace) - { - if (pts is null) - throw new ArgumentException(null, nameof(pts)); - if (bluges is null) - throw new ArgumentException(null, nameof(bluges)); - - var bvws = new List(); - - var itor1 = pts.GetEnumerator(); - var itor2 = bluges.GetEnumerator(); - while (itor1.MoveNext() && itor2.MoveNext()) - bvws.Add(new BulgeVertexWidth(itor1.Current, itor2.Current)); - - return btrOfAddEntitySpace.AddPline(bvws); - } -#endif - #endregion - - #region 枚举 - /// - /// 渐变色填充的图案名称 - /// - public enum GradientName - { - /// - /// 线状渐变 - /// - Linear, - /// - /// 圆柱状渐变 - /// - Cylinder, - /// - /// 反圆柱状渐变 - /// - Invcylinder, - /// - /// 球状渐变 - /// - Spherical, - /// - /// 反球状渐变 - /// - Invspherical, - /// - /// 半球状渐变 - /// - Hemisperical, - /// - /// 反半球状渐变 - /// - InvHemisperical, - /// - /// 抛物面状渐变 - /// - Curved, - /// - /// 反抛物面状渐变 - /// - Incurved - } - #endregion -} \ No newline at end of file diff --git "a/src/IFoxCAD.Cad/ExtensionMethod/\346\226\260\345\273\272\346\226\207\345\255\227/AttachmentPointHelper.cs" "b/src/IFoxCAD.Cad/ExtensionMethod/\346\226\260\345\273\272\346\226\207\345\255\227/AttachmentPointHelper.cs" deleted file mode 100644 index e630a51..0000000 --- "a/src/IFoxCAD.Cad/ExtensionMethod/\346\226\260\345\273\272\346\226\207\345\255\227/AttachmentPointHelper.cs" +++ /dev/null @@ -1,95 +0,0 @@ -namespace IFoxCAD.Cad; - -public static class AttachmentPointHelper -{ - static readonly Dictionary _alignment = new() - { - { "左上", AttachmentPoint.TopLeft }, - { "中上", AttachmentPoint.TopCenter },// 单行的对齐 - { "右上", AttachmentPoint.TopRight }, - - { "左中", AttachmentPoint.MiddleLeft }, - { "正中", AttachmentPoint.MiddleCenter },// 多行的正中 - { "右中", AttachmentPoint.MiddleRight }, - - { "左对齐", AttachmentPoint.BaseLeft },// ※优先(放在前面优先获取) - { "左", AttachmentPoint.BaseLeft }, - - { "中间", AttachmentPoint.BaseMid }, - - { "右对齐", AttachmentPoint.BaseRight },// ※优先(放在前面优先获取) - { "右", AttachmentPoint.BaseRight }, - - { "左下", AttachmentPoint.BottomLeft }, - { "中下", AttachmentPoint.BottomCenter }, - { "右下", AttachmentPoint.BottomRight }, - - { "对齐", AttachmentPoint.BaseAlign },// ※优先(放在前面优先获取) - { "调整", AttachmentPoint.BaseAlign }, - - { "居中", AttachmentPoint.BaseCenter },// 单行的中 - { "铺满", AttachmentPoint.BaseFit }, - }; - - /// - /// 输入文字获得对齐方式 - /// - /// - /// - public static AttachmentPoint Get(string key) - { - return _alignment[key]; - } - - /// - /// 输入对齐方式获得文字 - /// - /// - /// - public static string Get(AttachmentPoint value) - { - return _alignment.FirstOrDefault(q => q.Value == value).Key; - } -} - -#if false -// 反射描述 -// 这些东西cad没有用到啊...所以不纳入了 -public enum AttachmentPoint2 -{ - [Description("下对齐")] - BottomAlign = 14, - [Description("中对齐")] - MiddleAlign = 15,// 0xF - [Description("上对齐")] - TopAlign = 16,// 0x10 - [Description("下铺满")] - BottomFit = 18, - [Description("中铺满")] - MiddleFit = 19, - [Description("上铺满")] - TopFit = 20, - [Description("下居中")] - BottomMid = 22, - [Description("中居中")] - MiddleMid = 23, - [Description("下居中")] - TopMid = 24, -} - -public static Dictionary GetEnumDic(Type enumType) -{ - Dictionary dic = new(); - var fieldinfos = enumType.GetFields(); - for (int i = 0; i < fieldinfos.Length; i++) - { - var field = fieldinfos[i]; - if (field.FieldType.IsEnum) - { - var objs = field.GetCustomAttributes(typeof(DescriptionAttribute), false); - dic.Add(field.Name, ((DescriptionAttribute)objs[0]).Description); - } - } - return dic; -} -#endif diff --git "a/src/IFoxCAD.Cad/ExtensionMethod/\346\226\260\345\273\272\346\226\207\345\255\227/TextEntityAdd.cs" "b/src/IFoxCAD.Cad/ExtensionMethod/\346\226\260\345\273\272\346\226\207\345\255\227/TextEntityAdd.cs" deleted file mode 100644 index 5b37a31..0000000 --- "a/src/IFoxCAD.Cad/ExtensionMethod/\346\226\260\345\273\272\346\226\207\345\255\227/TextEntityAdd.cs" +++ /dev/null @@ -1,62 +0,0 @@ -namespace IFoxCAD.Cad; - -public static partial class EntityAdd -{ - /// - /// 创建单行文字 - /// - /// 数据库 - /// 内容 - /// 插入点 - /// 字体高度 - /// 文字样式 - /// 对齐方式 - /// 对齐点,因样式 可能无效 - /// - public static Entity AddDBTextToEntity(this Database db, - string textContents, - Point3d position, - double textHigh = 2.5, - ObjectId? textStyleId = null, - AttachmentPoint justify = AttachmentPoint.BaseLeft, - Point3d? justifyPoint = null) - { - var TextInfo = new TextInfo( - textContents, - position, - justify, - justifyPoint, - textStyleId, - textHigh, - db); - return TextInfo.AddDBTextToEntity(); - } - - /// - /// 新建多行文字 - /// - /// 数据库 - /// 内容 - /// 插入点 - /// 字体高度 - /// 文字样式 - /// 对齐方式 - /// - public static Entity AddMTextToEntity(this Database db, - string textContents, - Point3d position, - double textHigh = 2.5, - ObjectId? textStyleId = null, - AttachmentPoint justify = AttachmentPoint.BaseLeft) - { - var TextInfo = new TextInfo( - textContents, - position, - justify, - null, - textStyleId, - textHigh, - db); - return TextInfo.AddMTextToEntity(); - } -} diff --git "a/src/IFoxCAD.Cad/ExtensionMethod/\346\226\260\345\273\272\346\226\207\345\255\227/TextInfo.cs" "b/src/IFoxCAD.Cad/ExtensionMethod/\346\226\260\345\273\272\346\226\207\345\255\227/TextInfo.cs" deleted file mode 100644 index d8bfd7a..0000000 --- "a/src/IFoxCAD.Cad/ExtensionMethod/\346\226\260\345\273\272\346\226\207\345\255\227/TextInfo.cs" +++ /dev/null @@ -1,178 +0,0 @@ -namespace IFoxCAD.Cad; - -/// -/// 文字信息类 -/// -public class TextInfo -{ - readonly Database? Database; - readonly string? Contents; - readonly Point3d Position; - - public string TextJustifyCn => AttachmentPointHelper.Get(TextJustify); - readonly AttachmentPoint TextJustify; - readonly Point3d? AlignmentPoint; - - readonly double TextHeight; - readonly ObjectId? TextStyleId; - - /// - /// 文字信息类 - /// - /// 内容 - /// 基点 - /// 对齐方式 - /// 对齐点(对齐方式是左,此参数无效,为null不为左就报错) - /// 文字样式id - /// 文字高度 - /// 数据库 - public TextInfo(string? contents, - Point3d position, - AttachmentPoint justify, - Point3d? justifyPoint = null, - ObjectId? textStyleId = null, - double textHeight = 2.5, - Database? database = null) - { - Contents = contents; - Position = position; - TextJustify = justify; - - if (justifyPoint is null && TextJustify != AttachmentPoint.BaseLeft) - throw new ArgumentNullException(nameof(justifyPoint)); - - AlignmentPoint = justifyPoint; - TextHeight = textHeight; - TextStyleId = textStyleId; - Database = database; - } - - /// - /// 创建单行文字 - /// - public DBText AddDBTextToEntity() - { - if (string.IsNullOrEmpty(Contents)) - throw new ArgumentNullException(nameof(Contents) + "创建文字无内容"); - - var acText = new DBText(); - acText.SetDatabaseDefaults(); - - if (Database is not null) - acText.SetDatabaseDefaults(Database);// 我的默认值是填满的,所以可以不需要 - - if (TextStyleId is not null) - acText.SetTextStyleId(TextStyleId.Value); - - acText.Height = TextHeight; // 高度 - acText.TextString = Contents; // 内容 - acText.Position = Position; // 插入点(一定要先设置) - acText.Justify = TextJustify; // 使他们对齐 - // acText.HorizontalMode - - if (AlignmentPoint is not null) - acText.AlignmentPoint = AlignmentPoint.Value; - else if (acText.Justify != AttachmentPoint.BaseLeft) - acText.AlignmentPoint = Position; - - if (Database is not null) - acText.AdjustAlignment(Database); - return acText; - } - - /// - /// 创建多行文字 - /// - /// - public MText AddMTextToEntity() - { - if (string.IsNullOrEmpty(Contents)) - throw new ArgumentNullException(nameof(Contents) + "创建文字无内容"); - - var mText = new MText(); - mText.SetDatabaseDefaults(); - - if (Database is not null) - mText.SetDatabaseDefaults(Database); - - if (TextStyleId is not null) - mText.SetTextStyleId(TextStyleId.Value); - - mText.TextHeight = TextHeight; // 高度 - mText.Contents = Contents; // 内容 - mText.Location = Position; // 插入点(一定要先设置) - - // mText.SetAttachmentMovingLocation(TextJustify); - mText.Attachment = TextJustify;// 使他们对齐 - - return mText; - } -} - -// 反射设定对象的文字样式id -public static partial class TextInfoHelper -{ - /// - /// 设置文字样式id - /// - /// 单行文字 - /// 文字样式表记录id - public static void SetTextStyleId(this DBText acText, ObjectId ltrObjectId) - { - SetEntityTxtStyleId(acText, ltrObjectId); - } - - /// - /// 设置文字样式id - /// - /// 多行文字 - /// 文字样式表记录id - public static void SetTextStyleId(this MText acText, ObjectId ltrObjectId) - { - SetEntityTxtStyleId(acText, ltrObjectId); - } - - static void SetEntityTxtStyleId(Entity acText, ObjectId ltrObjectId) - { - GetTextStyleIdType(acText)?.SetValue(acText, ltrObjectId, null); - } - - /// - /// 获取文字样式id - /// - public static ObjectId GetTextStyleId(this DBText acText) - { - return GetEntityTxtStyleId(acText); - } - - /// - /// 获取文字样式id - /// - public static ObjectId GetTextStyleId(this MText acText) - { - return GetEntityTxtStyleId(acText); - } - - static ObjectId GetEntityTxtStyleId(Entity acText) - { - var result = ObjectId.Null; - var id = GetTextStyleIdType(acText)?.GetValue(acText, null); - if (id != null) - result = (ObjectId)id; - return result; - } - - static PropertyInfo? _textStyleId = null; - static PropertyInfo GetTextStyleIdType(Entity acText) - { - if (_textStyleId == null) - { - var entType = acText.GetType(); - var prs = entType.GetProperties(); - _textStyleId = prs.FirstOrDefault(a => a.Name == "TextStyle");// 反射获取属性 - if (_textStyleId == null) - _textStyleId = prs.FirstOrDefault(a => a.Name == "TextStyleId");// 反射获取属性 - } - return _textStyleId; - } -} diff --git a/src/IFoxCAD.Cad/GlobalUsings.cs b/src/IFoxCAD.Cad/GlobalUsings.cs deleted file mode 100644 index 2430170..0000000 --- a/src/IFoxCAD.Cad/GlobalUsings.cs +++ /dev/null @@ -1,45 +0,0 @@ -/// 系统引用 -global using System; -global using System.Collections; -global using System.Collections.Generic; -global using System.IO; -global using System.Linq; -global using System.Text; -global using System.Reflection; -global using System.Text.RegularExpressions; -global using Microsoft.Win32; -global using System.ComponentModel; -global using System.Runtime.InteropServices; -global using System.Collections.Specialized; - -global using Exception = System.Exception; - -global using Registry = Microsoft.Win32.Registry; -global using RegistryKey = Microsoft.Win32.RegistryKey; - -/// autocad 引用 -global using Autodesk.AutoCAD.ApplicationServices; -global using Autodesk.AutoCAD.EditorInput; -global using Autodesk.AutoCAD.Colors; -global using Autodesk.AutoCAD.DatabaseServices; -global using Autodesk.AutoCAD.Geometry; -global using Autodesk.AutoCAD.Runtime; -global using Acap = Autodesk.AutoCAD.ApplicationServices.Application; -global using Acgi = Autodesk.AutoCAD.GraphicsInterface; - -global using Autodesk.AutoCAD.DatabaseServices.Filters; -global using Autodesk.AutoCAD; - -// jig命名空间会引起Viewport/Polyline等等重义,最好逐个引入 using Autodesk.AutoCAD.GraphicsInterface -global using WorldDraw = Autodesk.AutoCAD.GraphicsInterface.WorldDraw; -global using Manager = Autodesk.AutoCAD.GraphicsSystem.Manager; -global using Group = Autodesk.AutoCAD.DatabaseServices.Group; -global using Viewport = Autodesk.AutoCAD.DatabaseServices.Viewport; -global using Autodesk.AutoCAD.GraphicsInterface; -global using Polyline = Autodesk.AutoCAD.DatabaseServices.Polyline; -global using Acad_DwgFiler = Autodesk.AutoCAD.DatabaseServices.DwgFiler; -global using Acad_DxfFiler = Autodesk.AutoCAD.DatabaseServices.DxfFiler; -global using Acad_ErrorStatus = Autodesk.AutoCAD.Runtime.ErrorStatus; - -/// ifoxcad.basal 引用 -global using IFoxCAD.Basal; \ No newline at end of file diff --git a/src/IFoxCAD.Cad/IFoxCAD.Cad.csproj b/src/IFoxCAD.Cad/IFoxCAD.Cad.csproj deleted file mode 100644 index 99b5567..0000000 --- a/src/IFoxCAD.Cad/IFoxCAD.Cad.csproj +++ /dev/null @@ -1,65 +0,0 @@ - - - NET35;NET40;NET45 - true - enable - true - true - 0.4 - InspireFunction - xsfhlzh;vicwjb;liuqihong - 基于.NET的Cad二次开发类库 - InspireFunction - https://gitee.com/inspirefunction/ifoxcad - https://gitee.com/inspirefunction/ifoxcad.git - git - IFoxCAD;CAD;AutoCad;C#;NET - 增加很多东西. - true - true - preview - true - LICENSE - true - x64 - - - - - - - - - - - - - - - - DEBUG - - - $(Configuration);ac2009 - - - $(Configuration);ac2013 - - - $(Configuration);ac2015 - - - - True - - - - - - - - - - \ No newline at end of file diff --git a/src/IFoxCAD.Cad/ResultData/LispDottedPair.cs b/src/IFoxCAD.Cad/ResultData/LispDottedPair.cs deleted file mode 100644 index 009d13e..0000000 --- a/src/IFoxCAD.Cad/ResultData/LispDottedPair.cs +++ /dev/null @@ -1,68 +0,0 @@ -namespace IFoxCAD.Cad; - -/// -/// lisp点对表的数据封装类 -/// -public class LispDottedPair : LispList -{ - #region 构造函数 - /// - /// 默认无参构造函数 - /// - public LispDottedPair() - { - } - - /// - /// 构造函数 - /// - /// TypedValue 迭代器 - public LispDottedPair(IEnumerable values) : base(values) - { - } - /// - /// 构造函数 - /// - /// 点对表左数 - /// 点对表右数 - public LispDottedPair(TypedValue left, TypedValue right) - { - Add(left); - Add(right); - } - #endregion - - #region 重写 - /// - /// 点对表的值 - /// - public override List Value - { - get - { - var value = new List - { - new TypedValue((int)LispDataType.ListBegin,-1), - new TypedValue((int)LispDataType.DottedPair,-1) - }; - value.InsertRange(1, this); - return value; - } - } - #endregion - - #region 转换器 - - /// - /// LispDottedPair 隐式转换到 TypedValue 数组 - /// - /// TypedValueList 实例 - public static implicit operator TypedValue[](LispDottedPair values) => values.Value.ToArray(); - /// - /// LispDottedPair 隐式转换到 ResultBuffer - /// - /// TypedValueList 实例 - public static implicit operator ResultBuffer(LispDottedPair values) => new(values.Value.ToArray()); - - #endregion -} diff --git a/src/IFoxCAD.Cad/ResultData/LispList.cs b/src/IFoxCAD.Cad/ResultData/LispList.cs deleted file mode 100644 index 8c72669..0000000 --- a/src/IFoxCAD.Cad/ResultData/LispList.cs +++ /dev/null @@ -1,195 +0,0 @@ -namespace IFoxCAD.Cad; - -/// -/// lisp数据封装类 -/// -public class LispList : TypedValueList -{ - #region 构造函数 - /// - /// 默认构造函数 - /// - public LispList() { } - - /// - /// 构造函数 - /// - /// TypedValue 迭代器 - public LispList(IEnumerable values) : base(values) { } - #endregion - - #region 重写 - /// - /// lisp 列表的值 - /// - public virtual List Value - { - get - { - var value = new List - { - new TypedValue((int)LispDataType.ListBegin,-1), - new TypedValue((int)LispDataType.ListEnd,-1) - }; - value.InsertRange(1, this); - return value; - } - } - #endregion - - #region 添加数据 - /// - /// 添加数据 - /// - /// 组码 - /// 组码值 - public override void Add(int code, object? obj) - { - if (code < 5000) - { - throw new System.Exception("传入的组码值不是 lisp数据 有效范围!"); - } - Add(new TypedValue(code, obj)); - } - - /// - /// 添加数据 - /// - /// dxfcode枚举值 - /// 组码值 - public void Add(LispDataType code, object? obj) - { - Add((int)code, obj); - } - /// - /// 添加数据,参数为true时添加 lisp 中的 T,false时添加 lisp 中的 nil - /// - /// bool 型的数据 - public void Add(bool value) - { - if (value) - { - Add(LispDataType.T_atom, true); - } - else - { - Add(LispDataType.Nil, null); - } - } - /// - /// 添加字符串 - /// - /// 字符串 - public void Add(string value) - { - Add(LispDataType.Text, value); - } - /// - /// 添加短整型数 - /// - /// 短整型数 - public void Add(short value) - { - Add(LispDataType.Int16, value); - } - /// - /// 添加整型数 - /// - /// 整型数 - public void Add(int value) - { - Add(LispDataType.Int32, value); - } - /// - /// 添加浮点数 - /// - /// 浮点数 - public void Add(double value) - { - Add(LispDataType.Double, value); - } - /// - /// 添加对象id - /// - /// 对象id - public void Add(ObjectId value) - { - Add(LispDataType.ObjectId, value); - } - /// - /// 添加选择集 - /// - /// 选择集 - public void Add(SelectionSet value) - { - Add(LispDataType.SelectionSet, value); - } - /// - /// 添加二维点 - /// - /// 二维点 - public void Add(Point2d value) - { - Add(LispDataType.Point2d, value); - } - /// - /// 添加三维点 - /// - /// 三维点 - public void Add(Point3d value) - { - Add(LispDataType.Point3d, value); - } - /// - /// 添加二维点 - /// - /// X - /// Y - public void Add(double x, double y) - { - Add(LispDataType.Point2d, new Point2d(x, y)); - } - /// - /// 添加三维点 - /// - /// X - /// Y - /// Z - public void Add(double x, double y, double z) - { - Add(LispDataType.Point3d, new Point3d(x, y, z)); - } - /// - /// 添加列表 - /// - /// lisp 列表 - public void Add(LispList value) - { - this.AddRange(value.Value); - } - - #endregion - - #region 转换器 - /// - /// ResultBuffer 隐式转换到 LispList - /// - /// ResultBuffer 实例 - public static implicit operator LispList(ResultBuffer buffer) => new(buffer.AsArray()); - /// - /// LispList 隐式转换到 TypedValue 数组 - /// - /// TypedValueList 实例 - public static implicit operator TypedValue[](LispList values) => values.Value.ToArray(); - /// - /// LispList 隐式转换到 ResultBuffer - /// - /// TypedValueList 实例 - public static implicit operator ResultBuffer(LispList values) => new(values.Value.ToArray()); - /// - /// TypedValue 数组隐式转换到 LispList - /// - /// TypedValue 数组 - public static implicit operator LispList(TypedValue[] values) => new(values); - #endregion -} diff --git a/src/IFoxCAD.Cad/ResultData/TypedValueList.cs b/src/IFoxCAD.Cad/ResultData/TypedValueList.cs deleted file mode 100644 index 896a226..0000000 --- a/src/IFoxCAD.Cad/ResultData/TypedValueList.cs +++ /dev/null @@ -1,63 +0,0 @@ -namespace IFoxCAD.Cad; - -/// -/// 用于集中管理扩展数据/扩展字典/resultbuffer的类 -/// -public class TypedValueList : List -{ - #region 构造函数 - /// - /// 默认无参构造函数 - /// - public TypedValueList() { } - /// - /// 采用 TypedValue 迭代器构造 TypedValueList - /// - /// - public TypedValueList(IEnumerable values) : base(values) { } - #endregion - - #region 添加数据 - /// - /// 添加数据 - /// - /// 组码 - /// 组码值 - public virtual void Add(int code, object obj) - { - Add(new TypedValue(code, obj)); - } - - #endregion - - #region 转换器 - /// - /// ResultBuffer 隐式转换到 TypedValueList - /// - /// ResultBuffer 实例 - public static implicit operator TypedValueList(ResultBuffer buffer) => new(buffer.AsArray()); - /// - /// TypedValueList 隐式转换到 TypedValue 数组 - /// - /// TypedValueList 实例 - public static implicit operator TypedValue[](TypedValueList values) => values.ToArray(); - /// - /// TypedValueList 隐式转换到 ResultBuffer - /// - /// TypedValueList 实例 - public static implicit operator ResultBuffer(TypedValueList values) => new(values); - /// - /// TypedValue 数组隐式转换到 TypedValueList - /// - /// TypedValue 数组 - public static implicit operator TypedValueList(TypedValue[] values) => new(values); - /// - /// 转换为字符串 - /// - /// ResultBuffer 字符串 - public override string ToString() - { - return new ResultBuffer(this).ToString(); - } - #endregion -} diff --git a/src/IFoxCAD.Cad/ResultData/XRecordDataList.cs b/src/IFoxCAD.Cad/ResultData/XRecordDataList.cs deleted file mode 100644 index 1fb19d8..0000000 --- a/src/IFoxCAD.Cad/ResultData/XRecordDataList.cs +++ /dev/null @@ -1,68 +0,0 @@ -namespace IFoxCAD.Cad; - -/// -/// 扩展字典数据封装类 -/// -public class XRecordDataList : TypedValueList -{ - #region 构造函数 - /// - /// 扩展字典数据封装类 - /// - public XRecordDataList() { } - - /// - /// 扩展字典数据封装类 - /// - public XRecordDataList(IEnumerable values) : base(values) { } - #endregion - - #region 添加数据 - /// - /// 添加数据 - /// - /// 组码 - /// 组码值 - public override void Add(int code, object obj) - { - if (code >= 1000) - { - throw new System.Exception("传入的组码值不是 XRecordData 有效范围!"); - } - Add(new TypedValue(code, obj)); - } - - /// - /// 添加数据 - /// - /// dxfcode枚举值 - /// 组码值 - public void Add(DxfCode code, object obj) - { - Add((int)code, obj); - } - #endregion - - #region 转换器 - /// - /// ResultBuffer 隐式转换到 XRecordDataList - /// - /// ResultBuffer 实例 - public static implicit operator XRecordDataList(ResultBuffer buffer) => new(buffer.AsArray()); - /// - /// XRecordDataList 隐式转换到 TypedValue 数组 - /// - /// TypedValueList 实例 - public static implicit operator TypedValue[](XRecordDataList values) => values.ToArray(); - /// - /// XRecordDataList 隐式转换到 ResultBuffer - /// - /// TypedValueList 实例s - public static implicit operator ResultBuffer(XRecordDataList values) => new(values); - /// - /// TypedValue 数组隐式转换到 XRecordDataList - /// - /// TypedValue 数组 - public static implicit operator XRecordDataList(TypedValue[] values) => new(values); - #endregion -} diff --git a/src/IFoxCAD.Cad/ResultData/XdataList.cs b/src/IFoxCAD.Cad/ResultData/XdataList.cs deleted file mode 100644 index a666100..0000000 --- a/src/IFoxCAD.Cad/ResultData/XdataList.cs +++ /dev/null @@ -1,68 +0,0 @@ -namespace IFoxCAD.Cad; - -/// -/// 扩展数据封装类 -/// -public class XDataList : TypedValueList -{ - #region 构造函数 - /// - /// 扩展数据封装类 - /// - public XDataList() { } - - /// - /// 扩展数据封装类 - /// - public XDataList(IEnumerable values) : base(values) { } - #endregion - - #region 添加数据 - /// - /// 添加数据 - /// - /// 组码 - /// 组码值 - public override void Add(int code, object obj) - { - if (code < 1000 || code > 1071) - throw new System.Exception("传入的组码值不是XData有效范围!"); - - Add(new TypedValue(code, obj)); - } - - /// - /// 添加数据 - /// - /// dxfcode枚举值 - /// 组码值 - public void Add(DxfCode code, object obj) - { - Add((int)code, obj); - } - - #endregion - - #region 转换器 - /// - /// ResultBuffer 隐式转换到 XDataList - /// - /// ResultBuffer 实例 - public static implicit operator XDataList(ResultBuffer buffer) => new(buffer.AsArray()); - /// - /// XDataList 隐式转换到 TypedValue 数组 - /// - /// TypedValueList 实例 - public static implicit operator TypedValue[](XDataList values) => values.ToArray(); - /// - /// XDataList 隐式转换到 ResultBuffer - /// - /// TypedValueList 实例 - public static implicit operator ResultBuffer(XDataList values) => new(values); - /// - /// TypedValue 数组隐式转换到 XDataList - /// - /// TypedValue 数组 - public static implicit operator XDataList(TypedValue[] values) => new(values); - #endregion -} diff --git a/src/IFoxCAD.Cad/Runtime/AOP.cs b/src/IFoxCAD.Cad/Runtime/AOP.cs deleted file mode 100644 index 4bb5cb3..0000000 --- a/src/IFoxCAD.Cad/Runtime/AOP.cs +++ /dev/null @@ -1,99 +0,0 @@ -// namespace IFoxCAD.Cad; -// using HarmonyLib; - -// public class IFoxRefuseInjectionTransaction : Attribute -// { -// /// -// /// 拒绝注入事务 -// /// -// public IFoxRefuseInjectionTransaction() -// { -// } -// } - -// public class AOP -// { -// /// -// /// 在此命名空间下的命令末尾注入清空事务栈函数 -// /// -// public static void Run(string nameSpace) -// { -// Dictionary cmdDic = new(); -// AutoClass.AppDomainGetTypes(type => { -// if (type.Namespace != nameSpace) -// return; -// // 类上面特性 -// if (type.IsClass) -// { -// var attr = type.GetCustomAttributes(true); -// if (RefuseInjectionTransaction(attr)) -// return; -// } - -// // 函数上面特性 -// var mets = type.GetMethods();// 获得它的成员函数 -// for (int ii = 0; ii < mets.Length; ii++) -// { -// var method = mets[ii]; -// // 找到特性,特性下面的方法要是Public,否则就被编译器优化掉了. -// var attr = method.GetCustomAttributes(true); -// for (int jj = 0; jj < attr.Length; jj++) -// if (attr[jj] is CommandMethodAttribute cmdAtt) -// { -// if (!RefuseInjectionTransaction(attr)) -// cmdDic.Add(cmdAtt.GlobalName, (cmdAtt, type, method)); -// } -// } -// }); - -// // 运行的命令写在了Test.dll,当然不是ifox.cad类库内了.... -// if (cmdDic.Count == 0) -// return; - -// var harmony = new Harmony(nameSpace); -// var mPrefix = SymbolExtensions.GetMethodInfo(() => IFoxCmdAddFirst());// 进入函数前 -// var mPostfix = SymbolExtensions.GetMethodInfo(() => IFoxCmdAddLast());// 进入函数后 -// var mp1 = new HarmonyMethod(mPrefix); -// var mp2 = new HarmonyMethod(mPostfix); - -// foreach (var item in cmdDic) -// { -// // 原函数执行(空间type,函数名) -// var mOriginal = AccessTools.Method(item.Value.MetType, item.Value.MetInfo.Name); -// // mOriginal.Invoke(); -// // 新函数执行:创造两个函数加入里面 -// var newMet = harmony.Patch(mOriginal, mp1, mp2); -// // newMet.Invoke(); -// } -// } - -// /// -// /// 拒绝注入事务 -// /// -// /// 属性 -// /// -// private static bool RefuseInjectionTransaction(object[] attr) -// { -// bool refuseInjectionTransaction = false; -// for (int kk = 0; kk < attr.Length; kk++) -// { -// if (attr[kk] is IFoxRefuseInjectionTransaction) -// { -// refuseInjectionTransaction = true; -// break; -// } -// } -// return refuseInjectionTransaction; -// } - -// public static void IFoxCmdAddFirst() -// { -// // 此生命周期会在静态事务栈上面,被无限延长 -// var _ = DBTrans.Top; -// } - -// public static void IFoxCmdAddLast() -// { -// DBTrans.FinishDatabase(); -// } -// } \ No newline at end of file diff --git a/src/IFoxCAD.Cad/Runtime/AcadEMR.cs b/src/IFoxCAD.Cad/Runtime/AcadEMR.cs deleted file mode 100644 index 96bb71d..0000000 --- a/src/IFoxCAD.Cad/Runtime/AcadEMR.cs +++ /dev/null @@ -1,149 +0,0 @@ -#if true -namespace IFoxCAD.Cad; - -using System.Diagnostics; - -// 作者: [VB.net]福萝卜 莱昂纳多·胖子 -// Email:oneeshine@163.com -// QQ: 461884072 -// 测试 2006-2019+ - -/// -/// 去教育版 -/// -/// -internal class AcadEMR -{ - /// - /// 释放库 - /// - /// 句柄 - /// - [DllImport("kernel32.dll", CharSet = CharSet.Auto)] - static extern IntPtr FreeLibrary(IntPtr loadLibraryIntPtr); - - /// - /// 获取一个应用程序或dll的模块句柄,要求已经载入 - /// - /// - /// - [DllImport("kernel32.dll", CharSet = CharSet.Auto, SetLastError = true)] - static extern IntPtr GetModuleHandle(string name); - - /// - /// 获取要引入的函数,将符号名或标识号转换为DLL内部地址 - /// - /// exe/dll句柄 - /// 接口名 - /// - [DllImport("kernel32.dll", SetLastError = true, CharSet = CharSet.Ansi, ExactSpelling = true)] - static extern IntPtr GetProcAddress(IntPtr hModule, string procName); - - /// - /// 虚拟保护 - /// - /// - /// - /// - /// - /// - [DllImport("kernel32.dll", CharSet = CharSet.Auto, SetLastError = true)] - static extern bool VirtualProtect(IntPtr lpAddress, IntPtr dwSize, uint flNewProtect, ref uint lpflOldProtect); - - - /// - /// 移除教育版 - /// - /// 打印出错信息 - public static void Remove(bool echoes = false) - { - var dllName = Env.GetAcapVersionDll(); - IntPtr moduleHandle = GetModuleHandle(dllName); - if (moduleHandle == IntPtr.Zero) - { - if (echoes) - Env.Printl(typeof(AcadEMR).FullName + "." + nameof(Remove) + "找不到模块:" + dllName); - return; - } - - string funcname = System.Text.Encoding.Unicode.GetString(new byte[] { 63 }); - if (IntPtr.Size == 4) - funcname += "isEMR@AcDbDatabase@@QBE_NXZ"; - else - funcname += "isEMR@AcDbDatabase@@QEBA_NXZ"; - - IntPtr funcAdress = GetProcAddress(moduleHandle, funcname); - if (funcAdress == IntPtr.Zero) - { - if (echoes) - Env.Printl("无法找指定函数:" + funcname); - return; - } - - IntPtr ptr; - if (IntPtr.Size == 4) - ptr = new IntPtr(funcAdress.ToInt32() + 3); - else - ptr = new IntPtr(funcAdress.ToInt64() + 4); - - if (!CheckFunc(ref ptr, 51, 2))//08 通过此处 - if (echoes) - Env.Printl("无法验证函数体:0x33"); - IntPtr destPtr = ptr; - - if (!CheckFunc(ref ptr, 57, 6))//08 无法通过此处,所以只是打印提示 - if (echoes) - Env.Printl("无法验证函数体:0x39"); - if (!CheckFunc(ref ptr, 15, 2))//08 无法通过此处,所以只是打印提示 - if (echoes) - Env.Printl("无法验证函数体:0x0F"); - - uint flag = default; - uint tccc = default; - - IntPtr ip100 = new(100); - if (!VirtualProtect(destPtr, ip100, 64, ref flag))// 修改内存权限 - { - if (echoes) - Env.Printl("内存模式修改失败!"); - return; - } - - Marshal.WriteByte(destPtr, 137); - VirtualProtect(destPtr, ip100, flag, ref tccc);// 恢复内存权限 - } - - /// - /// 验证函数体 - /// - /// - /// - /// - /// - static bool CheckFunc(ref IntPtr adress, byte val, int len) - { - if (Marshal.ReadByte(adress) == 233) - { - if (IntPtr.Size == 4) - { - var pass = Marshal.ReadInt32(new IntPtr(adress.ToInt32() + 1)); - adress = new IntPtr(adress.ToInt32() + pass + 5); - } - else - { - var pass = Marshal.ReadInt64(new IntPtr(adress.ToInt64() + 1)); - adress = new IntPtr(adress.ToInt64() + pass + 5); - } - } - if (Marshal.ReadByte(adress) == val) - { - if (IntPtr.Size == 4) - adress = new IntPtr(adress.ToInt32() + len); - else - adress = new IntPtr(adress.ToInt64() + len); - return true; - } - return false; - } -} -#endif \ No newline at end of file diff --git a/src/IFoxCAD.Cad/Runtime/AcadVersion.cs b/src/IFoxCAD.Cad/Runtime/AcadVersion.cs deleted file mode 100644 index 9f776fc..0000000 --- a/src/IFoxCAD.Cad/Runtime/AcadVersion.cs +++ /dev/null @@ -1,70 +0,0 @@ -namespace IFoxCAD.Cad; - -/// -/// cad版本号类 -/// -public static class AcadVersion -{ - private static readonly string _pattern = @"Autodesk\\AutoCAD\\R(\d+)\.(\d+)\\.*?"; - - /// - /// 所有安装的cad的版本号 - /// - public static List Versions - { - get - { - string[] copys = Registry.LocalMachine - .OpenSubKey(@"SOFTWARE\Autodesk\Hardcopy") - .GetValueNames(); - - var _versions = new List(); - foreach (var rootkey in copys) - { - if (!Regex.IsMatch(rootkey, _pattern)) - continue; - - var gs = Regex.Match(rootkey, _pattern).Groups; - var ver = new CadVersion - { - ProductRootKey = rootkey, - ProductName = Registry.LocalMachine - .OpenSubKey("SOFTWARE") - .OpenSubKey(rootkey) - .GetValue("ProductName") - .ToString(), - - Major = int.Parse(gs[1].Value), - Minor = int.Parse(gs[2].Value), - }; - _versions.Add(ver); - } - return _versions; - } - } - - /// 已打开的cad的版本号 - /// 已打开cad的application对象 - /// cad版本号对象 - public static CadVersion? FromApp(object app) - { - if (app == null) - throw new ArgumentNullException(nameof(app)); - - string acver = app.GetType() - .InvokeMember( - "Version", - BindingFlags.GetProperty, - null, - app, - new object[0]).ToString(); - - var gs = Regex.Match(acver, @"(\d+)\.(\d+).*?").Groups; - int major = int.Parse(gs[1].Value); - int minor = int.Parse(gs[2].Value); - foreach (var ver in Versions) - if (ver.Major == major && ver.Minor == minor) - return ver; - return null; - } -} diff --git a/src/IFoxCAD.Cad/Runtime/AssemInfo.cs b/src/IFoxCAD.Cad/Runtime/AssemInfo.cs deleted file mode 100644 index 337ac0d..0000000 --- a/src/IFoxCAD.Cad/Runtime/AssemInfo.cs +++ /dev/null @@ -1,82 +0,0 @@ -namespace IFoxCAD.Cad; - -/// -/// 程序集信息 -/// -[Serializable] -public struct AssemInfo -{ - /// - /// 注册名 - /// - public string Name; - - /// - /// 程序集全名 - /// - public string Fullname; - - /// - /// 程序集路径 - /// - public string Loader; - - /// - /// 加载方式 - /// - public AssemLoadType LoadType; - - /// - /// 程序集说明 - /// - public string Description; -} - - -/// -/// 程序集加载类型 -/// -public enum AssemLoadType -{ - /// - /// 启动 - /// - Startting = 2, - - /// - /// 随命令 - /// - ByCommand = 12, - - /// - /// 无效 - /// - Disabled = 20 -} - - -/// -/// 注册中心配置信息 -/// -public enum AutoRegConfig -{ - /// - /// 注册表 - /// - Regedit = 1, - /// - /// 反射特性 - /// - ReflectionAttribute = 2, - /// - /// 反射接口 - /// - ReflectionInterface = 4, - - /// - /// 移除教育版 - /// - RemoveEMR = 8, - - All = Regedit | ReflectionAttribute | ReflectionInterface | RemoveEMR, -} \ No newline at end of file diff --git a/src/IFoxCAD.Cad/Runtime/AutoRegAssem.cs b/src/IFoxCAD.Cad/Runtime/AutoRegAssem.cs deleted file mode 100644 index fd3bacc..0000000 --- a/src/IFoxCAD.Cad/Runtime/AutoRegAssem.cs +++ /dev/null @@ -1,175 +0,0 @@ -namespace IFoxCAD.Cad; - - -/// -/// 注册中心 -/// -/// 初始化程序集信息写入注册表并反射特性和接口
    -/// 启动cad后的执行顺序为:
    -/// 1:程序集配置中心构造函数
    -/// 2:特性..(多个)
    -/// 3:接口..(多个)
    -///
    -///
    -public abstract class AutoRegAssem : IExtensionApplication -{ - #region 字段 - readonly AutoReflection _autoRef; - readonly AssemInfo _info; - #endregion - - #region 静态方法 - /// - /// 程序集的路径 - /// - public static FileInfo Location => new(Assembly.GetCallingAssembly().Location); - - /// - /// 程序集的目录 - /// - public static DirectoryInfo CurrDirectory => Location.Directory; - - /// - /// 获取程序集的目录 - /// - /// 程序集 - /// 路径对象 - public static DirectoryInfo GetDirectory(Assembly? assem) - { - if (assem is null) - throw new(nameof(assem)); - - return new FileInfo(assem.Location).Directory; - } - #endregion - - #region 构造函数 - /// - /// 注册中心 - /// - /// 配置项目 - public AutoRegAssem(AutoRegConfig autoRegConfig) - { - var assem = Assembly.GetCallingAssembly(); - _info = new() - { - Loader = assem.Location, - Fullname = assem.FullName, - Name = assem.GetName().Name, - LoadType = AssemLoadType.Startting - }; - - if ((autoRegConfig & AutoRegConfig.Regedit) == AutoRegConfig.Regedit) - { - if (!SearchForReg()) - RegApp(); - } - - if ((autoRegConfig & AutoRegConfig.RemoveEMR) == AutoRegConfig.RemoveEMR) - AcadEMR.Remove(); - - // 实例化了 AutoClass 之后会自动执行 IFoxAutoGo 接口下面的类, - // 以及自动执行特性 [IFoxInitialize] - // 类库用户不在此处进行其他代码,而是实现特性 - _autoRef = new AutoReflection(_info.Name, autoRegConfig); - _autoRef.Initialize(); - } - #endregion - - #region RegApp - - /// - /// 获取当前cad注册表位置 - /// - /// 打开权限 - /// - public static RegistryKey GetAcAppKey(bool writable = true) - { - RegistryKey? ackey = null; - -#if NET35 - string key = HostApplicationServices.Current.RegistryProductRootKey; // 这里浩辰读出来是"" -#elif !HC2020 - string key = HostApplicationServices.Current.UserRegistryProductRootKey; -#endif - -#if !HC2020 - ackey = Registry.CurrentUser.OpenSubKey(key, writable); -#else - // 浩辰 - var s = GrxCAD.DatabaseServices.HostApplicationServices.Current.RegistryProductRootKey;// 浩辰奇怪的空值 - string str = CadSystem.Getvar("ACADVER"); - str = Regex.Replace(str, @"[^\d.\d]", ""); - double.TryParse(str, out double a); - // string regedit = @"Software\Gstarsoft\GstarCAD\R" + a.ToString() + @"\zh-CN"; // 2019 - // string regedit = @"Software\Gstarsoft\GstarCAD\B" + a.ToString() + @"\zh-CN";// 2020 这里是 - string regedit = @"Software\Gstarsoft\GstarCAD\B20\zh-CN";// 2020 这里是 - - ackey = Registry.CurrentUser.OpenSubKey(regedit, writable); -#endif - return ackey.CreateSubKey("Applications"); - } - - /// - /// 卸载注册表信息 - /// - public bool UnRegApp() - { - var appkey = GetAcAppKey(); - if (appkey.SubKeyCount == 0) - return false; - - var regApps = appkey.GetSubKeyNames(); - if (regApps.Contains(_info.Name)) - { - appkey.DeleteSubKey(_info.Name, false); - return true; - } - return false; - } - - /// - /// 是否已经存在注册表 - /// - /// - bool SearchForReg() - { - // 在使用netloadx的时候,此处注册表是失效的,具体原因要进行netloadx测试 - var appkey = GetAcAppKey(); - if (appkey.SubKeyCount == 0) - return false; - - var regApps = appkey.GetSubKeyNames(); - if (regApps.Contains(_info.Name)) - { - // 20220409 bug:文件名相同,路径不同,需要判断路径 - var info = appkey.OpenSubKey(_info.Name); - return info.GetValue("LOADER")?.ToString().ToLower() == _info.Loader.ToLower(); - } - return false; - } - - /// - /// 在注册表写入自动加载的程序集信息 - /// - public void RegApp() - { - var appkey = GetAcAppKey(); - var rk = appkey.CreateSubKey(_info.Name); - rk.SetValue("DESCRIPTION", _info.Fullname, RegistryValueKind.String); - rk.SetValue("LOADCTRLS", _info.LoadType, RegistryValueKind.DWord); - rk.SetValue("LOADER", _info.Loader, RegistryValueKind.String); - rk.SetValue("MANAGED", 1, RegistryValueKind.DWord); - appkey.Close(); - } - - // 这里的是不会自动执行的 - public void Initialize() { } - public void Terminate() { } - - ~AutoRegAssem() - { - _autoRef.Terminate(); - } - #endregion RegApp -} \ No newline at end of file diff --git a/src/IFoxCAD.Cad/Runtime/CadVersion.cs b/src/IFoxCAD.Cad/Runtime/CadVersion.cs deleted file mode 100644 index d9614bf..0000000 --- a/src/IFoxCAD.Cad/Runtime/CadVersion.cs +++ /dev/null @@ -1,92 +0,0 @@ -namespace IFoxCAD.Cad; - -#if ac2009 - -public class CadVersion -{ - /// - /// 主版本 - /// - public int Major { get; set; } - - /// - /// 次版本 - /// - public int Minor { get; set; } - - /// - /// 版本号 - /// - public double ProgId => double.Parse($"{Major}.{Minor}"); - - /// - /// 注册表名称 - /// - public string? ProductName { get; set; } - - /// - /// 注册表位置 - /// - public string? ProductRootKey { get; set; } - - /// - /// 转换为字符串 - /// - /// 表示版本号的字符串 - public override string ToString() - { - return $"名称:{ProductName}\n版本号:{ProgId}\n注册表位置:{ProductRootKey}"; - } - // public override bool Equals(object obj) - // { - // return base.Equals(obj); - // } - - // public override int GetHashCode() - // { - // return base.GetHashCode(); - // } - - // // public override string ToString() - // // { - // // return base.ToString(); - // // } -} -#else -public record CadVersion -{ - /// - /// 主版本 - /// - public int Major; - - /// - /// 次版本 - /// - public int Minor; - - /// - /// 版本号 - /// - public double ProgId => double.Parse($"{Major}.{Minor}"); - - /// - /// 注册表名称 - /// - public string? ProductName; - - /// - /// 注册表位置 - /// - public string? ProductRootKey; - - /// - /// 转换为字符串 - /// - /// 表示版本号的字符串 - public override string ToString() - { - return $"名称:{ProductName}\n版本号:{ProgId}\n注册表位置:{ProductRootKey}"; - } -} -#endif \ No newline at end of file diff --git a/src/IFoxCAD.Cad/Runtime/DBTrans.cs b/src/IFoxCAD.Cad/Runtime/DBTrans.cs deleted file mode 100644 index 270886f..0000000 --- a/src/IFoxCAD.Cad/Runtime/DBTrans.cs +++ /dev/null @@ -1,639 +0,0 @@ -namespace IFoxCAD.Cad; - -using System.Diagnostics; -using System.IO; -using System.Threading; -using System.Windows.Forms; - -/// -/// 事务栈 -/// 隐匿事务在数据库其中担任的角色 -/// -public class DBTrans : IDisposable -{ - #region 私有字段 - /// - /// 文档锁 - /// - private readonly DocumentLock? documentLock; - /// - /// 是否提交事务 - /// - private readonly bool _commit; - /// - /// 事务栈 - /// - private static readonly Stack dBTrans = new(); - /// - /// 文件名 - /// - private readonly string? _fileName; - #endregion - - #region 公开属性 - /// - /// 返回当前事务 - /// - public static DBTrans Top - { - get - { - /* - * 0x01 - * 事务栈上面有事务,这个事务属于当前文档, - * 那么直接提交原本事务然后再开一个(一直把栈前面的同数据库提交清空) - * 那不就发生跨事务读取图元了吗?....否决 - * - * 0x02 - * 跨文档事务出错 Autodesk.AutoCAD.Runtime.Exception:“eNotFromThisDocument” - * Curves.GetEntities()会从Top获取事务(Top会new一个),此时会是当前文档; - * 然后命令文中发生了 using var tr = new DBTrans(); - * 当退出命令此事务释放,但是从来不释放Top, - * 然后我新建了一个文档,再进行命令=>又进入Top,Top返回了前一个文档的事务 - * 因此所以无法清理栈,所以Dispose不触发,导致无法刷新图元和Ctrl+Z出错 - * 所以用AOP方式修复 - * - * 0x03 - * 经过艰苦卓绝的测试,aop模式由于不能断点调试,所以暂时放弃。 - */ - - // 由于大量的函数依赖本属性,强迫用户先开启事务 - if (dBTrans.Count == 0) - throw new ArgumentNullException("事务栈没有任何事务,请在调用前创建:" + nameof(DBTrans)); - var trans = dBTrans.Peek(); - return trans; - } - } - - /// - /// 文档 - /// - public Document? Document { get; private set; } - /// - /// 命令行 - /// - public Editor? Editor { get; private set; } - /// - /// 事务管理器 - /// - public Transaction Transaction { get; private set; } - /// - /// 数据库 - /// - public Database Database { get; private set; } - #endregion - - #region 构造函数 - /// - /// 事务栈 - /// 默认构造函数,默认为打开当前文档,默认提交事务 - /// - /// 要打开的文档 - /// 事务是否提交 - /// 是否锁文档 - public DBTrans(Document? doc = null, bool commit = true, bool doclock = false) - { - Document = doc ?? Acap.DocumentManager.MdiActiveDocument; - Database = Document.Database; - Editor = Document.Editor; - Transaction = Database.TransactionManager.StartTransaction(); - _commit = commit; - if (doclock) - documentLock = Document.LockDocument(); - - dBTrans.Push(this); - } - - /// - /// 事务栈 - /// 打开数据库,默认提交事务 - /// - /// 要打开的数据库 - /// 事务是否提交 - public DBTrans(Database database, bool commit = true) - { - Database = database; - Transaction = Database.TransactionManager.StartTransaction(); - _commit = commit; - dBTrans.Push(this); - } - - /// - /// 事务栈 - /// 打开文件,默认提交事务 - /// - /// 要打开的文件名 - /// 事务是否提交 - /// 开图模式 - /// 密码 - public DBTrans(string fileName, - bool commit = true, - FileOpenMode openMode = FileOpenMode.OpenForReadAndWriteNoShare, - string? password = null) - { - if (fileName == null || string.IsNullOrEmpty(fileName.Trim())) - throw new ArgumentNullException(nameof(fileName)); - - _fileName = fileName.Replace("/", "\\");//// doc.Name总是"D:\\JX.dwg" - - if (!File.Exists(_fileName)) - { - // 此处若为失败的文件名,那么保存的时候就会丢失名称, - // 因此用 _fileName 储存 - Database = new Database(true, false); - } - else - { - var doc = Acap.DocumentManager - .Cast() - .FirstOrDefault(doc => doc.Name == _fileName); - if (doc is not null) - { - Database = doc.Database; - Document = doc; - Editor = doc.Editor; - } - else - { - Database = new Database(false, true); - if (Path.GetExtension(_fileName).ToLower().Contains("dxf")) - { - Database.DxfIn(_fileName, null); - } - else - { -#if ac2008 - // FileAccess fileAccess = FileAccess.Read; - FileShare fileShare = FileShare.Read; - switch (openMode) - { - case FileOpenMode.OpenTryForReadShare:// 这个是什么状态?? - // fileAccess = FileAccess.ReadWrite; - fileShare = FileShare.ReadWrite; - break; - case FileOpenMode.OpenForReadAndAllShare:// 完美匹配 - // fileAccess = FileAccess.ReadWrite; - fileShare = FileShare.ReadWrite; - break; - case FileOpenMode.OpenForReadAndWriteNoShare:// 完美匹配 - // fileAccess = FileAccess.ReadWrite; - fileShare = FileShare.None; - break; - case FileOpenMode.OpenForReadAndReadShare:// 完美匹配 - // fileAccess = FileAccess.Read; - fileShare = FileShare.Read; - break; - default: - break; - } - - // 这个会致命错误 - // using FileStream fileStream = new(_fileName, FileMode.Open, fileAccess, fileShare); - // Database.ReadDwgFile(fileStream.SafeFileHandle.DangerousGetHandle(), true, password); - - Database.ReadDwgFile(_fileName, fileShare, true, password); -#else - Database.ReadDwgFile(_fileName, openMode, true, password); -#endif - } - Database.CloseInput(true); - } - } - - Transaction = Database.TransactionManager.StartTransaction(); - _commit = commit; - dBTrans.Push(this); - } - #endregion - - #region 类型转换 - /// - /// 隐式转换为Transaction - /// - /// 事务管理器 - /// 事务管理器 - public static implicit operator Transaction(DBTrans tr) - { - return tr.Transaction; - } - #endregion - - #region 符号表 - - /// - /// 块表 - /// - public SymbolTable BlockTable => _BlockTable ??= new(this, Database.BlockTableId); - SymbolTable? _BlockTable; - /// - /// 当前绘图空间 - /// - public BlockTableRecord CurrentSpace => BlockTable.GetRecord(Database.CurrentSpaceId)!; - /// - /// 模型空间 - /// - public BlockTableRecord ModelSpace => BlockTable.GetRecord(BlockTable.CurrentSymbolTable[BlockTableRecord.ModelSpace])!; - /// - /// 图纸空间 - /// - public BlockTableRecord PaperSpace => BlockTable.GetRecord(BlockTable.CurrentSymbolTable[BlockTableRecord.PaperSpace])!; - /// - /// 层表 - /// - public SymbolTable LayerTable => _LayerTable ??= new(this, Database.LayerTableId); - SymbolTable? _LayerTable; - /// - /// 文字样式表 - /// - public SymbolTable TextStyleTable => _TextStyleTable ??= new(this, Database.TextStyleTableId); - SymbolTable? _TextStyleTable; - /// - /// 注册应用程序表 - /// - public SymbolTable RegAppTable => _RegAppTable ??= new(this, Database.RegAppTableId); - SymbolTable? _RegAppTable; - /// - /// 标注样式表 - /// - public SymbolTable DimStyleTable => _DimStyleTable ??= new(this, Database.DimStyleTableId); - SymbolTable? _DimStyleTable; - /// - /// 线型表 - /// - public SymbolTable LinetypeTable => _LinetypeTable ??= new(this, Database.LinetypeTableId); - SymbolTable? _LinetypeTable; - /// - /// 用户坐标系表 - /// - public SymbolTable UcsTable => _UcsTable ??= new(this, Database.UcsTableId); - SymbolTable? _UcsTable; - /// - /// 视图表 - /// - public SymbolTable ViewTable => _ViewTable ??= new(this, Database.ViewTableId); - SymbolTable? _ViewTable; - /// - /// 视口表 - /// - public SymbolTable ViewportTable => _ViewportTable ??= new(this, Database.ViewportTableId); - SymbolTable? _ViewportTable; - #endregion - - #region 字典 - /// - /// 命名对象字典 - /// - public DBDictionary NamedObjectsDict => GetObject(Database.NamedObjectsDictionaryId)!; - /// - /// 组字典 - /// - public DBDictionary GroupDict => GetObject(Database.GroupDictionaryId)!; - /// - /// 多重引线样式字典 - /// - public DBDictionary MLeaderStyleDict => GetObject(Database.MLeaderStyleDictionaryId)!; - /// - /// 多线样式字典 - /// - public DBDictionary MLStyleDict => GetObject(Database.MLStyleDictionaryId)!; - /// - /// 材质字典 - /// - public DBDictionary MaterialDict => GetObject(Database.MaterialDictionaryId)!; - /// - /// 表格样式字典 - /// - public DBDictionary TableStyleDict => GetObject(Database.TableStyleDictionaryId)!; - /// - /// 视觉样式字典 - /// - public DBDictionary VisualStyleDict => GetObject(Database.VisualStyleDictionaryId)!; - /// - /// 颜色字典 - /// - public DBDictionary ColorDict => GetObject(Database.ColorDictionaryId)!; - /// - /// 打印设置字典 - /// - public DBDictionary PlotSettingsDict => GetObject(Database.PlotSettingsDictionaryId)!; - /// - /// 打印样式表名字典 - /// - public DBDictionary PlotStyleNameDict => GetObject(Database.PlotStyleNameDictionaryId)!; - /// - /// 布局字典 - /// - public DBDictionary LayoutDict => GetObject(Database.LayoutDictionaryId)!; - /// - /// 数据链接字典 - /// - public DBDictionary DataLinkDict => GetObject(Database.DataLinkDictionaryId)!; -#if !ac2009 - /// - /// 详细视图样式字典 - /// - public DBDictionary DetailViewStyleDict => GetObject(Database.DetailViewStyleDictionaryId)!; - /// - /// 剖面视图样式字典 - /// - public DBDictionary SectionViewStyleDict => GetObject(Database.SectionViewStyleDictionaryId)!; -#endif - #endregion - - #region 获取对象 - /// - /// 根据对象id获取图元对象 - /// - /// 要获取的图元对象的类型 - /// 对象id - /// 打开模式,默认为只读 - /// 是否打开已删除对象,默认为不打开 - /// 是否打开锁定图层对象,默认为不打开 - /// 图元对象,类型不匹配时返回 - public T? GetObject(ObjectId id, - OpenMode mode = OpenMode.ForRead, - bool openErased = false, - bool forceOpenOnLockedLayer = false) where T : DBObject - { - return Transaction.GetObject(id, mode, openErased, forceOpenOnLockedLayer) as T; - } - - /// - /// 根据对象句柄字符串获取对象Id - /// - /// 句柄字符串 - /// 对象id - public ObjectId GetObjectId(string handleString) - { - var hanle = new Handle(Convert.ToInt64(handleString, 16)); - // return Database.GetObjectId(false, hanle, 0); - return DBTransHelper.TryGetObjectId(Database, hanle); - } - #endregion - - #region 保存文件 - /// - /// 保存文件 - /// - /// - public void SaveDwgFile(DwgVersion version = DwgVersion.AC1800) - { - SaveFile(version); - } - - /// - /// 保存文件
    - ///
    - /// 默认2004dwg;若保存dxf则需要在路径输入扩展名 - /// 为true时候无效,将变为自动识别环境变量 - /// 另存为文件,前台将调用时它将无效,将变为弹出面板 - /// 保存路径失败的提示 - public void SaveFile(DwgVersion version = DwgVersion.AC1800, - bool automatic = true, - string? saveAsFile = null, - bool echoes = true) - { - // 遍历当前所有文档,文档必然是前台的 - Document? doc = null; - foreach (Document docItem in Acap.DocumentManager) - { - if (docItem.Database.Filename == this.Database.Filename) - { - doc = docItem; - break; - } - } - // 前台开图,使用命令保存;不需要切换文档 - if (doc != null) - { - if (saveAsFile == null) - doc.SendStringToExecute("_qsave\n", false, true, true); - else - /// 无法把 给这个面板 - doc.SendStringToExecute($"_Saveas\n", false, true, true); - return; - } - - // 后台开图,用数据库保存 - string? fileMsg; - bool creatFlag = false; - saveAsFile = saveAsFile?.Trim(); - if (string.IsNullOrEmpty(saveAsFile)) - { - fileMsg = _fileName; - creatFlag = true; - } - else - { - fileMsg = saveAsFile; - - // 路径失败也保存到桌面 - var path = Path.GetDirectoryName(saveAsFile); - if (string.IsNullOrEmpty(path)) - { - creatFlag = true; - } - else if (!Directory.Exists(path)) - { - try { Directory.CreateDirectory(path); } - catch { creatFlag = true; } - } - - // 文件名缺失时 - if (!creatFlag && - string.IsNullOrEmpty(Path.GetFileName(saveAsFile).Trim())) - creatFlag = true; - } - var fileNameWith = Path.GetFileNameWithoutExtension(saveAsFile).Trim(); - if (string.IsNullOrEmpty(fileNameWith)) - creatFlag = true; - - if (creatFlag) - { - var (error, file) = GetOrCreateSaveAsFile(); - if (echoes && error) - MessageBox.Show($"错误参数:\n{fileMsg}\n\n它将保存:\n{file}", "错误的文件路径"); - saveAsFile = file; - } - - if (Path.GetExtension(saveAsFile).ToLower().Contains("dxf")) - { - // dxf用任何版本号都会报错 - Database.DxfOut(saveAsFile, 7, true); - } - else - { - if (automatic) - version = Env.GetDefaultDwgVersion(); - - // dwg需要版本号,而dxf不用,dwg用dxf版本号会报错 - // 若扩展名和版本号冲突,按照扩展名为准 - if (version.IsDxfVersion()) - version = DwgVersion.Current; - Database.SaveAs(saveAsFile, version); - } - } - - - /// - /// 获取文件名,无效的话就制造 - /// - /// - (bool error, string path) GetOrCreateSaveAsFile() - { - var file = Database.Filename; - if (!string.IsNullOrEmpty(file)) - return (false, file); - - // 为了防止用户输入了错误的路径造成无法保存, - // 所以此处将进行保存到桌面, - // 而不是弹出警告就结束 - // 防止前台关闭了所有文档导致没有Editor,所以使用 MessageBox 发送警告 - var fileName = Path.GetFileNameWithoutExtension(_fileName).Trim(); - var fileExt = Path.GetExtension(_fileName); - - if (string.IsNullOrEmpty(fileName)) - fileName = DateTime.Now.ToString("--yyMMdd--hhmmssffff"); - if (string.IsNullOrEmpty(fileExt)) - fileExt = ".dwg"; - - // 构造函数(fileName)用了不存在的路径进行后台打开,就会出现此问题 - // 测试命令 FileNotExist - var dir = Environment.GetFolderPath( - Environment.SpecialFolder.DesktopDirectory) - + "\\后台保存出错的文件\\"; - - if (!Directory.Exists(dir)) - Directory.CreateDirectory(dir); - - file = dir + fileName + fileExt; - while (File.Exists(file)) - { - var time = DateTime.Now.ToString("--yyMMdd--hhmmssffff"); - file = dir + fileName + time + fileExt; - Thread.Sleep(100); - } - return (true, file); - } - - #endregion - - #region 前台后台任务 - /// - /// 前台后台任务分别处理 - /// - /// - /// 备注:
    - /// 0x01 文字偏移问题主要出现线性引擎函数上面,
    - /// 在 参照绑定/深度克隆 的底层共用此函数导致
    - /// 0x02 后台是利用前台当前数据库进行处理的
    - /// 0x03 跨进程通讯暂无测试(可能存在bug)
    - ///
    - /// 委托 - /// 开启单行文字偏移处理 - public void Task(Action action, bool handlingDBTextDeviation = true) - { - if (action == null) - throw new ArgumentNullException(nameof(action)); - - // 前台开图 || 后台直接处理 - if (Document != null || !handlingDBTextDeviation) - { - action.Invoke(); - return; - } - - // 后台 - // 这种情况发生在关闭了所有文档之后,进行跨进程通讯 - // 此处要先获取激活的文档,不能直接获取当前数据库否则异常 - var dm = Acap.DocumentManager; - var doc = dm.MdiActiveDocument; - if (doc == null) - { - action.Invoke(); - return; - } - // 处理单行文字偏移 - // 前台绑定参照的时候不能用它,否则抛出异常:eWasErased - // 所以本函数自动识别前后台做处理 - var dbBak = doc.Database; - HostApplicationServices.WorkingDatabase = Database; - action.Invoke(); - HostApplicationServices.WorkingDatabase = dbBak; - } - #endregion - - #region IDisposable接口相关函数 - /// - /// 取消事务 - /// - public void Abort() - { - Dispose(false); - } - - /// - /// 提交事务 - /// - public void Commit() - { - Dispose(true); - } - - public bool IsDisposed { get; private set; } = false; - - /// - /// 手动调用释放 - /// - public void Dispose() - { - Dispose(true); - GC.SuppressFinalize(this); - } - - /// - /// 析构函数调用释放 - /// - ~DBTrans() - { - Dispose(false); - } - - protected virtual void Dispose(bool disposing) - { - /* 事务dispose流程: - * 1. 根据传入的参数确定是否提交,true为提交,false为不提交 - * 2. 如果锁文档就将文档锁dispose - * 3. 不管是否提交,既然进入dispose,就要将事务栈的当前事务弹出 - * 注意这里的事务栈不是cad的事务管理器,而是dbtrans的事务 - * 4. 清理非托管的字段 - */ - - // 不重复释放,并设置已经释放 - if (IsDisposed) return; - IsDisposed = true; - - - if (disposing) - { - // 调用cad的事务进行提交,释放托管状态(托管对象) - Transaction.Commit(); - } - else - { - // 否则取消所有的修改 - Transaction.Abort(); - } - - // 将cad事务进行销毁 - if (!Transaction.IsDisposed) - Transaction.Dispose(); - - // 将文档锁销毁 - documentLock?.Dispose(); - - // 将当前事务栈弹栈 - dBTrans.Pop(); - } - #endregion -} \ No newline at end of file diff --git a/src/IFoxCAD.Cad/Runtime/Env.cs b/src/IFoxCAD.Cad/Runtime/Env.cs deleted file mode 100644 index 572e3e8..0000000 --- a/src/IFoxCAD.Cad/Runtime/Env.cs +++ /dev/null @@ -1,732 +0,0 @@ -namespace IFoxCAD.Cad; - -/// -/// 系统管理类 -/// -/// 封装了一些系统 osmode;cmdecho;dimblk 系统变量
    -/// 封装了常用的 文档 编辑器 数据库等对象为静态变量
    -/// 封装了配置页面的注册表信息获取函数 -///
    -///
    -public static class Env -{ - #region Goal - - /// - /// 当前的数据库 - /// - public static Database Database => HostApplicationServices.WorkingDatabase; - - /// - /// 当前文档 - /// - public static Document Document => Acap.DocumentManager.MdiActiveDocument; - - /// - /// 编辑器对象 - /// - public static Editor Editor => Document.Editor; - - /// - /// 图形管理器 - /// - public static Manager GsManager => Document.GraphicsManager; - - #endregion Goal - - #region Preferences - - /// - /// 获取当前配置的数据 - /// - /// 小节名 - /// 数据名 - /// 对象 - public static object GetCurrentProfileProperty(string subSectionName, string propertyName) - { - UserConfigurationManager ucm = Acap.UserConfigurationManager; - IConfigurationSection cpf = ucm.OpenCurrentProfile(); - IConfigurationSection ss = cpf.OpenSubsection(subSectionName); - return ss.ReadProperty(propertyName, ""); - } - - /// - /// 获取对话框配置的数据 - /// - /// 对话框对象 - /// 配置项 - public static IConfigurationSection GetDialogSection(object dialog) - { - UserConfigurationManager ucm = Acap.UserConfigurationManager; - IConfigurationSection ds = ucm.OpenDialogSection(dialog); - return ds; - } - - /// - /// 获取公共配置的数据 - /// - /// 数据名 - /// 配置项 - public static IConfigurationSection GetGlobalSection(string propertyName) - { - UserConfigurationManager ucm = Acap.UserConfigurationManager; - IConfigurationSection gs = ucm.OpenGlobalSection(); - IConfigurationSection ss = gs.OpenSubsection(propertyName); - return ss; - } - - #endregion Preferences - - #region Enum - - /// - /// 控制在AutoLISP的command函数运行时AutoCAD是否回显提示和输入, 为显示, 为不显示 - /// - public static bool CmdEcho - { - get => Convert.ToInt16(Acap.GetSystemVariable("cmdecho")) == 1; - set => Acap.SetSystemVariable("cmdecho", Convert.ToInt16(value)); - } - - /// - /// 控制在光标是否为正交模式, 为打开正交, 为关闭正交 - /// - public static bool OrthoMode - { - get => Convert.ToInt16(Acap.GetSystemVariable("ORTHOMODE")) == 1; - set => Acap.SetSystemVariable("ORTHOMODE", Convert.ToInt16(value)); - } - - #region Dimblk - - /// - /// 标注箭头类型 - /// - public enum DimblkType - { - /// - /// 实心闭合 - /// - Defult, - - /// - /// 点 - /// - Dot, - - /// - /// 小点 - /// - DotSmall, - - /// - /// 空心点 - /// - DotBlank, - - /// - /// 原点标记 - /// - Origin, - - /// - /// 原点标记2 - /// - Origin2, - - /// - /// 打开 - /// - Open, - - /// - /// 直角 - /// - Open90, - - /// - /// 30度角 - /// - Open30, - - /// - /// 闭合 - /// - Closed, - - /// - /// 空心小点 - /// - Small, - - /// - /// 无 - /// - None, - - /// - /// 倾斜 - /// - Oblique, - - /// - /// 实心框 - /// - BoxFilled, - - /// - /// 方框 - /// - BoxBlank, - - /// - /// 空心闭合 - /// - ClosedBlank, - - /// - /// 实心基准三角形 - /// - DatumFilled, - - /// - /// 基准三角形 - /// - DatumBlank, - - /// - /// 完整标记 - /// - Integral, - - /// - /// 建筑标记 - /// - ArchTick - } - - private static readonly Dictionary dimdescdict = new() - { - { "实心闭合", DimblkType.Defult }, - { "点", DimblkType.Dot }, - { "小点", DimblkType.DotSmall }, - { "空心点", DimblkType.DotBlank }, - { "原点标记", DimblkType.Origin }, - { "原点标记 2", DimblkType.Origin2 }, - { "打开", DimblkType.Open }, - { "直角", DimblkType.Open90 }, - { "30 度角", DimblkType.Open30 }, - { "闭合", DimblkType.Closed }, - { "空心小点", DimblkType.Small }, - { "无", DimblkType.None }, - { "倾斜", DimblkType.Oblique }, - { "实心框", DimblkType.BoxFilled }, - { "方框", DimblkType.BoxBlank }, - { "空心闭合", DimblkType.ClosedBlank }, - { "实心基准三角形", DimblkType.DatumFilled }, - { "基准三角形", DimblkType.DatumBlank }, - { "完整标记", DimblkType.Integral }, - { "建筑标记", DimblkType.ArchTick }, - - { "", DimblkType.Defult }, - { "_DOT", DimblkType.Dot }, - { "_DOTSMALL", DimblkType.DotSmall }, - { "_DOTBLANK", DimblkType.DotBlank }, - { "_ORIGIN", DimblkType.Origin }, - { "_ORIGIN2", DimblkType.Origin2 }, - { "_OPEN", DimblkType.Open }, - { "_OPEN90", DimblkType.Open90 }, - { "_OPEN30", DimblkType.Open30 }, - { "_CLOSED", DimblkType.Closed }, - { "_SMALL", DimblkType.Small }, - { "_NONE", DimblkType.None }, - { "_OBLIQUE", DimblkType.Oblique }, - { "_BOXFILLED", DimblkType.BoxFilled }, - { "_BOXBLANK", DimblkType.BoxBlank }, - { "_CLOSEDBLANK", DimblkType.ClosedBlank }, - { "_DATUMFILLED", DimblkType.DatumFilled }, - { "_DATUMBLANK", DimblkType.DatumBlank }, - { "_INTEGRAL", DimblkType.Integral }, - { "_ARCHTICK", DimblkType.ArchTick }, - }; - - - - /// - /// 标注箭头属性 - /// - public static DimblkType Dimblk - { - get - { - string s = ((string)Acap.GetSystemVariable("dimblk")).ToUpper(); - // if (string.IsNullOrEmpty(s)) - // { - // return DimblkType.Defult; - // } - // else - // { - // if (dimdescdict.TryGetValue(s, out DimblkType value)) - // { - // return value; - // } - // return s.ToEnum(); - // // return s.FromDescName(); - // } - return dimdescdict[s]; - } - set - { - string s = GetDimblkName(value); - Acap.SetSystemVariable("dimblk", s); - } - } - - /// - /// 获取标注箭头名 - /// - /// 标注箭头类型 - /// 箭头名 - public static string GetDimblkName(DimblkType dimblk) - { - return - dimblk == DimblkType.Defult - ? - "." - : - "_" + dimblk.GetName(); - } - - /// - /// 获取标注箭头ID - /// - /// 标注箭头类型 - /// 箭头ID - public static ObjectId GetDimblkId(DimblkType dimblk) - { - DimblkType oldDimblk = Dimblk; - Dimblk = dimblk; - ObjectId id = HostApplicationServices.WorkingDatabase.Dimblk; - Dimblk = oldDimblk; - return id; - } - - #endregion Dimblk - - #region OsMode - - /// - /// 捕捉模式系统变量类型 - /// - public enum OSModeType - { - /// - /// 无 - /// - None = 0, - - /// - /// 端点 - /// - End = 1, - - /// - /// 中点 - /// - Middle = 2, - - /// - /// 圆心 - /// - Center = 4, - - /// - /// 节点 - /// - Node = 8, - - /// - /// 象限点 - /// - Quadrant = 16, - - /// - /// 交点 - /// - Intersection = 32, - - /// - /// 插入点 - /// - Insert = 64, - - /// - /// 垂足 - /// - Pedal = 128, - - /// - /// 切点 - /// - Tangent = 256, - - /// - /// 最近点 - /// - Nearest = 512, - - /// - /// 几何中心 - /// - Quick = 1024, - - /// - /// 外观交点 - /// - Appearance = 2048, - - /// - /// 延伸 - /// - Extension = 4096, - - /// - /// 平行 - /// - Parallel = 8192 - } - - /// - /// 捕捉模式系统变量 - /// - public static OSModeType OSMode - { - get - { - return (OSModeType)Convert.ToInt16(Acap.GetSystemVariable("osmode")); - } - set - { - Acap.SetSystemVariable("osmode", (int)value); - } - } - /// - /// 捕捉模式osm1是否包含osm2 - /// - /// 原模式 - /// 要比较的模式 - /// 包含时返回 ,不包含时返回 - public static bool Include(this OSModeType osm1, OSModeType osm2) - { - return (osm1 & osm2) == osm2; - } - #endregion OsMode - - - private static string GetName(this T value) - { - return Enum.GetName(typeof(T), value); - } - - #endregion Enum - - #region acad系统变量 - /// - /// 获取cad系统变量 - /// - /// 变量名 - /// 变量值 - public static object GetVar(string? varName) - { - return Acap.GetSystemVariable(varName); - } - /// - /// 设置cad系统变量
    - /// 0x01 建议先获取现有变量值和设置的是否相同,否则直接设置会发生异常
    - /// 0x02 建议锁文档,否则 Psltscale 设置发生异常
    - /// 发生异常的时候vs输出窗口会打印一下,但是如果不介意也没啥问题 - ///
    - /// 变量名 - /// 变量值 - /// 输出异常,默认true;此设置仅为打印到命令栏,无法控制vs输出 - public static void SetVar(string? varName, object? value, bool echo = true) - { - try - { - Acap.SetSystemVariable(varName, value); - } - catch (System.Exception) - { - if (echo) - Env.Print($"{varName} 是不存在的变量!"); - } - } - #endregion - - #region acad环境变量 -#if NET35 - [System.Security.SuppressUnmanagedCodeSecurity] - [DllImport("acad.exe", CharSet = CharSet.Auto, CallingConvention = CallingConvention.Cdecl, EntryPoint = "acedGetEnv")] - static extern int AcedGetEnv(string? envName, StringBuilder ReturnValue); - - [System.Security.SuppressUnmanagedCodeSecurity] - [DllImport("acad.exe", CharSet = CharSet.Auto, CallingConvention = CallingConvention.Cdecl, EntryPoint = "acedSetEnv")] - static extern int AcedSetEnv(string? envName, StringBuilder NewValue); -#elif !HC2020 - [System.Security.SuppressUnmanagedCodeSecurity] - [DllImport("accore.dll", CharSet = CharSet.Auto, CallingConvention = CallingConvention.Cdecl, EntryPoint = "acedGetEnv")] - static extern int AcedGetEnv(string? envName, StringBuilder ReturnValue); - - [System.Security.SuppressUnmanagedCodeSecurity] - [DllImport("accore.dll", CharSet = CharSet.Auto, CallingConvention = CallingConvention.Cdecl, EntryPoint = "acedSetEnv")] - static extern int AcedSetEnv(string? envName, StringBuilder NewValue); -#endif -#if HC2020 - [System.Security.SuppressUnmanagedCodeSecurity] - [DllImport("gced.dll", CharSet = CharSet.Auto, CallingConvention = CallingConvention.Cdecl, EntryPoint = "gcedGetEnv")] - static extern int AcedGetEnv(string? envName, StringBuilder ReturnValue); - - [System.Security.SuppressUnmanagedCodeSecurity] - [DllImport("gced.dll", CharSet = CharSet.Auto, CallingConvention = CallingConvention.Cdecl, EntryPoint = "gcedSetEnv")] - static extern int AcedSetEnv(string? envName, StringBuilder NewValue); -#endif - - /// - /// 读取acad环境变量
    - /// 也能获取win环境变量 - ///
    - /// 变量名 - /// 返回值从不为null,需判断 - public static string GetEnv(string? name) - { - // 它将混合查询以下路径: - // acad2008注册表路径: 计算机\HKEY_CURRENT_USER\SOFTWARE\Autodesk\AutoCAD\R17.1\ACAD - 6001:804\FixedProfile\General - // 用户: 计算机\HKEY_CURRENT_USER\Environment - // 系统: 计算机\HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\Session Manager\Environment - - // GetEnv("Path")长度很长: - // 可用内存 (最新格式) 1 MB (标准格式) - // https://docs.microsoft.com/zh-cn/windows/win32/sysinfo/registry-element-size-limits - - var sbRes = new StringBuilder(1024 * 1024); - _ = AcedGetEnv(name, sbRes); - return sbRes.ToString(); - } - - /// - /// 设置acad环境变量
    - /// 它是不会报错的,但是直接设置会写入注册表的,
    - /// 如果是设置高低版本cad不同的变量,建议先读取判断再设置
    - ///
    - /// 变量名 - /// 变量值 - /// - public static int SetEnv(string? name, string? var) - { - return AcedSetEnv(name, new StringBuilder(var)); - } - #endregion - - #region win环境变量/由于 Aced的 能够同时获取此变量与cad内的,所以废弃 - ///// - ///// 获取系统环境变量 - ///// - ///// 变量名 - ///// 指定的环境变量的值;或者如果找不到环境变量,则返回 null - //public static string? GetEnv(string? var) - //{ - // // 从当前进程或者从当前用户或本地计算机的 Windows 操作系统注册表项检索环境变量的值 - // // 用户: 计算机\HKEY_CURRENT_USER\Environment - // // 系统: 计算机\HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\Session Manager\Environment - // return Environment.GetEnvironmentVariable(var); - //} - ///// - ///// 设置系统环境变量 - ///// - ///// 变量名 - ///// 变量值 - //public static void SetEnv(string? var, string? value) - //{ - // // 创建、修改或删除当前进程中或者为当前用户或本地计算机保留的 Windows 操作系统注册表项中存储的环境变量 - // Environment.SetEnvironmentVariable(var, value); - //} - #endregion - - - /// - /// 命令行打印,会自动调用对象的toString函数 - /// - /// 要打印的对象 - public static void Print(object message) => Editor.WriteMessage($"{message}\n"); - public static void Printl(object message) => Editor.WriteMessage($"{Environment.NewLine}{message}\n"); - - /// - /// 判断当前是否在UCS坐标下 - /// - /// Bool - public static bool IsUcs() => (short)GetVar("WORLDUCS") == 0; - - - #region dwg版本号/cad版本号/年份 - /// - /// 获取当前配置文件的保存版本 - /// - /// - public static DwgVersion GetDefaultDwgVersion() - { - DwgVersion version; - var ffs = Env.GetEnv("DefaultFormatForSave"); - version = ffs switch - { - "1" => DwgVersion.AC1009,// R12/LT12 dxf - "8" => DwgVersion.AC1014,// R14/LT98/LT97 dwg - "12" => DwgVersion.AC1015,// 2000 dwg - "13" => DwgVersion.AC1800a,// 2000 dxf - "24" => DwgVersion.AC1800,// 2004 dwg - "25" => (DwgVersion)26,// 2004 dxf - "36" => (DwgVersion)27,// 2007 dwg DwgVersion.AC1021 - "37" => (DwgVersion)28,// 2007 dxf - - //"38" => (DwgVersion),// dwt 样板文件...啊惊没找到这个是什么 - "48" => (DwgVersion)29,// 2010 dwg DwgVersion.AC1024 - "49" => (DwgVersion)30,// 2010 dxf - "60" => (DwgVersion)31,// 2013 dwg DwgVersion.AC1027 - "61" => (DwgVersion)32,// 2013 dxf - "64" => (DwgVersion)33,// 2018 dwg DwgVersion.AC1032 - "65" => (DwgVersion)34,// 2018 dxf - _ => throw new NotImplementedException(),//提醒维护 - }; - return version; - } - - /// - /// 是否为dxf版本号 - /// - /// - /// - public static bool IsDxfVersion(this DwgVersion dwgVersion) - { - var result = (int)dwgVersion switch - { - 16 => true,// R12/LT12 dxf - 24 => true,// 2000 dxf - 26 => true,// 2004 dxf - 28 => true,// 2007 dxf - 30 => true,// 2010 dxf - 32 => true,// 2013 dxf - 34 => true,// 2018 dxf - _ => false, - }; - return result; - } - - /// - /// 获取cad年份 - /// - /// 超出年份就报错 - public static int GetAcadVersion() - { - var ver = Acap.Version.Major + "." + Acap.Version.Minor; - int acarVarNum = ver switch - { - "16.2" => 2006, - "17.0" => 2007, - "17.1" => 2008, - "17.2" => 2009, - "18.0" => 2010, - "18.1" => 2011, - "18.2" => 2012, - "19.0" => 2013, - "19.1" => 2014, - "20.0" => 2015, - "20.1" => 2016, - "21.0" => 2017, - "22.0" => 2018, - "23.0" => 2019, - "23.1" => 2020, - "24.0" => 2021, - "24.1" => 2022, - _ => throw new NotImplementedException(), - }; - return acarVarNum; - } - - public static string GetAcapVersionDll(string str = "acdb") - { - return str + Acap.Version.Major + ".dll"; - } - #endregion - - - #region cad变量功能延伸 - /// - /// 设置cad系统变量
    - /// 提供一个反序列化后,无cad异常输出的功能
    - /// 注意,您需要再此执行时候设置文档锁
    - ///
    - /// 否则也将导致修改数据库异常
    - ///
    - /// - /// - /// 成功返回当前值,失败null - /// - public static object? SetVarEx(string? key, string? value) - { - if (key == null) - throw new ArgumentNullException(nameof(key)); - if (value == null) - throw new ArgumentNullException(nameof(value)); - - var currentVar = Env.GetVar(key); - if (currentVar == null) - return null; - - object? valueType = currentVar.GetType().Name switch - { - "String" => value.Replace("\"", string.Empty), - "Double" => double.Parse(value), - "Int16" => short.Parse(value), - "Int32" => int.Parse(value), - _ => throw new NotImplementedException(), - }; - - // 相同的参数进行设置会发生一次异常 - if (currentVar.ToString().ToUpper() != valueType!.ToString().ToUpper()) - Env.SetVar(key, valueType); - - return currentVar; - } - - /// - /// 设置新系统变量,返回现有系统变量 - /// - /// 设置的变量词典 - /// 返回现有变量词典,然后下次就可以利用它进行设置回来了 - public static Dictionary SaveCadVar(Dictionary args) - { - if (args is null) - throw new ArgumentNullException(nameof(args)); - - var dict = new Dictionary(); - foreach (var item in args) - { - // 判断是否为系统变量 - var ok = SetVarEx(item.Key, item.Value); - if (ok != null) - { - dict.Add(item.Key, ok.ToString()); - continue; - } - - // 判断是否为系统变量 - var envstr = Env.GetEnv(item.Key); - if (!string.IsNullOrEmpty(envstr)) - { - Env.SetEnv(item.Key, item.Value); - dict.Add(item.Key, envstr); - } - } - return dict; - } - #endregion -} \ No newline at end of file diff --git a/src/IFoxCAD.Cad/Runtime/FileOpenMode.cs b/src/IFoxCAD.Cad/Runtime/FileOpenMode.cs deleted file mode 100644 index 912b531..0000000 --- a/src/IFoxCAD.Cad/Runtime/FileOpenMode.cs +++ /dev/null @@ -1,13 +0,0 @@ -#if ac2008 // NET35 -namespace Autodesk.AutoCAD.DatabaseServices -{ - [Wrapper("AcDbDatabase::OpenMode")] - public enum FileOpenMode - { - OpenTryForReadShare = 4, - OpenForReadAndAllShare = 3, - OpenForReadAndWriteNoShare = 2, - OpenForReadAndReadShare = 1 - } -} -#endif \ No newline at end of file diff --git a/src/IFoxCAD.Cad/Runtime/IAutoGo.cs b/src/IFoxCAD.Cad/Runtime/IAutoGo.cs deleted file mode 100644 index c52e7c9..0000000 --- a/src/IFoxCAD.Cad/Runtime/IAutoGo.cs +++ /dev/null @@ -1,311 +0,0 @@ -namespace IFoxCAD.Cad; - -using System.Diagnostics; - -/// -/// 加载时优先级 -/// -[Flags] -public enum Sequence : byte -{ - First,// 最先 - Last, // 最后 -} - -/// -/// 加载时自动执行接口 -/// -public interface IFoxAutoGo -{ - // 控制加载顺序 - Sequence SequenceId(); - // 关闭cad的时候会自动执行 - void Terminate(); - // 打开cad的时候会自动执行 - void Initialize(); -} - -/// -/// 加载时自动执行特性 -/// -[AttributeUsage(AttributeTargets.Method)] -public class IFoxInitialize : Attribute -{ - /// - /// 优先级 - /// - internal Sequence SequenceId; - /// - /// 用于初始化;用于结束回收 - /// - internal bool IsInitialize; - /// - /// 用于初始化和结束回收 - /// - /// 优先级 - /// 用于初始化;用于结束回收 - public IFoxInitialize(Sequence sequence = Sequence.Last, bool isInitialize = true) - { - SequenceId = sequence; - IsInitialize = isInitialize; - } -} - -// 为了解决IExtensionApplication在一个dll内无法多次实现接口的关系 -// 所以在这里反射加载所有的 IAutoGo ,以达到能分开写"启动运行"函数的目的 -class RunClass -{ - public Sequence Sequence { get; } - readonly MethodInfo _methodInfo; - - public RunClass(MethodInfo method, Sequence sequence) - { - _methodInfo = method; - Sequence = sequence; - } - - /// - /// 运行方法 - /// - public void Run() - { - _methodInfo.Invoke(); - } -} - -/// -/// 此类作为加载后cad自动运行接口的一部分,用于反射特性和接口 -/// -/// 启动cad后的执行顺序为:
    -/// 1:特性..(多个)
    -/// 2:接口..(多个) -///
    -///
    -public class AutoReflection -{ - static List _InitializeList = new(); // 储存方法用于初始化 - static List _TerminateList = new(); // 储存方法用于结束释放 - - readonly string _dllName; - readonly AutoRegConfig _autoRegConfig; - - /// - /// 反射执行 - /// - /// 1.特性:
    - /// 2.接口: - ///
    - ///
    - /// 约束在此dll进行加速 - public AutoReflection(string dllName, AutoRegConfig configInfo) - { - _dllName = dllName; - _autoRegConfig = configInfo; - } - - // 启动cad的时候会自动执行 - public void Initialize() - { - try - { - // 收集特性,包括启动时和关闭时 - if ((_autoRegConfig & AutoRegConfig.ReflectionAttribute) == AutoRegConfig.ReflectionAttribute) - GetAttributeFunctions(_InitializeList, _TerminateList); - - if ((_autoRegConfig & AutoRegConfig.ReflectionInterface) == AutoRegConfig.ReflectionInterface) - GetInterfaceFunctions(_InitializeList, nameof(Initialize)); - - if (_InitializeList.Count > 0) - { - // 按照 SequenceId 排序_升序 - _InitializeList = _InitializeList.OrderBy(runClass => runClass.Sequence).ToList(); - RunFunctions(_InitializeList); - } - } - catch (System.Exception) - { - Debugger.Break(); - } - } - - // 关闭cad的时候会自动执行 - public void Terminate() - { - try - { - if ((_autoRegConfig & AutoRegConfig.ReflectionInterface) == AutoRegConfig.ReflectionInterface) - GetInterfaceFunctions(_TerminateList, nameof(Terminate)); - - if (_TerminateList.Count > 0) - { - // 按照 SequenceId 排序_降序 - _TerminateList = _TerminateList.OrderByDescending(runClass => runClass.Sequence).ToList(); - RunFunctions(_TerminateList); - } - } - catch (System.Exception e) - { - Env.Printl(e.Message); - Debugger.Break(); - } - } - - /// - /// 遍历程序域下所有类型 - /// - /// 输出每个成员执行 - /// 过滤此dll,不含后缀 - public static void AppDomainGetTypes(Action action, string? dllNameWithoutExtension = null) - { -#if DEBUG - int error = 0; -#endif - try - { - var assemblies = AppDomain.CurrentDomain.GetAssemblies(); -#if !NET35 - // cad2021出现如下报错 - // System.NotSupportedException:动态程序集中不支持已调用的成员 - // assemblies = assemblies.Where(p => !p.IsDynamic).ToArray();// 这个要容器类型转换 - assemblies = Array.FindAll(assemblies, p => !p.IsDynamic); -#endif - // 主程序域 - for (int ii = 0; ii < assemblies.Length; ii++) - { - var assembly = assemblies[ii]; - - // 获取类型集合,反射时候还依赖其他的dll就会这个错误 - // 此通讯库要跳过,否则会报错. - var location = Path.GetFileNameWithoutExtension(assembly.Location); - if (dllNameWithoutExtension != null && location != dllNameWithoutExtension) - continue; - if (location == "AcInfoCenterConn")// 通讯库 - continue; - - Type[]? types = null; - try - { - types = assembly.GetTypes(); - } - catch (ReflectionTypeLoadException) { continue; } - - if (types is null) - continue; - - for (int jj = 0; jj < types.Length; jj++) - { - var type = types[jj]; - if (type is not null) - { -#if DEBUG - ++error; -#endif - action(type); - } - } - } - } - catch (System.Exception e) - { -#if DEBUG - Debug.WriteLine($"出错:{nameof(AppDomainGetTypes)};计数{error};错误信息:{e.Message}"); - Debugger.Break(); -#endif - } - } - - /// - /// 收集接口下的函数 - /// - /// 储存要运行的方法 - /// 查找方法名 - /// - void GetInterfaceFunctions(List runClassList, string methodName) - { - const string sqid = nameof(Sequence) + "Id"; - - AppDomainGetTypes(type => { - if (type.IsAbstract) - return; - - var ints = type.GetInterfaces(); - for (int sss = 0; sss < ints.Length; sss++) - { - var inters = ints[sss]; - if (inters.Name != nameof(IFoxAutoGo)) - continue; - - Sequence? sequence = null; - MethodInfo? initialize = null; - - var mets = type.GetMethods(); - for (int jj = 0; jj < mets.Length; jj++) - { - var method = mets[jj]; - if (method.IsAbstract) - continue; - - if (method.Name == sqid) - { - var obj = method.Invoke(); - if (obj is not null) - sequence = (Sequence)obj; - continue; - } - else if (method.Name == methodName) - initialize = method; - - if (initialize is not null && sequence is not null) - break; - } - - if (initialize is null) - continue; - - var seq = sequence is null ? Sequence.Last : sequence.Value; - runClassList.Add(new RunClass(initialize, seq)); - break; - } - }, _dllName); - } - - /// - /// 收集特性下的函数 - /// - void GetAttributeFunctions(List initialize, List terminate) - { - AppDomainGetTypes(type => { - if (type.IsAbstract) - return; - - var mets = type.GetMethods(); - for (int ii = 0; ii < mets.Length; ii++) - { - var method = mets[ii]; - var attr = method.GetCustomAttributes(true); - for (int jj = 0; jj < attr.Length; jj++) - { - if (attr[jj] is IFoxInitialize jjAtt) - { - var runc = new RunClass(method, jjAtt.SequenceId); - if (jjAtt.IsInitialize) - initialize.Add(runc); - else - terminate.Add(runc); - break; - } - } - } - }, _dllName); - } - - /// - /// 执行收集到的函数 - /// - static void RunFunctions(List runClassList) - { - for (int i = 0; i < runClassList.Count; i++) - runClassList[i].Run(); - runClassList.Clear(); - } -} \ No newline at end of file diff --git a/src/IFoxCAD.Cad/Runtime/Log.cs b/src/IFoxCAD.Cad/Runtime/Log.cs deleted file mode 100644 index fe05b5a..0000000 --- a/src/IFoxCAD.Cad/Runtime/Log.cs +++ /dev/null @@ -1,453 +0,0 @@ -namespace IFoxCAD.Cad; - -using System; -using System.Diagnostics; -using System.Threading; -using Exception = Exception; - -#region 写入日志到不同的环境中 -// https://zhuanlan.zhihu.com/p/338492989 -public abstract class LogBase -{ - public abstract void DeleteLog(); - public abstract string[] ReadLog(); - public abstract void WriteLog(string message); -} - -/// -/// 日志输出环境 -/// -public enum LogTarget -{ - /// - /// 文件(包含错误和备注) - /// - File = 1, - /// - /// 文件(不包含错误,也就是只写备注信息) - /// - FileNotException = 2, - /// - /// 数据库 - /// - Database = 4, - /// - /// windows日志 - /// - EventLog = 8, -} - -/// -/// 写入到文件中 -/// -public class FileLogger : LogBase -{ - public override void DeleteLog() - { - File.Delete(LogHelper.LogAddress); - } - public override string[] ReadLog() - { - List lines = new(); - using (var sr = new StreamReader(LogHelper.LogAddress, true/*自动识别文件头*/)) - { - string line; - while ((line = sr.ReadLine()) != null) - lines.Add(line); - } - return lines.ToArray(); - } - public override void WriteLog(string? message) - { - // 把异常信息输出到文件 - var sw = new StreamWriter(LogHelper.LogAddress, true/*当天日志文件存在就追加,否则就创建*/); - sw.Write(message); - sw.Flush(); - sw.Close(); - sw.Dispose(); - } -} - -/// -/// 写入到数据库(暂时不支持) -/// -public class DBLogger : LogBase -{ - public override void DeleteLog() - { - throw new NotImplementedException(); - } - public override string[] ReadLog() - { - throw new NotImplementedException(); - } - public override void WriteLog(string? message) - { - throw new NotImplementedException(); - } -} - -/// -/// 写入到win日志 -/// -public class EventLogger : LogBase -{ - // 需要win权限 - // https://blog.csdn.net/weixin_38208401/article/details/77870909 - // NET50要加 - // https://docs.microsoft.com/en-us/answers/questions/526018/windows-event-log-with-net-5.html - - public string LogName = "IFoxCadLog"; - public override void DeleteLog() - { -#if !NET5_0 && !NET6_0 - if (EventLog.Exists(LogName)) - EventLog.Delete(LogName); -#endif - } - public override string[] ReadLog() - { - List lines = new(); -#if !NET5_0 && !NET6_0 - try - { - EventLog eventLog = new() - { - Log = LogName - }; - foreach (EventLogEntry entry in eventLog.Entries) - lines.Add(entry.Message); - } - catch (System.Security.SecurityException e) - { - throw new Exception("您没有权限读取win日志::" + e.Message); - } -#endif - return lines.ToArray(); - } - public override void WriteLog(string? message) - { -#if !NET5_0 && !NET6_0 - try - { - EventLog eventLog = new() - { - Source = LogName - }; - eventLog.WriteEntry(message, EventLogEntryType.Information); - } - catch (System.Security.SecurityException e) - { - throw new Exception("您没有权限写入win日志::" + e.Message); - } -#endif - } -} - -#endregion - -#region 静态方法 -public static class LogHelper -{ -#pragma warning disable CA2211 // 非常量字段应当不可见 - /// - /// 日志文件完整路径 - /// - public static string? LogAddress; - /// - /// 输出错误信息到日志文件的开关 - /// - public static bool FlagOutFile = false; - /// - /// 输出错误信息到vs输出窗口的开关 - /// - public static bool FlagOutVsOutput = true; -#pragma warning restore CA2211 // 非常量字段应当不可见 - - /// - /// 读写锁 - /// 当资源处于写入模式时,其他线程写入需要等待本次写入结束之后才能继续写入 - /// - static readonly ReaderWriterLockSlim _logWriteLock = new(); - - /// - /// 提供给外部设置log文件保存路径 - /// - /// null就生成默认配置 - public static void OptionFile(string? newlogAddress = null) - { - _logWriteLock.EnterWriteLock();// 写模式锁定 读写锁 - try - { - LogAddress = newlogAddress; - if (string.IsNullOrEmpty(LogAddress)) - LogAddress = GetDefaultOption(DateTime.Now.ToString("yy-MM-dd") + ".log"); - } - finally - { - _logWriteLock.ExitWriteLock();// 解锁 读写锁 - } - } - - /// - /// 输入文件名,获取保存路径的完整路径 - /// - /// 文件名,null获取默认路径 - /// 创建路径 - /// 完整路径 - public static string GetDefaultOption(string fileName, bool createDirectory = true) - { - // 微软回复:静态构造函数只会被调用一次, - // 并且在它执行完成之前,任何其它线程都不能创建这个类的实例或使用这个类的静态成员 - // https://blog.csdn.net/weixin_34204722/article/details/90095812 - var sb = new StringBuilder(); - sb.Append(Environment.CurrentDirectory); - sb.Append("\\ErrorLog"); - - // 新建文件夹 - if (createDirectory) - { - var path = sb.ToString(); - if (!Directory.Exists(path)) - { - // 设置文件夹属性为普通 - Directory.CreateDirectory(path) - .Attributes = FileAttributes.Normal; - } - } - sb.Append('\\'); - sb.Append(fileName); - return sb.ToString(); - } - - public static string WriteLog(this string? message, - LogTarget target = LogTarget.File) - { - if (message == null) - return string.Empty; - return LogAction(null, message, target); - } - - public static string WriteLog(this Exception? exception, - LogTarget target = LogTarget.File) - { - if (exception == null) - return string.Empty; - return LogAction(exception, null, target); - } - - public static string WriteLog(this Exception? exception, string? message, - LogTarget target = LogTarget.File) - { - if (exception == null) - return string.Empty; - return LogAction(exception, message, target); - } - - - /// 错误 - /// 备注信息 - /// 记录方式 - static string LogAction(Exception? ex, - string? message, - LogTarget target) - { - if (ex == null && message == null) - return string.Empty; - - if (LogAddress == null) - { - if (target == LogTarget.File || - target == LogTarget.FileNotException) - OptionFile(); - } - - // 不写入错误 - if (target == LogTarget.FileNotException) - ex = null; - - try - { - _logWriteLock.EnterWriteLock();// 写模式锁定 读写锁 - - var logtxt = new LogTxt(ex, message); - // var logtxtJson = Newtonsoft.Json.JsonConvert.SerializeObject(logtxt, Formatting.Indented); - var logtxtJson = logtxt?.ToString(); - if (logtxtJson == null) - return string.Empty; - - if (FlagOutFile) - { - LogBase? logger; - switch (target) - { - case LogTarget.File: - logger = new FileLogger(); - logger.WriteLog(logtxtJson); - break; - case LogTarget.FileNotException: - logger = new FileLogger(); - logger.WriteLog(logtxtJson); - break; - case LogTarget.Database: - logger = new DBLogger(); - logger.WriteLog(logtxtJson); - break; - case LogTarget.EventLog: - logger = new EventLogger(); - logger.WriteLog(logtxtJson); - break; - } - } - - if (FlagOutVsOutput) - { - Debug.WriteLine("错误日志: " + LogAddress); - Debug.Write(logtxtJson); - } - return logtxtJson; - } - finally - { - _logWriteLock.ExitWriteLock();// 解锁 读写锁 - } - } -} -#endregion - -#region 序列化 -[Serializable] -public class LogTxt -{ - public string? 当前时间; - public string? 备注信息; - public string? 异常信息; - public string? 异常对象; - public string? 触发方法; - public string? 调用堆栈; - - public LogTxt() { } - - public LogTxt(Exception? ex, string? message) : this() - { - if (ex == null && message == null) - throw new ArgumentNullException(nameof(ex)); - - // 以不同语言显示日期 - // DateTime.Now.ToString("f", new System.Globalization.CultureInfo("es-ES")) - // DateTime.Now.ToString("f", new System.Globalization.CultureInfo("zh-cn")) - // 为了最小信息熵,所以用这样的格式,并且我喜欢补0 - 当前时间 = DateTime.Now.ToString("yy-MM-dd hh:mm:ss"); - - if (ex != null) - { - 异常信息 = ex.Message; - 异常对象 = ex.Source; - 触发方法 = ex.TargetSite == null ? string.Empty : ex.TargetSite.ToString(); - 调用堆栈 = ex.StackTrace == null ? string.Empty : ex.StackTrace.Trim(); - } - if (message != null) - 备注信息 = message; - } - - /// 为了不引入json的dll,所以这里自己构造 - public override string? ToString() - { - var sb = new StringBuilder(); - sb.Append('{'); - sb.Append(Environment.NewLine); - sb.AppendLine($" \"{nameof(当前时间)}\": \"{当前时间}\""); - sb.AppendLine($" \"{nameof(备注信息)}\": \"{备注信息}\""); - sb.AppendLine($" \"{nameof(异常信息)}\": \"{异常信息}\""); - sb.AppendLine($" \"{nameof(异常对象)}\": \"{异常对象}\""); - sb.AppendLine($" \"{nameof(触发方法)}\": \"{触发方法}\""); - sb.AppendLine($" \"{nameof(调用堆栈)}\": \"{调用堆栈}\""); - sb.Append('}'); - return sb.ToString(); - } -} -#endregion - - -#if false // 最简单的实现 -public static class Log -{ - /// - /// 读写锁 - /// 当资源处于写入模式时,其他线程写入需要等待本次写入结束之后才能继续写入 - /// - static readonly ReaderWriterLockSlim _logWriteLock = new(); - - /// - /// 日志文件完整路径 - /// - static readonly string _logAddress; - - static Log() - { - // 微软回复:静态构造函数只会被调用一次, - // 并且在它执行完成之前,任何其它线程都不能创建这个类的实例或使用这个类的静态成员 - // https://blog.csdn.net/weixin_34204722/article/details/90095812 - var sb = new StringBuilder(); - sb.Append(Environment.CurrentDirectory); - sb.Append("\\ErrorLog"); - - // 新建文件夹 - var path = sb.ToString(); - if (!Directory.Exists(path)) - { - Directory.CreateDirectory(path) - .Attributes = FileAttributes.Normal; // 设置文件夹属性为普通 - } - - sb.Append('\\'); - sb.Append(DateTime.Now.ToString("yy-MM-dd")); - sb.Append(".log"); - _logAddress = sb.ToString(); - } - - - /// - /// 将异常打印到日志文件 - /// - /// 异常 - /// 备注 - /// DEBUG模式打印到vs输出窗口 - public static string? WriteLog(this Exception? ex, - string? remarks = null, - bool printDebugWindow = true) - { - try - { - _logWriteLock.EnterWriteLock();// 写模式锁定 读写锁 - - var logtxt = new LogTxt(ex, remarks); - // var logtxtJson = Newtonsoft.Json.JsonConvert.SerializeObject(logtxt, Formatting.Indented); - var logtxtJson = logtxt.ToString(); - - if (logtxtJson == null) - return string.Empty; - - // 把异常信息输出到文件 - var sw = new StreamWriter(_logAddress, true/*当天日志文件存在就追加,否则就创建*/); - sw.Write(logtxtJson); - sw.Flush(); - sw.Close(); - sw.Dispose(); - - if (printDebugWindow) - { - Debug.WriteLine("错误日志: " + _logAddress); - Debug.Write(logtxtJson); - // Debugger.Break(); - // Debug.Assert(false, "终止进程"); - } - return logtxtJson; - } - finally - { - _logWriteLock.ExitWriteLock();// 解锁 读写锁 - } - } -} -#endif \ No newline at end of file diff --git a/src/IFoxCAD.Cad/Runtime/MethodInfoHelper.cs b/src/IFoxCAD.Cad/Runtime/MethodInfoHelper.cs deleted file mode 100644 index 5f8e2bc..0000000 --- a/src/IFoxCAD.Cad/Runtime/MethodInfoHelper.cs +++ /dev/null @@ -1,53 +0,0 @@ -namespace IFoxCAD.Cad; - -internal static class MethodInfoHelper -{ - private static readonly Dictionary methodDic = new(); - - /// - /// 执行函数 - /// - /// 函数 - /// 已经外部创建的对象,为空则此处创建 - public static object? Invoke(this MethodInfo methodInfo, object? instance = null) - { - if (methodInfo == null) - throw new ArgumentNullException(nameof(methodInfo)); - - object? result = null; - if (methodInfo.IsStatic) - { - // 新函数指针进入此处 - // 参数数量一定要匹配,为null则参数个数不同导致报错, - // 参数为stirng[],则可以传入object[]代替,其他参数是否还可以实现默认构造? - var args = new List { }; - var paramInfos = methodInfo.GetParameters(); - for (int i = 0; i < paramInfos.Length; i++) - args.Add(null!); - result = methodInfo.Invoke(null, args.ToArray());// 静态调用 - } - else - { - // 原命令的函数指针进入此处 - // object instance; - if (methodDic.ContainsKey(methodInfo)) - instance = methodDic[methodInfo]; - - if (instance == null) - { - var reftype = methodInfo.ReflectedType; - if (reftype == null) return null; - var fullName = reftype.FullName; // 命名空间+类 - if (fullName == null) return null; - var type = reftype.Assembly.GetType(fullName);// 通过程序集反射创建类+ - if (type == null) return null; - instance = Activator.CreateInstance(type); - if (!type.IsAbstract)// 无法创建抽象类成员 - methodDic.Add(methodInfo, instance); - } - if (instance != null) - result = methodInfo.Invoke(instance, null); // 非静态,调用实例化方法 - } - return result; - } -} \ No newline at end of file diff --git a/src/IFoxCAD.Cad/Runtime/SymbolTable.cs b/src/IFoxCAD.Cad/Runtime/SymbolTable.cs deleted file mode 100644 index 8e0efba..0000000 --- a/src/IFoxCAD.Cad/Runtime/SymbolTable.cs +++ /dev/null @@ -1,364 +0,0 @@ -namespace IFoxCAD.Cad; - -public class SymbolTable : IEnumerable - where TTable : SymbolTable - where TRecord : SymbolTableRecord, new() -{ - #region 程序集内部属性 - /// - /// 事务管理器 - /// - internal DBTrans DTrans { get; private set; } - /// - /// 数据库 - /// - internal Database Database { get; private set; } - - #endregion - - #region 公开属性 - /// - /// 当前符号表 - /// - public TTable CurrentSymbolTable { get; private set; } - #endregion - - #region 构造函数 - /// - /// 构造函数,初始化Trans和CurrentSymbolTable属性 - /// - /// 事务管理器 - /// 符号表id - /// 默认行为:例如打开隐藏图层 - internal SymbolTable(DBTrans tr, ObjectId tableId, bool defaultBehavior = true) - { - DTrans = tr; - Database = tr.Database; - CurrentSymbolTable = DTrans.GetObject(tableId)!; - - if (defaultBehavior) - { - if (CurrentSymbolTable is LayerTable layer) - { - // 层表包含隐藏的,全部显示出来 - layer = layer.IncludingHidden; - if (layer is TTable tt) - CurrentSymbolTable = tt; - } - } - } - - #endregion - - #region 索引器 - /// - /// 索引器 - /// - /// 对象名称 - /// 对象的id - public ObjectId this[string key] - { - get - { - if (Has(key)) - return CurrentSymbolTable[key]; - return ObjectId.Null; - } - } - #endregion - - #region Has - /// - /// 判断是否存在符号表记录 - /// - /// 记录名 - /// 存在返回 , 不存在返回 - public bool Has(string key) - { - return CurrentSymbolTable.Has(key); - } - /// - /// 判断是否存在符号表记录 - /// - /// 记录id - /// 存在返回 , 不存在返回 - public bool Has(ObjectId objectId) - { - return CurrentSymbolTable.Has(objectId); - } - #endregion - - #region 添加符号表记录 - /// - /// 添加符号表记录 - /// - /// 符号表记录 - /// 对象id - private ObjectId Add(TRecord record) - { - ObjectId id; - using (CurrentSymbolTable.ForWrite()) - { - id = CurrentSymbolTable.Add(record); - DTrans.Transaction.AddNewlyCreatedDBObject(record, true); - } - return id; - } - /// - /// 添加符号表记录 - /// - /// 符号表记录名 - /// 符号表记录处理函数的无返回值委托 - /// 对象id - public ObjectId Add(string name, Action? action = null) - { - ObjectId id = this[name]; - if (id.IsNull) - { - var record = new TRecord() - { - Name = name - }; - id = Add(record); - using (record.ForWrite()) - action?.Invoke(record); - } - return id; - } - #endregion - - #region 删除符号表记录 - /// - /// 删除符号表记录 - /// - /// 符号表记录对象 - private static void Remove(TRecord record) - { - using (record.ForWrite()) - record.Erase(); - } - - /// - /// 删除符号表记录 - /// - /// 符号表记录名 - public void Remove(string name) - { - var record = GetRecord(name); - if (record is not null) - Remove(record); - } - - /// - /// 删除符号表记录 - /// - /// 符号表记录对象id - public void Remove(ObjectId id) - { - var record = GetRecord(id); - if (record is not null) - Remove(record); - } - #endregion - - #region 修改符号表记录 - /// - /// 修改符号表 - /// - /// 符号表记录 - /// 修改委托 - private static void Change(TRecord record, Action action) - { - using (record.ForWrite()) - action.Invoke(record); - // 调用regen()函数可能会导致卡顿 - // Env.Editor.Regen(); - } - - /// - /// 修改符号表 - /// - /// 符号表记录名 - /// 修改委托 - public void Change(string name, Action action) - { - var record = GetRecord(name); - if (record is not null) - Change(record, action); - } - - /// - /// 修改符号表 - /// - /// 符号表记录id - /// 修改委托 - public void Change(ObjectId id, Action action) - { - var record = GetRecord(id); - if (record is not null) - Change(record, action); - } - #endregion - - #region 获取符号表记录 - /// - /// 获取符号表记录 - /// - /// 符号表记录的id - /// 打开模式,默认为只读 - /// 符号表记录 - public TRecord? GetRecord(ObjectId id, OpenMode openMode = OpenMode.ForRead) => /*id.IsNull ? null : */DTrans.GetObject(id, openMode); - - /// - /// 获取符号表记录 - /// - /// 符号表记录名 - /// 打开模式,默认为只读 - /// 符号表记录 - public TRecord? GetRecord(string name, OpenMode openMode = OpenMode.ForRead) => GetRecord(this[name], openMode); - - /// - /// 获取符号表记录 - /// - /// 符号表记录集合 - public IEnumerable GetRecords() - { - foreach (var item in this) - { - var record = GetRecord(item); - if (record is not null) - yield return record; - } - } - - /// - /// 获取符号表记录的名字集合 - /// - /// 记录的名字集合 - public IEnumerable GetRecordNames() => GetRecords().Select(record => record.Name); - - /// - /// 获取符合过滤条件的符号表记录名字集合 - /// - /// 过滤器委托 - /// 记录的名字集合 - public IEnumerable GetRecordNames(Func filter) - { - foreach (var item in this) - { - var record = GetRecord(item); - if (record is not null && filter.Invoke(record)) - yield return record.Name; - } - } - - /// - /// 从源数据库拷贝符号表记录 - /// - /// 符号表 - /// 符号表记录名 - /// 是否覆盖, 为覆盖, 为不覆盖 - /// 对象id - public ObjectId GetRecordFrom(SymbolTable table, string name, bool over) - { - if (table is null) - throw new ArgumentNullException(nameof(table), "对象为null"); - - ObjectId rid = this[name]; - bool has = rid != ObjectId.Null; - if ((has && over) || !has) - { - ObjectId id = table[name]; - using IdMapping idm = new(); - using (ObjectIdCollection ids = new() { id }) - { - table.Database. - WblockCloneObjects(ids, - CurrentSymbolTable.Id, - idm, - DuplicateRecordCloning.Replace, - false); - } - rid = idm[id].Value; - } - return rid; - } - - /// - /// 从文件拷贝符号表记录 - /// - /// 符号表过滤器 - /// 文件名 - /// 符号表记录名 - /// 是否覆盖, 为覆盖, 为不覆盖 - /// 对象id - internal ObjectId GetRecordFrom(Func> tableSelector, - string fileName, - string name, - bool over) - { - using var tr = new DBTrans(fileName); - return GetRecordFrom(tableSelector(tr), name, over); - } - #endregion - - #region 遍历 - /// - /// 遍历集合的迭代器,执行action委托 - /// - /// 要运行的委托 - /// 打开模式,默认为只读 - /// 检查id是否删除,默认false - public void ForEach(Action action, - OpenMode openMode = OpenMode.ForRead, - bool checkIdOk = false) - { - foreach (var id in this) - { - if (checkIdOk && !id.IsOk()) - continue; - var record = GetRecord(id, openMode); - if (record is not null) - action(record); - } - } - - - /// - /// 遍历集合的迭代器,执行action委托 - /// - /// 要执行的委托 - /// 打开模式,默认为只读 - /// 检查id是否删除,默认false - public void ForEach(Action action, - OpenMode openMode = OpenMode.ForRead, - bool checkIdOk = false) - { - LoopState state = new();/*这种方式比Action改Func更友好*/ - foreach (var id in this) - { - if (checkIdOk && !id.IsOk()) - continue; - var record = GetRecord(id, openMode); - if (record is not null) - action(record, state); - if (!state.IsRun) - break; - } - } - #endregion - - #region IEnumerable 成员 - - public IEnumerator GetEnumerator() - { - foreach (var id in CurrentSymbolTable) - yield return id; - } - - IEnumerator IEnumerable.GetEnumerator() - { - return GetEnumerator(); - } - #endregion -} \ No newline at end of file diff --git a/src/IFoxCAD.Cad/Runtime/Utils.cs b/src/IFoxCAD.Cad/Runtime/Utils.cs deleted file mode 100644 index 6c668da..0000000 --- a/src/IFoxCAD.Cad/Runtime/Utils.cs +++ /dev/null @@ -1,98 +0,0 @@ -namespace IFoxCAD.Cad; - -using System; - -public class DBTransHelper -{ - /* - * id = db.GetObjectId(false, handle, 0); - * 参数意义: db.GetObjectId(如果没有找到就创建,句柄号,标记..将来备用) - * 在vs的输出会一直抛出: - * 引发的异常:“Autodesk.AutoCAD.Runtime.Exception”(位于 AcdbMgd.dll 中) - * "eUnknownHandle" - * 这就是为什么慢的原因,所以直接运行就好了!而Debug还是需要用arx的API替代. - */ - - [System.Security.SuppressUnmanagedCodeSecurity] - [DllImport("acdb17.dll", CallingConvention = CallingConvention.ThisCall/*08的调用约定 高版本是__cdecl*/, - EntryPoint = "?getAcDbObjectId@AcDbDatabase@@QAE?AW4ErrorStatus@Acad@@AAVAcDbObjectId@@_NABVAcDbHandle@@K@Z")] - extern static int getAcDbObjectId17x32(IntPtr db, out ObjectId id, [MarshalAs(UnmanagedType.U1)] bool createnew, ref Handle h, uint reserved); - - [System.Security.SuppressUnmanagedCodeSecurity] - [DllImport("acdb17.dll", CallingConvention = CallingConvention.ThisCall/*08的调用约定 高版本是__cdecl*/, - EntryPoint = "?getAcDbObjectId@AcDbDatabase@@QEAA?AW4ErrorStatus@Acad@@AEAVAcDbObjectId@@_NAEBVAcDbHandle@@K@Z")] - extern static int getAcDbObjectId17x64(IntPtr db, out ObjectId id, [MarshalAs(UnmanagedType.U1)] bool createnew, ref Handle h, uint reserved); - - [System.Security.SuppressUnmanagedCodeSecurity] - [DllImport("acdb18.dll", CallingConvention = CallingConvention.ThisCall/*08的调用约定 高版本是__cdecl*/, - EntryPoint = "?getAcDbObjectId@AcDbDatabase@@QAE?AW4ErrorStatus@Acad@@AAVAcDbObjectId@@_NABVAcDbHandle@@K@Z")] - extern static int getAcDbObjectId18x32(IntPtr db, out ObjectId id, [MarshalAs(UnmanagedType.U1)] bool createnew, ref Handle h, uint reserved); - - [System.Security.SuppressUnmanagedCodeSecurity] - [DllImport("acdb18.dll", CallingConvention = CallingConvention.ThisCall/*08的调用约定 高版本是__cdecl*/, - EntryPoint = "?getAcDbObjectId@AcDbDatabase@@QEAA?AW4ErrorStatus@Acad@@AEAVAcDbObjectId@@_NAEBVAcDbHandle@@K@Z")] - extern static int getAcDbObjectId18x64(IntPtr db, out ObjectId id, [MarshalAs(UnmanagedType.U1)] bool createnew, ref Handle h, uint reserved); - - /// - /// 句柄转id,NET35(08~12)专用的 - /// - /// 数据库 - /// 句柄 - /// 返回的id - /// 不存在则创建 - /// 保留,用于未来 - /// 成功0,其他值都是错误.可以强转ErrorStatus - static int GetAcDbObjectId(IntPtr db, Handle handle, out ObjectId id, bool createIfNotFound = false, uint reserved = 0) - { - id = ObjectId.Null; - switch (Acap.Version.Major) - { - case 17: - { - if (IntPtr.Size == 4) - return getAcDbObjectId17x32(db, out id, createIfNotFound, ref handle, reserved); - else - return getAcDbObjectId17x64(db, out id, createIfNotFound, ref handle, reserved); - } - case 18: - { - if (IntPtr.Size == 4) - return getAcDbObjectId18x32(db, out id, createIfNotFound, ref handle, reserved); - else - return getAcDbObjectId18x64(db, out id, createIfNotFound, ref handle, reserved); - } - } - return -1; - } - - /// - /// 句柄转id - /// - /// 数据库 - /// 句柄 - /// id - public static ObjectId TryGetObjectId(Database db, Handle handle) - { -#if !NET35 - // 高版本直接利用 - var es = db.TryGetObjectId(handle, out ObjectId id); - // if (!es) -#else - var es = GetAcDbObjectId(db.UnmanagedObject, handle, out ObjectId id); - // if (ErrorStatus.OK != (ErrorStatus)es) -#endif - return id; - } - - // public static int GetCadFileVersion(string filename) - // { - // var bytes = File.ReadAllBytes(filename); - // var headstr = Encoding.Default.GetString(bytes)[0..6]; - // if (!headstr.StartsWith("AC")) return 0; - // var vernum = int.Parse(headstr.Replace("AC", "")); - // var a = Enum.Parse(typeof(DwgVersion), "AC1800"); - // Enum.TryParse() - // return vernum + 986; - - // } -} \ No newline at end of file diff --git a/src/IFoxCAD.Cad/SelectionFilter/OpComp.cs b/src/IFoxCAD.Cad/SelectionFilter/OpComp.cs deleted file mode 100644 index 9727b72..0000000 --- a/src/IFoxCAD.Cad/SelectionFilter/OpComp.cs +++ /dev/null @@ -1,79 +0,0 @@ -namespace IFoxCAD.Cad; - -/// -/// 比较运算符类 -/// -public class OpComp : OpEqual -{ - /// - /// 比较运算符,如: - /// "<=" - /// 以及合并比较运算符: - /// "<=,<=,=" - /// - public string Content { get; } - - /// - /// 符号名 - /// - public override string Name - { - get { return "Comp"; } - } - - /// - /// 比较运算符类构造函数 - /// - /// 运算符 - /// 数据 - public OpComp(string content, TypedValue value) - : base(value) - { - Content = content; - } - - /// - /// 比较运算符类构造函数 - /// - /// 运算符 - /// 组码 - public OpComp(string content, int code) - : base(code) - { - Content = content; - } - - /// - /// 比较运算符类构造函数 - /// - /// 运算符 - /// 组码 - /// 组码值 - public OpComp(string content, int code, object value) - : base(code, value) - { - Content = content; - } - - /// - /// 比较运算符类构造函数 - /// - /// 运算符 - /// 组码 - /// 组码值 - public OpComp(string content, DxfCode code, object value) - : base(code, value) - { - Content = content; - } - - /// - /// 获取过滤器数据迭代器 - /// - /// TypedValue迭代器 - public override IEnumerable GetValues() - { - yield return new TypedValue(-4, Content); - yield return Value; - } -} diff --git a/src/IFoxCAD.Cad/SelectionFilter/OpEqual.cs b/src/IFoxCAD.Cad/SelectionFilter/OpEqual.cs deleted file mode 100644 index 370a0c1..0000000 --- a/src/IFoxCAD.Cad/SelectionFilter/OpEqual.cs +++ /dev/null @@ -1,86 +0,0 @@ -namespace IFoxCAD.Cad; - -/// -/// 相等运算符类 -/// -public class OpEqual : OpFilter -{ - /// - /// 组码与匹配值的TypedValue类型值 - /// - public TypedValue Value { get; private set; } - - /// - /// 符号名 - /// - public override string Name - { - get { return "Equal"; } - } - - /// - /// 相等运算符类构造函数 - /// - /// 组码 - public OpEqual(int code) - { - Value = new TypedValue(code); - } - - /// - /// 相等运算符类构造函数 - /// - /// 组码 - /// 组码值 - public OpEqual(int code, object value) - { - Value = new TypedValue(code, value); - } - - /// - /// 相等运算符类构造函数 - /// - /// 组码 - /// 组码值 - public OpEqual(DxfCode code, object value) - { - Value = new TypedValue((int)code, value); - } - - /// - /// 相等运算符类构造函数 - /// - /// 组码与组码值的TypedValue类型值 - internal OpEqual(TypedValue value) - { - Value = value; - } - - /// - /// 过滤器数据迭代器 - /// - /// TypedValue迭代器 - public override IEnumerable GetValues() - { - yield return Value; - } - - /// - /// 设置数据 - /// - /// 组码值 - public void SetValue(object value) - { - Value = new TypedValue(Value.TypeCode, value); - } - - /// - /// 设置数据 - /// - /// 组码 - /// 组码值 - public void SetValue(int code, object value) - { - Value = new TypedValue(code, value); - } -} diff --git a/src/IFoxCAD.Cad/SelectionFilter/OpFilter.cs b/src/IFoxCAD.Cad/SelectionFilter/OpFilter.cs deleted file mode 100644 index 48843f4..0000000 --- a/src/IFoxCAD.Cad/SelectionFilter/OpFilter.cs +++ /dev/null @@ -1,341 +0,0 @@ -namespace IFoxCAD.Cad; - -/// -/// 选择集过滤器抽象类 -/// -public abstract class OpFilter -{ - /// - /// 过滤器的名字 - /// - public abstract string Name { get; } - - /// - /// 只读属性,表示这个过滤器取反 - /// - public OpFilter Not => new OpNot(this); - - /// - /// 获取TypedValue类型的值的迭代器的抽象方法,子类必须重写 - /// - /// TypedValue迭代器 - public abstract IEnumerable GetValues(); - - /// - /// 非操作符,返回的是OpFilter类型变量的 属性 - /// - /// OpFilter类型变量 - /// OpFilter对象 - public static OpFilter operator !(OpFilter item) - { - return item.Not; - } - - /// - /// 过滤器值转换为 TypedValue 类型数组 - /// - /// TypedValue数组 - public TypedValue[] ToArray() - { - return GetValues().ToArray(); - } - - /// - /// 隐式类型转换,将自定义的过滤器转换为 Autocad 认识的选择集过滤器 - /// - /// 过滤器对象 - /// - /// 选择集过滤器. - /// - public static implicit operator SelectionFilter(OpFilter item) - { - return new SelectionFilter(item.ToArray()); - } - - /// - /// 转换为字符串 - /// - /// 字符串 - public override string ToString() - { - var sb = new StringBuilder(); - foreach (var value in GetValues()) - sb.Append(value); - return sb.ToString(); - } - - /// - /// 构建过滤器 - /// - /// - /// 举两个利用构建函数创建选择集过滤的例子 - /// - /// !(e.Dxf(0) == "line" & e.Dxf(8) == "0") - /// | e.Dxf(0) != "circle" & e.Dxf(8) == "2" & e.Dxf(10) >= p); - /// - /// 例子2: - /// var f2 = OpFilter.Build( - /// e => e.Or( - /// !e.And(e.Dxf(0) == "line", e.Dxf(8) == "0"), - /// e.And(e.Dxf(0) != "circle", e.Dxf(8) == "2", - /// e.Dxf(10) <= new Point3d(10, 10, 0)))); - /// ]]> - /// - /// 构建过滤器的函数委托 - /// 过滤器 - public static OpFilter Build(Func func) - { - return func(new Op()).Filter!; - } - - #region Operator - - /// - /// 过滤器操作符类 - /// - public class Op - { - /// - /// 过滤器属性 - /// - internal OpFilter? Filter { get; private set; } - - internal Op() { } - - private Op(OpFilter filter) - { - Filter = filter; - } - - /// - /// AND 操作符 - /// - /// 操作符类型的可变参数 - /// Op对象 -#pragma warning disable CA1822 // 将成员标记为 static - public Op And(params Op[] args) -#pragma warning restore CA1822 // 将成员标记为 static - { - var filter = new OpAnd(); - foreach (var op in args) - filter.Add(op.Filter!); - return new Op(filter); - } - - /// - /// or 操作符 - /// - /// 操作符类型的可变参数 - /// Op对象 -#pragma warning disable CA1822 // 将成员标记为 static - public Op Or(params Op[] args) -#pragma warning restore CA1822 // 将成员标记为 static - { - var filter = new OpOr(); - foreach (var op in args) - filter.Add(op.Filter!); - return new Op(filter); - } - - /// - /// dxf 操作符,此函数只能用于过滤器中,不是组码操作函数 - /// - /// 组码 - /// Op对象 -#pragma warning disable CA1822 // 将成员标记为 static - public Op Dxf(int code) -#pragma warning restore CA1822 // 将成员标记为 static - { - return new Op(new OpEqual(code)); - } - - /// - /// dxf 操作符,此函数只能用于过滤器中,不是组码操作函数 - /// - /// 组码 - /// 关系运算符的值,比如">,>,=" - /// Op对象 -#pragma warning disable CA1822 // 将成员标记为 static - public Op Dxf(int code, string content) -#pragma warning restore CA1822 // 将成员标记为 static - { - return new Op(new OpComp(content, code)); - } - - /// - /// 非操作符 - /// - /// 过滤器操作符对象 - /// Op对象 - public static Op operator !(Op right) - { - right.Filter = !right.Filter!; - return right; - } - - /// - /// 相等操作符 - /// - /// 过滤器操作符对象 - /// 数据 - /// Op对象 - public static Op operator ==(Op left, object right) - { - var eq = (OpEqual)left.Filter!; - eq.SetValue(right); - return left; - } - - /// - /// 不等操作符 - /// - /// 过滤器操作符对象 - /// 数据 - /// Op对象 - public static Op operator !=(Op left, object right) - { - var eq = (OpEqual)left.Filter!; - eq.SetValue(right); - left.Filter = eq.Not; - return left; - } - - private static Op GetCompOp(string content, Op left, object right) - { - var eq = (OpEqual)left.Filter!; - var comp = new OpComp(content, eq.Value.TypeCode, right); - return new Op(comp); - } - - /// - /// 大于操作符 - /// - /// 过滤器操作符对象 - /// 数据 - /// Op对象 - public static Op operator >(Op left, object right) - { - return GetCompOp(">", left, right); - } - - /// - /// 小于操作符 - /// - /// 过滤器操作符对象 - /// 数据 - /// Op对象 - public static Op operator <(Op left, object right) - { - return GetCompOp("<", left, right); - } - - /// - /// 大于等于操作符 - /// - /// 过滤器操作符对象 - /// 数据 - /// Op对象 - public static Op operator >=(Op left, object right) - { - return GetCompOp(">=", left, right); - } - - /// - /// 小于等于操作符 - /// - /// 过滤器操作符对象 - /// 数据 - /// Op对象 - public static Op operator <=(Op left, object right) - { - return GetCompOp("<=", left, right); - } - - /// - /// 大于等于操作符 - /// - /// 过滤器操作符对象 - /// 点 - /// Op对象 - public static Op operator >=(Op left, Point3d right) - { - return GetCompOp(">,>,*", left, right); - } - - /// - /// 小于等于操作符 - /// - /// 过滤器操作符对象 - /// 点 - /// Op对象 - public static Op operator <=(Op left, Point3d right) - { - return GetCompOp("<,<,*", left, right); - } - - /// - /// 并操作符 - /// - /// 过滤器操作符对象 - /// 过滤器操作符对象 - /// Op对象 - public static Op operator &(Op left, Op right) - { - var filter = new OpAnd - { - left.Filter!, - right.Filter! - }; - return new Op(filter); - } - - /// - /// 或操作符 - /// - /// 过滤器操作符对象 - /// 过滤器操作符对象 - /// Op对象 - public static Op operator |(Op left, Op right) - { - var filter = new OpOr - { - left.Filter!, - right.Filter! - }; - return new Op(filter); - } - - /// - /// 异或操作符 - /// - /// 过滤器操作符对象 - /// 过滤器操作符对象 - /// Op对象 - public static Op operator ^(Op left, Op right) - { - var filter = new OpXor(left.Filter!, right.Filter!); - return new Op(filter); - } - - /// - /// 比较函数 - /// - /// 对象 - /// - /// 是否相等 - /// - public override bool Equals(object obj) => base.Equals(obj); - - /// - /// 获取HashCode - /// - /// HashCode - public override int GetHashCode() => base.GetHashCode(); - } - - #endregion Operator -} diff --git a/src/IFoxCAD.Cad/SelectionFilter/OpList.cs b/src/IFoxCAD.Cad/SelectionFilter/OpList.cs deleted file mode 100644 index 3a02513..0000000 --- a/src/IFoxCAD.Cad/SelectionFilter/OpList.cs +++ /dev/null @@ -1,166 +0,0 @@ -namespace IFoxCAD.Cad; - -/// -/// 逻辑操作符的列表抽象类 -/// -public abstract class OpList : OpLogi -{ - /// - /// 过滤器列表 - /// - protected List _lst = new(); - - /// - /// 添加过滤器条件的虚函数,子类可以重写 - /// - /// 举个利用这个类及其子类创建选择集过滤的例子 - /// - /// ,>,*" } - /// }, - /// }; - /// ]]> - /// - /// 过滤器对象 - public virtual void Add(OpFilter value) - { - _lst.Add(value); - } - - /// - /// 添加过滤条件 - /// - /// 逻辑非~ - /// 组码 - /// 组码值 - public void Add(string speccode, int code, object value) - { - if (speccode == "~") - _lst.Add(new OpEqual(code, value).Not); - } - - /// - /// 添加过滤条件 - /// - /// 组码 - /// 组码值 - public void Add(int code, object value) - { - _lst.Add(new OpEqual(code, value)); - } - - /// - /// 添加过滤条件 - /// - /// 组码 - /// 组码值 - public void Add(DxfCode code, object value) - { - _lst.Add(new OpEqual(code, value)); - } - - /// - /// 添加过滤条件 - /// - /// 组码 - /// 组码值 - /// 比较运算符 - public void Add(int code, object value, string comp) - { - _lst.Add(new OpComp(comp, code, value)); - } - - /// - /// 添加过滤条件 - /// - /// 组码 - /// 组码值 - /// 比较运算符 - public void Add(DxfCode code, object value, string comp) - { - _lst.Add(new OpComp(comp, code, value)); - } - - /// - /// 过滤器迭代器 - /// - /// OpFilter迭代器 - public override IEnumerator GetEnumerator() - { - foreach (var value in _lst) - yield return value; - } -} - -/// -/// 逻辑与类 -/// -public class OpAnd : OpList -{ - /// - /// 符号名 - /// - public override string Name - { - get { return "And"; } - } - - /// - /// 添加过滤条件 - /// - /// 过滤器对象 - public override void Add(OpFilter value) - { - if (value is OpAnd opand) - { - foreach (var item in opand) - _lst.Add(item); - } - else - { - _lst.Add(value); - } - } -} - -/// -/// 逻辑或类 -/// -public class OpOr : OpList -{ - /// - /// 符号名 - /// - public override string Name - { - get { return "Or"; } - } - - /// - /// 添加过滤条件 - /// - /// 过滤器对象 - public override void Add(OpFilter value) - { - if (value is OpOr opor) - { - foreach (var item in opor) - _lst.Add(item); - } - else - { - _lst.Add(value); - } - } -} diff --git a/src/IFoxCAD.Cad/SelectionFilter/OpLogi.cs b/src/IFoxCAD.Cad/SelectionFilter/OpLogi.cs deleted file mode 100644 index 4227608..0000000 --- a/src/IFoxCAD.Cad/SelectionFilter/OpLogi.cs +++ /dev/null @@ -1,128 +0,0 @@ -namespace IFoxCAD.Cad; - -/// -/// 过滤器逻辑运算符抽象类 -/// -public abstract class OpLogi : OpFilter, IEnumerable -{ - /// - /// 返回-4组码的开始内容 - /// - public TypedValue First - { - get { return new TypedValue(-4, $"<{Name}"); } - } - - /// - /// 返回-4组码的结束内容 - /// - public TypedValue Last - { - get { return new TypedValue(-4, $"{Name}>"); } - } - - /// - /// 获取过滤条件 - /// - /// TypedValue迭代器 - public override IEnumerable GetValues() - { - yield return First; - foreach (var item in this) - { - foreach (var value in item.GetValues()) - yield return value; - } - yield return Last; - } - - /// - /// 获取迭代器 - /// - /// OpFilter迭代器 - public abstract IEnumerator GetEnumerator(); - - IEnumerator IEnumerable.GetEnumerator() - { - return GetEnumerator(); - } -} - -/// -/// 逻辑非类 -/// -public class OpNot : OpLogi -{ - private OpFilter Value { get; } - - /// - /// 逻辑非类构造函数 - /// - /// OpFilter数据 - public OpNot(OpFilter value) - { - Value = value; - } - - /// - /// 符号名 - /// - public override string Name - { - get { return "Not"; } - } - - /// - /// 获取迭代器 - /// - /// OpFilter迭代器 - public override IEnumerator GetEnumerator() - { - yield return Value; - } -} - -/// -/// 逻辑异或类 -/// -public class OpXor : OpLogi -{ - /// - /// 左操作数 - /// - public OpFilter Left { get; } - - /// - /// 右操作数 - /// - public OpFilter Right { get; } - - /// - /// 逻辑异或类构造函数 - /// - /// 左操作数 - /// 右操作数 - public OpXor(OpFilter left, OpFilter right) - { - Left = left; - Right = right; - } - - /// - /// 符号名 - /// - public override string Name - { - get { return "Xor"; } - } - - /// - /// 获取迭代器 - /// - /// 选择集过滤器类型迭代器 - public override IEnumerator GetEnumerator() - { - yield return Left; - yield return Right; - } -} diff --git a/src/IFoxCAD.WPF/Converter.cs b/src/IFoxCAD.WPF/Converter.cs deleted file mode 100644 index 828ece9..0000000 --- a/src/IFoxCAD.WPF/Converter.cs +++ /dev/null @@ -1,98 +0,0 @@ -namespace IFoxCAD.WPF; - -/// -/// 字符串到整数的转换器 -/// -public class StringToIntConverter : IValueConverter -{ - /// - /// 字符串转换到整数 - /// - /// 绑定源生成的值 - /// 绑定目标属性的类型 - /// 要使用的转换器参数 - /// 要用在转换器中的区域性 - /// 转换后的值。 如果该方法返回 null,则使用有效的 null 值。 - public object Convert(object value, Type targetType, object parameter, CultureInfo culture) - { - string? a = value as string; - _ = int.TryParse(a, out int b); - return b; - } - /// - /// 整数转换到字符串 - /// - /// 绑定目标生成的值 - /// 要转换为的类型 - /// 要使用的转换器参数 - /// 要用在转换器中的区域性 - /// 转换后的值。 如果该方法返回 null,则使用有效的 null 值。 - public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture) - { - return value.ToString(); - } -} -/// -/// 字符串到实数的转换器 -/// -public class StringToDoubleConverter : IValueConverter -{ - /// - /// 字符串转换到实数 - /// - /// 绑定源生成的值 - /// 绑定目标属性的类型 - /// 要使用的转换器参数 - /// 要用在转换器中的区域性 - /// 转换后的值。 如果该方法返回 null,则使用有效的 null 值。 - public object Convert(object value, Type targetType, object parameter, CultureInfo culture) - { - string? a = value as string; - _ = double.TryParse(a, out double b); - return b; - } - /// - /// 实数转换到字符串 - /// - /// 绑定目标生成的值 - /// 要转换为的类型 - /// 要使用的转换器参数 - /// 要用在转换器中的区域性 - /// 转换后的值。 如果该方法返回 null,则使用有效的 null 值。 - public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture) - { - return value.ToString(); - } -} -/// -/// 整数到字符串的转换器 -/// -public class IntToStringConverter : IValueConverter -{ - /// - /// 整数转换到字符串 - /// - /// 绑定源生成的值 - /// 绑定目标属性的类型 - /// 要使用的转换器参数 - /// 要用在转换器中的区域性 - /// 转换后的值。 如果该方法返回 null,则使用有效的 null 值。 - public object Convert(object value, Type targetType, object parameter, CultureInfo culture) - { - return value.ToString(); - } - /// - /// 字符串转换到整数 - /// - /// 绑定目标生成的值 - /// 要转换为的类型 - /// 要使用的转换器参数 - /// 要用在转换器中的区域性 - /// 转换后的值。 如果该方法返回 null,则使用有效的 null 值。 - public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture) - { - string? a = value as string; - _ = int.TryParse(a, out int b); - return b; - } -} diff --git a/src/IFoxCAD.WPF/DependencyObjectExtensions.cs b/src/IFoxCAD.WPF/DependencyObjectExtensions.cs deleted file mode 100644 index 596961d..0000000 --- a/src/IFoxCAD.WPF/DependencyObjectExtensions.cs +++ /dev/null @@ -1,34 +0,0 @@ -namespace IFoxCAD.WPF; - -/// -/// 依赖属性扩展类 -/// -public static class DependencyObjectExtensions -{ - /// - /// 获取父对象依赖属性 - /// - /// 子对象 - /// 依赖属性 - public static DependencyObject? GetParentObject(this DependencyObject child) - { - if (child is null) return null; - - if (child is ContentElement contentElement) - { - DependencyObject parent = ContentOperations.GetParent(contentElement); - if (parent is not null) return parent; - - FrameworkContentElement? fce = contentElement as FrameworkContentElement; - return fce?.Parent; - } - - if (child is FrameworkElement frameworkElement) - { - DependencyObject parent = frameworkElement.Parent; - if (parent is not null) return parent; - } - - return VisualTreeHelper.GetParent(child); - } -} diff --git a/src/IFoxCAD.WPF/EventBindingExtension.cs b/src/IFoxCAD.WPF/EventBindingExtension.cs deleted file mode 100644 index 7352143..0000000 --- a/src/IFoxCAD.WPF/EventBindingExtension.cs +++ /dev/null @@ -1,210 +0,0 @@ -namespace IFoxCAD.WPF; - -/// -/// 事件绑定标签类 -/// -/// -public class EventBindingExtension : MarkupExtension -{ - /// - /// 命令属性 - /// - public string? Command { get; set; } - /// - /// 命令参数属性 - /// - public string? CommandParameter { get; set; } - /// - /// 当在派生类中实现时,返回用作此标记扩展的目标属性值的对象。 - /// - /// 可为标记扩展提供服务的服务提供程序帮助程序。 - /// - /// 要在应用了扩展的属性上设置的对象值。 - /// - /// - /// - public override object? ProvideValue(IServiceProvider serviceProvider) - { - if (serviceProvider is null) - { - throw new ArgumentNullException(nameof(serviceProvider)); - } - if (serviceProvider.GetService(typeof(IProvideValueTarget)) is not IProvideValueTarget targetProvider) - { - throw new InvalidOperationException(); - } - - if (targetProvider.TargetObject is not FrameworkElement targetObject) - { - throw new InvalidOperationException(); - } - - if (targetProvider.TargetProperty is not MemberInfo memberInfo) - { - throw new InvalidOperationException(); - } - - if (string.IsNullOrWhiteSpace(Command)) - { - Command = memberInfo.Name.Replace("Add", ""); - if (Command.Contains("Handler")) - { - Command = Command.Replace("Handler", "Command"); - } - else - { - Command += "Command"; - } - } - - return CreateHandler(memberInfo, Command!, targetObject.GetType()); - } - - private Type? GetEventHandlerType(MemberInfo memberInfo) - { - Type? eventHandlerType = null; - if (memberInfo is EventInfo eventInfo) - { - // var info = memberInfo as EventInfo; - // var eventInfo = info; - eventHandlerType = eventInfo.EventHandlerType; - } - else if (memberInfo is MethodInfo methodInfo) - { - // var info = memberInfo as MethodInfo; - // var methodInfo = info; - ParameterInfo[] pars = methodInfo.GetParameters(); - eventHandlerType = pars[1].ParameterType; - } - - return eventHandlerType; - } - -#pragma warning disable IDE0060 // 删除未使用的参数 - private object? CreateHandler(MemberInfo memberInfo, string cmdName, Type targetType) -#pragma warning restore IDE0060 // 删除未使用的参数 - { - Type? eventHandlerType = GetEventHandlerType(memberInfo); - - if (eventHandlerType is null) return null; - - var handlerInfo = eventHandlerType.GetMethod("Invoke"); - var method = new DynamicMethod("", handlerInfo.ReturnType, - new Type[] - { - handlerInfo.GetParameters()[0].ParameterType, - handlerInfo.GetParameters()[1].ParameterType, - }); - - var gen = method.GetILGenerator(); - gen.Emit(OpCodes.Ldarg, 0); - gen.Emit(OpCodes.Ldarg, 1); - gen.Emit(OpCodes.Ldstr, cmdName); - if (CommandParameter is null) - { - gen.Emit(OpCodes.Ldnull); - } - else - { - gen.Emit(OpCodes.Ldstr, CommandParameter); - } - gen.Emit(OpCodes.Call, getMethod); - gen.Emit(OpCodes.Ret); - - return method.CreateDelegate(eventHandlerType); - } - - static readonly MethodInfo getMethod = typeof(EventBindingExtension).GetMethod("HandlerIntern", new Type[] { typeof(object), typeof(object), typeof(string), typeof(string) }); - -#pragma warning disable IDE0051 // 删除未使用的私有成员 - static void Handler(object sender, object args) -#pragma warning restore IDE0051 // 删除未使用的私有成员 - { - HandlerIntern(sender, args, "cmd", null); - } - /// - /// Handlers the intern. - /// - /// The sender. - /// The arguments. - /// Name of the command. - /// The command parameter. - public static void HandlerIntern(object sender, object args, string cmdName, string? commandParameter) - { - if (sender is FrameworkElement fe) - { - var cmd = GetCommand(fe, cmdName); - object? commandParam = null; - if (!string.IsNullOrWhiteSpace(commandParameter)) - { - commandParam = GetCommandParameter(fe, args, commandParameter!); - } - if ((cmd is not null) && cmd.CanExecute(commandParam)) - { - cmd.Execute(commandParam); - } - } - } - - internal static ICommand? GetCommand(FrameworkElement target, string cmdName) - { - var vm = FindViewModel(target); - if (vm is null) return null; - - var vmType = vm.GetType(); - var cmdProp = vmType.GetProperty(cmdName); - if (cmdProp is not null) - { - return cmdProp.GetValue(vm) as ICommand; - } -#if DEBUG - throw new Exception("EventBinding path error: '" + cmdName + "' property not found on '" + vmType + "' 'DelegateCommand'"); -#endif - - -#pragma warning disable CS0162 // 检测到无法访问的代码 - return null; -#pragma warning restore CS0162 // 检测到无法访问的代码 - } - - internal static object GetCommandParameter(FrameworkElement target, object args, string commandParameter) - { - var classify = commandParameter.Split('.'); - object ret = classify[0] switch - { - "$e" => args, - "$this" => classify.Length > 1 ? FollowPropertyPath(target, commandParameter.Replace("$this.", ""), target.GetType()) : target, - _ => commandParameter, - }; - return ret; - } - - internal static ViewModelBase? FindViewModel(FrameworkElement? target) - { - if (target is null) return null; - - if (target.DataContext is ViewModelBase vm) return vm; - - var parent = target.GetParentObject() as FrameworkElement; - - return FindViewModel(parent); - } - - internal static object FollowPropertyPath(object target, string path, Type? valueType = null) - { - if (target is null) throw new ArgumentNullException(nameof(target)); - if (path is null) throw new ArgumentNullException(nameof(path)); - - Type currentType = valueType ?? target.GetType(); - - foreach (string propertyName in path.Split('.')) - { - PropertyInfo property = currentType.GetProperty(propertyName); - if (property is null) throw new NullReferenceException("property"); - - target = property.GetValue(target); - currentType = property.PropertyType; - } - return target; - } -} diff --git a/src/IFoxCAD.WPF/RelayCommand.cs b/src/IFoxCAD.WPF/RelayCommand.cs deleted file mode 100644 index 72fabca..0000000 --- a/src/IFoxCAD.WPF/RelayCommand.cs +++ /dev/null @@ -1,199 +0,0 @@ -using Microsoft.Xaml.Behaviors; - -namespace IFoxCAD.WPF; - -/// -/// 命令基类 -/// -/// -public class RelayCommand : ICommand -{ - readonly Func? _canExecute; - readonly Action _execute; - /// - /// 初始化 类. - /// - /// 执行函数 - public RelayCommand(Action execute) : this(execute, null) - { - - } - /// - /// 初始化 类. - /// - /// 执行函数委托 - /// 是否可执行函数委托 - /// execute - public RelayCommand(Action execute, Func? canExecute) - { - _execute = execute ?? throw new ArgumentNullException(nameof(execute)); - _canExecute = canExecute; - } - - /// - /// 当出现影响是否应执行该命令的更改时发生。 - /// - public event EventHandler CanExecuteChanged - { - add - { - if (_canExecute is not null) - { - CommandManager.RequerySuggested += value; - } - } - remove - { - if (_canExecute is not null) - { - CommandManager.RequerySuggested -= value; - } - } - } - /// - /// 定义确定此命令是否可在其当前状态下执行的方法。 - /// - /// 此命令使用的数据。 如果此命令不需要传递数据,则该对象可以设置为 。 - /// - /// 如果可执行此命令,则为 ;否则为 。 - /// - [DebuggerStepThrough] - public bool CanExecute(object parameter) - { - return _canExecute is null || _canExecute(parameter); - } - /// - /// 定义在调用此命令时要调用的方法。 - /// - /// 此命令使用的数据。 如果此命令不需要传递数据,则该对象可以设置为 。 - public void Execute(object parameter) - { - _execute(parameter); - } -} - -/// -/// 命令泛型基类 -/// -/// 事件类型 -/// -public class RelayCommand : ICommand -{ - readonly Func _canExecute; - readonly Action _execute; - /// - /// 初始化 类。 - /// - /// 执行函数 - public RelayCommand(Action execute) : this(execute, (o) => true) - { - - } - - /// - /// 初始化 类。 - /// - /// 执行函数委托 - /// 是否可执行函数委托 - /// execute - public RelayCommand(Action execute, Func canExecute) - { - _execute = execute ?? throw new ArgumentNullException(nameof(execute)); - _canExecute = canExecute; - } - /// - /// 当出现影响是否应执行该命令的更改时发生。 - /// - public event EventHandler CanExecuteChanged - { - add - { - if (_canExecute is not null) - { - CommandManager.RequerySuggested += value; - } - } - remove - { - if (_canExecute is not null) - { - CommandManager.RequerySuggested -= value; - } - } - } - /// - /// 定义确定此命令是否可在其当前状态下执行的方法。 - /// - /// 此命令使用的数据。 如果此命令不需要传递数据,则该对象可以设置为 。 - /// - /// 如果可执行此命令,则为 ;否则为 。 - /// - public bool CanExecute(object parameter) - { - if (_canExecute is null) - { - return true; - } - return _canExecute((T)parameter); - } - /// - /// 定义在调用此命令时要调用的方法。 - /// - /// 此命令使用的数据。 如果此命令不需要传递数据,则该对象可以设置为 。 - public void Execute(object parameter) - { - if (_execute is not null && CanExecute(parameter)) - { - _execute((T)parameter); - } - } -} - -/// -/// 事件命令 -/// -public class EventCommand : TriggerAction -{ - /// - /// 执行动作 - /// - /// 要执行的动作参数, 如果动作为提供参数,就设置为null - protected override void Invoke(object parameter) - { - if (CommandParameter is not null) - { - parameter = CommandParameter; - } - if (Command is not null) - { - Command.Execute(parameter); - } - } - /// - /// 事件 - /// - public ICommand Command - { - get { return (ICommand)GetValue(CommandProperty); } - set { SetValue(CommandProperty, value); } - } - /// - /// 事件属性 - /// - public static readonly DependencyProperty CommandProperty = - DependencyProperty.Register("Command", typeof(ICommand), typeof(EventCommand), new PropertyMetadata(null)); - - /// - /// 事件参数,如果为空,将自动传入事件的真实参数 - /// - public object CommandParameter - { - get { return (object)GetValue(CommandParameterProperty); } - set { SetValue(CommandParameterProperty, value); } - } - /// - /// 事件参数属性 - /// - public static readonly DependencyProperty CommandParameterProperty = - DependencyProperty.Register("CommandParameter", typeof(object), typeof(EventCommand), new PropertyMetadata(null)); -} diff --git a/src/IFoxCAD.WPF/ViewModelBase.cs b/src/IFoxCAD.WPF/ViewModelBase.cs deleted file mode 100644 index c992e81..0000000 --- a/src/IFoxCAD.WPF/ViewModelBase.cs +++ /dev/null @@ -1,58 +0,0 @@ -namespace IFoxCAD.WPF; - -/// -/// ViewModel基类 -/// -/// -public class ViewModelBase : INotifyPropertyChanged -{ - /// - /// 属性值更改事件。 - /// - public event PropertyChangedEventHandler? PropertyChanged; - /// - /// 属性改变时调用 - /// - /// 属性名 - public void OnPropertyChanged([CallerMemberName] string propertyName = "") - { - - PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName)); - } - /// - /// 设置属性函数,自动通知属性改变事件 - /// - /// 属性类型 - /// 属性 - /// 属性值 - /// 属性名 - /// 成功返回 ,反之 - protected virtual bool Set(ref T storage, T value, [CallerMemberName] string propertyName = "") - { - if (object.Equals(storage, value)) return false; - - storage = value; - this.OnPropertyChanged(propertyName); - - return true; - } - /// - /// 创建命令 - /// - /// 要调用的命令函数委托 - /// WPF命令 - protected RelayCommand CreateCommand(Action executeMethod) - { - return CreateCommand(executeMethod, (o) => true); - } - /// - /// 创建命令 - /// - /// 要调用的命令函数委托 - /// 命令是否可以执行的委托 - /// WPF命令 - protected RelayCommand CreateCommand(Action executeMethod, Func canExecuteMethod) - { - return new RelayCommand(executeMethod, canExecuteMethod); - } -} -- Gitee From 581ddb2a5222c6dfb873772c7c4e0e687302d244 Mon Sep 17 00:00:00 2001 From: vicwjb Date: Thu, 26 Jan 2023 22:47:38 +0800 Subject: [PATCH 654/675] =?UTF-8?q?=E6=B8=85=E7=90=86=E5=90=88=E5=B9=B6?= =?UTF-8?q?=E5=90=8Etests=E7=9B=AE=E5=BD=95=E7=9A=84=E6=AE=8B=E4=BD=99?= =?UTF-8?q?=E6=96=87=E4=BB=B6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- tests/Test/CmdINI.cs | 174 --- tests/Test/GlobalUsings.cs | 50 - tests/Test/Properties/launchSettings.json | 10 - tests/Test/Test.cs | 366 ------ tests/Test/Test.csproj | 28 - tests/Test/TestAOP.cs | 68 -- tests/Test/TestCurve.cs | 150 --- tests/Test/TestDBTrans.cs | 106 -- tests/Test/TestDwgFilerEx.cs | 156 --- tests/Test/TestEnt.cs | 38 - tests/Test/TestJig.cs | 314 ----- tests/Test/TestJigExTransient.cs | 53 - tests/Test/TestLisp.cs | 121 -- tests/Test/TestLoop.cs | 30 - tests/Test/TestMirrorFile.cs | 75 -- tests/Test/TestPoint.cs | 150 --- tests/Test/TestQuadTree.cs | 462 -------- tests/Test/XrefExTest.cs | 24 - tests/Test/testConvexHull.cs | 76 -- tests/Test/testFileDatabase.cs | 29 - tests/Test/testblock.cs | 617 ---------- tests/Test/testeditor.cs | 63 - tests/Test/testenv.cs | 87 -- tests/Test/testid.cs | 73 -- tests/Test/testselectfilter.cs | 33 - tests/Test/wpf/Class1.cs | 11 - tests/Test/wpf/TestView.xaml | 24 - tests/Test/wpf/TestView.xaml.cs | 28 - tests/Test/wpf/TestViewModel.cs | 82 -- .../Properties/Resources.Designer.cs | 63 - tests/TestAcad08/Properties/Resources.resx | 101 -- .../TestAcad08/Properties/launchSettings.json | 10 - tests/TestAcad08/TestAcad08.csproj | 45 - ...63\351\224\256\350\217\234\345\215\225.cs" | 184 --- ...53\345\205\205\344\272\213\344\273\266.cs" | 1029 ----------------- .../HatchHelper.cs" | 124 -- ...350\275\254cad\345\235\220\346\240\207.cs" | 133 --- ...06\347\232\204\344\276\213\345\255\220.cs" | 94 -- ...3\345\205\205\350\276\271\347\225\214.txt" | 104 -- ...71\347\202\271\344\276\213\345\255\220.cs" | 100 -- 40 files changed, 5485 deletions(-) delete mode 100644 tests/Test/CmdINI.cs delete mode 100644 tests/Test/GlobalUsings.cs delete mode 100644 tests/Test/Properties/launchSettings.json delete mode 100644 tests/Test/Test.cs delete mode 100644 tests/Test/Test.csproj delete mode 100644 tests/Test/TestAOP.cs delete mode 100644 tests/Test/TestCurve.cs delete mode 100644 tests/Test/TestDBTrans.cs delete mode 100644 tests/Test/TestDwgFilerEx.cs delete mode 100644 tests/Test/TestEnt.cs delete mode 100644 tests/Test/TestJig.cs delete mode 100644 tests/Test/TestJigExTransient.cs delete mode 100644 tests/Test/TestLisp.cs delete mode 100644 tests/Test/TestLoop.cs delete mode 100644 tests/Test/TestMirrorFile.cs delete mode 100644 tests/Test/TestPoint.cs delete mode 100644 tests/Test/TestQuadTree.cs delete mode 100644 tests/Test/XrefExTest.cs delete mode 100644 tests/Test/testConvexHull.cs delete mode 100644 tests/Test/testFileDatabase.cs delete mode 100644 tests/Test/testblock.cs delete mode 100644 tests/Test/testeditor.cs delete mode 100644 tests/Test/testenv.cs delete mode 100644 tests/Test/testid.cs delete mode 100644 tests/Test/testselectfilter.cs delete mode 100644 tests/Test/wpf/Class1.cs delete mode 100644 tests/Test/wpf/TestView.xaml delete mode 100644 tests/Test/wpf/TestView.xaml.cs delete mode 100644 tests/Test/wpf/TestViewModel.cs delete mode 100644 tests/TestAcad08/Properties/Resources.Designer.cs delete mode 100644 tests/TestAcad08/Properties/Resources.resx delete mode 100644 tests/TestAcad08/Properties/launchSettings.json delete mode 100644 tests/TestAcad08/TestAcad08.csproj delete mode 100644 "tests/TestAcad08/\346\213\211\344\274\270\345\241\253\345\205\205/01.\346\213\211\344\274\270\345\241\253\345\205\205\345\217\263\351\224\256\350\217\234\345\215\225.cs" delete mode 100644 "tests/TestAcad08/\346\213\211\344\274\270\345\241\253\345\205\205/02.\346\213\211\344\274\270\345\241\253\345\205\205\344\272\213\344\273\266.cs" delete mode 100644 "tests/TestAcad08/\346\213\211\344\274\270\345\241\253\345\205\205/HatchHelper.cs" delete mode 100644 "tests/TestAcad08/\346\213\211\344\274\270\345\241\253\345\205\205/\345\261\217\345\271\225\345\235\220\346\240\207\350\275\254cad\345\235\220\346\240\207.cs" delete mode 100644 "tests/TestAcad08/\346\213\211\344\274\270\345\241\253\345\205\205/\350\277\207\347\250\213\346\226\207\344\273\266/\344\270\200\344\270\252\346\227\240\346\263\225\347\247\273\345\212\250\347\272\242\350\211\262\345\234\206\347\232\204\344\276\213\345\255\220.cs" delete mode 100644 "tests/TestAcad08/\346\213\211\344\274\270\345\241\253\345\205\205/\350\277\207\347\250\213\346\226\207\344\273\266/\350\207\252\345\256\232\344\271\211\345\233\276\345\205\203\345\241\253\345\205\205\350\276\271\347\225\214.txt" delete mode 100644 "tests/TestAcad08/\346\213\211\344\274\270\345\241\253\345\205\205/\350\277\207\347\250\213\346\226\207\344\273\266/\350\216\267\345\217\226\345\244\271\347\202\271\344\276\213\345\255\220.cs" diff --git a/tests/Test/CmdINI.cs b/tests/Test/CmdINI.cs deleted file mode 100644 index 1370267..0000000 --- a/tests/Test/CmdINI.cs +++ /dev/null @@ -1,174 +0,0 @@ -namespace Test; - -/// -/// 注册中心(自动执行接口): -/// -/// 继承虚函数后才能使用
    -/// 0x01 netload加载之后自动执行,写入启动注册表,下次就不需要netload了
    -/// 0x02 反射调用特性和接口
    -/// 启动cad后的执行顺序为:
    -/// 1:构造函数
    -/// 2:特性..多个
    -/// 3:接口..多个
    -/// 4:本类的构造函数
    -/// -/// **** 警告 **** -/// 如果不写一个 储存这个对象, -/// 而是直接写卸载命令在此, -/// 第一次加载的时候会初始化完成,然后这个类生命就结束了, -/// 第二次通过命令进入,会引发构造函数再次执行,留意构造函数的打印信息即可发现 -/// -///
    -///
    -public class AutoRegAssemEx : AutoRegAssem -{ - public AutoRegAssemEx() : base(AutoRegConfig.All) - { - var dm = Acap.DocumentManager; - var doc = dm.MdiActiveDocument; - var ed = doc.Editor; - ed.WriteMessage($"\n {nameof(AutoRegAssemEx)}构造函数,开始自动执行\r\n"); - - CmdInit.AutoRegAssemEx = this; - } -} - -public class CmdInit -{ - public static AutoRegAssemEx? AutoRegAssemEx; - - /// 如果netload之后用 删除注册表, - /// 由于不是也不能卸载dll,再netload是无法执行自动接口的, - /// 所以此时会产生无法再注册的问题...因此需要暴露此注册函数(硬来) - [CommandMethod("IFoxAddReg")] - public void IFoxAddReg() - { - var dm = Acap.DocumentManager; - var doc = dm.MdiActiveDocument; - var ed = doc.Editor; - ed.WriteMessage($"\n 加入注册表"); - - if (AutoRegAssemEx is null) - AutoRegAssemEx = new(); - AutoRegAssemEx.RegApp(); - } - - /// - /// 卸载注册表信息 - /// - [CommandMethod("IFoxRemoveReg")] - public void IFoxRemoveReg() - { - var dm = Acap.DocumentManager; - var doc = dm.MdiActiveDocument; - var ed = doc.Editor; - ed.WriteMessage($"\n 卸载注册表"); - - // 防止卸载两次,不然会报错的 - AutoRegAssemEx?.UnRegApp(); - AutoRegAssemEx = null; - } -} - - -/* - * 自动执行特性例子: - */ -public class Cmd_IFoxInitialize -{ - [IFoxInitialize] - public void NameCasual() - { - var dm = Acap.DocumentManager; - var doc = dm.MdiActiveDocument; - var ed = doc.Editor; - ed.WriteMessage("\n 开始自动执行 可以分开多个类和多个函数 \r\n"); - } - - [IFoxInitialize] - public void NameCasualtest() - { - var dm = Acap.DocumentManager; - var doc = dm.MdiActiveDocument; - var ed = doc.Editor; - ed.WriteMessage("\n 开始自动执行 又一次测试 \r\n"); - } - - [IFoxInitialize] - public void Initialize() - { - var dm = Acap.DocumentManager; - var doc = dm.MdiActiveDocument; - var ed = doc.Editor; - ed.WriteMessage("\n 开始自动执行 Initialize \r\n"); - } - - [IFoxInitialize(isInitialize: false)] - public void Terminate() - { - // try - // { - // var dm = Acap.DocumentManager; - // var doc = dm.MdiActiveDocument; - // var ed = doc.Editor; // 注意此时编辑器已经回收,所以此句没用,并引发错误 - // ed.WriteMessage("\n 结束自动执行 Terminate \r\n"); - // } - // catch (System.Exception) - // { - // } - } - - // [IFoxInitialize] - // public void Initialize() - // { - // // 文档管理器将比此接口前创建,因此此句会执行 - // Acap.DocumentManager.MdiActiveDocument.Editor.WriteMessage("\nload...."); - // } - // [IFoxInitialize(Sequence.First, false)] - // public void Terminate() - // { - // // 文档管理器将比此接口前死亡,因此此句不会执行 - // Acap.DocumentManager.MdiActiveDocument?.Editor.WriteMessage("\nunload...."); - // } -} - -public partial class Test -{ - [CommandMethod(nameof(Test_GetEnv))] - public static void Test_GetEnv() - { - var dir = Env.GetEnv("PrinterConfigDir"); - Env.Printl("pc3打印机位置:" + dir); - - Env.SetEnv("abc", "656"); - - var obj = Env.GetEnv("abc"); - Env.Printl("GetEnv:" + obj); - - Env.Printl("GetEnv:" + Env.GetEnv("abc")); - Env.Printl("GetEnv PATH:" + Env.GetEnv("PATH")); - } - - -#if !NET35 && !NET40 - - // 通过此功能获取全部变量,尚不清楚此处如何设置,没有通过测试 - [CommandMethod(nameof(Test_GetvarAll))] - public static void Test_GetvarAll() - { - GetvarAll(); - } - - public static Dictionary GetvarAll() - { - var dict = new Dictionary(); - var en = new SystemVariableEnumerator(); - while (en.MoveNext()) - { - Console.WriteLine(en.Current.Name + "-----" + en.Current.Value);//Value会出现异常 - dict.Add(en.Current.Name, en.Current.Value); - } - return dict; - } -#endif -} \ No newline at end of file diff --git a/tests/Test/GlobalUsings.cs b/tests/Test/GlobalUsings.cs deleted file mode 100644 index 1e1bc8d..0000000 --- a/tests/Test/GlobalUsings.cs +++ /dev/null @@ -1,50 +0,0 @@ -/// 系统引用 -global using System; -global using System.Collections; -global using System.Collections.Generic; -global using System.IO; -global using System.Linq; -global using System.Text; -global using System.Reflection; -global using System.Text.RegularExpressions; -global using Microsoft.Win32; -global using System.ComponentModel; -global using System.Runtime.InteropServices; -global using System.Collections.Specialized; - -global using Exception = System.Exception; - -global using Registry = Microsoft.Win32.Registry; -global using RegistryKey = Microsoft.Win32.RegistryKey; - -/// autocad 引用 -global using Autodesk.AutoCAD.ApplicationServices; -global using Autodesk.AutoCAD.EditorInput; -global using Autodesk.AutoCAD.Colors; -global using Autodesk.AutoCAD.DatabaseServices; -global using Autodesk.AutoCAD.Geometry; -global using Autodesk.AutoCAD.Runtime; -global using Acap = Autodesk.AutoCAD.ApplicationServices.Application; -global using Acgi = Autodesk.AutoCAD.GraphicsInterface; - -global using Autodesk.AutoCAD.DatabaseServices.Filters; -global using Autodesk.AutoCAD; - -// jig命名空间会引起Viewport/Polyline等等重义,最好逐个引入 using Autodesk.AutoCAD.GraphicsInterface -global using WorldDraw = Autodesk.AutoCAD.GraphicsInterface.WorldDraw; -global using Manager = Autodesk.AutoCAD.GraphicsSystem.Manager; -global using Group = Autodesk.AutoCAD.DatabaseServices.Group; -global using Viewport = Autodesk.AutoCAD.DatabaseServices.Viewport; -global using Autodesk.AutoCAD.GraphicsInterface; -global using Polyline = Autodesk.AutoCAD.DatabaseServices.Polyline; -global using Acad_DwgFiler = Autodesk.AutoCAD.DatabaseServices.DwgFiler; -global using Acad_DxfFiler = Autodesk.AutoCAD.DatabaseServices.DxfFiler; -global using Acad_ErrorStatus = Autodesk.AutoCAD.Runtime.ErrorStatus; - - -/// ifoxcad -global using IFoxCAD.Cad; -global using IFoxCAD.Basal; -#if !ac2008 -global using IFoxCAD.WPF; -#endif \ No newline at end of file diff --git a/tests/Test/Properties/launchSettings.json b/tests/Test/Properties/launchSettings.json deleted file mode 100644 index 4b464c3..0000000 --- a/tests/Test/Properties/launchSettings.json +++ /dev/null @@ -1,10 +0,0 @@ -{ - "profiles": { - "Test": { - "commandName": "Executable", - "executablePath": "C:\\Program Files\\Autodesk\\AutoCAD 2021\\acad.exe", - "commandLineArgs": "/nologo", - "nativeDebugging": false - } - } -} \ No newline at end of file diff --git a/tests/Test/Test.cs b/tests/Test/Test.cs deleted file mode 100644 index ceea8de..0000000 --- a/tests/Test/Test.cs +++ /dev/null @@ -1,366 +0,0 @@ -namespace Test; -#pragma warning disable CS0219 // 变量已被赋值,但从未使用过它的值 - -public partial class Test -{ - [CommandMethod("dbtest")] - public void Dbtest() - { - using var tr = new DBTrans(); - if (tr.Editor is null) - return; - tr.Editor.WriteMessage("\n测试 Editor 属性是否工作!"); - tr.Editor.WriteMessage("\n----------开始测试--------------"); - tr.Editor.WriteMessage("\n测试document属性是否工作"); - if (tr.Document == Getdoc()) - { - tr.Editor.WriteMessage("\ndocument 正常"); - } - tr.Editor.WriteMessage("\n测试database属性是否工作"); - if (tr.Database == Getdb()) - { - tr.Editor.WriteMessage("\ndatabase 正常"); - } - - Line line = new(new Point3d(0, 0, 0), new Point3d(1, 1, 0)); - Circle circle = new(new Point3d(0, 0, 0), Vector3d.ZAxis, 2); - // var lienid = tr.AddEntity(line); - // var cirid = tr.AddEntity(circle); - // var linent = tr.GetObject(lienid); - // var lineent = tr.GetObject(cirid); - // var linee = tr.GetObject(cirid); // 经测试,类型不匹配,返回null - // var dd = tr.GetObject(lienid); - // List ds = new() { linee, dd }; - // tr.CurrentSpace.AddEntity(line,tr); - } - - // add entity test - [CommandMethod("addent")] - public void Addent() - { - using var tr = new DBTrans(); - Line line = new(new Point3d(0, 0, 0), new Point3d(1, 1, 0)); - tr.CurrentSpace.AddEntity(line); - Line line1 = new(new Point3d(10, 10, 0), new Point3d(41, 1, 0)); - tr.ModelSpace.AddEntity(line1); - Line line2 = new(new Point3d(-10, 10, 0), new Point3d(41, 1, 0)); - tr.PaperSpace.AddEntity(line2); - } - - [CommandMethod("drawarc")] - public void Drawarc() - { - using var tr = new DBTrans(); - Arc arc1 = EntityEx.CreateArcSCE(new Point3d(2, 0, 0), new Point3d(0, 0, 0), new Point3d(0, 2, 0));// 起点,圆心,终点 - Arc arc2 = EntityEx.CreateArc(new Point3d(4, 0, 0), new Point3d(0, 0, 0), Math.PI / 2); // 起点,圆心,弧度 - Arc arc3 = EntityEx.CreateArc(new Point3d(1, 0, 0), new Point3d(0, 0, 0), new Point3d(0, 1, 0)); // 起点,圆上一点,终点 - tr.CurrentSpace.AddEntity(arc1, arc2, arc3); - tr.CurrentSpace.AddArc(new Point3d(0, 0, 0), new Point3d(1, 1, 0), new Point3d(2, 0, 0));// 起点,圆上一点,终点 - } - - [CommandMethod("drawcircle")] - public void DraCircle() - { - using var tr = new DBTrans(); - var circle1 = EntityEx.CreateCircle(new Point3d(0, 0, 0), new Point3d(1, 0, 0)); // 起点,终点 - var circle2 = EntityEx.CreateCircle(new Point3d(-2, 0, 0), new Point3d(2, 0, 0), new Point3d(0, 2, 0));// 三点画圆,成功 - var circle3 = EntityEx.CreateCircle(new Point3d(-2, 0, 0), new Point3d(0, 0, 0), new Point3d(2, 0, 0));// 起点,圆心,终点,失败 - tr.CurrentSpace.AddEntity(circle1, circle2!); - if (circle3 is not null) - { - tr.CurrentSpace.AddEntity(circle3); - } - else - { - tr.Editor?.WriteMessage("三点画圆失败"); - } - tr.CurrentSpace.AddEntity(circle3!); - tr.CurrentSpace.AddCircle(new Point3d(0, 0, 0), new Point3d(1, 1, 0), new Point3d(2, 0, 0));// 三点画圆,成功 - tr.CurrentSpace.AddCircle(new Point3d(0, 0, 0), new Point3d(1, 1, 0), new Point3d(2, 2, 0));// 起点,圆上一点,终点(共线) - } - - [CommandMethod("layertest")] - public void Layertest() - { - using var tr = new DBTrans(); - tr.LayerTable.Add("1"); - tr.LayerTable.Add("2", lt => { - lt.Color = Color.FromColorIndex(ColorMethod.ByColor, 1); - lt.LineWeight = LineWeight.LineWeight030; - - }); - tr.LayerTable.Remove("3"); - tr.LayerTable.Delete("0"); - tr.LayerTable.Change("4", lt => { - lt.Color = Color.FromColorIndex(ColorMethod.ByColor, 2); - }); - } - - - // 添加图层 - [CommandMethod("layerAdd1")] - public void Layertest1() - { - using var tr = new DBTrans(); - tr.LayerTable.Add("test1", Color.FromColorIndex(ColorMethod.ByColor, 1)); - } - - // 添加图层 - [CommandMethod("layerAdd2")] - public void Layertest2() - { - using var tr = new DBTrans(); - tr.LayerTable.Add("test2", 2); - // tr.LayerTable["3"] = new LayerTableRecord(); - } - // 删除图层 - [CommandMethod("layerdel")] - public void LayerDel() - { - using var tr = new DBTrans(); - Env.Editor.WriteMessage(tr.LayerTable.Delete("0").ToString()); // 删除图层 0 - Env.Editor.WriteMessage(tr.LayerTable.Delete("Defpoints").ToString());// 删除图层 Defpoints - Env.Editor.WriteMessage(tr.LayerTable.Delete("1").ToString()); // 删除不存在的图层 1 - Env.Editor.WriteMessage(tr.LayerTable.Delete("2").ToString()); // 删除有图元的图层 2 - Env.Editor.WriteMessage(tr.LayerTable.Delete("3").ToString()); // 删除图层 3 - - tr.LayerTable.Remove("2"); // 测试是否能强制删除 - } - - // 添加直线 - [CommandMethod("linedemo1")] - public void AddLine1() - { - using var tr = new DBTrans(); - // tr.ModelSpace.AddEnt(line); - // tr.ModelSpace.AddEnts(line,circle); - - // tr.PaperSpace.AddEnt(line); - // tr.PaperSpace.AddEnts(line,circle); - - // tr.addent(btr,line); - // tr.addents(btr,line,circle); - - - // tr.BlockTable.Add(new BlockTableRecord(), line => - // { - // line. - // }); - Line line1 = new(new Point3d(0, 0, 0), new Point3d(1, 1, 0)); - Line line2 = new(new Point3d(0, 0, 0), new Point3d(1, 1, 0)); - Line line3 = new(new Point3d(1, 1, 0), new Point3d(3, 3, 0)); - Circle circle = new(new Point3d(0, 0, 0), Vector3d.ZAxis, 10); - tr.CurrentSpace.AddEntity(line1); - tr.CurrentSpace.AddEntity(line2, line3, circle); - } - - // 增加多段线1 - [CommandMethod("Pldemo1")] - public void AddPolyline1() - { - using var tr = new DBTrans(); - Polyline pl = new(); - pl.SetDatabaseDefaults(); - pl.AddVertexAt(0, new Point2d(0, 0), 0, 0, 0); - pl.AddVertexAt(1, new Point2d(10, 10), 0, 0, 0); - pl.AddVertexAt(2, new Point2d(20, 20), 0, 0, 0); - pl.AddVertexAt(3, new Point2d(30, 30), 0, 0, 0); - pl.AddVertexAt(4, new Point2d(40, 40), 0, 0, 0); - pl.Closed = true; - pl.Color = Color.FromColorIndex(ColorMethod.ByColor, 6); - tr.CurrentSpace.AddEntity(pl); - } - - // 增加多段线2 - [CommandMethod("pldemo2")] - public void Addpl2() - { - var pts = new List<(Point3d, double, double, double)> - { - (new Point3d(0,0,0),0,0,0), - (new Point3d(10,0,0),0,0,0), - (new Point3d(10,10,0),0,0,0), - (new Point3d(0,10,0),0,0,0), - (new Point3d(5,5,0),0,0,0) - }; - using var tr = new DBTrans(); - tr.CurrentSpace.AddPline(pts); - } - - - - - - - // 测试扩展数据 - [CommandMethod("addxdata")] - public void AddXdata() - { - using var tr = new DBTrans(); - var appname = "myapp2"; - - tr.RegAppTable.Add("myapp1"); - tr.RegAppTable.Add(appname); // add函数会默认的在存在这个名字的时候返回这个名字的regapp的id,不存在就新建 - tr.RegAppTable.Add("myapp3"); - tr.RegAppTable.Add("myapp4"); - - var line = new Line(new Point3d(0, 0, 0), new Point3d(1, 1, 0)) - { - XData = new XDataList() - { - { DxfCode.ExtendedDataRegAppName, "myapp1" }, // 可以用dxfcode和int表示组码 - { DxfCode.ExtendedDataAsciiString, "xxxxxxx" }, - {1070, 12 }, - { DxfCode.ExtendedDataRegAppName, appname }, // 可以用dxfcode和int表示组码,移除中间的测试 - { DxfCode.ExtendedDataAsciiString, "要移除的我" }, - {1070, 12 }, - { DxfCode.ExtendedDataRegAppName, "myapp3" }, // 可以用dxfcode和int表示组码 - { DxfCode.ExtendedDataAsciiString, "aaaaaaaaa" }, - {1070, 12 }, - { DxfCode.ExtendedDataRegAppName, "myapp4" }, // 可以用dxfcode和int表示组码 - { DxfCode.ExtendedDataAsciiString, "bbbbbbbbb" }, - {1070, 12 } - } - }; - - tr.CurrentSpace.AddEntity(line); - } - - [CommandMethod("getxdata")] - public void GetXdata() - { - var doc = Acap.DocumentManager.MdiActiveDocument; - var ed = doc.Editor; - using var tr = new DBTrans(); - tr.RegAppTable.ForEach(id => - id.GetObject()?.Name.Print()); - tr.RegAppTable.GetRecords().ForEach(rec => rec.Name.Print()); - tr.RegAppTable.GetRecordNames().ForEach(name => name.Print()); - tr.RegAppTable.ForEach(re => re.Name.Print()); - - // var res = ed.GetEntity("\n select the entity:"); - // if (res.Status == PromptStatus.OK) - // { - // using var tr = new DBTrans(); - // tr.RegAppTable.ForEach(id => id.GetObject().Print()); - // var data = tr.GetObject(res.ObjectId).XData; - // ed.WriteMessage(data.ToString()); - // } - } - - [CommandMethod("changexdata")] - public void Changexdata() - { - var doc = Acap.DocumentManager.MdiActiveDocument; - var ed = doc.Editor; - var appname = "myapp"; - var res = ed.GetEntity("\n select the entity:"); - if (res.Status == PromptStatus.OK) - { - using var tr = new DBTrans(); - var data = tr.GetObject(res.ObjectId)!; - data.ChangeXData(appname, DxfCode.ExtendedDataAsciiString, "change"); - - ed.WriteMessage(data.XData.ToString()); - } - } - - - [CommandMethod("removexdata")] - public void Removexdata() - { - var doc = Acap.DocumentManager.MdiActiveDocument; - var ed = doc.Editor; - var appname = "myapp2"; - var res = ed.GetEntity("\n select the entity:"); - if (res.Status == PromptStatus.OK) - { - using var tr = new DBTrans(); - var ent = tr.GetObject(res.ObjectId); - ed.WriteMessage("\n移除前:" + ent?.XData.ToString()); - - ent?.RemoveXData(appname, DxfCode.ExtendedDataAsciiString); - ed.WriteMessage("\n移除后:" + ent?.XData.ToString()); - } - } - - [CommandMethod("PrintLayerName")] - public void PrintLayerName() - { - using var tr = new DBTrans(); - foreach (var layerRecord in tr.LayerTable.GetRecords()) - { - tr.Editor?.WriteMessage(layerRecord.Name); - } - foreach (var layerRecord in tr.LayerTable.GetRecords()) - { - tr.Editor?.WriteMessage(layerRecord.Name); - break; - } - } - - - [CommandMethod("testrec")] - public void TestRec() - { - Point2d p1 = new(10000.2, 100000.5); - Point2d p2 = new(15000.9, 100000.5); - Point2d p3 = new(15000.9, 105000.7); - Point2d p4 = new(10000.2, 105000.7); - - var p12 = p2 - p1; - var p23 = p3 - p2; - var p34 = p4 - p3; - var p41 = p1 - p4; - var p13 = p3 - p1; - var p24 = p4 - p2; - - - const double pi90 = Math.PI / 2; - - Tools.TestTimes(1000000, "对角线", () => { - var result = false; - if (Math.Abs(p13.Length - p24.Length) <= 1e8) - { - result = p41.IsParallelTo(p12); - } - - }); - - Tools.TestTimes(1000000, "三次点乘", () => { - var result = false; - - if (Math.Abs(p12.DotProduct(p23)) < 1e8 && - Math.Abs(p23.DotProduct(p34)) < 1e8 && - Math.Abs(p34.DotProduct(p41)) < 1e8) - { - result = true; - } - - }); - - Tools.TestTimes(1000000, "三次垂直", () => { - var result = false; - if (p12.IsParallelTo(p23) && - p23.IsParallelTo(p34) && - p34.IsParallelTo(p41)) - { - result = true; - } - - }); - } - - public Database Getdb() - { - var db = Acap.DocumentManager.MdiActiveDocument.Database; - return db; - } - - public Document Getdoc() - { - var doc = Acap.DocumentManager.MdiActiveDocument; - return doc; - } -} -#pragma warning restore CS0219 // 变量已被赋值,但从未使用过它的值 diff --git a/tests/Test/Test.csproj b/tests/Test/Test.csproj deleted file mode 100644 index 3768cfb..0000000 --- a/tests/Test/Test.csproj +++ /dev/null @@ -1,28 +0,0 @@ - - - - preview - enable - - NET45 - true - true - x64 - - - - 1701;1702;CS1685 - - - - 1701;1702;CS1685 - - - - - - - - - - diff --git a/tests/Test/TestAOP.cs b/tests/Test/TestAOP.cs deleted file mode 100644 index 2b9169f..0000000 --- a/tests/Test/TestAOP.cs +++ /dev/null @@ -1,68 +0,0 @@ -namespace Test; - -// 被注入的函数将不能使用断点, -// 因此用户要充分了解才能使用 -#if false -/* - * 类库用户想侵入的命名空间是用户的, - * 所以需要用户手动进行AOP.Run(), - * 默认情况不侵入用户的命令,必须用户手动启用此功能; - * 启动执行策略之后,侵入命名空间下的命令, - * 此时有拒绝特性的策略保证括免,因为用户肯定是想少写一个事务注入的特性; - */ -public class AutoAOP -{ - [IFoxInitialize] - public void Initialize() - { - AOP.Run(nameof(Test)); - } -} - -namespace Test -{ - /* - * 天秀的事务注入,让你告别事务处理 - * https://www.cnblogs.com/JJBox/p/16157578.html - */ - public class AopTestClass - { - // 类不拒绝,这里拒绝 - [IFoxRefuseInjectionTransaction] - [CommandMethod("IFoxRefuseInjectionTransaction")] - public void IFoxRefuseInjectionTransaction() - { - } - - // 不拒绝 - [CommandMethod("InjectionTransaction")] - public void InjectionTransaction() - { - // 怎么用事务呢? - // 直接用 DBTrans.Top - var dBTrans = new DBTrans(); - dBTrans.Commit(); - } - } - - // 拒绝注入事务,写类上,则方法全都拒绝 - [IFoxRefuseInjectionTransaction] - public class AopTestClassRefuseInjection - { - // 此时这个也是拒绝的..这里加特性只是无所谓 - [IFoxRefuseInjectionTransaction] - [CommandMethod("IFoxRefuseInjectionTransaction2")] - public void IFoxRefuseInjectionTransaction2() - { - // 拒绝注入就要自己开事务,通常用在循环提交事务上面. - // 另见 报错0x02 https://www.cnblogs.com/JJBox/p/10798940.html - using var tr = new DBTrans(); - } - - [CommandMethod("InjectionTransaction2")] - public void InjectionTransaction2() - { - } - } -} -#endif \ No newline at end of file diff --git a/tests/Test/TestCurve.cs b/tests/Test/TestCurve.cs deleted file mode 100644 index 541029c..0000000 --- a/tests/Test/TestCurve.cs +++ /dev/null @@ -1,150 +0,0 @@ -namespace Test; - -public class TestGraph -{ - [CommandMethod("testpointindict")] - public void TestPointInDict() - { - var pt1 = new Point3d(0.0255, 0.452, 0); - var pt2 = new Point3d(0.0255001, 0.452003, 0); - var pt3 = new Point3d(0.0255002, 0.4520001, 0); - var pt4 = new Point3d(0.0255450, 0.45287893, 0); - var pt5 = new Point3d(0.02554935, 0.452092375, 0); - var dict = new Dictionary - { - { pt1, 1 }, - { pt2, 2 }, - { pt3, 3 }, - { pt4, 4 }, - { pt5, 5 } - }; - Env.Print(dict[pt1]); - } - - [CommandMethod("testgraph")] - public void TestGraph1() - { - using var tr = new DBTrans(); - var ents = Env.Editor.SSGet()?.Value?.GetEntities(); - if (ents == null) - return; - Tools.TestTimes2(1, "new", () => { - var res = ents!.GetAllCycle(); - - // res.ForEach((i, t) => t.ForWrite(e => e.ColorIndex = i + 1)); - Env.Print(res.Count()); - tr.CurrentSpace.AddEntity(res); - }); - } - - [CommandMethod("testgraphspeed")] - public void TestGraphspeed() - { - using var tr = new DBTrans(); - var ents = Env.Editor.SSGet()?.Value?.GetEntities(); - if (ents == null) - return; - - var graph = new IFoxCAD.Cad.Graph(); // 为了调试先把图的访问改为internal - foreach (var curve in ents) - { -#if NET35 - graph.AddEdge(curve!.ToCurve3d()!); -#else - graph.AddEdge(curve!.GetGeCurve()); -#endif - } - - // 新建 dfs - var dfs = new DepthFirst(); -#if true - Tools.TestTimes2(100, "new", () => { - // 查询全部的 闭合环 - dfs.FindAll(graph); - }); - Tools.TestTimes2(1000, "new", () => { - // 查询全部的 闭合环 - dfs.FindAll(graph); - }); -#else - Tools.TestTimes2(100, "old", () => { - // 查询全部的 闭合环 - dfs.FindAll(graph); - }); - Tools.TestTimes2(1000, "old", () => { - // 查询全部的 闭合环 - dfs.FindAll(graph); - }); -#endif - // res.ForEach((i, t) => t.ForWrite(e => e.ColorIndex = i + 1)); - - // tr.CurrentSpace.AddEntity(res); - } -} - - - - -public class TestCurve -{ - [CommandMethod("testbreakcurve")] - public void TestBreakCurve() - { - using var tr = new DBTrans(); - var ents = Env.Editor.SSGet()?.Value.GetEntities(); - if (ents is null) - return; - var tt = CurveEx.BreakCurve(ents.ToList()!); - tt.ForEach(t => t.ForWrite(e => e.ColorIndex = 1)); - tr.CurrentSpace.AddEntity(tt); - } - - [CommandMethod("testCurveCurveIntersector3d")] - public void TestCurveCurveIntersector3d() - { - using var tr = new DBTrans(); - var ents = Env.Editor.SSGet()? - .Value.GetEntities() - .Select(e => e?.ToCompositeCurve3d()).ToList(); - if (ents == null) - return; - - var cci3d = new CurveCurveIntersector3d(); - for (int i = 0; i < ents.Count; i++) - { - var gc1 = ents[i]; - var int1 = gc1?.GetInterval(); - // var pars1 = paramss[i]; - for (int j = i; j < ents.Count; j++) - { - var gc2 = ents[j]; - // var pars2 = paramss[j]; - var int2 = gc2?.GetInterval(); - cci3d.Set(gc1, gc2, int1, int2, Vector3d.ZAxis); - var d = cci3d.OverlapCount(); - var a = cci3d.GetIntersectionRanges(); - Env.Print($"{a[0].LowerBound}-{a[0].UpperBound} and {a[1].LowerBound}-{a[1].UpperBound}"); - for (int m = 0; m < d; m++) - { - var b = cci3d.GetOverlapRanges(m); - Env.Print($"{b[0].LowerBound}-{b[0].UpperBound} and {b[1].LowerBound}-{b[1].UpperBound}"); - } - - for (int k = 0; k < cci3d.NumberOfIntersectionPoints; k++) - { - // var a = cci3d.GetOverlapRanges(k); - // var b = cci3d.IsTangential(k); - // var c = cci3d.IsTransversal(k); - // var d = cci3d.OverlapCount(); - // var e = cci3d.OverlapDirection(); - var pt = cci3d.GetIntersectionParameters(k); - var pts = cci3d.GetIntersectionPoint(k); - Env.Print(pts); - } - } - } - // var tt = CurveEx.Topo(ents.ToList()); - // tt.ForEach(t => t.ForWrite(e => e.ColorIndex = 1)); - // tr.CurrentSpace.AddEntity(tt); - } -} \ No newline at end of file diff --git a/tests/Test/TestDBTrans.cs b/tests/Test/TestDBTrans.cs deleted file mode 100644 index 5ffefa8..0000000 --- a/tests/Test/TestDBTrans.cs +++ /dev/null @@ -1,106 +0,0 @@ -namespace Test; - -public class TestTrans -{ - // 后台:不存在路径的dwg会在桌面进行临时保存 - [CommandMethod(nameof(FileNotExist))] - public void FileNotExist() - { - using var tr = new DBTrans("test.dwg"); - tr.SaveFile((DwgVersion)24, false); - } - - // 前台:由于是弹出面板,此时路径不会起任何作用 - [CommandMethod(nameof(FileNotExist2))] - public void FileNotExist2() - { - using var tr = new DBTrans(); - tr.SaveFile(saveAsFile: "D:\\"); - } - - // 后台:只有路径,没有文件名 - [CommandMethod(nameof(FileNotExist3))] - public void FileNotExist3() - { - using var tr = new DBTrans("D:\\"); - tr.SaveDwgFile(); - - using var tr2 = new DBTrans("D:\\"); - tr2.SaveFile(saveAsFile: "D:\\"); - } - - - [CommandMethod("testtr")] - public void Testtr() - { - string filename = @"C:\Users\vic\Desktop\test.dwg"; - using var tr = new DBTrans(filename); - tr.ModelSpace.AddCircle(new Point3d(10, 10, 0), 20); - // tr.Database.SaveAs(filename,DwgVersion.Current); - tr.SaveDwgFile(); - } - [CommandMethod("testifoxcommit")] - public void Testifoxcommit() - { - - using var tr = new DBTrans(); - tr.ModelSpace.AddCircle(new Point3d(0, 0, 0), 20); - tr.Abort(); - // tr.Commit(); - } - - // AOP 应用 预计示例: - // 1. 无参数 - // [AOP] - // [CommandMethod("TESTAOP")] - // public void testaop() - // { - // // 不用 using var tr = new DBTrans(); - // var tr = DBTrans.Top; - // tr.ModelSpace.AddCircle(new Point3d(0, 0, 0), 20); - // } - - // 2. 有参数 - // [AOP("file")] - // [CommandMethod("TESTAOP")] - // public void testaop() - // { - // // 不用 using var tr = new DBTrans(file); - // var tr = DBTrans.Top; - // tr.ModelSpace.AddCircle(new Point3d(0, 0, 0), 20); - // } - - - [CommandMethod("testpt")] - public void TestPt() - { - // var pt = Env.Editor.GetPoint("pick pt:").Value; - // var pl = Env.Editor.GetEntity("pick pl").ObjectId; - - var tr1 = HostApplicationServices.WorkingDatabase.TransactionManager.TopTransaction; - using var tr2 = new DBTrans(); - var tr3 = HostApplicationServices.WorkingDatabase.TransactionManager.TopTransaction; - var tr6 = Acap.DocumentManager.MdiActiveDocument.TransactionManager.TopTransaction; - Env.Print(tr2.Transaction == tr3); - Env.Print(tr3 == tr6); - using var tr4 = new DBTrans(); - var tr5 = HostApplicationServices.WorkingDatabase.TransactionManager.TopTransaction; - var tr7 = Acap.DocumentManager.MdiActiveDocument.TransactionManager.TopTransaction; - Env.Print(tr4.Transaction == tr5); - Env.Print(tr5 == tr7); - var trm = HostApplicationServices.WorkingDatabase.TransactionManager; - - // var ptt = tr.GetObject(pl).GetClosestPointTo(pt,false); - // var pt1 = new Point3d(0, 0.00000000000001, 0); - // var pt2 = new Point3d(0, 0.00001, 0); - // Env.Print(Tolerance.Global.EqualPoint); - // Env.Print(pt1.IsEqualTo(pt2).ToString()); - // Env.Print(pt1.IsEqualTo(pt2,new Tolerance(0.0,1e-6)).ToString()); - // Env.Print((pt1 == pt2).ToString()); - // Env.Print((pt1 != pt2).ToString()); - - - - } - -} diff --git a/tests/Test/TestDwgFilerEx.cs b/tests/Test/TestDwgFilerEx.cs deleted file mode 100644 index c2e4118..0000000 --- a/tests/Test/TestDwgFilerEx.cs +++ /dev/null @@ -1,156 +0,0 @@ -namespace Test; - -using DxfFiler = IFoxCAD.Cad.DxfFiler; - -public class CmdTestDwgFilerEx -{ - [CommandMethod(nameof(CmdTest_DwgFilerEx))] - public static void CmdTest_DwgFilerEx() - { - var dm = Acap.DocumentManager; - var doc = dm.MdiActiveDocument; - var db = doc.Database; - var ed = doc.Editor; - ed.WriteMessage("\n****测试,序列化图元"); - - var ssPsr = ed.SelectImplied();// 预选 - if (ssPsr.Status != PromptStatus.OK) - { - ssPsr = ed.GetSelection();// 手选 这里输入al会变成all,无法删除ssget的all关键字 - if (ssPsr.Status != PromptStatus.OK) - return; - } - - using var tr = new DBTrans(); - var ids = ssPsr.Value.GetObjectIds(); - foreach (var id in ids) - { - if (!id.IsOk()) - continue; - var ent = tr.GetObject(id, OpenMode.ForRead); - if (ent is null) - continue; - var dwgFilerEx = new DwgFilerEx(ent); - ed.WriteMessage(Environment.NewLine + dwgFilerEx.ToString()); - } - } - - [CommandMethod(nameof(CmdTest_EntDxfout))] - public static void CmdTest_EntDxfout() - { - var dm = Acap.DocumentManager; - var doc = dm.MdiActiveDocument; - var db = doc.Database; - var ed = doc.Editor; - - // 定义选择集选项 - var pso = new PromptSelectionOptions - { - RejectObjectsOnLockedLayers = true, // 不选择锁定图层对象 - AllowDuplicates = true, // 不允许重复选择 - }; - var ssPsr = ed.GetSelection(pso);// 手选 这里输入al会变成all,无法删除ssget的all关键字 - if (ssPsr.Status != PromptStatus.OK) - return; - - using var tr = new DBTrans(); - var ids = ssPsr.Value.GetObjectIds(); - foreach (var id in ids) - { - if (!id.IsOk()) - continue; - var ent = tr.GetObject(id, OpenMode.ForRead); - if (ent is null) - continue; - // ResultBuffer rbDxf = new(); - var filer = new DxfFiler(ent.UnmanagedObject, true);/// 这里有问题 - ent.DxfOut(filer); - } - } - - - [CommandMethod(nameof(CmdTest_TextOut))] - public static void CmdTest_TextOut() - { - var dm = Acap.DocumentManager; - var doc = dm.MdiActiveDocument; - var db = doc.Database; - var ed = doc.Editor; - -#if true - var peo1 = new PromptEntityOptions(Environment.NewLine + "点选源TCH_WIREDIM2:") - { - AllowObjectOnLockedLayer = false, - AllowNone = false - }; - var gt1 = ed.GetEntity(peo1); - if (gt1.Status != PromptStatus.OK) - return; -#else - var peo2 = new PromptEntityOptions(Environment.NewLine + "点选目标TCH_WIREDIM2:") - { - AllowObjectOnLockedLayer = false, - AllowNone = false - }; - var gt2 = ed.GetEntity(peo2); - if (gt2.Status != PromptStatus.OK) - return; -#endif - - using var tr = new DBTrans(); - var dwgFilerEx = new DwgFilerEx(); - var bText = tr.GetObject(gt1.ObjectId, OpenMode.ForRead); - if (bText is null) - return; - - // DwgFilerEx.StringList[0] = "1@2@3@4@5@6@7@"; - // 复制 TCH_WIREDIM2 不行,TEXT 也不行,直接崩溃。line等线就没事 - bText.DwgOut(dwgFilerEx.DwgFiler); - - int testNum = 1 | 2 | 4 | 8; - - if ((testNum & 1) == 1) - { - // 错误,原地克隆也是不行的,它会生成在了模型中. - var sIds = new List - { - bText.ObjectId - }; - // 克隆到目标块表内 - var newObjs = tr.CurrentSpace - .DeepCloneEx(new ObjectIdCollection(sIds.ToArray())); - var newTexts = newObjs.GetValues().GetObject(); - newTexts.ForEach(nText => { - if (nText == null) - return; - // 通过上面的克隆就已经在块表上面了.所以下面的设置也跟设置到已有图元上一样报错. - nText.UpgradeOpen(); - nText.DwgIn(dwgFilerEx); - tr.CurrentSpace.AddEntity(nText); - nText.DowngradeOpen(); - }); - } - if ((testNum & 2) == 2) - { - // 出错 - // 直接设置 - bText.DwgIn(dwgFilerEx); - } - if ((testNum & 4) == 4) - { - // 出错 - // 此时是内存中对象.... - var nText = (DBText)bText.Clone(); - nText.DwgIn(dwgFilerEx); - tr.CurrentSpace.AddEntity(nText); - } - if ((testNum & 8) == 8) - { - // 新对象相当于克隆,是ok的 - DBText nText = new(); - nText.SetDatabaseDefaults(); - nText.DwgIn(dwgFilerEx); - tr.CurrentSpace.AddEntity(nText); - } - } -} \ No newline at end of file diff --git a/tests/Test/TestEnt.cs b/tests/Test/TestEnt.cs deleted file mode 100644 index 010303a..0000000 --- a/tests/Test/TestEnt.cs +++ /dev/null @@ -1,38 +0,0 @@ -namespace Test; - -public class TestEnt -{ - [CommandMethod("TestEntRoration")] - public void TestEntRoration() - { - var line = new Line(new(0,0,0),new(100,0,0)); - - using var tr = new DBTrans(); - tr.CurrentSpace.AddEntity(line); - var line2 = (Line)line.Clone(); - tr.CurrentSpace.AddEntity(line2); - line2.Rotation(new(100, 0, 0), Math.PI / 2); - } - - - [CommandMethod("Testtypespeed")] - public void TestTypeSpeed() - { - var line = new Line(); - var line1 = line as Entity; - Tools.TestTimes(100000, "is 匹配:", () => - { - var t = line1 is Line; - }); - Tools.TestTimes(100000, "name 匹配:", () => - { - // var t = line.GetType().Name; - var tt = line1.GetType().Name == nameof(Line); - }); - Tools.TestTimes(100000, "dxfname 匹配:", () => - { - // var t = line.GetType().Name; - var tt = line1.GetRXClass().DxfName == nameof(Line); - }); - } -} diff --git a/tests/Test/TestJig.cs b/tests/Test/TestJig.cs deleted file mode 100644 index f8a9c5a..0000000 --- a/tests/Test/TestJig.cs +++ /dev/null @@ -1,314 +0,0 @@ -namespace Test; - -using System.Windows.Forms; - -public class Commands_Jig -{ - // 已在数据库的图元如何进入jig - [CommandMethod("TestCmd_jig33")] - public static void TestCmd_jig33() - { - using var tr = new DBTrans(); - var per = tr.Editor?.GetEntity("\n点选圆形:"); - if (per?.Status != PromptStatus.OK) - return; - var cir = tr.GetObject(per.ObjectId, OpenMode.ForWrite); - if (cir == null) - return; - var oldSp = cir.StartPoint; - JigEx? moveJig = null; - moveJig = new JigEx((mousePoint, drawEntitys) => { - moveJig!.SetOptions(oldSp);// 回调过程中也可以修改基点 - // cir.UpgradeOpen();// 已经提权了,所以这里不需要提权 - cir.Move(cir.StartPoint, mousePoint); - // cir.DowngradeOpen(); - - // 此处会Dispose图元, - // 所以此处不加入已经在数据库的图元,而是加入new Entity的. - // drawEntitys.Enqueue(cir); - }); - moveJig.SetOptions(cir.GeometricExtents.MinPoint, orthomode: true); - - // 此处详见方法注释 - moveJig.DatabaseEntityDraw(draw => { - draw.RawGeometry.Draw(cir); - }); - - while (true) - { - var prDrag = moveJig.Drag(); - if (prDrag.Status == PromptStatus.OK) - break; - } - } - - - // 不在数据库的图元如何进入jig - [CommandMethod("TestCmd_Jig44")] - public void TestCmd_Jig44() - { - using var tr = new DBTrans(); - var per = Env.Editor.GetEntity("\n请选择一条多段线:"); - if (per.Status != PromptStatus.OK) - return; - var ent = tr.GetObject(per.ObjectId, OpenMode.ForWrite); - if (ent is not Polyline pl) - return; - - /* - * 鼠标采样器执行时修改鼠标基点 - * 原因: 多段线与鼠标垂直点作为 BasePoint,jig鼠标点为确定点 - * 所以需要先声明再传入指针,但是我发现null也可以. - */ - JigEx? jig = null; - JigPromptPointOptions? options = null; - jig = new JigEx((mousePoint, drawEntitys) => { - var closestPt = pl.GetClosestPointTo(mousePoint, false); - - // 回调过程中SetOptions会覆盖配置,所以如果想增加关键字或者修改基点, - // 不要这样做: jig.SetOptions(closestPt) 而是使用底层暴露 - options!.BasePoint = closestPt; - - // 需要避免重复加入同一个关键字 - if (!options.Keywords.Contains("A")) - options.Keywords.Add("A"); - - // 生成文字 - var dictString = (pl.GetDistAtPoint(closestPt) * 0.001).ToString("0.00"); - var acText = new TextInfo(dictString, closestPt, AttachmentPoint.BaseLeft, textHeight: 200) - .AddDBTextToEntity(); - - // 加入刷新队列 - drawEntitys.Enqueue(acText); - }); - - options = jig.SetOptions(per.PickedPoint); - - // 如果没有这个,那么空格只会是 PromptStatus.None 而不是 PromptStatus.Keyword - // options.Keywords.Add(" ", " ", "空格结束啊"); - // jig.SetSpaceIsKeyword(); - - bool flag = true; - while (flag) - { - var pr = jig.Drag(); - if (pr.Status == PromptStatus.Keyword) - { - switch (pr.StringResult) - { - case "A": - tr.Editor?.WriteMessage($"\n 您触发了关键字{pr.StringResult}"); - flag = false; - break; - case " ": - tr.Editor?.WriteMessage("\n 触发关键字空格"); - flag = false; - break; - } - } - else if (pr.Status != PromptStatus.OK)// PromptStatus.None == 右键,空格,回车,都在这里结束 - { - tr.Editor?.WriteMessage(Environment.NewLine + pr.Status.ToString()); - return; - } - else - flag = false; - } - tr.CurrentSpace.AddEntity(jig.Entitys); - } - - [CommandMethod("TestCmd_loop")] - public void TestCmd_loop() - { - var dm = Acap.DocumentManager; - var ed = dm.MdiActiveDocument.Editor; - - // Create and add our message filter - MyMessageFilter filter = new(); - System.Windows.Forms.Application.AddMessageFilter(filter); - // Start the loop - while (true) - { - // Check for user input events - System.Windows.Forms.Application.DoEvents(); - // Check whether the filter has set the flag - if (filter.bCanceled == true) - { - ed.WriteMessage("\nLoop cancelled."); - break; - } - ed.WriteMessage($"\nInside while loop...and {filter.Key}"); - } - // We're done - remove the message filter - System.Windows.Forms.Application.RemoveMessageFilter(filter); - } - // Our message filter class - public class MyMessageFilter : IMessageFilter - { - public const int WM_KEYDOWN = 0x0100; - public bool bCanceled = false; - public Keys Key { get; private set; } - public bool PreFilterMessage(ref Message m) - { - if (m.Msg == WM_KEYDOWN) - { - // Check for the Escape keypress - Keys kc = (Keys)(int)m.WParam & Keys.KeyCode; - if (m.Msg == WM_KEYDOWN && kc == Keys.Escape) - bCanceled = true; - Key = kc; - // Return true to filter all keypresses - return true; - } - // Return false to let other messages through - return false; - } - } - - - [CommandMethod("TestCmd_QuickText")] - static public void TestCmd_QuickText() - { - var dm = Acap.DocumentManager; - var doc = dm.MdiActiveDocument; - var db = doc.Database; - var ed = doc.Editor; - - PromptStringOptions pso = new("\nEnter text string") - { - AllowSpaces = true - }; - var pr = ed.GetString(pso); - if (pr.Status != PromptStatus.OK) - return; - - using var tr = doc.TransactionManager.StartTransaction(); - var btr = (BlockTableRecord)tr.GetObject(db.CurrentSpaceId, OpenMode.ForWrite); - // Create the text object, set its normal and contents - - var acText = new TextInfo(pr.StringResult, - Point3d.Origin, - AttachmentPoint.BaseLeft, textHeight: 200) - .AddDBTextToEntity(); - - acText.Normal = ed.CurrentUserCoordinateSystem.CoordinateSystem3d.Zaxis; - btr.AppendEntity(acText); - tr.AddNewlyCreatedDBObject(acText, true); - - // Create our jig - var pj = new TextPlacementJig(tr, db, acText); - // Loop as we run our jig, as we may have keywords - var state = PromptStatus.Keyword; - while (state == PromptStatus.Keyword) - { - var res = ed.Drag(pj); - state = res.Status; - if (state != PromptStatus.OK && state != PromptStatus.Keyword) - return; - } - tr.Commit(); - } - -#if true - class TextPlacementJig : EntityJig - { - // Declare some internal state - readonly Database _db; - readonly Transaction _tr; - - Point3d _position; - double _angle, _txtSize; - - // Constructor - public TextPlacementJig(Transaction tr, Database db, Entity ent) : base(ent) - { - _db = db; - _tr = tr; - _angle = 0; - _txtSize = 1; - } - - protected override SamplerStatus Sampler(JigPrompts jp) - { - // We acquire a point but with keywords - JigPromptPointOptions po = new("\nPosition of text") - { - UserInputControls = - UserInputControls.Accept3dCoordinates | - UserInputControls.NullResponseAccepted | - UserInputControls.NoNegativeResponseAccepted | - UserInputControls.GovernedByOrthoMode - }; - po.SetMessageAndKeywords( - "\nSpecify position of text or " + - "[Bold/Italic/LArger/Smaller/" + - "ROtate90/LEft/Middle/RIght]: ", - "Bold Italic LArger Smaller " + - "ROtate90 LEft Middle RIght" - ); - - PromptPointResult ppr = jp.AcquirePoint(po); - if (ppr.Status == PromptStatus.Keyword) - { - switch (ppr.StringResult) - { - case "Bold": - break; - case "Italic": - break; - case "LArger": - { - // Multiple the text size by two - _txtSize *= 2; - break; - } - case "Smaller": - { - // Divide the text size by two - _txtSize /= 2; - break; - } - case "ROtate90": - { - // To rotate clockwise we subtract 90 degrees and - // then normalise the angle between 0 and 360 - _angle -= Math.PI / 2; - while (_angle < Math.PI * 2) - { - _angle += Math.PI * 2; - } - break; - } - case "LEft": - break; - case "RIght": - break; - case "Middle": - break; - } - return SamplerStatus.OK; - } - else if (ppr.Status == PromptStatus.OK) - { - // Check if it has changed or not (reduces flicker) - if (_position.DistanceTo(ppr.Value) < Tolerance.Global.EqualPoint) - return SamplerStatus.NoChange; - - _position = ppr.Value; - return SamplerStatus.OK; - } - return SamplerStatus.Cancel; - } - - protected override bool Update() - { - // Set properties on our text object - DBText txt = (DBText)Entity; - txt.Position = _position; - txt.Height = _txtSize; - txt.Rotation = _angle; - return true; - } - } -#endif -} \ No newline at end of file diff --git a/tests/Test/TestJigExTransient.cs b/tests/Test/TestJigExTransient.cs deleted file mode 100644 index 15b8434..0000000 --- a/tests/Test/TestJigExTransient.cs +++ /dev/null @@ -1,53 +0,0 @@ -#if !ac2008 -namespace Test; - -public partial class Test -{ - [CommandMethod(nameof(TestJigExTransient))] - public void TestJigExTransient() - { - // 先取1点,建2个圆 - var getpt = Env.Editor.GetPoint("\n选择点"); - if (getpt.Status != PromptStatus.OK) - return; - var pt = getpt.Value.Ucs2Wcs(); - - var c1 = new Circle(pt, Vector3d.ZAxis, 100); - var c2 = new Circle(pt.Polar(0, 300), Vector3d.ZAxis, 100); - - // 创建瞬态容器 - using var jet = new JigExTransient(); - - // 将c1以默认模式、c2以亮显模式加到瞬态容器,即在图纸上显示 - jet.Add(c1); - jet.Add(c2, Acgi.TransientDrawingMode.Highlight); - - // 再取一点,再建一个圆c3 - var r2 = Env.Editor.GetPoint("\n选择下一点"); - if (r2.Status != PromptStatus.OK) - return; - var pt2 = r2.Value.Ucs2Wcs(); - - var c3 = new Circle(pt2, Vector3d.ZAxis, 150); - - // 将c1从瞬态容器中移除,将c2修改颜色,c3加入瞬态容器 - jet.Remove(c1); - c2.ColorIndex = 1; - jet.Add(c3); - - // 由于c2进行了修改,所以需要更新, - // 可以单个更新或更新整个瞬态容器 - jet.Update(c2); - //jet.UpdateAll(); - - var r4 = Env.Editor.GetPoint("\n此拾取无意义,仅为了暂停查看"); - - // 加到图纸中,为测试瞬态容器可以自行dispose消失,所以未全部加入 - using var tr = new DBTrans(); - tr.CurrentSpace.AddEntity(c3); - - // 若想将容器中所有图元全部加入提供了Entities属性 - // tr.CurrentSpace.AddEntity(jet.Entities); - } -} -#endif \ No newline at end of file diff --git a/tests/Test/TestLisp.cs b/tests/Test/TestLisp.cs deleted file mode 100644 index 1a17bf0..0000000 --- a/tests/Test/TestLisp.cs +++ /dev/null @@ -1,121 +0,0 @@ -namespace Test; - -public class TestLisp -{ - // 定义lisp函数 - [LispFunction("LispTest_RunLisp")] - public static object LispTest_RunLisp(ResultBuffer rb) - { - CmdTest_RunLisp(); - return null!; - } - - // 模态命令,只有当CAD发出命令提示或当前没有其他的命令或程序活动的时候才可以被触发 - [CommandMethod("CmdTest_RunLisp1", CommandFlags.Modal)] - // 透明命令,可以在一个命令提示输入的时候触发例如正交切换,zoom等 - [CommandMethod("CmdTest_RunLisp2", CommandFlags.Transparent)] - // 选择图元之后执行命令将可以从 获取图元 - [CommandMethod("CmdTest_RunLisp3", CommandFlags.UsePickSet)] - // 命令执行前已选中部分实体.在命令执行过程中这些标记不会被清除 - [CommandMethod("CmdTest_RunLisp4", CommandFlags.Redraw)] - // 命令不能在透视图中使用 - [CommandMethod("CmdTest_RunLisp5", CommandFlags.NoPerspective)] - // 命令不能通过 MULTIPLE命令 重复触发 - [CommandMethod("CmdTest_RunLisp6", CommandFlags.NoMultiple)] - // 不允许在模型空间使用命令 - [CommandMethod("CmdTest_RunLisp7", CommandFlags.NoTileMode)] - // 不允许在布局空间使用命令 - [CommandMethod("CmdTest_RunLisp8", CommandFlags.NoPaperSpace)] - // 命令不能在OEM产品中使用 - [CommandMethod("CmdTest_RunLisp9", CommandFlags.NoOem)] - // 不能直接使用命令名调用,必须使用 组名.全局名 调用 - [CommandMethod("CmdTest_RunLisp10", CommandFlags.Undefined)] - // 定义lisp方法.已废弃 请使用lispfunction - [CommandMethod("CmdTest_RunLisp11", CommandFlags.Defun)] - // 命令不会被存储在新的命令堆上 - [CommandMethod("CmdTest_RunLisp12", CommandFlags.NoNewStack)] - // 命令不能被内部锁定(命令锁) - [CommandMethod("CmdTest_RunLisp13", CommandFlags.NoInternalLock)] - // 调用命令的文档将会被锁定为只读 - [CommandMethod("CmdTest_RunLisp14", CommandFlags.DocReadLock)] - // 调用命令的文档将会被锁定,类似document.lockdocument - [CommandMethod("CmdTest_RunLisp15", CommandFlags.DocExclusiveLock)] - // 命令在CAD运行期间都能使用,而不只是在当前文档 - [CommandMethod("CmdTest_RunLisp16", CommandFlags.Session)] - // 获取用户输入时,可以与属性面板之类的交互 - [CommandMethod("CmdTest_RunLisp17", CommandFlags.Interruptible)] - // 命令不会被记录在命令历史记录 - [CommandMethod("CmdTest_RunLisp18", CommandFlags.NoHistory)] - // 命令不会被 UNDO取消 - [CommandMethod("CmdTest_RunLisp19", CommandFlags.NoUndoMarker)] - // 不能在参照块中使用命令 - [CommandMethod("CmdTest_RunLisp20", CommandFlags.NoBlockEditor)] -#if !ac2008 - // acad09增,不会被动作录制器 捕捉到 - [CommandMethod("CmdTest_RunLisp21", CommandFlags.NoActionRecording)] - // acad09增,会被动作录制器捕捉 - [CommandMethod("CmdTest_RunLisp22", CommandFlags.ActionMacro)] -#endif -#if !NET35 - // 推断约束时不能使用命令 - [CommandMethod("CmdTest_RunLisp23", CommandFlags.NoInferConstraint)] - // 命令允许在选择图元时临时显示动态尺寸 - [CommandMethod("CmdTest_RunLisp24", CommandFlags.TempShowDynDimension)] -#endif - public static void CmdTest_RunLisp() - { - // 测试方法1: (command "CmdTest_RunLisp1") - // 测试方式2: (LispTest_RunLisp) - var dm = Acap.DocumentManager; - var doc = dm.MdiActiveDocument; - var ed = doc.Editor; - - var sb = new StringBuilder(); - foreach (var item in Enum.GetValues(typeof(EditorEx.RunLispFlag))) - { - sb.Append((byte)item); - sb.Append(','); - } - sb.Remove(sb.Length - 1, 1); - var option = new PromptIntegerOptions($"\n输入RunLispFlag枚举值:[{sb}]"); - var ppr = ed.GetInteger(option); - - if (ppr.Status != PromptStatus.OK) - return; - var flag = (EditorEx.RunLispFlag)ppr.Value; - - if (flag == EditorEx.RunLispFlag.AdsQueueexpr) - { - // 同步 - Env.Editor.RunLisp("(setq a 10)(princ)", - EditorEx.RunLispFlag.AdsQueueexpr); - Env.Editor.RunLisp("(princ a)", - EditorEx.RunLispFlag.AdsQueueexpr);// 成功输出 - } - else if (flag == EditorEx.RunLispFlag.AcedEvaluateLisp) - { - // 使用(command "CmdTest_RunLisp1")发送,然后 !b 查看变量,acad08是有值的,高版本是null - var strlisp0 = "(setq b 20)"; - var res0 = Env.Editor.RunLisp(strlisp0, - EditorEx.RunLispFlag.AcedEvaluateLisp); // 有lisp的返回值 - - var strlisp1 = "(defun f1( / )(princ \"aa\"))"; - var res1 = Env.Editor.RunLisp(strlisp1, - EditorEx.RunLispFlag.AcedEvaluateLisp); // 有lisp的返回值 - - var strlisp2 = "(defun f2( / )(command \"line\"))"; - var res2 = Env.Editor.RunLisp(strlisp2, - EditorEx.RunLispFlag.AcedEvaluateLisp); // 有lisp的返回值 - } - else if (flag == EditorEx.RunLispFlag.SendStringToExecute) - { - // 测试异步 - // (command "CmdTest_RunLisp1")和(LispTest_RunLisp)4都是异步 - var str = "(setq c 40)(princ)"; - Env.Editor.RunLisp(str, - EditorEx.RunLispFlag.SendStringToExecute); // 异步,后发送 - Env.Editor.RunLisp("(princ c)", - EditorEx.RunLispFlag.AdsQueueexpr); // 同步,先发送了,输出是null - } - } -} \ No newline at end of file diff --git a/tests/Test/TestLoop.cs b/tests/Test/TestLoop.cs deleted file mode 100644 index f69e676..0000000 --- a/tests/Test/TestLoop.cs +++ /dev/null @@ -1,30 +0,0 @@ -namespace Test; - -using IFoxCAD.Basal; -using System.Diagnostics.CodeAnalysis; - -public class TestLoop -{ - [CommandMethod("testloop")] - public void Testloop() - { - var loop = new LoopList - { - 0, - 1, - 2, - 3, - 4, - 5 - }; - - Env.Print(loop); - - loop.SetFirst(loop.Last!); - Env.Print(loop); - Env.Print(loop.Min()); - loop.SetFirst(new LoopListNode (loop.Min() ,loop)); - Env.Print(loop); - - } -} diff --git a/tests/Test/TestMirrorFile.cs b/tests/Test/TestMirrorFile.cs deleted file mode 100644 index 34bc6d6..0000000 --- a/tests/Test/TestMirrorFile.cs +++ /dev/null @@ -1,75 +0,0 @@ -namespace Test; - -public class MirrorFile -{ - const string file = "D:/JX.dwg"; - const string fileSave = "D:/JX222.dwg"; - - /// - /// 测试:后台打开图纸,镜像文字是否存在文字偏移 - /// 答案:不存在 - /// - [CommandMethod("CmdTest_MirrorFile")] - public static void CmdTest_MirrorFile() - { - using var tr = new DBTrans(file, openMode: FileOpenMode.OpenForReadAndReadShare); - - tr.BlockTable.Change(tr.ModelSpace.ObjectId, modelSpace => { - foreach (ObjectId entId in modelSpace) - { - var dbText = tr.GetObject(entId, OpenMode.ForRead)!; - if (dbText is null) - continue; - - dbText.UpgradeOpen(); - var pos = dbText.Position; - // text.Move(pos, Point3d.Origin); - // Y轴 - dbText.Mirror(Point3d.Origin, new Point3d(0, 1, 0)); - // text.Move(Point3d.Origin, pos); - dbText.DowngradeOpen(); - } - }); - var ver = (DwgVersion)27;/*AC1021 AutoCAD 2007/2008/2009.*/ - tr.Database.SaveAs(fileSave, ver); - } - - /// - /// 测试:后台设置 dbText.IsMirroredInX 属性会令文字偏移 - /// 答案:存在,并提出解决方案 - /// - [CommandMethod("CmdTest_MirrorFile2")] - public static void CmdTest_MirrorFile2() - { - using var tr = new DBTrans(file); - - tr.Task(() => { - var yaxis = new Point3d(0, 1, 0); - tr.BlockTable.Change(tr.ModelSpace.ObjectId, modelSpace => { - foreach (ObjectId entId in modelSpace) - { - var entity = tr.GetObject(entId, OpenMode.ForWrite)!; - if (entity is DBText dbText) - { - dbText.Mirror(Point3d.Origin, yaxis); - dbText.IsMirroredInX = true; // 这句将导致文字偏移 - - // 指定文字的垂直对齐方式 - if (dbText.VerticalMode == TextVerticalMode.TextBase) - dbText.VerticalMode = TextVerticalMode.TextBottom; - - // 指定文字的水平对齐方式 - dbText.HorizontalMode = dbText.HorizontalMode switch - { - TextHorizontalMode.TextLeft => TextHorizontalMode.TextRight, - TextHorizontalMode.TextRight => TextHorizontalMode.TextLeft, - _ => dbText.HorizontalMode - }; - dbText.AdjustAlignment(tr.Database); - } - } - }); - }); - tr.Database.SaveAs(fileSave, (DwgVersion)27 /*AC1021 AutoCAD 2007/2008/2009.*/); - } -} \ No newline at end of file diff --git a/tests/Test/TestPoint.cs b/tests/Test/TestPoint.cs deleted file mode 100644 index 688f09f..0000000 --- a/tests/Test/TestPoint.cs +++ /dev/null @@ -1,150 +0,0 @@ -namespace Test; - - -using System.Diagnostics; - - -public class TestPoint -{ - /// - /// 红黑树排序点集 - /// - [CommandMethod("TestptSortedSet")] - public void TestptSortedSet() - { - var ss1 = new SortedSet - { - new Point2d(1, 1), - new Point2d(4.6, 2), - new Point2d(8, 3), - new Point2d(4, 3), - new Point2d(5, 40), - new Point2d(6, 5), - new Point2d(1, 6), - new Point2d(7, 6), - new Point2d(9, 6) - }; - - /*判断区间,超过就中断*/ - foreach (var item in ss1) - { - if (item.X > 3 && item.X < 7) - Debug.WriteLine(item); - else if (item.X >= 7) - break; - } - } - - - - [CommandMethod("TestptGethash")] - public void TestptGethash() - { - // test - var pt = Env.Editor.GetPoint("pick pt").Value; - // Tools.TestTimes2(1_000_000, "新语法", () => { - // pt.GetHashString2(); - // }); - Tools.TestTimes2(1_000_000, "旧语法", () => { - pt.GetHashString(); - }); - } - - [CommandMethod("Testpoint3d")] - public void TestPoint3d() - { - Env.Print($"4位小数的hash:{new Point3d(0.0_001, 0.0_002, 0.0).GetHashCode()}"); - Env.Print($"5位小数的hash:{new Point3d(0.00_001, 0.00_002, 0.0).GetHashCode()}"); - Env.Print($"6位小数的hash:{new Point3d(0.000_001, 0.000_002, 0.0).GetHashCode()}"); - Env.Print($"7位小数的hash:{new Point3d(0.000_0_001, 0.000_0_002, 0.0).GetHashCode()}"); - Env.Print($"8位小数的hash:{new Point3d(0.000_00_001, 0.000_00_002, 0.0).GetHashCode()}"); - Env.Print($"9位小数的hash:{new Point3d(0.000_000_001, 0.000_000_002, 0.0).GetHashCode()}"); - Env.Print($"10位小数的hash:{new Point3d(0.000_000_0001, 0.000_000_0002, 0.0).GetHashCode()}"); - Env.Print($"10位小数的hash:{new Point3d(0.000_000_0001, 0.000_000_0001, 0.0).GetHashCode()}"); - - Env.Print($"11位小数的hash:{new Point3d(0.000_000_000_01, 0.000_000_000_02, 0.0).GetHashCode()}"); - Env.Print($"11位小数的hash:{new Point3d(0.000_000_000_01, 0.000_000_000_01, 0.0).GetHashCode()}"); - - Env.Print($"12位小数的hash:{new Point3d(0.000_000_000_001, 0.000_000_000_002, 0.0).GetHashCode()}"); - Env.Print($"12位小数的hash:{new Point3d(0.000_000_000_001, 0.000_000_000_001, 0.0).GetHashCode()}"); - - Env.Print($"13位小数的hash:{new Point3d(0.000_000_000_0001, 0.000_000_000_0002, 0.0).GetHashCode()}"); - Env.Print($"13位小数的hash:{new Point3d(0.000_000_000_0001, 0.000_000_000_0001, 0.0).GetHashCode()}"); - - Env.Print($"14位小数的hash:{new Point3d(0.000_000_000_000_01, 0.000_000_000_000_02, 0.0).GetHashCode()}"); - Env.Print($"14位小数的hash:{new Point3d(0.000_000_000_000_01, 0.000_000_000_000_01, 0.0).GetHashCode()}"); - - Env.Print($"15位小数的hash:{new Point3d(0.000_000_000_000_001, 0.000_000_000_000_002, 0.0).GetHashCode()}"); - Env.Print($"15位小数的hash:{new Point3d(0.000_000_000_000_001, 0.000_000_000_000_001, 0.0).GetHashCode()}"); - - Env.Print($"16位小数的hash:{new Point3d(0.000_000_000_000_000_1, 0.000_000_000_000_000_2, 0.0).GetHashCode()}"); - Env.Print($"16位小数的hash:{new Point3d(0.000_000_000_000_000_1, 0.000_000_000_000_000_1, 0.0).GetHashCode()}"); - - Env.Print($"17位小数的hash:{new Point3d(0.000_000_000_000_000_01, 0.000_000_000_000_000_02, 0.0).GetHashCode()}"); - Env.Print($"17位小数的hash:{new Point3d(0.000_000_000_000_000_01, 0.000_000_000_000_000_01, 0.0).GetHashCode()}"); - - Env.Print($"18位小数的hash:{new Point3d(0.000_000_000_000_000_001, 0.000_000_000_000_000_002, 0.0).GetHashCode()}"); - Env.Print($"18位小数的hash:{new Point3d(0.000_000_000_000_000_001, 0.000_000_000_000_000_001, 0.0).GetHashCode()}"); - - Env.Print($"19位小数的hash:{new Point3d(0.000_000_000_000_000_000_1, 0.000_000_000_000_000_000_2, 0.0).GetHashCode()}"); - Env.Print($"19位小数的hash:{new Point3d(0.000_000_000_000_000_000_1, 0.000_000_000_000_000_000_1, 0.0).GetHashCode()}"); - - Env.Print($"20位小数的hash:{new Point3d(0.000_000_000_000_000_000_01, 0.000_000_000_000_000_000_02, 0.0).GetHashCode()}"); - Env.Print($"20位小数的hash:{new Point3d(0.000_000_000_000_000_000_01, 0.000_000_000_000_000_000_01, 0.0).GetHashCode()}"); - } - - [CommandMethod("Testlistequalspeed")] - public void Testlistequalspeed() - { - var lst1 = new List { 1, 2, 3, 4 }; - var lst2 = new List { 1, 2, 3, 4}; - lst1.EqualsAll(null!); - Tools.TestTimes2(1000000, "eqaulspeed:", () => { - lst1.EqualsAll(lst2); - }); - } - - [CommandMethod("Testcontains")] - public void Testcontains() - { - // test list and dict contains speed - var lst = new List { 1, 2, 3, 4 , 5,6,7,8,9,10, 11,12,13,14,15,16,17,18,19,20}; - var hashset = new HashSet { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20 }; - var dict = new Dictionary - { - { 1, 0 }, - { 2, 1 }, - { 3, 2 }, - { 4, 3 }, - { 5, 4 }, - { 6, 5 }, - { 7, 6 }, - { 8, 7 }, - { 9, 8 }, - { 10, 9 }, - { 11, 11 }, - { 12, 12 }, - { 13, 13 }, - { 14, 14 }, - { 15, 15 }, - { 16, 16 }, - { 17, 17 }, - { 18, 18 }, - { 19, 19 }, - { 20, 20 }, - }; - - Tools.TestTimes2(100_0000, "list:", () => { - lst.Contains(20); - }); - - Tools.TestTimes2(100_0000, "hashset:", () => { - hashset.Contains(20); - }); - - Tools.TestTimes2(100_0000, "dict:", () => { - dict.ContainsKey(20); - }); - - } -} \ No newline at end of file diff --git a/tests/Test/TestQuadTree.cs b/tests/Test/TestQuadTree.cs deleted file mode 100644 index 2047a29..0000000 --- a/tests/Test/TestQuadTree.cs +++ /dev/null @@ -1,462 +0,0 @@ -namespace Test; - -#pragma warning disable CS8632 // 只能在 "#nullable" 注释上下文内的代码中使用可为 null 的引用类型的注释。 -/* - * 这里属于用户调用例子, - * 调用时候必须要继承它,再提供给四叉树 - * 主要是用户可以扩展属性 - */ -public class CadEntity : QuadEntity -{ - public ObjectId ObjectId; - // 这里加入其他字段 - public List? Link;// 碰撞链 - public System.Drawing.Color Color; - public double Angle; - public CadEntity(ObjectId objectId, Rect box) : base(box) - { - ObjectId = objectId; - } - public int CompareTo(CadEntity? other) - { - if (other == null) - return -1; - return GetHashCode() ^ other.GetHashCode(); - } - public override int GetHashCode() - { - return (base.GetHashCode(), ObjectId.GetHashCode()).GetHashCode(); - } -} -#pragma warning restore CS8632 // 只能在 "#nullable" 注释上下文内的代码中使用可为 null 的引用类型的注释。 - - - - - -public partial class TestQuadTree -{ - QuadTree? _quadTreeRoot; - #region 四叉树创建并加入 - [CommandMethod("Test_QuadTree")] - public void Test_QuadTree() - { - using var tr = new DBTrans(); - - Rect dbExt; - // 使用数据库边界来进行 - var dbExtent = tr.Database.GetValidExtents3d(); - if (dbExtent == null) - { - // throw new ArgumentException("画一个矩形"); - - // 这个初始值的矩形是很有意义, - // 主要是四叉树分裂过程中产生多个Rect,Rect内有很多重复的double值,是否可以内存复用,以此减少内存大小? - // 接着想了一下,Rect可以是int,long,这样可以利用位运算它扩展和缩小, - // 最小就是1,并且可以控制四叉树深度,不至于无限递归. - // 而且指针长度跟值是一样的,所以就不需要复用了,毕竟跳转一个函数地址挺麻烦的. - // 但是因为啊惊懒的原因,并没有单独制作这样的矩形, - // 而且非常糟糕的是,c#不支持模板约束运算符,使得值类型之间需要通过一层接口来委婉处理,拉低了效率..引用类型倒是无所谓.. - // 要么忍着,要么换c++去搞四叉树吧 - dbExt = new Rect(0, 0, 1 << 10, 1 << 10); - } - else - { - var a = new Point2d(dbExtent.Value.MinPoint.X, dbExtent.Value.MinPoint.Y); - var b = new Point2d(dbExtent.Value.MaxPoint.X, dbExtent.Value.MaxPoint.Y); - dbExt = new Rect(a, b); - } - - // 创建四叉树 - _quadTreeRoot = new QuadTree(dbExt); - - // 数据库边界 - var pl = dbExt.ToPoints(); - var databaseBoundary = new List<(Point3d, double, double, double)> - { - (new Point3d(pl[0].X,pl[0].Y,0),0,0,0), - (new Point3d(pl[1].X,pl[1].Y,0),0,0,0), - (new Point3d(pl[2].X,pl[2].Y,0),0,0,0), - (new Point3d(pl[3].X,pl[3].Y,0),0,0,0), - }; - tr.CurrentSpace.AddPline(databaseBoundary); - - // 生成多少个图元,导致cad会令undo出错(八叉树深度过大 treemax) - // int maximumItems = 30_0000; - int maximumItems = 1000; - - // 随机图元生成 - List ces = new(); // 用于随机获取图元 - Timer.RunTime(() => { - // 生成外边界和随机圆形 - var grc = GenerateRandomCircle(maximumItems, dbExt); - foreach (var ent in grc) - { - // 初始化图元颜色 - ent!.ColorIndex = 1; // Color.FromRgb(0, 0, 0);// 黑色 - var edge = ent.GeometricExtents; - // 四叉树数据 - var entRect = new Rect(edge.MinPoint.X, edge.MinPoint.Y, edge.MaxPoint.X, edge.MaxPoint.Y); - var entId = tr.CurrentSpace.AddEntity(ent); - - var ce = new CadEntity(entId, entRect) - { - Color = RandomEx.NextColor() - }; - ces.Add(ce); - /*加入随机点*/ - var p = edge.MinPoint + new Vector3d(10, 10, 0); - entRect = new Rect(p.Point2d(), p.Point2d()); - entId = tr.CurrentSpace.AddEntity(new DBPoint(p)); - var dbPointCe = new CadEntity(entId, entRect); - ces.Add(dbPointCe); - } - }, Timer.TimeEnum.Millisecond, "画圆消耗时间:");// 30万图元±3秒.cad2021 - - // 测试只加入四叉树的时间 - Timer.RunTime(() => { - for (int i = 0; i < ces.Count; i++) - _quadTreeRoot.Insert(ces[i]); - }, Timer.TimeEnum.Millisecond, "插入四叉树时间:");// 30万图元±0.7秒.cad2021 - - tr.Editor?.WriteMessage($"\n加入图元数量:{maximumItems}"); - } - - /// - /// 创建随机圆形 - /// - /// 创建数量 - /// 数据库边界 - static IEnumerable GenerateRandomCircle(int createNumber, Rect dbExt) - { - var x1 = (int)dbExt.X; - var x2 = (int)(dbExt.X + dbExt.Width); - var y1 = (int)dbExt.Y; - var y2 = (int)(dbExt.Y + dbExt.Height); - - var rand = RandomEx.GetRandom(); - for (int i = 0; i < createNumber; i++) - { - var x = rand.Next(x1, x2) + rand.NextDouble(); - var y = rand.Next(y1, y2) + rand.NextDouble(); - yield return EntityEx.CreateCircle(new Point3d(x, y, 0), rand.Next(1, 100)); // 起点,终点 - } - } - - /*TODO 啊惊: 有点懒不想改了*/ -#if true2 - - // 选择加入到四叉树 - [CommandMethod("CmdTest_QuadTree21")] - public void CmdTest_QuadTree21() - { - var dm = Acap.DocumentManager; - var doc = dm.MdiActiveDocument; - var db = doc.Database; - var ed = doc.Editor; - ed.WriteMessage("\n选择单个图元加入已有的四叉树"); - - var ss = ed.Ssget(); - if (ss.Count == 0) - return; - - AddQuadTreeRoot(db, ed, ss); - } - - // 自动加入全图到四叉树 - [CommandMethod("CmdTest_QuadTree20")] - public void CmdTest_QuadTree20() - { - var dm = Acap.DocumentManager; - var doc = dm.MdiActiveDocument; - var db = doc.Database; - var ed = doc.Editor; - ed.WriteMessage("\n自动加入全图到四叉树"); - - var ss = new List(); - int entnum = 0; - var time1 = Timer.RunTime(() => { - db.Action(tr => { - db.TraverseBlockTable(tr, btRec => { - if (!btRec.IsLayout)// 布局跳过 - return false; - - foreach (var item in btRec) - { - // var ent = item.ToEntity(tr); - ss.Add(item); - ++entnum;// 图元数量:100000, 遍历全图时间:0.216秒 CmdTest_QuadTree2 - } - return false; - }); - }); - }); - ed.WriteMessage($"\n图元数量:{entnum}, 遍历全图时间:{time1 / 1000.0}秒"); - - // 清空原有的 - _quadTreeRoot = null; - AddQuadTreeRoot(db, ed, ss); - } - - void AddQuadTreeRoot(Database db, Editor ed, List ss) - { - if (_quadTreeRoot is null) - { - ed.WriteMessage("\n四叉树是空的,重新初始化"); - - Rect dbExt; - // 使用数据库边界来进行 - var dbExtent = db.GetValidExtents3d(); - if (dbExtent == null) - { - // throw new ArgumentException("画一个矩形"); - - // 测试时候画个矩形,在矩形内画随机坐标的圆形 - dbExt = new Rect(0, 0, 32525, 32525); - } - else - { - dbExt = new Rect(dbExtent.Value.MinPoint.Point2d(), dbExtent.Value.MaxPoint.Point2d()); - } - _quadTreeRoot = new(dbExt); - } - - /* 测试: - * 为了测试删除内容释放了分支,再重复加入是否报错 - * 先创建 CmdTest_QuadTree1 - * 再减去 CmdTest_QuadTree0 - * 然后原有黑色边界,再生成边界 CmdTest_Create00,对比删除效果. - * 然后加入 CmdTest_QuadTree2 - * 然后原有黑色边界,再生成边界 CmdTest_Create00,对比删除效果. - */ - - List ces = new(); - db.Action(tr => { - ss.ForEach(entId => { - var ent = entId.ToEntity(tr); - if (ent is null) - return; - var edge = new EdgeEntity(ent); - // 四叉树数据 - var ce = new CadEntity(entId, edge.Edge) - { - Color = Utility.RandomColor - }; - ces.Add(ce); - - edge.Dispose(); - }); - }); - - var time2 = Timer.RunTime(() => { - _quadTreeRoot.Insert(ces); - }); - ed.WriteMessage($"\n图元数量:{ces.Count}, 加入四叉树时间:{time2 / 1000.0}秒"); - } -#endif - - #endregion - - /*TODO 啊惊: 有点懒不想改了*/ -#if true2 - - #region 节点边界显示 - // 四叉树减去节点 - [CommandMethod("CmdTest_QuadTree0")] - public void CmdTest_QuadTree0() - { - var dm = Acap.DocumentManager; - var doc = dm.MdiActiveDocument; - // var db = doc.Database; - var ed = doc.Editor; - ed.WriteMessage("\n四叉树减区"); - - if (_quadTreeRoot is null) - { - ed.WriteMessage("\n四叉树是空的"); - return; - } - var rect = GetCorner(ed); - if (rect is null) - return; - _quadTreeRoot.Remove(rect); - } - - // 创建节点边界 - [CommandMethod("CmdTest_QuadTree00")] - public void CmdTest_CreateNodesRect() - { - var dm = Acap.DocumentManager; - var doc = dm.MdiActiveDocument; - var db = doc.Database; - var ed = doc.Editor; - ed.WriteMessage("\n创建边界"); - - if (_quadTreeRoot is null) - { - ed.WriteMessage("\n四叉树是空的"); - return; - } - - // 此处发现了一个事务处理的bug,提交数量过多的时候,会导致 ctrl+z 无法回滚, - // 需要把事务放在循环体内部 - // 报错: 0x6B00500A (msvcr80.dll)处(位于 acad.exe 中)引发的异常: 0xC0000005: 写入位置 0xFFE00000 时发生访问冲突。 - // 画出所有的四叉树节点边界,因为事务放在外面引起 - var nodeRects = new List(); - _quadTreeRoot.ForEach(node => { - nodeRects.Add(node); - return false; - }); - var rectIds = new List(); - foreach (var item in nodeRects)// Count = 97341 当数量接近这个量级 - { - db.Action(tr => { - var pts = item.ToPoints(); - var rec = EntityAdd.AddPolyLineToEntity(pts.ToPoint2d()); - rec.ColorIndex = 250; - rectIds.Add(tr.AddEntityToMsPs(db, rec)); - }); - } - db.Action(tr => { - db.CoverGroup(tr, rectIds); - }); - - // 获取四叉树深度 - int dep = 0; - _quadTreeRoot.ForEach(node => { - dep = dep > node.Depth ? dep : node.Depth; - return false; - }); - ed.WriteMessage($"\n四叉树深度是: {dep}"); - } - #endregion - -#endif - - #region 四叉树查询节点 - // 选择范围改图元颜色 - [CommandMethod("CmdTest_QuadTree3")] - public void CmdTest_QuadTree3() - { - Ssget(QuadTreeSelectMode.IntersectsWith); - } - - [CommandMethod("CmdTest_QuadTree4")] - public void CmdTest_QuadTree4() - { - Ssget(QuadTreeSelectMode.Contains); - } - - /// - /// 改颜色 - /// - /// - void Ssget(QuadTreeSelectMode mode) - { - if (_quadTreeRoot is null) - return; - - using var tr = new DBTrans(); - if (tr.Editor is null) - return; - var rect = GetCorner(tr.Editor); - if (rect is null) - return; - - tr.Editor.WriteMessage("选择模式:" + mode); - - // 仿选择集 - var ces = _quadTreeRoot.Query(rect, mode); - ces.ForEach(item => { - var ent = tr.GetObject(item.ObjectId, OpenMode.ForWrite); - ent!.Color = Color.FromColor(item.Color); - ent.DowngradeOpen(); - ent.Dispose(); - }); - } - - /// - /// 交互获取 - /// - /// - /// -#pragma warning disable CS8632 // 只能在 "#nullable" 注释上下文内的代码中使用可为 null 的引用类型的注释。 - public static Rect? GetCorner(Editor ed) - { - var optionsA = new PromptPointOptions($"{Environment.NewLine}起点位置:"); - var pprA = ed.GetPoint(optionsA); - if (pprA.Status != PromptStatus.OK) - return null; - var optionsB = new PromptCornerOptions(Environment.NewLine + "输入矩形角点2:", pprA.Value) - { - UseDashedLine = true,// 使用虚线 - AllowNone = true,// 回车 - }; - var pprB = ed.GetCorner(optionsB); - if (pprB.Status != PromptStatus.OK) - return null!; - - return new Rect(new Point2d(pprA.Value.X, pprA.Value.Y), - new Point2d(pprB.Value.X, pprB.Value.Y), - true); - } -#pragma warning restore CS8632 // 只能在 "#nullable" 注释上下文内的代码中使用可为 null 的引用类型的注释。 - - #endregion -} - -// public partial class TestQuadTree -// { -// public void Cmd_tt6() -// { -// using var tr = new DBTrans(); -// var ed = tr.Editor; -// // 创建四叉树,默认参数无所谓 -// var TreeRoot = new QuadTree(new Rect(0, 0, 32525, 32525)); - -// var fil = OpFilter.Build(e => e.Dxf(0) == "LINE"); -// var psr = ed.SSGet("\n 选择需要连接的直线", fil); -// if (psr.Status != PromptStatus.OK) return; -// var LineEnts = new List(psr.Value.GetEntities(OpenMode.ForWrite)!); -// // 将实体插入到四岔树 -// foreach (var line in LineEnts) -// { -// var edge = line.GeometricExtents; -// var entRect = new Rect(edge.MinPoint.X, edge.MinPoint.Y, edge.MaxPoint.X, edge.MaxPoint.Y); -// var ce = new CadEntity(line.Id, entRect) -// { -// // 四叉树数据 -// Angle = line.Angle -// }; -// TreeRoot.Insert(ce); -// } - -// var ppo = new PromptPointOptions(Environment.NewLine + "\n指定标注点:<空格退出>") -// { -// AllowArbitraryInput = true,// 任意输入 -// AllowNone = true // 允许回车 -// }; -// var ppr = ed.GetPoint(ppo);// 用户点选 -// if (ppr.Status != PromptStatus.OK) -// return; -// var rect = new Rect(ppr.Value.Point2d(), 100, 100); -// tr.CurrentSpace.AddEntity(rect.ToPolyLine());// 显示选择靶标范围 - -// var nent = TreeRoot.FindNearEntity(rect);// 查询最近实体,按逆时针 -// var ent = tr.GetObject(nent.ObjectId, OpenMode.ForWrite);// 打开实体 -// ent.ColorIndex = Utility.GetRandom().Next(1, 256);// 1~256随机色 -// ent.DowngradeOpen();// 实体降级 -// ent.Dispose(); - -// var res = TreeRoot.Query(rect, QuadTreeSelectMode.IntersectsWith);// 查询选择靶标范围相碰的ID -// res.ForEach(item => { -// if (item.Angle == 0 || item.Angle == Math.PI) // 过滤直线角度为0或180的直线 -// { -// var ent = tr.GetObject(item.ObjectId, OpenMode.ForWrite); -// ent.ColorIndex = Utility.GetRandom().Next(1, 7); -// ent.DowngradeOpen(); -// ent.Dispose(); -// } -// }); -// } -// } \ No newline at end of file diff --git a/tests/Test/XrefExTest.cs b/tests/Test/XrefExTest.cs deleted file mode 100644 index b1d2545..0000000 --- a/tests/Test/XrefExTest.cs +++ /dev/null @@ -1,24 +0,0 @@ -namespace Test; - -public class TestCmd_BindXrefs -{ - //后台绑定 - [CommandMethod("Test_Bind1")] - public static void Test_Bind1() - { - string fileName = @"D:\Test.dwg"; - using var tr = new DBTrans(fileName, - openMode: FileOpenMode.OpenForReadAndAllShare/*后台绑定特别注意*/); - tr.XrefFactory(XrefModes.Bind); - tr.SaveDwgFile(); - } - - //前台绑定 - [CommandMethod("Test_Bind2")] - public static void Test_Bind2() - { - using var tr = new DBTrans(); - tr.XrefFactory(XrefModes.Bind); - tr.SaveDwgFile(); - } -} diff --git a/tests/Test/testConvexHull.cs b/tests/Test/testConvexHull.cs deleted file mode 100644 index f96b302..0000000 --- a/tests/Test/testConvexHull.cs +++ /dev/null @@ -1,76 +0,0 @@ -namespace Test; - - -public class TestConvexHull -{ - [CommandMethod("testch")] - public void Testch() - { - // using var tr = new DBTrans(); - // var pts = new List(); - // var flag = true; - // while (flag) - // { - // var pt = tr.Editor.GetPoint("qudian"); - // if (pt.Status == PromptStatus.OK) - // { - // pts.Add(pt.Value); - // tr.CurrentSpace.AddEntity(new DBPoint(pt.Value)); - // } - // else - // { - // flag = false; - // } - - // } - - // var ptt = ConvexHull.GetConvexHull(pts); - - // Polyline pl = new Polyline(); - // for (int i = 0; i < ptt.Count; i++) - // { - // pl.AddVertexAt(i, ptt[i].Point2d(), 0, 0, 0); - // } - //// pl.AddVertexAt(0, new Point2d(0, 0), 0, 0, 0); - //// pl.AddVertexAt(1, new Point2d(10, 10), 0, 0, 0); - //// pl.AddVertexAt(2, new Point2d(20, 20), 0, 0, 0); - //// pl.AddVertexAt(3, new Point2d(30, 30), 0, 0, 0); - //// pl.AddVertexAt(4, new Point2d(40, 40), 0, 0, 0); - // pl.Closed = true; - // pl.Color = Color.FromColorIndex(ColorMethod.ByColor, 6); - // tr.CurrentSpace.AddEntity(pl); - - // var a1 = GeometryEx.GetArea(new Point2d(0, 0), new Point2d(1, 0), new Point2d(1, 1)); - // var a2 = ConvexHull.cross(new Point3d(0, 0, 0), new Point3d(1, 0, 0), new Point3d(1, 1, 0)); - // tr.Editor.WriteMessage(a1.ToString()); - // tr.Editor.WriteMessage(a2.ToString()); - - - // var vec1 = new Vector2d(1, 1); - // var vec2 = new Vector2d(-1, 1); - - // var vec3 = vec1.GetPerpendicularVector(); - // var vec4 = vec2.GetPerpendicularVector(); - - // var area1 = vec2.DotProduct(vec1.GetPerpendicularVector()); - // var area2 = vec1.DotProduct(vec2.GetPerpendicularVector()); - - // var area3 = vec2.DotProduct(vec1); - // var area4 = vec1.DotProduct(vec2); - - var area5 = GeometryEx.GetArea(new List { new Point2d(0, 0), new Point2d(1, 1), new Point2d(-1, 1) }); - - var area6 = GeometryEx.GetArea(new List { new Point2d(0, 0), new Point2d(-1, 1), new Point2d(1, 1) }); - // Env.Editor.WriteMessage($"vec1 的法向量= {vec3} \n"); - // Env.Editor.WriteMessage($"vec2 的法向量= {vec4} \n"); - - // Env.Editor.WriteMessage($"vec2 点乘 vec1的法向量= {area1} \n"); - // Env.Editor.WriteMessage($"vec1 点乘 vec2的法向量= {area2} \n"); - - // Env.Editor.WriteMessage($"vec2 点乘 vec1= {area3} \n"); - // Env.Editor.WriteMessage($"vec1 点乘 vec2= {area4} \n"); - - Env.Editor.WriteMessage($"点集的有向面积:{area5} \n"); - Env.Editor.WriteMessage($"点集的有向面积:{area6} \n"); - } -} diff --git a/tests/Test/testFileDatabase.cs b/tests/Test/testFileDatabase.cs deleted file mode 100644 index cd3af21..0000000 --- a/tests/Test/testFileDatabase.cs +++ /dev/null @@ -1,29 +0,0 @@ -namespace Test; - -/************************************************************** -*作者:Leon -*创建时间:2022/2/11 9:55:32 -**************************************************************/ - -public class TestFileDatabase -{ - [CommandMethod("Test_FileDatabaseInit")] - public void TestDatabase() - { - try - { - var fileName = @"C:\Users\Administrator\Desktop\合并详图测试BUG.dwg"; - using DBTrans trans = new(fileName); - trans.ModelSpace.AddEntity(new Line(new(0, 0, 0), new(1000, 1000, 0))); - if (trans.Document is not null && trans.Document.IsActive) - trans.Document.SendStringToExecute("_qsave\n", false, true, true); - else - trans.Database.SaveAs(fileName, (DwgVersion)27); - } - catch (System.Exception e) - { - System.Windows.MessageBox.Show(e.Message); - } - - } -} \ No newline at end of file diff --git a/tests/Test/testblock.cs b/tests/Test/testblock.cs deleted file mode 100644 index 8581eba..0000000 --- a/tests/Test/testblock.cs +++ /dev/null @@ -1,617 +0,0 @@ -namespace Test; - -public class TestBlock -{ - [CommandMethod("Test_GetBoundingBoxEx")] - public void Test_GetBoundingBoxEx() - { - using var tr = new DBTrans(); - var ents = Env.Editor.SSGet()?.Value?.GetEntities(); - if (ents == null) - return; - foreach (var item in ents) - { - if (item is null) - continue; - var box = item.GetBoundingBoxEx(); - Env.Print("min:" + box.Min + ";max" + box.Max); - } - } - - // 块定义 - [CommandMethod("Test_blockdef")] - public void Test_BlockDef() - { - using var tr = new DBTrans(); - // var line = new Line(new Point3d(0, 0, 0), new Point3d(1, 1, 0)); - tr.BlockTable.Add("test", - btr => { - btr.Origin = new Point3d(0, 0, 0); - }, - () => // 图元 - { - return new List { new Line(new Point3d(0, 0, 0), new Point3d(1, 1, 0)) }; - }, - () => // 属性定义 - { - var id1 = new AttributeDefinition() { Position = new Point3d(0, 0, 0), Tag = "start", Height = 0.2 }; - var id2 = new AttributeDefinition() { Position = new Point3d(1, 1, 0), Tag = "end", Height = 0.2 }; - return new List { id1, id2 }; - } - ); - // ObjectId objectId = tr.BlockTable.Add("a");// 新建块 - // objectId.GetObject().AddEntity();// 测试添加空实体 - tr.BlockTable.Add("test1", - btr => { - btr.Origin = new Point3d(0, 0, 0); - }, - () => { - var line = new Line(new Point3d(0, 0, 0), new Point3d(1, 1, 0)); - var acText = new TextInfo("123", Point3d.Origin, AttachmentPoint.BaseLeft) - .AddDBTextToEntity(); - - return new List { line, acText }; - }); - } - // 修改块定义 - [CommandMethod("Test_blockdefchange")] - public void Test_BlockDefChange() - { - using var tr = new DBTrans(); - // var line = new Line(new Point3d(0, 0, 0), new Point3d(1, 1, 0)); - // tr.BlockTable.Change("test", btr => - // { - // btr.Origin = new Point3d(5, 5, 0); - // btr.AddEntity(new Circle(new Point3d(0, 0, 0), Vector3d.ZAxis, 2)); - // btr.GetEntities() - // .ToList() - // .ForEach(e => e.Flush()); // 刷新块显示 - - // }); - - - tr.BlockTable.Change("test", btr => { - foreach (var id in btr) - { - var ent = tr.GetObject(id); - using (ent!.ForWrite()) - { - if (ent is Dimension dBText) - { - dBText.DimensionText = "234"; - dBText.RecomputeDimensionBlock(true); - } - if (ent is Hatch hatch) - hatch.ColorIndex = 0; - } - } - }); - tr.Editor?.Regen(); - } - - [CommandMethod("Test_insertblockdef")] - public void Test_InsertBlockDef() - { - using var tr = new DBTrans(); - var line1 = new Line(new Point3d(0, 0, 0), new Point3d(1, 1, 0)); - var line2 = new Line(new Point3d(0, 0, 0), new Point3d(-1, 1, 0)); - var att1 = new AttributeDefinition() { Position = new Point3d(10, 10, 0), Tag = "tagTest1", Height = 1, TextString = "valueTest1" }; - var att2 = new AttributeDefinition() { Position = new Point3d(10, 12, 0), Tag = "tagTest2", Height = 1, TextString = "valueTest2" }; - tr.BlockTable.Add("test1", line1, line2, att1, att2); - - - var ents = new List(); - var line5 = new Line(new Point3d(0, 0, 0), new Point3d(1, 1, 0)); - var line6 = new Line(new Point3d(0, 0, 0), new Point3d(-1, 1, 0)); - ents.Add(line5); - ents.Add(line6); - tr.BlockTable.Add("test44", ents); - - - var line3 = new Line(new Point3d(5, 5, 0), new Point3d(6, 6, 0)); - var line4 = new Line(new Point3d(5, 5, 0), new Point3d(-6, 6, 0)); - var att3 = new AttributeDefinition() { Position = new Point3d(10, 14, 0), Tag = "tagTest3", Height = 1, TextString = "valueTest3" }; - var att4 = new AttributeDefinition() { Position = new Point3d(10, 16, 0), Tag = "tagTest4", Height = 1, TextString = "valueTest4" }; - tr.BlockTable.Add("test2", new List { line3, line4 }, new List { att3, att4 }); - // tr.CurrentSpace.InsertBlock(new Point3d(4, 4, 0), "test1"); // 测试默认 - // tr.CurrentSpace.InsertBlock(new Point3d(4, 4, 0), "test2"); - // tr.CurrentSpace.InsertBlock(new Point3d(4, 4, 0), "test3"); // 测试插入不存在的块定义 - // tr.CurrentSpace.InsertBlock(new Point3d(0, 0, 0), "test1", new Scale3d(2)); // 测试放大2倍 - // tr.CurrentSpace.InsertBlock(new Point3d(4, 4, 0), "test1", new Scale3d(2), Math.PI / 4); // 测试放大2倍,旋转45度 - - var def1 = new Dictionary - { - { "tagTest1", "1" }, - { "tagTest2", "2" } - }; - tr.CurrentSpace.InsertBlock(new Point3d(0, 0, 0), "test1", atts: def1); - var def2 = new Dictionary - { - { "tagTest3", "1" }, - { "tagTest4", "" } - }; - tr.CurrentSpace.InsertBlock(new Point3d(10, 10, 0), "test2", atts: def2); - tr.CurrentSpace.InsertBlock(new Point3d(-10, 0, 0), "test44"); - } - - [CommandMethod("Test_addattsdef")] - public void Test_AddAttsDef() - { - using var tr = new DBTrans(); - var blockid = Env.Editor.GetEntity("pick block:").ObjectId; - var blockref = tr.GetObject(blockid)!.BlockTableRecord; - - var att1 = new AttributeDefinition() { Position = new Point3d(20, 20, 0), Tag = "addtagTest1", Height = 1, TextString = "valueTest1" }; - var att2 = new AttributeDefinition() { Position = new Point3d(10, 12, 0), Tag = "tagTest2", Height = 1, TextString = "valueTest2" }; - - tr.BlockTable.AddAttsToBlocks(blockref, new List { att1, att2 }); - } - - [CommandMethod("test_blocknullbug")] - public void Test_BlockNullBug() - { - using var tr = new DBTrans(); - - var ents = new List(); - var line5 = new Line(new Point3d(0, 0, 0), new Point3d(1, 1, 0)); - var line6 = new Line(new Point3d(0, 0, 0), new Point3d(-1, 1, 0)); - ents.Add(line5); - ents.Add(line6); - tr.BlockTable.Add("test44", ents); - tr.CurrentSpace.InsertBlock(new Point3d(0, 0, 0), "test44"); - } - - [CommandMethod("test_block_file")] - public void Test_BlockFile() - { - var tr = new DBTrans(); - var id = tr.BlockTable.GetBlockFrom(@"C:\Users\vic\Desktop\test.dwg", false); - tr.CurrentSpace.InsertBlock(Point3d.Origin, id); - } - - - [CommandMethod("test_clip")] - public void Test_ClipBlock() - { - using var tr = new DBTrans(); - tr.BlockTable.Add("test1", - btr => { - btr.Origin = new Point3d(0, 0, 0); - btr.AddEntity(new Line(new Point3d(0, 0, 0), new Point3d(10, 10, 0)), - new Line(new Point3d(10, 10, 0), new Point3d(10, 0, 0)) - ); - } - ); - // tr.BlockTable.Add("hah"); - var id = tr.CurrentSpace.InsertBlock(new Point3d(0, 0, 0), "test1"); - var bref = tr.GetObject(id)!; - var pts = new List { new Point3d(3, 3, 0), new Point3d(7, 3, 0), new Point3d(7, 7, 0), new Point3d(3, 7, 0) }; - bref.ClipBlockRef(pts); - - var id1 = tr.CurrentSpace.InsertBlock(new Point3d(20, 20, 0), "test1"); - var bref1 = tr.GetObject(id); - bref1?.ClipBlockRef(new Point3d(13, 13, 0), new Point3d(17, 17, 0)); - } - - /// - /// 给用户的测试程序,不知道对错 - /// - [CommandMethod("test_block_ej")] - public void test_block_ej() - { - using (var tr = new DBTrans()) - { - // Point3d.Origin.AddBellowToModelSpace(100, 100, 5, 3, 30);// 画波纹管 - - // Database db2 = new Database(false, true); - // string fullFileName = @".\MyBlockDwgFile\001.dwg"; - // db2.ReadDwgFile(fullFileName, System.IO.FileShare.Read, true, null); - // db2.CloseInput(true); - // string blockName = "test"; - // if (!tr.BlockTable.Has(blockName)) - // { - // // tr.Database.Insert(blockName, db2, false);// 插入块 - // db.Insert(blockName, db2, false); - - // } - - string fullFileName = @"C:\Users\vic\Desktop\001.dwg"; - var blockdef = tr.BlockTable.GetBlockFrom(fullFileName, false); - - tr.Database.Clayer = tr.LayerTable["0"];// 当前图层切换为0图层 - tr.LayerTable.Change(tr.Database.Clayer, ltr => { - ltr.Color = Color.FromColorIndex(ColorMethod.ByAci, 2); // ColorMethod.ByAci可以让我们使用AutoCAD ACI颜色索引……这里为2(表示黄色) - }); - - var id = tr.ModelSpace.InsertBlock(Point3d.Origin, blockdef);// 插入块参照 - var entTest = tr.GetObject(id); - entTest?.Draw(); - } - - using var tr2 = new DBTrans(); - PromptEntityOptions PEO = new("\n请选择一个块"); - PEO.SetRejectMessage("\n对象必须是块"); - PEO.AddAllowedClass(typeof(BlockReference), true); - - PromptEntityResult PER = Env.Editor.GetEntity(PEO); - if (PER.Status != PromptStatus.OK) - { - return; - } - - var Bref = tr2.GetObject(PER.ObjectId)!; - // var BTR = tr.GetObject(Bref.BlockTableRecord, OpenMode.ForWrite); - //// 如果知道块名字BTRName - // BlockTableRecord BTR = tr.GetObject(tr.BlockTable[blockName], OpenMode.ForWrite); - - var btr = tr2.BlockTable[Bref.Name]; - - tr2.BlockTable.Change(btr, ltr => { - foreach (ObjectId OID in ltr) - { - var ent = tr2.GetObject(OID); - using (ent!.ForWrite()) - { - if (ent is MText mText) - { - switch (mText.Text) - { - case "$$A": - mText.Contents = "hahaha"; - break; - case "$$B": - break; - default: - break; - } - } - else if (ent is DBText dBText) { dBText.TextString = "haha"; } - else if (ent is Dimension dimension) - { - switch (dimension.DimensionText) - { - case "$$pipeLen": - dimension.DimensionText = "350"; - dimension.RecomputeDimensionBlock(true); - break; - default: - break; - } - } - } - } - }); - - tr2.Editor?.Regen(); - } - - [CommandMethod("W_KSZK")] - public void QuickBlockDef() - { - // Database db = HostApplicationServices.WorkingDatabase; - Editor ed = Acap.DocumentManager.MdiActiveDocument.Editor; - PromptSelectionOptions promptOpt = new() - { - MessageForAdding = "请选择需要快速制作块的对象" - }; - string blockName = "W_BLOCK_" + DateTime.Now.ToString("yyyyMMdd_HHmmss"); - // var rss = ed.GetSelection(promptOpt); - var rss = Env.Editor.GetSelection(promptOpt); - using var tr = new DBTrans(); - if (rss.Status == PromptStatus.OK) - { - // SelectionSet ss = rss.Value; - // ObjectId[] ids = ss.GetObjectIds(); - // var ents = new List>(); - // var extents = new Extents3d(); - // foreach (var id in ids) - // { - // Entity ent = tr.GetObject(id); - // if (ent is null) - // continue; - // try - // { - // extents.AddExtents(ent.GeometricExtents); - // var order = id.Handle.Value; - // var newEnt = ent.Clone() as Entity; - // ents.Add(new KeyValuePair(newEnt, order)); - // ent.UpgradeOpen(); - // ent.Erase(); - // ent.DowngradeOpen(); - // } - // catch (System.Exception exc) - // { - // ed.WriteMessage(exc.Message); - // } - // } - // ents = ents.OrderBy(x => x.Value).ToList(); - var ents = rss.Value.GetEntities(); - // ents.ForEach(ent => extents.AddExtents(ent.GeometricExtents)); - var extents = ents!.GetExtents(); - Point3d pt = extents.MinPoint; - Matrix3d matrix = Matrix3d.Displacement(Point3d.Origin - pt); - // var newEnts = new List(); - // foreach (var ent in ents) - // { - // var newEnt = ent.Key; - // newEnt.TransformBy(matrix); - // newEnts.Add(newEnt); - // } - // if (tr.BlockTable.Has(blockName)) - // { - // Acap.ShowAlertDialog(Environment.NewLine + "块名重复,程序退出!"); - // return; - // } - ents.ForEach(ent => - ent?.ForWrite(e => e?.TransformBy(matrix))); - // var newents = ents.Select(ent => - // { - // var maping = new IdMapping(); - // return ent.DeepClone(ent, maping, true) as Entity; - // }); - var newents = ents.Select(ent => ent?.Clone() as Entity); - - // ents.ForEach(ent => ent.ForWrite(e => e.Erase(true))); // 删除实体就会卡死,比较奇怪,估计是Clone()函数的问题 - // 经过测试不是删除的问题 - var btrId = tr.BlockTable.Add(blockName, newents!); - ents.ForEach(ent => ent?.ForWrite(e => e?.Erase(true))); - var bId = tr.CurrentSpace.InsertBlock(pt, blockName); - // tr.GetObject(bId, OpenMode.ForWrite).Move(Point3d.Origin, Point3d.Origin); - // var ed = Acap.DocumentManager.MdiActiveDocument.Editor; - // ed.Regen(); - // tr.Editor.Regen(); - // 调用regen() 卡死 - } - // tr.Editor.Regen(); - // ed.Regen(); - // using (var tr = new DBTrans()) - // { - // tr.CurrentSpace.InsertBlock(Point3d.Origin, blockName); - // tr.Editor.Regen(); - // } - } - - [CommandMethod("test_quickblockdef")] - public void Test_QuickBlockDef() - { - Database db = HostApplicationServices.WorkingDatabase; - Editor ed = Acap.DocumentManager.MdiActiveDocument.Editor; - PromptSelectionOptions promptOpt = new() - { - MessageForAdding = "请选择需要快速制作块的对象" - }; - string blockName = "W_BLOCK_" + DateTime.Now.ToString("yyyyMMdd_HHmmss"); - var rss = ed.GetSelection(promptOpt); - // var rss = Env.Editor.GetSelection(promptOpt); - if (rss.Status != PromptStatus.OK) - { - return; - } - - using var tr = db.TransactionManager.StartTransaction(); - var ids = rss.Value.GetObjectIds(); - var bt = tr.GetObject(db.BlockTableId, OpenMode.ForRead) as BlockTable; - var btr = new BlockTableRecord - { - Name = blockName - }; - foreach (var item in ids) - { - var ent = tr.GetObject(item, OpenMode.ForRead) as Entity; - btr.AppendEntity(ent!.Clone() as Entity); - ent.ForWrite(e => e.Erase(true)); - } - bt!.UpgradeOpen(); - bt.Add(btr); - tr.AddNewlyCreatedDBObject(btr, true); - bt.DowngradeOpen(); - // tr.Commit(); - // } - - // using (var tr1 = db.TransactionManager.StartTransaction()) - // { - // var bt = tr1.GetObject(db.BlockTableId, OpenMode.ForRead) as BlockTable; - var btr1 = tr.GetObject(db.CurrentSpaceId, OpenMode.ForWrite) as BlockTableRecord; - var br = new BlockReference(Point3d.Origin, bt[blockName]) - { - ScaleFactors = default - }; - btr1!.AppendEntity(br); - tr.AddNewlyCreatedDBObject(br, true); - btr1.DowngradeOpen(); - ed.Regen(); - tr.Commit(); - // ed.Regen(); - } - - public void TestWblock() - { - var curdb = HostApplicationServices.WorkingDatabase; - PromptSelectionOptions opts = new() - { - MessageForAdding = "选择对象" - }; - var ss = Env.Editor.GetSelection(opts).Value; - var ids = new ObjectIdCollection(ss.GetObjectIds()); - var db = curdb.Wblock(ids, Point3d.Origin); - db.SaveAs(@"c:\test.dwg", DwgVersion.Current); - } - - public void TestChangeDynameicBlock() - { - var pro = new Dictionary - { - { "haha", 1 } - }; - var blockid = Env.Editor.GetEntity("选择个块").ObjectId; - using var tr = new DBTrans(); - var blockref = tr.GetObject(blockid)!; - blockref.ChangeBlockProperty(pro); - // 这是第一个函数的用法 - } - - [CommandMethod("TestBack")] - public void TestBack() - { - string dir = Environment.GetFolderPath(Environment.SpecialFolder.DesktopDirectory); - string dwg = dir + "\\test.dwg"; - if (!File.Exists(dwg)) - { - System.Windows.Forms.MessageBox.Show(dwg, "你还没有创建此文件"); - return; - } - - using var tr = new DBTrans(dwg); - tr.ModelSpace.GetEntities().ForEach(ent => { - ent.ForWrite(e => e.ColorIndex = 3); - }); - tr.Database.SaveAs(dwg, DwgVersion.Current); - - tr.ModelSpace.GetEntities().ForEach(ent => { - ent.ForWrite(e => e.ColorIndex = 4); - }); - tr.Database.SaveAs(dwg, DwgVersion.Current); - } -} - -public class BlockImportClass -{ - [CommandMethod("Test_CBLL")] - public void Test_Cbll() - { - string filename = @"C:\Users\vic\Desktop\Drawing1.dwg"; - using var tr = new DBTrans(); - using var tr1 = new DBTrans(filename); - // tr.BlockTable.GetBlockFrom(filename, true); - string blkdefname = SymbolUtilityServices.RepairSymbolName(SymbolUtilityServices.GetSymbolNameFromPathName(filename, "dwg"), false); - tr.Database.Insert(blkdefname, tr1.Database, false); // 插入了块定义,未插入块参照 - } - -#if !NET35 - [CommandMethod("Test_CombineBlocksIntoLibrary")] - public void Test_CombineBlocksIntoLibrary() - { - Document doc = - Acap.DocumentManager.MdiActiveDocument; - Editor ed = doc.Editor; - Database destDb = doc.Database; - - // Get name of folder from which to load and import blocks - - PromptResult pr = - ed.GetString("\nEnter the folder of source drawings: "); - - if (pr.Status != PromptStatus.OK) - return; - string pathName = pr.StringResult; - - // Check the folder exists - - if (!Directory.Exists(pathName)) - { - ed.WriteMessage( - "\nDirectory does not exist: {0}", pathName - ); - return; - } - - // Get the names of our DWG files in that folder - - string[] fileNames = Directory.GetFiles(pathName, "*.dwg"); - - // A counter for the files we've imported - - int imported = 0, failed = 0; - - // For each file in our list - - foreach (string fileName in fileNames) - { - // Double-check we have a DWG file (probably unnecessary) - - if (fileName.EndsWith( - ".dwg", - StringComparison.InvariantCultureIgnoreCase - ) - ) - { - // Catch exceptions at the file level to allow skipping - - try - { - // Suggestion from Thorsten Meinecke... - - string destName = - SymbolUtilityServices.GetSymbolNameFromPathName( - fileName, "dwg" - ); - - // And from Dan Glassman... - - destName = - SymbolUtilityServices.RepairSymbolName( - destName, false - ); - - // Create a source database to load the DWG into - - using Database db = new(false, true); - // Read the DWG into our side database - - db.ReadDwgFile(fileName, FileShare.Read, true, ""); - bool isAnno = db.AnnotativeDwg; - - // Insert it into the destination database as - // a named block definition - - ObjectId btrId = destDb.Insert( - destName, - db, - false - ); - - if (isAnno) - { - // If an annotative block, open the resultant BTR - // and set its annotative definition status - - Transaction tr = - destDb.TransactionManager.StartTransaction(); - using (tr) - { - BlockTableRecord btr = - (BlockTableRecord)tr.GetObject( - btrId, - OpenMode.ForWrite - ); - btr.Annotative = AnnotativeStates.True; - tr.Commit(); - } - } - - // Print message and increment imported block counter - - ed.WriteMessage("\nImported from \"{0}\".", fileName); - imported++; - } - catch (System.Exception ex) - { - ed.WriteMessage( - "\nProblem importing \"{0}\": {1} - file skipped.", - fileName, ex.Message - ); - failed++; - } - } - } - - ed.WriteMessage( - "\nImported block definitions from {0} files{1} in " + - "\"{2}\" into the current drawing.", - imported, - failed > 0 ? " (" + failed + " failed)" : "", - pathName - ); - } -#endif -} \ No newline at end of file diff --git a/tests/Test/testeditor.cs b/tests/Test/testeditor.cs deleted file mode 100644 index 60d742f..0000000 --- a/tests/Test/testeditor.cs +++ /dev/null @@ -1,63 +0,0 @@ -namespace Test; - -public class Testeditor -{ - [CommandMethod("tested")] - public void Tested() - { - var pts = new List - { - new Point2d(0,0), - new Point2d(0,1), - new Point2d(1,1), - new Point2d(1,0) - }; - var res = EditorEx.GetLines(pts, false); - var res1 = EditorEx.GetLines(pts, true); - var res2 = pts.Select(pt => new TypedValue((int)LispDataType.Point2d, pt)).ToList(); - - Editor ed = Acap.DocumentManager.MdiActiveDocument.Editor; - var pt = ed.GetPoint("qudiam", new Point3d(0, 0, 0)); - var d = ed.GetDouble("qudoule"); - var i = ed.GetInteger("quint"); - var s = ed.GetString("qustr"); - Env.Editor.WriteMessage(""); - } - [CommandMethod("testzoom")] - public void Testzoom() - { - using var tr = new DBTrans(); - var res = Env.Editor.GetEntity("\npick ent:"); - if (res.Status == PromptStatus.OK) - Env.Editor.ZoomObject(res.ObjectId.GetObject()!); - } - [CommandMethod("testzoomextent")] - public void Testzoomextent() - { - // using var tr = new DBTrans(); - // var res = Env.Editor.GetEntity("\npick ent:"); - // if (res.Status == PromptStatus.OK) - // { - // Env.Editor.ZoomObject(res.ObjectId.GetObject()); - // } - - Env.Editor.ZoomExtents(); - } - - [CommandMethod("testssget")] - public void Testssget() - { - var action_a = () => { Env.Print("this is a"); }; - var action_b = () => { Env.Print("this is b"); }; - - var keyword = new Dictionary - { - { "A", action_a }, - { "B", action_b } - }; - - var ss = Env.Editor.SSGet( ":S", messages: new string[2] { "get", "del" }, - keywords: keyword); - Env.Print(ss!); - } -} diff --git a/tests/Test/testenv.cs b/tests/Test/testenv.cs deleted file mode 100644 index e86343e..0000000 --- a/tests/Test/testenv.cs +++ /dev/null @@ -1,87 +0,0 @@ -namespace Test; - -public class Testenv -{ - [CommandMethod("testenum")] - public void Testenum() - { - - Env.CmdEcho = true; - - } - [CommandMethod("testenum1")] - public void Testenum1() - { - - Env.CmdEcho = false; - - } - - [CommandMethod("testdimblk")] - public void Testdimblk() - { - - Env.Dimblk = Env.DimblkType.Dot; - Env.Dimblk = Env.DimblkType.Defult; - Env.Dimblk = Env.DimblkType.Oblique; - - } - [CommandMethod("testdimblk1")] - public void Testdimblk1() - { - var dim = Env.Dimblk; - Env.Editor.WriteMessage(dim.ToString()); - - } - - [CommandMethod("testosmode")] - public void Testosmode() - { - // 设置osmode变量,多个值用逻辑或 - Env.OSMode = Env.OSModeType.End | Env.OSModeType.Middle; - // 也可以直接写数值,进行强转 - Env.OSMode = (Env.OSModeType)5179; - // 追加模式 - Env.OSMode |= Env.OSModeType.Center; - // 检查是否有某个模式 - var os = Env.OSMode.Include(Env.OSModeType.Center); - // 取消某个模式 - Env.OSMode ^= Env.OSModeType.Center; - Env.Editor.WriteMessage(Env.OSMode.ToString()); - } - [CommandMethod("testosmode1")] - public void Testosmode1() - { - var dim = Env.OSMode; - Env.Editor.WriteMessage(dim.ToString()); - - } - - [CommandMethod("testcadver")] - public void Testcadver() - { - // Env.Print(AcadVersion.Versions); - AcadVersion.Versions.ForEach(v => Env.Print(v)); - AcadVersion.FromApp(Acap.AcadApplication)?.Print(); - 1.Print(); - "1".Print(); - } - - [CommandMethod("TestGetVar")] - public void TestGetVar() - { - // test getvar - var a = Env.GetVar("dbmod"); - a.Print(); - Env.SetVar("dbmod1", 1); - } - - [CommandMethod("TestDwgVersion")] - public void TestDwgVersion() - { - // string filename = @"C:\Users\vic\Desktop\test.dwg"; - // var a = Helper.GetCadFileVersion(filename); - // a.Print(); - // ((DwgVersion)a).Print(); - } -} diff --git a/tests/Test/testid.cs b/tests/Test/testid.cs deleted file mode 100644 index 10a4b35..0000000 --- a/tests/Test/testid.cs +++ /dev/null @@ -1,73 +0,0 @@ -namespace Test; - -public class Testid -{ - [CommandMethod("testid")] - public void TestId() - { - using var tr = new DBTrans(); - Line line = new(new Point3d(0, 0, 0), new Point3d(1, 1, 0)); - tr.CurrentSpace.AddEntity(line); - tr.Dispose(); - - var res = Env.Editor.GetEntity("\npick ent:"); - if (res.Status == PromptStatus.OK) - { - res.ObjectId.Erase(); - } - // using (var tr = new DBTrans()) - // { - // var res = Env.Editor.GetEntity("\npick ent:"); - // if(res.Status == PromptStatus.OK) - // { - // res.ObjectId.Erase(); - // } - - // } - } - - [CommandMethod("testmycommand")] - public void TestMyCommand() - { - using var dbtrans = new DBTrans(Env.Document, true, false); - using var trans = Env.Database.TransactionManager.StartTransaction(); - - var l1 = new Line(new Point3d(0, 0, 0), new Point3d(100, 100, 0)); - var blkred = trans.GetObject(Env.Database.CurrentSpaceId, OpenMode.ForWrite) as BlockTableRecord; - blkred?.AppendEntity(l1); - trans.AddNewlyCreatedDBObject(l1, true); - trans.Commit(); - // dbtrans.Dispose(); - } - [CommandMethod("testtextstyle")] - public void TestTextStyle() - { - using var tr = new DBTrans(); - tr.TextStyleTable.Add("宋体", "宋体.ttf", 0.8); - - tr.TextStyleTable.Add("宋体1", FontTTF.宋体, 0.8); - tr.TextStyleTable.Add("仿宋体", FontTTF.仿宋, 0.8); - tr.TextStyleTable.Add("fsgb2312", FontTTF.仿宋GB2312, 0.8); - tr.TextStyleTable.Add("arial", FontTTF.Arial, 0.8); - tr.TextStyleTable.Add("romas", FontTTF.Romans, 0.8); - - - - tr.TextStyleTable.Add("daziti", ttr => - { - ttr.FileName = "ascii.shx"; - ttr.BigFontFileName = "gbcbig.shx"; - }); - } - - [CommandMethod("testtextstylechange")] - public void TestTextStyleChange() - { - using var tr = new DBTrans(); - - - tr.TextStyleTable.AddWithChange("宋体1", "simfang.ttf", height: 5); - tr.TextStyleTable.AddWithChange("仿宋体", "宋体.ttf"); - tr.TextStyleTable.AddWithChange("fsgb2312", "Romans", "gbcbig"); - } -} diff --git a/tests/Test/testselectfilter.cs b/tests/Test/testselectfilter.cs deleted file mode 100644 index dab975c..0000000 --- a/tests/Test/testselectfilter.cs +++ /dev/null @@ -1,33 +0,0 @@ -namespace Test; - -public class Testselectfilter -{ - [CommandMethod("testfilter")] - public void Testfilter() - { - - var p = new Point3d(10, 10, 0); - var f = OpFilter.Build( - e =>!(e.Dxf(0) == "line" & e.Dxf(8) == "0") - | e.Dxf(0) != "circle" & e.Dxf(8) == "2" & e.Dxf(10) >= p); - - - var f2 = OpFilter.Build( - e => e.Or( - !e.And(e.Dxf(0) == "line", e.Dxf(8) == "0"), - e.And(e.Dxf(0) != "circle", e.Dxf(8) == "2", - e.Dxf(10) <= new Point3d(10, 10, 0)))); - - SelectionFilter f3 = f; - SelectionFilter f4 = f2; - - Env.Editor.WriteMessage(""); - } - - [CommandMethod("testselectanpoint")] - public void Testselectanpoint() - { - var sel2 = Env.Editor.SelectAtPoint(new Point3d(0, 0, 0)); - Env.Editor.WriteMessage(""); - } -} diff --git a/tests/Test/wpf/Class1.cs b/tests/Test/wpf/Class1.cs deleted file mode 100644 index b711b1e..0000000 --- a/tests/Test/wpf/Class1.cs +++ /dev/null @@ -1,11 +0,0 @@ -namespace Test.wpf; - -public class Class1 -{ - [CommandMethod("testwpf")] - public void TestWPf() - { - var test = new TestView(); - Acap.ShowModalWindow(test); - } -} diff --git a/tests/Test/wpf/TestView.xaml b/tests/Test/wpf/TestView.xaml deleted file mode 100644 index e51e2ac..0000000 --- a/tests/Test/wpf/TestView.xaml +++ /dev/null @@ -1,24 +0,0 @@ - - - - - - - - - - - - diff --git a/tests/Test/wpf/TestView.xaml.cs b/tests/Test/wpf/TestView.xaml.cs deleted file mode 100644 index 4eb001e..0000000 --- a/tests/Test/wpf/TestView.xaml.cs +++ /dev/null @@ -1,28 +0,0 @@ -namespace Test.wpf; - -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; -using System.Windows; -using System.Windows.Controls; -using System.Windows.Data; -using System.Windows.Documents; -using System.Windows.Input; -using System.Windows.Media; -using System.Windows.Media.Imaging; -using System.Windows.Shapes; - - -/// -/// TestView.xaml 的交互逻辑 -/// -public partial class TestView : Window -{ - public TestView() - { - InitializeComponent(); - DataContext = new TestViewModel(); - } -} diff --git a/tests/Test/wpf/TestViewModel.cs b/tests/Test/wpf/TestViewModel.cs deleted file mode 100644 index e726029..0000000 --- a/tests/Test/wpf/TestViewModel.cs +++ /dev/null @@ -1,82 +0,0 @@ -namespace Test.wpf; - -using System.Windows; -using System.Windows.Input; - - -class TestViewModel : ViewModelBase -{ - string? name; - public string? Name - { - get { return name; } - set { Set(ref name, value); } - } - - private RelayCommand? clickCommand; - - public RelayCommand? ClickCommand - { - get - { - clickCommand ??= - new(execute => Name = "hello " + Name, - can => !string.IsNullOrEmpty(Name)); - return clickCommand; - } - } - - private bool receiveMouseMove; - public bool ReceiveMouseMove - { - get { return receiveMouseMove; } - set { Set(ref receiveMouseMove, value); } - } - - private string? tipText; - public string? TipText - { - get { return tipText; } - set { Set(ref tipText, value); } - } - - private RelayCommand? loadedCommand; - public RelayCommand? LoadCommand - { - get - { - loadedCommand ??= new(execute => MessageBox.Show("程序加载完毕")); - return loadedCommand; - } - } - - private RelayCommand? mouseMoveCommand; - public RelayCommand? MouseMoveCommand - { - get - { - mouseMoveCommand ??= new( - execute => { - var pt = execute.GetPosition(execute.Device.Target); - var left = "左键放开"; - var mid = "中键放开"; - var right = "右键放开"; - - if (execute.LeftButton == MouseButtonState.Pressed) - left = "左键放下"; - if (execute.MiddleButton == MouseButtonState.Pressed) - mid = "中键放下"; - if (execute.RightButton == MouseButtonState.Pressed) - right = "右键放下"; - TipText = $"当前鼠标位置:X={pt.X},Y={pt.Y}。当前鼠标状态:{left}、{mid}、{right}"; - }, - can => ReceiveMouseMove); - return mouseMoveCommand; - } - } - - public TestViewModel() - { - Name = "world"; - } -} diff --git a/tests/TestAcad08/Properties/Resources.Designer.cs b/tests/TestAcad08/Properties/Resources.Designer.cs deleted file mode 100644 index 1617020..0000000 --- a/tests/TestAcad08/Properties/Resources.Designer.cs +++ /dev/null @@ -1,63 +0,0 @@ -//------------------------------------------------------------------------------ -// -// 此代码由工具生成。 -// 运行时版本:4.0.30319.42000 -// -// 对此文件的更改可能会导致不正确的行为,并且如果 -// 重新生成代码,这些更改将会丢失。 -// -//------------------------------------------------------------------------------ - -namespace Test.Properties { - using System; - - - /// - /// 一个强类型的资源类,用于查找本地化的字符串等。 - /// - // 此类是由 StronglyTypedResourceBuilder - // 类通过类似于 ResGen 或 Visual Studio 的工具自动生成的。 - // 若要添加或移除成员,请编辑 .ResX 文件,然后重新运行 ResGen - // (以 /str 作为命令选项),或重新生成 VS 项目。 - [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "17.0.0.0")] - [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] - [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] - internal class Resources { - - private static global::System.Resources.ResourceManager resourceMan; - - private static global::System.Globalization.CultureInfo resourceCulture; - - [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")] - internal Resources() { - } - - /// - /// 返回此类使用的缓存的 ResourceManager 实例。 - /// - [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] - internal static global::System.Resources.ResourceManager ResourceManager { - get { - if (object.ReferenceEquals(resourceMan, null)) { - global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("Test.Properties.Resources", typeof(Resources).Assembly); - resourceMan = temp; - } - return resourceMan; - } - } - - /// - /// 重写当前线程的 CurrentUICulture 属性,对 - /// 使用此强类型资源类的所有资源查找执行重写。 - /// - [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] - internal static global::System.Globalization.CultureInfo Culture { - get { - return resourceCulture; - } - set { - resourceCulture = value; - } - } - } -} diff --git a/tests/TestAcad08/Properties/Resources.resx b/tests/TestAcad08/Properties/Resources.resx deleted file mode 100644 index 4fdb1b6..0000000 --- a/tests/TestAcad08/Properties/Resources.resx +++ /dev/null @@ -1,101 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - text/microsoft-resx - - - 1.3 - - - System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.3500.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - - - System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.3500.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - - \ No newline at end of file diff --git a/tests/TestAcad08/Properties/launchSettings.json b/tests/TestAcad08/Properties/launchSettings.json deleted file mode 100644 index 3aa279d..0000000 --- a/tests/TestAcad08/Properties/launchSettings.json +++ /dev/null @@ -1,10 +0,0 @@ -{ - "profiles": { - "Test": { - "commandName": "Executable", - "executablePath": "C:\\Program Files (x86)\\AutoCAD 2008\\acad.exe", - "commandLineArgs": "/nologo", - "nativeDebugging": false - } - } -} \ No newline at end of file diff --git a/tests/TestAcad08/TestAcad08.csproj b/tests/TestAcad08/TestAcad08.csproj deleted file mode 100644 index 97fb1a5..0000000 --- a/tests/TestAcad08/TestAcad08.csproj +++ /dev/null @@ -1,45 +0,0 @@ - - - preview - enable - - NET35 - true - true - x86 - True - - - - $(Configuration);acad;ac2008;ac2009;NewtonsoftJson - - - - - - - - - - - - - - - - - - - - - - MSBuild:Compile - - - - - - MSBuild:Compile - - - diff --git "a/tests/TestAcad08/\346\213\211\344\274\270\345\241\253\345\205\205/01.\346\213\211\344\274\270\345\241\253\345\205\205\345\217\263\351\224\256\350\217\234\345\215\225.cs" "b/tests/TestAcad08/\346\213\211\344\274\270\345\241\253\345\205\205/01.\346\213\211\344\274\270\345\241\253\345\205\205\345\217\263\351\224\256\350\217\234\345\215\225.cs" deleted file mode 100644 index 6afeef7..0000000 --- "a/tests/TestAcad08/\346\213\211\344\274\270\345\241\253\345\205\205/01.\346\213\211\344\274\270\345\241\253\345\205\205\345\217\263\351\224\256\350\217\234\345\215\225.cs" +++ /dev/null @@ -1,184 +0,0 @@ -using Autodesk.AutoCAD.Windows; -using System.Diagnostics; -using static IFoxCAD.Cad.PostCmd; -using MenuItem = Autodesk.AutoCAD.Windows.MenuItem; - -namespace JoinBoxAcad; -public class HatchPick -{ - [IFoxInitialize] - [CommandMethod(nameof(HatchPickInit))] - public void HatchPickInit() - { - Env.Printl($"※拉伸填充控制※\n{nameof(HatchPickSwitch)} - 切换开关\n"); - - if (Debugger.IsAttached) - Env.SetVar("hpscale", 22); - // 设定高版本双击填充启动修改面板 - // JoinBoxAcad.Menu.Cui.CuiInit(); - LoadHelper(true); - } - - // 只能命令卸载哦,因为关闭cad是不需要卸载的 - [CommandMethod(nameof(UnLoadHatchPick))] - public void UnLoadHatchPick() - { - LoadHelper(false); - } - - [CommandMethod(nameof(HatchPickSwitch))] - public void HatchPickSwitch() - { - if (HatchPickEvent.State.IsStop) - { - Env.Printl("已经 卸载 拉伸填充控制+ 用: " + nameof(HatchPickInit) + " 加载"); - return; - } - - if (HatchPickEvent.State.IsRun) - HatchPickEvent.State.Break(); - else - HatchPickEvent.State.Start(); - Env.Printl("已经 " + (HatchPickEvent.State.IsRun ? "开启" : "禁用") + " 拉伸填充控制+"); - } - - - internal static Dictionary MapDocHatchPickEvent = new(); - void LoadHelper(bool isLoad) - { - var dm = Acap.DocumentManager; - if (dm is null || dm.Count == 0) - return; - if (isLoad) - { - dm.DocumentCreated += Dm_DocumentCreated; - Dm_DocumentCreated(); // 自执行一次 - AddRightClickMenu(); - HatchPickEvent.AddInit(); - } - else - { - HatchPickEvent.RemoveInit(); - dm.DocumentCreated -= Dm_DocumentCreated; - UnDocumentCreated(); - HatchPick.RemoveRightClickMenu(); - } - } - - /// - /// 文档创建反应器 - /// - void Dm_DocumentCreated(object? sender = null, DocumentCollectionEventArgs? e = null) - { - var dm = Acap.DocumentManager; - if (dm is null || dm.Count == 0) - return; - var doc = dm.MdiActiveDocument; - if (doc is null) - return; - if (!MapDocHatchPickEvent.ContainsKey(doc)) - MapDocHatchPickEvent.Add(doc, new HatchPickEvent(doc)); - } - - /// - /// 卸载文档创建反应器 - /// - static void UnDocumentCreated() - { - var dm = Acap.DocumentManager; - if (dm is null || dm.Count == 0) - return; - var doc = dm.MdiActiveDocument; - if (doc is null) - return; - if (MapDocHatchPickEvent.ContainsKey(doc)) - { - MapDocHatchPickEvent[doc].Dispose(); - MapDocHatchPickEvent.Remove(doc); - } - } - - - - private const string V0 = "拉伸填充-开"; - private const string V1 = "拉伸填充-关";// (面板的独立填充必须关,否则致命错误) - private const string V2 = "独立填充";//(快捷,不需要关...目前还是会崩溃) - static readonly HashSet _menuItems = new() { V0, V1, V2 }; - static readonly ContextMenuExtension _contextMenu = new() { Title = "惊惊盒子" }; - /// - /// 添加右键菜单 - /// - void AddRightClickMenu() - { - // 右键菜单 - foreach (var item in _menuItems) - { - MenuItem mi = new(item); // 添加菜单项 - mi.Click += MenuItemClick; // 添加单击事件 - - //mi.MenuItems.Add(new MenuItem("改颜色1")); // 二级菜单 - _contextMenu.MenuItems.Add(mi); // 提交 - } - Acap.AddDefaultContextMenuExtension(_contextMenu);// 添加默认上下文菜单扩展,带标题的 - - //加入到某一种对象的右键菜单中 - //RXClass rxClass = Entity.GetClass(typeof(BlockReference)); - //Acap.AddObjectContextMenuExtension(rxClass, contextMenu); - //// 选择实体右键菜单才有用. 获得实体所属的RXClass类型 - // RXClass rx = RXObject.GetClass(typeof(Entity)); - // Acap.AddObjectContextMenuExtension(rx, contextMenu); // 这里为什么又可以不带标题 - } - - /// - /// 卸载右键菜单 - /// - static void RemoveRightClickMenu() - { - if (_contextMenu is null) - return; - Acap.RemoveDefaultContextMenuExtension(_contextMenu); - } - - /// - /// 右键点击触发 - /// - /// - /// - void MenuItemClick(object sender, EventArgs e) - { - // 获取发出命令的快捷菜单项 - if (sender is not MenuItem mi) - return; - - // 根据快捷菜单项的名字,分别调用对应的命令 - if (!_menuItems.Contains(mi.Text)) - return; - - switch (mi.Text) - { - case V0: - HatchPickEvent.State.Start(); - break; - case V1: - HatchPickEvent.State.Break(); - break; - case V2: - { - HatchPickEvent.State.Break(); - PromptSelectionOptions pso = new() - { - AllowDuplicates = true, // 不允许重复选择 - SingleOnly = true, // 隐含窗口选择(不需要空格确认) - }; - var ssPsr = Env.Editor.GetSelection(pso, HatchPickEvent.FilterForHatch); - if (ssPsr.Status != PromptStatus.OK) - return; - - Env.Editor.SetImpliedSelection(ssPsr.Value.GetObjectIds()); - SendCommand("-hatchedit H ", RunCmdFlag.AcedPostCommand); - HatchPickEvent.State.Start(); - } - break; - } - } -} \ No newline at end of file diff --git "a/tests/TestAcad08/\346\213\211\344\274\270\345\241\253\345\205\205/02.\346\213\211\344\274\270\345\241\253\345\205\205\344\272\213\344\273\266.cs" "b/tests/TestAcad08/\346\213\211\344\274\270\345\241\253\345\205\205/02.\346\213\211\344\274\270\345\241\253\345\205\205\344\272\213\344\273\266.cs" deleted file mode 100644 index f08eea8..0000000 --- "a/tests/TestAcad08/\346\213\211\344\274\270\345\241\253\345\205\205/02.\346\213\211\344\274\270\345\241\253\345\205\205\344\272\213\344\273\266.cs" +++ /dev/null @@ -1,1029 +0,0 @@ -using System.Drawing; -using System.Runtime.CompilerServices; -using System.Windows.Forms; -using static IFoxCAD.Cad.PostCmd; - -namespace JoinBoxAcad; - -public class HatchPickEvent : IDisposable -{ - #region 静态成员 - public static ProState State = new(); - // 选择集过滤器 - public static readonly SelectionFilter FilterForHatch = new(new TypedValue[] { new TypedValue((int)DxfCode.Start, "HATCH") }); - // 临时标记(重设选择集会触发一次选择集反应器) - static bool _selectChangedStop = false; - // 临时选择集用 - static List _hatchIds = new(); - // 获取夹点在哪个图元边界上面,是为true - static bool _pickInBo = false; - static bool _vetoProperties = false; - private Tolerance _tol = new(1e-6, 1e-6); - - public static void AddInit() - { - HatchHook.SetHook(); - // 全局事件重复+=是不需要担心的 - Acap.DocumentManager.DocumentLockModeChanged += Dm_VetoCommand; - State.Start(); - } - - public static void RemoveInit() - { - State.Stop(); - Acap.DocumentManager.DocumentLockModeChanged -= Dm_VetoCommand; - HatchHook.RemoveHook(); - } - - /// - /// 反应器->命令否决触发命令前(不可锁文档) - /// - /// - /// - public static void Dm_VetoCommand(object sender, DocumentLockModeChangedEventArgs e) - { - if (!State.IsRun) - return; - if (string.IsNullOrEmpty(e.GlobalCommandName) || e.GlobalCommandName == "#") - return; - switch (e.GlobalCommandName.ToUpper()) - { - case "PROPERTIES": // 特性面板 - { - // 事件顺序问题: - // 开cad之后第一次双击必弹出特性面板 - // 所以这里直接删除填充边界 - HatchPick.MapDocHatchPickEvent[e.Document].SetPropertiesInfoTask(); - if (_vetoProperties) - { - Debugx.Printl("Dm_VetoCommand 否决了"); - e.Veto(); - _vetoProperties = false; - // 发送编辑填充命令 - SendCommand("_hatchedit ", RunCmdFlag.AcedPostCommand); - return; - } - Debugx.Printl("Dm_VetoCommand 没否决"); - } - break; - } - } - - - #endregion - - #region 动态成员 - /// - /// 在位编辑器 记录全图选择集的填充 - /// - readonly HashSet _refeditSsgeting = new(); - /// - /// 在位编辑器 获取当前选择集做差集=>内部填充 - /// - readonly HashSet _refeditSsgeted = new(); - // map<填充id,边界转换器> - readonly Dictionary _mapHatchConv = new(); - readonly Document _doc; - public HatchPickEvent(Document doc) - { - _doc = doc; - LoadHelper(true); - } - - void LoadHelper(bool isLoad) - { - if (isLoad) - { - _doc.ImpliedSelectionChanged += Md_ImpliedSelectionChanged; - _doc.CommandWillStart += Md_CommandWillStart; - _doc.LispWillStart += Md_LispWillStart; - _doc.CommandEnded += Md_CommandEnded; - _doc.Database.ObjectErased += DB_ObjectErased; - _doc.Database.ObjectModified += DB_ObjectModified; - } - else - { - _doc.ImpliedSelectionChanged -= Md_ImpliedSelectionChanged; - _doc.CommandWillStart -= Md_CommandWillStart; - _doc.LispWillStart -= Md_LispWillStart; - _doc.CommandEnded -= Md_CommandEnded; - _doc.Database.ObjectErased -= DB_ObjectErased; - _doc.Database.ObjectModified -= DB_ObjectModified; - } - } - #endregion - - #region 事件 - /// - /// 反应器->command命令完成前 - /// - /// - /// - void Md_CommandWillStart(object sender, CommandEventArgs e) - { - if (!State.IsRun) - return; - - // 此处无法使用文档锁,否则将导致文档锁无法释放,然后ctrl+z失败 - var cmdup = e.GlobalCommandName.ToUpper(); - Debugx.Printl("Md_CommandWillStart::" + cmdup); - - switch (cmdup) - { - case "REFEDIT": - { - // 在位编辑命令,执行前,获取当前空间所有填充 - var prompt = Env.Editor.SelectAll(FilterForHatch); - if (prompt.Status != PromptStatus.OK) - return; - using DBTrans tr = new(); - GetHatchIds(prompt); - if (_hatchIds.Count == 0) - return; - for (int i = 0; i < _hatchIds.Count; i++) - _refeditSsgeting.Add(_hatchIds[i]); - } - break; - } - - // 拉伸夹点命令前触发 - if (cmdup != "GRIP_STRETCH") - { - EraseAllHatchBorders(); - } - else - { - var mp = HatchHook.MouseStartPoint; - var mouseStart = Screen.ScreenToCad(mp); - Debugx.Printl("mouseStart,屏幕点::" + mp); - Debugx.Printl("mouseStart,cad点::" + mouseStart); - - // 获取当前选择的对象,然后提取所有的夹点 - var prompt = Env.Editor.SelectImplied(); - if (prompt.Status != PromptStatus.OK) - return; - using DBTrans tr = new(); - GetHatchIds(prompt); - if (_hatchIds.Count == 0) - return; - - // TODO 屏幕像素点转cad点的误差,要随着视口高度而动态计算....这里的计算可能不太正确 - var tol = (double)Env.GetVar("viewsize") / 10; - Debugx.Printl("tol::" + tol); - - // 0x01 移动了矩形填充中间的夹点,删除边界,并且重新生成填充和边界 - // 0x02 移动了填充边界上的夹点,不处理,然后它会通过关联进行自己修改 - _pickInBo = false; - for (int i = 0; i < _hatchIds.Count; i++) - { - var hatId = _hatchIds[i]; - if (!_mapHatchConv.ContainsKey(hatId)) - continue; - - _mapHatchConv[hatId].BoundaryIds.ForEach((id, idState) => { - var boEnt = tr.GetObject(id); - if (boEnt == null) - return; - - // 获取夹点在哪个图元边界上 - HashSet boPts = new(); - if (boEnt is Circle circle) - { - // 圆形的边界夹点是: 圆心+半径 - var x = circle.Center.X; - var y = circle.Center.Y; - var z = circle.Center.Z; - var r = circle.Radius; - boPts.Add(new(x + r, y, z));//上 - boPts.Add(new(x - r, y, z));//下 - boPts.Add(new(x, y - r, z));//左 - boPts.Add(new(x, y + r, z));//右 - } - else - { - // 获取所有的边点 - // 这里圆形会获取圆心,所以剔除圆形 - var tmp = GetEntityPoint3ds(boEnt); - for (int j = 0; j < tmp.Count; j++) - boPts.Add(tmp[j]); - } - - if (boEnt is Arc arc) - { - if (!arc.StartPoint.IsEqualTo(arc.EndPoint, _tol)) - { - // 圆弧的腰点 - var arc2 = arc.GetPointAtDist(arc.GetDistAtPoint(arc.EndPoint) * 0.5); - boPts.Add(arc2); - } - } - else if (boEnt is Polyline pl) - { - for (int j = 0; j < pl.NumberOfVertices; j++) - { - var bulge = pl.GetBulgeAt(j); - if (bulge == 0.0) - continue; - // 有凸度就是有每段的中点 - var pta = pl.GetPoint2dAt(j); - Point2d ptb; - if (j + 1 < pl.NumberOfVertices) - ptb = pl.GetPoint2dAt(j + 1); - else - ptb = pl.GetPoint2dAt(0); - - var p = MathHelper.GetArcMidPoint(pta, ptb, bulge); - boPts.Add(p.Point3d()); - } - } - - boPts.ForEach((pt, ptState) => { - var dist = pt.DistanceTo(mouseStart); - //Debugx.Printl("pt::" + pt + " dist::" + dist); - if (dist < tol) - { - ptState.Break(); - _pickInBo = true; - } - }); - }); - - // 点在边界上:就不处理了,它会通过cad的关联填充反应器自动修改 - if (_pickInBo) - Debugx.Printl("夹点在边界上"); - else - Debugx.Printl("夹点不在边界上"); - } - } - } - - /// - /// 图元拉伸点 - /// - /// - /// - static List GetEntityPoint3ds(Entity ent) - { - var pts3d = new Point3dCollection(); - ent.GetStretchPoints(pts3d); - return pts3d.Cast().ToList(); - } - - - - - /// - /// 反应器->command命令完成后 - /// - /// - /// - void Md_CommandEnded(object sender, CommandEventArgs e) - { - if (!State.IsRun) - return; - - var cmdup = e.GlobalCommandName.ToUpper(); - switch (cmdup) - { - case "REFEDIT": - { - Debugx.Printl("Md_CommandEnded:: REFEDIT"); - - // 在位编辑命令,执行后,获取当前空间所有填充 - var prompt = Env.Editor.SelectAll(FilterForHatch); - if (prompt.Status != PromptStatus.OK) - return; - - using DBTrans tr = new(); - GetHatchIds(prompt); - if (_hatchIds.Count == 0) - return; - - for (int i = 0; i < _hatchIds.Count; i++) - if (!_refeditSsgeting.Contains(_hatchIds[i]))//Except - _refeditSsgeted.Add(_hatchIds[i]); - - var sb = new StringBuilder(); - foreach (var id in _refeditSsgeted) - sb.AppendLine(id.ToString()); - Env.Printl("块内填充id:" + sb.ToString()); - } - break; - case "REFSET": // 加减在位编辑图元 - { - Debugx.Printl("Md_CommandEnded:: REFSET"); - - // 命令历史的最后一行是:添加/删除 - var last = Env.GetVar("lastprompt").ToString(); - if (last is null) - return; - - // 完成后必然有上次选择集 - var prompt = Env.Editor.SelectPrevious(); - if (prompt.Status != PromptStatus.OK) - return; - using DBTrans tr = new(); - GetHatchIds(prompt); - if (_hatchIds.Count == 0) - return; - - // 就是因为无法遍历到在位编辑的块内图元,只能进行布尔运算 - if (last.Contains("添加") || last.Contains("Added"))// 中英文cad - { - for (int i = 0; i < _hatchIds.Count; i++) - { - _refeditSsgeting.Remove(_hatchIds[i]); - _refeditSsgeted.Add(_hatchIds[i]); - } - return; - } - if (last.Contains("删除") || last.Contains("Removed"))// 中英文cad - { - for (int i = 0; i < _hatchIds.Count; i++) - { - _refeditSsgeted.Remove(_hatchIds[i]); - _refeditSsgeting.Add(_hatchIds[i]); - } - return; - } - } - break; - case "REFCLOSE":// 保存块,清空集合 - { - Debugx.Printl("Md_CommandEnded:: REFCLOSE"); - _refeditSsgeted.Clear(); - _refeditSsgeting.Clear(); - } - break; - case "GRIP_STRETCH":// 拉伸夹点命令后触发 - { - // 夹点在边界上,退出 - if (_pickInBo) - return; - - // 夹点不在边界上: - // cad会平移填充,在这之后,我们删除填充边界,重建填充边界 - var prompt = Env.Editor.SelectImplied(); - if (prompt.Status != PromptStatus.OK) - return; - using DBTrans tr = new(); - GetHatchIds(prompt); - if (_hatchIds.Count == 0) - return; - - // 删除指定填充的边界,并清理关联反应器 - HashSet idsOfSsget = new(); - foreach (var hatId in _hatchIds) - { - idsOfSsget.Add(hatId); - - if (!_mapHatchConv.ContainsKey(hatId)) - continue; - bool clearFlag = false; - _mapHatchConv[hatId].BoundaryIds.ForEach(boId => { - if (!boId.IsOk()) - return; - var boEnt = tr.GetObject(boId); - if (boEnt == null) - return; - if (!HatchPickEnv.IsMeCreate(boEnt)) - return; - boId.Erase(); - clearFlag = true; - }); - - if (!clearFlag) - return; - - _mapHatchConv[hatId].BoundaryIds.Clear(); - - // 清理填充反应器 - var hatch = tr.GetObject(hatId); - if (hatch == null) - return; - using (hatch.ForWrite()) - RemoveAssociative(hatch); - CreatHatchConverter(hatch, idsOfSsget); - } - SetImpliedSelection(idsOfSsget); - } - break; - } - } - - /// - /// 获取选择集上的填充,在缓存内提取 - /// - /// - /// - static void GetHatchIds(PromptSelectionResult psr, DBTrans? tr = null) - { - tr ??= DBTrans.Top; - _hatchIds.Clear(); - var ids = psr.Value.GetObjectIds(); - for (int i = 0; i < ids.Length; i++) - { - var hatch = tr.GetObject(ids[i]); - if (hatch is not null) - _hatchIds.Add(ids[i]); - } - } - - /// - /// 反应器->lisp命令 - /// - void Md_LispWillStart(object sender, LispWillStartEventArgs e) - { - if (!State.IsRun) - return; - - using DBTrans tr = new(doclock: true); - EraseAllHatchBorders(); - } - - /// - /// 反应器->选择集 - /// - /// - /// - void Md_ImpliedSelectionChanged(object sender, EventArgs e) - { - if (!State.IsRun) - return; - - // 此处必须要文档锁 - if (_selectChangedStop) - { - _selectChangedStop = false; - return; - } - Debugx.Printl("Md_ImpliedSelectionChanged"); - - using DBTrans tr = new(doclock: true); - var prompt = Env.Editor.SelectImplied(); - if (prompt.Status != PromptStatus.OK) - { - EraseAllHatchBorders(); - return; - } - - // 获取图层锁定的记录,用于跳过 - Dictionary islocks = new(); - foreach (var layerRecord in tr.LayerTable.GetRecords()) - if (!layerRecord.IsErased)// 08符号表记录保留了这个 - islocks.Add(layerRecord.Name, layerRecord.IsLocked); - - // 遍历选择,创建边界转换器 - // 重设选择集 - HashSet idsOfSsget = new(); - foreach (var entId in prompt.Value.GetObjectIds()) - { - idsOfSsget.Add(entId); - var hatch = tr.GetObject(entId, openLockedLayer: true); - if (hatch is null) - continue; - if (islocks[hatch.Layer]) - continue; - // 重复选择 || 在位编辑外 - if (_mapHatchConv.ContainsKey(entId) || _refeditSsgeting.Contains(entId)) - continue; - CreatHatchConverter(hatch, idsOfSsget); - } - SetImpliedSelection(idsOfSsget); - } - - /// - /// 创建填充和填充边界转换器 - /// - /// - /// - /// - void CreatHatchConverter(Hatch hatch, HashSet outSsgetIds, DBTrans? tr = null) - { - tr ??= DBTrans.Top; - - var hc = new HatchConverter(hatch); - ObjectId newid; - - // 如果边界在图纸上没有删除(删除就不是关联的), - // 那就不创建新的,然后选中它们 - if (hc.BoundaryIds.Count != 0) - { - Debugx.Printl("CreatHatchConverter:: 加入了现有边界到选择集"); - - // 加入选择集 - foreach (var item in hc.BoundaryIds) - outSsgetIds.Add(item); - outSsgetIds.Add(hatch.ObjectId); - newid = hatch.ObjectId; - } - else - { - Debugx.Printl("CreatHatchConverter:: 创建新填充和边界"); - - // 创建新填充和边界 - hc.GetBoundarysData(); - newid = hc.CreateBoundarysAndHatchToMsPs(tr.CurrentSpace, trans: tr); - HatchPickEnv.SetMeXData(newid, hc.BoundaryIds); - - // 清理上次,删除边界和填充 - if (_mapHatchConv.ContainsKey(hatch.ObjectId)) - { - var boIds = _mapHatchConv[hatch.ObjectId].BoundaryIds; - for (int i = 0; i < boIds.Count; i++) - boIds[i].Erase(); - _mapHatchConv.Remove(hatch.ObjectId); - } - // 删除选中的 - hatch.ObjectId.Erase(); - } - - if (!_mapHatchConv.ContainsKey(newid)) - _mapHatchConv.Add(newid, hc); - else - _mapHatchConv[newid] = hc; - - if (newid == hatch.ObjectId) - return; - - // 优先: 块内含有旧的,就加入新的 - if (_refeditSsgeted.Contains(hatch.ObjectId)) - { - _refeditSsgeted.Remove(hatch.ObjectId); - _refeditSsgeted.Add(newid); - } - else if (_refeditSsgeting.Contains(hatch.ObjectId)) - { - _refeditSsgeting.Remove(hatch.ObjectId); - _refeditSsgeting.Add(newid); - } - } - - /// - /// 重设选择集 - /// - /// 加入选择集的成员 - void SetImpliedSelection(HashSet setImpSelect) - { - // 获取填充 - foreach (var id in _mapHatchConv.Keys) - setImpSelect.Add(id); - - // 获取填充边界 - foreach (var item in _mapHatchConv.Values) - foreach (var id in item.BoundaryIds) - setImpSelect.Add(id); - - // 设置选择集,没有标记的话会死循环 - _selectChangedStop = true; - Env.Editor.SetImpliedSelection(setImpSelect.ToArray()); - } - - /// - /// 删除全部填充边界 - /// - void EraseAllHatchBorders() - { - if (_mapHatchConv.Count == 0) - return; - foreach (var dict in _mapHatchConv) - { - dict.Value.BoundaryIds.ForEach(boId => { - if (!boId.IsOk()) - return; - using DBTrans tr = new(database: boId.Database); - var boEnt = tr.GetObject(boId, OpenMode.ForWrite); - if (boEnt == null) - return; - // 删除填充边界并清理关联反应器 - if (!HatchPickEnv.IsMeCreate(boEnt)) - return; - boEnt.Erase(); - if (dict.Key.IsOk()) - { - var hatch = tr.GetObject(dict.Key, OpenMode.ForWrite); - if (hatch == null) - return; - RemoveAssociative(hatch); - } - }); - } - _mapHatchConv.Clear(); - } - - /// - /// 移除关联反应器 - /// - /// - static void RemoveAssociative(Hatch hatch) - { - // 撤回填充,没有边界就移除关联反应器 - if (!hatch.Associative) - return; - - // 填充边界反应器 - var assIds = hatch.GetAssociatedObjectIds(); - if (assIds == null) - return; - bool isok = true; - foreach (ObjectId id in assIds) - { - if (!id.IsOk()) - { - isok = false; - break; - } - } - // 这里边界id已经删除了,所以移除会导致异常 - if (isok) - hatch.RemoveAssociatedObjectIds(); - // 取消关联反应器才能生成的正确 - hatch.Associative = false; - } - - /// - /// 撤回事件(获取删除对象) - /// - /// - /// - static void DB_ObjectErased(object sender, ObjectErasedEventArgs e) - { - if (!State.IsRun) - return; - - // object erased. - if (e.Erased) - { - return; - } - - // UNDO - if (e.DBObject is Hatch hatch) - { - if (HatchPickEnv.IsMeCreate(hatch)) - RemoveAssociative(hatch); - } - else if (e.DBObject is Entity boEnt) - { - // 撤回边界 - if (HatchPickEnv.IsMeCreate(boEnt)) - { - boEnt.Erase(); - // 通过xdata回溯填充,清理关联反应器 - if (boEnt.XData != null) - { - using DBTrans tr = new(); - var hatchId = HatchPickEnv.GetXdataHatch(boEnt); - if (hatchId.IsOk()) - { - var hatchEnt = tr.GetObject(hatchId, OpenMode.ForWrite); - if (hatchEnt != null) - RemoveAssociative(hatchEnt); - } - } - } - } - } - - /// - /// 撤回事件(更改时触发) - /// 它会获取有修改步骤的图元id - /// - /// - /// - static void DB_ObjectModified(object sender, ObjectEventArgs e) - { - if (!State.IsRun) - return; - - // 然后删除我制造的拉伸填充上面的关联反应器 - if (!e.DBObject.IsUndoing) - return; - if (e.DBObject.IsErased) - return; - // 是我生成的填充才删除关联 - if (e.DBObject is Hatch hatch) - { - if (HatchPickEnv.IsMeCreate(hatch)) - RemoveAssociative(hatch); - } - } - - void SetPropertiesInfoTask() - { - // 原有选择集 - var prompt = Env.Editor.SelectImplied(); - if (prompt.Status != PromptStatus.OK) - return; - - using DBTrans tr = new(); - - // 获取记录的边界 - HashSet boAll = new(); - foreach (var hc in _mapHatchConv.Values) - foreach (var boid in hc.BoundaryIds) - boAll.Add(boid); - - // 获取选择集上面所有的填充,如果没有填充就结束(不屏蔽特性面板) - bool hasHatch = false; - HashSet idsOfSsget = new(); - foreach (var id in prompt.Value.GetObjectIds()) - { - // 含有填充 - if (_mapHatchConv.ContainsKey(id)) - hasHatch = true; - // 排除边界的加入 - if (!boAll.Contains(id)) - idsOfSsget.Add(id); - } - if (!hasHatch) - return; - - // 删除填充边界,并清理关联反应器 - EraseAllHatchBorders(); - - // 重设选择集 提供给后续命令判断 - SetImpliedSelection(idsOfSsget); - - // 如果有填充才否决 - _vetoProperties = idsOfSsget.Count != 0; - } - #endregion - - #region IDisposable接口相关函数 - public bool IsDisposed { get; private set; } = false; - - /// - /// 手动调用释放 - /// - public void Dispose() - { - Dispose(true); - GC.SuppressFinalize(this); - } - - /// - /// 析构函数调用释放 - /// - ~HatchPickEvent() - { - Dispose(false); - } - - protected virtual void Dispose(bool disposing) - { - // 不重复释放 - if (IsDisposed) return; - IsDisposed = true; - - if (_doc.IsDisposed) - return; - LoadHelper(false); - } - #endregion -} - -/// -/// 填充的鼠标钩子 -/// -public static class HatchHook -{ - static readonly MouseHook MouseHook; - // 夹点拉伸前的点,拉伸后利用命令后反应器去处理"GRIP_STRETCH" - static volatile int _X; - static volatile int _Y; - public static Point MouseStartPoint { get => new(_X, _Y); } - - /// - /// 鼠标双击事件 - /// - public static event EventHandler? DoubleClick; - - static HatchHook() - { - MouseHook = new(); - } - - /// - /// 查找主线程
    - /// 代替
    - /// 托管线程和他们不一样: - ///
    - /// 主窗口 - /// 进程ID - /// 线程ID - [DllImport("user32.dll", SetLastError = true)] - static extern uint GetWindowThreadProcessId(IntPtr hWnd, out uint lpdwProcessId); - - [DllImport("user32.dll")] - static extern bool IsWindowEnabled(IntPtr hWnd); - /// - /// 获取当前窗口 - /// - /// 当前窗口标识符 - [DllImport("user32.dll")] - static extern IntPtr GetForegroundWindow(); - - public static void SetHook() - { - // 如果是全局钩子会发生偶尔失效的情况,改用进程钩子反而好多了 - MouseHook.SetHook(true); - MouseHook.MouseDown += (sender, e) => { - // 此处断点时候就会使得钩子失效 - if (!IsWindowEnabled(Acap.MainWindow.Handle)) - return; - // 进程号拦截 - GetWindowThreadProcessId(GetForegroundWindow(), out uint winId); - if (MouseHook.Process.Id != winId) - return; - if (e.Button == MouseButtons.Left) - { - _X = e.X; - _Y = e.Y; - } - }; - - MouseHook.DoubleClick += (sender, e) => { - // 此处断点时候就会使得钩子失效 - if (!IsWindowEnabled(Acap.MainWindow.Handle)) - return; - // 进程号拦截 - GetWindowThreadProcessId(GetForegroundWindow(), out uint winId); - if (MouseHook.Process.Id != winId) - return; - DoubleClick?.Invoke(sender, e); - }; - } - - public static void RemoveHook() - { - MouseHook?.Dispose(); - } -} - -public static class HatchPickEnv -{ - //static readonly string _appName = nameof(JoinBox); - //static readonly string _data = nameof(CreateBoundary); - - static readonly string _appName = "JoinBox"; - static readonly string _data = "CreateBoundary"; - - /// - /// 判断图元是否由 我的转换器 创建(相对的是直接提取现有图元边界) - /// - /// 任何图元 - /// - public static bool IsMeCreate(Entity entity) - { - if (entity.XData == null) - return false; - var xl = (XDataList)entity.XData; - return xl.Contains(_appName, _data); - } - - /// - /// 我的转换器 xdata数据模板 - /// - /// - /// - /// - public static ResultBuffer GetMeBuffer(Handle hatchHandle, DBTrans? trans = null) - { - trans ??= DBTrans.Top; - trans.RegAppTable.Add(_appName); // add函数会默认的在存在这个名字的时候返回这个名字的regapp的id,不存在就新建 - ResultBuffer resBuf = new() - { - new((int)DxfCode.ExtendedDataRegAppName, _appName), - new((int)DxfCode.ExtendedDataAsciiString,_data), - new((int)DxfCode.ExtendedDataHandle, hatchHandle),//边界回溯这个填充的句柄,如果创建新填充,就需要再去改 - }; - return resBuf; - } - - /// - /// 填充和边界上面增加xdata,实现区分原生和我的数据 - /// - /// - /// - /// - public static void SetMeXData(ObjectId newHatchId, List boIds, DBTrans? trans = null) - { - trans ??= DBTrans.Top; - var hatchEnt = trans.GetObject(newHatchId); - if (hatchEnt != null) - using (hatchEnt.ForWrite()) - hatchEnt.XData = GetMeBuffer(hatchEnt.Handle, trans); // 设置xdata仅仅为debug可以通过鼠标悬停看见它数据,因此设置为自己 - - // 修改边界的xdata为新填充的 - boIds.ForEach(id => { - var boEnt = trans.GetObject(id); - if (boEnt is null) - return; - using (boEnt.ForWrite()) - { - boEnt.RemoveXData(_appName); - boEnt.XData = GetMeBuffer(newHatchId.Handle, trans); - } - }); - } - - /// - /// 通过边界ent获取填充id - /// - /// 边界图元 - /// - /// - public static ObjectId GetXdataHatch(Entity boEntity, DBTrans? trans = null) - { - if (boEntity.XData == null) - return ObjectId.Null; - XDataList data = boEntity.XData; - - if (!data.Contains(_appName, _data)) - return ObjectId.Null; - - var indexs = data.GetXdataAppIndex(_appName, new DxfCode[] { DxfCode.ExtendedDataHandle }); - if (indexs.Count == 0) - return ObjectId.Null; - - trans ??= DBTrans.Top; - return trans.GetObjectId(data[indexs[0]].Value.ToString()); - } -} - -public static class MathHelper -{ - /// - /// 圆弧的腰点 - /// - /// 圆弧点1 - /// 圆弧点3 - /// 凸度 - /// 返回腰点 - /// - [MethodImpl] - public static Point2d GetArcMidPoint(Point2d arc1, Point2d arc3, double bulge) - { - if (bulge == 0) - throw new ArgumentException("凸度为0,此线是平的"); - - var center = GetArcBulgeCenter(arc1, arc3, bulge); - var angle1 = center.GetVectorTo(arc1).GetAngle2XAxis(); - var angle3 = center.GetVectorTo(arc3).GetAngle2XAxis(); - // 利用边点进行旋转,就得到腰点,旋转角/2 - // 需要注意镜像的多段线 - double angle = angle3 - angle1; - if (bulge > 0) - { - if (angle < 0) - angle += Math.PI * 2; - } - else - { - if (angle > 0) - angle += Math.PI * 2; - } - return arc1.RotateBy(angle / 2, center); - } - /// http://bbs.xdcad.net/thread-722387-1-1.html - /// https://blog.csdn.net/jiangyb999/article/details/89366912 - /// - /// 凸度求圆心 - /// - /// 圆弧头点 - /// 圆弧尾点 - /// 凸度 - /// 圆心 - [MethodImpl] - public static Point2d GetArcBulgeCenter(Point2d arc1, Point2d arc3, double bulge) - { - if (bulge == 0) - throw new ArgumentException("凸度为0,此线是平的"); - - var x1 = arc1.X; - var y1 = arc1.Y; - var x2 = arc3.X; - var y2 = arc3.Y; - - var b = (1 / bulge - bulge) / 2; - var x = (x1 + x2 - b * (y2 - y1)) / 2; - var y = (y1 + y2 + b * (x2 - x1)) / 2; - return new Point2d(x, y); - } - - /// - /// X轴到向量的弧度,cad的获取的弧度是1PI,所以转换为2PI(上小,下大) - /// - /// 向量 - /// X轴到向量的弧度 - public static double GetAngle2XAxis(this Vector2d ve, double tolerance = 1e-6) - { - const double Tau = Math.PI + Math.PI; - // 世界重合到用户 Vector3d.XAxis->两点向量 - double al = Vector2d.XAxis.GetAngleTo(ve); - al = ve.Y > 0 ? al : Tau - al; // 逆时针为正,大于0是上半圆,小于则是下半圆,如果-负值控制正反 - al = Math.Abs(Tau - al) <= tolerance ? 0 : al; - return al; - } -} \ No newline at end of file diff --git "a/tests/TestAcad08/\346\213\211\344\274\270\345\241\253\345\205\205/HatchHelper.cs" "b/tests/TestAcad08/\346\213\211\344\274\270\345\241\253\345\205\205/HatchHelper.cs" deleted file mode 100644 index 6d6c243..0000000 --- "a/tests/TestAcad08/\346\213\211\344\274\270\345\241\253\345\205\205/HatchHelper.cs" +++ /dev/null @@ -1,124 +0,0 @@ -namespace JoinBoxAcad; - -public static class HatchHelper -{ - /// - /// 遍历填充每条边 - /// - /// - /// - public static void ForEach(this Hatch hatch, Action action) - { - for (int i = 0; i < hatch.NumberOfLoops; i++) - action.Invoke(hatch.GetLoopAt(i)); - } - - -#if false - /// - /// 分离填充边界 - /// 将外边界和包含的边界成为一个集 - /// - /// 填充的边界(只有多段线和圆) - /// 多个id集,id中第一个是外边界 - public static IEnumerable[] SeparationBorder(this IEnumerable bianjie) - { - IEnumerable[] objectIds = null; - if (bianjie.Length < 1) return null; - - Database db = bianjie[0].Database; - - // 首先获取一个图元,这个图元默认成边界,看它是否包含第二个,如果是,加入a集 - // 如果不是,它是否把自己包含了, - // 如果是,把它加入a集,并下次使用它来成为外边界 - // 如果不是,则为新边界,把它加入b集 - List alist = new List(); // 边界包含 - List blist = new List(); // 有另外的边界 - List> clist = new List>();// 边界集 - - using (Transaction tr = db.TransactionManager.StartTransaction()) - { - while (true) - { - for (int i = 0; i < bianjie.Length; i++) - { - Entity ent = bianjie[i].ObjectIdToEntity(false); - ent.UpgradeOpen(); - for (int j = i + 1; j < bianjie.Length; j++) - { - if (ent is Polyline polyline)// 多段线 - { - } - else if (ent is Circle circle) // 圆 - { - } - } - ent.DowngradeOpen(); - } - - if (blist.Count == 0)// 没有其他边界就结束循环 - { - break; - } - // 把blist的第一个用来作为新的外边界 - } - } - return objectIds; - } - - /// - /// 判断边界是否包含图元 布尔算法... - /// - /// 边界 - /// 包含的图元 - /// 是true,否false - public static bool BorderIntoCollect(this ObjectId border, ObjectId include) - { - bool flag = false; - Database db = border.Database; - using (Transaction tr = db.TransactionManager.StartTransaction()) - { - Entity ent = border.ObjectIdToEntity(false); - Entity ent2 = include.ObjectIdToEntity(false); - - if (ent is Polyline polyline)// 多段线边界 - { - if (ent2 is Polyline polyline2)// 多段线 - { - } - else if (ent2 is Circle circle2) // 圆 - { - // 判断圆心在多段线内 - if (circle2.Center.RayCasting(polyline.GetPolylinePoints()) != 3) - { - if (true)// 半径若大于多段线最长那段,表示包含不到,是圆包含了多段线(含有弧度就错了) - { - flag = true; - } - } - else // 圆心不在边界内,判断边界是否有交点 - { - } - } - } - else if (ent is Circle circle) // 圆边界 - { - if (ent2 is Polyline polyline2)// 多段线 - { - } - else if (ent2 is Circle circle2) // 圆 - { - // 填充边界不存在交集 - // 两个圆心的距离>两个圆的半径和=两圆分离 - double length = circle.Center.GetDistanceBetweenTwoPoint(circle2.Center); - if (length < circle.Radius + circle2.Radius) - { - flag = true; - } - } - } - return flag; - } - } -#endif -} \ No newline at end of file diff --git "a/tests/TestAcad08/\346\213\211\344\274\270\345\241\253\345\205\205/\345\261\217\345\271\225\345\235\220\346\240\207\350\275\254cad\345\235\220\346\240\207.cs" "b/tests/TestAcad08/\346\213\211\344\274\270\345\241\253\345\205\205/\345\261\217\345\271\225\345\235\220\346\240\207\350\275\254cad\345\235\220\346\240\207.cs" deleted file mode 100644 index 00e7c64..0000000 --- "a/tests/TestAcad08/\346\213\211\344\274\270\345\241\253\345\205\205/\345\261\217\345\271\225\345\235\220\346\240\207\350\275\254cad\345\235\220\346\240\207.cs" +++ /dev/null @@ -1,133 +0,0 @@ -//#define cpp -namespace JoinBoxAcad; - -using System.Drawing; -using static IFoxCAD.Cad.WindowsAPI; - -public partial class Screen -{ - [CommandMethod(nameof(GetScreenToCadxx))] - public static void GetScreenToCadxx() - { - var dm = Acap.DocumentManager; - var doc = dm.MdiActiveDocument; - var ed = doc.Editor; - var ucsPoint = GetScreenToCad(); - ed.WriteMessage(ucsPoint.ToString() + "\n"); - } - - /// - /// 屏幕坐标转cad坐标 - /// - public static Point3d GetScreenToCad() - { - // 两种获取方式都可以 - var cursorPos = System.Windows.Forms.Control.MousePosition; - // GetCursorPos(out Point cursorPos); - return ScreenToCad(cursorPos); - } - - /// - /// 屏幕像素点转cad图纸坐标点 - /// - /// 屏幕像素点 - /// 返回ucs的点 - public static Point3d ScreenToCad(Point cursorPos) - { - var dm = Acap.DocumentManager; - var doc = dm.MdiActiveDocument; - var ed = doc.Editor; - var mid = WindowsAPI.GetParent(doc.Window.Handle); - - ScreenToClient(mid, ref cursorPos); - var vn = ed.GetViewportNumber(cursorPos);// System.Windows.Forms.Control.MousePosition - var wcsPoint = ed.PointToWorld(cursorPos, vn); - var ucsPoint = wcsPoint.TransformBy(doc.Editor.CurrentUserCoordinateSystem.Inverse()); - return ucsPoint; - } - - /// - /// 屏幕坐标到客户区坐标转换 - /// - /// 窗口句柄 - /// 点结构,返回屏幕坐标 - /// - [DllImport("user32.dll")] - public static extern bool ScreenToClient(IntPtr hWnd, ref Point lpPoint); - [DllImport("user32.dll")] - static extern bool ClientToScreen(IntPtr hWnd, ref Point lpPoint); - - public static Point CadToScreen(Point3d pt3d, Point mousePosition) - { - var dm = Acap.DocumentManager; - var doc = dm.MdiActiveDocument; - var ed = doc.Editor; - var mid = WindowsAPI.GetParent(doc.Window.Handle); - - var vn = ed.GetViewportNumber(mousePosition);//System.Windows.Forms.Control.MousePosition - var ptScr = Env.Editor.PointToScreen(pt3d, vn);// 高版本这个不一样,转为客户区 - var ptScrWin = new Point((int)ptScr.X, (int)ptScr.Y); - ClientToScreen(mid, ref ptScrWin); - return ptScrWin; - } - - //#if NET35 - // [DllImport("acad.exe", EntryPoint = "?acedGetAcadDwgView@@YAPAVCView@@XZ")] //acad08 - //#else - // [DllImport("acad.exe", EntryPoint = "?acedGetAcadDwgView@@YAPEAVCView@@XZ")]//acad21 - //#endif - // static extern IntPtr AcedGetAcadDwgview(); - - delegate IntPtr DelegateAcedGetAcadDwgview(); - static DelegateAcedGetAcadDwgview? acedGetAcadDwgView; - /// - /// 获取视口指针 - /// - public static IntPtr AcedGetAcadDwgview() - { - if (acedGetAcadDwgView is null) - { - acedGetAcadDwgView = AcadPeInfo - .GetDelegate( - nameof(acedGetAcadDwgView), AcadPeEnum.AcadExe); - } - if (acedGetAcadDwgView is not null) - return acedGetAcadDwgView.Invoke();// 调用方法 - return IntPtr.Zero; - } - - delegate int DelegateAcedGetWinNum(int x, int y); - static DelegateAcedGetWinNum? acedGetWinNum; - /// - /// 获取窗口数字 - /// - public static int AcedGetWinNum(int x, int y) - { - if (acedGetWinNum is null) - acedGetWinNum = AcadPeInfo - .GetDelegate( - nameof(acedGetWinNum), AcadPeEnum.ExeAndCore); - if (acedGetWinNum is not null) - return acedGetWinNum.Invoke(x, y);// 调用方法 - return 0; - } - - /// - /// 将坐标从绘图窗口转换为活动视口坐标系 - /// - /// - /// - /// - /// -#if NET35 - // 此处都是acad08这个有重载,不知道PeInfo能不能正常运行 - [DllImport("acad.exe", EntryPoint = "?acedCoordFromPixelToWorld@@YAHHVCPoint@@QAN@Z")] - static extern int AcedCoordFromPixelToWorld(int windnum, Point pt, out Point3D ptOut); - - [DllImport("acad.exe", EntryPoint = "?acedCoordFromPixelToWorld@@YAXABVCPoint@@QAN@Z")]//这个重载参数不知道 - static extern int AcedCoordFromPixelToWorld(Point pt, out Point3D ptOut); -#else - [DllImport("accore.dll", EntryPoint = "?acedCoordFromPixelToWorld@@YAHHVCPoint@@QEAN@Z")] - static extern int AcedCoordFromPixelToWorld(int windnum, Point pt, out Point3D ptOut); -#endif -} \ No newline at end of file diff --git "a/tests/TestAcad08/\346\213\211\344\274\270\345\241\253\345\205\205/\350\277\207\347\250\213\346\226\207\344\273\266/\344\270\200\344\270\252\346\227\240\346\263\225\347\247\273\345\212\250\347\272\242\350\211\262\345\234\206\347\232\204\344\276\213\345\255\220.cs" "b/tests/TestAcad08/\346\213\211\344\274\270\345\241\253\345\205\205/\350\277\207\347\250\213\346\226\207\344\273\266/\344\270\200\344\270\252\346\227\240\346\263\225\347\247\273\345\212\250\347\272\242\350\211\262\345\234\206\347\232\204\344\276\213\345\255\220.cs" deleted file mode 100644 index 4e45d21..0000000 --- "a/tests/TestAcad08/\346\213\211\344\274\270\345\241\253\345\205\205/\350\277\207\347\250\213\346\226\207\344\273\266/\344\270\200\344\270\252\346\227\240\346\263\225\347\247\273\345\212\250\347\272\242\350\211\262\345\234\206\347\232\204\344\276\213\345\255\220.cs" +++ /dev/null @@ -1,94 +0,0 @@ -// 一个无法移动红色圆的例子 https://www.keanw.com/2008/08/rolling-back-th.html -#if true2 -namespace JoinBoxAcad; - -public class CmdReactor -{ - Document? _doc; - ObjectIdCollection _ids = new(); - Point3dCollection _pts = new(); - - [CommandMethod(nameof(Test_REACTOR))] - public void Test_REACTOR() - { - _doc = Acap.DocumentManager.MdiActiveDocument; - _doc.CommandWillStart += Doc_CommandWillStart; - } - - /// - /// 挂载一个命令反应器,如果出现了move就挂载一个 - /// - /// - /// - void Doc_CommandWillStart(object sender, CommandEventArgs e) - { - if (e.GlobalCommandName == "MOVE") - { - _ids.Clear(); - _pts.Clear(); - - if (_doc is null) - return; - _doc.Database.ObjectOpenedForModify += Db_ObjectOpenedForModify; - _doc.CommandCancelled += Doc_CommandEnded; - _doc.CommandEnded += Doc_CommandEnded; - _doc.CommandFailed += Doc_CommandEnded; - } - } - - /// - /// 卸载一堆反应器 - /// - void RemoveEventHandlers() - { - if (_doc is null) - return; - _doc.CommandCancelled -= Doc_CommandEnded; - _doc.CommandEnded -= Doc_CommandEnded; - _doc.CommandFailed -= Doc_CommandEnded; - _doc.Database.ObjectOpenedForModify -= Db_ObjectOpenedForModify; - } - - void Doc_CommandEnded(object sender, CommandEventArgs e) - { - // 在恢复位置之前删除数据库reactor - RemoveEventHandlers(); - RollbackLocations(); - } - - /// - /// 颜色是1的圆加入集合 - /// - /// - /// - void Db_ObjectOpenedForModify(object sender, ObjectEventArgs e) - { - if (e.DBObject is Circle circle && circle.ColorIndex == 1)// 如果颜色是1 - { - // 不含有就加入集合 - if (!_ids.Collection.Contains(circle.ObjectId)) - { - _ids.Add(circle.ObjectId); - _pts.Add(circle.Center); - } - } - } - - /// - /// 修改圆心 - /// - void RollbackLocations() - { - Debug.Assert(_ids.Count == _pts.Count, "预计相同数量的ID和位置"); - _doc?.Database.Action(tr => { - int i = 0; - foreach (ObjectId id in _ids.Collection) - { - var circle = tr.GetObject(id, OpenMode.ForWrite) as Circle; - if (circle is not null) - circle.Center = _pts[i++]; - } - }); - } -} -#endif \ No newline at end of file diff --git "a/tests/TestAcad08/\346\213\211\344\274\270\345\241\253\345\205\205/\350\277\207\347\250\213\346\226\207\344\273\266/\350\207\252\345\256\232\344\271\211\345\233\276\345\205\203\345\241\253\345\205\205\350\276\271\347\225\214.txt" "b/tests/TestAcad08/\346\213\211\344\274\270\345\241\253\345\205\205/\350\277\207\347\250\213\346\226\207\344\273\266/\350\207\252\345\256\232\344\271\211\345\233\276\345\205\203\345\241\253\345\205\205\350\276\271\347\225\214.txt" deleted file mode 100644 index 50ccab0..0000000 --- "a/tests/TestAcad08/\346\213\211\344\274\270\345\241\253\345\205\205/\350\277\207\347\250\213\346\226\207\344\273\266/\350\207\252\345\256\232\344\271\211\345\233\276\345\205\203\345\241\253\345\205\205\350\276\271\347\225\214.txt" +++ /dev/null @@ -1,104 +0,0 @@ -拉伸填充bug: -1号: 不闭合的多段线,新建边界会丢失一个不和头点重叠的倒数第二个点. -与头点重叠是ok的 -闭合是ok的 -解决:因为倒数第二个点要从 curve.EndPoint 获取. - -2号: -如果画了一个矩形,然后填充,再删除边界,再点选填充,那么通过生成出来关联标注, -移动第一个点的时候,是没有响应的,移动第二次的时候才会响应. - -两种想法解决这个问题, -a:生成之后,用移动矩阵影响一下关联反应器. - 答:不会触发的,即使我分成两个事务分别提交. -b:先克隆一个填充,再创建关联边界. - 答:可行的,但是会导致3号问题的发生. - -3号: -由于我是生成填充的,所以在在位编辑的时候会在长事务内部: - -【贵妃】惊惊 2019/7/13 17:52:02 -我遇到了一个问题,如果是在位编辑的时候,当前空间是模型空间,那么我用函数克隆一个块外的东西到模型空间,实际上会克隆到在位编辑的内部... -我都不知道怎么处理这个情况了..莫非要关闭用户的在位编辑状态么.. - -【才人】edata 2019/7/13 17:57:15 -在位编辑是这样的. - -【贵妃】惊惊 2019/7/13 17:58:08 -那桌子是怎么控制在位编辑-减出去块外的? - -【才人】edata 2019/7/13 17:58:20 -在位编辑实际上是对当前空间的修改,然后移动回块定义.. - -【才人】edata 2019/7/13 17:58:44 -这个就不是很清楚了... - -【贵妃】惊惊 2019/7/13 17:58:47 -也就是长事务上面记录了要移动回去的id? -如果减选了就是剔除了id? - -【才人】edata 2019/7/13 17:59:27 -你能卡到这个长事务吗? - -【贵妃】惊惊 2019/7/13 17:59:39 -net貌似无法控制长事务呀 -应该桌子有考虑到的,只是我还没有挖出来具体怎么处理的.. - -【才人】edata 2019/7/13 18:08:59 -用命令去移除当前在位编辑. - -我用了命令去移除块外的填充和边界,这样操作是可行的, -然后又产生了一个问题,如果是块内的填充,我并不想减去. -想法: 用命令反应器在在位编辑前获取当前空间所有的图元,然后在位编辑时候就知道两个集合的交叉部分了 -答:证明是可以分类出来的,但是会引起一个问题,在位编辑复制的填充id不在任何一个表上. - -判断执行的时候不是选择前的填充均执行产生边界(这样就不用复制反应器了 -再检测命令 _refset 加减三个集合的填充 -但是命令反应器无法检测二级命令,只能通过 lastprompt 获取最后一行命令,判断添加或者删除 - -命令完成后,执行选择上次选择,便是_refset命令的选择 -然后把id改成加减到判断的两个集合中 - - - - - -U 大写命令,用户回滚的时会导致集合信息不符: -之前koz找到的方法,先选择在位编辑命令触发时的所有图元,再选择触发后的所有图元,分别建立两个集合储存,然后进行差集运算, -得出那些是块内图元,这个方法是可行的,然后我发现在使用+加入块内和-减出块内这也都可以判断, -问题就是....如果是使用了U回滚,那么我将不知道如何修改这两个集合.... - -检测在位编辑命令启动之后,而无结束命令的时候, -期间如果用户使用了u就提示用户是否禁止拉伸填充. - -挂载一个图元的反应器,如果使用了命令U,那么判断填充是否被更改了-> ObjectOpenedForModify 反应器 -如果更改了,更改方式无法知道?? 可能是改颜色,也可能是会在块内外加减操作更改. -无解决方案! - - - -拉伸非圆的时候,如果中心移动,那么边界不会跟随 -发生了拉伸命令的时候, -答: 选择的对象有填充,就克隆填充,并且删除原有的边界,及填充 - - - - -频闪控制 -grips=0 - - - - -编辑填充 -样条曲线边界生成 - -在cad2008设计一条填充边界 -填充边界的自定义图元名称叫做: HatchBoundary -设定HatchBoundary变量,0为使用,1为不使用边界 -想法破产:不会自定义图元. - - - -在位编辑的锁定图元的方法,该不是一个隐藏图层导致的吧. -不是,但是在位编辑的时候出现了一个图层 0-RefEdit0 \ No newline at end of file diff --git "a/tests/TestAcad08/\346\213\211\344\274\270\345\241\253\345\205\205/\350\277\207\347\250\213\346\226\207\344\273\266/\350\216\267\345\217\226\345\244\271\347\202\271\344\276\213\345\255\220.cs" "b/tests/TestAcad08/\346\213\211\344\274\270\345\241\253\345\205\205/\350\277\207\347\250\213\346\226\207\344\273\266/\350\216\267\345\217\226\345\244\271\347\202\271\344\276\213\345\255\220.cs" deleted file mode 100644 index a2d7a98..0000000 --- "a/tests/TestAcad08/\346\213\211\344\274\270\345\241\253\345\205\205/\350\277\207\347\250\213\346\226\207\344\273\266/\350\216\267\345\217\226\345\244\271\347\202\271\344\276\213\345\255\220.cs" +++ /dev/null @@ -1,100 +0,0 @@ -#if !ac2008 -namespace GripOverruleTest; - -// https://through-the-interface.typepad.com/through_the_interface/2009/08/knowing-when-an-autocad-object-is-grip-edited-using-overrules-in-net.html -public class GripVectorOverrule : GripOverrule -{ - // A static pointer to our overrule instance - static public GripVectorOverrule theOverrule = new(); - // A flag to indicate whether we're overruling - static bool overruling = false; - // A single set of grips would not have worked in - // the case where multiple objects were selected. - static Dictionary _gripDict = new(); - - private string GetKey(Entity e) - { - // Generate a key based on the name of the object's type - // and its geometric extents - // (We cannot use the ObjectId, as this is null during - // grip-stretch operations.) - return e.GetType().Name + ":" + e.GeometricExtents.ToString(); - } - // Save the locations of the grips for a particular entity - private void StoreGripInfo(Entity e, Point3dCollection grips) - { - string key = GetKey(e); - if (_gripDict.ContainsKey(key)) - { - // Clear the grips if any already associated - Point3dCollection grps = _gripDict[key]; - using (grps) - grps.Clear(); - _gripDict.Remove(key); - } - // Now we add our grips - Point3d[] pts = new Point3d[grips.Count]; - grips.CopyTo(pts, 0); - Point3dCollection gps = new(pts); - _gripDict.Add(key, gps); - } - // Get the locations of the grips for an entity - private Point3dCollection? RetrieveGripInfo(Entity e) - { - Point3dCollection? grips = null; - string key = GetKey(e); - if (_gripDict.ContainsKey(key)) - grips = _gripDict[key]; - return grips; - } - public override void GetGripPoints(Entity e, Point3dCollection grips, IntegerCollection snaps, IntegerCollection geomIds) - { - base.GetGripPoints(e, grips, snaps, geomIds); - StoreGripInfo(e, grips); - } - public override void MoveGripPointsAt(Entity e, IntegerCollection indices, Vector3d offset) - { - var dm = Acap.DocumentManager; - var doc = dm.MdiActiveDocument; - var ed = doc.Editor; - - var grips = RetrieveGripInfo(e); - if (grips != null) - { - // Could get multiple points moved at once, - // hence the integer collection - foreach (int i in indices) - { - // Get the grip point from our internal state - Point3d pt = grips[i]; - // Draw a vector from the grip point to the newly - // offset location, using the index into the - // grip array as the color (excluding colours 0 and 7). - // These vectors don't getting cleared, which makes - // for a fun effect. - ed.DrawVector( - pt, - pt + offset, - (i >= 6 ? i + 2 : i + 1), // exclude colours 0 and 7 - false - ); - } - } - base.MoveGripPointsAt(e, indices, offset); - } - [CommandMethod(nameof(GripOverruleOnOff))] - public static void GripOverruleOnOff() - { - var dm = Acap.DocumentManager; - var doc = dm.MdiActiveDocument; - var ed = doc.Editor; - if (overruling) - RemoveOverrule(GetClass(typeof(Entity)), theOverrule); - else - AddOverrule(GetClass(typeof(Entity)), theOverrule, true); - overruling = !overruling; - Overruling = overruling; - ed.WriteMessage("\nGrip overruling turned {0}.", overruling ? "on" : "off"); - } -} -#endif \ No newline at end of file -- Gitee From 44fe352b316e4d7836aefc4641bfc1e546b8ce01 Mon Sep 17 00:00:00 2001 From: vicwjb Date: Thu, 26 Jan 2023 23:52:59 +0800 Subject: [PATCH 655/675] =?UTF-8?q?=E8=B0=83=E6=95=B4=E9=A1=B9=E7=9B=AE?= =?UTF-8?q?=E7=BC=96=E8=AF=91=E8=AE=BE=E7=BD=AE?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- ...ctory.Build.props => Directory.Build.props | 16 ++------- IFoxCAD.sln | 6 ---- src/Basal/BasalVersion.props | 14 ++++++++ src/CAD/CADVersion.props | 13 +++++++ src/CAD/Directory.Build.props | 34 ------------------- src/WPF/IFox.WPF.csproj | 23 +++++-------- tests/TestAcad09plus/TestAcad09plus.csproj | 1 + 7 files changed, 39 insertions(+), 68 deletions(-) rename src/Basal/Directory.Build.props => Directory.Build.props (77%) create mode 100644 src/Basal/BasalVersion.props create mode 100644 src/CAD/CADVersion.props delete mode 100644 src/CAD/Directory.Build.props diff --git a/src/Basal/Directory.Build.props b/Directory.Build.props similarity index 77% rename from src/Basal/Directory.Build.props rename to Directory.Build.props index b432f84..3a7821f 100644 --- a/src/Basal/Directory.Build.props +++ b/Directory.Build.props @@ -1,10 +1,7 @@  - - - 0.5.0 - 发布0.5版本 - - + + + preview enable @@ -16,22 +13,15 @@ True - InspireFunction xsfhlzh;vicwjb;liuqihong InspireFunction - 基于.NET的二次开发基本类库. MIT true https://gitee.com/inspirefunction/ifoxcad https://gitee.com/inspirefunction/ifoxcad.git git - IFox;C#;NET;Common;Basal - - - - \ No newline at end of file diff --git a/IFoxCAD.sln b/IFoxCAD.sln index 8eb4c1d..949035c 100644 --- a/IFoxCAD.sln +++ b/IFoxCAD.sln @@ -19,9 +19,6 @@ EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Test", "Test", "{C0BEFC15-6DF7-4636-BC76-4A0B88231470}" EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Basal", "Basal", "{1A5C27C1-FEF3-40A0-A1EE-3BC7BF2BD67A}" - ProjectSection(SolutionItems) = preProject - src\Basal\Directory.Build.props = src\Basal\Directory.Build.props - EndProjectSection EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "IFox.Basal", "src\Basal\IFox.Basal\IFox.Basal.csproj", "{FF4E1CDE-EEB3-4BE8-8C1D-6B5B17A299C7}" EndProject @@ -32,9 +29,6 @@ EndProject Project("{D954291E-2A0B-460D-934E-DC6B0785DB48}") = "IFox.CAD.Shared", "src\CAD\IFox.CAD.Shared\IFox.CAD.Shared.shproj", "{20F254F7-AEE5-42AE-A9B3-149BBDC7397F}" EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "CAD", "CAD", "{B2AB7DC7-DBF4-4197-808B-0D2A6613AF5E}" - ProjectSection(SolutionItems) = preProject - src\CAD\Directory.Build.props = src\CAD\Directory.Build.props - EndProjectSection EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "IFox.CAD.Source", "src\CAD\IFox.CAD.Source\IFox.CAD.Source.csproj", "{88F3CF78-23F5-494B-9D8F-2C6B4467E0B4}" EndProject diff --git a/src/Basal/BasalVersion.props b/src/Basal/BasalVersion.props new file mode 100644 index 0000000..5bda819 --- /dev/null +++ b/src/Basal/BasalVersion.props @@ -0,0 +1,14 @@ + + + + 0.5.0 + 发布0.5版本 + + + + + 基于.NET的二次开发基本类库. + IFox;C#;NET;Common;Basal + + + \ No newline at end of file diff --git a/src/CAD/CADVersion.props b/src/CAD/CADVersion.props new file mode 100644 index 0000000..5278232 --- /dev/null +++ b/src/CAD/CADVersion.props @@ -0,0 +1,13 @@ + + + + 0.5.1 + 增加autoload。 + + + + 基于.NET的二次开发基本类库. + IFox;CAD;AutoCad;C#;NET;GStarCAD;ZWCAD + + + \ No newline at end of file diff --git a/src/CAD/Directory.Build.props b/src/CAD/Directory.Build.props deleted file mode 100644 index ecc2647..0000000 --- a/src/CAD/Directory.Build.props +++ /dev/null @@ -1,34 +0,0 @@ - - - - 0.5.1 - 增加autoload。 - - - - preview - enable - true - $(MSBuildThisFileDirectory)bin\$(Configuration)\ - true - true - True - True - - - - - InspireFunction - xsfhlzh;vicwjb;liuqihong - InspireFunction - 基于.NET的二次开发基本类库. - MIT - true - https://gitee.com/inspirefunction/ifoxcad - https://gitee.com/inspirefunction/ifoxcad.git - git - IFox;CAD;AutoCad;C#;NET;GStarCAD;ZWCAD - - - - \ No newline at end of file diff --git a/src/WPF/IFox.WPF.csproj b/src/WPF/IFox.WPF.csproj index 0e2b0ff..7a1989c 100644 --- a/src/WPF/IFox.WPF.csproj +++ b/src/WPF/IFox.WPF.csproj @@ -1,27 +1,21 @@  - preview - enable + NET45 true true - true - true + + 0.4 - xsfhlzh;vicwjb - xsfhlzh;vicwjb;liuqihong - InspireFunction + WPF的简单MVVM模式开发类库 - InspireFunction - LICENSE - https://gitee.com/inspirefunction/ifoxcad - https://gitee.com/inspirefunction/ifoxcad.git - IFoxCAD;C#;NET;WPF;MVVM - git + + IFox;C#;NET;WPF;MVVM + 开启可空类型. - True + @@ -29,7 +23,6 @@ - diff --git a/tests/TestAcad09plus/TestAcad09plus.csproj b/tests/TestAcad09plus/TestAcad09plus.csproj index 35a0636..087569c 100644 --- a/tests/TestAcad09plus/TestAcad09plus.csproj +++ b/tests/TestAcad09plus/TestAcad09plus.csproj @@ -8,6 +8,7 @@ true x64 True + false -- Gitee From 74021c07bfd866326da7676338f0a6f6362dff49 Mon Sep 17 00:00:00 2001 From: vicwjb Date: Fri, 27 Jan 2023 00:01:13 +0800 Subject: [PATCH 656/675] =?UTF-8?q?Revert=20"=E8=B0=83=E6=95=B4=E9=A1=B9?= =?UTF-8?q?=E7=9B=AE=E7=BC=96=E8=AF=91=E8=AE=BE=E7=BD=AE"?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This reverts commit 44fe352b316e4d7836aefc4641bfc1e546b8ce01. --- IFoxCAD.sln | 6 ++++ src/Basal/BasalVersion.props | 14 -------- .../Basal/Directory.Build.props | 16 +++++++-- src/CAD/CADVersion.props | 13 ------- src/CAD/Directory.Build.props | 34 +++++++++++++++++++ src/WPF/IFox.WPF.csproj | 23 ++++++++----- tests/TestAcad09plus/TestAcad09plus.csproj | 1 - 7 files changed, 68 insertions(+), 39 deletions(-) delete mode 100644 src/Basal/BasalVersion.props rename Directory.Build.props => src/Basal/Directory.Build.props (77%) delete mode 100644 src/CAD/CADVersion.props create mode 100644 src/CAD/Directory.Build.props diff --git a/IFoxCAD.sln b/IFoxCAD.sln index 949035c..8eb4c1d 100644 --- a/IFoxCAD.sln +++ b/IFoxCAD.sln @@ -19,6 +19,9 @@ EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Test", "Test", "{C0BEFC15-6DF7-4636-BC76-4A0B88231470}" EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Basal", "Basal", "{1A5C27C1-FEF3-40A0-A1EE-3BC7BF2BD67A}" + ProjectSection(SolutionItems) = preProject + src\Basal\Directory.Build.props = src\Basal\Directory.Build.props + EndProjectSection EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "IFox.Basal", "src\Basal\IFox.Basal\IFox.Basal.csproj", "{FF4E1CDE-EEB3-4BE8-8C1D-6B5B17A299C7}" EndProject @@ -29,6 +32,9 @@ EndProject Project("{D954291E-2A0B-460D-934E-DC6B0785DB48}") = "IFox.CAD.Shared", "src\CAD\IFox.CAD.Shared\IFox.CAD.Shared.shproj", "{20F254F7-AEE5-42AE-A9B3-149BBDC7397F}" EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "CAD", "CAD", "{B2AB7DC7-DBF4-4197-808B-0D2A6613AF5E}" + ProjectSection(SolutionItems) = preProject + src\CAD\Directory.Build.props = src\CAD\Directory.Build.props + EndProjectSection EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "IFox.CAD.Source", "src\CAD\IFox.CAD.Source\IFox.CAD.Source.csproj", "{88F3CF78-23F5-494B-9D8F-2C6B4467E0B4}" EndProject diff --git a/src/Basal/BasalVersion.props b/src/Basal/BasalVersion.props deleted file mode 100644 index 5bda819..0000000 --- a/src/Basal/BasalVersion.props +++ /dev/null @@ -1,14 +0,0 @@ - - - - 0.5.0 - 发布0.5版本 - - - - - 基于.NET的二次开发基本类库. - IFox;C#;NET;Common;Basal - - - \ No newline at end of file diff --git a/Directory.Build.props b/src/Basal/Directory.Build.props similarity index 77% rename from Directory.Build.props rename to src/Basal/Directory.Build.props index 3a7821f..b432f84 100644 --- a/Directory.Build.props +++ b/src/Basal/Directory.Build.props @@ -1,7 +1,10 @@  - - - + + + 0.5.0 + 发布0.5版本 + + preview enable @@ -13,15 +16,22 @@ True + InspireFunction xsfhlzh;vicwjb;liuqihong InspireFunction + 基于.NET的二次开发基本类库. MIT true https://gitee.com/inspirefunction/ifoxcad https://gitee.com/inspirefunction/ifoxcad.git git + IFox;C#;NET;Common;Basal + + + + \ No newline at end of file diff --git a/src/CAD/CADVersion.props b/src/CAD/CADVersion.props deleted file mode 100644 index 5278232..0000000 --- a/src/CAD/CADVersion.props +++ /dev/null @@ -1,13 +0,0 @@ - - - - 0.5.1 - 增加autoload。 - - - - 基于.NET的二次开发基本类库. - IFox;CAD;AutoCad;C#;NET;GStarCAD;ZWCAD - - - \ No newline at end of file diff --git a/src/CAD/Directory.Build.props b/src/CAD/Directory.Build.props new file mode 100644 index 0000000..ecc2647 --- /dev/null +++ b/src/CAD/Directory.Build.props @@ -0,0 +1,34 @@ + + + + 0.5.1 + 增加autoload。 + + + + preview + enable + true + $(MSBuildThisFileDirectory)bin\$(Configuration)\ + true + true + True + True + + + + + InspireFunction + xsfhlzh;vicwjb;liuqihong + InspireFunction + 基于.NET的二次开发基本类库. + MIT + true + https://gitee.com/inspirefunction/ifoxcad + https://gitee.com/inspirefunction/ifoxcad.git + git + IFox;CAD;AutoCad;C#;NET;GStarCAD;ZWCAD + + + + \ No newline at end of file diff --git a/src/WPF/IFox.WPF.csproj b/src/WPF/IFox.WPF.csproj index 7a1989c..0e2b0ff 100644 --- a/src/WPF/IFox.WPF.csproj +++ b/src/WPF/IFox.WPF.csproj @@ -1,21 +1,27 @@  - + preview + enable NET45 true true - - + true + true 0.4 - + xsfhlzh;vicwjb + xsfhlzh;vicwjb;liuqihong + InspireFunction WPF的简单MVVM模式开发类库 - - IFox;C#;NET;WPF;MVVM - + InspireFunction + LICENSE + https://gitee.com/inspirefunction/ifoxcad + https://gitee.com/inspirefunction/ifoxcad.git + IFoxCAD;C#;NET;WPF;MVVM + git 开启可空类型. - + True @@ -23,6 +29,7 @@ + diff --git a/tests/TestAcad09plus/TestAcad09plus.csproj b/tests/TestAcad09plus/TestAcad09plus.csproj index 087569c..35a0636 100644 --- a/tests/TestAcad09plus/TestAcad09plus.csproj +++ b/tests/TestAcad09plus/TestAcad09plus.csproj @@ -8,7 +8,6 @@ true x64 True - false -- Gitee From 2b2c83419989071242a6c338219d674924c52631 Mon Sep 17 00:00:00 2001 From: vicwjb Date: Fri, 27 Jan 2023 00:11:09 +0800 Subject: [PATCH 657/675] =?UTF-8?q?=E8=B0=83=E6=95=B4=E9=A1=B9=E7=9B=AE?= =?UTF-8?q?=E7=94=9F=E6=88=90=E7=9B=AE=E5=BD=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/Basal/Directory.Build.props | 2 +- src/CAD/Directory.Build.props | 2 +- src/WPF/IFox.WPF.csproj | 9 +++++---- 3 files changed, 7 insertions(+), 6 deletions(-) diff --git a/src/Basal/Directory.Build.props b/src/Basal/Directory.Build.props index b432f84..c6e5c0c 100644 --- a/src/Basal/Directory.Build.props +++ b/src/Basal/Directory.Build.props @@ -9,7 +9,7 @@ preview enable true - $(MSBuildThisFileDirectory)bin\$(Configuration)\ + ..\..\..\bin\$(Configuration)\ true true True diff --git a/src/CAD/Directory.Build.props b/src/CAD/Directory.Build.props index ecc2647..c12dbff 100644 --- a/src/CAD/Directory.Build.props +++ b/src/CAD/Directory.Build.props @@ -9,7 +9,7 @@ preview enable true - $(MSBuildThisFileDirectory)bin\$(Configuration)\ + ..\..\..\bin\$(Configuration)\ true true True diff --git a/src/WPF/IFox.WPF.csproj b/src/WPF/IFox.WPF.csproj index 0e2b0ff..4f84d57 100644 --- a/src/WPF/IFox.WPF.csproj +++ b/src/WPF/IFox.WPF.csproj @@ -1,15 +1,18 @@  + 0.4 + 开启可空类型. + + preview enable - NET45 true true true + ..\..\bin\$(Configuration)\ true - 0.4 xsfhlzh;vicwjb xsfhlzh;vicwjb;liuqihong InspireFunction @@ -20,7 +23,6 @@ https://gitee.com/inspirefunction/ifoxcad.git IFoxCAD;C#;NET;WPF;MVVM git - 开启可空类型. True @@ -30,7 +32,6 @@ - -- Gitee From 6899a09a4a23bee3870f79c6adbdcfedd1121872 Mon Sep 17 00:00:00 2001 From: vicwjb Date: Sun, 29 Jan 2023 23:15:13 +0800 Subject: [PATCH 658/675] =?UTF-8?q?=E5=AE=8C=E5=96=84=E6=BA=90=E7=A0=81?= =?UTF-8?q?=E5=8C=85=EF=BC=8C=E6=BA=90=E7=A0=81=E5=8C=85=E6=98=A0=E5=B0=84?= =?UTF-8?q?=E5=8E=9F=E5=A7=8B=E7=9B=AE=E5=BD=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- IFoxCAD.sln | 2 -- src/Basal/Directory.Build.props | 4 ++-- .../IFox.Basal.Source/IFox.Basal.Source.csproj | 15 ++++++++++----- src/CAD/Directory.Build.props | 4 ++-- src/CAD/IFox.CAD.Source/IFox.CAD.Source.csproj | 14 +++++++++----- 5 files changed, 23 insertions(+), 16 deletions(-) diff --git a/IFoxCAD.sln b/IFoxCAD.sln index 8eb4c1d..802ff1c 100644 --- a/IFoxCAD.sln +++ b/IFoxCAD.sln @@ -128,10 +128,8 @@ Global src\CAD\IFox.CAD.Shared\IFox.CAD.Shared.projitems*{20f254f7-aee5-42ae-a9b3-149bbdc7397f}*SharedItemsImports = 13 src\CAD\IFox.CAD.Shared\IFox.CAD.Shared.projitems*{3d7d095d-ef0a-40b3-9776-5f08c61fd614}*SharedItemsImports = 5 tests\TestShared\TestShared.projitems*{5f478f68-19bc-4a3a-af39-8728f8d396dd}*SharedItemsImports = 5 - src\CAD\IFox.CAD.Shared\IFox.CAD.Shared.projitems*{88f3cf78-23f5-494b-9d8f-2c6b4467e0b4}*SharedItemsImports = 5 src\Basal\IFox.Basal.Shared\IFox.Basal.Shared.projitems*{c823514a-2bc2-45c2-aced-18924a7b80bf}*SharedItemsImports = 13 tests\TestShared\TestShared.projitems*{ced63d2d-0af6-4831-806d-5e8e9b0d0a07}*SharedItemsImports = 13 - src\Basal\IFox.Basal.Shared\IFox.Basal.Shared.projitems*{d0c6824c-53a3-4c16-ae7b-3227f18c5039}*SharedItemsImports = 5 src\CAD\IFox.CAD.Shared\IFox.CAD.Shared.projitems*{ef20e632-70a9-40eb-8c9a-539fd85fafad}*SharedItemsImports = 5 src\CAD\IFox.CAD.Shared\IFox.CAD.Shared.projitems*{f388f4b9-f636-414a-9bc9-2b008517b441}*SharedItemsImports = 5 src\Basal\IFox.Basal.Shared\IFox.Basal.Shared.projitems*{ff4e1cde-eeb3-4be8-8c1d-6b5b17a299c7}*SharedItemsImports = 5 diff --git a/src/Basal/Directory.Build.props b/src/Basal/Directory.Build.props index c6e5c0c..cad201d 100644 --- a/src/Basal/Directory.Build.props +++ b/src/Basal/Directory.Build.props @@ -1,8 +1,8 @@  - 0.5.0 - 发布0.5版本 + 0.5.2.2 + 完善源码包,源码包映射原始目录 diff --git a/src/Basal/IFox.Basal.Source/IFox.Basal.Source.csproj b/src/Basal/IFox.Basal.Source/IFox.Basal.Source.csproj index 9a26109..90297d7 100644 --- a/src/Basal/IFox.Basal.Source/IFox.Basal.Source.csproj +++ b/src/Basal/IFox.Basal.Source/IFox.Basal.Source.csproj @@ -22,13 +22,18 @@ - - + + + + true - $(ContentTargetFolders)\cs\any\$(PackageId)\ + + true $(ContentTargetFolders)\any\any\$(PackageId)\ @@ -37,11 +42,11 @@ - + diff --git a/src/CAD/Directory.Build.props b/src/CAD/Directory.Build.props index c12dbff..0a7bd60 100644 --- a/src/CAD/Directory.Build.props +++ b/src/CAD/Directory.Build.props @@ -1,8 +1,8 @@  - 0.5.1 - 增加autoload。 + 0.5.2 + 完善源码包,源码包映射原始目录 diff --git a/src/CAD/IFox.CAD.Source/IFox.CAD.Source.csproj b/src/CAD/IFox.CAD.Source/IFox.CAD.Source.csproj index 4b1bc06..d0a7a04 100644 --- a/src/CAD/IFox.CAD.Source/IFox.CAD.Source.csproj +++ b/src/CAD/IFox.CAD.Source/IFox.CAD.Source.csproj @@ -22,26 +22,30 @@ - + + + true - $(ContentTargetFolders)\cs\any\$(PackageId)\ + true - $(ContentTargetFolders)\any\any\$(PackageId)\ + - + -- Gitee From 452fab03471e3b4cd84894df4d08a4d8fe1733e6 Mon Sep 17 00:00:00 2001 From: vicwjb <403009819@qq.com> Date: Sun, 29 Jan 2023 15:33:13 +0000 Subject: [PATCH 659/675] update README.md. Signed-off-by: vicwjb <403009819@qq.com> --- README.md | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/README.md b/README.md index 1cb8b77..5de2269 100644 --- a/README.md +++ b/README.md @@ -54,15 +54,15 @@ using IFoxCAD.Cad; [CommandMethod(nameof(Hello))] public void Hello() { -     using DBTrans tr = new(); -     var line1 = new Line(new Point3d(0, 0, 0), new Point3d(1, 1, 0)); -     tr.CurrentSpace.AddEntity(line1); -     // 如果您没有添加preview到项目文件里的话:按如下旧语法: -     // using(var tr = new DBTrans()) -     // { -     // var line1 = new Line(new Point3d(0, 0, 0), new Point3d(1, 1, 0)); -     // tr.CurrentSpace.AddEntity(line1); -     // } + using DBTrans tr = new(); + var line1 = new Line(new Point3d(0, 0, 0), new Point3d(1, 1, 0)); + tr.CurrentSpace.AddEntity(line1); + // 如果您没有添加preview到项目文件里的话:按如下旧语法: + // using(var tr = new DBTrans()) + // { + // var line1 = new Line(new Point3d(0, 0, 0), new Point3d(1, 1, 0)); + // tr.CurrentSpace.AddEntity(line1); + // } } ``` -- Gitee From 8e93046be3ab53e48d5dc50f96edaefd9a0687e7 Mon Sep 17 00:00:00 2001 From: vicwjb Date: Mon, 30 Jan 2023 19:58:03 +0800 Subject: [PATCH 660/675] =?UTF-8?q?=E7=BB=A7=E7=BB=AD=E4=BC=98=E5=8C=96?= =?UTF-8?q?=E6=BA=90=E7=A0=81=E5=8C=85?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/Basal/Directory.Build.props | 2 +- .../IFox.Basal.Source.csproj | 22 +++++------------- src/CAD/Directory.Build.props | 2 +- .../IFox.CAD.Source/IFox.CAD.Source.csproj | 23 ++++++------------- 4 files changed, 15 insertions(+), 34 deletions(-) diff --git a/src/Basal/Directory.Build.props b/src/Basal/Directory.Build.props index cad201d..dffe68a 100644 --- a/src/Basal/Directory.Build.props +++ b/src/Basal/Directory.Build.props @@ -1,7 +1,7 @@  - 0.5.2.2 + 0.5.2.3 完善源码包,源码包映射原始目录 diff --git a/src/Basal/IFox.Basal.Source/IFox.Basal.Source.csproj b/src/Basal/IFox.Basal.Source/IFox.Basal.Source.csproj index 90297d7..4a5496d 100644 --- a/src/Basal/IFox.Basal.Source/IFox.Basal.Source.csproj +++ b/src/Basal/IFox.Basal.Source/IFox.Basal.Source.csproj @@ -21,32 +21,22 @@ true - - - - - + + true + $(ContentTargetFolders)\cs\any\$(PackageId)\ + false + true - - + true $(ContentTargetFolders)\any\any\$(PackageId)\ - - diff --git a/src/CAD/Directory.Build.props b/src/CAD/Directory.Build.props index 0a7bd60..80a2ce8 100644 --- a/src/CAD/Directory.Build.props +++ b/src/CAD/Directory.Build.props @@ -1,7 +1,7 @@  - 0.5.2 + 0.5.2.1 完善源码包,源码包映射原始目录 diff --git a/src/CAD/IFox.CAD.Source/IFox.CAD.Source.csproj b/src/CAD/IFox.CAD.Source/IFox.CAD.Source.csproj index d0a7a04..262b591 100644 --- a/src/CAD/IFox.CAD.Source/IFox.CAD.Source.csproj +++ b/src/CAD/IFox.CAD.Source/IFox.CAD.Source.csproj @@ -1,4 +1,4 @@ - + @@ -21,31 +21,22 @@ true - - - - + + true + $(ContentTargetFolders)\cs\any\$(PackageId)\ + false + true - + true - - - -- Gitee From bf34f228686c8206bd03f700ba15a5ca704191d6 Mon Sep 17 00:00:00 2001 From: vicwjb Date: Mon, 30 Jan 2023 20:08:16 +0800 Subject: [PATCH 661/675] =?UTF-8?q?=E4=B8=B4=E6=97=B6=E5=B1=8F=E8=94=BD?= =?UTF-8?q?=E7=94=9F=E6=88=90=E6=B5=A9=E8=BE=B0=E5=92=8C=E4=B8=AD=E6=9C=9B?= =?UTF-8?q?=E7=9A=84nuget=E5=8C=85?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/CAD/IFox.CAD.GCAD/IFox.CAD.GCAD.csproj | 2 +- src/CAD/IFox.CAD.ZCAD/IFox.CAD.ZCAD.csproj | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/CAD/IFox.CAD.GCAD/IFox.CAD.GCAD.csproj b/src/CAD/IFox.CAD.GCAD/IFox.CAD.GCAD.csproj index 2c06cff..7a239f6 100644 --- a/src/CAD/IFox.CAD.GCAD/IFox.CAD.GCAD.csproj +++ b/src/CAD/IFox.CAD.GCAD/IFox.CAD.GCAD.csproj @@ -5,7 +5,7 @@ true true - + false diff --git a/src/CAD/IFox.CAD.ZCAD/IFox.CAD.ZCAD.csproj b/src/CAD/IFox.CAD.ZCAD/IFox.CAD.ZCAD.csproj index 8f243fb..1147ccc 100644 --- a/src/CAD/IFox.CAD.ZCAD/IFox.CAD.ZCAD.csproj +++ b/src/CAD/IFox.CAD.ZCAD/IFox.CAD.ZCAD.csproj @@ -5,7 +5,7 @@ true true - + false -- Gitee From 02b841a5eaa6acc85f47ff58fbe70230749596d9 Mon Sep 17 00:00:00 2001 From: vicwjb Date: Tue, 31 Jan 2023 21:01:51 +0800 Subject: [PATCH 662/675] =?UTF-8?q?=E5=AE=8C=E5=96=84readme?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 59 +++++++++++-------------------------------------------- 1 file changed, 12 insertions(+), 47 deletions(-) diff --git a/README.md b/README.md index 5de2269..0b860ec 100644 --- a/README.md +++ b/README.md @@ -35,7 +35,7 @@ - 右键项目文件,选择管理nuget程序包。 -- 在nuget程序里搜索**ifox**,记得将包括预发行版打钩。截止本文最后更新时,nuget上最新的版本为ifox.cad.source 0.5.0-alpha6版本和ifox.Basal.source 0.5.0-alpha6版本。点击安装就可以。 +- 在nuget程序里搜索**ifox**,记得将包括预发行版打钩。截止本文最后更新时,nuget上最新的版本为ifox.cad.source 0.5.2.1版本和ifox.Basal.source 0.5.2.3版本。点击安装就可以。 - 添加引用,在新建的项目里的cs文件里添加相关的引用 @@ -99,7 +99,7 @@ git clone https://gitee.com/yourname/ifoxcad.git 打开ifoxcad文件夹,双击解决方案文件,打开vs,等待项目打开,加载nuget包,然后生成就可以了。 -**切记,不要用低版本的vs打开本项目,因为本项目采用了某些新的语法,所以老版本的vs是是兼容的。** +**切记,不要用低版本的vs打开本项目,因为本项目采用了某些新的语法,所以老版本的vs是不兼容的。** #### 五、IFoxCad 项目模版 @@ -117,62 +117,27 @@ git clone https://gitee.com/yourname/ifoxcad.git - 第二种是使用源码包。 - 此种方式使用便捷,只要在项目中引用了IFox.Basal.Source和IFox.CAD.Source两个nuget包就可以直接使用了。优点就是使用简单,生成的目录里没有ifox的dll,同时还可以通过定义预处理常量的方式屏蔽ifox提供的元组等功能。缺点就是无法修改源码。 + 此种方式使用便捷,只要在项目中引用了IFox.Basal.Source和IFox.CAD.Source两个nuget包就可以直接使用了。优点就是使用简单,生成的目录里没有ifox的dll,同时还可以通过定义预处理常量的方式屏蔽ifox提供的元组等功能。缺点就是无法修改源码,即便解包修改了,也不会同步到nuget上。 - 第三种是使用git子模块。 此种方法使用步骤复杂,需要熟悉git及其子模块的使用,需要引用ifox里的共享项目文件。优点就是可以使用最新的代码,可以修改代码。具体的可以参考如下说明进行: - -- **让 IFox 作为您的子模块** - - IFox的[jing分支](https://gitee.com/inspirefunction/ifoxcad/tree/jing/)是一个多cad版本分支,您可以利用此作为您的[git项目子模块](https://www.cnblogs.com/JJBox/p/13876501.html#_label13). - 子模块是以`共享工程`的方式加入到您的工程的,其为`IFoxCAD.Cad.Shared`: -1. 千万不要用`IFoxCAD.Cad`内的工程作为引用,否则您将遭遇cad加载失效. - -2. 一些全局命名空间的缺少,我们也建议您使用全局命名空间来补充, - 您只需要按照`IFoxCAD.Cad`的`GlobalUsings.cs`文件一样添加就好了. - -3. 若您使用acad08版本,需要在您的工程中设置`ac2008`和`ac2009`(大小写敏感)两个预定义标签. - 方能启用08工程中缺少的09工程才有的类. - 同时我们在`IFoxCAD.Cad`中提供了这两个例子. - -4. 上面的例子告诉了大家如何使用子模块,建议直接利用testjingsource分支进行操作。 -- **屏蔽IFox.Basal的元组功能** + **让 IFox 作为您的子模块** - 由于c#在每个版本提供的元组功能不一样(有的中间版本缺少),所以IFox内置了元组功能,但是内置元组又会引起某些用户的工程冲突. + IFox的develop分支是一个多cad版本分支,您可以利用此作为您的[git项目子模块](https://www.cnblogs.com/JJBox/p/13876501.html#_label13). - 因此您需要制作一个影子工程: -1. 您需要具备使用git子模块的能力,引用jing分支中的源码. - - 子模块是为了保证您不修改IFox项目,因为你需要定期`git pull`更新组织提供的内容. - - 熟悉子模块之后,你需要把IFox项目一个个加入你的解决方案, - - 除了本次主角IFox.Basal. - -2. 在子模块文件夹外新建 IFox.Basal(影) 文件夹 + 子模块是以`共享工程`的方式加入到您的工程的,其为`IFox.CAD.Shared`: +1. 千万不要用`IFox.CAD.ACAD`内的工程作为引用,否则您将遭遇cad加载失效. -3. 复制 IFox.Basal(本) 的 .csproj 到上一步的文件夹. - -4. 修改 .csproj(影) 利用引用链接的方式进行引用 IFox.Basal(本) 的文件,不引用CLS就等于屏蔽了元组 - - ```xml - - - - - ``` +2. 一些全局命名空间的缺少,我们也建议您使用全局命名空间来补充, + 您只需要按照`IFox.CAD.ACAD`的`GlobalUsings.cs`文件一样添加就好了. -5. 修改 .csproj(影) 引入微软提供的元组 nuget: `System.ValueTuple` (或者你喜欢的) +3. 若您使用acad是09版本以下的,比如 07 08版本,建议你升级至09 版本以上. -6. 解决方案加入 .csproj(影) 之后被内部其他项目引用. - - 这个方法便可以把 影子工程 独立在IFox项目外,令`git pull`仍然有效, - - 并且 本体工程 不做大更改的时候,影子工程更新幅度非常少,也多亏csproj改版了,不然也没有这个骚操作. +4. 上面的例子告诉了大家如何使用子模块。 -#### 软件架构及相关说明 +#### 七、软件架构及相关说明 1. [软件架构说明](/docs/关于IFoxCAD的架构说明.md) -- Gitee From 8c4e0b4e11201a46c860e716eff54e4cc418e4fb Mon Sep 17 00:00:00 2001 From: vicwjb Date: Wed, 1 Feb 2023 21:38:49 +0800 Subject: [PATCH 663/675] =?UTF-8?q?=E4=BF=AE=E5=A4=8Dacademr=E4=B8=AD?= =?UTF-8?q?=E6=8C=87=E9=92=88=E5=B0=8F=E4=BA=8E0=E6=97=B6=E6=8A=A5?= =?UTF-8?q?=E9=94=99=E7=9A=84=E9=97=AE=E9=A2=98=E3=80=82fixed=20#I6C8WR?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/CAD/Directory.Build.props | 4 +-- src/CAD/IFox.CAD.Shared/Runtime/AcadEMR.cs | 35 ++++++++++++---------- 2 files changed, 21 insertions(+), 18 deletions(-) diff --git a/src/CAD/Directory.Build.props b/src/CAD/Directory.Build.props index 80a2ce8..d5461cc 100644 --- a/src/CAD/Directory.Build.props +++ b/src/CAD/Directory.Build.props @@ -1,8 +1,8 @@  - 0.5.2.1 - 完善源码包,源码包映射原始目录 + 0.5.2.2 + 修复academr问题 diff --git a/src/CAD/IFox.CAD.Shared/Runtime/AcadEMR.cs b/src/CAD/IFox.CAD.Shared/Runtime/AcadEMR.cs index 4b8b106..0d4fe6c 100644 --- a/src/CAD/IFox.CAD.Shared/Runtime/AcadEMR.cs +++ b/src/CAD/IFox.CAD.Shared/Runtime/AcadEMR.cs @@ -122,26 +122,29 @@ public static void Remove(bool echoes = false) /// static bool CheckFunc(ref IntPtr adress, byte val, int len) { - if (Marshal.ReadByte(adress) == 233) + if (adress.ToInt64() > 0) { - if (IntPtr.Size == 4) + if (Marshal.ReadByte(adress) == 233) { - var pass = Marshal.ReadInt32(new IntPtr(adress.ToInt32() + 1)); - adress = new IntPtr(adress.ToInt32() + pass + 5); + if (IntPtr.Size == 4) + { + var pass = Marshal.ReadInt32(new IntPtr(adress.ToInt32() + 1)); + adress = new IntPtr(adress.ToInt32() + pass + 5); + } + else + { + var pass = Marshal.ReadInt64(new IntPtr(adress.ToInt64() + 1)); + adress = new IntPtr(adress.ToInt64() + pass + 5); + } } - else + if (adress.ToInt64() > 0 && Marshal.ReadByte(adress) == val) { - var pass = Marshal.ReadInt64(new IntPtr(adress.ToInt64() + 1)); - adress = new IntPtr(adress.ToInt64() + pass + 5); - } - } - if (Marshal.ReadByte(adress) == val) - { - if (IntPtr.Size == 4) - adress = new IntPtr(adress.ToInt32() + len); - else - adress = new IntPtr(adress.ToInt64() + len); - return true; + if (IntPtr.Size == 4) + adress = new IntPtr(adress.ToInt32() + len); + else + adress = new IntPtr(adress.ToInt64() + len); + return true; + } } return false; } -- Gitee From 94b8534e9b8356b0c6290b0bb1a908be7903f686 Mon Sep 17 00:00:00 2001 From: vicwjb Date: Wed, 1 Feb 2023 22:15:24 +0800 Subject: [PATCH 664/675] =?UTF-8?q?=E5=AE=8C=E5=96=84readme?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 0b860ec..d26a488 100644 --- a/README.md +++ b/README.md @@ -77,7 +77,9 @@ public void Hello() 特别提醒: 考虑到早期的框架没有提供System.Range类型、System.Index类型、System.ValueTuple类型,本项目IFox.Basal包里包含了他们。 如果引用了包含System.Range等类型的第三方包(如IndexRange等),请在项目文件中定义NOINDEX、NORANGE、NOVALUETUPLE常量,以避免重复定义。上述代码能起作用的前提是用源码包,普通包暂时无解。 ```xml -NOINDEX;NORANGE;NOVALUETUPLE + + $(Configuration);NOINDEX;NORANGE;NOVALUETUPLE + ``` #### 四、编译 IFox 源码工程 -- Gitee From 42737651381fa0b6910036b2967bc735ab7938c5 Mon Sep 17 00:00:00 2001 From: vicwjb Date: Wed, 1 Feb 2023 22:15:24 +0800 Subject: [PATCH 665/675] =?UTF-8?q?=E5=AE=8C=E5=96=84readme?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 0b860ec..c29b07b 100644 --- a/README.md +++ b/README.md @@ -74,12 +74,16 @@ public void Hello() #### 三、屏蔽IFox的元组、索引、范围功能 -特别提醒: 考虑到早期的框架没有提供System.Range类型、System.Index类型、System.ValueTuple类型,本项目IFox.Basal包里包含了他们。 如果引用了包含System.Range等类型的第三方包(如IndexRange等),请在项目文件中定义NOINDEX、NORANGE、NOVALUETUPLE常量,以避免重复定义。上述代码能起作用的前提是用源码包,普通包暂时无解。 +特别提醒: 考虑到早期的框架没有提供System.Range类型(net core 开始提供)、System.Index类型(net core 开始提供)、System.ValueTuple类型(net 47开始提供),本项目IFox.Basal包里包含了他们。 如果引用了包含System.Range等类型的第三方包(如IndexRange等),请在项目文件中定义NOINDEX、NORANGE、NOVALUETUPLE常量,以避免重复定义。上述代码能起作用的前提是用源码包,普通包暂时无解。 ```xml -NOINDEX;NORANGE;NOVALUETUPLE + + $(Configuration);NOINDEX;NORANGE;NOVALUETUPLE + ``` +**NOINDEX、NORANGE、NOVALUETUPLE 分别针对三种类型,哪种类型冲突就定义哪种。** + #### 四、编译 IFox 源码工程 由于vs2022抛弃了某几个net版本,所以我们同时安装vs2019和vs2022,然后使用vs2022; -- Gitee From b4e73a5eca76d8075809dd2c1739f8fa46f672c4 Mon Sep 17 00:00:00 2001 From: vicwjb Date: Sat, 4 Feb 2023 01:11:14 +0800 Subject: [PATCH 666/675] =?UTF-8?q?=E5=A2=9E=E5=8A=A0=E8=A1=A8=E8=BE=BE?= =?UTF-8?q?=E5=BC=8F=E6=A0=91=E6=89=A9=E5=B1=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../ExpressionTrees/ParameterRebinder.cs | 41 ++ .../ExpressionTrees/PredicateBuilder.cs | 83 ++++ .../ExpressionTrees/SqlExpressionVisitor.cs | 450 ++++++++++++++++++ .../IFox.Basal.Shared.projitems | 3 + src/Basal/IFox.Basal/GlobalUsings.cs | 14 +- src/Basal/IFox.Basal/IFox.Basal.csproj | 7 + 6 files changed, 592 insertions(+), 6 deletions(-) create mode 100644 src/Basal/IFox.Basal.Shared/ExpressionTrees/ParameterRebinder.cs create mode 100644 src/Basal/IFox.Basal.Shared/ExpressionTrees/PredicateBuilder.cs create mode 100644 src/Basal/IFox.Basal.Shared/ExpressionTrees/SqlExpressionVisitor.cs diff --git a/src/Basal/IFox.Basal.Shared/ExpressionTrees/ParameterRebinder.cs b/src/Basal/IFox.Basal.Shared/ExpressionTrees/ParameterRebinder.cs new file mode 100644 index 0000000..e4dfcd4 --- /dev/null +++ b/src/Basal/IFox.Basal.Shared/ExpressionTrees/ParameterRebinder.cs @@ -0,0 +1,41 @@ +#if NET45 +namespace IFoxCAD.Basal; + +/// +/// ذ +/// +public class ParameterRebinder : SqlExpressionVisitor +{ + private readonly Dictionary map; + /// + /// ذ + /// + /// ֵ + public ParameterRebinder(Dictionary map) + { + this.map = map ?? new Dictionary(); + } + /// + /// 滻 + /// + /// ֵ + /// ʽ + /// ʽ + public static Expression? ReplaceParameters(Dictionary map, Expression expression) + { + return new ParameterRebinder(map).Visit(expression); + } + /// + /// ʲ + /// + /// ʽ + /// ʽ + protected override Expression VisitParameter(ParameterExpression expression) + { + if (map.TryGetValue(expression, out var parameterExpression)) + expression = parameterExpression; + + return base.VisitParameter(expression); + } +} +#endif \ No newline at end of file diff --git a/src/Basal/IFox.Basal.Shared/ExpressionTrees/PredicateBuilder.cs b/src/Basal/IFox.Basal.Shared/ExpressionTrees/PredicateBuilder.cs new file mode 100644 index 0000000..7153e79 --- /dev/null +++ b/src/Basal/IFox.Basal.Shared/ExpressionTrees/PredicateBuilder.cs @@ -0,0 +1,83 @@ + +#if NET45 +namespace IFoxCAD.Basal; +/// +/// Predicateίй +/// +public static class PredicateBuilder +{ + /// + /// ίбʽ + /// + /// ķ + /// + public static Expression> True() + { + return param => true; + } + /// + /// ؼٵίбʽ + /// + /// ķ + /// + public static Expression> False() + { + return param => false; + } + /// + /// predicateί + /// + /// + /// ίбʽ + /// ίбʽ + public static Expression> Create(Expression> predicate) + { + return predicate; + } + /// + /// ʾıʽ + /// + /// + /// һ + /// ڶ + /// ʽ + public static Expression> And(this Expression> first, Expression> second) + { + return first.Compose(second, Expression.AndAlso); + } + /// + /// ʾıʽ + /// + /// + /// һ + /// ڶ + /// ʽ + public static Expression> Or(this Expression> first, Expression> second) + { + return first.Compose(second, Expression.OrElse); + } + /// + /// Ƿıʽ + /// + /// + /// ʽ + /// ʽ + public static Expression> Not(this Expression> expression) + { + return Expression.Lambda>(Expression.Not(expression.Body), expression.Parameters); + } + + private static Expression Compose(this Expression first, Expression second, Func merge) + { + var map = first.Parameters.Select((f, i) => new{f,s = second.Parameters[i]}).ToDictionary(p => p.s, p => p.f); + var expression = ParameterRebinder.ReplaceParameters(map, second.Body); + if (expression != null) + { + return Expression.Lambda(merge(first.Body, expression), first.Parameters); + } + return first; + + } +} + +#endif \ No newline at end of file diff --git a/src/Basal/IFox.Basal.Shared/ExpressionTrees/SqlExpressionVisitor.cs b/src/Basal/IFox.Basal.Shared/ExpressionTrees/SqlExpressionVisitor.cs new file mode 100644 index 0000000..bfd6283 --- /dev/null +++ b/src/Basal/IFox.Basal.Shared/ExpressionTrees/SqlExpressionVisitor.cs @@ -0,0 +1,450 @@ +#if NET45 +namespace IFoxCAD.Basal; + +/// +/// sqlʽ +/// +public abstract class SqlExpressionVisitor +{ + /// + /// + /// + /// ʽ + /// ʽ + /// + protected virtual Expression? Visit(Expression expression) + { + if (expression is null) + return null; + + return expression.NodeType switch + { + ExpressionType.Add => VisitBinary((BinaryExpression)expression), + ExpressionType.AddChecked => VisitBinary((BinaryExpression)expression), + ExpressionType.And => VisitBinary((BinaryExpression)expression), + ExpressionType.AndAlso => VisitBinary((BinaryExpression)expression), + ExpressionType.ArrayIndex => VisitBinary((BinaryExpression)expression), + ExpressionType.Coalesce => VisitBinary((BinaryExpression)expression), + ExpressionType.Divide => VisitBinary((BinaryExpression)expression), + ExpressionType.Equal => VisitBinary((BinaryExpression)expression), + ExpressionType.ExclusiveOr => VisitBinary((BinaryExpression)expression), + ExpressionType.GreaterThan => VisitBinary((BinaryExpression)expression), + ExpressionType.GreaterThanOrEqual => VisitBinary((BinaryExpression)expression), + ExpressionType.LeftShift => VisitBinary((BinaryExpression)expression), + ExpressionType.LessThan => VisitBinary((BinaryExpression)expression), + ExpressionType.LessThanOrEqual => VisitBinary((BinaryExpression)expression), + ExpressionType.Modulo => VisitBinary((BinaryExpression)expression), + ExpressionType.Multiply => VisitBinary((BinaryExpression)expression), + ExpressionType.MultiplyChecked => VisitBinary((BinaryExpression)expression), + ExpressionType.NotEqual => VisitBinary((BinaryExpression)expression), + ExpressionType.Or => VisitBinary((BinaryExpression)expression), + ExpressionType.OrElse => VisitBinary((BinaryExpression)expression), + ExpressionType.RightShift => VisitBinary((BinaryExpression)expression), + ExpressionType.Subtract => VisitBinary((BinaryExpression)expression), + ExpressionType.SubtractChecked => VisitBinary((BinaryExpression)expression), + ExpressionType.ArrayLength => VisitUnary((UnaryExpression)expression), + ExpressionType.Convert => VisitUnary((UnaryExpression)expression), + ExpressionType.ConvertChecked => VisitUnary((UnaryExpression)expression), + ExpressionType.Negate => VisitUnary((UnaryExpression)expression), + ExpressionType.NegateChecked => VisitUnary((UnaryExpression)expression), + ExpressionType.Not => VisitUnary((UnaryExpression)expression), + ExpressionType.Quote => VisitUnary((UnaryExpression)expression), + ExpressionType.TypeAs => VisitUnary((UnaryExpression)expression), + ExpressionType.Call => VisitMethodCall((MethodCallExpression)expression), + ExpressionType.Conditional => VisitConditional((ConditionalExpression)expression), + ExpressionType.Constant => VisitConstant((ConstantExpression)expression), + ExpressionType.Invoke => VisitInvocation((InvocationExpression)expression), + ExpressionType.Lambda => VisitLambda((LambdaExpression)expression), + ExpressionType.ListInit => VisitListInit((ListInitExpression)expression), + ExpressionType.MemberAccess => VisitMemberAccess((MemberExpression)expression), + ExpressionType.MemberInit => VisitMemberInit((MemberInitExpression)expression), + ExpressionType.New => VisitNew((NewExpression)expression), + ExpressionType.NewArrayInit => VisitNewArray((NewArrayExpression)expression), + ExpressionType.NewArrayBounds => VisitNewArray((NewArrayExpression)expression), + ExpressionType.Parameter => VisitParameter((ParameterExpression)expression), + ExpressionType.TypeIs => VisitTypeIs((TypeBinaryExpression)expression), + _ => throw new RuntimeBinderException(nameof(expression.NodeType)) + }; + } + /// + /// ߰ + /// + /// 󶨵 + /// 󶨵 + /// + protected virtual MemberBinding VisitBinding(MemberBinding binding) + { + return binding.BindingType switch + { + MemberBindingType.Assignment => VisitMemberAssignment((MemberAssignment)binding), + MemberBindingType.MemberBinding => VisitMemberMemberBinding((MemberMemberBinding)binding), + MemberBindingType.ListBinding => VisitMemberListBinding((MemberListBinding)binding), + _ => throw new RuntimeBinderException(nameof(binding.BindingType)) + }; + } + /// + /// ʼϳʼ趨 + /// + /// ϳʼ趨 + /// ϳʼ趨 + protected virtual ElementInit VisitElementInitializer(ElementInit initializer) + { + var arguments = VisitExpressionList(initializer.Arguments); + + if (arguments != initializer.Arguments) + return Expression.ElementInit(initializer.AddMethod, arguments); + + return initializer; + } + /// + /// һԪ + /// + /// һԪ + /// ʽ + protected virtual Expression VisitUnary(UnaryExpression unary) + { + var operand = Visit(unary.Operand); + + if (operand != unary.Operand) + return Expression.MakeUnary(unary.NodeType, operand, unary.Type, unary.Method); + + return unary; + } + /// + /// ʶ + /// + /// + /// ʽ + protected virtual Expression VisitBinary(BinaryExpression binary) + { + var left = Visit(binary.Left); + var right = Visit(binary.Right); + var conversion = Visit(binary.Conversion); + + if (left == binary.Left && right == binary.Right && conversion == binary.Conversion) + return binary; + + if (binary.NodeType == ExpressionType.Coalesce && binary.Conversion != null) + return Expression.Coalesce(left, right, conversion as LambdaExpression); + + return Expression.MakeBinary(binary.NodeType, left, right, binary.IsLiftedToNull, binary.Method); + } + /// + /// + /// + /// + /// ʽ + protected virtual Expression VisitTypeIs(TypeBinaryExpression typeBinary) + { + var expression = Visit(typeBinary.Expression); + + if (expression != typeBinary.Expression) + return Expression.TypeIs(expression, typeBinary.TypeOperand); + + return typeBinary; + } + /// + /// ʳֵ + /// + /// ֵ + /// ʽ + protected virtual Expression VisitConstant(ConstantExpression constant) + { + return constant; + } + /// + /// + /// + /// + /// ʽ + protected virtual Expression VisitConditional(ConditionalExpression conditional) + { + var test = Visit(conditional.Test); + var ifTrue = Visit(conditional.IfTrue); + var ifFalse = Visit(conditional.IfFalse); + + if (test != conditional.Test) + return Expression.Condition(test, ifTrue, ifFalse); + + if (ifTrue != conditional.IfTrue) + return Expression.Condition(test, ifTrue, ifFalse); + + if (ifFalse != conditional.IfFalse) + return Expression.Condition(test, ifTrue, ifFalse); + + return conditional; + } + /// + /// ʲ + /// + /// + /// ʽ + protected virtual Expression VisitParameter(ParameterExpression parameter) + { + return parameter; + } + /// + /// ʳԱ + /// + /// Ա + /// ʽ + protected virtual Expression VisitMemberAccess(MemberExpression member) + { + var expression = Visit(member.Expression); + + if (expression != member.Expression) + return Expression.MakeMemberAccess(expression, member.Member); + + return member; + } + /// + /// ʷ + /// + /// + /// ʽ + protected virtual Expression VisitMethodCall(MethodCallExpression methodCall) + { + var instance = Visit(methodCall.Object); + var arguments = (IEnumerable)VisitExpressionList(methodCall.Arguments); + + if (instance != methodCall.Object || !Equals(arguments, methodCall.Arguments)) + return Expression.Call(instance, methodCall.Method, arguments); + + return methodCall; + } + /// + /// ʱʽ + /// + /// ʽ + /// ʽֻ + protected virtual ReadOnlyCollection VisitExpressionList(ReadOnlyCollection original) + { + var index1 = 0; + var expressions = default(List); + + for (var count = original.Count; index1 < count; ++index1) + { + var expression = Visit(original[index1]); + if (expression != null) + { + if (expressions != null) + { + expressions.Add(expression); + } + + else if (expression != original[index1]) + { + expressions = new List(count); + + for (var index2 = 0; index2 < index1; ++index2) + expressions.Add(original[index2]); + + expressions.Add(expression); + } + } + + } + + return expressions != null ? expressions.AsReadOnly() : original; + } + /// + /// ʳԱֵ + /// + /// Աֵ + /// + protected virtual MemberAssignment VisitMemberAssignment(MemberAssignment assignment) + { + var expression = Visit(assignment.Expression); + + if (expression != assignment.Expression) + return Expression.Bind(assignment.Member, expression); + + return assignment; + } + /// + /// ¶ԱijԱ + /// + /// ¶ԱijԱ + /// ¶ԱijԱ + protected virtual MemberMemberBinding VisitMemberMemberBinding(MemberMemberBinding binding) + { + var bindings = VisitBindingList(binding.Bindings); + + if (!Equals(bindings, binding.Bindings)) + return Expression.MemberBind(binding.Member, bindings); + + return binding; + } + /// + /// ʳԱʼ + /// + /// Աʼ + /// Աʼ + protected virtual MemberListBinding VisitMemberListBinding(MemberListBinding binding) + { + var initializers = VisitElementInitializerList(binding.Initializers); + + if (!Equals(initializers, binding.Initializers)) + return Expression.ListBind(binding.Member, initializers); + + return binding; + } + /// + /// ʳԱʼб + /// + /// Աʼб + /// Աʼб + protected virtual IEnumerable VisitBindingList(ReadOnlyCollection original) + { + var index1 = 0; + var bindings = default(List); + + for (var count = original.Count; index1 < count; ++index1) + { + var memberBinding = VisitBinding(original[index1]); + + if (bindings != null) + { + bindings.Add(memberBinding); + } + + else if (memberBinding != original[index1]) + { + bindings = new List(count); + + for (var index2 = 0; index2 < index1; ++index2) + bindings.Add(original[index2]); + + bindings.Add(memberBinding); + } + } + + return (IEnumerable)bindings! ?? original; + } + /// + /// ʼ趨 + /// + /// 趨 + /// 趨 + protected virtual IEnumerable VisitElementInitializerList(ReadOnlyCollection original) + { + var index1 = 0; + var initializers = default(List); + + for (var count = original.Count; index1 < count; ++index1) + { + var initializer = VisitElementInitializer(original[index1]); + + if (initializers != null) + { + initializers.Add(initializer); + } + + else if (initializer != original[index1]) + { + initializers = new List(count); + + for (var index2 = 0; index2 < index1; ++index2) + initializers.Add(original[index2]); + + initializers.Add(initializer); + } + } + + return (IEnumerable)initializers! ?? original; + } + /// + /// lambdaʽ + /// + /// lambdaʽ + /// ʽ + protected virtual Expression VisitLambda(LambdaExpression lambda) + { + var body = Visit(lambda.Body); + + if (body != lambda.Body) + return Expression.Lambda(lambda.Type, body, lambda.Parameters); + + return lambda; + } + /// + /// ʹ캯 + /// + /// 캯 + /// + protected virtual NewExpression VisitNew(NewExpression expression) + { + var arguments = VisitExpressionList(expression.Arguments); + + if (Equals(arguments, expression.Arguments)) + return expression; + + if (expression.Members != null) + return Expression.New(expression.Constructor, arguments, expression.Members); + + return Expression.New(expression.Constructor, arguments); + } + /// + /// ʳԱʼ + /// + /// Աʼ + /// ʽ + protected virtual Expression VisitMemberInit(MemberInitExpression memberInit) + { + var expression = VisitNew(memberInit.NewExpression); + var bindings = VisitBindingList(memberInit.Bindings); + + if (expression != memberInit.NewExpression || !Equals(bindings, memberInit.Bindings)) + return Expression.MemberInit(expression, bindings); + + return memberInit; + } + /// + /// ʼϳʼ + /// + /// ϳʼ + /// ʽ + protected virtual Expression VisitListInit(ListInitExpression listInit) + { + var expression = VisitNew(listInit.NewExpression); + var initializers = VisitElementInitializerList(listInit.Initializers); + + if (expression != listInit.NewExpression || !Equals(initializers, listInit.Initializers)) + return Expression.ListInit(expression, initializers); + + return listInit; + } + /// + /// + /// + /// + /// ʽ + protected virtual Expression VisitNewArray(NewArrayExpression newArray) + { + var expressions = VisitExpressionList(newArray.Expressions); + + if (Equals(expressions, newArray.Expressions)) + return newArray; + + if (newArray.NodeType == ExpressionType.NewArrayInit) + return Expression.NewArrayInit(newArray.Type.GetElementType(), expressions); + + return Expression.NewArrayBounds(newArray.Type.GetElementType(), expressions); + } + /// + /// ίеñʽ + /// + /// ίеñʽ + /// ʽ + protected virtual Expression VisitInvocation(InvocationExpression invocation) + { + var arguments = VisitExpressionList(invocation.Arguments); + var expression = Visit(invocation.Expression); + + if (arguments != invocation.Arguments || expression != invocation.Expression) + return Expression.Invoke(expression, arguments); + + return invocation; + } +} +#endif \ No newline at end of file diff --git a/src/Basal/IFox.Basal.Shared/IFox.Basal.Shared.projitems b/src/Basal/IFox.Basal.Shared/IFox.Basal.Shared.projitems index 319c7e2..388b345 100644 --- a/src/Basal/IFox.Basal.Shared/IFox.Basal.Shared.projitems +++ b/src/Basal/IFox.Basal.Shared/IFox.Basal.Shared.projitems @@ -14,6 +14,9 @@ + + + diff --git a/src/Basal/IFox.Basal/GlobalUsings.cs b/src/Basal/IFox.Basal/GlobalUsings.cs index 6eb467e..35cc1cd 100644 --- a/src/Basal/IFox.Basal/GlobalUsings.cs +++ b/src/Basal/IFox.Basal/GlobalUsings.cs @@ -8,14 +8,16 @@ global using System.Reflection; global using System.ComponentModel; global using System.Runtime.InteropServices; - - global using System.Diagnostics; global using System.Drawing; - global using System.Windows.Forms; - global using Microsoft.Win32; - global using System.Runtime.CompilerServices; -global using System.Threading; \ No newline at end of file +global using System.Threading; +global using System.Linq.Expressions; +global using System.Collections.ObjectModel; + + +#if NET45 +global using Microsoft.CSharp.RuntimeBinder; +#endif \ No newline at end of file diff --git a/src/Basal/IFox.Basal/IFox.Basal.csproj b/src/Basal/IFox.Basal/IFox.Basal.csproj index af00b68..db74995 100644 --- a/src/Basal/IFox.Basal/IFox.Basal.csproj +++ b/src/Basal/IFox.Basal/IFox.Basal.csproj @@ -4,8 +4,15 @@ NET35;NET40;NET45 true true + MSB3270 + + + 4.7.0 + + + -- Gitee From 05323cad38a97e3bde3b4da1c0b710961057b1ae Mon Sep 17 00:00:00 2001 From: vicwjb Date: Sat, 4 Feb 2023 01:12:23 +0800 Subject: [PATCH 667/675] =?UTF-8?q?=E5=A2=9E=E5=8A=A0=E6=B3=A8=E9=87=8A?= =?UTF-8?q?=EF=BC=8C=E5=B1=8F=E8=94=BD32=E4=BD=8D64=E4=BD=8D=E7=9A=84?= =?UTF-8?q?=E8=AD=A6=E5=91=8A=E6=8F=90=E7=A4=BA?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/Basal/IFox.Basal.Shared/General/ArrayEx.cs | 3 +++ src/CAD/IFox.CAD.ACAD/IFox.CAD.ACAD.csproj | 1 + src/CAD/IFox.CAD.GCAD/IFox.CAD.GCAD.csproj | 1 + src/CAD/IFox.CAD.Shared/Runtime/Env.cs | 10 +++++++++- src/CAD/IFox.CAD.ZCAD/IFox.CAD.ZCAD.csproj | 1 + 5 files changed, 15 insertions(+), 1 deletion(-) diff --git a/src/Basal/IFox.Basal.Shared/General/ArrayEx.cs b/src/Basal/IFox.Basal.Shared/General/ArrayEx.cs index 442f453..5b0e2cd 100644 --- a/src/Basal/IFox.Basal.Shared/General/ArrayEx.cs +++ b/src/Basal/IFox.Basal.Shared/General/ArrayEx.cs @@ -5,6 +5,9 @@ * 使用状态机会变得缓慢,因此我们设计的时候着重于时间优化, * 本工具类在着重于数组遍历时候替代linq */ +/// +/// 数组扩展类 +/// public static class ArrayEx { /// diff --git a/src/CAD/IFox.CAD.ACAD/IFox.CAD.ACAD.csproj b/src/CAD/IFox.CAD.ACAD/IFox.CAD.ACAD.csproj index d93e6e3..4b99921 100644 --- a/src/CAD/IFox.CAD.ACAD/IFox.CAD.ACAD.csproj +++ b/src/CAD/IFox.CAD.ACAD/IFox.CAD.ACAD.csproj @@ -4,6 +4,7 @@ NET35;NET40;NET45 true true + MSB3270 diff --git a/src/CAD/IFox.CAD.GCAD/IFox.CAD.GCAD.csproj b/src/CAD/IFox.CAD.GCAD/IFox.CAD.GCAD.csproj index 7a239f6..506d96e 100644 --- a/src/CAD/IFox.CAD.GCAD/IFox.CAD.GCAD.csproj +++ b/src/CAD/IFox.CAD.GCAD/IFox.CAD.GCAD.csproj @@ -6,6 +6,7 @@ true true false + MSB3270 diff --git a/src/CAD/IFox.CAD.Shared/Runtime/Env.cs b/src/CAD/IFox.CAD.Shared/Runtime/Env.cs index e57dcda..7f2b5e5 100644 --- a/src/CAD/IFox.CAD.Shared/Runtime/Env.cs +++ b/src/CAD/IFox.CAD.Shared/Runtime/Env.cs @@ -573,6 +573,10 @@ public static int SetEnv(string? name, string? var) /// /// 要打印的对象 public static void Print(object message) => Editor.WriteMessage($"{message}\n"); + /// + /// 命令行打印,会自动调用对象的toString函数,在打印内容前添加换行 + /// + /// 要打印的对象 public static void Printl(object message) => Editor.WriteMessage($"{Environment.NewLine}{message}\n"); /// @@ -665,7 +669,11 @@ public static int GetAcadVersion() }; return acarVarNum; } - + /// + /// 获取带cad版本号的dll + /// + /// dll名字 + /// dll的前面 public static string GetAcapVersionDll(string str = "acdb") { return str + Acap.Version.Major + ".dll"; diff --git a/src/CAD/IFox.CAD.ZCAD/IFox.CAD.ZCAD.csproj b/src/CAD/IFox.CAD.ZCAD/IFox.CAD.ZCAD.csproj index 1147ccc..e99b5ef 100644 --- a/src/CAD/IFox.CAD.ZCAD/IFox.CAD.ZCAD.csproj +++ b/src/CAD/IFox.CAD.ZCAD/IFox.CAD.ZCAD.csproj @@ -6,6 +6,7 @@ true true false + MSB3270 -- Gitee From 26ca612211a2f57aa75c928d1392a23ece98aa08 Mon Sep 17 00:00:00 2001 From: vicwjb Date: Thu, 9 Feb 2023 00:05:36 +0800 Subject: [PATCH 668/675] =?UTF-8?q?=E4=BF=AE=E5=A4=8DAddDBTextToEntity?= =?UTF-8?q?=E5=87=BD=E6=95=B0=E5=9C=A8=E5=90=8E=E5=8F=B0=E4=BD=BF=E7=94=A8?= =?UTF-8?q?=E7=9A=84=E6=97=B6=E5=80=99=E6=8A=A5=E9=94=99=E3=80=82=20fixed?= =?UTF-8?q?=20https://gitee.com/inspirefunction/ifoxcad/issues/I6DTO0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/CAD/Directory.Build.props | 4 +- .../AttachmentPointHelper.cs" | 5 ++- .../TextEntityAdd.cs" | 10 ++--- .../TextInfo.cs" | 30 ++++++------- tests/TestShared/TestBlock.cs | 42 ++++++++++++++++++- 5 files changed, 65 insertions(+), 26 deletions(-) diff --git a/src/CAD/Directory.Build.props b/src/CAD/Directory.Build.props index d5461cc..19bc390 100644 --- a/src/CAD/Directory.Build.props +++ b/src/CAD/Directory.Build.props @@ -1,8 +1,8 @@  - 0.5.2.2 - 修复academr问题 + 0.5.2.3 + 修复TextInfo.AddDBTextToEntity()报错的问题 diff --git "a/src/CAD/IFox.CAD.Shared/ExtensionMethod/\346\226\260\345\273\272\346\226\207\345\255\227/AttachmentPointHelper.cs" "b/src/CAD/IFox.CAD.Shared/ExtensionMethod/\346\226\260\345\273\272\346\226\207\345\255\227/AttachmentPointHelper.cs" index 975e8a7..f94fd08 100644 --- "a/src/CAD/IFox.CAD.Shared/ExtensionMethod/\346\226\260\345\273\272\346\226\207\345\255\227/AttachmentPointHelper.cs" +++ "b/src/CAD/IFox.CAD.Shared/ExtensionMethod/\346\226\260\345\273\272\346\226\207\345\255\227/AttachmentPointHelper.cs" @@ -1,5 +1,8 @@ namespace IFoxCAD.Cad; +/// +/// 文字对齐点帮助类 +/// public static class AttachmentPointHelper { static readonly Dictionary _alignment = new() @@ -42,7 +45,7 @@ public static AttachmentPoint Get(string key) } /// - /// 输入对齐方式获得文字 + /// 输入对齐方式获得文字说明 /// /// /// diff --git "a/src/CAD/IFox.CAD.Shared/ExtensionMethod/\346\226\260\345\273\272\346\226\207\345\255\227/TextEntityAdd.cs" "b/src/CAD/IFox.CAD.Shared/ExtensionMethod/\346\226\260\345\273\272\346\226\207\345\255\227/TextEntityAdd.cs" index 5b37a31..f9ba03d 100644 --- "a/src/CAD/IFox.CAD.Shared/ExtensionMethod/\346\226\260\345\273\272\346\226\207\345\255\227/TextEntityAdd.cs" +++ "b/src/CAD/IFox.CAD.Shared/ExtensionMethod/\346\226\260\345\273\272\346\226\207\345\255\227/TextEntityAdd.cs" @@ -1,5 +1,5 @@ namespace IFoxCAD.Cad; - +#if false public static partial class EntityAdd { /// @@ -27,8 +27,7 @@ public static Entity AddDBTextToEntity(this Database db, justify, justifyPoint, textStyleId, - textHigh, - db); + textHigh); return TextInfo.AddDBTextToEntity(); } @@ -55,8 +54,9 @@ public static Entity AddMTextToEntity(this Database db, justify, null, textStyleId, - textHigh, - db); + textHigh); return TextInfo.AddMTextToEntity(); } } + +#endif \ No newline at end of file diff --git "a/src/CAD/IFox.CAD.Shared/ExtensionMethod/\346\226\260\345\273\272\346\226\207\345\255\227/TextInfo.cs" "b/src/CAD/IFox.CAD.Shared/ExtensionMethod/\346\226\260\345\273\272\346\226\207\345\255\227/TextInfo.cs" index d8bfd7a..caf50d8 100644 --- "a/src/CAD/IFox.CAD.Shared/ExtensionMethod/\346\226\260\345\273\272\346\226\207\345\255\227/TextInfo.cs" +++ "b/src/CAD/IFox.CAD.Shared/ExtensionMethod/\346\226\260\345\273\272\346\226\207\345\255\227/TextInfo.cs" @@ -5,10 +5,11 @@ /// public class TextInfo { - readonly Database? Database; readonly string? Contents; readonly Point3d Position; - + /// + /// 文字对齐方式的中文说明 + /// public string TextJustifyCn => AttachmentPointHelper.Get(TextJustify); readonly AttachmentPoint TextJustify; readonly Point3d? AlignmentPoint; @@ -25,14 +26,12 @@ public class TextInfo /// 对齐点(对齐方式是左,此参数无效,为null不为左就报错) /// 文字样式id /// 文字高度 - /// 数据库 public TextInfo(string? contents, Point3d position, AttachmentPoint justify, Point3d? justifyPoint = null, ObjectId? textStyleId = null, - double textHeight = 2.5, - Database? database = null) + double textHeight = 2.5) { Contents = contents; Position = position; @@ -44,7 +43,6 @@ public TextInfo(string? contents, AlignmentPoint = justifyPoint; TextHeight = textHeight; TextStyleId = textStyleId; - Database = database; } /// @@ -56,11 +54,8 @@ public DBText AddDBTextToEntity() throw new ArgumentNullException(nameof(Contents) + "创建文字无内容"); var acText = new DBText(); - acText.SetDatabaseDefaults(); - - if (Database is not null) - acText.SetDatabaseDefaults(Database);// 我的默认值是填满的,所以可以不需要 - + acText.SetDatabaseDefaults(DBTrans.Top.Database); + if (TextStyleId is not null) acText.SetTextStyleId(TextStyleId.Value); @@ -75,8 +70,8 @@ public DBText AddDBTextToEntity() else if (acText.Justify != AttachmentPoint.BaseLeft) acText.AlignmentPoint = Position; - if (Database is not null) - acText.AdjustAlignment(Database); + + acText.AdjustAlignment(DBTrans.Top.Database); return acText; } @@ -90,10 +85,8 @@ public MText AddMTextToEntity() throw new ArgumentNullException(nameof(Contents) + "创建文字无内容"); var mText = new MText(); - mText.SetDatabaseDefaults(); - if (Database is not null) - mText.SetDatabaseDefaults(Database); + mText.SetDatabaseDefaults(DBTrans.Top.Database); if (TextStyleId is not null) mText.SetTextStyleId(TextStyleId.Value); @@ -109,7 +102,10 @@ public MText AddMTextToEntity() } } -// 反射设定对象的文字样式id + +/// +/// 反射设定对象的文字样式id +/// public static partial class TextInfoHelper { /// diff --git a/tests/TestShared/TestBlock.cs b/tests/TestShared/TestBlock.cs index 39ad9aa..75403c3 100644 --- a/tests/TestShared/TestBlock.cs +++ b/tests/TestShared/TestBlock.cs @@ -39,7 +39,7 @@ public void Test_GetBoundingBoxEx() } } - // 块定义 + // 前台块定义 [CommandMethod(nameof(Test_BlockDef))] public void Test_BlockDef() { @@ -74,6 +74,46 @@ public void Test_BlockDef() return new List { line, acText }; }); } + + // 后台块定义 + [CommandMethod(nameof(Test_BlockDefbehind))] + public void Test_BlockDefbehind() + { + using DBTrans tr = new(@"C:\Users\vic\Desktop\test.dwg"); + // var line = new Line(new Point3d(0, 0, 0), new Point3d(1, 1, 0)); + tr.BlockTable.Add("test", + btr => { + btr.Origin = new Point3d(0, 0, 0); + }, + () => // 图元 + { + return new List { new Line(new Point3d(0, 0, 0), new Point3d(1, 1, 0)) }; + }, + () => // 属性定义 + { + var id1 = new AttributeDefinition() { Position = new Point3d(0, 0, 0), Tag = "start", Height = 0.2 }; + var id2 = new AttributeDefinition() { Position = new Point3d(1, 1, 0), Tag = "end", Height = 0.2 }; + return new List { id1, id2 }; + } + ); + // ObjectId objectId = tr.BlockTable.Add("a");// 新建块 + // objectId.GetObject().AddEntity();// 测试添加空实体 + tr.BlockTable.Add("test1", + btr => { + btr.Origin = new Point3d(0, 0, 0); + }, + () => { + var line = new Line(new Point3d(0, 0, 0), new Point3d(1, 1, 0)); + var acText = new TextInfo("12345", Point3d.Origin, AttachmentPoint.BaseLeft) + .AddDBTextToEntity(); + + return new List { line, acText }; + }); + tr.SaveDwgFile(); + } + + + // 修改块定义 [CommandMethod(nameof(Test_BlockDefChange))] public void Test_BlockDefChange() -- Gitee From 8850a04e7b33b9c492864906477b16d5bf7e570e Mon Sep 17 00:00:00 2001 From: vicwjb Date: Thu, 9 Feb 2023 23:21:59 +0800 Subject: [PATCH 669/675] =?UTF-8?q?=E7=BB=A7=E7=BB=AD=E4=BF=AE=E5=A4=8DTex?= =?UTF-8?q?tInfo.AddDBTextToEntity()=E6=8A=A5=E9=94=99?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../TextInfo.cs" | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git "a/src/CAD/IFox.CAD.Shared/ExtensionMethod/\346\226\260\345\273\272\346\226\207\345\255\227/TextInfo.cs" "b/src/CAD/IFox.CAD.Shared/ExtensionMethod/\346\226\260\345\273\272\346\226\207\345\255\227/TextInfo.cs" index caf50d8..cf010d4 100644 --- "a/src/CAD/IFox.CAD.Shared/ExtensionMethod/\346\226\260\345\273\272\346\226\207\345\255\227/TextInfo.cs" +++ "b/src/CAD/IFox.CAD.Shared/ExtensionMethod/\346\226\260\345\273\272\346\226\207\345\255\227/TextInfo.cs" @@ -5,6 +5,7 @@ /// public class TextInfo { + readonly Database? Database; readonly string? Contents; readonly Point3d Position; /// @@ -26,12 +27,14 @@ public class TextInfo /// 对齐点(对齐方式是左,此参数无效,为null不为左就报错) /// 文字样式id /// 文字高度 + /// 数据库 public TextInfo(string? contents, Point3d position, AttachmentPoint justify, Point3d? justifyPoint = null, ObjectId? textStyleId = null, - double textHeight = 2.5) + double textHeight = 2.5, + Database? database = null) { Contents = contents; Position = position; @@ -43,6 +46,7 @@ public TextInfo(string? contents, AlignmentPoint = justifyPoint; TextHeight = textHeight; TextStyleId = textStyleId; + Database = database; } /// @@ -54,8 +58,9 @@ public DBText AddDBTextToEntity() throw new ArgumentNullException(nameof(Contents) + "创建文字无内容"); var acText = new DBText(); - acText.SetDatabaseDefaults(DBTrans.Top.Database); - + + acText.SetDatabaseDefaults(Database ?? DBTrans.Top.Database); + if (TextStyleId is not null) acText.SetTextStyleId(TextStyleId.Value); @@ -71,7 +76,7 @@ public DBText AddDBTextToEntity() acText.AlignmentPoint = Position; - acText.AdjustAlignment(DBTrans.Top.Database); + acText.AdjustAlignment(Database ?? DBTrans.Top.Database); return acText; } @@ -86,7 +91,7 @@ public MText AddMTextToEntity() var mText = new MText(); - mText.SetDatabaseDefaults(DBTrans.Top.Database); + mText.SetDatabaseDefaults(Database ?? DBTrans.Top.Database); if (TextStyleId is not null) mText.SetTextStyleId(TextStyleId.Value); -- Gitee From b223820be41c5dd0177b2af4b5884ad35d5be2ed Mon Sep 17 00:00:00 2001 From: vicwjb Date: Thu, 9 Feb 2023 23:25:34 +0800 Subject: [PATCH 670/675] =?UTF-8?q?=E4=BF=AE=E5=A4=8DColorIndex=E6=98=AF?= =?UTF-8?q?=E5=8F=AF=E4=BB=A5=E8=AE=BE=E7=BD=AE=E6=88=90256=E7=9A=84?= =?UTF-8?q?=E9=97=AE=E9=A2=98=E3=80=82fixed=20#I6E18W?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/CAD/IFox.CAD.Shared/ExtensionMethod/SymbolTableEx.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/CAD/IFox.CAD.Shared/ExtensionMethod/SymbolTableEx.cs b/src/CAD/IFox.CAD.Shared/ExtensionMethod/SymbolTableEx.cs index dd5797e..4c13159 100644 --- a/src/CAD/IFox.CAD.Shared/ExtensionMethod/SymbolTableEx.cs +++ b/src/CAD/IFox.CAD.Shared/ExtensionMethod/SymbolTableEx.cs @@ -26,7 +26,7 @@ public static ObjectId Add(this SymbolTable table, /// 图层id public static ObjectId Add(this SymbolTable table, string name, int colorIndex) { - colorIndex %= 256;// 防止输入的颜色超出256 + colorIndex %= 257;// 防止输入的颜色超出256 colorIndex = Math.Abs(colorIndex);// 防止负数 return table.Add(name, lt => lt.Color = Color.FromColorIndex(ColorMethod.ByColor, (short)colorIndex)); } -- Gitee From 6b00de4f7bb2d6c5f22a13fbd481ff3722136b6b Mon Sep 17 00:00:00 2001 From: vicwjb Date: Mon, 13 Feb 2023 23:27:21 +0800 Subject: [PATCH 671/675] =?UTF-8?q?=E5=A2=9E=E5=8A=A0=E5=8F=82=E6=95=B0nul?= =?UTF-8?q?l=E6=A3=80=E6=9F=A5=E5=87=BD=E6=95=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../General/ArgumentNullEx.cs | 22 +++++++++++++++++++ .../IFox.Basal.Shared.projitems | 1 + 2 files changed, 23 insertions(+) create mode 100644 src/Basal/IFox.Basal.Shared/General/ArgumentNullEx.cs diff --git a/src/Basal/IFox.Basal.Shared/General/ArgumentNullEx.cs b/src/Basal/IFox.Basal.Shared/General/ArgumentNullEx.cs new file mode 100644 index 0000000..9c9f77d --- /dev/null +++ b/src/Basal/IFox.Basal.Shared/General/ArgumentNullEx.cs @@ -0,0 +1,22 @@ + + +namespace IFox.Basal +{ + /// + /// 参数null检查类 + /// + public static class ArgumentNullEx + { + /// + /// 检查参数是否为 null + /// + /// 参数类型 + /// 参数 + /// 参数为null时的提示信息 + /// + public static void NotNull(this T argument, string? argumentExpression = null) where T : class + { + if (argument == null) throw new ArgumentNullException(paramName: argumentExpression); + } + } +} diff --git a/src/Basal/IFox.Basal.Shared/IFox.Basal.Shared.projitems b/src/Basal/IFox.Basal.Shared/IFox.Basal.Shared.projitems index 388b345..814c381 100644 --- a/src/Basal/IFox.Basal.Shared/IFox.Basal.Shared.projitems +++ b/src/Basal/IFox.Basal.Shared/IFox.Basal.Shared.projitems @@ -17,6 +17,7 @@ + -- Gitee From 3529eafd6790a299f3bb66a95ec7343f68ba1f71 Mon Sep 17 00:00:00 2001 From: vicwjb Date: Tue, 14 Feb 2023 00:33:29 +0800 Subject: [PATCH 672/675] =?UTF-8?q?=E4=BB=8E=E7=94=A8notnull=E5=87=BD?= =?UTF-8?q?=E6=95=B0=E6=9B=BF=E6=8D=A2=E5=8F=82=E6=95=B0=E6=A3=80=E6=9F=A5?= =?UTF-8?q?=E4=BB=A3=E7=A0=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../IFox.Basal.Shared/CLS/RuntimeHelpers.cs | 7 +- .../General/ArgumentNullEx.cs | 3 +- .../IFox.Basal.Shared/Sortedset/Sortedset.cs | 117 +++++++++--------- .../WindowsAPI/WindowsAPI.cs | 7 +- .../IFox.CAD.Shared/Algorithms/Graph/Graph.cs | 13 +- .../Algorithms/QuadTree/Rect.cs | 20 +-- .../IFox.CAD.Shared/Copyclip/BitmapTool.cs | 7 +- src/CAD/IFox.CAD.Shared/Copyclip/EmfTool.cs | 9 +- .../Copyclip/TagClipboardInfo.cs | 7 +- .../ExtensionMethod/CurveEx.cs | 31 ++--- .../Entity/BlockReferenceEx.cs | 20 +-- .../IFox.CAD.Shared/ExtensionMethod/JigEx.cs | 9 +- .../ExtensionMethod/JsonConverter.cs | 10 +- .../ExtensionMethod/PointEx.cs | 9 +- .../ExtensionMethod/SelectionSetEx.cs | 14 +-- .../ExtensionMethod/SymbolTableRecordEx.cs | 1 - .../IFox.CAD.Shared/ExtensionMethod/XrefEx.cs | 19 +-- .../HatchConverter.cs" | 19 ++- .../HatchInfo.cs" | 10 +- .../IFox.CAD.Shared/Runtime/AcadVersion.cs | 7 +- src/CAD/IFox.CAD.Shared/Runtime/DBTrans.cs | 7 +- src/CAD/IFox.CAD.Shared/Runtime/Env.cs | 19 +-- .../Runtime/MethodInfoHelper.cs | 8 +- .../IFox.CAD.Shared/Runtime/SymbolTable.cs | 1 - 24 files changed, 210 insertions(+), 164 deletions(-) diff --git a/src/Basal/IFox.Basal.Shared/CLS/RuntimeHelpers.cs b/src/Basal/IFox.Basal.Shared/CLS/RuntimeHelpers.cs index 9e9cfe4..9957438 100644 --- a/src/Basal/IFox.Basal.Shared/CLS/RuntimeHelpers.cs +++ b/src/Basal/IFox.Basal.Shared/CLS/RuntimeHelpers.cs @@ -3,6 +3,8 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. +using IFox.Basal; + namespace System.Runtime.CompilerServices; /* @@ -24,8 +26,9 @@ internal static class RuntimeHelpers /// public static T[] GetSubArray(T[] array, Range range) { - if (array == null) - throw new ArgumentNullException(nameof(array)); + //if (array == null) + // throw new ArgumentNullException(nameof(array)); + array.NotNull(nameof(array)); (int offset, int length) = range.GetOffsetAndLength(array.Length); diff --git a/src/Basal/IFox.Basal.Shared/General/ArgumentNullEx.cs b/src/Basal/IFox.Basal.Shared/General/ArgumentNullEx.cs index 9c9f77d..22283a7 100644 --- a/src/Basal/IFox.Basal.Shared/General/ArgumentNullEx.cs +++ b/src/Basal/IFox.Basal.Shared/General/ArgumentNullEx.cs @@ -14,9 +14,10 @@ public static class ArgumentNullEx /// 参数 /// 参数为null时的提示信息 /// - public static void NotNull(this T argument, string? argumentExpression = null) where T : class + public static void NotNull(this T? argument, string? argumentExpression = null) where T : class { if (argument == null) throw new ArgumentNullException(paramName: argumentExpression); } + } } diff --git a/src/Basal/IFox.Basal.Shared/Sortedset/Sortedset.cs b/src/Basal/IFox.Basal.Shared/Sortedset/Sortedset.cs index 9560d17..9365cb4 100644 --- a/src/Basal/IFox.Basal.Shared/Sortedset/Sortedset.cs +++ b/src/Basal/IFox.Basal.Shared/Sortedset/Sortedset.cs @@ -26,6 +26,7 @@ namespace System.Collections.Generic { + using IFox.Basal; using System; using System.Diagnostics; using System.Diagnostics.CodeAnalysis; @@ -127,11 +128,11 @@ public SortedSet(IEnumerable collection) : this(collection, Comparer.Defau public SortedSet(IEnumerable collection, IComparer comparer) : this(comparer) { - if (collection == null) - { - throw new ArgumentNullException("collection"); - } - + //if (collection == null) + //{ + // throw new ArgumentNullException("collection"); + //} + collection.NotNull(nameof(collection)); // these are explicit type checks in the mould of HashSet. It would have worked better // with something like an ISorted (we could make this work for SortedList.Keys etc) SortedSet baseTreeSubSet = collection as TreeSubSet; @@ -1180,10 +1181,11 @@ internal T[] ToArray() /// public void UnionWith(IEnumerable other) { - if (other == null) - { - throw new ArgumentNullException("other"); - } + //if (other == null) + //{ + // throw new ArgumentNullException("other"); + //} + other.NotNull(nameof(other)); SortedSet s = other as SortedSet; TreeSubSet t = this as TreeSubSet; @@ -1346,10 +1348,11 @@ private static Node ConstructRootFromSortedArray(T[] arr, int startIndex, int en /// public virtual void IntersectWith(IEnumerable other) { - if (other == null) - { - throw new ArgumentNullException("other"); - } + //if (other == null) + //{ + // throw new ArgumentNullException("other"); + //} + other.NotNull(nameof(other)); if (Count == 0) return; @@ -1433,10 +1436,11 @@ internal virtual void IntersectWithEnumerable(IEnumerable other) /// public void ExceptWith(IEnumerable other) { - if (other == null) - { - throw new ArgumentNullException("other"); - } + //if (other == null) + //{ + // throw new ArgumentNullException("other"); + //} + other.NotNull(nameof(other)); if (count == 0) return; @@ -1480,11 +1484,11 @@ public void ExceptWith(IEnumerable other) /// public void SymmetricExceptWith(IEnumerable other) { - if (other == null) - { - throw new ArgumentNullException("other"); - } - + //if (other == null) + //{ + // throw new ArgumentNullException("other"); + //} + other.NotNull(nameof(other)); if (this.Count == 0) { this.UnionWith(other); @@ -1574,11 +1578,11 @@ internal void SymmetricExceptWithSameEC(T[] other) [System.Security.SecuritySafeCritical] public bool IsSubsetOf(IEnumerable other) { - if (other == null) - { - throw new ArgumentNullException("other"); - } - + //if (other == null) + //{ + // throw new ArgumentNullException("other"); + //} + other.NotNull(nameof(other)); if (Count == 0) return true; @@ -1619,11 +1623,11 @@ private bool IsSubsetOfSortedSetWithSameEC(SortedSet asSorted) [System.Security.SecuritySafeCritical] public bool IsProperSubsetOf(IEnumerable other) { - if (other == null) - { - throw new ArgumentNullException("other"); - } - + //if (other == null) + //{ + // throw new ArgumentNullException("other"); + //} + other.NotNull(nameof(other)); if ((other as ICollection) != null) { if (Count == 0) @@ -1661,11 +1665,11 @@ public bool IsProperSubsetOf(IEnumerable other) /// public bool IsSupersetOf(IEnumerable other) { - if (other == null) - { - throw new ArgumentNullException("other"); - } - + //if (other == null) + //{ + // throw new ArgumentNullException("other"); + //} + other.NotNull(nameof(other)); if ((other as ICollection) != null && (other as ICollection).Count == 0) return true; @@ -1701,11 +1705,11 @@ public bool IsSupersetOf(IEnumerable other) [System.Security.SecuritySafeCritical] public bool IsProperSupersetOf(IEnumerable other) { - if (other == null) - { - throw new ArgumentNullException("other"); - } - + //if (other == null) + //{ + // throw new ArgumentNullException("other"); + //} + other.NotNull(nameof(other)); if (Count == 0) return false; @@ -1753,11 +1757,11 @@ public bool IsProperSupersetOf(IEnumerable other) [System.Security.SecuritySafeCritical] public bool SetEquals(IEnumerable other) { - if (other == null) - { - throw new ArgumentNullException("other"); - } - + //if (other == null) + //{ + // throw new ArgumentNullException("other"); + //} + other.NotNull(nameof(other)); #if USING_HASH_SET HashSet asHash = other as HashSet; if (asHash != null && comparer.Equals(Comparer.Default) && asHash.Comparer.Equals(EqualityComparer.Default)) { @@ -1797,11 +1801,11 @@ public bool SetEquals(IEnumerable other) /// public bool Overlaps(IEnumerable other) { - if (other == null) - { - throw new ArgumentNullException("other"); - } - + //if (other == null) + //{ + // throw new ArgumentNullException("other"); + //} + other.NotNull(nameof(other)); if (this.Count == 0) return false; @@ -1930,10 +1934,11 @@ private unsafe ElementCount CheckUniqueAndUnfoundElements(IEnumerable other, } public int RemoveWhere(Predicate match) { - if (match == null) - { - throw new ArgumentNullException("match"); - } + //if (match == null) + //{ + // throw new ArgumentNullException("match"); + //} + match.NotNull(nameof(match)); List matches = new(this.Count); BreadthFirstTreeWalk(delegate (Node n) diff --git a/src/Basal/IFox.Basal.Shared/WindowsAPI/WindowsAPI.cs b/src/Basal/IFox.Basal.Shared/WindowsAPI/WindowsAPI.cs index 8f8f75d..3a1b4df 100644 --- a/src/Basal/IFox.Basal.Shared/WindowsAPI/WindowsAPI.cs +++ b/src/Basal/IFox.Basal.Shared/WindowsAPI/WindowsAPI.cs @@ -1,5 +1,7 @@ #define Marshal +using IFox.Basal; + namespace IFoxCAD.Basal; public partial class WindowsAPI { @@ -80,8 +82,9 @@ public partial class WindowsAPI /// public static bool GlobalLockTask(IntPtr data, Action task) { - if (task == null) - throw new ArgumentNullException(nameof(task)); + //if (task == null) + // throw new ArgumentNullException(nameof(task)); + task.NotNull(nameof(task)); if (data == IntPtr.Zero) return false; diff --git a/src/CAD/IFox.CAD.Shared/Algorithms/Graph/Graph.cs b/src/CAD/IFox.CAD.Shared/Algorithms/Graph/Graph.cs index d772b03..e22fe08 100644 --- a/src/CAD/IFox.CAD.Shared/Algorithms/Graph/Graph.cs +++ b/src/CAD/IFox.CAD.Shared/Algorithms/Graph/Graph.cs @@ -1,4 +1,6 @@ namespace IFoxCAD.Cad; + +using IFox.Basal; using Exception = System.Exception; /// @@ -89,8 +91,10 @@ public IGraphVertex AddVertex(Point3d pt) /// public void AddEdge(Curve3d curve) { - if (curve == null) - throw new ArgumentNullException(nameof(curve)); + //if (curve == null) + // throw new ArgumentNullException(nameof(curve)); + + curve.NotNull(nameof(curve)); var start = AddVertex(curve.StartPoint); var end = AddVertex(curve.EndPoint); @@ -151,8 +155,9 @@ public void RemoveVertex(Point3d pt) /// 曲线 public void RemoveEdge(Curve3d curve) { - if (curve == null) - throw new ArgumentNullException(nameof(curve)); + //if (curve == null) + // throw new ArgumentNullException(nameof(curve)); + curve.NotNull(nameof(curve)); RemoveVertex(curve.StartPoint); RemoveVertex(curve.EndPoint); diff --git a/src/CAD/IFox.CAD.Shared/Algorithms/QuadTree/Rect.cs b/src/CAD/IFox.CAD.Shared/Algorithms/QuadTree/Rect.cs index a6a5d84..c358691 100644 --- a/src/CAD/IFox.CAD.Shared/Algorithms/QuadTree/Rect.cs +++ b/src/CAD/IFox.CAD.Shared/Algorithms/QuadTree/Rect.cs @@ -1,4 +1,5 @@ -using System.Diagnostics; +using IFox.Basal; +using System.Diagnostics; using System.Runtime.CompilerServices; namespace IFoxCAD.Cad; @@ -314,9 +315,10 @@ public Rect Expand(double d) /// public static bool IsRectAngle(List? ptList, double tolerance = 1e-8) { - if (ptList == null) - throw new ArgumentNullException(nameof(ptList)); + //if (ptList == null) + // throw new ArgumentNullException(nameof(ptList)); + ptList.NotNull(nameof(ptList)); var pts = ptList.ToList(); /* * 消重,不这里设置,否则这不是一个正确的单元测试 @@ -369,9 +371,9 @@ static double DotProductValue(Point2d o, Point2d a, Point2d b) /// public static bool IsRect(List? ptList, double tolerance = 1e-10) { - if (ptList == null) - throw new ArgumentNullException(nameof(ptList)); - + //if (ptList == null) + // throw new ArgumentNullException(nameof(ptList)); + ptList.NotNull(nameof(ptList)); var pts = ptList.ToList(); if (ptList.Count == 5) { @@ -419,9 +421,9 @@ public static (Point2d boxMin, Point2d boxMax) GetMinMax(IEnumerable pt /// public static bool RectAnglePointOrder(List? pts) { - if (pts == null) - throw new ArgumentNullException(nameof(pts)); - + //if (pts == null) + // throw new ArgumentNullException(nameof(pts)); + pts.NotNull(nameof(pts)); if (!Rect.IsRectAngle(pts)) return false; diff --git a/src/CAD/IFox.CAD.Shared/Copyclip/BitmapTool.cs b/src/CAD/IFox.CAD.Shared/Copyclip/BitmapTool.cs index f044095..08e2be5 100644 --- a/src/CAD/IFox.CAD.Shared/Copyclip/BitmapTool.cs +++ b/src/CAD/IFox.CAD.Shared/Copyclip/BitmapTool.cs @@ -1,5 +1,6 @@ namespace IFoxCAD.Cad; +using IFox.Basal; using System; /// /// bitmap工具类 @@ -137,9 +138,9 @@ public enum TernaryRasterOperations : uint /// public static void CaptureWndImage(IntPtr hWnd, Action action) { - if (action == null) - throw new ArgumentNullException(nameof(action)); - + //if (action == null) + // throw new ArgumentNullException(nameof(action)); + action.NotNull(nameof(action)); var hDC = GetDC(hWnd); var hMemDC = CreateCompatibleDC(hDC); if (hMemDC == IntPtr.Zero) diff --git a/src/CAD/IFox.CAD.Shared/Copyclip/EmfTool.cs b/src/CAD/IFox.CAD.Shared/Copyclip/EmfTool.cs index 32b9545..08b068d 100644 --- a/src/CAD/IFox.CAD.Shared/Copyclip/EmfTool.cs +++ b/src/CAD/IFox.CAD.Shared/Copyclip/EmfTool.cs @@ -1,5 +1,6 @@ namespace IFoxCAD.Cad; +using IFox.Basal; using System; using System.Diagnostics; using System.Drawing; @@ -547,7 +548,7 @@ public static void SetEnhMetaFileDescriptionEx(ref IntPtr hMetaFile, string desc { if (hMetaFile == IntPtr.Zero) throw new ArgumentNullException(nameof(hMetaFile)); - + var emh = EnhMetaHeader.Create(hMetaFile);//emf结构 GetEnhMetaFileHeader // 创建画布句柄 IntRect intRect = emh.rclFrame; //new(0, 0, 0, 0); @@ -818,9 +819,9 @@ public static void SaveMetaFile(this Metafile file, string emfName) /// public static void ToMetafile(byte[] data, Func task) { - if (task == null) - throw new ArgumentNullException(nameof(task)); - + //if (task == null) + // throw new ArgumentNullException(nameof(task)); + task.NotNull(nameof(task)); IntPtr hemf = SetEnhMetaFileBits((uint)data.Length, data); using var mf = new Metafile(hemf, true); if (task.Invoke(mf)) // 对图像进行操作,就不能进行删除句柄 diff --git a/src/CAD/IFox.CAD.Shared/Copyclip/TagClipboardInfo.cs b/src/CAD/IFox.CAD.Shared/Copyclip/TagClipboardInfo.cs index 978e9f8..812693d 100644 --- a/src/CAD/IFox.CAD.Shared/Copyclip/TagClipboardInfo.cs +++ b/src/CAD/IFox.CAD.Shared/Copyclip/TagClipboardInfo.cs @@ -1,5 +1,6 @@ namespace IFoxCAD.Cad; +using IFox.Basal; using IFoxCAD.Com; using System; using System.Diagnostics; @@ -315,9 +316,9 @@ public partial class ClipTool [System.Diagnostics.DebuggerStepThrough] public static bool OpenClipboardTask(bool isWrite, Action action) { - if (action == null) - throw new ArgumentNullException(nameof(action)); - + //if (action == null) + // throw new ArgumentNullException(nameof(action)); + action.NotNull(nameof(action)); bool openFlag = false; try { diff --git a/src/CAD/IFox.CAD.Shared/ExtensionMethod/CurveEx.cs b/src/CAD/IFox.CAD.Shared/ExtensionMethod/CurveEx.cs index 905d38f..04d4bf6 100644 --- a/src/CAD/IFox.CAD.Shared/ExtensionMethod/CurveEx.cs +++ b/src/CAD/IFox.CAD.Shared/ExtensionMethod/CurveEx.cs @@ -1,3 +1,5 @@ +using IFox.Basal; + namespace IFoxCAD.Cad; /// @@ -23,9 +25,9 @@ public static double GetLength(this Curve curve) /// 打断后曲线的集合 public static IEnumerable GetSplitCurves(this Curve curve, IEnumerable pars) { - if (pars is null) - throw new ArgumentNullException(nameof(pars)); - + //if (pars is null) + // throw new ArgumentNullException(nameof(pars)); + pars.NotNull(nameof(pars)); return curve .GetSplitCurves(new DoubleCollection(pars.ToArray())) @@ -46,8 +48,9 @@ public static IEnumerable GetSplitCurves(this Curve curve, IEnumerable打断后曲线的集合 public static IEnumerable GetSplitCurves(this Curve curve, IEnumerable pars, bool isOrder = false) { - if (pars is null) - throw new ArgumentNullException(nameof(pars)); + //if (pars is null) + // throw new ArgumentNullException(nameof(pars)); + pars.NotNull(nameof(pars)); if (isOrder) pars = pars.OrderBy(x => x); @@ -65,9 +68,9 @@ public static IEnumerable GetSplitCurves(this Curve curve, IEnumerable打断后曲线的集合 public static IEnumerable GetSplitCurves(this Curve curve, IEnumerable points) { - if (points is null) - throw new ArgumentNullException(nameof(points)); - + //if (points is null) + // throw new ArgumentNullException(nameof(points)); + points.NotNull(nameof(points)); using var pts = new Point3dCollection(points.ToArray()); return curve.GetSplitCurves(pts).Cast(); } @@ -86,9 +89,9 @@ public static IEnumerable GetSplitCurves(this Curve curve, IEnumerable打断后曲线的集合 public static IEnumerable GetSplitCurves(this Curve curve, IEnumerable points, bool isOrder = false) { - if (points is null) - throw new ArgumentNullException(nameof(points)); - + //if (points is null) + // throw new ArgumentNullException(nameof(points)); + points.NotNull(nameof(points)); if (isOrder) points = points.OrderBy(point => { var pt = curve.GetClosestPointTo(point, false); @@ -106,8 +109,7 @@ public static IEnumerable GetSplitCurves(this Curve curve, IEnumerable所有的闭合环的曲线集合 public static IEnumerable GetAllCycle(this IEnumerable curves) { - if (curves is null) - throw new ArgumentNullException(nameof(curves)); + curves.NotNull(nameof(curves)); // 新建图 var graph = new Graph(); @@ -145,8 +147,7 @@ public static IEnumerable GetAllCycle(this IEnumerable curves) /// 打断后的曲线列表 public static List BreakCurve(this List curves) { - if (curves is null) - throw new ArgumentNullException(nameof(curves)); + curves.NotNull(nameof(curves)); var geCurves = new List(); // 存储曲线转换后的复合曲线 var paramss = new List>(); // 存储每个曲线的交点参数值 diff --git a/src/CAD/IFox.CAD.Shared/ExtensionMethod/Entity/BlockReferenceEx.cs b/src/CAD/IFox.CAD.Shared/ExtensionMethod/Entity/BlockReferenceEx.cs index 103cecc..b44ddcd 100644 --- a/src/CAD/IFox.CAD.Shared/ExtensionMethod/Entity/BlockReferenceEx.cs +++ b/src/CAD/IFox.CAD.Shared/ExtensionMethod/Entity/BlockReferenceEx.cs @@ -1,4 +1,6 @@ -namespace IFoxCAD.Cad; +using IFox.Basal; + +namespace IFoxCAD.Cad; /// /// 块参照扩展类 @@ -93,9 +95,9 @@ public static void ChangeBlockProperty(this BlockReference blockReference, [System.Diagnostics.DebuggerStepThrough] public static void ForEach(this BlockReference brf, Action action, DBTrans? tr = null) { - if (action == null) - throw new ArgumentNullException(nameof(action)); - + //if (action == null) + // throw new ArgumentNullException(nameof(action)); + action.NotNull(nameof(action)); tr ??= DBTrans.Top; var btr = tr.GetObject(brf.BlockTableRecord); if (btr == null) @@ -112,9 +114,9 @@ public static void ForEach(this BlockReference brf, Action action, DBT [System.Diagnostics.DebuggerStepThrough] public static void ForEach(this BlockReference brf, Action action, DBTrans? tr = null) { - if (action == null) - throw new ArgumentNullException(nameof(action)); - + //if (action == null) + // throw new ArgumentNullException(nameof(action)); + action.NotNull(nameof(action)); tr ??= DBTrans.Top; var btr = tr.GetObject(brf.BlockTableRecord); if (btr == null) @@ -131,9 +133,7 @@ public static void ForEach(this BlockReference brf, Action [System.Diagnostics.DebuggerStepThrough] public static void ForEach(this BlockReference brf, Action action, DBTrans? tr = null) { - if (action == null) - throw new ArgumentNullException(nameof(action)); - + action.NotNull(nameof(action)); tr ??= DBTrans.Top; var btr = tr.GetObject(brf.BlockTableRecord); if (btr == null) diff --git a/src/CAD/IFox.CAD.Shared/ExtensionMethod/JigEx.cs b/src/CAD/IFox.CAD.Shared/ExtensionMethod/JigEx.cs index 5b6d69c..6be55ef 100644 --- a/src/CAD/IFox.CAD.Shared/ExtensionMethod/JigEx.cs +++ b/src/CAD/IFox.CAD.Shared/ExtensionMethod/JigEx.cs @@ -1,4 +1,6 @@ -namespace IFoxCAD.Cad; +using IFox.Basal; + +namespace IFoxCAD.Cad; /* 封装jig * 20220726 隐藏事件,利用函数进行数据库图元重绘 @@ -319,8 +321,9 @@ static JigPromptPointOptions JigPointOptions() public void SetSpaceIsKeyword() { var opt = _options; - if (opt == null) - throw new ArgumentNullException(nameof(_options)); + //if (opt == null) + // throw new ArgumentNullException(nameof(_options)); + opt.NotNull(nameof(_options)); if ((opt.UserInputControls & UserInputControls.NullResponseAccepted) == UserInputControls.NullResponseAccepted) opt.UserInputControls ^= UserInputControls.NullResponseAccepted; // 输入了鼠标右键,结束jig diff --git a/src/CAD/IFox.CAD.Shared/ExtensionMethod/JsonConverter.cs b/src/CAD/IFox.CAD.Shared/ExtensionMethod/JsonConverter.cs index c1044a9..b846048 100644 --- a/src/CAD/IFox.CAD.Shared/ExtensionMethod/JsonConverter.cs +++ b/src/CAD/IFox.CAD.Shared/ExtensionMethod/JsonConverter.cs @@ -1,4 +1,6 @@ -namespace IFoxCAD.Cad; +using IFox.Basal; + +namespace IFoxCAD.Cad; #if NewtonsoftJson /* @@ -94,9 +96,9 @@ public override IDictionary Serialize(object obj, JavaScriptSeri /// public override object Deserialize(IDictionary dictionary, Type type, JavaScriptSerializer serializer) { - if (dictionary == null) - throw new ArgumentNullException(nameof(dictionary)); - + //if (dictionary == null) + // throw new ArgumentNullException(nameof(dictionary)); + dictionary.NotNull(nameof(dictionary)); if (type != typeof(ObjectId)) return null!; diff --git a/src/CAD/IFox.CAD.Shared/ExtensionMethod/PointEx.cs b/src/CAD/IFox.CAD.Shared/ExtensionMethod/PointEx.cs index 99187e8..2a329dd 100644 --- a/src/CAD/IFox.CAD.Shared/ExtensionMethod/PointEx.cs +++ b/src/CAD/IFox.CAD.Shared/ExtensionMethod/PointEx.cs @@ -1,3 +1,5 @@ +using IFox.Basal; + namespace IFoxCAD.Cad; public static class PointEx @@ -97,8 +99,7 @@ public static double GetArcBulge(this Point2d arc1, Point2d arc2, Point2d arc3, [System.Diagnostics.DebuggerStepThrough] public static void End2End(this Point2dCollection ptcol) { - if (ptcol == null) - throw new ArgumentNullException(nameof(ptcol)); + ptcol.NotNull(nameof(ptcol)); if (ptcol.Count == 0 || ptcol[0].Equals(ptcol[^1]))// 首尾相同直接返回 return; @@ -118,9 +119,7 @@ public static void End2End(this Point2dCollection ptcol) [System.Diagnostics.DebuggerStepThrough] public static void End2End(this Point3dCollection ptcol) { - if (ptcol == null) - throw new ArgumentNullException(nameof(ptcol)); - + ptcol.NotNull(nameof(ptcol)); if (ptcol.Count == 0 || ptcol[0].Equals(ptcol[^1]))// 首尾相同直接返回 return; diff --git a/src/CAD/IFox.CAD.Shared/ExtensionMethod/SelectionSetEx.cs b/src/CAD/IFox.CAD.Shared/ExtensionMethod/SelectionSetEx.cs index a87610f..51f1484 100644 --- a/src/CAD/IFox.CAD.Shared/ExtensionMethod/SelectionSetEx.cs +++ b/src/CAD/IFox.CAD.Shared/ExtensionMethod/SelectionSetEx.cs @@ -1,4 +1,6 @@ -namespace IFoxCAD.Cad; +using IFox.Basal; + +namespace IFoxCAD.Cad; /// /// 选择集扩展类 @@ -79,9 +81,9 @@ public static IEnumerable GetEntities(this SelectionSet ss, bool openErased = false, bool openLockedLayer = false) where T : Entity { - if (ss is null) - throw new ArgumentNullException(nameof(ss)); - + //if (ss is null) + // throw new ArgumentNullException(nameof(ss)); + ss.NotNull(nameof(ss)); trans ??= DBTrans.Top; return ss.GetObjectIds() .Select(id => trans.GetObject(id, openMode, openErased, openLockedLayer)) @@ -133,9 +135,7 @@ public static void ForEach(this SelectionSet ss, bool openErased = false, bool openLockedLayer = false) where T : Entity { - if (action == null) - throw new ArgumentNullException(nameof(action)); - + action.NotNull(nameof(action)); trans ??= DBTrans.Top; LoopState state = new(); diff --git a/src/CAD/IFox.CAD.Shared/ExtensionMethod/SymbolTableRecordEx.cs b/src/CAD/IFox.CAD.Shared/ExtensionMethod/SymbolTableRecordEx.cs index f2ddec2..d9f811c 100644 --- a/src/CAD/IFox.CAD.Shared/ExtensionMethod/SymbolTableRecordEx.cs +++ b/src/CAD/IFox.CAD.Shared/ExtensionMethod/SymbolTableRecordEx.cs @@ -505,7 +505,6 @@ public static void ForEach(this TRecord record, Action是否外部参照 public XrefPath(BlockReference brf, DBTrans tr) { - if (brf == null) - throw new ArgumentNullException(nameof(brf)); - + //if (brf == null) + // throw new ArgumentNullException(nameof(brf)); + brf.NotNull(nameof(brf)); CurrentDatabasePath = Path.GetDirectoryName(tr.Database.Filename); var btRec = tr.GetObject(brf.BlockTableRecord);// 块表记录 @@ -499,10 +501,13 @@ public XrefPath(BlockReference brf, DBTrans tr) /// public static string? PathConverter(string? directory, string? fileRelations, PathConverterModes converterModes) { - if (directory == null) - throw new ArgumentNullException(nameof(directory)); - if (fileRelations == null) - throw new ArgumentNullException(nameof(fileRelations)); + //if (directory == null) + // throw new ArgumentNullException(nameof(directory)); + //if (fileRelations == null) + // throw new ArgumentNullException(nameof(fileRelations)); + + directory.NotNull(nameof(directory)); + fileRelations.NotNull(nameof(fileRelations)); string? result = null; switch (converterModes) diff --git "a/src/CAD/IFox.CAD.Shared/ExtensionMethod/\346\226\260\345\273\272\345\241\253\345\205\205/HatchConverter.cs" "b/src/CAD/IFox.CAD.Shared/ExtensionMethod/\346\226\260\345\273\272\345\241\253\345\205\205/HatchConverter.cs" index ea7ca2e..8b4140d 100644 --- "a/src/CAD/IFox.CAD.Shared/ExtensionMethod/\346\226\260\345\273\272\345\241\253\345\205\205/HatchConverter.cs" +++ "b/src/CAD/IFox.CAD.Shared/ExtensionMethod/\346\226\260\345\273\272\345\241\253\345\205\205/HatchConverter.cs" @@ -1,5 +1,6 @@ namespace IFoxCAD.Cad; +using IFox.Basal; using System.Data; using PointV = Point2d; @@ -156,11 +157,15 @@ public void GetBoundarysData() /// 收集图元信息 static void HatchLoopIsPolyline(HatchLoop loop, HatchConverterData hcData) { - if (loop is null) - throw new ArgumentNullException(nameof(loop)); + //if (loop is null) + // throw new ArgumentNullException(nameof(loop)); + + //if (hcData is null) + // throw new ArgumentNullException(nameof(hcData)); + + loop.NotNull(nameof(loop)); + hcData.NotNull(nameof(hcData)); - if (hcData is null) - throw new ArgumentNullException(nameof(hcData)); // 判断为圆形: // 上下两个圆弧,然后填充,就会生成此种填充 @@ -186,8 +191,10 @@ static void HatchLoopIsPolyline(HatchLoop loop, HatchConverterData hcData) /// static CircleData? TwoArcFormOneCircle(HatchLoop loop) { - if (loop is null) - throw new ArgumentNullException(nameof(loop)); + //if (loop is null) + // throw new ArgumentNullException(nameof(loop)); + + loop.NotNull(nameof(loop)); if (loop.Curves.Count != 2) throw new ArgumentException( diff --git "a/src/CAD/IFox.CAD.Shared/ExtensionMethod/\346\226\260\345\273\272\345\241\253\345\205\205/HatchInfo.cs" "b/src/CAD/IFox.CAD.Shared/ExtensionMethod/\346\226\260\345\273\272\345\241\253\345\205\205/HatchInfo.cs" index cf58f07..660301e 100644 --- "a/src/CAD/IFox.CAD.Shared/ExtensionMethod/\346\226\260\345\273\272\345\241\253\345\205\205/HatchInfo.cs" +++ "b/src/CAD/IFox.CAD.Shared/ExtensionMethod/\346\226\260\345\273\272\345\241\253\345\205\205/HatchInfo.cs" @@ -1,4 +1,6 @@ -namespace IFoxCAD.Cad; +using IFox.Basal; + +namespace IFoxCAD.Cad; /* * 添加的第一个边界必须是外边界,即用于定义图案填充最外面的边界。 @@ -292,9 +294,9 @@ public HatchInfo AppendLoop(Point2dCollection pts, BlockTableRecord btrOfAddEntitySpace, HatchLoopTypes hatchLoopTypes = HatchLoopTypes.Default) { - if (pts == null) - throw new ArgumentNullException(nameof(pts)); - + //if (pts == null) + // throw new ArgumentNullException(nameof(pts)); + pts.NotNull(nameof(pts)); pts.End2End(); #if NET35 _boundaryIds.Add(CreateAddBoundary(pts, bluges, btrOfAddEntitySpace)); diff --git a/src/CAD/IFox.CAD.Shared/Runtime/AcadVersion.cs b/src/CAD/IFox.CAD.Shared/Runtime/AcadVersion.cs index 61aa620..97c0da7 100644 --- a/src/CAD/IFox.CAD.Shared/Runtime/AcadVersion.cs +++ b/src/CAD/IFox.CAD.Shared/Runtime/AcadVersion.cs @@ -1,4 +1,6 @@ -namespace IFoxCAD.Cad; +using IFox.Basal; + +namespace IFoxCAD.Cad; /// /// cad版本号类 @@ -48,8 +50,7 @@ public static List Versions /// cad版本号对象 public static CadVersion? FromApp(object app) { - if (app == null) - throw new ArgumentNullException(nameof(app)); + app.NotNull(nameof(app)); string acver = app.GetType() .InvokeMember( diff --git a/src/CAD/IFox.CAD.Shared/Runtime/DBTrans.cs b/src/CAD/IFox.CAD.Shared/Runtime/DBTrans.cs index 90fc981..32fa5cf 100644 --- a/src/CAD/IFox.CAD.Shared/Runtime/DBTrans.cs +++ b/src/CAD/IFox.CAD.Shared/Runtime/DBTrans.cs @@ -1,5 +1,6 @@ namespace IFoxCAD.Cad; +using IFox.Basal; using System.Diagnostics; using System.IO; using System.Threading; @@ -570,9 +571,9 @@ public void SaveFile(DwgVersion version = DwgVersion.AC1800, /// 开启单行文字偏移处理 public void Task(Action action, bool handlingDBTextDeviation = true) { - if (action == null) - throw new ArgumentNullException(nameof(action)); - + //if (action == null) + // throw new ArgumentNullException(nameof(action)); + action.NotNull(nameof(action)); // 前台开图 || 后台直接处理 if (Document != null || !handlingDBTextDeviation) { diff --git a/src/CAD/IFox.CAD.Shared/Runtime/Env.cs b/src/CAD/IFox.CAD.Shared/Runtime/Env.cs index 7f2b5e5..b3c5579 100644 --- a/src/CAD/IFox.CAD.Shared/Runtime/Env.cs +++ b/src/CAD/IFox.CAD.Shared/Runtime/Env.cs @@ -1,3 +1,5 @@ +using IFox.Basal; + namespace IFoxCAD.Cad; /// @@ -695,10 +697,13 @@ public static string GetAcapVersionDll(string str = "acdb") /// public static object? SetVarEx(string? key, string? value) { - if (key == null) - throw new ArgumentNullException(nameof(key)); - if (value == null) - throw new ArgumentNullException(nameof(value)); + //if (key == null) + // throw new ArgumentNullException(nameof(key)); + //if (value == null) + // throw new ArgumentNullException(nameof(value)); + key.NotNull(nameof(key)); + value.NotNull(nameof(value)); + var currentVar = Env.GetVar(key); if (currentVar == null) @@ -727,9 +732,9 @@ public static string GetAcapVersionDll(string str = "acdb") /// 返回现有变量词典,然后下次就可以利用它进行设置回来了 public static Dictionary SaveCadVar(Dictionary args) { - if (args is null) - throw new ArgumentNullException(nameof(args)); - + //if (args is null) + // throw new ArgumentNullException(nameof(args)); + args.NotNull(nameof(args)); var dict = new Dictionary(); foreach (var item in args) { diff --git a/src/CAD/IFox.CAD.Shared/Runtime/MethodInfoHelper.cs b/src/CAD/IFox.CAD.Shared/Runtime/MethodInfoHelper.cs index 1111037..5be6f6d 100644 --- a/src/CAD/IFox.CAD.Shared/Runtime/MethodInfoHelper.cs +++ b/src/CAD/IFox.CAD.Shared/Runtime/MethodInfoHelper.cs @@ -1,4 +1,6 @@ -namespace IFoxCAD.Cad; +using IFox.Basal; + +namespace IFoxCAD.Cad; internal static class MethodInfoHelper { @@ -13,9 +15,7 @@ internal static class MethodInfoHelper /// 已经外部创建的对象,为空则此处创建 public static object? Invoke(this MethodInfo methodInfo, ref object? instance) { - if (methodInfo == null) - throw new ArgumentNullException(nameof(methodInfo)); - + methodInfo.NotNull(nameof(methodInfo)); object? result = null; if (methodInfo.IsStatic) { diff --git a/src/CAD/IFox.CAD.Shared/Runtime/SymbolTable.cs b/src/CAD/IFox.CAD.Shared/Runtime/SymbolTable.cs index 1a0c0ef..86311a0 100644 --- a/src/CAD/IFox.CAD.Shared/Runtime/SymbolTable.cs +++ b/src/CAD/IFox.CAD.Shared/Runtime/SymbolTable.cs @@ -372,7 +372,6 @@ public void ForEach(Action task, { if (task == null) throw new ArgumentNullException(nameof(task)); - LoopState state = new();/*这种方式比Action改Func更友好*/ int i = 0; foreach (var id in this) -- Gitee From 05990d51ee2375e085776341bdda9e1178db879b Mon Sep 17 00:00:00 2001 From: vicwjb Date: Tue, 14 Feb 2023 23:28:01 +0800 Subject: [PATCH 673/675] =?UTF-8?q?=E7=BB=9F=E4=B8=80=E5=90=8D=E7=A7=B0?= =?UTF-8?q?=E7=A9=BA=E9=97=B4=EF=BC=8C=E6=9B=BF=E6=8D=A2=E5=8F=82=E6=95=B0?= =?UTF-8?q?=E6=A3=80=E6=9F=A5=E5=87=BD=E6=95=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/Basal/IFox.Basal.Shared/CLS/RuntimeHelpers.cs | 2 +- .../IFox.Basal.Shared/General/ArgumentNullEx.cs | 4 ++-- src/Basal/IFox.Basal.Shared/Sortedset/Sortedset.cs | 2 +- src/Basal/IFox.Basal.Shared/WindowsAPI/WindowsAPI.cs | 2 -- src/CAD/IFox.CAD.Shared/Algorithms/Graph/Graph.cs | 1 - src/CAD/IFox.CAD.Shared/Algorithms/QuadTree/Rect.cs | 3 +-- src/CAD/IFox.CAD.Shared/Copyclip/BitmapTool.cs | 1 - src/CAD/IFox.CAD.Shared/Copyclip/EmfTool.cs | 7 +++---- src/CAD/IFox.CAD.Shared/Copyclip/TagClipboardInfo.cs | 2 -- src/CAD/IFox.CAD.Shared/ExtensionMethod/CurveEx.cs | 2 +- .../ExtensionMethod/Entity/BlockReferenceEx.cs | 2 +- src/CAD/IFox.CAD.Shared/ExtensionMethod/JigEx.cs | 2 +- .../IFox.CAD.Shared/ExtensionMethod/JsonConverter.cs | 2 +- src/CAD/IFox.CAD.Shared/ExtensionMethod/PointEx.cs | 2 +- .../ExtensionMethod/SelectionSetEx.cs | 2 +- .../ExtensionMethod/SymbolTableRecordEx.cs | 11 ++++++----- src/CAD/IFox.CAD.Shared/ExtensionMethod/XrefEx.cs | 2 +- .../HatchConverter.cs" | 2 +- .../HatchInfo.cs" | 2 +- src/CAD/IFox.CAD.Shared/Runtime/AcadVersion.cs | 2 +- src/CAD/IFox.CAD.Shared/Runtime/DBTrans.cs | 2 +- src/CAD/IFox.CAD.Shared/Runtime/Env.cs | 2 +- src/CAD/IFox.CAD.Shared/Runtime/MethodInfoHelper.cs | 2 +- src/CAD/IFox.CAD.Shared/Runtime/SymbolTable.cs | 12 +++++++----- 24 files changed, 34 insertions(+), 39 deletions(-) diff --git a/src/Basal/IFox.Basal.Shared/CLS/RuntimeHelpers.cs b/src/Basal/IFox.Basal.Shared/CLS/RuntimeHelpers.cs index 9957438..841c0d5 100644 --- a/src/Basal/IFox.Basal.Shared/CLS/RuntimeHelpers.cs +++ b/src/Basal/IFox.Basal.Shared/CLS/RuntimeHelpers.cs @@ -3,7 +3,7 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. -using IFox.Basal; +using IFoxCAD.Basal; namespace System.Runtime.CompilerServices; diff --git a/src/Basal/IFox.Basal.Shared/General/ArgumentNullEx.cs b/src/Basal/IFox.Basal.Shared/General/ArgumentNullEx.cs index 22283a7..b969143 100644 --- a/src/Basal/IFox.Basal.Shared/General/ArgumentNullEx.cs +++ b/src/Basal/IFox.Basal.Shared/General/ArgumentNullEx.cs @@ -1,6 +1,6 @@  -namespace IFox.Basal +namespace IFoxCAD.Basal { /// /// 参数null检查类 @@ -14,7 +14,7 @@ public static class ArgumentNullEx /// 参数 /// 参数为null时的提示信息 /// - public static void NotNull(this T? argument, string? argumentExpression = null) where T : class + public static void NotNull(this T? argument, string? argumentExpression = null) { if (argument == null) throw new ArgumentNullException(paramName: argumentExpression); } diff --git a/src/Basal/IFox.Basal.Shared/Sortedset/Sortedset.cs b/src/Basal/IFox.Basal.Shared/Sortedset/Sortedset.cs index 9365cb4..66a6a7f 100644 --- a/src/Basal/IFox.Basal.Shared/Sortedset/Sortedset.cs +++ b/src/Basal/IFox.Basal.Shared/Sortedset/Sortedset.cs @@ -26,7 +26,7 @@ namespace System.Collections.Generic { - using IFox.Basal; + using IFoxCAD.Basal; using System; using System.Diagnostics; using System.Diagnostics.CodeAnalysis; diff --git a/src/Basal/IFox.Basal.Shared/WindowsAPI/WindowsAPI.cs b/src/Basal/IFox.Basal.Shared/WindowsAPI/WindowsAPI.cs index 3a1b4df..6f8ac91 100644 --- a/src/Basal/IFox.Basal.Shared/WindowsAPI/WindowsAPI.cs +++ b/src/Basal/IFox.Basal.Shared/WindowsAPI/WindowsAPI.cs @@ -1,7 +1,5 @@ #define Marshal -using IFox.Basal; - namespace IFoxCAD.Basal; public partial class WindowsAPI { diff --git a/src/CAD/IFox.CAD.Shared/Algorithms/Graph/Graph.cs b/src/CAD/IFox.CAD.Shared/Algorithms/Graph/Graph.cs index e22fe08..30a76f8 100644 --- a/src/CAD/IFox.CAD.Shared/Algorithms/Graph/Graph.cs +++ b/src/CAD/IFox.CAD.Shared/Algorithms/Graph/Graph.cs @@ -1,6 +1,5 @@ namespace IFoxCAD.Cad; -using IFox.Basal; using Exception = System.Exception; /// diff --git a/src/CAD/IFox.CAD.Shared/Algorithms/QuadTree/Rect.cs b/src/CAD/IFox.CAD.Shared/Algorithms/QuadTree/Rect.cs index c358691..a4c219d 100644 --- a/src/CAD/IFox.CAD.Shared/Algorithms/QuadTree/Rect.cs +++ b/src/CAD/IFox.CAD.Shared/Algorithms/QuadTree/Rect.cs @@ -1,5 +1,4 @@ -using IFox.Basal; -using System.Diagnostics; +using System.Diagnostics; using System.Runtime.CompilerServices; namespace IFoxCAD.Cad; diff --git a/src/CAD/IFox.CAD.Shared/Copyclip/BitmapTool.cs b/src/CAD/IFox.CAD.Shared/Copyclip/BitmapTool.cs index 08e2be5..444acae 100644 --- a/src/CAD/IFox.CAD.Shared/Copyclip/BitmapTool.cs +++ b/src/CAD/IFox.CAD.Shared/Copyclip/BitmapTool.cs @@ -1,6 +1,5 @@ namespace IFoxCAD.Cad; -using IFox.Basal; using System; /// /// bitmap工具类 diff --git a/src/CAD/IFox.CAD.Shared/Copyclip/EmfTool.cs b/src/CAD/IFox.CAD.Shared/Copyclip/EmfTool.cs index 08b068d..da8ca7d 100644 --- a/src/CAD/IFox.CAD.Shared/Copyclip/EmfTool.cs +++ b/src/CAD/IFox.CAD.Shared/Copyclip/EmfTool.cs @@ -1,6 +1,5 @@ namespace IFoxCAD.Cad; -using IFox.Basal; using System; using System.Diagnostics; using System.Drawing; @@ -546,9 +545,9 @@ public enum DeviceCap : int /// public static void SetEnhMetaFileDescriptionEx(ref IntPtr hMetaFile, string desc) { - if (hMetaFile == IntPtr.Zero) - throw new ArgumentNullException(nameof(hMetaFile)); - + //if (hMetaFile == IntPtr.Zero) + // throw new ArgumentNullException(nameof(hMetaFile)); + hMetaFile.NotNull(nameof(hMetaFile)); var emh = EnhMetaHeader.Create(hMetaFile);//emf结构 GetEnhMetaFileHeader // 创建画布句柄 IntRect intRect = emh.rclFrame; //new(0, 0, 0, 0); diff --git a/src/CAD/IFox.CAD.Shared/Copyclip/TagClipboardInfo.cs b/src/CAD/IFox.CAD.Shared/Copyclip/TagClipboardInfo.cs index 812693d..ed9195b 100644 --- a/src/CAD/IFox.CAD.Shared/Copyclip/TagClipboardInfo.cs +++ b/src/CAD/IFox.CAD.Shared/Copyclip/TagClipboardInfo.cs @@ -1,7 +1,5 @@ namespace IFoxCAD.Cad; -using IFox.Basal; -using IFoxCAD.Com; using System; using System.Diagnostics; using System.Text; diff --git a/src/CAD/IFox.CAD.Shared/ExtensionMethod/CurveEx.cs b/src/CAD/IFox.CAD.Shared/ExtensionMethod/CurveEx.cs index 04d4bf6..bfca634 100644 --- a/src/CAD/IFox.CAD.Shared/ExtensionMethod/CurveEx.cs +++ b/src/CAD/IFox.CAD.Shared/ExtensionMethod/CurveEx.cs @@ -1,4 +1,4 @@ -using IFox.Basal; + namespace IFoxCAD.Cad; diff --git a/src/CAD/IFox.CAD.Shared/ExtensionMethod/Entity/BlockReferenceEx.cs b/src/CAD/IFox.CAD.Shared/ExtensionMethod/Entity/BlockReferenceEx.cs index b44ddcd..203bc54 100644 --- a/src/CAD/IFox.CAD.Shared/ExtensionMethod/Entity/BlockReferenceEx.cs +++ b/src/CAD/IFox.CAD.Shared/ExtensionMethod/Entity/BlockReferenceEx.cs @@ -1,4 +1,4 @@ -using IFox.Basal; + namespace IFoxCAD.Cad; diff --git a/src/CAD/IFox.CAD.Shared/ExtensionMethod/JigEx.cs b/src/CAD/IFox.CAD.Shared/ExtensionMethod/JigEx.cs index 6be55ef..15024ab 100644 --- a/src/CAD/IFox.CAD.Shared/ExtensionMethod/JigEx.cs +++ b/src/CAD/IFox.CAD.Shared/ExtensionMethod/JigEx.cs @@ -1,4 +1,4 @@ -using IFox.Basal; + namespace IFoxCAD.Cad; diff --git a/src/CAD/IFox.CAD.Shared/ExtensionMethod/JsonConverter.cs b/src/CAD/IFox.CAD.Shared/ExtensionMethod/JsonConverter.cs index b846048..975e511 100644 --- a/src/CAD/IFox.CAD.Shared/ExtensionMethod/JsonConverter.cs +++ b/src/CAD/IFox.CAD.Shared/ExtensionMethod/JsonConverter.cs @@ -1,4 +1,4 @@ -using IFox.Basal; + namespace IFoxCAD.Cad; diff --git a/src/CAD/IFox.CAD.Shared/ExtensionMethod/PointEx.cs b/src/CAD/IFox.CAD.Shared/ExtensionMethod/PointEx.cs index 2a329dd..2cfa8d2 100644 --- a/src/CAD/IFox.CAD.Shared/ExtensionMethod/PointEx.cs +++ b/src/CAD/IFox.CAD.Shared/ExtensionMethod/PointEx.cs @@ -1,4 +1,4 @@ -using IFox.Basal; + namespace IFoxCAD.Cad; diff --git a/src/CAD/IFox.CAD.Shared/ExtensionMethod/SelectionSetEx.cs b/src/CAD/IFox.CAD.Shared/ExtensionMethod/SelectionSetEx.cs index 51f1484..2223554 100644 --- a/src/CAD/IFox.CAD.Shared/ExtensionMethod/SelectionSetEx.cs +++ b/src/CAD/IFox.CAD.Shared/ExtensionMethod/SelectionSetEx.cs @@ -1,4 +1,4 @@ -using IFox.Basal; + namespace IFoxCAD.Cad; diff --git a/src/CAD/IFox.CAD.Shared/ExtensionMethod/SymbolTableRecordEx.cs b/src/CAD/IFox.CAD.Shared/ExtensionMethod/SymbolTableRecordEx.cs index d9f811c..1bd99a8 100644 --- a/src/CAD/IFox.CAD.Shared/ExtensionMethod/SymbolTableRecordEx.cs +++ b/src/CAD/IFox.CAD.Shared/ExtensionMethod/SymbolTableRecordEx.cs @@ -202,9 +202,9 @@ public static ObjectId AddCircle(this BlockTableRecord btr, Point3d p0, Point3d { var circle = CircleEx.CreateCircle(p0, p1, p2); // return circle is not null ? btr.AddEnt(circle, action, trans) : throw new ArgumentNullException(nameof(circle), "对象为 null"); - if (circle is null) - throw new ArgumentNullException(nameof(circle), "对象为 null"); - + //if (circle is null) + // throw new ArgumentNullException(nameof(circle), "对象为 null"); + circle.NotNull(nameof(circle)); return btr.AddEnt(circle, action, trans); } /// @@ -503,8 +503,9 @@ public static void ForEach(this TRecord record, Action(this TRecord record, Action task) where TRecord : SymbolTableRecord, IEnumerable { - if (task == null) - throw new ArgumentNullException(nameof(task)); + //if (task == null) + // throw new ArgumentNullException(nameof(task)); + task.NotNull(nameof(task)); int i = 0; LoopState state = new();/*这种方式比Action改Func更友好*/ foreach (ObjectId id in record) diff --git a/src/CAD/IFox.CAD.Shared/ExtensionMethod/XrefEx.cs b/src/CAD/IFox.CAD.Shared/ExtensionMethod/XrefEx.cs index 3bff4df..db54edc 100644 --- a/src/CAD/IFox.CAD.Shared/ExtensionMethod/XrefEx.cs +++ b/src/CAD/IFox.CAD.Shared/ExtensionMethod/XrefEx.cs @@ -1,6 +1,6 @@ // #define error_demo -using IFox.Basal; + namespace IFoxCAD.Cad; diff --git "a/src/CAD/IFox.CAD.Shared/ExtensionMethod/\346\226\260\345\273\272\345\241\253\345\205\205/HatchConverter.cs" "b/src/CAD/IFox.CAD.Shared/ExtensionMethod/\346\226\260\345\273\272\345\241\253\345\205\205/HatchConverter.cs" index 8b4140d..9f549df 100644 --- "a/src/CAD/IFox.CAD.Shared/ExtensionMethod/\346\226\260\345\273\272\345\241\253\345\205\205/HatchConverter.cs" +++ "b/src/CAD/IFox.CAD.Shared/ExtensionMethod/\346\226\260\345\273\272\345\241\253\345\205\205/HatchConverter.cs" @@ -1,6 +1,6 @@ namespace IFoxCAD.Cad; -using IFox.Basal; + using System.Data; using PointV = Point2d; diff --git "a/src/CAD/IFox.CAD.Shared/ExtensionMethod/\346\226\260\345\273\272\345\241\253\345\205\205/HatchInfo.cs" "b/src/CAD/IFox.CAD.Shared/ExtensionMethod/\346\226\260\345\273\272\345\241\253\345\205\205/HatchInfo.cs" index 660301e..1763aaf 100644 --- "a/src/CAD/IFox.CAD.Shared/ExtensionMethod/\346\226\260\345\273\272\345\241\253\345\205\205/HatchInfo.cs" +++ "b/src/CAD/IFox.CAD.Shared/ExtensionMethod/\346\226\260\345\273\272\345\241\253\345\205\205/HatchInfo.cs" @@ -1,4 +1,4 @@ -using IFox.Basal; + namespace IFoxCAD.Cad; diff --git a/src/CAD/IFox.CAD.Shared/Runtime/AcadVersion.cs b/src/CAD/IFox.CAD.Shared/Runtime/AcadVersion.cs index 97c0da7..6fe6afd 100644 --- a/src/CAD/IFox.CAD.Shared/Runtime/AcadVersion.cs +++ b/src/CAD/IFox.CAD.Shared/Runtime/AcadVersion.cs @@ -1,4 +1,4 @@ -using IFox.Basal; + namespace IFoxCAD.Cad; diff --git a/src/CAD/IFox.CAD.Shared/Runtime/DBTrans.cs b/src/CAD/IFox.CAD.Shared/Runtime/DBTrans.cs index 32fa5cf..1bd5c74 100644 --- a/src/CAD/IFox.CAD.Shared/Runtime/DBTrans.cs +++ b/src/CAD/IFox.CAD.Shared/Runtime/DBTrans.cs @@ -1,6 +1,6 @@ namespace IFoxCAD.Cad; -using IFox.Basal; + using System.Diagnostics; using System.IO; using System.Threading; diff --git a/src/CAD/IFox.CAD.Shared/Runtime/Env.cs b/src/CAD/IFox.CAD.Shared/Runtime/Env.cs index b3c5579..3a351ab 100644 --- a/src/CAD/IFox.CAD.Shared/Runtime/Env.cs +++ b/src/CAD/IFox.CAD.Shared/Runtime/Env.cs @@ -1,4 +1,4 @@ -using IFox.Basal; + namespace IFoxCAD.Cad; diff --git a/src/CAD/IFox.CAD.Shared/Runtime/MethodInfoHelper.cs b/src/CAD/IFox.CAD.Shared/Runtime/MethodInfoHelper.cs index 5be6f6d..5eb5f93 100644 --- a/src/CAD/IFox.CAD.Shared/Runtime/MethodInfoHelper.cs +++ b/src/CAD/IFox.CAD.Shared/Runtime/MethodInfoHelper.cs @@ -1,4 +1,4 @@ -using IFox.Basal; + namespace IFoxCAD.Cad; diff --git a/src/CAD/IFox.CAD.Shared/Runtime/SymbolTable.cs b/src/CAD/IFox.CAD.Shared/Runtime/SymbolTable.cs index 86311a0..22fadac 100644 --- a/src/CAD/IFox.CAD.Shared/Runtime/SymbolTable.cs +++ b/src/CAD/IFox.CAD.Shared/Runtime/SymbolTable.cs @@ -1,4 +1,4 @@ -using static System.Windows.Forms.AxHost; +//using static System.Windows.Forms.AxHost; namespace IFoxCAD.Cad; @@ -276,8 +276,9 @@ public IEnumerable GetRecordNames(Func filter) /// 对象id public ObjectId GetRecordFrom(SymbolTable table, string name, bool over) { - if (table is null) - throw new ArgumentNullException(nameof(table), "对象为null"); + //if (table is null) + // throw new ArgumentNullException(nameof(table), "对象为null"); + table.NotNull(nameof(table)); ObjectId rid = this[name]; bool has = rid != ObjectId.Null; @@ -370,8 +371,9 @@ public void ForEach(Action task, bool openErased = false, bool openLockedLayer = false) { - if (task == null) - throw new ArgumentNullException(nameof(task)); + //if (task == null) + // throw new ArgumentNullException(nameof(task)); + task.NotNull(nameof(task)); LoopState state = new();/*这种方式比Action改Func更友好*/ int i = 0; foreach (var id in this) -- Gitee From cdc6faa6dd3f023fc66db3edeebe7030f81881e4 Mon Sep 17 00:00:00 2001 From: vicwjb Date: Thu, 23 Mar 2023 23:37:25 +0800 Subject: [PATCH 674/675] =?UTF-8?q?=E8=A1=A5=E5=85=852023=E7=9A=84?= =?UTF-8?q?=E4=BF=A1=E6=81=AF?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/CAD/Directory.Build.props | 4 ++-- src/CAD/IFox.CAD.Shared/Runtime/Env.cs | 1 + 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/src/CAD/Directory.Build.props b/src/CAD/Directory.Build.props index 19bc390..84265c2 100644 --- a/src/CAD/Directory.Build.props +++ b/src/CAD/Directory.Build.props @@ -1,8 +1,8 @@  - 0.5.2.3 - 修复TextInfo.AddDBTextToEntity()报错的问题 + 0.5.2.4 + 补充2023的信息 diff --git a/src/CAD/IFox.CAD.Shared/Runtime/Env.cs b/src/CAD/IFox.CAD.Shared/Runtime/Env.cs index 3a351ab..df64594 100644 --- a/src/CAD/IFox.CAD.Shared/Runtime/Env.cs +++ b/src/CAD/IFox.CAD.Shared/Runtime/Env.cs @@ -667,6 +667,7 @@ public static int GetAcadVersion() "23.1" => 2020, "24.0" => 2021, "24.1" => 2022, + "24.2" => 2023, _ => throw new NotImplementedException(), }; return acarVarNum; -- Gitee From dcee32e56d6b4a4ee043620c067188a9f941ed36 Mon Sep 17 00:00:00 2001 From: vicwjb Date: Fri, 31 Mar 2023 22:56:53 +0800 Subject: [PATCH 675/675] =?UTF-8?q?=E6=9B=B4=E6=96=B00.5.2.4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/Basal/Directory.Build.props | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Basal/Directory.Build.props b/src/Basal/Directory.Build.props index dffe68a..77a2d37 100644 --- a/src/Basal/Directory.Build.props +++ b/src/Basal/Directory.Build.props @@ -1,7 +1,7 @@  - 0.5.2.3 + 0.5.2.4 完善源码包,源码包映射原始目录 -- Gitee